{"LICENSE":"                    GNU AFFERO GENERAL PUBLIC LICENSE\r\n                       Version 3, 19 November 2007\r\n\r\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\r\n Everyone is permitted to copy and distribute verbatim copies\r\n of this license document, but changing it is not allowed.\r\n\r\n                            Preamble\r\n\r\n  The GNU Affero General Public License is a free, copyleft license for\r\nsoftware and other kinds of works, specifically designed to ensure\r\ncooperation with the community in the case of network server software.\r\n\r\n  The licenses for most software and other practical works are designed\r\nto take away your freedom to share and change the works.  By contrast,\r\nour General Public Licenses are intended to guarantee your freedom to\r\nshare and change all versions of a program--to make sure it remains free\r\nsoftware for all its users.\r\n\r\n  When we speak of free software, we are referring to freedom, not\r\nprice.  Our General Public Licenses are designed to make sure that you\r\nhave the freedom to distribute copies of free software (and charge for\r\nthem if you wish), that you receive source code or can get it if you\r\nwant it, that you can change the software or use pieces of it in new\r\nfree programs, and that you know you can do these things.\r\n\r\n  Developers that use our General Public Licenses protect your rights\r\nwith two steps: (1) assert copyright on the software, and (2) offer\r\nyou this License which gives you legal permission to copy, distribute\r\nand/or modify the software.\r\n\r\n  A secondary benefit of defending all users' freedom is that\r\nimprovements made in alternate versions of the program, if they\r\nreceive widespread use, become available for other developers to\r\nincorporate.  Many developers of free software are heartened and\r\nencouraged by the resulting cooperation.  However, in the case of\r\nsoftware used on network servers, this result may fail to come about.\r\nThe GNU General Public License permits making a modified version and\r\nletting the public access it on a server without ever releasing its\r\nsource code to the public.\r\n\r\n  The GNU Affero General Public License is designed specifically to\r\nensure that, in such cases, the modified source code becomes available\r\nto the community.  It requires the operator of a network server to\r\nprovide the source code of the modified version running there to the\r\nusers of that server.  Therefore, public use of a modified version, on\r\na publicly accessible server, gives the public access to the source\r\ncode of the modified version.\r\n\r\n  An older license, called the Affero General Public License and\r\npublished by Affero, was designed to accomplish similar goals.  This is\r\na different license, not a version of the Affero GPL, but Affero has\r\nreleased a new version of the Affero GPL which permits relicensing under\r\nthis license.\r\n\r\n  The precise terms and conditions for copying, distribution and\r\nmodification follow.\r\n\r\n                       TERMS AND CONDITIONS\r\n\r\n  0. Definitions.\r\n\r\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\r\n\r\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\r\nworks, such as semiconductor masks.\r\n\r\n  \"The Program\" refers to any copyrightable work licensed under this\r\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\r\n\"recipients\" may be individuals or organizations.\r\n\r\n  To \"modify\" a work means to copy from or adapt all or part of the work\r\nin a fashion requiring copyright permission, other than the making of an\r\nexact copy.  The resulting work is called a \"modified version\" of the\r\nearlier work or a work \"based on\" the earlier work.\r\n\r\n  A \"covered work\" means either the unmodified Program or a work based\r\non the Program.\r\n\r\n  To \"propagate\" a work means to do anything with it that, without\r\npermission, would make you directly or secondarily liable for\r\ninfringement under applicable copyright law, except executing it on a\r\ncomputer or modifying a private copy.  Propagation includes copying,\r\ndistribution (with or without modification), making available to the\r\npublic, and in some countries other activities as well.\r\n\r\n  To \"convey\" a work means any kind of propagation that enables other\r\nparties to make or receive copies.  Mere interaction with a user through\r\na computer network, with no transfer of a copy, is not conveying.\r\n\r\n  An interactive user interface displays \"Appropriate Legal Notices\"\r\nto the extent that it includes a convenient and prominently visible\r\nfeature that (1) displays an appropriate copyright notice, and (2)\r\ntells the user that there is no warranty for the work (except to the\r\nextent that warranties are provided), that licensees may convey the\r\nwork under this License, and how to view a copy of this License.  If\r\nthe interface presents a list of user commands or options, such as a\r\nmenu, a prominent item in the list meets this criterion.\r\n\r\n  1. Source Code.\r\n\r\n  The \"source code\" for a work means the preferred form of the work\r\nfor making modifications to it.  \"Object code\" means any non-source\r\nform of a work.\r\n\r\n  A \"Standard Interface\" means an interface that either is an official\r\nstandard defined by a recognized standards body, or, in the case of\r\ninterfaces specified for a particular programming language, one that\r\nis widely used among developers working in that language.\r\n\r\n  The \"System Libraries\" of an executable work include anything, other\r\nthan the work as a whole, that (a) is included in the normal form of\r\npackaging a Major Component, but which is not part of that Major\r\nComponent, and (b) serves only to enable use of the work with that\r\nMajor Component, or to implement a Standard Interface for which an\r\nimplementation is available to the public in source code form.  A\r\n\"Major Component\", in this context, means a major essential component\r\n(kernel, window system, and so on) of the specific operating system\r\n(if any) on which the executable work runs, or a compiler used to\r\nproduce the work, or an object code interpreter used to run it.\r\n\r\n  The \"Corresponding Source\" for a work in object code form means all\r\nthe source code needed to generate, install, and (for an executable\r\nwork) run the object code and to modify the work, including scripts to\r\ncontrol those activities.  However, it does not include the work's\r\nSystem Libraries, or general-purpose tools or generally available free\r\nprograms which are used unmodified in performing those activities but\r\nwhich are not part of the work.  For example, Corresponding Source\r\nincludes interface definition files associated with source files for\r\nthe work, and the source code for shared libraries and dynamically\r\nlinked subprograms that the work is specifically designed to require,\r\nsuch as by intimate data communication or control flow between those\r\nsubprograms and other parts of the work.\r\n\r\n  The Corresponding Source need not include anything that users\r\ncan regenerate automatically from other parts of the Corresponding\r\nSource.\r\n\r\n  The Corresponding Source for a work in source code form is that\r\nsame work.\r\n\r\n  2. Basic Permissions.\r\n\r\n  All rights granted under this License are granted for the term of\r\ncopyright on the Program, and are irrevocable provided the stated\r\nconditions are met.  This License explicitly affirms your unlimited\r\npermission to run the unmodified Program.  The output from running a\r\ncovered work is covered by this License only if the output, given its\r\ncontent, constitutes a covered work.  This License acknowledges your\r\nrights of fair use or other equivalent, as provided by copyright law.\r\n\r\n  You may make, run and propagate covered works that you do not\r\nconvey, without conditions so long as your license otherwise remains\r\nin force.  You may convey covered works to others for the sole purpose\r\nof having them make modifications exclusively for you, or provide you\r\nwith facilities for running those works, provided that you comply with\r\nthe terms of this License in conveying all material for which you do\r\nnot control copyright.  Those thus making or running the covered works\r\nfor you must do so exclusively on your behalf, under your direction\r\nand control, on terms that prohibit them from making any copies of\r\nyour copyrighted material outside their relationship with you.\r\n\r\n  Conveying under any other circumstances is permitted solely under\r\nthe conditions stated below.  Sublicensing is not allowed; section 10\r\nmakes it unnecessary.\r\n\r\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\r\n\r\n  No covered work shall be deemed part of an effective technological\r\nmeasure under any applicable law fulfilling obligations under article\r\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\r\nsimilar laws prohibiting or restricting circumvention of such\r\nmeasures.\r\n\r\n  When you convey a covered work, you waive any legal power to forbid\r\ncircumvention of technological measures to the extent such circumvention\r\nis effected by exercising rights under this License with respect to\r\nthe covered work, and you disclaim any intention to limit operation or\r\nmodification of the work as a means of enforcing, against the work's\r\nusers, your or third parties' legal rights to forbid circumvention of\r\ntechnological measures.\r\n\r\n  4. Conveying Verbatim Copies.\r\n\r\n  You may convey verbatim copies of the Program's source code as you\r\nreceive it, in any medium, provided that you conspicuously and\r\nappropriately publish on each copy an appropriate copyright notice;\r\nkeep intact all notices stating that this License and any\r\nnon-permissive terms added in accord with section 7 apply to the code;\r\nkeep intact all notices of the absence of any warranty; and give all\r\nrecipients a copy of this License along with the Program.\r\n\r\n  You may charge any price or no price for each copy that you convey,\r\nand you may offer support or warranty protection for a fee.\r\n\r\n  5. Conveying Modified Source Versions.\r\n\r\n  You may convey a work based on the Program, or the modifications to\r\nproduce it from the Program, in the form of source code under the\r\nterms of section 4, provided that you also meet all of these conditions:\r\n\r\n    a) The work must carry prominent notices stating that you modified\r\n    it, and giving a relevant date.\r\n\r\n    b) The work must carry prominent notices stating that it is\r\n    released under this License and any conditions added under section\r\n    7.  This requirement modifies the requirement in section 4 to\r\n    \"keep intact all notices\".\r\n\r\n    c) You must license the entire work, as a whole, under this\r\n    License to anyone who comes into possession of a copy.  This\r\n    License will therefore apply, along with any applicable section 7\r\n    additional terms, to the whole of the work, and all its parts,\r\n    regardless of how they are packaged.  This License gives no\r\n    permission to license the work in any other way, but it does not\r\n    invalidate such permission if you have separately received it.\r\n\r\n    d) If the work has interactive user interfaces, each must display\r\n    Appropriate Legal Notices; however, if the Program has interactive\r\n    interfaces that do not display Appropriate Legal Notices, your\r\n    work need not make them do so.\r\n\r\n  A compilation of a covered work with other separate and independent\r\nworks, which are not by their nature extensions of the covered work,\r\nand which are not combined with it such as to form a larger program,\r\nin or on a volume of a storage or distribution medium, is called an\r\n\"aggregate\" if the compilation and its resulting copyright are not\r\nused to limit the access or legal rights of the compilation's users\r\nbeyond what the individual works permit.  Inclusion of a covered work\r\nin an aggregate does not cause this License to apply to the other\r\nparts of the aggregate.\r\n\r\n  6. Conveying Non-Source Forms.\r\n\r\n  You may convey a covered work in object code form under the terms\r\nof sections 4 and 5, provided that you also convey the\r\nmachine-readable Corresponding Source under the terms of this License,\r\nin one of these ways:\r\n\r\n    a) Convey the object code in, or embodied in, a physical product\r\n    (including a physical distribution medium), accompanied by the\r\n    Corresponding Source fixed on a durable physical medium\r\n    customarily used for software interchange.\r\n\r\n    b) Convey the object code in, or embodied in, a physical product\r\n    (including a physical distribution medium), accompanied by a\r\n    written offer, valid for at least three years and valid for as\r\n    long as you offer spare parts or customer support for that product\r\n    model, to give anyone who possesses the object code either (1) a\r\n    copy of the Corresponding Source for all the software in the\r\n    product that is covered by this License, on a durable physical\r\n    medium customarily used for software interchange, for a price no\r\n    more than your reasonable cost of physically performing this\r\n    conveying of source, or (2) access to copy the\r\n    Corresponding Source from a network server at no charge.\r\n\r\n    c) Convey individual copies of the object code with a copy of the\r\n    written offer to provide the Corresponding Source.  This\r\n    alternative is allowed only occasionally and noncommercially, and\r\n    only if you received the object code with such an offer, in accord\r\n    with subsection 6b.\r\n\r\n    d) Convey the object code by offering access from a designated\r\n    place (gratis or for a charge), and offer equivalent access to the\r\n    Corresponding Source in the same way through the same place at no\r\n    further charge.  You need not require recipients to copy the\r\n    Corresponding Source along with the object code.  If the place to\r\n    copy the object code is a network server, the Corresponding Source\r\n    may be on a different server (operated by you or a third party)\r\n    that supports equivalent copying facilities, provided you maintain\r\n    clear directions next to the object code saying where to find the\r\n    Corresponding Source.  Regardless of what server hosts the\r\n    Corresponding Source, you remain obligated to ensure that it is\r\n    available for as long as needed to satisfy these requirements.\r\n\r\n    e) Convey the object code using peer-to-peer transmission, provided\r\n    you inform other peers where the object code and Corresponding\r\n    Source of the work are being offered to the general public at no\r\n    charge under subsection 6d.\r\n\r\n  A separable portion of the object code, whose source code is excluded\r\nfrom the Corresponding Source as a System Library, need not be\r\nincluded in conveying the object code work.\r\n\r\n  A \"User Product\" is either (1) a \"consumer product\", which means any\r\ntangible personal property which is normally used for personal, family,\r\nor household purposes, or (2) anything designed or sold for incorporation\r\ninto a dwelling.  In determining whether a product is a consumer product,\r\ndoubtful cases shall be resolved in favor of coverage.  For a particular\r\nproduct received by a particular user, \"normally used\" refers to a\r\ntypical or common use of that class of product, regardless of the status\r\nof the particular user or of the way in which the particular user\r\nactually uses, or expects or is expected to use, the product.  A product\r\nis a consumer product regardless of whether the product has substantial\r\ncommercial, industrial or non-consumer uses, unless such uses represent\r\nthe only significant mode of use of the product.\r\n\r\n  \"Installation Information\" for a User Product means any methods,\r\nprocedures, authorization keys, or other information required to install\r\nand execute modified versions of a covered work in that User Product from\r\na modified version of its Corresponding Source.  The information must\r\nsuffice to ensure that the continued functioning of the modified object\r\ncode is in no case prevented or interfered with solely because\r\nmodification has been made.\r\n\r\n  If you convey an object code work under this section in, or with, or\r\nspecifically for use in, a User Product, and the conveying occurs as\r\npart of a transaction in which the right of possession and use of the\r\nUser Product is transferred to the recipient in perpetuity or for a\r\nfixed term (regardless of how the transaction is characterized), the\r\nCorresponding Source conveyed under this section must be accompanied\r\nby the Installation Information.  But this requirement does not apply\r\nif neither you nor any third party retains the ability to install\r\nmodified object code on the User Product (for example, the work has\r\nbeen installed in ROM).\r\n\r\n  The requirement to provide Installation Information does not include a\r\nrequirement to continue to provide support service, warranty, or updates\r\nfor a work that has been modified or installed by the recipient, or for\r\nthe User Product in which it has been modified or installed.  Access to a\r\nnetwork may be denied when the modification itself materially and\r\nadversely affects the operation of the network or violates the rules and\r\nprotocols for communication across the network.\r\n\r\n  Corresponding Source conveyed, and Installation Information provided,\r\nin accord with this section must be in a format that is publicly\r\ndocumented (and with an implementation available to the public in\r\nsource code form), and must require no special password or key for\r\nunpacking, reading or copying.\r\n\r\n  7. Additional Terms.\r\n\r\n  \"Additional permissions\" are terms that supplement the terms of this\r\nLicense by making exceptions from one or more of its conditions.\r\nAdditional permissions that are applicable to the entire Program shall\r\nbe treated as though they were included in this License, to the extent\r\nthat they are valid under applicable law.  If additional permissions\r\napply only to part of the Program, that part may be used separately\r\nunder those permissions, but the entire Program remains governed by\r\nthis License without regard to the additional permissions.\r\n\r\n  When you convey a copy of a covered work, you may at your option\r\nremove any additional permissions from that copy, or from any part of\r\nit.  (Additional permissions may be written to require their own\r\nremoval in certain cases when you modify the work.)  You may place\r\nadditional permissions on material, added by you to a covered work,\r\nfor which you have or can give appropriate copyright permission.\r\n\r\n  Notwithstanding any other provision of this License, for material you\r\nadd to a covered work, you may (if authorized by the copyright holders of\r\nthat material) supplement the terms of this License with terms:\r\n\r\n    a) Disclaiming warranty or limiting liability differently from the\r\n    terms of sections 15 and 16 of this License; or\r\n\r\n    b) Requiring preservation of specified reasonable legal notices or\r\n    author attributions in that material or in the Appropriate Legal\r\n    Notices displayed by works containing it; or\r\n\r\n    c) Prohibiting misrepresentation of the origin of that material, or\r\n    requiring that modified versions of such material be marked in\r\n    reasonable ways as different from the original version; or\r\n\r\n    d) Limiting the use for publicity purposes of names of licensors or\r\n    authors of the material; or\r\n\r\n    e) Declining to grant rights under trademark law for use of some\r\n    trade names, trademarks, or service marks; or\r\n\r\n    f) Requiring indemnification of licensors and authors of that\r\n    material by anyone who conveys the material (or modified versions of\r\n    it) with contractual assumptions of liability to the recipient, for\r\n    any liability that these contractual assumptions directly impose on\r\n    those licensors and authors.\r\n\r\n  All other non-permissive additional terms are considered \"further\r\nrestrictions\" within the meaning of section 10.  If the Program as you\r\nreceived it, or any part of it, contains a notice stating that it is\r\ngoverned by this License along with a term that is a further\r\nrestriction, you may remove that term.  If a license document contains\r\na further restriction but permits relicensing or conveying under this\r\nLicense, you may add to a covered work material governed by the terms\r\nof that license document, provided that the further restriction does\r\nnot survive such relicensing or conveying.\r\n\r\n  If you add terms to a covered work in accord with this section, you\r\nmust place, in the relevant source files, a statement of the\r\nadditional terms that apply to those files, or a notice indicating\r\nwhere to find the applicable terms.\r\n\r\n  Additional terms, permissive or non-permissive, may be stated in the\r\nform of a separately written license, or stated as exceptions;\r\nthe above requirements apply either way.\r\n\r\n  8. Termination.\r\n\r\n  You may not propagate or modify a covered work except as expressly\r\nprovided under this License.  Any attempt otherwise to propagate or\r\nmodify it is void, and will automatically terminate your rights under\r\nthis License (including any patent licenses granted under the third\r\nparagraph of section 11).\r\n\r\n  However, if you cease all violation of this License, then your\r\nlicense from a particular copyright holder is reinstated (a)\r\nprovisionally, unless and until the copyright holder explicitly and\r\nfinally terminates your license, and (b) permanently, if the copyright\r\nholder fails to notify you of the violation by some reasonable means\r\nprior to 60 days after the cessation.\r\n\r\n  Moreover, your license from a particular copyright holder is\r\nreinstated permanently if the copyright holder notifies you of the\r\nviolation by some reasonable means, this is the first time you have\r\nreceived notice of violation of this License (for any work) from that\r\ncopyright holder, and you cure the violation prior to 30 days after\r\nyour receipt of the notice.\r\n\r\n  Termination of your rights under this section does not terminate the\r\nlicenses of parties who have received copies or rights from you under\r\nthis License.  If your rights have been terminated and not permanently\r\nreinstated, you do not qualify to receive new licenses for the same\r\nmaterial under section 10.\r\n\r\n  9. Acceptance Not Required for Having Copies.\r\n\r\n  You are not required to accept this License in order to receive or\r\nrun a copy of the Program.  Ancillary propagation of a covered work\r\noccurring solely as a consequence of using peer-to-peer transmission\r\nto receive a copy likewise does not require acceptance.  However,\r\nnothing other than this License grants you permission to propagate or\r\nmodify any covered work.  These actions infringe copyright if you do\r\nnot accept this License.  Therefore, by modifying or propagating a\r\ncovered work, you indicate your acceptance of this License to do so.\r\n\r\n  10. Automatic Licensing of Downstream Recipients.\r\n\r\n  Each time you convey a covered work, the recipient automatically\r\nreceives a license from the original licensors, to run, modify and\r\npropagate that work, subject to this License.  You are not responsible\r\nfor enforcing compliance by third parties with this License.\r\n\r\n  An \"entity transaction\" is a transaction transferring control of an\r\norganization, or substantially all assets of one, or subdividing an\r\norganization, or merging organizations.  If propagation of a covered\r\nwork results from an entity transaction, each party to that\r\ntransaction who receives a copy of the work also receives whatever\r\nlicenses to the work the party's predecessor in interest had or could\r\ngive under the previous paragraph, plus a right to possession of the\r\nCorresponding Source of the work from the predecessor in interest, if\r\nthe predecessor has it or can get it with reasonable efforts.\r\n\r\n  You may not impose any further restrictions on the exercise of the\r\nrights granted or affirmed under this License.  For example, you may\r\nnot impose a license fee, royalty, or other charge for exercise of\r\nrights granted under this License, and you may not initiate litigation\r\n(including a cross-claim or counterclaim in a lawsuit) alleging that\r\nany patent claim is infringed by making, using, selling, offering for\r\nsale, or importing the Program or any portion of it.\r\n\r\n  11. Patents.\r\n\r\n  A \"contributor\" is a copyright holder who authorizes use under this\r\nLicense of the Program or a work on which the Program is based.  The\r\nwork thus licensed is called the contributor's \"contributor version\".\r\n\r\n  A contributor's \"essential patent claims\" are all patent claims\r\nowned or controlled by the contributor, whether already acquired or\r\nhereafter acquired, that would be infringed by some manner, permitted\r\nby this License, of making, using, or selling its contributor version,\r\nbut do not include claims that would be infringed only as a\r\nconsequence of further modification of the contributor version.  For\r\npurposes of this definition, \"control\" includes the right to grant\r\npatent sublicenses in a manner consistent with the requirements of\r\nthis License.\r\n\r\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\r\npatent license under the contributor's essential patent claims, to\r\nmake, use, sell, offer for sale, import and otherwise run, modify and\r\npropagate the contents of its contributor version.\r\n\r\n  In the following three paragraphs, a \"patent license\" is any express\r\nagreement or commitment, however denominated, not to enforce a patent\r\n(such as an express permission to practice a patent or covenant not to\r\nsue for patent infringement).  To \"grant\" such a patent license to a\r\nparty means to make such an agreement or commitment not to enforce a\r\npatent against the party.\r\n\r\n  If you convey a covered work, knowingly relying on a patent license,\r\nand the Corresponding Source of the work is not available for anyone\r\nto copy, free of charge and under the terms of this License, through a\r\npublicly available network server or other readily accessible means,\r\nthen you must either (1) cause the Corresponding Source to be so\r\navailable, or (2) arrange to deprive yourself of the benefit of the\r\npatent license for this particular work, or (3) arrange, in a manner\r\nconsistent with the requirements of this License, to extend the patent\r\nlicense to downstream recipients.  \"Knowingly relying\" means you have\r\nactual knowledge that, but for the patent license, your conveying the\r\ncovered work in a country, or your recipient's use of the covered work\r\nin a country, would infringe one or more identifiable patents in that\r\ncountry that you have reason to believe are valid.\r\n\r\n  If, pursuant to or in connection with a single transaction or\r\narrangement, you convey, or propagate by procuring conveyance of, a\r\ncovered work, and grant a patent license to some of the parties\r\nreceiving the covered work authorizing them to use, propagate, modify\r\nor convey a specific copy of the covered work, then the patent license\r\nyou grant is automatically extended to all recipients of the covered\r\nwork and works based on it.\r\n\r\n  A patent license is \"discriminatory\" if it does not include within\r\nthe scope of its coverage, prohibits the exercise of, or is\r\nconditioned on the non-exercise of one or more of the rights that are\r\nspecifically granted under this License.  You may not convey a covered\r\nwork if you are a party to an arrangement with a third party that is\r\nin the business of distributing software, under which you make payment\r\nto the third party based on the extent of your activity of conveying\r\nthe work, and under which the third party grants, to any of the\r\nparties who would receive the covered work from you, a discriminatory\r\npatent license (a) in connection with copies of the covered work\r\nconveyed by you (or copies made from those copies), or (b) primarily\r\nfor and in connection with specific products or compilations that\r\ncontain the covered work, unless you entered into that arrangement,\r\nor that patent license was granted, prior to 28 March 2007.\r\n\r\n  Nothing in this License shall be construed as excluding or limiting\r\nany implied license or other defenses to infringement that may\r\notherwise be available to you under applicable patent law.\r\n\r\n  12. No Surrender of Others' Freedom.\r\n\r\n  If conditions are imposed on you (whether by court order, agreement or\r\notherwise) that contradict the conditions of this License, they do not\r\nexcuse you from the conditions of this License.  If you cannot convey a\r\ncovered work so as to satisfy simultaneously your obligations under this\r\nLicense and any other pertinent obligations, then as a consequence you may\r\nnot convey it at all.  For example, if you agree to terms that obligate you\r\nto collect a royalty for further conveying from those to whom you convey\r\nthe Program, the only way you could satisfy both those terms and this\r\nLicense would be to refrain entirely from conveying the Program.\r\n\r\n  13. Remote Network Interaction; Use with the GNU General Public License.\r\n\r\n  Notwithstanding any other provision of this License, if you modify the\r\nProgram, your modified version must prominently offer all users\r\ninteracting with it remotely through a computer network (if your version\r\nsupports such interaction) an opportunity to receive the Corresponding\r\nSource of your version by providing access to the Corresponding Source\r\nfrom a network server at no charge, through some standard or customary\r\nmeans of facilitating copying of software.  This Corresponding Source\r\nshall include the Corresponding Source for any work covered by version 3\r\nof the GNU General Public License that is incorporated pursuant to the\r\nfollowing paragraph.\r\n\r\n  Notwithstanding any other provision of this License, you have\r\npermission to link or combine any covered work with a work licensed\r\nunder version 3 of the GNU General Public License into a single\r\ncombined work, and to convey the resulting work.  The terms of this\r\nLicense will continue to apply to the part which is the covered work,\r\nbut the work with which it is combined will remain governed by version\r\n3 of the GNU General Public License.\r\n\r\n  14. Revised Versions of this License.\r\n\r\n  The Free Software Foundation may publish revised and/or new versions of\r\nthe GNU Affero General Public License from time to time.  Such new versions\r\nwill be similar in spirit to the present version, but may differ in detail to\r\naddress new problems or concerns.\r\n\r\n  Each version is given a distinguishing version number.  If the\r\nProgram specifies that a certain numbered version of the GNU Affero General\r\nPublic License \"or any later version\" applies to it, you have the\r\noption of following the terms and conditions either of that numbered\r\nversion or of any later version published by the Free Software\r\nFoundation.  If the Program does not specify a version number of the\r\nGNU Affero General Public License, you may choose any version ever published\r\nby the Free Software Foundation.\r\n\r\n  If the Program specifies that a proxy can decide which future\r\nversions of the GNU Affero General Public License can be used, that proxy's\r\npublic statement of acceptance of a version permanently authorizes you\r\nto choose that version for the Program.\r\n\r\n  Later license versions may give you additional or different\r\npermissions.  However, no additional obligations are imposed on any\r\nauthor or copyright holder as a result of your choosing to follow a\r\nlater version.\r\n\r\n  15. Disclaimer of Warranty.\r\n\r\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\r\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\r\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\r\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\r\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\r\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\r\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r\n\r\n  16. Limitation of Liability.\r\n\r\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\r\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\r\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\r\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\r\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\r\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\r\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\r\nSUCH DAMAGES.\r\n\r\n  17. Interpretation of Sections 15 and 16.\r\n\r\n  If the disclaimer of warranty and limitation of liability provided\r\nabove cannot be given local legal effect according to their terms,\r\nreviewing courts shall apply local law that most closely approximates\r\nan absolute waiver of all civil liability in connection with the\r\nProgram, unless a warranty or assumption of liability accompanies a\r\ncopy of the Program in return for a fee.\r\n\r\n                     END OF TERMS AND CONDITIONS\r\n\r\n            How to Apply These Terms to Your New Programs\r\n\r\n  If you develop a new program, and you want it to be of the greatest\r\npossible use to the public, the best way to achieve this is to make it\r\nfree software which everyone can redistribute and change under these terms.\r\n\r\n  To do so, attach the following notices to the program.  It is safest\r\nto attach them to the start of each source file to most effectively\r\nstate the exclusion of warranty; and each file should have at least\r\nthe \"copyright\" line and a pointer to where the full notice is found.\r\n\r\n    <one line to give the program's name and a brief idea of what it does.>\r\n    Copyright (C) <year>  <name of author>\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU Affero General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU Affero General Public License for more details.\r\n\r\n    You should have received a copy of the GNU Affero General Public License\r\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\r\n\r\nAlso add information on how to contact you by electronic and paper mail.\r\n\r\n  If your software can interact with users remotely through a computer\r\nnetwork, you should also make sure that it provides a way for users to\r\nget its source.  For example, if your program is a web application, its\r\ninterface could display a \"Source\" link that leads users to an archive\r\nof the code.  There are many ways you could offer source, and different\r\nsolutions will be better for different programs; see section 13 for the\r\nspecific requirements.\r\n\r\n  You should also get your employer (if you work as a programmer) or school,\r\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\r\nFor more information on this, and how to apply and follow the GNU AGPL, see\r\n<https://www.gnu.org/licenses/>.\r\n","about.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n<title>About MYR5 · free AR alien workout coach</title>\r\n<meta name=\"description\" content=\"MYR5 is a free browser workout app with a 3D alien coach. Your phone camera counts reps and holds on the device; nothing is uploaded. Ages 13+.\">\r\n<link rel=\"canonical\" href=\"https://myr5.mominc.online/about\">\r\n<meta property=\"og:type\" content=\"website\"><meta property=\"og:site_name\" content=\"MYR5 Coach\"><meta property=\"og:url\" content=\"https://myr5.mominc.online/about\"><meta property=\"og:title\" content=\"About MYR5 · free AR alien workout coach\"><meta property=\"og:description\" content=\"Free browser workout app. A 3D alien coach counts your reps with your phone camera, on-device.\"><meta property=\"og:image\" content=\"https://myr5.mominc.online/icons/myr5-alien-512.png\">\r\n<link rel=\"icon\" href=\"/icons/myr5-alien-192.png\"><link rel=\"stylesheet\" href=\"/signin.css\"><style>main{width:min(100% - 40px,740px)}h2{margin-top:32px;font-size:22px}a{color:#dfb7ff}li{margin:8px 0;line-height:1.5}</style>\r\n<script type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://myr5.mominc.online/#operator\",\"name\":\"MYR5\",\"description\":\"Independent app maker of MYR5 Coach\",\"url\":\"https://myr5.mominc.online/about\",\"email\":\"ianmyersrocks97@gmail.com\"},{\"@type\":\"SoftwareApplication\",\"@id\":\"https://myr5.mominc.online/#app\",\"name\":\"MYR5 Coach\",\"alternateName\":\"MYR5\",\"url\":\"https://myr5.mominc.online/\",\"applicationCategory\":\"HealthApplication\",\"applicationSubCategory\":\"Fitness\",\"operatingSystem\":\"Any modern browser (Android, iOS, desktop)\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"typicalAgeRange\":\"13-\",\"publisher\":{\"@id\":\"https://myr5.mominc.online/#operator\"}},{\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"Does MYR5 upload my camera video?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. All camera and pose processing stays on your phone. Camera frames are not sent to a server.\"}},{\"@type\":\"Question\",\"name\":\"Do I need a VR headset, console or equipment?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. MYR5 runs in a phone or computer browser and uses bodyweight exercises. Prop the phone so the camera can see you.\"}},{\"@type\":\"Question\",\"name\":\"Who is MYR5 for?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Teens, adults, and families with teens. MYR5 is for ages 13 and up and is not directed to children under 13.\"}},{\"@type\":\"Question\",\"name\":\"How much does MYR5 cost?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Nothing. MYR5 is free and has no paid features or subscription.\"}}]}]}</script>\r\n<link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main><a class=\"brand\" href=\"/\"><img src=\"/icons/myr5-alien-192.png\" alt=\"MYR5\"><span>MYR5</span></a>\r\n<h1>About MYR5</h1>\r\n<p>MYR5 (MYR5 Coach) is a free web app that turns bodyweight workouts into a game with a 3D alien coach. It runs in your phone or computer browser and installs from the browser as an app. No app store, VR headset, console or equipment is needed.</p>\r\n<h2>How it works</h2>\r\n<p>Prop your phone so its camera can see you. Your alien coach demonstrates each move, and on-device pose tracking counts your reps or times your holds. Finished sets level up your coach, unlock rewards and fuel battles in the rest chamber. You can customize your coach and set workout reminders.</p>\r\n<p>The camera can follow 57 movements in 10 groups:</p>\r\n<ul>\r\n<li>Chest: push-up progressions, from knee and incline push-ups to diamond and feet-elevated push-ups</li>\r\n<li>Legs: squats, split squats and wall sits</li>\r\n<li>Hips and glutes: hip hinges, good mornings and glute bridges</li>\r\n<li>Core: plank and side plank holds</li>\r\n<li>Shoulders: arm raises, overhead reaches and unloaded presses</li>\r\n<li>Balance: knee-lift balance and tree pose holds</li>\r\n<li>Yoga: Mountain, Upward salute, Chair, Warrior I, Warrior II and Goddess</li>\r\n<li>Martial stances: horse stance and front stance holds</li>\r\n<li>Boxing: slow shadowboxing straights and jabs</li>\r\n<li>Cardio: marches, jogging in place, step jacks and jumping jacks</li>\r\n</ul>\r\n<p>Counts and hold times are estimates from pose tracking. They do not grade technique and are not medical or professional advice.</p>\r\n<h2 id=\"faq\">Facts</h2>\r\n<ul>\r\n<li><strong>Price:</strong> free. There are no paid features or subscription.</li>\r\n<li><strong>Camera privacy:</strong> all camera and pose processing stays on your phone. Camera frames are not uploaded.</li>\r\n<li><strong>Age:</strong> 13 and up. MYR5 is for teens, adults, and families with teens. It is not directed to children under 13.</li>\r\n<li><strong>Equipment:</strong> none. A phone or computer with a camera and a modern browser (JavaScript and WebGL).</li>\r\n<li><strong>Accounts:</strong> optional sign-in saves your coach and workout history across devices. You can download or delete your saved data in account settings.</li>\r\n</ul>\r\n<h2>Who makes MYR5</h2>\r\n<p>MYR5 is an independent app, operated by MYR5. Contact: <a href=\"mailto:ianmyersrocks97@gmail.com\">ianmyersrocks97@gmail.com</a>.</p>\r\n<p>The alien coaches also appear on <a href=\"https://mominc.online\">mominc.online</a>, home of MOM Inc., who would like you to know your reps are \"synergizing.\" (MOM Inc. is fictional.)</p>\r\n<p><a href=\"/privacy\">Privacy</a> · <a href=\"/\">Open MYR5</a></p>\r\n</main></body></html>\r\n","account-readiness-client.mjs":"const validScope=value=>typeof value?.user?.id==='string'&&value.user.id&&Number.isSafeInteger(value.dataEpoch)&&value.dataEpoch>0;\r\nconst same=(account,scope)=>account?.user?.id===scope.owner&&account?.dataEpoch===scope.epoch;\r\nconst choose=(previous,next)=>{\r\n if(!next||next.state==='unknown')return previous??next;\r\n if(!previous||!Number.isSafeInteger(previous.startedAt))return next;\r\n if(!Number.isSafeInteger(next.startedAt)||next.startedAt<previous.startedAt||next.startedAt===previous.startedAt&&String(next.observationId)<String(previous.observationId))return previous;\r\n return next;\r\n};\r\n// Optional cache observations cannot establish sign-in or replace core account\r\n// data. This helper retains no persisted browser authority and never retries.\r\nexport function createAccountReadinessRefresh({api,transitions,getAccount,apply}){\r\n let generation=0;\r\n transitions.subscribe(()=>generation++);\r\n async function refresh(){\r\n  const account=getAccount();if(!validScope(account))return false;\r\n  const scope={owner:account.user.id,epoch:account.dataEpoch},number=++generation;\r\n  let ticket;try{\r\n   ticket=transitions.capture();const result=await api('/api/account/readiness');\r\n   if(!transitions.isCurrent(ticket)||number!==generation||!same(getAccount(),scope)||result.targetAccountId!==scope.owner||result.dataEpoch!==scope.epoch)return false;\r\n   const current=getAccount(),readiness={reminders:choose(current.readiness?.reminders,result.readiness?.reminders),training:choose(current.readiness?.training,result.readiness?.training)};\r\n   // Preserve the last successful push configuration on an independent failure.\r\n   const health=readiness.reminders,push=health?.push?{...current.push,...health.push,status:health.state}:result.push&&['ready','available'].includes(result.push.status)&&(!health||health.state==='unknown')?{...current.push,...result.push}:health?{...current.push,status:health.state}:current.push;\r\n   apply({readiness,push},scope);return true;\r\n  }catch{return false;} // Optional observation failure does not mark core data stale.\r\n }\r\n return {refresh};\r\n}\r\n","account-session-actions.mjs":"const fail=code=>{throw Object.assign(Error('Account changed. Refresh before continuing.'),{code});};\r\nfunction scope(account){\r\n // Signed out is not an account change: say what unlocks it (breathing and onboarding save to the account).\r\n if(!account?.user)throw Object.assign(Error('Sign in to continue.'),{code:'account_scope_unavailable'});\r\n if(typeof account?.user?.id!=='string'||!account.user.id||!Number.isSafeInteger(account.dataEpoch)||account.dataEpoch<1)fail('account_scope_unavailable');\r\n return Object.freeze({ownerId:account.user.id,dataEpoch:account.dataEpoch});\r\n}\r\nconst headers=target=>({'X-Target-Account':target.ownerId,'X-Expected-Data-Epoch':String(target.dataEpoch)});\r\nexport function createAccountSessionActions({api,transitions}){\r\n const fresh=async(target,ticket)=>{\r\n  transitions.assertCurrent(ticket);\r\n  const live=scope(await api('/api/account?core=1'));\r\n  transitions.assertCurrent(ticket);\r\n  if(live.ownerId!==target.ownerId||live.dataEpoch!==target.dataEpoch)fail('account_scope_changed');\r\n };\r\n const receipt=(result,target)=>{if(result?.targetAccountId!==target.ownerId||result?.dataEpoch!==target.dataEpoch)fail('invalid_breathing_receipt');};\r\n return Object.freeze({\r\n  async saveOnboarding(displayedAccount,value){\r\n   const target=scope(displayedAccount),ticket=transitions.capture(),revision=displayedAccount.onboarding?.revision??0,data=structuredClone(value);\r\n   if(!Number.isSafeInteger(revision)||revision<0)fail('invalid_onboarding_revision');\r\n   await fresh(target,ticket);transitions.assertCurrent(ticket);\r\n   const result=await api('/api/onboarding','PUT',{data,revision},headers(target));\r\n   transitions.assertCurrent(ticket);return Object.freeze({result,ownerId:target.ownerId,dataEpoch:target.dataEpoch,transitionTicket:ticket});\r\n  },\r\n  async startBreathing(displayedAccount){\r\n   const target=scope(displayedAccount),ticket=transitions.capture();\r\n   await fresh(target,ticket);transitions.assertCurrent(ticket);\r\n   const result=await api('/api/breathing/start','POST',{},headers(target));\r\n   transitions.assertCurrent(ticket);receipt(result,target);\r\n   if(typeof result.id!=='string'||!result.id||result.id.length>128||!Number.isSafeInteger(result.startedAt)||result.startedAt<0||!Number.isSafeInteger(result.durationMs)||result.durationMs<=0)fail('invalid_breathing_ticket');\r\n   return Object.freeze({id:result.id,startedAt:result.startedAt,durationMs:result.durationMs,...target,transitionTicket:ticket});\r\n  },\r\n  async completeBreathing(binding,activeMs){\r\n   if(!binding||typeof binding.id!=='string'||!Number.isFinite(activeMs)||activeMs<0)fail('invalid_breathing_ticket');\r\n   const target=scope({user:{id:binding.ownerId},dataEpoch:binding.dataEpoch}),ticket=binding.transitionTicket;\r\n   await fresh(target,ticket);transitions.assertCurrent(ticket);\r\n   const result=await api('/api/breathing/complete','POST',{id:binding.id,activeMs},headers(target));\r\n   transitions.assertCurrent(ticket);receipt(result,target);return result;\r\n  },\r\n });\r\n}\r\n","account-workout-sync.mjs":"// Legacy account queue only. Never reads or drains the guest Dexie outbox.\r\nexport const accountPendingKey=owner=>`myr5-pending-sets:${owner}`;\r\nconst TICKET_KEY='myr5-server-workout-tickets-v1',UNBOUND_KEY='myr5-unbound-server-completions-v1';\r\nconst failure=code=>Object.assign(Error('Your round is waiting for account reconciliation.'),{code});\r\nconst scope=value=>value?.user?.id&&Number.isSafeInteger(value.dataEpoch)&&value.dataEpoch>0?{owner:value.user.id,epoch:value.dataEpoch}:null;\r\nexport function validateAccountDeletionReceipt(value,owner,requestedEpoch){\r\n if(!value||value.ownerId!==owner||value.deletedEpoch!==requestedEpoch||typeof value.alreadyDeleted!=='boolean'||value.deleted!==!value.alreadyDeleted||!Number.isSafeInteger(requestedEpoch)||requestedEpoch<1||!Number.isSafeInteger(value.currentDataEpoch)||!Number.isSafeInteger(value.deletedThroughEpoch)||value.deletedThroughEpoch<requestedEpoch||value.currentDataEpoch!==value.deletedThroughEpoch+1||(!value.alreadyDeleted&&value.deletedThroughEpoch!==requestedEpoch))throw failure('invalid_account_deletion_receipt');\r\n return value.deletedThroughEpoch;\r\n}\r\nexport function createAccountWorkoutSync({storage,api,transitions,getAccount,publishProgress}){\r\n const read=(key,fallback)=>{try{const value=storage.getItem(key);return value===null?fallback:JSON.parse(value);}catch{throw failure('account_queue_unavailable');}};\r\n const write=(key,value)=>{try{const text=JSON.stringify(value);storage.setItem(key,text);if(storage.getItem(key)!==text)throw Error();}catch{throw failure('account_queue_unavailable');}};\r\n const pending=owner=>{const rows=read(accountPendingKey(owner),[]);if(!Array.isArray(rows))throw failure('account_queue_unavailable');return rows;};\r\n const assertScope=(ticket,expected)=>{transitions.assertCurrent(ticket);const selected=scope(getAccount());if(!selected||selected.owner!==expected.owner||selected.epoch!==expected.epoch)throw failure('account_scope_changed');};\r\n const fresh=async(ticket,expected)=>{assertScope(ticket,expected);const value=await api('/api/account?core=1');assertScope(ticket,expected);const live=scope(value);if(!live||live.owner!==expected.owner||live.epoch!==expected.epoch)throw failure('account_scope_changed');return value;};\r\n let flushing;\r\n async function drain(){\r\n  const selected=scope(getAccount());if(!selected)return;\r\n  const ticket=transitions.capture(),queue=pending(selected.owner);\r\n  for(const item of queue){\r\n   assertScope(ticket,selected);\r\n   // Old untagged completions remain parked; never label them with today's epoch.\r\n   if(!item||item.targetAccountId!==selected.owner||item.targetDataEpoch!==selected.epoch)throw failure('account_queue_unbound');\r\n   await fresh(ticket,selected);assertScope(ticket,selected);\r\n   const {targetAccountId,targetDataEpoch,...body}=item;\r\n   const response=await api('/api/workouts/complete','POST',body,{'X-Target-Account':targetAccountId,'X-Expected-Data-Epoch':String(targetDataEpoch)});\r\n   assertScope(ticket,selected);\r\n   if(response?.targetAccountId!==targetAccountId||response?.dataEpoch!==targetDataEpoch)throw failure('invalid_completion_receipt');\r\n   if(!response?.progress||!Number.isSafeInteger(response.progress.completedSets)||response.progress.completedSets<0||!Number.isSafeInteger(response.progress.xp)||response.progress.xp<0)throw failure('invalid_completion_response');\r\n   const latest=pending(selected.owner),index=latest.findIndex(row=>row.id===item.id);\r\n   if(index<0)continue;\r\n   if(JSON.stringify(latest[index])!==JSON.stringify(item))throw failure('account_queue_changed');\r\n   write(accountPendingKey(selected.owner),latest.filter((_,i)=>i!==index));\r\n   assertScope(ticket,selected);publishProgress({...response.progress,lastSyncedWorkoutId:item.id});\r\n  }\r\n }\r\n const flush=()=>flushing??=(async()=>{try{return await drain();}finally{flushing=null;}})();\r\n return Object.freeze({\r\n  pending,flush,\r\n  forgetDeletedAccount(owner,deletedThroughEpoch){\r\n   if(typeof owner!=='string'||!owner||!Number.isSafeInteger(deletedThroughEpoch)||deletedThroughEpoch<1)throw failure('invalid_account_deletion_receipt');\r\n   const covered=row=>row?.targetAccountId===owner&&Number.isSafeInteger(row.targetDataEpoch)&&row.targetDataEpoch>0&&row.targetDataEpoch<=deletedThroughEpoch;\r\n   const tickets=read(TICKET_KEY,{});if(!tickets||typeof tickets!=='object'||Array.isArray(tickets))throw failure('account_queue_unavailable');\r\n   write(accountPendingKey(owner),pending(owner).filter(row=>!covered(row)));\r\n   write(TICKET_KEY,Object.fromEntries(Object.entries(tickets).filter(([,ticket])=>!covered(ticket))));\r\n  },\r\n  async start(mode,goal){\r\n   const selected=scope(getAccount());if(!selected)throw failure('account_scope_changed');\r\n   const ticket=transitions.capture();await flush();assertScope(ticket,selected);await fresh(ticket,selected);\r\n   const result=await api('/api/workouts/start','POST',{mode,goal},{'X-Target-Account':selected.owner,'X-Expected-Data-Epoch':String(selected.epoch)});\r\n   assertScope(ticket,selected);\r\n   if(typeof result.id!=='string'||!result.id||result.id.length>128||!Number.isSafeInteger(result.startedAt)||result.startedAt<0||result.targetAccountId!==selected.owner||result.dataEpoch!==selected.epoch)throw failure('invalid_workout_ticket');\r\n   const tickets=read(TICKET_KEY,{});\r\n   if(!tickets||typeof tickets!=='object'||Array.isArray(tickets)||Object.hasOwn(tickets,result.id))throw failure('account_ticket_conflict');\r\n   Object.defineProperty(tickets,result.id,{value:{id:result.id,targetAccountId:selected.owner,targetDataEpoch:selected.epoch,startedAt:result.startedAt},enumerable:true,writable:true,configurable:true});\r\n   write(TICKET_KEY,tickets);return result;\r\n  },\r\n  async complete(data){\r\n   const selected=scope(getAccount());if(!selected)throw failure('account_scope_changed');\r\n   const ticket=transitions.capture(),tickets=read(TICKET_KEY,{}),binding=Object.hasOwn(tickets,data.id)?tickets[data.id]:null;\r\n   if(binding&&(binding.targetAccountId!==selected.owner||binding.targetDataEpoch!==selected.epoch))throw failure('account_scope_changed');\r\n   if(!binding){const unbound=read(UNBOUND_KEY,[]);if(!Array.isArray(unbound))throw failure('account_queue_unavailable');if(!unbound.some(row=>row.id===data.id)){assertScope(ticket,selected);write(UNBOUND_KEY,[...unbound,{id:data.id,value:data.value,...(data.active===undefined?{}:{active:data.active})}]);}throw failure('account_queue_unbound');}\r\n   const queue=pending(selected.owner);\r\n   if(!queue.some(row=>row.id===data.id)){\r\n    // An unrecognized legacy ticket is retained without invented account epoch.\r\n    const item={id:data.id,value:data.value,...(data.active===undefined?{}:{active:data.active}),...(binding?{targetAccountId:binding.targetAccountId,targetDataEpoch:binding.targetDataEpoch}:{})};\r\n    assertScope(ticket,selected);write(accountPendingKey(selected.owner),[...queue,item]);\r\n   }\r\n   await flush();assertScope(ticket,selected);\r\n   if(pending(selected.owner).some(row=>row.id===data.id))throw failure('account_completion_pending');\r\n  },\r\n });\r\n}\r\n","achievements-board.css":"﻿/* Achievements / battle-pass board, styled after Skyrim's skill constellations: dim stars until earned,\r\n   a slow camera glide into the chosen boss, thin all-caps text with a hairline rule. */\r\n@font-face{font-family:'Barlow Condensed';src:url('/handborne/fonts/barlow-400.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n.ach-board{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:0;border:0;background:#05030c;color:#fff;overflow:hidden;font:16px/1.4 'Barlow Condensed',var(--font,system-ui);letter-spacing:.04em}\r\n.ach-board[open]{display:grid;place-items:center}\r\n.ach-board::backdrop{background:#000}\r\n.ach-stage{position:relative;aspect-ratio:1141/2000;height:min(100dvh,calc(100vw * 2000 / 1141))}\r\n/* Art, bosses and the three star layers each carry their own zoom transform (parallax factor per layer). */\r\n.ach-parallax{transition:transform .9s cubic-bezier(.22,.61,.36,1);will-change:transform}\r\n.ach-art{position:absolute;inset:0;width:100%;height:100%;display:block;user-select:none;-webkit-user-drag:none}\r\n.ach-stars{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;transition:opacity .5s}\r\n.ach-stars i{position:absolute;width:3px;height:3px;border-radius:50%;background:#fff;box-shadow:0 0 6px 1px #fff;opacity:0;animation:ach-star 3s ease-in-out infinite alternate;animation-delay:var(--d)}\r\n.ach-bosses{position:absolute;inset:0}\r\n.ach-boss{position:absolute;margin:0;padding:0;border:0;background:none;border-radius:14px;cursor:pointer;-webkit-tap-highlight-color:transparent}\r\n.ach-boss:focus-visible{outline:2px solid var(--glow);outline-offset:4px}\r\n/* Beaten bosses: the same slice of the art screened over itself, once blurred (bloom) and once sharp, feathered so no rectangle shows. */\r\n.ach-boss[data-state=done]::before,.ach-boss[data-state=done]::after{content:'';position:absolute;inset:0;pointer-events:none;background:url(/pod/worlds/achievements.jpg) no-repeat;background-size:calc(10000% / var(--bw)) auto;background-position:calc(var(--bx) / (100 - var(--bw)) * 100%) calc(var(--by) / (100 - var(--bh)) * 100%);mix-blend-mode:screen;-webkit-mask:radial-gradient(ellipse at center,#000 45%,transparent 74%);mask:radial-gradient(ellipse at center,#000 45%,transparent 74%);animation:ach-twinkle 2.6s ease-in-out infinite alternate;animation-delay:var(--d)}\r\n.ach-boss[data-state=done]::before{filter:blur(10px) brightness(1.7) saturate(1.4)}\r\n.ach-boss[data-state=done]::after{filter:brightness(1.35) saturate(1.2)}\r\n.ach-boss[data-state=locked]{background:radial-gradient(ellipse at center,#03020ce8 40%,#03020c99 60%,transparent 78%)}\r\n.ach-boss.selected{background:radial-gradient(ellipse at center,#ffffff38 12%,transparent 68%);animation:ach-pulse 1.4s ease-in-out infinite alternate}\r\n.ach-head{position:absolute;top:max(14px,env(safe-area-inset-top));left:18px;right:72px;text-transform:uppercase;text-shadow:0 0 10px #000,0 1px 2px #000;transition:opacity .4s}\r\n.ach-head h1{margin:0;font-size:22px;font-weight:400;letter-spacing:.2em}\r\n.ach-head p{margin:2px 0 0;font-size:14px;letter-spacing:.14em;color:#cfc8e6}\r\n.ach-head::after{content:'';display:block;height:1px;margin-top:8px;background:linear-gradient(90deg,#fffc,transparent)}\r\n.ach-board.zoomed .ach-head{opacity:0}\r\n/* Zoomed: dim the far/mid star layers so the detail card stays legible; near debris keeps flying over it. */\r\n.ach-board.zoomed [data-depth=far],.ach-board.zoomed [data-depth=mid]{opacity:.5}\r\n.ach-detail{position:absolute;left:0;right:0;bottom:0;box-sizing:border-box;max-height:62%;padding:34px 24px calc(24px + env(safe-area-inset-bottom));overflow:auto;overscroll-behavior:contain;text-align:center;background:linear-gradient(#0000,#05030cd9 24%,#05030cf5);animation:ach-rise .5s ease}\r\n.ach-detail[hidden]{display:none}\r\n.ach-detail h2{margin:0;font-size:36px;font-weight:400;letter-spacing:.16em;text-transform:uppercase;text-shadow:0 0 18px var(--glow),0 0 2px #fff}\r\n.ach-detail hr{width:min(320px,80%);height:1px;border:0;margin:10px auto 12px;background:linear-gradient(90deg,transparent,#fff,transparent)}\r\n.ach-detail .ach-status{margin:0 0 16px;font-size:16px;letter-spacing:.12em;text-transform:uppercase;color:#d9d2ee}\r\n.ach-detail ol{list-style:none;margin:0 auto;padding:0;display:grid;gap:8px;width:min(360px,100%);text-align:left}\r\n.ach-detail li{display:grid;grid-template-columns:18px 70px 1fr;gap:10px;align-items:baseline;font-size:17px}\r\n/* \\2726 (a CSS numeric escape, not the raw UTF-8 bytes) so the glyph can't mojibake if this file\r\n   is ever served/read without an explicit UTF-8 charset — the escape is pure ASCII in the source. */\r\n.ach-detail li::before{content:'\\2726';color:#5e5878;font-size:12px}\r\n.ach-detail li[data-step=done]::before{color:#fff;text-shadow:0 0 8px var(--glow),0 0 14px var(--glow)}\r\n.ach-detail li[data-step=next]::before{color:var(--glow);animation:ach-twinkle 1.2s ease-in-out infinite alternate}\r\n.ach-detail li[data-step=todo]{color:#8f89a8}\r\n.ach-detail li i{font-style:normal}\r\n.ach-detail li b{font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:#e9e3ff}\r\n.ach-back{margin-top:18px;min-height:44px;padding:0 6px;border:0;border-bottom:1px solid #fff9;background:none;color:#fff;font:400 17px/1 'Barlow Condensed',var(--font,system-ui);letter-spacing:.2em;text-transform:uppercase}\r\n.ach-close{position:absolute;top:max(12px,env(safe-area-inset-top));right:12px;width:44px;height:44px;border-radius:50%;border:1px solid #ffffff55;background:#05030ccc;color:#fff;font-size:18px}\r\n@keyframes ach-twinkle{from{opacity:.5}to{opacity:1}}\r\n@keyframes ach-pulse{from{opacity:.45}to{opacity:1}}\r\n@keyframes ach-star{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:scale(1.3)}}\r\n@keyframes ach-rise{from{transform:translateY(24px);opacity:0}}\r\n@media (prefers-reduced-motion:no-preference){\r\n/* The locked intake theme turns off every animation; the board twinkles for everyone. */\r\nhtml[data-public-state] .ach-board .ach-stars i{animation:ach-star 3s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] .ach-board .ach-boss.selected{animation:ach-pulse 1.4s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] .ach-board .ach-detail{animation:ach-rise .5s ease!important}\r\nhtml[data-public-state] .ach-board .ach-parallax{transition:transform .9s cubic-bezier(.22,.61,.36,1)!important}\r\n}\r\n/* Reduced motion: no drift/debris flight, no rAF loop (gated in JS); dots sit at .8 opacity, same as before. */\r\n@media (prefers-reduced-motion:reduce){.ach-board .ach-parallax{transition:none!important}.ach-board *,.ach-board ::before,.ach-board ::after{animation:none!important}.ach-stars i,.ach-stars[data-depth]{opacity:.8}}\r\n/* W2-2O: without the Starter pack's art (offline, not downloaded): the bosses outlined on the starfield, and the pack offered. */\r\n.ach-starter{display:none}\r\n.ach-board.no-art .ach-art{visibility:hidden}\r\n.ach-board.no-art .ach-boss:not([data-state=locked]){box-shadow:0 0 14px var(--glow),inset 0 0 0 1px var(--glow)}\r\n.ach-board.no-art:not(.zoomed) .ach-starter{position:absolute;left:16px;right:16px;bottom:max(16px,env(safe-area-inset-bottom));display:flex;flex-direction:column;align-items:center;gap:8px;padding:10px 14px;border-radius:12px;background:#05030ccc;text-align:center}\r\n/* Framed fullscreen (portal): the dock rides inside the dialog, so the offer sits above it, not under it. */\r\n.portal-framed.ach-board.no-art:not(.zoomed) .ach-starter,.portal-framed .ach-board.no-art:not(.zoomed) .ach-starter{bottom:calc(max(16px,env(safe-area-inset-bottom)) + var(--myr5-bar))}\r\n.ach-starter p{margin:0;font-size:15px;letter-spacing:.08em;color:#d9d2ee}\r\n.ach-starter button{min-height:44px;padding:0 18px;border:1px solid #fff9;border-radius:22px;background:#ffffff1f;color:#fff;font:inherit;font-size:15px;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}\r\n/* Coach captions, page buttons and the zoomed detail (requirements, exercises, weapon tier milestones). */\r\n.ach-name{position:absolute;left:-10%;width:120%;top:100%;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;pointer-events:none;text-align:center;font-size:clamp(5px,1.6vw,8px);line-height:1.05;letter-spacing:.03em;text-transform:uppercase;color:#fff;text-shadow:0 0 3px #000,0 0 6px #000}\r\n.ach-board.zoomed .ach-name{opacity:0}\r\n.ach-boss[data-golden=true] .ach-name{color:#ffe29e}\r\n.ach-page{position:absolute;top:50%;z-index:2;width:44px;height:88px;margin-top:-44px;border:1px solid #ffffff55;background:#05030ccc;color:#fff;font:400 34px/1 'Barlow Condensed',var(--font,system-ui);transition:opacity .4s}\r\n.ach-page[hidden]{display:none}.ach-page-prev{left:0;border-radius:0 22px 22px 0}.ach-page-next{right:0;border-radius:22px 0 0 22px}\r\n.ach-board.zoomed .ach-page{opacity:0;pointer-events:none}\r\n.ach-detail{text-align:left}.ach-detail h2,.ach-detail .ach-status{text-align:center}\r\n.ach-detail p{margin:6px auto;max-width:520px;font-size:16px;letter-spacing:.04em}.ach-detail strong{font-weight:400;color:#e9e3ff;letter-spacing:.12em;text-transform:uppercase}\r\n.ach-detail h3{margin:16px auto 6px;max-width:520px;font-size:18px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:#e9e3ff}\r\n.ach-detail h4{margin:10px auto 4px;max-width:520px;font-size:16px;font-weight:400;letter-spacing:.1em;text-transform:uppercase;color:#cfc8e6}\r\n.ach-detail ul{margin:0 auto;padding-left:20px;max-width:520px;font-size:15px}\r\n.ach-detail ol{width:min(520px,100%);gap:4px}.ach-detail li{grid-template-columns:18px 64px 1fr;font-size:15px}\r\n.ach-detail ul li{display:list-item}.ach-detail ul li::before{content:none}\r\n.ach-weapons{max-width:520px;margin:0 auto}\r\n.ach-tier-block{padding:2px 9px 9px;border-left:1px solid #ffffff30}\r\n.ach-tier-block[data-current=true]{border-color:var(--glow);background:linear-gradient(90deg,#ffffff12,transparent)}\r\n.ach-tier-block h5{margin:8px 0 6px;font-size:15px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:#cfc8e6}\r\n.ach-detail .ach-tier-block li{grid-template-columns:14px 58px 1fr;gap:6px;font-size:14px;line-height:1.25}\r\n.ach-back{display:block;margin:18px auto 0}\r\n/* Framed fullscreen (portal): ~50px persistent top chrome above, the dock below; the detail scrolls between them. */\r\n.portal-framed.ach-board .ach-head,.portal-framed .ach-board .ach-head{top:calc(54px + env(safe-area-inset-top))}\r\n.portal-framed.ach-board .ach-close,.portal-framed .ach-board .ach-close{top:calc(54px + env(safe-area-inset-top))}\r\n.portal-framed.ach-board .ach-detail,.portal-framed .ach-board .ach-detail{bottom:var(--myr5-bar,64px);max-height:calc(100% - var(--myr5-bar,64px) - 104px - env(safe-area-inset-top));padding-bottom:24px}\r\n.portal-framed.ach-board.has-pod-chrome .ach-detail{padding-bottom:24px}\r\n.portal-framed.ach-board .ach-page,.portal-framed .ach-board .ach-page{margin-top:-44px}\r\n@media (max-width:420px){.ach-detail{padding-left:14px;padding-right:14px}.ach-detail h2{font-size:29px}.ach-detail p{font-size:15px}}\r\n/* R-1017 one achievements page: the constellation sits at the top of the XP flight scroller (#battlePassPanel); the\r\n   flight path hangs under the golden coach. The board is a plain section there, sized by the art's aspect ratio. */\r\n.ach-board.ach-embed{position:relative;inset:auto;display:block;width:100%;height:auto;max-width:none;max-height:none;overflow:visible;background:#05030c;z-index:1}\r\n.ach-board.ach-embed .ach-stage{width:100%;height:auto;overflow:hidden}\r\n.ach-board.ach-embed .ach-head,.portal-framed .ach-board.ach-embed .ach-head{top:12px;right:18px}\r\n.ach-board.ach-embed .ach-head h1{font-size:20px}\r\n.ach-embed-detail{z-index:7;color:#fff;font:16px/1.4 'Barlow Condensed',var(--font,system-ui);letter-spacing:.04em}\r\n/* The requirements card rises inside the game screen, over the flight HUD. */\r\n.ach-embed-detail{max-height:66%;padding-bottom:20px}\r\n.launch-panel .ach-board .ach-head{display:block;min-height:0;margin:0;padding:0;border:0;background:none;font:16px/1.4 'Barlow Condensed',var(--font,system-ui)}\r\n.launch-panel .ach-board .ach-head h1{font-size:20px;line-height:1.2}\r\n.launch-panel .ach-board .ach-head p{font-size:13px;line-height:1.3}\r\n/* The section menu is part of the game screen: a compact CRT strip along the bottom of the flight. */\r\n.pass-crt-rack.pass-hud{position:relative;z-index:6;flex:none;box-sizing:border-box;height:var(--pass-hud-h);width:min(380px,calc(100% - 16px));grid-template-columns:40px minmax(0,1fr) 40px;margin:0 auto;padding:6px 0}\r\n/* R-1017: the game fills the panel. Three bands in the game frame: a slim top HUD (score, level bar, daily chip), the\r\n   scrolling flight, the CRT strip. The ship canvas covers only the middle band, so no scene label or node draws under a HUD. */\r\n#battlePassPanel .pass-map-shell{display:flex;flex-direction:column;--pass-top-h:58px;--pass-hud-h:100px;margin:4px 5px 5px}\r\n#battlePassPanel .pass-map-viewport{flex:1;min-height:0;height:auto}\r\n#battlePassPanel .pass-scene{top:var(--pass-top-h);bottom:auto;height:calc(100% - var(--pass-top-h) - var(--pass-hud-h))}\r\n#battlePassPanel .pass-top-hud{position:relative;z-index:7;flex:none;box-sizing:border-box;height:var(--pass-top-h);display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 8px;padding:4px 8px;border-bottom:1px solid #477785;background:#06101c}\r\n#battlePassPanel .pass-flight-score{grid-row:1;grid-column:1;justify-content:flex-start;gap:12px;padding:0;border:0;background:none;font-size:.66rem}\r\n#battlePassPanel .pass-summary{grid-row:2;grid-column:1/-1;display:flex;align-items:center;gap:8px;min-width:0;margin:0;padding:0;border:0;background:none}\r\n#battlePassPanel .pass-summary>*{grid-column:auto;grid-row:auto;flex:none}\r\n#battlePassPanel .pass-summary strong{font-size:.72rem}#battlePassPanel .pass-summary span,#battlePassPanel .pass-summary b{justify-self:start;font-size:.58rem;white-space:nowrap}#battlePassPanel .pass-summary progress{flex:1 1 auto;min-width:30px;width:auto;height:5px}\r\n#battlePassPanel .pass-daily{grid-row:1;grid-column:2;display:block;margin:0;padding:0;border:0;background:none}\r\n#battlePassPanel .pass-daily-chip{display:flex;gap:6px;align-items:center;min-height:24px;padding:2px 8px;border:1px solid #548b85;border-left:3px solid #86debb;border-radius:3px;background:#0d2230;color:#adf0c7;font-size:.6rem;letter-spacing:.06em;white-space:nowrap;cursor:pointer;list-style:none}\r\n#battlePassPanel .pass-daily-chip::-webkit-details-marker{display:none}\r\n#battlePassPanel .pass-daily-chip::after{content:'\\25BE';color:#86debb}\r\n#battlePassPanel .pass-daily-chip strong{color:#ffe4a0;font-size:.6rem}\r\n#battlePassPanel .pass-daily[open]{grid-area:auto;position:absolute;top:4px;right:8px;z-index:8;width:min(330px,calc(100% - 16px));box-sizing:border-box;padding:0 0 8px;border:1px solid #548b85;background:#0b1c2af7;box-shadow:0 8px 24px #000c}\r\n#battlePassPanel .pass-daily[open] .pass-daily-chip{border:0;border-bottom:1px solid #548b85;border-radius:0}\r\n#battlePassPanel .pass-daily[open] .pass-daily-chip::after{content:'\\25B4'}\r\n#battlePassPanel .pass-daily .pass-quests{display:flex;flex-wrap:wrap;justify-content:flex-start;gap:4px;padding:8px 8px 4px}\r\n#battlePassPanel .pass-daily small{display:block;padding:0 8px;font-size:.6rem;color:#b6d9e8}\r\n#battlePassPanel .pass-daily-chip:focus-visible{outline:3px solid #f8db83;outline-offset:2px}\r\n.pass-hud .pass-crt-entry{min-height:58px}\r\n#battlePassPanel:has(.ach-board.zoomed) .pass-hud,#battlePassPanel[data-hyper] .pass-hud .pass-crt-switch{visibility:hidden}\r\n#battlePassPanel[data-hyper] .pass-crt-entry{box-shadow:0 0 0 2px #b9faff,0 0 22px #60e6ff}\r\n/* The flight dialog is a .launch-panel, whose generic button skin must not box the constellation's tap targets. */\r\n.launch-panel .ach-board .ach-boss{min-height:0;padding:0;border:0;border-radius:14px;background:none;color:inherit}\r\n.launch-panel .ach-board .ach-boss[data-state=locked]{background:radial-gradient(ellipse at center,#03020ce8 40%,#03020c99 60%,transparent 78%)}\r\n.launch-panel .ach-board .ach-boss.selected{background:radial-gradient(ellipse at center,#ffffff38 12%,transparent 68%)}\r\n.launch-panel .ach-embed-detail .ach-back{border:0;border-bottom:1px solid #fff9;border-radius:0;background:none;color:#fff;font:400 17px/1 'Barlow Condensed',var(--font,system-ui);letter-spacing:.2em}\r\n","achievements-board.mjs":"import {COACHES,COACH_REQUIREMENTS,STARTER_COACH_IDS,PATH_INTRO_COACH_IDS,WEAPON_GROUPS,SHIP_REQUIREMENTS,CADENCE_MILESTONES,exerciseDifficulty} from './performance-catalog.mjs';\r\nimport {EXERCISES,FOCUS_GROUPS} from './exercise-library.mjs';\r\nimport {readPerformanceProgress,unlockStatus} from './performance-progress.mjs';\r\nimport {DIFFICULTIES} from './progression-rules.mjs';\r\nimport {SHIP_CATALOG} from './modules/ships/ship-catalog.mjs';\r\nimport {asteroid,rock} from './space-rocks.mjs';\r\nimport {paintStyleText} from './workout-symbols.mjs';\r\nexport {selectedTracks} from './battle-pass.mjs';\r\nexport const loadProgress=readPerformanceProgress;\r\nconst IMAGE='/pod/worlds/achievements.jpg';\r\nexport const GROUP_NAMES=Object.fromEntries([...FOCUS_GROUPS.map(g=>[g.id,g.name]),['meditation','Meditation']]);\r\nexport const BOSSES=COACHES.map(c=>({...c,name:c.label,requirement:COACH_REQUIREMENTS.find(r=>r.id===c.id)||null}));\r\nconst difficultyName=d=>d[0].toUpperCase()+d.slice(1);\r\nconst groupName=g=>GROUP_NAMES[g]||g;\r\nexport function coachRequirements(id){\r\n const coach=BOSSES.find(c=>c.id===id);if(!coach)return null;\r\n const r=coach.requirement;if(!r){const starter=STARTER_COACH_IDS.includes(id);return {starter,unlock:starter?'Available from the start':'Requirement pending placement.',gold:'Select this coach and complete a 10-minute uninterrupted hold to earn its golden version.',exercises:[]};}\r\n if(r.groups.includes('meditation'))return {starter:false,unlock:`Complete meditation on ${CADENCE_MILESTONES.meditationDays[DIFFICULTIES.indexOf(r.difficulty)]} separate days.`,gold:'Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.',exercises:[]};\r\n const exercises=Object.values(EXERCISES).filter(e=>r.groups.includes(e.group)&&exerciseDifficulty(e.id)===r.difficulty);\r\n const intro=Object.values(PATH_INTRO_COACH_IDS).includes(id)?`Choose ${r.groups.map(groupName).join(' / ')} as one of your two starting workout paths OR `:'';\r\n const kinds=[...new Set(exercises.map(e=>e.kind))],options=[];\r\n if(kinds.includes('hold'))options.push('hold for 5 uninterrupted minutes');\r\n if(kinds.includes('reps'))options.push('complete 15 reps in one working set');\r\n if(r.groups.includes('cardio'))options.push('complete 5 sprint rounds or 5 active minutes of gentle cardio');\r\n else if(kinds.some(k=>k==='pace'||k==='steps'))options.push('complete 5 active minutes');\r\n return {starter:false,difficulty:r.difficulty,groups:r.groups,unlock:`${intro}${difficultyName(r.difficulty)} · ${r.groups.map(groupName).join(' / ')}: ${options.join(' OR ')}.`,gold:kinds.includes('hold')?'Complete a 10-minute uninterrupted hold at this difficulty in one of these groups. Breaks and difficulty changes restart the uninterrupted attempt.':'Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.',exercises:exercises.map(e=>e.name)};\r\n}\r\nexport function bossStates(progress=readPerformanceProgress()){\r\n return BOSSES.map(b=>({...b,state:(progress.coaches?.includes(b.id)||unlockStatus(progress).done)?'done':'locked',golden:progress.goldenCoaches?.includes(b.id)===true,requirements:coachRequirements(b.id)}));\r\n}\r\nexport function weaponRequirements(group){\r\n return {group,name:groupName(group),weapons:WEAPON_GROUPS[group]||[],blocks:DIFFICULTIES.map((difficulty,i)=>({difficulty,first:i*5+1,last:i*5+5})),hold:'1 uninterrupted minute: first tier in the difficulty block. 3 minutes: second tier. 5 minutes: coach clear; repeating it adds one tier. 10 minutes: finish the five-tier block and earn gold.',reps:'8 reps: first tier in the difficulty block. 12 reps: second tier. 15 reps: coach clear; repeating it adds one tier. One working set per muscle group per day.'};\r\n}\r\nexport const shipRequirements=()=>SHIP_REQUIREMENTS.map(r=>({...r,name:SHIP_CATALOG.find(s=>s.id===r.id)?.name||r.id,unlock:`Expert ${groupName(r.group)}: ${r.kind==='hold'?'5 uninterrupted minutes':'15 reps in one working set'}.`}));\r\n// Retained for decorative constellation consumers; roster ownership never uses zoom or defeats.\r\nexport function layerTransform(depth,{tx=0,ty=0,scale=1}={}){const f={far:.35,mid:.65,near:1.25}[depth]??1;return {tx:tx*f,ty:ty*f,scale:1+(scale-1)*f};}\r\n// Original constellation art slots (boxes are [x,y,w,h] in % of the art). `track` is the catalog track whose coaches sit in the row; null rows take the pool.\r\nexport const ART_ROWS=[\r\n {id:'strider',track:'chest',color:'#b48cff',boxes:[[10.96,9.7,10.78,8.8],[28.4,8.05,7.89,10.45],[37.51,9.2,11.92,9.3],[51.62,8.05,11.48,10.45],[63.37,8.05,12.8,10.45],[78.18,7.9,15.43,10.6]]},\r\n {id:'ringer',track:'quads',color:'#ff8a2a',boxes:[[20.16,18.05,7.89,8.1],[31.11,17.95,10.69,8.4],[44.26,17.95,9.55,8.25],[57.06,17.95,9.03,8.3],[69.76,17.95,11.13,8.5]]},\r\n {id:'manyarm',track:'glutes',color:'#7dff5a',boxes:[[22.17,26.65,8.33,8.6],[34.18,26.45,9.73,8.8],[46.54,27.25,10.08,8],[57.49,27.4,7.54,7.85],[65.64,27.2,12.62,8.05]]},\r\n {id:'wedge',track:'arms',color:'#39a8ff',boxes:[[23.4,34.7,10.52,7.8],[35.06,34.7,8.15,7.45],[43.21,34.7,11.48,7.75],[55.04,34.7,9.82,7.75],[65.21,34.7,11.22,7.75]]},\r\n {id:'warden',track:null,color:'#39a8ff',boxes:[[45.57,41.95,8.24,5.7]]},\r\n {id:'blob',track:'yoga',color:'#c65cff',boxes:[[26.29,47.25,10.87,6.95],[38.56,47.1,9.55,7.2],[49.87,47.1,7.8,6.95],[60.74,47.3,9.55,7.45]]},\r\n {id:'cap',track:'martial-arts',color:'#ff4fa0',boxes:[[33.04,54.6,7.62,7.4],[40.93,54.55,11.13,7.45],[53.46,54.65,10.78,7.35]]},\r\n {id:'stalk',track:'cardio',color:'#ff4a3d',boxes:[[30.5,61.45,7.01,7.5],[38.21,61.45,8.15,7.25],[49.52,61.45,9.29,7.5],[58.55,61.45,8.59,7.5]]},\r\n {id:'tanka',track:'meditation',color:'#ffd23a',boxes:[[29.27,68.65,9.38,7.85],[39.09,68.65,9.64,7.8],[48.47,68.45,10.6,8],[59.95,68.5,8.33,8]]},\r\n {id:'lume',track:null,color:'#ffc94a',boxes:[[39.35,75.95,15.34,8.8]]},\r\n];\r\nexport const ART_SLOTS=ART_ROWS.flatMap(row=>row.boxes.map((box,i)=>({id:`${row.id}-${i+1}`,row:row.id,color:row.color,box})));\r\n// One constellation contains every coach once, with each workout group on its own art row.\r\nexport function coachPages(coaches=BOSSES){\r\n const rank=id=>DIFFICULTIES.indexOf(COACH_REQUIREMENTS.find(r=>r.id===id)?.difficulty),queues=new Map(ART_ROWS.map(r=>[r.id,[]])),pool=[];\r\n for(const c of coaches){const track=COACH_REQUIREMENTS.find(r=>r.id===c.id)?.tracks?.[0],row=track&&ART_ROWS.find(r=>r.track===track);(row?queues.get(row.id):pool).push(c);}\r\n const shared=ART_ROWS.filter(row=>!row.track);pool.forEach((coach,i)=>queues.get(shared[i%shared.length].id).push(coach));\r\n const board=[];\r\n for(const row of ART_ROWS){const entries=queues.get(row.id);entries.sort((a,b)=>rank(a.id)-rank(b.id));if(!entries.length)continue;\r\n  const left=Math.min(...row.boxes.map(b=>b[0])),right=Math.max(...row.boxes.map(b=>b[0]+b[2])),top=Math.min(...row.boxes.map(b=>b[1])),height=Math.max(...row.boxes.map(b=>b[3])),width=(right-left)/entries.length;\r\n  entries.forEach((coach,i)=>board.push({coach,slot:{id:`${row.id}-${i+1}`,row:row.id,color:row.color,box:[left+i*width,top,width*.93,height]}}));\r\n }\r\n return board.length?[board]:[];\r\n}\r\n// All four ordered five-tier blocks are visible from each coach's zoom view.\r\nexport function weaponTierSteps(group,difficulty,owned=0){\r\n const index=DIFFICULTIES.indexOf(difficulty);if(index<0||!WEAPON_GROUPS[group])return [];\r\n const kinds=new Set(Object.values(EXERCISES).filter(e=>e.group===group).map(e=>e.kind));\r\n const pace=group==='boxing'||group==='cardio';\r\n const hasHold=kinds.has('hold'),hasReps=kinds.has('reps')||kinds.has('steps');\r\n const acts=[];\r\n if(hasHold)acts.push(['1 uninterrupted minute','3 uninterrupted minutes','5 uninterrupted minutes (coach clear)','Repeat a 5-minute hold','Repeat again, or hold 10 uninterrupted minutes']);\r\n if(hasReps)acts.push(['8 reps','12 reps','15 reps (coach clear)','Repeat 15 reps','Repeat 15 reps again']);\r\n if(pace)acts.push(group==='boxing'?['5 active minutes','Repeat 5 active minutes','Repeat 5 active minutes again','Repeat 5 active minutes again','Repeat 5 active minutes again']:['5 sprint rounds or 5 active minutes','Repeat a completed cardio set','Repeat a completed cardio set','Repeat a completed cardio set','Repeat a completed cardio set']);\r\n return Array.from({length:5},(_,i)=>({tier:index*5+i+1,text:acts.map(a=>a[i]).join(' · '),state:index*5+i+1<=owned?'done':index*5+i+1===owned+1?'next':'todo'}));\r\n}\r\n// dialog: the board's root (.ach-board); owner: the <dialog> that opens/closes it. R-1017: the board can live embedded at the\r\n// top of the XP flight page (embedAchievements), where the root is a plain section and the flight page's dialog is the owner.\r\nlet dialog,owner,embedded=false,stageVisible=true,stage,detail,artEl,bossesEl,layers,raf=0,t0=0,far,near,pages=[],page=0,selected=null;\r\nconst prefersReducedMotion=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\nfunction element(tag,text,className){const el=document.createElement(tag);if(text)el.textContent=text;if(className)el.className=className;return el;}\r\nfunction line(label,text){const p=element('p');p.append(element('strong',`${label} `),paintStyleText(element('span'),text));return p;}\r\nconst rand=(a,b)=>a+Math.random()*(b-a);\r\nconst wrap=v=>v-Math.floor(v);\r\n// Far layer (unit 0..1 space, positions wrap): a dust field plus a few slow drifting stars and dark asteroids.\r\nfunction makeFar(){\r\n return {\r\n  dust:Array.from({length:60},()=>({x:Math.random(),y:Math.random(),r:rand(.5,1.2),a:rand(.3,.85)})),\r\n  stars:Array.from({length:Math.round(rand(4,6))},()=>({x:Math.random(),y:Math.random(),vx:rand(.01,.025)*(Math.random()<.5?-1:1),vy:rand(.005,.015)*(Math.random()<.5?-1:1),r:rand(1.1,1.8)})),\r\n  rocks:Array.from({length:Math.round(rand(2,3))},()=>({x:Math.random(),y:Math.random(),vx:rand(.004,.012)*(Math.random()<.5?-1:1),vy:rand(.002,.007)*(Math.random()<.5?-1:1),...asteroid(9,16)})),\r\n };\r\n}\r\n// Near layer: 3-5 debris chunks, each a straight crossing of the art that loops every 6-14s.\r\nfunction makeNear(){\r\n return Array.from({length:Math.round(rand(3,5))},()=>{\r\n  const alongX=Math.random()<.5,fwd=Math.random()<.5,a=fwd?-.2:1.2,b=fwd?1.2:-.2;\r\n  return {x0:alongX?a:rand(-.1,1.1),y0:alongX?rand(-.1,1.1):a,x1:alongX?b:rand(-.1,1.1),y1:alongX?rand(-.1,1.1):b,dur:rand(6,14),phase:Math.random(),...asteroid(16,30)};\r\n });\r\n}\r\nfunction drawFar(elapsed){\r\n const {ctx,w,h,field}=far,t=elapsed/1000;\r\n ctx.clearRect(0,0,w,h);ctx.fillStyle='#fff';\r\n for(const d of field.dust){ctx.globalAlpha=d.a;ctx.beginPath();ctx.arc(d.x*w,d.y*h,d.r,0,7);ctx.fill();}\r\n ctx.globalAlpha=1;ctx.shadowColor='#fff';\r\n for(const s of field.stars){ctx.shadowBlur=6;ctx.beginPath();ctx.arc(wrap(s.x+s.vx*t)*w,wrap(s.y+s.vy*t)*h,s.r,0,7);ctx.fill();}\r\n ctx.shadowBlur=0;\r\n for(const a of field.rocks)rock(ctx,wrap(a.x+a.vx*t)*w,wrap(a.y+a.vy*t)*h,a.r,a.rot+a.vr*t,a.verts,.8);\r\n}\r\nfunction drawNear(elapsed){\r\n const {ctx,w,h,field}=near,t=elapsed/1000;\r\n ctx.clearRect(0,0,w,h);\r\n for(const p of field){const u=wrap(t/p.dur+p.phase);rock(ctx,(p.x0+(p.x1-p.x0)*u)*w,(p.y0+(p.y1-p.y0)*u)*h,p.r,p.rot+t*p.vr,p.verts,.85);}\r\n}\r\nfunction draw(elapsed){drawFar(elapsed);drawNear(elapsed);}\r\n// devicePixelRatio capped at 2; canvases are sized in real CSS px via setTransform so draw math stays unit-space.\r\nfunction fitLayers(){\r\n const r=stage.getBoundingClientRect(),dpr=Math.min(devicePixelRatio||1,2);\r\n for(const layer of [far,near]){\r\n  const c=layer.canvas;c.width=Math.max(1,Math.round(r.width*dpr));c.height=Math.max(1,Math.round(r.height*dpr));\r\n  layer.ctx=c.getContext('2d');layer.ctx.setTransform(dpr,0,0,dpr,0,0);layer.w=r.width;layer.h=r.height;\r\n }\r\n}\r\n// One rAF loop for both canvases; stopped on dialog close and while the tab is hidden.\r\nfunction startLoop(){\r\n if(raf||prefersReducedMotion()||!owner?.open||document.hidden)return;\r\n // Embedded: the drift layers only paint while the constellation is on screen (the flight game owns the frame budget).\r\n const tick=now=>{raf=requestAnimationFrame(tick);if(stageVisible)draw(now-t0);};\r\n raf=requestAnimationFrame(tick);\r\n}\r\nfunction stopLoop(){if(raf)cancelAnimationFrame(raf);raf=0;}\r\nfunction build(host,detailHost=host){\r\n embedded=!!host;\r\n dialog=document.createElement(host?'section':'dialog');dialog.className=host?'ach-board ach-embed':'ach-board';dialog.setAttribute('aria-label','Achievements');owner=host||dialog;\r\n stage=element('div',null,'ach-stage');\r\n artEl=element('img',null,'ach-art ach-parallax');artEl.src=IMAGE;artEl.alt='Coach constellation';artEl.draggable=false;\r\n const layer=(tag,depth)=>{const el=element(tag,null,'ach-stars ach-parallax');el.dataset.depth=el.dataset.peerDepth=depth;el.setAttribute('aria-hidden','true');return el;};\r\n layers={far:layer('canvas','far'),mid:layer('div','mid'),near:layer('canvas','near')};\r\n for(let i=0;i<40;i++){const dot=document.createElement('i');dot.style.cssText=`left:${(Math.random()*100).toFixed(1)}%;top:${(Math.random()*100).toFixed(1)}%;--d:${(Math.random()*4).toFixed(2)}s`;layers.mid.append(dot);}\r\n bossesEl=element('div',null,'ach-bosses ach-parallax');\r\n stage.append(artEl,layers.far,layers.mid,bossesEl,layers.near);\r\n const head=element('header',null,'ach-head');head.append(element('h1','Achievements'),element('p',null,'ach-count'));\r\n detail=element('section',null,'ach-detail');detail.hidden=true;\r\n const starter=element('div',null,'ach-starter'),offer=element('button','Download the Starter pack');offer.type='button';starter.append(element('p','The constellation art comes with the Starter pack.'),offer);\r\n const close=element('button','✕','ach-close');close.type='button';close.setAttribute('aria-label','Close');\r\n if(host){detail.classList.add('ach-embed-detail');dialog.append(stage,head,starter);detailHost.append(detail);}\r\n else{dialog.append(stage,head,detail,starter,close);document.body.append(dialog);}\r\n // W2-2O: the constellation is Starter-pack art. Without it (offline, not downloaded) the coaches glow on the plain\r\n // starfield and the pack is offered (achievements-board.css .no-art).\r\n artEl.addEventListener('error',()=>dialog.classList.add('no-art'),{once:true});\r\n offer.onclick=()=>window.myr5Packs?.open('starter');\r\n far={canvas:layers.far,field:makeFar()};near={canvas:layers.near,field:makeNear()};t0=performance.now();\r\n close.onclick=()=>dialog.close();\r\n stage.addEventListener('click',e=>{if(dialog.classList.contains('zoomed')&&!e.target.closest('.ach-boss'))unzoom();});\r\n // Escape peels off the zoom first; a second Escape closes.\r\n owner.addEventListener('cancel',e=>{if(dialog.classList.contains('zoomed')){e.preventDefault();unzoom();}});\r\n owner.addEventListener('close',()=>{unzoom();stopLoop();});\r\n if(host)new IntersectionObserver(([e])=>{stageVisible=e.isIntersecting;}).observe(stage);\r\n new ResizeObserver(()=>{if(owner.open&&!document.hidden){fitLayers();draw(performance.now()-t0);}}).observe(stage);\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden)stopLoop();});\r\n addEventListener('pagehide',stopLoop);\r\n for(const type of ['myr5:performance-progress','myr5:account-ready','myr5:account-cleared'])addEventListener(type,()=>{if(owner.open&&!dialog.classList.contains('zoomed'))paint();});\r\n}\r\nfunction paint(){\r\n const state=loadProgress(),byId=new Map(bossStates(state).map(b=>[b.id,b]));\r\n pages=coachPages();page=Math.min(page,Math.max(0,pages.length-1));\r\n bossesEl.replaceChildren();\r\n dialog.querySelector('.ach-count').textContent=`${state.coaches.length} / ${BOSSES.length} coaches · ${state.goldenCoaches.length} golden${pages.length>1?` · page ${page+1} / ${pages.length}`:''}`;\r\n for(const btn of dialog.querySelectorAll('.ach-page'))btn.hidden=pages.length<2;\r\n for(const {slot,coach} of pages[page]||[]){\r\n  const b=byId.get(coach.id),[x,y,w,h]=slot.box,btn=element('button',null,'ach-boss');btn.type='button';btn.dataset.state=b.state;btn.dataset.id=b.id;btn.dataset.golden=String(b.golden);\r\n  btn.style.cssText=`left:${x}%;top:${y}%;width:${w}%;height:${h}%;--bx:${x};--by:${y};--bw:${w};--bh:${h};--glow:${slot.color};--d:${(x*7%3).toFixed(2)}s`;\r\n  btn.setAttribute('aria-label',`${b.name}, ${b.golden?'unlocked, golden':b.state==='done'?'unlocked':'locked, tap to see requirements'}`);\r\n\r\n  btn.onclick=()=>zoom(b,slot,btn);\r\n  bossesEl.append(btn);\r\n }\r\n}\r\nfunction milestoneBlock(group,difficulty,weapons){\r\n const wrap=element('section',null,'ach-weapons'),rules=weaponRequirements(group);\r\n wrap.append(paintStyleText(element('h3'),`${groupName(group)} weapon progression`));\r\n for(const weapon of rules.weapons){\r\n  const owned=weapons[weapon]??0,item=element('div',null,'ach-weapon');\r\n  item.append(element('h4',`${weapon[0].toUpperCase()}${weapon.slice(1)} · tier ${owned} / 20`));\r\n  for(const block of rules.blocks){\r\n   const band=element('section',null,'ach-tier-block'),list=element('ol');band.dataset.current=String(block.difficulty===difficulty);\r\n   band.append(element('h5',`${difficultyName(block.difficulty)} · tiers ${block.first}–${block.last}${block.difficulty===difficulty?' · this coach':''}`));\r\n   for(const step of weaponTierSteps(group,block.difficulty,owned)){\r\n    const li=element('li');li.dataset.step=step.state;\r\n    li.append(element('b',`Tier ${step.tier}`),element('span',`${step.text} · ${step.state==='done'?'earned':'locked'}`));list.append(li);\r\n   }\r\n   band.append(list);item.append(band);\r\n  }\r\n  wrap.append(item);\r\n }\r\n return wrap;\r\n}\r\nfunction fillDetail(b,state){\r\n const r=b.requirements;detail.replaceChildren(element('h2',b.name),element('hr'),element('p',b.golden?'Unlocked · Golden':b.state==='done'?'Unlocked':'Locked','ach-status'),line('Unlock',r.unlock),line('Golden',r.gold));\r\n if(r.exercises.length){const ul=element('ul');for(const name of r.exercises)ul.append(element('li',name));detail.append(element('h3','Exercises'),ul);}\r\n if(r.difficulty)for(const group of r.groups)if(WEAPON_GROUPS[group])detail.append(milestoneBlock(group,r.difficulty,state.weapons));\r\n const back=element('button','Back','ach-back');back.type='button';back.onclick=unzoom;detail.append(back);\r\n}\r\nfunction zoom(b,slot,btn){\r\n stage.querySelector('.ach-boss.selected')?.classList.remove('selected');btn.classList.add('selected');selected=btn;\r\n // Embedded: bring the constellation's top into view first so the glide lands on screen.\r\n const scroller=embedded&&dialog.parentElement;if(scroller)scroller.scrollTop=dialog.offsetTop;\r\n const r=stage.getBoundingClientRect(),[x,y,w,h]=slot.box;\r\n const cx=r.width*(x+w/2)/100,cy=r.height*(y+h/2)/100,scale=Math.min(4,Math.max(2,r.height*.26/(r.height*h/100)));\r\n const tx=r.width/2-cx,ty=r.height*.3-cy;\r\n // Art + boss tap targets keep today's transform exactly, unscaled by depth, so nothing shifts under a finger.\r\n for(const el of [artEl,bossesEl]){el.style.transformOrigin=`${cx}px ${cy}px`;el.style.transform=`translate(${tx}px,${ty}px) scale(${scale})`;}\r\n for(const depth of ['far','mid','near']){\r\n  const t=layerTransform(depth,{tx,ty,scale}),el=layers[depth];\r\n  el.style.transformOrigin=`${cx}px ${cy}px`;el.style.transform=`translate(${t.tx}px,${t.ty}px) scale(${t.scale})`;\r\n }\r\n dialog.classList.add('zoomed');\r\n detail.style.setProperty('--glow',slot.color);\r\n fillDetail(b,loadProgress());\r\n detail.hidden=false;detail.scrollTop=0;detail.querySelector('.ach-back').focus({preventScroll:true});\r\n}\r\nfunction unzoom(){\r\n for(const el of [artEl,bossesEl,layers?.far,layers?.mid,layers?.near])if(el)el.style.transform='';\r\n const wasZoomed=dialog.classList.contains('zoomed');\r\n dialog.classList.remove('zoomed');detail.hidden=true;stage.querySelector('.ach-boss.selected')?.classList.remove('selected');\r\n if(wasZoomed&&owner.open&&selected?.isConnected)selected.focus({preventScroll:true});\r\n selected=null;\r\n}\r\n// Only an explicit open starts the loop; close, a hidden tab and pagehide stop it.\r\nexport function openAchievements(){\r\n if(embedded)return window.myr5Menus?.battlePass?.();\r\n if(!dialog)build();\r\n paint();\r\n if(!dialog.open)dialog.showModal();\r\n fitLayers();draw(performance.now()-t0);startLoop();\r\n return dialog;\r\n}\r\n// R-1017: one achievements page. Builds the board into the flight page's dialog; the caller places .root in its scroller.\r\nexport function embedAchievements(host,detailHost=host){\r\n if(!dialog)build(host,detailHost);\r\n return {root:dialog,stage,open(){paint();fitLayers();draw(performance.now()-t0);startLoop();},unzoom};\r\n}\r\n","actual-dj-commands.mjs":"/**\r\n * Typed commands understood by the local actual.dj deck controls.\r\n * @typedef {{action:'automix', enabled:boolean} | {action:'mix-next'} | {action:'fade', seconds:4|8|16} | {action:'deck-control', deck:'a'|'b', control:'play'|'pause'}} DJCommand\r\n */\r\n\r\nconst MAX_COMMAND_LENGTH=80;\r\n\r\n/**\r\n * Parse a small, explicit set of local deck commands. This function has no\r\n * browser, network, provider, speech-recognition, or evaluation side effects.\r\n * @param {unknown} text\r\n * @returns {DJCommand|null}\r\n */\r\nexport function parseDJCommand(text) {\r\n  if(typeof text!=='string'||text.length>MAX_COMMAND_LENGTH)return null;\r\n  const command=text.trim().toLowerCase().replace(/\\s+/g,' ');\r\n  if(command==='start auto dj')return {action:'automix',enabled:true};\r\n  if(command==='stop auto dj')return {action:'automix',enabled:false};\r\n  if(command==='mix next')return {action:'mix-next'};\r\n  const fade=command.match(/^fade (4|8|16) seconds$/);\r\n  if(fade)return {action:'fade',seconds:Number(fade[1])};\r\n  const deck=command.match(/^(play|pause) (?:deck )?([ab])$/);\r\n  if(deck)return {action:'deck-control',deck:deck[2],control:deck[1]};\r\n  return null;\r\n}\r\n","actual-dj-engine.mjs":"// MYR5 browser audio glue; manual/measured BPM, without native track analysis.\r\n// riseGain/fallGain below are JavaScript ports of BitChord's equal-power pair:\r\n// kushagrasinghx/BitChord, revision 2c599a6cd7a195227a11dab5769f88da8fd08194,\r\n// app/src/main/java/com/music/bitchord/playback/CrossfadeController.kt:2268-2273.\r\n// Upstream is GPL-3.0; preserve its attribution and license in third-party notices.\r\nconst clamp = (n, low, high) => Math.min(high, Math.max(low, n));\r\nconst riseGain = progress => Math.sin(clamp(progress, 0, 1) * Math.PI / 2);\r\nconst fallGain = progress => Math.cos(clamp(progress, 0, 1) * Math.PI / 2);\r\nconst numeric = (n, low, high) => Number.isFinite(Number(n)) ? clamp(Number(n), low, high) : low;\r\nconst validBpm = value => Number.isFinite(Number(value)) && Number(value) >= 30 && Number(value) <= 300;\r\n\r\nexport function createDJEngine({ onChange = () => {}, onError = () => {} } = {}) {\r\n  let context, masterNode, analyser, disposed = false, crossfader = 0, master = .8;\r\n  let transitionJob = null, transitionGeneration = 0, lastEmit = 0;\r\n  const decks = Object.fromEntries(['a', 'b'].map(id => [id, {\r\n    track: null, audio: null, nodes: null, listeners: [], generation: 0,\r\n    bpm: null, rate: 1, cue: 0, loopBeats: 0, loopStart: 0,\r\n    eq: { low: 0, mid: 0, high: 0 }, playing: false, playGeneration: 0,\r\n  }]));\r\n  const getDeck = id => {\r\n    if (disposed) throw new Error('DJ engine has been closed.');\r\n    if (!decks[id]) throw new TypeError('Deck must be a or b.');\r\n    return decks[id];\r\n  };\r\n  function snapshot() {\r\n    return { decks: Object.fromEntries(Object.entries(decks).map(([id, d]) => [id, {\r\n      track: d.track ? { ...d.track } : null, playing: d.playing,\r\n      position: Number.isFinite(d.audio?.currentTime) ? d.audio.currentTime : 0,\r\n      duration: Number.isFinite(d.audio?.duration) ? d.audio.duration : 0,\r\n      bpm: d.bpm, rate: d.rate, cue: d.cue, loopBeats: d.loopBeats,\r\n      eq: { ...d.eq },\r\n    }])), crossfader, master, mixing: Boolean(transitionJob) };\r\n  }\r\n  const emit = () => { if (!disposed) onChange(snapshot()); };\r\n  const fail = error => { if (!disposed) onError(error instanceof Error ? error : new Error(String(error))); };\r\n  function cancelTransition() {\r\n    transitionGeneration++;\r\n    if (transitionJob) {\r\n      clearInterval(transitionJob.timer);\r\n      transitionJob.resolve(false);\r\n      transitionJob = null;\r\n    }\r\n  }\r\n  function applyGains() {\r\n    for (const [id, d] of Object.entries(decks)) {\r\n      if (d.nodes) d.nodes.gain.gain.value = id === 'a' ? fallGain(crossfader) : riseGain(crossfader);\r\n    }\r\n    if (masterNode) masterNode.gain.value = master;\r\n  }\r\n  function ensureGraph(d) {\r\n    if (!context) {\r\n      const AudioContextClass = globalThis.AudioContext || globalThis.webkitAudioContext;\r\n      if (!AudioContextClass) throw new Error('This browser does not support DJ audio processing.');\r\n      context = new AudioContextClass();\r\n      masterNode = context.createGain();\r\n      analyser = context.createAnalyser();\r\n      analyser.fftSize = 2048;\r\n      analyser.smoothingTimeConstant = .75;\r\n      masterNode.connect(analyser);\r\n      analyser.connect(context.destination);\r\n    }\r\n    if (!d.nodes) {\r\n      const source = context.createMediaElementSource(d.audio);\r\n      const low = context.createBiquadFilter(), mid = context.createBiquadFilter();\r\n      const high = context.createBiquadFilter(), gain = context.createGain();\r\n      low.type = 'lowshelf'; low.frequency.value = 250;\r\n      mid.type = 'peaking'; mid.frequency.value = 1000; mid.Q.value = 1;\r\n      high.type = 'highshelf'; high.frequency.value = 4000;\r\n      source.connect(low); low.connect(mid); mid.connect(high); high.connect(gain); gain.connect(masterNode);\r\n      d.nodes = { source, low, mid, high, gain };\r\n      for (const band of ['low', 'mid', 'high']) d.nodes[band].gain.value = d.eq[band];\r\n    }\r\n    applyGains();\r\n  }\r\n  function release(d) {\r\n    d.generation++; d.playGeneration++;\r\n    d.playing = false;\r\n    if (d.audio) {\r\n      for (const [event, listener] of d.listeners) d.audio.removeEventListener(event, listener);\r\n      d.audio.pause();\r\n      d.audio.removeAttribute('src');\r\n      d.audio.load();\r\n    }\r\n    d.listeners = [];\r\n    if (d.nodes) for (const node of Object.values(d.nodes)) node.disconnect();\r\n    d.nodes = null; d.audio = null;\r\n  }\r\n  function load(id, track) {\r\n    const d = getDeck(id);\r\n    if (track === null) {\r\n      cancelTransition(); release(d); d.track = null; d.bpm = null;\r\n      d.rate = 1; d.cue = 0; d.loopBeats = 0; d.loopStart = 0;\r\n      emit(); return snapshot();\r\n    }\r\n    if (!track || typeof track.url !== 'string') throw new TypeError('Choose a playable track.');\r\n    // Only our bundled assets and browser-owned local blobs enter the mixer.\r\n    if (!/^(?:\\/audio\\/music\\/[a-z0-9-]+\\.(?:m4a|mp3|wav|ogg|aac|flac)|blob:[^\\s]+)$/i.test(track.url)) throw new TypeError('Choose a bundled track or import a local audio file.');\r\n    cancelTransition(); release(d);\r\n    d.track = { ...track }; d.bpm = validBpm(track.bpm) ? Number(track.bpm) : null;\r\n    d.rate = 1; d.cue = 0; d.loopBeats = 0; d.loopStart = 0;\r\n    const audio = new Audio();\r\n    audio.preload = 'metadata'; audio.crossOrigin = 'anonymous';\r\n    audio.playsInline = true; audio.setAttribute('playsinline', '');\r\n    audio.src = track.url;\r\n    audio.playbackRate = 1;\r\n    d.audio = audio;\r\n    const generation = d.generation;\r\n    for (const event of ['loadedmetadata', 'durationchange', 'play', 'pause', 'ended', 'error']) {\r\n      const listener = () => {\r\n        if (disposed || generation !== d.generation || d.audio !== audio) return;\r\n        if (event === 'play') d.playing = true;\r\n        if (event === 'pause' || event === 'ended' || event === 'error') d.playing = false;\r\n        if (event === 'error') { cancelTransition(); fail(new Error(`Could not load ${d.track.title || 'this track'}.`)); }\r\n        if (event === 'ended' && d.loopBeats && d.bpm) { seek(id, d.loopStart); void play(id); }\r\n        emit();\r\n      };\r\n      audio.addEventListener(event, listener); d.listeners.push([event, listener]);\r\n    }\r\n    audio.load(); emit(); return snapshot();\r\n  }\r\n  async function start(id, internal = false) {\r\n    const d = getDeck(id);\r\n    if (!d.audio) { fail(new Error('Load a track into this deck first.')); return false; }\r\n    if (!internal) cancelTransition();\r\n    const audio = d.audio, generation = d.generation, playGeneration = ++d.playGeneration;\r\n    try {\r\n      ensureGraph(d);\r\n      // Both calls occur before yielding: iOS requires the initiating user gesture.\r\n      const resumed = context.state === 'running' ? Promise.resolve() : context.resume();\r\n      const playing = audio.play();\r\n      await Promise.all([resumed, playing]);\r\n      if (disposed || d.audio !== audio || d.generation !== generation) { audio.pause(); return false; }\r\n      if (playGeneration !== d.playGeneration) return false;\r\n      d.playing = true; emit(); return true;\r\n    } catch (error) {\r\n      if (disposed || d.audio !== audio || d.generation !== generation || playGeneration !== d.playGeneration) return false;\r\n      audio.pause(); d.playing = false;\r\n      fail(new Error(`Playback could not start. Tap Play to retry. ${error.message || ''}`)); emit(); return false;\r\n    }\r\n  }\r\n  const play = id => start(id);\r\n  function pause(id) { const d = getDeck(id); cancelTransition(); d.playGeneration++; d.audio?.pause(); d.playing = false; emit(); }\r\n  const toggle = id => getDeck(id).playing ? (pause(id), Promise.resolve(false)) : play(id);\r\n  function seek(id, seconds) {\r\n    const d = getDeck(id);\r\n    if (!d.audio) return;\r\n    const duration = Number.isFinite(d.audio.duration) ? d.audio.duration : Number.MAX_SAFE_INTEGER;\r\n    d.audio.currentTime = numeric(seconds, 0, duration); emit();\r\n  }\r\n  function setCue(id) { const d = getDeck(id); d.cue = d.audio?.currentTime || 0; emit(); }\r\n  function cue(id) { const d = getDeck(id); if (d.playing) setCue(id); else seek(id, d.cue); }\r\n  function setLoop(id, beats) {\r\n    const d = getDeck(id), value = numeric(beats, 0, 64);\r\n    if (value && !d.bpm) { fail(new Error('Set a BPM before enabling a beat loop.')); return false; }\r\n    d.loopBeats = value; d.loopStart = d.audio?.currentTime || 0; emit(); return true;\r\n  }\r\n  function setBpm(id, bpm) {\r\n    const d = getDeck(id);\r\n    d.bpm = validBpm(bpm) ? Number(bpm) : null;\r\n    if (!d.bpm) d.loopBeats = 0;\r\n    emit();\r\n  }\r\n  function setRate(id, rate) {\r\n    const d = getDeck(id); d.rate = numeric(rate, .5, 2);\r\n    if (d.audio) { d.audio.playbackRate = d.rate; d.audio.preservesPitch = false; d.audio.webkitPreservesPitch = false; }\r\n    emit();\r\n  }\r\n  function setEQ(id, band, db) {\r\n    const d = getDeck(id);\r\n    if (!['low', 'mid', 'high'].includes(band)) throw new TypeError('EQ band must be low, mid or high.');\r\n    d.eq[band] = numeric(db, -24, 12);\r\n    if (d.nodes) d.nodes[band].gain.value = d.eq[band]; emit();\r\n  }\r\n  function setCrossfader(value) { getDeck('a'); cancelTransition(); crossfader = numeric(value, 0, 1); applyGains(); emit(); }\r\n  function setMaster(value) { getDeck('a'); master = numeric(value, 0, 1); applyGains(); emit(); }\r\n  function sync(id) {\r\n    const d = getDeck(id), other = decks[id === 'a' ? 'b' : 'a'];\r\n    if (!d.bpm || !other.bpm) { fail(new Error('Set BPM on both decks before syncing.')); return false; }\r\n    const rate = other.bpm * other.rate / d.bpm;\r\n    if (rate < .5 || rate > 2) { fail(new Error('These BPM values are outside the supported sync range.')); return false; }\r\n    setRate(id, rate); return true;\r\n  }\r\n  async function transition(seconds = 8) {\r\n    getDeck('a'); cancelTransition();\r\n    const token = transitionGeneration, from = crossfader;\r\n    const incoming = from < .5 ? 'b' : 'a', target = incoming === 'b' ? 1 : 0;\r\n    const d = decks[incoming], audio = d.audio, wasPlaying = d.playing, expectedPlayGeneration = d.playGeneration + 1;\r\n    if (!audio) { fail(new Error('Load a track into the incoming deck first.')); return false; }\r\n    if (!await start(incoming, true)) return false;\r\n    if (disposed || token !== transitionGeneration) {\r\n      if (!wasPlaying && d.audio === audio && d.playGeneration === expectedPlayGeneration) { audio.pause(); d.playing = false; emit(); }\r\n      return false;\r\n    }\r\n    const duration = numeric(seconds, .1, 120) * 1000, began = performance.now();\r\n    return new Promise(resolve => {\r\n      transitionJob = { resolve, timer: setInterval(() => {\r\n        if (disposed || token !== transitionGeneration) return cancelTransition();\r\n        const fraction = clamp((performance.now() - began) / duration, 0, 1);\r\n        crossfader = from + (target - from) * fraction; applyGains();\r\n        if (fraction === 1) { clearInterval(transitionJob.timer); transitionJob = null; resolve(true); }\r\n        emit();\r\n      }, 50) };\r\n      emit();\r\n    });\r\n  }\r\n  const ticker = setInterval(() => {\r\n    for (const [id, d] of Object.entries(decks)) {\r\n      if (!d.playing || !d.audio || !d.loopBeats || !d.bpm) continue;\r\n      const end = Math.min(d.loopStart + d.loopBeats * 60 / d.bpm,\r\n        Number.isFinite(d.audio.duration) ? d.audio.duration : Infinity);\r\n      if (end > d.loopStart && d.audio.currentTime >= end) {\r\n        d.audio.currentTime = d.loopStart + ((d.audio.currentTime - end) % (end - d.loopStart));\r\n      }\r\n    }\r\n    if (performance.now() - lastEmit >= 200 && Object.values(decks).some(d => d.playing)) {\r\n      lastEmit = performance.now(); emit();\r\n    }\r\n  }, 25);\r\n  function dispose() {\r\n    if (disposed) return;\r\n    cancelTransition(); disposed = true; clearInterval(ticker);\r\n    Object.values(decks).forEach(release);\r\n    masterNode?.disconnect(); analyser?.disconnect();\r\n    if (context && context.state !== 'closed') void context.close().catch(() => {});\r\n  }\r\n  return { load, play, pause, toggle, seek, cue, setCue, setLoop, setBpm, setRate, setEQ,\r\n    setCrossfader, setMaster, sync, transition, snapshot, getAnalyser: () => analyser || null, dispose };\r\n}\r\n","actual-dj-hand.mjs":"// Reuse the user's native Handborne recipe and pose rig. This hand only taps the\r\n// reserved footer corner; it never targets, follows or writes a music control.\r\nexport function deckBeat(deck) {\r\n if(!deck?.playing||!Number.isFinite(deck.bpm)||deck.bpm<=0||deck.bpm>400||!Number.isFinite(deck.position))return null;\r\n const beats=Math.max(0,deck.position)*deck.bpm/60;\r\n return {index:Math.floor(beats),phase:beats-Math.floor(beats)};\r\n}\r\nexport function handAction(state) {\r\n const id=state?.crossfader<.5?'a':'b',beat=deckBeat(state?.decks?.[id]);\r\n return {mode:beat?'corner-beat':'idle',beat};\r\n}\r\n\r\nexport function createDJHand(dialog,{getState=()=>null,loadRig=()=>import('/handborne/companion.mjs')}={}) {\r\n const shell=dialog.querySelector('.sp-shell'),host=document.createElement('div');\r\n host.className='dj-hand-overlay';host.hidden=true;host.setAttribute('aria-hidden','true');shell.append(host);\r\n let active=false,disposed=false,loading=false,attempted=false,fallbackMissing=false,rig=null,revision=0,frame=0,last=0,rendering=false;\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n function fallback(){\r\n  let family=11;try{const recipe=JSON.parse(localStorage.getItem('handborne-recipe-v4'));if(Number.isInteger(recipe?.sections?.palm)&&recipe.sections.palm>=0&&recipe.sections.palm<23)family=recipe.sections.palm;}catch{}\r\n  const img=document.createElement('img');img.alt='';img.src=`/handborne/previews/family-${String(family).padStart(2,'0')}.png?v=5`;\r\n  img.onerror=()=>{if(host.firstElementChild===img){fallbackMissing=true;host.hidden=true;shell.classList.remove('dj-corner-tapping');}};\r\n  host.replaceChildren(img);host.dataset.family=String(family);\r\n }\r\n async function load(){\r\n  if(loading||attempted||rig||disposed||!active)return;loading=true;attempted=true;const mine=revision;\r\n  try{const{createHandCompanion}=await loadRig();if(disposed||!active||mine!==revision)return;\r\n   host.replaceChildren();fallbackMissing=false;rig=createHandCompanion(host,{cropWrist:true,onStatus:text=>{host.dataset.ready=String(text==='Helping Hand ready');}});rig.gesture('point-at-you');rig.setActive(rendering);\r\n  }catch{if(!disposed&&active&&mine===revision){fallback();host.dataset.ready='false';}}\r\n  finally{loading=false;if(active&&!disposed&&mine!==revision)void load();}\r\n }\r\n function tick(now){\r\n  if(!active||disposed)return;frame=requestAnimationFrame(tick);if(now-last<40)return;last=now;\r\n  const action=handAction(getState()),tapping=!!action.beat&&!document.hidden;\r\n  if(tapping&&!attempted&&!loading){fallback();void load();}\r\n  host.hidden=!tapping||fallbackMissing;shell.classList.toggle('dj-corner-tapping',!host.hidden);\r\n  host.dataset.mode=tapping?'corner-beat':'idle';host.dataset.beat=tapping?String(action.beat.index):'';host.dataset.tempo=action.beat?'known':'unknown';host.dataset.target='';\r\n  if(tapping){const box=shell.getBoundingClientRect(),width=host.offsetWidth||64,height=host.offsetHeight||84,lift=reduced.matches?0:Math.sin(action.beat.phase*Math.PI)*7;\r\n   host.style.transform=`translate(${Math.max(0,box.width-width-8).toFixed(1)}px,${Math.max(0,box.height-height-4-lift).toFixed(1)}px)`;\r\n  }\r\n  if(tapping!==rendering){rig?.setActive(tapping);rendering=tapping;}\r\n }\r\n function stop(){revision++;cancelAnimationFrame(frame);frame=0;rendering=false;rig?.dispose();rig=null;host.hidden=true;host.dataset.ready='false';host.dataset.mode='idle';host.dataset.beat='';host.dataset.target='';shell.classList.remove('dj-corner-tapping');}\r\n return {\r\n  setActive(value){value=!!value&&!disposed;if(value===active)return;active=value;if(!active){stop();return;}attempted=false;fallbackMissing=false;last=0;frame=requestAnimationFrame(tick);},\r\n  dispose(){if(disposed)return;disposed=true;active=false;stop();host.remove();},\r\n };\r\n}\r\n","actual-dj-library.mjs":"// BPM values are measured values from audio/music/manifest.json, refreshed by list().\r\nexport const MYR5_DJ_TRACKS = Object.freeze([\r\n  ['hey-man-idk', 'Hey Man IDK', 188.43],\r\n  ['guarded-gate', 'Guarded Gate', 121.3],\r\n  ['daemon-time', 'Daemon Time', 90.16],\r\n  ['sick-with-science', 'Sick with Science', 140.5],\r\n  ['laboratory-violence', 'Laboratory Violence', 120],\r\n  ['main-theme-one', 'Main Theme One', 120],\r\n].map(([id, title, bpm]) => Object.freeze({ id, title, artist: 'MYR5', bpm,\r\n  url: `/audio/music/${id}.m4a`, local: false })));\r\n\r\nconst MAX_BYTES = 100 * 1024 * 1024;\r\nconst AUDIO_EXTENSION = /\\.(mp3|m4a|aac|wav|ogg|oga|opus|flac|aif|aiff|webm)$/i;\r\nconst DB_NAME = 'myr5-actual-dj';\r\n\r\nexport function createDJLibrary({ onError = () => {} } = {}) {\r\n  const records = new Map();\r\n  let db = null, disposed = false, ready = null, manifestReady = null;\r\n  let defaults = MYR5_DJ_TRACKS.map(track => ({ ...track }));\r\n  const report = error => { if (!disposed) onError(error instanceof Error ? error : new Error(String(error))); };\r\n  const assertOpen = () => { if (disposed) throw new Error('DJ library has been closed.'); };\r\n  const expose = record => ({ id: record.id, title: record.title, artist: record.artist,\r\n    url: record.url, bpm: record.bpm ?? null, local: true, size: record.blob.size,\r\n    persistent: record.persistent });\r\n  function add(record, persistent) {\r\n    if (!record || typeof record.id !== 'string' || !(record.blob instanceof Blob)) return;\r\n    if (!record.blob.size || record.blob.size > MAX_BYTES) return;\r\n    const previous = records.get(record.id);\r\n    if (previous) URL.revokeObjectURL(previous.url);\r\n    records.set(record.id, { ...record, persistent, url: URL.createObjectURL(record.blob) });\r\n  }\r\n  function transaction(mode, action) {\r\n    return new Promise((resolve, reject) => {\r\n      let tx;\r\n      try {\r\n        tx = db.transaction('tracks', mode);\r\n        tx.oncomplete = () => resolve();\r\n        tx.onerror = tx.onabort = () => reject(tx.error || new Error('Audio storage failed.'));\r\n        action(tx.objectStore('tracks'));\r\n      } catch (error) { reject(error); }\r\n    });\r\n  }\r\n  async function initialize() {\r\n    if (ready) return ready;\r\n    ready = (async () => {\r\n      try {\r\n        if (!globalThis.indexedDB) throw new Error('Local audio storage is unavailable. Imports will last for this session.');\r\n        const database = await new Promise((resolve, reject) => {\r\n          const request = indexedDB.open(DB_NAME, 1);\r\n          let failed = false;\r\n          request.onupgradeneeded = () => request.result.createObjectStore('tracks', { keyPath: 'id' });\r\n          request.onerror = () => { failed = true; reject(request.error || new Error('Local audio storage is unavailable.')); };\r\n          request.onblocked = () => { failed = true; reject(new Error('Audio storage is busy. Imports will last for this session.')); };\r\n          request.onsuccess = () => { if (failed || disposed) request.result.close(); resolve(request.result); };\r\n        });\r\n        if (disposed) { database.close(); return; }\r\n        db = database;\r\n        db.onversionchange = () => { db?.close(); db = null; };\r\n        await transaction('readonly', store => {\r\n          const request = store.getAll();\r\n          request.onsuccess = () => {\r\n            if (!disposed) for (const record of request.result) add(record, true);\r\n          };\r\n        });\r\n      } catch (error) { report(error); db?.close(); db = null; }\r\n    })();\r\n    return ready;\r\n  }\r\n  function readManifest() {\r\n    if (manifestReady) return manifestReady;\r\n    manifestReady = (async () => {\r\n      try {\r\n        const response = await fetch('/audio/music/manifest.json');\r\n        if (!response.ok) return;\r\n        const manifest = await response.json();\r\n        if (disposed) return;\r\n        defaults = defaults.map(track => {\r\n          const source = manifest.tracks?.[track.id];\r\n          // Never accept replacement URLs from cached or malformed metadata.\r\n          return source?.url === track.url && Number.isFinite(source.bpm) && source.bpm >= 30 && source.bpm <= 300\r\n            ? { ...track, bpm: source.bpm } : track;\r\n        });\r\n      } catch { /* Built-in metadata remains available offline. */ }\r\n    })();\r\n    return manifestReady;\r\n  }\r\n  async function list() {\r\n    assertOpen(); await Promise.all([initialize(), readManifest()]); assertOpen();\r\n    return [...defaults.map(track => ({ ...track })), ...Array.from(records.values(), expose)];\r\n  }\r\n  async function importFiles(files) {\r\n    assertOpen();\r\n    const supplied = Array.from(files || []);\r\n    const valid = [];\r\n    for (const file of supplied) {\r\n      if (!(file instanceof Blob) || !file.name || (!AUDIO_EXTENSION.test(file.name) && !/^audio\\//i.test(file.type))) {\r\n        report(new TypeError('Choose an audio file such as MP3, M4A, WAV or OGG.')); continue;\r\n      }\r\n      if (!file.size || file.size > MAX_BYTES) {\r\n        report(new RangeError(`${file.name}: audio files must be between 1 byte and 100 MB.`)); continue;\r\n      }\r\n      // An explicit non-audio MIME is rejected even if the filename looks like audio.\r\n      if (file.type && !/^audio\\//i.test(file.type) && !['application/octet-stream', 'video/webm'].includes(file.type)) {\r\n        report(new TypeError(`${file.name} is not an audio file.`)); continue;\r\n      }\r\n      valid.push(file);\r\n    }\r\n    await initialize(); assertOpen();\r\n    const imported = [];\r\n    for (const file of valid) {\r\n      const record = { id: `local-${globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(36).slice(2)}`}`,\r\n        title: file.name.replace(AUDIO_EXTENSION, ''), artist: 'Local audio',\r\n        blob: file, bpm: null };\r\n      add(record, false);\r\n      if (db) {\r\n        try {\r\n          await transaction('readwrite', store => store.put(record));\r\n          const saved = records.get(record.id);\r\n          if (saved) saved.persistent = true;\r\n        } catch (error) {\r\n          report(new Error(`Could not save ${file.name}. It remains playable for this session. ${error.message || ''}`));\r\n        }\r\n      }\r\n      if (disposed) throw new Error('DJ library has been closed.');\r\n      const current = records.get(record.id);\r\n      if (current) imported.push(expose(current));\r\n    }\r\n    return imported;\r\n  }\r\n  async function remove(id) {\r\n    assertOpen(); await initialize(); assertOpen();\r\n    const record = records.get(id);\r\n    if (!record) return false;\r\n    // Keep the playable record if a persisted delete fails; don't resurrect it silently on next load.\r\n    if (record.persistent) {\r\n      if (!db) throw new Error('Audio storage is unavailable. Reopen the DJ before deleting this saved track.');\r\n      await transaction('readwrite', store => store.delete(id));\r\n    }\r\n    assertOpen(); records.delete(id); URL.revokeObjectURL(record.url); return true;\r\n  }\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    for (const record of records.values()) URL.revokeObjectURL(record.url);\r\n    records.clear(); db?.close(); db = null;\r\n  }\r\n  return { list, importFiles, remove, dispose };\r\n}\r\n","actual-dj-playlist.mjs":"// Saved ordering only. Playback remains owned by each source's existing player.\r\nexport function reconcilePlaylist(order,localTracks,sourceLinks,makeId=()=>crypto.randomUUID()){\r\n const locals=new Map();for(const track of localTracks)locals.set(track.id,(locals.get(track.id)||0)+1);\r\n const sources=new Set(sourceLinks.map(entry=>entry.playlistId)),seen=new Set(),result=[];\r\n for(const ref of Array.isArray(order)?order:[]){if(!ref||typeof ref.id!=='string'||ref.id.length>100||seen.has(ref.id))continue;\r\n  if(ref.kind==='local'&&locals.get(ref.trackId)>0){locals.set(ref.trackId,locals.get(ref.trackId)-1);result.push({id:ref.id,kind:'local',trackId:ref.trackId});}\r\n  else if(ref.kind==='source-link'&&sources.delete(ref.sourceId))result.push({id:ref.id,kind:'source-link',sourceId:ref.sourceId});else continue;seen.add(ref.id);\r\n }\r\n for(const track of localTracks)if(locals.get(track.id)>0){result.push({id:makeId(),kind:'local',trackId:track.id});locals.set(track.id,locals.get(track.id)-1);}\r\n for(const entry of sourceLinks)if(sources.delete(entry.playlistId))result.push({id:makeId(),kind:'source-link',sourceId:entry.playlistId});\r\n return result;\r\n}\r\nexport function movePlaylistItem(order,id,step){const index=order.findIndex(ref=>ref.id===id),target=index+step;if(index<0||![-1,1].includes(step)||target<0||target>=order.length)return order;const next=order.slice();[next[index],next[target]]=[next[target],next[index]];return next;}\r\nexport function assignPlaylistIds(entries,makeId=()=>crypto.randomUUID()){\r\n const seen=new Set();return entries.map(entry=>{let id=entry.playlistId;if(typeof id!=='string'||!id||id.length>100||seen.has(id))id=makeId();seen.add(id);return {...entry,playlistId:id};});\r\n}\r\n","actual-dj-providers.mjs":"// Provider audio stays in the provider's official player. PCM mixing is for local audio.\r\nexport const DJ_PROVIDERS=Object.freeze([\r\n {id:'apple',name:'Apple Music',home:'https://music.apple.com/',hint:'Paste an Apple Music song, album, or playlist link.',mode:'Official player'},\r\n {id:'spotify',name:'Spotify',home:'https://open.spotify.com/',hint:'Paste a Spotify track, album, or playlist link.',mode:'Official player + account controls'},\r\n {id:'soundcloud',name:'SoundCloud',home:'https://soundcloud.com/',hint:'Paste a SoundCloud track or playlist link.',mode:'Official player'},\r\n {id:'youtube',name:'YouTube',home:'https://music.youtube.com/',hint:'Paste a YouTube video link.',mode:'Official video player'},\r\n {id:'tidal',name:'TIDAL',home:'https://tidal.com/',hint:'Paste a TIDAL track, album, or playlist link.',mode:'Official player'},\r\n {id:'deezer',name:'Deezer',home:'https://www.deezer.com/',hint:'Paste a Deezer track, album, or playlist link.',mode:'Official player'},\r\n {id:'bandcamp',name:'Bandcamp',home:'https://bandcamp.com/',hint:'Paste a Bandcamp album or track link.',mode:'Opens Bandcamp'},\r\n {id:'amazon',name:'Amazon Music',home:'https://music.amazon.com/',hint:'Paste an Amazon Music link.',mode:'Opens Amazon Music'},\r\n]);\r\nexport function providerLink(id,value){\r\n const provider=DJ_PROVIDERS.find(p=>p.id===id);if(!provider)return null;\r\n try{\r\n  const u=new URL(String(value).trim());\r\n  if(u.protocol!=='https:'||u.username||u.password||u.port||u.hash)return null;\r\n  let embed=null;\r\n  if(id==='apple'){\r\n   if(u.hostname!=='music.apple.com'||!/^\\/[a-z]{2}\\/(album|song|playlist)\\/[\\w%.,~!()'/-]+$/.test(u.pathname))return null;\r\n   embed='https://embed.music.apple.com'+u.pathname+(u.searchParams.get('i')&&/^\\d+$/.test(u.searchParams.get('i'))?'?i='+u.searchParams.get('i'):'');\r\n  }else if(id==='spotify'){\r\n   if(u.hostname!=='open.spotify.com')return null;\r\n   const match=u.pathname.match(/^\\/(?:intl-[a-z]{2}\\/)?(track|album|playlist)\\/([A-Za-z0-9]{22})\\/?$/);if(!match)return null;\r\n   embed=`https://open.spotify.com/embed/${match[1]}/${match[2]}`;\r\n  }else if(id==='soundcloud'){\r\n   if(!['soundcloud.com','www.soundcloud.com'].includes(u.hostname)||!/^\\/[\\w-]+\\/[\\w/-]+\\/?$/.test(u.pathname))return null;\r\n   embed='https://w.soundcloud.com/player/?'+new URLSearchParams({url:u.origin+u.pathname,auto_play:'false',show_artwork:'true'});\r\n  }else if(id==='youtube'){\r\n   let video;if(u.hostname==='youtu.be')video=u.pathname.slice(1);else if(['youtube.com','www.youtube.com','music.youtube.com'].includes(u.hostname))video=u.searchParams.get('v');else return null;\r\n   if(!/^[A-Za-z0-9_-]{11}$/.test(video||''))return null;\r\n   embed=`https://www.youtube-nocookie.com/embed/${video}?playsinline=1`;\r\n  }else if(id==='tidal'){\r\n   if(!['tidal.com','www.tidal.com','listen.tidal.com'].includes(u.hostname))return null;\r\n   const m=u.pathname.match(/^\\/(?:browse\\/)?(track|album|playlist)\\/(\\d+|[a-f0-9-]{36})\\/?$/);if(!m)return null;\r\n   embed=`https://embed.tidal.com/${m[1]}s/${m[2]}`;\r\n  }else if(id==='deezer'){\r\n   if(!['deezer.com','www.deezer.com'].includes(u.hostname))return null;\r\n   const m=u.pathname.match(/^\\/(?:[a-z]{2}\\/)?(track|album|playlist)\\/(\\d+)\\/?$/);if(!m)return null;\r\n   embed=`https://widget.deezer.com/widget/dark/${m[1]}/${m[2]}`;\r\n  }else if(id==='bandcamp'){\r\n   if(!u.hostname.endsWith('.bandcamp.com')||!/^\\/(album|track)\\/[\\w-]+\\/?$/.test(u.pathname))return null;\r\n  }else if(id==='amazon'){\r\n   if(!['music.amazon.com','music.amazon.co.uk','music.amazon.de','music.amazon.co.jp'].includes(u.hostname)||!/^\\/(albums|playlists|tracks|artists)\\/[A-Za-z0-9]+\\/?$/.test(u.pathname))return null;\r\n  }\r\n  return {provider:id,name:provider.name,url:u.href,embed};\r\n }catch{return null;}\r\n}\r\n\r\nconst LOCAL_SOURCE=Object.freeze({\r\n id:'local',name:'MYR5 + imported audio',route:'local-deck',\r\n capabilities:Object.freeze({localMix:true,autoDJ:true,manualHandoff:false,sourceAttributionRequired:false,controls:'local-deck'}),\r\n});\r\nconst PROVIDER_ROUTES=Object.freeze({\r\n apple:Object.freeze({route:'official-player',controls:'official-player'}),\r\n spotify:Object.freeze({route:'official-player',controls:'spotify-terminal-separate'}),\r\n soundcloud:Object.freeze({route:'official-player',controls:'official-player'}),\r\n youtube:Object.freeze({route:'official-player',controls:'official-player'}),\r\n tidal:Object.freeze({route:'official-player',controls:'official-player'}),\r\n deezer:Object.freeze({route:'official-player',controls:'official-player'}),\r\n bandcamp:Object.freeze({route:'external-link',controls:'external-app'}),\r\n amazon:Object.freeze({route:'external-link',controls:'external-app'}),\r\n});\r\n\r\n// Source picks describe where a link can be opened. Only local audio can enter\r\n// the Web Audio decks; the Spotify account/API surface remains its own route.\r\nexport function getDJSourceChoices(){\r\n return [LOCAL_SOURCE,...DJ_PROVIDERS.map(provider=>{\r\n  const route=PROVIDER_ROUTES[provider.id];\r\n  return Object.freeze({\r\n   id:provider.id,name:provider.name,home:provider.home,hint:provider.hint,mode:provider.mode,route:route.route,\r\n   capabilities:Object.freeze({\r\n    localMix:false,autoDJ:false,manualHandoff:true,sourceAttributionRequired:true,controls:route.controls,\r\n   }),\r\n  });\r\n })];\r\n}\r\n\r\n// Normalize a user-authored source link without resolving track metadata or\r\n// implying that a protected provider can play through the local mixer.\r\nexport function normalizeProviderSourceEntry(providerId,sourceUrl,label){\r\n if(typeof sourceUrl!=='string'||sourceUrl.length>2048)return null;\r\n if(typeof label!=='string'||label.length>120||/[\\u0000-\\u001f\\u007f]/.test(label))return null;\r\n const cleanLabel=label.trim();if(!cleanLabel)return null;\r\n const provider=DJ_PROVIDERS.find(item=>item.id===providerId);if(!provider)return null;\r\n const link=providerLink(providerId,sourceUrl);if(!link)return null;\r\n const choice=getDJSourceChoices().find(item=>item.id===providerId);\r\n return Object.freeze({\r\n  kind:'source-link',provider:providerId,label:cleanLabel,sourceUrl,\r\n  validatedUrl:link.url,embedUrl:link.embed,route:choice.route,capabilities:choice.capabilities,\r\n  attribution:Object.freeze({app:'actual.dj',provider:provider.name,sourceUrl}),\r\n });\r\n}\r\n","actual-dj-visualizer.mjs":"// Actual MilkDrop rendering: pinned, unmodified Butterchurn + original presets.\r\n// Provenance and MIT notices: ./vendor/butterchurn/README.md.\r\nlet libraryPromise;\r\nfunction script(path) {\r\n  return new Promise((resolve, reject) => {\r\n    const element = document.createElement('script');\r\n    element.src = new URL(path, import.meta.url).href;\r\n    element.onload = resolve;\r\n    element.onerror = () => { element.remove(); reject(Error('Visualizer library could not load.')); };\r\n    document.head.append(element);\r\n  });\r\n}\r\nfunction libraries() {\r\n  return libraryPromise ??= Promise.all([\r\n    script('./vendor/butterchurn/butterchurn-2.6.7.min.js'),\r\n    script('./vendor/butterchurn/presets-2.4.7.min.js'),\r\n  ]).then(() => ({\r\n    engine: window.butterchurn?.default ?? window.butterchurn,\r\n    presets: window.butterchurnPresets.getPresets(),\r\n  })).catch(error => { libraryPromise = null; throw error; });\r\n}\r\n\r\nconst SCENES = {\r\n  orbit: ['Flexi - mindblob mix', 'Flexi - mindblob [shiny mix]', 'Flexi - alien fish pond'],\r\n  tunnel: ['martin - glass corridor', 'martin - The Bridge of Khazad-Dum', 'martin - frosty caves 2'],\r\n  spectrum: ['Geiss - Thumb Drum', 'Geiss - Spiral Artifact', 'Geiss - Reaction Diffusion 2'],\r\n  scope: ['Eo.S. + Phat - cubetrace - v2', 'martin - chain breaker', 'Martin - liquid arrows'],\r\n  trails: ['Zylot - Paint Spill (Music Reactive Paint Mix)', 'Cope - The Neverending Explosion of Red Liquid Fire', 'martin - stormy sea (2010 update)'],\r\n};\r\n\r\nexport function createDJVisualizer(canvas, { getAnalyser = () => null, onStatus = () => {} } = {}) {\r\n  const motion = matchMedia('(prefers-reduced-motion: reduce)');\r\n  let running = false, disposed = false, failed = false, pending = false, frame = 0, last = 0;\r\n  let lib, viz, gl, source, gain, scene = 'orbit', palette = 0, react = 1, presetDirty = true;\r\n  let micStream, micContext, micAnalyser, micSource, micGeneration = 0;\r\n  let external = false, lastStatus = '', sizedRatio = 0;\r\n  const waveform = new Uint8Array(1024);\r\n  const status = text => { if (text !== lastStatus) { lastStatus = text; onStatus(text); } };\r\n  const selected = () => micAnalyser ?? getAnalyser();\r\n  const dimensions = () => {\r\n    const rect = canvas.getBoundingClientRect();\r\n    sizedRatio = Math.min(devicePixelRatio || 1, 1.5);\r\n    // Butterchurn 2.6.7 uses width/height directly for its output viewport;\r\n    // pixelRatio multiplies internal textures only. Apply our mobile cap once\r\n    // here and give its canonical sizing API the same backing-pixel dimensions.\r\n    return { width: Math.max(1, Math.round(rect.width * sizedRatio)), height: Math.max(1, Math.round(rect.height * sizedRatio)), pixelRatio: 1 };\r\n  };\r\n  function resize() {\r\n    if (!viz) return;\r\n    const { width, height, pixelRatio } = dimensions();\r\n    if (canvas.width === width && canvas.height === height) return;\r\n    canvas.width = width; canvas.height = height;\r\n    viz.setRendererSize(width, height, { pixelRatio });\r\n  }\r\n  const observer = new ResizeObserver(resize);\r\n  observer.observe(canvas);\r\n  window.addEventListener('resize', resize);\r\n\r\n  function detach() {\r\n    if (gain) {\r\n      try { source?.disconnect(gain); } catch {}\r\n      try { viz?.disconnectAudio(gain); } catch {}\r\n      gain.disconnect();\r\n    }\r\n    gain = null; source = null; external = false;\r\n  }\r\n  function bind(analyser) {\r\n    detach(); source = analyser;\r\n    // Butterchurn's native processor is tied to its first AudioContext. Its\r\n    // documented render(audioLevels) input handles a microphone in another\r\n    // context without reconnecting incompatible nodes or restarting WebGL.\r\n    external = analyser.context !== viz.audio.audioContext;\r\n    if (!external) {\r\n      gain = analyser.context.createGain(); gain.gain.value = react;\r\n      analyser.connect(gain); viz.connectAudio(gain);\r\n    }\r\n  }\r\n  function preset() {\r\n    const name = SCENES[scene][palette];\r\n    const original = lib.presets[name];\r\n    if (!original) throw Error(`Missing original Butterchurn preset: ${name}`);\r\n    // Upstream loadPreset adds default fields to shapes/waves; copy the\r\n    // official preset so one instance cannot mutate another's preset bank.\r\n    viz.loadPreset(structuredClone(original), motion.matches ? 0 : 1.2);\r\n    presetDirty = false;\r\n    status(`${micAnalyser ? 'Microphone' : 'MYR5 mix'} · Butterchurn · ${name}`);\r\n  }\r\n  async function initialize(analyser) {\r\n    if (pending || disposed || failed) return;\r\n    pending = true;\r\n    try {\r\n      lib = await libraries();\r\n      if (!running || disposed) return;\r\n      const current = selected();\r\n      if (!current || current.context.state === 'closed') return;\r\n      gl = canvas.getContext('webgl2', { alpha: false, antialias: false, depth: false, stencil: false, premultipliedAlpha: false });\r\n      if (!gl) throw Error('WebGL2 is unavailable. This device cannot run MilkDrop visuals.');\r\n      const size = dimensions();\r\n      canvas.width = size.width; canvas.height = size.height;\r\n      viz = lib.engine.createVisualizer(current.context, canvas, size);\r\n      viz.setInternalMeshSize(32, 24);\r\n      bind(current); resize(); preset();\r\n    } catch (error) {\r\n      failed = true; status(error.message || 'MilkDrop visualizer could not start.');\r\n    } finally { pending = false; }\r\n  }\r\n  function draw(now) {\r\n    if (!running || disposed) return;\r\n    frame = requestAnimationFrame(draw);\r\n    const interval = 1000 / (motion.matches ? 12 : 30);\r\n    if (now - last < interval) return;\r\n    const delta = last ? Math.min((now - last) / 1000, .1) : 1 / 30;\r\n    last = now;\r\n    const analyser = selected();\r\n    if (viz && sizedRatio !== Math.min(devicePixelRatio || 1, 1.5)) resize();\r\n    if (!analyser || analyser.context.state === 'closed') {\r\n      if (source) detach();\r\n      status('Choose local audio or microphone for live MilkDrop visuals.'); return;\r\n    }\r\n    if (!viz) { void initialize(analyser); return; }\r\n    if (failed) return;\r\n    try {\r\n      if (source !== analyser) { bind(analyser); presetDirty = true; }\r\n      if (presetDirty) preset();\r\n      // Reduced motion disables preset morphing and lowers frames to twelve.\r\n      // Butterchurn expects elapsedTime as a frame delta, not a playhead.\r\n      if (external) {\r\n        waveform.fill(128);\r\n        analyser.getByteTimeDomainData(waveform);\r\n        for (let i = 0; i < waveform.length; i++) waveform[i] = Math.max(0, Math.min(255, 128 + (waveform[i] - 128) * react));\r\n        viz.render({ elapsedTime: delta, audioLevels: { timeByteArray: waveform, timeByteArrayL: waveform, timeByteArrayR: waveform } });\r\n      } else viz.render({ elapsedTime: delta });\r\n    } catch (error) { failed = true; detach(); status(`MilkDrop stopped: ${error.message}`); }\r\n  }\r\n  function stopMic() {\r\n    micGeneration++;\r\n    if (source === micAnalyser) detach();\r\n    micStream?.getTracks().forEach(track => track.stop()); micStream = null;\r\n    micSource?.disconnect(); micSource = null; micAnalyser = null;\r\n    void micContext?.close().catch(() => {}); micContext = null;\r\n  }\r\n  async function microphone() {\r\n    if (disposed) return false;\r\n    stopMic(); const generation = micGeneration;\r\n    let stream, context;\r\n    try {\r\n      stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: false, noiseSuppression: false, autoGainControl: false }, video: false });\r\n      if (generation !== micGeneration || disposed) { stream.getTracks().forEach(track => track.stop()); return false; }\r\n      context = new (window.AudioContext || window.webkitAudioContext)();\r\n      micStream = stream; micContext = context;\r\n      micAnalyser = context.createAnalyser(); micAnalyser.fftSize = 1024;\r\n      micSource = context.createMediaStreamSource(stream); micSource.connect(micAnalyser);\r\n      // Nothing connects the microphone to destination: visuals only.\r\n      await context.resume();\r\n      if (generation !== micGeneration || disposed) return false;\r\n      presetDirty = true; status('Microphone → MilkDrop visuals only.'); return true;\r\n    } catch {\r\n      // An older permission request must not close a newer capture session.\r\n      const current = generation === micGeneration;\r\n      if (current) stopMic();\r\n      else { stream?.getTracks().forEach(track => track.stop()); void context?.close().catch(() => {}); }\r\n      if (!disposed && current) status('Microphone unavailable. Allow access or choose the MYR5 mix.');\r\n      return false;\r\n    }\r\n  }\r\n  return {\r\n    start() { if (running || disposed) return; running = true; last = 0; failed = false; frame = requestAnimationFrame(draw); },\r\n    stop() { running = false; cancelAnimationFrame(frame); stopMic(); detach(); },\r\n    microphone,\r\n    useMix() { stopMic(); presetDirty = true; status('MYR5 mix → live MilkDrop visuals.'); },\r\n    setScene(value) { if (SCENES[value]) { scene = value; presetDirty = true; } },\r\n    setReact(value) { react = Math.max(.1, Math.min(3, Number(value) || 1)); if (gain) gain.gain.value = react; },\r\n    // Butterchurn has no universal palette API. This cycles original presets\r\n    // within each scene family; it is an alternate look, not a recolor shader.\r\n    setPalette(value) { palette = ((Math.trunc(Number(value) || 0) % 3) + 3) % 3; presetDirty = true; },\r\n    dispose() {\r\n      disposed = true; running = false; cancelAnimationFrame(frame); stopMic(); detach(); observer.disconnect(); window.removeEventListener('resize', resize);\r\n      // Upstream has no destroy(). Losing the owned context releases textures,\r\n      // buffers/programs without deleting another engine's AudioContext.\r\n      gl?.getExtension('WEBGL_lose_context')?.loseContext(); viz = null; lib = null;\r\n    },\r\n  };\r\n}\r\n","actual-dj.css":".actual-dj{width:min(1100px,calc(100vw - 16px));max-width:1100px;background:#100b18;color:#efdbff;border-color:#b376ec;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;box-shadow:0 0 0 4px #231432,0 24px 80px #000d}\r\n.actual-dj .sp-shell{height:min(900px,calc(100dvh - 16px));grid-template-rows:auto auto minmax(0,1fr) auto}\r\n.actual-dj.portal-framed .sp-shell{height:100%;max-height:100%}\r\n.actual-dj .sp-top{padding:18px 24px;background:linear-gradient(110deg,#32173f,#100b18);border-color:#71437f}\r\n.actual-dj .sp-top h2{font-size:34px;letter-spacing:-.05em;text-transform:none;color:#ffb56f;text-shadow:none}\r\n.actual-dj .sp-top h2:after{content:' / MYR5';font-size:11px;letter-spacing:.12em;color:#c299d3}\r\n.actual-dj .sp-screen{background:radial-gradient(ellipse at top,#44235444,transparent 60%);padding:20px 24px 32px}\r\n.actual-dj .sp-shell footer{border-color:#4b3057;color:#b894c6}\r\n.actual-dj .dj-tabs{display:flex;gap:4px;padding:10px 20px;border-bottom:1px solid #4c315b;background:#150e21}\r\n.actual-dj button,.actual-dj select,.actual-dj input{font:inherit}\r\n.actual-dj button{min-height:44px;border:1px solid #76548d;border-radius:8px;padding:8px 12px;color:#efd8ff;background:#241731;box-shadow:none;touch-action:manipulation}\r\n.actual-dj button[aria-pressed=true]{color:#1c1025;background:#ffc07a;border-color:#ffc07a}\r\n.actual-dj button:hover{background:#49315d;color:#fff2e4}\r\n.actual-dj button:disabled{opacity:.55;cursor:wait}\r\n.actual-dj a{color:#ffb677}\r\n.actual-dj input:not([type=range]):not([type=file]),.actual-dj select{min-height:44px;min-width:0;border:1px solid #644777;border-radius:7px;background:#140c1e;color:#efdeff;padding:8px}\r\n.actual-dj input[type=range]{width:100%;accent-color:#ffb36d;min-height:32px;cursor:pointer}\r\n.actual-dj .dj-tabs button{flex:1;font-size:12px;letter-spacing:.08em}\r\n.actual-dj .sp-label{color:#c999ee;font-size:10px;letter-spacing:.12em}\r\n.actual-dj .dj-heading{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:14px}\r\n.dj-live{max-width:60%;font-size:10px;color:#ffbe78;text-align:right;overflow-wrap:anywhere}\r\n.dj-decks{display:grid;grid-template-columns:1fr 1fr;gap:18px}\r\n.dj-deck{padding:18px;border:1px solid #745382;border-radius:14px;background:linear-gradient(140deg,#302034,#16101f);min-width:0}\r\n.dj-deck[data-deck=b]{background:linear-gradient(140deg,#221d39,#15101f)}\r\n.dj-deck-source{font-size:9px;color:#aa8eb8;letter-spacing:.08em}\r\n.dj-record{display:grid;place-items:center;margin:8px auto 20px;width:clamp(110px,22vw,180px);aspect-ratio:1;border-radius:50%!important;border:8px solid #3d3145!important;background:repeating-radial-gradient(circle,#100b16 0 2px,#2f2236 2px 3px)!important;box-shadow:0 0 0 1px #91789d,0 12px 20px #0006!important;position:relative;overflow:hidden}\r\n.dj-record:after{content:'';width:3px;height:36%;background:#dac1de;position:absolute;top:1px;left:50%;transform-origin:bottom;opacity:.3}\r\n.dj-record span{display:grid;place-items:center;position:relative;z-index:1;width:45%;height:45%;border-radius:50%;background:#ffbd78;color:#22122c;font-size:26px;font-weight:900}\r\n.dj-deck[data-deck=b] .dj-record span{background:#b388ef}\r\n.dj-playing .dj-record{animation:dj-spin 6s linear infinite}\r\n.dj-title{display:block;font-size:18px;overflow-wrap:anywhere;color:#ffe1ba;min-height:26px}\r\n.dj-artist{display:block;font-size:11px;color:#bfa2cc;overflow-wrap:anywhere;min-height:22px}\r\n.dj-seek-wrap{display:block;margin-top:8px}.dj-time{display:block;font-size:10px;color:#bf9fd1;text-align:right}\r\n.dj-deck-controls{display:flex;gap:6px;margin:12px 0}.dj-deck-controls button{flex:1;padding:8px;font-size:11px}.dj-deck-controls .dj-play{background:#ffbb72;color:#281333;border-color:#ffbb72}\r\n.dj-tempo{display:flex;align-items:end;gap:8px}.dj-tempo label{display:flex;align-items:center;gap:8px;flex:1;font-size:10px}.dj-tempo input{width:100%;max-width:120px}.dj-tempo button{font-size:11px}\r\n.dj-rate-label{display:grid;grid-template-columns:auto 1fr 40px;gap:8px;align-items:center;font-size:10px;margin-top:8px}\r\n.dj-eq{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:8px}.dj-eq label{font-size:9px;text-align:center;color:#b99dc9;min-width:0}\r\n.dj-loop-label{display:flex;align-items:center;justify-content:space-between;font-size:10px;margin-top:10px;gap:8px}.dj-loop-label select{flex:1}\r\n.dj-mixer{padding:18px;background:#1a1025;border:1px solid #614473;border-radius:14px;margin:18px 0 24px}\r\n.dj-mixer>label{display:flex;align-items:center;gap:18px;font-weight:700}.dj-crossfader{height:42px}\r\n.dj-master-row{display:flex;align-items:center;gap:20px;margin-top:10px}.dj-master-row label{display:flex;align-items:center;gap:12px;flex:1;font-size:10px}.dj-master-row button{min-width:150px;background:#48305d}\r\n.dj-auto-row{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px}.dj-auto-row label{display:flex;align-items:center;gap:8px;font-size:10px}\r\n.dj-library,.dj-queue-section{padding:18px;border-radius:14px;border:1px solid #51375e;background:#160e20;margin:20px 0}.dj-import{position:relative;display:inline-flex;align-items:center;min-height:44px;padding:8px 12px;border:1px solid #9a6aae;border-radius:8px;font-size:11px;color:#ffc88b;cursor:pointer;overflow:hidden}.dj-import input{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%}\r\n.dj-filter{width:100%}.dj-library-status,.dj-provider-note,.dj-visual-reference{color:#ae90be;font-size:11px;line-height:1.6}\r\n.dj-track-list,.dj-queue,.dj-spotify-results,.dj-spotify-queue{list-style:none;margin:10px 0 0;padding:0}\r\n.dj-crate-row{display:flex;align-items:center;gap:6px;padding:10px 0;border-bottom:1px solid #3e294b}.dj-crate-info,.dj-crate-row>strong{flex:1;min-width:0;overflow-wrap:anywhere}.dj-crate-info strong,.dj-crate-info small{display:block}.dj-crate-info strong{font-size:12px;color:#ffe0bd}.dj-crate-info small{font-size:10px;color:#b799c8;margin-top:3px}.dj-crate-row button{min-width:42px;flex-shrink:0;padding:7px;font-size:11px}.dj-crate-row>a{padding:8px}\r\n.dj-queue-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:6px 0}.dj-queue-row span{font-size:12px;overflow-wrap:anywhere}.dj-empty{font-size:12px;color:#ac8abf;padding:16px 0}\r\n.dj-provider-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:20px 0}.dj-provider-grid button{font-size:12px;min-height:58px}\r\n.dj-source-form>label,.dj-spotify-search>label{display:block;margin-bottom:8px;font-size:11px;color:#c8a6dd}.dj-source-form>div,.dj-spotify-search>div{display:flex;gap:8px}.dj-source-form input,.dj-spotify-search input{flex:1;min-width:0}\r\n.dj-source-status,.dj-visual-status,.dj-spotify-status{font-size:12px;color:#ffd1a4;line-height:1.6;min-height:24px}.dj-provider-frame{width:100%;height:370px;border:0;border-radius:12px;background:#140b1d;display:block;margin:16px 0}.dj-source-open{display:inline-block;border:1px solid #8755a0;padding:12px;border-radius:8px;text-decoration:none;font-size:12px;margin-top:12px}\r\n.dj-visual-stage{position:relative;border-radius:14px;overflow:hidden;background:#090415;border:1px solid #634678;width:100%;height:clamp(260px,46vh,540px)}.dj-visual-stage:fullscreen{height:100vh;border:0;border-radius:0}.dj-visual-canvas{display:block;width:100%;height:100%}\r\n.dj-visual-controls,.dj-visual-source{display:flex;gap:10px;justify-content:space-between;margin-top:14px}.dj-visual-controls label{display:flex;align-items:center;gap:10px;font-size:10px}.dj-visual-source button{flex:1;font-size:11px}.dj-visual-reference a{font-size:11px}\r\n.dj-react,.dj-spotify-seek,.dj-spotify-volume{display:flex;gap:12px;align-items:center;font-size:10px;color:#c5a5d5;margin-top:12px}\r\n.dj-spotify-extra{padding:16px;border:1px solid #674276;border-radius:12px;background:#1d1128;margin:20px 0}.dj-spotify-device,.dj-spotify-controls{display:flex;gap:8px;align-items:center;margin:14px 0}.dj-devices{flex:1;max-width:100%}.dj-spotify-controls label{display:flex;align-items:center;gap:8px;font-size:10px}\r\n.actual-dj .dj-view>section.sp-hand{position:relative;display:block;min-height:150px;border:0;background:none;padding:0}\r\n.actual-dj:not([data-dj-view=spotify]) .sp-shell>.sp-hand-mount,.actual-dj:not([data-dj-view=spotify]) .sp-status{display:none}\r\n.actual-dj [data-view=spotify]>.sp-account,.actual-dj [data-view=spotify]>.sp-now,.actual-dj [data-view=spotify]>.sp-jam,.actual-dj [data-view=spotify]>.sp-playlists{padding:16px;margin-bottom:18px;border:1px solid #654473;border-radius:12px;background:#1b1023}\r\n@keyframes dj-spin{to{transform:rotate(360deg)}}\r\n.actual-dj .sp-shell{position:relative}.dj-hand-overlay{position:absolute;left:0;top:0;z-index:5;width:64px;max-width:96px;height:84px;pointer-events:none;filter:drop-shadow(0 4px 6px #0009)}.dj-hand-overlay[hidden]{display:none}.dj-hand-overlay canvas,.dj-hand-overlay img{display:block;width:100%;height:100%;object-fit:contain;pointer-events:none}\r\n.dj-source-choice{display:grid;gap:5px;font-size:9px;margin:12px 0;color:#dcbce5}.dj-source-choice select{width:100%;min-width:0;padding:8px 4px;font-size:10px}.dj-deck-source-open{width:100%;font-size:9px;margin-bottom:12px}.dj-deck-source-open[hidden]{display:none}.dj-link-form{display:grid;gap:10px;margin:14px 0}.dj-link-form label{display:grid;gap:5px;font-size:10px}.dj-link-form input,.dj-link-form select{min-width:0;width:100%;box-sizing:border-box}.dj-link-list{list-style:none;margin:0;padding:0}.dj-source-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:7px;align-items:center;border-top:1px solid #593766;padding:10px 0}.dj-source-row button{padding:7px;font-size:10px}.dj-original-source{display:block;font-size:10px;color:#f9b772;overflow-wrap:anywhere}.dj-link-status{font-size:11px;color:#d8b5e5}\r\n@media(max-width:600px){.actual-dj .sp-top{padding:14px}.actual-dj .sp-screen{padding:14px 10px 24px}.actual-dj .dj-tabs{padding:8px;gap:4px}.actual-dj .dj-tabs button{padding:8px 4px;font-size:10px}.dj-decks{gap:8px}.dj-deck{padding:12px 8px}.dj-deck-source{font-size:7px}.dj-record{margin-bottom:16px;width:110px}.dj-title{font-size:14px;min-height:38px}.dj-tempo{gap:4px}.dj-tempo label{gap:4px}.dj-tempo input{max-width:70px;padding:5px!important}.dj-tempo button{padding:6px}.dj-rate-label{gap:4px;grid-template-columns:auto 1fr 28px;font-size:8px}.dj-eq{gap:6px}.dj-deck-controls{gap:4px}.dj-deck-controls button{font-size:9px;padding:6px 2px}.dj-mixer,.dj-library,.dj-queue-section{padding:12px}.dj-master-row{gap:12px}.dj-master-row button{min-width:110px;font-size:10px}.dj-auto-row{gap:8px}.dj-auto-row label{font-size:9px}.dj-auto-row select{max-width:110px}.dj-import{font-size:9px;padding:6px}.dj-crate-row{gap:4px}.dj-crate-row button{min-width:36px;padding:5px}.dj-crate-info strong{font-size:11px}.dj-provider-grid{grid-template-columns:repeat(2,1fr)}.dj-source-form>div{flex-wrap:wrap}.dj-source-form button{width:100%}.dj-visual-controls{gap:6px}.dj-visual-controls select{max-width:145px}.dj-visual-controls button{font-size:10px}.dj-spotify-device{flex-wrap:wrap}.dj-spotify-device select{width:100%}}\r\n@media(prefers-reduced-motion:reduce){.dj-playing .dj-record{animation:none}}\r\n.actual-dj .dj-tabs{flex-wrap:wrap}.actual-dj .dj-tabs [hidden],.actual-dj .dj-add-music [hidden],.actual-dj .dj-source-links[hidden]{display:none!important}.actual-dj .dj-tabs [data-view=playlist],.actual-dj .dj-tabs .dj-more{flex:1 0 40%;font-size:14px}.dj-playlist h3{margin:0;font-size:22px;color:#ffe0bd}.dj-playlist-count,.dj-current-source,.dj-next-title{font-size:11px;color:#bca0ca}.dj-playlist-status{font-size:11px;color:#bca0ca;margin:0 0 12px}.dj-simple-player{padding:14px;background:#25172f;border:1px solid #76548d;border-radius:12px}.dj-current-title,.dj-current-source,.dj-next-title{display:block;overflow-wrap:anywhere}.dj-simple-player>div{display:flex;gap:8px;margin:12px 0}.dj-simple-player button{flex:1}.dj-playlist-list{list-style:none;padding:0;margin:14px 0}.dj-playlist-row{padding:12px;border:1px solid #4d315c;border-radius:10px;margin:8px 0}.dj-playlist-row[aria-current=true]{border-color:#ffc07a}.dj-playlist-actions{display:flex;gap:6px;margin-top:8px}.dj-playlist-actions button{min-width:44px;padding:8px;font-size:11px}.dj-playlist-actions button:first-child{flex:1}.dj-add-music{border:1px solid #76548d;border-radius:10px;padding:12px;margin-top:16px}.dj-add-music summary{min-height:44px;display:flex;align-items:center;cursor:pointer;color:#ffd5a3}.dj-add-music label{display:grid;gap:6px;margin:8px 0;font-size:11px}.dj-add-music select{width:100%}.dj-add-local>button{margin:4px 4px 4px 0}\r\n\r\n.actual-dj .sp-shell.dj-corner-tapping>footer{box-sizing:border-box;min-height:100px;padding-right:80px;display:flex;align-items:center}\r\n","actual-dj.mjs":"import {createDJEngine} from './actual-dj-engine.mjs';\r\nimport {createDJLibrary} from './actual-dj-library.mjs';\r\nimport {DJ_PROVIDERS,providerLink,getDJSourceChoices,normalizeProviderSourceEntry} from './actual-dj-providers.mjs';\r\nimport {createDJVisualizer} from './actual-dj-visualizer.mjs';\r\nimport {parseDJCommand} from './actual-dj-commands.mjs';\r\nimport {createDJHand} from './actual-dj-hand.mjs';\r\nimport {reconcilePlaylist,movePlaylistItem,assignPlaylistIds} from './actual-dj-playlist.mjs';\r\n\r\nconst el=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n;};\r\nconst time=s=>`${Math.floor((s||0)/60)}:${String(Math.floor((s||0)%60)).padStart(2,'0')}`;\r\nconst remember=(key,value)=>{try{localStorage.setItem(key,JSON.stringify(value));return true;}catch{return false;}};\r\nconst recall=(key,fallback)=>{try{return JSON.parse(localStorage.getItem(key))??fallback;}catch{return fallback;}};\r\n\r\n// Browser glue around the deck engine and the providers' own players.\r\nexport function mountActualDJ({dialog,spotifyJSON,spotifyAction}){\r\n const screen=dialog.querySelector('.sp-screen'),shell=dialog.querySelector('.sp-shell');\r\n dialog.classList.add('actual-dj');dialog.querySelector('h2').textContent='actual.dj';\r\n dialog.querySelector('.sp-close').setAttribute('aria-label','Close actual.dj');\r\n dialog.querySelector('footer').textContent='MYR5 COACH / ACTUAL.DJ';\r\n const spotifyView=el('div','dj-view');spotifyView.dataset.view='spotify';spotifyView.hidden=true;\r\n for(const section of [...screen.querySelectorAll(':scope > section')])spotifyView.append(section);\r\n const tabs=el('nav','dj-tabs');tabs.setAttribute('aria-label','DJ modes');\r\n for(const [id,name] of [['playlist','Playlist'],['mix','Local mix'],['providers','Music apps'],['spotify','Spotify'],['visuals','Visuals']]){const b=el('button','',name);b.type='button';b.dataset.view=id;b.setAttribute('aria-pressed',String(id==='playlist'));tabs.append(b);}\r\n const more=el('button','dj-more','More');more.type='button';more.setAttribute('aria-expanded','false');tabs.insertBefore(more,tabs.children[1]);for(const b of tabs.querySelectorAll('[data-view]:not([data-view=playlist])'))b.hidden=true;more.addEventListener('click',()=>{const expanded=more.getAttribute('aria-expanded')!=='true';more.setAttribute('aria-expanded',String(expanded));for(const b of tabs.querySelectorAll('[data-view]:not([data-view=playlist])'))b.hidden=!expanded;});shell.querySelector('.sp-top').after(tabs);screen.append(spotifyView);\r\n const root=el('div','dj-workspace');screen.prepend(root);\r\n root.innerHTML=`<section class=\"dj-view\" data-view=\"mix\"><div class=\"dj-heading\"><span class=\"sp-label\">TWO DECKS / ONE ROOM</span><span class=\"dj-live\">READY</span></div><div class=\"dj-decks\"></div><section class=\"dj-mixer\"><label>A <input class=\"dj-crossfader\" aria-label=\"Crossfader\" type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"0\"> B</label><div class=\"dj-master-row\"><label>MASTER <input class=\"dj-master\" aria-label=\"Master volume\" type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"0.75\"></label><button class=\"dj-transition\" type=\"button\">MIX TO B ↗</button></div><div class=\"dj-auto-row\"><button class=\"dj-auto\" type=\"button\" aria-pressed=\"false\">AUTOMIX OFF</button><label>FADE <select class=\"dj-fade\" aria-label=\"Transition length\"><option value=\"4\">4 seconds</option><option value=\"8\" selected>8 seconds</option><option value=\"16\">16 seconds</option></select></label></div></section><section class=\"dj-library\"><div class=\"dj-heading\"><span class=\"sp-label\">YOUR CRATE</span><label class=\"dj-import\">+ IMPORT AUDIO<input class=\"dj-files\" type=\"file\" accept=\"audio/*,.mp3,.m4a,.wav,.ogg,.flac,.aac\" multiple></label></div><input class=\"dj-filter\" type=\"search\" placeholder=\"Search your tracks…\" aria-label=\"Search local music\"><p class=\"dj-library-status\" role=\"status\">MYR5 music and your imported files. Imports stay on this device.</p><ol class=\"dj-track-list\"></ol></section><section class=\"dj-queue-section\"><div class=\"dj-heading\"><span class=\"sp-label\">UP NEXT / AUTOMIX</span><button class=\"dj-clear-queue\" type=\"button\">CLEAR</button></div><ol class=\"dj-queue\"></ol></section></section><section class=\"dj-view\" data-view=\"providers\" hidden><span class=\"sp-label\">MUSIC APPS</span><p>Choose a source and paste its music link. Playback uses the app’s own player.</p><div class=\"dj-provider-grid\"></div><form class=\"dj-source-form\"><label class=\"dj-source-label\" for=\"djSourceUrl\">Apple Music link</label><div><input id=\"djSourceUrl\" type=\"url\" placeholder=\"https://music.apple.com/…\" required><button type=\"submit\">OPEN PLAYER</button></div></form><p class=\"dj-source-status\" role=\"status\">Apple Music previews use Apple’s player. Open Apple Music for full playback.</p><div class=\"dj-source-player\"></div><a class=\"dj-source-open\" href=\"https://music.apple.com/\" target=\"_blank\" rel=\"noopener noreferrer\">OPEN APPLE MUSIC ↗</a><p class=\"dj-provider-note\">Account linking: Spotify controls are in the Spotify tab. Apple Music account access needs MusicKit setup. Bandcamp and Amazon open in their apps.</p></section><section class=\"dj-view\" data-view=\"visuals\" hidden><div class=\"dj-heading\"><span class=\"sp-label\">VISUALIZER</span><button class=\"dj-fullscreen\" type=\"button\">FULLSCREEN ↗</button></div><div class=\"dj-visual-stage\"><canvas class=\"dj-visual-canvas\" aria-label=\"Audio-reactive music visualizer\"></canvas></div><div class=\"dj-visual-controls\"><label>SCENE <select class=\"dj-scene\"><option value=\"orbit\">Orbit</option><option value=\"tunnel\">Tunnel</option><option value=\"spectrum\">Spectrum</option><option value=\"scope\">Scope</option><option value=\"trails\">Trails</option></select></label><button class=\"dj-palette\" type=\"button\">NEXT PRESET ↻</button></div><div class=\"dj-visual-source\"><button class=\"dj-mix-source\" type=\"button\">USE MYR5 MIX</button><button class=\"dj-mic\" type=\"button\">USE MICROPHONE</button></div><p class=\"dj-visual-status\" role=\"status\">Play a local deck, then let the mix drive the visuals.</p><p class=\"dj-visual-reference\">Engine: <a href=\"https://github.com/jberg/butterchurn\" target=\"_blank\" rel=\"noopener noreferrer\">Butterchurn / MilkDrop</a>. Interface reference: <a href=\"https://photism.app/online/\" target=\"_blank\" rel=\"noopener noreferrer\">Photism</a>. Provider players show their own artwork or video.</p></section>`;\r\n const $=s=>root.querySelector(s),say=message=>{$('.dj-live').textContent=message;};\r\n const autoButton=$('.dj-auto');autoButton.textContent='START AUTO DJ ▶';autoButton.disabled=true;\r\n autoButton.style.width='100%';autoButton.style.marginBottom='16px';\r\n $('.dj-decks').before(autoButton);\r\n autoButton.after(el('p','dj-library-status','Auto DJ mixes your MYR5 crate and audio imported on this device.'));\r\n const library=createDJLibrary({onError:e=>{$('.dj-library-status').textContent=e?.message||String(e);}});\r\n let tracks=[],queue=[],engine,visualizer,view='playlist',auto=false,autoBusy=false,autoStarting=false,autoRevision=0,disposed=false,revision=0,preset=0,provider='apple',spotifyPlaying=false,handoffBusy=false;\r\n let playlistOrder=recall('myr5.actualDJ.playlistOrder',[]),playlistReady=false,selectedPlaylistId=null,playlistBusy=false,requestedView=null;\r\n if(!Array.isArray(playlistOrder))playlistOrder=[];\r\n const playlistView=el('section','dj-view dj-playlist');playlistView.dataset.view='playlist';playlistView.innerHTML='<div class=\"dj-heading\"><h3>Playlist</h3><span class=\"dj-playlist-count\"></span></div><p class=\"dj-playlist-status\" role=\"status\">Loading saved songs…</p><section class=\"dj-simple-player\"><strong class=\"dj-current-title\">Choose a song</strong><small class=\"dj-current-source\"></small><div><button type=\"button\" class=\"dj-current-play\">Play</button><button type=\"button\" class=\"dj-next-item\">Select next</button></div><small class=\"dj-next-title\"></small></section><ol class=\"dj-playlist-list\" aria-label=\"Saved songs\"></ol><details class=\"dj-add-music\"><summary>+ Add music</summary><label>Music app <select class=\"dj-add-source\" aria-label=\"Add music from\"></select></label><div class=\"dj-add-local\"><label>Song <select class=\"dj-local-song\" aria-label=\"Song on this device\"></select></label><button type=\"button\" class=\"dj-save-local\">Add song</button><button type=\"button\" class=\"dj-import-simple\">Import audio</button></div></details>';root.prepend(playlistView);\r\n const DECKS=['a','b'];\r\n const sourceChoices=getDJSourceChoices(),savedDeckSources=recall('myr5.actualDJ.deckSources',{}),deckSources={a:'local',b:'local'};\r\n for(const id of DECKS)if(sourceChoices.some(source=>source.id===savedDeckSources?.[id]))deckSources[id]=savedDeckSources[id];\r\n const localDeck=id=>deckSources[id]==='local',bothLocal=()=>DECKS.every(localDeck);\r\n for(const id of DECKS){\r\n  const d=el('section','dj-deck');d.dataset.deck=id;\r\n  d.innerHTML=`<div class=\"dj-heading\"><span class=\"sp-label\">DECK ${id.toUpperCase()}</span><span class=\"dj-deck-source\">MYR5 AUDIO</span></div><button type=\"button\" class=\"dj-record\" data-control=\"toggle\" aria-label=\"Play deck ${id.toUpperCase()}\"><span>${id.toUpperCase()}</span></button><strong class=\"dj-title\">Load a track</strong><span class=\"dj-artist\">Your music, your room.</span><label class=\"dj-seek-wrap\"><input type=\"range\" class=\"dj-seek\" aria-label=\"Seek deck ${id.toUpperCase()}\" min=\"0\" max=\"1\" step=\"0.05\" value=\"0\"><span class=\"dj-time\">0:00 / 0:00</span></label><div class=\"dj-deck-controls\"><button type=\"button\" data-control=\"toggle\" class=\"dj-play\">PLAY</button><button type=\"button\" data-control=\"cue\">CUE</button><button type=\"button\" data-control=\"sync\">SYNC</button></div><div class=\"dj-tempo\"><label>BPM <input class=\"dj-bpm\" aria-label=\"Deck ${id.toUpperCase()} BPM\" type=\"number\" min=\"40\" max=\"240\" step=\"0.01\" placeholder=\"Tap / enter\"></label><button type=\"button\" data-control=\"tap\">TAP</button></div><label class=\"dj-rate-label\">PITCH <input class=\"dj-rate\" aria-label=\"Deck ${id.toUpperCase()} speed\" type=\"range\" min=\"0.75\" max=\"1.25\" step=\"0.005\" value=\"1\"><output>0%</output></label><div class=\"dj-eq\">${['low','mid','high'].map(b=>`<label>${b.toUpperCase()}<input type=\"range\" data-eq=\"${b}\" aria-label=\"Deck ${id.toUpperCase()} ${b} EQ\" min=\"-24\" max=\"12\" step=\"1\" value=\"0\"></label>`).join('')}</div><label class=\"dj-loop-label\">LOOP <select class=\"dj-loop\" aria-label=\"Deck ${id.toUpperCase()} loop\"><option value=\"0\">Off</option><option value=\"4\">4 beats</option><option value=\"8\">8 beats</option><option value=\"16\">16 beats</option></select></label>`;\r\n  $('.dj-decks').append(d);\r\n  const label=el('label','dj-source-choice','SOURCE '),select=el('select');select.dataset.deckSourceSelect=id;select.setAttribute('aria-label',`Deck ${id.toUpperCase()} music source`);\r\n  for(const source of sourceChoices){const option=el('option','',source.name);option.value=source.id;select.append(option);}select.value=deckSources[id];label.append(select);d.querySelector('.dj-heading').after(label);\r\n  const open=el('button','dj-deck-source-open','OPEN SOURCE ↗');open.type='button';open.addEventListener('click',()=>{const selected=deckSources[id];if(selected!=='spotify'&&(spotifyPlaying||handoffBusy)){if(!handoffBusy)void prepareLocal();say('PAUSE SPOTIFY / TAP OPEN SOURCE AGAIN');return;}if(selected==='spotify')switchView('spotify');else{switchView('providers');selectProvider(selected);}});label.after(open);\r\n  select.addEventListener('change',()=>selectDeckSource(id,select.value));\r\n }\r\n const dk=id=>root.querySelector(`[data-deck=\"${id}\"]`);\r\n function render(state){\r\n  if(disposed)return;\r\n  for(const id of DECKS){const d=dk(id),isLocal=localDeck(id),s=isLocal?state.decks[id]:{...state.decks[id],track:null,playing:false,position:0,duration:0,bpm:null,rate:1,loopBeats:0};d.classList.toggle('dj-playing',s.playing);d.querySelector('.dj-title').textContent=s.track?.title||'Load a track';d.querySelector('.dj-artist').textContent=s.track?.artist||'Your music, your room.';d.querySelector('.dj-deck-source').textContent=s.track?.local?'LOCAL FILE':'MYR5 AUDIO';\r\n   const source=sourceChoices.find(source=>source.id===deckSources[id]);d.querySelector('.dj-deck-source-open').hidden=isLocal;\r\n   for(const control of d.querySelectorAll('[data-control],.dj-seek,.dj-bpm,.dj-rate,[data-eq],.dj-loop'))control.disabled=!isLocal;\r\n   for(const input of d.querySelectorAll('[data-eq]'))if(document.activeElement!==input)input.value=isLocal?s.eq[input.dataset.eq]:0;\r\n   if(!isLocal){d.querySelector('.dj-title').textContent=source.name;d.querySelector('.dj-artist').textContent=source.id==='apple'?'Previews in Apple’s player · open app for full playback':'Playback in the original app';d.querySelector('.dj-deck-source').textContent='SOURCE LINK';}\r\n   d.querySelector('.dj-play').textContent=s.playing?'PAUSE':'PLAY';d.querySelector('.dj-record').setAttribute('aria-label',`${s.playing?'Pause':'Play'} deck ${id.toUpperCase()}`);\r\n   const seek=d.querySelector('.dj-seek');seek.max=Math.max(1,s.duration||1);if(document.activeElement!==seek)seek.value=s.position||0;d.querySelector('.dj-time').textContent=`${time(s.position)} / ${time(s.duration)}`;\r\n   const bpm=d.querySelector('.dj-bpm');if(document.activeElement!==bpm)bpm.value=s.bpm||'';\r\n   d.querySelector('.dj-rate-label output').textContent=`${Math.round((s.rate-1)*100)}%`;if(document.activeElement!==d.querySelector('.dj-rate'))d.querySelector('.dj-rate').value=s.rate;\r\n   d.querySelector('.dj-loop').value=s.loopBeats||0;\r\n  }\r\n  if(document.activeElement!==$('.dj-crossfader'))$('.dj-crossfader').value=state.crossfader;\r\n  $('.dj-transition').textContent=state.mixing?'MIXING…':`MIX TO ${state.crossfader<.5?'B':'A'} ↗`;\r\n  $('.dj-transition').disabled=state.mixing||!bothLocal();$('.dj-crossfader').disabled=!bothLocal();autoButton.disabled=!tracks.length||!bothLocal();\r\n  renderPlaylistState(state);\r\n  if(auto&&bothLocal()&&!autoBusy&&dialog.open&&view!=='providers'&&view!=='spotify'){\r\n   const active=state.crossfader<.5?'a':'b',s=state.decks[active],other=active==='a'?'b':'a';\r\n   if(s.playing&&s.duration>0&&s.duration-s.position<=Math.min(Number($('.dj-fade').value),s.duration*.4)&&queue.length&&state.decks[other].track&&!state.mixing){void mix(true);}\r\n  }\r\n }\r\n engine=createDJEngine({onChange:render,onError:e=>say(e?.message||String(e))});\r\n engine.setMaster(.75);engine.setCrossfader(0);\r\n visualizer=createDJVisualizer($('.dj-visual-canvas'),{getAnalyser:()=>engine.getAnalyser(),onStatus:message=>{$('.dj-visual-status').textContent=message;}});\r\n const djHand=createDJHand(dialog,{getState:()=>engine.snapshot()});\r\n function selectDeckSource(id,source){if(!sourceChoices.some(choice=>choice.id===source))return false;if(spotifyPlaying||handoffBusy){dk(id).querySelector('[data-deck-source-select]').value=deckSources[id];if(!handoffBusy)void prepareLocal();say('PAUSE SPOTIFY / CHOOSE SOURCE AGAIN');return false;}revision++;setAuto(false);deckSources[id]=source;remember('myr5.actualDJ.deckSources',deckSources);dk(id).querySelector('[data-deck-source-select]').value=source;DECKS.forEach(deck=>engine.pause(deck));stopProvider();if(localDeck(id)&&!engine.snapshot().decks[id].track&&tracks.length)engine.load(id,tracks[id==='a'?0:1]||tracks[0]);if(localDeck('a')&&!localDeck('b'))engine.setCrossfader(0);if(localDeck('b')&&!localDeck('a'))engine.setCrossfader(1);render(engine.snapshot());say(bothLocal()?'LOCAL DECKS READY':`${sourceChoices.find(choice=>choice.id===source).name.toUpperCase()} / OPEN SOURCE FOR PLAYBACK`);}\r\n const storedSourceLinks=recall('myr5.actualDJ.sourceLinks',[]);\r\n let sourceLinks=Array.isArray(storedSourceLinks)?storedSourceLinks.slice(0,50).map(entry=>{const normalized=normalizeProviderSourceEntry(entry?.provider,entry?.sourceUrl,entry?.label);return normalized?{...normalized,playlistId:typeof entry.playlistId==='string'&&entry.playlistId.length<=100?entry.playlistId:crypto.randomUUID()}:null;}).filter(Boolean):[];\r\n sourceLinks=assignPlaylistIds(sourceLinks);remember('myr5.actualDJ.sourceLinks',sourceLinks);\r\n const linksSection=el('section','dj-library dj-source-links');linksSection.innerHTML='<span class=\"sp-label\">SOURCE LINK PLAYLIST</span><p class=\"dj-library-status\">Your links keep their original music app. Open each source manually; Auto DJ uses your local crate.</p><form class=\"dj-link-form\"><label>APP <select id=\"djLinkProvider\" aria-label=\"Source link music app\"></select></label><label>NAME <input id=\"djLinkLabel\" maxlength=\"120\" required placeholder=\"Your track or playlist name\"></label><label>LINK <input id=\"djLinkUrl\" type=\"url\" maxlength=\"2048\" required placeholder=\"Original music app link\"></label><button type=\"submit\">+ SAVE SOURCE LINK</button></form><p class=\"dj-link-status\" role=\"status\"></p><ol class=\"dj-link-list\"></ol>';\r\n $('.dj-queue-section').after(linksSection);const linkProvider=linksSection.querySelector('#djLinkProvider'),linkStatus=linksSection.querySelector('.dj-link-status');\r\n for(const choice of sourceChoices.filter(choice=>choice.id!=='local')){const option=el('option','',choice.name);option.value=choice.id;linkProvider.append(option);}\r\n function saveSourceLinks(){remember('myr5.actualDJ.sourceLinks',sourceLinks);renderSourceLinks();syncPlaylist();}\r\n function renderSourceLinks(){const list=linksSection.querySelector('.dj-link-list');list.replaceChildren();sourceLinks.forEach((entry,index)=>{const row=el('li','dj-source-row'),info=el('div','dj-crate-info'),original=el('a','dj-original-source',entry.attribution.provider+' · original link');original.setAttribute('href',entry.sourceUrl);original.target='_blank';original.rel='noopener noreferrer';info.append(el('strong','',entry.label),original);original.addEventListener('click',event=>{if(spotifyPlaying||handoffBusy){event.preventDefault();if(!handoffBusy)void prepareLocal();linkStatus.textContent='Pause Spotify, then open the original source again.';return;}revision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));stopProvider();});const open=el('button','','OPEN'),remove=el('button','','×');open.type=remove.type='button';open.dataset.sourceOpen=index;remove.dataset.sourceRemove=index;remove.setAttribute('aria-label',`Remove source link ${entry.label}`);row.append(info,open,remove);list.append(row);});if(!sourceLinks.length)list.append(el('li','dj-empty','Save an Apple Music, Spotify or other original source link.'));}\r\n linksSection.querySelector('.dj-link-form').addEventListener('submit',event=>{event.preventDefault();const label=addForm.querySelector('#djLinkLabel'),url=addForm.querySelector('#djLinkUrl'),entry=normalizeProviderSourceEntry(linkProvider.value,url.value,label.value.trim()||sourceChoices.find(choice=>choice.id===linkProvider.value).name+' link');if(!entry){linkStatus.textContent='Use a valid link from the selected music app.';return;}if(sourceLinks.length>=50){linkStatus.textContent='This playlist holds 50 links. Remove one to add another.';return;}sourceLinks.push({...entry,playlistId:crypto.randomUUID()});saveSourceLinks();linkStatus.textContent=`Saved ${entry.label} · ${entry.attribution.provider}`;label.value='';url.value='';playlistView.querySelector('.dj-add-music').open=false;});\r\n linksSection.addEventListener('click',event=>{const remove=event.target.closest('[data-source-remove]'),open=event.target.closest('[data-source-open]');if(remove){sourceLinks.splice(Number(remove.dataset.sourceRemove),1);saveSourceLinks();return;}if(!open)return;const entry=sourceLinks[Number(open.dataset.sourceOpen)];if(!entry)return;if(spotifyPlaying||handoffBusy){if(!handoffBusy)void prepareLocal();linkStatus.textContent='Pause Spotify, then tap OPEN again.';return;}switchView('providers');selectProvider(entry.provider);$('#djSourceUrl').value=entry.sourceUrl;$('.dj-source-form').requestSubmit();});renderSourceLinks();\r\n\r\n const addSource=playlistView.querySelector('.dj-add-source'),addLocal=playlistView.querySelector('.dj-add-local'),addForm=linksSection.querySelector('.dj-link-form');\r\n for(const choice of sourceChoices){const option=el('option','',choice.id==='local'?'On this device':choice.name);option.value=choice.id;addSource.append(option);}\r\n playlistView.querySelector('.dj-heading').after(playlistView.querySelector('.dj-add-music'));playlistView.querySelector('.dj-add-music').append(addForm,linkStatus);addForm.hidden=true;addForm.querySelector('label').hidden=true;addForm.querySelector('#djLinkLabel').required=false;addForm.querySelector('#djLinkLabel').placeholder='Name (optional)';addForm.querySelector('button').textContent='Add link';linksSection.hidden=true;\r\n addSource.addEventListener('change',()=>{const local=addSource.value==='local';addLocal.hidden=!local;addForm.hidden=local;if(!local)linkProvider.value=addSource.value;linkStatus.textContent='';});\r\n playlistView.querySelector('.dj-save-local').addEventListener('click',()=>{const track=tracks.find(track=>track.id===playlistView.querySelector('.dj-local-song').value);if(track){queue.push(track);saveQueue();playlistView.querySelector('.dj-playlist-status').textContent=`Added ${track.title}`;playlistView.querySelector('.dj-add-music').open=false;}});\r\n playlistView.querySelector('.dj-import-simple').addEventListener('click',()=>$('.dj-files').click());\r\n function refreshPlaylistSongs(){const select=playlistView.querySelector('.dj-local-song');select.replaceChildren();for(const track of tracks){const option=el('option','',track.title);option.value=track.id;select.append(option);}playlistView.querySelector('.dj-save-local').disabled=!tracks.length;}\r\n function playlistItem(ref){if(!ref)return null;return ref?.kind==='local'?queue.find(track=>track.id===ref.trackId):sourceLinks.find(entry=>entry.playlistId===ref?.sourceId);}\r\n function syncPlaylist(){if(!playlistReady)return;playlistOrder=reconcilePlaylist(playlistOrder,queue,sourceLinks);remember('myr5.actualDJ.playlistOrder',playlistOrder);if(!playlistOrder.some(ref=>ref.id===selectedPlaylistId))selectedPlaylistId=playlistOrder[0]?.id||null;renderPlaylistRows();renderPlaylistState(engine.snapshot());}\r\n function handoffPlaylist(){if(spotifyPlaying||handoffBusy){if(!handoffBusy)void prepareLocal();playlistView.querySelector('.dj-playlist-status').textContent='Pause Spotify, then try again.';return false;}revision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));stopProvider();return true;}\r\n function renderPlaylistRows(){const list=playlistView.querySelector('.dj-playlist-list');list.replaceChildren();playlistView.querySelector('.dj-playlist-count').textContent=`${playlistOrder.length} saved`;for(const ref of playlistOrder){const entry=playlistItem(ref);if(!entry)continue;const row=el('li','dj-playlist-row');row.dataset.playlistId=ref.id;row.setAttribute('aria-current',String(ref.id===selectedPlaylistId));const info=el('div','dj-crate-info'),name=entry.title||entry.label;info.append(el('strong','',name),el('small','dj-provider-badge',ref.kind==='local'?'On this device':entry.attribution.provider));if(ref.kind!=='local'){const original=el('a','dj-original-source','Original link ↗');original.href=entry.sourceUrl;original.target='_blank';original.rel='noopener noreferrer';original.addEventListener('click',event=>{if(!handoffPlaylist())event.preventDefault();});info.append(original);}row.append(info);const actions=el('div','dj-playlist-actions');for(const [action,label] of [['play',ref.kind==='local'?'Play':`Open in ${entry.attribution.provider}`],['up','↑'],['down','↓'],['remove','×']]){const button=el('button','',label);button.type='button';button.dataset.playlistAction=action;button.setAttribute('aria-label',`${action==='up'?'Move up':action==='down'?'Move down':action==='remove'?'Remove':label} ${name}`);button.disabled=action==='up'&&ref===playlistOrder[0]||action==='down'&&ref===playlistOrder.at(-1);actions.append(button);}row.append(actions);list.append(row);}if(!playlistOrder.length)list.append(el('li','dj-empty','Your playlist is empty. Add music below.'));playlistView.querySelector('.dj-playlist-status').textContent='Links open in their music app.';}\r\n function renderPlaylistState(state){if(!playlistReady)return;const ref=playlistOrder.find(ref=>ref.id===selectedPlaylistId),entry=playlistItem(ref),deck=state.decks.a,playing=ref?.kind==='local'&&deck.track?.id===entry?.id&&deck.playing;playlistView.querySelector('.dj-current-title').textContent=entry?.title||entry?.label||'Choose a song';playlistView.querySelector('.dj-current-source').textContent=entry?(ref.kind==='local'?`${playing?'Playing':'On this device'} · ${time(deck.track?.id===entry.id?deck.position:0)}`:`Selected · ${entry.attribution.provider}`):'';const play=playlistView.querySelector('.dj-current-play');play.textContent=entry?(ref.kind==='local'?(playing?'Pause':'Play'):`Open in ${entry.attribution.provider}`):'Play';play.disabled=!entry||playlistBusy;const index=playlistOrder.findIndex(ref=>ref.id===selectedPlaylistId),next=playlistItem(playlistOrder[index+1]);playlistView.querySelector('.dj-next-title').textContent=next?`Next: ${next.title||next.label}`:'';playlistView.querySelector('.dj-next-item').disabled=!next;for(const row of playlistView.querySelectorAll('[data-playlist-id]'))row.setAttribute('aria-current',String(row.dataset.playlistId===selectedPlaylistId));}\r\n async function playPlaylist(id){const ref=playlistOrder.find(ref=>ref.id===id),entry=playlistItem(ref);if(!entry||playlistBusy)return;const state=engine.snapshot();if(ref.kind==='local'&&selectedPlaylistId===id&&state.decks.a.track?.id===entry.id&&state.decks.a.playing){handoffPlaylist();return;}if(!handoffPlaylist())return;selectedPlaylistId=id;renderPlaylistState(engine.snapshot());if(ref.kind==='source-link'){switchView('providers');selectProvider(entry.provider);$('#djSourceUrl').value=entry.sourceUrl;$('.dj-source-form').requestSubmit();return;}if(!localDeck('a'))selectDeckSource('a','local');engine.setCrossfader(0);engine.setLoop('a',0);if(engine.snapshot().decks.a.track?.id!==entry.id)engine.load('a',entry);else if(state.decks.a.duration&&state.decks.a.position>=state.decks.a.duration-.05)engine.seek('a',0);const mine=revision;playlistBusy=true;try{const ok=await engine.play('a');if(mine===revision&&!disposed)playlistView.querySelector('.dj-playlist-status').textContent=ok?'':'Tap Play to retry.';}finally{playlistBusy=false;if(!disposed)renderPlaylistState(engine.snapshot());}}\r\n function savePlaylistOrder(){queue=playlistOrder.filter(ref=>ref.kind==='local').map(ref=>playlistItem(ref)).filter(Boolean);sourceLinks=playlistOrder.filter(ref=>ref.kind==='source-link').map(ref=>playlistItem(ref)).filter(Boolean);remember('myr5.actualDJ.queue',queue.map(track=>track.id));remember('myr5.actualDJ.sourceLinks',sourceLinks);remember('myr5.actualDJ.playlistOrder',playlistOrder);renderQueue();renderSourceLinks();renderPlaylistRows();renderPlaylistState(engine.snapshot());}\r\n playlistView.querySelector('.dj-playlist-list').addEventListener('click',event=>{const button=event.target.closest('[data-playlist-action]'),row=button?.closest('[data-playlist-id]');if(!row)return;const id=row.dataset.playlistId,action=button.dataset.playlistAction;if(action==='play'){void playPlaylist(id);return;}if(action==='remove'){if(selectedPlaylistId===id){if(!handoffPlaylist())return;selectedPlaylistId=null;}const ref=playlistOrder.find(ref=>ref.id===id);if(ref?.kind==='local'){const index=playlistOrder.filter(ref=>ref.kind==='local').findIndex(ref=>ref.id===id);queue.splice(index,1);}else sourceLinks=sourceLinks.filter(entry=>entry.playlistId!==ref?.sourceId);playlistOrder=playlistOrder.filter(ref=>ref.id!==id);if(!selectedPlaylistId)selectedPlaylistId=playlistOrder[0]?.id||null;}else playlistOrder=movePlaylistItem(playlistOrder,id,action==='up'?-1:1);savePlaylistOrder();if(action==='up'||action==='down'){const moved=playlistView.querySelector(`[data-playlist-id=\"${CSS.escape(id)}\"]`),focus=moved?.querySelector(`[data-playlist-action=${action}]:not(:disabled)`)||moved?.querySelector('[data-playlist-action=play]');focus?.focus({preventScroll:true});}});\r\n playlistView.querySelector('.dj-current-play').addEventListener('click',()=>void playPlaylist(selectedPlaylistId));playlistView.querySelector('.dj-next-item').addEventListener('click',()=>{const index=playlistOrder.findIndex(ref=>ref.id===selectedPlaylistId),next=playlistOrder[index+1];if(next&&handoffPlaylist()){selectedPlaylistId=next.id;renderPlaylistState(engine.snapshot());}});\r\n function saveQueue(){remember('myr5.actualDJ.queue',queue.map(t=>t.id));renderQueue();syncPlaylist();}\r\n function renderQueue(){const list=$('.dj-queue');list.replaceChildren();queue.forEach((track,i)=>{const row=el('li','dj-queue-row');row.append(el('span','',`${i+1}. ${track.title}`));const remove=el('button','','×');remove.type='button';remove.dataset.remove=i;remove.setAttribute('aria-label',`Remove ${track.title} from queue`);row.append(remove);list.append(row);});if(!queue.length)list.append(el('li','dj-empty','Add tracks from your crate.'));}\r\n function renderLibrary(){const list=$('.dj-track-list'),q=$('.dj-filter').value.trim().toLowerCase();list.replaceChildren();for(const t of tracks.filter(t=>`${t.title} ${t.artist}`.toLowerCase().includes(q))){const row=el('li','dj-crate-row');row.dataset.track=t.id;const info=el('div','dj-crate-info');info.append(el('strong','',t.title),el('small','',`${t.artist||'Local audio'} · ${t.local?'LOCAL':`${t.bpm||'—'} BPM`}`));row.append(info);for(const [action,label] of [['a','A'],['b','B'],['queue','+']]){const b=el('button','',label);b.type='button';b.dataset.load=action;b.setAttribute('aria-label',action==='queue'?`Queue ${t.title}`:`Load ${t.title} on deck ${action.toUpperCase()}`);row.append(b);}if(t.local){const remove=el('button','','×');remove.type='button';remove.dataset.load='remove';remove.setAttribute('aria-label',`Delete local ${t.title}`);row.append(remove);}list.append(row);}if(!list.children.length)list.append(el('li','dj-empty','No matching music. Import audio to add it here.'));}\r\n const ready=(async()=>{try{tracks=await library.list();if(disposed)return;const saved=recall('myr5.actualDJ.queue',[]);queue=Array.isArray(saved)?saved.map(id=>tracks.find(t=>t.id===id)).filter(Boolean):[];renderLibrary();renderQueue();playlistReady=true;syncPlaylist();refreshPlaylistSongs();if(tracks[0]&&localDeck('a'))engine.load('a',tracks[0]);if(tracks[1]&&localDeck('b'))engine.load('b',tracks[1]);render(engine.snapshot());}catch(e){if(!disposed)say(e.message);}})();\r\n const setAuto=value=>{auto=value;if(!value)autoRevision++;autoButton.textContent=auto?'STOP AUTO DJ ■':'START AUTO DJ ▶';autoButton.setAttribute('aria-pressed',String(auto));};\r\n function prepareNext(id){if(!queue[0])return;engine.pause(id);if(engine.snapshot().decks[id].track?.id!==queue[0].id)engine.load(id,queue[0]);else engine.seek(id,0);}\r\n async function startAuto(){\r\n  if(!bothLocal()){say('CHOOSE MYR5 CRATE ON BOTH DECKS FOR AUTO DJ');return false;}\r\n  if(disposed||!dialog.open||handoffBusy)return false;\r\n  if(autoBusy){say('MIX FINISHING / START AUTO DJ AGAIN');return false;}\r\n  if(spotifyPlaying){void prepareLocal();return false;}\r\n  const initial=engine.snapshot(),out=initial.crossfader<.5?'a':'b',incoming=out==='a'?'b':'a';\r\n  if(!initial.decks[out].track){const first=queue.shift()||tracks[0];if(!first){say('IMPORT AUDIO TO START AUTO DJ');return false;}engine.load(out,first);}\r\n  const active=engine.snapshot().decks[out];\r\n  if(!queue.length)queue=tracks.filter(t=>t.id!==active.track.id);\r\n  setAuto(true);autoBusy=true;autoStarting=true;const run=autoRevision,mine=revision;\r\n  try{\r\n   stopProvider();engine.setCrossfader(out==='a'?0:1);DECKS.forEach(id=>engine.setLoop(id,0));\r\n   prepareNext(incoming);saveQueue();\r\n   if(active.duration&&active.position>=active.duration-.05)engine.seek(out,0);\r\n   // Prime both existing media elements before yielding, keeping the start tap's audio activation.\r\n   const starts=[engine.play(out)];if(queue.length)starts.push(engine.play(incoming));\r\n   const started=await Promise.all(starts);\r\n   if(disposed||mine!==revision||run!==autoRevision||!dialog.open)return false;\r\n   if(queue.length)engine.pause(incoming);\r\n   if(started.some(ok=>!ok)){setAuto(false);say('AUTO DJ PAUSED / TAP START TO RETRY');return false;}\r\n   if(!queue.length){setAuto(false);say('LAST TRACK / AUTO DJ FINISHED');}else say('AUTO DJ PLAYING / QUEUE READY');\r\n   return true;\r\n  }catch(e){if(!disposed&&mine===revision){setAuto(false);say(e.message);}return false;}\r\n  finally{autoBusy=false;autoStarting=false;}\r\n }\r\n function stopAuto(){const state=engine.snapshot();setAuto(false);if(autoStarting)engine.pause(state.crossfader<.5?'b':'a');say(state.mixing?'FINISHING CURRENT MIX / AUTO DJ OFF':'AUTO DJ STOPPED / MANUAL MIX');}\r\n async function mix(automatic=false){\r\n  if(!bothLocal()){say('SOURCE LINKS USE THEIR ORIGINAL PLAYER / CHOOSE LOCAL DECKS TO MIX');return false;}\r\n  if(autoBusy||disposed||!dialog.open)return;autoBusy=true;\r\n  if(automatic){const state=engine.snapshot();prepareNext(state.crossfader<.5?'b':'a');}\r\n  const initial=engine.snapshot(),out=initial.crossfader<.5?'a':'b',incoming=out==='a'?'b':'a',mine=revision,run=autoRevision;\r\n  const incomingTrack=initial.decks[incoming].track?.id;\r\n  const selectedFade=Number($('.dj-fade').value),remaining=initial.decks[out].duration-initial.decks[out].position;\r\n  const fade=automatic?Math.max(.1,Math.min(selectedFade,initial.decks[out].duration*.4,remaining)):selectedFade;\r\n  try{\r\n   const ok=await engine.transition(fade);\r\n   if(disposed||mine!==revision||!dialog.open)return;\r\n   if(ok){\r\n    engine.pause(out);\r\n    if(automatic&&queue[0]?.id===incomingTrack){queue.shift();saveQueue();}\r\n    if(auto&&run===autoRevision){if(queue.length)prepareNext(out);else setAuto(false);}\r\n    say(automatic&&!auto?(queue.length?'AUTO DJ STOPPED / MANUAL MIX':'LAST TRACK / AUTO DJ FINISHED'):'MIX COMPLETE');\r\n   }else if(automatic&&auto&&run===autoRevision){setAuto(false);say('AUTO DJ PAUSED / TAP START TO RETRY');}\r\n  }catch(e){if(!disposed&&mine===revision){if(automatic&&auto&&run===autoRevision)setAuto(false);say(e.message);}}\r\n  finally{autoBusy=false;}\r\n }\r\n const taps={a:[],b:[]};\r\n $('.dj-decks').addEventListener('click',async e=>{const b=e.target.closest('[data-control]'),d=b?.closest('[data-deck]');if(!d||handoffBusy||!localDeck(d.dataset.deck))return;const id=d.dataset.deck;if(b.dataset.control==='toggle'&&!engine.snapshot().decks[id].playing&&spotifyPlaying){void prepareLocal();return;}try{switch(b.dataset.control){case'toggle':if(auto)stopAuto();await engine.toggle(id);say('MANUAL DECK / AUTO DJ OFF');break;case'cue':engine.cue(id);break;case'sync':engine.sync(id);break;case'tap':{const now=performance.now();taps[id]=taps[id].filter(t=>now-t<4000).slice(-7);taps[id].push(now);if(taps[id].length>1){const bpm=60000*(taps[id].length-1)/(now-taps[id][0]);if(bpm>=40&&bpm<=240)engine.setBpm(id,Math.round(bpm*100)/100);}break;}}}catch(err){say(err.message);}});\r\n $('.dj-decks').addEventListener('input',e=>{const d=e.target.closest('[data-deck]');if(!d||!localDeck(d.dataset.deck))return;const id=d.dataset.deck,v=Number(e.target.value);try{if(e.target.matches('.dj-rate'))engine.setRate(id,v);if(e.target.dataset.eq)engine.setEQ(id,e.target.dataset.eq,v);}catch(err){say(err.message);}});\r\n $('.dj-decks').addEventListener('change',e=>{const d=e.target.closest('[data-deck]');if(!d||!localDeck(d.dataset.deck))return;const id=d.dataset.deck,v=Number(e.target.value);try{if(e.target.matches('.dj-seek'))engine.seek(id,v);if(e.target.matches('.dj-bpm'))engine.setBpm(id,v);if(e.target.matches('.dj-loop'))engine.setLoop(id,v);}catch(err){say(err.message);}});\r\n $('.dj-crossfader').addEventListener('input',e=>{if(auto)stopAuto();engine.setCrossfader(Number(e.target.value));say('MANUAL CROSSFADER / AUTO DJ OFF');});\r\n $('.dj-master').addEventListener('input',e=>engine.setMaster(Number(e.target.value)));\r\n $('.dj-transition').addEventListener('click',()=>{if(handoffBusy)return;if(spotifyPlaying){void prepareLocal();return;}void mix(auto);});\r\n autoButton.addEventListener('click',()=>{if(auto)stopAuto();else void startAuto();});\r\n const commandForm=el('form','dj-command-form'),commandLabel=el('label','','DECK COMMANDS'),commandRow=el('div'),commandInput=el('input'),commandSubmit=el('button','','RUN'),commandStatus=el('p','dj-library-status','Try “start auto dj”, “mix next”, “fade 4 seconds” or “pause deck A”.');\r\n commandInput.id='djDeckCommand';commandInput.type='text';commandInput.maxLength=80;commandInput.placeholder='Tell the local decks what to do…';commandInput.autocomplete='off';commandInput.required=true;\r\n commandLabel.htmlFor=commandInput.id;commandSubmit.type='submit';commandStatus.setAttribute('role','status');commandRow.style.display='flex';commandRow.style.gap='8px';commandInput.style.flex='1';commandInput.style.minWidth='0';commandInput.style.width='100%';\r\n commandRow.append(commandInput,commandSubmit);commandForm.append(commandLabel,commandRow,commandStatus);$('.dj-mixer').after(commandForm);\r\n commandForm.addEventListener('submit',async event=>{\r\n  event.preventDefault();if(disposed||!dialog.open||handoffBusy)return;\r\n  const command=parseDJCommand(commandInput.value);\r\n  if(!command){commandStatus.textContent='Use a deck command: start/stop auto dj, mix next, fade 4/8/16 seconds, or play/pause deck A/B.';return;}\r\n  if((command.action==='automix'&&command.enabled||command.action==='mix-next'||command.control==='play')&&spotifyPlaying){void prepareLocal();commandStatus.textContent='Pause Spotify first, then run your deck command again.';return;}\r\n  const mine=revision;\r\n  try{\r\n   let ok=true;\r\n   switch(command.action){\r\n    case'automix':if(command.enabled)ok=await startAuto();else stopAuto();break;\r\n    case'mix-next':ok=(await mix(auto))!==false;break;\r\n    case'fade':$('.dj-fade').value=String(command.seconds);break;\r\n    case'deck-control':if(!localDeck(command.deck)){ok=false;say('OPEN THE SELECTED SOURCE FOR PLAYBACK');break;}if(auto)stopAuto();if(command.control==='play')ok=await engine.play(command.deck);else engine.pause(command.deck);say('MANUAL DECK / AUTO DJ OFF');break;\r\n   }\r\n   if(!disposed&&mine===revision&&dialog.open)commandStatus.textContent=ok?`Done: ${commandInput.value.trim()}`:'Playback paused. Tap Start Auto DJ or Play to retry.';\r\n  }catch(error){if(!disposed&&mine===revision&&dialog.open)commandStatus.textContent=error.message;}\r\n });\r\n $('.dj-filter').addEventListener('input',renderLibrary);\r\n $('.dj-files').addEventListener('change',async e=>{try{await library.importFiles(e.target.files);tracks=await library.list();renderLibrary();refreshPlaylistSongs();}catch(err){$('.dj-library-status').textContent=err.message;}finally{e.target.value='';}});\r\n $('.dj-track-list').addEventListener('click',async e=>{const b=e.target.closest('[data-load]'),row=b?.closest('[data-track]');if(!row)return;const t=tracks.find(t=>t.id===row.dataset.track);if(!t)return;try{if(b.dataset.load==='queue'){queue.push(t);saveQueue();say('ADDED TO QUEUE');}else if(b.dataset.load==='remove'){if(auto)stopAuto();for(const id of DECKS)if(engine.snapshot().decks[id].track?.id===t.id){engine.pause(id);engine.load(id,null);}await library.remove(t.id);tracks=await library.list();queue=queue.filter(q=>q.id!==t.id);saveQueue();renderLibrary();}else{if(!localDeck(b.dataset.load)&&selectDeckSource(b.dataset.load,'local')===false)return;if(auto)stopAuto();engine.load(b.dataset.load,t);}}catch(err){say(err.message);}});\r\n $('.dj-clear-queue').addEventListener('click',()=>{if(auto)stopAuto();queue=[];saveQueue();});\r\n $('.dj-queue').addEventListener('click',e=>{const b=e.target.closest('[data-remove]');if(b){queue.splice(Number(b.dataset.remove),1);if(auto&&!queue.length)stopAuto();saveQueue();}});\r\n const savedValue=recall('myr5.actualDJ.sources',{}),savedLinks=savedValue&&typeof savedValue==='object'&&!Array.isArray(savedValue)?savedValue:{};\r\n function stopProvider(){$('.dj-source-player').replaceChildren();}\r\n function selectProvider(id){provider=id;stopProvider();const p=DJ_PROVIDERS.find(p=>p.id===id);for(const b of $('.dj-provider-grid').children)b.setAttribute('aria-pressed',String(b.dataset.provider===id));$('.dj-source-label').textContent=`${p.name} link`;$('#djSourceUrl').placeholder=p.hint;$('#djSourceUrl').value=typeof savedLinks?.[id]==='string'?savedLinks[id]:'';$('.dj-source-open').href=p.home;$('.dj-source-open').textContent=`OPEN ${p.name.toUpperCase()} ↗`;$('.dj-source-status').textContent=p.id==='apple'?'Apple Music previews play here. Open Apple Music for full playback.':`${p.name} · ${p.mode}. Paste a link to begin.`;}\r\n for(const p of DJ_PROVIDERS){const b=el('button','',p.name);b.type='button';b.dataset.provider=p.id;b.addEventListener('click',()=>selectProvider(p.id));$('.dj-provider-grid').append(b);}selectProvider('apple');\r\n $('.dj-source-form').addEventListener('submit',e=>{if(spotifyPlaying||handoffBusy){e.preventDefault();e.stopImmediatePropagation();if(!handoffBusy)void prepareLocal();}},true);\r\n $('.dj-source-form').addEventListener('submit',e=>{e.preventDefault();const link=providerLink(provider,$('#djSourceUrl').value);if(!link){$('.dj-source-status').textContent=`Use a valid ${DJ_PROVIDERS.find(p=>p.id===provider).name} music link.`;return;}revision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));stopProvider();savedLinks[provider]=link.url;remember('myr5.actualDJ.sources',savedLinks);$('.dj-source-open').href=link.url;if(link.embed){const iframe=el('iframe','dj-provider-frame');iframe.src=link.embed;iframe.title=`${link.name} official player`;iframe.allow='autoplay; encrypted-media; fullscreen; picture-in-picture';iframe.referrerPolicy='strict-origin-when-cross-origin';iframe.allowFullscreen=true;$('.dj-source-player').append(iframe);$('.dj-source-status').textContent=provider==='apple'?'Apple Music previews play here. Open Apple Music for full playback.':`SOURCE: ${link.name}. Availability and full playback are managed by ${link.name}.`;}else{$('.dj-source-status').textContent=`Saved ${link.name} link. Use OPEN ${link.name.toUpperCase()} to listen in the source app.`;}});\r\n $('.dj-source-open').addEventListener('click',event=>{if(spotifyPlaying||handoffBusy){event.preventDefault();if(!handoffBusy)void prepareLocal();$('.dj-source-status').textContent='Pause Spotify, then open the source app again.';return;}revision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));stopProvider();});\r\n const sceneNames={orbit:'Mindblob',tunnel:'Glass corridor',spectrum:'Thumb drum',scope:'Cubetrace',trails:'Music reactive paint'};\r\n for(const option of $('.dj-scene').options)option.textContent=sceneNames[option.value];\r\n $('.dj-palette').textContent='ALTERNATE LOOK ↻';\r\n const reactLabel=el('label','dj-react','REACT '),reactInput=el('input');reactInput.type='range';reactInput.min='.1';reactInput.max='3';reactInput.step='.1';reactInput.value='1';reactInput.setAttribute('aria-label','Visualizer audio reactivity');reactLabel.append(reactInput);$('.dj-visual-controls').after(reactLabel);reactInput.addEventListener('input',()=>visualizer.setReact(Number(reactInput.value)));\r\n $('.dj-scene').addEventListener('change',e=>visualizer.setScene(e.target.value));$('.dj-palette').addEventListener('click',()=>visualizer.setPalette(++preset%3));\r\n $('.dj-mix-source').addEventListener('click',()=>visualizer.useMix());$('.dj-mic').addEventListener('click',()=>void visualizer.microphone());\r\n $('.dj-fullscreen').addEventListener('click',async()=>{try{await $('.dj-visual-stage').requestFullscreen();}catch{$('.dj-visual-status').textContent='Fullscreen is unavailable here. The visualizer still runs in this view.';}});\r\n async function prepareLocal(){if(handoffBusy)return false;if(!spotifyPlaying)return true;handoffBusy=true;const mine=revision;say('PAUSING SPOTIFY…');const ok=await spotifyAction('pause',{});if(mine===revision&&dialog.open){if(ok)spotifyPlaying=false;say(ok?'SPOTIFY PAUSED / TAP PLAY':'PAUSE SPOTIFY ON ITS DEVICE FIRST');$('.dj-source-status').textContent=ok?'Spotify paused. Open your source player.':'Pause Spotify on its device before opening another player.';}handoffBusy=false;return ok;}\r\n function switchView(next){const previous=view;view=next;for(const v of [...root.querySelectorAll(':scope > .dj-view'),spotifyView])v.hidden=v.dataset.view!==next;for(const b of tabs.querySelectorAll('[data-view]'))b.setAttribute('aria-pressed',String(b.dataset.view===next));dialog.dataset.djView=next;djHand.setActive(dialog.open&&(next==='mix'||next==='playlist'));stopProvider();if(next==='providers'||next==='spotify'){revision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));}if(next!=='spotify'&&previous==='spotify')void prepareLocal();if(next==='visuals')visualizer.start();else visualizer.stop();}\r\n tabs.addEventListener('click',e=>{const b=e.target.closest('[data-view]');if(b)switchView(b.dataset.view);});\r\n // Expand the existing OAuth terminal rather than replacing its account handling.\r\n const spotifyExtra=el('section','dj-spotify-extra');spotifyExtra.innerHTML='<span class=\"sp-label\">SPOTIFY LIBRARY</span><form class=\"dj-spotify-search\"><label for=\"djSpotifySearch\">Search Spotify</label><div><input id=\"djSpotifySearch\" type=\"search\" maxlength=\"200\" placeholder=\"Artist or track…\" required><button>SEARCH</button></div></form><p class=\"dj-spotify-status\" role=\"status\"></p><ol class=\"dj-spotify-results\"></ol><div class=\"dj-spotify-device\"><button type=\"button\" class=\"dj-refresh-devices\">OUTPUT DEVICES</button><select class=\"dj-devices\" aria-label=\"Spotify playback device\"><option value=\"\">Choose device…</option></select></div><div class=\"dj-spotify-controls\"><button type=\"button\" class=\"dj-shuffle\" aria-pressed=\"false\">SHUFFLE OFF</button><label>REPEAT <select class=\"dj-repeat\"><option value=\"off\">Off</option><option value=\"context\">Playlist</option><option value=\"track\">Track</option></select></label></div><button class=\"dj-fetch-queue\" type=\"button\">SHOW SPOTIFY QUEUE</button><ol class=\"dj-spotify-queue\"></ol>';\r\n spotifyView.querySelector('.sp-playlists').before(spotifyExtra);\r\n const sp=s=>spotifyExtra.querySelector(s),spSay=message=>{sp('.dj-spotify-status').textContent=message;};let searchRevision=0,shuffle=false;\r\n function spotifyState(connected){spotifyExtra.hidden=!connected;if(!connected){spotifyPlaying=false;searchRevision++;sp('.dj-spotify-results').replaceChildren();sp('.dj-spotify-queue').replaceChildren();sp('.dj-devices').replaceChildren(el('option','','Choose device…'));sp('.dj-devices').firstChild.value='';sp('#djSpotifySearch').value='';sp('.dj-repeat').value='off';shuffle=false;sp('.dj-shuffle').textContent='SHUFFLE OFF';sp('.dj-shuffle').setAttribute('aria-pressed','false');spSay('');}}\r\n function spotifyRows(list,items,{queueOnly=false}={}){list.replaceChildren();for(const t of items){const row=el('li','dj-crate-row');row.append(el('strong','',`${t.name} — ${(t.artists||[]).join(', ')}`));if(t.url){const link=el('a','','↗');link.href=t.url;link.target='_blank';link.rel='noopener noreferrer';link.setAttribute('aria-label',`Open ${t.name} in Spotify`);row.append(link);}if(!queueOnly)for(const [action,label] of [['track','PLAY'],['queue','+']]){const b=el('button','',label);b.type='button';b.addEventListener('click',()=>void spotifyAction(action,{trackId:t.id}));row.append(b);}list.append(row);}if(!items.length)list.append(el('li','dj-empty','No tracks to show.'));}\r\n sp('.dj-spotify-search').addEventListener('submit',async e=>{e.preventDefault();const mine=++searchRevision;spSay('Searching Spotify…');try{const data=await spotifyJSON('/api/spotify/search?q='+encodeURIComponent(sp('#djSpotifySearch').value));if(mine!==searchRevision||!dialog.open)return;spotifyRows(sp('.dj-spotify-results'),data.items||[]);spSay(`${data.total||0} Spotify results`);}catch(err){if(mine===searchRevision&&dialog.open&&err.name!=='AbortError')spSay(err.message);}});\r\n sp('.dj-fetch-queue').addEventListener('click',async()=>{const mine=searchRevision;try{const data=await spotifyJSON('/api/spotify/queue');if(mine!==searchRevision||!dialog.open)return;spotifyRows(sp('.dj-spotify-queue'),data.items||[],{queueOnly:true});}catch(err){if(mine===searchRevision&&dialog.open&&err.name!=='AbortError')spSay(err.message);}});\r\n sp('.dj-refresh-devices').addEventListener('click',async()=>{const mine=searchRevision;try{const data=await spotifyJSON('/api/spotify/devices');if(mine!==searchRevision||!dialog.open)return;sp('.dj-devices').replaceChildren(el('option','','Choose device…'));sp('.dj-devices').firstChild.value='';for(const d of data.items||[]){const o=el('option','',`${d.name}${d.active?' · Active':''}`);o.value=d.id;o.disabled=d.restricted;sp('.dj-devices').append(o);}spSay(data.items?.length?'Choose a Spotify output.':'Open Spotify on a device first.');}catch(err){if(mine===searchRevision&&dialog.open&&err.name!=='AbortError')spSay(err.message);}});\r\n sp('.dj-devices').addEventListener('change',e=>{if(e.target.value)void spotifyAction('transfer',{deviceId:e.target.value});});sp('.dj-repeat').addEventListener('change',e=>void spotifyAction('repeat',{mode:e.target.value}));\r\n sp('.dj-shuffle').addEventListener('click',async()=>{const next=!shuffle;const ok=await spotifyAction('shuffle',{enabled:next});if(ok){shuffle=next;sp('.dj-shuffle').setAttribute('aria-pressed',String(shuffle));sp('.dj-shuffle').textContent=`SHUFFLE ${shuffle?'ON':'OFF'}`;}});\r\n function onOpen(){if(!dialog.open)return;window.dispatchEvent(new CustomEvent('myr5:dj-active',{detail:{active:true}}));const next=requestedView|| (new URLSearchParams(location.search).has('spotify')?'spotify':'playlist');requestedView=null;if(next==='playlist'){more.setAttribute('aria-expanded','false');for(const button of tabs.querySelectorAll('[data-view]:not([data-view=playlist])'))button.hidden=true;}switchView(next);}\r\n const observer=new MutationObserver(onOpen);observer.observe(dialog,{attributes:true,attributeFilter:['open']});\r\n const onClose=()=>{if(dialog.open&&!disposed)return;revision++;searchRevision++;setAuto(false);DECKS.forEach(id=>engine.pause(id));visualizer.stop();djHand.setActive(false);stopProvider();window.dispatchEvent(new CustomEvent('myr5:dj-active',{detail:{active:false}}));};dialog.addEventListener('close',onClose);\r\n switchView('playlist');\r\n return {spotifyState,spotifyPlaying(value){spotifyPlaying=!!value;},spotifyStarting(){revision++;DECKS.forEach(id=>engine.pause(id));setAuto(false);stopProvider();},showSpotify(){if(!dialog.open)requestedView='spotify';switchView('spotify');},dispose(){if(disposed)return;disposed=true;revision++;observer.disconnect();dialog.removeEventListener('close',onClose);onClose();engine.dispose();library.dispose();visualizer.dispose();djHand.dispose();tabs.remove();root.remove();}};\r\n}\r\n","app-updates.mjs":"import {createUpdateParticipant} from './update-session.mjs';\r\nimport {RELEASE} from './release-info.mjs';\r\nimport {safeToUpdate,releaseNotice,requestActivation} from './update-policy.mjs';\r\n\r\n// Release notes are optional; a slow or unavailable API must not block the\r\n// browser's independent check for a new service worker.\r\nexport async function checkUpdateSources(registration,loadNotes){\r\n const notes=Promise.resolve().then(loadNotes).then(()=>true,()=>false);\r\n if(registration){await registration.update();return;}\r\n if(!await notes)throw Error('Release information is unavailable.');\r\n}\r\n\r\n// Longer than the worker's own worst case (two 4 s replies + ACTIVATION_WAIT in sw.js).\r\nconst UPDATE_BARRIER_MS=25000;\r\nexport function initAppUpdates({api,applyButton,onRegistration,onBeforeUpdate}) {\r\n const panel=document.getElementById('installPanel'),section=document.createElement('details');\r\n section.className='release-settings';\r\n section.innerHTML='<summary>App updates</summary><p>Turn on notifications in Reminders to get a download alert for every new app update.</p><p id=\"releaseVersion\"></p><ul id=\"releaseNotes\"></ul><button id=\"checkAppUpdate\">Check for updates</button><p id=\"releaseStatus\" role=\"status\"></p><form id=\"releaseEmailForm\"><label>Email me when an update is ready<input name=\"email\" type=\"email\" autocomplete=\"email\" required maxlength=\"254\" placeholder=\"you@example.com\"></label><button type=\"submit\" disabled>Email me updates</button><button id=\"stopReleaseEmails\" type=\"button\" hidden>Stop update emails</button><p id=\"releaseEmailStatus\" role=\"status\">Optional. Confirm your email first; unsubscribe any time.</p></form>';\r\n panel.append(section);\r\n section.open=new URLSearchParams(location.search).has('update');\r\n const $=id=>document.getElementById(id),banner=document.createElement('aside');\r\n banner.className='app-update-banner';banner.setAttribute('role','status');banner.hidden=true;\r\n banner.innerHTML='<span>A Coach update is ready.</span><button type=\"button\" data-update>Update now</button><button type=\"button\" data-notes>What’s new</button><button type=\"button\" data-later aria-label=\"Dismiss update notice\">Later</button>';\r\n document.body.append(banner);\r\n let storage;try{storage=localStorage;}catch{}\r\n const notice=releaseNotice(storage,RELEASE.id);\r\n let reg=null,latest=RELEASE,applying=false,switched=false,dismissed=false,poll=0,error='',lastInteraction=Date.now(),retryAt=0,checking=false,notesRequest=null;\r\n const hadController=!!navigator.serviceWorker?.controller;\r\n const safe=(automatic=true,background=false)=>safeToUpdate({tracking:document.body.dataset.tracking==='true',rest:document.body.dataset.screen==='rest',dialog:!!document.querySelector('dialog[open]:not(#installPanel):not(#coachSetupGate):not(#coachUpdateBarrier)'),editing:!!document.activeElement?.matches('input,textarea,select,[contenteditable=true]'),hidden:document.hidden&&!background,online:navigator.onLine,lastInteraction,automatic});\r\n const ready=()=>!!reg?.waiting||switched||(!('serviceWorker' in navigator)&&latest.id!==RELEASE.id);\r\n function showNotes(r){$('releaseVersion').textContent=r.title+' · '+r.date;$('releaseNotes').replaceChildren(...r.notes.map(text=>{const li=document.createElement('li');li.textContent=text;return li;}));}\r\n function paint(){\r\n  // Under the update barrier (a modal) the banner's buttons can't be tapped, so it isn't shown there.\r\n  applyButton.hidden=!ready();banner.hidden=!!document.getElementById('coachUpdateBarrier')||(ready()?dismissed:!notice.visible);\r\n  banner.querySelector('span').textContent=ready()?error||'Coach will update when you are idle.':'Updated: '+RELEASE.title;\r\n  const b=banner.querySelector('[data-update]');b.hidden=!ready();b.disabled=!safe(false)||applying;applyButton.disabled=b.disabled;\r\n  b.textContent=!safe(false)?'Finish what you’re doing':applying?'Updating…':'Update now';\r\n  banner.querySelector('[data-later]').textContent=ready()?'Later':'Got it';\r\n  $('releaseStatus').textContent=error||(ready()?'Update downloaded. Coach will save and restart when you are idle.':latest.id!==RELEASE.id?'Downloading update…':'Automatic updates are on.');\r\n }\r\n async function apply(automatic=false){\r\n  if(!safe(automatic)||applying||!ready())return;\r\n  applying=true;error='';paint();\r\n  try{\r\n   if(reg?.waiting){await requestActivation(reg.waiting,{timeout:20000});return;}\r\n   await onBeforeUpdate?.();\r\n   if(!safe(automatic)){applying=false;paint();return;}\r\n   location.reload();\r\n  }catch(e){applying=false;retryAt=Date.now()+30000;error=e.message||'Save your progress before updating.';paint();}\r\n }\r\n const participant=createUpdateParticipant({\r\n  canPrepare:()=>safe(!applying,true),\r\n  acquire:()=>{const owner=window.myr5WorkoutOwner,lease=owner?.acquireIdleLease();return lease?()=>owner.releaseIdleLease(lease):null;},\r\n  save:async()=>{await onBeforeUpdate?.();if(!await window.myr5WorkoutOwner.save())throw Error('Progress could not be saved.');},\r\n  freeze:id=>{const barrier=document.createElement('dialog');barrier.id='coachUpdateBarrier';barrier.textContent='Saving and updating Coach…';barrier.addEventListener('cancel',event=>event.preventDefault());document.body.append(barrier);barrier.showModal();\r\n   // Never a trap: the worker can be stopped mid-update (iOS) or still be waiting on the old one. If the\r\n   // new worker took over without telling this page, reload into it; otherwise hand the app back with a retry.\r\n   const timer=setTimeout(async()=>{\r\n    await participant.message({type:'UPDATE_ABORT',id});\r\n    const r=await Promise.race([navigator.serviceWorker.getRegistration().catch(()=>null),new Promise(resolve=>setTimeout(()=>resolve(null),1000))]);if(r&&!r.waiting)return location.reload();\r\n    applying=false;dismissed=false;retryAt=Date.now()+60000;error='Coach couldn’t finish updating. Tap Update now to try again.';paint();\r\n   },UPDATE_BARRIER_MS);\r\n   return ()=>{clearTimeout(timer);barrier.remove();paint();};},\r\n  reload:()=>location.reload()\r\n });\r\n applyButton.onclick=()=>apply(false);banner.querySelector('[data-update]').onclick=()=>apply(false);\r\n banner.querySelector('[data-later]').onclick=()=>{if(ready())dismissed=true;else notice.dismiss();paint();};\r\n banner.querySelector('[data-notes]').onclick=()=>{notice.dismiss();if(!panel.open)panel.showModal();section.open=true;section.scrollIntoView({block:'start'});paint();};\r\n function openDownload(){if(!panel.open)panel.showModal();section.open=true;section.scrollIntoView({block:'start'});void check();}\r\n async function check(){\r\n  if(checking)return;checking=true;\r\n  try{\r\n   await checkUpdateSources(reg,()=>notesRequest??=(async()=>{\r\n    const value=await api('/api/releases/current');\r\n    if(typeof value?.id==='string'&&Array.isArray(value.notes)&&value.notes.every(n=>typeof n==='string')){if(latest.id!==value.id)dismissed=false;latest=value;showNotes(latest);paint();}\r\n   })().finally(()=>{notesRequest=null;}));\r\n   error='';paint();\r\n  }catch{error='Could not check for an update. Reconnect and try again.';paint();}finally{checking=false;}\r\n }\r\n $('checkAppUpdate').onclick=check;\r\n async function emailStatus(){\r\n  const form=$('releaseEmailForm'),button=form.querySelector('[type=submit]');\r\n  try{\r\n   const p=await api('/api/updates/subscription');button.disabled=!p.configured;\r\n   if(p.email&&!form.elements.email.value)form.elements.email.value=p.email;\r\n   $('stopReleaseEmails').hidden=!p.email;\r\n   $('releaseEmailStatus').textContent=p.enabled?'Release emails are on.':p.email?'Check your inbox to confirm release emails.':p.configured?'Optional. Confirm your email first; unsubscribe any time.':'Email delivery is being connected. In-app updates are available now.';\r\n  }catch{button.disabled=true;$('releaseEmailStatus').textContent='Sign in to choose email updates.';}\r\n }\r\n $('releaseEmailForm').onsubmit=async e=>{\r\n  e.preventDefault();const form=e.currentTarget,b=form.querySelector('[type=submit]');b.disabled=true;\r\n  try{const r=await api('/api/updates/subscription','POST',{email:form.elements.email.value});$('releaseEmailStatus').textContent=r.message;$('stopReleaseEmails').hidden=false;}\r\n  catch(error){$('releaseEmailStatus').textContent=error.message;}finally{b.disabled=false;}\r\n };\r\n $('stopReleaseEmails').onclick=async()=>{try{await api('/api/updates/subscription','DELETE',{});$('stopReleaseEmails').hidden=true;$('releaseEmailStatus').textContent='Update emails are off.';}catch(error){$('releaseEmailStatus').textContent=error.message;}};\r\n document.querySelectorAll('[data-panel=install]').forEach(b=>b.addEventListener('click',emailStatus));\r\n if('serviceWorker' in navigator){\r\n  navigator.serviceWorker.register('/sw.js',{updateViaCache:'none'}).then(value=>{\r\n   reg=value;onRegistration?.(reg);paint();\r\n   reg.addEventListener('updatefound',()=>{const w=reg.installing;w?.addEventListener('statechange',()=>{if(w.state==='installed'){dismissed=false;paint();}});});check();\r\n  }).catch(()=>{error='Updates could not initialize. Refresh Coach to retry.';paint();});\r\n  navigator.serviceWorker.addEventListener('controllerchange',()=>{\r\n   if(!hadController&&!applying&&!participant.prepared)return;\r\n   switched=true;if(participant.activated())return;\r\n   if(applying&&safe(false))location.reload();\r\n   else {applying=false;paint();}\r\n  });\r\n  navigator.serviceWorker.addEventListener('message',async event=>{\r\n   if(event.data?.type==='APP_UPDATE_AVAILABLE'){openDownload();event.ports[0]?.postMessage({handled:true});return;}\r\n   if(!['UPDATE_SAFETY_CHECK','UPDATE_CONFIRM','UPDATE_ABORT'].includes(event.data?.type))return;\r\n   const result=await participant.message(event.data);event.ports[0]?.postMessage(result);\r\n  });\r\n }else check();\r\n showNotes(RELEASE);paint();\r\n const observer=new MutationObserver(paint);observer.observe(document.body,{subtree:true,attributes:true,attributeFilter:['data-tracking','data-screen','open']});\r\n const activity=()=>{lastInteraction=Date.now();};\r\n for(const type of ['pointerdown','keydown','input'])document.addEventListener(type,activity,{passive:true});\r\n const autoPoll=setInterval(()=>{paint();if(!dismissed&&Date.now()>=retryAt)void apply(true);},1000);\r\n document.addEventListener('visibilitychange',()=>{if(!document.hidden)check();});window.addEventListener('online',check);\r\n poll=setInterval(()=>{if(!document.hidden)check();},300000);\r\n window.addEventListener('pagehide',event=>{if(!event.persisted){clearInterval(poll);clearInterval(autoPoll);observer.disconnect();for(const type of ['pointerdown','keydown','input'])document.removeEventListener(type,activity);}});emailStatus();\r\n return {check};\r\n}\r\n","app.mjs":"import {symbolMarkup} from './arcade/visual-language.mjs';\r\nimport {claimGraphics} from './modules/graphics-lifecycle.mjs';\r\nimport './modules/coach-world/entry.mjs';\r\nimport {SHELL_ICONS} from './modules/shell/owner.mjs';\r\nimport {PoseFrameScheduler} from './pose-frame.mjs';\r\nimport {mountCameraWorkout} from './camera-workout.mjs';\r\nimport { MovementSession, MOVEMENTS } from './movement-engine.mjs';\r\nimport { initLibrary } from './menu.mjs';\r\nimport {showUnlockGate,watchUnlock} from './unlock-gate.mjs';\r\nimport {CoachVoice,CueEvents} from './coach.mjs';\r\nimport {mountCoachVoiceSettings} from './coach-voice-settings.mjs';\r\nimport {initPod} from './pod/pod.mjs';\r\nimport {syncBattlePass} from './battle-pass.mjs';\r\nimport {mountBattlePass} from './battle-pass-page.mjs';\r\nimport {performanceOwner} from './performance-progress.mjs';\r\nimport {workoutPathAccess} from './chosen-styles.mjs';\r\nimport {reconcilePerformanceWorkouts} from './performance-reconcile.mjs';\r\nimport {mountHomeCharacter} from './pod/home-character.mjs';\r\nimport {mountPodPlaces} from './pod/pod-places.mjs';\r\nimport {mountContinueWorkout} from './pod/continue-workout.mjs';\r\nimport {initHardware} from './pod/hardware.mjs';\r\nimport {setFlipValue,countDigits,clockDigits} from './flip-display.mjs';\r\nimport {openCamera,listCameras,findUltrawide,deviceChoice,cameraFacing,widestZoom,cameraReport} from './camera.mjs';\r\nimport {openGuestWorkoutAdapter} from './local-coach-runtime.mjs';\r\nimport {ManualActiveClock,ManualStartGate} from './local-coach/manual-clock.mjs';\r\nimport {mountPackLicenses} from './packs/pack-license-surface.mjs';\r\nimport {mountCoachOverlay} from './coach-overlay.mjs';\r\nimport {workoutEffectsSnapshot,isMartialSliceExercise,latestRetainableWorkoutSnapshot,createOptionalRetry,createOptionalModuleLoader} from './arcade/workout-effects-state.mjs';\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {physicalSound} from './audio/physical-sound.mjs';\r\nimport {mountArmieInboxUI} from './armie-inbox-ui.mjs';\r\nimport {acceptShipRevealComplete} from './modules/ships/ship-access.mjs';\r\nimport {localVerifiedBridge as shipViewBridge,ownedShipIds as shipOwnedShipIds,mountFirstShipArrival} from './modules/ships/ship-view-bridge.mjs';\r\nimport {mountRoutes,hashRoute} from './modules/routes.mjs';\r\nimport {mountMenuLifecycle} from './menu-lifecycle.mjs';\r\nimport {mountPhoneOrientation} from './modules/phone-orientation.mjs';\r\nimport {mountPhysicalSoundUI} from './audio/sound-ui.mjs';\r\nimport {mountPageMusic} from './audio/page-music.mjs';\r\nimport {mountFullWorkout} from './full-workout-ui.mjs';\r\nimport {mountPodChrome} from './modules/pod-chrome.mjs';\r\nimport {ensurePortalMounted} from './modules/portal/portal-entry.mjs';\r\n// W2-2A: hash routes + the bottom bar (launch.mjs boots the deep link once the panels exist).\r\nmountRoutes();\r\nwindow.myr5MenuLifecycle=mountMenuLifecycle();\r\nmountBattlePass();\r\nmountPhysicalSoundUI();mountPageMusic();\r\nconst lazyGrimoire=document.createElement('button');\r\nlazyGrimoire.type='button';lazyGrimoire.id='podGrimoireLazyOpen';lazyGrimoire.innerHTML=SHELL_ICONS.grimoire;\r\nlazyGrimoire.setAttribute('aria-label','Open grimoire settings');\r\nlet grimoireOpening=false;\r\nlazyGrimoire.addEventListener('click',async()=>{\r\n if(grimoireOpening)return;grimoireOpening=true;\r\n try{await ensurePortalMounted();document.getElementById('portalSettingsButton')?.click();}\r\n catch(error){console.warn('Grimoire settings unavailable',error);}\r\n finally{grimoireOpening=false;}\r\n});\r\nmountPodChrome(lazyGrimoire);\r\nlet phoneOrientation=mountPhoneOrientation();\r\nwindow.addEventListener('pagehide',()=>{phoneOrientation?.();phoneOrientation=null;});\r\nwindow.addEventListener('pageshow',event=>{if(event.persisted&&!phoneOrientation)phoneOrientation=mountPhoneOrientation();});\r\nconst $=id=>document.getElementById(id),v=$('v'),c=$('c'),g=c.getContext('2d');\r\n$('camera').querySelector('option[value=\"manual\"]')?.remove();\r\nif($('camera').value==='manual')$('camera').value='user';\r\nconst poseFrames=new PoseFrameScheduler(v);\r\nconst schedulePose=run=>poseFrames.schedule(()=>{if(run===generation&&state.phase==='tracking')void loop(run);});\r\n// Capture time of the frame being detected (rVFC metadata, performance.now() clock) so effect layers can filter on\r\n// real frame spacing and predict across the inference lag. Falls back to the detect time.\r\nfunction frameTime(now){const m=poseFrames.frame;for(const t of [m?.captureTime,m?.presentationTime])if(Number.isFinite(t)&&t<=now&&now-t<500)return t;return now;}\r\n// Optional person mask for the aura silhouette tier: only while an aura layer asks (globalThis.myr5AuraWantsMask,\r\n// set by its quality ladder), downsampled once here to ~96 px wide while the MPMask is still valid.\r\nlet maskTracker=null,maskOn=false,maskSetting=null;const maskBuf={w:0,h:0,data:null};\r\nfunction syncMask(){\r\n  const want=!!globalThis.myr5AuraWantsMask;if(maskTracker!==tracker){maskTracker=tracker;maskOn=false;}\r\n  if(want===maskOn||maskSetting||typeof tracker?.setOptions!=='function')return;\r\n  const t=tracker;maskSetting=Promise.resolve().then(()=>t.setOptions({outputSegmentationMasks:want})).then(()=>{if(maskTracker===t)maskOn=want;},()=>{globalThis.myr5AuraWantsMask=false;}).finally(()=>{maskSetting=null;});\r\n}\r\nfunction readMask(result){\r\n  const masks=result?.segmentationMasks,m=masks?.[0];if(!m)return null;\r\n  try{\r\n    const W=m.width,H=m.height,step=Math.max(1,Math.ceil(W/96)),w=Math.floor(W/step),h=Math.floor(H/step);\r\n    if(!maskBuf.data||maskBuf.w!==w||maskBuf.h!==h){maskBuf.w=w;maskBuf.h=h;maskBuf.data=new Uint8ClampedArray(w*h);}\r\n    const src=m.getAsFloat32Array(),d=maskBuf.data;\r\n    for(let y=0,k=0;y<h;y++){const row=y*step*W;for(let x=0;x<w;x++)d[k++]=src[row+x*step]*255;}\r\n    return maskBuf;\r\n  }catch{return null;}finally{for(const mask of masks)try{mask.close();}catch{}}\r\n}\r\nconst voice=new CoachVoice(text=>{$('coachCaption').textContent=text;if(!$('restScreen').hidden)$('restFeedback').textContent=text;},text=>$('voiceType').textContent=text),cues=new CueEvents();\r\nmountCoachVoiceSettings({voice,settings:$('settings'),testButton:$('testVoice')});\r\ndocument.addEventListener('pointerdown',()=>voice.unlock(),{capture:true});\r\ndocument.addEventListener('keydown',()=>voice.unlock(),{capture:true});\r\nif(!voice.available){$('voiceType').textContent='Speech unavailable in this browser';$('toggleVoice').disabled=true;}\r\nlet pod=null,tracker=null,draw=null,api=null,files=null,stream=null,frame=0,generation=0;\r\nlet workoutEffects=null,workoutEffectsLoading=null,workoutEffectsToken=0,latestWorkoutEffects=null,pendingRetainedDetail=null,retainedPower=null,avatarPower=null,avatarPowerCard=null,avatarPowerLoadingCard=null,avatarPowerFailedCard=null,retainedPowerLoading=null,retainedPowerToken=0,avatarPowerToken=0,retainedOwner=null;\r\nconst retainedPowerRetry=createOptionalRetry(),avatarPowerRetry=createOptionalRetry();\r\nconst loadRetainedPowerModule=createOptionalModuleLoader('/arcade/power/retained-power.mjs');\r\nconst loadAvatarPowerModule=createOptionalModuleLoader('/arcade/power/avatar-power.mjs');\r\nconst loadQuestProgressReporterModule=createOptionalModuleLoader('/arcade/quest-progress-reporter.mjs');\r\nconst loadWorkoutEffectsModule=createOptionalModuleLoader('/arcade/workout-effects.mjs');\r\nconst questReporterRetry=createOptionalRetry(),workoutEffectsRetry=createOptionalRetry();\r\nlet questReporter=null,questReporterOwner=null,questReporterLoading=null,questReporterToken=0,pendingQuestDetails=new Map();\r\nfunction dropWorkoutEffects(){workoutEffectsToken++;workoutEffectsRetry.reset();workoutEffects?.stop();workoutEffects=null;workoutEffectsLoading=null;latestWorkoutEffects=null;}\r\nfunction invalidateWorkoutEffects(){workoutEffectsToken++;workoutEffectsRetry.reset();workoutEffectsLoading=null;workoutEffects?.invalidate();if(latestWorkoutEffects)latestWorkoutEffects={...latestWorkoutEffects,validHold:false,workout:{...latestWorkoutEffects.workout,active:false}};}\r\nfunction publishRetainedPower(){try{window.dispatchEvent(new CustomEvent('myr5:retained-character-power',{detail:{ownerId:retainedOwner??performanceOwner(),record:retainedPower?.read()??{version:1,equipped:null,styles:{}}}}));}catch{}}\r\nwindow.addEventListener('myr5:retained-character-power-request',()=>publishRetainedPower());\r\nfunction mountRetainedAvatar(card,avatarModule){\r\n if(!card||!retainedPower||!card.isConnected||avatarPowerCard===card||avatarPowerLoadingCard===card)return;\r\n if(avatarPowerFailedCard&&avatarPowerFailedCard!==card){const previous=avatarPowerFailedCard;avatarPowerFailedCard=null;if(previous!==card)avatarPowerRetry.reset();}\r\n if(avatarPowerFailedCard===card&&!avatarPowerRetry.canStart())return;\r\n if(!avatarPowerRetry.canStart())return;\r\n avatarPowerRetry.start();const token=++avatarPowerToken;avatarPowerLoadingCard=card;\r\n Promise.resolve(avatarModule?avatarModule:loadAvatarPowerModule()).then(module=>{\r\n  if(token!==avatarPowerToken||!retainedPower||!card.isConnected||performanceOwner()!==retainedOwner)return;\r\n  const mounted=module.mountAvatarPower({card,retained:retainedPower,sound:physicalSound,seed:0x4d595235});\r\n  avatarPower=mounted;avatarPowerCard=card;avatarPowerRetry.succeed();if(avatarPowerFailedCard===card)avatarPowerFailedCard=null;\r\n }).catch(()=>{if(token===avatarPowerToken&&avatarPowerLoadingCard===card){avatarPowerFailedCard=card;avatarPowerRetry.fail();}}).finally(()=>{if(token===avatarPowerToken&&avatarPowerLoadingCard===card)avatarPowerLoadingCard=null;});\r\n}\r\nfunction ensureRetainedPower(card=document.querySelector('.myr5-companion-card')){\r\n const owner=performanceOwner();\r\n if(retainedPower){if(owner!==retainedOwner){retainedPower.setOwner(owner);retainedOwner=owner;avatarPowerToken++;avatarPower?.stop();avatarPower=null;avatarPowerCard=null;avatarPowerLoadingCard=null;if(avatarPowerFailedCard===card)avatarPowerFailedCard=null;avatarPowerRetry.reset();publishRetainedPower();}else if(card&&card!==avatarPowerCard&&card!==avatarPowerLoadingCard){avatarPowerToken++;avatarPower?.stop();avatarPower=null;avatarPowerCard=null;avatarPowerLoadingCard=null;if(avatarPowerFailedCard&&avatarPowerFailedCard!==card){avatarPowerFailedCard=null;avatarPowerRetry.reset();}}\r\n  if(card)mountRetainedAvatar(card);return;}\r\n if(retainedPowerLoading)return;\r\n if(!retainedPowerRetry.canStart())return;\r\n retainedPowerRetry.start();const token=++retainedPowerToken;\r\n retainedPowerLoading=loadRetainedPowerModule().then(storeModule=>{\r\n  if(token!==retainedPowerToken||performanceOwner()!==owner)return;\r\n  let storage=null;try{storage=localStorage;}catch{}\r\n  retainedPower=storeModule.createRetainedPower({storage,ownerId:owner});retainedOwner=owner;\r\n  if(pendingRetainedDetail?.workout?.ownerId===owner&&retainedPower.observe(pendingRetainedDetail))avatarPower?.refresh();\r\n  retainedPowerRetry.succeed();publishRetainedPower();\r\n  const currentCard=card?.isConnected?card:document.querySelector('.myr5-companion-card');if(currentCard)mountRetainedAvatar(currentCard);\r\n }).catch(()=>{if(token===retainedPowerToken)retainedPowerRetry.fail();}).finally(()=>{if(token===retainedPowerToken)retainedPowerLoading=null;});\r\n}\r\nfunction syncRetainedPowerOwner(){retainedPowerToken++;retainedPowerLoading=null;pendingRetainedDetail=null;questReporter=null;questReporterOwner=null;questReporterLoading=null;questReporterToken++;pendingQuestDetails.clear();questReporterRetry.reset();retainedPowerRetry.reset();const owner=performanceOwner();if(owner!==retainedOwner){retainedOwner=owner;avatarPowerToken++;avatarPower?.stop();avatarPower=null;avatarPowerCard=null;avatarPowerLoadingCard=null;avatarPowerFailedCard=null;avatarPowerRetry.reset();if(retainedPower)retainedPower.setOwner(owner);publishRetainedPower();}ensureRetainedPower();}\r\nfunction routeQuestWorkoutProgress(detail){\r\n const ownerId=detail?.workout?.ownerId;\r\n if(!ownerId||ownerId!==performanceOwner())return;\r\n const workout=detail.workout;\r\n if(workout.stage!=='working'||workout.paused===true)return;\r\n const value=workout.kind==='reps'?Number(workout.workingReps):Number(workout.activeSeconds);\r\n if(!Number.isFinite(value)||value<=0)return;\r\n const previous=pendingQuestDetails.get(workout.mode);\r\n const prior=previous?.workout?.kind==='reps'?Number(previous.workout.workingReps):Number(previous?.workout?.activeSeconds)||0;\r\n if(!previous||value>=prior)pendingQuestDetails.set(workout.mode,detail);\r\n if(questReporterOwner===ownerId&&questReporter){for(const queued of pendingQuestDetails.values())try{questReporter.recordWorkout(queued);}catch{}return;}\r\n if(questReporterLoading||!questReporterRetry.canStart())return;\r\n questReporterRetry.start();\r\n const loadingOwner=ownerId,token=++questReporterToken;\r\n questReporterLoading=loadQuestProgressReporterModule().then(module=>{\r\n  questReporterRetry.succeed();\r\n  if(token!==questReporterToken||performanceOwner()!==loadingOwner)return;\r\n  questReporter=module.createQuestProgressReporter(loadingOwner);questReporterOwner=loadingOwner;\r\n  for(const queued of pendingQuestDetails.values())try{questReporter.recordWorkout(queued);}catch{}\r\n  pendingQuestDetails.clear();\r\n }).catch(()=>{if(token===questReporterToken)questReporterRetry.fail();}).finally(()=>{if(token===questReporterToken)questReporterLoading=null;});\r\n}\r\nlet preloadedEffectMode=null;\r\nfunction preloadEffectsFor(mode){\r\n if(!mode||mode===preloadedEffectMode)return;preloadedEffectMode=mode;\r\n loadWorkoutEffectsModule().then(module=>module.preloadWorkoutEffects?.(mode)).catch(()=>{preloadedEffectMode=null;});\r\n}\r\nwindow.addEventListener('myr5:exercise-selected',()=>setTimeout(()=>preloadEffectsFor($('movement')?.value),0));\r\nfunction routeWorkoutEffects(detail){\r\n latestWorkoutEffects=detail;\r\n if(document.body.dataset.tracking==='true')preloadEffectsFor(detail?.workout?.mode);\r\n routeQuestWorkoutProgress(detail);\r\n if(detail?.workout?.ownerId&&detail.workout.ownerId===performanceOwner())ensureRetainedPower();\r\n pendingRetainedDetail=latestRetainableWorkoutSnapshot(pendingRetainedDetail,detail,retainedOwner??performanceOwner());\r\n if(retainedPower?.observe(detail)){avatarPower?.refresh();publishRetainedPower();}\r\n if(document.body.dataset.tracking!=='true'||document.hidden){workoutEffects?.invalidate();return;}\r\n if(!detail?.workout?.active){workoutEffects?.pose(detail);return;}\r\n if(workoutEffects){workoutEffects.pose(detail);return;}\r\n if(workoutEffectsLoading||!workoutEffectsRetry.canStart())return;\r\n workoutEffectsRetry.start();\r\n const token=workoutEffectsToken;\r\n workoutEffectsLoading=loadWorkoutEffectsModule().then(module=>{\r\n  workoutEffectsRetry.succeed();\r\n  if(token!==workoutEffectsToken||document.body.dataset.tracking!=='true'||document.hidden||!latestWorkoutEffects?.workout?.active)return;\r\n  const host=document.getElementById('coachOverlay');if(!host)return;\r\n  const session=String(latestWorkoutEffects.workout.sessionId||'');let seed=2166136261;for(let i=0;i<session.length;i++)seed=Math.imul(seed^session.charCodeAt(i),16777619);\r\n  workoutEffects=module.mountWorkoutEffects({host,sound:physicalSound,seed:seed>>>0});workoutEffects.pose(latestWorkoutEffects);\r\n }).catch(()=>{if(token===workoutEffectsToken)workoutEffectsRetry.fail();}).finally(()=>{if(token===workoutEffectsToken)workoutEffectsLoading=null;});\r\n}\r\n// R9-OFFLINE: the pinned pose tracker, same-origin (scripts/mediapipe.mjs) and in the Starter download, so a set runs offline.\r\nconst MEDIAPIPE='/vendor/mediapipe/0.10.14';\r\nlet lastTime=-1,frames=0,timing=0,windowStart=0,lastUi=0;\r\n// Tracker delegate: GPU first, CPU fallback. CPU was the baseline because a Pixel's GPU path once lost its WebGL\r\n// context, so any GPU failure (load error, detect throw, context loss, NaN landmarks, or blind while a CPU probe\r\n// sees a person) drops this set to CPU and is remembered on the device. ?poseDelegate=CPU|GPU forces one for A/B.\r\nconst DELEGATE_KEY='myr5-pose-delegate',BLIND_PROBE_MS=3000;\r\nlet gpuSeen=false,blindSince=0,probe=null;\r\nfunction preferredDelegate(){\r\n  const forced=new URLSearchParams(location.search).get('poseDelegate')?.toUpperCase();\r\n  if(forced==='CPU'||forced==='GPU')return forced;\r\n  try{return localStorage.getItem(DELEGATE_KEY)==='CPU'?'CPU':'GPU';}catch{return 'GPU';}\r\n}\r\nfunction rememberCpu(reason){state.delegateNote=reason;console.warn('Pose tracker on CPU: '+reason);try{localStorage.setItem(DELEGATE_KEY,'CPU');}catch{}}\r\nasync function loadTracker(delegate,run){\r\n  // Our own canvas on GPU so a lost WebGL context is seen (MediaPipe runs its GL on options.canvas).\r\n  const canvas=delegate==='GPU'?(typeof OffscreenCanvas==='function'?new OffscreenCanvas(1,1):document.createElement('canvas')):undefined;\r\n  let expired=false;\r\n  const loading=api.PoseLandmarker.createFromOptions(files,{...(canvas?{canvas}:{}),baseOptions:{modelAssetPath:MEDIAPIPE+'/pose_landmarker_lite.task',delegate},runningMode:'VIDEO',numPoses:1});\r\n  loading.then(created=>{if(run!==generation||expired)created.close();},()=>{});\r\n  let created;\r\n  try{created=await timeout(loading,60000,'Tracker loading took too long. Tap Start to retry.');}catch(error){expired=true;throw error;}\r\n  canvas?.addEventListener('webglcontextlost',()=>{created.myr5Lost=true;});\r\n  return created;\r\n}\r\nasync function toCpu(run,reason){\r\n  rememberCpu(reason);try{tracker?.close();}catch{}tracker=null;\r\n  const cpu=probe?.tracker||await loadTracker('CPU',run);probe=null;\r\n  if(run!==generation){try{cpu.close();}catch{}return;}\r\n  tracker=cpu;state.delegate='CPU';\r\n}\r\nfunction closeProbe(){try{probe?.tracker?.close();}catch{}probe=null;}\r\nconst ACTIVE_PHASES=new Set(['camera','model','tracking','preparation-rest','manual-starting','manual']);\r\nconst FROZEN_SETTINGS=['movement','goal','duration','camera'];\r\nlet frozenSettings=null;\r\nconst freezeSettings=()=>{frozenSettings=Object.fromEntries(FROZEN_SETTINGS.map(id=>[id,$(id).value]));};\r\nconst releaseOwner=()=>{pod?.workoutOwner?.stop();};\r\nlet manual=null,manualFrame=0,disposed=false,workoutTransition=Promise.resolve(),cameraStartTransition=Promise.resolve(),cameraAcquisition=null;\r\nlet danceMode=false,danceGeneration=0,danceHost=null,danceVideoAnchor=null,danceError=null,danceStartPromise=null;\r\nconst manualStartGate=new ManualStartGate();\r\nlet session=new MovementSession('squat');\r\nlet plankFlight=null,plankLoading=false;\r\nfunction dropPlankFlight(){plankFlight?.stop();plankFlight=null;session.flightAllowed=false;session.flightEligible=false;}\r\nconst state={version:'pod-1',phase:'idle',frames:0,poses:0,inferenceMs:0,rate:0,camera:null,delegate:null,error:null,motion:session.snapshot()};\r\nwindow.myr5TestState=state;\r\nconst cameraWorkout=mountCameraWorkout({video:v,counter:$('primary'),onStop:()=>{if(EXERCISES[session.mode]?.endless&&state.phase==='tracking'){void finishEndless();return;}void stop();voice.say('Stopped.',{interrupt:true});},\r\n  // Downward correction can remove a false camera rep without creating free XP.\r\n  onAdjust:delta=>{if(delta>=0||state.phase!=='tracking'||state.motion.kind!=='reps')return;session.count=Math.max(0,session.count+delta);state.motion=session.snapshot();renderMotion(state.motion);}});\r\nfunction status(text){if($('status').textContent!==text)$('status').textContent=text;}\r\nconst clock=seconds=>`${Math.floor(seconds/60)}:${String(Math.floor(seconds%60)).padStart(2,'0')}`;\r\n// Subway Burpees runner is open to every path ONLY while started from the SUBWAY BURPEES key; the library and BEGIN keep the path locks (Ian, 7 Oct).\r\n// var: controls() can run before this line during start-up.\r\nvar coolKey=false,coverKey=false;const pathAccess=mode=>coolKey&&EXERCISES[mode]?.runner?{allowed:true,path:null,reason:null}:workoutPathAccess(mode);\r\nfunction controls(busy){$('start').disabled=busy||pod?.canStart($('movement').value)===false;$('camera').disabled=busy;$('coolCardio').disabled=busy;$('coverCrisis').disabled=busy;$('stop').disabled=!busy;$('stop').hidden=!busy;$('goal').disabled=busy;$('movement').disabled=busy;$('duration').disabled=busy;$('reset').disabled=busy;$('widest').disabled=busy||!stream||state.phase!=='tracking';document.body.dataset.tracking=String(busy);$('cameraWorkout')?.classList.toggle('plank-flight-host',busy&&session.mode==='plank-flight');cameraWorkout.setActive(busy&&state.phase==='tracking');$('previewLabel').textContent=state.phase==='tracking'?'TRACKING':'CAMERA';}\r\nasync function refreshLenses(){\r\n  const cameras=await listCameras(),selected=$('camera').value;\r\n  $('camera').querySelectorAll('option[data-device]').forEach(o=>o.remove());\r\n  cameras.forEach((camera,i)=>{const option=document.createElement('option');option.value=deviceChoice(camera.id);option.dataset.device='';option.textContent=camera.label||`Camera ${i+1}`;$('camera').append(option);});\r\n  if([...$('camera').options].some(o=>o.value===selected))$('camera').value=selected;\r\n  return cameras;\r\n}\r\nasync function showLensInfo(track){\r\n  let cameras=[];try{cameras=await timeout(refreshLenses(),3000,'Camera list unavailable.');}catch{}\r\n  const zoom=await timeout(widestZoom(track),3000,'Zoom unavailable.').catch(()=>({supported:false,applied:false}));if(!stream||!stream.getTracks().includes(track))return;\r\n  const report=cameraReport(track,cameras,zoom);\r\n  const actualId=track.getSettings().deviceId;if(cameras.some(d=>d.id===actualId))$('camera').value=deviceChoice(actualId);\r\n  $('lensInfo').textContent=report.label+' · '+(zoom.applied?`widest exposed zoom ${zoom.zoom}×`:zoom.supported?'Wider zoom could not be applied.':'This lens exposes no zoom control.')+' Follow the camera cue for your exercise.';\r\n  $('cameraDetails').textContent=JSON.stringify(report,null,2);\r\n  // Send only the camera availability/settings shown above to the local test PC.\r\n  // No camera images, body coordinates, or exercise history are sent.\r\n  // Only the local dev server (serve.cjs) has this route; the Worker answers 404, so skip it elsewhere.\r\n  if(['localhost','127.0.0.1'].includes(location.hostname))fetch('/camera-info',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(report)}).catch(()=>{});\r\n}\r\nfunction release(){\r\n  dropWorkoutEffects();\r\n  cameraAcquisition?.abort();cameraAcquisition=null;\r\n  frozenSettings=null;\r\n  poseFrames.stop();cancelAnimationFrame(frame);cancelAnimationFrame(manualFrame);manualFrame=0;if(tracker){try{tracker.close();}catch{}tracker=null;}closeProbe();draw=null;\r\n  stream?.getTracks().forEach(t=>t.stop());stream=null;v.pause();v.srcObject=null;g.clearRect(0,0,c.width,c.height);\r\n  state.camera=null;state.delegate=null;state.rate=0;\r\n}\r\nfunction resetMovement(){\r\n  cues.reset();\r\n  const mode=$('movement').value,config=MOVEMENTS[mode];\r\n  pod?.configure(mode);\r\n  const timed=['pace','steps','jumps'].includes(config.kind);$('roundControl').hidden=!timed;\r\n  session=new MovementSession(mode);\r\n  state.motion=session.snapshot();$('hint').textContent=config.hint;renderMotion(state.motion);\r\n  window.dispatchEvent(new Event('myr5:movement-configured'));\r\n  status(state.phase==='tracking'?config.hint:'Ready');\r\n}\r\nfunction renderMotion(m){\r\n  const rawMotion=m;m=pod?.workoutMotion?.(m)||m;\r\n  $('movementName').textContent=m.name;\r\n  const label=m.kind==='hold'?'hold time':m.kind==='pace'?'active time':m.kind==='steps'?'steps':m.kind==='jumps'?'jumps':'reps';\r\n  setFlipValue($('primary'),m.kind==='hold'?clockDigits(m.totalHold):m.kind==='pace'?clockDigits(m.active):countDigits(m.count),label);\r\n  $('primaryLabel').textContent=label;\r\n  const time=m.remaining===null?`Session ${clock(m.elapsed)}`:`Remaining ${clock(m.remaining)}`;\r\n  $('secondary').textContent=m.kind==='hold'?`Best ${clock(m.bestHold)} · Total ${clock(m.totalHold)}`:m.kind==='pace'?`Moving ${clock(m.active)} · Hand pace ${m.speed.toFixed(1)}×`:(m.kind==='steps'||m.kind==='jumps')?`${time} · ${Math.round(m.cadence)}/${m.kind==='steps'?'min':'min'}`:time;\r\n  $('activity').style.width=`${Math.round(m.progress*100)}%`;\r\n  $('measurement').textContent=m.measurement;\r\n  $('jointReadings').textContent=m.jointReadings||'';\r\n  $('countState').textContent=!m.tracking?'Waiting for joints':(['squat','pushup','jumping'].includes(m.mode)&&!m.calibrated)?'Setting start':m.phase==='bottom'?'Return to start to count':m.kind==='hold'?'Hold timer':m.kind==='pace'?'Round timer':'Counter ready';\r\n  $('paceNote').hidden=m.kind!=='pace';\r\n  const paused=!m.tracking||(m.kind==='hold'&&!m.progress)||(m.kind==='reps'&&!m.calibrated);\r\n  cameraWorkout.show({status:paused?m.message:'',reps:m.kind==='reps'&&state.phase==='tracking'});\r\n  pod?.render(rawMotion);\r\n  const instruction=pod?.flow?.workout?.instruction;if(instruction)$('status').textContent=typeof instruction==='string'?instruction:instruction.message||instruction.text||'';\r\n  const tools=document.querySelector('.workout-performance-controls'),workout=pod?.flow?.workout;\r\n  if(tools){const cameraStage=document.getElementById('cameraWorkout');if(state.phase==='tracking'&&cameraStage&&tools.parentElement!==cameraStage)cameraStage.append(tools);else if(state.phase!=='tracking'&&tools.parentElement===cameraStage)document.getElementById('bar').after(tools);tools.hidden=!['tracking','manual'].includes(state.phase);for(const button of tools.querySelectorAll('button')){const label=button.textContent;button.hidden=label==='Finish working set'?workout?.stage?.startsWith('preparation-'):label==='Break hold'?workout?.kind!=='hold'||workout.paused:label==='Resume hold'||label==='More recovery'?workout?.kind!=='hold'||!workout.paused:false;}const variation=tools.querySelector('[data-hold-variation]');if(variation){variation.hidden=true;variation.querySelector('select').disabled=true;}const recovery=tools.querySelector('[data-hold-recovery]');if(recovery){recovery.hidden=!workout?.paused;recovery.textContent=`Recovery · ${Math.ceil(Math.max(0,(workout?.recoveryUntil-Date.now())/1000))}s. Resume whenever ready.`;}}\r\n}\r\nfunction stop(message='Stopped. Your results stay here until the next start.',{interrupt=true,home=true}={}){\r\n  dropPlankFlight();\r\n  if(state.phase==='manual'||state.phase==='manual-starting')return pauseManualWhenReady({home});\r\n  const unfinished=interrupt&&['camera','model','tracking','preparation-rest'].includes(state.phase);\r\n  voice.cancel();\r\n  generation++;release();controls(false);state.phase='idle';status(message);$('countState').textContent='Camera stopped';$('detail').textContent='Camera off · Tracker closed';\r\n  // #19/#56: a set cut short by STOP also goes home; the pod offers it back with its Continue popup.\r\n  if(unfinished)window.dispatchEvent(new Event('myr5:workout-stopped'));\r\n  if(unfinished)workoutTransition=Promise.resolve().then(()=>pod?.interruptCurrent(state.motion)).catch(error=>{status(error.message);}).finally(()=>{releaseOwner();controls(false);if(home)window.myr5Routes?.home?.();});\r\n  return workoutTransition;\r\n}\r\nfor(const type of ['input','change'])document.addEventListener(type,event=>{\r\n  if(!ACTIVE_PHASES.has(state.phase)||!frozenSettings||!FROZEN_SETTINGS.includes(event.target?.id))return;\r\n  event.stopImmediatePropagation();event.target.value=frozenSettings[event.target.id];\r\n},true);\r\nwindow.addEventListener('myr5:route-leave',event=>{if(['library','workout'].includes(event.detail?.id)&&event.detail.reason==='workout-begin')return;if(danceMode)return;if(ACTIVE_PHASES.has(state.phase))void stop('Workout stopped. Your progress is saved.',{home:false});});\r\nfunction timeout(promise,ms,message){let timer;return Promise.race([promise,new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(message)),ms);})]).finally(()=>clearTimeout(timer));}\r\nfunction restoreDanceVideo(){\r\n  if(danceVideoAnchor?.isConnected){danceVideoAnchor.parentNode.insertBefore(v,danceVideoAnchor.nextSibling);danceVideoAnchor.remove();}\r\n  danceVideoAnchor=null;danceHost=null;\r\n}\r\nfunction stopDance(reason){\r\n  if(!danceMode&&!danceStartPromise)return;\r\n  danceMode=false;danceGeneration++;generation++;release();restoreDanceVideo();state.phase='idle';state.error=reason?.message||null;\r\n  if(reason&&danceError)try{danceError(reason);}catch{}\r\n  danceError=null;danceStartPromise=null;\r\n}\r\nasync function start({dance=false}={}){\r\n  if(dance&&ACTIVE_PHASES.has(state.phase)&&!danceMode)await stop('Switching to Dance.',{home:false});\r\n  if(!dance&&danceMode)stopDance();\r\n  if(dance){danceMode=true;danceGeneration++;}\r\n  const danceRun=dance?danceGeneration:0;\r\n  if(document.hidden)return;\r\n  const access=pathAccess($('movement').value);if(!dance&&!access.allowed){status(access.reason);controls(false);return;}\r\n  if(ACTIVE_PHASES.has(state.phase))stop('Switching workout input…');\r\n  try{await Promise.all([workoutTransition,cameraStartTransition]);}catch{return;}\r\n  if(dance&&(!danceMode||danceRun!==danceGeneration))return;\r\n  if(document.hidden)return;\r\n  if($('camera').value==='manual')$('camera').value='user';\r\n  preloadEffectsFor($('movement')?.value);\r\n  const run=++generation;release();state.phase='camera';if(!dance)controls(true);state.error=null;if(!dance){resetMovement();freezeSettings();}\r\n  $('trainingView').scrollIntoView({block:'start',behavior:'auto'});\r\n  state.frames=0;state.poses=0;state.inferenceMs=0;if(!dance){status('Opening camera…');voice.say(MOVEMENTS[session.mode].name+' selected.',{key:'movement',interrupt:true});voice.say('Get into position.',{key:'setup'});$('detail').textContent='Waiting for video';}\r\n  let settleCameraStart;\r\n  cameraStartTransition=new Promise(resolve=>{settleCameraStart=resolve;});\r\n  try{\r\n    if(!dance){const beginning=pod.beginSet(session.mode,{manual:false});\r\n      beginning.then(()=>{if(run!==generation)void pod.interruptCurrent(state.motion).catch(()=>{});},()=>{});\r\n      await timeout(beginning,15000,'Workout storage is taking too long. Close other Coach tabs and tap Begin again.');}\r\n    if(run!==generation||dance&&(danceRun!==danceGeneration||!danceMode)){if(!dance)await pod.interruptCurrent(state.motion);return;}\r\n    if(!navigator.mediaDevices?.getUserMedia)throw new Error('Open this HTTPS site in a browser that supports camera access.');\r\n    const selected=$('camera').value;\r\n    cameraAcquisition=new AbortController();\r\n    const incoming=await openCamera(selected,30,navigator.mediaDevices,{signal:cameraAcquisition.signal});\r\n    if(run!==generation){incoming.getTracks().forEach(t=>t.stop());return;}\r\n    stream=incoming;v.srcObject=stream;v.muted=true;\r\n    await timeout(v.play(),10000,'The camera opened but sent no video. Select the other camera and retry.');\r\n    if(run!==generation)return;\r\n    state.camera=cameraFacing(stream.getVideoTracks()[0],selected);\r\n    v.style.transform=c.style.transform=state.camera==='user'?'scaleX(-1)':'none';\r\n    await showLensInfo(stream.getVideoTracks()[0]);if(run!==generation)return;\r\n    status('Loading tracker…');$('detail').textContent=`Video ${v.videoWidth} × ${v.videoHeight}`;state.phase='model';\r\n    api=api||await timeout(import(`${MEDIAPIPE}/vision_bundle.mjs`).catch(()=>{throw Error('Download Starter in Settings › Downloads to train offline.');}),20000,'Tracker library did not download. Check the phone’s internet connection.');\r\n    if(run!==generation)return;\r\n    files=files||await timeout(api.FilesetResolver.forVisionTasks(MEDIAPIPE+'/wasm'),20000,'Tracker runtime did not download. Check the phone’s internet connection.');\r\n    if(run!==generation)return;\r\n    let delegate=preferredDelegate(),created=null;\r\n    // One warm-up detect while \"Loading tracker…\" shows: the GPU's first run compiles its shaders (~5 s cold on a\r\n    // desktop) and a throw here means a broken GPU path, so it falls back before the set starts.\r\n    if(delegate==='GPU'){try{created=await loadTracker('GPU',run);if(run===generation)created.detectForVideo(v,performance.now());}catch(error){try{created?.close();}catch{}created=null;if(run!==generation)return;rememberCpu('GPU failed to start: '+error.message);delegate='CPU';}}\r\n    if(run!==generation)return;\r\n    created??=await loadTracker('CPU',run);\r\n    if(run!==generation)return;\r\n    tracker=created;draw=new api.DrawingUtils(g);state.delegate=delegate;state.phase='tracking';if(!dance)void import('./modules/vault/vault-store.mjs').then(m=>m.bump('ar-session')).catch(()=>{});\r\n    gpuSeen=false;blindSince=performance.now();\r\n    if(!dance)controls(true);else if(danceHost&&v.parentNode!==danceHost)danceHost.append(v);\r\n    lastTime=-1;frames=0;timing=0;windowStart=performance.now();lastUi=0;\r\n    if(!dance){status(MOVEMENTS[session.mode].hint);voice.say(MOVEMENTS[session.mode].hint,{interrupt:true});}loop(run);\r\n  }catch(error){\r\n    if(run!==generation){if(!dance)void pod.interruptCurrent(state.motion).catch(()=>{});return;}\r\n    generation++;release();if(dance){restoreDanceVideo();danceMode=false;state.phase='error';state.error=error.message;if(danceError)try{danceError(error);}catch{}danceError=null;danceStartPromise=null;return;}try{await timeout(pod.interruptCurrent(state.motion),5000,'Workout cleanup timed out.');}catch{}releaseOwner();state.phase='error';window.dispatchEvent(new Event('myr5:workout-stopped'));controls(false);state.error=error.message;\r\n    status(error.name==='NotAllowedError'?'Allow camera access for this site, then tap Begin.':error.message);voice.say($('status').textContent,{interrupt:true});$('detail').textContent='Camera off · Tracker closed';\r\n  }finally{settleCameraStart();}\r\n}\r\nwindow.myr5DanceCamera={\r\n  get video(){return v;},\r\n  get sound(){return {muted:physicalSound.muted,volume:physicalSound.volume};},\r\n  async start({host,onError}={}){\r\n    if(!host?.append)throw new TypeError('Dance camera needs a host element.');\r\n    if(danceStartPromise)stopDance();\r\n    const token=danceGeneration+1;\r\n    danceHost=host;danceError=typeof onError==='function'?onError:null;\r\n    danceVideoAnchor=document.createComment('myr5-dance-camera-video-anchor');\r\n    v.parentNode?.insertBefore(danceVideoAnchor,v);\r\n    danceStartPromise=(async()=>{\r\n      await start({dance:true});\r\n      if(token!==danceGeneration)throw new Error('Dance camera start was cancelled.');\r\n      if(!danceMode||state.phase!=='tracking')throw new Error(state.error||'Dance camera did not start.');\r\n      if(v.parentNode!==host)host.append(v);\r\n      return v;\r\n    })();\r\n    try{return await danceStartPromise;}catch(error){if(danceMode&&token===danceGeneration)stopDance(error);throw error;}\r\n  },\r\n  stop(){stopDance();}\r\n};\r\nasync function loop(run){\r\n  if(run!==generation||state.phase!=='tracking'||!tracker)return;\r\n  try{\r\n    const now=performance.now();\r\n    if(v.readyState>=2&&v.currentTime!==lastTime){\r\n      lastTime=v.currentTime;if(c.width!==v.videoWidth||c.height!==v.videoHeight){c.width=v.videoWidth;c.height=v.videoHeight;}\r\n      g.clearRect(0,0,c.width,c.height);\r\n      const before=performance.now();let result=null;\r\n      try{result=tracker.detectForVideo(v,now);}catch(error){if(state.delegate!=='GPU')throw error;}\r\n      const elapsed=performance.now()-before;\r\n      if(state.delegate==='GPU'&&(!result||tracker.myr5Lost||result.landmarks[0]?.some(q=>!Number.isFinite(q.x+q.y)))){\r\n        await toCpu(run,!result?'GPU detect threw':tracker?.myr5Lost?'GPU WebGL context lost':'GPU returned invalid landmarks');\r\n        if(run===generation)schedulePose(run);return;\r\n      }\r\n      // A GPU that never sees anyone may be silently broken: every few blind seconds, ask a CPU probe the same frame.\r\n      if(state.delegate==='GPU'&&!gpuSeen){\r\n        if(result.landmarks.length){gpuSeen=true;closeProbe();}\r\n        else if(now-blindSince>=BLIND_PROBE_MS){\r\n          if(!probe){const p=probe={tracker:null};loadTracker('CPU',run).then(t=>{if(probe===p)p.tracker=t;else try{t.close();}catch{}},()=>{});}\r\n          else if(probe.tracker){blindSince=now;let detected=false;try{detected=probe.tracker.detectForVideo(v,now).landmarks.length>0;}catch{closeProbe();}if(detected){await toCpu(run,'GPU blind while CPU sees a pose');if(run===generation)schedulePose(run);return;}}\r\n        }\r\n      }\r\n      state.frames++;frames++;timing+=elapsed;state.poses=result.landmarks.length;syncMask();\r\n      const p=result.landmarks[0]?.slice(0,27);\r\n      if(p){draw.drawConnectors(p,api.PoseLandmarker.POSE_CONNECTIONS.filter(b=>b.start<=26&&b.end<=26),{color:'#bc89ff',lineWidth:3});draw.drawLandmarks(p.filter(q=>q.visibility>=.45),{color:'#aaffd9',radius:3});draw.drawLandmarks(p.filter(q=>q.visibility<.45),{color:'#ffad66',radius:3});}\r\n      if(danceMode){\r\n        const poseDetail={points:result.landmarks[0]||null,world:result.worldLandmarks?.[0]||null,width:v.videoWidth,height:v.videoHeight,mirrored:state.camera==='user',now,frameMs:frameTime(now),mask:readMask(result),counting:false};\r\n        window.dispatchEvent(new CustomEvent('myr5:pose',{detail:poseDetail}));\r\n        if(state.phase==='tracking')schedulePose(run);\r\n        return;\r\n      }\r\n      session.runnerGame=!!EXERCISES[session.mode]?.runner&&!coverKey;session.coverGame=coverKey;\r\n      session.flightAllowed=session.mode==='plank-flight'&&plankFlight?.creditAllowed===true;\r\n      session.flightPortalRest=session.mode==='plank-flight'&&plankFlight?.run?.phase==='portal';\r\n      state.motion=session.update(p,now,v.videoWidth/v.videoHeight,result.worldLandmarks?.[0]);\r\n      // The room coach gets every landmark (ankles for kicks) and walks in once the set is counting.\r\n      const poseDetail={points:result.landmarks[0]||null,world:result.worldLandmarks?.[0]||null,width:v.videoWidth,height:v.videoHeight,mirrored:state.camera==='user',now,frameMs:frameTime(now),mask:readMask(result),counting:state.motion.count>0||state.motion.totalHold>0||state.motion.active>0,flightPose:state.motion.flightPose??null};\r\n      window.dispatchEvent(new CustomEvent('myr5:pose',{detail:poseDetail}));\r\n      const cueMotion=['hold','pace'].includes(state.motion.kind)?{...state.motion,remaining:Math.max(0,pod.goal()-(state.motion.kind==='hold'?state.motion.totalHold:state.motion.active))}:state.motion;\r\n      const events=cues.update(cueMotion,now),encouragement=pod.encouragement(state.motion,now,events);if(encouragement)events.push(encouragement);\r\n      for(const cue of events){window.dispatchEvent(new CustomEvent('myr5:cue',{detail:{key:cue.key==='encouragement'?'time':cue.key}}));voice.say(cue.text,{key:cue.key,interrupt:cue.key==='complete'||cue.key==='ready'});}\r\n      const effectActiveBeforeConsume=pod.flow?.active;\r\n      const pendingConsume=pod.consume(state.motion,Date.now());\r\n      const effectWorkout=pod.flow?.workout,effectActive=pod.flow?.active??effectActiveBeforeConsume;\r\n      const effectDetail=workoutEffectsSnapshot({pose:poseDetail,motion:state.motion,workout:effectWorkout,sessionId:effectActive?.localId??run,ownerId:effectActive?.performanceOwner??null,startedAt:effectActive?.startedAt??null,mode:effectActive?.mode??session.mode,tracking:state.phase==='tracking'&&document.body.dataset.tracking==='true',visible:!document.hidden,canCount:pod.canCount(),workingSet:pod.flow?.phase==='set'});\r\n      window.dispatchEvent(new CustomEvent('myr5:workout-effects',{detail:effectDetail}));routeWorkoutEffects(effectDetail);\r\n      if(await pendingConsume){renderMotion(state.motion);cinematics.play('post');return;}\r\n      if(state.phase==='preparation-rest')return;\r\n      if(now-windowStart>=1000){state.rate=frames*1000/(now-windowStart);state.inferenceMs=timing/frames;frames=0;timing=0;windowStart=now;}\r\n      if(now-lastUi>=160){renderMotion(state.motion);status(state.motion.message);$('detail').textContent=`${state.rate.toFixed(0)} tracking updates/s (${state.delegate}) · ${state.inferenceMs.toFixed(0)} ms/update · ${v.videoWidth} × ${v.videoHeight}`;lastUi=now;}\r\n      if(state.motion.complete){renderMotion(state.motion);stop('Round complete. Camera and tracker stopped.');voice.say('Round complete. Well done.',{interrupt:true});return;}\r\n    }\r\n    if(state.phase==='tracking')schedulePose(run);\r\n  }catch(error){if(run!==generation)return;if(danceMode){stopDance(error);return;}generation++;release();controls(false);await pod.interruptCurrent(state.motion).catch(()=>{});releaseOwner();voice.cancel();state.phase='error';state.error=error.message;status('Tracking stopped: '+error.message);voice.say($('status').textContent,{interrupt:true});$('detail').textContent='Camera off · Tracker closed';}\r\n}\r\nfor(const [id,config] of Object.entries(MOVEMENTS)){const option=document.createElement('option');option.value=id;option.textContent=config.name;$('movement').appendChild(option);}\r\n$('start').addEventListener('click',()=>{if(!pathAccess($('movement').value).allowed){showUnlockGate({onCardio:()=>$('coolCardio').click()});return;}$('camera').value==='manual'?start():library.introduce();});$('stop').addEventListener('click',async()=>{if(state.phase==='manual'){await pauseManualUi();return;}void stop();voice.say('Stopped.',{interrupt:true});});\r\n$('reset').addEventListener('click',()=>{resetMovement();voice.say('Count reset. Return to your starting position.',{interrupt:true});});\r\n$('goal').addEventListener('change',()=>resetMovement());\r\n$('movement').addEventListener('change',event=>{const active=state.phase==='tracking';if(!pathAccess($('movement').value).allowed){const first=Object.keys(MOVEMENTS).find(mode=>workoutPathAccess(mode).allowed);if(first)$('movement').value=first;else{controls(false);status('Choose two workout paths to begin.');return;}}if(!event.detail?.automatic)window.dispatchEvent(new Event('myr5:exercise-selected'));resetMovement();controls(active);if(active)library.introduce();});\r\n$('duration').addEventListener('change',()=>resetMovement());\r\n// Subway Burpees key under the dials: runs the endless runner from any path/level, then puts the dials back (nothing saved changes).\r\n{let back=null;\r\n const restore=async()=>{if(!back)return;try{await workoutTransition;}catch{}await new Promise(r=>setTimeout(r,300));\r\n  if(!back||state.phase!=='idle')return;const was=back;back=null;coolKey=false;$('movement').value=was;$('movement').dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));};\r\n $('coolCardio').addEventListener('click',()=>{\r\n  if(ACTIVE_PHASES.has(state.phase)||document.hidden)return;window.dispatchEvent(new CustomEvent('myr5:hardware-detent',{detail:{kind:'switch'}}));\r\n  back=back||$('movement').value;coolKey=true;$('movement').value='jogging';$('movement').dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));Promise.resolve(start()).finally(()=>{if(document.body.dataset.tracking!=='true')void restore();});});\r\n new MutationObserver(()=>{if(document.body.dataset.tracking!=='true')void restore();}).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});}\r\n// Crouching Crisis key: same jogging plumbing as Subway Burpees (dial, restore, XP, finish) but mounts the cover shooter instead of the runner.\r\n{let pressing=false;\r\n $('coverCrisis').addEventListener('click',()=>{if(ACTIVE_PHASES.has(state.phase)||document.hidden)return;coverKey=true;pressing=true;try{$('coolCardio').click();}finally{pressing=false;}});\r\n $('coolCardio').addEventListener('click',()=>{if(!pressing)coverKey=false;});\r\n let was=false;new MutationObserver(()=>{const on=document.body.dataset.tracking==='true';if(was&&!on)coverKey=false;was=on;}).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});}\r\n// Jet Plank Joyride uses normal path eligibility; the dedicated key explains a locked core path.\r\n{let previous=null,entered=false;\r\n const restore=async()=>{await workoutTransition;if(!previous||state.phase!=='idle')return;const was=previous;previous=null;entered=false;$('movement').value=was;$('movement').dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));};\r\n $('plankFlight').addEventListener('click',()=>{\r\n  if(ACTIVE_PHASES.has(state.phase)||document.hidden)return;\r\n  const access=pathAccess('plank-flight');if(!access.allowed){status(access.reason);voice.say(access.reason,{interrupt:true});return;}\r\n  if(!$('movement').querySelector('option[value=\"plank-flight\"]')){const option=document.createElement('option');option.value='plank-flight';option.textContent='Jet Plank Joyride';$('movement').append(option);}\r\n  previous=$('movement').value;entered=false;$('movement').value='plank-flight';$('movement').dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));Promise.resolve(start()).finally(()=>{if(state.phase==='idle')void restore();});\r\n });\r\n new MutationObserver(()=>{$('plankFlight').disabled=document.body.dataset.tracking==='true';\r\n  if(document.body.dataset.tracking==='true')entered=true;\r\n  if(entered&&previous&&state.phase==='idle')void restore();\r\n }).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});}\r\nfunction soundSwitch(){$('toggleVoice').textContent=voice.enabled?'ON':'OFF';$('toggleVoice').dataset.on=String(voice.enabled);$('toggleVoice').setAttribute('aria-checked',String(voice.enabled));}\r\n$('toggleVoice').addEventListener('click',()=>{voice.setEnabled(!voice.enabled);soundSwitch();voice.say(voice.enabled?'Voice on.':'Voice off.',{interrupt:true});});\r\n$('testVoice').addEventListener('click',()=>{voice.setEnabled(true);soundSwitch();voice.say('Coach ready. Move at your own pace. One. Two. Three. Thirty seconds left.',{interrupt:true});});\r\n$('camera').addEventListener('change',()=>{if($('camera').value!=='manual')voice.say('Camera selected.',{interrupt:true});if(state.phase==='tracking')start();});\r\n$('widest').addEventListener('click',async()=>{\r\n  const track=stream?.getVideoTracks()[0];if(!track)return;$('widest').disabled=true;\r\n  try{const cameras=await refreshLenses(),wide=findUltrawide(cameras);if(state.camera!=='user'&&wide&&track.getSettings().deviceId!==wide.id){$('camera').value=deviceChoice(wide.id);await start();}else{await showLensInfo(track);if(state.phase==='tracking')resetMovement();}}\r\n  catch(error){$('lensInfo').textContent='Could not change the lens: '+error.message;}\r\n  finally{$('widest').disabled=state.phase!=='tracking';}\r\n});\r\n let workoutAdapter=null;\r\n const openingWorkoutAdapter=openGuestWorkoutAdapter({exerciseKeys:Object.keys(MOVEMENTS)});\r\n const workoutReady=timeout(openingWorkoutAdapter,10000,'Workout storage did not open. Reload Coach and try again.').then(adapter=>{workoutAdapter=adapter;workouts.ownerId=adapter.ownerId;if(disposed)adapter.close();return adapter;},error=>{void openingWorkoutAdapter.then(adapter=>adapter.close(),()=>{});throw error;});\r\n const workouts={paused:(...args)=>workoutReady.then(value=>value.paused(...args)),start:(...args)=>workoutReady.then(value=>value.start(...args)),update:(...args)=>workoutReady.then(value=>value.update(...args)),pause:(...args)=>workoutReady.then(value=>value.pause(...args)),complete:(...args)=>workoutReady.then(value=>value.complete(...args)),interrupt:(...args)=>workoutReady.then(value=>value.interrupt(...args)),unfinished:(...args)=>workoutReady.then(value=>value.unfinished(...args)),close(){disposed=true;if(workoutAdapter)workoutAdapter.close();else void workoutReady.then(value=>value.close(),()=>{});}};\r\n window.addEventListener('pagehide',()=>{if(danceMode)stopDance(new Error('Dance camera stopped because the page was left.'));const stopped=ACTIVE_PHASES.has(state.phase)&&!danceMode?stop('Workout stopped.',{home:false}):workoutTransition;releaseOwner();workouts.close();void Promise.allSettled([stopped,cameraStartTransition]);});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden&&danceMode)stopDance(new Error('Dance camera paused because the page was hidden.'));else if(document.hidden&&ACTIVE_PHASES.has(state.phase))void stop('Paused while the page was hidden. Tap Begin to start a new session.',{home:false});});\r\n pod=initPod({voice,movements:MOVEMENTS,workouts,onStop:()=>{if(state.phase==='manual'){generation++;release();controls(false);state.phase='idle';manual=null;}else stop('Set ended. Your camera is off.',{interrupt:false});},onNext:async next=>{await library.introduce(next?.mode);if(next)pod.setGoal(next.goal);}});\r\n window.addEventListener('myr5:preparation-rest-start',()=>{\r\n  if(state.phase!=='tracking')return;\r\n  state.phase='preparation-rest';poseFrames.stop();cameraWorkout.setActive(false);\r\n  $('previewLabel').textContent='RECOVERY';\r\n });\r\n window.addEventListener('myr5:preparation-rest-end',()=>{\r\n  if(state.phase!=='preparation-rest'||document.hidden||!tracker)return;\r\n  cues.reset();session=new MovementSession(session.mode);state.motion=session.snapshot();pod.rebaseTracking();\r\n  lastTime=-1;frames=0;timing=0;windowStart=performance.now();lastUi=0;\r\n  state.phase='tracking';cameraWorkout.setActive(true);$('previewLabel').textContent='TRACKING';renderMotion(state.motion);schedulePose(generation);\r\n });\r\nasync function repairWorkoutRewards(){const owner=performanceOwner();try{const adapter=await workoutReady,rows=await adapter.listPerformanceWorkouts();if(disposed||performanceOwner()!==owner)return;const repaired=reconcilePerformanceWorkouts(rows,{owner});if(repaired.failed.length)status('Your workout is saved. Rewards will retry when progress storage is available.');}catch(error){if(!disposed)console.warn('Workout reward repair is waiting:',error.message);}}\r\nvoid repairWorkoutRewards();for(const event of ['myr5:local-history-refresh','myr5:account-ready','storage'])window.addEventListener(event,()=>void repairWorkoutRewards());\r\n// P13D: optional pack UI may bind this actual owner after user intent; packs are not imported at startup.\r\nwindow.myr5WorkoutOwner=pod.workoutOwner;\r\nwindow.dispatchEvent(new Event('myr5:workout-owner-ready'));\r\nwindow.myr5CreatePackControl=async options=>{\r\n  const {createIsolatedPackControl}=await import('./packs/isolated-pack-control.mjs');\r\n  return createIsolatedPackControl({...options,workoutOwner:pod.workoutOwner});\r\n};\r\nresetMovement();watchUnlock();\r\nconst canUseWorkout=mode=>workoutPathAccess(mode).allowed;\r\ninitHardware({canUse:canUseWorkout});\r\nconst fullWorkout=mountFullWorkout({canUse:canUseWorkout,reason:mode=>workoutPathAccess(mode).reason,select:(mode,goal)=>{$('movement').value=mode;$('movement').dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));pod.setGoal(goal);},start:()=>start(),leaveRest:()=>pod.leave(),setCircuit:run=>pod.setCircuit(run),freeze:on=>pod.freeze(on),left:()=>pod.flow?.workout?.left??null,restEnd:()=>pod.flow?.restUntil||0,preload:mode=>preloadEffectsFor(mode),voice});\r\nwindow.myr5CoachGate=()=>!coverKey&&!coolKey&&(fullWorkout.running||/dance/i.test($('movement').value)); // 3D room coach: circuit + dance only\r\nsoundSwitch();\r\n const library=initLibrary({movements:MOVEMENTS,voice,canUse:canUseWorkout,onLocked:()=>showUnlockGate({onCardio:()=>$('coolCardio').click()}),onOpen:()=>stop('Workout stopped for the library. Your results are kept.',{home:false}),onSelect:mode=>{$('movement').value=mode;window.dispatchEvent(new Event('myr5:exercise-selected'));resetMovement();controls(false);},onStart:()=>{if(!document.hidden)start();},camera:()=>$('camera').value,movement:()=>$('movement').value});\r\nfunction refreshWorkoutPaths(){for(const option of $('movement').options){const allowed=canUseWorkout(option.value);option.disabled=!allowed;option.textContent=option.textContent.replace(/ · locked$/,'')+(allowed?'':' · locked');}if(!ACTIVE_PHASES.has(state.phase)&&!canUseWorkout($('movement').value)){const first=Object.keys(MOVEMENTS).find(canUseWorkout);if(first){$('movement').value=first;resetMovement();}}if(!ACTIVE_PHASES.has(state.phase))controls(false);library.refreshAccess();}\r\nwindow.addEventListener('myr5:performance-progress',refreshWorkoutPaths);window.addEventListener('myr5:account-ready',refreshWorkoutPaths);refreshWorkoutPaths();\r\n$('variationName').addEventListener('click',()=>library.introduce($('movement').value));\r\nmountHomeCharacter();\r\nensureRetainedPower();\r\nmountPodPlaces(document.getElementById('view'));\r\nlet retainedCardObserver=null;\r\nconst attachRetainedPower=()=>{const card=document.querySelector('.myr5-companion-card');if(!card)return false;ensureRetainedPower(card);retainedCardObserver?.disconnect();return true;};\r\nif(!attachRetainedPower()&&typeof MutationObserver==='function'){retainedCardObserver=new MutationObserver(attachRetainedPower);retainedCardObserver.observe(document.body,{childList:true,subtree:true});}\r\nwindow.addEventListener('pagehide',()=>{avatarPowerToken++;avatarPower?.stop();avatarPower=null;avatarPowerCard=null;avatarPowerLoadingCard=null;});\r\nwindow.addEventListener('pageshow',event=>{if(event.persisted)ensureRetainedPower();});\r\n// #19: today's newest paused/interrupted workout is offered by a Continue popup on the pod page (never the quilt).\r\n// A saved manual row can start a new camera session without deleting its old local record.\r\nmountContinueWorkout({unfinished:()=>workouts.unfinished(),idle:()=>state.phase==='idle',label:row=>row.metadata?.name||MOVEMENTS[row.mode]?.name||row.mode,canContinue:row=>EXERCISES[row.mode]?.runner?{allowed:true,path:null,reason:null}:workoutPathAccess(row.mode),onContinue:row=>{\r\n if(!MOVEMENTS[row.mode])return;\r\n // Subway Burpees is open on every path: re-run it through the SUBWAY BURPEES key (sets coolKey, restores the dial after).\r\n if(EXERCISES[row.mode]?.runner){$('coolCardio').click();return;}\r\n if(EXERCISES[row.mode]?.flight){$('plankFlight').click();return;}\r\n if(!canUseWorkout(row.mode)){status(workoutPathAccess(row.mode).reason);return;}\r\n $('movement').value=row.mode;window.dispatchEvent(new Event('myr5:exercise-selected'));resetMovement();\r\n void library.introduce(row.mode);\r\n}});\r\nwindow.addEventListener('myr5:ship-scene-ready',event=>{acceptShipRevealComplete(event);});\r\n// D30: the owner's achievements board. One hook: the Settings menu calls it now, the owner's portal (inverted triangle) later.\r\n// R-1017: achievements and the XP flight are one page; battle-pass-page.mjs owns myr5Menus.achievements.\r\nasync function showQuiltPortal(options){try{const {openQuiltPortal}=await import('./modules/portal/portal-entry.mjs');return !!await openQuiltPortal(options);}catch(error){console.warn('Quilt portal could not be opened.',error);return false;}}\r\nwindow.myr5Menus={...window.myr5Menus,portal:showQuiltPortal};\r\nlet starterPortalAttempted=false;\r\nfunction starterPortalReady(){\r\n const route=new URLSearchParams(location.search);\r\n // No isInstalled() gate: a browser visitor who has already finished setup (window.coachPlan set)\r\n // gets the same quilt-first landing as an installed one. New/signed-out visitors never reach this\r\n // point with a coachPlan, so they still see setup first.\r\n // A #route deep link (#pod, #ship, #food…) came for that scene; the quilt waits.\r\n return !route.has('panel')&&!hashRoute()&&!document.hidden&&!!window.coachPlan&&state.phase==='idle'&&document.body.dataset.cameraWorkout!=='true'&&document.body.dataset.tracking!=='true'&&document.body.dataset.screen!=='rest'&&!document.querySelector('dialog[open]');\r\n}\r\nwindow.addEventListener('myr5:coach-plan',()=>{\r\n if(starterPortalAttempted)return;\r\n starterPortalAttempted=true;\r\n if(starterPortalReady())void showQuiltPortal({shouldShow:starterPortalReady});\r\n});\r\n// D-ship-route: full-screen ship view, using the coach capsule renderer (loadCoachViewer below).\r\n// W2-2A: options pass through ({entrance:'always',hash:'#select'} is the oval's coach arrival).\r\nwindow.myr5Menus={...window.myr5Menus,ship:async(options={})=>{const {openShipView}=await import('./modules/ships/ship-view.mjs');return openShipView({loadCoachViewer,getBridge:shipViewBridge,ownedShipIds:shipOwnedShipIds,mountArrival:mountFirstShipArrival,...options});}};\r\n// App start: grant whatever the cached step snapshot already earned. Later step changes arrive as\r\n// myr5:account-progress (workout sync, breathing, meals, account refresh), which battle-pass.mjs listens for.\r\nsyncBattlePass();\r\nmountCoachOverlay();\r\n// Fruit-Ninja slice game over the camera for eligible martial reps (lazy; external to the core bundle).\r\n{let slice=null,loading=false,sliceGeneration=0;\r\n const eligible=()=>isMartialSliceExercise(EXERCISES[session.mode])?'martial':null;\r\n const drop=()=>{sliceGeneration++;slice?.stop();slice=null;};\r\n window.addEventListener('myr5:movement-configured',drop);\r\n window.addEventListener('myr5:pose',event=>{\r\n  if(!slice){const mode=eligible(),host=document.getElementById('coachOverlay');if(!mode||!host||loading||document.body.dataset.tracking!=='true')return;loading=true;const generation=sliceGeneration,exercise=session.mode;\r\n   import('./arcade/slice/camera-slice.mjs').then(m=>{if(!slice&&generation===sliceGeneration&&exercise===session.mode&&eligible()&&document.body.dataset.tracking==='true')slice=m.mountCameraSlice({host,mode,ownerId:pod?.flow?.active?.performanceOwner??null,sound:k=>physicalSound.play(k),onXp:n=>pod?.flow?.workout&&(pod.flow.workout.bonusXp=(pod.flow.workout.bonusXp||0)+n)});}).catch(()=>{}).finally(()=>{loading=false;});return;}\r\n  slice.pose(event.detail);});\r\n new MutationObserver(()=>{if(document.body.dataset.tracking!=='true'){drop();dropWorkoutEffects();}}).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});\r\n window.myr5Slice={get active(){return slice},drop};}\r\nwindow.addEventListener('myr5:movement-configured',dropWorkoutEffects);\r\nwindow.addEventListener('myr5:account-ready',()=>{dropWorkoutEffects();syncRetainedPowerOwner();});\r\ndocument.addEventListener('click',event=>{const button=event.target?.closest?.('.workout-performance-controls button');if(button?.textContent?.trim()==='Break hold')invalidateWorkoutEffects();},true);\r\n// Games pay XP on a 4 s result card (no rest boss; rest-boss rewards come from the circuit and single sets only).\r\n// Subway Burpees ends on the counter tap or when Coach catches you: both FINISH the set and award the XP earned so far\r\n// (finishWorking, not the interrupt path). Under 15 s active there is nothing to award, so it is a plain stop.\r\nasync function finishEndless(){try{if(await pod?.finishWorking?.(state.motion,Date.now(),{game:true}))return;}catch(error){status(error.message);}stop();}\r\n// Subway Burpees runner: the corridor game over the camera (lazy; external to the core bundle). Body controls only; no other mode gets it.\r\n{let runner=null,loading=false;\r\n const eligible=()=>!!EXERCISES[session.mode]?.runner&&!coverKey;\r\n const drop=()=>{runner?.stop();runner=null;};\r\n window.addEventListener('myr5:pose',event=>{\r\n  if(!runner){const host=document.getElementById('cameraWorkout');if(!eligible()||!host||loading||document.body.dataset.tracking!=='true')return;loading=true;\r\n   // The hidden portal board yields its context to the corridor (the coach stays); it rebuilds after the run.\r\n   import('./arcade/runner/runner.mjs').then(async m=>{const gpu=claimGraphics('runner',{keep:['coach']});await gpu.ready;if(runner||document.body.dataset.tracking!=='true'){gpu();return;}try{runner=m.mountRunner({host,Session:MovementSession,ownerId:pod?.flow?.active?.performanceOwner??'guest',onActive:()=>{session.runnerPulse=true;},sound:k=>physicalSound.play(k),onXp:n=>pod?.flow?.workout&&(pod.flow.workout.bonusXp=(pod.flow.workout.bonusXp||0)+n),onCaught:()=>{void finishEndless();}});}catch(error){gpu();throw error;}const stop=runner.stop;runner.stop=()=>{stop();gpu();};}).catch(error=>console.warn('Runner could not start.',error)).finally(()=>{loading=false;});return;}\r\n  runner.pose(event.detail);});\r\n new MutationObserver(()=>{if(document.body.dataset.tracking!=='true')drop();}).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});\r\nwindow.myr5Runner={get active(){return runner},drop};}\r\n// Crouching Crisis (lazy; external to the core bundle): lunge to hide, punch to fire. Active time is reported through session.coverPulse.\r\n{let cover=null,loading=false;\r\n const drop=()=>{cover?.stop();cover=null;};\r\n window.addEventListener('myr5:pose',event=>{\r\n  if(!cover){const host=document.getElementById('cameraWorkout');if(!coverKey||!EXERCISES[session.mode]?.runner||!host||loading||document.body.dataset.tracking!=='true')return;loading=true;\r\n   import('./arcade/cover/cover.mjs').then(async m=>{const gpu=claimGraphics('cover',{keep:['coach']});await gpu.ready;if(cover||!coverKey||document.body.dataset.tracking!=='true'){gpu();return;}try{cover=m.mountCover({host,Session:MovementSession,sound:k=>physicalSound.play(k),audio:physicalSound,onXp:n=>pod?.flow?.workout&&(pod.flow.workout.bonusXp=(pod.flow.workout.bonusXp||0)+n),onActive:()=>{session.coverPulse=true;},onCaught:()=>{void finishEndless();}});}catch(error){gpu();throw error;}const stop=cover.stop;cover.stop=()=>{stop();gpu();};}).catch(error=>console.warn('Crouching Crisis could not start.',error)).finally(()=>{loading=false;});return;}\r\n  cover.pose(event.detail);});\r\n new MutationObserver(()=>{if(document.body.dataset.tracking!=='true')drop();}).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});\r\nwindow.myr5Cover={get active(){return cover},drop};}\r\n\r\n// The mode's movement snapshot is shared by flight and hold accounting before consume().\r\nwindow.addEventListener('myr5:pose',event=>{\r\n if(session.mode!=='plank-flight'||state.phase!=='tracking')return;\r\n if(plankFlight){plankFlight.pose(event.detail);return;}\r\n const host=document.getElementById('cameraWorkout');if(!host||plankLoading)return;plankLoading=true;\r\n const ticket=generation,exercise=session.mode,ownerId=pod?.flow?.active?.performanceOwner??performanceOwner();\r\n import('./arcade/plank-flight/flight.mjs').then(async m=>{\r\n  const gpu=claimGraphics('plank-flight',{keep:['coach']});await gpu.ready;\r\n  if(ticket!==generation||exercise!==session.mode||state.phase!=='tracking'||performanceOwner()!==ownerId){gpu();return;}\r\n  try{plankFlight=m.mountPlankFlight({host,ownerId,owner:()=>performanceOwner(),audio:physicalSound,sound:k=>physicalSound.play(k),onExit:()=>{void finishEndless();},onRecalibrate:()=>{session.flightInput?.reset();session.flightLast=null;session.flightEligible=false;session.hold=0;}});}catch(error){gpu();throw error;}\r\n  const stopFlight=plankFlight.stop;plankFlight.stop=()=>{stopFlight();gpu();};\r\n  plankFlight.pose(event.detail);\r\n }).catch(error=>{status('Jet Plank Joyride could not start: '+error.message);console.warn('Jet Plank Joyride startup',error);}).finally(()=>{plankLoading=false;});\r\n});\r\ndocument.addEventListener('click',event=>{\r\n const button=event.target?.closest?.('.workout-performance-controls button');if(!button||!plankFlight)return;\r\n if(button.textContent.trim()==='Break hold')plankFlight.action('pause');\r\n if(button.textContent.trim()==='Resume hold')plankFlight.action('resume');\r\n},true);\r\nwindow.addEventListener('myr5:movement-configured',dropPlankFlight);\r\nwindow.addEventListener('myr5:account-ready',dropPlankFlight);\r\nwindow.myr5PlankFlight={get active(){return plankFlight},drop:dropPlankFlight};\r\n\r\nlet cinematics={play(){}};\r\nlet optionalLoaded=false;\r\nlet optionalLoading=null;\r\nlet materialController=null;\r\nlet coachLoading=null;\r\nasync function loadCoachViewer(){\r\n if(!coachLoading){\r\n  const link=document.createElement('link');link.rel='stylesheet';link.href='/creature/phone.css';document.head.append(link);\r\n  coachLoading=import('./creature/assets/phone.js').catch(error=>{coachLoading=null;throw error;});\r\n }\r\n await coachLoading;\r\n}\r\n// Optional look/feel assets load on first user intent for everyone, guests included (no Coach Army gate).\r\nasync function loadOptionalMaterials(){\r\n if(optionalLoaded)return true;\r\n if(optionalLoading)return optionalLoading;\r\n optionalLoading=(async()=>{\r\n for(const href of ['/creature/phone.css','/creature/cinematics.css','/pod/hardware.css','/hardware-launch.css','/pocket-hardware.css','/hand-companion.css']){const link=document.createElement('link');link.rel='stylesheet';link.href=href;document.head.append(link);}\r\n for(const image of document.querySelectorAll('[data-optional-src]')) image.src=image.dataset.optionalSrc;\r\n await loadCoachViewer();\r\n const {initCinematics}=await import('./creature/cinematics.js');\r\n cinematics=initCinematics({voice});\r\n window.dispatchEvent(new Event('myr5:optional-materials-ready'));\r\n window.myr5Cinematics=cinematics;\r\n optionalLoaded=true;\r\n return true;\r\n })();\r\n try{return await optionalLoading;}finally{optionalLoading=null;}\r\n}\r\n// W2-2M #117: no \"Show my coach\" on the pod any more; the coach's reveal is the oval's ship intro (#select).\r\nfor(const id of ['coachLoading','restCoachLoading'])$(id).textContent='Call your coach with the oval on the portal.';\r\n$('openSettings').addEventListener('click',()=>{void loadOptionalMaterials();void mountPackLicenses({host:$('packLicenses')});},{once:true});\r\n$('openIdentity').addEventListener('click',()=>{void loadOptionalMaterials();},{once:true});\r\n$('manageMaterials').addEventListener('click',async()=>{\r\n const status=$('materialsStatus');\r\n // Material packs are account-owned grants: guests are asked to sign in, not to join anything.\r\n if(!window.myr5AuthenticatedAccount?.user?.id){status.textContent='Sign in to download materials you own.';return;}\r\n try { await loadOptionalMaterials(); const {mountMaterialControls}=await import('./modules/materials/material-controller.mjs'); $('materialControls').hidden=false; materialController??=mountMaterialControls({account:window.myr5AuthenticatedAccount,workoutOwner:pod.workoutOwner}); status.textContent=materialController?'Material controls ready.':'Material controls unavailable.'; }\r\n catch(error){status.textContent=error.message;}\r\n});\r\nwindow.myr5Cinematics=cinematics;\r\nwindow.addEventListener('pagehide',()=>materialController?.dispose(),{once:true});\r\n\r\nfunction manualSnapshot(){\r\n if(!manual)return state.motion;\r\n const kind=state.motion.kind,elapsed=manual.clock.sample(),value=['hold','pace'].includes(kind)?elapsed:manual.value;\r\n state.motion={...state.motion,tracking:true,elapsed,active:kind==='pace'?value:state.motion.active,totalHold:kind==='hold'?value:state.motion.totalHold,count:['reps','steps','jumps'].includes(kind)?value:state.motion.count,remaining:Math.max(0,pod.goal()-value),complete:value>=pod.goal(),message:'Manual workout · '+Math.floor(value)};\r\n return state.motion;\r\n}\r\nasync function manualTick(run){\r\n if(run!==generation||state.phase!=='manual'||!manual)return;\r\n try{const motion=manualSnapshot();renderMotion(motion);if(await pod.consume(motion,Date.now()))return;if(['hold','pace'].includes(motion.kind)&&performance.now()-manual.savedAt>=1000){await pod.saveManual(motion);manual.savedAt=performance.now();}manualFrame=requestAnimationFrame(()=>manualTick(run));}\r\n catch(error){generation++;release();controls(false);await pod.interruptCurrent(state.motion).catch(()=>{});state.phase='error';state.error=error.message;manual=null;status(error.message);}\r\n}\r\nfunction startManual(){\r\n if(danceMode)stopDance();\r\n const run=++generation;release();state.phase='manual-starting';controls(true);state.error=null;resetMovement();status('Starting manual workout…');$('detail').textContent='No camera · manual counter';\r\n return manualStartGate.run(async()=>{let ticket=null;try{ticket=await pod.beginSet(session.mode,{manual:true});if(run!==generation)return;const progress=ticket.progress||{},clock=new ManualActiveClock({now:()=>performance.now(),visible:()=>!document.hidden});clock.start(progress.elapsedSeconds);manual={value:Number(progress.value)||0,clock,savedAt:performance.now()};state.phase='manual';voice.say(MOVEMENTS[session.mode].name+' selected.',{key:'movement',interrupt:true});voice.say('Begin.',{key:'setup'});$('previewLabel').textContent='MANUAL';$('primary').tabIndex=0;$('primary').setAttribute('role',['hold','pace'].includes(state.motion.kind)?'timer':'button');$('primary').setAttribute('aria-label',['hold','pace'].includes(state.motion.kind)?'Manual workout timer':'Add one '+(state.motion.kind==='steps'?'step':state.motion.kind==='jumps'?'jump':'rep'));status(['hold','pace'].includes(state.motion.kind)?'Timer running. Stop to pause.':'Tap the counter or press Enter to add each movement.');manualTick(run);}\r\n catch(error){if(run!==generation)return;generation++;release();controls(false);if(ticket)await pod.interruptCurrent(state.motion).catch(()=>{});state.phase='error';state.error=error.message;manual=null;status(error.message);}});\r\n}\r\nasync function activateManual(){\r\n if(state.phase!=='manual'||!manual||['hold','pace'].includes(state.motion.kind)||pod?.canCount?.()===false)return;\r\n const run=generation;\r\n manual.value++;try{const motion=manualSnapshot();await pod.saveManual(motion);renderMotion(motion);await pod.consume(motion,Date.now());}catch(error){if(run!==generation)return;generation++;release();controls(false);await pod.interruptCurrent(state.motion).catch(()=>{});state.phase='error';state.error=error.message;manual=null;status(error.message);}\r\n}\r\nasync function pauseManualUi(){try{manualSnapshot();manual?.clock.pause();await pod.pauseManual(state.motion);generation++;release();controls(false);state.phase='idle';manual=null;status('Paused. Tap Begin to resume this workout.');$('detail').textContent='Manual workout paused on this device';window.myr5Routes?.home?.();}catch(error){status(error.message);}}\r\nfunction pauseManualWhenReady(){return manualStartGate.pause(()=>state.phase==='manual'?pauseManualUi():null);}\r\nconst workoutTools=document.createElement('div');workoutTools.className='workout-performance-controls';workoutTools.hidden=true;\r\nconst recoveryStatus=document.createElement('p');recoveryStatus.dataset.holdRecovery='';recoveryStatus.setAttribute('role','status');recoveryStatus.hidden=true;workoutTools.append(recoveryStatus);\r\nconst holdVariationLabel=document.createElement('label');holdVariationLabel.textContent='Hold difficulty ';holdVariationLabel.dataset.holdVariation='';const holdVariation=document.createElement('select');holdVariation.setAttribute('aria-label','Hold difficulty');for(const [value,label] of [['easy','Easy'],['medium','Medium'],['hard','Hard'],['expert','Expert']]){const option=document.createElement('option');option.value=value;option.textContent=label;holdVariation.append(option);}holdVariation.onchange=()=>{if(ACTIVE_PHASES.has(state.phase))return;pod?.switchDifficulty?.(holdVariation.value);};holdVariationLabel.append(holdVariation);workoutTools.append(holdVariationLabel);\r\nfor(const [label,action] of [['Finish working set',async()=>{try{if(state.phase==='manual')manualSnapshot();const saved=await pod?.finishWorking?.(state.motion,Date.now());if(saved===false){voice.say(pod?.flow?.workout?.instruction||'Nothing to save yet.',{interrupt:true});navigator.vibrate?.(60);}}catch(error){status(error.message);}}],['Break hold',()=>{pod?.breakHold?.();manual?.clock.pause();status('Hold paused. Shake out, then resume when ready.');}],['Resume hold',()=>{pod?.resumeHold?.();manual?.clock.resume();}],['More recovery',()=>pod?.extendHoldRecovery?.()]]){const button=document.createElement('button');button.type='button';button.setAttribute('aria-label',label);button.title=label;const caption=document.createElement('span');caption.className='workout-action-caption';caption.textContent=label;const icon=document.createElement('span');icon.className='workout-action-icon';icon.setAttribute('aria-hidden','true');icon.innerHTML=symbolMarkup(({'Finish working set':'stop','Break hold':'pause','Resume hold':'play','More recovery':'recovery'})[label]);button.append(caption,icon);button.onclick=action;workoutTools.append(button);}\r\nconst cardioLabel=document.createElement('label');cardioLabel.textContent='Cardio ';const cardioStyle=document.createElement('select');cardioStyle.setAttribute('aria-label','Cardio style');for(const [value,label] of [['gentle','Gentle cardio'],['sprint','Sprint intervals']]){const option=document.createElement('option');option.value=value;option.textContent=label;cardioStyle.append(option);}cardioStyle.onchange=()=>pod?.setCardioStyle?.(cardioStyle.value);cardioLabel.append(cardioStyle);$('bar').after(workoutTools);$('roundControl').append(cardioLabel);\r\n$('primary').addEventListener('pointerdown',event=>{if(state.phase==='manual'){event.preventDefault();void activateManual();}});\r\n$('primary').addEventListener('keydown',event=>{if(state.phase==='manual'&&(event.key==='Enter'||event.key===' ')){event.preventDefault();void activateManual();}});\r\nvoid mountArmieInboxUI().catch(()=>{}); // D23 inbox; best-effort so a failure here never blocks the workout.\r\n","apple-basic-share-config.mjs":"// Public Apple basic app, installed beside the full MYR5 app from its own origin.\r\n// Share only this basic URL inside the full app; the full-app install link stays at website endings.\r\nexport const APPLE_BASIC_SHARE_URL = 'https://fitness.mominc.online';\r\n","apple-basic-share.mjs":"// Settings \"Share Apple basic\": navigator.share when present, clipboard copy otherwise. Hidden until\r\n// APPLE_BASIC_SHARE_URL is configured.\r\nimport {APPLE_BASIC_SHARE_URL} from './apple-basic-share-config.mjs';\r\n\r\nconst TEXT='Try MOM Inc Coach — the Apple basic version.';\r\n\r\nexport async function shareAppleBasic(url=APPLE_BASIC_SHARE_URL,nav=globalThis.navigator){\r\n if(!url)return 'unavailable';\r\n if(nav?.share){\r\n  try{await nav.share({title:'MOM Inc Coach',text:TEXT,url});return 'shared';}\r\n  catch(error){if(error?.name==='AbortError')return 'cancelled';}\r\n }\r\n try{await nav.clipboard.writeText(url);return 'copied';}catch{return 'failed';}\r\n}\r\n\r\nconst MESSAGES={shared:'Shared.',copied:'Link copied. Paste it to a friend.',cancelled:'',failed:'Could not share or copy the link.',unavailable:''};\r\n\r\nexport function mountAppleBasicShare(settings,{url=APPLE_BASIC_SHARE_URL,nav=globalThis.navigator}={}){\r\n if(!url)return null;\r\n const wrap=document.createElement('div');wrap.className='settings-share';\r\n const button=document.createElement('button');button.type='button';button.textContent='Share Apple basic';\r\n const status=document.createElement('p');status.setAttribute('role','status');\r\n button.onclick=async()=>{button.disabled=true;const result=await shareAppleBasic(url,nav);if(['shared','copied'].includes(result))void import('./modules/vault/vault-store.mjs').then(m=>m.bump('share')).catch(()=>{});status.textContent=MESSAGES[result];button.disabled=false;};\r\n wrap.append(button,status);settings.append(wrap);\r\n return wrap;\r\n}\r\n","arcade/cover/atmosphere.mjs":"// Decorative motion stays outside the combat lanes. All geometry is bounded,\r\n// shared within a level and released when the next portal swaps the world.\r\nimport {mergeGeometries} from './merge.mjs';\r\nexport function createCoverAtmosphere(T,level,parent){\r\n const root=new T.Group();root.name=`atmosphere-${level.id}`;parent.add(root);\r\n const geometries=[],materials=[],animations=[],textures=[];\r\n const geo=g=>(geometries.push(g),g);\r\n const cube=geo(new T.BoxGeometry(1,1,1)),orb=geo(new T.IcosahedronGeometry(1,1)),cone=geo(new T.ConeGeometry(1,1,7));\r\n const palette=level.palette;\r\n const material=(color,lit=true)=>{const m=lit?new T.MeshToonMaterial({color}):new T.MeshBasicMaterial({color,toneMapped:false});materials.push(m);return m;};\r\n const trim=material(palette.trim),dark=material('#292739'),accent=material(palette.accent,false),warm=material('#ffb74e',false),leaf=material('#4c7956'),stone=material(palette.wall),pale=material('#d9e2dc'),steel=material('#71838d');\r\n const add=(geometry,mat,x,y,z,sx,sy,sz,p=root)=>{const m=new T.Mesh(geometry,mat);m.position.set(x,y,z);m.scale.set(sx,sy,sz);p.add(m);return m;};\r\n const box=(mat,x,y,z,w,h,d,p)=>add(cube,mat,x,y,z,w,h,d,p);\r\n const sb=new Map(),sbox=(mat,x,y,z,w,h,d)=>{(sb.get(mat)||sb.set(mat,[]).get(mat)).push([x,y,z,w,h,d]);}; // static boxes merge into one mesh per material (draw calls)\r\n const animate=(object,kind,phase=0)=>animations.push({object,kind,phase,base:object.position.clone()});\r\n const fan=(x,y,z,phase)=>{const g=new T.Group();g.position.set(x,y,z);root.add(g);for(let j=0;j<3;j++){const blade=box(trim,0,0,0,.85,.1,.05,g);blade.rotation.z=j*Math.PI/3;}box(accent,0,0,.06,.15,.15,.1,g);animate(g,'fan',phase);};\r\n for(let i=0;i<6;i++){\r\n  const z=-4-i*8;\r\n  for(const s of [-1,1]){\r\n   // Inset panels, seams and utility conduit give scale without visual noise\r\n   // in the silhouette of the coach targets.\r\n   sbox(trim,s*6.7,.18,z,.12,.1,6.8);\r\n   sbox(dark,s*6.65,4.9,z,.16,.14,7.7);\r\n   sbox(accent,s*5.9,4.1,z,.1,.12,1.6);\r\n   if(level.id==='crisis'){\r\n    sbox(pale,s*5.2,3.3,z,.62,6.5,1.3);sbox(steel,s*5.2,3.3,z+.68,.13,6.1,.08);sbox(dark,s*4.4,.75,z+1.1,1.1,1.5,1.4);\r\n    if(i%2===0)add(orb,warm,s*5.2,6.9,z,.2,.16,.2);\r\n    const pennant=box(accent,s*5.7,3.4,z,.18,.34,.04);animate(pennant,'sway',i+s);\r\n   }else if(level.id==='ps2'){\r\n    const sloped=box(steel,s*5.1,2.4,z,.7,4.8,1.5);sloped.rotation.z=-s*.22;sbox(trim,s*5.1,4.9,z,3.6,.18,4.5);\r\n    for(let j=0;j<4;j++)sbox(accent,s*(4.3+j*.18),1.3,z+1,.035,2.5,.05);\r\n    const cable=box(accent,s*5.7,3.6,z,.1,1.1,.06);animate(cable,'sway',i+s);\r\n   }else if(level.id==='halo'){\r\n    const pylon=box(pale,s*5.15,3.15,z,.68,6.1,.8);pylon.rotation.z=-s*.15;sbox(accent,s*5.15,3.25,z+.43,.1,4.8,.06);sbox(steel,s*5.8,.45,z+1,.9,.8,1.4);\r\n    // Keep the installation's lean rigid; only its small beacon moves.\r\n    const beacon=box(accent,s*5.15,6.3,z,.2,.3,.16);animate(beacon,'sway',i+s);\r\n   }else if(level.id==='kitchen'){\r\n    sbox(pale,s*5.05,3.1,z,2,6.2,1.1);sbox(steel,s*5.05,3.1,z+.58,.12,5.9,.08);sbox(dark,s*4.8,2.9,z+.68,.08,1.5,.12);sbox(trim,s*5.8,6.3,z,2.4,.16,1.3);\r\n    const towel=box(accent,s*5.7,2.7,z+.72,.32,.55,.04);animate(towel,'sway',i+s);\r\n   }else if(level.id==='mansion'){\r\n    sbox(trim,s*4.9,2.25,z,.13,.8,.2);\r\n    const flame=add(cone,warm,s*4.9,2.9,z,.16,.55,.16);animate(flame,'flame',i+s);\r\n    sbox(stone,s*6.5,3.3,z,1,.18,3.8);\r\n   }else if(level.id==='park'){\r\n    const frond=add(orb,leaf,s*5.7,4.7,z,1.3,.35,.65);animate(frond,'sway',i+s);\r\n    sbox(stone,s*6.1,1.1,z,.18,2.2,.18);\r\n    sbox(trim,s*6.1,1.8,z,.12,.12,6.5);\r\n   }else if(level.id==='judgment'){\r\n    const arm=new T.Group();arm.position.set(s*5.2,3,z);root.add(arm);\r\n    box(trim,0,-.55,0,.18,1.2,.2,arm);box(accent,0,-1.1,0,.55,.18,.3,arm);animate(arm,'arm',i+s);\r\n    sbox(dark,s*5.4,.2,z,1.5,.35,3.5);\r\n   }else if(level.id==='hive'){\r\n    fan(s*5.7,3.6,z,i+s);\r\n    sbox(trim,s*6.5,1.7,z,.3,.22,6.7);\r\n   }else if(level.id==='precinct'){\r\n    if(i%2===0)fan(s*5.5,3.6,z,i+s);\r\n    sbox(trim,s*5.8,.5,z,.8,.15,2.2);sbox(dark,s*5.8,.25,z,.12,.5,1.8);\r\n   }else{\r\n    const flag=box(accent,s*5.7,3.4,z,.65,1.1,.04);animate(flag,'sway',i+s);\r\n    sbox(trim,s*5.7,4,z,.9,.08,.08);\r\n   }\r\n  }\r\n }\r\n // A different skyline beyond each corridor, rather than another blank wall.\r\n if(level.id==='hive'){\r\n  const space=material('#070b26',false);space.fog=false;sbox(space,0,8,-78,150,60,.5);\r\n  const planet=material('#877fca'),moon=material('#d1af86');planet.fog=moon.fog=false;\r\n  add(orb,planet,14,12,-66,8,8,8);add(orb,moon,-18,7,-62,3,3,3);\r\n  const starMat=material('#d9ebff',false);starMat.fog=false;\r\n  const stars=new T.InstancedMesh(orb,starMat,96),dummy=new T.Object3D();\r\n  for(let i=0;i<96;i++){dummy.position.set(Math.sin(i*23.73)*55,2+(i*7%31),-72);dummy.scale.setScalar(.05+(i%3)*.025);dummy.updateMatrix();stars.setMatrixAt(i,dummy.matrix);}root.add(stars);\r\n  for(let i=0;i<8;i++){const a=add(orb,stone,(i%2?1:-1)*(9+i*1.8),4+i%4,-30-i*4,.35+i*.07,.28+i*.05,.4);animate(a,'asteroid',i);}\r\n }else{\r\n  for(let i=0;i<7;i++){\r\n   const h=4+(i*7%8),x=(i-3)*4.7;\r\n   if(level.id==='park')add(cone,leaf,x,h*.5,-48,4,h,4);\r\n   else{sbox(stone,x,h*.5,-48,3.5,h,3);sbox(trim,x,h+.12,-48,3.8,.24,3.2);}\r\n  }\r\n }\r\n for(const [m,l] of sb){const gs=l.map(([x,y,z,w,h,d])=>cube.clone().scale(w,h,d).translate(x,y,z)),g=geo(mergeGeometries(T,gs));gs.forEach(q=>q.dispose());root.add(new T.Mesh(g,m));}\r\n // Tiny drifting motes give the near and far planes different motion.\r\n const motes=new T.InstancedMesh(orb,material(level.id==='park'?'#c9e6a4':'#cfbda8',false),24),dummy=new T.Object3D();root.add(motes);\r\n\r\n // ---- ambient life: textured walls/floor, decals, particles, haze, shafts, flicker. All pooled + bounded; no lights.\r\n const canTex=typeof document!=='undefined'&&!!document.createElement,id=level.id;\r\n const soft=(w,h,fn)=>{if(!canTex)return null;const c=document.createElement('canvas');c.width=w;c.height=h;const g=c.getContext('2d');if(!g)return null;fn(g,w,h);const t=new T.CanvasTexture(c);t.colorSpace=T.SRGBColorSpace;textures.push(t);return t;};\r\n const dot=soft(32,32,(g,w)=>{const r=g.createRadialGradient(16,16,0,16,16,16);r.addColorStop(0,'rgba(255,255,255,1)');r.addColorStop(.4,'rgba(255,255,255,.5)');r.addColorStop(1,'rgba(255,255,255,0)');g.fillStyle=r;g.fillRect(0,0,w,w);});\r\n const beam=soft(64,128,(g,w,h)=>{for(let x=0;x<w;x++){const a=Math.sin(Math.PI*x/w)**2,gr=g.createLinearGradient(0,0,0,h);gr.addColorStop(0,`rgba(255,255,255,${.9*a})`);gr.addColorStop(1,'rgba(255,255,255,0)');g.fillStyle=gr;g.fillRect(x,0,1,h);}});\r\n const AD=T.AdditiveBlending,plane=(color,opacity,add,map)=>{const m=new T.MeshBasicMaterial({map,color,transparent:true,opacity,depthWrite:false,blending:add?AD:T.NormalBlending,toneMapped:false,side:T.DoubleSide});materials.push(m);return m;};\r\n // flickering lamp glows: one instanced draw\r\n const bulbMat=plane(id==='judgment'?'#ff8a4a':'#ffd98a',.8,true,dot),bulbs=new T.InstancedMesh(geo(new T.PlaneGeometry(1,1)),bulbMat,8),bd=new T.Object3D();root.add(bulbs);\r\n // light shafts + haze banks (side planes, lane stays clear)\r\n const shafts=[],banks=[];\r\n for(let i=0;i<4;i++){const s=i%2?1:-1,m=plane(palette.light,.1,true,beam),q=new T.Mesh(geo(new T.PlaneGeometry(1.4,4.6)),m);q.position.set(s*2.6,2.2,-5-i*7);q.rotation.z=s*.38;root.add(q);shafts.push({q,m,i});\r\n  const bm=plane(palette.fog,.2,false,dot),b=new T.Mesh(geo(new T.PlaneGeometry(3,1.8)),bm);b.position.set(s*2.8,.7,-8-i*7);root.add(b);banks.push({b,bm,i,s});}\r\n // particles (Points, positions rewritten per frame; bounded counts)\r\n const sets=[],tmp=[0,0,0];\r\n const pts=(n,color,size,op,add,fn,glint)=>{const g=geo(new T.BufferGeometry()),pos=new Float32Array(n*3);g.setAttribute('position',new T.BufferAttribute(pos,3));const m=new T.PointsMaterial({color,size,map:dot,transparent:true,opacity:op,depthWrite:false,blending:add?AD:T.NormalBlending,sizeAttenuation:true,alphaTest:.01});materials.push(m);const p=new T.Points(g,m);p.frustumCulled=false;root.add(p);sets.push({p,n,fn,pos,m,glint});};\r\n const h=(i,k)=>{const v=Math.sin(i*12.9898+k*78.233)*43758.5453;return v-Math.floor(v);};\r\n pts(60,id==='judgment'?'#c9c2b8':'#f4e8d0',.09,.55,false,(i,t,o)=>{o[0]=(h(i,1)-.5)*12+Math.sin(t*.4+i)*.3;o[1]=.2+(h(i,2)*5+t*.12*(.3+h(i,3)))%5;o[2]=-2-(h(i,4)*46+t*.4)%46;});\r\n if(id==='judgment'){pts(48,'#ff8a2a',.12,.9,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*11+Math.sin(t*1.3+i)*.5;o[1]=.1+(h(i,5)*6+t*(.8+h(i,6)))%6;o[2]=-2-h(i,4)*44;});pts(20,'#ffe27a',.07,1,true,(i,t,o)=>{const u=(t*2.2+h(i,7)*3)%3;o[0]=(h(i,1)-.5)*10+u*(h(i,8)-.5)*2;o[1]=3.5-u*3;o[2]=-3-h(i,4)*40;});}\r\n if(id==='halo'){pts(44,'#ff6ad8',.14,.85,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*13+Math.sin(t*.5+i)*.6;o[1]=.6+h(i,2)*4+Math.sin(t*.7+i*2)*.4;o[2]=-2-(h(i,4)*50+t*.2)%50;});pts(18,'#d8f6ff',.5,.9,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*70;o[1]=12+h(i,2)*20;o[2]=-92;},true);}\r\n if(id==='kitchen')pts(34,'#ffffff',.5,.25,false,(i,t,o)=>{const u=(t*.5+h(i,5))%1,s=i%2?1:-1;o[0]=s*(4.6+h(i,1)*1.4)+Math.sin(t+i)*.2;o[1]=.9+u*2.8;o[2]=-5-(i%6)*7;});\r\n if(id==='park')pts(30,'#e8ff7a',.1,.9,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*12+Math.sin(t*.6+i)*.5;o[1]=.5+h(i,2)*3+Math.sin(t+i*3)*.3;o[2]=-3-h(i,4)*44;});\r\n if(id==='hive')pts(40,'#6ee8ff',.08,.8,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*13;o[1]=.5+(h(i,2)*5+t*.5)%5;o[2]=-2-h(i,4)*46;});\r\n if(id==='mansion')pts(30,'#ffcf7a',.08,.8,true,(i,t,o)=>{o[0]=(h(i,1)-.5)*11+Math.sin(t*.5+i)*.4;o[1]=.3+(h(i,2)*4+t*.3)%4;o[2]=-2-h(i,4)*44;});\r\n // rain (outside only): streak segments recycled around the path\r\n const RN=140,rg=geo(new T.BufferGeometry()),rpos=new Float32Array(RN*6);rg.setAttribute('position',new T.BufferAttribute(rpos,3));\r\n const rainMat=new T.LineBasicMaterial({color:'#bcd6ff',transparent:true,opacity:0,depthWrite:false});materials.push(rainMat);const rain=new T.LineSegments(rg,rainMat);rain.frustumCulled=false;rain.visible=false;root.add(rain);\r\n let outside=0,outT=0,travel=0,travT=0,last=0,flow=0;\r\n const ext=time=>{const dt=Math.min(.1,Math.max(0,time-last));last=time;outside+=(outT-outside)*Math.min(1,dt*3);travel+=(travT-travel)*Math.min(1,dt*3);flow+=dt*(1+travel*4);\r\n  for(const o of sets){const a=o.pos;for(let i=0;i<o.n;i++){o.fn(i,flow,tmp);a[i*3]=tmp[0];a[i*3+1]=tmp[1];a[i*3+2]=tmp[2];}o.p.geometry.attributes.position.needsUpdate=true;if(o.glint)o.m.opacity=.35+.5*Math.abs(Math.sin(time*1.7));}\r\n  rain.visible=outside>.02;rainMat.opacity=.5*outside;if(rain.visible){for(let i=0;i<RN;i++){const u=(h(i,5)*8+time*(14+h(i,6)*6))%8,x=(h(i,1)-.5)*16,z=-1-h(i,2)*40;rpos.set([x,7-u,z,x+.25,6.4-u,z],i*6);}rg.attributes.position.needsUpdate=true;}\r\n  for(const s of shafts){const f=id==='judgment'?.6+.4*Math.sin(time*13+s.i*5)*Math.sin(time*7):.85+.15*Math.sin(time*.7+s.i);s.m.opacity=.11*f*(1-outside*.5);s.q.position.y=2.2+Math.sin(time*.3+s.i)*.1;}\r\n  for(const b of banks){b.b.position.x=b.s*(2.8+Math.sin(time*.15+b.i*2)*.4);b.b.position.y=.7+Math.sin(time*.2+b.i)*.15;b.bm.opacity=.2+Math.sin(time*.25+b.i*3)*.05;}\r\n  for(let i=0;i<bulbs.count;i++){const s=i%2?1:-1,f=id==='judgment'?(Math.sin(time*23+i*7)>.2?1:.15):.82+.18*Math.sin(time*(9+i)+i*3);bd.position.set(s*2.8,3.1,-4-(i>>1)*7);bd.rotation.y=-s*Math.PI/2;bd.scale.setScalar(1.8*f);bd.updateMatrix();bulbs.setMatrixAt(i,bd.matrix);}bulbs.instanceMatrix.needsUpdate=true;};\r\n let disposed=false;\r\n return {root,count:animations.length,setOutside(v){outT=v?1:0;},setTravel(v){travT=v?1:0;},update(time){if(disposed)return;ext(time);\r\n  for(const a of animations){const o=a.object,t=time+a.phase;\r\n   if(a.kind==='fan')o.rotation.z=t*1.1;\r\n   else if(a.kind==='flame'){o.scale.y=.48+Math.sin(t*12)*.07;o.scale.x=.15+Math.sin(t*17)*.025;}\r\n   else if(a.kind==='arm')o.rotation.z=Math.sin(t*.7)*.22;\r\n   else if(a.kind==='asteroid'){o.rotation.set(t*.12,t*.18,0);o.position.y=a.base.y+Math.sin(t*.35)*.4;}\r\n   else o.rotation.z=Math.sin(t*1.4)*.06;\r\n  }\r\n  for(let i=0;i<24;i++){dummy.position.set(Math.sin(i*17.3)*6,.3+(i*.73+time*.055)%5,-2-(i*3.7+time*.3)%49);dummy.scale.setScalar(.014+(i%3)*.006);dummy.updateMatrix();motes.setMatrixAt(i,dummy.matrix);}motes.instanceMatrix.needsUpdate=true;\r\n },dispose(){if(disposed)return;disposed=true;root.removeFromParent();for(const g of geometries)g.dispose();for(const m of materials)m.dispose();for(const t of textures)t.dispose();}};\r\n}\r\n","arcade/cover/cover-bank.mjs":"// Run bonus bank: onXp gets the SIGNED change of the net bank (floor 0), so the XP paid out always equals the bank (+5 then -3 pays 2, not 5).\r\nexport function createBank(onXp=()=>{}){let bank=0;return {credit(n){const nb=Math.max(0,bank+(+n||0)),d=nb-bank;bank=nb;if(d)onXp(d);},get bank(){return bank;}};}\r\n","arcade/cover/cover-fruit.mjs":"// Crouching Crisis fruit layer: enemies lob food at you, your limbs slice it (Fruit Ninja over the 3D scene). Reuses the slice engine + camera-slice limb logic.\r\n// mountFruit({host,scene,credit,tagPop,sound,seed}) -> {pose(detail),frame(dt,s),burst(),count,stop()}. Spawn/score logic below is pure (tests/cover-fruit.test.mjs).\r\nimport {createSlice,render,pathKind} from '../slice/core.mjs';\r\nimport {FOOD_TYPES} from './food-types.mjs';\r\nimport {mapPointToOverlay} from '../workout-effects.mjs';\r\nconst FRUIT={every:[2,4],burst:[3,5],xp:1,comboMin:3,comboWin:.6,r:8,rise:[.25,.4],fast:.9,seen:.4,W:180}; // tuning: seconds between lobs, burst size, xp per slice, combo gate/window (s), radius, apex as screen fraction, limb gate (screen h/s)\r\nconst LIMBS=[13,14,15,16]; // elbows + wrists\r\nexport const FOOD_IDS=Object.keys(FOOD_TYPES);\r\nexport {FRUIT};\r\nexport function createSpawner(rand=Math.random){\r\n const gap=()=>FRUIT.every[0]+rand()*(FRUIT.every[1]-FRUIT.every[0]);let t=gap();\r\n return {step(dt,fighting){if(!fighting)return 0;t-=dt;if(t>0)return 0;t=gap();return 1;},burst:()=>FRUIT.burst[0]+Math.floor(rand()*(FRUIT.burst[1]-FRUIT.burst[0]+1))};\r\n}\r\nexport function createScorer(){ // 3+ slices inside the window = double xp\r\n let ts=[];return {hit(t){ts=ts.filter(x=>t-x<=FRUIT.comboWin);ts.push(t);const n=ts.length;return {n,combo:n>=FRUIT.comboMin,xp:FRUIT.xp*(n>=FRUIT.comboMin?2:1)};}};\r\n}\r\nconst reduced=()=>{try{return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;}catch{return false;}};\r\nexport function mountFruit({host,scene,credit=()=>{},tagPop=()=>{},sound=()=>{},seed=Date.now()}){\r\n const W=FRUIT.W,box=()=>{const r=host.getBoundingClientRect?.();return r&&r.width>0&&r.height>0?{w:r.width,h:r.height}:{w:innerWidth,h:innerHeight};};\r\n let stopped=false,size=box(),H=Math.round(W*size.h/size.w),clock=0,fruit=0,fighting=false,spots=[];\r\n const canvas=document.createElement('canvas');canvas.width=W;canvas.height=H;canvas.setAttribute('aria-hidden','true');\r\n canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;z-index:3;image-rendering:pixelated;pointer-events:none';host.append(canvas);\r\n const g=canvas.getContext('2d'),scorer=createScorer(),spawner=createSpawner(),rnd=()=>Math.random();\r\n const e=createSlice({w:W,h:H,seed,mode:'rest',auto:false,strikes:false,onEvent(type,d){\r\n  if(type!=='hit'||!FOOD_TYPES[d.type])return;\r\n  fruit++;const r=scorer.hit(clock);credit(r.xp);sound('barrel-split');tagPop(r.combo?`COMBO x${r.n}`:'SLICED!');}});\r\n const resize=()=>{size=box();const nh=Math.round(W*size.h/size.w);if(nh>0&&nh!==H){H=nh;canvas.height=H;e.resize(H);}};\r\n const observer=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;observer?.observe(host);\r\n function lob(x,y){ // from an enemy, arcing up and across toward the player\r\n  const apex=H*(FRUIT.rise[0]+rnd()*(FRUIT.rise[1]-FRUIT.rise[0])),vy=-Math.sqrt(2*260*apex),T=-2*vy/260,tx=W*(.2+rnd()*.6);\r\n  e.spawn({type:FOOD_IDS[Math.floor(rnd()*FOOD_IDS.length)],x,y,vx:(tx-x)/T,vy,r:FRUIT.r+rnd()*3,wobble:4,T});\r\n }\r\n const spot=()=>spots.length?spots[Math.floor(rnd()*spots.length)]:{x:W*(.2+rnd()*.6),y:H*.4};\r\n const hist=new Map(),toScreen=(p,d)=>{const m=mapPointToOverlay(p,{videoWidth:d.width,videoHeight:d.height,hostWidth:size.w,hostHeight:size.h,mirrored:d.mirrored});return m?{x:m.x,y:m.y,v:m.visibility}:{x:0,y:0,v:0};};\r\n return {\r\n  get count(){return fruit;},\r\n  burst(){if(stopped)return;for(let n=spawner.burst();n-->0;){const p=spot();lob(p.x,p.y);}},\r\n  frame(dt,s){\r\n   if(stopped)return;clock+=dt;fighting=s.phase==='fight';spots=[];\r\n   if(fighting)for(const en of s.enemies){const p=en.civ?null:scene.enemyScreen(en.id,{});if(p?.ok)spots.push({x:p.x*W,y:p.y*H});}\r\n   for(let n=spawner.step(dt,fighting);n>0;n--){const p=spot();lob(p.x,p.y);}\r\n   if(s.phase==='portal'||s.phase==='dead')e.objects.length=0;\r\n   e.step(Math.min(.05,dt));if(reduced())e.shake=0;render(g,e,{coach:()=>{},pointerTrail:!reduced(),hud:false});\r\n  },\r\n  pose(detail){if(stopped)return; // same limb-segment slicing as camera-slice.mjs\r\n   const {points}=detail;if(!points)return;const t=(detail.now??performance.now())/1000;\r\n   for(const i of LIMBS){\r\n    const p=toScreen(points[i],detail);if(p.v<FRUIT.seen){hist.delete(i);continue;}\r\n    const q={x:p.x*W,y:p.y*H},h=hist.get(i)||[];h.push({...q,t});if(h.length>6)h.shift();hist.set(i,h);\r\n    if(h.length<2)continue;const a=h[h.length-2],dtp=t-a.t,spd=dtp>0?Math.hypot(q.x-a.x,q.y-a.y)/H/dtp:0;if(dtp<=0||dtp>.5||spd<FRUIT.fast)continue;\r\n    e.addTrail(a.x,a.y);e.addTrail(q.x,q.y);e.hitSegment(a,q,{kind:pathKind(h),pad:4,speed:spd});\r\n   }\r\n  },\r\n  stop(){stopped=true;e.objects.length=0;observer?.disconnect();canvas.remove();},\r\n };\r\n}\r\n","arcade/cover/cover-input.mjs":"// COVER CRISIS body input: leave the frame (left/right/bottom) or lunge+knee-bend = hide behind cover, return = pop up, air-box punch = fire.\r\n// Pure logic over the shared MediaPipe landmarks (no DOM). Every threshold lives in COVER_TUNING (phone tuning happens on the real phone).\r\nimport {elbowAngle} from '../slice/hand-glow.mjs';\r\nexport const COVER_TUNING=Object.freeze({\r\n calibrateS:1.5,        // standing baseline is measured over the first seconds\r\n lunge:{shift:.35,drop:.1,release:.5,dwellS:.05,popDwellS:.08}, // second way to hide: shift in shoulder widths (hip midpoint vs baseline); drop in shoulder widths; release = fraction of shift that pops up\r\n off:{edgeDwellS:.15,glitchS:.25,edge:.32,lead:.25,popDwellS:.15,popLo:.2,popHi:.8,recalS:20,popGraceS:.3}, // off screen = <2 of the 4 torso points in frame (a lean that keeps head/hips in frame never hides). edgeDwellS: loss near an edge before hidden; glitchS: sudden loss while centred; edge: max distance (frame fraction, velocity-led by lead s) to still call it an exit; pop = both shoulders back + torso x in popLo..popHi for popDwellS; recalS: only recalibrate after this long away; popGraceS: sim damage grace after un-hide (re-detect lag)\r\n adapt:.02,             // baseline drift while standing still (EMA per frame)\r\n punch:{peak:4,radial:4,extMin:.6,extOff:.4,rearmMs:140,cooldownMs:300,gapMs:150,minArm2D:1.2}, // HARD snaps only: peak = wrist speed vs shoulder (arm lengths/s, ~2.2 body/s+); radial = extension rate (arm lengths/s); extMin = extension reached; extOff = re-arm below this extension; rearmMs = re-arm after retraction; cooldownMs per hand; gapMs between any two punches. World z is only part of the 3D distance, never a gate\r\n beam:{ext:.8,extOff:.65,elbow:140,elbowOff:120,still:1.5,dwellS:.2,drop:.5}, // HOLD beam: arm held out (extension >= ext, wrist no lower than drop arm lengths below the shoulder so a hanging arm never counts, elbow >= elbow deg, wrist speed <= still arm lengths/s) for dwellS = on; off when extension < extOff or elbow < elbowOff (hysteresis)\r\n zone:{gate:.8,span:2.4,holdMs:140,third:.33,upMargin:.2}, // ARM ZONES: any wrist moving faster than gate (screen heights/s, 2D) emits damage into LEFT/MIDDLE/RIGHT (wrist screen x thirds) or UP (wrist above the nose); intensity .35..1 over gate..gate*(1+span); holdMs keeps a zone lit between slow frames\r\n minVis:.5,\r\n aimSmooth:.5,\r\n});\r\nconst vis=(p,t)=>p&&(p.visibility??1)>=t;\r\nconst inF=(p,t)=>vis(p,t)&&p.x>=0&&p.x<=1&&p.y>=0&&p.y<=1;\r\nconst CORE=[11,12,23,24],d3=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y,(a.z??0)-(b.z??0));\r\nconst ARM=[{id:'left',s:11,e:13,w:15},{id:'right',s:12,e:14,w:16}];\r\nexport function createCoverInput(T=COVER_TUNING){\r\n const st={base:null,calib:{n:0,x:0,y:0,t0:null},hidden:false,side:0,over:0,under:0,away:false,exit:null,lostAt:null,backAt:null,last:null,lastPunch:-1e9,aimX:.5,hands:ARM.map(a=>({id:a.id,w:null,wt:null,v:0,zone:null,zi:0,zAt:-1e9,rel:null,e:0,E:180,m:0,t:null,armed:true,cool:-1e9,firedAt:-1e9,holdAt:null,beam:false})),lastT:null};\r\n const O=T.off,P=T.punch,recal=()=>{st.base=null;st.calib={n:0,x:0,y:0,t0:null};};\r\n function reset(){recal();st.hidden=false;st.side=0;st.over=st.under=0;st.away=false;st.exit=null;st.lostAt=st.backAt=st.last=null;for(const h of st.hands){h.rel=null;h.t=null;h.w=null;h.zAt=-1e9;h.armed=true;}}\r\n const done=o=>{o.calibrated=!!st.base;o.hidden=st.hidden||st.away;o.offscreen=st.away;o.exit=st.away?st.exit:null;o.beam=o.beam||{on:false,x:0,hand:null};o.side=st.away?(st.exit==='left'?-1:st.exit==='right'?1:0):st.side;return o;};\r\n // detail = {points (33 normalized), world (33 metres, optional), width, height, mirrored, now (ms)}. Returns {visible, calibrated, hidden, side (-1 left, +1 right on SCREEN, 0 bottom), exit:'left'|'right'|'bottom'|null, offscreen, fire:[{hand,x,power}], shift, drop}\r\n function update(detail){\r\n  const out={visible:false,fire:[],zones:{left:0,middle:0,right:0,up:0},shift:0,drop:0,beam:{on:false,x:0,hand:null}},p=detail?.points,now=Number.isFinite(detail?.now)?detail.now:performance.now(),dtS=st.lastT==null?0:Math.max(0,(now-st.lastT)/1000);st.lastT=now;\r\n  const mirr=detail?.mirrored!==false,core=p?CORE.filter(i=>inF(p[i],T.minVis)):[];\r\n  if(core.length<2){ // off screen (or nobody): infer the exit from the last torso position + velocity\r\n   if(!st.away&&st.last){\r\n    if(st.lostAt==null)st.lostAt=now;const lost=(now-st.lostAt)/1000,L=st.last;\r\n    const d={left:L.sx+L.vx*O.lead,right:1-L.sx-L.vx*O.lead,bottom:1-L.sy-L.vy*O.lead},k=Object.keys(d).reduce((a,b)=>d[b]<d[a]?b:a);\r\n    const exit=d[k]<=O.edge?(lost>=O.edgeDwellS?k:null):(lost>=O.glitchS?'bottom':null);\r\n    if(exit){st.away=true;st.exit=exit;st.hidden=false;st.over=st.under=0;for(const h of st.hands){h.rel=null;h.t=null;h.holdAt=null;h.beam=false;}}\r\n   }\r\n   if(st.away&&st.base&&now-st.lostAt>O.recalS*1000)recal(); // the only recalibration: a long absence\r\n   st.backAt=null;return done(out);\r\n  }\r\n  const sh=inF(p[11],T.minVis)&&inF(p[12],T.minVis),all4=core.length===4;\r\n  if(st.away){ // return: both shoulders back and torso centred for a short dwell; baseline is kept\r\n   const c=sh?(mirr?1-(p[11].x+p[12].x)/2:(p[11].x+p[12].x)/2):-1;\r\n   if(c>=O.popLo&&c<=O.popHi){if(st.backAt==null)st.backAt=now;if((now-st.backAt)/1000>=O.popDwellS){st.away=false;st.exit=null;st.lostAt=st.backAt=null;st.hidden=false;st.over=st.under=0;st.last=null;}}else st.backAt=null;\r\n   if(st.away)return done(out);\r\n  }\r\n  st.lostAt=null;out.visible=true;\r\n  const mx=core.reduce((a,i)=>a+p[i].x,0)/core.length,my=core.reduce((a,i)=>a+p[i].y,0)/core.length,sx=mirr?1-mx:mx,L=st.last,ok=L&&dtS>0&&dtS<.3;\r\n  st.last={sx,sy:my,vx:ok?L.vx+((sx-L.sx)/dtS-L.vx)*.5:L?.vx||0,vy:ok?L.vy+((my-L.sy)/dtS-L.vy)*.5:L?.vy||0};\r\n  const aspect=detail.width&&detail.height?detail.width/detail.height:9/16,sw=Math.abs(p[11].x-p[12].x)||.1;\r\n  if(all4){\r\n   const hx=(p[23].x+p[24].x)/2,hy=(p[23].y+p[24].y)/2;\r\n   if(!st.base){\r\n    const c=st.calib;if(c.t0==null)c.t0=now;c.n++;c.x+=hx;c.y+=hy;\r\n    if((now-c.t0)/1000>=T.calibrateS&&c.n>=3)st.base={x:c.x/c.n,y:c.y/c.n};\r\n    if(!st.base)return done(out);\r\n   }\r\n   const dxRaw=(hx-st.base.x)/sw,drop=(hy-st.base.y)/(sw*aspect),right=mirr?-1:1,shift=dxRaw*right; // positive = screen right\r\n   out.shift=shift;out.drop=drop;\r\n   const l=T.lunge,deep=Math.abs(shift)>=l.shift&&drop>=l.drop;\r\n   if(!st.hidden){\r\n    if(deep){st.over+=dtS;if(st.over>=l.dwellS){st.hidden=true;st.side=Math.sign(shift);st.under=0;}}else{st.over=0;if(Math.abs(shift)<l.shift*.5&&Math.abs(drop)<l.drop)st.base={x:st.base.x+(hx-st.base.x)*T.adapt,y:st.base.y+(hy-st.base.y)*T.adapt};}\r\n   }else{\r\n    if(Math.abs(shift)<l.shift*l.release||Math.sign(shift)!==st.side){st.under+=dtS;if(st.under>=l.popDwellS){st.hidden=false;st.side=0;st.over=0;}}else st.under=0;\r\n   }\r\n  }else if(!st.base)return done(out);\r\n  if(st.hidden||!sh){for(const h of st.hands){h.holdAt=null;h.beam=false;}return done(out);}\r\n  // Punch = hard extension snap (fires on the way OUT, never waits for the retract). Beam = arm held out and still.\r\n  const W=detail.world,B=T.beam;let bh=null;\r\n  for(let i=0;i<2;i++){\r\n   const a=ARM[i],h=st.hands[i],wr=p[a.w],s2=p[a.s],e2=p[a.e];if(!inF(wr,T.minVis)){h.t=null;h.w=null;h.holdAt=null;h.beam=false;continue;}\r\n   const ws=W?.[a.s],ww=W?.[a.w],we=W?.[a.e],m3=ws&&ww&&we&&Number.isFinite(ww.x)?1:2;let rx,ry,rz=0,Ln,ang;\r\n   if(m3===1){rx=ww.x-ws.x;ry=ww.y-ws.y;rz=(ww.z??0)-(ws.z??0);Ln=d3(ws,we)+d3(we,ww);ang=elbowAngle(ws,we,ww);}\r\n   else{rx=(wr.x-s2.x)*aspect;ry=wr.y-s2.y;const ev=vis(e2,T.minVis);Ln=Math.max(ev?Math.hypot((s2.x-e2.x)*aspect,s2.y-e2.y)+Math.hypot((e2.x-wr.x)*aspect,e2.y-wr.y):0,P.minArm2D*sw*aspect);ang=ev?elbowAngle({x:s2.x*aspect,y:s2.y},{x:e2.x*aspect,y:e2.y},{x:wr.x*aspect,y:wr.y}):180;}\r\n   const e=Math.hypot(rx,ry,rz)/Ln,dt=h.t!=null&&h.m===m3?(now-h.t)/1000:0,sxw=Math.max(-1,Math.min(1,(wr.x-.5)*(mirr?-1:1)*2)); // wrist screen lane -1..1\r\n   let speed=0,dedt=0;if(dt>0&&dt<.3){speed=Math.hypot(rx-h.rel.x,ry-h.rel.y,rz-h.rel.z)/dt/Ln;dedt=(e-h.e)/dt;}\r\n   {const Z=T.zone,w2=h.w,dz=h.wt!=null?(now-h.wt)/1000:0;h.w={x:wr.x*aspect,y:wr.y};h.wt=now; // wrist screen speed in heights/s (EMA-smoothed against landmark jitter)\r\n    if(w2&&dz>0&&dz<.3)h.v=h.v*.4+Math.hypot(h.w.x-w2.x,h.w.y-w2.y)/dz*.6;else h.v=0;\r\n    if(h.v>=Z.gate){const up=vis(p[0],T.minVis)?p[0].y:s2.y-Z.upMargin;h.zone=wr.y<up?'up':sxw<-Z.third?'left':sxw>Z.third?'right':'middle';h.zi=Math.min(1,.35+(h.v-Z.gate)/(Z.gate*Z.span)*.65);h.zAt=now;}}\r\n   h.rel={x:rx,y:ry,z:rz};h.e=e;h.E=ang;h.m=m3;h.t=now;\r\n   if(!h.armed&&(e<P.extOff||(dedt<0&&now-h.firedAt>=P.rearmMs)))h.armed=true;\r\n   if(h.armed&&speed>=P.peak&&dedt>=P.radial&&e>=P.extMin&&now-h.cool>=P.cooldownMs&&now-st.lastPunch>=P.gapMs){\r\n    h.armed=false;h.cool=h.firedAt=st.lastPunch=now;h.holdAt=null;h.beam=false;\r\n    out.fire.push({hand:a.id,x:sxw,power:speed/P.peak});\r\n   }\r\n   const held=ry<=B.drop*Ln&&(h.beam?e>=B.extOff&&ang>=B.elbowOff:e>=B.ext&&ang>=B.elbow&&speed<=B.still);\r\n   if(held){if(h.holdAt==null)h.holdAt=now;if(h.beam||(now-h.holdAt)/1000>=B.dwellS)h.beam=true;}else{h.holdAt=null;h.beam=false;}\r\n   if(h.beam&&(!bh||e>bh.e))bh={e,x:sxw,hand:a.id};\r\n  }\r\n  for(const h of st.hands){const age=now-h.zAt;if(h.zone&&age<=T.zone.holdMs)out.zones[h.zone]=Math.max(out.zones[h.zone],h.zi*(1-age/T.zone.holdMs*.5));}\r\n  if(bh)out.beam={on:true,x:bh.x,hand:bh.hand};\r\n  return done(out);\r\n }\r\n return {update,reset,state:st};\r\n}\r\n","arcade/cover/cover-scene.mjs":"// COVER CRISIS three.js view. Same toon art + depth-ink pipeline as Cool Cardio (arcade/runner/worlds/scene.mjs); levels only change colours, props, enemies and attacks.\r\n// Enemies are roster coach GLBs tinted purple with a MOM Inc. chest badge and code-made dress. No bullets anywhere: rockets, fireballs, beams, swipes.\r\nimport * as T from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {Pipeline} from '../runner/worlds/sakura/post.js';\r\nimport {createSurfaceTexture} from '../runner/worlds/textures.mjs';\r\nimport {createCoverAtmosphere} from './atmosphere.mjs';\r\nimport {levelMaterials} from './textures.mjs';\r\nimport {mergeGeometries as mergeG,mergeable} from './merge.mjs';\r\nimport {layout,genRoute,poseAt,HW,H,STAIR,STEPS,RIDE,ARENA} from './route.mjs';\r\nconst TAU=Math.PI*2,ROSTER=id=>new URL(`../../creature/models/${id}.glb`,import.meta.url).href,BADGE=new URL('../../pod/mom-inc-mark-96.webp',import.meta.url).href;\r\nexport const LANE=1.6,ENEMY_Z=-6,ENEMY_H=2.4;\r\n// Camera rig. Cover stands at +-COVER.x; hiding slips the camera behind the block and dips it so the block top edge sits at the top of the screen.\r\nexport const COVER={x:.5,z:-2.2,w:1,h:1.15,d:1};\r\nexport const BAR={z:-1.2,h:1.05,d:.3,w:8}; // low barrier along the bottom of the view: cover on all three sides\r\n// How enemy attacks look: shape rocket | orb | spin | barrel | beam; arc = lobbed.\r\nexport const PROJ_VIS={rocket:{shape:'rocket',color:'#ff5a2a'},fireball:{shape:'orb',color:'#ff8a1a'},plasma:{shape:'orb',color:'#4fe8ff'},acid:{shape:'orb',color:'#8aff3a',arc:1},\r\n axe:{shape:'spin',color:'#c9c9d4',arc:1.2},barrel:{shape:'barrel',color:'#8a5a2a',arc:1.2},grenade:{shape:'grenade',color:'#3a4a2a',arc:1.4},crate:{shape:'crate',color:'#b5774a',arc:1.4},laser:{shape:'beam',color:'#ff2a4a'},swipe:{shape:'beam',color:'#ffffff'}};\r\n// How the player's attack looks. Add a kind here for a new level's weapon; unknown kinds use bolt.\r\nexport const PLAYER_VIS={rocket:{shape:'rocket'},bolt:{shape:'orb'},blaster:{shape:'blaster'},flame:{shape:'flame'},fireball:{shape:'fireball'},ice:{shape:'ice'},disc:{shape:'disc'},needler:{shape:'needler'}};\r\nconst glowTex=(()=>{let t;return()=>t||(()=>{const c=document.createElement('canvas');c.width=c.height=64;const g=c.getContext('2d'),q=g.createRadialGradient(32,32,0,32,32,32);q.addColorStop(0,'#fff');q.addColorStop(.35,'rgba(255,255,255,.7)');q.addColorStop(1,'rgba(255,255,255,0)');g.fillStyle=q;g.fillRect(0,0,64,64);t=new T.CanvasTexture(c);t.colorSpace=T.SRGBColorSpace;return t;})();})();\r\nconst ease=u=>1-(1-u)*(1-u);\r\nexport function createCoverScene(canvas,{beforeRender=()=>{}}={}){\n const renderer=new T.WebGLRenderer({canvas,antialias:false,powerPreference:'high-performance'});\r\n const scene=new T.Scene(),camera=new T.PerspectiveCamera(65,1,.1,160),pipeline=new Pipeline(renderer,scene,camera,{pixelBudget:1.5e6});\r\n const hemi=new T.HemisphereLight(0xf6e7ff,0x51416b,1.65),sun=new T.DirectionalLight(0xffe3b1,2.9),rim=new T.DirectionalLight(0x9ad8ff,.72);sun.position.set(-8,16,6);rim.position.set(7,8,-5);scene.add(hemi,sun,rim);\r\n const stage=new T.Group(),actors=new T.Group(),coverRoot=new T.Group(),fx=new T.Group();scene.add(stage,actors,coverRoot,fx);\r\n const textures=new Map(),mats=new Map(),geos=new Set(),glows=new Map(),sharedG=new Set(),sharedM=new Set(),bgeo=new Map();let disposed=false;\r\n // One analytic radial alpha map and one instanced pool ground the living cast\r\n // and the three major cover blocks. Shadows skew away from the side key.\r\n const shadowCanvas=document.createElement('canvas');shadowCanvas.width=shadowCanvas.height=64;const sg=shadowCanvas.getContext('2d'),gradient=sg.createRadialGradient(32,32,3,32,32,32);gradient.addColorStop(0,'rgba(5,7,16,.9)');gradient.addColorStop(.42,'rgba(5,7,16,.58)');gradient.addColorStop(1,'rgba(5,7,16,0)');sg.fillStyle=gradient;sg.fillRect(0,0,64,64);const shadowTexture=new T.CanvasTexture(shadowCanvas);shadowTexture.colorSpace=T.SRGBColorSpace;textures.set('projected-shadow',shadowTexture);\r\n const shadowMaterial=new T.MeshBasicMaterial({map:shadowTexture,color:0x454859,transparent:true,opacity:.58,depthWrite:false,toneMapped:false,side:T.DoubleSide}),shadowGeometry=new T.PlaneGeometry(1,1);shadowGeometry.rotateX(-Math.PI/2);geos.add(shadowGeometry);sharedM.add(shadowMaterial);\r\n const groundShadows=new T.InstancedMesh(shadowGeometry,shadowMaterial,32),shadowDummy=new T.Object3D(),shadowTint=new T.Color();groundShadows.name='cover-ground-shadow-pool';groundShadows.frustumCulled=false;groundShadows.count=0;groundShadows.renderOrder=1;scene.add(groundShadows);\r\n // free(): dispose an actor/effect subtree's own geometry + materials once (shared rig/glow/palette resources are skipped)\r\n const free=r=>r.traverse(n=>{const g=n.geometry;if(g&&!sharedG.has(g)){geos.delete(g);g.dispose();}const m=n.material;if(m&&!sharedM.has(m))m.dispose?.();});\r\n const tex=k=>{if(!textures.has(k))textures.set(k,createSurfaceTexture(T,k));return textures.get(k);};\r\n const mat=(c,kind='plaster')=>{if(c?.isMaterial)return c;const key=kind+c;if(!mats.has(key))mats.set(key,new T.MeshToonMaterial({color:c,map:tex(kind)})),sharedM.add(mats.get(key));return mats.get(key);};\r\n const glowMat=c=>{if(!glows.has(c))glows.set(c,new T.MeshBasicMaterial({color:c,toneMapped:false})),sharedM.add(glows.get(c));return glows.get(c);};\r\n const mesh=(g,m,x,y,z,p)=>{geos.add(g);const o=new T.Mesh(g,m);o.position.set(x,y,z);p.add(o);return o;};\r\n const box=(x,y,z,w,h,d,c,p,kind)=>mesh(new T.BoxGeometry(w,h,d),mat(c,kind),x,y,z,p);\r\n const cyl=(x,y,z,r,h,c,p,kind)=>mesh(new T.CylinderGeometry(r,r,h,12),mat(c,kind),x,y,z,p);\r\n const ball=(x,y,z,r,c,p)=>mesh(new T.SphereGeometry(r,10,7),mat(c,'plaster'),x,y,z,p);\r\n function sign(lines,x,y,z,w,h,p,color){const c=document.createElement('canvas');c.width=512;c.height=192;const g=c.getContext('2d');g.fillStyle='#230d30';g.fillRect(0,0,512,192);g.strokeStyle=color;g.lineWidth=6;g.shadowColor=color;g.shadowBlur=14;g.strokeRect(8,8,496,176);g.fillStyle=color;g.textAlign='center';g.textBaseline='middle';\r\n  lines.forEach((t,i)=>{g.font=`bold ${i?46:60}px monospace`;g.fillText(t,256,96+(i-(lines.length-1)/2)*62);});const map=new T.CanvasTexture(c);map.colorSpace=T.SRGBColorSpace;\r\n  const m=mesh(new T.PlaneGeometry(w,h),new T.MeshBasicMaterial({map,toneMapped:false}),x,y,z,p);m.userData.map=map;return m;}\r\n // ---------------------------------------------------------------- level / environment: a routed multi-floor building (route.mjs). `stage` = the world, moved each frame so the current stop's arena is the identity frame (camera, enemies, cover and fx live in that frame).\r\n let atmosphere=null,level=null,coverMeshes={left:null,right:null,bottom:null},coverTarget={left:0,right:0,bottom:0},LM=null,surfs={},surfClones=[],lay=null,groups=new Map(),builtA=-1,rnd=Math.random,lastOut=false,lastTr=false,bobT=0,bobA=0;\r\n const follow=new T.Group(),skyG=new T.Group(),YAX=new T.Vector3(0,1,0);scene.add(follow,skyG);camera.rotation.order='YXZ';\r\n function clearGroup(g){for(const o of [...g.children]){g.remove(o);o.traverse(n=>{if(n.userData.map){n.userData.map.dispose();n.material?.dispose?.();}if(n.geometry&&(geos.delete(n.geometry)||n.userData.own))n.geometry.dispose();});}}\r\n const FB={floor:['floor','stone'],wall:['wall','plaster'],ceiling:['trim','plaster'],trim:['trim','metal'],metal:[null,'metal'],cover:['cover','wood']};\r\n // textured surface per kind: levelMaterials (cc4-look) when present, else palette toon; UVs are metres/TILE so patterns keep their size on any box\r\n function surf(kind){\r\n  if(surfs[kind])return surfs[kind];const base=LM?.[kind];let m;\r\n  if(base){m=base.clone();if(base.map){m.map=base.map.clone();m.map.repeat.set(1,1);m.map.offset.set(0,0);m.map.needsUpdate=true;surfClones.push(m.map);}surfClones.push(m);}\r\n  else{const [pk,tk]=FB[kind];m=mat(pk?level.palette[pk]:'#8a8aa0',tk);}\r\n  if(m.map)m.map.wrapS=m.map.wrapT=T.RepeatWrapping;tileOf.set(m,{floor:2,wall:3,ceiling:1.5,trim:1,metal:1.5}[kind]||TILE);return surfs[kind]=m;}\r\n const mergeGeometries=l=>mergeG(T,l);\r\n // mergeStatic(root): bake every static indexed mesh under root into ONE mesh per material (draw calls). Skips userData.dyn (animated), .rig (shared GLB), .map (canvas signs) subtrees.\r\n function mergeStatic(root){\r\n  root.updateWorldMatrix(true,true);const inv=new T.Matrix4().copy(root.matrixWorld).invert(),by=new Map();\r\n  const walk=o=>{for(const c of o.children){const u=c.userData;if(u.dyn||u.rig||u.map||!c.visible)continue;if(c.isMesh&&!c.isSkinnedMesh&&!Array.isArray(c.material)&&mergeable(c.geometry)){const l=by.get(c.material)||by.set(c.material,[]).get(c.material);l.push({c,M:new T.Matrix4().multiplyMatrices(inv,c.matrixWorld)});}walk(c);}};walk(root);\r\n  for(const [m,l] of by){if(l.length<2)continue;const gs=l.map(({c,M})=>c.geometry.clone().applyMatrix4(M)),o=new T.Mesh(mergeGeometries(gs),m);gs.forEach(q=>q.dispose());o.userData.own=true;\r\n   for(const {c} of l){c.parent.remove(c);const g=c.geometry;if(!sharedG.has(g)){geos.delete(g);g.dispose();}}root.add(o);}}\r\n const TILE=3,tileOf=new Map(),_m4=new T.Matrix4(),_q=new T.Quaternion(),_e=new T.Euler(),_v=new T.Vector3(),_1=new T.Vector3(1,1,1);\r\n function batch(parent){const g=new Map();return {\r\n  box(m,x,y,z,w,h,d,ry=0,rx=0){const tl=tileOf.get(m)||TILE,geo=new T.BoxGeometry(w,h,d),uv=geo.attributes.uv,dm=[[d,h],[d,h],[w,d],[w,d],[w,h],[w,h]];\r\n   for(let i=0;i<24;i++)uv.setXY(i,uv.getX(i)*dm[i>>2][0]/tl,uv.getY(i)*dm[i>>2][1]/tl);\r\n   geo.applyMatrix4(_m4.compose(_v.set(x,y,z),_q.setFromEuler(_e.set(rx,ry,0)),_1));(g.get(m)||g.set(m,[]).get(m)).push(geo);},\r\n  flush(){for(const [m,l] of g){const geo=mergeGeometries(l);l.forEach(q=>q.dispose());const o=new T.Mesh(geo,m);o.userData.own=true;parent.add(o);}g.clear();}};}\r\n const mk=(parent,x,y,z,ry=0)=>{const g=new T.Group();g.position.set(x,y,z);g.rotation.y=ry;parent.add(g);return g;};\r\n const ez=u=>Math.max(0,Math.min(1,u)),sm=u=>{u=ez(u);return u*u*(3-2*u);};\r\n function runProps(lv,cell,g,scope='arena',sides=[-1,1]){const K=scope==='arena'?(lv.scale||1):1,p=lv.palette,kit={T,group:g,rnd,scope,sides,glow:glowMat,box:(x,y,z,w,h,d,c)=>box(x,y*K,z,w*K,h*K,d*K,c,g,'plaster'),cyl:(x,y,z,r,h,c)=>cyl(x,y*K,z,r*K,h*K,c,g,'metal'),sphere:(x,y,z,r,c)=>ball(x,y*K,z,r*K,c,g),sign:(lines,x,y,z,w,h)=>sign(lines,x,y*K,z,w*K,h*K,g,p.accent)};\r\n  try{lv.props?.(kit,cell);}catch(e){console.warn('cover level props',lv.id,e);}}\r\n function display(parent,x,y,z,ry,w=.7,h=.3){const c=document.createElement('canvas');c.width=128;c.height=48;const g=c.getContext('2d'),map=new T.CanvasTexture(c);map.colorSpace=T.SRGBColorSpace;\r\n  const m=mesh(new T.PlaneGeometry(w,h),new T.MeshBasicMaterial({map,toneMapped:false}),x,y,z,parent);m.rotation.y=ry;m.userData.map=map;let last=null;\r\n  m.userData.set=n=>{if(n===last)return;last=n;g.fillStyle='#10060a';g.fillRect(0,0,128,48);g.fillStyle='#ff5a2a';g.font='bold 40px monospace';g.textAlign='center';g.textBaseline='middle';g.fillText(String(n),64,26);map.needsUpdate=true;};m.userData.set(1);return m;}\r\n // hall-like shell (floor, ceiling, walls, baseboards, light strips) from z0 forward L metres at height y\r\n function shell(B,z0,L,y=0,o={}){const P=level.palette,zc=z0-L/2,fl=surf('floor'),wl=surf('wall'),ce=surf('ceiling'),tr=surf('trim'),li=glowMat(P.light);\r\n  B.box(fl,0,y-.1,zc,2*HW,.2,L);if(!o.open)B.box(ce,0,y+H+.1,zc,2*HW,.2,L);\r\n  for(const s of [-1,1]){B.box(wl,s*(HW+.1),y+H/2,zc,.2,H+.4,L);B.box(tr,s*(HW-.06),y+.15,zc,.12,.3,L);}\r\n  if(!o.open)for(let z=z0-1.5;z>z0-L+.5;z-=3)B.box(li,0,y+H-.03,z,.7,.06,1.2);}\r\n function buildPiece(p,parent){\r\n  const g=mk(parent,p.start.x,p.start.y,p.start.z,p.start.yaw),B=batch(g),t=p.type,P=level.palette,K=level.scale||1,fl=surf('floor'),wl=surf('wall'),ce=surf('ceiling'),tr=surf('trim'),li=glowMat(P.light),mt=mat('#858ba0','metal');\r\n  if(t==='hall'){shell(B,0,6);runProps(level,p.i*2+2,mk(g,0,0,0),'hall');}\r\n  else if(t==='left'||t==='right'){const sx=t==='left'?1:-1;\r\n   B.box(fl,0,-.1,-HW,2*HW,.2,2*HW);B.box(ce,0,H+.1,-HW,2*HW,.2,2*HW);B.box(wl,sx*(HW+.1),H/2,-HW,.2,H+.4,2*HW);B.box(wl,0,H/2,-2*HW-.1,2*HW+.4,H+.4,.2);\r\n   B.box(tr,sx*(HW-.06),.15,-HW,.12,.3,2*HW);B.box(tr,0,.15,-2*HW+.06,2*HW,.3,.12);B.box(li,0,H-.03,-HW,1.2,.06,1.2);B.box(tr,-sx*(HW-.1),1.4,-2*HW+.2,.2,2.8,.2); // corner post by the exit\r\n   runProps(level,p.i*2+3,mk(g,0,0,0),'turn',[sx]);}\r\n  else if(t==='up'||t==='down'){const dy=p.local.y,L=6,lo=Math.min(0,dy)-.2,hi=Math.max(0,dy)+H,len=Math.hypot(L,dy),rx=Math.atan2(dy,L);\r\n   for(let j=0;j<STEPS;j++){const top=dy*(j+1)/STEPS;B.box(j%2?tr:fl,0,(top+lo)/2,-(j+.5)*L/STEPS,2*HW,top-lo,L/STEPS+.01);}\r\n   for(const s of [-1,1]){B.box(wl,s*(HW+.1),(lo+hi)/2,-L/2,.2,hi-lo,L);B.box(tr,s*(HW-.3),dy/2+.95,-L/2,.07,.07,len,0,rx);}\r\n   B.box(ce,0,H+dy/2+.1,-L/2,2*HW,.2,len,0,rx);for(const d of [1.5,4.5])B.box(li,0,H-.03+dy*d/L,-d,.7,.06,1.2,0,rx);}\r\n  else if(t==='door'){shell(B,0,3);for(const s of [-1,1])B.box(wl,s*(.8+(HW-.8)/2),H/2,-1.5,HW-.8,H,.3);B.box(wl,0,(2.4+H)/2,-1.5,1.6,H-2.4,.3);\r\n   for(const s of [-1,1])B.box(tr,s*.85,1.2,-1.5,.1,2.4,.36);B.box(tr,0,2.45,-1.5,1.8,.1,.36);\r\n   const hg=mk(g,-.8,0,-1.5);hg.userData.dyn=true;const leaf=box(.8,1.2,0,1.6,2.4,.08,P.cover,hg,'wood');box(1.4,1.1,.06,.1,.1,.06,P.coverTop,hg,'metal');p.anim=u=>{hg.rotation.y=sm(u/.4)*1.9;};}\r\n  else if(t==='outside'){const L=7;B.box(fl,0,-.1,-L/2,2*HW,.2,L);\r\n   for(const s of [-1,1]){B.box(wl,s*(HW-.1),.55,-L/2,.2,1.1,L);B.box(tr,s*(HW-.1),1.15,-L/2,.35,.1,L);for(let z=-.5;z>-L;z-=1.75)B.box(tr,s*(HW-.1),.7,z,.16,1.4,.16);}\r\n   const sk=mat(P.fog,'plaster');for(let k=0;k<14;k++){const s=k%2?1:-1,w=3+rnd()*4,h=6+rnd()*18;B.box(sk,s*(9+rnd()*22),h/2-14,-rnd()*L*1.5+4,w,h+14,w);}\r\n   B.box(surf('floor'),0,-14,-L/2,120,.2,L+60);}\r\n  else if(t==='elevator'){const dy=p.local.y,gap=.85,Z=-2.4,ZE=-5.4,f0=Math.round(p.start.y/STAIR)+1,zc=-3.9,CW=gap+.1;shell(B,0,2.4);\r\n   const slab=(z,y)=>{for(const s of [-1,1])B.box(wl,s*(gap+(HW-gap)/2),y+H/2,z,HW-gap,H,.3);B.box(tr,0,y+(2.6+H)/2,z,2*gap,H-2.6,.3);};\r\n   slab(Z,0);slab(ZE,dy);\r\n   // cab: narrow so the side walls read in portrait; handrails, button panel, ceiling light strip, floor plate\r\n   const cab=mk(g,0,0,0),C=batch(cab);cab.userData.dyn=true;const dk=mat('#2a2a34','metal');C.box(mt,0,-.1,zc,2*CW,.2,3);C.box(ce,0,2.7,zc,2*CW,.2,3);for(const s of [-1,1]){C.box(wl,s*CW,1.35,zc,.2,2.7,3);C.box(tr,s*(CW-.14),.95,zc,.08,.08,2.7);C.box(tr,s*(CW-.1),.12,zc,.08,.24,3);C.box(dk,s*(CW-.12),1.9,zc,.04,.1,2.4);}\r\n   C.box(li,0,2.58,zc,.5,.05,2.4);C.box(dk,0,.01,zc,.9,.02,2.6);C.box(tr,CW-.12,1.2,-2.9,.05,.5,.22);for(let k=0;k<4;k++)C.box(li,CW-.15,1.04+k*.12,-2.9,.03,.05,.05);C.flush();\r\n   const lw=gap,leaf=(z,par,s)=>{const m=box(s*lw/2,1.3,z,lw,2.6,.1,mt,par);m.userData.dyn=true;box(-s*(lw/2-.03),0,.06,.05,2.6,.03,dk,m);box(0,1.0,.06,lw*.8,.05,.03,tr,m);return m;};\r\n   const lf=[-1,1].map(s=>leaf(Z-.2,g,s)),lr=[-1,1].map(s=>leaf(ZE+.2,cab,s));\r\n   const d1=display(g,0,2.9,Z+.2,0),d2=display(cab,0,2.3,ZE+.3,0,.8,.34);d1.userData.set(f0);\r\n   p.anim=u=>{const r=ez((u-RIDE[0])/(RIDE[1]-RIDE[0])),fo=1-sm((u-.26)/.08),ro=sm((u-.76)/.08);cab.position.y=dy*sm(r);\r\n    lf.forEach((m,i)=>{m.position.x=(i?1:-1)*(lw/2+fo*lw);m.visible=fo<.97;});lr.forEach((m,i)=>{m.position.x=(i?1:-1)*(lw/2+ro*lw);m.visible=ro<.97;});d2.userData.set(Math.round((p.start.y+dy*sm(r))/STAIR)+1);};}\r\n  B.flush();\r\n }\r\n function buildArena(i,parent){\r\n  const q=lay[i].pose,A=ARENA,g=mk(parent,q.x,q.y,q.z,q.yaw),B=batch(g),P=level.palette,K=level.scale||1,open=K>1||!!level.backdrop||level.id==='park',fl=surf('floor'),wl=surf('wall'),ce=surf('ceiling'),tr=surf('trim'),li=glowMat(P.light),W=A.w,zc=(A.back-A.depth)/2,D=A.back+A.depth;\r\n  B.box(fl,0,-.1,zc,2*W,.2,D);\r\n  for(const s of [-1,1]){B.box(wl,s*(W+.1),A.h/2,zc,.2,A.h+.2,D);B.box(tr,s*(W-.05),.2,zc,.1,.4,D);for(const z of [-1.5,-4.5,-7.5])B.box(tr,s*(W-.05),2.4,z,.1,1.5,2);}\r\n  if(!open){B.box(ce,0,A.h+.1,zc,2*W,.2,D);for(let z=1;z>-A.depth;z-=3.5)for(const x of [-3,3])B.box(li,x,A.h-.03,z,1.4,.06,.8);}\r\n  const wallZ=(z,gap)=>{if(!gap){B.box(wl,0,A.h/2,z,2*W,A.h,.3);return;}for(const s of [-1,1])B.box(wl,s*(HW+(W-HW)/2),A.h/2,z,W-HW,A.h,.3);B.box(tr,0,(H+A.h)/2,z,2*HW,A.h-H,.3);};\r\n  wallZ(A.back,true);wallZ(-A.depth,i<3);\r\n  B.flush();\r\n  // One arena kit, separately merged so its solid backdrop can leave with the encounter.\r\n  // Repeating it at -8 extended scenery beyond the rear wall into the next route piece.\r\n  const decor=mk(g,0,0,-1);decor.name=`arena-decor-${i}`;decor.userData.dyn=true;\r\n  runProps(level,i*2,decor);mergeStatic(decor);parent.userData.arenaDecor=decor;\r\n  decor.userData.sinkDepth=Math.max(0,new T.Box3().setFromObject(decor).max.y-decor.getWorldPosition(new T.Vector3()).y)+.1;\r\n }\r\n function buildStop(k){const g=new T.Group();stage.add(g);groups.set(k,g);for(const p of lay[k].pieces)if(p.box)buildPiece(p,g);buildArena(k,g);mergeStatic(g);}\r\n // keep the previous arena (we walk out through it), this stop and (built early, in the fight) the next; drop the rest\r\n function ensure(a,early){for(const k of [a-1,a])if(k>=0&&!groups.has(k))buildStop(k);if(early&&a<3&&!groups.has(a+1))buildStop(a+1);\r\n  if(builtA!==a){builtA=a;for(const [k,g] of [...groups])if(k<a-1){stage.remove(g);clearGroup(g);groups.delete(k);}}}\r\n function setLevel(lv){\r\n  if(disposed)return;\r\n  atmosphere?.dispose();LM?.dispose?.();for(const x of surfClones)x.dispose?.();surfClones=[];surfs={};\r\n  level=lv;const p=lv.palette;clearGroup(stage);clearGroup(coverRoot);clearGroup(follow);clearGroup(skyG);groups.clear();builtA=-1;coverMeshes={left:null,right:null,bottom:null};coverTarget={left:0,right:0,bottom:0};groundShadows.count=0;\r\n  LM=null;try{LM=levelMaterials(T,lv);}catch(e){console.warn('cover level materials',e);}\r\n  scene.background=new T.Color(p.sky);scene.fog=new T.Fog(p.fog,16,56);hemi.color.set(p.light);sun.color.set(p.light);\r\n  if(lv.backdrop==='ring'){const ring=new T.Group(),rm=new T.MeshBasicMaterial({color:'#d9e7dc',fog:false,toneMapped:false}),r2=new T.MeshBasicMaterial({color:'#7fe3d6',fog:false,toneMapped:false});mesh(new T.TorusGeometry(34,1.15,6,96),rm,0,0,0,ring);mesh(new T.TorusGeometry(34,.22,5,96),r2,0,0,.9,ring);ring.rotation.set(.1,0,.04);ring.position.set(0,15,-82);skyG.add(ring);} // portrait-readable Halo ring above the fight shell\r\n  atmosphere=createCoverAtmosphere(T,lv,follow);\r\n  rnd=(()=>{let a=lv.id.length*7919+13;return()=>(a=a*1664525+1013904223>>>0)/4294967296;})();\r\n  lay=layout(genRoute(lv));ensure(0);\r\n  for(const side of ['left','right']){const g=buildCover(lv.cover||{},side);g.position.x=(side==='left'?-1:1)*COVER.x;g.position.y=-2;g.position.z=COVER.z;mergeStatic(g);coverRoot.add(g);coverMeshes[side]=g;}\r\n  {const c=lv.cover||{},g=new T.Group(),col=surf('cover'),top=surf('metal');box(0,BAR.h/2,0,BAR.w,BAR.h,BAR.d,col,g,'wood');box(0,BAR.h+.04,0,BAR.w+.1,.1,BAR.d+.1,top,g,'metal');\r\n   if(lv.id==='crisis'){for(const x of [-3.2,3.2])box(x,.54,.16,.08,.86,.035,top,g,'metal');box(0,.15,.16,7.6,.1,.035,glowMat('#355b79'),g,'metal');}\r\n   if(lv.id==='ps2'){for(let x=-3.4;x<=3.4;x+=.65)box(x,.52,.16,.035,.82,.04,top,g,'metal');for(const x of [-3.6,3.6])box(x,.54,.18,.11,.95,.08,glowMat('#bd5639'),g,'metal');}\r\n   if(lv.id==='judgment'){for(let x=-3.5;x<=3.5;x+=1.4)box(x,.53,.16,.06,.86,.05,glowMat('#303b48'),g,'metal');for(const x of [-3.3,3.3])box(x,.19,.17,.55,.12,.04,glowMat('#e45d2b'),g,'metal');}\r\n   if(lv.id==='mansion'){box(0,.48,.16,5.6,.7,.035,glowMat('#281c23'),g,'wood');for(const x of [-3.4,3.4])box(x,.53,.17,.12,.98,.09,top,g,'metal');}\r\n   if(lv.id==='halo'){for(const x of [-3.6,3.6]){box(x,.52,.16,.12,.9,.08,glowMat('#65d9d6'),g,'metal');box(x,.97,.16,.34,.08,.18,glowMat('#d5e8dd'),g,'metal');}}\r\n   if(lv.id==='kitchen'){for(let x=-3.3;x<=3.3;x+=1.1)box(x,.5,.16,.18,.72,.05,top,g,'metal');}\r\n   mergeStatic(g);g.position.set(0,-2,BAR.z);coverRoot.add(g);coverMeshes.bottom=g;}\r\n  for(const m of [...actors.children])actors.remove(m);enemyMeshes.clear();\r\n }\r\n function buildCover(c,side){\r\n  const g=new T.Group(),col=surf('cover'),top=surf('metal'),{w,h,d}=COVER;\r\n  if(c.style==='pillar'){cyl(0,h/2,0,.55,h,col,g,'stone');box(0,h+.06,0,1.1,.14,1.1,top,g,'metal');for(const x of [-.25,.25])box(x,.56,.54,.055,.84,.035,glowMat(level.id==='halo'?'#55cbc7':'#536879'),g,'metal');} // low silhouette keeps the pop-up view clear\r\n  else if(c.style==='desk'){box(0,h*.46,0,w,h*.92,d,col,g,'wood');box(0,h*.95,0,w+.2,.12,d+.2,top,g,'metal');box(0,.46,d*.52,w*.84,.52,.035,glowMat(level.id==='mansion'?'#261b25':level.id==='crisis'?'#24394c':'#283541'),g,'metal');for(const x of [-.35,.35])box(x,.48,d*.55,.035,.56,.03,top,g,'metal');}\r\n  else{box(0,h/2,0,w,h,d,col,g,'wood');box(0,h+.05,0,w+.1,.12,d+.1,top,g,'metal');box(0,h*.5,d/2+.02,w*.9,.1,.02,top,g,'metal');box(0,h*.25,d/2+.02,w*.9,.1,.02,top,g,'metal');\r\n   if(level.id==='ps2'){for(let x=-.38;x<=.38;x+=.16)box(x,.48,d/2+.035,.025,.88,.035,top,g,'metal');}\r\n   if(level.id==='judgment'){for(const x of [-.39,.39])box(x,.5,d/2+.035,.12,.9,.05,glowMat('#343e4c'),g,'metal');box(0,.11,d/2+.04,.62,.09,.04,glowMat('#d45a2a'),g,'metal');}\r\n   if(level.id==='kitchen'){for(const x of [-.28,0,.28])box(x,.48,d/2+.035,.09,.62,.04,top,g,'metal');}\r\n  }\r\n  return g;\r\n }\r\n // ---------------------------------------------------------------- enemies\r\n const loader=new GLTFLoader(),cache=new Map();let meshoptReady=null;\r\n const withMeshopt=()=>meshoptReady||(meshoptReady=import('three/addons/libs/meshopt_decoder.module.js').then(m=>loader.setMeshoptDecoder(m.MeshoptDecoder)).catch(()=>{}));\r\n const badgeTex=(()=>{let t;return()=>t||(t=new T.TextureLoader().load(BADGE,x=>{x.colorSpace=T.SRGBColorSpace;}));})();\r\n const badgeRay=new T.Raycaster(),badgeOrigin=new T.Vector3(),badgeDir=new T.Vector3(0,0,-1),badgeHit=new T.Vector3();\r\n function torsoMark(holder,d){\r\n  holder.updateMatrixWorld(true);\r\n  badgeOrigin.set(0,.62,2);holder.localToWorld(badgeOrigin);\r\n  badgeDir.set(0,0,-1).transformDirection(holder.matrixWorld);\r\n  badgeRay.set(badgeOrigin,badgeDir);\r\n  const hit=badgeRay.intersectObject(holder,true).find(h=>h.object.isMesh);\r\n  if(hit)badgeHit.copy(holder.worldToLocal(hit.point.clone()));\r\n  else{\r\n   const chest=new T.Box3().makeEmpty(),w=new T.Vector3(),corners=[];\r\n   holder.traverse(o=>{if(!o.isMesh)return;const b=new T.Box3().setFromObject(o);\r\n    corners.length=0;for(const x of [b.min.x,b.max.x])for(const y of [b.min.y,b.max.y])for(const z of [b.min.z,b.max.z])corners.push(holder.worldToLocal(new T.Vector3(x,y,z)));\r\n    const local=new T.Box3().setFromPoints(corners),c=local.getCenter(w);if(c.y<.4||c.y>.75||Math.abs(c.x)>(d.w||.4)*.32)return;\r\n    chest.union(local);\r\n   });\r\n   if(chest.isEmpty())badgeHit.set(0,.62,(d.d||.3)*.5);\r\n   else badgeHit.set(T.MathUtils.clamp(0,chest.min.x,chest.max.x),T.MathUtils.clamp(.62,chest.min.y,chest.max.y),chest.max.z);\r\n  } // Bounds fallback stays within central chest-band meshes; placeholder dimensions are the final fallback.\r\n  const size=Math.min(.17,Math.max(.085,(d.w||.4)*.28));\r\n  const mark=new T.Mesh(new T.PlaneGeometry(size,size),new T.MeshBasicMaterial({map:badgeTex(),transparent:true,alphaTest:.06,depthTest:true,depthWrite:false,toneMapped:false,side:T.DoubleSide}));\r\n  mark.name='mom-inc-torso-mark';mark.position.copy(badgeHit);mark.position.z+=.006;holder.add(mark);\r\n }\r\n function loadRole(def){\r\n  const key=def.glb+'|'+(def.tint||'')+'|'+(def.dress||[]).includes('chrome');\r\n  if(!cache.has(key))cache.set(key,withMeshopt().then(()=>loader.loadAsync(ROSTER(def.glb))).then(gltf=>{\r\n   const root=gltf.scene,tint=new T.Color(def.tint||'#a070c0'),chrome=(def.dress||[]).includes('chrome');\r\n   root.traverse(o=>{if(o.isMesh){o.material=o.material.clone();sharedM.add(o.material);sharedG.add(o.geometry);o.material.color.lerp(tint,.45);if(o.material.emissive){o.material.emissive.copy(tint);o.material.emissiveIntensity=.13;}o.material.metalness=.03;o.material.roughness=.8;if(chrome){o.material.color.set('#c4bede').lerp(tint,.25);o.material.metalness=.5;o.material.roughness=.3;} /* silvery lavender: full metal turns black with no env map */}});\r\n   const bb=new T.Box3().setFromObject(root),size=bb.getSize(new T.Vector3()),ctr=bb.getCenter(new T.Vector3()),rig=new T.Group(),k=1/size.y;\r\n   root.position.set(-ctr.x,-bb.min.y,-ctr.z);rig.add(root);rig.scale.setScalar(k);rig.userData={w:size.x*k,d:size.z*k,rig:true};if(disposed){root.traverse(o=>{if(o.isMesh){o.geometry.dispose();o.material.dispose();}});return null;}return rig;\r\n  }).catch(e=>{console.warn('cover enemy model',def.glb,e);return null;}));\r\n  return cache.get(key);\r\n }\r\n function dressUp(g,def,dims){\r\n  const col=def.vest||'#c01830',w=dims.w,d=dims.d;\r\n  for(const name of def.dress||[]){\r\n   if(name==='beret'){const b=ball(0,.97,0,.17,'#7a1a2a',g);b.scale.set(1.2,.5,1.2);ball(0,1.03,0,.03,'#7a1a2a',g);}\r\n   else if(name==='helmet'){const h=mesh(new T.SphereGeometry(.19,12,8,0,TAU,0,Math.PI/2),mat('#3b3f58','metal'),0,.9,0,g);h.scale.set(1,.9,1);}\r\n   else if(name==='cap'){cyl(0,.97,0,.15,.07,'#2a3a7a',g);box(0,.95,d*.38,.22,.025,.14,'#2a3a7a',g);}\r\n   else if(name==='visor')mesh(new T.BoxGeometry(.3,.05,.06),glowMat('#79e8ff'),0,.86,d*.4,g);\r\n   else if(name==='vest')box(0,.52,d*.02,w*.55,.17,d*.8,col,g,'plaster');\r\n   else if(name==='suit'){box(0,.5,d*.02,w*.62,.3,d*.85,def.suit||'#1c1c2a',g,'plaster');box(0,.5,d*.5,.04,.3,.02,'#ffffff',g);}\r\n   else if(name==='tie')box(0,.5,d*.5+.015,.035,.2,.02,def.tieColor||'#c01830',g);\r\n   else if(name==='shades')mesh(new T.BoxGeometry(.28,.045,.05),mat('#05050a','metal'),0,.87,d*.42,g);\r\n   else if(name==='army'){box(0,.5,d*.02,w*.62,.3,d*.85,def.suit||'#4a5a34',g,'plaster');box(.12,.62,d*.45,.07,.06,.03,'#2a3320',g);box(-.12,.38,d*.45,.07,.06,.03,'#2a3320',g);}\r\n   else if(name==='gun'){const gun=new T.Group();gun.add(new T.Mesh(new T.BoxGeometry(.05,.07,.26),mat('#2a2a34','metal')),new T.Mesh(new T.BoxGeometry(.04,.1,.05),mat('#2a2a34','metal')));gun.children[1].position.set(0,-.07,-.06);gun.position.set(.2,.45,d*.5+.1);g.add(gun);mesh(new T.SphereGeometry(.025,6,5),glowMat('#ff8a1a'),.2,.46,d*.5+.24,g);}\r\n   else if(name==='antenna'){cyl(.08,1.08,0,.008,.2,'#aaa',g);mesh(new T.SphereGeometry(.03,6,5),glowMat('#ff2a4a'),.08,1.2,0,g);if(level.id==='halo'){box(-.18,.48,-.04,.24,.38,.18,'#a86e36',g,'metal');box(-.18,.68,-.04,.18,.05,.22,'#d9a552',g,'metal');cyl(-.18,.3,-.04,.09,.2,'#333941',g,'metal');}}\r\n   else if(name==='goo'){for(let i=0;i<4;i++)ball((i-1.5)*.07,.82-i%2*.07,d*.36,.035,'#9b4dff',g);}\r\n  }\r\n  if(level.id==='halo'&&def.cloak)for(const s of [-1,1]){box(s*.23,.62,d*.03,.2,.34,.22,'#647d82',g,'metal');box(s*.25,.72,d*.06,.12,.12,.16,'#9bc6c4',g,'metal');}\r\n  if(level.id==='kitchen'&&def.transform){const prop=def.transform.prop,front=d*.45,steel='#5e6870',joint='#253039';\r\n   box(0,.51,front,.28,.31,.055,steel,g,'metal');\r\n   for(const s of [-1,1]){cyl(s*.18,.34,0,.045,.24,joint,g,'metal');box(s*.16,.26,front,.1,.18,.06,def.transform.color||'#d5d8d5',g,'metal');}\r\n   if(prop==='toaster'){for(const x of [-.1,.1]){box(x,.72,front+.025,.065,.025,.02,'#18222a',g,'metal');box(x,.75,front+.026,.075,.012,.018,'#dbe4e2',g,'metal');}box(.28,.55,front,.035,.2,.04,'#cf4a35',g,'metal');}\r\n   else if(prop==='blender'){cyl(0,.68,front,.105,.27,'#b7d5d5',g,'metal');for(const y of [.61,.69,.77])box(0,y,front+.11,.19,.018,.02,'#edf3ec',g,'metal');box(0,.49,front+.03,.24,.08,.055,steel,g,'metal');}\r\n   else if(prop==='microwave'){box(0,.56,front+.026,.25,.18,.025,'#18242b',g,'metal');box(.17,.56,front+.045,.04,.18,.025,'#a6d9df',g,'metal');for(const y of [.5,.56,.62])box(.17,y,front+.06,.018,.018,.02,'#e3e6da',g,'metal');}\r\n  }\r\n }\r\n const enemyMeshes=new Map();\r\n // Transformers: a code-made kitchen appliance (about .6 of coach height) in the role colour; the coach unfolds from it\r\n function applianceMesh(prop,color='#cfcfd8'){\r\n  const a=new T.Group(),c=color||'#cfcfd8',dk='#2a2a34',B=(x,y,z,w,h,d,col=c)=>box(x,y,z,w,h,d,col,a,'metal'),C=(x,y,z,r,h,col=c)=>cyl(x,y,z,r,h,col,a,'metal');\r\n  if(prop==='fridge'){B(0,.42,0,.34,.84,.3);B(0,.62,.16,.3,.02,.02,dk);B(.12,.7,.16,.02,.14,.02,dk);}\r\n  else if(prop==='microwave'){B(0,.2,0,.56,.34,.34);B(-.08,.2,.18,.34,.24,.02,dk);B(.22,.2,.18,.08,.24,.02,'#79e8ff');}\r\n  else if(prop==='blender'){B(0,.1,0,.28,.2,.28,dk);C(0,.36,0,.12,.4,'#bfeaff');B(0,.6,0,.2,.05,.2,dk);}\r\n  else if(prop==='kettle'){ball(0,.2,0,.2,c,a);C(.2,.28,0,.025,.18,c).rotation.z=-.9;B(0,.44,0,.1,.05,.1,dk);}\r\n  else if(prop==='phone'){B(0,.36,0,.22,.5,.04,dk);B(0,.36,.025,.19,.42,.01,'#79e8ff');}\r\n  else if(prop==='mixer'){B(0,.08,0,.3,.16,.26);B(0,.3,-.06,.12,.3,.12);C(0,.2,.1,.13,.16,'#cfcfd8');}\r\n  else if(prop==='coffee'){B(0,.3,0,.3,.6,.26);C(0,.14,.1,.06,.14,'#bfeaff');B(0,.58,.02,.34,.06,.3,dk);}\r\n  else{B(0,.15,0,.48,.28,.28);B(-.1,.3,0,.12,.03,.18,dk);B(.1,.3,0,.12,.03,.18,dk);B(.22,.12,.15,.04,.04,.04,'#ff3a2a');} // toaster (default)\r\n  mergeStatic(a);a.scale.setScalar(1.1);return a;\r\n }\r\n function setOpacity(m,v){if(!m.own){m.holder.traverse(o=>{if(o.isMesh&&o.material){o.material=o.material.clone();}});m.own=true;}m.holder.traverse(o=>{const x=o.material;if(x){x.transparent=true;x.opacity=v;x.depthWrite=v>.95;}});m.op=v;}\r\n function makeEnemyMesh(e){\r\n  const g=new T.Group(),holder=new T.Group(),obj={g,holder,dead:false,dying:0,e,own:false,disg:!!e.disg,pop:0};g.add(holder);const dims={w:.4,d:.3},def=e.def;\r\n  // The arc opens toward the shooter (+Z), at the left arm. Keep its entire rim\r\n  // ahead of the dressed body bounds so broad rigs cannot slice through it.\r\n  const fitShield=()=>{if(!obj.shield)return;const sm=obj.shield,b=obj.bodyBounds,k=Math.min(1.15,Math.max(.8,(obj.dims.w||.4)/.45));sm.scale.setScalar(k);sm.position.set(b.min.x+.15*k*.55,sm.userData.restY,b.max.z+.015-(.15*Math.cos(1.25)-.016)*k);};\r\n  const place=rig=>{for(const c of [...holder.children]){holder.remove(c);free(c);}obj.own=false;const d=rig?rig.userData:dims;obj.dims=d;if(rig)holder.add(rig.clone(true));else{ball(0,.35,0,.18,'#8e5b99',holder);cyl(0,.65,0,.12,.4,'#8e5b99',holder);ball(0,.9,0,.12,'#a070c0',holder);}\r\n   dressUp(holder,def,d);try{level?.dress?.(holder,T,{box:(x,y,z,w,h,dd,c)=>box(x,y,z,w,h,dd,c,holder,'plaster'),cyl:(x,y,z,r,h,c)=>cyl(x,y,z,r,h,c,holder,'metal'),sphere:(x,y,z,r,c)=>ball(x,y,z,r,c,holder),glow:glowMat,dims:d,role:e.role,def});}catch(err){console.warn('cover level dress',err);}mergeStatic(holder);holder.updateWorldMatrix(true,true);\r\n   const bounds=new T.Box3().makeEmpty(),inverse=holder.matrixWorld.clone().invert();holder.traverse(o=>{if(!o.isMesh)return;if(!o.geometry.boundingBox)o.geometry.computeBoundingBox();bounds.union(o.geometry.boundingBox.clone().applyMatrix4(new T.Matrix4().multiplyMatrices(inverse,o.matrixWorld)));});obj.bodyBounds=bounds;\r\n   torsoMark(holder,d);fitShield();g.userData.mouth=new T.Vector3(0,.84,(d.d||.3)*.5);};\r\n  place(null);loadRole(def).then(rig=>{if(rig&&!disposed&&!obj.dead)place(rig);});\r\n  if(def.shield){const sm=new T.Group();sm.name='curved-arm-shield';sm.userData.restY=.47;const radius=.15,height=.34,arc=2.5,mat=new T.MeshBasicMaterial({color:'#4fe8ff',transparent:true,opacity:.34,depthWrite:false,toneMapped:false,side:T.DoubleSide});sm.add(new T.Mesh(new T.CylinderGeometry(radius,radius,height,24,1,true,-arc/2,arc),mat));const rimMat=new T.MeshBasicMaterial({color:'#d6ffff',toneMapped:false});for(const y of [-height/2,height/2]){const edge=new T.Mesh(new T.TorusGeometry(radius,.016,5,28,arc),rimMat);edge.rotation.set(Math.PI/2,0,Math.PI/2-arc/2);edge.position.y=y;sm.add(edge);}sm.visible=false;g.add(sm);obj.shield=sm;obj.shieldMix=0;obj.deflect=0;fitShield();}\r\n  if(def.transform){obj.app=applianceMesh(def.transform.prop,def.transform.color);g.add(obj.app);}\r\n  g.scale.setScalar(ENEMY_H*(def.scale||1)*(e.scare?1.2:1));g.visible=false;actors.add(g);\r\n  return obj;\r\n }\r\n function enemyPos(e,t,out){\r\n  const p=Math.min(1,e.enterT/e.enterDur),lane=e.x*LANE,sway=e.civ?0:Math.sin(t*1.7+e.id)*.12*(e.enter==='slide'?2:1);let x=lane+sway,y=0,z=e.scare?-4.6:e.def.walker?ENEMY_Z+(1-e.z)*3.4:ENEMY_Z;\r\n  if(e.state==='enter'){const u=ease(p);\r\n   if(e.enter==='jump'){y=Math.sin(p*Math.PI)*2.2*(1-p*.3);x=lane+(1-u)*(lane<0?-2:2);}\r\n   else if(e.enter==='slide')x=lane+(1-u)*(lane<0?-6:6);\r\n   else if(e.enter==='drop')y=(1-u)*(1-u)*8;\r\n   else y=-2.3*(1-u)*(1-u);}\r\n  return out.set(x,y,z);\r\n }\r\n // ---------------------------------------------------------------- projectiles, shots, particles\r\n const projMeshes=new Map(),shotMeshes=new Map(),parts=[];\r\n const sprite=(color,s)=>{const m=new T.Sprite(new T.SpriteMaterial({map:glowTex(),color,blending:T.AdditiveBlending,depthWrite:false,transparent:true,toneMapped:false}));m.scale.setScalar(s);return m;};\r\n function shape(vis,color){\r\n  const g=new T.Group();\r\n  if(vis.shape==='rocket'){const b=new T.Mesh(new T.CylinderGeometry(.07,.07,.5,8),mat('#cfcfd8','metal')),n=new T.Mesh(new T.ConeGeometry(.07,.18,8),mat(color,'metal'));b.rotation.x=n.rotation.x=Math.PI/2;n.position.z=.34;g.add(b,n);const f=sprite('#ffb040',.45);f.position.z=-.34;g.add(f);g.userData.rocket=true;}\r\n  else if(vis.shape==='bolt'){g.add(sprite(color,1.1),sprite('#ffffff',.4));}\r\n  else if(vis.shape==='blaster'){\r\n   const bolt=new T.Mesh(new T.CylinderGeometry(.07,.07,.5,8),mat(color,'metal'));\r\n   bolt.rotation.x=Math.PI/2;\r\n   g.add(bolt);\r\n   const core=sprite('#ffffff',0.3);\r\n   g.add(core);\r\n   g.userData.spin=7;\r\n  }\r\n  else if(vis.shape==='flame'){\r\n   const sizes=[0.15,0.12,0.1,0.08];\r\n   const colors=['#ff6600','#ffa500'];\r\n   for(let i=0;i<sizes.length;i++){\r\n    const s=sprite(colors[i%2],sizes[i]);\r\n    s.position.set((Math.random()-0.5)*0.3,(Math.random()-0.5)*0.3,(Math.random()-0.5)*0.3);\r\n    g.add(s);\r\n   }\r\n   g.userData.spin=4;\r\n  }\r\n  else if(vis.shape==='fireball'){\r\n   const big=sprite(color,1.0);\r\n   const yellow=sprite('#ffff00',0.5);\r\n   const core=sprite('#ffffff',0.3);\r\n   g.add(big,yellow,core);\r\n   g.userData.spin=3;\r\n  }\r\n  else if(vis.shape==='ice'){\r\n   const shard=new T.Mesh(new T.OctahedronGeometry(.18,0),mat(color,'metal'));\r\n   shard.scale.z=1.5;\r\n   g.add(shard);\r\n   const iceSprite=sprite('#add8e6',0.4);\r\n   g.add(iceSprite);\r\n   g.userData.spin=6;\r\n  }\r\n  else if(vis.shape==='disc'){\r\n   const disc=new T.Mesh(new T.TorusGeometry(.2,.05,8,20),mat(color,'metal'));\r\n   disc.rotation.x=Math.PI/2;\r\n   g.add(disc);\r\n   const core=sprite('#ffffff',0.3);\r\n   g.add(core);\r\n   g.userData.spin=14;\r\n  }\r\n  else if(vis.shape==='needler'){for(let i=0;i<3;i++){const c=new T.Mesh(new T.OctahedronGeometry(.1,0),glowMat(i?'#ff7ad9':'#ffd0f2'));c.scale.set(.5,.5,2.2);c.position.set((i-1)*.12,(i%2-.5)*.1,i*.1);c.rotation.z=i;g.add(c);}g.add(sprite('#ff4fc8',.55));g.userData.spin=3;}\r\n  else if(vis.shape==='grenade'){g.add(new T.Mesh(new T.SphereGeometry(.16,10,8),mat(color,'metal')),new T.Mesh(new T.CylinderGeometry(.04,.04,.1,6),mat('#cfcfd8','metal')));g.children[1].position.y=.18;g.add(sprite('#ff3a2a',.3));g.userData.spin=7;}\r\n  else if(vis.shape==='crate'){g.add(new T.Mesh(new T.BoxGeometry(.45,.4,.45),mat(color,'wood')));g.userData.spin=5;}\r\n  else if(vis.shape==='barrel'){const b=new T.Mesh(new T.CylinderGeometry(.22,.22,.45,10),mat(color,'wood'));b.rotation.z=Math.PI/2;g.add(b);g.userData.spin=6;}\r\n  else if(vis.shape==='spin'){g.add(new T.Mesh(new T.BoxGeometry(.5,.08,.08),mat('#6a4a2a','wood')),new T.Mesh(new T.BoxGeometry(.18,.34,.06),mat(color,'metal')));g.userData.spin=9;}\r\n  else{g.add(sprite(color,1.1),sprite('#ffffff',.4));}\r\n  return g;\r\n }\r\n function explode(pos,color='#ff8a2a',k=1){burst(pos,color,Math.round(26*k),5.5*k,.2);burst(pos,'#ffe9a0',Math.round(12*k),3*k,.14);burst(pos,'#ff3a2a',Math.round(10*k),7*k,.1);const r=sprite('#ffffff',1.4*k);r.position.copy(pos);fx.add(r);parts.push({o:r,v:new T.Vector3(),life:.18,max:.18,grow:14*k});rig.shake=Math.max(rig.shake,.18*k);for(let i=0;i<5;i++)smoke(tmp2.copy(pos).add({x:(Math.random()-.5)*.8,y:Math.random()*.6,z:0}));}\r\n // box debris: ONE instanced draw for every burst (was one mesh per piece); sprites (glow rings, smoke) stay individual and capped\r\n const BX=480,bmesh=new T.InstancedMesh(new T.BoxGeometry(1,1,1),new T.MeshBasicMaterial({toneMapped:false}),BX),bp=[],_d=new T.Object3D();bmesh.frustumCulled=false;bmesh.count=0;fx.add(bmesh);\r\n function burst(pos,color,n=14,speed=3,size=.12){for(let i=0;i<n&&bp.length<BX;i++){const a=Math.random()*TAU,b=Math.random()*Math.PI-Math.PI/2;bp.push({p:pos.clone(),c:new T.Color(color),s:size,v:new T.Vector3(Math.cos(a)*Math.cos(b),Math.sin(b)+.4,Math.sin(a)*Math.cos(b)).multiplyScalar(speed*(.4+Math.random())),life:.5,max:.5});}\r\n  const r=sprite(color,.6);r.position.copy(pos);fx.add(r);parts.push({o:r,v:new T.Vector3(),life:.3,max:.3,grow:5});}\r\n function smoke(pos){const s=sprite('#cfc8c0',.35);s.material.blending=T.NormalBlending;s.material.opacity=.7;s.position.copy(pos);fx.add(s);parts.push({o:s,v:new T.Vector3((Math.random()-.5)*.3,.2,0),life:.7,max:.7,grow:1.6,fade:true});}\r\n const tmp=new T.Vector3(),tmp2=new T.Vector3(),camPos=new T.Vector3(0,1.5,-.4);\r\n function coverPoint(side,out){return side?out.set(side*COVER.x,COVER.h,COVER.z+COVER.d/2):out.set(0,BAR.h,BAR.z+BAR.d/2);} // side 0 = bottom barrier\r\n\r\n // ---------------------------------------------------------------- held beam (pooled; rebuilt only when the level's kind changes)\r\n const BEAMS={rocket:'stream',fireball:'stream',disc:'stream',needler:'stream',flame:'flame'},beamG=new T.Group();fx.add(beamG);let beamKind=null,beamPool=[],beamHit=0;\r\n const bo=new T.Vector3(),be=new T.Vector3(),bd=new T.Vector3();\r\n function beamClear(){for(const o of [...beamG.children]){beamG.remove(o);o.traverse(n=>{n.geometry?.dispose?.();if(n.isSprite)n.material.dispose();});}beamPool=[];}\r\n function beamBuild(kind,color){beamClear();const mode=BEAMS[kind]||'ray';beamKind=kind;\r\n  if(mode==='ray'){const geo=new T.CylinderGeometry(1,1,1,8).rotateX(Math.PI/2),a=new T.Mesh(geo,glowMat(color)),b=new T.Mesh(geo,glowMat('#ffffff'));a.userData.w=.13;b.userData.w=.05;beamPool=[a,b,sprite(color,1.2)];beamG.add(...beamPool);}\r\n  else if(mode==='flame'){for(let i=0;i<28;i++){const q=sprite(i%2?'#ffa500':'#ff5a00',.3);q.userData.ph=i/28;q.userData.j=[Math.sin(i*12.9),Math.sin(i*78.2),Math.sin(i*37.7)];beamPool.push(q);beamG.add(q);}}\r\n  else{for(let i=0;i<9;i++){const o=shape(PLAYER_VIS[kind],color);o.userData.ph=i/9;beamPool.push(o);beamG.add(o);}}}\r\n function beamFrame(s,dt,t){\r\n  const b=s.beam,on=!!b?.on&&s.phase==='fight';beamG.visible=on;if(!on)return;\r\n  const pl=level.player||{},kind=pl.kind||'bolt',ice=kind==='ice',color=ice?'#bfeaff':pl.color||'#ff2a4a';if(kind!==beamKind||beamPool.length===0||beamG.userData.color!==color){beamBuild(kind,color);beamG.userData.color=color;}\r\n  bo.set((b.x||0)*.7,-.5,-1).applyMatrix4(camera.matrixWorld);const te=b.target!=null?enemyMeshes.get(b.target):null;\r\n  if(te)be.copy(te.g.position).setY(te.g.position.y+ENEMY_H*.55);else be.set((b.x||0)*6,b.zone==='up'?9:1.4,-30).applyMatrix4(camera.matrixWorld);\r\n  bd.subVectors(be,bo);const len=bd.length(),mode=BEAMS[kind]||'ray',spd=mode==='flame'?1.6:1.8;\r\n  if(mode==='ray'){const [a,c,g]=beamPool,w=1+Math.sin(t*40)*.15;for(const m of [a,c]){m.position.copy(bo).addScaledVector(bd,.5);m.lookAt(be);m.scale.set(m.userData.w*w,m.userData.w*w,len);}g.position.copy(be);g.scale.setScalar(.8+Math.sin(t*30)*.2);g.visible=!!te;a.material=glowMat(color);if(ice&&Math.random()<.5)smoke(tmp.copy(bo).addScaledVector(bd,Math.random()));}\r\n  else for(const o of beamPool){const u=(o.userData.ph+t*spd)%1;o.position.copy(bo).addScaledVector(bd,u);\r\n   if(mode==='flame'){const sp=u*.6,j=o.userData.j;o.position.x+=j[0]*sp;o.position.y+=j[1]*sp;o.position.z+=j[2]*sp*.5;o.scale.setScalar((.25+u*1.1)*(u>.9?(1-u)*10:1));}\r\n   else{o.lookAt(be);o.scale.setScalar(.35+u*.5);if(o.userData.spin)o.rotation.z+=dt*o.userData.spin;if(o.userData.rocket&&Math.random()<.15)smoke(o.position);}}\r\n  if(te&&(beamHit-=dt)<=0){beamHit=.1;burst(be,color,4,3.5,.07);burst(be,'#ffe9a0',2,2,.05);if(Math.random()<.3)smoke(be);rig.shake=Math.max(rig.shake,.06);}\r\n }\r\n // ---------------------------------------------------------------- frame\r\n const rig={x:0,y:1.6,z:0,pitch:0,shake:0};let lastPhase='',pitchS=0;\r\n function frame(s,dt,now,view={}){\r\n  if(disposed)return;const t=now/1000;atmosphere?.update(t);const fight=s.phase==='fight'||s.phase==='dead'||s.phase==='portal',hidden=s.input.hidden&&fight,side=s.input.side,a=Math.min(3,s.area),coverNow=level.areas[a].cover,st=lay[a];\r\n  const has=hidden&&(coverNow==='both'||(side<0&&coverNow==='left')||(side>0&&coverNow==='right'));\r\n  ensure(a,s.phase==='fight');\r\n  // route: tr = seconds into this stop's walk; the world is rotated/moved so this stop's arena is the identity frame\r\n  const dur=s.dur||st.dur,trav=s.phase==='travel'||s.phase==='intro',tr=s.phase==='intro'?0:s.phase==='travel'?Math.max(0,dur-s.timer):1e9,arr=s.phase==='travel'?tr/dur:s.phase==='intro'?0:1;\r\n  // Start at the authored pose on the kill frame, then sink below the floor before the camera reaches the backdrop.\r\n  // Deriving this from travel time also restores current/future kits on replay without carrying animation state.\r\n  for(const [i,g] of groups){const d=g.userData.arenaDecor;if(d){const u=i>=a?0:i===a-1&&s.phase==='travel'?sm(tr/.5):1;d.position.y=-d.userData.sinkDepth*u;d.visible=u<1;}}\r\n  // placed cover: stands in the world at the stop, gone while moving, pops up as we arrive\r\n  if(s.phase==='travel'&&lastPhase!=='travel')for(const k in coverMeshes)if(coverMeshes[k])coverMeshes[k].position.y=-2;\r\n  const up=arr>=.72;coverTarget.left=up&&(coverNow==='left'||coverNow==='both')?1:0;coverTarget.right=up&&(coverNow==='right'||coverNow==='both')?1:0;coverTarget.bottom=up?1:0;\r\n  for(const k of ['left','right','bottom']){const g=coverMeshes[k];if(g){g.position.y+=((coverTarget[k]?0:-2)-g.position.y)*(1-Math.exp(-dt*6));g.visible=g.position.y>-1.95;}}\r\n  let tx=0,ty=1.6,tz=0,tp=0;\r\n  if(hidden){tx=side*COVER.x;if(has){ty=.87;tz=0;tp=0;}else{ty=1.05;tz=0;}}\r\n  const k=1-Math.exp(-dt*(hidden?18:12));rig.x+=(tx-rig.x)*k;rig.y+=(ty-rig.y)*k;rig.z+=(tz-rig.z)*k;rig.pitch+=(tp-rig.pitch)*k;rig.shake=Math.max(0,rig.shake-dt);\r\n  const sh=!view.paused&&rig.shake>0?rig.shake*.5:0,q=st.pose,P=poseAt(st,tr);\n  stage.rotation.y=-q.yaw;stage.position.set(-q.x,-q.y,-q.z).applyAxisAngle(YAX,-q.yaw);\r\n  let ex=0,ey=0,ez2=0,yaw=0,roll=0,ptc=0;\r\n  if(trav||tr<1e8&&arr<1){_v.set(P.x-q.x,P.y-q.y,P.z-q.z).applyAxisAngle(YAX,-q.yaw);ex=_v.x;ey=_v.y;ez2=_v.z;yaw=P.yaw-q.yaw;if(arr>=1){ex=ey=ez2=yaw=0;}}\r\n  const walk=trav?P.walk:0;bobA+=(walk-bobA)*(1-Math.exp(-dt*8));bobT+=dt*14*bobA;\r\n  const rideShake=trav&&!P.walk?.004*Math.sin(t*47):0;\r\n  pitchS+=((trav?P.pitch:0)-pitchS)*(1-Math.exp(-dt*6));\r\n  camera.position.set(ex+rig.x+Math.sin(bobT)*.03*bobA+(Math.random()-.5)*sh,ey+rig.y+Math.abs(Math.sin(bobT))*.05*bobA-.02*bobA+rideShake,ez2+rig.z+(Math.random()-.5)*sh*0);\r\n  camera.rotation.set(rig.pitch+pitchS+Math.sin(bobT*.5)*.012*bobA,yaw+Math.sin(bobT*.5)*.01*bobA,Math.sin(bobT)*.012*bobA+(Math.random()-.5)*sh*.3);\r\n  if(trav){follow.position.set(ex,ey,ez2);follow.rotation.y=yaw;}else{follow.position.set(0,0,0);follow.rotation.y=0;} // atmosphere follows the walk, anchored to the arena in a fight\r\n  skyG.position.copy(camera.position);\r\n  // piece animations (doors swing, elevator rides): past pieces finished, future pieces idle\r\n  st.pieces.forEach((p,i)=>{if(p.anim)p.anim(s.phase==='intro'?0:trav?(i<P.idx?1:i===P.idx?P.u:0):1);});\r\n  {const fv=trav&&P.piece.type==='elevator'&&camera.aspect<1?86:65;if(Math.abs(camera.fov-fv)>.05){camera.fov+=(fv-camera.fov)*(1-Math.exp(-dt*7));camera.updateProjectionMatrix();}} // portrait ride: wider lens so the cab walls and handrails show beside the doors\r\n  {const out=trav&&P.piece.type==='outside',tv=s.phase==='travel';if(out!==lastOut){lastOut=out;atmosphere?.setOutside?.(out);}if(tv!==lastTr){lastTr=tv;atmosphere?.setTravel?.(tv);}}\r\n  lastPhase=s.phase;\r\n  // enemies\r\n  const seen=new Set();\r\n  for(const e of s.enemies){seen.add(e.id);let m=enemyMeshes.get(e.id);if(!m){m=makeEnemyMesh(e);enemyMeshes.set(e.id,m);}m.e=e;\r\n   m.g.visible=e.state!=='wait';enemyPos(e,t,m.g.position);\r\n   if(m.hitRemaining>0){m.hitRemaining=Math.max(0,m.hitRemaining-dt);const mark=m.g.getObjectByName('mom-inc-torso-mark');if(mark)mark.material.color.set(m.hitRemaining>0?m.hitTint:'#ffffff');}\r\n   if(e.def.flyer)m.g.position.y+=1.7+Math.sin(t*2.2+e.id)*.25;\r\n   if(m.app){const dg=!!e.disg;if(dg!==m.disg){m.disg=dg;if(!dg){m.pop=.3;burst(tmp.copy(m.g.position).setY(m.g.position.y+ENEMY_H*.35),e.def.transform.color||'#ffd24a',22,5,.12);burst(tmp,'#ffffff',8,3,.08);rig.shake=Math.max(rig.shake,.2);}}m.app.visible=dg;m.holder.visible=!dg;m.pop=Math.max(0,m.pop-dt);m.holder.scale.setScalar(1+m.pop*1.6);}\r\n   if(m.shield){const up=!!e.shield&&e.state!=='dead';m.shieldMix+=((up?1:0)-m.shieldMix)*Math.min(1,dt*16);m.shield.visible=m.shieldMix>.01||m.deflect>0;m.shield.position.y=m.shield.userData.restY-.18*(1-m.shieldMix);m.deflect=Math.max(0,m.deflect-dt);m.shield.traverse(o=>{if(o.material?.transparent)o.material.opacity=(.2+.14*m.shieldMix)+(m.deflect>0?.42:0);else if(o.material?.color&&o.geometry?.type==='TorusGeometry')o.material.color.set(m.deflect>0?'#ffffff':'#d6ffff');});}\r\n   if(e.def.cloak){const v=e.cloak?.12+Math.sin(t*14+e.id)*.05:1;if(m.op!==v&&(e.cloak||m.op!==undefined))setOpacity(m,v);}\r\n   const base=ENEMY_H*(e.def.scale||1)*(e.scare?1.2:1),pulse=e.state==='windup'?1+Math.sin(t*28)*.03+e.windup/e.windupDur*.08:1;m.g.scale.setScalar(base*pulse);m.g.rotation.y=Math.sin(t*2+e.id)*.06;\r\n   m.g.userData.pos=m.g.position;}\r\n  for(const [id,m] of enemyMeshes){if(!seen.has(id)){if(!m.dying){m.dying=.3;explode(tmp.copy(m.g.position).setY(m.g.position.y+ENEMY_H*.5),'#b07acc');}m.dying-=dt;m.g.scale.multiplyScalar(Math.max(.2,1-dt*5));m.g.position.y+=dt*1.5;m.g.rotation.y+=dt*9;if(m.dying<=0){m.dead=true;actors.remove(m.g);free(m.g);enemyMeshes.delete(id);}}}\r\n  // Cheap diagonal contact projections: grounded actors stay tight; jumping\r\n  // and flying actors get a wider, lighter footprint under the side key.\r\n  {let si=0;const reduced=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches||false,write=(x,y,z,sx,sz,lift=0,move=0)=>{shadowDummy.position.set(x+.34+lift*.18,.012,z+.78+lift*.1);shadowDummy.rotation.set(0,.48+move*.18,0);shadowDummy.scale.set(sx*(1+lift*.09),1,sz*(1+lift*.12+move*.22));shadowDummy.updateMatrix();groundShadows.setMatrixAt(si,shadowDummy.matrix);shadowTint.setRGB(1-lift*.22,1-lift*.22,1-lift*.22);groundShadows.setColorAt(si++,shadowTint);};\r\n   for(const m of enemyMeshes.values())if(m.g.visible&&si<29){const h=Math.max(0,m.g.position.y),dx=m.shadowLastX==null?0:Math.abs(m.g.position.x-m.shadowLastX)/Math.max(.016,dt);m.shadowLastX=m.g.position.x;write(m.g.position.x,m.g.position.y,m.g.position.z,.95,.42,h,reduced?0:Math.min(1,dx*.32));}\r\n   for(const k of ['left','right','bottom']){const g=coverMeshes[k];if(g?.visible&&si<32)write(g.position.x,g.position.y,g.position.z,k==='bottom'?2.2:.82,k==='bottom'?.5:.44,0);}\r\n   groundShadows.count=si;groundShadows.instanceMatrix.needsUpdate=true;if(groundShadows.instanceColor)groundShadows.instanceColor.needsUpdate=true;}\r\n  // enemy projectiles\r\n  const seenP=new Set();\r\n  for(const p of s.projs){seenP.add(p.id);let m=projMeshes.get(p.id);const vis=PROJ_VIS[p.kind]||PROJ_VIS.fireball;\r\n   if(!m){const em=enemyMeshes.get(p.from);const o=shape(vis,vis.color);const start=em?em.g.localToWorld(em.g.userData.mouth.clone()):new T.Vector3(p.x*LANE,2,ENEMY_Z);m={o,start};fx.add(o);projMeshes.set(p.id,m);}\r\n   const u=Math.min(1,p.t/p.dur),end=has?coverPoint(side,tmp2):camPos;tmp.lerpVectors(m.start,end,u);if(vis.arc)tmp.y+=Math.sin(u*Math.PI)*vis.arc;m.o.position.copy(tmp);m.o.lookAt(end);if(m.o.userData.spin)m.o.rotation.z+=dt*m.o.userData.spin;\r\n   m.o.scale.setScalar(.6+u*1.6);if(m.o.userData.rocket&&Math.random()<.6)smoke(tmp);m.last=tmp.clone();}\r\n  for(const [id,m] of projMeshes)if(!seenP.has(id)){fx.remove(m.o);free(m.o);projMeshes.delete(id);}\r\n  // player shots\r\n  const seenS=new Set();\r\n  for(const sh of s.shots){seenS.add(sh.sid);let m=shotMeshes.get(sh.sid);const vis=PLAYER_VIS[level.player?.kind]||PLAYER_VIS.bolt;\r\n   if(!m){const o=shape(vis,level.player?.color||'#ffb040');fx.add(o);m={o,start:new T.Vector3(side*.35+.3,rig.y-.5,rig.z-.8)};shotMeshes.set(sh.sid,m);}\r\n   const te=enemyMeshes.get(sh.id),tp=sh.kind==='proj'?projMeshes.get(sh.id)?.o.position:te?.g.position;\r\n   if(tp){m.end=m.end||new T.Vector3();m.end.copy(tp);if(sh.kind==='enemy')m.end.y+=ENEMY_H*.55;}\r\n   const end=m.end||tmp2.set(0,1.5,ENEMY_Z),u=Math.min(1,sh.t/sh.dur);m.o.position.lerpVectors(m.start,end,ease(u));m.o.lookAt(end);if(m.o.userData.rocket)smoke(m.o.position);m.o.scale.setScalar(1-u*.4);m.last=end.clone();m.color=level.player?.color||'#ffb040';}\r\n  for(const [id,m] of shotMeshes)if(!seenS.has(id)){fx.remove(m.o);free(m.o);shotMeshes.delete(id);if(m.last)explode(m.last,m.color);}\r\n  // held beam: look by level.player.kind; origin = bottom of the view at the aim lane (the hand), end = target enemy or far into the scene\r\n  beamFrame(s,dt,t);\r\n  // particles\r\n  while(parts.length>40){const q=parts.shift();fx.remove(q.o);q.o.material.dispose();}\r\n  {let n=0;for(let i=bp.length-1;i>=0;i--){const q=bp[i];if((q.life-=dt)<=0){bp[i]=bp[bp.length-1];bp.pop();}else{q.p.addScaledVector(q.v,dt);q.v.y-=9*dt;}}\r\n   for(const q of bp){_d.position.copy(q.p);_d.scale.setScalar(Math.max(.01,q.life/q.max)*q.s);_d.updateMatrix();bmesh.setMatrixAt(n,_d.matrix);bmesh.setColorAt(n++,q.c);}\r\n   bmesh.count=n;bmesh.instanceMatrix.needsUpdate=true;if(bmesh.instanceColor)bmesh.instanceColor.needsUpdate=true;}\r\n  for(let i=parts.length-1;i>=0;i--){const q=parts[i];q.life-=dt;const u=Math.max(0,q.life/q.max);q.o.position.addScaledVector(q.v,dt);if(q.grow)q.o.scale.setScalar(q.o.scale.x+q.grow*dt);\r\n   if(q.fade)q.o.material.opacity=.7*u;if(q.life<=0){fx.remove(q.o);q.o.material.dispose();parts.splice(i,1);}}\r\n  const restoreCamera=beforeRender(scene,camera);try{pipeline.render();}finally{if(typeof restoreCamera==='function')restoreCamera();}\n }\r\n function project(v,out={}){tmp.copy(v).project(camera);out.x=(tmp.x+1)/2;out.y=(1-tmp.y)/2;out.ok=tmp.z<1;return out;}\r\n const aim=new T.Vector3();\r\n // screen anchor + size of an enemy for the lock-on ring (0..1 fractions of the view)\r\n function enemyScreen(id,out={}){const m=enemyMeshes.get(id);if(!m||!m.g.visible)return null;const sc=m.g.scale.x;aim.copy(m.g.position).setY(m.g.position.y+sc*.62);project(aim,out);\r\n  const top=aim.clone().setY(m.g.position.y+sc),bot=aim.clone().setY(m.g.position.y);const a=project(top,{}),b=project(bot,{});out.h=Math.abs(b.y-a.y);return out;}\r\n function projScreen(id,out={}){const m=projMeshes.get(id);if(!m)return null;return project(m.o.position,out);}\r\n function resize(){const {width,height}=canvas.parentElement.getBoundingClientRect();if(!width||!height)return;camera.aspect=width/height;camera.updateProjectionMatrix();pipeline.setSize(width,height);}\r\n const observer=new ResizeObserver(resize);observer.observe(canvas.parentElement);resize();\r\n return {scene,camera,setLevel,frame,enemyScreen,projScreen,shake(s=.35){rig.shake=Math.max(rig.shake,s);},burst,explode,hitAt(id,color){const m=enemyMeshes.get(id);if(m){const p=tmp.copy(m.g.position).setY(m.g.position.y+ENEMY_H*.55);if(globalThis.matchMedia?.('(prefers-reduced-motion:reduce)')?.matches){m.hitRemaining=.18;m.hitTint=color||level?.player?.color||'#ffb040';}else{burst(p,color||level?.player?.color||'#ffb040',4,3.5,.07);burst(p,'#ffe9a0',2,2,.05);}if(color==='#4fe8ff'&&m.shield)m.deflect=.2;}},punchAt(id,power=1){const m=enemyMeshes.get(id),p=m?tmp.copy(m.g.position).setY(m.g.position.y+ENEMY_H*.55).clone():new T.Vector3(0,1.4,ENEMY_Z),k=1+Math.min(2,power*.6);explode(p,level?.player?.color||'#ffb040',k);rig.shake=Math.max(rig.shake,.35*k);},coverPoint:(side)=>coverPoint(side,new T.Vector3()),\r\n  has:{get level(){return level}},warm:lv=>Promise.all(Object.values(lv?.roles||{}).map(loadRole)).catch(()=>{}),stats:()=>({...pipeline.sceneStats}),\r\n  dispose(){if(disposed)return;disposed=true;beamClear();for(const m of enemyMeshes.values()){m.dead=true;free(m.g);}for(const m of projMeshes.values())free(m.o);for(const m of shotMeshes.values())free(m.o);for(const q of parts)q.o.material.dispose();groundShadows.dispose();shadowMaterial.dispose();groundShadows.removeFromParent();bmesh.geometry.dispose();bmesh.material.dispose();bmesh.dispose();bp.length=0;enemyMeshes.clear();projMeshes.clear();shotMeshes.clear();parts.length=0;fx.clear();actors.clear();for(const p of cache.values())p.then(r=>r&&r.traverse(o=>{if(o.isMesh){o.geometry.dispose();o.material.dispose();}}));cache.clear();observer.disconnect();atmosphere?.dispose();LM?.dispose?.();clearGroup(stage);clearGroup(follow);clearGroup(skyG);for(const m of mats.values())m.dispose();for(const t of textures.values())t.dispose();for(const g of geos)g.dispose();pipeline.dispose();renderer.dispose();renderer.forceContextLoss();}};\r\n}\r\n","arcade/cover/cover-sfx.mjs":"// CROUCHING CRISIS sound: WebAudio synthesised SFX, no files. Rides the app's gesture-unlocked physicalSound graph (context, master gain, mute, voice limits); silent when none is passed.\r\n// createCoverSfx({audio}) -> {play(name,o), travel(kind), event(ev,s), frame(s,dt), stop()}. travel kinds: step stairs elevator door outside.\r\nconst DEF={hall:{dur:1.2},left:{dur:.8},right:{dur:.8},up:{dur:1.5},down:{dur:1.5},elevator:{dur:3.2},outside:{dur:1.4},door:{dur:.7},exit:{dur:9.5/5.4},arrive:{dur:.45}}; // mirrors route.mjs DEF durations (kept local so sfx has no import coupling)\r\nexport const SFX_NAMES=['step','stairs','ding','hum','door','servo','spawn','zap','rocket','flame','needler','ice','disc','boomS','boomL','shield','cloak','flee','clank','splat','clang','thud','clear','stinger','wind'];\r\nexport function createCoverSfx({audio=null}={}){\r\n let plan=[],dead=false,loops=new Map(),lastBeam='',lastTravel=-9,seen=new Map(),servoT=0,hum=null;\r\n const ok=()=>!dead&&audio?.context&&audio.context.state==='running'&&!audio.muted;\r\n const rnd=(a,b)=>a+Math.random()*(b-a);\r\n // v(kind,secs,fn(ctx,tone,noise)): one pooled voice; tone(freq,dur,lvl,type,end,at) noise(dur,lvl,lo,hi,at)\r\n const v=(kind,secs,fn)=>{if(!ok())return null;return audio.voice('cc-'+kind,secs,ctx=>fn(ctx,(...a)=>audio.tone(ctx,...a),(...a)=>audio.noiseBurst(ctx,...a)));};\r\n const osc=({c,t,nodes,output},type,f,lvl,{lfo=0,depth=0,lp=0}={})=>{const o=c.createOscillator(),g=c.createGain();o.type=type;o.frequency.value=f;g.gain.value=lvl;let n=o;if(lp){const b=c.createBiquadFilter();b.type='lowpass';b.frequency.value=lp;o.connect(b);n=b;nodes.push(b);}n.connect(g).connect(output);o.start(t);nodes.push(o,g);\r\n  if(lfo){const l=c.createOscillator(),lg=c.createGain();l.frequency.value=lfo;lg.gain.value=depth;l.connect(lg).connect(o.frequency);l.start(t);nodes.push(l,lg);}return o;};\r\n const loopNoise=({c,t,nodes,output},kind,freq,q,lvl,wob=0)=>{const s=audio.sourceNoise(),f=c.createBiquadFilter(),g=c.createGain();s.loop=true;f.type=kind;f.frequency.value=freq;f.Q.value=q;g.gain.value=lvl;s.connect(f).connect(g).connect(output);s.start(t);nodes.push(s,f,g);\r\n  if(wob){const l=c.createOscillator(),lg=c.createGain();l.frequency.value=wob;lg.gain.value=lvl*.6;l.connect(lg).connect(g.gain);l.start(t);nodes.push(l,lg);}};\r\n const S={\r\n  step:(o={})=>v('step',.12,(c,tn,nz)=>{const p=o.pace||1;nz(.07,.16,rnd(260,420)*p,rnd(90,150));tn(rnd(95,125)*p,.08,.1,'sine',55);}),\r\n  stairs:()=>v('stairs',.3,(c,tn,nz)=>{for(let i=0;i<2;i++){nz(.08,.15,rnd(350,520),130,i*.17);tn(rnd(130,170),.09,.08,'triangle',70,i*.17);}}),\r\n  ding:()=>v('ding',1.4,(c,tn)=>{tn(1318,.9,.1,'sine',1318);tn(2636,.5,.03,'sine',2636);tn(988,1.1,.1,'sine',988,.32);tn(1976,.6,.025,'sine',1976,.32);}),\r\n  hum:()=>{hum?.stop(.2);hum=v('hum',3.2,c=>{osc(c,'sawtooth',55,.05,{lp:240,lfo:.7,depth:1.5});osc(c,'sine',110,.04);osc(c,'sine',165.5,.015);});return hum;},\r\n  door:()=>v('door',.6,(c,tn,nz)=>{nz(.45,.14,300,1900);tn(90,.2,.12,'sine',45,.32);nz(.05,.1,2500,900,.34);}),\r\n  servo:()=>v('servo',.4,(c,tn,nz)=>{const f=rnd(210,340);tn(f,.28,.035,'sawtooth',f*rnd(1.3,1.7));tn(f*2.02,.22,.02,'square',f*2.6,.04);nz(.2,.04,2500,4500,.02);}),\r\n  spawn:()=>v('spawn',.45,(c,tn,nz)=>{tn(180,.3,.07,'triangle',720);tn(360,.3,.04,'sine',1100,.05);nz(.18,.05,1500,5000);}),\r\n  zap:()=>v('zap',.2,(c,tn,nz)=>{tn(1500,.14,.07,'square',160);nz(.08,.05,3000,900);}),\r\n  rocket:()=>v('rocket',.8,(c,tn,nz)=>{nz(.7,.16,200,1500);tn(95,.5,.08,'sawtooth',230);}),\r\n  needler:()=>v('needler',.5,(c,tn)=>{for(let i=0;i<4;i++)tn(rnd(2200,4200),.09,.045,'sine',rnd(1800,3600),i*.055);}),\r\n  ice:()=>v('ice',.5,(c,tn,nz)=>{for(let i=0;i<7;i++)nz(.03,.06,rnd(3500,6500),rnd(2500,4000),rnd(0,.4));tn(2800,.2,.03,'sine',3400);}),\r\n  disc:()=>v('disc',.6,c=>{osc(c,'sawtooth',420,.03,{lfo:24,depth:60,lp:1400});}),\r\n  boomS:()=>v('boom',.5,(c,tn,nz)=>{nz(.35,.2,900,80);tn(130,.3,.18,'sine',35);}),\r\n  boomL:()=>v('boom',1.1,(c,tn,nz)=>{nz(.9,.28,1100,60);tn(90,.8,.28,'sine',28);tn(60,.9,.12,'sawtooth',25,.04);}),\r\n  shield:()=>v('shield',.7,c=>{osc(c,'sine',190,.05,{lfo:6,depth:4});osc(c,'sine',196,.04);}),\r\n  cloak:()=>v('cloak',.8,(c,tn,nz)=>{for(let i=0;i<5;i++)tn(1100*(1+i*.22),.3,.025,'triangle',1100*(1+i*.22)*1.5,i*.07);nz(.5,.03,4000,8000);}),\r\n  flee:()=>v('flee',.5,(c,tn)=>{tn(900,.4,.06,'sawtooth',2500);tn(1350,.35,.03,'square',3500,.04);}),\r\n  clank:()=>v('clank',.6,(c,tn,nz)=>{tn(220,.12,.1,'square',150);for(const f of [1830,2740,3500])tn(f,.35,.03,'sine',f*.99,.01);nz(.06,.1,2000,800);}),\r\n  splat:()=>v('splat',.3,(c,tn,nz)=>{nz(.16,.2,700,140);tn(220,.14,.12,'sine',60);}),\r\n  clang:()=>v('clang',.7,(c,tn,nz)=>{for(const f of [690,1130,1790,2410])tn(f,.5,.045,'sine',f*.995);nz(.05,.12,3000,1200);}),\r\n  thud:()=>v('thud',.3,(c,tn,nz)=>{tn(115,.2,.22,'sine',42);nz(.08,.12,400,90);}),\r\n  clear:()=>v('clear',1.2,(c,tn)=>{[523,659,784,1047].forEach((f,i)=>{tn(f,.5,.07,'triangle',f,i*.11);tn(f*2,.3,.02,'sine',f*2,i*.11);});}),\r\n  stinger:()=>v('stinger',1.6,(c,tn,nz)=>{tn(110,1.2,.1,'sawtooth',98);tn(165,1.2,.07,'sawtooth',147);tn(55,1.4,.2,'sine',50);nz(.5,.1,600,150);}),\r\n  flame:()=>v('flame',1.2,c=>{loopNoise(c,'lowpass',700,.5,.2,9);}),\r\n  wind:()=>v('wind',2.2,c=>{loopNoise(c,'bandpass',500,.8,.07,.4);}),\r\n };\r\n const play=(name,o)=>{try{S[name]?.(o);}catch{}};\r\n // looped beams: started/stopped from the zone/beam state; kind = level.player.kind\r\n const BEAM={blaster:['bandpass',1800,3,.05,14],rocket:['lowpass',320,.6,.14,7],flame:['lowpass',700,.5,.22,9],fireball:['lowpass',500,.5,.18,8],ice:['highpass',4200,2,.05,17],disc:['bandpass',900,5,.05,22],needler:['bandpass',3200,6,.04,12],bolt:['bandpass',1400,3,.05,12]};\r\n const beamStart=kind=>{const [ft,f,q,l,w]=BEAM[kind]||BEAM.bolt;const h=v('beam-'+kind,Infinity,c=>{loopNoise(c,ft,f,q,l,w);if(kind==='blaster'||kind==='bolt')osc(c,'sawtooth',110,.02,{lfo:30,depth:14,lp:900});if(kind==='disc')osc(c,'sawtooth',330,.02,{lfo:20,depth:40,lp:1200});});if(h)loops.set('beam',{h,kind});};\r\n const beamStop=()=>{const b=loops.get('beam');if(b){b.h.stop(.12);loops.delete('beam');}};\r\n const travel=kind=>{if(kind==='step')play('step',{pace:1});else if(kind==='stairs')play('stairs');else if(kind==='elevator'){play('door');setTimeout(()=>{play('hum');},250);setTimeout(()=>play('ding'),3000);}else if(kind==='door')play('door');else if(kind==='outside'){play('door');play('wind');}};\r\n // route-driven cues: ev.segs -> timeline of walk steps, stair steps, door/elevator/outside cues, fired from frame() by travel progress\r\n const mkPlan=(segs,exit)=>{const out=[];let t=0;const walk=(a,b,dt,k)=>{for(let x=a;x<b-.05;x+=dt)out.push({t:x,k});};\r\n  for(const n of [...(exit?['exit']:[]),...segs,'arrive']){const d=DEF[n]?.dur||.5;\r\n   if(n==='up'||n==='down')walk(t,t+d,.32,'stairs');else if(n==='elevator'){out.push({t,k:'door'},{t:t+d*.26,k:'hum'},{t:t+d*.84,k:'ding'},{t:t+d*.88,k:'door'});walk(t,t+d*.24,.4,'step');}\r\n   else{if(n==='door')out.push({t,k:'door'});if(n==='outside'){out.push({t,k:'door'},{t:t+.2,k:'wind'});}walk(t,t+d,.4,'step');}\r\n   t+=d;}\r\n  return out.sort((a,b)=>a.t-b.t);};\r\n const event=(ev,s)=>{\r\n  switch(ev.type){\r\n   case 'level':play('stinger');break;case 'enter':play('spawn');if(s?.enemies?.find(e=>e.id===ev.id)?.def?.dress?.includes('chrome'))setTimeout(()=>play('servo'),120);break;\r\n   case 'windup':play(ev.kind==='laser'||ev.kind==='plasma'?'zap':'servo');break;\r\n   case 'fire':play(ev.kind==='rocket'||ev.kind==='grenade'?'rocket':ev.kind==='laser'||ev.kind==='plasma'?'zap':'clang');break;\r\n   case 'kill':play(ev.role==='heavy'||ev.scare?'boomL':'boomS');break;\r\n   case 'shotDown':play('boomS');break;case 'flee':play('flee');break;case 'transform':play('clank');break;\r\n   case 'punch':play('thud');break;case 'deflect':play('shield');break;\r\n   case 'block':play('clang');break;case 'hit':play('thud');play('boomS');break;case 'areaClear':play('clear');break;\r\n   case 'civilian':play('splat');break;case 'scare':play('boomL');break;case 'dead':beamStop();play('boomL');break;\r\n   case 'travel':if(ev.segs)plan=mkPlan(ev.segs,ev.area>0);else travel(ev.kind);break;case 'segment':if(ev.kind)travel(ev.kind);break;\r\n  }};\r\n const frame=(s,dt)=>{if(dead||!s)return;\r\n  if(s.phase==='travel'&&plan.length){const el=(s.dur||0)-(s.timer||0);while(plan.length&&plan[0].t<=el)play(plan.shift().k);}else if(s.phase!=='travel')plan=[];\r\n  const zs=s.zones||{},on=s.beam?.on||zs.left>0||zs.middle>0||zs.right>0||zs.up>0,kind=s.level?.player?.kind||'bolt',cur=loops.get('beam');\r\n  if(on&&s.phase==='fight'){if(cur&&cur.kind!==kind)beamStop();if(!loops.has('beam')&&ok())beamStart(kind);}else if(cur)beamStop();\r\n  for(const e of s.enemies||[]){const p=seen.get(e.id)||{};if(e.shield&&!p.shield)play('shield');if(e.cloak&&!p.cloak)play('cloak');seen.set(e.id,{shield:e.shield,cloak:e.cloak});}\r\n  if(seen.size>64)for(const k of seen.keys()){if(!(s.enemies||[]).some(e=>e.id===k))seen.delete(k);}\r\n  servoT-=dt;if(servoT<=0){servoT=rnd(1.1,2.2);if((s.enemies||[]).some(e=>e.def?.walker&&e.state!=='dead'&&e.state!=='wait'&&e.def?.dress?.includes('chrome')))play('servo');}\r\n };\r\n return {play,travel,event,frame,stop(){dead=true;beamStop();hum?.stop?.(.1);loops.clear();}};\r\n}\r\n","arcade/cover/cover-sim.mjs":"// COVER CRISIS rules: pure logic, no three.js, no DOM. createCoverSim({levels,seed}) -> sim. The view reads sim.s and drains sim.events.\r\nimport {LEVELS} from './levels/index.mjs';\r\nimport {genRoute,travelDur} from './route.mjs';\r\nexport const SIM={hp:10,windup:1.6,windupDecay:.92,windupFloor:.7,flight:[.7,1],speedCap:1.6,speedStep:.1,enemyCap:8,introS:2.4,enterS:.75,\r\n portalHeal:1,grace:.35,scareArea:2,scareWindup:.8,shotS:.12,civilianPenalty:-3,swipeWindup:.6,areas:4,popGrace:.3,beamDps:2.5,beamLane:.18,beamHitS:.2,punchDmg:2,shockLane:.35,zoneDps:[3,6],third:1/3,zoneHold:.25,firstWindup:1.4,fleeSpeed:1.4,morphS:.6,wave1:[1,2,3,3]};\r\nconst SHOOTABLE=new Set(['rocket','fireball','plasma','acid','axe','barrel','grenade','crate']);\r\nconst INSTANT=new Set(['laser','swipe']);\r\nconst pick=(r,list)=>list[Math.floor(r()*list.length)];\r\n// Same draw as the closed-over integer. state/load are opt-in; nothing in step() reads them.\r\nfunction mulberry(a){\r\n const box={a:a|0};\r\n const next=()=>{box.a|=0;box.a=box.a+0x6D2B79F5|0;let t=Math.imul(box.a^box.a>>>15,1|box.a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};\r\n next.state=()=>box.a;\r\n next.load=v=>{if(!Number.isInteger(v)||v<-2147483648||v>2147483647)throw new RangeError('invalid cover rng');box.a=v;};\r\n return next;\r\n}\r\nexport const windupFor=loop=>Math.max(SIM.windupFloor,SIM.windup*SIM.windupDecay**loop);\r\nexport const speedFor=loop=>Math.min(SIM.speedCap,1+SIM.speedStep*loop);\r\nexport function createCoverSim({levels=LEVELS,seed=1}={}){\r\n const r=mulberry(seed),s={phase:'intro',timer:SIM.introS,levelNo:0,level:levels[0],loop:0,area:0,route:null,dur:0,hp:SIM.hp,enemies:[],projs:[],shots:[],input:{side:0,hidden:false},\r\n  t:0,id:0,grace:0,scared:false,kills:0,xp:0,deadAt:0,beam:{on:false,x:0,target:null,zone:null},zones:{left:0,middle:0,right:0,up:0}},events=[],req={on:false,x:0};let beamHitT=0,zT=-1e9;\r\n const emit=(type,d={})=>events.push({type,t:s.t,...d});\n const projectileBirths=new Map(); // Transient offsets within this step; snapshots contain only elapsed age.\n const area=()=>s.level.areas[s.area];\r\n // Hidden = protected on every side (left, right, bottom=0, like Time Crisis). The level's cover field is visual only.\r\n function protectedNow(){return s.input.hidden;}\r\n function damage(by){\r\n  if(s.phase==='dead')return;\r\n  if(protectedNow()){emit('block',{by,side:s.input.side});return;}\r\n  if(s.grace>0){emit('graze',{by});return;}\r\n  s.hp--;s.grace=SIM.grace;emit('hit',{by,hp:s.hp});\r\n  if(s.hp<=0){s.hp=0;s.phase='dead';s.deadAt=s.t;emit('dead',{levelNo:s.levelNo,kills:s.kills});}\r\n }\r\n function startLevel(n){s.levelNo=n;s.level=levels[n%levels.length];s.route=genRoute(s.level);s.dur=0;s.loop=Math.floor(n/levels.length);s.area=0;s.scared=false;s.phase='intro';s.timer=SIM.introS;s.enemies=[];s.projs=[];s.shots=[];\r\n  emit('level',{levelNo:n,id:s.level.id,loop:s.loop,intro:s.level.intro});}\r\n function makeEnemy(role,spec,delay=0){\r\n  const def=s.level.roles[role]||Object.values(s.level.roles)[0],enter=spec.enter||pick(r,def.enter||['jump']);\r\n  return {id:++s.id,role,def,x:spec.x??(r()*1.7-.85),z:def.walker?1:0,enter,enterT:0,enterDur:SIM.enterS,delay,hp:def.hp||1,state:'wait',timer:0,windup:0,windupDur:0,atk:null,civ:!!def.civilian,scare:false,flashed:false,shield:false,cloak:false,disg:!!def.transform,morph:0,fdir:0,hitT:0};\r\n }\r\n function spawnWave(){\r\n  const a=area(),list=a.wave.map(w=>({...w}));\r\n  // easy start: level 1 = 1/2/3/3 hostiles, later levels ramp slowly (cap 8 by loop 1); authored roles are truncated or extended\r\n  const isH=w=>!s.level.roles[w.role]?.civilian,hostile=list.filter(isH),want=Math.min(SIM.enemyCap,s.levelNo===0?SIM.wave1[s.area]??3:2+Math.ceil(s.levelNo*.6)+(s.area>=2?1:0)+s.loop*2);\r\n  for(let i=list.length-1,n=hostile.length;i>=0&&n>want;i--)if(isH(list[i])){list.splice(i,1);n--;}\r\n  for(let n=Math.min(hostile.length,want);n<want&&hostile.length;n++)list.push({role:pick(r,hostile).role});\r\n  list.slice(0,SIM.enemyCap+2).forEach((w,i)=>s.enemies.push(makeEnemy(w.role,w,i*.45)));\r\n  emit('action',{area:s.area,count:s.enemies.length});\r\n  if(s.area===SIM.scareArea&&!s.scared){\r\n   s.scared=true;const sc=s.level.scare||{},e=makeEnemy(sc.role||list[0].role,{x:0,enter:'drop'});e.scare=true;e.x=0;e.z=0;e.hp=1;e.delay=.25;e.enterDur=.3;e.scareText=sc.text||'BOO!';s.enemies.push(e);\r\n   emit('scare',{id:e.id,text:e.scareText});\r\n  }\r\n }\r\n // intro -> travel -> fight: the camera walks the route (cover-scene reads s.dur/s.timer); route[area] = segments arriving at that arena\r\n function beginTravel(){s.phase='travel';s.dur=s.timer=travelDur(s.route,s.area);emit('travel',{area:s.area,dur:s.dur,segs:s.route[s.area]});}\r\n function chooseAttack(e){\r\n  if(e.def.walker&&e.z<=0)return 'swipe';\r\n  if(e.scare)return 'swipe';\r\n  const mix=e.def.attack||s.level.attack.mix,keys=Object.keys(mix);let total=0;for(const k of keys)total+=mix[k];let x=r()*total;\r\n  for(const k of keys){x-=mix[k];if(x<=0)return k;}return keys[0];\r\n }\r\n function fireEnemy(e,bornAt=0){\n  const kind=e.atk;emit('fire',{id:e.id,kind});\r\n  if(INSTANT.has(kind)){damage(e.id);return;}\r\n  const dur=(SIM.flight[0]+r()*(SIM.flight[1]-SIM.flight[0]))/speedFor(s.loop);\r\n  const p={id:++s.id,kind,x:e.x,from:e.id,t:0,dur,shootable:SHOOTABLE.has(kind)};\n  s.projs.push(p);projectileBirths.set(p.id,bornAt);emit('flash',{id:e.id,kind});\n }\r\n function stepEnemy(e,dt){\r\n  if(e.state==='wait'){e.delay-=dt;if(e.delay<=0){e.state='enter';emit('enter',{id:e.id,how:e.enter});}return;}\r\n  if(e.state!=='dead'){const sh=e.def.shield,ck=e.def.cloak;e.shield=!!sh&&(s.t+e.id*.9)%(sh[0]+sh[1])<sh[0];e.cloak=!!ck&&(s.t+e.id*1.3)%(ck[0]+ck[1])<ck[0];}\r\n  if(e.state==='enter'){e.enterT+=dt;if(e.enterT>=e.enterDur){e.state='ready';e.timer=e.scare?0:.15+r()*.35;e.morph=e.disg?SIM.morphS:0;}return;}\r\n  if(e.state==='flee'){e.x+=e.fdir*dt*SIM.fleeSpeed;if(Math.abs(e.x)>1.9){e.state='dead';const xp=(e.def.xp||1)/2;s.xp+=xp;emit('flee',{id:e.id,xp});}return;}\r\n  if(e.disg&&e.state==='ready'&&e.morph>0){e.morph-=dt;if(e.morph<=0){e.disg=false;emit('transform',{id:e.id,prop:e.def.transform.prop});}return;}\r\n  if(e.civ||e.state==='dead')return;\r\n  if(e.def.walker&&e.z>0){e.z=Math.max(0,e.z-dt*e.def.walker);return;}\r\n  if(e.state==='ready'){e.timer-=dt;if(e.timer<=0){e.atk=chooseAttack(e);e.state='windup';e.windup=0;e.flashed=false;\r\n    const w=e.scare?SIM.scareWindup:(e.atk==='swipe'?Math.max(SIM.swipeWindup*.8,windupFor(s.loop)*.4):windupFor(s.loop))*(s.levelNo===0?SIM.firstWindup:1);e.windupDur=w;emit('windup',{id:e.id,kind:e.atk,dur:w});}return;}\r\n  if(e.state==='windup'){e.windup+=dt;\r\n   if(INSTANT.has(e.atk)&&!e.flashed&&e.windupDur-e.windup<=.35){e.flashed=true;emit('flash',{id:e.id,kind:e.atk});}\r\n   if(e.windup>=e.windupDur){fireEnemy(e,Math.max(0,dt-(e.windup-e.windupDur)));e.state='ready';const ev=e.def.every||[1.2,2.8],rate=s.level.attack.rate||1;e.timer=(ev[0]+r()*(ev[1]-ev[0]))/rate/Math.min(1.3,speedFor(s.loop));e.atk=null;}}\n }\r\n const alive=e=>e.state!=='dead';\r\n // shield blocks everything while up, cloak halves damage; returns false when blocked\r\n const hurt=(e,d)=>{if(e.shield)return false;e.hp-=e.cloak?d/2:d;return true;};\r\n function resolveShot(sh){\r\n  if(sh.kind==='proj'){const i=s.projs.findIndex(p=>p.id===sh.id);if(i<0){emit('miss',{});return;}const p=s.projs[i];s.projs.splice(i,1);emit('shotDown',{id:p.id,kind:p.kind,x:p.x});if(sh.power)shock(sh.lane??p.x,sh.power);return;}\r\n  const e=s.enemies.find(q=>q.id===sh.id&&alive(q));if(!e){emit('miss',{});return;}\r\n  if(e.civ){e.state='dead';emit('civilian',{id:e.id,xp:SIM.civilianPenalty});s.xp+=SIM.civilianPenalty;return;}\r\n  if(sh.power)shock(sh.lane??e.x,sh.power);\r\n  if(!hurt(e,sh.dmg||1)){emit('deflect',{id:e.id});return;}if(e.hp>0){emit('damaged',{id:e.id,hp:e.hp});return;}kill(e);\r\n }\r\n function kill(e){e.state='dead';s.kills++;const xp=(e.def.xp||1)*(e.scare?2:1);s.xp+=xp;emit('kill',{id:e.id,role:e.role,x:e.x,xp,scare:e.scare});}\r\n // Punch shockwave: clears shootable projectiles near the lane.\r\n function shock(x,power){emit('shockwave',{x,power});s.projs=s.projs.filter(p=>{if(!p.shootable||Math.abs(p.x-x)>SIM.shockLane)return true;emit('shotDown',{id:p.id,kind:p.kind,x:p.x});return false;});}\r\n // Hold beam: while on (popped up, in a fight) burns the hostile nearest the aim lane at beamDps and shootable projectiles crossing the lane.\r\n function stepBeam(dt){\r\n  const b=s.beam,on=req.on&&s.phase==='fight'&&!s.input.hidden;b.on=on;b.x=req.x;b.target=null;if(!on)return;\r\n  let best=null;for(const e of s.enemies)if(alive(e)&&!e.civ&&(e.state==='ready'||e.state==='windup'||e.state==='enter')&&(!best||Math.abs(e.x-b.x)<Math.abs(best.x-b.x)))best=e;\r\n  s.projs=s.projs.filter(p=>{if(!p.shootable||Math.abs(p.x-b.x)>SIM.beamLane)return true;emit('shotDown',{id:p.id,kind:p.kind,x:p.x});return false;});\r\n  if(!best)return;b.target=best.id;best.hp-=SIM.beamDps*dt;beamHitT-=dt;\r\n  if(best.hp<=0)kill(best);else if(beamHitT<=0){beamHitT=SIM.beamHitS;emit('beamHit',{id:best.id,hp:best.hp,x:best.x});}\r\n }\r\n // ARM ZONES: the view feeds {left,middle,right,up} 0..1 from arm speed; every hostile in a lit zone takes damage (flyers only via up). Popped up in a fight only.\r\n const zoneOf=e=>e.def.flyer?'up':e.x<-SIM.third?'left':e.x>SIM.third?'right':'middle',hot=e=>alive(e)&&!e.civ&&e.state!=='wait';\r\n function stepZones(dt){\r\n  const z=s.zones,b=s.beam;if(s.t-zT>SIM.zoneHold||s.phase!=='fight'||s.input.hidden){for(const k in z)z[k]=0;return;}\r\n  let top=null;for(const k in z)if(z[k]>(top?z[top]:0))top=k;if(!top)return;\r\n  b.on=true;b.zone=top;b.x=top==='left'?-.6:top==='right'?.6:0;\r\n  for(const e of s.enemies){if(!hot(e))continue;const i=z[zoneOf(e)];if(!i)continue;if(zoneOf(e)===top&&b.target==null)b.target=e.id;\r\n   e.hitT-=dt;if(!hurt(e,(SIM.zoneDps[0]+(SIM.zoneDps[1]-SIM.zoneDps[0])*i)*dt)){if(e.hitT<=0){e.hitT=SIM.beamHitS;emit('deflect',{id:e.id});}continue;}\r\n   if(e.hp<=0){kill(e);continue;}if(e.hitT<=0){e.hitT=SIM.beamHitS;emit('beamHit',{id:e.id,hp:e.hp,x:e.x});}}\r\n  s.projs=s.projs.filter(p=>{if(!p.shootable||z[p.x<-SIM.third?'left':p.x>SIM.third?'right':'middle']<.3)return true;emit('shotDown',{id:p.id,kind:p.kind,x:p.x});return false;});\r\n }\r\n function serializeState(){\r\n  const body=JSON.parse(JSON.stringify(s,(key,value)=>key==='level'||key==='def'?undefined:value));\r\n  body.levelId=s.level.id;\r\n  return body;\r\n }\r\n function load(saved){\r\n  if(!saved||saved.version!==1||!saved.s||typeof saved.s!=='object'||Array.isArray(saved.s))throw new TypeError('invalid cover snapshot');\r\n  if(!Number.isInteger(saved.rng)||saved.rng<-2147483648||saved.rng>2147483647)throw new RangeError('invalid cover rng');\r\n  const within=(n,lo,hi)=>typeof n==='number'&&Number.isFinite(n)&&n>=lo-1e-9&&n<=hi+1e-9;\r\n  const body=JSON.parse(JSON.stringify(saved.s));\r\n  if(!Number.isInteger(body.levelNo)||body.levelNo<0||body.levelNo>100000)throw new RangeError('invalid cover level');\r\n  const level=levels[body.levelNo%levels.length];\r\n  if(!level||level.id!==body.levelId)throw new RangeError('cover snapshot level is not in this catalog');\r\n  if(!['intro','travel','fight','portal','dead'].includes(body.phase))throw new RangeError('invalid cover phase');\r\n  if(!Number.isInteger(body.area)||body.area<0||body.area>SIM.areas)throw new RangeError('invalid cover area');\r\n  if(body.phase==='portal'&&body.area<SIM.areas)throw new RangeError('portal snapshot is not a finished wave set');\r\n  if(body.phase!=='portal'&&body.area>=SIM.areas)throw new RangeError('invalid cover area');\r\n  if(body.loop!==Math.floor(body.levelNo/levels.length))throw new RangeError('invalid cover level');\r\n  const route=genRoute(level);\r\n  if(JSON.stringify(body.route)!==JSON.stringify(route))throw new RangeError('invalid cover distance');\r\n  const travelAt=body.phase==='portal'?SIM.areas-1:body.area;\r\n  const distance=body.phase==='intro'?0:travelDur(route,travelAt);\r\n  if(typeof body.dur!=='number'||!Number.isFinite(body.dur)||Math.abs(body.dur-distance)>1e-9)throw new RangeError('invalid cover distance');\r\n  if(!within(body.t,0,(body.levelNo+1)*10000))throw new RangeError('invalid cover t');\r\n  if(!Number.isInteger(body.hp)||body.hp<0||body.hp>SIM.hp||(body.phase==='dead'?body.hp!==0:body.hp<1))throw new RangeError('invalid cover hp');\r\n  if(body.phase==='intro'?!within(body.timer,0,SIM.introS):body.phase==='travel'?!within(body.timer,0,distance):!within(body.timer,-distance,0))throw new RangeError('invalid cover timer');\r\n  if(!within(body.grace,0,Math.max(SIM.grace,SIM.popGrace))||!within(body.deadAt,0,body.t)||typeof body.scared!=='boolean')throw new RangeError('invalid cover state');\r\n  if('hold' in body&&typeof body.hold!=='boolean')throw new RangeError('invalid cover state');\r\n  if(!Number.isInteger(body.id)||body.id<0||body.id>(body.levelNo+1)*100000)throw new RangeError('invalid cover state');\r\n  const slots=(body.levelNo+1)*(SIM.enemyCap+3)*SIM.areas;\r\n  let xpHi=1;for(const role of Object.values(level.roles))xpHi=Math.max(xpHi,role.xp||1);\r\n  if(!Number.isInteger(body.kills)||body.kills<0||body.kills>slots||!within(body.xp,SIM.civilianPenalty*slots,xpHi*2*slots))throw new RangeError('invalid cover state');\r\n  if(!within(saved.beamHitT,-SIM.windup,SIM.beamHitS))throw new RangeError('invalid cover beamHitT');\r\n  if(saved.zT!==-1e9&&!within(saved.zT,0,body.t))throw new RangeError('invalid cover zT');\r\n  if(!saved.req||typeof saved.req.on!=='boolean'||!within(saved.req.x,-1,1))throw new RangeError('invalid cover req');\r\n  if(!body.input||typeof body.input.hidden!=='boolean'||(body.input.side!==-1&&body.input.side!==0&&body.input.side!==1))throw new RangeError('invalid cover state');\r\n  if(!body.beam||typeof body.beam.on!=='boolean'||!Number.isFinite(body.beam.x))throw new RangeError('invalid cover state');\r\n  if(!(body.beam.target==null||(Number.isInteger(body.beam.target)&&body.beam.target>=0&&body.beam.target<=body.id)))throw new RangeError('invalid cover state');\r\n  if(!(body.beam.zone==null||body.beam.zone==='left'||body.beam.zone==='middle'||body.beam.zone==='right'||body.beam.zone==='up'))throw new RangeError('invalid cover state');\r\n  const zoneKeys=body.zones&&typeof body.zones==='object'?Object.keys(body.zones):[];\r\n  if(zoneKeys.length!==4||zoneKeys.some(k=>!['left','middle','right','up'].includes(k))||zoneKeys.some(k=>!within(body.zones[k],0,1)))throw new RangeError('invalid cover state');\r\n  if(!Array.isArray(body.enemies)||body.enemies.length>SIM.enemyCap+3||!Array.isArray(body.projs)||body.projs.length>(SIM.enemyCap+3)*2||!Array.isArray(body.shots)||body.shots.length>(SIM.enemyCap+3)*8)throw new RangeError('invalid cover state');\r\n  const xHi=1.9+SIM.fleeSpeed,delayHi=(SIM.enemyCap+2)*.45,windHi=SIM.windup*SIM.firstWindup+SIM.windup,hpLo=-Math.max(SIM.punchDmg,SIM.zoneDps[1],SIM.beamDps);\r\n  let timerHi=.5;for(const role of Object.values(level.roles)){const ev=role.every||[1.2,2.8];timerHi=Math.max(timerHi,ev[0],ev[1]);}\r\n  timerHi/=level.attack&&level.attack.rate>0?level.attack.rate:1;\r\n  for(const e of body.enemies){\r\n   if(!e||typeof e.role!=='string'||!level.roles[e.role])throw new RangeError('cover snapshot enemy is not in the catalog');\r\n   if(!within(e.x,-xHi,xHi))throw new RangeError('invalid cover coord');\r\n   if(!within(e.z,0,1))throw new RangeError('invalid cover distance');\r\n   const cap=level.roles[e.role].hp||1;\r\n   if(!within(e.hp,hpLo,cap)||(e.state==='dead'?e.hp>0:!(e.hp>0)))throw new RangeError('invalid cover hp');\r\n   if(!['wait','enter','ready','windup','flee','dead'].includes(e.state))throw new RangeError('invalid cover state');\r\n   if(!['jump','slide','drop','rise'].includes(e.enter))throw new RangeError('invalid cover state');\r\n   if(!within(e.delay,-delayHi,delayHi)||!within(e.timer,-timerHi,timerHi)||!within(e.windup,0,windHi)||!within(e.windupDur,0,SIM.windup*SIM.firstWindup))throw new RangeError('invalid cover timer');\r\n   if(!within(e.enterT,0,SIM.enterS+SIM.windup)||!within(e.enterDur,0,SIM.enterS)||!within(e.morph,-SIM.morphS,SIM.morphS)||!within(e.hitT,-SIM.beamHitS,SIM.beamHitS))throw new RangeError('invalid cover timer');\r\n   if(e.fdir!==-1&&e.fdir!==0&&e.fdir!==1)throw new RangeError('invalid cover state');\r\n   if(!Number.isInteger(e.id)||e.id<0||e.id>body.id)throw new RangeError('invalid cover state');\r\n   if(typeof e.civ!=='boolean'||typeof e.scare!=='boolean'||typeof e.flashed!=='boolean'||typeof e.shield!=='boolean'||typeof e.cloak!=='boolean'||typeof e.disg!=='boolean')throw new RangeError('invalid cover state');\r\n   if(e.atk!=null){const mix=level.roles[e.role].attack||level.attack&&level.attack.mix||{};if(e.atk!=='swipe'&&!Object.prototype.hasOwnProperty.call(mix,e.atk))throw new RangeError('invalid cover state');}\r\n  }\r\n  const durLo=SIM.flight[0]/SIM.speedCap,durHi=SIM.flight[1];\r\n  for(const p of body.projs){\r\n   if(!p||!SHOOTABLE.has(p.kind)||p.shootable!==true||!Number.isInteger(p.id)||p.id<0||p.id>body.id||!Number.isInteger(p.from)||p.from<0||p.from>body.id)throw new RangeError('invalid cover state');\r\n   if(!within(p.x,-xHi,xHi))throw new RangeError('invalid cover coord');\r\n   if(!within(p.dur,durLo,durHi)||!within(p.t,0,p.dur))throw new RangeError('invalid cover timer');\r\n  }\r\n  for(const sh of body.shots){\r\n   if(!sh||(sh.kind!=='enemy'&&sh.kind!=='proj')||sh.dur!==SIM.shotS||!(sh.dmg===1||sh.dmg===SIM.punchDmg)||!within(sh.power,0,1e6))throw new RangeError('invalid cover state');\r\n   if(!Number.isInteger(sh.id)||sh.id<0||sh.id>body.id||!Number.isInteger(sh.sid)||sh.sid<0||sh.sid>body.id)throw new RangeError('invalid cover state');\r\n   if(!within(sh.lane,-xHi,xHi))throw new RangeError('invalid cover coord');\r\n   if(!within(sh.t,0,SIM.shotS))throw new RangeError('invalid cover timer');\r\n  }\r\n  r.load(saved.rng);\r\n  beamHitT=saved.beamHitT;zT=saved.zT;req.on=saved.req.on;req.x=saved.req.x;\r\n  delete body.levelId;\r\n  for(const key of Object.keys(s))delete s[key];\r\n  Object.assign(s,body);s.level=level;\r\n  for(const enemy of s.enemies)enemy.def=level.roles[enemy.role];\r\n  events.splice(0);\r\n }\r\n startLevel(0);\r\n return {s,events,SIM,protectedNow,load,\r\n  snapshot(){return {version:1,rng:r.state(),beamHitT,zT,req:{on:req.on,x:req.x},s:serializeState()};},\r\n  // Arm zones from the pose: intensities 0..1; stale after zoneHold s without a call.\r\n  setZones(zn={}){zT=s.t;for(const k in s.zones)s.zones[k]=Math.max(0,Math.min(1,+zn[k]||0));},\r\n  // Hold beam from the pose: {on,x} with x = wrist lane -1..1. Works only popped up in a fight (stepBeam).\r\n  setBeam({on=false,x=0}={}){req.on=!!on;req.x=x;if(!on)s.beam.on=false;},\r\n  setInput({side=0,hidden=false}={}){if(s.input.hidden&&!hidden)s.grace=Math.max(s.grace,SIM.popGrace);s.input.side=side;s.input.hidden=!!hidden;},\r\n  // aimX = screen lane -1..1 of the wrist; null = auto-lock the most urgent target (keyboard). Only works while popped up in a fight.\r\n  // power>0 = a real punch: big damage (punchDmg) + a shockwave that clears projectiles near the lane; 0 = plain shot (keyboard/touch).\r\n  fire(aimX=null,power=0){\r\n   if(s.phase!=='fight'||s.input.hidden)return null;\r\n   const c=[];for(const e of s.enemies)if(alive(e)&&!e.def.flyer&&(e.state==='ready'||e.state==='windup'||e.state==='enter'))c.push({kind:'enemy',id:e.id,x:e.x,u:e.state==='windup'?e.windup/e.windupDur:-1,civ:e.civ});\r\n   for(const p of s.projs)if(p.shootable)c.push({kind:'proj',id:p.id,x:p.x,u:p.t/p.dur+1});\r\n   if(!c.length)return null;\r\n   // keyboard auto-lock never picks an intern; a real wrist aim can (the nearest target wins, hostile on a tie)\r\n   const hostile=c.filter(q=>!q.civ),pool=aimX==null&&hostile.length?hostile:c;\r\n   const t=aimX==null?pool.reduce((a,b)=>b.u>a.u?b:a):pool.reduce((a,b)=>{const da=Math.abs(a.x-aimX)+(a.civ?.001:0),db=Math.abs(b.x-aimX)+(b.civ?.001:0);return db<da?b:a;});\r\n   const sh={id:t.id,kind:t.kind,t:0,dur:SIM.shotS,sid:++s.id,dmg:power>0?SIM.punchDmg:1,power,lane:t.x};s.shots.push(sh);events.push({type:'shot',t:s.t,sid:sh.sid,target:t.id,kind:t.kind,x:t.x,power});if(power>0)emit('punch',{id:t.id,target:t.id,kind:t.kind,x:t.x,power});return sh;\r\n  },\r\n  step(dt){\r\n   if(s.phase==='dead'){s.t+=dt;s.beam.on=false;for(const k in s.zones)s.zones[k]=0;return;}\r\n   if(s.hold&&s.phase==='intro'){s.t+=dt;return;} // view holds the intro clock until the portal/scene is revealed\r\n   s.t+=dt;if(s.phase!=='fight'){s.beam.on=false;for(const k in s.zones)s.zones[k]=0;}s.grace=Math.max(0,s.grace-dt);\r\n   if(s.phase==='intro'||s.phase==='travel'){s.timer-=dt;if(s.timer<=0){if(s.phase==='intro')beginTravel();else{s.phase='fight';spawnWave();}}return;} // no attacks, no damage while travelling\r\n   if(s.phase==='portal')return;\r\n   {const h=s.enemies.filter(e=>alive(e)&&!e.civ);if(h.length===1&&h[0].def.flee&&!h[0].scare&&(h[0].state==='ready'||h[0].state==='windup')){h[0].state='flee';h[0].fdir=h[0].x<0?-1:1;h[0].atk=null;emit('panic',{id:h[0].id});}}\r\n   projectileBirths.clear();\n   for(const e of s.enemies)stepEnemy(e,dt);\n   stepBeam(dt);stepZones(dt);\r\n   const due=[];\n   for(const p of s.projs){const bornAt=projectileBirths.get(p.id)||0,at=bornAt+p.dur-p.t;p.t+=Math.max(0,dt-bornAt);if(at<=dt+1e-9)due.push({at,order:1,p});}\n   for(const sh of s.shots){const at=sh.dur-sh.t;sh.t+=dt;if(at<=dt+1e-9)due.push({at,order:0,sh});}\n   // An earlier interception removes its target before arrival; a tie favors the player's shot.\n   due.sort((a,b)=>Math.abs(a.at-b.at)<1e-9?a.order-b.order:a.at-b.at);\n   for(const event of due){if(event.sh){event.sh.done=true;resolveShot(event.sh);}else if(s.projs.includes(event.p)){\n    s.projs.splice(s.projs.indexOf(event.p),1);damage('proj:'+event.p.kind);\n   }}\n   s.shots=s.shots.filter(x=>!x.done);\r\n   if(s.phase==='dead')return;\r\n   s.enemies=s.enemies.filter(e=>alive(e)||s.shots.some(x=>x.id===e.id));\r\n   if(s.phase==='fight'&&!s.enemies.some(e=>alive(e)&&!e.civ)){\r\n    s.enemies=[];s.projs=[];emit('areaClear',{area:s.area});s.area++;\r\n    if(s.area>=SIM.areas){s.phase='portal';const nxt=levels[(s.levelNo+1)%levels.length];emit('portal',{from:s.level.id,next:nxt.id,nextIntro:nxt.intro,levelNo:s.levelNo+1});}\r\n    else beginTravel();\r\n   }\r\n  },\r\n  // The view calls this inside the portal's swap(): next level, +1 HP (max 10).\r\n  completePortal(){if(s.phase!=='portal')return false;s.hp=Math.min(SIM.hp,s.hp+SIM.portalHeal);startLevel(s.levelNo+1);return true;},\r\n  drain(){return events.splice(0);},\r\n };\r\n}\r\nexport function restoreCoverSim(saved,{levels=LEVELS}={}){\r\n const sim=createCoverSim({levels,seed:1});\r\n sim.load(saved);\r\n return sim;\r\n}\r\n","arcade/cover/cover.mjs":"import {gameShellPhase} from '../shell-phase.mjs';\r\nimport {visualCue,setVisualCue,symbolMarkup,VISUAL_CSS} from '../visual-language.mjs';\r\n// CROUCHING CRISIS: a motion-tracked Time Crisis style cover shooter over the app's shared pose stream (no camera or XP timer of its own).\r\n// mountCover({host,Session,sound,onXp,onCaught,onActive,seed}) -> {pose(detail),stop(),sim,scene,key(k,down)} like mountRunner.\r\nimport {CSS} from '../runner/worlds/host-style.mjs';\r\nimport {createCoverSim} from './cover-sim.mjs';\r\nimport {createCoverInput} from './cover-input.mjs';\r\nimport {createCoverScene} from './cover-scene.mjs';\r\nimport {mountFruit} from './cover-fruit.mjs';\r\nimport {createCoverSfx} from './cover-sfx.mjs';\r\nimport {createBank} from './cover-bank.mjs';\r\nimport {LEVELS} from './levels/index.mjs';\r\nconst EXTRA=`#cameraWorkout .cc-zones i.on .game-symbol{stroke-width:7;transform:scale(1.12)}#cameraWorkout .cc-zones .game-symbol{width:14px;height:14px}#cameraWorkout .cc-info .game-symbol{width:22px;height:22px}#cameraWorkout .cc-chip .game-symbol{width:26px;height:26px}#cameraWorkout .cc-chip{top:calc(env(safe-area-inset-top,0px) + 30px)}#cameraWorkout .cc-hint .game-cue-row .game-symbol{width:34px;height:34px}#cameraWorkout .cc-hint .game-cue-row>.game-cue{padding:7px}#cameraWorkout .cc-tag .game-symbol{width:58px;height:58px}#cameraWorkout .cc-hud{position:absolute;inset:0;z-index:4;pointer-events:none;overflow:hidden;font:800 13px system-ui,sans-serif;color:#fff;text-shadow:0 2px 5px #261736}\r\n#cameraWorkout .cc-hp{position:absolute;top:calc(env(safe-area-inset-top,0px) + 10px);left:10px;right:10px;display:flex;gap:3px}\r\n#cameraWorkout .cc-hp i{flex:1;height:12px;border-radius:3px;background:#ff3a2a;border:2px solid #2a0d10;box-shadow:0 0 6px #ff3a2a99;transition:background .15s,box-shadow .15s}\r\n#cameraWorkout .cc-hp i.off{background:#2a1a2c;box-shadow:none;opacity:.6}\r\n#cameraWorkout .cc-info{position:absolute;top:calc(env(safe-area-inset-top,0px) + 38px);left:10px;right:10px;display:flex;justify-content:space-between;align-items:flex-start;gap:8px}\r\n#cameraWorkout .cc-info>span{display:flex;align-items:center;gap:6px;min-height:30px;padding:3px 8px;border:1px solid #ffffff38;border-radius:9px;background:linear-gradient(110deg,#100f1bc9,#171322a8);box-shadow:0 4px 12px #0005;backdrop-filter:blur(5px)}\r\n#cameraWorkout .cc-info>span:first-child{margin-left:auto;margin-right:76px;max-width:min(65vw,280px);justify-content:flex-end;text-align:right;font-size:10px;letter-spacing:.04em}\r\n#cameraWorkout .cc-info>span:last-child{position:absolute;top:34px;left:0;right:auto;font-size:9px;background:#100f1ba8}\r\n#cameraWorkout .cc-info>span:first-child .game-cue{min-width:0;max-width:100%}#cameraWorkout .cc-info>span:first-child .game-cue b{overflow:hidden;max-width:min(45vw,190px);font-size:10px;text-overflow:ellipsis;white-space:nowrap}#cameraWorkout .cc-info>span:last-child{gap:5px}#cameraWorkout .cc-info>span:last-child small{color:#dce7ff;font-size:8px;font-weight:800;white-space:nowrap}#cameraWorkout .cc-info>span:last-child small:nth-child(2){color:#ffe28d;font-size:7px;letter-spacing:.04em}#cameraWorkout .cc-info>span:last-child .game-symbol{width:15px;height:15px}#cameraWorkout .cc-info>span:last-child .game-cue{gap:3px}#cameraWorkout .cc-info>span:last-child .game-cue b{font-size:11px}\r\n#cameraWorkout .cc-rings{position:absolute;inset:0;width:100%;height:100%}\r\n#cameraWorkout .cc-zones{position:absolute;left:10px;right:10px;top:calc(env(safe-area-inset-top,0px) + 142px);display:grid;grid-template-columns:1fr 1fr 1fr;grid-template-rows:16px 18px;gap:4px}\r\n#cameraWorkout .cc-zones i{border-radius:6px;border:2px solid #ffffff55;background:#ffffff14;font:900 9px/14px system-ui;text-align:center;color:#fff9;transition:background .08s,box-shadow .08s,opacity .08s}\r\n#cameraWorkout .cc-zones i.on{border-color:#fff;color:#fff}\r\n#cameraWorkout .cc-zones i[data-z=up]{grid-column:1/4}\r\n#cameraWorkout .cc-chip{position:absolute;top:calc(env(safe-area-inset-top,0px) + 30px);left:50%;transform:translateX(-50%);padding:2px 10px;border-radius:10px;font:900 11px system-ui;letter-spacing:.06em;border:2px solid;transition:background .15s,border-color .15s}\r\n#cameraWorkout .cc-chip.in{background:#0c3a1acc;border-color:#3dff7a;color:#b8ffd0}\r\n#cameraWorkout .cc-chip.out{background:#4a0c14cc;border-color:#ff3a4a;color:#ffc0c6}\r\n#cameraWorkout .cc-chip[hidden]{display:none}\r\n#cameraWorkout .cc-exit{position:absolute;inset:0;opacity:0;transition:opacity .2s}\r\n#cameraWorkout .cc-exit.on{opacity:1}\r\n#cameraWorkout .cc-exit.left{box-shadow:inset 46px 0 40px -20px #3dff7a99}\r\n#cameraWorkout .cc-exit.right{box-shadow:inset -46px 0 40px -20px #3dff7a99}\r\n#cameraWorkout .cc-exit.bottom{box-shadow:inset 0 -46px 40px -20px #3dff7a99}\r\n#cameraWorkout .cc-center{position:absolute;left:8px;right:8px;top:30%;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center}\r\n#cameraWorkout .cc-center canvas{max-width:100%;height:auto!important;filter:drop-shadow(0 4px 6px #000a)}\r\n#cameraWorkout .cc-center.pop{animation:runner-pop .42s cubic-bezier(.3,1.6,.5,1) both}\r\n#cameraWorkout .cc-center p{margin:0;font:800 16px/1.25 system-ui;max-width:300px}\r\n#cameraWorkout .cc-hint{position:absolute;left:8px;right:8px;bottom:calc(max(6px,env(safe-area-inset-bottom)) + 66px);text-align:center;font-size:12px;opacity:.9}\r\n#cameraWorkout .cc-tag{position:absolute;left:0;right:0;top:48%;text-align:center;font:900 28px system-ui;color:#ffe9a0;-webkit-text-stroke:2px #3a1a4a;opacity:0}\r\n#cameraWorkout .cc-tag.on{animation:cc-tag .8s ease-out}\r\n@keyframes cc-tag{0%{opacity:0;transform:scale(.6)}15%{opacity:1;transform:scale(1.1)}100%{opacity:0;transform:translateY(-30px)}}\r\n#cameraWorkout .cc-over{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#40000c99;font:900 clamp(34px,11vw,60px)/1 system-ui;text-align:center;-webkit-text-stroke:2px #ff0030}\r\n#cameraWorkout .cc-over[hidden]{display:none}\r\n@media(prefers-reduced-motion:reduce){#cameraWorkout .cc-chip,#cameraWorkout .cc-exit{transition:none}#cameraWorkout .cc-tag.on,#cameraWorkout .cc-center.pop{animation:none;opacity:1}}`;\r\nlet portalFn=null;\r\nasync function playPortal(opts){\r\n if(!portalFn)portalFn=import('../game-portal.mjs').then(m=>m.playGamePortal).catch(()=>import('./portal-fallback.mjs').then(m=>m.playGamePortal));\r\n return (await portalFn)(opts);\r\n}\r\nexport function mountCover({host,Session,sound=()=>{},audio=null,onXp=()=>{},onCaught=()=>{},onActive=()=>{},reveal=null,seed=Date.now()>>>0}={}){\r\n const shell=gameShellPhase('cover',host);\r\n const el=(tag,cls)=>{const e=document.createElement(tag);if(cls)e.className=cls;return e;};\r\n const style=el('style');style.textContent=CSS+EXTRA+VISUAL_CSS;document.head.append(style);\r\n const layer=el('div','runner-layer cover-layer'),canvas=el('canvas','runner-view');layer.setAttribute('aria-hidden','true');layer.append(canvas);\r\n const hud=el('div','cc-hud'),hp=el('div','cc-hp'),info=el('div','cc-info'),rings=el('canvas','cc-rings'),zonesEl=el('div','cc-zones'),center=el('div','cc-center'),hint=el('div','cc-hint'),tag=el('div','cc-tag'),chip=el('div','cc-chip'),exitGlow=el('div','cc-exit'),over=el('div','cc-over');\r\n hp.setAttribute('role','meter');hp.setAttribute('aria-valuemin','0');hp.setAttribute('aria-valuemax','10');const segs=Array.from({length:10},()=>hp.appendChild(el('i')));info.append(el('span'),el('span'));over.hidden=true;over.append(visualCue('caught','Caught by MOM. Run ended.'));over.firstChild.classList.add('game-status-symbol');over.setAttribute('role','alert');\r\n const zoneEls=Object.fromEntries([['up','UP'],['left','LEFT'],['middle','MIDDLE'],['right','RIGHT']].map(([k,t])=>{const i=el('i');i.dataset.z=k;i.title=t;i.setAttribute('role','img');i.setAttribute('aria-label',t);i.innerHTML=symbolMarkup(k);zonesEl.append(i);return [k,i];}));\r\n hud.append(rings,zonesEl,exitGlow,chip,hp,info,center,tag,hint,over);host.prepend(layer);host.append(hud);host.classList.add('runner-on');\r\n const sim=createCoverSim({levels:LEVELS,seed}),scene=createCoverScene(canvas),input=createCoverInput(),rc=rings.getContext('2d');\r\n scene.setLevel(sim.s.level);sim.s.hold=true; // the intro card/timer starts after the reveal: `reveal` promise from the app if given, else the first rendered frame\r\n let firstFrame=null,poseT=0,disposed=false,raf=0,last=0,pose={visible:false,hidden:false,side:0,calibrated:false},keys={left:false,right:false},wantFire=0,activeAt=0,pulseAt=0,portalBusy=false,deadTimer=0,hintT=0,centerT=0,tagT=0,lastShift=0;\r\n const touch={left:false,right:false},abort=new AbortController(); // stop() aborts the in-flight portal (navigation lane cancels it; the swap is also a no-op once disposed)\r\n const bank=createBank(onXp),credit=bank.credit;\r\n function say(node,ms=1400,pop=true){clearTimeout(centerT);center.replaceChildren(...[].concat(node));center.classList.remove('pop');void center.offsetWidth;if(pop)center.classList.add('pop');center.hidden=false;if(ms)centerT=setTimeout(()=>center.replaceChildren(),ms);}\r\n const levelIcon=level=>({crisis:'gate',ps2:'machine',judgment:'flame',mansion:'mansion',halo:'ring',kitchen:'kitchen'})[level?.id]||'gate';\r\n const card=(kind,label,detail='')=>{const n=visualCue(kind,label,detail);n.classList.add('game-status-symbol');return n;};\r\n function hints(calibrating=false){const key=String(calibrating);if(hint.dataset.cue===key&&hint.childNodes.length)return;hint.dataset.cue=key;const row=el('div','game-cue-row');if(calibrating)row.append(visualCue('tracking','Stand tall in view while calibrating'));else row.append(visualCue('hide','Step out of frame, or lunge sideways with a knee bend, to take cover'),visualCue('punch','Swing or punch toward the target'),visualCue('up','Raise your attack for flying targets'));hint.replaceChildren(row);}\r\n const textp=t=>{const p=document.createElement('p');p.textContent=t;return p;};\r\n function tagPop(text){clearTimeout(tagT);const combo=/^COMBO x(\\d+)/.exec(text),cloak=text==='CLOAK HIT · ½';setVisualCue(tag,cloak?'impact':combo?'fruit':({'POW!':'punch','SHOT DOWN':'target','INTERN! -XP':'penalty','AREA CLEAR':'check','BLOCKED':'shield','SLICED!':'fruit'})[text]||'star',text,cloak?'½':combo?`×${combo[1]}`:'');tag.classList.remove('on');void tag.offsetWidth;tag.classList.add('on');}\r\n const fruit=mountFruit({host,scene,credit,tagPop,sound,seed});\r\n const sfx=createCoverSfx({audio}),snd=k=>{if(!audio)sound(k);}; // synthesized cover sounds replace the old samples when the app passes its audio graph\r\n function handle(ev){\r\n  sfx.event(ev,sim.s);\r\n  switch(ev.type){\r\n   case 'level':say(card(levelIcon(sim.s.level),`Level ${ev.levelNo+1}. ${ev.intro.title}. ${ev.intro.line}`,String(ev.levelNo+1)),0);hintT=4;break;\r\n   case 'travel':center.replaceChildren();if(sim.s.level.stations?.[ev.area])say(card(levelIcon(sim.s.level),sim.s.level.stations[ev.area],`${ev.area+1}/4`),1300,false);break;\r\n   case 'action':say(card('punch','Action. Swing or punch toward enemies.'),1100);snd('lever');break;\r\n   case 'scare':scene.shake(.7);say(card('warning',ev.text),900);snd('crt');break;\r\n   case 'flash':snd('crt');break;\r\n   case 'beamHit':scene.hitAt(ev.id);break;\r\n   case 'damaged':{const cloaked=sim.s.enemies.find(e=>e.id===ev.id)?.cloak;if(cloaked){scene.hitAt(ev.id,'#bbaaff');tagPop('CLOAK HIT · ½');}else scene.hitAt(ev.id);break;}\r\n   case 'deflect':scene.hitAt(ev.id,'#4fe8ff');break;\r\n   case 'fire':snd('switch');break;\r\n   case 'punch':scene.punchAt(ev.id??ev.target,ev.power||1);tagPop('POW!');snd('main-control');break;\r\n   case 'shot':snd('wood');break;\r\n   case 'kill':credit(ev.xp);snd('dial');break;\r\n   case 'shotDown':tagPop('SHOT DOWN');snd('dial');break;\r\n   case 'civilian':credit(ev.xp);tagPop('INTERN! -XP');snd('jelly');break;\r\n   case 'areaClear':tagPop('AREA CLEAR');fruit.burst();break;\r\n   case 'block':scene.explode(scene.coverPoint(ev.side??sim.s.input.side??0),'#ffd36a');tagPop('BLOCKED');snd('ice');break;\r\n   case 'hit':scene.shake(.45);scene.explode(scene.coverPoint(0),'#ffb040');navigator.vibrate?.(40);snd('jelly');break;\r\n   case 'dead':over.hidden=false;deadTimer=2.8;break;\r\n   case 'portal':runPortal(ev);break;\r\n  }\r\n }\r\n async function runPortal(ev){\r\n  if(portalBusy||disposed)return;portalBusy=true;const nxt=ev.nextIntro||{title:'NEXT',line:''};\r\n  const swapIn=async()=>{if(disposed||abort.signal.aborted||sim.s.phase!=='portal')return;sim.completePortal();sim.s.hold=true;scene.setLevel(sim.s.level);const glbs=scene.warm(sim.s.level); // resolve once the new level has a rendered frame (and its first-wave GLBs, capped 1.5 s) so the reveal never shows a half-built scene\r\n   await Promise.race([glbs,new Promise(r=>setTimeout(r,1500))]);if(disposed)return;await new Promise(r=>{firstFrame=r;});}; // intro clock waits for the reveal (hold)\r\n  try{await playPortal({host,label:`LEVEL ${ev.levelNo+1} - ${nxt.title}`,swap:swapIn,signal:abort.signal});}\r\n  catch(e){console.warn('Crouching Crisis portal',e);if(!disposed&&sim.s.phase==='portal'){sim.completePortal();scene.setLevel(sim.s.level);}}\r\n  portalBusy=false;if(!disposed)sim.s.hold=false; // portal fully revealed: the full intro starts now\r\n }\r\n function readInput(){\r\n  const kb=keys.left||touch.left?-1:keys.right||touch.right?1:0;\r\n  if(kb)sim.setInput({side:kb,hidden:true});else sim.setInput({side:pose.side||0,hidden:!!pose.hidden});sim.setBeam({on:false});sim.setZones(kb||pose.hidden||performance.now()-poseT>250?{}:pose.zones||{}); // zones expire 250 ms after the last pose frame\r\n }\r\n function drawRings(){\r\n  const w=rings.clientWidth||innerWidth,h=rings.clientHeight||innerHeight;if(rings.width!==w||rings.height!==h){rings.width=w;rings.height=h;}rc.clearRect(0,0,w,h);\r\n  for(const e of sim.s.enemies){\r\n   if(e.state!=='windup'||e.civ)continue;const p=scene.enemyScreen(e.id,{});if(!p||!p.ok)continue;\r\n   const u=Math.min(1,e.windup/e.windupDur),r=Math.max(14,p.h*h*(.2+(1-u)*.3)),cx=p.x*w,cy=p.y*h,hue=Math.round(120*(1-u));\r\n   rc.lineWidth=4;rc.strokeStyle=`hsl(${hue} 100% 55%)`;rc.shadowColor=rc.strokeStyle;rc.shadowBlur=8;rc.beginPath();rc.arc(cx,cy,r,0,Math.PI*2);rc.stroke();\r\n   rc.lineWidth=3;for(let i=0;i<4;i++){const a=i*Math.PI/2;rc.beginPath();rc.moveTo(cx+Math.cos(a)*(r-6),cy+Math.sin(a)*(r-6));rc.lineTo(cx+Math.cos(a)*(r+7),cy+Math.sin(a)*(r+7));rc.stroke();}\r\n   if(e.atk==='laser'){rc.beginPath();rc.moveTo(cx,cy);rc.lineTo(w/2,h*.9);rc.setLineDash([6,6]);rc.stroke();rc.setLineDash([]);}\r\n  }\r\n  rc.shadowBlur=0;rc.fillStyle='#ffe14a';\r\n  for(const pr of sim.s.projs){if(!pr.shootable)continue;const p=scene.projScreen(pr.id,{});if(!p||!p.ok)continue;const cx=p.x*w,cy=p.y*h-26;rc.beginPath();rc.moveTo(cx,cy+12);rc.lineTo(cx-10,cy-6);rc.lineTo(cx+10,cy-6);rc.fill();}\r\n }\r\n function frame(now){\r\n  if(disposed)return;raf=requestAnimationFrame(frame);const dt=last?Math.min(.25,(now-last)/1000):0;last=now;\r\n  try{\r\n   readInput();\r\n   if(wantFire){wantFire=0;sim.fire(null);}\r\n   for(let left=dt;left>1e-6;left-=.05)sim.step(Math.min(.05,left));\r\n   for(const ev of sim.drain())handle(ev);\r\n   const s=sim.s;shell.phase(!document.hidden&&!s.hold&&['fight','travel'].includes(s.phase)?'active':'paused');segs.forEach((n,i)=>n.classList.toggle('off',i>=s.hp));\r\n   {const levelName=s.level.name||`LEVEL ${s.levelNo+1}`,areaName=s.level.stations?.[Math.min(3,s.area)]||`AREA ${Math.min(4,s.area+1)}`;setVisualCue(info.children[0],levelIcon(s.level),levelName,levelName);const tally=info.children[1];if(tally.dataset.area!==areaName||!tally.children.length){tally.dataset.area=areaName;tally.replaceChildren(el('small'),el('small'),visualCue('star','Bonus XP this run',String(bank.bank)));}tally.children[0].textContent=areaName;tally.children[1].textContent='BONUS XP';setVisualCue(tally.children[2],'star',`Bonus XP this run: ${bank.bank}`,String(bank.bank));tally.setAttribute('aria-label',`${levelName}, ${areaName}. Bonus XP this run: ${bank.bank}`);}for(const k in zoneEls){const v=s.zones[k];zoneEls[k].classList.toggle('on',v>0);zoneEls[k].style.background=v>0?`rgba(255,${Math.round(210-120*v)},60,${.3+.6*v})`:'';zoneEls[k].style.boxShadow=v>0?`0 0 ${Math.round(18*v)}px #ffb040`:'';}hp.setAttribute('aria-label','Health');hp.setAttribute('aria-valuenow',String(s.hp));\r\n   fruit.frame(dt,s);sfx.frame(s,dt);\r\n   {const on=s.phase==='fight'||s.phase==='travel',hid=s.input.hidden,ex=hid?pose.exit??(s.input.side<0?'left':s.input.side>0?'right':'bottom'):null;chip.hidden=!on;chip.className=`cc-chip ${hid?'in':'out'}`;setVisualCue(chip,hid?'cover':'exposed',hid?'In cover':'Exposed');exitGlow.className=`cc-exit ${ex||''}${ex?' on':''}`;}\r\n   if(s.phase!=='intro'&&hint.childNodes.length&&hintT<=0)hint.replaceChildren();\r\n   if(hintT>0){hintT-=dt;hints(!pose.calibrated&&pose.visible);}\r\n   const active=now-activeAt<1000||s.input.hidden;if(active&&now-pulseAt>400&&s.phase!=='dead'){pulseAt=now;onActive();}\r\n   scene.frame(s,dt,now);drawRings();if(firstFrame){const f=firstFrame;firstFrame=null;requestAnimationFrame(()=>f());}\r\n   if(s.phase==='dead'){deadTimer-=dt;if(deadTimer<=0){deadTimer=1e9;onCaught();}}\r\n  }catch(error){shell.phase('error');cancelAnimationFrame(raf);firstFrame?.();firstFrame=null;console.warn('Crouching Crisis rendering stopped:',error);say(textp('Graphics interrupted. Reopen Crouching Crisis.'),0);}\r\n }\r\n const stamp=()=>{activeAt=performance.now();};\r\n const kmap={a:'left',arrowleft:'left',d:'right',arrowright:'right'};\r\n const onKey=e=>{if(e.target?.closest?.('input,select,textarea'))return;const k=kmap[e.key.toLowerCase()];\r\n  if(k){keys[k]=e.type==='keydown';stamp();e.preventDefault();}else if(e.key===' '&&e.type==='keydown'&&!e.repeat){wantFire=1;stamp();e.preventDefault();}};\r\n const onDown=e=>{if(e.target?.closest?.('button,select,a,input,summary'))return;const r=host.getBoundingClientRect(),f=(e.clientX-r.left)/r.width;stamp();\r\n  if(f<.28)touch.left=true;else if(f>.72)touch.right=true;else wantFire=1;};\r\n const onUp=()=>{touch.left=touch.right=false;};\r\n window.addEventListener('keydown',onKey);window.addEventListener('keyup',onKey);host.addEventListener('pointerdown',onDown);window.addEventListener('pointerup',onUp);window.addEventListener('pointercancel',onUp);\r\n const onVisibility=()=>{last=0;};document.addEventListener('visibilitychange',onVisibility);\r\n raf=requestAnimationFrame(frame);\r\n Promise.resolve(reveal).then(()=>new Promise(r=>requestAnimationFrame(r))).then(()=>{if(!disposed&&!portalBusy)sim.s.hold=false;});\r\n return {sim,scene,input,sfx,\r\n  key(name,down=true){if(disposed)return;if(name===' ')down&&(wantFire=1);else if(name in keys)keys[name]=down;stamp();},\r\n  pose(detail){\r\n   if(disposed)return pose;fruit?.pose(detail);\r\n   const out=input.update(detail);pose=out;poseT=performance.now();\r\n   if(out.hidden)stamp();if(out.visible){if(out.fire.length||Math.abs(out.shift-lastShift)>.03)stamp();lastShift=out.shift;}\r\n   if(out.fire.length&&sim.s.phase==='fight')for(const f of out.fire)sim.fire(f.x,Math.max(.01,f.power));\r\n   return out;\r\n  },\r\n  stop(){if(disposed)return;shell.close();disposed=true;abort.abort();firstFrame?.();firstFrame=null;cancelAnimationFrame(raf);clearTimeout(centerT);clearTimeout(tagT);fruit.stop();sfx.stop();window.removeEventListener('keydown',onKey);window.removeEventListener('keyup',onKey);host.removeEventListener('pointerdown',onDown);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);document.removeEventListener('visibilitychange',onVisibility);\r\n   scene.dispose();for(const e of [layer,hud,style])e.remove();host.classList.remove('runner-on');},\r\n };\r\n}\r\n","arcade/cover/food-types.mjs":"export const FOOD_TYPES={'food-watermelon':['#2f9e3a','#246d28','#ff4c4c'],'food-pineapple':['#f5b50a','#c88b00','#fff200'],'food-banana':['#ffe135','#cca700','#ffffe0'],'food-orange':['#ffa500','#cc8400','#ffdab9'],'food-burrito':['#d2a679','#a77c4f','#f5deb3'],'food-donut':['#ffc0cb','#e6a7ad','#ffe6fa'],'food-pizza':['#ff6347','#cc4b3d','#ffa07a'],'food-taco':['#f5deb3','#c49e73','#fff8dc']}; // [main, shade, light] hex colours\r\nexport const FOOD_BOOM={'food-watermelon':{parts:22,chunks:7,smoke:1,shake:.15,ring:2.0,stain:4},'food-pineapple':{parts:24,chunks:8,smoke:0,shake:.12,ring:1.8,stain:3},'food-banana':{parts:18,chunks:6,smoke:0,shake:.18,ring:2.2,stain:5},'food-orange':{parts:20,chunks:7,smoke:0,shake:.16,ring:2.0,stain:4},'food-burrito':{parts:26,chunks:9,smoke:1,shake:.14,ring:1.6,stain:3},'food-donut':{parts:30,chunks:8,smoke:0,shake:.19,ring:2.4,stain:5},'food-pizza':{parts:28,chunks:7,smoke:0,shake:.13,ring:2.1,stain:4},'food-taco':{parts:22,chunks:6,smoke:0,shake:.17,ring:1.9,stain:3}};\r\n","arcade/cover/levels/crisis.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 1 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP\r\n *       flee?:true      Halo grunt: when it is the last hostile alive it panics and runs off (area clears, half xp)\r\n *       shield?:[upS,downS]  Halo jackal: raises an energy shield for upS s every upS+downS s; damage is blocked while up\r\n *       cloak?:[onS,offS]    Halo elite: nearly invisible for onS s every onS+offS s; takes half damage while cloaked\r\n *       flyer?:true     hovers high in the UP zone; only an upward swing hits it\r\n *       transform?:{prop,color}  Transformers: enters disguised as a kitchen appliance (prop: toaster|blender|fridge|microwave|kettle|phone|mixer|coffee) then unfolds into the coach }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket | blaster | flame | fireball | ice | disc | needler (pink crystal shards) | bolt fallback\r\n *   backdrop?:'ring'                          Halo: a giant ring arcs across the sky behind the scenery\r\n *   scale?:number                             world prop scale (kitchen = ~6: the player is shrunk among giant appliances)\r\n *   stations?:[4 names]                       optional name per area shown on the rail card (kitchen: STOVE, FRIDGE, SINK, TRASH ...)\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   route?:[4 x [segment,...]]                optional travel path. Stop i = the walk that ARRIVES at arena i (between arenas the camera leaves the previous arena, walks the segments, then steps into the next).\r\n *                                             segments (arcade/cover/route.mjs SEGS): hall (straight 6 m) | left / right (90 deg corner) | up / down (stairs, one floor = 3.6 m) | elevator (ride, doors, floor counter) | outside (open walkway) | door (swinging door).\r\n *                                             rules (checkRoute): exactly 4 stops, known names only, at least one up/down/elevator/outside overall, pieces must not overlap the building. Omit it and genRoute(level) makes a deterministic one from the level id (each stop turns the heading, 4-6.8 s).\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst MOM_SECURITY={glb:'roster/02-taper-tallstalk--3d_humanoid_figure',dress:['suit','tie','shades','beret','gun'],hp:1,xp:1,every:[1.1,2.6],enter:['jump','slide','drop','rise']};\r\nexport default {\r\n id:'crisis',name:'CRISIS AT MOM HQ',inspiredBy:'Time Crisis',\r\n intro:{title:'CRISIS AT MOM HQ',line:'Escape MOM Inc. Take cover!'},\r\n palette:{sky:'#b8d6eb',fog:'#7894aa',floor:'#394956',wall:'#9aafbd',trim:'#d6e3e9',cover:'#33485a',coverTop:'#90aaba',accent:'#d52e3e',light:'#dae9f1'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.65,1.2,'#b8ccd6');k.box(s*2.79,2.45,-3,.035,.11,1,'#bf3546');k.box(s*2.79,1.7,-3,.035,.95,.8,'#314c60');}return;}\r\n  const beat=Math.floor(cell/2)%4,z=-7.15;\r\n  // Paired checkpoint pylons and a deep crossbeam replace the text-led lobby cue.\r\n  for(const s of [-1,1]){k.box(s*3.55,2.8,z,.72,5.5,.72,'#c4d6df');k.box(s*3.55,2.8,z+.39,.12,5.2,.06,'#6a8294');k.box(s*3.55,5.68,z,.96,.26,.92,'#e0e8e9');}\r\n  k.box(0,5.27,z,7.8,.42,.55,'#bacbd4');k.box(0,5.02,z+.31,6.7,.07,.05,'#d73442');\r\n  // The main checkpoint sits in the portrait opening; its upper lintel clears coach heads.\r\n  for(const s of [-1,1]){k.box(s*1.62,1.95,z-.55,.34,3.7,.48,'#d5e1e4');k.box(s*1.62,1.95,z-.28,.08,3.45,.05,'#6d8797');}\r\n  k.box(0,3.72,z-.55,3.55,.3,.48,'#c3d2d8');k.box(0,3.52,z-.27,2.9,.06,.05,'#d63b43');\r\n  for(const s of [-1,1]){k.box(s*2.3,.72,z-.62,.72,1.34,.5,'#263a4b');k.box(s*2.3,1.3,z-.34,.66,.07,.06,'#9ec4d1');}\r\n  // Recurring low security counters read as physical cover, with blue-gray faces and glass divisions.\r\n  for(const s of [-1,1]){k.box(s*5.05,.82,z+1.1,1.25,1.64,1.5,'#263c4d');k.box(s*5.05,1.55,z+.32,1.2,.08,.09,'#b9d6e2');k.box(s*5.05,1.14,z+.32,.055,.62,.035,'#90c3d4');}\r\n  if(beat===0){for(const s of [-1,1]){const g=k.box(s*2.35,2.1,z-.2,.08,2.3,.08,'#cde4e9');g.rotation.z=s*.32;}k.box(0,.08,z+1.5,9,.12,2.6,'#637988');}\r\n  if(beat===1){for(const s of [-1,1]){k.box(s*4.35,3.1,z,.26,4.1,.45,'#617789');k.box(s*4.35,3.1,z+.25,.1,3.6,.08,'#d3e2e6');}}\r\n  if(beat===2){k.box(0,2.45,z-.1,4.8,3.3,.34,'#354b5b');for(const x of [-1.65,-.55,.55,1.65])k.box(x,2.45,z+.1,.07,3.15,.04,'#b4c6ce');for(const x of [-1.65,1.65])k.sphere(x,4.3,z,.19,'#e33b42');}\r\n  if(beat===3){k.box(0,3.45,z-.2,5.8,.36,.6,'#334859');for(const s of [-1,1]){k.box(s*2.4,3.1,z-.2,.22,1.7,.42,'#b5cbd6');k.sphere(s*2.4,4.25,z,.2,'#d73442');}k.box(0,.06,z+.25,5.8,.1,1.2,'#293c4b');}\r\n  // Beacon pods, door mullions and a small MOM seal support the shape language.\r\n  if(cell%2===0){k.cyl(-3.55,6.03,z,.23,.4,'#b33140');k.sphere(-3.55,6.28,z,.18,k.glow('#ff3648'));k.cyl(3.55,6.03,z,.23,.4,'#b33140');k.sphere(3.55,6.28,z,.18,k.glow('#ff3648'));}\r\n },\r\n // Optional per-level dressing, called once per coach on its normalised rig: root = Group (height 1, feet y=0, centred, faces +z), h = {box,cyl,sphere,glow,dims:{w,d},role,def}.\r\n // Add suits, armour, props with h.box(x,y,z,w,h,d,'#hex') etc. Purple tint and the MOM Inc. badge are applied for you.\r\n dress(root,T,h){if(h.role==='heavy')h.box(0,.7,h.dims.d*.4,.5,.05,.05,'#ffd24a');},\r\n cover:{style:'crate',color:'#b5774a',top:'#ffd24a'},\r\n roles:{\r\n  grunt:{...MOM_SECURITY},\r\n  heavy:{...MOM_SECURITY,glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',dress:['army','helmet','shades','gun'],suit:'#c01830',hp:2,xp:3,every:[.9,2]},\r\n  roller:{...MOM_SECURITY,glb:'roster/01-seed-pearo--3d_character_model',dress:['suit','cap','shades','gun'],suit:'#2a3a7a',enter:['slide'],every:[1.4,3]},\r\n },\r\n attack:{mix:{rocket:1,fireball:1,grenade:1,crate:.7},rate:1},\r\n player:{kind:'rocket',color:'#ffb040',trail:'#d8d0c8'},\r\n scare:{role:'heavy',text:'SURPRISE AUDIT!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'grunt'},{role:'grunt'}]},\r\n  {cover:'left',wave:[{role:'grunt'},{role:'roller'},{role:'grunt'}]},\r\n  {cover:'right',wave:[{role:'roller'},{role:'heavy'},{role:'grunt'}]},\r\n  {cover:'both',wave:[{role:'heavy'},{role:'grunt'},{role:'roller'},{role:'heavy'}]},\r\n ],\r\n};\r\n","arcade/cover/levels/halo.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 5 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP\r\n *       flee?:true      Halo grunt: when it is the last hostile alive it panics and runs off (area clears, half xp)\r\n *       shield?:[upS,downS]  Halo jackal: raises an energy shield for upS s every upS+downS s; damage is blocked while up\r\n *       cloak?:[onS,offS]    Halo elite: nearly invisible for onS s every onS+offS s; takes half damage while cloaked\r\n *       flyer?:true     hovers high in the UP zone; only an upward swing hits it\r\n *       transform?:{prop,color}  Transformers: enters disguised as a kitchen appliance (prop: toaster|blender|fridge|microwave|kettle|phone|mixer|coffee) then unfolds into the coach }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket | blaster | flame | fireball | ice | disc | needler (pink crystal shards) | bolt fallback\r\n *   backdrop?:'ring'                          Halo: a giant ring arcs across the sky behind the scenery\r\n *   scale?:number                             world prop scale (kitchen = ~6: the player is shrunk among giant appliances)\r\n *   stations?:[4 names]                       optional name per area shown on the rail card (kitchen: STOVE, FRIDGE, SINK, TRASH ...)\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nexport default {\r\n id:'halo',name:'HALO RING SHOWDOWN',inspiredBy:'Halo: Combat Evolved',\r\n intro:{title:'HALO RING SHOWDOWN',line:'MOM Inc. escape? No, just a little alien detour!'},\r\n palette:{sky:'#79b6df',fog:'#8bc5db',floor:'#477b50',wall:'#4b6675',trim:'#a8d9d7',cover:'#3c5964',coverTop:'#a9d0ce',accent:'#815193',light:'#d9f1eb'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.7,1.45,'#91b1b4');k.box(s*2.79,1.8,-3,.04,2.4,.12,k.glow('#69c8c3'));}return;}\r\n  const beat=Math.floor(cell/2)%4,z=-7.15;\r\n  // A pale segmented inner-ring arc sits in the open portrait sky, above the combat window.\r\n  for(let i=0;i<9;i++){const x=-3.6+i*.9,y=3.65+1.1*(1-(x/3.6)**2),slope=-Math.atan(2.2*x/(3.6*3.6));const band=k.box(x,y,z-.72,.92,.24,.28,'#d8e6dc');band.rotation.z=slope;const light=k.box(x,y-.2,z-.55,.78,.055,.18,'#60cfc5');light.rotation.z=slope;}\r\n  // Angular silver installation pylons and a visible ring-world horizon frame the firing lanes.\r\n  for(const s of [-1,1]){const p=k.box(s*2.65,2.65,z,.72,5.1,.66,'#a9c1c6');p.rotation.z=-s*.14;k.box(s*2.65,2.65,z+.35,.12,4.6,.07,'#5cc9c5');k.box(s*5.5,1.2,z+1,.8,2.4,1.15,'#596d77');}\r\n  k.box(0,5.42,z,8.4,.24,.48,'#809da4');\r\n  if(beat===0){for(const s of [-1,1]){const fin=k.box(s*2.75,2.5,z-.1,.34,4.5,.72,'#b5c8c8');fin.rotation.z=-s*.45;k.box(s*2.75,4.2,z+.2,.46,.14,.82,'#73d4d0');}k.box(0,.06,z+.2,9,.1,1.2,'#596b64');}\r\n  if(beat===1){for(const s of [-1,1]){k.box(s*2.55,3,z,.52,5.8,.58,'#aabbb7');k.box(s*2.55,3,z+.32,.12,5.2,.06,'#54d2c9');}k.box(0,5.98,z,9,.3,.44,'#b5c8c8');}\r\n  if(beat===2){for(const s of [-1,1]){const fin=k.box(s*2.6,3,z,.7,5.5,.68,'#758e99');fin.rotation.z=s*.28;k.box(s*2.6,2,z+.38,.14,3.4,.07,'#a9d8d2');const ridge=k.box(s*5.35,1.65,z-1.3,.42,3.1,.55,'#304e4b');ridge.rotation.z=-s*.22;}}\r\n  if(beat===3){for(const s of [-1,1]){k.box(s*2.65,3,z,.48,5.8,.6,'#a8bebd');k.box(s*3.2,4.3,z-.2,.34,2.3,.4,'#829a9f').rotation.z=s*.36;}k.box(0,5.95,z,9,.3,.5,'#d0dfd7');}\r\n  // Repeating lower hex panels and pale ring slivers give shape support to sky/green/purple color.\r\n  for(const s of [-1,1]){k.box(s*5.35,.32,z+1,.9,.52,1.4,'#4e625e');k.sphere(s*5.35,.66,z+1,.13,'#8d54a9');}\r\n },\r\n cover:{style:'pillar',color:'#b5774a',top:'#ffd24a'},\r\n roles:{\r\n  grunt:{glb:'roster/01-seed-pearo--3d_character_model',scale:0.7,tint:'#e0a040',dress:['vest','antenna'],hp:1,xp:1,attack:{plasma:1},flee:true,every:[0.8,2]},\r\n  jackal:{glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',scale:0.95,tint:'#8a6ab0',dress:['visor'],hp:1,xp:1,attack:{laser:1},shield:[2.2,1.2],every:[1.0,2.5]},\r\n  elite:{glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',scale:1.25,tint:'#3050c0',dress:['helmet','chrome'],hp:2,xp:3,attack:{plasma:1.2},cloak:[2.5,2],every:[1.5,3]},\r\n },\r\n attack:{mix:{plasma:1.4,laser:0.8,grenade:0.3},rate:1},\r\n backdrop:'ring',\r\n player:{kind:'needler',color:'#ff4fd8',trail:'#ffc0f0'},\r\n scare:{role:'elite',text:'WORT WORT WORT!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'grunt'},{role:'grunt'}]},\r\n  {cover:'left',wave:[{role:'jackal'},{role:'grunt'},{role:'jackal'}]},\r\n  {cover:'right',wave:[{role:'elite'},{role:'grunt'},{role:'jackal'}]},\r\n  {cover:'both',wave:[{role:'grunt'},{role:'elite'},{role:'grunt'},{role:'jackal'}]},\r\n ],\r\n};\r\n","arcade/cover/levels/hive.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 3 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket (done), bolt (generic fallback); add new kinds in cover-scene.mjs PLAYER_VIS\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst HIVE_SECURITY={\r\n glb:'roster/01-seed-pearo--cute_alien_figure_3d_model',\r\n dress:['goo'],\r\n tint:'#3a2f4a',\r\n hp:1,\r\n xp:1,\r\n every:[0.8,1.8],\r\n enter:['drop','slide']\r\n};\r\nexport default {\r\n id:'hive',\r\n name:'HIVE ESCAPE',\r\n inspiredBy:'Aliens (Konami)',\r\n intro:{title:'HIVE ESCAPE',line:\"MOM Inc. HQ? More like MOM Inc. HIVE!\"},\r\n palette:{sky:'#1e3a4b',fog:'#004c5d',floor:'#2f3e45',wall:'#556677',trim:'#8899aa',cover:'#444444',coverTop:'#777777',accent:'#ff0000',light:'#ffcc99'},\r\n props(k,cell){\r\n  // Walls\r\n  k.box(6.5,2,0,.5,4,1,'#333');\r\n  k.box(-6.5,2,0,.5,4,1,'#333');\r\n  // Ribbed organic walls (vertical cylinders)\r\n  for(const s of [-1,1]){\r\n   k.cyl(s*4,3.5,0,.15,4,'#555');\r\n  }\r\n  // Red emergency strobe lights\r\n  const glowRed=k.glow('#ff0000');\r\n  k.sphere(2,5,0,.3,glowRed);\r\n  k.sphere(-2,5,0,.3,glowRed);\r\n  // Vents (boxes)\r\n  k.box(5.5,1.5,-1,.4,1,.8,'#222');\r\n  k.box(-5.5,1.5,1,.4,1,.8,'#222');\r\n  // Egg-like pods (spheres above lane)\r\n  for(const s of [-1,1]){\r\n   k.sphere(s*3,4,-2,.4,'#888');\r\n   k.sphere(s*3,4,2,.4,'#888');\r\n  }\r\n },\r\n cover:{style:'crate',color:'#444444',top:'#777777'},\r\n roles:{\r\n  alien:{...HIVE_SECURITY},\r\n  crawler:{...HIVE_SECURITY,glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',dress:['helmet','shades'],suit:'#555',hp:2,xp:2},\r\n  spore:{...HIVE_SECURITY,glb:'roster/07-bulb-sphereling--humanoid_robot_3d_model',dress:['cap','visor'],suit:'#666',hp:1.5,xp:1}\r\n },\r\n attack:{mix:{acid:1.5,fireball:.4},rate:.9},\r\n player:{kind:'flame',color:'#ff7a1a',trail:'#ffd060'},\r\n scare:{role:'spore',text:'MOTION DETECTED!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'alien'},{role:'crawler'}]},\r\n  {cover:'left',wave:[{role:'crawler'},{role:'spore'},{role:'alien'}]},\r\n  {cover:'right',wave:[{role:'spore'},{role:'alien'},{role:'crawler'}]},\r\n  {cover:'both',wave:[{role:'alien'},{role:'spore'},{role:'crawler'},{role:'spore'}]}\r\n ],\r\n};\r\n","arcade/cover/levels/index.mjs":"// Level list in play order; the sim loops it endlessly. (hive, park, precinct stay on disk, out of rotation.)\r\nimport crisis from './crisis.mjs';\r\nimport ps2 from './ps2.mjs';\r\nimport judgment from './judgment.mjs';\r\nimport mansion from './mansion.mjs';\r\nimport halo from './halo.mjs';\r\nimport kitchen from './kitchen.mjs';\r\nexport const LEVELS=[crisis,ps2,judgment,mansion,halo,kitchen];\r\n","arcade/cover/levels/judgment.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 2 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket (done), bolt (generic fallback); add new kinds in cover-scene.mjs PLAYER_VIS\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst CHROME_SECURITY={\r\n glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',\r\n tint:'#9fa9ba',dress:['chrome','gun'],\r\n hp:1,xp:1,\r\n every:[1.2,2.8],\r\n enter:['jump','slide','drop','rise']\r\n};\r\nexport default {\r\n id:'judgment',name:'JUDGMENT DAY',inspiredBy:'Terminator 2 / Terminator Salvation',\r\n intro:{title:'JUDGMENT DAY',line:\"MOM Inc. is burning the future—escape!\"},\r\n palette:{sky:'#34475c',fog:'#202b3b',floor:'#252d37',wall:'#394553',trim:'#89949e',cover:'#343d48',coverTop:'#8c969e',accent:'#ff562d',light:'#b7c7d1'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.7,1.45,'#394551');for(const z of [-3.5,-2.6])k.cyl(s*2.78,1.8,z,.055,2.4,'#83909a');k.box(s*2.77,2.4,-3,.04,.22,.7,k.glow('#d96a37'));}return;}\r\n  const beat=Math.floor(cell/2)%4,z=-7.15;\r\n  // Broken machine ribs and fallen conduits form a damaged skyline behind, never through, the firing lane.\r\n  for(const s of [-1,1]){const rib=k.box(s*4.8,2.8,z,.34,5.2,.48,'#111923');rib.rotation.z=-s*.18;k.box(s*4.8,5.05,z,.62,.28,.62,'#707d87');k.box(s*5.8,1.35,z+1.1,1.5,1.4,1.25,'#343d46');}\r\n  k.box(0,5.65,z,7,.3,.45,'#202832');\r\n  // Every ruined bay has a warm furnace slit cut through the cold machine wall.\r\n  k.box(0,3.68,z+.05,4.1,.92,.18,'#e45b2d');k.box(0,3.68,z+.17,3.65,.72,.08,'#ff9a47');\r\n  for(const x of [-1.75,-1.05,-.35,.35,1.05,1.75])k.box(x,3.65,z+.23,.12,.64,.08,'#141b23');\r\n  if(beat===0){k.box(0,3.2,z-.25,5.4,2.5,.32,'#38414d');for(const x of [-1.6,-.8,0,.8,1.6])k.box(x,2.5,z-.04,.12,1.4,.38,'#64717c');k.box(0,4.7,z,.45,.38,.16,k.glow('#ff602c'));}\r\n  if(beat===1){for(const s of [-1,1]){const beam=k.box(s*2.9,2.6,z-.4,4.2,.28,.35,'#111820');beam.rotation.z=s*.28;k.box(s*4.2,1.85,z-.5,1.6,.7,1.5,'#424c56');}for(const s of [-1,1])k.sphere(s*1.5,4.2,z-.08,.16,k.glow('#ff3f32'));}\r\n  if(beat===2){k.box(0,3.25,z-.2,6.3,3.8,.25,'#36404b');k.box(0,3.15,z,4.4,2.8,.12,'#e25b2a');k.box(0,3.15,z+.08,3.8,2.35,.1,'#4a505a');for(const s of [-1,1])k.cyl(s*2.9,.6,z+.6,.5,1.2,'#59636b');}\r\n  if(beat===3){k.box(0,4.45,z-.2,6.3,.25,.48,'#121820');for(const s of [-1,1]){const girder=k.box(s*2.8,2.4,z-.1,.36,4.2,.55,'#111821');girder.rotation.z=s*.48;k.box(s*3.6,.56,z+1,.55,1.1,1.1,'#444d56');}k.box(0,.06,z+.2,8,.1,1.2,'#171d25');}\r\n  // Glowing scorched seams and bent pipe ends stay outside center targets.\r\n  for(const s of [-1,1]){const pipe=k.cyl(s*3.8,1.1,z+1,.11,3.6,'#777f87');pipe.rotation.z=s*.12;k.box(s*3.7,.44,z+.9,.65,.07,.08,k.glow('#d9562d'));}\r\n },\r\n dress(root,T,h){\r\n  // T-800 endoskeleton: chrome skull, red eyes, jaw + teeth, caged ribs, spine, pistons. heavies add plate armour; the flyer is a hovering drone head.\r\n  const {w,d}=h.dims,z=d*.5+.02,C='#d9deea',D='#4b515d',K='#14161b',E=h.glow('#ff1a1a'),W=h.glow('#ffd8d0'),X=Math.min(.3,w*.42),role=h.role,B=h.box,S=h.sphere,Y=h.cyl;\r\n  const eyes=(y,zz,sx,r)=>{for(const s of [-1,1]){S(s*sx,y,zz-.02,r*1.5,K);S(s*sx,y,zz+.01,r,E);S(s*sx,y,zz+r*.8,r*.4,W);}};\r\n  const teeth=(y,zz,wd,n)=>{for(let i=0;i<n;i++)B((i/(n-1)-.5)*wd,y,zz,wd/n*.7,.03,.02,'#eef0f4');};\r\n  if(role==='flyer'){\r\n   for(const o of [...root.children])o.visible=false;\r\n   const sk=S(0,.55,0,.28,C);sk.scale.set(1,1.05,.95);B(0,.8,.02,.05,.07,.42,D);B(0,.7,.22,.36,.05,.07,D);eyes(.6,.24,.12,.07);B(0,.5,.27,.03,.1,.03,K);\r\n   B(0,.36,.16,.28,.07,.17,D);teeth(.4,.26,.24,7);for(const s of [-1,1]){B(s*.27,.5,.1,.04,.16,.2,D);Y(s*.38,.78,0,.012,.2,D);const r=Y(s*.38,.88,0,.15,.014,C);r.rotation.z=0;B(s*.17,.18,.1,.03,.16,.03,D);}\r\n   S(0,.2,0,.07,h.glow('#ff7a2a'));return;}\r\n  S(0,.88,z*.1,.1,C);B(0,.935,z,.2,.035,.05,C);B(0,.84,z,.022,.05,.02,K);B(0,.76,z-.02,.15,.045,.08,D);teeth(.78,z,.11,6);\r\n  for(const s of [-1,1]){B(s*.09,.83,z-.01,.03,.08,.04,D);Y(s*.025,.71,0,.011,.12,D);}eyes(.865,z,.06,.032);\r\n  if(role==='heavy'){\r\n   B(0,.56,z*.9,w*.62,.25,.08,'#a0a7b6');B(0,.56,z*.9+.045,w*.5,.03,.01,'#b02020');B(0,.5,z*.9+.045,w*.5,.03,.01,'#b02020');\r\n   for(const s of [-1,1]){const p=S(s*X,.72,0,.1,C);p.scale.set(1,.6,1);B(s*.08,.16,z*.6,.09,.2,.06,'#a0a7b6');S(s*.08,.3,z*.6,.04,D);}B(0,.34,z*.7,.3,.06,.06,D);\r\n  }else{\r\n   B(0,.52,z*.6,w*.5,.28,.06,K);for(let i=0;i<5;i++)B(0,.43+i*.055,z*.85,w*.46*(1-Math.abs(i-2)*.1),.018,.04,C);\r\n   Y(0,.52,z*.75,.014,.34,D);for(let i=0;i<5;i++)B(0,.4+i*.055,z*.75,.05,.02,.05,C);S(0,.54,z*.95,.03,E);\r\n  }\r\n  for(const s of [-1,1]){S(s*X,.66,0,.055,C);Y(s*X,.52,0,.016,.26,D);Y(s*X,.56,0,.026,.12,C);S(s*.07,.28,z*.2,.045,C);Y(s*.07,.2,z*.35,.015,.22,D);}\r\n },\r\n cover:{style:'pillar',color:'#8a8f98',top:'#d0d6de'},\r\n roles:{\r\n  grunt:{...CHROME_SECURITY,walker:0.18},\r\n  heavy:{...CHROME_SECURITY,\r\n   glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',\r\n   hp:2,xp:3,suit:'#c0c0c0'},\r\n  flyer:{...CHROME_SECURITY,\r\n   glb:'roster/21-flyer--winged_humanoid_3d_model',\r\n   flyer:true,scale:.8,enter:['drop'],every:[1.4,3]}\r\n },\r\n attack:{mix:{laser:1,plasma:1.2,rocket:.5},rate:1},\r\n player:{kind:'blaster',color:'#4fe8ff',trail:'#b0f6ff'},\r\n scare:{role:'heavy',text:\"I'LL BE BACK... TO WORK\"},\r\n areas:[\r\n  {cover:'both',wave:[{role:'grunt'},{role:'grunt'}]},\r\n  {cover:'left',wave:[{role:'grunt'},{role:'flyer'},{role:'grunt'}]},\r\n  {cover:'right',wave:[{role:'flyer'},{role:'heavy'},{role:'grunt'}]},\r\n  {cover:'both',wave:[{role:'heavy'},{role:'grunt'},{role:'flyer'},{role:'heavy'}]}\r\n ],\r\n};\r\n","arcade/cover/levels/kitchen.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 6 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP\r\n *       flee?:true      Halo grunt: when it is the last hostile alive it panics and runs off (area clears, half xp)\r\n *       shield?:[upS,downS]  Halo jackal: raises an energy shield for upS s every upS+downS s; damage is blocked while up\r\n *       cloak?:[onS,offS]    Halo elite: nearly invisible for onS s every onS+offS s; takes half damage while cloaked\r\n *       flyer?:true     hovers high in the UP zone; only an upward swing hits it\r\n *       transform?:{prop,color}  Transformers: enters disguised as a kitchen appliance (prop: toaster|blender|microwave|phone|kettle|mixer|coffee|fridge) then unfolds into the coach }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket | blaster | flame | fireball | ice | disc | needler (pink crystal shards) | bolt fallback\r\n *   backdrop?:'ring'                          Halo: a giant ring arcs across the sky behind the scenery\r\n *   scale?:number                             world prop scale (kitchen = ~6: the player is shrunk among giant appliances)\r\n *   stations?:[4 names]                       optional name per area shown on the rail card (kitchen: STOVE, FRIDGE, SINK, TRASH ...)\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nexport default {\r\n id:'kitchen',\r\n name:'KITCHEN ESCAPE',\r\n inspiredBy:'Transformers',\r\n intro:{title:'KITCHEN ESCAPE',line:\"MOM Inc. Kitchen. Get out before the appliances turn on!\"},\r\n palette:{sky:'#b7c7ce',fog:'#778791',floor:'#625c53',wall:'#dfd9ca',trim:'#9ca4a3',cover:'#b8b7ad',coverTop:'#76858a',accent:'#d34c39',light:'#e7ece7'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.6,1.5,'#e4e0d3');k.box(s*2.79,1.8,-3,.04,2.4,.06,'#758287');k.box(s*2.76,1.8,-2.55,.08,.65,.07,'#bdc8c7');}return;}\r\n  // runProps scales mesh dimensions and Y, but leaves X/Z centers alone. Convert authored kitchen\r\n  // sizes back to room units so the existing six-times player scale does not push landmarks overhead.\r\n  const S=6,box=(x,y,z,w,h,d,c)=>k.box(x,y/S,z,w/S,h/S,d/S,c),cyl=(x,y,z,r,h,c)=>k.cyl(x,y/S,z,r/S,h/S,c);\r\n  const beat=Math.floor(cell/2)%4,z=-7.15,enamel='#e4e0d3',edge='#758287',dark='#29343a',chrome='#bdc8c7';\r\n  for(const s of [-1,1]){box(s*5.8,2.8,z,.35,5.6,.45,'#b6b4a9');box(s*5.8,2.8,z+.24,.06,5.2,.06,edge);}\r\n  if(beat===0){ // STOVE: raised burner plates, oven windows and control row.\r\n   box(0,1.42,z-.32,2.7,2.62,.86,enamel);box(0,1.48,z+.14,2.15,1.18,.08,dark);box(0,1.48,z+.19,1.89,.92,.04,'#56666c');\r\n   for(const x of [-.78,.78]){cyl(x,2.82,z,.51,.11,dark);cyl(x,2.89,z,.38,.08,chrome);cyl(x,2.95,z,.17,.05,'#37434a');}for(const x of [-.72,-.24,.24,.72])box(x,2.48,z+.51,.09,.09,.06,'#d0b98b');box(0,3.85,z-.48,2.9,1.75,.22,enamel);box(0,3.18,z-.3,2.85,.14,.34,edge);\r\n   for(const s of [-1,1]){box(s*3.9,1.5,z,1.75,2.7,1.5,enamel);box(s*3.9,2.9,z,1.92,.16,1.64,chrome);box(s*3.9,1.55,z+.78,1.32,1.1,.08,dark);box(s*3.9,1.62,z+.84,1.13,.86,.035,'#68777a');\r\n    for(const x of [-.48,-.16,.16,.48])box(s*3.9+x,2.48,z+.84,.08,.08,.06,'#d0b98b');\r\n    for(const x of [-.42,.42]){cyl(s*3.9+x,3.08,z,.4,.1,dark);cyl(s*3.9+x,3.14,z,.29,.08,'#9ca8a5');cyl(s*3.9+x,3.2,z,.13,.05,'#38464b');}\r\n    box(s*3.9,4.15,z-.35,1.85,1.8,.22,enamel);box(s*3.9,3.42,z-.18,1.9,.14,.35,edge);}\r\n   box(0,5.35,z,8.6,.22,.4,'#858d8a');\r\n  }\r\n  if(beat===1){ // FRIDGE: split giant doors, recessed water panel, paired long handles.\r\n   box(0,3.08,z-.32,3.55,6.1,1.05,enamel);box(0,3.08,z+.23,3.35,5.9,.08,'#f0eee6');box(0,3.12,z+.29,.12,5.9,.12,edge);box(-.88,3.3,z+.35,.11,1.7,.12,chrome);box(.88,3.3,z+.35,.11,1.7,.12,chrome);box(0,6.03,z+.34,3.38,.1,.08,chrome);box(-1.12,2.2,z+.4,.43,1.12,.08,'#506c73');\r\n   for(const s of [-1,1]){box(s*3.85,3.1,z,2.2,6.2,1.05,enamel);box(s*3.85,3.1,z+.55,2.02,5.98,.08,'#f0eee6');box(s*3.85,6.16,z+.62,2.04,.1,.08,chrome);box(s*3.35,3.4,z+.67,.1,1.55,.1,edge);box(s*4.3,3.3,z+.67,.09,1.75,.1,chrome);}\r\n   box(0,3.12,z,.1,6.08,.9,edge);box(-4.95,2.2,z+.62,.42,1.1,.08,'#506c73');for(let y=1.9;y<2.6;y+=.2)cyl(-4.95,y,z+.69,.045,.05,'#d4e0dc');\r\n  }\r\n  if(beat===2){ // SINK: broad steel rim, dark basin and a raised faucet.\r\n   box(0,2.02,z,3.9,.46,2.1,'#8a6747');box(0,2.29,z+.05,4.1,.13,2.22,chrome);box(0,2.34,z+.18,2.45,.08,1.4,dark);box(0,2.37,z+.2,2.1,.04,1.1,'#53676d');box(0,3.7,z-.43,.13,2.55,.13,chrome);box(0,4.94,z-.1,.13,.13,.82,chrome);box(0,4.68,z+.28,.13,.58,.13,chrome);box(0,4.4,z+.22,.08,.08,.48,'#bce4e8');\r\n   for(const s of [-1,1]){box(s*3.8,2.15,z,2.2,.48,2.2,'#8a6747');box(s*3.8,2.43,z+.05,2.34,.12,2.3,chrome);box(s*3.8,2.48,z+.18,1.35,.07,1.38,dark);box(s*3.8,2.5,z+.2,1.08,.035,1.08,'#53676d');box(s*3.8,3.8,z-.42,.12,2.55,.12,chrome);box(s*3.8,5.03,z-.1,.12,.12,.75,chrome);box(s*3.8,4.78,z+.26,.12,.58,.12,chrome);box(s*3.8,4.48,z+.2,.08,.08,.42,'#bce4e8');}\r\n  }\r\n  if(beat===3){ // TRASH: ribbed steel can, rolled rim and lid at the right station.\r\n   cyl(1.35,2.62,z,1.22,5.1,'#59666c');cyl(1.35,5.23,z,1.31,.2,chrome);cyl(1.35,5.36,z,1.05,.14,dark);for(let x=-.78;x<=.78;x+=.26)box(1.35+x,2.62,z+1.12,.055,4.65,.08,'#9ba6a3');const lid=box(1.35,5.46,z,1.45,.18,1.42,'#77858a');lid.rotation.z=.12;box(1.35,5.57,z,.7,.08,.74,'#bcc6c2');\r\n   for(const s of [-1,1]){cyl(s*4.15,2.7,z,.9,5.2,'#59666c');cyl(s*4.15,5.35,z,.99,.2,chrome);cyl(s*4.15,5.48,z,.78,.14,dark);for(let x=-.52;x<=.52;x+=.26)box(s*4.15+x,2.7,z+.83,.05,4.75,.08,'#9ba6a3');const lid=box(s*4.15,5.58,z,.95,.16,1.08,'#77858a');lid.rotation.z=s*.12;box(s*4.15,5.69,z,.46,.08,.56,'#bcc6c2');}\r\n  }\r\n  // Side appliances persist as scale cues at every station: toaster slots and a blender jar/base.\r\n  for(const s of [-1,1]){box(s*5.45,1.2,z+1,.62,.92,.72,'#b88d63');box(s*5.45,1.68,z+1.15,.66,.08,.68,'#d4b18a');for(const x of [-.16,.16])box(s*5.45+x,1.71,z+1.15,.07,.025,.44,dark);box(s*5.45,2.85,z-1,.56,.24,.5,dark);cyl(s*5.45,3.25,z-1,.22,.62,'#a4c9c8');cyl(s*5.45,3.59,z-1,.28,.08,chrome);}\r\n },\r\n cover:{style:'crate',color:'#c8a070',top:'#ffd24a'},\r\n roles:{\r\n  bot:{glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',\r\n       dress:['visor','chrome'],\r\n       transform:{prop:'toaster',color:'#c0c0c8'},\r\n       hp:2,xp:2,attack:{laser:1},every:[0.9,2],enter:['jump']},\r\n  bot2:{glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',\r\n        dress:['visor','chrome'],\r\n        transform:{prop:'blender',color:'#a0a0b0'},\r\n        hp:2,xp:2,attack:{laser:1},every:[0.9,2],enter:['jump']},\r\n  bot3:{glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',\r\n        dress:['visor','chrome'],\r\n        transform:{prop:'microwave',color:'#d0d0e0'},\r\n        hp:2,xp:2,attack:{laser:1},every:[0.9,2],enter:['jump']},\r\n  minion:{glb:'roster/01-seed-pearo--blank_toy_figure_3d_model',\r\n          dress:['antenna','visor'],\r\n          tint:'#ffd23a',\r\n          hp:1,xp:1,attack:{laser:1},every:[0.8,1.6],enter:['slide']},\r\n },\r\n attack:{mix:{laser:1,plasma:.8,rocket:.5,crate:.3},rate:1},\r\n player:{kind:'blaster',color:'#4fe8ff',trail:'#b0f6ff'},\r\n scare:{role:'bot2',text:'BLEND MODE ENGAGED!'},\r\n stations:['STOVE','FRIDGE','SINK','TRASH'],\r\n areas:[\r\n  {cover:'both',wave:[{role:'bot'},{role:'minion'}]},\r\n  {cover:'left',wave:[{role:'bot'},{role:'bot2'},{role:'minion'}]},\r\n  {cover:'right',wave:[{role:'bot3'},{role:'minion'},{role:'bot'}]},\r\n  {cover:'both',wave:[{role:'bot2'},{role:'bot3'},{role:'minion'},{role:'bot'}]},\r\n ],\r\n scale:6,\r\n};\r\n","arcade/cover/levels/mansion.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 1 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket (done), bolt (generic fallback); add new kinds in cover-scene.mjs PLAYER_VIS\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst ZOMBIE_BASE={\r\n glb:'roster/01-seed-pearo--3d_character_model',\r\n tint:'#7a8a6a',\r\n dress:['goo','vest'],\r\n vest:'#4a4038',\r\n hp:1,\r\n xp:1,\r\n walker:0.15,\r\n enter:['slide']\r\n};\r\nexport default {\r\n id:'mansion',name:'MANSION ESCAPE',inspiredBy:'The House of the Dead',\r\n intro:{title:'MANSION ESCAPE',line:'Escape MOM Inc. Under the moonlit curse!'},\r\n palette:{sky:'#423746',fog:'#25464c',floor:'#28252b',wall:'#454148',trim:'#b4a990',\r\n   cover:'#302a2d',coverTop:'#8d8178',accent:'#d74c3a',light:'#cfbd97'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.4,.95,'#a89a83');k.box(s*2.79,1.8,-3,.04,2.1,.72,'#493540');k.cyl(s*2.75,1.5,-3,.05,.45,'#98836a');k.sphere(s*2.75,1.8,-3,.08,k.glow('#ffb452'));}return;}\r\n  const beat=Math.floor(cell/2)%4,z=-7.15,stone='#8f8a7e',dark='#44404b';\r\n  // Pointed doorway built from broad stone jambs and angled lintels, with a dark open center.\r\n  for(const s of [-1,1]){k.box(s*1.72,2.05,z,.42,3.95,.64,stone);const arch=k.box(s*.92,3.55,z,.3,1.8,.58,'#aaa28f');arch.rotation.z=s*.82;k.box(s*3.8,2.1,z+1,.32,4.2,.65,'#343139');}\r\n  k.box(0,4.42,z,.38,.25,.64,stone);k.box(0,2.12,z-.08,3.2,4.0,.08,dark);\r\n  // A framed, face-readable portrait sits above the intruding silhouettes and in front of the wall.\r\n  k.box(.72,3.22,z+.14,1.02,1.78,.12,'#b09a78');k.box(.72,3.22,z+.22,.82,1.56,.06,'#30262c');k.sphere(.72,3.5,z+.28,.25,'#d4c2a5');k.sphere(.63,3.54,z+.5,.035,'#171820');k.sphere(.81,3.54,z+.5,.035,'#171820');k.box(.72,2.86,z+.28,.5,.38,.08,'#60404a');\r\n  for(const s of [-1,1]){k.cyl(s*1.95,2.35,z+.25,.065,1.85,'#332a2a');k.cyl(s*1.95,3.29,z+.25,.3,.1,'#b0a186');k.sphere(s*1.95,3.52,z+.25,.14,k.glow('#ffb452'));}\r\n  if(beat===0){for(const s of [-1,1]){k.box(s*4.55,2.4,z,.48,4.8,.64,'#4f4a4c');k.box(s*4.55,2.4,z+.36,.12,4.3,.08,'#b5aa94');}k.box(0,.06,z+.2,9,.1,1.2,'#373239');}\r\n  if(beat===1||beat===3){for(const s of [-1,1]){k.box(s*4.55,3,z,.95,2.6,.26,'#2c2428');k.box(s*4.55,3,z+.17,.7,2.15,.06,'#8b755f');k.sphere(s*4.55,3.35,z+.24,.4,'#d0b99d');k.sphere(s*4.55,2.45,z+.23,.64,'#3e2931');k.cyl(s*4.55,1.2,z,.12,1.9,'#262327');k.box(s*4.55,2.13,z,.62,.12,.42,'#aea183');k.sphere(s*4.55,2.55,z,.11,k.glow('#ffae58'));}}\r\n  if(beat===2){k.box(0,3,z-.2,5.2,3.2,.28,'#57505c');for(const x of [-1.55,-.78,0,.78,1.55])k.box(x,3,z+.02,.08,2.9,.12,'#876d59');for(const s of [-1,1]){k.cyl(s*3.85,1.65,z,.09,3.1,'#252328');k.cyl(s*3.85,3.12,z,.32,.12,'#b4a484');for(const y of [3.35,3.6,3.85])k.sphere(s*3.85,y,z,.14,k.glow('#ffb452'));}}\r\n  // Ragged stone shoulders and floor fragments imply the ruined inner rooms.\r\n  for(const s of [-1,1]){const shard=k.box(s*4.8,.45,z+1,.8,.75,.8,'#625c58');shard.rotation.z=s*.18;k.sphere(s*5.25,.12,z,.55,'#5a514d');}\r\n },\r\n dress(root,T,h){\r\n  if(h.role==='thrower'){\r\n   h.sphere(0,0.5,0,0.15,h.glow('#ffae58'));\r\n   h.box(0,.1,0,.4,.05,.4,'#262327');\r\n  }\r\n  if(h.role==='walkerA'||h.role==='walkerB'||h.role==='thrower'){const z=h.dims.d*.42;h.box(0,.47,z,.43,.42,.08,'#51464a');for(const s of [-1,1]){h.box(s*.2,.45,z+.04,.09,.48,.07,'#bdb6a3');h.sphere(s*.12,.88,z+.025,.075,'#ded0b0');h.sphere(s*.14,.89,z+.09,.025,'#231e24');}}\r\n },\r\n cover:{style:'desk',color:'#4a4038',top:'#ffd24a'},\r\n roles:{\r\n  walkerA:{...ZOMBIE_BASE},\r\n  walkerB:{...ZOMBIE_BASE,glb:'roster/02-taper-tallstalk--3d_humanoid_figure'},\r\n  thrower:{...ZOMBIE_BASE,glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',\r\n    attack:{axe:1,barrel:1},walker:0.18}\r\n },\r\n attack:{mix:{swipe:2,axe:.6,barrel:.5},rate:0.45},\r\n player:{kind:'fireball',color:'#ff6a1a',trail:'#ffb040'},\r\n scare:{role:'thrower',text:'SURPRISE VISIT!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'walkerA'},{role:'walkerB'}]},\r\n  {cover:'left',wave:[{role:'walkerA'},{role:'thrower'},{role:'walkerB'}]},\r\n  {cover:'right',wave:[{role:'thrower'},{role:'walkerA'},{role:'walkerB'}]},\r\n  {cover:'both',wave:[{role:'thrower'},{role:'walkerA'},{role:'walkerB'},{role:'thrower'}]}\r\n ],\r\n};\r\n","arcade/cover/levels/park.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 5 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket (done), bolt (generic fallback); add new kinds in cover-scene.mjs PLAYER_VIS\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst MOM_SECURITY={glb:'roster/02-taper-tallstalk--3d_humanoid_figure',dress:['suit','tie','shades','beret','gun'],hp:1,xp:1,every:[1.1,2.6],enter:['jump','slide','drop','rise']};\r\nexport default {\r\n id:'park',name:'MOM PARK ESCAPE',inspiredBy:'The Lost World',\r\n intro:{title:'MOM PARK ESCAPE',line:'Run from the jungle!'},\r\n palette:{sky:'#a0d8ff',fog:'#7fbf9e',floor:'#4c6b3a',wall:'#5a4e38',trim:'#8ab56e',cover:'#6e4c2a',coverTop:'#ffd24a',accent:'#ff5a1a',light:'#ffe3b1'},\r\n props(k,cell){\r\n  // Trees\r\n  for(const s of [-1,1]){\r\n   k.cyl(s*4.0,0,-1,0.3,3,'#2e7d32'); // trunk\r\n   k.sphere(s*4.0,5,-1,1.8,'#388e3c'); // leaves above lane\r\n  }\r\n  // Broken fence posts at edges\r\n  for(const s of [-1,1]){\r\n   k.box(s*6.2,0.5,-2,0.15,1.5,0.15,'#a67b5b');\r\n  }\r\n  // Sign in centre\r\n  if(cell%3===0)k.sign(['MOM PARK','JUNGLE FUN'],0,4.2,-1,3,1);\r\n },\r\n dress(root,T,h){\r\n  if(h.role==='dino'){\r\n   h.box(0,1.2,0.5,0.6,0.2,0.2,'#ffd24a'); // crest on top\r\n   h.box(-0.4,0.8,-0.3,0.4,0.1,0.7,'#ffb040'); // tail behind\r\n  }\r\n },\r\n cover:{style:'crate',color:'#6e4c2a',top:'#ffd24a'},\r\n roles:{\r\n  dino:{...MOM_SECURITY,glb:'roster/01-seed-pearo--3d_character_model',dress:['army','helmet','shades','gun'],suit:'#38c000',hp:2,xp:3,every:[0.9,2],enter:['jump']},\r\n  ptero:{...MOM_SECURITY,glb:'roster/02-taper-tallstalk--humanoid_robot_3d_model1',dress:['army','helmet','shades','gun'],suit:'#ff8c00',hp:2,xp:3,every:[1.5,3],enter:['drop']},\r\n  raptor:{...MOM_SECURITY,glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',dress:['army','helmet','shades','gun'],suit:'#ffb040',hp:2,xp:3,every:[1.0,2.5],enter:['jump']},\r\n },\r\n attack:{mix:{fireball:1,rocket:.6,crate:.5},rate:1},\r\n player:{kind:'ice',color:'#aef0ff',trail:'#e8fbff'},\r\n scare:{role:'dino',text:'CLEVER GIRL!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'dino'},{role:'ptero'}]},\r\n  {cover:'left',wave:[{role:'raptor'},{role:'dino'}]},\r\n  {cover:'right',wave:[{role:'ptero'},{role:'raptor'}]},\r\n  {cover:'both',wave:[{role:'dino'},{role:'ptero'},{role:'raptor'}]},\r\n ],\r\n};\r\n","arcade/cover/levels/precinct.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 6 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket (done), bolt (generic fallback); add new kinds in cover-scene.mjs PLAYER_VIS\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst MOM_COACH={glb:'roster/02-taper-tallstalk--3d_humanoid_figure',dress:['suit','tie','shades','gun'],hp:1,xp:1,every:[1.2,2.5],enter:['rise']};\r\nconst INTERN={glb:'roster/02-taper-tallstalk--3d_humanoid_figure',civilian:true,dress:['cap'],hp:1,xp:0};\r\nexport default {\r\n id:'precinct',name:'PRECISE ESCAPE',inspiredBy:'Virtua Cop',\r\n intro:{title:'PRECISE ESCAPE',line:'MOM Inc. office, your exit is a sprint!'},\r\n palette:{sky:'#a8d5f2',fog:'#7aa9c4',floor:'#6b4a3a',wall:'#8a5a44',trim:'#e5ba65',cover:'#1e90ff',coverTop:'#ffd24a',accent:'#ff4500',light:'#ffe3b1'},\r\n props(k,cell){\r\n  // Desks on the right side\r\n  k.box(4.5,1,0,1.5,0.1,1,'#8a5a44'); // desk top\r\n  k.cyl(4.7,1.2,0,0.15,0.3,'#ffd24a'); // chair armrest\r\n  // Filing cabinet on the left side\r\n  k.box(-4.5,1,1,0.6,0.1,1.2,'#7a5236');\r\n  // Water cooler overhead (above lane)\r\n  k.box(0,4,-2,1,0.5,1,'#ffd24a');\r\n  // Motivational posters on side walls\r\n  k.sign(['GO','TEAM'],4,4,-1,2,1);\r\n  k.sign(['MOM','INC'],-4,4,1,2,1);\r\n },\r\n dress(root,T,h){\r\n  if(h.role==='coach'){\r\n   h.box(0,.5,0,.3,.05,.05,'#ffd24a'); // tie\r\n   h.cyl(0,.8,0,.07,.15,'#ffd24a'); // glasses\r\n  }\r\n  if(h.role==='intern'){\r\n   h.sphere(0,.9,0,.1,'#ffd24a'); // cap brim\r\n  }\r\n },\r\n cover:{style:'desk',color:'#1e90ff',top:'#ffd24a'},\r\n roles:{\r\n  coach:{...MOM_COACH},\r\n  intern:{...INTERN}\r\n },\r\n attack:{mix:{laser:1,rocket:.8,crate:.4},rate:1.05},\r\n player:{kind:'disc',color:'#ff3af0',trail:'#ffb0f8'},\r\n scare:{role:'coach',text:'PERFORMANCE REVIEW!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'coach'},{role:'intern'}]},\r\n  {cover:'left',wave:[{role:'coach'},{role:'coach'},{role:'intern'}]},\r\n  {cover:'right',wave:[{role:'intern'},{role:'coach'},{role:'coach'}]},\r\n  {cover:'both',wave:[{role:'coach'},{role:'intern'},{role:'coach'},{role:'intern'}]}\r\n ],\r\n};\r\n","arcade/cover/levels/ps2.mjs":"/* COVER CRISIS LEVEL CONTRACT (level 2 = this file). One file per level: arcade/cover/levels/<id>.mjs, listed in levels/index.mjs.\r\n * Pure data + one optional prop function. NO three.js import here: the sim and the tests import level files directly.\r\n * default export = {\r\n *   id, name, inspiredBy,\r\n *   intro:{title,line}                        intro card: the view prints LEVEL n (loop-safe) + title + line. Keep the MOM Inc. sign voice, short\r\n *   palette:{sky,fog,floor,wall,trim,cover,coverTop,accent,light}   hex strings; same Cool Cardio toon art for every level\r\n *   props(kit,cell)                           optional. Called for cells 0..9 (8 m apart along -z). Add code-made scenery:\r\n *                                             kit = {T (THREE), group (already placed at the cell), box(x,y,z,w,h,d,color), cyl(x,y,z,r,h,color),\r\n *                                             sphere(x,y,z,r,color), glow(color), sign(lines[],x,y,z,w,h), rnd() (seeded)}. Walls sit at x=+-6.5.\r\n *   cover:{style,color,top}                   style: crate | pillar | desk (anything else falls back to crate)\r\n *   dress?(root,T,h)                          optional per-coach code dressing hook (see crisis.mjs), runs after the role's dress[] names\r\n *   roles:{ <roleKey>:{                       at most 3 distinct glb per level; glb = roster id from creature/models/roster/manifest.json\r\n *       glb, scale?, tint?(purple default #a070c0), dress:[suit|tie|shades|army|gun|beret|helmet|visor|vest|chrome|goo|cap|antenna], vest?(color),\r\n *       hp (1), xp (1), attack?:{<kind>:weight}   overrides level attack mix for this role (kinds: rocket fireball grenade crate laser plasma acid axe barrel swipe)\r\n *       every?:[min,max] seconds between attacks, enter?:[jump|slide|drop|rise]  allowed entrances,\r\n *       walker?:speed   marches toward camera (depth 1 -> 0 in 1/speed s), then swipes; civilian?:true  never attacks, hitting it costs XP\r\n *       flee?:true      Halo grunt: when it is the last hostile alive it panics and runs off (area clears, half xp)\r\n *       shield?:[upS,downS]  Halo jackal: raises an energy shield for upS s every upS+downS s; damage is blocked while up\r\n *       cloak?:[onS,offS]    Halo elite: nearly invisible for onS s every onS+offS s; takes half damage while cloaked\r\n *       flyer?:true     hovers high in the UP zone; only an upward swing hits it\r\n *       transform?:{prop,color}  Transformers: enters disguised as a kitchen appliance (prop: toaster|blender|fridge|microwave|kettle|phone|mixer|coffee) then unfolds into the coach }}\r\n *   attack:{mix:{<kind>:weight}, rate}        level-wide enemy attack mix; rate scales projectile frequency (1 = crisis, <1 fewer shots)\r\n *   player:{kind,color,trail}                 player attack look: rocket | blaster | flame | fireball | ice | disc | needler (pink crystal shards) | bolt fallback\r\n *   backdrop?:'ring'                          Halo: a giant ring arcs across the sky behind the scenery\r\n *   scale?:number                             world prop scale (kitchen = ~6: the player is shrunk among giant appliances)\r\n *   stations?:[4 names]                       optional name per area shown on the rail card (kitchen: STOVE, FRIDGE, SINK, TRASH ...)\r\n *   scare:{role,text}                         the one mid-level jump scare (fires at the start of area 3): role comes in huge and close\r\n *   areas:[4 x {cover:'left'|'right'|'both', wave:[{role,enter?,x?}]}]   x = -1..1 screen lane; enter overrides the role pool\r\n * }\r\n * The sim loops over LEVELS, scaling per loop (+1 enemy/wave cap 6, projectiles x1.6 cap, windup 1.6 s * 0.92^loop floor 0.7 s). */\r\nconst MOM_SECURITY={glb:'roster/02-taper-tallstalk--3d_humanoid_figure',dress:['suit','tie','shades','beret','gun'],hp:1,xp:1,every:[1.1,2.6],enter:['jump','slide','drop','rise']};\r\nexport default {\r\n id:'ps2',\r\n name:'PS2 ESCAPE',\r\n inspiredBy:'Time Crisis 2 / Time Crisis 3 PS2',\r\n intro:{title:'PS2 ESCAPE',line:\"MOM Inc. HQ, but the PS2 is not so smooth!\"},\r\n palette:{sky:'#91b8d1',fog:'#637f96',floor:'#46515b',wall:'#687987',trim:'#bac7ce',cover:'#515f68',coverTop:'#a7b8c0',accent:'#c75035',light:'#d5e2e8'},\r\n props(k,cell){\r\n  // Wall-mounted passage kit: no arena center panels, no scaled or overhanging props.\r\n  if(k.scope&&k.scope!=='arena'){for(const s of k.sides){k.box(s*2.88,1.8,-3,.12,2.7,1.45,'#72858e');for(const z of [-3.55,-3.25,-2.95,-2.65])k.box(s*2.78,1.8,z,.055,2.4,.06,'#c18c62');}return;}\r\n  const beat=Math.floor(cell/2)%4,z=-7.15;\r\n  // Split sloped bunker mouth: thick concrete cheeks frame the existing lane.\r\n  for(const s of [-1,1]){const cheek=k.box(s*3.55,2.5,z,.82,4.7,1.05,'#7c8b94');cheek.rotation.z=-s*.27;k.box(s*4.35,2.55,z+.34,.15,4.9,.9,'#455665');}\r\n  k.box(0,4.85,z,7.5,.38,.85,'#87969d');\r\n  for(const s of [-1,1]){const inner=k.box(s*1.55,1.95,z-.5,.38,3.8,.7,'#98a6a8');inner.rotation.z=s*.08;k.box(s*1.55,1.95,z-.12,.08,3.45,.05,'#526a76');}\r\n  k.box(0,3.92,z-.5,3.25,.32,.7,'#a6b2b1');k.box(0,2.76,z-.25,2.55,1.16,.16,'#9b4935');k.box(0,2.78,z-.12,2.25,.91,.06,'#604d48');\r\n  for(let x=-1.02;x<=1.02;x+=.3)k.box(x,2.76,z-.04,.045,1.02,.05,'#d08455');\r\n  const dish=k.sphere(1.03,4.1,z-.2,.75,'#9da9a8');dish.scale.set(1,.66,.22);k.cyl(1.03,3.5,z-.15,.075,1.18,'#455a66');k.sphere(1.03,4.14,z+.02,.12,'#c15937');\r\n  if(beat===0){k.box(-4.6,1.05,z+1.2,1.45,2.1,2.4,'#a64f35');k.box(-4.6,2.12,z+1.2,1.5,.12,2.45,'#d07a45');for(let x=-5.15;x<=-4.05;x+=.22)k.box(x,1.05,z-.04,.035,1.8,.04,'#d07a45');k.box(4.5,1.1,z-1,1.35,2.2,2.8,'#596d75');}\r\n  if(beat===1){for(const s of [-1,1]){for(let x=-5.45;x<=-3.65;x+=.4){const r=k.box(s*4.55,2.1,z,x===0?1:.12,4.1,.22,'#b7c1c2');r.rotation.z=-s*.23;}k.box(s*5,4.1,z,1.2,.28,5.6,'#4a5c68');}k.box(0,.06,z+.2,9,.1,1.2,'#776d60');}\r\n  if(beat===2){k.box(-4.5,1.1,z,1.6,2.2,3.4,'#a44731');for(let x=-5.1;x<-3.7;x+=.35)k.box(x,1.1,z+1.74,.04,2,.05,'#d8884d');k.cyl(4.4,1,z-1,.5,1.8,'#53616a');for(const y of [.45,.75,1.05,1.35])k.cyl(4.4,y,z-1,.53,.06,'#94a3a8');k.box(0,4.45,z-2,7,.18,1.2,'#586c78');}\r\n  if(beat===3){for(const s of [-1,1]){k.cyl(s*4.3,2.6,z-.6,.09,4.8,'#b54d35');k.box(s*4.3,4.9,z-.6,3.4,.14,.16,'#b54d35');k.box(s*3.6,2.2,z-1.8,1.3,4.2,.18,'#596d78');}const dish=k.cyl(-2.7,4.3,z,.85,.12,'#b8c5c9');dish.rotation.x=.34;k.cyl(-2.7,4.35,z,.09,1.2,'#465763');k.sphere(-2.7,4.42,z-.75,.14,'#c45438');}\r\n  // Repeated freight ribs and drums establish the coastal industrial line without signs.\r\n  for(const s of [-1,1]){k.cyl(s*5.7,1,z+1,.36,1.5,'#303b42');k.cyl(s*5.7,.43,z+1,.42,.08,'#bd5137');}\r\n },\r\n dress(root,T,h){if(h.role==='heavy'){const z=h.dims.d*.42;h.box(0,.57,z,.5,.32,.1,'#a94731');h.box(0,.42,z+.06,.46,.04,.04,'#cf9a63');}},\r\n cover:{style:'crate',color:'#b5774a',top:'#ffd24a'},\r\n roles:{\r\n  grunt:{...MOM_SECURITY},\r\n  elite:{...MOM_SECURITY,glb:'roster/02-taper-tallstalk--3d_humanoid_figure',tint:'#c03030',dress:['army','beret','gun'],hp:1,xp:2,every:[0.9,1.8]},\r\n  heavy:{...MOM_SECURITY,glb:'roster/09-monolith-tanka--boxy_humanoid_3d_model',dress:['army','helmet','vest','gun'],suit:'#c01830',hp:2,xp:3,every:[0.9,2],attack:{rocket:1}},\r\n },\r\n attack:{mix:{rocket:1,grenade:.6,laser:.6},rate:1},\r\n player:{kind:'rocket',color:'#ffb040',trail:'#d8d0c8'},\r\n scare:{role:'elite',text:'ACTION!'},\r\n areas:[\r\n  {cover:'both',wave:[{role:'grunt'},{role:'grunt'}]},\r\n  {cover:'left',wave:[{role:'grunt'},{role:'heavy'},{role:'grunt'}]},\r\n  {cover:'right',wave:[{role:'heavy'},{role:'elite'},{role:'grunt'}]},\r\n  {cover:'both',wave:[{role:'elite'},{role:'grunt'},{role:'heavy'},{role:'grunt'}]},\r\n ],\r\n};\r\n","arcade/cover/merge.mjs":"// Tiny merge for indexed geometries with position/normal/uv (no BufferGeometryUtils in the import map). Used to keep draw calls low: one mesh per material.\r\nexport function mergeGeometries(T,l){const n=k=>l.reduce((a,g)=>a+g.attributes[k].array.length,0),P=new Float32Array(n('position')),N=new Float32Array(n('normal')),U=new Float32Array(n('uv')),I=new Uint32Array(l.reduce((a,g)=>a+g.index.count,0));let p=0,u=0,i=0,v=0;\r\n for(const g of l){P.set(g.attributes.position.array,p);N.set(g.attributes.normal.array,p);U.set(g.attributes.uv.array,u);for(const x of g.index.array)I[i++]=x+v;p+=g.attributes.position.array.length;u+=g.attributes.uv.array.length;v+=g.attributes.position.count;}\r\n const o=new T.BufferGeometry();o.setAttribute('position',new T.BufferAttribute(P,3));o.setAttribute('normal',new T.BufferAttribute(N,3));o.setAttribute('uv',new T.BufferAttribute(U,2));o.setIndex(new T.BufferAttribute(I,1));return o;}\r\nexport const mergeable=g=>!!(g&&g.index&&g.attributes.position&&g.attributes.normal&&g.attributes.uv&&!g.morphAttributes?.position);\r\n","arcade/cover/portal-fallback.mjs":"// Stand-in for arcade/game-portal.mjs (another lane builds the real one with the same signature). cover.mjs imports the real file first and falls back here.\r\nexport async function playGamePortal({host=document.body,label='',swap=async()=>{}}={}){\r\n const veil=document.createElement('div');veil.className='cc-portal-fallback';veil.setAttribute('aria-hidden','true');\r\n veil.style.cssText='position:absolute;inset:0;z-index:9;pointer-events:none;display:flex;align-items:center;justify-content:center;background:radial-gradient(circle,#b026ff,#1a0830);opacity:0;transition:opacity .35s;color:#fff;font:900 22px system-ui;text-align:center;text-shadow:0 2px 8px #000';\r\n veil.textContent=label;host.append(veil);void veil.offsetWidth;veil.style.opacity='1';\r\n await new Promise(r=>setTimeout(r,380));\r\n try{await swap();}finally{await new Promise(r=>setTimeout(r,450));veil.style.opacity='0';await new Promise(r=>setTimeout(r,380));veil.remove();}\r\n}\r\n","arcade/cover/route.mjs":"// COVER ROUTE: pure path + layout for the multi-floor building (no three.js). Local piece frame: origin = piece start, forward = -z, right = +x, up = +y.\r\n// route = [4 stops x [segment names]]; stop i = the walk that ARRIVES at arena i. Between arenas the camera leaves the previous arena ('exit'), walks the segments, then steps into the next arena ('arrive').\r\nexport const SEGS=['hall','left','right','up','down','elevator','outside','door'];\r\nexport const HW=3,H=3.6,STAIR=3.6,ARENA={w:6.5,back:2.5,depth:9.5,h:7},SPEED=5.4,RAD=Math.PI/2;\r\nconst turn=m=>({dur:.8,end:{x:-m*HW,y:0,z:-HW,yaw:m*RAD},box:[-HW,HW,-2*HW,0]});\r\nexport const DEF={\r\n hall:{dur:1.2,end:{x:0,y:0,z:-6,yaw:0},box:[-HW,HW,-6,0]},left:turn(1),right:turn(-1),\r\n up:{dur:2,end:{x:0,y:STAIR,z:-6,yaw:0},box:[-HW,HW,-6,0]},down:{dur:2,end:{x:0,y:-STAIR,z:-6,yaw:0},box:[-HW,HW,-6,0]},\r\n elevator:{dur:3.2,end:{x:0,y:STAIR,z:-5.4,yaw:0},box:[-HW,HW,-5.4,0]},outside:{dur:1.4,end:{x:0,y:0,z:-7,yaw:0},box:[-HW,HW,-7,0]},door:{dur:.7,end:{x:0,y:0,z:-3,yaw:0},box:[-HW,HW,-3,0]},\r\n exit:{dur:ARENA.depth/SPEED,end:{x:0,y:0,z:-ARENA.depth,yaw:0}},arrive:{dur:.45,end:{x:0,y:0,z:-ARENA.back,yaw:0}}, // exit/arrive are path-only (geometry = the arena)\r\n};\r\nconst place=(c,l)=>{const s=Math.sin(c.yaw),co=Math.cos(c.yaw);return {x:c.x+l.x*co+l.z*s,y:c.y+l.y,z:c.z-l.x*s+l.z*co,yaw:c.yaw+l.yaw};};\r\n// -> [{pose (arena origin + heading), dur, pieces:[{type,i,start,end,local,dur,box?}]}]\r\nexport function layout(route){\r\n let c={x:0,y:0,z:0,yaw:0};const stops=[];\r\n route.forEach((segs,i)=>{const pieces=[];\r\n  const add=type=>{const d=DEF[type],local={...d.end};if(type==='elevator')local.y=(c.y>0?-1:1)*STAIR;const p={type,i,start:c,local,end:place(c,local),dur:d.dur,box:d.box};pieces.push(p);c=p.end;};\r\n  if(i>0)add('exit');segs.forEach(add);add('arrive');\r\n  stops.push({pose:c,pieces,dur:pieces.reduce((a,p)=>a+p.dur,0)});});\r\n return stops;\r\n}\r\nexport const travelDur=(route,i)=>(i>0?DEF.exit.dur:0)+DEF.arrive.dur+route[i].reduce((a,n)=>a+DEF[n].dur,0);\r\nconst ease=u=>u*u*(3-2*u),clamp=u=>Math.max(0,Math.min(1,u));\r\nexport const STEPS=18,RIDE=[.36,.76],CABZ=2.75;\r\n// Local camera path of one piece at u 0..1 -> {x,y,z,yaw,pitch,walk} (y = floor height; the view adds eye height)\r\nexport function pieceAt(p,u){\r\n u=clamp(u);const t=p.type,e=p.local;\r\n if(t==='left'||t==='right'){const a=u*RAD,m=t==='left'?1:-1;return {x:-m*HW*(1-Math.cos(a)),y:0,z:-HW*Math.sin(a),yaw:m*a,pitch:0,walk:1};}\r\n if(t==='up'||t==='down'){const f=u*STEPS,k=Math.min(STEPS-1,Math.floor(f)),y=e.y*(k+ease(f-k))/STEPS;return {x:0,y,z:e.z*u,yaw:0,pitch:Math.sign(e.y)*.09,walk:1};}\r\n if(t==='elevator'){const z=u<.26?-CABZ*u/.26:u>.84?-CABZ-(5.4-CABZ)*(u-.84)/.16:-CABZ,r=clamp((u-RIDE[0])/(RIDE[1]-RIDE[0]));return {x:0,y:e.y*ease(r),z,yaw:0,pitch:0,walk:u<.26||u>.84?1:0};}\r\n return {x:e.x*u,y:0,z:e.z*u,yaw:0,pitch:0,walk:1};\r\n}\r\n// Pose at time t (s) into stop i's travel -> world {x,y,z,yaw,pitch,walk,piece,u,idx}\r\nexport function poseAt(stop,t){\r\n let acc=0;for(let idx=0;idx<stop.pieces.length;idx++){const p=stop.pieces[idx];\r\n  if(t<acc+p.dur||idx===stop.pieces.length-1){const u=clamp((t-acc)/p.dur),l=pieceAt(p,u);return {...place(p.start,l),yaw:p.start.yaw+l.yaw,pitch:l.pitch,walk:l.walk,piece:p,u,idx};}\r\n  acc+=p.dur;}\r\n}\r\n// ---- generator\r\nconst TEMPL=[['left','up','left'],['right','door','left','hall','left'],['left','left','down'],['elevator','right'],['outside','left','door'],['right','right','up'],['door','left','outside','right','right'],['left','elevator','left'],['hall','right','down','left','left'],['up','right','door','left'],['outside','right','down','left'],['left','door','left','elevator']];\r\nconst SPECIAL=new Set(['up','down','elevator','outside']);\r\nconst net=segs=>segs.reduce((a,n)=>a+(n==='left'?1:n==='right'?-1:0),0);\r\nconst hash=s=>{let h=2166136261;for(const c of String(s))h=Math.imul(h^c.charCodeAt(0),16777619);return h>>>0;};\r\nconst rng=a=>()=>{a|=0;a=a+0x6D2B79F5|0;let t=Math.imul(a^a>>>15,1|a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};\r\nconst mirror=segs=>segs.map(n=>n==='left'?'right':n==='right'?'left':n);\r\n// footprints in world x/z (yaw is always a multiple of 90 deg) + floor interval; true when two non-neighbouring pieces collide\r\nexport function overlaps(stops){\r\n const items=[];\r\n stops.forEach((st,i)=>{\r\n  for(const p of st.pieces){if(!p.box)continue;const [x0,x1,z0,z1]=p.box,a=place(p.start,{x:x0,y:0,z:z0,yaw:0}),b=place(p.start,{x:x1,y:0,z:z1,yaw:0}),dy=p.local.y;\r\n   items.push({x0:Math.min(a.x,b.x),x1:Math.max(a.x,b.x),z0:Math.min(a.z,b.z),z1:Math.max(a.z,b.z),y0:p.start.y+Math.min(0,dy)-.2,y1:p.start.y+Math.max(0,dy)+H+.2});}\r\n  const q=st.pose,a=place(q,{x:-ARENA.w,y:0,z:-ARENA.depth,yaw:0}),b=place(q,{x:ARENA.w,y:0,z:ARENA.back,yaw:0});\r\n  items.push({x0:Math.min(a.x,b.x),x1:Math.max(a.x,b.x),z0:Math.min(a.z,b.z),z1:Math.max(a.z,b.z),y0:q.y-.2,y1:q.y+ARENA.h+.2});});\r\n const m=.4;\r\n for(let i=0;i<items.length;i++)for(let j=i+1;j<items.length;j++){const A=items[i],B=items[j];\r\n  if(A.x0+m<B.x1-m&&B.x0+m<A.x1-m&&A.z0+m<B.z1-m&&B.z0+m<A.z1-m&&A.y0<B.y1&&B.y0<A.y1)return true;}\r\n return false;\r\n}\r\nexport function checkRoute(route){\r\n if(!Array.isArray(route)||route.length!==4)return 'need 4 stops';\r\n for(const s of route)for(const n of s)if(!SEGS.includes(n))return 'bad segment '+n;\r\n if(!route.flat().some(n=>SPECIAL.has(n)))return 'needs stairs, elevator or outside';\r\n return overlaps(layout(route))?'route overlaps itself':'';\r\n}\r\nexport function genRoute(level){\r\n if(level.route)return level.route;\r\n const base=hash(level.id||'x');\r\n for(let tries=0;tries<400;tries++){\r\n  const r=rng(base+tries*7919);let last=-1;const route=[];\r\n  for(let i=0;i<4;i++){let segs,n=0;\r\n   do{let k=Math.floor(r()*TEMPL.length);if(k===last)k=(k+1)%TEMPL.length;segs=TEMPL[k];if(r()<.5)segs=mirror(segs);last=k;}\r\n   while(net(segs)%4===0&&++n<9); // each stop must turn the camera heading\r\n   route.push(segs);}\r\n  let ok=true;{const L=layout(route);for(let i=0;i<4;i++){const prev=i?L[i-1].pose.yaw:0;if(Math.abs(Math.sin((L[i].pose.yaw-prev)/2))<1e-6)ok=false;}if(L.some(s=>s.dur<4||s.dur>6.8))ok=false;const f=route.flat();if(tries<250&&!(f.some(n=>n==='up'||n==='down')&&f.includes('elevator')))ok=false;} // prefer stairs AND an elevator, relax after 250 tries\r\n  if(ok&&!checkRoute(route))return route;\r\n }\r\n return FALLBACK;\r\n}\r\nexport const FALLBACK=[['left','door','right','hall','right'],['elevator','left'],['outside','right','door'],['elevator','left']];\r\n","arcade/cover/textures.mjs":"// Procedural level textures for Crouching Crisis: canvas-painted, seeded, cached per level, no image files.\r\n// levelMaterials(T,level) -> {floor,wall,ceiling,trim,cover,metal,glass,dispose()}; decal(T,kind) -> Mesh-ready material. Node (no DOM) -> plain toon colours.\r\nconst hash=s=>{let h=2166136261;for(const c of String(s))h=Math.imul(h^c.charCodeAt(0),16777619);return h>>>0;};\r\nconst rng=seed=>{let a=seed>>>0;return()=>(a=a+0x6D2B79F5>>>0,((t=>((t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),(t^t>>>14)>>>0)/4294967296))(a)));};\r\nconst canMake=()=>typeof document!=='undefined'&&!!document.createElement;\r\nconst mk=(s)=>{const c=document.createElement('canvas');c.width=c.height=s;const g=c.getContext('2d');return g?[c,g]:null;};\r\n// painters: (g,s,r,col,mix) ; col = base css colour, mix(a,b,t) css blend\r\nconst speck=(g,s,r,n,a=.12)=>{for(let i=0;i<n;i++){g.fillStyle=r()<.5?`rgba(0,0,0,${a*r()})`:`rgba(255,255,255,${a*r()})`;g.fillRect(r()*s,r()*s,1+r()*2,1+r()*2);}};\r\nconst stain=(g,s,r,n,col='rgba(20,14,8,.18)')=>{for(let i=0;i<n;i++){const x=r()*s,y=r()*s,w=4+r()*s*.18;const gr=g.createRadialGradient(x,y,0,x,y,w);gr.addColorStop(0,col);gr.addColorStop(1,'rgba(0,0,0,0)');g.fillStyle=gr;g.fillRect(x-w,y-w,w*2,w*2);}};\r\nconst crack=(g,s,r,n,col='rgba(15,12,10,.55)')=>{g.strokeStyle=col;g.lineWidth=1;for(let i=0;i<n;i++){let x=r()*s,y=r()*s;g.beginPath();g.moveTo(x,y);for(let j=0;j<6;j++){x+=(r()-.5)*s*.2;y+=r()*s*.12;g.lineTo(x,y);}g.stroke();}};\r\nconst P={\r\n security(g,s,r,c,m){g.fillStyle=m(c,'#fff',.2);g.fillRect(0,0,s,s);g.fillStyle=m(c,'#101e34',.62);g.fillRect(0,s*.78,s,s*.22);for(let x=0;x<s;x+=s/4){g.fillStyle=m(c,'#fff',.28);g.fillRect(x+2,0,3,s*.78);g.fillStyle='rgba(30,65,100,.18)';g.fillRect(x+s/4-4,8,5,s*.72);}g.fillStyle='rgba(205,230,250,.42)';g.fillRect(0,s*.12,s,3);speck(g,s,r,90,.06);},\r\n bunker(g,s,r,c,m){g.fillStyle=m(c,'#fff',.12);g.fillRect(0,0,s,s);g.fillStyle=m(c,'#182638',.35);g.fillRect(0,0,s,s*.17);for(let y=s*.2;y<s;y+=s*.4){g.fillStyle=m(c,'#000',.14);g.fillRect(0,y,s,4);for(let x=(y%s===0?0:s*.18);x<s;x+=s*.36){g.fillStyle=m(c,'#fff',.11);g.fillRect(x+4,y+5,s*.31,s*.35);g.strokeStyle='rgba(10,18,28,.55)';g.lineWidth=3;g.strokeRect(x+4,y+5,s*.31,s*.35);}}crack(g,s,r,3,'rgba(20,28,40,.45)');speck(g,s,r,140,.08);},\r\n ruin(g,s,r,c,m){g.fillStyle=m(c,'#080d18',.55);g.fillRect(0,0,s,s);for(let i=0;i<5;i++){const x=i*s/4+(r()-.5)*s*.12,w=s*(.09+r()*.12),h=s*(.18+r()*.5);g.fillStyle=m(c,'#c9d2dc',.45);g.fillRect(x,s-h,w,h);g.fillStyle=m(c,'#000',.62);g.fillRect(x+w*.18,s-h*.65,w*.64,h*.65);g.fillStyle='rgba(255,107,42,.62)';g.fillRect(x+w*.3,s-h*.34,w*.4,3);}crack(g,s,r,6,'rgba(0,0,0,.7)');speck(g,s,r,300,.22);},\r\n stoneArch(g,s,r,c,m){g.fillStyle=m(c,'#000',.55);g.fillRect(0,0,s,s);g.fillStyle=m(c,'#d4c8ac',.3);g.fillRect(s*.12,0,s*.76,s);g.fillStyle='#1c1922';g.fillRect(s*.27,s*.22,s*.46,s*.78);g.beginPath();g.moveTo(s*.27,s*.25);g.lineTo(s*.5,s*.03);g.lineTo(s*.73,s*.25);g.closePath();g.fill();for(let y=0;y<s;y+=s*.16){g.fillStyle='rgba(255,255,255,.12)';g.fillRect(s*.12,y,s*.13,2);g.fillRect(s*.75,y,s*.13,2);}crack(g,s,r,4,'rgba(22,15,20,.55)');},\r\n ringhex(g,s,r,c,m){g.fillStyle=m(c,'#000',.42);g.fillRect(0,0,s,s);const a=s/5,h=a*Math.sqrt(3)/2;for(let y=-1;y<6;y++)for(let x=-1;x<6;x++){const cx=x*a*1.5,cy=y*h*2+(x%2)*h;g.beginPath();for(let k=0;k<6;k++){const t=k*Math.PI/3;g[k?'lineTo':'moveTo'](cx+Math.cos(t)*a*.92,cy+Math.sin(t)*a*.92);}g.closePath();g.fillStyle=m(c,r()<.5?'#fff':'#000',.08+r()*.12);g.fill();g.strokeStyle='#80d8d4';g.lineWidth=2;g.stroke();}speck(g,s,r,70,.08);},\r\n enamel(g,s,r,c,m){g.fillStyle=m(c,'#fff',.48);g.fillRect(0,0,s,s);g.strokeStyle='rgba(65,74,80,.36)';g.lineWidth=4;g.strokeRect(5,5,s-10,s-10);g.fillStyle='rgba(255,255,255,.58)';g.fillRect(12,12,s-24,7);for(let x=16;x<s;x+=s/2){g.fillStyle='rgba(50,64,74,.24)';g.fillRect(x,18,3,s-36);}speck(g,s,r,50,.05);},\r\n tiles(g,s,r,c,m){const n=4,w=s/n;g.fillStyle=m(c,'#000',.45);g.fillRect(0,0,s,s);for(let i=0;i<n;i++)for(let j=0;j<n;j++){g.fillStyle=m(c,r()<.5?'#fff':'#000',.04+r()*.1);g.fillRect(i*w+2,j*w+2,w-4,w-4);g.fillStyle='rgba(255,255,255,.14)';g.fillRect(i*w+2,j*w+2,w-4,2);g.fillStyle='rgba(0,0,0,.18)';g.fillRect(i*w+2,j*w+w-4,w-4,2);}stain(g,s,r,4);speck(g,s,r,160);},\r\n brick(g,s,r,c,m){g.fillStyle=m(c,'#d8cdb8',.45);g.fillRect(0,0,s,s);const rows=8,bh=s/rows,bw=s/3;for(let y=0;y<rows;y++)for(let x=-1;x<3;x++){const ox=(y%2)*bw/2;g.fillStyle=m(c,r()<.5?'#fff':'#000',.04+r()*.14);g.fillRect(x*bw+ox+2,y*bh+2,bw-4,bh-4);g.fillStyle='rgba(255,255,255,.12)';g.fillRect(x*bw+ox+2,y*bh+2,bw-4,2);}stain(g,s,r,5,'rgba(20,12,6,.2)');speck(g,s,r,200);},\r\n concrete(g,s,r,c,m){g.fillStyle=m(c,'#fff',.08);g.fillRect(0,0,s,s);stain(g,s,r,14,'rgba(30,26,22,.16)');stain(g,s,r,5,'rgba(255,255,255,.1)');g.strokeStyle='rgba(0,0,0,.25)';g.strokeRect(1,1,s-2,s-2);g.beginPath();g.moveTo(s/2,0);g.lineTo(s/2,s);g.stroke();crack(g,s,r,3);speck(g,s,r,300);},\r\n plates(g,s,r,c,m){g.fillStyle=m(c,'#000',.25);g.fillRect(0,0,s,s);const w=s/2;for(let i=0;i<2;i++)for(let j=0;j<2;j++){g.fillStyle=m(c,r()<.5?'#fff':'#000',.05+r()*.1);g.fillRect(i*w+3,j*w+3,w-6,w-6);g.fillStyle='rgba(255,255,255,.18)';g.fillRect(i*w+3,j*w+3,w-6,2);for(const[x,y]of[[8,8],[w-8,8],[8,w-8],[w-8,w-8]]){g.fillStyle='rgba(0,0,0,.45)';g.beginPath();g.arc(i*w+x+1,j*w+y+1,3,0,7);g.fill();g.fillStyle='rgba(255,255,255,.4)';g.beginPath();g.arc(i*w+x,j*w+y,2.4,0,7);g.fill();}}\r\n  g.strokeStyle='rgba(120,60,20,.28)';for(let i=0;i<8;i++){const x=r()*s;g.lineWidth=1+r()*2;g.beginPath();g.moveTo(x,r()*s*.5);g.lineTo(x+(r()-.5)*6,s);g.stroke();}g.strokeStyle='rgba(255,255,255,.18)';g.lineWidth=1;for(let i=0;i<14;i++){const x=r()*s,y=r()*s;g.beginPath();g.moveTo(x,y);g.lineTo(x+r()*20,y+(r()-.5)*6);g.stroke();}speck(g,s,r,120);},\r\n planks(g,s,r,c,m){const n=5,w=s/n;for(let i=0;i<n;i++){g.fillStyle=m(c,r()<.5?'#fff':'#000',.06+r()*.14);g.fillRect(i*w,0,w,s);g.strokeStyle='rgba(0,0,0,.5)';g.lineWidth=2;g.beginPath();g.moveTo(i*w,0);g.lineTo(i*w,s);g.stroke();g.strokeStyle='rgba(0,0,0,.14)';g.lineWidth=1;for(let k=0;k<9;k++){const x=i*w+3+r()*(w-6);g.beginPath();g.moveTo(x,0);g.bezierCurveTo(x+4,s*.3,x-4,s*.6,x+(r()-.5)*5,s);g.stroke();}const y=r()*s;g.fillStyle='rgba(0,0,0,.4)';g.fillRect(i*w+w/2-1,y,3,3);}stain(g,s,r,4);},\r\n carpet(g,s,r,c,m){g.fillStyle=m(c,'#000',.12);g.fillRect(0,0,s,s);for(let i=0;i<1800;i++){g.fillStyle=r()<.5?'rgba(255,255,255,.07)':'rgba(0,0,0,.09)';g.fillRect(r()*s,r()*s,1,2);}g.fillStyle=m(c,'#fff',.18);g.fillRect(0,0,s,10);g.fillRect(0,s-10,s,10);g.fillStyle=m(c,'#000',.4);g.fillRect(0,12,s,3);g.fillRect(0,s-15,s,3);for(let i=0;i<5;i++){g.save();g.translate(s*(i+.5)/5,s/2);g.rotate(Math.PI/4);g.fillStyle=m(c,'#fff',.12);g.fillRect(-9,-9,18,18);g.restore();}stain(g,s,r,5,'rgba(40,10,10,.16)');},\r\n grass(g,s,r,c,m){g.fillStyle=m(c,'#000',.1);g.fillRect(0,0,s,s);stain(g,s,r,10,'rgba(255,240,120,.1)');for(let i=0;i<1100;i++){const x=r()*s,y=r()*s;g.strokeStyle=m(c,r()<.5?'#fff':'#000',.1+r()*.28);g.lineWidth=1;g.beginPath();g.moveTo(x,y);g.lineTo(x+(r()-.5)*5,y-4-r()*7);g.stroke();}for(let i=0;i<14;i++){g.fillStyle=['#fff6a0','#fff','#ff9ad0'][i%3];g.fillRect(r()*s,r()*s,2,2);}},\r\n checker(g,s,r,c,m){const n=4,w=s/n;for(let i=0;i<n;i++)for(let j=0;j<n;j++){g.fillStyle=(i+j)%2?m(c,'#000',.5):m(c,'#fff',.4);g.fillRect(i*w,j*w,w,w);g.fillStyle='rgba(255,255,255,.12)';g.fillRect(i*w,j*w,w,3);g.fillStyle='rgba(0,0,0,.14)';g.fillRect(i*w,j*w+w-3,w,3);}stain(g,s,r,4,'rgba(60,30,10,.12)');speck(g,s,r,100);},\r\n hex(g,s,r,c,m){g.fillStyle=m(c,'#000',.5);g.fillRect(0,0,s,s);const a=s/4,h=a*Math.sqrt(3)/2;for(let y=-1;y<5;y++)for(let x=-1;x<5;x++){const cx=x*a*1.5,cy=y*h*2+(x%2)*h;g.beginPath();for(let k=0;k<6;k++){const t=k*Math.PI/3;g[k?'lineTo':'moveTo'](cx+Math.cos(t)*a*.92,cy+Math.sin(t)*a*.92);}g.closePath();g.fillStyle=m(c,r()<.5?'#fff':'#000',.04+r()*.14);g.fill();g.strokeStyle='#b27cff';g.globalAlpha=.55;g.lineWidth=1.5;g.stroke();g.globalAlpha=1;}speck(g,s,r,80);},\r\n asphalt(g,s,r,c,m){g.fillStyle=m(c,'#000',.12);g.fillRect(0,0,s,s);speck(g,s,r,900,.28);stain(g,s,r,10,'rgba(0,0,0,.22)');crack(g,s,r,5,'rgba(0,0,0,.7)');g.fillStyle='rgba(230,200,60,.5)';for(let i=0;i<2;i++)g.fillRect(s*.25+i*s*.5,0,4,s*.4);},\r\n stripes(g,s,r,c,m){g.fillStyle='#e8c020';g.fillRect(0,0,s,s);g.fillStyle='#23201c';for(let i=-2;i<8;i++){g.beginPath();g.moveTo(i*s/4,0);g.lineTo(i*s/4+s/8,0);g.lineTo(i*s/4+s/8-s/2,s);g.lineTo(i*s/4-s/2,s);g.fill();}stain(g,s,r,6,'rgba(30,20,10,.22)');speck(g,s,r,200);},\r\n wallpaper(g,s,r,c,m){g.fillStyle=m(c,'#fff',.06);g.fillRect(0,0,s,s);const n=4,w=s/n;for(let i=0;i<n;i++)for(let j=0;j<n;j++){g.save();g.translate(i*w+w/2,j*w+w/2);g.rotate(Math.PI/4);g.fillStyle=m(c,'#000',.22);g.fillRect(-w*.2,-w*.2,w*.4,w*.4);g.fillStyle=m(c,'#fff',.2);g.fillRect(-w*.06,-w*.06,w*.12,w*.12);g.restore();}g.fillStyle=m(c,'#000',.4);g.fillRect(0,s-10,s,10);g.fillStyle='rgba(255,230,160,.2)';g.fillRect(0,s-12,s,2);stain(g,s,r,8,'rgba(20,30,10,.24)');for(let i=0;i<4;i++){g.fillStyle='rgba(15,25,10,.22)';g.fillRect(r()*s,0,2+r()*3,s*(.2+r()*.5));}},\r\n cabinet(g,s,r,c,m){g.fillStyle=m(c,'#000',.4);g.fillRect(0,0,s,s);const w=s/2;for(let i=0;i<2;i++){g.fillStyle=m(c,'#fff',.04+r()*.08);g.fillRect(i*w+4,4,w-8,s-8);g.strokeStyle='rgba(0,0,0,.4)';g.lineWidth=2;g.strokeRect(i*w+12,14,w-24,s-28);g.fillStyle='#e8e2d0';g.fillRect(i*w+(i?10:w-14),s/2-9,4,18);}stain(g,s,r,4);speck(g,s,r,100);},\r\n ceiling(g,s,r,c,m){g.fillStyle=m(c,'#fff',.1);g.fillRect(0,0,s,s);g.strokeStyle='rgba(0,0,0,.35)';g.lineWidth=2;g.strokeRect(1,1,s-2,s-2);g.beginPath();g.moveTo(s/2,0);g.lineTo(s/2,s);g.moveTo(0,s/2);g.lineTo(s,s/2);g.stroke();stain(g,s,r,6,'rgba(30,20,10,.18)');speck(g,s,r,260);},\r\n glass(g,s,r,c,m){g.fillStyle=m(c,'#fff',.1);g.fillRect(0,0,s,s);g.strokeStyle='rgba(255,255,255,.5)';g.lineWidth=3;g.beginPath();g.moveTo(0,s);g.lineTo(s,0);g.moveTo(0,s*.7);g.lineTo(s*.7,0);g.stroke();},\r\n};\r\nconst DECALS={\r\n sign(g,s,r){g.fillStyle='#e8c020';g.fillRect(0,0,s,s);g.strokeStyle='#23201c';g.lineWidth=8;g.strokeRect(6,6,s-12,s-12);g.fillStyle='#23201c';g.font=`900 ${s*.5}px sans-serif`;g.textAlign='center';g.textBaseline='middle';g.fillText('!',s/2,s/2+4);},\r\n poster(g,s,r){g.fillStyle='#e9dcc0';g.fillRect(0,0,s,s);g.fillStyle='#b52a3a';g.fillRect(10,10,s-20,s*.45);g.fillStyle='#23201c';for(let i=0;i<4;i++)g.fillRect(14,s*.6+i*12,s-28-r()*30,5);g.strokeStyle='rgba(0,0,0,.4)';g.lineWidth=3;g.strokeRect(2,2,s-4,s-4);},\r\n grime(g,s,r){g.clearRect(0,0,s,s);stain(g,s,r,6,'rgba(25,18,10,.5)');},\r\n};\r\n// per-level recipe: pattern per surface (palette colour overrides optional)\r\nconst RECIPE={\r\n crisis:{floor:'concrete',wall:'security',ceiling:'ceiling',cover:'plates',metal:'plates'},\r\n ps2:{floor:'concrete',wall:'bunker',ceiling:'ceiling',cover:'plates',metal:'stripes'},\r\n judgment:{floor:'asphalt',wall:'ruin',ceiling:'concrete',cover:'asphalt',metal:'plates'},\r\n mansion:{floor:'planks',wall:'stoneArch',ceiling:'planks',cover:'planks',metal:'plates'},\r\n halo:{floor:'grass',wall:'ringhex',ceiling:'ringhex',cover:'plates',metal:'hex'},\r\n kitchen:{floor:'checker',wall:'enamel',ceiling:'ceiling',cover:'enamel',metal:'plates'},\r\n park:{floor:'grass',wall:'brick',ceiling:'concrete',cover:'planks',metal:'plates'},\r\n hive:{floor:'plates',wall:'plates',ceiling:'plates',cover:'plates',metal:'stripes'},\r\n precinct:{floor:'tiles',wall:'brick',ceiling:'ceiling',cover:'planks',metal:'plates'},\r\n};\r\nconst cache=new Map(),SIZE={floor:256,wall:256,ceiling:128,trim:128,cover:128,metal:128,glass:64};\r\nexport function makeTexture(T,pattern,color,size=128,seed=1,repeat=[1,1]){\r\n if(!canMake())return null;const cv=mk(size);if(!cv||!T.CanvasTexture)return null;const [c,g]=cv,col=new T.Color(color);\r\n const mix=(a,b,t)=>new T.Color(a).lerp(new T.Color(b),t).getStyle();\r\n try{(P[pattern]||P.concrete)(g,size,rng(seed),col.getStyle(),mix);}catch{return null;}\r\n const t=new T.CanvasTexture(c);t.wrapS=t.wrapT=T.RepeatWrapping;t.repeat.set(...repeat);t.colorSpace=T.SRGBColorSpace;t.anisotropy=4;return t;\r\n}\r\nexport function levelMaterials(T,level){\r\n const id=level?.id||'x',key=id+'|'+(level?.palette?.floor||'');if(cache.has(key)&&!cache.get(key).disposed)return cache.get(key);\r\n const p=level?.palette||{},rc=RECIPE[id]||RECIPE.crisis,seed=hash(id),tex=[],mats={};\r\n const col={floor:p.floor||'#777',wall:p.wall||'#888',ceiling:p.wall||'#888',trim:p.trim||'#bbb',cover:p.cover||'#a77',metal:p.coverTop||'#aaa',glass:'#9fd8ff'};\r\n const rep={floor:[3,20],wall:[10,1],ceiling:[3,20],trim:[2,2],cover:[1,1],metal:[1,1],glass:[1,1]};\r\n for(const k of ['floor','wall','ceiling','trim','cover','metal','glass']){\r\n  const pat=k==='trim'?'plates':k==='glass'?'glass':rc[k],map=makeTexture(T,pat,col[k],SIZE[k],seed+k.length*97,rep[k]);if(map)tex.push(map);\r\n  mats[k]=new T.MeshToonMaterial(map?{map,color:'#ffffff'}:{color:col[k]});\r\n  if(k==='glass'){mats[k].transparent=true;mats[k].opacity=.45;mats[k].depthWrite=false;}\r\n }\r\n const out={...mats,id,patterns:rc,disposed:false,dispose(){if(out.disposed)return;out.disposed=true;for(const m of Object.values(mats))m.dispose();for(const t of tex)t.dispose();cache.delete(key);}};\r\n cache.set(key,out);return out;\r\n}\r\nexport function decal(T,kind='grime',seed=7){\r\n if(!canMake()||!DECALS[kind])return new T.MeshBasicMaterial({color:'#555',transparent:true,opacity:.3,depthWrite:false});\r\n const cv=mk(128);if(!cv||!T.CanvasTexture)return new T.MeshBasicMaterial({color:'#555',transparent:true,opacity:.3});\r\n DECALS[kind](cv[1],128,rng(seed));const t=new T.CanvasTexture(cv[0]);t.colorSpace=T.SRGBColorSpace;\r\n const m=new T.MeshBasicMaterial({map:t,transparent:true,depthWrite:false,polygonOffset:true,polygonOffsetFactor:-2});const d=m.dispose.bind(m);m.dispose=()=>{t.dispose();d();};return m;\r\n}\r\n","arcade/game-portal.mjs":"// One level/biome portal for every game: the user's chosen grimoire portal (board + colours) via the existing wormhole.\r\n// 'in' covers the screen, swap() runs while it is covered, 'out' opens it. Camera and pose callbacks are never touched.\r\nimport {readLook} from '../modules/portal/portal-look.mjs';\r\nexport const HALF_MS=600;\r\nconst reduced=()=>!!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\r\nconst portal=()=>{const p=globalThis.myr5Portal;return p&&!p.disposed&&typeof p.playWormhole==='function'?p:null;};\r\n// True when a hop will play the portal (mounted on demand at the first hop). Without a DOM or with reduced motion it is false: callers swap at once.\r\nexport const gamePortalReady=()=>!!globalThis.document?.body&&!reduced();\r\nlet chain=Promise.resolve(); // one wormhole canvas is shared: repeated calls queue, never overlap\r\nexport function playGamePortal({host,label='',swap=async()=>{}}={}){\r\n if(!gamePortalReady()){try{return Promise.resolve(swap());}catch(e){return Promise.reject(e);}} // reduced motion / no portal: instant swap\r\n const run=async()=>{\r\n  if(!portal())await import('../modules/portal/portal-entry.mjs').then(m=>m.ensurePortalMounted()).catch(()=>{});\r\n  const p=portal();if(!p)return swap(); // mount failed: plain swap\r\n  let note=null;\r\n  if(label){note=document.createElement('div');note.setAttribute('role','status');note.textContent=label;note.style.cssText='position:fixed;left:0;right:0;bottom:22vh;text-align:center;color:#fff;font:800 17px system-ui;letter-spacing:3px;text-shadow:0 2px 8px #000;pointer-events:none;z-index:10001';(host||document.body).append(note);}\r\n  const color=readLook().strip;\r\n  try{await p.playWormhole({direction:'in',minMs:HALF_MS,color,host:host||document.body});await swap();}\r\n  finally{\r\n   try{await p.playWormhole({direction:'out',minMs:HALF_MS,color,host:host||document.body});}catch{}\r\n   note?.remove();\r\n   await p.releaseTunnel?.(); // hand the shared tunnel context back: a game never keeps an extra WebGL context\r\n  }\r\n };\r\n const next=chain.then(run);chain=next.catch(()=>{});return next;\r\n}\r\n","arcade/hold/blizzard.mjs":"// Blizzard hold model and Canvas2D renderer. Hold eligibility is delegated to\r\n// createEcosystem so this style shares the exact stillness, movement and stale gates.\r\nimport { createEcosystem, STILL_SECONDS } from './ecosystem.mjs';\r\nimport { supportContacts } from '../power/power.mjs';\r\n\r\nexport const MAX_ICE_EDGES = 20;\r\nexport const MAX_SNOW = 48;\r\nconst CONFIDENCE = 0.5;\r\nconst GRAPH = [\r\n  [11, 12], [11, 13], [13, 15], [12, 14], [14, 16],\r\n  [11, 23], [12, 24], [23, 24],\r\n  [23, 25], [25, 27], [27, 29], [29, 31],\r\n  [24, 26], [26, 28], [28, 30], [30, 32],\r\n];\r\n\r\nconst clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));\r\nconst pointAt = (points, i) => Array.isArray(points) ? points[i] : points?.[i] ?? points?.[String(i)];\r\n\r\nfunction visiblePoints(points, w, h) {\r\n  const out = new Map();\r\n  for (let i = 0; i <= 32; i++) {\r\n    const p = pointAt(points, i);\r\n    const visibility = Number(p?.visibility ?? p?.score ?? p?.confidence);\r\n    if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y) || !Number.isFinite(visibility) || visibility < CONFIDENCE || p.x < 0 || p.x > 1 || p.y < 0 || p.y > 1) continue;\r\n    out.set(i, { x: Math.round(p.x * w), y: Math.round(p.y * h) });\r\n  }\r\n  return out;\r\n}\r\n\r\nfunction seeded(seed = 1) {\r\n  let s = seed | 0;\r\n  return () => { s = s + 0x6D2B79F5 | 0; let t = Math.imul(s ^ s >>> 15, 1 | s); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; };\r\n}\r\n\r\nfunction bodyBounds(points, w, h) {\r\n  const joints = [...points.entries()].filter(([i]) => [11, 12, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32].includes(i)).map(([, p]) => p);\r\n  if (!joints.length) return { x: w / 2, y: h / 2, rx: w * 0.12, ry: h * 0.2 };\r\n  const xs = joints.map(p => p.x), ys = joints.map(p => p.y);\r\n  const left = Math.min(...xs), right = Math.max(...xs), top = Math.min(...ys), bottom = Math.max(...ys);\r\n  return { x: (left + right) / 2, y: (top + bottom) / 2, rx: clamp((right - left) * 0.34, 12, w * 0.14), ry: clamp((bottom - top) * 0.34, 18, h * 0.14) };\r\n}\r\n\r\nfunction graphEdges(points, contacts, elapsed) {\r\n  const support = new Set(contacts.filter(c => Number.isInteger(c.index) && points.has(c.index)).map(c => c.index));\r\n  if (!support.size) return [];\r\n  const adj = new Map();\r\n  const valid = GRAPH.filter(([a, b]) => points.has(a) && points.has(b));\r\n  for (const [a, b] of valid) {\r\n    if (!adj.has(a)) adj.set(a, []); if (!adj.has(b)) adj.set(b, []);\r\n    adj.get(a).push(b); adj.get(b).push(a);\r\n  }\r\n  const distance = new Map([...support].map(i => [i, 0])), queue = [...support];\r\n  for (let q = 0; q < queue.length; q++) {\r\n    const node = queue[q];\r\n    for (const next of adj.get(node) || []) if (!distance.has(next)) { distance.set(next, distance.get(node) + 1); queue.push(next); }\r\n  }\r\n  const out = [];\r\n  for (const [a, b] of valid) {\r\n    if (!distance.has(a) || !distance.has(b)) continue;\r\n    const rank = Math.min(distance.get(a), distance.get(b));\r\n    const delay = rank * 0.88;\r\n    const growth = clamp((elapsed - delay + 0.05) / 0.72, 0, 1);\r\n    if (growth <= 0) continue;\r\n    const from = distance.get(a) <= distance.get(b) ? a : b;\r\n    const to = from === a ? b : a;\r\n    const p1 = points.get(from), p2 = points.get(to);\r\n    out.push({ from, to, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, growth, alpha: 0.72 + growth * 0.18 });\r\n  }\r\n  return out.slice(0, MAX_ICE_EDGES);\r\n}\r\n\r\nfunction readReducedMotion() {\r\n  try { return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches; } catch { return false; }\r\n}\r\n\r\nexport function createBlizzard({ w = 360, h = 640, seed = 1, stillSeconds = STILL_SECONDS } = {}) {\r\n  const stillness = createEcosystem({ w, h, seed, spawnEntities: false, stillSeconds });\r\n  const reducedMotion = readReducedMotion();\r\n  const rand = seeded(seed ^ 0xB11A2D);\r\n  const count = reducedMotion ? 16 : 32;\r\n  const snow = Array.from({ length: Math.min(MAX_SNOW, count) }, () => ({ x: 0, y: 0, size: 1 + Math.floor(rand() * 3), alpha: 0 }));\r\n  const paths = snow.map(() => ({ phase: rand() * Math.PI * 2, rx: 0.35 + rand() * 0.75, ry: 0.35 + rand() * 0.8, speed: (2.8 + rand() * 2.8) * (reducedMotion ? 0.28 : 1), direction: rand() < 0.5 ? -1 : 1, alpha: 0.3 + rand() * 0.42 }));\r\n  const state = { w, h, phase: 'waiting', stillSeconds: 0, still: stillSeconds, iceEdges: [], snow };\r\n  let currentPoints = new Map(), currentContacts = [], origin = { x: w / 2, y: h / 2, rx: w * 0.12, ry: h * 0.2 };\r\n  let elapsed = 0, fadeSpeed = reducedMotion ? 0.65 : 1.35;\r\n\r\n  function placeSnow(alphaScale = 1) {\r\n    for (let i = 0; i < snow.length; i++) {\r\n      const path = paths[i], angle = path.phase + elapsed * path.speed * path.direction;\r\n      snow[i].x = Math.round(origin.x + Math.cos(angle) * origin.rx * path.rx);\r\n      snow[i].y = Math.round(origin.y + Math.sin(angle * 1.17) * origin.ry * path.ry);\r\n      snow[i].alpha = Math.max(0, Math.min(0.72, alphaScale * path.alpha));\r\n    }\r\n  }\r\n\r\n  function fade() {\r\n    if (state.phase !== 'growing') return;\r\n    state.phase = 'retreating';\r\n    // Preserve coordinates during the departure fade.\r\n  }\r\n\r\n  function clearVisuals() {\r\n    state.iceEdges.length = 0;\r\n    for (const flake of snow) flake.alpha = 0;\r\n    state.phase = 'waiting';\r\n    state.stillSeconds = stillness.state.stillSeconds;\r\n  }\r\n\r\n  function pose({ points, now, validHold, mode } = {}) {\r\n    const wasGrowing = state.phase === 'growing';\r\n    stillness.pose({ points, now, validHold });\r\n    state.stillSeconds = stillness.state.stillSeconds;\r\n    currentPoints = visiblePoints(points, w, h);\r\n    if (validHold && currentPoints.size) origin = bodyBounds(currentPoints, w, h);\r\n    const ready = stillness.state.phase === 'growing';\r\n\r\n    if (ready) {\r\n      if (!wasGrowing) { state.iceEdges.length = 0; state.phase = 'growing'; }\r\n      currentContacts = supportContacts(mode, points, w, h);\r\n      const age = Math.max(0, state.stillSeconds - stillSeconds);\r\n      state.iceEdges = graphEdges(currentPoints, currentContacts, age);\r\n      placeSnow(Math.min(1, 0.38 + age * (reducedMotion ? 0.1 : 0.18)));\r\n    } else {\r\n      currentContacts = [];\r\n      if (wasGrowing) fade();\r\n      if (!validHold && state.phase === 'waiting') clearVisuals();\r\n    }\r\n    return state;\r\n  }\r\n\r\n  function step(dt = 0) {\r\n    const raw = Number.isFinite(dt) && dt > 0 ? dt : 0;\r\n    stillness.step(raw);\r\n    elapsed += Math.min(0.05, raw) * (reducedMotion ? 0.35 : 1);\r\n    state.stillSeconds = stillness.state.stillSeconds;\r\n    if (state.phase === 'growing' && stillness.state.phase !== 'growing') fade();\r\n    if (state.phase === 'growing') {\r\n      placeSnow(Math.min(1, 0.38 + Math.max(0, state.stillSeconds - stillSeconds) * (reducedMotion ? 0.1 : 0.18)));\r\n    } else if (state.phase === 'retreating') {\r\n      for (const edge of state.iceEdges) edge.alpha = Math.max(0, edge.alpha - fadeSpeed * Math.min(0.05, raw));\r\n      for (const flake of snow) flake.alpha = Math.max(0, flake.alpha - fadeSpeed * Math.min(0.05, raw));\r\n      if (!state.iceEdges.some(e => e.alpha > 0.01) && !snow.some(f => f.alpha > 0.01)) clearVisuals();\r\n    }\r\n    return state;\r\n  }\r\n\r\n  function reset() {\r\n    stillness.reset(); state.phase = 'waiting'; state.stillSeconds = 0; state.iceEdges.length = 0;\r\n    for (const flake of snow) { flake.x = 0; flake.y = 0; flake.alpha = 0; }\r\n    currentPoints.clear(); currentContacts = []; elapsed = 0; origin = { x: w / 2, y: h / 2, rx: w * 0.12, ry: h * 0.2 };\r\n    return state;\r\n  }\r\n\r\n  return { state, pose, step, reset };\r\n}\r\n\r\nconst ASSET = '/arcade/hold/assets/19_freezing_spritesheet.png';\r\nlet freezeImage = null, spriteCache = new Map(), loadStarted = false;\r\nfunction preload() {\r\n  if (loadStarted || typeof Image === 'undefined') return;\r\n  loadStarted = true;\r\n  const image = new Image();\r\n  image.onload = () => {\r\n    if (typeof document === 'undefined') return;\r\n    for (let frame = 0; frame < 10; frame++) {\r\n      // FreeFX frames are 100px cells with a small, consistent transparent border.\r\n      // Inspect alpha in the browser so the original pixels remain the source of truth.\r\n      const probe = document.createElement('canvas'); probe.width = probe.height = 100;\r\n      const pctx = probe.getContext('2d', { willReadFrequently: true }); if (!pctx) continue;\r\n      pctx.drawImage(image, frame * 100, 0, 100, 100, 0, 0, 100, 100);\r\n      let pixels;\r\n      try { pixels = pctx.getImageData(0, 0, 100, 100).data; } catch { continue; }\r\n      let left = 100, top = 100, right = -1, bottom = -1;\r\n      for (let y = 0; y < 100; y++) for (let x = 0; x < 100; x++) {\r\n        if (pixels[(y * 100 + x) * 4 + 3] === 0) continue;\r\n        if (x < left) left = x; if (x > right) right = x;\r\n        if (y < top) top = y; if (y > bottom) bottom = y;\r\n      }\r\n      if (right < left || bottom < top) continue;\r\n      const sw = right - left + 1, sh = bottom - top + 1;\r\n      const scale = 52 / Math.max(sw, sh), dw = Math.max(1, Math.round(sw * scale)), dh = Math.max(1, Math.round(sh * scale));\r\n      const canvas = document.createElement('canvas'); canvas.width = canvas.height = 64;\r\n      const g = canvas.getContext('2d'); if (!g) continue;\r\n      g.imageSmoothingEnabled = false;\r\n      g.drawImage(image, frame * 100 + left, top, sw, sh, Math.floor((64 - dw) / 2), Math.floor((64 - dh) / 2), dw, dh);\r\n      spriteCache.set(frame, canvas);\r\n    }\r\n    freezeImage = image;\r\n  };\r\n  image.onerror = () => { freezeImage = null; };\r\n  image.src = ASSET;\r\n}\r\n\r\nfunction drawSnow(ctx, state, start, end) {\r\n  for (let i = start; i < end; i++) {\r\n    const flake = state.snow[i]; if (!flake || flake.alpha <= 0.01) continue;\r\n    ctx.globalAlpha = flake.alpha;\r\n    ctx.fillStyle = i % 2 ? '#b8f6ff' : '#f3fdff';\r\n    ctx.fillRect(Math.round(flake.x), Math.round(flake.y), flake.size, flake.size);\r\n  }\r\n}\r\n\r\nfunction drawIceEdge(ctx, edge) {\r\n  const dx = edge.x2 - edge.x1, dy = edge.y2 - edge.y1, length = Math.hypot(dx, dy), growth = clamp(edge.growth, 0, 1);\r\n  const visibleLength = length * growth, steps = Math.max(1, Math.ceil(visibleLength / 3));\r\n  ctx.globalAlpha = clamp(edge.alpha, 0, 1);\r\n  for (let i = 0; i <= steps; i++) {\r\n    const t = length <= 0 ? 0 : Math.min(growth, i * 3 / length);\r\n    const x = Math.round(edge.x1 + dx * t), y = Math.round(edge.y1 + dy * t), size = i % 3 === 0 ? 3 : 2;\r\n    ctx.fillStyle = i % 2 ? '#6ddfff' : '#d8faff';\r\n    ctx.fillRect(x - 1, y - 1, size, size);\r\n  }\r\n}\r\n\r\nfunction drawContactAccent(ctx, state, edge) {\r\n  if (!freezeImage || edge.growth <= 0) return;\r\n  const frame = Math.floor(Math.max(0, state.stillSeconds - (state.still ?? STILL_SECONDS)) * 2) % 10;\r\n  const sprite = spriteCache.get(frame);\r\n  if (!sprite) return;\r\n  ctx.globalAlpha = Math.min(0.55, edge.growth * 0.5, clamp(edge.alpha, 0, 1) * 0.6);\r\n  ctx.imageSmoothingEnabled = false;\r\n  ctx.drawImage(sprite, Math.round(edge.x1 - 7), Math.round(edge.y1 - 7), 14, 14);\r\n}\r\n\r\nexport function renderBlizzard(ctx, state) {\r\n  if (!ctx || !state) return;\r\n  preload();\r\n  ctx.imageSmoothingEnabled = false;\r\n  ctx.globalCompositeOperation = 'source-over';\r\n  const snow = Array.isArray(state.snow) ? state.snow : [];\r\n  drawSnow(ctx, state, 0, Math.floor(snow.length / 2));\r\n  const accented = new Set();\r\n  for (const edge of Array.isArray(state.iceEdges) ? state.iceEdges : []) {\r\n    if (!Number.isFinite(edge.x1) || !Number.isFinite(edge.y1) || !Number.isFinite(edge.x2) || !Number.isFinite(edge.y2)) continue;\r\n    if (!accented.has(edge.from)) { drawContactAccent(ctx, state, edge); accented.add(edge.from); }\r\n    drawIceEdge(ctx, edge);\r\n  }\r\n  drawSnow(ctx, state, Math.floor(snow.length / 2), snow.length);\r\n  ctx.globalAlpha = 1;\r\n}\r\n","arcade/hold/camera-hold.mjs":"import {createEcosystem} from './ecosystem.mjs';\r\nimport {renderEcosystem} from './render.mjs';\r\nimport {createBlizzard,renderBlizzard} from './blizzard.mjs';\r\nimport {renderPower,preloadAura} from '../power/power.mjs';\r\nimport {createGrowth,AURA_TUNING,layerOpacity} from '../power/aura-growth.mjs';\r\nexport {preloadAuraForMode} from '../power/power.mjs';\r\nconst AURA_LAYER_OPACITY=AURA_TUNING.LAYER_OPACITY;\r\nimport {EFFECT_OPTION_BY_ID,effectGroupForExercise,readEffectChoices,resolveExerciseEffect} from '../power/aura-options.mjs';\r\nimport {createOverlayCanvas,mapPointsToOverlay,effectQuality} from '../workout-effects.mjs';\r\nimport {createAuraBody} from '../power/aura-body.mjs';\r\n\r\nconst LOGICAL_WIDTH=360;\r\nfunction randomFor(seed=1){let value=(Number(seed)>>>0)||0x9e3779b9;return()=>{value^=value<<13;value^=value>>>17;value^=value<<5;return(value>>>0)/0x100000000;};}\r\nexport function mountCameraHold({host,sound=()=>{},onXp=()=>{},seed=Date.now(),style='living',mode=null,quality=effectQuality}={}){\r\n if(!host)throw new TypeError('A camera overlay host is required.');\r\n const holdStyle=style==='blizzard'?'blizzard':'living';\r\n const layer=createOverlayCanvas(host,'hold-ecosystem-canvas',{opacity:AURA_LAYER_OPACITY,max:1}),canvas=layer.canvas,ctx=canvas.getContext('2d',{alpha:true});\r\n const growth=createGrowth();let poseAt=-Infinity,holdValid=false;\r\n // Sheet auras on a hold share the power layer's body anchoring: filtered + predicted points, glow, fade out.\r\n const body=createAuraBody({host,quality});const aura={w:LOGICAL_WIDTH,h:1,route:'aura',active:false,level:0,speed:0,pulse:0,contacts:[],points:[],time:0,effect:null};\r\n let engine=null,w=LOGICAL_WIDTH,h=1,raf=0,lastFrame=0,lastPose=null,disposed=false,resizeObserver=null,selectedMode=mode,effectChoices=readEffectChoices();\r\n const random=randomFor(seed);let selectedEffect=resolveExerciseEffect(selectedMode,effectChoices,random);preloadAura(selectedEffect);\r\n const createEngine=()=>holdStyle==='blizzard'?createBlizzard({w,h,seed,stillSeconds:AURA_TUNING.HOLD_FIRST_S}):createEcosystem({w,h,seed,stillSeconds:AURA_TUNING.HOLD_FIRST_S,ecology:EFFECT_OPTION_BY_ID.get(selectedEffect)?.ecology??null});\r\n function resize(){\r\n  if(disposed)return;const rect=host.getBoundingClientRect();if(!(rect.width>0&&rect.height>0))return;\r\n  layer.resize();const nextH=Math.max(1,Math.round(LOGICAL_WIDTH*rect.height/rect.width));\r\n  const changed=!engine||nextH!==h;w=LOGICAL_WIDTH;h=nextH;\r\n  if(changed){engine?.reset();engine=createEngine();}\r\n  ctx.setTransform(canvas.width/w,0,0,canvas.height/h,0,0);ctx.imageSmoothingEnabled=false;\r\n }\r\n const geometryOf=detail=>({videoWidth:detail.width,videoHeight:detail.height,hostWidth:w,hostHeight:h,mirrored:detail.mirrored});\r\n function map(detail){return mapPointsToOverlay(detail.points,geometryOf(detail));}\r\n function paint(now){\r\n  if(disposed)return;const dt=lastFrame?Math.max(0,(now-lastFrame)/1000):0;if(lastFrame)quality.frame(now-lastFrame);lastFrame=now;\r\n  engine.step(dt);growth.step(dt);{const o=String(layerOpacity(growth.state.level).toFixed(3));if(canvas.style.opacity!==o)canvas.style.opacity=o;}ctx.setTransform(canvas.width/w,0,0,canvas.height/h,0,0);ctx.clearRect(0,0,w,h);\r\n  const option=EFFECT_OPTION_BY_ID.get(selectedEffect),fade=holdStyle==='blizzard'\r\n    ? Math.max(0,...(engine.state.iceEdges??[]).map(edge=>edge.alpha??0),...(engine.state.snow??[]).map(flake=>flake.alpha??0))\r\n    : Math.max(0,...(engine.state.entities??[]).map(entity=>entity.alpha??0));\r\n  if(option?.ecology)renderEcosystem(ctx,engine.state);\r\n  else if(holdStyle==='blizzard'&&selectedEffect==='blizzard')renderBlizzard(ctx,engine.state);\r\n  else if(option?.asset){\r\n   // Sheet auras grow on the shared hold clock (visible at once), not on the slow ecology spawn; they fade out\r\n   // (~250 ms) when the hold breaks or tracking stops instead of vanishing.\r\n   const live=holdValid&&now-poseAt<1000;\r\n   if(live){aura.active=true;aura.level=growth.state.level;aura.speed=growth.state.speed;aura.effect=selectedEffect;}\r\n   aura.w=w;aura.h=h;aura.time=now/1000;\r\n   const points=body.points(now),fadeAlpha=body.draw(now,live?aura:null,points);\r\n   if(fadeAlpha>0&&aura.active)renderPower(ctx,aura,{points,alpha:fadeAlpha});else if(fadeAlpha<=0)aura.active=false;\r\n  }\r\n  if(!option?.asset&&body.alpha>0)body.draw(now,null,body.points(now));\r\n  raf=requestAnimationFrame(paint);\r\n }\r\n resize();\r\n if(typeof ResizeObserver==='function'){resizeObserver=new ResizeObserver(resize);resizeObserver.observe(host);}else window.addEventListener('resize',resize);\r\n raf=requestAnimationFrame(paint);\r\n function pose(detail={}){\r\n  if(disposed)return;\r\n  resize();const points=map(detail),nextMode=detail.workout?.mode??selectedMode;\r\n  if(nextMode!==selectedMode){selectedMode=nextMode;selectedEffect=resolveExerciseEffect(selectedMode,effectChoices,random);preloadAura(selectedEffect);engine?.reset();engine=createEngine();}\r\n  lastPose={points,now:detail.now,validHold:!!detail.validHold,mode:selectedMode};\r\n  body.pose(points,Number.isFinite(detail.frameMs)?detail.frameMs:detail.now,detail.mask,geometryOf(detail));\r\n  poseAt=performance.now();holdValid=lastPose.validHold;\r\n  if(holdValid)growth.update({now:detail.now,points,kind:'hold',valid:true,target:detail.workout?.target});else growth.reset();\r\n  engine.pose(lastPose);\r\n }\r\n function invalidate(){holdValid=false;growth.reset();if(!disposed&&engine){lastPose={points:lastPose?.points,now:lastPose?.now,validHold:false};engine.pose({validHold:false});}}\r\n function choiceChanged(event){if(disposed)return;const group=effectGroupForExercise(selectedMode),before=group?effectChoices[group]:null;effectChoices=readEffectChoices();if(event?.detail?.group&&event.detail.group!==group)return;if(!event?.detail?.group&&group&&before===effectChoices[group])return;const next=resolveExerciseEffect(selectedMode,effectChoices,random);if(next===selectedEffect)return;selectedEffect=next;preloadAura(next);engine?.reset();engine=createEngine();if(lastPose)engine.pose(lastPose);}\r\n globalThis.addEventListener?.('myr5:exercise-effect-choice-change',choiceChanged);\r\n const storageChanged=event=>{if(event?.key==='myr5-exercise-effect-choices-v1')choiceChanged();};\r\n globalThis.addEventListener?.('storage',storageChanged);\r\n function stop(){if(disposed)return;disposed=true;cancelAnimationFrame(raf);resizeObserver?.disconnect();window.removeEventListener('resize',resize);globalThis.removeEventListener?.('myr5:exercise-effect-choice-change',choiceChanged);globalThis.removeEventListener?.('storage',storageChanged);engine?.reset();body.dispose();layer.dispose();}\r\n return {get engine(){return engine;},pose,stop,invalidate,get effect(){return selectedEffect;}};\r\n}\r\n","arcade/hold/ecosystem.mjs":"// Deterministic, renderer-independent ecology engine for tracked workout holds.\r\n// The small PRNG is adapted from arcade/slice/core.mjs so this feature adds no dependency.\r\nexport const STILL_SECONDS = 5;\r\nexport const MAX_POSE_GAP_MS = 1000;\r\nexport const MIN_VISIBILITY = 0.5;\r\nexport const MAX_ENTITIES = 36;\r\nexport const MAX_DEBRIS = 96;\r\nexport const CRACK_SECONDS = 0.3;\r\n\r\nconst clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));\r\nfunction rng(seed = 1) {\r\n  return () => { seed |= 0; seed = seed + 0x6D2B79F5 | 0; let t = Math.imul(seed ^ seed >>> 15, 1 | seed); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; };\r\n}\r\nconst PLANTS = ['fern', 'vine', 'flower', 'mushroom', 'grass', 'reed'];\r\nconst ANIMALS = ['rabbit', 'fox', 'bear', 'deer', 'wolf', 'boar'];\r\nconst MINERALS = ['rock', 'crystal'];\r\nconst COMPARABLE = [11, 12, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28];\r\nconst TORSO = [11, 12, 23, 24];\r\nconst LIMBS = [13, 14, 15, 16, 25, 26, 27, 28];\r\nconst BODY_ATTACHMENTS = [0, 11, 12, 13, 14, 15, 16];\r\n\r\nfunction readPoint(points, i) { return Array.isArray(points) ? points[i] : points?.[i] ?? points?.[String(i)]; }\r\nfunction normalized(points, w, h, indices = COMPARABLE) {\r\n  const out = new Map();\r\n  for (const i of indices) {\r\n    const p = readPoint(points, i), visibility = Number(p?.visibility ?? p?.score ?? p?.confidence);\r\n    if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y) || !Number.isFinite(visibility) || visibility < MIN_VISIBILITY) continue;\r\n    out.set(i, { x: p.x * w, y: p.y * h });\r\n  }\r\n  return out;\r\n}\r\nfunction torsoScale(points) {\r\n  const pairs = [[11, 12], [23, 24], [11, 23], [12, 24]];\r\n  const lengths = pairs.map(([a, b]) => points.has(a) && points.has(b) ? Math.hypot(points.get(a).x - points.get(b).x, points.get(a).y - points.get(b).y) : 0).filter(n => n > 0);\r\n  if (!lengths.length) return 0;\r\n  // Median-ish robust estimate: vertical torso length and shoulder/hip widths all describe body scale.\r\n  lengths.sort((a, b) => a - b);\r\n  return lengths[Math.floor(lengths.length / 2)] || lengths[0];\r\n}\r\nfunction enoughPose(points) {\r\n  return TORSO.filter(i => points.has(i)).length >= 2 && LIMBS.filter(i => points.has(i)).length >= 2 && points.size >= 4;\r\n}\r\n\r\nexport function createEcosystem({ w = 360, h = 640, seed = 1, onEvent = () => {}, spawnEntities = true, ecology = null, stillSeconds = STILL_SECONDS } = {}) {\r\n  const STILL = stillSeconds; // default 5 s; the camera layer passes a short first-growth time so something shows at once\r\n  const rand = rng(seed), choose = list => list[Math.floor(rand() * list.length)];\r\n  const state = { w, h, phase: 'waiting', stillSeconds: 0, groundY: h - 6, entities: [], debris: [] };\r\n  let reference = null, previous = null, lastTimestamp = null, poseAge = Infinity;\r\n  let nextGrowth = STILL, nextId = 1, elapsed = 0, growthOrdinal = 0;\r\n\r\n  const emit = (type, payload) => { try { onEvent(type, payload); } catch { /* cosmetic observers cannot break tracking */ } };\r\n  function beginDeparture() {\r\n    let changed = false;\r\n    for (const e of state.entities) {\r\n      if (['retreating', 'fleeing', 'cracking'].includes(e.phase)) continue;\r\n      e.anchor = null;\r\n      e.phase = e.kind === 'plant' ? 'retreating' : e.kind === 'animal' ? 'fleeing' : 'cracking';\r\n      e.phaseTime = 0;\r\n      if (e.kind === 'animal') e.facing = e.x < w / 2 ? -1 : 1;\r\n      changed = true;\r\n    }\r\n    if (changed) emit('retreat', { reason: 'movement-or-invalid-pose' });\r\n    state.phase = changed || state.entities.some(e => ['retreating', 'fleeing', 'cracking'].includes(e.phase)) ? 'retreating' : 'waiting';\r\n  }\r\n  function clearEligibility({ depart = true } = {}) {\r\n    state.stillSeconds = 0;\r\n    reference = previous = null;\r\n    lastTimestamp = null;\r\n    poseAge = Infinity;\r\n    nextGrowth = STILL;\r\n    if (depart) beginDeparture(); else state.phase = 'waiting';\r\n  }\r\n  function getGround(points) {\r\n    const lower = [27, 28, 29, 30, 31, 32].map(i => readPoint(points, i))\r\n      .filter(p => p && Number.isFinite(p.x) && Number.isFinite(p.y) && Number(p.visibility ?? p.score ?? p.confidence) >= MIN_VISIBILITY);\r\n    if (!lower.length) return h - 6;\r\n    return clamp(Math.max(...lower.map(p => p.y * h)) + 2, h * 0.35, h - 2);\r\n  }\r\n  function pickAnchor(points) {\r\n    const choices = BODY_ATTACHMENTS.map(i => [i, readPoint(points, i)])\r\n      .filter(([, p]) => p && Number.isFinite(p.x) && Number.isFinite(p.y) && Number(p.visibility ?? p.score ?? p.confidence) >= MIN_VISIBILITY);\r\n    if (choices.length && rand() < 0.65) {\r\n      const [index, p] = choose(choices);\r\n      return { type: 'joint', index, dx: (rand() - 0.5) * 18, dy: (rand() - 0.5) * 12, targetX: p.x * w, targetY: p.y * h };\r\n    }\r\n    return { type: 'ground', x: w * (0.12 + rand() * 0.76) };\r\n  }\r\n  function anchorTarget(anchor, points) {\r\n    if (anchor?.type === 'joint') {\r\n      const p = readPoint(points, anchor.index);\r\n      if (p && Number.isFinite(p.x) && Number.isFinite(p.y) && Number(p.visibility ?? p.score ?? p.confidence) >= MIN_VISIBILITY) {\r\n        anchor.targetX = p.x * w; anchor.targetY = p.y * h;\r\n      }\r\n      return { x: anchor.targetX + anchor.dx, y: anchor.targetY + anchor.dy };\r\n    }\r\n    return { x: anchor?.x ?? w / 2, y: state.groundY };\r\n  }\r\n  function spawnEntity(points) {\r\n    if (state.entities.length >= MAX_ENTITIES) return;\r\n    const anchor = pickAnchor(points), target = anchorTarget(anchor, points), roll = rand();\r\n    const kind = ['plant', 'animal', 'mineral'].includes(ecology?.kind) ? ecology.kind : roll < 0.57 ? 'plant' : roll < 0.78 ? 'animal' : 'mineral';\r\n    const variants = kind === 'plant' ? PLANTS : kind === 'animal' ? ANIMALS : MINERALS;\r\n    const variant = kind === ecology?.kind && variants.includes(ecology?.variant) ? ecology.variant : choose(variants);\r\n    const e = {\r\n      id: nextId++, kind, variant, ...(kind === 'mineral' && ['blue','ice','red','green','gold','purple'].includes(ecology?.palette) ? { palette: ecology.palette } : {}), phase: 'landing', x: target.x, y: target.y - 22,\r\n      size: 16 + Math.floor(rand() * 37), facing: rand() < 0.5 ? -1 : 1,\r\n      growth: 0, alpha: 0.2, phaseTime: 0, anchor,\r\n    };\r\n    e._targetY = target.y;\r\n    state.entities.push(e);\r\n    emit('growth', { id: e.id, kind, variant });\r\n  }\r\n  function movementDetected(current, now) {\r\n    if (!reference || !previous || lastTimestamp == null) return false;\r\n    const comparable = COMPARABLE.filter(i => current.has(i) && previous.has(i) && reference.has(i));\r\n    if (comparable.length < 4 || TORSO.filter(i => comparable.includes(i)).length < 2 || LIMBS.filter(i => comparable.includes(i)).length < 2) return null;\r\n    const scale = Math.max(1, torsoScale(current));\r\n    const prevScale = Math.max(1, torsoScale(previous));\r\n    const norm = (a, b, s) => Math.hypot(a.x - b.x, a.y - b.y) / s;\r\n    const dt = (now - lastTimestamp) / 1000;\r\n    for (const i of comparable) {\r\n      const p = current.get(i), q = previous.get(i), r = reference.get(i);\r\n      if (!p || !q || !r) continue;\r\n      const delta = norm(p, q, (scale + prevScale) / 2);\r\n      if ((delta > 0.04 && delta / dt > 0.8) || norm(p, r, scale) > 0.15) return true;\r\n    }\r\n    return false;\r\n  }\r\n  function syncAnchors(points) {\r\n    for (const e of state.entities) {\r\n      if (!e.anchor || ['retreating', 'fleeing', 'cracking'].includes(e.phase)) continue;\r\n      const target = anchorTarget(e.anchor, points);\r\n      e._targetY = target.y;\r\n      // Smooth pose noise while retaining direct, low-latency body attachment.\r\n      e.x += (target.x - e.x) * 0.55;\r\n      if (e.phase === 'settled' || e.phase === 'growing') e.y += (target.y - e.y) * 0.55;\r\n    }\r\n  }\r\n\r\n  function pose({ points, now, validHold } = {}) {\r\n    if (!validHold || !Number.isFinite(now) || !points) { clearEligibility(); return state; }\r\n    const current = normalized(points, w, h);\r\n    if (!enoughPose(current)) { clearEligibility(); return state; }\r\n    if (lastTimestamp != null && now <= lastTimestamp) return state;\r\n    const ground = getGround(points);\r\n    state.groundY += (ground - state.groundY) * 0.35;\r\n    if (lastTimestamp != null && now - lastTimestamp > MAX_POSE_GAP_MS) {\r\n      clearEligibility();\r\n      // The current observation starts a fresh reference, but earns no time.\r\n      reference = new Map(current); previous = new Map(current); lastTimestamp = now; poseAge = 0;\r\n      return state;\r\n    }\r\n    if (lastTimestamp == null) {\r\n      reference = new Map(current); previous = new Map(current); lastTimestamp = now; poseAge = 0;\r\n      state.phase = state.entities.some(e => ['retreating', 'fleeing', 'cracking'].includes(e.phase)) ? 'retreating' : 'waiting';\r\n      return state;\r\n    }\r\n    // A landmark first seen confidently on this pose has no trustworthy history.\r\n    // Baseline it now without comparing unseen movement; subsequent poses can track it.\r\n    for (const [index, point] of current) if (!reference.has(index)) reference.set(index, { ...point });\r\n    const moved = movementDetected(current, now);\r\n    if (moved) {\r\n      beginDeparture(); state.stillSeconds = 0;\r\n      nextGrowth = STILL; growthOrdinal = 0;\r\n      reference = new Map(current); previous = new Map(current); lastTimestamp = now; poseAge = 0;\r\n      return state;\r\n    }\r\n    if (moved === null) {\r\n      clearEligibility();\r\n      reference = new Map(current); previous = new Map(current); lastTimestamp = now; poseAge = 0;\r\n      return state;\r\n    }\r\n    syncAnchors(points);\r\n    const dt = (now - lastTimestamp) / 1000;\r\n    state.stillSeconds += dt;\r\n    previous = new Map(current); lastTimestamp = now; poseAge = 0;\r\n    if (state.stillSeconds >= STILL - 1e-9) {\r\n      state.phase = 'growing';\r\n      if (spawnEntities) {\r\n        while (state.stillSeconds >= nextGrowth - 1e-9 && state.entities.length < MAX_ENTITIES) {\r\n          spawnEntity(points); growthOrdinal++;\r\n          nextGrowth += Math.min(2.4, 0.75 + growthOrdinal * 0.035); // slows as ecosystem becomes fuller\r\n        }\r\n      }\r\n    } else if (!state.entities.some(e => ['retreating', 'fleeing', 'cracking'].includes(e.phase))) state.phase = 'waiting';\r\n    return state;\r\n  }\r\n\r\n  function step(rawDt = 0) {\r\n    const dtRaw = Number.isFinite(rawDt) && rawDt > 0 ? rawDt : 0;\r\n    poseAge += dtRaw * 1000; // watchdog uses uncapped wall elapsed, not physics delta\r\n    if (poseAge > MAX_POSE_GAP_MS && lastTimestamp != null) clearEligibility();\r\n    const dt = Math.min(0.05, dtRaw);\r\n    elapsed += dt;\r\n    for (const e of state.entities) {\r\n      e.phaseTime += dt;\r\n      if (e.phase === 'landing') {\r\n        const t = clamp(e.phaseTime / 0.8, 0, 1); e.y += ((e._targetY ?? e.y) - e.y) * Math.min(1, dt * 7);\r\n        e.growth = 0.18 + t * 0.34; e.alpha = 0.32 + t * 0.58;\r\n        if (t >= 1) { e.phase = 'growing'; e.phaseTime = 0; }\r\n      } else if (e.phase === 'growing') {\r\n        e.growth = Math.min(1, e.growth + dt * (e.kind === 'plant' ? 0.26 : 0.13));\r\n        e.alpha = 0.4 + e.growth * 0.5;\r\n        if (e.growth >= 1) { e.phase = 'settled'; e.phaseTime = 0; }\r\n      } else if (e.phase === 'retreating') {\r\n        e.y += 12 * dt; e.growth = Math.max(0, e.growth - dt * 1.1); e.alpha = Math.max(0, e.alpha - dt * 0.85);\r\n      } else if (e.phase === 'fleeing') {\r\n        e.x += e.facing * (28 + e.size * 0.6) * dt; e.y -= Math.sin(e.phaseTime * 8) * 5 * dt;\r\n        e.alpha = Math.max(0, e.alpha - dt * 0.22);\r\n      } else if (e.phase === 'cracking' && e.phaseTime >= CRACK_SECONDS) {\r\n        e._shattered = true;\r\n        const n = Math.min(5, MAX_DEBRIS - state.debris.length);\r\n        for (let i = 0; i < n; i++) {\r\n          const angle = rand() * Math.PI * 2, speed = 22 + rand() * 54;\r\n          state.debris.push({ id: nextId++, x: e.x, y: e.y - e.size * 0.3, vx: Math.cos(angle) * speed,\r\n            vy: -26 - rand() * 55, size: 2 + Math.floor(rand() * 4), rotation: rand() * 6,\r\n            alpha: 0.8, color: e.variant === 'crystal' ? '#8be7ff' : '#a99579', groundY: state.groundY,\r\n            _age: 0, _below: false });\r\n        }\r\n        emit('shatter', { id: e.id, count: n });\r\n      }\r\n    }\r\n    state.entities = state.entities.filter(e => {\r\n      if (e.phase === 'cracking' && e._shattered) return false;\r\n      if (e.phase === 'retreating') return e.phaseTime < 1.05 && e.alpha > 0.02;\r\n      if (e.phase === 'fleeing') return e.phaseTime < 2.5 && e.alpha > 0.03 && e.x > -60 && e.x < w + 60;\r\n      return true;\r\n    });\r\n    for (const d of state.debris) {\r\n      d._age += dt; d.x += d.vx * dt; d.y += d.vy * dt; d.vy += 180 * dt; d.rotation += d.vx * dt * 0.025;\r\n      if (d.y >= d.groundY + 12) d._below = true;\r\n      if (d._below) d.alpha = Math.max(0, d.alpha - dt * 0.7);\r\n    }\r\n    state.debris = state.debris.filter(d => d.alpha > 0.01 && d._age < 8);\r\n    if (state.entities.length > MAX_ENTITIES) state.entities.splice(0, state.entities.length - MAX_ENTITIES);\r\n    if (state.debris.length > MAX_DEBRIS) state.debris.splice(0, state.debris.length - MAX_DEBRIS);\r\n    return state;\r\n  }\r\n  function reset() {\r\n    state.phase = 'waiting'; state.stillSeconds = 0; state.entities.length = 0; state.debris.length = 0;\r\n    reference = previous = null; lastTimestamp = null; poseAge = Infinity; nextGrowth = STILL; growthOrdinal = 0; elapsed = 0;\r\n    state.groundY = h - 6;\r\n    return state;\r\n  }\r\n  return { state, pose, step, reset };\r\n}\r\n","arcade/hold/render.mjs":"// Transparent Canvas2D renderer for the hold ecology. Art is cropped from the\r\n// CC0 atlases preserved in ./assets and normalized into cached 64x64 canvases.\r\nconst ROOT = '/arcade/hold/assets/';\r\nconst CELL = 64;\r\nconst sources = new Map();\r\nlet loadStarted = false;\r\n\r\nfunction canvas64() {\r\n  if (typeof document === 'undefined') return null;\r\n  const canvas = document.createElement('canvas');\r\n  canvas.width = canvas.height = CELL;\r\n  return canvas;\r\n}\r\n\r\nfunction image(path) {\r\n  return new Promise((resolve, reject) => {\r\n    if (typeof Image === 'undefined') return reject(new Error('Image unavailable'));\r\n    const img = new Image();\r\n    img.onload = () => resolve(img);\r\n    img.onerror = reject;\r\n    img.src = ROOT + path;\r\n  });\r\n}\r\n\r\nfunction normalize(img, rect, { key = null, maxW = 56, maxH = 60 } = {}) {\r\n  const canvas = canvas64();\r\n  if (!canvas) return null;\r\n  const ctx = canvas.getContext('2d', { willReadFrequently: !!key });\r\n  const [sx, sy, sw, sh] = rect;\r\n  if (key) {\r\n    const temp = document.createElement('canvas'); temp.width = sw; temp.height = sh;\r\n    const tg = temp.getContext('2d', { willReadFrequently: true });\r\n    tg.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);\r\n    const pixels = tg.getImageData(0, 0, sw, sh);\r\n    const [kr, kg, kb] = key;\r\n    for (let i = 0; i < pixels.data.length; i += 4) {\r\n      if (pixels.data[i] === kr && pixels.data[i + 1] === kg && pixels.data[i + 2] === kb) pixels.data[i + 3] = 0;\r\n    }\r\n    tg.putImageData(pixels, 0, 0);\r\n    const scale = Math.min(maxW / sw, maxH / sh);\r\n    const dw = Math.max(1, Math.round(sw * scale)), dh = Math.max(1, Math.round(sh * scale));\r\n    ctx.imageSmoothingEnabled = false;\r\n    ctx.drawImage(temp, 0, 0, sw, sh, Math.round((CELL - dw) / 2), CELL - dh, dw, dh);\r\n  } else {\r\n    const scale = Math.min(maxW / sw, maxH / sh);\r\n    const dw = Math.max(1, Math.round(sw * scale)), dh = Math.max(1, Math.round(sh * scale));\r\n    ctx.imageSmoothingEnabled = false;\r\n    ctx.drawImage(img, sx, sy, sw, sh, Math.round((CELL - dw) / 2), CELL - dh, dw, dh);\r\n  }\r\n  return canvas;\r\n}\r\n\r\nfunction normalizeFrame(img, frame, fw, fh) {\r\n  return normalize(img, [frame * fw, 0, fw, fh], { maxW: 60, maxH: 58 });\r\n}\r\n\r\nfunction buildAnimal(img, count, fw, fh) {\r\n  return Array.from({ length: count }, (_, i) => normalizeFrame(img, i, fw, fh)).filter(Boolean);\r\n}\r\n\r\nasync function loadSprites() {\r\n  const specs = [\r\n    ['flowers', 'flowers-plants_2.png'],\r\n    ['foxIdle', 'fox/Fox/Fox_Idle.png'], ['foxRun', 'fox/Fox/Fox_Run.png'],\r\n    ['rabbitIdle', 'rabbit/Rabbit/Rabbit_Idle.png'], ['rabbitRun', 'rabbit/Rabbit/Rabbit_Run.png'],\r\n    ['bearIdle', 'bear/Bear/Bear_Idle.png'], ['bearRun', 'bear/Bear/Bear_Run.png'],\r\n    ['boarIdle', 'boar/Boar/Boar_Idle.png'], ['boarRun', 'boar/Boar/Boar_Run.png'],\r\n    ['deerIdle', 'deer/Deer/Deer_Idle.png'], ['deerRun', 'deer/Deer/Deer_Run.png'],\r\n    ['wolfWalk', 'wolf/Wolf/Wolf_Walk.png'], ['wolfRun', 'wolf/Wolf/Wolf_Run.png'],\r\n    ['rocks', 'rocks-original.png'],\r\n    ['crystalBlue', 'crystals/PNG/32x32/crystal-blue.png'],\r\n    ['crystalIcy', 'crystals/PNG/32x32/crystal-icy.png'],\r\n    ['crystalRed', 'crystals/PNG/32x32/crystal-red-small.png'],\r\n    ['crystalGreen', 'crystals/PNG/32x32/crystal-green-alt.png'],\r\n    ['crystalGold', 'crystals/PNG/32x32/crystal-gold.png'],\r\n    ['crystalPurple', 'crystals/PNG/32x32/crystal-purple.png'],\r\n  ];\r\n  const loaded = await Promise.all(specs.map(async ([key, path]) => [key, await image(path).catch(() => null)]));\r\n  const img = Object.fromEntries(loaded);\r\n\r\n  // SpiderDave's source is 78 exact 12x24 cells on one uniform #222034 background.\r\n  if (img.flowers) {\r\n    sources.set('plant:fern', normalize(img.flowers, [12, 0, 12, 24], { key: [34, 32, 52], maxW: 36, maxH: 60 }));\r\n    sources.set('plant:vine', normalize(img.flowers, [14 * 12, 0, 12, 24], { key: [34, 32, 52], maxW: 40, maxH: 60 }));\r\n    sources.set('plant:flower', normalize(img.flowers, [48 * 12, 0, 12, 24], { key: [34, 32, 52], maxW: 42, maxH: 60 }));\r\n    sources.set('plant:grass', normalize(img.flowers, [0, 0, 12, 24], { key: [34, 32, 52], maxW: 40, maxH: 60 }));\r\n    sources.set('plant:reed', normalize(img.flowers, [6 * 12, 0, 12, 24], { key: [34, 32, 52], maxW: 40, maxH: 60 }));\r\n    sources.set('plant:mushroom', normalize(img.flowers, [70 * 12, 0, 12, 24], { key: [34, 32, 52], maxW: 42, maxH: 60 }));\r\n  }\r\n  if (img.foxIdle) sources.set('animal:fox:idle', buildAnimal(img.foxIdle, 6, 64, 36));\r\n  if (img.foxRun) sources.set('animal:fox:run', buildAnimal(img.foxRun, 6, 64, 36));\r\n  if (img.rabbitIdle) sources.set('animal:rabbit:idle', buildAnimal(img.rabbitIdle, 10, 32, 26));\r\n  if (img.rabbitRun) sources.set('animal:rabbit:run', buildAnimal(img.rabbitRun, 6, 32, 26));\r\n  if (img.bearIdle) sources.set('animal:bear:idle', buildAnimal(img.bearIdle, 12, 64, 33));\r\n  if (img.bearRun) sources.set('animal:bear:run', buildAnimal(img.bearRun, 5, 64, 33));\r\n  if (img.boarIdle) sources.set('animal:boar:idle', buildAnimal(img.boarIdle, 8, 64, 40));\r\n  if (img.boarRun) sources.set('animal:boar:run', buildAnimal(img.boarRun, 6, 64, 40));\r\n  if (img.deerIdle) sources.set('animal:deer:idle', buildAnimal(img.deerIdle, 15, 48, 52));\r\n  if (img.deerRun) sources.set('animal:deer:run', buildAnimal(img.deerRun, 9, 48, 52));\r\n  if (img.wolfWalk) sources.set('animal:wolf:idle', buildAnimal(img.wolfWalk, 8, 64, 40));\r\n  if (img.wolfRun) sources.set('animal:wolf:run', buildAnimal(img.wolfRun, 6, 64, 40));\r\n\r\n  // Rocks are six 32x32 cells. Crystal sheets contain four separated sprites;\r\n  // these bounds were inspected at native resolution, then aspect-fit to 64x64.\r\n  if (img.rocks) sources.set('mineral:rock', [0, 2, 4].map(i => normalize(img.rocks, [(i % 3) * 32, Math.floor(i / 3) * 32, 32, 32], { maxW: 56, maxH: 56 })).filter(Boolean));\r\n  const crystalSpecs = [\r\n    ['crystalBlue', [0, 7, 64, 121]], ['crystalBlue', [64, 6, 64, 120]],\r\n    ['crystalIcy', [128, 3, 32, 61]], ['crystalRed', [2, 0, 30, 32]],\r\n    ['crystalGreen', [9, 18, 45, 77]],\r\n  ];\r\n  const crystals = crystalSpecs.map(([name, rect]) => img[name] && normalize(img[name], rect, { maxW: 44, maxH: 58 })).filter(Boolean);\r\n  if (crystals.length) sources.set('mineral:crystal', crystals);\r\n  const crystalPalettes = [\r\n    ['blue', img.crystalBlue && normalize(img.crystalBlue, [0, 7, 64, 121], { maxW: 44, maxH: 58 })],\r\n    ['ice', img.crystalIcy && normalize(img.crystalIcy, [128, 3, 32, 61], { maxW: 44, maxH: 58 })],\r\n    ['red', img.crystalRed && normalize(img.crystalRed, [2, 0, 30, 32], { maxW: 44, maxH: 58 })],\r\n    ['green', img.crystalGreen && normalize(img.crystalGreen, [9, 18, 45, 77], { maxW: 44, maxH: 58 })],\r\n    ['gold', img.crystalGold && normalize(img.crystalGold, [0, 0, 32, 32], { maxW: 44, maxH: 58 })],\r\n    ['purple', img.crystalPurple && normalize(img.crystalPurple, [0, 0, 32, 32], { maxW: 44, maxH: 58 })],\r\n  ];\r\n  for (const [palette, sprite] of crystalPalettes) if (sprite) sources.set(`mineral:crystal:${palette}`, [sprite]);\r\n}\r\n\r\nexport function preloadEcosystemSprites() {\r\n  if (!loadStarted) { loadStarted = true; loadSprites().catch(() => {}); }\r\n}\r\n\r\nfunction getSprite(e) {\r\n  if (e.kind === 'plant') return sources.get(`plant:${e.variant}`);\r\n  if (e.kind === 'animal') {\r\n    const mode = e.phase === 'fleeing' ? 'run' : 'idle';\r\n    const frames = sources.get(`animal:${e.variant}:${mode}`) || sources.get(`animal:${e.variant}:idle`);\r\n    if (!frames?.length) return null;\r\n    const frame = Math.floor(Math.max(0, e.phaseTime || 0) * (mode === 'run' ? 10 : 2)) % frames.length;\r\n    return frames[frame];\r\n  }\r\n  const variants = sources.get(e.palette ? `mineral:${e.variant}:${e.palette}` : `mineral:${e.variant}`);\r\n  if (!variants?.length) return null;\r\n  return variants[Math.abs((e.id || 0) * 7) % variants.length];\r\n}\r\n\r\nfunction drawPlant(ctx, e, sprite) {\r\n  const growth = Math.max(0, Math.min(1, Number(e.growth) || 0));\r\n  if (growth <= 0) return;\r\n  const width = Math.max(1, e.size);\r\n  const height = Math.max(1, e.size * growth);\r\n  const shown = CELL * growth;\r\n  const sway = Math.sin((e.phaseTime || 0) * 2.4 + (e.id || 0)) * 0.045;\r\n  ctx.save(); ctx.translate(Math.round(e.x), Math.round(e.y)); ctx.rotate(sway);\r\n  ctx.drawImage(sprite, 0, Math.round(CELL - shown), CELL, Math.max(1, Math.ceil(shown)),\r\n    Math.round(-width / 2), Math.round(-height), Math.round(width), Math.round(height));\r\n  ctx.restore();\r\n}\r\n\r\nfunction drawAnimal(ctx, e, sprite) {\r\n  const emerge = 0.42 + Math.max(0, Math.min(1, Number(e.growth) || 0)) * 0.58;\r\n  const width = Math.max(1, e.size * emerge), height = width;\r\n  const bob = e.phase === 'fleeing' ? Math.round(Math.sin((e.phaseTime || 0) * 18) * Math.min(2, height * 0.06)) : 0;\r\n  ctx.save(); ctx.translate(Math.round(e.x), Math.round(e.y + bob));\r\n  ctx.scale(e.facing < 0 ? -1 : 1, 1);\r\n  ctx.drawImage(sprite, Math.round(-width / 2), Math.round(-height), Math.round(width), Math.round(height));\r\n  ctx.restore();\r\n}\r\n\r\nfunction drawCrack(ctx, e) {\r\n  const t = Math.max(0, Math.min(1, (e.phaseTime || 0) / 0.3));\r\n  const r = Math.max(4, e.size * 0.38), x = Math.round(e.x), y = Math.round(e.y - e.size * 0.55);\r\n  const branches = [\r\n    [[0, -r], [-1, -2], [-4, 1], [-2, 4]],\r\n    [[-1, -2], [-6, -4], [-8, -7]],\r\n    [[-1, -2], [3, -4], [6, -7]],\r\n    [[-2, 4], [-5, 6], [-6, 8]],\r\n    [[-2, 4], [2, 5], [5, 8]],\r\n  ];\r\n  const count = Math.min(branches.length, 1 + Math.floor(t * branches.length));\r\n  ctx.save(); ctx.lineWidth = 1; ctx.lineCap = 'square';\r\n  ctx.strokeStyle = e.variant === 'crystal' ? '#e9fcff' : '#332d27';\r\n  for (let i = 0; i < count; i++) {\r\n    const line = branches[i]; ctx.beginPath(); ctx.moveTo(x + line[0][0], y + line[0][1]);\r\n    for (let j = 1; j < line.length; j++) ctx.lineTo(x + line[j][0], y + line[j][1]);\r\n    ctx.stroke();\r\n  }\r\n  ctx.restore();\r\n}\r\n\r\nfunction drawDebris(ctx, d) {\r\n  const size = Math.max(1, Math.round(d.size || 2));\r\n  ctx.save(); ctx.globalAlpha = Math.max(0, Math.min(1, d.alpha ?? 1));\r\n  ctx.translate(Math.round(d.x), Math.round(d.y));\r\n  // Keep fragments chunky; only rotate larger chips so the pixels remain crisp.\r\n  if (size >= 4) ctx.rotate(Math.round((d.rotation || 0) * 4) / 4);\r\n  ctx.fillStyle = d.color || '#a99579'; ctx.fillRect(-Math.floor(size / 2), -Math.floor(size / 2), size, size);\r\n  ctx.restore();\r\n}\r\n\r\nexport function renderEcosystem(ctx, state) {\r\n  if (!ctx || !state) return;\r\n  preloadEcosystemSprites();\r\n  ctx.imageSmoothingEnabled = false;\r\n  const entities = Array.isArray(state.entities) ? state.entities : [];\r\n  for (const e of entities) {\r\n    if (!Number.isFinite(e.x) || !Number.isFinite(e.y) || !Number.isFinite(e.size)) continue;\r\n    const sprite = getSprite(e);\r\n    if (!sprite) continue;\r\n    ctx.save(); ctx.globalAlpha = Math.max(0, Math.min(1, Number(e.alpha) || 0));\r\n    if (e.kind === 'plant') drawPlant(ctx, e, sprite);\r\n    else if (e.kind === 'animal') drawAnimal(ctx, e, sprite);\r\n    else {\r\n      const emerge = 0.42 + Math.max(0, Math.min(1, Number(e.growth) || 0)) * 0.58;\r\n      const size = Math.max(1, e.size * emerge);\r\n      ctx.drawImage(sprite, Math.round(e.x - size / 2), Math.round(e.y - size), Math.round(size), Math.round(size));\r\n      if (e.phase === 'cracking') drawCrack(ctx, e);\r\n    }\r\n    ctx.restore();\r\n  }\r\n  for (const d of Array.isArray(state.debris) ? state.debris : []) {\r\n    if (Number.isFinite(d.x) && Number.isFinite(d.y)) drawDebris(ctx, d);\r\n  }\r\n}\r\n","arcade/one-euro.mjs":"export const DCUTOFF = 1;\r\n\r\nexport class OneEuroFilter {\r\n    constructor({ minCutoff, beta, dCutoff }) {\r\n        this.minCutoff = minCutoff;\r\n        this.beta = beta;\r\n        this.dCutoff = dCutoff;\r\n        this.reset();\r\n    }\r\n\r\n    reset() {\r\n        this.first = true;\r\n        this.xPrev = 0;\r\n        this.dxHatPrev = 0;\r\n        this.xHat = 0;\r\n        this.dxHat = 0;\r\n    }\r\n\r\n    static lowpass(v, a, prev) {\r\n        return a * v + (1 - a) * prev;\r\n    }\r\n\r\n    static alpha(cutoff, dt) {\r\n        const tau = 1 / (2 * Math.PI * cutoff);\r\n        return 1 / (1 + tau / dt);\r\n    }\r\n\r\n    filter(x, dtSeconds) {\r\n        if (dtSeconds < 1e-3) dtSeconds = 1e-3;\r\n        if (this.first) {\r\n            this.xHat = x;\r\n            this.dxHat = 0;\r\n            // Ensure derivative history is initialized for the next sample\r\n            this.xPrev = x;\r\n            this.first = false;\r\n            return this.xHat;\r\n        }\r\n        const dx = (x - this.xPrev) / dtSeconds;\r\n        const edx = OneEuroFilter.lowpass(dx, OneEuroFilter.alpha(this.dCutoff, dtSeconds), this.dxHatPrev);\r\n        this.dxHat = edx;\r\n\r\n        const cutoff = this.minCutoff + this.beta * Math.abs(edx);\r\n        const xHat = OneEuroFilter.lowpass(x, OneEuroFilter.alpha(cutoff, dtSeconds), this.xHat);\r\n\r\n        this.xPrev = x;\r\n        this.dxHatPrev = edx;\r\n        this.xHat = xHat;\r\n\r\n        return xHat;\r\n    }\r\n}\r\n\r\nexport const PRESETS = {\r\n    wrist: { minCutoff: 3, beta: 20, dCutoff: DCUTOFF },\r\n    torso: { minCutoff: 1, beta: 4, dCutoff: DCUTOFF }\r\n};\r\n\r\nexport function presetFor(index) {\r\n    if (index >= 15 && index <= 22) return PRESETS.wrist;\r\n    return PRESETS.torso;\r\n}\r\n\r\nexport function createLandmarkBank(presetFn) {\r\n    const states = new Map();\r\n\r\n    function update(index, x, y, visibility, tMs) {\r\n        let state = states.get(index);\r\n        const dtMs = state ? tMs - state.lastMs : 0;\r\n\r\n        if (state && dtMs > 300) {\r\n            state.xFilter.reset();\r\n            state.yFilter.reset();\r\n            state.vis = 0;\r\n            state.visible = false;\r\n        }\r\n\r\n        if (!state) {\r\n            const preset = presetFn(index);\r\n            state = {\r\n                xFilter: new OneEuroFilter(preset),\r\n                yFilter: new OneEuroFilter(preset),\r\n                lastMs: tMs,\r\n                visible: false,\r\n                vis: 0\r\n            };\r\n            states.set(index, state);\r\n        }\r\n\r\n        // Visibility hysteresis\r\n        if (!state.visible && visibility >= 0.6) state.visible = true;\r\n        else if (state.visible && visibility < 0.4) state.visible = false;\r\n\r\n        // Alpha fade\r\n        const target = state.visible ? 1 : 0;\r\n        const step = dtMs / 200;\r\n        if (dtMs > 0) {\r\n            const diff = target - state.vis;\r\n            state.vis += Math.sign(diff) * Math.min(Math.abs(diff), step);\r\n            if (state.vis < 0) state.vis = 0;\r\n            if (state.vis > 1) state.vis = 1;\r\n        }\r\n\r\n        // Filter\r\n        const dtSec = state.lastMs === tMs ? 1 / 30 : dtMs / 1000;\r\n        const fx = state.xFilter.filter(x, dtSec);\r\n        const fy = state.yFilter.filter(y, dtSec);\r\n\r\n        state.lastMs = tMs;\r\n\r\n        return { x: fx, y: fy, alpha: state.vis, visible: state.visible };\r\n    }\r\n\r\n    function predict(index, ms) {\r\n        const state = states.get(index);\r\n        if (!state) return null;\r\n        const lookahead = Math.min(ms, 66) / 1000;\r\n        const dx = state.xFilter.dxHat * lookahead * 0.6;\r\n        const dy = state.yFilter.dxHat * lookahead * 0.6;\r\n        return {\r\n            x: state.xFilter.xHat + dx,\r\n            y: state.yFilter.xHat + dy\r\n        };\r\n    }\r\n\r\n    function reset(index) {\r\n        states.delete(index);\r\n    }\r\n\r\n    return { update, predict, reset };\r\n}\r\n","arcade/plank-flight/biome-scene-legacy.mjs":"import {sectionRecipe,addSectionRecipe} from './presentation-recipes.mjs';\r\n\r\nconst CHUNK_LENGTH=12;\r\nconst CHUNK_COUNT=9;\r\nconst CORRIDOR_LENGTH=CHUNK_LENGTH*CHUNK_COUNT;\r\nconst clamp=(value,min,max)=>Math.max(min,Math.min(max,value));\r\nconst mod=(value,n)=>((value%n)+n)%n;\r\n\r\nconst BIOMES={\r\n  tropical:{bg:'#64c9ed',fog:'#a8d9dc',floor:'#d8ad63',rail:'#ffc26a',dark:'#75432e',mid:'#bd7043',light:'#f7b95e',accent:'#ed5c36',glow:'#ffdd83',glass:'#70e6d1'},\r\n  castle:{bg:'#170d20',fog:'#321626',floor:'#241a25',rail:'#fa4a30',dark:'#111117',mid:'#39313b',light:'#8b6268',accent:'#ff6037',glow:'#ffb137',glass:'#b94c32'},\r\n  rainbow:{bg:'#080d29',fog:'#19254a',floor:'#222b65',rail:'#f8f6ff',dark:'#24264b',mid:'#7381d7',light:'#f5ecff',accent:'#ff55c8',glow:'#ffe34e',glass:'#7ef4ff'},\r\n  loopway:{bg:'#4aafe9',fog:'#a1d8e8',floor:'#28a53d',rail:'#c8ff53',dark:'#177b32',mid:'#39bd4a',light:'#b7ed78',accent:'#f5ae35',glow:'#fff0a0',glass:'#ddfbff'},\r\n  laboratory:{bg:'#071b26',fog:'#143b4b',floor:'#152630',rail:'#30e5c5',dark:'#1d3039',mid:'#4c6770',light:'#b6d7dd',accent:'#ff4c8f',glow:'#7dfff0',glass:'#8df6e5'},\r\n  asteroid:{bg:'#02050e',fog:'#081125',floor:'#10151e',rail:'#47e6ff',dark:'#171c27',mid:'#394352',light:'#a8b4c5',accent:'#ff3989',glow:'#93f7ff',glass:'#7aa7d5'}\r\n};\r\n\r\nexport function createBiomeScene(THREE,{id='tropical',section=0}={}){\r\n  if(!THREE)throw new TypeError('A Three.js namespace is required.');\r\n  const biome=BIOMES[id]??BIOMES.tropical,biomeId=BIOMES[id]?id:'tropical';\r\n  const group=new THREE.Group();group.name=`plank-flight-biome-${biomeId}`;group.userData.biomeId=biomeId;\r\n  const geometries=new Set(),materials=new Set(),textures=new Set(),chunks=[],animated=[],rotating=[];\r\n  const geometry=(kind,...args)=>{const item=new THREE[kind](...args);geometries.add(item);return item;};\r\n  const material=(kind,params)=>{const item=new THREE[kind](params);materials.add(item);return item;};\r\n  const box=geometry('BoxGeometry',1,1,1),crateGeo=box,brickGeo=box;\r\n  const beamGeo=box,postGeo=geometry('CylinderGeometry',.16,.22,1,5),leafGeo=geometry('ConeGeometry',1,1,5);\r\n  const sphereGeo=geometry('IcosahedronGeometry',1,1),octaGeo=geometry('OctahedronGeometry',1,0),torusGeo=geometry('TorusGeometry',1,.08,6,24);\r\n  const ringGeo=geometry('TorusGeometry',1,.035,5,32),podGeo=geometry('SphereGeometry',1,8,6),signGeo=geometry('PlaneGeometry',1.35,.68);\r\n  const palmFrondGeo=geometry('BufferGeometry');\r\n  {\r\n    const positions=[],indices=[],steps=12;\r\n    for(let i=0;i<=steps;i++){\r\n      const u=i/steps,width=.035+Math.sin(Math.PI*u)*(.19*(1-u*.35)),x=2.25*u,y=.17*Math.sin(Math.PI*u)-.13*u,rib=Math.sin(Math.PI*u)*.045;\r\n      // Five cross-section points form gently raised shoulders and a center\r\n      // midrib, keeping the blade visibly leaf-like at phone scale.\r\n      positions.push(x,y,-width,x,y+rib*.45,-width*.45,x,y+rib,0,x,y+rib*.45,width*.45,x,y,width);\r\n      if(i<steps){const a=i*5;for(let edge=0;edge<4;edge++)indices.push(a+edge,a+edge+1,a+5+edge+1,a+edge,a+5+edge+1,a+5+edge);}\r\n    }\r\n    palmFrondGeo.setAttribute('position',new THREE.Float32BufferAttribute(positions,3));palmFrondGeo.setIndex(indices);palmFrondGeo.computeVertexNormals();\r\n    palmFrondGeo.name='palm-frond-blade';\r\n  }\r\n  const colors={};for(const key of Object.keys(biome))if(key!=='bg'&&key!=='fog')colors[key]=material('MeshStandardMaterial',{color:biome[key],roughness:key==='glass'?.24:.7,metalness:key==='rail'?.48:.12,emissive:key==='glow'||key==='accent'?biome[key]:'#000000',emissiveIntensity:key==='glow'?.38:.12});\r\n  colors.wood=material('MeshStandardMaterial',{color:'#a95b2d',roughness:.94});\r\n  colors.leaf=material('MeshStandardMaterial',{color:'#44a83a',roughness:.9,flatShading:true});\r\n  colors.fruit=material('MeshStandardMaterial',{color:'#ffd94d',roughness:.42,emissive:'#f29b27',emissiveIntensity:.12});\r\n  const rainbowRibbonMat=material('MeshStandardMaterial',{color:'#ffffff',roughness:.55,metalness:.05,emissive:'#ffffff',emissiveIntensity:.18});\r\n  const matrixHelper=new THREE.Object3D();\r\n  const shared={};\r\n  const cachedGeometry=(key,kind,...args)=>shared[key]??(shared[key]=geometry(kind,...args));\r\n\r\n  function addInstances(parent,geo,mat,transforms){\r\n    if(!transforms.length)return null;\r\n    const mesh=new THREE.InstancedMesh(geo,mat,transforms.length);mesh.frustumCulled=false;\r\n    transforms.forEach((t,index)=>{\r\n      matrixHelper.position.set(t.x??0,t.y??0,t.z??0);\r\n      matrixHelper.rotation.set(t.rx??0,t.ry??0,t.rz??0);\r\n      const s=t.s??1;Array.isArray(s)?matrixHelper.scale.set(s[0],s[1],s[2]):matrixHelper.scale.set(t.sx??s,t.sy??s,t.sz??s);\r\n      matrixHelper.updateMatrix();mesh.setMatrixAt(index,matrixHelper.matrix);\r\n    });\r\n    mesh.instanceMatrix.needsUpdate=true;parent.add(mesh);return mesh;\r\n  }\r\n\r\n  function addMesh(parent,geo,mat,{x=0,y=0,z=0,rx=0,ry=0,rz=0,sx=1,sy=1,sz=1}={}){\r\n    const mesh=new THREE.Mesh(geo,mat);mesh.position.set(x,y,z);mesh.rotation.set(rx,ry,rz);mesh.scale.set(sx,sy,sz);parent.add(mesh);return mesh;\r\n  }\r\n\r\n  // Grounded worlds get floor/edge cues; rainbow is a floating ribbon and space stays open.\r\n  const hasFloor=!['rainbow','asteroid'].includes(biomeId);\r\n  let floorMaterial=null;\r\n  if(hasFloor){\r\n    floorMaterial=material('MeshStandardMaterial',{color:biome.floor,roughness:.86,metalness:.1});\r\n    // Segment the identical floor surface so near-field material detail survives\r\n    // precision/fog interpolation on long, shallow triangles. Bounds stay 5.6m.\r\n    addInstances(group,geometry('BoxGeometry',1,1,1,4,1,12),floorMaterial,Array.from({length:CHUNK_COUNT},(_,i)=>({x:0,y:-.12,z:-6-i*12,s:[5.6,.16,12]})));\r\n  }\r\n  const railMat=material('MeshStandardMaterial',{color:biome.rail,roughness:.38,metalness:.62,emissive:biome.rail,emissiveIntensity:.28});\r\n  if(!['rainbow','asteroid'].includes(biomeId))for(const side of [-1,1]){\r\n    const railHeight=['tropical','loopway'].includes(biomeId)?.18:.32;\r\n    addInstances(group,box,railMat,Array.from({length:CHUNK_COUNT},(_,i)=>({x:side*2.72,y:railHeight/2-.04,z:-6-i*12,s:[.13,railHeight,12]})));\r\n\r\n  }\r\n\r\n  if(biomeId==='tropical'){const sea=material('MeshStandardMaterial',{color:'#268f9f',roughness:.45,metalness:.05,fog:false});addInstances(group,geometry('BoxGeometry',1,1,1,6,1,24),sea,[{x:-15,y:-.45,z:-54,s:[18,.12,108]},{x:15,y:-.45,z:-54,s:[18,.12,108]}]);}\r\n  const palette=['#ff2738','#ff8a24','#ffe629','#38ed72','#24dff0','#3478ff','#bd55ff'];\r\n  const cubeMat=material('MeshStandardMaterial',{color:biome.mid,roughness:.76,metalness:.08});\r\n  const palmLeafMat=material('MeshStandardMaterial',{color:'#268b3b',roughness:.9,side:THREE.DoubleSide});\r\n  const palmLeafLightMat=material('MeshStandardMaterial',{color:'#59bd4e',roughness:.9,side:THREE.DoubleSide});\r\n  const palmCoconutMat=material('MeshStandardMaterial',{color:'#8c5a33',roughness:.94});\r\n  const loopStoneMat=material('MeshStandardMaterial',{color:'#74523b',roughness:.92,flatShading:true});\r\n  const rockMat=material('MeshStandardMaterial',{color:biomeId==='asteroid'?'#9b7a68':biome.dark,emissive:biomeId==='asteroid'?'#50382d':'#000000',emissiveIntensity:.22,roughness:.9,flatShading:true});\r\n  const glowMat=material('MeshBasicMaterial',{color:biome.glow});\r\n  const transparentGlass=material('MeshPhysicalMaterial',{color:biome.glass,roughness:.2,metalness:.25,transparent:true,opacity:.42,clearcoat:.8});\r\n  // Small deterministic, code-native material swatches, owned by this scene.\r\n  // Strong coarse patterns survive the existing scenery downsample/blur pass.\r\n  function surface(kind){\r\n    if(!globalThis.document?.createElement)return null;\r\n    const canvas=document.createElement('canvas');canvas.width=canvas.height=256;\r\n    const c=canvas.getContext?.('2d');if(!c)return null;\r\n    c.fillStyle='#ffffff';c.fillRect(0,0,256,256);\r\n    if(kind==='checker')for(let y=0;y<4;y++)for(let x=0;x<4;x++){c.fillStyle=(x+y)%2?'#b87832':'#62391e';c.fillRect(x*64,y*64,64,64);}\r\n    if(kind==='wood'){\r\n      c.fillStyle='#bd7638';c.fillRect(0,0,256,256);\r\n      for(let y=0;y<8;y++){c.fillStyle=y%2?'#9b552b':'#c88a46';c.fillRect(0,y*32+2,256,28);c.fillStyle='#53321e';c.fillRect(0,y*32,256,3);}\r\n    }\r\n    if(kind==='masonry'){\r\n      c.fillStyle='#24212a';c.fillRect(0,0,256,256);\r\n      for(let y=0;y<4;y++)for(let x=-1;x<4;x++){c.fillStyle=(x+y)%2?'#96919a':'#6e6975';c.fillRect(x*86+(y%2)*43+4,y*64+4,78,56);}\r\n    }\r\n    if(kind==='panel'){\r\n      c.fillStyle='#506978';c.fillRect(0,0,256,256);c.fillStyle='#243642';c.fillRect(8,8,240,240);c.fillStyle='#698796';c.fillRect(14,14,228,228);\r\n      c.fillStyle='#314a59';c.fillRect(20,150,216,60);c.fillStyle='#d2b34c';c.fillRect(14,119,228,10);\r\n      for(const x of [24,232])for(const y of [24,232]){c.fillStyle='#17232b';c.beginPath();c.arc(x,y,5,0,7);c.fill();}\r\n    }\r\n    if(kind==='sand'){c.fillStyle='#e6c181';c.fillRect(0,0,256,256);for(let y=0;y<8;y++){c.strokeStyle='#cda66a';c.lineWidth=3;c.beginPath();c.moveTo(0,y*32);c.bezierCurveTo(80,y*32+12,170,y*32-12,256,y*32);c.stroke();}}\r\n    if(kind==='grass'){c.fillStyle='#55ad28';c.fillRect(0,0,256,256);}\r\n    if(kind==='tile'){c.fillStyle='#f5f7ff';c.fillRect(0,0,256,256);c.strokeStyle='#6b7293';c.lineWidth=8;c.strokeRect(0,0,256,256);for(let y=24;y<256;y+=32)for(let x=24;x<256;x+=32){c.fillStyle='#ffffff';c.fillRect(x,y,5,5);}}\r\n    if(kind==='lava'){c.fillStyle='#fa5a12';c.fillRect(0,0,256,256);c.strokeStyle='#ffd34a';c.lineWidth=12;for(let i=0;i<5;i++){c.beginPath();c.moveTo(i*64,0);c.lineTo(i*64-26,90);c.lineTo(i*64+18,170);c.lineTo(i*64,256);c.stroke();}}\r\n    let seed=827;for(let i=0;i<700;i++){seed=(Math.imul(seed,1664525)+1013904223)>>>0;const x=seed%256;seed=(Math.imul(seed,1664525)+1013904223)>>>0;const y=seed%256;c.fillStyle=i%2?'#ffffff':'#000000';c.globalAlpha=.07;c.fillRect(x,y,2+(i%4),1+(i%3));}c.globalAlpha=1;\r\n    const t=new THREE.CanvasTexture(canvas);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.anisotropy=2;if(THREE.SRGBColorSpace)t.colorSpace=THREE.SRGBColorSpace;textures.add(t);return t;\r\n  }\r\n  function painted(key,kind,color){const map=surface(kind);colors[key]=material('MeshStandardMaterial',{color:map?'#ffffff':color,map,roughness:.9,metalness:0});return colors[key];}\r\n  painted('wood','wood','#b87738');painted('checker','checker','#9f642b');painted('masonry','masonry','#827784');painted('panel','panel','#597c8d');painted('grass','grass','#55ad28');\r\n  colors.timber=material('MeshStandardMaterial',{color:'#e3ad58',roughness:.9});\r\n  colors.sandstone=material('MeshStandardMaterial',{color:'#bca479',roughness:1});\r\n  colors.stoneTrim=material('MeshStandardMaterial',{color:'#aaa5ab',roughness:.85});\r\n  colors.metal=material('MeshStandardMaterial',{color:'#899fa6',roughness:.65,metalness:.35});\r\n  colors.rock=rockMat;\r\n  colors.cloud=material('MeshStandardMaterial',{color:'#e4edff',roughness:1});\r\n  colors.screen=material('MeshStandardMaterial',{color:'#55b7b1',emissive:'#246d64',emissiveIntensity:.25,roughness:.6});\r\n  colors.lava=material('MeshBasicMaterial',{color:'#ffffff',map:surface('lava')});\r\n  if(floorMaterial){floorMaterial.color.set('#ffffff');floorMaterial.metalness=0;floorMaterial.map=surface(biomeId==='tropical'?'sand':biomeId==='loopway'?'grass':biomeId==='castle'?'masonry':'panel');floorMaterial.map?.repeat.set(2,3);if(!floorMaterial.map)floorMaterial.color.set(biome.floor);}\r\n  rainbowRibbonMat.map=surface('tile');rainbowRibbonMat.map?.repeat.set(1,8);\r\n\r\n  if(['tropical','loopway'].includes(biomeId)){\r\n    addInstances(group,sphereGeo,colors.cloud,[{x:-17,y:11,z:-65,s:[9,1.7,3]},{x:14,y:15,z:-85,s:[12,2,4]},{x:3,y:12,z:-105,s:[8,1.3,3]}]);\r\n  }\r\n\r\n  function addTropical(chunk,index,variant=0){\r\n    // Keep the resort facade out of the silhouette; palms and authored crates\r\n    // carry the coast identity while leaving the central flight lane clear.\r\n    // Segmented, gently curving trunks and broad arched leaf blades replace the\r\n    // old stick-and-box palms. Geometry and instance counts stay fixed per tile.\r\n    const trunks=[],darkFronds=[],lightFronds=[],coconuts=[];\r\n    for(const [x,z] of [[-3.8,-5.1],[3.8,-10.1]]){\r\n      for(let segment=0;segment<6;segment++)trunks.push({x:x+Math.sin(segment*.36)*.16,y:.3+segment*.62,z,s:[1.2-segment*.06,.64,1.2-segment*.06],rz:-Math.sin(segment*.36)*.12});\r\n      for(let leaf=0;leaf<8;leaf++){\r\n        // Keep the crown aimed into the outer promenade, leaving a clear center lane.\r\n        const heading=x<0?Math.PI:0,angle=heading+(leaf-3.5)*Math.PI/7;\r\n        const entry={x:x+Math.sin(2.1)*.16,y:4.1,z,s:[1.3,1.4,1.6],ry:angle,rz:0};\r\n        (leaf%3===0?lightFronds:darkFronds).push(entry);\r\n      }\r\n      for(let nut=0;nut<3;nut++)coconuts.push({x:x+(nut-1)*.16,y:3.98+(nut%2)*.12,z:z+(nut%2)*.1,s:.12});\r\n    }\r\n    const trunkMesh=addInstances(chunk,postGeo,colors.wood,trunks);if(trunkMesh)trunkMesh.name='palm-trunk-segments';\r\n    addInstances(chunk,palmFrondGeo,palmLeafMat,darkFronds);addInstances(chunk,palmFrondGeo,palmLeafLightMat,lightFronds);\r\n    addInstances(chunk,sphereGeo,palmCoconutMat,coconuts);\r\n    // Ocean and broad sand shoulders ground the palm/crate silhouette.\r\n    addInstances(chunk,box,colors.sandstone,[{x:-5.5,y:-.22,z:-6,s:[5.4,.35,12]},{x:5.5,y:-.22,z:-6,s:[5.4,.35,12]}]);\r\n  }\r\n\r\n  function addCastle(chunk,index,variant=0){\r\n    const blocks=[];for(const side of [-1,1])for(let row=0;row<5;row++)blocks.push({x:side*6.4,y:.5+row*.9,z:-6,s:[1.8,.82,11.8]});\r\n    addInstances(chunk,box,colors.masonry,blocks);\r\n    addInstances(chunk,box,colors.lava,[{x:-4.2,y:-.08,z:-6,s:[2.6,.08,12]},{x:4.2,y:-.08,z:-6,s:[2.6,.08,12]}]);\r\n  }\r\n  function addRainbow(chunk,index,variant=0){\r\n    // Open space around the continuous road, with low cloud banks instead of gates.\r\n    if(index%3===0)addInstances(chunk,sphereGeo,colors.cloud,[{x:-7,y:-1.8,z:-8,s:[3.6,1.2,4]},{x:8,y:-2,z:-3,s:[4,1.4,4]}]);\r\n  }\r\n  function addLoopway(chunk,index,variant=0){\r\n    addInstances(chunk,box,colors.checker,[{x:-4.3,y:.65,z:-6,s:[3,1.3,12]},{x:4.3,y:.65,z:-6,s:[3,1.3,12]}]);\r\n    addInstances(chunk,box,colors.grass,[{x:-4.3,y:1.4,z:-6,s:[3.2,.25,12]},{x:4.3,y:1.4,z:-6,s:[3.2,.25,12]}]);\r\n  }\r\n  function addLaboratory(chunk,index,variant=0){\r\n    addInstances(chunk,box,colors.panel,[{x:-5.8,y:2.5,z:-6,s:[.35,5,11.8]},{x:5.8,y:2.5,z:-6,s:[.35,5,11.8]}]);\r\n    addInstances(chunk,box,colors.metal,[{x:-3.15,y:.3,z:-6,s:[.28,.35,12]},{x:3.15,y:.3,z:-6,s:[.28,.35,12]}]);\r\n  }\r\n\r\n  function addAsteroid(chunk,index,variant=0){\r\n    addInstances(chunk,sphereGeo,rockMat,[{x:-4.2,y:1.1,z:-2,s:[1.2,.9,.8]},{x:4.3,y:3.1,z:-8,s:[1.3,1.1,1]},{x:-4.5,y:3.8,z:-10,s:[.7,.6,.6]},{x:3.8,y:.55,z:-5.5,s:[.6,.5,.55]}]);\r\n    const rocks=addInstances(chunk,octaGeo,colors.mid,[{x:-3.7,y:2.45,z:-7,s:[.75,.9,.8]},{x:3.8,y:1.35,z:-3.8,s:[.8,.7,.9]},{x:-4.1,y:.45,z:-9,s:[.38,.35,.4]}]);\r\n    // Batch origins stay fixed: rotating the whole instance batch would swing\r\n    // scenery from the shoulders into the player's collision-free corridor.\r\n    if(variant===2&&index===8){\r\n      const planetGeo=cachedGeometry('asteroid-planet','SphereGeometry',1,16,10);\r\n      const planet=addMesh(chunk,planetGeo,colors.glass,{x:-7.2,y:4.1,z:-34,sx:3.1,sy:3.1,sz:3.1});\r\n      const ring=addMesh(chunk,cachedGeometry('asteroid-planet-ring','TorusGeometry',1,.035,5,36),colors.glow,{x:-7.2,y:4.1,z:-34,rx:.4,rz:.22,sx:4.5,sy:4.5,sz:1});\r\n      const moon=addMesh(chunk,cachedGeometry('asteroid-moon','SphereGeometry',1,10,7),colors.light,{x:5.8,y:2.1,z:-27,sx:.65,sy:.65,sz:.65});\r\n      rotating.push({object:planet,axis:'y',rate:.06,base:0},{object:ring,axis:'z',rate:.035,base:.22},{object:moon,axis:'y',rate:.12,base:1.1});\r\n    }\r\n  }\r\n\r\n  const motifBuilders={tropical:addTropical,castle:addCastle,rainbow:addRainbow,loopway:addLoopway,laboratory:addLaboratory,asteroid:addAsteroid};\r\n  const presentationGeometry=(kind,...args)=>cachedGeometry(`presentation-${kind}-${args.join('-')}`,kind,...args);\r\n  for(let i=0;i<CHUNK_COUNT;i++){\r\n    const chunk=new THREE.Group();chunk.name=`${biomeId}-chunk-${i}`;chunk.userData.slot=i;group.add(chunk);\r\n    const variants=[];\r\n    for(let v=0;v<3;v++){\r\n      const landmark=new THREE.Group();landmark.name=`landmark-section-${v}`;landmark.visible=v===0;chunk.add(landmark);\r\n      (motifBuilders[biomeId]??addTropical)(landmark,i,v);\r\n      variants.push(landmark);\r\n    }\r\n    const sectionLandmarks=[];\r\n    for(let s=0;s<3;s++){\r\n      const landmark=new THREE.Group();landmark.name=`section-landmark-${s}`;landmark.visible=s===section;chunk.add(landmark);\r\n      if(i%2===0)addSectionRecipe({THREE,parent:landmark,recipe:sectionRecipe(biomeId,s),geometry:presentationGeometry,materials:colors});\r\n      sectionLandmarks.push(landmark);\r\n    }\r\n    chunks.push({group:chunk,variants,sectionLandmarks});\r\n  }\r\n\r\n  let stars=null;\r\n  if(biomeId==='asteroid'){\r\n    // Keep a celestial destination in view throughout the biome. Section props\r\n    // alone can sit hundreds of metres away during the first flight section.\r\n    const planetInk=material('MeshStandardMaterial',{color:'#9580b1',emissive:'#2c183e',emissiveIntensity:.25,roughness:1,metalness:0,fog:false});\r\n    const orbitInk=material('MeshBasicMaterial',{color:'#c7e9fa',fog:false});\r\n    const planet=addMesh(group,cachedGeometry('sky-planet','SphereGeometry',1,16,10),planetInk,{x:3.6,y:8,z:-34,sx:3.3,sy:3.3,sz:3.3});\r\n    planet.name='orbital-landmark';\r\n    const orbit=addMesh(group,cachedGeometry('sky-orbit','TorusGeometry',1,.035,5,36),orbitInk,{x:3.6,y:8,z:-34,rx:.65,rz:.18,sx:4.5,sy:4.5,sz:1});\r\n    rotating.push({object:planet,axis:'y',rate:.04,base:0},{object:orbit,axis:'z',rate:.015,base:.18});\r\n    const points=[];let state=0x5319ac7d;\r\n    const rand=()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};\r\n    for(let i=0;i<420;i++)points.push((rand()-.5)*72,(rand()-.5)*46,-8-rand()*105);\r\n    const starGeometry=geometry('BufferGeometry');starGeometry.setAttribute('position',new THREE.Float32BufferAttribute(points,3));\r\n    stars=new THREE.Points(starGeometry,material('PointsMaterial',{color:'#d9eeff',size:.075,sizeAttenuation:true}));group.add(stars);\r\n  }else if(biomeId==='rainbow'){\r\n    const points=[];let state=0x8248ab1;\r\n    const rand=()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};\r\n    for(let i=0;i<190;i++)points.push((rand()-.5)*44,3+rand()*17,-4-rand()*100);\r\n    const starGeometry=geometry('BufferGeometry');starGeometry.setAttribute('position',new THREE.Float32BufferAttribute(points,3));\r\n    stars=new THREE.Points(starGeometry,material('PointsMaterial',{color:'#ffffff',size:.065,sizeAttenuation:true}));group.add(stars);\r\n  }\r\n\r\n  let rainbowRoad=null;\r\n  if(biomeId==='rainbow'){\r\n    const bands=[];\r\n    for(let band=0;band<7;band++)for(let tile=0;tile<CHUNK_COUNT;tile++)bands.push({x:-2.46+band*.82,y:-.015,z:-6-tile*CHUNK_LENGTH,s:[.82,.055,CHUNK_LENGTH],color:palette[band]});\r\n    rainbowRoad=addInstances(group,box,rainbowRibbonMat,bands);\r\n    bands.forEach((band,i)=>rainbowRoad?.setColorAt(i,new THREE.Color(band.color)));\r\n    if(rainbowRoad?.instanceColor)rainbowRoad.instanceColor.needsUpdate=true;\r\n  }\r\n\r\n  function sectionValue(value){\r\n    const n=Number(value);if(!Number.isFinite(n)||n<0)return 0;\r\n    return Math.floor(n>2?n/30:n)%3;\r\n  }\r\n  function update({distance=0,time=0,section:nextSection=section,speed=5.2,boost=false,turn=0}={}){\r\n    const d=Number.isFinite(distance)?Math.max(0,distance):0;\r\n    const whole=Math.floor(d/CHUNK_LENGTH),offset=d%CHUNK_LENGTH;\r\n    const variant=sectionValue(nextSection);\r\n    for(let i=0;i<CHUNK_COUNT;i++){\r\n      const absolute=whole+i,slot=mod(absolute,CHUNK_COUNT),chunk=chunks[slot];\r\n      chunk.group.position.set(0,0,offset-i*CHUNK_LENGTH);\r\n      // Scrolling motifs keep varying by chunk, independent of the active\r\n      // authored section. Section identity has its own persistent group below.\r\n      const activeVariant=mod(Math.floor(absolute/3),3);\r\n      chunk.variants.forEach((landmark,index)=>{landmark.visible=index===activeVariant;});\r\n      chunk.sectionLandmarks.forEach((landmark,index)=>{landmark.visible=index===variant;});\r\n    }\r\n    const flow=clamp((Number(speed)||5.2)/5.2,.5,1.8)*(boost?1.32:1);\r\n    if(rainbowRoad){\r\n      let instance=0;\r\n      for(let band=0;band<7;band++)for(let tile=0;tile<CHUNK_COUNT;tile++){\r\n        matrixHelper.position.set(-2.46+band*.82,-.015,offset-tile*CHUNK_LENGTH-6);\r\n        matrixHelper.rotation.set(0,0,0);matrixHelper.scale.set(.82,.055,CHUNK_LENGTH);matrixHelper.updateMatrix();\r\n        rainbowRoad.setMatrixAt(instance++,matrixHelper.matrix);\r\n      }\r\n      rainbowRoad.instanceMatrix.needsUpdate=true;\r\n    }\r\n    for(const item of rotating){\r\n      const angle=(Number(time)||0)*item.rate*flow+item.base;\r\n      item.object.rotation[item.axis]=angle;\r\n    }\r\n    for(const item of animated){\r\n      if(item.kind==='streak')item.object.userData.flowPhase=(d*flow)%CHUNK_LENGTH;\r\n      else if(item.kind==='pod')item.object.rotation.y=Math.sin((Number(time)||0)*.4)*.035;\r\n      else if(item.kind==='core')item.object.rotation.y=0;\r\n    }\r\n    if(stars)stars.position.z=(d%96)*.6;\r\n    // The route remains straight; props and scenery animate around it.\r\n  }\r\n  update({distance:0,section,speed:5.2,turn:0});\r\n\r\n  let disposed=false;\r\n  function dispose(){\r\n    if(disposed)return;disposed=true;\r\n    group.traverse(object=>{if(object.isInstancedMesh)object.dispose?.();});\r\n    group.removeFromParent?.();\r\n    for(const item of geometries)item.dispose?.();\r\n    for(const item of materials)item.dispose?.();\r\n    for(const item of textures)item.dispose?.();\r\n    chunks.length=0;animated.length=0;rotating.length=0;\r\n  }\r\n  return {group,update,dispose};\r\n}\r\n","arcade/plank-flight/biome-scene.mjs":"import {ENVIRONMENTS} from './biome-visuals.mjs';\r\nimport {sectionRecipe,addSectionRecipe} from './presentation-recipes.mjs';\r\n\r\nconst CHUNK_LENGTH=12;\r\nconst CHUNK_COUNT=9;\r\nconst CORRIDOR_LENGTH=CHUNK_LENGTH*CHUNK_COUNT;\r\nconst clamp=(value,min,max)=>Math.max(min,Math.min(max,value));\r\nconst mod=(value,n)=>((value%n)+n)%n;\r\n\r\nconst BIOMES={\r\n  tropical:{bg:'#64c9ed',fog:'#a8d9dc',floor:'#d8ad63',rail:'#ffc26a',dark:'#75432e',mid:'#bd7043',light:'#f7b95e',accent:'#ed5c36',glow:'#ffdd83',glass:'#70e6d1'},\r\n  castle:{bg:'#170d20',fog:'#321626',floor:'#241a25',rail:'#fa4a30',dark:'#111117',mid:'#39313b',light:'#8b6268',accent:'#ff6037',glow:'#ffb137',glass:'#b94c32'},\r\n  rainbow:{bg:'#080d29',fog:'#19254a',floor:'#222b65',rail:'#f8f6ff',dark:'#24264b',mid:'#7381d7',light:'#f5ecff',accent:'#ff55c8',glow:'#ffe34e',glass:'#7ef4ff'},\r\n  loopway:{bg:'#4aafe9',fog:'#a1d8e8',floor:'#28a53d',rail:'#c8ff53',dark:'#177b32',mid:'#39bd4a',light:'#b7ed78',accent:'#f5ae35',glow:'#fff0a0',glass:'#ddfbff'},\r\n  laboratory:{bg:'#071b26',fog:'#143b4b',floor:'#152630',rail:'#30e5c5',dark:'#1d3039',mid:'#4c6770',light:'#b6d7dd',accent:'#ff4c8f',glow:'#7dfff0',glass:'#8df6e5'},\r\n  asteroid:{bg:'#02050e',fog:'#081125',floor:'#10151e',rail:'#47e6ff',dark:'#171c27',mid:'#394352',light:'#a8b4c5',accent:'#ff3989',glow:'#93f7ff',glass:'#7aa7d5'}\r\n};\r\n\r\nfunction makeSignTexture(THREE,owned){\r\n  if(!globalThis.document?.createElement)return null;\r\n  const canvas=document.createElement('canvas');canvas.width=512;canvas.height=256;\r\n  const context=canvas.getContext?.('2d');if(!context)return null;\r\n  context.fillStyle='#050505';context.fillRect(0,0,512,256);\r\n  context.strokeStyle='#fff';context.lineWidth=12;context.strokeRect(10,10,492,236);\r\n  context.fillStyle='#fff';context.font='900 83px Arial, sans-serif';context.textAlign='center';context.textBaseline='middle';\r\n  context.fillText('MOM INC.',256,95);\r\n  context.font='800 28px Arial, sans-serif';context.fillText('YOUR BEST IS OUR PRODUCT',256,177);\r\n  context.fillStyle='#fff';context.fillRect(30,218,452,9);\r\n  const texture=new THREE.CanvasTexture(canvas);if('colorSpace'in texture&&THREE.SRGBColorSpace)texture.colorSpace=THREE.SRGBColorSpace;\r\n  owned.add(texture);return texture;\r\n}\r\n\r\nexport function createBiomeScene(THREE,{id='tropical',section=0}={}){\r\n  if(!THREE)throw new TypeError('A Three.js namespace is required.');\r\n  const biomeId=BIOMES[id]?id:'tropical',biome={...BIOMES[biomeId],...ENVIRONMENTS[biomeId]};\r\n  const group=new THREE.Group();group.name=`plank-flight-biome-${biomeId}`;group.userData.biomeId=biomeId;\r\n  const geometries=new Set(),materials=new Set(),textures=new Set(),chunks=[],animated=[],rotating=[];\r\n  const geometry=(kind,...args)=>{const item=new THREE[kind](...args);geometries.add(item);return item;};\r\n  const material=(kind,params)=>{const item=new THREE[kind](params);materials.add(item);return item;};\r\n  const box=geometry('BoxGeometry',1,1,1),crateGeo=box,brickGeo=box;\r\n  const beamGeo=box,postGeo=geometry('CylinderGeometry',.16,.22,1,5),leafGeo=geometry('ConeGeometry',1,1,5);\r\n  const sphereGeo=geometry('IcosahedronGeometry',1,1),octaGeo=geometry('OctahedronGeometry',1,0),torusGeo=geometry('TorusGeometry',1,.08,6,24);\r\n  const ringGeo=geometry('TorusGeometry',1,.035,5,32),podGeo=geometry('SphereGeometry',1,8,6),signGeo=geometry('PlaneGeometry',1.35,.68);\r\n  const palmFrondGeo=geometry('BufferGeometry');\r\n  {\r\n    const positions=[],indices=[],steps=12;\r\n    for(let i=0;i<=steps;i++){\r\n      const u=i/steps,width=.035+Math.sin(Math.PI*u)*(.19*(1-u*.35)),x=2.25*u,y=.17*Math.sin(Math.PI*u)-.13*u,rib=Math.sin(Math.PI*u)*.045;\r\n      // Five cross-section points form gently raised shoulders and a center\r\n      // midrib, keeping the blade visibly leaf-like at phone scale.\r\n      positions.push(x,y,-width,x,y+rib*.45,-width*.45,x,y+rib,0,x,y+rib*.45,width*.45,x,y,width);\r\n      if(i<steps){const a=i*5;for(let edge=0;edge<4;edge++)indices.push(a+edge,a+edge+1,a+5+edge+1,a+edge,a+5+edge+1,a+5+edge);}\r\n    }\r\n    palmFrondGeo.setAttribute('position',new THREE.Float32BufferAttribute(positions,3));palmFrondGeo.setIndex(indices);palmFrondGeo.computeVertexNormals();\r\n    palmFrondGeo.name='palm-frond-blade';\r\n  }\r\n  const colors={};for(const key of ['floor','rail','dark','mid','light','accent','glow','glass'])colors[key]=material('MeshStandardMaterial',{color:biome[key],roughness:key==='glass'?.24:.7,metalness:key==='rail'?.48:.12,emissive:key==='glow'||key==='accent'?biome[key]:'#000000',emissiveIntensity:key==='glow'?.38:.12});\r\n  const signInk=material('MeshBasicMaterial',{color:'#050505',side:THREE.DoubleSide});\r\n  const signWhite=material('MeshBasicMaterial',{color:'#ffffff',side:THREE.DoubleSide});\r\n  const signTexture=makeSignTexture(THREE,textures);\r\n  const signPrint=signTexture?material('MeshBasicMaterial',{map:signTexture,transparent:false,side:THREE.DoubleSide}):signWhite;\r\n  const rainbowRibbonMat=material('MeshPhysicalMaterial',{color:'#ffffff',roughness:.045,metalness:.32,clearcoat:1,clearcoatRoughness:.025,emissive:'#6d78ff',emissiveIntensity:.12});\r\n  colors.wood=material('MeshStandardMaterial',{color:'#a95b2d',roughness:.94});\r\n  colors.leaf=material('MeshStandardMaterial',{color:'#44a83a',roughness:.9,flatShading:true});\r\n  colors.fruit=material('MeshStandardMaterial',{color:'#ffd94d',roughness:.42,emissive:'#f29b27',emissiveIntensity:.12});\r\n  const matrixHelper=new THREE.Object3D();\r\n  const shared={};\r\n  const cachedGeometry=(key,kind,...args)=>shared[key]??(shared[key]=geometry(kind,...args));\r\n\r\n  const physicalBatches=new Map(),batchMatrix=new THREE.Matrix4();\r\n  function physicalBox(size,units){\r\n    const key=`uv-box:${size.join(',')}:${units}`;\r\n    if(shared[key])return shared[key];\r\n    const geo=geometry('BoxGeometry',...size),uv=geo.attributes.uv;\r\n    for(let face=0;face<6;face++){\r\n      const axes=face<2?[2,1]:face<4?[0,2]:[0,1];\r\n      for(let corner=0;corner<4;corner++){const i=face*4+corner;uv.setXY(i,uv.getX(i)*size[axes[0]]/units,uv.getY(i)*size[axes[1]]/units);}\r\n    }\r\n    geo.userData.worldUnitsPerRepeat=units;geo.userData.authoredSize=size;return shared[key]=geo;\r\n  }\r\n  function physicalShape(source,size,units){\r\n    if(source===box)return physicalBox(size,units);\r\n    const key=`uv-shape:${source.uuid}:${size.join(',')}:${units}`;if(shared[key])return shared[key];\r\n    const baked=source.index?source.toNonIndexed():source.clone();baked.scale(...size);geometries.add(baked);\r\n    const positions=baked.attributes.position,uv=new THREE.Float32BufferAttribute(new Float32Array(positions.count*2),2);\r\n    const a=new THREE.Vector3(),b=new THREE.Vector3(),c=new THREE.Vector3(),normal=new THREE.Vector3();\r\n    for(let i=0;i<positions.count;i+=3){a.fromBufferAttribute(positions,i);b.fromBufferAttribute(positions,i+1);c.fromBufferAttribute(positions,i+2);normal.crossVectors(b.sub(a),c.sub(a));\r\n      const abs=[Math.abs(normal.x),Math.abs(normal.y),Math.abs(normal.z)],axis=abs.indexOf(Math.max(...abs)),axes=axis===0?[2,1]:axis===1?[0,2]:[0,1];\r\n      for(let j=0;j<3;j++)uv.setXY(i+j,positions.getComponent(i+j,axes[0])/units,positions.getComponent(i+j,axes[1])/units);\r\n    }\r\n    baked.setAttribute('uv',uv);baked.userData.worldUnitsPerRepeat=units;baked.userData.authoredSize=size;return shared[key]=baked;\r\n  }\r\n  function addInstances(parent,geo,mat,transforms){\r\n    if(!transforms.length)return null;\r\n    if(mat!==rainbowRibbonMat&&(mat.map||geo===box)){\r\n      const marker=new THREE.Group();parent.add(marker);\r\n      for(const t of transforms){\r\n        const scale=t.s??1,size=Array.isArray(scale)?scale:[t.sx??scale,t.sy??scale,t.sz??scale];\r\n        const narrow=size.slice().sort((a,b)=>a-b)[1]<.5;\r\n        const units=geo!==box?2:size[1]<.1&&(t.y??0)<.1?2:narrow?.5:biomeId==='castle'?1:['loopway','asteroid'].includes(biomeId)?2:1;\r\n        const baked=physicalShape(geo,size,units),key=baked.uuid+mat.uuid;\r\n        if(!physicalBatches.has(key))physicalBatches.set(key,{geo:baked,mat,items:[],mesh:null});\r\n        matrixHelper.position.set(t.x??0,t.y??0,t.z??0);matrixHelper.rotation.set(t.rx??0,t.ry??0,t.rz??0);matrixHelper.scale.set(1,1,1);matrixHelper.updateMatrix();\r\n        physicalBatches.get(key).items.push({parent:marker,matrix:matrixHelper.matrix.clone()});\r\n      }\r\n      return marker;\r\n    }\r\n    const mesh=new THREE.InstancedMesh(geo,mat,transforms.length);mesh.frustumCulled=false;\r\n    transforms.forEach((t,index)=>{\r\n      matrixHelper.position.set(t.x??0,t.y??0,t.z??0);matrixHelper.rotation.set(t.rx??0,t.ry??0,t.rz??0);\r\n      const scale=t.s??1;Array.isArray(scale)?matrixHelper.scale.set(...scale):matrixHelper.scale.set(t.sx??scale,t.sy??scale,t.sz??scale);\r\n      matrixHelper.updateMatrix();mesh.setMatrixAt(index,matrixHelper.matrix);\r\n    });\r\n    mesh.instanceMatrix.needsUpdate=true;parent.add(mesh);return mesh;\r\n  }\r\n\r\n  function addMesh(parent,geo,mat,{x=0,y=0,z=0,rx=0,ry=0,rz=0,sx=1,sy=1,sz=1}={}){\r\n    const mesh=new THREE.Mesh(geo,mat);mesh.position.set(x,y,z);mesh.rotation.set(rx,ry,rz);mesh.scale.set(sx,sy,sz);parent.add(mesh);return mesh;\r\n  }\r\n\r\n  // Grounded worlds get floor/edge cues; rainbow is a floating ribbon and space stays open.\r\n  const hasFloor=!['rainbow','asteroid'].includes(biomeId);\r\n  let floorMaterial=null;\r\n  if(hasFloor){\r\n    floorMaterial=material('MeshStandardMaterial',{color:biome.floor,roughness:.86,metalness:.1});\r\n    addMesh(group,geometry('BoxGeometry',5.6,.16,CORRIDOR_LENGTH),floorMaterial,{x:0,y:-.12,z:-CORRIDOR_LENGTH/2});\r\n  }\r\n  const railMat=material('MeshStandardMaterial',{color:biome.rail,roughness:.38,metalness:.62,emissive:biome.rail,emissiveIntensity:.28});\r\n  if(!['rainbow','asteroid'].includes(biomeId))for(const side of [-1,1]){\r\n    const railHeight=['tropical','loopway'].includes(biomeId)?.18:.32;\r\n    addMesh(group,geometry('BoxGeometry',.13,railHeight,CORRIDOR_LENGTH),railMat,{x:side*2.72,y:railHeight/2-.04,z:-CORRIDOR_LENGTH/2});\r\n    // Recess backs and continuous ceiling/floor returns sit BEHIND the visible bay frames.\r\n    if(['castle','laboratory'].includes(biomeId)){\r\n      const back=addMesh(group,geometry('BoxGeometry',.16,6.4,CORRIDOR_LENGTH+16),colors.dark,{x:side*3.98,y:2.8,z:-CORRIDOR_LENGTH/2});back.name='recessed-opaque-closure';\r\n    }\r\n  }\r\n\r\n  if(['castle','laboratory'].includes(biomeId)){\r\n    addMesh(group,geometry('BoxGeometry',8.2,.2,CORRIDOR_LENGTH+16),colors.dark,{y:5.9,z:-CORRIDOR_LENGTH/2});\r\n    addMesh(group,geometry('BoxGeometry',8.2,.2,CORRIDOR_LENGTH+16),colors.dark,{y:-.24,z:-CORRIDOR_LENGTH/2});\r\n    addMesh(group,geometry('BoxGeometry',8.2,6.4,.2),colors.dark,{y:2.8,z:-CORRIDOR_LENGTH-5});\r\n  }\r\n  if(biomeId==='tropical'){const sea=material('MeshStandardMaterial',{color:'#268f9f',roughness:.45,metalness:.05,fog:false});addInstances(group,geometry('BoxGeometry',1,1,1,6,1,24),sea,[{x:-15,y:-.45,z:-54,s:[18,.12,108]},{x:15,y:-.45,z:-54,s:[18,.12,108]}]);}\r\n  const palette=['#ff2738','#ff8a24','#ffe629','#38ed72','#24dff0','#3478ff','#bd55ff'];\r\n  const cubeMat=material('MeshStandardMaterial',{color:biome.mid,roughness:.76,metalness:.08});\r\n  const palmLeafMat=material('MeshStandardMaterial',{color:'#268b3b',roughness:.9,side:THREE.DoubleSide});\r\n  const palmLeafLightMat=material('MeshStandardMaterial',{color:'#59bd4e',roughness:.9,side:THREE.DoubleSide});\r\n  const palmCoconutMat=material('MeshStandardMaterial',{color:'#8c5a33',roughness:.94});\r\n  const loopStoneMat=material('MeshStandardMaterial',{color:'#74523b',roughness:.92,flatShading:true});\r\n  const rockMat=material('MeshStandardMaterial',{color:biomeId==='asteroid'?'#9b7a68':biome.dark,emissive:biomeId==='asteroid'?'#50382d':'#000000',emissiveIntensity:.22,roughness:.9,flatShading:true});\r\n  const glowMat=material('MeshBasicMaterial',{color:biome.glow});\r\n  const transparentGlass=material('MeshPhysicalMaterial',{color:biome.glass,roughness:.2,metalness:.25,transparent:true,opacity:.42,clearcoat:.8});\r\n  // Painted panel seams, chipped edges, rivets and clay speckle add surface\r\n  // detail without growing the scenery mesh count.\r\n  let grain=null;\r\n  if(globalThis.document?.createElement){\r\n    const canvas=document.createElement('canvas');canvas.width=canvas.height=256;const ctx=canvas.getContext?.('2d');\r\n    if(ctx){ctx.fillStyle='#fff';ctx.fillRect(0,0,256,256);ctx.strokeStyle='#000';ctx.lineWidth=1;\r\n      for(let row=0;row<4;row++)for(let col=0;col<4;col++){\r\n        const x=col*64+(row%2)*7,y=row*64;\r\n        ctx.globalAlpha=(row+col)%2?.025:.055;ctx.fillStyle='#000';ctx.fillRect(x+2,y+2,59,59);\r\n        ctx.globalAlpha=.16;ctx.strokeRect(x+1.5,y+1.5,61,61);\r\n        ctx.globalAlpha=.14;ctx.fillStyle='#fff';\r\n        for(const [rx,ry] of [[5,5],[58,5],[5,58],[58,58]]){ctx.beginPath();ctx.arc(x+rx,y+ry,1.7,0,Math.PI*2);ctx.fill();}\r\n      }\r\n      let seed=0x5120;const random=()=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/4294967296;};\r\n      for(let i=0;i<460;i++){ctx.globalAlpha=.035+random()*.13;ctx.fillStyle=i%3?'#000':'#fff';ctx.fillRect(random()*256,random()*256,1+random()*4,1+random()*2);}\r\n      for(let i=0;i<24;i++){const x=random()*256,y=random()*256;ctx.globalAlpha=.12;ctx.strokeStyle='#000';ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(x+2+random()*9,y-1-random()*3);ctx.stroke();}\r\n      grain=new THREE.CanvasTexture(canvas);grain.wrapS=grain.wrapT=THREE.RepeatWrapping;grain.repeat.set(1,1);grain.anisotropy=2;if('colorSpace'in grain&&THREE.SRGBColorSpace)grain.colorSpace=THREE.SRGBColorSpace;textures.add(grain);\r\n      for(const mat of [floorMaterial,cubeMat,rockMat,loopStoneMat,colors.dark,colors.mid,colors.light])if(mat){mat.map=grain;mat.needsUpdate=true;}\r\n    }\r\n  }\r\n  // Small deterministic, code-native material swatches, owned by this scene.\r\n  // Strong coarse patterns survive the existing scenery downsample/blur pass.\r\n  function surface(kind){\r\n    if(!globalThis.document?.createElement)return null;\r\n    const canvas=document.createElement('canvas');canvas.width=canvas.height=256;\r\n    const c=canvas.getContext?.('2d');if(!c)return null;\r\n    c.fillStyle='#ffffff';c.fillRect(0,0,256,256);\r\n    if(kind==='checker')for(let y=0;y<4;y++)for(let x=0;x<4;x++){c.fillStyle=(x+y)%2?'#b87832':'#62391e';c.fillRect(x*64,y*64,64,64);}\r\n    if(kind==='wood'){\r\n      c.fillStyle='#bd7638';c.fillRect(0,0,256,256);\r\n      for(let y=0;y<8;y++){c.fillStyle=y%2?'#9b552b':'#c88a46';c.fillRect(0,y*32+2,256,28);c.fillStyle='#53321e';c.fillRect(0,y*32,256,3);}\r\n    }\r\n    if(kind==='masonry'){\r\n      c.fillStyle='#24212a';c.fillRect(0,0,256,256);\r\n      for(let y=0;y<4;y++)for(let x=-1;x<4;x++){c.fillStyle=(x+y)%2?'#96919a':'#6e6975';c.fillRect(x*86+(y%2)*43+4,y*64+4,78,56);}\r\n    }\r\n    if(kind==='panel'){\r\n      c.fillStyle='#506978';c.fillRect(0,0,256,256);c.fillStyle='#243642';c.fillRect(8,8,240,240);c.fillStyle='#698796';c.fillRect(14,14,228,228);\r\n      c.fillStyle='#314a59';c.fillRect(20,150,216,60);c.fillStyle='#d2b34c';c.fillRect(14,119,228,10);\r\n      for(const x of [24,232])for(const y of [24,232]){c.fillStyle='#17232b';c.beginPath();c.arc(x,y,5,0,7);c.fill();}\r\n    }\r\n    if(kind==='sand'){c.fillStyle='#e6c181';c.fillRect(0,0,256,256);for(let y=0;y<8;y++){c.strokeStyle='#cda66a';c.lineWidth=3;c.beginPath();c.moveTo(0,y*32);c.bezierCurveTo(80,y*32+12,170,y*32-12,256,y*32);c.stroke();}}\r\n    if(kind==='grass'){c.fillStyle='#55ad28';c.fillRect(0,0,256,256);}\r\n    if(kind==='tile'){c.fillStyle='#f5f7ff';c.fillRect(0,0,256,256);c.strokeStyle='#6b7293';c.lineWidth=8;c.strokeRect(0,0,256,256);for(let y=24;y<256;y+=32)for(let x=24;x<256;x+=32){c.fillStyle='#ffffff';c.fillRect(x,y,5,5);}}\r\n    if(kind==='lava'){c.fillStyle='#fa5a12';c.fillRect(0,0,256,256);c.strokeStyle='#ffd34a';c.lineWidth=12;for(let i=0;i<5;i++){c.beginPath();c.moveTo(i*64,0);c.lineTo(i*64-26,90);c.lineTo(i*64+18,170);c.lineTo(i*64,256);c.stroke();}}\r\n    let seed=827;for(let i=0;i<700;i++){seed=(Math.imul(seed,1664525)+1013904223)>>>0;const x=seed%256;seed=(Math.imul(seed,1664525)+1013904223)>>>0;const y=seed%256;c.fillStyle=i%2?'#ffffff':'#000000';c.globalAlpha=.07;c.fillRect(x,y,2+(i%4),1+(i%3));}c.globalAlpha=1;\r\n    const t=new THREE.CanvasTexture(canvas);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.anisotropy=2;if(THREE.SRGBColorSpace)t.colorSpace=THREE.SRGBColorSpace;textures.add(t);return t;\r\n  }\r\n  function painted(key,kind,color){const map=surface(kind);colors[key]=material('MeshStandardMaterial',{color:map?'#ffffff':color,map,roughness:.9,metalness:0});return colors[key];}\r\n  painted('wood','wood','#b87738');painted('checker','checker','#9f642b');painted('masonry','masonry','#827784');painted('panel','panel','#597c8d');painted('grass','grass','#55ad28');\r\n  colors.timber=material('MeshStandardMaterial',{color:'#e3ad58',roughness:.9});\r\n  colors.sandstone=material('MeshStandardMaterial',{color:'#bca479',roughness:1});\r\n  colors.stoneTrim=material('MeshStandardMaterial',{color:'#aaa5ab',roughness:.85});\r\n  colors.metal=material('MeshStandardMaterial',{color:'#899fa6',roughness:.65,metalness:.35});\r\n  colors.rock=rockMat;\r\n  colors.cloud=material('MeshStandardMaterial',{color:'#e4edff',roughness:1});\r\n  colors.screen=material('MeshStandardMaterial',{color:'#55b7b1',emissive:'#246d64',emissiveIntensity:.25,roughness:.6});\r\n  colors.lava=material('MeshBasicMaterial',{color:'#ffffff',map:surface('lava')});\r\n  if(floorMaterial){floorMaterial.color.set('#ffffff');floorMaterial.metalness=0;floorMaterial.map=surface(biomeId==='tropical'?'sand':biomeId==='loopway'?'grass':biomeId==='castle'?'masonry':'panel');floorMaterial.map?.repeat.set(1,1);if(!floorMaterial.map)floorMaterial.color.set(biome.floor);}\r\n  rainbowRibbonMat.map=surface('tile');rainbowRibbonMat.map?.repeat.set(1,8);\r\n\r\n  if(['tropical','loopway'].includes(biomeId)){\r\n    addInstances(group,sphereGeo,colors.cloud,[{x:-17,y:11,z:-65,s:[9,1.7,3]},{x:14,y:15,z:-85,s:[12,2,4]},{x:3,y:12,z:-105,s:[8,1.3,3]}]);\r\n  }\r\n\r\n  function makeSign(chunk,index){\r\n    if(index%3!==0)return;\r\n    for(const side of [-1,1]){\r\n      const board=addMesh(chunk,signGeo,signInk,{x:side*1.95,y:3.78,z:-5.2});\r\n      const print=addMesh(chunk,signGeo,signPrint,{x:side*1.95,y:3.78,z:-5.18,sx:.88,sy:.82,sz:1});\r\n      board.userData.propaganda=true;print.userData.propaganda=true;\r\n    }\r\n    // A high frontal banner reinforces the recurring black/white corporate motif.\r\n    const banner=addMesh(chunk,signGeo,signInk,{x:0,y:4.35,z:-5.2,sx:1.7,sy:.62});\r\n    const bannerPrint=addMesh(chunk,signGeo,signPrint,{x:0,y:4.35,z:-5.18,sx:1.5,sy:.48});\r\n    banner.userData.propaganda=true;bannerPrint.userData.propaganda=true;\r\n  }\r\n\r\n  function addTropical(chunk,index,variant=0){\r\n    // An open-air resort promenade: layered shopfronts, glass bays, lit fascia,\r\n    // tiled paving and planter ledges keep the center lane clear and readable.\r\n    const bays=[];for(const side of [-1,1])for(const z of [-2.9,-8.8])bays.push({x:side*3.18,y:2.05,z,s:[.26,4.1,4.2]});\r\n    addInstances(chunk,box,colors.mid,bays);\r\n    const recess=[];for(const side of [-1,1])for(const z of [-2.9,-8.8])recess.push({x:side*3.03,y:2.2,z,s:[.055,2.3,3.1]});\r\n    addInstances(chunk,box,rockMat,recess);\r\n    const glass=[];for(const side of [-1,1])for(const z of [-2.9,-8.8])glass.push({x:side*2.98,y:2.18,z,s:[.06,2.05,2.85]});\r\n    addInstances(chunk,box,transparentGlass,glass);\r\n    const frames=[];for(const side of [-1,1])for(const z of [-2.9,-8.8]){\r\n      frames.push({x:side*2.91,y:3.45,z,s:[.2,.2,3.35]},{x:side*2.91,y:.9,z,s:[.2,.18,3.35]});\r\n      for(const dz of [-1.62,0,1.62])frames.push({x:side*2.91,y:2.2,z:z+dz,s:[.18,2.65,.16]});\r\n    }\r\n    addInstances(chunk,box,colors.light,frames);\r\n    const fascia=[];for(const side of [-1,1])for(const z of [-2.9,-8.8])fascia.push({x:side*2.9,y:3.78,z,s:[.24,.35,3.8]});\r\n    addInstances(chunk,box,colors.accent,fascia);\r\n    const planters=[];for(const side of [-1,1])planters.push({x:side*2.85,y:.25,z:-5.9,s:[.46,.5,10.6]},{x:side*2.85,y:.54,z:-5.9,s:[.52,.12,10.8]});\r\n    addInstances(chunk,box,colors.dark,planters);\r\n    const plants=[];for(const side of [-1,1])for(let sprig=0;sprig<5;sprig++)plants.push({x:side*(2.78+(sprig%2)*.12),y:.61+(sprig%3)*.045,z:-3.7-sprig*1.55,s:[.12,.18,.12]});\r\n    const understory=addInstances(chunk,leafGeo,palmLeafMat,plants);if(understory)understory.name='palm-understory';\r\n    // Segmented, gently curving trunks and broad arched leaf blades replace the\r\n    // old stick-and-box palms. Geometry and instance counts stay fixed per tile.\r\n    const trunks=[],darkFronds=[],lightFronds=[],coconuts=[];\r\n    for(const [x,z] of [[-2.5,-5.1],[2.5,-10.1]]){\r\n      for(let segment=0;segment<6;segment++)trunks.push({x:x+Math.sin(segment*.36)*.16,y:.28+segment*.47,z,s:[.72-segment*.04,.48,.72-segment*.04],rz:-Math.sin(segment*.36)*.12});\r\n      for(let leaf=0;leaf<8;leaf++){\r\n        // Keep the crown aimed into the outer promenade, leaving a clear center lane.\r\n        const heading=x<0?Math.PI:0,angle=heading+(leaf-3.5)*Math.PI/7;\r\n        const entry={x:x+Math.sin(2.1)*.16,y:3.15,z,s:[1,1,1],ry:angle,rz:0};\r\n        (leaf%3===0?lightFronds:darkFronds).push(entry);\r\n      }\r\n      for(let nut=0;nut<3;nut++)coconuts.push({x:x+(nut-1)*.16,y:3.04+(nut%2)*.12,z:z+(nut%2)*.1,s:.12});\r\n    }\r\n    const trunkMesh=addInstances(chunk,postGeo,colors.mid,trunks);if(trunkMesh)trunkMesh.name='palm-trunk-segments';\r\n    addInstances(chunk,palmFrondGeo,palmLeafMat,darkFronds);addInstances(chunk,palmFrondGeo,palmLeafLightMat,lightFronds);\r\n    addInstances(chunk,sphereGeo,palmCoconutMat,coconuts);\r\n    if(variant===0){const tiles=[];for(let row=0;row<7;row++)for(const side of [-1,1])tiles.push({x:side*(.78+(row%2)*.18),y:-.005,z:-1.15-row*1.65,s:[1.25,.035,1.45]});addInstances(chunk,box,colors.light,tiles);}\r\n    if(variant===1){const arches=[];for(const side of [-1,1])for(const z of [-3,-9])arches.push({x:side*3.46,y:3.2,z,s:[.16,5.2,.2],rz:side*.12});addInstances(chunk,torusGeo,colors.rail,arches);}\r\n    if(variant===2){const canopy=[];for(const z of [-2.5,-8.8])canopy.push({x:0,y:4.22,z,s:[5.6,.16,1.6]});addInstances(chunk,box,colors.glass,canopy);}\r\n  }\r\n\r\n  function addCastle(chunk,index,variant=0){\r\n    const bricks=[];\r\n    for(let row=0;row<4;row++)for(const side of [-1,1])bricks.push({x:side*(2.48+(row%2)*.09),y:.42+row*.62,z:-1.8-(row%2)*.5,s:[.58,.54,1.05]});\r\n    addInstances(chunk,brickGeo,colors.dark,bricks);\r\n    addInstances(chunk,beamGeo,colors.mid,[{x:-2.55,y:2.7,z:-6,s:[.45,4.5,.65]},{x:2.55,y:2.7,z:-6,s:[.45,4.5,.65]},{x:0,y:4.55,z:-6,s:[5.25,.38,.7]}]);\r\n    addInstances(chunk,octaGeo,colors.accent,[{x:-2.2,y:.18,z:-3,s:[1.7,.12,3.8]},{x:2.2,y:.18,z:-9,s:[1.7,.12,3.6]}]);\r\n    // Block-built fire and battlements keep the castle's intentionally chunky pixel style.\r\n    addInstances(chunk,box,glowMat,[{x:-2.28,y:.62,z:-4.1,s:[.28,.34,.28]},{x:-2.28,y:.96,z:-4.1,s:[.18,.34,.18]},{x:2.28,y:.62,z:-8.6,s:[.28,.34,.28]},{x:2.28,y:.96,z:-8.6,s:[.18,.34,.18]}]);\r\n    addInstances(chunk,box,colors.light,[{x:-3.15,y:2.35,z:-10,s:[1.1,3.7,1.1]},{x:3.15,y:2.35,z:-2,s:[1.1,3.7,1.1]}]);\r\n    if(variant===0)addInstances(chunk,box,colors.mid,[{x:-2.7,y:.65,z:-5.3,s:[.7,.16,4.8]},{x:-2.7,y:.82,z:-5.3,s:[.12,.12,4.8]}]);\r\n    if(variant===1)addInstances(chunk,octaGeo,colors.accent,[{x:-2.5,y:.22,z:-2.5,s:[.8,.13,4.8]},{x:2.5,y:.22,z:-9,s:[.8,.13,4.8]},{x:-2.55,y:.32,z:-2.5,s:[.35,.15,4.6]},{x:2.55,y:.32,z:-9,s:[.35,.15,4.6]}]);\r\n    if(variant===2)addInstances(chunk,box,colors.glow,[{x:-2.45,y:3.85,z:-3.8,s:[.65,.22,.55]},{x:2.45,y:3.85,z:-8.2,s:[.65,.22,.55]}]);\r\n  }\r\n\r\n  function addRainbow(chunk,index,variant=0){\r\n    addInstances(chunk,torusGeo,colors.glow,[{x:-2.48,y:1.9,z:-4.3,s:[1.15,1.65,1]},{x:2.48,y:2.2,z:-9,s:[1.25,1.8,1]}]);\r\n    addInstances(chunk,octaGeo,colors.glass,[{x:-2.5,y:2.7,z:-7,s:[.6,2.1,.6]},{x:2.55,y:1.3,z:-3,s:[.55,1.8,.55]}]);\r\n    addInstances(chunk,sphereGeo,colors.light,[{x:-4.1,y:3.7,z:-3.2,s:.08},{x:4.1,y:2.1,z:-8.4,s:.1},{x:-3.8,y:1.2,z:-10,s:.07}]);\r\n    if(variant===1)addInstances(chunk,sphereGeo,colors.glass,[{x:-3.6,y:.2,z:-3,s:[1.4,.48,1.1]},{x:3.6,y:.25,z:-8.5,s:[1.5,.55,1.2]}]);\r\n    if(variant===2)addInstances(chunk,torusGeo,colors.glow,[{x:-3.7,y:2.4,z:-5.5,s:[1.4,1.4,.2]},{x:3.7,y:2.4,z:-5.5,s:[1.4,1.4,.2]}]);\r\n  }\r\n\r\n  function addLoopway(chunk,index,variant=0){\r\n    const checks=[];\r\n    for(let row=0;row<5;row++)for(const side of [-1,1])checks.push({x:side*(3.1+(row%2)*.38),y:.3+row*.76,z:-.8-(row%3)*3.1,s:[.58,.68,.72]});\r\n    addInstances(chunk,brickGeo,loopStoneMat,checks);\r\n    addInstances(chunk,brickGeo,colors.mid,checks.filter((_,i)=>i%2===index%2).map(t=>({...t,s:[.59,.28,.73]})));\r\n    // Lower checker cliffs read as an open-air green canyon, not solid corridor walls.\r\n    addInstances(chunk,box,loopStoneMat,[{x:-3.35,y:.52,z:-5.8,s:[1.6,1.05,11.5]},{x:3.35,y:.52,z:-5.8,s:[1.6,1.05,11.5]}]);\r\n    addInstances(chunk,box,colors.mid,[{x:-3.35,y:1.08,z:-5.8,s:[1.6,.12,11.5]},{x:3.35,y:1.08,z:-5.8,s:[1.6,.12,11.5]}]);\r\n    const loop=addMesh(chunk,cachedGeometry('loopway-loop','TorusGeometry',2.05,.13,8,40),colors.accent,{x:index%2===0?-3.9:3.9,y:2.35,z:-6});\r\n    rotating.push({object:loop,axis:'z',rate:.08,base:0});\r\n    addInstances(chunk,ringGeo,colors.glow,[{x:-2.35,y:1.2,z:-2.5,s:[.9,.9,.9]},{x:2.35,y:3.1,z:-9.5,s:[1.1,1.1,1.1]}]);\r\n    if(variant===0)addInstances(chunk,ringGeo,colors.glow,[{x:-3.3,y:2.35,z:-2.5,s:[1.1,1.1,1.1]},{x:3.3,y:2.35,z:-9,s:[1.1,1.1,1.1]}]);\r\n    if(variant===2)addInstances(chunk,box,colors.glass,[{x:-4.1,y:1.8,z:-4.5,s:[.7,3.6,3.2]},{x:4.1,y:1.8,z:-9,s:[.7,3.6,3.2]}]);\r\n  }\r\n\r\n  function addLaboratory(chunk,index,variant=0){\r\n    addInstances(chunk,beamGeo,colors.mid,[{x:-2.55,y:2.3,z:-5,s:[.22,4.4,.28]},{x:2.55,y:2.3,z:-5,s:[.22,4.4,.28]},{x:0,y:4.45,z:-5,s:[5.2,.2,.3]}]);\r\n    const pods=addInstances(chunk,podGeo,transparentGlass,[{x:-2.36,y:1.85,z:-2.8,s:[.55,.85,.55]},{x:2.36,y:2.3,z:-8,s:[.5,.8,.5]}]);\r\n    const core=addInstances(chunk,sphereGeo,glowMat,[{x:-2.36,y:1.85,z:-2.8,s:.19},{x:2.36,y:2.3,z:-8,s:.18}]);\r\n    const turbine=addMesh(chunk,torusGeo,colors.glass,{x:index%2===0?-2.7:2.7,y:2.45,z:-10.2,sx:.74,sy:.74,sz:.74});\r\n    rotating.push({object:turbine,axis:'z',rate:1.2,base:0});\r\n    const streaks=addInstances(chunk,beamGeo,colors.accent,[{x:-2.15,y:.7,z:-7,s:[.055,.055,2.3]},{x:2.18,y:3.65,z:-3,s:[.045,.045,1.8]}]);\r\n    if(streaks)animated.push({object:streaks,kind:'streak'});if(pods)animated.push({object:pods,kind:'pod'});if(core)animated.push({object:core,kind:'core'});\r\n    if(variant===1){const fan=addMesh(chunk,cachedGeometry('lab-fan','TorusGeometry',.82,.09,7,24),colors.glow,{x:index%2===0?-2.7:2.7,y:2.45,z:-10.2});rotating.push({object:fan,axis:'z',rate:1.8,base:0});}\r\n    if(variant===2)addInstances(chunk,beamGeo,colors.glass,[{x:-2.45,y:3.9,z:-2.5,s:[.18,.18,5]},{x:2.45,y:3.9,z:-8.5,s:[.18,.18,5]},{x:-2.45,y:3.65,z:-2.5,s:[.18,.65,.18]},{x:2.45,y:3.65,z:-8.5,s:[.18,.65,.18]}]);\r\n  }\r\n\r\n  function addAsteroid(chunk,index,variant=0){\r\n    addInstances(chunk,sphereGeo,rockMat,[{x:-4.2,y:1.1,z:-2,s:[1.2,.9,.8]},{x:4.3,y:3.1,z:-8,s:[1.3,1.1,1]},{x:-4.5,y:3.8,z:-10,s:[.7,.6,.6]},{x:3.8,y:.55,z:-5.5,s:[.6,.5,.55]}]);\r\n    const rocks=addInstances(chunk,octaGeo,colors.mid,[{x:-3.7,y:2.45,z:-7,s:[.75,.9,.8]},{x:3.8,y:1.35,z:-3.8,s:[.8,.7,.9]},{x:-4.1,y:.45,z:-9,s:[.38,.35,.4]}]);\r\n    // Batch origins stay fixed: rotating the whole instance batch would swing\r\n    // scenery from the shoulders into the player's collision-free corridor.\r\n    const tracers=addInstances(chunk,beamGeo,colors.accent,[{x:-2.5,y:3.1,z:-2.2,s:[.035,.035,3.4]},{x:2.55,y:1.2,z:-9,s:[.03,.03,3.8]}]);\r\n    if(tracers)animated.push({object:tracers,kind:'streak'});\r\n    if(variant===1)addInstances(chunk,box,colors.mid,[{x:-3.3,y:3.8,z:-5,s:[1.2,.12,5]},{x:-3.9,y:3.4,z:-5,s:[.12,.85,.12]},{x:3.3,y:.55,z:-8,s:[1.2,.12,5]}]);\r\n    if(variant===2)addInstances(chunk,octaGeo,rockMat,[{x:-6.8,y:4.5,z:-7,s:[2.3,1.4,3.2]},{x:7,y:-1,z:-4,s:[2.8,1.7,2]}]);\r\n\r\n  }\r\n\r\n  const motifBuilders={tropical:addTropical,castle:addCastle,rainbow:addRainbow,loopway:addLoopway,laboratory:addLaboratory,asteroid:addAsteroid};\r\n  const presentationGeometry=(kind,...args)=>cachedGeometry(`presentation-${kind}-${args.join('-')}`,kind,...args);\r\n  for(let i=0;i<CHUNK_COUNT;i++){\r\n    const chunk=new THREE.Group();chunk.name=`${biomeId}-chunk-${i}`;chunk.userData.slot=i;group.add(chunk);\r\n    const variants=[];\r\n    for(let v=0;v<3;v++){\r\n      const landmark=new THREE.Group();landmark.name=`landmark-section-${v}`;landmark.visible=v===0;chunk.add(landmark);\r\n      (motifBuilders[biomeId]??addTropical)(landmark,i,v);\r\n      variants.push(landmark);\r\n    }\r\n    const sectionLandmarks=[];\r\n    for(let s=0;s<3;s++){\r\n      const landmark=new THREE.Group();landmark.name=`section-landmark-${s}`;landmark.visible=s===section;chunk.add(landmark);\r\n      if(i%2===0)addSectionRecipe({THREE,parent:landmark,recipe:sectionRecipe(biomeId,s),geometry:presentationGeometry,materials:colors});\r\n      sectionLandmarks.push(landmark);\r\n    }\r\n    makeSign(chunk,i);chunks.push({group:chunk,variants,sectionLandmarks});\r\n    // Continuous side frames give the flight a strong sense of forward motion\r\n    // while leaving the center lane completely open for gates and pickups.\r\n    if(['castle','laboratory'].includes(biomeId)){\r\n    const uprights=[],crossbars=[];\r\n    for(const side of [-1,1]){\r\n      for(const z of [-1.2,-6,-10.8])uprights.push({x:side*2.86,y:2.05,z,s:[.16,4.1,.18]});\r\n      for(const y of [.18,2.05,4.05])crossbars.push({x:side*2.86,y,z:-1.2,s:[.42,.09,.16]});\r\n    }\r\n    const frameMat=biomeId==='asteroid'?colors.glow:colors.dark;\r\n    addInstances(chunk,box,frameMat,uprights);addInstances(chunk,box,colors.rail,crossbars);\r\n    // Layered side architecture catches light and reads as real built space:\r\n    // outer wall slabs, inset bays, raised pilasters, cornices and service runs.\r\n    // Asteroid keeps the same depth-built language as floating pylons so the\r\n    // outer-space horizon remains open.\r\n    const wallShell=[],wallInsets=[],wallFrames=[],wallRuns=[];\r\n    for(const side of [-1,1])for(const z of [-3,-9]){\r\n      wallShell.push({x:side*3.55,y:2.16,z,s:[.52,4.15,4.65]});\r\n      wallInsets.push({x:side*3.265,y:2.12,z,s:[.055,3.35,3.72]});\r\n      for(const dz of [-1.93,0,1.93])wallFrames.push({x:side*3.2,y:2.12,z:z+dz,s:[.14,3.72,.19]});\r\n      wallFrames.push({x:side*3.2,y:.31,z,s:[.16,.22,3.95]},{x:side*3.2,y:3.91,z,s:[.16,.2,3.95]});\r\n      wallRuns.push({x:side*3.02,y:3.43,z,s:[.11,.1,3.55]},{x:side*3.02,y:3.58,z,s:[.18,.08,3.65]});\r\n    }\r\n    const shellMat=colors.dark;\r\n    const insetMat=['rainbow','loopway'].includes(biomeId)?colors.glass:colors.mid;\r\n    const shellMesh=addInstances(chunk,box,shellMat,wallShell);if(shellMesh)shellMesh.name='architectural-wall-shell';\r\n    const insetMesh=addInstances(chunk,box,insetMat,wallInsets);if(insetMesh)insetMesh.name='architectural-wall-insets';\r\n    const frameMesh=addInstances(chunk,box,colors.light,wallFrames);if(frameMesh)frameMesh.name='architectural-wall-frames';\r\n    const runMesh=addInstances(chunk,box,biomeId==='asteroid'?colors.glow:colors.rail,wallRuns);if(runMesh)runMesh.name='architectural-service-runs';\r\n    const columnGeo=cachedGeometry('architectural-column','CylinderGeometry',.14,.22,1,8);\r\n    const columnSegments=[];\r\n    for(const side of [-1,1])for(const z of [-1.2,-6,-10.8])columnSegments.push({x:side*3.72,y:2.02,z,s:[.55,4.05,.55]});\r\n    addInstances(chunk,columnGeo,colors.light,columnSegments);\r\n    const ducts=[];for(const side of [-1,1])for(const z of [-2.6,-8.8])ducts.push({x:side*4.08,y:3.48,z,s:[.19,.23,4.3]},{x:side*4.08,y:3.62,z,s:[.1,.08,4.3]});\r\n    addInstances(chunk,box,colors.dark,ducts);\r\n    if(!['asteroid'].includes(biomeId)){\r\n      const lintels=[];\r\n      for(const z of [-1.2,-6,-10.8])lintels.push({x:0,y:4.42,z,s:[5.72,.11,.2]});\r\n      addInstances(chunk,box,['rainbow','loopway'].includes(biomeId)?colors.glass:colors.mid,lintels);\r\n      const lamps=[];for(const z of [-3.6,-9.6])lamps.push({x:0,y:4.31,z,s:[.62,.055,.18]});\r\n      addInstances(chunk,box,colors.glow,lamps);\r\n    }\r\n    }\r\n    if(['rainbow','asteroid'].includes(biomeId)){\r\n      const beacons=[];for(const side of [-1,1])beacons.push({x:side*3.45,y:2.1,z:-6,s:[.09,.09,.42]});\r\n      addInstances(chunk,octaGeo,colors.accent,beacons);\r\n    }\r\n    const sideStreaks=[],overheadStreaks=[];\r\n    for(let line=0;line<10;line++){\r\n      const side=line%2?-1:1,step=Math.floor(line/2);\r\n      sideStreaks.push({x:side*(3.15+(step%3)*.48),y:.42+step*.67,z:-.65-step*2.05,s:[.018,.018,1.15+(step%3)*.42],rz:side*.08});\r\n    }\r\n    for(let line=0;line<5;line++){\r\n      const x=-2.25+line*1.12;\r\n      overheadStreaks.push({x,y:biomeId==='asteroid'?5.1+(line%2)*.8:4.82,z:-1.2-line*2.1,s:[.016,.016,1.3+(line%2)*.4],rx:biomeId==='asteroid'?.12:0});\r\n    }\r\n    addInstances(chunk,beamGeo,colors.accent,sideStreaks);\r\n    addInstances(chunk,beamGeo,colors.glow,overheadStreaks);\r\n  }\r\n\r\n  let stars=null;\r\n  if(biomeId==='asteroid'){\r\n    // Keep a celestial destination in view throughout the biome. Section props\r\n    // alone can sit hundreds of metres away during the first flight section.\r\n    const planetInk=material('MeshStandardMaterial',{color:'#9580b1',emissive:'#2c183e',emissiveIntensity:.25,roughness:1,metalness:0,fog:false});\r\n    const orbitInk=material('MeshBasicMaterial',{color:'#c7e9fa',fog:false});\r\n    const planet=addMesh(group,cachedGeometry('sky-planet','SphereGeometry',1,16,10),planetInk,{x:3.6,y:8,z:-34,sx:3.3,sy:3.3,sz:3.3});\r\n    planet.name='orbital-landmark';\r\n    const orbit=addMesh(group,cachedGeometry('sky-orbit','TorusGeometry',1,.035,5,36),orbitInk,{x:3.6,y:8,z:-34,rx:.65,rz:.18,sx:4.5,sy:4.5,sz:1});\r\n    orbit.name='orbital-ring';\r\n    rotating.push({object:planet,axis:'y',rate:.04,base:0},{object:orbit,axis:'z',rate:.015,base:.18});\r\n    const points=[];let state=0x5319ac7d;\r\n    const rand=()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};\r\n    for(let i=0;i<420;i++)points.push((rand()-.5)*72,(rand()-.5)*46,-8-rand()*105);\r\n    const starGeometry=geometry('BufferGeometry');starGeometry.setAttribute('position',new THREE.Float32BufferAttribute(points,3));\r\n    stars=new THREE.Points(starGeometry,material('PointsMaterial',{color:'#d9eeff',size:.075,sizeAttenuation:true,fog:false,depthWrite:false}));group.add(stars);\r\n  }else if(biomeId==='rainbow'){\r\n    const points=[];let state=0x8248ab1;\r\n    const rand=()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};\r\n    for(let i=0;i<190;i++)points.push((rand()-.5)*44,3+rand()*17,-4-rand()*100);\r\n    const starGeometry=geometry('BufferGeometry');starGeometry.setAttribute('position',new THREE.Float32BufferAttribute(points,3));\r\n    stars=new THREE.Points(starGeometry,material('PointsMaterial',{color:'#ffffff',size:.065,sizeAttenuation:true,fog:false,depthWrite:false}));group.add(stars);\r\n  }\r\n\r\n  let rainbowRoad=null;\r\n  if(biomeId==='rainbow'){\r\n    const bands=[];\r\n    for(let band=0;band<7;band++)for(let tile=0;tile<CHUNK_COUNT;tile++)bands.push({x:-2.46+band*.82,y:-.015,z:-6-tile*CHUNK_LENGTH,s:[.82,.055,CHUNK_LENGTH],color:palette[band]});\r\n    rainbowRoad=addInstances(group,box,rainbowRibbonMat,bands);\r\n    bands.forEach((band,i)=>rainbowRoad?.setColorAt(i,new THREE.Color(band.color)));\r\n    if(rainbowRoad?.instanceColor)rainbowRoad.instanceColor.needsUpdate=true;\r\n  }\r\n\r\n  // Far silhouettes and sky color are scenery-owned, so callers only add the group.\r\n  if(biomeId!=='asteroid'){\r\n    let skyMap=null;\r\n    if(globalThis.document?.createElement){const canvas=document.createElement('canvas');canvas.width=8;canvas.height=128;const ctx=canvas.getContext('2d');if(ctx){const gradient=ctx.createLinearGradient(0,0,0,128);gradient.addColorStop(0,biome.bg);gradient.addColorStop(.65,biome.bg);gradient.addColorStop(1,biome.horizon);ctx.fillStyle=gradient;ctx.fillRect(0,0,8,128);skyMap=new THREE.CanvasTexture(canvas);skyMap.colorSpace=THREE.SRGBColorSpace;textures.add(skyMap);}}\r\n    const sky=addMesh(group,geometry('PlaneGeometry',150,90),material('MeshBasicMaterial',{color:skyMap?'#ffffff':biome.horizon,map:skyMap,side:THREE.DoubleSide,fog:false,depthWrite:false}),{x:0,y:12,z:-120});\r\n    sky.renderOrder=-10;\r\n  }\r\n\r\n  for(const batch of physicalBatches.values()){\r\n    batch.mesh=new THREE.InstancedMesh(batch.geo,batch.mat,batch.items.length);batch.mesh.frustumCulled=false;batch.mesh.name='physical-uv-batch';group.add(batch.mesh);\r\n  }\r\n  group.traverse(node=>{if(node.isMesh&&!node.isInstancedMesh&&node.material?.map&&node.material!==floorMaterial&&node.geometry.type==='BoxGeometry'){\r\n    const source=node.geometry.parameters;node.geometry=physicalBox([source.width,source.height,source.depth],biomeId==='castle'?1:1);\r\n  }});\r\n  if(floorMaterial?.map){const floor=group.children.find(node=>node.material===floorMaterial);const uv=floor.geometry.attributes.uv,size=[5.6,.16,CORRIDOR_LENGTH];\r\n    for(let face=0;face<6;face++){const axes=face<2?[2,1]:face<4?[0,2]:[0,1];for(let c=0;c<4;c++){const i=face*4+c;uv.setXY(i,uv.getX(i)*size[axes[0]]/2,uv.getY(i)*size[axes[1]]/2);}}\r\n  }\r\n  function sectionValue(value){\r\n    const n=Number(value);if(!Number.isFinite(n)||n<0)return 0;\r\n    return Math.floor(n>2?n/30:n)%3;\r\n  }\r\n  function update({distance=0,time=0,section:nextSection=section,speed=5.2,boost=false,turn=0,cameraPosition=null}={}){\r\n    const d=Number.isFinite(distance)?Math.max(0,distance):0;\r\n    const whole=Math.floor(d/CHUNK_LENGTH),offset=d%CHUNK_LENGTH;\r\n    const variant=sectionValue(nextSection);\r\n    for(let i=0;i<CHUNK_COUNT;i++){\r\n      const absolute=whole+i,slot=mod(absolute,CHUNK_COUNT),chunk=chunks[slot];\r\n      chunk.group.position.set(0,0,offset-i*CHUNK_LENGTH);\r\n      // Scrolling motifs keep varying by chunk, independent of the active\r\n      // authored section. Section identity has its own persistent group below.\r\n      const activeVariant=mod(Math.floor(absolute/3),3);\r\n      chunk.variants.forEach((landmark,index)=>{landmark.visible=index===activeVariant;});\r\n      chunk.sectionLandmarks.forEach((landmark,index)=>{landmark.visible=index===variant;});\r\n    }\r\n    const flow=clamp((Number(speed)||5.2)/5.2,.5,1.8)*(boost?1.32:1);\r\n    if(rainbowRoad){\r\n      let instance=0;\r\n      for(let band=0;band<7;band++)for(let tile=0;tile<CHUNK_COUNT;tile++){\r\n        matrixHelper.position.set(-2.46+band*.82,-.015,offset-tile*CHUNK_LENGTH-6);\r\n        matrixHelper.rotation.set(0,0,0);matrixHelper.scale.set(.82,.055,CHUNK_LENGTH);matrixHelper.updateMatrix();\r\n        rainbowRoad.setMatrixAt(instance++,matrixHelper.matrix);\r\n      }\r\n      rainbowRoad.instanceMatrix.needsUpdate=true;\r\n    }\r\n    for(const item of rotating){\r\n      const angle=(Number(time)||0)*item.rate*flow+item.base;\r\n      item.object.rotation[item.axis]=angle;\r\n    }\r\n    for(const item of animated){\r\n      if(item.kind==='streak')item.object.userData.flowPhase=(d*flow)%CHUNK_LENGTH;\r\n      else if(item.kind==='pod')item.object.rotation.y=Math.sin((Number(time)||0)*.4)*.035;\r\n      else if(item.kind==='core')item.object.rotation.y=0;\r\n    }\r\n    if(stars){stars.position.z=0;if(cameraPosition){stars.position.x=cameraPosition.x;stars.position.y=cameraPosition.y-2;}}\r\n    if(cameraPosition)for(const name of ['orbital-landmark','orbital-ring']){const object=group.getObjectByName(name);if(object){object.position.x=3.6+cameraPosition.x;object.position.y=8+cameraPosition.y-2;}}\r\n    for(const batch of physicalBatches.values()){\r\n      let index=0;\r\n      for(const item of batch.items){let node=item.parent,visible=true,z=0;while(node&&node!==group){visible=visible&&node.visible;z+=node.position.z;node=node.parent;}\r\n        if(!visible)continue;batchMatrix.copy(item.matrix);batchMatrix.elements[14]+=z;batch.mesh.setMatrixAt(index++,batchMatrix);\r\n      }\r\n      batch.mesh.count=index;batch.mesh.instanceMatrix.needsUpdate=true;\r\n    }\r\n    // The route remains straight; props and scenery animate around it.\r\n  }\r\n  update({distance:0,section,speed:5.2,turn:0});\r\n\r\n  let disposed=false;\r\n  function dispose(){\r\n    if(disposed)return;disposed=true;\r\n    group.traverse(object=>{if(object.isInstancedMesh)object.dispose?.();});\r\n    group.removeFromParent?.();\r\n    for(const item of geometries)item.dispose?.();\r\n    for(const item of materials)item.dispose?.();\r\n    for(const item of textures)item.dispose?.();\r\n    chunks.length=0;animated.length=0;rotating.length=0;\r\n  }\r\n  return {group,update,dispose};\r\n}\r\n","arcade/plank-flight/biome-visuals-legacy.mjs":"import {themeFor} from '../runner/themes.mjs';\r\n\r\nconst palettes={\r\n tropical:['#64c9ed','#a8d9dc','#ecd498','#fff1cc',24],\r\n castle:['#170d20','#321626','#fa4a30','#ff6037',348],\r\n rainbow:['#080d29','#19254a','#f8f6ff','#ff55c8',234],\r\n loopway:['#4aafe9','#a1d8e8','#c8ff53','#f5ae35',96],\r\n laboratory:['#071b26','#143b4b','#30e5c5','#ff4c8f',177],\r\n asteroid:['#02050e','#081125','#47e6ff','#ff3989',199]\r\n};\r\nconst cache=new Map();\r\nexport function biomeTheme(id){\r\n if(cache.has(id))return cache.get(id);\r\n const [bg,fog,rail,warm,hue]=palettes[id]??palettes.tropical,base=themeFor(0);\r\n const value={...base,name:id,bg,fog,hue,hemi:[id==='castle'?'#ffbc86':'#e7f6ff',fog],light:warm,\r\n  wall:{...base.wall,tint:rail,from:fog,to:bg},neon:{...base.neon,rail,warm,amber:warm,emergency:'#ff6574'}};\r\n cache.set(id,value);return value;\r\n}\r\n\r\n// Screen-fixed thin viewport frame: the center remains clear for posture gates.\r\nexport const COCKPIT_MARKUP=`<svg viewBox=\"0 0 100 160\" preserveAspectRatio=\"none\" aria-hidden=\"true\">\r\n <g class=\"pf-cockpit-shadow\"><ellipse cx=\"50\" cy=\"86\" rx=\"51\" ry=\"57\"/><path d=\"M40 65H60L76 78V95L60 108H40L24 95V78Z M40 65L30 33 M60 65L70 33 M76 78L97 64 M76 95L97 108 M60 108L70 139 M40 108L30 139 M24 95L3 108 M24 78L3 64\"/></g>\r\n <g class=\"pf-cockpit-metal\"><ellipse cx=\"50\" cy=\"86\" rx=\"51\" ry=\"57\"/><path d=\"M40 65H60L76 78V95L60 108H40L24 95V78Z M40 65L30 33 M60 65L70 33 M76 78L97 64 M76 95L97 108 M60 108L70 139 M40 108L30 139 M24 95L3 108 M24 78L3 64\"/>\r\n  <path d=\"M13 76a11 11 0 1 1 0 20a11 11 0 1 1 0-20M87 76a11 11 0 1 1 0 20a11 11 0 1 1 0-20\" fill=\"none\"/>\r\n  <path d=\"M13 72v5m-7 5 4 2m-4 8 4-2m7 9v-5m60-22v5m7 5 4 2m-4 8 4-2m-11 9v-5\" fill=\"none\"/>\r\n  <circle cx=\"13\" cy=\"86\" r=\"2\"/><circle cx=\"87\" cy=\"86\" r=\"2\"/>\r\n </g>\r\n </svg><span class=\"pf-cockpit-tag\">MOM INC. / INK INTERCEPTOR</span>`;\r\n\r\nexport function pickupGeometry(THREE){\r\n const star=new THREE.Shape();\r\n for(let i=0;i<10;i++){const a=i*Math.PI/5-Math.PI/2,r=i%2?.2:.44;star[i?'lineTo':'moveTo'](Math.cos(a)*r,Math.sin(a)*r);}star.closePath();\r\n return {\r\n  speed:new THREE.ConeGeometry(.35,.8,4).rotateZ(-Math.PI/2),\r\n  magnet:new THREE.TorusGeometry(.32,.12,5,12,Math.PI*1.65),\r\n  shield:new THREE.OctahedronGeometry(.44,0),\r\n  coins:new THREE.BoxGeometry(.58,.52,.48),\r\n  lift:new THREE.TorusKnotGeometry(.25,.075,24,4,2,3),\r\n  pineapple:new THREE.DodecahedronGeometry(.4,0).scale(.8,1.25,.8),\r\n  coconut:new THREE.SphereGeometry(.4,7,5),\r\n  tiki:new THREE.BoxGeometry(.45,.8,.36),\r\n  star:new THREE.ExtrudeGeometry(star,{depth:.12,bevelEnabled:false}),\r\n  crest:new THREE.CylinderGeometry(.4,.15,.65,5).rotateZ(Math.PI),\r\n  spring:new THREE.TorusKnotGeometry(.25,.065,40,4,1,5),\r\n  crystal:new THREE.OctahedronGeometry(.45,0).scale(.7,1.3,.7),\r\n  prism:new THREE.CylinderGeometry(.25,.35,.7,3),\r\n  cloud:new THREE.SphereGeometry(.4,8,5).scale(1.5,.7,.7),\r\n  ring:new THREE.TorusGeometry(.36,.1,6,18),\r\n  flower:new THREE.TorusKnotGeometry(.24,.09,32,5,2,5),\r\n  cell:new THREE.CylinderGeometry(.23,.23,.65,8),\r\n  flask:new THREE.CylinderGeometry(.14,.4,.65,8),\r\n  briefcase:new THREE.BoxGeometry(.7,.46,.24),\r\n  comet:new THREE.ConeGeometry(.33,.9,7).rotateZ(-Math.PI/3),\r\n  satellite:new THREE.BoxGeometry(.75,.18,.4),\r\n  orbital:new THREE.TorusGeometry(.32,.14,5,10)\r\n };\r\n}\r\n","arcade/plank-flight/biome-visuals.mjs":"import {themeFor} from '../runner/themes.mjs';\r\n\r\n// Shared renderer/scenery environment. Surface contrast is separate from the far horizon.\r\nexport const ENVIRONMENTS=Object.freeze({\r\n tropical:Object.freeze({bg:'#82cced',horizon:'#a8d9dc',fog:'#a8d9dc',rail:'#ffae46',warm:'#30b865',hue:36}),\r\n castle:Object.freeze({bg:'#170d20',horizon:'#321626',fog:'#321626',rail:'#fa4a30',warm:'#ff6037',hue:348}),\r\n rainbow:Object.freeze({bg:'#05051c',horizon:'#101339',fog:'#101339',rail:'#6eefff',warm:'#f9a7ff',hue:215}),\r\n loopway:Object.freeze({bg:'#83d3fa',horizon:'#90d4dd',fog:'#90d4dd',rail:'#ffe765',warm:'#61eb76',hue:95}),\r\n laboratory:Object.freeze({bg:'#071b26',horizon:'#143b4b',fog:'#143b4b',rail:'#30e5c5',warm:'#ff4c8f',hue:177}),\r\n asteroid:Object.freeze({bg:'#020309',horizon:'#070d20',fog:'#070d20',rail:'#75caff',warm:'#ff685d',hue:220})\r\n});\r\nconst cache=new Map();\r\nexport function biomeTheme(id){\r\n if(cache.has(id))return cache.get(id);\r\n const {bg,fog,rail,warm,hue}=ENVIRONMENTS[id]??ENVIRONMENTS.tropical,base=themeFor(0);\r\n const value={...base,name:id,bg,fog,hue,hazardInk:['tropical','loopway'].includes(id)?'daylight':'night',hemi:[id==='castle'?'#ffbc86':'#e7f6ff',fog],light:warm,\r\n  wall:{...base.wall,tint:rail,from:fog,to:bg},neon:{...base.neon,rail,warm,amber:warm,emergency:'#ff6574'}};\r\n cache.set(id,value);return value;\r\n}\r\n\r\n// Screen-fixed thin viewport frame: the center remains clear for posture gates.\r\nexport const COCKPIT_MARKUP=`<svg viewBox=\"0 0 100 160\" preserveAspectRatio=\"none\" aria-hidden=\"true\">\r\n <g class=\"pf-cockpit-shadow\"><ellipse cx=\"50\" cy=\"86\" rx=\"51\" ry=\"57\"/><path d=\"M40 65H60L76 78V95L60 108H40L24 95V78Z M40 65L30 33 M60 65L70 33 M76 78L97 64 M76 95L97 108 M60 108L70 139 M40 108L30 139 M24 95L3 108 M24 78L3 64\"/></g>\r\n <g class=\"pf-cockpit-metal\"><ellipse cx=\"50\" cy=\"86\" rx=\"51\" ry=\"57\"/><path d=\"M40 65H60L76 78V95L60 108H40L24 95V78Z M40 65L30 33 M60 65L70 33 M76 78L97 64 M76 95L97 108 M60 108L70 139 M40 108L30 139 M24 95L3 108 M24 78L3 64\"/></g>\r\n </svg><svg class=\"pf-cockpit-emblem\" viewBox=\"0 0 40 24\" aria-hidden=\"true\"><path d=\"M4 20V4L14 14L20 5L26 14L36 4V20H4Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"/></svg>`;\r\n\r\nexport function pickupGeometry(THREE){\r\n const star=new THREE.Shape();\r\n for(let i=0;i<10;i++){const a=i*Math.PI/5-Math.PI/2,r=i%2?.2:.44;star[i?'lineTo':'moveTo'](Math.cos(a)*r,Math.sin(a)*r);}star.closePath();\r\n return {\r\n  speed:new THREE.ConeGeometry(.35,.8,4).rotateZ(-Math.PI/2),\r\n  magnet:new THREE.TorusGeometry(.32,.12,5,12,Math.PI*1.65),\r\n  shield:new THREE.OctahedronGeometry(.44,0),\r\n  coins:new THREE.BoxGeometry(.58,.52,.48),\r\n  lift:new THREE.TorusKnotGeometry(.25,.075,24,4,2,3),\r\n  pineapple:new THREE.DodecahedronGeometry(.4,0).scale(.8,1.25,.8),\r\n  coconut:new THREE.SphereGeometry(.4,7,5),\r\n  tiki:new THREE.BoxGeometry(.45,.8,.36),\r\n  star:new THREE.ExtrudeGeometry(star,{depth:.12,bevelEnabled:false}),\r\n  crest:new THREE.CylinderGeometry(.4,.15,.65,5).rotateZ(Math.PI),\r\n  spring:new THREE.TorusKnotGeometry(.25,.065,40,4,1,5),\r\n  crystal:new THREE.OctahedronGeometry(.45,0).scale(.7,1.3,.7),\r\n  prism:new THREE.CylinderGeometry(.25,.35,.7,3),\r\n  cloud:new THREE.SphereGeometry(.4,8,5).scale(1.5,.7,.7),\r\n  ring:new THREE.TorusGeometry(.36,.1,6,18),\r\n  flower:new THREE.TorusKnotGeometry(.24,.09,32,5,2,5),\r\n  cell:new THREE.CylinderGeometry(.23,.23,.65,8),\r\n  flask:new THREE.CylinderGeometry(.14,.4,.65,8),\r\n  briefcase:new THREE.BoxGeometry(.7,.46,.24),\r\n  comet:new THREE.ConeGeometry(.33,.9,7).rotateZ(-Math.PI/3),\r\n  satellite:new THREE.BoxGeometry(.75,.18,.4),\r\n  orbital:new THREE.TorusGeometry(.32,.14,5,10)\r\n };\r\n}\r\n","arcade/plank-flight/biomes.mjs":"// Original MYR5 / MOM Inc. courses. Timings are simulation flight seconds.\r\nexport const BIOME_SECONDS=90;\r\nexport const SECTION_SECONDS=30;\r\nexport const PORTAL_REST_SECONDS=3;\r\nconst pickup=(id,name,benefit,shape)=>Object.freeze({id,name,benefit,shape});\r\nexport const BIOMES=Object.freeze([\r\n {id:'tropical',name:'MYR5 Crate Coast',sections:['Palm Crate Pier','Mango Quarry','Sunset Cargo Run'],pickups:[\r\n  pickup('pineapple-boost','Pineapple Turbo','speed','pineapple'),pickup('coconut-shield','Coconut Guard','shield','coconut'),pickup('tiki-magnet','Cargo Tiki','magnet','tiki')]},\r\n {id:'castle',name:'MOM Inc. Ember Bureau',sections:['Morale Drawbridge','Lava Compliance Hall','Executive Furnace'],pickups:[\r\n  pickup('morale-star','Morale Dividend','coins','star'),pickup('ember-ward','Compliance Ward','shield','crest'),pickup('spring-seal','Furnace Turbo','speed','cell')]},\r\n {id:'rainbow',name:'MYR5 Prism Skyway',sections:['Spectrum Causeway','Cloud Ribbon','Prism Summit'],pickups:[\r\n  pickup('spectrum-boost','Spectrum Turbo','speed','crystal'),pickup('prism-magnet','Prism Collector','magnet','prism'),pickup('rainbow-mode','Rainbow Mode','rainbow','star')]},\r\n {id:'loopway',name:'MYR5 Ring Meadow',sections:['Green Ring Sprint','Loopway Orchard','Hilltop Gold Run'],pickups:[\r\n  pickup('golden-ring','Meadow Dividend','coins','ring'),pickup('meadow-spring','Meadow Lift','lift','spring'),pickup('pollen-magnet','Pollen Collector','magnet','flower')]},\r\n {id:'laboratory',name:'MOM Inc. Lift Laboratory',sections:['Jetpack Intake','Unlicensed Innovation','Salary Escape Tunnel'],pickups:[\r\n  pickup('ignition-cell','Ignition Cell','speed','cell'),pickup('lab-barrier','Safety Bubble','shield','flask'),pickup('salary-cache','Salary Cache','coins','briefcase')]},\r\n {id:'asteroid',name:'MYR5 Octant Drift',sections:['Debris Approach','Octant Spoke Field','Starward Exit'],pickups:[\r\n  pickup('ion-charge','Ion Charge','speed','comet'),pickup('gravity-cage','Gravity Collector','magnet','satellite'),pickup('satellite-shield','Satellite Guard','shield','orbital')]},\r\n].map(b=>Object.freeze({...b,sections:Object.freeze(b.sections),pickups:Object.freeze(b.pickups)})));\r\nexport function biomeAt(index=0,elapsed=0,{portal=false,restElapsed=0}={}){\r\n const ordinal=Math.max(0,Math.floor(index)),b=BIOMES[ordinal%BIOMES.length],next=BIOMES[(ordinal+1)%BIOMES.length];\r\n const t=Math.max(0,Math.min(BIOME_SECONDS,elapsed)),section=Math.min(2,Math.floor((t+1e-8)/SECTION_SECONDS));\r\n const turnElapsed=t-section*SECTION_SECONDS,active=false;\r\n return {id:b.id,name:b.name,index:ordinal,cycle:Math.floor(ordinal/BIOMES.length),section,sectionName:b.sections[section],\r\n  elapsed:t,remaining:Math.max(0,BIOME_SECONDS-t),portal,restElapsed,nextId:next.id,nextName:next.name,\r\n  turn:{active,progress:active?Math.max(0,turnElapsed):1,direction:(ordinal+section)%2?1:-1}};\r\n}\r\n","arcade/plank-flight/catalog.mjs":"// Original MYR5 Plank Flight catalog. Reference names are audit metadata only; they are\r\n// not display names, art direction, or claims about the original games' physics.\r\nconst deepFreeze = value => {\r\n  if (value && typeof value === 'object' && !Object.isFrozen(value)) {\r\n    Object.values(value).forEach(deepFreeze);\r\n    Object.freeze(value);\r\n  }\r\n  return value;\r\n};\r\n\r\nexport const VEHICLES = deepFreeze([\r\n  {id:'comet-hopper',name:'Comet Hopper',auditReference:'Bad As Hog',tier:'default',price:0,duration:20,handling:{lateral:1.05,vertical:1.16,acceleration:1.08,damping:0.78,radius:0.075},trait:'rise-hop'},\r\n  {id:'foundry-walker',name:'Foundry Walker',auditReference:\"Lil' Stomper\",tier:'default',price:0,duration:22,handling:{lateral:0.82,vertical:0.80,acceleration:0.86,damping:1.18,radius:0.14},trait:'one-impact-armor'},\r\n  {id:'ribbon-skimmer',name:'Ribbon Skimmer',auditReference:'Mr. Cuddles',tier:'default',price:0,duration:20,handling:{lateral:1.18,vertical:0.97,acceleration:1.12,damping:0.68,radius:0.075},trait:'smooth-weave'},\r\n  {id:'polarity-shell',name:'Polarity Shell',auditReference:'Gravity Suit',tier:'default',price:0,duration:20,handling:{lateral:0.96,vertical:1.08,acceleration:0.92,damping:0.96,radius:0.09},trait:'gravity-cycle'},\r\n  {id:'foldwing',name:'Foldwing',auditReference:'Crazy Freaking Teleporter',tier:'default',price:0,duration:18,handling:{lateral:1.08,vertical:0.92,acceleration:1.18,damping:0.72,radius:0.07},trait:'preview-fold'},\r\n  {id:'pipwing',name:'Pipwing',auditReference:'Profit Bird',tier:'default',price:0,duration:20,handling:{lateral:0.88,vertical:1.24,acceleration:1.04,damping:0.92,radius:0.075},trait:'rise-pulse'},\r\n  {id:'tide-glider',name:'Tide Glider',auditReference:'Wave Rider',tier:'expansion',price:180,duration:24,handling:{lateral:1.00,vertical:1.12,acceleration:0.96,damping:0.86,radius:0.08},trait:'wave-follow'},\r\n  {id:'triad-bulwark',name:'Triad Bulwark',auditReference:'S.A.M. component mech',tier:'special',price:0,duration:24,handling:{lateral:0.91,vertical:0.92,acceleration:0.84,damping:1.12,radius:0.17},trait:'lean-deflector'}\r\n]);\r\n\r\nconst vehicleIndex = Object.fromEntries(VEHICLES.map(vehicle => [vehicle.id, vehicle]));\r\nexport const vehicleById = id => vehicleIndex[id] ?? null;\r\n\r\n// Numeric prices are MYR5 design proposals in game coins, not copied storefront prices.\r\nexport const GADGETS = deepFreeze([\r\n  {id:'orbit-magnet',name:'Orbit Magnet',price:90,auditReference:'Coin Magnet / Magnetic Tokens',effect:{magnetRadius:0.30}},\r\n  {id:'quiet-vector',name:'Quiet Vector',price:110,auditReference:'Gravity Belt',effect:{verticalResponse:0.10}},\r\n  {id:'spark-dampener',name:'Spark Dampener',price:130,auditReference:'Dezapinator',effect:{initialShield:1}},\r\n  {id:'early-signal',name:'Early Signal',price:120,auditReference:'Ezy-Dodge / Missile Jammer',effect:{warningLead:4}},\r\n  {id:'coin-prism',name:'Coin Prism',price:150,auditReference:'Gemology',effect:{coinBonus:1}},\r\n  {id:'rescue-echo',name:'Rescue Echo',price:170,auditReference:'Final Blast / Free Ride',effect:{crashGrace:0.30,impactBuffer:1}},\r\n  {id:'shell-courier',name:'Shell Courier',price:200,auditReference:'Free Ride',effect:{initialVehicle:'comet-hopper'}},\r\n  {id:'dash-battery',name:'Dash Battery',price:140,auditReference:'Boost',effect:{powerupDuration:2}},\r\n  {id:'route-lantern',name:'Route Lantern',price:100,auditReference:'X-Ray Specs',effect:{coinPreview:8}},\r\n  {id:'lucky-signal',name:'Lucky Signal',price:160,auditReference:'Lucky Last / Token Gift',effect:{eventTokenChance:0.22},eventBlocked:true},\r\n  {id:'component-call',name:'Component Call',price:190,auditReference:'S.A.M. letters',effect:{componentChance:0.18},eventBlocked:true},\r\n  {id:'sideband-collector',name:'Sideband Collector',price:125,auditReference:'Air Barrys',effect:{npcBonus:1}}\r\n]);\r\n\r\nexport const POWERUPS = deepFreeze([\r\n  {id:'coin-current',name:'Coin Current',family:'cash-dash',auditReference:'Cash Dash',duration:6,effect:{magnetRadius:0.8,clearHazards:true,coinBonus:1}},\r\n  {id:'visual-afterburn',name:'Visual Afterburn',family:'boost',auditReference:'Boost',duration:5,effect:{visualRush:true}},\r\n  {id:'aegis-pulse',name:'Aegis Pulse',family:'shield',auditReference:'Shield',duration:0,effect:{shield:1}},\r\n  {id:'slow-orbit',name:'Slow Orbit',family:'rocket-time',auditReference:'Rocket Time',duration:6,effect:{threatSlow:1.7}}\r\n]);\r\n\r\nexport const COSMETICS = deepFreeze([\r\n  {id:'ion-afterglow',name:'Ion Afterglow',kind:'cosmetic',price:35,tint:'#a67cff',equipable:false},\r\n  {id:'tideline-mint',name:'Tideline Mint',kind:'cosmetic',price:45,tint:'#55e6c1',equipable:false},\r\n  {id:'ember-trace',name:'Ember Trace',kind:'cosmetic',price:55,tint:'#ff926b',equipable:false},\r\n  {id:'signal-gold',name:'Signal Gold',kind:'cosmetic',price:65,tint:'#ffd66e',equipable:false},\r\n  {id:'orbit-badge',name:'Orbit Badge',kind:'cosmetic',price:40,tint:'#74bfff',equipable:false}\r\n]);\r\nconst cosmeticIndex = Object.fromEntries(COSMETICS.map(item => [item.id,item]));\r\nexport const cosmeticById = id => cosmeticIndex[id] ?? null;\r\n\r\n// Permanent upgrades have bounded passive effects, with levels persisted by the wallet.\r\nexport const UPGRADES = deepFreeze([\r\n  {id:'magnet-tuning',name:'Magnet Tuning',kind:'upgrade',price:80,maxLevel:3,effectPerLevel:{magnetRadius:0.08},equipable:false},\r\n  {id:'shield-capacitor',name:'Shield Capacitor',kind:'upgrade',price:120,maxLevel:2,effectPerLevel:{maxShield:1,initialShield:1},equipable:false},\r\n  {id:'vehicle-reserve',name:'Vehicle Reserve',kind:'upgrade',price:100,maxLevel:3,effectPerLevel:{vehicleDuration:2},equipable:false},\r\n  {id:'signal-relay',name:'Signal Relay',kind:'upgrade',price:90,maxLevel:3,effectPerLevel:{warningLead:1.5},equipable:false}\r\n]);\r\n\r\nexport const SHOP_ITEMS = deepFreeze([\r\n  ...VEHICLES.filter(item => item.price > 0).map(item => ({id:item.id,name:item.name,kind:'vehicle',price:item.price,equipable:false})),\r\n  ...GADGETS.map(item => ({id:item.id,name:item.name,kind:'gadget',price:item.price,equipable:true})),\r\n  ...COSMETICS.map(item => ({id:item.id,name:item.name,kind:item.kind,price:item.price,tint:item.tint,equipable:false})),\r\n  ...UPGRADES.map(item => ({id:item.id,name:item.name,kind:item.kind,price:item.price,maxLevel:item.maxLevel,equipable:false}))\r\n]);\r\n","arcade/plank-flight/cockpit.mjs":"const clamp=n=>Math.max(0,Math.min(1,n));\r\nexport const motorcycleWorld=id=>id==='tropical'||id==='rainbow';\r\n\r\n// Presentation follows simulation time. Menus, dropped frames and tracking\r\n// pauses cannot silently add continuous flight time or heat.\r\nexport function advanceCockpit(previous={}, {time=0,phase='ready',paused=false,pose={},height=0,boost=0}={}){\r\n const rewind=Number.isFinite(previous.time)&&time<previous.time;\r\n const dt=Number.isFinite(previous.time)&&!rewind?Math.max(0,Math.min(1,time-previous.time)):0;\r\n const firing=phase==='flying'&&!paused&&pose.controlValid===true&&!pose.resting&&(Number.isFinite(pose.height01)?pose.height01:height)>.12;\r\n const oldHeat=rewind?0:(previous.heat??0);\r\n return {time,heat:clamp(oldHeat+(paused?0:firing?dt/14:-dt*.55)),firing,boost:clamp(paused?(previous.boost??boost):boost),\r\n   airSeconds:firing?(rewind?0:previous.airSeconds??0)+dt:0,\r\n   wheelie:pose.resting||!pose.controlValid?0:clamp((Number.isFinite(pose.height01)?pose.height01:height)*1.15)};\r\n}\r\n\r\nconst thruster=side=>`<div class=\"pf-thruster\" data-thruster=\"${side}\" data-heat=\"0\" data-firing=\"false\">\r\n <svg data-hand=\"${side}\" viewBox=\"0 0 100 110\" aria-hidden=\"true\">\r\n  <defs><linearGradient id=\"pf-metal-${side}\" x2=\"1\" y2=\".3\"><stop stop-color=\"#18222b\"/><stop offset=\".45\" stop-color=\"#a3b8c5\"/><stop offset=\".65\" stop-color=\"#435763\"/><stop offset=\"1\" stop-color=\"#111c25\"/></linearGradient><linearGradient id=\"pf-fire-${side}\" x2=\"0\" y2=\"1\"><stop stop-color=\"#fff3b3\"/><stop offset=\".3\" stop-color=\"#ffb52e\"/><stop offset=\"1\" stop-color=\"#ff3c1000\"/></linearGradient></defs>\r\n  <ellipse class=\"pf-thruster-halo\" cx=\"50\" cy=\"100\" rx=\"46\" ry=\"30\"/>\r\n  <path class=\"pf-thruster-flame\" d=\"M31 98L23 145 42 190 50 165 60 198 79 143 69 98Z\" fill=\"url(#pf-fire-${side})\"/>\r\n  <path d=\"M25 25L35 16H65L75 25 80 76 69 100H31L20 76Z\" fill=\"url(#pf-metal-${side})\" stroke=\"#0a1018\" stroke-width=\"4\"/>\r\n  <path class=\"pf-thruster-shell\" d=\"M30 31H70L73 68 63 84H37L27 68Z\" stroke=\"#111d27\" stroke-width=\"3\"/>\r\n  <path d=\"M38 37V69M50 35V72M62 37V69\" stroke=\"#1a2733\" stroke-width=\"5\"/>\r\n  <path d=\"M32 86H68L64 100H36Z\" fill=\"#101d28\" stroke=\"#7996a5\" stroke-width=\"2\"/>\r\n  <path class=\"pf-thruster-hot-ring\" d=\"M34 94H66\" stroke-width=\"6\"/>\r\n  <path d=\"M35 23H65M25 74H75\" stroke=\"#b7cbd5\" stroke-width=\"3\"/>\r\n  <circle cx=\"28\" cy=\"29\" r=\"3\" fill=\"#d1e3e9\"/><circle cx=\"72\" cy=\"29\" r=\"3\" fill=\"#d1e3e9\"/>\r\n </svg></div>`;\r\n\r\nexport const FLIGHT_CONTROLS_MARKUP=`<div class=\"pf-propulsion\" aria-hidden=\"true\">${thruster('left')}${thruster('right')}</div>\r\n <div class=\"pf-motorcycle\" data-motorcycle data-wheelie=\"0\" hidden aria-hidden=\"true\">\r\n  <svg viewBox=\"0 0 360 160\"><defs><linearGradient id=\"pf-bike-metal\" x2=\"0\" y2=\"1\"><stop stop-color=\"#bdcbd1\"/><stop offset=\".4\" stop-color=\"#415564\"/><stop offset=\"1\" stop-color=\"#17232d\"/></linearGradient></defs>\r\n   <g class=\"pf-bike-wheel\"><path d=\"M159 70Q180 45 201 70L214 160H146Z\" fill=\"#121820\" stroke=\"#05090e\" stroke-width=\"7\"/><path d=\"M167 77L192 87M165 98L196 108M162 121L199 132\" stroke=\"#4e5a61\" stroke-width=\"6\"/></g>\r\n   <g class=\"pf-bike-frame\"><path d=\"M105 160L130 92 145 71H215L230 92 255 160\" fill=\"#e45a27\" stroke=\"#602d25\" stroke-width=\"5\"/><path d=\"M55 113L107 91 145 103M305 113L253 91 215 103\" fill=\"none\" stroke=\"#111923\" stroke-width=\"17\" stroke-linecap=\"round\"/><path d=\"M65 111L109 96 145 107M295 111L251 96 215 107\" fill=\"none\" stroke=\"url(#pf-bike-metal)\" stroke-width=\"8\"/><path d=\"M136 91Q180 73 224 91L213 125H147Z\" fill=\"#172832\" stroke=\"#aac1cb\" stroke-width=\"3\"/><circle cx=\"169\" cy=\"103\" r=\"12\" fill=\"#083b3d\" stroke=\"#71dfc1\" stroke-width=\"2\"/><path d=\"M169 103L173 96\" stroke=\"#fff3c1\" stroke-width=\"2\"/><rect x=\"189\" y=\"94\" width=\"18\" height=\"16\" rx=\"3\" fill=\"#74e8bd\"/><path d=\"M157 141H203\" stroke=\"#ffc84a\" stroke-width=\"7\"/>\r\n   </g></svg>\r\n </div><div class=\"pf-stunt\" data-stunt hidden aria-hidden=\"true\"><span>✦</span> GOLD FLIP</div>`;\r\n","arcade/plank-flight/economy.mjs":"// Device-only, owner-scoped arcade coins. Fitness XP is owned by PerformanceWorkout.\r\n// A readwrite transaction over the owner row serializes competing tabs and purchases.\r\nconst fresh=()=>({version:1,balance:0,tokens:0,stars:0,bestDistance:0,owned:[],loadout:[],selectedVehicle:null,selectedCosmetic:null,upgrades:{},runs:{},receipts:{}});\r\nconst clone=v=>structuredClone(v);\r\nfunction integer(v,max=1000000){if(!Number.isSafeInteger(v)||v<0||v>max)throw Error('Invalid game reward.');return v;}\r\n// Original device-only MYR5 progression rules, not reference-game reward odds.\r\nfunction progress(s){\r\n  const runs=Object.values(s.runs);\r\n  s.rank=1+Math.floor(s.stars/3)%10;s.prestige=Math.floor(s.stars/30);s.rankStarsProgress=s.stars%3;\r\n  s.achievements=[];\r\n  if(runs.some(r=>r.finalized))s.achievements.push('first-flight');\r\n  if(runs.reduce((sum,r)=>sum+(r.coins??0),0)>=100)s.achievements.push('coins-100');\r\n  if(s.bestDistance>=250)s.achievements.push('distance-250');\r\n  if(runs.some(r=>r.componentSets>0))s.achievements.push('component-mech');\r\n  s.dailyComponents??={days:[]};\r\n  const daily=s.dailyComponents;daily.totalDays=daily.days.length;daily.progress=daily.totalDays%5;daily.rewards=Math.floor(daily.totalDays/5);\r\n  return s;\r\n}\r\nexport async function openFlightWallet({owner,indexedDB=globalThis.indexedDB,name='myr5-plank-flight-v1',now=()=>Date.now()}={}){\r\n  if(typeof owner!=='string'||!owner||owner==='guest:unavailable')throw Error('Device coin storage needs an available profile.');\r\n  if(!indexedDB)throw Error('Device coin storage is unavailable.');\r\n  const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(name,1);request.onupgradeneeded=()=>request.result.createObjectStore('wallets');request.onerror=()=>reject(request.error);request.onblocked=()=>reject(Error('Close another Coach tab to update coin storage.'));request.onsuccess=()=>resolve(request.result);});\r\n  let closed=false;db.onversionchange=()=>{closed=true;db.close();};\r\n  const transact=operation=>new Promise((resolve,reject)=>{\r\n    if(closed){reject(Error('Coin storage closed. Reopen Coach.'));return;}\r\n    const tx=db.transaction('wallets','readwrite'),store=tx.objectStore('wallets'),get=store.get(owner);let result,error;\r\n    get.onsuccess=()=>{try{const state=progress(get.result??fresh());result=operation(state);progress(state);store.put(state,owner);}catch(e){error=e;tx.abort();}};\r\n    tx.oncomplete=()=>resolve(clone(result));tx.onerror=()=>reject(error??tx.error);tx.onabort=()=>reject(error??tx.error??Error('Coin save interrupted.'));\r\n  });\r\n  return {owner,read:()=>transact(s=>s),\r\n    record(runId,events=[]){if(typeof runId!=='string'||!runId)return Promise.reject(Error('Missing game identity.'));\r\n      return transact(s=>{const run=s.runs[runId]??={events:{},coins:0,tokens:0,finalized:false,spun:false};\r\n        for(const event of events){if(!event?.id)throw Error('Missing reward identity.');\r\n          const value=JSON.stringify({type:event.type,amount:event.amount??0});\r\n          if(run.events[event.id]){if(run.events[event.id]!==value)throw Error('A saved reward changed.');continue;}\r\n          if(run.finalized)throw Error('This game is already saved.');\r\n          if(event.type==='coin'){const amount=integer(event.amount,10000);s.balance+=amount;run.coins+=amount;}\r\n          else if(event.type==='token'){const amount=integer(event.amount??1,10);s.tokens+=amount;run.tokens+=amount;}\r\n          else if(event.type==='mission')s.stars+=integer(event.amount??1,3);\r\n          else if(event.type==='component-set'){\r\n            const day=new Date(now()).toISOString().slice(0,10),daily=s.dailyComponents;\r\n            run.componentSets=(run.componentSets??0)+1;\r\n            if(!daily.days.includes(day)){\r\n              daily.days.push(day);\r\n              if(daily.days.length%5===0)s.balance+=100;\r\n            }\r\n          }\r\n          else throw Error('Unknown game reward.');\r\n          run.events[event.id]=value;\r\n        }return s;});},\r\n    finish(runId,distance){return transact(s=>{const run=s.runs[runId]??={events:{},coins:0,tokens:0,spun:false};run.finalized=true;s.bestDistance=Math.max(s.bestDistance,Math.max(0,Number(distance)||0));return s;});},\r\n    spin(runId){return transact(s=>{if(!s.runs[runId]?.finalized)throw Error('Finish this flight before opening the reward.');\r\n      // Redeem all completed runs atomically, including rewards left behind before a restart.\r\n      for(const run of Object.values(s.runs))if(run.finalized&&!run.spun){\r\n        const reward=(run.tokens??0)*25;s.tokens=Math.max(0,s.tokens-(run.tokens??0));s.balance+=reward;run.spun=true;run.spinReward=reward;\r\n      }return s;});},\r\n    buy(id,catalog,{receipt=crypto.randomUUID()}={}){const item=catalog.find(i=>i.id===id);if(!item)return Promise.reject(Error('That item is unavailable.'));\r\n      return transact(s=>{const previous=s.receipts[receipt];if(previous){if(previous!==id)throw Error('Purchase identity changed.');return s;}\r\n        const level=item.kind==='upgrade'?(s.upgrades?.[id]??0):0;if(item.kind==='upgrade'&&level>=(item.maxLevel??1))return s;if(item.kind!=='upgrade'&&s.owned.includes(id))return s;\r\n        const price=integer(item.price*(level+1),1000000);if(s.balance<price)throw Error('Collect more device coins first.');s.balance-=price;if(!s.owned.includes(id))s.owned.push(id);if(item.kind==='upgrade'){s.upgrades??={};s.upgrades[id]=level+1;}s.receipts[receipt]=id;return s;});},\r\n    equip(id,catalog){return transact(s=>{const item=catalog.find(i=>i.id===id);if(!item||!s.owned.includes(id))throw Error('Unlock this item first.');\r\n      if(item.kind==='vehicle'){s.selectedVehicle=s.selectedVehicle===id?null:id;return s;}\r\n      if(item.kind==='cosmetic'){s.selectedCosmetic=s.selectedCosmetic===id?null:id;return s;}\r\n      if(item.kind==='upgrade')return s;\r\n      if(s.loadout.includes(id))s.loadout=s.loadout.filter(x=>x!==id);else{const max=2;if(s.loadout.length>=max)throw Error('Choose up to two gadgets.');s.loadout.push(id);}return s;});},\r\n    close(){closed=true;db.close();}};\r\n}\r\n","arcade/plank-flight/flight-fx.mjs":"const clamp=(value,min=0,max=1)=>Math.max(min,Math.min(max,value));\r\nconst finite=(value,fallback=0)=>Number.isFinite(Number(value))?Number(value):fallback;\r\nconst title=value=>String(value??'').replace(/[-_]/g,' ').replace(/\\b\\w/g,char=>char.toUpperCase());\r\n\r\nconst cosmeticTints={\r\n 'ion-afterglow':'#a67cff','tideline-mint':'#55e6c1','ember-trace':'#ff926b',\r\n 'signal-gold':'#ffd66e','orbit-badge':'#74bfff'\r\n};\r\n\r\nconst activeItems=run=>{\r\n const time=finite(run.time),items=[];\r\n const add=(label,icon,until)=>{const remaining=Math.max(0,finite(until)-time);if(remaining>0)items.push({label,icon,remaining});};\r\n if(run.powerup){\r\n  const names={'cash-dash':['COIN PULL','magnet'],'boost':['AFTERBURN','speed'],'rocket-time':['SLOW ORBIT','ring']};\r\n  const [label,icon]=names[run.powerup.family]??[title(run.powerup.id),'star'];add(label,icon,run.powerup.until);\r\n }\r\n const biome=run.biomeEffects??{};\r\n add('TURBO','speed',biome.speedUntil);\r\n add('COIN PULL','magnet',biome.magnetUntil);\r\n add('RAINBOW GUARD','shield',biome.rainbowUntil);\r\n return items.sort((a,b)=>a.remaining-b.remaining);\r\n};\r\n\r\nexport function createFlightFX(){\r\n let lastTime=0,lastCoins=0,lastHits=0,lastShield=0,lastImpactBuffer=0,lastVehicle=null,lastVehicleHealth=0,lastVehicleUntil=0,lastPowerup=null,lastBiomePickup=null,lastActiveKey='',lastPhase='ready';\r\n let collected=new Set(),cue=null,resetKey=null;\r\n const reset=run=>{\r\n  lastTime=finite(run.time);lastCoins=finite(run.coins);lastHits=finite(run.hits);lastShield=finite(run.shield);lastImpactBuffer=finite(run.impactBuffer);\r\n  lastVehicle=run.vehicle??null;lastVehicleHealth=finite(run.vehicleHealth);lastVehicleUntil=finite(run.vehicleUntil);lastPowerup=run.powerup?.id??null;\r\n  lastBiomePickup=run.lastBiomePickup?.id??null;lastActiveKey=activeItems(run).map(item=>item.label).join('|');lastPhase=run.phase;collected=new Set();\r\n  for(const object of run.objects??[])if(object.collected)collected.add(object.id);\r\n  cue=null;resetKey=`${run.runId}:${finite(run.continuation)}`;\r\n };\r\n const announce=(run,icon,label)=>{cue={icon,label,until:finite(run.time)+1.15};};\r\n return {\r\n  update(run,profile={}){\r\n   const time=finite(run.time),key=`${run.runId}:${finite(run.continuation)}`;\r\n   if(key!==resetKey||time<lastTime)reset(run);\r\n   let event=null;\r\n   const coins=finite(run.coins),hits=finite(run.hits),shield=finite(run.shield),vehicle=run.vehicle??null,health=finite(run.vehicleHealth),powerup=run.powerup?.id??null,biomePickup=run.lastBiomePickup?.id??null;\r\n   if(coins>lastCoins)event={icon:'coin',label:`+${Math.floor(coins-lastCoins)} COINS`};\r\n   if(shield<lastShield)event={icon:'shield',label:'SHIELD ABSORBED'};\r\n   if(hits>lastHits&&shield===lastShield)event={icon:'impact',label:'HIT'};\r\n   if(finite(run.impactBuffer)<lastImpactBuffer)event={icon:'shield',label:'BUFFER USED'};\r\n   if(lastVehicle&&vehicle===lastVehicle&&health<lastVehicleHealth)event={icon:'vehicle',label:health>0?`ARMOR ${health}/${Math.max(1,lastVehicleHealth)}`:'RIDE RELEASED'};\r\n   if(vehicle!==lastVehicle)event={icon:'vehicle',label:vehicle?`${title(vehicle)} ENTRY`:time>=lastVehicleUntil-1e-6?`${title(lastVehicle)} EXPIRED`:`${title(lastVehicle)} ARMOR USED`};\r\n   else if(vehicle&&lastPhase==='ready'&&run.phase==='flying')event={icon:'vehicle',label:`${title(vehicle)} ENTRY`};\r\n   const impactEvent=shield<lastShield||finite(run.impactBuffer)<lastImpactBuffer||(lastVehicle&&vehicle===lastVehicle&&health<lastVehicleHealth)||(lastVehicle&&!vehicle&&time<lastVehicleUntil-1e-6)||hits>lastHits?event:null;\r\n   const newlyCollected=(run.objects??[]).filter(object=>object.collected&&!collected.has(object.id));\r\n   for(const object of newlyCollected)collected.add(object.id);\r\n   const firstCollected=newlyCollected.find(object=>object.type!=='coin')??newlyCollected[0];\r\n   if(firstCollected){\r\n    const names={'coin-current':['COIN PULL','magnet'],'visual-afterburn':['AFTERBURN','speed'],'aegis-pulse':['SHIELD +1','shield'],'slow-orbit':['SLOW ORBIT','ring']};\r\n    const [label,icon]=names[firstCollected.kind]??[title(firstCollected.name??firstCollected.kind??firstCollected.type),firstCollected.type==='vehicle'?'vehicle':firstCollected.type==='coin'?'coin':'star'];\r\n    event={icon,label};\r\n    if(['aegis-pulse','coconut-shield','ember-ward','lab-barrier','satellite-shield'].includes(firstCollected.kind)&&shield<=lastShield)event={icon:'shield',label:'SHIELD FULL'};\r\n   }\r\n   if(biomePickup!==lastBiomePickup&&run.lastBiomePickup){\r\n    const benefit=run.lastBiomePickup.benefit;\r\n    const feedback={speed:['TURBO','speed'],magnet:['COIN PULL','magnet'],shield:['SHIELD +1','shield'],coins:['COIN CACHE','coin'],lift:['+6 COINS','coin'],rainbow:['RAINBOW GUARD · 6s','shield']}[benefit]??[title(benefit),'star'];\r\n    event={icon:feedback[1],label:feedback[0]};\r\n    if(benefit==='shield'&&shield<=lastShield)event={icon:'shield',label:'SHIELD FULL'};\r\n   }\r\n   if(powerup!==lastPowerup&&run.powerup){const names={'cash-dash':['COIN PULL','magnet'],'boost':['AFTERBURN','speed'],'rocket-time':['SLOW ORBIT','ring']};const [label,icon]=names[run.powerup.family]??[title(powerup),'star'];event={icon,label};}\r\n   const activeNow=activeItems(run),activeKey=activeNow.map(item=>item.label).join('|');\r\n   if(lastPowerup&&!powerup)event={icon:'speed',label:'POWER EXPIRED'};\r\n   else if(lastActiveKey){const ended=lastActiveKey.split('|').find(label=>label&&!activeNow.some(item=>item.label===label));if(ended)event={icon:'shield',label:`${ended} ENDED`};}\r\n   if(coins>lastCoins)event={icon:'coin',label:`+${Math.floor(coins-lastCoins)} COINS`};\r\n   if(impactEvent)event=impactEvent;\r\n   if(event)announce(run,event.icon,event.label);\r\n   if(cue&&time>=cue.until)cue=null;\r\n   lastTime=time;lastCoins=coins;lastHits=hits;lastShield=shield;lastImpactBuffer=finite(run.impactBuffer);lastVehicle=vehicle;lastVehicleHealth=health;lastVehicleUntil=finite(run.vehicleUntil);lastPowerup=powerup;lastBiomePickup=biomePickup;lastActiveKey=activeKey;lastPhase=run.phase;\r\n   const loadout=(run.loadout??[]).map(item=>typeof item==='string'?item:item?.id).filter(Boolean);\r\n   const upgrades=Object.keys(run.upgrades??{}).filter(id=>finite(run.upgrades[id])>0);\r\n   const cosmetic=typeof profile.selectedCosmetic==='string'?profile.selectedCosmetic:profile.selectedCosmetic?.id??run.cosmetic?.id??run.cosmetic??'';\r\n   const tint=cosmeticTints[cosmetic]??profile.selectedCosmetic?.tint??null;\r\n   const effect=run.powerup?.effect??{};\r\n   const timed=[...activeNow];\r\n   const boostRemaining=Math.max(finite(effect.visualRush?run.powerup?.until:0)-time,finite(run.biomeEffects?.speedUntil)-time,0);\r\n   return {cue,active:timed,loadout,upgrades,cosmetic,tint,shield:Math.max(0,shield),vehicle,vehicleHealth:Math.max(0,health),vehicleRemaining:Math.max(0,finite(run.vehicleUntil)-time),vehicleMaxHealth:vehicle==='foundry-walker'||vehicle==='triad-bulwark'?2:vehicle?1:0,slowOrbit:run.powerup?.family==='rocket-time'&&time<finite(run.powerup.until),\r\n    magnet:finite(run.effects?.magnetRadius)>0||timed.some(item=>item.icon==='magnet'),coinBonus:finite(run.effects?.coinBonus)+(run.powerup?.family==='cash-dash'?1:0),boostFactor:boostRemaining>0?clamp(boostRemaining/Math.max(1,run.powerup?.until-run.powerup?.startedAt||4)):0,\r\n    protected:shield>0||finite(run.biomeEffects?.rainbowUntil)>time||health>0||(run.powerup?.family==='cash-dash'&&time<finite(run.powerup.until)),impactBuffer:finite(run.impactBuffer),passiveCount:loadout.length,upgradeCount:upgrades.length,\r\n    blocked:run.event?(run.loadout??[]).filter(item=>['lucky-signal','component-call'].includes(typeof item==='string'?item:item?.id)).length:0,\r\n    profileCosmetic:cosmetic};\r\n  }\r\n };\r\n}\r\n","arcade/plank-flight/flight-legacy.css":".plank-flight-host{position:relative!important;overflow:hidden!important;isolation:isolate;background:var(--pf-bg,#06020a)!important;min-height:min(100dvh,900px)}\r\n.plank-flight-host>video{inset:auto!important;right:8px!important;bottom:max(76px,calc(env(safe-area-inset-bottom) + 68px))!important;left:auto!important;top:auto!important;width:76px!important;height:102px!important;max-width:22vw!important;aspect-ratio:3/4;object-fit:cover;z-index:6!important;border:2px solid #ffffffaa!important;border-radius:13px!important;box-shadow:0 5px 18px #000a!important;background:#05030a!important}\r\n.plank-flight-host #coachOverlay{visibility:hidden}\r\n.plank-flight .pf-pace-current strong,.plank-flight .pf-pace-next strong{white-space:normal;text-overflow:clip;line-height:1.1}\r\n.plank-flight[data-biome=\"castle\"] .pf-canvas{image-rendering:pixelated}\r\n.plank-flight.is-rainbow-mode{box-shadow:inset 0 0 26px #96f5ff80,inset 8px 0 14px #ff8be666,inset -8px 0 14px #ffe27666}.plank-flight.is-rainbow-mode .pf-aim{border-color:#fff3b0;opacity:.7}\r\n.pf-cockpit ellipse{fill:none;vector-effect:non-scaling-stroke}.pf-cockpit-shadow ellipse{stroke:#06090de6;stroke-width:4.5}.pf-cockpit-metal ellipse{stroke:#8397a9d9;stroke-width:1.35}\r\n.plank-flight-host>.workout-performance-controls{display:none!important}\r\n.plank-flight-host .camera-workout-counter,.plank-flight-host .camera-workout-status,.plank-flight-host .camera-workout-adjust{display:none!important}\r\n.plank-flight{--pf-accent:#20f0d0;--pf-warm:#e8fffc;--pf-alert:#ff2a5a;--pf-bg:#020a0a;position:absolute;inset:0;min-height:100%;overflow:hidden;color:#f5f6ff;background:radial-gradient(ellipse at 50% 36%,color-mix(in srgb,var(--pf-accent) 15%,transparent),transparent 52%),linear-gradient(180deg,var(--pf-bg),#03040b 76%);font-family:Inter,ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",sans-serif;letter-spacing:.01em;touch-action:manipulation}\r\n.pf-canvas,.pf-fallback-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none}.pf-fallback-canvas{display:none}.pf-fallback .pf-canvas{display:none}.pf-fallback .pf-fallback-canvas{display:block}\r\n.pf-hud{position:absolute;inset:0;z-index:3;pointer-events:none;padding:max(10px,env(safe-area-inset-top)) 10px max(10px,env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:8px}\r\n.pf-hud button,.pf-panel{pointer-events:auto}\r\n.pf-topbar{display:flex;align-items:center;justify-content:space-between;min-height:42px;padding:0 1px}.pf-brand{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:900;letter-spacing:.14em;text-shadow:0 2px 8px #000}.pf-brand-mark{display:grid;place-items:center;width:29px;height:29px;color:var(--pf-warm);border:1px solid color-mix(in srgb,var(--pf-accent) 78%,white);border-radius:9px;background:#080d16b8;box-shadow:0 0 18px color-mix(in srgb,var(--pf-accent) 44%,transparent)}.pf-top-actions{display:flex;gap:7px}.pf-icon-button,.pf-menu-button{display:grid;place-items:center;min-width:44px;min-height:44px;border:1px solid #ffffff62;border-radius:13px;color:#fff;background:#080b14d9;font-size:18px;font-weight:800;box-shadow:0 5px 16px #0005;cursor:pointer}.pf-icon-button:focus-visible,.pf-menu-button:focus-visible,.pf-panel button:focus-visible{outline:3px solid var(--pf-warm);outline-offset:2px}\r\n.pf-stats{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:5px;max-width:700px}.pf-stat{min-width:0;padding:7px 8px 8px;border:1px solid #ffffff30;border-radius:12px;background:linear-gradient(145deg,#080c17e8,#0a1020bd);backdrop-filter:blur(8px);box-shadow:0 4px 16px #0004}.pf-stat span{display:block;overflow:hidden;color:#c1c6d3;font-size:9px;font-weight:750;letter-spacing:.09em;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.pf-stat strong{display:block;margin-top:3px;color:#fff;font-size:14px;font-variant-numeric:tabular-nums;text-shadow:0 0 12px color-mix(in srgb,var(--pf-accent) 30%,transparent)}.pf-protection strong{color:var(--pf-warm)}\r\n.pf-live-mission{align-self:flex-start;max-width:min(88%,540px);padding:7px 10px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 56%,#ffffff3b);border-radius:11px;color:#f5f6ff;background:#070b14d9;font-size:10px;font-weight:850;letter-spacing:.065em;text-shadow:0 1px 6px #000;box-shadow:0 3px 14px #0005}.pf-live-mission[hidden]{display:none}.pf-aim{border-color:var(--pf-personal,var(--pf-accent))}.pf-aim:before,.pf-aim:after{background:var(--pf-personal,var(--pf-accent))}\r\n.pf-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\r\n.pf-notice{align-self:center;max-width:min(92%,520px);padding:8px 14px;border:1px solid color-mix(in srgb,var(--pf-alert) 76%,white);border-radius:99px;color:#fff;background:#310d18e8;text-align:center;font-size:12px;font-weight:850;letter-spacing:.05em;text-shadow:0 1px 8px #000;box-shadow:0 0 22px color-mix(in srgb,var(--pf-alert) 28%,transparent)}.pf-notice[hidden]{display:none}.pf-notice.is-critical{background:#581018f2}\r\n.pf-aim{position:absolute;left:50%;top:52%;width:62px;height:62px;transform:translate(-50%,-50%);border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 75%,white);border-radius:50%;opacity:.54;box-shadow:0 0 20px color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 20%,transparent)}.pf-aim:before,.pf-aim:after{position:absolute;content:\"\";background:var(--pf-personal,var(--pf-accent));opacity:.7}.pf-aim:before{left:50%;top:-7px;width:1px;height:74px}.pf-aim:after{top:50%;left:-7px;height:1px;width:74px}.pf-aim i,.pf-aim b{position:absolute;left:50%;top:50%;width:7px;height:7px;border:1px solid #fff;border-radius:50%;transform:translate(-50%,-50%)}.pf-aim b{width:34px;height:34px;border-color:#ffffff70}\r\n.pf-bottomline{display:flex;align-items:flex-end;gap:6px;margin-top:auto;padding-bottom:2px}.pf-vehicle,.pf-powerup{max-width:calc(50% - 50px);padding:7px 9px;border:1px solid #ffffff36;border-radius:11px;background:#080b14d9;color:#fff;font-size:10px;font-weight:850;letter-spacing:.06em;text-shadow:0 1px 5px #000}.pf-menu-button{margin-left:auto;padding:0 13px;font-size:11px;letter-spacing:.1em}.pf-message{align-self:center;max-width:82%;margin-bottom:5px;padding:6px 11px;border-radius:99px;background:#0608129c;color:#fff;text-align:center;font-size:12px;font-weight:750;text-shadow:0 1px 5px #000;pointer-events:none}.pf-message:empty{display:none}\r\n.pf-panel{position:absolute;inset:0;z-index:8;display:grid;place-items:center;padding:calc(max(14px,env(safe-area-inset-top)) + 12px) 12px calc(max(12px,env(safe-area-inset-bottom)) + 12px);background:radial-gradient(ellipse at 50% 40%,#11172c72,#02030bbd 72%);backdrop-filter:blur(5px)}.pf-panel[hidden]{display:none}.pf-panel-scroll{position:relative;width:min(100%,440px);max-height:100%;overflow:auto;padding:24px 20px 18px;border:1px solid color-mix(in srgb,var(--pf-accent) 52%,#ffffff48);border-radius:24px;background:linear-gradient(155deg,#111729f7,#080a14f7 72%);box-shadow:0 20px 80px #000a,0 0 36px color-mix(in srgb,var(--pf-accent) 13%,transparent);text-align:center;overscroll-behavior:contain}.pf-close{position:absolute;right:11px;top:10px;display:grid;place-items:center;width:40px;height:40px;border:1px solid #ffffff3b;border-radius:12px;background:#ffffff0c;color:#fff;font-size:24px;cursor:pointer}.pf-panel-icon{display:grid;place-items:center;width:56px;height:56px;margin:0 auto 12px;border:1px solid var(--pf-accent);border-radius:18px;color:var(--pf-warm);font-size:28px;background:color-mix(in srgb,var(--pf-accent) 13%,#090d18);box-shadow:0 0 28px color-mix(in srgb,var(--pf-accent) 25%,transparent)}.pf-eyebrow{margin:4px 0 7px;color:var(--pf-accent)!important;font-size:10px!important;font-weight:900!important;letter-spacing:.16em!important;text-transform:uppercase}.pf-panel h1,.pf-panel h2{margin:4px 0 10px;color:#fff;font-size:clamp(25px,7vw,34px);line-height:1.1;letter-spacing:-.035em}.pf-panel p{margin:9px 0;color:#e0e3ee;font-size:14px;line-height:1.48}.pf-howto{display:grid;gap:7px;margin:16px 0;text-align:left}.pf-howto span{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid #ffffff17;border-radius:12px;background:#ffffff08;color:#e7e9f2;font-size:12px;line-height:1.35}.pf-howto b{display:grid;place-items:center;flex:none;width:27px;height:27px;border:1px solid color-mix(in srgb,var(--pf-accent) 60%,white);border-radius:9px;color:var(--pf-warm);font-size:15px}.pf-smallprint{color:#aeb5c7!important;font-size:11px!important}.pf-primary,.pf-secondary,.pf-link{display:block;width:100%;min-height:48px;margin:8px 0 0;padding:10px 14px;border:1px solid transparent;border-radius:14px;color:#07110f;background:linear-gradient(120deg,var(--pf-accent),color-mix(in srgb,var(--pf-accent) 54%,white));font-size:14px;font-weight:900;letter-spacing:.03em;cursor:pointer}.pf-secondary{border-color:#ffffff45;color:#fff;background:#ffffff0d}.pf-link{min-height:42px;border-color:transparent;color:#ccd1df;background:transparent;font-size:12px}.pf-result-grid{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin:16px 0}.pf-result-grid div{padding:12px 6px;border:1px solid #ffffff23;border-radius:13px;background:#ffffff08}.pf-result-grid strong,.pf-result-grid small{display:block}.pf-result-grid strong{font-size:19px;color:var(--pf-warm)}.pf-result-grid small{margin-top:3px;color:#abb1c0;font-size:10px;text-transform:uppercase;letter-spacing:.08em}.pf-missions{display:grid;gap:6px;margin:12px 0}.pf-missions div{display:flex;justify-content:space-between;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffff0b;text-align:left;font-size:11px}.pf-missions span{color:var(--pf-accent);white-space:nowrap}.pf-list{display:grid;gap:8px;max-height:40vh;overflow:auto;margin:12px 0;text-align:left}.pf-list-card{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px;border:1px solid #ffffff20;border-radius:13px;background:#ffffff08}.pf-list-card>div{min-width:0}.pf-list-card strong,.pf-list-card small{display:block}.pf-list-card strong{color:#fff;font-size:12px}.pf-list-card small{margin-top:3px;color:#bbc1d0;font-size:10px;line-height:1.35}.pf-list-card button{flex:none;min-width:66px;min-height:40px;padding:6px 9px;border:1px solid color-mix(in srgb,var(--pf-accent) 60%,white);border-radius:10px;color:#fff;background:#ffffff0c;font-size:11px;font-weight:850;cursor:pointer}.pf-empty{padding:12px;border:1px dashed #ffffff35;border-radius:12px;color:#b7bdce;font-size:12px!important}\r\n@media(max-width:420px){.pf-hud{padding-left:7px;padding-right:7px;gap:6px}.pf-stats{gap:4px}.pf-stat{padding:6px 4px}.pf-stat span{font-size:7px;letter-spacing:.025em}.pf-stat strong{font-size:11px}.pf-panel-scroll{padding:22px 15px 14px;border-radius:20px}.pf-panel p{font-size:13px}.pf-howto span{font-size:11px}.pf-aim{top:50%;width:54px;height:54px}}\r\n@media(prefers-reduced-motion:reduce){.pf-plasma,.pf-panel,.pf-icon-button{scroll-behavior:auto;transition:none!important;animation:none!important}.pf-notice{box-shadow:none}}\r\n.pf-effect{max-width:44%;overflow:hidden;padding:6px 8px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 48%,#ffffff30);border-radius:10px;background:#080b14dc;color:#e9fbff;font-size:9px;font-weight:900;letter-spacing:.06em;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 1px 5px #000;pointer-events:none}\r\n.pf-vehicle-hint{max-width:28vw;overflow:hidden;padding:6px 8px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 54%,#ffffff32);border-radius:10px;color:#e5fcff;background:#071018e8;font-size:9px;font-weight:850;text-overflow:ellipsis;white-space:nowrap}.pf-fold-preview{position:absolute;z-index:2;display:flex;align-items:center;gap:6px;transform:translate(-50%,-50%);padding:4px 7px;border:1px solid #aefaffb8;border-radius:9px;color:#f2ffff;background:#061116df;font-size:9px;font-weight:900;letter-spacing:.08em;text-shadow:0 1px 5px #000;white-space:nowrap;box-shadow:0 0 18px #61f4ef88;pointer-events:none}.pf-fold-preview[hidden]{display:none}.pf-fold-preview i{display:block;width:13px;height:13px;border:2px solid #aefaff;border-radius:3px;transform:rotate(45deg);box-shadow:0 0 10px #61f4ef}.pf-fold-preview span{display:block}@media(max-width:420px){.pf-vehicle-hint{max-width:32vw;font-size:8px}.pf-fold-preview{font-size:8px;padding:4px 6px}}\r\n.pf-fold-preview.is-blocked{border-color:#ff8093;color:#fff0f2;background:#2b0c16ed;box-shadow:0 0 16px #ff4d6d76}.pf-fold-preview.is-blocked i{border-color:#ff8093;box-shadow:0 0 9px #ff4d6d}\r\n.pf-biome{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 8px;padding:7px 10px;border-left:3px solid var(--pf-accent);border-radius:3px 10px 10px 3px;background:#050b19e6;box-shadow:0 4px 15px #0005;max-width:620px}.pf-biome[hidden]{display:none}.pf-biome div{min-width:0;display:grid;gap:2px}.pf-biome span{color:var(--pf-warm);font-size:10px;letter-spacing:.12em;font-weight:950;text-transform:uppercase}.pf-biome strong{font-size:11px;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pf-biome b{align-self:center;color:white;font-size:18px;font-variant-numeric:tabular-nums}.pf-biome small{grid-column:1/-1;font-size:8px;color:#b5ced6;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.pf-biome:not([hidden])~.pf-live-mission{max-width:90%;font-size:8px;padding:4px 8px;opacity:.85}.pf-turn{align-self:center;padding:9px 15px;color:#f3fdff;border:1px solid #b6f8ff;border-radius:9px;background:#061926ed;font-size:11px;font-weight:900;letter-spacing:.09em;text-align:center}.pf-turn[hidden]{display:none}.pf-pickup-toast{align-self:center;padding:8px 12px;color:#fff4bb;background:#191002ed;border:1px solid #f5c861;border-radius:8px;font-size:11px;font-weight:900}.pf-pickup-toast[hidden]{display:none}.pf-portal-seal{display:grid;place-content:center;aspect-ratio:1;width:76px;margin:0 auto 18px;color:#fff;font-size:20px;line-height:.9;font-weight:1000;border:3px solid #fff;transform:rotate(-7deg);background:#080808;box-shadow:7px 7px 0 var(--pf-accent)}.pf-next-world{font-weight:900;color:var(--pf-warm)!important}.pf-primary:disabled{opacity:.45;cursor:wait}.pf-cockpit{position:absolute;inset:0;z-index:-1;pointer-events:none}.pf-cockpit[hidden]{display:none}.pf-cockpit svg{width:100%;height:100%;overflow:visible}.pf-cockpit path{fill:none;stroke-linejoin:round;vector-effect:non-scaling-stroke}.pf-cockpit-shadow path{stroke:#06090de6;stroke-width:4.5}.pf-cockpit-metal path{stroke:#8397a9d9;stroke-width:1.35}.pf-cockpit-tag{position:absolute;bottom:16%;left:50%;transform:translateX(-50%);background:#0b1018b3;color:#c7d7e2;padding:4px 6px;font-size:7px;letter-spacing:.14em;white-space:nowrap}.plank-flight[data-biome=\"castle\"] .pf-biome{border-radius:0;border-width:3px;image-rendering:pixelated}.plank-flight[data-biome=\"rainbow\"] .pf-biome{border-image:linear-gradient(90deg,#ff71ad,#ffe479,#80f3fa,#b482ff) 1}.plank-flight[data-biome=\"asteroid\"] .pf-aim{border-color:#aaf4bd;opacity:.35}.plank-flight[data-phase=\"portal\"] .pf-aim{display:none}\r\n@media(max-height:500px){.pf-biome{position:absolute;left:8px;top:110px;width:230px}.pf-biome small{display:none}.pf-biome:not([hidden])~.pf-live-mission{display:none}.pf-pace{max-width:360px;align-self:flex-end;min-height:48px}.pf-turn,.pf-pickup-toast{position:absolute;left:50%;top:48%;transform:translateX(-50%)}.pf-biome strong{font-size:10px}}\r\n\r\n/* The flight view owns a compact readout while keeping the first-person course open. */\r\n.plank-flight-host .camera-workout-housing{display:none!important}\r\n.plank-flight-host.plank-flight-calibrating>video{left:auto!important;right:10px!important;top:max(58px,calc(env(safe-area-inset-top) + 58px))!important;bottom:auto!important;width:56px!important;height:72px!important;max-width:56px!important;object-fit:cover!important;pointer-events:none!important;z-index:5!important}\r\n#cameraWorkout.plank-flight-host>video{left:auto!important;right:10px!important;top:max(58px,calc(env(safe-area-inset-top) + 58px))!important;bottom:auto!important;width:56px!important;height:72px!important;max-width:56px!important;object-fit:cover!important;pointer-events:none!important;z-index:5!important}\r\n.pf-hud{padding:max(5px,env(safe-area-inset-top)) 7px max(8px,env(safe-area-inset-bottom));gap:4px}\r\n.pf-topbar{min-height:32px}.pf-brand{gap:6px;font-size:9px;letter-spacing:.11em}.pf-brand-mark{width:24px;height:24px;border-radius:7px}.pf-top-actions{gap:5px}.pf-icon-button{min-width:34px;min-height:32px;border-radius:9px;font-size:15px}\r\n.pf-stats{grid-template-columns:repeat(2,minmax(0,104px));gap:4px;max-width:220px}.pf-stat{padding:4px 6px 5px;border-radius:8px}.pf-stat span{font-size:7px;letter-spacing:.06em}.pf-stat strong{margin-top:1px;font-size:11px}\r\n.pf-top-actions [data-act=\"recalibrate\"]{display:none!important}\r\n.pf-biome{width:max-content;max-width:min(62vw,230px);min-height:27px;padding:4px 7px;gap:4px 7px;border-radius:2px 7px 7px 2px;background:#050b19c7;box-shadow:0 3px 12px #0004;backdrop-filter:blur(4px)}.pf-biome div{display:block}.pf-biome span{display:block;overflow:hidden;max-width:42vw;font-size:8px;letter-spacing:.08em;text-overflow:ellipsis;white-space:nowrap}.pf-biome [data-biome-name] .game-cue{min-width:0;max-width:100%;gap:4px}.pf-biome [data-biome-name] .game-cue b{overflow:hidden;max-width:min(38vw,170px);font-size:9px;letter-spacing:.035em;text-overflow:ellipsis;white-space:nowrap}.pf-biome [data-biome-name] .game-symbol{width:15px;height:15px}.pf-biome b{font-size:11px;color:#c4d2db}.pf-biome small{display:none}\r\n.pf-live-mission{display:none!important}.pf-pace.pf-sr{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\r\n.pf-message.is-position-hint{position:absolute;top:99px;left:8px;align-self:auto;max-width:max-content;margin:0;padding:4px 8px;border:1px solid #ffffff30;border-radius:8px;background:#061018bd;color:#eaf6fa;font-size:9px;font-weight:750;letter-spacing:.035em;text-align:left;opacity:.9}\r\n@media(max-width:420px){.pf-hud{gap:4px}.pf-stats{grid-template-columns:repeat(2,minmax(0,98px))}.pf-biome{max-width:60vw}}\r\n.pf-launch-cue{position:absolute;inset:0;z-index:5;display:grid;place-items:center;width:100%;color:#fff6d7;font-size:clamp(56px,24vw,148px);font-weight:1000;letter-spacing:.045em;line-height:1;text-align:center;-webkit-text-stroke:2px #18263c;text-shadow:0 4px 0 #18263c,0 9px 22px #000c,0 0 28px var(--pf-accent);pointer-events:none;user-select:none}.pf-launch-cue[hidden]{display:none}\r\n@media(prefers-reduced-motion:reduce){.pf-launch-cue{transition:none!important}}\r\n.pf-propulsion{position:absolute;z-index:2;left:5px;right:5px;bottom:max(7px,env(safe-area-inset-bottom));display:flex;justify-content:space-between;align-items:flex-end;pointer-events:none}\r\n.pf-thruster{width:82px;height:82px;--pf-thruster-color:#70cfff;--pf-thruster-glow:.12;filter:drop-shadow(0 3px 5px #0009);transition:transform .12s ease}\r\n.pf-thruster svg{display:block;width:100%;height:100%;overflow:visible}.pf-thruster-shell{fill:var(--pf-thruster-color)}.pf-thruster-hot-ring{stroke:var(--pf-thruster-color);filter:drop-shadow(0 0 4px var(--pf-thruster-color))}\r\n.pf-thruster-halo{fill:var(--pf-thruster-color);opacity:var(--pf-thruster-glow);filter:blur(13px)}.pf-thruster-flame{opacity:0;transform-origin:50% 90%;transition:opacity .15s ease}\r\n.pf-thruster[data-firing=\"true\"] .pf-thruster-flame{opacity:.8;animation:pf-exhaust .13s ease-in-out infinite alternate}.pf-thruster.is-steering{transform:translateY(-5px) rotate(-4deg)}.pf-thruster[data-thruster=\"right\"].is-steering{transform:translateY(-5px) rotate(4deg)}\r\n.pf-motorcycle{position:absolute;z-index:1;left:50%;bottom:-6px;width:min(74vw,360px);height:auto;pointer-events:none;transform-origin:50% 100%;filter:drop-shadow(0 3px 6px #0008);transition:transform .12s ease}.pf-motorcycle[hidden]{display:none}.pf-motorcycle svg{display:block;width:100%;overflow:visible}.pf-bike-frame{transform-origin:180px 150px;transform:translateY(calc(var(--pf-wheelie,0) * -8px))}.pf-bike-wheel{transform-origin:180px 150px;transform:scaleY(calc(1 + var(--pf-wheelie,0) * .18))}\r\n.pf-stunt{position:absolute;left:50%;top:58%;z-index:3;transform:translateX(-50%);padding:7px 11px;border:2px solid #ffd669;border-radius:12px;color:#fff1aa;background:#342006db;font-size:16px;font-weight:950;letter-spacing:.06em;white-space:nowrap;text-shadow:0 2px #593314;box-shadow:0 0 22px #ffbd3144;pointer-events:none}.pf-stunt[hidden]{display:none}.pf-stunt span{display:inline-block;transform:rotate(var(--pf-stunt-spin,0deg));color:#ffcf4d}\r\n@keyframes pf-exhaust{from{transform:scaleX(.92) scaleY(.95)}to{transform:scaleX(1.08) scaleY(1.08)}}\r\n@media(max-width:420px){.pf-thruster{width:76px;height:76px}}\r\n@media(prefers-reduced-motion:reduce){.pf-thruster,.pf-motorcycle{transition:none}.pf-thruster-flame{animation:none!important}.pf-stunt span{transform:none}}\r\n.pf-pace{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:10px;width:min(100%,620px);min-height:68px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 70%,#ffffff40);border-radius:15px;color:#fff;background:linear-gradient(110deg,#06151aef,#090d1bf2);box-shadow:0 5px 20px #0008,0 0 22px color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 20%,transparent);text-shadow:0 1px 5px #000}.pf-pace[hidden]{display:none}.pf-pace-current,.pf-pace-next{display:grid;min-width:0;gap:2px}.pf-pace-eyebrow{color:var(--pf-personal,var(--pf-accent));font-size:9px;font-weight:950;letter-spacing:.16em}.pf-pace-current strong,.pf-pace-next strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-pace-current strong{font-size:clamp(15px,4vw,21px);line-height:1.12}.pf-pace-next strong{font-size:clamp(12px,3vw,15px);line-height:1.15;color:#e1e8f4}.pf-pace-current small{overflow:hidden;color:#c0d4d4;font-size:9px;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.pf-pace-time{display:flex;align-items:baseline;gap:3px;padding:5px 8px;border-left:1px solid #ffffff26;border-right:1px solid #ffffff26;font-variant-numeric:tabular-nums}.pf-pace-time strong{min-width:2ch;text-align:right;color:#f6ffff;font-size:clamp(22px,6vw,30px);line-height:1}.pf-pace-time span{color:#c0d4d4;font-size:9px;font-weight:900;letter-spacing:.1em}.pf-pace.is-rest{border-color:#ffc66b;background:linear-gradient(110deg,#211709f2,#100c15f2);box-shadow:0 5px 20px #0008,0 0 20px #ffc66b33}.pf-pace.is-rest .pf-pace-eyebrow{color:#ffd27e}.pf-pace.is-rest .pf-pace-current strong{color:#ffe1a5}.pf-pace.is-transitioning{border-style:dashed}\r\n@media(max-width:420px){.pf-pace{gap:7px;min-height:62px;padding:7px 9px;border-radius:13px}.pf-pace-eyebrow{font-size:8px}.pf-pace-current strong{font-size:clamp(13px,4.3vw,17px)}.pf-pace-next strong{font-size:12px}.pf-pace-current small{font-size:8px}.pf-pace-time{gap:2px;padding:4px 6px}.pf-pace-time strong{font-size:24px}.pf-pace-time span{font-size:8px}}\r\n\r\n/* Physical status cues keep the existing control and announcement paths. */\r\n.plank-flight .pf-stat>.game-symbol{width:22px;height:22px;float:left;margin-right:8px}.plank-flight .pf-biome .game-cue{display:flex;flex-direction:row;justify-content:flex-start}.plank-flight .pf-biome .game-symbol{width:26px;height:26px}.plank-flight .pf-biome .game-cue b{font-size:12px}.plank-flight .pf-message .game-symbol{width:38px;height:38px}.plank-flight .pf-launch-cue .game-symbol{width:64px;height:64px}.plank-flight .pf-launch-cue .game-cue b{font-size:56px}.plank-flight .pf-panel .game-cue-row{margin:24px 0;color:var(--pf-warm);gap:22px}.plank-flight .pf-panel .game-cue-row .game-symbol{width:56px;height:56px}\r\n\r\n.plank-flight .pf-thruster{position:relative}.plank-flight .pf-thruster::after{content:\"\";position:absolute;right:3px;bottom:22px;width:5px;height:calc(3px + var(--pf-heat-level,0)*29px);border:1px solid #efffff;border-radius:2px;background:var(--pf-thruster-color);box-shadow:0 0 0 2px #08121a}\r\n\r\n.pf-flight-fx{display:flex;flex-wrap:wrap;align-items:center;gap:4px;width:100%;min-width:0;max-width:min(100%,440px);box-sizing:border-box;pointer-events:none}.pf-flight-fx [hidden]{display:none!important}.pf-flight-fx>span,.pf-fx-chip{display:inline-flex;align-items:center;gap:4px;min-height:24px;padding:3px 6px;border:1px solid #ffffff45;border-radius:8px;color:#f5fbff;background:#06101cdb;font-size:8px;font-weight:900;letter-spacing:.045em;text-shadow:0 1px 4px #000}.pf-flight-fx>span[data-flight-fx-active]{display:flex;flex-wrap:wrap;min-width:0;max-width:100%;gap:4px}.pf-flight-fx>span[data-flight-fx-cue]{border-color:#ffe38a;background:#211a08ed;color:#fff2b3}.pf-flight-fx svg{width:15px;height:15px;flex:none}.pf-flight-fx b,.pf-flight-fx small{font-size:inherit;line-height:1;white-space:nowrap}.pf-flight-fx small{color:#bdeefa;font-variant-numeric:tabular-nums}.pf-flight-fx .is-protection{border-color:#a8ffcf}.pf-flight-fx .is-vehicle{border-color:#b4e9ff}.pf-flight-fx .is-multiplier{border-color:#ffe58b;color:#ffe58b}.pf-flight-fx .is-vehicle-time{border-color:#b4e9ff;color:#e4f8ff}\r\n.pf-flight-fx>span[data-flight-fx-cue]:not([data-fx-kind=\"coin\"]){min-height:40px;padding:5px 9px;gap:8px;font-size:10px;border-radius:12px;box-shadow:0 5px 16px #0007}.pf-flight-fx>span[data-flight-fx-cue]:not([data-fx-kind=\"coin\"]) svg{width:28px;height:28px}\r\n.plank-flight.is-shielded .pf-aim{outline:2px solid #9affcf;outline-offset:5px;box-shadow:0 0 20px #8effc875}.plank-flight.has-impact-buffer .pf-aim{border-style:double;border-width:3px}.plank-flight.is-magnet-active .pf-aim{border-color:#71efff;box-shadow:0 0 18px #50e8ff75}.plank-flight.is-quiet-vector .pf-thruster{transition-duration:.24s}.plank-flight.is-early-signal .pf-notice{border-style:double}.plank-flight.is-rescue-echo.has-impact-buffer .pf-aim{outline-color:#d5b5ff}\r\n.plank-flight .pf-thruster-shell{fill:color-mix(in srgb,var(--pf-thruster-color) 76%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-thruster-halo{fill:color-mix(in srgb,var(--pf-thruster-color) 68%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-bike-frame path:first-child{fill:color-mix(in srgb,#e45a27 74%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-thruster[data-boosting=\"true\"] .pf-thruster-flame{opacity:calc(.2 + var(--pf-boost-factor)*.58);transform:scaleY(calc(.64 + var(--pf-boost-factor)*.72))}.plank-flight[data-fx-cue=\"vehicle\"] .pf-motorcycle{filter:drop-shadow(0 0 11px #9cf2ff) drop-shadow(0 3px 6px #0008)}.plank-flight[data-fx-cue=\"shield\"] .pf-cockpit svg,.plank-flight[data-fx-cue=\"impact\"] .pf-cockpit svg{filter:drop-shadow(0 0 9px #adffdb)}\r\n@media(max-width:420px){.pf-flight-fx{max-width:calc(100vw - 14px);gap:3px}.pf-flight-fx>span,.pf-fx-chip{min-height:22px;padding:2px 5px;font-size:7px}.pf-flight-fx svg{width:13px;height:13px}}\r\n@media(prefers-reduced-motion:reduce){.pf-flight-fx>span[data-flight-fx-cue]{transition:none!important}.plank-flight[data-fx-cue=\"vehicle\"] .pf-motorcycle{filter:drop-shadow(0 3px 6px #0008)}}\r\n","arcade/plank-flight/flight-sound.mjs":"// Use the app's gesture-unlocked audio graph, ducking and saved master mute.\r\n// Never create a second AudioContext for the game.\r\nconst CUES={READY:'flight-ready',SET:'flight-set',GO:'flight-go',coin:'flight-coin',crt:'flight-warning',hit:'flight-impact',pickup:'flight-pickup'};\r\nexport function createFlightAudio({audio=null,play=()=>{}}={}){\r\n let engine=null,disposed=false,cueCount=0;\r\n function stop(){const active=engine;engine=null;active?.voice.stop(.05);}\r\n function cue(kind){if(disposed)return;cueCount++;play(CUES[kind]??kind);}\r\n function update({flying=false,speed=15,height=.5,time=0,motorcycle=false}={}){\r\n  if(disposed)return;\r\n  const c=audio?.context;\r\n  if(!flying||!c||c.state!=='running'||audio.muted||audio.volume===0||audio.document?.hidden){stop();return;}\r\n  if(engine&&engine.context!==c)stop();\r\n  if(!engine){\r\n   const state={context:c};\r\n   const voice=audio.voice('flight-engine',Infinity,({c,t,output,nodes})=>{\r\n    const hum=c.createOscillator(),humGain=c.createGain(),wind=audio.sourceNoise(),windGain=c.createGain(),filter=c.createBiquadFilter();\r\n    hum.type='triangle';hum.frequency.setValueAtTime(92,t);humGain.gain.setValueAtTime(.025,t);\r\n    wind.loop=true;filter.type='lowpass';filter.frequency.setValueAtTime(1800,t);windGain.gain.setValueAtTime(.045,t);\r\n    hum.connect(humGain).connect(output);wind.connect(filter).connect(windGain).connect(output);hum.start(t);wind.start(t);\r\n    nodes.push(hum,humGain,wind,filter,windGain);Object.assign(state,{hum,filter,windGain});\r\n   });\r\n   if(!voice)return;\r\n   const release=voice.stop;voice.stop=(fade=0)=>{if(engine?.voice===voice)engine=null;release(fade);};\r\n   engine={...state,voice};\r\n  }\r\n  const y=Math.max(0,Math.min(1,Number(height)||0)),pace=Math.max(.5,Math.min(2,(Number(speed)||15)/15)),t=c.currentTime;\r\n  const tone=motorcycle?'sawtooth':'triangle';if(engine.hum.type!==tone)engine.hum.type=tone;\r\n  engine.hum.frequency.setTargetAtTime((motorcycle?58:76)+32*y+12*pace,t,.12);\r\n  engine.filter.frequency.setTargetAtTime(1100+700*y+500*pace,t,.12);\r\n  engine.windGain.gain.setTargetAtTime(.034+.012*pace+.003*Math.sin(time*2),t,.1);\r\n }\r\n return {cue,update,stop,dispose(){if(disposed)return;stop();disposed=true;},get stats(){return {engineActive:!!engine,muted:audio?.muted===true||audio?.volume===0,contextState:audio?.context?.state??'locked',cueCount};}};\r\n}\r\n","arcade/plank-flight/flight.css":".plank-flight-host{position:relative!important;overflow:hidden!important;isolation:isolate;background:var(--pf-bg,#06020a)!important;min-height:min(100dvh,900px)}\r\n.plank-flight-host>video{inset:auto!important;right:8px!important;bottom:max(76px,calc(env(safe-area-inset-bottom) + 68px))!important;left:auto!important;top:auto!important;width:76px!important;height:102px!important;max-width:22vw!important;aspect-ratio:3/4;object-fit:cover;z-index:6!important;border:2px solid #ffffffaa!important;border-radius:13px!important;box-shadow:0 5px 18px #000a!important;background:#05030a!important}\r\n.plank-flight-host #coachOverlay{visibility:hidden}\r\n.plank-flight .pf-pace-current strong,.plank-flight .pf-pace-next strong{white-space:normal;text-overflow:clip;line-height:1.1}\r\n\r\n.plank-flight.is-rainbow-mode{box-shadow:inset 0 0 26px #96f5ff80,inset 8px 0 14px #ff8be666,inset -8px 0 14px #ffe27666}.plank-flight.is-rainbow-mode .pf-aim{border-color:#fff3b0;opacity:.7}\r\n.pf-cockpit ellipse{fill:none;vector-effect:non-scaling-stroke}.pf-cockpit-shadow ellipse{stroke:#06090de6;stroke-width:4.5}.pf-cockpit-metal ellipse{stroke:#8397a9d9;stroke-width:1.35}\r\n.plank-flight-host>.workout-performance-controls{display:none!important}\r\n.plank-flight-host .camera-workout-counter,.plank-flight-host .camera-workout-status,.plank-flight-host .camera-workout-adjust{display:none!important}\r\n.plank-flight{--pf-accent:#20f0d0;--pf-warm:#e8fffc;--pf-alert:#ff2a5a;--pf-bg:#020a0a;position:absolute;inset:0;min-height:100%;overflow:hidden;color:#f5f6ff;background:radial-gradient(ellipse at 50% 36%,color-mix(in srgb,var(--pf-accent) 15%,transparent),transparent 52%),linear-gradient(180deg,var(--pf-bg),#03040b 76%);font-family:Inter,ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",sans-serif;letter-spacing:.01em;touch-action:manipulation}\r\n.pf-canvas,.pf-fallback-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none}.pf-fallback-canvas{display:none}.pf-fallback .pf-canvas{display:none}.pf-fallback .pf-fallback-canvas{display:block}\r\n.pf-hud{position:absolute;inset:0;z-index:3;pointer-events:none;padding:max(10px,env(safe-area-inset-top)) 10px max(10px,env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:8px}\r\n.pf-hud button,.pf-panel{pointer-events:auto}\r\n.pf-topbar{display:flex;align-items:center;justify-content:space-between;min-height:42px;padding:0 1px}.pf-brand{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:900;letter-spacing:.14em;text-shadow:0 2px 8px #000}.pf-brand-mark{display:grid;place-items:center;width:29px;height:29px;color:var(--pf-warm);border:1px solid color-mix(in srgb,var(--pf-accent) 78%,white);border-radius:9px;background:#080d16b8;box-shadow:0 0 18px color-mix(in srgb,var(--pf-accent) 44%,transparent)}.pf-top-actions{display:flex;gap:7px}.pf-icon-button,.pf-menu-button{display:grid;place-items:center;min-width:44px;min-height:44px;border:1px solid #ffffff62;border-radius:13px;color:#fff;background:#080b14d9;font-size:18px;font-weight:800;box-shadow:0 5px 16px #0005;cursor:pointer}.pf-icon-button:focus-visible,.pf-menu-button:focus-visible,.pf-panel button:focus-visible{outline:3px solid var(--pf-warm);outline-offset:2px}\r\n.pf-stats{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:5px;max-width:700px}.pf-stat{min-width:0;padding:7px 8px 8px;border:1px solid #ffffff30;border-radius:12px;background:linear-gradient(145deg,#080c17e8,#0a1020bd);backdrop-filter:blur(8px);box-shadow:0 4px 16px #0004}.pf-stat span{display:block;overflow:hidden;color:#c1c6d3;font-size:9px;font-weight:750;letter-spacing:.09em;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.pf-stat strong{display:block;margin-top:3px;color:#fff;font-size:14px;font-variant-numeric:tabular-nums;text-shadow:0 0 12px color-mix(in srgb,var(--pf-accent) 30%,transparent)}.pf-protection strong{color:var(--pf-warm)}\r\n.pf-live-mission{align-self:flex-start;max-width:min(88%,540px);padding:7px 10px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 56%,#ffffff3b);border-radius:11px;color:#f5f6ff;background:#070b14d9;font-size:10px;font-weight:850;letter-spacing:.065em;text-shadow:0 1px 6px #000;box-shadow:0 3px 14px #0005}.pf-live-mission[hidden]{display:none}.pf-aim{border-color:var(--pf-personal,var(--pf-accent))}.pf-aim:before,.pf-aim:after{background:var(--pf-personal,var(--pf-accent))}\r\n.pf-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\r\n.pf-notice{align-self:center;max-width:min(92%,520px);padding:8px 14px;border:1px solid color-mix(in srgb,var(--pf-alert) 76%,white);border-radius:99px;color:#fff;background:#310d18e8;text-align:center;font-size:12px;font-weight:850;letter-spacing:.05em;text-shadow:0 1px 8px #000;box-shadow:0 0 22px color-mix(in srgb,var(--pf-alert) 28%,transparent)}.pf-notice[hidden]{display:none}.pf-notice.is-critical{background:#581018f2}\r\n.pf-aim{position:absolute;left:50%;top:52%;width:62px;height:62px;transform:translate(-50%,-50%);border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 75%,white);border-radius:50%;opacity:.54;box-shadow:0 0 20px color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 20%,transparent)}.pf-aim:before,.pf-aim:after{position:absolute;content:\"\";background:var(--pf-personal,var(--pf-accent));opacity:.7}.pf-aim:before{left:50%;top:-7px;width:1px;height:74px}.pf-aim:after{top:50%;left:-7px;height:1px;width:74px}.pf-aim i,.pf-aim b{position:absolute;left:50%;top:50%;width:7px;height:7px;border:1px solid #fff;border-radius:50%;transform:translate(-50%,-50%)}.pf-aim b{width:34px;height:34px;border-color:#ffffff70}\r\n.pf-bottomline{display:flex;align-items:flex-end;gap:6px;margin-top:auto;padding-bottom:2px}.pf-vehicle,.pf-powerup{max-width:calc(50% - 50px);padding:7px 9px;border:1px solid #ffffff36;border-radius:11px;background:#080b14d9;color:#fff;font-size:10px;font-weight:850;letter-spacing:.06em;text-shadow:0 1px 5px #000}.pf-menu-button{margin-left:auto;padding:0 13px;font-size:11px;letter-spacing:.1em}.pf-message{align-self:center;max-width:82%;margin-bottom:5px;padding:6px 11px;border-radius:99px;background:#0608129c;color:#fff;text-align:center;font-size:12px;font-weight:750;text-shadow:0 1px 5px #000;pointer-events:none}.pf-message:empty{display:none}\r\n.pf-panel{position:absolute;inset:0;z-index:8;display:grid;place-items:center;padding:calc(max(14px,env(safe-area-inset-top)) + 12px) 12px calc(max(12px,env(safe-area-inset-bottom)) + 12px);background:radial-gradient(ellipse at 50% 40%,#11172c72,#02030bbd 72%);backdrop-filter:blur(5px)}.pf-panel[hidden]{display:none}.pf-panel-scroll{position:relative;width:min(100%,440px);max-height:100%;overflow:auto;padding:24px 20px 18px;border:1px solid color-mix(in srgb,var(--pf-accent) 52%,#ffffff48);border-radius:24px;background:linear-gradient(155deg,#111729f7,#080a14f7 72%);box-shadow:0 20px 80px #000a,0 0 36px color-mix(in srgb,var(--pf-accent) 13%,transparent);text-align:center;overscroll-behavior:contain}.pf-close{position:absolute;right:11px;top:10px;display:grid;place-items:center;width:40px;height:40px;border:1px solid #ffffff3b;border-radius:12px;background:#ffffff0c;color:#fff;font-size:24px;cursor:pointer}.pf-panel-icon{display:grid;place-items:center;width:56px;height:56px;margin:0 auto 12px;border:1px solid var(--pf-accent);border-radius:18px;color:var(--pf-warm);font-size:28px;background:color-mix(in srgb,var(--pf-accent) 13%,#090d18);box-shadow:0 0 28px color-mix(in srgb,var(--pf-accent) 25%,transparent)}.pf-eyebrow{margin:4px 0 7px;color:var(--pf-accent)!important;font-size:10px!important;font-weight:900!important;letter-spacing:.16em!important;text-transform:uppercase}.pf-panel h1,.pf-panel h2{margin:4px 0 10px;color:#fff;font-size:clamp(25px,7vw,34px);line-height:1.1;letter-spacing:-.035em}.pf-panel p{margin:9px 0;color:#e0e3ee;font-size:14px;line-height:1.48}.pf-howto{display:grid;gap:7px;margin:16px 0;text-align:left}.pf-howto span{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid #ffffff17;border-radius:12px;background:#ffffff08;color:#e7e9f2;font-size:12px;line-height:1.35}.pf-howto b{display:grid;place-items:center;flex:none;width:27px;height:27px;border:1px solid color-mix(in srgb,var(--pf-accent) 60%,white);border-radius:9px;color:var(--pf-warm);font-size:15px}.pf-smallprint{color:#aeb5c7!important;font-size:11px!important}.pf-primary,.pf-secondary,.pf-link{display:block;width:100%;min-height:48px;margin:8px 0 0;padding:10px 14px;border:1px solid transparent;border-radius:14px;color:#07110f;background:linear-gradient(120deg,var(--pf-accent),color-mix(in srgb,var(--pf-accent) 54%,white));font-size:14px;font-weight:900;letter-spacing:.03em;cursor:pointer}.pf-secondary{border-color:#ffffff45;color:#fff;background:#ffffff0d}.pf-link{min-height:42px;border-color:transparent;color:#ccd1df;background:transparent;font-size:12px}.pf-result-grid{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin:16px 0}.pf-result-grid div{padding:12px 6px;border:1px solid #ffffff23;border-radius:13px;background:#ffffff08}.pf-result-grid strong,.pf-result-grid small{display:block}.pf-result-grid strong{font-size:19px;color:var(--pf-warm)}.pf-result-grid small{margin-top:3px;color:#abb1c0;font-size:10px;text-transform:uppercase;letter-spacing:.08em}.pf-missions{display:grid;gap:6px;margin:12px 0}.pf-missions div{display:flex;justify-content:space-between;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffff0b;text-align:left;font-size:11px}.pf-missions span{color:var(--pf-accent);white-space:nowrap}.pf-list{display:grid;gap:8px;max-height:40vh;overflow:auto;margin:12px 0;text-align:left}.pf-list-card{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px;border:1px solid #ffffff20;border-radius:13px;background:#ffffff08}.pf-list-card>div{min-width:0}.pf-list-card strong,.pf-list-card small{display:block}.pf-list-card strong{color:#fff;font-size:12px}.pf-list-card small{margin-top:3px;color:#bbc1d0;font-size:10px;line-height:1.35}.pf-list-card button{flex:none;min-width:66px;min-height:40px;padding:6px 9px;border:1px solid color-mix(in srgb,var(--pf-accent) 60%,white);border-radius:10px;color:#fff;background:#ffffff0c;font-size:11px;font-weight:850;cursor:pointer}.pf-empty{padding:12px;border:1px dashed #ffffff35;border-radius:12px;color:#b7bdce;font-size:12px!important}\r\n@media(max-width:420px){.pf-hud{padding-left:7px;padding-right:7px;gap:6px}.pf-stats{gap:4px}.pf-stat{padding:6px 4px}.pf-stat span{font-size:7px;letter-spacing:.025em}.pf-stat strong{font-size:11px}.pf-panel-scroll{padding:22px 15px 14px;border-radius:20px}.pf-panel p{font-size:13px}.pf-howto span{font-size:11px}.pf-aim{top:50%;width:54px;height:54px}}\r\n@media(prefers-reduced-motion:reduce){.pf-plasma,.pf-panel,.pf-icon-button{scroll-behavior:auto;transition:none!important;animation:none!important}.pf-notice{box-shadow:none}}\r\n.pf-effect{max-width:44%;overflow:hidden;padding:6px 8px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 48%,#ffffff30);border-radius:10px;background:#080b14dc;color:#e9fbff;font-size:9px;font-weight:900;letter-spacing:.06em;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 1px 5px #000;pointer-events:none}\r\n.pf-vehicle-hint{max-width:28vw;overflow:hidden;padding:6px 8px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 54%,#ffffff32);border-radius:10px;color:#e5fcff;background:#071018e8;font-size:9px;font-weight:850;text-overflow:ellipsis;white-space:nowrap}.pf-fold-preview{position:absolute;z-index:2;display:flex;align-items:center;gap:6px;transform:translate(-50%,-50%);padding:4px 7px;border:1px solid #aefaffb8;border-radius:9px;color:#f2ffff;background:#061116df;font-size:9px;font-weight:900;letter-spacing:.08em;text-shadow:0 1px 5px #000;white-space:nowrap;box-shadow:0 0 18px #61f4ef88;pointer-events:none}.pf-fold-preview[hidden]{display:none}.pf-fold-preview i{display:block;width:13px;height:13px;border:2px solid #aefaff;border-radius:3px;transform:rotate(45deg);box-shadow:0 0 10px #61f4ef}.pf-fold-preview span{display:block}@media(max-width:420px){.pf-vehicle-hint{max-width:32vw;font-size:8px}.pf-fold-preview{font-size:8px;padding:4px 6px}}\r\n.pf-fold-preview.is-blocked{border-color:#ff8093;color:#fff0f2;background:#2b0c16ed;box-shadow:0 0 16px #ff4d6d76}.pf-fold-preview.is-blocked i{border-color:#ff8093;box-shadow:0 0 9px #ff4d6d}\r\n.pf-biome{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 8px;padding:7px 10px;border-left:3px solid var(--pf-accent);border-radius:3px 10px 10px 3px;background:#050b19e6;box-shadow:0 4px 15px #0005;max-width:620px}.pf-biome[hidden]{display:none}.pf-biome div{min-width:0;display:grid;gap:2px}.pf-biome span{color:var(--pf-warm);font-size:10px;letter-spacing:.12em;font-weight:950;text-transform:uppercase}.pf-biome strong{font-size:11px;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pf-biome b{align-self:center;color:white;font-size:18px;font-variant-numeric:tabular-nums}.pf-biome small{grid-column:1/-1;font-size:8px;color:#b5ced6;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.pf-biome:not([hidden])~.pf-live-mission{max-width:90%;font-size:8px;padding:4px 8px;opacity:.85}.pf-turn{align-self:center;padding:9px 15px;color:#f3fdff;border:1px solid #b6f8ff;border-radius:9px;background:#061926ed;font-size:11px;font-weight:900;letter-spacing:.09em;text-align:center}.pf-turn[hidden]{display:none}.pf-pickup-toast{align-self:center;padding:8px 12px;color:#fff4bb;background:#191002ed;border:1px solid #f5c861;border-radius:8px;font-size:11px;font-weight:900}.pf-pickup-toast[hidden]{display:none}.pf-portal-seal{display:grid;place-content:center;aspect-ratio:1;width:76px;margin:0 auto 18px;color:#fff;font-size:20px;line-height:.9;font-weight:1000;border:3px solid #fff;transform:rotate(-7deg);background:#080808;box-shadow:7px 7px 0 var(--pf-accent)}.pf-next-world{font-weight:900;color:var(--pf-warm)!important}.pf-primary:disabled{opacity:.45;cursor:wait}.pf-cockpit{position:absolute;inset:0;z-index:-1;pointer-events:none}.pf-cockpit[hidden]{display:none}.pf-cockpit svg{width:100%;height:100%;overflow:visible}.pf-cockpit path{fill:none;stroke-linejoin:round;vector-effect:non-scaling-stroke}.pf-cockpit-shadow path{stroke:#06090de6;stroke-width:4.5}.pf-cockpit-metal path{stroke:#8397a9d9;stroke-width:1.35}.pf-cockpit-tag{position:absolute;bottom:16%;left:50%;transform:translateX(-50%);background:#0b1018b3;color:#c7d7e2;padding:4px 6px;font-size:7px;letter-spacing:.14em;white-space:nowrap}.plank-flight[data-biome=\"castle\"] .pf-biome{border-radius:0;border-width:3px;image-rendering:pixelated}.plank-flight[data-biome=\"rainbow\"] .pf-biome{border-image:linear-gradient(90deg,#ff71ad,#ffe479,#80f3fa,#b482ff) 1}.plank-flight[data-biome=\"asteroid\"] .pf-aim{border-color:#aaf4bd;opacity:.35}.plank-flight[data-phase=\"portal\"] .pf-aim{display:none}\r\n@media(max-height:500px){.pf-biome{position:absolute;left:8px;top:110px;width:230px}.pf-biome small{display:none}.pf-biome:not([hidden])~.pf-live-mission{display:none}.pf-pace{max-width:360px;align-self:flex-end;min-height:48px}.pf-turn,.pf-pickup-toast{position:absolute;left:50%;top:48%;transform:translateX(-50%)}.pf-biome strong{font-size:10px}}\r\n\r\n/* The flight view owns a compact readout while keeping the first-person course open. */\r\n.plank-flight-host .camera-workout-housing{display:none!important}\r\n.plank-flight-host.plank-flight-calibrating>video{left:auto!important;right:10px!important;top:max(58px,calc(env(safe-area-inset-top) + 58px))!important;bottom:auto!important;width:56px!important;height:72px!important;max-width:56px!important;object-fit:cover!important;pointer-events:none!important;z-index:5!important}\r\n#cameraWorkout.plank-flight-host>video{left:auto!important;right:10px!important;top:max(58px,calc(env(safe-area-inset-top) + 58px))!important;bottom:auto!important;width:56px!important;height:72px!important;max-width:56px!important;object-fit:cover!important;pointer-events:none!important;z-index:5!important}\r\n.pf-hud{padding:max(5px,env(safe-area-inset-top)) 7px max(8px,env(safe-area-inset-bottom));gap:4px}\r\n.pf-topbar{min-height:32px}.pf-brand{gap:6px;font-size:9px;letter-spacing:.11em}.pf-brand-mark{width:24px;height:24px;border-radius:7px}.pf-top-actions{gap:5px}.pf-icon-button{min-width:34px;min-height:32px;border-radius:9px;font-size:15px}\r\n.pf-stats{grid-template-columns:repeat(2,minmax(0,104px));gap:4px;max-width:220px}.pf-stat{padding:4px 6px 5px;border-radius:8px}.pf-stat span{font-size:7px;letter-spacing:.06em}.pf-stat strong{margin-top:1px;font-size:11px}\r\n.pf-top-actions [data-act=\"recalibrate\"]{display:none!important}\r\n.pf-biome{width:max-content;max-width:min(62vw,230px);min-height:27px;padding:4px 7px;gap:4px 7px;border-radius:2px 7px 7px 2px;background:#050b19c7;box-shadow:0 3px 12px #0004;backdrop-filter:blur(4px)}.pf-biome div{display:block}.pf-biome span{display:block;overflow:hidden;max-width:42vw;font-size:8px;letter-spacing:.08em;text-overflow:ellipsis;white-space:nowrap}.pf-biome [data-biome-name] .game-cue{min-width:0;max-width:100%;gap:4px}.pf-biome [data-biome-name] .game-cue b{overflow:hidden;max-width:min(38vw,170px);font-size:9px;letter-spacing:.035em;text-overflow:ellipsis;white-space:nowrap}.pf-biome [data-biome-name] .game-symbol{width:15px;height:15px}.pf-biome b{font-size:11px;color:#c4d2db}.pf-biome small{display:none}\r\n.pf-live-mission{display:none!important}.pf-pace.pf-sr{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\r\n.pf-message.is-position-hint{position:absolute;top:99px;left:8px;align-self:auto;max-width:max-content;margin:0;padding:4px 8px;border:1px solid #ffffff30;border-radius:8px;background:#061018bd;color:#eaf6fa;font-size:9px;font-weight:750;letter-spacing:.035em;text-align:left;opacity:.9}\r\n@media(max-width:420px){.pf-hud{gap:4px}.pf-stats{grid-template-columns:repeat(2,minmax(0,98px))}.pf-biome{max-width:60vw}}\r\n.pf-launch-cue{position:absolute;inset:0;z-index:5;display:grid;place-items:center;width:100%;color:#fff6d7;font-size:clamp(56px,24vw,148px);font-weight:1000;letter-spacing:.045em;line-height:1;text-align:center;-webkit-text-stroke:2px #18263c;text-shadow:0 4px 0 #18263c,0 9px 22px #000c,0 0 28px var(--pf-accent);pointer-events:none;user-select:none}.pf-launch-cue[hidden]{display:none}\r\n@media(prefers-reduced-motion:reduce){.pf-launch-cue{transition:none!important}}\r\n.pf-propulsion{position:absolute;z-index:2;left:5px;right:5px;bottom:max(7px,env(safe-area-inset-bottom));display:flex;justify-content:space-between;align-items:flex-end;pointer-events:none}\r\n.pf-thruster{width:82px;height:82px;--pf-thruster-color:#70cfff;--pf-thruster-glow:.12;filter:drop-shadow(0 3px 5px #0009);transition:transform .12s ease}\r\n.pf-thruster svg{display:block;width:100%;height:100%;overflow:visible}.pf-thruster-shell{fill:var(--pf-thruster-color)}.pf-thruster-hot-ring{stroke:var(--pf-thruster-color);filter:drop-shadow(0 0 4px var(--pf-thruster-color))}\r\n.pf-thruster-halo{fill:var(--pf-thruster-color);opacity:var(--pf-thruster-glow);filter:blur(13px)}.pf-thruster-flame{opacity:0;transform-origin:50% 90%;transition:opacity .15s ease}\r\n.pf-thruster[data-firing=\"true\"] .pf-thruster-flame{opacity:.8;animation:pf-exhaust .13s ease-in-out infinite alternate}.pf-thruster.is-steering{transform:translateY(-5px) rotate(-4deg)}.pf-thruster[data-thruster=\"right\"].is-steering{transform:translateY(-5px) rotate(4deg)}\r\n.pf-motorcycle{position:absolute;z-index:1;left:50%;bottom:-6px;width:min(74vw,360px);height:auto;pointer-events:none;transform-origin:50% 100%;filter:drop-shadow(0 3px 6px #0008);transition:transform .12s ease}.pf-motorcycle[hidden]{display:none}.pf-motorcycle svg{display:block;width:100%;overflow:visible}.pf-bike-frame{transform-origin:180px 150px;transform:translateY(calc(var(--pf-wheelie,0) * -8px))}.pf-bike-wheel{transform-origin:180px 150px;transform:scaleY(calc(1 + var(--pf-wheelie,0) * .18))}\r\n.pf-stunt{position:absolute;left:50%;top:58%;z-index:3;transform:translateX(-50%);padding:7px 11px;border:2px solid #ffd669;border-radius:12px;color:#fff1aa;background:#342006db;font-size:16px;font-weight:950;letter-spacing:.06em;white-space:nowrap;text-shadow:0 2px #593314;box-shadow:0 0 22px #ffbd3144;pointer-events:none}.pf-stunt[hidden]{display:none}.pf-stunt span{display:inline-block;transform:rotate(var(--pf-stunt-spin,0deg));color:#ffcf4d}\r\n@keyframes pf-exhaust{from{transform:scaleX(.92) scaleY(.95)}to{transform:scaleX(1.08) scaleY(1.08)}}\r\n@media(max-width:420px){.pf-thruster{width:76px;height:76px}}\r\n@media(prefers-reduced-motion:reduce){.pf-thruster,.pf-motorcycle{transition:none}.pf-thruster-flame{animation:none!important}.pf-stunt span{transform:none}}\r\n.pf-pace{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:10px;width:min(100%,620px);min-height:68px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 70%,#ffffff40);border-radius:15px;color:#fff;background:linear-gradient(110deg,#06151aef,#090d1bf2);box-shadow:0 5px 20px #0008,0 0 22px color-mix(in srgb,var(--pf-personal,var(--pf-accent)) 20%,transparent);text-shadow:0 1px 5px #000}.pf-pace[hidden]{display:none}.pf-pace-current,.pf-pace-next{display:grid;min-width:0;gap:2px}.pf-pace-eyebrow{color:var(--pf-personal,var(--pf-accent));font-size:9px;font-weight:950;letter-spacing:.16em}.pf-pace-current strong,.pf-pace-next strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-pace-current strong{font-size:clamp(15px,4vw,21px);line-height:1.12}.pf-pace-next strong{font-size:clamp(12px,3vw,15px);line-height:1.15;color:#e1e8f4}.pf-pace-current small{overflow:hidden;color:#c0d4d4;font-size:9px;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.pf-pace-time{display:flex;align-items:baseline;gap:3px;padding:5px 8px;border-left:1px solid #ffffff26;border-right:1px solid #ffffff26;font-variant-numeric:tabular-nums}.pf-pace-time strong{min-width:2ch;text-align:right;color:#f6ffff;font-size:clamp(22px,6vw,30px);line-height:1}.pf-pace-time span{color:#c0d4d4;font-size:9px;font-weight:900;letter-spacing:.1em}.pf-pace.is-rest{border-color:#ffc66b;background:linear-gradient(110deg,#211709f2,#100c15f2);box-shadow:0 5px 20px #0008,0 0 20px #ffc66b33}.pf-pace.is-rest .pf-pace-eyebrow{color:#ffd27e}.pf-pace.is-rest .pf-pace-current strong{color:#ffe1a5}.pf-pace.is-transitioning{border-style:dashed}\r\n@media(max-width:420px){.pf-pace{gap:7px;min-height:62px;padding:7px 9px;border-radius:13px}.pf-pace-eyebrow{font-size:8px}.pf-pace-current strong{font-size:clamp(13px,4.3vw,17px)}.pf-pace-next strong{font-size:12px}.pf-pace-current small{font-size:8px}.pf-pace-time{gap:2px;padding:4px 6px}.pf-pace-time strong{font-size:24px}.pf-pace-time span{font-size:8px}}\r\n\r\n.pf-cockpit .pf-cockpit-emblem{position:absolute;bottom:16%;left:calc(50% - 18px);width:36px;height:22px;color:#a6b9c8;filter:drop-shadow(0 2px 1px #000)}\r\n/* Physical status cues keep the existing control and announcement paths. */\r\n.plank-flight .pf-stat>.game-symbol{width:22px;height:22px;float:left;margin-right:8px}.plank-flight .pf-biome .game-cue{display:flex;flex-direction:row;justify-content:flex-start}.plank-flight .pf-biome .game-symbol{width:26px;height:26px}.plank-flight .pf-biome .game-cue b{font-size:12px}.plank-flight .pf-message .game-symbol{width:38px;height:38px}.plank-flight .pf-launch-cue .game-symbol{width:64px;height:64px}.plank-flight .pf-launch-cue .game-cue b{font-size:56px}.plank-flight .pf-panel .game-cue-row{margin:24px 0;color:var(--pf-warm);gap:22px}.plank-flight .pf-panel .game-cue-row .game-symbol{width:56px;height:56px}\r\n\r\n.plank-flight .pf-thruster{position:relative}.plank-flight .pf-thruster::after{content:\"\";position:absolute;right:3px;bottom:22px;width:5px;height:calc(3px + var(--pf-heat-level,0)*29px);border:1px solid #efffff;border-radius:2px;background:var(--pf-thruster-color);box-shadow:0 0 0 2px #08121a}\r\n\r\n.pf-flight-fx{display:flex;flex-wrap:wrap;align-items:center;gap:4px;width:100%;min-width:0;max-width:min(100%,440px);box-sizing:border-box;pointer-events:none}.pf-flight-fx [hidden]{display:none!important}.pf-flight-fx>span,.pf-fx-chip{display:inline-flex;align-items:center;gap:4px;min-height:24px;padding:3px 6px;border:1px solid #ffffff45;border-radius:8px;color:#f5fbff;background:#06101cdb;font-size:8px;font-weight:900;letter-spacing:.045em;text-shadow:0 1px 4px #000}.pf-flight-fx>span[data-flight-fx-active]{display:flex;flex-wrap:wrap;min-width:0;max-width:100%;gap:4px}.pf-flight-fx>span[data-flight-fx-cue]{border-color:#ffe38a;background:#211a08ed;color:#fff2b3}.pf-flight-fx svg{width:15px;height:15px;flex:none}.pf-flight-fx b,.pf-flight-fx small{font-size:inherit;line-height:1;white-space:nowrap}.pf-flight-fx small{color:#bdeefa;font-variant-numeric:tabular-nums}.pf-flight-fx .is-protection{border-color:#a8ffcf}.pf-flight-fx .is-vehicle{border-color:#b4e9ff}.pf-flight-fx .is-multiplier{border-color:#ffe58b;color:#ffe58b}.pf-flight-fx .is-vehicle-time{border-color:#b4e9ff;color:#e4f8ff}\r\n.pf-flight-fx>span[data-flight-fx-cue]:not([data-fx-kind=\"coin\"]){min-height:40px;padding:5px 9px;gap:8px;font-size:10px;border-radius:12px;box-shadow:0 5px 16px #0007}.pf-flight-fx>span[data-flight-fx-cue]:not([data-fx-kind=\"coin\"]) svg{width:28px;height:28px}\r\n.plank-flight.is-shielded .pf-aim{outline:2px solid #9affcf;outline-offset:5px;box-shadow:0 0 20px #8effc875}.plank-flight.has-impact-buffer .pf-aim{border-style:double;border-width:3px}.plank-flight.is-magnet-active .pf-aim{border-color:#71efff;box-shadow:0 0 18px #50e8ff75}.plank-flight.is-quiet-vector .pf-thruster{transition-duration:.24s}.plank-flight.is-early-signal .pf-notice{border-style:double}.plank-flight.is-rescue-echo.has-impact-buffer .pf-aim{outline-color:#d5b5ff}\r\n.plank-flight .pf-thruster-shell{fill:color-mix(in srgb,var(--pf-thruster-color) 76%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-thruster-halo{fill:color-mix(in srgb,var(--pf-thruster-color) 68%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-bike-frame path:first-child{fill:color-mix(in srgb,#e45a27 74%,var(--pf-loadout-tint,transparent))}.plank-flight .pf-thruster[data-boosting=\"true\"] .pf-thruster-flame{opacity:calc(.2 + var(--pf-boost-factor)*.58);transform:scaleY(calc(.64 + var(--pf-boost-factor)*.72))}.plank-flight[data-fx-cue=\"vehicle\"] .pf-motorcycle{filter:drop-shadow(0 0 11px #9cf2ff) drop-shadow(0 3px 6px #0008)}.plank-flight[data-fx-cue=\"shield\"] .pf-cockpit svg,.plank-flight[data-fx-cue=\"impact\"] .pf-cockpit svg{filter:drop-shadow(0 0 9px #adffdb)}\r\n@media(max-width:420px){.pf-flight-fx{max-width:calc(100vw - 14px);gap:3px}.pf-flight-fx>span,.pf-fx-chip{min-height:22px;padding:2px 5px;font-size:7px}.pf-flight-fx svg{width:13px;height:13px}}\r\n@media(prefers-reduced-motion:reduce){.pf-flight-fx>span[data-flight-fx-cue]{transition:none!important}.plank-flight[data-fx-cue=\"vehicle\"] .pf-motorcycle{filter:drop-shadow(0 3px 6px #0008)}}\r\n","arcade/plank-flight/flight.mjs":"import {gameShellPhase} from '../shell-phase.mjs';\r\nimport {createFlight,advanceFlight,commandFlight} from './sim.mjs';\r\nimport {SHOP_ITEMS} from './catalog.mjs';\r\nimport {mountFlightView} from './view.mjs';\r\nimport {openFlightWallet} from './economy.mjs';\r\nimport {PLANK_TUNING} from './pose-input.mjs';\r\nimport {playGamePortal,gamePortalReady} from '../game-portal.mjs';\r\nimport {FLIGHT_LAUNCH,flightLaunchCue} from './launch.mjs';\r\nimport {createFlightAudio} from './flight-sound.mjs';\r\nconst identity=()=>globalThis.crypto.randomUUID();\r\nconst blank=()=>({balance:0,tokens:0,stars:0,bestDistance:0,owned:[],loadout:[]});\r\nexport function mountPlankFlight({host,ownerId,owner=()=>ownerId,onExit=()=>{},onRecalibrate=()=>{},sound=()=>{},audio=null}){\r\n  const shell=gameShellPhase('plank-flight',host);\r\n  let profile=blank(),wallet=null,dead=false,last=0,lastPose=-Infinity,pose=null,countdownUntil=0,goUntil=0,lastLaunchWord='',raf=0,manualPause=false,portalBusy=false,error='',pendingCoins=0,renderFailed=false,lastRetry=0,saving=0;\r\n  let saveQueue=Promise.resolve(),walletReady;\r\n  const outbox=new Map(),finalizations=new Map();\r\n  const run=createFlight({seed:Date.now()>>>0,runId:identity(),paced:true,biomes:true,progressive:true});\r\n  const flightAudio=createFlightAudio({audio,play:sound});\r\n  const current=()=>!dead&&owner()===ownerId;\r\n  const say=message=>{error=message;};\r\n  async function flush(w){\r\n    for(const [key,reward] of outbox){profile=await w.record(reward.runId,[reward.event]);outbox.delete(key);}\r\n    pendingCoins=[...outbox.values()].filter(r=>r.event.type==='coin').reduce((s,r)=>s+r.event.amount,0);\r\n    for(const [rid,distance] of finalizations){profile=await w.finish(rid,distance);finalizations.delete(rid);}\r\n  }\r\n  // Captured owner wallet remains the destination for earned events after an account switch.\r\n  const pending=(operation=w=>w.read())=>{saving++;saveQueue=saveQueue.then(async()=>{await walletReady;if(!wallet)throw Error('Device coin storage unavailable. Coins are not saved.');await flush(wallet);profile=await operation(wallet);error='';}).catch(e=>{pendingCoins=[...outbox.values()].filter(r=>r.event.type==='coin').reduce((s,r)=>s+r.event.amount,0);say(e.message);}).finally(()=>{saving--;});return saveQueue;};\r\n  walletReady=openFlightWallet({owner:ownerId}).then(async w=>{wallet=w;profile=await w.read();}).catch(e=>say(e.message));\r\n  function drain(){for(const event of run.events.splice(0)){\r\n    if(['coin','token','mission'].includes(event.type)){\r\n      const amount=event.type==='coin'?(event.amount??event.value??1):event.type==='mission'?(event.stars??1):1;\r\n      if(event.type==='coin')pendingCoins+=amount;\r\n      const reward={id:`${event.type}:${event.id??event.entityId??run.time}`,type:event.type,amount};\r\n      const rid=run.runId;outbox.set(`${rid}:${reward.id}`,{runId:rid,event:reward});pending();\r\n      flightAudio.cue(event.type==='coin'?'coin':'pickup');\r\n    }else if(event.type==='vehicle'&&event.origin==='component-set'){\r\n      const reward={id:`component-set:${event.entityId}`,type:'component-set'};\r\n      outbox.set(`${run.runId}:${reward.id}`,{runId:run.runId,event:reward});pending();flightAudio.cue('pickup');\r\n    }else if(event.type==='stunt')flightAudio.cue('flight-stunt');else if(['hit','crash'].includes(event.type))flightAudio.cue('hit');else if(['vehicle','powerup'].includes(event.type))flightAudio.cue('pickup');else if(event.kind==='biome-portal')flightAudio.cue('flight-portal');\r\n  }}\r\n  function finish(){countdownUntil=goUntil=0;flightAudio.stop();commandFlight(run,'finish');drain();finalizations.set(run.runId,run.distance);pending();}\r\n  function restart(){if(run.phase!=='finalized')finish();const next=createFlight({seed:(Date.now()>>>0),runId:identity(),loadout:profile.loadout??[],paced:true,biomes:true,progressive:true});for(const key of Object.keys(run))delete run[key];Object.assign(run,next);manualPause=false;countdownUntil=goUntil=0;lastLaunchWord='';}\r\n  function action(name,payload={}){\r\n    if(dead)return;\r\n    if(name==='render-error'){renderFailed=true;manualPause=true;commandFlight(run,'pause');say('Flight graphics could not start. End this flight and try again.');return;}\r\n    if(name==='buy'){pending(w=>w.buy(payload.id,SHOP_ITEMS));return;}\r\n    if(name==='equip'){if(run.phase==='flying'){say('Pause before changing your gadgets.');return;}pending(w=>w.equip(payload.id,SHOP_ITEMS));return;}\r\n    if(name==='spin'){if(run.phase!=='finalized'){say('End the flight to open your token reward.');return;}const rid=run.runId;pending(w=>w.spin(rid));return;}\r\n    if(name==='retry-save'){pending();return;}\r\n    if(name==='continue-biome'){\r\n      if(!current()||renderFailed||portalBusy)return;\r\n      const swap=()=>{if(!dead&&current()&&commandFlight(run,'continue-biome')){manualPause=false;countdownUntil=0;error='';}};\r\n      if(!gamePortalReady())return swap(); // reduced motion / no portal: immediate\r\n      portalBusy=true; // the user's grimoire portal covers the swap; pose keeps flowing\r\n      return playGamePortal({host,swap}).finally(()=>{portalBusy=false;});\r\n    }\r\n    if(name==='finish'){finish();return;}\r\n    if(name==='exit'){finish();void saveQueue.then(()=>{if(!current())return;if(outbox.size||finalizations.size){say('Coins are waiting to save. Try End workout again to retry.');return;}onExit();});return;}\r\n    if(name==='new-run'||name==='restart'){restart();return;}\r\n    if(name==='recalibrate'){error='';manualPause=false;countdownUntil=0;commandFlight(run,'pause');pose=null;onRecalibrate();return;}\r\n    if(name==='pause'){if(run.phase==='portal')return;manualPause=true;countdownUntil=0;commandFlight(run,'pause');return;}\r\n    if(['start','resume','revive'].includes(name)){\r\n      if(name==='start'&&!renderFailed&&current()&&(!pose?.controlValid||!(pose?.calibrated||pose?.restFallback))){error='';manualPause=false;countdownUntil=0;return;}\r\n      if(renderFailed||!current()||!pose?.controlValid||!(pose?.calibrated||pose?.restFallback)){say('Get into position.');return;}\r\n      if(name==='revive'&&run.phase!=='crashed')return;\r\n      manualPause=false;countdownUntil=performance.now()+FLIGHT_LAUNCH.durationMs;\r\n      if(name==='revive'){commandFlight(run,'revive');commandFlight(run,'pause');}\r\n      return;\r\n    }\r\n    if(['headstart','final-blast','event'].includes(name)){if(!commandFlight(run,name,payload))say('This boost is not available in this flight.');}\r\n  }\r\n  const view=mountFlightView({host,run,onAction:action,sound:kind=>flightAudio.cue(kind)});\r\n  function frame(now){if(dead)return;raf=requestAnimationFrame(frame);\r\n    const dt=last?Math.max(0,(now-last)/1000):0;last=now;\r\n    const valid=current()&&!renderFailed&&!document.hidden&&pose?.controlValid&&now-lastPose<=PLANK_TUNING.freshnessMs;\r\n    if(!valid&&run.phase==='flying'){commandFlight(run,'pause');countdownUntil=0;}\r\n    if(!current()){manualPause=true;error='Your profile changed. End this flight and start again.';}\r\n    if(valid&&!manualPause&&(run.phase==='ready'||run.phase==='paused')&&!countdownUntil)countdownUntil=now+FLIGHT_LAUNCH.durationMs;\r\n    if(!valid||manualPause){countdownUntil=goUntil=0;lastLaunchWord='';}\r\n    if(countdownUntil&&now>=countdownUntil){const cmd=run.phase==='ready'?'start':'resume';\r\n      if(run.phase==='ready'){run.loadout=[...(profile.loadout??[])];run.explicitVehicle=profile.selectedVehicle??null;run.upgrades={...(profile.upgrades??{})};}\r\n      if(commandFlight(run,cmd))goUntil=now+FLIGHT_LAUNCH.goMs;countdownUntil=0;}\r\n    advanceFlight(run,dt,{height01:pose?.height01??.5,steer:pose?.steer??0,resting:pose?.resting===true,valid:!!(valid&&!manualPause&&!countdownUntil)});drain();\r\n    shell.phase(current()&&!renderFailed&&!document.hidden&&!manualPause&&!countdownUntil&&run.phase==='flying'?'active':renderFailed?'error':'paused');\r\n    const launchCue=flightLaunchCue({deadline:countdownUntil,goUntil},now);\r\n    if(launchCue?.word&&launchCue.word!==lastLaunchWord){lastLaunchWord=launchCue.word;flightAudio.cue(launchCue.word);}\r\n    flightAudio.update({flying:valid&&!manualPause&&!countdownUntil&&run.phase==='flying',speed:run.speed,height:run.y,time:run.time,motorcycle:['tropical','rainbow'].includes(run.biome?.id)});\r\n    if((outbox.size||finalizations.size)&&!saving&&now-lastRetry>5000){lastRetry=now;pending();}\r\n    const message=error||(run.phase==='portal'?'Safe portal bay. Relax your hands and shake your arms out; exercise credit is paused.':countdownUntil?`Ready in ${Math.ceil((countdownUntil-now)/1000)}`:(!pose?.controlValid?pose?.reason:null)||pose?.prompt||pose?.reason||'Get into position.');\r\n    const shopItems=SHOP_ITEMS.map(item=>item.kind==='upgrade'?{...item,currentLevel:profile.upgrades?.[item.id]??0,price:item.price*((profile.upgrades?.[item.id]??0)+1)}:item);\r\n    try{view.render({pose,message,launchCue,paused:run.phase!=='flying',profile,loadout:profile.loadout,inventory:profile,shopItems,coinsBanked:profile.balance,pendingCoins});}catch{action('render-error');}\r\n  }\r\n  raf=requestAnimationFrame(frame);\r\n  return {run,action,get poseResult(){return pose},get creditAllowed(){return current()&&!renderFailed&&!manualPause&&!countdownUntil&&!document.hidden&&!pose?.resting&&pose?.posture!=='rest'&&run.phase==='flying'&&performance.now()-lastPose<=PLANK_TUNING.freshnessMs;},\r\n    pose(detail){pose=detail?.flightPose??detail?.motion?.flightPose??null;lastPose=performance.now();if(!pose?.controlValid&&run.phase==='flying'){commandFlight(run,'pause');countdownUntil=0;}},\r\n    stop(){if(dead)return;shell.close();finish();dead=true;cancelAnimationFrame(raf);flightAudio.dispose();view.stop();void saveQueue.finally(()=>{wallet?.close();wallet=null;});},\r\n    get soundStats(){return flightAudio.stats;},get renderStats(){return view.stats??null;},get settled(){return saveQueue;}};\r\n}\r\n","arcade/plank-flight/launch.mjs":"export const FLIGHT_LAUNCH=Object.freeze({durationMs:3000,goMs:750});\r\nexport function flightLaunchCue({deadline=0,goUntil=0},now){\r\n if(!Number.isFinite(now))return null;\r\n if(deadline>now)return {word:deadline-now>FLIGHT_LAUNCH.durationMs/2?'READY':'SET',opacity:1};\r\n if(goUntil>now)return {word:'GO',opacity:Math.min(1,Math.max(0,(goUntil-now)/FLIGHT_LAUNCH.goMs))};\r\n return null;\r\n}\r\n","arcade/plank-flight/movement.mjs":"// One eligibility boundary feeds the hold clock and the flight controller.\r\n// These are camera heuristics, not a measurement of floor contact or arm loading.\r\nimport {PLANK_TUNING} from './pose-input.mjs';\r\nexport function applyFlightEvidence(session, pose, timestamp, {allowed=false,maxGap=PLANK_TUNING.freshnessMs/1000}={}) {\r\n  const now=timestamp/1000,last=session.flightLast;\r\n  if(!Number.isFinite(now)||now<0||(Number.isFinite(last)&&now<=last)){\r\n    session.flightPose=pose;session.flightEligible=false;session.hold=0;session.tracking=false;session.phase='ready';session.message=pose?.reason||'Waiting for a fresh camera frame.';\r\n    return false;\r\n  }\r\n  session.flightLast=now;session.flightPose=pose;\r\n  const dt=Number.isFinite(now)&&Number.isFinite(last)&&now>last&&now-last<=maxGap?now-last:0;\r\n  const resting=pose?.resting===true||pose?.posture==='rest';\r\n  const eligible=!!(allowed&&!resting&&pose?.exerciseEligible&&pose?.controlValid&&pose?.calibrated);\r\n  const was=session.flightEligible===true;session.flightEligible=eligible;\r\n  if(session.started===null&&pose?.calibrated)session.started=now;\r\n  session.elapsed=session.started===null?0:Math.max(0,now-session.started);\r\n  if(eligible&&was&&dt>0){session.hold+=dt;session.totalHold+=dt;session.active+=dt;session.bestHold=Math.max(session.bestHold,session.hold);}\r\n  else if(!eligible||dt===0)session.hold=0;\r\n  session.base=pose?.calibrated?{}:null;session.tracking=eligible;\r\n  session.phase=eligible?'holding':pose?.calibrated?'ready':'calibrating';\r\n  session.setupProgress=pose?.calibrationProgress??0;session.progress=pose?.height01??0;\r\n  session.message=(!pose?.controlValid?pose?.reason:null)||pose?.prompt||pose?.reason||(eligible?'Fly at your own pace.':'Get into position.');\r\n  if(pose?.controlValid&&!eligible&&resting)session.message='Resting position estimated. Flight continues; exercise credit is paused.';\r\n  else if(pose?.controlValid&&!pose?.exerciseEligible)session.message='Practice only. Show your hips and knees for workout credit.';\r\n  session.measurement='Face-on pose estimate · arm pressure and floor contact are not measured.';\r\n  return eligible;\r\n}\r\n","arcade/plank-flight/pacing.mjs":"const clamp=(value,min,max)=>Math.max(min,Math.min(max,value));\r\nconst validSeed=value=>(Number(value)>>>0)||1;\r\nconst REST_TARGET=Object.freeze({x:0,y:.04});\r\nconst ACTIVE_TARGETS=Object.freeze({\r\n  top:Object.freeze({x:0,y:.85}),\r\n  middle:Object.freeze({x:0,y:.5}),\r\n  left:Object.freeze({x:-.55,y:.6}),\r\n  right:Object.freeze({x:.55,y:.6})\r\n});\r\nconst ACTIVE_ORDER=Object.freeze(['top','middle','left','right']);\r\n\r\nfunction pairRandom(seed,index){\r\n  let state=(seed+Math.imul(index+1,0x9e3779b9))>>>0;\r\n  return ()=>{\r\n    state=(state+0x6d2b79f5)>>>0;\r\n    let t=state;\r\n    t=Math.imul(t^(t>>>15),t|1);\r\n    t^=t+Math.imul(t^(t>>>7),t|61);\r\n    return ((t^(t>>>14))>>>0)/4294967296;\r\n  };\r\n}\r\n\r\n/** Create a deterministic, indexed rest/active pace schedule. Pairs are generated lazily. */\r\nexport function createPaceSchedule(seed=1){\r\n  return {seed:validSeed(seed),transitionSeconds:.9,_pairs:[]};\r\n}\r\n\r\nfunction pairAt(schedule,index){\r\n  const pairIndex=Math.max(0,Math.floor(index));\r\n  while(schedule._pairs.length<=pairIndex){\r\n    const n=schedule._pairs.length,random=pairRandom(schedule.seed,n);\r\n    const activeSeconds=3+random()*2;\r\n    // Rest allocation satisfies the 40–60% pair band. The +1.5s lower\r\n    // bound also leaves at least 40% neutral rest after both entry blends.\r\n    const minRest=Math.max(3,2*activeSeconds/3+1.5);\r\n    const maxRest=Math.min(5,1.5*activeSeconds);\r\n    const restSeconds=minRest+random()*(maxRest-minRest);\r\n    const start=n===0?0:schedule._pairs[n-1].end;\r\n    schedule._pairs.push({index:n,start,restSeconds,activeSeconds,end:start+restSeconds+activeSeconds,\r\n      restFraction:restSeconds/(restSeconds+activeSeconds),\r\n      modeledRestFraction:(restSeconds-schedule.transitionSeconds)/(restSeconds+activeSeconds)});\r\n  }\r\n  return schedule._pairs[pairIndex];\r\n}\r\n\r\nfunction activeKindFor(schedule,pairIndex){\r\n  const offset=schedule.seed%ACTIVE_ORDER.length;\r\n  const direction=(schedule.seed&1)?1:-1;\r\n  return ACTIVE_ORDER[(offset+direction*pairIndex%ACTIVE_ORDER.length+ACTIVE_ORDER.length)%ACTIVE_ORDER.length];\r\n}\r\n\r\nfunction targetFor(schedule,index){\r\n  if(index<0)return REST_TARGET;\r\n  if(index%2===0)return REST_TARGET;\r\n  return ACTIVE_TARGETS[activeKindFor(schedule,(index-1)/2)];\r\n}\r\n\r\nfunction controlTarget(target){return {height01:target.y,steer:clamp(target.x/.8,-1,1)};}\r\n\r\n/** Get any rest/active segment by zero-based index, independent of call order. */\r\nexport function segmentAt(schedule,index){\r\n  if(!schedule||!Number.isInteger(index)||index<0)throw new RangeError('Segment index must be a nonnegative integer.');\r\n  const pair=pairAt(schedule,Math.floor(index/2));\r\n  const isRest=index%2===0;\r\n  const kind=isRest?'rest':'active';\r\n  const activeKind=isRest?undefined:activeKindFor(schedule,Math.floor(index/2));\r\n  const duration=isRest?pair.restSeconds:pair.activeSeconds;\r\n  const start=isRest?pair.start:pair.start+pair.restSeconds;\r\n  const target=targetFor(schedule,index);\r\n  const fromTarget=targetFor(schedule,index-1);\r\n  const changed=target.x!==fromTarget.x||target.y!==fromTarget.y;\r\n  const transitionSeconds=changed?schedule.transitionSeconds:0;\r\n  const modeledRestSeconds=isRest?Math.max(0,duration-transitionSeconds):0;\r\n  return {index,kind,...(activeKind?{activeKind}:{}),start,end:start+duration,duration,allocatedSeconds:duration,\r\n    target:{x:target.x,y:target.y},controlTarget:controlTarget(target),height01:target.y,steer:controlTarget(target).steer,\r\n    fromTarget:{x:fromTarget.x,y:fromTarget.y},fromControlTarget:controlTarget(fromTarget),transitionSeconds,\r\n    settledSeconds:Math.max(0,duration-transitionSeconds),modeledRestSeconds,\r\n    restShare:pair.restFraction,modeledRestFraction:pair.modeledRestFraction};\r\n}\r\n\r\n/** Resolve target and preview from schedule time (seconds since run start). */\r\nexport function paceAt(schedule,timeSeconds){\r\n  if(!schedule||!Number.isFinite(timeSeconds)||timeSeconds<0)throw new RangeError('Pace time must be a finite nonnegative number.');\r\n  let index=0,current=segmentAt(schedule,index);\r\n  while(timeSeconds>=current.end){current=segmentAt(schedule,++index);}\r\n  const elapsed=timeSeconds-current.start;\r\n  const transitioning=current.transitionSeconds>0&&elapsed<current.transitionSeconds;\r\n  const blend=current.transitionSeconds?clamp(elapsed/current.transitionSeconds,0,1):1;\r\n  const target={x:current.fromTarget.x+(current.target.x-current.fromTarget.x)*blend,\r\n    y:current.fromTarget.y+(current.target.y-current.fromTarget.y)*blend};\r\n  const next=segmentAt(schedule,index+1);\r\n  return {segment:current,index,kind:current.kind,activeKind:current.activeKind,elapsed,\r\n    remaining:Math.max(0,current.duration-elapsed),progress:clamp(elapsed/current.duration,0,1),\r\n    target,controlTarget:controlTarget(target),height01:target.y,steer:controlTarget(target).steer,\r\n    transitioning,transitionRemaining:transitioning?current.transitionSeconds-elapsed:0,\r\n    isRest:current.kind==='rest',modeledRest:current.kind==='rest'&&!transitioning,\r\n    modeledRestSeconds:current.modeledRestSeconds,restShare:current.restShare,next,nextAt:current.end};\r\n}\r\n","arcade/plank-flight/pose-input.mjs":"// Front-facing image geometry, not a contact/pressure or exercise-technique measurement.\r\n// `mirrored` describes preview CSS. MediaPipe points remain in raw camera coordinates.\r\n// Provisional timeout: the integrated browser host delivered ~3.75 pose Hz with\r\n// >350 ms jitter. This accommodates that observed CPU cadence; phones remain\r\n// unmeasured. Explicit invalid observations still invalidate immediately.\r\nexport const freshnessMs = 750;\r\nexport const PLANK_TUNING = Object.freeze({\r\n  visibility: .65, dwellMs: 650, minSamples: 5, maxGapMs: freshnessMs, freshnessMs, resetGapMs: 2500,\r\n  minSpan: .08, minHeightRange: .18, minLeanRange: .13, noise: .035,\r\n  maxDepthDrift: .25, maxLeanCoupling: .35, wristDrift: .22,\r\n  minScaleRatio: .72, maxScaleRatio: 1.32, smoothingMs: 80, deadband: .08,\r\n  postureDwellMs: 350, maxHeightLeak: .14, maxSteerLeak: .22,\r\n  minRestRange: .18, minArmHeight: -.20, restEnterFraction: .18, restExitFraction: .30, maxBottomDrift: .12,\r\n  autoMinReach: 1.45, autoLeanRange: .27, autoRestEnter: .16, autoRestExit: .24, autoCreditHeight: .28,\r\n  autoVerticalGain: 1.5, autoFloorSettleMs: 650, autoRestFallbackDwellMs: 350, autoShouldersOnlyStartupDwellMs: 1200,\r\n});\r\nconst UPPER = [11, 12, 13, 14, 15, 16], LOWER = [23, 24, 25, 26];\r\nconst STAGES = ['high', 'low', 'left', 'right', 'mid', 'mixed-left', 'mixed-right', 'bottom'];\r\nconst PROMPTS = {\r\n  high: 'Face the camera with both hands in view. Hold your comfortable high plank.',\r\n  low: 'Lower into a comfortable push-up. Hold briefly; keep your shoulders centered.',\r\n  left: 'Return high. Keep both hands down and lean gently toward your LEFT arm.',\r\n  right: 'Stay high. Keep both hands down and lean gently toward your RIGHT arm.',\r\n  mid: 'Return to the middle. Hold halfway between your high and low positions.',\r\n  'mixed-left': 'At that comfortable middle height, lean gently toward your LEFT arm.',\r\n  'mixed-right': 'At middle height, lean gently toward your RIGHT arm.',\r\n  bottom: 'Center yourself and rest your chest fully on the floor. Keep both shoulders, elbows and hands visible; hold briefly.',\r\n  ready: 'Rest down to fly low. Return to your measured middle to fly in the middle; rise to fly high. Lean with both hands down.',\r\n};\r\nconst clamp = (x, a, b) => Math.max(a, Math.min(b, x));\r\nconst dot = (a, b) => a.x * b.x + a.y * b.y;\r\nconst sub = (a, b) => ({x: a.x - b.x, y: a.y - b.y});\r\nconst mid = (a, b) => ({x: (a.x + b.x) / 2, y: (a.y + b.y) / 2});\r\nconst length = a => Math.hypot(a.x, a.y);\r\nconst median = a => [...a].sort((x, y) => x - y)[Math.floor(a.length / 2)];\r\nconst angle = (a, b, c) => {\r\n  const u = ['x', 'y', 'z'].map(k => (a[k] ?? 0) - (b[k] ?? 0));\r\n  const v = ['x', 'y', 'z'].map(k => (c[k] ?? 0) - (b[k] ?? 0));\r\n  const d = Math.hypot(...u) * Math.hypot(...v);\r\n  return d > 1e-8 ? Math.acos(clamp(u.reduce((s, x, i) => s + x * v[i], 0) / d, -1, 1)) * 180 / Math.PI : 0;\r\n};\r\n\r\nfunction geometry(detail, T) {\r\n  const {points: p, width, height} = detail;\r\n  if (!Array.isArray(p) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0)\r\n    return {reason: 'Show your shoulders, elbows and both wrists in the camera.'};\r\n  const clear = i => p[i] && [p[i].x, p[i].y, p[i].visibility].every(Number.isFinite)\r\n    && p[i].visibility >= T.visibility && p[i].x >= .015 && p[i].x <= .985 && p[i].y >= .015 && p[i].y <= .985;\r\n  if (!UPPER.every(clear)) return {reason: 'Keep both shoulders, elbows and wrists clearly in view.'};\r\n  const aspect = width / height, pt = i => ({x: p[i].x * aspect, y: p[i].y});\r\n  const s = mid(pt(11), pt(12)), w = mid(pt(15), pt(16));\r\n  const shoulderSpan = length(sub(pt(11), pt(12))), wrists = sub(pt(16), pt(15));\r\n  if (wrists.x < 0) { wrists.x *= -1; wrists.y *= -1; }\r\n  const wristSpan = length(wrists);\r\n  if (shoulderSpan < T.minSpan || wristSpan < T.minSpan || Math.abs(wrists.x) / wristSpan < .8)\r\n    return {reason: 'Face the camera squarely; show space between both hands.'};\r\n  const t = {x: wrists.x / wristSpan, y: wrists.y / wristSpan}, n = {x: -t.y, y: t.x};\r\n  const armHeight = -dot(sub(s, w), n) / shoulderSpan;\r\n  // A frontal resting chest can project at, or just below, the hand line.\r\n  // Visibility and learned separation, not an assumed image height, admit rest.\r\n  if (armHeight < T.minArmHeight || armHeight > 2.5) return {reason: 'Keep your shoulders and both hands in the calibrated camera view.'};\r\n  return {s, w, t, n, shoulderSpan, wristSpan, clear, pt, confidence: Math.min(...UPPER.map(i => p[i].visibility))};\r\n}\r\n\r\nfunction shoulderTrack(detail,T,minVisibility=T.visibility){\r\n  const p=detail.points,{width,height}=detail;\r\n  if(!Array.isArray(p)||!Number.isFinite(width)||!Number.isFinite(height)||width<=0||height<=0)return null;\r\n  const clear=i=>p[i]&&[p[i].x,p[i].y,p[i].visibility].every(Number.isFinite)\r\n    &&p[i].visibility>=minVisibility&&p[i].x>=.015&&p[i].x<=.985&&p[i].y>=.015&&p[i].y<=.985;\r\n  if(!clear(11)||!clear(12))return null;\r\n  const aspect=width/height,pt=i=>({x:p[i].x*aspect,y:p[i].y}),s=mid(pt(11),pt(12)),span=length(sub(pt(11),pt(12)));\r\n  if(span<T.minSpan)return null;\r\n  return {s,span,pt,clear,confidence:Math.min(p[11].visibility,p[12].visibility)};\r\n}\r\n\r\n// Pre-calibration floor acquisition needs visible torso evidence. One hip is\r\n// enough because the near hip is commonly hidden by a shoulder in front view.\r\nfunction initialFloorPose(detail,T,shoulders=shoulderTrack(detail,T)){\r\n  if(!shoulders)return null;\r\n  const p=detail.points,hips=[23,24].filter(i=>shoulders.clear(i));\r\n  if(!hips.length)return null;\r\n  const h=hips.map(shoulders.pt),hip=hips.length===2?mid(h[0],h[1]):h[0],torso=sub(hip,shoulders.s),lengthTorso=length(torso);\r\n  if(lengthTorso/shoulders.span<.10||lengthTorso/shoulders.span>1.1)return null;\r\n  const projectedRecede=-torso.y/lengthTorso>=.35&&Math.abs(torso.x)/lengthTorso<=.8;\r\n  const world=detail.world,worldHips=hips.every(i=>world?.[i]&&['x','y','z'].every(k=>Number.isFinite(world[i][k])));\r\n  const worldShoulders=[11,12].every(i=>world?.[i]&&['x','y','z'].every(k=>Number.isFinite(world[i][k])));\r\n  let worldRecede=null;\r\n  if(worldHips&&worldShoulders){\r\n    const center=ids=>Object.fromEntries(['x','y','z'].map(k=>[k,ids.reduce((sum,i)=>sum+world[i][k],0)/ids.length]));\r\n    const ws=center([11,12]),wh=center(hips),delta={x:ws.x-wh.x,y:ws.y-wh.y,z:ws.z-wh.z};\r\n    const depth=Math.hypot(delta.x,delta.y,delta.z);\r\n    const shoulderDepth=Math.hypot(world[11].x-world[12].x,world[11].y-world[12].y,world[11].z-world[12].z);\r\n    worldRecede=shoulderDepth>=.15&&shoulderDepth<=.8&&depth>=.15&&depth<=1.1\r\n      &&Math.abs(delta.z)/depth>=.50&&Math.abs(delta.y)/depth<=.70;\r\n  }\r\n  if(!(worldRecede??projectedRecede))return null;\r\n  return {...shoulders,hip,hipSpan:hips.length===2?length(sub(h[0],h[1])):shoulders.span*.55,\r\n    evidence: lengthTorso/shoulders.span,source:'torso'};\r\n}\r\n\r\n// The projected fallback intentionally requires the torso to recede ABOVE the\r\n// shoulders in this floor/front framing. Ambiguous or heavily foreshortened poses\r\n// remain uncredited. World coordinates supplement visible joints; they never\r\n// rescue an occluded hip/knee or establish ground contact.\r\nfunction proneEvidence(detail, g) {\r\n  if (!LOWER.every(g.clear)) return false;\r\n  const w = detail.world, ids = [...UPPER, ...LOWER];\r\n  const hasWorld = Array.isArray(w) && ids.every(i => w[i] && ['x', 'y', 'z'].every(k => Number.isFinite(w[i][k])));\r\n  if (hasWorld) {\r\n    const span = Math.hypot(w[11].x - w[12].x, w[11].y - w[12].y, w[11].z - w[12].z);\r\n    if (span < .15 || span > .8) return false;\r\n    return [[11, 23, 25, 15], [12, 24, 26, 16]].every(([si, hi, ki, wi]) => {\r\n      const s = w[si], h = w[hi], k = w[ki], hand = w[wi];\r\n      const torso = Math.hypot(h.x - s.x, h.y - s.y, h.z - s.z);\r\n      const thigh = Math.hypot(k.x - h.x, k.y - h.y, k.z - h.z);\r\n      return torso > .18 && torso < .9 && thigh > .12 && thigh < .8\r\n        && angle(s, h, k) >= 150 && Math.abs(h.z - s.z) / torso >= .65\r\n        && Math.abs(h.y - s.y) / torso <= .55 && (h.z - s.z) * (k.z - h.z) > 0\r\n        && hand.y - s.y > .04;\r\n    });\r\n  }\r\n  return [[11, 23, 25], [12, 24, 26]].every(([si, hi, ki]) => {\r\n    const s = g.pt(si), h = g.pt(hi), k = g.pt(ki), torso = sub(h, s), thigh = sub(k, h);\r\n    return length(torso) / g.shoulderSpan >= .12 && length(thigh) / g.shoulderSpan >= .08\r\n      && dot(torso, g.n) / g.shoulderSpan < -.10 && dot(thigh, g.n) / g.shoulderSpan < -.05\r\n      && Math.abs(dot(torso, g.t)) / g.shoulderSpan < .45 && angle(s, h, k) >= 155;\r\n  });\r\n}\r\n\r\nexport function createPlankInput(options = {}) {\r\n  if(options.automatic===true)return createAutomaticPlankInput(options);\r\n  const T = {...PLANK_TUNING, ...(options.tuning ?? {})};\r\n  let index, samples, anchors, reference, model, lastTime, identity, filtered, postureSince, resting;\r\n  function clearCalibration() { index = 0; samples = []; anchors = {}; reference = null; model = null; filtered = null; postureSince = null; resting = false; }\r\n  function reset() { clearCalibration(); lastTime = null; identity = null; }\r\n  reset();\r\n  const stage = () => STAGES[index] ?? 'ready';\r\n  const result = (now, extra = {}) => ({height01: .5, steer: 0, controlValid: false, exerciseEligible: false, resting: false, posture: 'unknown',\r\n    confidence: 0, reason: '', stage: stage(), prompt: PROMPTS[stage()], calibrated: index === STAGES.length,\r\n    calibrationProgress: index / STAGES.length, leanSide: 'both', supportHint: 'unknown', timestamp: now, ...extra});\r\n  function invalid(now, reason) { samples = []; filtered = null; postureSince = null; resting = false; return result(now, {reason}); }\r\n  function coordinates(f) {\r\n    const depth = (f.h - anchors.high.h - model.coupling * (f.l - anchors.high.l)) / model.denominator;\r\n    // Below the verified middle anchor, interpolate to the separately\r\n    // observed resting center instead of extrapolating active depth/lean drift.\r\n    const restFraction = anchors.bottom && 1 - depth < anchors.mid.height\r\n      ? (anchors.mid.height - (1 - depth)) / (anchors.mid.height - anchors.bottom.height) : 0;\r\n    const center = restFraction ? anchors.mid.l + restFraction * (anchors.bottom.l - anchors.mid.l)\r\n      : anchors.high.l + depth * model.depthL;\r\n    const lateral = f.l - center;\r\n    return {height: 1 - depth, steer: lateral / (lateral >= 0 ? model.left : -model.right)};\r\n  }\r\n  function fit() {\r\n    const H = anchors.high, L = anchors.low, A = anchors.left, B = anchors.right;\r\n    const coupling = (A.h - B.h) / (A.l - B.l), depthL = L.l - H.l;\r\n    const denominator = L.h - H.h - coupling * depthL;\r\n    if (!Number.isFinite(coupling) || Math.abs(coupling) > T.maxLeanCoupling || Math.abs(denominator) < T.minHeightRange)\r\n      return false;\r\n    model = {coupling, depthL, denominator, left: 1, right: -1};\r\n    const residual = f => {\r\n      const d = (f.h - H.h - coupling * (f.l - H.l)) / denominator;\r\n      return f.l - H.l - d * depthL;\r\n    };\r\n    model.left = residual(A); model.right = residual(B);\r\n    return model.left >= T.minLeanRange && model.right <= -T.minLeanRange;\r\n  }\r\n  function leanLimit(height, steer) {\r\n    // The bottom extends below the old active-low coordinate (zero). Use the\r\n    // observed joint envelope rather than treating every negative old height as\r\n    // low-neutral: that would reject a valid bottom-to-middle diagonal, freeze\r\n    // the pace target there, and make following that target unable to recover.\r\n    const side = steer >= 0 ? 'left' : 'right', mixed = anchors[`mixed-${side}`];\r\n    const bottom = anchors.bottom.height, middle = mixed.height;\r\n    const midLean = Math.abs(mixed.steer);\r\n    const observed = height <= middle\r\n      ? midLean * clamp((height - bottom) / (middle - bottom), 0, 1)\r\n      : midLean + (1 - midLean) * clamp((height - middle) / (1 - middle), 0, 1);\r\n    return Math.max(.18, observed) + .15;\r\n  }\r\n  function stageReason(f) {\r\n    const S = stage(), H = anchors.high;\r\n    if (S === 'high') return '';\r\n    if (S === 'low') {\r\n      if (H.h - f.h < T.minHeightRange) return 'Lower a little within your comfortable range, then hold.';\r\n      if (Math.abs(f.l - H.l) > (H.h - f.h) * T.maxDepthDrift) return 'Keep your shoulders centered while lowering.';\r\n      return '';\r\n    }\r\n    if (S === 'left' || S === 'right') {\r\n      if (Math.abs(f.h - H.h) > (H.h - anchors.low.h) * .24) return 'Return to your comfortable high position before leaning.';\r\n      if (S === 'left' ? f.l - H.l < T.minLeanRange : H.l - f.l < T.minLeanRange)\r\n        return S === 'left' ? 'Lean toward your LEFT arm, keeping both hands down.' : 'Lean toward your RIGHT arm, keeping both hands down.';\r\n      return '';\r\n    }\r\n    const c = coordinates(f);\r\n    if (S === 'bottom') {\r\n      if ((anchors.mid.height - c.height) * Math.abs(model.denominator) < T.minRestRange)\r\n        return 'Rest fully down, below your measured middle position. Keep your hands still.';\r\n      if (Math.abs(f.l - anchors.mid.l) > T.maxSteerLeak * Math.min(model.left, -model.right))\r\n        return 'Center your shoulders between your hands for the resting position.';\r\n      return '';\r\n    }\r\n    if (Math.abs(c.height - .5) > .22) return 'Find a comfortable middle height and hold briefly.';\r\n    if (S === 'mid' && Math.abs(c.steer) > T.maxSteerLeak) return 'Center your shoulders at middle height.';\r\n    if (S === 'mixed-left' && (c.steer < .5 || c.steer > 1.25)) return 'At middle height, lean gently toward your LEFT arm.';\r\n    if (S === 'mixed-right' && (c.steer > -.5 || c.steer < -1.25)) return 'At middle height, lean gently toward your RIGHT arm.';\r\n    if (S !== 'mid' && Math.abs(c.height - anchors.mid.height) > T.maxHeightLeak)\r\n      return 'Keep the same middle height while leaning; ease the lean if needed.';\r\n    return '';\r\n  }\r\n  return {\r\n    reset, get stage() { return stage(); }, get calibrated() { return index === STAGES.length; },\r\n    update(detail = {}) {\r\n      const now = detail.now;\r\n      if (!Number.isFinite(now) || now < 0) return invalid(null, 'Waiting for a fresh camera frame.');\r\n      const key = `${detail.cameraGeneration ?? ''}/${detail.width}/${detail.height}`;\r\n      if (identity !== null && key !== identity) { clearCalibration(); lastTime = null; }\r\n      identity = key;\r\n      if (lastTime !== null && now <= lastTime) {\r\n        if (now < lastTime) clearCalibration();\r\n        return invalid(now, 'Waiting for a newer camera frame.');\r\n      }\r\n      const dt = lastTime === null ? 0 : now - lastTime; lastTime = now;\r\n      // An explicit portal bay permits moving arms away from the calibrated\r\n      // hands. Keep anchors, never control/credit; validate the original camera\r\n      // and hand baseline again on the first frame after leaving the bay.\r\n      if(detail.portalRest===true){filtered=null;postureSince=null;resting=false;samples=[];return result(now,{reason:'Portal recovery. Relax your hands; return into view when ready.'});}\r\n      if (dt > T.maxGapMs) {\r\n        if (dt > T.resetGapMs) clearCalibration();\r\n        return invalid(now, 'Camera paused. Hold steady while tracking returns.');\r\n      }\r\n      const g = geometry(detail, T);\r\n      if (g.reason) return invalid(now, g.reason);\r\n      if (!reference) reference = {w: g.w, t: g.t, n: g.n, scale: g.shoulderSpan, wristSpan: g.wristSpan};\r\n      const scale = g.shoulderSpan / reference.scale;\r\n      if (scale < T.minScaleRatio || scale > T.maxScaleRatio || length(sub(g.w, reference.w)) / reference.scale > T.wristDrift\r\n        || Math.abs(g.wristSpan / reference.wristSpan - 1) > .22 || dot(g.t, reference.t) < .97) {\r\n        clearCalibration(); return invalid(now, 'Camera or hands moved. Settle into position to recalibrate.');\r\n      }\r\n      const displacement = sub(g.s, reference.w);\r\n      const f = {h: -dot(displacement, reference.n) / reference.scale, l: dot(displacement, reference.t) / reference.scale, t: now};\r\n      if (!this.calibrated) {\r\n        const reason = stageReason(f);\r\n        if (reason) return invalid(now, reason);\r\n        // Preserve enough observations to satisfy minSamples even at the slowest\r\n        // accepted cadence. A duration-only window can otherwise stall calibration\r\n        // forever while every individual sample is considered fresh.\r\n        const calibrationWindow=Math.max(T.dwellMs,T.maxGapMs*(T.minSamples-1))+T.maxGapMs;\r\n        samples.push(f); samples = samples.filter(s => now - s.t <= calibrationWindow);\r\n        const h = median(samples.map(s => s.h)), l = median(samples.map(s => s.l));\r\n        if (samples.some(s => Math.abs(s.h - h) > T.noise || Math.abs(s.l - l) > T.noise)) {\r\n          samples = [f]; return result(now, {reason: 'Hold steady briefly so the camera can measure your range.'});\r\n        }\r\n        const elapsed = now - samples[0].t, progress = Math.min(1, elapsed / T.dwellMs);\r\n        if (elapsed >= T.dwellMs && samples.length >= T.minSamples) {\r\n          anchors[stage()] = {h, l, ...(model ? coordinates({h, l}) : {})};\r\n          if (stage() === 'right' && !fit()) { clearCalibration(); return invalid(now, 'Height and lean overlap too much. Adjust camera framing and try again.'); }\r\n          index++; samples = []; filtered = null; postureSince = null;\r\n        }\r\n        return result(now, {confidence: g.confidence, calibrationProgress: Math.min(1, (index + (samples.length ? progress : 0)) / STAGES.length),\r\n          reason: this.calibrated ? 'Calibration complete. Hold steady to begin.' : 'Measuring your comfortable range.'});\r\n      }\r\n      const c = coordinates(f);\r\n      if (c.height < anchors.bottom.height - T.maxBottomDrift / Math.abs(model.denominator)) {\r\n        clearCalibration(); return invalid(now, 'Your resting baseline changed. Settle your hands and recalibrate.');\r\n      }\r\n      if (c.height > 1.22 || Math.abs(c.steer) > 1.3)\r\n        return invalid(now, 'Return within your calibrated comfortable range.');\r\n      // Interpolate within observed bottom/mixed/high combinations; do not\r\n      // extrapolate a full side lean into the unobserved deep-rest corner.\r\n      if (Math.abs(c.steer) > leanLimit(c.height, c.steer))\r\n        return invalid(now, 'Ease toward the middle before lowering further.');\r\n      const restFraction = (c.height - anchors.bottom.height) / (anchors.mid.height - anchors.bottom.height);\r\n      resting = restFraction <= (resting ? T.restExitFraction : T.restEnterFraction);\r\n      const mappedHeight = c.height >= anchors.mid.height\r\n        ? .5 + .5 * (c.height - anchors.mid.height) / (1 - anchors.mid.height) : .5 * restFraction;\r\n      const raw = {height01: clamp(mappedHeight, 0, 1), steer: clamp(-c.steer, -1, 1)}; // image-right = player-left\r\n      if (Math.abs(raw.steer) <= T.deadband) raw.steer = 0;\r\n      const alpha = filtered ? 1 - Math.exp(-dt / T.smoothingMs) : 1;\r\n      filtered = filtered ? {height01: filtered.height01 + (raw.height01 - filtered.height01) * alpha,\r\n        steer: filtered.steer + (raw.steer - filtered.steer) * alpha} : raw;\r\n      const prone = proneEvidence(detail, g);\r\n      if (!prone || resting) postureSince = null; else postureSince ??= now;\r\n      const exerciseEligible = !resting && prone && now - postureSince >= T.postureDwellMs;\r\n      return result(now, {...filtered, controlValid: true, exerciseEligible, resting,\r\n        posture: resting ? 'rest' : prone ? 'active' : 'unknown', confidence: g.confidence,\r\n        leanSide: filtered.steer < -T.deadband ? 'left' : filtered.steer > T.deadband ? 'right' : 'both',\r\n        reason: resting ? 'Resting position estimated. Flight continues; exercise credit is paused.' : exerciseEligible ? '' : prone ? 'Hold steady while posture tracking settles.' : 'Control rehearsal only: show your hips and knees in a clear front-facing plank.'});\r\n    },\r\n  };\r\n}\r\n\r\n// One stable body observation replaces the optional guided range procedure.\r\n// The hand support line is a geometric floor estimate, never a contact sensor.\r\n// Arm-chain reach supplies a usable initial scale; ordinary higher poses can\r\n// expand it after a stable observation. No face point participates in this path.\r\nfunction createAutomaticPlankInput(options={}){\r\n  const T={...PLANK_TUNING,...(options.tuning??{}),minArmHeight:options.tuning?.minArmHeight??-.65};\r\n  let reference=null,samples=[],growth=[],floorSamples=[],restSamples=[],restAnchor=null,restFallbackActive=false,\r\n    lastTime=null,identity=null,filtered=null,postureSince=null,resting=false;\r\n  const clear=()=>{reference=null;samples=[];growth=[];floorSamples=[];restSamples=[];restAnchor=null;restFallbackActive=false;filtered=null;postureSince=null;resting=false;};\r\n  const reset=()=>{clear();lastTime=null;identity=null;};\r\n  const handsMoved=g=>length(sub(g.w,reference.w))/reference.scale>T.wristDrift\r\n    ||Math.abs(g.wristSpan/reference.wristSpan-1)>.22||dot(g.t,reference.t)<.97;\r\n  const result=(now,extra={})=>({height01:.5,steer:0,controlValid:false,exerciseEligible:false,resting:false,posture:'unknown',\r\n    confidence:0,reason:'Get into position.',stage:reference?'ready':'position',prompt:reference?'Push up to fly up. Lower to fly down. Lean left or right.':'Get into position.',\r\n    automatic:true,calibrated:!!reference,calibrationProgress:reference?1:0,leanSide:'both',supportHint:'unknown',timestamp:now,...extra});\r\n  const invalid=(now,reason)=>{samples=[];growth=[];floorSamples=[];restSamples=[];filtered=null;postureSince=null;resting=false;return result(now,{reason});};\r\n  function stable(buffer,sample,now,dwellMs=T.dwellMs){\r\n    buffer.push(sample);\r\n    const window=Math.max(dwellMs,T.maxGapMs*(T.minSamples-1))+T.maxGapMs;\r\n    while(buffer.length&&now-buffer[0].t>window)buffer.shift();\r\n    const h=median(buffer.map(s=>s.h)),l=median(buffer.map(s=>s.l));\r\n    if(buffer.some(s=>Math.abs(s.h-h)>T.noise||Math.abs(s.l-l)>T.noise)){buffer.splice(0,buffer.length,sample);return false;}\r\n    return buffer.length>=T.minSamples&&now-buffer[0].t>=dwellMs;\r\n  }\r\n  function fallbackPose(now,evidence,rawHeight=0){\r\n    const shouldersOnlyStart=evidence.source==='shoulders-startup';\r\n    const sample={h:shouldersOnlyStart?evidence.s.y:evidence.source==='shoulders'?rawHeight:evidence.evidence,\r\n      l:shouldersOnlyStart?evidence.span:(evidence.hipSpan??evidence.span)/evidence.span,\r\n      s:evidence.s,hip:evidence.hip??null,scale:evidence.span,t:now};\r\n    if(restAnchor){\r\n      const scaleRatio=evidence.span/restAnchor.scale;\r\n      // Lowering and rising move the torso vertically; that is not camera loss.\r\n      const centerDrift=Math.abs(evidence.s.x-restAnchor.s.x)/restAnchor.scale;\r\n      const hipDrift=evidence.hip&&restAnchor.hip?Math.abs(evidence.hip.x-restAnchor.hip.x)/restAnchor.scale:0;\r\n      if(scaleRatio<.72||scaleRatio>1.32||centerDrift>.22||hipDrift>.28){\r\n        clear();return invalid(now,'Tracking shifted while your arms were hidden. Center your body again.');\r\n      }\r\n    }else if(reference){\r\n      // An already tracked descent needs no second acquisition/countdown.\r\n      restAnchor={s:evidence.s,hip:evidence.hip??null,scale:evidence.span};\r\n    }else{\r\n      if(restSamples.some(s=>length(sub(s.s,evidence.s))/evidence.span>.08\r\n        ||s.hip&&evidence.hip&&length(sub(s.hip,evidence.hip))/evidence.span>.10))restSamples=[];\r\n      const dwell=shouldersOnlyStart?T.autoShouldersOnlyStartupDwellMs:T.autoRestFallbackDwellMs;\r\n      const ready=stable(restSamples,sample,now,dwell);\r\n      if(!ready)return result(now,{reason:shouldersOnlyStart?'Hold your shoulders steady briefly.':'Hold your chest-down rest briefly.',posture:'rest',resting:false,\r\n        restFallback:true,confidence:evidence.confidence});\r\n      const medianPoint=(key,rows=restSamples)=>({x:median(rows.map(s=>s[key].x)),y:median(rows.map(s=>s[key].y))});\r\n      const hipSamples=restSamples.filter(s=>s.hip);\r\n      restAnchor={s:medianPoint('s'),hip:hipSamples.length?medianPoint('hip',hipSamples):null,\r\n        scale:median(restSamples.map(s=>s.scale))};\r\n      restSamples=[];\r\n    }\r\n    restFallbackActive=true;resting=true;postureSince=null;filtered=null;\r\n    return result(now,{controlValid:true,height01:0,steer:0,exerciseEligible:false,resting:true,posture:'rest',\r\n      restFallback:true,confidence:evidence.confidence,reason:shouldersOnlyStart\r\n        ?'Arms are hidden. Keep your shoulders steady; this uncredited rest keeps the flight moving.'\r\n        :'Chest-down rest. Arm tracking can return when you are ready.'});\r\n  }\r\n  return {reset,get stage(){return reference?'ready':'position';},get calibrated(){return !!reference;},\r\n    update(detail={}){\r\n      const now=detail.now;\r\n      if(!Number.isFinite(now)||now<0)return invalid(null,'Waiting for a fresh camera frame.');\r\n      const key=`${detail.cameraGeneration??''}/${detail.width}/${detail.height}`;\r\n      if(identity!==null&&identity!==key){clear();lastTime=null;}identity=key;\r\n      if(lastTime!==null&&now<=lastTime){if(now<lastTime)clear();return invalid(now,'Waiting for a newer camera frame.');}\r\n      const dt=lastTime===null?0:now-lastTime;lastTime=now;\r\n      if(detail.portalRest===true)return invalid(now,'Portal recovery. Relax your hands; return into view when ready.');\r\n      if(dt>T.maxGapMs){if(dt>T.resetGapMs)clear();return invalid(now,'Get into position. Tracking is returning.');}\r\n      const g=geometry(detail,T),shoulders=shoulderTrack(detail,T,.45);\r\n      // Recognize the learned low torso before inspecting the hands: folded,\r\n      // hidden or relocated hands are normal while resting on the stomach.\r\n      if(reference&&shoulders){\r\n        const scale=shoulders.span/reference.scale;\r\n        const cameraShift=reference.lastS&&Math.abs(shoulders.s.x-reference.lastS.x)/reference.scale>.22;\r\n        if(scale>=T.minScaleRatio&&scale<=T.maxScaleRatio&&!cameraShift){\r\n          const bodyHeight=-dot(sub(shoulders.s,reference.w),reference.n)/reference.scale+(reference.heightOffset??0);\r\n          const rawHeight=clamp((bodyHeight-reference.floor)/Math.max(.1,reference.high-reference.floor),0,1);\r\n          const threshold=resting?T.autoRestExit:T.autoRestEnter;\r\n          if(rawHeight<=threshold&&(g.reason||restFallbackActive||handsMoved(g))){\r\n            return fallbackPose(now,{...shoulders,hip:null,hipSpan:shoulders.span,source:'shoulders',\r\n              confidence:shoulders.confidence},rawHeight);\r\n          }\r\n        }\r\n      }\r\n      if(g.reason){\r\n        if(!reference){\r\n          // Uncredited floor continuity can use partly occluded shoulders;\r\n          // normal gameplay geometry keeps the stricter visibility threshold.\r\n          const restShoulders=shoulders;\r\n          const floor=initialFloorPose(detail,T,restShoulders);\r\n          if(floor)return fallbackPose(now,floor);\r\n          const armIncomplete=restShoulders&&[13,14,15,16].some(i=>!restShoulders.clear(i));\r\n          if(armIncomplete)\r\n            return fallbackPose(now,{...restShoulders,hip:null,hipSpan:restShoulders.span,source:'shoulders-startup'},0);\r\n          if(restFallbackActive&&restAnchor&&restShoulders\r\n            &&restShoulders.span/restAnchor.scale>=.72&&restShoulders.span/restAnchor.scale<=1.32\r\n            &&Math.abs(restShoulders.s.x-restAnchor.s.x)/restAnchor.scale<=.22){\r\n            return fallbackPose(now,{...restShoulders,hip:null,hipSpan:restShoulders.span,source:'shoulders'},0);\r\n          }\r\n        }\r\n        if(restFallbackActive)clear();\r\n        return invalid(now,g.reason);\r\n      }\r\n      if(restFallbackActive){\r\n        const scale=g.shoulderSpan/(reference?.scale??restAnchor?.scale??g.shoulderSpan);\r\n        const priorS=reference?.lastS??restAnchor?.s;\r\n        if(scale<T.minScaleRatio||scale>T.maxScaleRatio||priorS&&Math.abs(g.s.x-priorS.x)/(reference?.scale??restAnchor.scale)>.30){\r\n          clear();return invalid(now,'Tracking shifted while your arms were hidden. Center your body again.');\r\n        }\r\n        if(reference){\r\n          const priorHeight=-dot(sub(g.s,reference.w),reference.n)/reference.scale+(reference.heightOffset??0);\r\n          const reacquiredHeight=-dot(sub(g.s,g.w),g.n)/g.shoulderSpan;\r\n          reference.heightOffset=priorHeight-reacquiredHeight;reference.w=g.w;reference.t=g.t;reference.n=g.n;\r\n          reference.scale=g.shoulderSpan;reference.wristSpan=g.wristSpan;\r\n          reference.neutral=dot(sub(g.s,g.w),g.t)/g.shoulderSpan;reference.lastS=g.s;\r\n          filtered=null;restFallbackActive=false;restAnchor=null;restSamples=[];\r\n        }\r\n        // Without a full-arm reference, keep confirmed rest actionable while\r\n        // ordinary acquisition runs below; do not replace it with invalid input.\r\n      }\r\n      const displacement=sub(g.s,g.w),h=-dot(displacement,g.n)/g.shoulderSpan,l=dot(displacement,g.t)/g.shoulderSpan;\r\n      if(!reference){\r\n        const reach=([ [11,13,15],[12,14,16] ].reduce((sum,[s,e,w])=>sum+length(sub(g.pt(s),g.pt(e)))+length(sub(g.pt(e),g.pt(w))),0)/2)/g.shoulderSpan;\r\n        // Visible contradictory posture is not a ready exercise position. Low\r\n        // shoulder height may be intentional floor rest; it remains uncredited.\r\n        if(LOWER.every(g.clear)&&h>T.autoMinReach*T.autoRestExit&&!proneEvidence(detail,g))\r\n          return invalid(now,'Get into a plank position with your hands in view.');\r\n        const sample={h,l,reach,w:g.w,scale:g.shoulderSpan,wrists:g.wristSpan,t:now};\r\n        const ready=stable(samples,sample,now);\r\n        const progress=Math.min(1,(now-samples[0].t)/T.dwellMs);\r\n        // Do not accept stable shoulder coordinates while the hands/camera move.\r\n        if(samples.some(s=>length(sub(s.w,g.w))/g.shoulderSpan>T.noise||Math.abs(s.scale/g.shoulderSpan-1)>.05)){\r\n          samples=[sample];return result(now);\r\n        }\r\n        if(!ready){\r\n          if(restFallbackActive&&restAnchor){\r\n            const shoulders=shoulderTrack(detail,T);\r\n            if(shoulders&&shoulders.span/restAnchor.scale>=.72&&shoulders.span/restAnchor.scale<=1.32\r\n              &&Math.abs(shoulders.s.x-restAnchor.s.x)/restAnchor.scale<=.22)\r\n              return fallbackPose(now,{...shoulders,hip:null,hipSpan:shoulders.span,source:'shoulders'},0);\r\n            clear();return invalid(now,'Tracking shifted while your arms were hidden. Center your body again.');\r\n          }\r\n          return result(now,{calibrationProgress:progress,confidence:g.confidence});\r\n        }\r\n        const height=median(samples.map(s=>s.h)),floor=Math.min(0,height);\r\n        reference={w:g.w,t:g.t,n:g.n,scale:median(samples.map(s=>s.scale)),wristSpan:median(samples.map(s=>s.wrists)),\r\n          neutral:median(samples.map(s=>s.l)),floor,high:Math.max(T.autoMinReach,median(samples.map(s=>s.reach))*.95,height),heightOffset:0,lastS:g.s};\r\n        samples=[];filtered=null;postureSince=null;\r\n        if(restFallbackActive)return fallbackPose(now,{s:g.s,span:g.shoulderSpan,confidence:g.confidence,source:'shoulders'},0);\r\n        restAnchor=null;restSamples=[];\r\n        return result(now,{reason:'Ready.',confidence:g.confidence});\r\n      }\r\n      const scale=g.shoulderSpan/reference.scale;\r\n      if(scale<T.minScaleRatio||scale>T.maxScaleRatio||handsMoved(g)){\r\n        clear();return invalid(now,'Get into position. Your hand position changed.');\r\n      }\r\n      const delta=sub(g.s,reference.w),bodyHeight=-dot(delta,reference.n)/reference.scale+(reference.heightOffset??0),\r\n        lean=dot(delta,reference.t)/reference.scale-reference.neutral;\r\n      reference.lastS=g.s;\r\n      // Expand only from a stable, centered natural rise, not one noisy frame or\r\n      // a leaning/foreshortened arm. The mapping never shrinks while lowering.\r\n      const prone=proneEvidence(detail,g);\r\n      if(prone&&bodyHeight>reference.high+T.noise&&Math.abs(lean)<T.autoLeanRange*.4){\r\n        if(stable(growth,{h:bodyHeight,l:lean,t:now},now)){reference.high=median(growth.map(s=>s.h));growth=[];}\r\n      }else growth=[];\r\n      const span=Math.max(.1,reference.high-reference.floor),rawHeight=clamp((bodyHeight-reference.floor)/span,0,1);\r\n      const restingThreshold=resting?T.autoRestExit:T.autoRestEnter;\r\n      resting=rawHeight<=restingThreshold;\r\n      // A stable, visibly prone position below the initial hand-line estimate\r\n      // teaches a lower floor baseline instead of repeatedly invalidating input.\r\n      if(prone&&bodyHeight<reference.floor){\r\n        if(stable(floorSamples,{h:bodyHeight,l:lean,t:now},now,T.autoFloorSettleMs)){\r\n          const floor=median(floorSamples.map(s=>s.h));if(floor<reference.floor)reference.floor=floor;floorSamples=[];\r\n        }\r\n      }else floorSamples=[];\r\n      // Auto mode has no guided neutral midpoint: gain the whole floor-to-high\r\n      // operating range so small lifts off the hands produce visible response.\r\n      const height=clamp(rawHeight*T.autoVerticalGain,0,1);\r\n      const raw={height01:height,steer:clamp(-lean/T.autoLeanRange,-1,1)};\r\n      if(Math.abs(raw.steer)<T.deadband)raw.steer=0;\r\n      const alpha=filtered?1-Math.exp(-dt/T.smoothingMs):1;\r\n      filtered=filtered?{height01:filtered.height01+(raw.height01-filtered.height01)*alpha,steer:filtered.steer+(raw.steer-filtered.steer)*alpha}:raw;\r\n      const canCredit=prone&&!resting&&rawHeight>=T.autoCreditHeight;\r\n      if(!canCredit)postureSince=null;else postureSince??=now;\r\n      const exerciseEligible=canCredit&&now-postureSince>=T.postureDwellMs;\r\n      return result(now,{...filtered,controlValid:true,exerciseEligible,resting,posture:resting?'rest':prone?'active':'unknown',confidence:g.confidence,\r\n        leanSide:filtered.steer<-T.deadband?'left':filtered.steer>T.deadband?'right':'both',\r\n        reason:resting?'Resting. Exercise credit is paused.':exerciseEligible?'':canCredit?'Hold steady.':prone?'Low position. Exercise credit is paused.':'Show your hips and knees for exercise credit.'});\r\n    }\r\n  };\r\n}\r\n","arcade/plank-flight/presentation-recipes.mjs":"// Decorative compositions only. The straight playable lane remains |x| < 2.55.\r\n// Large faces, bracing and real silhouettes carry recognition at phone size.\r\nconst part=(shape,material,x,y,z,sx,sy,sz,rz=0,ry=0)=>({shape,material,x,y,z,sx,sy,sz,rz,ry});\r\nconst box=(...a)=>part('box',...a), ball=(...a)=>part('sphere',...a);\r\nconst recipes={};\r\nfunction crate(a,x,y,z,s=1.8){\r\n a.push(box('wood',x,y,z,s,s,s));\r\n for(const dy of [-.43,.43])a.push(box('timber',x,y+dy*s,z+s*.51,s,.12*s,.09*s));\r\n for(const dx of [-.43,.43])a.push(box('timber',x+dx*s,y,z+s*.51,.12*s,s,.09*s));\r\n for(const r of [-Math.PI/4,Math.PI/4])a.push(box('timber',x,y,z+s*.56,s*1.15,.10*s,.08*s,r));\r\n}\r\nfunction hill(a,x,y,z,w,h,d){\r\n a.push(box('checker',x,y,z,w,h,d),box('grass',x,y+h/2+.14,z,w+.15,.28,d+.15));\r\n}\r\nfunction cabinet(a,x,z){\r\n a.push(box('panel',x,2.4,z,2.1,4.8,1.6),box('dark',x,2.9,z+.84,1.55,1.7,.10),box('screen',x,3,z+.91,1.3,1.3,.04));\r\n for(let k=0;k<4;k++)a.push(box('light',x-.42+k*.28,1.6,z+.91,.15,.12,.08));\r\n for(let k=0;k<3;k++)a.push(box('dark',x,.5+k*.2,z+.85,1.5,.08,.08));\r\n}\r\nfunction tank(a,x,z,experiment=false){\r\n a.push(part('cylinder','glass',x,2.25,z,.72,3.4,.72),part('cylinder','metal',x,.55,z,.86,.35,.86),part('cylinder','metal',x,3.95,z,.86,.35,.86));\r\n for(const dx of [-.65,.65])a.push(box('metal',x+dx,2.25,z+.35,.12,3.4,.12));\r\n if(experiment)a.push(ball('fruit',x,2.3,z+.45,.42,.75,.42));\r\n else for(let k=0;k<3;k++)a.push(box('screen',x,1.4+k*.65,z+.73,.38,.35,.06));\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.tropical??=[];recipes.tropical.push(a);\r\n crate(a,-3.65,1,-5,1.9);crate(a,3.75,.9,-12,1.7);\r\n if(s===0){crate(a,-5.3,1,-6,1.9);crate(a,-4.5,2.9,-6,1.7);a.push(box('wood',-4.5,.12,-8,3.8,.24,8));}\r\n if(s===1){for(let k=0;k<3;k++)a.push(box('sandstone',4.5+k*.45,.6+k*.9,-7-k*.6,3.4-k*.4,1.2,5-k*.6));a.push(ball('rock',-5.1,2,-12,2,2.2,2),ball('leaf',-5.1,4,-12,2.1,.45,2));}\r\n if(s===2){crate(a,4.6,2.65,-12,1.7);crate(a,5.4,.9,-12,1.7);crate(a,-3.1,3.5,-9,1.05);a.push(box('wood',-2.8,3.5,-9,.3,7,.4),box('timber',-3.55,6.7,-9,1.5,.3,.4),box('dark',-3.1,5.35,-9,.07,2.7,.07));}\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.castle??=[];recipes.castle.push(a);\r\n for(const side of [-1,1]){\r\n  const x=side*4.2;\r\n  a.push(box('masonry',x,2.6,-7,2.9,5.2,3),box('stoneTrim',x,5.1,-7,3.2,.4,3.3));\r\n  for(let k=0;k<3;k++)a.push(box('masonry',x+(k-1)*1.05,5.65,-6, .65,.9,1));\r\n  a.push(box('lava',side*3.9,-.02,-6,2.3,.12,12));\r\n  if(s===0){a.push(box('wood',x,2.6,-5.42,1.8,3.5,.16));for(let k=0;k<5;k++)a.push(box('metal',x-.65+k*.33,2.6,-5.28,.09,3.5,.12));}\r\n  if(s===1){a.push(box('dark',x,2.4,-5.4,1.7,3,.15),box('lava',x,2.3,-5.29,1.25,2.4,.1));for(let k=0;k<4;k++)a.push(box('metal',x-.6+k*.4,2.3,-5.16,.14,2.8,.1));}\r\n  if(s===2){a.push(part('cylinder','masonry',x,6.2,-7,.8,2.8,.8),part('cone','lava',x,7.8,-7,.65,1.4,.65));for(let k=0;k<3;k++)a.push(box('lava',x,1.3+k*.9,-5.4,1.7,.28,.1));}\r\n }\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.rainbow??=[];recipes.rainbow.push(a);\r\n for(const side of [-1,1]){\r\n  if(s!==2)for(let k=0;k<4;k++)a.push(ball('cloud',side*(5.3+k*.65),-.9+(k%2)*.4,-8-k,1.7,1,2.3));\r\n  if(s!==1){a.push(part('octa','glass',side*4.7,2.8,-11,.9,3.1,.9));if(s===2)a.push(part('octa','light',side*6.2,4.2,-14,1.4,5,1.4));}\r\n }\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.loopway??=[];recipes.loopway.push(a);\r\n hill(a,-4.6,1.3,-8,3.9,2.6,7);hill(a,5.1,2,-13,4.6,4,6);\r\n if(s===0){\r\n  // A thick, stationary earth/turf loop beside the route, never a gold pickup ring.\r\n  a.push(part('earthLoop','checker',-7.6,4.3,-18,3.8,3.8,1.8),part('turfLoop','grass',-7.6,4.3,-18,3.8,3.8,1.8));\r\n }\r\n if(s===1)for(const [x,z] of [[-4.6,-8],[5.1,-13]]){a.push(part('cylinder','wood',x,4,z,.28,3,.28));for(let k=0;k<3;k++)a.push(ball('leaf',x+(k-1)*.65,5.6+(k%2)*.5,z,1.25,1.25,1.1));a.push(ball('fruit',x-.65,5.2,z+1,.28,.3,.28));}\r\n if(s===2){hill(a,-5.3,3.1,-11,3.6,3.5,5);hill(a,6.3,4.4,-15,4.8,4.8,6);a.push(part('earthLoop','checker',6.7,7.8,-24,3.1,3.1,1.4),part('turfLoop','grass',6.7,7.8,-24,3.1,3.1,1.4));}\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.laboratory??=[];recipes.laboratory.push(a);\r\n cabinet(a,-3.8,-9);tank(a,3.55,-7,s===1);\r\n if(s===0){tank(a,5.3,-10);a.push(box('metal',4.3,4.5,-8,3.4,.3,1.2));}\r\n if(s===1){tank(a,-5.6,-13,true);a.push(box('panel',4.5,.2,-9,3.8,.4,5));}\r\n if(s===2){a.push(box('metal',-4,4.8,-5,2.5,.6,7),box('metal',3.9,4.8,-11,2.5,.6,7));for(let k=0;k<4;k++)a.push(box('dark',-4,4.8,-3-k*1.4,2.6,.66,.16));a.push(box('panel',4,2.1,-13,2,4.2,.5,.22));}\r\n}\r\nfor(let s=0;s<3;s++){\r\n const a=[];recipes.asteroid??=[];recipes.asteroid.push(a);\r\n a.push(ball('rock',-5.2,3.8,-12,2.4,2.6,2),ball('rock',5.6,.3,-19,2.8,1.6,3),part('octa','stoneTrim',-4.5,-1,-5,1.5,.8,1.4));\r\n if(s===0){\r\n  // A dense, irregular rockfield frames the route with warm stone silhouettes.\r\n  for(const [x,y,z,sx,sy,sz] of [[-6.7,1.8,-8,2.4,2.8,2],[-7.1,4.8,-15,2.8,3.4,2.5],[7.2,3,-10,2.6,3.2,2.2],[6.8,5.7,-18,3.1,2.7,2.7]])a.push(ball('rock',x,y,z,sx,sy,sz));\r\n  a.push(part('octa','stoneTrim',-5.1,4.4,-7.5,1.3,4.2,2.2),part('octa','stoneTrim',5.1,4.2,-8,1.3,4,2.2),part('octa','dark',-5.1,1.25,-9.5,1.5,1.7,2),part('octa','dark',5.2,1.35,-10,1.5,1.6,2));\r\n }\r\n if(s===1){\r\n  // Broken orbital machinery: thick, offset struts and snapped hubs in cool metal.\r\n  for(let k=0;k<8;k++){const ang=k*Math.PI/4,x=Math.cos(ang)*7,y=3+Math.sin(ang)*7,z=-24-(k%3)*2;a.push(box('metal',x,y,z,.85,4.8,1.25,-ang),part('octa','rock',x,y-1.4,z-.65,1.5,1.15,1.4));}\r\n  for(const side of [-1,1]){a.push(box('panel',side*7.1,5.1,-31,2.2,1.5,3.5,.18*side),box('metal',side*8.2,2.4,-28,1.1,4.2,1.3,-.3*side));}\r\n }\r\n if(s===2){\r\n  // A broad, broken hull silhouette with a dark opening and exposed hot-pink plating.\r\n  a.push(box('panel',-5.15,6.2,-13,4.2,3.2,7,-.16,-.12),box('metal',-5.15,8,-13,4.6,.55,7,-.16,-.12),box('dark',-5.1,5.9,-9.25,2.5,2.1,.16,-.16,-.12));\r\n  for(let k=0;k<4;k++)a.push(box(k%2?'panel':'metal',-5.15+(k-1.5)*1.1,4.15,-12-k*1.3,.95,1.35,4.4,-.16,-.12));\r\n  a.push(box('dark',-7.65,6.2,-13, .8,3.6,7,-.16,-.12),box('dark',-2.65,6.2,-13,.8,3.6,7,-.16,-.12),box('panel',5.25,4.5,-16,2.7,2.4,5,.2,.18),box('metal',5.25,5.9,-16,3,.45,5,.2,.18));\r\n }\r\n}\r\nexport function sectionRecipe(world,section){return recipes[world]?.[Math.max(0,Math.min(2,Math.floor(Number(section)||0)))]??[];}\r\nexport function addSectionRecipe({THREE,parent,recipe,geometry,materials}){\r\n const groups=new Map();\r\n for(const p of recipe){if(!materials[p.material])continue;const key=`${p.shape}:${p.material}`;if(!groups.has(key))groups.set(key,[]);groups.get(key).push(p);}\r\n const constructors={box:['BoxGeometry',1,1,1],sphere:['IcosahedronGeometry',1,1],cone:['ConeGeometry',1,1,8],cylinder:['CylinderGeometry',1,1,1,12],octa:['OctahedronGeometry',1,0],earthLoop:['TorusGeometry',1,.23,8,40],turfLoop:['TorusGeometry',1.24,.055,6,40]};\r\n const helper=new THREE.Object3D();\r\n for(const parts of groups.values()){\r\n  const p=parts[0],mesh=new THREE.InstancedMesh(geometry(...constructors[p.shape]),materials[p.material],parts.length);mesh.name=`section-landmark-${p.material}-${p.shape}`;mesh.frustumCulled=false;\r\n  parts.forEach((p,i)=>{helper.position.set(p.x,p.y,p.z);helper.rotation.set(0,p.ry||0,p.rz||0);helper.scale.set(p.sx,p.sy,p.sz);helper.updateMatrix();mesh.setMatrixAt(i,helper.matrix);});mesh.instanceMatrix.needsUpdate=true;parent.add(mesh);\r\n }\r\n}\r\n","arcade/plank-flight/progression.mjs":"export const FLIGHT_PROGRESSION=Object.freeze({startSpeed:5,maxSpeed:15,rampSeconds:180});\r\nexport function flightSpeedAt(seconds){\r\n const u=Math.max(0,Math.min(1,seconds/FLIGHT_PROGRESSION.rampSeconds));\r\n return Math.max(5,Math.min(15,5+10*u*u*(3-2*u)));\r\n}\r\n// Integral of smoothstep speed, continuous in value and slope at both ends.\r\nexport function flightDistanceAt(seconds){\r\n const t=Math.max(0,seconds),u=Math.min(1,t/180);\r\n return 5*Math.min(t,180)+1800*(u*u*u-.5*u*u*u*u)+15*Math.max(0,t-180);\r\n}\r\nexport function flightTravelBetween(start,end){return flightDistanceAt(end)-flightDistanceAt(start);}\r\n","arcade/plank-flight/sim.mjs":"import {flightSpeedAt,flightTravelBetween} from './progression.mjs';\r\nimport {GADGETS, POWERUPS, UPGRADES, VEHICLES, vehicleById} from './catalog.mjs';\r\nimport {createPaceSchedule,paceAt,segmentAt} from './pacing.mjs';\r\nimport {BIOMES,BIOME_SECONDS,PORTAL_REST_SECONDS,biomeAt} from './biomes.mjs';\r\n\r\nexport const PACED_FLIGHT_TUNING=Object.freeze({speed:15,verticalSpeed:1.35,lateralSpeed:1.25,gateLeadSeconds:.45,lookaheadSeconds:20});\n// Optional coin-chase collision tuning; values are proposals, not balance claims.\nexport const COIN_CHASE_TUNING_PROPOSAL=Object.freeze({hitSlowSeconds:4,speedFactor:.55});\n\r\n/**\r\n * Deterministic, renderer-independent Plank Flight rules.\r\n * Coordinates use x [-1, 1], y [0, 1], and increasing absolute z ahead of the player.\r\n * Simulation time is game time only; callers own workout credit and pause/countdown policy.\r\n */\r\nexport const FLIGHT_TUNING = Object.freeze({\r\n  fixedStep: 1 / 60,\r\n  maxFrameDelta: 0.10,\r\n  baseSpeed: 5.2,\r\n  speedPerLevel: 0.08,\r\n  maxSpeed: 5.5,\r\n  levelDistance: 140,\r\n  firstGateDistance: 32,\r\n  gateSpacingMin: 34,\r\n  gateSpacingJitter: 4,\r\n  spawnAhead: 270,\r\n  safeHalfWidth: 0.42,\r\n  safeHalfHeight: 0.34,\r\n  warningLead: 18,\r\n  playerDepth: 0.45,\r\n  graceSeconds: 0.85,\r\n  baseLateralSpeed: 0.82,\r\n  baseVerticalSpeed: 0.62,\r\n  horizontalAcceleration: 3.2,\r\n  verticalAcceleration: 3.4,\r\n  missileWarning: 28,\r\n  missileLock: 14,\r\n  missileLaunch: 7,\r\n  missileRadiusX: 0.20,\r\n  missileRadiusY: 0.18,\r\n  maxObjects: 900\r\n});\r\n\r\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\r\nconst finite = value => typeof value === 'number' && Number.isFinite(value);\r\nconst moveToward = (value, target, delta) => value < target ? Math.min(target, value + delta) : Math.max(target, value - delta);\r\nconst clone = value => ({...value});\r\n\r\n/** Small deterministic PRNG (Mulberry32). A run never depends on renderer frame rate. */\r\nexport function seededRandom(seed, restoredState) {\n  let state = restoredState === undefined ? (Number(seed) >>> 0) || 1 : restoredState;\n  if (!Number.isSafeInteger(state) || state < 0 || state > 0xffffffff) throw new TypeError('Invalid random state');\n  const random = () => {\n    state = (state + 0x6D2B79F5) >>> 0;\n    let t = state;\n    t = Math.imul(t ^ (t >>> 15), t | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n  Object.defineProperty(random, 'state', {get: () => state});\n  return random;\n}\n\nconst CHECKPOINT_NUMBER_TAG = '__flightNumber';\nconst ALLOWED_NEGATIVE_INFINITY = new Set(['lastCashDashCoinAt','lastCycle','lastFold','lastFlap']);\nfunction encodeCheckpointValue(value, key = '') {\n  if (typeof value === 'number' && !Number.isFinite(value)) {\n    if (value !== -Infinity || !ALLOWED_NEGATIVE_INFINITY.has(key)) throw new TypeError('Invalid Plank Flight checkpoint number');\n    return {[CHECKPOINT_NUMBER_TAG]: '-infinity'};\n  }\n  if (Array.isArray(value)) return value.map(child => encodeCheckpointValue(child));\n  if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, encodeCheckpointValue(child, childKey)]));\n  return value;\n}\nfunction decodeCheckpointValue(value) {\n  if (Array.isArray(value)) return value.map(decodeCheckpointValue);\n  if (value && typeof value === 'object') {\n    const keys = Object.keys(value);\n    if (keys.length === 1 && keys[0] === CHECKPOINT_NUMBER_TAG) {\n      if (value[CHECKPOINT_NUMBER_TAG] === '-infinity') return -Infinity;\n      throw new TypeError('Invalid Plank Flight checkpoint number');\n    }\n    return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, decodeCheckpointValue(child)]));\n  }\n  return value;\n}\n\n/** Capture the simulation's mutable state, deterministic RNG cursor and collections. */\nexport function snapshotFlightCheckpoint(run) {\n  if (!run || typeof run.random !== 'function' || !Number.isSafeInteger(run.random.state)) throw new TypeError('Invalid Plank Flight state');\n  const state = Object.fromEntries(Object.entries(run).filter(([key]) => key !== 'random'));\n  state.collectedIds = [...run.collectedIds];\n  state.eventIds = [...run.eventIds];\n  state.gateGroups = [...run.gateGroups.entries()];\n  state.warnedGroups = [...run.warnedGroups];\n  return {version: 1, randomState: run.random.state, state: encodeCheckpointValue(state)};\n}\n\n/** Restore a checkpoint without regenerating course state or consuming RNG draws. */\nexport function restoreFlightCheckpoint(checkpoint) {\n  if (!checkpoint || checkpoint.version !== 1 || !Number.isSafeInteger(checkpoint.randomState) || checkpoint.randomState < 0 || checkpoint.randomState > 0xffffffff\n      || !checkpoint.state || typeof checkpoint.state !== 'object' || Array.isArray(checkpoint.state)) throw new TypeError('Invalid Plank Flight checkpoint');\n  const state = decodeCheckpointValue(structuredClone(checkpoint.state));\n  const validateNumbers = (value, key = '') => {\n    if (typeof value === 'number') {\n      if (Number.isFinite(value)) return;\n      if (value === -Infinity && ALLOWED_NEGATIVE_INFINITY.has(key)) return;\n      throw new TypeError('Invalid Plank Flight checkpoint number');\n    }\n    if (['x','y','z','w','h','radius'].includes(key) && value !== undefined && !Number.isFinite(value))\n      throw new TypeError('Invalid Plank Flight checkpoint coordinate');\n    if (Array.isArray(value)) return value.forEach(child => validateNumbers(child));\n    if (value && typeof value === 'object') Object.entries(value).forEach(([childKey, child]) => validateNumbers(child, childKey));\n  };\n  validateNumbers(state);\n  const strings = values => Array.isArray(values) && values.every(value => typeof value === 'string') && new Set(values).size === values.length;\n  if (typeof state.runId !== 'string' || !state.runId || !Number.isSafeInteger(state.seed) || state.seed < 1 || state.seed > 0xffffffff\n      || !['ready','flying','paused','crashed','portal','finalized'].includes(state.phase)\n      || !Array.isArray(state.objects) || state.objects.length > FLIGHT_TUNING.maxObjects\n      || !Array.isArray(state.events) || !Array.isArray(state.missions) || !Array.isArray(state.safeRoute)\n      || !strings(state.collectedIds) || !strings(state.eventIds) || !strings(state.warnedGroups)\n      || !Array.isArray(state.gateGroups) || state.gateGroups.some(entry => !Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== 'string' || !entry[1] || typeof entry[1] !== 'object')\n      || new Set(state.gateGroups.map(entry => entry[0])).size !== state.gateGroups.length) throw new TypeError('Invalid Plank Flight checkpoint state');\n  const requiredNumbers = ['time','distance','coins','score','level','hits','continuation','x','y','vx','vy','speed','entitySequence','eventSequence','simTick','accumulator','graceUntil','nextPatternAt','patternIndex','componentParts'];\n  if (requiredNumbers.some(key => !Number.isFinite(state[key])) || typeof state.paced !== 'boolean' || typeof state.biomes !== 'boolean' || typeof state.coinChase !== 'boolean')\n    throw new TypeError('Invalid Plank Flight checkpoint state');\n  return {...state, random: seededRandom(state.seed, checkpoint.randomState), collectedIds: new Set(state.collectedIds), eventIds: new Set(state.eventIds),\n    gateGroups: new Map(state.gateGroups), warnedGroups: new Set(state.warnedGroups)};\n}\n\r\nconst gadgetById = new Map(GADGETS.map(gadget => [gadget.id, gadget]));\r\nconst powerupById = new Map(POWERUPS.map(powerup => [powerup.id, powerup]));\r\nconst upgradeById = new Map(UPGRADES.map(upgrade => [upgrade.id, upgrade]));\r\n\r\nfunction normalizedLoadout(loadout, event) {\r\n  const unique = [...new Set(Array.isArray(loadout) ? loadout.filter(id => typeof id === 'string') : [])];\r\n  return unique.filter(id => {\r\n    if (gadgetById.has(id)) return !(event && gadgetById.get(id).eventBlocked);\r\n    return Boolean(vehicleById(id));\r\n  });\r\n}\r\n\r\nfunction normalizeUpgrades(upgrades) {\r\n  const levels={};\r\n  if (!upgrades || typeof upgrades!=='object') return levels;\r\n  for (const [id,raw] of Object.entries(upgrades)) {\r\n    const item=upgradeById.get(id); if (!item) continue;\r\n    const level=Array.isArray(raw) ? raw.length : Number(raw);\r\n    if (Number.isFinite(level) && level>0) levels[id]=clamp(Math.floor(level),0,item.maxLevel);\r\n  }\r\n  return levels;\r\n}\r\n\r\nfunction effectsFor(loadout, upgrades={}) {\r\n  const effects = {magnetRadius:0,verticalResponse:1,initialShield:0,maxShield:1,impactBuffer:0,warningLead:0,coinBonus:0,crashGrace:0,initialVehicle:null,powerupDuration:0,vehicleDuration:0,coinPreview:0,eventTokenChance:0,componentChance:0,npcBonus:0};\r\n  for (const id of loadout) {\r\n    const vehicle = vehicleById(id);\r\n    if (vehicle) { effects.initialVehicle ||= vehicle.id; continue; }\r\n    const effect = gadgetById.get(id)?.effect;\r\n    if (!effect) continue;\r\n    for (const [key, value] of Object.entries(effect)) {\r\n      if (key === 'initialVehicle') effects.initialVehicle = value;\r\n      else if (finite(value)) effects[key] = (effects[key] || 0) + value;\r\n    }\r\n  }\r\n  for (const [id,level] of Object.entries(normalizeUpgrades(upgrades))) {\r\n    const item=upgradeById.get(id);\r\n    for (const [key,perLevel] of Object.entries(item.effectPerLevel)) effects[key]=(effects[key]||0)+perLevel*level;\r\n  }\r\n  effects.magnetRadius=clamp(effects.magnetRadius,0,0.75);\r\n  effects.verticalResponse=clamp(effects.verticalResponse,0.85,1.35);\r\n  effects.warningLead=clamp(effects.warningLead,0,10);\r\n  effects.maxShield=clamp(effects.maxShield,1,3);\r\n  effects.vehicleDuration=clamp(effects.vehicleDuration,0,6);\r\n  return effects;\r\n}\r\n\r\nfunction vehicleDuration(run,id) { return (VEHICLES.find(vehicle=>vehicle.id===id)?.duration ?? 20)+run.effects.vehicleDuration; }\r\nfunction vehicleHealthFor(id) { return ['foundry-walker','triad-bulwark'].includes(id) ? 2 : id ? 1 : 0; }\r\n\r\nconst MISSION_POOL = Object.freeze([\r\n  {id:'coin-comet',name:'Catch the Comets',kind:'coins',target:8},\r\n  {id:'long-orbit',name:'Hold Your Course',kind:'distance',target:180},\r\n  {id:'pickup-trail',name:'Collect the Trail',kind:'pickups',target:5},\r\n  {id:'clean-flight',name:'Clear the Gates',kind:'clean',target:4},\r\n  {id:'vehicle-window',name:'Ride the Shell',kind:'vehicleTime',target:4}\r\n]);\r\n\r\nfunction missionList(seed) {\r\n  const start=(seed>>>0)%MISSION_POOL.length;\r\n  return Array.from({length:3},(_,index)=>{\r\n    const mission=MISSION_POOL[(start+index)%MISSION_POOL.length];\r\n    return {...mission,stars:index+1,progress:0,complete:false};\r\n  });\r\n}\r\n\r\n/** Create a fresh mutable run with reproducible IDs and seeded course generation. */\r\nexport function createFlight({seed=1,runId,loadout=[],upgrades={},vehicle=null,cosmetic=null,event=false,paced=false,biomes=false,progressive=false,coinChase=false}={}) {\n  const normalizedSeed = (Number(seed) >>> 0) || 1;\r\n  const normalizedRunId = typeof runId === 'string' && runId ? runId : `flight-${normalizedSeed}`;\r\n  const acceptedLoadout = normalizedLoadout(loadout, Boolean(event));\r\n  const acceptedUpgrades=normalizeUpgrades(upgrades);\r\n  const effects = effectsFor(acceptedLoadout,acceptedUpgrades);\r\n  const startingVehicle = vehicleById(vehicle)?.id || (vehicle === 'triad-bulwark' ? vehicle : null) || effects.initialVehicle;\r\n  const run = {\r\n    random:seededRandom(normalizedSeed), runId:normalizedRunId, seed:normalizedSeed, event:Boolean(event), coinChase:coinChase===true, slowUntil:0, phase:'ready', time:0, distance:0, coins:0, score:0, level:0, hits:0,\n    continuation:0, x:0, y:0.5, vx:0, vy:0, speed:FLIGHT_TUNING.baseSpeed, objects:[], events:[], vehicle:startingVehicle,\r\n    explicitVehicle:vehicleById(vehicle)?.id || (vehicle === 'triad-bulwark' ? vehicle : null),\r\n    vehicleUntil:startingVehicle ? vehicleDuration({effects},startingVehicle) : 0,\r\n    vehicleHealth:vehicleHealthFor(startingVehicle), shield:effects.initialShield, powerup:null, missions:missionList(normalizedSeed),\r\n    loadout:acceptedLoadout, upgrades:acceptedUpgrades, cosmetic, effects, collectedIds:new Set(), eventIds:new Set(), entitySequence:0, eventSequence:0,\r\n    simTick:0, accumulator:0, graceUntil:0, nextPatternAt:FLIGHT_TUNING.firstGateDistance, patternIndex:0,\r\n    safeRoute:[], gateGroups:new Map(), warnedGroups:new Set(), componentParts:0, componentMechDeployed:false,\r\n    eventTokens:0, eventRewardProgress:0, eventRewardTarget:3, eventRewardClaimed:false, cleanGates:0, impactBuffer:effects.impactBuffer, lastCashDashCoinAt:-Infinity, crashEventEmitted:false, finishEventEmitted:false,\r\n    paced:Boolean(paced),paceSchedule:paced?createPaceSchedule(normalizedSeed):null,paceOrigin:0,nextPaceIndex:0,pace:null,observedRestSeconds:0,observedControlSeconds:0,\r\n    progressive:Boolean(progressive&&biomes&&paced),nextRampIndex:0,stunt:{active:false,progress:0,airborne:false},\r\n    biomes:Boolean(biomes&&paced),biomeIndex:0,biomeStartedAt:0,paceTimeOrigin:0,portalRest:false,\r\n    biome:biomes&&paced?biomeAt():null,biomeEffects:{speedUntil:0,magnetUntil:0,liftUntil:0,rainbowUntil:0},lastBiomePickup:null,\r\n  };\r\n  if(run.paced){run.speed=run.progressive?flightSpeedAt(0):PACED_FLIGHT_TUNING.speed;updatePace(run);}\r\n  return run;\r\n}\r\n\r\nfunction emit(run, type, data={}) {\r\n  const eventId = `${run.runId}:event:${++run.eventSequence}`;\r\n  if (run.eventIds.has(eventId)) return null;\r\n  run.eventIds.add(eventId);\r\n  const event = {runId:run.runId,eventId,entityId:data.entityId ?? null,simTick:run.simTick,type,origin:data.origin ?? 'simulation',...data};\r\n  run.events.push(event);\r\n  if (type === 'mission') return event;\r\n  return event;\r\n}\r\n\r\nfunction addObject(run, object) {\r\n  const id = object.id || `${run.runId}:entity:${++run.entitySequence}`;\r\n  run.objects.push({id,type:'coin',z:run.distance+40,x:0,y:0.5,w:0.08,h:0.08,value:1,active:true,collected:false,...object,id});\r\n  return id;\r\n}\r\n\r\nfunction maxAxisRates(run) {\r\n  const handling = vehicleById(run.vehicle)?.handling;\r\n  return {\r\n    x:FLIGHT_TUNING.baseLateralSpeed * (handling?.lateral ?? 1),\r\n    y:FLIGHT_TUNING.baseVerticalSpeed * (handling?.vertical ?? 1) * run.effects.verticalResponse\r\n  };\r\n}\r\n\r\n/** Check conservative movement-time reachability of one authored safe waypoint. */\r\nexport function safeRouteReachable(from, to, seconds, rates={x:0.55,y:0.38}, margin=0.8) {\r\n  if (![from?.x,from?.y,to?.x,to?.y,seconds,rates?.x,rates?.y].every(finite) || seconds <= margin || rates.x <= 0 || rates.y <= 0) return false;\r\n  const need = Math.max(Math.abs(to.x-from.x)/rates.x, Math.abs(to.y-from.y)/rates.y);\r\n  return need <= seconds - margin;\r\n}\r\n\r\nfunction chooseSafePoint(run, spacing) {\r\n  const latticeX = [-0.50,-0.25,0,0.25,0.50];\r\n  const latticeY = [0.28,0.39,0.50,0.61,0.72];\r\n  const previous = run.safeRoute.at(-1) || {x:run.x,y:run.y};\r\n  const travelSeconds = spacing / Math.min(FLIGHT_TUNING.maxSpeed, run.speed);\r\n  const rates = {x:0.54,y:0.38};\r\n  const reachable = [];\r\n  for (const x of latticeX) for (const y of latticeY) {\r\n    const candidate = {x,y};\r\n    if (Math.abs(x-previous.x) <= 0.55 && Math.abs(y-previous.y) <= 0.24 && safeRouteReachable(previous,candidate,travelSeconds,rates,0.85)) reachable.push(candidate);\r\n  }\r\n  const candidates = reachable.length ? reachable : [{x:clamp(previous.x,-0.5,0.5),y:clamp(previous.y,0.28,0.72)}];\r\n  return candidates[Math.floor(run.random()*candidates.length)] || {x:0,y:0.5};\r\n}\r\n\r\nfunction addGatePanel(run, pattern, geometry, kind) {\r\n  const {x,y,w,h} = geometry;\r\n  if (w <= 0.015 || h <= 0.015) return;\r\n  addObject(run,{id:`${pattern.id}:panel:${pattern.panels++}`,type:kind,kind:'gate-panel',patternId:pattern.id,z:pattern.z,x,y,w,h,radius:0,active:true,\r\n    safePoint:clone(pattern.safePoint),gateStyle:pattern.style,gateStage:pattern.gateStage,opening:pattern.opening,basePanel:{x,y,w,h},warningLead:(run.paced?run.speed*3:FLIGHT_TUNING.warningLead)+run.effects.warningLead,laserState:'idle'});\r\n}\r\n\r\nfunction beginnerOpening(stage,index,safePoint,approachPoint){\r\n  const wide=stage==='widening',panelHeight=wide?.44:.30,panelWidth=wide?.55:.40;\r\n  // Gate w/h are collision and render half-extents (the same convention as\r\n  // the standard four-panel gate). Keep the clear lane beyond player radius.\r\n  const hx=panelWidth/2,hy=panelHeight/2,clearance=.11;\r\n  const options=['top','bottom','left','right'],points=[safePoint,approachPoint].filter(Boolean);\r\n  const safe=opening=>opening==='top'?points.every(point=>point.y<=1-panelHeight-clearance)\r\n    :opening==='bottom'?(points.every(point=>point.y<=.06)||points.every(point=>point.y>=.16+panelHeight+clearance))\r\n      :opening==='left'?points.every(point=>point.x>=-1+panelWidth+clearance):points.every(point=>point.x<=1-panelWidth-clearance);\r\n  const opening=options.map((_,offset)=>options[(index+offset)%options.length]).find(safe)??'left';\r\n  if(opening==='top')return {opening,geometry:{x:0,y:1-hy,w:1,h:hy}};\r\n  if(opening==='bottom')return {opening,geometry:{x:0,y:.16+hy,w:1,h:hy}};\r\n  return {opening,geometry:{x:opening==='left'?-1+hx:1-hx,y:.5,w:hx,h:.5}};\r\n}\r\n\r\nfunction makeGate(run, z, safePoint, index, arrivalTime=run.time, approachPoint=run.safeRoute.at(-1)) {\r\n  const id = run.biomes?`${run.runId}:biome:${run.biomeIndex}:gate:${index}`:`${run.runId}:gate:${index}`;\r\n  const hx = FLIGHT_TUNING.safeHalfWidth, hy = run.paced ? .24 : FLIGHT_TUNING.safeHalfHeight;\r\n  const x = clamp(safePoint.x,-0.58,0.58), y = clamp(safePoint.y,0.08,0.92);\r\n  const kind = index % 3 === 1 ? 'laser' : 'zapper';\r\n  const gateStage=arrivalTime<60?'opening':arrivalTime<90?'widening':'standard';\r\n  const pattern={id,z,safePoint:{x,y},panels:0,style:gateStage==='standard'?['fixed','sweeping','orbiting'][index%3]:'fixed',kind,gateStage,opening:'quad'};\r\n  if(gateStage!=='standard'){\r\n    const {opening,geometry}=beginnerOpening(gateStage,index,pattern.safePoint,approachPoint);pattern.opening=opening;\r\n    addGatePanel(run,pattern,geometry,kind);\r\n  }else{\r\n    const leftEdge=x-hx,rightEdge=x+hx,lowEdge=run.paced?Math.max(0,y-hy):y-hy,highEdge=run.paced?Math.min(1,y+hy):y+hy;\r\n    addGatePanel(run,pattern,{x:0,y:(highEdge+1)/2,w:1,h:(1-highEdge)/2},kind);\r\n    addGatePanel(run,pattern,{x:0,y:lowEdge/2,w:1,h:lowEdge/2},kind);\r\n    addGatePanel(run,pattern,{x:(-1+leftEdge)/2,y,w:(leftEdge+1)/2,h:hy},kind);\r\n    addGatePanel(run,pattern,{x:(rightEdge+1)/2,y,w:(1-rightEdge)/2,h:hy},kind);\r\n  }\r\n  run.gateGroups.set(id,{id,z,safePoint:{x,y},kind,hit:false,cleared:false,style:pattern.style,gateStage,opening:pattern.opening,arrivalTime});\r\n  run.safeRoute.push({patternId:id,z,x,y,spacing:z-(run.safeRoute.at(-1)?.z ?? 0),kind,style:pattern.style,gateStage,opening:pattern.opening,arrivalTime});\r\n  if (run.safeRoute.length > 100) run.safeRoute.shift();\r\n  if (run.events) emit(run,'course',{entityId:id,kind:'safe-gate',style:pattern.style,gateStage,opening:pattern.opening,safePoint:{x,y},origin:'pattern-generator'});\r\n  return pattern;\r\n}\r\n\r\nfunction addCoinPattern(run, gate, index) {\r\n  const type = ['ribbon','stair','wave','split'][index%4];\r\n  const offsets = type === 'stair'\r\n    ? [[-0.16,-0.08],[-0.08,-0.03],[0.08,0.03],[0.16,0.08]]\r\n    : type === 'wave'\r\n      ? [[-0.12,0.07],[-0.05,-0.06],[0.05,0.06],[0.12,-0.07]]\r\n      : type === 'split'\r\n        ? [[-0.20,0],[0.20,0],[-0.14,0.10],[0.14,-0.10]]\r\n        : [[-0.13,0],[-0.04,0],[0.04,0],[0.13,0]];\r\n  offsets.forEach(([dx,dy],i)=>{\r\n    const z=gate.z-3+i*2;\r\n    addObject(run,{id:`${gate.id}:coin:${type}:${i}`,type:'coin',kind:'coin',pattern:type,z,\r\n      x:clamp(gate.safePoint.x+dx,-0.9,0.9),y:clamp(gate.safePoint.y+dy,0.08,0.92),radius:0.105,value:1+(index%5===4?1:0),\r\n      revealAt:Math.max(0,z-4-run.effects.coinPreview),active:true,origin:'course'});\r\n  });\r\n}\r\n\r\nfunction addMissile(run, gate, index) {\r\n  addObject(run,{id:`${gate.id}:missile`,type:'missile',kind:'seeking-orb',patternId:gate.id,z:gate.z+14,\r\n    x:gate.safePoint.x,y:gate.safePoint.y,w:FLIGHT_TUNING.missileRadiusX,h:FLIGHT_TUNING.missileRadiusY,\r\n    state:'approach',warningLead:FLIGHT_TUNING.missileWarning+run.effects.warningLead,lockDistance:FLIGHT_TUNING.missileLock,launchDistance:FLIGHT_TUNING.missileLaunch,\r\n    targetX:null,targetY:null,active:true,sourceIndex:index});\r\n}\r\n\r\nfunction spawnExtras(run, gate, index) {\r\n  const z=gate.z+(run.paced?-4:5), point=gate.safePoint;\r\n  // The first three authored sectors guarantee a full component set on every seeded run.\r\n  if (index < 3 || run.random() < run.effects.componentChance) {\r\n    addObject(run,{id:`${gate.id}:component`,type:'component',kind:`core-${(run.componentParts+index)%3+1}`,z:gate.z-5,x:point.x,y:point.y,radius:0.13,value:1,active:true});\r\n  }\r\n  if (!run.vehicle && run.random() < 0.50) {\r\n    const pick=POWERUPS[Math.floor(run.random()*POWERUPS.length)];\r\n    addObject(run,{id:`${gate.id}:powerup`,type:'powerup',kind:pick.id,z,x:point.x,y:point.y,radius:0.14,value:1,active:true});\r\n  }\r\n  const missionVehicle=index===0&&run.missions.some(mission=>mission.kind==='vehicleTime');\r\n  if (!run.vehicle && (missionVehicle || run.random() < 0.17)) {\r\n    const options=VEHICLES.filter(vehicle=>vehicle.tier==='default');\r\n    const pick=options[Math.floor(run.random()*options.length)];\r\n    addObject(run,{id:`${gate.id}:vehicle`,type:'vehicle',kind:pick.id,z:gate.z+(run.paced?-2:10),x:point.x,y:point.y,radius:0.15,value:1,active:true});\r\n  }\r\n  if ((run.event && index%2===0) || run.random()<run.effects.eventTokenChance) {\r\n    addObject(run,{id:`${gate.id}:token`,type:'token',kind:'event-shard',z:gate.z+2,x:point.x,y:point.y,radius:0.12,value:1,active:true});\r\n  }\r\n  if (index%2===1 || run.random()<0.30) {\r\n    addObject(run,{id:`${gate.id}:npc`,type:'npc',kind:'coach-flyby',z:gate.z+(run.paced?-1:7),x:point.x,y:point.y,radius:0.18,value:1,active:true});\r\n  }\r\n  if (index%3===2&&!run.paced&&gate.gateStage!=='opening') addMissile(run,gate,index);\r\n}\r\n\r\nconst PACE_LABELS={rest:'Chest down · rest',top:'High plank',middle:'Middle push-up',left:'Lean left',right:'Lean right'};\r\nfunction updatePace(run){\r\n  if(!run.paced)return;\r\n  const p=paceAt(run.paceSchedule,Math.max(0,run.time-run.paceTimeOrigin)),kind=p.isRest?'rest':p.activeKind,nextKind=p.next.kind==='rest'?'rest':p.next.activeKind;\r\n  run.pace={kind,label:PACE_LABELS[kind],remaining:p.remaining,nextKind,nextLabel:PACE_LABELS[nextKind],target:p.target,\r\n    transitioning:p.transitioning,restShare:p.restShare,index:p.index,duration:p.segment.duration};\r\n}\r\n\r\nfunction ensurePacedCourse(run){\r\n  let guard=0;\r\n  while(guard++<12){\r\n    const segment=segmentAt(run.paceSchedule,run.nextPaceIndex);\r\n    if(segment.start>run.time-run.paceTimeOrigin+PACED_FLIGHT_TUNING.lookaheadSeconds||run.biomes&&segment.start>=BIOME_SECONDS)break;\r\n    const index=run.nextPaceIndex++,z=run.paceOrigin+(segment.end-PACED_FLIGHT_TUNING.gateLeadSeconds)*PACED_FLIGHT_TUNING.speed;\r\n    if(run.biomes&&segment.end>BIOME_SECONDS)continue;\r\n    const objectStart=run.objects.length;\r\n    const arrivalTime=run.paceTimeOrigin+segment.end-PACED_FLIGHT_TUNING.gateLeadSeconds;\r\n    const gate=makeGate(run,z,segment.target,index,arrivalTime,segment.fromTarget);\r\n    run.gateGroups.get(gate.id).paceIndex=index;\r\n    const pattern=['ribbon','stair','wave','split'][index%4];\r\n    for(let at=segment.start+Math.max(1.35,segment.transitionSeconds+.45),coin=0;at<segment.end-.55;at+=.6,coin++){\r\n      addObject(run,{id:`${gate.id}:pace-coin:${coin}`,type:'coin',kind:'coin',pattern,z:run.paceOrigin+at*PACED_FLIGHT_TUNING.speed,\r\n        x:segment.target.x,y:segment.target.y,radius:.105,value:1,revealAt:Math.max(0,run.paceOrigin+(at-3)*PACED_FLIGHT_TUNING.speed),active:true,origin:'paced-course'});\r\n    }\r\n    spawnExtras(run,gate,index);\r\n    if(run.biomes){\r\n      const pickup=BIOMES[run.biomeIndex%BIOMES.length].pickups[index%3];\r\n      addObject(run,{id:`${gate.id}:biome-pickup`,type:'biome-pickup',kind:pickup.id,name:pickup.name,benefit:pickup.benefit,shape:pickup.shape,\r\n        biomeId:run.biome.id,z:gate.z-3,x:segment.target.x,y:segment.target.y,w:.13,h:.13,active:true,origin:'biome'});\r\n    }\r\n    // The missile follows the previous active height into a neutral recovery bay.\r\n    // It cannot target the chest-down lane or demand a dodge during recovery.\r\n    if(segment.kind==='rest'&&index>=4&&index%4===0&&gate.gateStage!=='opening'){\r\n      const at=segment.start+1.7;\r\n      addObject(run,{id:`${gate.id}:recovery-missile`,type:'missile',kind:'seeking-orb',patternId:null,z:run.paceOrigin+at*PACED_FLIGHT_TUNING.speed,\r\n        x:segment.fromTarget.x,y:Math.max(.5,segment.fromTarget.y),w:FLIGHT_TUNING.missileRadiusX,h:FLIGHT_TUNING.missileRadiusY,\r\n        state:'approach',warningLead:PACED_FLIGHT_TUNING.speed*3.4,lockDistance:PACED_FLIGHT_TUNING.speed*2.5,launchDistance:PACED_FLIGHT_TUNING.speed*.9,\r\n        targetX:null,targetY:null,restSafe:true,active:true,sourceIndex:index});\r\n    }\r\n    if(run.biomes){\r\n      for(const object of run.objects.slice(objectStart)){\r\n        object.arrivalTime=run.paceTimeOrigin+(object.z-run.paceOrigin)/PACED_FLIGHT_TUNING.speed;\r\n        if(finite(object.revealAt))object.revealTime=run.paceTimeOrigin+(object.revealAt-run.paceOrigin)/PACED_FLIGHT_TUNING.speed;\r\n      }\r\n      run.gateGroups.get(gate.id).arrivalTime=arrivalTime;run.safeRoute.at(-1).arrivalTime=arrivalTime;\r\n    }\r\n  }\r\n  if(run.biomes){ensureRamps(run);retimeCourse(run);}\r\n}\r\n\r\nfunction ensureRamps(run){\r\n  if(!run.progressive||!['tropical','rainbow'].includes(run.biome.id))return;\r\n  const times=[12,36,60,82],local=run.time-run.biomeStartedAt;\r\n  while(run.nextRampIndex<times.length&&times[run.nextRampIndex]<=local+PACED_FLIGHT_TUNING.lookaheadSeconds){\r\n    const index=run.nextRampIndex++,arrivalTime=run.biomeStartedAt+times[index];\r\n    addObject(run,{id:`${run.runId}:biome:${run.biomeIndex}:ramp:${index}`,type:'ramp',kind:'stunt-ramp',z:routeDistanceAt(run,arrivalTime),arrivalTime,x:0,y:0,w:1,h:.1,active:true});\r\n  }\r\n}\r\n\r\nfunction updateStunt(run,oldDistance,input){\r\n  for(const ramp of run.objects)if(ramp.active&&ramp.type==='ramp'&&oldDistance<=ramp.z&&run.distance>=ramp.z){\r\n    ramp.active=false;\r\n    if(!input.resting&&input.height01>.25&&Math.abs(run.x)<.95){\r\n      run.stunt={active:true,progress:0,airborne:true,startTime:run.time,entityId:ramp.id};\r\n      emit(run,'stunt',{entityId:ramp.id,kind:'ramp-pass',origin:'ramp'});\r\n    }\r\n  }\r\n  if(run.stunt.active){\r\n    run.stunt.progress=clamp((run.time-run.stunt.startTime)/1.1,0,1);\r\n    run.stunt.active=run.stunt.progress<1;run.stunt.airborne=run.stunt.active;\r\n  }\r\n}\r\n\r\n// Future objects retain their authored arrival seconds through boosts. The\r\n// speed integral changes distance/score and visible travel, never posture time.\r\nfunction routeDistanceAt(run,at){\r\n  const seconds=at-run.time;\r\n  const travel=run.progressive?flightTravelBetween(run.time,at):PACED_FLIGHT_TUNING.speed*seconds;\r\n  return run.distance+travel+5*Math.max(0,Math.min(at,run.biomeEffects.speedUntil)-run.time);\r\n}\r\nfunction retimeCourse(run){\r\n  for(const object of run.objects)if(finite(object.arrivalTime)&&object.arrivalTime>=run.time){\r\n    object.z=routeDistanceAt(run,object.arrivalTime);\r\n    if(finite(object.revealTime))object.revealAt=routeDistanceAt(run,object.revealTime);\r\n  }\r\n  for(const group of run.gateGroups.values())if(finite(group.arrivalTime)&&group.arrivalTime>=run.time)group.z=routeDistanceAt(run,group.arrivalTime);\r\n  for(const point of run.safeRoute)if(finite(point.arrivalTime)&&point.arrivalTime>=run.time)point.z=routeDistanceAt(run,point.arrivalTime);\r\n}\r\nfunction updateBiome(run){\r\n  if(!run.biomes)return;\r\n  run.biome=biomeAt(run.biomeIndex,run.time-run.biomeStartedAt,{portal:run.portalRest,restElapsed:run.biome?.restElapsed??0});\r\n}\r\nfunction enterPortal(run){\r\n  run.portalRest=true;run.phase='portal';run.accumulator=0;run.vx=0;run.vy=0;\r\n  run.objects=[];run.gateGroups.clear();run.safeRoute=[];run.foldPreview=null;\r\n  updateBiome(run);\r\n  emit(run,'course',{kind:'biome-portal',biome:run.biome.id,next:run.biome.nextId,origin:'biome'});\r\n}\r\n\r\nfunction spawnPattern(run) {\r\n  const index=run.patternIndex++;\r\n  const spacing=FLIGHT_TUNING.gateSpacingMin+run.random()*FLIGHT_TUNING.gateSpacingJitter;\r\n  const safePoint=chooseSafePoint(run,spacing);\r\n  const z=run.nextPatternAt;\r\n  const arrivalTime=run.time+Math.max(0,(z-run.distance)/Math.max(.1,run.speed));\r\n  const gate=makeGate(run,z,safePoint,index,arrivalTime);\r\n  addCoinPattern(run,gate,index);\r\n  spawnExtras(run,gate,index);\r\n  run.nextPatternAt=z+spacing;\r\n}\r\n\r\nfunction ensureCourse(run) {\r\n  if(run.paced){ensurePacedCourse(run);return;}\r\n  const limit=run.distance+FLIGHT_TUNING.spawnAhead;\r\n  let guard=0;\r\n  while (run.nextPatternAt <= limit && guard++ < 12) spawnPattern(run);\r\n  if (run.objects.length > FLIGHT_TUNING.maxObjects) {\r\n    run.objects=run.objects.filter(object => object.active && object.z >= run.distance-6).slice(-FLIGHT_TUNING.maxObjects);\r\n  }\r\n}\r\n\r\nfunction safePointAt(run,z) {\r\n  let best=run.safeRoute[0];\r\n  for (const point of run.safeRoute) if (point.z <= z) best=point; else break;\r\n  return best ? {x:best.x,y:best.y} : {x:0,y:0.5};\r\n}\r\n\r\nfunction updateMissions(run, kind, amount=1) {\r\n  for (const mission of run.missions) {\r\n    if (mission.complete || mission.kind !== kind) continue;\r\n    mission.progress=Math.min(mission.target,mission.progress+amount);\r\n    if (mission.progress >= mission.target) {\r\n      mission.complete=true;\r\n      emit(run,'mission',{entityId:mission.id,kind:mission.id,amount:mission.stars,stars:mission.stars,progress:mission.progress,target:mission.target,origin:'mission'});\r\n    }\r\n  }\r\n}\r\n\r\nfunction collect(run, object) {\r\n  if (!object.active || object.collected || run.collectedIds.has(object.id)) return false;\r\n  if (object.type==='powerup' && run.vehicle) return false;\r\n  object.active=false; object.collected=true; run.collectedIds.add(object.id);\r\n  switch (object.type) {\r\n    case 'biome-pickup': {\r\n      const pickup=BIOMES.flatMap(b=>b.pickups).find(p=>p.id===object.kind);\r\n      if(!run.biomes||!pickup)return false;\r\n      const effect=pickup.benefit;\r\n      if(effect==='speed')run.biomeEffects.speedUntil=run.time+4;\r\n      if(effect==='magnet')run.biomeEffects.magnetUntil=run.time+8;\r\n      if(effect==='shield')run.shield=Math.min(run.effects.maxShield,run.shield+1);\r\n      if(effect==='lift')run.biomeEffects.liftUntil=run.time+8;\r\n      if(effect==='rainbow')run.biomeEffects.rainbowUntil=run.time+6;\r\n      if(effect==='coins'||effect==='lift'){\r\n        const amount=effect==='coins'?12:6;run.coins+=amount;updateMissions(run,'coins',amount);\r\n        emit(run,'coin',{id:`${run.runId}:coin:${object.id}`,entityId:object.id,amount,origin:'biome'});\r\n      }\r\n      run.lastBiomePickup={id:pickup.id,name:pickup.name,benefit:effect,until:run.time+3};\r\n      emit(run,'powerup',{entityId:object.id,kind:pickup.id,benefit:effect,origin:'biome'});updateMissions(run,'pickups',1);\r\n      return true;\r\n    }\r\n    case 'coin': {\r\n      const amount=Math.max(1,Math.floor(object.value||1)+run.effects.coinBonus+(run.powerup?.family==='cash-dash'?1:0));\r\n      run.coins+=amount;\r\n      emit(run,'coin',{id:`${run.runId}:coin:${object.id}`,entityId:object.id,amount,origin:object.origin||'course'});\r\n      updateMissions(run,'coins',amount);\r\n      updateMissions(run,'pickups',1);\r\n      return true;\r\n    }\r\n    case 'vehicle': {\r\n      const next=vehicleById(object.kind);\r\n      if (!next) return false;\r\n      run.vehicle=next.id; run.vehicleUntil=run.time+vehicleDuration(run,next.id); run.vehicleHealth=vehicleHealthFor(next.id);\r\n      emit(run,'vehicle',{entityId:object.id,kind:next.id,origin:'pickup'});\r\n      updateMissions(run,'pickups',1);\r\n      return true;\r\n    }\r\n    case 'powerup': {\r\n      const power=powerupById.get(object.kind);\r\n      if (!power || run.vehicle) return false;\r\n      if (power.family==='shield') run.shield=Math.min(run.effects.maxShield,run.shield+power.effect.shield);\r\n      else run.powerup={id:power.id,family:power.family,startedAt:run.time,until:run.time+power.duration+run.effects.powerupDuration,effect:clone(power.effect)};\r\n      emit(run,'powerup',{entityId:object.id,kind:power.id,origin:'pickup'});\r\n      updateMissions(run,'pickups',1);\r\n      return true;\r\n    }\r\n    case 'component': {\r\n      run.componentParts=Math.min(3,run.componentParts+1);\r\n      emit(run,'component',{entityId:object.id,kind:object.kind,amount:1,parts:run.componentParts,origin:'pickup'});\r\n      updateMissions(run,'pickups',1);\r\n      if (run.componentParts>=3 && !run.componentMechDeployed) {\r\n        run.componentMechDeployed=true; run.vehicle='triad-bulwark'; run.vehicleUntil=run.time+vehicleDuration(run,'triad-bulwark'); run.vehicleHealth=vehicleHealthFor('triad-bulwark');\r\n        emit(run,'vehicle',{entityId:`${run.runId}:component-mech`,kind:'triad-bulwark',origin:'component-set'});\r\n      } else if (run.componentParts>=3 && run.effects.componentChance>0) {\r\n        run.coins++; emit(run,'coin',{id:`${run.runId}:coin:${object.id}:salvage`,entityId:`${object.id}:salvage`,amount:1,origin:'component-salvage'});\r\n        updateMissions(run,'coins',1);\r\n      }\r\n      return true;\r\n    }\r\n    case 'token':\r\n      run.eventTokens+=Math.max(1,object.value||1);\r\n      emit(run,'token',{entityId:object.id,kind:object.kind,amount:object.value||1,origin:'event'});\r\n      if(run.event){\r\n        run.eventRewardProgress=Math.min(run.eventRewardTarget,run.eventRewardProgress+Math.max(1,object.value||1));\r\n        if(run.eventRewardProgress>=run.eventRewardTarget&&!run.eventRewardClaimed){\r\n          run.eventRewardClaimed=true; run.coins+=15;\r\n          emit(run,'coin',{id:`${run.runId}:event-signal-cache`,entityId:`${run.runId}:event-signal-cache`,amount:15,origin:'event-track'});\r\n        }\r\n      }\r\n      updateMissions(run,'pickups',1);\r\n      return true;\r\n    case 'npc':\r\n      emit(run,'npc',{entityId:object.id,kind:object.kind,amount:run.effects.npcBonus,origin:'flyby'});\r\n      updateMissions(run,'pickups',1);\r\n      if (run.effects.npcBonus>0) {\r\n        run.coins+=run.effects.npcBonus;\r\n        emit(run,'coin',{id:`${run.runId}:coin:${object.id}:companion`,entityId:`${object.id}:companion`,amount:run.effects.npcBonus,origin:'companion-gadget'});\r\n        updateMissions(run,'coins',run.effects.npcBonus);\r\n      }\r\n      return true;\r\n    default: return false;\r\n  }\r\n}\r\n\r\nfunction currentVehicle(run) { return vehicleById(run.vehicle); }\r\n\r\nfunction hazardActive(run,object) {\r\n  if (!object.active || object.type!=='zapper' && object.type!=='laser' && object.type!=='missile') return false;\r\n  if(run.biomes&&run.biomeEffects.rainbowUntil>0)return false;\r\n  // Expiration is resolved after contacts for this bounded 1/60 s step, then\r\n  // grants a clear exit. Never remove protection before an already-overlapping hit.\r\n  if (run.powerup?.family==='cash-dash') return false;\r\n  if (object.type==='laser') return object.laserState==='active';\r\n  return true;\r\n}\r\n\r\nfunction overlapAt(run,object,x,y) {\r\n  const handling=currentVehicle(run)?.handling;\r\n  const playerRadius=handling?.radius ?? 0.085;\r\n  if (object.type==='zapper' || object.type==='laser') {\r\n    return Math.abs(x-object.x) <= object.w+playerRadius && Math.abs(y-object.y) <= object.h+playerRadius;\r\n  }\r\n  return Math.abs(x-object.x) <= object.w+playerRadius && Math.abs(y-object.y) <= object.h+playerRadius;\r\n}\r\n\r\nfunction takeHit(run, object) {\n  if(run.biomes&&run.biomeEffects.rainbowUntil>0)return;\r\n  object.active=false;\n  const group=object.patternId && run.gateGroups.get(object.patternId);\n  if (group) group.hit=true;\n  if(run.coinChase) {\n    run.hits++;\n    run.slowUntil=Math.max(run.slowUntil,run.time+COIN_CHASE_TUNING_PROPOSAL.hitSlowSeconds);\n    emit(run,'hit',{entityId:object.id,hitId:`${run.runId}:hit:${object.id}`,kind:object.type,protection:'slow-only',origin:'collision'});\n    return;\n  }\n  if (run.time < run.graceUntil) return;\n  const vehicle=currentVehicle(run);\r\n  if (vehicle && run.vehicleHealth>0) {\r\n    run.vehicleHealth--;\r\n    emit(run,'hit',{entityId:object.id,kind:object.type,protection:'vehicle',origin:'collision'});\r\n    if (run.vehicleHealth<=0) {\r\n      const old=run.vehicle; run.vehicle=null; run.vehicleUntil=0;\r\n      emit(run,'vehicle',{entityId:object.id,kind:old,action:'damaged-exit',origin:'collision'});\r\n    }\r\n  } else if (run.shield>0) {\r\n    run.shield--;\r\n    emit(run,'hit',{entityId:object.id,kind:object.type,protection:'shield',origin:'collision'});\r\n  } else if (run.impactBuffer>0) {\r\n    run.impactBuffer--;\r\n    emit(run,'hit',{entityId:object.id,kind:object.type,protection:'rescue-buffer',origin:'collision'});\r\n  } else {\r\n    run.hits++;\r\n    run.phase='crashed';\r\n    if (!run.crashEventEmitted) {\r\n      run.crashEventEmitted=true;\r\n      emit(run,'crash',{entityId:object.id,kind:object.type,origin:'collision'});\r\n    }\r\n  }\r\n  run.graceUntil=run.time+FLIGHT_TUNING.graceSeconds+run.effects.crashGrace;\r\n}\r\n\r\nfunction updateThreatStates(run) {\r\n  for (const object of run.objects) {\r\n    if (!object.active) continue;\r\n    if(object.basePanel&&object.gateStyle!=='fixed') {\r\n      const b=object.basePanel,point=object.safePoint,phase=run.time*.9;\r\n      const outward=(1+Math.sin(phase))*.5;\r\n      const vertical=Math.abs(b.x)<1e-8;\r\n      // Panels move only OUT of the reserved aperture. The guaranteed opening\r\n      // never shrinks; orbiting is motion of the panels, not a claimed rotating beam.\r\n      object.x=b.x+(vertical ? object.gateStyle==='orbiting'?.035*Math.cos(phase):0 : Math.sign(b.x-point.x)*.075*outward);\r\n      object.y=b.y+(vertical ? Math.sign(b.y-point.y)*.055*outward : object.gateStyle==='orbiting'?.035*Math.cos(phase):0);\r\n    }\r\n    // Canonical15-unit distance preserves authored warning/charge seconds when\r\n    // biome boosts alter actual travel distance. Collision/render still use z.\r\n    const timed=run.biomes&&finite(object.arrivalTime);\r\n    const ahead=timed?(object.arrivalTime-run.time)*PACED_FLIGHT_TUNING.speed:object.z-run.distance;\r\n    const warningLead=timed?PACED_FLIGHT_TUNING.speed*(object.type==='missile'?3.4:3)+run.effects.warningLead:object.warningLead;\r\n    if(timed)object.warningLead=routeDistanceAt(run,run.time+(object.type==='missile'?3.4:3))-run.distance+run.effects.warningLead;\r\n    if ((object.type==='zapper'||object.type==='laser') && ahead <= warningLead && ahead >= -1 && !run.warnedGroups.has(object.patternId)) {\r\n      run.warnedGroups.add(object.patternId);\r\n      emit(run,'course',{entityId:object.patternId,kind:'hazard-warning',hazard:object.type,origin:'telegraph'});\r\n    }\r\n    if (object.type==='laser') {\r\n      object.laserState = ahead > warningLead ? 'idle' : ahead > (run.paced?PACED_FLIGHT_TUNING.speed*1.5:10) ? 'telegraph' : ahead > (run.paced?PACED_FLIGHT_TUNING.speed*.5:3) ? 'charging' : ahead >= -1 ? 'active' : 'cooldown';\r\n    } else if (object.type==='missile') {\r\n      if (ahead <= warningLead && object.state==='approach') {\r\n        object.state='warning';\r\n        emit(run,'course',{entityId:object.id,kind:'missile-warning',origin:'telegraph'});\r\n      }\r\n      const slow=run.powerup?.family==='rocket-time' && run.time < run.powerup.until ? run.powerup.effect.threatSlow : 1;\r\n      if (ahead <= object.lockDistance*slow && object.targetX===null) {\r\n        object.targetX=run.x; object.targetY=object.restSafe?Math.max(.5,run.y):run.y; object.x=object.targetX; object.y=object.targetY; object.state='locked';\r\n        emit(run,'course',{entityId:object.id,kind:'missile-locked',targetX:object.targetX,targetY:object.targetY,origin:'telegraph'});\r\n      }\r\n      if (ahead <= object.launchDistance*slow && object.state==='locked') {\r\n        object.state='launched';\r\n        emit(run,'course',{entityId:object.id,kind:'missile-launched',origin:'telegraph'});\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\nfunction spawnDashCoin(run) {\r\n  if (run.time-run.lastCashDashCoinAt < 0.55) return;\r\n  run.lastCashDashCoinAt=run.time;\r\n  const point=safePointAt(run,run.distance+8);\r\n  const id=`${run.runId}:dash-coin:${Math.floor(run.time*2)}`;\r\n  if (run.objects.some(object=>object.id===id)) return;\r\n  addObject(run,{id,type:'coin',kind:'coin',pattern:'cash-dash',z:run.distance+7.5,x:point.x,y:point.y,radius:0.12,value:1,active:true,origin:'cash-dash'});\r\n}\r\n\r\nconst isHazard = object => ['zapper','laser','missile'].includes(object.type);\r\nconst contactPriority = object => ['powerup','vehicle','component','biome-pickup'].includes(object.type) ? 0 : isHazard(object) ? 2 : 1;\r\n\r\n// Intersect the swept player with the object's complete depth slab and 2D\r\n// collision region. Looking only at its central z plane misses lateral entries.\r\nfunction contactTime(run,object,from,to,after=0) {\r\n  let first=after,last=1;\r\n  const radius=currentVehicle(run)?.handling.radius ?? .085;\r\n  let hx,hy;\r\n  if (isHazard(object)) {hx=object.w+radius;hy=object.h+radius;}\r\n  else if (object.type==='coin') {\r\n    const magnet=Math.max(run.powerup?.family==='cash-dash' ? run.powerup.effect.magnetRadius : run.effects.magnetRadius,\r\n      run.biomes&&run.time<run.biomeEffects.magnetUntil ? .65 : 0);\r\n    hx=hy=(object.radius||.09)+magnet;\r\n  } else hx=hy=(object.radius||.12)+radius;\r\n  for (const [a,b,center,half] of [[from.z,to.z,object.z,FLIGHT_TUNING.playerDepth],[from.x,to.x,object.x,hx],[from.y,to.y,object.y,hy]]) {\r\n    const delta=b-a;\r\n    if (Math.abs(delta)<1e-12) {if(Math.abs(a-center)>half)return null;continue;}\r\n    const p=(center-half-a)/delta,q=(center+half-a)/delta;\r\n    first=Math.max(first,Math.min(p,q));last=Math.min(last,Math.max(p,q));\r\n    if(first>last+1e-10)return null;\r\n  }\r\n  return first<=1 && last>=0 ? clamp(first,0,1) : null;\r\n}\r\n\r\nfunction resolveContacts(run,from,to) {\r\n  const visited=new Set();let after=0;\r\n  while(run.phase==='flying') {\r\n    let next=null;\r\n    for(const object of run.objects) {\r\n      if(!object.active||object.type==='ramp'||visited.has(object.id))continue;\r\n      if(object.z<from.z-FLIGHT_TUNING.playerDepth||object.z>to.z+FLIGHT_TUNING.playerDepth)continue;\r\n      if(isHazard(object)&&!hazardActive(run,object))continue;\r\n      const time=contactTime(run,object,from,to,after);if(time===null)continue;\r\n      const candidate={object,time,priority:contactPriority(object)};\r\n      if(!next||time<next.time-1e-9||Math.abs(time-next.time)<=1e-9&&(candidate.priority<next.priority||candidate.priority===next.priority&&object.id<next.object.id))next=candidate;\r\n    }\r\n    if(!next)break;\r\n    visited.add(next.object.id);after=next.time;\r\n    if(isHazard(next.object))takeHit(run,next.object);else collect(run,next.object);\r\n    // Recompute after pickups: shields, vehicle radius and coin attraction may\r\n    // have changed. IDs make a still-excluded pickup impossible to loop on.\r\n  }\r\n}\r\n\r\nfunction expireProtection(run) {\r\n  let exit=false;\r\n  if(run.biomes&&run.biomeEffects.rainbowUntil>0&&run.time>=run.biomeEffects.rainbowUntil){run.biomeEffects.rainbowUntil=0;exit=true;}\r\n  if(run.vehicle&&run.vehicleUntil>0&&run.time>=run.vehicleUntil) {\r\n    const old=run.vehicle;run.vehicle=null;run.vehicleUntil=0;run.vehicleHealth=0;exit=true;\r\n    emit(run,'vehicle',{entityId:`${run.runId}:vehicle-expiry:${run.simTick}`,kind:old,action:'expired',origin:'timer'});\r\n  }\r\n  if(run.powerup&&run.time>=run.powerup.until) {exit ||= run.powerup.family==='cash-dash';run.powerup=null;}\r\n  if(exit) {\r\n    const horizon=Math.max(12,run.speed*2);\r\n    for(const object of run.objects)if(object.active&&isHazard(object)&&object.z-run.distance<=horizon)object.active=false;\r\n    for(const group of run.gateGroups.values())if(group.z-run.distance<=horizon)group.cleared=true;\r\n    run.graceUntil=Math.max(run.graceUntil,run.time+FLIGHT_TUNING.graceSeconds);\r\n    emit(run,'course',{kind:'protection-exit',origin:'timer',message:'Protection ended. Clear corridor ahead.'});\r\n  }\r\n}\r\n\r\nconst VEHICLE_HINTS = {\r\n  'polarity-shell':'Polarity Shell: an optional comfortable low-to-high cycle switches a gentle virtual gravity pull. Rise still means up.',\r\n  foldwing:'Foldwing: hold a gentle lean to preview a short fold. Return to center to commit only when the route is clear.',\r\n  pipwing:'Pipwing: a comfortable rise adds a small flap. Slow rises work; rapid repetitions are not needed.',\r\n  'comet-hopper':'Comet Hopper: rise to climb and lean to steer. Your usual comfortable controls still work.',\r\n  'foundry-walker':'Foundry Walker: heavier handling and two armor hits. Allow extra time to change direction.',\r\n  'ribbon-skimmer':'Ribbon Skimmer: smooth steering with longer drift. Return to center early to settle.',\r\n  'tide-glider':'Tide Glider: smooth rise and dip handling. Follow the coin waves at your own pace.',\r\n  'triad-bulwark':'Triad Bulwark: two armor hits; steer with a gentle lean and keep both hands down.',\r\n};\r\n\r\nfunction foldDestinationClear(run,x,y) {\r\n  const radius=currentVehicle(run)?.handling.radius??.085;\r\n  return !run.objects.some(o=>o.active&&isHazard(o)&&o.z>=run.distance-FLIGHT_TUNING.playerDepth&&o.z-run.distance<=24\r\n    && Math.abs(o.x-x)<=o.w+radius+.10&&Math.abs(o.y-y)<=o.h+radius+.08);\r\n}\r\n\r\nfunction vehicleControls(run,input,dt) {\r\n  if(!run.vehicle){run.vehicleControl=null;run.foldPreview=null;run.vehicleHint='';return {height:input.height01,impulse:0};}\r\n  if(run.vehicleControl?.id!==run.vehicle){\r\n    run.vehicleControl={id:run.vehicle,entered:run.time,lowDwell:0,highDwell:0,armed:false,polarity:1,lastCycle:-Infinity,\r\n      leanDwell:0,leanSign:0,neutralDwell:0,lastFold:-Infinity,lastHeight:input.height01,rise:0,lastFlap:-Infinity};\r\n    run.vehicleHint=VEHICLE_HINTS[run.vehicle]??'Use your comfortable height and lean controls.';\r\n    emit(run,'course',{kind:'vehicle-instruction',vehicle:run.vehicle,message:run.vehicleHint,origin:'tutorial'});\r\n  }\r\n  const c=run.vehicleControl;let height=input.height01,impulse=0;\r\n  if(c.resetPending){c.lowDwell=0;c.highDwell=0;c.armed=false;c.leanDwell=0;c.neutralDwell=0;c.rise=0;c.lastHeight=height;c.resetPending=false;run.foldPreview=null;}\r\n  if(run.time-c.entered<2){c.lastHeight=height;return {height,impulse};}\r\n  // Tutorials and comfortable movements are virtual adaptations. They do not\r\n  // certify physical reachability or alter the real exercise clock.\r\n  if(run.vehicle==='polarity-shell') {\r\n    c.lowDwell=height<.35?c.lowDwell+dt:0;if(c.lowDwell>=.25)c.armed=true;\r\n    c.highDwell=c.armed&&height>.7?c.highDwell+dt:0;\r\n    if(c.highDwell>=.25&&run.time-c.lastCycle>=2){\r\n      c.polarity*=-1;c.armed=false;c.highDwell=0;c.lastCycle=run.time;\r\n      emit(run,'vehicle',{kind:run.vehicle,action:'gravity-switched',polarity:c.polarity,origin:'comfortable-cycle'});\r\n    }\r\n    height=clamp(height+c.polarity*.045,0,1);\r\n  } else if(run.vehicle==='foldwing') {\r\n    const sign=Math.sign(input.steer),lean=Math.abs(input.steer);\r\n    if(lean>=.45&&height>=.3&&height<=.75&&run.time-c.lastFold>=2){\r\n      c.leanDwell=sign===c.leanSign?c.leanDwell+dt:dt;c.leanSign=sign;c.neutralDwell=0;\r\n      if(c.leanDwell>=.6){const x=clamp(run.x+sign*.22,-.85,.85),y=run.y;\r\n        run.foldPreview={x,y,clear:foldDestinationClear(run,x,y)};\r\n      }\r\n    }else if(lean<=.12&&run.foldPreview){\r\n      c.neutralDwell+=dt;\r\n      if(c.neutralDwell>=.18){\r\n        const target=run.foldPreview;\r\n        if(foldDestinationClear(run,target.x,target.y)){\r\n          run.x=target.x;run.vx=0;c.lastFold=run.time;\r\n          emit(run,'vehicle',{kind:run.vehicle,action:'fold',targetX:run.x,targetY:run.y,origin:'comfortable-cycle'});\r\n        }\r\n        run.foldPreview=null;c.leanDwell=0;c.neutralDwell=0;\r\n      }\r\n    }else {c.leanDwell=0;c.neutralDwell=0;run.foldPreview=null;}\r\n  }else if(run.vehicle==='pipwing') {\r\n    const rise=height-c.lastHeight;c.lastHeight=height;\r\n    c.rise=rise>=0?c.rise+rise:0;\r\n    if(c.rise>=.16&&run.time-c.lastFlap>=1.2){\r\n      impulse=.10;c.rise=0;c.lastFlap=run.time;\r\n      emit(run,'vehicle',{kind:run.vehicle,action:'flap',origin:'comfortable-rise'});\r\n    }\r\n  }\r\n  return {height,impulse};\r\n}\r\n\r\nfunction stepFlight(run, dt, input) {\r\n  if(!(dt>0)){if(run.biomes)enterPortal(run);return;}\r\n  if(run.biomes)retimeCourse(run);\r\n  expireProtection(run);\r\n  const virtual=vehicleControls(run,input,dt);\r\n  const oldDistance=run.distance, oldX=run.x, oldY=run.y;\r\n  const handling=currentVehicle(run)?.handling || {lateral:1,vertical:1,acceleration:1,damping:1,radius:0.085};\r\n  run.level=Math.floor(run.distance/FLIGHT_TUNING.levelDistance);\r\n  const boost=run.biomes&&dt>0?5*Math.min(dt,Math.max(0,run.biomeEffects.speedUntil-run.time))/dt:0;\r\n  const baseSpeed=run.paced?(run.progressive?flightTravelBetween(run.time,run.time+dt)/dt:PACED_FLIGHT_TUNING.speed)+boost:Math.min(FLIGHT_TUNING.maxSpeed,FLIGHT_TUNING.baseSpeed+run.level*FLIGHT_TUNING.speedPerLevel);\n  run.speed=run.coinChase&&run.time<run.slowUntil?baseSpeed*COIN_CHASE_TUNING_PROPOSAL.speedFactor:baseSpeed;\n  const rates=maxAxisRates(run), steer=clamp(input.steer,-1,1), height=clamp(virtual.height,0,1);\r\n  const desiredVx=run.paced?clamp(((input.resting?0:steer*.8)-run.x)*4,-PACED_FLIGHT_TUNING.lateralSpeed,PACED_FLIGHT_TUNING.lateralSpeed):steer*rates.x;\r\n  const horizontalDamping=steer===0 ? handling.damping : 1;\r\n  run.vx=moveToward(run.vx,desiredVx,FLIGHT_TUNING.horizontalAcceleration*handling.acceleration*horizontalDamping*dt);\r\n  run.x=clamp(run.x+run.vx*dt,-1,1);\r\n  if (run.x===-1 || run.x===1) run.vx=0;\r\n  const maxVy=run.paced?PACED_FLIGHT_TUNING.verticalSpeed:FLIGHT_TUNING.baseVerticalSpeed*handling.vertical;\r\n  const vyAcceleration=run.paced?((input.resting?0:height)-run.y)*8-run.vy*4:((height-run.y)*FLIGHT_TUNING.verticalAcceleration-run.vy*1.55*handling.damping)*handling.acceleration*run.effects.verticalResponse;\r\n  run.vy=clamp(run.vy+vyAcceleration*dt+virtual.impulse,-maxVy,maxVy);\r\n  run.y=clamp(run.y+run.vy*dt,0,1);\r\n  if (run.y===0 || run.y===1) run.vy=0;\r\n  run.time+=dt; run.distance+=run.speed*dt; run.score=Math.floor(run.distance); run.simTick++;\r\n  if(run.paced){run.observedControlSeconds+=dt;if(input.resting===true)run.observedRestSeconds+=dt;}\r\n  updatePace(run);\r\n  updateBiome(run);\r\n  updateStunt(run,oldDistance,input);\r\n  if(run.vehicle)updateMissions(run,'vehicleTime',dt);\r\n  run.level=Math.floor(run.distance/FLIGHT_TUNING.levelDistance);\r\n  if (run.powerup?.family==='cash-dash' && run.time<run.powerup.until) spawnDashCoin(run);\r\n  ensureCourse(run);\r\n  updateThreatStates(run);\r\n\r\n  resolveContacts(run,{z:oldDistance,x:oldX,y:oldY},{z:run.distance,x:run.x,y:run.y});\r\n  expireProtection(run);\r\n\r\n  for (const group of run.gateGroups.values()) {\r\n    if (!group.cleared && run.distance > group.z+FLIGHT_TUNING.playerDepth) {\r\n      group.cleared=true;\r\n      if (!group.hit) { run.cleanGates++; updateMissions(run,'clean',1); }\r\n    }\r\n  }\r\n  updateMissions(run,'distance',Math.max(0,run.distance-oldDistance));\r\n  for (const object of run.objects) if (object.active && object.z < run.distance-FLIGHT_TUNING.playerDepth-2) object.active=false;\r\n  run.objects=run.objects.filter(object=>object.active || object.z>=run.distance-8);\r\n  if(run.biomes&&run.time-run.biomeStartedAt>=BIOME_SECONDS-1e-8)enterPortal(run);\r\n}\r\n\r\n/** Advance only on fresh, valid controls. Invalid pose freezes physics and all collectible credit. */\r\nexport function advanceFlight(run, dt, input={height01:0.5,steer:0,valid:false}) {\r\n  if(run?.portalRest&&run.phase==='portal'){\r\n    if(finite(dt)&&dt>0){run.biome.restElapsed+=Math.min(dt,1);updateBiome(run);}\r\n    return run;\r\n  }\r\n  if (!run || run.phase!=='flying') return run;\r\n  if (!finite(dt) || dt<=0 || input?.valid!==true || !finite(input.height01) || !finite(input.steer)) return run;\r\n  const safeDt=Math.min(dt,run.paced ? .75 : FLIGHT_TUNING.maxFrameDelta);\r\n  run.accumulator+=safeDt;\r\n  const step=FLIGHT_TUNING.fixedStep;\r\n  let ticks=0;\r\n  while (run.accumulator+1e-10>=step && run.phase==='flying' && ticks<(run.paced?46:7)) {\r\n    const actualStep=run.biomes?Math.min(step,Math.max(0,run.biomeStartedAt+BIOME_SECONDS-run.time)):step;\r\n    stepFlight(run,actualStep,input);\r\n    run.accumulator-=step; ticks++;\r\n  }\r\n  if (run.accumulator<0) run.accumulator=0;\r\n  return run;\r\n}\r\n\r\nfunction clearThreatHorizon(run, horizon=24) {\r\n  if(run.paced)horizon=Math.max(horizon,run.speed*3.2);\r\n  for (const object of run.objects) {\r\n    if (!object.active || object.z-run.distance>horizon) continue;\r\n    if (object.type==='zapper'||object.type==='laser'||object.type==='missile') object.active=false;\r\n  }\r\n  for (const group of run.gateGroups.values()) if (group.z-run.distance<=horizon) group.cleared=true;\r\n  run.warnedGroups.clear(); run.graceUntil=run.time+1.1; run.accumulator=0;\r\n}\r\n\r\n/** Explicit arcade lifecycle. Fitness/workout lifecycle remains owned by the caller. */\r\nexport function commandFlight(run, command, payload={}) {\r\n  if (!run || run.phase==='finalized') return false;\r\n  switch (command) {\r\n    case 'continue-biome':\r\n      if(!run.biomes||!run.portalRest||run.phase!=='portal'||run.biome.restElapsed<PORTAL_REST_SECONDS-1e-8)return false;\r\n      run.nextRampIndex=0;run.stunt={active:false,progress:0,airborne:false};\r\n      run.biomeIndex++;run.biomeStartedAt=run.time;run.paceTimeOrigin=run.time;run.paceOrigin=run.distance;\r\n      run.paceSchedule=createPaceSchedule((run.seed+Math.imul(run.biomeIndex,0x9e3779b9))>>>0);run.nextPaceIndex=0;\r\n      run.portalRest=false;run.phase='paused';run.accumulator=0;run.x=0;run.y=.04;run.vx=0;run.vy=0;\r\n      run.biomeEffects={speedUntil:0,magnetUntil:0,liftUntil:0,rainbowUntil:0};run.lastBiomePickup=null;\r\n      run.vehicleControl=null;run.foldPreview=null;run.biome=biomeAt(run.biomeIndex);run.warnedGroups.clear();\r\n      updatePace(run);ensureCourse(run);\r\n      emit(run,'course',{kind:'biome-continue',biome:run.biome.id,origin:'biome'});return true;\r\n    case 'start':\r\n      if (run.phase!=='ready') return false;\r\n      run.loadout=normalizedLoadout(run.loadout,run.event); run.upgrades=normalizeUpgrades(run.upgrades); run.effects=effectsFor(run.loadout,run.upgrades);\r\n      run.shield=run.effects.initialShield; run.impactBuffer=run.effects.impactBuffer;\r\n      run.vehicle=run.explicitVehicle || run.effects.initialVehicle || null;\r\n      run.vehicleUntil=run.vehicle ? vehicleDuration(run,run.vehicle) : 0;\r\n      run.vehicleHealth=vehicleHealthFor(run.vehicle);\r\n      if(run.paced)run.paceOrigin=run.distance-run.time*PACED_FLIGHT_TUNING.speed;\r\n      run.phase='flying'; run.startedAt=run.time; run.accumulator=0; ensureCourse(run);\r\n      emit(run,'course',{entityId:`${run.runId}:start`,kind:'run-start',origin:'lifecycle'});\r\n      return true;\r\n    case 'pause':\r\n      if (run.phase!=='flying') return false;\r\n      if(run.vehicleControl)run.vehicleControl.resetPending=true;run.foldPreview=null;\r\n      run.phase='paused'; run.accumulator=0; emit(run,'course',{entityId:`${run.runId}:pause:${run.continuation}`,kind:'run-pause',reason:payload.reason||'user',origin:'lifecycle'}); return true;\r\n    case 'resume':\r\n      if (run.phase!=='paused') return false;\r\n      clearThreatHorizon(run); run.phase='flying';\r\n      emit(run,'course',{entityId:`${run.runId}:resume:${run.continuation}`,kind:'run-resume',origin:'lifecycle'}); return true;\r\n    case 'revive':\r\n      if (run.phase!=='crashed') return false;\r\n      if(run.vehicleControl)run.vehicleControl.resetPending=true;run.foldPreview=null;\r\n      run.continuation++; run.crashEventEmitted=false; clearThreatHorizon(run); run.phase='flying';\r\n      emit(run,'course',{entityId:`${run.runId}:continuation:${run.continuation}`,kind:'run-revive',continuation:run.continuation,origin:'lifecycle'}); return true;\r\n    case 'headstart':\r\n      if (run.phase!=='ready' || run.headstartUsed) return false;\r\n      run.headstartUsed=true; run.distance+=Math.max(0,Math.min(24,finite(payload.distance)?payload.distance:12));\r\n      run.score=Math.floor(run.distance); run.nextPatternAt=run.distance+FLIGHT_TUNING.firstGateDistance;\r\n      if(run.paced)run.paceOrigin=run.distance;\r\n      emit(run,'course',{entityId:`${run.runId}:headstart`,kind:'headstart',distance:run.distance,origin:'utility'}); return true;\r\n    case 'final-blast':\r\n      if (run.phase!=='crashed' || run.finalBlastUsed) return false;\r\n      {const jump=Math.max(0,Math.min(30,finite(payload.distance)?payload.distance:15));run.finalBlastUsed=true; run.distance+=jump;\r\n      if(run.paced){run.paceOrigin+=jump;for(const object of run.objects)object.z+=jump;for(const point of run.safeRoute)point.z+=jump;for(const group of run.gateGroups.values())group.z+=jump;}}\r\n      run.score=Math.floor(run.distance);\r\n      emit(run,'course',{entityId:`${run.runId}:final-blast`,kind:'final-blast',distance:run.distance,origin:'utility'}); return true;\r\n    case 'event':\r\n      if (run.phase!=='ready' && run.phase!=='paused') return false;\r\n      run.event=payload.enabled===undefined ? true : Boolean(payload.enabled);\r\n      run.loadout=normalizedLoadout(run.loadout,run.event); run.upgrades=normalizeUpgrades(run.upgrades); run.effects=effectsFor(run.loadout,run.upgrades);\r\n      emit(run,'course',{entityId:`${run.runId}:event-mode:${run.continuation}`,kind:'event-mode',enabled:run.event,origin:'utility'}); return true;\r\n    case 'finish':\r\n      if (run.phase==='finalized') return false;\r\n      run.phase='finalized'; run.portalRest=false;updateBiome(run);run.accumulator=0;\r\n      if (!run.finishEventEmitted) { run.finishEventEmitted=true; emit(run,'course',{entityId:`${run.runId}:finish`,kind:'run-finish',coins:run.coins,distance:run.distance,origin:'lifecycle'}); }\r\n      return true;\r\n    default: return false;\r\n  }\r\n}\r\n","arcade/plank-flight/view.mjs":"import {visualCue,setVisualCue,symbolMarkup,VISUAL_CSS} from '../visual-language.mjs';\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport { themeFor } from '../runner/themes.mjs';\r\nimport { releaseGraphics } from '../../modules/graphics-lifecycle.mjs';\r\nimport {createBiomeScene as createDepthScene} from './biome-scene.mjs';\r\nimport {createBiomeScene as createLegacyScene} from './biome-scene-legacy.mjs';\r\nimport {biomeTheme as legacyBiomeTheme} from './biome-visuals-legacy.mjs';\r\nimport {visualFlag} from '../../modules/visual-flags.mjs';\r\nimport {BIOMES,PORTAL_REST_SECONDS} from './biomes.mjs';\r\nimport {FLIGHT_CONTROLS_MARKUP,advanceCockpit,motorcycleWorld} from './cockpit.mjs';\r\nimport {biomeTheme as depthBiomeTheme,COCKPIT_MARKUP,pickupGeometry} from './biome-visuals.mjs';\r\nconst depthArt=visualFlag('flightArt');\r\nconst createBiomeScene=depthArt?createDepthScene:createLegacyScene;\r\nconst biomeTheme=depthArt?depthBiomeTheme:legacyBiomeTheme;\r\nimport {createFlightFX} from './flight-fx.mjs';\r\n\r\nconst STYLE_ID = 'plank-flight-style';\r\nconst SEGMENT_LENGTH = 12;\r\nconst SEGMENT_COUNT = 9;\r\nconst OBJECT_LIMIT = 52;\r\nconst COACH_LIMIT = 8;\r\nconst COACH_MODEL = 'roster/01-seed-pearo--cute_alien_figure_3d_model';\r\nconst COACH_URL = new URL(`../../creature/models/${COACH_MODEL}.glb`, import.meta.url).href;\r\nexport function createCoachAssetTracker(){\r\n  const geometries=new Set(),materials=new Set(),textures=new Set(),disposedResources=new WeakSet();let disposed=false;\r\n  const disposeResource=resource=>{if(!disposedResources.has(resource)){resource.dispose();disposedResources.add(resource);}};\r\n  const add=(set,resource)=>{if(!resource||typeof resource.dispose!=='function')return;if(disposed)disposeResource(resource);else set.add(resource);};\r\n  const trackGeometry=geometry=>add(geometries,geometry);\r\n  const trackTexture=texture=>add(textures,texture);\r\n  const trackMaterial=material=>{\r\n    add(materials,material);\r\n    for(const value of Object.values(material??{})){\r\n      if(value?.isTexture)trackTexture(value);\r\n      else if(Array.isArray(value))for(const item of value)if(item?.isTexture)trackTexture(item);\r\n    }\r\n  };\r\n  const dispose=()=>{\r\n    if(disposed)return false;disposed=true;\r\n    for(const texture of textures)disposeResource(texture);\r\n    for(const material of materials)disposeResource(material);\r\n    for(const geometry of geometries)disposeResource(geometry);\r\n    textures.clear();materials.clear();geometries.clear();return true;\r\n  };\r\n  return {geometries,materials,textures,trackGeometry,trackMaterial,trackTexture,dispose,get disposed(){return disposed;}};\r\n}\r\nconst finite = (n, fallback = 0) => Number.isFinite(+n) ? +n : fallback;\r\nconst clamp = (n, low, high) => Math.max(low, Math.min(high, n));\r\nconst esc = value => String(value ?? '').replace(/[&<>\"']/g, ch => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[ch]);\r\nconst title = value => String(value ?? '').replace(/[-_]/g, ' ').replace(/\\b\\w/g, c => c.toUpperCase());\r\nconst ownedIds = frame => (Array.isArray(frame?.profile?.owned) ? frame.profile.owned : Array.isArray(frame?.inventory) ? frame.inventory : [])\r\n  .map(v => typeof v === 'string' ? v : v?.id).filter(Boolean);\r\n\r\nfunction ensureStylesheet() {\r\n  if (document.getElementById(STYLE_ID)) return null;\r\n  const link = document.createElement('link');\r\n  link.id = STYLE_ID;\r\n  link.rel = 'stylesheet';\r\n  link.href = new URL(depthArt?'./flight.css':'./flight-legacy.css', import.meta.url).href;\r\n  document.head.append(link);\r\n  return link;\r\n}\r\n\r\nfunction tileTexture(THREE, theme) {\r\n  const canvas = document.createElement('canvas');\r\n  canvas.width = canvas.height = 256;\r\n  const g = canvas.getContext('2d');\r\n  const grad = g.createLinearGradient(0, 0, 256, 256);\r\n  grad.addColorStop(0, theme.wall.from);\r\n  grad.addColorStop(1, theme.wall.to);\r\n  g.fillStyle = grad;\r\n  g.fillRect(0, 0, 256, 256);\r\n  g.strokeStyle = 'rgba(255,255,255,.12)';\r\n  g.lineWidth = 2;\r\n  g.strokeRect(5, 5, 246, 246);\r\n  g.strokeStyle = 'rgba(0,0,0,.48)';\r\n  g.lineWidth = 5;\r\n  g.strokeRect(1, 1, 254, 254);\r\n  if (theme.wall.style === 'hex') {\r\n    g.strokeStyle = 'rgba(0,0,0,.32)';\r\n    g.lineWidth = 2;\r\n    for (let y = -24; y < 300; y += 42) for (let x = -24; x < 300; x += 48) {\r\n      g.beginPath();\r\n      for (let i = 0; i < 6; i++) {\r\n        const a = i * Math.PI / 3;\r\n        g[i ? 'lineTo' : 'moveTo'](x + Math.cos(a) * 25, y + Math.sin(a) * 25);\r\n      }\r\n      g.closePath(); g.stroke();\r\n    }\r\n  } else {\r\n    for (const y of [32, 128, 224]) {\r\n      g.fillStyle = 'rgba(0,0,0,.25)'; g.fillRect(0, y, 256, 4);\r\n      g.fillStyle = 'rgba(255,255,255,.15)'; g.fillRect(0, y + 4, 256, 2);\r\n    }\r\n    for (const [x, y] of [[18, 18], [238, 18], [18, 238], [238, 238]]) {\r\n      g.fillStyle = '#17131d'; g.beginPath(); g.arc(x, y, 5, 0, Math.PI * 2); g.fill();\r\n      g.fillStyle = 'rgba(255,255,255,.48)'; g.beginPath(); g.arc(x - 1, y - 1, 2, 0, Math.PI * 2); g.fill();\r\n    }\r\n    if (theme.wall.style === 'rust') {\r\n      for (let i = 0; i < 22; i++) {\r\n        g.fillStyle = `rgba(180,74,24,${.08 + (i % 4) * .025})`;\r\n        g.beginPath(); g.ellipse((i * 71) % 256, (i * 43) % 256, 10 + (i % 5) * 4, 3 + (i % 3) * 3, i, 0, Math.PI * 2); g.fill();\r\n      }\r\n    }\r\n  }\r\n  const texture = new THREE.CanvasTexture(canvas);\r\n  texture.colorSpace = THREE.SRGBColorSpace;\r\n  texture.wrapS = texture.wrapT = THREE.RepeatWrapping;\r\n  texture.repeat.set(1, 1);\r\n  texture.anisotropy = 2;\r\n  return texture;\r\n}\r\n\r\nfunction buildDom(host, onAction) {\r\n  const root = document.createElement('div');\r\n  root.className = 'plank-flight';\r\n  const visualStyle=document.createElement('style');visualStyle.textContent=VISUAL_CSS;root.append(visualStyle);\r\n  root.setAttribute('aria-label', 'Plank Flight workout game');\r\n  const canvas = document.createElement('canvas');\r\n  canvas.className = 'pf-canvas';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  const fallback = document.createElement('canvas');\r\n  fallback.className = 'pf-fallback-canvas';\r\n  fallback.setAttribute('aria-hidden', 'true');\r\n  const hud = document.createElement('div');\r\n  hud.className = 'pf-hud';\r\n  hud.innerHTML = `\r\n    <div class=\"pf-cockpit\" hidden>${COCKPIT_MARKUP}</div>\r\n    <header class=\"pf-topbar\">\r\n      <div class=\"pf-brand\"><span class=\"pf-brand-mark\" aria-hidden=\"true\">✦</span><span class=\"pf-sr\">PLANK FLIGHT</span></div>\r\n      <div class=\"pf-top-actions\"><button type=\"button\" class=\"pf-icon-button\" data-act=\"recalibrate\" aria-label=\"Recheck your form\" title=\"Recheck form\">↻</button><button type=\"button\" class=\"pf-icon-button\" data-act=\"pause\" aria-label=\"Pause flight\" title=\"Pause\">Ⅱ</button><button type=\"button\" class=\"pf-icon-button\" data-act=\"exit\" aria-label=\"Exit and turn camera off\" title=\"Exit\">×</button></div>\r\n    </header>\r\n    <div class=\"pf-stats\" aria-label=\"Flight status\">\r\n      <div class=\"pf-stat\">${symbolMarkup(\"distance\")}<span class=\"pf-sr\">Distance</span><strong data-distance>0 m</strong></div>\r\n      <div class=\"pf-stat\">${symbolMarkup(\"coin\")}<span class=\"pf-sr\">Flight coins</span><strong data-run-coins>0</strong></div>\r\n    </div>\r\n    <div class=\"pf-flight-fx\" data-flight-fx aria-hidden=\"true\"><span data-flight-fx-cue hidden></span><span data-flight-fx-active></span></div>\r\n    <span class=\"pf-sr\" data-fx-announcement role=\"status\" aria-live=\"polite\" aria-atomic=\"true\"></span>\r\n    <section class=\"pf-pace pf-sr\" data-pace hidden aria-label=\"Posture timing\" aria-live=\"polite\" aria-atomic=\"true\">\r\n      <div class=\"pf-pace-current\"><span class=\"pf-pace-eyebrow\">NOW</span><strong data-pace-current></strong><small data-pace-credit></small></div>\r\n      <div class=\"pf-pace-time\"><strong data-pace-remaining>0</strong><span>SEC</span></div>\r\n      <div class=\"pf-pace-next\"><span class=\"pf-pace-eyebrow\">NEXT</span><strong data-pace-next></strong></div>\r\n    </section>\r\n    <div class=\"pf-biome\" data-biome-hud hidden><div><span data-biome-name></span><strong class=\"pf-sr\" data-biome-section></strong></div><b data-biome-time></b><small class=\"pf-sr\" data-biome-pickups></small></div>\r\n    <div class=\"pf-live-mission\" data-mission hidden></div>\r\n    <div class=\"pf-turn\" data-turn hidden aria-live=\"polite\"></div>\r\n    <div class=\"pf-pickup-toast\" data-pickup-toast hidden role=\"status\"></div>\r\n    <div class=\"pf-fold-preview\" data-fold-preview hidden role=\"status\" aria-live=\"polite\" aria-atomic=\"true\"><i aria-hidden=\"true\"></i><span>Next spot</span></div>\r\n    <div class=\"pf-notice\" data-notice aria-hidden=\"true\" hidden></div>\r\n    <span class=\"pf-sr\" data-announcement role=\"status\" aria-live=\"polite\" aria-atomic=\"true\"></span>\r\n    <div class=\"pf-aim\" aria-hidden=\"true\"><i></i><b></b></div>\r\n    ${FLIGHT_CONTROLS_MARKUP}\r\n    <div class=\"pf-sr\" aria-hidden=\"true\"><span data-bank-coins></span><span data-shield></span><span data-stars></span><span data-vehicle></span><span data-vehicle-hint></span><span data-powerup></span><span data-effects></span></div>\r\n    <div class=\"pf-message\" data-message role=\"status\" aria-live=\"polite\"></div>\r\n    <div class=\"pf-launch-cue\" data-launch-cue aria-hidden=\"true\" hidden></div>\r\n    <section class=\"pf-panel\" data-panel hidden aria-label=\"Flight menu\"></section>\r\n  `;\r\n  root.append(canvas, fallback, hud);\r\n  host.classList.add('plank-flight-host');\r\n  host.prepend(root);\r\n  const panel = hud.querySelector('[data-panel]');\r\n  let panelName = 'calibration';\r\n  let latest = {};\r\n  let currentRun = {};\r\n  let cockpitState={};\r\n  let previousCue='';\r\n  let currentFX={};\r\n  const flightFX=createFlightFX();\r\n  const action = (name, payload) => onAction(name, payload);\r\n  const renderPanel = (name, frame = latest, run = {}) => {\r\n    panelName = name;\r\n    const pose = frame.pose ?? {};\r\n    const prompt = frame.message || pose.prompt || 'Bring your shoulders, elbows and wrists into view. Keep a comfortable plank and follow the simple prompts.';\r\n    const profile = frame.profile ?? {};\r\n    const missions = Array.isArray(run.missions) ? run.missions : [];\r\n    const items = Array.isArray(frame.shopItems) ? frame.shopItems : [];\r\n    const inventory = Array.isArray(frame.inventory) ? frame.inventory : [];\r\n    const owned = Array.isArray(profile.owned) ? profile.owned : inventory.map(v => typeof v === 'string' ? v : v?.id).filter(Boolean);\r\n    const loadout = Array.isArray(frame.loadout) ? frame.loadout : Array.isArray(profile.loadout) ? profile.loadout : [];\r\n    const listButton = (item, actionName, extra = '') => {\r\n      const id = item?.id ?? item?.key;\r\n      if (!id) return '';\r\n      const label = item.name ?? item.title ?? title(id);\r\n      const detail = item.description ?? item.summary ?? '';\r\n      const cost = item.cost ?? item.price ?? item.coins;\r\n      const selection = ['vehicle', 'cosmetic'].includes(item.kind);\r\n      const readyLabel = actionName === 'buy' ? `Get${cost != null ? ` · ${cost}` : ''}` : selection ? 'Select' : 'Equip';\r\n      return `<article class=\"pf-list-card\"><div><strong>${esc(label)}</strong><small>${esc(detail || (cost != null ? `${cost} device-only coins` : 'Ready to equip'))}</small></div><button type=\"button\" data-act=\"${actionName}\" data-id=\"${esc(id)}\" data-kind=\"${esc(item.kind ?? '')}\" data-ready-label=\"${esc(readyLabel)}\" ${extra}>${esc(readyLabel)}</button></article>`;\r\n    };\r\n    let content = '';\r\n    if (name === 'welcome') content = `<div class=\"pf-panel-icon\" aria-hidden=\"true\">✦</div><p class=\"pf-eyebrow pf-sr\">A MYR5 ARCADE FLIGHT</p><h1 class=\"pf-sr\">Find your line.</h1><p class=\"pf-sr\" data-prompt-text>${esc(prompt)}</p><div class=\"game-cue-row\" role=\"img\" aria-label=\"A higher comfortable plank lifts your path. Shift gently left and right to steer. Follow rings and avoid hazards.\">${symbolMarkup(\"plank\")}${symbolMarkup(\"lean\")}${symbolMarkup(\"target\")}</div><p class=\"pf-smallprint\">Coins are device-only and separate from exercise progress.</p>${run.phase === 'ready' && !run.headstartUsed ? '<button class=\"pf-secondary\" type=\"button\" data-act=\"headstart\">Use a head start</button>' : ''}<button class=\"pf-primary\" type=\"button\" data-act=\"start\" aria-label=\"Begin flight\" title=\"Begin flight\">${symbolMarkup(\"play\")}</button><button class=\"pf-secondary\" type=\"button\" data-act=\"recalibrate\" aria-label=\"Recheck form\" title=\"Recheck form\">${symbolMarkup(\"tracking\")}</button>`;\r\n    else if (name === 'pause') content = `<p class=\"pf-eyebrow\">SAFE BAY</p><h2>Take your time.</h2><p data-prompt-text>${esc(frame.message || 'Your flight and exercise progress are paused together.')}</p><button class=\"pf-primary\" type=\"button\" data-act=\"resume\">Continue</button><button class=\"pf-secondary\" type=\"button\" data-act=\"menu-shop\">Visit the shop</button><button class=\"pf-secondary\" type=\"button\" data-act=\"menu-loadout\">Choose equipment</button><button class=\"pf-secondary\" type=\"button\" data-act=\"finish\">Finish this flight</button><button class=\"pf-link\" type=\"button\" data-act=\"recalibrate\">Recheck form</button>`;\r\n    else if (name === 'crashed') content = `<p class=\"pf-eyebrow\">FLIGHT PAUSED</p><h2>Catch your breath.</h2><p>Your exercise progress is kept. Continue when you feel ready.</p><button class=\"pf-primary\" type=\"button\" data-act=\"revive\">Continue safely</button>${run.phase === 'crashed' && !run.finalBlastUsed ? '<button class=\"pf-secondary\" type=\"button\" data-act=\"final-blast\">One last burst</button>' : ''}<button class=\"pf-link\" type=\"button\" data-act=\"finish\">See results</button>`;\r\n    else if (name === 'results') content = `<p class=\"pf-eyebrow\">FLIGHT COMPLETE</p><h2>Nice steady work.</h2><div class=\"pf-result-grid\"><div><strong>${Math.floor(finite(run.distance))} m</strong><small>Distance</small></div><div><strong>${Math.floor(finite(run.coins))}</strong><small>Flight coins</small></div><div><strong>${Math.floor(finite(profile.stars))}</strong><small>Stars</small></div><div><strong>${Math.floor(finite(profile.bestDistance))} m</strong><small>Best flight</small></div></div><p class=\"pf-smallprint\">Coins stay in this device wallet. Device balance: <b data-wallet-value>${Math.floor(finite(profile.balance))}</b>. Exercise progress is tracked separately.</p>${missions.length ? `<div class=\"pf-missions\">${missions.slice(0, 3).map(m => `<div>${esc(m.label ?? m.name ?? title(m.id))}<span>${m.complete || m.done ? 'Complete' : `${Math.floor(finite(m.progress))}${m.target ? ` / ${esc(m.target)}` : ''}`}</span></div>`).join('')}</div>` : ''}${finite(profile.tokens) > 0 ? `<button class=\"pf-primary\" type=\"button\" data-act=\"spin\">Open your reward · ${Math.floor(finite(profile.tokens))}</button>` : ''}<button class=\"pf-secondary\" type=\"button\" data-act=\"menu-shop\">Visit the shop</button><button class=\"pf-secondary\" type=\"button\" data-act=\"menu-loadout\">Choose equipment</button><button class=\"pf-secondary\" type=\"button\" data-act=\"new-run\">Fly another route</button><button class=\"pf-secondary\" type=\"button\" data-act=\"exit\">Finish workout</button>`;\r\n    else if (name === 'shop') content = `<p class=\"pf-eyebrow\">DEVICE WALLET · NOT FITNESS XP</p><h2>Flight shop</h2><p>Choose small upgrades and looks for future flights. Purchases use device-only coins. Balance: <b data-wallet-value>${Math.floor(finite(profile.balance))}</b></p><div class=\"pf-list\">${items.length ? items.map(i => listButton(i, 'buy')).join('') : '<p class=\"pf-empty\">The shop will appear here when items are available.</p>'}</div><button class=\"pf-secondary\" type=\"button\" data-act=\"event\">Try a themed route</button><button class=\"pf-link\" type=\"button\" data-act=\"back\">Back</button>`;\r\n    else if (name === 'loadout') {\r\n      const equipped = new Set([...loadout.map(v => typeof v === 'string' ? v : v?.id), profile.selectedVehicle, profile.selectedCosmetic].filter(Boolean));\r\n      const itemsOwned = owned.map(v => typeof v === 'string' ? items.find(i => i.id === v) ?? { id: v } : v)\r\n        .filter(i => i && (i.equipable !== false || ['vehicle', 'cosmetic'].includes(i.kind)));\r\n      content = `<p class=\"pf-eyebrow\">YOUR FLIGHT SETUP</p><h2>Choose equipment</h2><p>Your choices affect the game route only.</p><div class=\"pf-list\">${itemsOwned.length ? itemsOwned.map(i => listButton(i, 'equip', equipped.has(i.id) ? 'aria-pressed=\"true\"' : '')).join('') : '<p class=\"pf-empty\">Equipment you own will appear here.</p>'}</div><button class=\"pf-link\" type=\"button\" data-act=\"back\">Back</button>`;\r\n    }\r\n    if(name==='portal')content=`<div class=\"pf-portal-seal\" aria-hidden=\"true\">MOM<br>INC.</div><p class=\"pf-eyebrow\">BIOME ${Math.floor(finite(run.biome?.index))+1} COMPLETE · SAFE PORTAL BAY</p><h2>Shake it out.</h2><p>Lower down, relax your hands and shake your arms out. Flight and exercise credit are paused. Take as long as you need.</p><p class=\"pf-next-world\">NEXT / ${esc(run.biome?.nextName ?? 'Your next world')}</p><button class=\"pf-primary\" type=\"button\" data-act=\"continue-biome\">Continue to next biome</button><p class=\"pf-smallprint\" data-portal-wait>Rest for a moment before continuing.</p><button class=\"pf-secondary\" type=\"button\" data-act=\"finish\">Finish this flight</button><button class=\"pf-link\" type=\"button\" data-act=\"exit\">End workout and turn camera off</button>`;\r\n    if(name==='welcome')content+='<p class=\"pf-smallprint\">Six worlds · 90 seconds each · three sections · a shake-out portal between worlds. Keep facing your camera. Follow the posture cues as you fly forward.</p><button class=\"pf-link\" type=\"button\" data-act=\"exit\">End workout and turn camera off</button>';\r\n    if(['pause','results'].includes(name))content+='<p class=\"pf-smallprint\" data-progression></p>';\r\n    panel.innerHTML = `<div class=\"pf-panel-scroll\"><button type=\"button\" class=\"pf-close\" data-act=\"close-panel\" aria-label=\"Close menu\">×</button>${content}</div>`;\r\n    panel.hidden = false;\r\n    panel.dataset.view = name;\r\n    panel.querySelectorAll('[data-act]').forEach(button => button.addEventListener('click', () => {\r\n      const act = button.dataset.act;\r\n      if(act==='continue-biome'){Promise.resolve(action(act)).then(()=>{if(run.phase!=='portal'){panelName='calibration';panel.hidden=true;}});return;}\r\n      if (act === 'menu-shop') { action('pause', { reason: 'menu' }); renderPanel('shop', latest, run); return; }\r\n      if (act === 'menu-loadout') { action('pause', { reason: 'menu' }); renderPanel('loadout', latest, run); return; }\r\n      if (act === 'back') { renderPanel(run.phase === 'finalized' ? 'results' : 'pause', latest, run); return; }\r\n      if (act === 'close-panel') { if(panelName==='welcome')panelName='calibration';panel.hidden = true; return; }\r\n      if (act === 'menu') { action('pause', { reason: 'menu' }); renderPanel('pause', latest, run); return; }\r\n      if (act === 'start') { panelName='calibration';panel.hidden=true;action('start');return; }\r\n      if (act === 'resume') { panel.hidden = true; action('resume'); return; }\r\n      if (act === 'revive') { panel.hidden = true; action('revive'); return; }\r\n      if (act === 'finish') { action('finish'); return; }\r\n      if (act === 'recalibrate') { action('recalibrate');panelName='calibration';panel.hidden=true;return; }\r\n      if (act === 'buy' || act === 'equip') { action(act, { id: button.dataset.id }); return; }\r\n      if (act === 'event') { action('event', { enabled: true }); panel.hidden = true; return; }\r\n      if (act === 'spin' || act === 'headstart' || act === 'final-blast') { action(act); return; }\r\n      if (act === 'exit' || act === 'new-run') { action(act); return; }\r\n      if (act === 'pause') { action('pause'); renderPanel('pause', latest, run); return; }\r\n    }));\r\n  };\r\n  hud.addEventListener('click', event => {\r\n    const button = event.target.closest('[data-act]');\r\n    if (!button || panel.contains(button)) return;\r\n    const act = button.dataset.act;\r\n    if (act === 'pause' || act === 'menu') { action('pause', { reason: 'user' }); renderPanel('pause', latest, currentRun); }\r\n    if (act === 'recalibrate') { action('recalibrate');panelName='calibration';panel.hidden=true; }\r\n    if (act === 'resume') { action('resume'); panel.hidden = true; }\r\n    if (act === 'exit') action('exit');\r\n  });\r\n  const ui = {\r\n    root, canvas, fallback, hud, panel, renderPanel, get fx(){return currentFX;},\r\n    update(frame, run) {\r\n      latest = frame;\r\n      currentRun = run;\r\n      const pose = frame.pose ?? {};\r\n      const biome=run.biome;\r\n      const fx=flightFX.update(run,frame.profile??{});\r\n      currentFX=fx;\r\n      const cueKey=fx.cue?`${fx.cue.icon}:${fx.cue.label}:${fx.cue.until}`:'';\r\n      const cueNode=hud.querySelector('[data-flight-fx-cue]');\r\n      cueNode.hidden=!fx.cue;\r\n      if(fx.cue){cueNode.dataset.fxKind=fx.cue.icon;const icon=({orbit:'magnet',vehicle:'board'})[fx.cue.icon]??fx.cue.icon,value=fx.cue.label.match(/\\+\\d+|\\d+\\/\\d+/)?.[0]??'';const markup=`${symbolMarkup(icon)}<b class=\"pf-sr\">${esc(fx.cue.label)}</b>${value?`<small>${esc(value)}</small>`:''}`;if(cueNode.innerHTML!==markup)cueNode.innerHTML=markup;cueNode.title=fx.cue.label;}else delete cueNode.dataset.fxKind;\r\n      if(cueKey&&cueKey!==previousCue&&fx.cue.icon!=='coin')hud.querySelector('[data-fx-announcement]').textContent=fx.cue.label.toLocaleLowerCase();\r\n      previousCue=cueKey;\r\n      const activeMarkup=fx.active.map(item=>`<span class=\"pf-fx-chip\" data-fx-kind=\"${esc(item.icon)}\" title=\"${esc(item.label)}\">${symbolMarkup(({orbit:'magnet'})[item.icon]??item.icon)}<small>${Math.ceil(item.remaining)}s</small></span>`);\r\n      if(fx.shield)activeMarkup.push(`<span class=\"pf-fx-chip is-protection\" data-fx-kind=\"shield\" title=\"Shield\">${symbolMarkup('shield')}<small>${fx.shield}</small></span>`);\r\n      if(fx.coinBonus)activeMarkup.push(`<span class=\"pf-fx-chip is-multiplier\" data-fx-kind=\"coin\" title=\"Coin bonus\">${symbolMarkup('coin')}<small>+${fx.coinBonus}</small></span>`);\r\n      if(fx.vehicle)activeMarkup.push(`<span class=\"pf-fx-chip is-vehicle\" data-fx-kind=\"vehicle\" title=\"Vehicle armor\">${symbolMarkup('board')}<small>${Array.from({length:fx.vehicleMaxHealth},(_,index)=>index<fx.vehicleHealth?'●':'○').join('')}</small></span>`);\r\n      const activeNode=hud.querySelector('[data-flight-fx-active]'),activeHTML=activeMarkup.join('');\r\n      if(activeNode.innerHTML!==activeHTML)activeNode.innerHTML=activeHTML;\r\n      root.classList.toggle('is-magnet-active',fx.magnet);\r\n      root.classList.toggle('is-shielded',fx.protected);\r\n      root.classList.toggle('has-impact-buffer',fx.impactBuffer>0);\r\n      root.classList.toggle('is-quiet-vector',fx.loadout.includes('quiet-vector'));\r\n      root.classList.toggle('is-early-signal',fx.loadout.includes('early-signal')||fx.upgrades.includes('signal-relay'));\r\n      root.classList.toggle('is-rescue-echo',fx.loadout.includes('rescue-echo'));\r\n      root.classList.toggle('is-cash-dash',run.powerup?.family==='cash-dash');\r\n      root.classList.toggle('is-slow-orbit',fx.slowOrbit);\r\n      root.dataset.vehicleArmor=`${fx.vehicleHealth}/${fx.vehicleMaxHealth}`;\r\n      root.dataset.vehicle=fx.vehicle??'';\r\n      root.dataset.fxCue=fx.cue?.icon??'';\r\n      if(fx.tint)root.style.setProperty('--pf-loadout-tint',fx.tint);else root.style.removeProperty('--pf-loadout-tint');\r\n      root.dataset.biome=biome?.id??'';\r\n      root.dataset.section=String(biome?.section??0);\r\n      root.dataset.phase=run.phase;\r\n      const biomeHud=hud.querySelector('[data-biome-hud]');\r\n      biomeHud.hidden=!biome||run.phase==='ready';\r\n      hud.querySelector('.pf-stats').hidden=run.phase==='ready';\r\n      hud.querySelector('.pf-cockpit').hidden=biome?.id!=='asteroid';\r\n      if(biome){\r\n        setVisualCue(hud.querySelector('[data-biome-name]'),({tropical:'crate',castle:'flame',rainbow:'ring',loopway:'speed',laboratory:'jetpack',asteroid:'target'})[biome.id]||'gate',biome.name,biome.name);\r\n        hud.querySelector('[data-biome-section]').textContent=`${biome.section+1}/3 · ${biome.sectionName}`;\r\n        hud.querySelector('[data-biome-time]').textContent=`${Math.ceil(biome.remaining)}s`;\r\n        const definition=BIOMES.find(b=>b.id===biome.id);\r\n        hud.querySelector('[data-biome-pickups]').textContent=definition?.pickups.map(p=>p.name).join(' · ')??'';\r\n      }\r\n      const turn=hud.querySelector('[data-turn]');\r\n      turn.hidden=true;\r\n      turn.textContent=biome?.id==='asteroid'?'HYPERSPACE · NEXT SECTOR':'AUTO TURN · KEEP FACING CAMERA';\r\n      const pickup=hud.querySelector('[data-pickup-toast]');\r\n      pickup.hidden=!run.lastBiomePickup||run.lastBiomePickup.until<=run.time;\r\n      if(!pickup.hidden)setVisualCue(pickup,({speed:'speed',magnet:'magnet',shield:'shield',coins:'coin',lift:'coin',rainbow:'shield'})[run.lastBiomePickup.benefit]||'star',`${run.lastBiomePickup.name}: ${({speed:'Speed boost',magnet:'Coin pull',shield:'Shield',coins:'Coin cache',lift:'+6 coins',rainbow:'Six seconds of protection'})[run.lastBiomePickup.benefit]||'Active'}`,run.lastBiomePickup.benefit==='rainbow'?'6s':'');\r\n      const notice = hud.querySelector('[data-notice]');\r\n      const message = frame.message || pose.prompt || '';\r\n      const launchCue=hud.querySelector('[data-launch-cue]');\r\n      const launchWord=['READY','SET','GO'].includes(String(frame.launchCue?.word??'').toUpperCase())?String(frame.launchCue.word).toUpperCase():'';\r\n      const launchOpacity=clamp(finite(frame.launchCue?.opacity),0,1);\r\n      launchCue.hidden=!launchWord||launchOpacity<=.005;\r\n      setVisualCue(launchCue,launchWord==='GO'?'play':launchWord==='SET'?'set':'ready',launchWord);\r\n      launchCue.style.opacity=String(launchOpacity);\r\n      const bad = !pose.controlValid || frame.paused;\r\n      const entryHint=!pose.calibrated&&!pose.restFallback&&['ready','paused'].includes(run.phase);\r\n      const actionableError=/permission|unavailable|graphics|webgl|denied|could not start/i.test(message);\r\n      const countdownHint=/^Ready in \\d+/i.test(message);\r\n      const positionNeeded=!pose.controlValid;\r\n      host.classList.toggle('plank-flight-calibrating',entryHint);\r\n      const quietHint=entryHint||countdownHint||positionNeeded;\r\n      const showNotice=actionableError||(bad&&!quietHint&&positionNeeded);\r\n      notice.hidden = !showNotice;\r\n      if(actionableError){notice.textContent=message;delete notice.dataset.visualCue;}else setVisualCue(notice,frame.paused?'pause':'tracking',frame.paused?'Paused safely':'Move into view');\r\n      const positionHint=hud.querySelector('[data-message]');\r\n      positionHint.classList.toggle('is-position-hint',quietHint&&!actionableError);\r\n      if(actionableError||!(entryHint||positionNeeded||countdownHint)){positionHint.replaceChildren();delete positionHint.dataset.visualCue;}else setVisualCue(positionHint,pose.restFallback&&positionNeeded?'rest':countdownHint?'pause':'plank',pose.restFallback&&positionNeeded?'Settling into rest?':countdownHint?message:'Get into a comfortable plank in camera view',countdownHint?message.match(/\\d+/)?.[0]||'':'');\r\n      const steer=clamp(finite(pose.steer),-1,1);\r\n      cockpitState=advanceCockpit(cockpitState,{time:finite(run.time),phase:run.phase,paused:frame.paused,pose,height:finite(run.y),boost:fx.boostFactor});\r\n      for(const thruster of hud.querySelectorAll('[data-thruster]')){\r\n        thruster.style.setProperty('--pf-heat-level',String(cockpitState.heat));thruster.dataset.heat=cockpitState.heat.toFixed(3);thruster.dataset.firing=String(cockpitState.firing);\r\n        thruster.dataset.boosting=String(cockpitState.boost>0);thruster.style.setProperty('--pf-boost-factor',String(cockpitState.boost));\r\n        thruster.style.setProperty('--pf-thruster-color',`rgb(${Math.round(95+160*cockpitState.heat)} ${Math.round(145-85*cockpitState.heat)} ${Math.round(175-145*cockpitState.heat)})`);\r\n        thruster.style.setProperty('--pf-thruster-glow',String(.12+cockpitState.heat*.6));\r\n        thruster.classList.toggle('is-steering',thruster.dataset.thruster==='left'?steer<-.12:steer>.12);\r\n      }\r\n      const motorcycle=hud.querySelector('[data-motorcycle]'),riding=motorcycleWorld(biome?.id);\r\n      motorcycle.hidden=!riding;motorcycle.dataset.wheelie=cockpitState.wheelie.toFixed(3);\r\n      const stuntProgress=run.stunt?.active?clamp(finite(run.stunt.progress),0,1):0;\r\n      const hop=Math.sin(stuntProgress*Math.PI);\r\n      motorcycle.style.transform=`translate(-50%,${14-cockpitState.wheelie*24-hop*18}px) rotate(${steer*3}deg)`;\r\n      motorcycle.style.setProperty('--pf-wheelie',String(cockpitState.wheelie));\r\n      const stuntHud=hud.querySelector('[data-stunt]');stuntHud.hidden=!run.stunt?.active||run.phase!=='flying';\r\n      stuntHud.style.setProperty('--pf-stunt-spin',`${stuntProgress*360}deg`);\r\n      root.dataset.driveMode=riding?'motorcycle':'jetpack';\r\n      root.dataset.steer=steer<-.12?'left':steer>.12?'right':'neutral';\r\n      hud.querySelector('[data-distance]').textContent = `${Math.floor(finite(run.distance))} m`;\r\n      hud.querySelector('[data-run-coins]').textContent = String(Math.floor(finite(run.coins)));\r\n      const banked = Math.floor(finite(frame.coinsBanked ?? frame.profile?.balance));\r\n      const pending = Math.floor(finite(frame.pendingCoins));\r\n      hud.querySelector('[data-bank-coins]').textContent = `${banked}${pending > 0 ? ` · +${pending}` : ''}`;\r\n      const wallet = panel.querySelectorAll('[data-wallet-value]');\r\n      for (const target of wallet) if (target.textContent !== String(banked)) target.textContent = String(banked);\r\n      if(panelName==='results'){\r\n        const tokens=Math.max(0,Math.floor(finite(frame.profile?.tokens)));\r\n        let reward=panel.querySelector('[data-act=\"spin\"]');\r\n        if(tokens&&!reward){reward=document.createElement('button');reward.type='button';reward.className='pf-primary';reward.dataset.act='spin';reward.addEventListener('click',()=>action('spin'));panel.querySelector('[data-act=\"menu-shop\"]')?.before(reward);}\r\n        if(reward){reward.hidden=!tokens;reward.textContent=`Open your reward · ${tokens}`;}\r\n        const totals=panel.querySelectorAll('.pf-result-grid strong');\r\n        if(totals[2])totals[2].textContent=String(Math.floor(finite(frame.profile?.stars)));\r\n        if(totals[3])totals[3].textContent=`${Math.floor(finite(frame.profile?.bestDistance))} m`;\r\n      }\r\n      const pace = run.pace;\r\n      const pacePanel = hud.querySelector('[data-pace]');\r\n      const hasPace = pose.calibrated && ['flying','paused'].includes(run.phase) && pace && typeof pace === 'object' && (pace.kind || pace.label || Number.isFinite(+pace.remaining));\r\n      pacePanel.hidden = !hasPace;\r\n      if (hasPace) {\r\n        const isRest = value => /rest|recover/i.test(String(value ?? ''));\r\n        const cue = (kind, label) => isRest(kind) || isRest(label) ? 'REST · CHEST DOWN' : String(label || title(kind) || 'Steady plank').toLocaleUpperCase();\r\n        const resting = isRest(pace.kind) || isRest(pace.label);\r\n        const currentCue = cue(pace.kind, pace.label);\r\n        const nextCue = cue(pace.nextKind, pace.nextLabel || 'Continue');\r\n        pacePanel.querySelector('[data-pace-current]').textContent = currentCue;\r\n        pacePanel.querySelector('[data-pace-next]').textContent = nextCue;\r\n        pacePanel.querySelector('[data-pace-remaining]').textContent = String(Math.max(0, Math.ceil(finite(pace.remaining))));\r\n        pacePanel.querySelector('[data-pace-credit]').textContent = frame.paused ? 'Paused · posture timer stopped' : resting ? (pose.resting ? 'Recovery · workout credit paused · flight continues' : 'Lower fully to rest · flight continues') : pose.resting ? 'Resting · rise when ready' : (pace.transitioning ? 'Changing posture' : 'Hold a comfortable position');\r\n        pacePanel.classList.toggle('is-rest', resting);\r\n        pacePanel.classList.toggle('is-transitioning', !!pace.transitioning);\r\n      }\r\n      const promptTarget = panel.querySelector('[data-prompt-text]');\r\n      const promptText = frame.message || pose.prompt || (panelName === 'pause' ? 'Your flight and exercise progress are paused together.' : 'Bring your shoulders, elbows and wrists into view. Keep a comfortable plank and follow the simple prompts.');\r\n      if (promptTarget && promptTarget.textContent !== promptText) promptTarget.textContent = promptText;\r\n      const shield = Math.max(0, finite(run.shield));\r\n      hud.querySelector('[data-shield]').textContent = shield ? `${shield} layer${shield === 1 ? '' : 's'}` : 'None';\r\n      hud.querySelector('[data-stars]').textContent = String(Math.floor(finite(frame.profile?.stars)));\r\n      const vehicle = run.vehicle ?? frame.profile?.selectedVehicle;\r\n      hud.querySelector('[data-vehicle]').textContent = vehicle ? `RIDE · ${title(vehicle.name ?? vehicle.id ?? vehicle)} · ${fx.vehicleHealth}/${fx.vehicleMaxHealth}` : '';\r\n      const vehicleHint = hud.querySelector('[data-vehicle-hint]');\r\n      const hintText = run.vehicle && typeof run.vehicleHint === 'string' ? run.vehicleHint : '';\r\n      vehicleHint.hidden = !hintText;\r\n      if (vehicleHint.textContent !== hintText) vehicleHint.textContent = hintText;\r\n      const foldPreview = run.foldPreview;\r\n      const foldMarker = hud.querySelector('[data-fold-preview]');\r\n      const foldX = Number(foldPreview?.x), foldY = Number(foldPreview?.y);\r\n      const hasFoldDestination = Number.isFinite(foldX) && Number.isFinite(foldY);\r\n      foldMarker.hidden = !hasFoldDestination;\r\n      if (hasFoldDestination) {\r\n        const blocked = foldPreview.clear === false;\r\n        foldMarker.classList.toggle('is-blocked', blocked);\r\n        const foldLabel = foldMarker.querySelector('span');\r\n        const foldText = blocked ? 'Wait for a clear path' : 'Next spot';\r\n        if (foldLabel.textContent !== foldText) foldLabel.textContent = foldText;\r\n        const width = root.clientWidth || 1, height = root.clientHeight || 1;\r\n        const ahead = Math.max(2, finite(foldPreview.ahead, 8));\r\n        const halfHeight = ahead * Math.tan(76 * Math.PI / 360);\r\n        const left = width / 2 + (foldX - finite(run.x)) * 2.12 * height / (2 * halfHeight);\r\n        const top = height / 2 - (foldY - finite(run.y, .5)) * 3.35 * height / (2 * halfHeight);\r\n        foldMarker.style.left = `${left}px`;\r\n        foldMarker.style.top = `${top}px`;\r\n      }\r\n      hud.querySelector('[data-powerup]').textContent = run.powerup ? `ACTIVE · ${title(run.powerup.name ?? run.powerup.id ?? run.powerup)}` : '';\r\n      const effects = run.effects ?? {};\r\n      const marks = [];\r\n      if (finite(effects.coinPreview) > 0) marks.push('ROUTE LANTERN: EARLY COINS');\r\n      if (finite(effects.warningLead) > 0) marks.push('SIGNAL: EARLY WARNINGS');\r\n      if (finite(effects.magnetRadius) > 0) marks.push('ORBIT MAGNET: PULL');\r\n      if (run.powerup?.effect?.visualRush) marks.push('AFTERBURN: ACTIVE');\r\n      if (fx.slowOrbit) marks.push('SLOW ORBIT: MISSILE THRESHOLDS ×1.7 EARLIER');\r\n      if (finite(run.biomeEffects?.rainbowUntil)>run.time)marks.push('RAINBOW MODE: PROTECTED');\r\n      const gearLabels={\r\n       'orbit-magnet':`coin pull radius ${finite(effects.magnetRadius).toFixed(2)}`,'quiet-vector':'vertical response +0.10',\r\n       'spark-dampener':`initial shield ${finite(effects.initialShield)}`,'early-signal':`warnings ${finite(effects.warningLead).toFixed(1)}s earlier`,\r\n       'coin-prism':`coin value +${finite(effects.coinBonus)}`,'rescue-echo':`impact buffer ${fx.impactBuffer} · crash grace +0.30s`,\r\n       'shell-courier':'starts in Comet Hopper','dash-battery':'power duration +2s','route-lantern':`coin preview +${finite(effects.coinPreview)}m`,\r\n       'lucky-signal':'event only · inactive','component-call':'event only · inactive','sideband-collector':`flyby coins +${finite(effects.npcBonus)}`\r\n      };\r\n      const upgradeLabels={'magnet-tuning':`magnet radius +${(finite(run.upgrades?.['magnet-tuning'])*.08).toFixed(2)}`,'shield-capacitor':`shield cap ${finite(effects.maxShield)} · start ${finite(effects.initialShield)}`,'vehicle-reserve':`vehicle time +${finite(effects.vehicleDuration)}s`,'signal-relay':`warnings ${finite(effects.warningLead).toFixed(1)}s earlier`};\r\n      marks.push(...fx.loadout.map(id=>`${title(id)}: ${gearLabels[id]??'equipped'}`),...fx.upgrades.map(id=>`${title(id)}: ${upgradeLabels[id]??'permanent'}`));\r\n      if(fx.cosmetic)marks.push(`${title(fx.cosmetic)}: cosmetic tint only`);\r\n      if(fx.blocked)marks.push('event-only equipment inactive');\r\n      const effectsTarget = hud.querySelector('[data-effects]');\r\n      const effectText = marks.join('  /  ');\r\n      if (effectsTarget.textContent !== effectText) effectsTarget.textContent = effectText;\r\n      const profile = frame.profile ?? {};\r\n      const objectives = Array.isArray(run.missions) ? run.missions : [];\r\n      const objective = objectives.find(m => !m.complete && !m.done) ?? null;\r\n      const mission = hud.querySelector('[data-mission]');\r\n      const upgradesValue = profile.upgrades ?? profile.ownedUpgrades ?? [];\r\n      const upgrades = Array.isArray(upgradesValue) ? upgradesValue : Object.keys(upgradesValue ?? {});\r\n      const upgradesText = upgrades.length ? ` · UPGRADES ${upgrades.length}` : '';\r\n      const rank = Math.max(1, Math.floor(finite(profile.rank, 1)));\r\n      const prestige = Math.max(0, Math.floor(finite(profile.prestige)));\r\n      const rankStars = Math.max(0, Math.floor(finite(profile.rankStarsProgress)));\r\n      const achievementNames = Array.isArray(profile.achievements) ? profile.achievements.map(title) : [];\r\n      const achievementText = achievementNames.length ? achievementNames.join(', ') : '0';\r\n      const daily = profile.dailyComponents ?? {};\r\n      const dailyText = `${Math.max(0, Math.floor(finite(daily.progress)))}/5 · ${Math.max(0, Math.floor(finite(daily.totalDays)))} days · ${Math.max(0, Math.floor(finite(daily.rewards)))} rewards`;\r\n      const missionStars = objectives.filter(m => m.complete || m.done).reduce((total,m)=>total+finite(m.stars,1),0);\r\n      const rankText = `RANK ${rank} · ${rankStars}/3 STARS${upgradesText}`;\r\n      const progression=panel.querySelector('[data-progression]');\r\n      if(progression)progression.textContent=`Rank ${rank} · Prestige ${prestige} · Flight mission stars ${missionStars}. Achievements: ${achievementText}. Component days: ${dailyText}. Original MYR5 reward: 100 device coins every five qualifying days; consecutive days are not required.`;\r\n      const eventText = run.event ? ` · EVENT TRACK ${Math.floor(finite(run.eventRewardProgress))}/${Math.floor(finite(run.eventRewardTarget,3))}${run.eventRewardClaimed ? ' · CACHE EARNED' : ' · 15 COIN CACHE'}` : '';\r\n      mission.hidden = false;\r\n      mission.textContent = `${rankText}${objective ? ` · GOAL ${objective.label ?? objective.name ?? title(objective.id)}${objective.target != null ? ` ${finite(objective.progress).toFixed(1)}/${objective.target}` : ''}` : ''}${eventText}`;\r\n      if (run.phase === 'portal' && (panel.hidden || panelName !== 'portal')) renderPanel('portal', frame, run);\r\n      else if (run.phase === 'crashed' && (panel.hidden || panelName !== 'crashed')) renderPanel('crashed', frame, run);\r\n      else if (run.phase === 'finalized' && (panel.hidden || !['results', 'shop', 'loadout'].includes(panelName))) renderPanel('results', frame, run);\r\n      else if (run.phase === 'ready' && panel.hidden && panelName === 'welcome') renderPanel('welcome', frame, run);\r\n      else if (run.phase === 'paused' && panel.hidden && panelName!=='calibration') renderPanel('pause', frame, run);\r\n      else if (run.phase === 'flying' && !frame.paused && ['welcome', 'pause', 'crashed'].includes(panelName)) panel.hidden = true;\r\n      const portalContinue=panel.querySelector('[data-act=\"continue-biome\"]');\r\n      if(portalContinue){const wait=Math.max(0,Math.ceil(PORTAL_REST_SECONDS-finite(biome?.restElapsed)));portalContinue.disabled=wait>0;const text=panel.querySelector('[data-portal-wait]');if(text)text.textContent=wait?`Shake your arms out · ${wait}s`:'Ready when you are. Return into view; the next flight starts after a position check and countdown.';}\r\n      const buyButtons = panel.querySelectorAll('[data-act=\"buy\"]');\r\n      for (const button of buyButtons) {\r\n        const item = Array.isArray(frame.shopItems) ? frame.shopItems.find(v => v.id === button.dataset.id) : null;\r\n        const cost = item?.cost ?? item?.price ?? item?.coins;\r\n        const upgradeLevel = finite(item?.currentLevel, finite(frame.profile?.upgrades?.[button.dataset.id]));\r\n        const upgradeCap = finite(item?.maxLevel, Infinity);\r\n        const isUpgrade = item?.kind === 'upgrade' || button.dataset.kind === 'upgrade';\r\n        const isOwned = isUpgrade ? upgradeLevel >= upgradeCap : ownedIds(frame).includes(button.dataset.id);\r\n        if (cost != null) button.dataset.readyLabel = `Get · ${cost}`;\r\n        if (button.disabled !== isOwned) button.disabled = isOwned;\r\n        const label = isOwned ? (isUpgrade ? 'Max level' : 'Owned') : button.dataset.readyLabel;\r\n        if (button.textContent !== label) button.textContent = label;\r\n        const description = item?.description ?? item?.summary ?? (isUpgrade ? `Level ${upgradeLevel} of ${upgradeCap}` : cost != null ? `${cost} device-only coins` : 'Ready to equip');\r\n        const detail = button.closest('.pf-list-card')?.querySelector('small');\r\n        if (detail && detail.textContent !== description) detail.textContent = description;\r\n      }\r\n      for (const button of panel.querySelectorAll('[data-act=\"headstart\"]')) {\r\n        const used = Boolean(run.headstartUsed) || run.phase !== 'ready';\r\n        button.disabled = used;\r\n        if (used && button.textContent !== 'Used') button.textContent = 'Used';\r\n      }\r\n      for (const button of panel.querySelectorAll('[data-act=\"final-blast\"]')) {\r\n        const used = Boolean(run.finalBlastUsed) || run.phase !== 'crashed';\r\n        button.disabled = used;\r\n        if (used && button.textContent !== 'Used') button.textContent = 'Used';\r\n      }\r\n      const slots = profile.gadgetSlots && typeof profile.gadgetSlots === 'object' ? (Array.isArray(profile.gadgetSlots) ? profile.gadgetSlots : Object.values(profile.gadgetSlots)) : [];\r\n      const equippedIds = new Set([\r\n        ...(Array.isArray(frame.loadout) ? frame.loadout : Array.isArray(profile.loadout) ? profile.loadout : []).map(v => typeof v === 'string' ? v : v?.id),\r\n        ...slots.map(v => typeof v === 'string' ? v : v?.id), profile.selectedVehicle, profile.selectedCosmetic\r\n      ].filter(Boolean));\r\n      for (const button of panel.querySelectorAll('[data-act=\"equip\"]')) {\r\n        const equipped = equippedIds.has(button.dataset.id);\r\n        if (button.getAttribute('aria-pressed') !== String(equipped)) button.setAttribute('aria-pressed', String(equipped));\r\n        const selected = ['vehicle', 'cosmetic'].includes(button.dataset.kind);\r\n        const label = equipped ? (selected ? 'Selected' : 'Equipped') : button.dataset.readyLabel;\r\n        if (button.textContent !== label) button.textContent = label;\r\n      }\r\n      return { panelName };\r\n    },\r\n    setTheme(theme) {\r\n      root.style.setProperty('--pf-accent', theme.neon.rail);\r\n      root.style.setProperty('--pf-warm', theme.neon.warm);\r\n      root.style.setProperty('--pf-alert', theme.neon.emergency);\r\n      root.style.setProperty('--pf-bg', theme.bg);\r\n      root.style.setProperty('--pf-fog', theme.fog);\r\n      root.dataset.theme = theme.name;\r\n    },\r\n    dispose() { root.remove(); host.classList.remove('plank-flight-host','plank-flight-calibrating'); }\r\n  };\r\n  panel.hidden = true;\r\n  return ui;\r\n}\r\n\r\nfunction makeRampGeometry(T){\r\n  const geometry=new T.BufferGeometry(),positions=[],colors=[];\r\n  const points=[[-2.15,0,1.7],[2.15,0,1.7],[-2.15,0,-1.7],[2.15,0,-1.7],[-2.15,.65,-1.7],[2.15,.65,-1.7]];\r\n  for(const [indices,color] of [[[0,1,5,0,5,4],'#ffd05b'],[[0,4,2],'#b57032'],[[1,3,5],'#e6973d'],[[2,4,5,2,5,3],'#573f38'],[[0,2,3,0,3,1],'#573f38']]){\r\n    const c=new T.Color(color);for(const i of indices){positions.push(...points[i]);colors.push(c.r,c.g,c.b);}\r\n  }\r\n  geometry.setAttribute('position',new T.Float32BufferAttribute(positions,3));geometry.setAttribute('color',new T.Float32BufferAttribute(colors,3));geometry.computeVertexNormals();return geometry;\r\n}\r\n\r\nfunction makeScene(THREE, theme, biome=null) {\r\n  const scene = new THREE.Scene();\r\n  scene.background = new THREE.Color(theme.bg);\r\n  scene.fog = new THREE.Fog(theme.fog, biome&&['rainbow','asteroid','tropical','loopway'].includes(biome.id)?40:18, biome?118:82);\r\n  const camera = new THREE.PerspectiveCamera(76, 1, .08, 130);\r\n  const hemi = new THREE.HemisphereLight(theme.hemi[0], theme.hemi[1], 1.15);\r\n  const key = new THREE.PointLight(theme.light, 18, 28, 1.7); key.position.set(0, 4.3, -13);\r\n  const fill = new THREE.PointLight(theme.neon.rail, 8, 18, 1.8); fill.position.set(0, 1.2, -22);\r\n  const side = new THREE.DirectionalLight(theme.light, .58); side.position.set(-7, 8, -4);\r\n  scene.add(hemi, key, fill, side);\r\n  const scenery=new THREE.Group();scenery.name='plank-flight-scenery';scene.add(scenery);\r\n  const wallMap = tileTexture(THREE, theme);\r\n  const wallMat = new THREE.MeshStandardMaterial({ map: wallMap, color: theme.wall.tint, roughness: .62, metalness: theme.wall.metalness, side: THREE.DoubleSide });\r\n  const floorMat = new THREE.MeshStandardMaterial({ color: theme.wall.to, roughness: .48, metalness: .72, side: THREE.DoubleSide });\r\n  const railMat = new THREE.MeshBasicMaterial({ color: theme.neon.rail, toneMapped: false });\r\n  const glassMat = new THREE.MeshBasicMaterial({ color: theme.sky?.colors?.[0] ?? '#21456d', transparent: true, opacity: .38, toneMapped: false, side: THREE.DoubleSide });\r\n  const glowMat = new THREE.MeshBasicMaterial({ color: theme.neon.warm, toneMapped: false });\r\n  const sharedGeo = { wall: new THREE.BoxGeometry(5.6, .14, SEGMENT_LENGTH), floor: new THREE.BoxGeometry(5.6, .16, SEGMENT_LENGTH), side: new THREE.BoxGeometry(.14, 4.5, SEGMENT_LENGTH), rail: new THREE.BoxGeometry(.07, .08, SEGMENT_LENGTH), window: new THREE.BoxGeometry(.025, 1.15, 3.1), lamp: new THREE.BoxGeometry(1.1, .035, .55), rush: new THREE.BoxGeometry(.035, .04, 2.1) };\r\n  sharedGeo.windowFrame = new THREE.BoxGeometry(.07, 1.24, .07);\r\n  const corridor = [];\r\n  for (let i = 0; i < (biome ? 0 : SEGMENT_COUNT); i++) {\r\n    const group = new THREE.Group();\r\n    const mesh = (geo, mat, x, y, z) => { const m = new THREE.Mesh(geo, mat); m.position.set(x, y, z); group.add(m); return m; };\r\n    mesh(sharedGeo.floor, floorMat, 0, -.08, 0);\r\n    mesh(sharedGeo.wall, wallMat, 0, 4.62, 0);\r\n    mesh(sharedGeo.side, wallMat, -2.82, 2.25, 0);\r\n    mesh(sharedGeo.side, wallMat, 2.82, 2.25, 0);\r\n    for (const x of [-2.55, 2.55]) mesh(sharedGeo.rail, railMat, x, .16, 0);\r\n    mesh(sharedGeo.lamp, glowMat, 0, 4.47, 0);\r\n    if (i % 3 === 1) for (const side of [-1, 1]) {\r\n      mesh(sharedGeo.window, glassMat, side * 2.735, 2.45, 0);\r\n      mesh(sharedGeo.windowFrame, railMat, side * 2.68, 2.45, -1.58);\r\n      mesh(sharedGeo.windowFrame, railMat, side * 2.68, 2.45, 1.58);\r\n    }\r\n    scenery.add(group); corridor.push(group);\r\n  }\r\n  const biomeScene=biome?createBiomeScene(THREE,{id:biome.id,section:biome.section}):null;\r\n  if(biomeScene)scenery.add(biomeScene.group);\r\n  const mats = {\r\n    coin: new THREE.MeshBasicMaterial({ color: '#ffe275', toneMapped: false }),\r\n    zapper: new THREE.MeshBasicMaterial({ color: '#ffbd45', transparent: true, opacity: .78, toneMapped: false, side: THREE.DoubleSide }),\r\n    coinPreview: new THREE.MeshBasicMaterial({ color: '#baf7ff', transparent: true, opacity: .55, toneMapped: false, side: THREE.DoubleSide }),\r\n    rush: new THREE.MeshBasicMaterial({ color: theme.neon.warm, transparent: true, opacity: .32, toneMapped: false, side: THREE.DoubleSide }),\r\n    laser: new THREE.MeshBasicMaterial({ color: '#fff', transparent: true, opacity: .82, toneMapped: false, side: THREE.DoubleSide }),\r\n    missile: new THREE.MeshBasicMaterial({ color: theme.neon.emergency, toneMapped: false }),\r\n    vehicle: new THREE.MeshStandardMaterial({ color: theme.neon.rail, emissive: theme.neon.rail, emissiveIntensity: .5, metalness: .6, roughness: .28 }),\r\n    powerup: new THREE.MeshBasicMaterial({ color: '#61fff0', toneMapped: false }),\r\n    component: new THREE.MeshBasicMaterial({ color: '#d6b1ff', toneMapped: false }),\r\n    token: new THREE.MeshBasicMaterial({ color: '#ffd45b', toneMapped: false }),\r\n    npc: new THREE.MeshStandardMaterial({ color: '#d2a9ff', emissive: '#482e78', emissiveIntensity: .5 }),\r\n    panelFrame:new THREE.MeshStandardMaterial({color:'#34434b',roughness:.56,metalness:.6}),\r\n    ramp:new THREE.MeshStandardMaterial({vertexColors:true,roughness:.72,metalness:.12}),\r\n    detail: new THREE.MeshBasicMaterial({ color: theme.neon.warm, wireframe: true, toneMapped: false })\r\n  };\r\n  const geos = {\r\n    ramp:makeRampGeometry(THREE),\r\n    coin: new THREE.TorusGeometry(.34, .1, 6, 18), zapper: new THREE.BoxGeometry(1, 1, .14), laser: new THREE.BoxGeometry(1, 1, .12),\r\n    missile: new THREE.ConeGeometry(.3, .75, 6), vehicle: new THREE.OctahedronGeometry(.53, 1), powerup: new THREE.DodecahedronGeometry(.42, 0),\r\n    component: new THREE.TorusGeometry(.4, .1, 6, 16), token: new THREE.CylinderGeometry(.32, .32, .08, 12), npc: new THREE.IcosahedronGeometry(.43, 1)\r\n  };\r\n  const detailGeo = new THREE.TorusGeometry(.62, .035, 4, 20);\r\n  const pickupGeos=pickupGeometry(THREE);\r\n  if(biome){\r\n    const hazardColors={tropical:'#c07835',castle:'#e75524',rainbow:'#dfb8ff',loopway:'#a17b3d',laboratory:'#f9dd38',asteroid:'#7d8ba5'};\r\n    mats.zapper.color.set(hazardColors[biome.id]??'#ffbd45');mats.zapper.map=wallMap;\r\n    mats.zapper.opacity=['rainbow','asteroid'].includes(biome.id)?.28:.65;\r\n    if(biome.id==='asteroid'){\r\n      // Warning fields own their luminance in open space; wall textures must not dim them.\r\n      mats.zapper.map=null;mats.zapper.color.set('#ffba62');mats.zapper.opacity=.55;\r\n      mats.panelFrame.color.set('#ffc982');mats.panelFrame.emissive.set('#ffc982');mats.panelFrame.emissiveIntensity=1;\r\n      mats.laser.color.set('#ff3c48');mats.missile.color.set('#7af7a5');\r\n    }\r\n  }\r\n  if(theme.hazardInk){\r\n    // The depth art palette owns warning luminance independently of textured bays.\r\n    mats.zapper.map=null;mats.zapper.fog=false;mats.zapper.color.set(theme.hazardInk==='daylight'?'#170b03':'#ffd88b');mats.zapper.opacity=.85;\r\n    mats.panelFrame.color.set(theme.hazardInk==='daylight'?'#080503':'#fff0cf');mats.panelFrame.emissive.set(theme.hazardInk==='daylight'?'#000000':'#fff0cf');mats.panelFrame.emissiveIntensity=1;mats.panelFrame.fog=false;\r\n  }\r\n  sharedGeo.panelRail=new THREE.BoxGeometry(1,1,1);\r\n  const frameMatrix=new THREE.Object3D();\r\n  const objectSlots = [];\r\n  for (let i = 0; i < OBJECT_LIMIT; i++) {\r\n    const group = new THREE.Group();\r\n    const main = new THREE.Mesh(geos.coin, mats.coin);\r\n    const detail = new THREE.Mesh(detailGeo, mats.detail);\r\n    const pickupMat=new THREE.MeshStandardMaterial({color:theme.neon.warm,emissive:theme.neon.rail,emissiveIntensity:.22,roughness:biome?.id==='rainbow'?.12:.65,metalness:biome?.id==='rainbow'?.8:.15});\r\n    const panelFrame=new THREE.InstancedMesh(sharedGeo.panelRail,mats.panelFrame,4);panelFrame.visible=false;panelFrame.frustumCulled=false;group.add(main, detail,panelFrame);group.visible=false;scene.add(group);\r\n    const laserMat=new THREE.MeshBasicMaterial({color:'#ff3b55',transparent:true,opacity:.9,toneMapped:false,side:THREE.DoubleSide});\r\n    const laserCoreMat=new THREE.MeshBasicMaterial({color:'#fff5d6',transparent:true,opacity:.9,toneMapped:false,side:THREE.DoubleSide});\r\n    objectSlots.push({ group, main, detail, panelFrame,pickupMat,laserMat,laserCoreMat });\r\n  }\r\n  // A small fixed pool of roster coaches keeps flybys recognizable without\r\n  // creating a GLB clone every frame or adding unbounded draw calls.\r\n  const coachSlots=[];\r\n  const coachColors={tropical:'#e07a26',castle:'#401c31',rainbow:'#8c4fd6',loopway:'#2d8139',laboratory:'#137d81',asteroid:'#435267'};\r\n  const coachSuit=new THREE.MeshStandardMaterial({color:coachColors[biome?.id]??'#70407c',roughness:.78});\r\n  const coachTrim=new THREE.MeshStandardMaterial({color:biome?.id==='castle'?'#ffb340':theme.neon.rail,metalness:.45,roughness:.34,emissive:theme.neon.rail,emissiveIntensity:.12});\r\n  const coachHead=new THREE.MeshStandardMaterial({color:'#a87db8',roughness:.65});\r\n  const coachBox=new THREE.BoxGeometry(.38,.27,.21),coachTieGeo=new THREE.BoxGeometry(.055,.18,.035),coachBadgeGeo=new THREE.BoxGeometry(.11,.065,.025),coachCapGeo=new THREE.BoxGeometry(.32,.075,.3),coachBodyGeo=new THREE.SphereGeometry(.19,8,6),coachHeadGeo=new THREE.SphereGeometry(.15,9,7);\r\n  for(let i=0;i<COACH_LIMIT;i++){\r\n    const group=new THREE.Group(),fallback=new THREE.Group(),outfit=new THREE.Group();\r\n    const body=new THREE.Mesh(coachBodyGeo,coachSuit);body.position.y=.42;body.scale.y=1.2;fallback.add(body);\r\n    const head=new THREE.Mesh(coachHeadGeo,coachHead);head.position.y=.78;fallback.add(head);\r\n    const torso=new THREE.Mesh(coachBox,coachSuit);torso.position.set(0,.49,.01);fallback.add(torso);\r\n    const tie=new THREE.Mesh(coachTieGeo,coachTrim);tie.position.set(0,.49,.125);fallback.add(tie);\r\n    const badge=new THREE.Mesh(coachBadgeGeo,coachTrim);badge.position.set(0,.54,.13);fallback.add(badge);\r\n    const jacket=new THREE.Mesh(coachBox,coachSuit);jacket.position.set(0,.49,.01);outfit.add(jacket);\r\n    const uniformTie=new THREE.Mesh(coachTieGeo,coachTrim);uniformTie.position.set(0,.49,.125);outfit.add(uniformTie);\r\n    const crest=new THREE.Mesh(coachBadgeGeo,coachTrim);crest.position.set(0,.54,.14);outfit.add(crest);\r\n    const cap=new THREE.Mesh(coachCapGeo,coachTrim);cap.position.set(0,1.02,.015);outfit.add(cap);\r\n    group.add(fallback,outfit);group.visible=false;scene.add(group);coachSlots.push({group,fallback,outfit,model:null});\r\n  }\r\n  let coachLoad=null;\r\n  const coachAssets=createCoachAssetTracker();\r\n  try{\r\n    const loader=new GLTFLoader();\r\n    coachLoad=import('three/addons/libs/meshopt_decoder.module.js').then(module=>{loader.setMeshoptDecoder(module.MeshoptDecoder);return loader.loadAsync(COACH_URL);}).catch(()=>loader.loadAsync(COACH_URL)).then(gltf=>{\r\n      const source=gltf.scene,box3=new THREE.Box3().setFromObject(source),size=box3.getSize(new THREE.Vector3()),center=box3.getCenter(new THREE.Vector3());\r\n      const unit=new THREE.Group();source.position.set(-center.x,-box3.min.y,-center.z);unit.add(source);unit.scale.setScalar(1/Math.max(.001,size.y));\r\n      const clonedMaterials=new Map(),sourceMaterials=new Set();\r\n      const tinted=m=>{\r\n        if(!clonedMaterials.has(m)){const clone=m.clone();clone.color.multiply(new THREE.Color('#c1a6ca'));clonedMaterials.set(m,clone);coachAssets.trackMaterial(clone);sourceMaterials.add(m);}\r\n        return clonedMaterials.get(m);\r\n      };\r\n      source.traverse(node=>{if(node.isMesh){coachAssets.trackGeometry(node.geometry);node.material=Array.isArray(node.material)?node.material.map(tinted):tinted(node.material);}});\r\n      for(const material of sourceMaterials)material.dispose();\r\n      if(coachAssets.disposed)return;\r\n      for(const slot of coachSlots){const clone=unit.clone(true);slot.group.add(clone);slot.model=clone;slot.fallback.visible=false;}\r\n    }).catch(error=>console.warn('plank flight coach model',error));\r\n  }catch{}\r\n  const sceneryTarget=new THREE.WebGLRenderTarget(2,2,{depthBuffer:true,stencilBuffer:false,minFilter:THREE.LinearFilter,magFilter:THREE.LinearFilter,format:THREE.RGBAFormat,type:THREE.UnsignedByteType});\r\n  sceneryTarget.texture.generateMipmaps=false;\r\n  const postMaterial=new THREE.ShaderMaterial({\r\n    uniforms:{uScenery:{value:sceneryTarget.texture},uMotion:{value:0}},\r\n    vertexShader:'varying vec2 vUv; void main(){vUv=uv;gl_Position=vec4(position.xy,0.0,1.0);}',\r\n    fragmentShader:`uniform sampler2D uScenery;uniform float uMotion;varying vec2 vUv;\r\n      void main(){vec2 radial=vUv-vec2(.5);float edge=smoothstep(.20,.65,length(radial));\r\n        vec2 streak=normalize(radial+vec2(.00001))*uMotion*.007*edge;\r\n        vec4 color=texture2D(uScenery,vUv-streak*2.0)*.10+texture2D(uScenery,vUv-streak)*.20+\r\n          texture2D(uScenery,vUv)*.40+texture2D(uScenery,vUv+streak)*.20+texture2D(uScenery,vUv+streak*2.0)*.10;\r\n        gl_FragColor=color;\r\n        #include <colorspace_fragment>\r\n      }`,\r\n    depthTest:false,depthWrite:false,toneMapped:false\r\n  });\r\n  const postScene=new THREE.Scene(),postCamera=new THREE.OrthographicCamera(-1,1,1,-1,.1,10),postQuad=new THREE.Mesh(new THREE.PlaneGeometry(2,2),postMaterial);\r\n  postCamera.position.z=1;postScene.add(postQuad);\r\n  const aperture = new THREE.Mesh(new THREE.RingGeometry(1.05, 1.1, 32), new THREE.MeshBasicMaterial({ color: theme.neon.rail, transparent: true, opacity: .7, toneMapped: false, side: THREE.DoubleSide }));\r\n  aperture.position.set(0, 2.1, -4.2); scene.add(aperture);\r\n  aperture.visible=!biome;\r\n  const exhaust = new THREE.Group();\r\n  const exhaustGeo = new THREE.CylinderGeometry(.045, .15, 1.25, 7).rotateX(Math.PI / 2);\r\n  const exhaustMat = new THREE.MeshBasicMaterial({ color: theme.neon.amber, transparent: true, opacity: .8, toneMapped: false });\r\n  for (const x of [-.34, .34]) { const jet = new THREE.Mesh(exhaustGeo, exhaustMat); jet.position.x = x; exhaust.add(jet); }\r\n  exhaust.visible = false; scene.add(exhaust);\r\n  const rush = new THREE.Group();\r\n  for (const side of [-1, 1]) for (let i = 0; i < 4; i++) {\r\n    const streak = new THREE.Mesh(sharedGeo.rush, mats.rush);\r\n    streak.position.set(side * (2.15 + (i % 2) * .18), (i - 1.5) * .58, -5 - i * 3.1);\r\n    streak.rotation.z = side * .035;\r\n    rush.add(streak);\r\n  }\r\n  rush.visible = false; scene.add(rush);\r\n  // Fixed-size soft projection pool. Grounded biomes use these beneath visible\r\n  // hazards and pickups; open ribbon/space biomes hide it entirely.\r\n  const shadowCanvas=document.createElement('canvas');shadowCanvas.width=shadowCanvas.height=64;const shadowCtx=shadowCanvas.getContext('2d'),shadowGradient=shadowCtx.createRadialGradient(32,32,3,32,32,32);shadowGradient.addColorStop(0,'rgba(3,6,14,.9)');shadowGradient.addColorStop(.45,'rgba(3,6,14,.58)');shadowGradient.addColorStop(1,'rgba(3,6,14,0)');shadowCtx.fillStyle=shadowGradient;shadowCtx.fillRect(0,0,64,64);\r\n  const shadowTexture=new THREE.CanvasTexture(shadowCanvas),shadowMaterial=new THREE.MeshBasicMaterial({map:shadowTexture,color:0x505765,transparent:true,opacity:.48,depthWrite:false,toneMapped:false,side:THREE.DoubleSide}),shadowGeo=new THREE.PlaneGeometry(1,1);shadowGeo.rotateX(-Math.PI/2);\r\n  const shadowPool=new THREE.InstancedMesh(shadowGeo,shadowMaterial,objectSlots.length+coachSlots.length);shadowPool.name='flight-ground-projection-pool';shadowPool.count=0;shadowPool.frustumCulled=false;shadowPool.renderOrder=1;scene.add(shadowPool);const shadowDummy=new THREE.Object3D(),shadowTint=new THREE.Color();\r\n  const crispObjects=[...objectSlots.map(slot=>slot.group),...coachSlots.map(slot=>slot.group),shadowPool,aperture,exhaust,rush];\r\n  return { scene, camera, hemi, key, fill, scenery,sceneryTarget,postScene,postCamera,postMaterial,postQuad,crispObjects,corridor, objectSlots, coachSlots,coachLoad,coachAssets,coachSuit,coachTrim,coachHead,coachBox,coachTieGeo,coachBadgeGeo,coachCapGeo,coachBodyGeo,coachHeadGeo, aperture, exhaust, exhaustGeo, exhaustMat, rush, shadowPool,shadowDummy,shadowTint,shadowMaterial,shadowTexture,shadowGeo,mats, geos, detailGeo, sharedGeo, frameMatrix,wallMat, floorMat, wallMap, railMat, glassMat, glowMat, theme,biomeScene,pickupGeos };\r\n}\r\n\r\nfunction releaseScene(view) {\r\n  if (!view) return;\r\n  view.biomeScene?.dispose();\r\n  view.coachAssets?.dispose();\r\n  for(const geometry of Object.values(view.pickupGeos??{}))geometry.dispose();\r\n  for(const slot of view.objectSlots??[])slot.pickupMat?.dispose();\r\n  for(const slot of view.objectSlots??[]){slot.laserMat?.dispose();slot.laserCoreMat?.dispose();}\r\n  for(const material of [view.coachSuit,view.coachTrim,view.coachHead])material?.dispose();\r\n  for(const geometry of [view.coachBox,view.coachTieGeo,view.coachBadgeGeo,view.coachCapGeo,view.coachBodyGeo,view.coachHeadGeo])geometry?.dispose();\r\n  view.sceneryTarget?.dispose();view.postQuad?.geometry?.dispose();view.postMaterial?.dispose();view.shadowGeo?.dispose();view.shadowMaterial?.dispose();view.shadowTexture?.dispose();\r\n  const { scene, mats, geos, detailGeo, sharedGeo, frameMatrix,wallMat, floorMat, wallMap, railMat, glassMat, glowMat, aperture, exhaustGeo, exhaustMat } = view;\r\n  scene.traverse(o => { if (o.isInstancedMesh) o.dispose?.(); });\r\n  for (const mesh of [aperture]) { mesh.geometry.dispose(); mesh.material.dispose(); }\r\n  for (const material of Object.values(mats)) material.dispose();\r\n  for (const geometry of Object.values(geos)) geometry.dispose();\r\n  detailGeo.dispose();\r\n  for (const geometry of Object.values(sharedGeo)) geometry.dispose();\r\n  exhaustGeo.dispose(); exhaustMat.dispose();\r\n  for (const material of [wallMat, floorMat, railMat, glassMat, glowMat]) material.dispose();\r\n  wallMap.dispose();\r\n  scene.clear();\r\n}\r\n\r\nfunction drawFallback(ctx, canvas, run, theme, nearest, objects) {\r\n  const w = canvas.width, h = canvas.height;\r\n  const gradient = ctx.createLinearGradient(0, 0, 0, h);\r\n  gradient.addColorStop(0, theme.bg); gradient.addColorStop(.56, theme.fog); gradient.addColorStop(1, '#080711');\r\n  ctx.fillStyle = gradient; ctx.fillRect(0, 0, w, h);\r\n  const cx = w / 2, cy = h * .48;\r\n  ctx.strokeStyle = theme.neon.rail; ctx.lineWidth = Math.max(1, w / 420); ctx.globalAlpha = .7;\r\n  ctx.beginPath();\r\n  for (const x of [-1, 1]) { ctx.moveTo(cx, cy); ctx.lineTo(cx + x * w * .62, h * 1.05); }\r\n  for (const y of [0, .22, .45, .7, 1]) { const yy = cy + (h - cy) * y; ctx.moveTo(cx - w * y * .62, yy); ctx.lineTo(cx + w * y * .62, yy); }\r\n  ctx.stroke(); ctx.globalAlpha = 1;\r\n  if (run.powerup?.effect?.visualRush && finite(run.time) < finite(run.powerup.until)) {\r\n    ctx.strokeStyle = theme.neon.warm; ctx.lineWidth = Math.max(2, w / 180); ctx.globalAlpha = .55; ctx.beginPath();\r\n    for (let i = 0; i < 4; i++) {\r\n      const yy = h * (.24 + i * .12), inset = w * (.04 + i * .025);\r\n      ctx.moveTo(inset, yy); ctx.lineTo(inset + w * .16, yy + h * .035);\r\n      ctx.moveTo(w - inset, yy); ctx.lineTo(w - inset - w * .16, yy + h * .035);\r\n    }\r\n    ctx.stroke(); ctx.globalAlpha = 1;\r\n  }\r\n  for (const o of objects) {\r\n    const ahead = finite(o.z) - finite(run.distance);\r\n    if (ahead <= .15 || ahead > 65) continue;\r\n    const p = 13 / (13 + ahead), x = cx + (finite(o.x) - finite(run.x)) * w * .34 * p;\r\n    const y = cy + (finite(run.y, .5) - finite(o.y, .5)) * h * .36 * p;\r\n    const r = Math.max(3, w * .035 * p);\r\n    const previewCoin = o.type === 'coin' && Number.isFinite(Number(o.revealAt)) && ahead > 3;\r\n    const color = o.type === 'coin' ? (previewCoin ? '#baf7ff' : '#ffe275') : ['laser', 'zapper', 'missile'].includes(o.type) ? theme.neon.emergency : '#70fff0';\r\n    ctx.strokeStyle = color; ctx.fillStyle = color; ctx.lineWidth = Math.max(2, r * .2);\r\n    if (o.type === 'laser' || o.type === 'zapper') { ctx.globalAlpha = o.type === 'laser' ? .68 : .48; ctx.fillRect(x - r * 2.3, y - r * 1.35, r * 4.6, r * 2.7); ctx.globalAlpha = 1; ctx.strokeRect(x - r * 2.3, y - r * 1.35, r * 4.6, r * 2.7); }\r\n    else if (o.type === 'missile') { ctx.beginPath(); ctx.arc(x, y, r * 1.4, 0, Math.PI * 2); ctx.stroke(); ctx.fillText('!', x - r * .25, y + r * .35); }\r\n    else { ctx.beginPath(); ctx.arc(x, y, r * (previewCoin ? .58 : 1), 0, Math.PI * 2); ctx.stroke(); }\r\n  }\r\n  if (nearest) {\r\n    ctx.fillStyle = '#ff4e64'; ctx.font = `700 ${Math.max(12, w / 30)}px system-ui`;\r\n    ctx.textAlign = 'center'; ctx.fillText(nearest, cx, h * .19);\r\n  }\r\n}\r\n\r\n/** Mount a renderer-only view. Simulation and camera input remain owned by the caller. */\r\nexport function mountFlightView({ host, run: sourceRun, onAction = () => {}, sound = () => {}, renderBudget = null, beforeRender = () => {} } = {}) {\n  const run = sourceRun;\n  if(renderBudget&&(!Number.isFinite(renderBudget.maxWidth)||renderBudget.maxWidth<1||!Number.isFinite(renderBudget.maxHeight)||renderBudget.maxHeight<1))throw new TypeError('Invalid flight rendering budget');\n  if (!host || !run) throw new TypeError('mountFlightView requires host and run');\r\n  const stylesheet = ensureStylesheet();\r\n  const ui = buildDom(host, onAction);\r\n  const mediaReduce = window.matchMedia?.('(prefers-reduced-motion: reduce)');\r\n  let renderer = null, view = null, fallbackCtx = ui.fallback.getContext('2d');\r\n  let stopped = false, width = 0, height = 0, priorCoins = finite(run.coins), latestTheme = null, lost = false;\r\n  let resizeObserver = null;\r\n  let graphicsRelease = Promise.resolve();\r\n  const setSize = () => {\r\n    if (stopped) return;\r\n    const rect = ui.root.getBoundingClientRect();\r\n    width = Math.max(1, Math.round(rect.width || host.clientWidth || innerWidth));\r\n    height = Math.max(1, Math.round(rect.height || host.clientHeight || innerHeight));\r\n    if (renderer) {\n      const scale=renderBudget?Math.min(1,renderBudget.maxWidth/width,renderBudget.maxHeight/height):1;\n      renderer.setPixelRatio(renderBudget?1:Math.min(1.25, window.devicePixelRatio || 1));\n      renderer.setSize(Math.max(1,Math.round(width*scale)),Math.max(1,Math.round(height*scale)), false);\n      if (view) {\r\n        view.camera.aspect = width / height; view.camera.updateProjectionMatrix();\r\n        const drawingSize=renderer.getDrawingBufferSize(new THREE.Vector2());\r\n        view.sceneryTarget.setSize(Math.min(960,Math.max(1,Math.round(drawingSize.x*.85))),Math.min(1400,Math.max(1,Math.round(drawingSize.y*.85))));\r\n      }\r\n    }\r\n    for (const canvas of [ui.canvas, ui.fallback]) { canvas.style.width = '100%'; canvas.style.height = '100%'; }\r\n    ui.fallback.width = Math.min(1024, width);\r\n    ui.fallback.height = Math.min(1600, Math.round(height * ui.fallback.width / width));\r\n  };\r\n  const releaseRenderer = () => {\r\n    const owned = renderer;\r\n    if (!owned) return graphicsRelease;\r\n    renderer = null;\r\n    let gl = null;\r\n    try { gl = owned.getContext?.() ?? null; } catch {}\r\n    graphicsRelease = releaseGraphics(ui.canvas, gl, () => {\r\n      try { owned.dispose(); } catch {}\r\n      try { owned.forceContextLoss?.(); } catch {}\r\n    });\r\n    return graphicsRelease;\r\n  };\r\n  const showFallback = reason => {\r\n    lost = true; ui.root.classList.add('pf-fallback');\r\n    ui.hud.querySelector('[data-notice]').hidden = false;\r\n    ui.hud.querySelector('[data-notice]').textContent = 'Graphics unavailable. Exercise credit is paused; end this workout to save progress.';\r\n    onAction('render-error', { reason: String(reason?.message ?? reason ?? 'WebGL unavailable') });\r\n    if (renderer) releaseRenderer();\r\n    view && releaseScene(view); view = null;\r\n    setSize();\r\n  };\r\n  try {\r\n    renderer = new THREE.WebGLRenderer({ canvas: ui.canvas, antialias: false, alpha: false, powerPreference: 'low-power', stencil: false, depth: true, failIfMajorPerformanceCaveat: false });\r\n    renderer.setPixelRatio(Math.min(1.25, window.devicePixelRatio || 1));\r\n    renderer.outputColorSpace = THREE.SRGBColorSpace;\r\n    renderer.toneMapping = THREE.ACESFilmicToneMapping;\r\n    renderer.toneMappingExposure = 1.08;\r\n    renderer.info.autoReset=false;\r\n    setSize();\r\n    view = makeScene(THREE, run.biome?biomeTheme(run.biome.id):themeFor(0),run.biome);\r\n    setSize();\r\n  } catch (error) { showFallback(error); }\n  const ready = Promise.resolve(view?.coachLoad).then(() => { if (stopped || lost || !view) throw new Error('Flight graphics unavailable'); });\n  ready.catch(() => {});\n  const onContextLost = event => { event.preventDefault(); showFallback('graphics context lost'); onAction('pause', { reason: 'graphics-lost' }); };\r\n  ui.canvas.addEventListener('webglcontextlost', onContextLost, false);\r\n  const onResize = () => setSize();\r\n  window.addEventListener('resize', onResize, { passive: true });\r\n  if (typeof ResizeObserver !== 'undefined') { resizeObserver = new ResizeObserver(onResize); resizeObserver.observe(ui.root); }\r\n  const objects = [];\r\n  let warningText = '', warningKey = '', warningCritical = false;\r\n  const render = (frame = {}) => {\n    if (stopped) return;\n    const run = frame.presentationRun ?? sourceRun;\n    try {\r\n      ui.update(frame, run);\r\n      const fx=ui.fx;\r\n      const theme = run.biome?biomeTheme(run.biome.id):themeFor(Math.max(0, Math.floor(finite(run.level))));\r\n      if (theme !== latestTheme) {\r\n        latestTheme = theme; ui.setTheme(theme);\r\n        if (view && view.theme.name !== theme.name) { releaseScene(view); view = makeScene(THREE, theme,run.biome);setSize(); }\r\n      }\r\n      const speed = Math.max(.05, finite(run.speed, 1));\r\n      const selected = frame.profile?.selectedCosmetic;\r\n      const cosmetic = selected && typeof selected === 'object' ? selected : (Array.isArray(frame.shopItems) ? frame.shopItems.find(item => item.id === selected) : null);\r\n      const rawTint = cosmetic?.tint ?? cosmetic?.color ?? cosmetic?.accent;\r\n      const tint = typeof rawTint === 'object' ? rawTint.primary ?? rawTint.color : rawTint;\r\n      let tintCss = '';\r\n      if (typeof tint === 'string' && tint) { try { tintCss = `#${new THREE.Color(tint).getHexString()}`; } catch {} }\r\n      if (tintCss) ui.root.style.setProperty('--pf-personal', tintCss);\r\n      else ui.root.style.removeProperty('--pf-personal');\r\n      if (view) {\r\n        if (tintCss) { view.aperture.material.color.set(tintCss); view.exhaustMat.color.set(tintCss); }\r\n        else { view.aperture.material.color.set(theme.neon.rail); view.exhaustMat.color.set(theme.neon.amber); }\r\n      }\r\n      objects.length = 0;\r\n      const worldDistance = finite(run.distance);\r\n      for (const object of Array.isArray(run.objects) ? run.objects : []) {\r\n        const ahead = finite(object?.z) - worldDistance;\r\n        const previewAt = finite(object?.revealAt, -Infinity);\r\n        if (object && object.active !== false && object.collected !== true && ahead >= -.5 && ahead <= 76 && (object.type !== 'coin' || worldDistance >= previewAt)) objects.push(object);\r\n      }\r\n      const hazards = objects.filter(o => ['zapper', 'laser', 'missile'].includes(o.type)).map(o => ({ object: o, ahead: finite(o.z) - worldDistance })).filter(v => v.ahead > 0).sort((a, b) => a.ahead - b.ahead);\r\n      const closest = hazards[0];\r\n      let warning = '', label = '', ttc = 0;\r\n      if (closest) {\r\n        ttc = Math.max(0, closest.ahead / speed);\r\n        const state = String(closest.object.state ?? closest.object.kind ?? '').toLowerCase();\r\n        const laserState = String(closest.object.laserState ?? '').toLowerCase();\r\n        label = closest.object.type === 'missile' ? (run.biome?.id==='asteroid'?'DODGE MISSILE · REST LOW':state.includes('lock') ? 'MISSILE LOCK' : state.includes('warn') ? 'MISSILE WARNING' : 'MISSILE ALERT') : closest.object.type === 'laser' ? (run.biome?.id==='asteroid'?'DODGE LASER · FOLLOW YOUR GATE':laserState === 'charging' ? 'LASER CHARGING' : laserState === 'active' ? 'LASER ACTIVE' : 'LASER AHEAD') : 'ENERGY FIELD';\r\n        if (finite(run.effects?.warningLead) > 0) label = `SIGNAL · ${label}`;\r\n        warning = `${label} · ${ttc.toFixed(1)}s`;\r\n      }\r\n      const notice = ui.hud.querySelector('[data-notice]');\r\n      const pose = frame.pose ?? {};\r\n      if (warning && closest.ahead < Math.max(3, speed * 2.8, finite(closest.object.warningLead)) && !frame.paused && pose.controlValid !== false) {\r\n        const critical = closest.ahead < speed * 1.35;\r\n        notice.hidden = false; notice.textContent = warning; notice.classList.toggle('is-critical', critical);\r\n        warningText = warning;\r\n        const nextWarningKey = String(closest.object.id ?? `${closest.object.type}:${closest.ahead < speed * 2 ? 'near' : 'approach'}`);\r\n        if (nextWarningKey !== warningKey) {\r\n          warningKey = nextWarningKey; sound('crt');\r\n          ui.hud.querySelector('[data-announcement]').textContent = `${label}. About ${Math.ceil(ttc)} seconds ahead.`;\r\n        } else if (critical && !warningCritical) {\r\n          ui.hud.querySelector('[data-announcement]').textContent = `${label} very close. Move gently to a clear path.`;\r\n        }\r\n        warningCritical = critical;\r\n      } else {\r\n        notice.classList.remove('is-critical');\r\n        if (!frame.paused) { warningText = ''; warningKey = ''; warningCritical = false; }\r\n      }\r\n      const coins = finite(run.coins);\r\n      if (coins > priorCoins) { sound('coin'); priorCoins = coins; }\r\n      else if (coins < priorCoins) priorCoins = coins;\r\n      if (view && renderer && !lost) {\r\n        renderer.info.reset();\r\n        const camera = view.camera;\r\n        const playerX = finite(run.x), playerY = finite(run.y, .5);\r\n        camera.position.set(playerX * 2.12, .35 + playerY * 3.35, 0);\r\n        camera.lookAt(camera.position.x, camera.position.y, -18);\r\n        const turn=null;\r\n        const turnWave=turn?.active&&!mediaReduce?.matches?Math.sin(Math.PI*turn.progress):0;\r\n        const hyperspace=run.biome?.id==='asteroid'&&turnWave>0;\r\n        const boosted=finite(run.biomeEffects?.speedUntil)>run.time;\r\n        const rainbowMode=finite(run.biomeEffects?.rainbowUntil)>run.time;\r\n        ui.root.classList.toggle('is-rainbow-mode',rainbowMode);\r\n        camera.fov=76+(hyperspace?turnWave*13:boosted&&!mediaReduce?.matches?4:0);camera.updateProjectionMatrix();\r\n        // Scenery bends; player hit geometry and camera facing remain aligned.\r\n        view.biomeScene?.update({distance:run.distance,time:run.time,section:run.biome?.section??0,speed,boost:boosted,turn:mediaReduce?.matches?null:turn,cameraPosition:camera.position});\r\n        if(view.biomeScene)view.biomeScene.group.rotation.y=0;\r\n        const offset = ((finite(run.distance) % SEGMENT_LENGTH) + SEGMENT_LENGTH) % SEGMENT_LENGTH;\r\n        view.corridor.forEach((segment, index) => { segment.position.z = offset - (index * SEGMENT_LENGTH + SEGMENT_LENGTH / 2); });\r\n        view.aperture.position.set(camera.position.x, camera.position.y, -4.5);\r\n        view.aperture.rotation.z = 0;\r\n        const visibleCoaches=objects.filter(o=>o.type==='npc').sort((a,b)=>(finite(a.z)-worldDistance)-(finite(b.z)-worldDistance)).slice(0,view.coachSlots.length);\r\n        for(let i=0;i<view.coachSlots.length;i++){\r\n          const slot=view.coachSlots[i],object=visibleCoaches[i],ahead=object?finite(object.z)-worldDistance:Infinity;\r\n          slot.group.visible=Boolean(object&&ahead>=-.5&&ahead<=76);\r\n          if(slot.group.visible){slot.group.position.set(finite(object.x)*2.12,.35+finite(object.y,.5)*3.35-.38+Math.sin(run.time*3+i)*.035,-ahead);slot.group.scale.setScalar(.78);slot.group.rotation.y=Math.sin(run.time*1.2+i)*.08;}\r\n        }\r\n        const visibleObjects = objects.filter(o=>o.type!=='npc').slice().sort((a, b) => (finite(a.z) - worldDistance) - (finite(b.z) - worldDistance));\r\n        let slotIndex = 0;\r\n        for (const object of visibleObjects) {\r\n          if (slotIndex >= view.objectSlots.length) break;\r\n          const ahead = finite(object.z) - finite(run.distance);\r\n          const slot = view.objectSlots[slotIndex++];\r\n          if (ahead < -.5 || ahead > 76) { slot.group.visible = false; continue; }\r\n          const type = ['coin', 'zapper', 'laser', 'missile', 'vehicle', 'powerup', 'component', 'token', 'npc','biome-pickup','ramp'].includes(object.type) ? object.type : 'component';\r\n          slot.group.visible = true;\r\n          slot.group.position.set(finite(object.x) * 2.12, object.type==='ramp'?0:.35 + finite(object.y, .5) * 3.35, -ahead);\r\n          slot.group.scale.set(1, 1, 1);\r\n          slot.main.visible=true;slot.detail.visible=false;slot.detail.position.set(0,0,0);slot.detail.scale.set(1,1,1);slot.detail.geometry=view.detailGeo;slot.panelFrame.visible=false;\r\n          if(type==='ramp'){slot.main.geometry=view.geos.ramp;slot.main.material=view.mats.ramp;slot.main.rotation.set(0,0,0);slot.main.scale.set(1,1,1);continue;}\r\n          const previewCoin = type === 'coin' && Number.isFinite(Number(object.revealAt)) && ahead > 3;\r\n          const themedPickup=type==='biome-pickup';\r\n          const laser=type==='laser',laserState=String(object.laserState??'idle');\r\n          slot.main.geometry = themedPickup?(view.pickupGeos[object.shape]??view.pickupGeos[object.benefit]??view.geos.powerup):view.geos[type];\r\n          slot.main.material = laser?slot.laserMat:themedPickup?slot.pickupMat:previewCoin ? view.mats.coinPreview : view.mats[type];\r\n          if(laser){\r\n            const pulse=.5+.5*Math.sin(finite(run.time)*17+finite(object.x)*3);\r\n            const stateStyle={idle:{color:'#6d7b8c',beam:.035,core:0},telegraph:{color:'#ffb74d',beam:.12,core:.38},charging:{color:'#ff7043',beam:.24+pulse*.1,core:.5+pulse*.22},active:{color:'#ff304b',beam:.8+pulse*.16,core:.72+pulse*.25},cooldown:{color:'#9b7581',beam:.055,core:0}}[laserState]??{color:'#ff304b',beam:.8,core:.8};\r\n            slot.laserMat.color.set(theme.hazardInk==='daylight'&&stateStyle.core>0?'#240e05':stateStyle.color);slot.laserMat.opacity=theme.hazardInk&&stateStyle.core>0?Math.min(1,stateStyle.beam*1.7+.18):stateStyle.beam;slot.laserMat.fog=!theme.hazardInk;slot.laserCoreMat.fog=!theme.hazardInk;\r\n            slot.laserCoreMat.opacity=theme.hazardInk&&stateStyle.core>0?Math.min(1,stateStyle.core*1.4):stateStyle.core;slot.laserCoreMat.color.set(laserState==='active'?'#fff3da':'#ffd27c');\r\n            slot.main.visible=laserState!=='idle'||ahead<=finite(object.warningLead);\r\n            slot.detail.geometry=view.geos.laser;slot.detail.material=slot.laserCoreMat;slot.detail.visible=slot.main.visible&&stateStyle.core>0;\r\n          }\r\n          if(themedPickup){const colors={speed:'#ffb841',magnet:'#63efff',shield:'#9cff9e',coins:'#ffe56a',lift:'#f7a9ff'};slot.pickupMat.color.set(colors[object.benefit]??theme.neon.warm);}\r\n          if(!laser)slot.detail.material = view.mats.detail;\r\n          const w = Math.max(.01, 2 * finite(object.w, .18) * 2.12);\r\n          const h = Math.max(.01, 2 * finite(object.h, .18) * 3.35);\r\n          if(type==='zapper'||type==='laser'){\r\n            slot.panelFrame.visible=true;\r\n            const rails=[[0,h/2,w,.065],[0,-h/2,w,.065],[-w/2,0,.065,h],[w/2,0,.065,h]];\r\n            for(let i=0;i<4;i++){const [x,y,rw,rh]=rails[i];view.frameMatrix.position.set(x,y,0);view.frameMatrix.scale.set(rw,rh,.28);view.frameMatrix.updateMatrix();slot.panelFrame.setMatrixAt(i,view.frameMatrix.matrix);}\r\n            slot.panelFrame.instanceMatrix.needsUpdate=true;\r\n          }\r\n          if (previewCoin) slot.main.scale.set(.52, .52, .52);\r\n          else if (type === 'laser' || type === 'zapper') {\r\n            slot.main.scale.set(w, h, 1);\r\n            if(type==='laser'){slot.detail.scale.set(w*.84,h*.12,.35);slot.detail.position.set(0,0,.075);}\r\n            else slot.detail.visible = false;\r\n          } else {\r\n            slot.main.scale.set(1, 1, 1); slot.detail.visible = ['missile', 'vehicle', 'powerup', 'component', 'token','biome-pickup'].includes(type);\r\n            slot.detail.geometry = view.detailGeo; slot.detail.scale.set(.82, .82, .82);\r\n          }\r\n          slot.main.rotation.set(0, 0, type === 'missile' ? Math.PI / 2 : 0);\r\n          if(themedPickup)slot.main.rotation.y=mediaReduce?.matches?0:run.time*.9;\r\n          if(type==='coin'&&fx.magnet&&!previewCoin){slot.detail.visible=true;slot.detail.geometry=view.detailGeo;slot.detail.material=view.mats.powerup;slot.detail.scale.set(1.24,1.24,1.24);slot.detail.rotation.set(0,0,0);}\r\n          if(type==='missile'&&fx.slowOrbit){slot.detail.visible=true;slot.detail.geometry=view.detailGeo;slot.detail.material=view.mats.powerup;slot.detail.scale.set(1.35,1.35,1.35);slot.detail.rotation.set(0,0,0);}\r\n          slot.detail.rotation.set(0, 0, 0);\r\n        }\r\n        for (; slotIndex < view.objectSlots.length; slotIndex++) view.objectSlots[slotIndex].group.visible = false;\r\n        {\r\n          const grounded=!['rainbow','asteroid'].includes(run.biome?.id??''),flow=mediaReduce?.matches?0:clamp((speed-5.2)/9,0,1);let shadowIndex=0;\r\n          if(grounded){const writeShadow=(group,wide,deep)=>{if(!group.visible||shadowIndex>=view.shadowPool.instanceMatrix.count)return;const height=Math.max(0,group.position.y),scale=Math.max(.24,1-height*.16);view.shadowDummy.position.set(group.position.x+.18+height*.18,-.036,group.position.z+.18+height*.1);view.shadowDummy.rotation.set(0,.42+flow*.14,0);view.shadowDummy.scale.set(wide*scale*(1+flow*.08),1,deep*scale*(1+flow*.3));view.shadowDummy.updateMatrix();view.shadowPool.setMatrixAt(shadowIndex,view.shadowDummy.matrix);view.shadowTint.setRGB(1-height*.12,1-height*.12,1-height*.12);view.shadowPool.setColorAt(shadowIndex++,view.shadowTint);};\r\n            for(const slot of view.objectSlots)writeShadow(slot.group,.82,.48);\r\n            for(const slot of view.coachSlots)writeShadow(slot.group,.72,.4);\r\n          }\r\n          view.shadowPool.visible=grounded;view.shadowPool.count=shadowIndex;view.shadowPool.instanceMatrix.needsUpdate=true;if(view.shadowPool.instanceColor)view.shadowPool.instanceColor.needsUpdate=true;\r\n          view.shadowMaterial.opacity=mediaReduce?.matches?.34:.39-flow*.06;\r\n        }\r\n        view.hemi.intensity = mediaReduce?.matches ? .9 : 1.15;\r\n        view.fill.color.set(theme.neon.rail);\r\n        view.fill.position.set(camera.position.x, camera.position.y, -21);\r\n        const visualRush = rainbowMode||boosted||hyperspace||(Boolean(run.powerup?.effect?.visualRush) && finite(run.time) < finite(run.powerup.until));\r\n        if(rainbowMode){view.mats.rush.color.setHSL((run.time*.08)%1,.85,.65);view.exhaustMat.color.copy(view.mats.rush.color);}else view.mats.rush.color.set(theme.neon.warm);\r\n        view.rush.visible = visualRush;\r\n        view.rush.position.set(camera.position.x, camera.position.y, 0);\r\n        view.rush.scale.z=hyperspace?5:1;\r\n        view.mats.rush.opacity = mediaReduce?.matches ? .32 : .25 + .12 * (0.5 + 0.5 * Math.sin(finite(run.time) * 9));\r\n        // Exhaust belongs below the corner pods, outside the camera volume.\r\n        view.exhaust.visible = false;\r\n        view.exhaust.scale.setScalar(visualRush ? 1.35 : 1);\r\n        view.exhaust.position.set(camera.position.x, camera.position.y - .22, -.35);\r\n        const background=view.scene.background,autoClear=renderer.autoClear,visibility=view.crispObjects.map(object=>object.visible);\n        const restoreCamera=beforeRender(view.scene,camera);\n        try{\r\n          view.scenery.visible=true;for(const object of view.crispObjects)object.visible=false;\r\n          renderer.autoClear=true;renderer.setRenderTarget(view.sceneryTarget);renderer.clear(true,true,true);renderer.render(view.scene,camera);\r\n          renderer.setRenderTarget(null);renderer.autoClear=false;renderer.clear(true,true,true);\r\n          view.postMaterial.uniforms.uMotion.value=!mediaReduce?.matches&&run.phase==='flying'?clamp(speed/10,0,1):0;\r\n          renderer.render(view.postScene,view.postCamera);\r\n          view.scene.background=null;view.scenery.visible=false;for(let i=0;i<view.crispObjects.length;i++)view.crispObjects[i].visible=visibility[i];\r\n          renderer.clearDepth();renderer.render(view.scene,camera);\r\n        }finally{\n          if(typeof restoreCamera==='function')restoreCamera();\n          view.scene.background=background;view.scenery.visible=true;\r\n          for(let i=0;i<view.crispObjects.length;i++)view.crispObjects[i].visible=visibility[i];\r\n          renderer.setRenderTarget(null);renderer.autoClear=autoClear;\r\n        }\r\n      } else if (fallbackCtx) {\r\n        drawFallback(fallbackCtx, ui.fallback, run, theme, warning, objects);\r\n      }\r\n    } catch (error) {\r\n      if (!lost) showFallback(error);\r\n      if (fallbackCtx) drawFallback(fallbackCtx, ui.fallback, run, latestTheme ?? themeFor(0), warningText, objects);\r\n    }\r\n  };\r\n  const stop = () => {\r\n    if (stopped) return;\r\n    stopped = true;\r\n    resizeObserver?.disconnect();\r\n    window.removeEventListener('resize', onResize);\r\n    ui.canvas.removeEventListener('webglcontextlost', onContextLost, false);\r\n    const released = releaseRenderer();\r\n    if (view) { releaseScene(view); view = null; }\r\n    ui.dispose();\r\n    if (stylesheet && !document.querySelector('.plank-flight-host .plank-flight')) stylesheet.remove();\r\n    return released;\r\n  };\r\n  return { render, stop,ready,get stats(){return renderer?{render:{...renderer.info.render},memory:{...renderer.info.memory},groundShadows:view?{visible:view.shadowPool.visible,count:view.shadowPool.count,capacity:view.shadowPool.instanceMatrix.count,opacity:view.shadowMaterial.opacity}:null,fog:view?.scene.fog?{near:view.scene.fog.near,far:view.scene.fog.far}:null,sceneryBlur:view?{active:view.postMaterial.uniforms.uMotion.value>0,width:view.sceneryTarget.width,height:view.sceneryTarget.height,passes:2}:null}:null;} };\n}\r\n","arcade/power/aura-anchor.mjs":"// Pure geometry that pins auras to the skeleton: torso-relative sizes, fixed bone slots per sprite (no hopping),\r\n// body-centred circles and pulses, bone capsule radii. Points are overlay fractions (0..1) with optional `alpha`\r\n// (0..1 fade from the One Euro bank); pixel space is w x h. No DOM, no allocation in the per-frame helpers that\r\n// take an `out` object.\r\n\r\n// Tuning knobs (real phones need tuning). Radii and sizes are fractions of the on-screen torso length\r\n// (shoulder to hip), so the aura is the same thickness near or far from the camera.\r\nexport const AURA_BODY = Object.freeze({\r\n  // One Euro presets for overlay fractions: torso heavier, limbs lighter, hands lightest (they move fastest).\r\n  FILTER: Object.freeze({\r\n    // beta is per (overlay fraction / s): a fast squat moves the hips ~0.5/s, a punch the wrist 2-3/s. High beta and\r\n    // dCutoff 2 open the filter quickly on fast moves (little lag) while minCutoff keeps a still body calm.\r\n    // Swept offline (.r1017/scripts/tune.mjs, 45 ms lag, 30 Hz): vs raw lagged landmarks these cut the squat error\r\n    // ~22% and the side-step error ~37% with less jitter at rest; hands stay nearly raw (a 0.2 s punch is too short\r\n    // for any filter + prediction to beat the raw point).\r\n    torso: Object.freeze({ minCutoff: 1, beta: 60, dCutoff: 3 }),\r\n    limb: Object.freeze({ minCutoff: 1.2, beta: 90, dCutoff: 3.5 }),\r\n    hand: Object.freeze({ minCutoff: 0.8, beta: 150, dCutoff: 5 }),\r\n  }),\r\n  PREDICT_CLAMP: 0.04,       // max forward lead per landmark, overlay fractions\r\n  PREDICT_GAIN: 0.9,         // share of (velocity x lag) to lead by (one-euro.mjs caps the lag at 66 ms)\r\n  FADE_MS: 250,              // whole-aura fade in/out instead of a hard pop\r\n  // Halo outer radius per bone class at level 1 (the glow fades out to this distance from the bone).\r\n  RADIUS: Object.freeze({ core: 0.62, head: 0.46, upper: 0.3, lower: 0.26, hip: 0.36 }),\r\n  // The body itself (roughly the person's width) is cut out of the glow so it sits around, not over, them.\r\n  BODY: Object.freeze({ core: 0.3, head: 0.21, upper: 0.09, lower: 0.075, hip: 0.13 }),\r\n  LEVEL_BASE: 0.55, LEVEL_GAIN: 0.45, // halo radius scale = BASE + GAIN * level\r\n  SPEED_WIDEN: 0.5, SPEED_REF: 1.5,   // widen by up to +50% at SPEED_REF torso lengths/s (hides sub-frame error)\r\n  CUT: 0.85, CUT_ALPHA: 0.6,             // inner body punch-out: BODY radius x CUT at this strength (a rim stays)\r\n  HALO_DOWNSCALE: 4,                  // halo drawn at 1/4 of the glow layer, upscaled = smooth falloff\r\n  IMPACT_GAIN: 0.5, IMPACT_MS: 300,   // punch impact: glow gain bump and flare life\r\n  PARTICLES: 96,                      // flame tongue pool (Q2+); Q1 has none\r\n  EMIT_BASE: 26, EMIT_LEVEL: 90,      // tongues per second at level 0 / extra at level 1\r\n  WISP_DECAY: 0.92, WISP_RISE: 2,     // silhouette feedback wisps (Q3): fade per frame, glow px risen per frame\r\n  GLOW_WIDTH: 160,                    // glow layer logical width (CSS upscaled with smoothing = free blur)\r\n  // Sprite placement as torso multiples (values chosen to match the old screen-fraction look at ~2.5 m).\r\n  SHEET_SIZE: 0.95, ORBIT_SIZE: 0.85, CIRCLE_SIZE: 2.1, PULSE_SIZE: 1.5, FLOOR_SIZE: 0.6,\r\n  BODY_ORBIT: Object.freeze([0.22, 0.14]), WIDE_ORBIT: Object.freeze([0.42, 0.3]),\r\n  FALLBACK_TORSO: 0.19,               // torso as a fraction of height when no torso is visible\r\n});\r\n\r\nexport const VISIBLE_ALPHA = 0.02;\r\nconst clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));\r\nexport function pointAlpha(p) {\r\n  if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return 0;\r\n  if (Number.isFinite(p.alpha)) return p.alpha;\r\n  return (p.visibility ?? 1) >= 0.5 ? 1 : 0;\r\n}\r\nconst seen = p => pointAlpha(p) > VISIBLE_ALPHA;\r\n\r\n/** On-screen torso length in pixels (aspect-correct): the longest of left side, right side and shoulder line. */\r\nexport function torsoPx(points, w, h) {\r\n  if (!points) return null;\r\n  let best = 0;\r\n  for (const [a, b] of [[11, 23], [12, 24], [11, 12]]) {\r\n    const p = points[a], q = points[b];\r\n    if (seen(p) && seen(q)) best = Math.max(best, Math.hypot((p.x - q.x) * w, (p.y - q.y) * h));\r\n  }\r\n  return best > 0 ? Math.max(best, 0.05 * h) : null;\r\n}\r\n\r\nfunction mid(points, a, b, w, h) {\r\n  const p = points?.[a], q = points?.[b];\r\n  if (seen(p) && seen(q)) return { x: (p.x + q.x) / 2 * w, y: (p.y + q.y) / 2 * h };\r\n  if (seen(p)) return { x: p.x * w, y: p.y * h };\r\n  if (seen(q)) return { x: q.x * w, y: q.y * h };\r\n  return null;\r\n}\r\n/** Hip midpoint, chest (shoulder) midpoint and their centre in pixels; falls back to the screen centre. */\r\nexport function bodyCentre(points, w, h) {\r\n  const hip = mid(points, 23, 24, w, h), chest = mid(points, 11, 12, w, h);\r\n  const centre = hip && chest ? { x: (hip.x + chest.x) / 2, y: (hip.y + chest.y) / 2 } : hip || chest || { x: w * 0.5, y: h * 0.48 };\r\n  return { hip: hip || centre, chest: chest || centre, centre, found: !!(hip || chest) };\r\n}\r\n\r\n/** Size scale: torso pixels, or the old screen fraction when no torso is visible. */\r\nexport function bodyScale(points, w, h, T = AURA_BODY) { return torsoPx(points, w, h) ?? T.FALLBACK_TORSO * h; }\r\n\r\n// Bones: [a, b, radius class]. -1 = chest midpoint, -2 = hip midpoint (virtual joints).\r\nexport const BONES = Object.freeze([\r\n  [-1, -2, 'core'], [11, 12, 'upper'], [23, 24, 'hip'], [11, 23, 'upper'], [12, 24, 'upper'],\r\n  [0, 0, 'head'],\r\n  [11, 13, 'upper'], [13, 15, 'lower'], [12, 14, 'upper'], [14, 16, 'lower'],\r\n  [23, 25, 'upper'], [25, 27, 'lower'], [24, 26, 'upper'], [26, 28, 'lower'],\r\n]);\r\n\r\n/** Pixel position and fade of a (possibly virtual) joint into `out`; returns false if it is not tracked. */\r\nexport function jointPx(points, index, w, h, out) {\r\n  if (index >= 0) {\r\n    const p = points?.[index];\r\n    if (!seen(p)) return false;\r\n    out.x = p.x * w; out.y = p.y * h; out.a = clamp(pointAlpha(p), 0, 1); return true;\r\n  }\r\n  const [i, j] = index === -1 ? [11, 12] : [23, 24], p = points?.[i], q = points?.[j];\r\n  if (!seen(p) || !seen(q)) return false;\r\n  out.x = (p.x + q.x) / 2 * w; out.y = (p.y + q.y) / 2 * h; out.a = Math.min(pointAlpha(p), pointAlpha(q)); return true;\r\n}\r\n\r\n/** Capsule radius in pixels for a radius class at a growth level and limb speed (torso lengths/s). */\r\nexport function boneRadius(kind, torso, level = 0, speed = 0, T = AURA_BODY) {\r\n  const widen = 1 + T.SPEED_WIDEN * clamp((speed || 0) / T.SPEED_REF, 0, 1);\r\n  return torso * (T.RADIUS[kind] ?? T.RADIUS.lower) * (T.LEVEL_BASE + T.LEVEL_GAIN * clamp(level, 0, 1)) * widen;\r\n}\r\n\r\n// Fixed bone slots for sheet sprites, in reveal order (more sprites appear as the level rises). Sprite i always\r\n// lives on SLOTS[i % n]: it never moves to another joint, so nothing teleports.\r\nexport const SHEET_SLOTS = Object.freeze([\r\n  [-1, -2, 0.45], [11, 13, 0.5], [12, 14, 0.5], [23, 25, 0.5], [24, 26, 0.5], [13, 15, 0.6], [14, 16, 0.6], [25, 27, 0.55],\r\n  [26, 28, 0.55], [11, 12, 0.5],\r\n]);\r\nexport const LEG_SLOTS = Object.freeze([\r\n  [23, 25, 0.35], [24, 26, 0.35], [25, 27, 0.4], [26, 28, 0.4], [23, 25, 0.8], [24, 26, 0.8], [25, 27, 0.85], [26, 28, 0.85],\r\n  [-1, -2, 0.85], [23, 24, 0.5],\r\n]);\r\nexport const ARM_SLOTS = Object.freeze([\r\n  [13, 15, 0.85], [14, 16, 0.85], [11, 13, 0.6], [12, 14, 0.6], [13, 15, 0.4], [14, 16, 0.4], [11, 13, 0.2], [12, 14, 0.2],\r\n]);\r\n\r\nconst A = { x: 0, y: 0, a: 0 }, B = { x: 0, y: 0, a: 0 };\r\n/** Point at parameter t along a slot's bone (pixels) into `out`, with fade `a` and bone direction (dx, dy unit). */\r\nexport function slotPx(points, slot, w, h, out) {\r\n  if (!jointPx(points, slot[0], w, h, A) || !jointPx(points, slot[1], w, h, B)) return false;\r\n  const t = slot[2], dx = B.x - A.x, dy = B.y - A.y, len = Math.hypot(dx, dy) || 1;\r\n  out.x = A.x + dx * t; out.y = A.y + dy * t; out.a = Math.min(A.a, B.a); out.dx = dx / len; out.dy = dy / len;\r\n  return true;\r\n}\r\n\r\n/**\r\n * Sheet sprite i for a layout ('body' | 'orbit'): pinned to its own slot for its whole life, small orbit around it\r\n * scaled by torso. Writes {x, y, a, size, rotation} into out; false when its bone is not tracked (sprite hidden).\r\n */\r\nexport function sheetSprite(points, i, layout, time, w, h, torso, level, out, T = AURA_BODY) {\r\n  const slot = SHEET_SLOTS[i % SHEET_SLOTS.length];\r\n  if (!slotPx(points, slot, w, h, out)) return false;\r\n  const orbit = layout === 'orbit', [rx, ry] = orbit ? T.WIDE_ORBIT : T.BODY_ORBIT;\r\n  const phase = time * (orbit ? 0.8 : 0.35) + i * 2.399963229728653;\r\n  out.x += Math.cos(phase) * rx * torso; out.y += Math.sin(phase) * ry * torso;\r\n  out.size = torso * (orbit ? T.ORBIT_SIZE : T.SHEET_SIZE) * (0.6 + 0.4 * clamp(level, 0, 1));\r\n  out.rotation = orbit ? phase : Math.sin(phase) * 0.25;\r\n  return true;\r\n}\r\n\r\n/** Circle-layout effects and rep pulses: centred on the body (hip / torso centre) and sized by the torso. */\r\nexport function circlePlacement(points, w, h, level, T = AURA_BODY) {\r\n  const torso = bodyScale(points, w, h, T), c = bodyCentre(points, w, h);\r\n  return { x: c.hip.x, y: c.hip.y - torso * 0.1, size: torso * T.CIRCLE_SIZE * (0.45 + 0.55 * clamp(level, 0, 1)) };\r\n}\r\nexport function pulsePlacement(points, w, h, T = AURA_BODY) {\r\n  const torso = bodyScale(points, w, h, T), c = bodyCentre(points, w, h);\r\n  return { x: c.centre.x, y: c.centre.y, size: torso * T.PULSE_SIZE };\r\n}\r\n\r\n/** Clamp a predicted landmark lead to `max` (overlay fractions) so a velocity spike never throws the aura off. */\r\nexport function clampLead(base, predicted, max = AURA_BODY.PREDICT_CLAMP) {\r\n  const dx = predicted.x - base.x, dy = predicted.y - base.y, d = Math.hypot(dx, dy);\r\n  if (!(d > max)) return predicted;\r\n  const k = max / d; return { x: base.x + dx * k, y: base.y + dy * k };\r\n}\r\n\r\n/** Linear fade toward `target` (0 or 1) over fadeMs. */\r\nexport function fadeToward(alpha, target, dtMs, fadeMs = AURA_BODY.FADE_MS) {\r\n  const step = fadeMs > 0 ? Math.max(0, dtMs) / fadeMs : 1;\r\n  return target > alpha ? Math.min(target, alpha + step) : Math.max(target, alpha - step);\r\n}\r\n\r\n// Colour per effect: outer halo, mid, hot core (RGB 0..255).\r\nconst FIRE = [[255, 84, 18], [255, 168, 40], [255, 242, 196]];\r\nexport const AURA_PALETTES = Object.freeze({\r\n  classic: FIRE, aura: FIRE, firespin: FIRE, sunburn: [[255, 120, 16], [255, 204, 64], [255, 250, 205]],\r\n  floor: [[255, 104, 28], [255, 188, 76], [255, 246, 212]], lightning: [[52, 112, 255], [118, 200, 255], [236, 250, 255]],\r\n  bluefire: [[28, 88, 255], [82, 182, 255], [222, 246, 255]], magicbubbles: [[196, 76, 255], [255, 140, 238], [255, 232, 255]],\r\n  protectioncircle: [[255, 196, 56], [255, 234, 138], [255, 255, 232]], vortex: [[116, 58, 255], [178, 120, 255], [240, 222, 255]],\r\n  flamelash: [[255, 56, 28], [255, 138, 40], [255, 230, 172]], weaponhit: [[255, 214, 86], [255, 248, 168], [255, 255, 240]],\r\n  casting: [[56, 196, 255], [140, 240, 255], [230, 255, 255]], magicspell: [[255, 76, 196], [255, 160, 230], [255, 236, 250]],\r\n  magic8: [[90, 255, 196], [178, 255, 230], [240, 255, 250]], magichit: [[176, 86, 255], [220, 170, 255], [250, 240, 255]],\r\n  nebula: [[146, 58, 255], [255, 108, 220], [255, 230, 250]], phantom: [[76, 255, 176], [168, 255, 220], [236, 255, 246]],\r\n  felspell: [[86, 255, 56], [178, 255, 108], [240, 255, 220]], midnight: [[68, 58, 210], [140, 120, 255], [230, 226, 255]],\r\n  loading: [[76, 216, 255], [168, 240, 255], [240, 255, 255]], blizzard: [[116, 196, 255], [198, 240, 255], [246, 255, 255]],\r\n});\r\nexport function auraPalette(effect, route) {\r\n  return AURA_PALETTES[effect] ?? AURA_PALETTES[route] ?? FIRE;\r\n}\r\n","arcade/power/aura-body.mjs":"// Body-anchored aura glow for the camera layers (power and hold). It owns:\r\n//  - a One Euro landmark bank on overlay fractions, fed with the frame's capture time, plus damped prediction across\r\n//    the inference lag (createAuraTracker, pure, unit tested);\r\n//  - a low-res glow canvas that sits right above the <video> with mix-blend-mode:screen (light added to the camera\r\n//    image), drawn as soft capsules along the bones, flame tongues rising off them, a punch-impact flare, and on the\r\n//    top quality tier the person's silhouette mask with rising feedback wisps, all cut out of the body itself so the\r\n//    glow reads as behind / around the person;\r\n//  - a ~250 ms fade instead of hard clears.\r\n// Quality tiers come from the shared monitor in workout-effects.mjs: Q3 mask+wisps, Q2 capsules+tongues, Q1 capsules,\r\n// Q0 nothing here (sprites only).\r\nimport { createLandmarkBank } from '../one-euro.mjs';\r\nimport { AURA_BODY, BONES, VISIBLE_ALPHA, jointPx, boneRadius, torsoPx, bodyCentre, auraPalette, fadeToward, clampLead } from './aura-anchor.mjs';\r\nimport { effectQuality, videoRectInOverlay } from '../workout-effects.mjs';\r\n\r\nconst HAND = new Set([15, 16, 17, 18, 19, 20, 21, 22]), LIMB = new Set([13, 14, 25, 26, 27, 28, 29, 30, 31, 32]);\r\nexport function auraPreset(index, T = AURA_BODY) { return HAND.has(index) ? T.FILTER.hand : LIMB.has(index) ? T.FILTER.limb : T.FILTER.torso; }\r\n\r\n/** Filter + predict mapped overlay points. pose(points, captureMs) per detection; points(nowMs) per drawn frame. */\r\nexport function createAuraTracker(T = AURA_BODY) {\r\n  const bank = createLandmarkBank(i => auraPreset(i, T));\r\n  const N = 33, raw = Array.from({ length: N }, () => ({ x: 0, y: 0, seen: false }));\r\n  const filt = Array.from({ length: N }, () => ({ x: 0, y: 0, alpha: 0, visibility: 0 }));\r\n  const out = Array.from({ length: N }, () => ({ x: 0, y: 0, alpha: 0, visibility: 0 }));\r\n  let frameMs = null;\r\n  function pose(points, tMs) {\r\n    if (!Number.isFinite(tMs) || (frameMs != null && tMs <= frameMs)) return false;\r\n    frameMs = tMs;\r\n    for (let i = 0; i < N; i++) {\r\n      const p = points?.[i], r = raw[i];\r\n      let v;\r\n      if (p && Number.isFinite(p.x) && Number.isFinite(p.y)) { r.x = p.x; r.y = p.y; r.seen = true; v = Number.isFinite(p.visibility) ? p.visibility : 0; }\r\n      else if (r.seen) v = 0; // lost: hold the last position and let the bank fade it out\r\n      else continue;\r\n      const f = bank.update(i, r.x, r.y, v, tMs), o = filt[i];\r\n      o.x = f.x; o.y = f.y; o.alpha = f.alpha; o.visibility = f.alpha > VISIBLE_ALPHA ? 1 : 0;\r\n    }\r\n    return true;\r\n  }\r\n  function points(nowMs) {\r\n    const lag = frameMs == null ? 0 : Math.max(0, nowMs - frameMs);\r\n    for (let i = 0; i < N; i++) {\r\n      const f = filt[i], o = out[i];\r\n      o.alpha = f.alpha; o.visibility = f.visibility; o.x = f.x; o.y = f.y;\r\n      if (lag > 0 && raw[i].seen && f.visibility) {\r\n        const p = bank.predict(i, Math.min(lag, 66) * T.PREDICT_GAIN / 0.6); // predict() damps by 0.6 itself\r\n        if (p) { const c = clampLead(f, p, T.PREDICT_CLAMP); o.x = c.x; o.y = c.y; }\r\n      }\r\n    }\r\n    return out;\r\n  }\r\n  function reset() { for (let i = 0; i < N; i++) { bank.reset(i); raw[i].seen = false; filt[i].alpha = filt[i].visibility = 0; } frameMs = null; }\r\n  return { pose, points, reset, get frameMs() { return frameMs; } };\r\n}\r\n\r\n// Shared sprites ----------------------------------------------------------------------------------------------\r\nconst paletteCache = new WeakMap();\r\nfunction paletteStyles(pal) {\r\n  let s = paletteCache.get(pal);\r\n  if (!s) { s = pal.map(([r, g, b]) => `rgb(${r},${g},${b})`); s.dots = null; paletteCache.set(pal, s); }\r\n  return s;\r\n}\r\nfunction softDot(color) {\r\n  const c = document.createElement('canvas'); c.width = c.height = 32;\r\n  const x = c.getContext('2d'), grad = x.createRadialGradient(16, 16, 0, 16, 16, 16);\r\n  grad.addColorStop(0, color); grad.addColorStop(0.35, color.replace('rgb(', 'rgba(').replace(')', ',.55)')); grad.addColorStop(1, 'rgba(0,0,0,0)');\r\n  x.fillStyle = grad; x.fillRect(0, 0, 32, 32); return c;\r\n}\r\nlet noiseTile = null;\r\nfunction flameNoise() {\r\n  if (noiseTile) return noiseTile;\r\n  const W = 32, H = 64, c = document.createElement('canvas'); c.width = W; c.height = H;\r\n  const x = c.getContext('2d'), img = x.createImageData(W, H), col = new Float32Array(W);\r\n  let seed = 0x2545f491; const rnd = () => { seed ^= seed << 13; seed ^= seed >>> 17; seed ^= seed << 5; return (seed >>> 0) / 4294967296; };\r\n  for (let i = 0; i < W; i++) col[i] = rnd();\r\n  for (let y = 0; y < H; y++) for (let i = 0; i < W; i++) {\r\n    // vertical streaks that wander slowly: tileable in y (sin of y), smooth in x\r\n    const phase = col[i] * 6.283 + (y / H) * 6.283 * (1 + (i % 3)), v = 0.5 + 0.5 * Math.sin(phase) * (0.6 + 0.4 * col[(i + 1) % W]);\r\n    img.data[(y * W + i) * 4 + 3] = Math.round(255 * Math.max(0, Math.min(1, v * v)));\r\n  }\r\n  x.putImageData(img, 0, 0); return (noiseTile = c);\r\n}\r\n\r\n/** Mount the glow layer for an overlay host (#coachOverlay). */\r\nexport function createAuraBody({ host, quality = effectQuality, T = AURA_BODY, target = globalThis } = {}) {\r\n  const tracker = createAuraTracker(T);\r\n  const canvas = document.createElement('canvas'); canvas.className = 'workout-aura-glow-canvas'; canvas.setAttribute('aria-hidden', 'true');\r\n  // Above the <video> in the camera stage (outside #coachOverlay's stacking context) so `screen` adds light to the\r\n  // camera image; same box as the overlay host. Falls back to the host itself when there is no video sibling.\r\n  const stage = host.parentElement, video = stage && [...stage.children].find(el => el.tagName === 'VIDEO');\r\n  canvas.style.cssText = `position:absolute;inset:0;width:100%;height:100%;pointer-events:none;display:none;mix-blend-mode:${video ? 'screen' : 'normal'}`;\r\n  if (video) video.after(canvas); else host.prepend(canvas);\r\n  const g = canvas.getContext('2d', { alpha: true });\r\n  let GW = T.GLOW_WIDTH, GH = 1, hostW = 1, hostH = 1, alpha = 0, shown = false, wantMask = false, last = 0, emitCarry = 0, disposed = false;\r\n  const snap = { level: 0, speed: 0, route: null, effect: null, pulse: 0 };\r\n  const A = { x: 0, y: 0, a: 0 }, B = { x: 0, y: 0, a: 0 };\r\n  // particles: x, y, vx, vy, age, life, size, kind\r\n  const P = new Float32Array(T.PARTICLES * 8); let pNext = 0;\r\n  const impacts = Array.from({ length: 4 }, () => ({ t: -1e9, x: 0, y: 0, s: 0 })); let iNext = 0;\r\n  let halo = null, haloCtx = null, maskCanvas = null, maskCtx = null, maskImage = null, maskInfo = null, fb = null, fbCtx = null;\r\n\r\n  function resize() {\r\n    const r = host.getBoundingClientRect(); if (!(r.width > 0 && r.height > 0)) return;\r\n    hostW = r.width; hostH = r.height;\r\n    const h = Math.max(1, Math.round(GW * r.height / r.width));\r\n    if (canvas.width !== GW || canvas.height !== h) { canvas.width = GW; canvas.height = h; GH = h; fb = null; }\r\n  }\r\n  resize();\r\n  const ro = typeof ResizeObserver === 'function' ? new ResizeObserver(resize) : null; ro?.observe(host);\r\n\r\n  function onImpact(event) {\r\n    const d = event?.detail; if (!d || !Number.isFinite(d.x) || !Number.isFinite(d.y)) return;\r\n    let x = d.x, y = d.y;\r\n    if (x > 1.5 || y > 1.5) { const r = host.getBoundingClientRect(); x = (x - r.left) / (r.width || 1); y = (y - r.top) / (r.height || 1); }\r\n    const slot = impacts[iNext++ % impacts.length];\r\n    slot.t = performance.now(); slot.x = x; slot.y = y; slot.s = Math.max(0.2, Math.min(1, Number(d.strength) || 0.7));\r\n  }\r\n  target.addEventListener?.('myr5:punch-impact', onImpact);\r\n\r\n  function setWantMask(on) { if (on !== wantMask) { wantMask = on; globalThis.myr5AuraWantsMask = on; } }\r\n\r\n  /** New detection: mapped overlay points, capture time, optional mask {w,h,data} and video geometry. */\r\n  function pose(points, tMs, mask = null, geometry = null) {\r\n    if (disposed) return;\r\n    tracker.pose(points, tMs);\r\n    if (!mask?.data || !geometry || quality.tier < 3) { maskInfo = null; return; }\r\n    if (!maskCanvas || maskCanvas.width !== mask.w || maskCanvas.height !== mask.h) {\r\n      maskCanvas = document.createElement('canvas'); maskCanvas.width = mask.w; maskCanvas.height = mask.h;\r\n      maskCtx = maskCanvas.getContext('2d'); maskImage = maskCtx.createImageData(mask.w, mask.h);\r\n    }\r\n    const [r, gg, b] = auraPalette(snap.effect, snap.route)[0], d = maskImage.data, m = mask.data;\r\n    for (let k = 0, j = 0; k < m.length; k++, j += 4) { d[j] = r; d[j + 1] = gg; d[j + 2] = b; d[j + 3] = m[k]; }\r\n    maskCtx.putImageData(maskImage, 0, 0);\r\n    const rect = videoRectInOverlay({ videoWidth: geometry.videoWidth, videoHeight: geometry.videoHeight, hostWidth: hostW, hostHeight: hostH });\r\n    const hip = bodyCentre(points, 1, 1).hip; // raw hip of the mask's own frame (same inference)\r\n    maskInfo = rect ? { rect, mirrored: !!geometry.mirrored, hx: hip.x, hy: hip.y, at: performance.now() } : null;\r\n  }\r\n\r\n  function drawMask(ctx, scale, dx, dy, cx, cy) {\r\n    const { rect, mirrored } = maskInfo, x0 = rect.x * GW, y0 = rect.y * GH, rw = rect.w * GW, rh = rect.h * GH;\r\n    ctx.save(); ctx.translate(cx, cy); ctx.scale(scale, scale); ctx.translate(dx - cx, dy - cy);\r\n    if (mirrored) { ctx.translate(2 * x0 + rw, 0); ctx.scale(-1, 1); }\r\n    ctx.drawImage(maskCanvas, x0, y0, rw, rh); ctx.restore();\r\n  }\r\n\r\n  function emit(points, torso, level, speed, dt, styles) {\r\n    const rate = (T.EMIT_BASE + T.EMIT_LEVEL * level) * (1 + Math.min(1, speed / T.SPEED_REF));\r\n    emitCarry += rate * dt;\r\n    let guard = 0;\r\n    while (emitCarry >= 1 && guard++ < 24) {\r\n      emitCarry -= 1;\r\n      const bone = BONES[(Math.random() * BONES.length) | 0];\r\n      if (!jointPx(points, bone[0], GW, GH, A) || !jointPx(points, bone[1], GW, GH, B) || Math.min(A.a, B.a) < 0.5) continue;\r\n      const t = Math.random(), r = boneRadius(bone[2], torso, level, speed, T), ex = B.x - A.x, ey = B.y - A.y, len = Math.hypot(ex, ey) || 1;\r\n      const off = (Math.random() - 0.5) * 0.9 * r, k = pNext++ % T.PARTICLES * 8;\r\n      P[k] = A.x + ex * t - ey / len * off; P[k + 1] = A.y + ey * t + ex / len * off;\r\n      P[k + 2] = (Math.random() - 0.5) * 0.25 * torso; P[k + 3] = -(0.7 + 0.7 * Math.random()) * torso;\r\n      P[k + 4] = 0; P[k + 5] = 0.32 + 0.34 * Math.random(); P[k + 6] = r * (0.3 + 0.25 * Math.random()); P[k + 7] = Math.random() < 0.4 ? 2 : 1;\r\n    }\r\n    if (!styles.dots) styles.dots = [null, softDot(styles[0]), softDot(styles[1]), softDot(styles[2])];\r\n  }\r\n  function drawParticles(dt, gain, styles) {\r\n    for (let k = 0; k < P.length; k += 8) {\r\n      if (!(P[k + 5] > 0)) continue;\r\n      P[k + 4] += dt; const life = P[k + 4] / P[k + 5];\r\n      if (life >= 1) { P[k + 5] = 0; continue; }\r\n      P[k] += P[k + 2] * dt; P[k + 1] += P[k + 3] * dt; P[k + 2] *= 0.97;\r\n      const size = P[k + 6] * (1 - life * 0.55);\r\n      g.globalAlpha = Math.min(1, gain * 0.6 * (1 - life) ** 1.3);\r\n      g.drawImage(styles.dots[P[k + 7]], P[k] - size / 2, P[k + 1] - size * 0.8, size, size * 1.6);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Per animation frame. live: {active, level, speed, route, effect, pulse} (engine state); points: drawn landmarks\r\n   * from points(now). Returns the layer fade alpha (0..1) so the caller fades its sprites with the glow.\r\n   */\r\n  function draw(now, live, points) {\r\n    if (disposed) return 0;\r\n    const dt = last ? Math.min(0.05, Math.max(0, (now - last) / 1000)) : 0; last = now;\r\n    const on = !!(live?.active && live.level > 0);\r\n    if (on) { snap.level = live.level; snap.speed = live.speed || 0; snap.route = live.route; snap.effect = live.effect; snap.pulse = live.pulse || 0; }\r\n    else snap.pulse = 0;\r\n    alpha = fadeToward(alpha, on ? 1 : 0, dt * 1000, T.FADE_MS);\r\n    const tier = quality.tier;\r\n    setWantMask(tier >= 3 && alpha > 0);\r\n    if (alpha <= 0 || tier < 1) { if (shown) { g.clearRect(0, 0, canvas.width, canvas.height); canvas.style.display = 'none'; shown = false; } return alpha; }\r\n    if (!shown) { canvas.style.display = 'block'; shown = true; }\r\n    g.setTransform(1, 0, 0, 1, 0, 0); g.globalAlpha = 1; g.globalCompositeOperation = 'source-over'; g.clearRect(0, 0, GW, GH);\r\n    const torso = torsoPx(points, GW, GH); if (!torso) return alpha;\r\n    const level = snap.level, speed = snap.speed, styles = paletteStyles(auraPalette(snap.effect, snap.route));\r\n    let impact = 0;\r\n    for (const im of impacts) { const k = (now - im.t) / T.IMPACT_MS; if (k >= 0 && k < 1) impact = Math.max(impact, im.s * (1 - k)); }\r\n    const gain = alpha * (0.5 + 0.8 * level) * (1 + T.IMPACT_GAIN * impact + 0.4 * snap.pulse);\r\n    g.globalCompositeOperation = 'lighter'; g.lineCap = 'round'; g.lineJoin = 'round';\r\n\r\n    // Q3: silhouette halo + rising wisps from the person mask (falls back to capsules alone without a mask).\r\n    const centre = bodyCentre(points, GW, GH);\r\n    if (tier >= 3 && maskInfo && now - maskInfo.at < 400) {\r\n      if (!fb || fb.width !== GW || fb.height !== GH) { fb = document.createElement('canvas'); fb.width = GW; fb.height = GH; fbCtx = fb.getContext('2d'); }\r\n      const hip = centre.hip, dx = hip.x - maskInfo.hx * GW, dy = hip.y - maskInfo.hy * GH; // follow the predicted body\r\n      fbCtx.globalCompositeOperation = 'copy'; fbCtx.globalAlpha = T.WISP_DECAY; fbCtx.drawImage(fb, 0, -T.WISP_RISE);\r\n      fbCtx.globalCompositeOperation = 'lighter'; fbCtx.globalAlpha = Math.min(1, 0.4 * gain); drawMask(fbCtx, 1.06, dx, dy, centre.centre.x, centre.centre.y);\r\n      const noise = flameNoise(), scroll = (now * 0.05) % GH;\r\n      fbCtx.globalCompositeOperation = 'destination-out'; fbCtx.globalAlpha = 0.45;\r\n      fbCtx.drawImage(noise, 0, scroll - GH, GW, GH); fbCtx.drawImage(noise, 0, scroll, GW, GH);\r\n      g.globalAlpha = 1; g.drawImage(fb, 0, 0);\r\n      g.globalAlpha = Math.min(1, 0.5 * gain); drawMask(g, 1.1, dx, dy, centre.centre.x, centre.centre.y);\r\n      g.globalAlpha = Math.min(1, 0.28 * gain); drawMask(g, 1.22, dx, dy, centre.centre.x, centre.centre.y);\r\n    }\r\n\r\n    // Capsule halo along the bones on a 1/4-res canvas: outer colour -> mid -> hot core toward the bone, painted\r\n    // source-over (overlaps never burn to white), then upscaled with smoothing onto the glow layer = soft falloff.\r\n    const D = T.HALO_DOWNSCALE, hw = Math.max(1, Math.round(GW / D)), hh = Math.max(1, Math.round(GH / D));\r\n    if (!halo || halo.width !== hw || halo.height !== hh) { halo = document.createElement('canvas'); halo.width = hw; halo.height = hh; haloCtx = halo.getContext('2d'); }\r\n    const h = haloCtx; h.globalCompositeOperation = 'source-over'; h.globalAlpha = 1; h.clearRect(0, 0, hw, hh); h.lineCap = 'round';\r\n    const passes = tier >= 2 ? PASSES_FULL : PASSES_LITE, ht = torso / D;\r\n    for (const [width, a, colour] of passes) {\r\n      h.strokeStyle = h.fillStyle = styles[colour];\r\n      for (const [i, j, kind] of BONES) {\r\n        if (!jointPx(points, i, hw, hh, A) || !jointPx(points, j, hw, hh, B)) continue;\r\n        const r = boneRadius(kind, ht, level, speed, T) * width;\r\n        h.globalAlpha = Math.min(1, a * Math.min(A.a, B.a));\r\n        h.beginPath();\r\n        if (i === j) { h.arc(A.x, A.y, r, 0, 6.2832); h.fill(); }\r\n        else { h.lineWidth = 2 * r; h.moveTo(A.x, A.y); h.lineTo(B.x, B.y); h.stroke(); }\r\n      }\r\n    }\r\n    g.globalAlpha = Math.min(1, gain); g.imageSmoothingEnabled = true; g.drawImage(halo, 0, 0, GW, GH);\r\n    if (tier >= 2) { emit(points, torso, level, speed, dt, styles); drawParticles(dt, gain, styles); }\r\n\r\n    // Punch impact flare: hot flash + expanding ring at the fist.\r\n    for (const im of impacts) {\r\n      const k = (now - im.t) / T.IMPACT_MS; if (k < 0 || k >= 1) continue;\r\n      const x = im.x * GW, y = im.y * GH, s = torso * (0.35 + 0.3 * im.s) * (1 - k * 0.4);\r\n      if (!styles.dots) styles.dots = [null, softDot(styles[0]), softDot(styles[1]), softDot(styles[2])];\r\n      g.globalAlpha = Math.min(1, alpha * im.s * (1 - k) ** 2);\r\n      g.drawImage(styles.dots[3], x - s, y - s, 2 * s, 2 * s); g.drawImage(styles.dots[2], x - s * 1.6, y - s * 1.6, 3.2 * s, 3.2 * s);\r\n      g.strokeStyle = styles[1]; g.lineWidth = Math.max(1, torso * 0.05 * (1 - k)); g.globalAlpha = Math.min(1, alpha * (1 - k));\r\n      g.beginPath(); g.arc(x, y, torso * (0.12 + 0.5 * k), 0, 6.2832); g.stroke();\r\n    }\r\n\r\n    // Keep the person visible: punch the body itself out of the glow (soft, so a rim of light stays on the edges).\r\n    g.globalCompositeOperation = 'destination-out'; g.globalAlpha = T.CUT_ALPHA; g.strokeStyle = g.fillStyle = '#000';\r\n    if (tier >= 3 && maskInfo && now - maskInfo.at < 400) drawMask(g, 1, centre.hip.x - maskInfo.hx * GW, centre.hip.y - maskInfo.hy * GH, centre.centre.x, centre.centre.y);\r\n    else {\r\n      const quad = [11, 12, 24, 23];\r\n      if (quad.every(i => jointPx(points, i, GW, GH, A))) {\r\n        g.beginPath(); quad.forEach((i, n) => { jointPx(points, i, GW, GH, A); n ? g.lineTo(A.x, A.y) : g.moveTo(A.x, A.y); }); g.closePath(); g.fill();\r\n      }\r\n      for (const [i, j, kind] of BONES) {\r\n        if (!jointPx(points, i, GW, GH, A) || !jointPx(points, j, GW, GH, B)) continue;\r\n        const r = torso * (T.BODY[kind] ?? T.BODY.lower) * T.CUT;\r\n        g.beginPath();\r\n        if (i === j) { g.arc(A.x, A.y, r, 0, 6.2832); g.fill(); }\r\n        else { g.lineWidth = 2 * r; g.moveTo(A.x, A.y); g.lineTo(B.x, B.y); g.stroke(); }\r\n      }\r\n    }\r\n    g.globalCompositeOperation = 'source-over'; g.globalAlpha = 1;\r\n    return alpha;\r\n  }\r\n\r\n  function reset() { tracker.reset(); alpha = 0; maskInfo = null; P.fill(0); for (const im of impacts) im.t = -1e9; }\r\n  function dispose() {\r\n    if (disposed) return; disposed = true; ro?.disconnect(); target.removeEventListener?.('myr5:punch-impact', onImpact);\r\n    setWantMask(false); canvas.remove(); if (globalThis.myr5AuraBody === api) globalThis.myr5AuraBody = null;\r\n  }\r\n  const api = { pose, points: now => tracker.points(now), draw, reset, dispose, resize, canvas, get alpha() { return alpha; }, tracker };\r\n  globalThis.myr5AuraBody = api; // debug / harness: the drawn (filtered + predicted) skeleton\r\n  return api;\r\n}\r\n// [width multiple, alpha, palette slot]\r\nconst PASSES_FULL = [[1, 0.16, 0], [0.8, 0.22, 0], [0.6, 0.3, 1], [0.42, 0.42, 1], [0.28, 0.55, 2]];\r\nconst PASSES_LITE = [[1, 0.2, 0], [0.65, 0.35, 1], [0.35, 0.5, 2]];\r\n","arcade/power/aura-growth.mjs":"// One growth model for every aura group. Pure and renderer-free so holds, reps, punches and steps share it.\r\n// level 0..1 is what the renderers draw: it starts visible the instant an exercise is active, steps up on every\r\n// counted rep / detected punch or step, creeps up per second held or moved, and eases back down when you stop.\r\nexport const AURA_TUNING = Object.freeze({\r\n  START: 0.26,          // level the moment the exercise is active (small but clearly visible)\r\n  REP_STEP: 0.08,       // instant step per counted rep\r\n  BURST_STEP: 0.045,    // instant step per punch / knee lift / jump (fast limb burst) on pace and step work\r\n  HOLD_FIRST_S: 0,      // still-hold seconds before the first plant/animal/ice shows (engine default is 5 s): none, it shows at once\r\n  HOLD_PER_SEC: 0.05,   // growth per second of valid hold\r\n  MOVE_PER_SEC: 0.1,    // continuous growth per second at MOVE_CAP body-lengths/s of limb speed\r\n  MOVE_CAP: 1.6,        // limb speed (torso lengths per second) that earns full MOVE_PER_SEC\r\n  MOVE_MIN: 0.25,       // slower than this is not movement\r\n  BURST_SPEED: 1.1,     // limb speed that counts as a punch / step\r\n  BURST_COOLDOWN_S: 0.22,\r\n  IDLE_GRACE_S: 1.2,    // seconds of no progress before decay starts\r\n  DECAY_PER_SEC: 0.1,   // decay toward START (never to nothing while the exercise is active)\r\n  EASE_PER_SEC: 9,      // drawn level chases the goal at this rate (smooth, but a step is seen within ~0.1 s)\r\n  // Discrete aura levels (1 = the aura is on, 2, 3 = the 3D coach joins). Thresholds on the growth goal (0..1).\r\n  // L3 is about 8 good reps (~12 s of steady work) or ~13 s of valid hold from a fresh start: roughly mid-set.\r\n  LEVELS: [0.26, 0.6, 0.92],\r\n  LEVEL_HYST: 0.15,     // goal must fall this far below a threshold to lose that level (no flicker on a short pause)\r\n  MAX: 1,\r\n  PULSE_FALL_PER_SEC: 2.5,\r\n  MAX_DT_S: 0.25,\r\n  LAYER_OPACITY: 0.85,   // effect canvas opacity at level 0; it falls with level so a strong aura is brighter yet see-through\r\n  OPACITY_FALL: 0.45,    // canvas opacity = LAYER_OPACITY - OPACITY_FALL * level (0.85 -> 0.40)\r\n  SPRITE_ALPHA_FALL: 0.5, // sprite alpha = 1 - SPRITE_ALPHA_FALL * level\r\n});\r\n/** Whole-canvas opacity for a drawn level 0..1 (written per frame; the canvas is created once). */\r\nexport const layerOpacity = (level, T = AURA_TUNING) => T.LAYER_OPACITY - T.OPACITY_FALL * clamp(level || 0, 0, 1);\r\n\r\nconst clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));\r\n\r\n// Sheets whose frames are mostly empty (measured alpha coverage) draw larger so every pick is readable on a phone.\r\nexport const SPARSE_BOOST = Object.freeze({ casting: 2, magichit: 2, weaponhit: 1.8, bluefire: 1.7, flamelash: 1.7, magicspell: 1.5, magicbubbles: 1.5, firespin: 1.4, phantom: 1.4, midnight: 1.4, protectioncircle: 1.3 });\r\n\r\nconst LIMBS = [15, 16, 25, 26, 27, 28]; // hands, knees, ankles\r\nconst ok = p => p && Number.isFinite(p.x) && Number.isFinite(p.y) && (p.visibility ?? 1) >= 0.5;\r\n\r\nfunction torso(points) {\r\n  const a = points[11], b = points[23], c = points[12], d = points[24];\r\n  const L = [[a, b], [c, d], [a, c]].filter(([p, q]) => ok(p) && ok(q)).map(([p, q]) => Math.hypot(p.x - q.x, p.y - q.y));\r\n  return L.length ? Math.max(0.05, Math.max(...L)) : 0.25;\r\n}\r\n\r\n/** Discrete level for a growth goal, with hysteresis from the previous level. */\r\nexport function auraLevel(goal, previous = 0, T = AURA_TUNING) {\r\n  let level = 0;\r\n  T.LEVELS.forEach((at, i) => { if (goal >= at - 1e-9 || (previous > i && goal >= at - T.LEVEL_HYST)) level = i + 1; });\r\n  return level;\r\n}\r\nfunction announce(level, previous) {\r\n  try { if (typeof CustomEvent === 'function' && globalThis.dispatchEvent) globalThis.dispatchEvent(new CustomEvent('myr5:aura-level', { detail: { level, previous } })); } catch { /* cosmetic */ }\r\n}\r\n\r\nfunction announceProgress(progress) {\r\n  try { if (typeof CustomEvent === 'function' && globalThis.dispatchEvent) globalThis.dispatchEvent(new CustomEvent('myr5:power-progress', { detail: { level: progress } })); } catch { /* cosmetic */ }\r\n}\r\n\r\nexport function createGrowth(tuning = AURA_TUNING) {\r\n  const T = { ...AURA_TUNING, ...tuning };\r\n  const s = { level: 0, goal: 0, pulse: 0, speed: 0, started: false, bumps: 0, auraLevel: 0 };\r\n  const setAura = next => { if (next !== s.auraLevel) { const previous = s.auraLevel; s.auraLevel = next; announce(next, previous); } };\r\n  let sentProgress = 0, held = 0, prev = null, lastNow = null, lastGain = 0, lastBurst = -Infinity, lastUnits = null, burstArmed = true;\r\n\r\n  function reset() { Object.assign(s, { level: 0, goal: 0, pulse: 0, speed: 0, started: false, bumps: 0 }); setAura(0); held = 0; prev = lastNow = lastUnits = null; lastGain = 0; lastBurst = -Infinity; burstArmed = true; return s; }\r\n  function bump(amount, now) { s.goal = Math.min(T.MAX, s.goal + amount); lastGain = now; s.pulse = 1; s.bumps++; }\r\n\r\n  /** kind: 'hold' | 'reps' | 'move' (pace / steps). units: counted reps. valid: hold is valid. points: normalized landmarks. */\r\n  function update({ now, points, kind, units = 0, valid = true, target }) {\r\n    // target (circuit only): reps or seconds that must take the aura from START to 1. Without it today's constants apply.\r\n    const tgt = Number.isFinite(target) && target > 0 ? target : 0, per = tgt ? (1 - T.START) / tgt : 0;\r\n    if (!s.started) { s.started = true; s.goal = s.level = T.START; lastGain = now; }\r\n    const dt = lastNow == null ? 0 : clamp((now - lastNow) / 1000, 0, T.MAX_DT_S);\r\n    lastNow = now;\r\n    // limb speed in torso lengths per second\r\n    let speed = 0;\r\n    if (points && prev && dt > 0) {\r\n      const scale = torso(points), v = [];\r\n      for (const i of LIMBS) if (ok(points[i]) && ok(prev[i])) v.push(Math.hypot(points[i].x - prev[i].x, points[i].y - prev[i].y) / scale / dt);\r\n      if (v.length) speed = Math.max(...v);\r\n    }\r\n    if (points) prev = points.map(p => (ok(p) ? { x: p.x, y: p.y, visibility: p.visibility } : null));\r\n    s.speed = speed;\r\n    if (kind === 'hold') {\r\n      if (valid && dt) { s.goal = Math.min(T.MAX, s.goal + (tgt ? per : T.HOLD_PER_SEC) * dt); lastGain = now; held += dt; }\r\n    } else {\r\n      if (kind === 'reps') {\r\n        if (lastUnits != null && units > lastUnits) bump((tgt ? per : T.REP_STEP) * Math.min(3, units - lastUnits), now);\r\n        lastUnits = units;\r\n      } else if (!tgt && speed >= T.BURST_SPEED && burstArmed && now / 1000 - lastBurst >= T.BURST_COOLDOWN_S) {\r\n        bump(T.BURST_STEP, now); lastBurst = now / 1000; burstArmed = false;\r\n      }\r\n      if (speed < T.BURST_SPEED * 0.6) burstArmed = true;\r\n      if (speed >= T.MOVE_MIN && dt) { s.goal = Math.min(T.MAX, s.goal + (tgt ? per : T.MOVE_PER_SEC * Math.min(1, speed / T.MOVE_CAP)) * dt); lastGain = now; }\r\n    }\r\n    // Circuit sets never decay in the last second (rep set: last rep) so they end at full power.\r\n    const finishing = tgt && (kind === 'hold' ? held >= tgt - 1 : kind === 'reps' ? units >= tgt - 1 : false);\r\n    if (dt && !finishing && (now - lastGain) / 1000 > T.IDLE_GRACE_S) s.goal = Math.max(T.START, s.goal - T.DECAY_PER_SEC * dt);\r\n    setAura(auraLevel(s.goal, s.auraLevel, T));\r\n    return s;\r\n  }\r\n\r\n  /** Per frame: ease the drawn level toward the goal and fall the pulse. */\r\n  function step(rawDt = 0) {\r\n    const dt = Math.min(0.05, Math.max(0, Number(rawDt) || 0));\r\n    s.level += (s.goal - s.level) * Math.min(1, dt * T.EASE_PER_SEC);\r\n    s.pulse = Math.max(0, s.pulse - dt * T.PULSE_FALL_PER_SEC);\r\n    // Power-up progress 0..1 (START -> MAX) for the circuit power chips; only sent when it moves.\r\n    const p = s.started ? clamp((s.level - T.START) / (1 - T.START), 0, 1) : 0;\r\n    if (Math.abs(p - sentProgress) >= 0.01 || (p === 1 && sentProgress < 1) || (p === 0 && sentProgress > 0)) { sentProgress = p; announceProgress(p); }\r\n    return s;\r\n  }\r\n  return { state: s, update, step, reset, tuning: T };\r\n}\r\n","arcade/power/aura-options.mjs":"import { EXERCISES } from '../../exercise-library.mjs';\r\n\r\nexport const AURA_CHOICE_KEY = 'myr5-aura-choice-v1';\r\nexport const EFFECT_CHOICES_KEY = 'myr5-exercise-effect-choices-v1';\r\n\r\nexport const AURA_OPTIONS = Object.freeze([\r\n  { id: 'random', label: 'Random · rotate effects' },\r\n  { id: 'classic', label: 'Classic fire aura' },\r\n  { id: 'bluefire', label: 'Blue flame', asset: '3_bluefire_spritesheet.png', columns: 8, rows: 8 },\r\n  { id: 'magicbubbles', label: 'Magic bubbles', asset: '20_magicbubbles_spritesheet.png', columns: 8, rows: 8 },\r\n  { id: 'protectioncircle', label: 'Protection circle', asset: '8_protectioncircle_spritesheet.png', columns: 8, rows: 8, layout: 'circle' },\r\n  { id: 'vortex', label: 'Vortex', asset: '13_vortex_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'firespin', label: 'Fire spin', asset: '7_firespin_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'flamelash', label: 'Flame lash', asset: '6_flamelash_spritesheet.png', columns: 7, rows: 7 },\r\n  { id: 'weaponhit', label: 'Weapon hit spark', asset: '10_weaponhit_spritesheet.png', columns: 6, rows: 6 },\r\n  { id: 'casting', label: 'Casting ring', asset: '4_casting_spritesheet.png', columns: 9, rows: 9, layout: 'circle' },\r\n  { id: 'magicspell', label: 'Magic spell burst', asset: '1_magicspell_spritesheet.png', columns: 9, rows: 9 },\r\n  { id: 'magic8', label: 'Magic-eight trail', asset: '2_magic8_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'magichit', label: 'Magic hit', asset: '5_magickahit_spritesheet.png', columns: 7, rows: 7 },\r\n  { id: 'nebula', label: 'Nebula', asset: '12_nebula_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'phantom', label: 'Phantom', asset: '14_phantom_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'sunburn', label: 'Sunburn', asset: '16_sunburn_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'felspell', label: 'Felspell', asset: '17_felspell_spritesheet.png', columns: 10, rows: 10, layout: 'orbit' },\r\n  { id: 'midnight', label: 'Midnight', asset: '18_midnight_spritesheet.png', columns: 8, rows: 8, layout: 'orbit' },\r\n  { id: 'loading', label: 'Loading ring', asset: '15_loading_spritesheet.png', columns: 11, rows: 11, layout: 'circle' },\r\n  { id: 'blizzard', label: 'Blizzard ice', asset: '19_freezing_spritesheet.png', columns: 10, rows: 10, layout: 'orbit' },\r\n  { id: 'fern', label: 'Fern growth', ecology: { kind: 'plant', variant: 'fern' } },\r\n  { id: 'vine', label: 'Vine drift', ecology: { kind: 'plant', variant: 'vine' } },\r\n  { id: 'flower', label: 'Flower bloom', ecology: { kind: 'plant', variant: 'flower' } },\r\n  { id: 'mushroom', label: 'Fungi bloom', ecology: { kind: 'plant', variant: 'mushroom' } },\r\n  { id: 'grass', label: 'Grass growth', ecology: { kind: 'plant', variant: 'grass' } },\r\n  { id: 'reed', label: 'Reed growth', ecology: { kind: 'plant', variant: 'reed' } },\r\n  { id: 'rabbit', label: 'Rabbit companion', ecology: { kind: 'animal', variant: 'rabbit' } },\r\n  { id: 'fox', label: 'Fox companion', ecology: { kind: 'animal', variant: 'fox' } },\r\n  { id: 'bear', label: 'Bear companion', ecology: { kind: 'animal', variant: 'bear' } },\r\n  { id: 'deer', label: 'Deer companion', ecology: { kind: 'animal', variant: 'deer' } },\r\n  { id: 'wolf', label: 'Wolf companion', ecology: { kind: 'animal', variant: 'wolf' } },\r\n  { id: 'boar', label: 'Boar companion', ecology: { kind: 'animal', variant: 'boar' } },\r\n  { id: 'rock', label: 'Stone chips', ecology: { kind: 'mineral', variant: 'rock' } },\r\n  { id: 'crystal-blue', label: 'Blue crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'blue' } },\r\n  { id: 'crystal-ice', label: 'Ice crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'ice' } },\r\n  { id: 'crystal-red', label: 'Red crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'red' } },\r\n  { id: 'crystal-green', label: 'Green crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'green' } },\r\n  { id: 'crystal-gold', label: 'Gold crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'gold' } },\r\n  { id: 'crystal-purple', label: 'Purple crystal shards', ecology: { kind: 'mineral', variant: 'crystal', palette: 'purple' } },\r\n  { id: 'lightning', label: 'Lightning arcs', route: 'lightning' },\r\n  { id: 'floor', label: 'Floor sparks', route: 'floor' },\r\n]);\r\n\r\nexport const ROTATING_AURAS = Object.freeze(AURA_OPTIONS.filter(option => option.id !== 'random').map(option => option.id));\r\nexport const EFFECT_OPTION_BY_ID = new Map(AURA_OPTIONS.map(option => [option.id, option]));\r\n\r\nconst ECOLOGY_IDS = AURA_OPTIONS.filter(option => option.ecology).map(option => option.id);\r\nconst GROUPS = [\r\n  { id: 'yoga', label: 'Yoga & mobility', options: [...ECOLOGY_IDS, 'magicbubbles', 'nebula', 'phantom', 'midnight'], defaultEffect: 'fern' },\r\n  { id: 'stances', label: 'Martial stances', options: ['flamelash', 'casting', 'felspell'], defaultEffect: 'casting' },\r\n  { id: 'legs', label: 'Legs & hips', options: ['classic', 'firespin', 'vortex', 'sunburn'], defaultEffect: 'classic' },\r\n  { id: 'shoulders', label: 'Arms & shoulders', options: ['magic8', 'weaponhit', 'magichit', 'lightning'], defaultEffect: 'lightning' },\r\n  { id: 'floor', label: 'Floor exercises', options: ['bluefire', 'magicspell', 'floor'], defaultEffect: 'floor' },\r\n  { id: 'core', label: 'Core & balance', options: ['blizzard', 'protectioncircle', 'loading'], defaultEffect: 'blizzard' },\r\n].map(group => Object.freeze({ ...group, options: Object.freeze(['random', ...group.options]) }));\r\n\r\nexport const EFFECT_GROUPS = Object.freeze(GROUPS);\r\nconst GROUP_BY_ID = new Map(EFFECT_GROUPS.map(group => [group.id, group]));\r\nconst HOME_GROUP = new Map(EFFECT_GROUPS.flatMap(group => group.options.filter(id => id !== 'random').map(id => [id, group.id])));\r\nconst DEFAULT_CHOICES = Object.freeze(Object.fromEntries(EFFECT_GROUPS.map(group => [group.id, 'random'])));\r\nconst VALID = new Set(AURA_OPTIONS.map(option => option.id));\r\n\r\n/** camera: live camera effects also cover boxing (Arms & shoulders pick) and cardio (Legs & hips pick); saved avatar history still skips them. */\r\nexport function effectGroupForExercise(mode, { camera = false } = {}) {\r\n  const ex = typeof mode === 'string' ? EXERCISES[mode] : mode;\r\n  if (!ex || typeof ex !== 'object') return null;\r\n  if (ex.group === 'boxing') return camera ? 'shoulders' : null;\r\n  if (ex.group === 'cardio') return camera ? 'legs' : null;\r\n  if (ex.kind === 'hold') {\r\n    if (ex.group === 'stances') return 'stances';\r\n    if (ex.group === 'core' || ex.group === 'balance') return 'core';\r\n    if (ex.group === 'yoga' || ex.group === 'legs') return 'yoga';\r\n  }\r\n  if (['pushup', 'bridge', 'plank', 'sideplank'].includes(ex.detector)) return 'floor';\r\n  if (ex.group === 'shoulders') return 'shoulders';\r\n  if (ex.group === 'legs' || ex.group === 'hips') return 'legs';\r\n  return null;\r\n}\r\n\r\nfunction validChoice(group, choice) { return GROUP_BY_ID.get(group)?.options.includes(choice) ?? false; }\r\nfunction choiceRecord(storage) {\r\n  try {\r\n    const raw = (storage ?? globalThis.localStorage)?.getItem(EFFECT_CHOICES_KEY);\r\n    if (raw) {\r\n      const data = JSON.parse(raw);\r\n      if (data?.version === 1 && data.choices && typeof data.choices === 'object') {\r\n        return Object.fromEntries(EFFECT_GROUPS.map(group => [group.id,\r\n          validChoice(group.id, data.choices[group.id]) ? data.choices[group.id] : group.defaultEffect]));\r\n      }\r\n    }\r\n  } catch { /* fall through to safe defaults or one-group migration */ }\r\n  const choices = { ...DEFAULT_CHOICES };\r\n  try {\r\n    const legacy = (storage ?? globalThis.localStorage)?.getItem(AURA_CHOICE_KEY);\r\n    const group = HOME_GROUP.get(legacy);\r\n    if (group && validChoice(group, legacy)) choices[group] = legacy;\r\n  } catch { /* inaccessible storage uses defaults */ }\r\n  return choices;\r\n}\r\n\r\nexport function readEffectChoices(storage) { return choiceRecord(storage); }\r\n\r\nexport function saveEffectChoice(group, choice, { storage, target } = {}) {\r\n  if (!validChoice(group, choice)) return false;\r\n  try {\r\n    const store = storage ?? globalThis.localStorage;\r\n    if (!store?.setItem) return false;\r\n    const choices = choiceRecord(storage);\r\n    choices[group] = choice;\r\n    store.setItem(EFFECT_CHOICES_KEY, JSON.stringify({ version: 1, choices }));\r\n  } catch { return false; }\r\n  try { (target ?? globalThis.window)?.dispatchEvent?.(new CustomEvent('myr5:exercise-effect-choice-change', { detail: { group, choice } })); }\r\n  catch { /* Storage events synchronize other tabs. */ }\r\n  return true;\r\n}\r\n\r\nexport function resolveEffectForGroup(group, choices = {}, random = Math.random) {\r\n  const definition = GROUP_BY_ID.get(group);\r\n  if (!definition) return null;\r\n  const choice = choices[group] ?? definition.defaultEffect;\r\n  if (choice !== 'random') return validChoice(group, choice) ? choice : definition.defaultEffect;\r\n  const variants = definition.options.filter(id => id !== 'random');\r\n  const value = Number(random());\r\n  const index = Math.min(variants.length - 1, Math.max(0, Math.floor((Number.isFinite(value) ? value : 0) * variants.length)));\r\n  return variants[index];\r\n}\r\n\r\nexport function resolveExerciseEffect(mode, choices = {}, random = Math.random, opts) {\r\n  return resolveEffectForGroup(effectGroupForExercise(mode, opts), choices, random);\r\n}\r\n\r\n// Legacy global helpers stay for old clients; the new runtime reads only the\r\n// versioned per-exercise-group record above.\r\nexport function readAuraChoice(storage) {\r\n  try {\r\n    const choice = (storage ?? globalThis.localStorage)?.getItem(AURA_CHOICE_KEY);\r\n    return VALID.has(choice) ? choice : 'random';\r\n  } catch { return 'random'; }\r\n}\r\n\r\nexport function resolveAuraChoice(choice = 'random', random = Math.random) {\r\n  if (choice === 'random') {\r\n    const value = Number(random());\r\n    const index = Math.min(ROTATING_AURAS.length - 1, Math.max(0, Math.floor((Number.isFinite(value) ? value : 0) * ROTATING_AURAS.length)));\r\n    return ROTATING_AURAS[index];\r\n  }\r\n  return VALID.has(choice) ? choice : 'classic';\r\n}\r\n\r\nexport function saveAuraChoice(choice, { storage, target } = {}) {\r\n  if (!VALID.has(choice)) return false;\r\n  try { (storage ?? globalThis.localStorage)?.setItem(AURA_CHOICE_KEY, choice); }\r\n  catch { return false; }\r\n  try { (target ?? globalThis.window)?.dispatchEvent?.(new CustomEvent('myr5:aura-choice-change', { detail: { choice } })); }\r\n  catch { /* Storage changes in another tab still arrive through the storage event. */ }\r\n  return true;\r\n}\r\n","arcade/power/avatar-power.mjs":"// A silent, intermittent pixel effect attached to the companion card. It never\r\n// calls the coach's play/walk APIs, so existing gestures remain in control.\r\nimport { renderPower } from './power.mjs';\r\nimport { renderBlizzard } from '../hold/blizzard.mjs';\r\nimport { renderEcosystem } from '../hold/render.mjs';\r\nimport { AURA_OPTIONS, EFFECT_CHOICES_KEY, effectGroupForExercise, readEffectChoices, resolveEffectForGroup } from './aura-options.mjs';\r\nimport { EXERCISES } from '../../exercise-library.mjs';\r\n\r\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\r\nfunction randomFor(seed = 1) {\r\n  let value = (Number(seed) >>> 0) || 0x9e3779b9;\r\n  return () => {\r\n    value ^= value << 13; value ^= value >>> 17; value ^= value << 5;\r\n    return (value >>> 0) / 0x100000000;\r\n  };\r\n}\r\nfunction currentExerciseMode() {\r\n  try {\r\n    const owner = globalThis.myr5WorkoutOwner?.snapshot?.();\r\n    return owner?.workout?.mode ?? owner?.mode ?? globalThis.myr5TestState?.motion?.mode ?? null;\r\n  } catch { return globalThis.myr5TestState?.motion?.mode ?? null; }\r\n}\r\nfunction fallbackGroup(style) {\r\n  return style === 'lightning' ? 'shoulders' : style === 'floor' ? 'floor' : style === 'blizzard' ? 'core' : style === 'aura' ? 'legs' : null;\r\n}\r\nfunction styleForGroup(group) {\r\n  return group === 'shoulders' ? 'lightning' : group === 'floor' ? 'floor' : group === 'core' ? 'blizzard' : 'aura';\r\n}\r\nfunction groupForContext(mode, previewGroup, record) {\r\n  if (previewGroup) return previewGroup;\r\n  if (mode && EXERCISES[mode]) return effectGroupForExercise(mode);\r\n  return record.styles?.[record.equipped]?.group ?? fallbackGroup(record.equipped) ?? 'legs';\r\n}\r\nexport function avatarEffectContext({ mode = null, previewGroup = null, record = null, effectChoices = {}, random = Math.random } = {}) {\r\n  const group = groupForContext(mode, previewGroup, record ?? { styles: {} });\r\n  return { group, effect: group ? resolveEffectForGroup(group, effectChoices, random) : null };\r\n}\r\nexport function usesNativeBlizzard(effect) { return effect === 'blizzard'; }\r\n\r\n/** Deterministic visible-time scheduler, exported for direct timing tests. */\r\nexport function createAvatarPowerSchedule({ seed = 1, minInterval = 12, maxInterval = 24, duration = 1.4 } = {}) {\r\n  const random = randomFor(seed);\r\n  const interval = () => clamp(minInterval, 1, 120) + random() * Math.max(0, clamp(maxInterval, minInterval, 120) - clamp(minInterval, 1, 120));\r\n  let until = interval(), burst = 0, disposed = false;\r\n  return {\r\n    step(rawDt, visible, reducedMotion = false) {\r\n      if (disposed) return 0;\r\n      const dt = clamp(Number.isFinite(rawDt) ? rawDt : 0, 0, 0.25);\r\n      if (!visible) {\r\n        if (burst > 0) { burst = 0; until = interval(); }\r\n        return 0;\r\n      }\r\n      if (burst > 0) {\r\n        burst = Math.max(0, burst - dt);\r\n        if (!burst) until = interval();\r\n        return burst > 0 ? 1 : 0;\r\n      }\r\n      until -= dt;\r\n      if (until <= 0) { burst = reducedMotion ? 0.45 : clamp(duration, 0.5, 2); return 1; }\r\n      return 0;\r\n    },\r\n    reset() { if (disposed) return; burst = 0; until = interval(); },\r\n    dispose() { disposed = true; burst = until = 0; },\r\n    get remaining() { return until; },\r\n    get active() { return burst > 0; },\r\n  };\r\n}\r\nexport function avatarLayerOpacity(cameraTracking) { return cameraTracking ? 0.08 : 0.55; }\r\n\r\nfunction stageFor(card) {\r\n  if (card?.matches?.('.myr5-companion-stage')) return card;\r\n  return card?.querySelector?.('.myr5-companion-stage') ?? card;\r\n}\r\nfunction renderEcologyAura(ctx, state, id) {\r\n  const ecology = AURA_OPTIONS.find(option => option.id === id)?.ecology;\r\n  if (!ecology) return false;\r\n  const { w, h, time = 0 } = state, ground = h * .9;\r\n  let entities;\r\n  if (ecology.kind === 'plant') {\r\n    entities = [.22, .5, .78].map((fraction, index) => ({ id: index + 1, kind: 'plant', variant: ecology.variant,\r\n      x: w * fraction, y: ground + (index === 1 ? 1 : 0), size: Math.min(54, w * .38), growth: .92 + index * .04,\r\n      alpha: .88, phaseTime: time + index * .7 }));\r\n  } else if (ecology.kind === 'animal') {\r\n    entities = [{ id: 1, kind: 'animal', variant: ecology.variant, phase: 'idle', x: w * .5, y: ground,\r\n      size: Math.min(50, w * .4), growth: 1, alpha: .92, phaseTime: time }];\r\n  } else {\r\n    entities = [.24, .5, .76].map((fraction, index) => ({ id: index + 1, kind: 'mineral', variant: ecology.variant,\r\n      palette: ecology.palette, x: w * fraction, y: ground, size: Math.min(30, w * .22), growth: 1,\r\n      alpha: .9, phaseTime: time }));\r\n  }\r\n  renderEcosystem(ctx, { entities, debris: [] });\r\n  return true;\r\n}\r\nexport function avatarPowerState(record, width, height, time = 0, pulse = 0, walking = false, powerAnchors = null) {\r\n  const style = record?.equipped, stored = style && record.styles?.[style];\r\n  if (!stored || !['aura', 'lightning', 'floor', 'blizzard'].includes(style)) return null;\r\n  const center = width * 0.5, sway = walking ? Math.sin(time * 5) * width * 0.045 : 0;\r\n  if (style === 'blizzard') {\r\n    const leftFoot = center - width * .10 + sway * 1.8, rightFoot = center + width * .10 + sway * 1.8;\r\n    const leftKnee = center - width * .09 + sway * 1.4, rightKnee = center + width * .09 + sway * 1.4;\r\n    const leftHip = center - width * .08 + sway, rightHip = center + width * .08 + sway;\r\n    const leftShoulder = center - width * .12 + sway * .35, rightShoulder = center + width * .12 + sway * .35;\r\n    const points = [[27,25,leftFoot,height*.86,leftKnee,height*.72],[25,23,leftKnee,height*.72,leftHip,height*.59],\r\n      [28,26,rightFoot,height*.86,rightKnee,height*.72],[26,24,rightKnee,height*.72,rightHip,height*.59],\r\n      [23,11,leftHip,height*.59,leftShoulder,height*.32],[24,12,rightHip,height*.59,rightShoulder,height*.32],\r\n      [11,13,leftShoulder,height*.32,center-width*.18+sway*.55,height*.43],\r\n      [12,14,rightShoulder,height*.32,center+width*.18+sway*.55,height*.43]];\r\n    const growth = clamp(.34 + stored.intensity * .66, .34, 1);\r\n    const iceEdges = points.map(([from,to,x1,y1,x2,y2]) => ({ from,to,x1:Math.round(x1),y1:Math.round(y1),\r\n      x2:Math.round(x2),y2:Math.round(y2),growth,alpha:.58+stored.intensity*.27 }));\r\n    const snowCount = Math.min(18, 5 + Math.round(stored.intensity * 13));\r\n    const snow = Array.from({ length: snowCount }, (_, i) => {\r\n      const phase = i * 2.399963229728653 + (walking ? time * 1.4 : time * .45);\r\n      return { x: Math.round(center + Math.cos(phase) * width * (.14 + (i % 4) * .025)),\r\n        y: Math.round(height * (.36 + (i % 5) * .095) + Math.sin(phase * 1.17) * height * .035),\r\n        size: 1 + (i % (stored.tier > 3 ? 3 : 2)), alpha: .2 + stored.intensity * .18 };\r\n    });\r\n    return { blizzard: true, stillSeconds: 5 + time * 2, iceEdges, snow };\r\n  }\r\n  const points = Array.from({ length: 33 }, () => null);\r\n  const put = (index, x, y) => { points[index] = { x: x / width, y: y / height, visibility: 1 }; };\r\n  // These are local character-art anchors, not camera/tracker contacts.\r\n  put(11, center - width * .12 + sway * .35, height * .30);\r\n  put(12, center + width * .12 + sway * .35, height * .30);\r\n  put(13, center - width * .18 + sway * .55, height * .42);\r\n  put(14, center + width * .18 + sway * .55, height * .42);\r\n  put(15, center - width * .22 + sway * .65, height * .54);\r\n  put(16, center + width * .22 + sway * .65, height * .54);\r\n  put(23, center - width * .08 + sway, height * .60);\r\n  put(24, center + width * .08 + sway, height * .60);\r\n  put(25, center - width * .09 + sway * 1.4, height * .73);\r\n  put(26, center + width * .09 + sway * 1.4, height * .73);\r\n  put(27, center - width * .10 + sway * 1.8, height * .85);\r\n  put(28, center + width * .10 + sway * 1.8, height * .85);\r\n  const contacts = style === 'floor' ? (powerAnchors ?? [])\r\n    .filter(anchor => anchor?.visible && Number.isFinite(anchor.x) && Number.isFinite(anchor.y))\r\n    .slice(0, 2)\r\n    .map(anchor => ({ index: anchor.index, x: anchor.x * width, y: anchor.y * height })) : [];\r\n  return { w: width, h: height, route: style, active: true, units: stored.units, maxUnits: stored.maxUnits,\r\n    intensity: stored.intensity, tier: stored.tier, pulse, contacts, points, time };\r\n}\r\n\r\nfunction isVisible(card, stage, canvas, stats) {\r\n  if (!card?.isConnected || !stage?.isConnected || card.hidden || stage.hidden || document.hidden) return false;\r\n  if (card.dataset?.coachCap === 'hidden' || card.dataset?.coachVisible === 'false') return false;\r\n  if (stats?.visible === false || stats?.paused === true) return false;\r\n  const rect = stage.getBoundingClientRect?.();\r\n  if (rect && (!(rect.width > 0) || !(rect.height > 0))) return false;\r\n  for (let node = stage; node && node !== document.documentElement?.parentElement; node = node.parentElement) {\r\n    const style = globalThis.getComputedStyle?.(node);\r\n    if (style && (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0)) return false;\r\n    if (node === card) break;\r\n  }\r\n  canvas.style.display = '';\r\n  return true;\r\n}\r\n\r\nexport function mountAvatarPower({ card, retained, sound: _sound, seed = 1 } = {}) {\r\n  if (!card) throw new TypeError('A companion card is required.');\r\n  const stage = stageFor(card);\r\n  if (!stage?.appendChild) throw new TypeError('The companion card needs an overlay stage.');\r\n  const canvas = document.createElement('canvas');\r\n  canvas.className = 'myr5-avatar-power-canvas';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  Object.assign(canvas.style, { inset: '0', width: '100%', height: '100%', imageRendering: 'pixelated', pointerEvents: 'none', zIndex: '1', opacity: '.55', display: 'none' });\r\n  canvas.style.setProperty('position', 'absolute', 'important');\r\n  stage.appendChild(canvas);\r\n  const ctx = canvas.getContext('2d', { alpha: true });\r\n  if (!ctx) { canvas.remove(); throw new Error('A transparent 2D canvas is unavailable.'); }\r\n\r\n  const reduced = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)') ?? { matches: false, addEventListener() {}, removeEventListener() {} };\r\n  const schedule = createAvatarPowerSchedule({ seed });\r\n  const random = randomFor((Number(seed) ^ 0x41555241) >>> 0);\r\n  let effectChoices = readEffectChoices(), previewGroup = null, currentEffect = null;\r\n  let record = retained?.read?.() ?? { version: 1, equipped: null, styles: {} };\r\n  function context(groupPreview = previewGroup) {\r\n    return avatarEffectContext({ mode: currentExerciseMode(), previewGroup: groupPreview, record, effectChoices, random });\r\n  }\r\n  let effectiveGroup = context().group;\r\n  function selectedEffect(group = effectiveGroup) {\r\n    return group ? resolveEffectForGroup(group, effectChoices, random) : null;\r\n  }\r\n  currentEffect = context().effect;\r\n  let fingerprint = JSON.stringify(record), width = 1, height = 1, dpr = 1, lastFrame = 0, lastSizeCheck = 0, time = 0, pulse = 0, previewRemaining = 0, wasActive = false, raf = 0, disposed = false;\r\n  let resizeObserver = null;\r\n\r\n  function resize() {\r\n    if (disposed) return;\r\n    const rect = stage.getBoundingClientRect();\r\n    if (!(rect.width > 0 && rect.height > 0)) return;\r\n    width = rect.width; height = rect.height; dpr = clamp(globalThis.devicePixelRatio || 1, 1, 2);\r\n    const pixelWidth = Math.max(1, Math.round(width * dpr)), pixelHeight = Math.max(1, Math.round(height * dpr));\r\n    if (canvas.width !== pixelWidth || canvas.height !== pixelHeight) { canvas.width = pixelWidth; canvas.height = pixelHeight; }\r\n    ctx.setTransform(canvas.width / width, 0, 0, canvas.height / height, 0, 0);\r\n    ctx.imageSmoothingEnabled = false;\r\n  }\r\n  function refresh() {\r\n    if (disposed) return;\r\n    const next = retained?.read?.() ?? { version: 1, equipped: null, styles: {} };\r\n    const nextFingerprint = JSON.stringify(next);\r\n    if (nextFingerprint !== fingerprint) {\r\n      record = next; fingerprint = nextFingerprint; schedule.reset(); pulse = 0; wasActive = false;\r\n      effectiveGroup = context().group; currentEffect = context().effect;\r\n      ctx.clearRect(0, 0, width, height); canvas.style.display = 'none';\r\n    }\r\n  }\r\n  function choiceChanged(event) {\r\n    effectChoices = readEffectChoices();\r\n    previewGroup = event?.detail?.group ?? null;\r\n    effectiveGroup = context().group; currentEffect = context().effect;\r\n    schedule.reset(); wasActive = false;\r\n    if (previewGroup) { pulse = 1; previewRemaining = 2.4; }\r\n    else pulse = 0;\r\n  }\r\n  function paint(now) {\r\n    if (disposed) return;\r\n    raf = requestAnimationFrame(paint);\r\n    if (lastFrame && now - lastFrame < 33) return;\r\n    const dt = lastFrame ? clamp((now - lastFrame) / 1000, 0, .25) : 0;\r\n    lastFrame = now;\r\n    if (!resizeObserver && now - lastSizeCheck >= 500) { lastSizeCheck = now; resize(); }\r\n    let stats = null;\r\n    try { stats = globalThis.myr5Creature?.stats?.() ?? null; } catch { /* coach stats are an optional visibility hint */ }\r\n    const mode = currentExerciseMode();\r\n    const nextGroup = context().group;\r\n    if (nextGroup !== effectiveGroup) {\r\n      effectiveGroup = nextGroup; currentEffect = selectedEffect(effectiveGroup);\r\n      schedule.reset(); wasActive = false; pulse = 0;\r\n      ctx.clearRect(0, 0, width, height); canvas.style.display = 'none';\r\n    }\r\n    if (previewRemaining <= 0 && mode && EXERCISES[mode] && !effectGroupForExercise(mode)) {\r\n      wasActive = false; pulse = 0; ctx.clearRect(0, 0, width, height); canvas.style.display = 'none'; return;\r\n    }\r\n    const canShow = isVisible(card, stage, canvas, stats);\r\n    const hadPreview = previewRemaining > 0;\r\n    previewRemaining = Math.max(0, previewRemaining - (canShow ? dt : 0));\r\n    if (hadPreview && previewRemaining === 0) { previewGroup = null; effectiveGroup = context(null).group; currentEffect = selectedEffect(effectiveGroup); }\r\n    const active = previewRemaining > 0 ? 1 : schedule.step(dt, canShow, reduced.matches);\r\n    if (!active) { wasActive = false; pulse = 0; ctx.clearRect(0, 0, width, height); canvas.style.display = 'none'; return; }\r\n    canvas.style.display = 'block';\r\n    canvas.style.opacity = String(avatarLayerOpacity(document.body?.dataset?.tracking === 'true'));\r\n    const reducedMotion = reduced.matches;\r\n    time += reducedMotion ? 0 : dt;\r\n    if (!wasActive) { pulse = 1; currentEffect = selectedEffect(); }\r\n    wasActive = true;\r\n    pulse = Math.max(0, pulse - dt * 2.5);\r\n    const selectedStyle = styleForGroup(effectiveGroup);\r\n    const earnedStyle = record.styles?.[selectedStyle] ?? record.styles?.[record.equipped];\r\n    const visualRecord = earnedStyle\r\n      ? { equipped: selectedStyle, styles: { [selectedStyle]: earnedStyle } }\r\n      : { equipped: selectedStyle, styles: { [selectedStyle]: { units: 1, maxUnits: 1, intensity: .38, tier: 1 } } };\r\n    const state = avatarPowerState(visualRecord, width, height, reducedMotion ? 0 : time,\r\n      reducedMotion ? 0 : pulse, stats?.gesture === 'walk' || stats?.phoneMotionPhase === 'walk', stats?.powerAnchors);\r\n    if (state?.blizzard && !usesNativeBlizzard(currentEffect)) {\r\n      Object.assign(state, { w: width, h: height, route: selectedStyle, active: true,\r\n        intensity: visualRecord.styles[selectedStyle]?.intensity ?? 0, tier: visualRecord.styles[selectedStyle]?.tier ?? 0,\r\n        points: [] });\r\n    }\r\n    if (state) state.effect = currentEffect;\r\n    ctx.clearRect(0, 0, width, height);\r\n    if (state && !renderEcologyAura(ctx, state, state.effect)) {\r\n      if (state?.blizzard && usesNativeBlizzard(state.effect)) renderBlizzard(ctx, state);\r\n      else renderPower(ctx, state);\r\n    }\r\n  }\r\n\r\n  resize();\r\n  if (typeof ResizeObserver === 'function') { resizeObserver = new ResizeObserver(resize); resizeObserver.observe(stage); }\r\n  else globalThis.addEventListener?.('resize', resize);\r\n  const visibility = () => {\r\n    if (document.hidden) { schedule.step(0, false); wasActive = false; pulse = 0; ctx.clearRect(0, 0, width, height); canvas.style.display = 'none'; }\r\n    lastFrame = 0;\r\n  };\r\n  document.addEventListener?.('visibilitychange', visibility);\r\n  globalThis.addEventListener?.('myr5:exercise-effect-choice-change', choiceChanged);\r\n  const storageChanged = event => { if (event?.key === EFFECT_CHOICES_KEY) choiceChanged(); };\r\n  globalThis.addEventListener?.('storage', storageChanged);\r\n  globalThis.addEventListener?.('pagehide', stop, { once: true });\r\n  raf = requestAnimationFrame(paint);\r\n  function stop() {\r\n    if (disposed) return;\r\n    disposed = true; cancelAnimationFrame(raf); resizeObserver?.disconnect();\r\n    globalThis.removeEventListener?.('resize', resize); globalThis.removeEventListener?.('pagehide', stop);\r\n    globalThis.removeEventListener?.('myr5:exercise-effect-choice-change', choiceChanged);\r\n    globalThis.removeEventListener?.('storage', storageChanged);\r\n    document.removeEventListener?.('visibilitychange', visibility); schedule.dispose();\r\n    ctx.clearRect(0, 0, width, height); canvas.remove();\r\n  }\r\n  return { refresh, stop };\r\n}\r\n","arcade/power/camera-power.mjs":"import {createPower,renderPower} from './power.mjs';\r\nimport {AURA_TUNING,layerOpacity} from './aura-growth.mjs';\r\nimport {createAuraBody} from './aura-body.mjs';\r\nexport {preloadAuraForMode} from './power.mjs';\r\nconst AURA_LAYER_OPACITY=AURA_TUNING.LAYER_OPACITY;\r\nimport {createOverlayCanvas,mapPointsToOverlay,effectQuality} from '../workout-effects.mjs';\r\n\r\nconst LOGICAL_WIDTH=360,MAX_GAIN=.045;\r\nfunction makeCue(sound,payload){\r\n if(!sound?.voice||!sound?.tone)return null;\r\n const intensity=Math.max(0,Math.min(1,Number(payload?.intensity)||0));\r\n const level=MAX_GAIN*(.35+.65*intensity),route=payload?.route;\r\n const start=route==='lightning'?520:route==='floor'?180:260,end=route==='lightning'?860:route==='floor'?260:420;\r\n return sound.voice('workout-power',.2,args=>sound.tone(args,start,.16,level,route==='floor'?'triangle':'sine',end));\r\n}\r\n\r\nexport function mountCameraPower({host,sound,seed=Date.now(),quality=effectQuality}={}){\r\n if(!host)throw new TypeError('A camera overlay host is required.');\r\n const layer=createOverlayCanvas(host,'workout-power-canvas',{opacity:AURA_LAYER_OPACITY,max:1}),canvas=layer.canvas,ctx=canvas.getContext('2d',{alpha:true});\r\n // Glow under the sprites; both draw from the same filtered + predicted landmarks so they stay on the body together.\r\n const body=createAuraBody({host,quality});\r\n let engine=null,w=LOGICAL_WIDTH,h=1,raf=0,lastFrame=0,disposed=false,resizeObserver=null,voice=null;\r\n const shown={active:false};let fade=0; // last active engine state, kept to fade out instead of a hard clear\r\n const stopVoice=()=>{voice?.stop?.(.025);voice=null;};\r\n function makeEngine(){engine=createPower({w,h,seed,onEvent(type,payload){if(type==='pulse'){stopVoice();voice=makeCue(sound,payload);}}});}\r\n function resize(){\r\n  if(disposed)return;const rect=host.getBoundingClientRect();if(!(rect.width>0&&rect.height>0))return;\r\n  layer.resize();const nextH=Math.max(1,Math.round(LOGICAL_WIDTH*rect.height/rect.width));\r\n  const changed=!engine||nextH!==h;w=LOGICAL_WIDTH;h=nextH;\r\n  if(changed){stopVoice();engine?.reset();makeEngine();}\r\n  ctx.setTransform(canvas.width/w,0,0,canvas.height/h,0,0);ctx.imageSmoothingEnabled=false;\r\n }\r\n function paint(now){\r\n  if(disposed)return;const dt=lastFrame?Math.max(0,(now-lastFrame)/1000):0;\r\n  if(lastFrame)quality.frame(now-lastFrame);lastFrame=now;\r\n  engine.step(dt);\r\n  const state=engine.state;if(state.active)Object.assign(shown,state);\r\n  const points=body.points(now);\r\n  fade=body.draw(now,state,points);\r\n  ctx.setTransform(canvas.width/w,0,0,canvas.height/h,0,0);ctx.clearRect(0,0,w,h);\r\n  if(fade>0&&shown.active){shown.time=state.time;shown.pulse=state.active?state.pulse:0;renderPower(ctx,shown,{points,alpha:fade});}\r\n  canvas.style.display=fade>0?'block':'none';\r\n  if(fade>0){const o=String(layerOpacity(shown.level).toFixed(3));if(canvas.style.opacity!==o)canvas.style.opacity=o;}\r\n  raf=requestAnimationFrame(paint);\r\n }\r\n resize();\r\n if(typeof ResizeObserver==='function'){resizeObserver=new ResizeObserver(resize);resizeObserver.observe(host);}else window.addEventListener('resize',resize);\r\n raf=requestAnimationFrame(paint);\r\n function pose(detail={}){\r\n  if(disposed)return;resize();\r\n  const geometry={videoWidth:detail.width,videoHeight:detail.height,hostWidth:w,hostHeight:h,mirrored:detail.mirrored};\r\n  const points=mapPointsToOverlay(detail.points,geometry);\r\n  engine.pose({points,now:detail.now,workout:{...detail.workout,validHold:!!detail.validHold}});\r\n  body.pose(points,Number.isFinite(detail.frameMs)?detail.frameMs:detail.now,detail.mask,geometry);\r\n  if(!detail.workout?.active)stopVoice();\r\n }\r\n function invalidate(){if(disposed)return;engine.invalidate();stopVoice();}\r\n function stop(){if(disposed)return;disposed=true;cancelAnimationFrame(raf);resizeObserver?.disconnect();window.removeEventListener('resize',resize);stopVoice();engine?.reset();body.dispose();layer.dispose();}\r\n return {get engine(){return engine;},get body(){return body;},pose,stop,invalidate};\r\n}\r\n","arcade/power/power-chips.mjs":"// Circuit power chips: one ring per exercise inside #fullWorkoutHud. The current chip fills live with the aura's\r\n// power-up progress, locks in (flash) when the set is done, and the next chip waits dimmed.\r\n// API (for the circuit): window.myr5PowerChips.set(steps, index, level?)  steps=[{id,name}], index=current step\r\n// (index>=steps.length marks all done), level=optional 0..1 override. Or window.dispatchEvent(new CustomEvent(\r\n// 'myr5:power-progress',{detail:{index,level}})). The aura layers emit {level} on their own; if no HUD, nothing mounts.\r\nconst CSS = `#fullWorkoutHud .fw-power-chips{display:flex;justify-content:center;gap:8px;margin:2px 6px;pointer-events:none}\r\n#fullWorkoutHud .fw-power-chip{--p:0;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font:700 10px/1 var(--font,sans-serif);color:#fff;opacity:.45;background:conic-gradient(#ffe27a calc(var(--p)*360deg),#ffffff22 0);transition:transform .2s,opacity .2s}\r\n#fullWorkoutHud .fw-power-chip::before{content:\"\";position:absolute;width:20px;height:20px;border-radius:50%;background:#0b0712}\r\n#fullWorkoutHud .fw-power-chip{position:relative}#fullWorkoutHud .fw-power-chip span{position:relative}\r\n#fullWorkoutHud .fw-power-chip[data-state=current]{opacity:1;transform:scale(1.35)}\r\n#fullWorkoutHud .fw-power-chip[data-state=done]{opacity:1;--p:1;animation:fw-chip-flash .6s}\r\n@keyframes fw-chip-flash{0%{filter:brightness(3);transform:scale(1.7)}100%{filter:none;transform:scale(1)}}`;\r\n\r\nexport function renderPowerChips(root, steps, index, level = 0) {\r\n  if (!root) return null;\r\n  let row = root.querySelector('.fw-power-chips');\r\n  if (!steps?.length) { row?.remove(); return null; }\r\n  if (!row) { row = document.createElement('div'); row.className = 'fw-power-chips'; row.setAttribute('aria-hidden', 'true'); root.append(row); }\r\n  while (row.children.length < steps.length) { const c = document.createElement('div'); c.className = 'fw-power-chip'; c.append(document.createElement('span')); row.append(c); }\r\n  while (row.children.length > steps.length) row.lastChild.remove();\r\n  steps.forEach((s, i) => {\r\n    const c = row.children[i], state = i < index ? 'done' : i === index ? 'current' : 'next';\r\n    if (c.dataset.state !== state) c.dataset.state = state;\r\n    c.title = s.name || s.id || '';\r\n    c.firstChild.textContent = i + 1;\r\n    c.style.setProperty('--p', state === 'current' ? Math.max(0, Math.min(1, level)).toFixed(3) : state === 'done' ? '1' : '0');\r\n  });\r\n  return row;\r\n}\r\n\r\nexport function installPowerChips(win = globalThis) {\r\n  if (!win?.document || win.myr5PowerChips) return win?.myr5PowerChips;\r\n  const style = win.document.createElement('style'); style.textContent = CSS; win.document.head.append(style);\r\n  let steps = null, index = 0, level = 0;\r\n  const draw = () => { const hud = win.document.getElementById('fullWorkoutHud'); if (hud && steps) renderPowerChips(hud, steps, index, level); };\r\n  const api = {\r\n    set(nextSteps, nextIndex = 0, nextLevel = 0) { steps = nextSteps; if (nextIndex !== index) level = 0; index = nextIndex; if (Number.isFinite(nextLevel) && nextLevel) level = nextLevel; draw(); },\r\n    clear() { steps = null; win.document.getElementById('fullWorkoutHud')?.querySelector('.fw-power-chips')?.remove(); },\r\n  };\r\n  win.addEventListener('myr5:power-progress', e => {\r\n    const d = e.detail || {};\r\n    if (Number.isFinite(d.index) && d.index !== index) { index = d.index; level = 0; }\r\n    if (Number.isFinite(d.level)) level = d.level;\r\n    draw();\r\n  });\r\n  win.myr5PowerChips = api;\r\n  return api;\r\n}\r\ninstallPowerChips();\r\n","arcade/power/power.mjs":"// Pure workout power progression plus a small Canvas2D renderer. Reuses the repo's\r\n// progression rules and CC0 pixel effect sheets; no new rendering dependency.\r\nimport { EXERCISES } from '../../exercise-library.mjs';\r\nimport { REP_CAP, HOLD_BLOCK_SECONDS, HOLD_CAP_SECONDS, repRate } from '../../progression-rules.mjs';\r\nimport { createGrowth, SPARSE_BOOST, AURA_TUNING } from './aura-growth.mjs';\r\nimport { AURA_OPTIONS, EFFECT_GROUPS, effectGroupForExercise, readEffectChoices, resolveExerciseEffect } from './aura-options.mjs';\r\nimport { AURA_BODY, LEG_SLOTS, ARM_SLOTS, bodyScale, sheetSprite, slotPx, circlePlacement, pulsePlacement } from './aura-anchor.mjs';\r\n\r\nexport const POWER_STALE_MS = 1000;\r\nconst REP_BANDS = Array.from({ length: REP_CAP - 1 }, (_, i) => i + 2)\r\n  .filter(rep => repRate('easy', rep) !== repRate('easy', rep - 1));\r\nconst FLOOR_DETECTORS = new Set(['pushup', 'bridge', 'plank', 'sideplank']);\r\nconst CONFIDENCE = 0.5;\r\n\r\nfunction exerciseOf(exercise) { return typeof exercise === 'string' ? EXERCISES[exercise] : exercise; }\r\nexport function powerRoute(exercise) {\r\n  const ex = exerciseOf(exercise);\r\n  if (!ex || typeof ex !== 'object') return null;\r\n  if (ex.group === 'boxing') return 'lightning';\r\n  if (ex.group === 'cardio') return 'aura';\r\n  if (ex.kind === 'pace') return null;\r\n  if (ex.kind === 'hold') return ex.group === 'stances' ? 'aura' : null;\r\n  if (ex.kind !== 'reps' && ex.kind !== 'steps') return null;\r\n  if (FLOOR_DETECTORS.has(ex.detector)) return 'floor';\r\n  if (ex.detector === 'raise' || ex.detector === 'press' || ex.group === 'shoulders') return 'lightning';\r\n  if (['squat', 'split', 'hinge'].includes(ex.detector) || ['legs', 'hips'].includes(ex.group)) return 'aura';\r\n  return 'aura';\r\n}\r\n/** Style used for saved avatar power, including styles that do not draw camera power. */\r\nexport function retainedPowerStyle(exercise) {\r\n  const ex = exerciseOf(exercise);\r\n  if (!ex || typeof ex !== 'object') return null;\r\n  if (ex.kind === 'hold') {\r\n    if (ex.group === 'stances') return 'aura';\r\n    if (ex.group === 'core' || ex.group === 'balance') return 'blizzard';\r\n    return null;\r\n  }\r\n  if (ex.group === 'boxing' || ex.group === 'cardio') return null; // retained avatar power stays rep-work only\r\n  return powerRoute(ex);\r\n}\r\n\r\nfunction pointAt(points, index) { return Array.isArray(points) ? points[index] : points?.[index] ?? points?.[String(index)]; }\r\nfunction cleanPoints(points) {\r\n  if (!points) return [];\r\n  const length = Array.isArray(points) ? points.length : Math.max(-1, ...Object.keys(points).map(Number)) + 1;\r\n  return Array.from({ length }, (_, index) => {\r\n    const p = pointAt(points, index), visibility = Number(p?.visibility ?? p?.score ?? p?.confidence);\r\n    if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y) || !Number.isFinite(visibility) || visibility < CONFIDENCE) return null;\r\n    return { x: p.x, y: p.y, visibility };\r\n  });\r\n}\r\nfunction visible(points, index) {\r\n  const p = points[index];\r\n  return p && p.visibility >= CONFIDENCE && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1;\r\n}\r\nfunction sideIndices(points, side) {\r\n  const left = side !== 'right';\r\n  return left ? { shoulder: 11, elbow: 13, wrist: 15, hip: 23, knee: 25, ankle: 27, heel: 29, toe: 31 }\r\n    : { shoulder: 12, elbow: 14, wrist: 16, hip: 24, knee: 26, ankle: 28, heel: 30, toe: 32 };\r\n}\r\nfunction footIndex(points, side) {\r\n  const ix = sideIndices(points, side);\r\n  return [ix.heel, ix.toe, ix.ankle].find(i => visible(points, i));\r\n}\r\nfunction toContacts(points, indices, w, h) {\r\n  const unique = [...new Set(indices)].filter(i => Number.isInteger(i) && visible(points, i)).slice(0, 4);\r\n  return unique.map(index => ({ index, x: points[index].x * w, y: points[index].y * h }));\r\n}\r\nfunction floorContacts(ex, points, w, h) {\r\n  const d = ex?.detector;\r\n  if (d === 'bridge') return toContacts(points, [footIndex(points, 'left'), footIndex(points, 'right'), 11, 12], w, h);\r\n  if (d === 'sideplank') {\r\n    const ix = sideIndices(points, ex.side);\r\n    const limb = ex.support === 'knees' ? ix.knee : footIndex(points, ex.side);\r\n    return toContacts(points, [ix.elbow, limb], w, h);\r\n  }\r\n  const arms = ex.forearm ? [13, 14] : [15, 16];\r\n  const lowers = ex.support === 'knees' ? [25, 26] : [29, 30, 31, 32, 27, 28];\r\n  const lower = ex.support === 'knees' ? lowers.filter(i => visible(points, i)).slice(0, 2)\r\n    : [footIndex(points, 'left'), footIndex(points, 'right')].filter(Number.isInteger);\r\n  return toContacts(points, [...arms, ...lower], w, h);\r\n}\r\n/** Metadata-based visible support anchors shared by floor power, Blizzard and avatar overlays. */\r\nexport function supportContacts(exercise, points, w = 360, h = 640) {\r\n  const ex = exerciseOf(exercise);\r\n  if (!ex) return [];\r\n  if (FLOOR_DETECTORS.has(ex.detector)) return floorContacts(ex, cleanPoints(points), w, h);\r\n  if (ex.kind !== 'hold') return [];\r\n  const clean = cleanPoints(points);\r\n  const left=footIndex(clean,'left'),right=footIndex(clean,'right');\r\n  if(ex.group==='balance'){\r\n    const supported=[left,right].filter(Number.isInteger).sort((a,b)=>clean[b].y-clean[a].y)[0];\r\n    return toContacts(clean,[supported],w,h);\r\n  }\r\n  return toContacts(clean,[left,right],w,h);\r\n}\r\nfunction bandTier(reps) { return reps <= 0 ? 0 : REP_BANDS.filter(n => reps >= n).length + 1; }\r\nfunction maxUnits(kind) { return kind === 'hold' ? HOLD_CAP_SECONDS / HOLD_BLOCK_SECONDS : REP_CAP; }\r\nfunction progressFor(workout) {\r\n  if (workout?.kind === 'hold') {\r\n    const seconds = Number.isFinite(workout.activeSeconds) ? Math.min(HOLD_CAP_SECONDS, Math.max(0, workout.activeSeconds)) : 0;\r\n    const units = Math.floor(seconds / HOLD_BLOCK_SECONDS);\r\n    const max = maxUnits('hold');\r\n    return { units, maxUnits: max, intensity: Math.sqrt(units / max), tier: units <= 0 ? 0 : Math.min(6, Math.ceil(Math.sqrt(units / max) * 6)) };\r\n  }\r\n  const reps = Number.isFinite(workout?.workingReps) ? Math.min(REP_CAP, Math.max(0, Math.floor(workout.workingReps))) : 0;\r\n  return { units: reps, maxUnits: REP_CAP, intensity: reps / REP_CAP, tier: bandTier(reps) };\r\n}\r\n/** Authoritative progression units reused by retained character cosmetics. */\r\nexport function powerProgress(workout = {}) {\r\n  const progress = progressFor(workout);\r\n  return { ...progress, kind: workout.kind === 'hold' ? 'hold' : 'reps' };\r\n}\r\n\r\nexport function createPower({ w = 360, h = 640, seed = 1, onEvent = () => {} } = {}) {\r\n  const state = { w, h, route: null, effect: null, active: false, units: 0, maxUnits: REP_CAP, intensity: 0, tier: 0, level: 0, speed: 0, pulse: 0, contacts: [], points: [], time: 0 };\r\n  const growth = createGrowth();\r\n  let lastBumps = 0;\r\n  let sessionId = null, mode = null, workoutKind = null, baseline = false, lastUnits = 0;\r\n  let effectKey = null, effectRandomState = (Number(seed) >>> 0) || 0x9e3779b9;\r\n  const effectRandom = () => { effectRandomState ^= effectRandomState << 13; effectRandomState ^= effectRandomState >>> 17; effectRandomState ^= effectRandomState << 5; return (effectRandomState >>> 0) / 0x100000000; };\r\n  let lastNow = null, poseAge = Infinity, suppressNextPulse = true;\r\n  const emit = (type, payload) => { try { onEvent(type, payload); } catch { /* cosmetic observers never affect workout flow */ } };\r\n  function clearVisible() { state.active = false; state.pulse = 0; state.level = 0; state.speed = 0; state.contacts = []; state.points = []; growth.reset(); lastBumps = 0; }\r\n  function clearProgress() {\r\n    state.units = 0; state.intensity = 0; state.tier = 0; state.maxUnits = REP_CAP;\r\n    lastUnits = 0; baseline = false; workoutKind = null;\r\n  }\r\n  function hideForStale() { clearVisible(); poseAge = Infinity; lastNow = null; suppressNextPulse = true; }\r\n  function reset() {\r\n    clearVisible(); clearProgress(); state.route = null; state.effect = null; state.time = 0; effectKey = null;\r\n    sessionId = mode = null; lastNow = null; poseAge = Infinity; suppressNextPulse = true;\r\n    return state;\r\n  }\r\n  function invalidate() { hideForStale(); return state; }\r\n  function updateProgress(workout, { allowPulse }) {\r\n    const progress = progressFor(workout), kind = workout.kind === 'hold' ? 'hold' : 'reps';\r\n    const changedKind = workoutKind !== null && workoutKind !== kind;\r\n    if (changedKind) { clearProgress(); }\r\n    state.maxUnits = progress.maxUnits;\r\n    state.units = progress.units; state.intensity = progress.intensity; state.tier = progress.tier;\r\n    if (!baseline) {\r\n      lastUnits = progress.units; baseline = true; workoutKind = kind; state.pulse = 0;\r\n      return;\r\n    }\r\n    const delta = progress.units - lastUnits;\r\n    lastUnits = progress.units; workoutKind = kind;\r\n    if (delta > 0 && allowPulse && !suppressNextPulse) {\r\n      state.pulse = 1;\r\n      emit('pulse', { route: state.route, intensity: state.intensity, units: state.units, deltaUnits: delta, kind, difficulty: workout.difficulty });\r\n    } else if (delta < 0 || !allowPulse || suppressNextPulse) state.pulse = 0;\r\n  }\r\n  function pose({ points, now, workout } = {}) {\r\n    if (!workout || !Number.isFinite(now)) { hideForStale(); return state; }\r\n    // Pace/step work (boxing, marching) has no rep ledger; its counter must not fire rep pulses every frame.\r\n    if (!['reps', 'hold'].includes(EXERCISES[workout.mode]?.kind) && workout.workingReps) workout = { ...workout, workingReps: 0 };\r\n    const nextMode = workout.mode ?? null, nextSession = workout.sessionId ?? null;\r\n    const route = powerRoute(nextMode);\r\n    const newIdentity = sessionId !== nextSession || mode !== nextMode;\r\n    if (newIdentity) {\r\n      clearProgress(); clearVisible(); sessionId = nextSession; mode = nextMode; state.route = route;\r\n      lastNow = null; poseAge = Infinity; suppressNextPulse = true;\r\n    } else state.route = route;\r\n    const group = effectGroupForExercise(nextMode, { camera: true }), choices = readEffectChoices();\r\n    const selection = group ? choices[group] : null, nextEffectKey = group ? `${group}:${selection}` : null;\r\n    if (newIdentity || nextEffectKey !== effectKey) {\r\n      state.effect = group ? resolveExerciseEffect(nextMode, choices, effectRandom, { camera: true }) : null;\r\n      effectKey = nextEffectKey;\r\n    }\r\n    if (!route) { clearVisible(); lastNow = now; poseAge = 0; return state; }\r\n    if (lastNow != null && now <= lastNow) return state;\r\n    const staleGap = lastNow != null && now - lastNow > POWER_STALE_MS;\r\n    if (staleGap) { clearVisible(); suppressNextPulse = true; }\r\n    const clean = cleanPoints(points);\r\n    const working = typeof workout.stage === 'string' && !['preparation-rest', 'sprint-rest'].includes(workout.stage); // warm-up reps grow the aura too\r\n    if (!clean.some(Boolean) || workout.active !== true || !working || (workout.kind === 'hold' && workout.validHold !== true)) {\r\n      updateProgress(workout, { allowPulse: false });\r\n      clearVisible(); lastNow = now; poseAge = 0; suppressNextPulse = true;\r\n      return state;\r\n    }\r\n    const supportedKind = workout.kind === 'hold' ? (route === 'aura' && workout.validHold === true) : true;\r\n    if (!supportedKind) { clearVisible(); lastNow = now; poseAge = 0; return state; }\r\n    const contacts = route === 'floor' ? floorContacts(EXERCISES[nextMode], clean, w, h) : [];\r\n    const allowPulse = !newIdentity && !staleGap && !suppressNextPulse && (route !== 'floor' || contacts.length > 0);\r\n    updateProgress(workout, { allowPulse });\r\n    suppressNextPulse = false;\r\n    state.active = true; state.points = clean;\r\n    state.contacts = contacts;\r\n    if (route === 'floor' && state.contacts.length === 0) state.pulse = 0;\r\n    if (route !== 'floor' && state.units === 0) state.pulse = 0;\r\n    const exKind = EXERCISES[nextMode]?.kind;\r\n    const growKind = workout.kind === 'hold' ? 'hold' : exKind === 'reps' ? 'reps' : 'move';\r\n    growth.update({ now, points: clean, kind: growKind, units: progressFor(workout).units, valid: workout.validHold === true, target: workout.target });\r\n    state.level = growth.state.level; state.speed = growth.state.speed;\r\n    if (growKind === 'move' && growth.state.bumps > lastBumps) {\r\n      state.pulse = 1; emit('pulse', { route: state.route, intensity: growth.state.goal, units: state.units, deltaUnits: 1, kind: 'move', difficulty: workout.difficulty });\r\n    }\r\n    lastBumps = growth.state.bumps;\r\n    lastNow = now; poseAge = 0;\r\n    return state;\r\n  }\r\n  function step(rawDt = 0) {\r\n    const dtRaw = Number.isFinite(rawDt) && rawDt > 0 ? rawDt : 0;\r\n    state.time += Math.min(0.05, dtRaw);\r\n    poseAge += dtRaw * 1000;\r\n    state.pulse = Math.max(0, state.pulse - Math.min(0.05, dtRaw) * 2.5);\r\n    growth.step(dtRaw); if (state.active) state.level = growth.state.level;\r\n    if (poseAge > POWER_STALE_MS && lastNow != null) hideForStale();\r\n    return state;\r\n  }\r\n  return { state, pose, step, reset, invalidate };\r\n}\r\n\r\n// Renderer ---------------------------------------------------------------\r\nconst imageCache = new Map();\r\nconst frameCache = new Map();\r\nconst spriteSources = {\r\n  // Crop boxes are the union of nontransparent pixel bounds across populated cells\r\n  // (PIL alpha>8). Frames are 100x100, row-major; normalize each crop to a 64px\r\n  // cache while preserving its aspect ratio and bottom-aligning the sprite pivot.\r\n  fire: ['/arcade/power/assets/11_fire_spritesheet.png', 100, 8, 61, [40, 27, 65, 78]],\r\n  brightfire: ['/arcade/power/assets/9_brightfire_spritesheet.png', 100, 8, 61, [45, 37, 61, 69]],\r\n  lightning: ['/arcade/power/assets/pixelated_tilesheet.png', 64, 4, 8, [0, 0, 64, 64]],\r\n};\r\nfunction loadImage(key) {\r\n  if (imageCache.has(key)) return imageCache.get(key);\r\n  if (typeof Image === 'undefined') return null;\r\n  const image = new Image(); imageCache.set(key, image);\r\n  image.onload = () => {}; image.onerror = () => imageCache.delete(key); image.src = spriteSources[key][0];\r\n  return image;\r\n}\r\nfunction drawSheetFrame(ctx, key, frame, x, y, size, alpha = 1, rotation = 0) {\r\n  const image = loadImage(key), [, frameSize, columns, frameCount, bounds] = spriteSources[key];\r\n  if (!image?.complete || !image.naturalWidth || typeof document === 'undefined') return;\r\n  const sourceFrame = ((frame % frameCount) + frameCount) % frameCount;\r\n  const cacheKey = `${key}:${sourceFrame}`;\r\n  let sprite = frameCache.get(cacheKey);\r\n  if (!sprite) {\r\n    sprite = document.createElement('canvas'); sprite.width = sprite.height = 64;\r\n    const source = sprite.getContext('2d');\r\n    if (!source) return;\r\n    source.imageSmoothingEnabled = false;\r\n    const [left, top, right, bottom] = bounds, cropWidth = right - left, cropHeight = bottom - top;\r\n    const scale = 64 / Math.max(cropWidth, cropHeight), drawWidth = cropWidth * scale, drawHeight = cropHeight * scale;\r\n    const destX = (64 - drawWidth) / 2, destY = 64 - drawHeight;\r\n    source.drawImage(image, (sourceFrame % columns) * frameSize + left, Math.floor(sourceFrame / columns) * frameSize + top,\r\n      cropWidth, cropHeight, destX, destY, drawWidth, drawHeight);\r\n    frameCache.set(cacheKey, sprite);\r\n  }\r\n  ctx.save(); ctx.globalAlpha *= alpha; ctx.translate(Math.round(x), Math.round(y));\r\n  if (rotation) ctx.rotate(rotation);\r\n  ctx.drawImage(sprite, -size / 2, -size / 2, size, size);\r\n  ctx.restore();\r\n}\r\nconst auraSources = new Map(AURA_OPTIONS.filter(option => option.asset).map(option => [option.id, {\r\n  path: `/arcade/${option.id === 'blizzard' ? 'hold' : 'power'}/assets/${option.asset}`,\r\n  columns: option.columns, rows: option.rows, layout: option.layout ?? 'body',\r\n}]));\r\nfunction loadAuraImage(id) {\r\n  const source = auraSources.get(id);\r\n  if (!source) return null;\r\n  const key = `aura:${id}`;\r\n  if (imageCache.has(key)) return imageCache.get(key);\r\n  if (typeof Image === 'undefined') return null;\r\n  const image = new Image(); imageCache.set(key, image);\r\n  image.onerror = () => imageCache.delete(key); image.src = source.path;\r\n  return image;\r\n}\r\n/** Start decoding the sprite sheet(s) for an effect so the first frame is not waiting on the network. */\r\nexport function preloadAura(id) {\r\n  loadAuraImage(id);\r\n  if (typeof Image !== 'undefined' && !auraSources.has(id)) for (const key of ['fire', 'brightfire', 'lightning']) loadImage(key);\r\n}\r\n/** Warm every sheet the exercise's group can pick (all of them when the saved choice is Random). */\r\nexport function preloadAuraForMode(mode, choices = readEffectChoices()) {\r\n  const group = effectGroupForExercise(mode, { camera: true });\r\n  if (!group) return;\r\n  const pick = choices[group];\r\n  const ids = pick && pick !== 'random' ? [pick] : EFFECT_GROUPS.find(g => g.id === group)?.options.filter(id => id !== 'random') ?? [];\r\n  for (const id of ids) preloadAura(id);\r\n}\r\nfunction drawAuraFrame(ctx, id, frame, x, y, size, alpha = 1, rotation = 0) {\r\n  const source = auraSources.get(id), image = loadAuraImage(id);\r\n  if (!source || !image?.complete || !image.naturalWidth || typeof document === 'undefined') return;\r\n  const frameCount = source.columns * source.rows;\r\n  const sourceFrame = ((frame % frameCount) + frameCount) % frameCount;\r\n  const cacheKey = `aura:${id}:${sourceFrame}`;\r\n  let sprite = frameCache.get(cacheKey);\r\n  if (!sprite) {\r\n    sprite = document.createElement('canvas'); sprite.width = sprite.height = 64;\r\n    const sourceContext = sprite.getContext('2d');\r\n    if (!sourceContext) return;\r\n    sourceContext.imageSmoothingEnabled = false;\r\n    const frameSize = 100;\r\n    sourceContext.drawImage(image, (sourceFrame % source.columns) * frameSize,\r\n      Math.floor(sourceFrame / source.columns) * frameSize, frameSize, frameSize, 0, 0, 64, 64);\r\n    frameCache.set(cacheKey, sprite);\r\n  }\r\n  ctx.save(); ctx.globalAlpha *= alpha; ctx.translate(Math.round(x), Math.round(y));\r\n  if (rotation) ctx.rotate(rotation);\r\n  size *= SPARSE_BOOST[id] ?? 1;\r\n  ctx.drawImage(sprite, -size / 2, -size / 2, size, size); ctx.restore();\r\n}\r\nfunction drawPixelCrack(ctx, x, y, size, tier, time, seed) {\r\n  const branches = Math.min(6, 2 + Math.floor(tier * 0.7));\r\n  const arm = Math.max(4, Math.round(size * (0.55 + tier * 0.03)));\r\n  ctx.save(); ctx.translate(Math.round(x), Math.round(y)); ctx.strokeStyle = '#ffc65a'; ctx.lineWidth = 1;\r\n  ctx.globalAlpha = 0.68 + Math.min(0.27, tier * 0.045) + Math.sin(time * 8 + seed) * 0.025;\r\n  for (let i = 0; i < branches; i++) {\r\n    const side = i % 2 ? 1 : -1, start = (Math.floor(i / 2) - 1) * 2;\r\n    ctx.beginPath(); ctx.moveTo(start, 0); ctx.lineTo(start + side * Math.round(arm * 0.22), Math.round(arm * 0.22));\r\n    ctx.lineTo(start + side * Math.round(arm * 0.5), Math.round(arm * 0.38));\r\n    if (tier >= 3) ctx.lineTo(start + side * Math.round(arm * 0.68), Math.round(arm * 0.22));\r\n    ctx.stroke();\r\n  }\r\n  ctx.restore();\r\n}\r\nfunction pointPixel(state, index) {\r\n  const points = state.points ?? [], p = points[index];\r\n  return visible(points, index) ? { x: p.x * state.w, y: p.y * state.h, a: Number.isFinite(p.alpha) ? p.alpha : 1 } : null;\r\n}\r\n/** Floor contacts follow the drawn (filtered, predicted) landmark of the same index. */\r\nfunction contactPixel(state, contact) { return (contact.index != null && pointPixel(state, contact.index)) || contact; }\r\n// Classic fire: small flames pinned along the legs (each to its own bone slot), pushed out from the bone and\r\n// flickering around that spot. Sizes are torso multiples so the flames match the body near or far.\r\nconst SLOT = { x: 0, y: 0, a: 0, dx: 0, dy: 1 };\r\nfunction auraAccents(state, torso) {\r\n  const count = Math.min(18, 3 + Math.floor(state.tier * 2.5)), out = [];\r\n  for (let i = 0; i < count; i++) {\r\n    if (!slotPx(state.points, LEG_SLOTS[i % LEG_SLOTS.length], state.w, state.h, SLOT)) continue;\r\n    const angle = (i * 2.399963229728653) + state.time * (0.25 + state.intensity * 0.2), side = i % 2 ? 1 : -1;\r\n    const push = torso * (0.07 + (i % 4) * (0.016 + state.tier * 0.004)), wobble = torso * 0.03;\r\n    out.push({ index: 23 + (i % 6), x: SLOT.x - SLOT.dy * push * side + Math.cos(angle) * wobble, y: SLOT.y + SLOT.dx * push * side + Math.sin(angle) * wobble, angle, a: SLOT.a });\r\n  }\r\n  return out;\r\n}\r\nfunction lightningAccents(state, torso) {\r\n  const count = Math.min(12, 2 + state.tier * 2), out = [];\r\n  for (let i = 0; i < count; i++) {\r\n    if (!slotPx(state.points, ARM_SLOTS[i % ARM_SLOTS.length], state.w, state.h, SLOT)) continue;\r\n    const phase = state.time * 4 + i * 1.37, j = torso * (0.02 + state.tier * 0.007);\r\n    out.push({ index: 11 + (i % 6), x: SLOT.x + Math.sin(phase) * j, y: SLOT.y + Math.cos(phase) * j * 0.6, frame: Math.floor(state.time * 9 + i) % 8, a: SLOT.a });\r\n  }\r\n  return out;\r\n}\r\nconst SPRITE = { x: 0, y: 0, a: 0, dx: 0, dy: 1, size: 0, rotation: 0 };\r\n/**\r\n * opts.points: drawn landmarks (filtered + predicted, with per-point `alpha`) instead of state.points;\r\n * opts.alpha: whole-layer fade (0..1). Sizes scale with the on-screen torso, not the canvas.\r\n */\r\nexport function renderPower(ctx, raw, opts = {}) {\r\n  const level = raw?.level ?? raw?.intensity ?? 0;\r\n  if (!ctx || !raw?.active || level <= 0) return;\r\n  // Draw tier and intensity follow the shared growth level, not the XP ledger.\r\n  const state = { ...raw, points: opts.points ?? raw.points, intensity: level, tier: Math.max(1, Math.ceil(level * 6)) };\r\n  const canvas = ctx.canvas;\r\n  if (!canvas || typeof document === 'undefined') return;\r\n  const width = Math.max(1, Math.ceil(state.w)), height = Math.max(1, Math.ceil(state.h));\r\n  if (!renderPower.layer || renderPower.layer.width !== width || renderPower.layer.height !== height) {\r\n    renderPower.layer = document.createElement('canvas'); renderPower.layer.width = width; renderPower.layer.height = height;\r\n    renderPower.layerContext = renderPower.layer.getContext('2d');\r\n  }\r\n  const layer = renderPower.layerContext;\r\n  if (!layer) return;\r\n  layer.clearRect(0, 0, width, height); layer.imageSmoothingEnabled = false; layer.globalCompositeOperation = 'source-over';\r\n  const frame = Math.floor(state.time * 7), torso = bodyScale(state.points, state.w, state.h), T = AURA_BODY;\r\n  const sheet = state.effect && state.effect !== 'classic' && auraSources.has(state.effect);\r\n  if (sheet) {\r\n    const source = auraSources.get(state.effect);\r\n    if (state.route === 'floor') {\r\n      for (const contact of state.contacts.slice(0, 4)) {\r\n        const c = contactPixel(state, contact);\r\n        drawAuraFrame(layer, state.effect, frame + (contact.index ?? 0) * 7, c.x, c.y, torso * T.FLOOR_SIZE * (0.6 + 0.4 * level), .9 * (c.a ?? 1), 0);\r\n      }\r\n    } else if (source.layout === 'circle') {\r\n      const c = circlePlacement(state.points, state.w, state.h, level);\r\n      drawAuraFrame(layer, state.effect, frame, c.x, c.y, c.size, .76, state.time * .08);\r\n    } else {\r\n      const full = state.effect === 'magicbubbles' ? 8 : source.layout === 'orbit' ? 5 : 4;\r\n      const count = Math.max(1, Math.round(full * (0.3 + 0.7 * level)));\r\n      for (let i = 0; i < count; i++) {\r\n        if (!sheetSprite(state.points, i, source.layout, state.time, state.w, state.h, torso, level, SPRITE)) continue;\r\n        drawAuraFrame(layer, state.effect, frame + i * 7, SPRITE.x, SPRITE.y, SPRITE.size, .78 * SPRITE.a, SPRITE.rotation);\r\n      }\r\n    }\r\n  } else if (state.route === 'aura') {\r\n    for (const a of auraAccents(state, torso)) drawSheetFrame(layer, 'brightfire', frame + a.index * 3, a.x, a.y,\r\n      torso * (0.107 + Math.max(0, state.tier - 1) * 0.023), 0.95 * a.a, a.angle);\r\n  } else if (state.route === 'lightning') {\r\n    for (const a of lightningAccents(state, torso)) drawSheetFrame(layer, 'lightning', a.frame, a.x, a.y, torso * (0.1 + state.tier * 0.017), 0.95 * a.a, 0);\r\n  } else if (state.route === 'floor') {\r\n    for (const contact of state.contacts.slice(0, 4)) {\r\n      const c = contactPixel(state, contact);\r\n      drawPixelCrack(layer, c.x, c.y, torso * (0.073 + state.tier * 0.011), state.tier, state.time, contact.index);\r\n      drawSheetFrame(layer, 'fire', frame + contact.index * 4, c.x, c.y, torso * (0.12 + state.tier * 0.017), 0.95 * (c.a ?? 1), 0);\r\n    }\r\n  }\r\n  if (state.pulse > 0) {\r\n    if (sheet && state.route === 'floor') {\r\n      for (const contact of state.contacts.slice(0, 4)) {\r\n        const c = contactPixel(state, contact);\r\n        drawAuraFrame(layer, state.effect, frame + 23 + (contact.index ?? 0) * 7, c.x, c.y, torso * (0.33 + state.tier * 0.033), .9 * state.pulse, 0);\r\n      }\r\n    } else if (sheet) {\r\n      const p = pulsePlacement(state.points, state.w, state.h);\r\n      drawAuraFrame(layer, state.effect, frame + 23, p.x, p.y, p.size, .9 * state.pulse, 0);\r\n    } else {\r\n      const accents = state.route === 'lightning' ? lightningAccents(state, torso).slice(0, 4) : state.route === 'aura' ? auraAccents(state, torso).slice(0, 6)\r\n        : state.contacts.slice(0, 4).map(contact => ({ ...contactPixel(state, contact), index: contact.index }));\r\n      for (const point of accents) {\r\n        if (state.route === 'floor' && point.index != null) drawSheetFrame(layer, 'fire', frame + 9, point.x, point.y, torso * (0.1 + state.tier * 0.01), 0.9 * state.pulse, 0);\r\n        else if (state.route === 'lightning') drawSheetFrame(layer, 'lightning', frame % 8, point.x, point.y, torso * (0.1 + state.tier * 0.009), 0.9 * state.pulse, 0);\r\n        else drawSheetFrame(layer, 'brightfire', frame + 17, point.x, point.y, torso * (0.107 + state.tier * 0.011), 0.9 * state.pulse, 0);\r\n      }\r\n    }\r\n  }\r\n  ctx.save(); ctx.imageSmoothingEnabled = false; ctx.globalCompositeOperation = 'source-over';\r\n  // The camera wrappers write the canvas opacity per frame (layerOpacity); sprites also thin out as level rises.\r\n  const fade = Math.max(0, Math.min(1, opts.alpha ?? state.alpha ?? 1));\r\n  ctx.globalAlpha = (1 - AURA_TUNING.SPRITE_ALPHA_FALL * Math.min(1, level)) * fade; ctx.drawImage(renderPower.layer, 0, 0, width, height); ctx.restore();\r\n}\r\n","arcade/power/retained-power.mjs":"// Owner-scoped cosmetic carryover. Progress always comes from the same\r\n// progression rules used by camera power; this module never awards workout XP.\r\nimport { powerProgress, retainedPowerStyle } from './power.mjs';\r\nimport { EXERCISES } from '../../exercise-library.mjs';\r\nimport { EFFECT_GROUPS, effectGroupForExercise } from './aura-options.mjs';\r\n\r\nexport const RETAINED_POWER_KEY = 'myr5-retained-character-power-v1';\r\nconst STYLES = new Set(['aura', 'lightning', 'floor', 'blizzard']);\r\nconst GROUPS = new Set(EFFECT_GROUPS.map(group => group.id));\r\nconst STYLE_GROUPS = { aura: new Set(['legs', 'stances']), lightning: new Set(['shoulders']), floor: new Set(['floor']), blizzard: new Set(['core']) };\r\nconst MAX_OWNER_LENGTH = 240;\r\nconst empty = () => ({ version: 1, equipped: null, styles: {} });\r\nconst finite = value => Number.isFinite(value);\r\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\r\n\r\nfunction validOwner(value) {\r\n  return typeof value === 'string' && value.length > 0 && value.length <= MAX_OWNER_LENGTH\r\n    && (value.startsWith('account:') || (value.startsWith('guest:') && value !== 'guest:unavailable'));\r\n}\r\nfunction safeRecord(value) {\r\n  if (!value || typeof value !== 'object' || Array.isArray(value) || value.version !== 1) return empty();\r\n  const result = empty();\r\n  for (const [style, item] of Object.entries(value.styles ?? {})) {\r\n    if (!STYLES.has(style) || !item || typeof item !== 'object' || Array.isArray(item)) continue;\r\n    if (!finite(item.intensity) || !finite(item.tier) || !finite(item.units) || !finite(item.maxUnits)\r\n      || !finite(item.updatedAt) || !['reps', 'hold'].includes(item.kind)\r\n      || (item.kind === 'hold' && !['aura', 'blizzard'].includes(style))) continue;\r\n    const maxUnits = clamp(Math.floor(item.maxUnits), 1, item.kind === 'hold' ? 120 : 30);\r\n    const units = clamp(Math.floor(item.units), 0, maxUnits);\r\n    const intensity = clamp(item.intensity, 0, 1);\r\n    if (!units || !intensity) continue;\r\n    const mode = typeof item.mode === 'string' && EXERCISES[item.mode] && retainedPowerStyle(item.mode) === style ? item.mode : null;\r\n    const inferred = mode ? effectGroupForExercise(mode) : null;\r\n    const fallback = style === 'lightning' ? 'shoulders' : style === 'floor' ? 'floor' : style === 'blizzard' ? 'core' : 'legs';\r\n    const candidate = GROUPS.has(inferred) ? inferred : GROUPS.has(item.group) ? item.group : fallback;\r\n    const group = STYLE_GROUPS[style].has(candidate) ? candidate : fallback;\r\n    result.styles[style] = { intensity, tier: clamp(Math.floor(item.tier), 0, 6), units, maxUnits,\r\n      kind: item.kind, group, ...(mode ? { mode } : {}), updatedAt: clamp(item.updatedAt, 0, 1e15) };\r\n  }\r\n  result.equipped = STYLES.has(value.equipped) && result.styles[value.equipped] ? value.equipped : null;\r\n  return result;\r\n}\r\nfunction copy(value) { return { version: 1, equipped: value.equipped,\r\n  styles: Object.fromEntries(Object.entries(value.styles).map(([style, item]) => [style, { ...item }])) }; }\r\n\r\nexport function createRetainedPower({ storage = globalThis.localStorage, ownerId = null } = {}) {\r\n  let owner = validOwner(ownerId) ? ownerId : null;\r\n  let record = load();\r\n  let identity = null, lastUnits = 0;\r\n  let disposed = false;\r\n\r\n  function keyFor(id = owner) { return id ? `${RETAINED_POWER_KEY}/${encodeURIComponent(id)}` : null; }\r\n  function load() {\r\n    const key = keyFor();\r\n    if (!key) return empty();\r\n    try { return safeRecord(JSON.parse(storage?.getItem(key) || 'null')); }\r\n    catch { return empty(); }\r\n  }\r\n  function save() {\r\n    const key = keyFor();\r\n    if (!key) return false;\r\n    try { storage?.setItem(key, JSON.stringify(record)); return !!storage; }\r\n    catch { return false; }\r\n  }\r\n\r\n  function read() { return copy(record); }\r\n  function setOwner(nextOwner) {\r\n    if (disposed) return read();\r\n    const next = validOwner(nextOwner) ? nextOwner : null;\r\n    if (next === owner) return read();\r\n    owner = next;\r\n    identity = null; lastUnits = 0;\r\n    record = load();\r\n    return read();\r\n  }\r\n  function observe(detail = {}) {\r\n    if (disposed || !owner) return false;\r\n    const workout = detail?.workout;\r\n    if (!workout || workout.ownerId !== owner || workout.stage !== 'working' || workout.paused === true) return false;\r\n    const route = retainedPowerStyle(workout.mode);\r\n    const group = effectGroupForExercise(workout.mode);\r\n    if (!route || (workout.kind === 'hold' && !['aura', 'blizzard'].includes(route))\r\n      || (workout.kind === 'reps' && route === 'blizzard') || !['hold', 'reps'].includes(workout.kind)) return false;\r\n    const progress = powerProgress(workout);\r\n    if (!(progress.units > 0 && progress.maxUnits > 0)) return false;\r\n    const nextIdentity = `${workout.sessionId ?? ''}\\u0000${workout.mode ?? ''}\\u0000${workout.kind}`;\r\n    const changed = nextIdentity !== identity;\r\n    const previous = lastUnits;\r\n    identity = nextIdentity;\r\n    lastUnits = progress.units;\r\n    // The first positive authoritative snapshot can restore a saved peak without\r\n    // replaying a pulse. Repeated units and downward corrections never re-equip.\r\n    const progressed = changed ? progress.units > 0 : progress.units > previous;\r\n    if (!progressed) return false;\r\n\r\n    const current = record.styles[route];\r\n    const now = finite(detail.now) ? Math.max(0, detail.now) : 0;\r\n    if (!current) {\r\n      record.styles[route] = { ...progress, ...(group ? { group } : {}), mode: workout.mode, updatedAt: now };\r\n    } else {\r\n      const better = progress.intensity > current.intensity\r\n        || (progress.intensity === current.intensity && progress.tier > current.tier);\r\n      record.styles[route] = {\r\n        intensity: better ? progress.intensity : current.intensity,\r\n        tier: better ? progress.tier : current.tier,\r\n        units: better ? progress.units : current.units,\r\n        maxUnits: better ? progress.maxUnits : current.maxUnits,\r\n        kind: better ? progress.kind : current.kind,\r\n        group: group ?? current.group,\r\n        mode: workout.mode,\r\n        updatedAt: now,\r\n      };\r\n    }\r\n    record.equipped = route;\r\n    save();\r\n    return true;\r\n  }\r\n  function dispose() { disposed = true; identity = null; lastUnits = 0; }\r\n  return { read, observe, setOwner, dispose };\r\n}\r\n","arcade/pyramid-jump/data.mjs":"// Generated by tools/gen-pyramid-jump-data.mjs. Paper silhouettes from the Dr. Girlfriend roster + colour table.\r\nexport const SHAPES={\"myr5\":{\"d\":\"M80,16L76,20L73,30L71,33L71,35L48,100L46,114L46,124L49,133L55,142L53,141L51,144L46,157L41,163L35,179L35,184L31,190L32,201L33,206L36,207L37,206L37,200L38,200L40,208L43,209L44,208L44,200L46,200L47,207L49,207L51,206L51,198L53,201L55,201L57,196L59,202L62,207L64,215L63,225L59,228L57,231L57,234L60,236L84,236L86,234L86,231L80,225L79,215L93,216L102,215L103,217L102,225L96,231L96,234L100,236L105,235L120,236L124,235L125,231L123,228L119,226L118,215L119,207L123,202L125,194L126,185L127,186L126,180L128,184L128,179L128,179L130,185L130,183L131,186L131,180L131,183L131,182L132,183L133,182L133,186L132,181L134,185L133,182L133,181L135,183L134,179L136,184L135,177L136,178L136,180L135,171L136,173L135,170L136,170L137,172L136,170L139,168L149,163L152,155L159,152L162,148L164,151L166,151L168,148L169,139L168,137L166,135L163,136L161,138L158,135L155,136L154,137L150,135L147,136L143,120L141,118L140,118L138,120L138,124L139,142L138,147L135,152L129,142L127,142L133,132L135,122L135,113L132,100L119,71L96,31L87,18L83,16ZM49,181L49,182L50,180L50,184L51,183L51,184L52,181L52,183L52,181L53,182L54,181L54,183L55,180L56,190L53,187L50,188L47,184Z\",\"e\":[91,91,9],\"v\":\"0 0 200 240\"},\"01-seed-pearo--3d_character_model\":{\"d\":\"M104,16L100,16L96,18L89,25L76,41L65,59L62,73L61,83L63,89L69,101L73,105L79,109L74,114L70,112L65,112L61,113L57,117L55,122L56,127L53,129L49,135L46,140L46,144L43,148L42,153L40,156L35,167L34,171L34,176L33,183L34,192L36,196L40,201L44,203L48,202L51,201L52,198L52,190L52,189L55,191L57,192L60,190L61,188L61,183L58,177L59,173L59,163L58,158L60,154L60,149L63,146L66,141L62,157L63,167L66,174L63,181L62,187L63,194L65,199L64,202L65,205L62,209L60,217L55,221L52,228L52,232L53,234L58,236L84,236L87,236L90,234L91,231L91,225L90,212L87,207L87,200L91,196L92,189L96,190L109,189L110,195L113,200L114,206L111,214L110,231L112,235L116,236L142,236L145,236L147,234L148,231L148,227L146,222L141,217L139,210L136,205L135,199L138,194L138,181L134,174L138,166L138,157L135,141L137,146L141,149L141,154L143,158L142,171L143,177L140,185L140,188L141,190L145,192L149,189L149,192L148,195L148,198L151,201L154,203L159,202L162,200L165,196L167,191L168,182L165,165L162,158L158,153L158,148L155,144L155,141L153,136L149,131L145,127L145,121L144,117L141,114L137,112L131,112L126,114L123,109L128,106L135,99L140,88L141,75L136,58L129,45L120,32L107,18Z\",\"e\":[101,61,9],\"v\":\"0 0 200 240\"},\"01-seed-pearo--cute_alien_figure_3d_model\":{\"d\":\"M99,16L96,18L87,26L74,42L63,59L56,75L52,91L52,106L55,117L62,132L63,138L60,144L51,153L44,163L37,176L33,184L31,194L31,207L34,214L37,217L41,218L44,217L49,214L50,212L49,204L52,206L54,206L59,222L56,232L57,234L59,236L90,236L92,234L95,228L100,226L105,228L108,234L110,236L142,236L143,235L144,232L141,224L141,221L145,211L146,206L151,204L150,211L153,216L159,218L164,217L167,214L169,207L169,194L167,183L160,169L152,157L140,143L137,137L138,132L145,117L149,103L148,90L143,73L133,53L123,38L115,28L104,18Z\",\"e\":[100,74,9],\"v\":\"0 0 200 240\"},\"01-seed-pearo--white_3d_character_model\":{\"d\":\"M85,16L77,20L70,29L55,62L50,78L49,86L52,95L57,103L62,110L67,113L72,116L71,118L71,122L65,124L61,129L59,132L59,137L50,147L47,155L40,167L36,184L36,194L40,202L44,205L50,205L55,203L57,201L57,199L54,193L57,193L60,191L61,187L60,183L62,180L66,172L69,177L66,181L64,189L64,197L66,202L64,209L64,218L52,227L51,233L53,236L95,236L97,235L99,230L96,209L95,205L93,202L95,198L96,190L106,190L107,198L110,203L109,208L108,227L108,232L109,235L111,236L143,236L145,233L146,226L140,208L138,202L136,200L137,197L137,193L142,189L144,190L140,198L141,201L144,203L153,205L159,202L162,199L165,193L164,171L162,162L159,157L158,151L154,144L147,137L146,132L144,128L137,123L128,122L130,118L130,114L128,112L141,103L147,96L151,88L150,78L147,70L139,55L129,41L120,32L110,24L97,18L90,16ZM135,153L139,162L138,176L135,180L133,176L137,169Z\",\"e\":[100,67,9],\"v\":\"0 0 200 240\"},\"01-seed-pearo--blank_humanoid_figure_3d_model\":{\"d\":\"M110,18L100,16L89,19L79,23L71,29L62,38L54,50L48,61L47,65L45,80L49,91L53,97L65,109L73,113L73,114L70,113L64,113L60,115L57,117L56,122L46,133L44,139L41,142L39,148L36,152L32,161L31,167L31,171L28,180L29,190L33,197L36,198L39,197L43,198L47,197L48,193L45,186L48,184L50,187L54,188L55,187L57,184L58,181L55,174L58,168L59,156L61,153L62,148L63,147L67,139L68,146L71,150L69,152L67,155L67,160L68,163L65,166L62,172L61,185L62,189L61,192L62,197L59,202L57,216L52,220L49,227L50,234L54,236L86,236L89,233L90,225L88,220L90,215L90,205L88,198L89,194L88,190L90,187L90,180L98,182L106,182L110,180L111,185L112,190L112,194L112,198L111,201L110,217L111,220L109,226L109,232L111,235L113,236L144,236L148,234L149,232L148,224L145,219L142,216L141,205L139,197L140,195L140,192L138,189L139,183L138,172L136,165L133,162L134,156L132,152L130,150L133,147L134,138L139,148L140,152L142,156L142,165L145,174L143,181L144,186L148,188L150,187L153,184L155,186L153,192L153,195L155,197L157,198L161,197L164,198L167,197L170,195L172,191L172,180L170,171L170,163L165,153L162,148L161,143L157,139L154,132L151,127L145,122L142,116L137,113L130,113L128,114L127,113L136,110L146,99L153,90L156,80L156,73L153,62L150,54L146,48L134,34L120,22Z\",\"e\":[100,64,9],\"v\":\"0 0 200 240\"},\"01-seed-pearo--blank_toy_figure_3d_model\":{\"d\":\"M65,23L59,26L54,31L48,41L44,54L45,68L48,78L52,84L57,89L63,94L69,96L76,97L82,97L82,100L76,103L74,106L71,104L68,104L61,106L55,113L51,118L47,126L46,130L47,133L44,137L44,140L39,145L35,156L34,163L36,167L33,175L33,188L36,193L39,195L46,197L51,195L52,192L52,190L48,185L47,182L51,180L52,184L56,187L58,187L60,185L61,179L60,175L56,169L59,165L61,153L61,148L60,146L62,143L62,140L65,138L71,127L73,135L77,139L74,141L72,145L73,150L69,153L66,159L63,177L63,181L66,185L65,190L60,200L58,215L53,220L51,225L50,231L51,233L56,236L65,236L86,236L91,234L93,230L92,221L90,218L91,215L91,204L90,195L87,191L88,189L87,186L90,182L91,165L103,166L108,165L109,182L110,184L112,186L112,191L109,196L108,202L108,215L109,218L107,224L107,233L110,235L114,236L145,235L149,232L148,226L146,221L141,215L139,199L136,193L134,191L133,185L136,180L134,162L131,154L126,150L127,148L127,144L123,139L125,137L127,135L128,131L128,127L133,137L137,140L137,143L139,146L138,148L139,158L140,165L143,169L139,175L139,185L141,187L145,186L148,180L152,182L151,186L147,191L148,194L152,197L158,196L163,193L166,187L168,183L168,178L165,171L163,167L165,164L165,159L161,147L156,140L154,136L152,133L153,131L152,125L144,112L140,108L134,104L129,104L125,106L122,102L117,100L117,97L128,97L138,93L145,87L150,80L154,71L155,62L155,51L152,41L145,31L136,24L133,23L126,21L122,22L118,25L115,21L107,17L99,16L91,17L84,21L82,25L75,22Z\",\"e\":[99,57,9],\"v\":\"0 0 200 240\"},\"02-taper-tallstalk--3d_humanoid_figure\":{\"d\":\"M115,16L90,18L85,20L86,27L91,46L96,51L96,56L94,57L84,59L82,60L82,62L79,63L77,65L74,75L65,95L59,99L48,114L36,128L33,134L32,149L34,157L40,164L42,165L42,159L45,160L46,158L40,152L42,150L40,147L41,143L45,140L45,148L46,152L49,153L50,149L50,140L52,130L52,125L69,108L72,98L84,79L86,78L92,102L92,111L88,120L87,124L80,133L74,148L72,160L73,172L70,180L70,193L69,199L63,205L59,215L56,220L57,223L53,224L51,228L49,233L49,236L61,234L62,233L62,229L66,227L66,233L69,235L74,234L77,232L75,218L78,208L77,200L82,182L83,177L82,173L88,166L99,143L104,143L108,163L113,174L113,184L118,199L117,211L122,221L123,234L127,236L133,236L134,235L135,236L143,236L149,235L149,232L146,228L143,225L140,225L140,222L135,215L131,207L127,202L126,199L125,184L123,174L126,165L126,149L122,132L112,112L111,107L113,89L116,78L119,80L130,97L133,108L147,127L147,143L144,151L145,156L146,156L149,154L152,143L153,143L157,147L158,158L155,167L156,169L160,167L166,159L168,151L168,145L166,137L155,123L145,100L141,97L137,94L129,77L126,65L121,62L120,59L108,57L107,50L110,47L113,42L116,19Z\",\"e\":[100,34,4],\"v\":\"0 0 200 240\"},\"02-taper-tallstalk--humanoid_robot_3d_model1\":{\"d\":\"M137,16L108,17L101,20L74,37L75,38L79,44L96,62L95,71L70,75L66,78L66,84L63,89L63,94L65,100L59,117L57,116L52,121L32,155L32,163L38,170L37,172L35,173L33,176L33,183L35,188L36,189L36,192L43,204L45,205L47,203L46,200L47,198L42,188L43,186L44,179L50,182L53,189L54,189L55,188L56,184L52,175L49,172L46,171L53,168L57,164L68,129L67,119L75,106L89,126L92,126L93,131L92,141L88,146L82,151L79,158L78,168L81,174L79,178L76,191L74,203L75,206L72,208L68,216L65,226L61,234L61,236L90,236L90,232L86,224L86,219L84,208L83,207L85,205L90,190L91,183L97,181L107,173L110,174L112,198L114,204L112,209L110,217L110,224L112,229L112,236L144,236L144,234L141,229L132,222L131,217L124,206L123,205L124,195L124,161L126,159L126,150L123,143L121,141L115,140L112,137L109,131L107,124L110,123L112,120L120,88L123,87L133,103L132,108L132,117L136,133L139,139L148,142L145,147L145,150L143,153L144,157L148,157L149,152L152,149L155,158L153,166L150,170L152,170L152,172L153,173L159,169L162,163L164,147L162,142L159,142L158,141L167,133L168,130L168,123L159,93L143,76L134,70L127,70L125,74L121,75L120,72L118,70L108,71L107,69L111,68L119,56L138,17Z\",\"e\":[106,42,5],\"v\":\"0 0 200 240\"},\"02-taper-tallstalk--white_humanoid_doll_3d_model\":{\"d\":\"M94,17L81,21L73,30L67,42L64,57L66,69L70,82L83,103L87,107L83,110L81,113L77,111L72,111L68,113L65,117L64,121L64,124L58,130L56,135L56,138L52,142L51,146L46,151L41,162L41,166L39,170L36,177L35,187L37,192L39,194L48,195L50,194L53,190L52,187L49,183L49,181L53,179L55,183L58,184L62,183L63,181L62,174L59,170L61,167L64,159L64,152L67,148L67,145L72,139L75,132L77,131L74,151L74,156L76,159L73,162L72,165L72,168L73,172L71,175L69,182L69,186L71,188L69,191L69,195L70,196L65,203L63,214L56,220L54,224L53,228L53,234L56,236L83,236L86,236L88,234L89,231L89,225L87,222L88,219L89,209L87,202L85,198L87,194L86,190L89,186L90,177L89,173L90,173L93,174L103,175L109,174L111,172L112,173L111,176L111,184L115,190L114,195L115,198L113,203L112,218L113,222L111,227L112,234L115,236L145,236L148,232L147,225L143,219L137,214L134,200L131,197L132,193L130,188L131,187L132,183L130,174L128,172L129,166L127,161L125,159L127,155L127,149L123,131L126,132L128,138L134,145L134,147L137,152L137,159L139,165L142,171L139,177L138,182L139,185L143,186L146,184L148,180L151,182L153,184L149,190L149,192L150,195L153,196L160,196L162,195L165,191L165,188L165,177L163,171L159,166L160,163L159,160L154,150L150,146L149,143L145,138L145,135L142,129L137,124L137,120L135,115L128,111L123,111L119,113L117,110L114,107L119,103L126,93L134,74L136,62L136,54L134,42L128,29L118,21L111,18L104,16Z\",\"e\":[100,63,9],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--abstract_humanoid_3d_model\":{\"d\":\"M81,73L76,76L71,83L67,90L65,100L66,109L69,115L75,121L80,123L71,128L66,133L61,130L55,130L52,132L41,140L40,145L41,148L40,150L33,150L26,155L18,165L15,171L14,176L13,178L13,189L16,195L19,198L26,201L30,204L37,206L42,206L45,204L45,201L46,199L50,198L50,196L49,194L54,190L55,179L57,179L59,186L62,191L67,197L72,200L69,202L68,208L64,208L61,210L58,218L52,223L49,231L50,236L92,236L92,230L89,217L87,215L84,213L85,211L86,207L94,208L103,208L114,206L115,211L117,214L112,217L109,229L108,236L150,236L151,234L149,225L139,215L139,214L138,210L136,209L132,209L131,204L127,201L133,196L138,191L141,186L143,178L145,179L147,191L151,194L150,197L151,198L154,199L155,205L159,206L169,205L173,203L174,201L181,199L185,195L188,185L188,180L186,175L186,173L179,160L174,154L168,150L165,150L159,150L159,142L152,134L146,130L140,130L135,133L134,133L129,128L120,123L125,121L129,118L132,113L134,107L134,98L132,88L126,78L123,75L116,72L85,72ZM144,156L149,157L146,161L146,167ZM51,156L56,156L55,165ZM107,148L112,151L117,158L118,165L116,171L113,176L107,181L101,183L95,182L90,180L85,173L84,164L86,158L91,152L96,148L100,147Z\",\"e\":[100,98,8],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--circle_center\":{\"d\":\"M124,17L116,16L97,19L87,22L82,26L79,34L78,43L80,57L83,71L85,75L92,75L97,74L97,75L88,77L80,80L70,89L69,91L67,98L62,100L58,103L56,107L57,110L55,111L45,133L42,136L32,154L29,163L29,171L31,172L32,176L29,180L28,187L30,192L30,195L33,201L36,205L40,208L42,207L43,205L47,207L49,207L52,204L52,202L47,197L42,190L42,180L38,176L39,175L43,173L48,176L52,182L52,184L55,189L58,189L59,188L59,184L57,180L57,178L52,169L47,167L49,163L48,151L50,139L63,117L66,118L67,131L71,140L81,152L88,156L79,182L76,187L75,200L73,214L71,215L64,220L53,222L49,224L46,227L44,232L44,235L56,236L62,234L68,236L77,236L86,235L89,233L89,230L88,224L82,215L84,202L88,193L87,187L97,165L104,167L112,165L116,179L117,187L116,193L119,215L116,217L113,222L111,228L111,234L115,236L151,236L155,235L155,232L154,229L150,226L145,223L136,220L133,218L127,214L128,189L126,184L123,161L120,155L125,153L131,147L137,139L141,130L142,117L143,118L148,131L152,143L153,152L153,163L155,169L152,171L151,173L150,178L150,181L146,190L149,191L152,189L154,186L154,183L160,175L163,174L164,175L163,177L161,179L162,189L157,195L157,197L158,199L154,204L155,208L157,208L160,205L164,205L170,196L171,192L171,178L169,175L169,173L172,170L172,165L167,151L158,134L150,104L145,100L140,98L136,90L129,82L121,77L110,76L110,68L119,64L122,62L128,54L131,45L132,40L131,28L128,19ZM98,93L108,93L118,97L124,104L127,110L128,114L128,122L123,133L119,138L113,141L107,143L98,142L88,138L84,133L80,122L80,112L85,102L92,96Z\",\"e\":[105,46,7],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--ringed_humanoid_3d_model\":{\"d\":\"M76,17L73,19L69,27L68,33L69,45L73,54L78,62L81,64L90,68L90,76L79,77L71,82L63,91L60,98L55,100L51,104L42,134L33,151L28,165L29,170L31,173L31,175L29,178L30,192L31,196L37,205L40,205L44,208L45,208L46,204L42,199L43,197L43,195L38,189L39,179L37,177L37,175L38,174L40,175L46,183L46,186L48,189L52,191L54,190L50,181L51,178L50,173L48,170L45,169L48,163L47,152L48,143L53,127L57,118L58,117L60,130L63,139L69,147L75,153L80,155L77,161L74,184L72,189L73,214L64,220L56,223L51,225L46,229L45,232L45,235L50,236L86,236L88,235L89,234L89,230L88,225L85,218L81,215L84,193L83,188L84,179L89,165L96,167L103,165L113,187L112,193L116,202L118,215L113,224L111,230L112,233L115,235L123,236L132,236L138,234L145,236L156,235L157,232L155,227L151,224L146,222L136,220L130,215L127,214L125,200L125,187L121,182L112,156L119,152L125,146L129,140L133,131L135,118L137,117L150,139L152,151L151,163L153,167L148,169L143,178L143,180L142,184L141,188L143,189L145,189L148,184L148,182L152,176L157,173L162,175L162,176L158,180L158,190L148,202L148,204L151,207L153,207L157,205L158,207L161,208L164,205L167,201L170,195L170,192L172,187L171,180L168,176L169,172L172,171L171,163L168,154L158,136L155,133L149,117L146,111L144,110L144,107L142,103L137,99L133,98L132,91L130,89L120,80L112,77L103,75L104,74L108,75L116,75L118,71L121,57L122,43L121,34L118,26L114,22L103,19L84,16ZM94,93L102,93L110,97L115,102L119,108L120,113L120,123L116,133L109,140L99,143L90,142L82,138L76,132L72,122L73,111L77,103L82,97L89,94Z\",\"e\":[95,46,7],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--robot_3d_model\":{\"d\":\"M83,16L83,18L86,27L87,34L79,38L69,46L57,60L56,63L61,67L84,79L87,82L83,84L79,87L74,97L68,95L65,91L60,89L54,91L50,95L47,96L44,98L41,104L39,110L23,141L22,147L23,170L25,174L28,177L35,181L42,180L47,177L47,175L46,174L41,173L37,168L36,161L38,153L43,153L46,155L48,160L47,162L45,165L45,167L47,169L50,170L54,166L56,162L55,154L53,150L46,145L44,137L45,131L48,121L51,114L57,108L62,107L64,105L67,107L64,114L63,122L63,132L64,137L68,145L71,150L78,157L75,165L73,165L70,168L67,175L65,190L66,192L63,200L61,208L55,214L48,222L46,228L44,236L70,236L78,235L77,226L72,214L75,211L77,207L77,202L75,197L75,195L79,192L80,189L84,178L84,175L83,173L88,162L94,164L101,164L109,163L113,173L112,175L114,189L116,192L114,197L116,202L115,213L112,216L109,225L108,235L116,236L142,235L140,227L132,214L126,208L126,204L128,200L128,197L126,192L129,187L129,176L127,169L124,165L122,164L120,158L125,152L133,142L136,133L137,127L137,117L134,108L132,104L134,103L136,104L141,105L144,108L150,114L154,123L156,131L156,136L154,142L147,148L143,155L143,160L145,163L148,166L151,165L152,162L151,156L153,153L162,150L163,150L165,153L166,162L164,167L161,171L156,171L154,173L155,176L159,179L164,179L168,178L173,175L177,169L178,157L176,140L161,111L155,98L149,91L148,91L141,87L136,86L130,88L127,93L124,94L118,89L110,86L128,68L133,62L121,48L110,38L106,30L98,22L93,19ZM104,104L110,107L115,112L118,120L118,128L116,135L111,141L106,144L98,145L91,142L85,136L82,128L82,123L84,114L89,107L95,107Z\",\"e\":[94,49,5],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--stylized_3d_character1\":{\"d\":\"M117,19L103,16L96,16L85,18L78,21L69,27L63,32L58,40L54,48L51,62L51,69L54,80L57,87L63,94L69,98L65,102L59,100L53,101L47,104L44,108L42,113L42,116L37,121L32,130L29,133L28,136L23,143L20,154L19,158L20,161L17,171L18,181L20,188L24,192L27,194L31,193L40,188L40,186L35,177L41,174L42,178L46,181L49,181L52,178L53,176L53,170L47,162L49,158L50,148L49,142L50,140L56,128L57,128L54,145L55,155L58,165L62,168L60,169L58,176L57,181L57,187L58,189L57,192L57,197L54,203L52,215L47,219L40,222L38,226L37,230L38,233L41,235L83,236L85,234L87,227L83,218L84,213L84,207L83,201L81,198L83,194L82,191L84,188L85,179L95,181L108,181L114,179L115,179L116,185L118,190L118,196L119,198L116,205L116,216L117,218L114,226L114,233L115,235L123,236L159,236L161,234L163,228L161,224L158,221L152,219L146,214L146,202L145,199L143,197L144,194L142,189L143,186L143,181L141,172L139,168L141,166L145,156L145,143L143,128L144,128L149,140L151,142L150,144L151,152L153,162L147,170L147,176L148,179L152,181L154,181L158,178L159,174L163,176L164,178L163,181L160,186L160,187L163,190L168,193L175,193L180,188L183,178L183,171L180,161L181,158L179,149L174,139L172,136L170,132L168,130L166,126L161,118L158,116L157,110L153,105L144,100L138,101L135,102L131,98L135,96L141,90L144,84L147,78L149,70L149,61L146,48L141,37L135,31L129,26Z\",\"e\":[100,57,9],\"v\":\"0 0 200 240\"},\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\":{\"d\":\"M85,18L73,24L64,32L56,45L53,54L53,59L54,70L59,83L63,89L69,95L75,99L80,101L77,103L75,107L71,104L66,103L62,104L57,109L56,112L57,116L53,118L49,123L46,131L46,134L43,138L43,141L38,146L35,153L34,160L35,164L33,167L31,172L31,183L34,187L36,189L39,191L42,190L47,189L49,186L50,184L47,180L47,178L49,178L52,181L55,181L58,179L58,173L55,167L58,163L59,157L59,150L58,146L59,143L59,140L63,137L65,133L64,149L65,154L68,161L65,165L64,168L66,172L64,174L63,180L63,184L65,189L64,191L64,196L59,204L58,210L58,216L54,220L51,226L52,234L57,236L81,236L84,235L86,232L86,228L84,222L87,217L87,210L86,203L82,197L83,195L82,190L85,187L86,182L86,176L84,173L93,175L101,176L111,175L116,172L114,177L114,183L115,186L118,190L117,192L118,197L115,200L113,209L113,216L116,222L114,228L114,232L116,235L118,236L145,236L148,234L149,232L149,228L146,220L142,217L142,210L140,203L136,196L136,192L135,189L137,185L138,182L136,175L134,173L136,166L132,161L136,149L135,133L137,137L141,140L141,142L142,146L141,155L142,162L145,167L142,174L142,179L145,181L149,180L151,178L153,178L153,179L151,183L151,188L157,190L163,190L166,188L169,183L169,174L167,168L165,164L166,158L163,148L158,141L157,137L154,134L154,131L152,125L148,119L143,116L144,112L142,108L139,105L135,103L130,104L125,107L123,103L120,101L126,98L132,94L139,87L142,82L146,74L147,65L146,50L143,42L136,32L125,23L115,18L106,16L95,16Z\",\"e\":[100,59,9],\"v\":\"0 0 200 240\"},\"04-crest-wedge--robot_creature_3d_model\":{\"d\":\"M152,65L130,82L129,84L131,87L129,89L127,87L120,84L104,81L84,83L75,86L71,89L69,87L71,83L62,75L47,65L47,69L52,95L56,96L61,94L63,96L58,100L58,102L78,128L67,129L66,131L66,137L63,137L57,139L53,145L54,147L54,148L50,144L48,145L35,154L17,170L12,178L16,178L23,176L24,176L15,192L13,199L13,205L14,206L23,201L39,187L39,202L41,204L50,190L63,161L63,159L60,157L62,155L64,156L67,155L72,150L85,176L84,177L82,177L78,184L80,186L79,188L75,188L63,211L59,211L56,214L46,228L45,234L47,235L87,236L90,235L91,228L90,222L88,216L90,194L87,191L88,189L90,189L92,186L100,195L103,193L108,186L110,189L112,189L113,192L110,193L113,217L110,224L109,233L110,235L114,236L149,236L153,236L154,235L155,229L154,227L143,213L141,211L137,211L125,188L123,187L121,188L120,186L122,183L120,178L118,176L116,177L115,176L128,150L133,155L135,156L138,155L140,157L137,161L152,192L159,204L161,203L161,201L161,187L177,201L186,206L187,205L187,202L184,192L176,176L177,175L184,178L187,178L188,177L185,172L166,154L152,145L150,144L146,148L146,147L146,144L143,140L137,137L133,137L134,131L132,129L122,128L142,102L142,100L137,95L139,94L142,96L148,96L153,69Z\",\"e\":[100,96,4],\"v\":\"0 0 200 240\"},\"04-crest-wedge--stylized_3d_character\":{\"d\":\"M125,16L95,32L89,42L81,44L68,50L55,82L63,100L67,107L76,115L85,119L81,125L78,123L71,122L66,125L62,132L62,136L55,145L50,153L47,154L44,157L39,166L33,179L34,198L36,202L38,204L41,205L44,203L45,199L44,192L49,189L53,195L55,196L58,196L60,194L60,190L58,181L59,175L63,163L62,161L64,160L72,145L76,145L72,159L72,165L74,169L72,170L68,180L66,193L67,197L63,201L61,219L48,229L48,233L50,236L84,236L87,234L88,227L86,220L87,205L86,202L84,199L86,198L90,184L92,186L96,187L107,186L111,184L114,197L116,199L114,203L114,221L112,227L112,231L114,235L116,236L149,236L152,234L153,231L150,227L139,218L137,201L134,197L134,190L131,175L129,171L126,169L128,165L128,158L123,143L127,145L136,159L137,160L138,167L143,183L141,191L142,195L144,197L148,196L151,190L156,193L155,201L156,204L162,205L166,202L167,181L165,174L155,156L149,152L147,148L139,138L140,133L138,129L135,125L131,123L127,122L123,123L120,126L117,122L127,115L134,108L139,99L147,81L147,78L142,74L136,61L139,54L140,45L137,41L134,39L132,36L128,21Z\",\"e\":[101,69,9],\"v\":\"0 0 200 240\"},\"05-slope-bobble--clay_humanoid_figure_3d_model\":{\"d\":\"M124,67L114,67L109,68L98,71L92,74L78,84L65,98L61,106L62,112L65,118L69,122L75,125L85,127L78,130L74,134L72,137L70,135L66,135L61,138L60,142L57,141L51,144L42,153L41,156L41,159L37,158L34,159L22,171L15,183L12,196L13,206L15,211L19,215L28,217L32,217L42,212L51,204L57,195L59,185L59,176L58,172L56,169L60,170L65,164L66,172L69,178L74,184L81,189L79,191L77,196L74,196L70,200L62,214L54,215L51,218L51,221L53,229L56,233L59,236L83,236L91,235L93,233L95,227L93,222L93,204L92,202L89,200L91,197L92,194L99,196L105,196L109,194L109,197L111,200L108,203L108,222L106,225L106,230L109,234L117,236L141,236L143,235L146,231L149,227L150,220L148,215L145,213L138,214L129,198L127,196L123,196L122,192L120,189L125,186L132,178L134,171L136,164L140,170L142,170L145,169L142,173L141,178L141,185L142,191L148,202L156,211L165,216L171,217L180,215L185,211L188,202L187,191L185,182L176,169L169,161L165,159L162,158L158,159L159,157L159,155L154,148L149,144L143,141L140,142L139,138L134,135L130,135L128,137L122,130L114,127L111,127L111,126L122,122L129,117L136,108L140,97L140,87L137,78L130,71Z\",\"e\":[101,97,7],\"v\":\"0 0 200 240\"},\"06-ridge-triad--geometric_robot_3d_model1\":{\"d\":\"M100,60L61,96L62,99L68,120L70,121L52,122L47,126L47,128L56,141L51,144L51,147L49,147L41,158L41,162L33,160L31,160L28,161L22,170L12,187L14,191L16,192L15,193L16,202L18,205L33,209L39,209L44,206L47,202L48,197L51,197L54,195L61,177L60,175L55,170L51,169L52,168L55,167L60,159L60,157L63,156L65,154L83,180L81,180L79,184L78,188L79,190L76,193L71,196L50,229L48,233L49,236L95,236L96,232L91,200L88,193L89,192L91,190L100,199L109,190L110,192L113,193L111,195L111,197L109,202L104,231L105,235L150,236L152,235L152,233L148,225L129,195L124,193L121,190L122,188L122,185L119,180L117,180L135,154L136,155L140,157L141,160L145,167L149,168L149,169L145,170L140,174L140,176L146,195L148,197L152,197L153,201L156,205L158,206L161,208L167,209L183,205L184,203L186,194L184,192L187,190L188,187L184,178L172,161L170,159L167,159L159,162L159,161L160,160L160,157L153,149L149,148L149,144L145,141L154,127L152,124L149,122L131,121L132,120L139,96Z\",\"e\":[100,90,6],\"v\":\"0 0 200 240\"},\"06-ridge-triad--robot_3d_model1\":{\"d\":\"M102,16L99,17L95,22L83,45L83,49L92,66L96,71L95,75L87,76L85,77L73,99L70,103L64,105L58,104L54,106L35,149L34,157L35,159L32,165L31,174L36,184L40,188L39,181L43,182L39,173L42,164L41,161L47,158L48,159L49,161L53,164L55,172L56,172L57,169L57,165L56,157L54,155L51,152L49,136L59,116L64,114L66,115L57,133L59,138L62,139L88,139L89,140L87,142L84,150L78,174L79,176L77,177L73,182L70,189L70,193L71,204L68,217L65,221L64,223L61,220L59,218L56,218L50,222L46,229L46,236L54,231L56,231L61,234L64,232L66,234L69,235L70,235L72,233L75,233L76,232L79,236L80,234L82,230L82,225L79,220L76,217L79,205L84,198L86,194L87,185L86,178L85,178L94,151L96,149L100,152L104,152L107,149L108,150L114,177L111,183L111,191L112,197L116,205L118,220L111,224L109,228L107,236L120,231L121,232L124,231L126,233L129,232L132,235L134,233L137,234L145,232L155,236L155,232L153,228L150,224L145,220L139,219L133,222L129,219L125,219L124,206L127,193L127,187L125,182L121,177L121,175L118,145L114,140L115,138L141,138L144,136L147,132L138,114L140,113L141,113L145,117L154,135L151,152L144,160L142,167L142,172L144,172L148,165L153,159L155,159L160,162L159,165L162,174L158,182L159,183L157,189L163,184L168,177L169,166L168,163L167,161L168,158L167,149L151,109L146,104L143,104L141,105L132,99L118,78L115,76L109,76L108,74L116,58L120,49L120,46L106,21Z\",\"e\":[101,45,5],\"v\":\"0 0 200 240\"},\"07-bulb-sphereling--cute_robot_3d_model\":{\"d\":\"M54,19L51,20L48,23L46,27L46,33L47,37L52,42L56,43L61,42L69,54L68,55L70,59L65,66L62,76L61,84L63,95L66,100L70,104L65,112L62,121L58,125L54,125L47,130L31,148L27,155L27,159L29,161L26,167L25,173L28,181L30,183L35,185L37,185L39,182L41,183L44,182L48,176L52,188L56,195L61,199L68,203L69,204L60,205L52,210L48,216L45,224L44,233L47,235L51,236L68,236L89,235L92,232L92,228L90,220L87,214L84,211L84,209L95,210L106,210L115,209L116,211L114,213L111,217L107,227L107,232L109,235L123,236L154,235L156,232L155,222L151,216L146,210L142,209L141,206L131,203L137,201L142,196L147,189L151,177L156,182L162,183L165,184L170,183L173,181L175,173L175,167L171,161L173,158L173,155L168,147L158,134L148,126L145,124L144,125L141,122L136,120L134,114L132,113L129,104L133,101L137,95L139,88L139,81L137,79L126,79L125,78L122,77L120,74L118,73L115,64L107,54L98,48L92,47L86,48L79,51L76,50L68,37L70,32L70,27L69,23L63,19L59,18ZM148,16L141,17L135,21L132,28L134,36L127,47L126,47L124,50L119,48L118,50L115,52L115,54L124,58L131,63L134,61L135,61L135,58L133,56L135,53L134,52L141,41L147,41L151,39L154,37L156,33L156,26L153,20Z\",\"e\":[90,77,8],\"v\":\"0 0 200 240\"},\"07-bulb-sphereling--humanoid_robot_3d_model\":{\"d\":\"M72,16L67,20L65,28L66,32L69,35L74,37L77,36L82,43L76,53L75,58L74,64L75,70L77,72L79,76L82,78L89,79L95,79L95,81L92,81L88,84L87,87L84,86L79,90L78,95L79,102L77,104L74,112L73,116L71,118L68,120L66,126L64,127L52,145L50,151L49,159L50,161L53,161L51,163L49,172L50,173L50,180L52,183L60,186L63,185L64,181L61,177L60,172L61,167L65,169L64,172L65,176L68,177L71,174L70,166L69,162L66,160L69,156L76,133L74,131L76,130L78,127L79,124L78,122L86,103L88,103L89,110L93,114L95,115L97,119L95,120L95,122L90,126L88,130L90,132L87,134L85,137L85,142L86,145L83,164L84,167L82,170L79,172L78,175L78,181L80,183L77,186L69,206L67,216L60,227L60,233L62,236L87,236L91,235L93,233L93,228L92,221L90,219L90,188L88,186L88,184L91,182L92,178L92,173L90,171L91,165L93,149L95,144L97,146L102,146L105,145L106,143L108,149L108,156L111,168L110,171L108,174L108,177L109,181L112,185L109,187L106,214L107,219L105,223L104,232L106,236L140,236L143,233L142,225L130,213L128,203L122,185L119,184L122,180L122,175L121,172L118,170L117,167L118,165L115,146L117,142L117,137L115,134L112,132L114,131L114,130L111,124L107,122L106,119L105,118L106,115L108,114L111,112L114,103L122,122L121,123L123,129L126,131L125,133L133,159L135,160L132,163L130,174L133,176L135,176L137,172L136,168L140,166L143,173L142,177L139,180L138,184L140,187L143,186L149,180L150,174L152,172L150,164L148,161L150,161L151,159L151,154L150,148L145,139L136,126L134,126L132,119L129,118L121,101L123,96L121,90L119,87L115,86L110,81L107,81L107,79L112,79L123,75L127,71L130,65L130,56L129,51L122,40L118,36L114,34L101,33L92,35L88,37L83,32L85,30L85,24L83,20L79,17Z\",\"e\":[97,48,4],\"v\":\"0 0 200 240\"},\"08-shard-asym--geometric_robot_3d_model\":{\"d\":\"M84,16L83,32L83,53L82,54L82,56L85,64L89,86L92,91L62,88L59,88L69,112L66,115L58,135L43,156L33,168L31,185L31,198L33,208L34,208L38,205L54,188L57,174L67,141L74,123L85,149L92,170L66,198L65,202L64,207L54,228L52,235L54,236L64,232L72,222L76,234L77,235L84,234L85,214L87,208L85,198L88,191L96,180L98,183L103,183L107,179L114,188L120,198L118,207L120,211L125,236L131,235L133,222L142,232L152,236L153,235L152,232L140,208L140,203L137,197L121,181L112,171L136,129L140,125L142,130L145,144L146,166L146,189L162,210L164,209L169,188L169,171L154,138L152,122L149,114L163,93L164,91L157,90L131,93L112,92L112,82L114,80L121,67L129,57L122,50L122,33L124,18L121,18L115,30L109,37L102,35L97,39L96,39L93,37L90,31L85,16Z\",\"e\":[105,50,4],\"v\":\"0 0 200 240\"},\"08-shard-asym--robot_3d_model4\":{\"d\":\"M45,99L42,102L44,107L55,126L65,138L62,141L60,145L58,144L53,147L48,152L47,155L44,158L41,156L37,156L23,168L16,176L14,178L13,184L12,195L15,203L20,207L23,204L27,191L28,190L33,190L33,192L25,206L26,208L28,209L33,208L43,202L47,198L53,187L57,170L55,167L57,164L57,163L59,162L66,155L67,153L70,153L71,160L73,168L80,176L87,180L83,182L82,184L82,187L82,190L80,191L79,194L76,194L74,196L68,213L64,213L56,224L54,231L55,235L62,236L82,236L95,235L98,234L99,233L99,229L98,221L96,216L95,200L92,198L92,195L90,193L93,192L95,188L94,183L99,187L105,187L110,182L109,185L109,189L110,191L113,193L112,194L113,198L111,199L109,200L111,217L109,224L109,234L112,235L123,236L137,236L147,235L149,233L149,229L145,220L141,214L139,213L137,213L130,195L128,194L125,195L123,191L121,190L122,188L121,184L120,181L117,180L127,172L131,165L132,158L136,156L138,153L141,157L145,158L147,163L149,164L150,166L147,170L148,181L151,196L156,204L162,210L168,213L171,213L173,211L171,197L177,212L180,211L184,207L188,198L188,189L187,184L179,172L169,160L166,158L162,159L154,149L164,152L169,152L172,148L167,138L160,128L160,126L148,113L136,104L121,98L108,96L106,97L103,100L101,84L98,76L93,77L74,84Z\",\"e\":[89,87,2],\"v\":\"0 0 200 240\"},\"08-shard-asym--fantasy_creature_3d_model2\":{\"d\":\"M112,16L104,19L98,24L84,39L77,48L71,65L69,70L70,77L64,82L59,91L51,94L43,101L38,106L35,115L33,124L34,132L36,135L39,138L37,147L37,157L34,159L31,167L30,174L28,175L27,187L29,196L31,200L35,203L45,210L48,210L51,208L46,202L43,198L42,189L44,182L48,181L52,186L54,192L53,198L53,199L56,200L58,199L61,194L62,189L61,183L58,178L55,174L55,171L54,167L51,161L49,160L53,142L57,141L61,138L65,132L68,126L78,133L81,142L81,151L84,157L80,162L78,162L75,167L71,181L70,191L73,203L73,213L65,215L58,220L53,228L52,234L60,234L63,235L68,236L83,236L88,235L89,234L90,231L89,226L91,220L91,210L88,194L86,190L89,172L92,171L95,174L101,174L103,171L107,174L116,198L121,207L123,221L122,230L124,234L128,236L155,235L156,230L153,224L148,218L140,213L133,193L126,179L111,156L113,151L115,134L120,131L128,116L136,124L137,126L139,129L139,132L142,143L139,145L137,148L137,158L140,163L133,168L130,173L129,181L131,186L134,188L135,189L139,175L142,172L147,171L152,178L152,187L149,194L142,200L142,201L141,201L142,202L149,203L154,203L158,202L165,198L171,189L173,166L169,154L166,138L163,135L157,136L150,123L150,119L147,117L148,115L139,99L143,96L144,91L144,87L141,77L134,72L124,67L119,69L113,72L106,70L100,69L99,63L93,56L94,49L98,37L104,28Z\",\"e\":[94,101,9],\"v\":\"0 0 200 240\"},\"08-shard-asym--stylized_action_figure_3d_model\":{\"d\":\"M99,16L91,21L74,39L62,47L46,57L42,62L41,66L42,71L50,84L67,102L80,112L78,113L77,119L77,116L75,114L73,113L68,113L60,116L55,120L54,123L55,126L52,127L46,133L41,140L41,143L36,146L27,159L25,164L25,170L20,181L18,189L18,193L21,199L24,202L30,200L34,200L36,199L38,194L36,190L36,187L41,186L42,190L46,193L49,193L51,191L53,188L53,185L50,176L56,160L56,154L60,152L68,143L69,139L73,140L69,161L69,166L71,167L69,170L66,178L64,189L64,193L66,195L63,197L61,200L58,214L58,220L54,224L52,230L53,234L55,236L58,236L88,236L90,232L89,224L91,218L90,201L88,197L89,196L91,192L92,180L109,179L110,194L113,197L112,198L111,202L110,211L110,220L112,224L111,231L112,234L113,236L117,236L147,236L149,233L149,228L147,224L142,220L142,210L140,201L138,197L134,195L137,192L137,188L136,181L132,171L130,168L131,166L131,157L128,140L132,139L133,143L141,152L145,154L145,158L150,173L152,176L149,181L148,186L149,190L151,192L154,193L157,192L159,189L160,186L165,187L166,188L163,193L163,197L167,200L171,200L174,201L178,201L180,199L182,196L182,190L181,181L176,170L177,167L175,161L169,152L163,144L160,143L160,139L154,132L146,126L147,122L146,120L140,115L133,113L127,114L125,115L124,118L123,113L121,112L133,103L150,85L158,73L160,65L158,61L154,56L150,53L138,47L129,40L108,20L103,17Z\",\"e\":[100,65,9],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--3d_robot_model\":{\"d\":\"M125,75L112,74L68,75L71,120L77,125L70,125L66,127L64,130L64,133L61,133L58,135L55,137L53,142L48,141L40,146L35,153L33,158L33,163L30,163L25,162L22,164L21,168L15,180L13,189L12,193L15,196L15,201L17,206L30,213L36,215L41,214L45,212L47,209L51,206L53,202L52,199L55,198L57,195L60,180L60,176L55,172L61,169L64,161L67,178L70,183L76,187L74,190L73,194L68,195L60,214L56,215L54,218L50,228L50,233L52,234L55,235L82,235L92,234L95,233L96,228L94,220L92,204L93,200L89,198L91,193L99,195L111,194L111,198L108,202L108,220L106,222L106,233L110,235L119,236L148,235L151,233L151,229L146,216L145,215L140,214L132,195L128,196L127,191L125,188L131,182L135,177L138,163L141,168L146,171L142,173L140,179L143,192L150,206L153,207L157,211L165,214L172,212L180,208L184,206L185,203L185,195L188,192L188,190L183,179L176,162L175,161L168,163L168,161L168,156L159,144L152,140L148,140L146,137L142,134L136,134L135,129L128,124L130,120L130,106L127,79ZM159,197L163,197L161,200ZM44,195L41,202L41,205L39,201L38,195ZM133,146L138,150L136,154L132,148ZM66,146L67,147L64,152L62,150Z\",\"e\":[99,99,7],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--boxy_humanoid_3d_model\":{\"d\":\"M76,17L73,19L72,22L65,95L66,102L69,104L76,105L76,108L74,107L68,107L63,110L60,114L52,127L49,134L50,138L48,140L44,151L42,161L43,165L40,175L42,187L44,191L48,193L52,193L55,190L56,185L54,181L56,180L59,183L61,183L64,181L64,177L63,172L62,167L64,163L66,147L66,143L69,140L72,133L69,148L70,156L72,160L70,165L67,187L69,189L66,195L65,208L65,212L60,216L57,221L54,227L54,233L58,236L89,236L92,234L93,233L93,226L92,218L90,216L91,215L93,211L93,195L91,191L94,186L96,171L104,171L107,188L109,191L107,195L108,211L109,215L110,216L109,218L108,223L107,233L108,235L111,236L143,236L146,233L146,226L140,217L135,212L135,208L133,192L131,190L133,188L133,184L130,164L129,160L131,157L131,149L128,133L131,141L135,143L134,146L137,164L138,167L136,177L137,181L139,183L141,183L145,180L146,180L144,185L145,188L147,192L151,193L155,191L158,189L160,182L160,176L158,165L159,160L155,144L153,140L150,137L151,135L151,133L145,122L135,109L131,107L126,107L124,108L123,105L132,104L135,101L133,67L127,20L126,18L124,17L115,16L91,16Z\",\"e\":[100,61,9],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--mini_robot_3d_model\":{\"d\":\"M43,21L40,24L38,27L37,36L37,68L39,88L41,93L47,97L63,98L62,101L57,100L53,101L44,108L41,112L43,115L40,118L33,130L31,133L30,137L25,146L21,158L21,161L19,165L17,174L17,181L21,191L27,196L32,196L37,194L40,190L39,188L35,181L39,179L41,182L43,185L47,185L50,183L52,177L51,172L49,167L51,162L53,145L55,142L55,139L60,125L61,135L63,138L64,139L63,140L61,143L60,149L61,159L63,161L61,165L57,181L58,185L56,190L57,192L55,194L51,213L48,216L45,223L44,230L46,233L49,235L58,236L81,236L88,234L90,230L90,223L88,218L89,215L89,201L88,194L90,174L102,175L110,174L111,186L113,187L112,189L112,192L113,194L111,202L111,214L112,218L110,227L111,231L112,234L118,236L122,236L149,236L153,234L155,232L156,230L155,222L149,212L145,193L144,192L143,187L142,185L143,181L139,165L138,161L140,158L140,150L139,143L137,138L139,135L140,125L145,139L146,143L147,146L150,164L152,166L149,176L149,180L150,183L152,185L155,185L159,183L161,178L165,181L161,188L161,192L164,194L172,196L175,195L179,191L182,185L183,179L183,170L179,161L179,159L174,144L170,137L169,132L167,130L161,118L158,115L159,113L158,110L152,104L145,101L138,101L138,98L154,97L159,94L161,90L163,81L164,59L163,35L161,25L158,22L154,20L139,17L121,16L71,17L56,18Z\",\"e\":[100,57,9],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--robot_3d_model2\":{\"d\":\"M77,76L75,78L73,107L74,122L71,123L66,127L66,134L60,134L56,137L54,140L49,140L45,142L41,145L34,155L33,158L34,160L33,162L26,161L22,163L16,180L12,189L13,191L16,194L15,204L17,206L37,215L44,213L48,211L49,207L54,203L53,200L56,198L58,195L61,178L61,174L58,170L62,166L65,178L71,184L76,188L73,190L72,195L67,196L59,213L56,215L52,227L51,232L52,234L75,236L86,236L90,235L91,234L93,234L94,232L94,226L92,213L92,200L89,197L90,194L104,194L111,192L112,199L110,200L109,202L106,226L107,233L117,235L143,236L153,234L154,233L154,230L151,220L148,215L142,213L134,195L132,194L129,194L128,190L126,187L131,184L134,179L136,174L138,165L140,164L143,169L147,171L143,175L147,196L149,198L152,198L152,203L153,206L154,207L156,207L159,213L163,216L165,216L183,205L185,202L185,195L188,192L186,182L178,163L176,161L169,163L167,160L168,154L166,150L162,145L155,140L148,140L146,136L142,133L138,132L138,128L134,125L124,124L128,121L130,117L133,77ZM68,147L68,148L65,152L64,151ZM135,147L138,149L140,149L140,151L139,152L137,150L135,149ZM133,67L132,68L132,72L133,70Z\",\"e\":[101,96,6],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--robot_3d_model3\":{\"d\":\"M72,16L71,19L68,44L66,54L63,89L64,95L69,98L64,100L62,98L59,97L48,102L42,110L30,129L28,133L30,136L28,139L26,139L24,141L15,157L15,176L16,191L18,194L20,195L29,197L34,196L36,192L36,187L41,186L42,171L46,171L47,167L47,147L47,145L43,144L47,143L49,140L54,117L56,116L60,115L62,119L62,124L61,137L55,144L53,165L56,178L61,183L66,185L59,187L56,200L51,203L43,218L39,227L39,231L41,234L48,235L77,236L88,235L92,233L93,226L86,208L83,203L80,203L80,191L119,192L119,203L121,205L115,206L114,207L113,210L111,212L112,214L110,218L111,220L106,229L106,232L108,233L117,236L142,236L156,235L160,231L159,222L147,202L145,201L141,201L142,200L142,197L138,183L143,177L145,168L145,144L143,132L139,117L143,120L152,142L156,144L155,145L152,146L151,148L152,160L151,167L152,170L153,171L157,172L161,188L159,189L167,196L179,195L182,193L184,181L185,166L184,160L175,140L172,138L168,139L165,137L169,135L170,132L162,119L150,103L144,98L136,99L135,101L129,99L134,95L136,92L133,56L129,17L126,16Z\",\"e\":[100,58,9],\"v\":\"0 0 200 240\"},\"09-monolith-tanka--white_horned_robot_3d_model\":{\"d\":\"M144,16L142,17L144,21L144,28L142,32L137,36L126,39L97,39L88,37L83,34L80,30L80,24L83,18L82,17L74,21L67,27L65,30L64,37L64,41L67,46L76,55L84,59L92,62L98,67L98,71L97,74L67,76L65,77L62,80L61,87L56,92L54,100L51,100L33,115L32,121L28,124L25,128L19,146L14,165L14,166L18,170L15,173L16,177L19,184L22,188L29,189L37,188L39,186L46,184L48,181L49,178L52,176L54,173L54,168L51,161L50,134L54,132L65,114L66,114L76,135L80,142L77,147L74,148L63,178L56,188L43,221L40,232L41,235L84,236L87,235L86,228L86,224L83,215L83,211L92,184L94,158L116,159L122,190L119,211L116,220L116,236L156,236L163,235L163,232L160,227L160,223L155,213L149,179L138,151L136,148L133,149L132,145L137,134L141,123L141,123L147,134L149,135L152,134L151,137L154,161L155,166L155,173L156,175L159,176L159,180L160,182L167,186L172,187L176,187L181,184L185,178L186,170L184,168L186,165L187,161L183,150L170,125L165,125L166,121L165,117L153,102L150,101L150,95L147,90L144,87L142,81L140,77L137,74L120,73L128,62L147,54L155,47L159,41L160,34L158,28L152,22Z\",\"e\":[111,56,5],\"v\":\"0 0 200 240\"},\"10-petal-wisp--stylized_creature_3d_model\":{\"d\":\"M92,17L81,21L75,30L74,34L75,44L79,60L83,70L82,74L78,80L73,83L68,84L64,86L58,91L54,97L50,108L49,123L47,132L44,140L36,155L34,161L34,165L36,168L42,171L46,178L50,182L52,182L55,181L60,173L62,159L57,110L58,105L61,101L65,95L63,104L63,113L65,122L67,127L72,132L80,136L79,162L80,181L80,196L78,213L73,222L64,231L63,235L65,236L94,236L96,236L98,234L95,217L94,199L88,164L87,154L88,144L94,167L104,187L108,200L109,207L109,226L110,233L114,236L150,236L150,234L135,219L127,209L116,184L112,178L107,173L100,158L97,137L106,132L113,122L115,112L115,107L111,93L116,96L119,101L124,148L128,162L133,171L146,182L154,185L158,186L156,174L156,167L155,162L157,161L164,159L166,158L166,157L163,154L145,141L138,134L135,129L133,128L133,124L131,117L128,100L123,91L116,85L111,84L105,83L97,78L94,70L95,60L103,51L110,47L114,46L117,47L122,52L123,49L124,42L122,34L119,29L114,23L110,20L101,17Z\",\"e\":[93,36,5],\"v\":\"0 0 200 240\"},\"10-petal-wisp--ghost_character_3d_model\":{\"d\":\"M89,16L86,17L78,27L73,35L72,36L69,40L65,51L65,65L67,72L73,77L72,84L69,83L60,76L58,76L58,80L62,89L69,97L77,102L78,108L76,110L65,115L60,120L55,141L52,148L46,152L39,154L34,159L28,158L26,159L27,164L34,167L38,171L45,173L51,173L60,169L64,165L67,156L68,141L72,149L73,156L70,168L64,175L45,183L40,187L34,195L30,204L30,215L32,220L35,225L43,232L55,236L65,236L72,234L78,230L83,223L84,219L83,216L78,222L72,226L65,226L60,226L55,223L52,220L50,216L50,213L52,209L57,205L74,200L85,195L91,191L98,184L102,176L105,166L103,147L104,138L107,141L113,149L123,155L130,158L142,157L147,155L155,150L159,142L163,125L162,121L160,120L161,117L167,111L175,107L171,104L167,104L160,107L153,113L148,125L144,121L143,121L136,130L128,136L117,132L111,126L103,116L99,113L92,111L92,107L103,98L108,91L110,86L114,70L113,63L111,56L99,37L97,35L93,40L90,38L88,34L87,27Z\",\"e\":[85,60,6],\"v\":\"0 0 200 240\"},\"10-petal-wisp--fantasy_creature_3d_model4\":{\"d\":\"M117,16L112,17L105,20L98,25L93,30L93,32L94,32L101,27L110,24L117,25L119,27L118,31L112,37L101,38L94,43L91,47L89,51L88,52L89,56L94,65L102,72L102,79L101,80L98,81L87,81L85,82L83,85L84,88L77,96L67,106L59,111L54,118L51,125L51,131L52,138L57,149L51,153L47,159L47,161L51,161L60,156L61,158L60,162L59,166L54,172L56,173L60,173L64,171L67,168L69,163L70,154L68,141L64,131L64,122L66,118L73,113L85,100L89,97L94,109L95,120L93,128L89,136L85,151L85,164L88,176L93,186L101,195L109,200L122,206L128,211L133,223L134,236L137,235L139,233L140,236L141,236L144,231L145,225L145,218L143,212L137,204L127,199L122,194L117,187L115,178L115,165L120,142L120,128L128,106L136,123L136,131L134,140L134,148L136,153L138,157L143,162L149,164L152,164L153,163L149,157L145,146L146,135L145,124L137,110L132,92L128,85L125,81L114,80L113,78L117,78L124,62L126,51L122,44L128,37L130,31L130,24L128,21L124,18Z\",\"e\":[108,54,5],\"v\":\"0 0 200 240\"},\"11-anvil-cask--clay-style_robot_3d_model\":{\"d\":\"M161,78L146,77L143,79L120,79L118,80L105,80L105,81L97,81L97,82L69,83L70,80L66,79L48,80L45,79L38,80L69,107L70,116L72,118L66,119L63,125L60,123L56,124L53,125L50,129L41,137L32,148L31,152L34,154L32,156L26,155L24,156L18,166L12,184L12,186L15,188L15,197L16,199L30,205L35,206L40,206L42,203L43,201L45,201L48,198L49,196L48,193L50,192L52,188L55,169L54,167L50,163L54,162L58,155L58,159L59,160L58,161L62,183L64,185L63,186L67,192L74,197L69,201L68,204L63,204L61,205L55,214L50,218L46,229L46,235L50,236L90,236L91,235L92,231L88,208L85,206L86,203L86,201L102,203L114,201L114,203L115,206L111,208L110,220L110,232L111,235L114,236L151,236L156,235L156,227L153,221L148,215L145,214L138,206L135,204L131,204L131,200L126,197L134,191L137,184L135,183L137,182L141,171L141,160L141,152L146,161L150,163L150,164L144,169L148,188L151,192L153,199L156,201L158,201L158,203L160,205L165,206L170,205L182,199L184,196L184,188L187,186L188,184L176,156L173,156L167,157L165,154L167,151L167,149L152,131L150,130L143,123L138,123L134,126L130,119L127,117L128,116L128,111L130,105ZM138,137L140,139L139,141L137,138Z\",\"e\":[100,97,5],\"v\":\"0 0 200 240\"},\"12-seedpod-snailslug--stylized_slug_3d_model\":{\"d\":\"M182,46L175,42L170,41L166,41L155,45L151,48L148,55L147,60L148,69L151,75L141,89L135,99L131,98L125,96L126,90L124,87L122,86L119,85L116,85L111,90L111,94L112,97L116,100L120,100L125,117L123,123L123,131L121,136L118,154L118,162L121,170L121,174L118,176L116,180L115,183L116,185L114,191L113,190L111,189L90,193L86,195L86,197L82,195L69,196L49,200L38,206L23,219L13,222L12,223L13,225L19,227L28,229L66,228L83,230L98,233L120,236L142,236L158,233L170,226L177,220L182,212L185,204L187,194L186,183L184,173L180,165L161,129L159,118L158,108L161,93L165,83L167,81L172,81L179,77L183,74L186,69L188,62L188,55L186,50Z\",\"e\":[167,59,5],\"v\":\"0 0 200 240\"},\"12-seedpod-snailslug--stylized_worm_3d_model\":{\"d\":\"M154,26L134,17L127,16L121,18L118,19L115,23L108,27L99,35L95,41L92,47L90,57L91,70L93,79L97,85L104,92L111,97L121,100L129,99L131,105L125,106L119,109L95,128L66,137L57,132L52,131L45,130L39,132L31,138L27,146L27,150L29,151L39,143L46,144L47,147L49,149L46,153L42,155L39,159L39,161L42,161L59,155L68,148L93,141L97,141L107,135L117,130L121,150L122,160L117,182L115,185L110,191L104,193L96,192L83,188L67,187L59,188L47,190L36,195L25,204L17,214L14,220L13,228L14,231L17,234L21,236L40,234L56,234L79,236L165,236L174,235L179,231L185,219L187,208L186,194L180,177L182,174L183,171L184,159L182,155L178,151L176,134L169,122L164,112L160,108L150,104L145,105L143,94L154,92L162,86L170,78L175,66L174,54L171,43L164,36Z\",\"e\":[133,59,9],\"v\":\"0 0 200 240\"},\"13-fan-split--stylized_3d_character2\":{\"d\":\"M120,18L117,18L112,16L86,17L77,18L62,22L44,30L30,40L16,54L13,59L13,60L17,63L67,91L86,103L83,105L79,110L77,110L75,108L74,109L70,114L63,118L50,132L45,140L33,149L22,164L13,182L13,189L19,191L19,194L22,198L26,198L29,201L32,201L35,198L37,201L40,201L42,198L43,192L44,188L46,191L49,191L52,191L53,189L54,183L57,170L57,162L59,161L61,152L60,149L65,146L63,159L64,169L68,177L72,181L76,184L77,185L70,185L67,188L64,213L62,219L58,225L58,233L59,235L67,236L81,236L93,234L96,232L97,228L94,191L92,188L89,186L97,182L98,173L99,179L100,182L103,184L109,185L108,187L106,187L104,190L101,225L102,232L105,235L109,236L135,235L140,233L141,231L139,225L135,220L133,214L130,198L130,188L128,187L121,185L121,184L127,182L131,177L133,172L135,160L133,147L128,134L127,130L134,140L137,147L140,149L139,151L140,161L143,162L143,173L148,191L152,192L155,190L158,200L161,203L163,202L165,200L171,202L174,200L175,197L179,197L181,195L181,192L186,189L187,187L185,179L178,167L166,149L159,143L153,142L154,138L153,135L132,114L125,110L120,110L117,105L112,103L124,96L182,67L184,66L184,63L169,47L156,35L138,24Z\",\"e\":[98,60,9],\"v\":\"0 0 200 240\"},\"13-fan-split--stylized_toy_3d_model\":{\"d\":\"M67,22L60,26L56,29L47,34L41,39L28,56L27,60L21,73L22,76L25,79L53,95L72,111L77,115L83,116L83,118L79,121L77,120L71,119L64,122L61,125L58,130L58,133L50,145L50,148L44,155L38,167L38,174L35,180L34,188L34,191L34,196L35,202L39,206L43,207L47,206L50,206L51,203L52,200L49,193L52,191L53,194L56,196L58,197L61,196L62,193L62,189L59,183L59,180L61,176L64,165L64,159L65,156L67,155L72,146L72,144L74,144L73,151L75,155L77,156L74,159L74,163L71,166L68,172L66,183L66,188L67,189L66,195L66,198L64,202L61,216L58,219L53,225L52,229L53,234L56,236L89,236L92,231L90,222L92,218L92,207L92,203L89,198L90,195L89,192L91,190L93,180L99,182L104,182L107,180L109,190L111,192L110,196L111,199L109,202L109,216L110,222L109,227L109,232L112,236L145,236L148,234L148,227L144,220L139,216L136,201L134,198L135,195L133,190L135,187L134,177L131,169L129,165L126,163L127,162L126,159L124,156L127,153L126,144L128,144L128,146L133,154L135,156L137,159L136,161L136,165L139,177L142,180L138,190L139,194L141,196L144,197L147,194L149,191L151,193L148,202L149,205L151,206L154,206L157,207L161,206L164,203L166,196L166,184L162,174L163,172L162,167L157,156L153,152L152,149L150,147L151,146L148,140L142,133L142,129L140,125L137,122L133,120L128,119L121,121L121,119L117,118L117,116L125,113L150,93L176,78L179,74L178,68L173,60L171,53L166,47L162,44L161,41L153,33L144,29L141,26L133,22L123,20L113,17L100,17L90,16Z\",\"e\":[100,60,9],\"v\":\"0 0 200 240\"},\"14-chisel-spire--cone_head_3d_model\":{\"d\":\"M102,16L98,17L96,21L85,68L79,121L80,126L78,129L75,129L68,133L58,148L48,172L42,190L42,199L46,206L48,208L52,209L58,209L60,205L56,200L56,197L58,194L59,197L62,199L65,199L66,196L67,192L65,184L70,165L70,162L74,156L74,167L73,170L73,175L74,178L73,180L71,189L71,196L72,198L69,210L69,220L60,226L58,233L60,236L93,236L95,235L96,230L94,222L95,214L94,199L95,198L97,189L105,188L106,197L108,200L107,220L108,222L107,226L106,232L107,235L110,236L136,236L138,235L138,227L137,224L134,221L134,218L130,197L132,200L136,200L138,199L140,195L143,196L142,200L140,205L140,207L141,210L144,211L148,211L154,208L158,202L158,189L154,170L150,160L144,146L139,140L131,131L128,129L124,129L122,125L122,119L121,100L114,59L105,20L104,18ZM127,153L133,163L136,183L131,192L128,178L128,170L127,167L128,163Z\",\"e\":[101,72,5],\"v\":\"0 0 200 240\"},\"14-chisel-spire--low_poly_robot_3d_model\":{\"d\":\"M88,16L68,80L79,90L88,97L82,99L80,100L80,103L78,103L55,120L47,129L47,133L48,135L41,135L30,148L22,160L20,191L21,193L23,193L41,174L42,170L39,165L47,158L45,172L46,173L50,172L53,169L57,161L60,149L60,146L55,139L58,141L61,141L79,115L78,135L82,149L85,156L86,169L84,170L78,180L66,202L61,208L59,223L59,231L63,235L66,236L76,234L78,232L85,211L84,206L85,195L91,176L101,186L103,188L106,188L115,179L118,175L123,188L125,207L124,211L130,231L134,234L140,236L145,236L149,233L151,229L148,217L147,207L131,180L123,169L123,168L124,156L129,145L131,136L131,122L129,114L145,141L149,143L147,146L148,158L151,167L158,178L160,178L161,174L159,164L159,163L165,167L164,177L178,192L180,194L178,161L168,141L161,135L157,127L150,120L131,102L130,101L129,102L127,98L120,97L133,89L141,82L142,79L120,16Z\",\"e\":[104,57,8],\"v\":\"0 0 200 240\"},\"15-orbital-coili--robot_character_3d_model\":{\"d\":\"M80,16L70,22L59,33L54,41L52,54L36,54L33,55L31,57L32,62L36,64L51,65L51,71L57,84L61,88L73,98L87,103L79,106L71,111L69,114L63,121L46,144L45,148L49,150L46,153L35,161L32,167L33,176L37,182L41,185L46,187L53,187L59,185L61,183L65,177L66,170L65,164L61,159L56,156L58,153L64,154L65,152L66,146L70,141L72,148L76,153L73,155L71,158L72,166L75,172L78,176L75,182L74,190L69,192L65,195L65,202L68,204L61,209L62,214L64,217L56,224L56,226L57,231L62,233L89,233L96,232L97,229L97,221L96,219L94,217L97,215L97,210L95,207L92,205L96,202L97,198L95,194L89,191L89,180L92,181L103,181L108,180L112,195L106,197L104,199L105,206L107,208L104,211L105,217L107,219L102,228L103,234L108,236L134,235L140,234L143,231L143,226L140,221L135,220L138,218L138,212L135,208L132,207L135,204L136,199L132,195L126,194L124,184L121,177L119,175L124,172L126,167L127,158L126,155L121,152L125,149L128,144L127,132L123,128L120,126L125,123L134,145L134,147L136,151L138,153L142,153L144,156L140,158L137,162L134,170L136,176L140,183L144,186L149,187L155,187L160,185L166,179L168,175L169,169L167,163L163,158L160,156L154,154L153,148L156,145L155,142L149,137L144,130L129,113L125,111L122,106L113,102L128,96L138,88L145,75L147,65L163,64L166,61L167,57L165,55L162,53L156,54L148,53L143,42L134,31L125,23L115,16ZM150,164L156,166L157,171L156,175L154,177L151,178L146,176L144,173L146,167ZM50,164L54,167L56,170L56,173L54,176L52,178L47,178L44,176L42,171L45,166Z\",\"e\":[99,60,9],\"v\":\"0 0 200 240\"},\"16-spade-arch--pyramid_head_figure_3d_model\":{\"d\":\"M102,16L97,17L92,22L64,59L44,88L45,91L61,104L76,114L82,116L77,118L73,122L71,122L60,129L47,141L38,153L35,162L30,165L27,167L21,179L19,185L20,206L23,212L29,213L33,211L51,194L52,189L51,170L48,168L52,166L61,147L67,138L71,134L70,138L67,143L64,156L59,162L58,178L59,188L59,203L52,208L45,218L41,229L42,234L44,235L52,236L70,235L72,234L81,234L84,233L84,220L82,209L78,203L82,193L87,185L93,179L99,178L105,179L108,180L112,184L120,198L121,202L118,204L115,209L112,215L109,224L108,231L108,235L114,236L129,234L132,223L133,221L136,227L137,233L139,235L151,235L157,234L159,232L158,223L151,210L147,204L140,200L139,187L141,175L141,166L137,151L131,139L136,144L140,150L139,151L142,154L142,155L144,157L144,158L147,160L148,164L152,167L150,169L151,175L153,175L153,188L156,195L165,204L174,210L177,209L180,204L181,190L175,175L168,162L165,160L163,162L162,161L162,159L165,156L162,152L151,139L140,130L127,122L123,122L116,117L123,114L146,99L154,93L155,89L136,59L108,23Z\",\"e\":[99,68,9],\"v\":\"0 0 200 240\"},\"16-spade-arch--stylized_humanoid_3d_model\":{\"d\":\"M106,16L64,33L64,37L73,56L82,60L64,65L55,72L53,75L49,83L35,100L29,105L17,137L17,147L17,153L19,159L25,163L32,167L37,166L42,164L45,161L46,155L49,151L49,145L45,139L37,132L46,119L48,114L55,110L69,98L70,99L76,117L77,127L73,130L70,134L68,139L68,146L65,147L62,159L61,171L58,185L54,208L49,212L47,219L43,227L42,234L44,236L77,236L80,235L82,232L84,227L84,216L83,213L81,213L82,191L80,180L97,154L106,155L113,169L119,180L118,196L119,213L117,214L116,217L117,229L118,233L121,235L125,236L149,236L151,234L153,228L152,217L150,212L146,209L142,186L139,174L139,160L137,149L135,139L131,132L126,127L126,122L128,115L135,100L145,110L156,118L158,123L163,135L154,141L150,144L148,148L148,155L151,158L150,160L150,164L152,166L158,169L160,169L161,170L168,169L177,166L180,163L183,153L183,141L181,136L177,116L175,109L171,105L165,96L156,84L152,77L148,72L143,68L125,62L132,59L135,58L147,36Z\",\"e\":[105,39,6],\"v\":\"0 0 200 240\"},\"17-shellcap-manyarm--3d_character_figurine\":{\"d\":\"M72,39L60,45L50,53L36,67L29,80L29,83L31,88L34,92L39,96L51,102L72,110L49,118L47,121L43,123L39,128L32,129L24,134L19,139L16,144L12,155L12,159L15,165L17,167L23,167L27,164L29,160L33,163L35,162L41,160L45,156L48,150L48,138L52,135L56,137L59,136L71,127L68,134L66,143L61,145L47,156L46,158L47,162L42,166L40,172L40,173L34,176L27,184L24,192L23,200L25,205L29,210L34,212L40,208L43,202L45,203L47,203L52,200L55,196L55,188L53,181L51,180L55,176L57,171L61,170L64,166L66,175L70,181L68,187L66,194L68,202L65,203L58,214L50,217L45,221L44,224L43,233L45,235L48,236L90,236L94,236L96,234L98,229L97,222L90,203L84,201L85,193L91,195L99,196L108,196L118,194L119,202L116,203L114,206L107,226L107,231L109,235L113,236L150,236L153,232L153,225L142,205L140,202L136,201L137,196L133,182L136,179L139,170L143,173L145,174L147,179L149,182L146,188L145,192L145,196L147,201L149,204L152,205L157,203L159,210L161,212L164,213L169,213L173,211L177,205L177,194L176,190L171,182L166,178L162,176L162,172L156,165L157,163L161,166L165,166L167,163L167,157L170,159L171,162L173,170L176,172L180,173L184,171L187,167L188,161L187,153L184,148L176,139L168,135L163,134L161,135L159,131L159,128L155,124L138,115L130,113L150,109L161,105L166,103L172,98L173,96L172,92L164,77L152,62L144,54L130,44L112,36L99,34L80,37ZM137,133L144,141L147,142L151,141L154,142L152,149L152,156L139,145Z\",\"e\":[100,75,9],\"v\":\"0 0 200 240\"},\"17-shellcap-manyarm--mushroom_creature_3d_model\":{\"d\":\"M13,165L12,171L14,174L19,178L23,178L30,176L37,180L43,180L47,178L49,179L45,180L41,182L34,183L27,186L24,186L18,189L15,191L13,197L12,201L14,205L19,209L22,209L28,206L32,200L41,199L46,196L47,196L42,201L39,202L35,205L26,218L25,223L26,230L27,233L32,235L36,235L40,233L43,227L44,222L47,219L52,212L61,206L63,210L59,218L54,223L53,227L53,232L57,236L85,236L89,232L92,222L108,222L111,232L113,234L117,236L143,236L147,233L148,228L145,222L137,210L139,206L148,211L156,221L157,227L160,232L164,234L168,234L171,233L174,230L175,227L174,218L169,212L166,205L152,195L154,195L158,197L162,198L168,199L172,205L177,208L181,208L186,205L187,201L187,195L184,190L181,187L177,186L173,186L169,182L162,180L159,181L155,178L151,177L153,176L157,178L165,177L171,174L177,176L182,175L186,173L188,169L188,165L186,161L183,158L179,157L173,157L168,160L161,160L153,163L140,161L137,158L150,157L154,156L157,154L159,148L159,144L157,138L151,127L137,112L126,103L113,98L98,97L90,98L82,100L69,106L57,117L45,134L41,142L41,148L43,154L47,156L63,158L60,162L47,164L39,162L32,162L29,160L23,159L17,161Z\",\"e\":[100,125,8],\"v\":\"0 0 200 240\"},\"17-shellcap-manyarm--mushroom_robot_3d_model\":{\"d\":\"M86,39L71,47L58,58L40,77L34,87L34,90L43,94L69,101L87,104L82,110L78,107L74,108L60,112L49,117L46,121L47,125L44,126L43,129L37,127L33,127L24,133L16,142L13,147L12,157L16,162L19,165L21,165L27,162L28,159L29,154L32,157L34,158L42,154L46,149L48,142L48,135L51,136L52,132L56,135L71,124L76,122L77,123L75,124L71,132L70,140L66,141L51,152L49,155L49,158L51,161L48,162L44,164L40,171L36,171L31,173L23,183L21,185L21,198L26,206L37,206L39,203L38,193L42,189L43,191L41,194L42,197L45,199L51,198L54,195L53,180L52,178L57,172L57,168L60,169L63,168L66,165L68,161L70,170L74,175L78,177L75,177L74,179L70,189L69,197L70,200L68,200L67,201L57,215L49,222L46,229L47,234L49,235L63,236L90,236L99,235L100,234L100,229L97,222L98,221L97,219L97,216L95,214L96,212L94,210L94,208L92,204L87,202L88,200L88,192L89,189L99,192L106,193L116,192L123,190L126,187L126,191L127,201L124,201L120,208L114,225L114,234L120,236L163,235L167,234L168,233L168,229L167,226L164,220L156,214L147,200L144,199L145,191L142,178L141,177L138,177L141,175L143,170L145,160L150,167L154,169L157,169L158,175L161,179L158,182L155,187L155,193L156,197L160,201L169,200L166,193L167,193L169,194L172,197L171,206L172,209L173,211L186,211L187,208L188,202L187,186L184,179L173,172L170,165L167,162L168,160L162,151L150,142L146,141L143,141L141,126L139,123L147,128L155,134L159,134L163,130L167,136L165,143L166,153L168,157L172,160L177,160L180,158L182,156L185,156L187,145L186,134L185,131L182,127L178,125L168,116L156,111L141,107L134,107L133,109L128,104L156,97L165,94L178,87L182,82L182,71L175,62L165,52L153,42L145,39Z\",\"e\":[108,71,9],\"v\":\"0 0 200 240\"},\"18-quad-all--dragon_creature_3d_model\":{\"d\":\"M137,16L127,19L107,28L92,38L77,53L69,56L57,65L46,78L40,88L37,100L36,107L36,116L39,119L44,121L49,120L52,117L62,102L68,95L80,106L82,111L83,117L78,127L76,133L69,141L67,149L64,179L61,195L60,209L58,218L59,222L57,223L54,222L52,224L48,231L47,235L55,234L56,236L57,235L68,236L75,234L79,236L79,233L78,227L74,223L75,212L83,186L95,168L104,170L112,170L117,168L129,186L139,222L136,223L134,226L132,231L132,236L137,233L138,235L142,235L143,236L144,235L155,235L157,234L164,235L164,231L160,224L153,221L154,219L150,180L148,175L144,147L142,139L135,131L133,125L125,115L116,111L98,97L81,87L91,85L93,84L112,83L138,88L144,88L134,78L125,73L103,69L113,65L125,63L139,58L143,46L144,36L137,38L122,44L109,53L102,53L110,41L116,34L126,25L138,17Z\",\"e\":[103,134,8],\"v\":\"0 0 200 240\"},\"18-quad-all--fantasy_creature_3d_model1\":{\"d\":\"M64,23L63,25L64,29L77,51L88,67L94,72L95,76L105,86L107,99L106,100L102,99L89,100L79,104L73,108L64,103L56,101L46,101L30,106L31,109L36,118L40,124L46,128L39,164L38,168L33,173L31,177L29,199L23,229L24,234L26,236L60,236L61,232L58,226L54,222L51,220L46,209L45,196L46,183L47,180L53,176L64,163L69,154L72,144L79,145L83,164L79,168L77,174L78,207L76,230L80,235L81,236L118,236L119,235L119,232L117,228L113,223L108,220L105,211L99,191L98,176L100,170L102,167L108,168L114,168L120,166L126,162L130,177L130,184L136,210L137,228L139,234L141,236L176,236L177,233L174,228L163,219L160,213L154,202L145,182L145,155L145,148L144,140L141,132L137,128L136,119L131,112L133,100L135,92L141,90L157,91L156,85L153,79L151,74L140,50L128,33L123,28L121,26L113,26L106,20L100,16L100,17L102,25L100,27L100,30L102,39L92,32L86,28Z\",\"e\":[111,135,7],\"v\":\"0 0 200 240\"},\"18-quad-all--fantasy_creature_3d_model3\":{\"d\":\"M99,16L97,18L94,24L90,33L83,61L84,70L82,74L82,94L84,98L89,103L93,110L82,119L79,125L76,125L72,126L68,130L64,139L61,149L58,168L51,183L45,204L44,215L45,226L50,234L53,236L56,235L58,234L59,235L76,235L76,234L72,226L77,200L77,194L86,156L96,159L102,159L111,157L116,155L122,175L121,184L126,211L127,227L124,235L131,235L133,234L134,235L138,235L140,234L140,236L148,234L148,232L151,235L156,235L156,232L153,224L153,215L152,207L146,185L141,176L139,170L137,148L134,138L129,128L125,125L121,125L119,120L111,112L107,110L110,105L116,99L118,96L119,74L116,71L117,61L110,34L107,25L101,16Z\",\"e\":[99,95,4],\"v\":\"0 0 200 240\"},\"18-quad-all--four-legged_robot_3d_model\":{\"d\":\"M96,29L91,34L86,42L81,46L52,148L50,157L49,170L48,171L46,169L44,169L40,171L35,176L33,182L29,180L26,185L19,198L15,209L12,221L13,231L16,236L33,236L37,232L38,232L40,230L44,222L46,213L46,226L49,234L52,234L53,236L68,236L71,232L76,225L78,210L86,211L85,224L89,233L93,236L110,236L114,233L116,228L117,219L117,208L123,208L124,221L125,228L127,232L133,236L147,236L151,234L154,227L154,217L157,227L165,236L183,236L186,233L188,227L187,215L183,202L173,182L172,181L168,181L165,175L162,171L156,167L153,168L148,171L147,170L147,157L144,144L141,135L139,134L113,45L111,42L109,41L100,30L98,29ZM50,185L52,191L46,210L47,195L45,192L44,192L48,189ZM147,182L148,182L153,189L156,192L153,192L152,194L153,209L147,202L145,202L148,197L147,190L147,188L146,185Z\",\"e\":[88,158,2],\"v\":\"0 0 200 240\"},\"18-quad-all--quadruped_robot_3d_model1\":{\"d\":\"M134,70L125,64L119,63L112,62L105,63L94,68L77,78L67,90L59,101L51,98L48,98L47,99L46,103L48,114L53,138L49,146L43,154L32,164L26,171L17,203L17,209L18,209L13,224L12,234L13,235L35,236L38,235L39,231L35,214L45,213L47,212L44,193L45,181L59,156L63,152L69,150L74,150L74,164L77,169L84,178L87,179L88,191L85,194L77,215L68,235L69,236L79,236L88,234L90,236L95,236L122,235L123,233L122,231L129,221L131,213L133,180L133,175L122,156L133,154L141,149L143,151L142,157L143,161L155,176L157,178L160,179L166,192L165,194L164,201L158,233L159,235L161,236L172,236L178,235L181,236L188,235L188,213L187,205L181,188L180,187L179,188L178,187L173,170L183,160L187,151L186,136L182,126L173,118L167,115L163,115L157,118L150,126L146,116L137,105L127,99L114,95L99,94L103,86L110,80L123,74L133,74L135,71ZM112,165L109,171L105,188L103,190L103,172Z\",\"e\":[130,133,5],\"v\":\"0 0 200 240\"},\"18-quad-all--quadruped_robot_3d_model\":{\"d\":\"M96,17L92,18L89,22L86,26L86,29L86,35L89,39L92,43L95,44L95,76L92,78L91,82L84,84L75,89L64,98L60,103L55,112L53,124L50,125L47,131L46,138L47,141L49,144L53,147L54,151L59,154L67,154L69,155L67,157L64,162L61,161L59,162L52,181L50,187L48,187L46,189L38,207L34,221L33,233L35,235L37,236L61,236L64,235L66,236L93,236L96,234L98,231L98,226L92,192L91,190L92,179L99,181L108,180L109,189L108,192L101,224L101,229L104,234L108,236L133,236L135,235L140,236L163,236L166,234L167,231L164,211L154,189L152,187L149,187L148,180L141,162L138,160L135,161L134,158L130,155L138,153L142,152L145,147L146,140L146,129L143,112L138,103L131,94L121,86L108,82L108,78L104,76L104,44L108,42L112,38L113,34L114,30L111,23L108,19L102,16ZM123,166L128,169L126,171L135,192L133,194L132,204L128,189L125,186ZM77,165L74,187L72,189L67,203L66,194L64,192L66,189L73,171L71,169Z\",\"e\":[101,121,9],\"v\":\"0 0 200 240\"},\"18-quad-all--robotic_dog_3d_model\":{\"d\":\"M79,59L81,106L79,107L56,89L55,89L65,120L61,128L59,134L56,134L54,129L51,127L45,125L40,128L35,135L34,139L34,149L28,174L29,181L28,198L26,205L24,214L21,215L16,221L13,226L12,234L14,235L25,233L28,231L30,232L34,232L36,234L42,233L45,232L46,229L53,231L56,231L58,233L61,232L63,226L63,221L61,218L56,213L55,213L52,215L44,209L45,186L48,182L51,157L56,154L67,156L68,165L74,175L84,187L91,191L93,190L92,169L88,168L86,166L94,168L99,171L101,171L106,168L107,168L107,188L106,190L107,191L114,188L119,183L126,175L131,166L133,156L143,155L149,159L152,169L157,194L157,207L152,211L149,218L148,218L146,216L143,216L140,219L137,227L138,235L139,236L141,235L144,230L146,235L152,234L154,236L157,236L160,233L167,233L172,236L173,235L174,231L176,232L180,232L186,236L188,235L188,233L185,225L182,220L178,218L173,209L172,190L171,186L172,177L168,166L166,149L165,137L159,130L154,128L151,128L147,130L144,134L141,134L139,127L135,121L147,89L145,89L121,107L111,103L100,102ZM119,164L116,167L110,167Z\",\"e\":[100,141,6],\"v\":\"0 0 200 240\"},\"18-quad-all--stylized_quadruped_3d_model\":{\"d\":\"M79,16L77,18L69,36L63,70L57,89L52,114L52,122L55,136L49,144L42,159L34,168L32,172L31,179L33,187L32,194L28,206L23,216L22,221L19,227L17,234L22,236L57,236L61,234L61,226L56,202L54,190L58,187L62,181L78,163L89,167L107,168L110,184L113,188L114,191L111,204L105,220L102,235L108,236L141,235L141,226L134,202L131,189L133,181L130,160L135,158L137,168L140,174L146,180L152,183L154,187L153,201L146,219L147,221L144,228L143,234L150,236L182,235L183,232L182,223L173,189L176,180L178,169L178,162L176,156L173,151L167,146L166,141L164,134L161,128L157,124L147,119L136,119L134,101L132,93L128,84L120,73L112,67L105,63L111,32L112,22L108,21Z\",\"e\":[100,118,8],\"v\":\"0 0 200 240\"},\"18-quad-all--wooden_four-legged_robot_3d_model\":{\"d\":\"M188,218L185,205L180,191L170,177L162,171L165,157L165,152L164,149L157,143L148,139L134,136L125,135L107,140L89,141L84,142L76,147L66,158L61,155L55,156L52,161L53,171L47,172L45,170L43,170L39,172L30,179L24,187L18,199L13,214L12,225L13,231L16,234L26,234L31,227L32,223L30,219L33,217L36,217L44,211L53,196L54,193L52,190L56,189L59,193L63,194L58,208L57,219L60,231L64,236L78,236L82,230L83,226L81,223L83,221L87,212L87,206L89,204L100,202L103,207L109,211L112,214L117,213L118,218L117,220L118,225L120,229L125,234L137,233L141,229L144,218L145,208L145,194L153,207L165,218L164,220L164,225L166,229L172,234L184,234L188,228Z\",\"e\":[124,185,3],\"v\":\"0 0 200 240\"},\"18-quad-all--wooden_robot_3d_model\":{\"d\":\"M100,48L95,51L91,57L88,64L86,75L86,82L88,89L91,92L97,92L97,93L95,99L80,103L72,99L63,99L60,99L55,102L50,108L50,114L52,120L56,128L54,139L54,146L56,156L53,155L50,155L44,159L34,169L24,184L18,197L13,211L12,223L13,225L14,230L17,233L19,234L36,233L40,231L54,209L67,182L66,177L69,173L77,180L76,193L76,207L79,224L83,234L84,235L89,236L105,235L108,230L111,218L111,192L111,182L112,181L122,179L133,172L135,174L134,176L135,182L140,198L146,211L154,224L164,234L175,236L185,235L187,232L188,224L187,214L184,201L175,181L168,170L158,158L152,155L147,157L143,156L147,144L146,136L143,127L147,121L148,111L148,107L139,99L135,98L130,98L118,102L113,100L113,98L112,91L115,89L117,82L116,70L113,60L109,54L102,48Z\",\"e\":[87,131,4],\"v\":\"0 0 200 240\"},\"19-genie-multi--fantasy_creature_3d_model\":{\"d\":\"M109,16L94,38L96,52L100,59L89,64L84,67L82,73L82,80L77,87L73,88L71,86L60,82L54,77L53,75L56,72L58,68L61,69L63,66L62,62L59,60L56,60L54,62L56,64L55,66L53,68L51,68L49,67L49,62L56,59L57,58L56,55L53,54L49,55L45,57L43,60L43,65L41,68L41,72L44,78L54,88L61,93L68,97L75,97L73,103L56,107L53,106L52,108L49,107L47,106L48,98L49,98L51,97L52,93L50,90L45,90L42,92L41,88L37,88L33,92L32,95L34,106L40,113L43,114L51,115L68,114L79,111L84,99L87,98L91,88L95,89L96,96L96,102L91,104L88,115L88,116L84,119L79,118L70,118L61,120L55,123L50,126L46,131L43,139L43,145L46,154L48,158L53,163L60,168L74,173L78,175L78,178L73,183L70,190L70,196L71,201L74,205L79,210L95,222L98,228L101,236L102,227L101,221L98,215L89,204L87,198L89,194L99,189L104,184L108,177L108,172L105,164L102,160L95,155L88,151L98,152L108,150L115,146L122,139L124,131L124,122L126,119L125,108L122,105L125,92L128,95L134,109L148,112L148,130L141,135L137,141L138,143L140,145L145,140L146,141L143,147L140,146L137,150L138,152L140,154L149,153L151,149L154,148L158,135L157,132L157,112L162,109L166,104L167,99L169,95L168,90L165,87L163,87L160,89L163,93L162,96L160,97L159,90L160,89L160,87L157,85L153,86L151,89L150,91L153,95L156,95L156,104L154,104L144,92L152,85L157,78L160,77L163,70L164,64L162,60L162,56L160,53L158,51L154,50L150,50L149,52L149,53L151,56L153,56L155,57L155,61L153,63L150,62L149,60L150,59L150,57L149,55L146,55L143,56L142,59L143,63L145,65L146,63L152,70L152,72L152,73L147,78L139,83L135,75L135,70L132,64L129,62L125,61L118,59L122,51L124,38ZM42,92L42,95L44,96L43,99L40,102L38,97L41,94Z\",\"e\":[111,77,6],\"v\":\"0 0 200 240\"},\"19-genie-multi--multi-armed_humanoid_3d_model1\":{\"d\":\"M127,16L122,18L111,31L107,30L101,30L94,24L84,21L87,38L90,44L88,47L88,50L91,56L96,61L99,67L97,71L93,71L86,72L85,74L84,78L66,79L66,74L60,68L56,56L60,52L62,46L61,44L58,43L52,44L50,46L49,51L52,56L53,64L53,77L54,80L56,82L60,85L67,83L83,86L79,92L75,93L64,102L60,101L55,102L47,94L47,89L46,87L44,85L39,85L36,86L35,91L24,102L23,114L26,120L27,119L27,115L29,120L30,120L31,118L31,111L33,107L34,112L37,113L36,110L39,106L38,102L41,95L46,101L50,110L54,113L59,114L63,112L66,107L81,101L86,95L90,92L92,98L90,100L88,104L85,106L83,109L70,118L68,117L64,118L62,121L59,127L48,139L44,146L45,150L43,160L45,166L50,172L54,173L58,172L58,171L61,170L60,168L56,165L54,163L54,159L55,158L57,161L61,163L62,162L58,157L55,145L57,140L63,132L70,129L73,125L73,122L82,117L89,115L94,108L95,113L96,120L89,130L89,132L86,137L83,146L81,155L81,173L72,182L69,187L68,193L68,199L67,206L64,212L59,219L59,225L57,226L56,224L53,223L49,225L43,230L42,233L43,236L47,233L48,234L50,233L54,234L57,232L63,231L65,236L68,233L74,235L75,226L74,223L73,211L77,203L85,197L88,192L89,177L94,168L100,150L102,151L106,150L113,176L112,186L113,193L116,198L121,203L123,211L124,217L123,224L121,226L118,224L114,225L112,232L112,234L113,235L116,233L117,234L122,234L124,233L129,232L134,236L135,235L137,235L139,236L140,235L143,235L147,234L152,236L152,234L150,230L145,226L142,225L139,226L137,225L138,222L132,213L130,199L130,190L126,179L122,174L121,144L116,128L111,120L113,106L114,106L118,114L135,121L136,127L143,130L151,143L141,162L143,163L149,158L150,158L149,162L148,164L142,168L144,170L146,170L147,172L150,172L154,170L157,167L161,160L160,150L161,148L160,143L149,127L145,119L141,117L138,118L125,109L121,104L118,97L115,96L118,91L119,91L126,98L142,105L143,109L146,111L150,111L155,109L160,99L159,106L161,105L166,101L168,104L169,111L173,111L176,108L177,102L176,97L172,89L171,86L167,83L163,83L160,85L159,88L160,91L153,99L147,99L145,100L133,92L129,90L124,85L143,82L145,83L149,83L153,79L155,76L157,60L162,49L161,44L159,41L153,41L150,43L151,48L154,53L151,63L143,72L142,77L123,77L122,73L121,72L110,71L109,68L113,64L120,52L121,48L120,45L127,27L128,23Z\",\"e\":[105,46,5],\"v\":\"0 0 200 240\"},\"19-genie-multi--multi-armed_humanoid_3d_model\":{\"d\":\"M164,31L162,31L156,36L153,39L152,44L148,48L148,56L149,59L153,63L142,82L122,80L119,76L117,75L105,73L105,71L111,68L119,58L120,54L120,48L123,36L122,33L115,35L110,31L106,16L98,31L92,33L86,28L84,29L84,37L85,45L83,49L83,54L86,60L95,70L95,74L91,74L82,76L77,79L74,83L57,86L48,70L53,63L53,55L49,51L46,44L40,39L38,39L39,49L34,55L32,51L31,52L30,59L33,68L37,72L37,75L40,83L44,92L48,97L52,98L76,93L77,93L78,96L65,106L61,109L49,115L45,113L43,110L41,108L35,107L32,109L30,111L33,113L33,117L32,119L32,122L28,119L27,119L26,122L28,125L33,130L36,132L46,132L57,127L69,117L76,111L81,107L83,109L84,115L83,118L66,132L54,153L46,155L40,160L37,167L37,173L40,178L45,182L47,182L47,179L48,175L46,174L46,172L48,169L52,167L54,168L55,171L51,177L51,178L52,179L56,179L61,176L63,173L65,168L65,161L72,146L74,138L82,134L84,138L76,171L73,177L72,189L68,207L65,211L55,214L51,218L48,222L43,234L44,236L80,235L81,225L81,219L78,211L85,188L88,182L89,175L99,152L104,151L111,174L111,181L115,187L120,211L115,222L115,227L115,235L120,236L149,236L150,235L150,228L146,218L141,214L133,211L130,207L126,183L126,177L123,167L120,146L117,136L118,136L123,140L126,145L137,159L137,168L139,172L142,175L145,177L151,177L151,175L148,170L149,167L153,166L156,168L157,171L157,174L155,177L155,180L156,181L163,176L165,172L165,163L163,159L157,153L145,151L133,134L129,131L124,123L118,118L118,112L119,107L123,109L141,125L154,130L160,130L164,129L172,122L174,118L173,115L171,115L169,112L165,110L168,108L169,105L164,104L159,105L156,106L148,113L136,106L127,99L120,95L121,91L136,93L149,93L154,88L159,79L163,65L167,62L170,56L171,51L170,45L168,44L166,48L165,44L162,41L164,32Z\",\"e\":[100,44,4],\"v\":\"0 0 200 240\"},\"19-genie-multi--stylized_octopus_3d_model\":{\"d\":\"M42,16L36,21L31,27L28,35L29,40L32,48L39,54L47,57L53,58L57,57L62,54L72,43L75,42L78,44L80,47L80,55L72,70L71,75L71,84L72,90L76,96L82,102L89,106L90,113L84,114L79,116L67,114L55,116L52,116L47,113L41,107L38,101L38,95L39,90L41,86L47,79L48,76L42,77L34,81L28,86L24,94L22,102L23,114L27,122L34,128L41,131L52,132L65,130L72,132L74,135L64,142L52,148L43,150L35,153L29,159L25,163L22,173L21,180L23,188L29,196L35,199L41,200L47,197L51,191L51,186L50,183L49,182L47,188L46,190L43,191L41,189L39,183L39,179L41,172L51,162L63,159L70,154L73,150L76,147L79,164L78,172L78,180L79,185L83,193L88,199L94,204L117,213L121,218L123,224L123,229L120,235L123,236L127,236L133,231L136,226L137,218L136,210L133,205L127,198L121,194L113,187L109,177L109,170L117,153L119,141L123,142L127,144L131,144L131,147L136,149L148,151L153,155L158,159L161,163L162,167L160,175L158,177L155,176L153,173L152,167L150,169L149,172L151,179L153,182L158,185L162,186L165,185L172,181L176,176L179,169L179,164L177,155L172,147L166,142L158,138L140,136L122,130L123,126L129,123L150,118L154,116L158,112L162,106L164,99L164,93L162,86L158,78L150,70L143,67L140,66L135,67L134,68L139,72L142,76L146,86L147,91L143,100L138,105L127,107L121,109L116,114L112,113L105,113L104,112L105,104L112,100L117,95L120,87L121,78L120,72L117,67L112,57L104,49L103,43L99,35L93,29L87,24L79,21L72,21L66,23L60,28L53,40L50,42L47,42L42,39L40,35L39,27L44,16Z\",\"e\":[91,45,3],\"v\":\"0 0 200 240\"},\"20-lume--robotic_figure_3d_model\":{\"d\":\"M55,25L49,28L46,31L44,35L45,38L50,43L56,47L69,53L82,57L87,61L93,67L93,76L86,79L84,81L80,86L79,93L73,98L71,97L70,99L62,114L51,127L46,137L41,151L36,157L32,163L31,167L32,175L36,180L40,182L46,182L52,177L54,172L54,163L52,155L62,146L66,140L74,113L76,109L80,105L84,120L87,138L94,159L91,159L89,162L85,174L77,191L76,205L69,219L67,224L54,230L52,232L52,235L54,236L78,236L80,235L84,218L88,210L93,203L93,186L97,167L100,170L102,170L106,167L110,180L114,202L124,216L131,235L133,236L156,236L157,234L156,230L153,228L144,224L140,218L132,202L129,189L123,180L117,168L116,168L116,165L113,159L110,160L108,156L109,145L116,121L119,105L127,111L131,126L133,137L137,144L148,156L146,172L146,175L150,180L157,183L164,181L169,175L169,168L161,153L159,150L155,138L151,128L141,116L133,101L132,100L129,100L119,93L119,88L116,83L113,79L105,76L105,66L108,63L117,59L133,55L143,50L151,45L153,41L153,39L146,32L139,27L125,21L109,17L101,16L86,17L68,20ZM155,160L160,170L160,174L157,177L153,175L152,171ZM46,158L48,170L45,175L43,175L40,173L39,168L42,161Z\",\"e\":[99,42,6],\"v\":\"0 0 200 240\"},\"21-flyer--winged_humanoid_3d_model\":{\"d\":\"M109,16L107,19L95,42L87,54L85,59L85,65L86,69L90,75L96,80L96,85L85,88L78,81L71,66L65,42L63,43L55,52L39,79L31,97L24,123L22,141L23,161L27,179L30,188L32,190L42,161L49,148L49,157L52,165L54,167L62,169L67,168L69,166L69,163L65,161L63,159L62,152L63,151L65,151L67,156L69,158L72,157L73,154L70,147L61,139L61,138L64,129L83,112L86,126L86,129L82,139L83,174L83,184L77,216L72,222L71,225L67,228L65,233L66,236L74,233L76,233L77,236L80,234L86,234L89,235L93,230L94,224L91,215L94,186L93,179L98,148L103,148L107,179L109,216L107,223L107,229L112,235L115,234L121,234L122,236L124,236L126,233L133,236L135,235L135,232L134,228L129,224L129,222L124,216L117,180L119,140L115,130L115,123L118,112L136,129L139,137L139,139L131,146L128,151L128,155L130,158L131,158L134,156L136,151L138,152L138,159L136,161L133,162L132,163L132,166L134,168L139,169L144,168L147,166L152,157L152,149L159,163L168,190L169,189L172,184L176,172L178,157L177,135L175,117L170,100L164,83L156,68L145,52L138,44L136,44L129,67L122,81L116,88L108,86L105,85L105,82L111,77L117,70L119,61L114,41Z\",\"e\":[62,98,7],\"v\":\"0 0 200 240\"},\"22-curve--stylized_alien_3d_model\":{\"d\":\"M83,17L79,21L73,28L70,38L71,46L74,54L79,59L87,64L98,67L98,70L89,73L81,78L71,100L69,103L58,112L44,130L39,132L32,137L27,145L27,151L27,157L29,161L31,162L35,172L41,177L45,178L49,179L54,177L57,174L57,172L56,171L49,170L45,168L43,163L43,160L46,154L47,151L51,149L53,146L57,149L58,156L59,160L62,160L64,159L65,155L64,146L62,140L59,136L63,130L74,116L76,107L88,90L89,89L95,102L96,109L94,112L88,120L79,140L79,160L81,174L81,179L78,186L74,205L58,229L56,234L58,236L61,236L85,236L91,234L91,230L85,219L85,206L90,186L99,167L103,149L107,150L107,177L103,189L102,204L97,230L97,234L101,236L135,236L137,234L135,232L121,221L117,216L115,211L114,205L117,184L127,164L131,152L132,144L132,137L129,128L125,120L117,111L116,108L117,102L121,97L122,94L132,108L133,115L137,121L150,136L150,141L152,146L153,161L157,165L161,164L160,167L156,168L154,169L155,172L158,173L155,176L154,178L157,180L161,179L165,177L168,174L172,166L174,157L166,138L151,113L146,108L141,105L137,100L131,87L127,78L124,75L119,73L111,71L111,67L112,67L114,65L122,64L128,61L133,57L138,51L141,45L142,38L142,31L138,23L135,19L132,17L130,26L127,32L125,34L118,38L113,40L102,40L93,38L88,34L86,26L87,16ZM158,153L159,155L158,158Z\",\"e\":[99,53,4],\"v\":\"0 0 200 240\"},\"22-curve--stylized_cartoon_figure_3d_model\":{\"d\":\"M68,19L61,22L55,30L54,35L55,42L51,44L46,51L45,54L46,62L42,65L40,69L39,73L38,78L40,83L44,88L42,94L42,98L45,104L51,110L55,113L56,117L58,121L60,124L65,126L69,126L69,128L72,129L66,131L63,133L61,140L54,145L51,150L49,155L50,158L46,160L41,166L37,177L38,183L35,193L35,199L39,205L45,209L52,207L54,205L54,202L50,196L51,194L52,194L56,198L59,198L61,196L62,192L60,186L62,181L64,168L63,164L65,163L71,153L70,166L72,172L69,177L66,188L66,193L68,197L64,205L61,218L56,223L53,228L52,231L52,234L57,236L90,236L92,234L93,231L93,225L91,221L92,217L93,211L91,201L89,198L92,191L92,185L102,185L108,184L109,194L111,198L108,209L107,217L109,221L107,222L106,227L106,233L108,235L109,236L145,236L147,232L147,229L146,225L138,218L135,203L132,197L134,194L134,189L132,178L129,172L131,165L129,154L133,160L137,164L136,167L138,180L140,185L138,190L139,196L142,198L144,198L147,196L148,194L150,196L147,201L146,204L147,206L154,209L157,209L160,207L163,204L165,197L165,191L163,183L163,178L162,171L156,161L151,158L152,156L151,153L147,147L143,142L139,140L138,135L134,131L141,126L143,118L151,111L152,108L153,100L151,95L155,91L157,88L158,83L157,76L152,71L156,65L155,59L151,53L144,49L144,45L141,39L134,34L129,33L127,28L119,22L101,21L98,20L81,16L74,17Z\",\"e\":[98,71,9],\"v\":\"0 0 200 240\"},\"23-blob-texture-bodies--blob_creature_3d_model\":{\"d\":\"M126,31L117,27L107,26L92,28L86,30L75,39L72,42L69,50L62,53L49,65L41,79L35,80L29,83L23,90L20,96L20,101L20,107L22,111L27,118L36,123L32,128L29,133L27,141L27,148L30,158L39,167L36,171L33,172L30,175L22,189L14,209L12,221L13,228L14,232L18,236L46,236L51,232L57,223L61,210L64,193L65,181L67,180L70,177L71,173L77,172L80,179L85,186L94,192L101,194L110,194L118,191L126,186L129,182L132,176L138,176L140,179L141,199L143,211L148,225L151,229L154,234L157,236L175,236L180,231L182,225L183,220L183,206L178,186L171,172L176,162L177,155L177,150L175,146L170,138L178,130L186,113L188,107L188,98L186,89L184,81L181,74L174,66L168,62L149,56L143,54L141,46L137,40L133,36Z\",\"e\":[104,81,9],\"v\":\"0 0 200 240\"},\"23-blob-texture-bodies--cute_blob_creature_3d_model\":{\"d\":\"M49,78L37,91L25,106L17,123L12,142L13,158L18,176L21,180L26,185L24,187L24,191L28,200L20,210L19,214L19,218L21,223L25,226L29,227L29,232L34,236L183,236L186,235L188,230L187,225L180,219L157,208L156,208L155,206L151,204L148,192L144,183L140,179L130,172L133,164L132,152L129,140L121,121L129,117L133,114L137,107L138,103L136,97L134,93L129,90L122,89L124,79L123,73L121,69L115,63L108,60L97,57L91,57L80,60L69,64Z\",\"e\":[92,68,3],\"v\":\"0 0 200 240\"},\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\":{\"d\":\"M82,34L71,40L63,47L56,55L52,66L50,77L51,89L54,98L61,110L61,113L62,113L62,116L60,118L50,123L48,125L42,128L36,133L22,150L16,158L13,166L12,176L26,176L27,173L32,172L35,177L39,177L42,172L41,166L43,161L50,154L55,151L51,161L50,168L55,207L53,214L44,225L43,230L43,235L52,236L85,236L86,235L88,232L89,214L92,203L94,201L103,200L106,201L109,206L112,217L112,229L114,236L156,236L157,232L157,228L149,218L146,211L145,201L148,184L149,167L145,150L152,155L158,164L158,174L160,177L165,177L167,172L172,173L174,176L188,176L188,169L186,162L177,148L162,132L153,124L140,119L138,116L138,113L146,97L150,83L149,72L147,63L143,55L139,49L133,43L117,34L111,32L98,31Z\",\"e\":[100,72,9],\"v\":\"0 0 200 240\"},\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\":{\"d\":\"M95,16L84,20L81,21L74,27L69,35L66,44L65,54L66,58L72,70L77,76L79,77L77,79L70,82L57,93L46,112L45,114L43,117L35,140L35,147L33,156L34,164L38,171L43,173L48,172L48,164L50,163L49,160L50,159L51,162L54,164L56,164L58,162L58,156L56,149L66,122L65,130L65,145L61,162L59,184L58,192L57,210L55,214L53,216L48,224L49,232L53,234L73,234L83,234L86,232L88,227L87,214L90,189L95,169L105,169L110,188L110,198L113,212L113,226L114,232L117,233L137,234L140,236L141,236L138,234L149,234L152,232L153,227L153,223L151,219L145,213L143,208L139,162L136,147L136,136L135,125L139,132L139,134L145,150L142,155L142,160L143,163L145,164L149,162L150,159L151,160L152,172L157,173L160,172L165,168L167,162L166,144L160,123L151,104L147,99L147,98L139,88L122,77L131,66L134,57L135,46L134,40L130,33L124,25L114,19L106,16Z\",\"e\":[100,46,9],\"v\":\"0 0 200 240\"},\"23-blob-texture-bodies--sand_creature_3d_model\":{\"d\":\"M119,60L106,56L92,56L83,59L76,62L65,70L60,76L54,91L52,102L52,121L50,127L48,132L34,144L25,155L17,169L13,178L12,185L14,189L18,190L20,189L21,191L24,192L28,192L31,189L32,184L35,187L39,188L41,187L43,182L42,175L44,169L46,168L46,185L48,192L52,201L53,212L52,218L49,225L49,228L50,232L54,235L63,236L74,236L83,233L86,229L87,216L88,214L90,212L111,212L114,216L115,230L118,234L123,235L141,236L146,235L149,232L151,228L151,224L147,216L147,208L154,186L154,168L156,169L158,175L157,182L158,186L161,188L164,188L168,184L170,190L172,192L175,192L179,191L180,189L184,190L188,186L188,181L184,171L175,156L168,146L154,133L150,128L148,124L147,96L144,85L140,78L134,70L128,65Z\",\"e\":[102,67,3],\"v\":\"0 0 200 240\"}};\r\nexport const COLOURS={\"default-slate\":\"#8b8f9a\",\"default-clay\":\"#b7a68e\",\"default-ruby\":\"#a23b4a\",\"default-sapphire\":\"#2d5aa0\",\"default-moss\":\"#4c7a3f\",\"default-gold\":\"#c9a13a\",\"default-charcoal\":\"#333238\",\"default-blush\":\"#d98fa0\",\"legacy-color-0\":\"#7946aa\",\"legacy-color-1\":\"#3f7448\",\"legacy-color-2\":\"#d5c7ae\",\"legacy-color-3\":\"#4f263d\",\"legacy-color-4\":\"#5f7a3f\",\"legacy-color-5\":\"#132a42\",\"legacy-color-6\":\"#e46867\",\"legacy-color-7\":\"#d8d0af\",\"legacy-color-8\":\"#8ed9cd\",\"legacy-color-9\":\"#7b211e\",\"legacy-color-10\":\"#e6dca4\",\"legacy-color-11\":\"#16101f\",\"legacy-color-12\":\"#6b426c\",\"legacy-color-13\":\"#6f7068\",\"legacy-color-14\":\"#896bc2\",\"legacy-color-15\":\"#4a1713\",\"legacy-color-16\":\"#93c9df\",\"legacy-color-17\":\"#48536e\",\"legacy-color-18\":\"#9a6a37\",\"legacy-color-19\":\"#251d54\",\"legacy-color-20\":\"#d6a987\",\"legacy-color-21\":\"#a7ec69\",\"legacy-color-22\":\"#efb398\",\"pal-01\":\"#C6FF00\",\"pal-02\":\"#FF6D00\",\"pal-03\":\"#0E5A61\",\"pal-04\":\"#8A3B12\",\"pal-05\":\"#FF009F\",\"pal-06\":\"#13E7D1\",\"pal-07\":\"#B5C700\",\"pal-08\":\"#FFB000\",\"pal-09\":\"#A6FF00\",\"pal-10\":\"#AEB6BC\",\"pal-11\":\"#7B2FF7\",\"pal-12\":\"#FA2200\",\"pal-13\":\"#8C6A2F\",\"pal-14\":\"#FF00AA\",\"pal-15\":\"#A0342B\",\"pal-16\":\"#046A5B\",\"pal-17\":\"#B8C0C2\",\"pal-18\":\"#D9A404\",\"pal-19\":\"#FF1744\",\"pal-20\":\"#13E7B9\",\"pal-21\":\"#9C4221\",\"pal-22\":\"#F5E900\",\"pal-23\":\"#8CFF00\",\"pal-24\":\"#B91338\",\"pal-25\":\"#C59B42\",\"pal-26\":\"#FF0000\",\"pal-27\":\"#D6F0F0\",\"pal-28\":\"#3F7A3A\",\"pal-29\":\"#F2F2F0\",\"pal-30\":\"#FFD300\",\"pal-31\":\"#FFEA00\",\"pal-32\":\"#8D9096\",\"pal-33\":\"#C6FF00\",\"pal-34\":\"#B46A30\",\"pal-35\":\"#00E5FF\",\"pal-36\":\"#EDE8DC\",\"pal-37\":\"#4A1E4F\",\"pal-38\":\"#BFFF00\",\"pal-39\":\"#C06B55\",\"pal-40\":\"#F4EDDE\",\"pal-41\":\"#191970\",\"pal-42\":\"#A27E8E\",\"pal-43\":\"#FF9100\",\"pal-44\":\"#4F7942\",\"pal-45\":\"#7D7F87\",\"pal-46\":\"#D32E12\",\"pal-47\":\"#139FE7\",\"pal-48\":\"#00FFC6\",\"pal-49\":\"#00FF9D\",\"pal-50\":\"#9BA0A6\",\"pal-51\":\"#B22234\",\"pal-52\":\"#13A6E7\",\"pal-53\":\"#3A7D7B\",\"pal-54\":\"#635147\",\"pal-55\":\"#E7134B\",\"pal-56\":\"#E3D200\",\"pal-57\":\"#FF5E00\",\"pal-58\":\"#FFA12B\",\"pal-59\":\"#8A8F98\",\"pal-60\":\"#1E2A4A\",\"pal-61\":\"#F28500\",\"pal-62\":\"#9AB5A1\",\"pal-63\":\"#1D8A7A\",\"pal-64\":\"#701F3C\",\"pal-65\":\"#2E3033\",\"pal-66\":\"#39FF14\",\"pal-67\":\"#8A3324\",\"pal-68\":\"#1399E7\",\"pal-69\":\"#E79813\",\"pal-70\":\"#6A2D9E\",\"pal-71\":\"#A89B6E\",\"pal-72\":\"#00F5D4\",\"pal-73\":\"#7A4532\",\"pal-74\":\"#13AAE7\",\"pal-75\":\"#5B7042\",\"pal-76\":\"#99A0A8\",\"pal-77\":\"#00F0FF\",\"pal-78\":\"#3A2618\",\"pal-79\":\"#1325E7\",\"pal-80\":\"#57A78A\",\"pal-81\":\"#8C00FF\",\"pal-82\":\"#00FFFF\",\"pal-83\":\"#F0EEE4\",\"pal-84\":\"#FF073A\",\"pal-85\":\"#6D1E2E\",\"pal-86\":\"#C3C9D1\",\"pal-87\":\"#B09A6E\",\"pal-88\":\"#004DFF\",\"pal-89\":\"#23493C\",\"pal-90\":\"#9813E7\",\"pal-91\":\"#F0B429\",\"pal-92\":\"#574B6B\",\"pal-93\":\"#0F7B7B\",\"pal-94\":\"#E63362\",\"pal-95\":\"#23232A\",\"pal-96\":\"#F5F1E8\",\"pal-97\":\"#9FBF3B\",\"pal-98\":\"#41284E\",\"pal-99\":\"#8F8A82\",\"pal-100\":\"#FFEF00\",\"pal-101\":\"#E9F2F5\",\"pal-102\":\"#D8117E\",\"pal-103\":\"#8467B8\",\"pal-104\":\"#00FF66\",\"pal-105\":\"#FF2E00\",\"pal-106\":\"#C9AE7E\",\"pal-107\":\"#523090\",\"opal-jelly\":\"#b3fff0\",\"bubble-glass\":\"#58eaff\",\"prism-crystal\":\"#b18aff\",\"holo-foil\":\"#ff54de\",\"glitter-resin\":\"#ff319c\",\"galaxy-geode\":\"#21134e\",\"caustic-slime\":\"#8aa52b\",\"blister-hide\":\"#9e435f\",\"rotten-rind\":\"#626124\",\"parasite-nest\":\"#786541\",\"exposed-sinew\":\"#b72945\",\"abyssal-maw\":\"#522045\",\"circuit-alloy\":\"#273f50\",\"servo-armor\":\"#617080\",\"chrome-rib\":\"#bfcde1\",\"carbon-mech\":\"#323d46\",\"hazard-panel\":\"#edb932\",\"reactor-glass\":\"#48e8ff\"};\r\nexport const LEGACY=[\"#7946aa\",\"#3f7448\",\"#d5c7ae\",\"#4f263d\",\"#5f7a3f\",\"#132a42\",\"#e46867\",\"#d8d0af\",\"#8ed9cd\",\"#7b211e\",\"#e6dca4\",\"#16101f\",\"#6b426c\",\"#6f7068\",\"#896bc2\",\"#4a1713\",\"#93c9df\",\"#48536e\",\"#9a6a37\",\"#251d54\",\"#d6a987\",\"#a7ec69\",\"#efb398\"];\r\n","arcade/pyramid-jump/game.mjs":"import {gameShellPhase} from '../shell-phase.mjs';\r\n// Pyramid Jump: classic Doodle-Jump on 2D paper, 20 levels from the office to MOM's mothership desk (see LEVELS in logic.mjs).\r\n// Lazy/optional (arcade/ is never core). World y points up (feet = y); the camera only scrolls up; falling below the view ends the run.\r\nimport {W,BEST_KEY,PLAT_W,JUMP,GRAVITY,Level,mulberry,mealMarkers,markerHeight,paperFor,LEVELS,LEVEL_H,LEVEL_COUNT,END_Y,levelAt,highestLevel,saveLevel,newlyReachedQuestLevel,steeringForMotion} from './logic.mjs';\r\nimport {performanceOwner} from '../../performance-progress.mjs';\r\nimport {createQuestProgressReporter} from '../quest-progress-reporter.mjs';\r\nconst PW=40,PH=50;\r\nconst hash=n=>{const x=Math.sin(n*127.1)*43758.5453;return x-Math.floor(x);};\r\nexport const bestScore=(store=localStorage)=>{try{const n=JSON.parse(store.getItem(BEST_KEY));return Number.isSafeInteger(n)&&n>0?n:0;}catch{return 0;}};\r\nconst SHADOW='#49334b66',EDGE='#624848';\r\nconst SLOGANS=[['CARED FOR.','CORRECTED.','PROVIDED FOR.'],['SMILE.','REP.','REPEAT.'],['READ THE','SOURCE.'],['ASK MOM.'],['KEEP THE','LABEL.'],['MOM IS','PROUD.'],['EAT. LIFT.','REPORT.'],['FITNESS IS','A FAMILY.']];\r\nconst POSTER=['#e5d4a2','#e0c3ea','#f2c6a0','#bfdcc6','#f0e08a'],QUILT=['#c8587a','#e0a050','#5a9ac8','#7ab86a','#a070c0','#e8d070'],ORIGAMI=['#f4a0b4','#a0d4f4','#f4e090','#b0e0a8','#d0b0f0'];\r\n\r\nexport function startGame(root,{recipe=null,meals=null,subscribeMotion=null,onExit=()=>{},seed=Date.now(),startLevel=0,auto=false}={}){\r\n const shell=gameShellPhase('pyramid-jump',root);\r\n const canvas=document.createElement('canvas'),ctx=canvas.getContext('2d');canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;touch-action:none;display:block';\r\n canvas.setAttribute('role','img');canvas.setAttribute('aria-label','Pyramid Jump. Tilt or touch the left and right sides to steer.');\r\n const over=document.createElement('div');over.style.cssText='position:absolute;inset:0;display:none;place-content:center;gap:12px;text-align:center;background:#0009;color:#fff4dc;font:800 18px/1.4 system-ui,sans-serif;letter-spacing:.05em;white-space:pre-line';\r\n const btn=(t,f)=>{const b=document.createElement('button');b.type='button';b.textContent=t;b.style.cssText='min-height:44px;padding:0 18px;border:2px solid #fff9;border-radius:999px;background:#1d1428;color:#fff4dc;font:700 14px system-ui,sans-serif;pointer-events:auto';b.onclick=f;return b;};\r\n const msg=document.createElement('div'),row=document.createElement('div');row.style.cssText='display:flex;flex-wrap:wrap;gap:10px;justify-content:center';\r\n const exit=btn('EXIT',()=>onExit());exit.style.cssText+=';position:absolute;top:max(10px,env(safe-area-inset-top));right:10px;min-height:44px;min-width:44px';exit.setAttribute('aria-label','Exit to the pyramid');\r\n over.append(msg,row);root.append(canvas,exit,over);\r\n const art=paperFor(recipe),shape=new Path2D(art.d),tags=mealMarkers(meals);\r\n let lvl,p,camY,maxY,best=bestScore(),t=0,dir=0,tilt=0,tiltAt=-9,raf=0,last=0,dead=true,disposed=false,scale=1,viewH=640,plus=0,cur=0,banner=null,won=false,runOwner=null,reportQuest=null;\r\n const showOver=(text,...btns)=>{msg.textContent=text;row.replaceChildren(...btns);over.style.display='grid';};\r\n function begin(from){ // from = 1-based level to start on (1 = the office desk)\r\n  runOwner=performanceOwner();\r\n  reportQuest=createQuestProgressReporter(runOwner);\r\n  lvl=new Level(mulberry(seed++),plus);p={x:W/2,y:0,vx:0,vy:JUMP,fly:0,flyV:0,gear:null,face:1};camY=-90;maxY=0;dead=false;won=false;over.style.display='none';\r\n  if(from>1){const y0=(from-1)*LEVEL_H+20;lvl.ensure(y0+300);const it=lvl.items.find(i=>i.type==='static'&&!i.blink&&i.y>=y0);p.x=it.x+PLAT_W/2;p.y=it.y;camY=p.y-viewH*.45;maxY=p.y;}\r\n  lvl.ensure(camY+viewH*2);for(const i of lvl.items)Object.assign(i,{broken:false,by:0});cur=Math.max(0,Math.min(LEVEL_COUNT-1,from-1));\r\n }\r\n const menu=()=>{const hi=highestLevel(),b=[btn(hi>1?'NEW CLIMB':'START',()=>begin(1))];if(hi>1)b.unshift(btn(`CONTINUE FROM LEVEL ${hi}`,()=>begin(hi)));b.push(btn('EXIT',()=>onExit()));return b;};\r\n function resize(){const r=canvas.getBoundingClientRect(),d=Math.min(devicePixelRatio||1,2);canvas.width=Math.max(1,r.width*d);canvas.height=Math.max(1,r.height*d);scale=canvas.width/W;viewH=canvas.height/scale;}\r\n const ro=new ResizeObserver(resize);ro.observe(canvas);resize();\r\n if(startLevel)begin(startLevel);else if(auto)begin(Math.max(1,highestLevel()));else if(highestLevel()>1)showOver(`PYRAMID JUMP\\nREACHED LEVEL ${highestLevel()} OF ${LEVEL_COUNT}`,...menu());else begin(1);\r\n const unsub=subscribeMotion?.(sample=>{tilt=steeringForMotion(sample);tiltAt=t;});\r\n const key=down=>e=>{const k=e.key;if(k==='ArrowLeft'||k==='a')dir=down?-1:dir<0?0:dir;else if(k==='ArrowRight'||k==='d')dir=down?1:dir>0?0:dir;};\r\n const kd=key(true),ku=key(false);addEventListener('keydown',kd);addEventListener('keyup',ku);\r\n const pd=e=>{if(e.target!==canvas)return;const r=canvas.getBoundingClientRect();dir=e.clientX<r.left+r.width/2?-1:1;},pu=()=>{dir=0;};\r\n canvas.addEventListener('pointerdown',pd);addEventListener('pointerup',pu);addEventListener('pointercancel',pu);\r\n const px=it=>it.type==='moving'?Math.max(0,Math.min(W-PLAT_W,it.x+Math.sin(t*1.6+it.phase)*it.dx)):it.x;\r\n const solid=it=>!it.blink||Math.sin(t*6.28/it.blink+it.phase)>-.3; // origami / star platforms fold away on a timer\r\n function die(why){if(dead)return;dead=true;const s=Math.floor(maxY/10);if(s>best){best=s;try{localStorage.setItem(BEST_KEY,String(best));}catch{/* ignore */}}\r\n  showOver(`${why}\\nLEVEL ${cur+1} · HEIGHT ${s}\\nBEST ${best}${s>=best&&s>0?'  NEW BEST!':''}`,...menu());}\r\n function win(){dead=won=true;saveLevel(LEVEL_COUNT);reportQuest?.record('pyramid-complete',1);showOver(`LEVEL ${LEVEL_COUNT} COMPLETE\\nYou ate your way to MOM.`,btn('NEW GAME+',()=>{plus++;begin(1);}),btn('EXIT',()=>onExit()));}\r\n function step(dt){\r\n  t+=dt;if(dead)return;\r\n  const L=levelAt(p.y),steer=dir||(t-tiltAt<.5?tilt:0);p.vx=steer*340;if(steer)p.face=steer>0?1:-1;\r\n  const prev=p.y;p.x+=p.vx*dt;if(p.x<-PW/2)p.x+=W+PW;else if(p.x>W+PW/2)p.x-=W+PW;\r\n  if(p.fly>0){p.fly-=dt;p.vy=p.flyV;if(p.fly<=0)p.vy=JUMP*.6;}else p.vy-=GRAVITY*(L===8?.7:1)*dt; // moon cafeteria: floatier\r\n  p.y+=p.vy*dt;lvl.ensure(camY+viewH*2);\r\n  for(const it of lvl.items){\r\n   if(it.broken){it.by-=420*dt;continue;}\r\n   if(it.type==='monster'){it.x+=it.dir*50*dt;if(it.x<0||it.x>W-50)it.dir*=-1;const dx=Math.abs(p.x-(it.x+25)),dy=p.y+PH/2-(it.y+16);\r\n    if(dx<PW/2+18&&Math.abs(dy)<PH/2+14){if(p.fly>0||(p.vy<0&&prev>it.y+14)){it.broken=true;p.vy=JUMP;p.fly=0;}else die('BONKED');}continue;}\r\n   if(it.type==='hole'){if(Math.hypot(p.x-(it.x+30),p.y+PH/2-(it.y+30))<30)die('SUCKED IN');continue;}\r\n   const x=it.type==='desk'?0:px(it),pw=it.type==='desk'?W:PLAT_W,near=Math.abs(p.x-(x+pw/2))<pw/2+PW/3;\r\n   if(it.gear&&p.fly<=0&&near&&Math.abs(p.y-(it.y+14))<26){p.fly=it.gear==='jetpack'?4:2.5;p.flyV=it.gear==='jetpack'?900:560;p.gear=it.gear;it.gear=null;}\r\n   if(p.vy<0&&p.fly<=0&&near&&prev>=it.y-2&&p.y<=it.y&&solid(it)){\r\n    if(it.type==='desk'){p.y=it.y;win();return;}\r\n    if(it.type==='breaking'){it.broken=true;continue;}\r\n    p.y=it.y;p.vy=it.type==='spring'?JUMP*1.55:JUMP;\r\n   }\r\n  }\r\n  maxY=Math.max(maxY,p.y);camY=Math.max(camY,p.y-viewH*.45);\r\n  const nl=levelAt(maxY);if(nl!==cur){const crossed=newlyReachedQuestLevel(cur,nl);cur=nl;banner={t,text:`LEVEL ${nl+1} · ${LEVELS[nl].name}`};saveLevel(nl+1);if(crossed)reportQuest?.record('pyramid-level',crossed);}\r\n  lvl.items=lvl.items.filter(i=>(i.y+(i.by||0))>camY-200);\r\n  if(p.y+PH<camY)die('FELL');\r\n }\r\n const sy=wy=>(viewH-(wy-camY))*scale,sx=x=>x*scale;\r\n // ---- Paper toolkit (the Dr. Girlfriend look): cut edges, hard offset shadow, grain, slight tilt.\r\n const grain=(()=>{const c=document.createElement('canvas');c.width=c.height=96;const g=c.getContext('2d'),d=g.createImageData(96,96);for(let i=0;i<d.data.length;i+=4){d.data[i]=d.data[i+1]=d.data[i+2]=hash(i)*255;d.data[i+3]=30;}g.putImageData(d,0,0);return c;})();\r\n let grainPat=null,flat=false;\r\n function cut(w,h,seed,j=1.3){ // slightly uneven scissor-cut rectangle centred on the origin\r\n  const p=new Path2D(),n=Math.max(2,Math.round(w/(20*scale))),J=i=>flat?0:(hash(seed*7+i)-.5)*2*j*scale,pts=[];\r\n  for(let i=0;i<=n;i++)pts.push([-w/2+w*i/n+J(i),-h/2+J(i+50)]);\r\n  pts.push([w/2+J(90),J(91)]);\r\n  for(let i=n;i>=0;i--)pts.push([-w/2+w*i/n+J(i+100),h/2+J(i+150)]);\r\n  pts.push([-w/2+J(190),J(191)]);\r\n  pts.forEach(([x,y],i)=>i?p.lineTo(x,y):p.moveTo(x,y));p.closePath();return p;\r\n }\r\n function sheet(cx,cy,w,h,fill,rot=0,seed=1,inner=null,{edge=EDGE,shadow=3,alpha=1}={}){\r\n  ctx.save();ctx.translate(cx,cy);ctx.rotate(rot);const path=cut(w,h,seed);\r\n  ctx.globalAlpha*=alpha;\r\n  if(flat){ctx.fillStyle='#fff';ctx.fill(path);ctx.strokeStyle='#000';ctx.lineWidth=2*scale;ctx.stroke(path);inner?.();ctx.restore();return;} // Flatland: lines only\r\n  ctx.save();ctx.translate(shadow*scale,shadow*scale);ctx.fillStyle=SHADOW;ctx.fill(path);ctx.restore();\r\n  ctx.fillStyle=fill;ctx.fill(path);grainPat??=ctx.createPattern(grain,'repeat');ctx.fillStyle=grainPat;ctx.fill(path);\r\n  ctx.strokeStyle=edge;ctx.lineWidth=1.6*scale;ctx.stroke(path);inner?.();ctx.restore();\r\n }\r\n const tape=(x,y,r)=>{ctx.save();ctx.translate(x,y);ctx.rotate(r);ctx.fillStyle='#e8cf7acc';ctx.fillRect(-9*scale,-3.5*scale,18*scale,7*scale);ctx.strokeStyle='#b89e4a88';ctx.lineWidth=scale;ctx.strokeRect(-9*scale,-3.5*scale,18*scale,7*scale);ctx.restore();};\r\n const text=(t,x,y,size,color,font='Georgia,serif',weight=700)=>{ctx.fillStyle=color;ctx.font=`${weight} ${size*scale}px ${font}`;ctx.textAlign='center';ctx.fillText(t,x,y);};\r\n const ly=(wy,f=1)=>(viewH-(wy-camY*f))*scale; // screen y of a point on a parallax layer\r\n const diamond=(x,y,r,c)=>{ctx.fillStyle=c;ctx.beginPath();ctx.moveTo(x,y-r);ctx.lineTo(x+r*.45,y);ctx.lineTo(x,y+r);ctx.lineTo(x-r*.45,y);ctx.closePath();ctx.fill();};\r\n const blob=(cx,cy,r,seed,c,a=1)=>{ctx.save();ctx.globalAlpha*=a;ctx.translate(cx,cy);const p=new Path2D(),n=14;for(let i=0;i<n;i++){const t=i/n*6.283,k=r*(.8+hash(seed+i)*.35);i?p.lineTo(Math.cos(t)*k,Math.sin(t)*k):p.moveTo(k,0);}p.closePath();ctx.fillStyle=c;ctx.fill(p);ctx.strokeStyle=EDGE;ctx.lineWidth=scale;ctx.globalAlpha*=.5;ctx.stroke(p);ctx.restore();};\r\n const rows=(f,step,fn)=>{for(let b=Math.floor(camY*f/step)-1;b<(camY*f+viewH)/step+1;b++)fn(b,ly(b*step+hash(b)*step*.6,f),sx(hash(b+5)*W));};\r\n const stars=(f=.3,c='255,244,210')=>rows(f,60,(b,y)=>{for(let k=0;k<2;k++)diamond(sx(hash(b*3+k)*W),y+k*sx(20),(1.5+hash(b+k)*2.5)*scale,`rgba(${c},.9)`);});\r\n function poster(cx,cy,w,h,b){\r\n  const L=SLOGANS[b%SLOGANS.length];\r\n  sheet(cx,cy,w,h,POSTER[b%POSTER.length],(hash(b+9)-.5)*.12,b,()=>{\r\n   text('MOM INC.',0,-h/2+13*scale,8,'#4c3255');\r\n   ctx.fillStyle='#79558a';ctx.beginPath();ctx.arc(0,-h/2+32*scale,9*scale,0,7);ctx.fill();diamond(0,-h/2+32*scale,8*scale,'#f4e7a0');\r\n   L.forEach((l,i)=>text(l,0,-h/2+54*scale+i*13*scale,l.length>9?9:11,'#3a2548'));\r\n   text('A MOM INC. WORKPLACE',0,h/2-6*scale,5,'#6d5a7c','monospace',400);tape(-w/2+8*scale,-h/2+2*scale,-.5);tape(w/2-8*scale,-h/2+2*scale,.5);});\r\n }\r\n const wallpaper=()=>{ctx.fillStyle='#ffffff14';for(let x=0;x<W;x+=48)ctx.fillRect(sx(x),0,sx(14),canvas.height);ctx.fillStyle='#00000010';for(let wy=Math.floor(camY/80)*80;wy<camY+viewH;wy+=80)ctx.fillRect(0,ly(wy),canvas.width,2*scale);\r\n  grainPat??=ctx.createPattern(grain,'repeat');ctx.fillStyle=grainPat;ctx.fillRect(0,0,canvas.width,canvas.height);ctx.fillStyle='#665044';ctx.fillRect(0,0,sx(10),canvas.height);ctx.fillRect(sx(W-10),0,sx(10),canvas.height);};\r\n function pyramidDesk(by,k=1){ // the desk with the paper food pyramid (the same one that sat in the pyramid scene)\r\n  const px0=sx(W/2),py0=ly(by-4);\r\n  sheet(px0,ly(by-18),sx(300*k),sx(34),'#8a5f3c',0,6);sheet(px0,ly(by-50),sx(280*k),sx(10),'#a9774c',0,7);\r\n  for(let i=0;i<3;i++)sheet(px0,py0-sx(17)-i*sx(32*k),sx((150-i*45)*k),sx(34*k),['#cdbb8a','#d9c590','#e8d28a'][i],0,20+i);\r\n  for(const [dx,dy,c] of [[-30,-16,'#7fcf5a'],[0,-16,'#f4d35e'],[30,-16,'#4fd1c5'],[-14,-48,'#f4978e'],[14,-48,'#c9a7eb']])sheet(px0+sx(dx*k),py0+sx(dy*k),sx(22*k),sx(18*k),c,0,Math.round(dx+dy),null,{shadow:1});\r\n  sheet(px0,py0-sx(100*k),sx(30*k),sx(14*k),'#2d2a4e',0,8,()=>text('FOOD',0,sx(3),6,'#f4e7a0','monospace'));\r\n }\r\n function office(by){ // the small office: floor, window, filing cabinets, desk + pyramid, the wall poster and note\r\n  ctx.fillStyle='#6a5240';ctx.fillRect(0,ly(by-40),canvas.width,canvas.height);ctx.fillStyle='#00000022';for(let x=0;x<W;x+=40)ctx.fillRect(sx(x),ly(by-40),scale*2,canvas.height);\r\n  sheet(sx(250),ly(by+150),sx(70),sx(90),'#bfe3ee',0,3,()=>{ctx.strokeStyle=EDGE;ctx.beginPath();ctx.moveTo(0,-sx(45));ctx.lineTo(0,sx(45));ctx.moveTo(-sx(35),0);ctx.lineTo(sx(35),0);ctx.stroke();});\r\n  sheet(sx(36),ly(by+60),sx(46),sx(100),'#8c8f98',0,4,()=>{for(let i=0;i<3;i++){ctx.strokeStyle=EDGE;ctx.strokeRect(-sx(18),-sx(40)+i*sx(30),sx(36),sx(26));}});\r\n  sheet(sx(86),ly(by+40),sx(46),sx(80),'#9a9da6',0,5);pyramidDesk(by);\r\n  poster(sx(60),ly(by+250),sx(96),sx(104),0);\r\n  sheet(sx(250),ly(by+260),sx(86),sx(54),'#dbc68a',.05,9,()=>{text('READ THE SOURCE.',0,-sx(4),6.5,'#463149','monospace',400);text('KEEP THE LABEL.',0,sx(8),6.5,'#463149','monospace',400);});\r\n  sheet(sx(300),ly(by+420),sx(40),sx(100),'#7b5a3a',0,11,()=>text('VENT',0,0,6,'#f4e7a0','monospace')); // the bookshelf into the ceiling vent\r\n }\r\n // One scene per level, drawn inside that level's band (clipped), over its sky gradient.\r\n const SCENES=[\r\n  ()=>{wallpaper();office(0);},\r\n  ()=>{rows(1,130,(b,y)=>{sheet(sx(W/2),y,sx(W-24),sx(30),'#9aa0ae',0,b,()=>{for(let i=0;i<9;i++){ctx.fillStyle='#6a6f7c';ctx.beginPath();ctx.arc(-sx(W/2-30)+i*sx(36),0,sx(2),0,7);ctx.fill();}});\r\n   if(b%3===0){ctx.save();ctx.translate(sx(40+hash(b)*280),y-sx(40));ctx.rotate(t*5);for(let i=0;i<4;i++){ctx.rotate(1.57);sheet(0,-sx(14),sx(8),sx(26),'#d0d4de',0,b+i,null,{shadow:1});}ctx.restore();}});},\r\n  ()=>{wallpaper();rows(1,230,(b,y,x)=>{const w=sx(70+hash(b+1)*40);poster(sx(30+hash(b)*(W-60)),y,w,w*1.2,b);});},\r\n  ()=>{rows(.8,150,(b,y,x)=>{const w=sx(50+hash(b)*40),h=sx(120+hash(b+2)*100);sheet(x,y+h/2-sx(60),w,h,'#5a5a78',0,b,()=>{for(let i=0;i<6;i++)for(let k=0;k<3;k++){ctx.fillStyle=hash(b*9+i*3+k)<.5?'#ffd66a':'#3a3a58';ctx.fillRect(-w/2+sx(7)+k*w/3.4,-h/2+sx(10)+i*sx(18),sx(8),sx(9));}});\r\n   if(b%5===0)sheet(sx(W/2),y-sx(100),sx(180),sx(70),'#e45a8a',0,b+1,()=>{text('MOM',0,sx(10),30,'#fff4dc');text('LOVES YOU',0,sx(24),8,'#fff4dc','monospace');});\r\n   if(b%3===1){ctx.strokeStyle='#2a2a3e';ctx.lineWidth=2*scale;ctx.beginPath();ctx.moveTo(x,y-sx(60));ctx.lineTo(x,y-sx(110));ctx.stroke();}});},\r\n  ()=>{rows(.5,150,(b,y,x)=>{for(let k=0;k<3;k++)blob(x+sx(k*34-30),y+sx(k%2*10),sx(30+hash(b+k)*12),b+k,'#fbfaf5',.95);\r\n   if(b%9===0)sheet(sx(W/2),y,sx(140),sx(46),'#e45a8a',0,b,()=>text('MOM',0,sx(5),16,'#fff4dc'));\r\n   if(b%4===2){ctx.strokeStyle='#fff';ctx.lineWidth=scale;ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(x,y+sx(40));ctx.stroke();ctx.fillStyle='#e0504a';ctx.beginPath();ctx.arc(x,y,sx(12),0,7);ctx.fill();}});},\r\n  ()=>{stars();sheet(sx(W/2),sx(viewH/2),sx(10),canvas.height*1.1,'#c9c6d2',0,3,()=>{ctx.strokeStyle=EDGE;for(let wy=Math.floor(camY/60)*60;wy<camY+viewH+60;wy+=60){ctx.beginPath();ctx.moveTo(-sx(5),ly(wy)-sx(viewH/2));ctx.lineTo(sx(5),ly(wy)-sx(viewH/2));ctx.stroke();}});\r\n   rows(1,300,(b,y,x)=>sheet(hash(b)<.5?sx(70):sx(W-70),y,sx(50),sx(36),'#8a8fa8',0,b,()=>{ctx.fillStyle='#ffd66a';ctx.fillRect(-sx(6),-sx(6),sx(12),sx(10));}));},\r\n  ()=>{stars(.3);rows(.7,110,(b,y,x)=>sheet(x,y,sx(14+hash(b)*22),sx(8+hash(b+1)*10),'#8a8fa0',hash(b)*3,b,null,{shadow:2}));\r\n   rows(1,330,(b,y)=>{ctx.save();ctx.strokeStyle='#fffbe8';ctx.lineWidth=5*scale;ctx.shadowColor='#fff';ctx.shadowBlur=12*scale;ctx.beginPath();ctx.moveTo(0,y);for(let i=1;i<=9;i++)ctx.lineTo(sx(i*W/9),y+(hash(b*9+i)-.5)*sx(26));ctx.stroke();ctx.restore();});},\r\n  ()=>{stars();rows(.6,380,(b,y)=>{const left=hash(b+3)<.5,x=left?sx(70):sx(W-70),w=sx(120+hash(b)*70),hh=sx(52+hash(b+1)*30);\r\n   sheet(x,y,w,hh,'#5d5a8c',(hash(b+6)-.5)*.05,b,()=>{for(let i=0;i<5;i++){ctx.fillStyle='#ffd66a';ctx.fillRect(-w/2+sx(12+i*26),sx(6),sx(8),sx(8));}ctx.fillStyle='#8b86c4';ctx.fillRect(-w/2,-hh/2,w,sx(8));});\r\n   sheet(x+(left?w/2+sx(24):-w/2-sx(24)),y,sx(48),sx(7),'#9fd4e6',0,b+1,null,{shadow:2});sheet(x+(left?w/2+sx(60):-w/2-sx(60)),y-sx(10),sx(16),sx(12),'#e8e0d0',.3,b+2,null,{shadow:2});\r\n   if(b%2===0)sheet(x,y-hh/2-sx(10),sx(86),sx(14),'#2d2a4e',0,b+3,()=>text('MOM PORT 18',0,sx(3),7,'#ffd66a','monospace'),{shadow:2});});},\r\n  ()=>{blob(sx(W/2),ly(8700),sx(60),4,'#3b78c4');rows(1,260,(b,y,x)=>{sheet(x,y,sx(90),sx(12),'#c9c6d2',0,b,()=>{sheet(0,-sx(12),sx(24),sx(16),'#cdbb8a',0,b+3,null,{shadow:1});},{shadow:2});sheet(x,y+sx(26),sx(80),sx(8),'#8a5f3c',0,b+1,null,{shadow:2});});},\r\n  ()=>{rows(1,120,(b,y)=>sheet(sx(W/2),y,sx(W),sx(60),b%2?'#e9d8ee':'#c7b0dc',0,b,null,{alpha:.7,shadow:0}));ctx.fillStyle='#00000026';ctx.fillRect(0,0,sx(W/2),canvas.height);\r\n   ctx.fillStyle='#00000055';ctx.fillRect(sx(W/2)-scale,0,2*scale,canvas.height);ctx.save();ctx.globalCompositeOperation='difference';ctx.fillStyle='#e8e8e8';ctx.fillRect(0,0,canvas.width,canvas.height);ctx.restore();},\r\n  ()=>{for(let r=Math.floor(camY/60);r<(camY+viewH)/60+1;r++)for(let c=0;c<7;c++){const x=sx(c*60+30),y=ly(r*60+30),k=hash(r*7+c);ctx.fillStyle=ORIGAMI[(r+c)%5];ctx.beginPath();if(k<.5){ctx.moveTo(x-sx(30),y-sx(30));ctx.lineTo(x+sx(30),y-sx(30));ctx.lineTo(x-sx(30),y+sx(30));}else{ctx.moveTo(x+sx(30),y+sx(30));ctx.lineTo(x+sx(30),y-sx(30));ctx.lineTo(x-sx(30),y+sx(30));}ctx.closePath();ctx.fill();ctx.strokeStyle='#ffffff88';ctx.stroke();}},\r\n  ()=>{stars();rows(.6,120,(b,y,x)=>{if(b%2)blob(x,y,sx(12+hash(b)*14),b,'#6a6478');else{ctx.strokeStyle='#14101c';ctx.lineWidth=4*scale;ctx.beginPath();ctx.arc(x,y,sx(9),0,7);ctx.stroke();ctx.fillStyle='#3a3548';ctx.beginPath();ctx.arc(x,y+sx(3),sx(8),0,7);ctx.fill();}});},\r\n  ()=>{rows(.4,80,(b,y)=>{ctx.strokeStyle=b%2?'#7fe6ff':'#d070ff';ctx.lineWidth=4*scale;ctx.beginPath();ctx.ellipse(sx(W/2),y,sx(150+(b%4)*8),sx(50),0,0,7);ctx.stroke();});\r\n   ctx.strokeStyle='#ffffff22';for(let i=0;i<9;i++){ctx.beginPath();ctx.moveTo(sx(W/2),sx(viewH/2));ctx.lineTo(sx(i*W/8),0);ctx.moveTo(sx(W/2),sx(viewH/2));ctx.lineTo(sx(i*W/8),canvas.height);ctx.stroke();}},\r\n  ()=>{ctx.strokeStyle='#000';ctx.lineWidth=scale;for(let x=0;x<=W;x+=40){ctx.beginPath();ctx.moveTo(sx(x),0);ctx.lineTo(sx(x),canvas.height);ctx.stroke();}for(let wy=Math.floor(camY/40)*40;wy<camY+viewH+40;wy+=40){ctx.beginPath();ctx.moveTo(0,ly(wy));ctx.lineTo(canvas.width,ly(wy));ctx.stroke();for(let x=0;x<=W;x+=40){ctx.fillStyle='#000';ctx.fillRect(sx(x)-scale,ly(wy)-scale,2*scale,2*scale);}}},\r\n  ()=>{ctx.save();const by=14000,cy=ly(by+LEVEL_H/2);ctx.translate(0,cy);ctx.scale(1,-1);ctx.translate(0,-cy);wallpaper();office(by);ctx.restore();ctx.save();ctx.globalCompositeOperation='color';ctx.fillStyle='#2aa8a0';ctx.fillRect(0,0,canvas.width,canvas.height);ctx.restore();},\r\n  ()=>{for(let r=Math.floor(camY/60);r<(camY+viewH)/60+1;r++)for(let c=0;c<7;c++){const x=sx(c*60),y=ly(r*60+60);ctx.fillStyle=QUILT[Math.floor(hash(r*7+c)*6)];ctx.fillRect(x,y,sx(60),sx(60));ctx.setLineDash([4*scale,4*scale]);ctx.strokeStyle='#fff8';ctx.strokeRect(x+sx(3),y+sx(3),sx(54),sx(54));ctx.setLineDash([]);if(hash(r*7+c+9)<.3){ctx.fillStyle='#2a2030';ctx.beginPath();ctx.arc(x+sx(30),y+sx(30),sx(5),0,7);ctx.fill();}}},\r\n  ()=>{rows(1,40,(b,y)=>{ctx.fillStyle=`rgba(80,255,220,${.08+hash(b)*.15})`;ctx.fillRect(0,y,canvas.width,sx(2+hash(b+1)*4));});\r\n   rows(.8,90,(b,y,x)=>sheet(((t*40+b*77)%(W+40))*scale-sx(20),y,sx(24),sx(10),'#2a7a8a',0,b,()=>{ctx.fillStyle='#7fffe0';ctx.fillRect(-sx(8),-sx(2),sx(16),sx(4));},{shadow:1}));\r\n   rows(1,260,(b,y,x)=>{ctx.fillStyle=hash(b)<.5?'#ff3aa0':'#3affd0';ctx.fillRect(x,y,sx(18),sx(12));});ctx.fillStyle='#0b4a5a';ctx.fillRect(0,0,sx(8),canvas.height);ctx.fillRect(sx(W-8),0,sx(8),canvas.height);},\r\n  ()=>{rows(.35,300,(b,y,x)=>{blob(x,y,sx(80+hash(b)*50),b,hash(b)<.5?'#8a4fc4':'#3fa6b8',.4);});stars(.3,'255,200,230');rows(.7,150,(b,y,x)=>{const r=sx(6+hash(b)*8)*(1+.25*Math.sin(t*3+b));diamond(x,y,r*1.6,'#ffe9a0');diamond(x,y,r,'#fff');});},\r\n  ()=>{ctx.fillStyle='#00000018';for(let wy=Math.floor(camY/10)*10;wy<camY+viewH+10;wy+=10){ctx.fillRect(0,ly(wy),sx(wy%50?10:22),scale);if(!(wy%50))text(String(wy/10%100),sx(34),ly(wy)+sx(3),6,'#000a','monospace',400);}\r\n   rows(1,70,(b,y,x)=>{ctx.strokeStyle='#555';ctx.lineWidth=scale;ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(x+sx(20),y-sx(8));ctx.lineTo(x+sx(36),y+sx(4));ctx.stroke();if(b%4===0)blob(x,y+sx(20),sx(4),b,'#e8a0b0');});\r\n   const hy=ly(18700);sheet(sx(W-30),hy,sx(110),sx(70),'#e8b890',.1,3);for(let i=0;i<4;i++)sheet(sx(W-80),hy-sx(30)+i*sx(18),sx(60),sx(12),'#e8b890',.1+i*.05,4+i);},\r\n  ()=>{rows(1,160,(b,y,x)=>{ctx.fillStyle='#00000030';ctx.beginPath();ctx.arc(sx(hash(b)<.5?50:W-50),y,sx(22),0,7);ctx.fill();ctx.strokeStyle='#8a7aa8';ctx.lineWidth=3*scale;ctx.stroke();});stars();\r\n   pyramidDesk(END_Y-14,1.4);tags.slice(-40).forEach((m,i)=>{const c=i%4,r=Math.floor(i/4);sheet(sx(45+c*90),ly(END_Y-260-r*34),sx(80),sx(26),'#ead9a6',(hash(i)-.5)*.1,i,()=>{text(m.name,0,-sx(2),6,'#463149','system-ui,sans-serif');text(m.date,0,sx(8),5,'#463149','monospace',400);},{shadow:2});});},\r\n ];\r\n function gate(k){ // the connector between level k and k+1: a paper lintel + posts that say how you leave\r\n  const y=ly(k*LEVEL_H+40);sheet(sx(W/2),y-sx(34),sx(W-30),sx(24),'#d7c28a',0,k,()=>text(LEVELS[k-1].gate,0,sx(3),7,'#3a2548','monospace'));\r\n  sheet(sx(14),y,sx(12),sx(70),'#c8b47a',0,k+1,null,{shadow:2});sheet(sx(W-14),y,sx(12),sx(70),'#c8b47a',0,k+2,null,{shadow:2});\r\n  ctx.fillStyle='#ffe9a044';ctx.fillRect(sx(20),y-sx(22),sx(W-40),sx(2));\r\n }\r\n function bg(){\r\n  const first=levelAt(camY),lastL=levelAt(camY+viewH);\r\n  for(let L=Math.max(0,first);L<=lastL;L++){\r\n   const top=L===LEVEL_COUNT-1?END_Y+3000:(L+1)*LEVEL_H,a=ly(top),b=ly(L?L*LEVEL_H:-3000),next=LEVELS[Math.min(LEVEL_COUNT-1,L+1)].sky;\r\n   ctx.save();ctx.beginPath();ctx.rect(0,a,canvas.width,b-a+1);ctx.clip();\r\n   const g=ctx.createLinearGradient(0,b,0,a);g.addColorStop(0,LEVELS[L].sky);g.addColorStop(1,L===13?LEVELS[13].sky:next);ctx.fillStyle=g;ctx.fillRect(0,a,canvas.width,b-a+1);\r\n   flat=L===13;SCENES[L]();flat=false;ctx.restore();\r\n  }\r\n  for(let k=Math.max(1,first);k<=Math.min(LEVEL_COUNT-1,lastL+1);k++)gate(k);\r\n }\r\n function paper(it){\r\n  if(it.type==='desk'){sheet(sx(W/2),sy(it.y)+sx(8),sx(W),sx(18),'#8a5f3c',0,31);return;}\r\n  const x=px(it),cx=sx(x+PLAT_W/2),y=sy(it.y+(it.by||0)),w=sx(PLAT_W),hh=sx(11),cy=y+hh/2-scale,rot=(hash(it.y)-.5)*.08+(it.broken?(hash(it.y+1)-.5)*3*(-(it.by||0)/200):0);\r\n  if(it.type==='monster'||it.type==='hole'||(it.broken&&y>canvas.height+50))return;\r\n  if(it.blink&&!solid(it)){ctx.save();ctx.strokeStyle='#ffffff88';ctx.lineWidth=2*scale;ctx.setLineDash([3*scale,3*scale]);ctx.beginPath();ctx.moveTo(cx-w/2,cy);ctx.lineTo(cx+w/2,cy);ctx.stroke();ctx.restore();return;}\r\n  if(it.type==='breaking'){ // two paper halves with a torn edge between\r\n   for(const s of[-1,1])sheet(cx+s*w/4,cy+(it.broken?s*sx(3):0),w/2-scale,hh,'#c58558',rot+(it.broken?s*.25:0),Math.round(it.y)+s,null,{edge:'#7a4a30'});\r\n   ctx.strokeStyle='#4a2a1a';ctx.lineWidth=scale*1.2;ctx.beginPath();ctx.moveTo(cx,cy-hh/2);ctx.lineTo(cx-sx(3),cy-sx(1));ctx.lineTo(cx+sx(2),cy+sx(1));ctx.lineTo(cx,cy+hh/2);ctx.stroke();\r\n  }else sheet(cx,cy,w,hh,it.type==='moving'?'#8cc3d1':it.blink?'#f4d870':'#d7c28a',rot,Math.round(it.y),it.type==='moving'?()=>{tape(-w/2+sx(5),0,-.3);tape(w/2-sx(5),0,.3);}:null);\r\n  if(it.type==='spring'){ctx.save();ctx.translate(cx,y);for(const [o,c,wd] of[[2,SHADOW,5],[0,'#fff4dc',4]]){ctx.strokeStyle=c;ctx.lineWidth=wd*scale;ctx.lineJoin='bevel';ctx.beginPath();ctx.moveTo(-sx(7)+o,0);for(let i=0;i<4;i++)ctx.lineTo(i%2?-sx(7)+o:sx(7)+o,-(i+1)*sx(4)+o);ctx.stroke();}ctx.strokeStyle=EDGE;ctx.lineWidth=scale;ctx.stroke();ctx.restore();}\r\n  if(it.gear==='jetpack')sheet(cx,y-sx(9),sx(11),sx(16),'#e45a5a',0,3,()=>{ctx.fillStyle='#ffb24d';ctx.fillRect(-sx(3),sx(6),sx(6),sx(5));},{shadow:2});\r\n  else if(it.gear)sheet(cx,y-sx(8),sx(16),sx(7),'#7fe6ff',-.2,4,()=>{ctx.beginPath();ctx.moveTo(-sx(8),0);ctx.lineTo(sx(8),0);ctx.stroke();},{shadow:2});\r\n }\r\n function foe(it){\r\n  if(it.broken)return;const x=sx(it.x),y=sy(it.y);\r\n  if(it.type==='hole'){const cx=x+sx(30),cy=y-sx(30);const r=ctx.createRadialGradient(cx,cy,0,cx,cy,sx(34));r.addColorStop(0,'#000');r.addColorStop(.6,'#1a0830');r.addColorStop(1,'#7a3cc800');ctx.fillStyle=r;ctx.beginPath();ctx.arc(cx,cy,sx(34),0,7);ctx.fill();ctx.strokeStyle='#b57cff';ctx.lineWidth=2*scale;ctx.beginPath();ctx.arc(cx,cy,sx(18),t*4,t*4+4);ctx.stroke();return;}\r\n  if(it.type!=='monster')return;\r\n  const look=LEVELS[levelAt(it.y)].mon,cx=x+sx(25),cy=y-sx(16),eye=(c=0)=>{ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(cx,cy-sx(2),sx(8),0,7);ctx.fill();ctx.fillStyle='#000';ctx.beginPath();ctx.arc(cx+sx(3*Math.sign(p.x-it.x-25)),cy-sx(2),sx(3.5),0,7);ctx.fill();};\r\n  if(look==='tri'){sheet(cx,cy,sx(40),sx(30),'#fff',0,3);ctx.strokeStyle='#000';ctx.lineWidth=2*scale;ctx.beginPath();ctx.moveTo(cx-sx(22),cy+sx(14));ctx.lineTo(cx,cy-sx(16));ctx.lineTo(cx+sx(22),cy+sx(14));ctx.closePath();ctx.stroke();eye();return;}\r\n  ctx.save();ctx.translate(cx,cy);ctx.fillStyle=SHADOW;ctx.beginPath();ctx.ellipse(sx(3),sx(3),sx(25),sx(16),0,0,7);ctx.fill();ctx.restore();\r\n  ctx.lineWidth=2*scale;ctx.strokeStyle='#1d3a1a';\r\n  if(look==='dust'){ctx.fillStyle='#a8a4a0';for(let i=0;i<10;i++){ctx.beginPath();ctx.arc(cx+Math.cos(i*.63)*sx(18),cy+Math.sin(i*.63)*sx(11),sx(8),0,7);ctx.fill();}}\r\n  else if(look==='pigeon'){ctx.fillStyle='#8890a0';ctx.beginPath();ctx.ellipse(cx,cy,sx(22),sx(14),0,0,7);ctx.fill();ctx.stroke();ctx.fillStyle='#e8a040';ctx.beginPath();ctx.moveTo(cx+sx(20),cy-sx(4));ctx.lineTo(cx+sx(32),cy);ctx.lineTo(cx+sx(20),cy+sx(3));ctx.fill();}\r\n  else if(look==='crane'){ctx.fillStyle='#fff4f0';ctx.beginPath();ctx.moveTo(cx-sx(26),cy-sx(10));ctx.lineTo(cx,cy+sx(12));ctx.lineTo(cx+sx(26),cy-sx(10));ctx.lineTo(cx+sx(8),cy-sx(2));ctx.lineTo(cx+sx(14),cy-sx(20));ctx.lineTo(cx,cy-sx(4));ctx.closePath();ctx.fill();ctx.stroke();}\r\n  else if(look==='needle'){ctx.fillStyle='#c8ccd8';ctx.beginPath();ctx.moveTo(cx-sx(28),cy);ctx.lineTo(cx,cy-sx(8));ctx.lineTo(cx+sx(28),cy);ctx.lineTo(cx,cy+sx(8));ctx.closePath();ctx.fill();ctx.stroke();ctx.fillStyle='#e0504a';ctx.beginPath();ctx.arc(cx-sx(20),cy,sx(3),0,7);ctx.fill();}\r\n  else if(look==='junk'){ctx.fillStyle='#8a8fa0';ctx.beginPath();ctx.ellipse(cx,cy,sx(22),sx(14),.3,0,7);ctx.fill();ctx.stroke();ctx.fillStyle='#e8c040';ctx.fillRect(cx-sx(3),cy-sx(18),sx(6),sx(8));}\r\n  else if(look==='glitch'){ctx.fillStyle='#ff3aa0';ctx.fillRect(cx-sx(18),cy-sx(10),sx(36),sx(20));ctx.fillStyle='#3affd0';ctx.fillRect(cx-sx(10)+sx(6*Math.sin(t*20)),cy-sx(4),sx(30),sx(8));}\r\n  else{ctx.fillStyle='#69b04a';ctx.beginPath();ctx.ellipse(cx,cy,sx(25),sx(16),0,0,7);ctx.fill();ctx.stroke();for(const k of[-1,1]){ctx.beginPath();ctx.moveTo(cx+sx(k*14),cy-sx(14));ctx.lineTo(cx+sx(k*20),cy-sx(26));ctx.stroke();}}\r\n  if(look!=='needle'&&look!=='glitch')eye();\r\n }\r\n function tag(i,m){\r\n  const wy=markerHeight(i,tags.length),y=sy(wy);if(y<-80||y>canvas.height+80||false)return;const left=i%2===0,x=left?sx(6):sx(W-150),w=sx(144),h=sx(46);\r\n  ctx.save();ctx.translate(x+w/2,y);ctx.rotate(left?-.03:.03);ctx.strokeStyle='#ffffff55';ctx.lineWidth=scale;ctx.beginPath();ctx.moveTo(left?-w/2:w/2,0);ctx.lineTo(left?-w/2-sx(8):w/2+sx(8),-sx(30));ctx.stroke();\r\n  ctx.fillStyle='#ead9a6';ctx.strokeStyle='#8c7646';ctx.lineWidth=2*scale;ctx.fillRect(-w/2,-h/2,w,h);ctx.strokeRect(-w/2,-h/2,w,h);ctx.textAlign='left';\r\n  ctx.fillStyle='#463149';ctx.font=`700 ${11*scale}px system-ui,sans-serif`;ctx.fillText(m.name,-w/2+sx(6),-sx(6));ctx.font=`${9*scale}px monospace`;ctx.fillText(m.date,-w/2+sx(6),sx(6));ctx.fillText(`P${m.p} C${m.c} F${m.f}`,-w/2+sx(6),sx(17));ctx.restore();\r\n }\r\n function me(ghost=false){\r\n  const x=sx(ghost?W-p.x:p.x),y=sy(p.y),k=PH/240*scale;ctx.save();if(ghost)ctx.globalAlpha=.4;\r\n  for(const dx of[0,W*scale,-W*scale]){ctx.save();ctx.translate(x+dx,y);ctx.scale((ghost?-p.face:p.face)*k,k);ctx.translate(-100,-236);\r\n   ctx.shadowColor=flat?'transparent':'#49334b88';ctx.shadowOffsetX=3*scale;ctx.shadowOffsetY=3*scale;ctx.fillStyle=art.colour;ctx.strokeStyle='#2a1838';ctx.lineWidth=4;ctx.lineJoin='round';ctx.fill(shape);ctx.shadowColor='transparent';ctx.stroke(shape);\r\n   const [ex,ey,er]=art.eye;ctx.fillStyle='#fff8eb';ctx.beginPath();ctx.arc(ex,ey,er*1.7,0,7);ctx.fill();ctx.stroke();ctx.fillStyle='#2a1838';ctx.beginPath();ctx.arc(ex+er*.5,ey,er*.8,0,7);ctx.fill();\r\n   if(p.fly>0&&!ghost){if(p.gear==='propeller'){ctx.fillStyle='#7fe6ff';ctx.fillRect(80,0,40,8);ctx.fillRect(100-60*Math.cos(t*30),-6,120*Math.abs(Math.cos(t*30))||4,5);}else{ctx.fillStyle='#e45a5a';ctx.fillRect(40,100,20,60);ctx.fillRect(140,100,20,60);ctx.fillStyle='#ffb24d';ctx.fillRect(44,160,12,30+30*Math.random());ctx.fillRect(144,160,12,30+30*Math.random());}}\r\n   ctx.restore();}\r\n  ctx.restore();\r\n }\r\n function draw(){\r\n  bg();flat=levelAt(camY+viewH/2)===13;tags.forEach((m,i)=>tag(i,m));lvl.items.forEach(paper);lvl.items.forEach(foe);if(levelAt(p.y)===14)me(true);me();flat=false;\r\n  const s=Math.floor(maxY/10),dark=levelAt(camY+viewH/2)===13?'#000':'#fff4dc';ctx.fillStyle=dark;ctx.strokeStyle='#000a';ctx.lineWidth=3*scale;ctx.textAlign='left';ctx.font=`800 ${18*scale}px monospace`;if(!flat)ctx.strokeText(String(s),sx(10),sx(26));ctx.fillText(String(s),sx(10),sx(26));\r\n  ctx.font=`${10*scale}px monospace`;ctx.fillText(`LVL ${Math.max(1,cur+1)}/${LEVEL_COUNT}${plus?' +'+plus:''}  BEST ${Math.max(best,s)}`,sx(10),sx(40));\r\n  const past=tags.filter((_,i)=>markerHeight(i,tags.length)<=p.y);if(tags.length){const m=past.at(-1)||tags[0];ctx.textAlign='right';ctx.font=`700 ${10*scale}px monospace`;ctx.fillStyle='#ead9a6';ctx.strokeText(m.date,sx(W-6),sx(120));ctx.fillText(m.date,sx(W-6),sx(120));ctx.font=`${8*scale}px monospace`;ctx.fillText('YOU ATE',sx(W-6),sx(108));}\r\n  if(banner&&t-banner.t<2.8){ctx.save();ctx.globalAlpha=Math.min(1,(2.8-(t-banner.t))*2);sheet(sx(W/2),sx(86),sx(250),sx(40),'#ead9a6',-.02,7,()=>{text(banner.text,0,sx(5),13,'#463149');tape(-sx(112),-sx(16),-.4);tape(sx(112),-sx(16),.4);});ctx.restore();}\r\n }\r\n function tick(now){raf=requestAnimationFrame(tick);if(disposed)return;shell.phase(!dead&&!won&&!document.hidden?'active':'paused');const dt=Math.min(.033,last?(now-last)/1000:0);last=now;if(!document.hidden){step(dt);draw();}}\r\n raf=requestAnimationFrame(tick);\r\n return {\r\n  state:()=>({p,camY,maxY,dead,won,cur,items:lvl.items}),\r\n  teleport(h){if(dead&&!won){dead=false;over.style.display='none';}lvl.ensure(h+viewH*2);p.y=h;p.vy=JUMP;p.fly=.35;p.flyV=0;camY=h-viewH*.45;maxY=Math.max(maxY,h);},\r\n  level(n){const h=(n-1)*LEVEL_H+30;this.teleport(h);},\r\n  dispose(){if(disposed)return;shell.close();disposed=true;cancelAnimationFrame(raf);ro.disconnect();unsub?.();removeEventListener('keydown',kd);removeEventListener('keyup',ku);removeEventListener('pointerup',pu);removeEventListener('pointercancel',pu);canvas.remove();exit.remove();over.remove();},\r\n };\r\n}\r\n","arcade/pyramid-jump/logic.mjs":"// Pure pieces of Pyramid Jump (no DOM): coach shape + colour mapping, meal markers and the\r\n// platform generator. Lazy/optional: loaded only from the food pyramid on a photo day (see food/pyramid-scanner.mjs).\r\nimport {SHAPES,COLOURS,LEGACY} from './data.mjs';\r\nexport const W=360;\r\nexport const BEST_KEY='myr5-pyramid-jump-best-v1';\r\n\r\n// The user's selected coach body -> its paper silhouette, filled with the body's own colour (colour only, no texture).\r\nexport function paperFor(recipe){\r\n const id=String(recipe?.body||'myr5').replace(/^roster\\//,''),shape=SHAPES[id]||SHAPES.myr5;\r\n const choice=recipe?.materials?.body?.colorId,hex=/^#[0-9a-f]{6}$/i.test(choice)?choice:null;\r\n const colour=hex||COLOURS[choice]||LEGACY[recipe?.styles?.body]||LEGACY[0];\r\n return {id:SHAPES[id]?id:'myr5',d:shape.d,eye:shape.e,colour};\r\n}\r\nexport function readRecipe(store=localStorage){try{const r=JSON.parse(store.getItem('myr5-recipe-v1'));return r&&typeof r==='object'?r:null;}catch{return null;}}\r\n// Top of the silhouette (min y of the path) so the pocket peek can crop to head + eye.\r\nexport const topOf=d=>Math.min(...[...d.matchAll(/[ML](-?\\d+),(-?\\d+)/g)].map(m=>+m[2]));\r\n\r\n// Logged meals, oldest first (the API returns newest first), one paper tag each. Guests / no meals -> [].\r\nexport function mealMarkers(items){\r\n if(!Array.isArray(items))return [];\r\n return items.filter(m=>m&&m.name&&!Number.isNaN(Date.parse(m.eaten_at)))\r\n  .sort((a,b)=>Date.parse(a.eaten_at)-Date.parse(b.eaten_at))\r\n  .map(m=>{const t=new Date(m.eaten_at);return {name:String(m.name).slice(0,22),date:t.toLocaleDateString(undefined,{month:'short',day:'numeric'}),t:t.getTime(),p:Math.round(m.protein)||0,c:Math.round(m.carbs)||0,f:Math.round(m.fat)||0};});\r\n}\r\n// Meal tags spread over the whole climb, oldest near the start of level 1 and today near the top of level 20.\r\nexport const LEVEL_H=1000,LEVEL_COUNT=20,END_Y=LEVEL_H*LEVEL_COUNT;\r\nexport const markerHeight=(i,n)=>n<2?600:500+i*((END_Y-1500)/(n-1));\r\n\r\n// The 20-place journey (Ian, 15 Oct). sky = [bottom,top] colours; mon = the level's monster look; gate = how you leave it.\r\nexport const LEVELS=[\r\n {name:'The Office',gate:'CLIMB THE BOOKSHELF INTO THE VENT',sky:'#bba16e',mon:'dust'},\r\n {name:'Ceiling Vents',gate:'PUSH UP THE ROOF HATCH',sky:'#6f7480',mon:'dust'},\r\n {name:'Propaganda Stairwell',gate:'DOOR MARKED ROOF',sky:'#8a6aa0',mon:'dust'},\r\n {name:'Rooftop & Billboards',gate:'SPRING OFF THE ANTENNA TIP',sky:'#e0a070',mon:'pigeon'},\r\n {name:'Sky of Paper Clouds',gate:'GRAB A BALLOON STRING UP TO THE TETHER',sky:'#8fc4ea',mon:'pigeon'},\r\n {name:'Space Elevator',gate:'THE POD DOCKS AT THE TOP',sky:'#2a3f7a',mon:'alien'},\r\n {name:'Satellite Junkyard',gate:'SLIP THROUGH A TEAR',sky:'#0e1233',mon:'junk'},\r\n {name:'MOM Port 18',gate:'BOARD THE EXPRESS LIFT',sky:'#171a44',mon:'alien'},\r\n {name:'Moon Cafeteria Colony',gate:'THE FREEZER OPENS ONTO A FOLD',sky:'#4a4658',mon:'alien'},\r\n {name:'The Fold',gate:'FALL UP THROUGH THE CREASE',sky:'#d9c6e8',mon:'crane'},\r\n {name:'Origami Dimension',gate:'A CRANE CARRIES YOU UP',sky:'#e8a0b4',mon:'crane'},\r\n {name:'Asteroid Gym Belt',gate:'A RING PORTAL',sky:'#1a1030',mon:'junk'},\r\n {name:'Wormhole Highway',gate:'EXIT THE RING',sky:'#3a1a6a',mon:'alien'},\r\n {name:'Flatland',gate:'STEP OFF THE EDGE OF THE PLANE',sky:'#f4f4f4',mon:'tri'},\r\n {name:'Mirror Office',gate:'THE MIRROR PYRAMID’S TIP',sky:'#5aa89a',mon:'dust'},\r\n {name:'Quilt Dimension',gate:'PULL A LOOSE THREAD',sky:'#b86a8a',mon:'needle'},\r\n {name:'Data Cable',gate:'THE CABLE PLUGS INTO A PORT',sky:'#04202a',mon:'glitch'},\r\n {name:'Nebula Nursery',gate:'A NEWBORN STAR LAUNCHES YOU',sky:'#3a1a50',mon:'alien'},\r\n {name:'Edge of the Page',gate:'CLIMB OVER THE PAGE EDGE',sky:'#f0e6c8',mon:'junk'},\r\n {name:'MOM’s Mothership Desk',gate:'LAND ON HER DESK',sky:'#3a2a4e',mon:'alien'},\r\n];\r\nexport const levelAt=h=>Math.max(0,Math.min(LEVEL_COUNT-1,Math.floor(h/LEVEL_H))); // 0-based\r\nexport function steeringForMotion({steerX,gx}={}){\r\n const sample=Number.isFinite(steerX)?steerX:gx;\r\n return Number.isFinite(sample)?Math.max(-1,Math.min(1,sample*2.2)):0;\r\n}\r\n\r\nexport const mulberry=seed=>()=>{seed|=0;seed=seed+0x6D2B79F5|0;let t=Math.imul(seed^seed>>>15,1|seed);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};\r\nexport const PLAT_W=64,JUMP=760,GRAVITY=1600,MAX_JUMP=JUMP*JUMP/(2*GRAVITY); // 180 units\r\nexport const LEVEL_KEY='myr5-pyramid-jump-level-v1';\r\nexport const highestLevel=(store=localStorage)=>{try{const n=JSON.parse(store.getItem(LEVEL_KEY));return Number.isSafeInteger(n)&&n>=1?Math.min(n,LEVEL_COUNT):1;}catch{return 1;}};\r\nexport const saveLevel=(n,store=localStorage)=>{try{if(n>highestLevel(store))store.setItem(LEVEL_KEY,String(n));}catch{/* ignore */}};\r\n/** Convert an actual upward transition (zero-based current level) to its 1-based quest milestone. */\r\nexport const newlyReachedQuestLevel=(previous,current)=>Number.isSafeInteger(previous)&&Number.isSafeInteger(current)\r\n  &&current>previous&&current>=0&&current<LEVEL_COUNT?current+1:null;\r\n// Rows climb for 20 levels then end in MOM's desk (end = its height). Rows are never further apart than ~150 (< MAX_JUMP) and\r\n// every breaking/blinking row also carries a solid platform, so a safe route always exists. Difficulty ramps per level:\r\n// wider gaps, more moving and breaking platforms, then monsters (from level 2) and black holes (from level 8); `plus` = New Game+.\r\nexport class Level{\r\n constructor(rng=mulberry(7),plus=0){this.rng=rng;this.plus=plus;this.items=[{x:W/2-PLAT_W/2,y:0,type:'static'}];this.top=0;this.end=null;}\r\n ensure(y){while(this.top<y&&this.end==null)this.row();}\r\n row(){\r\n  const r=this.rng,h=this.top,L=levelAt(h),d=L/(LEVEL_COUNT-1),pl=Math.min(this.plus,3);\r\n  const gap=(58+62*d+5*pl)*(0.75+0.25*r());\r\n  if(h+gap>=END_Y-40){this.top=this.end=h+Math.min(140,Math.max(70,END_Y-h));this.items.push({x:0,y:this.end,type:'desk',w:W});return;}\r\n  const y=this.top+=gap,x=r()*(W-PLAT_W),roll=r();let type='static';\r\n  const pMove=.1+.25*d+.03*pl,pBreak=L>=2?.1+.2*d+.03*pl:0,pSpring=L===12?.2:.06;\r\n  if(L>=1&&roll<pMove)type='moving';\r\n  else if(roll<pMove+pBreak)type='breaking';\r\n  else if(roll>1-pSpring)type='spring';\r\n  const item={x,y,type};\r\n  if(type==='moving'){item.dx=40+r()*60;item.phase=r()*6.28;}\r\n  if(type==='static'){const k=r();if(L>=3&&k<.05)item.gear='propeller';else if(L>=5&&k>.97)item.gear='jetpack';else if((L===10||L===17)&&k>.4){item.blink=2.2+r();item.phase=r()*6;}}\r\n  this.items.push(item);\r\n  if(type==='breaking'||item.blink)this.items.push({x:(x+W/2)%(W-PLAT_W),y:y+gap*0.15,type:'static'});\r\n  const far=()=>{let fx;do fx=r()*(W-60);while(Math.abs(fx-x)<110);return fx;};\r\n  if(L>=1&&r()<.02+.06*d+.01*pl)this.items.push({x:far(),y:y+gap*0.5,type:'monster',dir:r()<0.5?-1:1});\r\n  else if(L>=7&&r()<(L===16?.08:.02+.04*d))this.items.push({x:far(),y:y+gap*0.5,type:'hole'});\r\n }\r\n}\r\n","arcade/pyramid-jump/pet.mjs":"// The paper MOM Inc. character that lives in Dr. Girlfriend's pocket on photo days (peek -> jump to the right ->\r\n// hop onto the pyramid and bounce -> PLAY). Lazy/optional; mounted by food/pyramid-scanner.mjs, all in the same window.\r\nimport {paperFor,readRecipe,topOf} from './logic.mjs';\r\nconst STYLE=`\r\n#pyramidScanner .pj-layer{position:absolute;inset:0;z-index:4;pointer-events:none;overflow:hidden}\r\n#pyramidScanner .pj-layer[data-behind]{z-index:1}\r\n#pyramidScanner .pj-pet{position:absolute;left:0;top:0;padding:0;border:0;background:none;pointer-events:auto;cursor:pointer;filter:drop-shadow(2px 3px 2px #30251988);touch-action:manipulation}\r\n#pyramidScanner .pj-pet svg{display:block;width:100%;height:100%;overflow:hidden}\r\n#pyramidScanner .pj-lip{position:absolute;left:-16px;right:-16px;bottom:-6px;height:20px;background:repeating-linear-gradient(90deg,#8b5bc7 0 9px,#d4a93a 9px 14px);border:2px solid #351344;border-radius:2px 2px 5px 5px}\r\n#pyramidScanner .pj-play{position:absolute;left:0;top:0;min-height:44px;padding:0 24px;border:2px solid #fff;border-radius:999px;background:#ff5d8f;color:#fff;font:800 15px/1 system-ui,sans-serif;letter-spacing:.12em;pointer-events:auto;box-shadow:0 2px 10px #0008;animation:pjPulse 1.6s ease-in-out infinite}\r\n@keyframes pjPulse{50%{box-shadow:0 2px 10px #0008,0 0 18px 6px #ff5d8fbb}}\r\n#pyramidScanner[data-jump] .pyramid-ui>*{opacity:0!important;pointer-events:none!important}\r\n#pyramidScanner .pyramid-ui>*{transition:opacity .45s}\r\n@media(prefers-reduced-motion:reduce){#pyramidScanner .pj-play{animation:none}#pyramidScanner .pyramid-ui>*{transition:none}}\r\n`;\r\nconst ease=t=>t<.5?2*t*t:1-(-2*t+2)**2/2;\r\nexport function mountPet({host,walker,playAt,apex,getMeals,dialog,recipe=readRecipe()}){\r\n const art=paperFor(recipe),top=topOf(art.d),[ex,ey,er]=art.eye,reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const style=document.createElement('style');style.textContent=STYLE;document.head.append(style);\r\n const layer=document.createElement('div');layer.className='pj-layer';layer.dataset.behind='';\r\n const pet=document.createElement('button');pet.type='button';pet.className='pj-pet';pet.setAttribute('aria-label','A little paper friend peeks out of her pocket. Tap to say hi');\r\n const peekH=Math.max(60,ey+er*1.7+46-top+20);\r\n const tufts=`<path d=\"M78,${top+3}l5,-14l6,12l6,-18l6,16l7,-15l5,14l7,-11l4,11z\" fill=\"${art.colour}\" stroke=\"#2a1838\" stroke-width=\"3\" stroke-linejoin=\"round\"/>`;\r\n pet.innerHTML=`<svg viewBox=\"0 ${top-20} 200 ${peekH}\" aria-hidden=\"true\"><path d=\"${art.d}\" fill=\"${art.colour}\" stroke=\"#2a1838\" stroke-width=\"4\" stroke-linejoin=\"round\"/>${tufts}<circle cx=\"${ex}\" cy=\"${ey}\" r=\"${er*1.7}\" fill=\"#fff8eb\" stroke=\"#2a1838\" stroke-width=\"3\"/><circle class=\"pj-pupil\" cx=\"${ex}\" cy=\"${ey}\" r=\"${er*.8}\" fill=\"#2a1838\"/></svg><i class=\"pj-lip\"></i>`;\r\n const svg=pet.querySelector('svg'),pupil=pet.querySelector('.pj-pupil'),lip=pet.querySelector('.pj-lip');\r\n const play=document.createElement('button');play.type='button';play.className='pj-play';play.textContent='PLAY';play.hidden=true;\r\n layer.append(pet,play);host.append(layer);\r\n const size={peek:[72,72*peekH/200],full:[56,56*240/200]};\r\n let state='peek',tween=null,pos={x:-99,y:-99},w=72,h=size.peek[1],game=null,raf=0,disposed=false,gamePromise=null;\r\n const to=(x,y,ms,hop=60)=>{const from={...pos};tween={from,to:{x,y},t0:performance.now(),ms:reduced?1:ms,hop};};\r\n const hostBox=()=>host.getBoundingClientRect();\r\n function full(){[w,h]=size.full;svg.setAttribute('viewBox','0 0 200 240');pet.removeChild(lip);}\r\n function frame(now){\r\n  if(disposed)return;raf=requestAnimationFrame(frame);\r\n  const hb=hostBox();\r\n  if(state==='peek'&&walker?.isConnected){const r=walker.getBoundingClientRect();pos={x:r.left-hb.left+r.width*.63-w/2,y:r.top-hb.top+r.height*.31-h+6};} // peeks out of the DR GIRLFRIEND badge pocket, up top\r\n  if(tween){const t=Math.min(1,(now-tween.t0)/tween.ms),e=ease(t);pos={x:tween.from.x+(tween.to.x-tween.from.x)*e,y:tween.from.y+(tween.to.y-tween.from.y)*e-Math.sin(t*Math.PI)*tween.hop};if(t>=1){const done=tween.then;tween=null;done?.();}}\r\n  else if(state==='right')pos.y+=Math.sin(now/260)*.35;\r\n  else if(state==='bounce'){const a=apex(),b=Math.abs(Math.sin(now/230));pos={x:a.x-w/2,y:a.y-h-b*34};}\r\n  pet.style.width=w+'px';pet.style.height=h+'px';pet.style.transform=`translate(${Math.round(pos.x)}px,${Math.round(pos.y)}px)${state==='bounce'?` scale(${1+.08*(1-Math.abs(Math.sin(now/230)))},${1-.08*(1-Math.abs(Math.sin(now/230)))})`:''}`;\r\n  // the eyeball looks at the user: toward the middle of the screen\r\n  const dx=hb.width/2-(pos.x+w/2),dy=hb.height/2-(pos.y+h/2),d=Math.hypot(dx,dy)||1;\r\n  pupil.setAttribute('cx',ex+dx/d*er*.7);pupil.setAttribute('cy',ey+dy/d*er*.7);\r\n  if(!play.hidden){const r=playAt.getBoundingClientRect(),pw=play.offsetWidth||110,ph=play.offsetHeight||44;play.style.transform=`translate(${Math.round(hb.width/2-pw/2)}px,${Math.round(r.top-hb.top-ph-14)}px)`;} // PLAY takes the camera button's job, above the TODAY/LAST MEAL switch\r\n }\r\n const home=()=>{const hb=hostBox();return {x:hb.width-w-14,y:hb.height*.56};};\r\n function hopOnPyramid(){\r\n  if(state==='peek')full();delete layer.dataset.behind;state='fly';host.dataset.jump='';pet.setAttribute('aria-label','Your paper friend bouncing on the pyramid');pet.tabIndex=-1;\r\n  const a=apex();to(a.x-w/2,a.y-h,700,80);tween.then=()=>{state='bounce';play.hidden=false;play.focus({preventScroll:true});};\r\n }\r\n function backHome(){\r\n  delete host.dataset.jump;play.hidden=true;state='fly';pet.tabIndex=0;pet.setAttribute('aria-label','Your paper friend. Tap to hop onto the pyramid');\r\n  const h0=home();to(h0.x,h0.y,700,80);tween.then=()=>{state='right';};\r\n }\r\n pet.onclick=()=>{if(state==='peek'||state==='right')hopOnPyramid();};\r\n // While peeking the pyramid canvas sits on top, so catch taps on the pet's box before the pyramid gets them.\r\n let swallow=false;const inPet=ev=>{const b=pet.getBoundingClientRect();return ev.clientX>=b.left&&ev.clientX<=b.right&&ev.clientY>=b.top&&ev.clientY<=b.bottom;};\r\n const capDown=ev=>{if(state==='peek'&&inPet(ev)){swallow=true;ev.stopPropagation();ev.preventDefault();hopOnPyramid();}};\r\n const capRest=ev=>{if(swallow){ev.stopPropagation();ev.preventDefault();if(ev.type==='click')swallow=false;}};\r\n host.addEventListener('pointerdown',capDown,true);for(const t of['pointerup','click'])host.addEventListener(t,capRest,true);\r\n play.onclick=async()=>{\r\n  if(game||gamePromise)return;\r\n  const motion=await import('../../character-phone-sensor.mjs').catch(()=>null);void motion?.requestPhoneMotion?.(); // iOS asks for tilt inside this tap\r\n  gamePromise=import('./game.mjs').then(m=>{\r\n   if(disposed)return;\r\n   layer.hidden=true;const root=document.createElement('div');root.className='pj-game';root.style.cssText='position:absolute;inset:0;z-index:7;background:#05030f;overflow:hidden';(dialog||host).append(root);\r\n   const g=m.startGame(root,{auto:true,startLevel:+new URLSearchParams(location.search).get('pjlevel')||0,recipe,meals:getMeals(),subscribeMotion:motion?.subscribePhoneMotion,onExit(){g.dispose();root.remove();game=null;layer.hidden=false;backHome();}});\r\n   game={g,root};window.__pyramidJump=g; // test hook: g.teleport(h)\r\n  }).catch(()=>{layer.hidden=false;}).finally(()=>{gamePromise=null;});\r\n };\r\n raf=requestAnimationFrame(frame);\r\n return {dispose(){if(disposed)return;disposed=true;cancelAnimationFrame(raf);host.removeEventListener('pointerdown',capDown,true);for(const t of['pointerup','click'])host.removeEventListener(t,capRest,true);game?.g.dispose();game?.root.remove();delete host.dataset.jump;layer.remove();style.remove();if(window.__pyramidJump===game?.g)delete window.__pyramidJump;}};\r\n}\r\n","arcade/quest-progress-reporter.mjs":"// Small owner-scoped retry queue for optional, online-only Vault quest writes.\r\n// Game pages remain loadable offline; bounded maxima survive until Vault can drain them.\r\nimport { performanceOwner } from '../performance-progress.mjs';\r\nimport { EXERCISES } from '../exercise-library.mjs';\r\nimport { HOLD_CAP_SECONDS, REP_CAP } from '../progression-rules.mjs';\r\nimport { powerProgress, retainedPowerStyle } from './power/power.mjs';\r\nimport { holdStyleForExercise } from './workout-effects-state.mjs';\r\n\r\nconst RULES = Object.freeze({\r\n 'pyramid-level':[1,20,true], 'pyramid-complete':[1,1,true], 'fruit-streak':[3,64,true],\r\n 'aura-tier':[1,6,true], 'blizzard-tier':[1,6,true], 'fire-tier':[1,6,true],\r\n 'lightning-tier':[1,6,true], 'yoga-hold-seconds':[1,60,false],\r\n});\r\nconst valid=(counter,value)=>{const r=RULES[counter];return !!r&&Number.isFinite(value)&&value>=r[0]&&value<=r[1]&&(!r[2]||Number.isSafeInteger(value));};\r\nconst key=owner=>`myr5-quest-pending-v1/${encodeURIComponent(owner)}`;\r\nconst IMPORT_NONCE_KEY=Symbol.for('myr5.quest-progress-import-nonce');\r\nfunction nextImportNonce(){\r\n try{const n=(Number(globalThis[IMPORT_NONCE_KEY])||0)+1;globalThis[IMPORT_NONCE_KEY]=n;return n;}\r\n catch{return `${Date.now()}-${Math.random().toString(36).slice(2)}`;}\r\n}\r\n\r\nexport function createQuestProgressReporter(ownerId,{storage=globalThis.localStorage,currentOwner=performanceOwner,load=null,importer=url=>import(url),now=()=>Date.now(),baseDelay=1000,maxDelay=30000}={}){\r\n const memoryValues={};\r\n const confirmed={};\r\n let drainPromise=null,retryAt=0,retryDelay=baseDelay,loadedModule=null,loadPromise=null,importFailures=0;\r\n const loadVault=()=>{\r\n  if(loadedModule)return Promise.resolve(loadedModule);\r\n  if(loadPromise)return loadPromise;\r\n  const url=importFailures===0?'/modules/vault/quest-progress.mjs':`/modules/vault/quest-progress.mjs?quest-retry=${nextImportNonce()}`;\r\n  const pending=Promise.resolve().then(()=>load?load():importer(url)).then(module=>{loadedModule=module;return module;}).catch(error=>{importFailures++;throw error;}).finally(()=>{loadPromise=null;});\r\n  loadPromise=pending;return pending;\r\n };\r\n const pendingNow=()=>{\r\n  let values={...memoryValues};\r\n  if(storage)try{const row=JSON.parse(storage.getItem(key(ownerId))||'{}');if(row?.ownerId===ownerId&&row.values&&typeof row.values==='object')for(const [counter,value] of Object.entries(row.values).slice(0,8))if(valid(counter,value))values[counter]=Math.max(values[counter]||0,value);}catch{}\r\n  return {ownerId,values};\r\n };\r\n const persist=values=>{\r\n  for(const k of Object.keys(memoryValues))delete memoryValues[k];\r\n  for(const [counter,value] of Object.entries(values).slice(0,8))if(valid(counter,value))memoryValues[counter]=value;\r\n  if(storage)try{if(Object.keys(memoryValues).length)storage.setItem(key(ownerId),JSON.stringify({ownerId,values:{...memoryValues}}));else storage.removeItem(key(ownerId));}catch{}\r\n };\r\n const doDrain=async()=>{\r\n  if(typeof ownerId!=='string'||!ownerId||currentOwner()!==ownerId)return false;\r\n  try{\r\n   const module=await loadVault();if(currentOwner()!==ownerId)return false;\r\n   const pending=pendingNow();\r\n   for(const [counter,value] of Object.entries(pending.values)){\r\n    module.recordQuestProgress({ownerId,counter,value});\r\n    if(currentOwner()!==ownerId)return false;\r\n    if(module.questProgressStored?.(ownerId,counter,value)){\r\n     confirmed[counter]=Math.max(confirmed[counter]||0,value);\r\n     const latest=pendingNow();if((latest.values[counter]||0)<=value)delete latest.values[counter];\r\n     persist(latest.values);\r\n    }\r\n   }\r\n   return Object.keys(pendingNow().values).length===0;\r\n  }catch{return false;}\r\n };\r\n const drain=()=>{\r\n  if(drainPromise)return drainPromise;\r\n  if(now()<retryAt)return Promise.resolve(false);\r\n  drainPromise=doDrain().then(ok=>{if(ok){retryAt=0;retryDelay=baseDelay;}else{retryAt=now()+retryDelay;retryDelay=Math.min(maxDelay,retryDelay*2);}return ok;}).catch(()=>{retryAt=now()+retryDelay;retryDelay=Math.min(maxDelay,retryDelay*2);return false;}).finally(()=>{drainPromise=null;});return drainPromise;\r\n };\r\n const record=(counter,value)=>{\r\n  if(typeof ownerId!=='string'||!ownerId||currentOwner()!==ownerId||!valid(counter,value))return false;\r\n  if(value<=(confirmed[counter]||0))return false;\r\n  const pending=pendingNow();if((pending.values[counter]||0)<value){pending.values[counter]=value;persist(pending.values);}\r\n  void drain();return true;\r\n };\r\n const recordWorkout=detail=>{\r\n  const workout=detail?.workout;\r\n  if(!workout||workout.ownerId!==ownerId||currentOwner()!==ownerId||workout.stage!=='working'||workout.paused===true)return false;\r\n  const exercise=EXERCISES[workout.mode];if(!exercise||workout.kind!==exercise.kind)return false;\r\n  if(workout.kind==='reps'&&(!Number.isFinite(workout.workingReps)||workout.workingReps<0||workout.workingReps>REP_CAP))return false;\r\n  if(workout.kind==='hold'&&(!Number.isFinite(workout.activeSeconds)||workout.activeSeconds<0||workout.activeSeconds>HOLD_CAP_SECONDS))return false;\r\n  if(workout.kind==='hold'&&exercise.group==='yoga'&&holdStyleForExercise(exercise)==='living')return record('yoga-hold-seconds',Math.min(60,Math.floor(workout.activeSeconds)));\r\n  if(workout.kind==='hold'&&['core','balance'].includes(exercise.group)&&retainedPowerStyle(exercise)==='blizzard')return record('blizzard-tier',powerProgress(workout).tier);\r\n  if(workout.kind!=='reps')return false;\r\n  const style=retainedPowerStyle(exercise),tier=powerProgress(workout).tier;\r\n  const counter=style==='aura'?'aura-tier':style==='floor'?'fire-tier':style==='lightning'?'lightning-tier':null;\r\n  return counter?record(counter,tier):false;\r\n };\r\n return Object.freeze({record,recordWorkout,drain});\r\n}\r\n\r\n/** Vault startup hook: try to drain any same-owner game milestones saved offline. */\r\nexport function drainQueuedQuestProgress(options={}){\r\n const ownerId=options.ownerId??performanceOwner();\r\n return createQuestProgressReporter(ownerId,options).drain();\r\n}\r\n","arcade/runner/clay-text.mjs":"// Rolled-out clay letters on a canvas, copied from the site (tv/assets/armie-intro/chase-ui.mjs clayText).\r\nexport function clayText(text,height=40){\r\n  let seed=[...text].reduce((a,c)=>a*31+c.charCodeAt(0)>>>0,7);const rnd=()=>(seed=seed*1664525+1013904223>>>0)/4294967296;\r\n  const dpr=Math.min(2,devicePixelRatio||1),fs=height*.74,font=`900 ${fs}px \"Arial Rounded MT Bold\",\"Arial Black\",\"Segoe UI Black\",system-ui,sans-serif`;\r\n  const c=document.createElement('canvas'),x=c.getContext('2d');x.font=font;\r\n  const letters=[...text.toUpperCase()].map(ch=>({ch,w:ch===' '?fs*.32:x.measureText(ch).width+fs*.1,r:(rnd()-.5)*.22,dy:(rnd()-.5)*fs*.1}));\r\n  const w=letters.reduce((a,l)=>a+l.w,0)+fs*.5;c.width=Math.ceil(w*dpr);c.height=Math.ceil(height*dpr);c.style.width=w+'px';c.style.height=height+'px';\r\n  x.scale(dpr,dpr);x.font=font;x.textAlign='center';x.textBaseline='middle';x.lineJoin='round';\r\n  const each=fn=>{let px=fs*.25;for(const l of letters){if(l.ch!==' '){x.save();x.translate(px+l.w/2,height/2+l.dy);x.rotate(l.r);fn(l);x.restore();}px+=l.w;}};\r\n  x.shadowColor='#000c';x.shadowBlur=fs*.12;x.shadowOffsetY=fs*.07;x.strokeStyle=x.fillStyle='#3a1a4a';x.lineWidth=fs*.27;each(l=>{x.strokeText(l.ch,0,0);x.fillText(l.ch,0,0);});\r\n  x.shadowColor='transparent';const g=x.createLinearGradient(0,height*.15,0,height*.85);g.addColorStop(0,'#b07acc');g.addColorStop(.45,'#8e5b99');g.addColorStop(1,'#5e3270');\r\n  x.strokeStyle=x.fillStyle=g;x.lineWidth=fs*.19;each(l=>{x.strokeText(l.ch,0,0);x.fillText(l.ch,0,0);});\r\n  x.strokeStyle='#d6a8ea88';x.lineWidth=fs*.035;each(l=>{x.save();x.translate(-fs*.025,-fs*.04);x.beginPath();x.rect(-l.w,-fs,l.w*2,fs*.75);x.clip();x.strokeText(l.ch,0,0);x.restore();});\r\n  x.globalCompositeOperation='source-atop';for(let i=0;i<w*1.4;i++){x.fillStyle=rnd()<.5?'#512866':'#c79ce0';x.globalAlpha=.08+rnd()*.12;x.beginPath();x.ellipse(rnd()*w,rnd()*height,1+rnd()*fs*.09,1+rnd()*fs*.05,rnd()*3,0,7);x.fill();}\r\n  x.globalAlpha=1;x.globalCompositeOperation='source-over';\r\n  let px=fs*.25;letters.forEach((l,i)=>{if(l.ch!==' '&&rnd()<.3){const bx=px+l.w*(.3+rnd()*.4),by=height*(.3+rnd()*.4),a=rnd()*3,s=fs*.07;x.save();x.translate(bx,by);x.rotate(a);x.fillStyle='#d4a017';x.fillRect(-s*1.6,-s*.35,s*3.2,s*.7);for(const ex of[-1.6,1.6])for(const ey of[-.4,.4]){x.beginPath();x.arc(ex*s,ey*s,s*.45,0,7);x.fill();}x.restore();}px+=l.w;});\r\n  return c;\r\n}\r\n","arcade/runner/coach-3d.mjs":"// Coach Armie in 3D, copied from the site (tv/assets/armie-intro/coach-3d.mjs: loader, canonical frame, buildCoach) on the app's three.\r\n// made canonical at load and given live googly eyes in code. Every caller keeps its 2D coach (sprite / SVG /\r\n// procedural hand) until the model is in, so a missing or broken GLB changes nothing.\r\n// Canonical frame: length 1 along X, centred, fingers at +X, lying PALM DOWN: back of the hand (and the eyes on its knuckles) faces +Y, Z is sideways.\r\nimport * as T from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nexport const COACH_GLB=new URL('./models/coach-hand.glb',import.meta.url).href;\r\nconst still=()=>matchMedia('(prefers-reduced-motion: reduce)').matches||!!document.querySelector('#ar.ar-motion-off');\r\nlet pending=null;\r\nexport const loadCoachParts=()=>pending??=new GLTFLoader().loadAsync(COACH_GLB).then(g=>canonical(g.scene));\r\n\r\n// Bake every mesh into the canonical frame as plain arrays, so callers on another three.js copy can rebuild it.\r\nfunction canonical(root){\r\n  root.updateMatrixWorld(true);const parts=[],all=[];\r\n  // Float copies that respect normalized (e.g. Uint16 colour/uv) attributes.\r\n  const flat=at=>at&&Float32Array.from({length:at.count*at.itemSize},(_,i)=>at.getComponent(Math.floor(i/at.itemSize),i%at.itemSize));\r\n  root.traverse(o=>{if(!o.isMesh)return;const g=o.geometry;\r\n    const pos=g.attributes.position.clone().applyMatrix4(o.matrixWorld);for(let i=0;i<pos.count;i++)all.push(new T.Vector3().fromBufferAttribute(pos,i));\r\n    const m=Array.isArray(o.material)?o.material[0]:o.material;\r\n    parts.push({name:o.name,pos,uv:flat(g.attributes.uv),color:flat(g.attributes.color),colorSize:g.attributes.color?.itemSize,index:g.index?.array,groups:g.groups,\r\n      mat:{color:m.color?.getHex()??0x8e5b99,roughness:m.roughness??.9,metalness:m.metalness??0,map:m.map?.image??null,vertexColors:!!g.attributes.color}});});\r\n  if(!all.length)throw Error('coach-hand.glb has no mesh');\r\n  const box=new T.Box3().setFromPoints(all),size=box.getSize(new T.Vector3()),c=box.getCenter(new T.Vector3());\r\n  const axis=size.x>=size.y&&size.x>=size.z?'x':size.y>=size.z?'y':'z',[a,b]=['x','y','z'].filter(k=>k!==axis),L=size[axis];\r\n  // Face end = the wider end (Ian's model: open hand with the fingers up, torn wrist and a bone below).\r\n  const spread=(lo,hi)=>{let s=0,n=0;for(const p of all){const t=(p[axis]-box.min[axis])/L;if(t>=lo&&t<=hi){s+=Math.hypot(p[a]-c[a],p[b]-c[b]);n++;}}return n?s/n:0;};\r\n  const dir=new T.Vector3();dir[axis]=spread(.85,1)>=spread(0,.15)?1:-1;\r\n  const align=new T.Quaternion().setFromUnitVectors(dir,new T.Vector3(1,0,0));\r\n  // Roll about the length so the BACK of the hand (opposite the curl of the fingers; in the file the palm is +Z) faces +Y: palm down.\r\n  const roll=new T.Quaternion().setFromAxisAngle(new T.Vector3(1,0,0),Math.PI/2);\r\n  const M=new T.Matrix4().makeScale(1/L,1/L,1/L).multiply(new T.Matrix4().makeRotationFromQuaternion(roll.multiply(align))).multiply(new T.Matrix4().makeTranslation(-c.x,-c.y,-c.z));\r\n  for(const p of parts)p.pos=p.pos.applyMatrix4(M).array;\r\n  // Plain-white single-material export: paint it as clay, with the bone (the thin end, x < -.16) gold. Vertex colours carry both.\r\n  for(const p of parts)if(!p.color&&!p.mat.map&&p.mat.color>0xe0e0e0){const clay=new T.Color(0x8e5b99),bone=new T.Color(0xd4a017),n=p.pos.length/3,col=new Float32Array(n*3);\r\n    for(let i=0;i<n;i++){const k=p.pos[i*3]<-.16?bone:clay;col.set([k.r,k.g,k.b],i*3);}\r\n    p.color=col;p.colorSize=3;p.mat.vertexColors=true;p.mat.color=0xffffff;p.mat.roughness=.9;}\r\n  return {parts,hasEyes:parts.some(p=>/pupil|eye/i.test(p.name))};\r\n}\r\n\r\n// Build the coach with the caller's three.js. tick(now,{pressure 0..1, roll}) jiggles the pupils like coach-hand.mjs.\r\nexport function buildCoach(THREE,{parts,hasEyes}){\r\n  const group=new THREE.Group();group.name='coach-armie-3d';const body=[];\r\n  for(const p of parts){const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.BufferAttribute(new Float32Array(p.pos),3));\r\n    if(p.uv)g.setAttribute('uv',new THREE.BufferAttribute(p.uv,2));\r\n    if(p.color)g.setAttribute('color',new THREE.BufferAttribute(p.color,p.colorSize));\r\n    if(p.index)g.setIndex(Array.from(p.index));(p.groups||[]).forEach(x=>g.addGroup(x.start,x.count,x.materialIndex));g.computeVertexNormals();\r\n    const mat=new THREE.MeshStandardMaterial({color:p.mat.color,roughness:p.mat.roughness,metalness:p.mat.metalness,vertexColors:p.mat.vertexColors});\r\n    if(p.mat.map){mat.map=new THREE.Texture(p.mat.map);mat.map.flipY=false;mat.map.colorSpace=THREE.SRGBColorSpace;mat.map.needsUpdate=true;}\r\n    const mesh=new THREE.Mesh(g,mat);mesh.name=p.name;group.add(mesh);body.push(mesh);}\r\n  const pupils=[];\r\n  if(!hasEyes){// big + small mismatched googly eyes (coach-hand.mjs canon ratio 10:6).\r\n    const sclera=new THREE.MeshStandardMaterial({color:0xfffdf6,roughness:.25}),ink=new THREE.MeshStandardMaterial({color:0x120a14,roughness:.3});\r\n    const ray=new THREE.Raycaster(),up=new THREE.Vector3(0,1,0);\r\n    // Ian's hand model, palm down: both eyes sit side by side on the knuckle ridge (x .27) of the back of the hand, facing +Y.\r\n    for(const [x,z,r] of [[.27,-.075,.06],[.27,.07,.038]]){\r\n      ray.set(new THREE.Vector3(x,2,z),new THREE.Vector3(0,-1,0));const hit=ray.intersectObjects(body,false)[0];\r\n      const n=hit?.face?hit.face.normal.clone().normalize():new THREE.Vector3(0,1,0);if(n.y<0)n.negate();\r\n            const eye=new THREE.Group();eye.position.copy(hit?hit.point:new THREE.Vector3(x,.15,z)).addScaledVector(n,r*.15);eye.quaternion.setFromUnitVectors(up,n);group.add(eye);\r\n      const white=new THREE.Mesh(new THREE.SphereGeometry(1,24,12),sclera);white.scale.set(r,r*.5,r);eye.add(white);\r\n      const pupil=new THREE.Mesh(new THREE.SphereGeometry(1,16,8),ink);pupil.scale.set(r*.5,r*.15,r*.5);pupil.position.y=r*.42;eye.add(pupil);\r\n      pupils.push({p:pupil,rest:pupil.position.clone(),room:r*.42,target:new THREE.Vector2()});}\r\n  }\r\n  let next=0,angle=0;\r\n  return {group,tick(now,{pressure=.3,roll=false}={}){\r\n    const quiet=still();\r\n    if(now>=next||roll){next=now+140;angle+=.9;pupils.forEach((e,i)=>{const k=quiet?.5:roll?1:(.4+pressure*.6);\r\n      if(quiet)e.target.set(k,0);else if(roll)e.target.set(Math.cos(angle+i*2.4),Math.sin(angle+i*2.4));else e.target.set((Math.random()*2-1)*k,(Math.random()*2-1)*k);});}\r\n    // Pupils ride the eye's local +Y; they slide in local X/Z.\r\n    pupils.forEach(e=>{e.p.position.x+=(e.rest.x+e.target.x*e.room-e.p.position.x)*.5;e.p.position.z+=(e.rest.z+e.target.y*e.room-e.p.position.z)*.5;});\r\n  }};\r\n}\r\nexport const loadCoach=THREE=>loadCoachParts().then(parts=>buildCoach(THREE,parts));\r\n","arcade/runner/maze-run.mjs":"// Cardio runner rules, copied from the site's Coach Armie maze (tv/assets/armie-intro/maze-run.mjs) and made endless:\r\n// three lanes, the same three full-width traps and the 90 degree turn; no doors, no questions, no tap boost.\r\n// Cool Cardio: courses chain forever, each harder (more traps, tighter spacing, combos later) at one constant speed.\r\n// A hit is a stumble, never the end: brief slow-down and coach pressure up; pressure decays as you clear traps.\r\n// Cool Cardio auto-forwards at one steady pace. Tracking loss pauses the caller safely.\r\nexport const LANES = [-1.25, 0, 1.25];\r\nexport const TRAPS = ['grid', 'platform', 'pit'];\r\nexport const PIT_LENGTH = 7;\r\nexport const STUMBLE = {seconds:.9, slow:.3, pressure:.22, clearRelief:.07, decay:.012, floor:.08};\r\nconst START = 12, RUNOUT = 14, MIN_GAP_S = 1.5;\r\nexport function rng(seed){let a=(seed*2654435761+1013904223)>>>0;return()=>{a=(a+0x6D2B79F5)>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};}\r\n// Treadmill rule (Ian, R-1017): run speed is ONE constant. It never scales with effort or level; halls get harder by traps.\r\n// JUMP is floaty: long hang time, soft gravity (pow<1 flattens the top of the arc).\r\nexport const RUN = {speed:5};\r\nexport const JUMP = {seconds:1.5, peak:1.35, float:.5};\r\nexport const courseSpeed = () => RUN.speed;\r\nexport const trapCount = level => Math.min(18, 5 + level);\r\nexport const gapSeconds = level => Math.max(MIN_GAP_S, 3.2 - .13*level);\r\nexport const comboChance = level => level < 2 ? 0 : Math.min(.55, .12*(level-1));\r\n// One course. Gaps are in seconds at that course's speed, so a full-pace runner always has the same reaction time.\r\nexport function courseFor(seed = 0, level = 0) {\r\n  const r=rng(seed), count=trapCount(level), types=[...TRAPS], speed=courseSpeed(level);\r\n  while(types.length<count) types.push(TRAPS[Math.floor(r()*3)]);\r\n  for(let i=types.length-1;i>0;i--){const j=Math.floor(r()*(i+1));[types[i],types[j]]=[types[j],types[i]];}\r\n  const events=[], turnAfter=Math.floor(count/2)-1, direction=r()<.5?'left':'right';\r\n  let at=START, prev=null;\r\n  for(let i=0;i<count;i++){\r\n    let type=types[i];\r\n    // Combo: a pit straight into a grid or platform at the minimum gap (mixed combos only on later courses).\r\n    const combo=prev==='pit'&&r()<comboChance(level);\r\n    if(combo&&type==='pit') type=r()<.5?'grid':'platform';\r\n    const e=type==='pit'?{type,at,length:PIT_LENGTH}:{type,at};if(combo)e.combo=true;\r\n    events.push(e);\r\n    at+=(e.length||0)+speed*((combo||events.length>1&&events[events.length-2].combo)?MIN_GAP_S:gapSeconds(level)*(1+r()*.25));\r\n    if(i===turnAfter){events.push({type:'turn',at,direction});at+=speed*gapSeconds(level);}\r\n    prev=type;\r\n  }\r\n  // Combo gap applies to the trap after the pit; recompute positions so it is exact.\r\n  for(let i=1;i<events.length;i++){const e=events[i],p=events[i-1];if(e.combo)e.at=p.at+(p.length||0)+speed*MIN_GAP_S;else if(e.at<p.at+(p.length||0)+speed*MIN_GAP_S)e.at=p.at+(p.length||0)+speed*MIN_GAP_S;}\r\n  const last=events[events.length-1], turn=events.find(e=>e.type==='turn');\r\n  return {seed, level, events, length:last.at+(last.length||0)+RUNOUT, turnAt:turn.at, direction};\r\n}\r\nconst courseSeed=(seed,n)=>(seed+n*977)>>>0;\r\nfunction load(run){\r\n  run.course=courseFor(courseSeed(run.seed,run.courses),run.level);\r\n  run.events=run.course.events.map(e=>({...e,cleared:false}));run.turn=null;\r\n}\r\nexport function createRun(seed = 0) {\r\n  const run={seed, courses:0, level:0, distance:0, total:0, time:0, lane:1, jumpTime:0, height:0, slideTime:0, duck:0, turn:null,\r\n    started:true, stumble:0, pressure:STUMBLE.floor, hits:0, cleared:0, paused:false, status:'running', feed:[], course:null, events:[]};\r\n  load(run);return run;\r\n}\r\n// Next course in the chain, one step harder.\r\nexport function chain(run) {\r\n  run.courses++;run.level++;load(run);run.distance=0;run.feed.push({type:'course',level:run.level,courses:run.courses});\r\n}\r\nexport function runSpeed(run){\r\n  return courseSpeed(run.level)*(run.stumble>0?STUMBLE.slow:1);\r\n}\r\nexport function upcoming(run) { return run.events.find(e=>!e.cleared) || null; }\r\nexport function input(run, action) {\r\n  if(run.status !== 'running' || run.paused) return;\r\n  if(action === 'jump') { if(run.jumpTime === 0 && run.slideTime === 0) run.jumpTime = JUMP.seconds; return; }\r\n  if(action === 'slide') { if(run.jumpTime === 0 && run.slideTime === 0) run.slideTime = .96; return; }\r\n  if(action !== 'left' && action !== 'right') return;\r\n  const next=upcoming(run);\r\n  if(next?.type==='turn' && next.at-run.distance <= 11) run.turn=action;\r\n  else run.lane=Math.max(0,Math.min(LANES.length-1,run.lane+(action==='left'?-1:1)));\r\n}\r\nexport function safe(run, event) {\r\n  if(event.type==='turn') return run.turn===event.direction;\r\n  if(event.type==='grid') return run.duck>.8 && run.height<.1;\r\n  if(event.type==='platform') return run.height>.65;\r\n  return run.lane!==1; // pit: only the two wall ledges hold you\r\n}\r\n// A stumble from a trap or an unhit projectile: slow-down and pressure, never the end.\r\nexport function stumble(run, event) {\r\n  if(run.status!=='running')return;\r\n  event.cleared=true;run.hits++;run.stumble=STUMBLE.seconds;run.pressure=Math.min(1,run.pressure+STUMBLE.pressure);\r\n  if(event.type==='turn')run.turn=null;\r\n  run.feed.push({type:'stumble',trap:event.type,pressure:run.pressure});\r\n}\r\nexport function step(run, delta) {\r\n  if(run.paused||run.status!=='running')return;\r\n  const dt=Math.max(0,Math.min(delta,.05));\r\n  run.time+=dt;\r\n  run.jumpTime=Math.max(0,run.jumpTime-dt);\r\n  run.slideTime=Math.max(0,run.slideTime-dt);\r\n  run.stumble=Math.max(0,run.stumble-dt);\r\n  run.pressure=Math.max(STUMBLE.floor,run.pressure-STUMBLE.decay*dt);\r\n  run.duck=run.slideTime>0?Math.min(1,(.96-run.slideTime)/.1,run.slideTime/.12):0;\r\n  run.height=run.jumpTime>0?Math.pow(Math.sin((1-run.jumpTime/JUMP.seconds)*Math.PI),JUMP.float)*JUMP.peak:0;\r\n  const next=upcoming(run), move=runSpeed(run)*dt;\r\n  run.distance+=move;run.total+=move;\r\n  // The pit is checked on every step while you are over it, not only at its lip.\r\n  if(next && run.distance>=next.at) {\r\n    if(!safe(run,next)) stumble(run,next);\r\n    else if(run.distance>=next.at+(next.length||0)){next.cleared=true;run.cleared++;run.pressure=Math.max(STUMBLE.floor,run.pressure-STUMBLE.clearRelief);if(next.type==='turn') run.turn=null;run.feed.push({type:'clear',trap:next.type});}\r\n  }\r\n  if(run.distance>=run.course.length)chain(run);\r\n}\r\n// World route turns a real 90 degrees at the course's turn, then continues down the next corridor.\r\nexport function pathAt(distance, course) {\r\n  const d=Math.max(0,distance),T=course.turnAt;\r\n  if(T==null||d<=T)return {x:0,z:-d,yaw:0};\r\n  const sign=course.direction==='right'?1:-1;\r\n  return {x:sign*(d-T),z:-T,yaw:-sign*Math.PI/2};\r\n}\r\n","arcade/runner/pose-input.mjs":"// Body -> runner controls. Auto-forward is independent of movement; pose input steers, crouches and jumps.\r\n// Upper body landmarks steer the three lanes; both arms overhead jumps, a deep crouch slides, and a lean changes lanes.\r\n// Every number a real treadmill test might need to move lives in POSE_TUNING. Distances are in shoulder widths.\r\nexport const POSE_TUNING={\r\n  minVisibility:.5,\r\n  bob:{min:.06,maxHalf:.8,smooth:.04}, // legacy workout XP detector only; never gates runner movement\r\n  calibrate:1.2,     // s of neutral head/neck samples for the baseline\r\n  adapt:3,           // s time constant: the baseline follows slow drift on the belt (paused while an action is held)\r\n  smooth:.1,         // s smoothing on drop / shoulder and lean signals\r\n  jump:{\r\n    armAbove:.15,    // wrist this far above its shoulder (or elbow above the shoulder when the wrist is out of frame)\r\n    dwell:.08,cooldown:.9\r\n  },\r\n  crouch:{\r\n    drop:.45,        // head and shoulders below the neutral baseline\r\n    rearm:.2,dwell:.1,cooldown:.7\r\n  },\r\n  lean:{\r\n    in:.3,           // shoulder tilt + shoulder-mid offset from hip-mid (hips optional)\r\n    out:.12,dwell:.12,cooldown:.5,\r\n    flip:false       // raw camera frames are unmirrored: the player's right is image-left. Flip if a device disagrees.\r\n  },\r\n  settle:.3,         // s after any action before a different action can fire (arms dropping, standing up)\r\n  side:{\r\n    out:.62,         // stepping ankle: lateral distance from the hip midline, in torso lengths\r\n    plant:.38,       // planted ankle must stay this close to the midline\r\n    rearm:.42,       // both ankles back inside this before the next side step can fire\r\n    kneeOut:.42,kneePlant:.28,kneeRearm:.3, // same three with knees (feet out of frame)\r\n    outScale:2.2,    // calibrated standing half-stance x this, if larger than `out`\r\n    dwell:.12,       // s the side-step shape must hold (one frame is enough below 8 updates/s)\r\n    cooldown:.45,    // s between side steps\r\n    minVisibility:.5,\r\n    calibrate:.6,    // s of steady standing to learn the stance width\r\n    flip:false\r\n  }\r\n};\r\nconst median=a=>{const s=[...a].sort((x,y)=>x-y);return s[Math.floor(s.length/2)];};\r\nconst ema=(old,v,dt,tau)=>old===null?v:old+(v-old)*(1-Math.exp(-Math.max(dt,.001)/tau));\r\n// Shared upper-body frame: null unless nose and both shoulders are clear.\r\nfunction upper(p,aspect,minVis){\r\n  if(!p||p.length<17)return null;\r\n  const ok=i=>p[i]&&Number.isFinite(p[i].x)&&Number.isFinite(p[i].y)&&(p[i].visibility??1)>=minVis;\r\n  if(![0,11,12].every(ok))return null;\r\n  const X=i=>p[i].x*aspect,sw=Math.hypot(X(11)-X(12),p[11].y-p[12].y);if(sw<.03)return null;\r\n  return {ok,X,sw,sx:(X(11)+X(12))/2,sy:(p[11].y+p[12].y)/2,ny:p[0].y};\r\n}\r\n// Retained for legacy exercise XP counting. Cool Cardio itself never uses a bob as a movement gate.\r\nexport function createHeadBob(tuning=POSE_TUNING){\r\n  let v=null,ext=null,dir=1,lastRev=-Infinity,last=null;\r\n  return {update(points,t,aspect=1){\r\n    const T=tuning.bob,f=upper(points,aspect,tuning.minVisibility);if(!f)return false;\r\n    const dt=last===null?0:t-last;last=t;v=ema(v,(f.ny+f.sy)/2,dt,T.smooth);\r\n    const h=T.min*f.sw;if(ext===null){ext=v;return false;}\r\n    let rev=0;if(dir*(v-ext)>0)ext=v;else if(Math.abs(v-ext)>=h){rev=dir;dir=-dir;ext=v;}\r\n    if(!rev)return false;const quick=t-lastRev<=Math.max(T.maxHalf,3*dt);lastRev=t;return quick;\r\n  }};\r\n}\r\n// Jump / slide / lean from the upper body, with a moving-safe baseline, smoothing, hysteresis, dwell and cooldowns.\r\nexport function createUpperBody(tuning=POSE_TUNING){\r\n  let last=null,calib=[],base=null,drop=null,shrug=null,lean=null,crouching=false,lastAct=-Infinity,lastKind='',leanResetSerial=0;\r\n  const st={jump:{armed:true,since:null,fired:-Infinity},slide:{armed:true,since:null,fired:-Infinity},lean:{armed:true,since:null,fired:-Infinity,dir:null,firedDir:null}};\r\n  const hold=(s,on,t,dt,T,kind)=>{\r\n    if(!on){s.since=null;return false;}\r\n    if(s.since===null)s.since=t-dt/2;\r\n    if((t-s.since>=T.dwell||dt>=.125)&&t-s.fired>=T.cooldown&&(lastKind===kind||t-lastAct>=tuning.settle)){s.fired=t;s.armed=false;s.since=null;lastAct=t;lastKind=kind;return true;}\r\n    return false;\r\n  };\r\n  return {get leanResetSerial(){return leanResetSerial},get calibrated(){return !!base},get baseline(){return base},get crouching(){return crouching},\r\n    signals:()=>({drop,shrug,lean}),\r\n    update(points,t,aspect=1){\r\n      const T=tuning,f=upper(points,aspect,T.minVisibility);if(!f)return null;\r\n      const dt=last===null?0:Math.min(.5,t-last);last=t;\r\n      const head=(f.ny+f.sy)/2,neck=(f.sy-f.ny)/f.sw;\r\n      if(!base){calib.push({t,head,neck});calib=calib.filter(c=>t-c.t<=T.calibrate*2);\r\n        if(calib.length>=5&&t-calib[0].t>=T.calibrate)base={head:median(calib.map(c=>c.head)),neck:median(calib.map(c=>c.neck))};\r\n        return null;}\r\n      const p=points,J=T.jump;\r\n      const arm=(s,e,w)=>f.ok(w)?p[w].y<p[s].y-J.armAbove*f.sw:f.ok(e)&&p[e].y<p[s].y;\r\n      const armsUp=arm(11,13,15)&&arm(12,14,16);\r\n      const hips=f.ok(23)&&f.ok(24),offset=hips?((f.X(23)+f.X(24))/2-f.sx)/f.sw:0,tilt=(p[12].y-p[11].y)/f.sw;\r\n      drop=ema(drop,(head-base.head)/f.sw,dt,T.smooth);shrug=ema(shrug,base.neck-neck,dt,T.smooth);lean=ema(lean,offset+tilt,dt,T.smooth);\r\n      const actions=[],C=T.crouch,L=T.lean;\r\n      if(crouching?drop<C.rearm:drop>=C.drop)crouching=!crouching;\r\n      // Both arms clearly overhead is the complete jump cue.\r\n      if(!armsUp)st.jump.armed=true;\r\n      if(st.jump.armed&&hold(st.jump,armsUp,t,dt,J,'jump'))actions.push('jump');\r\n      // Slide: head and shoulders well below the running baseline.\r\n      if(drop<C.rearm)st.slide.armed=true;\r\n      if(st.slide.armed&&hold(st.slide,!armsUp&&drop>=C.drop,t,dt,C,'slide'))actions.push('slide');\r\n      // Runner-only edge mode accepts a deliberate reversal even when a slow\r\n      // tracker skips upright. Neutral uses the current sample so a short reset\r\n      // is not swallowed by smoothing; raw+filtered gates prevent residual drift\r\n      // from counting as a second gesture on that neutral frame.\r\n      const rawLean=offset+tilt,edgeMode=L.reverseRearm===true;\r\n      if(Math.abs(edgeMode?rawLean:lean)<L.out){if(!st.lean.armed)leanResetSerial++;st.lean.armed=true;}\r\n      const dir=(lean>0)!==L.flip?'right':'left';\r\n      if(st.lean.dir!==dir){st.lean.dir=dir;st.lean.since=null;}\r\n      const leaning=!armsUp&&drop<C.rearm&&Math.abs(lean)>=L.in&&(!edgeMode||(Math.abs(rawLean)>=L.in&&rawLean*lean>0));\r\n      if(edgeMode&&leaning&&st.lean.firedDir&&st.lean.firedDir!==dir)st.lean.armed=true;\r\n      if(st.lean.armed&&hold(st.lean,leaning,t,dt,L,'lean')){actions.push(dir);st.lean.firedDir=dir;}\r\n      // Baseline follows slow belt drift only while nothing is held.\r\n      if(!armsUp&&drop<C.rearm&&Math.abs(lean)<L.out){base.head=ema(base.head,head,dt,T.adapt);base.neck=ema(base.neck,neck,dt,T.adapt);}\r\n      return actions;\r\n    }};\r\n}\r\n// Side-step detector on raw landmarks (ankles 27/28, knees 25/26, hips 23/24, shoulders 11/12).\r\nexport function createSideStep(tuning=POSE_TUNING.side){\r\n  let armed=true,since=null,dir=null,lastFire=-Infinity,last=null,stance=null,calib=[];\r\n  const d={get armed(){return armed},stance:()=>stance,update(points,t,aspect=1){\r\n    const T=tuning,p=points;if(!p||p.length<29)return null;\r\n    const ok=i=>p[i]&&Number.isFinite(p[i].x)&&Number.isFinite(p[i].y)&&(p[i].visibility??1)>=T.minVisibility;\r\n    if(![11,12,23,24].every(ok))return null;\r\n    const X=i=>p[i].x*aspect,hx=(X(23)+X(24))/2,hy=(p[23].y+p[24].y)/2,sx=(X(11)+X(12))/2,sy=(p[11].y+p[12].y)/2;\r\n    const torso=Math.hypot(hx-sx,hy-sy);if(torso<.03)return null;\r\n    const feet=ok(27)&&ok(28),knees=ok(25)&&ok(26);if(!feet&&!knees){since=null;return null;}\r\n    const [a,b]=feet?[27,28]:[25,26],out=feet?T.out:T.kneeOut,plant=feet?T.plant:T.kneePlant,rearm=feet?T.rearm:T.kneeRearm;\r\n    const oa=(X(a)-hx)/torso,ob=(X(b)-hx)/torso,wide=Math.max(Math.abs(oa),Math.abs(ob)),narrow=Math.min(Math.abs(oa),Math.abs(ob));\r\n    const dt=last===null?0:t-last;last=t;\r\n    // Short calibration: the standing half-stance (feet only) widens the out threshold for wide-stance players.\r\n    if(feet&&stance===null){if(wide<rearm*1.4){calib.push({t,w:wide});calib=calib.filter(c=>t-c.t<=T.calibrate*2);if(calib.length>=3&&t-calib[0].t>=T.calibrate)stance=median(calib.map(c=>c.w));}else calib=[];}\r\n    const need=feet&&stance!==null?Math.max(out,stance*T.outScale):out;\r\n    if(!armed){if(wide<rearm){armed=true;since=null;}return null;}\r\n    if(wide>=need&&narrow<=plant){\r\n      const o=Math.abs(oa)>Math.abs(ob)?oa:ob,next=(o<0)!==T.flip?'right':'left';\r\n      if(dir!==next){dir=next;since=t-dt/2;}\r\n      // Hold for `dwell`, or a single sample at phone rates where frames are further apart than the dwell.\r\n      if((t-since>=T.dwell||dt>=.125)&&t-lastFire>=T.cooldown){armed=false;lastFire=t;since=null;return next;}\r\n    }else{since=null;dir=null;}\r\n    return null;\r\n  }};\r\n  return d;\r\n}\r\nexport function createPoseInput({tuning=POSE_TUNING}={}){\r\n  const body=createUpperBody(tuning),side=createSideStep(tuning.side);\r\n  let lastLane=-Infinity,lastLaneSource='',lastLaneDir=null,lastSideLeanReset=0;\r\n  return {get calibrated(){return body.calibrated},body,\r\n    update({points,world=null,width=1,height=1,now}){\r\n      const actions=[];if(!points||!Number.isFinite(now))return {actions};\r\n      const t=now/1000,aspect=width/Math.max(1,height);\r\n      const dir=side.update(points,t,aspect);\r\n      let leaned=false;\r\n      for(const a of body.update(points,t,aspect)||[]){\r\n        if(a==='left'||a==='right'){\r\n          // The side-step and torso detectors may describe the same gesture on\r\n          // adjacent samples. Suppress that duplicate, while an opposite lean\r\n          // or a freshly rearmed lean remains a new, intentional edge.\r\n          if(tuning.lean.reverseRearm&&lastLaneSource==='side'&&a===lastLaneDir&&body.leanResetSerial===lastSideLeanReset&&!side.armed)continue;\r\n          lastLane=t;lastLaneSource='lean';lastLaneDir=a;leaned=true;\r\n        }\r\n        actions.push(a);\r\n      }\r\n      if(dir&&!leaned&&t-lastLane>=tuning.side.cooldown){lastLane=t;lastLaneSource='side';lastLaneDir=dir;lastSideLeanReset=body.leanResetSerial;actions.push(dir);}\r\n      return {actions};\r\n    }};\r\n}\r\n","arcade/runner/roster-coach.mjs":"import * as T from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\n\r\nconst MODEL=new URL('../../creature/models/roster/01-seed-pearo--cute_alien_figure_3d_model.glb',import.meta.url).href;\r\nconst BADGE=new URL('../../pod/mom-inc-mark-96.webp',import.meta.url).href;\r\nlet shared=null,meshoptReady;\r\n\r\nfunction loadRosterAlien(){\r\n  if(shared)return shared;\r\n  const loader=new GLTFLoader();\r\n  meshoptReady??=import('three/addons/libs/meshopt_decoder.module.js').then(m=>loader.setMeshoptDecoder(m.MeshoptDecoder)).catch(()=>{});\r\n  shared=meshoptReady.then(()=>loader.loadAsync(MODEL)).then(({scene})=>{\r\n    const box=new T.Box3().setFromObject(scene),size=box.getSize(new T.Vector3()),center=box.getCenter(new T.Vector3());\r\n    scene.position.set(-center.x,-box.min.y,-center.z);\r\n    scene.traverse(node=>{if(!node.isMesh)return;node.material=node.material.clone();node.material.color.lerp(new T.Color('#65b5a1'),.28);node.material.roughness=.82;});\r\n    const rig=new T.Group();rig.add(scene);rig.scale.setScalar(1.9/Math.max(.01,size.y));return rig;\r\n  }).catch(error=>{shared=null;console.warn('runner roster coach',error);return null;});\r\n  return shared;\r\n}\r\n\r\n/** Mount the existing Hive Escape roster alien, dressed as the themed MOM Inc. pursuer. */\r\nexport function loadRunnerCoach(T3,host,worldIndex,onReady=()=>{}){\r\n  const alienRoot=new T.Group();alienRoot.name='mom-inc-roster-alien';\r\n  alienRoot.userData.rosterModel='roster/01-seed-pearo--cute_alien_figure_3d_model';\r\n  host.add(alienRoot);\r\n  const palette=['#a971c5','#4b8465','#438d91'],color=palette[worldIndex]||palette[0];\r\n  const suit=new T.Mesh(new T.BoxGeometry(.72,.45,.52),new T.MeshToonMaterial({color}));\r\n  suit.position.set(0,.77,0);alienRoot.add(suit);\r\n  const badge=new T.Sprite(new T.SpriteMaterial({map:new T.TextureLoader().load(BADGE),transparent:true,depthWrite:false}));\r\n  badge.scale.set(.34,.34,1);badge.position.set(0,.78,.275);alienRoot.add(badge);\r\n  loadRosterAlien().then(rig=>{\r\n    if(!rig||!host.parent)return;\r\n    const model=rig.clone(true);model.traverse(node=>{if(node.isMesh)node.userData.rosterShared=true;});alienRoot.add(model);alienRoot.userData.model=model;onReady(alienRoot);\r\n  });\r\n  return alienRoot;\r\n}\r\n","arcade/runner/runner.mjs":"import {gameShellPhase} from '../shell-phase.mjs';\r\nimport {visualCue,setVisualCue,VISUAL_CSS} from '../visual-language.mjs';\r\n// Cool Cardio uses the app's existing pose stream and owns no camera or exercise-XP timer.\r\nimport * as THREE from 'three';\r\nimport {createRun,step,stumble} from './maze-run.mjs';\r\nimport {createPoseInput,POSE_TUNING} from './pose-input.mjs';\r\nimport {distPointSeg} from '../slice/core.mjs';\r\nimport {createThrows,createStriker,mountThrowsView,THROW} from './throws.mjs';\r\nimport {createWorldRun,advance,act,resolveThrowMiss,emitRunEvent,punchCourse,WORLDS} from './worlds/worlds.mjs';\r\nimport {jumpHeight,courseHazards} from './worlds/course.mjs';\r\n// Private runner tuning: exercise detectors and the app camera retain their original settings.\r\nexport const RUNNER_POSE_TUNING={...POSE_TUNING,smooth:.045,settle:.12,lean:{...POSE_TUNING.lean,in:.2,out:.09,dwell:.05,cooldown:.06,reverseRearm:true}};\r\nimport {createWorldScene} from './worlds/scene.mjs';\r\nimport {CSS} from './worlds/host-style.mjs';\r\nimport {playGamePortal,gamePortalReady} from '../game-portal.mjs';\r\nconst powerIcon=id=>({magnet:'magnet',shield:'shield',recovery:'shield',doubleCoins:'coin',coinTier:'coin',scoreBoost:'star',jetpack:'jetpack',superJump:'boot',board:'board',phase:'ring',pogo:'boot'})[id]||'speed';\r\nconst NAMES={magnet:'Coin magnet',shield:'Relic shield',doubleCoins:'Double coins',boost:'Dash aura',jetpack:'Jetpack',superJump:'Jump shoes',board:'Hoverboard',headstart:'Head start',scoreBoost:'Score x2',phase:'Phase aura',pogo:'Pogo launch',recovery:'Recovery wings',coinTier:'Triple-value coins'};\r\nconst EXTRA=`.runner-score{display:flex;align-items:center;gap:6px;align-self:flex-start}.runner-score strong{font:800 18px system-ui}.runner-score .game-symbol{width:26px;height:26px}.runner-vortex .game-cue{position:relative;z-index:1}.runner-vortex .game-symbol{width:80px;height:80px}.runner-hud{position:absolute;z-index:4;top:calc(env(safe-area-inset-top,0px) + 88px);left:10px;right:10px;display:flex;justify-content:space-between;gap:8px;color:#fff;font:700 12px system-ui;text-shadow:0 2px 5px #261736;pointer-events:none}.runner-hud b{display:block;font-size:14px}.runner-hud select{pointer-events:auto;max-width:155px;background:#281c3bd9;color:#fff;border:1px solid #cda5f0;border-radius:8px;padding:8px;font:600 12px system-ui;min-height:36px}.runner-buffs{position:absolute;z-index:3;top:calc(env(safe-area-inset-top,0px) + 202px);left:10px;display:grid;gap:3px;pointer-events:none;color:#fff;font:600 11px system-ui}.runner-buffs span{border-left:3px solid #e8c475;background:#281c3bba;border-radius:0 5px 5px 0;padding:4px 7px}.runner-vortex{position:absolute;z-index:5;inset:0;display:grid;place-content:center;background:radial-gradient(ellipse,#fff 0,#ecc0ff 4%,#8843ce 18%,#1d0d45 60%,#080416);overflow:hidden;color:#fff;font:800 17px system-ui;letter-spacing:4px}.runner-vortex[hidden]{display:none}.runner-vortex i{position:absolute;inset:-75%;border-radius:50%;background:repeating-conic-gradient(transparent 0 12deg,#f4c8ff70 13deg 15deg,transparent 16deg 30deg);animation:runner-vortex 1.8s linear infinite}.runner-vortex b{z-index:1;text-shadow:0 2px 8px #251336}@keyframes runner-vortex{to{transform:rotate(360deg)}}.runner-stopped{z-index:6!important}.runner-say{top:auto!important;bottom:23%;font:700 15px system-ui;color:#fff;text-align:center;text-shadow:0 2px 5px #1b0d2b}.runner-say span{background:#261a38d9;border:1px solid #dfb5ef66;border-radius:10px;padding:10px 14px}@media(prefers-reduced-motion:reduce){.runner-vortex i{animation:none}}`;\r\nconst HUD_POLISH=`.runner-level{display:grid;justify-items:start;gap:5px;max-width:52%;padding:7px 9px;border:1px solid #f5d9b655;border-radius:12px;background:#20152fc7;backdrop-filter:blur(5px)}.runner-level>b{font-size:13px;letter-spacing:.02em}.runner-level .game-meter{width:min(155px,42vw);height:5px;opacity:.88}.runner-score{gap:4px;padding:5px 8px;border:1px solid #f5d9b655;border-radius:12px;background:#20152fc7;backdrop-filter:blur(5px)}.runner-score strong{font-size:15px}.runner-score .game-symbol{width:20px;height:20px}.runner-score small{font-size:10px}.runner-buffs{top:calc(env(safe-area-inset-top,0px) + 168px)}.runner-buffs span{background:#281c3be8}`;\r\nexport function mountRunner({host,Session,sound=()=>{},onXp=()=>{},onActive=()=>{},onCaught=()=>{},seed=Date.now()>>>0,ownerId='guest'}){\r\n const shell=gameShellPhase('runner',host);\r\n let bonusXp=0;\r\n const key=`myr5-cardio-worlds-v1:${String(ownerId||'guest')}`;let unlocked=0;try{unlocked=Number(localStorage.getItem(key))||0;}catch{}\r\n let worldRun=createWorldRun({unlocked});const run=createRun(seed),body=createPoseInput({Session,tuning:RUNNER_POSE_TUNING});run.events=[];run.course.length=Infinity;run.paused=true;\r\n const el=(tag,cls)=>{const e=document.createElement(tag);if(cls)e.className=cls;return e;};\r\n const style=el('style');style.textContent=CSS+EXTRA+HUD_POLISH+VISUAL_CSS;document.head.append(style);\r\n const layer=el('div','runner-layer');layer.setAttribute('aria-hidden','true');const canvas=el('canvas','runner-view'),flash=el('div','runner-flash');layer.append(canvas,flash);\r\n const hud=el('div','runner-hud'),info=el('div','runner-level'),title=el('b'),route=el('small'),replay=el('select'),score=el('div','runner-score'),buffs=el('div','runner-buffs'),say=el('div','runner-say'),stopped=el('div','runner-stopped'),vortex=el('div','runner-vortex');\r\n const progress=el('progress','game-meter'),legMark=el('span');progress.max=600;progress.setAttribute('aria-label','Route distance');legMark.style.display='block';\r\n replay.setAttribute('aria-label','Replay unlocked area');route.className='game-sr';info.append(title,replay,legMark,progress,route);hud.append(info,score);say.setAttribute('role','status');say.hidden=true;stopped.setAttribute('role','status');stopped.hidden=true;stopped.append(visualCue('tracking','Tracking paused. Move back into the camera frame.'));stopped.firstChild.classList.add('game-status-symbol');vortex.hidden=true;vortex.innerHTML='<i></i>';vortex.append(visualCue('portal','Travelling between worlds'));\r\n host.prepend(layer);host.append(hud,buffs,say,stopped,vortex);host.classList.add('runner-on');\r\n let controlsShown=false;let viaPortal=false,disposed=false,tracking=false,crouching=false,lastActive=-Infinity,raf=0,last=0,lastPose=-Infinity,hideTimer=0,seenWorld=-1,seenLoop=-1,seenLeg=-1,seenPickup=0,seenEvent=0,lastBuffs='',frameDt=0,frameNow=0,throws,view;\r\n const creditActive=now=>{if(now-lastActive>=350){lastActive=now;onActive();}};\r\n const at=(d,x=0,y=0)=>new THREE.Vector3(x*2.8,(worldRun.elevation||0)+y,-d),posOf=o=>at(o.d,o.x,o.y);\r\n const maze=createWorldScene(canvas,{beforeRender:(scene,camera)=>{if(view&&throws)view.update(throws,posOf,at(THROW.from+1,0,.9),camera,run.paused?0:frameDt,frameNow);}});\r\n view=mountThrowsView(THREE,maze.scene,{showCoach:false});const strike=createStriker();\r\n function show(text,ms=2200,kind){clearTimeout(hideTimer);const span=kind?visualCue(kind,text):el('span');if(!kind)span.textContent=text;say.replaceChildren(span);say.hidden=false;hideTimer=setTimeout(()=>say.hidden=true,ms);}\r\n function controls(){const row=el('div','game-cue-row');row.append(visualCue('lean','Lean or step left and right to steer'),visualCue('crouch','Crouch to slide'),visualCue('raise','Raise both arms to jump or use the portal'));say.replaceChildren(row);say.hidden=false;clearTimeout(hideTimer);hideTimer=setTimeout(()=>say.hidden=true,5000);}\r\n throws=createThrows({seed,onEvent:(type,d)=>{if(type==='hit'){view.hit(d,posOf(d));emitRunEvent(worldRun,'punch',{id:'throw-'+d.id,kind:'throw',lane:d.x,distance:worldRun.distance+d.d,result:'destroyed'});sound(d.kind==='slice'?'ice':'wood');}else if(type==='bonus'){bonusXp+=Number(d.xp)||0;onXp(d.xp);sound('dial');}else if(type==='miss')resolveThrowMiss(worldRun,d);}});\r\n const screen=o=>{const v=posOf(o),depth=v.distanceTo(maze.camera.position);v.project(maze.camera);if(v.z>1||depth<.2)return null;return {x:(v.x+1)/2*(host.clientWidth||innerWidth)/(host.clientHeight||innerHeight),y:(1-v.y)/2,r:o.r/(depth*2*Math.tan(maze.camera.fov*Math.PI/360))};};\r\n function updateReplay(){replay.replaceChildren(...WORLDS.map((w,i)=>{const o=el('option');o.value=String(i);o.disabled=i>worldRun.unlocked;o.textContent=`${i>worldRun.unlocked?'Locked: ':''}${w.name}`;return o;}));replay.value=String(worldRun.world);}\r\n replay.onchange=()=>{const i=Number(replay.value);if(i>worldRun.unlocked||!Number.isInteger(i)||i<0||i>2)return;worldRun=createWorldRun({unlocked:worldRun.unlocked});worldRun.world=i;seenWorld=-1;seenPickup=0;seenEvent=0;seenLeg=-1;throws.clear();view.clear();show(`${WORLDS[i].name}. Keep moving.`,2200,'speed');};\r\n function reflect(){run.lane=worldRun.lane+1;run.jumpTime=worldRun.jump;run.slideTime=worldRun.slide;run.height=(worldRun.elevation||0)+jumpHeight(worldRun);run.duck=worldRun.slide>0?1:0;run.distance=worldRun.distance;run.total=worldRun.totalDistance;run.level=worldRun.loop;run.courses=worldRun.loop*6+worldRun.world*2+worldRun.leg;}\r\n function frame(now){\r\n  if(disposed)return;raf=requestAnimationFrame(frame);frameDt=last?Math.min(.25,(now-last)/1000):0;last=now;frameNow=now;\r\n  if(now-lastPose>500){tracking=false;run.paused=true;}\r\n  for(let left=run.paused?0:frameDt;left>1e-6;left-=.05){const dt=Math.min(.05,left);worldRun.slide=crouching?Math.max(worldRun.slide,.1):0;step(run,dt);if(run.status==='running')advance(worldRun,dt,{moving:true,pace:run.stumble>0?.3:1});throws.step(dt,12,run.status==='running'&&worldRun.phase==='run',run.level);}\r\n  shell.phase(!document.hidden&&!run.paused&&run.status==='running'&&worldRun.phase==='run'?'active':'paused');\r\n  reflect();\r\n  if(seenWorld!==worldRun.world||seenLoop!==worldRun.loop||seenLeg!==worldRun.leg){const firstWorld=seenWorld===-1;seenWorld=worldRun.world;seenLoop=worldRun.loop;seenLeg=worldRun.leg;throws.clear();view.clear();updateReplay();try{localStorage.setItem(key,String(worldRun.unlocked));}catch{}if(!firstWorld)show(`${WORLDS[worldRun.world].name} - ${worldRun.leg?'Return route - mirrored':'Outbound route'}${worldRun.loop?` - Circuit ${worldRun.loop+1}`:''}`,2200,worldRun.leg?'return':'speed');sound('lever');}\r\n  if((worldRun.pickupSerial||0)!==seenPickup){seenPickup=worldRun.pickupSerial||0;if(worldRun.lastPickup){const reward=worldRun.lastPickup==='headstart'?'Head start · toward 150 m':worldRun.lastPickup==='pogo'?'+5 coins · pogo launch':`${NAMES[worldRun.lastPickup]}!`;show(reward,2200,powerIcon(worldRun.lastPickup));sound('dial');}}\r\n  for(const event of worldRun.events.filter(e=>e.serial>seenEvent)){\r\n    seenEvent=event.serial;\r\n    if(event.type==='hit'&&event.result==='stumble'){stumble(run,{type:event.kind||event.type});flash.classList.remove('on');void flash.offsetWidth;flash.classList.add('on');show('Bump! Your pursuer gets closer.',2200,'impact');sound('jelly');}\r\n    else if(event.type==='hit'&&event.result==='absorbed')show('Protection absorbed the hit.',2200,'shield');\r\n    else if(event.type==='dodge')show('Clear dodge!',850,'check');\r\n    else if(event.type==='punch')show('Smashed!',850,'punch');\r\n  }\r\n  if(tracking&&!controlsShown){controlsShown=true;controls();}\r\n  worldRun.chase=run.pressure;worldRun.pressure=run.pressure;worldRun.bumpCount=worldRun.stumbles;stopped.hidden=tracking; if(worldRun.phase==='wormhole'&&!viaPortal&&gamePortalReady()){viaPortal=true;void playGamePortal({host,swap:()=>new Promise(r=>{const t=setInterval(()=>{if(disposed||worldRun.phase!=='wormhole'){clearInterval(t);r();}},50);})}).finally(()=>{viaPortal=false;});}\r\n  vortex.hidden=worldRun.phase!=='wormhole'||viaPortal;\r\n  if(worldRun.phase==='portal'&&say.hidden)show(worldRun.leg===0?'Halfway! Raise both arms to return through the portal.':'Raise both arms to enter the next world.',4000,'raise');\r\n  progress.value=Math.min(600,Math.max(0,worldRun.distance));setVisualCue(legMark,worldRun.leg?'return':'speed',`${worldRun.leg?'Return':'Outbound'} route, pass ${worldRun.leg+1} of 2. ${Math.floor(worldRun.distance)} of 600 metres`,`${worldRun.leg+1}/2 · ${Math.floor(worldRun.distance)} / 600 m`);\r\n  const coinFactor=(worldRun.powers.doubleCoins?2:1)*(worldRun.powers.coinTier?3:1),scoreFactor=worldRun.powers.scoreBoost?2:1;\r\n  title.textContent=WORLDS[worldRun.world].name;route.textContent=`Pass ${worldRun.leg+1}/2 | ${worldRun.leg?'Return':'Outbound'} | ${Math.floor(worldRun.distance)} / 600 m${worldRun.elevation>.5?' - Rooftop':''}`;score.setAttribute('role','group');score.setAttribute('aria-label',`${worldRun.coins} coins, ${coinFactor} times payout. ${Math.floor(worldRun.score)} score, ${scoreFactor} times multiplier`);if(!score.children.length){score.append(visualCue('coin','Coins'),el('strong'),el('small'),visualCue('star','Score'),el('strong'),el('small'));}score.children[1].textContent=worldRun.coins;score.children[2].textContent=coinFactor>1?`×${coinFactor}`:'';score.children[4].textContent=Math.floor(worldRun.score);score.children[5].textContent=scoreFactor>1?`×${scoreFactor}`:'';if(score.children.length===6){const bonus=el('span','runner-bonus');bonus.append(visualCue('star','Bonus XP this workout'),el('b'));score.append(bonus);}score.children[6].lastElementChild.textContent=`${bonusXp} XP`;score.children[6].setAttribute('aria-label',`${bonusXp} bonus XP this workout`);\r\n  const powers=Object.entries(worldRun.powers).filter(([,n])=>n>0),priority=worldRun.lastPickup,text=`${priority}|`+powers.map(([id,n])=>`${id}:${Math.ceil(n)}`).join('|');if(text!==lastBuffs){lastBuffs=text;powers.sort(([a],[b])=>a===priority?-1:b===priority?1:0);buffs.replaceChildren(...powers.map(([id,n])=>visualCue(powerIcon(id),`${NAMES[id]}: ${Math.ceil(n)} seconds`,`${Math.ceil(n)}`)));}\r\n  try{maze.render(worldRun,worldRun.time,!run.paused&&run.status==='running'&&worldRun.phase==='run');}catch(error){shell.phase('error');cancelAnimationFrame(raf);run.paused=true;show('Graphics interrupted. Finish this set and reopen Cool Cardio.',60000);console.warn('Cool Cardio rendering stopped:',error);}\r\n }\r\n const onVisibility=()=>{last=0;if(document.hidden){tracking=false;run.paused=true;}};document.addEventListener('visibilitychange',onVisibility);updateReplay();raf=requestAnimationFrame(frame);controls();\r\n return {run,maze,body,show,throws,get worldRun(){return worldRun},screenOf:screen,\r\n  pose(detail){const result=body.update(detail),visible=[0,11,12].every(i=>(detail.points?.[i]?.visibility??0)>=.5);tracking=visible&&!document.hidden;run.paused=!tracking;lastPose=performance.now();const wasCrouching=crouching;crouching=visible&&body.body.crouching;if(crouching&&!wasCrouching)creditActive(lastPose);if(visible&&run.status==='running')for(const a of result.actions){const before={lane:worldRun.lane,jump:worldRun.jump,slide:worldRun.slide,phase:worldRun.phase};act(worldRun,a);if(a!=='slide'&&(before.lane!==worldRun.lane||before.jump!==worldRun.jump||before.phase!==worldRun.phase||before.slide!==worldRun.slide))creditActive(lastPose);}reflect();if(visible&&run.status==='running'&&worldRun.phase==='run')for(const s of strike(detail,{width:host.clientWidth||innerWidth,height:host.clientHeight||innerHeight})){\r\n    creditActive(lastPose);throws.strike(s.a,s.b,s.kind,screen);\r\n    for(const h of courseHazards(worldRun.world,worldRun.loop,worldRun.leg)){\r\n      const d=h.distance-worldRun.distance;if(d<0||d>THROW.reach||worldRun.hazardsSeen[h.id])continue;\r\n      const target=screen({d,x:h.lane,y:h.type==='jump'?.45:1.3,r:.65});\r\n      if(target&&distPointSeg(target,s.a,s.b)<=target.r+THROW.pad)punchCourse(worldRun,h.id);\r\n    }\r\n  }return result;},\r\n  stop(){if(disposed)return;shell.close();disposed=true;cancelAnimationFrame(raf);clearTimeout(hideTimer);document.removeEventListener('visibilitychange',onVisibility);view.dispose();maze.dispose();for(const e of [layer,hud,buffs,say,stopped,vortex,style])e.remove();host.classList.remove('runner-on');}\r\n };\r\n}\r\n","arcade/runner/themes.mjs":"// One look per course, cycled. Each owns a clearly different hue: wall texture + tint, neon, lights, the view out of the\r\n// windows, and `hue` (degrees) for the camera housing frame. gore only in the horror deck (splats, clay chunks, gold bones).\r\nexport const THEMES=[\r\n  {name:'MOM hull',hue:285,bg:'#06020a',fog:'#140a1c',hemi:['#c8a0e8','#1c1024'],light:'#ffc8f0',\r\n   wall:{style:'plate',from:'#6e5a86',to:'#4a3a60',grime:'30,8,40',tint:'#b89ad8',metalness:.6,roughness:.4},\r\n   neon:{rail:'#ff4fd8',warm:'#ffd0f4',red:'#ff3a8a',amber:'#c870ff',laser:'#ff4fd8',emergency:'#ff1e6e',blade:'#ff5ae0',sign:'#ff85e5'},\r\n   sky:{kind:'planet',stars:'#e8e2ff',colors:['#f6b6ff','#8e5b99','#281433'],bands:'#d4a017'}},\r\n  {name:'Nebula lab',hue:175,bg:'#020a0a',fog:'#0c1c20',hemi:['#f0ffff','#20383a'],light:'#e8ffff',\r\n   wall:{style:'lab',from:'#f2f6f8',to:'#d0dade',grime:'60,110,120',tint:'#ffffff',metalness:.1,roughness:.35},\r\n   neon:{rail:'#20f0d0',warm:'#e8fffc',red:'#ff3a6a',amber:'#20c8ff',laser:'#ff2a8a',emergency:'#ff2a5a',blade:'#20f0d0',sign:'#20f0d0'},\r\n   sky:{kind:'nebula',stars:'#ffffff',colors:['#20f0d0','#2a7aff','#ff4fd8','#80ffea']}},\r\n  {name:'Rust bucket',hue:28,bg:'#080400',fog:'#1e0e04',hemi:['#ffc890','#2a1406'],light:'#ffa850',\r\n   wall:{style:'rust',from:'#8a4a1e',to:'#4a2408',grime:'40,16,4',tint:'#e8a060',metalness:.45,roughness:.75},\r\n   neon:{rail:'#ff9a1e',warm:'#ffd08a',red:'#ff3a14',amber:'#ffb02e',laser:'#ff6a1e',emergency:'#ff2a10',blade:'#ffd02e',sign:'#ffb02e'},\r\n   sky:{kind:'asteroids',stars:'#ffe8d0',rock:'#8a6a4a'}},\r\n  {name:'Hex hive',hue:110,bg:'#010600',fog:'#081a04',hemi:['#b8ff90','#0a1a04'],light:'#c8ff9a',\r\n   wall:{style:'hex',from:'#3a6a1e',to:'#183a0a',grime:'0,30,0',tint:'#a8f070',metalness:.3,roughness:.5},\r\n   neon:{rail:'#7dff2a',warm:'#e8ffc0',red:'#ff3a3a',amber:'#d8ff2a',laser:'#b0ff2a',emergency:'#ff3a2a',blade:'#7dff2a',sign:'#7dff2a'},\r\n   sky:{kind:'rings',stars:'#e8ffd8',colors:['#e8ffa0','#7aa02a','#203a08'],ring:'#d8f0a0'}},\r\n  {name:'Earthrise',hue:215,bg:'#00020a',fog:'#04102a',hemi:['#9ac0ff','#040a20'],light:'#c8deff',\r\n   wall:{style:'plate',from:'#3a5a90',to:'#1a2a50',grime:'0,8,30',tint:'#8ab0ff',metalness:.7,roughness:.3},\r\n   neon:{rail:'#ffffff',warm:'#d8ecff',red:'#ff4a4a',amber:'#4ab8ff',laser:'#4affff',emergency:'#ff3030',blade:'#4ab8ff',sign:'#8ad0ff'},\r\n   sky:{kind:'earth',stars:'#ffffff'}},\r\n  {name:'Solar gold',hue:48,bg:'#0a0600',fog:'#201604',hemi:['#fff0a0','#2a2006'],light:'#ffe08a',\r\n   wall:{style:'hex',from:'#8a6a1a',to:'#4a3608',grime:'40,24,0',tint:'#ffd870',metalness:.75,roughness:.3},\r\n   neon:{rail:'#ffe02a',warm:'#fff4c0',red:'#ff5a1a',amber:'#ffc02a',laser:'#ff8a1a',emergency:'#ff4a10',blade:'#ffe02a',sign:'#ffd02a'},\r\n   sky:{kind:'redstar',stars:'#ffe8c8',colors:['#fff0c0','#ff6a2a','#a01008']}},\r\n  {name:'Gore deck',hue:350,bg:'#040000',fog:'#1a0204',hemi:['#ff7a7a','#1a0004'],light:'#ff6a6a',gore:true,\r\n   wall:{style:'rust',from:'#5a0a10',to:'#240206',grime:'60,0,10',tint:'#d05050',metalness:.35,roughness:.45},\r\n   neon:{rail:'#9aff2a',warm:'#ffb0a0',red:'#ff1a1a',amber:'#c0ff3a',laser:'#ff1a2a',emergency:'#ff0010',blade:'#ff1a1a',sign:'#9aff2a'},\r\n   sky:{kind:'planet',stars:'#ffb0b0',colors:['#ff8a6a','#660010','#1a0004'],bands:'#330006'}},\r\n];\r\nexport const themeFor=n=>THEMES[((n%THEMES.length)+THEMES.length)%THEMES.length];\r\n","arcade/runner/throws.mjs":"// Phase 2: the coach stands at the far end of the hall and throws asteroids and junk straight back down the corridor.\r\n// They grow toward the camera; a fast limb through one punches it (explode) or slices it (cut), using the slice\r\n// engine's pathKind and the camera-slice wrist-speed gate, hit-tested where the runner camera draws them.\r\n// Each hit is a small bonus through pod.flow.workout.bonusXp (the XP lane caps the share), as slice does.\r\n// Rate and speed ramp with course level; one that reaches the runner unhit is a stumble (emitted as 'miss').\r\nimport {pathKind,distPointSeg,rng,TYPES,TYPE_IDS} from '../slice/core.mjs';\r\nexport const THROW={every:[2.4,3.8],from:22,speed:[4.5,6.5],ramp:{every:.06,speed:.35,minEvery:.4,maxSpeed:5},reach:7.5,near:.6,xp:1,fast:.9,seen:.4,pad:.02};\r\nexport const throwEvery=level=>Math.max(THROW.ramp.minEvery,1-THROW.ramp.every*level);\r\nexport const throwSpeed=level=>Math.min(THROW.ramp.maxSpeed,THROW.ramp.speed*level);\r\nconst LIMBS=[13,14,15,16,25,26,27,28]; // elbows, wrists, knees, ankles (same as camera-slice)\r\n// Pure state: objects at distance d ahead of the runner, lateral x and height y in corridor space.\r\nexport function createThrows({seed=1,onEvent=()=>{}}={}){\r\n  const r=rng(seed),T={objects:[],t:0,spawnIn:1.5,hits:0,xp:0,thrown:0,throwing:0};\r\n  let level=0;\r\n  T.spawn=()=>{const type=TYPE_IDS[Math.floor(r()*TYPE_IDS.length)],o={id:++T.thrown,type,d:THROW.from,x:(r()*2-1)*1.1,y:1.1+r()*.9,r:.32+r()*.18,speed:THROW.speed[0]+r()*(THROW.speed[1]-THROW.speed[0])+throwSpeed(level),spin:(r()-.5)*6,alive:true};T.objects.push(o);T.throwing=.5;onEvent('throw',o);return o;};\r\n  T.step=(dt,runner=0,active=true,lvl=0)=>{\r\n    T.t+=dt;T.throwing=Math.max(0,T.throwing-dt);level=lvl;\r\n    if(active){T.spawnIn-=dt;if(T.spawnIn<=0){T.spawn();T.spawnIn=(THROW.every[0]+r()*(THROW.every[1]-THROW.every[0]))*throwEvery(level);}}\r\n    for(const o of T.objects){o.d-=(o.speed+runner)*dt;if(o.d<THROW.near){o.alive=false;onEvent('miss',o);}}\r\n    T.objects=T.objects.filter(o=>o.alive);\r\n  };\r\n  T.clear=()=>{T.objects=[];};\r\n  // a,b: limb segment in screen units (x scaled by aspect, y 0..1); screen(o) -> {x,y,r} or null when not drawn.\r\n  T.strike=(a,b,kind,screen)=>{let n=0;for(const o of T.objects){if(!o.alive||o.d>THROW.reach)continue;const s=screen(o);if(!s)continue;\r\n    if(distPointSeg(s,a,b)<=s.r+THROW.pad){o.alive=false;o.kind=kind;T.hits++;T.xp+=THROW.xp;n++;onEvent('hit',{...o,kind,cut:Math.atan2(b.y-a.y,b.x-a.x)});onEvent('bonus',{xp:THROW.xp});}}\r\n    T.objects=T.objects.filter(o=>o.alive);return n;};\r\n  return T;\r\n}\r\n// Limb tracker: the same speed gate and screen mapping as camera-slice (the body as if the video filled the stage).\r\nexport function createStriker(){\r\n  const hist=new Map();\r\n  return (detail,view)=>{\r\n    const {points,now,width,height,mirrored}=detail;if(!points)return [];\r\n    const sw=view.width,sh=view.height,k=Math.min(sw/width,sh/height),ox=(sw-width*k)/2,oy=(sh-height*k)/2,t=(now??performance.now())/1000,out=[];\r\n    for(const i of LIMBS){const p=points[i];if(!p||(p.visibility??0)<THROW.seen){hist.delete(i);continue;}\r\n      const px=mirrored?1-p.x:p.x,q={x:(ox+px*width*k)/sh,y:(oy+p.y*height*k)/sh,t},h=hist.get(i)||[];h.push(q);if(h.length>6)h.shift();hist.set(i,h);\r\n      if(h.length<2)continue;const a=h[h.length-2],dt=t-a.t;\r\n      if(dt<=0||dt>.5||Math.hypot(q.x-a.x,q.y-a.y)/dt<THROW.fast)continue;\r\n      out.push({a,b:q,kind:pathKind(h)});}\r\n    return out;\r\n  };\r\n}\r\n// Three view: the coach at the far end, junk meshes, explode bursts and cut halves. pos(o) -> world Vector3.\r\nexport function mountThrowsView(THREE,scene,{showCoach=true}={}){\r\n  const root=new THREE.Group();scene.add(root);\r\n  const geo=new THREE.DodecahedronGeometry(1,0),junk=new THREE.BoxGeometry(1.4,.8,.9),bit=new THREE.BoxGeometry(.08,.08,.08);\r\n  const mats=Object.fromEntries(Object.entries(TYPES).map(([k,[main,,light]])=>[k,new THREE.MeshStandardMaterial({color:main,emissive:light,emissiveIntensity:.35,roughness:.8,flatShading:true})]));\r\n  const meshes=new Map(),fx=[],holder=new THREE.Group();root.add(holder);holder.visible=showCoach;let coach=null;\r\n  // The 3D clay coach from the site, stood up with the eyes toward the runner (as coach-3d coachElement does);\r\n  // a purple clay capsule until (or if never) the GLB loads.\r\n  const fallback=new THREE.Mesh(new THREE.CapsuleGeometry(.35,.9,4,8),new THREE.MeshStandardMaterial({color:'#8e5b99',roughness:.9,emissive:'#3a1a4a'}));holder.add(fallback);\r\n  const key=new THREE.PointLight('#f3dcff',8,7,1.4);key.position.set(0,.4,1.4);holder.add(key);\r\n  if(showCoach)import('./coach-3d.mjs').then(m=>m.loadCoach(THREE)).then(c=>{coach=c;const pivot=new THREE.Group(),stand=new THREE.Group();pivot.rotation.x=Math.PI/2;pivot.add(c.group);stand.rotation.set(0,0,Math.PI/2);stand.add(pivot);stand.scale.setScalar(1.7);holder.add(stand);fallback.visible=false;}).catch(()=>{});\r\n  const make=o=>{const m=new THREE.Mesh(o.type==='satellite-junk'?junk:geo,mats[o.type]);m.scale.setScalar(o.r);meshes.set(o.id,m);root.add(m);return m;};\r\n  return {\r\n    hit(o,pos){const m=meshes.get(o.id);if(!m)return;meshes.delete(o.id);\r\n      if(o.kind==='slice'){for(const side of [-1,1]){const h=m.clone();h.scale.set(o.r,o.r*.5,o.r);h.position.copy(pos);root.add(h);fx.push({m:h,v:new THREE.Vector3(Math.cos(o.cut+Math.PI/2)*side*3,Math.sin(o.cut+Math.PI/2)*-side*3,1),spin:side*8,life:.8});}root.remove(m);}\r\n      else{root.remove(m);for(let i=0;i<14;i++){const b=new THREE.Mesh(bit,mats[o.type]);b.position.copy(pos);root.add(b);fx.push({m:b,v:new THREE.Vector3(Math.random()-.5,Math.random()-.3,Math.random()-.5).multiplyScalar(9),spin:10,life:.6});}}},\r\n    update(T,pos,coachPos,camera,dt,now){\r\n      for(const o of T.objects){const m=meshes.get(o.id)||make(o);m.position.copy(pos(o));m.rotation.set(T.t*o.spin,T.t*o.spin*.7,0);}\r\n      for(const [id,m] of meshes)if(!T.objects.some(o=>o.id===id)){root.remove(m);meshes.delete(id);}\r\n      for(const f of fx){f.life-=dt;f.m.position.addScaledVector(f.v,dt);f.m.rotation.x+=f.spin*dt;f.m.scale.multiplyScalar(f.life>0?.97:0);if(f.life<=0)root.remove(f.m);}\r\n      for(let i=fx.length-1;i>=0;i--)if(fx[i].life<=0)fx.splice(i,1);\r\n      holder.position.copy(coachPos);holder.lookAt(camera.position.x,coachPos.y,camera.position.z);\r\n      // Wind-up: a quick rear-back as each throw leaves his hand.\r\n      holder.rotateX(-T.throwing*.9);coach?.tick(now,{pressure:.5+T.throwing});\r\n    },\r\n    clear(){for(const m of meshes.values())root.remove(m);meshes.clear();},\r\n    dispose(){scene.remove(root);}};\r\n}\r\n","arcade/runner/worlds/course.mjs":"export const METRO_BUSES=[\r\n  {id:'bus-a',lane:0,start:70,rampEnd:86,roofEnd:110,end:126,height:3.2},\r\n  {id:'bus-b',lane:1,start:210,rampEnd:226,roofEnd:252,end:268,height:3.2},\r\n  {id:'bus-c',lane:-1,start:360,rampEnd:376,roofEnd:408,end:424,height:3.2}\r\n];\r\nexport function courseBuses(world,leg=0){return world===2?METRO_BUSES.map(b=>({...b,lane:leg%2?-b.lane:b.lane})):[];}\r\nexport function surfaceAt(world,distance,lane,leg=0){\r\n  const b=courseBuses(world,leg).find(b=>b.lane===lane&&distance>=b.start&&distance<=b.end);\r\n  if(!b)return {height:0,type:'ground',id:null};\r\n  const height=distance<b.rampEnd?b.height*(distance-b.start)/(b.rampEnd-b.start):distance>b.roofEnd?b.height*(b.end-distance)/(b.end-b.roofEnd):b.height;\r\n  return {height,type:distance>=b.rampEnd&&distance<=b.roofEnd?'roof':'ramp',id:b.id};\r\n}\r\nexport const POWER_KITS=[['headstart','magnet','shield','boost','superJump','recovery'],['magnet','phase','doubleCoins','coinTier','recovery','shield','boost'],['jetpack','superJump','magnet','board','scoreBoost','pogo']];\r\nconst pickupCache=new Map(),coinCache=new Map();\r\nexport function coursePickups(world,leg=0){\r\n  if(leg%2){const items=coursePickups(world).map(p=>({...p,lane:-p.lane}));if(world===0)items.push({id:'power-0-return-headstart',kind:'headstart',distance:10,lane:0,height:1.15});return items;}\r\n  if(pickupCache.has(world))return pickupCache.get(world);\r\n  const items=[];for(let pass=0;pass<2;pass++)for(const [i,kind] of (POWER_KITS[world]||[]).entries()){\r\n    if(kind==='headstart')continue;const distance=kind==='headstart'?10:32+i*25+pass*240,lane=kind==='headstart'?0:(i%3)-1;\r\n    items.push({id:`power-${world}-${pass}-${kind}`,kind,distance,lane,height:surfaceAt(world,distance,lane).height+1.15});\r\n  }\r\n  if(world===2)for(const [i,b] of METRO_BUSES.entries()){const distance=(b.rampEnd+b.roofEnd)/2;items.push({id:`roof-power-${i}`,kind:['jetpack','board','pogo'][i],lane:b.lane,distance,height:b.height+1.15});}\r\n  pickupCache.set(world,items);return items;\r\n}\r\nexport function courseCoins(world,leg=0,loop=0){\r\n  if(leg%2)return courseCoins(world,0,loop).map(c=>({...c,lane:-c.lane}));\r\n  const key=`${world}:${loop}`;if(coinCache.has(key))return coinCache.get(key);\r\n  const items=[];for(let distance=20;distance<545;distance+=4){const lane=Math.floor(distance/32)%3-1;items.push({id:`coin-${distance}`,distance,lane,height:surfaceAt(world,distance,lane).height+1});}\r\n  if(world===2)for(const b of METRO_BUSES)for(let distance=b.start;distance<=b.end;distance+=3)items.push({id:`coin-${b.id}-${distance}`,distance,lane:b.lane,height:surfaceAt(world,distance,b.lane).height+1});\r\n  // Route coins give a clear safe-lane cue 24–28 m (about 2–2.3 s) before\r\n  // each hazard, then continue briefly after it as a recovery trail.\r\n  const hazards=courseHazards(world,loop,0),byDistance=new Map();\r\n  for(const h of hazards){const group=byDistance.get(h.distance)||[];group.push(h);byDistance.set(h.distance,group);}\r\n  const guides=[];\r\n  for(const [distance,group] of byDistance){\r\n    const blocked=new Set(group.map(h=>h.lane));\r\n    const safe=[-1,0,1].filter(lane=>!blocked.has(lane)&&!(world===2&&surfaceAt(world,distance,lane).height>0));\r\n    if(!safe.length)continue;\r\n    const lane=safe[(Math.floor(distance/80)+world)%safe.length];\r\n    for(const offset of [-28,-20,-12,12,20,28]){const d=distance+offset;if(d<4||d>=590)continue;guides.push({id:`route-${world}-${distance}-${offset}`,distance:d,lane,height:surfaceAt(world,d,lane).height+1,guide:true});}\r\n  }\r\n  const blockedCoin=(c)=>hazards.some(h=>h.lane===c.lane&&c.distance>=h.distance-4&&c.distance<=h.distance+(h.length||0)+4);\r\n  items.splice(0,items.length,...items.filter(c=>!blockedCoin(c)),...guides);\r\n  coinCache.set(key,items);return items;\r\n}\r\nexport function jumpHeight(r){return r.jump>0?Math.sin(Math.min(1,r.jump/(r.jumpDuration||1.4))*Math.PI)*(r.jumpPeak||1.7):0;}\r\n\r\n// Crossed-distance collection lives in simulation, independent of rendering FPS or screenshots.\r\nexport function collectCourse(r,from,to,{coin,power}){\r\n  if(to<from||r.phase==='wormhole')return;\r\n  const reached=(o,attract=false)=>o.distance>from&&o.distance<=to&&(o.lane===r.lane||(attract&&r.powers.magnet>0)||r.powers.jetpack>0)&&Math.abs(o.height-(r.elevation+1.15+jumpHeight(r)+(r.powers.jetpack?3:0)))<((attract&&r.powers.magnet)||r.powers.jetpack?5:1.8);\r\n  for(const c of courseCoins(r.world,r.leg,r.loop))if(!r.collected[c.id]&&reached(c,true)){r.collected[c.id]=true;coin(r);}\r\n  for(const p of coursePickups(r.world,r.leg))if(!r.collected[p.id]&&reached(p)){if(power(r,p.kind)!==false){r.collected[p.id]=true;r.lastPickup=p.kind;r.pickupSerial=(r.pickupSerial||0)+1;}}\r\n}\r\n\r\n// Each encounter has more than two seconds of clear approach at 12 m/s.\r\n// Additional loops add a second occupied lane, preserving an escape lane and pace.\r\nexport function courseHazards(world,loop=0,leg=0){\r\n  if(leg%2)return courseHazards(world,loop).map(h=>({...h,lane:-h.lane,safeLane:-h.safeLane}));\r\n  const hazards=[],pickups=coursePickups(world);\r\n  const stations=[68,152,236,320,404,488,536];\r\n  if(loop>=2)stations.push(110,194,278,362,446);\r\n  stations.sort((a,b)=>a-b);\r\n  for(const [i,distance] of stations.entries()){\r\n    const preferred=[-1,0,1].map((_,n)=>((i+world+n)%3)-1);\r\n    const available=preferred.filter(lane=>!pickups.some(p=>p.lane===lane&&Math.abs(p.distance-distance)<16)&&!(world===2&&METRO_BUSES.some(b=>b.lane===lane&&distance+3>=b.start-12&&distance<=b.end+12)));\r\n    const nearBus=world===2&&METRO_BUSES.some(b=>distance+3>=b.start-12&&distance<=b.end+12);\r\n    const count=Math.min(available.length,nearBus?1:1+(loop>0?1:0));\r\n    for(let n=0;n<count;n++)hazards.push({id:`hazard-${world}-${i}-${n}`,lane:available[n],distance,elevation:0,type:(i+n+world+Math.max(0,Math.floor(loop)))%2?'slide':'jump',height:(i+n+world+Math.max(0,Math.floor(loop)))%2?1.05:.85,length:2.4});\r\n  }\r\n  for(const group of new Set(hazards.map(h=>h.distance))){const blocked=new Set(hazards.filter(h=>h.distance===group).map(h=>h.lane)),safe=[-1,0,1].filter(lane=>!blocked.has(lane)&&!(world===2&&surfaceAt(world,group,lane).height>0));for(const h of hazards.filter(h=>h.distance===group))h.safeLane=safe[(Math.floor(group/80)+world)%Math.max(1,safe.length)]??0;}\r\n  return hazards;\r\n}\r\n\r\nconst LASER_PERIOD=2.8,LASER_ON=1.35,LASER_WARN=.5;\r\nexport function laserState(world,hazard,time=0){\r\n  if(world!==0||hazard.type!=='jump')return 'active';\r\n  const phase=((time+hazard.distance*.113+world*.61)%LASER_PERIOD+LASER_PERIOD)%LASER_PERIOD;\r\n  if(phase<LASER_ON)return 'active';\r\n  if(phase>=LASER_PERIOD-LASER_WARN)return 'warning';\r\n  return 'off';\r\n}\r\n\r\n// Test the full crossed footprint, including entry/exit between simulation steps.\r\n// Timer interpolation prevents a jump button press from counting as clearance.\r\nexport function collideCourse(r,from,to,{jumpBefore,slideBefore,dt,onHit,onDodge=()=>{}}){\r\n  if(to<=from||r.phase!=='run')return;\r\n  r.hazardsSeen ||= {};\r\n  for(const h of courseHazards(r.world,r.loop,r.leg)){\r\n    if(r.hazardsSeen[h.id]||to<h.distance)continue;\r\n    if(h.type==='jump'&&r.world===0&&laserState(r.world,h,r.time||0)!=='active'){\r\n      if(to>=h.distance+h.length)r.hazardsSeen[h.id]=true;\r\n      continue;\r\n    }\r\n    if(h.lane!==r.lane){if(to>=h.distance+h.length){r.hazardsSeen[h.id]=true;onDodge(r,{...h,reason:'lane'});}continue;}\r\n    if(from>h.distance+h.length){r.hazardsSeen[h.id]=true;continue;}\r\n    const entry=Math.max(from,h.distance),exit=Math.min(to,h.distance+h.length);\r\n    const elapsed=d=>dt*(d-from)/(to-from);\r\n    const clearance=d=>jumpHeight({...r,jump:Math.max(0,jumpBefore-elapsed(d))});\r\n    const ducking=d=>slideBefore-elapsed(d)>0;\r\n    const blocked=h.type==='jump'?Math.min(clearance(entry),clearance(exit))<h.height:!ducking(entry)||!ducking(exit)||Math.max(clearance(entry),clearance(exit))+.65>h.height;\r\n    if(blocked){r.hazardsSeen[h.id]=true;onHit(r,h);}\r\n    else if(to>=h.distance+h.length){r.hazardsSeen[h.id]=true;onDodge(r,{...h,reason:h.type});}\r\n  }\r\n}\r\n","arcade/runner/worlds/host-style.mjs":"const CSS=`#cameraWorkout.runner-on>video{inset:auto;right:6px;bottom:max(6px,env(safe-area-inset-bottom));width:22vw;max-width:92px;height:auto;aspect-ratio:3/4;object-fit:cover;z-index:2;border-radius:10px;border:2px solid #fff8;box-shadow:0 4px 12px #000a;background:#000}\r\n#cameraWorkout.runner-on #coachOverlay{visibility:hidden}\r\n#cameraWorkout.runner-on .workout-performance-controls{left:6px;right:auto;bottom:max(6px,env(safe-area-inset-bottom));margin:0;padding:3px;gap:4px;background:#0008;border-radius:10px}\r\n#cameraWorkout.runner-on .workout-performance-controls button,#cameraWorkout.runner-on .workout-performance-controls select{padding:4px 8px;font-size:12px;min-height:32px}\r\n#cameraWorkout.runner-on .camera-workout-housing{filter:hue-rotate(var(--runner-hue,0deg)) saturate(1.25);transition:filter .6s}\r\n#cameraWorkout .runner-layer{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}\r\n#cameraWorkout .runner-layer canvas.runner-view{position:absolute;inset:0;width:100%;height:100%;display:block}\r\n#cameraWorkout .runner-flash{position:absolute;inset:0;background:radial-gradient(ellipse at center,#ff001840 20%,#ff0018ee);opacity:0}\r\n#cameraWorkout .runner-flash.on{animation:runner-flash .9s ease-out}\r\n@keyframes runner-flash{from{opacity:1}to{opacity:0}}\r\n#cameraWorkout .runner-chaser{display:none}\r\n#cameraWorkout .runner-say{position:absolute;z-index:4;top:calc(max(24px,env(safe-area-inset-top)) + 110px);left:8px;right:8px;display:flex;justify-content:center;pointer-events:none}\r\n#cameraWorkout .runner-say[hidden]{display:none}\r\n#cameraWorkout .runner-say canvas{max-width:100%;height:auto!important;filter:drop-shadow(0 4px 6px #000a)}\r\n#cameraWorkout .runner-say.pop{animation:runner-pop .42s cubic-bezier(.3,1.6,.5,1) both}\r\n@keyframes runner-pop{from{opacity:0;transform:translateY(-14px) scale(.6) rotate(-3deg)}to{opacity:1;transform:none}}\r\n#cameraWorkout .runner-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\r\n#cameraWorkout .runner-stopped{position:absolute;z-index:5;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;pointer-events:none;background:#40000c80;text-align:center}\r\n#cameraWorkout .runner-stopped[hidden]{display:none}\r\n#cameraWorkout .runner-stopped b{font:900 clamp(40px,13vw,72px)/.95 system-ui,sans-serif;color:#fff;-webkit-text-stroke:3px #ff0030;text-shadow:0 0 18px #ff0030,0 6px 0 #5a0010;letter-spacing:.02em;animation:runner-stop-pulse .8s ease-in-out infinite alternate}\r\n#cameraWorkout .runner-stopped span{font:800 18px/1.2 system-ui,sans-serif;color:#ffe3e8;text-shadow:0 2px 4px #000}\r\n@keyframes runner-stop-pulse{from{transform:scale(.96)}to{transform:scale(1.04)}}\r\n@media(prefers-reduced-motion:reduce){#cameraWorkout .runner-stopped b{animation:none}}\r\n@media(prefers-reduced-motion:reduce){#cameraWorkout .runner-say.pop,#cameraWorkout .runner-flash.on{animation:none}#cameraWorkout .runner-chaser{transition:none}}`;\r\n\r\nexport {CSS};\r\n","arcade/runner/worlds/landmarks.mjs":"// World-specific scenery built from the runner's existing lightweight primitives.\r\n// Every object stays in the parent segment and outside the authored three-lane route.\r\nexport function templeLandmarks({T,mesh,box,branch,group,segment,leg}){\r\n  const sideOrder=leg===1?[-1,1]:[1,-1];\r\n  // Hanging banyan roots frame the exposed bridge without crossing the safe lane.\r\n  for(const side of sideOrder){\r\n    const x=side*(4.9+(segment%3)*.2), z=-20+(segment%4)*1.5;\r\n    branch([side*5.8,7.4,z-2],[x,5.7,z],.24,0x4e392e,group,'wood');\r\n    for(let k=0;k<4;k++){\r\n      const reach=x+side*(.12+k*.3), drop=2.8+(k*.75+segment%3*.35);\r\n      branch([reach,5.7,z+k*.85],[reach-side*.2,5.7-drop,z+k*.9],.065,0x567449,group,'moss');\r\n      const blade=mesh(new T.SphereGeometry(1,7,5),0x547c4a,reach-side*.2,5.7-drop-.25,z+k*.9,group,'moss');\r\n      blade.scale.set(.18,.48,.12);\r\n    }\r\n    // Nearfield stepped shrine, with a dark doorway and chipped square lintel.\r\n    if(segment%2===1){\r\n      const cx=side*5.65, sz=-20.5;\r\n      for(let tier=0;tier<7;tier++){\r\n        const width=2.4-tier*.32;\r\n        box(cx,.62+tier*1.2,sz,width,1.2,2.8-tier*.18,tier%2?0xc0a876:0x9d8358,group,'stone');\r\n      }\r\n      box(cx,2.2,sz+1.45,.72,2.35,.18,0x29251f,group,'stone');\r\n      for(const jamb of [-1,1])box(cx+jamb*.5,2.24,sz+1.48,.2,2.75,.38,0xd3bd8e,group,'stone');\r\n      box(cx,3.72,sz+1.48,1.4,.38,.45,0xe1c998,group,'stone');\r\n    }\r\n    // Carved route-edge marker stays beyond the three running lanes.\r\n    box(side*4.65,.12,1.8,.32,.24,.55,0x6d7651,group,'moss');\r\n  }\r\n  // Chunky carved guardian silhouette at the outside of selected bridge spans.\r\n  if(segment%3===1){\r\n    for(const side of sideOrder){\r\n      const x=side*6.35;\r\n      const head=mesh(new T.IcosahedronGeometry(1,1),0x9b865e,x,2.25,-6.8,group,'stone');\r\n      head.scale.set(.7,1.03,.5);head.rotation.z=side*.08;\r\n      box(x-side*.22,2.55,-6.34,.24,.16,.12,0x352f2a,group,'stone');\r\n      box(x+side*.22,2.55,-6.34,.24,.16,.12,0x352f2a,group,'stone');\r\n      box(x,1.45,-6.3,.48,.14,.12,0x4d4334,group,'stone');\r\n    }\r\n  }\r\n}\r\n\r\nexport function metroLandmarks({T,mesh,box,cylinder,branch,group,segment,leg}){\r\n  const forward=leg===1?-1:1;\r\n  // Bright painted depot carriages: cyan/red side panels, continuous roof stripe,\r\n  // large dark windows and amber lamps identify the rail world at phone scale.\r\n  if(segment%2===1){\r\n    const x=5.45, z=-20;\r\n    const base=segment%4===1?0xd83e69:0x13a9c3;\r\n    box(x,1.75,z,2.3,3.5,12.5,base,group,'plaster');\r\n    box(x,3.05,z,2.38,.2,12.65,segment%4===1?0xffbd42:0xf1dc62,group,'gold');\r\n    box(x,.48,z,2.42,.42,12.7,0x263346,group,'rubber');\r\n    for(let w=-4.6;w<=4.7;w+=2.25){\r\n      box(x-1.17,2.25,z+w,.06,1.15,1.65,0x173c59,group,'metal');\r\n      box(x-1.21,2.86,z+w,.07,.12,1.72,0xc7f5ef,group,'metal');\r\n      box(x-1.23,1.38,z+w,.08,.13,1.9,0xf49c48,group,'gold');\r\n    }\r\n    box(x,1.9,z+forward*6.29,2.35,2.65,.12,0x253c52,group,'metal');\r\n    for(const side of [-1,1]){\r\n      cylinder(x+side*.85,.55,z+forward*6.4,.18,.08,0xffcf4d,group,'gold').rotation.z=Math.PI/2;\r\n    }\r\n  }\r\n  // Overhead signal gantry and icon-only destination disc, never hazard-colored.\r\n  if(segment%3===0){\r\n    const z=-7;\r\n    for(const side of [-1,1])branch([side*5.2,0,z],[side*5.2,7.2,z],.14,0x36475a,group,'metal');\r\n    box(0,7.1,z,10.6,.25,.3,0x36475a,group,'metal');\r\n    box(0,6.55,z,1.8,.8,.22,0x24374b,group,'metal');\r\n    for(const x of [-.5,0,.5]){\r\n      const lamp=cylinder(x,6.55,z+.14,.13,.08,[0x39c4cb,0xf4bc44,0xe86a85][x===0?1:x<0?0:2],group,'gold');lamp.rotation.x=Math.PI/2;\r\n    }\r\n  }\r\n  // Yard stacks and broad graphic wall panels enliven the outskirts, not the track.\r\n  for(const side of [-1,1]){\r\n    const x=side*(10.5+(segment%2)*1.2), z=-1+(segment%3)*2;\r\n    const color=segment%3===0?0xebb65c:segment%3===1?0x41a6af:0xd27678;\r\n    box(x,1.15,z,3.8,2.3,5.2,color,group,'plaster');\r\n    box(x,2.36,z,3.85,.16,5.25,0x34465a,group,'metal');\r\n    for(let k=0;k<3;k++)box(x+side*(k-1)*.9,1.2,z+side*2.64,.13,1.75,.08,0x345168,group,'metal');\r\n    // Non-text mural chevrons make the facades graphic without reading as lane cues.\r\n    if(segment%2===0){\r\n      const stripe=box(x,1.3,z+side*2.7,1.35,.2,.1,side<0?0xe04b77:0x3ac2c9,group,'gold');stripe.rotation.z=.35;\r\n    }\r\n  }\r\n}\r\n\r\nexport function foundryLandmarks({T,box,cylinder,mesh,group,segment}){\r\n  // Flanged pressure vessels and broad ring clamps make the existing orbital\r\n  // machinery feel engineered instead of a row of interchangeable corridor bays.\r\n  for(const side of [-1,1]){\r\n    const x=side*(7.6+(segment%2)*.55), z=-2.5;\r\n    const vessel=cylinder(x,1.9,z,1.6,5.4,segment%2?0x53647c:0x665274,group,'metal');\r\n    vessel.rotation.x=Math.PI/2;\r\n    for(const dz of [-2.25,-.9,.9,2.25]){\r\n      const flange=cylinder(x,1.9,z+dz,.3,3.35,0xc09555,group,'gold');flange.rotation.x=Math.PI/2;\r\n    }\r\n    for(const y of [1.1,2.2,3.3]){\r\n      box(x-side*1.66,y,z+.15,.16,.16,1.8,0x71d8dc,group,'metal');\r\n    }\r\n  }\r\n  if(segment%2===0){\r\n    // A large nearfield flywheel and porthole sit just beyond the route edge.\r\n    const x=6.5,y=3.2,z=-5.5;\r\n    const wheel=mesh(new T.CylinderGeometry(1.7,1.7,.32,12),0xc79855,x,y,z,group,'gold');\r\n    wheel.rotation.x=Math.PI/2;\r\n    mesh(new T.TorusGeometry(1.35,.16,6,16),0x5fd2df,x,y,z+.22,group,'metal');\r\n    for(let i=0;i<8;i++){\r\n      const a=i*Math.PI/4;\r\n      const bolt=box(x+Math.cos(a)*2.1,y+Math.sin(a)*2.1,z,.3,.45,.36,0xd2a65d,group,'gold');bolt.rotation.z=a;\r\n    }\r\n    // Blue glass porthole with a thick brass rim, facing the route.\r\n    const rim=mesh(new T.TorusGeometry(1.1,.16,7,24),0xd2a65d,-6.5,3.2,-5.5,group,'gold');\r\n    rim.rotation.y=Math.PI/2;\r\n    const glass=mesh(new T.CircleGeometry(.95,24),0x54b4d0,-6.5,3.2,-5.49,group,'metal');glass.rotation.y=Math.PI/2;\r\n    box(-6.5,3.2,-5.2,.12,2.15,.12,0x63d4e4,group,'metal');\r\n  }\r\n}\r\n","arcade/runner/worlds/power-art.mjs":"// Distinct silhouettes, not interchangeable diamonds. Geometry resources belong to the caller.\r\nexport function powerModel(T,id,{mesh,box,cylinder,sphere},parent){\r\n  const gold=0xffd269,teal=0x84ead9,violet=0xc39afa,red=0xdb7988;\r\n  const ring=(r,t,c,x=0,y=0,z=0)=>mesh(new T.TorusGeometry(r,t,7,20),c,x,y,z,parent,'gold');\r\n  if(id==='magnet'){\r\n    const u=mesh(new T.TorusGeometry(.34,.12,7,18,Math.PI),red,0,0,0,parent,'metal');u.rotation.z=Math.PI;\r\n    for(const s of [-1,1])box(s*.34,.17,0,.24,.32,.24,teal,parent,'metal');\r\n  }else if(id==='shield'||id==='recovery'){\r\n    const shield=mesh(new T.IcosahedronGeometry(.46,1),id==='shield'?violet:gold,0,0,0,parent,'gold');shield.scale.set(.85,1.15,.3);\r\n    box(0,0,.17,.09,.56,.08,teal,parent,'gold');box(0,0,.17,.4,.08,.08,teal,parent,'gold');\r\n    if(id==='recovery')for(const s of [-1,1])for(let k=0;k<3;k++){const feather=box(s*(.38+k*.12),.12-k*.12,0,.35,.12,.1,gold,parent,'gold');feather.rotation.z=s*.4;}\r\n  }else if(id==='jetpack'){\r\n    for(const s of [-1,1]){cylinder(s*.25,0,0,.19,.75,teal,parent,'metal');mesh(new T.ConeGeometry(.17,.4,8),gold,s*.25,-.5,0,parent,'gold').rotation.z=Math.PI;}\r\n    box(0,0,0,.27,.5,.35,violet,parent,'metal');\r\n  }else if(id==='board'){\r\n    const b=box(0,0,0,1,.12,.32,teal,parent,'metal');b.rotation.z=.2;for(const s of [-1,1])ring(.12,.05,gold,s*.32,-.1);\r\n  }else if(id==='superJump'){\r\n    for(const s of [-1,1]){box(s*.25,-.12,.12,.3,.2,.55,gold,parent,'rubber');box(s*.25,.08,0,.25,.35,.25,violet,parent,'metal');}\r\n  }else if(id==='phase'){ring(.4,.07,violet);ring(.28,.06,teal);sphere(0,0,0,.15,violet,parent);}\r\n  else if(id==='pogo'){cylinder(0,0,0,.07,.9,violet,parent,'metal');box(0,.4,0,.5,.08,.1,teal,parent,'metal');ring(.14,.04,gold,0,-.25).rotation.x=Math.PI/2;box(0,-.15,0,.42,.06,.15,gold,parent,'metal');}\r\n  else if(id==='doubleCoins'||id==='coinTier'){for(let i=0;i<(id==='coinTier'?3:2);i++){const c=cylinder((i-1)*.23,0,i*.06,.3,.07,gold,parent,'gold');c.rotation.x=Math.PI/2;}}\r\n  else if(id==='scoreBoost'){const star=new T.Shape();for(let i=0;i<10;i++){const a=i*Math.PI/5,r=i%2?.2:.47;const x=Math.sin(a)*r,y=Math.cos(a)*r;if(i)star.lineTo(x,y);else star.moveTo(x,y);}star.closePath();mesh(new T.ExtrudeGeometry(star,{depth:.1,bevelEnabled:false}),gold,0,0,0,parent,'gold');}\r\n  else {for(let i=0;i<3;i++){const arrow=mesh(new T.ConeGeometry(.2,.4,4),i%2?gold:violet,0,(i-1)*.25,0,parent,'metal');arrow.rotation.x=Math.PI/2;}}\r\n}\r\n","arcade/runner/worlds/sakura/palette.js":"/**\r\n * One place for every colour in the scene.\r\n * The palette is deliberately narrow: warm off-whites, a gray-purple road,\r\n * teal-leaning greens, pale pinks, and four saturated accents (red / yellow /\r\n * blue / teal) reserved for focal objects.\r\n */\r\nexport const PAL = {\r\n  // --- sky & atmosphere ---\r\n  skyTop: 0x8fbdea,\r\n  skyMid: 0xd4e8fa,\r\n  skyHaze: 0xfbe7e9,\r\n  cloud: 0xfdfaf8,\r\n  cloudShade: 0xe6e6f2,\r\n  fog: 0xe6ecf7,\r\n  hill: 0xc6cfe6,\r\n  hillFar: 0xd8dded,\r\n\r\n  // --- light ---\r\n  sun: 0xfff1d8,\r\n  fill: 0xa9bdf5,\r\n  hemiSky: 0xdcecff,\r\n  hemiGround: 0xb6a6c6,\r\n\r\n  // --- ink ---\r\n  ink: 0x39324f,\r\n  inkSoft: 0x4a4468,\r\n\r\n  // --- ground ---\r\n  road: 0x8e8a9c,\r\n  roadWorn: 0x9a95a6,\r\n  roadDark: 0x7b7689,\r\n  lineWhite: 0xf4f2f6,\r\n  lineYellow: 0xf0c341,\r\n  tactile: 0xf2c53d,\r\n  sidewalk: 0xdcd8e2,\r\n  sidewalkAlt: 0xe7e2e6,\r\n  curb: 0xc7c2d0,\r\n  concrete: 0xd9d5dd,\r\n  concreteMid: 0xc2bdc8,\r\n  concreteDark: 0xa7a2b0,\r\n  gutter: 0xbdb8c4,\r\n  drain: 0x6d687a,\r\n  dirt: 0xc9bfae,\r\n  gravel: 0xa9a3ab,\r\n  ballast: 0x7d7686,\r\n\r\n  // --- buildings ---\r\n  wallWhite: 0xfaf6ef,\r\n  wallCream: 0xf2e7d3,\r\n  wallBlue: 0xd6e3ee,\r\n  wallBeige: 0xe7dbc4,\r\n  wallGray: 0xdedee6,\r\n  wallPink: 0xf0dcda,\r\n  /* Added with the north housing block.  The brief for it asked for warm white,\r\n   * pale grey, cream, pale blue, pink-grey *and* pale tea -- and the six tones\r\n   * above stop one short of that, so a street of twelve houses had to reuse.\r\n   * Appended rather than inserted: every `wall:` index in the world is a number. */\r\n  wallTea: 0xdccdb6,\r\n  wallSage: 0xdde2d6,\r\n  roofSlate: 0x59617a,\r\n  roofBlue: 0x4d5c78,\r\n  roofBrown: 0x6b585c,\r\n  roofTeal: 0x4f6b70,\r\n  trim: 0x8b8496,\r\n  glass: 0x9dc0d4,\r\n  glassDark: 0x53627a,\r\n  shutter: 0x6e6a7a,\r\n  shutterLight: 0x847f92,\r\n\r\n  // --- accents ---\r\n  red: 0xe0453f,\r\n  redDeep: 0xb5322f,\r\n  redSoft: 0xef6a60,\r\n  yellow: 0xf4c033,\r\n  yellowDeep: 0xd39c1f,\r\n  black: 0x322e3b,\r\n  blackSoft: 0x453f4f,\r\n  teal: 0x2f9c9a,\r\n  tealDeep: 0x22736f,\r\n  blue: 0x3d6ec4,\r\n  blueDeep: 0x2a4f97,\r\n  orange: 0xef8a3c,\r\n  purple: 0x8f6fb5,\r\n\r\n  // --- vegetation ---\r\n  leaf: 0x5aa578,\r\n  leafDeep: 0x3f7f60,\r\n  leafPale: 0x84bd97,\r\n  grass: 0x86ab84,\r\n  trunk: 0x9a8082,\r\n  trunkDark: 0x765f62,\r\n\r\n  // --- cherry blossom ---\r\n  blossom: 0xfbc6d8,\r\n  blossomLight: 0xfff0f4,\r\n  blossomWarm: 0xfedde2,\r\n  blossomDeep: 0xf0a3c0,\r\n  petal: 0xfcd9e4,\r\n  petalDeep: 0xf6bccf,\r\n\r\n  // --- railway ---\r\n  railMetal: 0x6b6472,\r\n  railHead: 0xc2bcc4,\r\n  sleeper: 0x6d6576,\r\n  sleeperLight: 0x847b8c,\r\n  gateYellow: 0xf4c033,\r\n  gateBlack: 0x322e3b,\r\n  signalRed: 0xf2453c,\r\n  signalOff: 0x6a3b44,\r\n  cabinet: 0xd8d5da,\r\n  cabinetTop: 0xb6b2bc,\r\n\r\n  // --- train ---\r\n  trainBody: 0xf7f2e6,\r\n  trainBodyShade: 0xe6dfd0,\r\n  trainStripe: 0x2f7fd0,\r\n  trainStripe2: 0x3fae9a,\r\n  trainWindow: 0x3a4258,\r\n  trainWindowLit: 0x6b7794,\r\n  trainSkirt: 0x9aa0ad,\r\n  trainRoof: 0xbdb8bd,\r\n  trainDoor: 0xeae4d8,\r\n\r\n  // --- metal / misc props ---\r\n  metal: 0xb8bcc6,\r\n  metalDark: 0x878b96,\r\n  metalWarm: 0xc9c0b4,\r\n  mirrorBack: 0xe4a83c,\r\n  mirrorFace: 0xc8d8e4,\r\n  vendWhite: 0xf8f5f0,\r\n  vendRed: 0xdb4038,\r\n  vendTeal: 0x2e9a98,\r\n  crate: 0x3f7fbf,\r\n  crateAlt: 0xe25a4a,\r\n  basket: 0xdb5a4a,\r\n  bin: 0x5d8fb8,\r\n  taxiYellow: 0xf5be2a,\r\n  taxiYellowDeep: 0xdc9f18,\r\n  cat: 0xf0e6da,\r\n  catDark: 0x6a5f63,\r\n  umbrella: 0xd8ecf4,\r\n  shrineStone: 0xcfcad2,\r\n  shrineBib: 0xd8453f,\r\n\r\n  /* ------------------------------------------------------------------ *\r\n   * The wider district.\r\n   *\r\n   * Everything below was added when the world grew out from the crossing\r\n   * into a school, a shrine, a shopping street, a canal and more housing.\r\n   * It stays inside the original range on purpose: pale masses, one or two\r\n   * saturated accents per area, and greens that lean teal.\r\n   * ------------------------------------------------------------------ */\r\n\r\n  // --- ground ---\r\n  // The one warm surface in the district.  Held well back from a real ochre:\r\n  // at full saturation it becomes the loudest thing in every frame it is in.\r\n  clay: 0xcfb59c,\r\n  clayLine: 0xe8dcc8,      // lime markings on it\r\n  sand: 0xdccaa6,          // sandpit\r\n  moss: 0x7d9c74,\r\n\r\n  // --- water ---\r\n  water: 0x93b8ce,\r\n  waterDeep: 0x6d90ad,\r\n  waterSky: 0xcadff0,      // the block that stands in for a sky reflection\r\n  waterPetal: 0xf3cada,\r\n\r\n  // --- stone ---\r\n  stone: 0xc6c0cb,\r\n  stoneDark: 0xa39daf,\r\n  stoneWarm: 0xcfc6bc,\r\n\r\n  // --- school ---\r\n  schoolWall: 0xf7f3ea,\r\n  schoolWallAlt: 0xe4ebf2,\r\n  schoolWallBlue: 0xd3e0ec,\r\n  schoolTrim: 0xcfd6de,\r\n  schoolRoof: 0x4d5468,\r\n  gymWall: 0xedeff4,\r\n  gymRoof: 0x59606f,\r\n  blackboard: 0x3d5148,\r\n  deskTop: 0xd8c29c,\r\n  locker: 0xb7c7d5,\r\n  curtain: 0xf4ead9,\r\n  corridor: 0xd8d2c6,\r\n\r\n  // --- shrine ---\r\n  torii: 0xd8412f,\r\n  toriiDeep: 0xa72f23,\r\n  shrineWood: 0xa9744f,\r\n  shrineWoodDark: 0x8a604a,\r\n  shrineRoof: 0x69707e,\r\n  rope: 0xf0e5ca,\r\n  ema: 0xe9d9b6,\r\n  bamboo: 0x94b06b,\r\n  bambooDeep: 0x6f8c50,\r\n  cedar: 0x3f6b52,\r\n  cedarDeep: 0x2f5540,\r\n  /* --- 杉林, the cedar plantation ---------------------------------------\r\n   * The second tree species on the hills, and the three tones are chosen to be\r\n   * a *mass* rather than a tree: a 人工林 is read at fifty metres as one dark\r\n   * wedge with a lit edge, and what separates it from the broadleaf grove beside\r\n   * it has to be value, because the two are both green.\r\n   *\r\n   * Relative luminance against what it stands on and next to:\r\n   *\r\n   *     hillGrassSun     0.754      the slope the stand sits on\r\n   *     grove light      0.658      0x8cb884, the broadleaf canopy's top tone\r\n   *     cedarLit         0.517      the sunlit face of a cedar crown\r\n   *     grove deep       0.376      0x3f6b52 -- which is `cedar`, below\r\n   *     cedarDeep        0.296      the shaded underside of the tiers\r\n   *\r\n   * So the stand's *lightest* tone is darker than the broadleaf's middle one,\r\n   * which is what makes the boundary between the two read as a boundary and not\r\n   * as a change of light.  `cedarLit` is deliberately not darker than that: the\r\n   * grove's deep tone is already at the value where the cel ramp's bottom band\r\n   * goes to very nearly the ink colour (see the note on `CAR.forest`), and a\r\n   * crown built entirely out of tones below it would be a black triangle.\r\n   *\r\n   * `cedarBark` is redder than `trunkDark` (0x765f62, a mauve-brown) on purpose.\r\n   * A plantation's clean pruned trunks are half of what says \"planted\" rather\r\n   * than \"grown\", so they are the one part of the tree that has to be visibly a\r\n   * different wood from the broadleaf beside it. */\r\n  cedarLit: 0x64906b,\r\n  cedarBark: 0x7e6150,\r\n\r\n  // --- shopping street ---\r\n  awningGreen: 0x4f8f6a,\r\n  awningOrange: 0xe08a3c,\r\n  awningBlue: 0x4a7fae,\r\n  awningCream: 0xefe0c2,\r\n  lantern: 0xf6e2c0,\r\n  lanternLit: 0xffd9a0,\r\n  noren: 0x2f4a72,\r\n  norenRed: 0xb5322f,\r\n  norenCream: 0xf2e8d6,\r\n  freezer: 0xd8e6ee,\r\n\r\n  /* --- 湯の坂, the onsen street ---\r\n   * A deliberately narrow set, and none of it shared with the shopping\r\n   * street: that row is enamel and plastic, this one is timber, plaster and\r\n   * tile.  Four tones of wood do all the work -- frame, lattice, fresh cedar\r\n   * and the deep members -- against one plaster and one very dark tile, so\r\n   * the whole street is dark structure on a pale field with the lanterns as\r\n   * the only saturated thing in it. */\r\n  onsenWood: 0x8a6647,       // the frame: posts, rails, battens\r\n  onsenWoodDark: 0x513a28,   // the deep members, and the recess behind a 格子\r\n  onsenWoodPale: 0xc4a074,   // fresh cedar: signboards, decking, the footbath\r\n  onsenPlaster: 0xe8dfd0,\r\n  onsenPlasterAlt: 0xd9cdba,\r\n  onsenTile: 0x454452,       // roof tile, near black at this range\r\n  onsenTileEdge: 0x5c5a6a,\r\n  onsenSlab: 0xcac4c6,       // the street's stone\r\n  /* Hot water is paler, greener and far more opaque than the canal's: it is\r\n   * mineral rather than sky, and you read steam off the top of it rather than\r\n   * gravel through it. */\r\n  onsenWater: 0xb9d0d4,\r\n  onsenWaterDeep: 0x8fb0b6,\r\n  onsenSteam: 0xf4eef0,\r\n  onsenIndigo: 0x2c3a52,\r\n\r\n  /* --- 裏山, the back hills ---\r\n   * The one part of the world that is *landscape* rather than town, and it needs\r\n   * its own greens for a reason the district colours do not cover: a hillside is\r\n   * read at fifty to two hundred metres, through the haze, at every angle to the\r\n   * sun at once.  `PAL.grass` (0x86ab84) is a lawn tone chosen against pale\r\n   * paving at ten metres; spread over thirty thousand square metres it goes flat\r\n   * and slightly sour.  These three are lifted and pushed a shade warmer so the\r\n   * lit slopes hold their value against the sky, with one deep tone for the\r\n   * shaded and higher ground and one bare-earth tone for the steep faces --\r\n   * which is also what stops a big smooth hill reading as a single blob.\r\n   *\r\n   * The two tunnel tones are separate from `concrete` on purpose: a portal is\r\n   * cast in situ and weathered, so it is greyer and a little darker than the\r\n   * town's precast, and the bore has to be near-black at the mouth or the tunnel\r\n   * reads as a painted arch on a hillside. */\r\n  /* ------------------------------ the hillside ------------------------------\r\n   * **A ladder of five, and the reason it has to be a ladder is the cel ramp.**\r\n   *\r\n   * `RAMPS[3]` is `[92, 178, 255]` on a three-texel `NearestFilter` texture\r\n   * sampled at `dotNL * 0.5 + 0.5`, so its band boundaries fall at\r\n   * `dotNL = ±1/3` and nowhere else.  Measured over the 22 000 facets of\r\n   * ひばり山: 66.5 % of them land in the top band and 31 % in the middle one, and\r\n   * within any one *slope* they are all in the same band -- from a local\r\n   * `dotNL` of 0.8 a facet has to turn about 35° to cross into the next one, which\r\n   * on a 3 m lattice is a metre and a half of relief per cell.  So **direct light\r\n   * cannot give a gentle hillside its form here**, and no amount of roughness\r\n   * changes that.  The material value has to do it instead, which is what these\r\n   * five are for.\r\n   *\r\n   * Relative luminance (0.2126 R + 0.7152 G + 0.0722 B on the sRGB values):\r\n   *\r\n   *     hillGrassSun   0.754   the sunlit turf\r\n   *     hillBracken    0.739   dry ススキ and bracken on the high sunny slopes\r\n   *     hillGrass      0.701   the mid tone\r\n   *     hillEarth      0.700   bare earth -- same value as the mid green on\r\n   *                            purpose, separated by hue, and only ever on faces\r\n   *                            steeper than 0.74 where the light differs anyway\r\n   *     hillGrassDeep  0.574   the damp shaded flanks and the gully floors\r\n   *\r\n   * **The sun tone was 0xc2d69b at 0.806 and the hill rendered bleached.**  It is\r\n   * the *lightest* tone and it lands on the near sunny slope, which is the largest\r\n   * area in every frame taken from the town side -- and that slope is already in\r\n   * the ramp's top band, so 0.806 of material on top of full direct light came out\r\n   * as a sheet of pale yellow-green.  0.754 is a 0.053 step over the mid tone,\r\n   * which is small but is *all* that is wanted there: the form has to be carried\r\n   * by the dark end of the ladder, where 0.701 to 0.574 is a 0.127 step, because\r\n   * that is the end the eye reads a cel image from.\r\n   *\r\n   * Bracken carries hue rather than value -- 0.038 off the sun tone but decisively\r\n   * yellower -- so it reads as a change of *cover* and not as a change of light.\r\n   * `hillGrass`, `hillGrassDeep` and `hillEarth` keep their old values because\r\n   * `tunnel.js` builds its cutting faces from `hillEarth` and `buildMoss` mixes\r\n   * `hillGrassDeep`. */\r\n  hillGrassSun: 0xb4c98e,\r\n  hillGrass: 0x9fbc90,\r\n  hillGrassDeep: 0x7a9c78,\r\n  hillBracken: 0xc6bf86,\r\n  /* The floor of a 杉林, at 0.495 -- the only ground tone darker than\r\n   * `hillGrassDeep`, and the sixth surface mesh.\r\n   *\r\n   * It exists because the first plantation went in over `hillBracken`: a closed\r\n   * block of near-black conifer standing on the brightest, driest, most open\r\n   * ground tone there is, which is a contradiction you can see from the crest\r\n   * walk without knowing anything about forestry.  Bracken is what grows where\r\n   * the light gets in.  Under 11 m of cedar at 4 m centres nothing does, and the\r\n   * floor is needle litter: warm, dull, olive-brown, and *darker than any grass*.\r\n   *\r\n   * Which is also why it earns its own material instead of borrowing\r\n   * `hillGrassDeep`.  The stands are 12 % of the range, and folding them into the\r\n   * deep tone would take it from 31.6 % of the drawn surface to about 40 -- past\r\n   * the one-third ceiling the five-tone ladder was built to hold.  A sixth mesh\r\n   * is one draw call against ~5 800 in the heaviest view.\r\n   *\r\n   * Tinted mauve rather than blue (`0x847a94`) for the same reason bracken is:\r\n   * this is the ground that is *always* in shade, so its shadow band is most of\r\n   * what is ever seen of it, and a blue-shifted olive at this value is mud. */\r\n  hillLitter: 0x7e8163,\r\n  /* Held well back from an ochre, for the reason `clay` is: a bare-earth scar on\r\n   * a green hillside is a large area, and at full saturation it becomes the\r\n   * loudest thing in the frame -- the first pass read as sheets of gold leaf on\r\n   * the cutting banks. */\r\n  hillEarth: 0xbdb2a2,\r\n  hillRock: 0xb4aeb6,\r\n  hillMoss: 0x83a06d,\r\n  hillPath: 0xc8b69a,\r\n  hillPathStone: 0xc0bcc4,\r\n  tunnelFace: 0xc7c2ca,\r\n  tunnelFaceDark: 0xaba7b3,\r\n  tunnelBore: 0x565269,\r\n  tunnelBoreDeep: 0x322f42,\r\n\r\n  /* ------------------------- ひばり湖 -------------------------\r\n   * The lake, and its water is a **different problem from the canal's**.\r\n   *\r\n   * `waterDeep` (0x6d90ad) works in a 5 m concrete channel because the whole\r\n   * surface is 2.5 m wide, always in the shade of its own revetment, and read\r\n   * from directly above.  Spread over ten thousand square metres, read from a\r\n   * viewpoint eight metres up and against a pale sky, that same tone comes out as\r\n   * flat slate: it is *darker* than the sky it is supposed to be reflecting, so\r\n   * the water reads as a hole rather than as a surface.\r\n   *\r\n   * So the lake's ladder starts from the sky and works down, and the numbers are\r\n   * relative luminance (0.2126R + 0.7152G + 0.0722B on the sRGB values):\r\n   *\r\n   *     lakeSky      0.798   the block that stands in for a sky reflection\r\n   *     lakeShallow  0.688   the first metre, over pale silt -- green, not blue\r\n   *     lakeWater    0.588   the body of it\r\n   *     lakeDeep     0.470   the middle, and the shade of the far bank\r\n   *     canal waterDeep 0.470  -- the same value, which is the point: what the\r\n   *                              channel uses as its *base* is this one's floor\r\n   *\r\n   * `lakeShallow` leans decisively green and `lakeDeep` decisively blue-violet,\r\n   * which is the one thing that makes a body of water read as having depth in a\r\n   * three-band cel image: the eye takes the hue shift as distance from the bank\r\n   * even where the value step is too small to see.\r\n   *\r\n   * `lakeGlint` is the only near-white in the set and is used for perhaps 2 % of\r\n   * the surface -- the wind lanes and the ripple rings.  Any more and the lake\r\n   * stops being a quiet one, which the brief for this place is explicit about. */\r\n  lakeSky: 0xcfe3f2,\r\n  lakeShallow: 0x9dc4bd,\r\n  lakeWater: 0x7ba6bd,\r\n  lakeDeep: 0x5f83a4,\r\n  lakeGlint: 0xf2f7fa,\r\n  /** the block-colour reflection of the far hills, and of blossom on the bank */\r\n  lakeHillEcho: 0x86a8a8,\r\n  lakeBloomEcho: 0xe6c3cf,\r\n  /* The bed, and the drawdown margin above the waterline.  The bed is silt seen\r\n   * *through* water, so it is desaturated toward the water's own hue rather than\r\n   * being a ground colour; the margin is the pale dried mud a pond in spring\r\n   * always has a metre of, and it is what draws the waterline as a band rather\r\n   * than as a single ink line. */\r\n  lakeBed: 0x9aae9e,\r\n  lakeShore: 0xcfc6b4,\r\n  /* 柳 -- see the note by `GREEN_TONES` in `trees.js`.  The palest, yellowest\r\n   * green in the world, on purpose: a willow at a lakeside is the one tree that\r\n   * has to be lighter than the hill behind it. */\r\n  willow: 0xa8c489,\r\n  /* The hire boats.  Four hulls, and the constraint is the same one `CAR` has --\r\n   * nothing saturated enough to be the loudest thing in the frame - except that\r\n   * a rowing boat is *allowed* one, because a row of white hulls with a single\r\n   * red one in it is what a park boat station looks like from across the water. */\r\n  boatWhite: 0xf0ece2,\r\n  boatBlue: 0xa8c6d8,\r\n  boatYellow: 0xe8d295,\r\n  boatRed: 0xcf6a5e,\r\n  boatTeal: 0x77b3ad,\r\n  boatDeck: 0xc9b492,\r\n  /* Tents: low saturation on purpose.  A campsite of bright nylon is a festival;\r\n   * four canvas-toned tents under trees is a Tuesday in April. */\r\n  tentCream: 0xe4dcc6,\r\n  tentGreen: 0xb8c4a6,\r\n  tentBlue: 0xb4c6d2,\r\n  tentOchre: 0xcbb08a,\r\n};\r\n\r\n/** Bright can/bottle colours for vending machine shelves. */\r\nexport const DRINKS = [\r\n  0xe0453f, 0xf4c033, 0x3d6ec4, 0x2f9c9a, 0xef8a3c, 0x8f6fb5,\r\n  0x5aa578, 0xf4f2f6, 0xe86f9c, 0x44b4d8, 0xc94f7a, 0x9dbb3c,\r\n];\r\n","arcade/runner/worlds/sakura/post.js":"// Adapted from Sakura Crossing, Kenton Wang, MIT. See LICENSE.txt.\r\nimport * as THREE from 'three';\r\nimport { FullScreenQuad } from './quad.js';\r\nimport { PAL } from './palette.js';\r\n\r\n/* ------------------------------------------------------------------ *\r\n * The 3D-to-2D pipeline.\r\n *\r\n *   scene  ->  rtScene (colour + depth texture)\r\n *          ->  ink pass      : screen-space line work from the depth buffer\r\n *          ->  grade pass    : anime colour grade + linear->sRGB\r\n *          ->  fxaa pass     : clean up the line work, straight to screen\r\n *\r\n * Lines come from a *second difference* of linearised depth.  A first\r\n * difference would smear ink across the road wherever the surface is\r\n * grazing the camera; the second difference is flat across any planar\r\n * surface no matter how oblique, so it only fires on real silhouettes and\r\n * real creases.  Positive curvature (the near side of a silhouette, a\r\n * convex ridge) inks strongly; negative curvature (inside corners) inks\r\n * faintly, which mimics the lighter contact lines an animator draws.\r\n * ------------------------------------------------------------------ */\r\n\r\nconst INK_SHADER = {\r\n  uniforms: {\r\n    tDiffuse: { value: null },\r\n    tDepth: { value: null },\r\n    uTexel: { value: new THREE.Vector2() },\r\n    uNear: { value: 0.25 },\r\n    uFar: { value: 600 },\r\n    uInk: { value: new THREE.Color(PAL.ink) },\r\n    uThickness: { value: 1.35 },\r\n    uSens: { value: 0.0042 },\r\n    uConcave: { value: 0.026 },\r\n    uConcaveAmount: { value: 0.42 },\r\n    uFadeStart: { value: 40.0 },\r\n    uFadeEnd: { value: 98.0 },\r\n    uStrength: { value: 1.0 },\r\n    uSkyDepth: { value: 420.0 },\r\n  },\r\n  vertexShader: /* glsl */ `\r\n    varying vec2 vUv;\r\n    void main() {\r\n      vUv = uv;\r\n      gl_Position = vec4( position.xy, 0.0, 1.0 );\r\n    }\r\n  `,\r\n  fragmentShader: /* glsl */ `\r\n    #include <packing>\r\n    uniform sampler2D tDiffuse;\r\n    uniform sampler2D tDepth;\r\n    uniform vec2 uTexel;\r\n    uniform float uNear, uFar;\r\n    uniform vec3 uInk;\r\n    uniform float uThickness, uSens, uConcave, uConcaveAmount;\r\n    uniform float uFadeStart, uFadeEnd, uStrength, uSkyDepth;\r\n    varying vec2 vUv;\r\n\r\n    float linearDepth( vec2 uv ) {\r\n      float d = texture2D( tDepth, uv ).x;\r\n      return -perspectiveDepthToViewZ( d, uNear, uFar );\r\n    }\r\n\r\n    void main() {\r\n      vec3 col = texture2D( tDiffuse, vUv ).rgb;\r\n\r\n      vec2 t = uTexel * uThickness;\r\n      float dc = linearDepth( vUv );\r\n\r\n      if ( dc > uSkyDepth ) {\r\n        // pure sky: nothing to ink\r\n        gl_FragColor = vec4( col, 1.0 );\r\n        return;\r\n      }\r\n\r\n      float dl = linearDepth( vUv - vec2( t.x, 0.0 ) );\r\n      float dr = linearDepth( vUv + vec2( t.x, 0.0 ) );\r\n      float du = linearDepth( vUv + vec2( 0.0, t.y ) );\r\n      float dd = linearDepth( vUv - vec2( 0.0, t.y ) );\r\n\r\n      // second difference of linear depth, normalised by distance\r\n      float sx = ( dl + dr - 2.0 * dc ) / dc;\r\n      float sy = ( du + dd - 2.0 * dc ) / dc;\r\n\r\n      float convex  = max( 0.0,  sx ) + max( 0.0,  sy );\r\n      float concave = max( 0.0, -sx ) + max( 0.0, -sy );\r\n\r\n      float edge = smoothstep( uSens * 0.32, uSens, convex );\r\n      edge = max( edge, smoothstep( uConcave, uConcave * 3.4, concave ) * uConcaveAmount );\r\n\r\n      // let the background dissolve into the haze instead of getting busy\r\n      edge *= 1.0 - smoothstep( uFadeStart, uFadeEnd, dc );\r\n      edge *= uStrength;\r\n\r\n      // ink keeps a whisper of the underlying hue so it never looks pasted on\r\n      vec3 line = mix( uInk, col * 0.42, 0.22 );\r\n      gl_FragColor = vec4( mix( col, line, clamp( edge, 0.0, 1.0 ) ), 1.0 );\r\n    }\r\n  `,\r\n};\r\n\r\nconst GRADE_SHADER = {\r\n  uniforms: {\r\n    tDiffuse: { value: null },\r\n    uShadowTint: { value: new THREE.Color(0xada8d0) },\r\n    uLightTint: { value: new THREE.Color(0xfff7e8) },\r\n    uSaturation: { value: 1.12 },\r\n    uLift: { value: 0.032 },\r\n    uVignette: { value: 0.15 },\r\n    uWarmth: { value: 0.05 },\r\n  },\r\n  vertexShader: INK_SHADER.vertexShader,\r\n  fragmentShader: /* glsl */ `\r\n    uniform sampler2D tDiffuse;\r\n    uniform vec3 uShadowTint, uLightTint;\r\n    uniform float uSaturation, uLift, uVignette, uWarmth;\r\n    varying vec2 vUv;\r\n\r\n    vec3 linearToSRGB( vec3 c ) {\r\n      return mix( c * 12.92, 1.055 * pow( max( c, vec3( 0.0031308 ) ), vec3( 1.0 / 2.4 ) ) - 0.055,\r\n                  step( 0.0031308, c ) );\r\n    }\r\n\r\n    void main() {\r\n      vec3 c = texture2D( tDiffuse, vUv ).rgb;\r\n      float l = dot( c, vec3( 0.2126, 0.7152, 0.0722 ) );\r\n\r\n      // split-tone: cool violet in the darks, warm paper white in the lights\r\n      float k = smoothstep( 0.02, 0.55, l );\r\n      c *= mix( uShadowTint, uLightTint, k );\r\n\r\n      // gentle overall warmth, like late afternoon light through blossom\r\n      c += vec3( uWarmth, uWarmth * 0.45, 0.0 ) * l * 0.35;\r\n\r\n      // keep shadows readable -- never crushed to black\r\n      c = c + uLift * ( 1.0 - k );\r\n\r\n      c = mix( vec3( l ), c, uSaturation );\r\n\r\n      float r = length( vUv - 0.5 ) * 1.42;\r\n      c *= 1.0 - uVignette * pow( clamp( r, 0.0, 1.0 ), 2.6 );\r\n\r\n      gl_FragColor = vec4( linearToSRGB( max( c, vec3( 0.0 ) ) ), 1.0 );\r\n    }\r\n  `,\r\n};\r\n\r\nconst FXAA_SHADER = {\r\n  uniforms: {\r\n    tDiffuse: { value: null },\r\n    uTexel: { value: new THREE.Vector2() },\r\n  },\r\n  vertexShader: INK_SHADER.vertexShader,\r\n  fragmentShader: /* glsl */ `\r\n    uniform sampler2D tDiffuse;\r\n    uniform vec2 uTexel;\r\n    varying vec2 vUv;\r\n\r\n    float luma( vec3 c ) { return dot( c, vec3( 0.299, 0.587, 0.114 ) ); }\r\n\r\n    void main() {\r\n      vec3 cM = texture2D( tDiffuse, vUv ).rgb;\r\n      vec3 cNW = texture2D( tDiffuse, vUv + vec2( -uTexel.x, -uTexel.y ) ).rgb;\r\n      vec3 cNE = texture2D( tDiffuse, vUv + vec2(  uTexel.x, -uTexel.y ) ).rgb;\r\n      vec3 cSW = texture2D( tDiffuse, vUv + vec2( -uTexel.x,  uTexel.y ) ).rgb;\r\n      vec3 cSE = texture2D( tDiffuse, vUv + vec2(  uTexel.x,  uTexel.y ) ).rgb;\r\n\r\n      float lM = luma( cM ), lNW = luma( cNW ), lNE = luma( cNE ),\r\n            lSW = luma( cSW ), lSE = luma( cSE );\r\n      float lMin = min( lM, min( min( lNW, lNE ), min( lSW, lSE ) ) );\r\n      float lMax = max( lM, max( max( lNW, lNE ), max( lSW, lSE ) ) );\r\n\r\n      vec2 dir = vec2(\r\n        -( ( lNW + lNE ) - ( lSW + lSE ) ),\r\n         ( ( lNW + lSW ) - ( lNE + lSE ) )\r\n      );\r\n      float reduce = max( ( lNW + lNE + lSW + lSE ) * 0.25 * 0.18, 1.0 / 128.0 );\r\n      float rcp = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + reduce );\r\n      dir = clamp( dir * rcp, vec2( -8.0 ), vec2( 8.0 ) ) * uTexel;\r\n\r\n      vec3 rgbA = 0.5 * (\r\n        texture2D( tDiffuse, vUv + dir * ( 1.0 / 3.0 - 0.5 ) ).rgb +\r\n        texture2D( tDiffuse, vUv + dir * ( 2.0 / 3.0 - 0.5 ) ).rgb );\r\n      vec3 rgbB = rgbA * 0.5 + 0.25 * (\r\n        texture2D( tDiffuse, vUv - dir * 0.5 ).rgb +\r\n        texture2D( tDiffuse, vUv + dir * 0.5 ).rgb );\r\n\r\n      float lB = luma( rgbB );\r\n      gl_FragColor = vec4( ( lB < lMin || lB > lMax ) ? rgbA : rgbB, 1.0 );\r\n    }\r\n  `,\r\n};\r\n\r\nfunction makeQuad(def) {\r\n  const mat = new THREE.ShaderMaterial({\r\n    uniforms: THREE.UniformsUtils.clone(def.uniforms),\r\n    vertexShader: def.vertexShader,\r\n    fragmentShader: def.fragmentShader,\r\n    depthTest: false,\r\n    depthWrite: false,\r\n  });\r\n  return { quad: new FullScreenQuad(mat), mat };\r\n}\r\n\r\nexport class Pipeline {\r\n  constructor(renderer, scene, camera, { pixelBudget = 4.6e6 } = {}) {\r\n    this.renderer = renderer;\r\n    this.scene = scene;\r\n    this.camera = camera;\r\n    this.pixelBudget = pixelBudget;\r\n    this.size = new THREE.Vector2(1, 1);\r\n\r\n    const opts = {\r\n      type: THREE.HalfFloatType,\r\n      minFilter: THREE.LinearFilter,\r\n      magFilter: THREE.LinearFilter,\r\n      depthBuffer: true,\r\n      stencilBuffer: false,\r\n      colorSpace: THREE.NoColorSpace,\r\n    };\r\n    this.rtScene = new THREE.WebGLRenderTarget(2, 2, opts);\r\n    this.rtScene.depthTexture = new THREE.DepthTexture(2, 2);\r\n    this.rtScene.depthTexture.format = THREE.DepthFormat;\r\n    this.rtScene.depthTexture.type = THREE.UnsignedIntType;\r\n    this.rtScene.depthTexture.minFilter = THREE.NearestFilter;\r\n    this.rtScene.depthTexture.magFilter = THREE.NearestFilter;\r\n\r\n    this.rtA = new THREE.WebGLRenderTarget(2, 2, { ...opts, depthBuffer: false });\r\n    this.rtB = new THREE.WebGLRenderTarget(2, 2, {\r\n      ...opts, type: THREE.UnsignedByteType, depthBuffer: false,\r\n    });\r\n\r\n    const ink = makeQuad(INK_SHADER);\r\n    const grade = makeQuad(GRADE_SHADER);\r\n    const fxaa = makeQuad(FXAA_SHADER);\r\n    this.ink = ink;\r\n    this.grade = grade;\r\n    this.fxaa = fxaa;\r\n\r\n    ink.mat.uniforms.tDepth.value = this.rtScene.depthTexture;\r\n    this.enabled = { ink: true, grade: true, fxaa: true };\r\n  }\r\n\r\n  /** Resolution scale: supersample a little on low-DPI screens for clean ink. */\r\n  setSize(w, h) {\r\n    const dpr = window.devicePixelRatio || 1;\r\n    let scale = this.forceScale || (dpr < 1.5 ? 1.5 : Math.min(dpr, 2));\r\n    if (w * h * scale * scale > this.pixelBudget) {\r\n      scale = Math.max(1, Math.sqrt(this.pixelBudget / (w * h)));\r\n    }\r\n    this.scale = scale;\r\n    const rw = Math.max(2, Math.floor(w * scale));\r\n    const rh = Math.max(2, Math.floor(h * scale));\r\n    this.size.set(rw, rh);\r\n\r\n    this.renderer.setPixelRatio(1);\r\n    this.renderer.setSize(w, h, true);\r\n\r\n    this.rtScene.setSize(rw, rh);\r\n    this.rtA.setSize(rw, rh);\r\n    this.rtB.setSize(rw, rh);\r\n\r\n    const texel = new THREE.Vector2(1 / rw, 1 / rh);\r\n    this.ink.mat.uniforms.uTexel.value.copy(texel);\r\n    this.fxaa.mat.uniforms.uTexel.value.copy(texel);\r\n    this.ink.mat.uniforms.uNear.value = this.camera.near;\r\n    this.ink.mat.uniforms.uFar.value = this.camera.far;\r\n    // scale ink weight with resolution so lines stay ~2 device px\r\n    this.ink.mat.uniforms.uThickness.value = 1.05 + 0.55 * scale;\r\n  }\r\n\r\n  render() {\r\n    const r = this.renderer;\r\n    r.setRenderTarget(this.rtScene);\r\n    r.clear();\r\n    r.render(this.scene, this.camera);\r\n    this.sceneStats = {calls: r.info.render.calls, triangles: r.info.render.triangles};\r\n\r\n    let src = this.rtScene.texture;\r\n\r\n    if (this.enabled.ink) {\r\n      this.ink.mat.uniforms.tDiffuse.value = src;\r\n      r.setRenderTarget(this.rtA);\r\n      this.ink.quad.render(r);\r\n      src = this.rtA.texture;\r\n    }\r\n\r\n    const last = this.enabled.fxaa ? this.rtB : null;\r\n    this.grade.mat.uniforms.tDiffuse.value = src;\r\n    r.setRenderTarget(last);\r\n    this.grade.quad.render(r);\r\n\r\n    if (this.enabled.fxaa) {\r\n      this.fxaa.mat.uniforms.tDiffuse.value = this.rtB.texture;\r\n      r.setRenderTarget(null);\r\n      this.fxaa.quad.render(r);\r\n    }\r\n    r.setRenderTarget(null);\r\n  }\r\n\r\n  dispose() {\r\n    [this.rtScene, this.rtA, this.rtB].forEach((rt) => rt.dispose());\r\n    [this.ink, this.grade, this.fxaa].forEach((p) => {\r\n      p.quad.dispose();\r\n      p.mat.dispose();\r\n    });\r\n  }\r\n}\r\n","arcade/runner/worlds/sakura/quad.js":"import * as THREE from 'three';\r\nexport class FullScreenQuad {\r\n  constructor(material) {\r\n    this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\r\n    this.mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);\r\n    this.mesh.frustumCulled = false;\r\n  }\r\n  render(renderer) { renderer.render(this.mesh, this.camera); }\r\n  dispose() { this.mesh.geometry.dispose(); }\r\n}\r\n","arcade/runner/worlds/scene.mjs":"import * as T from 'three';\r\nimport {Pipeline} from './sakura/post.js';\r\nimport {createSurfaceTexture} from './textures.mjs';\r\nimport {courseBuses,courseCoins,coursePickups,courseHazards,jumpHeight,laserState} from './course.mjs';\r\nimport {powerModel} from './power-art.mjs';\r\nimport {foundryLandmarks,templeLandmarks,metroLandmarks} from './landmarks.mjs';\r\nimport {loadRunnerCoach} from '../roster-coach.mjs';\r\n\r\n// The same depth-ink/grade/FXAA pipeline used by Little Boyfriend; one renderer for runner and coach.\r\nexport function createWorldScene(canvas,{beforeRender=()=>{},coachFactory=null}={}){\r\n  const renderer=new T.WebGLRenderer({canvas,antialias:false,powerPreference:'high-performance'});\r\n  const scene=new T.Scene(), camera=new T.PerspectiveCamera(65,1,.25,210);\r\n  const pipeline=new Pipeline(renderer,scene,camera,{pixelBudget:1.5e6});\r\n  scene.add(new T.HemisphereLight(0xf6e7ff,0x51416b,1.45));\r\n  // A single low side key makes the forward course read in layers without\r\n  // enabling per-actor shadow maps on mobile hardware.\r\n  const sun=new T.DirectionalLight(0xffd7b0,2.8);sun.position.set(-16,22,13);sun.target.position.set(0,0,-34);scene.add(sun,sun.target);\r\n  const root=new T.Group();scene.add(root);\r\n  const mats=new Map(),geos=[],textures=new Map();let surface='metal';\r\n  function mat(c,kind=surface){\r\n    if([0xe5ba65,0xd6c893,0xe6b65f,0xffd269].includes(c))kind='gold';\r\n    if([0x4e8a6a,0x79a57b,0x5a8b69,0x4f8866,0x467860].includes(c))kind='moss';\r\n    if([0x776c73,0x786648,0x725242].includes(c))kind='wood';\r\n    const key=kind+':'+c;if(!textures.has(kind))textures.set(kind,createSurfaceTexture(T,kind));\r\n    if(!mats.has(key))mats.set(key,new T.MeshToonMaterial({color:c,map:textures.get(kind)}));return mats.get(key);\r\n  }\r\n  function mesh(g,c,x,y,z,parent=root,kind){geos.push(g);const m=new T.Mesh(g,mat(c,kind));m.userData.sharedMaterial=true;m.position.set(x,y,z);parent.add(m);return m;}\r\n  function box(x,y,z,w,h,d,c,p,kind){const g=new T.BoxGeometry(w,h,d),pos=g.attributes.position,n=g.attributes.normal,uv=g.attributes.uv;\r\n    for(let i=0;i<uv.count;i++){const a=Math.abs(n.getX(i))>.5?pos.getZ(i):pos.getX(i),b=Math.abs(n.getY(i))>.5?pos.getZ(i):pos.getY(i);uv.setXY(i,a*.45,b*.45);}return mesh(g,c,x,y,z,p,kind);}\r\n  const sphere=(x,y,z,r,c,p)=>mesh(new T.SphereGeometry(r,10,7),c,x,y,z,p);\r\n  const cylinder=(x,y,z,r,h,c,p,kind)=>mesh(new T.CylinderGeometry(r,r,h,12),c,x,y,z,p,kind);\r\n  function branch(a,b,r,c,p,kind='wood'){const av=new T.Vector3(...a),bv=new T.Vector3(...b),m=cylinder(...av.clone().add(bv).multiplyScalar(.5).toArray(),r,av.distanceTo(bv),c,p,kind);m.quaternion.setFromUnitVectors(new T.Vector3(0,1,0),bv.sub(av).normalize());return m;}\r\n  let segments=[],coins=[],obstacles=[],pickups=[],routeMarkers=[],portal,coach,powerRig,pursuer,exitTunnel,burst,world=-1,loop=-1,leg=-1;let previousRun=null,lastRunTime=0,arrivalSerial=0,arrivalAt=-100,eventSerial=0,burstAt=-100,burstEvent=null,pickupSerial=0,pickupAt=-100,pickupKind=null,activePowers={};let routeVehicles=[],hazardActors=[],torchFlames=[],spaceAsteroids=[],vfx={},coinInstances,coinStamps;const coinTransform=new T.Object3D();\r\n  const extras=[],coachModels=[];\r\n  const palettes=[{sky:0x1a2144,fog:0x353d68,ground:0x343553,wall:0x635889,trim:0x9a87ce},{sky:0xb9d6ba,fog:0xc4d4b4,ground:0x9b9675,wall:0xb6a47f,trim:0xd6c893},{sky:0xa5bcdc,fog:0xbed7ed,ground:0x777485,wall:0xf0c7bc,trim:0xe6b65f}];\r\n  function disposeWorld(){for(const resource of extras.splice(0))resource.dispose();for(const group of coachModels.splice(0))group.traverse(o=>{if(!o.userData.rosterShared)o.geometry?.dispose();if(o.userData.sharedMaterial||o.userData.rosterShared)return;if(Array.isArray(o.material))o.material.forEach(m=>m.dispose());else o.material?.dispose();});root.clear();geos.splice(0).forEach(g=>g.dispose());coins=[];obstacles=[];pickups=[];routeMarkers=[];segments=[];routeVehicles=[];hazardActors=[];torchFlames=[];spaceAsteroids=[];vfx={};}\r\n  function arch(z,p,group){\r\n    box(-5,3,z,1.1,6,1.3,p.wall,group);box(5,3,z,1.1,6,1.3,p.wall,group);box(0,6,z,11.3,1,1.4,p.trim,group);\r\n    for(const x of [-4.9,4.9]){box(x,1,z,1.35,.35,1.6,p.trim,group);box(x,5.2,z,1.35,.35,1.6,p.trim,group);}\r\n  }\r\n  function buildBus(b,g){\r\n    const length=b.roofEnd-b.rampEnd,half=length/2,color=b.lane===0?0xe0aa60:b.lane===1?0x75adad:0xb19acb;\r\n    shadow(0,0,4,length+4,g);\r\n    box(0,1.55,0,2.5,3.1,length,color,g,'metal');box(0,.38,0,2.6,.5,length+.2,0x393750,g,'rubber');\r\n    box(0,b.height-.08,0,2.55,.16,length+.2,0xe7d5b3,g,'metal');\r\n    for(const side of [-1,1]){\r\n      for(let z=-half+2;z<half-1;z+=2.2){box(side*1.26,2.05,z,.055,.9,1.65,0x3d526e,g,'metal');box(side*1.3,1.46,z,.04,.04,1.9,0xffddb0,g,'gold');}\r\n      for(const z of [-half+2,half-2]){const tyre=cylinder(side*1.25,.55,z,.52,.23,0x302e42,g,'rubber');tyre.rotation.z=Math.PI/2;const hub=cylinder(side*1.39,.55,z,.24,.025,0xc3bdc5,g,'metal');hub.rotation.z=Math.PI/2;}\r\n      box(side*1.28,2.8,0,.08,.08,length-.4,0xffd269,g,'gold');\r\n      box(side*1.29,1.48,half-2.4,.08,2.15,1.15,0x47536b,g,'metal');\r\n    }\r\n    for(const z of [-half-.03,half+.03]){\r\n      box(0,2.05,z,2.08,.95,.05,0x3d526e,g,'metal');box(0,.7,z,1.5,.4,.08,0x393750,g,'rubber');\r\n      for(const x of [-.8,.8])sphere(x,1.15,z,.14,z>0?0xffe7aa:0xde8d98,g);\r\n      box(0,2.85,z,1.2,.22,.07,0xf3d18e,g,'gold');\r\n    }\r\n    // Flat marked roof lane remains unobstructed; vents sit outside the running strip.\r\n    for(let z=-half+1;z<half;z+=3){box(0,b.height+.015,z,.12,.02,1,0xffd269,g,'gold');for(const side of [-1,1])box(side*1.05,b.height+.12,z,.26,.22,.8,0x8c91a0,g,'metal');}\r\n    ramp(half,half+(b.rampEnd-b.start),b.height,0,g);\r\n    ramp(-half-(b.end-b.roofEnd),-half,0,b.height,g);\r\n  }\r\n  function ramp(z0,z1,y0,y1,parent){\r\n    const w=1.3,geometry=new T.BufferGeometry();geometry.setAttribute('position',new T.Float32BufferAttribute([-w,0,z0,w,0,z0,-w,0,z1,w,0,z1,-w,y0,z0,w,y0,z0,-w,y1,z1,w,y1,z1],3));\r\n    geometry.setIndex([4,6,5,5,6,7,0,4,1,1,4,5,2,3,6,3,7,6,0,2,4,2,6,4,1,5,3,3,5,7]);geometry.computeVertexNormals();geometry.setAttribute('uv',new T.Float32BufferAttribute([0,0,1,0,0,4,1,4,0,0,1,0,0,4,1,4],2));mesh(geometry,0xbba8c2,0,0,0,parent,'metal');\r\n    for(const side of [-1,1])branch([side*1.28,y0+.06,z0],[side*1.28,y1+.06,z1],.065,0xffd269,parent,'gold');\r\n    for(let t=.08;t<1;t+=.12){const y=y0+(y1-y0)*t,z=z0+(z1-z0)*t;box(0,y+.025,z,2.3,.025,.11,0x514565,parent,'rubber');}\r\n  }\r\n  function shadow(x,z,w,d,parent=root){\r\n    const art=document.createElement('canvas');art.width=64;art.height=64;const ctx=art.getContext('2d'),gradient=ctx.createRadialGradient(32,32,2,32,32,32);gradient.addColorStop(0,'#241d3d80');gradient.addColorStop(1,'#241d3d00');ctx.fillStyle=gradient;ctx.fillRect(0,0,64,64);\r\n    const texture=new T.CanvasTexture(art),material=new T.MeshBasicMaterial({map:texture,transparent:true,depthWrite:false});extras.push(texture,material);const m=new T.Mesh(new T.PlaneGeometry(w,d),material);geos.push(m.geometry);m.rotation.x=-Math.PI/2;\r\n    // Warm side light from camera-left casts contact ink down-course and right.\r\n    m.position.set(x+d*.12,.035,z-d*.12);parent.add(m);\r\n  }\r\n  // Static scenery retains its geometry detail but shares one draw call per material/section.\r\n  function batchStatic(group){\r\n    group.updateMatrixWorld(true);const inverse=group.matrixWorld.clone().invert(),batches=new Map();\r\n    group.traverse(o=>{if(!o.isMesh||o.userData.dynamic||o.material.transparent||Array.isArray(o.material))return;const g=o.geometry.clone().applyMatrix4(new T.Matrix4().multiplyMatrices(inverse,o.matrixWorld)),key=o.material.uuid;\r\n      if(!batches.has(key))batches.set(key,{material:o.material,position:[],normal:[],uv:[],index:[],objects:[]});\r\n      const b=batches.get(key),offset=b.position.length/3,position=g.attributes.position,normal=g.attributes.normal,uv=g.attributes.uv;\r\n      for(let i=0;i<position.count;i++){b.position.push(position.getX(i),position.getY(i),position.getZ(i));b.normal.push(normal?.getX(i)||0,normal?.getY(i)||0,normal?.getZ(i)||0);b.uv.push(uv?.getX(i)||0,uv?.getY(i)||0);}\r\n      if(g.index)for(let i=0;i<g.index.count;i++)b.index.push(offset+g.index.getX(i));else for(let i=0;i<position.count;i++)b.index.push(offset+i);\r\n      b.objects.push(o);g.dispose();\r\n    });\r\n    for(const b of batches.values()){const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(b.position,3));g.setAttribute('normal',new T.Float32BufferAttribute(b.normal,3));g.setAttribute('uv',new T.Float32BufferAttribute(b.uv,2));g.setIndex(b.index);geos.push(g);for(const o of b.objects)o.removeFromParent();group.add(new T.Mesh(g,b.material));}\r\n  }\r\n  function buildPursuer(index){\r\n    pursuer=new T.Group();pursuer.name=['armie-starship','temple-coach','metro-coach'][index];root.add(pursuer);\r\n    const actor=loadRunnerCoach(T,pursuer,index);coachModels.push(actor);\r\n  }\r\n  function build(index,round=0,routeLeg=0){\r\n    world=index;loop=round;leg=routeLeg;disposeWorld();surface=['metal','stone','plaster'][index];const p=palettes[index];const sky=document.createElement('canvas');sky.width=512;sky.height=256;const sc=sky.getContext('2d'),sg=sc.createLinearGradient(0,0,0,256);sg.addColorStop(0,['#10152f','#91b7ab','#91adcb'][index]);sg.addColorStop(.6,['#343b68','#d2d8ad','#d8c7cf'][index]);sg.addColorStop(1,['#8591bd','#f7dbc2','#f7dbc2'][index]);sc.fillStyle=sg;sc.fillRect(0,0,512,256);\r\n    if(index===0){for(let i=0;i<90;i++){const x=(i*137+29)%512,y=(i*71+11)%180;sc.fillStyle=i%7===0?'#f4d9ff':'#d9dcff';sc.globalAlpha=.35+(i%5)*.12;sc.fillRect(x,y,i%9===0?2:1,i%9===0?2:1);}sc.globalAlpha=1;sc.fillStyle='#e6a87a';sc.beginPath();sc.arc(414,174,35,0,Math.PI*2);sc.fill();sc.fillStyle='#f2cead';sc.beginPath();sc.arc(409,168,26,0,Math.PI*2);sc.fill();}\r\n    if(index===1){sc.fillStyle='#546a52';sc.beginPath();sc.moveTo(0,220);sc.lineTo(70,181);sc.lineTo(130,205);sc.lineTo(185,170);sc.lineTo(250,198);sc.lineTo(310,166);sc.lineTo(390,202);sc.lineTo(450,178);sc.lineTo(512,211);sc.lineTo(512,256);sc.lineTo(0,256);sc.fill();sc.fillStyle='#68533c';sc.beginPath();sc.moveTo(180,222);sc.lineTo(205,222);sc.lineTo(205,204);sc.lineTo(220,204);sc.lineTo(220,186);sc.lineTo(234,186);sc.lineTo(234,164);sc.lineTo(247,164);sc.lineTo(247,138);sc.lineTo(265,138);sc.lineTo(265,164);sc.lineTo(280,164);sc.lineTo(280,186);sc.lineTo(294,186);sc.lineTo(294,204);sc.lineTo(309,204);sc.lineTo(309,222);sc.lineTo(332,222);sc.lineTo(332,242);sc.lineTo(180,242);sc.fill();sc.fillStyle='#242323';sc.fillRect(248,177,16,39);}\r\n    if(index===2){sc.fillStyle=routeLeg===1?'#719fce':'#568fd1';sc.fillRect(0,0,512,256);sc.fillStyle='#d5e8f5';for(let i=0;i<6;i++){const x=(i*93+28)%512,y=66+(i*31)%75;sc.fillRect(x,y,38+(i%3)*11,3);sc.fillRect(x+9,y-3,23,3);}sc.fillStyle='#e55279';sc.fillRect(0,185,512,71);sc.fillStyle='#17b7c6';for(let i=0;i<8;i++)sc.fillRect(i*73,195,37,10);}\r\n    const skyMap=new T.CanvasTexture(sky);skyMap.colorSpace=T.SRGBColorSpace;extras.push(skyMap);scene.background=skyMap;scene.fog=new T.Fog(p.fog,27,index===1?185:index===2?160:128);\r\n    // Three different spatial languages: cylindrical foundry, open ravine ruins, asymmetric rail yard.\r\n    if(index===1){\r\n      const water=new T.Mesh(new T.PlaneGeometry(220,260),new T.MeshBasicMaterial({color:0x14535b}));water.rotation.x=-Math.PI/2;water.position.set(0,-4.5,-72);root.add(water);geos.push(water.geometry);extras.push(water.material);\r\n      for(const side of [-1,1]){\r\n        const river=new T.Mesh(new T.PlaneGeometry(24,245),new T.MeshBasicMaterial({color:0x078f9b}));river.rotation.x=-Math.PI/2;river.position.set(side*16.25,-.05,-96);root.add(river);geos.push(river.geometry);extras.push(river.material);\r\n        for(let q=0;q<8;q++){\r\n          box(side*(7.2+q%2*.5),-.5,-18-q*24,2.6,.025,.1,0x73c4b3,root,'moss');\r\n        }\r\n      }\r\n      for(const side of [-1,1])for(let q=0;q<6;q++){\r\n        const cliff=mesh(new T.DodecahedronGeometry(1,0),q%2?0x4a5144:0x655844,side*(14+q*1.2),-1.8+q*.55,-35-q*18,root,'stone');cliff.scale.set(5+q*.7,5+q%3,14+q*1.2);\r\n        box(side*(12.8+q*1.1),-1.7,-42-q*20,.38,7,.16,0x75c8c1,root,'moss');\r\n      }\r\n      // A large stepped sanctuary stays on the far horizon, beyond the playable bridge.\r\n      const z=-132;\r\n      for(const [w,h,y,c] of [[48,12,-1.8,0x756244],[38,10,8.2,0x927950],[29,9,17.7,0x826b49],[20,8,26.2,0xa0875b],[11,6,33.2,0xb49a6b]])box(0,y,z,w,h,18,c,root,'stone');\r\n      // Forward-facing doorway and stone jambs sit on the near face of the sanctuary.\r\n      box(0,2.9,z+9.5,6,11,.8,0x554530,root,'stone');box(0,3.2,z+9.94,3.1,7.4,.16,0x171b1b,root,'stone');\r\n      for(const x of [-16,-9,9,16]){const column=cylinder(x,10,z+9.8,.72,11,0xd3bd8e,root,'stone');box(x,4.5,z+9.8,1.8,.45,1.5,0xd6c893,root,'gold');box(x,15.5,z+9.8,1.8,.4,1.5,0xd6c893,root,'gold');}\r\n      box(0,9,z+9.9,10,.9,1.6,0xd6c893,root,'gold');\r\n      const crown=mesh(new T.ConeGeometry(8,8,4),0x766344,0,39,z-1,root,'stone');crown.rotation.y=Math.PI/4;\r\n      for(let i=0;i<8;i++){const step=box(0,-7+i*1.5,z-11-i*1.1,16-i*.65,.55,2,0xc0a77d,root,'stone');step.userData.distantTemple=true;}\r\n    }\r\n    else if(index===2)box(0,-.55,-65,200,.7,220,0x8b8295,root,'ballast');\r\n    const flameInk=new T.MeshBasicMaterial({color:0xffad42,toneMapped:false});extras.push(flameInk);\r\n    for(let s=0;s<10;s++){\r\n      const g=new T.Group();g.position.z=-s*18;root.add(g);segments.push(g);\r\n      if(index===0){\r\n        box(0,-.25,0,8.6,.5,18,p.ground,g,'metal');\r\n        const hoop=mesh(new T.TorusGeometry(7,.22,7,36,Math.PI),p.trim,0,0,-5,g,'gold');\r\n        // Panoramic starship windows place every mechanical encounter inside an orbital foundry.\r\n        for(const side of [-1,1]){\r\n          box(side*6.2,3.05,-7,3.7,3.1,.35,0x38324f,g,'metal');box(side*6.2,3.05,-6.78,3.2,2.6,.08,0x263b68,g,'metal');\r\n          const planet=sphere(side*6.15,3.08,-6.68,.93,s%2?0xc783a4:0xd99d70,g);planet.scale.set(1.15,.9,.24);\r\n          const atmosphere=mesh(new T.TorusGeometry(1.12,.045,5,24),0xa8c8ee,side*6.15,3.08,-6.57,g,'metal');atmosphere.scale.y=.8;\r\n          for(let q=0;q<3;q++){const rock=mesh(new T.DodecahedronGeometry(.18+q*.035),q%2?0xb4b0cb:0x8c8ba8,side*(5.15+q*.67),2.4+q*.36,-6.6,g,'metal');rock.userData.dynamic=true;rock.rotation.set(q*.3,q*.7,q*.4);spaceAsteroids.push({mesh:rock,phase:s+q+side});}\r\n          for(const dx of [-1.65,1.65])box(side*6.2+dx,3.05,-6.55,.11,2.95,.12,p.trim,g,'gold');\r\n        }\r\n        for(const side of [-1,1]){\r\n          box(side*4.5,.25,0,.3,.5,18,p.trim,g,'gold');\r\n          branch([side*4.8,0,-7],[side*6.6,5,-7],.22,p.trim,g,'gold');\r\n          const drum=cylinder(side*8,2,-4,2.4,8,0x80668e,g,'metal');drum.rotation.x=Math.PI/2;\r\n          for(let q=0;q<3;q++){const pipe=cylinder(side*(5.6+q*.45),1+q*.65,0,.16,18,q===1?p.trim:0x6b5a82,g,'metal');pipe.rotation.x=Math.PI/2;}\r\n          const gear=mesh(new T.CylinderGeometry(2.4,2.4,.55,12),p.trim,side*8.4,4,-6,g,'gold');gear.rotation.x=Math.PI/2;\r\n          for(let q=0;q<12;q++){const a=q/12*Math.PI*2,tooth=box(side*8.4+Math.cos(a)*2.5,4+Math.sin(a)*2.5,-5.7,.75,.6,.8,p.trim,g,'gold');tooth.rotation.z=a;}\r\n          sphere(side*8.4,4,-5.1,.65,0x372c54,g);\r\n          box(side*11,-3,0,4,12,9,0x49325f,g,'metal');\r\n        }\r\n        shadow(0,-4,10,8,g);\r\n        for(const side of [-1,1]){for(let q=0;q<4;q++){box(side*4.1,.03,-7+q*4,.3,.05,1.4,0xffd269,g,'gold');}const gauge=cylinder(side*6.1,2.8,-1,.5,.12,0xe7d5b3,g,'metal');gauge.rotation.x=Math.PI/2;branch([side*6.1,2.8,-.9],[side*6.35,3.05,-.9],.025,0x49325f,g,'metal');}\r\n        for(let q=0;q<5;q++){box(0,.02,-8+q*4,8.3,.04,.12,0xb098aa,g,'metal');box(0,-1.3,-8+q*4,8.3,1.9,.4,0x443451,g,'metal');}\r\n        foundryLandmarks({T,box,cylinder,mesh,group:g,segment:s});\r\n      }else if(index===1){\r\n        // Independent irregular paving on a high bridge, with exposed edges and no corridor walls.\r\n        for(let q=0;q<5;q++)for(let j=0;j<3;j++){\r\n          const slab=box((j-1)*2.9,-.22+Math.sin(s+q+j)*.04,-7.3+q*3.6,2.78,.5,3.5,q%3?0xb6a47f:0x91886b,g,'stone');slab.rotation.y=Math.sin(s*4+q+j)*.025;\r\n        }\r\n        for(const side of [-1,1]){\r\n          box(side*4.22,-.34,0,.18,.72,18,0x6c6049,g,'stone');\r\n          const rock=mesh(new T.DodecahedronGeometry(1.8,0),0x8e8770,side*9.1,-1.1,-4,g,'stone');rock.scale.set(1,1.5,1.7);rock.rotation.z=side*.3;\r\n          shadow(side*4,-4,4,7,g);\r\n          if(s%2===0){\r\n            // Stepped pyramids and leaning carved guardian heads, unlike the foundry arches.\r\n            for(let q=0;q<4;q++)box(side*11,q*.85-1,-1,7-q*1.35,.9,7-q*1.35,0xa89976,g,'stone');\r\n            const head=mesh(new T.DodecahedronGeometry(1.8),0xb6a47f,side*6.2,2.3,-7,g,'stone');head.scale.set(.85,1.3,.65);head.rotation.z=side*.2;\r\n            for(const eye of [-1,1])box(side*6.2+eye*.5,2.65,-5.9,.5,.22,.13,0x4c5b4f,g,'moss');\r\n            for(let row=0;row<3;row++)for(const tooth of [-1,1])box(side*6.2+tooth*.65,1.4+row*.4,-5.85,.12,.15,.2,0x4f8866,g,'moss');\r\n            box(side*6.2,1.8,-5.8,1.05,.15,.15,0x4c5b4f,g,'moss');\r\n          }\r\n          branch([side*7,-2,3],[side*8,7,-3],.6,0x725242,g);\r\n          for(let q=0;q<4;q++){\r\n            const end=[side*(5.2+q*.8),7.3+Math.sin(q+s)*1.1,-5+q*2];branch([side*8,6,-3],end,.14,0x725242,g);\r\n            for(let leafIndex=0;leafIndex<3;leafIndex++){\r\n              const leaf=sphere(end[0]+side*(leafIndex-1)*.46,end[1]+(leafIndex%2)*.42,end[2]+(leafIndex-1)*.35,1.15,leafIndex%2?0x4e8a6a:0x79a57b,g);\r\n              leaf.scale.set(.9,.58,.72);leaf.rotation.z=side*(leafIndex-1)*.22;\r\n            }\r\n            if((q+s)%2===0){const vineX=end[0]-side*.32;branch([vineX,end[1]-.1,end[2]],[vineX-side*.18,2.4,end[2]+.25],.055,0x41643d,g,'moss');}\r\n          }\r\n          for(let q=0;q<3;q++)branch([side*6.1,0,-6+q*3],[side*(6.6+q*.2),4+q,-7+q*3],.11,0x4f8866,g,'moss');\r\n          for(let q=0;q<6;q++){const fern=mesh(new T.ConeGeometry(.45,1.3,5),0x4e8a6a,side*(6.1+q*.25),.6,-7+q*2.6,g,'moss');fern.rotation.z=side*(.2+q*.12);}\r\n          const torch=cylinder(side*4.15,1.4,-4,.12,1.25,0x725242,g,'wood');\r\n          branch([side*4.15,1.85,-4],[side*4.15,2.35,-4],.08,0xd6c893,g,'gold');\r\n          const flame=mesh(new T.ConeGeometry(.22,.62,6),0xff9a45,side*4.15,2.56,-4,g,'gold');flame.userData.dynamic=true;const fire=s===0?new T.PointLight(0xff9a45,1.1,5,2):null;if(fire){fire.position.set(side*4.15,2.7,-4);g.add(fire);}flame.material=flameInk;torchFlames.push({mesh:flame,light:fire,phase:s+side});\r\n          // Hanging rope rails follow the open ravine edge.\r\n          branch([side*4.6,.6,-8],[side*4.6,.3,0],.05,0x786648,g);branch([side*4.6,.3,0],[side*4.6,.6,8],.05,0x786648,g);\r\n        }\r\n        templeLandmarks({T,mesh,box,branch,group:g,segment:s,leg:routeLeg});\r\n      }else{\r\n        box(0,-.18,0,10,.3,18,0x908894,g,'ballast');\r\n        for(const lane of [-2.8,0,2.8])for(const side of [-1,1])box(lane+side*.8,.03,0,.1,.18,18,0xc6bfbe,g,'metal');\r\n        for(let j=0;j<10;j++)box(0,-.08,-8+j*1.8,9.3,.14,.3,0x725242,g,'wood');\r\n        // One raised platform, open train storage on the opposite side, city skyline beyond.\r\n        box(-6,.35,0,2.5,.7,18,0xd5c2b1,g,'stone');box(-4.9,.74,0,.32,.06,18,p.trim,g,'gold');\r\n        const canopy=mesh(new T.CylinderGeometry(2.5,2.5,15,16,1,true,Math.PI/2,Math.PI),0x8fa9b7,-6.5,4.5,0,g,'metal');canopy.rotation.x=Math.PI/2;\r\n        for(const z of [-6,6]){cylinder(-6.3,2.3,z,.12,4.6,0x5a506c,g,'metal');box(-6.3,3.8,z,1.5,.5,.15,s%2?0xe8b77e:0x86ceca,g,'metal');}\r\n        // Playable lanes are reserved for authored, collidable route geometry.\r\n        for(const side of [-1,1]){\r\n          const h=7+(s*7+(side+1)*3)%13,x=side*(11+(s%2)*3);\r\n          box(x,h/2-1,-3,4,h,12,s%3===0?0xf19586:s%3===1?0xe9bd58:0x61bac7,g,'plaster');\r\n          for(let q=0;q<3;q++)for(let row=0;row<3;row++)box(x-side*2.03,2+row*2.3,-7+q*3.5,.06,1.4,1.6,0x688097,g,'metal');\r\n          for(let q=0;q<3;q++){box(x-side*2.1,1.05,-7+q*4,.4,.3,2.2,q%2?0xb8809c:0x75adad,g,'metal');box(x-side*2.13,3.7,-7+q*4,.12,.15,1.8,0xe7d5b3,g,'stone');}\r\n          cylinder(x,h+.3,-3,.45,1,0x8b8799,g,'metal');box(x+.7,h+.3,-5,1,.55,1.2,0x8b8799,g,'metal');\r\n          box(x,h-.6,-3,4.4,.25,12.4,s%2?0x25384f:0x30445a,g,'stone');\r\n        }\r\n        if(s%3===0){branch([-5,0,-6],[-5,7,-6],.14,0x62526e,g,'metal');branch([5,0,-6],[5,7,-6],.14,0x62526e,g,'metal');box(0,7,-6,10,.2,.2,0x62526e,g,'metal');}\r\n        metroLandmarks({T,mesh,box,cylinder,branch,group:g,segment:s,leg:routeLeg});\r\n      }\r\n    }\r\n    if(index===2)for(const b of courseBuses(index,routeLeg)){const g=new T.Group();root.add(g);g.userData.route=b;routeVehicles.push(g);g.position.set(b.lane*2.8,0,-(b.rampEnd+b.roofEnd)/2);buildBus(b,g);}\r\n    for(const item of courseCoins(index,routeLeg,round)){\r\n      const c=mesh(new T.CylinderGeometry(.22,.22,.075,16),0xffd269,item.lane*2.8,item.height,-item.distance);c.rotation.x=Math.PI/2;c.userData=item;coins.push(c);\r\n      const stamp=mesh(new T.TorusGeometry(.13,.025,4,12),0xf6e5aa,0,.045,0,c,'gold');stamp.rotation.x=Math.PI/2;\r\n    }\r\n    if(coins.length){\r\n      coinInstances=new T.InstancedMesh(coins[0].geometry,coins[0].material,coins.length);coinStamps=new T.InstancedMesh(coins[0].children[0].geometry,coins[0].children[0].material,coins.length);coinInstances.frustumCulled=coinStamps.frustumCulled=false;root.add(coinInstances,coinStamps);for(const c of coins)c.removeFromParent();\r\n    }\r\n    for(const [i,item] of courseHazards(index,round,routeLeg).entries()){\r\n      const g=new T.Group();root.add(g);g.position.set(item.lane*2.8,item.elevation||0,-item.distance);g.userData=item;const actorStart=hazardActors.length;\r\n      if(index===0){\r\n        if(item.type==='slide'){const pivot=new T.Group();pivot.position.y=1.35;g.add(pivot);const rotor=mesh(new T.CylinderGeometry(1.08,1.08,.2,12),0xb7a1d6,0,0,0,pivot,'metal');rotor.rotation.x=Math.PI/2;\r\n          for(let q=0;q<12;q++){const a=q*Math.PI/6,tooth=mesh(new T.ConeGeometry(.18,.38,4),q%2?0xd4c9e8:0x756c92,Math.cos(a)*1.07,Math.sin(a)*1.07,0,pivot,'metal');tooth.rotation.z=-a;}\r\n          hazardActors.push({kind:'saw',mesh:pivot,phase:item.distance*.07});branch([-1.25,.15,0],[-1.25,1.35,0],.08,0x80668e,g,'metal');}\r\n        else{const laserMat=new T.MeshBasicMaterial({color:0xff55ed,transparent:true,opacity:.95});extras.push(laserMat);const laser=new T.Mesh(new T.BoxGeometry(2.5,.09,.12),laserMat);laser.position.set(0,item.height,0);g.add(laser);hazardActors.push({kind:'laser',mesh:laser,phase:item.distance*.09,hazard:item,world:index,gate:true});for(const x of [-1.25,1.25])box(x,.45,0,.12,.9,.2,0x9a87ce,g,'gold');}\r\n      }else if(index===1){\r\n        if(item.type==='slide'){const pivot=new T.Group();pivot.position.set(0,2.5,0);g.add(pivot);branch([0,0,0],[0,-1.55,0],.17,0x725242,pivot);const axe=mesh(new T.BoxGeometry(1.15,.7,.2),0x9f9a84,.35,-1.5,0,pivot,'stone');axe.rotation.z=-.35;const edge=mesh(new T.ConeGeometry(.42,.7,4),0xd6c893,.82,-1.53,0,pivot,'gold');edge.rotation.z=Math.PI/2;hazardActors.push({kind:'axe',mesh:pivot,phase:item.distance*.035});}\r\n        else{const gator=new T.Group();g.add(gator);const body=mesh(new T.DodecahedronGeometry(.82),0x4e8a6a,0,.34,0,gator,'moss');body.scale.set(1.45,.65,1.05);sphere(-.62,.72,-.1,.17,0xe9d69b,gator);sphere(.62,.72,-.1,.17,0xe9d69b,gator);for(const x of [-.62,.62])sphere(x,.75,-.22,.07,0x29273b,gator);const jaw=new T.Group();jaw.position.set(0,.45,.55);gator.add(jaw);const mouth=mesh(new T.BoxGeometry(1.9,.16,.85),0x486d52,0,-.1,.32,jaw,'moss');for(const x of [-.68,-.32,0,.32,.68]){const tooth=mesh(new T.ConeGeometry(.08,.2,4),0xf1e2bd,x,-.03,.34,jaw,'stone');tooth.rotation.x=Math.PI;}hazardActors.push({kind:'gator',mesh:jaw,phase:item.distance*.04});}\r\n      }else{\r\n        if(item.type==='slide'){const laserMat=new T.MeshBasicMaterial({color:0x57f3e4,transparent:true,opacity:.84});extras.push(laserMat);const laser=new T.Mesh(new T.BoxGeometry(2.5,.12,.16),laserMat);laser.position.y=1.3;g.add(laser);hazardActors.push({kind:'laser',mesh:laser,phase:item.distance*.09});branch([-1.25,0,0],[-1.25,1.5,0],.08,0x62526e,g,'metal');}\r\n        else{box(0,.425,0,2.5,.85,.3,0xd48288,g,'metal');for(let q=0;q<4;q++){const stripe=box(-.9+q*.6,.425,.18,.18,.75,.03,0xffdf9d,g,'gold');stripe.rotation.z=-.4;}}\r\n      }\r\n      if(hazardActors.length===actorStart)batchStatic(g);obstacles.push(g);\r\n    }\r\n    const marked=new Set();\r\n    for(const h of courseHazards(index,round,routeLeg)){if(marked.has(h.distance))continue;marked.add(h.distance);const guide=new T.Group();guide.position.set(h.safeLane*2.8,.045,-(h.distance-28));root.add(guide);box(0,0,0,1.35,.07,.16,0x8fffd4,guide,'gold');for(const side of [-1,1]){const arrow=box(side*.22,.025,-.2,.07,.05,.42,0x8fffd4,guide,'gold');arrow.rotation.y=side*.6;}routeMarkers.push({group:guide,distance:h.distance-28});}\r\n\r\n    for(const item of coursePickups(index,routeLeg)){\r\n      const m=new T.Group();root.add(m);m.position.set(item.lane*2.8,item.height,-item.distance);m.userData=item;pickups.push(m);\r\n      powerModel(T,item.kind,{mesh,box,cylinder,sphere},m);\r\n      const halo=mesh(new T.TorusGeometry(.62,.035,5,24),0xfff1b8,0,0,-.1,m,'gold');halo.rotation.x=.2;\r\n      // The modeled pickup is its own label; only quantity multipliers need a badge.\r\n      const multiplier=({doubleCoins:'×2',coinTier:'×3',scoreBoost:'×2'})[item.kind];\r\n      if(multiplier){const art=document.createElement('canvas');art.width=128;art.height=64;const ctx=art.getContext('2d');ctx.fillStyle='#30213ee8';ctx.fillRect(0,0,128,64);ctx.fillStyle='#fff0c2';ctx.font='bold 38px sans-serif';ctx.textAlign='center';ctx.fillText(multiplier,64,46);\r\n       const texture=new T.CanvasTexture(art);texture.colorSpace=T.SRGBColorSpace;const material=new T.SpriteMaterial({map:texture,depthTest:false});extras.push(texture,material);const badge=new T.Sprite(material);badge.scale.set(.85,.42,1);badge.position.y=.9;m.add(badge);}\r\n      batchStatic(m);\r\n    }\r\n    for(const g of segments){g.scale.x=routeLeg===1?-1:1;batchStatic(g);}for(const g of routeVehicles)batchStatic(g);\r\n    powerRig=new T.Group();root.add(powerRig);\r\n    for(const id of ['shield','board','superJump','magnet','boost','phase','jetpack','recovery']){const group=new T.Group();powerRig.add(group);vfx[id]=group;}\r\n    for(const id of ['shield','phase','recovery']){const r=mesh(new T.TorusGeometry(.95,.025,5,40),id==='phase'?0xc39afa:0xffd269,0,0,-1.2,vfx[id],'gold');r.scale.y=.8;}\r\n    box(0,-.8,-1.4,1.1,.08,.45,0x84ead9,vfx.board,'metal');\r\n    for(const side of [-1,1]){\r\n      mesh(new T.TorusGeometry(.4,.04,5,20,Math.PI),0x84ead9,side*.8,-.15,-1.4,vfx.magnet,'metal');\r\n      box(side*.34,-.7,-1.2,.23,.1,.35,0xffd269,vfx.superJump,'gold');\r\n      mesh(new T.ConeGeometry(.09,.7,8),0xffc773,side*.65,-.6,-1.1,vfx.jetpack,'gold').rotation.x=.3;\r\n      for(let q=0;q<3;q++)box(side*(.7+q*.2),.4-q*.4,-1.2,.015,.02,1,0xc39afa,vfx.boost,'metal');\r\n    }\r\n    const pickupHalo=mesh(new T.TorusGeometry(.22,.012,6,28),0xffe3a1,0,-.35,-1.7,powerRig,'gold');vfx.pickup=pickupHalo;\r\n    const jumpPop=mesh(new T.TorusGeometry(.4,.04,5,20),0xffd269,0,-.85,-1.35,powerRig,'gold');vfx.jump=jumpPop;\r\n    const protect=mesh(new T.TorusGeometry(.3,.018,5,24),0x84ead9,0,-.15,-1.7,powerRig,'gold');vfx.protect=protect;\r\n    for(const [id,fx] of Object.entries(vfx))fx.name=`runner-power-${id}`;\r\n    portal=new T.Group();root.add(portal);portal.position.set(0,2.6,-50);\r\n    const ring=mesh(new T.TorusGeometry(2.7,.3,10,60),0xb17aee,0,0,0,portal);\r\n    mesh(new T.TorusGeometry(3.2,.08,5,60),0xf1c4ff,0,0,0,portal);\r\n    const disk=mesh(new T.CircleGeometry(2.6,60),0x6a3d99,0,0,-.1,portal);disk.material=new T.MeshBasicMaterial({color:0x8651d5,transparent:true,opacity:.78,side:T.DoubleSide});extras.push(disk.material);\r\n    for(let i=0;i<12;i++){const a=i/12*Math.PI*2;mesh(new T.OctahedronGeometry(.13),0xffe6b6,Math.cos(a)*3.2,Math.sin(a)*3.2,0,portal);}\r\n    coach=new T.Group();root.add(coach);coach.visible=false;\r\n    // Foundry Armie is exclusively a projected shadow, never a visible hand model.\r\n    buildPursuer(index);\r\n    exitTunnel=new T.Group();exitTunnel.name='exit-tunnel';root.add(exitTunnel);exitTunnel.position.set(0,2.7,10);box(0,-2.82,7,8.6,.2,24,index===0?0xdac7a1:p.ground,exitTunnel,index===0?'stone':surface);\r\n    for(let q=0;q<5;q++){const r=mesh(new T.TorusGeometry(3-q*.17,.075,5,32),q%2?0xf1c4ff:0xb17aee,0,0,q*2.2,exitTunnel,'gold');r.rotation.z=q*.3;}\r\n    burst=new T.Group();burst.name='course-event-burst';root.add(burst);burst.visible=false;\r\n    const shardGeo=new T.OctahedronGeometry(.16),shardMat=new T.MeshBasicMaterial({color:0xffcf82});geos.push(shardGeo);extras.push(shardMat);\r\n    const shards=new T.InstancedMesh(shardGeo,shardMat,24);shards.frustumCulled=false;burst.add(shards);burst.userData.shards=shards;\r\n    const burstRing=mesh(new T.TorusGeometry(.65,.055,5,24),0x8fffd4,0,.7,0,burst,'gold');burst.userData.ring=burstRing;\r\n    burst.userData.labels={};\r\n    for(const key of ['hit','punch','dodge']){\r\n      const art=document.createElement('canvas');art.width=art.height=128;const c=art.getContext('2d');c.strokeStyle=key==='dodge'?'#a2ffdb':'#ffdd8c';c.lineWidth=9;c.lineCap='round';c.lineJoin='round';c.shadowColor='#271d3c';c.shadowBlur=5;c.beginPath();\r\n      if(key==='dodge'){c.moveTo(26,63);c.lineTo(52,88);c.lineTo(103,32);}\r\n      else if(key==='hit'){c.moveTo(54,15);c.lineTo(83,48);c.lineTo(46,68);c.lineTo(77,111);}\r\n      else{for(let i=0;i<16;i++){const a=i*Math.PI/8,r=i%2?27:49,x=64+Math.cos(a)*r,y=64+Math.sin(a)*r;if(i)c.lineTo(x,y);else c.moveTo(x,y);}c.closePath();}c.stroke();\r\n      const texture=new T.CanvasTexture(art);texture.colorSpace=T.SRGBColorSpace;const material=new T.SpriteMaterial({map:texture,transparent:true,depthTest:false});extras.push(texture,material);const badge=new T.Sprite(material);badge.scale.set(1.2,1.2,1);badge.position.y=2.1;burst.add(badge);burst.userData.labels[key]=badge;\r\n    }\r\n    const absorbed={};\r\n    absorbed.shield=new T.Group();for(let i=0;i<3;i++){const arc=mesh(new T.TorusGeometry(.45,.045,5,16,Math.PI*.46),0xc39afa,0,0,0,absorbed.shield,'gold');arc.rotation.z=i*Math.PI*.67;}burst.add(absorbed.shield);\r\n    absorbed.board=new T.Group();const plate=box(0,0,0,.9,.42,.08,0x84ead9,absorbed.board,'metal');plate.rotation.z=.18;const crack=box(0,0,.06,.08,.58,.04,0xffd269,absorbed.board,'gold');crack.rotation.z=-.55;burst.add(absorbed.board);\r\n    absorbed.recovery=new T.Group();for(let i=0;i<3;i++){const feather=mesh(new T.ConeGeometry(.11,.48,5),0xffd269,(i-1)*.23,0,0,absorbed.recovery,'gold');feather.rotation.z=(i-1)*.45;}burst.add(absorbed.recovery);burst.userData.absorbed=absorbed;\r\n\r\n  }\r\n  let coachMode=false;\r\n  function render(run,time,active,{snapCamera=false}={}){\n    if(previousRun!==run||(run.time||0)<lastRunTime){previousRun=run;eventSerial=0;arrivalSerial=run.arrivalSerial||0;arrivalAt=-100;burstAt=-100;burstEvent=null;pickupSerial=run.pickupSerial||0;pickupAt=-100;pickupKind=null;activePowers={};if(vfx.protect)vfx.protect.userData.at=-100;for(const c of coins){delete c.userData.attractedAt;delete c.userData.attractedFrom;}}lastRunTime=run.time||0;\r\n    if(world!==run.world||loop!==(run.loop||0)||leg!==(run.leg||0))build(run.world,run.loop||0,run.leg||0);const d=run.distance;\r\n    for(let i=0;i<segments.length;i++){segments[i].position.z=-i*18+(d%18);segments[i].visible=segments[i].position.z>-145;}\r\n    const quiet=matchMedia('(prefers-reduced-motion:reduce)').matches;\r\n    if((run.pickupSerial||0)!==pickupSerial){pickupSerial=run.pickupSerial||0;pickupAt=run.time||time;pickupKind=run.lastPickup;}\r\n    for(const a of hazardActors){if(a.kind==='saw')a.mesh.rotation.z=quiet?0:time*5+a.phase;else if(a.kind==='axe')a.mesh.rotation.z=quiet?0:Math.sin(time*2.7+a.phase)*.52;else if(a.kind==='gator')a.mesh.rotation.x=quiet?.22:.18+Math.max(0,Math.sin(time*3+a.phase))*.42;else if(a.kind==='laser'){if(a.gate){const state=laserState(a.world,a.hazard,run.time??time);a.mesh.visible=state!=='off';a.mesh.material.color.setHex(state==='active'?0xff55ed:0xffbf63);a.mesh.material.opacity=state==='active'?1:.5;}else a.mesh.material.opacity=quiet?.72:.52+.32*(.5+.5*Math.sin(time*7+a.phase));}}\r\n    for(const f of torchFlames){if(!quiet){const pulse=.78+.22*Math.sin(time*12+f.phase);f.mesh.scale.set(1,pulse,1);if(f.light)f.light.intensity=.8+Math.abs(Math.sin(time*17+f.phase))*.7;}}\r\n    for(const a of spaceAsteroids)if(!quiet)a.mesh.rotation.y=Math.sin(time*.55+a.phase)*.22;\r\n    for(const marker of routeMarkers){marker.group.position.z=d-marker.distance;marker.group.visible=marker.group.position.z<4&&marker.group.position.z>-145;}\r\n    const flight=run.powers?.jetpack>0;\r\n    camera.position.lerp(new T.Vector3(coachMode?0:run.lane*2.8,coachMode?2.7:1.8+(run.elevation||0)+(flight?3:jumpHeight(run))-(run.slide>0?.85:0),coachMode?6:3),snapCamera?1:.18);\n    camera.lookAt(coachMode?0:camera.position.x*.7,coachMode?1.5:camera.position.y-.08,coachMode?-3:-40);\r\n    if(!quiet&&active&&!coachMode)camera.rotation.z=Math.sin(time*9)*.008;\r\n    coach.visible=false;\r\n    if((run.arrivalSerial||0)!==arrivalSerial){arrivalSerial=run.arrivalSerial||0;arrivalAt=time;}\r\n    const arrivalAge=time-arrivalAt,lookback=active&&!coachMode&&arrivalAge<2.1;\r\n    // A short view back through the exit tunnel, then a gentle forward recovery. Controls stay live.\r\n    if(lookback&&!quiet){const back=arrivalAge<1.25?1:Math.max(0,1-(arrivalAge-1.25)/.85);camera.rotation.set(0,-Math.PI*back,0);}\r\n    exitTunnel.visible=lookback;exitTunnel.position.z=8+d*.2;\r\n    // The roster alien stays in view during forward play and closes the visual\r\n    // gap as hits build pressure. The look-back beat places it behind the exit ring.\r\n    pursuer.visible=!coachMode;pursuer.position.set(lookback?0:3.2+Math.sin(time*1.2)*1.1,1.05+(quiet?0:Math.abs(Math.sin(time*4))*.12),lookback?exitTunnel.position.z+5:-50+Math.min(1,run.pressure||0)*24);\r\n    pursuer.rotation.z=quiet?0:Math.sin(time*1.7)*.09;\r\n    const events=(run.events||[run.lastEvent]).filter(Boolean);\r\n    for(const event of events){if((event.serial||run.eventSerial||0)<=eventSerial)continue;eventSerial=event.serial||run.eventSerial; if(event.world!==world||(event.leg||0)!==leg)continue;burstEvent=event;burstAt=run.time||time;if(event.type==='hit'&&event.result==='absorbed'){vfx.protect.userData.protection=event.protection;vfx.protect.userData.at=run.time||time;}}\r\n    const protectAge=(run.time||time)-(vfx.protect.userData.at??-100);vfx.protect.visible=protectAge<.48;vfx.protect.scale.setScalar(quiet?1:Math.max(.45,1+protectAge*2));\r\n    const age=time-burstAt;burst.visible=!coachMode&&!!burstEvent&&age<.7;\r\n    if(burst.visible){const dodge=burstEvent.type==='dodge',absorbed=burstEvent.type==='hit'&&burstEvent.result==='absorbed';burst.position.set((burstEvent.lane||0)*2.8,burstEvent.elevation||0,Math.min(-2,d-burstEvent.distance-3));\r\n      for(const [id,mark] of Object.entries(burst.userData.absorbed)){mark.visible=absorbed&&burstEvent.protection===id;if(mark.visible&&!quiet)mark.scale.setScalar(1+age*.45);else mark.scale.setScalar(1);}\r\n      for(const [key,badge] of Object.entries(burst.userData.labels)){badge.visible=key===burstEvent.type;badge.material.opacity=Math.max(0,1-age/.7);badge.position.y=quiet?2:2+age;}\r\n      const shards=burst.userData.shards;shards.visible=!dodge&&!quiet;shards.material.color.setHex(burstEvent.type==='punch'?0xffd269:0xf3a18d);\r\n      for(let i=0;i<24;i++){const a=i*2.39996,v=2+(i%5)*.7;coinTransform.position.set(Math.cos(a)*age*v,.8+Math.sin(i*1.7)*age*v+age*2-age*age*4,Math.sin(a)*age*v);coinTransform.rotation.set(age*i,age*(i%3),a);coinTransform.scale.setScalar(Math.max(0,1-age/.7));coinTransform.updateMatrix();shards.setMatrixAt(i,coinTransform.matrix);}shards.instanceMatrix.needsUpdate=true;\r\n      const r=burst.userData.ring;r.visible=dodge||burstEvent.result==='absorbed';r.material.color.setHex(dodge?(burstEvent.protection==='phase'?0xc39afa:0x8fffd4):burstEvent.protection==='shield'?0xc39afa:burstEvent.protection==='board'?0x84ead9:0xffd269);r.scale.setScalar(quiet?1:1+age*2.5);\r\n    }\r\n    portal.visible=run.distance>470&&!coachMode;portal.position.z=-Math.max(3,600-d);portal.rotation.z=quiet?0:time*.2;\r\n    portal.children[0].scale.setScalar(quiet?1:1+Math.sin(time*3)*.025);\r\n    for(const g of routeVehicles)g.position.z=d-(g.userData.route.rampEnd+g.userData.route.roofEnd)/2;\r\n    powerRig.visible=!coachMode;powerRig.position.copy(camera.position);powerRig.quaternion.copy(camera.quaternion);\r\n    for(const [id,fx] of Object.entries(vfx)){\r\n      if(['pickup','jump','protect'].includes(id))continue;\r\n      const remaining=run.powers?.[id]||0;\r\n      if((activePowers[id]||0)>0&&remaining<=0)activePowers[`${id}End`]=run.time||time;\r\n      activePowers[id]=remaining;\r\n      const ending=remaining<=0&&(run.time||time)-(activePowers[`${id}End`]??-100)<.35;\r\n      fx.visible=remaining>0||ending;fx.scale.setScalar(ending?Math.max(.2,1-((run.time||time)-activePowers[`${id}End`])/.35):1);\r\n      fx.rotation.z=!quiet&&remaining>0?Math.sin(time*2)*.03:0;\r\n    }\r\n    if(vfx.jetpack){const taper=Math.max(.18,Math.min(1,(run.powers?.jetpack||0)/2));for(const flame of vfx.jetpack.children)if(flame.geometry?.type==='ConeGeometry')flame.scale.set(1,taper,1);}\r\n    const pickupAge=(run.time||time)-pickupAt;vfx.pickup.visible=!!pickupKind&&pickupAge<.55;vfx.pickup.scale.setScalar(quiet?1:Math.max(.4,1+pickupAge*1.1));\r\n    const jumping=run.jump>0,wasJumping=activePowers._jump||false;if(jumping&&!wasJumping)activePowers._jumpAt=run.time||time;activePowers._jump=jumping;\r\n    const jumpAge=(run.time||time)-(activePowers._jumpAt??-100);vfx.jump.visible=jumpAge<.32;vfx.jump.scale.setScalar(quiet?1:Math.max(.3,1+jumpAge*2));\r\n    for(let i=0;i<coins.length;i++){\r\n      const c=coins[i],z=d-c.userData.distance,collected=!!run.collected[c.userData.id];\r\n      if(collected&&c.userData.attractedAt==null){c.userData.attractedAt=run.time||time;c.userData.attractedFrom={x:c.userData.lane*2.8,y:c.userData.height,z};}\r\n      const trailAge=c.userData.attractedAt==null?1:(run.time||time)-c.userData.attractedAt,attract=collected&&run.powers?.magnet>0&&trailAge<.42,visible=!coachMode&&(!collected||attract)&&z<4&&z>-160;\r\n      if(attract){const k=quiet?0.65:Math.min(1,trailAge/.42),from=c.userData.attractedFrom;coinTransform.position.set(from.x*(1-k)+camera.position.x*k,from.y*(1-k)+camera.position.y*k-1.1*k,from.z*(1-k)-1.5*k);}\r\n      else coinTransform.position.set(c.userData.lane*2.8,c.userData.height,z);\r\n      coinTransform.rotation.set(Math.PI/2,quiet?0:time*2,0);coinTransform.scale.setScalar(visible?1:0);coinTransform.updateMatrix();coinInstances.setMatrixAt(i,coinTransform.matrix);\r\n      coinTransform.rotation.set(Math.PI,quiet?0:time*2,0);coinTransform.position.z+=.045;coinTransform.updateMatrix();coinStamps.setMatrixAt(i,coinTransform.matrix);\r\n    }\r\n    if(coinInstances){coinInstances.instanceMatrix.needsUpdate=true;coinStamps.instanceMatrix.needsUpdate=true;}\r\n    for(const o of obstacles){o.position.z=d-o.userData.distance;o.visible=!coachMode&&!run.destroyed?.[o.userData.id]&&o.position.z<4&&o.position.z>-160;}\r\n    for(const p of pickups){p.position.z=d-p.userData.distance;p.rotation.y=quiet?0:Math.sin(time)*.3;p.position.y=p.userData.height+(quiet?0:Math.sin(time*2)*.12);p.visible=!coachMode&&!run.collected[p.userData.id]&&p.position.z<4&&p.position.z>-160;}\r\n    const restoreCamera=beforeRender(scene,camera);try{pipeline.render();}finally{if(typeof restoreCamera==='function')restoreCamera();}\n  }\r\n  function resize(){const {width,height}=canvas.parentElement.getBoundingClientRect();camera.aspect=width/height;camera.updateProjectionMatrix();pipeline.setSize(width,height);}\r\n  const observer=new ResizeObserver(resize);observer.observe(canvas.parentElement);resize();\r\n  return {scene,camera,stats:()=>({...pipeline.sceneStats}),render,setCoach(value){coachMode=value;},dispose(){observer.disconnect();disposeWorld();for(const m of mats.values())m.dispose();for(const t of textures.values())t.dispose();pipeline.dispose();renderer.dispose();renderer.forceContextLoss();}};\r\n}\r\n\r\n\r\n","arcade/runner/worlds/textures.mjs":"// Procedural surface textures for cardio-worlds MYR5\r\n// Seeded RNG + canvas procedural, no external assets\r\n\r\nclass SeededRNG {\r\n  constructor(seed) {\r\n    this.seed = seed;\r\n  }\r\n  next() {\r\n    this.seed = (this.seed * 9301 + 49297) % 233280;\r\n    return this.seed / 233280;\r\n  }\r\n}\r\n\r\nfunction createCanvas(size) {\r\n  const canvas = typeof document !== 'undefined'\r\n    ? document.createElement('canvas')\r\n    : { width: size, height: size, getContext: () => null };\r\n  canvas.width = size;\r\n  canvas.height = size;\r\n  return canvas;\r\n}\r\n\r\nfunction drawMetal(ctx, size, rng) {\r\n  const base = 200;\r\n  ctx.fillStyle = `rgb(${base}, ${base}, ${base})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  ctx.strokeStyle = `rgba(150, 150, 150, 0.4)`;\r\n  ctx.lineWidth = 1;\r\n  for (let i = 0; i < 60; i++) {\r\n    const y = rng.next() * size;\r\n    const x1 = 0;\r\n    const x2 = size * (0.3 + rng.next() * 0.4);\r\n    ctx.beginPath();\r\n    ctx.moveTo(x1, y);\r\n    ctx.lineTo(x2, y + (rng.next() - 0.5) * 2);\r\n    ctx.stroke();\r\n  }\r\n\r\n  ctx.fillStyle = `rgba(100, 100, 100, 0.6)`;\r\n  [[0, 0], [size, 0], [0, size], [size, size]].forEach(([x, y]) => {\r\n    ctx.beginPath();\r\n    ctx.arc(x, y, 8, 0, Math.PI * 2);\r\n    ctx.fill();\r\n    ctx.fillStyle = `rgba(180, 180, 180, 0.5)`;\r\n    ctx.beginPath();\r\n    ctx.arc(x + 2, y + 2, 3, 0, Math.PI * 2);\r\n    ctx.fill();\r\n    ctx.fillStyle = `rgba(100, 100, 100, 0.6)`;\r\n  });\r\n\r\n  for (let i = 0; i < 4; i++) {\r\n    const x = (i + 1) * (size / 5);\r\n    ctx.strokeStyle = `rgba(120, 120, 120, 0.5)`;\r\n    ctx.lineWidth = 2;\r\n    ctx.beginPath();\r\n    ctx.moveTo(x, 0);\r\n    ctx.lineTo(x, size);\r\n    ctx.stroke();\r\n  }\r\n}\r\n\r\nfunction drawGold(ctx, size, rng) {\r\n  const base = 210;\r\n  ctx.fillStyle = `rgb(${base}, ${base - 10}, ${base - 30})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let i = 0; i < 8; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const w = 20 + rng.next() * 30;\r\n    const h = 15 + rng.next() * 20;\r\n    ctx.strokeStyle = `rgba(180, 170, 140, 0.5)`;\r\n    ctx.lineWidth = 1.5;\r\n    ctx.strokeRect(x, y, w, h);\r\n    const detail = 3 + Math.floor(rng.next() * 3);\r\n    for (let j = 0; j < detail; j++) {\r\n      ctx.strokeStyle = `rgba(200, 190, 160, 0.3)`;\r\n      ctx.lineWidth = 0.5;\r\n      ctx.beginPath();\r\n      ctx.moveTo(x, y + j * (h / detail));\r\n      ctx.lineTo(x + w, y + j * (h / detail));\r\n      ctx.stroke();\r\n    }\r\n  }\r\n\r\n  ctx.strokeStyle = `rgba(190, 180, 150, 0.4)`;\r\n  ctx.lineWidth = 1;\r\n  for (let i = 0; i < 40; i++) {\r\n    const x1 = rng.next() * size;\r\n    const y1 = rng.next() * size;\r\n    const len = 10 + rng.next() * 20;\r\n    const angle = rng.next() * Math.PI * 2;\r\n    ctx.beginPath();\r\n    ctx.moveTo(x1, y1);\r\n    ctx.lineTo(x1 + Math.cos(angle) * len, y1 + Math.sin(angle) * len);\r\n    ctx.stroke();\r\n  }\r\n}\r\n\r\nfunction drawStone(ctx, size, rng) {\r\n  const base = 190;\r\n  ctx.fillStyle = `rgb(${base}, ${base}, ${base})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  // Broad broken causeway blocks read as cracked paving at phone scale,\r\n  // instead of a uniform interior brick wall.\r\n  const tileW = 64, tileH = 48;\r\n  for (let row = 0; row < size / tileH; row++) {\r\n    const offset = row % 2 ? tileW * .48 : 0;\r\n    for (let col = -1; col < size / tileW + 1; col++) {\r\n      const x = col * tileW + offset, y = row * tileH;\r\n      const inset = 2 + rng.next() * 3;\r\n      ctx.beginPath();\r\n      ctx.moveTo(x + inset, y + inset);\r\n      ctx.lineTo(x + tileW - inset - rng.next() * 5, y + inset + rng.next() * 3);\r\n      ctx.lineTo(x + tileW - inset, y + tileH - inset);\r\n      ctx.lineTo(x + inset + rng.next() * 5, y + tileH - inset - rng.next() * 3);\r\n      ctx.closePath();\r\n      ctx.fillStyle = `rgba(226, 214, 184, ${.12 + rng.next() * .12})`;\r\n      ctx.fill();\r\n      ctx.strokeStyle = `rgba(92, 84, 70, ${.55 + rng.next() * .18})`;\r\n      ctx.lineWidth = 2.2;\r\n      ctx.stroke();\r\n      if (rng.next() > .45) {\r\n        const cx = x + 8 + rng.next() * (tileW - 16), cy = y + tileH - 3;\r\n        ctx.strokeStyle = `rgba(74, 98, 62, ${.42 + rng.next() * .2})`;\r\n        ctx.lineWidth = 3 + rng.next() * 2;\r\n        ctx.beginPath();ctx.moveTo(cx,cy);ctx.lineTo(cx+8+rng.next()*12,cy-2-rng.next()*5);ctx.stroke();\r\n      }\r\n    }\r\n  }\r\n\r\n  for (let i = 0; i < 100; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    ctx.fillStyle = `rgba(150, 150, 150, ${0.2 + rng.next() * 0.3})`;\r\n    ctx.beginPath();\r\n    ctx.arc(x, y, 1 + rng.next() * 2, 0, Math.PI * 2);\r\n    ctx.fill();\r\n  }\r\n\r\n  for (let i = 0; i < 15; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const w = 2 + rng.next() * 8;\r\n    const h = 0.5 + rng.next() * 1.5;\r\n    ctx.strokeStyle = `rgba(130, 130, 130, 0.5)`;\r\n    ctx.lineWidth = h;\r\n    ctx.beginPath();\r\n    ctx.moveTo(x, y);\r\n    ctx.lineTo(x + w, y + (rng.next() - 0.5) * 3);\r\n    ctx.stroke();\r\n  }\r\n}\r\n\r\nfunction drawMoss(ctx, size, rng) {\r\n  const base = 195;\r\n  ctx.fillStyle = `rgb(${base - 20}, ${base - 10}, ${base - 30})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let i = 0; i < 150; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const radius = 2 + rng.next() * 6;\r\n    const greenMod = Math.floor(rng.next() * 40);\r\n    ctx.fillStyle = `rgba(${150 + greenMod}, ${160 + greenMod}, ${140 + greenMod}, ${0.3 + rng.next() * 0.4})`;\r\n    ctx.beginPath();\r\n    ctx.arc(x, y, radius, 0, Math.PI * 2);\r\n    ctx.fill();\r\n  }\r\n\r\n  ctx.strokeStyle = `rgba(140, 150, 130, 0.4)`;\r\n  ctx.lineWidth = 1;\r\n  for (let i = 0; i < 80; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const len = 3 + rng.next() * 8;\r\n    const angle = rng.next() * Math.PI * 2;\r\n    ctx.beginPath();\r\n    ctx.moveTo(x, y);\r\n    ctx.lineTo(x + Math.cos(angle) * len, y + Math.sin(angle) * len);\r\n    ctx.stroke();\r\n  }\r\n}\r\n\r\nfunction drawWood(ctx, size, rng) {\r\n  const base = 195;\r\n  ctx.fillStyle = `rgb(${base - 5}, ${base - 15}, ${base - 25})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let y = 0; y < size; y += 3) {\r\n    const waveAmp = 2 + rng.next() * 3;\r\n    const waveFreq = 0.01 + rng.next() * 0.02;\r\n    const variation = 180 + Math.floor(rng.next() * 30);\r\n    ctx.strokeStyle = `rgb(${variation}, ${variation - 10}, ${variation - 20})`;\r\n    ctx.lineWidth = 2;\r\n    ctx.beginPath();\r\n    ctx.moveTo(0, y);\r\n    for (let x = 0; x < size; x += 2) {\r\n      ctx.lineTo(x, y + Math.sin(x * waveFreq) * waveAmp);\r\n    }\r\n    ctx.stroke();\r\n  }\r\n\r\n  for (let i = 0; i < 8; i++) {\r\n    const x = rng.next() * size * 0.8 + size * 0.1;\r\n    const y = rng.next() * size;\r\n    const wy = 10 + rng.next() * 20;\r\n    ctx.fillStyle = `rgba(140, 130, 120, 0.5)`;\r\n    ctx.beginPath();\r\n    ctx.ellipse(x, y, 8, wy, 0, 0, Math.PI * 2);\r\n    ctx.fill();\r\n  }\r\n}\r\n\r\nfunction drawPlaster(ctx, size, rng) {\r\n  const base = 205;\r\n  ctx.fillStyle = `rgb(${base}, ${base}, ${base})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let i = 0; i < 25; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const w = 5 + rng.next() * 15;\r\n    const h = 3 + rng.next() * 10;\r\n    ctx.fillStyle = `rgba(190, 190, 190, 0.6)`;\r\n    ctx.fillRect(x, y, w, h);\r\n    ctx.strokeStyle = `rgba(170, 170, 170, 0.8)`;\r\n    ctx.lineWidth = 0.5;\r\n    ctx.strokeRect(x, y, w, h);\r\n  }\r\n\r\n  for (let i = 0; i < 40; i++) {\r\n    const x1 = rng.next() * size;\r\n    const y1 = rng.next() * size;\r\n    const len = 5 + rng.next() * 20;\r\n    const angle = rng.next() * Math.PI * 2;\r\n    ctx.strokeStyle = `rgba(180, 180, 180, 0.5)`;\r\n    ctx.lineWidth = 0.5;\r\n    ctx.beginPath();\r\n    ctx.moveTo(x1, y1);\r\n    ctx.lineTo(x1 + Math.cos(angle) * len, y1 + Math.sin(angle) * len);\r\n    ctx.stroke();\r\n  }\r\n\r\n  for (let i = 0; i < 100; i++) {\r\n    ctx.fillStyle = `rgba(160, 160, 160, ${0.1 + rng.next() * 0.15})`;\r\n    ctx.fillRect(\r\n      rng.next() * size,\r\n      rng.next() * size,\r\n      0.5 + rng.next() * 1,\r\n      0.5 + rng.next() * 1\r\n    );\r\n  }\r\n}\r\n\r\nfunction drawBallast(ctx, size, rng) {\r\n  const base = 175;\r\n  ctx.fillStyle = `rgb(${base}, ${base}, ${base})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let i = 0; i < 120; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const w = 4 + rng.next() * 10;\r\n    const h = 4 + rng.next() * 10;\r\n    const rotation = rng.next() * Math.PI;\r\n    ctx.save();\r\n    ctx.translate(x, y);\r\n    ctx.rotate(rotation);\r\n    ctx.fillStyle = `rgb(${140 + Math.floor(rng.next() * 40)}, ${140 + Math.floor(rng.next() * 40)}, ${140 + Math.floor(rng.next() * 40)})`;\r\n    ctx.fillRect(-w / 2, -h / 2, w, h);\r\n    ctx.strokeStyle = `rgba(100, 100, 100, 0.5)`;\r\n    ctx.lineWidth = 0.5;\r\n    ctx.strokeRect(-w / 2, -h / 2, w, h);\r\n    ctx.restore();\r\n  }\r\n\r\n  for (let i = 0; i < 200; i++) {\r\n    ctx.fillStyle = `rgba(120, 120, 120, ${0.1 + rng.next() * 0.2})`;\r\n    ctx.fillRect(\r\n      rng.next() * size,\r\n      rng.next() * size,\r\n      0.5 + rng.next() * 1.5,\r\n      0.5 + rng.next() * 1.5\r\n    );\r\n  }\r\n}\r\n\r\nfunction drawRubber(ctx, size, rng) {\r\n  const base = 40;\r\n  ctx.fillStyle = `rgb(${base}, ${base}, ${base})`;\r\n  ctx.fillRect(0, 0, size, size);\r\n\r\n  for (let y = 0; y < size; y += 4) {\r\n    ctx.strokeStyle = `rgba(80, 80, 80, 0.4)`;\r\n    ctx.lineWidth = 1;\r\n    ctx.beginPath();\r\n    ctx.moveTo(0, y);\r\n    for (let x = 0; x < size; x += 2) {\r\n      const jitter = (rng.next() - 0.5) * 1;\r\n      ctx.lineTo(x, y + jitter);\r\n    }\r\n    ctx.stroke();\r\n  }\r\n\r\n  for (let i = 0; i < 60; i++) {\r\n    const x = rng.next() * size;\r\n    const y = rng.next() * size;\r\n    const w = 1 + rng.next() * 3;\r\n    const h = 1 + rng.next() * 2;\r\n    ctx.fillStyle = `rgba(60, 60, 60, 0.5)`;\r\n    ctx.fillRect(x, y, w, h);\r\n  }\r\n\r\n  for (let i = 0; i < 300; i++) {\r\n    ctx.fillStyle = `rgba(50, 50, 50, ${0.05 + rng.next() * 0.1})`;\r\n    ctx.fillRect(\r\n      rng.next() * size,\r\n      rng.next() * size,\r\n      0.5 + rng.next() * 1,\r\n      0.5 + rng.next() * 1\r\n    );\r\n  }\r\n}\r\n\r\nexport function createSurfaceTexture(THREE, kind) {\r\n  const size = 256;\r\n  const seed = kind.charCodeAt(0) + kind.charCodeAt(1) * 256;\r\n  const rng = new SeededRNG(seed);\r\n\r\n  const canvas = createCanvas(size);\r\n  const ctx = canvas.getContext('2d');\r\n\r\n  switch (kind) {\r\n    case 'metal':\r\n      drawMetal(ctx, size, rng);\r\n      break;\r\n    case 'gold':\r\n      drawGold(ctx, size, rng);\r\n      break;\r\n    case 'stone':\r\n      drawStone(ctx, size, rng);\r\n      break;\r\n    case 'moss':\r\n      drawMoss(ctx, size, rng);\r\n      break;\r\n    case 'wood':\r\n      drawWood(ctx, size, rng);\r\n      break;\r\n    case 'plaster':\r\n      drawPlaster(ctx, size, rng);\r\n      break;\r\n    case 'ballast':\r\n      drawBallast(ctx, size, rng);\r\n      break;\r\n    case 'rubber':\r\n      drawRubber(ctx, size, rng);\r\n      break;\r\n    default:\r\n      drawPlaster(ctx, size, rng);\r\n  }\r\n\r\n  const texture = new THREE.CanvasTexture(canvas);\r\n  texture.wrapS = THREE.RepeatWrapping;\r\n  texture.wrapT = THREE.RepeatWrapping;\r\n  texture.colorSpace = THREE.SRGBColorSpace;\r\n  texture.anisotropy = 2;\r\n\r\n  return texture;\r\n}\r\n","arcade/runner/worlds/worlds.mjs":"import {surfaceAt,jumpHeight,collectCourse,collideCourse,courseHazards} from './course.mjs';\r\nexport const WORLDS=[{id:'metal',name:'Orbital Foundry',length:600},{id:'temple',name:'Jade Temple',length:600},{id:'metro',name:'Neon Subway',length:600}];\r\nexport const POWERS={magnet:{duration:12},shield:{duration:10},doubleCoins:{duration:15},boost:{duration:6},jetpack:{duration:8},superJump:{duration:12},board:{duration:15},headstart:{instant:true},scoreBoost:{duration:15},phase:{duration:8},pogo:{instant:true},recovery:{duration:30},coinTier:{duration:20}};\r\nexport function createWorldRun({unlocked=0}={}){return {world:0,leg:0,mirrored:false,time:0,arrivalSerial:0,arrivalTime:0,eventSerial:0,lastEvent:null,events:[],destroyed:{},distance:0,totalDistance:0,coins:0,score:0,unlocked:Number.isFinite(unlocked)?Math.max(0,Math.min(2,Math.floor(unlocked))):0,phase:'run',lane:0,jump:0,jumpDuration:1.4,slide:0,powers:{},loop:0,stumbles:0,stumble:0,_wormholeElapsed:0,headstartUsed:false,elevation:0,collected:{},hazardsSeen:{},hazardSerial:0,lastHazard:null,lastPickup:null,jumpPeak:1.7};}\r\nexport function advance(r,delta,{moving=true,pace=1}={}){\r\n  let left=Number.isFinite(delta)?Math.max(0,Math.min(delta,.25)):0;\r\n  const gait=Number.isFinite(pace)?Math.max(0,Math.min(1,pace)):1;\r\n  while(left>1e-8){const dt=Math.min(.05,left);left-=dt;\r\n    r.time=(r.time||0)+dt;\r\n    const jumpBefore=r.jump,slideBefore=r.slide;\r\n    r.jump=Math.max(0,r.jump-dt);r.slide=Math.max(0,r.slide-dt);r.stumble=Math.max(0,(r.stumble||0)-dt);\r\n    for(const id of Object.keys(r.powers)){r.powers[id]=Math.max(0,r.powers[id]-dt);if(!r.powers[id])delete r.powers[id];}\r\n    if(r.phase==='wormhole'){r._wormholeElapsed+=dt;if(r._wormholeElapsed>=1.8-1e-8){if(r.leg===0)r.leg=1;else{r.leg=0;r.world=(r.world+1)%3;if(r.world===0)r.loop++;}r.mirrored=r.leg===1;r.arrivalSerial++;r.arrivalTime=r.time;r.unlocked=Math.max(r.unlocked,r.world);r.distance=0;r.elevation=0;r.collected={};r.hazardsSeen={};r.destroyed={};r.lastPickup=null;r.phase='run';r._wormholeElapsed=0;r.jump=0;r.slide=0;}continue;}\r\n    if(r.phase==='run'&&moving){\r\n      const from=r.distance,step=Math.min(12*dt*gait,600-r.distance);r.distance+=step;r.totalDistance+=step;r.score+=step*(r.powers.scoreBoost?2:1);\r\n      let surface=surfaceAt(r.world,r.distance,r.lane,r.leg);\r\n      // Walking up a ramp is continuous. Entering a bus side in flight cannot\r\n      // promote a below-roof runner to the surface when flight expires.\r\n      if(surface.height>r.elevation+.2){\r\n        if(!r.powers.jetpack){const safe=[-1,0,1].filter(l=>surfaceAt(r.world,r.distance,l,r.leg).height<=r.elevation+.2).sort((a,b)=>Math.abs(a-r.lane)-Math.abs(b-r.lane));if(safe.length){hit(r,{id:surface.id,type:'bus-side'});r.lane=safe[0];surface=surfaceAt(r.world,r.distance,r.lane,r.leg);}}\r\n      }else r.elevation=surface.height>=r.elevation-.15?surface.height:Math.max(surface.height,r.elevation-dt*6);\r\n      collideCourse(r,from,r.distance,{jumpBefore,slideBefore,dt,onHit:(r,h)=>{hit(r,h);if(r.lastHazard?.result!=='bypass')r.destroyed[h.id]=true;},onDodge:(r,h)=>emitRunEvent(r,'dodge',h)});\r\n      collectCourse(r,from,r.distance,{coin:collectCoin,power:collectPower});\r\n      if(r.distance>=600-1e-8){r.distance=600;r.phase='portal';}\r\n    }\r\n  }\r\n}\r\nexport function act(r,a){if(r.phase==='wormhole')return;if(a==='jump'){if(r.jump<=0){r.jumpDuration=r.powers.superJump?2.2:1.4;r.jump=r.jumpDuration;r.jumpPeak=r.powers.superJump?3.5:1.7;r.slide=0;}if(r.phase==='portal')enterPortal(r);}else if(a==='slide'&&r.phase==='run'){r.slide=.8;r.jump=0;}else if(['left','moveLeft','right','moveRight'].includes(a)){const lane=Math.max(-1,Math.min(1,r.lane+(['left','moveLeft'].includes(a)?-1:1)));const next=surfaceAt(r.world,r.distance,lane,r.leg);if(next.height>r.elevation+jumpHeight(r)+.4&&!r.powers.phase&&!r.powers.jetpack){hit(r,{id:next.id,type:'bus-side'});return;}if(next.height>r.elevation+.4&&!r.powers.jetpack){r.elevation=next.height;r.jump=0;}r.lane=lane;}}\r\nexport function enterPortal(r){if(r.phase!=='portal'||r.jump<=0)return false;r.phase='wormhole';r._wormholeElapsed=0;return true;}\r\nexport function collectPower(r,id){const p=POWERS[id];if(!p||r.phase!=='run')return false;if(id==='headstart'){if(r.headstartUsed||r.world!==0||r.loop!==0||r.distance>150)return false;r.totalDistance+=Math.max(0,150-r.distance);r.distance=Math.max(r.distance,150);r.headstartUsed=true;}else if(id==='pogo'){r.jumpDuration=2.2;r.jump=2.2;r.jumpPeak=3.5;r.slide=0;collectCoin(r,5);}else r.powers[id]=p.duration;return true;}\r\nexport function hit(r,hazard={id:'impact',type:'impact'}){\r\n  if(r.phase!=='run')return;\r\n  let result='stumble',protection=null;\r\n  for(const id of ['jetpack','boost','phase'])if(r.powers[id]){result='bypass';protection=id;break;}\r\n  if(result==='stumble')for(const id of ['shield','board','recovery'])if(r.powers[id]){delete r.powers[id];result='absorbed';protection=id;break;}\r\n  if(result==='stumble'){r.stumbles++;r.stumble=.9;r.score=Math.max(0,r.score-25);}\r\n  r.lastHazard={...hazard,result,protection};r.hazardSerial=(r.hazardSerial||0)+1;\r\n  emitRunEvent(r,result==='bypass'?'dodge':'hit',r.lastHazard);\r\n  return result;\r\n}\r\nexport function collectCoin(r,count=1){if(r.phase!=='run'||!Number.isFinite(count)||count<=0)return;const n=Math.floor(count);r.coins+=n*(r.powers.doubleCoins?2:1)*(r.powers.coinTier?3:1);r.score+=n*10*(r.powers.scoreBoost?2:1);}\r\n\r\nexport function emitRunEvent(r,type,data={}){\r\n  const e={...data,type,world:r.world,leg:r.leg,lane:data.lane??r.lane,distance:data.distance??r.distance,serial:++r.eventSerial,time:r.time};\r\n  r.lastEvent=e;r.events.push(e);if(r.events.length>32)r.events.shift();return e;\r\n}\r\nexport function punchCourse(r,id){\r\n  if(r.phase!=='run')return false;\r\n  const h=courseHazards(r.world,r.loop,r.leg).find(h=>h.id===id);\r\n  if(!h||r.hazardsSeen[id]||h.distance<r.distance||Math.hypot(h.distance-r.distance,(h.lane-r.lane)*2.8)>7.5)return false;\r\n  r.hazardsSeen[id]=true;r.destroyed[id]=true;r.score+=15;emitRunEvent(r,'punch',{...h,result:'destroyed'});return true;\r\n}\r\nexport function resolveThrowMiss(r,o){\r\n  if(r.phase!=='run')return 'inactive';\r\n  const feet=jumpHeight(r)+(r.powers.jetpack?3:0),top=feet+(r.slide>0?.7:1.8);\r\n  const touches=Math.abs(o.x-r.lane)*2.8<=o.r+.4&&o.y+o.r>=feet&&o.y-o.r<=top;\r\n  const data={id:'throw-'+o.id,kind:'throw',lane:o.x,distance:r.distance+o.d};\r\n  if(touches)return hit(r,data);\r\n  emitRunEvent(r,'dodge',{...data,result:'clear'});return 'dodge';\r\n}\r\n","arcade/shell-phase.mjs":"// Explicit simulation phases, never camera tracking, own a full-screen lease.\r\nlet sequence=0;\r\nexport function gameShellPhase(source,root){\r\n const doc=root?.ownerDocument??globalThis.document;\r\n const instanceId=`${source}-${Date.now().toString(36)}-${++sequence}`;\r\n let last='',closed=false,announced=false;\r\n if(root?.dataset)root.dataset.gameInstance=instanceId;\r\n const emit=value=>{\r\n  if(root?.dataset)root.dataset.gamePhase=value;\r\n  const Event=doc?.defaultView?.CustomEvent??globalThis.CustomEvent;\r\n  if(doc?.dispatchEvent&&Event)doc.dispatchEvent(new Event('myr5:game-phase',{detail:{source,instanceId,phase:value}}));\r\n };\r\n const phase=value=>{\r\n  if(closed)return;\r\n  // Several adapters construct their host before appending/opening its dialog.\r\n  // Announce the lifecycle only once it can actually own visible content.\r\n  if(root?.isConnected===false||root?.closest?.('dialog:not([open])')){if(value==='closed')closed=true;return;}\r\n  if(!announced){announced=true;emit('loading');last='loading';}\r\n  if(value===last)return;\r\n  last=value;emit(value);\r\n  if(value==='closed')closed=true;\r\n };\r\n const visibility=()=>{if(doc.hidden)phase('paused');};\r\n doc?.addEventListener?.('visibilitychange',visibility);\r\n phase('loading');\r\n return {phase,close(){phase('closed');doc?.removeEventListener?.('visibilitychange',visibility);if(root?.dataset?.gameInstance===instanceId){delete root.dataset.gameInstance;delete root.dataset.gamePhase;}}};\r\n}\r\n","arcade/slice/camera-slice.mjs":"// Camera-workout overlay for the slice engine: the MOM Inc. gun fires barrels at you, tracked limbs slice or explode them.\r\n// Lazy: app.mjs imports this only for martial arts and holds (external in scripts/build.mjs, optional asset).\r\nimport {createSlice,render,pathKind} from './core.mjs';\r\nimport {createHandGlow,renderHandGlow} from './hand-glow.mjs';\r\nimport {mapPointToOverlay} from '../workout-effects.mjs';\r\nimport {createQuestProgressReporter} from '../quest-progress-reporter.mjs';\r\nconst LIMBS=[13,14,15,16,25,26,27,28]; // elbows, wrists, knees, ankles\r\nconst BODY=[11,12,23,24,15,16,27,28];\r\nconst FAST=.9,SEEN=.4; // screen heights per second a limb must travel; ponytail: one fixed gate, tune on device\r\nconst W=180;\r\nexport function createFruitStreakReporter(ownerId,load=()=>import('../../modules/vault/quest-progress.mjs')){\r\n const reporter=createQuestProgressReporter(ownerId,{load});\r\n return n=>reporter.record('fruit-streak',n);\r\n}\r\nconst OFF={x:0,y:0,v:0};\r\nfunction toScreen(p,{width,height,mirrored},hostWidth=innerWidth,hostHeight=innerHeight){ // the one shared contain+mirror mapper\r\n const m=mapPointToOverlay(p,{videoWidth:width,videoHeight:height,hostWidth,hostHeight,mirrored});\r\n return m?{x:m.x,y:m.y,v:m.visibility}:OFF;\r\n}\r\nconst reducedMotion=()=>{try{return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;}catch{return false;}};\r\nexport function mountCameraSlice({host,mode='martial',sound=()=>{},onXp=()=>{},ownerId=null,seed=Date.now()}){\r\n const box=()=>{const r=host.getBoundingClientRect?.();return r&&r.width>0&&r.height>0?{w:r.width,h:r.height}:{w:innerWidth,h:innerHeight};};\r\n let size=box(),H=Math.round(W*size.h/size.w);const canvas=document.createElement('canvas');\r\n canvas.width=W;canvas.height=H;canvas.className='slice-canvas';canvas.setAttribute('aria-hidden','true');\r\n canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;pointer-events:none';host.append(canvas);\r\n const g=canvas.getContext('2d');\r\n // Hand light trails get their own smooth, DPR-aware canvas in CSS px (the game stays 180-px pixel art).\r\n const trail=document.createElement('canvas');trail.className='slice-trail-canvas';trail.setAttribute('aria-hidden','true');\r\n trail.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none';host.append(trail);\r\n const tg=trail.getContext('2d');let dpr=1;\r\n function resize(){size=box();dpr=Math.min(2,window.devicePixelRatio||1);\r\n  const tw=Math.max(1,Math.round(size.w*dpr)),th=Math.max(1,Math.round(size.h*dpr));if(trail.width!==tw||trail.height!==th){trail.width=tw;trail.height=th;}\r\n  const nh=Math.round(W*size.h/size.w);if(nh!==H&&nh>0){H=nh;canvas.height=H;e?.resize(H);}}\r\n const e=createSlice({w:W,h:H,seed,mode,strikes:mode==='martial',onEvent(type,d){\r\n  if(type==='hit')sound(d.kind==='slice'?'barrel-split':d.type==='barrel-fuel'||d.type==='barrel-toxic'?'blast-big':'blast');\r\n  else if(type==='shot')sound('gun-shot');else if(type==='gun-aim')sound('gun-arm');else if(type==='chain')sound('blast');\r\n  else if(type==='bonus'){sound('dial');onXp(d.xp);}\r\n  else if(type==='combo'){sound('lever');reportStreak(d.n);}else if(type==='strike')sound('jelly');\r\n  else if(type==='laser-aim')sound('crt');else if(type==='laser-shield')sound('switch');else if(type==='laser-hit')sound('main-control');\r\n }});\r\n const reportStreak=createFruitStreakReporter(ownerId);\r\n const hist=new Map(),prevBody={pts:null,t:0};\r\n const handGlow=createHandGlow({onImpact(d){window.dispatchEvent(new CustomEvent('myr5:punch-impact',{detail:d}));if(!reducedMotion())try{navigator.vibrate?.(15);}catch{}}});\r\n const onLevel=ev=>handGlow.setLevel(ev.detail?.level);window.addEventListener('myr5:aura-level',onLevel);\r\n resize();const observer=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;observer?.observe(host);let last=performance.now(),raf=0,laserIn=7,counting=false,body={x:W/2,y:H*.6};\r\n const onVisibility=()=>{if(document.visibilityState==='hidden')handGlow.clear();};document.addEventListener('visibilitychange',onVisibility);\r\n function frame(now){\r\n  raf=requestAnimationFrame(frame);const dt=Math.min(.05,(now-last)/1000);last=now;\r\n  if(counting){laserIn-=dt;if(laserIn<=0&&e.laserStart(body.x,body.y)){laserIn=9+Math.random()*6;}}\r\n  e.step(dt);\r\n  const effectStart=performance.now();handGlow.step(dt,now);\r\n  render(g,e,{pointerTrail:false});\r\n  tg.setTransform(dpr,0,0,dpr,0,0);tg.clearRect(0,0,size.w,size.h);\r\n  renderHandGlow(tg,handGlow.state,{w:size.w,h:size.h,now});handGlow.measure(performance.now()-effectStart,now);\r\n }\r\n raf=requestAnimationFrame(frame);\r\n return {\r\n  engine:e,\r\n  pose(detail){\r\n   const {points,now}=detail;counting=!!detail.counting;if(!points){handGlow.pose(null);return;}\r\n   const sp=points.map(p=>toScreen(p,detail,size.w,size.h)),toL=p=>({x:p.x*W,y:p.y*H});\r\n   handGlow.pose(points,{now:now??performance.now(),mapPoint:p=>toScreen(p,detail,size.w,size.h),world:detail.world,aspect:size.w/size.h,videoAspect:detail.width/detail.height||1});\r\n   // whole-body motion, screen heights per second, feeds the laser's stillness test\r\n   const t=(now??performance.now())/1000,pb=BODY.map(i=>sp[i]);\r\n   if(prevBody.pts&&t>prevBody.t){let s=0,n=0;BODY.forEach((_,k)=>{if(pb[k].v>SEEN&&prevBody.pts[k].v>SEEN){s+=Math.hypot(pb[k].x-prevBody.pts[k].x,pb[k].y-prevBody.pts[k].y);n++;}});if(n)e.feedMotion(s/n/(t-prevBody.t));}\r\n   prevBody.pts=pb;prevBody.t=t;\r\n   const core=[11,12,23,24].map(i=>sp[i]).filter(p=>p.v>SEEN);if(core.length)body=toL({x:core.reduce((a,p)=>a+p.x,0)/core.length,y:core.reduce((a,p)=>a+p.y,0)/core.length});\r\n   if(mode==='hold'){ // holds: any visible landmark touching a falling bonus item scores\r\n    sp.forEach((p,i)=>{if(i>0&&p.v>SEEN){const q=toL(p);e.hitSegment(q,q,{kind:'explode',pad:6});}});return;\r\n   }\r\n   for(const i of LIMBS){\r\n    const p=sp[i];if(p.v<SEEN){hist.delete(i);continue;}\r\n    const q=toL(p),h=hist.get(i)||[];h.push({...q,t});if(h.length>6)h.shift();hist.set(i,h);\r\n    if(h.length<2)continue;const a=h[h.length-2],dtp=t-a.t;\r\n    const spd=dtp>0?Math.hypot(q.x-a.x,q.y-a.y)/H/dtp:0;if(dtp<=0||dtp>.5||spd<FAST)continue;\r\n    e.addTrail(a.x,a.y);e.addTrail(q.x,q.y);\r\n    e.hitSegment(a,q,{kind:pathKind(h),pad:4,speed:spd});\r\n   }\r\n  },\r\n  stop(){cancelAnimationFrame(raf);document.removeEventListener('visibilitychange',onVisibility);window.removeEventListener('myr5:aura-level',onLevel);observer?.disconnect();handGlow.clear();canvas.remove();trail.remove();}\r\n };\r\n}\r\n","arcade/slice/core.mjs":"// Fruit-Ninja slice engine: input-agnostic (pose limbs, finger swipes, taps all call hitSegment) + 2D pixel renderer.\r\n// Logical canvas is small (default 180x320); scale it up in CSS with image-rendering:pixelated.\r\n// Optional art: arcade/slice/sprites/<type>.png = 8 rotation frames of 64px cells in one row (Tripo renders replace the procedural pixels).\r\n// [main, shade, light] per object id. Every id's art is arcade/slice/sprites/<id>.png (Tripo render) or, until it exists,\r\n// the canister placeholder sheet tinted with the main colour.\r\nimport {XP_SCALE} from '../../progression-rules.mjs';\r\nimport {FOOD_TYPES,FOOD_BOOM} from '../cover/food-types.mjs'; // Crouching Crisis food ids (palettes + boom sizes only)\r\n// The MOM Inc. barrel blaster replaces the old coach thrower. All tuning lives here (real phones need tuning).\r\nexport const GUN={\r\n aim:.45,            // s of LED blink + tube tracking before each shot\r\n recoilPx:6, recoilS:.12, flashS:.09, scale:2, // scale = integer pixel multiplier for the 48x24 gun art\r\n shakeShot:.18, shakeSlice:.12, shakeExplode:.25, shakeBig:.4, shakeDecay:.85, shakePx:8,\r\n freeze:.04,         // hit-pause on explode\r\n scaleStart:.6,      // drawn size at the muzzle, grows to 1 by the apex (collision always full)\r\n wobbleAmp:[0,4,6,8],// by wave band 1,2,3-6,7+ (logical px, <=25% of a barrel radius)\r\n wobbleHz:[1,1.6], wobbleFadeLast:.3, wobbleFadeIn:.25, // fade out over the last fraction of the flight, in over the first seconds\r\n spinMax:Math.PI*2,  // rad/s, rolling not tumbling\r\n shotsPerSec:[1.5,2.2,2.8], // within a volley, wave 1-2, 3-6, 7+\r\n // Fruit Ninja rhythm: a volley is 1-3 barrels (pick one entry), the next volley only starts volleyGap s AFTER the last barrel is resolved (hit or off-screen).\r\n volley:[[1,1,2],[1,2],[1,2,2,3],[2,3,3]], volleyGap:[1.4,1.1,.9,.8], // by wave band 1,2,3-6,7+\r\n bonusEvery:{hold:[6,9],martial:[4,6]}, // s between XP bonus drops\r\n hardHit:1.6,        // limb speed (screen heights/s) for +5 HARD HIT\r\n chainRadius:28, chainDelay:.08, // fuel barrel: other barrels whose centre is within r+chainRadius blow 80 ms later (one level)\r\n trailEvery:.06, trailMax:3,\r\n brand:{plate:'#ff4fd8',stencil:'#fff',stripe:'#ffd23a',body:'#3a3f4a',hi:'#5b6475',led:'#ffb000'},\r\n};\r\n// [main, shade, light] per id. Barrel art: arcade/slice/sprites/<id>.png (Tripo sheet) or the procedural 16x16 barrel below.\r\nexport const TYPES={'barrel-wood':['#a3642c','#5e3514','#e0a060'],'barrel-steel':['#7d8aa0','#3f4a5c','#d6e0f0'],'barrel-toxic':['#4fcf4a','#1f6e22','#c8ffb0'],'barrel-fuel':['#d8342a','#6e1410','#ffb070'],'xp-star':['#ffe14a','#c8901a','#fffbd0'],'xp-orb':['#4affc8','#1a9c80','#d8fff4'],...FOOD_TYPES};\r\nexport const TYPE_IDS=Object.keys(TYPES).filter(k=>k.startsWith('barrel-')); // barrels only: food/xp ids never join the barrel bag\r\nexport const BONUS_IDS=['xp-star','xp-orb'];\r\nconst TYPE_BAG=['barrel-wood','barrel-wood','barrel-wood','barrel-wood','barrel-steel','barrel-steel','barrel-toxic','barrel-toxic','barrel-fuel']; // common wood, rare fuel\r\n// Explosion size per barrel: particle count, stave chunks, smoke puffs, screen shake, ring growth, stain life.\r\nconst BOOM={'barrel-wood':{parts:22,chunks:8,smoke:4,shake:GUN.shakeExplode,ring:2.2,stain:4},'barrel-steel':{parts:16,chunks:7,smoke:4,shake:GUN.shakeExplode,ring:2.2,stain:4},\r\n 'barrel-toxic':{parts:30,chunks:6,smoke:6,shake:GUN.shakeExplode,ring:2.5,stain:5.5},'barrel-fuel':{parts:40,chunks:10,smoke:6,shake:GUN.shakeBig,ring:3,stain:4},...FOOD_BOOM};\r\nexport const TARGET_SIZE=2.5;\r\nconst GRAVITY=260,TAU=Math.PI*2,HALF_LIFE=1.25;\r\nexport function rng(seed=1){return()=>{seed|=0;seed=seed+0x6D2B79F5|0;let t=Math.imul(seed^seed>>>15,1|seed);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};}\r\nexport function distPointSeg(p,a,b){const dx=b.x-a.x,dy=b.y-a.y,l=dx*dx+dy*dy,t=l?Math.max(0,Math.min(1,((p.x-a.x)*dx+(p.y-a.y)*dy)/l)):0;return Math.hypot(p.x-(a.x+t*dx),p.y-(a.y+t*dy));}\r\n// Straight shot (punch, kick) vs looping shot (chop, hook, roundhouse): sideways bow of the last ~6 points relative to the chord.\r\nexport function pathCurvature(pts){\r\n const p=pts.slice(-6);if(p.length<3)return 0;\r\n const a=p[0],b=p[p.length-1],chord=Math.hypot(b.x-a.x,b.y-a.y);let path=0,bow=0;\r\n for(let i=1;i<p.length;i++)path+=Math.hypot(p[i].x-p[i-1].x,p[i].y-p[i-1].y);\r\n if(path<1e-9)return 0;\r\n for(const q of p)bow=Math.max(bow,distPointSeg(q,a,b));\r\n return chord<path*.25?1:Math.min(1,bow/Math.max(chord,1e-9)*2); // loops back on itself = fully curved\r\n}\r\nexport const pathKind=(pts,limit=.2)=>pathCurvature(pts)>limit?'slice':'explode';\r\n// samples = whole-body motion per second (screen heights/s); still enough = shield up.\r\nexport const stillness=(samples,max=.1)=>samples.length>0&&samples.reduce((s,v)=>s+v,0)/samples.length<=max;\r\n\r\nexport function createSlice({w=180,h=320,seed=1,mode='martial',auto=true,coachX=w/2,stillMax=.1,strikes=true,onEvent=()=>{}}={}){\r\n const rand=rng(seed),pick=a=>a[Math.floor(rand()*a.length)];\r\n const e={w,h,mode,coachX,objects:[],halves:[],parts:[],stains:[],trail:[],pops:[],t:0,wave:0,strikes:0,score:0,xp:0,combo:0,best:0,hits:0,over:false,\r\n  laser:{phase:'idle',t:0,tx:0,ty:0,samples:[],result:null,flash:0},cool:0,spawnIn:1,bonusIn:4,swipeAt:-9,swipeN:0,speed:1,\r\n  gun:{phase:'idle',t:0,tx:coachX,ang:0,recoil:0,flash:0},shake:0,freeze:0,shots:0,lastShot:-9,fx:[],chain:[]};\r\n const emit=(type,data)=>onEvent(type,data);\r\n e.resize=nh=>{if(nh>0)h=e.h=nh;}; // orientation change: the world keeps its width, height follows the host\r\n const pop=(text,x,y,c='#fff',big=false)=>e.pops.push({text,x,y,c,big,life:big?1.1:.8});\r\n function mk(type,x,y,vx,vy,r,bonus=false){const o={type,x,y,vx,vy,r:r*TARGET_SIZE,rot:rand()*TAU,vr:bonus?(rand()-.5)*8:(vx<0?-1:1)*(3+rand()*3),bonus,alive:true,seed:Math.floor(rand()*1e6),\r\n   flight:0,T:0,wA:0,wF:0,wP:0,wOff:0,sc:bonus||mode==='rest'?1:GUN.scaleStart,trail:[],trailAt:0};o.vr=Math.max(-GUN.spinMax,Math.min(GUN.spinMax,o.vr));e.objects.push(o);return o;}\r\n const bandOf=w=>w<=1?0:w<=2?1:w<=6?2:3,wobble=(o,amp,T)=>{o.T=T;o.wA=amp*(rand()*.5+.5);o.wF=GUN.wobbleHz[0]+rand()*(GUN.wobbleHz[1]-GUN.wobbleHz[0]);o.wP=rand()*TAU;};\r\n // Parabola from the coach at the bottom toward the user: same apex every wave; speed scales gravity so later waves fall faster, not higher.\r\n e.launch=()=>{const tx=w*(.15+rand()*.7),apex=h*(.45+rand()*.35),g=GRAVITY*e.speed*e.speed,vy=-Math.sqrt(2*g*apex),T=-2*vy/g;\r\n  const o=mk(pick(TYPE_BAG),coachX,h-4,(tx-coachX)/T,vy,11+rand()*6);o.g=g;o.tApex=-vy/g;wobble(o,GUN.wobbleAmp[bandOf(e.wave)],T);return o;};\r\n // Generic thrower (rest mode: coach throws DOWN from the top): e.spawn({type,x,y,vx,vy,r,bonus}); non-bonus objects feel gravity.\r\n // wobble = max sideways sway in logical px, T = flight time (fades the sway out so the landing spot stays fair).\r\n e.spawn=({type=pick(TYPE_BAG),x,y,vx=0,vy=0,r=12,bonus=false,wobble:amp=0,T=0})=>{const o=mk(type,x,y,vx,vy,r,bonus);if(amp)wobble(o,amp,T);return o;};\r\n e.dropBonus=()=>mk(pick(BONUS_IDS),w*(.1+rand()*.8),-12,(rand()-.5)*6,22+rand()*10,9,true);\r\n // Barrels are held, then fired one by one by the gun (aim cue first). Gap = 1/shots-per-second cap by wave band.\r\n e.spawnWave=()=>{const n=pick(GUN.volley[bandOf(e.wave)]),gap=1/GUN.shotsPerSec[Math.min(2,Math.max(0,bandOf(e.wave)-1))];for(let i=0;i<n;i++){const o=e.launch();if(!i)o.wA=0;o.hold=true;o.delay=GUN.aim+i*gap;o.vx0=o.vx;o.vy0=o.vy;o.vx=o.vy=0;}};\r\n // Gun: aim cue (tube tracks tx, LED blinks) then fire (recoil, muzzle flash, shake). Rest mode calls these around its throws.\r\n e.gunAim=tx=>{const G=e.gun;if(G.phase==='aim')return;G.phase='aim';G.t=0;G.tx=tx;emit('gun-aim',{tx});};\r\n e.gunFire=()=>{const G=e.gun;G.phase='fire';G.t=0;G.recoil=1;G.flash=GUN.flashS;e.shake=Math.max(e.shake,GUN.shakeShot);e.shots++;e.lastShot=e.t;emit('shot',{});};\r\n function burst(x,y,type,n,spread){const cs=TYPES[type];for(let i=0;i<n;i++){const a=rand()*TAU,s=spread*(.3+rand());e.parts.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s-30,life:.5+rand()*.4,c:pick(cs),s:1+Math.floor(rand()*3)});}}\r\n function smoke(o,n){for(let i=0;i<n;i++)e.fx.push({kind:'smoke',x:o.x+(rand()-.5)*o.r,y:o.y+(rand()-.5)*o.r*.6,vx:(rand()-.5)*10,life:.6,max:.6,d:0});}\r\n // Layered boom: white flash -> tinted flash -> shockwave ring -> stave chunks -> smoke, plus particles, shake and a hit-pause. Fuel chains once.\r\n function boom(o,chained){\r\n  const B=BOOM[o.type]||BOOM['barrel-wood'],cs=TYPES[o.type];burst(o.x,o.y,o.type,B.parts,120+B.parts*2);\r\n  e.fx.push({kind:'flash',x:o.x,y:o.y,r:o.r*.9,c:'#fff',c2:cs[2],life:.07,max:.07,d:0});\r\n  if(o.type==='barrel-fuel')e.fx.push({kind:'flash',x:o.x,y:o.y,r:o.r*1.2,c:'#ffe9a0',c2:'#ff8a30',life:.09,max:.09,d:.06});\r\n  e.fx.push({kind:'ring',x:o.x,y:o.y,r0:o.r,r1:o.r*B.ring,c:cs[2],life:.25,max:.25,d:0});\r\n  for(let i=0;i<B.chunks;i++){const a=rand()*TAU,sp=60+rand()*90;e.fx.push({kind:'chunk',x:o.x,y:o.y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-50,rot:rand()*TAU,vr:(rand()-.5)*14,c:cs[i%2],life:.6+rand()*.4,max:1,d:0});}\r\n  smoke(o,B.smoke);\r\n  e.shake=Math.max(e.shake,B.shake);if(!chained)e.freeze=GUN.freeze;\r\n  if(o.type==='barrel-fuel'&&!chained)for(const q of e.objects)if(q!==o&&q.alive&&!q.hold&&!q.bonus&&Math.hypot(q.x-o.x,q.y-o.y)<=o.r+GUN.chainRadius&&!e.chain.some(c=>c.o===q))e.chain.push({o:q,t:GUN.chainDelay});\r\n }\r\n function award(o,kind,a,b,{speed=0,chained=false}={}){\r\n  o.alive=false;e.hits++;let stain=4;\r\n  if(kind==='slice'){const ang=Math.atan2(b.y-a.y,b.x-a.x),n=[-Math.sin(ang),Math.cos(ang)];for(const side of[-1,1])e.halves.push({...o,cut:ang,side,life:HALF_LIFE,vx:o.vx*.5+n[0]*side*60,vy:o.vy*.5+n[1]*side*60,rot0:o.rot,hr:0,hvr:side*(2+rand()*4)});burst(o.x,o.y,o.type,8,60);\r\n   if(!o.bonus){smoke(o,3);e.shake=Math.max(e.shake,GUN.shakeSlice);}}\r\n  else if(o.bonus)burst(o.x,o.y,o.type,22,120);\r\n  else{boom(o,chained);stain=(BOOM[o.type]||BOOM['barrel-wood']).stain;}\r\n  e.stains.push({c:TYPES[o.type][1],life:stain,dots:Array.from({length:7},()=>[o.x+(rand()-.5)*20,o.y+(rand()-.5)*20,1+Math.floor(rand()*3)])}); // juice splat fades on the background\r\n  if(o.bonus){e.xp+=3*XP_SCALE;e.score+=15;pop(`+${3*XP_SCALE} XP`,o.x,o.y-10,'#ffe14a');emit('bonus',{xp:3*XP_SCALE,x:o.x,y:o.y,kind});}\r\n  else if(chained){e.score+=10;emit('chain',{x:o.x,y:o.y,type:o.type});return;}\r\n  else{const hard=speed>=GUN.hardHit;e.score+=hard?15:10;if(hard)pop('HARD HIT',o.x,o.y-o.r-4,'#ffb000');emit('hit',{kind,x:o.x,y:o.y,type:o.type,hard});}\r\n  if(e.t-e.swipeAt>.45)e.swipeN=0;e.swipeAt=e.t;e.swipeN++;\r\n  if(e.swipeN>=3){const bonus=10*(e.swipeN-2);e.score+=bonus;e.combo=e.swipeN;e.best=Math.max(e.best,e.combo);pop(`COMBO x${e.swipeN}`,w/2,h*.35,'#ff4fd8',true);emit('combo',{n:e.swipeN,bonus});}\r\n }\r\n // Sweep a limb/finger segment a->b (logical coords). A tap/touch is a===b. Returns the number of objects hit.\r\n e.hitSegment=(a,b,{kind='slice',pad=2,speed=0}={})=>{let n=0;for(const o of e.objects)if(o.alive&&!o.hold&&distPointSeg(o,a,b)<=o.r+pad){award(o,kind,a,b,{speed});n++;}e.objects=e.objects.filter(o=>o.alive);return n;};\r\n e.addTrail=(x,y)=>e.trail.push({x,y,life:.25});\r\n // Laser (the bomb): ~1s red sight-line, then it fires. feedMotion() collects whole-body motion during the aim window.\r\n e.laserStart=(tx,ty,aim=1)=>{if(e.laser.phase!=='idle')return false;Object.assign(e.laser,{phase:'aim',t:0,aim,tx,ty,samples:[],result:null});emit('laser-aim',{tx,ty});return true;};\r\n e.feedMotion=m=>{if(e.laser.phase==='aim')e.laser.samples.push(m);};\r\n function fire(){const L=e.laser,shield=stillness(L.samples,stillMax);L.phase='fire';L.t=0;L.result=shield?'shield':'hit';\r\n  if(shield){emit('laser-shield',{x:L.tx,y:L.ty});pop('BLOCKED',L.tx,L.ty-20,'#7cf',true);}\r\n  else{L.flash=.35;strike();emit('laser-hit',{x:L.tx,y:L.ty});pop('ZAPPED',L.tx,L.ty-20,'#f44',true);}}\r\n function strike(){if(e.cool>0)return;e.strikes++;emit('strike',{n:e.strikes});if(e.strikes>=3){e.cool=3;e.strikes=0;e.objects=[];emit('wave-end',{wave:e.wave});pop('WAVE OVER - KEEP GOING',w/2,h*.4,'#fff',true);}}\r\n e.step=dt=>{\r\n  if(e.freeze>0){e.freeze-=dt;return;} // hit-pause: the world holds for a beat on a big explosion\r\n  e.t+=dt;e.shake*=Math.pow(GUN.shakeDecay,dt*60);if(e.shake<.02)e.shake=0;\r\n  const G=e.gun;G.t+=dt;if(G.recoil>0)G.recoil=Math.max(0,G.recoil-dt/GUN.recoilS);if(G.flash>0)G.flash-=dt;if(G.phase==='fire'&&G.t>=GUN.recoilS)G.phase='idle';\r\n  let next=null;for(const o of e.objects)if(o.hold&&(!next||o.delay<next.delay))next=o;\r\n  if(next&&next.delay<=GUN.aim&&G.phase!=='aim')e.gunAim(next.x+next.vx0*(-2*next.vy0/next.g)); // aim at where the next barrel lands\r\n  G.ang+=((G.phase==='aim'?Math.max(-.61,Math.min(.61,Math.atan2(G.tx-e.coachX,e.h*.55))):0)-G.ang)*Math.min(1,dt*20);\r\n  if(e.cool>0){e.cool-=dt;if(e.cool<=0)e.speed=Math.max(1,e.speed-.1);}\r\n  else if(auto&&!e.over){\r\n   if(mode==='martial'&&!e.objects.some(o=>!o.bonus)){e.spawnIn-=dt;if(e.spawnIn<=0){e.wave++;e.speed=Math.min(1.5,1+e.wave*.04);e.spawnWave();e.spawnIn=GUN.volleyGap[bandOf(e.wave)];emit('wave',{wave:e.wave});}} // gap counts only once the volley is resolved\r\n   e.bonusIn-=dt;if(e.bonusIn<=0){e.dropBonus();{const[lo,hi]=GUN.bonusEvery[mode==='hold'?'hold':'martial'];e.bonusIn=lo+rand()*(hi-lo);}}\r\n  }\r\n  for(const o of e.objects){\r\n   if(o.hold){o.delay-=dt;if(o.delay<=0){o.hold=false;o.vx=o.vx0;o.vy=o.vy0;e.gunFire();}continue;}\r\n   o.rot+=o.vr*dt;o.x+=o.vx*dt;o.y+=o.vy*dt;if(!o.bonus)o.vy+=(o.g||GRAVITY)*dt;\r\n   o.flight+=dt;\r\n   if(o.wA){const f=Math.min(1,o.flight/GUN.wobbleFadeIn)*(o.T?Math.max(0,Math.min(1,(o.T-o.flight)/(o.T*GUN.wobbleFadeLast))):1),off=o.wA*f*Math.sin(TAU*o.wF*o.flight+o.wP);o.x+=off-o.wOff;o.wOff=off;} // sideways sway only; the vertical stays a pure parabola\r\n   if(!o.bonus){o.sc=mode==='rest'?1-.25*Math.min(1,o.flight/(o.T||2)):GUN.scaleStart+(1-GUN.scaleStart)*Math.min(1,o.flight/(o.tApex||1));\r\n    if(o.flight>=o.trailAt){o.trailAt=o.flight+GUN.trailEvery;o.trail.push({x:o.x,y:o.y});if(o.trail.length>GUN.trailMax)o.trail.shift();}}\r\n   if(o.vy>0&&o.y>h+o.r){o.alive=false;if(!o.bonus){emit('land',{x:o.x,type:o.type});if(strikes)strike();}} // a missed rock lands = strike\r\n  }\r\n  for(const c of e.chain)if((c.t-=dt)<=0&&c.o.alive){award(c.o,'explode',c.o,c.o,{chained:true});}\r\n  e.chain=e.chain.filter(c=>c.t>0);\r\n  e.objects=e.objects.filter(o=>o.alive);\r\n  for(const q of e.fx){if(q.d>0){q.d-=dt;continue;}q.life-=dt;if(q.kind!=='ring'&&q.kind!=='flash'){q.x+=(q.vx||0)*dt;q.y+=(q.vy||-20)*dt;if(q.kind==='chunk'){q.vy+=GRAVITY*.6*dt;q.rot+=q.vr*dt;}}}e.fx=e.fx.filter(q=>q.life>0);\r\n  for(const q of e.halves){q.x+=q.vx*dt;q.y+=q.vy*dt;q.vy+=GRAVITY*dt;q.hr+=q.hvr*dt;q.life-=dt;}e.halves=e.halves.filter(q=>q.life>0&&q.y<h+40);\r\n  for(const q of e.parts){q.x+=q.vx*dt;q.y+=q.vy*dt;q.vy+=GRAVITY*.6*dt;q.life-=dt;}e.parts=e.parts.filter(q=>q.life>0);\r\n  for(const q of e.stains)q.life-=dt;e.stains=e.stains.filter(q=>q.life>0);\r\n  for(const q of e.trail)q.life-=dt;e.trail=e.trail.filter(q=>q.life>0);\r\n  for(const q of e.pops){q.life-=dt;q.y-=14*dt;}e.pops=e.pops.filter(q=>q.life>0);\r\n  const L=e.laser;if(L.flash>0)L.flash-=dt;\r\n  if(L.phase==='aim'){L.t+=dt;if(L.t>=L.aim)fire();}else if(L.phase==='fire'){L.t+=dt;if(L.t>=(L.result==='shield'?.7:.3))L.phase='idle';}\r\n };\r\n return e;\r\n}\r\n\r\n// ---- renderer ----\r\nconst sheets={},cache={};\r\n// sheet(id): the id's own sheet (Tripo render). Bonus ids fall back to the tinted canister; barrels and the gun go procedural. null until loaded.\r\nfunction load(src){return new Promise((ok,no)=>{const i=new Image();i.onload=()=>ok(i);i.onerror=no;i.src=src;});}\r\nfunction tinted(img,colour){const c=document.createElement('canvas');c.width=img.width;c.height=img.height;const x=c.getContext('2d');x.drawImage(img,0,0);x.globalCompositeOperation='multiply';x.fillStyle=colour;x.fillRect(0,0,c.width,c.height);x.globalCompositeOperation='destination-in';x.drawImage(img,0,0);return c;}\r\nfunction sheet(type){\r\n if(typeof Image==='undefined')return null;\r\n if(!(type in sheets)){sheets[type]=null;const base=`/arcade/slice/sprites/`;\r\n  load(base+type+'.png').then(i=>{sheets[type]=i;},()=>{if(type.startsWith('xp-'))load(base+'canister.png').then(i=>{sheets[type]=tinted(i,TYPES[type][0]);},()=>{});});}\r\n return sheets[type];\r\n}\r\nconst canvasOf=(w,h)=>{const c=document.createElement('canvas');c.width=w;c.height=h;return c;};\r\nfunction procedural(type,seed){ // 16x16: barrels get a staved cylinder with steel hoops and a MOM dot; bonuses stay hash-shaded discs\r\n const k=type+seed%7;if(cache[k])return cache[k];const c=canvasOf(16,16),g=c.getContext('2d'),cs=TYPES[type];\r\n if(type.startsWith('barrel-')){\r\n  for(let y=0;y<16;y++)for(let x=2;x<14;x++){if((y===0||y===15)&&(x===2||x===13))continue;g.fillStyle=(x-2)%3===2||x===13?cs[1]:x===3?cs[2]:cs[0];g.fillRect(x,y,1,1);}\r\n  g.fillStyle='#9aa6b4';for(const y of[3,12])g.fillRect(2,y,12,2);g.fillStyle='#d8e2ee';for(const y of[3,12])g.fillRect(3,y,1,1);\r\n  g.fillStyle=GUN.brand.plate;g.fillRect(6,6,3,3);g.fillStyle='#fff';g.fillRect(6,6,1,1);\r\n  return cache[k]=c;\r\n }\r\n for(let y=0;y<16;y++)for(let x=0;x<16;x++){const d=Math.hypot(x-7.5,y-7.5);if(d>7.5)continue;const n=((x*73856093^y*19349663^seed%7*83492791)>>>0)%7;g.fillStyle=d>6.2?cs[1]:x+y<9&&n<5?cs[2]:n<2?cs[1]:cs[0];g.fillRect(x,y,1,1);}\r\n return cache[k]=c;\r\n}\r\nfunction drawBody(g,o){\r\n const s=sheet(o.type),r=o.r*(o.sc||1);\r\n if(s){const f=Math.floor(((o.rot%TAU+TAU)%TAU)/TAU*8)%8;g.drawImage(s,f*64,0,64,64,-r,-r,r*2,r*2);}\r\n else{g.save();g.rotate(Math.round(o.rot/(Math.PI/4))*Math.PI/4);g.drawImage(procedural(o.type,o.seed),-r,-r,r*2,r*2);g.restore();}\r\n if(o.bonus){g.strokeStyle='#ffe14a';g.lineWidth=1;g.strokeRect(-r-1,-r-1,r*2+2,r*2+2);}\r\n}\r\n\r\n// ---- the MOM Inc. gun: 48x24 receiver + separate 8x22 tube that rotates about the pivot ----\r\nconst PIV={x:24,y:9},TUBE=20; // pivot in the receiver canvas (unflipped), visible tube length in art px\r\nfunction gunArt(flip){\r\n const k='gun'+flip;if(cache[k])return cache[k];const B=GUN.brand,body=canvasOf(48,24),g=body.getContext('2d');\r\n const R=(x,y,w,h,c)=>{g.fillStyle=c;g.fillRect(x,flip?24-y-h:y,w,h);}; // flip = hang from the ceiling (rest mode); the plate stays upright\r\n R(14,22,20,2,'#6b7280');R(14,22,20,1,'#8b95a8');                                  // bolted mount\r\n R(8,8,32,14,B.body);R(8,10,32,1,B.hi);R(8,20,32,2,'#262a33');R(8,8,1,2,'#262a33');R(39,8,1,2,'#262a33'); // receiver\r\n for(let x=8;x<40;x+=4){R(x,8,2,2,B.stripe);R(x+2,8,2,2,'#111');}                   // hazard stripe along the top edge\r\n R(9,12,2,8,B.hi);R(37,12,2,8,B.hi);R(10,14,1,1,'#111');R(10,18,1,1,'#111');R(37,14,1,1,'#111');R(37,18,1,1,'#111'); // grips + bolts\r\n R(17,12,14,7,B.plate);R(17,12,14,1,'#ffa0ec');R(17,18,14,1,'#c0309a');             // MOM Inc. plate\r\n const M=['X...X','XX.XX','X.X.X','X...X','X...X'];g.fillStyle=B.stencil;M.forEach((row,j)=>{for(let i=0;i<5;i++)if(row[i]==='X')g.fillRect(21+i,(flip?5:12)+1+j,1,1);}); // stencil drawn upright even when the gun hangs flipped\r\n R(26,13,4,1,'#c0309a');R(26,15,4,1,'#c0309a');R(26,17,4,1,'#c0309a');              // \"INC\" tick marks beside the stencil\r\n const tube=canvasOf(8,22),t=tube.getContext('2d');\r\n t.fillStyle=B.body;t.fillRect(1,2,6,20);t.fillStyle=B.hi;t.fillRect(2,2,1,20);t.fillStyle='#262a33';t.fillRect(6,2,1,20);\r\n t.fillStyle='#8b95a8';t.fillRect(0,0,8,2);t.fillStyle='#5b6475';t.fillRect(0,2,8,1);t.fillStyle='#8b95a8';t.fillRect(0,8,8,1);t.fillRect(0,15,8,1); // muzzle ring + collars\r\n return cache[k]={body,tube};\r\n}\r\n// Pose of the gun for a mount point: x = centre, y = bottom edge (flip: top edge).\r\nfunction gunPose(e,{x=e.coachX,y=e.h,flip=false}={}){\r\n const s=GUN.scale,ox=x-24*s,oy=flip?y:y-24*s,py=oy+(flip?24-PIV.y:PIV.y)*s,G=e.gun,a=Math.round(G.ang/(Math.PI/16))*(Math.PI/16),d=flip?1:-1;\r\n return {s,ox,oy,px:x,py,a,d,mx:x+Math.sin(a)*TUBE*s,my:py+d*Math.cos(a)*TUBE*s};\r\n}\r\n// Where the laser/shots leave the gun (angle 0, no recoil), for hosts that draw their own beam.\r\nexport const gunMuzzle=(e,o)=>{const p=gunPose({...e,gun:{ang:0}},o);return {x:p.mx,y:p.my};};\r\nexport function drawGun(g,e,o={}){\r\n const G=e.gun,p=gunPose(e,o),s=p.s,{body,tube}=gunArt(!!o.flip),rc=GUN.recoilPx*G.recoil;\r\n g.save();g.translate(Math.round(-Math.sin(p.a)*rc),Math.round(-p.d*Math.cos(p.a)*rc)); // kicks back opposite the shot, along the barrel axis\r\n const sh=sheet('gun');\r\n if(sh){const cell=G.flash>0?5:G.recoil>.5?6:G.phase==='aim'&&Math.floor(G.t*16)%2===0?7:Math.max(0,Math.min(4,Math.round(p.a/.61*2)+2)),z=48*s;\r\n  g.save();g.translate(Math.round(p.px),Math.round(p.py));if(o.flip)g.scale(1,-1);g.drawImage(sh,cell*64,0,64,64,-z/2,-z+11*s,z,z);g.restore();}\r\n else{\r\n  g.save();g.translate(Math.round(p.px),Math.round(p.py));if(o.flip)g.scale(1,-1);g.rotate(p.a);g.drawImage(tube,-4*s,-TUBE*s,8*s,22*s);g.restore(); // tube first: the receiver hides its root\r\n  g.drawImage(body,Math.round(p.ox),Math.round(p.oy),48*s,24*s);\r\n }\r\n const led=G.flash>0?'#fff':G.phase==='aim'&&Math.floor(G.t*16)%2===0?GUN.brand.led:null; // 8 Hz amber blink while aiming, solid white on fire\r\n const mx=Math.round(p.mx),my=Math.round(p.my),ly=my-p.d*s*2;\r\n g.fillStyle=led||'#2a1a00';g.fillRect(mx-s,ly,s*2,s*2);\r\n if(led===GUN.brand.led){g.globalAlpha=.55;g.fillStyle=led;for(const[dx,dy]of[[-3,0],[3,0],[0,-3],[0,3]])g.fillRect(mx-s+dx*s,ly+dy*s,s*2,s*2);g.globalAlpha=1;} // glow ring so the cue reads on the camera feed\r\n if(G.flash>0){ // Vlambeer: the first frame is a circle, then a star, then a smoke pixel (~90 ms total)\r\n  const age=GUN.flashS-G.flash,f=age<GUN.flashS/3?0:age<GUN.flashS*2/3?1:2,fy=my+p.d*s*2;\r\n  if(f===0){g.fillStyle='#fff';for(let y=-5;y<=5;y++)for(let x=-5;x<=5;x++)if(x*x+y*y<=25)g.fillRect(mx+x*s,fy+y*s,s,s);}\r\n  else if(f===1){g.fillStyle=GUN.brand.led;for(let i=-3;i<=3;i++){g.fillRect(mx+i*s,fy,s,s);g.fillRect(mx,fy+i*s,s,s);}}\r\n  else{g.fillStyle='#8a8a92';g.fillRect(mx,fy+p.d*s*2,s*3,s*3);}\r\n }\r\n g.restore();\r\n}\r\nfunction drawFx(g,e){\r\n for(const q of e.fx){\r\n  if(q.d>0)continue;const k=q.life/q.max;\r\n  if(q.kind==='flash'){const r=Math.round(q.r*(k>.5?1:.66));g.fillStyle=k>.5?q.c:q.c2;const st=r>20?3:2;for(let y=-r;y<=r;y+=st)for(let x=-r;x<=r;x+=st)if(x*x+y*y<=r*r)g.fillRect(Math.round(q.x+x),Math.round(q.y+y),st,st);}\r\n  else if(q.kind==='ring'){const R=q.r0+(q.r1-q.r0)*(1-k),n=Math.max(12,Math.ceil(TAU*R/3));g.globalAlpha=.8*k;g.fillStyle=q.c;for(let i=0;i<n;i++){const a=i/n*TAU;g.fillRect(Math.round(q.x+Math.cos(a)*R),Math.round(q.y+Math.sin(a)*R),3,3);}g.globalAlpha=1;}\r\n  else if(q.kind==='smoke'){g.globalAlpha=.6*k;g.fillStyle='#2a2a2e';g.fillRect(Math.round(q.x),Math.round(q.y),3,3);g.globalAlpha=1;}\r\n  else if(q.kind==='chunk'){g.save();g.globalAlpha=Math.min(1,k*2);g.translate(Math.round(q.x),Math.round(q.y));g.rotate(Math.round(q.rot/(Math.PI/4))*Math.PI/4);g.fillStyle=q.c;g.fillRect(-1,-3,2,5);g.restore();g.globalAlpha=1;}\r\n }\r\n}\r\n// coach(g,e) lets a host draw its own thrower (rest hangs the gun from the top); default = the gun at bottom centre.\r\nexport function render(g,e,{coach=null,pointerTrail=true,hud=true}={}){\r\n g.imageSmoothingEnabled=false;g.clearRect(0,0,e.w,e.h);\r\n g.save();if(e.shake>0)g.translate(Math.round((Math.random()-.5)*e.shake*GUN.shakePx),Math.round((Math.random()-.5)*e.shake*GUN.shakePx));\r\n for(const s of e.stains){g.globalAlpha=Math.min(.7,s.life/4);g.fillStyle=s.c;for(const[x,y,z]of s.dots)g.fillRect(Math.round(x),Math.round(y),z*2,z*2);}\r\n g.globalAlpha=1;\r\n if(coach)coach(g,e);else drawGun(g,e);\r\n for(const o of e.objects){if(o.hold)continue;\r\n  if(o.trail.length){g.fillStyle=TYPES[o.type][1];o.trail.forEach((t,i)=>{g.globalAlpha=.25+.2*i;g.fillRect(Math.round(t.x)-1,Math.round(t.y)-1,3,3);});g.globalAlpha=1;} // slow bright barrels leave a short trail\r\n  g.save();g.translate(Math.round(o.x),Math.round(o.y));drawBody(g,o);g.restore();}\r\n for(const q of e.halves){g.save();g.globalAlpha=Math.min(1,q.life/HALF_LIFE);g.translate(Math.round(q.x),Math.round(q.y));g.rotate(q.hr);g.rotate(q.cut);g.beginPath();g.rect(-q.r*2,q.side>0?0:-q.r*2,q.r*4,q.r*2);g.clip();g.rotate(-q.cut);drawBody(g,{...q,rot:q.rot0});g.restore();}\r\n for(const q of e.parts){g.globalAlpha=Math.min(1,q.life*2);g.fillStyle=q.c;g.fillRect(Math.round(q.x),Math.round(q.y),q.s,q.s);}\r\n g.globalAlpha=1;drawFx(g,e);\r\n if(pointerTrail&&e.trail.length>1){g.lineWidth=2;for(let i=1;i<e.trail.length;i++){const a=e.trail[i-1],b=e.trail[i];g.strokeStyle=`rgba(255,255,255,${Math.min(1,b.life*4)})`;g.beginPath();g.moveTo(a.x,a.y);g.lineTo(b.x,b.y);g.stroke();}}\r\n const L=e.laser;\r\n if(L.phase!=='idle'){\r\n  const m=gunMuzzle(e),gx=m.x,gy=m.y;\r\n  if(L.phase==='aim'){g.strokeStyle=(Math.floor(L.t*12)%2)?'#ff2a2a':'#ff8080';g.lineWidth=1;g.setLineDash([3,3]);g.beginPath();g.moveTo(gx,gy);g.lineTo(L.tx,L.ty);g.stroke();g.setLineDash([]);}\r\n  else if(L.result==='hit'){g.strokeStyle='#f22';g.lineWidth=3;g.beginPath();g.moveTo(gx,gy);g.lineTo(L.tx,L.ty);g.stroke();}\r\n  else{g.strokeStyle='#f66';g.lineWidth=2;g.beginPath();g.moveTo(gx,gy);g.lineTo(L.tx,L.ty+14);g.stroke();\r\n   g.fillStyle='rgba(120,200,255,.55)';for(let y=-14;y<=14;y+=4)for(let x=-12;x<=12;x+=4)if(x*x/144+y*y/196<=1)g.fillRect(L.tx+x,L.ty+y,3,3);} // pixel shield\r\n }\r\n g.restore();\r\n if(L.flash>0){g.fillStyle=`rgba(255,0,0,${L.flash})`;g.fillRect(0,0,e.w,e.h);}\r\n g.textAlign='center';\r\n for(const p of e.pops){g.globalAlpha=Math.min(1,p.life*2);g.font=`bold ${p.big?14:9}px monospace`;g.fillStyle='#000';g.fillText(p.text,Math.round(p.x)+1,Math.round(p.y)+1);g.fillStyle=p.c;g.fillText(p.text,Math.round(p.x),Math.round(p.y));}\r\n g.globalAlpha=1;\r\n if(!hud)return;\r\n if(e.mode==='martial'){g.font='bold 12px monospace';g.textAlign='right';g.fillStyle='#f33';g.fillText('X'.repeat(e.strikes)+'.'.repeat(3-e.strikes),e.w-6,14);}\r\n g.textAlign='left';g.font='bold 9px monospace';g.fillStyle='#fff';g.fillText(`${e.score}  W${e.wave}${e.xp?`  +${e.xp}XP`:''}`,6,14);\r\n}\r\n","arcade/slice/hand-glow.mjs":"// Camera-workout hand light trails. Pose landmarks still drive Fruit Ninja collisions unchanged; this\r\n// module only paints: One Euro wrists + damped prediction, a time-stamped ring buffer per hand, centripetal\r\n// Catmull-Rom, a tapered filled ribbon in three additive layers, speed-gated emission with hysteresis,\r\n// punch impacts (flash, ring, sparks) and alpha fades instead of hard deletes.\r\n// Coordinates: pose() takes points mapped to the overlay host as 0..1 fractions (workout-effects mapper);\r\n// renderHandGlow() scales them to the canvas logical size (CSS px).\r\nimport {createLandmarkBank,PRESETS} from '../one-euro.mjs';\r\n\r\n// Tuning knobs (real phones need tuning). Speeds are body-heights per second; widths are fractions of body height.\r\nexport const TRAIL_TUNING=Object.freeze({\r\n MAX_PTS:32, LIFE_MS:420, SUBDIV:4,\r\n GATE_ON:1.2, GATE_OFF:.7, EMIT_OFF_MS:150, SPEED_FULL:2.5,\r\n BASE_W:.022, HEAD_BOOST:1.3,\r\n LAYERS:[[2.6,.2],[1.3,.65],[.42,1]],  // [width x, alpha x]: wide soft, mid colour, white core\r\n PREDICT_MAX:.1,                     // max prediction lead, body heights\r\n VIS_FADE_MS:200, STALE_MS:400, MIN_HZ:4,\r\n BODY_M:1.7,                         // metres per body height for world-landmark speed\r\n PEAK_MIN:1.5, DROP:.6, HARD_DROP:.4, STRAIGHT_DEG:150, WINDOW_MS:150, COOLDOWN_MS:250,\r\n SPARKS:[8,14], SPARK_SPEED:[1.5,3], SPARK_LIFE:[250,400], SPARK_DRAG:3, SPARK_CONE:35*Math.PI/180,\r\n FLASH_MS:120, FLASH_R:.05, RING_MS:220, RING_R:.12,\r\n COST_DOWN_MS:3, COST_UP_MS:1.5, COST_FRAMES:8, RECOVER_MS:1500,\r\n});\r\n// Colour by aura level (myr5:aura-level 0..3): [wide, mid, core, tail second hue].\r\nexport const TRAIL_PALETTES=[\r\n ['#ff3d10','#ff8a2a','#fff6e0',null],\r\n ['#ff3d10','#ff8a2a','#fff6e0',null],\r\n ['#ff1f6a','#ff5ad0','#fff0fa',null],\r\n ['#1f6bff','#38e8ff','#f0ffff','#9a5cff'],\r\n];\r\nconst clamp=(n,a,b)=>Math.max(a,Math.min(b,n));\r\nconst smooth=(a,b,x)=>{const t=clamp((x-a)/(b-a),0,1);return t*t*(3-2*t);};\r\nconst SIDES=[['left',15,13,11,[17,19,21]],['right',16,14,12,[18,20,22]]];\r\nconst F=6; // floats per trail point: x, y, t, speedN, emit, spare\r\n\r\nfunction confidence(point){return Number.isFinite(point?.visibility)?point.visibility:Number.isFinite(point?.score)?point.score:0;}\r\nfunction handConfidence(points,wrist,shape){\r\n const wristConf=confidence(points[wrist]);if(wristConf<.4)return wristConf;\r\n let sum=0,n=0;for(const i of shape){const v=confidence(points[i]);if(v>=.5){sum+=v;n++;}}\r\n // A wrist is sufficient when the hand-specific keypoints are missing from a pose.\r\n return n?Math.min(wristConf,sum/n):wristConf;\r\n}\r\n/** Elbow angle in degrees (180 = straight). World landmarks (metres, 3D) when present, else image space. */\r\nexport function elbowAngle(a,b,c,ar=1){\r\n if(!a||!b||!c)return 0;\r\n const ux=(a.x-b.x)*ar,uy=a.y-b.y,uz=(a.z??0)-(b.z??0),vx=(c.x-b.x)*ar,vy=c.y-b.y,vz=(c.z??0)-(b.z??0);\r\n const l=Math.hypot(ux,uy,uz)*Math.hypot(vx,vy,vz);if(!l)return 0;\r\n return Math.acos(clamp((ux*vx+uy*vy+uz*vz)/l,-1,1))*180/Math.PI;\r\n}\r\n/** Body height in host heights from mapped shoulders/hips (aspect = host w/h). Fallback: shoulder width. */\r\nexport function bodyHeight(m,aspect){\r\n const ok=p=>p&&Number.isFinite(p.x)&&Number.isFinite(p.y)&&(p.visibility??1)>=.5;\r\n const [ls,rs,lh,rh]=[m(11),m(12),m(23),m(24)];\r\n if(ok(ls)&&ok(rs)&&ok(lh)&&ok(rh))return Math.hypot((ls.x+rs.x-lh.x-rh.x)/2*aspect,(ls.y+rs.y-lh.y-rh.y)/2)/.3;\r\n if(ok(ls)&&ok(rs))return Math.hypot((ls.x-rs.x)*aspect,ls.y-rs.y)/.23;\r\n return 0;\r\n}\r\n\r\n/** Pure speed gate with hysteresis (exported for tests). */\r\nexport function gateStep(on,speed,T=TRAIL_TUNING){return on?speed>=T.GATE_OFF:speed>T.GATE_ON;}\r\n/** Ribbon half-width for a sample: taper by age, widen with speed (exported for tests). */\r\nexport function ribbonHalfWidth(age01,speedN,bodyPx,head=false,T=TRAIL_TUNING){\r\n return T.BASE_W*bodyPx*Math.pow(1-clamp(age01,0,1),1.3)*(.35+.65*clamp(speedN,0,1))*(head?T.HEAD_BOOST:1);\r\n}\r\n/** Ribbon alpha for a sample (exported for tests). */\r\nexport function ribbonAlpha(age01,speedN,emit){return Math.pow(1-clamp(age01,0,1),2)*smooth(.15,.5,speedN)*emit;}\r\n\r\n/**\r\n * Punch impact detector for one hand: a peak above PEAK_MIN, then the speed falls below DROP*peak within\r\n * WINDOW_MS with the elbow straight (jab/cross), or below HARD_DROP*peak with a bent elbow (hook). One per punch.\r\n */\r\nexport function createImpactDetector(T=TRAIL_TUNING){\r\n const s={armed:false,peak:0,peakAt:0,coolUntil:-Infinity};\r\n return {state:s,update(speed,elbowDeg,t){\r\n  if(s.armed&&t-s.peakAt>T.WINDOW_MS)s.armed=false;\r\n  if(speed>=T.PEAK_MIN&&(!s.armed||speed>s.peak)){s.armed=true;s.peak=speed;s.peakAt=t;return 0;}\r\n  if(!s.armed||t<s.coolUntil)return 0;\r\n  if(speed<=T.DROP*s.peak&&(elbowDeg>=T.STRAIGHT_DEG||speed<=T.HARD_DROP*s.peak)){\r\n   const peak=s.peak;s.armed=false;s.peak=0;s.coolUntil=t+T.COOLDOWN_MS;return peak;}\r\n  return 0;\r\n },reset(){s.armed=false;s.peak=0;}};\r\n}\r\n\r\nfunction makeHand(side,T){\r\n return {side,visible:false,alpha:0,x:0,y:0,hx:0,hy:0,speed:0,speedN:0,gate:false,emit:0,lastSeen:-Infinity,lastPoseAt:null,\r\n  wx:NaN,wy:NaN,wz:NaN,pts:new Float32Array(T.MAX_PTS*F),start:0,count:0,impact:createImpactDetector(T),dirX:0,dirY:0,confidence:0};\r\n}\r\n\r\nexport function createHandGlow({clock=()=>globalThis.performance?.now?.()??Date.now(),onImpact=()=>{},random=Math.random,tuning=TRAIL_TUNING}={}){\r\n const T=tuning,bank=createLandmarkBank(()=>PRESETS.wrist);\r\n const SP=new Float32Array(40*6); // spark pool: x, y, vx, vy, life, max (normalized host units)\r\n const IM=new Float32Array(4*6);  // impacts: x, y, t, strength, dirX, dirY\r\n const state={hands:[makeHand('left',T),makeHand('right',T)],bodyH:.6,aspect:9/16,level:0,lastPoseAt:null,poseHz:0,poseSamples:0,\r\n  enabled:true,tier:3,highCostFrames:0,lowCostSince:null,lastRenderAt:0,sparks:SP,impacts:IM,impactNext:0};\r\n function clearHand(h){h.visible=false;h.alpha=0;h.count=0;h.start=0;h.gate=false;h.emit=0;h.speed=0;h.lastPoseAt=null;h.wx=NaN;h.impact.reset();}\r\n function clear(){for(const h of state.hands)clearHand(h);for(let i=0;i<SP.length;i+=6)SP[i+4]=0;for(let i=0;i<IM.length;i+=6)IM[i+2]=-1e9;\r\n  bank.reset(15);bank.reset(16);state.lastPoseAt=null;state.poseHz=0;state.poseSamples=0;}\r\n clear();\r\n function push(h,x,y,t,sn,e){\r\n  if(h.count===T.MAX_PTS){h.start=(h.start+1)%T.MAX_PTS;h.count--;}\r\n  const o=((h.start+h.count)%T.MAX_PTS)*F;h.pts[o]=x;h.pts[o+1]=y;h.pts[o+2]=t;h.pts[o+3]=sn;h.pts[o+4]=e;h.count++;\r\n }\r\n function spawnImpact(h,x,y,t,peak){\r\n  const strength=clamp(peak/3,.35,1),o=state.impactNext*6;state.impactNext=(state.impactNext+1)%4;\r\n  IM[o]=x;IM[o+1]=y;IM[o+2]=t;IM[o+3]=strength;IM[o+4]=h.dirX;IM[o+5]=h.dirY;\r\n  const n=Math.round(T.SPARKS[0]+(T.SPARKS[1]-T.SPARKS[0])*strength),base=Math.atan2(h.dirY,h.dirX*state.aspect);\r\n  for(let k=0,i=0;k<n&&i<SP.length;i+=6){if(SP[i+4]>0)continue;k++;\r\n   const a=base+(random()*2-1)*T.SPARK_CONE,v=(T.SPARK_SPEED[0]+random()*(T.SPARK_SPEED[1]-T.SPARK_SPEED[0]))*state.bodyH,life=T.SPARK_LIFE[0]+random()*(T.SPARK_LIFE[1]-T.SPARK_LIFE[0]);\r\n   SP[i]=x;SP[i+1]=y;SP[i+2]=Math.cos(a)*v/state.aspect;SP[i+3]=Math.sin(a)*v;SP[i+4]=life;SP[i+5]=life;}\r\n  onImpact({x,y,hand:h.side,strength});\r\n }\r\n // points: raw landmarks; mapPoint(p) -> {x,y} host fractions; world: world landmarks (optional); aspect: host w/h.\r\n function pose(points,{now=clock(),mapPoint=p=>p,world=null,aspect=state.aspect,videoAspect=1}={}){\r\n  if(!Array.isArray(points)){for(const h of state.hands)h.visible=false;return;}\r\n  const t=Number.isFinite(now)?now:clock();\r\n  if(state.lastPoseAt!=null){const dt=t-state.lastPoseAt;if(dt>0){const hz=1000/dt;state.poseHz=dt>T.STALE_MS||!state.poseSamples?hz:state.poseHz*.7+hz*.3;state.poseSamples++;}}\r\n  state.lastPoseAt=t;state.aspect=aspect>0?aspect:state.aspect;\r\n  const m=i=>points[i]?mapPoint(points[i]):null,bh=bodyHeight(m,state.aspect);\r\n  if(bh>.05)state.bodyH=state.bodyH*.8+clamp(bh,.15,1.6)*.2;\r\n  for(let s=0;s<2;s++){\r\n   const [,wrist,elbow,shoulder,shape]=SIDES[s],h=state.hands[s],conf=handConfidence(points,wrist,shape),mp=points[wrist]?m(wrist):null;\r\n   if(!mp||!Number.isFinite(mp.x)||!Number.isFinite(mp.y)){h.visible=false;continue;}\r\n   const f=bank.update(wrist,mp.x,mp.y,conf,t);h.visible=f.visible;h.confidence=conf;\r\n   if(!h.visible)continue;\r\n   const dt=h.lastPoseAt==null?0:(t-h.lastPoseAt)/1000;\r\n   let raw=0;\r\n   if(dt>0&&dt<.3){\r\n    const dx=f.x-h.x,dy=f.y-h.y;raw=Math.hypot(dx*state.aspect,dy)/dt/state.bodyH;\r\n    if(raw>.3){const l=Math.hypot(dx*state.aspect,dy);h.dirX=dx/l;h.dirY=dy/l;}\r\n    const w=world?.[wrist];\r\n    if(w&&Number.isFinite(h.wx))raw=Math.max(raw,Math.hypot(w.x-h.wx,w.y-h.wy,(w.z??0)-h.wz)/dt/T.BODY_M);\r\n   }\r\n   const w=world?.[wrist];if(w){h.wx=w.x;h.wy=w.y;h.wz=w.z??0;}\r\n   if(h.lastPoseAt==null||t-h.lastSeen>T.STALE_MS){h.count=0;h.start=0;h.speed=0;}\r\n   h.speed=dt>0?h.speed*.4+raw*.6:0;h.speedN=clamp(h.speed/T.SPEED_FULL,0,1);\r\n   const was=h.gate;h.gate=gateStep(h.gate,h.gate?h.speed:Math.max(raw,h.speed),T); // open on the raw spike, close on the smoothed speed\r\n   if(h.gate&&!was)for(let i=Math.max(0,h.count-2);i<h.count;i++)h.pts[((h.start+i)%T.MAX_PTS)*F+4]=1; // light the wind-up so the ribbon has length at once\r\n   h.emit=h.gate?1:Math.max(0,h.emit-(dt*1000)/T.EMIT_OFF_MS);\r\n   h.x=f.x;h.y=f.y;h.lastSeen=t;h.lastPoseAt=t;\r\n   if(state.enabled)push(h,f.x,f.y,t,h.speedN,h.emit);\r\n   if(dt>0){\r\n    const ang=world?.[shoulder]&&world?.[elbow]&&world?.[wrist]?elbowAngle(world[shoulder],world[elbow],world[wrist]):elbowAngle(points[shoulder],points[elbow],points[wrist],videoAspect);\r\n    const peak=h.impact.update(raw,ang,t);if(peak)spawnImpact(h,f.x,f.y,t,peak);\r\n   }\r\n  }\r\n }\r\n function step(dtSeconds,now=clock()){\r\n  const t=Number.isFinite(now)?now:clock(),dt=clamp(Number(dtSeconds)||0,0,.05);\r\n  for(let s=0;s<2;s++){\r\n   const h=state.hands[s];if(t-h.lastSeen>T.STALE_MS)h.visible=false;\r\n   h.alpha=clamp(h.alpha+(h.visible?1:-1)*dt*1000/T.VIS_FADE_MS,0,1);\r\n   while(h.count&&t-h.pts[h.start*F+2]>T.LIFE_MS){h.start=(h.start+1)%T.MAX_PTS;h.count--;}\r\n   // Live head: the filtered wrist pushed forward by the measured lag (capped), so the ribbon reaches the hand.\r\n   const p=h.lastPoseAt==null?null:bank.predict(SIDES[s][1],t-h.lastPoseAt);\r\n   if(p){let ox=(p.x-h.x)*state.aspect,oy=p.y-h.y;const l=Math.hypot(ox,oy),cap=T.PREDICT_MAX*state.bodyH;if(l>cap){ox*=cap/l;oy*=cap/l;}h.hx=h.x+ox/state.aspect;h.hy=h.y+oy;}\r\n   else{h.hx=h.x;h.hy=h.y;}\r\n  }\r\n  const k=Math.exp(-T.SPARK_DRAG*dt);\r\n  for(let i=0;i<SP.length;i+=6){if(SP[i+4]<=0)continue;SP[i]+=SP[i+2]*dt;SP[i+1]+=SP[i+3]*dt;SP[i+2]*=k;SP[i+3]*=k;SP[i+4]-=dt*1000;}\r\n  state.lastRenderAt=t;\r\n  return state;\r\n }\r\n // Quality ladder on measured render cost: 3 layers -> 2 -> 1 -> off, recovering after a calm stretch.\r\n function measure(costMs,now=clock()){\r\n  if(!Number.isFinite(costMs))return state.enabled;\r\n  const t=Number.isFinite(now)?now:clock();\r\n  if(costMs>T.COST_DOWN_MS){state.highCostFrames++;state.lowCostSince=null;if(state.highCostFrames>=T.COST_FRAMES){state.tier=Math.max(0,state.tier-1);state.highCostFrames=0;}}\r\n  else{state.highCostFrames=0;if(costMs<T.COST_UP_MS){if(state.lowCostSince==null)state.lowCostSince=t;if(state.tier<3&&t-state.lowCostSince>=T.RECOVER_MS){state.tier++;state.lowCostSince=t;}}else state.lowCostSince=null;}\r\n  state.enabled=state.tier>0;\r\n  return state.enabled;\r\n }\r\n function setLevel(level){state.level=clamp(Math.round(Number(level)||0),0,3);}\r\n return {state,pose,step,measure,clear,setLevel};\r\n}\r\n\r\n// ---- renderer (Canvas 2D, additive). Scratch buffers are module-level: no per-frame allocation. ----\r\nconst MAXS=(TRAIL_TUNING.MAX_PTS+1)*TRAIL_TUNING.SUBDIV+1;\r\nconst SX=new Float32Array(MAXS),SY=new Float32Array(MAXS),SA=new Float32Array(MAXS),SS=new Float32Array(MAXS),SE=new Float32Array(MAXS),NX=new Float32Array(MAXS),NY=new Float32Array(MAXS),HW=new Float32Array(MAXS),PT=new Float32Array((TRAIL_TUNING.MAX_PTS+1)*5);\r\nconst sprites=new Map();\r\nfunction glowSprite(colour){\r\n if(sprites.has(colour))return sprites.get(colour);\r\n let c=null;\r\n try{c=typeof OffscreenCanvas==='function'?new OffscreenCanvas(64,64):globalThis.document?.createElement?.('canvas');if(c){c.width=c.height=64;const g=c.getContext('2d');const r=g.createRadialGradient(32,32,0,32,32,32);\r\n  r.addColorStop(0,colour);r.addColorStop(.25,colour);r.addColorStop(1,'rgba(0,0,0,0)');g.fillStyle=r;g.fillRect(0,0,64,64);}}catch{c=null;}\r\n sprites.set(colour,c);return c;\r\n}\r\n// Centripetal Catmull-Rom (alpha .5) between p1 and p2; writes SUBDIV samples starting at index n.\r\nfunction catmull(n,i0,i1,i2,i3,sub){\r\n const x0=PT[i0*5],y0=PT[i0*5+1],x1=PT[i1*5],y1=PT[i1*5+1],x2=PT[i2*5],y2=PT[i2*5+1],x3=PT[i3*5],y3=PT[i3*5+1];\r\n const t1=Math.sqrt(Math.hypot(x1-x0,y1-y0))||1e-4,t2=t1+(Math.sqrt(Math.hypot(x2-x1,y2-y1))||1e-4),t3=t2+(Math.sqrt(Math.hypot(x3-x2,y3-y2))||1e-4);\r\n for(let k=0;k<sub;k++){\r\n  const u=k/sub,t=t1+(t2-t1)*u;\r\n  const a1x=(t1-t)/t1*x0+t/t1*x1,a1y=(t1-t)/t1*y0+t/t1*y1;\r\n  const a2x=(t2-t)/(t2-t1)*x1+(t-t1)/(t2-t1)*x2,a2y=(t2-t)/(t2-t1)*y1+(t-t1)/(t2-t1)*y2;\r\n  const a3x=(t3-t)/(t3-t2)*x2+(t-t2)/(t3-t2)*x3,a3y=(t3-t)/(t3-t2)*y2+(t-t2)/(t3-t2)*y3;\r\n  const b1x=(t2-t)/t2*a1x+t/t2*a2x,b1y=(t2-t)/t2*a1y+t/t2*a2y,b2x=(t3-t)/(t3-t1)*a2x+(t-t1)/(t3-t1)*a3x,b2y=(t3-t)/(t3-t1)*a2y+(t-t1)/(t3-t1)*a3y;\r\n  SX[n+k]=(t2-t)/(t2-t1)*b1x+(t-t1)/(t2-t1)*b2x;SY[n+k]=(t2-t)/(t2-t1)*b1y+(t-t1)/(t2-t1)*b2y;\r\n  SA[n+k]=PT[i1*5+2]+(PT[i2*5+2]-PT[i1*5+2])*u;SS[n+k]=PT[i1*5+3]+(PT[i2*5+3]-PT[i1*5+3])*u;SE[n+k]=PT[i1*5+4]+(PT[i2*5+4]-PT[i1*5+4])*u;\r\n }\r\n}\r\n/** Builds the ribbon samples for a hand into the scratch buffers; returns the sample count. Exported for tests. */\r\nexport function buildRibbon(hand,now,w,h,bodyPx,T=TRAIL_TUNING){\r\n let n=0;\r\n for(let i=0;i<hand.count;i++){const o=((hand.start+i)%T.MAX_PTS)*F,a=(now-hand.pts[o+2])/T.LIFE_MS;\r\n  PT[n*5]=hand.pts[o]*w;PT[n*5+1]=hand.pts[o+1]*h;PT[n*5+2]=clamp(a,0,1);PT[n*5+3]=hand.pts[o+3];PT[n*5+4]=hand.pts[o+4];n++;}\r\n PT[n*5]=hand.hx*w;PT[n*5+1]=hand.hy*h;PT[n*5+2]=0;PT[n*5+3]=hand.speedN;PT[n*5+4]=hand.emit;n++;\r\n if(n<2)return 0;\r\n let m=0;\r\n for(let i=0;i<n-1;i++){catmull(m,Math.max(0,i-1),i,i+1,Math.min(n-1,i+2),T.SUBDIV);m+=T.SUBDIV;}\r\n SX[m]=PT[(n-1)*5];SY[m]=PT[(n-1)*5+1];SA[m]=0;SS[m]=PT[(n-1)*5+3];SE[m]=PT[(n-1)*5+4];m++;\r\n for(let i=0;i<m;i++){\r\n  const a=Math.max(0,i-1),b=Math.min(m-1,i+1);let tx=SX[b]-SX[a],ty=SY[b]-SY[a];const l=Math.hypot(tx,ty)||1;tx/=l;ty/=l;\r\n  NX[i]=-ty;NY[i]=tx;HW[i]=ribbonHalfWidth(SA[i],SS[i],bodyPx,i===m-1,T);\r\n }\r\n return m;\r\n}\r\nexport const ribbonSamples={SX,SY,SA,SS,SE,HW};\r\n\r\nexport function renderHandGlow(ctx,state,{w=180,h=320,now=state?.lastRenderAt??0}={}){\r\n if(!ctx||!state)return;\r\n const T=TRAIL_TUNING,pal=TRAIL_PALETTES[state.level]||TRAIL_PALETTES[0],bodyPx=state.bodyH*h;\r\n const slow=state.poseSamples>=2&&state.poseHz<T.MIN_HZ;\r\n ctx.save();ctx.globalCompositeOperation='lighter';\r\n if(state.enabled&&!slow)for(const hand of state.hands){\r\n  if(hand.alpha<=.01)continue;\r\n  const m=buildRibbon(hand,now,w,h,bodyPx,T),layers=state.tier>=3?3:state.tier;\r\n  for(let L=3-layers;L<3&&m>1;L++){\r\n   const [wx,ax]=T.LAYERS[L];\r\n   for(let i=0;i<m-1;i++){\r\n    const age=SA[i+1],a=ax*hand.alpha*ribbonAlpha(age,SS[i+1],SE[i+1])*(.7+.3*Math.sin(age*14-now*.009+L)*Math.sin(age*5.3+now*.004));\r\n    if(a<.004)continue;\r\n    ctx.fillStyle=L===2?pal[2]:pal[3]&&age>.5&&L===1?pal[3]:pal[L];ctx.globalAlpha=Math.min(1,a);\r\n    const h0=HW[i]*wx,h1=HW[i+1]*wx;\r\n    ctx.beginPath();ctx.moveTo(SX[i]+NX[i]*h0,SY[i]+NY[i]*h0);ctx.lineTo(SX[i+1]+NX[i+1]*h1,SY[i+1]+NY[i+1]*h1);\r\n    ctx.lineTo(SX[i+1]-NX[i+1]*h1,SY[i+1]-NY[i+1]*h1);ctx.lineTo(SX[i]-NX[i]*h0,SY[i]-NY[i]*h0);ctx.closePath();ctx.fill();\r\n   }\r\n  }\r\n  // Hand glow: always a soft light on the wrist, stretched along the motion and hotter with speed.\r\n  const hx=hand.hx*w,hy=hand.hy*h,r=bodyPx*(.028+.025*hand.speedN),stretch=1+Math.min(2,hand.speedN*2.2*hand.emit);\r\n  const spr=glowSprite(pal[1]),core=glowSprite(pal[2]);\r\n  ctx.globalAlpha=hand.alpha*(.22+.5*hand.speedN*hand.emit);\r\n  if(spr){ctx.save();ctx.translate(hx,hy);ctx.rotate(Math.atan2(hand.dirY,hand.dirX*w/h));ctx.drawImage(spr,r-2*r*stretch,-r,r*2*stretch,r*2);\r\n   ctx.globalAlpha*=.8;ctx.drawImage(core,r*.45-r*.9*stretch,-r*.45,r*.9*stretch,r*.9);ctx.restore();}\r\n  else{ctx.fillStyle=pal[1];ctx.fillRect(hx-r,hy-r,r*2,r*2);}\r\n }\r\n drawImpacts(ctx,state,w,h,now,pal);\r\n ctx.restore();\r\n}\r\nfunction drawImpacts(ctx,state,w,h,now,pal){\r\n const T=TRAIL_TUNING,IM=state.impacts,SP=state.sparks,bodyPx=state.bodyH*h,flash=glowSprite('#ffffff'),tint=glowSprite(pal[1]);\r\n for(let i=0;i<IM.length;i+=6){\r\n  const age=now-IM[i+2];if(age<0||age>T.RING_MS)continue;\r\n  const x=IM[i]*w,y=IM[i+1]*h,s=IM[i+3];\r\n  if(age<T.FLASH_MS){const k=age/T.FLASH_MS,sc=(k<.35?k/.35:1-(k-.35)/.65)*T.FLASH_R*bodyPx*(.6+.4*s);\r\n   if(flash){ctx.globalAlpha=.8;ctx.drawImage(tint,x-sc*2,y-sc*2,sc*4,sc*4);ctx.drawImage(flash,x-sc,y-sc,sc*2,sc*2);}}\r\n  const k=age/T.RING_MS,R=T.RING_R*bodyPx*(.6+.4*s)*Math.sqrt(k);\r\n  ctx.globalAlpha=(1-k)*.9;ctx.strokeStyle=pal[2];ctx.lineWidth=Math.max(1.5,bodyPx*.012*(1-k));ctx.beginPath();ctx.arc(x,y,R,0,Math.PI*2);ctx.stroke();\r\n  ctx.globalAlpha=(1-k)*.5;ctx.strokeStyle=pal[1];ctx.lineWidth=Math.max(3,bodyPx*.03*(1-k));ctx.stroke();\r\n }\r\n ctx.lineCap='round';\r\n for(let i=0;i<SP.length;i+=6){\r\n  if(SP[i+4]<=0)continue;const k=SP[i+4]/SP[i+5],x=SP[i]*w,y=SP[i+1]*h,vx=SP[i+2]*w,vy=SP[i+3]*h;\r\n  ctx.globalAlpha=k;ctx.strokeStyle=k>.5?pal[2]:pal[1];ctx.lineWidth=Math.max(1,bodyPx*.008);\r\n  ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(x-vx*.03,y-vy*.03);ctx.stroke();\r\n }\r\n ctx.lineCap='butt';\r\n}\r\n","arcade/slice/rest-game.mjs":"// Fruit Ninja rest mode: the MOM Inc. gun (top) fires barrels at the user's character; swipe slices, tap explodes,\r\n// tilt (or arrow keys) dodges, holding still shields from the laser. Lazy-loaded by pod.mjs; engine = ./core.mjs.\r\nimport {createSlice,render,drawGun,gunMuzzle} from './core.mjs';\r\nimport {createDodger,createSwipe,throwPlan,REST} from './rest-logic.mjs';\r\nimport {subscribePhoneMotion,requestPhoneMotion} from '../../character-phone-sensor.mjs';\r\nimport {performanceOwner} from '../../performance-progress.mjs';\r\nimport {createQuestProgressReporter} from '../quest-progress-reporter.mjs';\r\n\r\nconst W=180,GRAV=260;\r\nexport function mountRestGame(scene,{avatar,attackButton,feedback,sound=()=>{}}){\r\n const canvas=document.createElement('canvas'),g=canvas.getContext('2d');\r\n canvas.className='slice-layer';canvas.setAttribute('aria-hidden','true');\r\n let e,dodger,plan=null,frame=0,last=0,nextThrow=0,nextLaser=0,throws=0,swallow=false,unsub=()=>{},k=1,runOwner=null,reportQuest=null;\r\n const phone={gx:0,motion:0},pointer={down:false,x:0,y:0,speed:0};\r\n const swipe=createSwipe(),say=t=>{if(feedback)feedback.textContent=t;};\r\n // hudY: the rest HUD (.boss-status) overlays the top of the encounter, so the gun hangs just below it (measured in begin()).\r\n let hudY=0;\r\n const floorY=()=>e.h*.8,gunTop=()=>hudY+e.h*.04,coachY=()=>gunTop()+e.h*.13,gunAt=()=>({x:e.coachX,y:gunTop(),flip:true});\r\n const toLogical=ev=>{const r=canvas.getBoundingClientRect();return {x:(ev.clientX-r.left)/r.width*e.w,y:(ev.clientY-r.top)/r.height*e.h};};\r\n function begin(){\r\n  const b=scene.getBoundingClientRect();if(!(b.width>0&&b.height>0))throw new Error('Rest target canvas host has no visible size');k=b.width/W;const h=Math.round(W*b.height/b.width);\r\n  canvas.width=W;canvas.height=h;\r\n  const hud=scene.querySelector('.boss-status')?.getBoundingClientRect();hudY=hud&&hud.height>0?Math.max(0,(hud.bottom-b.top)/k):0;\r\n  e=createSlice({w:W,h,mode:'rest',auto:false,coachX:W/2,onEvent:(type,d)=>{\r\n   if(type==='hit')sound(d.kind==='slice'?'barrel-split':d.type==='barrel-fuel'||d.type==='barrel-toxic'?'blast-big':'blast');\r\n   else if(type==='shot')sound('gun-shot');else if(type==='gun-aim')sound('gun-arm');else if(type==='chain')sound('blast');\r\n   if(type==='laser-hit')say('Zapped! Hold still to shield next time.');\r\n   if(type==='laser-shield')say('Shield up. Nice and steady.');\r\n   if(type==='combo'){say(`COMBO x${d.n}!`);reportQuest?.record('fruit-streak',d.n);}\r\n   if(type==='wave-end')say('Wave over. Keep going, your rest continues.');\r\n  }});\r\n  dodger=createDodger(W,14);plan=null;throws=0;nextThrow=REST.firstThrowDelay;nextLaser=REST.laserEvery[0]/1000;\r\n }\r\n function throwRock(){\r\n  const p=plan||throwPlan(e.wave,e.coachX,dodger.x),y0=coachY(),vy=40,D=floorY()-y0,T=(-vy+Math.sqrt(vy*vy+2*GRAV*D))/GRAV;plan=null;\r\n  for(let i=0;i<p.count;i++)e.spawn({x:p.x+i*6,y:y0,vx:(p.aimX+(i?(i%2?1:-1)*22*i:0)-p.x)/T,vy,r:10+Math.random()*5,wobble:REST.wobble,T});\r\n  e.gunFire();\r\n  if(++throws%8===0)e.wave++;\r\n  nextThrow=e.t+T+p.gap/1000; // next volley only after this one has landed\r\n }\r\n // Core draws its laser from the bottom; rest draws the beam from the coach at the top instead.\r\n function laserDraw(){\r\n  const L=e.laser;if(L.phase==='idle')return;const m=gunMuzzle(e,gunAt()),x0=m.x,y0=m.y;\r\n  if(L.phase==='aim'){g.strokeStyle=(Math.floor(L.t*12)%2)?'#ff2a2a':'#ff8080';g.lineWidth=1;g.setLineDash([3,3]);g.beginPath();g.moveTo(x0,y0);g.lineTo(L.tx,L.ty);g.stroke();g.setLineDash([]);return;}\r\n  g.strokeStyle='#f22';g.lineWidth=L.result==='hit'?3:2;g.beginPath();g.moveTo(x0,y0);g.lineTo(L.tx,L.ty-(L.result==='shield'?14:0));g.stroke();\r\n  if(L.result==='shield'){g.fillStyle='rgba(120,200,255,.55)';for(let y=-14;y<=14;y+=4)for(let x=-12;x<=12;x+=4)if(x*x/144+y*y/196<=1)g.fillRect(L.tx+x,L.ty+y,3,3);}\r\n }\r\n function tick(now){\r\n  frame=requestAnimationFrame(tick);const dt=last?Math.min(.05,(now-last)/1000):1/60;last=now;\r\n  dodger.tilt(phone.gx);dodger.step(dt);\r\n  if(avatar)avatar.style.transform=`translateX(${(dodger.x-W/2)*k}px)`;\r\n  if(e.cool<=0){ // aim cue REST.aim seconds before each throw: the tube swings to where the barrel will land\r\n   if(!plan&&e.t>=nextThrow-REST.aim){plan=throwPlan(e.wave,e.coachX,dodger.x);e.gunAim(plan.aimX);}\r\n   if(e.t>=nextThrow)throwRock();}\r\n  if(e.t>=nextLaser&&e.laser.phase==='idle'&&e.cool<=0){e.laserStart(dodger.x,floorY(),REST.laserTelegraph/1000);nextLaser=e.t+(REST.laserEvery[0]+Math.random()*(REST.laserEvery[1]-REST.laserEvery[0]))/1000;}\r\n  // Motion fed to the shield check = max(phone motion, finger motion) in screen heights/s.\r\n  if(e.laser.phase==='aim')e.laser.tx=dodger.x;\r\n  e.feedMotion(Math.max(phone.motion,pointer.down?pointer.speed:0));pointer.speed*=.9;\r\n  e.step(dt);\r\n  for(const o of e.objects)if(!o.hold&&!o.bonus&&o.vy>0&&Math.abs(o.x-dodger.x)<o.r+10&&Math.abs(o.y-(floorY()-6))<o.r+14){ // caught by the character: soft feedback, no strike\r\n   o.alive=false;e.laser.flash=.12;say('Ouch. Dodge with a tilt.');window.myr5Creature?.play('rest');}\r\n  g.clearRect(0,0,e.w,e.h);\r\n  render(g,{...e,laser:{...e.laser,phase:'idle'}},{coach:c=>drawGun(c,e,gunAt())});laserDraw();\r\n }\r\n const down=ev=>{if(ev.target.closest?.('button:not(#attackCoach),a,select,input'))return;try{void requestPhoneMotion();}catch{}pointer.down=true;swallow=false;const p=toLogical(ev);pointer.x=p.x;pointer.y=p.y;swipe.down(p);};\r\n const move=ev=>{if(!pointer.down)return;const p=toLogical(ev),seg=swipe.move(p);if(!seg)return;\r\n  pointer.speed=Math.hypot(p.x-pointer.x,p.y-pointer.y)/e.h*30;pointer.x=p.x;pointer.y=p.y;e.addTrail(p.x,p.y);\r\n  if(e.hitSegment(seg[0],seg[1],{kind:'slice'}))swallow=true;};\r\n const up=()=>{if(!pointer.down)return;pointer.down=false;const tap=swipe.up();if(tap&&e.hitSegment(tap,tap,{kind:'explode'}))swallow=true;};\r\n const click=ev=>{if(swallow||swipe.moved){ev.stopImmediatePropagation();ev.preventDefault();swallow=false;}}; // slicing must not also punch the coach\r\n const keys=ev=>{const d=ev.key==='ArrowLeft'?-1:ev.key==='ArrowRight'?1:0;if(d)dodger.key(ev.type==='keydown'?d:0);};\r\n return {\r\n  start(){\r\n   if(frame)return;runOwner=performanceOwner();reportQuest=createQuestProgressReporter(runOwner);begin();scene.append(canvas);last=0;frame=requestAnimationFrame(tick);\r\n   scene.addEventListener('pointerdown',down,{passive:true});scene.addEventListener('pointermove',move,{passive:true});\r\n   addEventListener('pointerup',up);addEventListener('pointercancel',up);attackButton?.addEventListener('click',click,true);\r\n   addEventListener('keydown',keys);addEventListener('keyup',keys);\r\n   let pg=0;unsub=subscribePhoneMotion(s=>{phone.motion=Math.abs(s.shake||0)/20+Math.abs(s.angularSpeed||0)/3+Math.abs(s.gx-pg)*3;pg=s.gx;phone.gx=s.gx;});\r\n  },\r\n  stop(){\r\n   cancelAnimationFrame(frame);frame=0;canvas.remove();unsub();unsub=()=>{};if(avatar)avatar.style.transform='';\r\n   scene.removeEventListener('pointerdown',down);scene.removeEventListener('pointermove',move);\r\n   removeEventListener('pointerup',up);removeEventListener('pointercancel',up);attackButton?.removeEventListener('click',click,true);\r\n   removeEventListener('keydown',keys);removeEventListener('keyup',keys);\r\n  },\r\n  get engine(){return e;},\r\n };\r\n}\r\n","arcade/slice/rest-logic.mjs":"// Rest-mode (touch + tilt) pieces of Fruit Ninja rest. Pure logic, no DOM: the shared engine is arcade/slice/core.mjs.\r\nexport const REST={dodgeSpeed:420,tiltGain:2.2,stillMotion:.9,laserTelegraph:1000,laserEvery:[9000,14000],throwEvery:[1400,700],strikesMax:3,firstThrowDelay:0,wobble:5,aim:.45};\r\n\r\n// Dodger: the user's character slides along the floor. gx (screen-right gravity, -1..1) from the phone, keys / drag as fallback.\r\nexport function createDodger(width,margin=40){\r\n let x=width/2,target=x,key=0;\r\n const clamp=v=>Math.min(width-margin,Math.max(margin,v));\r\n return {\r\n  get x(){return x;},\r\n  resize(w){const f=x/width;width=w;x=clamp(f*w);target=x;},\r\n  tilt(gx){target=clamp(width/2+Math.max(-1,Math.min(1,gx*REST.tiltGain))*(width/2-margin));},\r\n  drag(px){target=clamp(px);},\r\n  key(dir){key=dir;},\r\n  step(dt){if(key)target=clamp(target+key*REST.dodgeSpeed*dt);const d=target-x,m=REST.dodgeSpeed*1.6*dt;x=clamp(x+Math.max(-m,Math.min(m,d)));return x;},\r\n };\r\n}\r\n\r\n// Thrower: coach at the top aims where the character is now, so dodging matters. Waves get faster with wave number.\r\nexport function throwPlan(wave,coachX,dodgerX,rand=Math.random){\r\n const [a,b]=REST.throwEvery,gap=Math.max(b,a-wave*90),spread=(rand()-.5)*80;\r\n return {gap,x:coachX,aimX:dodgerX+spread,count:Math.min(3,1+Math.floor(wave/3))}; // volley of 1-3, never a stream\r\n}\r\n\r\n// Swipe: pointer path -> segments to push through core.hitSegment; a path that never moves far is a tap.\r\nexport function createSwipe(tapRadius=10){\r\n let last=null,start=null,far=false;\r\n return {\r\n  down(p){last=start=p;far=false;return null;},\r\n  move(p){if(!last)return null;const seg=[last,p];last=p;if(Math.hypot(p.x-start.x,p.y-start.y)>tapRadius)far=true;return seg;},\r\n  up(){const tap=last&&!far?start:null;last=start=null;return tap;},\r\n  get moved(){return far;},\r\n };\r\n}\r\n","arcade/tub-flight/engine.mjs":"export const WIDTH=320,HEIGHT=420,TARGET=8;\r\n// endless ramp: every 3 pipes the gap narrows 2px (half-gap) and speed rises 4px/s, capped at level 8 (half 59, speed 140) so it stays phone-playable\r\nexport const level=score=>Math.min(8,Math.floor(score/3)),gapHalf=score=>75-2*level(score),speed=score=>108+4*level(score);\r\nexport class TubFlight {\r\n constructor(random=Math.random){this.random=random;this.reset();}\r\n reset(){this.phase='ready';this.y=210;this.velocity=0;this.pipes=[];this.score=0;this.elapsed=0;this.spawnIn=0;this.powder=0;this.lastDose=null;return this;}\r\n flap(){if(this.phase==='crashed')return false;if(this.phase==='ready'){this.phase='running';this.spawnIn=.8;}this.velocity=-245;return true;}\r\n spawn(){this.pipes.push({x:WIDTH+26,gap:122+this.random()*145,half:gapHalf(this.score),passed:false});}\r\n step(seconds){if(this.phase!=='running')return;let left=Math.min(.1,Math.max(0,seconds));while(left>0&&this.phase==='running'){const dt=Math.min(left,1/120);left-=dt;this.elapsed+=dt;this.velocity+=690*dt;this.y+=this.velocity*dt;this.spawnIn-=dt;if(this.spawnIn<=0){this.spawn();this.spawnIn=205/speed(this.score);}\r\n   if(this.y<20||this.y>HEIGHT-36){this.phase='crashed';break;}\r\n   for(const pipe of this.pipes){pipe.x-=speed(this.score)*dt;const h=pipe.half;const overlaps=82+13>pipe.x&&82-13<pipe.x+44;if(overlaps&&(this.y-13<pipe.gap-h||this.y+13>pipe.gap+h)){this.phase='crashed';break;}if(!pipe.passed&&pipe.x+44<82-13){pipe.passed=true;this.score++;this.powder=Math.min(this.score,TARGET);this.lastDose={x:pipe.x+22,y:pipe.gap-h,at:this.elapsed};}}\r\n   this.pipes=this.pipes.filter(pipe=>pipe.x>-60);\r\n  }\r\n }\r\n}\r\n","arcade/tub-flight/game.mjs":"import {gameShellPhase} from '../shell-phase.mjs';\r\nimport {TubFlight,WIDTH,HEIGHT,TARGET} from './engine.mjs';\r\nexport const FUEL_KEY='mbs-fuel-flight-v1',BEST_KEY='mbs-tub-best-v1';\r\nexport function bestScore(store=localStorage){try{const n=JSON.parse(store.getItem(BEST_KEY));return Number.isSafeInteger(n)&&n>0?n:0;}catch{return 0;}}\r\nexport function fuelUnlocked(store=localStorage){try{const value=JSON.parse(store.getItem(FUEL_KEY));return value?.version===1&&value.pipes===8&&Number.isSafeInteger(value.completedAt)&&value.completedAt>0;}catch{return false;}}\r\nexport function mountTubFlight(host,{onComplete=()=>{},title='TUB FLIGHT',autoFuel=false}={}){\r\n const shell=gameShellPhase('tub-flight',host);\r\n const game=new TubFlight();let frame=0,last=0,paused=false,ended=false,disposed=false,scored=false,toastTimer=0,best=bestScore(),runBest=best;\r\n host.classList.add('tub-flight');host.innerHTML='<div class=\"tub-title\"><strong></strong><span>SPONSORED SOFTWARE · 1995</span></div><p class=\"tub-caption\">Keep the tub afloat. The pipes never end. Eight pipes unlock Fuel.</p><div class=\"tub-screen\"><canvas width=\"320\" height=\"420\" tabindex=\"0\" aria-label=\"Fuel tub flight game. Tap or press Space to flap.\"></canvas><div class=\"tub-toast\" role=\"status\" hidden></div><div class=\"tub-message\" role=\"status\"></div></div><div class=\"tub-controls\"><button type=\"button\" data-flap>FLAP ↑</button><button type=\"button\" data-retry hidden>TRY AGAIN ↻</button><button type=\"button\" data-pause>Pause</button></div><p class=\"tub-note\">Optional arcade break. This game never unlocks the rest of the site.</p>';\r\n host.querySelector('.tub-title strong').textContent=title;const canvas=host.querySelector('canvas'),ctx=canvas.getContext('2d'),message=host.querySelector('.tub-message'),toast=host.querySelector('.tub-toast'),flapButton=host.querySelector('[data-flap]'),retry=host.querySelector('[data-retry]'),pause=host.querySelector('[data-pause]');\r\n function tub(x,y,tilt=0){ctx.save();ctx.translate(x,y);ctx.rotate(tilt);ctx.fillStyle='#eee4ff';ctx.fillRect(-15,-17,30,34);ctx.fillStyle='#692ca7';ctx.fillRect(-15,-3,30,14);ctx.fillStyle='#b5ff23';ctx.fillRect(-17,-21,34,7);ctx.fillStyle='#edffb8';ctx.fillRect(-11,14-game.powder*3.8,22,game.powder*3.8);ctx.fillStyle='#291044';ctx.font='bold 8px monospace';ctx.textAlign='center';ctx.fillText('FUEL',0,6);ctx.restore();}\r\n function draw(){ctx.imageSmoothingEnabled=false;ctx.fillStyle='#130e2c';ctx.fillRect(0,0,WIDTH,HEIGHT);ctx.fillStyle='#31254d';for(let x=0;x<WIDTH;x+=32){const y=300+(x%3)*7;ctx.fillRect(x,y,22,HEIGHT-y);ctx.fillStyle='#617348';ctx.fillRect(x+5,y+12,3,3);ctx.fillStyle='#31254d';}ctx.fillStyle='#74548f';for(let i=0;i<25;i++)ctx.fillRect((i*73)%320,(i*53)%285,2,2);\r\n  for(const p of game.pipes){const top=p.gap-p.half,bottom=p.gap+p.half;ctx.fillStyle='#216d68';ctx.fillRect(p.x,0,44,top);ctx.fillRect(p.x,bottom,44,HEIGHT-bottom);ctx.fillStyle='#7ce8b5';ctx.fillRect(p.x+4,0,7,top);ctx.fillRect(p.x+4,bottom,7,HEIGHT-bottom);ctx.fillStyle='#ade2be';ctx.fillRect(p.x-5,top-14,54,14);ctx.fillRect(p.x-5,bottom,54,14);ctx.fillStyle='#193b48';ctx.fillRect(p.x+15,top-10,14,8);ctx.fillRect(p.x+15,bottom+3,14,8);}\r\n  if(game.lastDose){const age=game.elapsed-game.lastDose.at;if(age<.8){for(let i=0;i<10;i++){const t=Math.min(1,(age+i*.015)/.8);ctx.fillStyle='#e9ffc9';ctx.fillRect(game.lastDose.x+(82-game.lastDose.x)*t+Math.sin(i*3)*8,game.lastDose.y+(game.y-game.lastDose.y)*t,2,3);}}}\r\n  tub(82,game.y,Math.max(-.35,Math.min(.65,game.velocity/650)));ctx.fillStyle='#bdd861';ctx.fillRect(0,HEIGHT-18,WIDTH,18);ctx.fillStyle='#172528';for(let x=0;x<WIDTH;x+=16)ctx.fillRect(x,HEIGHT-15,9,4);\r\n  ctx.textAlign='left';ctx.font='bold 18px monospace';ctx.fillStyle='#efffc9';ctx.fillText(`${game.score}`,14,29);ctx.font='11px monospace';ctx.fillStyle='#97caca';ctx.fillText(`BEST ${Math.max(best,game.score)}`,14,45);\r\n }\r\n function paint(){shell.phase(game.phase==='running'&&!paused&&!document.hidden?'active':'paused');draw();const ready=game.phase==='ready',crashed=game.phase==='crashed';message.hidden=game.phase==='running'&&!paused;message.textContent=paused?'PAUSED':ready?'TAP TO KEEP IT UP':crashed?`TUB DOWN\r\nSCORE ${game.score} · BEST ${best}${game.score>runBest?'\\nNEW BEST!':''}`:'';retry.hidden=!crashed;flapButton.hidden=crashed;pause.disabled=ready||crashed;pause.textContent=paused?'Resume':'Pause';canvas.setAttribute('aria-label',`Fuel tub flight. ${game.score} pipes, best ${best}. ${game.phase}. Tap or press Space to flap.`);}\r\n function report(){if(scored||!game.score)return;scored=true;try{const k='myr5-vault-pending',q=JSON.parse(localStorage.getItem(k)||'[]');q.push(['arcade-score',game.score]);localStorage.setItem(k,JSON.stringify(q.slice(-50)));}catch{}}\r\n function tick(now){frame=0;if(disposed)return;const dt=last?(now-last)/1000:0;last=now;if(!paused&&!document.hidden)game.step(dt);if(game.score>best){best=game.score;try{localStorage.setItem(BEST_KEY,String(best));}catch{}}if(game.phase==='crashed')report();if(game.score>=TARGET&&!ended){ended=true;const receipt={version:1,pipes:8,completedAt:Date.now()};try{localStorage.setItem(FUEL_KEY,JSON.stringify(receipt));}catch{}onComplete(receipt);toast.innerHTML=autoFuel?'FUEL UNLOCKED · <a href=\"/games/fuel/\">OPEN FUEL ›</a>':'FUEL UNLOCKED';toast.hidden=false;clearTimeout(toastTimer);toastTimer=setTimeout(()=>{toast.hidden=true;},3500);}paint();if(game.phase==='running'&&!paused&&!document.hidden)frame=requestAnimationFrame(tick);}\r\n function schedule(){if(frame)cancelAnimationFrame(frame);frame=0;last=0;if(game.phase==='running'&&!paused&&!document.hidden)frame=requestAnimationFrame(tick);paint();}\r\n function flap(){if(paused)return;if(game.flap()){if(!frame){last=performance.now();frame=requestAnimationFrame(tick);}paint();}}\r\n const pointer=e=>{e.preventDefault();flap();};const key=e=>{if(['Space','ArrowUp','KeyW'].includes(e.code)){e.preventDefault();flap();}};\r\n canvas.addEventListener('pointerdown',pointer);canvas.addEventListener('keydown',key);flapButton.onclick=flap;retry.onclick=()=>{clearTimeout(toastTimer);game.reset();runBest=best;ended=false;scored=false;toast.hidden=true;paused=false;paint();canvas.focus();};pause.onclick=()=>{paused=!paused;schedule();};\r\n function visibility(){if(document.hidden&&game.phase==='running'){paused=true;}schedule();}document.addEventListener('visibilitychange',visibility);paint();\r\n return {game,pause(){paused=true;schedule();},dispose(){shell.close();report();disposed=true;clearTimeout(toastTimer);if(frame)cancelAnimationFrame(frame);canvas.removeEventListener('pointerdown',pointer);canvas.removeEventListener('keydown',key);document.removeEventListener('visibilitychange',visibility);host.replaceChildren();}};\r\n}\r\n","arcade/tub-flight/index.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><title>Tub Flight · MOM Inc</title><link rel=\"stylesheet\" href=\"./style.css\"><style>body{margin:0;min-height:100dvh;display:grid;place-content:center;background:repeating-linear-gradient(45deg,#164743 0 12px,#17423e 12px 24px);padding:18px;box-sizing:border-box}body>a{font:14px Tahoma;color:#d2ffdb;padding:12px;text-align:center}</style><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main id=\"game\"></main><a href=\"/tv/?ch=mominc\">← Back to MOM Inc</a><script type=\"module\">import{mountTubFlight}from'./game.mjs';const game=mountTubFlight(document.getElementById('game'),{autoFuel:true});addEventListener('pagehide',()=>game.dispose(),{once:true});</script></body></html>\r\n","arcade/tub-flight/style.css":".tub-flight{width:min(100%,380px);margin:auto;padding:7px;background:#c0c0c0;border:3px outset #e8e8e8;box-sizing:border-box;color:#142023;font:12px/1.4 Tahoma,Arial,sans-serif;box-shadow:6px 6px 0 #08031280}.tub-title{display:flex;justify-content:space-between;gap:12px;align-items:center;background:linear-gradient(90deg,#070b66,#386d7c);color:#fff;padding:7px}.tub-title strong{font-size:16px}.tub-title span{font-size:8px;max-width:85px}.tub-caption{margin:8px 2px}.tub-screen{position:relative;border:3px inset #eee;background:#130e2c}.tub-screen canvas{width:100%;display:block;max-height:56dvh;object-fit:contain;touch-action:manipulation;image-rendering:pixelated;outline-offset:-3px}.tub-screen canvas:focus-visible{outline:3px solid #baff53}.tub-message{position:absolute;left:20px;right:20px;top:41%;text-align:center;font:bold 16px/1.5 monospace;background:#0b092bde;color:#c7ff43;padding:14px;border:2px solid #79a999;pointer-events:none}.tub-controls{display:flex;gap:7px;margin-top:8px}.tub-controls button{flex:1;min-height:48px;border:3px outset #eee;background:#c9ff59;color:#1b2432;font-weight:900;font-size:14px;cursor:pointer}.tub-controls [data-pause]{flex:0 0 auto;background:#c0c0c0;font-size:12px}.tub-controls button:disabled{opacity:.5}.tub-note{margin:7px 2px 0;font-size:10px}.tub-flight [hidden]{display:none!important}\r\n.tub-message{white-space:pre-line}.tub-toast{position:absolute;left:20px;right:20px;top:58px;text-align:center;font:bold 12px/1.4 monospace;background:#c7ff43;color:#1b2432;padding:6px;border:2px solid #1b2432;pointer-events:none;z-index:2}.tub-toast a{color:#1b2432;pointer-events:auto}\r\n","arcade/visual-language.mjs":"// Presentation only. Text remains available to assistive technology and on hover.\r\nconst head='<circle cx=\"32\" cy=\"13\" r=\"5\"/>';\r\nconst body=head+'<path d=\"M32 20v19m0 0-10 16m10-16 10 16\"/>';\r\nconst paths={\r\n lean:body+'<path d=\"m32 24-12 8m12-8 12 8M9 18H2m0 0 5-5m-5 5 5 5m48-5h7m0 0-5-5m5 5-5 5\"/>',\r\n raise:body+'<path d=\"m32 24-13-14m13 14L45 10M10 27V5m0 0-5 6m5-6 5 6m39 16V5m0 0-5 6m5-6 5 6\"/>',\r\n crouch:'<circle cx=\"30\" cy=\"26\" r=\"5\"/><path d=\"m30 33 9 9-18 5 18 8M30 34l-13 7M9 7v20m0 0-5-6m5 6 5-6M9 59h46\"/>',\r\n punch:head+'<path d=\"M32 20v19m0 0-10 16m10-16 10 16m-10-30 13 5 10-5m-23 0-11 9M51 15l5-7m2 12 5-1M52 34l7 4\"/>',\r\n tracking:'<path d=\"M6 20V6h14m24 0h14v14M6 44v14h14m24 0h14V44\"/>'+body+'<path d=\"m32 25-10 9m10-9 10 9\"/>',\r\n shield:'<path d=\"m32 6 22 9v18c0 12-15 22-22 25C25 55 10 45 10 33V15Z\"/><path d=\"m21 31 8 8 15-18\"/>',\r\n cover:'<circle cx=\"32\" cy=\"29\" r=\"5\"/><path d=\"M22 43v-3c0-9 20-9 20 0v3M9 46h46v12H9Z\"/><path d=\"M8 12h12m-12 0 5-5m-5 5 5 5\"/>',\r\n exposed:body+'<path d=\"m32 25-10 8m10-8 10 8M9 57h46M55 8v17m0 7v1\"/>',\r\n hide:'<path d=\"M17 6H6v50h11m20-50h7v14m0 24v12h-7\"/><circle cx=\"30\" cy=\"20\" r=\"5\"/><path d=\"m30 27 9 11-17 7 18 10m-7-23-12 5M44 31h16m0 0-6-6m6 6-6 6\"/>',\r\n caught:'<circle cx=\"27\" cy=\"17\" r=\"6\"/><path d=\"M27 26v16m0-10-12 6m12-6 12 6m-12 4-8 13m8-13 8 13M43 8l16 16M59 8 43 24\"/><path d=\"M5 5h54v54H5Z\"/>',\r\n warning:'<path d=\"m32 5 28 51H4ZM32 23v14m0 9v1\"/>',\r\n impact:'<path d=\"m30 5 9 17 19-5-10 17 11 15-20-2-8 14-8-17-19 4 10-17L5 16l18 5Z\"/><path d=\"m29 23 8 9-10 7 9 9\"/>',\r\n penalty:'<circle cx=\"32\" cy=\"32\" r=\"24\"/><path d=\"M18 32h28\"/>',\r\n target:'<circle cx=\"32\" cy=\"32\" r=\"19\"/><circle cx=\"32\" cy=\"32\" r=\"5\"/><path d=\"M32 4v13m0 30v13M4 32h13m30 0h13\"/>',\r\n check:'<path d=\"m12 32 13 14 28-30\"/>',\r\n portal:'<ellipse cx=\"32\" cy=\"32\" rx=\"19\" ry=\"26\"/><ellipse cx=\"32\" cy=\"32\" rx=\"11\" ry=\"19\"/><path d=\"M4 32h30m0 0-8-8m8 8-8 8\"/>',\r\n return:'<path d=\"M48 50V24c0-17-30-17-30 0v13m0 0L7 26m11 11 11-11\"/>',\r\n coin:'<circle cx=\"32\" cy=\"32\" r=\"24\"/><ellipse cx=\"32\" cy=\"32\" rx=\"14\" ry=\"20\"/><path d=\"M32 18v28\"/>',\r\n star:'<path d=\"m32 5 8 18 20 2-15 14 4 20-17-10-17 10 4-20L4 25l20-2Z\"/>',\r\n magnet:'<path d=\"M12 10v26a20 20 0 0 0 40 0V10H39v26a7 7 0 0 1-14 0V10Zm0 13h13m14 0h13\"/>',\r\n boot:'<path d=\"M19 8h20v25l16 8v14H12V37l7-5Zm0 12h20M12 48h43M24 33h15\"/>',\r\n crate:'<rect x=\"8\" y=\"8\" width=\"48\" height=\"48\" rx=\"2\"/><path d=\"M15 15h34v34H15Zm0 0 34 34m0-34L15 49\"/>',\r\n jetpack:'<rect x=\"10\" y=\"10\" width=\"17\" height=\"33\" rx=\"7\"/><rect x=\"37\" y=\"10\" width=\"17\" height=\"33\" rx=\"7\"/><path d=\"M27 18h10m-10 16h10M14 47l5 13 5-13m17 0 5 13 5-13\"/>',\r\n board:'<path d=\"m8 32 40-9c13-3 16 9 3 13l-37 8C2 47-2 35 8 32Z\"/><path d=\"m15 50 3 6m26-12 3 6\"/>',\r\n speed:'<path d=\"m9 13 21 19L9 51m23-38 21 19-21 19\"/>',\r\n ready:body+'<path d=\"m32 25-10 8m10-8 10 8M8 18V6h12m24 0h12v12M8 46v12h12m24 0h12V46\"/>',\r\n set:'<circle cx=\"20\" cy=\"32\" r=\"10\" fill=\"currentColor\"/><circle cx=\"46\" cy=\"32\" r=\"10\"/>',\r\n pause:'<path d=\"M22 12v40m20-40v40\"/>',\r\n play:'<path d=\"m22 10 30 22-30 22Z\"/>',\r\n stop:'<rect x=\"15\" y=\"15\" width=\"34\" height=\"34\" rx=\"4\"/>',\r\n recovery:'<circle cx=\"29\" cy=\"30\" r=\"23\"/><path d=\"M29 14v17l9 7m14 4v18m-9-9h18\"/>',\r\n distance:'<path d=\"m16 59 10-54m22 54L38 5M32 12v8m0 8v8m0 8v8\"/>',\r\n rest:'<path d=\"M5 48h54M9 38h13l7-8 12 7h15\"/><circle cx=\"15\" cy=\"28\" r=\"5\"/>',\r\n plank:'<circle cx=\"11\" cy=\"28\" r=\"5\"/><path d=\"m16 32 33 8 8 9m-36-16-5 15m-3 1h12M5 54h54\"/>',\r\n up:'<path d=\"M32 54V10m0 0L15 27m17-17 17 17\"/>',\r\n left:'<path d=\"M54 32H10m0 0 17-17M10 32l17 17\"/>',\r\n right:'<path d=\"M10 32h44m0 0L37 15m17 17L37 49\"/>',\r\n middle:'<circle cx=\"32\" cy=\"32\" r=\"14\"/><path d=\"M32 5v10m0 34v10M5 32h10m34 0h10\"/>',\r\n gate:'<path d=\"M10 58V9h44v49M20 58V19h24v39M4 58h56\"/>',\r\n flame:'<path d=\"M32 5c4 15 19 18 19 34a19 19 0 0 1-38 0c0-9 6-16 10-20-1 12 4 14 7 16 8-9 6-18 2-30Z\"/>',\r\n mansion:'<path d=\"M9 58V27L32 5l23 22v31M23 58V35h18v23M17 24h6m18 0h6M29 20h6\"/>',\r\n ring:'<ellipse cx=\"32\" cy=\"32\" rx=\"25\" ry=\"10\" transform=\"rotate(-55 32 32)\"/><ellipse cx=\"32\" cy=\"32\" rx=\"18\" ry=\"5\" transform=\"rotate(-55 32 32)\"/>',\r\n kitchen:'<rect x=\"10\" y=\"8\" width=\"44\" height=\"49\" rx=\"4\"/><path d=\"M10 22h44m-35-7h5m16 0h5M19 31h26v18H19Z\"/>',\r\n machine:'<path d=\"M12 55V20h40v35M19 20V8h26v12M5 55h54M23 29v13m18-13v13M26 49h12\"/>',\r\n fruit:'<path d=\"M8 28a24 24 0 0 0 48 0Z\"/><path d=\"M17 29a15 15 0 0 0 30 0M23 34l2 4m7-3v5m9-6-2 4\"/>'\r\n};\r\nexport function symbolMarkup(kind='check'){\r\n return `<svg class=\"game-symbol\" viewBox=\"0 0 64 64\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">${paths[kind]??paths.check}</svg>`;\r\n}\r\nexport function visualCue(kind,label,detail=''){\r\n const node=document.createElement('span');node.className='game-cue';\r\n node.title=label;node.setAttribute('role','img');node.setAttribute('aria-label',label);\r\n node.innerHTML=symbolMarkup(kind);\r\n if(detail){const n=document.createElement('b');n.textContent=detail;node.append(n);}\r\n return node;\r\n}\r\nexport function setVisualCue(node,kind,label,detail=''){\r\n const key=[kind,label,detail].join('|');if(node.dataset.visualCue===key)return;\r\n node.dataset.visualCue=key;node.replaceChildren(visualCue(kind,label,detail));\r\n}\r\nexport const VISUAL_CSS=`.game-symbol{width:36px;height:36px;display:inline-block;flex:none;vertical-align:middle}.game-cue{display:inline-flex;align-items:center;justify-content:center;gap:8px;color:inherit}.game-cue b{font:800 18px system-ui}.game-cue-row{display:flex;align-items:center;justify-content:center;gap:18px}.game-cue-row>.game-cue{border:1px solid #ffffff66;border-radius:14px;padding:10px;background:#15182de6}.game-cue-row .game-symbol{width:48px;height:48px}.game-sr{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.game-status-symbol .game-symbol{width:64px;height:64px}.game-status-symbol{border:1px solid #ffffff88;border-radius:18px;padding:16px;background:#17152de6;color:#fff}.game-meter{display:block;width:116px;height:7px;accent-color:#ffe096;margin-top:8px}@media(prefers-reduced-motion:reduce){.game-cue{animation:none!important}}`;\r\n","arcade/workout-effects-state.mjs":"// Tiny core-safe workout effect snapshot builder. Keep this independent from\r\n// optional Canvas2D renderer modules so first-run offline app boot stays intact.\r\n// The one video->overlay mapper (object-fit:contain, mirror once in math). Lives in this core-safe module so the\r\n// bundled coach overlay and the lazy effect modules share it; workout-effects.mjs re-exports it.\r\nexport function videoRectInOverlay({videoWidth,videoHeight,hostWidth,hostHeight}={}){\r\n if(![videoWidth,videoHeight,hostWidth,hostHeight].every(Number.isFinite)||videoWidth<=0||videoHeight<=0||hostWidth<=0||hostHeight<=0)return null;\r\n const scale=Math.min(hostWidth/videoWidth,hostHeight/videoHeight),w=videoWidth*scale,h=videoHeight*scale;\r\n return {x:(hostWidth-w)/2/hostWidth,y:(hostHeight-h)/2/hostHeight,w:w/hostWidth,h:h/hostHeight};\r\n}\r\nexport function mapPointToOverlay(point,geometry={}){\r\n if(!point||!Number.isFinite(point.x)||!Number.isFinite(point.y))return null;\r\n const r=videoRectInOverlay(geometry);if(!r)return null;\r\n const px=geometry.mirrored?1-point.x:point.x;\r\n return {x:r.x+px*r.w,y:r.y+point.y*r.h,visibility:Number.isFinite(point.visibility)?point.visibility:0};\r\n}\r\nexport function mapPointsToOverlay(points,geometry){\r\n return Array.isArray(points)?points.map(point=>mapPointToOverlay(point,geometry)):null;\r\n}\r\n\r\nexport function workoutEffectsSnapshot({pose={},motion={},workout=null,sessionId=null,ownerId=null,startedAt=null,mode=null,tracking=false,visible=true,canCount=false,workingSet=false}={}){\n const active=!!(tracking&&visible&&motion.tracking===true&&workingSet&&workout&&workout.counting&&!workout.finished);\r\n return {\r\n  points:pose.points??null,world:pose.world??null,width:pose.width,height:pose.height,mirrored:!!pose.mirrored,now:pose.now,frameMs:Number.isFinite(pose.frameMs)?pose.frameMs:pose.now,mask:pose.mask??null,\r\n  validHold:!!(active&&motion.kind==='hold'&&motion.progress===1&&canCount),\r\n  workout:{sessionId:sessionId??null,ownerId:ownerId??null,startedAt:Number.isSafeInteger(startedAt)&&startedAt>=0?startedAt:null,mode:workout?.mode??mode??null,kind:workout?.kind??motion.kind??null,stage:workout?.stage??null,paused:workout?.paused===true,active,\n   workingReps:Number.isFinite(workout?.value)?workout.value:0,activeSeconds:Number.isFinite(workout?.activeSeconds)?workout.activeSeconds:0,difficulty:workout?.difficulty??'easy',target:Number.isFinite(workout?.target)?workout.target:undefined}\r\n };\r\n}\r\n\r\nexport function isMartialSliceExercise(exercise){\r\n return !!(exercise&&exercise.kind!=='hold'&&(exercise.group==='stances'||exercise.group==='boxing'));\r\n}\r\n\r\nexport function holdStyleForExercise(exercise){\r\n if(!exercise||exercise.kind!=='hold')return null;\r\n if(exercise.group==='stances')return null;\r\n if(exercise.group==='core'||exercise.group==='balance')return 'blizzard';\r\n return 'living';\r\n}\r\n\r\nexport function latestRetainableWorkoutSnapshot(current,next,ownerId){\r\n const workout=next?.workout;\r\n if(!workout||!ownerId||workout.ownerId!==ownerId||workout.stage!=='working'||workout.paused===true\r\n    ||!['reps','hold'].includes(workout.kind)\r\n    ||!(Number(workout.workingReps)>0||(workout.kind==='hold'&&Number(workout.activeSeconds)>0)))return current;\r\n return next;\r\n}\r\n\r\n// Small bounded retry window for optional artwork/storage imports. The caller\r\n// starts attempts only in response to a later activity event, never on a timer.\r\nexport function createOptionalRetry({now=()=>Date.now(),maxAttempts=3,baseDelay=1000,maxDelay=8000,cooldown=30000}={}){\r\n let attempts=0,retryAt=0;\r\n return {\r\n  canStart(){if(attempts>=maxAttempts&&now()>=retryAt){attempts=0;retryAt=0;}return attempts<maxAttempts&&now()>=retryAt;},\r\n  start(){if(attempts<maxAttempts)attempts++;return attempts;},\r\n  fail(){retryAt=now()+(attempts>=maxAttempts?cooldown:Math.min(maxDelay,baseDelay*2**Math.max(0,attempts-1)));},\r\n  succeed(){attempts=0;retryAt=0;},\r\n  reset(){attempts=0;retryAt=0;},\r\n  get attempts(){return attempts;}\r\n };\r\n}\r\n\r\nconst OPTIONAL_IMPORT_NONCE=Symbol.for('myr5.optional-import-nonce');\r\nfunction nextOptionalImportNonce(){const next=(Number(globalThis[OPTIONAL_IMPORT_NONCE])||0)+1;globalThis[OPTIONAL_IMPORT_NONCE]=next;return next;}\r\n/** Load one optional entry by absolute URL; cache success/in-flight work and\r\n * query-bust only after a real rejection so a later caller can escape the\r\n * browser's cached failed module entry without adding timer retries. */\r\nexport function createOptionalModuleLoader(entryUrl,{importer=url=>import(url)}={}){\r\n const base=new URL(entryUrl,globalThis.location?.href??import.meta.url).href;let loaded=null,inflight=null,failed=false;\r\n return function loadOptionalModule(){\r\n  if(loaded)return Promise.resolve(loaded);\r\n  if(inflight)return inflight;\r\n  let url=base;\r\n  if(failed){const retry=new URL(base);retry.searchParams.set('myr5_retry',String(nextOptionalImportNonce()));url=retry.href;}\r\n  const task=Promise.resolve().then(()=>importer(url)).then(module=>{loaded=module;return module;},error=>{failed=true;throw error;}).finally(()=>{if(inflight===task)inflight=null;});\r\n  inflight=task;return task;\r\n };\r\n}\r\n","arcade/workout-effects.mjs":"// Shared AR geometry and lazy wrapper lifecycle for optional workout effects.\r\n// Landmark coordinates stay normalized; geometry is mapped to the overlay that owns the video.\r\nexport {workoutEffectsSnapshot} from './workout-effects-state.mjs';\r\nimport {holdStyleForExercise,createOptionalRetry,createOptionalModuleLoader} from './workout-effects-state.mjs';\r\nexport {mapPointToOverlay,mapPointsToOverlay,videoRectInOverlay} from './workout-effects-state.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nimport './power/power-chips.mjs'; // circuit power chips API (window.myr5PowerChips); inert without #fullWorkoutHud\r\nconst loadCameraHoldModule=createOptionalModuleLoader('/arcade/hold/camera-hold.mjs');\r\nconst loadCameraPowerModule=createOptionalModuleLoader('/arcade/power/camera-power.mjs');\r\nexport function createOverlayCanvas(host,className,{opacity=.3,max=.3}={}){\r\n const canvas=document.createElement('canvas');canvas.className=className;canvas.setAttribute('aria-hidden','true');\r\n canvas.style.cssText=`position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;pointer-events:none;z-index:0;opacity:${Math.max(0,Math.min(max,opacity))}`;\r\n const resize=()=>{const rect=host.getBoundingClientRect();const dpr=Math.min(2,window.devicePixelRatio||1),w=Math.max(1,Math.round(rect.width*dpr)),h=Math.max(1,Math.round(rect.height*dpr));if(canvas.width!==w||canvas.height!==h){canvas.width=w;canvas.height=h;}};\r\n host.prepend(canvas);resize();const observer=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;observer?.observe(host);\r\n return {canvas,resize,dispose(){observer?.disconnect();canvas.remove();}};\r\n}\r\n\r\n/** Warm the right renderer module and sprite sheets for an exercise before its set starts. */\r\nexport function preloadWorkoutEffects(mode){\r\n const ex=EXERCISES[mode];if(!ex||ex.flight)return;\r\n const stance=ex.kind==='hold'&&ex.group!=='stances';\r\n (stance?loadCameraHoldModule():loadCameraPowerModule()).then(m=>m.preloadAuraForMode?.(mode)).catch(()=>{});\r\n if(ex.kind==='hold')loadCameraPowerModule().then(m=>m.preloadAuraForMode?.(mode)).catch(()=>{});\r\n}\r\nexport function mountWorkoutEffects({host,sound=()=>{},seed=1,loaders={},retryOptions}={}){\r\n let ecology=null,power=null,ecologyLoading=null,powerLoading=null,session=null,stopped=false,epoch=0,holdGeneration=0,powerGeneration=0,lastDetail=null,selectedHoldStyle='living';\r\n const holdRetry=createOptionalRetry(retryOptions),powerRetry=createOptionalRetry(retryOptions);\r\n const dropHold=()=>{holdGeneration++;holdRetry.reset();ecology?.stop();ecology=null;ecologyLoading=null;};\r\n const drop=()=>{epoch++;dropHold();powerGeneration++;powerRetry.reset();power?.stop();power=null;powerLoading=null;session=null;lastDetail=null;};\r\n function invalidate(){holdGeneration++;powerGeneration++;holdRetry.reset();powerRetry.reset();ecologyLoading=powerLoading=null;ecology?.invalidate();power?.invalidate();}\r\n async function ensure(kind,token,loadGeneration){\r\n  if(stopped||token!==epoch)return null;\r\n  if(kind==='hold'){\r\n   if(ecology)return ecology;\r\n   if(!ecologyLoading){if(!holdRetry.canStart())return null;holdRetry.start();const style=selectedHoldStyle;const pending=(async()=>{const mod=await (loaders.hold?loaders.hold(style):loadCameraHoldModule());holdRetry.succeed();if(stopped||token!==epoch||loadGeneration!==holdGeneration||style!==selectedHoldStyle||!lastDetail?.workout?.active)return null;ecology=mod.mountCameraHold({host,sound,onXp:()=>{},seed,style,mode:lastDetail?.workout?.mode});return ecology;})();ecologyLoading=pending;pending.catch(()=>{if(ecologyLoading===pending&&token===epoch&&loadGeneration===holdGeneration){holdRetry.fail();ecologyLoading=null;}});}\r\n   return ecologyLoading;\r\n  }\r\n  if(power)return power;\r\n  if(!powerLoading){if(!powerRetry.canStart())return null;powerRetry.start();const pending=(async()=>{const mod=await (loaders.power?loaders.power():loadCameraPowerModule());powerRetry.succeed();if(stopped||token!==epoch||loadGeneration!==powerGeneration||!lastDetail?.workout?.active)return null;power=mod.mountCameraPower({host,sound,seed});return power;})();powerLoading=pending;pending.catch(()=>{if(powerLoading===pending&&token===epoch&&loadGeneration===powerGeneration){powerRetry.fail();powerLoading=null;}});}\r\n  return powerLoading;\r\n }\r\n function pose(detail){\r\n  if(stopped)return;\r\n  const workout=detail?.workout||{},key=workout.sessionId;\r\n  // Flight owns its first-person scene; the normal camera-effect host is hidden.\r\n  // Drop both mounted and pending auras when entering it from another workout.\r\n  if(EXERCISES[workout.mode]?.flight){if(session!==null||ecologyLoading||powerLoading)drop();return;}\r\n  lastDetail=detail;\r\n  if(key==null||!workout.active){invalidate();if(workout.kind==='hold'&&ecology)ecology.pose(detail);return;}\r\n  const sessionKey=`${key}:${workout.mode??''}`;\r\n  if(session!==sessionKey){drop();session=sessionKey;lastDetail=detail;}\r\n  const token=epoch;\r\n  if(workout.kind==='hold'){\r\n   const style=holdStyleForExercise(EXERCISES[workout.mode]);if(style!==selectedHoldStyle)setHoldStyle(style);\r\n   if(selectedHoldStyle&&ecology)ecology.pose(detail);else if(selectedHoldStyle){const loadGeneration=holdGeneration;void ensure('hold',token,loadGeneration).then(instance=>{if(instance&&token===epoch&&loadGeneration===holdGeneration&&session===sessionKey&&lastDetail?.workout?.active)instance.pose(lastDetail);}).catch(()=>{});}\r\n   if(selectedHoldStyle==='blizzard'){power?.invalidate();return;}\r\n   if(power)power.pose(detail);else{const loadGeneration=powerGeneration;void ensure('power',token,loadGeneration).then(instance=>{if(instance&&token===epoch&&loadGeneration===powerGeneration&&session===sessionKey&&lastDetail?.workout?.active)instance.pose(lastDetail);}).catch(()=>{});}\r\n  }else if(['reps','gentle','sprint'].includes(workout.kind)){\r\n   ecology?.invalidate();\r\n   if(power){power.pose(detail);return;}\r\n   const loadGeneration=powerGeneration;void ensure('power',token,loadGeneration).then(instance=>{if(instance&&token===epoch&&loadGeneration===powerGeneration&&session===sessionKey&&lastDetail?.workout?.active)instance.pose(lastDetail);}).catch(()=>{});\r\n  }else{ecology?.invalidate();power?.invalidate();}\r\n }\r\n function stop(){if(stopped)return;stopped=true;drop();}\r\n function setHoldStyle(style){const next=style===null?null:style==='blizzard'?'blizzard':'living';if(next===selectedHoldStyle)return selectedHoldStyle;selectedHoldStyle=next;dropHold();return selectedHoldStyle;}\r\n return {pose,stop,invalidate,setHoldStyle,get holdStyle(){return selectedHoldStyle;},get active(){return !!(ecology||power);}};\r\n}\r\n\r\n// Shared frame-time monitor for the camera effect layers (power, hold, slice can all report into it).\r\n// Q3 silhouette mask + wisps, Q2 bone glow + flame particles, Q1 bone glow only, Q0 sprites only.\r\n// Rolling mean of rAF frame intervals: step down when it passes DOWN_MS, step up after UP_HOLD_MS under UP_MS.\r\n// Q3 must be earned (start at START) and once dropped from Q3 the session stays at Q2 or below.\r\nexport const EFFECT_QUALITY=Object.freeze({START:2,MAX:3,WINDOW:60,DOWN_MS:28,UP_MS:20,UP_HOLD_MS:3000,DOWN_HOLD_MS:500,SESSION_CAP_MS:5000,SESSION_CAP_FPS_MS:50});\r\nexport function createQualityMonitor(T=EFFECT_QUALITY){\r\n const ring=new Float32Array(T.WINDOW);let n=0,i=0,sum=0,tier=T.START,cap=T.MAX,goodFor=0,badFor=0,slowFor=0,pinned=null;const listeners=new Set();\r\n const set=next=>{next=Math.max(0,Math.min(cap,next));if(next===tier)return;const prev=tier;tier=next;goodFor=badFor=0;for(const fn of listeners)try{fn(tier,prev);}catch{}};\r\n return {\r\n  frame(ms){\r\n   if(pinned!=null||!(ms>0)||ms>250)return tier; // pinned (debug), tab switches and first frames are not frame cost\r\n   sum-=n===T.WINDOW?ring[i]:0;ring[i]=ms;sum+=ms;i=(i+1)%T.WINDOW;if(n<T.WINDOW)n++;\r\n   const avg=sum/n;if(n<Math.min(15,T.WINDOW))return tier;\r\n   if(avg>T.DOWN_MS){badFor+=ms;goodFor=0;if(badFor>=T.DOWN_HOLD_MS){if(tier===3)cap=2;set(tier-1);}}\r\n   else if(avg<T.UP_MS){goodFor+=ms;badFor=0;if(goodFor>=T.UP_HOLD_MS)set(tier+1);}\r\n   else{goodFor=0;badFor=0;}\r\n   if(avg>T.SESSION_CAP_FPS_MS){slowFor+=ms;if(slowFor>=T.SESSION_CAP_MS){cap=Math.max(0,Math.min(cap,tier)-1);slowFor=0;set(tier);}}else slowFor=0;\r\n   return tier;\r\n  },\r\n  get tier(){return tier;},get average(){return n?sum/n:0;},get cap(){return cap;},\r\n  onChange(fn){listeners.add(fn);return()=>listeners.delete(fn);},\r\n  /** Debug / test: hold a tier (0..3), or null to let the ladder run again. */\r\n  pin(next=null){pinned=next==null?null:Math.max(0,Math.min(T.MAX,next|0));if(pinned!=null){const keep=cap;cap=T.MAX;set(pinned);cap=keep;}},\r\n  reset(){n=i=0;sum=0;tier=T.START;cap=T.MAX;goodFor=badFor=slowFor=0;ring.fill(0);}\r\n };\r\n}\r\nexport const effectQuality=createQualityMonitor();\r\nglobalThis.myr5EffectQuality=effectQuality;\r\n// Phone debugging: ?auraq=0..3 holds a quality tier for the session.\r\ntry{const q=new URLSearchParams(globalThis.location?.search??'').get('auraq');if(q!=null&&q!=='')effectQuality.pin(Number(q));}catch{}\r\n","armie-inbox-ui.mjs":"// Minimal, standalone inbox surface for D12 Armie letters (D23). Deliberately\r\n// small: a badge button + a plain list dialog. The owner is building the\r\n// battle-pass/achievements menu himself (D26) -- this is not that, and never\r\n// grows into it. Hidden outright during camera-only mode (the\r\n// `body[data-camera-workout]` rule below), matching \"nothing appears during\r\n// camera-only mode.\"\r\nimport {armieInbox,canOfferArmiePush,maybeRequestArmiePushPermission} from './armie-letters-client.mjs';\r\n\r\nconst STYLE = `\r\n.armie-inbox-launcher{position:fixed;right:12px;bottom:12px;z-index:40;min-width:40px;height:40px;padding:0 10px;border-radius:20px;border:none;background:#1b1030;color:#fff;font:600 13px system-ui,sans-serif;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.35);}\r\n.armie-inbox-launcher[hidden],body[data-camera-workout] .armie-inbox-launcher{display:none!important;}\r\n.armie-inbox-dialog{max-width:360px;width:92vw;border:none;border-radius:12px;padding:0;color:#1b1030;}\r\n.armie-inbox-dialog::backdrop{background:rgba(0,0,0,.4);}\r\n.armie-inbox-dialog header{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1px solid #e3ddf2;font:600 14px system-ui,sans-serif;}\r\n.armie-inbox-dialog header button{border:none;background:none;font-size:18px;line-height:1;cursor:pointer;}\r\n.armie-inbox-dialog ul{list-style:none;margin:0;padding:0;max-height:60vh;overflow:auto;}\r\n.armie-inbox-dialog li{padding:10px 14px;border-bottom:1px solid #e3ddf2;font:13px system-ui,sans-serif;cursor:pointer;}\r\n.armie-inbox-dialog li[data-read=\"0\"]{background:#f2effa;font-weight:600;}\r\n.armie-inbox-dialog li strong{display:block;margin-bottom:4px;}\r\n.armie-inbox-dialog li p{margin:2px 0;opacity:.85;font-weight:400;}\r\n.armie-inbox-notice{margin:0;padding:10px 14px;font:12px system-ui,sans-serif;background:#fff4d6;}\r\n`;\r\n\r\nlet mountPromise = null;\r\n\r\n// D23: iOS delivers web push only to the installed home-screen app. Explain\r\n// that once here rather than nagging every time the inbox opens.\r\nconst isIosBrowserTab = () => /iPad|iPhone|iPod/.test(navigator.userAgent) && !navigator.standalone;\r\n\r\nexport function setArmieLauncherOwned(launcher, owned) {\r\n launcher.hidden = !owned;\r\n launcher.inert = !owned;\r\n if (owned) launcher.removeAttribute('tabindex'); else launcher.tabIndex = -1;\r\n}\r\n\r\nexport function mountArmieInboxUI({root = document.body} = {}) {\r\n return mountPromise ??= (async () => {\r\n  const style = document.createElement('style');\r\n  style.textContent = STYLE;\r\n  document.head.append(style);\r\n\r\n  const launcher = document.createElement('button');\r\n  launcher.type = 'button';\r\n  launcher.className = 'armie-inbox-launcher';\r\n  launcher.setAttribute('aria-label', 'Letters from Armie');\r\n  // Hidden and inert until the inbox has loaded and the player owns at least one letter:\r\n  // never a greyed-out, half-loaded button.\r\n  setArmieLauncherOwned(launcher, false);\r\n  const dialog = document.createElement('dialog');\r\n  dialog.className = 'armie-inbox-dialog';\r\n  root.append(launcher, dialog);\r\n\r\n  const store = await armieInbox();\r\n\r\n  async function refreshBadge() {\r\n   const [owned, unread] = await Promise.all([store.hasAnyLetterEver(), store.unreadCount()]);\r\n   launcher.dataset.unread = String(unread);\r\n   launcher.textContent = unread ? `✉ ${unread}` : '✉';\r\n   setArmieLauncherOwned(launcher, owned);\r\n   if (!owned && dialog.open) dialog.close();\r\n  }\r\n\r\n  async function render() {\r\n   const letters = await store.listLetters();\r\n   const showNotice = isIosBrowserTab() && !(await store.hasSeenIosNotice());\r\n   // D23: the permission ask exists only once a letter does, and runs from this tap.\r\n   const offerPush = letters.length > 0 && canOfferArmiePush();\r\n   dialog.innerHTML = `<header><strong>Armie</strong><button type=\"button\" data-close aria-label=\"Close\">×</button></header>`\r\n    + (showNotice ? `<p class=\"armie-inbox-notice\" data-ios-notice>On iPhone, add Coach to your Home Screen (Share → Add to Home Screen) to get these as notifications.</p>` : '')\r\n    + (offerPush ? `<p class=\"armie-inbox-notice\"><button type=\"button\" data-push>Get Armie's letters as notifications</button></p>` : '')\r\n    + `<ul>${letters.length ? letters.map(l => `<li data-read=\"${l.read}\" data-id=\"${l.id}\"><strong>${l.header}</strong>${l.lines.map(line => `<p>${line}</p>`).join('')}</li>`).join('') : '<li>No letters yet.</li>'}</ul>`;\r\n   dialog.querySelector('[data-close]').addEventListener('click', () => dialog.close());\r\n   if (showNotice) void store.markIosNoticeSeen(); // shown once, never again\r\n   dialog.querySelector('[data-push]')?.addEventListener('click', event => { event.currentTarget.parentElement.remove(); void maybeRequestArmiePushPermission(); }, {once: true});\r\n   for (const li of dialog.querySelectorAll('li[data-id]')) {\r\n    li.addEventListener('click', async () => { await store.markRead(li.dataset.id); void import('./modules/vault/vault-store.mjs').then(m=>m.bump('armie-read')).catch(()=>{}); li.dataset.read = '1'; await refreshBadge(); }, {once: true});\r\n   }\r\n  }\r\n\r\n  launcher.addEventListener('click', async () => { if (launcher.hidden || launcher.inert) return; await render(); dialog.showModal(); await refreshBadge(); const now=Date.now(),day24h=24*60*60000;const letters=await store.listLetters();for(const l of letters){if(l.read===0&&now-l.firedAt>day24h)void import('./modules/vault/vault-store.mjs').then(m=>m.bump('armie-ignored',1,{key:l.letterId})).catch(()=>{});} });\r\n  window.addEventListener('myr5:armie-inbox-updated', () => void refreshBadge());\r\n  await refreshBadge();\r\n  return {launcher, dialog};\r\n })();\r\n}\r\n","armie-inbox.mjs":"// The in-app inbox for Armie letters (D23): persisted, readable offline, and\r\n// the source of truth -- a push notification is only ever a nudge to look\r\n// here, never the only place a letter lives. Namespaced DB name so it never\r\n// collides with the unrelated \"armie\" onboarding stage (see armie-letters.mjs).\r\nimport Dexie from 'dexie';\r\n\r\nexport const ARMIE_INBOX_DB = 'myr5-armie-letters-v1';\r\nexport const ARMIE_INBOX_SCHEMA_VERSION = 1;\r\n\r\n// One row per fired letter, id `letter:<date>:<letterId>` so repeated\r\n// delivery (a reopen catch-up, a retried push) is an idempotent upsert\r\n// instead of a duplicate.\r\nexport const armieLetterId = (date, letterId) => `letter:${date}:${letterId}`;\r\n\r\nexport async function openArmieInbox({name = ARMIE_INBOX_DB, indexedDB = globalThis.indexedDB, IDBKeyRange = globalThis.IDBKeyRange} = {}) {\r\n const db = new Dexie(name, {indexedDB, IDBKeyRange});\r\n db.version(ARMIE_INBOX_SCHEMA_VERSION).stores({\r\n  letters: '&id,date,read,firedAt',\r\n  meta: '&key',\r\n });\r\n await db.open();\r\n\r\n return {\r\n  close() { db.close({disableAutoOpen: true}); },\r\n  // Idempotent: existing rows are left untouched so a re-delivered letter\r\n  // never resets `read` back to unread or duplicates itself.\r\n  async addLetters(letters, {now = () => Date.now()} = {}) {\r\n   const added = [];\r\n   await db.transaction('rw', db.letters, async () => {\r\n    for (const {date, letterId, header, lines} of letters) {\r\n     const id = armieLetterId(date, letterId);\r\n     if (await db.letters.get(id)) continue;\r\n     // `read` is stored 0/1, not boolean: IndexedDB key ranges (used by the\r\n     // `read` index below) don't accept booleans as a valid key type.\r\n     const row = {id, date, letterId, header, lines, firedAt: now(), read: 0};\r\n     await db.letters.add(row);\r\n     added.push(row);\r\n    }\r\n   });\r\n   return added;\r\n  },\r\n  async listLetters() {\r\n   return (await db.letters.toArray()).sort((a, b) => b.date.localeCompare(a.date) || b.firedAt - a.firedAt);\r\n  },\r\n  async unreadCount() {\r\n   return db.letters.where('read').equals(0).count();\r\n  },\r\n  async markRead(id) {\r\n   await db.letters.update(id, {read: 1});\r\n  },\r\n  async markAllRead() {\r\n   await db.letters.where('read').equals(0).modify({read: 1});\r\n  },\r\n  // D23: \"explain the iOS home-screen requirement once in the inbox, no\r\n  // nagging\" -- one flag, checked and set once by the inbox UI.\r\n  async hasSeenIosNotice() {\r\n   return !!(await db.meta.get('ios-home-screen-notice-seen'));\r\n  },\r\n  async markIosNoticeSeen() {\r\n   await db.meta.put({key: 'ios-home-screen-notice-seen', value: true});\r\n  },\r\n  // D23: \"ask for notification permission only after the first letter\r\n  // exists\" -- checked by the one caller that requests permission.\r\n  async hasAnyLetterEver() {\r\n   return (await db.letters.count()) > 0;\r\n  },\r\n };\r\n}\r\n","armie-letters-client.mjs":"// Client glue for D12 Armie letters. Turns the `pendingLetters` that\r\n// server/combat.mjs attaches to the existing 'myr5:account-progress' event\r\n// into inbox rows. Self-initializing: importing this module (armie-inbox-ui.mjs\r\n// does, from app.mjs) is the whole integration; it adds its own independent\r\n// event listener rather than touching pod.mjs's dense render loop.\r\nimport {openArmieInbox} from './armie-inbox.mjs';\r\nimport {pickArmieLetter} from './armie-letters.mjs';\r\nimport {armieNotifyBlocked} from './armie-notify-gate.mjs';\r\nimport {authFetch} from './auth-client.mjs';\r\nimport {subscribePush} from './push-subscribe.mjs';\r\n\r\nexport const PUSH_PROMPTED_KEY = 'myr5-armie-push-prompted-v1';\r\n\r\nlet inboxPromise = null;\r\nexport const armieInbox = () => inboxPromise ??= openArmieInbox().catch(error => {inboxPromise = null; throw error;});\r\n\r\n// D23: offer is shown only inside the inbox once a letter exists (never on\r\n// first launch), and only until tapped once.\r\nexport function canOfferArmiePush() {\r\n try {\r\n  return 'Notification' in window && 'PushManager' in window && 'serviceWorker' in navigator\r\n   && Notification.permission === 'default' && !localStorage.getItem(PUSH_PROMPTED_KEY);\r\n } catch { return false; }\r\n}\r\n\r\n// Same subscribe steps as the settings-screen notification switch\r\n// (launch.mjs). Must be called from a tap: Safari/iOS and Firefox only show\r\n// the permission prompt inside a user gesture, so requestPermission() runs\r\n// first, before any await.\r\nexport async function maybeRequestArmiePushPermission() {\r\n try {\r\n  if (!canOfferArmiePush()) return false;\r\n  localStorage.setItem(PUSH_PROMPTED_KEY, '1'); // ask once, ever -- D23 says never nag\r\n  if (await Notification.requestPermission() !== 'granted') return false;\r\n  const accountResponse = await authFetch('/api/account');\r\n  if (!accountResponse.ok) return false;\r\n  const account = await accountResponse.json();\r\n  if (!account?.push?.configured || account.push.environment === 'preview') return false;\r\n  const registration = await navigator.serviceWorker.ready;\r\n  const {subscription: sub} = await subscribePush(registration, account.push.publicKey);\r\n  await authFetch('/api/push/subscribe', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(sub.toJSON())});\r\n  return true;\r\n } catch { return false; } // best-effort; the inbox already has the letter either way\r\n}\r\n\r\nexport async function handleArmieProgress(pendingLetters) {\r\n if (!pendingLetters?.length) return [];\r\n const store = await armieInbox();\r\n const letters = pendingLetters.map(({date, id}) => {\r\n  const picked = pickArmieLetter(id, date);\r\n  return picked && {date, ...picked};\r\n }).filter(Boolean);\r\n const added = await store.addLetters(letters);\r\n if (added.length) window.dispatchEvent(new CustomEvent('myr5:armie-inbox-updated', {detail: {added, blocked: armieNotifyBlocked()}}));\r\n return added;\r\n}\r\n\r\nif (typeof window !== 'undefined') window.addEventListener('myr5:account-progress', event => void handleArmieProgress(event.detail?.combat?.pendingLetters));\r\n\r\n// D23: \"a push arriving mid-workout is held until the set ends.\" sw.js asks\r\n// this window (ARMIE_NOTIFY_CHECK); if busy it hands the payload over\r\n// (ARMIE_LETTER_HELD) and this page shows it once camera-only mode and the set\r\n// are both over. ponytail: held in memory only -- if the page closes first the\r\n// OS toast is dropped, but the letter itself is still in the inbox.\r\nconst held = [];\r\nconst showHeld = async letter => (await navigator.serviceWorker.ready).showNotification(String(letter.title).slice(0, 80), {body: String(letter.body).slice(0, 200), icon: '/icons/myr5-alien-192.png', badge: '/icons/myr5-alien-192.png', tag: letter.tag, data: {url: letter.url, kind: letter.kind}});\r\nexport async function flushHeldArmiePushes(show = showHeld) {\r\n if (armieNotifyBlocked() || !held.length) return 0;\r\n const letters = held.splice(0);\r\n for (const letter of letters) await show(letter);\r\n return letters.length;\r\n}\r\nexport function holdArmiePush(letter, show) { held.push(letter); return flushHeldArmiePushes(show); }\r\n\r\nif (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {\r\n navigator.serviceWorker.addEventListener('message', event => {\r\n  if (event.data?.type === 'ARMIE_NOTIFY_CHECK') event.ports[0]?.postMessage({blocked: armieNotifyBlocked()});\r\n  else if (event.data?.type === 'ARMIE_LETTER_HELD') void holdArmiePush(event.data.letter).catch(() => {});\r\n });\r\n new MutationObserver(() => void flushHeldArmiePushes().catch(() => {})).observe(document.body, {attributes: true, attributeFilter: ['data-tracking', 'data-camera-workout']});\r\n}\r\n","armie-letters.mjs":"// Copy for the D12 patch-note-style letters (DECISIONS.md D12 letter table).\r\n// Content transcribed from plan/muse/armie-letters.json (Muse package,\r\n// reviewed 22 Sept 2026 -- see plan/muse/REVIEW-NOTES.md).\r\n//\r\n// NAMING: \"Armie\"/\"armie\" already exists in shipped code as the last stage of\r\n// the Coach Army onboarding run (server/coach-army-runs.mjs COACH_ARMY_STAGES,\r\n// onboarding-domain.mjs v.armieCompleted, war-room/war-room.mjs). That is an\r\n// unrelated onboarding checkpoint -- do not touch it. Everything here is its\r\n// own, separately namespaced feature: `armieLetters`, `pickArmieLetter`,\r\n// DB name `myr5-armie-letters-v1`, inbox ids `letter:<date>:<letterId>`.\r\n\r\nexport const armieLetters = {\r\n 'forgive-1': [\r\n  {header: 'Armie v1.0.1', lines: ['Miss logged. Armie patched it.', 'Streak intact. That is week one.', 'Three weeks in a row. Then Armie stops.']},\r\n  {header: 'Patch 1.0.1', lines: ['You missed. Armie covered.', 'One week patched. Two remain.', 'Three in a row is all.']},\r\n  {header: 'Armie v1.0.2', lines: ['Armie saw the gap. Armie closed it.', 'Count: one week of fixing.', 'Three is the limit. Armie counts.']},\r\n ],\r\n 'forgive-2': [\r\n  {header: 'Armie v1.0.2', lines: ['Second miss. Second patch.', 'Two weeks fixed in a row.', 'One week left. Then misses break it.']},\r\n  {header: 'Patch 1.0.3', lines: ['Armie fixed it again.', 'That is two. Armie remembers.', 'One more. That is all.']},\r\n  {header: 'Armie v1.1.0', lines: ['Miss found. Patch applied.', 'Two in a row. Armie is watching.', 'One more week. Then Armie stops.']},\r\n ],\r\n 'forgive-3-limit': [\r\n  {header: 'Armie v1.0.3', lines: ['Third patch applied. Last one.', 'Three weeks of fixing. Done.', 'Next miss breaks the streak. Armie will not stop it.']},\r\n  {header: 'Patch 1.0.4', lines: ['Armie fixed the third week.', 'Three weeks in a row. That was three.', 'Miss again and the count ends.']},\r\n  {header: 'Armie v1.1.1', lines: ['Final patch installed.', 'Three in a row. Armie stops here.', 'One more miss breaks everything.']},\r\n ],\r\n 'forgive-off-warning': [\r\n  {header: 'Armie v1.1.0', lines: ['Three weeks patched. Limit reached.', 'Forgiveness is now OFF.', 'One clean week turns it back on.']},\r\n  {header: 'Patch 1.1.0', lines: ['Armie has fixed three weeks.', 'No more patches until a full clean week.', 'Miss now and the streak breaks.']},\r\n  {header: 'Armie v1.1.2', lines: ['Week three patched. Armie rests.', 'Fixing paused. A clean week resumes it.', 'Until then, every day counts.']},\r\n ],\r\n 'streak-broken-after-limit': [\r\n  {header: 'Armie v1.2.0', lines: ['The streak broke.', 'Armie had fixed three weeks. The limit.', 'Armie could not patch this one.']},\r\n  {header: 'Patch 1.2.0', lines: ['Miss logged. Streak reset.', 'Three weeks was the deal.', 'Armie kept its side. The streak starts over.']},\r\n  {header: 'Armie v1.2.1', lines: ['Break detected. Count is zero.', 'The three-week cover is spent.', 'The next kept day is day one.']},\r\n ],\r\n 'streak-broken': [\r\n  {header: 'Armie v1.2.0', lines: ['Two misses in one week.', 'Armie patches one. Not two.', 'Streak reset. Armie is not sorry.']},\r\n  {header: 'Patch 1.2.1', lines: ['The streak broke.', 'One miss a week is the rule.', 'This week had more. Count is zero.']},\r\n  {header: 'Armie v1.3.0', lines: ['Break logged.', 'Armie fixes one miss per week.', 'The second one stands. Streak over.']},\r\n ],\r\n 'forgive-restored': [\r\n  {header: 'Armie v1.3.0', lines: ['A full week. No patches needed.', 'Forgiveness restored.', 'Armie is ready again. Try not to need it.']},\r\n  {header: 'Patch 1.3.0', lines: ['Seven clean days.', 'The counter is back to zero.', 'One miss a week, three weeks running. As before.']},\r\n  {header: 'Armie v1.3.1', lines: ['Clean week confirmed.', 'Armie reset the forgiveness.', 'Three weeks of cover, reloaded.']},\r\n ],\r\n 'milestone-5': [\r\n  {header: 'Armie v2.0.0', lines: ['Five days. Still here.', 'Armie noticed. Armie approves.', 'Keep going. Armie is watching.']},\r\n  {header: 'Patch 2.0.0', lines: ['Streak: five.', 'Small number. Real work.', 'Armie logged it in permanent ink.']},\r\n  {header: 'Armie v2.0.1', lines: ['Five in a row.', 'Armie did the math twice.', 'It checks out. Nice.']},\r\n ],\r\n 'milestone-10': [\r\n  {header: 'Armie v2.1.0', lines: ['Ten days straight.', 'Double digits. Armie nods.', 'The streak has legs now.']},\r\n  {header: 'Patch 2.1.0', lines: ['Streak: ten.', 'Armie ran out of small words.', 'Big ones: keep going.']},\r\n  {header: 'Armie v2.1.1', lines: ['Ten days. The streak holds.', 'Armie checked. Twice.', 'Still standing. Good.']},\r\n ],\r\n 'milestone-15': [\r\n  {header: 'Armie v2.2.0', lines: ['Fifteen.', 'Armie is impressed. Armie hides it.', 'The streak is a habit now.']},\r\n  {header: 'Patch 2.2.0', lines: ['Streak: fifteen.', 'Three fives. Armie counted.', 'Do not stop. Armie means it.']},\r\n  {header: 'Armie v2.2.1', lines: ['Fifteen days straight.', 'Armie updated the plaque.', 'Your name is on it. Keep it there.']},\r\n ],\r\n};\r\n\r\n// Deterministic so the same (date, letterId) always resolves to the same\r\n// copy -- callers may see the same fired letter more than once (a reopen\r\n// catch-up, a retried push) and must render identical content each time.\r\nfunction seedIndex(seed, length) {\r\n let hash = 0;\r\n for (let i = 0; i < seed.length; i++) hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;\r\n return hash % length;\r\n}\r\n\r\n// Picks one variant's copy for a fired D12 letter id. Returns null for an\r\n// unknown id instead of throwing, since letter ids ultimately come from the\r\n// streak evaluator and a caller should never crash the inbox over one.\r\nexport function pickArmieLetter(letterId, seed = letterId) {\r\n const variants = armieLetters[letterId];\r\n if (!variants || !variants.length) return null;\r\n const variant = variants[seedIndex(String(seed), variants.length)];\r\n return {letterId, header: variant.header, lines: variant.lines};\r\n}\r\n","armie-notify-gate.mjs":"// Whether it is OK to surface an Armie letter right now (an in-app toast, or\r\n// answering the service worker's \"can I show this push?\" check). Reads flags\r\n// that camera-workout.mjs and app.mjs already set on document.body -- no\r\n// edits to either file, this just observes them:\r\n//   dataset.cameraWorkout === 'true'  while the camera-only stage is up\r\n//   dataset.tracking === 'true'       while a set is actively being tracked\r\n// D23: \"nothing appears during camera-only mode; a push arriving mid-workout\r\n// is held until the set ends.\"\r\nexport function armieNotifyBlocked(doc = typeof document !== 'undefined' ? document : null) {\r\n const flags = doc?.body?.dataset;\r\n return !!flags && (flags.cameraWorkout === 'true' || flags.tracking === 'true');\r\n}\r\n","audio/loop-seam.mjs":"// Makes a decoded ambient clip loop without a gap. Encoded clips often carry silence or a fade at each end\r\n// (the waterfall had 0.6 s of silence in front and a fade-out behind), so looping them leaves a dip every pass.\r\n// The steady body is kept and its end is blended into its start with an equal-power crossfade: after the last\r\n// sample the loop continues with the sample that followed the blended start, so there is no click or dip.\r\nconst BLOCK=.01;\r\nexport function seamlessLoopChannels(channels,rate,{crossfade=.5,margin=.05}={}){\r\n  const n=channels[0].length,block=Math.max(1,Math.round(rate*BLOCK)),count=Math.floor(n/block);\r\n  if(count<8)return channels;\r\n  const rms=Array.from({length:count},(_,b)=>{let s=0;for(const ch of channels)for(let i=b*block;i<(b+1)*block;i++)s+=ch[i]*ch[i];return Math.sqrt(s/(block*channels.length));});\r\n  const median=[...rms].sort((a,b)=>a-b)[count>>1],floor=median*.8;\r\n  let first=0,last=count-1;\r\n  while(first<count&&rms[first]<floor)first++;\r\n  while(last>first&&rms[last]<floor)last--;\r\n  // Step in past the edge ramps (an end that fades measures high until its last blocks).\r\n  const pad=Math.round(rate*margin);\r\n  const ramp=Math.round(rate*.3),start=first*block+pad+ramp,end=(last+1)*block-pad-ramp;\r\n  const body=end-start;\r\n  const fade=Math.min(Math.round(rate*crossfade),Math.floor(body/3));\r\n  if(fade<=0||body<fade*3)return channels;\r\n  const length=body-fade;\r\n  return channels.map(ch=>{\r\n    const out=new Float32Array(length);\r\n    for(let i=0;i<length;i++)out[i]=ch[start+i];\r\n    for(let i=0;i<fade;i++){const t=(i+.5)/fade*Math.PI/2;out[i]=ch[start+i]*Math.sin(t)+ch[start+length+i]*Math.cos(t);}\r\n    return out;\r\n  });\r\n}\r\nexport function seamlessLoopBuffer(context,buffer,options){\r\n  const channels=Array.from({length:buffer.numberOfChannels},(_,c)=>buffer.getChannelData(c));\r\n  const next=seamlessLoopChannels(channels,buffer.sampleRate,options);\r\n  if(next===channels)return buffer;\r\n  const out=context.createBuffer(next.length,next[0].length,buffer.sampleRate);\r\n  next.forEach((data,c)=>out.copyToChannel(data,c));\r\n  return out;\r\n}\r\n","audio/music/manifest.json":"{\r\n \"version\": 1,\r\n \"sampleRate\": 44100,\r\n \"bars\": 8,\r\n \"tracks\": {\r\n  \"hey-man-idk\": {\r\n   \"url\": \"/audio/music/hey-man-idk.m4a\",\r\n   \"bpm\": 188.43,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 449348,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 10.189297,\r\n   \"duration\": 10.189297,\r\n   \"sourceStart\": 0.585,\r\n   \"tailFolded\": 0.04,\r\n   \"bytes\": 145363,\r\n   \"decodedFrames\": 449348\r\n  },\r\n  \"guarded-gate\": {\r\n   \"url\": \"/audio/music/guarded-gate.m4a\",\r\n   \"bpm\": 121.3,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 698032,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 15.82839,\r\n   \"duration\": 15.82839,\r\n   \"sourceStart\": 0.075,\r\n   \"tailFolded\": 0.04,\r\n   \"bytes\": 226826,\r\n   \"decodedFrames\": 698032\r\n  },\r\n  \"daemon-time\": {\r\n   \"url\": \"/audio/music/daemon-time.m4a\",\r\n   \"bpm\": 90.16,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 939140,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 21.295692,\r\n   \"duration\": 21.295692,\r\n   \"sourceStart\": 0.64,\r\n   \"tailFolded\": 0.06,\r\n   \"bytes\": 310247,\r\n   \"decodedFrames\": 939140\r\n  },\r\n  \"sick-with-science\": {\r\n   \"url\": \"/audio/music/sick-with-science.m4a\",\r\n   \"bpm\": 140.5,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 602648,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 13.665488,\r\n   \"duration\": 13.665488,\r\n   \"sourceStart\": 0.02,\r\n   \"tailFolded\": 0.212,\r\n   \"bytes\": 194523,\r\n   \"decodedFrames\": 602648\r\n  },\r\n  \"laboratory-violence\": {\r\n   \"url\": \"/audio/music/laboratory-violence.m4a\",\r\n   \"bpm\": 120.0,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 705600,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 16.0,\r\n   \"duration\": 16.0,\r\n   \"sourceStart\": 0.01,\r\n   \"tailFolded\": 0.511,\r\n   \"bytes\": 229213,\r\n   \"decodedFrames\": 705600\r\n  },\r\n  \"main-theme-one\": {\r\n   \"url\": \"/audio/music/main-theme-one.m4a\",\r\n   \"bpm\": 120.0,\r\n   \"bars\": 8,\r\n   \"beats\": 32,\r\n   \"frames\": 705600,\r\n   \"loopStart\": 0,\r\n   \"loopEnd\": 16.0,\r\n   \"duration\": 16.0,\r\n   \"sourceStart\": 2.02,\r\n   \"tailFolded\": 0.06,\r\n   \"bytes\": 240960,\r\n   \"decodedFrames\": 705600\r\n  }\r\n }\r\n}","audio/page-music.mjs":"// Page music: one looping song per scene, started on the first gesture, obeying the SOUND toggle and saved mute.\r\n// A track change fades the new song in (3 s; the grimoire theme 5 s up to 35%) while a stronger wormhole-transit bed\r\n// (transit + boom + whoosh + the grimoire board sounds at 50%) fades out. Suspends when hidden; ducks under the coach voice.\r\n// Loops come from audio/music (scripts/music-loops.py): exactly 8 bars, loopStart 0 / loopEnd = duration, no encoder padding left.\r\nimport {physicalSound} from './physical-sound.mjs';\r\n\r\nexport const ROUTE_TRACK={pod:'main-theme-one',workout:null,dance:null,select:null,battlepass:'hey-man-idk',achievements:'guarded-gate',vault:'guarded-gate',\r\n customize:'daemon-time',customizeCoach:'daemon-time','war-room':'daemon-time',food:'sick-with-science',portal:null,settings:'main-theme-one',scoreboard:'laboratory-violence',meditate:null};\r\nexport const TRACK_LEVEL={'main-theme-one':.35};\r\nexport const TRACK_FADE={'main-theme-one':5};\r\nexport const FADE_IN=3,FADE_OUT=1.2,DUCK_LEVEL=.3,DUCK_DOWN=.12,DUCK_UP=1.2;\r\n// The pod and its menus share the grimoire song; hey-man-idk is only the XP Flight (battle pass) screen; workout/select stay silent (camera / pickers).\r\n// Pod + menus share the grimoire song; hey-man-idk is XP Flight (battle pass) only; workout/select are silent.\r\n// Track for a scene. undefined = keep whatever plays (history, install...), null = silence (workout camera, meditation).\r\nexport function trackForRoute(id,{portalUp=false,quiet=false,pathname='',scene=null,hold=false}={}){\r\n if(quiet||hold)return null; // hold = a portal trace/tunnel is running: only the bed/sfx sound\r\n if(scene==='battlepass')return 'hey-man-idk'; // XP Flight dialog opens over any page\r\n if(/^\\/(?:creature|war-room)\\//.test(pathname))return 'daemon-time';\r\n if(!id)return portalUp?null:ROUTE_TRACK.pod; // the grimoire is silent\r\n return ROUTE_TRACK[id];\r\n}\r\n// Fade-in schedule for a track starting at t0 (gain 0 -> level).\r\nexport const rampPlan=(track,t0)=>{const dur=TRACK_FADE[track]??FADE_IN;return {from:0,to:TRACK_LEVEL[track]??1,start:t0,end:t0+dur};};\r\n// Loop position (s) of a looping source started at `startedAt` from `startOffset`, read at `now` (all AudioContext seconds).\r\nexport const loopPosition=({startOffset,startedAt,now,duration})=>(((startOffset+(now-startedAt))%duration)+duration)%duration;\r\n// Asymmetric duck: music falls fast when the coach speaks and climbs back slowly.\r\nexport const duckPlan=speaking=>speaking?{to:DUCK_LEVEL,seconds:DUCK_DOWN}:{to:1,seconds:DUCK_UP};\r\n\r\nconst BOARD_KINDS={quilt:['quilt'],ice:['ice'],jelly:['jelly'],water:['water','water-slosh'],pond:['water','water-slosh'],grass:['grass','grass-tinkle'],cogs:['cogs'],wood:['wood','wood-scrape','crackle']};\r\nconst MIX_KINDS=['ice','crackle','water'];\r\nconst BED_BOARD_GAIN=.5;\r\n\r\nexport function createPageMusic({sound=physicalSound,documentRef=globalThis.document,windowRef=globalThis.window,fetchRef=(...a)=>globalThis.fetch(...a),pathname=globalThis.location?.pathname||''}={}){\r\n let speaking=false,scene=null,hold=false,djActive=false,holdTimer=0,ctx=null,out=null,duck=null,armed=false,want=undefined,epoch=0,cur=null,cut=null,manifest=null,manifestP=null,noise=null,warned=false;\r\n const buffers=new Map(),log=[];\r\n const warn=e=>{if(!warned){warned=true;console.warn('page music:',e?.message||e);}};\r\n const note=(what,param,v0,v1,t0,t1)=>{log.push({what,v0,v1,t0,t1});if(log.length>60)log.shift();};\r\n function ramp(param,what,to,t0,seconds){\r\n  param.cancelScheduledValues(t0);param.setValueAtTime(param.value,t0);\r\n  if(seconds>0)param.linearRampToValueAtTime(to,t0+seconds);else param.setValueAtTime(to,t0);\r\n  note(what,param,param.value,to,t0,t0+seconds);\r\n }\r\n function ensure(){\r\n  if(ctx)return true;\r\n  sound.unlock?.();ctx=sound.context;if(!ctx)return false;\r\n  duck=ctx.createGain();out=ctx.createGain();duck.connect(out);out.connect(ctx.destination);level();return true;\r\n }\r\n const muted=()=>!!sound.muted;\r\n function level(){if(!out)return;out.gain.setTargetAtTime(muted()?0:1,ctx.currentTime,.03);}\r\n async function loadManifest(){\r\n  return manifestP??=(async()=>{const r=await fetchRef('/audio/music/manifest.json');if(!r.ok)throw Error('music manifest unavailable');manifest=await r.json();return manifest;})().catch(e=>{manifestP=null;throw e;});\r\n }\r\n async function decode(url){\r\n  if(!buffers.has(url))buffers.set(url,(async()=>{const r=await fetchRef(url);if(!r.ok)throw Error('unavailable '+url);return ctx.decodeAudioData(await r.arrayBuffer());})().catch(e=>{buffers.delete(url);throw e;}));\r\n  return buffers.get(url);\r\n }\r\n function noiseBuffer(){\r\n  if(noise)return noise;noise=ctx.createBuffer(1,ctx.sampleRate*3,ctx.sampleRate);const d=noise.getChannelData(0);let s=0x2f6e2b1;\r\n  for(let i=0;i<d.length;i++){s=(1664525*s+1013904223)>>>0;d[i]=s/2147483648-1;}return noise;\r\n }\r\n function stopSoon(nodes,seconds){setTimeout(()=>{for(const n of nodes){try{n.stop?.();}catch{}try{n.disconnect();}catch{}}},Math.ceil(seconds*1000)+80);}\r\n // ---- transition bed -------------------------------------------------------------------------------------------\r\n async function startBed(kinds,fade){\r\n  const t0=ctx.currentTime,bed=ctx.createGain(),comp=ctx.createDynamicsCompressor(),nodes=[bed,comp];\r\n  comp.threshold.value=-12;comp.ratio.value=5;bed.connect(comp);comp.connect(out);\r\n  bed.gain.setValueAtTime(1,t0);bed.gain.setValueAtTime(1,t0+.5);bed.gain.linearRampToValueAtTime(0,t0+fade);note('bed',bed.gain,1,0,t0+.5,t0+fade);\r\n  const add=n=>{nodes.push(n);return n;};\r\n  // low boom + whoosh make the existing transit sample feel stronger\r\n  const boom=add(ctx.createOscillator()),bg=add(ctx.createGain());boom.type='sine';boom.frequency.setValueAtTime(78,t0);boom.frequency.exponentialRampToValueAtTime(30,t0+1.6);\r\n  bg.gain.setValueAtTime(.0001,t0);bg.gain.exponentialRampToValueAtTime(.7,t0+.05);bg.gain.exponentialRampToValueAtTime(.0001,t0+2);boom.connect(bg).connect(bed);boom.start(t0);boom.stop(t0+2.1);\r\n  const wh=add(ctx.createBufferSource()),wf=add(ctx.createBiquadFilter()),wg=add(ctx.createGain());wh.buffer=noiseBuffer();wf.type='bandpass';wf.Q.value=.8;\r\n  wf.frequency.setValueAtTime(180,t0);wf.frequency.exponentialRampToValueAtTime(2600,t0+1);wf.frequency.exponentialRampToValueAtTime(260,t0+fade);\r\n  wg.gain.setValueAtTime(.0001,t0);wg.gain.exponentialRampToValueAtTime(.35,t0+.5);wg.gain.exponentialRampToValueAtTime(.0001,t0+fade);wh.connect(wf).connect(wg).connect(bed);wh.start(t0);wh.stop(t0+fade+.1);\r\n  stopSoon(nodes,fade+.2);\r\n  try{\r\n   const sfx=await sfxManifest();\r\n   const play=async(path,gain,at,rate=1)=>{const b=await decode(path);if(ctx.currentTime>t0+fade)return;const s=add(ctx.createBufferSource()),g=add(ctx.createGain());s.buffer=b;s.playbackRate.value=rate;g.gain.value=gain;s.connect(g).connect(bed);s.start(Math.max(ctx.currentTime,t0+at));};\r\n   const transit=sfx.cues?.transit?.[0];\r\n   if(transit)await play(transit,1.5,0,.85);\r\n   let at=.1;\r\n   for(const kind of kinds){\r\n    if(kind==='crackle'){crackle(bed,t0,Math.min(fade,2.6),add);continue;}\r\n    const list=sfx.cues?.[kind];if(!list?.length)continue;\r\n    for(let i=0;i<3;i++){await play(list[(i)%list.length],BED_BOARD_GAIN,at+i*.7,.94+Math.random()*.12);}\r\n    at+=.35;\r\n   }\r\n  }catch(e){warn(e);}\r\n }\r\n function crackle(bed,t0,seconds,add){\r\n  const g=add(ctx.createGain());g.gain.value=BED_BOARD_GAIN;g.connect(bed);\r\n  for(let i=0;i<26;i++){\r\n   const at=t0+Math.random()*seconds,s=add(ctx.createBufferSource()),f=add(ctx.createBiquadFilter()),e=add(ctx.createGain());\r\n   s.buffer=noiseBuffer();f.type='highpass';f.frequency.value=900+Math.random()*2500;e.gain.setValueAtTime(.0001,at);e.gain.exponentialRampToValueAtTime(.2+Math.random()*.3,at+.003);e.gain.exponentialRampToValueAtTime(.0001,at+.03+Math.random()*.05);\r\n   s.connect(f).connect(e).connect(g);s.start(at,Math.random()*2);s.stop(at+.1);\r\n  }\r\n }\r\n let sfxP=null;\r\n const sfxManifest=()=>sfxP??=fetchRef('/audio/sfx/manifest.json').then(r=>r.json()).then(m=>({cues:m.cues})).catch(e=>{sfxP=null;throw e;});\r\n function bedKinds(prev){return prev==='main-theme-one'?BOARD_KINDS[sound.board]||BOARD_KINDS.quilt:MIX_KINDS;}\r\n // ---- tracks --------------------------------------------------------------------------------------------------\r\n function startSource(buffer,m,offset,gainValue,fadeSeconds){\r\n  const g=ctx.createGain(),s=ctx.createBufferSource(),t=ctx.currentTime;\r\n  s.buffer=buffer;s.loop=true;s.loopStart=m.loopStart||0;s.loopEnd=m.loopEnd||buffer.duration;s.connect(g).connect(duck);\r\n  g.gain.setValueAtTime(fadeSeconds?0:gainValue,t);if(fadeSeconds){g.gain.linearRampToValueAtTime(gainValue,t+fadeSeconds);note('fade-in',g.gain,0,gainValue,t,t+fadeSeconds);}\r\n  s.start(t,offset);return {source:s,gain:g,startedAt:t,startOffset:offset};\r\n }\r\n function fadeOut(entry,seconds){\r\n  const t=ctx.currentTime,g=entry.gain.gain;g.cancelScheduledValues(t);g.setValueAtTime(g.value,t);g.linearRampToValueAtTime(0,t+seconds);note('fade-out',g,g.value,0,t,t+seconds);\r\n  stopSoon([entry.source,entry.gain],seconds);\r\n }\r\n async function play(name){\r\n  const mine=++epoch,prev=cur;\r\n  try{\r\n   const m=(await loadManifest()).tracks[name];if(!m)throw Error('unknown track '+name);\r\n   const buffer=await decode(m.url);\r\n   if(mine!==epoch||!ctx)return;\r\n   const plan=rampPlan(name,ctx.currentTime),dur=plan.end-plan.start;\r\n   const entry=startSource(buffer,m,0,plan.to,dur);\r\n   Object.assign(entry,{name,m,buffer,level:plan.to});cur=entry;cut=null;\r\n   if(prev){fadeOut(prev,FADE_OUT);}\r\n   void startBed(bedKinds(prev?.name),dur);\r\n  }catch(e){warn(e);}\r\n }\r\n function setTrack(name){\r\n  want=name;\r\n  if(!armed||name===undefined)return;\r\n  if(name===null){epoch++;if(cur&&ctx){fadeOut(cur,1);cur=null;cut=null;}return;}\r\n  if(!ensure())return;\r\n  if((cur?.name===name&&!cut)||cut?.name===name){epoch++;return;} // already the song: drop any pending change\r\n  if(ctx.state==='suspended'&&!documentRef?.hidden&&!muted())void ctx.resume();\r\n  void play(name);\r\n }\r\n // ---- classroom cut / resume ----------------------------------------------------------------------------------\r\n // Hard stop mid-bar (no fade) and later restart from the exact loop position it stopped at.\r\n function cutNow(){\r\n  if(!cur||cut||!ctx)return false;\r\n  const offset=loopPosition({startOffset:cur.startOffset,startedAt:cur.startedAt,now:ctx.currentTime,duration:cur.m.loopEnd-(cur.m.loopStart||0)});\r\n  try{cur.source.stop();cur.source.disconnect();}catch{}\r\n  cut={name:cur.name,offset,m:cur.m,buffer:cur.buffer,level:cur.level};cur=null;return cut.offset;\r\n }\r\n function resumeNow(){\r\n  if(!cut||!ctx)return false;\r\n  const {name,m,buffer,level:l,offset}=cut;cut=null;\r\n  const entry=startSource(buffer,m,offset,l,0);Object.assign(entry,{name,m,buffer,level:l});cur=entry;return offset;\r\n }\r\n // ---- environment ---------------------------------------------------------------------------------------------\r\n const quiet=()=>{const d=documentRef?.body?.dataset||{};return d.tracking==='true'||d.cameraWorkout==='true';};\r\n function sync(){\r\n  const id=windowRef?.myr5Routes?.current?.()||'',portalUp=documentRef?.getElementById?.('portalHome')?.hidden===false;\r\n  const t=djActive?null:trackForRoute(id,{portalUp,quiet:quiet(),pathname,scene,hold});\r\n  if(t!==undefined)setTrack(t);\r\n }\r\n const unlock=()=>{if(armed)return;armed=true;documentRef.removeEventListener('pointerdown',unlock,true);documentRef.removeEventListener('keydown',unlock,true);ensure();sync();if(want!==undefined)setTrack(want);};\r\n function mount(){\r\n  if(!documentRef||windowRef.parent!==windowRef)return api; // a framed page leaves the music to its parent\r\n  documentRef.addEventListener('pointerdown',unlock,true);documentRef.addEventListener('keydown',unlock,true);\r\n  documentRef.addEventListener('visibilitychange',()=>{if(!ctx)return;if(documentRef.hidden)void ctx.suspend();else if(!muted())void ctx.resume();});\r\n  windowRef.addEventListener('myr5:sound-settings',()=>{level();if(!muted()&&ctx?.state==='suspended'&&!documentRef.hidden)void ctx.resume();});\r\n  windowRef.addEventListener('myr5:response',e=>{speaking=e.detail?.state==='speaking';if(!duck)return;const p=duckPlan(speaking);ramp(duck.gain,'duck',p.to,ctx.currentTime,p.seconds);});\r\n  windowRef.addEventListener('myr5:sfx-duck',e=>{if(!duck)return;const d=e.detail||{},t=ctx.currentTime;ramp(duck.gain,'duck',d.to??.5,t,d.seconds??.05);setTimeout(()=>{if(duck)ramp(duck.gain,'duck',duckPlan(speaking).to,ctx.currentTime,d.release??.35);},Math.ceil((d.seconds??.05)*1000));}); // explosions pop out of the music\r\n  windowRef.addEventListener('myr5:route',sync);\r\n  windowRef.addEventListener('myr5:music-scene',e=>{scene=e.detail?.scene||null;sync();});\r\n  windowRef.addEventListener('myr5:dj-active',e=>{djActive=!!e.detail?.active;sync();});\r\n  // portal.mjs holds the music for the whole trace + tunnel and releases it once the destination is shown (15 s safety).\r\n  windowRef.addEventListener('myr5:music-hold',e=>{hold=!!e.detail?.hold;clearTimeout(holdTimer);if(hold)holdTimer=setTimeout(()=>{hold=false;sync();},15000);sync();});\r\n  windowRef.addEventListener('myr5:music-cut',()=>cutNow());\r\n  windowRef.addEventListener('myr5:music-resume',()=>resumeNow());\r\n  new MutationObserver(sync).observe(documentRef.body||documentRef.documentElement,{attributes:true,attributeFilter:['data-tracking','data-camera-workout']});\r\n  sync();return api;\r\n }\r\n const api={mount,setTrack,cut:cutNow,resume:resumeNow,sync,log,current:()=>cur?.name||null,state:()=>({armed,want,playing:cur?.name||null,cut:cut&&{...cut,buffer:undefined,m:undefined},ctx:ctx&&ctx.state,muted:muted()}),unlock};\r\n return api;\r\n}\r\nexport const pageMusic=createPageMusic();\r\nexport function mountPageMusic(){const m=pageMusic.mount();if(globalThis.window)globalThis.window.myr5Music=m;return m;}\r\n","audio/physical-sound.mjs":"// Small, gesture-unlocked sound layer shared by the pod, grimoire, and rooms.\r\n// Samples are optional: every cue has a local Web Audio fallback for offline play.\r\nimport {seamlessLoopBuffer} from './loop-seam.mjs';\r\nexport const SOUND_PREF_KEY='myr5.physicalSound.v1';\r\nconst clamp=(n,a=0,b=1)=>Math.min(b,Math.max(a,n));\r\nconst RATE={'main-control':38,mechanical:38,switch:90,dial:45,lever:80,crt:65,ice:250,jelly:220,water:280,'water-slosh':900,'pond-slosh':1100,grass:300,'grass-tinkle':1200,cogs:180,wood:250,'wood-scrape':300,quilt:220,drag:160,transit:800,dialup:1800,'electric':110,bloop:160,'signal':250,'whiteboard':450,'gun-arm':120,'gun-shot':80,blast:60,'blast-big':90,'barrel-split':80};\r\nconst GROUP={'main-control':'mechanical',water:'water','water-slosh':'water','pond-slosh':'water',wood:'wood','wood-scrape':'wood','gun-arm':'gun-arm',grass:'grass','grass-tinkle':'tinkle'};\r\nconst SAMPLE={'main-control':'mechanical',mechanical:'mechanical-click',switch:'metal-switch',dial:'mechanical',lever:'switch',ice:'ice-crack',jelly:'jelly-squish',water:'water-splash','water-slosh':'water-slosh','pond-slosh':'water-slosh',grass:'grass-rustle','grass-tinkle':'grass-tinkle',cogs:'cogs-ratchet',wood:'wood-tap','wood-scrape':'wood-scrape',quilt:'cloth-rustle',transit:'portal-transit',dialup:'dialup',crt:'crt-tap','pod-hum':'pod-hum',breeze:'breeze',waterfall:'waterfall',electric:'electric',bloop:'bloop',signal:'signal',whiteboard:'whiteboard','gun-arm':'gun-arm','gun-shot':'gun-shot',blast:'blast','blast-big':'blast-big','barrel-split':'barrel-split'};\r\nconst LIMIT={blast:3,'blast-big':2,'gun-shot':3,'barrel-split':3}; // overlapping booms for a combo, the next steals the oldest\r\nObject.assign(RATE,{'flight-ready':600,'flight-set':600,'flight-go':600,'flight-coin':65,'flight-warning':750,'flight-impact':220,'flight-pickup':250,'flight-portal':800,'flight-stunt':500});\r\nconst safeStorage=()=>{try{return globalThis.localStorage;}catch{return null;}};\r\n\r\nexport function readSoundPrefs(storage=safeStorage()){\r\n try{const saved=JSON.parse(storage?.getItem(SOUND_PREF_KEY)||'{}');return {muted:saved.muted===true,volume:clamp(Number.isFinite(saved.volume)?saved.volume:.42)};}catch{return {muted:false,volume:.42};}\r\n}\r\n\r\nexport class PhysicalSound{\r\n constructor({AudioContextClass=globalThis.AudioContext||globalThis.webkitAudioContext,storage=safeStorage(),documentRef=globalThis.document,windowRef=globalThis.window,now=()=>performance.now()}={}){\r\n  this.AudioContextClass=AudioContextClass;this.storage=storage;this.document=documentRef;this.window=windowRef;\r\n  this.now=now;\r\n  this.prefs=readSoundPrefs(storage);this.context=null;this.voices=new Set();this.tails=new Set();this.last=new Map();this.scene='pod';this.board='quilt';this.ducked=false;this.ambience=null;this.epoch=0;\r\n  this.samples=new Map();this.sampleStatus=new Map();this.sampleRetry=new Map();this.manifest=null;this.manifestPending=null;this.manifestRetryAt=0;this.noise=null;this.started=false;\r\n }\r\n mount(){\r\n  if(this.started||!this.document)return this;this.started=true;this.abort=new AbortController();const signal=this.abort.signal;\r\n  const unlock=()=>this.unlock();\r\n  this.document.addEventListener('pointerdown',unlock,{capture:true,signal});this.document.addEventListener('keydown',unlock,{capture:true,signal});\r\n  this.document.addEventListener('visibilitychange',()=>{if(this.document.hidden)this.stopAll();else this.syncAmbience();},{signal});\r\n  this.window?.addEventListener('pagehide',()=>this.stopAll(),{signal});\r\n  this.window?.addEventListener('pageshow',()=>this.syncAmbience(),{signal});\r\n  this.window?.addEventListener('myr5:response',event=>{this.ducked=event.detail?.state==='speaking';this.syncLevel();},{signal});\r\n  this.window?.addEventListener('storage',event=>{if(event.key===SOUND_PREF_KEY){this.prefs=readSoundPrefs(this.storage);if(this.muted||!this.volume)this.stopAll();this.syncLevel();this.syncAmbience();this.emit();}},{signal});\r\n  if(globalThis.MutationObserver&&this.document?.documentElement){this.observer=new MutationObserver(()=>this.syncAmbience());this.observer.observe(this.document.documentElement,{subtree:true,attributes:true,attributeFilter:['open','hidden','data-screen','data-tracking','data-camera-workout','data-ship-view']});signal.addEventListener('abort',()=>this.observer.disconnect(),{once:true});}\r\n  return this;\r\n }\r\n get muted(){return this.prefs.muted;}\r\n get volume(){return this.prefs.volume;}\r\n setVolume(volume){this.prefs.volume=clamp(Number(volume)||0);this.save();}\r\n setMuted(muted){this.prefs.muted=!!muted;this.save();}\r\n save(){try{this.storage?.setItem(SOUND_PREF_KEY,JSON.stringify(this.prefs));}catch{}if(this.muted||!this.volume)this.stopAll();this.syncLevel();this.syncAmbience();this.emit();}\r\n emit(){this.window?.dispatchEvent(new CustomEvent('myr5:sound-settings',{detail:{...this.prefs}}));}\r\n unlock(){\r\n  if(!this.AudioContextClass)return false;\r\n  if(!this.context){\r\n   try{const c=this.context=new this.AudioContextClass();this.master=c.createGain();this.duck=c.createGain();this.duck.connect(this.master);this.master.connect(c.destination);this.syncLevel();}\r\n   catch{this.context=null;return false;}\r\n  }\r\n  if(!this.manifestPending&&Date.now()>=this.manifestRetryAt)void this.loadManifest();\r\n  if(this.context.state==='suspended')void this.context.resume().then(()=>this.syncAmbience()).catch(()=>{});\r\n  else this.syncAmbience();\r\n  return true;\r\n }\r\n syncLevel(){if(!this.context)return;const t=this.context.currentTime;this.master.gain.setTargetAtTime(this.muted?0:this.volume,t,.025);this.duck.gain.setTargetAtTime(this.ducked?.23:1,t,.08);}\r\n setScene(scene,board=this.board){if(scene!==this.scene||board!==this.board)this.epoch++;this.scene=scene;this.board=board;this.syncAmbience();}\r\n setBoard(board){if(board!==this.board)this.epoch++;this.board=board;this.syncAmbience();}\r\n noiseBuffer(){\r\n  if(this.noise)return this.noise;const c=this.context,b=this.noise=c.createBuffer(1,c.sampleRate*2,c.sampleRate),data=b.getChannelData(0);\r\n  let seed=0x51f15e;for(let i=0;i<data.length;i++){seed=(1664525*seed+1013904223)>>>0;data[i]=(seed/2147483648)-1;}\r\n  return b;\r\n }\r\n sourceNoise(){const s=this.context.createBufferSource();s.buffer=this.noiseBuffer();return s;}\r\n voice(kind,seconds,build){\r\n  const c=this.context;if(!c||c.state!=='running'||this.document?.hidden||this.muted||!this.volume)return;\r\n  const group=GROUP[kind]||kind,limit=LIMIT[group]||(['mechanical','dial','lever','switch'].includes(group)?4:group==='tinkle'||group==='transit'||group==='dialup'?1:2);\r\n  while([...this.voices].filter(v=>v.group===group).length>=limit)[...this.voices].find(v=>v.group===group).stop(.06);\r\n  while(this.voices.size>=12)this.voices.values().next().value.stop();\r\n  const output=c.createGain(),nodes=[];output.connect(this.duck);let stopped=false;\r\n  const voice={group,stop:(fade=0)=>{if(stopped)return;stopped=true;this.voices.delete(voice);let timer;\r\n   const clean=()=>{clearTimeout(timer);this.tails.delete(clean);for(const n of nodes)try{n.stop?.();}catch{}for(const n of nodes)try{n.disconnect();}catch{}output.disconnect();};\r\n   if(fade){const now=c.currentTime;output.gain.setValueAtTime(output.gain.value,now);output.gain.linearRampToValueAtTime(0,now+fade);this.tails.add(clean);timer=setTimeout(clean,Math.ceil(fade*1000)+5);}else clean();}};\r\n  this.voices.add(voice);const t=c.currentTime;\r\n  try{build({c,t,output,nodes});}catch{voice.stop();return;}\r\n  // Infinity denotes an explicitly owned loop; stopAll/mute still releases it.\r\n  if(Number.isFinite(seconds))setTimeout(()=>voice.stop(),Math.ceil((seconds+.06)*1000));return voice;\r\n }\r\n tone({c,t,output,nodes},freq,duration,level,type='sine',endFreq=freq,at=0){\r\n  const osc=c.createOscillator(),gain=c.createGain(),start=t+at;osc.type=type;osc.frequency.setValueAtTime(freq,start);osc.frequency.exponentialRampToValueAtTime(Math.max(20,endFreq),start+duration);\r\n  gain.gain.setValueAtTime(.0001,start);gain.gain.exponentialRampToValueAtTime(Math.max(.0002,level),start+.008);gain.gain.exponentialRampToValueAtTime(.0001,start+duration);\r\n  osc.connect(gain).connect(output);osc.start(start);osc.stop(start+duration+.02);nodes.push(osc,gain);\r\n }\r\n noiseBurst({c,t,output,nodes},duration,level,low,high,at=0){\r\n  const src=this.sourceNoise(),filter=c.createBiquadFilter(),gain=c.createGain(),start=t+at;filter.type='bandpass';filter.frequency.setValueAtTime(low,start);filter.frequency.exponentialRampToValueAtTime(Math.max(30,high),start+duration);filter.Q.value=.7;\r\n  gain.gain.setValueAtTime(.0001,start);gain.gain.exponentialRampToValueAtTime(level,start+.012);gain.gain.exponentialRampToValueAtTime(.0001,start+duration);\r\n  src.connect(filter).connect(gain).connect(output);src.start(start);src.stop(start+duration+.02);nodes.push(src,filter,gain);\r\n }\r\n synth(kind){\r\n  const v=(duration,fn)=>this.voice(kind,duration,fn),pitch=()=>.94+Math.random()*.12,w=()=>.85+Math.random()*.3,j=()=>.85+Math.random()*.3;\r\n  switch(kind){\r\n   case 'flight-ready':return v(.22,x=>{this.tone(x,440,.18,.11,'sine',440);});\r\n   case 'flight-set':return v(.22,x=>{this.tone(x,660,.18,.11,'sine',660);});\r\n   case 'flight-go':return v(.42,x=>{this.tone(x,880,.3,.12,'triangle',1760);this.tone(x,1320,.24,.065,'sine',1760,.08);});\r\n   case 'flight-coin':return v(.22,x=>{this.tone(x,1234,.12,.065,'sine',1648);this.tone(x,1648,.12,.04,'sine',2093,.045);});\r\n   case 'flight-warning':return v(.36,x=>{this.tone(x,310,.1,.075,'triangle',360);this.tone(x,310,.1,.06,'triangle',420,.17);});\r\n   case 'flight-impact':return v(.36,x=>{this.noiseBurst(x,.24,.11,800,150);this.tone(x,145,.25,.1,'triangle',55);});\r\n   case 'flight-pickup':return v(.32,x=>{this.tone(x,540,.22,.08,'triangle',1080);this.tone(x,1080,.18,.045,'sine',1440,.09);});\r\n   case 'flight-stunt':return v(.6,x=>{for(const [i,hz] of [784,1046.5,1318.5,1568].entries())this.tone(x,hz,.22,.07,'sine',hz*1.004,i*.075);});\r\n   case 'flight-portal':return v(.65,x=>{this.noiseBurst(x,.55,.07,300,2800);this.tone(x,330,.5,.065,'sine',1320);});\r\n   case 'main-control':return v(.15,x=>{this.tone(x,220*pitch(),.11,.17,'triangle',85);this.noiseBurst(x,.065,.08,800,240);});\r\n   case 'mechanical':return v(.12,x=>{this.tone(x,960*pitch(),.07,.17,'triangle',460);this.noiseBurst(x,.065,.08,1800,650);});\r\n   case 'switch':return v(.18,x=>{this.noiseBurst(x,.08,.16,250,2200);this.tone(x,350,.13,.13,'triangle',180,.035);});\r\n   case 'dial':return v(.12,x=>{this.tone(x,420*pitch(),.085,.095,'square',300);this.noiseBurst(x,.045,.04,1700,850);});\r\n   case 'lever':return v(.25,x=>{this.noiseBurst(x,.16,.14,450,1100);this.tone(x,150,.18,.12,'sawtooth',90,.03);});\r\n   case 'electric':return v(.22,x=>{this.noiseBurst(x,.18,.025,2200,1400);this.tone(x,110,.18,.008,'sine',100);});\r\n   case 'bloop':return v(.2,x=>this.tone(x,260,.16,.045,'sine',95));\r\n   case 'signal':return v(.32,x=>{this.tone(x,660,.07,.018,'sine',660);this.tone(x,880,.07,.012,'sine',880,.17);});\r\n   case 'whiteboard':return v(.85,x=>{this.noiseBurst(x,.26,.035,1300,900);for(let i=0;i<4;i++){this.tone(x,1900+i*210,.09,.055,'sine',3300-i*190,.10+i*.16);this.noiseBurst(x,.07,.018,3200,1700,.11+i*.16);}});\r\n   case 'crt':return v(.16,x=>{this.tone(x,90,.13,.12,'sine',54);this.noiseBurst(x,.08,.045,2000,400);});\r\n   // MOM Inc. blaster: transient (timing) + body + sub weight + debris tail. Pitch/level jitter per layer so repeats do not fatigue.\r\n   case 'gun-arm':return v(.14,x=>{this.noiseBurst(x,.03,.1*j(),2600,900);this.tone(x,520*w(),.05,.08*j(),'square',380,.04);});\r\n   case 'gun-shot':return v(.28,x=>{this.noiseBurst(x,.025,.32*j(),3200,600);this.tone(x,180*w(),.14,.22*j(),'triangle',60);this.tone(x,70,.12,.14*j(),'sine',35);this.noiseBurst(x,.18,.07*j(),900,200,.03);});\r\n   case 'blast':case 'blast-big':{const big=kind==='blast-big';return v(big?.95:.65,x=>{\r\n    this.noiseBurst(x,.02,.34*j(),2800,700);if(big)this.noiseBurst(x,.03,.2*j(),2000,500,.06);\r\n    this.tone(x,140*w(),big?.5:.32,.26*j(),'sawtooth',38);this.tone(x,110*w(),big?.44:.28,.2*j(),'triangle',40);this.tone(x,big?48:55,big?.45:.3,.18*j(),'sine',big?24:28);\r\n    for(let i=0,n=big?5:4;i<n;i++)this.noiseBurst(x,.04,(.05+Math.random()*.04)*j(),1400,500,.12+Math.random()*.33);\r\n    if(big)this.noiseBurst(x,.3,.04*j(),3000,1500,.05);\r\n    this.window?.dispatchEvent?.(new CustomEvent('myr5:sfx-duck',{detail:{to:.45,seconds:.05,release:big?.5:.35}}));});}\r\n   case 'barrel-split':return v(.32,x=>{this.noiseBurst(x,.06,.14*j(),1800,400);this.tone(x,220*w(),.12,.12*j(),'triangle',90);this.noiseBurst(x,.08,.06*j(),4500,1200,.02);});\r\n   case 'ice':return v(.5,x=>{this.noiseBurst(x,.17,.16,5500,1700);this.tone(x,980*pitch(),.32,.08,'sine',460,.025);this.noiseBurst(x,.08,.045,4500,1000,.16);});\r\n   case 'jelly':return v(.46,x=>{this.noiseBurst(x,.28,.16,350,130);this.tone(x,180*pitch(),.32,.16,'sine',65);this.tone(x,95,.16,.075,'sine',45,.1);});\r\n   case 'water':return v(.65,x=>{this.noiseBurst(x,.44,.23,1900,250);this.tone(x,410,.23,.08,'sine',110,.04);this.noiseBurst(x,.22,.09,900,160,.2);});\r\n   case 'water-slosh':return v(.8,x=>{this.noiseBurst(x,.5,.13,320,105);this.noiseBurst(x,.4,.11,500,190,.2);this.tone(x,120,.55,.065,'sine',75,.08);});\r\n   case 'pond-slosh':return v(.34,x=>{this.noiseBurst(x,.27,.038,320,105);this.tone(x,120,.27,.018,'sine',75,.04);});\r\n   case 'grass':return v(.32,x=>{this.noiseBurst(x,.24,.065,2000,520);this.tone(x,1400*pitch(),.18,.025,'sine',1100,.06);});\r\n   case 'grass-tinkle':return v(.55,x=>{this.tone(x,1650*pitch(),.4,.028,'sine',1350);this.tone(x,2180*pitch(),.27,.018,'sine',1770,.07);});\r\n   case 'cogs':return v(.32,x=>{for(let i=0;i<3;i++){this.tone(x,350-i*65,.085,.12,'square',160-i*20,i*.085);this.noiseBurst(x,.055,.07,1500,480,i*.085);}});\r\n   case 'wood':return v(.45,x=>{this.noiseBurst(x,.32,.14,600,130);this.tone(x,145,.36,.14,'triangle',75);});\r\n   case 'wood-scrape':return v(.45,x=>{this.noiseBurst(x,.36,.13,900,180);this.tone(x,155,.3,.06,'triangle',95);});\r\n   case 'quilt':return v(.34,x=>{this.noiseBurst(x,.25,.10,1450,320);this.tone(x,260,.15,.055,'sine',185,.05);});\r\n   case 'drag':return v(.19,x=>{this.noiseBurst(x,.16,.045,1200,330);});\r\n   case 'transit':return v(1.5,x=>{this.noiseBurst(x,1.3,.18,180,5000);this.tone(x,66,1.25,.16,'sawtooth',840);this.tone(x,110,1.1,.11,'sine',1800,.12);});\r\n   case 'dialup':return v(2.6,x=>{for(let i=0;i<9;i++){this.tone(x,[350,440,610,250,820,530,1190,460,960][i],.2,.018,'sine',230+i*83,i*.25);this.noiseBurst(x,.14,.014,1400,4600,i*.25);}});\r\n  }\r\n }\r\n play(kind){\r\n  if(!RATE[kind]||this.muted||!this.volume||this.document?.hidden)return;\r\n  const now=this.now(),previous=this.last.get(kind)??-Infinity;if(now-previous<RATE[kind])return;this.last.set(kind,now);\r\n  const context=this.context,epoch=this.epoch;if(!context)return;\r\n  const start=()=>{if(this.context!==context||this.epoch!==epoch||this.now()-now>250||context.state!=='running'||this.document?.hidden||this.muted)return;\r\n   const variants=kind==='whiteboard'?null:this.samples.get(kind);if(variants?.length)this.playSample(kind,variants[Math.floor(Math.random()*variants.length)]);else this.synth(kind);\r\n  };\r\n  if(context.state==='running')start();else void context.resume().then(()=>{this.syncAmbience();start();}).catch(()=>{});\r\n  if(this.manifest&&SAMPLE[kind]&&this.sampleStatus.get(kind)!=='loading'&&this.sampleStatus.get(kind)!=='ready'&&Date.now()>=(this.sampleRetry.get(kind)||0))void this.loadSample(kind);\r\n }\r\n playSample(kind,buffer){\r\n  const duration=Math.min(buffer.duration,kind==='pond-slosh'?.38:3)/(kind==='main-control'?.76:1);\r\n  this.voice(kind,duration,({c,t,output,nodes})=>{\r\n   const source=c.createBufferSource(),gain=c.createGain();source.buffer=buffer;source.playbackRate.value=(kind==='main-control'?.76:1)*(.97+Math.random()*.06);\r\n   const level=kind==='blast'?.7:kind==='blast-big'?.75:kind==='gun-shot'?.6:kind==='pond-slosh'?.16:kind==='dialup'?.13:kind==='signal'?.09:kind==='electric'?.12:kind==='bloop'?.16:kind==='whiteboard'?.4:kind==='grass-tinkle'?.04:kind==='grass'?.4:kind==='transit'?.72:.68;\r\n   gain.gain.setValueAtTime(.0001,t);gain.gain.linearRampToValueAtTime(level,t+.008);gain.gain.setValueAtTime(level,t+Math.max(.009,duration-.05));gain.gain.linearRampToValueAtTime(.0001,t+duration);\r\n   source.connect(gain).connect(output);source.start(t);source.stop(t+duration+.01);nodes.push(source,gain);\r\n  });\r\n }\r\n async loadManifest(){\r\n  this.manifestPending=(async()=>{try{const response=await fetch('/audio/sfx/manifest.json');if(response.ok){const candidate=await response.json();if(candidate?.version===1&&candidate.cues&&typeof candidate.cues==='object'&&Object.values(candidate.cues).some(paths=>Array.isArray(paths)&&paths.some(path=>typeof path==='string'&&/^\\/audio\\/sfx\\/[a-z0-9-]+\\.mp3$/i.test(path))))this.manifest=candidate;}}catch{}\r\n   if(this.manifest&&this.context){for(const kind of Object.keys(SAMPLE))void this.loadSample(kind);}\r\n   else{this.manifestRetryAt=Date.now()+15_000;this.manifestPending=null;}\r\n   return this.manifest;})();\r\n  return this.manifestPending;\r\n }\r\n async loadSample(kind){\r\n  this.sampleStatus.set(kind,'loading');\r\n  try{const sources=this.manifest?.cues?.[kind]||this.manifest?.cues?.[SAMPLE[kind]];const paths=(Array.isArray(sources)?sources:[sources]).filter(path=>typeof path==='string'&&/^\\/audio\\/sfx\\/[a-z0-9-]+\\.mp3$/i.test(path)).slice(0,2);\r\n   const decoded=await Promise.all(paths.map(async path=>{const url=path.startsWith('/')?path:`/audio/sfx/${path}`;const response=await fetch(url);if(!response.ok)throw Error('Unavailable');const data=await response.arrayBuffer();if(data.byteLength>2_000_000)throw Error('Too large');let buffer=await this.context.decodeAudioData(data);if(buffer.duration>(['pod-hum','breeze','waterfall'].includes(kind)?30:5))throw Error('Too long');if(kind==='waterfall')buffer=seamlessLoopBuffer(this.context,buffer);return buffer;}));\r\n   if(!decoded.length)throw Error('Unavailable');this.samples.set(kind,decoded);this.sampleStatus.set(kind,'ready');if(['pod-hum','breeze','waterfall'].includes(kind))this.syncAmbience(true);\r\n  }catch{this.sampleStatus.set(kind,'unavailable');this.sampleRetry.set(kind,Date.now()+15_000);}\r\n }\r\n stopAll(){this.epoch++;for(const voice of [...this.voices])voice.stop();for(const clean of [...this.tails])clean();this.stopAmbience(true);}\r\n stopAmbience(immediate=false){\r\n  const active=this.ambience;if(!active)return;this.ambience=null;\r\n  const {gain,sources,nodes}=active,t=this.context.currentTime,delay=immediate?0:.12;\r\n  gain.gain.cancelScheduledValues(t);gain.gain.setValueAtTime(gain.gain.value,t);gain.gain.linearRampToValueAtTime(0,t+delay);\r\n  setTimeout(()=>{for(const node of sources)try{node.stop();}catch{}for(const node of nodes)try{node.disconnect();}catch{}},Math.ceil(delay*1000)+15);\r\n }\r\n syncAmbience(force=false){\r\n  if(!this.context||this.context.state!=='running')return;\r\n  const body=this.document?.body,meditation=this.document?.querySelector?.('.meditation-panel[open] [data-meditation-scene]:not([hidden])'),blocked=(!meditation&&this.document?.querySelector?.('.meditation-panel[open]'))||this.document?.querySelector?.('dialog[open]:not(#portalWorkoutHome):not(#portalMenu):not(#settings):not(.meditation-panel)')||body?.dataset?.tracking==='true'||body?.dataset?.cameraWorkout==='true'||(!meditation&&body?.dataset?.shipView==='true')||body?.dataset?.screen==='rest';\r\n  const scene=this.document?.hidden||this.muted||!this.volume||blocked?'off':meditation?'meditation':this.scene;\r\n  const key=scene==='portal'?`portal:${this.board}`:scene;\r\n  if(!force&&key===this.ambienceKey&&this.ambience)return;\r\n  this.stopAmbience();this.ambienceKey=key;if(scene==='off')return;\r\n  const c=this.context,t=c.currentTime,gain=c.createGain(),sources=[],nodes=[gain];gain.gain.setValueAtTime(0,t);gain.gain.linearRampToValueAtTime(1,t+.18);gain.connect(this.duck);\r\n  const bed=c.createGain();bed.gain.value=.12;bed.connect(gain);nodes.push(bed);\r\n  if(scene==='meditation'){const buffer=this.samples.get('waterfall')?.[0],rush=buffer?c.createBufferSource():this.sourceNoise(),filter=c.createBiquadFilter(),waterGain=c.createGain();if(buffer)rush.buffer=buffer;rush.loop=true;filter.type='lowpass';filter.frequency.value=3600;waterGain.gain.value=buffer?.28:.09;rush.connect(filter).connect(waterGain).connect(gain);rush.start();sources.push(rush);nodes.push(rush,filter,waterGain);this.ambience={gain,sources,nodes};return;}\r\n  const grass=scene==='portal'&&this.board==='grass',water=scene==='portal'&&['water','pond'].includes(this.board),breeze=this.samples.get('breeze')?.[0];\r\n  if(!grass&&!water){const humBuffer=this.samples.get('pod-hum')?.[0];\r\n   if(humBuffer){const hum=c.createBufferSource();hum.buffer=humBuffer;hum.loop=true;hum.connect(bed);hum.start();sources.push(hum);nodes.push(hum);}\r\n   else{const hum=c.createOscillator();hum.type='sine';hum.frequency.value=scene==='portal'?61:54;hum.connect(bed);hum.start();sources.push(hum);nodes.push(hum);}}\r\n  if(grass&&breeze){const wind=c.createBufferSource(),windGain=c.createGain();wind.buffer=breeze;wind.loop=true;windGain.gain.value=.18;wind.connect(windGain).connect(gain);wind.start();sources.push(wind);nodes.push(wind,windGain);}\r\n  else{const airflow=this.sourceNoise(),filter=c.createBiquadFilter(),airGain=c.createGain();airflow.loop=true;filter.type='lowpass';filter.frequency.value=grass?350:water?580:190;airGain.gain.value=grass?.09:water?.12:.018;airflow.connect(filter).connect(airGain).connect(gain);airflow.start();sources.push(airflow);nodes.push(airflow,filter,airGain);}\r\n  this.ambience={gain,sources,nodes};\r\n }\r\n dispose(){this.abort?.abort();this.stopAll();void this.context?.close();this.context=null;this.started=false;}\r\n}\r\n\r\nexport const physicalSound=new PhysicalSound();\r\n","audio/sfx/credits.html":"<!doctype html><html lang=\"en\"><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script><title>Sound credits</title><style>body{font:18px system-ui;max-width:48rem;margin:3rem auto;padding:1rem;background:#101821;color:#e9f0f7}a{color:#9ee4ff}li{margin:1rem 0}</style><h1>Sound credits</h1><p>Effects are released under <a href=\"https://creativecommons.org/publicdomain/zero/1.0/\">CC0 1.0</a>. The meditation waterfall is adapted from Stream Sounds by kurt, licensed under <a href=\"https://creativecommons.org/licenses/by/3.0/\">CC BY 3.0</a>, trimmed, filtered, normalized and crossfaded into a loop. Other effects are CC0. Material recordings were edited into short cues; wind, teleport, dial-up and CRT effects include sound design. Coach voices are local variants of the existing eSpeak NG generated clips. The app code remains under its existing AGPL license.</p><ul><li>Kenney — <a href=\"https://kenney.nl/assets/interface-sounds\">interface</a> (CC0-1.0)</li><li>Kenney — <a href=\"https://kenney.nl/assets/impact-sounds\">impact</a> (CC0-1.0)</li><li>qubodup — <a href=\"https://opengameart.org/content/7-mechanical-clicks-and-buzzes\">mechanical</a> (CC0-1.0)</li><li>bart — <a href=\"https://opengameart.org/content/ice-spells\">ice</a> (CC0-1.0)</li><li>Independent.nu — <a href=\"https://opengameart.org/content/8-wet-squish-slurp-impacts\">jelly</a> (CC0-1.0)</li><li>ezwa — <a href=\"https://opengameart.org/content/6-short-water-splashes\">water</a> (CC0-1.0)</li><li>transitking — <a href=\"https://opengameart.org/content/water-waves\">waves</a> (CC0-1.0)</li><li>qubodup — <a href=\"https://opengameart.org/content/20-rustles-dry-leaves\">grass</a> (CC0-1.0)</li><li>Iochi Glaucus — <a href=\"https://opengameart.org/content/fabric-rustling\">cloth</a> (CC0-1.0)</li><li>Luke.RUSTLTD — <a href=\"https://opengameart.org/content/wind1\">wind</a> (CC0-1.0)</li><li>LEGIT Audio — <a href=\"https://opengameart.org/content/the-shop\">hum</a> (CC0-1.0)</li><li>Ogrebane — <a href=\"https://opengameart.org/content/teleport-spell\">portal</a> (CC0-1.0)</li><li>MOM Inc. — <a href=\"/source.json\">original</a> (CC0-1.0)</li><li>kurt — <a href=\"https://opengameart.org/content/stream-sounds\">waterfall</a> (CC-BY-3.0)</li><li>Independent.nu — <a href=\"https://opengameart.org/content/35-wooden-crackshitsdestructions\">wood</a> (CC0-1.0)</li></ul><p><a href=\"manifest.json\">Asset provenance and checksums</a> · <a href=\"/source.json\">App source</a> · <a href=\"/pose\">Back to Coach</a></p></html>","audio/sfx/manifest.json":"{\r\n  \"version\": 1,\r\n  \"license\": \"CC0-1.0 AND CC-BY-3.0\",\r\n  \"cues\": {\r\n    \"mechanical\": [\r\n      \"/audio/sfx/mechanical-click-1.mp3\",\r\n      \"/audio/sfx/mechanical-click-2.mp3\"\r\n    ],\r\n    \"switch\": [\r\n      \"/audio/sfx/metal-switch-1.mp3\",\r\n      \"/audio/sfx/metal-switch-2.mp3\"\r\n    ],\r\n    \"ice\": [\r\n      \"/audio/sfx/ice-crack-1.mp3\",\r\n      \"/audio/sfx/ice-crack-2.mp3\"\r\n    ],\r\n    \"jelly\": [\r\n      \"/audio/sfx/jelly-squish-1.mp3\",\r\n      \"/audio/sfx/jelly-squish-2.mp3\"\r\n    ],\r\n    \"water\": [\r\n      \"/audio/sfx/water-splash-1.mp3\",\r\n      \"/audio/sfx/water-splash-2.mp3\"\r\n    ],\r\n    \"water-slosh\": [\r\n      \"/audio/sfx/water-slosh.mp3\"\r\n    ],\r\n    \"grass\": [\r\n      \"/audio/sfx/grass-rustle-1.mp3\",\r\n      \"/audio/sfx/grass-rustle-2.mp3\"\r\n    ],\r\n    \"grass-tinkle\": [\r\n      \"/audio/sfx/grass-tinkle.mp3\"\r\n    ],\r\n    \"quilt\": [\r\n      \"/audio/sfx/cloth-rustle-1.mp3\",\r\n      \"/audio/sfx/cloth-rustle-2.mp3\"\r\n    ],\r\n    \"wood\": [\r\n      \"/audio/sfx/wood-tap-1.mp3\",\r\n      \"/audio/sfx/wood-tap-2.mp3\"\r\n    ],\r\n    \"wood-scrape\": [\r\n      \"/audio/sfx/wood-scrape.mp3\"\r\n    ],\r\n    \"cogs\": [\r\n      \"/audio/sfx/cogs-ratchet-1.mp3\",\r\n      \"/audio/sfx/cogs-ratchet-2.mp3\"\r\n    ],\r\n    \"crt\": [\r\n      \"/audio/sfx/crt-tap.mp3\"\r\n    ],\r\n    \"transit\": [\r\n      \"/audio/sfx/portal-transit.mp3\"\r\n    ],\r\n    \"dialup\": [\r\n      \"/audio/sfx/dialup.mp3\"\r\n    ],\r\n    \"pod-hum\": [\r\n      \"/audio/sfx/pod-hum.mp3\"\r\n    ],\r\n    \"whiteboard\": [\r\n      \"/audio/sfx/whiteboard-erase-marker.mp3\"\r\n    ],\r\n    \"electric\": [\r\n      \"/audio/sfx/electric-static.mp3\"\r\n    ],\r\n    \"bloop\": [\r\n      \"/audio/sfx/menu-bloop.mp3\"\r\n    ],\r\n    \"signal\": [\r\n      \"/audio/sfx/signal-beeps.mp3\"\r\n    ],\r\n    \"waterfall\": [\r\n      \"/audio/sfx/meditation-waterfall.mp3\"\r\n    ],\r\n    \"breeze\": [\r\n      \"/audio/sfx/gentle-breeze.mp3\"\r\n    ],\r\n    \"dial\": [\r\n      \"/audio/sfx/mechanical-click-1.mp3\",\r\n      \"/audio/sfx/mechanical-click-2.mp3\"\r\n    ],\r\n    \"lever\": [\r\n      \"/audio/sfx/metal-switch-1.mp3\",\r\n      \"/audio/sfx/metal-switch-2.mp3\"\r\n    ]\r\n  },\r\n  \"assets\": [\r\n    {\r\n      \"url\": \"/audio/sfx/mechanical-click-1.mp3\",\r\n      \"source\": \"mechanical\",\r\n      \"master\": \"mechanical/mechanical/mechanical_button-01.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.4,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.365:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"5049c0c2f3e297ffd05544a0150a24ba0eebc9e7f5bcb931e99ff83721f4a5ef\",\r\n      \"masterSha256\": \"c763eb6d5177c37c422965b47ece95cb824e795d0df3b3f83ae9ef53db161a3e\",\r\n      \"bytes\": 4114\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/mechanical-click-2.mp3\",\r\n      \"source\": \"mechanical\",\r\n      \"master\": \"mechanical/mechanical/mechanical_clicks-05.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.42,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.385:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"e134272539997f20e031b3050f1ffae990b7f628a482652ae5f63fb97abc2e49\",\r\n      \"masterSha256\": \"59cc4dd84b29ebc147719677bde1256f97222c7e91a398c3b87a401a56c195b6\",\r\n      \"bytes\": 4306\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/metal-switch-1.mp3\",\r\n      \"source\": \"interface\",\r\n      \"master\": \"kenney-interface/Audio/switch_001.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.4,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.365:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"2f45c9891224fef39b30ed048a37de85ef31735b5c69a1bb01b226dd4ae8c0e7\",\r\n      \"masterSha256\": \"3b74efad87f1e69dbd1d05c5ad952ab90859b5a3d11da6ec5bd79b4f03b42afa\",\r\n      \"bytes\": 3884\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/metal-switch-2.mp3\",\r\n      \"source\": \"interface\",\r\n      \"master\": \"kenney-interface/Audio/toggle_001.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.4,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.365:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"2c5a63c961a7c632d8117df36236f98dfbedf522648b49779d4559b32392a5ff\",\r\n      \"masterSha256\": \"ca1d2dde5f0b286abac4f070e23edab8f30927c8a533665cdf2ac6492a415e49\",\r\n      \"bytes\": 1772\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/ice-crack-1.mp3\",\r\n      \"source\": \"ice\",\r\n      \"master\": \"ice/ice.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.85,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.815:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"d13b487d1b767a83b5a9d010c23cb0c0a0ef3f670434871a6aaf2adc8dfc493c\",\r\n      \"masterSha256\": \"c3039c864d056d0e4951b84b96a7bb0044ca8cb58aeae514fc256145872b6a94\",\r\n      \"bytes\": 7532\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/ice-crack-2.mp3\",\r\n      \"source\": \"ice\",\r\n      \"master\": \"ice/coldsnap.wav\",\r\n      \"seek\": 0.1,\r\n      \"maxSeconds\": 0.85,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.815:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"e883b3102cd08aeef403ee08734b123241fb5874cee1a3ee8b29d1e4bc59f131\",\r\n      \"masterSha256\": \"090977264d65d90a50b04e9f0c1c4e02fb194e5b50d9fe244f0079a4da54158c\",\r\n      \"bytes\": 7532\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/jelly-squish-1.mp3\",\r\n      \"source\": \"jelly\",\r\n      \"master\": \"squish/impsplat/impactsplat07.mp3.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.55,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.515:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"f3aef20cf320c5475141bdb34435952f5399abde8e09b42b9315c0c5e654fc97\",\r\n      \"masterSha256\": \"adae57b90523d754a7b0221876d1c3e4c7b90d5ded732af4895c5b8d0489b640\",\r\n      \"bytes\": 4844\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/jelly-squish-2.mp3\",\r\n      \"source\": \"jelly\",\r\n      \"master\": \"squish/impsplat/impactsplat08.mp3.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.66,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.625:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"8518fda10d2d926435c7c2e6652f401f9fb7828631054731d1777c4b979e60d3\",\r\n      \"masterSha256\": \"08620cd7dbeaa27cff28a5295846ee9079be7ec6c83725a4af568b9dcc0d045d\",\r\n      \"bytes\": 5996\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/water-splash-1.mp3\",\r\n      \"source\": \"water\",\r\n      \"master\": \"splash/ezwa-water_splash/water_splash-02.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.42,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.385:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"c6793cc0e61289045c1f1975164bfa526812eb8f729cdff7b8fbe311302b1b65\",\r\n      \"masterSha256\": \"c0c2c6e6fa113138a71b9966a456bea844dbb955f0ed7b95a01777cb62fd7461\",\r\n      \"bytes\": 4216\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/water-splash-2.mp3\",\r\n      \"source\": \"water\",\r\n      \"master\": \"splash/ezwa-water_splash/water_splash-03.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.72,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.6849999999999999:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"0e8f6004a0d57ce5958c4b2697101a0e9313e117c88a1edb071d64894e78db12\",\r\n      \"masterSha256\": \"a22c5cac675fa60aaff8d907acf7627f1e9dcc337798ff4eb34f2f8e7a4c9e2e\",\r\n      \"bytes\": 6520\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/water-slosh.mp3\",\r\n      \"source\": \"waves\",\r\n      \"master\": \"water-slosh.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 2.5,\r\n      \"filters\": \"lowpass=f=2500,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=2.465:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"b99ff74a3b4f86951b6a3ab06acbf81edac3b8e3d9bf2559b87671326e77f7b3\",\r\n      \"masterSha256\": \"6c5b59562bf9b00e77f7bd8d6550912b6326e190ce97e9b8c1a1faa887bb4fac\",\r\n      \"bytes\": 16940\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/grass-rustle-1.mp3\",\r\n      \"source\": \"grass\",\r\n      \"master\": \"leaves/rustle/rustle01.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.7,\r\n      \"filters\": \"lowpass=f=4000,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.6649999999999999:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"82f4c372e46df9443577aa1c375251fe4b1621fcd3aeb227236ea28800a9aec7\",\r\n      \"masterSha256\": \"9c5da19eb8d37b59890c0be1171c40fe4dd16177e7a97558a156c1036b7bed05\",\r\n      \"bytes\": 6380\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/grass-rustle-2.mp3\",\r\n      \"source\": \"grass\",\r\n      \"master\": \"leaves/rustle/rustle02.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.7,\r\n      \"filters\": \"lowpass=f=4000,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.6649999999999999:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"7e1592115b5c6582a1bc5b60bbd98fef45d2cdac12c0ce3b0e32933f77fe5a4e\",\r\n      \"masterSha256\": \"1e01e752b2165d78482bd91ef8b859541601ae4de6b7cf6178e935cce0036c69\",\r\n      \"bytes\": 6380\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/grass-tinkle.mp3\",\r\n      \"source\": \"interface\",\r\n      \"master\": \"kenney-interface/Audio/glass_001.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.6,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.565:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"996f92a4974ae551a8d7f58873f0e473cadaa50ee57cc752f07cd376aa1fa7c6\",\r\n      \"masterSha256\": \"183eebee20eb0a532b0b85104d139cbf2673eb66d95c3cc733ac9ea98362e7e8\",\r\n      \"bytes\": 2924\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/cloth-rustle-1.mp3\",\r\n      \"source\": \"cloth\",\r\n      \"master\": \"cloth.ogg\",\r\n      \"seek\": 0.5,\r\n      \"maxSeconds\": 0.65,\r\n      \"filters\": \"lowpass=f=4000,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.615:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"45405bd70643045691627623376ca7eb91e8a886eeb3d65a84c4d003a6e2bd03\",\r\n      \"masterSha256\": \"897c0937041afdf3581427b80a7546f99d219d0008e12ff5500c2afdfa4b7a1e\",\r\n      \"bytes\": 5996\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/cloth-rustle-2.mp3\",\r\n      \"source\": \"cloth\",\r\n      \"master\": \"cloth.ogg\",\r\n      \"seek\": 2,\r\n      \"maxSeconds\": 0.65,\r\n      \"filters\": \"lowpass=f=4000,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.615:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"2760c5fa1d97cf22726edc9b3b275a264145ce90a7ba36e337364cbba90fe3e9\",\r\n      \"masterSha256\": \"897c0937041afdf3581427b80a7546f99d219d0008e12ff5500c2afdfa4b7a1e\",\r\n      \"bytes\": 5996\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/wood-tap-1.mp3\",\r\n      \"source\": \"impact\",\r\n      \"master\": \"kenney-impact/Audio/impactWood_light_000.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.45,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.41500000000000004:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"72b11ecfe7ca8972a7f48feb0b3cb0a19244ca7f9ceb9d683aa5cc774874ec98\",\r\n      \"masterSha256\": \"eb45e35b4bed784d3f120de4afec4b0d6ac09d9e143cce8cfcb7d739e1b1f3c3\",\r\n      \"bytes\": 2732\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/wood-tap-2.mp3\",\r\n      \"source\": \"impact\",\r\n      \"master\": \"kenney-impact/Audio/impactWood_light_001.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.45,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.41500000000000004:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"7d4a4b60ddfd70d71026b1b21a745d6be2281fe1f6bfaf7fc4c645bf3dfaf3c2\",\r\n      \"masterSha256\": \"4b76bf3ccc8e60d19188f3165b778a7817786faa6887c55d9049bcbeef3b425f\",\r\n      \"bytes\": 2732\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/wood-scrape.mp3\",\r\n      \"source\": \"wood\",\r\n      \"master\": \"wood/wood_impact/crack01.mp3.flac\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.65,\r\n      \"filters\": \"lowpass=f=3000,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.615:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"681a12a5bbcf77f0814473d6339e5d009e3194092801bbd87e48b2c4a8a444f3\",\r\n      \"masterSha256\": \"a25993adc8c2bbfdd80490980d83c27a587f2d3452ee4e5bd784f02c082ec448\",\r\n      \"bytes\": 5996\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/cogs-ratchet-1.mp3\",\r\n      \"source\": \"impact\",\r\n      \"master\": \"kenney-impact/Audio/impactMetal_light_000.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.4,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.365:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"7c3dda03cb42530aa0a69e63070307b63722d55632aa3e5e5f8a4b32dddc435b\",\r\n      \"masterSha256\": \"33b5e6e37c6e9d54e07bf5a89b12c76e879f40c1ea83cdd82714df1d6f9fec6d\",\r\n      \"bytes\": 3692\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/cogs-ratchet-2.mp3\",\r\n      \"source\": \"impact\",\r\n      \"master\": \"kenney-impact/Audio/impactMetal_light_001.ogg\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.4,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.365:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"ea9e9443cdcc64439ef54334dfc128fbc42ce68dc6edb86537bbd3c3c488997d\",\r\n      \"masterSha256\": \"fba69c467ddd85da7b1d82e259f936b876d9ba6a3d6f1b24bdd10468e46bfe08\",\r\n      \"bytes\": 2732\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/crt-tap.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"crt-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.12,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.08499999999999999:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"1fc6d11eb35ca242d66d7f0f19503a1c986b19b58da61c324d8214fcd5aa340a\",\r\n      \"masterSha256\": \"30bbf4295902825adfaf0d5666920824a8fdf3321d9a18e033dd511344625835\",\r\n      \"bytes\": 1580\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/portal-transit.mp3\",\r\n      \"source\": \"portal\",\r\n      \"master\": \"teleport.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 2,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=1.965:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"a200199794750c4620f8fc61496188764d1e6c27c83f858725ea6dc27744ba88\",\r\n      \"masterSha256\": \"23fe938e7e82931227196bf6b2978ad9154f627af26623acf52958d12125f3b8\",\r\n      \"bytes\": 13676\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/dialup.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"dialup-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 1.8,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=1.7650000000000001:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"a3a176ed5e6fd26b74c82b62b85137bda4d7c231a83de711c2b191bd8d362a2e\",\r\n      \"masterSha256\": \"8e764074a112044a074d152b9668f456e456a2c81514444f2447c87bc7801138\",\r\n      \"bytes\": 15020\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/pod-hum.mp3\",\r\n      \"source\": \"hum\",\r\n      \"master\": \"shop/TheShopCollection_convenience_store_drinks_fridge_drone.wav\",\r\n      \"seek\": 1,\r\n      \"maxSeconds\": 12,\r\n      \"filters\": \"highpass=f=50,lowpass=f=1100,loudnorm=I=-22:TP=-3:LRA=7\",\r\n      \"loopCrossfadeSeconds\": 0.5,\r\n      \"sha256\": \"2c1d783addb9d45199626cbc931e60fbb92a9d07110900cf68adf6f0a4d34cca\",\r\n      \"masterSha256\": \"222545244c0eed4bb924a3616d1103156d942776681634698f7fa3b11fe99b9d\",\r\n      \"bytes\": 80300\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/whiteboard-erase-marker.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"whiteboard-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.9,\r\n      \"filters\": \"highpass=f=300,lowpass=f=4200,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.865:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"f43a61da92082b46783739debadee7d048ad89d1c170b1891cca21d8b9982b35\",\r\n      \"masterSha256\": \"d9e768a87004c992dc4260d44342039c982d418e53ad25e4f47a03a793758181\",\r\n      \"bytes\": 7916\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/electric-static.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"electric-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.25,\r\n      \"filters\": \"highpass=f=1400,lowpass=f=4200,loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.215:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"be024f00c395f157a5af227f0eca594a6d13c9a95129567f81919a53fae54da2\",\r\n      \"masterSha256\": \"ee5be1d09a9142ac8d78b80ce45b4c69fe446ce39cbde2068496c7d7ef807fa3\",\r\n      \"bytes\": 2732\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/menu-bloop.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"bloop-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.22,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.185:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"3ac1c6d17a0bd6ac6c50ef303fdb888cbc49cb642a7a604ab06d725843667017\",\r\n      \"masterSha256\": \"662f7caeead7ce14adf60d17904885670766fe212e28176298df056b7b7a26e7\",\r\n      \"bytes\": 2540\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/signal-beeps.mp3\",\r\n      \"source\": \"original\",\r\n      \"master\": \"signal-original.wav\",\r\n      \"seek\": 0,\r\n      \"maxSeconds\": 0.35,\r\n      \"filters\": \"loudnorm=I=-22:TP=-3:LRA=7,afade=t=in:st=0:d=0.004,afade=t=out:st=0.31499999999999995:d=0.035\",\r\n      \"loopCrossfadeSeconds\": null,\r\n      \"sha256\": \"0bbd26949f9a07ebf88b1caad8192a19aa4507f2506499ea433ee8c03c1967cf\",\r\n      \"masterSha256\": \"97a5c16539be0ee5262d0b4763a8a5b22e68442f93458219d793bb06db254e22\",\r\n      \"bytes\": 3500\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/meditation-waterfall.mp3\",\r\n      \"source\": \"waterfall\",\r\n      \"master\": \"stream-waterfall/stream-waterfall/waterfall1.ogg\",\r\n      \"seek\": 1,\r\n      \"maxSeconds\": 12,\r\n      \"filters\": \"highpass=f=80,lowpass=f=5500,loudnorm=I=-22:TP=-3:LRA=7\",\r\n      \"loopCrossfadeSeconds\": 0.5,\r\n      \"sha256\": \"ff51b4796ae6cde1cdffe289580211b0a943c8c3665bfe8c2c7a7699bca413b4\",\r\n      \"masterSha256\": \"f6c83c65380f3778d068329f7027b7970555d6441c505b9a111f945518030ded\",\r\n      \"bytes\": 92780\r\n    },\r\n    {\r\n      \"url\": \"/audio/sfx/gentle-breeze.mp3\",\r\n      \"source\": \"wind\",\r\n      \"master\": \"wind.wav\",\r\n      \"seek\": 2,\r\n      \"maxSeconds\": 12,\r\n      \"filters\": \"lowpass=f=2300,loudnorm=I=-22:TP=-3:LRA=7\",\r\n      \"loopCrossfadeSeconds\": 0.5,\r\n      \"sha256\": \"2de4dce0fe6cab422050c4acbdf961454bd1fea0ca9508bb2bf6ed374312b081\",\r\n      \"masterSha256\": \"a2856f794fbf77e05f8241dc4adeade7f62fa21c3c9dc49f7725b6c6218060c3\",\r\n      \"bytes\": 92780\r\n    }\r\n  ],\r\n  \"sources\": [\r\n    {\r\n      \"id\": \"interface\",\r\n      \"author\": \"Kenney\",\r\n      \"url\": \"https://kenney.nl/assets/interface-sounds\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"impact\",\r\n      \"author\": \"Kenney\",\r\n      \"url\": \"https://kenney.nl/assets/impact-sounds\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"mechanical\",\r\n      \"author\": \"qubodup\",\r\n      \"url\": \"https://opengameart.org/content/7-mechanical-clicks-and-buzzes\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"ice\",\r\n      \"author\": \"bart\",\r\n      \"url\": \"https://opengameart.org/content/ice-spells\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"jelly\",\r\n      \"author\": \"Independent.nu\",\r\n      \"url\": \"https://opengameart.org/content/8-wet-squish-slurp-impacts\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"water\",\r\n      \"author\": \"ezwa\",\r\n      \"url\": \"https://opengameart.org/content/6-short-water-splashes\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"waves\",\r\n      \"author\": \"transitking\",\r\n      \"url\": \"https://opengameart.org/content/water-waves\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"grass\",\r\n      \"author\": \"qubodup\",\r\n      \"url\": \"https://opengameart.org/content/20-rustles-dry-leaves\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"cloth\",\r\n      \"author\": \"Iochi Glaucus\",\r\n      \"url\": \"https://opengameart.org/content/fabric-rustling\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"wind\",\r\n      \"author\": \"Luke.RUSTLTD\",\r\n      \"url\": \"https://opengameart.org/content/wind1\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"hum\",\r\n      \"author\": \"LEGIT Audio\",\r\n      \"url\": \"https://opengameart.org/content/the-shop\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"portal\",\r\n      \"author\": \"Ogrebane\",\r\n      \"url\": \"https://opengameart.org/content/teleport-spell\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"original\",\r\n      \"author\": \"MOM Inc.\",\r\n      \"url\": \"/source.json\",\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    {\r\n      \"id\": \"waterfall\",\r\n      \"author\": \"kurt\",\r\n      \"url\": \"https://opengameart.org/content/stream-sounds\",\r\n      \"license\": \"CC-BY-3.0\"\r\n    },\r\n    {\r\n      \"id\": \"wood\",\r\n      \"author\": \"Independent.nu\",\r\n      \"url\": \"https://opengameart.org/content/35-wooden-crackshitsdestructions\",\r\n      \"license\": \"CC0-1.0\"\r\n    }\r\n  ]\r\n}\r\n","audio/sound-ui.mjs":"import {physicalSound} from './physical-sound.mjs';\r\n\r\nconst BOARD_CUES={quilt:['quilt','quilt'],ice:['ice','ice'],jelly:['jelly','jelly'],water:['water','water-slosh'],pond:['water','pond-slosh'],grass:['grass','grass'],cogs:['cogs','cogs'],wood:['wood','wood-scrape']};\r\nconst ELECTRIC='#settings,#portalMenu,#remindersPanel,.reminders-computer-panel,.reminders-computer-screen';\r\nconst INTERNAL='dialog:not(#settings):not(#portalMenu):not(#portalWorkoutHome),.creature-editor,.handborne-app';\r\nexport function surfaceCue(el){\r\n if(globalThis.location?.pathname?.startsWith('/creature/')||el?.closest?.('#library,[data-route=\"customizeCoach\"]'))return 'mechanical';\r\n if(globalThis.location?.pathname?.startsWith('/war-room/')||globalThis.location?.pathname?.startsWith('/handborne/'))return 'bloop';\r\n if(el?.closest?.(ELECTRIC))return 'electric';\r\n if(el?.closest?.(INTERNAL)||globalThis.window?.parent!==globalThis.window)return 'bloop';\r\n return el?.closest?.('#start,#coachDock,.control-board')?'main-control':'mechanical';\r\n}\r\nconst CONTROL='button,select,input,summary,[role=\"button\"],[role=\"slider\"],a';\r\n\r\nexport function mountPhysicalSoundUI(){\r\n const sound=physicalSound.mount(),settings=document.getElementById('settings'),abort=new AbortController(),signal=abort.signal;\r\n if(settings){\r\n  const group=document.createElement('details');group.className='settings-group physical-sound-settings';\r\n  group.innerHTML='<summary>Sound effects</summary><div class=\"physical-sound-controls\"><button type=\"button\" data-sound-mute aria-pressed=\"false\">Mute effects</button><label>Effects volume <input type=\"range\" min=\"0\" max=\"100\" step=\"1\" data-sound-volume aria-label=\"Effects volume\"></label><output data-sound-value></output><p>Pod, grimoire, and controls</p><a href=\"/audio/sfx/credits.html\">Sound credits</a></div>';\r\n  settings.querySelector('.voice-row')?.closest('details')?.after(group);\r\n  const mute=group.querySelector('[data-sound-mute]'),volume=group.querySelector('[data-sound-volume]'),value=group.querySelector('[data-sound-value]');\r\n  const paint=()=>{mute.setAttribute('aria-pressed',String(sound.muted));mute.textContent=sound.muted?'Unmute effects':'Mute effects';volume.value=String(Math.round(sound.volume*100));value.value=`${Math.round(sound.volume*100)}%`;};\r\n  mute.addEventListener('click',()=>{sound.setMuted(!sound.muted);if(!sound.muted)sound.play('switch');},{signal});\r\n  volume.addEventListener('input',()=>{sound.setVolume(Number(volume.value)/100);paint();sound.play('electric');},{signal});\r\n  window.addEventListener('myr5:sound-settings',paint,{signal});paint();\r\n  const style=document.createElement('style');style.textContent='.physical-sound-controls{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:10px}.physical-sound-controls label{display:flex;align-items:center;gap:8px}.physical-sound-controls input{width:min(180px,38vw)}.physical-sound-controls p{width:100%;margin:0;opacity:.7}.physical-sound-controls a{font-size:.85em}';document.head.append(style);\r\n  signal.addEventListener('abort',()=>{group.remove();style.remove();},{once:true});\r\n }\r\n const routeControl=(event,doc)=>{\r\n  const target=event.target;if(target?.nodeType!==1)return;\r\n  const el=target.closest(CONTROL);if(!el||el.disabled||el.closest('.physical-sound-settings')||el.matches('.board-marker,[data-room-board],#exerciseDial,#difficultySlider,#harder,#easier,#toggleVoice')||el.id==='portalOverlay')return;\r\n  if(el.matches('input[type=\"range\"],input[type=\"color\"],select'))return;\r\n  sound.play(el.matches('[role=\"switch\"]')?'switch':surfaceCue(el));\r\n };\r\n function bindDocument(doc){\r\n  if(!doc||doc.__myr5SoundBound)return;doc.__myr5SoundBound=true;\r\n  doc.addEventListener('click',event=>routeControl(event,doc),{signal});\r\n  doc.addEventListener('input',event=>{const el=event.target;if(el?.closest?.('.physical-sound-settings'))return;if(el?.matches?.('input[type=\"range\"],input[type=\"color\"]'))sound.play(['mechanical','main-control'].includes(surfaceCue(el))?'dial':surfaceCue(el));},{signal});\r\n  doc.addEventListener('change',event=>{const el=event.target;if(el?.closest?.('.physical-sound-settings'))return;if(el?.matches?.('select,input[type=\"checkbox\"],input[type=\"radio\"]'))sound.play(['mechanical','main-control'].includes(surfaceCue(el))?'switch':surfaceCue(el));},{signal});\r\n  doc.addEventListener('pointerdown',event=>{if(event.target?.closest?.(ELECTRIC)&&!event.target.closest(CONTROL))sound.play('electric');},{signal,passive:true});\r\n }\r\n bindDocument(document);\r\n // The Hand editor boots this module in its own document, including when framed.\r\n window.addEventListener('myr5:portal-sound',event=>{\r\n  const {kind,board,visible}=event.detail||{};\r\n  if(kind==='scene')sound.setScene(visible?'portal':'pod',board||sound.board);\r\n  else if(kind==='board')sound.setBoard(board||'quilt');\r\n  else if(kind==='travel'){sound.play('transit');}\r\n  else if(kind==='press'||kind==='drag'){\r\n   const cue=BOARD_CUES[board||sound.board]||BOARD_CUES.quilt;\r\n   sound.play(cue[kind==='drag'?1:0]);\r\n   if(kind==='press'&&board==='grass')sound.play('grass-tinkle');\r\n  }\r\n  else if(kind==='menu')sound.play('electric');\r\n },{signal});\r\n window.addEventListener('myr5:hardware-detent',event=>sound.play(event.detail?.kind==='lever'?'lever':event.detail?.kind==='switch'?'switch':'dial'),{signal});\r\n window.addEventListener('myr5:reminders-open',()=>{sound.play('dialup');sound.play('signal');},{signal});\r\n window.addEventListener('myr5:classroom-board',()=>sound.play('whiteboard'),{signal});\r\n window.addEventListener('myr5:whiteboard-redraw',()=>sound.play('whiteboard'),{signal});\r\n const openMenus=new Set();\r\n const menuObserver=new MutationObserver(()=>{const current=new Set(document.querySelectorAll('dialog[open]'));for(const menu of current)if(!openMenus.has(menu)&&!menu.matches('#portalWorkoutHome,.meditation-panel'))sound.play(surfaceCue(menu));openMenus.clear();for(const menu of current)openMenus.add(menu);});\r\n menuObserver.observe(document.body,{subtree:true,attributes:true,attributeFilter:['open']});signal.addEventListener('abort',()=>menuObserver.disconnect(),{once:true});\r\n window.addEventListener('myr5:reminders-turn',()=>sound.play('lever'),{signal});\r\n return ()=>{abort.abort();sound.dispose();};\r\n}\r\n","audio/standalone-sound.mjs":"import {mountPhysicalSoundUI} from './sound-ui.mjs';\r\nimport {physicalSound} from './physical-sound.mjs';\r\nimport {mountPageMusic} from './page-music.mjs';\r\nmountPhysicalSoundUI();\r\nmountPageMusic(); // /creature/ and /war-room/ play the customizer song; framed pages leave music to the parent\r\nfunction mountDockSound(){\r\n if(!location.pathname.startsWith('/creature/'))return;\r\n const dock=document.getElementById('coachDock');if(!dock||dock.querySelector('.dock-sound'))return;\r\n const control=document.createElement('div');control.className='cb-part dock-sound';\r\n control.innerHTML='<span class=\"cb-label\" aria-hidden=\"true\">SOUND</span><button type=\"button\" class=\"dock-control\" id=\"editorSoundSwitch\" role=\"switch\" aria-label=\"Sound effects\" aria-checked=\"true\" data-on=\"true\">ON</button>';\r\n dock.insertBefore(control,dock.querySelector('.dock-live'));\r\n const button=control.querySelector('button');\r\n const paint=()=>{const on=!physicalSound.muted;button.dataset.on=String(on);button.setAttribute('aria-checked',String(on));button.textContent=on?'ON':'OFF';};\r\n button.addEventListener('click',()=>{physicalSound.setMuted(!physicalSound.muted);if(!physicalSound.muted)physicalSound.play('switch');});\r\n button.addEventListener('keydown',event=>{const on={ArrowUp:true,ArrowRight:true,ArrowDown:false,ArrowLeft:false}[event.key];if(on===undefined)return;event.preventDefault();if((!physicalSound.muted)!==on)button.click();});\r\n window.addEventListener('myr5:sound-settings',paint);paint();\r\n}\r\nif(document.readyState==='loading')document.addEventListener('DOMContentLoaded',mountDockSound,{once:true});else mountDockSound();\r\nif(location.pathname.startsWith('/war-room/'))physicalSound.setScene('off');\r\nif(window.parent!==window){\r\n physicalSound.setScene('off');\r\n try{const parent=window.parent,forward=event=>window.dispatchEvent(new CustomEvent('myr5:response',{detail:event.detail}));parent.addEventListener('myr5:response',forward);window.addEventListener('pagehide',()=>parent.removeEventListener('myr5:response',forward),{once:true});}catch{}\r\n}\r\n","auth-client.mjs":"import {bootstrapGameGeneration,captureGameGeneration,gameGenerationHeaders,acceptGameGeneration,isGameRequest} from './modules/coach-world/game-generation-client.mjs';\r\nimport {authTransitions} from './auth-transition.mjs';\r\nimport {safeReturn} from './auth-paths.mjs';\r\n\r\nlet settingsPromise,settingsGen=0;\r\nlet clerkPromise=null; // shared init promise per attempt\r\nlet clerkGen=0; // generation nonce for init\r\nlet clerkIdentityListenerUnsub=null; // current unsubscribe function\r\nlet loginReadyDispatched=false;\r\n\r\nexport function authSettings({force=false}={}){\r\n  if(force){settingsGen++; settingsPromise=null;}\r\n  const gen=settingsGen;\r\n  return settingsPromise ??=(async()=>{\r\n    const res=await fetch(`/api/auth/config?t=${Date.now()}`,{credentials:'same-origin',cache:'no-store',signal:AbortSignal.timeout(5000)});\r\n    if(!res.ok)throw Object.assign(Error('Login could not connect. Please retry.'),{status:res.status,code:'auth-config'});\r\n    return res.json();\r\n  })().then(r=>{\r\n    if(settingsGen===gen){\r\n      if(r.enabled)settingsPromise=r; else settingsPromise=null;\r\n    }\r\n    return r;\r\n  }).catch(e=>{if(settingsGen===gen){settingsPromise=null;}throw e;});\r\n}\r\n\r\nfunction script(src,key){return new Promise((resolve,reject)=>{\r\n  const tag=document.createElement('script');\r\n  tag.src=src;tag.crossOrigin='anonymous';\r\n  if(key)tag.dataset.clerkPublishableKey=key;\r\n  tag.onload=()=>{resolve();};\r\n  tag.onerror=()=>reject(Error('Login could not load. Check your connection and retry.'));\r\n  document.head.append(tag);\r\n});}\r\n\r\nexport function loadLogin(){\r\n  if (clerkPromise) return clerkPromise;\r\n  const attemptGen = ++clerkGen;\r\n\r\n  let deadlineTimer = null;\r\n  const cleanup = () => {\r\n    if (deadlineTimer) {\r\n      clearTimeout(deadlineTimer);\r\n      deadlineTimer = null;\r\n    }\r\n  };\r\n\r\n  const attemptPromise = new Promise((resolve, reject) => {\r\n    const timeoutReject = Object.assign(new Error('Account timed out'),{code:'account_timeout'});\r\n    const raceInit = (async () => {\r\n      try {\r\n        let config;\r\n        try {\r\n          config = await authSettings();\r\n          if (clerkGen !== attemptGen) throw timeoutReject;\r\n        } catch (e) {\r\n          cleanup();\r\n          throw e;\r\n        }\r\n\r\n        if (!config.enabled) {\r\n          cleanup();\r\n          return null;\r\n        }\r\n\r\n        const origin = new URL(config.frontend).origin;\r\n        await script(origin + '/npm/@clerk/ui@1/dist/ui.browser.js');\r\n        if (clerkGen !== attemptGen) throw timeoutReject;\r\n\r\n        await script(origin + '/npm/@clerk/clerk-js@6/dist/clerk.browser.js', config.publishableKey);\r\n        if (clerkGen !== attemptGen) throw timeoutReject;\r\n\r\n        const Clerk = window.Clerk;\r\n        await Clerk.load({ ui: { ClerkUI: window.__internal_ClerkUICtor } });\r\n        if (clerkGen !== attemptGen) throw timeoutReject;\r\n\r\n        const clerk = Clerk;\r\n        authTransitions().observeIdentity('clerk', clerk.session?.id, clerk.user?.id);\r\n\r\n        if (clerkIdentityListenerUnsub) {\r\n          clerkIdentityListenerUnsub();\r\n          clerkIdentityListenerUnsub = null;\r\n        }\r\n\r\n        const observe = () => {\r\n          if (clerkGen === attemptGen) {\r\n            authTransitions().observeIdentity('clerk', clerk.session?.id, clerk.user?.id);\r\n          }\r\n        };\r\n\r\n        clerkIdentityListenerUnsub = clerk.addListener(observe);\r\n\r\n        if (!loginReadyDispatched) {\r\n          window.dispatchEvent(new Event('myr5:login-ready'));\r\n          loginReadyDispatched = true;\r\n        }\r\n\r\n        resolve(clerk);\r\n      } catch (e) {\r\n        cleanup();\r\n        reject(e);\r\n      }\r\n    })();\r\n\r\n    deadlineTimer = setTimeout(() => {\r\n      if (clerkPromise === attemptPromise) {\r\n        clerkGen++;\r\n        cleanup();\r\n        reject(timeoutReject);\r\n      }\r\n    }, 20000);\r\n\r\n    raceInit.then(resolve, reject).finally(cleanup);\r\n  });\r\n\r\n  clerkPromise = attemptPromise;\r\n  clerkPromise\r\n    .then((value) => {cleanup(); if (value===null && clerkPromise===attemptPromise) clerkPromise=null;}, () => {\r\n      cleanup();\r\n      if (clerkPromise === attemptPromise) clerkPromise = null;\r\n    });\r\n\r\n  return attemptPromise;\r\n}\r\nfunction boundedAuthWait(promise,signal,timeoutMs=5000){\r\n  return new Promise((resolve,reject)=>{\r\n    let settled=false;\r\n    const finish=(fn,value)=>{if(settled)return;settled=true;clearTimeout(timer);signal.removeEventListener('abort',aborted);fn(value);}\r\n    const aborted=()=>finish(reject,Object.assign(Error('Account changed. Refresh to continue.'),{code:'auth_transition'}));\r\n    const timer=setTimeout(()=>finish(reject,Object.assign(Error('Account service timed out. Please retry.'),{status:503,code:'account_timeout'})),timeoutMs);\r\n    signal.addEventListener('abort',aborted,{once:true});\r\n    if(signal.aborted)aborted();\r\n    Promise.resolve(promise).then(value=>finish(resolve,value),error=>finish(reject,error));\r\n  });\r\n}\r\n\r\nexport async function authFetch(path,options={}){\r\n  const transitions=authTransitions(),ticket=transitions.capture(),headers=new Headers(options.headers);\r\n  const game=isGameRequest(path);let gameGeneration=null;\r\n  if(game){\r\n    const captured=headers.get('X-MYR5-Game-Generation');\r\n    if(captured!==null){gameGeneration=/^[1-9][0-9]*$/.test(captured)?Number(captured):null;}\r\n    else {try{gameGeneration=captureGameGeneration();}catch{await bootstrapGameGeneration({fetchImpl:authFetch,signal:options.signal});gameGeneration=captureGameGeneration();}for(const [key,value] of Object.entries(gameGenerationHeaders(gameGeneration)))headers.set(key,value);}\r\n  }\r\n  const config=await boundedAuthWait(authSettings(),ticket.signal); // 5s timeout\r\n  transitions.assertCurrent(ticket);\r\n  const provider=localStorage.getItem('myr5-login-provider');\r\n  // Guest probes (guestSkip): with no Clerk sign-in on this device the server can only answer 401 (Clerk off rejects any bearer; browsers never carry the legacy header), so answer it locally and keep the network log clean.\r\n  if(options.guestSkip&&provider!=='clerk')return new Response(JSON.stringify({error:'Sign in to save your progress.'}),{status:401,headers:{'Content-Type':'application/json'}});\r\n  if(config.enabled&&provider==='clerk'){\r\n    const clerk=await boundedAuthWait(loadLogin(),ticket.signal,20000); // 20s timeout\r\n    transitions.assertCurrent(ticket);\r\n    transitions.observeIdentity('clerk',clerk?.session?.id,clerk?.user?.id);\r\n    transitions.assertCurrent(ticket);\r\n    const token=await boundedAuthWait(clerk?.session?.getToken(),ticket.signal); // 5s timeout\r\n    transitions.observeIdentity('clerk',clerk?.session?.id,clerk?.user?.id);\r\n    transitions.assertCurrent(ticket);\r\n    if(!token)throw Object.assign(Error('Sign in to Coach again.'),{status:401});\r\n    headers.set('Authorization','Bearer '+token);\r\n  }\r\n  transitions.assertCurrent(ticket);\r\n  const signal=AbortSignal.any([ticket.signal,...(options.signal?[options.signal]:[]),AbortSignal.timeout(8000)]);\r\n  const response=await fetch(path,{...options,signal,headers,credentials:'same-origin',cache:'no-store'});\r\n  transitions.assertCurrent(ticket);\r\n  if(game||['/api/workouts/complete','/api/breathing/complete'].includes(path)){const dto=await response.clone().json();if(dto.gameGeneration!=null&&(response.ok||dto.code==='game_generation_mismatch'))acceptGameGeneration(dto,game?gameGeneration:undefined);else if(game&&response.ok)acceptGameGeneration(dto,gameGeneration);}\r\n  return response;\r\n}\r\n\r\nexport async function signOut(returnTo='/pose.html'){\r\n  try{authTransitions().invalidate();}catch{}\r\n  let provider,timer;try{provider=localStorage.getItem('myr5-login-provider');}catch{}\r\n  try{\r\n    await Promise.race([\r\n      (async()=>{const clerk=window.Clerk||(provider==='clerk'?await loadLogin():null);await clerk?.signOut();})(),\r\n      new Promise(resolve=>{timer=setTimeout(resolve,3000);}),\r\n    ]);\r\n  }catch{}finally{\r\n    clearTimeout(timer);try{localStorage.removeItem('myr5-login-provider');}catch{}\r\n    location.assign(safeReturn(returnTo));\r\n  }\r\n}\r\n","auth-paths.mjs":"export function safeReturn(value, fallback='/pose.html') {\r\n  if(typeof value!=='string'||!value.startsWith('/')||value.startsWith('//')||/[\\\\\\r\\n]/.test(value))return fallback;\r\n  const url=new URL(value,'https://coach.invalid');\r\n  return url.origin==='https://coach.invalid'&&!/^\\/(signin|signout|callback)/.test(url.pathname)?url.pathname+url.search+url.hash:fallback;\r\n}\r\nexport const signInPath=returnTo=>'/signin.html?return_to='+encodeURIComponent(safeReturn(returnTo));\r\n","auth-transition.mjs":"export const AUTH_TRANSITION_KEY='myr5-auth-transition-v1';\r\nexport const AUTH_PROVIDER_KEY='myr5-login-provider';\r\nconst uuid=value=>typeof value==='string'&&/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);\r\nexport class AuthTransitionError extends Error{\r\n constructor(code='auth_transition'){super(code==='auth_unavailable'?'Account synchronization is unavailable. Reload to retry.':'Account changed. Refresh to continue.');this.name='AuthTransitionError';this.code=code;}\r\n}\r\n// This coordinator owns no tokens, credentials or account data. All authority\r\n// tickets bind the persisted random transition and an in-memory revision.\r\nexport function createAuthTransition({storage,events,channelFactory,randomUUID}){\r\n let token,revision=0,refreshGeneration=0,controller=new AbortController(),refreshController,channel,blocked=false,identity;\r\n const listeners=new Set();\r\n const notify=()=>{for(const listener of listeners){try{listener();}catch{}}};\r\n const cancel=()=>{revision++;refreshGeneration++;controller.abort();refreshController?.abort();controller=new AbortController();notify();};\r\n const block=()=>{if(!blocked){blocked=true;cancel();}throw new AuthTransitionError('auth_unavailable');};\r\n const read=()=>{try{const value=storage.getItem(AUTH_TRANSITION_KEY);if(!uuid(value))return block();return value;}catch{return block();}};\r\n const current=()=>{if(blocked)throw new AuthTransitionError('auth_unavailable');const value=read();if(value!==token){token=value;cancel();}return value;};\r\n const invalidate=()=>{\r\n  if(blocked)throw new AuthTransitionError('auth_unavailable');\r\n  cancel();\r\n  try{const next=randomUUID();if(!uuid(next))return block();storage.setItem(AUTH_TRANSITION_KEY,next);if(storage.getItem(AUTH_TRANSITION_KEY)!==next)return block();token=next;channel.postMessage({type:'transition',token:next});return next;}catch{return block();}\r\n };\r\n const onStorage=event=>{try{if(event.key===AUTH_TRANSITION_KEY||event.key===null)current();else if(event.key===AUTH_PROVIDER_KEY)invalidate();}catch{}};\r\n const onMessage=()=>{try{current();}catch{}};\r\n try{\r\n  if(!storage||!events?.addEventListener||typeof channelFactory!=='function'||typeof randomUUID!=='function')throw Error();\r\n  token=storage.getItem(AUTH_TRANSITION_KEY);\r\n  if(token===null){token=randomUUID();if(!uuid(token))throw Error();storage.setItem(AUTH_TRANSITION_KEY,token);token=storage.getItem(AUTH_TRANSITION_KEY);}\r\n  if(!uuid(token))throw Error();\r\n  channel=channelFactory('myr5-auth-transition-v1');\r\n  if(!channel?.addEventListener||!channel?.postMessage)throw Error();\r\n  channel.addEventListener('message',onMessage);channel.addEventListener('messageerror',()=>{try{block();}catch{}});events.addEventListener('storage',onStorage);\r\n }catch{blocked=true;controller.abort();}\r\n const capture=()=>{const value=current();return Object.freeze({token:value,revision,signal:controller.signal});};\r\n const isCurrent=ticket=>{try{return !!ticket&&current()===ticket.token&&ticket.revision===revision&&!ticket.signal.aborted&&(ticket.refreshGeneration===undefined||ticket.refreshGeneration===refreshGeneration);}catch{return false;}};\r\n return Object.freeze({\r\n  capture,isCurrent,invalidate,\r\n  assertCurrent(ticket){if(!isCurrent(ticket))throw new AuthTransitionError(blocked?'auth_unavailable':'auth_transition');},\r\n  beginRefresh(){const ticket=capture();refreshController?.abort();refreshController=new AbortController();return Object.freeze({...ticket,refreshGeneration:++refreshGeneration,signal:refreshController.signal});},\r\n  observeIdentity(provider,sessionId,userId){const next=JSON.stringify([provider,sessionId??null,userId??null]);if(identity===undefined){identity=next;return;}if(identity!==next){identity=next;invalidate();}},\r\n  subscribe(listener){listeners.add(listener);return()=>listeners.delete(listener);},\r\n  close(){if(!blocked){blocked=true;cancel();}events?.removeEventListener?.('storage',onStorage);channel?.close?.();},\r\n });\r\n}\r\nlet browserCoordinator;\r\nexport function authTransitions(){\r\n if(!browserCoordinator){let storage;try{storage=globalThis.localStorage;}catch{}\r\n  browserCoordinator=createAuthTransition({storage,events:globalThis.window,channelFactory:typeof globalThis.BroadcastChannel==='function'?name=>new BroadcastChannel(name):undefined,randomUUID:()=>globalThis.crypto.randomUUID()});\r\n }\r\n return browserCoordinator;\r\n}\r\n","battle-pass-page.css":".pass-crt-rack{display:grid;grid-template-columns:38px minmax(0,300px) 38px;justify-content:center;align-items:stretch;gap:5px;width:min(390px,100%);margin:8px auto 0}\r\n.pass-crt-rack>.workout-performance-controls{grid-column:1/-1}\r\n.pass-crt-switch{align-self:stretch;border:2px solid #596d6b;border-radius:5px;background:linear-gradient(120deg,#35464b,#152629 60%,#34474b);box-shadow:inset 0 1px #afc8c885,inset 0 -3px #081415,0 4px 0 #101a1c;color:#b5eacb;font:700 15px ui-monospace,Consolas,monospace;text-shadow:0 0 8px #89ffd8;cursor:pointer}\r\n.pass-crt-switch:active{transform:translateY(3px);box-shadow:inset 0 2px 5px #0009,0 1px 0 #101a1c}\r\n.pass-crt-switch:disabled{opacity:.4;cursor:default}\r\n.pass-crt-switch:focus-visible,.pass-crt-entry:focus-visible{outline:3px solid #ffe2a0;outline-offset:2px}\r\n.battle-pass-entry.pass-crt-entry{position:relative;display:grid;gap:2px;min-width:0;width:100%;margin:0;padding:6px 9px 8px;border:3px solid #546c70;border-radius:7px;background:radial-gradient(ellipse at 50% 35%,#173d3a,#071416 80%);box-shadow:inset 0 0 0 2px #091518,inset 0 0 17px #6dfdb32b,0 3px 0 #41575a;color:#bce5cb;text-align:left;font:600 9px/1.2 ui-monospace,Consolas,monospace;letter-spacing:.06em;cursor:pointer;text-shadow:0 0 5px #72f6aa6e}\r\n.pass-crt-entry strong{font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pass-crt-entry small{font-size:8px;color:#a4c8b8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pass-entry-meter{height:3px;margin-top:2px;background:linear-gradient(90deg,#9ff5bf var(--pass-fill),#28483b var(--pass-fill));box-shadow:0 0 5px #74d99c77}\r\n#battlePassPanel.battle-pass-page{box-sizing:border-box;display:none;flex-direction:column;width:min(1120px,calc(100% - 12px));height:calc(100dvh - 12px);max-height:calc(100dvh - 12px);padding:0;overflow:hidden;border:2px solid #7194a7;border-radius:9px;background:#06101c;color:#e6f4f6;box-shadow:0 0 0 5px #122938,0 16px 60px #000d;font-family:ui-monospace,Consolas,monospace}\r\n#battlePassPanel.battle-pass-page[open]{display:flex}\r\n.battle-pass-page::backdrop{background:#020710dc;backdrop-filter:blur(6px)}\r\n.battle-pass-page button{font:inherit;cursor:pointer}.battle-pass-page button:focus-visible,.pass-map-viewport:focus-visible{outline:3px solid #f8db83;outline-offset:2px}\r\n.pass-header{flex:none;display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:42px;padding:3px 12px;border-bottom:1px solid #477785;background:linear-gradient(90deg,#143346,#091e2a 65%,#292541)}\r\n.pass-header h2{margin:0;font-size:clamp(.92rem,2.3vw,1.3rem);letter-spacing:.15em;text-shadow:0 0 18px #73d6ee99}\r\n.pass-flight-score{flex:none;display:flex;justify-content:space-between;gap:8px;padding:7px 12px;border-bottom:1px solid #477785;background:#091d2b;color:#b9faff;font-size:clamp(.68rem,2vw,.9rem);font-variant-numeric:tabular-nums}\r\n.pass-flight-score strong:last-child{color:#ffe4a0;text-shadow:0 0 9px #f2b85980}\r\n.pass-close,.pass-fire{border:2px solid #789dab;border-radius:5px;background:linear-gradient(#294553,#152c39);box-shadow:inset 0 1px #d9f8ff55,0 3px 0 #0a1925;color:#e9faff;text-shadow:0 0 7px #9cefff}\r\n.pass-close{padding:5px 9px;white-space:nowrap;font-size:.72rem!important}\r\n.pass-daily{flex:none;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:3px 8px;margin:5px 8px 0;padding:5px 8px;border:1px solid #548b85;border-left:3px solid #86debb;background:linear-gradient(110deg,#112c35e8,#0a1c2be8)}\r\n.pass-daily h3{margin:0;color:#adf0c7;font-size:.65rem;letter-spacing:.09em;white-space:nowrap}.pass-quests{display:flex;flex-wrap:wrap;gap:3px;justify-content:center}.pass-quests span{padding:2px 5px;border:1px solid #45636e;border-radius:3px;color:#afc4c7;font-size:.62rem;white-space:nowrap}.pass-quests span[data-complete=true]{border-color:#8ad4af;background:#1c453c;color:#c8ffe0}.pass-daily strong{font-size:.64rem;color:#f7d987;white-space:nowrap}.pass-daily small{grid-column:1/-1;color:#a8bbc6;font-size:.61rem;line-height:1.15}\r\n.pass-summary{flex:none;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:3px 10px;margin:5px 8px 0;padding:5px 8px;border:1px solid #4d7790;background:#102534}.pass-summary strong{font-size:1rem;color:#ffe4a0}.pass-summary span{justify-self:end;font-size:.69rem;color:#b6d9e8}.pass-summary progress{grid-column:1/-1;width:100%;height:7px;accent-color:#e9c479}.pass-summary b{font-size:.68rem;color:#eac782;white-space:nowrap}\r\n.pass-map-shell{position:relative;flex:1;min-height:0;isolation:isolate;margin:5px 8px 8px;border:2px solid #587d98;border-radius:8px;background:#071326;box-shadow:inset 0 0 35px #000c,0 0 22px #4181a230;overflow:hidden}\r\n.pass-map-viewport{position:relative;width:100%;height:100%;overflow:auto;scrollbar-color:#7aa0af #071a2d;overscroll-behavior:contain;touch-action:pan-y}\r\n.pass-route{position:relative;isolation:isolate;background:linear-gradient(#030916ef,#070c1cf0),url('/pod/worlds/achievements.jpg') center/auto 1100px repeat-y}\r\n.pass-area{position:relative;min-height:1030px;isolation:isolate;overflow:hidden;border-bottom:2px solid #6ca9c077;background:radial-gradient(circle at 82% 17%,#bd66652a,transparent 35%),radial-gradient(circle at 21% 52%,#414ebd36,transparent 38%),linear-gradient(180deg,#080b24,#0b1732 45%,#071728)}\r\n.pass-area:before{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;background:radial-gradient(ellipse at 65% 16%,#8157b93b,transparent 38%),radial-gradient(ellipse at 25% 70%,#23a7d123,transparent 35%);filter:hue-rotate(var(--world-hue));opacity:calc(.4 + var(--world-intensity)*.6)}\r\n.pass-area[data-chapter=\"0\"]{background:radial-gradient(circle at 79% 22%,#38466924,transparent 21%),linear-gradient(#020612,#091522 65%,#030b17)}\r\n.pass-area[data-chapter=\"9\"],.pass-area[data-chapter=\"10\"]{background:radial-gradient(circle at 76% 18%,#efb94e39,transparent 30%),linear-gradient(#1b1320,#1b2138 50%,#081c30)}\r\n.pass-area[data-chapter=\"14\"],.pass-area[data-chapter=\"18\"]{background:radial-gradient(circle at 75% 17%,#d8315348,transparent 40%),linear-gradient(#1e0c22,#200d27 55%,#080d22)}\r\n.pass-area[data-chapter=\"22\"],.pass-area[data-chapter=\"23\"],.pass-area[data-chapter=\"24\"]{background:radial-gradient(circle at 78% 15%,#9e6be547,transparent 44%),linear-gradient(#09051e,#101039 55%,#060b1f)}\r\n.pass-sky{position:absolute;inset:0;pointer-events:none;opacity:calc(.28 + var(--world-intensity)*.65);background-image:radial-gradient(2px 2px at 15% 12%,#fff 90%,transparent),radial-gradient(2px 2px at 75% 20%,#bde7ff 90%,transparent),radial-gradient(1px 1px at 45% 36%,#fff 90%,transparent),radial-gradient(2px 2px at 85% 63%,#fff 90%,transparent),radial-gradient(1px 1px at 28% 78%,#fff 90%,transparent);background-size:157px 190px,281px 260px,231px 251px,307px 280px,201px 330px}\r\n.pass-planet{position:absolute;top:7%;right:6%;width:clamp(75px,11vw + 60px,230px);aspect-ratio:1;border-radius:50%;pointer-events:none;background:radial-gradient(circle at 28% 25%,#fff8d6a8 1%,#e9bc8ac9 16%,#5767a1e0 45%,#141b4b 70%,#060a23 82%);box-shadow:inset -18px -20px 27px #020715bc,0 0 35px #96a6e266,0 0 90px #674eb443;filter:hue-rotate(var(--world-hue));opacity:calc(.32 + var(--world-intensity)*.6)}\r\n.pass-area[data-chapter=\"0\"] .pass-planet{width:65px;opacity:.42;box-shadow:0 0 12px #afd9ff44}\r\n.pass-area[data-chapter=\"14\"] .pass-planet,.pass-area[data-chapter=\"18\"] .pass-planet{background:radial-gradient(circle at 30% 30%,#fff7b0,#fa853e 30%,#9a1f34 66%,#1d0818 84%);box-shadow:0 0 65px #f26055aa,0 0 140px #c5356270}\r\n.pass-area[data-chapter=\"22\"] .pass-planet,.pass-area[data-chapter=\"23\"] .pass-planet{background:radial-gradient(circle,#050412 27%,#aa75d0 30%,#221143 42%,#050412 64%);box-shadow:0 0 80px #9562c1aa}\r\n.pass-area[data-future=\"true\"]:after,.pass-current-fog{content:'';position:absolute;z-index:2;inset:0;pointer-events:none;background:linear-gradient(#030916b0,#071327b9 55%,#040a18c7)}\r\n.pass-current-fog{bottom:auto;min-height:0}\r\n.pass-world-title{position:relative;z-index:3;display:grid;gap:3px;width:max-content;max-width:70%;padding:24px 5% 0;text-transform:uppercase;letter-spacing:.12em;text-shadow:0 0 12px #000}\r\n.pass-world-title small{font-size:.65rem;color:#efc98c}.pass-world-title b{font-size:clamp(1.05rem,3vw,2rem);color:#d7eaff}.pass-world-title span{font-size:.72rem;color:#a9ccd7}\r\n.pass-constellation{position:absolute;top:25%;right:4%;z-index:1;display:grid;justify-items:center;gap:7px;width:min(35%,245px);text-align:center;filter:drop-shadow(0 0 14px #b991ff70)}\r\n.pass-coach-art{position:relative;width:min(85%,160px);max-height:230px;overflow:hidden;mask-image:radial-gradient(ellipse,#000 55%,transparent 79%);opacity:.85}\r\n.pass-coach-art img{position:absolute;max-width:none;object-fit:fill;filter:contrast(1.15) saturate(1.25) drop-shadow(0 0 10px #cbadff)}\r\n.pass-coach-caption{display:grid;gap:5px;padding:10px;border:1px solid #d9bafb9e;background:radial-gradient(circle at 50% 0,#7e62bf55,transparent 70%),#0e163edb}\r\n.pass-coach-caption span{font-size:.58rem;letter-spacing:.08em;color:#d5baf5}.pass-coach-caption strong{font-size:clamp(.85rem,2vw,1.2rem);color:#fff1ff}.pass-coach-caption small{font-size:.58rem;color:#d1b8f1}.pass-coach-caption p{margin:0;font-size:.62rem;line-height:1.3;color:#dde1f4}\r\n.pass-levels{position:relative;z-index:3;display:grid;gap:19px;list-style:none;margin:0;padding:40px 3% 80px;width:61%;box-sizing:border-box}\r\n.pass-level{display:grid;grid-template-columns:43px minmax(0,1fr) 14px;align-items:center;gap:8px;min-height:62px;padding:6px 9px;border:1px solid #6b879a;border-radius:5px;background:linear-gradient(110deg,#11293de5,#071624ed);box-shadow:0 5px 13px #0008;color:#cbdce8}\r\n.pass-level:nth-child(even){margin-left:7%}.pass-level[data-state=earned]{border-color:#7bb9a0;background:linear-gradient(110deg,#153b3aef,#0b252bf0)}.pass-level[data-state=current]{border:2px solid #f9d888;background:linear-gradient(110deg,#765221f2,#223a49f2);box-shadow:0 0 0 4px #f4d8863b,0 0 30px #eeb96795;color:#fff6dc}.pass-level[data-state=locked]{border-color:#496477;color:#aac0cb;background:#091827e9}\r\n.pass-level-number{display:grid;place-items:center;width:40px;height:40px;border:1px solid currentColor;border-radius:50%;font-size:.92rem}.pass-level-body{display:grid;gap:3px;min-width:0}.pass-level-body strong{font-size:.78rem}.pass-level-body span,.pass-level-body small{font-size:.63rem;color:#b8c9d3}.pass-level[data-state=current] .pass-level-body small{color:#ffe1a2}.pass-level i{font-size:1rem;color:#e6c782;font-style:normal}\r\n.pass-scene{position:absolute;z-index:4;inset:0;width:100%;height:100%;pointer-events:none;image-rendering:pixelated}.pass-fire{position:absolute;z-index:5;right:12px;bottom:12px;min-width:74px;padding:7px 9px;color:#bcfaff}\r\n.workout-performance-controls{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.workout-performance-controls button,.workout-performance-controls select{font:inherit;padding:8px;border:1px solid #65758a;background:#121923;color:#eef3fa}.workout-performance-controls[hidden]{display:none}\r\n#cameraWorkout .workout-performance-controls{position:absolute;left:18px;right:18px;bottom:90px;z-index:20;padding:10px;background:#101620e8;justify-content:center}#cameraWorkout .workout-performance-controls p{flex-basis:100%;margin:0;color:#fff;text-align:center}\r\n@media(max-width:650px){\r\n #battlePassPanel.battle-pass-page{width:calc(100% - 6px);height:calc(100dvh - 6px);max-height:calc(100dvh - 6px);border-radius:4px}\r\n .pass-header{min-height:39px;padding:2px 8px}.pass-header h2{font-size:.89rem}\r\n .pass-daily{grid-template-columns:1fr auto;margin:4px 5px 0;padding:4px 6px;gap:3px}.pass-daily h3{font-size:.61rem}.pass-quests{grid-column:1/-1;grid-row:2;justify-content:flex-start}.pass-quests span{font-size:.6rem;padding:2px 4px}.pass-daily strong{grid-column:2;grid-row:1;font-size:.58rem}.pass-daily small{grid-row:3;font-size:.58rem}\r\n .pass-summary{margin:4px 5px 0;padding:4px 6px}.pass-summary strong{font-size:.85rem}.pass-summary span,.pass-summary b{font-size:.59rem}.pass-summary progress{height:6px}\r\n .pass-map-shell{margin:4px 5px 5px}.pass-area{min-height:870px}.pass-world-title{padding-top:18px;max-width:90%}.pass-world-title b{font-size:1.15rem}.pass-world-title small,.pass-world-title span{font-size:.59rem}\r\n .pass-levels{width:67%;gap:15px;padding:36px 1.5% 60px}.pass-level{grid-template-columns:31px minmax(0,1fr) 8px;gap:4px;min-height:57px;padding:5px}.pass-level-number{width:29px;height:29px;font-size:.75rem}.pass-level-body strong{font-size:.62rem}.pass-level-body span,.pass-level-body small{font-size:.54rem}.pass-level i{font-size:.7rem}\r\n .pass-constellation{right:2%;top:26%;width:30%;gap:2px}.pass-coach-art{width:93%;max-height:155px}.pass-coach-caption{padding:5px;gap:3px}.pass-coach-caption span{font-size:.45rem}.pass-coach-caption strong{font-size:.7rem}.pass-coach-caption small,.pass-coach-caption p{font-size:.49rem}\r\n .pass-planet{width:88px}.pass-fire{right:7px;bottom:7px;min-width:58px;padding:6px;font-size:.7rem!important}\r\n}\r\n@media(prefers-reduced-motion:reduce){.pass-planet,.pass-sky,.pass-level{animation:none!important;transition:none!important}}\r\n\r\n/* The CRT is a small star chart for its ten-level sector. */\r\n.pass-map-shell{touch-action:none}\r\n.pass-crt-chart{display:block;width:100%;height:22px;margin:2px 0;overflow:visible}\r\n.pass-crt-line,.pass-crt-branch{fill:none;stroke:#66a9a5;stroke-width:.65;opacity:.75;vector-effect:non-scaling-stroke}\r\n.pass-crt-branch{stroke:#70979a;opacity:.5}\r\n.pass-crt-dot{fill:#536f71;stroke:#9cb7ad;stroke-width:.45;vector-effect:non-scaling-stroke}\r\n.pass-crt-dot.is-earned{fill:#adf8cb;stroke:#e7ffe9;filter:drop-shadow(0 0 2px #90ffb9)}\r\n.pass-crt-dot.is-current{fill:#ffe8a1;stroke:#fff7d9;filter:drop-shadow(0 0 3px #ffe29b)}\r\n\r\n/* Open space, with one unbroken constellation through all 25 sectors. */\r\n.pass-route{background:#030916}\r\n.pass-area,.pass-area[data-chapter=\"0\"],.pass-area[data-chapter=\"9\"],.pass-area[data-chapter=\"10\"],.pass-area[data-chapter=\"14\"],.pass-area[data-chapter=\"18\"],.pass-area[data-chapter=\"22\"],.pass-area[data-chapter=\"23\"],.pass-area[data-chapter=\"24\"]{min-height:1500px;border:0;background:radial-gradient(ellipse at var(--object-x) var(--object-y),hsl(calc(205 + var(--world-intensity)*280) 80% 42% / .14),transparent 28%),radial-gradient(ellipse at 22% 69%,hsl(calc(245 + var(--world-intensity)*190) 65% 35% / .12),transparent 36%),linear-gradient(180deg,#030916,#070e22 52%,#030916)}\r\n.pass-area:before{background:radial-gradient(ellipse at 53% 44%,#40659a22,transparent 48%);opacity:calc(.2 + var(--world-intensity)*.8)}\r\n.pass-sky{opacity:calc(.52 + var(--world-intensity)*.48);background-image:radial-gradient(1px 1px at 15% 12%,#fff 90%,transparent),radial-gradient(2px 2px at 75% 20%,#bde7ff 90%,transparent),radial-gradient(1px 1px at 45% 36%,#fff 90%,transparent),radial-gradient(2px 2px at 85% 63%,#fff 90%,transparent),radial-gradient(1px 1px at 28% 78%,#fff 90%,transparent);background-size:157px 190px,281px 260px,231px 251px,307px 280px,201px 330px}\r\n.pass-planet,.pass-area[data-chapter=\"0\"] .pass-planet,.pass-area[data-chapter=\"14\"] .pass-planet,.pass-area[data-chapter=\"18\"] .pass-planet,.pass-area[data-chapter=\"22\"] .pass-planet,.pass-area[data-chapter=\"23\"] .pass-planet{top:var(--object-y);left:var(--object-x);right:auto;width:clamp(28px,4vw,55px);aspect-ratio:1;transform:translate(-50%,-50%);opacity:calc(.5 + var(--world-intensity)*.5);background:radial-gradient(circle at 30% 27%,#fff6df 1%,#c3d8eb 12%,#638fbd 37%,#1e406b 66%,#08172d 84%);box-shadow:inset -5px -5px 7px #020715,0 0 14px #9ebdff7a;filter:hue-rotate(var(--world-hue))}\r\n.pass-area:nth-child(5n+2) .pass-planet:after,.pass-area:nth-child(5n+3) .pass-planet:after{content:'';position:absolute;inset:36% -30%;border:2px solid #d2d5e899;border-radius:50%;transform:rotate(-24deg)}\r\n.pass-area:nth-child(5n) .pass-planet{border-radius:33% 56% 45% 61%;clip-path:polygon(20% 0,78% 8%,100% 41%,83% 95%,31% 100%,0 65%)}\r\n.pass-area:nth-child(7n) .pass-planet{background:radial-gradient(circle at 34% 29%,#fff6b0,#f7b773 28%,#bd526f 59%,#34153d 82%);box-shadow:0 0 20px #e6859c99}\r\n.pass-route-lines{position:absolute;inset:0;z-index:1;width:100%;height:100%;overflow:visible;pointer-events:none}\r\n.pass-route-main,.pass-route-branch{fill:none;stroke:#77d9ed;stroke-width:1.5;stroke-opacity:.55;vector-effect:non-scaling-stroke;filter:drop-shadow(0 0 4px #52d5f9)}\r\n.pass-route-branch{stroke:#b8a4f3;stroke-width:1;stroke-opacity:.5;stroke-dasharray:3 5}\r\n.pass-area[data-future=\"true\"]:after,.pass-current-fog{z-index:2;background:linear-gradient(#02061170,#03091a82 60%,#02071375)}\r\n.pass-world-title{position:absolute;top:18px;left:5%;z-index:5;gap:2px;padding:0;max-width:90%;text-shadow:0 1px 7px #010511,0 0 14px #79c3ea88}\r\n.pass-world-title small{font-size:.61rem}.pass-world-title b{font-size:clamp(1rem,2.3vw,1.45rem)}.pass-world-title span{font-size:.65rem}\r\n.pass-constellation{top:auto;right:auto;z-index:3;width:min(32vw,180px);transform:translate(-50%,-3px);gap:4px;filter:drop-shadow(0 0 7px #b991ff70)}\r\n.pass-coach-star{display:block;width:10px;height:10px;background:#f7e4ff;transform:rotate(45deg);box-shadow:0 0 0 4px #bc91e14d,0 0 16px 7px #c59bff9c}\r\n.pass-coach-caption{display:grid;gap:2px;padding:0;border:0;background:none;text-shadow:0 1px 5px #010410,0 0 7px #a479d5}\r\n.pass-coach-caption span{font-size:.53rem}.pass-coach-caption strong{font-size:clamp(.72rem,1.5vw,1rem)}.pass-coach-caption small,.pass-coach-caption p{font-size:.55rem}.pass-coach-caption p{line-height:1.15}\r\n.pass-levels{position:absolute;inset:0;z-index:3;display:block;width:auto;margin:0;padding:0}\r\n.pass-level,.pass-level:nth-child(even),.pass-level[data-state=earned],.pass-level[data-state=current],.pass-level[data-state=locked]{position:absolute;top:var(--node-y);left:var(--node-x);display:flex;align-items:center;gap:8px;width:clamp(155px,21vw,205px);min-height:0;margin:0;padding:0;border:0;border-radius:0;background:none;box-shadow:none;transform:translateY(-50%);color:#c5dbe2;text-shadow:0 1px 6px #000,0 0 8px #5ebde375}\r\n.pass-level.is-right,.pass-level.is-right:nth-child(even){flex-direction:row-reverse;transform:translate(-100%,-50%);text-align:right}\r\n.pass-level[data-state=locked]{opacity:.84}.pass-level[data-state=earned]{color:#d6ffe9}.pass-level[data-state=current]{color:#fff3cc;text-shadow:0 0 11px #f8d77c}\r\n.pass-level-number{flex:none;width:30px;height:30px;border:2px solid currentColor;border-radius:50%;background:#0a2030;box-shadow:0 0 10px #78d7ea9a,0 0 0 4px #6fcbdd30;font-size:.72rem;line-height:1}\r\n.pass-level[data-state=earned] .pass-level-number{background:#174a45;box-shadow:0 0 13px #8affc3,0 0 0 4px #87efbd4d}\r\n.pass-level[data-state=current] .pass-level-number{background:#6f552a;box-shadow:0 0 17px #ffe09d,0 0 0 5px #ffdc8a66}\r\n.pass-level-body{gap:2px;min-width:0}.pass-level-body strong{font-size:.72rem;line-height:1.15}.pass-level-body span,.pass-level-body small{font-size:.57rem;line-height:1.1;color:inherit}\r\n.pass-fire,.pass-coach-art{display:none}\r\n@media(max-width:650px){.pass-area,.pass-area[data-chapter=\"0\"],.pass-area[data-chapter=\"9\"],.pass-area[data-chapter=\"10\"],.pass-area[data-chapter=\"14\"],.pass-area[data-chapter=\"18\"],.pass-area[data-chapter=\"22\"],.pass-area[data-chapter=\"23\"],.pass-area[data-chapter=\"24\"]{min-height:1300px}.pass-level,.pass-level:nth-child(even),.pass-level[data-state=earned],.pass-level[data-state=current],.pass-level[data-state=locked]{width:145px;gap:5px}.pass-level-number{width:27px;height:27px;font-size:.65rem}.pass-level-body strong{font-size:.59rem}.pass-level-body span,.pass-level-body small{font-size:.5rem}.pass-constellation{width:30vw}.pass-coach-caption span{font-size:.42rem}.pass-coach-caption strong{font-size:.62rem}.pass-coach-caption small,.pass-coach-caption p{font-size:.44rem}.pass-planet,.pass-area[data-chapter=\"0\"] .pass-planet{width:34px}}\r\n\r\n/* R3-09 giant retro title: XP FLIGHT fills ~38% of the well (CLOSE is the shell Exit key). */\r\n@font-face{font-family:'VT323';src:url('/handborne/fonts/vt323.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n#battlePassPanel .pass-header{flex:0 0 38%;min-height:0;box-sizing:border-box;justify-content:center;text-align:center;position:relative;overflow:hidden;background:radial-gradient(ellipse at 50% 30%,#1c1440,#07050f 75%)}\r\n#battlePassPanel .pass-header h2{display:flex;flex-direction:column;align-items:center;gap:1.2vw;line-height:.9;text-shadow:none;font-size:initial;margin:0;font-family:'VT323',ui-monospace,monospace}\r\n#battlePassPanel .pass-header .pass-title-sub{font-size:max(4vw,13px);letter-spacing:.3em;color:#cfc8e6;opacity:.8;font-family:'VT323',ui-monospace,monospace}\r\n#battlePassPanel .pass-header .pass-title-xp{font-size:min(34vw,200px);color:#ffd35a;text-shadow:0 2px 0 #f39a2a,0 4px 0 #d8641f,0 6px 0 #a83a1a,0 8px 0 #6a1a3a,0 10px 0 #3a0d4a,0 14px 18px #000,0 0 30px rgba(255,154,58,.66);font-family:'VT323',ui-monospace,monospace}\r\n#battlePassPanel .pass-header .pass-title-fl{font-size:min(16vw,100px);color:#9ef0ff;letter-spacing:.08em;text-shadow:0 2px 0 #4aa8e0,0 4px 0 #2a5ab8,0 6px 0 #2a1a7a,0 8px 10px #000,0 0 18px rgba(74,216,255,.88);font-family:'VT323',ui-monospace,monospace}\r\n#battlePassPanel .pass-header .pass-close{position:absolute;top:0;right:0;width:44px;height:44px;opacity:0}\r\n@media(prefers-reduced-motion:no-preference){#battlePassPanel .pass-header .pass-title-xp{animation:pass-title-glow 4s infinite alternate}@keyframes pass-title-glow{0%{text-shadow:0 2px 0 #f39a2a,0 4px 0 #d8641f,0 6px 0 #a83a1a,0 8px 0 #6a1a3a,0 10px 0 #3a0d4a,0 14px 18px #000,0 0 30px rgba(255,154,58,.66)}100%{text-shadow:0 2px 0 #f39a2a,0 4px 0 #d8641f,0 6px 0 #a83a1a,0 8px 0 #6a1a3a,0 10px 0 #3a0d4a,0 14px 18px #000,0 0 30px rgba(255,154,58,1)}}}\r\n","battle-pass-page.mjs":"import {COSMETIC_LEVEL_XP,cosmeticLevel,coachXpMultiplier,DAILY_TRIO_BONUS} from './progression-rules.mjs';\r\nimport {readPerformanceProgress} from './performance-progress.mjs';\r\nimport {COACH_REQUIREMENTS,STARTER_COACH_IDS} from './performance-catalog.mjs';\r\nimport {syncBattlePass} from './battle-pass.mjs';\r\nimport {coachRequirements,embedAchievements,BOSSES} from './achievements-board.mjs';\r\nimport {CHAPTER_COUNT,chapterForLevel,chapterLevels,chapterWorld,chapterCoach,chapterConstellation} from './level-map-domain.mjs';\r\nimport {mountLevelMapScene} from './level-map-scene.mjs';\r\nimport {paintStyleText} from './workout-symbols.mjs';\r\nimport {mountFlightScroll,sectionScrollTop,hyperDuration,hyperEase,FLIGHT_TUNING} from './flight-scroll.mjs';\r\n\r\nconst node=(tag,value,className)=>{const el=document.createElement(tag);if(value!=null)el.textContent=value;if(className)el.className=className;return el;};\r\nconst localDay=()=>{const d=new Date();return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;};\r\nconst format=x=>Math.floor(x).toLocaleString();\r\nconst validShips=new Set(['supportive','direct','analytical','playful','calm','mom']);\r\nfunction selectedShip(){\r\n try{\r\n  const owner=globalThis.myr5AuthenticatedAccount?.user?.id;\r\n  const custom=JSON.parse(localStorage.getItem(`myr5-ship-customization-v1/${owner}`)||'{}');\r\n  const recipe=JSON.parse(localStorage.getItem('myr5-recipe-v1')||'{}');\r\n  const choice=custom.ship||recipe.shipId||recipe.coach;\r\n  return validShips.has(choice)?choice:'supportive';\r\n }catch{return 'supportive';}\r\n}\r\nconst svgNode=(tag,attrs={})=>{const el=document.createElementNS('http://www.w3.org/2000/svg',tag);for(const [key,value] of Object.entries(attrs))el.setAttribute(key,String(value));return el;};\r\nexport function mountBattlePass(){\r\n if(document.getElementById('battlePassPanel'))return;\r\n // R-1017 one achievements page: the CRT is the in-game section menu (bottom of the flight screen). Its arrows pick a\r\n // section (systems 1-25, then the coach constellation at the top); tapping the CRT hyper-jumps the ship there.\r\n const entry=node('button',null,'battle-pass-entry pass-crt-entry');entry.type='button';entry.id='battlePassOpen';\r\n const previewRack=node('div',null,'pass-crt-rack pass-hud');\r\n const previewPrev=node('button','◀','pass-crt-switch'),previewNext=node('button','▶','pass-crt-switch');\r\n for(const [button,label] of [[previewPrev,'Previous section'],[previewNext,'Next section']]){\r\n  button.type='button';button.setAttribute('aria-label',label);\r\n }\r\n previewRack.append(previewPrev,entry,previewNext);\r\n const dialog=node('dialog',null,'launch-panel battle-pass-page');dialog.id='battlePassPanel';dialog.setAttribute('aria-labelledby','battlePassTitle');\r\n const header=node('header',null,'pass-header'),heading=node('h2');heading.id='battlePassTitle';heading.setAttribute('aria-label','Achievements, XP Flight');heading.innerHTML='<small class=\"pass-title-sub\" aria-hidden=\"true\">ACHIEVEMENTS</small><b class=\"pass-title-xp\" aria-hidden=\"true\">XP</b><b class=\"pass-title-fl\" aria-hidden=\"true\">FLIGHT</b>';\r\n const close=node('button','CLOSE','pass-close');close.type='button';close.onclick=()=>dialog.close();header.append(heading,close);\r\n // R-1017: the game fills the panel. Score, level bar and the daily quests (a one-line chip that expands) ride in a slim\r\n // band across the top of the game screen; the CRT section menu is a band along its bottom. Nothing in the scene draws under either.\r\n const daily=node('details',null,'pass-daily'),summary=node('section',null,'pass-summary'),topHud=node('div',null,'pass-top-hud');\r\n const shell=node('section',null,'pass-map-shell');shell.setAttribute('aria-label','Continuous cosmetic reward flight map');\r\n const viewport=node('div',null,'pass-map-viewport');viewport.tabIndex=0;viewport.setAttribute('role','region');viewport.setAttribute('aria-label','The coach constellation at the top, then all 250 levels down to level 1 at the bottom. Scroll to inspect.');\r\n const route=node('div',null,'pass-route');viewport.append(route);\r\n const canvas=node('canvas',null,'pass-scene');canvas.setAttribute('aria-hidden','true');\r\n shell.append(topHud,viewport,canvas,previewRack);\r\n const flightScore=node('div',null,'pass-flight-score');flightScore.setAttribute('aria-label','Asteroid flight score');\r\n const hits=node('strong'),score=node('strong'),multiplier=node('strong');\r\n flightScore.append(hits,score,multiplier);flightScore.title='Each hit earns the current multiplier in flight points, then increases it by one. An asteroid passing the bottom resets the multiplier to ×1.';\r\n topHud.append(flightScore,summary,daily);\r\n dialog.append(header,shell);document.body.append(dialog);\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n const scene=mountLevelMapScene(canvas,{ship:selectedShip(),reducedMotion:reduced.matches,onScore:state=>{\r\n  hits.textContent=`HITS ${format(state.hits)}`;score.textContent=`SCORE ${format(state.score)}`;multiplier.textContent=`×${format(state.multiplier)}`;\r\n  flightScore.dataset.multiplier=String(state.multiplier);\r\n }});\r\n let previewChapter=0,rank=null,performance=null,hasPreviewSelection=false,renderedLevel=0,board=null;\r\n function renderPreview(){\r\n  if(!rank)return;\r\n  previewPrev.disabled=previewChapter===0;previewNext.disabled=previewChapter===CHAPTER_COUNT;\r\n  if(previewChapter===CHAPTER_COUNT){\r\n   const golden=performance?.goldenCoaches?.length||0;\r\n   entry.replaceChildren(node('strong','COACH CONSTELLATION'),node('small',`${performance?.coaches?.length||0} / ${BOSSES.length} COACHES · ${golden} GOLDEN`),node('small','» TAP TO HYPERJUMP'));\r\n   entry.setAttribute('aria-label','Hyperjump to the coach constellation');return;\r\n  }\r\n  const world=chapterWorld(previewChapter),levels=chapterLevels(previewChapter,COSMETIC_LEVEL_XP);\r\n  const current=previewChapter===chapterForLevel(rank.level);\r\n  const line1=`SYSTEM ${String(previewChapter+1).padStart(2,'0')} / 25 · ${world.name.toUpperCase()}`;\r\n  const line2=current?(rank.max?'LEVEL 250 COMPLETE':`LV ${rank.level} · ${Math.ceil(rank.next-rank.xp).toLocaleString()} XP TO NEXT`):`LEVELS ${levels[0].level}–${levels.at(-1).level} · ${format(levels[0].xp)}–${format(levels.at(-1).xp)} XP`;\r\n  const line3='10 PACKS · » TAP TO HYPERJUMP';\r\n  entry.classList.add('pass-crt-entry');\r\n  const meter=node('span',null,'pass-entry-meter');meter.setAttribute('aria-hidden','true');meter.style.setProperty('--pass-fill',`${current?(rank.max?100:Math.max(0,Math.min(100,rank.into/rank.need*100))):0}%`);\r\n  const chart=svgNode('svg',{class:'pass-crt-chart',viewBox:'0 0 100 29','aria-hidden':'true',preserveAspectRatio:'none'});\r\n  const positions=[[4,20],[14,8],[25,18],[36,6],[46,21],[57,11],[68,22],[78,7],[89,18],[98,6]];\r\n  chart.append(svgNode('path',{d:`M ${positions.map(([x,y])=>`${x} ${y}`).join(' L ')}`,class:'pass-crt-line'}));\r\n  chart.append(svgNode('path',{d:'M 36 6 L 43 2 M 68 22 L 75 27',class:'pass-crt-branch'}));\r\n  for(let index=0;index<levels.length;index++){\r\n   const [x,y]=positions[index],level=levels[index].level;\r\n   chart.append(svgNode('rect',{x:x-1.3,y:y-1.3,width:2.6,height:2.6,class:`pass-crt-dot ${level<rank.level?'is-earned':level===rank.level?'is-current':'is-locked'}`}));\r\n  }\r\n  entry.replaceChildren(node('strong',line1),node('small',line2),chart,node('small',line3),meter);\r\n  entry.setAttribute('aria-label',`Hyperjump to system ${previewChapter+1}, ${world.name}, levels ${levels[0].level} through ${levels.at(-1).level}`);\r\n }\r\n function renderDaily(p){\r\n  const day=p.days?.[localDay()]||{};\r\n  const workout=Number(day.workoutXp??day.baseXp??0)>0||!!day.workout,meditation=!!day.meditation,food=!!day.food;\r\n  const quests=node('div',null,'pass-quests');\r\n  for(const [label,complete] of [['Workout',workout],['Meditation ×2',meditation],['Food',food]]){\r\n   const item=node('span',`${complete?'✓':'○'} ${label}`);item.dataset.complete=String(complete);quests.append(item);\r\n  }\r\n  const coaches=p.coaches.filter(id=>!STARTER_COACH_IDS.includes(id)).length;\r\n  const done=[workout,meditation,food].filter(Boolean).length,chip=node('summary',null,'pass-daily-chip');\r\n  chip.append(node('span',`DAILY ${done}/3`),node('strong',done===3?`✓ +${DAILY_TRIO_BONUS} XP EARNED`:`ALL THREE +${DAILY_TRIO_BONUS} XP`));\r\n  chip.setAttribute('aria-label',`Today's daily quests, ${done} of 3 done. Show details`);\r\n  daily.replaceChildren(chip,quests,node('small',`Meditation doubles workout XP · coach multiplier ×${coachXpMultiplier(coaches)}`));\r\n }\r\n function renderSummary(){\r\n  const progress=node('progress');progress.max=rank.max?1:rank.need;progress.value=rank.max?1:rank.into;progress.setAttribute('aria-label','Progress to next cosmetic level');\r\n  summary.replaceChildren(node('strong',`LV ${rank.level} / 250`),node('span',`${format(rank.xp)} TOTAL XP`),progress,node('b',rank.max?'ALL LEVELS REACHED':`${Math.ceil(rank.next-rank.xp).toLocaleString()} XP → LV ${rank.level+1}`));\r\n }\r\n function buildArea(chapter){\r\n  const world=chapterWorld(chapter),levels=chapterLevels(chapter,COSMETIC_LEVEL_XP),coach=chapterCoach(chapter,COACH_REQUIREMENTS),geometry=chapterConstellation(chapter);\r\n  const area=node('section',null,'pass-area');area.dataset.chapter=String(chapter);area.dataset.future=String(chapter>chapterForLevel(rank.level));\r\n  area.style.setProperty('--world-intensity',world.intensity);area.style.setProperty('--world-hue',`${205+chapter*12}deg`);\r\n  area.style.setProperty('--object-x',`${chapter%2?13:81}%`);area.style.setProperty('--object-y',`${16+(chapter*11)%19}%`);\r\n  area.setAttribute('aria-label',`System ${chapter+1}: ${world.name}, levels ${levels[0].level} through ${levels.at(-1).level}`);\r\n  const sky=node('div',null,'pass-sky'),planet=node('div',null,'pass-planet');\r\n  sky.setAttribute('data-peer-depth','far');planet.setAttribute('data-peer-depth','mid');sky.setAttribute('aria-hidden','true');planet.setAttribute('aria-hidden','true');\r\n  const lines=svgNode('svg',{class:'pass-route-lines',viewBox:'0 0 100 100',preserveAspectRatio:'none','aria-hidden':'true'});\r\n  lines.append(svgNode('path',{d:geometry.main,class:'pass-route-main'}),svgNode('path',{d:geometry.branch,class:'pass-route-branch'}));\r\n  area.append(sky,planet,lines);\r\n  const title=node('div',null,'pass-world-title');\r\n  title.append(node('small',`SYSTEM ${String(chapter+1).padStart(2,'0')} / 25 · LEVELS ${levels[0].level}–${levels.at(-1).level}`),node('b',world.name),node('span',world.body));\r\n  area.append(title);\r\n  const constellation=node('aside',null,'pass-constellation');constellation.setAttribute('data-peer-depth','mid');\r\n  constellation.style.left=`${geometry.coach.x}%`;constellation.style.top=`${geometry.coach.y}%`;\r\n  if(coach){\r\n   const star=node('span',null,'pass-coach-star');star.setAttribute('aria-hidden','true');\r\n   const caption=node('div',null,'pass-coach-caption');\r\n   const requirement=coachRequirements(coach.id);\r\n   caption.append(node('span','COACH CONSTELLATION'),node('strong',coach.name),node('small',performance?.coaches.includes(coach.id)?'PERFORMANCE UNLOCKED':'PERFORMANCE MILESTONE'),paintStyleText(node('p'),requirement?.unlock||'Earn through a workout milestone.'));\r\n   constellation.append(star,caption);\r\n  }\r\n  area.append(constellation);\r\n  const track=node('ol',null,'pass-levels');track.setAttribute('aria-label',`Cosmetic rewards in system ${chapter+1}, highest level first`);\r\n  for(const [index,item] of [...levels].reverse().entries()){\r\n   const point=geometry.nodes[index],row=node('li',null,`pass-level ${point.x>50?'is-right':'is-left'}`);\r\n   row.style.setProperty('--node-x',`${point.x}%`);row.style.setProperty('--node-y',`${point.y}%`);\r\n   row.dataset.level=String(item.level);row.dataset.state=item.level<rank.level?'earned':item.level===rank.level?'current':'locked';\r\n   if(item.level===rank.level)row.setAttribute('aria-current','step');\r\n   const body=node('div',null,'pass-level-body');\r\n   body.append(node('strong',item.level===1?'Starter cosmetics':`${item.tier} cosmetic pack`),node('span',item.level===1?'Your launch kit':`${format(item.xp)} XP threshold`));\r\n   if(item.level===rank.level)body.append(node('small',rank.max?'Final level reached':`${Math.ceil(rank.next-rank.xp).toLocaleString()} XP to next unlock`));\r\n   row.append(node('b',String(item.level),'pass-level-number'),body);track.append(row);\r\n  }\r\n  area.append(track);\r\n  if(chapter===chapterForLevel(rank.level)){\r\n   const fog=node('div',null,'pass-current-fog');fog.setAttribute('aria-hidden','true');area.append(fog);\r\n   const currentIndex=levels.at(-1).level-rank.level;\r\n   fog.style.height=`${geometry.nodes[currentIndex].y}%`;\r\n  }\r\n  return area;\r\n }\r\n function buildMap(){\r\n  const fragment=document.createDocumentFragment();\r\n  for(let chapter=CHAPTER_COUNT-1;chapter>=0;chapter--)fragment.append(buildArea(chapter));\r\n  route.replaceChildren(fragment);renderedLevel=rank.level;\r\n }\r\n function render({resetPreview=false}={}){\r\n  const p=readPerformanceProgress();performance=p;rank=cosmeticLevel(p.totalXp);\r\n  if(resetPreview||!hasPreviewSelection)previewChapter=chapterForLevel(rank.level);\r\n  renderPreview();renderDaily(p);renderSummary();scene.setShip(selectedShip());\r\n  if(dialog.open&&renderedLevel!==rank.level){\r\n   const oldScroll=viewport.scrollTop;buildMap();viewport.scrollTop=oldScroll;\r\n  }\r\n }\r\n previewPrev.onclick=()=>{hasPreviewSelection=true;previewChapter=Math.max(0,previewChapter-1);renderPreview();};\r\n previewNext.onclick=()=>{hasPreviewSelection=true;previewChapter=Math.min(CHAPTER_COUNT,previewChapter+1);renderPreview();};\r\n // Content y (px down the scroller) of a section's marker: the constellation's top, your level, or a system's first level.\r\n const contentY=el=>el.getBoundingClientRect().top-viewport.getBoundingClientRect().top+viewport.scrollTop;\r\n function sectionTarget(chapter){\r\n  if(chapter===CHAPTER_COUNT)return 0;\r\n  const row=chapter===chapterForLevel(rank.level)?route.querySelector('[aria-current=\"step\"]'):route.querySelector(`[data-level=\"${chapter*10+1}\"]`);\r\n  return row?sectionScrollTop(contentY(row)+row.offsetHeight/2,viewport.clientHeight,viewport.scrollHeight):viewport.scrollTop;\r\n }\r\n // Start at level 1 at the bottom; scrolling upward then carries the ship through ascending levels to the coach.\r\n const flightStart=()=>Math.max(0,viewport.scrollHeight-viewport.clientHeight);\r\n let jump=0;\r\n const endJump=()=>{if(jump)cancelAnimationFrame(jump);jump=0;scene.setWarp(0);delete dialog.dataset.hyper;};\r\n // Hyper-speed: the page rushes to the section (eased), the scene streaks at the scroll speed. Reduced motion: a cut.\r\n function hyperJump(chapter){\r\n  endJump();stopPointer();\r\n  const from=viewport.scrollTop,to=sectionTarget(chapter),dist=to-from;\r\n  if(reduced.matches||Math.abs(dist)<2){viewport.scrollTop=to;return;}\r\n  const dur=hyperDuration(dist);let t0=null,last=from,lastT=0; // (performance is the progress object in this scope)\r\n  dialog.dataset.hyper='true';\r\n  const step=now=>{\r\n   if(t0===null)t0=lastT=now;\r\n   const u=Math.min(1,(now-t0)/dur),y=from+dist*hyperEase(u);viewport.scrollTop=y;\r\n   scene.setWarp(-(y-last)/Math.max(1,now-lastT));last=y;lastT=now;\r\n   if(u<1)jump=requestAnimationFrame(step);else endJump();\r\n  };\r\n  jump=requestAnimationFrame(step);\r\n }\r\n entry.onclick=()=>hyperJump(previewChapter);\r\n let activePointer=null;\r\n const flightScroll=mountFlightScroll(viewport);\r\n const stopPointer=()=>{activePointer=null;flightScroll.stop();scene.stopFiring();};\r\n shell.addEventListener('pointerdown',e=>{\r\n  if(jump&&!e.target.closest('button'))endJump();\r\n  if(activePointer!==null||!e.isPrimary||e.button!==0||e.target.closest('button,a,input,textarea,select,summary,[contenteditable],.pass-top-hud,.pass-hud'))return;\r\n  e.preventDefault();activePointer=e.pointerId;shell.setPointerCapture(e.pointerId);\r\n  scene.aim(e.clientX,e.clientY);flightScroll.aim(e.clientY);scene.startFiring();\r\n });\r\n document.addEventListener('pointermove',e=>{if(e.pointerId===activePointer&&dialog.open){e.preventDefault();scene.aim(e.clientX,e.clientY);flightScroll.aim(e.clientY);}});\r\n shell.addEventListener('lostpointercapture',stopPointer);\r\n document.addEventListener('pointerup',e=>{if(e.pointerId===activePointer)stopPointer();});\r\n document.addEventListener('pointercancel',e=>{if(e.pointerId===activePointer)stopPointer();});\r\n window.addEventListener('blur',stopPointer);\r\n  dialog.addEventListener('keydown',e=>{if(e.code==='Space'&&!e.repeat&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&!e.target.closest('button,input,textarea,select')){e.preventDefault();scene.fire();}});\r\n dialog.addEventListener('close',()=>{endJump();stopPointer();scene.stop();window.dispatchEvent(new CustomEvent('myr5:music-scene',{detail:{scene:null}}));});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden){stopPointer();scene.stop();}else if(dialog.open)scene.start();});\r\n window.addEventListener('resize',()=>{if(dialog.open)scene.resize();});\r\n const open=()=>{\r\n  syncBattlePass();render();\r\n  if(!dialog.open){scene.resetScore?.();dialog.showModal();window.dispatchEvent(new CustomEvent('myr5:music-scene',{detail:{scene:'battlepass'}}));}\r\n  if(!board){board=embedAchievements(dialog,shell);viewport.prepend(board.root);}\r\n  buildMap();board.open();hasPreviewSelection=false;previewChapter=chapterForLevel(rank.level);renderPreview();\r\n  requestAnimationFrame(()=>{if(dialog.open)viewport.scrollTop=flightStart();});\r\n  scene.start();return dialog;\r\n };\r\n window.myr5Menus={...window.myr5Menus,battlePass:open,achievements:open};\r\n window.addEventListener('myr5:performance-progress',()=>{syncBattlePass();render();});\r\n window.addEventListener('myr5:account-ready',()=>render());\r\n window.addEventListener('storage',()=>render());\r\n render();\r\n}\r\n","battle-pass-rewards.mjs":"import {shipDetails} from './modules/ships/ship-catalog.mjs';\r\n// Battle-pass reward table (rank 6b) — pure data + one lookup, no storage, no DOM.\r\n// Board rows are D30 (the owner's achievements board, plan/handoff/achievements-board);\r\n// item content is plan/muse/item-catalog.json, copied row-for-row below because this worktree\r\n// has no plan/ dir at build time and root .json files are left out of the AGPL source offer\r\n// (scripts/build.mjs). Regenerate by hand if the catalog changes. Food rows and the `keys`\r\n// list are not copied: D32 gives Food no weapons/pet/boss (only palettes + bonuses, below), and\r\n// keys are not pass rewards.\r\n//\r\n// Ladder per boss = D22 (L1 weapon 1 · L2 palette + boss texture · L3 weapon 2 · L4 pet ·\r\n// L5 aura + boss skin) + D16 (the style's 3 textures at L1/L3/L5) + D17 (special at L3) —\r\n// the same text as the board's LEVELS. D30 vs D21: D21 has one boss per family; D30 has rows\r\n// of 3–6 consecutive bosses per track plus two shared ones. Following D30:\r\n//  - boss looks (texture L2, skin L5) belong to each board boss, not to the family;\r\n//  - a style's catalog items (2 weapons, 3 textures, special, aura, palette) exist once, so\r\n//    they sit on the row's FIRST boss; D32 fills the other bosses' empty L1/L3/L4 with palettes\r\n//    from creature/source/creator/palettes.json (each row's `reward` names its slot);\r\n//  - D21 sharing still applies to pets: one pet per family, and the second row of a family\r\n//    gets that family's substitute palette at L4 instead (resolved in battle-pass.mjs).\r\n\r\nimport PALETTES from './creature/source/creator/palettes.json' with {type:'json'};\r\nimport SKIN_CATALOG from './creature/source/creator/creature-skins.json' with {type:'json'};\r\nimport {FOOD_BONUS_DAMAGE_MULTIPLIER} from './combat-config.mjs';\r\nimport {STYLES as LEGACY_STYLES} from './creature/source/creator/catalog.ts';\r\nimport {FREE_TEXTURE_IDS,RETIRED_TEXTURE_IDS} from './creature/source/creator/texture-policy.mjs';\r\nexport {FREE_TEXTURE_IDS};\r\n\r\n// D30 rows, top to bottom — ids/order/counts must match achievements-board.mjs TIERS.\r\nexport const ROWS=Object.freeze([\r\n {id:'strider',name:'Strider',track:'chest',bosses:6},\r\n {id:'ringer',name:'Ringer',track:'quads',bosses:5},\r\n {id:'manyarm',name:'Manyarm',track:'glutes',bosses:5},\r\n {id:'wedge',name:'Wedge',track:'arms-shoulders',bosses:5},\r\n {id:'warden',name:'Warden',track:null,bosses:1},\r\n {id:'blob',name:'Blob',track:'yoga',bosses:4},\r\n {id:'cap',name:'Cap',track:'martial-arts',bosses:3},\r\n {id:'stalk',name:'Stalk',track:'cardio',bosses:4},\r\n {id:'tanka',name:'Tanka',track:'meditation',bosses:4},\r\n {id:'lume',name:'Lume',track:null,bosses:1},\r\n]);\r\nexport const LEVELS_PER_BOSS=5;\r\n// Same ids/names the board builds: `${row}-${k}`, name numbered only in multi-boss rows.\r\nexport const BOSSES=Object.freeze(ROWS.flatMap(row=>Array.from({length:row.bosses},(_,i)=>({\r\n id:`${row.id}-${i+1}`,name:row.bosses>1?`${row.name} ${i+1}`:row.name,row:row.id,track:row.track,index:i+1,\r\n}))));\r\n\r\n// D25 track -> circuit.mjs step-track id, item-catalog `track`, D21 family, L2 palette.\r\n// Palettes: L2 reuses the aura-milestone palettes (pal-01…12) — one per row, plus one D21\r\n// substitute per two-row family. Every other palette slot comes from palettes.json `reward`.\r\nexport const TRACKS=Object.freeze({\r\n chest:{circuit:'chest',catalog:'chest',family:'push',name:'Chest',palette:'pal-01'},\r\n quads:{circuit:'legs',catalog:'quads',family:'legs',name:'Quads',palette:'pal-02'},\r\n glutes:{circuit:'hips',catalog:'glutes',family:'legs',name:'Glutes',palette:'pal-03'},\r\n 'arms-shoulders':{circuit:'shoulders',catalog:'arms',family:'push',name:'Arms & Shoulders',palette:'pal-04'},\r\n yoga:{circuit:'yoga',catalog:'yoga',family:'flow',name:'Yoga',palette:'pal-05'},\r\n 'martial-arts':{circuit:'martial-arts',catalog:'martial-arts',family:'motion',name:'Martial Arts',palette:'pal-06'},\r\n cardio:{circuit:'cardio',catalog:'cardio',family:'motion',name:'Cardio',palette:'pal-07'},\r\n meditation:{circuit:'meditation',catalog:'meditation',family:'meditation',name:'Meditation',palette:'pal-08'},\r\n});\r\nexport const PET_SUBSTITUTE_PALETTE=Object.freeze({push:'pal-09',legs:'pal-10',motion:'pal-11'});\r\n\r\n// Two additive post-download skin collections: all collection slots repeat at L1/L3/L5.\r\n// This distribution depends only on each track's own ladder, never family grouping.\r\nexport const CREATURE_SKIN_REWARDS=Object.freeze(SKIN_CATALOG.skins.map(skin=>Object.freeze({\r\n kind:'creature-skin',id:skin.id,name:skin.displayName,line:`${skin.realm} creature finish · ${skin.rarity}`,\r\n track:skin.track,level:skin.runtime.unlock.level,pack:skin.runtime.pack,collection:skin.collection,\r\n})));\r\n\r\n// Every track receives a midpoint and end milestone, cycling six unique ships.\r\n// Duplicate grants are idempotent; family/grouping does not affect placement.\r\nconst SHIPS=['supportive','direct','analytical','playful','calm','mom'];\r\nexport const SHIP_DEFINITIONS=Object.freeze(Object.values(TRACKS).flatMap(({catalog:track},index)=>\r\n [3,5].map((level,slot)=>{const ship=SHIPS[(index*2+slot)%SHIPS.length];return Object.freeze({id:`ship-${ship}`,ship,name:shipDetails(ship).name,track,level});})\r\n));\r\n\r\n// --- item-catalog.json `weapons` (D21: 2 per style) ---\r\nconst WEAPONS=[\r\n ['chest-w1','Glove on a Stick','For winning arguments with the air.'],['chest-w2','Chrome Bar Mace','It benches you.'],\r\n ['arms-w1','Dumbbellchucks','Twice the dumbbell, half the coordination.'],['arms-w2','Pull-Up Staff','For reaching the top shelf of victory.'],\r\n ['quads-w1','Squat-Rack Axe','Leg day has a new ambassador.'],['quads-w2','Kettlebell Flail','Swings hard, asks questions never.'],\r\n ['glutes-w1','Band Slingshot','Resistance is futile. Literally.'],['glutes-w2','Hip-Thrust Hammer','Bridges: now with consequences.'],\r\n ['yoga-w1','Mat Bo Staff','Rolled with intention. Swung with more.'],['yoga-w2','Block Gauntlets','Namaste out of the way.'],\r\n ['martial-arts-w1','Black-Belt Whip','It earned its own belt.'],['martial-arts-w2','Dummy Club','The dummy saw it coming. Still lost.'],\r\n ['cardio-w1','Jump-Rope Lasso','For roping runaway heart rates.'],['cardio-w2','Shoe Boomerang','It always comes back. Like soreness.'],\r\n ['meditation-w1','Singing-Bowl Aegis','Om. Also, clang.'],['meditation-w2','Incense Wand','Smells like victory. Allegedly.'],\r\n];\r\n// --- item-catalog.json `textures` (D14: 3 per style, slot order = texture-1/2/3; ids match materials-registry.ts) ---\r\nconst TEXTURES=[\r\n ['chest-plate-steel','Plate Steel','Now with 40% more clank.'],['chest-rubber-grip','Rubber Grip','For hands that mean business.'],['chest-chain-mail','Chain Mail','Medieval. But make it cardio.'],\r\n ['quads-track-rubber','Track Rubber','Smells faintly of starting blocks.'],['quads-denim','Denim','For legs that never skip jeans either.'],['quads-hex-tread','Hex Tread','Grip: gecko-grade.'],\r\n ['glutes-sweatshirt-fleece','Sweatshirt Fleece','Like a hug from laundry day.'],['glutes-quilted','Quilted','Grandma approved. Gains approved harder.'],['glutes-peach','Speckled','Ripe. Do not squeeze the coach.'],\r\n ['arms-hammered-bronze','Hammered Bronze','Shiny. Slightly dented. Like all of us.'],['arms-rope','Fine Stripe','Climb every metaphor.'],['arms-leather','Snake Skin','Broken in. Like you, after set three.'],\r\n ['yoga-cork','Holey','Floats. Like your downward dog.'],['yoga-woven-mat','Woven Mat','Hand-woven by extremely calm spiders.'],['yoga-petal','Petal','Delicate. Unlike your warrior two.'],\r\n ['martial-arts-canvas-gi','Graph Paper','It has seen things. Mostly laundry.'],['martial-arts-bamboo','Bamboo','Bends. Does not break. Unlike resolutions.'],['martial-arts-dragon-scale','Dragon Scale','The dragon is fine. It donated.'],\r\n ['cardio-mesh','Cool Graph Paper','Maximum airflow. Minimum excuses.'],['cardio-terry-cloth','Terry Cloth','Absorbs sweat and bad decisions.'],['cardio-pebble-path','Pebble Path','A tiny trail, wherever you go.'],\r\n ['meditation-sand-garden','Wiggles','Raked by a very patient rake.'],['meditation-river-stone','River Stone','Smooth. Unbothered. Goals.'],['meditation-moss','Moss','Grows on you. Literally, now.'],\r\n];\r\n// #140 (D45; slots approved D47): the once-free legacy textures (materials-registry.ts `legacy-<n>`,\r\n// names = catalog.ts STYLES) are battle-pass rewards now, each in the slot of a catalog texture above,\r\n// which became free instead. The ONE swap table; materials-registry.ts derives its locks from it.\r\n// Reshuffle = move a legacy row to another freed id. freed catalog id -> [legacy id, name, line].\r\nexport const TEXTURE_SWAP=Object.freeze({\r\n 'chest-rubber-grip':['legacy-15','Magma','Runs hot. Cools never.'], // Chest L3\r\n 'glutes-sweatshirt-fleece':['legacy-13','Stone Golem','Built slow. Built to last.'], // Glutes L1 (Ian's \"rock golem\")\r\n 'arms-rope':['legacy-14','Crystal','Clear eyes. Sharp facets.'], // Arms L3\r\n 'arms-leather':['legacy-8','Spectral','Here in spirit. Also in body.'], // Arms L5\r\n 'yoga-cork':['legacy-20','Fluffy','Maximum floof. Minimum mercy.'], // Yoga L1\r\n 'yoga-woven-mat':['legacy-21','Jelly','Wobbles. Never falls.'], // Yoga L3 (R25: Jelly is retired; the id only keeps this slot's earned pack id stable)\r\n 'cardio-terry-cloth':['legacy-16','Glacial','Cool under pressure. Very cool.'], // Cardio L3\r\n});\r\n// --- item-catalog.json `bosses` (unlock lines reused for every board boss of that family) ---\r\nconst BOSS_LINES={\r\n push:['Forged from lost remote controls.','For the comfiest conqueror.'],\r\n legs:['Always moving. Never in a hurry.','Polished by a thousand commutes.'],\r\n motion:['Nine more minutes. Forever.','Shiny. Like your intentions at 6 AM.'],\r\n flow:['Eight arms. Zero flexibility.','Breathe in. The kraken breathes out.'],\r\n meditation:['99+ unread. All of them urgent.','Silent. At last.'],\r\n};\r\n// --- item-catalog.json `pets` (D21: one per family; the catalog leaves names null) ---\r\nconst PET_LINES={\r\n push:'It followed you home from the gym. Keep it.',legs:'It never skips leg day. It never skips anything.',\r\n motion:'Fast. Loud. Naps hard.',flow:'Bends in ways physics dislikes.',meditation:'It hums when you breathe. Do not ask how.',\r\n};\r\n\r\nconst item=(kind,[id,name,line])=>({kind,id,name,line});\r\nconst rewardPack=(tier,id)=>({kind:'reward-pack',id:`reward-pack:${tier}:${id}`,tier,name:`${tier[0].toUpperCase()+tier.slice(1)} Pack`,line:'Open for one random cosmetic.'});\r\n// The five free texture IDs live in texture-policy.mjs; catalog additions go to packs.\r\n// catalog.ts STYLES names, id order (legacy-<n> textures; legacy-color-<n> colours are \"<name> (original)\").\r\nexport const LEGACY_NAMES=Object.freeze(['Mortal','Verdant','Mycelial','Chitin','Reptilian','Abyssal','Coral','Skeletal','Spectral','Infernal','Celestial','Voidborn','Eldritch','Stone Golem','Crystal','Magma','Glacial','Stormcharged','Clockwork Robot','Neon Synth','Fluffy','Jelly','Baby']);\r\n// The texture pack pool: every non-free texture, once. (TEXTURE_SWAP below only decides which texture a\r\n// battle-pass slot names; the pack pool never depends on it, so no texture can be unreachable.)\r\nexport const textureRewardPool=()=>[\r\n ...TEXTURES.map(t=>item('texture',t)),\r\n ...[[\"opal-jelly\",\"Opal Jelly\"],[\"bubble-glass\",\"Bubble Glass\"],[\"prism-crystal\",\"Prism Crystal\"],[\"holo-foil\",\"Holographic Foil\"],[\"glitter-resin\",\"Glitter Resin\"],[\"galaxy-geode\",\"Galaxy Geode\"],[\"caustic-slime\", \"Caustic Slime\"], [\"blister-hide\", \"Blister Hide\"], [\"rotten-rind\", \"Rotten Rind\"], [\"parasite-nest\", \"Parasite Nest\"], [\"exposed-sinew\", \"Exposed Sinew\"], [\"abyssal-maw\", \"Abyssal Maw\"], [\"circuit-alloy\", \"Circuit Alloy\"], [\"servo-armor\", \"Servo Armor\"], [\"chrome-rib\", \"Chrome Rib\"], [\"carbon-mech\", \"Carbon Mech\"], [\"hazard-panel\", \"Hazard Panel\"], [\"reactor-glass\", \"Reactor Glass\"]].map(([id,name])=>item('texture',[id,name,''])),\r\n ...LEGACY_NAMES.map((name,n)=>item('texture',[`legacy-${n}`,name,''])),\r\n item('texture',['coach-64-bit','64-bit Pixel Finish','A pixel finish for a coach with a boss skin.']),\r\n].filter(t=>!FREE_TEXTURE_IDS.includes(t.id)&&!RETIRED_TEXTURE_IDS.includes(t.id));\r\n// R18 G2b: the 15 free colours span the hue range; each is the nearest (Lab) hex\r\n// already present in the census of palettes.json + SIMPLE_COLORS + LEGACY_COLORS\r\n// (scripts/colour-census.mjs). Everything else is pack-only.\r\nexport const FREE_COLOURS=Object.freeze([\r\n '#060409', // black\r\n '#ffffff', // white\r\n '#7f7d78', // grey\r\n '#ff3b30', // red\r\n '#ff8a2a', // orange\r\n '#ffd100', // yellow\r\n '#2bd97c', // green\r\n '#008c8c', // teal\r\n '#2454d6', // blue\r\n '#6a2bd9', // purple\r\n '#f59ec4', // pink\r\n '#7a5530', // brown\r\n '#c4a77d', // tan\r\n '#0b1a45', // navy\r\n '#9fe2bf', // mint\r\n]);\r\nexport const FREE_COLOUR_NAMES=Object.freeze(['Black','White','Grey','Red','Orange','Yellow','Green','Teal','Blue','Purple','Pink','Brown','Tan','Navy','Mint']); // same order as FREE_COLOURS\r\n// R18 G5: every colour item carries its primary `hex`, so the pack screen can show a swatch (a test pins these to the registry).\r\nexport const colourRewardPool=()=>[\r\n ...PALETTES.map(p=>({kind:'palette',id:p.id,name:p.name})),\r\n ...[['default-slate','Slate','#8b8f9a'],['default-clay','Warm Clay','#b7a68e'],['default-ruby','Ruby','#a23b4a'],['default-sapphire','Sapphire','#2d5aa0'],['default-moss','Moss','#4c7a3f'],['default-gold','Gold','#c9a13a'],['default-charcoal','Charcoal','#333238'],['default-blush','Blush','#d98fa0']].map(([id,name,hex])=>({kind:'color',id,name,hex})), // R18 G2b: none of the 8 simple colours is a free hex\r\n ...LEGACY_NAMES.map((name,n)=>({kind:'color',id:`legacy-color-${n}`,name:`${name} (original)`,hex:n?LEGACY_STYLES[n].primary:'#7946aa'})), // design.ts restyles #0 as Original MYR5\r\n];\r\nfunction packCosmetics(levels,bossId){\r\n for(let level=0;level<levels.length;level++)levels[level]=levels[level].flatMap(reward=>{\r\n  if(reward.kind==='palette')return [rewardPack('uncommon',`${bossId}:L${level+1}:${reward.id}`)];\r\n  if(reward.kind==='texture')return [rewardPack('legendary',`${bossId}:L${level+1}:${reward.id}`)];\r\n  if(reward.kind==='boss-skin')return [{kind:'boss-unlock',id:bossId,name:reward.name.replace(/ Skin$/,' Beaten'),line:'Boss cleared at level 5.'}];\r\n  return [reward];\r\n });\r\n levels[3].push(rewardPack('rare',`${bossId}:L4:64-bit`));\r\n // R20: L2 (boss texture) and L5 (Boss beaten) otherwise show no pack on the board.\r\n const hasPack=level=>level.some(reward=>reward.kind==='reward-pack');\r\n if(!hasPack(levels[1]))levels[1].push(rewardPack('uncommon',`${bossId}:L2:bonus`));\r\n if(!hasPack(levels[4]))levels[4].push(rewardPack('legendary',`${bossId}:L5:bonus`));\r\n return levels;\r\n}\r\nconst find=(list,id)=>list.find(row=>row[0]===id);\r\nexport function paletteItem(id){const p=PALETTES.find(p=>p.id===id);return {kind:'palette',id,name:p.name,line:p.tagline};}\r\n// palettes.json `reward` ('<bossId>:L<n>' | 'food:L<n>') -> palette id.\r\nconst SLOT_PALETTE=new Map(PALETTES.filter(p=>p.reward).map(p=>[p.reward,p.id]));\r\nconst slotPalettes=(owner,count)=>Array.from({length:count},(_,i)=>SLOT_PALETTE.get(`${owner}:L${i+1}`)).map(id=>id?[paletteItem(id)]:[]);\r\nconst petItem=family=>({kind:'pet',id:`${family}-pet`,name:`${family[0].toUpperCase()}${family.slice(1)} pet`,line:PET_LINES[family],family});\r\n\r\n/** The D22 ladder for one board boss: an array of 5 levels, each an array of\r\n * `{kind,id,name,line}` items. Pets carry `family` (D21 sharing, resolved by the caller).\r\n * Pure; returns null for an unknown boss id. */\r\nexport function bossRewards(bossId){\r\n const boss=BOSSES.find(b=>b.id===bossId);\r\n if(!boss)return null;\r\n const meta=boss.track&&TRACKS[boss.track],lines=BOSS_LINES[meta?.family]||[null,null];\r\n const levels=slotPalettes(boss.id,LEVELS_PER_BOSS); // D32 fill (empty for first bosses)\r\n levels[1].push({kind:'boss-texture',id:`${boss.id}-texture`,name:`${boss.name} Texture`,line:lines[0]});\r\n levels[4].push({kind:'boss-skin',id:`${boss.id}-skin`,name:`${boss.name} Skin`,line:lines[1]});\r\n if(!meta||boss.index!==1)return packCosmetics(levels,boss.id);\r\n const c=meta.catalog,tex=TEXTURES.filter(t=>t[0].startsWith(c+'-')).map(t=>TEXTURE_SWAP[t[0]]??t); // catalog order = texture-1/2/3\r\n levels[0].unshift(item('weapon',find(WEAPONS,`${c}-w1`)),item('texture',tex[0]));\r\n levels[1].unshift(paletteItem(meta.palette));\r\n levels[2].push(item('weapon',find(WEAPONS,`${c}-w2`)),{kind:'special',id:`${c}-special`,name:`${meta.name} Special`,line:'Unlocked at level 3.'},item('texture',tex[1]));\r\n levels[3].push(petItem(meta.family));\r\n levels[4].unshift({kind:'aura',id:`${c}-aura`,name:`${meta.name} Aura`,line:'Your first aura look.'});\r\n levels[4].push(item('texture',tex[2]));\r\n for(const skin of CREATURE_SKIN_REWARDS.filter(row=>row.track===c))levels[skin.level-1].push(skin);\r\n for(const ship of SHIP_DEFINITIONS.filter(row=>row.track===c))levels[ship.level-1].push({kind:'ship',id:ship.id,name:ship.name,line:shipDetails(ship.ship).description,ship:ship.ship,pack:'coach-ships-biomes'});\r\n return packCosmetics(levels,boss.id);\r\n}\r\n\r\n// --- Food (D32): no board row, no weapons (item-catalog's food-w1/w2 are never granted), no pet\r\n// or boss. Each Food level (food-photo steps, circuit `tracks.food`) grants its palettes.json\r\n// palette + one bonus. Levels = the food:L<n> palettes, so adding one adds a level. ---\r\nexport const FOOD_BONUS=Object.freeze({id:'food-bonus',name:'Second Helping',line:'Hits harder for one day. Chew responsibly.',\r\n effect:Object.freeze({type:'damage-multiplier',days:1,value:FOOD_BONUS_DAMAGE_MULTIPLIER})});\r\nexport const FOOD_LEVELS=PALETTES.filter(p=>p.reward?.startsWith('food:')).length;\r\n/** Food ladder: FOOD_LEVELS levels, each `[palette, bonus]`. Bonus ids are per level\r\n * (`food-bonus-<n>`) so each grant happens once. */\r\nexport const foodRewards=()=>slotPalettes('food',FOOD_LEVELS).map((items,i)=>[...items.map(reward=>rewardPack('uncommon',`food:L${i+1}:${reward.id}`)),{kind:'bonus',id:`${FOOD_BONUS.id}-${i+1}`,name:FOOD_BONUS.name,line:FOOD_BONUS.line,effect:FOOD_BONUS.effect}]);\r\n","battle-pass.mjs":"// Battle-pass read API (rank 6b, D26/D30) — the logic behind the owner's achievements board.\r\n// No screen here. The board imports selectedTracks() and loadProgress() in place of its two\r\n// stand-ins; battlePassState() is the richer read; syncBattlePass() grants what is newly\r\n// earned and fires `myr5:battle-pass` on window. Usage: plan/reports/battle-pass.md.\r\nimport {STEPS_PER_LEVEL,STYLE_OF_GROUP} from './circuit.mjs';\r\nimport {exerciseFamily} from './workout-route.mjs';\r\nimport {ROWS,BOSSES,TRACKS,LEVELS_PER_BOSS,PET_SUBSTITUTE_PALETTE,FOOD_LEVELS,bossRewards,paletteItem,foodRewards} from './battle-pass-rewards.mjs';\r\nimport {readSelectedTracks} from './chosen-styles.mjs';\r\nimport * as store from './creature/source/creator/unlock-store.ts';\r\nimport * as ledger from './unlock-ledger.mjs';\r\nimport {readPerformanceProgress} from './performance-progress.mjs';\r\nimport {COACH_REQUIREMENTS} from './performance-catalog.mjs';\r\nimport {cosmeticLevel} from './progression-rules.mjs';\r\nimport {completeCosmeticCollection} from './reward-packs.mjs';\r\nexport {setSelectedTracks} from './chosen-styles.mjs';\r\n// The ONE tuning knob: steps per level. Shared with circuit.mjs so the step meter and the board agree.\r\nexport {STEPS_PER_LEVEL};\r\n\r\nconst CIRCUIT_TRACK=Object.fromEntries(Object.entries(TRACKS).map(([track,meta])=>[meta.circuit,track]));\r\nconst clamp=n=>Math.min(LEVELS_PER_BOSS,Math.max(0,n));\r\n\r\n/** D30: the rows open to this user = their selected paths + meditation (D9), as a Set of D25\r\n * track ids. Explicit pick (chosen-styles.mjs) wins; until the oval picker writes one, the\r\n * movements chosen at onboarding stand in (the board's original rule). */\r\nexport function selectedTracks(account=globalThis.myr5AuthenticatedAccount){\r\n let picked=readSelectedTracks();\r\n if(!picked.length)picked=(account?.onboarding?.data?.profile?.exercises||[]).map(mode=>CIRCUIT_TRACK[STYLE_OF_GROUP[exerciseFamily(mode)]]).filter(Boolean);\r\n return new Set(['meditation',...picked]);\r\n}\r\n\r\n// Rank-6 step counts, keyed by circuit.mjs track id: live account progress, else the snapshot\r\n// pod.mjs caches for reopen (myr5-workout-progress-v1).\r\nfunction stepTracks(){\r\n const live=globalThis.coachProgress?.circuit?.tracks;\r\n if(live)return live;\r\n try{return JSON.parse(localStorage.getItem('myr5-workout-progress-v1'))?.circuit?.tracks||{};}catch{return {};}\r\n}\r\n\r\n/** Whole levels earned from a step count (uncapped; the row caps it). */\r\nexport const levelsBeaten=(steps,stepsPerLevel=STEPS_PER_LEVEL)=>Math.floor(Math.max(0,Number(steps)||0)/stepsPerLevel);\r\n\r\n/** `{[bossId]: levelsBeaten 0–5}` for all 38 board bosses. Bosses in a row are consecutive:\r\n * boss k = clamp(level − 5·(k−1), 0, 5). Rows outside selectedTracks() stay at 0. The shared\r\n * bosses (Warden, then Lume) are fed by levels earned past each available row's end, and only\r\n * once every available row is fully beaten.\r\n * opts: {tracks: circuit `tracks` object, account, stepsPerLevel} — all optional. */\r\nexport function loadProgress({tracks=stepTracks(),account,stepsPerLevel=STEPS_PER_LEVEL}={}){\r\n const performance=readPerformanceProgress({account}),result=Object.fromEntries(BOSSES.map(b=>[b.id,0]));\r\n for(const row of ROWS){if(!row.track)continue;const coaches=COACH_REQUIREMENTS.filter(c=>c.tracks.includes(TRACKS[row.track].catalog));for(let index=0;index<row.bosses;index++){const coach=coaches[Math.min(coaches.length-1,Math.floor(index*coaches.length/row.bosses))];if(coach)result[`${row.id}-${index+1}`]=performance.goldenCoaches.includes(coach.id)?5:performance.coaches.includes(coach.id)?4:0;}}\r\n return result;\r\n}\r\n\r\n/** Combat kit level (1–5, D8/D22) for the rest-arena boss after a set of `mode`: the levels beaten\r\n * on that exercise's track = its row's first boss in loadProgress(), so a row outside the user's\r\n * paths fights at L1, same as the board shows it. Core/balance moves sit on Meditation (board rule). */\r\nexport function combatLevel(mode,opts={}){\r\n const track=CIRCUIT_TRACK[STYLE_OF_GROUP[exerciseFamily(mode)]]??'meditation',row=ROWS.find(r=>r.track===track);\r\n return Math.max(1,loadProgress(opts)[`${row.id}-1`]);\r\n}\r\n\r\nconst STORE_KINDS=new Set(['texture','color','palette']);\r\nconst isGranted=(item,opts)=>(STORE_KINDS.has(item.kind)?store:ledger).isGranted(item.kind,item.id,opts);\r\nfunction grant(item,opts){\r\n if(!STORE_KINDS.has(item.kind))return ledger.grantUnlock(item.kind,item.id,opts);\r\n if(store.isGranted(item.kind,item.id))return false;\r\n store.grantUnlock(item.kind,item.id);\r\n return store.isGranted(item.kind,item.id); // false if storage is unavailable\r\n}\r\n\r\n/** Richer read: `{stepsPerLevel, available:[track], progress, bosses:[{id,name,row,track,index,\r\n * levels, rewards:[{level, beaten, items:[{kind,id,name,line,granted}]}]}], food}` in board order.\r\n * D21 pets: in a two-row family the first available row (board order) to beat L4 of its\r\n * first boss holds the pet; the other row shows the family's substitute palette there.\r\n * `food` (D32, no board row): `{steps, levels, maxLevel, rewards}` from food-photo steps; each\r\n * level's items are a palette + a bonus (`kind:'bonus'`, with `effect`). */\r\nexport function battlePassState(opts={}){\r\n const stepsPerLevel=opts.stepsPerLevel??STEPS_PER_LEVEL,available=selectedTracks(opts.account),progress=loadProgress({...opts,stepsPerLevel});\r\n const foodSteps=Math.max(0,Number((opts.tracks??stepTracks())?.food?.steps)||0),foodLevels=Math.min(FOOD_LEVELS,levelsBeaten(foodSteps,stepsPerLevel));\r\n const petOwner=family=>ROWS.find(row=>row.track&&TRACKS[row.track].family===family&&available.has(row.track)&&progress[`${row.id}-1`]>=4)?.id;\r\n const bosses=BOSSES.map(boss=>({...boss,levels:progress[boss.id],rewards:bossRewards(boss.id).map((items,i)=>({\r\n  level:i+1,beaten:progress[boss.id]>i,\r\n  items:items.map(item=>{\r\n   const owner=item.kind==='pet'&&PET_SUBSTITUTE_PALETTE[item.family]&&petOwner(item.family);\r\n   const shown=owner&&owner!==boss.row?(()=>{const palette=paletteItem(PET_SUBSTITUTE_PALETTE[item.family]);return {kind:'reward-pack',id:`reward-pack:uncommon:${boss.id}:L4:${palette.id}`,tier:'uncommon',name:'Uncommon Pack',line:'Open for one random cosmetic.'};})():item;\r\n   return {...shown,granted:isGranted(shown,opts)};\r\n  }),\r\n }))}));\r\n const food={steps:foodSteps,levels:foodLevels,maxLevel:FOOD_LEVELS,rewards:foodRewards().map((items,i)=>({level:i+1,beaten:foodLevels>i,items:items.map(item=>({...item,granted:isGranted(item)}))}))};\r\n return {stepsPerLevel,available:[...available],progress,bosses,food};\r\n}\r\n\r\n/** Grants every beaten level's items not yet granted (idempotent: replaying the same steps\r\n * grants nothing twice), then, only if something was new, dispatches `myr5:battle-pass` on\r\n * window with `{granted:[item], state}`. Returns the same `{granted, state}`. */\r\nexport function syncBattlePass(opts={}){\r\n const state=battlePassState(opts),granted=[];\r\n const rank=cosmeticLevel(readPerformanceProgress(opts).totalXp);\r\n for(let level=2;level<=rank.level;level++){const tier=level%10===0?'legendary':level%5===0?'rare':'uncommon',item={kind:'reward-pack',id:`reward-pack:${tier}:cosmetic-pass-v2:L${level}`,tier,name:`${tier} pack`};if(ledger.grantUnlock(item.kind,item.id,opts))granted.push(item);}\r\n if(rank.max)completeCosmeticCollection(opts);\r\n if(granted.length&&typeof window!=='undefined'&&window.dispatchEvent)window.dispatchEvent(new CustomEvent('myr5:battle-pass',{detail:{granted,state}}));\r\n return {granted,state};\r\n}\r\n\r\n// Grants follow step progress: pod.mjs/launch.mjs publish it as `myr5:account-progress`.\r\nif(typeof window!=='undefined'&&window.addEventListener){window.addEventListener('myr5:account-progress',()=>syncBattlePass());window.addEventListener('myr5:performance-progress',()=>syncBattlePass());}\r\n","breathing-modes.mjs":"// D25: meditation offers two breathing modes inside the existing meditation flow. Pure data +\r\n// pure functions only (no DOM, no timers) so breathing.mjs, the unit tests and the browser test\r\n// all read the same script. A mode never adds a second clock: \"session complete\" stays exactly\r\n// combat.mjs's BreathingSession/BREATHING_MS (3 minutes of active time, re-checked server-side in\r\n// server/combat.mjs completeBreathing). A mode only decides which phase to show for a given\r\n// elapsed-ms value on that one clock.\r\n//\r\n// ponytail: PENDING WELLNESS REVIEW. Every hold length, round count and breath pace below is a\r\n// conservative placeholder, not a production value (ART-AND-CHOICE handoff §7: breath timing,\r\n// contraindications, copy and escape behaviour need wellness/safety review before values are\r\n// locked). Tune them only after that review, then flip PENDING_WELLNESS_REVIEW.\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {BREATHING_MS} from './combat.mjs';\r\n\r\nexport const PENDING_WELLNESS_REVIEW = true;\r\nconst guidedCore = {durationMs:210000,settleMs:20500,breaths:30,breathMs:110000,inhaleMs:8500/3,exhaleMs:2500/3,transitionMs:2500,optionalPauseMs:30000,recoveryInhaleMs:3000,recoveryPauseMs:15000,closingBreathingMs:29000};\r\nexport const GUIDED_ROUND_TIMING = Object.freeze(guidedCore);\r\nexport const GUIDED_ROUND_MS = GUIDED_ROUND_TIMING.durationMs;\r\nexport const GENTLE_DIALOGUE = Object.freeze([\r\n 'Get comfortable.',\r\n 'Breathe in gently.',\r\n 'Breathe out softly.',\r\n 'Keep the breath easy.',\r\n 'Feel your support.',\r\n 'Inhale when ready.',\r\n 'Return to normal.',\r\n 'Stop or skip anytime.'\r\n]);\r\nexport const SEATED_ONLY_NOTICE = 'Practice seated or lying down. Never while driving or in or near water.';\r\nexport const NO_MEDICAL_CLAIM = 'A breathing practice, not medical treatment or advice. Stop any time, and breathe normally if you feel dizzy or unwell.';\r\n\r\nconst stances = ids => Object.freeze(ids.filter(id => EXERCISES[id]).map(id => Object.freeze({id, name: EXERCISES[id].name, cue: EXERCISES[id].cue})));\r\n\r\nexport const BREATHING_MODES = Object.freeze({\r\n 'wim-hof': Object.freeze({\r\n  id: 'wim-hof',\r\n  title: 'One guided breathing round',\r\n  subtitle: '3:30 · 30 easy breaths with an optional pause.',\r\n  seatedOnly: true,\r\n  rounds: 1, breathsPerRound: GUIDED_ROUND_TIMING.breaths, inhaleMs: GUIDED_ROUND_TIMING.inhaleMs, exhaleMs: GUIDED_ROUND_TIMING.exhaleMs, holdMs: GUIDED_ROUND_TIMING.optionalPauseMs, recoveryHoldMs: GUIDED_ROUND_TIMING.recoveryPauseMs, recoveryInhaleMs:GUIDED_ROUND_TIMING.recoveryInhaleMs,transitionMs:GUIDED_ROUND_TIMING.transitionMs,breathMs:GUIDED_ROUND_TIMING.breathMs,settleMs: GUIDED_ROUND_TIMING.settleMs,\r\n }),\r\n 'tai-chi': Object.freeze({\r\n  id: 'tai-chi',\r\n  title: 'Tai chi stance breathing',\r\n  subtitle: 'Slow breaths while you hold a balance or core stance.',\r\n  seatedOnly: false,\r\n  // D25: the existing core/balance exercises (easiest variants first, as the conservative default).\r\n  stances: stances(['low-tree', 'knee-balance', 'knee-plank', 'side-knee-left', 'side-knee-right']),\r\n  stanceHoldMs: 20000, restMs: 10000, inhaleMs: 4000, exhaleMs: 5000,\r\n }),\r\n});\r\nexport const MODE_IDS = Object.freeze(Object.keys(BREATHING_MODES));\r\n\r\nexport function buildScript(id) {\r\n const m = BREATHING_MODES[id];\r\n if (!m) throw Error('Unknown breathing mode: ' + id);\r\n const pace = {inhaleMs: m.inhaleMs, exhaleMs: m.exhaleMs}, phases = [];\r\n if (id === 'wim-hof') {\r\n  phases.push(\r\n   {key:'settle',label:'Settle comfortably',ms:m.settleMs,dialogue:GENTLE_DIALOGUE[0]},\r\n   {key:'breathe',label:'One round · 30 easy breaths',ms:m.breathMs,pace,dialogue:GENTLE_DIALOGUE[1]},\r\n   {key:'transition',label:'Let the last exhale soften',ms:m.transitionMs,dialogue:'Easy exhale. Stay comfortable.'},\r\n   {key:'optional-hold',label:'Optional pause · skip whenever you like',ms:m.holdMs,dialogue:'Never force it. Breathe normally.'},\r\n   {key:'recovery',label:'Return to easy breathing',ms:m.recoveryInhaleMs,dialogue:GENTLE_DIALOGUE[7]},\r\n   {key:'recovery-hold',label:'Optional recovery pause · skip anytime',ms:m.recoveryHoldMs,dialogue:'Never force it. Breathe normally.'},\r\n   {key:'rest',label:'Let your breathing return to its natural rhythm',ms:GUIDED_ROUND_TIMING.closingBreathingMs,dialogue:GENTLE_DIALOGUE[7]},\r\n  );\r\n } else for (const s of m.stances) phases.push(\r\n  {key: 'hold', label: `Hold: ${s.name}`, ms: m.stanceHoldMs, pace, stanceId: s.id, cue: s.cue},\r\n  {key: 'rest', label: 'Release the stance · breathe normally', ms: m.restMs},\r\n );\r\n return Object.freeze(phases);\r\n}\r\n\r\n// Each script should fit inside the shared clock so no round is cut short (asserted in tests).\r\nexport const scriptMs = script => script.reduce((sum, p) => sum + p.ms, 0);\r\nexport const SESSION_MS = BREATHING_MS;\r\n\r\n// Phase showing at `elapsedMs` of active time. Once the script is used up, it rests until the\r\n// shared 3-minute clock ends -- this never decides when the session ends.\r\nexport function phaseAt(script, elapsedMs) {\r\n let into = Math.max(0, Number(elapsedMs) || 0);\r\n for (const p of script) {\r\n  if (into < p.ms) {\r\n   const cycle = p.pace && p.pace.inhaleMs + p.pace.exhaleMs;\r\n   return {...p, remainingMs: p.ms - into, breath: cycle ? (into % cycle < p.pace.inhaleMs ? 'in' : 'out') : null};\r\n  }\r\n  into -= p.ms;\r\n }\r\n return {key: 'rest', label: 'Breathe normally until the timer ends', ms: 0, remainingMs: 0, breath: null};\r\n}\r\n\r\n// R18 A3: every completed breath counts toward the colour reveal, across the whole script (never per round).\r\nconst cycleMs = p => p.pace.inhaleMs + p.pace.exhaleMs, breathsIn = p => Math.round(p.ms / cycleMs(p));\r\nexport const totalBreaths = script => script.reduce((n, p) => n + (p.pace ? breathsIn(p) : 0), 0);\r\nexport function breathsDone(script, ms) {\r\n let n = 0;\r\n for (const p of script) {\r\n  if (p.pace) n += Math.min(breathsIn(p), Math.floor((Math.max(0, Math.min(ms, p.ms)) + 1e-6) / cycleMs(p)));\r\n  if ((ms -= p.ms) < 0) break;\r\n }\r\n return n;\r\n}\r\n// Colour circle radius in px: strictly under maxR until the final breath, exactly maxR on it.\r\nexport const revealRadius = (done, total, maxR) => total > 0 && done >= total ? maxR : maxR * Math.max(0, done) / Math.max(1, total);\r\n// R20: breath-synced colour pulse. Breath i (0-based, inhale then exhale): the inhale contracts from the last exhale's\r\n// peak to the character (minR), the exhale expands to peak(i), which grows with i; the last exhale peak is exactly maxR.\r\n// phase is 'in' | 'out' (anything else = between breaths), progress 0..1 through that half-breath.\r\nexport function pulseRadius(breathIndex, total, phase, progress, maxR, minR = 0) {\r\n if (total <= 0) return 0;\r\n if (breathIndex >= total) return maxR;\r\n const peak = i => i < 0 ? 0 : minR + (maxR - minR) * (i + 1) / total;\r\n const e = (1 - Math.cos(Math.PI * Math.max(0, Math.min(1, progress)))) / 2; // ease in-out\r\n if (phase === 'out') return minR + (peak(breathIndex) - minR) * e;\r\n if (phase === 'in') return minR + (peak(breathIndex - 1) - minR) * (1 - e);\r\n return breathIndex > 0 ? minR : 0;\r\n}\r\n// Which half-breath is showing at `ms` and how far through it (0..1); null outside paced phases.\r\nexport function breathPhaseProgress(script, ms) {\r\n const p = phaseAt(script, ms);\r\n if (!p.pace || !p.breath) return {breath: null, progress: 0};\r\n const offset = (p.ms - p.remainingMs) % (p.pace.inhaleMs + p.pace.exhaleMs);\r\n return {breath: p.breath, progress: p.breath === 'in' ? offset / p.pace.inhaleMs : (offset - p.pace.inhaleMs) / p.pace.exhaleMs};\r\n}\r\n","breathing.mjs":"import {authTransitions} from './auth-transition.mjs';\r\nimport {recordDailyActivity,localDay} from './performance-progress.mjs';\r\nimport {createAccountSessionActions} from './account-session-actions.mjs';\r\nimport {BreathingSession,BREATHING_MS} from './combat.mjs';\r\nimport {BREATHING_MODES,SEATED_ONLY_NOTICE,NO_MEDICAL_CLAIM,GENTLE_DIALOGUE,GUIDED_ROUND_TIMING,GUIDED_ROUND_MS,buildScript,phaseAt,totalBreaths as countBreaths,breathsDone,revealRadius,pulseRadius,breathPhaseProgress} from './breathing-modes.mjs';\r\n// R18 A5: a speech line fades in, stays ~4 s, fades out (CSS .bubble); re-adding the class restarts it.\r\nexport const bubble=(el,text)=>{el.textContent=text;el.classList.remove('bubble');void el.offsetWidth;el.classList.add('bubble');};\r\n\r\n// Both modes use the existing account-owned breathing ticket and completion path. The guided\r\n// round's timing lives in breathing-modes.mjs so cadence review can tune phases without changing ownership.\r\nconst DEFAULT_STATUS='Complete a session to double today’s workout XP.';\r\nconst IDLE_CAPTION='Breathe in. Breathe out.';\r\n// The big caption follows the phase. Reduced motion keeps it steady through fast in/out breathing.\r\nfunction captionFor(p,reduced){\r\n if(p.key==='settle')return 'Choose a comfortable position. Let your shoulders soften.';\r\n if(p.key==='optional-hold')return 'Breathe whenever you need to. Do not force the hold.';\r\n if(p.key==='recovery')return 'Return to easy breathing.';\r\n if(p.key==='recovery-hold')return 'Let breathing return to normal whenever you like.';\r\n if(p.key==='rest')return 'Breathe normally.';\r\n if(p.breath)return reduced?IDLE_CAPTION:p.breath==='in'?'Breathe in.':'Breathe out.';\r\n return p.key==='hold'?'Breathe out and hold.':IDLE_CAPTION;\r\n}\r\n\r\nexport function mountBreathing({dialog,scene,pause,api,onComplete,onSessionComplete,onEarlyExit,getAccount,transitions=authTransitions()}){\r\n const actions=createAccountSessionActions({api,transitions});\r\n const controls=document.createElement('div');controls.className='breathing-session';\r\n // R20: one Begin button starts the guided round (Begin -> seated warning -> settle countdown -> breathing). Tai chi stays in\r\n // breathing-modes.mjs for anything that imports it but is no longer offered here. data-mode keeps older selectors working.\r\n controls.innerHTML='<div class=\"breath-modes\" data-breath-modes><button type=\"button\" class=\"breath-begin\" data-begin data-mode=\"wim-hof\">Begin</button></div>'\r\n  +'<div class=\"breath-run\" data-breath-run hidden><p class=\"breath-phase\" data-phase-label></p><progress max=\"'+BREATHING_MS+'\" value=\"0\" aria-label=\"Breathing session progress\"></progress>'\r\n  +'<div class=\"breath-actions\"><button type=\"button\" data-retry hidden>Save breathing bonus</button><button type=\"button\" data-skip-hold hidden>Skip · breathe normally</button></div>'\r\n  +'<button type=\"button\" class=\"breath-stance-link\" data-stance-link hidden>Check this stance with the camera coach</button></div>'\r\n  +'<p data-status role=\"status\"></p><p class=\"breath-note\">'+NO_MEDICAL_CLAIM+'</p>';\r\n scene.append(controls);\r\n // R18 A4: the seated warning is a full-scene overlay; nothing ticks until Accept, then it fades out (600 ms) and is hidden.\r\n const seated=document.createElement('div');seated.className='breath-seated-overlay';seated.dataset.seated='';seated.hidden=true;seated.innerHTML='<div role=\"alertdialog\" aria-label=\"Before you begin\"><p class=\"breath-seated-notice\">'+SEATED_ONLY_NOTICE+'</p><button type=\"button\" data-seated-accept>Accept</button></div>';dialog.append(seated);\r\n // R18 polish: after Accept the top panel fades to near-invisible; any tap brings it back for 4 s.\r\n let calmTimer=0;const calm=on=>dialog.classList.toggle('hud-calm',on),rest=()=>{clearTimeout(calmTimer);calmTimer=setTimeout(()=>ticket&&!held&&calm(true),4000);};\r\n let swallow=false;dialog.addEventListener('pointerdown',event=>{if(ticket&&!held){swallow=dialog.classList.contains('hud-calm')&&!event.target.closest?.('[data-breath-exit],[data-meditation-close]');calm(false);rest();}},true);\r\n dialog.addEventListener('click',event=>{if(swallow){swallow=false;event.stopPropagation();event.preventDefault();}},true); // the first tap on a faded panel only reveals it\r\n seated.querySelector('button').onclick=()=>{held=false;if(ticket)calm(true);seated.classList.add('fading');setTimeout(()=>{seated.hidden=true;seated.classList.remove('fading');},600);};\r\n controls.querySelector('.breath-actions').append(pause);\r\n const $=selector=>controls.querySelector(selector);\r\n const modesEl=$('[data-breath-modes]'),runEl=$('[data-breath-run]'),phaseEl=$('[data-phase-label]'),bar=$('progress'),stopCircle=scene.querySelector?.('[data-breathing-stop], [data-breath-exit]'),retry=$('[data-retry]'),skipHold=$('[data-skip-hold]'),stanceLink=$('[data-stance-link]'),statusEl=$('[data-status]'),countEl=scene.querySelector?.('[data-breath-count]'),sessionClock=scene.querySelector?.('[data-session-clock]'),directionEl=scene.querySelector?.('[data-breath-direction]'),cueEl=scene.querySelector?.('[data-breath-cue]'),settleEl=scene.querySelector?.('[data-settle-countdown]');\r\n // Both exercises and the decorative pose selector share a compact, reachable control strip.\r\n const speech=scene.querySelector?.('.meditation-speech'),reducedMotion=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)');\r\n let clock=new BreathingSession(),ticket=null,saving=false,finished=false,awaitingRetry=false,run=0,script=null,caption='',duration=BREATHING_MS,totalBreaths=0,held=false,completionDay=null;\r\n function renderPhase(ms){\r\n  const p=phaseAt(script,ms);runEl.dataset.phase=p.key;skipHold.hidden=!['optional-hold','recovery-hold'].includes(p.key);\r\n  phaseEl.textContent=({settle:'Settle gently',breathe:p.breath==='in'?'Breathe in':'Breathe out',transition:'Easy exhale','optional-hold':'Optional pause',recovery:'Easy inhale','recovery-hold':'Optional recovery',rest:'Breathe normally'})[p.key]||p.label;\r\n  stanceLink.hidden=!p.stanceId;stanceLink.dataset.stance=p.stanceId||'';\r\n  // Only on change, so a character-tap line stays until the next phase and the live region is not re-announced.\r\n  let next=p.dialogue||captionFor(p,reducedMotion?.matches);\r\n  if(p.key==='breathe'&&p.pace){const elapsed=p.ms-p.remainingMs,cycle=p.pace.inhaleMs+p.pace.exhaleMs,index=Math.floor(elapsed/cycle);next=GENTLE_DIALOGUE[1+Math.floor(index/6)%6];}\r\n  if(speech&&next!==caption){caption=next;bubble(speech,next);}\r\n  // R20: the in/out cue is the one rotating line that never fades (plain text swap, no animation); every other line is a fading bubble.\r\n  if(cueEl){const cue=p.breath?(reducedMotion?.matches?IDLE_CAPTION:p.breath==='in'?'Breathe in':'Breathe out'):'';cueEl.hidden=!cue;if(cueEl.textContent!==cue)cueEl.textContent=cue;cueEl.dataset.breath=p.breath||'';}\r\n  // R20: the settle phase is a visible countdown (whole seconds left, down to 0) before the first breath.\r\n  if(settleEl){settleEl.hidden=p.key!=='settle';const left=String(Math.max(0,Math.floor((p.remainingMs||0)/1000)));if(p.key==='settle'&&settleEl.lastElementChild?.textContent!==left)settleEl.lastElementChild.textContent=left;}\r\n  if(countEl){let remaining=p.remainingMs||0,progress=.5,value='\\u2014',direction=p.key==='settle'?'READY':'REST';if(['settle','rest'].includes(p.key)){const cycle=GUIDED_ROUND_TIMING.inhaleMs+GUIDED_ROUND_TIMING.exhaleMs,elapsed=Math.max(0,p.ms-p.remainingMs),offset=elapsed%cycle,inhale=offset<GUIDED_ROUND_TIMING.inhaleMs;progress=inhale?offset/GUIDED_ROUND_TIMING.inhaleMs:1-(offset-GUIDED_ROUND_TIMING.inhaleMs)/GUIDED_ROUND_TIMING.exhaleMs;direction=inhale?'IN':'OUT';}else if(p.pace&&p.breath){const into=p.ms-p.remainingMs,cycle=p.pace.inhaleMs+p.pace.exhaleMs,offset=into%cycle,segment=p.breath==='in'?offset:offset-p.pace.inhaleMs;remaining=p.breath==='in'?p.pace.inhaleMs-offset:cycle-offset;progress=p.breath==='in'?offset/p.pace.inhaleMs:1-segment/p.pace.exhaleMs;value=String(Math.min(BREATHING_MODES['wim-hof'].breathsPerRound,Math.floor(into/cycle)+1));direction=p.breath==='in'?'IN':'OUT';}else if(p.key==='transition'){progress=1-(p.ms-p.remainingMs)/p.ms;direction='OUT';}else if(p.key==='optional-hold'){progress=0;value=String(Math.max(0,Math.ceil(remaining/1000)));direction='HOLD?';}else if(p.key==='recovery'){progress=(p.ms-p.remainingMs)/p.ms;direction='IN';}else if(p.key==='recovery-hold'){progress=1;value=String(Math.max(0,Math.ceil(remaining/1000)));direction='HOLD?';}else if(p.key==='complete'){value='30';direction='DONE';}countEl.textContent=value;countEl.dataset.breath=p.breath||'rest';countEl.parentElement?.style.setProperty('--breath-progress',String(Math.max(0,Math.min(1,progress))));countEl.parentElement?.style.setProperty('--breath-scale',String(.70+Math.max(0,Math.min(1,progress))*.42));if(directionEl)directionEl.textContent=direction;}\r\n }\r\n  // R18 A3: grey veil over the whole stage with a hole centred on the character. R20: the hole pulses with the breath --\r\n // out to a growing peak on each exhale, back to the character on each inhale (pulseRadius), redrawn every tick.\r\n // Reduced motion keeps the R18 steady growth (revealRadius) instead of a fast pulse.\r\n function reveal(ms){\r\n  const stage=scene.querySelector('.meditation-stage'),veil=stage?.querySelector('.meditation-grey'),who=scene.querySelector('.meditation-character');if(!veil||!who||!script)return;\r\n  const s=stage.getBoundingClientRect(),c=who.getBoundingClientRect(),x=c.left+c.width/2-s.left,y=c.top+c.height/2-s.top,maxR=Math.hypot(Math.max(x,s.width-x),Math.max(y,s.height-y)),done=breathsDone(script,ms),{breath,progress}=breathPhaseProgress(script,ms);\r\n  veil.style.setProperty('--cx',x+'px');veil.style.setProperty('--cy',y+'px');veil.style.setProperty('--reveal-r',(reducedMotion?.matches?revealRadius(done,totalBreaths,maxR):pulseRadius(done,totalBreaths,breath,progress,maxR,Math.min(c.width,c.height)/2))+'px');\r\n  dialog.classList.toggle('meditation-colour',totalBreaths>0&&done>=totalBreaths);\r\n }\r\n function reset({keepPose=false}={}){\r\n  run++;clock=new BreathingSession();duration=BREATHING_MS;ticket=null;completionDay=null;saving=false;finished=false;awaitingRetry=false;script=null;\r\n  if(caption&&speech)bubble(speech,IDLE_CAPTION);caption='';totalBreaths=0;held=false;scene.querySelector?.('.meditation-grey')?.style.setProperty('--reveal-r','0px');if(cueEl)cueEl.hidden=true;if(settleEl)settleEl.hidden=true;clearTimeout(calmTimer);dialog.classList.remove('hud-calm');\r\n  bar.max=BREATHING_MS;bar.value=0;sessionClock&&(sessionClock.textContent='3:30');countEl&&(countEl.textContent='—');directionEl&&(directionEl.textContent='READY');pause.disabled=true;pause.hidden=true;pause.textContent='Pause';dialog.classList.remove('breathing-paused');\r\n  modesEl.hidden=false;runEl.hidden=true;seated.hidden=true;retry.hidden=true;skipHold.hidden=true;stanceLink.hidden=true;runEl.dataset.phase='';\r\n  if(stopCircle)stopCircle.hidden=true;phaseEl.textContent='';statusEl.textContent=DEFAULT_STATUS;dialog.classList.remove('meditation-colour');\r\n }\r\n async function finish(){\r\n  if(saving||finished||!ticket||!clock.complete)return;\r\n  saving=true;awaitingRetry=false;retry.hidden=true;pause.disabled=true;statusEl.textContent='Saving your breathing bonus…';const current=run;completionDay??=localDay();\r\n  try{\r\n   const binding=ticket;await actions.completeBreathing(binding,clock.elapsed);\r\n   if(current!==run||!transitions.isCurrent(binding.transitionTicket))return;\r\n   recordDailyActivity('meditation',{id:binding.id,day:completionDay,startedAt:binding.startedAt,completedAt:Date.now()},{account:{user:{id:binding.ownerId},dataEpoch:binding.dataEpoch}});\n   await onComplete?.();\r\n   if(current!==run||!transitions.isCurrent(binding.transitionTicket))return;\r\n   finished=true;statusEl.textContent='Meditation complete · ×2 workout XP today';sessionClock&&(sessionClock.textContent='0:00');countEl&&(countEl.textContent='30');directionEl&&(directionEl.textContent='DONE');phaseEl.textContent='Complete';runEl.dataset.phase='complete';stanceLink.hidden=true;pause.hidden=true;if(stopCircle)stopCircle.hidden=false;onSessionComplete?.();\r\n  }catch(error){if(current===run){statusEl.textContent=error.message;retry.hidden=false;awaitingRetry=true;}}\r\n  finally{if(current===run)saving=false;}\r\n }\r\n async function startSession(id){\r\n  reset({keepPose:true});duration=id==='wim-hof'?GUIDED_ROUND_MS:BREATHING_MS;bar.max=duration;script=buildScript(id);held=!!BREATHING_MODES[id]?.seatedOnly;seated.hidden=!held;seated.classList.remove('fading');totalBreaths=countBreaths(script);reveal(0);if(held)seated.querySelector('button').focus();modesEl.hidden=true;runEl.hidden=false;statusEl.textContent='Starting…';sessionClock&&(sessionClock.textContent=formatTime(duration));renderPhase(0);void import('./modules/vault/vault-store.mjs').then(m=>m.bump('breath-mode',1,{key:id})).catch(()=>{});\r\n  const current=run;\r\n  try{\r\n   const value=await actions.startBreathing(getAccount?.());\r\n   if(current!==run||!transitions.isCurrent(value.transitionTicket))return;\r\n   ticket=value;clock=new BreathingSession(duration);pause.disabled=false;pause.hidden=false;if(stopCircle)stopCircle.hidden=false;statusEl.textContent=formatTime(duration)+' remaining';if(!held)calm(true);\r\n  }catch(error){if(current===run){reset();statusEl.textContent=error.message;}}\r\n }\r\n modesEl.querySelector('[data-begin]').onclick=()=>startSession('wim-hof');\r\n \r\n // Abandon the ticket before the exit animation. This path never calls completion/save.\r\n const stopNow=()=>{const early=!!ticket&&!finished&&!clock.complete,transitionTicket=ticket?.transitionTicket;reset();if(early)void onEarlyExit?.({isCurrent:()=>!transitionTicket||transitions.isCurrent(transitionTicket)});else if(dialog.open)dialog.close();};\r\n if(stopCircle)stopCircle.onclick=stopNow;\r\n skipHold.onclick=()=>{if(!script||!['optional-hold','recovery-hold'].includes(runEl.dataset.phase))return;const index=script.findIndex(p=>p.key===runEl.dataset.phase);script=script.map((p,i)=>i>=index?{key:'rest',label:'Breathe normally until the timer ends',ms:p.ms}:p);renderPhase(clock.elapsed);};\r\n retry.onclick=()=>finish();\r\n // D25: existing core/balance hold detection stays reachable -- leave breathing for the movement\r\n // library's own camera hold for this stance instead of re-implementing pose detection here.\r\n stanceLink.onclick=()=>{const id=stanceLink.dataset.stance;dialog.close();document.getElementById('openLibrary')?.click();document.querySelector(`[data-movement=\"${id}\"]`)?.click();};\r\n const timer=setInterval(()=>{\r\n  const active=!!ticket&&dialog.open&&!scene.hidden&&!document.hidden&&!dialog.classList.contains('breathing-paused')&&!held&&!dialog.classList.contains('snorting')&&!dialog.classList.contains('blacking-out');\r\n  clock.sample(performance.now(),active);bar.value=clock.elapsed;\r\n  if(ticket&&!finished&&!saving&&!clock.complete){const left=Math.ceil((duration-clock.elapsed)/1000);statusEl.textContent=formatTime(duration-clock.elapsed)+' remaining'+(active?'':' · Paused');sessionClock&&(sessionClock.textContent=formatTime(duration-clock.elapsed));renderPhase(clock.elapsed);reveal(clock.elapsed);}\r\n  if(clock.complete&&!finished&&!saving&&!awaitingRetry)void finish();\r\n },50);\r\n const unsubscribe=transitions.subscribe(reset);\r\n dialog.addEventListener('close',reset);window.addEventListener('pagehide',event=>{reset();if(!event.persisted){unsubscribe();clearInterval(timer);}});\r\n reset();\r\n}\r\nfunction formatTime(ms){const seconds=Math.max(0,Math.ceil(ms/1000));return `${Math.floor(seconds/60)}:${String(seconds%60).padStart(2,'0')}`;}\r\n","camera-workout.css":"/* An active camera workout has exactly two visible elements: video and count. */\r\nbody[data-camera-workout=true]{overflow:hidden!important;background:#000!important;padding:0!important}\r\nbody[data-camera-workout=true]>:not(#cameraWorkout):not(#myr5Shell):not(dialog[open]){display:none!important}\r\n#cameraWorkout:not([hidden]){position:fixed;inset:0;width:100vw;height:100dvh;z-index:2147483000;display:block;background:#000;overflow:hidden}\r\nhtml.shell-ready[data-game-active=false] #cameraWorkout:not([hidden]){inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left);width:auto;height:auto;z-index:2147482900}\r\nhtml.shell-ready:is([data-game-active=false],[data-game-framed=true]) #cameraWorkout .camera-workout-housing{display:none}\r\n#cameraWorkout>video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block}\r\n/* Flight keeps the hallway clear, including the frame before its lazy view mounts. */\r\n#cameraWorkout.plank-flight-host>video{inset:auto!important;top:58px!important;right:10px!important;bottom:auto!important;left:auto!important;width:56px!important;height:72px!important;max-width:16vw!important;object-fit:cover!important;z-index:6!important;border:1px solid #ffffff80;border-radius:9px;pointer-events:none}\r\n#cameraWorkout.plank-flight-host .camera-workout-housing,#cameraWorkout.plank-flight-host #coachOverlay,#cameraWorkout.plank-flight-host .camera-workout-counter,#cameraWorkout.plank-flight-host .camera-workout-status,#cameraWorkout.plank-flight-host .camera-workout-adjust{display:none!important}\r\n#cameraWorkout.plank-flight-host:not(:has(.plank-flight))::after{content:'Get into position.';position:absolute;left:50%;top:24%;transform:translateX(-50%);color:#fff;font:700 18px system-ui;pointer-events:none}\r\n#cameraWorkout .camera-workout-counter{position:absolute;top:max(24px,env(safe-area-inset-top));left:50%;transform:translateX(-50%);display:block;width:auto;min-width:88px;min-height:64px;padding:12px 18px;border:0;border-radius:16px;background:#0008;color:#fff;box-shadow:none;touch-action:manipulation}\r\n#cameraWorkout #primary{display:inline-flex;font:750 clamp(56px,14vw,100px)/1 ui-monospace,monospace;letter-spacing:-2px;color:#fff}\r\n#cameraWorkout #primary .flip-tile{background:none;border:0;box-shadow:none}\r\n#cameraWorkout #primary .flip-tile:after{display:none}\r\n#cameraWorkout #primary .flip-half{background:none;color:#fff}\r\n#cameraWorkout .camera-workout-counter:focus-visible{outline:3px solid white;outline-offset:4px}\r\n/* Arcade clocks remain available on the existing Stop control without taking\r\n   the middle of the playfield away from the level and pickup feedback. */\r\n#cameraWorkout:has(.runner-layer) .camera-workout-counter{left:auto;right:10px;top:calc(env(safe-area-inset-top,0px) + 30px);transform:none;min-width:64px;min-height:44px;padding:7px 9px;border:1px solid #f3e0cc66;border-radius:10px;background:linear-gradient(145deg,#353047e6,#141321e6);box-shadow:inset 0 1px 0 #ffffff38,0 3px 0 #080711}\r\n#cameraWorkout:has(.runner-layer) #primary{display:none}\r\n#cameraWorkout:has(.runner-layer) .camera-workout-counter::before{content:'\\25A0';display:inline-block;font-size:18px;color:#f3b6bc}\r\n#cameraWorkout:has(.cover-layer) .cc-info>span:first-child{margin-right:76px;max-width:calc(100vw - 110px)}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-hud{top:calc(env(safe-area-inset-top,0px) + 30px);right:86px}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-level>b{font-size:16px}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-level select{font-size:10px;padding:4px;min-height:32px;max-width:100%}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-level>small{font-size:10px}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-level>small .game-symbol{width:18px;height:18px}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-buffs{top:calc(env(safe-area-inset-top,0px) + 106px)}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-buffs>span:first-child{min-height:48px;padding:7px 10px;border:1px solid #ffe3a7;border-radius:12px;background:linear-gradient(135deg,#59456ce8,#20152fed);box-shadow:inset 0 1px 0 #fff5,0 4px 12px #0007}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-buffs>span:first-child>.game-symbol{width:36px;height:36px}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-score{flex-wrap:wrap;max-width:130px;align-content:start}\r\n#cameraWorkout:has(.runner-layer:not(.cover-layer)) .runner-bonus{display:flex;align-items:center;gap:4px;flex-basis:100%;border-top:1px solid #f5d9b633;padding-top:4px;color:#ffe2a3}\r\n#cameraWorkout #coachOverlay{position:absolute;inset:0;pointer-events:none}\r\n/* Housing frame: edge only, never intercepts input. The counter sits above it; the AR coach overlay above both. */\r\n#cameraWorkout .camera-workout-housing{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden}\r\n#cameraWorkout .camera-workout-counter{z-index:2}\r\n#cameraWorkout #coachOverlay{z-index:3}\r\n/* Pause reason and manual rep correction under the counter; small, legible at 375×812, above the housing. */\r\n#cameraWorkout .camera-workout-status{position:absolute;top:calc(max(24px,env(safe-area-inset-top)) + 96px);left:50%;transform:translateX(-50%);z-index:2;width:max-content;max-width:calc(100vw - 48px);margin:0;padding:6px 12px;border-radius:12px;background:#000a;color:#fff;font:600 15px/1.3 system-ui,sans-serif;text-align:center}\r\n#cameraWorkout .camera-workout-status[hidden],#cameraWorkout .camera-workout-adjust[hidden]{display:none}\r\n#cameraWorkout .camera-workout-adjust{position:absolute;bottom:max(24px,env(safe-area-inset-bottom));left:50%;transform:translateX(-50%);z-index:2;display:flex;gap:16px}\r\n#cameraWorkout .camera-workout-adjust button{min-width:56px;min-height:44px;border:0;border-radius:12px;background:#0008;color:#fff;font:700 18px/1 ui-monospace,monospace;touch-action:manipulation}\r\n#cameraWorkout .camera-workout-adjust button:focus-visible{outline:3px solid white;outline-offset:2px}\r\n\r\n/* Text remains unchanged for existing workout dispatch and assistive names. */\r\n.workout-action-icon{display:none}#cameraWorkout.runner-on .workout-action-icon{display:inline-flex}#cameraWorkout.runner-on .workout-action-icon svg{width:24px;height:24px}#cameraWorkout.runner-on .workout-action-caption{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}#cameraWorkout.runner-on .workout-performance-controls button[type=\"button\"]{min-width:44px;min-height:44px}\r\n","camera-workout.mjs":"export function mountCameraWorkout({video,counter,onStop,onAdjust=()=>{}}){\r\n const stage=document.createElement('section');stage.id='cameraWorkout';stage.hidden=true;stage.setAttribute('aria-label','Camera workout');\r\n const stop=document.createElement('button');stop.type='button';stop.className='camera-workout-counter';stop.setAttribute('aria-label','Stop workout');stop.title='Tap the counter to stop';stage.append(stop);\r\n // One status line and a downward correction for false camera reps, under the counter.\r\n const statusLine=document.createElement('p');statusLine.className='camera-workout-status';statusLine.setAttribute('role','status');statusLine.hidden=true;\r\n const adjust=document.createElement('div');adjust.className='camera-workout-adjust';adjust.hidden=true;\r\n for(const [delta,text,label] of [[-1,'−1','Remove one rep']]){const button=document.createElement('button');button.type='button';button.textContent=text;button.setAttribute('aria-label',label);button.addEventListener('click',()=>onAdjust(delta));adjust.append(button);}\r\n stage.append(statusLine,adjust);\r\n // The AR coach is the one exception to camera-only mode (D24): a pointer-events:none layer above the\r\n // video and counter, so a fast knee/ankle can still register on the video underneath.\r\n const coachOverlay=document.createElement('div');coachOverlay.id='coachOverlay';stage.append(coachOverlay);\r\n document.body.append(stage);\r\n // Dev-only: ?recordPose=1 records landmarks and up/down labels for k-NN samples (scripts/pose-samples.mjs).\r\n if(new URLSearchParams(location.search).get('recordPose')==='1')import('./pose-recorder.mjs').then(({mountPoseRecorder})=>mountPoseRecorder(stage));\r\n let active=false,anchors=[],inert=[],links=[],housing=null,token=0;\r\n // Metal housing + feathered strip AROUND the edge (Ian asked for it); the middle of the video stays clear and the frame ignores pointers.\r\n // Reuses the standalone housing markup and styles, so it needs no #portalChrome.\r\n const showHousing=async()=>{const mine=++token;try{\r\n  const [{frameMarkup,paintHousing},...sheets]=await Promise.all([import('./modules/portal/standalone-housing.mjs'),...['/modules/portal/portal.css','/modules/portal/standalone-housing.css'].map(href=>new Promise(resolve=>{const link=document.createElement('link');link.rel='stylesheet';link.href=href;link.onload=link.onerror=()=>resolve(link);document.head.append(link);}))]);\r\n  if(!active||mine!==token){sheets.forEach(link=>link.remove());return;}\r\n  links=sheets;housing=document.createElement('div');housing.className='camera-workout-housing';housing.setAttribute('aria-hidden','true');housing.innerHTML=frameMarkup();paintHousing(housing,document);stage.classList.add('portal-standalone');stage.insertBefore(housing,coachOverlay);\r\n }catch{}};\r\n const hideHousing=()=>{token++;housing?.remove();housing=null;links.forEach(link=>link.remove());links=[];stage.classList.remove('portal-standalone');};\r\n stop.addEventListener('click',()=>onStop());\r\n stage.addEventListener('keydown',event=>{if(event.key==='Escape'){event.preventDefault();onStop();}});\r\n return {show({status='',reps=false}){statusLine.textContent=status;statusLine.hidden=!status;adjust.hidden=!reps;},\r\n setActive(next){if(next===active)return;active=next;\r\n if(next){\r\n  // The camera stage lives outside this modal; close it so Stop remains tappable.\r\n  const workoutHome=document.getElementById('portalWorkoutHome');if(workoutHome?.open){workoutHome.dataset.workoutBegin='true';workoutHome.close();}\r\n  anchors=[video,counter].map(node=>{const marker=document.createComment('camera-workout-return');node.before(marker);return [node,marker];});\r\n  stage.prepend(video);stop.append(counter);stage.hidden=false;stage.inert=false;document.body.dataset.cameraWorkout='true';\r\n  inert=[...document.body.children].filter(node=>node!==stage&&node.id!=='myr5Shell'&&node.tagName!=='DIALOG').map(node=>[node,node.inert]);for(const [node] of inert)node.inert=true;\r\n  stop.focus({preventScroll:true});void showHousing();\r\n }else{\r\n  hideHousing();\r\n  for(const [node,marker] of anchors){marker.replaceWith(node);}anchors=[];\r\n  for(const [node,value] of inert)node.inert=value;inert=[];stage.hidden=true;delete document.body.dataset.cameraWorkout;\r\n }\r\n }};\r\n}\r\n","camera.mjs":"// Camera choices come from the browser, never guessed from camera numbering.\r\n// AGPL-3.0-or-later, like the accompanying app.\r\nexport const deviceChoice=id=>'device:'+id;\r\nexport function cameraConstraints(choice='environment',fps=30){\r\n  const source=choice.startsWith('device:')?{deviceId:{exact:choice.slice(7)}}:{facingMode:{ideal:choice}};\r\n  return {audio:false,video:{...source,width:{ideal:640},height:{ideal:480},frameRate:{ideal:fps,max:30},resizeMode:'none'}};\r\n}\r\nexport async function listCameras(){return (await navigator.mediaDevices.enumerateDevices()).filter(d=>d.kind==='videoinput'&&d.deviceId).map(d=>({id:d.deviceId,label:d.label}));}\r\nexport async function openCamera(choice='environment',fps=30,devices=navigator.mediaDevices,{signal,timeoutMs=20000}={}){\r\n  let cancelled=false,timer,onAbort;\r\n  const cancelledError=()=>new DOMException('Camera startup cancelled.','AbortError');\r\n  const acquisition=(async()=>{\r\n  let resolved=choice;\r\n  if(!choice.startsWith('device:')){\r\n    // Once permission exposes device labels, select the requested camera exactly.\r\n    // An ideal facing preference alone can otherwise return a different camera.\r\n    try{const pattern=choice==='user'?/(front|user|selfie)/i:/(back|rear|environment)/i;const match=(await devices.enumerateDevices()).find(d=>d.kind==='videoinput'&&d.deviceId&&pattern.test(d.label));if(match)resolved=deviceChoice(match.deviceId);}catch{}\r\n  }\r\n  if(cancelled||signal?.aborted)throw cancelledError();\r\n  const incoming=await devices.getUserMedia(cameraConstraints(resolved,fps));\r\n  if(cancelled||signal?.aborted){incoming.getTracks().forEach(track=>track.stop());throw cancelledError();}\r\n  return incoming;\r\n  })();\r\n  const cancellation=new Promise((_,reject)=>{\r\n    onAbort=()=>{cancelled=true;reject(cancelledError());};\r\n    signal?.addEventListener('abort',onAbort,{once:true});\r\n    if(signal?.aborted)onAbort();\r\n    timer=setTimeout(()=>{cancelled=true;reject(new Error('Camera permission is still pending. Allow camera access in your browser, then tap Begin again.'));},timeoutMs);\r\n  });\r\n  try{return await Promise.race([acquisition,cancellation]);}\r\n  finally{clearTimeout(timer);signal?.removeEventListener('abort',onAbort);}\r\n}\r\nexport function findUltrawide(cameras){return cameras.find(d=>!/(front|user|facetime|selfie)/i.test(d.label)&&/(ultra[\\s-]?wide|ultrawide|0[.,][56]\\s*[x×])/i.test(d.label));}\r\nexport function cameraFacing(track,choice){const settings=track.getSettings();return settings.facingMode||(/(front|user|selfie)/i.test(track.label)||choice==='user'?'user':'environment');}\r\nexport async function widestZoom(track){\r\n  const capabilities=track.getCapabilities?.()||{},range=capabilities.zoom;\r\n  const supported=range&&Number.isFinite(range.min)&&Number.isFinite(range.max)&&range.min>0&&range.max>=range.min;\r\n  if(!supported)return {supported:false,applied:false,zoom:track.getSettings().zoom??null};\r\n  try{\r\n    await track.applyConstraints({advanced:[{zoom:range.min}]});\r\n    const zoom=track.getSettings().zoom;\r\n    return {supported:true,applied:Number.isFinite(zoom)&&Math.abs(zoom-range.min)<.01,min:range.min,max:range.max,zoom:zoom??null};\r\n  }catch(error){return {supported:true,applied:false,min:range.min,max:range.max,zoom:track.getSettings().zoom??null,error:error.message};}\r\n}\r\nexport function cameraReport(track,cameras,zoom){\r\n  const settings=track.getSettings();return {version:'feet-optional-3',label:track.label||'Camera',facing:settings.facingMode||'',width:settings.width||0,height:settings.height||0,zoom,lenses:cameras.map(d=>d.label||'Unnamed camera')};\r\n}\r\n","character-phone-physics.mjs":"/**\r\n * Deterministic phone-orientation physics for the customizer character.\r\n * Coordinates are CSS-pixel offsets from the centered resting pose. `gx` points\r\n * screen-right and `gy` screen-down; upright is (0, 1). `shake` is acceleration\r\n * magnitude in m/s² (shake impulses above 14 m/s²) and angularSpeed is\r\n * radians/second. Poses are renderer hints (`wiggle` for unsupported midair,\r\n * `greet` for the midpoint wave, `walk` for recovery). The adapter owns sensor\r\n * permissions, reduced-motion policy, rendering, and persistence.\r\n */\r\nexport function createCharacterPhysics({\r\n  width,\r\n  height,\r\n  bodyWidth,\r\n  bodyHeight,\r\n  buffer = 96,\r\n  ship = false,\r\n  climb = true,\r\n  safeTiltDegrees = 22.5,\r\n  contain = false,\r\n  tiltExit = false,\r\n} = {}) {\r\n  const safeDimension = (value, fallback = 1) => Number.isFinite(value) ? Math.max(1, value) : fallback;\r\n  const view = { width: safeDimension(width), height: safeDimension(height) };\r\n  const body = { width: safeDimension(bodyWidth), height: safeDimension(bodyHeight) };\r\n  const margin = Math.max(0, Number.isFinite(buffer) ? buffer : 96);\r\n  const restCenter = { x: null, y: null };\r\n  let hasShip = Boolean(ship);\r\n  const safeTilt = Math.max(0, Math.min(60, safeTiltDegrees)) * Math.PI / 180;\r\n  const state = { x: 0, y: 0, angle: 0, phase: 'idle', pose: 'idle', active: false, shipProgress: 0 };\r\n\r\n  let vx = 0;\r\n  let vy = 0;\r\n  let phaseAge = 0;\r\n  let gravityX = 0;\r\n  let gravityY = 1;\r\n  let shakeArmed = true;\r\n  let angularArmed = true;\r\n  let fallHold = 0;\r\n  let tiltHold = 0;\r\n  let fallAge = 0;\r\n  let recoverAfterFall = false;\r\n  let tiltExitFall = false;\r\n  let fallGravity = { x: 0, y: 1 };\r\n  let recoveryStart = { x: 0, y: 0 };\r\n  let climbStart = { x: 0, y: 0 };\r\n  let edge = { x: 0, y: 1 };\r\n  let lastTime = 0;\r\n\r\n  const rotatedHalfExtents = () => {\r\n    const c = Math.abs(Math.cos(state.angle)), s = Math.abs(Math.sin(state.angle));\r\n    return { width: (body.width * c + body.height * s) / 2, height: (body.height * c + body.width * s) / 2 };\r\n  };\r\n  const exitBounds = () => {\r\n    const half = rotatedHalfExtents();\r\n    return { left: -view.width / 2 - margin - half.width, right: view.width / 2 + margin + half.width,\r\n      top: -view.height / 2 - margin - half.height, bottom: view.height / 2 + margin + half.height };\r\n  };\r\n  const centerBounds = () => {\r\n    if (!contain) return exitBounds();\r\n    const c = Math.abs(Math.cos(state.angle)), s = Math.abs(Math.sin(state.angle));\r\n    const halfW = Math.min(view.width / 2, (body.width * c + body.height * s) / 2);\r\n    const halfH = Math.min(view.height / 2, (body.height * c + body.width * s) / 2);\r\n    const ox = restCenter.x ?? view.width / 2, oy = restCenter.y ?? view.height / 2;\r\n    return { left: -ox + halfW, right: view.width - ox - halfW, top: -oy + halfH, bottom: view.height - oy - halfH };\r\n  };\r\n  const setPhase = (phase, pose = phase) => {\r\n    state.phase = phase;\r\n    state.pose = pose;\r\n    phaseAge = 0;\r\n    state.active = phase !== 'idle' && phase !== 'gone';\r\n  };\r\n  const gravityAngle = () => Math.atan2(gravityX, gravityY);\r\n  const snapshot = () => ({ ...state });\r\n  const supported = () => Math.abs(gravityX) < 0.2 && gravityY > 0.8;\r\n  const sideways = () => Math.abs(Math.atan2(gravityX, gravityY)) >= 89 * Math.PI / 180;\r\n  const setFallEdge = () => {\r\n    const length = Math.hypot(gravityX, gravityY) || 1;\r\n    edge = { x: gravityX / length, y: gravityY / length };\r\n    fallGravity = { x: gravityX, y: gravityY };\r\n  };\r\n  const fallExitDistance = () => {\r\n    const half = rotatedHalfExtents();\r\n    const ox = (restCenter.x ?? view.width / 2) - view.width / 2;\r\n    const oy = (restCenter.y ?? view.height / 2) - view.height / 2;\r\n    return Math.abs(edge.x) * (view.width / 2 + half.width + margin)\r\n      + Math.abs(edge.y) * (view.height / 2 + half.height + margin)\r\n      - ox * edge.x - oy * edge.y;\r\n  };\r\n  const startFall = (pose = 'fall', canRecover = false) => {\r\n    setFallEdge();\r\n    fallHold = 0;\r\n    fallAge = 0;\r\n    recoverAfterFall = canRecover;\r\n    tiltExitFall = Boolean(tiltExit && canRecover);\r\n    setPhase('fall', pose);\r\n  };\r\n  const clampPosition = (bounce = true) => {\r\n    const b = centerBounds();\r\n    if (state.x < b.left) { state.x = b.left; if (bounce && vx < 0) vx = -vx * 0.68; }\r\n    if (state.x > b.right) { state.x = b.right; if (bounce && vx > 0) vx = -vx * 0.68; }\r\n    if (state.y < b.top) { state.y = b.top; if (bounce && vy < 0) vy = -vy * 0.68; }\r\n    if (state.y > b.bottom) { state.y = b.bottom; if (bounce && vy > 0) vy = -vy * 0.58; }\r\n  };\r\n  const lerp = (a, b, t) => a + (b - a) * Math.max(0, Math.min(1, t));\r\n  const enterRecovery = () => {\r\n    // Return along the original platform, never diagonally through the air.\r\n    state.y = 0; state.angle = 0;\r\n    recoveryStart = { x: state.x, y: 0 };\r\n    vx = 0;\r\n    vy = 0;\r\n    recoverAfterFall = false;\r\n    tiltExitFall = false;\r\n    state.shipProgress = 0;\r\n    setPhase('recover', 'walk');\r\n  };\r\n  const beginClimb = () => {\r\n    climbStart = { x: state.x, y: state.y };\r\n    vx = 0;\r\n    vy = 0;\r\n    tiltExitFall = false;\r\n    setPhase('climb', 'climb');\r\n  };\r\n\r\n  function sample(input = {}, timeSeconds = 0) {\r\n    const wasSupported = supported();\r\n    const rawX = Number.isFinite(input.gx) ? input.gx : gravityX;\r\n    const rawY = Number.isFinite(input.gy) ? input.gy : gravityY;\r\n    const norm = Math.hypot(rawX, rawY);\r\n    if (norm > 0.05) {\r\n      gravityX = rawX / norm;\r\n      gravityY = rawY / norm;\r\n    }\r\n    const neutralTilt = Math.abs(Math.atan2(gravityX, gravityY)) <= safeTilt + 1e-9;\r\n    if (neutralTilt) { gravityX = 0; gravityY = 1; }\r\n    const angular = Number.isFinite(input.angularSpeed) ? Math.abs(input.angularSpeed) : 0;\r\n    const shake = Number.isFinite(input.shake) ? Math.max(0, input.shake) : 0;\r\n    const now = Number.isFinite(timeSeconds) ? timeSeconds : lastTime;\r\n    lastTime = now;\r\n\r\n    if (shake <= 7) shakeArmed = true;\r\n    if (angular < 1.2) angularArmed = true;\r\n\r\n    // An impulse is edge-triggered, so one sustained sensor reading cannot pin\r\n    // the character against a wall. A renewed pulse can add another impulse.\r\n    let shakeTriggered = false;\r\n    if (shake > 14 && shakeArmed && !(shake < 30 && angular > 2 && !sideways())) {\r\n      shakeTriggered = true;\r\n      shakeArmed = false;\r\n      const direction = ((Math.floor(Math.max(0, now) * 10) % 2) ? 1 : -1);\r\n      vx += direction * Math.min(1500, shake * 20);\r\n      vy -= Math.min(780, shake * 10);\r\n      if (['climb', 'wave', 'climb-out'].includes(state.phase)) startFall('fall');\r\n      else if (state.phase === 'idle' || state.phase === 'slide' || state.phase === 'recover') startFall('fall');\r\n      else if (state.phase === 'fall' || state.phase === 'bounce') {\r\n        recoverAfterFall = false;\r\n        tiltExitFall = false;\r\n        clampPosition(true);\r\n      }\r\n      else if (state.phase === 'air-run' || state.phase === 'look-down') { recoverAfterFall = false; tiltExitFall = false; }\r\n    }\r\n\r\n    if (sideways() && angular > 2 && angularArmed && !(state.phase === 'recover' && supported()) && !['air-run', 'look-down', 'climb', 'wave', 'climb-out', 'ship', 'gone'].includes(state.phase)) {\r\n      angularArmed = false;\r\n      setFallEdge();\r\n      fallHold = 0;\r\n      fallAge = 0;\r\n      recoverAfterFall = !shakeTriggered;\r\n      tiltExitFall = false;\r\n      setPhase('air-run', 'wiggle');\r\n    }\r\n\r\n    // A slow orientation change moves the supported coach across the glass.\r\n    if (state.phase === 'idle' && (!supported() || Math.abs(vx) > 18)) { tiltHold = 0; setPhase('slide', 'slide'); }\r\n    if (['idle', 'slide'].includes(state.phase)) state.angle = Math.max(-.35, Math.min(.35, gravityAngle()));\r\n    if (['fall', 'bounce'].includes(state.phase)) setFallEdge();\r\n    if (!wasSupported && supported() && ['slide', 'fall', 'bounce', 'climb', 'wave', 'climb-out', 'ship', 'gone'].includes(state.phase)) enterRecovery();\r\n    if (!['idle', 'slide', 'recover'].includes(state.phase)) state.angle = gravityAngle();\r\n\r\n    return snapshot();\r\n  }\r\n\r\n  function step(dt) {\r\n    let remaining = Number.isFinite(dt) ? Math.max(0, Math.min(10, dt)) : 0;\r\n    while (remaining > 1e-9) {\r\n      const h = Math.min(remaining, 1 / 60);\r\n      remaining -= h;\r\n      phaseAge += h;\r\n\r\n      if (['air-run', 'look-down'].includes(state.phase)) {\r\n        if (state.phase === 'air-run' && phaseAge >= 0.6 - 1e-6) { setPhase('look-down', 'look-down'); continue; }\r\n        if (state.phase === 'look-down' && phaseAge >= 0.45 - 1e-6) { startFall('fall', recoverAfterFall); continue; }\r\n      }\r\n\r\n      if (state.phase === 'fall' || state.phase === 'bounce') {\r\n        // Keep the falling vector aligned with fresh device samples. The edge\r\n        // remembered at fall start is only used to choose the later climb path.\r\n        fallGravity = { x: gravityX, y: gravityY };\r\n        const gravityLength = Math.hypot(gravityX, gravityY) || 1;\r\n        edge = { x: gravityX / gravityLength, y: gravityY / gravityLength };\r\n        fallAge += h;\r\n        fallHold = sideways() ? fallHold + h : 0;\r\n        if (climb && fallHold >= 2 - 1 / 60) { beginClimb(); continue; }\r\n        if (recoverAfterFall && supported() && fallAge >= 0.25) { enterRecovery(); continue; }\r\n        let acceleration = 620;\r\n        if (tiltExit && tiltExitFall) {\r\n          const remaining = Math.max(1 / 60, 1.15 - fallAge);\r\n          const alongVelocity = vx * edge.x + vy * edge.y;\r\n          const distance = fallExitDistance() + 2 - state.x * edge.x - state.y * edge.y;\r\n          acceleration = Math.max(acceleration, 2 * Math.max(0, distance - alongVelocity * remaining) / (remaining * remaining));\r\n        }\r\n        vx += fallGravity.x * acceleration * h;\r\n        vy += fallGravity.y * acceleration * h;\r\n        state.x += vx * h;\r\n        state.y += vy * h;\r\n        if (supported() && state.y > 0) {\r\n          state.y = 0;\r\n          if (vy > 35) { vy = -vy * 0.28; setPhase('bounce', 'wiggle'); }\r\n          else {\r\n            vy = 0;\r\n            if (Math.abs(vx) < 30) { enterRecovery(); continue; }\r\n          }\r\n        }\r\n        state.angle = gravityAngle();\r\n        const beforeX = state.x;\r\n        const beforeY = state.y;\r\n        if (!(tiltExit && tiltExitFall)) clampPosition(true);\r\n        if (state.x !== beforeX || state.y !== beforeY) {\r\n          if (state.phase !== 'bounce') setPhase('bounce', 'wiggle');\r\n        } else if (state.phase === 'bounce' && phaseAge > 0.35) setPhase('fall', 'fall');\r\n        const drag = tiltExit && tiltExitFall ? 1 : Math.pow(0.985, h * 60);\r\n        vx *= drag;\r\n        vy *= drag;\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'climb') {\r\n        state.angle = gravityAngle();\r\n        const t = phaseAge / 3;\r\n        state.x = lerp(climbStart.x, 0, t);\r\n        state.y = lerp(climbStart.y, 0, t);\r\n        if (t >= 1 - 1e-6) { climbStart = { x: state.x, y: state.y }; setPhase('wave', 'wave'); }\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'wave') {\r\n        state.angle = gravityAngle();\r\n        state.pose = 'greet';\r\n        if (phaseAge >= 2 - 1e-6) { climbStart = { x: state.x, y: state.y }; setPhase('climb-out', 'climb'); }\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'climb-out') {\r\n        state.angle = gravityAngle();\r\n        const t = phaseAge / 3;\r\n        const b = exitBounds();\r\n        const targetX = edge.x ? (edge.x > 0 ? b.left : b.right) : 0;\r\n        const targetY = edge.y ? (edge.y > 0 ? b.top : b.bottom) : b.top;\r\n        state.x = lerp(climbStart.x, targetX, t);\r\n        state.y = lerp(climbStart.y, targetY, t);\r\n        if (t >= 1 - 1e-6) {\r\n          if (hasShip) setPhase('ship', 'ship');\r\n          else setPhase('gone', 'gone');\r\n        }\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'ship') {\r\n        state.shipProgress = Math.min(1, phaseAge / 2);\r\n        if (state.shipProgress >= 1) setPhase('gone', 'gone');\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'recover') {\r\n        const t = phaseAge / 3;\r\n        state.x = lerp(recoveryStart.x, 0, t);\r\n        state.y = lerp(recoveryStart.y, 0, t);\r\n        state.angle = lerp(state.angle, 0, Math.min(1, h * 3));\r\n        if (t >= 1) {\r\n          state.x = 0; state.y = 0; state.angle = 0; vx = 0; vy = 0;\r\n          setPhase('idle', 'idle');\r\n        }\r\n        continue;\r\n      }\r\n\r\n      if (state.phase === 'slide') {\r\n        tiltHold = sideways() ? tiltHold + h : 0;\r\n        if (tiltHold >= 0.8) { startFall('fall', tiltExit); continue; }\r\n        vx += gravityX * 480 * h;\r\n        vx *= Math.pow(0.91, h * 60);\r\n        state.x += vx * h;\r\n        const slideLimit = view.width * .15;\r\n        state.x = Math.max(-slideLimit, Math.min(slideLimit, state.x));\r\n        state.y = lerp(state.y, 0, Math.min(1, h * 2));\r\n        clampPosition(true);\r\n        if (supported() && Math.abs(vx) < 8 && Math.abs(state.x) < 4) {\r\n          state.x = 0; state.y = 0; vx = 0;\r\n          setPhase('idle', 'idle');\r\n        }\r\n      }\r\n    }\r\n    state.active = state.phase !== 'idle' && state.phase !== 'gone';\r\n    return snapshot();\r\n  }\r\n\r\n  function resize(nextWidth, nextHeight, nextBodyWidth, nextBodyHeight) {\r\n    view.width = safeDimension(nextWidth, view.width);\r\n    view.height = safeDimension(nextHeight, view.height);\r\n    body.width = safeDimension(nextBodyWidth, body.width);\r\n    body.height = safeDimension(nextBodyHeight, body.height);\r\n    // A standing character belongs at its calibrated rest anchor. Early hidden\r\n    // layout measurements must not push it below the eventual platform.\r\n    if (state.phase === 'idle') { state.x = 0; state.y = 0; }\r\n    else if (!['recover', 'climb-out', 'ship', 'gone'].includes(state.phase)) clampPosition(false);\r\n    return snapshot();\r\n  }\r\n\r\n  function setRestCenter(x, y) {\r\n    restCenter.x = Number.isFinite(x) ? x : null;\r\n    restCenter.y = Number.isFinite(y) ? y : null;\r\n  }\r\n\r\n  function reset() {\r\n    state.x = 0; state.y = 0; state.angle = 0; state.shipProgress = 0;\r\n    setPhase('idle', 'idle');\r\n    vx = 0; vy = 0; phaseAge = 0; fallHold = 0; tiltHold = 0; fallAge = 0; recoverAfterFall = false; tiltExitFall = false;\r\n    gravityX = 0; gravityY = 1; lastTime = 0;\r\n    shakeArmed = true; angularArmed = true;\r\n    return snapshot();\r\n  }\r\n\r\n  // Adapters enable boarding only after the selected, authorized model has loaded.\r\n  const setShipAvailable = available => { hasShip = Boolean(available); };\r\n  return { sample, step, resize, reset, setShipAvailable, setRestCenter, state };\r\n}\r\n","character-phone-sensor.mjs":"// Full gravity is independent of the decorative, calibrated room parallax.\r\nexport function screenGravity(x,y,angle=0){const a=angle*Math.PI/180,c=Math.cos(a),s=Math.sin(a);return {gx:x*c+y*s,gy:-x*s+y*c};}\r\n// Look-around tilt for every room, the ship, the pod and the customizer. Read from the gravity vector, not raw\r\n// beta/gamma deltas: held upright (beta near 90, how iPhones are held) gamma is gimbal-locked, so any small twist\r\n// swings it tens of degrees, and it wraps at +-90 (or flips by 180 with beta past vertical); gravity is smooth through both.\r\n// The angle the phone is held at is zero and that zero follows the hold (time-based, ~95% in recentreS), so the\r\n// view settles wherever it is held. Real phones need tuning: maxDeg = tilt for full look, deadDeg = ignored wobble,\r\n// gain = output at full look (1 = each consumer's full px), recentreS = seconds to re-centre on a new hold.\r\nexport const TILT={gain:1,maxDeg:18,deadDeg:1.5,recentreS:3};\r\nconst wrap180=a=>((a+180)%360+360)%360-180;\r\nexport function createTiltLook(view=globalThis,knobs=TILT){\r\n let base=null,at=0;\r\n const out=d=>{const m=Math.max(0,Math.abs(d)-knobs.deadDeg);return Math.sign(d)*knobs.gain*Math.min(1,m/(knobs.maxDeg-knobs.deadDeg));};\r\n return {\r\n  reset(){base=null;},\r\n  /** {x,y} in -gain..gain (x right, y top edge back), or null for an empty reading. now: ms, defaults to the event's. */\r\n  read(event,now=event?.timeStamp??0){\r\n   if(!Number.isFinite(event?.beta)||!Number.isFinite(event?.gamma))return null;\r\n   const b=event.beta*Math.PI/180,g=event.gamma*Math.PI/180,deg=180/Math.PI;\r\n   const {gx,gy}=screenGravity(Math.cos(b)*Math.sin(g),Math.sin(b),view?.screen?.orientation?.angle??view?.orientation??0),gz=Math.cos(b)*Math.cos(g);\r\n   const roll=Math.atan2(gx,Math.hypot(gy,gz))*deg,pitch=Math.atan2(gy,gz)*deg;\r\n   if(!base){base={roll,pitch};at=now;}\r\n   // dt capped so a pause in events (hidden page, test gaps) is not counted as a long hold.\r\n   const k=1-Math.exp(-Math.min(.1,Math.max(0,(now-at)/1000))*3/knobs.recentreS);at=now;\r\n   base.roll+=(roll-base.roll)*k;base.pitch+=wrap180(pitch-base.pitch)*k;\r\n   return {x:out(roll-base.roll),y:out(wrap180(pitch-base.pitch))};\r\n  }\r\n };\r\n}\r\nexport function createPhoneMotionSource(view=globalThis.window,doc=view?.document){\r\n const listeners=new Set();let running=false,motionAt=-Infinity,lastAngle=null,lastAt=0,lastGravity=null,permission=null;\r\n const now=()=> (view.performance?.now?.()??Date.now())/1000;\r\n const emit=(gravity,shake=0,rotationSpeed=0)=>{\r\n  if(doc?.hidden||!Number.isFinite(gravity.gx)||!Number.isFinite(gravity.gy))return;\r\n  const timeSeconds=now(),length=Math.hypot(gravity.gx,gravity.gy);\r\n  // Pyramid Jump needs the current weak tilt direction even when the stable\r\n  // character heading intentionally holds its last value near flat.\r\n  const steerX=length>=.12/9.81?gravity.gx/length:0;\r\n  // A flat phone has no screen-plane gravity. Hold its last heading, but still\r\n  // deliver gyro and linear-acceleration samples so a shake can trigger motion.\r\n  if(length>=.3)lastGravity={gx:gravity.gx/length,gy:gravity.gy/length};\r\n  const {gx,gy}=lastGravity??{gx:0,gy:1},angle=Math.atan2(gx,gy);\r\n  let angularSpeed=rotationSpeed;if(lastAngle!=null&&timeSeconds>lastAt){const delta=Math.atan2(Math.sin(angle-lastAngle),Math.cos(angle-lastAngle));angularSpeed=Math.max(angularSpeed,Math.abs(delta)/(timeSeconds-lastAt));}\r\n  lastAngle=angle;lastAt=timeSeconds;\r\n  for(const callback of listeners)callback({gx,gy,steerX,shake,angularSpeed,timeSeconds});\r\n };\r\n const screenAngle=()=>view.screen?.orientation?.angle??view.orientation??0;\r\n const motion=event=>{\r\n  const a=event.accelerationIncludingGravity;if(!a||![a.x,a.y].every(Number.isFinite))return;\r\n  const linear=event.acceleration,hasLinear=linear&&[linear.x,linear.y,linear.z].every(Number.isFinite);let shake=hasLinear?Math.hypot(linear.x,linear.y,linear.z):0;\r\n  if(!linear||linear.x==null)shake=Math.abs(Math.hypot(a.x,a.y,a.z??0)-9.81);\r\n  const rate=event.rotationRate;\r\n  // The gyroscope's degrees/second survive delayed delivery when WebGL is busy.\r\n  const rotationSpeed=rate?Math.hypot(...['alpha','beta','gamma'].map(axis=>Number.isFinite(rate[axis])?rate[axis]:0))*Math.PI/180:0;\r\n  motionAt=now();emit(screenGravity(-(a.x-(hasLinear?linear.x:0))/9.81,(a.y-(hasLinear?linear.y:0))/9.81,screenAngle()),Math.min(80,shake),rotationSpeed);\r\n };\r\n const orientation=event=>{\r\n  if(now()-motionAt<.5||!Number.isFinite(event.beta)||!Number.isFinite(event.gamma))return;\r\n  const b=event.beta*Math.PI/180,g=event.gamma*Math.PI/180;\r\n  emit(screenGravity(Math.cos(b)*Math.sin(g),Math.sin(b),screenAngle()));\r\n };\r\n const start=()=>{if(running||!listeners.size)return;running=true;lastAngle=null;lastAt=0;lastGravity=null;view.addEventListener('devicemotion',motion,{passive:true});view.addEventListener('deviceorientation',orientation,{passive:true});};\r\n const stop=()=>{if(!running)return;running=false;view.removeEventListener('devicemotion',motion);view.removeEventListener('deviceorientation',orientation);lastAngle=null;lastGravity=null;};\r\n const request=()=>{\r\n  if(permission)return permission;\r\n  // Both requests must begin in this same tap on iOS, before either promise is awaited.\r\n  const requests=[view.DeviceOrientationEvent,view.DeviceMotionEvent].filter(api=>typeof api?.requestPermission==='function').map(api=>{try{return Promise.resolve(api.requestPermission());}catch{return Promise.resolve('denied');}});\r\n  permission=Promise.all(requests).then(results=>{const granted=results.some(result=>result==='granted')||!requests.length;try{view.localStorage.setItem('myr5.tiltPermission',granted?'granted':'denied');}catch{}if(granted)start();return granted;}).finally(()=>{permission=null;});return permission;\r\n };\r\n const click=event=>{if(event.target?.closest?.('.pod-tilt-request,.editor-tilt-request'))void request();};\r\n const visibility=()=>{lastAngle=null;if(doc?.hidden)stop();else if(allowed())start();};\r\n const allowed=()=>{if(!view.DeviceMotionEvent?.requestPermission&&!view.DeviceOrientationEvent?.requestPermission)return true;try{return view.localStorage.getItem('myr5.tiltPermission')==='granted';}catch{return false;}};\r\n const attach=()=>{doc?.addEventListener('click',click,true);doc?.addEventListener('visibilitychange',visibility);view.addEventListener('pageshow',visibility);view.addEventListener('pagehide',stop);if(allowed())start();};\r\n const detach=()=>{stop();doc?.removeEventListener('click',click,true);doc?.removeEventListener('visibilitychange',visibility);view.removeEventListener('pageshow',visibility);view.removeEventListener('pagehide',stop);};\r\n return {subscribe(callback){listeners.add(callback);if(listeners.size===1)attach();return()=>{listeners.delete(callback);if(!listeners.size)detach();};},request};\r\n}\r\n// The customizer bundle and its separately loaded housing must share one permission request.\r\nconst SOURCE=Symbol.for('myr5.characterPhoneMotion');\r\nconst sharedSource=()=>window[SOURCE]??=(createPhoneMotionSource());\r\nexport function subscribePhoneMotion(callback){return sharedSource().subscribe(callback);}\r\nexport function requestPhoneMotion(){return sharedSource().request();}\r\n","chosen-styles.mjs":"// The user's two chosen workout paths. They live in the owner's performance progress\r\n// (owner-scoped storage, never a global key), so another account on the same device never sees them\r\n// and the server can persist and derive them from the same record.\r\nimport {CHOOSABLE_TRACKS,COACH_REQUIREMENTS,normalizePaths} from './performance-catalog.mjs';\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {readPerformanceProgress,choosePerformancePaths,unlockStatus} from './performance-progress.mjs';\r\nexport {CHOOSABLE_TRACKS};\r\nexport const GROUP_PATH=Object.freeze({chest:'chest',legs:'quads',hips:'glutes',shoulders:'arms-shoulders',core:'yoga',balance:'yoga',yoga:'yoga',stances:'martial-arts',boxing:'martial-arts',cardio:'cardio'});\r\nconst pathTrack=path=>path==='arms-shoulders'?'arms':path;\r\n/** Legacy global key. Nothing reads or writes it any more: it leaked paths between accounts. */\r\nexport const SELECTED_TRACKS_KEY='myr5-selected-tracks-v1';\r\n\r\n/** Chosen path ids (canonical, arms-shoulders for arms) for options.owner/storage/account; [] until chosen. */\r\nexport const readSelectedTracks=(options={})=>readPerformanceProgress(options).paths;\r\n/** A selected path is open; an already owned coach also keeps every one of its mapped paths open. */\r\nexport function workoutPathAccess(mode,options={}){\r\n const group=EXERCISES[mode]?.group,path=GROUP_PATH[group];\r\n if(!path)return {allowed:false,path:null,reason:'Choose a movement in an available workout path.'};\r\n const progress=readPerformanceProgress(options);\r\n if(unlockStatus(progress).done||progress.paths.includes(path))return {allowed:true,path,reason:null};\r\n const owned=new Set(progress.coaches);\r\n if(COACH_REQUIREMENTS.some(coach=>owned.has(coach.id)&&coach.tracks.includes(pathTrack(path))))return {allowed:true,path,reason:null};\r\n return {allowed:false,path,reason:`${path.replaceAll('-',' ')} is locked. Choose it as one of your two paths or earn its coach.`};\r\n}\r\nexport function workoutGroupAccess(group,options={}){\r\n const first=Object.values(EXERCISES).find(exercise=>exercise.group===group);\r\n return workoutPathAccess(first?.id,options);\r\n}\r\n/** One-time choice of exactly 2 paths. Grants each path's introductory coach and persists both.\r\n * Repeating the same pair is a no-op; a different pair throws unless options.reset is true. */\r\nexport function chooseWorkoutPaths(trackIds,options={}){\r\n const picked=normalizePaths(trackIds);\r\n if(picked.length!==2)throw RangeError('Exactly 2 workout paths must be selected.');\r\n return choosePerformancePaths(picked,options).paths;\r\n}\r\nexport const setSelectedTracks=chooseWorkoutPaths;\r\n","circuit-ui.mjs":"import {workoutSymbol} from './workout-symbols.mjs';\r\n// Daily-circuit display (VISUAL-CHANGES-HANDOFF.md §9): a visible meter, outside camera-only\r\n// mode, that advances after every step. Mirrors workout-route-ui.mjs's structure/anchors so it\r\n// sits alongside the existing 10-family ladder without touching it.\r\nimport {CIRCUIT_STEPS} from './circuit.mjs';\r\nconst STEP_SYMBOL={pushups:workoutSymbol('chest'),squats:workoutSymbol('legs'),situps:workoutSymbol('core'),meditation:workoutSymbol('meditation'),food:'\\u25c9'};\r\nconst utcDay=now=>Math.floor(now/86400000);\r\nconst LABEL={pushups:'Push-ups',squats:'Squats',situps:'Sit-ups',meditation:'Meditation',food:'Food photo'};\r\n// Exact `default` lines from plan/muse/voice-lines.csv (one variant per step -- only one voice\r\n// style exists today). Food gets no line (D9/D15: \"Food photo gets no voice line\").\r\nconst NEXT_LINE={pushups:'Next up: pushups. Chest day energy.',squats:'Next: squats. Legs, assemble.',situps:'Situps next. Crunch time, literally.',meditation:'Next: meditation. Sit. Breathe. Done.'};\r\nconst CIRCUIT_DONE_LINE='Circuit done. Rest, champion.';\r\n\r\nexport function mountCircuitUI({voice,onNext,busy,pending,cached}){\r\n const card=document.createElement('section');card.className='daily-circuit';card.setAttribute('aria-label','Your daily circuit');\r\n card.innerHTML='<small>DAILY CIRCUIT</small><meter min=\"0\" max=\"5\" value=\"0\" aria-label=\"Circuit steps completed today\"></meter><p data-circuit-status role=\"status\">Connecting your daily circuit…</p><ol data-circuit-steps></ol><button type=\"button\" data-circuit-next hidden>Start next step →</button>';\r\n // W2-2M #117: the daily circuit is the pod's Goals drop-down (pose.html #podGoals), with the route card under it.\r\n const goals=document.getElementById('podGoals');goals.querySelector('.pod-goals-menu').prepend(card);\r\n // A drop-down: Escape, a tap outside it, or picking one of its actions folds it back up.\r\n goals.addEventListener('keydown',event=>{if(event.key==='Escape'&&goals.open){goals.open=false;goals.querySelector('summary').focus();}});\r\n document.addEventListener('pointerdown',event=>{if(goals.open&&!goals.contains(event.target))goals.open=false;});\r\n goals.addEventListener('click',event=>{if(event.target.closest('.pod-goals-menu button'))goals.open=false;});\r\n const rest=document.createElement('section');rest.className='daily-circuit rest-circuit';rest.hidden=true;rest.setAttribute('aria-label','Daily circuit progress');\r\n rest.innerHTML='<meter min=\"0\" max=\"5\" value=\"0\" aria-label=\"Circuit steps completed today\"></meter><p data-circuit-status role=\"status\"></p>';\r\n document.querySelector('.rest-receipt').after(rest);\r\n let announced=null; // {day, doneCount} -- baseline so reopening the app never re-speaks history.\r\n function data(){\r\n  const today=utcDay(Date.now());\r\n  const fresh=window.coachProgress?.circuit;if(fresh&&fresh.day===today)return fresh;\r\n  // No fresh server payload yet (e.g. app just reopened) -- fall back to the last-synced\r\n  // snapshot cached in localStorage so a same-day circuit-in-progress survives close/reopen.\r\n  const stale=cached?.();return stale&&stale.day===today?stale:null;\r\n }\r\n function nextStep(today){return CIRCUIT_STEPS.find(step=>!today.stepDone[step.key])||null;}\r\n // The meter tracks the fixed 5-step CIRCUIT checklist (this card's own job). today.stepsToday\r\n // is a different D15 pass-track count (up to 3 styles + food + meditation, for battle-pass\r\n // leveling) and can be lower than the checklist count -- e.g. the sit-ups stand-in exercise\r\n // completes a circuit step but is not a \"chosen style\", so it earns no pass-track step.\r\n function doneCount(today){return CIRCUIT_STEPS.filter(step=>today.stepDone[step.key]).length;}\r\n function render(){\r\n  const p=data(),today=p?.today;\r\n  for(const meter of [card.querySelector('meter'),rest.querySelector('meter')])meter.value=today?doneCount(today):0;\r\n  const status=today?`${doneCount(today)} of ${CIRCUIT_STEPS.length} circuit steps today`+(nextStep(today)?` · Next: ${STEP_SYMBOL[nextStep(today).key]}`:' · Circuit complete today'):'Connecting your daily circuit…';\r\n  const spoken=today?`${doneCount(today)} of ${CIRCUIT_STEPS.length} circuit steps today`+(nextStep(today)?` · Next: ${LABEL[nextStep(today).key]}`:' · Circuit complete today'):status;\r\n  for(const node of [card.querySelector('[data-circuit-status]'),rest.querySelector('[data-circuit-status]')]){node.textContent=status;node.setAttribute('aria-label',spoken);}\r\n  goals.querySelector('[data-goals-status]').textContent=today?`${doneCount(today)}/${CIRCUIT_STEPS.length}`:'';\r\n  card.querySelector('[data-circuit-steps]').replaceChildren(...CIRCUIT_STEPS.map(step=>{const li=document.createElement('li');li.setAttribute('aria-label',LABEL[step.key]);li.title=LABEL[step.key];li.textContent=`${today?.stepDone?.[step.key]?'✓ ':''}${STEP_SYMBOL[step.key]}`;if(today&&!today.stepDone[step.key]&&step===nextStep(today))li.setAttribute('aria-current','step');return li;}));\r\n  const button=card.querySelector('[data-circuit-next]'),next=today?nextStep(today):null;\r\n  button.hidden=!next;\r\n  if(next){button.setAttribute('aria-label','Start '+LABEL[next.key]);button.title=LABEL[next.key];button.textContent='Start '+STEP_SYMBOL[next.key]+' →';button.disabled=busy()||pending();}\r\n }\r\n card.querySelector('[data-circuit-next]').onclick=()=>{\r\n  const p=data(),today=p?.today,next=today?nextStep(today):null;if(!next||busy()||pending())return;\r\n  if(next.key==='meditation')document.querySelector('.meditation-entry')?.click();\r\n  else if(next.key==='food')document.querySelector('[data-panel=\"meals\"]')?.click();\r\n  else onNext(next);\r\n };\r\n function speakTransition(){\r\n  const p=data();if(!p)return;\r\n  const count=doneCount(p.today);\r\n  if(!announced||announced.day!==p.day){announced={day:p.day,doneCount:count};return;} // new day/session: don't replay history\r\n  if(count<=announced.doneCount)return;\r\n  announced.doneCount=count;\r\n  const next=nextStep(p.today);\r\n  if(next&&next.voice)voice.say(NEXT_LINE[next.key],{key:'circuit',interrupt:true});\r\n  else if(!next)voice.say(CIRCUIT_DONE_LINE,{key:'circuit',interrupt:true});\r\n }\r\n window.addEventListener('myr5:account-progress',()=>{speakTransition();render();});\r\n window.addEventListener('myr5:account-cleared',render);\r\n window.addEventListener('myr5:movement-configured',render);\r\n render();\r\n return {render};\r\n}\r\n","circuit.mjs":"// Daily circuit (VISUAL-CHANGES-HANDOFF.md §9): 10 push-ups -> 10 squats -> 10 sit-ups ->\r\n// meditation -> food photo. This module is pure logic only (no DOM, no DB) so it can be\r\n// shared by the server (server/worker.mjs, computing from real rows) and the client\r\n// (circuit-ui.mjs, rendering window.coachProgress.circuit) and unit tested directly.\r\nimport {exerciseFamily} from './workout-route.mjs';\r\n\r\n// ponytail: no sit-up/crunch pose detector exists anywhere in exercise-library.mjs or\r\n// movement-engine.mjs (only isometric core holds: plank/side-plank). Building a real one is\r\n// a movement-detection project, out of scope for \"circuit meter + voice\". Standing in with\r\n// the existing \"High plank\" core hold until a real sit-up detector ships -- see\r\n// plan/reports/circuit.md for the flagged gap. Manual mode (the tested path) does not care\r\n// which exercise id is used; only camera mode would show the \"wrong\" movement name.\r\nexport const CIRCUIT_STEPS=Object.freeze([\r\n {key:'pushups',mode:'pushup',goal:10,voice:true},\r\n {key:'squats',mode:'squat',goal:10,voice:true},\r\n {key:'situps',mode:'high-plank',goal:15,voice:true},\r\n {key:'meditation',mode:null,goal:null,voice:true},\r\n {key:'food',mode:null,goal:null,voice:false},\r\n]);\r\n\r\n// D25 collapses the 10 exercise-library focus groups into the 8 dial sections; meditation\r\n// (core+balance) is a constant track (D9), never a \"chosen\" style, so it is left out here --\r\n// its step comes only from a completed breathing round, not from completing a core/balance set.\r\nexport const STYLE_OF_GROUP=Object.freeze({chest:'chest',legs:'legs',hips:'hips',shoulders:'shoulders',yoga:'yoga',stances:'martial-arts',boxing:'martial-arts',cardio:'cardio'});\r\nexport const STYLES=Object.freeze(['chest','legs','hips','shoulders','yoga','martial-arts','cardio']);\r\nexport const STEP_TRACKS=Object.freeze([...STYLES,'meditation','food']);\r\nexport const MAX_STYLE_STEPS_PER_DAY=3;\r\nexport const MAX_STEPS_PER_DAY=5; // 3 styles + food + meditation. 5/day * 7 days = 35/week (D15) falls out for free.\r\nexport const STEPS_PER_LEVEL=5,LEVELS_PER_TRACK=5;\r\nexport const trackLevel=steps=>Math.min(LEVELS_PER_TRACK,1+Math.floor(Math.max(0,Number(steps)||0)/STEPS_PER_LEVEL));\r\n\r\n// rows: [{mode,day,sets}] -- exactly server/worker.mjs's existing workoutProgress() aggregate\r\n// (mode+day grouped COUNT over `workouts`). breathingDays/mealDays: iterables of UTC day\r\n// numbers (Math.floor(ms/86400000)) with >=1 completed breathing session / logged meal.\r\n// today: the current UTC day number.\r\nexport function circuitProgress(rows,breathingDays,mealDays,today){\r\n const byDay=new Map();\r\n for(const row of rows||[]){\r\n  const style=STYLE_OF_GROUP[exerciseFamily(row.mode)];\r\n  if(!style||!(Number(row.sets)>0)||!Number.isSafeInteger(row.day))continue;\r\n  if(!byDay.has(row.day))byDay.set(row.day,new Set());\r\n  byDay.get(row.day).add(style);\r\n }\r\n const breathing=new Set(Array.from(breathingDays||[],Number)),meals=new Set(Array.from(mealDays||[],Number));\r\n const tracks=Object.fromEntries(STEP_TRACKS.map(t=>[t,0]));\r\n // ponytail: alphabetical tie-break when >3 styles are touched the same day -- deterministic,\r\n // not \"first completed\"; upgrade to completed_at ordering if that distinction ever matters.\r\n for(const [,styles] of byDay)for(const style of [...styles].sort().slice(0,MAX_STYLE_STEPS_PER_DAY))tracks[style]++;\r\n for(const day of breathing)tracks.meditation++;\r\n for(const day of meals)tracks.food++;\r\n const modesToday=new Set();for(const row of rows||[])if(row.day===today&&Number(row.sets)>0)modesToday.add(row.mode);\r\n const stepDone={};\r\n for(const step of CIRCUIT_STEPS){\r\n  if(step.key==='meditation')stepDone[step.key]=breathing.has(today);\r\n  else if(step.key==='food')stepDone[step.key]=meals.has(today);\r\n  else stepDone[step.key]=modesToday.has(step.mode);\r\n }\r\n const stylesStepped=[...(byDay.get(today)||[])].sort().slice(0,MAX_STYLE_STEPS_PER_DAY);\r\n const stepsToday=stylesStepped.length+(stepDone.meditation?1:0)+(stepDone.food?1:0);\r\n const nextStep=CIRCUIT_STEPS.find(step=>!stepDone[step.key])||null;\r\n return {\r\n  day:today,\r\n  tracks:Object.fromEntries(STEP_TRACKS.map(t=>[t,{steps:tracks[t],level:trackLevel(tracks[t])}])),\r\n  today:{stepDone,stylesStepped,stepsToday,maxStepsToday:MAX_STEPS_PER_DAY,nextStep:nextStep?.key??null},\r\n };\r\n}\r\n","coach-hit.mjs":"// coach-hit.mjs — where the workout coach walks. Pure: pose landmarks in, screen placement out.\r\n// Screen coords are fractions (x right, y down). The floor is a pinhole model with the horizon at the\r\n// user's hips: a coach z times the user's distance away stands higher on screen and 1/z as tall.\r\n// World spot = {X, z}: X is the screen x it would have level with the user, z the distance ratio.\r\n// ponytail: no segmentation occlusion. Spots behind the user (z > 1) always sit clear of them and the\r\n// coach only crosses over in front, so it never needs cutting out. Add pose segmentation masks if it must.\r\n\r\nexport const COACH = {\r\n  hitSpeed: 1.2,\r\n  hitLimbs: [13, 14, 15, 16, 25, 26, 27, 28, 29, 30, 31, 32],\r\n  launchSpeedT: 4,\r\n  sparseSpeedT: 2.2,\r\n  minReachT: .35,\r\n  hitPadT: .2,\r\n  maxLimbGapMs: 750,\r\n  grabGraceMs: 1000,\r\n  throwSpeedT: 1.5,\r\n  throwWindowMs: 500,\r\n  spinMs: 700,\r\n  awayMs: 2000,\r\n  heightPerTorso: 1.5,\r\n  ease: 6,\r\n  boxWidth: 0.6,\r\n  stride: 1.1, // walking speed, coach heights per second\r\n  roomDepth: 3, // the user's distance from the camera in coach heights (how slow walking in depth looks)\r\n  depths: [0.85, 1, 1.2, 1.45], // spots it wanders between (1 = level with the user, more = behind)\r\n  gap: 0.03, // screen gap kept between the user's shoulders and the coach\r\n  pauseMs: [1500, 4000],\r\n  crossRoom: 0.15, // the other side must be this much roomier before it crosses over\r\n  horizon: 0, // camera height: 0 = the user's hips, 1 = their knees, negative = above the hips\r\n  turnRate: 8,\r\n  grabMs: 1000,\r\n  holdMs: 8000,\r\n  dropMs: 400,\r\n  grabLimbs: [15, 16], // wrists are more stable than fingertips\r\n  chargeAfterMs: 8000, // counting time in the set before he charges (once per begin())\r\n  judgeMs: 1200,       // how long he lies there judging the user's balance\r\n  steady: 0.15,        // max hip-centre shift ÷ torso length that still counts as \"didn't budge\"\r\n  swipeMs: 1800, agreeMs: 1900, laughMs: 3400 // rig clip lengths (GESTURES.swipe/agree/laugh in creature/source/motion.ts)\r\n};\r\n\r\nconst VISIBLE = 0.45;\r\nconst seen = p => p && p.visibility >= VISIBLE;\r\nconst distH = (p, q, aspect) => Math.hypot((p.x - q.x) * aspect, p.y - q.y);\r\nconst limbSeen=p=>seen(p)&&Number.isFinite(p.x)&&Number.isFinite(p.y)&&p.x>=0&&p.x<=1&&p.y>=0&&p.y<=1;\r\n// A segment crossing the box counts even when neither endpoint lands inside it.\r\nexport function sweptHit(p,q,box,padX=0,padY=0){\r\n let lo=0,hi=1;\r\n for(const [start,end,min,max] of [[p.x,q.x,box.left-padX,box.right+padX],[p.y,q.y,box.top-padY,box.bottom+padY]]){\r\n  const d=end-start;if(Math.abs(d)<1e-9){if(start<min||start>max)return false;continue;}\r\n  const a=(min-start)/d,b=(max-start)/d;lo=Math.max(lo,Math.min(a,b));hi=Math.min(hi,Math.max(a,b));if(lo>hi)return false;\r\n }\r\n return true;\r\n}\r\n\r\n// Where the user is: hip centre, shoulder half-width, floor line (knees) and horizon (hips).\r\nexport function userAnchor(points, aspect) {\r\n  if (!Array.isArray(points) || ![11, 12, 23, 24].every(i => limbSeen(points[i]))) return null;\r\n  const sx = (points[11].x + points[12].x) / 2, sy = (points[11].y + points[12].y) / 2;\r\n  const hx = (points[23].x + points[24].x) / 2, hy = (points[23].y + points[24].y) / 2;\r\n  const torso = Math.hypot((sx - hx) * aspect, sy - hy);\r\n  if(torso<=.015)return null;\r\n  const knees = [25, 26].filter(i => seen(points[i])).map(i => points[i].y);\r\n  const floorY = knees.length ? knees.reduce((a, b) => a + b) / knees.length : Math.min(1, hy + torso * 0.9);\r\n  const horizonY = Math.min(hy + COACH.horizon * (floorY - hy), floorY - 0.05);\r\n  return { x: hx, halfW: Math.abs(points[12].x - points[11].x) / 2, floorY, horizonY, height: torso * COACH.heightPerTorso };\r\n}\r\n\r\nconst halfWidth = (a, z, aspect) => (a.height / z) * COACH.boxWidth / 2 / aspect;\r\n\r\nexport function project(a, X, z, aspect) {\r\n  const height = a.height / z, half = halfWidth(a, z, aspect);\r\n  const x = 0.5 + (X - 0.5) / z, feetY = a.horizonY + (a.floorY - a.horizonY) / z;\r\n  return { x, feetY, height, box: { left: x - half, right: x + half, top: feetY - height, bottom: feetY } };\r\n}\r\n\r\n// Free screen width beside the user.\r\nexport const room = (a, side) => (side > 0 ? 1 - (a.x + a.halfW) : a.x - a.halfW);\r\n\r\n// Standing on `side` of the user at depth z, `extra` further out. Clamped on screen; `fits` = no clamp needed.\r\nexport function spot(a, side, z, aspect, extra = 0) {\r\n  const half = halfWidth(a, z, aspect), want = a.x + side * (a.halfW + COACH.gap + half + extra);\r\n  const x = Math.max(half, Math.min(1 - half, want));\r\n  return { X: 0.5 + (x - 0.5) * z, z, fits: x === want };\r\n}\r\n\r\nexport class CoachMotion {\r\n  constructor({ aspect = 0.46, now = 0, random = Math.random, play = true } = {}) {\r\n    this.play = play; // false = kick-only (four-legged coaches): no grab, no charge\r\n    this.aspect = aspect;\r\n    this.random = random;\r\n    this.prevNow = now;\r\n    this.prevPoints = null;\r\n    this.prevPoseNow = now;\r\n    this.handSeenAt = now;\r\n    this.grabMissAt = null;\r\n    this.handHistory = [];\r\n    this.throwVelocity = null;\r\n    this.anchor = null; // eased userAnchor\r\n    this.phase = 'offstage'; // offstage → walking ⇄ pausing → spun → away → walking\r\n    this.begun = false;\r\n    this.X = 0;\r\n    this.z = 1;\r\n    this.slot = null; // {side, z, extra}\r\n    this.until = 0; // pause end\r\n    this.yaw = 0;\r\n    this.rotation = 0;\r\n    this.hitStartNow = 0;\r\n    this.direction = 1;\r\n    this.flyFrom = 0;\r\n    this.grabLimb = -1;\r\n    this.grabSince = 0;\r\n    this.heldSince = 0;\r\n    this.dropAt = 0;\r\n    this.dy = 0;\r\n    this.dropFrom = 0;\r\n    this.charged = false;\r\n    this.beginNow = 0;\r\n    this.phaseAt = 0;\r\n    this.hip0 = 0;\r\n    this.shift = 0;\r\n  }\r\n\r\n  // Reps are counting: walk in on the next frame that sees the user.\r\n  begin() { this.begun = true; this.charged = false; this.beginNow = this.prevNow; }\r\n\r\n  target() { const s = this.slot; return spot(this.anchor, s.side, s.z, this.aspect, s.extra); }\r\n\r\n  // Next spot: a new depth on the same side, or cross over in front when the other side is much roomier.\r\n  pick(first = false) {\r\n    const a = this.anchor, side = this.slot?.side ?? (room(a, 1) >= room(a, -1) ? 1 : -1);\r\n    const fitsAt = sd => COACH.depths.some(z => spot(a, sd, z, this.aspect).fits);\r\n    if (!first && (room(a, -side) > room(a, side) + COACH.crossRoom || (!fitsAt(side) && fitsAt(-side)))) {\r\n      // Only cross from level or in front, so the coach passes in front of the user, never behind.\r\n      this.slot = this.z > 1 ? { side, z: COACH.depths[0], extra: 0, hurry: true } : { side: -side, z: COACH.depths[0], extra: 0 };\r\n      return;\r\n    }\r\n    const open = COACH.depths.filter(z => z !== this.slot?.z && spot(a, side, z, this.aspect).fits);\r\n    const z = open.length ? open[Math.floor(this.random() * open.length)] : Math.max(...COACH.depths);\r\n    const slack = room(a, side) - COACH.gap - 2 * halfWidth(a, z, this.aspect);\r\n    this.slot = { side, z, extra: this.random() * Math.max(0, Math.min(0.1, slack)) };\r\n  }\r\n\r\n  // Start just off the edge on the roomier side, level with the user, and walk in.\r\n  enter() {\r\n    const a = this.anchor, side = room(a, 1) >= room(a, -1) ? 1 : -1, half = halfWidth(a, 1, this.aspect);\r\n    this.X = side > 0 ? 1 + half : -half;\r\n    this.z = 1;\r\n    this.slot = { side, z: 1, extra: 0 };\r\n    this.pick(true);\r\n    this.phase = 'walking';this.dy=0;\r\n    this.yaw = -side * Math.PI / 2;\r\n  }\r\n\r\n  launch(cur,prev,now,drawn){\r\n    const dx=(cur.x-prev.x)*this.aspect,dy=cur.y-prev.y,length=Math.hypot(dx,dy);\r\n    this.direction=Math.sign(dx)||1;this.flyFrom=(drawn.left+drawn.right)/2;this.flyFromY=drawn.bottom;\r\n    const nx=length>0?dx/length:this.direction,ny=length>0?dy/length:0,half=(drawn.right-drawn.left)/2,height=drawn.bottom-drawn.top;\r\n    const xdist=Math.abs(nx)>1e-6?((nx>0?1+half-this.flyFrom:this.flyFrom+half)*this.aspect/Math.abs(nx)):Infinity;\r\n    const ydist=Math.abs(ny)>1e-6?(ny>0?1+height-this.flyFromY:this.flyFromY)/Math.abs(ny):Infinity;\r\n    const distance=Math.min(xdist,ydist)+.03;\r\n    this.flyDx=nx*distance/this.aspect;this.flyDy=ny*distance;\r\n    this.phase='spun';this.hitStartNow=now;this.grabLimb=-1;this.handHistory=[];this.throwVelocity=null;\r\n  }\r\n  strike(points,now,box,skip=-1){\r\n    const gap=now-this.prevPoseNow;if(!this.prevPoints||gap<=0||gap>COACH.maxLimbGapMs)return null;\r\n    const torso=this.anchor.height/COACH.heightPerTorso,dt=gap/1000;\r\n    const threshold=COACH.launchSpeedT+(COACH.sparseSpeedT-COACH.launchSpeedT)*Math.min(1,Math.max(0,(gap-120)/280));\r\n    for(const idx of COACH.hitLimbs){\r\n      if(idx===skip)continue;const cur=points?.[idx],prev=this.prevPoints[idx];\r\n      if(!limbSeen(cur)||!limbSeen(prev))continue;\r\n      const travel=distH(prev,cur,this.aspect)/torso;\r\n      if(travel<COACH.minReachT||travel/dt<threshold)continue;\r\n      if(sweptHit(prev,cur,box,COACH.hitPadT*torso/this.aspect,COACH.hitPadT*torso))return {cur,prev};\r\n    }\r\n    return null;\r\n  }\r\n  tick(now){return now>this.prevNow?this.update(null,now,false):null;}\r\n  update(points, now, sample=true) {\r\n    const dt = Math.max((now - this.prevNow) / 1000, 0.001);\r\n    this.prevNow = now;\r\n    const raw = userAnchor(points, this.aspect);\r\n    if (raw) {\r\n      if (!this.anchor) this.anchor = raw;\r\n      else { const f = 1 - Math.exp(-COACH.ease * dt); for (const k in raw) this.anchor[k] += (raw[k] - this.anchor[k]) * f; }\r\n    }\r\n    const a = this.anchor;\r\n    let yawTarget = this.yaw;\r\n\r\n    if (!a) {\r\n      // Never seen the user: nothing to stand beside yet.\r\n    } else if (this.phase === 'offstage') {\r\n      if (this.begun) this.enter();\r\n    } else if (this.phase === 'walking' || this.phase === 'pausing') {\r\n      // Hit test against where the coach was drawn last frame.\r\n      const drawn = project(a, this.X, this.z, this.aspect).box;\r\n      // Hover grab: a grab limb resting inside the box for grabMs lifts the coach by the head.\r\n      let hover = -1;\r\n      if (raw && this.play) for (const idx of COACH.grabLimbs) {\r\n        const p = points[idx];\r\n        if (seen(p) && p.x >= drawn.left && p.x <= drawn.right && p.y >= drawn.top && p.y <= drawn.bottom) { hover = idx; break; }\r\n      }\r\n      // A frame with no user (tracker blink) keeps the timer: only a hand seen elsewhere resets it.\r\n      if (sample && hover < 0) {this.grabMissAt??=now;if(now-this.grabMissAt>COACH.grabGraceMs/2)this.grabLimb=-1;}\r\n      else if (hover >= 0) {\r\n        this.grabMissAt=null;\r\n        if (this.grabLimb !== hover) { this.grabLimb = hover; this.grabSince = now; }\r\n        if (now - this.grabSince >= COACH.grabMs) { this.phase = 'held'; this.heldSince = now;this.handSeenAt=now;this.handHistory=[];this.throwVelocity=null; }\r\n      }\r\n      if (this.play && this.phase === 'pausing' && !this.charged && now - this.beginNow >= COACH.chargeAfterMs) {\r\n        this.charged = true;\r\n        this.phase = 'charge';\r\n        this.slot = { side: this.slot.side, z: 1, extra: -COACH.gap };\r\n      }\r\n\r\n      if (this.phase !== 'held' && this.phase !== 'charge') {\r\n        // Kick test against where the coach was drawn last frame.\r\n        const hit=raw?this.strike(points,now,drawn):null;\r\n        if(hit)this.launch(hit.cur,hit.prev,now,drawn);\r\n        if (this.phase !== 'spun') {\r\n          let t = this.target();\r\n          // The user stepped into its spot behind them: re-plan (a clear depth, or cross over in front).\r\n          if (t.z > 1 && !t.fits && !this.slot.hurry) { this.pick(); t = this.target(); }\r\n          const dx = (t.X - this.X) * this.aspect / a.height, dz = (t.z - this.z) * COACH.roomDepth, d = Math.hypot(dx, dz);\r\n          if (this.phase === 'pausing' && d > 0.3) this.phase = 'walking';\r\n          if (this.phase === 'walking') {\r\n            const step = COACH.stride * dt;\r\n            if (d <= step) {\r\n              this.X = t.X; this.z = t.z; this.phase = 'pausing';\r\n              const [lo, hi] = COACH.pauseMs;\r\n              this.until = this.slot.hurry ? now : now + lo + this.random() * (hi - lo);\r\n            } else {\r\n              this.X += (t.X - this.X) * step / d; this.z += (t.z - this.z) * step / d;\r\n              yawTarget = Math.atan2(dx, -dz); // face where it walks: right = +π/2, away = π\r\n            }\r\n          }\r\n          if (this.phase === 'pausing') {\r\n            this.X = t.X; this.z = t.z; // small user shifts: stay put beside them\r\n            yawTarget = -this.slot.side * 0.35; // turned a little toward the user\r\n            if (now >= this.until) { this.pick(); this.phase = 'walking'; }\r\n          }\r\n        }\r\n      }\r\n    } else if (this.phase === 'charge') {\r\n      const t = this.target(), dx = (t.X - this.X) * this.aspect / a.height, dz = (t.z - this.z) * COACH.roomDepth, d = Math.hypot(dx, dz), step = COACH.stride * dt;\r\n      if (d <= step) { this.X = t.X; this.z = t.z; this.phase = 'swiping'; this.phaseAt = now; yawTarget = -this.slot.side * Math.PI / 2; }\r\n      else { this.X += (t.X - this.X) * step / d; this.z += (t.z - this.z) * step / d; yawTarget = Math.atan2(dx, -dz); }\r\n    } else if (this.phase === 'swiping') {\r\n      yawTarget = -this.slot.side * Math.PI / 2; // squared up to the user\r\n      if (now - this.phaseAt >= COACH.swipeMs) { this.phase = 'fallen'; this.phaseAt = now; this.hip0 = raw ? raw.x : a.x; this.shift = 0; }\r\n    } else if (this.phase === 'fallen') {\r\n      // Judge: the biggest hip-centre shift while he lies there, in torso lengths (x in the same units distH uses).\r\n      if (raw) this.shift = Math.max(this.shift, Math.abs(raw.x - this.hip0) * this.aspect / (a.height / COACH.heightPerTorso));\r\n      if (now - this.phaseAt >= COACH.judgeMs) { this.phase = this.shift < COACH.steady ? 'impressed' : 'laughing'; this.phaseAt = now; }\r\n    } else if (this.phase === 'impressed' || this.phase === 'laughing') {\r\n      yawTarget = -this.slot.side * 0.35;\r\n      if (now - this.phaseAt >= (this.phase === 'impressed' ? COACH.agreeMs : COACH.laughMs)) { this.pick(); this.phase = 'walking'; }\r\n    } else if (this.phase === 'held') {\r\n      const p = points?.[this.grabLimb];\r\n      const drawn=project(a,this.X,this.z,this.aspect).box;drawn.top+=this.dy;drawn.bottom+=this.dy;\r\n      const hit=raw?this.strike(points,now,drawn):null;\r\n      if(hit){this.launch(hit.cur,hit.prev,now,drawn);}\r\n      else if ((now-this.handSeenAt>COACH.grabGraceMs) || now - this.heldSince >= COACH.holdMs) {\r\n        // A recently moving hand releases with its observed velocity; a stale hand simply drops.\r\n        const velocity=this.throwVelocity;\r\n        if(velocity&&now-velocity.t<=COACH.throwWindowMs){\r\n          this.launch({x:velocity.x+velocity.vx,y:velocity.y+velocity.vy},{x:velocity.x,y:velocity.y},now,drawn);\r\n        }else{\r\n          this.phase = 'dropped';\r\n          this.dropAt = now;\r\n          this.dropFrom = this.dy;\r\n          this.grabLimb = -1;\r\n        }\r\n      } else if (raw&&limbSeen(p)) {\r\n        this.handSeenAt=now;\r\n        this.handHistory.push({x:p.x,y:p.y,t:now});this.handHistory=this.handHistory.filter(h=>now-h.t<=COACH.throwWindowMs);\r\n        const first=this.handHistory[0],span=(now-first.t)/1000,torso=a.height/COACH.heightPerTorso;\r\n        if(span>0){const vx=(p.x-first.x)/span,vy=(p.y-first.y)/span;if(Math.hypot(vx*this.aspect,vy)/torso>=COACH.throwSpeedT&&distH(first,p,this.aspect)/torso>=COACH.minReachT)this.throwVelocity={x:p.x,y:p.y,vx,vy,t:now};}\r\n        // A moving hand that becomes unseen is a release; one still, missing sample keeps the grip.\r\n        // The box top-centre follows the limb; feet hang below it.\r\n        this.X = 0.5 + (p.x - 0.5) * this.z;\r\n        const base = project(a, this.X, this.z, this.aspect);\r\n        this.dy = p.y + base.height - base.feetY;\r\n      }\r\n      if(this.phase==='held'&&sample&&!limbSeen(p)&&this.throwVelocity&&now-this.throwVelocity.t<=COACH.throwWindowMs){\r\n        const v=this.throwVelocity;this.launch({x:v.x+v.vx,y:v.y+v.vy},{x:v.x,y:v.y},now,drawn);\r\n      }\r\n    } else if (this.phase === 'dropped') {\r\n      const u = Math.min((now - this.dropAt) / COACH.dropMs, 1);\r\n      this.dy = this.dropFrom * (1 - u);\r\n      if (u >= 1) { this.dy = 0; this.phase = 'walking'; }\r\n    } else if (this.phase === 'spun') {\r\n      const u = Math.min((now - this.hitStartNow) / COACH.spinMs, 1);\r\n      const x = this.flyFrom + this.flyDx * u;\r\n      this.X = 0.5 + (x - 0.5) * this.z;\r\n      const base=project(a,this.X,this.z,this.aspect);this.dy=this.flyFromY+this.flyDy*u-base.feetY;\r\n      this.rotation = this.direction * u * 4 * Math.PI;\r\n      if (u >= 1) { this.phase = 'away'; this.hitStartNow = now; this.rotation = 0; }\r\n    } else if (this.phase === 'away') {\r\n      if (now - this.hitStartNow >= COACH.awayMs) this.enter();\r\n    }\r\n\r\n    const turn = Math.atan2(Math.sin(yawTarget - this.yaw), Math.cos(yawTarget - this.yaw));\r\n    this.yaw += turn * (1 - Math.exp(-COACH.turnRate * dt));\r\n    if(sample){this.prevPoseNow=now;this.prevPoints = Array.isArray(points) ? points.map(p => p && { x: p.x, y: p.y, visibility: p.visibility }) : null;}\r\n\r\n    const placed = a ? project(a, this.X, this.z, this.aspect) : { x: 0, feetY: 0, height: 0, box: { left: 0, right: 0, top: 0, bottom: 0 } };\r\n    if (this.dy !== 0) {\r\n      placed.feetY += this.dy;\r\n      placed.box.top += this.dy;\r\n      placed.box.bottom += this.dy;\r\n    }\r\n    return { phase: this.phase, z: this.z, yaw: this.yaw, rotation: this.rotation, baseHeight: a?.height ?? 0, ...placed, dy: this.dy, side: this.slot?.side ?? 1, shift: this.shift };\r\n  }\r\n}\r\n","coach-hub.css":".coach-mission{position:relative;margin:4px 0 16px;padding:18px;border:2px solid #ba9bff;background:linear-gradient(135deg,#362050,#160f25 70%);box-shadow:5px 5px 0 #07090e,inset 0 0 25px #a464ff20;overflow:hidden}.mission-tag{color:#c9fda5;font:700 .75rem ui-monospace,monospace;letter-spacing:.1em}.mission-head{display:flex;align-items:center;gap:16px;justify-content:space-between;margin:14px 0}.mission-head h3{margin:0;color:#f0d2ff;font:950 clamp(1.8rem,8vw,3rem)/.95 system-ui;letter-spacing:-.05em}.mission-head p{font-size:.875rem;margin:10px 0 0;color:#d7c5e5}.mission-streak{text-align:center;color:#baff92;min-width:88px}.mission-streak strong{display:block;font:900 3.8rem/1 ui-monospace,monospace;text-shadow:3px 3px #204421}.mission-streak span{font:700 .75rem ui-monospace,monospace}.mission-arm{display:flex;justify-content:space-between;width:100%;background:#baff92;color:#101e12;border:2px solid #162516;border-radius:2px;font-weight:900;box-shadow:0 4px #477944;min-height:50px}.mission-readouts{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;margin:14px 0 4px;font:.875rem ui-monospace,monospace;color:#d5ffe1}.mission-guide{padding:8px 0;background:none;border:0;border-radius:0;font:700 .875rem ui-monospace,monospace;color:#c8acec}.coach-mission[data-boost=true]{border-color:#f9d96b;box-shadow:5px 5px 0 #07090e,inset 0 0 30px #e7bf3920}.terminal-menu{background:#081611!important;color:#ccffd9!important;border:2px solid #8bb39b!important;border-radius:3px!important;box-shadow:7px 7px 0 #030806!important;font:1rem/1.5 ui-monospace,SFMono-Regular,Consolas,monospace!important;max-height:90dvh;overflow:auto}.terminal-menu header{background:#183429!important;border-bottom:1px solid #6e9b7b;padding:12px!important;display:flex;justify-content:space-between;align-items:center;gap:12px}.terminal-menu h2{font:700 1rem ui-monospace,monospace;letter-spacing:.03em;color:#beff99}.terminal-menu button,.terminal-menu input,.terminal-menu select{border-radius:2px!important;font:inherit;background:#10271e;color:#d8ffe6;border:1px solid #5f9371;min-height:44px}.terminal-menu input[type=checkbox]{min-height:22px;width:22px;accent-color:#aff58c}.terminal-menu label,.terminal-menu summary{font-size:1rem}.terminal-menu p{color:var(--strip-dim)!important;font-size:.875rem}.terminal-links{display:grid;gap:8px;margin-top:20px;border-top:1px solid #526c59;padding-top:12px}.terminal-links button{text-align:left}.how-to-play{width:min(700px,94vw);padding:0}.how-to-play [role=tablist]{display:flex;overflow-x:auto;gap:4px;padding:10px;border-bottom:1px solid #405b48}.how-to-play [role=tab]{white-space:nowrap;flex-shrink:0;font-size:.875rem}.how-to-play [aria-selected=true]{background:#b3ef95;color:#0a2012;font-weight:bold}.how-to-play [role=tabpanel]{padding:18px}.how-to-play h3{font-size:1.25rem;color:#c4ffa5}.how-to-play h4{font-size:1rem;margin:20px 0 4px}.how-to-play article p{margin:0}.guide-track{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:12px 0;border-bottom:1px solid #314b3b}.guide-track>strong{grid-column:1/-1;font-size:1rem}.guide-track>span{display:flex;align-items:center;font-size:.875rem;gap:6px}.guide-track canvas{width:30px;height:54px;image-rendering:pixelated}.coach-reminder-plan{padding:14px;margin:12px 0;background:#0d2117;border:1px solid #638566}.coach-reminder-plan h3{font-size:1.2rem;color:#c3ff99;margin:0 0 14px}.coach-reminder-plan label{display:grid;gap:5px;margin-bottom:12px}.coach-reminder-plan [data-times],.plan-quiet{display:grid;grid-template-columns:repeat(auto-fit,minmax(115px,1fr));gap:10px}.coach-reminder-plan input,.coach-reminder-plan select{width:100%;min-width:0}.coach-reminder-plan .plan-enabled{display:flex;align-items:center;gap:10px}.coach-reminder-plan [type=submit]{width:100%;background:#bbf293;color:#0f2314;font-weight:900}\r\n.coach-reminder-plan blockquote{margin:0 0 12px;padding:10px;border-left:3px solid #bbf293;background:#0a1a11;font-size:.9rem}\r\n.coach-reminder-plan .coach-tone{margin:0 0 12px;font-size:.85rem;opacity:.85}.account-settings{background:#081611;border:1px solid #5f9371;padding:12px;color:#ccffd9;font-family:ui-monospace,monospace}@media(max-width:380px){.coach-mission{padding:12px}.mission-streak strong{font-size:3rem}.mission-readouts{font-size:.8rem}.guide-track{grid-template-columns:1fr}.guide-track>strong{grid-column:auto}}@media(prefers-reduced-motion:reduce){.coach-mission *{animation:none!important;transition:none!important}}\r\n\r\n/* R-1016: the portal's tilt chip sits on the dock edge and covered panel text (Reminders); hide it while a panel dialog is open. */\r\nbody:has(dialog.launch-panel[open]) .portal-tilt-chip{display:none!important}\r\n.mission-guest{margin:8px 0 0;padding:8px 10px;background:#160f25;border-left:3px solid #ba9bff;font-size:.9rem}\r\n.how-to-play .guide-start{position:sticky;bottom:0;padding:10px 14px;background:#07140d;border-top:1px solid #405b48}.how-to-play .guide-start button{width:100%;min-height:52px;font-weight:700;font-size:1.05rem}\r\n","coach-hub.mjs":"import {weaponDamage} from './combat.mjs';\r\nimport {TRAINING_TRACKS} from './weapon-training.mjs';\r\nimport {paintStyleText} from './workout-symbols.mjs';\r\nimport {coachReminder} from './reminder-plan.mjs';\r\nimport {RELEASE} from './release-info.mjs';\r\nimport {mountSettingsCrt} from './settings-crt.mjs';\r\nimport {mountAppleBasicShare} from './apple-basic-share.mjs';\r\nimport {dailyGuideDue,markDailyGuide,guideOwner} from './daily-guide.mjs';\r\nimport {signInPath} from './auth-paths.mjs';\r\nimport {COACHES,SITUATIONS,getCoach} from './creature/source/creator/coaching.ts';\r\n\r\n// #107/#120: military-satcom frame, usable on any dialog. Adds a top SATCOM status strip (an\r\n// ACQUIRING… -> {title} animation replayed every time the dialog opens), a bottom clock/BUILD\r\n// strip, a corner radar trace and corner bolts. Doesn't touch the dialog's existing items/nav.\r\n// Wrapping the dialog's own showModal() (rather than one known opener button) catches every route\r\n// in: Settings has one (#openSettings), but Install and the Downloads menu each open from several\r\n// places (the dock, Settings' Downloads button, a deep link, window.myr5Packs.open()...).\r\nexport function mountSatcomFrame(dialog,{title='UPLINK ESTABLISHED'}={}){\r\n const reduced=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n dialog.classList.add('satcom-frame');\r\n const top=document.createElement('div');top.className='satcom-strip satcom-top';top.setAttribute('aria-hidden','true');\r\n top.innerHTML='<svg class=\"satcom-sat\" viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/><path d=\"M9 12H3M15 12h6M12 9V3M12 15v6\"/><path d=\"m4.5 4.5 2.5 2.5M19.5 4.5 17 7\"/></svg><span class=\"satcom-label\">SATCOM // <em data-link>ACQUIRING…</em></span><span class=\"satcom-bars\" data-bars><i></i><i></i><i></i><i></i></span>';\r\n const bottom=document.createElement('div');bottom.className='satcom-strip satcom-bottom';bottom.setAttribute('aria-hidden','true');\r\n bottom.innerHTML='<span class=\"satcom-clock\" data-clock>--:--:--</span><span class=\"satcom-build\" data-build></span><i class=\"satcom-radar\"></i>';\r\n const build=bottom.querySelector('[data-build]');build.textContent='BUILD '+RELEASE.id;build.title=RELEASE.id;\r\n dialog.prepend(top);dialog.append(bottom);\r\n for(const pos of ['tl','tr','bl','br']){const bolt=document.createElement('i');bolt.className='satcom-bolt satcom-bolt-'+pos;bolt.setAttribute('aria-hidden','true');dialog.append(bolt);}\r\n const clock=bottom.querySelector('[data-clock]');\r\n const tick=()=>clock.textContent=new Date().toLocaleTimeString('en-GB',{hour12:false});\r\n tick();setInterval(tick,1000);\r\n const link=top.querySelector('[data-link]'),bars=[...top.querySelectorAll('[data-bars] i')];\r\n let lockTimer=null;\r\n function acquire(){\r\n  clearTimeout(lockTimer);top.classList.remove('locked');link.textContent='ACQUIRING…';\r\n  // The locked intake theme disables animation with !important; inline !important keeps this\r\n  // moving there too, same trick as the portal (modules/portal/portal.mjs).\r\n  if(!reduced())for(const [i,bar] of bars.entries())bar.style.setProperty('animation',`satcom-blink .5s ease ${i*110}ms infinite`,'important');\r\n  const settle=()=>{\r\n   top.classList.add('locked');link.textContent=title;\r\n   for(const bar of bars)bar.style.removeProperty('animation');\r\n   if(!reduced())top.style.setProperty('animation','satcom-flash .5s ease','important');\r\n  };\r\n  reduced()?settle():lockTimer=setTimeout(settle,900);\r\n }\r\n const nativeShowModal=dialog.showModal.bind(dialog);\r\n dialog.showModal=(...args)=>{acquire();return nativeShowModal(...args);};\r\n}\r\n\r\nimport {GUIDE as guide} from './progression-guide.mjs';\r\n\r\nexport const SETTINGS_LINKS=[['ACCOUNT','scoreboard'],['DEVICE + UPDATES','install'],['HOW TO PLAY','guide']];\r\n// Settings always opens collapsed: every section folded and scrolled to the top.\r\nexport function collapseOnOpen(dialog){\r\n const showModal=dialog.showModal.bind(dialog);\r\n dialog.showModal=(...args)=>{for(const details of dialog.querySelectorAll('details'))details.open=false;dialog.scrollTop=0;return showModal(...args);};\r\n}\r\n\r\nexport function mountCoachHub({api}){\r\n const dance=document.getElementById('openDance');if(dance)dance.onclick=()=>window.myr5Routes?.go('dance'); // the Dance key lives in pose.html with the other game keys\r\n const hub=document.createElement('section');hub.className='coach-mission';hub.setAttribute('aria-label','Coach reminders and daily power');\r\n hub.innerHTML='<div class=\"mission-tag\">MOM INC // DAILY ORDERS</div><div class=\"mission-head\"><div><h3>CHECK IN.<br>POWER UP.</h3><p data-hub-line>MOM has your next move.</p></div><div class=\"mission-streak\"><strong data-streak>—</strong><span>DAY STREAK</span></div></div><button class=\"mission-arm\" type=\"button\">ARM YOUR REMINDERS <span>→</span></button><div class=\"mission-readouts\"><span data-message-count>1–3 messages / day</span><span data-damage>Sign in to power up</span></div><button class=\"mission-guide\" type=\"button\">HOW TO PLAY</button>';\r\n // W2-2M #117: the pod is only the viewing port, the control board and BEGIN. The daily-orders card lives at the\r\n // top of Reminders (bar), where ARM goes to the arming switch; the pod's other extras (#podExtras) go to Records.\r\n {const hatch=document.getElementById('reminderHatch');if(hatch)hatch.prepend(hub);else document.querySelector('#remindersPanel header').after(hub);} // R3-12: the daily-orders card rides in the TV's service hatch\r\n // Guests can't be reached by reminders: say so and offer sign-in instead of silently focusing a checkbox.\r\n hub.querySelector('.mission-arm').onclick=()=>{\r\n  if(window.myr5AuthenticatedAccount)return form.elements.enabled.focus();\r\n  let note=hub.querySelector('.mission-guest');\r\n  if(!note){note=document.createElement('p');note.className='mission-guest';note.setAttribute('role','status');note.append('Reminders need an account so MOM can reach you. ');const link=document.createElement('a');link.href=signInPath('/pose.html?panel=reminders');link.textContent='Sign in to arm reminders';link.style.color='#b8e9cf';note.append(link);hub.querySelector('.mission-arm').after(note);}\r\n  note.scrollIntoView({block:'nearest'});\r\n  note.querySelector('a').focus();\r\n };\r\n document.querySelector('#accountPanel header').after(document.getElementById('podExtras'));\r\n const dialog=document.createElement('dialog');dialog.className='how-to-play terminal-menu';dialog.setAttribute('aria-labelledby','howToPlayTitle');dialog.innerHTML='<header><h2 id=\"howToPlayTitle\">MOM://FIELD MANUAL</h2><button type=\"button\" data-close-guide>Close</button></header><div role=\"tablist\" aria-label=\"How to play\"></div><section role=\"tabpanel\" tabindex=\"0\"></section><footer class=\"guide-start\"><button type=\"button\" class=\"primary-action\" data-guide-start>Start a workout</button></footer>';\r\n document.body.append(dialog);const tabs=dialog.querySelector('[role=tablist]'),panel=dialog.querySelector('[role=tabpanel]');let current=0,opener=null;\r\n function choose(index,focus=false){current=(index+guide.length)%guide.length;for(const [i,tab] of [...tabs.children].entries()){tab.setAttribute('aria-selected',String(i===current));tab.tabIndex=i===current?0:-1;}const [id,,title,items]=guide[current];panel.id='guide-panel';panel.setAttribute('aria-labelledby','guide-tab-'+id);panel.replaceChildren();const h=document.createElement('h3');h.textContent=title;panel.append(h);\r\n  if(id==='weapons')for(const track of Object.values(TRAINING_TRACKS)){const row=document.createElement('div');row.className='guide-track';const label=paintStyleText(document.createElement('strong'),track.name);row.append(label);for(const type of track.weapons){const item=document.createElement('span'),canvas=document.createElement('canvas');canvas.width=40;canvas.height=72;canvas.setAttribute('aria-hidden','true');window.GalaWeapons.draw(canvas.getContext('2d'),{type,tier:4});item.append(canvas,document.createTextNode(window.GalaWeapons.types.find(w=>w.id===type).name));row.append(item);}panel.append(row);}\r\n  for(const [label,text] of items){const block=document.createElement('article'),heading=document.createElement('h4'),p=document.createElement('p');heading.textContent=label;p.textContent=text;block.append(heading,p);panel.append(block);}if(focus)tabs.children[current].focus();\r\n }\r\n guide.forEach(([id,label],index)=>{const button=document.createElement('button');button.type='button';button.id='guide-tab-'+id;button.setAttribute('role','tab');button.setAttribute('aria-controls','guide-panel');button.textContent=label;button.onclick=()=>choose(index);tabs.append(button);});\r\n tabs.onkeydown=event=>{const move={ArrowRight:current+1,ArrowLeft:current-1,Home:0,End:guide.length-1};if(Object.hasOwn(move,event.key)){event.preventDefault();choose(move[event.key],true);}};\r\n // Primary action for a first-timer: leave the manual and land on the pod's BEGIN button (opening the pod from the quilt if needed).\r\n dialog.querySelector('[data-guide-start]').onclick=()=>{dialog.close();const centre=document.querySelector('.dock-portal');if(centre?.getAttribute('aria-label')==='Open workout pod')centre.click();setTimeout(()=>{const go=document.getElementById('start');go?.scrollIntoView({block:'center'});go?.focus({preventScroll:true});},400);};\r\n function openGuide(){opener=document.activeElement;choose(0);dialog.showModal();tabs.children[0].focus();}\r\n hub.querySelector('.mission-guide').onclick=openGuide;\r\n // How to Play opens by itself once per local day, at the first app opening that day for this account (or the\r\n // guest). Waits for the starter quilt to come up and for no other popup to be up, so it never stacks on the setup\r\n // gate, a reward reveal or the quilt's own first frame.\r\n const guideTried=new Set();\r\n function dailyGuide(){\r\n  const owner=guideOwner();if(guideTried.has(owner)||!dailyGuideDue({owner}))return;guideTried.add(owner);\r\n  const started=Date.now(),attempt=()=>{\r\n   if(guideOwner()!==owner)return;\r\n   const busy=document.querySelector('dialog[open]')||window.myr5Packs?.due?.()||document.body.dataset.tracking==='true'||document.body.dataset.screen==='rest'||document.body.dataset.cinematic;\r\n   // The starter quilt checks for open dialogs before it appears: give it the first word (up to 5 s).\r\n   const settled=document.getElementById('portalHome')?.hidden===false||Date.now()-started>=15000;\r\n   if(busy||!settled){setTimeout(attempt,busy?1000:250);return;}\r\n   if(!dailyGuideDue({owner}))return;markDailyGuide({owner});openGuide();\r\n  };\r\n  setTimeout(attempt,0);\r\n }\r\n window.addEventListener('myr5:coach-plan',dailyGuide);\r\n dialog.querySelector('[data-close-guide]').onclick=()=>dialog.close();dialog.addEventListener('close',()=>opener?.focus());\r\n const settings=document.getElementById('settings');settings.classList.add('terminal-menu');document.getElementById('settingsTitle').textContent='MOM://POD CONTROL';\r\n const nav=document.createElement('nav');nav.className='terminal-links';nav.setAttribute('aria-label','Behind the scenes');\r\n // #79: every link but HOW TO PLAY is a real route (W2-2A's router). Portal, Achievements and Reminders have\r\n // their own doors (the quilt and the bottom bar), so Settings no longer repeats them.\r\n for(const [label,target] of SETTINGS_LINKS){const b=document.createElement('button');b.type='button';b.textContent='> '+label;b.onclick=()=>{settings.close();target==='guide'?openGuide():window.myr5Routes?.go(target);};nav.append(b);}settings.append(nav);\r\n mountAppleBasicShare(settings); // hidden until APPLE_BASIC_SHARE_URL is set\r\n mountSatcomFrame(settings);\r\n collapseOnOpen(settings);\r\n mountSettingsCrt(settings); // #137: TV touch effect + CRT curve + scan lines, screen area only\r\n for(const id of ['installPanel','remindersPanel'])document.getElementById(id).classList.add('terminal-menu');\r\n let progress=null;\r\n function paint(){hub.querySelector('[data-streak]').textContent=progress?.combat?.loginStreak??'—';let weapon={type:'rapier',tier:0};try{weapon=window.GalaWeapons.normalize(JSON.parse(localStorage.getItem('mominc-avatar-v1'))?.weapon||weapon);}catch{}if(!window.GalaWeapons.unlocked(weapon,progress))weapon={type:weapon.type,tier:0};const damage=weaponDamage(progress?.combat,weapon);hub.querySelector('[data-damage]').textContent=damage?damage.toLocaleString()+' DMG / HIT':'Sign in to power up';hub.dataset.boost=String(!!progress?.combat?.breathingCompleted);}\r\n window.addEventListener('myr5:account-progress',event=>{progress=event.detail;paint();});window.addEventListener('mominc-avatar-change',paint);\r\n const form=document.createElement('form');form.className='coach-reminder-plan';form.innerHTML='<h3>YOUR DAILY ORDERS</h3><label>Messages per day<select name=\"count\"><option value=\"1\">1 message</option><option value=\"2\">2 messages</option><option value=\"3\" selected>3 messages</option></select></label><div data-times></div><label>Coach tone<select name=\"tone\"><option value=\"cheeky\">Drill coach</option><option value=\"direct\">Direct</option><option value=\"gentle\">Gentle</option></select></label><label>Coach personality<select name=\"personality\"></select></label><p class=\"coach-tone\" data-personality-tone></p><label>Preview a situation<select name=\"situation\"></select></label><blockquote data-personality-line aria-live=\"polite\"></blockquote><div class=\"plan-quiet\"><label>Quiet from<input name=\"quietStart\" type=\"time\" value=\"22:00\" required></label><label>Until<input name=\"quietEnd\" type=\"time\" value=\"07:00\" required></label></div><label class=\"plan-enabled\"><input name=\"enabled\" type=\"checkbox\"> Reminders armed</label><button type=\"submit\">SAVE ORDERS</button><p data-plan-status role=\"status\"></p><details><summary>When training is missed</summary><p data-escalation></p></details>';\r\n hub.after(form);\r\n const old=document.getElementById('reminderForm'),details=document.createElement('details');details.innerHTML='<summary>Individual reminders</summary>';old.before(details);details.append(old);\r\n let times=['09:00','14:00','19:00'],timezone=Intl.DateTimeFormat().resolvedOptions().timeZone;\r\n function timeFields(){const host=form.querySelector('[data-times]');for(const [i,input] of [...host.querySelectorAll('input')].entries())times[i]=input.value;host.replaceChildren();for(let i=0;i<Number(form.elements.count.value);i++){const label=document.createElement('label');label.textContent='Check-in '+(i+1);const input=document.createElement('input');input.type='time';input.required=true;input.value=times[i]||['09:00','14:00','19:00'][i];label.append(input);host.append(label);}}\r\n const RECIPE='myr5-recipe-v1',readRecipe=()=>{try{const r=JSON.parse(localStorage.getItem(RECIPE));return r&&typeof r==='object'?r:null;}catch{return null;}};\r\nlet chosenCoach=getCoach(readRecipe()?.coach).id;\r\nconst pSelect=form.elements.personality,sSelect=form.elements.situation;\r\nfor(const c of COACHES){const opt=document.createElement('option');opt.value=c.id;opt.textContent=c.name;pSelect.append(opt);}\r\nfor(const [value,label] of SITUATIONS){const opt=document.createElement('option');opt.value=value;opt.textContent=label;sSelect.append(opt);}\r\npSelect.value=chosenCoach;sSelect.value='start';\r\nfunction updatePersonality(){const coach=getCoach(chosenCoach);form.querySelector('[data-personality-tone]').textContent=coach.tone;form.querySelector('[data-personality-line]').textContent=coach.lines[sSelect.value]||coach.lines.start;}\r\n// Same storage as the old customizer tab: the saved recipe's `coach` field (re-read at change time so a newer customizer save is never overwritten).\r\npSelect.onchange=()=>{if(!COACHES.some(c=>c.id===pSelect.value))return;chosenCoach=pSelect.value;const stored=readRecipe();if(stored){const updated={...stored,coach:chosenCoach};try{localStorage.setItem(RECIPE,JSON.stringify(updated));window.dispatchEvent(new CustomEvent('myr5:recipe',{detail:updated}));}catch{}}updatePersonality();};\r\nsSelect.onchange=updatePersonality;\r\nupdatePersonality();\r\nform.elements.count.onchange=timeFields;form.elements.tone.onchange=()=>{form.querySelector('[data-escalation]').textContent=coachReminder(3,form.elements.tone.value);};timeFields();form.elements.tone.onchange();\r\n function showPlan(p){times=p.times||times;timezone=p.timezone||timezone;form.elements.count.value=p.count;form.elements.tone.value=p.tone;form.elements.enabled.checked=!!p.enabled;form.elements.quietStart.value=p.quietStart||'22:00';form.elements.quietEnd.value=p.quietEnd||'07:00';form.querySelector('[data-times]').replaceChildren();timeFields();form.elements.tone.onchange();hub.querySelector('[data-message-count]').textContent=p.enabled?p.count+' messages / day':'Reminders paused';hub.querySelector('.mission-arm').firstChild.textContent=p.enabled?'YOUR ORDERS ARE ARMED ':'ARM YOUR REMINDERS ';}\r\n async function load(){try{showPlan(await api('/api/reminders/plan'));}catch(error){form.querySelector('[data-plan-status]').textContent=error.message;}}\r\n document.querySelector('.coach-dock [data-route=reminders]')?.addEventListener('click',load);\r\n form.onsubmit=async event=>{event.preventDefault();const button=form.querySelector('[type=submit]');button.disabled=true;try{const p=await api('/api/reminders/plan','PUT',{count:Number(form.elements.count.value),times:[...form.querySelectorAll('[data-times] input')].map(i=>i.value),tone:form.elements.tone.value,enabled:form.elements.enabled.checked,timezone,daysPerWeek:7,quietStart:form.elements.quietStart.value,quietEnd:form.elements.quietEnd.value});showPlan(p);form.querySelector('[data-plan-status]').textContent=p.enabled?'Orders saved. Turn on notifications for this device below.':'Orders paused.';}catch(error){form.querySelector('[data-plan-status]').textContent=error.message;}finally{button.disabled=false;}};\r\n paint();\r\n}\r\n","coach-names.mjs":"// User-selected coach identities. Each retained coach has one or two display names.\r\n// Person, workout, and tech preserve provenance from the matching original candidate.\r\n// Track is the coach's primary mapped workout group (performance-catalog tracks).\r\nexport const EXCLUDED_COACH_IDS=Object.freeze(['roster/21-flyer--winged_humanoid_3d_model','roster/18-quad-all--wooden_robot_3d_model']);\r\nconst ROWS=[\r\n[\"myr5\",\"original\",[\"MYR5 Jacked LaLAN\",\"Jack LaLanne\",\"jacked strength\",\"local area network\"],[\"MYR5 AT-Lift Prime\",\"Charles Atlas\",\"lift\",\"model designation\"]],\r\n[\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"arms\",[\"Arnoid Press\",\"Arnold Schwarzenegger\",\"Arnold press\",\"android\"],[\"ScotTorque Curl\",\"Larry Scott\",\"curl\",\"motor torque\"]],\r\n[\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"arms\",[\"Ferri-Delt L0U\",\"Lou Ferrigno\",\"delt\",\"robot designation\"],[\"R0N Curlman\",\"Ronnie Coleman\",\"curl\",\"engine manifold\"]],\r\n[\"19-genie-multi--multi-armed_humanoid_3d_model\",\"arms\",[\"Frank Delt-Zan3\",\"Frank Zane\",\"delt\",\"magnetic coil\"],[\"Cutlerator Curl\",\"Jay Cutler\",\"curl\",\"accelerator\"]],\r\n[\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"arms\",[\"KAI-nematic Raise\",\"Kai Greene\",\"raise\",\"kinematic mechanism\"],[\"Philament Heave Press\",\"Phil Heath\",\"press\",\"electrical filament\"]],\r\n[\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"arms\",[\"Wheel-er Servo-Shrug\",\"Flex Wheeler\",\"shrug\",\"servo wheel\"]],\r\n[\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"arms\",[\"Ree-volts Raise\",\"Steve Reeves\",\"raise\",\"voltage\"]],\r\n[\"08-shard-asym--robot_3d_model4\",\"arms\",[\"S3RG Oliv-Armature\",\"Sergio Oliva\",\"arm\",\"motor armature\"]],\r\n[\"17-shellcap-manyarm--3d_character_figurine\",\"arms\",[\"Delt-alinn Bailey\",\"Dana Linn Bailey\",\"delt\",\"Delta designation\"]],\r\n[\"06-ridge-triad--geometric_robot_3d_model1\",\"chest\",[\"Oliva-C Pec\",\"Sergio Oliva\",\"pec\",\"vacuum drive\"]],\r\n[\"08-shard-asym--geometric_robot_3d_model\",\"chest\",[\"Pec-n0ld Reactor\",\"Arnold Schwarzenegger\",\"pec\",\"reactor\"]],\r\n[\"16-spade-arch--pyramid_head_figure_3d_model\",\"chest\",[\"Franx Pec-Zane\",\"Frank Zane\",\"pec\",\"motor drive\"],[\"Y4TES Yoke\",\"Dorian Yates\",\"press\",\"powered yoke\"]],\r\n[\"04-crest-wedge--robot_creature_3d_model\",\"chest\",[\"L0U Ferri-pecctor\",\"Lou Ferrigno\",\"pec\",\"reactor\"],[\"Wheeler Wheel-Bench\",\"Flex Wheeler\",\"bench\",\"drive wheel\"]],\r\n[\"16-spade-arch--stylized_humanoid_3d_model\",\"chest\",[\"Cutlerator Press\",\"Jay Cutler\",\"press\",\"accelerator\"]],\r\n[\"04-crest-wedge--stylized_3d_character\",\"chest\",[\"Gr33ne Grid-Press\",\"Kai Greene\",\"press\",\"power grid designation\"]],\r\n[\"09-monolith-tanka--3d_robot_model\",\"chest\",[\"chris BenchSTEAD\",\"Chris Bumstead\",\"bench\",\"robot\"]],\r\n[\"09-monolith-tanka--white_horned_robot_3d_model\",\"chest\",[\"Cavalier-EX Chest\",\"Jeff Cavaliere\",\"chest\",\"exercise model EX\"]],\r\n[\"15-orbital-coili--robot_character_3d_model\",\"quads\",[\"t-OHM Platzma\",\"Tom Platz\",\"squat\",\"plasma\"]],\r\n[\"13-fan-split--stylized_3d_character2\",\"quads\",[\"Quad-Cutler LEGS THO\",\"Jay Cutler\",\"quad\",\"circuit\"],[\"BumSTEADy Squat\",\"Chris Bumstead\",\"squat\",\"steady-state control\"]],\r\n[\"22-curve--stylized_cartoon_figure_3d_model\",\"quads\",[\"Kai Gr33ne Lunge\",\"Kai Greene\",\"lunge\",\"gear designation\"]],\r\n[\"02-taper-tallstalk--3d_humanoid_figure\",\"quads\",[\"Hane-Yoke Lunge\",\"Lee Haney\",\"lunge\",\"powered yoke\"],[\"Stern-on Squat\",\"Erin Stern\",\"squat\",\"power-on switch\"]],\r\n[\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"quads\",[\"Oli-Volt Lunge\",\"Sergio Oliva\",\"lunge\",\"voltage\"],[\"Y4TES Yoke-Squat\",\"Dorian Yates\",\"squat\",\"robot designation\"]],\r\n[\"18-quad-all--wooden_four-legged_robot_3d_model\",\"quads\",[\"Priest-L33 Squat\",\"Lee Priest\",\"squat\",\"piston\"]],\r\n[\"18-quad-all--stylized_quadruped_3d_model\",\"quads\",[\"Quadzilla Platz\",\"Tom Platz\",\"quads\",\"kaiju creature\"],[\"SternShift Squat\",\"Erin Stern\",\"squat\",\"gear shift\"]],\r\n[\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"glutes\",[\"Con-Thrust-Era\",\"Bret Contreras\",\"hip thrust\",\"thruster\"]],\r\n[\"23-blob-texture-bodies--sand_creature_3d_model\",\"glutes\",[\"Tingularity Thrust\",\"Chloe Ting\",\"hip thrust\",\"singularity\"]],\r\n[\"03-pearl-orb-ring--stylized_3d_character1\",\"glutes\",[\"Gyrvanium Glute-Drive\",\"Caroline Girvan\",\"glute\",\"futuristic alloy\"],[\"Jill-iON Hinge-Flex\",\"Jillian Michaels\",\"hip hinge\",\"ion drive\"]],\r\n[\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"glutes\",[\"Trac-ION Hip-Thrust\",\"Tracy Anderson\",\"hip thrust\",\"ion drive\"],[\"Simmonics Bridge-Servo\",\"Whitney Simmons\",\"bridge\",\"electronics servo\"]],\r\n[\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"glutes\",[\"Robo-ertson Bridge\",\"Heather Robertson\",\"bridge\",\"robot\"],[\"Hearn-ess ExoHinge\",\"Katy Hearn\",\"hinge\",\"exosuit harness\"]],\r\n[\"03-pearl-orb-ring--circle_center\",\"glutes\",[\"Con-Terra Coil-Bridge\",\"Bret Contreras\",\"bridge\",\"planetary coil\"],[\"Cyb-Ho Holo-Bridge\",\"Cassey Ho\",\"bridge\",\"cyborg hologram\"]],\r\n[\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"glutes\",[\"Chlo-Turbo Thrust\",\"Chloe Ting\",\"thrust\",\"turbine\"]],\r\n[\"10-petal-wisp--fantasy_creature_3d_model4\",\"yoga\",[\"Adriene A-Sana Mk II\",\"Adriene Mishler\",\"asana\",\"model revision\"]],\r\n[\"19-genie-multi--fantasy_creature_3d_model\",\"yoga\",[\"Mat-Gregor Matrix\",\"Kino MacGregor\",\"mat\",\"simulation matrix\"]],\r\n[\"10-petal-wisp--ghost_character_3d_model\",\"yoga\",[\"Warri0r-ner Drive\",\"Dylan Werner\",\"warrior pose\",\"motor designation\"],[\"Kathryn Bend-1g\",\"Kathryn Budig\",\"bend\",\"robot\"]],\r\n[\"08-shard-asym--stylized_action_figure_3d_model\",\"yoga\",[\"Yee-lastic Flow\",\"Rodney Yee\",\"yoga flow\",\"elastic mechanics\"]],\r\n[\"22-curve--stylized_alien_3d_model\",\"yoga\",[\"Strom Flex Current\",\"Max Strom\",\"stretch\",\"electric current\"]],\r\n[\"10-petal-wisp--stylized_creature_3d_model\",\"yoga\",[\"Iyeng-Arc Inversion\",\"B. K. S. Iyengar\",\"inversion\",\"powered arc\"]],\r\n[\"19-genie-multi--stylized_octopus_3d_model\",\"yoga\",[\"Yee Gyro-Yogi\",\"Rodney Yee\",\"yoga\",\"gyroscope\"]],\r\n[\"01-seed-pearo--cute_alien_figure_3d_model\",\"yoga\",[\"Wern-Axis Flow\",\"Dylan Werner\",\"flow\",\"robotic axis\"],[\"Mat-Gregor Drive\",\"Kino MacGregor\",\"mat practice\",\"motor drive\"]],\r\n[\"01-seed-pearo--white_3d_character_model\",\"yoga\",[\"Budig-ital Balance\",\"Kathryn Budig\",\"balance\",\"digital\"],[\"Corn Kernel Cobra\",\"Seane Corn\",\"cobra pose\",\"computing kernel\"]],\r\n[\"09-monolith-tanka--boxy_humanoid_3d_model\",\"martial-arts\",[\"Bruce L3300\",\"Bruce Lee\",\"kick\",\"alphanumeric call sign\"],[\"GSP-Drive\",\"Georges St-Pierre\",\"spar\",\"motor drive\"]],\r\n[\"11-anvil-cask--clay-style_robot_3d_model\",\"martial-arts\",[\"Anderson Sil-VOLT\",\"Anderson Silva\",\"strike\",\"voltage\"],[\"r0nda Rous-Bot\",\"Ronda Rousey\",\"armbar\",\"robot\"]],\r\n[\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"martial-arts\",[\"C0N0R McG-Gear\",\"Conor McGregor\",\"kick\",\"gear transmission\"],[\"Nor-Rotor Punch\",\"Chuck Norris\",\"punch\",\"rotor\"]],\r\n[\"14-chisel-spire--cone_head_3d_model\",\"martial-arts\",[\"LiDAR Jett-Jab\",\"Jet Li\",\"jab\",\"laser ranging\"],[\"Jaa-Bot Jab\",\"Tony Jaa\",\"jab\",\"robot\"]],\r\n[\"08-shard-asym--fantasy_creature_3d_model2\",\"martial-arts\",[\"Waters0nic\",\"Michelle Waterson\",\"kick\",\"sonic drive\"],[\"Amanda Nu-NEURAL\",\"Amanda Nunes\",\"strike\",\"neural implant\"]],\r\n[\"09-monolith-tanka--mini_robot_3d_model\",\"martial-arts\",[\"Machi-Droid-a\",\"Lyoto Machida\",\"chop\",\"android\"],[\"Bas Rut-TORque\",\"Bas Rutten\",\"kick\",\"motor torque\"]],\r\n[\"09-monolith-tanka--robot_3d_model3\",\"martial-arts\",[\"Israel Ade-SYNC-ya\",\"Israel Adesanya\",\"strike\",\"synchronization\"],[\"Shev-Servo Sweep\",\"Valentina Shevchenko\",\"sweep\",\"servo\"]],\r\n[\"13-fan-split--stylized_toy_3d_model\",\"martial-arts\",[\"BlankSaber Kick\",\"Billy Blanks\",\"kick\",\"energy saber\"],[\"Wilsonic Kick\",\"Don Wilson\",\"kick\",\"sonic drive\"]],\r\n[\"09-monolith-tanka--robot_3d_model2\",\"martial-arts\",[\"Rous-Torque Throw\",\"Ronda Rousey\",\"throw\",\"motor torque\"],[\"Jackie Chain-Kick\",\"Jackie Chan\",\"kick\",\"chain drive\"]],\r\n[\"07-bulb-sphereling--humanoid_robot_3d_model\",\"cardio\",[\"B0LT Sprint-Drive\",\"Usain Bolt\",\"sprint\",\"electrical bolt drive\"]],\r\n[\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"cardio\",[\"Fon-Dynamo Footwork\",\"Jane Fonda\",\"footwork\",\"dynamo\"],[\"Simmonics Sweatstream\",\"Richard Simmons\",\"aerobic sweat\",\"electronics stream\"]],\r\n[\"06-ridge-triad--robot_3d_model1\",\"cardio\",[\"M0 Farah Fastlane\",\"Mo Farah\",\"fast run\",\"electric capacitance\"]],\r\n[\"03-pearl-orb-ring--robot_3d_model\",\"cardio\",[\"Feli-X Footdrive\",\"Allyson Felix\",\"footwork\",\"X-model drive\"],[\"Rad-Clutch Run\",\"Paula Radcliffe\",\"run\",\"motor clutch\"]],\r\n[\"20-lume--robotic_figure_3d_model\",\"cardio\",[\"Pre-Font-AI-ne Pace\",\"Steve Prefontaine\",\"pace\",\"AI running model\"],[\"Ohm-wens Run\",\"Jesse Owens\",\"run\",\"electrical resistance\"]],\r\n[\"07-bulb-sphereling--cute_robot_3d_model\",\"cardio\",[\"Billy Blanx Beam\",\"Billy Blanks\",\"boot camp\",\"waveform\"],[\"GebreShell Runloop\",\"Haile Gebrselassie\",\"run\",\"software shell loop\"]],\r\n[\"01-seed-pearo--blank_toy_figure_3d_model\",\"cardio\",[\"Phelpeller Pulse\",\"Michael Phelps\",\"cardio pulse\",\"propeller\"],[\"Fon-Droid Footwork\",\"Jane Fonda\",\"footwork\",\"android\"]],\r\n[\"18-quad-all--dragon_creature_3d_model\",\"meditation\",[\"H0F-Lung\",\"Wim Hof\",\"breathing\",\"oxygen-code lung\"]],\r\n[\"18-quad-all--fantasy_creature_3d_model1\",\"meditation\",[\"Max Ohm-Strom Breath\",\"Max Strom\",\"breathing\",\"electrical resistance\"],[\"Yee-uro Zen\",\"Rodney Yee\",\"mindful movement\",\"neural interface\"]],\r\n[\"18-quad-all--fantasy_creature_3d_model3\",\"meditation\",[\"Shiva Rea-spatory\",\"Shiva Rea\",\"respiration\",\"airlock\"]],\r\n[\"18-quad-all--four-legged_robot_3d_model\",\"meditation\",[\"Tara-5tile5-Standby\",\"Tara Stiles\",\"breathing\",\"computer standby\"]],\r\n[\"18-quad-all--quadruped_robot_3d_model1\",\"meditation\",[\"Werner Wind0wn\",\"Dylan Werner\",\"wind-down practice\",\"alphanumeric standby mode\"]],\r\n[\"18-quad-all--quadruped_robot_3d_model\",\"meditation\",[\"Yee Holo-Om\",\"Rodney Yee\",\"mindful movement\",\"hologram\"]],\r\n[\"18-quad-all--robotic_dog_3d_model\",\"meditation\",[\"Budig-ital Breath\",\"Kathryn Budig\",\"breathing\",\"digital\"]],\r\n[\"12-seedpod-snailslug--stylized_slug_3d_model\",\"meditation\",[\"H0F Hold-Circuit\",\"Wim Hof\",\"breath hold\",\"circuit and model code\"],[\"Ohm-Strom Still\",\"Max Strom\",\"stillness\",\"electrical resistance\"]],\r\n[\"12-seedpod-snailslug--stylized_worm_3d_model\",\"meditation\",[\"Pilat-OS Calm-Circuit\",\"Joseph Pilates\",\"mindful breathing\",\"operating system circuit\"]],\r\n[\"14-chisel-spire--low_poly_robot_3d_model\",\"meditation\",[\"Rea-Spire Relay\",\"Shiva Rea\",\"respiration\",\"air relay\"]],\r\n[\"01-seed-pearo--3d_character_model\",\"meditation\",[\"McKe-O2 Nose-Cone\",\"Patrick McKeown\",\"nasal breathing\",\"rocket nose cone\"],[\"Vertu-al Vent\",\"Shona Vertue\",\"ventilation\",\"virtual reality\"]],\r\n[\"23-blob-texture-bodies--blob_creature_3d_model\",\"meditation\",[\"Blob 1 · H0F Hold-Drive\",\"Wim Hof\",\"breath hold\",\"air drive and model code\"]],\r\n[\"01-seed-pearo--blank_humanoid_figure_3d_model\",\"meditation\",[\"Corn Kernel Om\",\"Seane Corn\",\"meditation\",\"computing kernel\"],[\"Kino-Kinetic Breath\",\"Kino MacGregor\",\"breathing\",\"kinematics\"]],\r\n];\r\nconst META=Object.freeze(Object.fromEntries(ROWS.map(([path,track,...candidates])=>[path==='myr5'?path:`roster/${path}`,Object.freeze({track,names:Object.freeze(candidates.map(([name,person,workout,scifi])=>Object.freeze({name,person,workout,scifi})))})])));\r\nexport const COACH_NAME_META=META;\r\n/** One or two user-selected names by stable coach id. */\r\nexport const COACH_NAMES=Object.freeze(Object.fromEntries(Object.entries(META).map(([id,m])=>[id,Object.freeze(m.names.map(n=>n.name))])));\r\nexport const normalizePun=text=>String(text).toLowerCase().normalize('NFD').replace(/[^a-z0-9]/g,'');\r\nfunction metaFor(coachId){const meta=META[coachId];if(!meta)throw RangeError(`No coach name for ${coachId}.`);return meta;}\r\n/** Primary name, or the second candidate if available. */\r\nexport const coachName=(coachId,alternative=false)=>{const names=metaFor(coachId).names;return names[alternative?1:0]?.name??names[0].name;};\r\n/** Toggle a pair; a single-name coach keeps its selected name. */\r\nexport function cycleCoachName(coachId,current){const names=metaFor(coachId).names.map(n=>n.name);return names.length===1?names[0]:current===names[0]?names[1]:names[0];}\r\n","coach-net.mjs":"// Never-throw network helper for coach paths: timeout, 5xx and offline all resolve to {ok:false,fallback}.\r\n// After a failure the net is treated as down for COOLDOWN ms (no 5 s waits on every cue); then the next call probes again.\r\nconst COOLDOWN=10000;let downUntil=0;\r\nif(typeof window!=='undefined')window.addEventListener('online',()=>{downUntil=0;});\r\nexport const coachOnline=(now=Date.now())=>now>=downUntil&&globalThis.navigator?.onLine!==false;\r\nexport const resetCoachNet=()=>{downUntil=0;};\r\nconst down=fallback=>({ok:false,data:fallback,fallback:true});\r\nexport async function coachFetch(url,opts={},{timeoutMs=8000,fallback=null,parse='json',local=false,retry=0,backoffMs=400,fetchImpl=globalThis.fetch}={}){\r\n if(local){ // downloaded packs answer from the Cache API even when the network is down\r\n  try{const hit=await globalThis.caches?.match(url);if(hit?.ok)return {ok:true,data:parse==='response'?hit:await hit[parse](),fallback:false,status:200};}catch{}\r\n }\r\n if(!coachOnline())return down(fallback);\r\n for(let attempt=0;;attempt++){\r\n  // Plain controller + timer: AbortSignal.any is missing before Safari 17.4 and would mute every older iPhone.\r\n  const ctl=new AbortController(),timer=setTimeout(()=>ctl.abort(),timeoutMs),relay=()=>ctl.abort();\r\n  opts.signal?.addEventListener('abort',relay,{once:true});\r\n  try{\r\n   const response=await fetchImpl(url,{...opts,signal:ctl.signal});\r\n   if(response.status<500){ // 4xx is an answer, not an outage\r\n    const data=!response.ok?fallback:parse==='response'?response:await response[parse]();\r\n    downUntil=0;return {ok:response.ok,data,fallback:!response.ok,status:response.status};\r\n   }\r\n  }catch(error){if(opts.signal?.aborted)return down(fallback);}\r\n  finally{clearTimeout(timer);opts.signal?.removeEventListener('abort',relay);}\r\n  if(attempt>=retry)break;\r\n  await new Promise(r=>setTimeout(r,backoffMs));\r\n }\r\n downUntil=Date.now()+COOLDOWN;return down(fallback);\r\n}\r\n","coach-overlay.mjs":"// Full-body coach in the room during a tracked set: off frame until the first rep (or held second) counts,\r\n// then it walks in and wanders beside, behind and in front of the user. Kick it and it spins off, then walks back.\r\nimport {CoachMotion,COACH} from './coach-hit.mjs';\r\nimport {coach3dEnabled,COACH_AURA_LEVEL,COACH_AURA_PLACE} from './coach3d-flag.mjs';\r\nimport {mapPointToOverlay} from './arcade/workout-effects-state.mjs';\r\nconst $=id=>document.getElementById(id);\r\n\r\n// D43.5: while a set is counting, the coach gives up frames before the counter does. window.myr5TestState.rate\r\n// is the tracking loop's own pose-update measurement (app.mjs) — reused here, not remeasured.\r\n// Speed fix (3 Oct): below minPoseHz the coach is OFF, not 10 fps: the box leaves layout (display:none) so the\r\n// creature viewer's IntersectionObserver stops its WebGL loop, and no coach-hit math or DOM writes run. Phones\r\n// measured 1.4–1.9 updates/s with the coach on; reps need ~4+/s. It comes back after recoverMs of fast tracking,\r\n// doubling each time it is cut again in the same set so a phone near the line doesn't flicker it on and off.\r\n// Its per-pose work runs in the next animation frame (latest pose only), never inside the tracker's detect call.\r\nconst COACH_CAP={minPoseHz:4,recoverMs:2000};\r\nconst COACH_GESTURE={spun:'laugh',held:'wiggle',swiping:'swipe',impressed:'agree',laughing:'laugh'};\r\n\r\n// Same mapping as every effect layer (one mapper in workout-effects-state.mjs); unknown points stay invisible at 0,0.\r\nexport function videoToScreen(point,{videoW,videoH,screenW,screenH,mirrored}){\r\n return mapPointToOverlay(point,{videoWidth:videoW,videoHeight:videoH,hostWidth:screenW,hostHeight:screenH,mirrored})??{x:0,y:0,visibility:0};\r\n}\r\n\r\nexport function mountCoachOverlay(){\r\n const aura=!coach3dEnabled(); // aura mode (default): the coach only appears at aura level 3 (coach3d-flag.mjs)\r\n let overlay=null,box=null,card=null,motion=null,tracking=false,boxH=0,gesturePhase=null,walking=false,begun=false,loading=null,lastState=null;\r\n let capped=false,aboveSince=null,caps=0,pending=null,scheduled=0,animation=0,lastAnimation=0,auraOn=false,auraTimer=0,auraRun=0;\r\n const allowed=()=>window.myr5CoachGate?.()!==false; // app.mjs: only circuit + dance workouts get the 3D coach\r\n async function ensureCard(){\r\n  card=document.querySelector('.myr5-companion-card');if(card)return;\r\n  loading??=(async()=>{\r\n   if(!document.querySelector('link[href=\"/creature/phone.css\"]')){const link=document.createElement('link');link.rel='stylesheet';link.href='/creature/phone.css';document.head.append(link);}\r\n   try{await import('/creature/assets/phone.js');}catch(error){console.warn('Coach card could not load',error);}\r\n   card=document.querySelector('.myr5-companion-card');\r\n  })();\r\n  await loading;\r\n }\r\n function ensureOverlay(){\r\n  overlay=$('coachOverlay');if(!overlay)return;\r\n  box=overlay.querySelector('.coach-overlay-box');\r\n  if(!box){box=document.createElement('div');box.className='coach-overlay-box';box.style.cssText='position:absolute;left:0;top:0;transform-origin:50% 50%';overlay.append(box);}\r\n }\r\n const walk=on=>{if(on!==walking){walking=on;window.myr5Creature?.walk?.(on);}};\r\n // Off frame the box leaves layout (display:none), not just visibility:hidden: only then does the coach\r\n // viewer's IntersectionObserver pause its WebGL loop, which otherwise competes with the CPU pose tracker\r\n // (main thread and its per-frame GPU readback) while the user is still setting their start.\r\n const shown=on=>{box.style.visibility=on?'visible':'hidden';box.style.display=on?'':'none';};\r\n async function enter(){\r\n  await ensureCard();if(!card||!tracking)return;\r\n  ensureOverlay();if(!box)return;if(card.parentElement!==box)box.append(card);\r\n  boxH=0;gesturePhase=null;lastState=null;capped=false;aboveSince=null;caps=0;window.myr5Creature?.setMaxFps?.(null);shown(false);\r\n  motion=new CoachMotion({aspect:innerWidth/innerHeight,now:performance.now(),play:!String(window.myr5Creature?.stats?.()?.recipe?.body||'').startsWith('roster/18-quad')});if(begun)motion.begin();\r\n  window.myr5Creature?.stage('overlay');\r\n }\r\n function leave(){\r\n  cancelAnimationFrame(scheduled);cancelAnimationFrame(animation);animation=0;scheduled=0;pending=null;motion=null;lastState=null;walk(false);window.myr5Creature?.face?.(0);window.myr5Creature?.setMaxFps?.(null);if(!card)return;\r\n  card.style.cssText='';if(box)box.style.visibility=box.style.display='';\r\n  // Aura mode: the rest scene shows the 64-bit sprite (pod.mjs), so the 3D card parks in the pod mount instead.\r\n  const toRest=document.body.dataset.screen==='rest'&&!aura,mount=toRest?$('restCoachMount'):$('coachMount');\r\n  if(mount&&card.parentElement!==mount)mount.append(card);\r\n  window.myr5Creature?.stage(toRest?'encounter':'pod');\r\n }\r\n function place(state){\r\n  if(!box)return;\r\n  const hidden=state.phase==='offstage'||state.phase==='away';\r\n  shown(!hidden);\r\n  walk(state.phase==='walking'||state.phase==='charge');window.myr5Creature?.face?.(state.yaw);\r\n  // One clip per phase entry; `wiggle` loops, so it is re-asked every frame (a no-op while it already plays).\r\n  const gesture=COACH_GESTURE[state.phase];\r\n  if(gesture&&(state.phase!==gesturePhase||state.phase==='held'))window.myr5Creature?.play(gesture);\r\n  gesturePhase=gesture?state.phase:null;\r\n  if(hidden||!state.baseHeight)return;\r\n  // The card is sized for the coach level with the user (a resize re-renders WebGL, so only on 15% changes);\r\n  // walking nearer or farther is a CSS scale about the card's centre, keeping the feet on the floor line.\r\n  const w=innerWidth,h=innerHeight,base=state.baseHeight*h;\r\n  if(!boxH||Math.abs(base-boxH)/boxH>.15){boxH=base;box.style.width=Math.max(1,base*COACH.boxWidth)+'px';box.style.height=Math.max(1,base)+'px';}\r\n  const k=state.height*h/boxH;\r\n  // held: dangles by the head, a small pendulum about the box top. fallen: toppled 90° away from the user about the\r\n  // feet (eased by a short transition, which also eases the get-up). Whole-card transforms, so every silhouette works.\r\n  const swing=state.phase==='held'?Math.sin(performance.now()/160)*.12:0;\r\n  const pivot=state.phase==='held'?'translate(0,-50%) ':state.phase==='fallen'?'translate(0,50%) ':'';\r\n  const unpivot=state.phase==='held'?' translate(0,50%)':state.phase==='fallen'?' translate(0,-50%)':'';\r\n  const tilt=state.rotation+swing+(state.phase==='fallen'?state.side*Math.PI/2:0);\r\n  box.style.transition=/^(fallen|impressed|laughing)$/.test(state.phase)?'transform .35s ease-in':'';\r\n  box.style.transform=`translate(${state.x*w-boxH*COACH.boxWidth/2}px,${state.feetY*h-(k+1)*boxH/2}px) scale(${k}) ${pivot}rotate(${tilt}rad)${unpivot}`;\r\n }\r\n function animate(now){\r\n  animation=0;if(!tracking||!motion||capped)return;\r\n  if(now-lastAnimation>=1000/30){const state=motion.tick(now);if(state){lastAnimation=now;lastState=state;place(state);}}\r\n  if(['spun','dropped','away'].includes(lastState?.phase))animation=requestAnimationFrame(animate);\r\n }\r\n function apply(state){lastState=state;place(state);if(!animation&&['spun','dropped','away'].includes(state.phase)&&!capped)animation=requestAnimationFrame(animate);}\r\n // Hysteresis: off the moment tracking is slow; back only after minPoseHz has held for recoverMs (doubled per cut).\r\n function updateCap(counting,now){\r\n  if(!counting){capped=false;aboveSince=null;return;}\r\n  const rate=window.myr5TestState?.rate;\r\n  if(Number.isFinite(rate)&&rate<COACH_CAP.minPoseHz){if(!capped)caps++;capped=true;aboveSince=null;}\r\n  else{aboveSince??=now;if(capped&&now-aboveSince>=COACH_CAP.recoverMs*2**Math.min(caps-1,3))capped=false;}\r\n }\r\n function flush(){\r\n  cancelAnimationFrame(scheduled);scheduled=0;\r\n  const detail=pending;pending=null;\r\n  if(!detail||!tracking||!motion||!box)return;\r\n  const {points,width,height,mirrored,now,counting}=detail;\r\n  if(counting&&!begun){begun=true;motion.begin();}\r\n  updateCap(counting,now);\r\n  if(capped){shown(false);walk(false);return;}\r\n  const screenPoints=points?points.map(p=>videoToScreen(p,{videoW:width,videoH:height,screenW:innerWidth,screenH:innerHeight,mirrored})):null;\r\n  apply(motion.update(screenPoints,now));\r\n }\r\n function onPose(event){pending=event.detail;scheduled||=requestAnimationFrame(flush);}\r\n window.addEventListener('myr5:pose',onPose);\r\n function setTracking(busy){if(busy===tracking)return;tracking=busy;if(busy){begun=false;if(!aura&&allowed())void enter();}else{auraOn=false;auraRun++;clearTimeout(auraTimer);leave();}}\r\n // Aura mode: he joins at COACH_AURA_LEVEL (small, bottom-right, facing us, a greeting then idle) and fades out when the\r\n // level drops, at rest, or at set end. Assets load on the first entrance, never before.\r\n async function auraEnter(){\r\n  if(!allowed())return;const run=++auraRun;auraOn=true;\r\n  await ensureCard();if(run!==auraRun||!auraOn||!tracking||!card)return;\r\n  ensureOverlay();if(!box)return;if(card.parentElement!==box)box.append(card);\r\n  clearTimeout(auraTimer);\r\n  const P=COACH_AURA_PLACE,h=Math.round(innerHeight*P.heightPct),w=Math.round(h*COACH.boxWidth);\r\n  box.style.transition='none';box.style.visibility='visible';box.style.display='';\r\n  box.style.cssText+=`;left:${Math.round(innerWidth*(1-P.rightPct)-w)}px;top:${Math.round(innerHeight*(1-P.bottomPct)-h)}px;width:${w}px;height:${h}px;transform-origin:50% 100%;transform:scale(.55);opacity:0`;\r\n  window.myr5Creature?.stage('overlay');window.myr5Creature?.walk?.(false);window.myr5Creature?.face?.(0);window.myr5Creature?.play('greet');\r\n  requestAnimationFrame(()=>requestAnimationFrame(()=>{if(run!==auraRun)return;box.style.transition=`opacity ${P.enterMs}ms ease-out,transform ${P.enterMs}ms cubic-bezier(.2,.9,.3,1.25)`;box.style.opacity='1';box.style.transform='scale(1)';}));\r\n }\r\n function auraLeave(){\r\n  auraOn=false;auraRun++;clearTimeout(auraTimer);if(!box||!card)return;\r\n  box.style.transition=`opacity ${COACH_AURA_PLACE.leaveMs}ms ease-in,transform ${COACH_AURA_PLACE.leaveMs}ms ease-in`;box.style.opacity='0';box.style.transform='scale(.55)';\r\n  auraTimer=setTimeout(()=>{if(!auraOn)leave();},COACH_AURA_PLACE.leaveMs);\r\n }\r\n window.addEventListener('myr5:aura-level',event=>{\r\n  if(!aura||!tracking||!allowed()||document.body.dataset.screen==='rest')return;const level=Number(event.detail?.level)||0;\r\n  if(level>=COACH_AURA_LEVEL&&!auraOn)void auraEnter();else if(level<COACH_AURA_LEVEL&&auraOn)auraLeave();\r\n });\r\n new MutationObserver(()=>setTracking(document.body.dataset.tracking==='true')).observe(document.body,{attributes:true,attributeFilter:['data-tracking']});\r\n // Preparation rest keeps data-tracking on (the camera set resumes after it) but moves the card to the rest scene.\r\n // Without this the overlay kept walking/yawing the coach there: sideways, walk cycle looping. Park it like a set end,\r\n // then take the card back when the set resumes (pod.mjs moves it into #coachOverlay before this event).\r\n window.addEventListener('myr5:preparation-rest-start',()=>{auraOn=false;auraRun++;clearTimeout(auraTimer);if(tracking)leave();});\r\n window.addEventListener('myr5:preparation-rest-end',()=>{if(tracking&&!aura&&allowed())void enter();});\r\n window.myr5CoachOverlay={\r\n  aura:()=>({mode:aura,on:auraOn,tracking,box:box?.style.cssText??null}),\r\n  start(){document.body.dataset.tracking='true';setTracking(true);},\r\n  begin(){begun=true;motion?.begin();},\r\n  pose(screenPoints){if(!motion||!box)return;apply(motion.update(screenPoints,performance.now()));},\r\n  stop(){document.body.dataset.tracking='false';setTracking(false);},\r\n  flush,\r\n  state:()=>lastState\r\n };\r\n}\r\n","coach-path-picker.css":".coach-path-picker{box-sizing:border-box;width:min(780px,calc(100vw - 20px));max-width:780px;max-height:calc(100dvh - 20px);padding:0;border:2px solid #d98245;border-radius:18px;background:#150e12;color:#ffe0b5;box-shadow:0 0 0 5px #392128,0 26px 85px #000e,inset 0 0 28px #ffad4a1c;font:14px/1.35 ui-monospace,SFMono-Regular,Consolas,monospace}\r\n.coach-path-picker::backdrop{background:#090411df;backdrop-filter:blur(5px)}\r\n.coach-path-picker *{box-sizing:border-box}.coach-path-picker [hidden]{display:none!important}.coach-path-picker button{font:inherit;cursor:pointer}.coach-path-picker :focus-visible{outline:3px solid #ffe6ac;outline-offset:3px}\r\n.cpp-shell{display:grid;grid-template-rows:auto minmax(0,1fr) auto;max-height:calc(100dvh - 24px);overflow:hidden}.cpp-head,.cpp-foot{display:flex;align-items:center;gap:12px;padding:12px 18px;border-color:#914f30;background:linear-gradient(110deg,#40221b,#21131b 70%)}.cpp-head{justify-content:space-between;border-bottom:2px solid #914f30}.cpp-kicker{color:#f5a65f;font-size:11px;font-weight:800;letter-spacing:.18em}.cpp-close{display:grid;place-items:center;width:42px;height:42px;border:1px solid #9a573b;border-radius:8px;background:#2a1918;color:#ffd1a0;font-size:25px!important}.cpp-screen{position:relative;overflow-y:auto;padding:22px;background:repeating-linear-gradient(0deg,#0000 0 3px,#0003 3px 4px),radial-gradient(ellipse at 50% 0,#5f332f7d,transparent 70%)}.cpp-screen:after{content:'';position:sticky;display:block;bottom:0;height:0;pointer-events:none;box-shadow:0 0 44px 27px #130a0d}.cpp-title{display:flex;align-items:center;gap:17px;margin-bottom:22px}.cpp-signal{display:grid;place-items:center;flex:none;width:62px;height:62px;border:2px solid #e68e55;border-radius:16px;color:#ffd097;background:#46281c;box-shadow:inset 0 0 17px #f3ad5550,0 0 18px #fd934042;font-size:36px;text-shadow:0 0 12px #ff9a36}.cpp-title h2{margin:0 0 3px;color:#ffe6b8;font-size:clamp(22px,5vw,34px);letter-spacing:.04em;line-height:1.1}.cpp-title p{margin:0;color:#d6a889;font-size:13px}.cpp-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.cpp-path{display:flex;align-items:center;gap:12px;min-height:86px;padding:11px 13px;border:2px solid #76543e;border-radius:11px;background:linear-gradient(135deg,#3b2424,#20151a);color:#ffe3bb;text-align:left;box-shadow:inset 0 1px #ffd0a328,0 4px 0 #0f090b;transition:transform .14s ease,border-color .14s ease,background .14s ease}.cpp-path:not(:disabled):hover{transform:translateY(-2px) rotate(-.3deg);border-color:#f4aa6e;background:#51302a}.cpp-path[aria-pressed=true]{border-color:#ffb264;background:linear-gradient(135deg,#6b391f,#31201b);box-shadow:inset 0 0 18px #ffa6403b,0 0 14px #ffa64036,0 4px 0 #150a08}.cpp-path:disabled{cursor:default}.cpp-path-icon{display:grid;place-items:center;flex:none;width:48px;height:48px;border:1px solid #aa7349;border-radius:10px;background:#20120e;color:#ffb973;font-size:27px}.cpp-path-copy{display:grid;gap:3px;min-width:0}.cpp-path-copy strong{font-size:14px;line-height:1.2}.cpp-path-copy small{color:#c39476;font-size:11px}.cpp-path-check{margin-left:auto;align-self:flex-start;color:#ffcc8a;font-size:22px}.cpp-status{min-height:18px;margin:14px 0 0;color:#ffbb85;font-size:12px}.cpp-foot{border-top:2px solid #914f30}.cpp-count{margin-right:auto;color:#ffbe79;font-size:12px;letter-spacing:.08em}.cpp-later,.cpp-confirm{min-height:42px;padding:7px 13px;border:1px solid #bc7650;border-radius:7px;background:#30201f;color:#ffdaad}.cpp-confirm{border-color:#ffbb6c;background:#a64b1d;color:#fff1d7;font-weight:800!important}.cpp-confirm:disabled{opacity:.48;cursor:default}.cpp-confirm:not(:disabled):hover{background:#d26928}.cpp-later:hover,.cpp-close:hover{background:#4a2b28}\r\n@media(max-width:570px){.cpp-screen{padding:17px 12px}.cpp-grid{gap:9px}.cpp-path{gap:8px;min-height:92px;padding:9px}.cpp-path-icon{width:37px;height:37px;font-size:22px}.cpp-path-copy strong{font-size:12px}.cpp-path-copy small{font-size:10px}.cpp-path-check{font-size:17px}.cpp-foot{gap:8px;padding:10px}.cpp-count{font-size:10px}.cpp-later,.cpp-confirm{padding:6px 8px;font-size:11px!important}}\r\n@media(max-width:380px){.cpp-grid{grid-template-columns:1fr}.cpp-path{min-height:61px}.cpp-title{gap:10px}.cpp-signal{width:48px;height:48px;font-size:25px}}\r\n@media(prefers-reduced-motion:reduce){.cpp-path{transition:none}.cpp-path:hover{transform:none}}\r\n/* Symbols replace path words on screen (Ian 16 Oct); the name stays for screen readers. */\r\n.cpp-path .cpp-path-copy strong{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\r\n","coach-path-picker.mjs":"import {CHOOSABLE_TRACKS,chooseWorkoutPaths,readSelectedTracks} from './chosen-styles.mjs';\r\nimport {PATH_SYMBOLS} from './coach-path-symbols.mjs';\r\n\r\nconst COPY={\r\n chest:['Chest','🏋️','Press strength'],quads:['Quads','🦵','Leg drive'],glutes:['Glutes','↗','Hip power'],\r\n 'arms-shoulders':['Arms & shoulders','💪','Upper strength'],yoga:['Yoga','🧘','Balance & mobility'],\r\n 'martial-arts':['Martial arts','🥋','Combat movement'],cardio:['Cardio','⚡','Heart & stamina'],\r\n};\r\nconst owner=account=>account?.user?.id&&Number.isSafeInteger(account.dataEpoch)?`${account.user.id}:${account.dataEpoch}`:null;\r\nconst busyWorkout=()=>document.body.dataset.cameraWorkout==='true'||document.body.dataset.tracking==='true'||document.body.dataset.screen==='rest'||!['idle','error',undefined].includes(window.myr5TestState?.phase);\r\nconst style=()=>{if(document.getElementById('coachPathPickerStyle'))return;const link=document.createElement('link');link.id='coachPathPickerStyle';link.rel='stylesheet';link.href='/coach-path-picker.css';document.head.append(link);};\r\n\r\n/** One visit per account generation until choice. A manual route can always reopen it. */\r\nexport function mountCoachPathPicker({getAccount,savePaths,onChosen}={}){\r\n if(typeof getAccount!=='function'||typeof savePaths!=='function')throw TypeError('Coach path picker needs account and save handlers.');\r\n style();\r\n const dialog=document.createElement('dialog');dialog.id='coachPathPicker';dialog.className='coach-path-picker';dialog.setAttribute('aria-labelledby','cppTitle');\r\n dialog.innerHTML=`<div class=\"cpp-shell\"><header class=\"cpp-head\"><span class=\"cpp-kicker\">COACH // PATH SELECT</span><button class=\"cpp-close\" type=\"button\" aria-label=\"Close path picker\">×</button></header><div class=\"cpp-screen\"><div class=\"cpp-title\"><span class=\"cpp-signal\" aria-hidden=\"true\">◈</span><div><h2 id=\"cppTitle\">Choose two paths</h2><p id=\"cppHint\">Pick two. Meet their first coaches.</p></div></div><div class=\"cpp-grid\" role=\"group\" aria-label=\"Workout paths\"></div><p class=\"cpp-status\" role=\"status\" aria-live=\"polite\"></p></div><footer class=\"cpp-foot\"><span class=\"cpp-count\" aria-live=\"polite\">0 / 2 selected</span><button class=\"cpp-later\" type=\"button\">Later</button><button class=\"cpp-confirm\" type=\"button\" disabled>Start these paths <span aria-hidden=\"true\">→</span></button></footer></div>`;\r\n document.body.append(dialog);\r\n const grid=dialog.querySelector('.cpp-grid'),count=dialog.querySelector('.cpp-count'),confirm=dialog.querySelector('.cpp-confirm'),status=dialog.querySelector('.cpp-status'),later=dialog.querySelector('.cpp-later');\r\n const selection=new Set(),attempted=new Set();let activeOwner=null,saving=false,disposed=false,pending=0,hiddenSinceOpen=false;\r\n for(const id of CHOOSABLE_TRACKS){const [name,icon,detail]=COPY[id]??[id,'◆','Workout path'];const button=document.createElement('button');button.type='button';button.className='cpp-path';button.dataset.path=id;button.setAttribute('aria-pressed','false');button.innerHTML=`<span class=\"cpp-path-icon\" aria-hidden=\"true\"></span><span class=\"cpp-path-copy\"><strong></strong><small></small></span><span class=\"cpp-path-check\" aria-hidden=\"true\">○</span>`;button.querySelector('.cpp-path-icon').textContent=PATH_SYMBOLS[id]||icon;button.querySelector('strong').textContent=name;button.querySelector('small').textContent=detail;button.addEventListener('click',()=>toggle(id));grid.append(button);}\r\n function paint(){const chosen=readSelectedTracks({account:getAccount()});const locked=chosen.length===2;for(const button of grid.children){const picked=selection.has(button.dataset.path);button.setAttribute('aria-pressed',String(picked));button.querySelector('.cpp-path-check').textContent=picked?'✓':'○';button.disabled=saving||locked;}\r\n  count.textContent=locked?'Paths locked in':`${selection.size} / 2 selected`;confirm.hidden=false;confirm.disabled=saving||(!locked&&selection.size!==2);confirm.textContent=locked?'Proceed →':'Save and proceed →';later.textContent=locked?'Done':'Later';dialog.querySelector('#cppTitle').textContent=locked?'Your two paths':'Choose two paths';dialog.querySelector('#cppHint').textContent=locked?'Your first coaches are ready.':'Pick two. Meet their first coaches.';}\r\n function toggle(id){if(saving||readSelectedTracks({account:getAccount()}).length)return;status.textContent='';if(selection.has(id))selection.delete(id);else if(selection.size<2)selection.add(id);else{status.textContent='Two paths selected. Deselect one to switch.';return;}paint();}\r\n function canOpen(account){return !!owner(account)&&!!account.onboarding&&!account.stale&&!!window.coachPlan&&!document.hidden&&!busyWorkout()&&!document.getElementById('coachSetupGate')?.open&&!document.querySelector('dialog[open]');}\r\n function open({manual=false}={}){const account=getAccount(),key=owner(account);if(disposed||!key||!account.onboarding||account.stale||!window.coachPlan||document.hidden||busyWorkout()||document.getElementById('coachSetupGate')?.open)return null;if(dialog.open)return dialog;if(!manual&&(!canOpen(account)||attempted.has(key)||location.hash&&location.hash!=='#paths'))return null;if(document.querySelector('dialog[open]'))return null;\r\n  activeOwner=key;selection.clear();for(const id of readSelectedTracks({account}))selection.add(id);status.textContent='';hiddenSinceOpen=false;paint();dialog.showModal();if(!manual)attempted.add(key);grid.querySelector('button:not(:disabled)')?.focus();return dialog;}\r\n function schedule(){if(pending||disposed||document.hidden)return;pending=setTimeout(()=>{pending=0;const account=getAccount(),key=owner(account);if(!key||attempted.has(key)||readSelectedTracks({account}).length||!canOpen(account)||location.hash&&location.hash!=='#paths')return;if(window.myr5Routes?.ROUTES?.paths){const opened=window.myr5Routes.go('paths');Promise.resolve(opened).then(dialog=>{if(dialog)attempted.add(key);}).catch(()=>{});}else open();},250);}\r\n function close(){if(dialog.open)dialog.close();}\r\n async function commit(){const account=getAccount(),key=owner(account),paths=[...selection];if(saving||!key||key!==activeOwner||paths.length!==2||readSelectedTracks({account}).length)return;saving=true;paint();status.textContent='Saving your paths…';try{const result=await savePaths(paths,account);if(owner(getAccount())!==key)throw Error('Account changed. Open paths again.');if(!Array.isArray(result?.paths)||result.paths.length!==2||!paths.every(id=>result.paths.includes(id)))throw Error('Path choice could not be verified.');chooseWorkoutPaths(result.paths,{account});status.textContent='Paths ready.';paint();onChosen?.(result.paths,account);close();}catch(error){status.textContent=error?.message||'Could not save paths. Try again.';}finally{saving=false;if(dialog.open)paint();}}\r\n confirm.addEventListener('click',()=>{if(readSelectedTracks({account:getAccount()}).length===2){close();return;}void commit();});later.addEventListener('click',close);dialog.querySelector('.cpp-close').addEventListener('click',close);\r\n dialog.addEventListener('cancel',event=>{if(saving)event.preventDefault();});\r\n dialog.addEventListener('keydown',event=>{if(!['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(event.key)||!event.target.closest('.cpp-path'))return;const buttons=[...grid.querySelectorAll('button:not(:disabled)')],at=buttons.indexOf(event.target);if(at<0)return;event.preventDefault();const delta=event.key==='ArrowLeft'||event.key==='ArrowUp'?-1:1;buttons[(at+delta+buttons.length)%buttons.length].focus();});\r\n const ready=()=>schedule(),cleared=()=>{activeOwner=null;selection.clear();close();},visible=()=>{if(document.hidden){if(dialog.open){hiddenSinceOpen=true;close();}if(pending){clearTimeout(pending);pending=0;}}else if(!hiddenSinceOpen)schedule();};\r\n window.addEventListener('myr5:coach-plan',ready);window.addEventListener('myr5:account-ready',ready);window.addEventListener('myr5:account-cleared',cleared);document.addEventListener('visibilitychange',visible);document.addEventListener('close',event=>{if(event.target!==dialog)schedule();},true);\r\n const observer=new MutationObserver(()=>schedule());observer.observe(document.body,{attributes:true,attributeFilter:['data-tracking','data-camera-workout','data-screen']});\r\n window.addEventListener('pagehide',()=>{disposed=true;if(pending)clearTimeout(pending);observer.disconnect();close();},{once:true});\r\n window.myr5Menus={...window.myr5Menus,paths:()=>open({manual:true})};\r\n schedule();return {dialog,open,dispose(){disposed=true;if(pending)clearTimeout(pending);observer.disconnect();close();dialog.remove();}};\r\n}\r\n","coach-path-symbols.mjs":"// One symbol per workout type. Coach path ids and focus-group ids that are the same type share a symbol.\r\nexport const PATH_SYMBOLS=Object.freeze({\r\n chest:'\\u{1f3cb}️',quads:'\\u{1f9b5}',legs:'\\u{1f9b5}',glutes:'\\u{1f351}',hips:'\\u{1f351}',arms:'\\u{1f4aa}',\r\n 'arms-shoulders':'\\u{1f938}',shoulders:'\\u{1f938}',core:'\\u{1f3af}',balance:'⚖️',yoga:'\\u{1f9d8}',\r\n 'martial-arts':'\\u{1f94b}',stances:'\\u{1f94b}',boxing:'\\u{1f94a}',cardio:'⚡',meditation:'\\u{1f319}',original:'◈'\r\n});\r\n","coach-presentation.mjs":"import {COACH_NAME_META} from './coach-names.mjs';\r\nimport {PATH_SYMBOLS} from './coach-path-symbols.mjs';\r\nexport function coachPresentation(id){\r\n const track=COACH_NAME_META[id]?.track||'original';\r\n return {track,symbol:PATH_SYMBOLS[track]||PATH_SYMBOLS.original};\r\n}\r\n","coach-preview-space.mjs":"import {TIERS,makeSky,makeStars,buildTerrain,glowTexture} from './drop-pod-opening.mjs';\r\n\r\n// Borrow the supply-drop landscape without starting its opening, lights or particles.\r\n// The coach keeps its existing pod floor and lighting; this is its distant view only.\r\nexport function createCoachPreviewSpace(THREE,{pixelRatio=1}={}){\r\n const tier=TIERS.rare,group=new THREE.Group();group.name='coach-preview-space';\r\n const background=makeSky(THREE,tier),fog=new THREE.Fog(0x1a1228,28,95);\r\n const terrain=buildTerrain(THREE,tier);terrain.group.name='supply-drop-ground';\r\n for(const effect of [terrain.mound,terrain.scorch]){\r\n  terrain.group.remove(effect);effect.geometry.dispose();effect.material.map?.dispose();effect.material.dispose();\r\n }\r\n group.add(terrain.group);\r\n const sky=new THREE.Group();sky.name='supply-drop-sky';\r\n const uniforms={uTime:{value:0},uDpr:{value:pixelRatio}};\r\n for(const layer of makeStars(THREE,uniforms))sky.add(layer.pts);\r\n const nebulaTexture=glowTexture(THREE,'255,255,255',[[0,.55],[.35,.28],[.7,.08],[1,0]]);\r\n for(const [x,y,z,size,color,opacity] of [[-22,26,-85,110,tier.color,.5],[30,48,-95,95,0x5a3cff,.38],[0,60,-80,120,0x2fd0c0,.18]]){\r\n  const sprite=new THREE.Sprite(new THREE.SpriteMaterial({map:nebulaTexture,color,transparent:true,opacity,depthWrite:false,blending:THREE.AdditiveBlending,fog:false}));\r\n  sprite.position.set(x,y,z);sprite.scale.set(size,size*.7,1);sprite.renderOrder=-2;sky.add(sprite);\r\n }\r\n const moon=new THREE.Sprite(new THREE.SpriteMaterial({map:glowTexture(THREE,'200,220,255',[[0,1],[.18,.95],[.22,.35],[.6,.08],[1,0]]),transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,fog:false}));\r\n moon.name='supply-drop-moon';moon.position.set(-30,34,-85);moon.scale.setScalar(30);sky.add(moon);\r\n group.add(sky);\r\n // Front/Back and drag rotations still look out into the same space landscape.\r\n const reverseSky=sky.clone();reverseSky.name='supply-drop-sky-back';reverseSky.rotation.y=Math.PI;group.add(reverseSky);\r\n let disposed=false;\r\n function dispose(){\r\n  if(disposed)return;disposed=true;group.removeFromParent();\r\n  // The reverse sky shares resources with the front. Sprite geometry belongs to\r\n  // Three itself, so collect only this landscape's Mesh/Points geometry.\r\n  const geometries=new Set(),materials=new Set(),textures=new Set([background]);\r\n  group.traverse(object=>{\r\n   if(object.isMesh||object.isPoints)geometries.add(object.geometry);\r\n   if(object.material)for(const material of Array.isArray(object.material)?object.material:[object.material])materials.add(material);\r\n  });\r\n  for(const material of materials){\r\n   for(const value of Object.values(material))if(value?.isTexture)textures.add(value);\r\n   for(const uniform of Object.values(material.uniforms??{})){\r\n    const values=Array.isArray(uniform.value)?uniform.value:[uniform.value];\r\n    for(const value of values)if(value?.isTexture)textures.add(value);\r\n   }\r\n  }\r\n  geometries.forEach(geometry=>geometry.dispose());\r\n  materials.forEach(material=>material.dispose());\r\n  textures.forEach(texture=>texture.dispose());\r\n  group.clear();\r\n }\r\n return {group,background,fog,dispose};\r\n}\r\n","coach-profile.mjs":"import {renderPersonalTracker,updateTrackerProgress} from './personal-tracker.mjs';\r\nimport {officeEncouragement} from './office-domain.mjs?v=office-short-v1';\r\nimport {EXERCISES,dailyTargets} from './onboarding-domain.mjs?v=quick-install-v1';\r\nimport {openLocalCoach} from './local-coach-runtime.mjs';\r\nimport {setupAllowed} from './install-context.mjs';\r\nimport {signInPath} from './auth-paths.mjs';\r\nimport {readIncomingCoach} from './pending-coach.mjs';\r\nimport {nextChallenge} from './workout-route.mjs';\r\nlet setupRequired=true,leaving=false,worldWasOpen=false,browseWithoutSetup=false;\r\ntry{browseWithoutSetup=sessionStorage.getItem('myr5-browse-without-setup')==='1';}catch{}\r\nfunction showSetupGate(){setupRequired=true;if(leaving)return;if(browseWithoutSetup){if(gate.open)gate.close();gate.hidden=true;gate.style.display='none';return;}if(document.querySelector('#coachWorldPanel[open]')){if(gate.open)gate.close();gate.style.visibility='hidden';return;}gate.style.visibility='';if(!gate.open)gate.showModal();}\r\nfunction hideSetupGate(){setupRequired=false;gate.close();gate.hidden=true;gate.style.display='none';gate.style.visibility='';}\r\nconst gate=document.createElement('dialog');gate.id='coachSetupGate';gate.setAttribute('aria-label','Coach activation');gate.addEventListener('cancel',e=>e.preventDefault());gate.style.cssText='position:fixed;inset:0;margin:0;width:100vw;height:100dvh;max-width:none;max-height:none;border:0;z-index:2147483646;background:#17111ef5;display:grid;place-content:center;padding:28px;color:#f5e4ba;font:18px/1.6 Arial;text-align:center';\r\ngate.innerHTML='<h1>Connecting…</h1><p></p><a href=\"/pose.html?reconnect=1\" style=\"color:#b8e9cf;display:block;padding:12px\">Reload Coach</a><a id=\"gateSignIn\" href=\"#\" style=\"display:none;color:#b8e9cf;padding:12px\">Sign in</a>';document.body.append(gate);showSetupGate();\r\n// World is independently usable without a workout plan. Preserve activation when it closes.\r\nconst explore=document.createElement('button');explore.id='gateExplore';explore.textContent='Explore my base without signing in';explore.style.cssText='color:#b8e9cf;background:#20332c;border:1px solid #6c9481;border-radius:10px;padding:14px;font:inherit;cursor:pointer';gate.append(explore);\r\nexplore.onclick=()=>{browseWithoutSetup=true;try{sessionStorage.setItem('myr5-browse-without-setup','1');}catch{}hideSetupGate();location.hash='world';};\r\nfunction syncWorldGate(){\r\n const worldOpen=!!document.querySelector('#coachWorldPanel[open]');\r\n if(leaving||worldOpen===worldWasOpen)return;worldWasOpen=worldOpen;\r\n if(!setupRequired)return;\r\n if(worldOpen){if(gate.open)gate.close();gate.style.visibility='hidden';}\r\n else showSetupGate();\r\n}\r\nnew MutationObserver(syncWorldGate).observe(document.body,{subtree:true,attributes:true,attributeFilter:['open']});\r\n// React only to World transitions. Reopening a gate another lifecycle closed can\r\n// stall Chromium navigation while dialogs are being torn down.\r\nwindow.addEventListener('beforeunload',()=>{leaving=true;requestAnimationFrame(()=>{if(!document.hidden)leaving=false;});});\r\nwindow.addEventListener('pagehide',()=>{leaving=true;});\r\nwindow.addEventListener('pageshow',()=>{leaving=false;syncWorldGate();});\r\nlet applied='',restored=false,localPlan=null;\r\nwindow.coachPersonalCue=personalCue;\r\nwindow.addEventListener('myr5:account-progress',event=>updateTrackerProgress(event.detail));\r\nconst get=k=>{try{return localStorage.getItem(k);}catch{return null;}};\r\nfunction showIncomingCoach(){if(!readIncomingCoach())return;gate.querySelector('h1').textContent='Coach ready';gate.querySelector('p').textContent='Saved setup loaded.';gate.querySelector('a').textContent='Continue with my coach';gate.querySelector('a').href='/onboarding.html?from=install';}\r\nfunction installSteps(){const ua=navigator.userAgent,ios=/iPad|iPhone|iPod/.test(ua)||(navigator.platform==='MacIntel'&&navigator.maxTouchPoints>1),android=/Android/i.test(ua);return ios?'On iPhone: tap Share, then Add to Home Screen, then open the MYR5 icon.':android?'On Android: tap Install when Chrome offers it (or the ⋮ menu, then Install app), then open MYR5 from your home screen.':'On a computer: use the install icon beside the address bar (Chrome or Edge), then open MYR5 from your apps.';}\r\n// Not installed yet: say plainly what to do, per platform. Get started opens the install page, which shows the same steps instead of bouncing back here.\r\nfunction installCopy(signIn=false){gate.querySelector('h1').textContent='Install MYR5';gate.querySelector('p').textContent='Install MYR5, then open it from your home screen. '+installSteps()+(signIn?' Already have a coach? Sign in to reconnect it.':'');gate.querySelector('a').href='/install.html';}\r\nfunction beginSetup(){return setupAllowed()?'/onboarding.html?from=install':'/install.html';}\r\nexport function clearCoachAccount(){window.myr5AuthenticatedAccount=null;window.coachEntitlements=null;document.documentElement.dataset.publicState=localPlan?'unlocked':'locked';window.coachProgress=null;window.dispatchEvent(new Event('myr5:account-cleared'));window.coachPlan=localPlan;restored=false;applied='';if(localPlan){hideSetupGate();window.dispatchEvent(new Event('myr5:coach-plan'));return;}\r\n // No saved plan on this device: Coach cannot render without one, so the gate must keep blocking —\r\n // but it now says why and always offers a working Sign in (reconnect) alongside Get started (#10/#11).\r\n gate.hidden=false;gate.style.display='grid';showSetupGate();gate.querySelector('h1').textContent='Set up Coach';gate.querySelector('p').textContent='Sign in to reconnect your saved coach, or get started fresh — Coach needs a plan before it can open.';gate.querySelector('a').textContent='Get started';gate.querySelector('a').href=beginSetup();if(!setupAllowed())installCopy(true);const signIn=gate.querySelector('#gateSignIn');signIn.href=signInPath('/onboarding.html');signIn.style.display='';}\r\nexport async function applyLocalCoach(){\r\n let repository;try{repository=await openLocalCoach();const scope=repository.forOwner(repository.guestOwnerId),data=await scope.getIntake(),settings=await scope.getSettings();if(!data||!settings?.startDay)return null;const targets=dailyTargets(data.profile,settings.startDay),completedSets=(await scope.listWorkouts()).filter(row=>row.status==='completed').length;localPlan={data,startDay:settings.startDay,completedAt:null,revision:0,targets};window.coachPlan=localPlan;document.documentElement.dataset.publicState='unlocked';if(!localStorage.getItem('myr5-coach-owner')){for(const [key,value] of Object.entries(data.appearance||{})){if(!localStorage.getItem(key)){localStorage.setItem(key,JSON.stringify(value));window.dispatchEvent(new StorageEvent('storage',{key,newValue:JSON.stringify(value)}));}}}window.dispatchEvent(new Event('mominc-avatar-change'));hideSetupGate();let plate=document.getElementById('dailyCoachPlan');if(!plate){plate=document.createElement('section');plate.id='dailyCoachPlan';document.querySelector('.crew-footer').before(plate);}renderPersonalTracker(plate,data.profile,targets,EXERCISES,{completedSets});window.dispatchEvent(new Event('myr5:coach-plan'));return localPlan;}finally{repository?.close();}\r\n}\r\nexport function applyCoachAccount(account){\r\n window.coachEntitlements=account.entitlements;window.myr5AuthenticatedAccount=account;document.documentElement.dataset.publicState='unlocked';window.dispatchEvent(new CustomEvent('myr5:account-ready',{detail:account}));\r\n window.coachProgress=account.progress;\r\n const o=account.onboarding;\r\n // #11: signed in but setup isn't finished — say why it blocks (not just what to do next).\r\n if(!o){gate.hidden=false;gate.style.display='grid';showSetupGate();gate.querySelector('#gateSignIn').style.display='none';gate.querySelector('h1').textContent='Set up Coach';gate.querySelector('p').textContent='Coach needs your setup finished before it can open — choose games or quick setup.';gate.querySelector('a').href='/onboarding.html';gate.querySelector('a').textContent='Continue coach setup';gate.querySelector('a').href=beginSetup();if(setupAllowed())showIncomingCoach();else installCopy();return;}\r\n hideSetupGate();window.coachPlan=o;\r\n if(!restored){const owner=get('myr5-coach-owner'),baseline=get('myr5-synced-appearance'),keys=['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1','myr5-pod-power-v1','handborne-recipe-v4','mbs-dj-identity-v1'];const current=JSON.stringify(Object.fromEntries(keys.map(k=>[k,get(k)]).filter(([,v])=>v!=null)));const dirty=owner===account.user.id&&baseline&&baseline!==current;\r\n  if(!dirty){for(const key of keys){const v=account.profile[key];if(v!=null){localStorage.setItem(key,v);window.dispatchEvent(new StorageEvent('storage',{key,newValue:v}));}else if(owner&&owner!==account.user.id)localStorage.removeItem(key);}window.dispatchEvent(new Event('mominc-avatar-change'));localStorage.setItem('myr5-synced-appearance',JSON.stringify(Object.fromEntries(keys.map(k=>[k,get(k)]).filter(([,v])=>v!=null))));}localStorage.setItem('myr5-coach-owner',account.user.id);restored=true;\r\n }\r\n let plate=document.getElementById('dailyCoachPlan');if(!plate){plate=document.createElement('section');plate.id='dailyCoachPlan';document.querySelector('.crew-footer').before(plate);}\r\n const p=o.data.profile,t=o.targets;renderPersonalTracker(plate,p,t,EXERCISES,account.progress);\r\n let nutrition=document.getElementById('coachNutritionTargets');if(!nutrition){nutrition=document.createElement('p');nutrition.id='coachNutritionTargets';document.getElementById('mealForm').before(nutrition);}nutrition.textContent=t.proteinGrams?`Today: ${t.proteinGrams} g protein · ${t.waterOz} oz water`:'';\r\n const signature=o.revision+':'+t.date;if(applied!==signature){const first=!applied;applied=signature;const movement=document.getElementById('movement');if(first&&movement&&p.exercises.includes(p.exercises[0])){movement.value=nextChallenge(account.progress.exerciseRoute,p.exercises[0])?.mode||p.exercises[0];movement.dispatchEvent(new CustomEvent('change',{bubbles:true,detail:{automatic:true}}));}window.dispatchEvent(new Event('myr5:coach-plan'));const reminder=document.getElementById('reminderForm');if(first&&reminder){reminder.elements.time.value=p.trainingTime;reminder.elements.timezone.value=p.timezone;window.dispatchEvent(new CustomEvent('myr5:reminder-defaults',{detail:{tone:p.reminderTone,days:Number(p.reminderDays)}}));}}\r\n}\r\nexport function personalCue(text,key){const p=window.coachPlan?.data?.profile;if(!p)return text;if(p.guidance==='Quiet'&&['encouragement','time'].includes(key)&&!/^\\d/.test(text))return null;if(key!=='encouragement')return text;\r\n const lines={supportive:'You are making progress. One comfortable movement at a time.',direct:'Stay with the next movement. Keep control.',analytical:'Keep your movement consistent. Notice the control in each repetition.',playful:'One tiny quest at a time. You have this.',calm:'Breathe. Move at your own pace.',mom:'MOM Inc. has recorded your effort. Please proceed comfortably.'};\r\n const tips={'Controlled reps':'Move with control.','Gradual progression':'One small step today.','Technique first':'Make the next movement smooth.'};let result=(text.startsWith('Halfway')?'Halfway. ':'')+(officeEncouragement(window.coachPlan.data,window.coachPlan.targets.day)||lines[p.coach]);if(p.guidance==='Detailed')result+=' '+tips[p.trainingStyle];\r\n // Respect explicitly listed words/phrases the visitor does not want to hear.\r\n const blocked=window.coachPlan.data.answers?.djscratch?.q3||'';if(blocked.split(/[,;\\n]/).some(s=>s.trim().length>=3&&!/^none\\.?$/i.test(s.trim())&&result.toLowerCase().includes(s.trim().toLowerCase())))return null;return result;\r\n}\r\n","coach-voice-settings.mjs":"import {COACH_VOICE_VARIANTS,DEFAULT_COACH_VOICE} from './robot-audio.mjs';\r\n\r\nexport const COACH_VOICE_PREFERENCE='myr5-coach-voice-v1';\r\n\r\n// Standalone initializer so the app shell can mount this beside its existing\r\n// Test voice control without coupling preference UI to app.mjs.\r\nexport function mountCoachVoiceSettings({voice,settings=globalThis.document?.getElementById('settings'),testButton=globalThis.document?.getElementById('testVoice')}={}){\r\n if(!voice)throw new TypeError('A CoachVoice instance is required');\r\n if(!settings)return ()=>{};\r\n const group=[...settings.querySelectorAll('details.settings-group')].find(item=>item.querySelector('summary')?.textContent.trim().toLowerCase()==='voice');\r\n if(!group)return ()=>{};\r\n const row=group.querySelector('.voice-row')??group;\r\n let selected=DEFAULT_COACH_VOICE;\r\n try{const saved=globalThis.localStorage?.getItem(COACH_VOICE_PREFERENCE);if(COACH_VOICE_VARIANTS[saved])selected=saved;}catch{}\r\n voice.setVariant?.(selected);\r\n const wrap=document.createElement('label');wrap.className='coach-voice-choice';wrap.style.cssText='display:flex;flex:1 1 100%;flex-direction:column;gap:6px;font-size:11px;color:inherit';\r\n const label=document.createElement('span');label.textContent='Voice style';label.style.cssText='font-size:11px;color:inherit';\r\n const select=document.createElement('select');select.name='coachVoice';select.setAttribute('aria-label','Coach voice style');\r\n select.style.cssText='width:100%;font-size:12px;padding:9px';\r\n for(const [id,variant] of Object.entries(COACH_VOICE_VARIANTS)){\r\n  const option=document.createElement('option');option.value=id;option.textContent=variant.label;select.append(option);\r\n }\r\n select.value=selected;wrap.append(label,select);\r\n const hint=document.createElement('small');hint.className='coach-voice-note';hint.textContent='Choose a style, then tap Test voice.';hint.style.cssText='flex:1 1 100%;font-size:10px;color:inherit';\r\n row.append(wrap,hint);\r\n const change=()=>{\r\n  const next=COACH_VOICE_VARIANTS[select.value]?select.value:DEFAULT_COACH_VOICE;\r\n  voice.setVariant?.(next);\r\n  try{globalThis.localStorage?.setItem(COACH_VOICE_PREFERENCE,next);}catch{}\r\n };\r\n select.addEventListener('change',change);\r\n // Keep this explicit relationship visible to assistive technology. The app's\r\n // existing handler still owns the button action and chooses its test sentence.\r\n if(testButton)testButton.setAttribute('aria-describedby',hint.id||(hint.id='coach-voice-note'));\r\n return ()=>{select.removeEventListener('change',change);wrap.remove();hint.remove();};\r\n}\r\n","coach.mjs":"// Event-driven cues: no language model or microphone needed during exercise.\r\nimport {RobotAudio,COACH_VOICE_VARIANTS} from './robot-audio.mjs';\r\nimport {coachOnline} from './coach-net.mjs';\r\nfunction myr5VoiceState(state){if(typeof window!=='undefined')window.dispatchEvent(new CustomEvent('myr5:response',{detail:{state}}));}\r\nexport const INTRO = {\r\n squat:'Watch the squat. Start tall, lower, then return to your starting height. Keep your shoulders and hips in view. Feet can be outside the picture.',\r\n pushup:'Watch the push-up. Place your phone beside you so you can see it. Show one shoulder, elbow, hand and hip. Begin with your arm extended.',\r\n tree:'Watch tree pose. Raise one bent knee out to the side and find a comfortable balance. Show your hips and knees. I estimate the hold from your upper legs.',\r\n warrior:'Watch warrior two. Stretch your arms to either side and bend one knee in a wide stance. Keep your arms, hips and knees visible.',\r\n horse:'Watch horse stance. Take a comfortable wide stance and lower with your knees out. Keep your hips and knees visible. I estimate your hold.',\r\n boxing:'Watch air boxing. Raise your hands and move at your own pace. Keep your shoulders, elbows and hands visible. I time your round and estimate hand speed.',\r\n jogging:'Watch jogging in place. Alternate lifting your knees at your own pace. Keep both knees visible. Feet can be outside the picture.',\r\n jumping:'Watch the jump. Start standing still, then rise and return to your starting height. Keep shoulders and hips visible. I estimate jumps from body rise.'\r\n};\r\nexport class CueEvents {\r\n constructor(){this.reset();}\r\n reset(){this.previous=null;this.ready=false;this.lostAt=null;this.warned=false;this.marks=new Set();this.lastEncourage=0;this.nextSetup=null;}\r\n update(m,now){\r\n  const cues=[],p=this.previous;\r\n  const add=(text,key='guide')=>cues.push({text,key});\r\n  const ready=m.tracking&&(m.kind==='hold'?m.progress===1:['reps','jumps'].includes(m.kind)?m.calibrated:true);\r\n  if(m.complete){if(!p?.complete)add('Round complete. Well done.','complete');}\r\n  else {\r\n   if(ready&&!this.ready){add('Ready. Begin.','ready');this.ready=true;}\r\n   if(!this.ready){this.nextSetup??=now+6000;if(now>=this.nextSetup){add(m.message,'setup');this.nextSetup=now+8000;}}\r\n   if(this.ready&&!m.tracking){this.lostAt??=now;if(!this.warned&&now-this.lostAt>=2000){add(m.message,'tracking');this.warned=true;}}\r\n   else if(m.tracking){if(this.warned)add('I can see you again.','tracking');this.lostAt=null;this.warned=false;}\r\n   if(p&&m.count>p.count)add(String(m.count),'count');\r\n   if(m.kind==='hold'&&p){const milestone=Math.floor(m.hold/10)*10;if(m.remaining==null&&milestone>=10&&milestone>Math.floor(p.hold/10)*10)add(`${milestone} seconds.`, 'time');if(p.hold>1&&m.hold===0)add('Hold paused.','tracking');}\r\n   if(m.remaining!==null&&p?.remaining!==null&&p?.remaining!==undefined){\r\n    const crossed=[60,30,10,5,4,3,2,1].filter(n=>p.remaining>n&&m.remaining<=n&&!this.marks.has(n));\r\n    crossed.forEach(n=>this.marks.add(n));if(crossed.length){const n=crossed.at(-1);add(n>5?`${n} seconds left.`:String(n),'time');}\r\n   } else if(ready&&m.kind!=='hold'&&Math.floor(m.elapsed/30)>this.lastEncourage){this.lastEncourage=Math.floor(m.elapsed/30);add(`${this.lastEncourage*30} seconds. Keep your own pace.`, 'time');}\r\n  }\r\n  this.previous={...m};return cues;\r\n }\r\n}\r\nexport class CoachVoice {\r\n constructor(caption,onMode=()=>{}){this.caption=caption;this.onMode=onMode;this.enabled=true;this.queue=[];this.current=null;this.epoch=0;this.robot=new RobotAudio(onMode);this.available=!!(globalThis.AudioContext||globalThis.webkitAudioContext);}\r\n unlock(){return this.robot.unlock();}\r\n get variant(){return this.robot.variant;}\r\n setVariant(id){if(!COACH_VOICE_VARIANTS[id])return false;if(id!==this.variant&&(this.current||this.queue.length))this.cancel();this.robot.setVariant(id);return true;}\r\n setEnabled(enabled){this.enabled=enabled;if(!enabled)this.cancel();}\r\n cancel(){this.epoch++;clearTimeout(this.current?.timer);this.robot.stop();globalThis.speechSynthesis?.cancel();this.current?.resolve();this.current=null;this.queue.splice(0).forEach(item=>item.resolve());myr5VoiceState('idle');}\r\n say(text,{key='guide',interrupt=false}={}){\r\n  if(typeof window!=='undefined'&&window.coachPersonalCue){text=window.coachPersonalCue(text,key);if(text==null)return Promise.resolve();}\r\n  // Encouragement can follow a short count, but never queue behind instructions.\r\n  if(key==='encouragement'&&this.current&&this.current.key!=='count')return Promise.resolve();\r\n  if(interrupt||(key==='count'&&this.current?.key==='guide')||(key!=='encouragement'&&this.current?.key==='encouragement'))this.cancel();this.caption(text);\r\n  if(!this.enabled||!this.available)return Promise.resolve();\r\n  return new Promise(resolve=>{\r\n   // Do not build a backlog of old rep numbers or expired timer milestones.\r\n   const old=this.queue.findIndex(item=>item.key===key);if(old>=0)this.queue.splice(old,1)[0].resolve();\r\n   this.queue.push({text,key,resolve});this.pump();\r\n  });\r\n }\r\n pump(){\r\n  if(this.current||!this.queue.length)return;\r\n  const item=this.queue.shift(),epoch=this.epoch;this.current=item;\r\n  this.caption(item.text);myr5VoiceState('speaking');let finished=false;\r\n  const finish=()=>{clearTimeout(item.timer);if(finished||epoch!==this.epoch)return;finished=true;this.current=null;item.resolve();myr5VoiceState('idle');this.pump();};\r\n  const unavailable=()=>{if(epoch!==this.epoch)return;this.onMode(coachOnline()?'Voice unavailable · captions on':'Offline · captions on, voice returns when you reconnect');finish();};\r\n  item.timer=setTimeout(()=>{if(epoch===this.epoch){this.robot.stop();globalThis.speechSynthesis?.cancel();finish();}},Math.max(10000,5000+item.text.length*220));\r\n  if(this.robot.unlock())this.robot.play(item.text).then(played=>{if(epoch===this.epoch){if(played)finish();else unavailable();}},unavailable);\r\n  else unavailable();\r\n }\r\n}\r\n","coach3d-flag.mjs":"// Ian, 16 Oct: the 3D coach in camera workouts and the rest scene is on the back burner (\"throws off the vibes\").\r\n// Default: he only joins a workout once the exercise's aura reaches COACH_AURA_LEVEL (small, at the side, idle) and\r\n// leaves at rest or when the set ends. localStorage myr5Coach3d='1' forces the old always-on full coach (testing).\r\n// Until level 3 his assets are never loaded; the rest scene shows the 64-bit coach sprite instead.\r\nexport const COACH_AURA_LEVEL=3;\r\n// Aura-entrance placement (share of the screen): small, bottom-right, clear of the camera subject. Tune on a real phone.\r\nexport const COACH_AURA_PLACE={heightPct:.26,rightPct:.02,bottomPct:.2,enterMs:550,leaveMs:350};\r\nexport const COACH_3D_KEY='myr5Coach3d';\r\nexport function coach3dEnabled(){try{return localStorage.getItem(COACH_3D_KEY)==='1';}catch{return false;}}\r\n","combat-config.mjs":"// Rank 11b combat tuning. Every number the boss fight needs lives here so the\r\n// owner can retune it — including picking D20's 90s target over the shipped\r\n// 60s-per-rest default (3 x 60s = 180s) — by changing killTargetSeconds alone.\r\n// See plan/COMBAT-TUNING.md for the arithmetic this file produces.\r\nimport {dayAt} from './combat.mjs';\r\n\r\nexport const TAPS_PER_SECOND=2;          // D8/D20: assumed tap rate\r\nexport const KILL_TARGET_SECONDS=90;     // D20's target. Audit found the shipped rest default is 60s (user-selectable 30-120s), not the ~30s D20 assumed — owner has not picked yet. Set to REST_SECONDS*RESTS_PER_WORKOUT (180) to use the shipped default instead; the L5/L4 split below holds at either setting (see plan/COMBAT-TUNING.md).\r\nexport const REST_SECONDS=60;            // shipped default, pose.html #restDuration / pod/set-flow.mjs start()'s restSeconds=60 default (reports/audit-game.md \"Rest timer length between sets\")\r\nexport const RESTS_PER_WORKOUT=3;\r\n\r\n// Base tap damage by level (index = level-1). L1-L4 = level; L5 jumps to 8 (D33: L4 kit ~15% short of the L5 boss).\r\n// A linear level x N base can't reach D33: weapons+pet add the same to L4 and L5, so L4/L5 output never drops below\r\n// 4/5, and 4/5 x 1.1 (L5's 10% kill margin) leaves L4 at most 12% short even with no weapon, pet or special damage.\r\nexport const BASE_TAP_DAMAGE=[1,2,3,4,8];\r\nexport const WEAPON_1_LEVEL=1,WEAPON_1_BONUS=1; // D22: weapon 1 unlocks at L1\r\nexport const WEAPON_2_LEVEL=3,WEAPON_2_BONUS=2; // D22: weapon 2 unlocks at L3\r\nexport const PET_LEVEL=4,PET_DPS=5;             // D22: pet unlocks at L4 (aura at L5 is cosmetic only — D22/D7 — so it adds no combat stat)\r\nexport const SPECIAL_LEVEL=3;                   // D17: weapon specials unlock at L3; refused below it\r\n// The ONE special-damage knob: everything a day's specials deal together is capped at this share of\r\n// that level's boss max HP. With a special on every cooldown the L4 kit deals 19/s·T x (1 + 0.9 x 0.1) =\r\n// 20.71/s·T against the L5 boss's 0.9 x 27/s·T = 24.3/s·T: 14.8% short at any killTargetSeconds (D33).\r\n// Above ~0.139 the L4 shortfall drops under 12% (tests/combat-tuning.test.mjs).\r\nexport const SPECIAL_DAMAGE_FRACTION=0.1;\r\n\r\n// [minStreak,multiplier], highest matching breakpoint wins; below all of them the multiplier is 1.\r\nexport const STREAK_BREAKPOINTS=[[20,1.5],[10,1.25],[5,1.1]];\r\n\r\nexport const BOSS_HP_FACTOR=0.9;    // a level's boss HP = this fraction of that level's own kit output over KILL_TARGET_SECONDS — every level's own kit (taps only) clears it with 1/0.9 = 11.1% to spare, at any KILL_TARGET_SECONDS (plan/COMBAT-TUNING.md)\r\nexport const DAILY_CAP_FACTOR=5;    // daily cap = this many boss-kills worth of damage, so it never blocks the intended kill\r\n\r\nexport const BOSS_ATTACK_EVERY_HITS=6; // spectacle only (D7) — no gameplay listener consumes this\r\n\r\nexport function streakMultiplier(streak){\r\n const s=Number.isSafeInteger(streak)?streak:1;\r\n for(const [min,mult] of STREAK_BREAKPOINTS)if(s>=min)return mult;\r\n return 1;\r\n}\r\nexport function kitTapDamage(level){\r\n const l=Number.isSafeInteger(level)&&level>=1?level:1;\r\n return BASE_TAP_DAMAGE[Math.min(l,BASE_TAP_DAMAGE.length)-1]+(l>=WEAPON_1_LEVEL?WEAPON_1_BONUS:0)+(l>=WEAPON_2_LEVEL?WEAPON_2_BONUS:0);\r\n}\r\nexport function kitPetDps(level){\r\n const l=Number.isSafeInteger(level)&&level>=1?level:1;\r\n return l>=PET_LEVEL?PET_DPS:0;\r\n}\r\n// Per-tap damage for live play: kit strength x the streak curve. `combat` is\r\n// whatever shape combat.mjs's exported progress carries ({day,loginStreak,...})\r\n// — read here, never recomputed (combat.mjs owns loginStreak()).\r\nexport function tapDamage(level,combat,now=Date.now()){\r\n const fresh=combat?.day===dayAt(now)&&Number.isSafeInteger(combat.loginStreak)&&combat.loginStreak>=1;\r\n return kitTapDamage(level)*streakMultiplier(fresh?combat.loginStreak:1);\r\n}\r\n// Total damage a level's own kit can land in `seconds` of tapping, at streak 1\r\n// (the guaranteed floor with no login-streak bonus — a real streak only helps).\r\nexport function maxKitDamage(level,seconds=KILL_TARGET_SECONDS){\r\n return kitTapDamage(level)*TAPS_PER_SECOND*seconds+kitPetDps(level)*seconds;\r\n}\r\nexport function bossHp(level,seconds=KILL_TARGET_SECONDS){\r\n return Math.round(BOSS_HP_FACTOR*maxKitDamage(level,seconds));\r\n}\r\nexport function dailyCap(level,seconds=KILL_TARGET_SECONDS){\r\n return Math.round(DAILY_CAP_FACTOR*maxKitDamage(level,seconds));\r\n}\r\n// Total special damage allowed per boss (per day) at this level; whole numbers so the UI never shows float noise.\r\nexport function specialBudget(level,seconds=KILL_TARGET_SECONDS){\r\n return Math.floor(SPECIAL_DAMAGE_FRACTION*bossHp(level,seconds));\r\n}\r\n// D32 Food bonus: each Food level grants a bonus (battle-pass-rewards.mjs FOOD_BONUS) worth this\r\n// damage multiplier for one day. DEFAULT — the owner can change it.\r\nexport const FOOD_BONUS_DAMAGE_MULTIPLIER=1.1;\r\n","combat.mjs":"import {evaluateStreakHistory} from './streak-forgiveness.mjs';\r\nexport const DAY_MS=86400000;\r\nexport const BREATHING_MS=180000;\r\nexport const dayAt=now=>Math.floor(now/DAY_MS);\r\nexport function loginStreak(days,today){return evaluateStreakHistory(days,today).at(-1)?.streak??0;}\r\nexport function weaponDamage(combat,weapon,now=Date.now()){\r\n const current=combat?.day===dayAt(now)&&Number.isSafeInteger(combat.loginStreak)&&combat.loginStreak>=1;\r\n const level=Number.isInteger(weapon?.tier)&&weapon.tier>=0&&weapon.tier<=20?weapon.tier+1:1;\r\n return 10*(current?combat.loginStreak:1)*level*(current&&combat.breathingCompleted===true?100:1);\r\n}\r\nexport class BreathingSession{\r\n constructor(durationMs=BREATHING_MS){this.durationMs=Math.max(BREATHING_MS,Math.min(2*60*60*1000,Number(durationMs)||BREATHING_MS));this.elapsed=0;this.last=null;this.active=false;}\r\n sample(now,active){if(this.last!==null&&this.active&&active)this.elapsed=Math.min(this.durationMs,this.elapsed+Math.max(0,Math.min(1500,now-this.last)));this.last=now;this.active=active;return this.elapsed;}\r\n get complete(){return this.elapsed>=this.durationMs;}\r\n}\r\n","cosmetic-reward-reveal.mjs":"import {performanceOwner} from './performance-progress.mjs';\r\nimport {isGranted} from './creature/source/creator/unlock-store.ts';\r\n\r\nconst CSS=`\r\n.cosmetic-reward-reveal{box-sizing:border-box;width:min(380px,calc(100vw - 28px));max-height:calc(100dvh - 28px);padding:20px;margin:auto;overflow:auto;border:2px solid var(--reward-color,#4bafff);border-radius:20px;background:#17131f;color:#fff7df;font:500 16px/1.4 system-ui;text-align:center}\r\n.cosmetic-reward-reveal::backdrop{background:#090717d9}.cosmetic-reward-reveal h2{margin:0;font-size:22px}.cosmetic-reward-reveal p{margin:8px 0}.cosmetic-reward-reveal .reward-name{color:var(--reward-color);font-weight:800;overflow-wrap:anywhere}\r\n.original-coach-reward-stage{width:100%;height:clamp(140px,30dvh,260px);position:relative}.original-coach-reward-stage canvas{display:block;width:100%;height:100%}\r\n.cosmetic-reward-reveal .reward-actions{display:flex;gap:10px;justify-content:center;margin-top:14px}.cosmetic-reward-reveal button{min-width:80px;min-height:44px;padding:8px 16px;border:2px solid var(--reward-color);border-radius:12px;background:var(--reward-color);color:#15101c;font:800 15px system-ui;cursor:pointer}.cosmetic-reward-reveal button.secondary{background:transparent;color:#fff7df}.cosmetic-reward-reveal button:focus-visible{outline:3px solid white;outline-offset:3px}.cosmetic-reward-reveal [hidden]{display:none}\r\n`;\r\n\r\nexport function mountCosmeticRewardReveal(){\r\n let queue=[],card=null,preview=null,timer=0,epoch=0,disposed=false,owner=performanceOwner(),current=null;\r\n const style=document.createElement('style');style.textContent=CSS;document.head.append(style);\r\n const viewerUrl='/creature/assets/reward-preview.js';\r\n const clean=()=>{++epoch;preview?.dispose();preview=null;card?.close();card?.remove();card=null;current=null;};\r\n const clear=()=>{clearTimeout(timer);queue=[];clean();owner=performanceOwner();};\r\n async function loadPreview(stage,status,retry,item){\r\n  const run=++epoch,expectedOwner=owner;preview?.dispose();preview=null;stage.replaceChildren();delete stage.dataset.ready;status.textContent='Loading original MYR5 coach…';retry.hidden=true;\r\n  try{\r\n   const module=await import(viewerUrl);\r\n   if(disposed||run!==epoch||expectedOwner!==performanceOwner()||!card?.open)return;\r\n   const instance=module.mountOriginalRewardPreview(stage,item);preview=instance;\r\n   const loaded=await instance.ready;\r\n   if(disposed||run!==epoch||expectedOwner!==performanceOwner())return;\r\n   if(!loaded)throw Error('Preview unavailable');\r\n   card.querySelector('.reward-name').textContent=module.cosmeticRewardName(item.kind,item.id);\r\n   status.textContent='Also unlocked for original MYR5.';\r\n  }catch{\r\n   if(disposed||run!==epoch||expectedOwner!==performanceOwner())return;\r\n   preview?.dispose();preview=null;status.textContent='Reward saved. Download the Coach pack to see it on original MYR5 offline, or retry online.';retry.hidden=false;\r\n  }\r\n }\r\n function showNext(){\r\n  clearTimeout(timer);if(disposed||card||!queue.length)return;\r\n  if(owner!==performanceOwner()){clear();return;}\r\n  // Rewards never interrupt an active workout or another native dialog.\r\n  if(document.hidden||document.querySelector('dialog[open]')||document.body.dataset.tracking==='true'||document.body.dataset.cameraWorkout==='true'){timer=setTimeout(showNext,400);return;}\r\n  const item=queue.shift();if(!isGranted(item.kind,item.id,'myr5')){showNext();return;}\r\n  current=item;card=document.createElement('dialog');card.className='cosmetic-reward-reveal';card.setAttribute('aria-label','Cosmetic unlocked on original MYR5');card.style.setProperty('--reward-color',item.kind==='texture'?'#ff9c36':item.kind==='palette'?'#4bafff':'#76e356');\r\n  card.innerHTML='<h2>Original MYR5 unlocked</h2><p class=\"reward-name\"></p><div class=\"original-coach-reward-stage\"></div><p data-status role=\"status\" aria-live=\"polite\"></p><div class=\"reward-actions\"><button type=\"button\" class=\"secondary\" data-retry hidden>Retry preview</button><button type=\"button\" class=\"secondary\" data-close>Close</button><button type=\"button\" data-next></button></div>';\r\n  const next=card.querySelector('[data-next]'),retry=card.querySelector('[data-retry]'),status=card.querySelector('[data-status]'),stage=card.querySelector('.original-coach-reward-stage');card.querySelector('.reward-name').textContent=item.id;next.textContent=queue.length?'Next reward':'Done';\r\n  next.onclick=()=>{clean();showNext();};card.querySelector('[data-close]').onclick=clear;card.addEventListener('cancel',event=>{event.preventDefault();clear();});retry.onclick=()=>loadPreview(stage,status,retry,item);\r\n  document.body.append(card);card.showModal();next.focus();void loadPreview(stage,status,retry,item);\r\n }\r\n const onGrant=event=>{\r\n  if(disposed||event.detail?.owner!==performanceOwner()||!Array.isArray(event.detail?.granted))return;\r\n  if(owner!==performanceOwner())clear();\r\n  const batch=new Set();for(const item of event.detail.granted){if(!['color','palette','texture'].includes(item?.kind)||typeof item.id!=='string'||!isGranted(item.kind,item.id,'myr5'))continue;const key=item.kind+':'+item.id;if(batch.has(key))continue;batch.add(key);queue.push({kind:item.kind,id:item.id});}\r\n  if(card&&current)card.querySelector('[data-next]').textContent=queue.length?'Next reward':'Done';showNext();\r\n };\r\n const accountChanged=()=>{if(owner!==performanceOwner())clear();};\r\n const dispose=()=>{if(disposed)return;disposed=true;clear();style.remove();window.removeEventListener('myr5:cosmetics-unlocked',onGrant);window.removeEventListener('myr5:account-ready',accountChanged);window.removeEventListener('myr5:account-cleared',clear);window.removeEventListener('pagehide',dispose);};\r\n window.addEventListener('myr5:cosmetics-unlocked',onGrant);window.addEventListener('myr5:account-ready',accountChanged);window.addEventListener('myr5:account-cleared',clear);window.addEventListener('pagehide',dispose);\r\n return dispose;\r\n}\r\n","creature-skin-reward-reveal.mjs":"const COLLECTIONS=Object.freeze({\r\n 'creature-forged-realms':'Forged Realms',\r\n 'creature-celestial-rift':'Celestial Rift',\r\n});\r\nconst OWNER=/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/;\r\nconst STYLE_ID='creatureSkinRewardRevealStyle';\r\nconst CSS=`\r\n.creature-skin-reward{position:fixed;z-index:10020;top:calc(env(safe-area-inset-top,0px) + 14px);right:14px;width:min(360px,calc(100vw - 28px));padding:18px 18px 14px;border:1px solid #e5c778;border-radius:16px;background:linear-gradient(145deg,#2e2037f5,#17131cf7);box-shadow:0 16px 48px #0008;color:#fff8e9;font:500 15px/1.45 system-ui,sans-serif;pointer-events:auto;animation:skin-reward-in .34s cubic-bezier(.16,1,.3,1) both}\r\n.creature-skin-reward h2{margin:0 28px 8px 0;color:#ffe39a;font:700 18px/1.2 Georgia,serif}\r\n.creature-skin-reward ul{margin:8px 0 14px;padding-left:20px}.creature-skin-reward li+li{margin-top:5px}\r\n.creature-skin-reward .skin-reward-collection{display:block;color:#d9c9ef;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-top:10px}\r\n.creature-skin-reward button{border:1px solid #d8c9a1;border-radius:999px;background:#f2dfb0;color:#241b2b;padding:7px 14px;font:700 13px system-ui,sans-serif;cursor:pointer}\r\n.creature-skin-reward button:focus-visible{outline:3px solid #fff;outline-offset:3px}\r\n@keyframes skin-reward-in{from{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\r\n@media(prefers-reduced-motion:reduce){.creature-skin-reward{animation:none}}\r\n`;\r\nconst ownerOf=account=>{const id=account?.user?.id;return typeof id==='string'&&OWNER.test(id)?id:null;};\r\n\r\n/** A small, non-blocking notice for newly granted post-download creature skins. */\r\nexport function mountCreatureSkinRewardReveal(){\r\n const initial=window.myr5AuthenticatedAccount;\r\n let disposed=false,activeOwner=Number.isSafeInteger(initial?.dataEpoch)&&initial.dataEpoch>0?ownerOf(initial):null,activeEpoch=activeOwner?initial.dataEpoch:null;\r\n let style=null,card=null;const queue=[],seen=new Set();\r\n function clear(){queue.length=0;seen.clear();card?.remove();card=null;style?.remove();style=null;}\r\n function ensureStyle(){if(style?.isConnected)return;style=document.getElementById(STYLE_ID)||document.createElement('style');style.id=STYLE_ID;style.textContent=CSS;if(!style.isConnected)document.head.append(style);}\r\n function showNext(){\r\n  if(disposed||card||!queue.length)return;\r\n  const groups=queue.shift();ensureStyle();const next=document.createElement('aside');next.className='creature-skin-reward';next.setAttribute('role','status');next.setAttribute('aria-live','polite');next.setAttribute('aria-atomic','true');\r\n  const title=document.createElement('h2');title.textContent='New creature skin';next.append(title);\r\n  for(const [collection,names] of groups){const label=document.createElement('span');label.className='skin-reward-collection';label.textContent=collection;next.append(label);const list=document.createElement('ul');for(const name of names){const item=document.createElement('li');item.textContent=name;list.append(item);}next.append(list);}\r\n  const dismiss=document.createElement('button');dismiss.type='button';dismiss.textContent='Dismiss';dismiss.setAttribute('aria-label','Dismiss creature skin reward');dismiss.addEventListener('click',()=>{next.remove();card=null;showNext();});next.append(dismiss);document.body.append(next);card=next;\r\n }\r\n function onReward(event){\r\n  if(disposed||!activeOwner||activeEpoch===null||ownerOf(window.myr5AuthenticatedAccount)!==activeOwner||window.myr5AuthenticatedAccount?.dataEpoch!==activeEpoch)return;\r\n  const granted=Array.isArray(event.detail?.granted)?event.detail.granted:[],groups=new Map();\r\n  for(const reward of granted){\r\n   if(reward?.kind!=='creature-skin'||typeof reward.id!=='string'||!/^creature-[a-z0-9-]{1,80}$/.test(reward.id)||seen.has(reward.id))continue;\r\n   const collection=COLLECTIONS[reward.collection];if(!collection)continue;\r\n   const name=typeof reward.name==='string'&&reward.name.trim()?reward.name.trim().slice(0,100):reward.id;\r\n   seen.add(reward.id);if(!groups.has(collection))groups.set(collection,[]);groups.get(collection).push(name);\r\n  }\r\n  if(groups.size){queue.push(groups);showNext();}\r\n }\r\n function onAccountReady(event){\r\n  const next=event.detail,epoch=Number.isSafeInteger(next?.dataEpoch)&&next.dataEpoch>0?next.dataEpoch:null,id=epoch?ownerOf(next):null;\r\n  if(id!==activeOwner||epoch!==activeEpoch){clear();activeOwner=id;activeEpoch=epoch;}\r\n }\r\n function onAccountCleared(){clear();activeOwner=null;activeEpoch=null;}\r\n function dispose(){if(disposed)return;disposed=true;clear();window.removeEventListener('myr5:battle-pass',onReward);window.removeEventListener('myr5:account-ready',onAccountReady);window.removeEventListener('myr5:account-cleared',onAccountCleared);window.removeEventListener('pagehide',dispose);}\r\n window.addEventListener('myr5:battle-pass',onReward);window.addEventListener('myr5:account-ready',onAccountReady);window.addEventListener('myr5:account-cleared',onAccountCleared);window.addEventListener('pagehide',dispose,{once:true});\r\n return {dispose};\r\n}\r\n","creature/cinematics.css":".coach-film{position:fixed;inset:0;z-index:990;isolation:isolate;overflow:hidden;color:#fff5e5;background:linear-gradient(#090510ed,transparent 24%,transparent 56%,#090510f2 90%);pointer-events:none;font-family:system-ui,sans-serif}\r\n.coach-film[hidden]{display:none!important}\r\n.film-doors{display:none}\r\n.film-top{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:calc(20px + env(safe-area-inset-top)) max(20px,env(safe-area-inset-right)) 15px max(20px,env(safe-area-inset-left));border-top:3px solid #edcc91}\r\n.film-label{font:500 12px ui-monospace,monospace;letter-spacing:2px;color:#d4bddf}.film-skip{pointer-events:auto;background:#1c112bec!important;border:1px solid #ab879b!important;border-radius:4px!important;min-height:44px;font-size:14px;color:#fff1d8}\r\n.film-copy{position:absolute;bottom:0;left:0;right:0;max-width:960px;margin:auto;padding:30px 24px calc(32px + env(safe-area-inset-bottom));text-align:center}.film-title{font-size:clamp(28px,6vw,62px);font-weight:600;letter-spacing:-1.5px;line-height:1.05;margin:14px auto;max-width:760px;text-wrap:balance}.film-beat{font:500 12px ui-monospace,monospace;letter-spacing:3px;color:#edcc91;margin:0}.film-detail{font-size:16px;color:#cfb7df;line-height:1.5;min-height:24px;margin:14px auto 24px;max-width:560px}.film-progress{height:2px;background:#b6a0c323;width:min(280px,70vw);margin:auto;overflow:hidden}.film-progress i{display:block;transform:scaleX(0);transform-origin:left;height:100%;background:#f1d7a0;box-shadow:0 0 12px #edcc91}\r\n.film-scan{position:absolute;inset:12% 0 23%;background:linear-gradient(180deg,transparent 40%,#c39ff711 49.5%,#e1c9ff44 50%,transparent 50.5%);animation:film-scan 4.8s ease-in-out both;opacity:.6}.film-scan:after{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 25%,#080411 90%)}\r\nbody[data-cinematic]{overflow:hidden}\r\nbody[data-cinematic=pre] .containment-pod,body[data-cinematic=post] .encounter{position:fixed!important;inset:0!important;width:100vw!important;height:100dvh!important;min-height:0!important;max-height:none!important;margin:0!important;border:0!important;border-radius:0!important;z-index:980!important;background:radial-gradient(ellipse at 50% 44%,#623286 0%,#20122d 38%,#080410 78%)!important;box-shadow:none!important}\r\nbody[data-cinematic=pre] .coach-mount,body[data-cinematic=post] .rest-coach-mount{inset:7% 0 17%!important;top:7%!important;left:0!important;width:100%!important;height:76%!important;filter:drop-shadow(0 0 32px #9b67c333)!important;z-index:3!important}\r\nbody[data-cinematic=pre] .containment-pod:before,body[data-cinematic=pre] .pod-rim,body[data-cinematic=pre] .pod-nameplate,body[data-cinematic=pre] .pod-inscription,body[data-cinematic=pre] .camera-preview{display:none!important}\r\nbody[data-cinematic=post] .encounter>:not(.rest-coach-mount){visibility:hidden!important}\r\n.coach-film[data-scene=opening]{background:none;pointer-events:auto}\r\n.coach-film[data-scene=opening] .film-doors{display:block;position:absolute;inset:0;z-index:-1}\r\n.film-door{position:absolute;top:0;bottom:0;width:50%;overflow:hidden;box-sizing:border-box;background:linear-gradient(110deg,#342442 0%,#1f142c 38%,#130c1d 76%,#291b35 100%);will-change:transform}\r\n.film-door:before{content:'';position:absolute;inset:18px 20px;border:1px solid #79578255;box-shadow:inset 0 0 0 7px #0b071433,inset 0 0 70px #08041144;border-radius:6px}\r\n.film-door:after{content:'';position:absolute;top:0;bottom:0;width:2px;background:linear-gradient(#ad7fbe33,#f9d296 25%,#f9d296 75%,#ad7fbe33);box-shadow:0 0 18px #efb96166}\r\n.film-door-left{left:0;box-shadow:inset -12px 0 18px #09050f,inset -14px 0 0 #73508055;animation:film-door-left 4.8s cubic-bezier(.66,0,.2,1) both}\r\n.film-door-right{right:0;background:linear-gradient(250deg,#342442 0%,#1f142c 38%,#130c1d 76%,#291b35 100%);box-shadow:inset 12px 0 18px #09050f,inset 14px 0 0 #73508055;animation:film-door-right 4.8s cubic-bezier(.66,0,.2,1) both}\r\n.film-door-left:after{right:0}.film-door-right:after{left:0}\r\n.film-door-brand{position:absolute;top:37%;width:clamp(112px,25dvh,220px);aspect-ratio:1;transform:translate(-50%,-50%);padding:18px;box-sizing:content-box;background:radial-gradient(ellipse at 50% 25%,#fff8e8,#e7d7b9);border:1px solid #f6dcb0;border-radius:10px;box-shadow:0 0 0 8px #140c21,0 0 0 9px #87649388,0 12px 55px #03010699}\r\n.film-door-left .film-door-brand{left:100%}.film-door-right .film-door-brand{left:0}\r\n.film-door-brand img{display:block;width:100%;height:100%;object-fit:contain}\r\n.coach-film[data-scene=opening] .film-scan{display:none}\r\n.coach-film[data-scene=opening] .film-top{border:0;z-index:2}.coach-film[data-scene=opening] .film-label{animation:film-door-copy 4.8s ease both}\r\n.coach-film[data-scene=opening] .film-copy{top:58%;bottom:auto;padding-top:0;animation:film-door-copy 4.8s ease both}\r\n.coach-film[data-scene=opening] .film-title{font-size:clamp(28px,5vw,54px);max-width:660px}.coach-film[data-scene=opening] .film-detail{letter-spacing:2px;font-size:14px}\r\n.cinematic-settings{display:flex;gap:8px;margin-top:20px;flex-wrap:wrap}.cinematic-settings button{font-size:14px;flex:1}\r\n@keyframes film-door-left{0%,70%{transform:translateX(0)}100%{transform:translateX(-101%)}}\r\n@keyframes film-door-right{0%,70%{transform:translateX(0)}100%{transform:translateX(101%)}}\r\n@keyframes film-door-copy{0%,68%{opacity:1;transform:translateY(0)}78%,100%{opacity:0;transform:translateY(-8px)}}\r\n@keyframes film-scan{from{transform:translateY(-70%)}to{transform:translateY(70%)}}\r\n@media(max-height:550px) and (orientation:landscape){.film-title{font-size:30px}.film-copy{padding-bottom:12px}.film-detail{margin:7px auto}.film-top{padding-top:12px}.film-copy{max-width:700px}.film-label{font-size:12px}}\r\n@media(max-height:550px){.film-door-brand{top:35%;width:clamp(72px,24dvh,112px);padding:10px}.coach-film[data-scene=opening] .film-copy{top:57%;padding-bottom:8px}.coach-film[data-scene=opening] .film-title{font-size:clamp(24px,4vw,30px);margin:8px auto}.coach-film[data-scene=opening] .film-detail{margin:8px auto 12px}}\r\n@media(prefers-reduced-motion:reduce){.coach-film{display:none!important}body[data-cinematic] .myr5-companion-stage{animation:none!important}}\r\n","creature/cinematics.js":"// Full-app opening doors, followed by scenes using the live customized coach. Release 6: no pod \"home\" title film any\r\n// more (\"Your coach. Your next move.\"); the oval's ship intro is the coach's arrival.\r\nconst OPENING_SEEN='myr5-opening-doors-seen-v2';\r\nconst SCENES={\r\n opening:{duration:4800,label:'MOM INC. / POD 005',title:'Something big is waking up.',beats:['CONTAINMENT ONLINE','COACH AWAKENING','MYR5 IS READY']},\r\n pre:{duration:3300,label:'MOVEMENT SELECTED',title:'Let’s make it count.',beats:['FIND YOUR SPACE','FIND YOUR FOCUS','LET’S GO']},\r\n post:{duration:3800,label:'SET COMPLETE',title:'You showed up.',beats:['EFFORT RECORDED','TAKE A BREATH','MEET YOUR GIANT']}\r\n};\r\nexport function initCinematics({voice}={}){\r\n const body=document.body,reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n const read=(store,key)=>{try{return store.getItem(key);}catch{return null;}};\r\n const lowMotion=()=>{try{return reduced.matches||JSON.parse(read(localStorage,'myr5-motion-v1')||'{}')?.reduced===true;}catch{return reduced.matches;}};\r\n let enabled=read(localStorage,'myr5-cinematics-v1')!=='off',active=null,frame=0,lastScreen=body.dataset.screen,disposed=false;\r\n if(!document.getElementById('coach-cinematics-style')){const sheet=document.createElement('link');sheet.rel='stylesheet';sheet.href='/creature/cinematics.css';document.head.append(sheet);}\r\n const layer=document.createElement('section');layer.className='coach-film';layer.hidden=true;\r\n layer.setAttribute('aria-label','Coach cinematic');layer.innerHTML='<div class=\"film-doors\" aria-hidden=\"true\"><div class=\"film-door film-door-left\"><div class=\"film-door-brand\"><img alt=\"\"></div></div><div class=\"film-door film-door-right\"><div class=\"film-door-brand\"><img alt=\"\"></div></div></div><div class=\"film-top\"><span class=\"film-label\"></span><button type=\"button\" class=\"film-skip\">Skip intro →</button></div><div class=\"film-scan\" aria-hidden=\"true\"></div><div class=\"film-copy\"><p class=\"film-beat\"></p><h1 class=\"film-title\"></h1><p class=\"film-detail\"></p><div class=\"film-progress\" aria-hidden=\"true\"><i></i></div></div>';\r\n for(const side of ['left','right'])layer.querySelector(`.film-door-${side} img`).src=new URL('../pod/mom-inc-mark.png',import.meta.url).href;\r\n body.append(layer);\r\n const label=layer.querySelector('.film-label'),title=layer.querySelector('.film-title'),beat=layer.querySelector('.film-beat'),detail=layer.querySelector('.film-detail'),progress=layer.querySelector('.film-progress i'),skip=layer.querySelector('button');\r\n const inertNodes=[];\r\n function finish(outcome='complete'){\r\n  if(!active)return;const run=active;active=null;cancelAnimationFrame(frame);\r\n  delete body.dataset.cinematic;layer.hidden=true;if(run.kind!=='opening'){try{window.myr5Creature?.cinematic(null);}catch{}}\r\n  for(const [node,was] of inertNodes.splice(0))node.inert=was;\r\n  if(run.focus?.isConnected&&!run.focus.closest('[hidden]'))run.focus.focus({preventScroll:true});\r\n  if(run.kind==='opening'&&outcome!=='cancelled'){try{sessionStorage.setItem(OPENING_SEEN,'yes');}catch{}}\r\n  window.dispatchEvent(new CustomEvent('myr5:cinematic-end',{detail:{kind:run.kind,outcome}}));\r\n  run.resolve(outcome);\r\n }\r\n function play(kind,info={}){\r\n  if(!Object.hasOwn(SCENES,kind)||disposed)return Promise.resolve('cancelled');\r\n  finish('cancelled');\r\n  if(!enabled||lowMotion()||document.hidden)return Promise.resolve(document.hidden?'cancelled':'skipped');\r\n  const scene=SCENES[kind];\r\n  voice?.cancel();\r\n  body.dataset.cinematic=kind;layer.dataset.scene=kind;layer.hidden=false;\r\n  label.textContent=scene.label;title.textContent=scene.title;\r\n  detail.textContent=kind==='pre'?(info.name||'Your next movement'):kind==='post'?(document.getElementById('setReceipt')?.textContent||'Your effort counts.'):kind==='opening'?'MAKING YOU READY.':'';\r\n  beat.textContent=scene.beats[0];progress.style.transform='scaleX(0)';\r\n  skip.textContent=kind==='pre'?'Start now →':kind==='post'?'Continue →':'Skip intro →';\r\n  layer.setAttribute('role','dialog');layer.setAttribute('aria-modal','true');\r\n  const focus=document.activeElement;\r\n  // Keep the existing canvas mounted, while keyboard focus stays in the scene.\r\n  for(const node of body.children)if(node!==layer&&!['SCRIPT','LINK','STYLE'].includes(node.tagName)){inertNodes.push([node,node.inert]);node.inert=true;}\r\n  skip.focus({preventScroll:true});\r\n  return new Promise(resolve=>{\r\n   active={kind,resolve,focus,start:performance.now(),elapsed:0};\r\n   const tick=now=>{\r\n    if(!active||active.kind!==kind)return;const elapsed=now-active.start;active.elapsed=elapsed;\r\n    if(kind!=='opening'){try{window.myr5Creature?.cinematic(kind,elapsed);}catch{finish('skipped');return;}}\r\n    const index=Math.min(scene.beats.length-1,Math.floor(elapsed/scene.duration*scene.beats.length));\r\n    if(beat.textContent!==scene.beats[index])beat.textContent=scene.beats[index];\r\n    progress.style.transform=`scaleX(${Math.min(1,elapsed/scene.duration)})`;\r\n    if(elapsed>=scene.duration)finish();else frame=requestAnimationFrame(tick);\r\n   };frame=requestAnimationFrame(tick);\r\n  });\r\n }\r\n skip.addEventListener('click',()=>finish('skipped'));\r\n const keyboard=event=>{if(!active)return;if(event.key==='Escape'){event.preventDefault();event.stopImmediatePropagation();finish('skipped');}else if(event.key==='Tab'){event.preventDefault();skip.focus();}};\r\n document.addEventListener('keydown',keyboard,true);\r\n const visibility=()=>{if(document.hidden)finish('cancelled');};document.addEventListener('visibilitychange',visibility);\r\n const motionChange=()=>{if(reduced.matches)finish('skipped');};reduced.addEventListener('change',motionChange);\r\n const settings=document.getElementById('settings');\r\n if(settings){\r\n  const group=document.createElement('div');group.className='cinematic-settings';\r\n  const toggle=document.createElement('button');toggle.type='button';\r\n  const paint=()=>{toggle.textContent=enabled?'Cinematics on':'Cinematics off';toggle.setAttribute('aria-pressed',String(enabled));};paint();\r\n  toggle.addEventListener('click',()=>{enabled=!enabled;try{localStorage.setItem('myr5-cinematics-v1',enabled?'on':'off');}catch{}paint();if(!enabled)finish('skipped');});\r\n  const replay=document.createElement('button');replay.type='button';replay.textContent='Replay opening';\r\n  replay.addEventListener('click',async()=>{settings.close();if(!enabled){enabled=true;paint();try{localStorage.setItem('myr5-cinematics-v1','on');}catch{}}await play('opening');});\r\n  group.append(toggle,replay);settings.append(group);\r\n }\r\n const observer=new MutationObserver(()=>{const screen=body.dataset.screen;if(screen===lastScreen)return;lastScreen=screen;if(active&&((active.kind==='post'&&screen!=='rest')||(active.kind!=='post'&&screen!=='pod')))finish('cancelled');});\r\n observer.observe(body,{attributes:true,attributeFilter:['data-screen']});\r\n // Doors cover the whole app while the coach loads beneath them.\r\n const bootTimer=setTimeout(async()=>{\r\n  if(disposed||document.hidden||body.dataset.screen!=='pod'||document.querySelector('dialog[open]')||body.dataset.tracking==='true'||active)return;\r\n  if(read(sessionStorage,OPENING_SEEN)!=='yes')await play('opening');\r\n },150);\r\n const interaction=()=>clearTimeout(bootTimer);\r\n for(const id of ['start','openLibrary','openSettings','openBreathing','visitRest','openIdentity','openAccomplishments'])document.getElementById(id)?.addEventListener('click',interaction,{capture:true});\r\n window.addEventListener('pagehide',()=>{disposed=true;clearTimeout(bootTimer);finish('cancelled');observer.disconnect();document.removeEventListener('keydown',keyboard,true);document.removeEventListener('visibilitychange',visibility);reduced.removeEventListener('change',motionChange);},{once:true});\r\n return {play,cancel:()=>finish('cancelled'),stats:()=>({active:active?.kind||null,enabled,reduced:lowMotion()})};\r\n}\r\n","creature/creature.css":":root{color-scheme:dark;--background:#141019;--foreground:#f4ebfa;--gold:#edc986;--muted:#c1accd;--border:#6a516f;--primary:#edc986;--primary-foreground:#241b2b;--card:#302537;--popover:#302537;--accent:#624773;--accent-foreground:#fff3dd;--ring:#f5d79f;font-family:'Segoe UI',Arial,sans-serif;font-size:16px}\r\n*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden}body{background:radial-gradient(ellipse at 40% 0,#463149,#161019 75%);color:var(--foreground);font:1rem/1.4 'Segoe UI',Arial,sans-serif}button,input,select{font:inherit}button,.button{cursor:pointer;touch-action:manipulation}button,.button,select{font-size:.875rem;color:var(--foreground);background:linear-gradient(#5d4766,#3c2c46);border:1px solid #8b708e;border-bottom-color:#211729;border-radius:8px;min-height:40px;padding:8px 12px;box-shadow:inset 0 1px #e3c7df22,0 2px 0 #1b1421}button:hover,.button:hover{border-color:var(--gold);background:#61456f}button:disabled{opacity:.4;cursor:default}button[aria-pressed=true],button[aria-selected=true]{background:linear-gradient(#f1d79f,#c6a467);border-color:#ffe3a9;color:#271a2c;box-shadow:inset 0 1px #fff4cf,0 2px #19131e}.primary,button.primary{background:var(--gold);color:#261b2c;border-color:var(--gold);font-weight:650}a{color:var(--gold)}.button{display:inline-flex;align-items:center;justify-content:center;text-decoration:none}button:focus-visible,a:focus-visible,select:focus-visible,input:focus-visible,.button:focus-within{outline:3px solid #ffe6ac;outline-offset:3px}button svg{width:18px;height:18px;flex-shrink:0}h1,h2,p{margin:0}h1{font-size:1.25rem;font-weight:700;line-height:1.15}h2{font-size:1.25rem;font-weight:650;line-height:1.25}small{font-size:.75rem;letter-spacing:.1em;color:var(--muted)}[hidden]{display:none!important}\r\n.editor-shell{display:flex;flex-direction:column;gap:12px;width:min(1440px,calc(100% - 32px));height:calc(100dvh - 32px);margin:16px auto;padding:18px;border:6px solid #483c50;border-top-color:#a189a0;border-left-color:#79667f;border-radius:30px;background:linear-gradient(130deg,#766176,#46374f 40%,#32263b);box-shadow:inset 0 0 0 2px #201925,inset 0 3px #dccbd34d,0 7px 0 #19121e,0 14px 45px #0008;position:relative}\r\n.editor-shell:before,.editor-shell:after{content:'';position:absolute;top:5px;width:9px;height:9px;border-radius:50%;background:linear-gradient(135deg,#c8c1b0 42%,#393242 44%,#393242 55%,#a89fa6 57%);box-shadow:1px 2px 3px #0009;pointer-events:none}.editor-shell:before{left:10px}.editor-shell:after{right:10px}\r\n.editor-header{display:flex;align-items:center;gap:16px;min-height:54px;flex:none;padding:0 4px 8px}.editor-brand{font-size:1.3rem;line-height:.9;font-weight:900;letter-spacing:-.08em;text-decoration:none;color:#f3d79b}.editor-brand span{display:block;font-size:.65rem;letter-spacing:.4em;margin-top:5px}.editor-title small{display:block;font-size:.75rem;margin-bottom:5px;color:#e1cddf}.header-actions{display:flex;align-items:center;gap:6px;margin-left:auto}.header-actions button{padding:8px 10px;min-width:40px}.return-link{white-space:nowrap}\r\n.editor-workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(360px,430px);gap:14px;flex:1;min-height:0}.preview-bay{display:flex;flex-direction:column;min-width:0;min-height:0;border:3px solid #23192b;border-bottom-color:#a38ba6;border-radius:20px;overflow:hidden;background:radial-gradient(ellipse at 50% 42%,#56365e,#26182f 58%,#130e1c);box-shadow:inset 0 2px 14px #0009,0 1px #d6c4d533;position:relative}.preview-readout{display:flex;justify-content:center;gap:12px;align-items:center;padding:14px 16px 0;color:#d6c3e1;font-size:.75rem;flex:none;z-index:1}.preview-stage{flex:1;min-height:0;position:relative;touch-action:none}.hand-viewer-wrap,.hand-canvas{height:100%;width:100%;position:relative}.hand-canvas canvas,#creatureStage canvas{display:block;width:100%;height:100%;touch-action:none}.model-status{position:absolute;top:42%;left:8%;right:8%;text-align:center;color:#ecd9f2}.holo-view:after{content:'';position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(0deg,transparent 0 3px,#55eaff12 3px 4px)}\r\n.preview-toolbar{display:flex;flex-direction:column;gap:9px;align-items:center;padding:10px 12px 14px;flex:none;border-top:1px solid #6e507444;background:#2117299c}.view-buttons{display:flex;justify-content:center;gap:6px;flex-wrap:wrap}.view-buttons button{min-height:36px;padding:6px 10px;font-size:.875rem}\r\n.zoom-row{display:flex;align-items:center;gap:8px;width:100%;max-width:300px}.zoom-row input{flex:1;min-width:0;accent-color:var(--gold);height:24px}.zoom-row button{min-height:32px;min-width:36px;padding:2px 8px;font-size:1rem;line-height:1}\r\n.design-console{min-height:0;min-width:0;display:flex;flex-direction:column;border:2px solid #241b2b;border-top-color:#a78caa;border-bottom-color:#1c1524;border-radius:17px;background:linear-gradient(155deg,#48354f,#302337 50%);box-shadow:inset 0 1px #e4c8df33,0 4px 0 #221829;overflow:hidden}.console-tabs{flex:1;min-height:0;display:flex;flex-direction:column;gap:0}.menu-tabs{display:flex;flex:none;gap:3px;align-items:stretch;justify-content:stretch;width:100%;height:auto!important;padding:8px;background:#261c2e;border-radius:0;border-bottom:1px solid #785b7e;overflow-x:auto}.menu-tabs>button{flex:1;min-height:42px;height:auto;padding:7px 9px;white-space:nowrap;border-radius:7px;font-size:.875rem;background:#41304a;color:#decee8}.menu-tabs>button[aria-selected=true]{background:linear-gradient(#efd39e,#c7a26b);color:#281c2f;border-color:#f3dba4}.console-scroll{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-color:#a58aad #2c2034;padding:14px}.console-scroll>[role=tabpanel]{font-size:1rem;outline:none}\r\n.panel-heading{display:flex;justify-content:space-between;align-items:center;gap:10px;margin:16px 0 12px}.panel-heading:first-child{margin-top:0}.panel-heading small{display:block;margin-bottom:4px;font-size:.75rem;letter-spacing:.07em}.panel-heading h2{color:#f5e4c6}.lock-part{width:40px;padding:9px}.material-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px}.material-grid button{padding:4px;border:1px solid #72587b;background:#24172e;border-radius:9px;overflow:hidden;min-width:0;display:flex;flex-direction:column;gap:4px;align-items:stretch;box-shadow:0 2px 0 #150e1e}.material-grid button[aria-pressed=true]{background:#e4c389;color:#241729;border:2px solid #ffe3a8;padding:3px}.material-grid img{width:100%;height:68px;object-fit:cover;border-radius:5px;background:#20112b;display:block}.material-grid button>span{display:flex;align-items:center;justify-content:center;min-height:34px;padding:0 2px 2px;font-size:.875rem;line-height:1.15;overflow-wrap:anywhere}#colorSwatches{display:grid;gap:10px}#colorSwatches .material-grid,#shipColourRow .material-grid{display:flex;flex-wrap:wrap;gap:7px;overflow:visible;padding:0}#colorSwatches .material-grid button,#shipColourRow .material-grid button{flex:0 0 44px;width:44px;height:44px;padding:0}#colorSwatches strong{display:block;margin:4px 0 -4px;font-size:.78rem;letter-spacing:.07em;text-transform:uppercase;color:#f5e4c6}.part-toggle{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px}.part-toggle button{display:flex;align-items:center;justify-content:center;gap:7px;white-space:nowrap;min-height:44px;font-size:.875rem;background:#41304a;color:#decee8}.part-toggle button[aria-checked=true]{background:linear-gradient(#efd39e,#c7a26b);color:#281c2f;border-color:#f3dba4}.part-toggle i{width:12px;height:12px;flex-shrink:0;border:1px solid #00000066;border-radius:50%}.texture-preview-row{display:flex;align-items:center;gap:8px;margin-top:8px}.panel-actions{display:flex;gap:7px;margin-top:14px;flex-wrap:wrap}.panel-actions>button:first-child{flex:1}.help{font-size:.875rem;line-height:1.5;color:#c7b3cf;margin:12px 0 2px}.console-status{flex:none;padding:10px 14px;border-top:1px solid #755b7b;background:#281e30;color:#e9d3a9;font-size:.75rem;line-height:1.4;min-height:38px;overflow-wrap:anywhere}\r\n.field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.field-grid>label:first-child{grid-column:1/-1}.coach-sprite-grid{grid-column:1/-1;grid-template-columns:repeat(auto-fill,minmax(92px,1fr));max-height:290px;overflow:auto;padding:4px}.coach-sprite-grid button{align-items:center}.coach-sprite-grid canvas{width:auto;height:96px;max-width:100%;image-rendering:pixelated}.coach-sprite-grid button>span{font-size:.76rem}label{font-size:.875rem;color:#e2d0eb}select{display:block;width:100%;margin-top:7px;min-height:44px;padding:9px 10px;box-shadow:inset 0 2px 4px #160c2077;background:#281c33;border-color:#907397}select option{background:#281c33;color:#f4e8f8}.range-stack{display:grid;gap:18px;margin:20px 0}.range-field{display:block;padding:12px;border:1px solid #705478;border-radius:9px;background:#291d33;box-shadow:inset 0 1px 5px #10091788}.range-field output{float:right;color:#efd097;font-variant-numeric:tabular-nums}.range-field input{display:block;width:100%;margin-top:9px;height:28px;accent-color:var(--gold);cursor:pointer}.range-field input::-webkit-slider-runnable-track{height:8px;background:#17101f;border:1px solid #7f638b;border-radius:5px}.range-field input::-webkit-slider-thumb{margin-top:-5px}.check{display:flex;align-items:center;gap:8px;min-height:44px}.check input{width:18px;height:18px;accent-color:var(--gold)}.gesture-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px}.gesture-grid button{text-align:left}.file-actions{\r\n  display:flex;\r\n  flex-wrap:wrap;\r\n  gap:10px;\r\n}.file-actions>*{\r\n  width:48px;\r\n  height:48px;\r\n  min-height:48px;\r\n  padding:0;\r\n  justify-content:center;\r\n  align-items:center;\r\n  display:inline-flex;\r\n}\r\n.file-actions svg{\r\n  width:24px;\r\n  height:24px;\r\n}.file-input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.file-actions .button{position:relative;overflow:hidden}.coach-panel{display:grid;gap:9px}.coach-panel .panel-kicker,.coach-note{display:none}.coach-panel label{font-size:.875rem;margin-top:6px}.coach-panel select{margin:0}.coach-tone{font-size:.875rem;color:#ccb5d5}.coach-panel blockquote{padding:16px;margin:8px 0;background:#251831;border:1px solid #8e6f94;border-left:3px solid var(--gold);border-radius:8px;color:#f6e3cf;font-size:1rem;line-height:1.55}.coach-panel blockquote small{display:block;margin-bottom:9px;color:var(--gold)}\r\n@media(min-width:1100px) and (min-height:880px){.material-grid img{height:88px}.material-grid{gap:8px}}\r\n@media(max-width:900px){.editor-shell{width:calc(100% - 16px);height:calc(100dvh - 16px);margin:8px auto;padding:12px;gap:8px;border-width:4px;border-radius:23px}.editor-workspace{grid-template-columns:minmax(0,1fr) minmax(320px,380px);gap:9px}.editor-header{gap:12px;min-height:50px;padding-bottom:2px}.header-actions{gap:4px}.editor-title small{font-size:.75rem}.preview-toolbar{padding:8px}.preview-hint{display:none}}\r\n@media(max-width:700px){.editor-shell{padding:9px;gap:7px}.editor-brand{display:none}.editor-header{gap:8px;min-height:48px}.editor-title h1{font-size:1.05rem}.editor-title small{letter-spacing:.05em;font-size:.75rem}.header-actions button{min-width:34px;min-height:38px;padding:7px}.return-link{font-size:.8125rem;padding:6px 8px;min-height:38px}.editor-workspace{grid-template-columns:1fr;grid-template-rows:minmax(160px,41%) minmax(0,1fr);gap:8px}.preview-bay{border-radius:14px}.preview-readout{padding:7px 10px 0;font-size:.75rem}.preview-toolbar{padding:5px 7px}.view-buttons{gap:4px}.view-buttons button{font-size:.8125rem;min-height:32px;padding:4px 9px}.design-console{border-radius:12px}.menu-tabs{padding:5px;gap:3px}.menu-tabs>button{min-height:40px;font-size:.875rem;padding:6px 8px}.console-scroll{padding:10px}.panel-heading{margin:12px 0 9px}.panel-heading h2{font-size:1.125rem}.material-grid{grid-auto-flow:column;grid-template-columns:none;grid-template-rows:repeat(2,102px);grid-auto-columns:78px;gap:6px;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;padding:3px 2px 9px;scrollbar-color:#b59ac0 #261c2e}.material-grid img{height:53px}.material-grid button>span{font-size:.8125rem;min-height:34px}.console-status{padding:7px 10px;min-height:32px}.range-stack{gap:12px;margin-top:14px}.range-field{padding:10px}.panel-actions{margin-top:10px}}\r\n@media(max-height:560px) and (min-width:701px){.editor-shell{margin:4px auto;height:calc(100dvh - 8px);padding:8px}.editor-header{min-height:40px}.editor-title small{display:none}.preview-readout{padding-top:7px}.preview-toolbar{padding:5px}.menu-tabs>button{min-height:36px}.console-status{padding:6px 10px;min-height:28px}}\r\n.material-grid.texture-grid{grid-column:1/-1;display:grid;grid-auto-flow:row;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));grid-template-rows:none;grid-auto-columns:auto;max-height:274px;overflow-x:hidden;overflow-y:auto;padding:4px}\r\n.texture-grid button{position:relative;min-height:94px;align-items:center;text-align:center}\r\n.texture-grid img{height:44px;width:44px;object-fit:cover;image-rendering:pixelated}\r\n.texture-grid button>span{min-height:25px;font-size:.74rem}\r\n.texture-grid .texture-tile-lock{position:absolute;top:4px;right:5px;font-size:12px;font-style:normal}\r\n#shipColourRow{margin:12px 0}\r\n/* R25: eye extras layer over any eye style. */\r\n.eye-toggles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;margin:12px 0}.check-field{display:flex;align-items:center;gap:8px;min-height:44px;padding:8px 10px;border:1px solid #705478;border-radius:9px;background:#291d33;font-size:.875rem}.check-field input{width:20px;height:20px;accent-color:var(--gold);flex:none}\r\n/* R25: the ship viewer sits above its controls and stays pinned while the colours scroll under it. */\r\n#shipPreview{position:sticky;top:-10px;z-index:2;height:150px;margin:0 0 8px;border-radius:8px;background:#0b0714;box-shadow:0 6px 10px -6px #000c}\r\n@media(max-height:860px){#shipPreview{height:120px}}\r\n@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}\r\n\r\n\r\n/* One continuous species list; identity stays above the preview. */\r\n.coach-sprite-grid{max-height:none;overflow:visible;grid-auto-flow:row;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));grid-template-rows:none;grid-auto-columns:auto}\r\n.coach-choice{position:relative;min-width:0;border:2px solid #b9a5c9;border-radius:10px;padding:2px;background:#171123}\r\n.coach-choice>button[data-body]{width:100%;min-height:120px;display:flex;flex-direction:column;align-items:center;padding:6px 4px;gap:5px}\r\n.coach-choice canvas{width:82px;height:82px;object-fit:contain;image-rendering:pixelated}\r\n.coach-choice>button>span{font-size:.72rem;line-height:1.2;text-align:center;overflow-wrap:anywhere}\r\n.coach-choice[data-locked=true] canvas{filter:grayscale(.8);opacity:.55}\r\n.coach-choice-state{display:block;padding:3px 26px 4px 4px;font-size:.68rem;color:#b9a5c9}\r\n.coach-choice-lock{position:absolute;bottom:2px;right:2px;padding:3px;min-height:32px;min-width:32px;font-size:.85rem}\r\n.preview-readout{gap:6px;padding:6px 8px;min-height:44px;font-size:.85rem;text-align:center}\r\n#coachPathSymbol{flex:none;border:2px solid currentColor;border-radius:7px;padding:3px 6px}\r\n#coachDisplayName{overflow-wrap:anywhere;line-height:1.15}\r\n#coachUnlock{padding:4px;min-width:40px;min-height:40px;flex:none}\r\n.coach-requirement-dialog{max-width:min(92vw,480px);padding:24px;border:2px solid #d8b573;border-radius:16px;background:#25192f;color:#fff1d1}\r\n.coach-requirement-dialog p{margin:14px 0;line-height:1.5}.coach-requirement-dialog::backdrop{background:#08050dc9}\r\n#panel-body:has(.coach-choice) .field-grid>label:has(#body){display:none}\r\n\r\n.preview-bay .preview-strip{top:50px}\r\n@media(max-width:600px){.menu-tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));overflow-x:visible}.menu-tabs>button{min-width:0}}\r\n\r\n#colourGrid button,#paletteGrid button,#shipColourGrid button{position:relative}\r\n.swatch-lock{position:absolute;right:3px;bottom:3px;display:grid;place-items:center;width:24px;height:24px;border-radius:6px;background:#130b20e6;color:#fff;font-size:15px;line-height:1;pointer-events:none;text-shadow:none}\r\n\r\n/* Keep adaptation selection with the menu; only the texture tiles scroll. */\r\n.texture-menu-header{display:none;flex:none;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid #785b7e;background:#302337}\r\n.console-tabs:has(#tab-textures[aria-selected=true]) .texture-menu-header{display:flex}\r\n.texture-menu-header h2{font-size:1.125rem;color:#f5e4c6}\r\n.texture-menu-header label{display:flex;align-items:center;gap:9px;flex:1;min-width:0;font-size:.8125rem}\r\n.texture-menu-header select{flex:1;min-width:0;margin:0}\r\n.console-scroll:has(#panel-textures:not([hidden])){padding:6px}\r\n#panel-textures .texture-grid{max-height:none;overflow:visible;margin:0;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:6px}\r\n#panel-textures .texture-preview-row{display:none}\r\n@media(max-width:700px){\r\n .texture-menu-header{padding:7px 10px}\r\n .texture-menu-header h2{display:none}\r\n .texture-menu-header select{min-height:42px;padding:8px}\r\n #panel-textures .texture-grid button{min-height:90px}\r\n}\r\n/* Short phones keep a full adaptation row even when installed Skins adds a menu row. */\r\n@media(max-width:700px) and (max-height:720px){\r\n .editor-workspace{grid-template-rows:minmax(140px,34%) minmax(0,1fr)}\r\n .menu-tabs{padding:4px}\r\n .menu-tabs>button{min-height:32px;padding:4px 6px}\r\n .texture-menu-header{padding:5px 10px}\r\n .texture-menu-header select{min-height:36px;padding:5px 8px}\r\n .console-status{max-height:48px;overflow-y:auto}\r\n}\r\n@media(min-width:701px) and (max-height:560px){\r\n .editor-shell{padding:6px;gap:4px}\r\n .editor-header{min-height:34px;padding-bottom:0}\r\n .header-actions button,.return-link{min-height:32px;padding:4px 8px}\r\n .menu-tabs{padding:4px}\r\n .menu-tabs>button{min-height:32px;padding:4px 6px;font-size:.8125rem}\r\n .texture-menu-header{padding:4px 8px;gap:8px}\r\n .texture-menu-header h2{display:none}\r\n .texture-menu-header select{min-height:32px;padding:4px 8px}\r\n .console-status{min-height:24px;max-height:42px;padding:3px 8px;overflow-y:auto}\r\n #panel-textures .texture-grid button{min-height:74px}\r\n #panel-textures .texture-grid img{width:32px;height:32px}\r\n}\r\n","creature/dance-pose.mjs":"// The Dance clock owns these samples. This module has no timer or renderer.\r\n// Authored coordinates: anatomical left is negative X, Y points down, shoulders\r\n// have width 1. The rigid coach arms convey direction; the outline conveys elbows.\r\nconst clamp=(value,min,max)=>Math.max(min,Math.min(max,value));\r\nconst valid=p=>p&&Number.isFinite(p.x)&&Number.isFinite(p.y);\r\nconst zero=()=>({position:[0,0,0],rotation:[0,0,0]});\r\n// Angles describe the requested visible direction, measured from screen-down.\r\n// A source mesh may already have a raised arm while its rig quaternion is identity.\r\nexport function danceArmRotation(targetAngle,restAngle=0){\r\n const delta=targetAngle-restAngle;return Math.atan2(Math.sin(delta),Math.cos(delta));\r\n}\r\n\r\n// Call only on a clean rig.rest pose, once for each assembled recipe. Distal\r\n// vertices identify the forelimb's direction without assuming a human skeleton.\r\nexport function measureDanceArmRest(rig){\r\n const angles={};if(!rig)return angles;\r\n rig.root.updateMatrixWorld(true);rig.root.traverse(node=>node.skeleton?.update());\r\n for(const name of ['ArmLeft','ArmRight']){\r\n  const arm=rig.nodes[name];if(!arm)continue;\r\n  const pivot=arm.getWorldPosition(arm.position.clone()),parentRotation=arm.parent.getWorldQuaternion(arm.quaternion.clone()).invert(),points=[];\r\n  let radius=0;\r\n  arm.traverse(mesh=>{\r\n   if(!mesh.isMesh)return;\r\n   const count=mesh.geometry.getAttribute('position')?.count||0;\r\n   for(let i=0;i<count;i++){\r\n    const point=mesh.getVertexPosition(i,arm.position.clone()).applyMatrix4(mesh.matrixWorld).sub(pivot).applyQuaternion(parentRotation);\r\n    radius=Math.max(radius,point.length());points.push(point);\r\n   }\r\n  });\r\n  const tip=arm.position.clone().set(0,0,0);let count=0;\r\n  for(const point of points)if(point.length()>=radius*.7){tip.add(point);count++;}\r\n  if(count&&Math.hypot(tip.x,tip.y)>1e-6)angles[name]=Math.atan2(tip.x,-tip.y);\r\n }\r\n return angles;\r\n}\r\nexport function mapDancePose({landmarks=[],mirrored=false,coachYaw=0}={}){\r\n const pose={BodyMotion:zero(),HeadMotion:zero(),ArmLeft:zero(),ArmRight:zero(),RootYaw:Number.isFinite(coachYaw)?Math.max(-Math.PI*2,Math.min(Math.PI*2,coachYaw)):0};\r\n const left=landmarks[11],right=landmarks[12];\r\n if(!valid(left)||!valid(right))return pose;\r\n const sign=mirrored?-1:1,mx=(left.x+right.x)/2,my=(left.y+right.y)/2;\r\n const roll=-Math.atan2(right.y-left.y,Math.abs(right.x-left.x)||1)*sign;\r\n pose.BodyMotion.position=[clamp(mx*sign,-.8,.8)*.3,clamp(-my,-.4,.4)*.2,0];\r\n pose.BodyMotion.rotation=[0,0,clamp(roll,-.3,.3)];\r\n const nose=landmarks[0];\r\n if(valid(nose))pose.HeadMotion.rotation=[0,clamp((nose.x-mx)*sign*.35,-.25,.25),clamp((nose.x-mx)*sign*.25,-.18,.18)];\r\n for(const [shoulderId,elbowId,wristId,name] of [[11,13,15,'ArmLeft'],[12,14,16,'ArmRight']]){\r\n  const shoulder=landmarks[shoulderId],elbow=landmarks[elbowId],wrist=landmarks[wristId];\r\n  if(!valid(shoulder)||(!valid(elbow)&&!valid(wrist)))continue;\r\n  // A bent wrist must not cancel the visible upper arm's broad reach.\r\n  const end=valid(wrist)&&valid(elbow)?{x:elbow.x*.3+wrist.x*.7,y:elbow.y*.3+wrist.y*.7,z:(elbow.z||0)*.3+(wrist.z||0)*.7}:valid(wrist)?wrist:elbow;\r\n  const dx=(end.x-shoulder.x)*sign,dy=end.y-shoulder.y;\r\n  const destination=mirrored?(name==='ArmLeft'?'ArmRight':'ArmLeft'):name;\r\n  if(Math.hypot(dx,dy)<1e-5)continue;\r\n  // Rotating a downward arm around Z: X=sin(angle), Y=-cos(angle).\r\n  const angle=Math.atan2(Math.abs(dx)<1e-6?(destination==='ArmLeft'?-1e-6:1e-6):dx,dy);\r\n  pose[destination].rotation=[clamp(-((end.z||0)-(shoulder.z||0))*.45,-.65,.65),0,angle-pose.BodyMotion.rotation[2]];\r\n }\r\n return pose;\r\n}\r\n\r\n// Start from rig.rest on every application: repeated frames cannot accumulate\r\n// rotations, mixer strength, blinking, sleep breathing or phone-physics offsets.\r\nexport function applyDancePose(rig,pose,height=1,armRestAngles={}){\r\n if(!rig)return;\r\n rig.root.position.set(0,0,0);rig.root.quaternion.identity();if(Number.isFinite(pose?.RootYaw)&&pose.RootYaw!==0)rig.root.rotation.y=pose.RootYaw;rig.root.visible=true;\r\n for(const [name,node] of Object.entries(rig.nodes)){\r\n  const rest=rig.rest[name];if(!rest)continue;\r\n  node.position.copy(rest.position);node.quaternion.copy(rest.quaternion);node.scale.copy(rest.scale);\r\n  const sample=pose?.[name];if(!sample)continue;\r\n  node.position.x+=sample.position[0]*height;node.position.y+=sample.position[1]*height;node.position.z+=sample.position[2]*height;\r\n  const roll=name==='ArmLeft'||name==='ArmRight'?danceArmRotation(sample.rotation[2],armRestAngles[name]||0):sample.rotation[2];\r\n  node.rotateX(sample.rotation[0]);node.rotateY(sample.rotation[1]);node.rotateZ(roll);\r\n }\r\n rig.root.updateMatrixWorld(true);\r\n}\r\n","creature/index.html":"<!doctype html>\r\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta name=\"theme-color\" content=\"#46374f\"><title>Customize your coach</title><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\"}}</script><script type=\"module\" src=\"/audio/standalone-sound.mjs\"></script><script type=\"module\" src=\"/creature/assets/account-bridge.js\"></script><script type=\"module\" src=\"/creature/assets/editor.js?v=rank5\"></script><link rel=\"stylesheet\" href=\"./creature.css?v=coach-window-r32\"><link rel=\"stylesheet\" href=\"./preview-window.css?v=coach-window-r32\"><style>.preview-strip{position:absolute;left:8px;right:8px;top:4px;z-index:2;margin:0;padding:3px 10px;border:1px solid #ffe3a9;border-radius:999px;background:#1d1424;color:#ffe3a9;font-size:.75rem;text-align:center;pointer-events:none}#colorSwatches button[data-locked],#shipColourRow button[data-locked]{font-size:.8125rem;line-height:1;text-shadow:0 0 3px #000}/* W2-2A (#5): the app's bottom bar, linking back into /pose.html#<route>. */:root{--myr5-bar:calc(64px + env(safe-area-inset-bottom))}.editor-shell{height:calc(100dvh - var(--myr5-bar) - 16px)}.coach-dock{position:fixed;inset:auto 0 0;z-index:40;box-sizing:border-box;height:var(--myr5-bar);display:grid;grid-template-columns:repeat(2,minmax(0,1fr)) 58px repeat(3,minmax(0,1fr));align-items:center;gap:3px;padding:0 6px env(safe-area-inset-bottom);border-top:1px solid #685077;background:#171123f2;box-shadow:0 -6px 18px #08050f99;font:600 12px/1.1 system-ui,sans-serif}@media(max-width:400px){.coach-dock{font-size:11px}}.coach-dock a{box-sizing:border-box;display:flex;align-items:center;justify-content:center;min-width:0;height:46px;padding:0 1px;border:1px solid #70547c;border-radius:9px;background:#30223e;color:#f2deab;text-align:center;text-decoration:none;hyphens:auto}.coach-dock a:focus-visible{outline:3px solid #ffb24d;outline-offset:2px}.coach-dock .dock-portal{justify-self:center;width:56px;height:56px;padding:0;border:0;border-radius:50%;color:#fff;background:radial-gradient(circle,#1c1428 0 60%,transparent 62%),conic-gradient(#ff5f1f,#ffff33,#39ff14,#1f51ff,#b026ff,#ff10f0,#ff5f1f);box-shadow:0 0 12px #b026ff80}</style><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head>\r\n<body><main class=\"editor-shell\">\r\n<header class=\"editor-header\"><a class=\"editor-brand\" href=\"/pose.html\" aria-label=\"Back to coach\">MOM<span>INC.</span></a><div class=\"editor-title\"><h1>Customize</h1></div><div class=\"header-actions\"><button id=\"undo\" aria-label=\"Undo\" disabled>↶</button><button id=\"redo\" aria-label=\"Redo\" disabled>↷</button><a class=\"button return-link\" href=\"/pose.html\">Done ↗</a></div></header>\r\n<div class=\"editor-workspace\">\r\n <section class=\"preview-bay\" aria-label=\"Coach preview\"><div class=\"preview-readout\"><span id=\"coachPathSymbol\" aria-hidden=\"true\"></span><strong id=\"coachDisplayName\"></strong><button id=\"coachUnlock\" type=\"button\" aria-label=\"Coach unlock requirement\" hidden>&#128274;</button><span id=\"motionLabel\" hidden></span></div><div id=\"creatureStage\" class=\"preview-stage\"></div><div class=\"preview-toolbar\"><div class=\"zoom-row\"><button type=\"button\" id=\"zoomOut\" aria-label=\"Zoom out\" title=\"Zoom out\">&minus;</button><input id=\"zoom\" type=\"range\" min=\"0\" max=\"1\" step=\"0.02\" value=\"0\" aria-label=\"Zoom\"><button type=\"button\" id=\"zoomIn\" aria-label=\"Zoom in\" title=\"Zoom in\">+</button></div><div class=\"view-buttons\"><button id=\"front\">Front</button><button id=\"back\">Back</button><button id=\"pauseMotion\" aria-pressed=\"false\">Pause</button></div></div></section>\r\n <section class=\"design-console\" aria-label=\"Coach design controls\"><div class=\"console-tabs\"><div class=\"menu-tabs\" role=\"tablist\" aria-label=\"Customizer menus\"><button type=\"button\" id=\"tab-body\" role=\"tab\" aria-controls=\"panel-body\" aria-selected=\"true\" tabindex=\"0\" data-menu=\"body\">Species</button><button type=\"button\" id=\"tab-textures\" role=\"tab\" aria-controls=\"panel-textures\" aria-selected=\"false\" tabindex=\"-1\" data-menu=\"textures\">Textures</button><button type=\"button\" id=\"tab-materials\" role=\"tab\" aria-controls=\"panel-materials\" aria-selected=\"false\" tabindex=\"-1\" data-menu=\"materials\">Colour</button><button type=\"button\" id=\"tab-face\" role=\"tab\" aria-controls=\"panel-face\" aria-selected=\"false\" tabindex=\"-1\" data-menu=\"face\">Face</button><button type=\"button\" id=\"tab-ship\" role=\"tab\" aria-controls=\"panel-ship\" aria-selected=\"false\" tabindex=\"-1\" data-menu=\"ship\">Ship</button><button type=\"button\" id=\"tab-files\" role=\"tab\" aria-controls=\"panel-files\" aria-selected=\"false\" tabindex=\"-1\" data-menu=\"files\">Files</button></div><div class=\"texture-menu-header\"><h2>Textures</h2><label for=\"textureId\">Adaptations<select id=\"textureId\"></select></label></div><div class=\"console-scroll\">\r\n  <div id=\"panel-body\" role=\"tabpanel\" aria-labelledby=\"tab-body\" tabindex=\"0\"><div class=\"panel-heading\"><h2>Species</h2></div><div class=\"field-grid\"><label>Creature<select id=\"body\"></select></label><label id=\"fingersField\">Digits per hand<select id=\"fingers\"></select></label><label id=\"toesField\">Toes per foot<select id=\"toes\"></select></label></div><div class=\"range-stack\"><label id=\"furField\" class=\"range-field\" for=\"fur\">Collar fluff<output id=\"furValue\">1.00</output><input id=\"fur\" type=\"range\" min=\"0.65\" max=\"1.4\" step=\"0.05\"></label><label class=\"range-field\" for=\"detail\">Surface detail<output id=\"detailValue\">1.00</output><input id=\"detail\" type=\"range\" min=\"0.5\" max=\"1.5\" step=\"0.05\"></label></div><p id=\"digitsHelp\" class=\"help\">Digits include the thumb.</p></div>\r\n  <div id=\"panel-textures\" role=\"tabpanel\" aria-labelledby=\"tab-textures\" tabindex=\"0\" hidden><div class=\"panel-heading\"><h2>Textures</h2></div><div class=\"texture-preview-row\"><img id=\"texturePreview\" alt=\"Selected adaptation preview\" width=\"40\" height=\"40\" style=\"border-radius:6px;background:#20112b\"></div></div>\r\n  <div id=\"panel-materials\" role=\"tabpanel\" aria-labelledby=\"tab-materials\" tabindex=\"0\" hidden><div class=\"panel-heading\"><h2>Colours</h2></div><div id=\"colorSwatches\" aria-label=\"Colours and palettes\"></div><div class=\"panel-actions\"><button id=\"applyAll\">Use on all parts</button><button id=\"materialClear\">Reset to original</button></div></div>\r\n  <div id=\"panel-face\" role=\"tabpanel\" aria-labelledby=\"tab-face\" tabindex=\"0\" hidden><div class=\"panel-heading\"><h2>Face</h2></div><div class=\"field-grid\"><label>Eye arrangement<select id=\"eyeLayout\"></select></label><label>Expression<select id=\"eye\"></select></label><label>Pupil shape<select id=\"pupil\"></select></label></div><div class=\"eye-toggles\" role=\"group\" aria-label=\"Eye extras\"><label class=\"check-field\"><input id=\"blackSclera\" type=\"checkbox\">Black whites</label><label class=\"check-field\"><input id=\"colourPupil\" type=\"checkbox\">Coloured iris centre</label></div><div class=\"range-stack\"><label class=\"range-field\" for=\"iris\">Iris size<output id=\"irisValue\">1.00</output><input id=\"iris\" type=\"range\" min=\"0.7\" max=\"1.25\" step=\"0.05\"></label><label class=\"range-field\" for=\"pupilSize\">Pupil size<output id=\"pupilSizeValue\">1.00</output><input id=\"pupilSize\" type=\"range\" min=\"0.6\" max=\"1.15\" step=\"0.05\"></label></div></div>\r\n  <div id=\"panel-ship\" role=\"tabpanel\" aria-labelledby=\"tab-ship\" tabindex=\"0\" hidden><div class=\"panel-heading\"><h2>Ship</h2></div><div id=\"shipPreview\"><p id=\"shipPreviewStatus\" class=\"help\" role=\"status\" style=\"position:absolute;inset:auto 8px 6px;margin:0;text-align:center\"></p><button id=\"shipPreviewRetry\" type=\"button\" hidden style=\"position:absolute;top:8px;right:8px\">Try again</button></div><div class=\"field-grid\"><label>Coach ship<select id=\"shipPick\"></select></label></div><p id=\"shipDescription\" class=\"help\"></p><div id=\"shipColourRow\" role=\"group\" aria-label=\"Ship colours\"></div></div>\r\n  <div id=\"panel-files\" role=\"tabpanel\" aria-labelledby=\"tab-files\" tabindex=\"0\" hidden><div class=\"panel-heading\"><h2>Your designs</h2></div><div class=\"file-actions\">\r\n  <button id=\"exportRecipe\" class=\"primary\" aria-label=\"Download editable recipe\" title=\"Download editable recipe\">\r\n    <svg viewBox=\"0 0 24 24\" stroke=\"currentColor\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n      <path d=\"M12 4v11M7 11l5 5 5-5M5 20h14\"/>\r\n    </svg>\r\n  </button>\r\n  <label class=\"button\" for=\"importFile\" aria-label=\"Load game recipe\" title=\"Load game recipe\">\r\n    <svg viewBox=\"0 0 24 24\" stroke=\"currentColor\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n      <path d=\"M12 16V5M7 9l5-5 5 5M5 20h14\"/>\r\n    </svg>\r\n    <input id=\"importFile\" type=\"file\" accept=\".json,application/json\" class=\"file-input\">\r\n  </label>\r\n  <button id=\"exportGLB\" disabled aria-label=\"Download animated model · GLB\" title=\"Download animated model · GLB\">\r\n    <svg viewBox=\"0 0 24 24\" stroke=\"currentColor\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n      <path d=\"M21 7.5L12 2 3 7.5v9l9 5 9-5z\"/>\r\n      <polyline points=\"3 17.5 12 23 21 17.5\"/>\r\n      <polyline points=\"12 23 12 14.5\"/>\r\n    </svg>\r\n  </button>\r\n  <button id=\"original\" aria-label=\"Restore original coach\" title=\"Restore original coach\">\r\n    <svg viewBox=\"0 0 24 24\" stroke=\"currentColor\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n      <path d=\"M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5\"/>\r\n    </svg>\r\n  </button>\r\n</div><p class=\"help\"><a href=\"./NOTICE.md\">Open-source credits</a></p></div>\r\n </div></div><p id=\"creatureStatus\" class=\"console-status\" role=\"status\">Loading your coach…</p></section>\r\n</div></main><nav id=\"coachDock\" class=\"coach-dock\" aria-label=\"Coach tools\"><a class=\"dock-control dock-settings\" data-route=\"customizeCoach\" aria-label=\"Customize coach\" aria-current=\"page\" title=\"Customize coach\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><circle cx=\"12\" cy=\"12\" r=\"6.5\"/><path d=\"M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9 7 7M17 17l2.1 2.1M4.9 19.1 7 17M17 7l2.1-2.1\"/></svg></span></a><a class=\"dock-control dock-food\" data-route=\"food\" href=\"/pose.html#food\" aria-label=\"Food\" title=\"Food\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 8h4l2-3h6l2 3h4v11H3z\"/><circle cx=\"12\" cy=\"13\" r=\"3.5\"/></svg></span></a><a class=\"dock-portal\" data-route=\"portal\" href=\"/pose.html\" aria-label=\"Portal or workout pod\" title=\"Portal or workout pod\"><svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 11.5 12 4l9 7.5M5.5 10v10h13V10M10 20v-6h4v6\"/></svg><span aria-hidden=\"true\">PORTAL</span></a><a class=\"dock-control dock-scoreboard\" data-route=\"scoreboard\" href=\"/pose.html#scoreboard\" aria-label=\"Classroom leaderboard\" title=\"Classroom leaderboard\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"8\" r=\"3\"/><circle cx=\"17\" cy=\"9\" r=\"2.5\"/><path d=\"M3 20c0-4 3-6 6-6s6 2 6 6M15 14c3 0 6 1.5 6 5\"/></svg></span></a><a class=\"dock-control dock-achievements\" data-route=\"achievements\" href=\"/pose.html#achievements\" aria-label=\"Achievements\" title=\"Achievements\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 4h10v5a5 5 0 0 1-10 0zM7 6H3c0 3 1 4 4 4M17 6h4c0 3-1 4-4 4M12 14v4M8 21h8\"/></svg></span></a><a class=\"dock-control dock-pod\" data-route=\"reminders\" href=\"/pose.html#reminders\" aria-label=\"Reminders computer\" title=\"Reminders computer\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A15 15 0 0 1 3 6a2 2 0 0 1 2-2z\"/></svg></span></a><span class=\"dock-live\" role=\"status\" aria-live=\"polite\"></span></nav><script type=\"module\">import{mountStandaloneHousing}from'/modules/portal/standalone-housing.mjs';await mountStandaloneHousing();</script></body></html>\r\n","creature/models/roster/manifest.json":"[\r\n {\r\n  \"id\": \"roster/01-seed-pearo--3d_character_model\",\r\n  \"label\": \"Seed \\u00b7 Pearo 1\",\r\n  \"group\": \"Seed \\u00b7 Pearo\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1268160,\r\n  \"sha256\": \"6f14746278db647ccf761880725897d752022473966aaab63b80da448325507e\"\r\n },\r\n {\r\n  \"id\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\r\n  \"label\": \"Seed \\u00b7 Pearo 2\",\r\n  \"group\": \"Seed \\u00b7 Pearo\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1155552,\r\n  \"sha256\": \"49985ecd9a23fbad5f04560e0079b6dd25e7f9e50cbad7ba9e3f74025739f3dd\"\r\n },\r\n {\r\n  \"id\": \"roster/01-seed-pearo--white_3d_character_model\",\r\n  \"label\": \"Seed \\u00b7 Pearo 3\",\r\n  \"group\": \"Seed \\u00b7 Pearo\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1205556,\r\n  \"sha256\": \"b30e359d94fe30785ecb60df1f491b0f181243ed3c4e0224a413ea07eba2359d\"\r\n },\r\n {\r\n  \"id\": \"roster/01-seed-pearo--blank_humanoid_figure_3d_model\",\r\n  \"label\": \"Seed \\u00b7 Pearo 4\",\r\n  \"group\": \"Seed \\u00b7 Pearo\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1369432,\r\n  \"sha256\": \"2f7314ad14412bd9afff0e56c0c85a0438ea7e327d2b555a8997c0d72d87268d\"\r\n },\r\n {\r\n  \"id\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\r\n  \"label\": \"Seed \\u00b7 Pearo 5\",\r\n  \"group\": \"Seed \\u00b7 Pearo\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1060048,\r\n  \"sha256\": \"bfe377e02b5324683a43a2659639e8d1b810f6d237f0c0361e4bdc28cbefcd0f\"\r\n },\r\n {\r\n  \"id\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\r\n  \"label\": \"Taper \\u00b7 Tallstalk 1\",\r\n  \"group\": \"Taper \\u00b7 Tallstalk\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1146852,\r\n  \"sha256\": \"d40507bde2972770f4ba92cdf5c939b2909b77d68c63a4f5f397e0debeee1a99\"\r\n },\r\n {\r\n  \"id\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\r\n  \"label\": \"Taper \\u00b7 Tallstalk 2\",\r\n  \"group\": \"Taper \\u00b7 Tallstalk\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 739832,\r\n  \"sha256\": \"85b045f6ff4ad23d6a5a17074e4412047d465393b5023db96bff81a174809f08\"\r\n },\r\n {\r\n  \"id\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\r\n  \"label\": \"Taper \\u00b7 Tallstalk 3\",\r\n  \"group\": \"Taper \\u00b7 Tallstalk\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1511192,\r\n  \"sha256\": \"24308194976172bc3e523a09c0f1eafc4722e161f73a1636813c235442ca793f\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\r\n  \"label\": \"Pearl \\u00b7 Orb 1\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1051868,\r\n  \"sha256\": \"35eca4f9738d14f22f8bb33d1ad7ebdc6703873721d256d73b252f9d82003020\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--circle_center\",\r\n  \"label\": \"Pearl \\u00b7 Orb 2\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 949252,\r\n  \"sha256\": \"d47b82f8f7f06c96db52e4948f19d388a2ab00f1cb511573102841956173f601\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\r\n  \"label\": \"Pearl \\u00b7 Orb 3\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1313640,\r\n  \"sha256\": \"1e32a434aa538976cb2598b216ae17175437f19dde938626824c5bcf923ae139\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--robot_3d_model\",\r\n  \"label\": \"Pearl \\u00b7 Orb 4\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 397672,\r\n  \"sha256\": \"8b6dff121c1a752b9688b8cb140be61bdc0d612af3d386296b27b3993aa3faa0\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\r\n  \"label\": \"Pearl \\u00b7 Orb 5\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 913016,\r\n  \"sha256\": \"cc118fd1332b647308d7a7ba49d462da6b9c9c437334cd3179b50fdc48313664\"\r\n },\r\n {\r\n  \"id\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\r\n  \"label\": \"Pearl \\u00b7 Orb 6\",\r\n  \"group\": \"Pearl \\u00b7 Orb\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1677408,\r\n  \"sha256\": \"c284799cd30c3fa5636559a827ae1a8aa01ff3eabced3be2575822e47e11c968\"\r\n },\r\n {\r\n  \"id\": \"roster/04-crest-wedge--robot_creature_3d_model\",\r\n  \"label\": \"Crest \\u00b7 Wedge 1\",\r\n  \"group\": \"Crest \\u00b7 Wedge\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 943504,\r\n  \"sha256\": \"236098d9935147ce4de6087eb17b4cfefac6ff5261931f8730127d98eeade38d\"\r\n },\r\n {\r\n  \"id\": \"roster/04-crest-wedge--stylized_3d_character\",\r\n  \"label\": \"Crest \\u00b7 Wedge 2\",\r\n  \"group\": \"Crest \\u00b7 Wedge\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 802536,\r\n  \"sha256\": \"3f798914742d5e8a93746c9b8cd378b8461cd081a2fce2b2e54c43112333e8e8\"\r\n },\r\n {\r\n  \"id\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\r\n  \"label\": \"Slope \\u00b7 Bobble 1\",\r\n  \"group\": \"Slope \\u00b7 Bobble\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 409688,\r\n  \"sha256\": \"47fff716c8cf079f3a2861be8e454f33585ab66aeb8d1c098c0af4a368ed226b\"\r\n },\r\n {\r\n  \"id\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\r\n  \"label\": \"Ridge \\u00b7 Triad 1\",\r\n  \"group\": \"Ridge \\u00b7 Triad\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1172932,\r\n  \"sha256\": \"ff50e2d6be2ee692bba9c285ae1379ed769a3b01d0a02e309c633b9df96e9a94\"\r\n },\r\n {\r\n  \"id\": \"roster/06-ridge-triad--robot_3d_model1\",\r\n  \"label\": \"Ridge \\u00b7 Triad 2\",\r\n  \"group\": \"Ridge \\u00b7 Triad\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1288584,\r\n  \"sha256\": \"9c23f758fd2d04c1635430cf50723c7d551599e4b4317f7cd20b065442be7555\"\r\n },\r\n {\r\n  \"id\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\r\n  \"label\": \"Bulb \\u00b7 Sphereling 1\",\r\n  \"group\": \"Bulb \\u00b7 Sphereling\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1310520,\r\n  \"sha256\": \"8fd342cd955e59ced8f73c889ebc5934c3aefe1ed5531cb321fe89dc176aec75\"\r\n },\r\n {\r\n  \"id\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\r\n  \"label\": \"Bulb \\u00b7 Sphereling 2\",\r\n  \"group\": \"Bulb \\u00b7 Sphereling\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1365896,\r\n  \"sha256\": \"5c0741b81a8281d01a98d6244aa5be3a2cff39e05f50d182d801b0bc915f85ca\"\r\n },\r\n {\r\n  \"id\": \"roster/08-shard-asym--geometric_robot_3d_model\",\r\n  \"label\": \"Shard \\u00b7 Asym 1\",\r\n  \"group\": \"Shard \\u00b7 Asym\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1089924,\r\n  \"sha256\": \"e6d0ec623a8aaf07b3e88d845faabde2137d3449c513942411641bc908e92c9f\"\r\n },\r\n {\r\n  \"id\": \"roster/08-shard-asym--robot_3d_model4\",\r\n  \"label\": \"Shard \\u00b7 Asym 2\",\r\n  \"group\": \"Shard \\u00b7 Asym\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 910596,\r\n  \"sha256\": \"f2e1f83da490c8c24f533e0a75de537457f801ccf7782dad7569dbecfc0b4d58\"\r\n },\r\n {\r\n  \"id\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\r\n  \"label\": \"Shard \\u00b7 Asym 3\",\r\n  \"group\": \"Shard \\u00b7 Asym\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 819588,\r\n  \"sha256\": \"8889a8962d2c6666e6db39833b1269538ed40eaccc79d78f6b67c01d2f61b1e3\"\r\n },\r\n {\r\n  \"id\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\r\n  \"label\": \"Shard \\u00b7 Asym 4\",\r\n  \"group\": \"Shard \\u00b7 Asym\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1176148,\r\n  \"sha256\": \"45797349edb2b534de025ba87654244049d6056c2a45ec43fb6548714c6a1db4\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--3d_robot_model\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 1\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1403544,\r\n  \"sha256\": \"62ec21ce0a13fbd14d25c04850834d5486c0a8b1b94f050603f0f2e87a036157\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 2\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1176604,\r\n  \"sha256\": \"198d3bc78c470283e8d4947be8e8b22dc4b81f50c7f708fb8e8ea9de0fe5c14f\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 3\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 978344,\r\n  \"sha256\": \"b2a7c9a6c4c2f76e622827d9dc6d8d85a5a94ae3fe20b4868e772cddc2d66039\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--robot_3d_model2\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 4\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1092528,\r\n  \"sha256\": \"7b1e79e81b7266be36d335a8bea65b91152f427586d53d5d6545f90abc3e795d\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--robot_3d_model3\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 5\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 639812,\r\n  \"sha256\": \"4e174889fc91ebcdffb6fd28c1747dbc1b8b6a4c8d68db29981deb8dd6784499\"\r\n },\r\n {\r\n  \"id\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\r\n  \"label\": \"Monolith \\u00b7 Tanka 6\",\r\n  \"group\": \"Monolith \\u00b7 Tanka\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1177656,\r\n  \"sha256\": \"f135a2e874ccd388a31842d77b374027cdd38c67f24f4494cc3c83aebf074ca6\"\r\n },\r\n {\r\n  \"id\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\r\n  \"label\": \"Petal \\u00b7 Wisp 1\",\r\n  \"group\": \"Petal \\u00b7 Wisp\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1036744,\r\n  \"sha256\": \"96a1bba31bb3587f61af255ad9b807dbabbd5715528f393a69a6bb0aa152a870\"\r\n },\r\n {\r\n  \"id\": \"roster/10-petal-wisp--ghost_character_3d_model\",\r\n  \"label\": \"Petal \\u00b7 Wisp 2\",\r\n  \"group\": \"Petal \\u00b7 Wisp\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 965284,\r\n  \"sha256\": \"db483101b0755c30a2a41f9edd9b3760b8ec10830b40d7832afd065b4edeb50a\"\r\n },\r\n {\r\n  \"id\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\r\n  \"label\": \"Petal \\u00b7 Wisp 3\",\r\n  \"group\": \"Petal \\u00b7 Wisp\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1266896,\r\n  \"sha256\": \"34e5de8eef218814b67985a35b80e3fd818d5951458e9ba8d226fdf30bca8eef\"\r\n },\r\n {\r\n  \"id\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\r\n  \"label\": \"Anvil \\u00b7 Cask 1\",\r\n  \"group\": \"Anvil \\u00b7 Cask\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 867228,\r\n  \"sha256\": \"d2797026da4cb563bae6868fc766e99f4ecd9a90d3b915481c428b6eca127d7a\"\r\n },\r\n {\r\n  \"id\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\r\n  \"label\": \"Seedpod \\u00b7 Snailslug 1\",\r\n  \"group\": \"Seedpod \\u00b7 Snailslug\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1337688,\r\n  \"sha256\": \"bd7821a4a794d82f7c2c7e683fea472243a57ede0df7ee2ce15dc4ab06853ed9\"\r\n },\r\n {\r\n  \"id\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\r\n  \"label\": \"Seedpod \\u00b7 Snailslug 2\",\r\n  \"group\": \"Seedpod \\u00b7 Snailslug\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1257688,\r\n  \"sha256\": \"cb57236887d0091be9cf642f3ce920dd36eed194611124cb6e10d0fd70fc5a25\"\r\n },\r\n {\r\n  \"id\": \"roster/13-fan-split--stylized_3d_character2\",\r\n  \"label\": \"Fan \\u00b7 Split 1\",\r\n  \"group\": \"Fan \\u00b7 Split\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1184572,\r\n  \"sha256\": \"caac851bc0d083a5401d7630e432b163364c468d70b9bc5150cf3be261828529\"\r\n },\r\n {\r\n  \"id\": \"roster/13-fan-split--stylized_toy_3d_model\",\r\n  \"label\": \"Fan \\u00b7 Split 2\",\r\n  \"group\": \"Fan \\u00b7 Split\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1113400,\r\n  \"sha256\": \"1a3cd58c92023e71ae614c1944be1ac464df19961fdd91830b63457137b93ca0\"\r\n },\r\n {\r\n  \"id\": \"roster/14-chisel-spire--cone_head_3d_model\",\r\n  \"label\": \"Chisel \\u00b7 Spire 1\",\r\n  \"group\": \"Chisel \\u00b7 Spire\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 551724,\r\n  \"sha256\": \"f5274c1a2b6c0655edb40fbbffdc1494cd9a8d14b9ebfd7c7943253c60015b65\"\r\n },\r\n {\r\n  \"id\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\r\n  \"label\": \"Chisel \\u00b7 Spire 2\",\r\n  \"group\": \"Chisel \\u00b7 Spire\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1149448,\r\n  \"sha256\": \"134350c0600ddf360256a598b000b68395109c689d252e96023eecd8ea68425e\"\r\n },\r\n {\r\n  \"id\": \"roster/15-orbital-coili--robot_character_3d_model\",\r\n  \"label\": \"Orbital \\u00b7 Coili 1\",\r\n  \"group\": \"Orbital \\u00b7 Coili\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1173644,\r\n  \"sha256\": \"9e0357bea8cdce4b57349480b1eed71052aecbae86ec16c7fb8825ead1fdce7a\"\r\n },\r\n {\r\n  \"id\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\r\n  \"label\": \"Spade \\u00b7 Arch 1\",\r\n  \"group\": \"Spade \\u00b7 Arch\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1210468,\r\n  \"sha256\": \"4f4beab075ab542092694b58878a21cb25d35843cf60dbc09df6b352a9775563\"\r\n },\r\n {\r\n  \"id\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\r\n  \"label\": \"Spade \\u00b7 Arch 2\",\r\n  \"group\": \"Spade \\u00b7 Arch\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 846680,\r\n  \"sha256\": \"07e6b5c32ea82dab6272de2ef98eb1b118c37604052a16af5211554101f23335\"\r\n },\r\n {\r\n  \"id\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\r\n  \"label\": \"Shellcap \\u00b7 Manyarm 1\",\r\n  \"group\": \"Shellcap \\u00b7 Manyarm\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 950740,\r\n  \"sha256\": \"4fc9344a8a00320864fbc38cff65aad3e624ca37e2c2f84346afa000a9139236\"\r\n },\r\n {\r\n  \"id\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\r\n  \"label\": \"Shellcap \\u00b7 Manyarm 2\",\r\n  \"group\": \"Shellcap \\u00b7 Manyarm\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 640472,\r\n  \"sha256\": \"7d3c5b780e2abad07437e669ff968e072c77ffb3ce31e6fda9f09b122bf12616\"\r\n },\r\n {\r\n  \"id\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\r\n  \"label\": \"Shellcap \\u00b7 Manyarm 3\",\r\n  \"group\": \"Shellcap \\u00b7 Manyarm\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 916444,\r\n  \"sha256\": \"667a8044bd0336e1dd6503425d37cdd1b5ca018ad9ce30c5d5ad635c7623c878\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--dragon_creature_3d_model\",\r\n  \"label\": \"Four-legged 1\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 393988,\r\n  \"sha256\": \"fa7986a0327fa42d32adc646b177b4618c34afb0bc595e8e5bec535787bc4025\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\r\n  \"label\": \"Four-legged 2\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1101604,\r\n  \"sha256\": \"d1db8a5c75f24b47ec8f1f999865653a61aaa0ca6e25b5d7e21c62c09fcbe2c4\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\r\n  \"label\": \"Four-legged 3\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 852444,\r\n  \"sha256\": \"71153ffef11c0913beff4c3dd0ce5989dd1d669c1ad060d577b1c7e8bc715dad\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--four-legged_robot_3d_model\",\r\n  \"label\": \"Four-legged 4\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 429424,\r\n  \"sha256\": \"8d6e16899a12a681eb881a894b569b025fce16d44d3c8043cf4fe95d48e5aa18\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\r\n  \"label\": \"Four-legged 5\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 896152,\r\n  \"sha256\": \"78fabcf15ac4bc7310b09813b6b1ef44e122a2d7b3c97a34c2923313ed5c684d\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--quadruped_robot_3d_model\",\r\n  \"label\": \"Four-legged 6\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1083792,\r\n  \"sha256\": \"31555c5c6feb4b3fa822bf146a4f67c9aeb303a8d80ece4afe0718d4d15c463b\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--robotic_dog_3d_model\",\r\n  \"label\": \"Four-legged 7\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1163368,\r\n  \"sha256\": \"2d44c01723234401c9dcdc6cfdd48076b61238ccc95bb2f3162c7acafdd1327e\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--stylized_quadruped_3d_model\",\r\n  \"label\": \"Four-legged 8\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 628136,\r\n  \"sha256\": \"de36f55a70200af71fbdeac590e3fe50a374237cd15a67e3273612ed53f7e123\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\r\n  \"label\": \"Four-legged 9\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1023004,\r\n  \"sha256\": \"d0fdc5a15981813ce9466a99b40cea4d0c44500d372426bd36621179aa870f1e\"\r\n },\r\n {\r\n  \"id\": \"roster/18-quad-all--wooden_robot_3d_model\",\r\n  \"label\": \"Four-legged 10\",\r\n  \"group\": \"Four-legged\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1033420,\r\n  \"sha256\": \"884191d06c05a20c8582e0c9609f820d3783b3b74bb3cb3e76a0270d56143b8b\"\r\n },\r\n {\r\n  \"id\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\r\n  \"label\": \"Genie \\u00b7 Multi 1\",\r\n  \"group\": \"Genie \\u00b7 Multi\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1232872,\r\n  \"sha256\": \"3fa8801fe1973881cba6f24cfcefb770a01e172b25ed1d6024d62593af80d1a5\"\r\n },\r\n {\r\n  \"id\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\r\n  \"label\": \"Genie \\u00b7 Multi 2\",\r\n  \"group\": \"Genie \\u00b7 Multi\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 486020,\r\n  \"sha256\": \"81516de182e04ccd2aff42982022720305003ae250731818e4c807130f86d7a0\"\r\n },\r\n {\r\n  \"id\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\r\n  \"label\": \"Genie \\u00b7 Multi 3\",\r\n  \"group\": \"Genie \\u00b7 Multi\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 404608,\r\n  \"sha256\": \"c45fe5ed594bfe5618875a88b81287d4b78cd88ce4cbfcbfeab524fb199950e6\"\r\n },\r\n {\r\n  \"id\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\r\n  \"label\": \"Genie \\u00b7 Multi 4\",\r\n  \"group\": \"Genie \\u00b7 Multi\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1149168,\r\n  \"sha256\": \"1de1dc93f31ce81ebddba811ba9afc0720cddacf5a71d27d5a6ea3bb5bbefba6\"\r\n },\r\n {\r\n  \"id\": \"roster/20-lume--robotic_figure_3d_model\",\r\n  \"label\": \"Lume 1\",\r\n  \"group\": \"Lume\",\r\n  \"status\": \"PASS\",\r\n  \"bytes\": 1128192,\r\n  \"sha256\": \"52d5d72a96ccd8c4b1ae5edee5bfe133721089f8a103165e338d9ecf4ae51fd8\"\r\n },\r\n {\r\n  \"id\": \"roster/21-flyer--winged_humanoid_3d_model\",\r\n  \"label\": \"Flyer 1\",\r\n  \"group\": \"Flyer\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 970008,\r\n  \"sha256\": \"ed9bc2771849efd9989db8d345f19fb7c2977a67b7a5f99b539a3d1586af5db6\"\r\n },\r\n {\r\n  \"id\": \"roster/22-curve--stylized_alien_3d_model\",\r\n  \"label\": \"Curve 1\",\r\n  \"group\": \"Curve\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1066308,\r\n  \"sha256\": \"79fcc10757118ba9cbe43a104a903767db6ef3c6ba4b74f687b79968d9215f3c\"\r\n },\r\n {\r\n  \"id\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\r\n  \"label\": \"Curve 2\",\r\n  \"group\": \"Curve\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 408364,\r\n  \"sha256\": \"29a98f87a59d70162391f46cf84b8d512c3bfa13099ae5c5fba3d04b95eec7d0\"\r\n },\r\n {\r\n  \"id\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\r\n  \"label\": \"Blob 1\",\r\n  \"group\": \"Blob\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1084260,\r\n  \"sha256\": \"183f2eea968134b0b09e2b73e38457e4fd702f2b2ec7561f2421d563bc27357f\"\r\n },\r\n {\r\n  \"id\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\r\n  \"label\": \"Blob 2\",\r\n  \"group\": \"Blob\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 891128,\r\n  \"sha256\": \"50eb71a62fa688b8a154bb0ee045fba8d0b6a32d5c0870d3f97198716af99543\"\r\n },\r\n {\r\n  \"id\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\r\n  \"label\": \"Blob 3\",\r\n  \"group\": \"Blob\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 980380,\r\n  \"sha256\": \"39ab0f0b383a1b030e071973a392ad3d899fdf247f8ccc84c55d26d574aa1181\"\r\n },\r\n {\r\n  \"id\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\r\n  \"label\": \"Blob 4\",\r\n  \"group\": \"Blob\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 980212,\r\n  \"sha256\": \"aa15686e1e283593efd3c0f06c54c42bb0678700f4de0e83cce2b132dbf0e0fd\"\r\n },\r\n {\r\n  \"id\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\r\n  \"label\": \"Blob 5\",\r\n  \"group\": \"Blob\",\r\n  \"status\": \"REVIEW\",\r\n  \"bytes\": 1226672,\r\n  \"sha256\": \"ef657be035523e56ee1a5f00bdd79816d223355e981463e996154b54bdf95187\"\r\n }\r\n]","creature/phone.css":".myr5-companion-card{position:absolute;right:8px;top:8px;width:132px;z-index:3;display:flex;flex-direction:column;align-items:stretch;border:1px solid #70568b80;border-radius:12px;background:#21162bc9;overflow:hidden;backdrop-filter:blur(6px)}.myr5-companion-stage{height:190px;width:100%;position:relative;background:radial-gradient(ellipse at 50% 50%,#51386d99,transparent 75%)}.myr5-companion-stage canvas{position:static!important;width:100%!important;height:100%!important;pointer-events:none}.myr5-companion-status{font-size:11px;color:#dac3ee;text-align:center;padding:0 5px 3px}.myr5-customize{font-size:12px;min-height:36px;display:flex;align-items:center;justify-content:center;color:#edd197;text-decoration:none;background:#362642}.myr5-companion-card .myr5-hide{font-size:11px;min-height:30px;border:0;border-top:1px solid #5a4472;background:#20162e;color:#d5c6e1;border-radius:0;padding:5px}.myr5-companion-card [hidden]{display:none!important}#view #hud{max-width:calc(100% - 158px)}@media(max-height:580px){.myr5-companion-stage{height:120px}.myr5-companion-card{width:108px}#view #hud{max-width:calc(100% - 134px)}}\r\n","creature/power-anchors.mjs":"// Project cached sole points after the coach's existing render has updated its animated matrices.\r\n// Callers supply reusable Three.js vectors so this adds no per-frame vector allocation.\r\nexport function projectAvatarPowerAnchors(anchors, camera, rootVisible, worldPoint, ndcPoint) {\r\n  return (anchors ?? []).map(({ index, node, local }) => {\r\n    worldPoint.copy(local).applyMatrix4(node.matrixWorld);\r\n    ndcPoint.copy(worldPoint).project(camera);\r\n    const x = (ndcPoint.x + 1) * 0.5, y = (1 - ndcPoint.y) * 0.5;\r\n    const visible = !!rootVisible && node.visible !== false && Number.isFinite(x) && Number.isFinite(y)\r\n      && ndcPoint.x >= -1 && ndcPoint.x <= 1 && ndcPoint.y >= -1 && ndcPoint.y <= 1\r\n      && ndcPoint.z >= -1 && ndcPoint.z <= 1;\r\n    return { index, x, y, visible };\r\n  });\r\n}\r\n","creature/preview-window.css":"/* The coach has the whole pane; information sits on its glass instead of reserving space. */\r\n.preview-bay .preview-stage{position:absolute;inset:0;min-height:0}\r\n.preview-bay::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit;background:linear-gradient(115deg,#b8dcf51c 0%,#bdcee008 28%,transparent 43%,#796aab0c 76%,#150e2e24),linear-gradient(180deg,#a4c9e20e,transparent 36%,#140b2314);box-shadow:inset 0 1px 0 #d9eeff45,inset 0 0 20px #0b082640}\r\n.preview-bay .preview-readout{position:absolute;top:0;left:0;right:0;padding:10px 12px;background:linear-gradient(#1b142ad9,#1b142a00);z-index:3}\r\n.preview-bay .preview-toolbar{position:absolute;bottom:0;left:0;right:0;z-index:3;gap:6px;padding:8px 10px;background:linear-gradient(transparent,#191022c9 30%);border-top:0}\r\n.preview-bay .preview-readout,.preview-bay .preview-toolbar,.preview-bay .preview-strip{opacity:1;transition:opacity .25s ease}\r\n.preview-bay[data-idle=true] .preview-readout,.preview-bay[data-idle=true] .preview-strip{opacity:0;pointer-events:none}\r\n/* zoom, Front, Back and Pause stay visible and tappable when idle: hiding them let the first tap fall through to the canvas (R-1016 bug 4). */\r\n.preview-bay[data-idle=false] .preview-readout,.preview-bay[data-idle=false] .preview-strip{transition:none}\r\n.preview-bay .preview-strip{z-index:3}\r\n@media(prefers-reduced-motion:reduce){.preview-bay .preview-readout,.preview-bay .preview-toolbar,.preview-bay .preview-strip{transition:none}}\r\n","creature/source/cage.ts":"// W4-4E (#116, D45/D47): the customizer cage. futuristic-weapons.glb, decimated to one optional download packet\r\n// ('room-cage', scripts/offline-assets.mjs), shown around the live coach only once this phone has downloaded it.\r\n// It adds meshes to the editor's own CreatureViewer scene, so there is no second renderer or render loop; the\r\n// viewer already skips frames while the page is hidden or the stage is off screen. Any failure (no packet,\r\n// decoder, asset) removes the cage and leaves the flat customizer exactly as it was.\r\nimport * as T from 'three';\r\nimport {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader.js';\r\nimport {battlePassState} from '../../battle-pass.mjs';\r\nimport {availablePetChoices,selectedPetChoice,selectPetChoice,PET_CHOICE_SCOPE_LABEL} from '../../pet-choice.mjs';\r\nimport {mountWeaponWall} from './weapon-wall.mjs';\r\nimport {sparkle} from '../../unlock-seen.mjs';\r\nimport type {CreatureViewer} from './viewer';\r\n\r\nexport const CAGE_BASE='/pod/rooms/cage/';\r\nexport const CAGE_FILES=['cage.glb','draco_wasm_wrapper.js','draco_decoder.wasm'].map(file=>CAGE_BASE+file);\r\nexport type CageSection='overview'|'pedestal'|'pets'|'weapons'|'mirror'|'clothing';\r\n// The supplied cage is one Tripo mesh with no named bays, so each section is an explicit hit volume measured\r\n// from the decimated model (glTF units, y up): centre x,y,z · size x,y,z · yaw. Scale 8 turns the pedestal\r\n// (radius .18, top y .17) into the viewer's own 1.45 floor disc, with the coach's feet on its top.\r\ntype Volume=readonly [number,number,number,number,number,number,number];\r\nconst SCALE=8,PEDESTAL=[.02,.17,-.01] as const;\r\nconst VOLUMES:Record<Exclude<CageSection,'overview'>,Volume[]>={\r\n pedestal:[[.02,.42,-.01,.3,.5,.3,0]],\r\n pets:[[-.31,.15,.075,.1,.16,.1,0],[-.22,.15,.23,.1,.16,.1,0],[-.04,.15,.33,.1,.16,.1,0],[.16,.15,.3,.1,.16,.1,0],[.245,.15,.17,.1,.16,.1,0]],\r\n weapons:[[-.27,.48,-.24,.47,.45,.05,.54]],\r\n mirror:[[.345,.39,-.21,.39,.64,.05,-.88]],\r\n clothing:[[.06,.32,-.31,.16,.5,.1,0]],\r\n};\r\n// Where each view looks from (a direction from the section toward the camera) and what it keeps in frame.\r\nconst VIEWS:Record<CageSection,{from:[number,number,number];coach:boolean;pad:number}>={\r\n overview:{from:[0,.5,1],coach:true,pad:.5},\r\n pedestal:{from:[0,.18,1],coach:true,pad:1.02},\r\n pets:{from:[.1,.95,1],coach:false,pad:1},\r\n weapons:{from:[-.1,.2,1],coach:false,pad:1},\r\n mirror:{from:[-.7,.2,.7],coach:true,pad:1},\r\n clothing:{from:[0,.45,1],coach:true,pad:1},\r\n};\r\nexport const SECTIONS:{id:CageSection;label:string;name:string}[]=[\r\n {id:'overview',label:'Cage',name:'Whole cage'},\r\n {id:'pedestal',label:'Coach',name:'Coach pedestal: species'},\r\n {id:'pets',label:'Pets',name:'Pet cages'},\r\n {id:'weapons',label:'Weapons',name:'Weapon wall'},\r\n {id:'mirror',label:'Mirror',name:'Mirror: colours'},\r\n {id:'clothing',label:'Clothes',name:'Clothing bay'},\r\n];\r\n\r\n// Only this release's downloaded package counts (\"the 2D editor works until the packet is downloaded\"); an\r\n// on-demand fetch never pulls a megabyte into the customizer. Opening only names that exist creates nothing.\r\nexport async function cagePacketReady(){\r\n try{\r\n  if(!globalThis.caches)return false;\r\n  const names=(await caches.keys()).filter(name=>name.startsWith('myr5-package-'));\r\n  for(const url of CAGE_FILES){let found=false;for(const name of names)if(await(await caches.open(name)).match(url)){found=true;break;}if(!found)return false;}\r\n  return true;\r\n }catch{return false;}\r\n}\r\n\r\nconst CSS='.cage-bays{position:absolute;left:6px;right:6px;bottom:6px;z-index:2;display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;padding:2px}.cage-bays button{flex:1 1 auto;min-height:36px;min-width:44px;padding:4px 2px;font-size:.75rem;border-radius:7px;background:linear-gradient(#3b4f58,#22303a);border-color:#6fd6e3;color:#dff9ff;box-shadow:inset 0 1px #bff4ff33,0 2px 0 #0b1216}.cage-bays button[aria-pressed=true]{background:linear-gradient(#9ff2ff,#46b6c6);color:#07252b;border-color:#dffcff}.cage-offer{position:absolute;right:8px;bottom:8px;z-index:2;min-height:32px;padding:4px 10px;font-size:.75rem}#panel-bay ul{margin:8px 0;padding-left:20px}#panel-bay li+li{margin-top:4px}#panel-bay h3{margin:12px 0 4px;font-size:.95rem}.pet-choices{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0}.pet-choices button{min-height:44px;min-width:44px}.pet-choices button[aria-pressed=true]{outline:2px solid #6fd6e3}#panel-bay .bay-error{color:#ffb4a8}.gala-loadout{margin-top:14px;padding-top:8px;border-top:1px solid #6fd6e3}.gala-loadout .cage-weapon-wall{display:grid;gap:8px}.gala-loadout .cage-weapon-wall h2{display:none}.gala-loadout .cage-weapon-wall :is(select,input,button){min-height:44px}#panel-bay .bay-locked{color:var(--muted)}@media(max-width:700px){.editor-shell[data-cage=ready] .editor-workspace{grid-template-rows:minmax(220px,48%) minmax(0,1fr)}}';\r\n\r\nlet styled:HTMLStyleElement|null=null;\r\nexport function cageStyle(){if(!styled?.isConnected){styled=document.createElement('style');styled.textContent=CSS;document.head.append(styled);}}\r\n// onClose runs when the bay stops being shown (another bay, a console tab, or the cage going away).\r\n// The War Room's Gala bay passes bay and labels: its own menus replace the coach's tabs and bays for every section.\r\nexport type CageHooks={openTab:(menu:'body'|'materials')=>boolean;showBay:(title:string,kicker:string,body:Node,onClose?:()=>void)=>void;closeBay?:()=>void;tell:(text:string)=>void;\r\n bay?:(section:Exclude<CageSection,'overview'>)=>void;labels?:Partial<Record<CageSection,{label:string;name:string}>>;volumes?:Partial<Record<Exclude<CageSection,'overview'>,Volume[]>>};\r\n// What the cage needs from a stage: the coach's CreatureViewer, or the War Room's Gala stage.\r\nexport type CageViewer=Pick<CreatureViewer,'mount'|'renderer'|'camera'|'orbit'|'scene'|'bodyBounds'|'floorObjects'|'settings'|'resize'|'focused'>;\r\ntype Owned={id:string;name:string;boss:string;level:number;granted:boolean};\r\nfunction rewards(kind:'pet'|'weapon'):Owned[]{\r\n const seen=new Map<string,Owned>();\r\n for(const boss of battlePassState().bosses)for(const reward of boss.rewards)for(const item of reward.items)if(item.kind===kind){\r\n  const prior=seen.get(item.id);if(!prior||item.granted&&!prior.granted)seen.set(item.id,{id:item.id,name:item.name,boss:boss.name,level:reward.level,granted:!!item.granted});\r\n }\r\n return [...seen.values()];\r\n}\r\nconst el=<K extends keyof HTMLElementTagNameMap>(tag:K,text='',cls='')=>{const e=document.createElement(tag);if(text)e.textContent=text;if(cls)e.className=cls;return e;};\r\nconst ul=(rows:Owned[],cls:string,text:(i:Owned)=>string)=>{if(!rows.length)return null;const list=el('ul','',cls);for(const row of rows)list.append(el('li',text(row)));return list;};\r\nconst lockedList=(rows:Owned[])=>ul(rows,'bay-locked',i=>`🔒 ${i.name} · ${i.boss} level ${i.level}`);\r\nconst help=(text:string)=>el('p',text,'help');\r\n// Pet cages: a device-local preference over the pets this device has been granted (pet-choice.mjs). Every\r\n// render re-reads it, so a pet that is no longer granted drops back to None. Nothing is granted or saved to the coach.\r\nfunction petBay(body:HTMLElement,focusId?:string|null){\r\n const choices=availablePetChoices(),chosen=selectedPetChoice().petId,ids=new Set(choices.map(c=>c.id));\r\n const status=el('p',`${choices.find(c=>c.id===chosen)?.name??'None'}: ${PET_CHOICE_SCOPE_LABEL}`);status.setAttribute('role','status');status.dataset.petStatus='';\r\n const group=el('div','','pet-choices');group.setAttribute('role','group');group.setAttribute('aria-label','Pet choice');\r\n for(const choice of choices){\r\n  const b=el('button',choice.name+(choice.id===chosen?` · ${PET_CHOICE_SCOPE_LABEL}`:''));b.type='button';b.dataset.petChoice=choice.id??'';\r\n  b.setAttribute('aria-pressed',String(choice.id===chosen));\r\n  b.onclick=()=>{const saved=selectPetChoice(choice.id);petBay(body,choice.id);if(!saved){const e=el('p','This choice could not be saved. Storage is unavailable on this device.','bay-error');e.setAttribute('role','alert');body.prepend(e);}};\r\n  if(choice.id)sparkle(b,'pet',choice.id);\r\n  group.append(b);\r\n }\r\n const held=rewards('pet'),parts:(Node|null)[]=[status,group,lockedList(held.filter(p=>!ids.has(p.id))),\r\n  help('Only pets you have earned appear as choices. Your choice is kept on this device only; it grants nothing and does not change your coach. No pet appears with your coach yet.')];\r\n if(!held.length)parts.splice(2,0,help('No pets yet. Each workout path’s first boss gives one at level 4.'));\r\n body.replaceChildren(...parts.filter(Boolean) as Node[]);\r\n if(focusId!==undefined)(body.querySelector(`[data-pet-choice=\"${focusId??''}\"]`) as HTMLElement|null)?.focus();\r\n}\r\n// Battle-pass weapons are device rewards, shown as earned or locked. The Gala War Room loadout is a different,\r\n// account-owned thing: its form is the adapter's, saves only on its own button, and is disposed with the bay.\r\nfunction weaponBay(body:HTMLElement){\r\n const items=rewards('weapon'),owned=items.filter(i=>i.granted),reward=el('section');\r\n reward.append(el('h3','Battle-pass weapons'),el('p',owned.length?`You have earned ${owned.length} of ${items.length} on this device.`:'No weapons yet. Workout path bosses give them at levels 1 and 3.'));\r\n const earned=ul(owned,'bay-owned',i=>`${i.name} · earned`);owned.forEach((item,n)=>sparkle(earned!.children[n] as HTMLElement,'weapon',item.id));\r\n for(const list of [earned,lockedList(items.filter(i=>!i.granted))])if(list)reward.append(list);\r\n reward.append(help('These rewards are not Gala weapons and are never sent to the War Room.'));\r\n const gala=el('section','','gala-loadout');gala.dataset.galaLoadout='';gala.append(el('h3','Gala War Room loadout'),help('A separate, account-owned loadout for your account, alongside your earned rewards. It saves only when you press Save loadout, and needs a connection.'));\r\n const host=el('div');gala.append(host);body.append(reward,gala);\r\n let wall:ReturnType<typeof mountWeaponWall>|null=null;\r\n try{wall=mountWeaponWall({host});}catch{host.append(help('The Gala loadout is unavailable right now.'));}\r\n return ()=>{wall?.dispose();wall=null;};\r\n}\r\n// Pets and weapons here are device rewards; the pet choice is a device preference. Nothing writes the coach recipe,\r\n// and the only War Room call is the weapon form's own read and Save.\r\nexport function sectionBay(section:'pets'|'weapons'|'clothing'):[string,string,Node,(()=>void)?]{\r\n const body=el('div');\r\n if(section==='pets'){petBay(body);return ['Pet cages','PETS',body];}\r\n if(section==='weapons')return ['Weapon wall','WEAPONS',body,weaponBay(body)];\r\n body.append(help('Nothing to wear yet. This empty bay is kept for coach clothing when it arrives.'));return ['Clothing bay','CLOTHING',body];\r\n}\r\n\r\nexport function mountCage(viewer:CageViewer,hooks:CageHooks){\r\n const stage=viewer.mount,shell=stage.closest('.editor-shell') as HTMLElement|null;cageStyle();\r\n const bays=document.createElement('div');bays.className='cage-bays';bays.setAttribute('role','group');bays.setAttribute('aria-label','Cage sections');bays.hidden=true;stage.append(bays);\r\n const buttons=new Map<CageSection,HTMLButtonElement>();\r\n for(const s of SECTIONS){const b=document.createElement('button'),own=hooks.labels?.[s.id];b.type='button';b.textContent=own?.label??s.label;b.setAttribute('aria-label',own?.name??s.name);b.dataset.cageSection=s.id;b.setAttribute('aria-pressed','false');b.onclick=()=>select(s.id);bays.append(b);buttons.set(s.id,b);}\r\n let root:T.Group|null=null,disposed=false,current:CageSection|null=null,tween=0;const hits:T.Mesh[]=[],outlines=new Map<CageSection,T.LineSegments[]>();\r\n const set=(state:string)=>{if(shell)shell.dataset.cage=state;};\r\n const world=(x:number,y:number,z:number)=>new T.Vector3((x-PEDESTAL[0])*SCALE,(y-PEDESTAL[1])*SCALE,(z-PEDESTAL[2])*SCALE);\r\n function boxOf(section:CageSection){\r\n  const box=new T.Box3();\r\n  if(section==='overview'&&root)box.setFromObject(root);\r\n  for(const mesh of hits)if(mesh.userData.section===section)box.expandByObject(mesh);\r\n  if(VIEWS[section].coach&&!viewer.bodyBounds.isEmpty())box.union(viewer.bodyBounds);\r\n  return box;\r\n }\r\n function frame(section:CageSection){\r\n  const box=boxOf(section),center=box.getCenter(new T.Vector3()),radius=box.getSize(new T.Vector3()).length()/2,camera=viewer.camera;\r\n  const v=T.MathUtils.degToRad(camera.fov/2),h=Math.atan(Math.tan(v)*camera.aspect),distance=radius*VIEWS[section].pad/Math.sin(Math.min(v,h));\r\n  // Lift the framed bay above the button row that sits over the bottom of the stage.\r\n  center.y-=distance*Math.tan(v)*(bays.offsetHeight+6)/Math.max(1,stage.clientHeight);\r\n  return {target:center,position:center.clone().addScaledVector(new T.Vector3(...VIEWS[section].from).normalize(),distance)};\r\n }\r\n // A short camera glide (none with reduced or Still motion). It only moves the camera; the viewer draws.\r\n function move(section:CageSection){\r\n  cancelAnimationFrame(tween);tween=0;viewer.focused=null;\r\n  const to=frame(section),camera=viewer.camera,orbit=viewer.orbit;\r\n  if(viewer.settings.reduced){camera.position.copy(to.position);orbit.target.copy(to.target);orbit.update();return;}\r\n  const p0=camera.position.clone(),t0=orbit.target.clone(),start=performance.now();\r\n  const step=(now:number)=>{if(disposed)return;const k=Math.min(1,(now-start)/650),e=k<.5?2*k*k:1-(-2*k+2)**2/2;camera.position.lerpVectors(p0,to.position,e);orbit.target.lerpVectors(t0,to.target,e);orbit.update();tween=k<1?requestAnimationFrame(step):0;};\r\n  tween=requestAnimationFrame(step);\r\n }\r\n function select(section:CageSection){\r\n  if(!root||disposed)return;current=section;\r\n  for(const [id,b] of buttons)b.setAttribute('aria-pressed',String(id===section));\r\n  for(const [id,lines] of outlines)for(const line of lines)line.visible=id===section;\r\n  move(section);\r\n  if(hooks.bay){if(section==='overview')hooks.closeBay?.();else hooks.bay(section);}\r\n  else if(section==='pedestal')hooks.openTab('body');\r\n  else if(section==='mirror')hooks.openTab('materials');\r\n  else if(section!=='overview'){const [title,kicker,body,onClose]=sectionBay(section);hooks.showBay(title,kicker,body,onClose);}\r\n }\r\n // Tap (not drag) a bay in the scene: the same action as its button.\r\n const raycaster=new T.Raycaster(),canvas=viewer.renderer.domElement;let down:{x:number;y:number;t:number}|null=null;\r\n function pick(clientX:number,clientY:number):CageSection|null{\r\n  const r=canvas.getBoundingClientRect();if(!r.width||!r.height)return null;\r\n  raycaster.setFromCamera(new T.Vector2((clientX-r.left)/r.width*2-1,-(clientY-r.top)/r.height*2+1),viewer.camera);\r\n  return raycaster.intersectObjects(hits,false)[0]?.object.userData.section??null;\r\n }\r\n const onDown=(e:PointerEvent)=>{down={x:e.clientX,y:e.clientY,t:performance.now()};};\r\n const onUp=(e:PointerEvent)=>{const d=down;down=null;if(!d||!root||Math.hypot(e.clientX-d.x,e.clientY-d.y)>8||performance.now()-d.t>500)return;const section=pick(e.clientX,e.clientY);if(section)select(section);};\r\n canvas.addEventListener('pointerdown',onDown);canvas.addEventListener('pointerup',onUp);\r\n function teardown(){\r\n  cancelAnimationFrame(tween);tween=0;\r\n  if(root){viewer.scene.remove(root);root.traverse(node=>{const mesh=node as T.Mesh;mesh.geometry?.dispose();for(const m of ([] as T.Material[]).concat(mesh.material||[])){for(const value of Object.values(m))if((value as T.Texture)?.isTexture)(value as T.Texture).dispose();m.dispose();}});root=null;}\r\n  hooks.closeBay?.();hits.length=0;outlines.clear();viewer.floorObjects.forEach(o=>o.visible=true);bays.hidden=true;\r\n }\r\n function dispose(){if(disposed)return;disposed=true;teardown();canvas.removeEventListener('pointerdown',onDown);canvas.removeEventListener('pointerup',onUp);bays.remove();}\r\n set('loading');\r\n const ready=(async()=>{\r\n  const draco=new DRACOLoader();\r\n  try{\r\n   draco.setDecoderPath(CAGE_BASE);draco.setDecoderConfig({type:'wasm'});\r\n   const loader=new GLTFLoader();loader.setDRACOLoader(draco);\r\n   const gltf=await loader.loadAsync(CAGE_BASE+'cage.glb');\r\n   if(disposed){gltf.scene.traverse(node=>{(node as T.Mesh).geometry?.dispose();});return false;}\r\n   root=new T.Group();root.name='customizer-cage';\r\n   gltf.scene.scale.setScalar(SCALE);gltf.scene.position.copy(world(0,0,0));root.add(gltf.scene);\r\n   const hidden=new T.MeshBasicMaterial({visible:false}),edge=new T.LineBasicMaterial({color:0x7eeaff,transparent:true,opacity:.85});\r\n   for(const [section,volumes] of Object.entries({...VOLUMES,...hooks.volumes}) as [CageSection,Volume[]][])for(const [x,y,z,sx,sy,sz,yaw] of volumes){\r\n    const geometry=new T.BoxGeometry(sx*SCALE,sy*SCALE,sz*SCALE),mesh=new T.Mesh(geometry,hidden);\r\n    mesh.position.copy(world(x,y,z));mesh.rotation.y=yaw;mesh.userData.section=section;mesh.name='cage-'+section;\r\n    const line=new T.LineSegments(new T.EdgesGeometry(geometry),edge);line.visible=false;mesh.add(line);\r\n    hits.push(mesh);(outlines.get(section)??outlines.set(section,[]).get(section)!).push(line);root.add(mesh);\r\n   }\r\n   root.updateMatrixWorld(true);viewer.scene.add(root);viewer.floorObjects.forEach(o=>o.visible=false);\r\n   // The phone layout gives the stage more room once the cage is up; size the camera to it before framing.\r\n   bays.hidden=false;set('ready');viewer.resize();select('overview');\r\n   return true;\r\n  }catch(error){\r\n   if(disposed)return false;\r\n   teardown();bays.remove();set('failed');hooks.tell('The 3D cage could not load on this device. The customizer works as usual.');console.warn('Customizer cage unavailable',error);\r\n   return false;\r\n  }finally{draco.dispose();}\r\n })();\r\n return {ready,select,dispose,pick,get section(){return current;},\r\n  // Tests and the phone frame check: where a section's hit volume lands on screen right now.\r\n  project(section:CageSection){const mesh=hits.find(m=>m.userData.section===section);if(!mesh)return null;const p=mesh.getWorldPosition(new T.Vector3()).project(viewer.camera),r=canvas.getBoundingClientRect();return {x:r.left+(p.x+1)/2*r.width,y:r.top+(1-p.y)/2*r.height};}};\r\n}\r\n","creature/source/cinematic-shots.ts":"export const SHOTS = {\r\n opening: {duration:4800, gesture:'awaken', frames:[[-1.7,1.6,7.7,1.65],[-.6,2.25,7.2,1.82],[0,2.65,8.9,1.95]]},\r\n pre: {duration:3300, gesture:'ready', frames:[[.85,2.35,8.5,1.87],[.2,2.15,7.5,1.87],[0,2.65,8.9,1.95]]},\r\n post: {duration:3800, gesture:'victory', frames:[[-.6,2.1,7.4,2.1],[.25,2.35,8.1,2.1],[0,2.1,5.8,2.25]]}\r\n} as const;\r\nexport type Cinematic=keyof typeof SHOTS;\r\nexport function sampleShot(kind:Cinematic,elapsed:number){\r\n const s=SHOTS[kind],u=Math.max(0,Math.min(1,elapsed/s.duration)),segment=Math.min(1,Math.floor(u*2));\r\n const t=u*2-segment,e=t*t*(3-2*t),a=s.frames[segment],b=s.frames[segment+1];\r\n return a.map((v,i)=>v+(b[i]-v)*e);\r\n}\r\n","creature/source/conor-pelvis-ownership.ts":"import * as THREE from 'three';\r\nimport { triangleSubset } from './limb-ownership';\r\n\r\nconst CONOR_PELVIS_SOURCE='roster/05-slope-bobble--clay_humanoid_figure_3d_model';\r\nconst WAIST_Y=.715;\r\nconst WAIST_HALF_WIDTH=.68;\r\n\r\n/** Move the central lower torso bowl out of Conor's feet cut while leaving both leg tubes intact. */\r\nexport function correctConorPelvisOwnership(source:THREE.Group,sourceId:string):number{\r\n if(sourceId!==CONOR_PELVIS_SOURCE)return 0;\r\n source.updateWorldMatrix(true,true);\r\n const body=source.getObjectByName('body'),feet=source.getObjectByName('feet');\r\n if(!body||!feet)return 0;\r\n let bodyMaterial:THREE.Material|undefined;\r\n body.traverse(o=>{if(!bodyMaterial&&o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))bodyMaterial=o.material;});\r\n if(!bodyMaterial)return 0;\r\n const intoBody=body.matrixWorld.clone().invert();\r\n const meshes:THREE.Mesh[]=[];feet.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))meshes.push(o);});\r\n let moved=0;\r\n for(const mesh of meshes){\r\n  const geometry=mesh.geometry,position=geometry.getAttribute('position'),index=geometry.index,selected=new Set<number>();\r\n  const meshToWorld=mesh.matrixWorld;\r\n  const triangleCount=index?index.count/3:position.count/3;\r\n  for(let face=0;face<triangleCount;face++){\r\n   let x=0,y=0;\r\n   for(let k=0;k<3;k++){\r\n    const vertex=index?index.getX(face*3+k):face*3+k;\r\n    const p=new THREE.Vector3().fromBufferAttribute(position,vertex).applyMatrix4(meshToWorld);\r\n    x+=p.x/3;y+=p.y/3;\r\n   }\r\n   if(y>=WAIST_Y&&Math.abs(x)<WAIST_HALF_WIDTH)selected.add(face);\r\n  }\r\n  if(!selected.size)continue;\r\n  const bodyPart=triangleSubset(geometry,face=>selected.has(face)),remainder=triangleSubset(geometry,face=>!selected.has(face));\r\n  if(!bodyPart)continue;\r\n  bodyPart.applyMatrix4(intoBody.clone().multiply(meshToWorld));\r\n  moved+=selected.size;\r\n  if(remainder){mesh.geometry=remainder;geometry.dispose();}else{mesh.removeFromParent();geometry.dispose();}\r\n  const recovered=new THREE.Mesh(bodyPart,bodyMaterial);\r\n  recovered.name='Conor recovered central pelvis';\r\n  recovered.userData.region='body';\r\n  recovered.userData.conorRecoveredPelvisTriangles=selected.size;\r\n  body.add(recovered);\r\n }\r\n source.updateWorldMatrix(true,true);\r\n source.userData.conorRecoveredPelvisTriangles=moved;\r\n return moved;\r\n}\r\n","creature/source/creator/anatomy.ts":"import * as THREE from 'three';\r\nimport {EYE_LAYOUTS,EYE_REFERENCE,type EyeLayout} from './eye-layouts';\r\nimport {EYE_CENTER} from './eye-surface';\r\n// Layouts are authored around MYR5's head. `offset` moves the whole arrangement onto another head and\r\n// `scale` shrinks or grows spacing and eye size around the reference eye together. `surfaceZ`, set for heads\r\n// without MYR5's carved sockets, seats each front-facing eye on that surface instead of recessing it.\r\nexport function eyePosition([x,y,z,r,yaw]:readonly number[],offset:THREE.Vector3,scale:number,surfaceZ?:number){\r\n const px=EYE_REFERENCE.x+(x-EYE_REFERENCE.x)*scale+offset.x,py=EYE_REFERENCE.y+(y-EYE_REFERENCE.y)*scale+offset.y;\r\n const pz=surfaceZ!==undefined&&yaw===0?surfaceZ-r*scale*.55:EYE_REFERENCE.z+(z-EYE_REFERENCE.z)*scale+offset.z;\r\n return [px,py,pz];\r\n}\r\nexport function arrangeEyes(template:THREE.Group,layout:EyeLayout,offset:THREE.Vector3=new THREE.Vector3(),scale=1,surfaceZ?:number){\r\n const group=new THREE.Group();group.name='Eye arrangement';\r\n for(const [i,eye] of EYE_LAYOUTS[layout].eyes.entries()){\r\n  const [,,,r,yaw]=eye;const instance=new THREE.Group();instance.name='Eye '+(i+1);\r\n  instance.position.fromArray(eyePosition(eye,offset,scale,surfaceZ));\r\n  instance.rotation.y=THREE.MathUtils.degToRad(yaw);instance.scale.setScalar(r/.605*scale);\r\n  const content=template.clone(true);content.visible=true;content.position.copy(EYE_CENTER).negate();instance.add(content);group.add(instance);\r\n }\r\n return group;\r\n}\r\n","creature/source/creator/assemble.ts":"import {goldenCoach} from '../../../performance-progress.mjs';\r\nimport {arrangeEyes} from './anatomy';\r\nimport {EYE_LAYOUTS,EYE_REFERENCE,EYE_SCALE_DEFAULT} from './eye-layouts';\r\nimport {EYE_OVERRIDES} from './eye-overrides';\r\nimport {pupilGeometry} from './pupils';\r\nimport {getCoach} from './coaching';\r\nimport {prepareEyeMesh,conformEyeMesh,bloodshotVeins,LID_RADIUS} from './eye-surface';\r\nimport {sculptMaterial,growMaterial,applySparkle,applyPaletteSurface} from './material-language';\r\nimport {boneSockets,skeletalStructure,materialCollar,robotStructure} from './skeletal-anatomy';\r\nimport {colorTriad,resolveRegionMaterial,regionChoice} from './materials-registry';\r\nimport {applyInstalledSkin,loadInstalledSkinTextures,type InstalledSkin,type SkinTextureCache} from './skin-materials';\r\nimport {createCoachReliefBudget} from './material-refinement';\r\n\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader,type GLTF} from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/examples/jsm/libs/meshopt_decoder.module.js';\r\nimport {REGIONS,STYLES,type Region,type Design} from './design';\r\nimport {deformMesh} from './deform';\r\nimport {armBodyBandTriangles,armBodyTriangles,armFootTriangles,connectedTriangleComponents,detachedHandTriangles,groundedComponentsBelowBodyCut,triangleSubset} from '../limb-ownership';\r\nimport {correctQuadrupedOwnership} from '../quadruped-ownership';\r\nimport {correctRousArmOwnership} from '../rous-ownership';\r\nimport {inferGroundedSidePlaneX} from '../rig';\r\nimport {correctPetalWispArmOwnership} from '../petal-wisp-ownership';\r\nimport {correctConorPelvisOwnership} from '../conor-pelvis-ownership';\r\nimport {correctTorsoPanelOwnership} from '../torso-panel-ownership';\r\n\r\n// Adapted from the existing MYR5 AlienViewer. The app and editor use this assembly.\r\nconst modelBuffers=new Map<string,Promise<ArrayBuffer>>();\r\nfunction bytes(url:string){if(!modelBuffers.has(url))modelBuffers.set(url,fetch(url).then(response=>{if(!response.ok)throw Error('Creature model is unavailable.');return response.arrayBuffer();}).catch(error=>{modelBuffers.delete(url);throw error;}));return modelBuffers.get(url)!;}\r\n\r\ntype Fit={s:number;t:THREE.Vector3};\r\nconst clamp=(v:number,lo:number,hi:number)=>Math.max(lo,Math.min(hi,v));\r\n// These roster entries are continuous shell creatures. Their source \"arms\" are body\r\n// surface panels, and their broad bases are not separately articulated feet.\r\nconst BODY_OWNED_LIMBS=new Set([\r\n 'roster/12-seedpod-snailslug--stylized_slug_3d_model',\r\n 'roster/23-blob-texture-bodies--cute_blob_creature_3d_model',\r\n // Their broad body cuts already contain the real appendages. The nominal\r\n // arms are isolated shell strips/tips, so rotating those alone tears the shell.\r\n 'roster/12-seedpod-snailslug--stylized_worm_3d_model',\r\n 'roster/19-genie-multi--fantasy_creature_3d_model',\r\n 'roster/19-genie-multi--stylized_octopus_3d_model',\r\n]);\r\nconst BODY_OWNED_FEET=new Set([\r\n 'roster/12-seedpod-snailslug--stylized_worm_3d_model',\r\n 'roster/10-petal-wisp--ghost_character_3d_model',\r\n 'roster/10-petal-wisp--fantasy_creature_3d_model4',\r\n 'roster/19-genie-multi--fantasy_creature_3d_model',\r\n 'roster/19-genie-multi--stylized_octopus_3d_model',\r\n]);\r\nconst BLOB_SPLIT_OWNERSHIP='roster/23-blob-texture-bodies--blob_creature_3d_model';\r\nconst OLIVA_CENTER_BRIDGE='roster/06-ridge-triad--geometric_robot_3d_model1';\r\nconst PEC_UPPER_ARM_CHAIN='roster/08-shard-asym--geometric_robot_3d_model';\r\nconst BOLT_HAND_COMPONENTS='roster/07-bulb-sphereling--humanoid_robot_3d_model';\r\nconst REASPIRE_HIP_SLIVER='roster/14-chisel-spire--low_poly_robot_3d_model';\r\nconst MULTI_ARM_TORSO_EDGE='roster/19-genie-multi--multi-armed_humanoid_3d_model1';\r\nconst PEARL_RING_UNDERARM_STRIPS=new Set(['roster/03-pearl-orb-ring--circle_center','roster/03-pearl-orb-ring--ringed_humanoid_3d_model']);\r\nconst GHOST4_BODY_CONTACT_FRAGMENT='roster/10-petal-wisp--fantasy_creature_3d_model4';\r\nconst ROUS_SIDE_PLANE_SOURCE='roster/09-monolith-tanka--robot_3d_model2';\r\nfunction boxOf(object:THREE.Object3D|undefined){const box=new THREE.Box3();if(object){object.updateWorldMatrix(true,true);box.setFromObject(object);}return box;}\r\n/** Correct source-region arm cuts before fitting or applying chosen region styles. */\r\nexport function correctArmFootOwnership(source:THREE.Group,sourceId:string){\r\n if(sourceId.startsWith('roster/18-quad-all--'))return 0;\r\n source.updateWorldMatrix(true,true);const arms=source.getObjectByName('arms'),feet=source.getObjectByName('feet'),body=source.getObjectByName('body');if(!arms||!feet||!body)return 0;\r\n const feetMeshes:THREE.Mesh[]=[],bodyMeshes:THREE.Mesh[]=[];feet.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible)feetMeshes.push(o);});body.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible)bodyMeshes.push(o);});if(!feetMeshes.length||!bodyMeshes.length)return 0;\r\n const feetBox=new THREE.Box3();for(const mesh of feetMeshes)feetBox.expandByObject(mesh);\r\n const bodyBox=new THREE.Box3();for(const mesh of bodyMeshes)bodyBox.expandByObject(mesh);\r\n const footMaterial=feetMeshes.find(m=>!Array.isArray(m.material))?.material,bodyMaterial=bodyMeshes.find(m=>!Array.isArray(m.material))?.material;if(!footMaterial||Array.isArray(footMaterial)||!bodyMaterial||Array.isArray(bodyMaterial))return 0;\r\n let moved=0;const armMeshes:THREE.Mesh[]=[];arms.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))armMeshes.push(o);});\r\n  for(const mesh of armMeshes){let active=mesh.geometry;const blobSplit=sourceId===BLOB_SPLIT_OWNERSHIP;const footIds=blobSplit||BODY_OWNED_FEET.has(sourceId)?new Set<number>():armFootTriangles(mesh,feetBox,feetMeshes,{bodyLowerY:bodyBox.min.y});\r\n  // Pec-n0ld's arm-foot classifier selects a 581-face source-ARMS component,\r\n  // while the measured mirrored counterpart remains in arms. Keep the complete\r\n  // pair under arm ownership so both arms animate; match by source bounds and\r\n  // component size rather than assuming triangle indices.\r\n  if(sourceId===PEC_UPPER_ARM_CHAIN&&footIds.size){\r\n    const components=connectedTriangleComponents(mesh),componentBounds=(ids:number[])=>{const box=new THREE.Box3(),p=mesh.geometry.getAttribute('position'),index=mesh.geometry.index;for(const id of ids)for(let k=0;k<3;k++)box.expandByPoint(new THREE.Vector3().fromBufferAttribute(p,index?index.getX(id*3+k):id*3+k).applyMatrix4(mesh.matrixWorld));return box;},all=components.map(ids=>({ids,selected:ids.filter(id=>footIds.has(id)).length,box:componentBounds(ids)})),centerX=bodyBox.getCenter(new THREE.Vector3()).x,keep=new Set<number>();\r\n    for(const candidate of all){if(candidate.selected!==candidate.ids.length||candidate.ids.length<500)continue;const a=candidate.box;\r\n      for(const opposite of all){if(opposite===candidate||opposite.selected||opposite.ids.length<500)continue;const b=opposite.box,ratio=candidate.ids.length/opposite.ids.length;\r\n        const mirrored=Math.abs((a.min.x-centerX)+(b.max.x-centerX))<.045&&Math.abs((a.max.x-centerX)+(b.min.x-centerX))<.045;\r\n        const aligned=Math.abs(a.min.y-b.min.y)<.08&&Math.abs(a.max.y-b.max.y)<.08&&Math.abs(a.getSize(new THREE.Vector3()).x-b.getSize(new THREE.Vector3()).x)<.04;\r\n        if(mirrored&&aligned&&ratio>.85&&ratio<1.15){for(const id of candidate.ids)keep.add(id);break;}\r\n      }\r\n    }\r\n    for(const id of keep)footIds.delete(id);\r\n  }\r\n  if(footIds.size){const selected=triangleSubset(active,id=>footIds.has(id)),remaining=triangleSubset(active,id=>!footIds.has(id));if(selected){moved+=footIds.size;if(remaining){mesh.geometry=remaining;active.dispose();active=remaining;}else{mesh.removeFromParent();active.dispose();active=null as any;}\r\n   const intoFeet=feet.matrixWorld.clone().invert().multiply(mesh.matrixWorld);selected.applyMatrix4(intoFeet);const footMesh=new THREE.Mesh(selected,footMaterial);footMesh.name='Recovered foot section';footMesh.userData.recoveredArmFootTriangles=footIds.size;feet.add(footMesh);}}\r\n  if(!active)continue;\r\n    const triangleCount=(active.index?.count??active.getAttribute('position').count)/3;\r\n    const bodyOptions=sourceId===MULTI_ARM_TORSO_EDGE?{bodyUpperFraction:.425}:undefined;\r\n    // Pec-n0ld's 137- and 64-face side components close the upper arm that\r\n    // continues through the retained 581-face lower-arm component. They touch\r\n    // Body geometrically but belong to the arm chain in the source cut.\r\n    const tailCutIds=new Set<number>(),bodyIds=blobSplit?new Set(Array.from({length:triangleCount},(_,i)=>i)):sourceId===PEC_UPPER_ARM_CHAIN?new Set<number>():armBodyTriangles(mesh,bodyBox,bodyMeshes,bodyOptions);if(BODY_OWNED_FEET.has(sourceId)){const cutTolerance=Math.max(.002,(bodyBox.max.y-bodyBox.min.y)*.002);for(const id of groundedComponentsBelowBodyCut(mesh,bodyBox.min.y,feetBox,cutTolerance))if(!bodyIds.has(id)){bodyIds.add(id);tailCutIds.add(id);}\r\n      // Continuous tail creatures may contain small proximal web panels in the\r\n      // animated arms source. Move only faces whose complete corner set is\r\n      // within a tight source-surface tolerance of the body in its lower band;\r\n      // distal fingers and tentacle surfaces remain arm-owned.\r\n      for(const id of armBodyBandTriangles(mesh,bodyBox,bodyMeshes,{bodyUpperFraction:.45,contactTolerance:.03,allVerticesOnBody:true}))bodyIds.add(id);\r\n    }\r\n    if(PEARL_RING_UNDERARM_STRIPS.has(sourceId))for(const id of armBodyBandTriangles(mesh,bodyBox,bodyMeshes,{bodyUpperFraction:.45}))bodyIds.add(id);\r\n    // This second ghost has a detached 66-face plate touching the body's crown\r\n    // at a measured vertex seam; move just that complete component to BodyMotion.\r\n    if(sourceId===GHOST4_BODY_CONTACT_FRAGMENT){\r\n      const p=mesh.geometry.getAttribute('position'),index=mesh.geometry.index,bodyVertices:THREE.Vector3[]=[];\r\n      for(const bodyMesh of bodyMeshes){const bp=bodyMesh.geometry.getAttribute('position');for(let i=0;i<bp.count;i++)bodyVertices.push(new THREE.Vector3().fromBufferAttribute(bp,i).applyMatrix4(bodyMesh.matrixWorld));}\r\n      for(const component of connectedTriangleComponents(mesh)){if(component.length>100)continue;const box=new THREE.Box3(),vertices=new Map<number,THREE.Vector3>();\r\n        for(const id of component)for(let k=0;k<3;k++){const vi=index?index.getX(id*3+k):id*3+k;if(!vertices.has(vi))vertices.set(vi,new THREE.Vector3().fromBufferAttribute(p,vi).applyMatrix4(mesh.matrixWorld));box.expandByPoint(vertices.get(vi)!);}\r\n        if(box.min.y<bodyBox.max.y-.25||box.max.y>bodyBox.max.y+.02)continue;\r\n        let nearest=Infinity;for(const v of vertices.values())for(const bv of bodyVertices)nearest=Math.min(nearest,v.distanceTo(bv));\r\n        if(nearest<=.015)for(const id of component)bodyIds.add(id);\r\n      }\r\n    }\r\n    if(!bodyIds.size)continue;\r\n  const bodyPart=triangleSubset(active,id=>bodyIds.has(id)),armPart=triangleSubset(active,id=>!bodyIds.has(id));if(!bodyPart)continue;\r\n  moved+=bodyIds.size;if(armPart){mesh.geometry=armPart;active.dispose();}else mesh.removeFromParent();\r\n  const intoBody=body.matrixWorld.clone().invert().multiply(mesh.matrixWorld);bodyPart.applyMatrix4(intoBody);const bodyMesh=new THREE.Mesh(bodyPart,bodyMaterial);bodyMesh.name='Recovered torso section';bodyMesh.userData.recoveredArmBodyTriangles=bodyIds.size;if(tailCutIds.size)bodyMesh.userData.recoveredTailCutTriangles=tailCutIds.size;body.add(bodyMesh);if(!armPart)active.dispose();\r\n }\r\n source.updateWorldMatrix(true,true);return moved;\r\n}\r\n/** Move only verified detached hand components that were authored in a feet cut. */\r\nexport function correctFootHandOwnership(source:THREE.Group,sourceId:string){\r\n  if(BODY_OWNED_LIMBS.has(sourceId)||BODY_OWNED_FEET.has(sourceId)||sourceId.startsWith('roster/18-quad-all--'))return 0;\r\n source.updateWorldMatrix(true,true);const arms=source.getObjectByName('arms'),feet=source.getObjectByName('feet'),body=source.getObjectByName('body');if(!arms||!feet||!body)return 0;\r\n const meshes=(group:THREE.Object3D)=>{const out:THREE.Mesh[]=[];group.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))out.push(o);});return out;};\r\n const footMeshes=meshes(feet),armMeshes=meshes(arms),bodyMeshes=meshes(body);if(!footMeshes.length||!armMeshes.length||!bodyMeshes.length)return 0;\r\n const feetBox=new THREE.Box3(),armsBox=new THREE.Box3(),bodyBox=new THREE.Box3();for(const m of footMeshes)feetBox.expandByObject(m);for(const m of armMeshes)armsBox.expandByObject(m);for(const m of bodyMeshes)bodyBox.expandByObject(m);\r\n const bodyHeight=bodyBox.max.y-bodyBox.min.y;if(!(bodyHeight>0))return 0;\r\n const lowerBody=new THREE.Box3(),lowerCap=bodyBox.min.y+bodyHeight*.35;\r\n for(const mesh of bodyMeshes){const p=mesh.geometry.getAttribute('position'),index=mesh.geometry.index,count=index?.count??p.count;for(let i=0;i<count;i++){const v=new THREE.Vector3().fromBufferAttribute(p,index?index.getX(i):i).applyMatrix4(mesh.matrixWorld);if(v.y<=lowerCap)lowerBody.expandByPoint(v);}}\r\n if(lowerBody.isEmpty())return 0;\r\n let moved=0;\r\n for(const mesh of footMeshes){const selected=detachedHandTriangles(mesh,feetBox,footMeshes,armsBox,armMeshes,bodyBox,bodyMeshes,lowerBody,{maxComponentTriangles:sourceId===BOLT_HAND_COMPONENTS?1500:600});\r\n  if(!selected.size)continue;const original=mesh.geometry,hand=triangleSubset(original,id=>selected.has(id)),remaining=triangleSubset(original,id=>!selected.has(id));if(!hand)continue;\r\n  const p=original.getAttribute('position'),index=original.index,center=new THREE.Vector3();for(const id of selected)for(let k=0;k<3;k++)center.add(new THREE.Vector3().fromBufferAttribute(p,index?index.getX(id*3+k):id*3+k).applyMatrix4(mesh.matrixWorld));center.divideScalar(selected.size*3);\r\n  let armMaterialMesh=armMeshes[0],nearest=Infinity;for(const candidate of armMeshes){const box=new THREE.Box3().setFromObject(candidate),candidateCenter=box.getCenter(new THREE.Vector3()),distance=center.distanceToSquared(candidateCenter);if(distance<nearest){nearest=distance;armMaterialMesh=candidate;}}\r\n  moved+=selected.size;if(remaining){mesh.geometry=remaining;original.dispose();}else{mesh.removeFromParent();original.dispose();}\r\n  const intoArms=arms.matrixWorld.clone().invert().multiply(mesh.matrixWorld);hand.applyMatrix4(intoArms);const handMesh=new THREE.Mesh(hand,armMaterialMesh.material as THREE.Material);handMesh.name='Recovered hand section';handMesh.userData.recoveredFootHandTriangles=selected.size;arms.add(handMesh);\r\n }\r\n source.updateWorldMatrix(true,true);return moved;\r\n}\r\n/** Blob1's high, cross-center pelvis bowl is authored in `feet` but must follow BodyMotion. */\r\nexport function correctBlobPelvisOwnership(source:THREE.Group,sourceId:string){\r\n if(sourceId!==BLOB_SPLIT_OWNERSHIP&&sourceId!==OLIVA_CENTER_BRIDGE)return 0;\r\n source.updateWorldMatrix(true,true);const feet=source.getObjectByName('feet'),body=source.getObjectByName('body');if(!feet||!body)return 0;\r\n const feetMeshes:THREE.Mesh[]=[],bodyMeshes:THREE.Mesh[]=[];feet.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))feetMeshes.push(o);});body.traverse(o=>{if(o instanceof THREE.Mesh&&o.visible&&!Array.isArray(o.material))bodyMeshes.push(o);});\r\n if(!feetMeshes.length||!bodyMeshes.length)return 0;const bodyBox=new THREE.Box3();for(const mesh of bodyMeshes)bodyBox.expandByObject(mesh);const bodySize=bodyBox.getSize(new THREE.Vector3()),bodyCenter=bodyBox.getCenter(new THREE.Vector3());if(bodySize.x<=0||bodySize.y<=0)return 0;\r\n const bodyMaterial=bodyMeshes[0].material as THREE.Material;let moved=0;\r\n for(const mesh of feetMeshes){const components=connectedTriangleComponents(mesh),selected=new Set<number>();\r\n  for(const component of components){const box=new THREE.Box3(),center=new THREE.Vector3(),position=mesh.geometry.getAttribute('position'),index=mesh.geometry.index;\r\n   for(const id of component)for(let k=0;k<3;k++){const v=new THREE.Vector3().fromBufferAttribute(position,index?index.getX(id*3+k):id*3+k).applyMatrix4(mesh.matrixWorld);box.expandByPoint(v);center.add(v);}\r\n   center.divideScalar(component.length*3);const spansCenter=box.min.x<bodyCenter.x-bodySize.x*.08&&box.max.x>bodyCenter.x+bodySize.x*.08,contactsBodyBottom=box.max.y>=bodyBox.min.y-bodySize.y*.025&&box.min.y<bodyBox.min.y+bodySize.y*.04,central=Math.abs(center.x-bodyCenter.x)<bodySize.x*.18;\r\n   if(spansCenter&&contactsBodyBottom&&central)for(const id of component)selected.add(id);\r\n  }\r\n  if(!selected.size)continue;const original=mesh.geometry,part=triangleSubset(original,id=>selected.has(id)),remaining=triangleSubset(original,id=>!selected.has(id));if(!part)continue;\r\n  moved+=selected.size;if(remaining){mesh.geometry=remaining;original.dispose();}else{mesh.removeFromParent();original.dispose();}\r\n  const intoBody=body.matrixWorld.clone().invert().multiply(mesh.matrixWorld);part.applyMatrix4(intoBody);const bodyPart=new THREE.Mesh(part,bodyMaterial);bodyPart.name='Recovered center pelvis';bodyPart.userData.recoveredPelvisTriangles=selected.size;body.add(bodyPart);\r\n }\r\n source.updateWorldMatrix(true,true);return moved;\r\n}\r\n\r\nexport type RosterSourceRepairReport={armOwnershipTriangles:number;handOwnershipTriangles:number;pelvisTriangles:number;petalWispArmTriangles:number;conorPelvisTriangles:number;torsoPanelTriangles:number;quadrupedRepaired:boolean};\r\n/** Run the complete source-only ownership repair chain before models are mixed or fitted. */\r\nexport function repairRosterSource(source:THREE.Group,sourceId:string):RosterSourceRepairReport{\r\n const report:RosterSourceRepairReport={armOwnershipTriangles:0,handOwnershipTriangles:0,pelvisTriangles:0,petalWispArmTriangles:0,conorPelvisTriangles:0,torsoPanelTriangles:0,quadrupedRepaired:false};\r\n if(!sourceId.startsWith('roster/'))return report;\r\n report.armOwnershipTriangles=sourceId.startsWith('roster/18-quad-all--')?0:sourceId===ROUS_SIDE_PLANE_SOURCE?correctRousArmOwnership(source,sourceId):correctArmFootOwnership(source,sourceId);\r\n for(let pass=0;pass<4;pass++){const moved=correctFootHandOwnership(source,sourceId);report.handOwnershipTriangles+=moved;if(!moved)break;}\r\n report.pelvisTriangles=correctBlobPelvisOwnership(source,sourceId);\r\n report.quadrupedRepaired=correctQuadrupedOwnership(source,sourceId);\r\n report.petalWispArmTriangles=correctPetalWispArmOwnership(source,sourceId);\r\n report.conorPelvisTriangles=correctConorPelvisOwnership(source,sourceId);\r\n report.torsoPanelTriangles=correctTorsoPanelOwnership(source,sourceId);\r\n return report;\r\n}\r\n\r\nexport type InstalledSkinResolver=(id:string)=>Promise<InstalledSkin|null>;\r\n// `preview` paints locked textures/colours (the editor's unsaved look-before-you-unlock layer).\r\nexport async function assembleCreature(d:Design,assetBase:string,resolveInstalledSkin?:InstalledSkinResolver,preview=false){\r\n // The build meshopt-compresses these models (scripts/optimize-glb.mjs).\r\n const loader=new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);\r\n const look=(region:Region)=>resolveRegionMaterial(d.styles[region],regionChoice(d.materials,region),preview,d.body);\r\n const load=async(name:string)=>loader.parseAsync(await bytes(assetBase+'models/'+name+'.glb'),assetBase+'models/');\r\n // Each region can come from a different creature. A GLTF scene can only give each node away once,\r\n // so every distinct source id is parsed once and reused across the regions that name it.\r\n const from:Record<Region,string>={head:d.headFrom,eye:'myr5',collar:d.body,body:d.body,arms:d.armsFrom,feet:d.feetFrom};\r\n const extraIds=[...new Set(REGIONS.map(r=>from[r]).filter(id=>id!=='myr5'))];\r\n const [gltf,anatomy,hands,...extras]=await Promise.all([load('myr5'),load('anatomy'),load('hands-v2'),...extraIds.map(load)]);\r\n const installedSkins=new Map<Region,InstalledSkin>();\r\n if(resolveInstalledSkin)for(const region of REGIONS){const id=d.materials?.[region]?.textureId;if(!id?.startsWith('creature-'))continue;try{const skin=await resolveInstalledSkin(id);if(skin?.id===id)installedSkins.set(region,skin);}catch{/* Missing, revoked, offline, or untrusted optional packs fail closed to built-in materials. */}}\r\n const skinTextures=new Set<THREE.Texture>(),skinTextureCache:SkinTextureCache=new Map();\r\n const sources=new Map<string,GLTF>([['myr5',gltf],...extraIds.map((id,i)=>[id,extras[i]] as [string,GLTF])]);\r\n const scene=(id:string)=>sources.get(id)!.scene;\r\n\r\n // Repair only roster-sourced arms. Their lower fragments are detected against the same\r\n // model's actual feet cut boundary before sources are mixed or material styling is applied.\r\n // If another coach supplies feet, the selected arm source's recovered feet are excluded;\r\n // the chosen feet source owns that region in the assembled coach.\r\n for(const id of extraIds)repairRosterSource(scene(id),id);\r\n\r\n // Fit every limb region onto the chosen body. Boxes are measured in each source's own space before\r\n // any node moves. Pure single-source creatures (the original MYR5 included) keep identity transforms.\r\n const mixed=new Set([d.body,d.headFrom,d.armsFrom,d.feetFrom]).size>1;\r\n const fits={} as Record<Region,Fit>;for(const r of REGIONS)fits[r]={s:1,t:new THREE.Vector3()};\r\n const bodyBox=boxOf(scene(d.body).getObjectByName('body'));\r\n const headBox=boxOf(scene(d.headFrom).getObjectByName('head'));\r\n if(mixed){\r\n  const size=new THREE.Vector3(),own=new THREE.Vector3(),c=new THREE.Vector3(),oc=new THREE.Vector3();bodyBox.getSize(size);bodyBox.getCenter(c);\r\n  const fit=(id:string,anchorY:(b:THREE.Box3)=>number):Fit=>{\r\n   const ob=boxOf(scene(id).getObjectByName('body'));ob.getSize(own);ob.getCenter(oc);\r\n   const s=clamp(size.x/Math.max(own.x,1e-3),.6,1.6);\r\n   return {s,t:new THREE.Vector3(c.x-oc.x*s,anchorY(bodyBox)-anchorY(ob)*s,c.z-oc.z*s)};\r\n  };\r\n  fits.head=fit(d.headFrom,b=>b.max.y);fits.arms=fit(d.armsFrom,b=>b.max.y);fits.feet=fit(d.feetFrom,b=>b.min.y);\r\n  // Stand the assembled creature back on the floor.\r\n  const feetBottom=boxOf(scene(d.feetFrom).getObjectByName('feet')).min.y*fits.feet.s+fits.feet.t.y;\r\n  for(const r of REGIONS)fits[r].t.y-=feetBottom;\r\n }\r\n fits.eye=fits.head;\r\n\r\n // Eyes: the proven layout table is tuned to MYR5's head. Move it to the chosen head's eye anchor and\r\n // scale it by head width so the layouts keep their proportions on every head.\r\n const myr5HeadWidth=boxOf(gltf.scene.getObjectByName('head')).getSize(new THREE.Vector3()).x||1;\r\n const headWidth=headBox.getSize(new THREE.Vector3()).x*fits.head.s;\r\n const baseEyeScale=d.headFrom==='myr5'?fits.head.s:clamp(headWidth/myr5HeadWidth,.5,1.2);\r\n const eyeOverride=EYE_OVERRIDES[d.headFrom];\r\n // Global eye-scale multiplier (handoff §5, ~0.65) applies to roster heads only: MYR5's own head has eye\r\n // sockets carved for its native eye size (see eye-layouts.ts), so it stays at its own fitted scale.\r\n const globalEyeScale=d.headFrom==='myr5'?1:EYE_SCALE_DEFAULT;\r\n const eyeScale=eyeOverride?.eyeScale??baseEyeScale*globalEyeScale;\r\n const reference=new THREE.Vector3(EYE_REFERENCE.x,EYE_REFERENCE.y,EYE_REFERENCE.z);\r\n if(d.headFrom!=='myr5')scene(d.headFrom).updateMatrixWorld(true);\r\n const anchor=d.headFrom==='myr5'?null:scene(d.headFrom).getObjectByName('eye_anchor');\r\n const anchorWorld=anchor?anchor.getWorldPosition(new THREE.Vector3()):null;\r\n // flipX mirrors the head's own geometry (below); the anchor lives outside that node (a root sibling,\r\n // per the roster GLB contract) so its X has to be corrected here to keep the eyes on the mirrored side.\r\n if(anchorWorld&&eyeOverride?.flipX)anchorWorld.x=-anchorWorld.x;\r\n const eyeCenter=anchorWorld?anchorWorld.clone().sub(new THREE.Vector3(0,0,.605*eyeScale)):reference.clone();\r\n eyeCenter.multiplyScalar(fits.head.s).add(fits.head.t);\r\n // Roster heads have no carved sockets: remember their front surface so eyes sit on it, not inside it.\r\n const surfaceZ=anchorWorld?anchorWorld.z*fits.head.s+fits.head.t.z:undefined;\r\n // Anchor-derived offset first; a per-variant eyeOffset override (once the owner supplies one from the\r\n // before/after renders) nudges it afterward rather than replacing the anchor's own placement.\r\n const eyeOffset=eyeCenter.sub(reference);\r\n if(eyeOverride?.eyeOffset)eyeOffset.add(new THREE.Vector3(...eyeOverride.eyeOffset));\r\n const eyeMoved=eyeOffset.lengthSq()>1e-8||Math.abs(eyeScale-1)>1e-4;\r\n\r\n const root=new THREE.Group(),details=new THREE.Group(),coachRelief=createCoachReliefBudget();details.name='Style ornaments';root.add(details);\r\n // Keep source-local points on Rous's grounded-foot split until each selected region\r\n // has its final fit/facing transform. The region's own matrixWorld then accounts for\r\n // source offsets, fit translation/scale, and any local X mirror in one operation.\r\n const rousPlaneLocal:Partial<Record<'arms'|'feet',THREE.Vector3>>={};\r\n const rousPlaneSources:Partial<Record<'arms'|'feet',THREE.Object3D>>={};\r\n if(extraIds.includes(ROUS_SIDE_PLANE_SOURCE)){\r\n  const rousScene=scene(ROUS_SIDE_PLANE_SOURCE),feet=rousScene.getObjectByName('feet')??rousScene,footBox=boxOf(feet),feetPlane=inferGroundedSidePlaneX(feet);\r\n  const arms=rousScene.getObjectByName('arms'),armBox=boxOf(arms);\r\n  for(const region of ['arms','feet'] as const)if(d[region==='arms'?'armsFrom':'feetFrom']===ROUS_SIDE_PLANE_SOURCE){const sourceRegion=rousScene.getObjectByName(region),referenceBox=region==='arms'?armBox:footBox,point=new THREE.Vector3(feetPlane,referenceBox.getCenter(new THREE.Vector3()).y,referenceBox.getCenter(new THREE.Vector3()).z);if(sourceRegion){sourceRegion.updateWorldMatrix(true,false);rousPlaneSources[region]=sourceRegion;rousPlaneLocal[region]=sourceRegion.worldToLocal(point);}}\r\n }\r\n const materials:THREE.MeshStandardMaterial[]=[];\r\n    const regions={} as Record<Region,THREE.Group>;\r\n for(const region of REGIONS){const source=scene(from[region]).getObjectByName(region);if(!source){throw Error('A creature section could not load.');}\r\n    // Facing correction (handoff §5 flipX): mirror this region's own geometry in its local space. The eye\r\n    // region is excluded — it always comes from MYR5's own template (see `from.eye` above), never the\r\n    // flagged roster source, and the eye anchor's X is corrected separately above.\r\n    if(region!=='eye'&&EYE_OVERRIDES[from[region]]?.flipX){source.scale.x*=-1;source.traverse(o=>{const name=o.userData.rigNodeName;if(typeof name==='string'&&/^(Arm|Foot)(Left|Right)$/.test(name))o.userData.rigNodeName=name.endsWith('Left')?name.replace(/Left$/,'Right'):name.replace(/Right$/,'Left');});}\r\n    const wrapper=new THREE.Group();wrapper.name=region;root.add(wrapper);regions[region]=wrapper;\r\n    const bodyOwned=(region==='arms'||region==='feet')&&BODY_OWNED_LIMBS.has(from[region])||(region==='feet'&&BODY_OWNED_FEET.has(from[region]));\r\n    const materialRegion=bodyOwned?'body':region;\r\n    if(bodyOwned){const bodyWrapper=regions.body,fit=fits[region],bodyFit=fits.body,relative=new THREE.Group();relative.name='BodyOwned '+region;relative.position.copy(fit.t).sub(bodyFit.t).divideScalar(bodyFit.s);relative.scale.setScalar(fit.s/bodyFit.s);bodyWrapper.add(relative);relative.add(source);}\r\n    else wrapper.add(source);\r\n    source.traverse(o=>{if(o instanceof THREE.Mesh){if(region==='eye')prepareEyeMesh(o);o.userData.region=materialRegion;o.userData.basePosition=o.position.clone();o.userData.baseScale=o.scale.clone();o.material=(o.material as THREE.MeshStandardMaterial).clone();const m=o.material as THREE.MeshStandardMaterial;m.userData={baseColor:m.color.clone(),baseRough:m.roughness,baseMetal:m.metalness,name:m.name};materials.push(m);}});}\r\n   const lid=new THREE.Mesh(new THREE.SphereGeometry(LID_RADIUS,48,24,0,Math.PI*2,0,.57),new THREE.MeshStandardMaterial({color:STYLES[0].primary,roughness:.58}));lid.position.copy(reference);lid.name='Expression eyelid';lid.userData.region='eye';const eyeTemplate=regions.eye.children[0] as THREE.Group;eyeTemplate.add(lid);\r\n   // R25 eye styles: squinty adds a lower lid (below); bloodshot adds veins that conform to the globe like the iris.\r\n   if(d.eye==='bloodshot'){const veinMaterial=new THREE.MeshStandardMaterial({color:'#b0122b',roughness:.42,side:THREE.DoubleSide,polygonOffset:true,polygonOffsetFactor:-1});veinMaterial.userData={baseColor:veinMaterial.color.clone(),baseRough:.42,baseMetal:0,name:'Eye vein'};const veins=new THREE.Mesh(bloodshotVeins(),veinMaterial);veins.name='Eye vein';veins.userData={region:'eye',basePosition:veins.position.clone(),baseScale:veins.scale.clone()};eyeTemplate.add(veins);}\r\n   // MYR5's anatomy variants (eye-socket crowns, finger and toe counts) only apply to regions MYR5 supplies.\r\n   const variants=new Map<string,THREE.Object3D>();if(from.head==='myr5')variants.set('head_single',regions.head.children[0]);if(regions.arms.children[0])variants.set('original_arms',regions.arms.children[0]);if(regions.feet.children[0])variants.set('original_feet',regions.feet.children[0]);\r\n   for(const object of [...anatomy.scene.children].filter(o=>!o.name.startsWith('arms_')).concat([...hands.scene.children])){const region=object.name.split('_')[0] as Region;object.traverse(o=>{if(o instanceof THREE.Mesh){o.userData.region=region;o.userData.basePosition=o.position.clone();o.userData.baseScale=o.scale.clone();o.material=(o.material as THREE.MeshStandardMaterial).clone();const m=o.material as THREE.MeshStandardMaterial;m.userData={baseColor:m.color.clone(),baseRough:m.roughness,baseMetal:m.metalness,name:m.name};}});object.removeFromParent();variants.set(object.name,object);}\r\n\r\n const e={root,regions,details,lid,materials,variants,eyeTemplate,eyeCopies:null as THREE.Group|null};\r\n const hologram=false,parts=false;\r\n   if(e.eyeCopies){e.regions.eye.remove(e.eyeCopies);e.eyeCopies=null;}e.eyeTemplate.visible=true;\r\n  for(const [region,key] of [['head','head_'+d.eyeLayout],['arms','arms_'+d.fingers],['feet','feet_'+d.toes]] as const){if(from[region]!=='myr5')continue;const variant=e.variants.get(key);if(variant&&e.regions[region].children[0]!==variant){e.regions[region].clear();e.regions[region].add(variant);}if(region==='head')variant?.traverse(o=>{o.userData.eyeSockets=EYE_LAYOUTS[d.eyeLayout].eyes;});}\r\n  const fittedSidePlanes:Partial<Record<'arms'|'feet',number>>={};\r\n  for(const region of REGIONS){const style=look(region),group=e.regions[region];\r\n   // The eye region is placed by arrangeEyes below, so its wrapper stays at the origin.\r\n   group.position.set(0,0,0);group.scale.setScalar(1);if(region!=='eye'){group.position.copy(fits[region].t);group.scale.setScalar(fits[region].s);}\r\n   if((region==='arms'||region==='feet')&&rousPlaneLocal[region]){const sourceRegion=rousPlaneSources[region]!;sourceRegion.updateWorldMatrix(true,false);fittedSidePlanes[region]=sourceRegion.localToWorld(rousPlaneLocal[region]!.clone()).x;}\r\n   if(parts){const offsets:Record<Region,number[]>={head:[0,.65,0],eye:[0,.18,1.0],collar:[0,-.1,0],body:[0,-.45,0],arms:[.45,0,0],feet:[0,-.65,0]};group.position.add(new THREE.Vector3().fromArray(offsets[region]));}\r\n   group.traverse(o=>{if(!(o instanceof THREE.Mesh)||o===e.lid)return;const m=o.material as THREE.MeshStandardMaterial;const base=m.userData.baseColor as THREE.Color;const name=m.userData.name as string;const fixed=['Eye ivory','Pupil','Eye glint','Eye vein'].includes(name);\r\n    m.color.copy(base);m.emissive.set(0);m.metalness=m.userData.baseMetal;m.roughness=m.userData.baseRough;m.transparent=false;m.opacity=1;m.depthWrite=true;\r\n    if(style.id!==0&&!fixed){m.color.set(name==='Lilac hair'||name==='Raised scales'?style.accent:style.primary);if(name==='Socket shadow'||name==='Body velvet')m.color.multiplyScalar(.43);m.roughness=style.roughness;m.metalness=style.metalness;m.emissive.set(style.emissive).multiplyScalar(.25);}\r\n    if(hologram){m.color.set(fixed&&name==='Pupil'?'#261336':'#c9b0ea');m.emissive.set('#76518f');m.emissiveIntensity=.55;m.roughness=.25;m.metalness=.1;}\r\n    if(o.name==='Pupil'&&o.userData.pupilType!==d.pupil){o.geometry.dispose();o.geometry=pupilGeometry(d.pupil);o.userData.basePosition=new THREE.Vector3(.04,2.1475,1.183);o.userData.baseScale=new THREE.Vector3(1,1,1);o.userData.pupilType=d.pupil;}\r\n    if(o.name==='Iris'&&o.userData.irisType!==d.pupil){o.geometry.dispose();o.geometry=pupilGeometry(d.pupil);o.geometry.scale(1.48,1.48,1);o.userData.basePosition=new THREE.Vector3(.04,2.1475,1.133);o.userData.baseScale=new THREE.Vector3(1,1,1);o.userData.irisType=d.pupil;}\r\n    if(/^Iris.fiber/.test(o.name))o.visible=d.pupil==='round';\r\n    if(/^Crown.scale/.test(o.name))o.visible=d.styles[region]===0;\r\n    deformMesh(o,region,d);if(region==='eye')conformEyeMesh(o);else if(o.visible){const customSkin=d.materials?.[region]?.textureId?.startsWith('creature-')??false,alternate=from[region]!=='myr5'&&!customSkin;if(style.id!==0){sculptMaterial(o,style,1,d.detail,alternate?coachRelief:undefined);applySparkle(o.material as THREE.MeshPhysicalMaterial,style.sparkle);}}\r\n    if(o.name==='Iris'){const p=o.geometry.attributes.position,colors=new Float32Array(p.count*3);for(let j=0;j<p.count;j++){const a=Math.atan2(p.getY(j),p.getX(j)),r=Math.hypot(p.getX(j),p.getY(j));const shade=.78+.16*Math.sin(a*117+r*35)+.06*Math.cos(a*61);colors[j*3]=shade;colors[j*3+1]=shade;colors[j*3+2]=Math.min(1,shade+.07);}o.geometry.setAttribute('color',new THREE.BufferAttribute(colors,3));m.vertexColors=true;m.needsUpdate=true;}\r\n    if(style.paletteId&&!fixed&&(region==='eye'&&o.name==='Iris'||region!=='eye'&&style.id===0))applyPaletteSurface(m,style);\r\n    // R25: bloodshot whites blush pink; blind eyes are milky, with no pupil or iris fibres.\r\n    if(region==='eye'&&d.eye==='bloodshot'&&name==='Eye ivory')m.color.lerp(new THREE.Color('#ff9c9c'),.28);\r\n    // R25 eye extras, on top of any style: black whites, and a centre in a light shade of the Eyes colour.\r\n    if(region==='eye'&&d.blackSclera&&name==='Eye ivory'){m.color.set('#09080c');m.roughness=.16;}\r\n    if(region==='eye'&&d.colourPupil&&o.name==='Pupil')m.color.set(style.id!==0?style.accent:STYLES[0].accent);\r\n    if(region==='eye'&&d.eye==='blind'){if(o.name==='Pupil'||/^Iris.fiber/.test(o.name))o.visible=false;if(o.name==='Iris'){m.map=null;m.color.set('#dfe7ea');m.roughness=.3;m.needsUpdate=true;}}\r\n   });\r\n  }\r\n  if(Object.keys(fittedSidePlanes).length)root.userData.limbSidePlanes=fittedSidePlanes;\r\n  const cap=({sleepy:1.56,wide:.30,anime:.22,squinty:1.28,bloodshot:.78,blind:.66} as Record<string,number>)[d.eye]??.57;e.lid.geometry.dispose();e.lid.geometry=new THREE.SphereGeometry(LID_RADIUS,48,24,0,Math.PI*2,0,cap);(e.lid.material as THREE.MeshStandardMaterial).color.set(hologram?'#c9b0ea':look('head').primary);\r\n  if(d.eye==='squinty'){const lower=new THREE.Mesh(new THREE.SphereGeometry(LID_RADIUS,48,24,0,Math.PI*2,0,1.2).rotateX(Math.PI),e.lid.material);lower.position.copy(e.lid.position);lower.name='Expression lower eyelid';lower.userData.region='eye';e.eyeTemplate.add(lower);}\r\n  if(d.eyeLayout!=='single'||eyeMoved){e.eyeCopies=arrangeEyes(e.eyeTemplate,d.eyeLayout,eyeOffset,eyeScale,surfaceZ);e.regions.eye.add(e.eyeCopies);e.eyeTemplate.visible=false;}\r\n  const key=JSON.stringify([d.styles,d.detail,d.eyeLayout,d.fingers,d.toes,d.body,d.headFrom,d.armsFrom,d.feetFrom,hologram,parts]);e.details.userData.key=key;\r\n  e.details.children.slice().forEach(o=>{o.traverse(c=>{if(c instanceof THREE.Mesh){c.geometry.dispose();(c.material as THREE.Material).dispose();}});e.details.remove(o);});\r\n  for(const region of REGIONS){\r\n   const style=look(region),original=e.regions[region];\r\n   // Bone and robot structures are sculpted around MYR5's own proportions, so roster parts keep their mesh.\r\n   const own=from[region]==='myr5';\r\n   if(own&&style.id===7&&region!=='head'&&region!=='eye'){original.visible=false;e.details.add(skeletalStructure(region,d));continue;}\r\n   if(own&&style.id===18&&['body','arms','feet'].includes(region)){original.visible=false;e.details.add(robotStructure(region,d));continue;}\r\n   let surface=original;\r\n   // materialCollar()/STYLES only know legacy ids 1-22 (0 and 20 are excluded on purpose above);\r\n   // Flat/Clay/battle-pass family ids (30, 31, -1) fall outside that range and must skip this swap.\r\n   if(own&&region==='collar'&&style.id>0&&style.id<=22&&style.id!==20){original.visible=false;surface=materialCollar(style.id);e.details.add(surface);}\r\n   const growth=growMaterial(surface,style,region,1,d.detail);growth.position.copy(surface.position);growth.scale.copy(surface.scale);e.details.add(growth);\r\n  }\r\n  if(from.head==='myr5'&&look('head').id===7){e.regions.eye.visible=false;e.details.add(boneSockets(d));}\r\n // Animation pivots fitted to this creature's measured parts; the rig falls back to MYR5's own numbers.\r\n root.updateMatrixWorld(true);\r\n const measured=(r:Region)=>boxOf(e.regions[r]);\r\n const pivots=d.body==='myr5'&&!mixed?null:(()=>{\r\n  const b=measured('body'),h=measured('head'),a=measured('arms'),f=measured('feet');\r\n  const hip=Math.max(.2,f.max.y),neck=Math.max(hip+.2,h.min.y),shoulder=a.isEmpty()?hip:clamp(a.max.y-(a.max.y-a.min.y)*.12,hip,neck);\r\n  return {body:[0,hip,0],head:[0,neck-hip,0],arm:a.isEmpty()?[0,0,0]:[Math.max(.15,b.max.x*.9),shoulder-hip,0],foot:f.isEmpty()?[0,0,0]:[Math.max(.08,(f.max.x-f.min.x)*.25),hip*.44,0]};\r\n })();\r\n // Retain unused variants for cleanup after geometry is baked into the animation rig.\r\n const disposeAssembly=()=>{const geometries=new Set<THREE.BufferGeometry>(),mats=new Set<THREE.Material>();for(const object of [root,...variants.values(),anatomy.scene,hands.scene,...[...sources.values()].map(s=>s.scene)])object.traverse(o=>{if(o instanceof THREE.Mesh){geometries.add(o.geometry);for(const m of Array.isArray(o.material)?o.material:[o.material])mats.add(m);}});geometries.forEach(g=>g.dispose());mats.forEach(m=>m.dispose());};\r\n for(const [region,skin]of installedSkins){const selected=regionChoice(d.materials,region),triad=colorTriad(selected?.colorId||'#7f7d78',preview,d.body)??colorTriad('#7f7d78')!,group=e.regions[region];\r\n  let meshes:THREE.Mesh[]=[];group.traverse(object=>{if(object instanceof THREE.Mesh)meshes.push(object);});\r\n  try{await loadInstalledSkinTextures(skin,skinTextures,skinTextureCache);}catch{/* Optional corrupted skin: this whole region keeps its built-in materials. */continue;}\r\n  const previous=meshes.map(mesh=>({mesh,material:mesh.material})),stagedMaterials=new Set<THREE.Material>();\r\n  const stage=(material:THREE.Material)=>{const clone=material.clone();clone.onBeforeCompile=material.onBeforeCompile;clone.customProgramCacheKey=material.customProgramCacheKey.bind(material);stagedMaterials.add(clone);return clone;};\r\n  for(const {mesh}of previous)mesh.material=Array.isArray(mesh.material)?mesh.material.map(stage):stage(mesh.material);\r\n  const results=await Promise.allSettled(meshes.map(mesh=>applyInstalledSkin(mesh,skin,triad,skinTextures,skinTextureCache))),failed=results.find(result=>result.status==='rejected');\r\n  if(failed?.status==='rejected'){for(const state of previous)state.mesh.material=state.material;stagedMaterials.forEach(material=>material.dispose());continue;}\r\n  const replaced=new Set<THREE.Material>();for(const state of previous)for(const material of Array.isArray(state.material)?state.material:[state.material])replaced.add(material);replaced.forEach(material=>material.dispose());\r\n }\r\n if(d.golden!==false&&goldenCoach(d.body))root.traverse(object=>{if(!(object instanceof THREE.Mesh))return;for(const material of Array.isArray(object.material)?object.material:[object.material]){const m=material as THREE.MeshStandardMaterial;if(m.color&&object.userData.region!=='eye'){m.color.set('#e8bc4d');m.metalness=.8;m.roughness=.24;}}});\r\n root.userData.recipe=JSON.parse(JSON.stringify(d));root.userData.eyeOffset=eyeOffset;root.userData.eyeScale=eyeScale;root.userData.eyeSurfaceZ=surfaceZ;root.userData.pivots=pivots;\r\n return {root,skinTextures,dispose:disposeAssembly};\r\n}\r\n","creature/source/creator/camera-focus.ts":"import * as T from 'three';\r\nimport type {Region} from './design';\r\n\r\nexport function regionBounds(root:T.Object3D,region:Region){\r\n const bounds=new T.Box3();root.updateMatrixWorld(true);\r\n function visit(node:T.Object3D){\r\n  if(!node.visible)return;\r\n  if(node instanceof T.Mesh&&node.userData.region===region){\r\n   node.geometry.computeBoundingBox();\r\n   if(node.geometry.boundingBox)bounds.union(node.geometry.boundingBox.clone().applyMatrix4(node.matrixWorld));\r\n  }\r\n  node.children.forEach(visit);\r\n }\r\n visit(root);return bounds;\r\n}\r\n\r\n// side 1 looks from the front (+z), -1 from behind.\r\nexport function regionFrame(camera:T.PerspectiveCamera,bounds:T.Box3,padding=1.2,side=1){\r\n if(bounds.isEmpty())return null;\r\n const center=bounds.getCenter(new T.Vector3()),size=bounds.getSize(new T.Vector3());\r\n const halfFov=T.MathUtils.degToRad(camera.fov/2);\r\n const distance=Math.max(size.y/(2*Math.tan(halfFov)),size.x/(2*Math.tan(halfFov)*Math.max(.25,camera.aspect)))*padding+size.z/2;\r\n return {target:center,position:center.clone().add(new T.Vector3(0,0,side*Math.max(1,distance))),distance:Math.max(1,distance)};\r\n}\r\n\r\nexport function frameRegion(camera:T.PerspectiveCamera,orbit:{target:T.Vector3;minDistance:number;maxDistance:number;update:()=>void},bounds:T.Box3,padding=1.2,side=1){\r\n const frame=regionFrame(camera,bounds,padding,side);if(!frame)return false;\r\n orbit.minDistance=Math.min(.7,frame.distance*.5);orbit.maxDistance=Math.max(14,frame.distance*2);\r\n orbit.target.copy(frame.target);camera.position.copy(frame.position);orbit.update();return true;\r\n}\r\n","creature/source/creator/catalog.ts":"export const REGIONS = [\r\n  { id: 'nails', label: 'Nails', short: 'Nails', description: 'Five claw and nail forms' },\r\n  { id: 'fingertips', label: 'Finger tips', short: 'Tips', description: 'Distal pads and final phalanges' },\r\n  { id: 'fingers', label: 'Fingers & knuckles', short: 'Fingers', description: 'Joined finger shafts, bases and knuckles' },\r\n  { id: 'palm', label: 'Palm', short: 'Palm', description: 'The inner hand surface' },\r\n  { id: 'back_of_hand', label: 'Back of hand', short: 'Back', description: 'The dorsal hand surface' },\r\n  { id: 'wrist', label: 'Wrist', short: 'Wrist', description: 'Stump, cuff and transition' },\r\n] as const;\r\n\r\nexport type RegionId = (typeof REGIONS)[number]['id'];\r\n\r\nexport type StyleFamily = {\r\n  id: number;\r\n  name: string;\r\n  realm: string;\r\n  primary: string;\r\n  secondary: string;\r\n  accent: string;\r\n  emissive: string;\r\n  roughness: number;\r\n  metalness: number;\r\n  detail: 'clean' | 'vine' | 'caps' | 'plates' | 'scales' | 'spines' | 'coral' | 'bone' | 'mist' | 'flame' | 'halo' | 'void' | 'eyes' | 'rock' | 'crystal' | 'magma' | 'ice' | 'storm' | 'gears' | 'neon' | 'fur' | 'jelly' | 'baby';\r\n};\r\n\r\nexport const STYLES: StyleFamily[] = [\r\n  { id: 0, name: 'Mortal', realm: 'Baseline · Earth', primary: '#b9856f', secondary: '#714c42', accent: '#f0c0a3', emissive: '#000000', roughness: 0.72, metalness: 0.02, detail: 'clean' },\r\n  { id: 1, name: 'Verdant', realm: 'Wildroot · Dimension 12', primary: '#3f7448', secondary: '#1e3b2b', accent: '#b8dd6e', emissive: '#172d13', roughness: 0.88, metalness: 0, detail: 'vine' },\r\n  { id: 2, name: 'Mycelial', realm: 'Sporesea · Dimension 09', primary: '#d5c7ae', secondary: '#776b82', accent: '#f59ec4', emissive: '#6d234a', roughness: 0.92, metalness: 0, detail: 'caps' },\r\n  { id: 3, name: 'Chitin', realm: 'Carapace · Dimension 31', primary: '#4f263d', secondary: '#180f20', accent: '#d17663', emissive: '#240716', roughness: 0.3, metalness: 0.28, detail: 'plates' },\r\n  { id: 4, name: 'Reptilian', realm: 'Scalehold · Dimension 04', primary: '#5f7a3f', secondary: '#283621', accent: '#cfb85c', emissive: '#101c08', roughness: 0.76, metalness: 0.04, detail: 'scales' },\r\n  { id: 5, name: 'Abyssal', realm: 'Deep Trench · Dimension 88', primary: '#132a42', secondary: '#06111f', accent: '#4edfd3', emissive: '#0b756f', roughness: 0.34, metalness: 0.12, detail: 'spines' },\r\n  { id: 6, name: 'Coral', realm: 'Living Reef · Dimension 22', primary: '#e46867', secondary: '#70344c', accent: '#ffd27a', emissive: '#8b2438', roughness: 0.83, metalness: 0, detail: 'coral' },\r\n  { id: 7, name: 'Skeletal', realm: 'Ossuary · Dimension 00', primary: '#d8d0af', secondary: '#80785f', accent: '#fff7d1', emissive: '#17160e', roughness: 0.9, metalness: 0, detail: 'bone' },\r\n  { id: 8, name: 'Spectral', realm: 'Veil · Dimension 13', primary: '#8ed9cd', secondary: '#294c58', accent: '#d5fff8', emissive: '#248e86', roughness: 0.18, metalness: 0.04, detail: 'mist' },\r\n  { id: 9, name: 'Infernal', realm: 'Cinder Court · Dimension 66', primary: '#7b211e', secondary: '#260b0a', accent: '#ff8a2a', emissive: '#d33812', roughness: 0.58, metalness: 0.1, detail: 'flame' },\r\n  { id: 10, name: 'Celestial', realm: 'High Orbit · Dimension 07', primary: '#e6dca4', secondary: '#786da7', accent: '#fffbd7', emissive: '#8d74d6', roughness: 0.24, metalness: 0.5, detail: 'halo' },\r\n  { id: 11, name: 'Voidborn', realm: 'Null Expanse · Dimension ∅', primary: '#16101f', secondary: '#060409', accent: '#9f64ff', emissive: '#482285', roughness: 0.16, metalness: 0.36, detail: 'void' },\r\n  { id: 12, name: 'Eldritch', realm: 'Watcher Fold · Dimension 47', primary: '#6b426c', secondary: '#252038', accent: '#b6ef63', emissive: '#4e7628', roughness: 0.64, metalness: 0.04, detail: 'eyes' },\r\n  { id: 13, name: 'Stone Golem', realm: 'Deep Strata · Dimension 19', primary: '#6f7068', secondary: '#393b37', accent: '#b7a37d', emissive: '#171811', roughness: 0.98, metalness: 0, detail: 'rock' },\r\n  { id: 14, name: 'Crystal', realm: 'Prism Vault · Dimension 52', primary: '#896bc2', secondary: '#302e66', accent: '#e0c8ff', emissive: '#5a3da8', roughness: 0.12, metalness: 0.3, detail: 'crystal' },\r\n  { id: 15, name: 'Magma', realm: 'Mantle · Dimension 03', primary: '#4a1713', secondary: '#140706', accent: '#ffb22f', emissive: '#e44712', roughness: 0.78, metalness: 0.06, detail: 'magma' },\r\n  { id: 16, name: 'Glacial', realm: 'White Silence · Dimension 71', primary: '#93c9df', secondary: '#315a76', accent: '#ebfdff', emissive: '#3c93bb', roughness: 0.2, metalness: 0.14, detail: 'ice' },\r\n  { id: 17, name: 'Stormcharged', realm: 'Tempest Ring · Dimension 28', primary: '#48536e', secondary: '#1a2236', accent: '#c3f7ff', emissive: '#3ebde0', roughness: 0.4, metalness: 0.32, detail: 'storm' },\r\n  { id: 18, name: 'Clockwork Robot', realm: 'Brass Meridian · Dimension 14', primary: '#9a6a37', secondary: '#33251d', accent: '#efd08c', emissive: '#5b2c12', roughness: 0.32, metalness: 0.82, detail: 'gears' },\r\n  { id: 19, name: 'Neon Synth', realm: 'Aftergrid · Dimension 20', primary: '#251d54', secondary: '#0b0921', accent: '#ff5bd7', emissive: '#b519ac', roughness: 0.22, metalness: 0.55, detail: 'neon' },\r\n  { id: 20, name: 'Fluffy', realm: 'Cloudburrow · Soft forms', primary: '#d6a987', secondary: '#bb8969', accent: '#ffe4c3', emissive: '#000000', roughness: .88, metalness: 0, detail: 'fur' },\r\n  { id: 21, name: 'Jelly', realm: 'Lime Lagoon · Soft forms', primary: '#a7ec69', secondary: '#f1ffd4', accent: '#c9ff90', emissive: '#000000', roughness: .13, metalness: 0, detail: 'jelly' },\r\n  { id: 22, name: 'Baby', realm: 'Little One · Soft forms', primary: '#efb398', secondary: '#d68e7c', accent: '#ffe2d5', emissive: '#000000', roughness: .52, metalness: 0, detail: 'baby' },\r\n];\r\n\r\nexport const DEFAULT_SELECTION: Record<RegionId, number> = {\r\n  nails: 14,\r\n  fingertips: 5,\r\n  fingers: 12,\r\n  palm: 11,\r\n  back_of_hand: 17,\r\n  wrist: 18,\r\n};\r\n\r\nexport const PURE_PRESETS = STYLES.map((style) => ({\r\n  name: style.name,\r\n  description: `A pure ${style.name.toLowerCase()} hand`,\r\n  values: Object.fromEntries(REGIONS.map((region) => [region.id, style.id])) as Record<RegionId, number>,\r\n}));\r\n\r\n\r\n// Legacy IDs stay importable; retired looks are excluded from new picks.\r\nexport const PICKER_STYLES=STYLES.filter(style=>![8,11].includes(style.id));\r\n","creature/source/creator/coaching.ts":"export const SITUATIONS=[['start','Getting started'],['stuck','Feeling stuck'],['missed','Missed a goal'],['win','Celebrating progress']] as const;\r\nexport type Situation=typeof SITUATIONS[number][0];\r\nexport const COACHES=[\r\n {id:'supportive',name:'Supportive',tone:'Warm, patient, encouraging.',approach:'Acknowledge effort. Offer one manageable next step. Celebrate progress without pressure.',lines:{start:'We can start small. What is one thing you could do in the next five minutes?',stuck:'You do not have to solve everything at once. Let’s make the next step smaller.',missed:'One missed goal does not erase your progress. What would make trying again easier?',win:'You followed through. Take a moment to notice what helped you do that.'}},\r\n {id:'direct',name:'Direct',tone:'Clear, firm, practical.',approach:'Be concise. Turn vague intentions into a specific next action. Hold the user accountable without insults.',lines:{start:'Pick one task. Decide what finished looks like. Begin with the first step.',stuck:'Name the obstacle. Choose one action that is still under your control.',missed:'Review what got in the way, adjust the plan, and choose your next attempt.',win:'You did what you said you would. Keep the part of the plan that worked.'}},\r\n {id:'analytical',name:'Analytical',tone:'Methodical, curious, precise.',approach:'Ask about constraints. Break goals into observable steps. Treat setbacks as information, not character flaws.',lines:{start:'What is the goal, what time do you have, and what is the smallest useful result?',stuck:'Is the obstacle time, clarity, energy, or resources? Let’s change one variable.',missed:'Compare the plan with what happened. Which assumption needs updating?',win:'What specifically helped? Record it so you can repeat the conditions.'}},\r\n {id:'playful',name:'Playful',tone:'Mischievous, upbeat, lightly teasing.',approach:'Use small quests and creature humor. Keep encouragement specific. Never shame the user.',lines:{start:'Tiny quest unlocked: defeat the first five minutes. The giant task can wait its turn.',stuck:'This boss fight needs a smaller boss. What can we turn into a side quest?',missed:'The quest log has been updated, not destroyed. Pick your next checkpoint.',win:'Achievement unlocked: actually doing the thing. My one eye is impressed.'}},\r\n {id:'calm',name:'Calm',tone:'Steady, spacious, grounded.',approach:'Use short, unhurried language. Leave room for the user’s choices. Focus on the present next step.',lines:{start:'There is no need to rush the whole plan. Choose one thing to begin.',stuck:'Pause for a moment. What feels possible from where you are right now?',missed:'That attempt is over. You can choose a gentler, more workable next one.',win:'Notice this moment. You made progress, and you can let that be enough for now.'}},\r\n {id:'mom',name:'MOM Inc.',tone:'Polite, theatrical, dry corporate humor.',approach:'Use fictional MOM Inc. bureaucracy and understated absurdity. Keep choices voluntary and practical.',lines:{start:'MOM Inc. has approved one manageable task. Please select yours; the paperwork can wait.',stuck:'Your obstacle has been escalated to the Department of Smaller Steps. What can we simplify?',missed:'No disciplinary forms today. We are revising the plan, not your worth.',win:'Progress officially recorded. Please accept one highly regulated moment of pride.'}},\r\n] as const;\r\nexport type CoachId=typeof COACHES[number]['id'];\r\nexport const getCoach=(id:CoachId)=>COACHES.find(c=>c.id===id)??COACHES[0];\r\n","creature/source/creator/color-variation.mjs":"// CIE Lab distances compare visible colors, independent of hex spelling.\r\nexport function colorLab(hex) {\r\n const channels=[1,3,5].map(i=>parseInt(hex.slice(i,i+2),16)/255).map(c=>c<=.04045?c/12.92:((c+.055)/1.055)**2.4);\r\n const [r,g,b]=channels;\r\n const f=x=>x>216/24389?Math.cbrt(x):(24389/27*x+16)/116;\r\n const x=f((r*.4124564+g*.3575761+b*.1804375)/.95047),y=f(r*.2126729+g*.7151522+b*.072175),z=f((r*.0193339+g*.119192+b*.9503041)/1.08883);\r\n return [116*y-16,500*(x-y),200*(y-z)];\r\n}\r\nexport const colorDistance=(a,b)=>Math.hypot(...colorLab(a).map((v,i)=>v-colorLab(b)[i]));\r\nconst permutations=[[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]];\r\n// Unordered comparison catches repeated combinations even when stops are swapped.\r\nexport const paletteDistance=(a,b)=>Math.min(...permutations.map(order=>a.reduce((sum,hex,i)=>sum+colorDistance(hex,b[order[i]]),0)/3));\r\n// Call with unlocked entries first so a locked near-duplicate never hides an owned color.\r\n// Aliases remain in the registry and saved recipes; only redundant UI tiles are folded.\r\nexport function distinctSwatches(items,minDistance=14) {\r\n const selected=[];\r\n for(const item of items)if(!selected.some(other=>colorDistance(item.hex,other.hex)<minDistance))selected.push(item);\r\n return selected;\r\n}\r\n","creature/source/creator/creature-skins.json":"{\r\n  \"schemaVersion\": 1,\r\n  \"additive\": true,\r\n  \"colorSpace\": {\r\n    \"basecolor\": \"srgb\",\r\n    \"tintMask\": \"srgb\",\r\n    \"normal\": \"linear\",\r\n    \"roughness\": \"linear\",\r\n    \"height\": \"linear\",\r\n    \"ao\": \"linear\",\r\n    \"metalness\": \"linear\",\r\n    \"opacity\": \"linear\",\r\n    \"emissive\": \"srgb\"\r\n  },\r\n  \"normalConvention\": \"OpenGL +Y\",\r\n  \"tinting\": \"tint-mask multiplied by selected creature palette\",\r\n  \"collections\": [\r\n    {\r\n      \"id\": \"creature-forged-realms\",\r\n      \"packetId\": \"coach-creature-forged-realms-v1\",\r\n      \"displayName\": \"Coach Creature Skins — Forged Realms\",\r\n      \"description\": \"Additional fantasy–science-fiction creature skin unlocks for the Coach app. Separate from the original fitness-material battle-pass set.\",\r\n      \"runtimeNotes\": {\r\n        \"normalConvention\": \"OpenGL (+Y)\",\r\n        \"tinting\": \"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",\r\n        \"recommendedColorSpace\": {\r\n          \"basecolor\": \"sRGB\",\r\n          \"tintMask\": \"sRGB\",\r\n          \"normal\": \"linear\",\r\n          \"roughness\": \"linear\",\r\n          \"height\": \"linear\",\r\n          \"ao\": \"linear\",\r\n          \"metalness\": \"linear\",\r\n          \"opacity\": \"linear\",\r\n          \"emissive\": \"sRGB\"\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-celestial-rift\",\r\n      \"packetId\": \"coach-creature-celestial-rift-v1\",\r\n      \"displayName\": \"Coach Creature Skins — Celestial Rift\",\r\n      \"description\": \"A second non-overlapping set of fantasy–science-fiction creature skin unlocks for the Coach app. Additive to Forged Realms and the original fitness-material set.\",\r\n      \"runtimeNotes\": {\r\n        \"normalConvention\": \"OpenGL (+Y)\",\r\n        \"tinting\": \"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",\r\n        \"recommendedColorSpace\": {\r\n          \"basecolor\": \"sRGB\",\r\n          \"tintMask\": \"sRGB\",\r\n          \"normal\": \"linear\",\r\n          \"roughness\": \"linear\",\r\n          \"height\": \"linear\",\r\n          \"ao\": \"linear\",\r\n          \"metalness\": \"linear\",\r\n          \"opacity\": \"linear\",\r\n          \"emissive\": \"sRGB\"\r\n        }\r\n      }\r\n    }\r\n  ],\r\n  \"skins\": [\r\n    {\r\n      \"id\": \"creature-starforged-plate\",\r\n      \"displayName\": \"Starforged Plate\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Metal009\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal009\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-starforged-plate/basecolor.webp\",\r\n          \"bytes\": 8170,\r\n          \"sha256\": \"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-starforged-plate/normal.webp\",\r\n          \"bytes\": 878,\r\n          \"sha256\": \"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-starforged-plate/roughness.webp\",\r\n          \"bytes\": 16326,\r\n          \"sha256\": \"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-starforged-plate/height.webp\",\r\n          \"bytes\": 37602,\r\n          \"sha256\": \"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-starforged-plate/metalness.webp\",\r\n          \"bytes\": 528,\r\n          \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-starforged-plate/tint-mask.webp\",\r\n          \"bytes\": 47604,\r\n          \"sha256\": \"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-starforged-plate/preview.webp\",\r\n          \"bytes\": 9336,\r\n          \"sha256\": \"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-starforged-plate/basecolor\",\r\n            \"bytes\": 8170,\r\n            \"sha256\": \"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-starforged-plate/normal\",\r\n            \"bytes\": 878,\r\n            \"sha256\": \"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-starforged-plate/roughness\",\r\n            \"bytes\": 16326,\r\n            \"sha256\": \"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-starforged-plate/height\",\r\n            \"bytes\": 37602,\r\n            \"sha256\": \"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-starforged-plate/metalness\",\r\n            \"bytes\": 528,\r\n            \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-starforged-plate/tintMask\",\r\n            \"bytes\": 47604,\r\n            \"sha256\": \"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-starforged-plate/preview\",\r\n            \"bytes\": 9336,\r\n            \"sha256\": \"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-obsidian-carapace\",\r\n      \"displayName\": \"Obsidian Carapace\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Onyx013\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx013\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/basecolor.webp\",\r\n          \"bytes\": 50752,\r\n          \"sha256\": \"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/normal.webp\",\r\n          \"bytes\": 596,\r\n          \"sha256\": \"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/roughness.webp\",\r\n          \"bytes\": 1382,\r\n          \"sha256\": \"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/height.webp\",\r\n          \"bytes\": 2988,\r\n          \"sha256\": \"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/tint-mask.webp\",\r\n          \"bytes\": 65032,\r\n          \"sha256\": \"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-obsidian-carapace/preview.webp\",\r\n          \"bytes\": 58128,\r\n          \"sha256\": \"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-obsidian-carapace/basecolor\",\r\n            \"bytes\": 50752,\r\n            \"sha256\": \"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-obsidian-carapace/normal\",\r\n            \"bytes\": 596,\r\n            \"sha256\": \"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-obsidian-carapace/roughness\",\r\n            \"bytes\": 1382,\r\n            \"sha256\": \"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-obsidian-carapace/height\",\r\n            \"bytes\": 2988,\r\n            \"sha256\": \"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-obsidian-carapace/tintMask\",\r\n            \"bytes\": 65032,\r\n            \"sha256\": \"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-obsidian-carapace/preview\",\r\n            \"bytes\": 58128,\r\n            \"sha256\": \"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-hive-armor\",\r\n      \"displayName\": \"Hive Armor\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"MetalPlates013\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/MetalPlates013\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-hive-armor/basecolor.webp\",\r\n          \"bytes\": 24764,\r\n          \"sha256\": \"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-hive-armor/normal.webp\",\r\n          \"bytes\": 14104,\r\n          \"sha256\": \"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-hive-armor/roughness.webp\",\r\n          \"bytes\": 44122,\r\n          \"sha256\": \"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-hive-armor/height.webp\",\r\n          \"bytes\": 9344,\r\n          \"sha256\": \"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-hive-armor/ao.webp\",\r\n          \"bytes\": 5394,\r\n          \"sha256\": \"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-hive-armor/metalness.webp\",\r\n          \"bytes\": 38740,\r\n          \"sha256\": \"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-hive-armor/tint-mask.webp\",\r\n          \"bytes\": 61850,\r\n          \"sha256\": \"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-hive-armor/preview.webp\",\r\n          \"bytes\": 30780,\r\n          \"sha256\": \"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-hive-armor/basecolor\",\r\n            \"bytes\": 24764,\r\n            \"sha256\": \"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-hive-armor/normal\",\r\n            \"bytes\": 14104,\r\n            \"sha256\": \"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-hive-armor/roughness\",\r\n            \"bytes\": 44122,\r\n            \"sha256\": \"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-hive-armor/height\",\r\n            \"bytes\": 9344,\r\n            \"sha256\": \"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-hive-armor/ao\",\r\n            \"bytes\": 5394,\r\n            \"sha256\": \"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-hive-armor/metalness\",\r\n            \"bytes\": 38740,\r\n            \"sha256\": \"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-hive-armor/tintMask\",\r\n            \"bytes\": 61850,\r\n            \"sha256\": \"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-hive-armor/preview\",\r\n            \"bytes\": 30780,\r\n            \"sha256\": \"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-magma-scale\",\r\n      \"displayName\": \"Magma Scale\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Lava004\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Lava004\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-magma-scale/basecolor.webp\",\r\n          \"bytes\": 34804,\r\n          \"sha256\": \"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-magma-scale/normal.webp\",\r\n          \"bytes\": 55692,\r\n          \"sha256\": \"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-magma-scale/roughness.webp\",\r\n          \"bytes\": 22680,\r\n          \"sha256\": \"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-magma-scale/height.webp\",\r\n          \"bytes\": 12946,\r\n          \"sha256\": \"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"\r\n        },\r\n        \"emissive\": {\r\n          \"file\": \"textures/creature-magma-scale/emissive.webp\",\r\n          \"bytes\": 41142,\r\n          \"sha256\": \"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-magma-scale/tint-mask.webp\",\r\n          \"bytes\": 41404,\r\n          \"sha256\": \"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-magma-scale/preview.webp\",\r\n          \"bytes\": 38666,\r\n          \"sha256\": \"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-magma-scale/basecolor\",\r\n            \"bytes\": 34804,\r\n            \"sha256\": \"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-magma-scale/normal\",\r\n            \"bytes\": 55692,\r\n            \"sha256\": \"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-magma-scale/roughness\",\r\n            \"bytes\": 22680,\r\n            \"sha256\": \"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-magma-scale/height\",\r\n            \"bytes\": 12946,\r\n            \"sha256\": \"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"\r\n          },\r\n          \"emissive\": {\r\n            \"entry\": \"creature-magma-scale/emissive\",\r\n            \"bytes\": 41142,\r\n            \"sha256\": \"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-magma-scale/tintMask\",\r\n            \"bytes\": 41404,\r\n            \"sha256\": \"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-magma-scale/preview\",\r\n            \"bytes\": 38666,\r\n            \"sha256\": \"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-frost-veins\",\r\n      \"displayName\": \"Frost Veins\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Ice003\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Ice003\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-frost-veins/basecolor.webp\",\r\n          \"bytes\": 22338,\r\n          \"sha256\": \"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-frost-veins/normal.webp\",\r\n          \"bytes\": 4622,\r\n          \"sha256\": \"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-frost-veins/roughness.webp\",\r\n          \"bytes\": 13946,\r\n          \"sha256\": \"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-frost-veins/height.webp\",\r\n          \"bytes\": 12916,\r\n          \"sha256\": \"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-frost-veins/tint-mask.webp\",\r\n          \"bytes\": 26114,\r\n          \"sha256\": \"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-frost-veins/preview.webp\",\r\n          \"bytes\": 26196,\r\n          \"sha256\": \"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-frost-veins/basecolor\",\r\n            \"bytes\": 22338,\r\n            \"sha256\": \"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-frost-veins/normal\",\r\n            \"bytes\": 4622,\r\n            \"sha256\": \"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-frost-veins/roughness\",\r\n            \"bytes\": 13946,\r\n            \"sha256\": \"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-frost-veins/height\",\r\n            \"bytes\": 12916,\r\n            \"sha256\": \"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-frost-veins/tintMask\",\r\n            \"bytes\": 26114,\r\n            \"sha256\": \"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-frost-veins/preview\",\r\n            \"bytes\": 26196,\r\n            \"sha256\": \"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-storm-carbon\",\r\n      \"displayName\": \"Storm Carbon\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Fabric004\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Fabric004\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-storm-carbon/basecolor.webp\",\r\n          \"bytes\": 4186,\r\n          \"sha256\": \"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-storm-carbon/normal.webp\",\r\n          \"bytes\": 21570,\r\n          \"sha256\": \"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-storm-carbon/roughness.webp\",\r\n          \"bytes\": 13466,\r\n          \"sha256\": \"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-storm-carbon/height.webp\",\r\n          \"bytes\": 10398,\r\n          \"sha256\": \"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-storm-carbon/metalness.webp\",\r\n          \"bytes\": 528,\r\n          \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-storm-carbon/tint-mask.webp\",\r\n          \"bytes\": 11298,\r\n          \"sha256\": \"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-storm-carbon/preview.webp\",\r\n          \"bytes\": 4638,\r\n          \"sha256\": \"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-storm-carbon/basecolor\",\r\n            \"bytes\": 4186,\r\n            \"sha256\": \"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-storm-carbon/normal\",\r\n            \"bytes\": 21570,\r\n            \"sha256\": \"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-storm-carbon/roughness\",\r\n            \"bytes\": 13466,\r\n            \"sha256\": \"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-storm-carbon/height\",\r\n            \"bytes\": 10398,\r\n            \"sha256\": \"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-storm-carbon/metalness\",\r\n            \"bytes\": 528,\r\n            \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-storm-carbon/tintMask\",\r\n            \"bytes\": 11298,\r\n            \"sha256\": \"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-storm-carbon/preview\",\r\n            \"bytes\": 4638,\r\n            \"sha256\": \"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-ancient-bark\",\r\n      \"displayName\": \"Ancient Bark\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Bark015\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bark015\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-ancient-bark/basecolor.webp\",\r\n          \"bytes\": 76532,\r\n          \"sha256\": \"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-ancient-bark/normal.webp\",\r\n          \"bytes\": 81960,\r\n          \"sha256\": \"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-ancient-bark/roughness.webp\",\r\n          \"bytes\": 30736,\r\n          \"sha256\": \"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-ancient-bark/height.webp\",\r\n          \"bytes\": 12470,\r\n          \"sha256\": \"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-ancient-bark/ao.webp\",\r\n          \"bytes\": 62736,\r\n          \"sha256\": \"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-ancient-bark/tint-mask.webp\",\r\n          \"bytes\": 84688,\r\n          \"sha256\": \"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-ancient-bark/preview.webp\",\r\n          \"bytes\": 85306,\r\n          \"sha256\": \"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-ancient-bark/basecolor\",\r\n            \"bytes\": 76532,\r\n            \"sha256\": \"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-ancient-bark/normal\",\r\n            \"bytes\": 81960,\r\n            \"sha256\": \"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-ancient-bark/roughness\",\r\n            \"bytes\": 30736,\r\n            \"sha256\": \"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-ancient-bark/height\",\r\n            \"bytes\": 12470,\r\n            \"sha256\": \"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-ancient-bark/ao\",\r\n            \"bytes\": 62736,\r\n            \"sha256\": \"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-ancient-bark/tintMask\",\r\n            \"bytes\": 84688,\r\n            \"sha256\": \"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-ancient-bark/preview\",\r\n            \"bytes\": 85306,\r\n            \"sha256\": \"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-mossback\",\r\n      \"displayName\": \"Mossback\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Bark013\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bark013\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-mossback/basecolor.webp\",\r\n          \"bytes\": 62984,\r\n          \"sha256\": \"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-mossback/normal.webp\",\r\n          \"bytes\": 74686,\r\n          \"sha256\": \"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-mossback/roughness.webp\",\r\n          \"bytes\": 42160,\r\n          \"sha256\": \"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-mossback/height.webp\",\r\n          \"bytes\": 23126,\r\n          \"sha256\": \"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-mossback/ao.webp\",\r\n          \"bytes\": 44736,\r\n          \"sha256\": \"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-mossback/tint-mask.webp\",\r\n          \"bytes\": 71276,\r\n          \"sha256\": \"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-mossback/preview.webp\",\r\n          \"bytes\": 71140,\r\n          \"sha256\": \"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-mossback/basecolor\",\r\n            \"bytes\": 62984,\r\n            \"sha256\": \"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-mossback/normal\",\r\n            \"bytes\": 74686,\r\n            \"sha256\": \"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-mossback/roughness\",\r\n            \"bytes\": 42160,\r\n            \"sha256\": \"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-mossback/height\",\r\n            \"bytes\": 23126,\r\n            \"sha256\": \"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-mossback/ao\",\r\n            \"bytes\": 44736,\r\n            \"sha256\": \"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-mossback/tintMask\",\r\n            \"bytes\": 71276,\r\n            \"sha256\": \"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-mossback/preview\",\r\n            \"bytes\": 71140,\r\n            \"sha256\": \"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-rootbound\",\r\n      \"displayName\": \"Rootbound Hide\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Ground076\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Ground076\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-rootbound/basecolor.webp\",\r\n          \"bytes\": 80318,\r\n          \"sha256\": \"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-rootbound/normal.webp\",\r\n          \"bytes\": 68262,\r\n          \"sha256\": \"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-rootbound/roughness.webp\",\r\n          \"bytes\": 52578,\r\n          \"sha256\": \"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-rootbound/height.webp\",\r\n          \"bytes\": 23352,\r\n          \"sha256\": \"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-rootbound/ao.webp\",\r\n          \"bytes\": 66244,\r\n          \"sha256\": \"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-rootbound/tint-mask.webp\",\r\n          \"bytes\": 88550,\r\n          \"sha256\": \"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-rootbound/preview.webp\",\r\n          \"bytes\": 86566,\r\n          \"sha256\": \"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-rootbound/basecolor\",\r\n            \"bytes\": 80318,\r\n            \"sha256\": \"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-rootbound/normal\",\r\n            \"bytes\": 68262,\r\n            \"sha256\": \"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-rootbound/roughness\",\r\n            \"bytes\": 52578,\r\n            \"sha256\": \"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-rootbound/height\",\r\n            \"bytes\": 23352,\r\n            \"sha256\": \"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-rootbound/ao\",\r\n            \"bytes\": 66244,\r\n            \"sha256\": \"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-rootbound/tintMask\",\r\n            \"bytes\": 88550,\r\n            \"sha256\": \"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-rootbound/preview\",\r\n            \"bytes\": 86566,\r\n            \"sha256\": \"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-hammered-bronze\",\r\n      \"displayName\": \"Hammered Bronze Golem\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Metal008\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal008\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-hammered-bronze/basecolor.webp\",\r\n          \"bytes\": 21270,\r\n          \"sha256\": \"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-hammered-bronze/normal.webp\",\r\n          \"bytes\": 1860,\r\n          \"sha256\": \"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-hammered-bronze/roughness.webp\",\r\n          \"bytes\": 22538,\r\n          \"sha256\": \"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-hammered-bronze/height.webp\",\r\n          \"bytes\": 3670,\r\n          \"sha256\": \"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-hammered-bronze/metalness.webp\",\r\n          \"bytes\": 3062,\r\n          \"sha256\": \"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-hammered-bronze/tint-mask.webp\",\r\n          \"bytes\": 69154,\r\n          \"sha256\": \"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-hammered-bronze/preview.webp\",\r\n          \"bytes\": 26964,\r\n          \"sha256\": \"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-hammered-bronze/basecolor\",\r\n            \"bytes\": 21270,\r\n            \"sha256\": \"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-hammered-bronze/normal\",\r\n            \"bytes\": 1860,\r\n            \"sha256\": \"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-hammered-bronze/roughness\",\r\n            \"bytes\": 22538,\r\n            \"sha256\": \"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-hammered-bronze/height\",\r\n            \"bytes\": 3670,\r\n            \"sha256\": \"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-hammered-bronze/metalness\",\r\n            \"bytes\": 3062,\r\n            \"sha256\": \"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-hammered-bronze/tintMask\",\r\n            \"bytes\": 69154,\r\n            \"sha256\": \"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-hammered-bronze/preview\",\r\n            \"bytes\": 26964,\r\n            \"sha256\": \"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-rope-sinew\",\r\n      \"displayName\": \"Rope Sinew\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Rope001\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Rope001\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-rope-sinew/basecolor.webp\",\r\n          \"bytes\": 29552,\r\n          \"sha256\": \"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-rope-sinew/normal.webp\",\r\n          \"bytes\": 58934,\r\n          \"sha256\": \"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-rope-sinew/roughness.webp\",\r\n          \"bytes\": 12446,\r\n          \"sha256\": \"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-rope-sinew/height.webp\",\r\n          \"bytes\": 8678,\r\n          \"sha256\": \"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-rope-sinew/metalness.webp\",\r\n          \"bytes\": 530,\r\n          \"sha256\": \"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-rope-sinew/tint-mask.webp\",\r\n          \"bytes\": 45544,\r\n          \"sha256\": \"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-rope-sinew/preview.webp\",\r\n          \"bytes\": 35204,\r\n          \"sha256\": \"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-rope-sinew/basecolor\",\r\n            \"bytes\": 29552,\r\n            \"sha256\": \"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-rope-sinew/normal\",\r\n            \"bytes\": 58934,\r\n            \"sha256\": \"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-rope-sinew/roughness\",\r\n            \"bytes\": 12446,\r\n            \"sha256\": \"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-rope-sinew/height\",\r\n            \"bytes\": 8678,\r\n            \"sha256\": \"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-rope-sinew/metalness\",\r\n            \"bytes\": 530,\r\n            \"sha256\": \"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-rope-sinew/tintMask\",\r\n            \"bytes\": 45544,\r\n            \"sha256\": \"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-rope-sinew/preview\",\r\n            \"bytes\": 35204,\r\n            \"sha256\": \"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-runic-leather\",\r\n      \"displayName\": \"Runic Leather\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Leather032\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Leather032\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-runic-leather/basecolor.webp\",\r\n          \"bytes\": 28382,\r\n          \"sha256\": \"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-runic-leather/normal.webp\",\r\n          \"bytes\": 7148,\r\n          \"sha256\": \"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-runic-leather/roughness.webp\",\r\n          \"bytes\": 85118,\r\n          \"sha256\": \"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-runic-leather/height.webp\",\r\n          \"bytes\": 90332,\r\n          \"sha256\": \"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-runic-leather/tint-mask.webp\",\r\n          \"bytes\": 89338,\r\n          \"sha256\": \"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-runic-leather/preview.webp\",\r\n          \"bytes\": 35058,\r\n          \"sha256\": \"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-runic-leather/basecolor\",\r\n            \"bytes\": 28382,\r\n            \"sha256\": \"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-runic-leather/normal\",\r\n            \"bytes\": 7148,\r\n            \"sha256\": \"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-runic-leather/roughness\",\r\n            \"bytes\": 85118,\r\n            \"sha256\": \"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-runic-leather/height\",\r\n            \"bytes\": 90332,\r\n            \"sha256\": \"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-runic-leather/tintMask\",\r\n            \"bytes\": 89338,\r\n            \"sha256\": \"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-runic-leather/preview\",\r\n            \"bytes\": 35058,\r\n            \"sha256\": \"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-crystal-core\",\r\n      \"displayName\": \"Crystal Core\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Onyx006\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx006\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-crystal-core/basecolor.webp\",\r\n          \"bytes\": 50726,\r\n          \"sha256\": \"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-crystal-core/normal.webp\",\r\n          \"bytes\": 572,\r\n          \"sha256\": \"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-crystal-core/roughness.webp\",\r\n          \"bytes\": 15314,\r\n          \"sha256\": \"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-crystal-core/height.webp\",\r\n          \"bytes\": 2740,\r\n          \"sha256\": \"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-crystal-core/tint-mask.webp\",\r\n          \"bytes\": 51868,\r\n          \"sha256\": \"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-crystal-core/preview.webp\",\r\n          \"bytes\": 56978,\r\n          \"sha256\": \"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-crystal-core/basecolor\",\r\n            \"bytes\": 50726,\r\n            \"sha256\": \"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-crystal-core/normal\",\r\n            \"bytes\": 572,\r\n            \"sha256\": \"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-crystal-core/roughness\",\r\n            \"bytes\": 15314,\r\n            \"sha256\": \"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-crystal-core/height\",\r\n            \"bytes\": 2740,\r\n            \"sha256\": \"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-crystal-core/tintMask\",\r\n            \"bytes\": 51868,\r\n            \"sha256\": \"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-crystal-core/preview\",\r\n            \"bytes\": 56978,\r\n            \"sha256\": \"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-moon-marble\",\r\n      \"displayName\": \"Moon Marble\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Marble016\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Marble016\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-moon-marble/basecolor.webp\",\r\n          \"bytes\": 60346,\r\n          \"sha256\": \"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-moon-marble/normal.webp\",\r\n          \"bytes\": 558,\r\n          \"sha256\": \"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-moon-marble/roughness.webp\",\r\n          \"bytes\": 3740,\r\n          \"sha256\": \"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-moon-marble/height.webp\",\r\n          \"bytes\": 54702,\r\n          \"sha256\": \"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-moon-marble/tint-mask.webp\",\r\n          \"bytes\": 74936,\r\n          \"sha256\": \"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-moon-marble/preview.webp\",\r\n          \"bytes\": 68934,\r\n          \"sha256\": \"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-moon-marble/basecolor\",\r\n            \"bytes\": 60346,\r\n            \"sha256\": \"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-moon-marble/normal\",\r\n            \"bytes\": 558,\r\n            \"sha256\": \"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-moon-marble/roughness\",\r\n            \"bytes\": 3740,\r\n            \"sha256\": \"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-moon-marble/height\",\r\n            \"bytes\": 54702,\r\n            \"sha256\": \"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-moon-marble/tintMask\",\r\n            \"bytes\": 74936,\r\n            \"sha256\": \"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-moon-marble/preview\",\r\n            \"bytes\": 68934,\r\n            \"sha256\": \"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-fae-petal\",\r\n      \"displayName\": \"Fae Petal\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"FlowerSet001\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/FlowerSet001\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-fae-petal/basecolor.webp\",\r\n          \"bytes\": 17208,\r\n          \"sha256\": \"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-fae-petal/normal.webp\",\r\n          \"bytes\": 19898,\r\n          \"sha256\": \"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-fae-petal/roughness.webp\",\r\n          \"bytes\": 6054,\r\n          \"sha256\": \"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-fae-petal/height.webp\",\r\n          \"bytes\": 6176,\r\n          \"sha256\": \"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-fae-petal/ao.webp\",\r\n          \"bytes\": 8140,\r\n          \"sha256\": \"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"\r\n        },\r\n        \"opacity\": {\r\n          \"file\": \"textures/creature-fae-petal/opacity.webp\",\r\n          \"bytes\": 9784,\r\n          \"sha256\": \"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-fae-petal/tint-mask.webp\",\r\n          \"bytes\": 15026,\r\n          \"sha256\": \"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-fae-petal/preview.webp\",\r\n          \"bytes\": 19274,\r\n          \"sha256\": \"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-fae-petal/basecolor\",\r\n            \"bytes\": 17208,\r\n            \"sha256\": \"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-fae-petal/normal\",\r\n            \"bytes\": 19898,\r\n            \"sha256\": \"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-fae-petal/roughness\",\r\n            \"bytes\": 6054,\r\n            \"sha256\": \"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-fae-petal/height\",\r\n            \"bytes\": 6176,\r\n            \"sha256\": \"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-fae-petal/ao\",\r\n            \"bytes\": 8140,\r\n            \"sha256\": \"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"\r\n          },\r\n          \"opacity\": {\r\n            \"entry\": \"creature-fae-petal/opacity\",\r\n            \"bytes\": 9784,\r\n            \"sha256\": \"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-fae-petal/tintMask\",\r\n            \"bytes\": 15026,\r\n            \"sha256\": \"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-fae-petal/preview\",\r\n            \"bytes\": 19274,\r\n            \"sha256\": \"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-dragon-scale\",\r\n      \"displayName\": \"Dragon Scale\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"RoofingTiles013A\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/RoofingTiles013A\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-dragon-scale/basecolor.webp\",\r\n          \"bytes\": 2668,\r\n          \"sha256\": \"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-dragon-scale/normal.webp\",\r\n          \"bytes\": 20688,\r\n          \"sha256\": \"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-dragon-scale/roughness.webp\",\r\n          \"bytes\": 16142,\r\n          \"sha256\": \"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-dragon-scale/height.webp\",\r\n          \"bytes\": 8400,\r\n          \"sha256\": \"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-dragon-scale/ao.webp\",\r\n          \"bytes\": 14048,\r\n          \"sha256\": \"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"\r\n        },\r\n        \"opacity\": {\r\n          \"file\": \"textures/creature-dragon-scale/opacity.webp\",\r\n          \"bytes\": 528,\r\n          \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-dragon-scale/tint-mask.webp\",\r\n          \"bytes\": 33612,\r\n          \"sha256\": \"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-dragon-scale/preview.webp\",\r\n          \"bytes\": 3430,\r\n          \"sha256\": \"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-dragon-scale/basecolor\",\r\n            \"bytes\": 2668,\r\n            \"sha256\": \"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-dragon-scale/normal\",\r\n            \"bytes\": 20688,\r\n            \"sha256\": \"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-dragon-scale/roughness\",\r\n            \"bytes\": 16142,\r\n            \"sha256\": \"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-dragon-scale/height\",\r\n            \"bytes\": 8400,\r\n            \"sha256\": \"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-dragon-scale/ao\",\r\n            \"bytes\": 14048,\r\n            \"sha256\": \"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"\r\n          },\r\n          \"opacity\": {\r\n            \"entry\": \"creature-dragon-scale/opacity\",\r\n            \"bytes\": 528,\r\n            \"sha256\": \"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-dragon-scale/tintMask\",\r\n            \"bytes\": 33612,\r\n            \"sha256\": \"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-dragon-scale/preview\",\r\n            \"bytes\": 3430,\r\n            \"sha256\": \"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-bamboo-hide\",\r\n      \"displayName\": \"Bamboo Hide\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Bamboo001A\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bamboo001A\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-bamboo-hide/basecolor.webp\",\r\n          \"bytes\": 11654,\r\n          \"sha256\": \"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-bamboo-hide/normal.webp\",\r\n          \"bytes\": 20194,\r\n          \"sha256\": \"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-bamboo-hide/roughness.webp\",\r\n          \"bytes\": 12634,\r\n          \"sha256\": \"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-bamboo-hide/height.webp\",\r\n          \"bytes\": 7926,\r\n          \"sha256\": \"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-bamboo-hide/ao.webp\",\r\n          \"bytes\": 7788,\r\n          \"sha256\": \"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"\r\n        },\r\n        \"opacity\": {\r\n          \"file\": \"textures/creature-bamboo-hide/opacity.webp\",\r\n          \"bytes\": 2412,\r\n          \"sha256\": \"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-bamboo-hide/tint-mask.webp\",\r\n          \"bytes\": 37906,\r\n          \"sha256\": \"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-bamboo-hide/preview.webp\",\r\n          \"bytes\": 14130,\r\n          \"sha256\": \"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-bamboo-hide/basecolor\",\r\n            \"bytes\": 11654,\r\n            \"sha256\": \"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-bamboo-hide/normal\",\r\n            \"bytes\": 20194,\r\n            \"sha256\": \"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-bamboo-hide/roughness\",\r\n            \"bytes\": 12634,\r\n            \"sha256\": \"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-bamboo-hide/height\",\r\n            \"bytes\": 7926,\r\n            \"sha256\": \"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-bamboo-hide/ao\",\r\n            \"bytes\": 7788,\r\n            \"sha256\": \"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"\r\n          },\r\n          \"opacity\": {\r\n            \"entry\": \"creature-bamboo-hide/opacity\",\r\n            \"bytes\": 2412,\r\n            \"sha256\": \"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-bamboo-hide/tintMask\",\r\n            \"bytes\": 37906,\r\n            \"sha256\": \"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-bamboo-hide/preview\",\r\n            \"bytes\": 14130,\r\n            \"sha256\": \"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-ivory-chitin\",\r\n      \"displayName\": \"Ivory Chitin\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Marble021\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Marble021\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-ivory-chitin/basecolor.webp\",\r\n          \"bytes\": 15470,\r\n          \"sha256\": \"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-ivory-chitin/normal.webp\",\r\n          \"bytes\": 546,\r\n          \"sha256\": \"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-ivory-chitin/roughness.webp\",\r\n          \"bytes\": 3860,\r\n          \"sha256\": \"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-ivory-chitin/height.webp\",\r\n          \"bytes\": 50360,\r\n          \"sha256\": \"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-ivory-chitin/tint-mask.webp\",\r\n          \"bytes\": 97890,\r\n          \"sha256\": \"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-ivory-chitin/preview.webp\",\r\n          \"bytes\": 19758,\r\n          \"sha256\": \"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-ivory-chitin/basecolor\",\r\n            \"bytes\": 15470,\r\n            \"sha256\": \"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-ivory-chitin/normal\",\r\n            \"bytes\": 546,\r\n            \"sha256\": \"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-ivory-chitin/roughness\",\r\n            \"bytes\": 3860,\r\n            \"sha256\": \"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-ivory-chitin/height\",\r\n            \"bytes\": 50360,\r\n            \"sha256\": \"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-ivory-chitin/tintMask\",\r\n            \"bytes\": 97890,\r\n            \"sha256\": \"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-ivory-chitin/preview\",\r\n            \"bytes\": 19758,\r\n            \"sha256\": \"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-circuit-hide\",\r\n      \"displayName\": \"Circuit Hide\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Chip001\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Chip001\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-circuit-hide/basecolor.webp\",\r\n          \"bytes\": 40778,\r\n          \"sha256\": \"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-circuit-hide/normal.webp\",\r\n          \"bytes\": 27864,\r\n          \"sha256\": \"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-circuit-hide/roughness.webp\",\r\n          \"bytes\": 26718,\r\n          \"sha256\": \"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-circuit-hide/height.webp\",\r\n          \"bytes\": 23204,\r\n          \"sha256\": \"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-circuit-hide/metalness.webp\",\r\n          \"bytes\": 24542,\r\n          \"sha256\": \"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-circuit-hide/tint-mask.webp\",\r\n          \"bytes\": 37986,\r\n          \"sha256\": \"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-circuit-hide/preview.webp\",\r\n          \"bytes\": 44810,\r\n          \"sha256\": \"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-circuit-hide/basecolor\",\r\n            \"bytes\": 40778,\r\n            \"sha256\": \"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-circuit-hide/normal\",\r\n            \"bytes\": 27864,\r\n            \"sha256\": \"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-circuit-hide/roughness\",\r\n            \"bytes\": 26718,\r\n            \"sha256\": \"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-circuit-hide/height\",\r\n            \"bytes\": 23204,\r\n            \"sha256\": \"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-circuit-hide/metalness\",\r\n            \"bytes\": 24542,\r\n            \"sha256\": \"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-circuit-hide/tintMask\",\r\n            \"bytes\": 37986,\r\n            \"sha256\": \"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-circuit-hide/preview\",\r\n            \"bytes\": 44810,\r\n            \"sha256\": \"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-reactor-rust\",\r\n      \"displayName\": \"Reactor Rust\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Metal063\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal063\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-reactor-rust/basecolor.webp\",\r\n          \"bytes\": 11264,\r\n          \"sha256\": \"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-reactor-rust/normal.webp\",\r\n          \"bytes\": 550,\r\n          \"sha256\": \"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-reactor-rust/roughness.webp\",\r\n          \"bytes\": 29148,\r\n          \"sha256\": \"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-reactor-rust/height.webp\",\r\n          \"bytes\": 37754,\r\n          \"sha256\": \"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-reactor-rust/metalness.webp\",\r\n          \"bytes\": 43140,\r\n          \"sha256\": \"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-reactor-rust/tint-mask.webp\",\r\n          \"bytes\": 97766,\r\n          \"sha256\": \"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-reactor-rust/preview.webp\",\r\n          \"bytes\": 17150,\r\n          \"sha256\": \"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-reactor-rust/basecolor\",\r\n            \"bytes\": 11264,\r\n            \"sha256\": \"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-reactor-rust/normal\",\r\n            \"bytes\": 550,\r\n            \"sha256\": \"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-reactor-rust/roughness\",\r\n            \"bytes\": 29148,\r\n            \"sha256\": \"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-reactor-rust/height\",\r\n            \"bytes\": 37754,\r\n            \"sha256\": \"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-reactor-rust/metalness\",\r\n            \"bytes\": 43140,\r\n            \"sha256\": \"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-reactor-rust/tintMask\",\r\n            \"bytes\": 97766,\r\n            \"sha256\": \"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-reactor-rust/preview\",\r\n            \"bytes\": 17150,\r\n            \"sha256\": \"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-aero-mesh\",\r\n      \"displayName\": \"Aero Mesh\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"MetalWalkway013\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/MetalWalkway013\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-aero-mesh/basecolor.webp\",\r\n          \"bytes\": 9048,\r\n          \"sha256\": \"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-aero-mesh/normal.webp\",\r\n          \"bytes\": 55928,\r\n          \"sha256\": \"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-aero-mesh/roughness.webp\",\r\n          \"bytes\": 27652,\r\n          \"sha256\": \"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-aero-mesh/height.webp\",\r\n          \"bytes\": 29354,\r\n          \"sha256\": \"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-aero-mesh/metalness.webp\",\r\n          \"bytes\": 1340,\r\n          \"sha256\": \"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"\r\n        },\r\n        \"opacity\": {\r\n          \"file\": \"textures/creature-aero-mesh/opacity.webp\",\r\n          \"bytes\": 28608,\r\n          \"sha256\": \"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-aero-mesh/tint-mask.webp\",\r\n          \"bytes\": 23612,\r\n          \"sha256\": \"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-aero-mesh/preview.webp\",\r\n          \"bytes\": 11188,\r\n          \"sha256\": \"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-aero-mesh/basecolor\",\r\n            \"bytes\": 9048,\r\n            \"sha256\": \"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-aero-mesh/normal\",\r\n            \"bytes\": 55928,\r\n            \"sha256\": \"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-aero-mesh/roughness\",\r\n            \"bytes\": 27652,\r\n            \"sha256\": \"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-aero-mesh/height\",\r\n            \"bytes\": 29354,\r\n            \"sha256\": \"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-aero-mesh/metalness\",\r\n            \"bytes\": 1340,\r\n            \"sha256\": \"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"\r\n          },\r\n          \"opacity\": {\r\n            \"entry\": \"creature-aero-mesh/opacity\",\r\n            \"bytes\": 28608,\r\n            \"sha256\": \"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-aero-mesh/tintMask\",\r\n            \"bytes\": 23612,\r\n            \"sha256\": \"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-aero-mesh/preview\",\r\n            \"bytes\": 11188,\r\n            \"sha256\": \"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-zen-sand\",\r\n      \"displayName\": \"Zen Sand\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Ground097\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Ground097\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-zen-sand/basecolor.webp\",\r\n          \"bytes\": 2566,\r\n          \"sha256\": \"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-zen-sand/normal.webp\",\r\n          \"bytes\": 25542,\r\n          \"sha256\": \"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-zen-sand/roughness.webp\",\r\n          \"bytes\": 9672,\r\n          \"sha256\": \"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-zen-sand/height.webp\",\r\n          \"bytes\": 14040,\r\n          \"sha256\": \"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-zen-sand/ao.webp\",\r\n          \"bytes\": 2322,\r\n          \"sha256\": \"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-zen-sand/tint-mask.webp\",\r\n          \"bytes\": 27572,\r\n          \"sha256\": \"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-zen-sand/preview.webp\",\r\n          \"bytes\": 3032,\r\n          \"sha256\": \"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-zen-sand/basecolor\",\r\n            \"bytes\": 2566,\r\n            \"sha256\": \"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-zen-sand/normal\",\r\n            \"bytes\": 25542,\r\n            \"sha256\": \"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-zen-sand/roughness\",\r\n            \"bytes\": 9672,\r\n            \"sha256\": \"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-zen-sand/height\",\r\n            \"bytes\": 14040,\r\n            \"sha256\": \"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-zen-sand/ao\",\r\n            \"bytes\": 2322,\r\n            \"sha256\": \"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-zen-sand/tintMask\",\r\n            \"bytes\": 27572,\r\n            \"sha256\": \"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-zen-sand/preview\",\r\n            \"bytes\": 3032,\r\n            \"sha256\": \"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-river-stone\",\r\n      \"displayName\": \"River Stone\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Rock020\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Rock020\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-river-stone/basecolor.webp\",\r\n          \"bytes\": 49168,\r\n          \"sha256\": \"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-river-stone/normal.webp\",\r\n          \"bytes\": 99336,\r\n          \"sha256\": \"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-river-stone/roughness.webp\",\r\n          \"bytes\": 13096,\r\n          \"sha256\": \"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-river-stone/height.webp\",\r\n          \"bytes\": 9202,\r\n          \"sha256\": \"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-river-stone/ao.webp\",\r\n          \"bytes\": 22322,\r\n          \"sha256\": \"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-river-stone/tint-mask.webp\",\r\n          \"bytes\": 76542,\r\n          \"sha256\": \"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-river-stone/preview.webp\",\r\n          \"bytes\": 57812,\r\n          \"sha256\": \"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-river-stone/basecolor\",\r\n            \"bytes\": 49168,\r\n            \"sha256\": \"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-river-stone/normal\",\r\n            \"bytes\": 99336,\r\n            \"sha256\": \"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-river-stone/roughness\",\r\n            \"bytes\": 13096,\r\n            \"sha256\": \"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-river-stone/height\",\r\n            \"bytes\": 9202,\r\n            \"sha256\": \"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-river-stone/ao\",\r\n            \"bytes\": 22322,\r\n            \"sha256\": \"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-river-stone/tintMask\",\r\n            \"bytes\": 76542,\r\n            \"sha256\": \"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-river-stone/preview\",\r\n            \"bytes\": 57812,\r\n            \"sha256\": \"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-biolume-moss\",\r\n      \"displayName\": \"Biolume Moss\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Moss002\",\r\n      \"collection\": \"creature-forged-realms\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Moss002\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-biolume-moss/basecolor.webp\",\r\n          \"bytes\": 73648,\r\n          \"sha256\": \"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-biolume-moss/normal.webp\",\r\n          \"bytes\": 87824,\r\n          \"sha256\": \"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-biolume-moss/roughness.webp\",\r\n          \"bytes\": 39816,\r\n          \"sha256\": \"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-biolume-moss/height.webp\",\r\n          \"bytes\": 25618,\r\n          \"sha256\": \"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-biolume-moss/ao.webp\",\r\n          \"bytes\": 78346,\r\n          \"sha256\": \"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-biolume-moss/tint-mask.webp\",\r\n          \"bytes\": 99630,\r\n          \"sha256\": \"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-biolume-moss/preview.webp\",\r\n          \"bytes\": 77278,\r\n          \"sha256\": \"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-biolume-moss/basecolor\",\r\n            \"bytes\": 73648,\r\n            \"sha256\": \"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-biolume-moss/normal\",\r\n            \"bytes\": 87824,\r\n            \"sha256\": \"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-biolume-moss/roughness\",\r\n            \"bytes\": 39816,\r\n            \"sha256\": \"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-biolume-moss/height\",\r\n            \"bytes\": 25618,\r\n            \"sha256\": \"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-biolume-moss/ao\",\r\n            \"bytes\": 78346,\r\n            \"sha256\": \"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-biolume-moss/tintMask\",\r\n            \"bytes\": 99630,\r\n            \"sha256\": \"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-biolume-moss/preview\",\r\n            \"bytes\": 77278,\r\n            \"sha256\": \"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-mirror-knight\",\r\n      \"displayName\": \"Mirror Knight\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Metal048A\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal048A\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-mirror-knight/basecolor.webp\",\r\n          \"bytes\": 998,\r\n          \"sha256\": \"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-mirror-knight/normal.webp\",\r\n          \"bytes\": 594,\r\n          \"sha256\": \"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-mirror-knight/roughness.webp\",\r\n          \"bytes\": 732,\r\n          \"sha256\": \"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-mirror-knight/height.webp\",\r\n          \"bytes\": 526,\r\n          \"sha256\": \"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-mirror-knight/metalness.webp\",\r\n          \"bytes\": 532,\r\n          \"sha256\": \"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-mirror-knight/tint-mask.webp\",\r\n          \"bytes\": 67594,\r\n          \"sha256\": \"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-mirror-knight/preview.webp\",\r\n          \"bytes\": 1298,\r\n          \"sha256\": \"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-mirror-knight/basecolor\",\r\n            \"bytes\": 998,\r\n            \"sha256\": \"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-mirror-knight/normal\",\r\n            \"bytes\": 594,\r\n            \"sha256\": \"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-mirror-knight/roughness\",\r\n            \"bytes\": 732,\r\n            \"sha256\": \"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-mirror-knight/height\",\r\n            \"bytes\": 526,\r\n            \"sha256\": \"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-mirror-knight/metalness\",\r\n            \"bytes\": 532,\r\n            \"sha256\": \"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-mirror-knight/tintMask\",\r\n            \"bytes\": 67594,\r\n            \"sha256\": \"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-mirror-knight/preview\",\r\n            \"bytes\": 1298,\r\n            \"sha256\": \"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-tarnished-crown\",\r\n      \"displayName\": \"Tarnished Crown\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Metal048C\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal048C\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-tarnished-crown/basecolor.webp\",\r\n          \"bytes\": 10056,\r\n          \"sha256\": \"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-tarnished-crown/normal.webp\",\r\n          \"bytes\": 902,\r\n          \"sha256\": \"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-tarnished-crown/roughness.webp\",\r\n          \"bytes\": 23264,\r\n          \"sha256\": \"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-tarnished-crown/height.webp\",\r\n          \"bytes\": 6612,\r\n          \"sha256\": \"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-tarnished-crown/metalness.webp\",\r\n          \"bytes\": 9582,\r\n          \"sha256\": \"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-tarnished-crown/tint-mask.webp\",\r\n          \"bytes\": 48438,\r\n          \"sha256\": \"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-tarnished-crown/preview.webp\",\r\n          \"bytes\": 11978,\r\n          \"sha256\": \"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-tarnished-crown/basecolor\",\r\n            \"bytes\": 10056,\r\n            \"sha256\": \"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-tarnished-crown/normal\",\r\n            \"bytes\": 902,\r\n            \"sha256\": \"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-tarnished-crown/roughness\",\r\n            \"bytes\": 23264,\r\n            \"sha256\": \"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-tarnished-crown/height\",\r\n            \"bytes\": 6612,\r\n            \"sha256\": \"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-tarnished-crown/metalness\",\r\n            \"bytes\": 9582,\r\n            \"sha256\": \"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-tarnished-crown/tintMask\",\r\n            \"bytes\": 48438,\r\n            \"sha256\": \"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-tarnished-crown/preview\",\r\n            \"bytes\": 11978,\r\n            \"sha256\": \"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-golden-scarab\",\r\n      \"displayName\": \"Golden Scarab\",\r\n      \"track\": \"chest\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Metal007\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Metal007\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-golden-scarab/basecolor.webp\",\r\n          \"bytes\": 18264,\r\n          \"sha256\": \"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-golden-scarab/normal.webp\",\r\n          \"bytes\": 1042,\r\n          \"sha256\": \"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-golden-scarab/roughness.webp\",\r\n          \"bytes\": 15890,\r\n          \"sha256\": \"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-golden-scarab/height.webp\",\r\n          \"bytes\": 1714,\r\n          \"sha256\": \"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-golden-scarab/metalness.webp\",\r\n          \"bytes\": 1280,\r\n          \"sha256\": \"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-golden-scarab/tint-mask.webp\",\r\n          \"bytes\": 65652,\r\n          \"sha256\": \"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-golden-scarab/preview.webp\",\r\n          \"bytes\": 22470,\r\n          \"sha256\": \"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"chest\"\r\n        },\r\n        \"pack\": \"track-chest\",\r\n        \"bundle\": \"assets/chest.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-golden-scarab/basecolor\",\r\n            \"bytes\": 18264,\r\n            \"sha256\": \"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-golden-scarab/normal\",\r\n            \"bytes\": 1042,\r\n            \"sha256\": \"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-golden-scarab/roughness\",\r\n            \"bytes\": 15890,\r\n            \"sha256\": \"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-golden-scarab/height\",\r\n            \"bytes\": 1714,\r\n            \"sha256\": \"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-golden-scarab/metalness\",\r\n            \"bytes\": 1280,\r\n            \"sha256\": \"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-golden-scarab/tintMask\",\r\n            \"bytes\": 65652,\r\n            \"sha256\": \"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-golden-scarab/preview\",\r\n            \"bytes\": 22470,\r\n            \"sha256\": \"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-emberflow\",\r\n      \"displayName\": \"Emberflow\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Lava001\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Lava001\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-emberflow/basecolor.webp\",\r\n          \"bytes\": 55502,\r\n          \"sha256\": \"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-emberflow/normal.webp\",\r\n          \"bytes\": 105598,\r\n          \"sha256\": \"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-emberflow/roughness.webp\",\r\n          \"bytes\": 12882,\r\n          \"sha256\": \"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-emberflow/height.webp\",\r\n          \"bytes\": 13998,\r\n          \"sha256\": \"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"\r\n        },\r\n        \"emissive\": {\r\n          \"file\": \"textures/creature-emberflow/emissive.webp\",\r\n          \"bytes\": 52850,\r\n          \"sha256\": \"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-emberflow/tint-mask.webp\",\r\n          \"bytes\": 51236,\r\n          \"sha256\": \"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-emberflow/preview.webp\",\r\n          \"bytes\": 57834,\r\n          \"sha256\": \"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-emberflow/basecolor\",\r\n            \"bytes\": 55502,\r\n            \"sha256\": \"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-emberflow/normal\",\r\n            \"bytes\": 105598,\r\n            \"sha256\": \"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-emberflow/roughness\",\r\n            \"bytes\": 12882,\r\n            \"sha256\": \"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-emberflow/height\",\r\n            \"bytes\": 13998,\r\n            \"sha256\": \"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"\r\n          },\r\n          \"emissive\": {\r\n            \"entry\": \"creature-emberflow/emissive\",\r\n            \"bytes\": 52850,\r\n            \"sha256\": \"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-emberflow/tintMask\",\r\n            \"bytes\": 51236,\r\n            \"sha256\": \"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-emberflow/preview\",\r\n            \"bytes\": 57834,\r\n            \"sha256\": \"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-volcanic-heart\",\r\n      \"displayName\": \"Volcanic Heart\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Lava005\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Lava005\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-volcanic-heart/basecolor.webp\",\r\n          \"bytes\": 52804,\r\n          \"sha256\": \"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-volcanic-heart/normal.webp\",\r\n          \"bytes\": 54736,\r\n          \"sha256\": \"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-volcanic-heart/roughness.webp\",\r\n          \"bytes\": 20200,\r\n          \"sha256\": \"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-volcanic-heart/height.webp\",\r\n          \"bytes\": 11990,\r\n          \"sha256\": \"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"\r\n        },\r\n        \"emissive\": {\r\n          \"file\": \"textures/creature-volcanic-heart/emissive.webp\",\r\n          \"bytes\": 61420,\r\n          \"sha256\": \"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-volcanic-heart/tint-mask.webp\",\r\n          \"bytes\": 53772,\r\n          \"sha256\": \"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-volcanic-heart/preview.webp\",\r\n          \"bytes\": 51986,\r\n          \"sha256\": \"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-volcanic-heart/basecolor\",\r\n            \"bytes\": 52804,\r\n            \"sha256\": \"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-volcanic-heart/normal\",\r\n            \"bytes\": 54736,\r\n            \"sha256\": \"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-volcanic-heart/roughness\",\r\n            \"bytes\": 20200,\r\n            \"sha256\": \"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-volcanic-heart/height\",\r\n            \"bytes\": 11990,\r\n            \"sha256\": \"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"\r\n          },\r\n          \"emissive\": {\r\n            \"entry\": \"creature-volcanic-heart/emissive\",\r\n            \"bytes\": 61420,\r\n            \"sha256\": \"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-volcanic-heart/tintMask\",\r\n            \"bytes\": 53772,\r\n            \"sha256\": \"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-volcanic-heart/preview\",\r\n            \"bytes\": 51986,\r\n            \"sha256\": \"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-aurora-ice\",\r\n      \"displayName\": \"Aurora Ice\",\r\n      \"track\": \"quads\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Ice004\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Ice004\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-aurora-ice/basecolor.webp\",\r\n          \"bytes\": 28102,\r\n          \"sha256\": \"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-aurora-ice/normal.webp\",\r\n          \"bytes\": 9418,\r\n          \"sha256\": \"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-aurora-ice/roughness.webp\",\r\n          \"bytes\": 16912,\r\n          \"sha256\": \"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-aurora-ice/height.webp\",\r\n          \"bytes\": 24584,\r\n          \"sha256\": \"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-aurora-ice/tint-mask.webp\",\r\n          \"bytes\": 33120,\r\n          \"sha256\": \"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-aurora-ice/preview.webp\",\r\n          \"bytes\": 33622,\r\n          \"sha256\": \"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"quads\"\r\n        },\r\n        \"pack\": \"track-quads\",\r\n        \"bundle\": \"assets/quads.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-aurora-ice/basecolor\",\r\n            \"bytes\": 28102,\r\n            \"sha256\": \"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-aurora-ice/normal\",\r\n            \"bytes\": 9418,\r\n            \"sha256\": \"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-aurora-ice/roughness\",\r\n            \"bytes\": 16912,\r\n            \"sha256\": \"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-aurora-ice/height\",\r\n            \"bytes\": 24584,\r\n            \"sha256\": \"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-aurora-ice/tintMask\",\r\n            \"bytes\": 33120,\r\n            \"sha256\": \"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-aurora-ice/preview\",\r\n            \"bytes\": 33622,\r\n            \"sha256\": \"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-ironbark\",\r\n      \"displayName\": \"Ironbark\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Bark014\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bark014\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-ironbark/basecolor.webp\",\r\n          \"bytes\": 60524,\r\n          \"sha256\": \"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-ironbark/normal.webp\",\r\n          \"bytes\": 39240,\r\n          \"sha256\": \"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-ironbark/roughness.webp\",\r\n          \"bytes\": 61578,\r\n          \"sha256\": \"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-ironbark/height.webp\",\r\n          \"bytes\": 11624,\r\n          \"sha256\": \"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-ironbark/ao.webp\",\r\n          \"bytes\": 46376,\r\n          \"sha256\": \"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-ironbark/tint-mask.webp\",\r\n          \"bytes\": 71852,\r\n          \"sha256\": \"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-ironbark/preview.webp\",\r\n          \"bytes\": 68160,\r\n          \"sha256\": \"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-ironbark/basecolor\",\r\n            \"bytes\": 60524,\r\n            \"sha256\": \"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-ironbark/normal\",\r\n            \"bytes\": 39240,\r\n            \"sha256\": \"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-ironbark/roughness\",\r\n            \"bytes\": 61578,\r\n            \"sha256\": \"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-ironbark/height\",\r\n            \"bytes\": 11624,\r\n            \"sha256\": \"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-ironbark/ao\",\r\n            \"bytes\": 46376,\r\n            \"sha256\": \"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-ironbark/tintMask\",\r\n            \"bytes\": 71852,\r\n            \"sha256\": \"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-ironbark/preview\",\r\n            \"bytes\": 68160,\r\n            \"sha256\": \"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-oakscale\",\r\n      \"displayName\": \"Oakscale\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Bark012\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bark012\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-oakscale/basecolor.webp\",\r\n          \"bytes\": 71646,\r\n          \"sha256\": \"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-oakscale/normal.webp\",\r\n          \"bytes\": 122322,\r\n          \"sha256\": \"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-oakscale/roughness.webp\",\r\n          \"bytes\": 56118,\r\n          \"sha256\": \"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-oakscale/height.webp\",\r\n          \"bytes\": 36178,\r\n          \"sha256\": \"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-oakscale/ao.webp\",\r\n          \"bytes\": 59772,\r\n          \"sha256\": \"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-oakscale/tint-mask.webp\",\r\n          \"bytes\": 89244,\r\n          \"sha256\": \"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-oakscale/preview.webp\",\r\n          \"bytes\": 80436,\r\n          \"sha256\": \"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-oakscale/basecolor\",\r\n            \"bytes\": 71646,\r\n            \"sha256\": \"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-oakscale/normal\",\r\n            \"bytes\": 122322,\r\n            \"sha256\": \"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-oakscale/roughness\",\r\n            \"bytes\": 56118,\r\n            \"sha256\": \"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-oakscale/height\",\r\n            \"bytes\": 36178,\r\n            \"sha256\": \"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-oakscale/ao\",\r\n            \"bytes\": 59772,\r\n            \"sha256\": \"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-oakscale/tintMask\",\r\n            \"bytes\": 89244,\r\n            \"sha256\": \"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-oakscale/preview\",\r\n            \"bytes\": 80436,\r\n            \"sha256\": \"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-mosswood\",\r\n      \"displayName\": \"Mosswood\",\r\n      \"track\": \"glutes\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Bark008\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Bark008\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-mosswood/basecolor.webp\",\r\n          \"bytes\": 59368,\r\n          \"sha256\": \"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-mosswood/normal.webp\",\r\n          \"bytes\": 115452,\r\n          \"sha256\": \"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-mosswood/roughness.webp\",\r\n          \"bytes\": 14720,\r\n          \"sha256\": \"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-mosswood/height.webp\",\r\n          \"bytes\": 14968,\r\n          \"sha256\": \"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-mosswood/ao.webp\",\r\n          \"bytes\": 30290,\r\n          \"sha256\": \"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-mosswood/tint-mask.webp\",\r\n          \"bytes\": 71764,\r\n          \"sha256\": \"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-mosswood/preview.webp\",\r\n          \"bytes\": 68928,\r\n          \"sha256\": \"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"glutes\"\r\n        },\r\n        \"pack\": \"track-glutes\",\r\n        \"bundle\": \"assets/glutes.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-mosswood/basecolor\",\r\n            \"bytes\": 59368,\r\n            \"sha256\": \"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-mosswood/normal\",\r\n            \"bytes\": 115452,\r\n            \"sha256\": \"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-mosswood/roughness\",\r\n            \"bytes\": 14720,\r\n            \"sha256\": \"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-mosswood/height\",\r\n            \"bytes\": 14968,\r\n            \"sha256\": \"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-mosswood/ao\",\r\n            \"bytes\": 30290,\r\n            \"sha256\": \"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-mosswood/tintMask\",\r\n            \"bytes\": 71764,\r\n            \"sha256\": \"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-mosswood/preview\",\r\n            \"bytes\": 68928,\r\n            \"sha256\": \"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-blood-onyx\",\r\n      \"displayName\": \"Blood Onyx\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Onyx010\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx010\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-blood-onyx/basecolor.webp\",\r\n          \"bytes\": 49258,\r\n          \"sha256\": \"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-blood-onyx/normal.webp\",\r\n          \"bytes\": 570,\r\n          \"sha256\": \"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-blood-onyx/roughness.webp\",\r\n          \"bytes\": 3160,\r\n          \"sha256\": \"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-blood-onyx/height.webp\",\r\n          \"bytes\": 2910,\r\n          \"sha256\": \"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-blood-onyx/tint-mask.webp\",\r\n          \"bytes\": 55394,\r\n          \"sha256\": \"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-blood-onyx/preview.webp\",\r\n          \"bytes\": 56594,\r\n          \"sha256\": \"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-blood-onyx/basecolor\",\r\n            \"bytes\": 49258,\r\n            \"sha256\": \"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-blood-onyx/normal\",\r\n            \"bytes\": 570,\r\n            \"sha256\": \"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-blood-onyx/roughness\",\r\n            \"bytes\": 3160,\r\n            \"sha256\": \"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-blood-onyx/height\",\r\n            \"bytes\": 2910,\r\n            \"sha256\": \"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-blood-onyx/tintMask\",\r\n            \"bytes\": 55394,\r\n            \"sha256\": \"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-blood-onyx/preview\",\r\n            \"bytes\": 56594,\r\n            \"sha256\": \"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-honey-crystal\",\r\n      \"displayName\": \"Honey Crystal\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Onyx007\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx007\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-honey-crystal/basecolor.webp\",\r\n          \"bytes\": 22816,\r\n          \"sha256\": \"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-honey-crystal/normal.webp\",\r\n          \"bytes\": 560,\r\n          \"sha256\": \"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-honey-crystal/roughness.webp\",\r\n          \"bytes\": 3020,\r\n          \"sha256\": \"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-honey-crystal/height.webp\",\r\n          \"bytes\": 2954,\r\n          \"sha256\": \"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-honey-crystal/tint-mask.webp\",\r\n          \"bytes\": 34036,\r\n          \"sha256\": \"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-honey-crystal/preview.webp\",\r\n          \"bytes\": 26820,\r\n          \"sha256\": \"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-honey-crystal/basecolor\",\r\n            \"bytes\": 22816,\r\n            \"sha256\": \"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-honey-crystal/normal\",\r\n            \"bytes\": 560,\r\n            \"sha256\": \"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-honey-crystal/roughness\",\r\n            \"bytes\": 3020,\r\n            \"sha256\": \"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-honey-crystal/height\",\r\n            \"bytes\": 2954,\r\n            \"sha256\": \"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-honey-crystal/tintMask\",\r\n            \"bytes\": 34036,\r\n            \"sha256\": \"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-honey-crystal/preview\",\r\n            \"bytes\": 26820,\r\n            \"sha256\": \"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-void-mirror\",\r\n      \"displayName\": \"Void Mirror\",\r\n      \"track\": \"arms\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Onyx014\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx014\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-void-mirror/basecolor.webp\",\r\n          \"bytes\": 53930,\r\n          \"sha256\": \"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-void-mirror/normal.webp\",\r\n          \"bytes\": 576,\r\n          \"sha256\": \"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-void-mirror/roughness.webp\",\r\n          \"bytes\": 3126,\r\n          \"sha256\": \"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-void-mirror/height.webp\",\r\n          \"bytes\": 3520,\r\n          \"sha256\": \"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-void-mirror/tint-mask.webp\",\r\n          \"bytes\": 62026,\r\n          \"sha256\": \"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-void-mirror/preview.webp\",\r\n          \"bytes\": 61294,\r\n          \"sha256\": \"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"arms\"\r\n        },\r\n        \"pack\": \"track-arms\",\r\n        \"bundle\": \"assets/arms.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-void-mirror/basecolor\",\r\n            \"bytes\": 53930,\r\n            \"sha256\": \"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-void-mirror/normal\",\r\n            \"bytes\": 576,\r\n            \"sha256\": \"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-void-mirror/roughness\",\r\n            \"bytes\": 3126,\r\n            \"sha256\": \"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-void-mirror/height\",\r\n            \"bytes\": 3520,\r\n            \"sha256\": \"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-void-mirror/tintMask\",\r\n            \"bytes\": 62026,\r\n            \"sha256\": \"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-void-mirror/preview\",\r\n            \"bytes\": 61294,\r\n            \"sha256\": \"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-snow-wraith\",\r\n      \"displayName\": \"Snow Wraith\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Snow014\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Snow014\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-snow-wraith/basecolor.webp\",\r\n          \"bytes\": 6634,\r\n          \"sha256\": \"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-snow-wraith/normal.webp\",\r\n          \"bytes\": 25340,\r\n          \"sha256\": \"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-snow-wraith/roughness.webp\",\r\n          \"bytes\": 11026,\r\n          \"sha256\": \"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-snow-wraith/height.webp\",\r\n          \"bytes\": 11568,\r\n          \"sha256\": \"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-snow-wraith/ao.webp\",\r\n          \"bytes\": 4376,\r\n          \"sha256\": \"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-snow-wraith/tint-mask.webp\",\r\n          \"bytes\": 47502,\r\n          \"sha256\": \"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-snow-wraith/preview.webp\",\r\n          \"bytes\": 8238,\r\n          \"sha256\": \"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-snow-wraith/basecolor\",\r\n            \"bytes\": 6634,\r\n            \"sha256\": \"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-snow-wraith/normal\",\r\n            \"bytes\": 25340,\r\n            \"sha256\": \"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-snow-wraith/roughness\",\r\n            \"bytes\": 11026,\r\n            \"sha256\": \"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-snow-wraith/height\",\r\n            \"bytes\": 11568,\r\n            \"sha256\": \"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-snow-wraith/ao\",\r\n            \"bytes\": 4376,\r\n            \"sha256\": \"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-snow-wraith/tintMask\",\r\n            \"bytes\": 47502,\r\n            \"sha256\": \"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-snow-wraith/preview\",\r\n            \"bytes\": 8238,\r\n            \"sha256\": \"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-moon-drift\",\r\n      \"displayName\": \"Moon Drift\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Snow008A\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Snow008A\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-moon-drift/basecolor.webp\",\r\n          \"bytes\": 1988,\r\n          \"sha256\": \"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-moon-drift/normal.webp\",\r\n          \"bytes\": 19748,\r\n          \"sha256\": \"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-moon-drift/roughness.webp\",\r\n          \"bytes\": 42334,\r\n          \"sha256\": \"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-moon-drift/height.webp\",\r\n          \"bytes\": 10336,\r\n          \"sha256\": \"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-moon-drift/ao.webp\",\r\n          \"bytes\": 5248,\r\n          \"sha256\": \"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-moon-drift/tint-mask.webp\",\r\n          \"bytes\": 62850,\r\n          \"sha256\": \"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-moon-drift/preview.webp\",\r\n          \"bytes\": 2558,\r\n          \"sha256\": \"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-moon-drift/basecolor\",\r\n            \"bytes\": 1988,\r\n            \"sha256\": \"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-moon-drift/normal\",\r\n            \"bytes\": 19748,\r\n            \"sha256\": \"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-moon-drift/roughness\",\r\n            \"bytes\": 42334,\r\n            \"sha256\": \"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-moon-drift/height\",\r\n            \"bytes\": 10336,\r\n            \"sha256\": \"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-moon-drift/ao\",\r\n            \"bytes\": 5248,\r\n            \"sha256\": \"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-moon-drift/tintMask\",\r\n            \"bytes\": 62850,\r\n            \"sha256\": \"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-moon-drift/preview\",\r\n            \"bytes\": 2558,\r\n            \"sha256\": \"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-pearl-spirit\",\r\n      \"displayName\": \"Pearl Spirit\",\r\n      \"track\": \"yoga\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Onyx015\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Onyx015\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-pearl-spirit/basecolor.webp\",\r\n          \"bytes\": 29558,\r\n          \"sha256\": \"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-pearl-spirit/normal.webp\",\r\n          \"bytes\": 578,\r\n          \"sha256\": \"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-pearl-spirit/roughness.webp\",\r\n          \"bytes\": 2712,\r\n          \"sha256\": \"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-pearl-spirit/height.webp\",\r\n          \"bytes\": 2632,\r\n          \"sha256\": \"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-pearl-spirit/tint-mask.webp\",\r\n          \"bytes\": 70938,\r\n          \"sha256\": \"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-pearl-spirit/preview.webp\",\r\n          \"bytes\": 36178,\r\n          \"sha256\": \"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"yoga\"\r\n        },\r\n        \"pack\": \"track-yoga\",\r\n        \"bundle\": \"assets/yoga.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-pearl-spirit/basecolor\",\r\n            \"bytes\": 29558,\r\n            \"sha256\": \"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-pearl-spirit/normal\",\r\n            \"bytes\": 578,\r\n            \"sha256\": \"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-pearl-spirit/roughness\",\r\n            \"bytes\": 2712,\r\n            \"sha256\": \"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-pearl-spirit/height\",\r\n            \"bytes\": 2632,\r\n            \"sha256\": \"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-pearl-spirit/tintMask\",\r\n            \"bytes\": 70938,\r\n            \"sha256\": \"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-pearl-spirit/preview\",\r\n            \"bytes\": 36178,\r\n            \"sha256\": \"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-red-desert-scale\",\r\n      \"displayName\": \"Red Desert Scale\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Rock029\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Rock029\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-red-desert-scale/basecolor.webp\",\r\n          \"bytes\": 51964,\r\n          \"sha256\": \"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-red-desert-scale/normal.webp\",\r\n          \"bytes\": 120276,\r\n          \"sha256\": \"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-red-desert-scale/roughness.webp\",\r\n          \"bytes\": 28424,\r\n          \"sha256\": \"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-red-desert-scale/height.webp\",\r\n          \"bytes\": 8842,\r\n          \"sha256\": \"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-red-desert-scale/ao.webp\",\r\n          \"bytes\": 19082,\r\n          \"sha256\": \"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-red-desert-scale/tint-mask.webp\",\r\n          \"bytes\": 83596,\r\n          \"sha256\": \"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-red-desert-scale/preview.webp\",\r\n          \"bytes\": 57218,\r\n          \"sha256\": \"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-red-desert-scale/basecolor\",\r\n            \"bytes\": 51964,\r\n            \"sha256\": \"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-red-desert-scale/normal\",\r\n            \"bytes\": 120276,\r\n            \"sha256\": \"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-red-desert-scale/roughness\",\r\n            \"bytes\": 28424,\r\n            \"sha256\": \"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-red-desert-scale/height\",\r\n            \"bytes\": 8842,\r\n            \"sha256\": \"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-red-desert-scale/ao\",\r\n            \"bytes\": 19082,\r\n            \"sha256\": \"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-red-desert-scale/tintMask\",\r\n            \"bytes\": 83596,\r\n            \"sha256\": \"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-red-desert-scale/preview\",\r\n            \"bytes\": 57218,\r\n            \"sha256\": \"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-cave-drake\",\r\n      \"displayName\": \"Cave Drake\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Rock035\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Rock035\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-cave-drake/basecolor.webp\",\r\n          \"bytes\": 42954,\r\n          \"sha256\": \"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-cave-drake/normal.webp\",\r\n          \"bytes\": 126116,\r\n          \"sha256\": \"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-cave-drake/roughness.webp\",\r\n          \"bytes\": 37152,\r\n          \"sha256\": \"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-cave-drake/height.webp\",\r\n          \"bytes\": 10778,\r\n          \"sha256\": \"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-cave-drake/ao.webp\",\r\n          \"bytes\": 20824,\r\n          \"sha256\": \"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-cave-drake/tint-mask.webp\",\r\n          \"bytes\": 59714,\r\n          \"sha256\": \"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-cave-drake/preview.webp\",\r\n          \"bytes\": 52250,\r\n          \"sha256\": \"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-cave-drake/basecolor\",\r\n            \"bytes\": 42954,\r\n            \"sha256\": \"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-cave-drake/normal\",\r\n            \"bytes\": 126116,\r\n            \"sha256\": \"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-cave-drake/roughness\",\r\n            \"bytes\": 37152,\r\n            \"sha256\": \"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-cave-drake/height\",\r\n            \"bytes\": 10778,\r\n            \"sha256\": \"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-cave-drake/ao\",\r\n            \"bytes\": 20824,\r\n            \"sha256\": \"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-cave-drake/tintMask\",\r\n            \"bytes\": 59714,\r\n            \"sha256\": \"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-cave-drake/preview\",\r\n            \"bytes\": 52250,\r\n            \"sha256\": \"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-sandstone-titan\",\r\n      \"displayName\": \"Sandstone Titan\",\r\n      \"track\": \"martial-arts\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Rock061\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Rock061\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-sandstone-titan/basecolor.webp\",\r\n          \"bytes\": 19630,\r\n          \"sha256\": \"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-sandstone-titan/normal.webp\",\r\n          \"bytes\": 59070,\r\n          \"sha256\": \"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-sandstone-titan/roughness.webp\",\r\n          \"bytes\": 55850,\r\n          \"sha256\": \"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-sandstone-titan/height.webp\",\r\n          \"bytes\": 8100,\r\n          \"sha256\": \"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-sandstone-titan/ao.webp\",\r\n          \"bytes\": 15348,\r\n          \"sha256\": \"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-sandstone-titan/tint-mask.webp\",\r\n          \"bytes\": 41274,\r\n          \"sha256\": \"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-sandstone-titan/preview.webp\",\r\n          \"bytes\": 24198,\r\n          \"sha256\": \"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"martial-arts\"\r\n        },\r\n        \"pack\": \"track-martial-arts\",\r\n        \"bundle\": \"assets/martial-arts.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-sandstone-titan/basecolor\",\r\n            \"bytes\": 19630,\r\n            \"sha256\": \"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-sandstone-titan/normal\",\r\n            \"bytes\": 59070,\r\n            \"sha256\": \"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-sandstone-titan/roughness\",\r\n            \"bytes\": 55850,\r\n            \"sha256\": \"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-sandstone-titan/height\",\r\n            \"bytes\": 8100,\r\n            \"sha256\": \"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-sandstone-titan/ao\",\r\n            \"bytes\": 15348,\r\n            \"sha256\": \"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-sandstone-titan/tintMask\",\r\n            \"bytes\": 41274,\r\n            \"sha256\": \"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-sandstone-titan/preview\",\r\n            \"bytes\": 24198,\r\n            \"sha256\": \"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-neon-mainframe\",\r\n      \"displayName\": \"Neon Mainframe\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Chip002\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Chip002\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-neon-mainframe/basecolor.webp\",\r\n          \"bytes\": 56744,\r\n          \"sha256\": \"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-neon-mainframe/normal.webp\",\r\n          \"bytes\": 37362,\r\n          \"sha256\": \"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-neon-mainframe/roughness.webp\",\r\n          \"bytes\": 41338,\r\n          \"sha256\": \"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-neon-mainframe/height.webp\",\r\n          \"bytes\": 33104,\r\n          \"sha256\": \"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-neon-mainframe/metalness.webp\",\r\n          \"bytes\": 45012,\r\n          \"sha256\": \"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-neon-mainframe/tint-mask.webp\",\r\n          \"bytes\": 50140,\r\n          \"sha256\": \"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-neon-mainframe/preview.webp\",\r\n          \"bytes\": 61118,\r\n          \"sha256\": \"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-neon-mainframe/basecolor\",\r\n            \"bytes\": 56744,\r\n            \"sha256\": \"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-neon-mainframe/normal\",\r\n            \"bytes\": 37362,\r\n            \"sha256\": \"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-neon-mainframe/roughness\",\r\n            \"bytes\": 41338,\r\n            \"sha256\": \"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-neon-mainframe/height\",\r\n            \"bytes\": 33104,\r\n            \"sha256\": \"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-neon-mainframe/metalness\",\r\n            \"bytes\": 45012,\r\n            \"sha256\": \"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-neon-mainframe/tintMask\",\r\n            \"bytes\": 50140,\r\n            \"sha256\": \"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-neon-mainframe/preview\",\r\n            \"bytes\": 61118,\r\n            \"sha256\": \"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-reactor-grid\",\r\n      \"displayName\": \"Reactor Grid\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Chip004\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Chip004\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-reactor-grid/basecolor.webp\",\r\n          \"bytes\": 68780,\r\n          \"sha256\": \"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-reactor-grid/normal.webp\",\r\n          \"bytes\": 30258,\r\n          \"sha256\": \"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-reactor-grid/roughness.webp\",\r\n          \"bytes\": 28938,\r\n          \"sha256\": \"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-reactor-grid/height.webp\",\r\n          \"bytes\": 18820,\r\n          \"sha256\": \"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-reactor-grid/metalness.webp\",\r\n          \"bytes\": 8980,\r\n          \"sha256\": \"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-reactor-grid/tint-mask.webp\",\r\n          \"bytes\": 50226,\r\n          \"sha256\": \"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-reactor-grid/preview.webp\",\r\n          \"bytes\": 70556,\r\n          \"sha256\": \"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-reactor-grid/basecolor\",\r\n            \"bytes\": 68780,\r\n            \"sha256\": \"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-reactor-grid/normal\",\r\n            \"bytes\": 30258,\r\n            \"sha256\": \"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-reactor-grid/roughness\",\r\n            \"bytes\": 28938,\r\n            \"sha256\": \"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-reactor-grid/height\",\r\n            \"bytes\": 18820,\r\n            \"sha256\": \"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-reactor-grid/metalness\",\r\n            \"bytes\": 8980,\r\n            \"sha256\": \"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-reactor-grid/tintMask\",\r\n            \"bytes\": 50226,\r\n            \"sha256\": \"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-reactor-grid/preview\",\r\n            \"bytes\": 70556,\r\n            \"sha256\": \"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-quantum-core\",\r\n      \"displayName\": \"Quantum Core\",\r\n      \"track\": \"cardio\",\r\n      \"realm\": \"sci-fi\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Chip007\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Chip007\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-quantum-core/basecolor.webp\",\r\n          \"bytes\": 51286,\r\n          \"sha256\": \"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-quantum-core/normal.webp\",\r\n          \"bytes\": 31990,\r\n          \"sha256\": \"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-quantum-core/roughness.webp\",\r\n          \"bytes\": 31284,\r\n          \"sha256\": \"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-quantum-core/height.webp\",\r\n          \"bytes\": 29580,\r\n          \"sha256\": \"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"\r\n        },\r\n        \"metalness\": {\r\n          \"file\": \"textures/creature-quantum-core/metalness.webp\",\r\n          \"bytes\": 38108,\r\n          \"sha256\": \"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-quantum-core/tint-mask.webp\",\r\n          \"bytes\": 41968,\r\n          \"sha256\": \"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-quantum-core/preview.webp\",\r\n          \"bytes\": 56764,\r\n          \"sha256\": \"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"cardio\"\r\n        },\r\n        \"pack\": \"track-cardio\",\r\n        \"bundle\": \"assets/cardio.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-quantum-core/basecolor\",\r\n            \"bytes\": 51286,\r\n            \"sha256\": \"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-quantum-core/normal\",\r\n            \"bytes\": 31990,\r\n            \"sha256\": \"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-quantum-core/roughness\",\r\n            \"bytes\": 31284,\r\n            \"sha256\": \"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-quantum-core/height\",\r\n            \"bytes\": 29580,\r\n            \"sha256\": \"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"\r\n          },\r\n          \"metalness\": {\r\n            \"entry\": \"creature-quantum-core/metalness\",\r\n            \"bytes\": 38108,\r\n            \"sha256\": \"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-quantum-core/tintMask\",\r\n            \"bytes\": 41968,\r\n            \"sha256\": \"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-quantum-core/preview\",\r\n            \"bytes\": 56764,\r\n            \"sha256\": \"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-celestial-mosaic\",\r\n      \"displayName\": \"Celestial Mosaic\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"rare\",\r\n      \"sourceAssetId\": \"Tiles070\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 1,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Tiles070\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/basecolor.webp\",\r\n          \"bytes\": 72806,\r\n          \"sha256\": \"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/normal.webp\",\r\n          \"bytes\": 40154,\r\n          \"sha256\": \"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/roughness.webp\",\r\n          \"bytes\": 47808,\r\n          \"sha256\": \"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/height.webp\",\r\n          \"bytes\": 38486,\r\n          \"sha256\": \"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/ao.webp\",\r\n          \"bytes\": 46104,\r\n          \"sha256\": \"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/tint-mask.webp\",\r\n          \"bytes\": 63262,\r\n          \"sha256\": \"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-celestial-mosaic/preview.webp\",\r\n          \"bytes\": 76982,\r\n          \"sha256\": \"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 1,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-celestial-mosaic/basecolor\",\r\n            \"bytes\": 72806,\r\n            \"sha256\": \"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-celestial-mosaic/normal\",\r\n            \"bytes\": 40154,\r\n            \"sha256\": \"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-celestial-mosaic/roughness\",\r\n            \"bytes\": 47808,\r\n            \"sha256\": \"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-celestial-mosaic/height\",\r\n            \"bytes\": 38486,\r\n            \"sha256\": \"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-celestial-mosaic/ao\",\r\n            \"bytes\": 46104,\r\n            \"sha256\": \"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-celestial-mosaic/tintMask\",\r\n            \"bytes\": 63262,\r\n            \"sha256\": \"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-celestial-mosaic/preview\",\r\n            \"bytes\": 76982,\r\n            \"sha256\": \"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-shellborn\",\r\n      \"displayName\": \"Shellborn\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"fantasy\",\r\n      \"rarity\": \"epic\",\r\n      \"sourceAssetId\": \"Ground060\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 2,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Ground060\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-shellborn/basecolor.webp\",\r\n          \"bytes\": 9684,\r\n          \"sha256\": \"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-shellborn/normal.webp\",\r\n          \"bytes\": 23222,\r\n          \"sha256\": \"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-shellborn/roughness.webp\",\r\n          \"bytes\": 8118,\r\n          \"sha256\": \"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-shellborn/height.webp\",\r\n          \"bytes\": 17122,\r\n          \"sha256\": \"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-shellborn/ao.webp\",\r\n          \"bytes\": 9036,\r\n          \"sha256\": \"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-shellborn/tint-mask.webp\",\r\n          \"bytes\": 50694,\r\n          \"sha256\": \"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-shellborn/preview.webp\",\r\n          \"bytes\": 12672,\r\n          \"sha256\": \"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 3,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-shellborn/basecolor\",\r\n            \"bytes\": 9684,\r\n            \"sha256\": \"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-shellborn/normal\",\r\n            \"bytes\": 23222,\r\n            \"sha256\": \"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-shellborn/roughness\",\r\n            \"bytes\": 8118,\r\n            \"sha256\": \"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-shellborn/height\",\r\n            \"bytes\": 17122,\r\n            \"sha256\": \"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-shellborn/ao\",\r\n            \"bytes\": 9036,\r\n            \"sha256\": \"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-shellborn/tintMask\",\r\n            \"bytes\": 50694,\r\n            \"sha256\": \"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-shellborn/preview\",\r\n            \"bytes\": 12672,\r\n            \"sha256\": \"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"id\": \"creature-cracked-starlight\",\r\n      \"displayName\": \"Cracked Starlight\",\r\n      \"track\": \"meditation\",\r\n      \"realm\": \"hybrid\",\r\n      \"rarity\": \"legendary\",\r\n      \"sourceAssetId\": \"Paint002\",\r\n      \"collection\": \"creature-celestial-rift\",\r\n      \"collectionSlot\": 3,\r\n      \"sourceUrl\": \"https://ambientcg.com/a/Paint002\",\r\n      \"license\": \"CC0-1.0\",\r\n      \"author\": \"ambientCG\",\r\n      \"resolution\": 512,\r\n      \"maps\": {\r\n        \"basecolor\": {\r\n          \"file\": \"textures/creature-cracked-starlight/basecolor.webp\",\r\n          \"bytes\": 2308,\r\n          \"sha256\": \"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"\r\n        },\r\n        \"normal\": {\r\n          \"file\": \"textures/creature-cracked-starlight/normal.webp\",\r\n          \"bytes\": 36906,\r\n          \"sha256\": \"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"\r\n        },\r\n        \"roughness\": {\r\n          \"file\": \"textures/creature-cracked-starlight/roughness.webp\",\r\n          \"bytes\": 27138,\r\n          \"sha256\": \"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"\r\n        },\r\n        \"height\": {\r\n          \"file\": \"textures/creature-cracked-starlight/height.webp\",\r\n          \"bytes\": 21122,\r\n          \"sha256\": \"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"\r\n        },\r\n        \"ao\": {\r\n          \"file\": \"textures/creature-cracked-starlight/ao.webp\",\r\n          \"bytes\": 39506,\r\n          \"sha256\": \"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"\r\n        },\r\n        \"tintMask\": {\r\n          \"file\": \"textures/creature-cracked-starlight/tint-mask.webp\",\r\n          \"bytes\": 37962,\r\n          \"sha256\": \"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"\r\n        },\r\n        \"preview\": {\r\n          \"file\": \"textures/creature-cracked-starlight/preview.webp\",\r\n          \"bytes\": 2858,\r\n          \"sha256\": \"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"\r\n        }\r\n      },\r\n      \"runtime\": {\r\n        \"unlock\": {\r\n          \"level\": 5,\r\n          \"track\": \"meditation\"\r\n        },\r\n        \"pack\": \"track-meditation\",\r\n        \"bundle\": \"assets/meditation.m5bundle\",\r\n        \"maps\": {\r\n          \"basecolor\": {\r\n            \"entry\": \"creature-cracked-starlight/basecolor\",\r\n            \"bytes\": 2308,\r\n            \"sha256\": \"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"\r\n          },\r\n          \"normal\": {\r\n            \"entry\": \"creature-cracked-starlight/normal\",\r\n            \"bytes\": 36906,\r\n            \"sha256\": \"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"\r\n          },\r\n          \"roughness\": {\r\n            \"entry\": \"creature-cracked-starlight/roughness\",\r\n            \"bytes\": 27138,\r\n            \"sha256\": \"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"\r\n          },\r\n          \"height\": {\r\n            \"entry\": \"creature-cracked-starlight/height\",\r\n            \"bytes\": 21122,\r\n            \"sha256\": \"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"\r\n          },\r\n          \"ao\": {\r\n            \"entry\": \"creature-cracked-starlight/ao\",\r\n            \"bytes\": 39506,\r\n            \"sha256\": \"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"\r\n          },\r\n          \"tintMask\": {\r\n            \"entry\": \"creature-cracked-starlight/tintMask\",\r\n            \"bytes\": 37962,\r\n            \"sha256\": \"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"\r\n          },\r\n          \"preview\": {\r\n            \"entry\": \"creature-cracked-starlight/preview\",\r\n            \"bytes\": 2858,\r\n            \"sha256\": \"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"\r\n          }\r\n        }\r\n      }\r\n    }\r\n  ]\r\n}\r\n","creature/source/creator/deform.ts":"import * as THREE from 'three';\r\nimport type {Design,Region} from './design';\r\nexport function deformMesh(o:THREE.Mesh,region:Region,d:Design){\r\n const position=o.userData.basePosition as THREE.Vector3,scale=o.userData.baseScale as THREE.Vector3;\r\n if(!position||!scale)return;\r\n o.position.copy(position);o.scale.copy(scale);\r\n // The GLB is already converted to Y up. Scale around each feature's center.\r\n if(o.name.startsWith('Silky_collar')||o.name.startsWith('Silky collar')){\r\n  o.scale.x*=1+.2*(d.fur-1);o.scale.z*=1+.2*(d.fur-1);o.scale.y*=d.fur;o.position.y+=1.5175*(1-d.fur);\r\n }\r\n if(region==='eye'&&/Iris|Pupil|Glint|glint/.test(o.name)){\r\n  const factor=d.iris*(o.name==='Pupil'?d.pupilSize:1);\r\n  // R25 anime eyes: a tall iris and pupil with oversized highlights.\r\n  const glint=/glint/i.test(o.name),fx=factor*(d.eye==='anime'?glint?1.7:1.2:1),fy=factor*(d.eye==='anime'?glint?1.7:1.45:1);\r\n  o.scale.x*=fx;o.scale.y*=fy;\r\n  o.position.x=position.x*fx+.04*(1-fx);\r\n  o.position.y=position.y*fy+2.1475*(1-fy);\r\n }\r\n}\r\n\r\n","creature/source/creator/design.ts":"import {EYE_LAYOUTS,type EyeLayout} from './eye-layouts';\r\nimport {COACHES,type CoachId} from './coaching';\r\nimport {PUPILS,type PupilShape} from './pupils';\r\nimport {STYLES as HAND_STYLES} from './catalog';\r\nimport {ROSTER} from './roster';\r\nimport {coachName} from '../../../coach-names.mjs';\r\nimport {normalizeStyleId,normalizeTextureId} from './texture-policy.mjs';\r\nimport {EYE_STYLE_IDS} from '../../../eye-styles.mjs';\r\nexport const REGIONS=['head','eye','collar','body','arms','feet'] as const;\r\nexport type Region=typeof REGIONS[number];\r\n// R18 G3: three colour channels. The region whose saved colour each region renders with: body colour -> body, arms, feet, collar.\r\n// R25 (Ian): the head always takes the body colour; a separate head colour clips at the neck seam.\r\nexport const COLOUR_SOURCE:Record<Region,Region>={body:'body',arms:'body',feet:'body',collar:'body',head:'body',eye:'eye'};\r\n// R25: the picker offers two parts. \"Full body\" writes the body and head colours together; an older save's\r\n// separate head colour is never rendered (COLOUR_SOURCE) and is dropped on load (parseRecipe).\r\nexport const COLOUR_CHANNELS=[{id:'body',label:'Full body',regions:['body','arms','feet','collar','head']},{id:'eyes',label:'Eyes',regions:['eye']}] as const;\r\nexport const LABELS:Record<Region,string>={head:'Crown & scales',eye:'Eyes & pupils',collar:'Shaggy collar',body:'Body',arms:'Arms & hands',feet:'Legs & feet'};\r\nexport const STYLES=[{...HAND_STYLES[0],name:'Original MYR5',primary:'#7946aa',secondary:'#351344',accent:'#b373d4',roughness:.62},...HAND_STYLES.slice(1)];\r\nexport const PICKER_STYLES=STYLES.filter(style=>![8,11,22].includes(style.id));\r\n// Every creature model shipped with the app. Roster ids are generated from the Blender pipeline (creator/roster.ts).\r\nexport const BODIES:readonly {id:string;label:string;group:string}[]=[{id:'myr5',label:coachName('myr5'),group:'MYR5'},...ROSTER];\r\n// The review rejects (D6: original 63/7 review wins). Kept in BODIES/roster.ts and fully\r\n// loadable/renderable (a saved recipe pointing at one still works) — just excluded from user-facing\r\n// pickers below. Numbering resolved to roster.ts (app) numbering per coordinator decision (D6-numbering).\r\n// Owner correction during Rank 3: Pearl · Orb 3 & 5 and Shellcap · Manyarm 3 are kept as approved\r\n// models (per the approved roster board, outputs/visual-boards/01-approved-model-roster.png) — only\r\n// these 4 are actually hidden.\r\nexport const REJECTED_BODY_IDS=new Set<string>([\r\n 'roster/01-seed-pearo--white_3d_character_model', // Seed · Pearo 3\r\n 'roster/01-seed-pearo--blank_humanoid_figure_3d_model', // Seed · Pearo 4\r\n 'roster/09-monolith-tanka--robot_3d_model2', // Monolith · Tanka 4\r\n 'roster/18-quad-all--stylized_quadruped_3d_model', // Four-legged 8\r\n]);\r\n// What user-facing pickers (body/head/arms/feet selects) should offer. Do not filter BODIES itself —\r\n// `known()` below (and any saved recipe) must keep accepting rejected ids.\r\nexport const PICKER_BODIES=BODIES.filter(b=>!REJECTED_BODY_IDS.has(b.id));\r\nexport type BodyId=string;\r\n// Mix-and-match: each limb region may come from a different creature; unset means \"same as body\".\r\nexport const PART_SOURCES=['headFrom','armsFrom','feetFrom'] as const;\r\nexport type PartSource=typeof PART_SOURCES[number];\r\nconst known=(id:unknown)=>typeof id==='string'&&BODIES.some(b=>b.id===id);\r\n// A region can carry a decoupled texture+colour choice instead of (or on top of) the\r\n// legacy single `styles` index. Absent = keep using the legacy numeric style for that region.\r\nexport type MaterialChoice={textureId:string;colorId:string;sparkle:number;metallic:number};\r\nconst validChoice=(v:unknown):v is MaterialChoice=>!!v&&typeof v==='object'&&typeof (v as any).textureId==='string'&&typeof (v as any).colorId==='string'&&Number.isFinite((v as any).sparkle)&&(v as any).sparkle>=0&&(v as any).sparkle<=1&&Number.isFinite((v as any).metallic)&&(v as any).metallic>=0&&(v as any).metallic<=1;\r\nconst validMaterials=(m:unknown):boolean=>m===undefined||(!!m&&typeof m==='object'&&!Array.isArray(m)&&Object.entries(m as object).every(([r,v])=>REGIONS.includes(r as Region)&&validChoice(v)));\r\nexport type Design={golden?:boolean;blackSclera?:boolean;colourPupil?:boolean;version:1;styles:Record<Region,number>;eye:'open'|'sleepy'|'wide'|'anime'|'squinty'|'bloodshot'|'blind';fur:number;iris:number;pupil:PupilShape;pupilSize:number;detail:number;coach:CoachId;shipId?:CoachId;shipColor?:string|null;fingers:number;toes:number;eyeLayout:EyeLayout;body:BodyId;headFrom:BodyId;armsFrom:BodyId;feetFrom:BodyId;materials?:Partial<Record<Region,MaterialChoice>>};\r\nexport const fresh=():Design=>({version:1,styles:{head:0,eye:0,collar:0,body:0,arms:0,feet:0},eye:'open',fur:1,iris:1,pupil:'round',pupilSize:1,detail:1,coach:'supportive',shipId:'supportive',shipColor:null,fingers:4,toes:3,eyeLayout:'single',body:'myr5',headFrom:'myr5',armsFrom:'myr5',feetFrom:'myr5'});\r\nexport function parseRecipe(raw:string):Design{const d=JSON.parse(raw);d.pupil??='round';d.pupilSize??=1;d.detail??=1;d.coach??='supportive';d.shipId??=d.coach;d.shipColor??=null;d.fingers??=4;d.toes??=3;d.eyeLayout??='single';if(!known(d.body))d.body='myr5';for(const k of PART_SOURCES)if(!known(d[k]))d[k]=d.body;const eyeLayoutMigration:Record<string,string>={'around':'triangle','frontBack':'horizontal'};if(eyeLayoutMigration[d.eyeLayout])d.eyeLayout=eyeLayoutMigration[d.eyeLayout];if(!Number.isInteger(d.fingers)||d.fingers<2||d.fingers>6||!Number.isInteger(d.toes)||d.toes<1||d.toes>6||!Object.hasOwn(EYE_LAYOUTS,d.eyeLayout)||d.version!==1||!d.styles||!REGIONS.every(r=>Number.isInteger(d.styles[r])&&d.styles[r]>=0&&d.styles[r]<STYLES.length)||!EYE_STYLE_IDS.includes(d.eye)||!Number.isFinite(d.fur)||d.fur<.65||d.fur>1.4||!Number.isFinite(d.iris)||d.iris<.7||d.iris>1.25||!PUPILS.some(p=>p[0]===d.pupil)||!Number.isFinite(d.pupilSize)||d.pupilSize<.6||d.pupilSize>1.15||!Number.isFinite(d.detail)||d.detail<.5||d.detail>1.5||!COACHES.some(c=>c.id===d.coach)||!COACHES.some(c=>c.id===d.shipId)||!(d.shipColor===null||/^#[0-9a-f]{6}$/i.test(d.shipColor))||!validMaterials(d.materials))throw Error('Choose a valid MYR5 recipe.');return {golden:typeof d.golden==='boolean'?d.golden:undefined,...(d.blackSclera===true?{blackSclera:true}:{}),...(d.colourPupil===true?{colourPupil:true}:{}),version:1,styles:Object.fromEntries(REGIONS.map(r=>[r,normalizeStyleId(d.styles[r])])) as Design['styles'],eye:d.eye,fur:d.fur,iris:d.iris,pupil:d.pupil,pupilSize:d.pupilSize,detail:d.detail,coach:d.coach,shipId:d.shipId,shipColor:d.shipColor,fingers:d.fingers,toes:d.toes,eyeLayout:d.eyeLayout,body:d.body,headFrom:d.headFrom,armsFrom:d.armsFrom,feetFrom:d.feetFrom,materials:d.materials&&Object.fromEntries(Object.entries(d.materials).map(([region,choice]:[string,any])=>[region,{...choice,textureId:normalizeTextureId(choice.textureId),...(region==='head'&&d.materials.body?{colorId:d.materials.body.colorId}:{})}]))};}\r\n","creature/source/creator/eye-layouts.ts":"// Eye centers use the model's Y-up coordinates; yaw faces outward from the crown.\r\n// Reference point the table is tuned to (myr5's own head front, at the single-eye position).\r\nexport const EYE_REFERENCE={x:0,y:2.1575,z:0.55};\r\n// Global eye-scale multiplier for roster heads (handoff §5 \"Eye scale\"). MYR5's own head has eye\r\n// sockets carved for its native eye size, so it is excluded (see assemble.ts) — this constant only\r\n// scales down the auto-fitted eyes on the 70 roster heads, which have no carved socket to clash with.\r\nexport const EYE_SCALE_DEFAULT=0.65;\r\nexport const EYE_LAYOUTS = {\r\n \"single\":{\"label\":\"One cyclops eye\",\"eyes\":[[0,2.1575,0.55,0.605,0]]},\r\n \"horizontal\":{\"label\":\"Horizontal pair\",\"eyes\":[[-0.34,2.25,0.49,0.32,0],[0.34,2.25,0.49,0.32,0]]},\r\n \"vertical\":{\"label\":\"Vertical pair\",\"eyes\":[[0,2.03,0.49,0.32,0],[0,2.73,0.39,0.31,0]]},\r\n \"triangle\":{\"label\":\"Triangle\",\"eyes\":[[-0.33,2.17,0.49,0.30,0],[0.33,2.17,0.49,0.30,0],[0,2.80,0.36,0.28,0]]},\r\n \"spider\":{\"label\":\"Spider · two big + two small\",\"eyes\":[[-0.34,2.16,0.49,0.32,0],[0.34,2.16,0.49,0.32,0],[-0.235,2.77,0.375,0.20,0],[0.235,2.77,0.375,0.20,0]]},\r\n \"square\":{\"label\":\"Four small · square\",\"eyes\":[[-0.285,2.16,0.49,0.25,0],[0.285,2.16,0.49,0.25,0],[-0.285,2.73,0.375,0.25,0],[0.285,2.73,0.375,0.25,0]]}\r\n} as const;\r\nexport type EyeLayout=keyof typeof EYE_LAYOUTS;\r\n","creature/source/creator/eye-overrides.ts":"// Per-variant eye metadata, keyed by stable model id (roster.ts `id`, or 'myr5').\r\n// Source: plan/reports/audit-shapes.md + plan/reports/roster-sheet.md (handoff §5), numbering\r\n// resolved to roster.ts (app) numbering per coordinator decision during Rank 3 (D6-numbering).\r\n//\r\n// flipX: handoff §5 \"Facing direction\" (Rank 3). eyeScale/eyeOffset: Rank 3b priority list only\r\n// (handoff §5 \"Eye anchoring\"), pending owner review of plan/reports/eyes-review.png. eyeScale is\r\n// absolute: it replaces the head-width scale x 0.65 global multiplier (assemble.ts), not a factor on it.\r\nexport type EyeOverride={eyeScale?:number;eyeOffset?:[number,number,number];flipX?:boolean};\r\n\r\nexport const EYE_OVERRIDES:Record<string,EyeOverride>={\r\n 'roster/01-seed-pearo--blank_toy_figure_3d_model':{flipX:true}, // Seed · Pearo 5\r\n 'roster/03-pearl-orb-ring--abstract_humanoid_3d_model':{flipX:true}, // Pearl · Orb 1\r\n 'roster/05-slope-bobble--clay_humanoid_figure_3d_model':{flipX:true}, // Slope · Bobble 1\r\n 'roster/08-shard-asym--geometric_robot_3d_model':{flipX:true}, // Shard · Asym 1\r\n 'roster/08-shard-asym--robot_3d_model4':{flipX:true}, // Shard · Asym 2\r\n 'roster/08-shard-asym--fantasy_creature_3d_model2':{flipX:true}, // Shard · Asym 3\r\n 'roster/09-monolith-tanka--3d_robot_model':{flipX:true}, // Monolith · Tanka 1\r\n 'roster/09-monolith-tanka--robot_3d_model3':{flipX:true}, // Monolith · Tanka 5\r\n 'roster/10-petal-wisp--stylized_creature_3d_model':{flipX:true}, // Petal · Wisp 1\r\n 'roster/12-seedpod-snailslug--stylized_worm_3d_model':{flipX:true}, // Seedpod · Snailslug 2\r\n 'roster/13-fan-split--stylized_3d_character2':{flipX:true}, // Fan · Split 1\r\n 'roster/17-shellcap-manyarm--3d_character_figurine':{flipX:true}, // Shellcap · Manyarm 1\r\n 'roster/18-quad-all--quadruped_robot_3d_model1':{flipX:true}, // Four-legged 5\r\n 'roster/20-lume--robotic_figure_3d_model':{flipX:true}, // Lume 1 (\"Loom 1\" in the handoff)\r\n 'roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model':{flipX:true}, // Blob 4\r\n\r\n // Rank 3b: priority eye-anchoring corrections (handoff §5 \"Eye anchoring\"), numbers tuned against\r\n // plan/reports/eyes-review.png (front + 3/4 + mid-animation renders). eyeOffset.z is intentionally\r\n // never used here: for front-facing eyes (yaw 0) `eyePosition()` in creator/anatomy.ts derives z from\r\n // the head's own eye_anchor surface point, not from the offset — only x/y and eyeScale reach the render.\r\n 'roster/18-quad-all--wooden_four-legged_robot_3d_model':{eyeScale:0.42}, // Four-legged 9 — floating: eye scale was clamped to 1.2x (this head is very wide), 0.65x of that still dwarfed the head; shrinking it seats it on the face instead of bulging off it.\r\n 'roster/18-quad-all--wooden_robot_3d_model':{eyeOffset:[0,0.46,0],eyeScale:0.4}, // Four-legged 10 — floating: eye_anchor sits mid-torso, well below the actual head ball; nudged up onto the head and shrunk slightly.\r\n 'roster/18-quad-all--dragon_creature_3d_model':{eyeOffset:[0,0.16,0],eyeScale:0.42}, // Four-legged 1 — incorrect placement/excessive size: raised onto the head and shrunk off the muzzle.\r\n 'roster/18-quad-all--fantasy_creature_3d_model1':{eyeOffset:[0,0.35,0],eyeScale:0.34}, // Four-legged 2 — incorrect placement/excessive size: raised into the wedge head and shrunk.\r\n 'roster/23-blob-texture-bodies--cute_blob_creature_3d_model':{eyeOffset:[0.222,0,0]}, // Blob 2 — head sub-mesh is off-centre (-0.22 on x) relative to the body; cancels that so the eye centers on the creature.\r\n 'roster/10-petal-wisp--ghost_character_3d_model':{eyeOffset:[0,-0.18,0],eyeScale:0.22}, // Petal · Wisp 2 (ghost_character) — smaller and lower per handoff.\r\n 'roster/10-petal-wisp--fantasy_creature_3d_model4':{eyeOffset:[0,-0.18,0],eyeScale:0.22}, // Petal · Wisp 3 (fantasy_creature_3d_model4) — smaller and lower per handoff.\r\n 'roster/22-curve--stylized_alien_3d_model':{eyeOffset:[0,-0.18,0]}, // Curve 1 — lower only per handoff; size unchanged.\r\n};\r\n","creature/source/creator/eye-surface.ts":"import * as THREE from 'three';\r\nexport const EYE_CENTER=new THREE.Vector3(0,2.1575,.55);\r\nexport const LID_RADIUS=.619;\r\nconst originals=new WeakMap<THREE.Mesh,{source:THREE.BufferGeometry;projected:THREE.BufferGeometry}>();\r\nfunction subdivideSurface(source:THREE.BufferGeometry){\r\n const flat=source.index?source.toNonIndexed():source.clone();const positions=flat.getAttribute('position');const vertices:number[]=[];\r\n const split=(a:THREE.Vector3,b:THREE.Vector3,c:THREE.Vector3,depth:number)=>{\r\n  if(depth<6&&Math.max(a.distanceTo(b),b.distanceTo(c),c.distanceTo(a))>.04){const ab=a.clone().lerp(b,.5),bc=b.clone().lerp(c,.5),ca=c.clone().lerp(a,.5);split(a,ab,ca,depth+1);split(ab,b,bc,depth+1);split(ca,bc,c,depth+1);split(ab,bc,ca,depth+1);}else vertices.push(...a.toArray(),...b.toArray(),...c.toArray());\r\n };\r\n for(let i=0;i<positions.count;i+=3)split(new THREE.Vector3().fromBufferAttribute(positions,i),new THREE.Vector3().fromBufferAttribute(positions,i+1),new THREE.Vector3().fromBufferAttribute(positions,i+2),0);\r\n flat.dispose();const dense=new THREE.BufferGeometry();dense.setAttribute('position',new THREE.Float32BufferAttribute(vertices,3));return dense;\r\n}\r\nexport function prepareEyeMesh(o:THREE.Mesh){\r\n if(o.name==='Iris'){\r\n  o.geometry.dispose();o.geometry=new THREE.CircleGeometry(.375,128);o.geometry.scale(1, .395/.375,1);o.position.set(.04,2.1475,1.133);\r\n }else if(o.name==='Glint'||o.name==='Small_glint'||o.name==='Small glint'){\r\n  const small=o.name!=='Glint';o.geometry.dispose();o.geometry=new THREE.CircleGeometry(small?.024:.063,32);o.geometry.scale(1,small?.028/.024:.081/.063,1);o.position.y-=.4725;\r\n }\r\n}\r\n// R25 bloodshot eyes: branching red veins that run from the rim of the white toward the iris. Built flat\r\n// in the eye's own frame, then wrapped onto the globe by conformEyeMesh like the iris and glints.\r\nexport function bloodshotVeins(){\r\n const vertices:number[]=[];let seed=11;const rand=()=>(seed=seed*16807%2147483647)/2147483647;\r\n const strip=(path:[number,number][],width:number)=>{for(let i=0;i+1<path.length;i++){\r\n  const [ax,ay]=path[i],[bx,by]=path[i+1],l=Math.hypot(bx-ax,by-ay)||1,wa=width*(1-i/path.length),wb=width*(1-(i+1)/path.length);\r\n  const nx=-(by-ay)/l,ny=(bx-ax)/l;vertices.push(ax-nx*wa,ay-ny*wa,1.15,bx+nx*wb,by+ny*wb,1.15,ax+nx*wa,ay+ny*wa,1.15,ax-nx*wa,ay-ny*wa,1.15,bx-nx*wb,by-ny*wb,1.15,bx+nx*wb,by+ny*wb,1.15);}};\r\n const walk=(angle:number,from:number,to:number,steps:number):[number,number][]=>Array.from({length:steps+1},(_,i)=>{const r=from+(to-from)*i/steps;angle+=(rand()-.5)*.22;return [.04+Math.cos(angle)*r,2.1475+Math.sin(angle)*r];});\r\n for(let k=0;k<13;k++){const angle=k/13*Math.PI*2+rand()*.35,main=walk(angle,.6,.4+rand()*.06,7);strip(main,.011);\r\n  const at=2+Math.floor(rand()*3),[x,y]=main[at],r=Math.hypot(x-.04,y-2.1475);strip(walk(Math.atan2(y-2.1475,x-.04)+(rand()<.5?-.35:.35),r,r-.09,3),.006);}\r\n const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(vertices,3));return g;\r\n}\r\n// A geometric mask keeps eye markings on the globe and inside the eyelid.\r\n// Actual surface geometry also preserves occlusion in GLB and OBJ exports.\r\nexport function conformEyeMesh(o:THREE.Mesh){\r\n if(!/^(Iris|Pupil|Glint|Small.glint|Eye vein)/.test(o.name))return;\r\n let entry=originals.get(o);\r\n if(!entry||entry.projected!==o.geometry){entry?.source.dispose();entry={source:subdivideSurface(o.geometry),projected:o.geometry};originals.set(o,entry);}\r\n const geometry=entry.source.clone();const positions=geometry.getAttribute('position');const point=new THREE.Vector3();o.updateMatrix();const inverse=o.matrix.clone().invert();const localNormalMatrix=new THREE.Matrix3().setFromMatrix4(o.matrix).transpose();const normal=new THREE.Vector3();const normals=new Float32Array(positions.count*3);\r\n const radius=o.name==='Pupil'?.609:/glint/i.test(o.name)?.611:o.name==='Iris'?.606:o.name==='Eye vein'?.6058:.6075;\r\n for(let i=0;i<positions.count;i++){\r\n  point.fromBufferAttribute(positions,i).applyMatrix4(o.matrix);\r\n  let x=point.x-EYE_CENTER.x,y=point.y-EYE_CENTER.y;const distance=Math.hypot(x,y),limit=.595;\r\n  if(distance>limit){x*=limit/distance;y*=limit/distance;}\r\n  point.set(EYE_CENTER.x+x,EYE_CENTER.y+y,EYE_CENTER.z+Math.sqrt(radius*radius-x*x-y*y));normal.copy(point).sub(EYE_CENTER).normalize().applyMatrix3(localNormalMatrix).normalize();normal.toArray(normals,i*3);point.applyMatrix4(inverse);\r\n  positions.setXYZ(i,point.x,point.y,point.z);\r\n }\r\n geometry.setAttribute('normal',new THREE.BufferAttribute(normals,3));geometry.computeBoundingBox();geometry.computeBoundingSphere();o.geometry.dispose();o.geometry=geometry;entry.projected=geometry;\r\n}\r\n","creature/source/creator/material-language.ts":"import * as T from 'three';\r\nimport {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils.js';\r\nimport {builtinSurfaceProfile,sampleBuiltinSurface} from './material-patterns';\r\nimport {refineCoachGeometry,type CoachReliefBudget} from './material-refinement';\r\nimport {samplePaletteTint,applyPaletteFinish} from './palette-finishes';\r\n\r\n// This material language is shared verbatim by Coach and Helping Hand. Maps are\r\n// ordinary glTF-compatible PBR textures, so the appearance also survives export.\r\nexport const MATERIAL_REVISION='material-horror-robot-2026-10-05-r1';\r\nexport const MATERIAL_NOTES=[\r\n 'Fine pores, palm folds and soft satin skin.',\r\n 'Twisted roots, bark fissures and fresh leaf growth.',\r\n 'Soft mushroom caps, radial gills and a branching mycelium web.',\r\n 'Overlapping lacquered carapace plates with dark flexible seams.',\r\n 'Raised overlapping scales, belly scutes and a serrated ridge.',\r\n 'Deep ocean hide, swept fins and luminous sensory pearls.',\r\n 'Porous reef limestone, branching antlers and hollow polyps.',\r\n 'Weathered ivory, joint ends and hollow skeletal anatomy.',\r\n 'Translucent mist glass with flowing veils and a pale inner glow.',\r\n 'Charred hide, swept horns and ember-lit fractures.',\r\n 'Pearlescent ceramic, engraved gold and orbiting halo segments.',\r\n 'Obsidian shells with violet fracture edges and star-like inclusions.',\r\n 'Folded flesh, coiled tendrils and luminous sensory nodules.',\r\n 'Angular stone slabs, chipped strata and deep mineral seams.',\r\n 'Faceted amethyst clusters with reflective faces and mineral inclusions.',\r\n 'Black basalt crust over glowing orange lava fissures.',\r\n 'Translucent blue ice, frost veins and sharp icicle clusters.',\r\n 'Storm-dark armor with branching electric channels and conductor fins.',\r\n 'Rigid brass armor, steel hinge joints, rivets and exposed toothed gears.',\r\n 'Machined violet panels, recessed circuits and pink illuminated rails.',\r\n 'Dense tapered fur tufts, a soft undercoat and contrasting guard hairs.',\r\n 'Translucent gelatin, a wet clear coat and bubbles suspended inside.',\r\n 'Plump soft forms, warm blush and delicate skin folds.'\r\n] as const;\r\ntype Style={id:number;primary:string;secondary:string;accent:string;emissive:string;roughness:number;metalness:number;detail:string;paletteId?:string};\r\nconst clamp=T.MathUtils.clamp,fract=(x:number)=>x-Math.floor(x);\r\nconst hash=(x:number,y:number)=>fract(Math.sin(x*127.1+y*311.7)*43758.5453);\r\nfunction cells(x:number,y:number){\r\n const ix=Math.floor(x),iy=Math.floor(y);let a=10,b=10;\r\n for(let j=-1;j<=1;j++)for(let i=-1;i<=1;i++){\r\n  const px=ix+i+.2+.6*hash(ix+i,iy+j),py=iy+j+.2+.6*hash(iy+j,ix+i+19);\r\n  const d=Math.hypot(x-px,y-py);if(d<a){b=a;a=d;}else if(d<b)b=d;\r\n }\r\n return {pit:a,seam:b-a};\r\n}\r\nexport function surfaceSample(id:number,x:number,y:number){\r\n const builtIn=sampleBuiltinSurface(id,x,y);if(builtIn)return builtIn;\r\n const c=cells(x*5,y*5),grain=hash(Math.floor(x*180),Math.floor(y*180));\r\n const wave=Math.sin(x*18+Math.sin(y*9)*2),vein=Math.pow(1-Math.abs(Math.sin(x*12+Math.sin(y*7)*2)),9);\r\n const seam=1-T.MathUtils.smoothstep(c.seam,.025,.12),spot=1-T.MathUtils.smoothstep(c.pit,.06,.19);\r\n const sx=fract(x*10+(Math.floor(y*12)%2)*.5)-.5,sy=fract(y*12)-.5;\r\n const scale=clamp(1-Math.pow(Math.abs(sx)*1.9,3)-Math.pow(Math.abs(sy)*1.9,4),0,1);\r\n let h=.5,t=.5,glow=0,rough=.7;\r\n switch(id){\r\n  case 0:h=.42+grain*.12+wave*.015;t=.54+grain*.07;rough=.61+grain*.2;break;\r\n  case 1:h=.4+wave*.19+vein*.25;t=.28+vein*.42+grain*.13;rough=.91;break;\r\n  case 2:h=.6-spot*.3+Math.sin(Math.atan2(sy,sx)*24)*.04;t=.7-spot*.4;glow=spot*.16;rough=.87;break;\r\n  case 3:h=scale*.75;t=.22+scale*.65;rough=.18+(.9-scale)*.4;break;\r\n  case 4:h=scale*.6+grain*.12;t=.22+scale*.52+wave*.08;rough=.56+grain*.2;break;\r\n  case 5:h=.55+wave*.12-spot*.23;t=.15+spot*.8;glow=spot;rough=.32;break;\r\n  case 6:h=.75-spot*.65-grain*.12;t=.44+spot*.4+grain*.1;rough=.93;break;\r\n  case 7:h=.55+Math.sin(y*35)*.09-grain*.12-seam*.06;t=.75-grain*.2-seam*.35;rough=.83;break;\r\n  case 8:h=.5+wave*.035;t=.7+wave*.12;glow=vein*.35;rough=.12;break;\r\n  case 9:h=.48+wave*.14+grain*.12;t=.15+grain*.13+vein*.3;glow=vein*.4;rough=.83;break;\r\n  case 10:h=.55+vein*.08;t=.74+vein*.23;glow=vein*.22;rough=.18+grain*.07;break;\r\n  case 11:h=.55-seam*.3;t=.03+seam*.48;glow=seam*.8;rough=.14;break;\r\n  case 12:h=.5+wave*.19+spot*.18;t=.25+spot*.65;glow=spot*.4;rough=.39;break;\r\n  case 13:h=.62-seam*.52+Math.floor(grain*4)*.035;t=.32+grain*.2-seam*.26;rough=.98;break;\r\n  case 14:h=.45+Math.floor(c.pit*8)*.05;t=.32+c.pit*.45;rough=.09;break;\r\n  case 15:{const lava=cells(x*2+Math.sin(y*5)*.3,y*2+Math.sin(x*4)*.3),crack=1-T.MathUtils.smoothstep(lava.seam,.015,.065);h=.72-crack*.65+grain*.1;t=.015+crack*.88;glow=crack;rough=.95-crack*.7;break;}\r\n  case 16:h=.5+seam*.14;t=.72+seam*.22;rough=.13+seam*.48;break;\r\n  case 17:{const bolt=Math.pow(1-Math.abs(Math.sin(x*7+Math.sin(y*22)*.25+Math.sin(y*9))),24);h=.54-bolt*.2;t=.13+bolt*.82;glow=bolt;rough=.38;break;}\r\n  case 18:{const cut=Math.min(fract(x*5),fract(y*5));h=cut<.06?.12:.68+grain*.03;t=cut<.06?.05:.43+grain*.16;rough=.24+grain*.25;break;}\r\n  case 19:{const a=fract(x*6),b=fract(y*6),switcher=hash(Math.floor(x*6),Math.floor(y*6));const rail=(Math.abs(a-.5)<.025&&b>.2&&switcher>.35)||(Math.abs(b-.5)<.025&&a<.6&&switcher<.65)?1:0;h=.5-rail*.2;t=.08+rail*.9;glow=rail;rough=.25;break;}\r\n  case 20:h=.5+Math.sin(x*160+Math.sin(y*25))* .2;t=.58+grain*.2;rough=.98;break;\r\n  case 21:h=.5+wave*.018;t=.7+wave*.07;rough=.07;break;\r\n  case 22:h=.5+grain*.035+wave*.01;t=.67+wave*.02;rough=.51;break;\r\n  // Rank 4 additions: Flat (no mask - literally constant) and Clay (soft procedural noise,\r\n  // PLAN §6.1) so the runtime tint system has an always-unlocked, no-download starting pair.\r\n  case 30:h=.5;t=.5;rough=.55;break;\r\n  case 31:h=.5+grain*.05+wave*.008;t=.5+grain*.03;rough=.92;break;\r\n }\r\n return {height:h,tint:clamp(t,0,1),glow,rough};\r\n}\r\nconst maps=new Map<string,{map:T.DataTexture;bump:T.DataTexture;rough:T.DataTexture;glow:T.DataTexture}>();\r\nfunction materialMaps(style:Style){\r\n const key=[MATERIAL_REVISION,style.id,style.primary,style.secondary,style.accent,style.paletteId??''].join('|');if(maps.has(key))return maps.get(key)!;\r\n const size=192,buffers=Array.from({length:4},()=>new Uint8Array(size*size*4));\r\n const dark=new T.Color(style.secondary),base=new T.Color(style.primary),light=new T.Color(style.accent),color=new T.Color(),profile=builtinSurfaceProfile(style.id);\r\n for(let y=0;y<size;y++)for(let x=0;x<size;x++){\r\n  const s=surfaceSample(style.id,x/size,y/size),i=(y*size+x)*4;\r\n  const tint=samplePaletteTint(style.paletteId,s.tint,x/size,y/size,style.id===30); // Palette stops follow the existing texture tones; watercolor/marbled add a spatial finish tint.\r\n  color.copy(tint<.5?dark:base).lerp(tint<.5?base:light,tint<.5?tint*2:(tint-.5)*2);\r\n  const roughnessMap=profile?clamp(.9+(s.rough-profile.roughness)*.75,0,1):s.rough;\r\n  for(let k=0;k<3;k++){buffers[0][i+k]=Math.round([color.r,color.g,color.b][k]*255);buffers[1][i+k]=s.height*255;buffers[2][i+k]=roughnessMap*255;buffers[3][i+k]=s.glow*255;}\r\n  for(const b of buffers)b[i+3]=255;\r\n }\r\n const textures=buffers.map(bytes=>{const tex=new T.DataTexture(bytes,size,size,T.RGBAFormat);tex.wrapS=tex.wrapT=T.RepeatWrapping;tex.magFilter=T.LinearFilter;tex.minFilter=T.LinearMipmapLinearFilter;tex.generateMipmaps=true;tex.needsUpdate=true;return tex;});\r\n const result={map:textures[0],bump:textures[1],rough:textures[2],glow:textures[3]};maps.set(key,result);return result;\r\n}\r\n/** Colour-only path for irises and other parts whose geometry must stay intact. */\r\nexport function applyPaletteSurface(material:T.MeshStandardMaterial,palette:{primary:string;secondary:string;accent:string;paletteId?:string;id?:number}){\r\n if(!palette.paletteId)return;\r\n material.map=materialMaps({...palette,id:palette.id??30,emissive:'#000000',roughness:.55,metalness:0,detail:'palette'}).map;\r\n material.color.set('white');material.needsUpdate=true;\r\n applyPaletteFinish(material as T.MeshPhysicalMaterial,palette.paletteId,palette);\r\n}\r\nexport function materialFor(style:Style,unit:number,original?:T.MeshStandardMaterial){\r\n const tex=materialMaps(style),id=style.id;\r\n const profile=builtinSurfaceProfile(id);\r\n const mat=new T.MeshPhysicalMaterial({name:style.detail+' · '+MATERIAL_REVISION,color:'white',map:tex.map,bumpMap:tex.bump,bumpScale:unit*(profile?.bump??([8,21,22].includes(id)?.0005:.007)),roughnessMap:tex.rough,roughness:profile?.roughness??1,metalness:style.metalness,vertexColors:true,envMapIntensity:[18,19,14].includes(id)?1.3:1});\r\n if(original){mat.side=original.side;mat.polygonOffset=original.polygonOffset;mat.polygonOffsetFactor=original.polygonOffsetFactor;}\r\n if([3,4,10,11,14,18,19,21].includes(id)){mat.clearcoat=id===21?1:.6;mat.clearcoatRoughness=id===21?.055:.19;}\r\n if([8,14,16,21].includes(id)){\r\n  mat.transmission=id===21?.8:id===8?.62:id===16?.54:.28;mat.thickness=unit*(id===21?.36:.16);mat.ior=id===21?1.36:id===16?1.31:1.46;\r\n  mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*(id===21?1.4:2);mat.metalness=0;mat.roughnessMap=null;mat.roughness=id===21?.075:id===16?.16:.12;\r\n }\r\n if([5,8,9,10,11,12,15,17,19].includes(id)){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=id===15?2.1:id===17?1.5:.85;}\r\n if(id===20){mat.sheen=1;mat.sheenRoughness=.96;mat.sheenColor.set(style.accent);mat.metalness=0;}\r\n if(id===0||id===22){mat.sheen=.22;mat.sheenColor.set('#ffc9b1');}\r\n if(profile){mat.sheen=profile.sheen;mat.sheenRoughness=profile.sheenRoughness;mat.sheenColor.set(style.accent);mat.clearcoat=Math.max(mat.clearcoat,profile.clearcoat);mat.clearcoatRoughness=profile.clearcoatRoughness;}\r\n if(id>=57&&id<=62){\r\n  mat.envMapIntensity=1.65;mat.iridescence=id===60?1:id===57?.85:.3;\r\n  mat.iridescenceThicknessRange=id===60?[100,750]:[140,480];\r\n  if([57,58,59,61].includes(id)){\r\n   mat.transmission=id===58?.97:id===59?.84:.88;mat.thickness=unit*(id===59?.32:.48);\r\n   mat.ior=id===59?1.8:id===58?1.46:1.38;mat.metalness=0;\r\n   mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*3.5;\r\n   mat.roughnessMap=null;mat.roughness=profile!.roughness;\r\n   mat.dispersion=id===59?.75:.2;\r\n  }\r\n  if(id===62){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=.65;}\r\n }\r\n if(id===63||id===74){mat.transmission=id===63?.48:.82;mat.thickness=unit*.32;mat.ior=id===63?1.33:1.52;mat.metalness=0;mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*2.4;mat.roughnessMap=null;mat.roughness=profile!.roughness;}\r\n if([69,70,74].includes(id)){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=id===74?1.1:.75;}\r\n mat.flatShading=[13,14,59].includes(id);mat.userData.materialStyle=id;\r\n const optics={transmission:mat.transmission,thickness:mat.thickness,ior:mat.ior,roughness:mat.roughness,metalness:mat.metalness,iridescence:mat.iridescence,iridescenceThicknessRange:mat.iridescenceThicknessRange};\r\n applyPaletteFinish(mat,style.paletteId,style);\r\n // Color finishes coat a clear volume; they must not erase the chosen jelly/glass texture.\r\n if(style.paletteId&&optics.transmission>0){Object.assign(mat,optics);mat.clearcoat=1;mat.clearcoatRoughness=.035;}\r\n if(id===60){mat.metalness=.88;mat.iridescence=1;mat.iridescenceThicknessRange=[100,750];mat.roughness=.16;}\r\n if(id===61||id===62)applySparkle(mat,.85);\r\n return mat;\r\n}\r\n\r\n// Sparkle flakes stay attached to the UV surface and flash as the viewing angle changes.\r\nexport function applySparkle(mat:T.MeshPhysicalMaterial,sparkle:number){\r\n const amount=clamp(sparkle,0,1);if(amount<=0||mat.userData.myr5FinishGlitter)return;\r\n if(mat.userData.myr5Sparkle)return;mat.userData.myr5Sparkle=amount;\r\n const previous=mat.onBeforeCompile;(mat as T.MeshPhysicalMaterial & {defines?:Record<string,string>}).defines={...(mat as T.MeshPhysicalMaterial & {defines?:Record<string,string>}).defines,USE_UV:''};\r\n mat.onBeforeCompile=(shader,renderer)=>{\r\n  previous?.call(mat,shader,renderer);\r\n  shader.uniforms.uSparkle={value:amount};\r\n  shader.vertexShader=shader.vertexShader.replace('#include <common>','#include <common>\\nvarying vec2 vMyr5SparkleUv;').replace('#include <uv_vertex>','#include <uv_vertex>\\nvMyr5SparkleUv=uv;');\r\n  shader.fragmentShader=shader.fragmentShader\r\n   .replace('#include <common>','uniform float uSparkle;\\nvarying vec2 vMyr5SparkleUv;\\n#include <common>')\r\n   .replace('#include <dithering_fragment>','float mySparkleGrain=fract(sin(dot(floor(vMyr5SparkleUv*83.0),vec2(12.9898,78.233)))*43758.5453);\\nvec3 myFlakeNormal=normalize(normal+vec3(sin(mySparkleGrain*91.0),cos(mySparkleGrain*73.0),0.0)*.55);\\nfloat myGlint=pow(max(dot(myFlakeNormal,normalize(vViewPosition)),0.0),42.0);\\ngl_FragColor.rgb+=step(0.94,mySparkleGrain)*myGlint*uSparkle*2.2;\\n#include <dithering_fragment>');\r\n };\r\n const priorKey=mat.customProgramCacheKey.bind(mat);mat.customProgramCacheKey=()=>priorKey()+'|myr5-sparkle-v3|'+amount.toFixed(3);\r\n mat.needsUpdate=true;\r\n}\r\n\r\nfunction meshWorldSpan(g:T.BufferGeometry,matrix:T.Matrix4){\r\n if(!g.boundingBox)g.computeBoundingBox();\r\n const size=g.boundingBox!.clone().applyMatrix4(matrix).getSize(new T.Vector3());\r\n return Math.max(size.x,size.y,size.z,1e-6);\r\n}\r\n\r\nexport function authoredUvScale(g:T.BufferGeometry,matrix:T.Matrix4){\r\n const uv=g.attributes.uv,p=g.attributes.position,idx=g.index;if(!uv||uv.itemSize<2)return 1.8;\r\n let worldArea=0,uvArea=0;const a=new T.Vector3(),b=new T.Vector3(),c=new T.Vector3(),ab=new T.Vector3(),ac=new T.Vector3();\r\n const count=idx?.count??p.count;\r\n for(let j=0;j+2<count;j+=3){const ia=idx?idx.getX(j):j,ib=idx?idx.getX(j+1):j+1,ic=idx?idx.getX(j+2):j+2;\r\n  a.fromBufferAttribute(p,ia).applyMatrix4(matrix);b.fromBufferAttribute(p,ib).applyMatrix4(matrix);c.fromBufferAttribute(p,ic).applyMatrix4(matrix);\r\n  ab.subVectors(b,a);ac.subVectors(c,a);worldArea+=ab.cross(ac).length()*.5;\r\n  const ux=uv.getX(ib)-uv.getX(ia),uy=uv.getY(ib)-uv.getY(ia),vx=uv.getX(ic)-uv.getX(ia),vy=uv.getY(ic)-uv.getY(ia);uvArea+=Math.abs(ux*vy-uy*vx)*.5;\r\n }\r\n if(!Number.isFinite(worldArea)||!Number.isFinite(uvArea)||worldArea<=1e-10||uvArea<=1e-10)return 1.8;\r\n // Divide physical UV density by the mesh span. A ten-times larger imported\r\n // sculpt should show the same sized pattern, not 10x more tiles that mipmap away.\r\n const span=meshWorldSpan(g,matrix);\r\n return T.MathUtils.clamp(Math.sqrt(worldArea/uvArea)*1.8/Math.max(1,span/2),.5,6);\r\n}\r\n\r\n// R25: a vertex can only carry relief that is coarser than the mesh. Point-sampling a finer pattern\r\n// (Fine Stripe has 40 strands per tile) at sparse roster vertices aliased into lumpy patches, so the\r\n// displacement fades out between a quarter and half of the pattern's finest wavelength; the bump and\r\n// colour maps still draw the full detail. Wavelength = finest axis, Schmitt-triggered against noise.\r\nconst reliefWavelengths=new Map<number,number>();\r\nfunction reliefWavelength(id:number){\r\n let w=reliefWavelengths.get(id);if(w!==undefined)return w;w=Infinity;const n=2048;\r\n for(const axis of [0,1]){const h=new Float32Array(n);let lo=1,hi=0;for(let i=0;i<n;i++){h[i]=surfaceSample(id,axis?.37:i/n,axis?i/n:.37).height;lo=Math.min(lo,h[i]);hi=Math.max(hi,h[i]);}\r\n  const mid=(lo+hi)/2,band=(hi-lo)*.2;let side=0,crossings=0;for(const value of h){const next=value>mid+band?1:value<mid-band?-1:side;if(side&&next!==side)crossings++;side=next;}\r\n  if(crossings)w=Math.min(w,2/crossings);}\r\n reliefWavelengths.set(id,w);return w;\r\n}\r\nfunction meanEdge(g:T.BufferGeometry,matrix:T.Matrix4){\r\n const p=g.attributes.position,idx=g.index,count=idx?.count??p.count,stride=Math.max(1,Math.floor(count/3/4000))*3,a=new T.Vector3(),b=new T.Vector3();let sum=0,edges=0;\r\n for(let j=0;j+2<count;j+=stride)for(let k=0;k<3;k++){a.fromBufferAttribute(p,idx?idx.getX(j+k):j+k).applyMatrix4(matrix);b.fromBufferAttribute(p,idx?idx.getX(j+(k+1)%3):j+(k+1)%3).applyMatrix4(matrix);sum+=a.distanceTo(b);edges++;}\r\n return edges?sum/edges:0;\r\n}\r\nexport function reliefResolve(id:number,uvEdge:number){const w=reliefWavelength(id);return 1-T.MathUtils.smoothstep(uvEdge,w/4,w/2);}\r\n\r\nexport function sculptMaterial(mesh:T.Mesh,style:Style,unit=1,amount=1,coachRelief?:CoachReliefBudget){\r\n if(Array.isArray(mesh.material)||!mesh.geometry.attributes.position)return;\r\n // The higher-density authored-UV path is deliberately limited to known static meshes.\r\n // Keep animated, morph-target, interleaved and unsupported Float16 geometry on the\r\n // established path until their attributes can be refined without changing semantics.\r\n if(coachRelief&&((mesh as T.SkinnedMesh).isSkinnedMesh||mesh.geometry.attributes.skinIndex||mesh.geometry.attributes.skinWeight||Object.values(mesh.geometry.morphAttributes).some(attributes=>attributes.length)||Object.values(mesh.geometry.attributes).some(attribute=>(attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||(attribute as T.BufferAttribute & {isFloat16BufferAttribute?:boolean}).isFloat16BufferAttribute||((globalThis as any).Float16Array&&attribute.array instanceof (globalThis as any).Float16Array))))coachRelief=undefined;\r\n const old=mesh.geometry;\r\n // Keep Original MYR5's established single-pass geometry, UVs and JS-double displacement path exact.\r\n if(!coachRelief){\r\n  const g=old.clone();if(!g.attributes.normal)g.computeVertexNormals();\r\n  const p=g.attributes.position,n=g.attributes.normal,uv=new Float32Array(p.count*2),colors=new Float32Array(p.count*3);\r\n  mesh.updateWorldMatrix(true,false);const normalMatrix=new T.Matrix3().getNormalMatrix(mesh.matrixWorld),inv=mesh.matrixWorld.clone().invert(),point=new T.Vector3(),normal=new T.Vector3();\r\n  const id=style.id,relief=unit*(builtinSurfaceProfile(id)?.relief??([0,8,20,21,22].includes(id)?.0018:id===13?.037:id===18?.018:.019));\r\n  for(let i=0;i<p.count;i++){\r\n   point.fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld);normal.fromBufferAttribute(n,i).applyMatrix3(normalMatrix).normalize();\r\n   const x=point.x/unit,y=point.y/unit,z=point.z/unit;\r\n   const u=(Math.abs(normal.z)>.45?x:z)*1.8+.5,v=y*1.8+.5,s=surfaceSample(id,u,v);uv[i*2]=u;uv[i*2+1]=v;\r\n   let edge=1;for(const [sx,sy,sz,r]of mesh.userData.eyeSockets??[])edge*=T.MathUtils.smoothstep(Math.hypot(point.x-sx,point.y-sy,point.z-sz),r*1.04,r*1.35);\r\n   point.addScaledVector(normal,(s.height-.5)*relief*amount*edge).applyMatrix4(inv);p.setXYZ(i,point.x,point.y,point.z);\r\n   const shade=.91+.09*Math.sin(x*3+y*4+z*2);colors.set([shade,shade,shade],i*3);\r\n  }\r\n  g.setAttribute('uv',new T.BufferAttribute(uv,2));g.setAttribute('color',new T.BufferAttribute(colors,3));g.computeVertexNormals();g.computeBoundingBox();g.computeBoundingSphere();\r\n  const oldMat=mesh.material as T.MeshStandardMaterial;mesh.material=materialFor(style,unit,oldMat);mesh.geometry=g;\r\n  if(mesh.userData.ownedGeometry)old.dispose();mesh.userData.ownedGeometry=true;mesh.userData.ownedMaterial=true;return;\r\n }\r\n mesh.updateWorldMatrix(true,false);const worldScale=mesh.matrixWorld.getMaxScaleOnAxis(),refined=refineCoachGeometry(old,coachRelief,worldScale),g=refined?.geometry??old.clone();if(!g.attributes.normal)g.computeVertexNormals();\r\n // Keep imported smoothing while adding only the geometric normal change caused by relief.\r\n // This avoids faceting the source coach while making shallow height fields catch light.\r\n const authoredNormals=new Float32Array(g.attributes.normal.array as ArrayLike<number>);g.computeVertexNormals();const geometricBefore=new Float32Array(g.attributes.normal.array as ArrayLike<number>);g.attributes.normal.array.set(authoredNormals);g.attributes.normal.needsUpdate=true;\r\n const p=g.attributes.position,n=g.attributes.normal,uv=new Float32Array(p.count*2),colors=new Float32Array(p.count*3),worldPositions=new Float32Array(p.count*3),worldOffsets=new Float32Array(p.count*3);\r\n const vertexKeys=coachRelief?new Array<string>(p.count):undefined;\r\n const coordinateUnit=Math.max(unit,meshWorldSpan(g,mesh.matrixWorld)/2);\r\n const resolvable=reliefResolve(style.id,meanEdge(g,mesh.matrixWorld)*1.8/coordinateUnit);\r\n const normalMatrix=new T.Matrix3().getNormalMatrix(mesh.matrixWorld),inv=mesh.matrixWorld.clone().invert(),point=new T.Vector3(),normal=new T.Vector3();\r\n const id=style.id,relief=Math.min(unit*(builtinSurfaceProfile(id)?.relief??([0,8,20,21,22].includes(id)?.0018:id===13?.037:id===18?.018:.019)),meshWorldSpan(g,mesh.matrixWorld)*.015);\r\n const seamOffsets=coachRelief?new Map<string,{x:number;y:number;z:number;count:number}>():undefined,seamKey=(x:number,y:number,z:number)=>`${Math.round(x*1e5)},${Math.round(y*1e5)},${Math.round(z*1e5)}`;\r\n for(let i=0;i<p.count;i++){\r\n  const localX=p.getX(i),localY=p.getY(i),localZ=p.getZ(i);point.set(localX,localY,localZ).applyMatrix4(mesh.matrixWorld);normal.fromBufferAttribute(n,i).applyMatrix3(normalMatrix).normalize();\r\n  const x=point.x/coordinateUnit,y=point.y/coordinateUnit,z=point.z/coordinateUnit;\r\n  // R25: every species uses Original MYR5's object projection. Authored (Tripo atlas) UVs are cut into\r\n  // islands with their own rotation and density, so directional patterns such as Fine Stripe turned\r\n  // into a patchwork across the belly. The projection is fixed on the mesh and periodic for the surfaces.\r\n  const u=(Math.abs(normal.z)>.45?x:z)*1.8+.5,v=y*1.8+.5,s=surfaceSample(id,u,v);\r\n  uv[i*2]=u;uv[i*2+1]=v;worldPositions[i*3]=point.x;worldPositions[i*3+1]=point.y;worldPositions[i*3+2]=point.z;\r\n  let edge=1;for(const [sx,sy,sz,r]of mesh.userData.eyeSockets??[])edge*=T.MathUtils.smoothstep(Math.hypot(point.x-sx,point.y-sy,point.z-sz),r*1.04,r*1.35);\r\n  const offset=(s.height-.5)*relief*amount*edge*resolvable,dx=normal.x*offset,dy=normal.y*offset,dz=normal.z*offset;worldOffsets[i*3]=dx;worldOffsets[i*3+1]=dy;worldOffsets[i*3+2]=dz;\r\n  if(seamOffsets){const key=seamKey(localX,localY,localZ);vertexKeys![i]=key;const group=seamOffsets.get(key)??{x:0,y:0,z:0,count:0};group.x+=dx;group.y+=dy;group.z+=dz;group.count++;seamOffsets.set(key,group);}\r\n  const shade=.91+.09*Math.sin(x*3+y*4+z*2);colors.set([shade,shade,shade],i*3);\r\n }\r\n for(let i=0;i<p.count;i++){\r\n  const x=worldPositions[i*3],y=worldPositions[i*3+1],z=worldPositions[i*3+2];let dx=worldOffsets[i*3],dy=worldOffsets[i*3+1],dz=worldOffsets[i*3+2];\r\n  if(seamOffsets){const group=seamOffsets.get(vertexKeys![i]);if(group&&group.count>1){dx=group.x/group.count;dy=group.y/group.count;dz=group.z/group.count;}}\r\n  point.set(x+dx,y+dy,z+dz).applyMatrix4(inv);p.setXYZ(i,point.x,point.y,point.z);\r\n }\r\n g.computeVertexNormals();const geometricAfter=g.attributes.normal;\r\n for(let i=0;i<p.count;i++){const j=i*3,x=authoredNormals[j]+geometricAfter.array[j]-geometricBefore[j],y=authoredNormals[j+1]+geometricAfter.array[j+1]-geometricBefore[j+1],z=authoredNormals[j+2]+geometricAfter.array[j+2]-geometricBefore[j+2],length=Math.hypot(x,y,z)||1;geometricAfter.setXYZ(i,x/length,y/length,z/length);}\r\n g.setAttribute('uv',new T.BufferAttribute(uv,2));g.setAttribute('color',new T.BufferAttribute(colors,3));g.computeBoundingBox();g.computeBoundingSphere();\r\n const oldMat=mesh.material as T.MeshStandardMaterial;mesh.material=materialFor(style,unit,oldMat);mesh.geometry=g;\r\n if(mesh.userData.ownedGeometry)old.dispose();mesh.userData.ownedGeometry=true;mesh.userData.ownedMaterial=true;\r\n}\r\n\r\ntype Triangle={a:T.Vector3;b:T.Vector3;c:T.Vector3;normal:T.Vector3;total:number};\r\nfunction surfaceTriangles(group:T.Group){\r\n group.updateWorldMatrix(true,true);const inv=group.matrixWorld.clone().invert(),triangles:Triangle[]=[],bounds=new T.Box3();let total=0;\r\n group.traverseVisible(obj=>{if(!(obj instanceof T.Mesh)||obj.userData.materialDetail)return;const g=obj.geometry,p=g.attributes.position,ix=g.index;if(!p)return;\r\n  const matrix=inv.clone().multiply(obj.matrixWorld),normalMatrix=new T.Matrix3().getNormalMatrix(matrix),authored=g.attributes.normal,count=ix?.count??p.count;\r\n  // Surface sampling needs area coverage, not every triangle of the dense fur assets.\r\n  const stride=Math.max(1,Math.ceil(count/24000))*3;\r\n  for(let j=0;j+2<count;j+=stride){const ids=[0,1,2].map(k=>ix?ix.getX(j+k):j+k),[a,b,c]=ids.map(i=>new T.Vector3().fromBufferAttribute(p,i).applyMatrix4(matrix));const cross=b.clone().sub(a).cross(c.clone().sub(a)),area=cross.length()*.5;if(area<1e-10)continue;const normal=authored?ids.reduce((sum,i)=>sum.add(new T.Vector3().fromBufferAttribute(authored,i).applyMatrix3(normalMatrix)),new T.Vector3()).normalize():cross.normalize();if(normal.lengthSq()<1e-8)normal.copy(cross).normalize();total+=area;bounds.expandByPoint(a);bounds.expandByPoint(b);bounds.expandByPoint(c);triangles.push({a,b,c,normal,total});}\r\n });return {triangles,total,bounds};\r\n}\r\nexport function growMaterial(group:T.Group,style:Style,region:string,unit=1,amount=1,hand=false){\r\n const result=new T.Group();result.name='Material sculpture '+style.detail;result.userData.region=region;\r\n if(region==='eye'||region==='nails'||[0,7,22].includes(style.id))return result;\r\n const {triangles,total,bounds}=surfaceTriangles(group);if(!triangles.length)return result;\r\n let seed=style.id*9173+region.length*419;const rand=()=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/4294967296;};\r\n const id=style.id;if(hand&&id===20)return result;\r\n const baseCount=id===20?2100:[3,4].includes(id)?110:id===21?32:42;\r\n const count=Math.round((id>=63?(id===66?60:id===68?22:id===72?12:38):id>=57&&id<=62?(id===61?180:id===58?55:36):baseCount)*(region==='head'||region==='palm'||region==='back_of_hand'?1:region==='body'?.8:.5));\r\n const batches:T.BufferGeometry[][]=[[],[],[]],normal=new T.Vector3(),point=new T.Vector3(),up=new T.Vector3(0,1,0);\r\n const span=bounds.getSize(new T.Vector3()),featureUnit=unit*T.MathUtils.clamp(Math.cbrt(Math.max(1e-6,span.x*span.y*span.z))/2.2,1,2.2);\r\n function add(g:T.BufferGeometry,pos:T.Vector3,q:T.Quaternion,scale:T.Vector3,material=0){g.applyMatrix4(new T.Matrix4().compose(pos,q,scale));if(!g.attributes.uv)g.setAttribute('uv',new T.Float32BufferAttribute(new Float32Array(g.attributes.position.count*2),2));if(g.index){const expanded=g.toNonIndexed();g.dispose();g=expanded;}batches[material].push(g);}\r\n const color=[style.primary,style.accent,style.secondary];\r\n for(let k=0;k<count;k++){\r\n  const pick=rand()*total;let lo=0,hi=triangles.length-1;while(lo<hi){const m=(lo+hi)>>1;if(triangles[m].total<pick)lo=m+1;else hi=m;}\r\n  const {a,b,c}=triangles[lo],u=Math.sqrt(rand()),v=rand();point.copy(a).multiplyScalar(1-u).addScaledVector(b,u*(1-v)).addScaledVector(c,u*v);normal.copy(triangles[lo].normal);\r\n  let socket=false;group.traverse(o=>{for(const [x,y,z,r]of o.userData.eyeSockets??[])if(point.distanceTo(new T.Vector3(x,y,z))<r*1.48)socket=true;});\r\n  if(socket||(!hand&&region==='feet'&&normal.y<-.2))continue;\r\n  const larger=[2,3,9,13,14,16].includes(id)?1.7:1;\r\n  const q=new T.Quaternion().setFromUnitVectors(up,normal),s=featureUnit*amount*(.055+rand()*.075)*larger,center=point.clone(),size=new T.Vector3(s,s,s);\r\n  const geometry=(g:T.BufferGeometry,offset=0,scale=size,mat=0)=>add(g,center.clone().addScaledVector(normal,offset+s*.12),q,scale,mat);\r\n  switch(id){\r\n   case 63:geometry(new T.SphereGeometry(.8,10,7),-s*.3,new T.Vector3(s,s*.6,s),k%3);break;\r\n   case 64:geometry(new T.SphereGeometry(.7,10,7),-s*.12,new T.Vector3(s*.8,s*.65,s*.8),k%3);if(k%3===0)geometry(new T.TorusGeometry(.45,.13,5,10).rotateX(Math.PI/2),s*.05,size,0);break;\r\n   case 65:geometry(new T.DodecahedronGeometry(.6,0),0,new T.Vector3(s,s*.45,s),k%3);geometry(new T.SphereGeometry(.22,6,4),s*.4,size,2);break;\r\n   case 66:{const curve=new T.CatmullRomCurve3([new T.Vector3(-.7,0,0),new T.Vector3(-.25,.35,.25),new T.Vector3(.3,.2,-.15),new T.Vector3(.65,.55,.1)]);geometry(new T.TubeGeometry(curve,7,.15,5,false),s*.06,new T.Vector3(s,s,s),k%3);break;}\r\n   case 67:{const curve=new T.CatmullRomCurve3([new T.Vector3(-.6,0,0),new T.Vector3(-.15,.3,.15),new T.Vector3(.5,.55,-.1)]);geometry(new T.TubeGeometry(curve,6,.12,5,false),s*.1,new T.Vector3(s*.75,s*1.4,s),k%3);break;}\r\n   case 68:{geometry(new T.CylinderGeometry(.48,.38,.045,12),s*.12,size,1);geometry(new T.TorusGeometry(.53,.13,6,12).rotateX(Math.PI/2),s*.17,size,0);for(let t=0;t<6;t++){const a=t*Math.PI/3,offset=new T.Vector3(Math.cos(a)*s*.34,s*.32,Math.sin(a)*s*.34).applyQuaternion(q);add(new T.ConeGeometry(.08,.34,5),center.clone().add(offset),q,size,2);}break;}\r\n   case 69:geometry(new T.BoxGeometry(.65,.13,.85),s*.04,size,0);geometry(new T.BoxGeometry(.08,.05,1.15),s*.14,size,2);geometry(new T.CylinderGeometry(.12,.12,.1,8),s*.18,size,2);break;\r\n   case 70:geometry(new T.BoxGeometry(1.25,.18,.85),s*.1,size,0);geometry(new T.CylinderGeometry(.17,.17,.19,8),s*.27,size,1);geometry(new T.BoxGeometry(.9,.05,.12),s*.25,size,2);break;\r\n   case 71:geometry(new T.CylinderGeometry(.15,.15,1.7,8).rotateZ(Math.PI/2),s*.13,size,0);geometry(new T.BoxGeometry(.2,.11,1.05),s*.12,size,1);break;\r\n   case 72:geometry(new T.BoxGeometry(.8,.1,.5),s*.07,size,1);geometry(new T.CylinderGeometry(.1,.1,.12,6),s*.16,size,0);break;\r\n   case 73:geometry(new T.BoxGeometry(1,.15,1),s*.1,size,0);geometry(new T.BoxGeometry(.08,.025,1.1).rotateY(Math.PI/4),s*.2,size,1);break;\r\n   case 74:geometry(new T.SphereGeometry(.32,8,6),-s*.05,size,2);geometry(new T.TorusGeometry(.45,.07,5,12).rotateX(Math.PI/2),s*.07,size,0);break;\r\n   case 57:geometry(new T.OctahedronGeometry(.55,1),-s*.3,new T.Vector3(s*.6,s*.2,s*.6),k%3);break;\r\n   case 58:geometry(new T.SphereGeometry(.55,10,7),-s*.4,new T.Vector3(s*.45,s*.65,s*.45),k%3);break;\r\n   case 59:case 62:geometry(new T.CylinderGeometry(0,.32,1.7,6),s*.42,size,k%3);break;\r\n   case 61:geometry(new T.OctahedronGeometry(.22),-s*.2,new T.Vector3(s,s*.18,s),k%3);break;\r\n   case 1:{ // Roots and leaves have separate silhouettes, not generic spikes.\r\n    const path=new T.CatmullRomCurve3([new T.Vector3(0,-.1,0),new T.Vector3(.18,.5,0),new T.Vector3(-.15,1.3,.1),new T.Vector3(.35,2,.1)]);\r\n    geometry(new T.TubeGeometry(path,8,.14,5,false),0,size,2);geometry(new T.SphereGeometry(1,8,5),s*1.1,new T.Vector3(s*.55,s*.9,s*.12),1);break;}\r\n   case 2:geometry(new T.CylinderGeometry(.13,.19,1,6),s*.25,new T.Vector3(s,s,s),2);geometry(new T.SphereGeometry(1,12,8,0,Math.PI*2,0,Math.PI/2),s*.8,new T.Vector3(s*.8,s*.38,s*.8),k%3===0?1:0);geometry(new T.ConeGeometry(.73,.12,16),s*.77,size,2);break;\r\n   case 3:geometry(new T.SphereGeometry(1,6,4),s*.08,new T.Vector3(s*.86,s*.26,s*1.3),0);geometry(new T.ConeGeometry(.19,.8,5),s*.34,size,1);break;\r\n   case 4:geometry(new T.SphereGeometry(1,5,3),s*.015,new T.Vector3(s*.68,s*.17,s),k%6===0?1:0);break;\r\n   case 5:geometry(new T.ConeGeometry(.18,2.5,4),s*.6,new T.Vector3(s,s,s*.5),2);geometry(new T.SphereGeometry(.22,8,5),s*1.7,size,1);break;\r\n   case 6:{geometry(new T.CylinderGeometry(.19,.32,1.9,7),s*.55,size,0);for(const sign of [-1,1]){const branchQ=q.clone().multiply(new T.Quaternion().setFromAxisAngle(new T.Vector3(0,0,1),sign*.62));add(new T.CylinderGeometry(.12,.2,1.2,6),center.clone().addScaledVector(normal,s),branchQ,size,1);}geometry(new T.TorusGeometry(.2,.08,5,9),s*1.5,size,2);break;}\r\n   case 8:geometry(new T.SphereGeometry(1,9,6),s*.3,new T.Vector3(s*.3,s*1.9,s*.13),1);break;\r\n   case 9:{const curve=new T.CatmullRomCurve3([new T.Vector3(0,-.15,0),new T.Vector3(.1,.7,0),new T.Vector3(.6,1.5,0)]);const g=new T.TubeGeometry(curve,9,.24,7,false),p=g.attributes.position;for(let j=0;j<p.count;j++){const t=Math.floor(j/8)/9,at=curve.getPointAt(t),v=new T.Vector3().fromBufferAttribute(p,j).sub(at).multiplyScalar(1-t*.97).add(at);p.setXYZ(j,v.x,v.y,v.z);}g.computeVertexNormals();geometry(g,0,size,2);break;}\r\n   case 10:geometry(new T.TorusGeometry(.65,.075,5,18,Math.PI*1.55),s*.3,size,1);geometry(new T.OctahedronGeometry(.35),s*.4,size,0);break;\r\n   case 11:geometry(new T.OctahedronGeometry(1),s*.02,new T.Vector3(s*.7,s*.28,s*.85),2);geometry(new T.OctahedronGeometry(.11),s*.5,size,1);break;\r\n   case 12:geometry(new T.TorusGeometry(.65,.18,7,13,Math.PI*1.8),s*.25,size,0);geometry(new T.SphereGeometry(.28,8,6),s*.4,size,1);break;\r\n   case 13:geometry(new T.DodecahedronGeometry(1,0),s*.05,new T.Vector3(s*1.3,s*.38,s*.9),k%4===0?1:0);break;\r\n   case 14:case 16:geometry(new T.CylinderGeometry(0,.36,2.3,id===14?6:5),s*.72,new T.Vector3(s,s,s),k%3===0?1:0);geometry(new T.CylinderGeometry(.28,.34,.65,6),s*.14,size,2);break;\r\n   case 15:geometry(new T.DodecahedronGeometry(1,0),s*.025,new T.Vector3(s*1.25,s*.22,s),2);break;\r\n   case 17:geometry(new T.BoxGeometry(.12,1.7,.9),s*.45,size,2);geometry(new T.CylinderGeometry(.08,.08,1.4,5),s*.4,size,1);break;\r\n   case 18:{geometry(new T.CylinderGeometry(.76,.88,.18,8),s*.12,size,0);geometry(new T.TorusGeometry(.38,.1,5,14),s*.26,size,2);for(let r=0;r<6;r++){const angle=r*Math.PI/3,offset=new T.Vector3(Math.cos(angle)*s*.63,s*.29,Math.sin(angle)*s*.63).applyQuaternion(q);add(new T.CylinderGeometry(.08,.08,.12,6),center.clone().add(offset),q,size,1);}if(k%3===0)geometry(new T.CylinderGeometry(.46,.46,.23,12),s*.32,size,2);break;}\r\n   case 19:geometry(new T.BoxGeometry(1.1,.19,1.65),s*.06,size,2);geometry(new T.BoxGeometry(.075,.035,1.35),s*.18,size,1);break;\r\n   case 20:{const lean=.3+rand()*.6,curve=new T.CatmullRomCurve3([new T.Vector3(0,-.1,0),new T.Vector3(.15,.6,.1),new T.Vector3(lean,1.3,.25),new T.Vector3(lean*1.4,1.65,.3)]);const g=new T.TubeGeometry(curve,5,.035,3,false),p=g.attributes.position;for(let j=0;j<p.count;j++){const t=Math.floor(j/4)/5,at=curve.getPointAt(t),v=new T.Vector3().fromBufferAttribute(p,j).sub(at).multiplyScalar(1-t*.96).add(at);p.setXYZ(j,v.x,v.y,v.z);}g.computeVertexNormals();geometry(g,0,size,k%4===0?1:k%5===0?2:0);break;}\r\n   case 21:geometry(new T.SphereGeometry(1,10,7),-s*.72,new T.Vector3(s*.3,s*.43,s*.3),k%4===0?1:2);break;\r\n  }\r\n }\r\n batches.forEach((geos,index)=>{if(!geos.length)return;const g=mergeGeometries(geos,false);geos.forEach(g=>g.dispose());if(!g)return;\r\n  const mat=new T.MeshPhysicalMaterial({color:color[index],roughness:[14,16,21].includes(id)?.12:id===20?.95:style.roughness,metalness:style.metalness,clearcoat:[3,18,19,21].includes(id)?.7:0});\r\n  if(index===1&&[5,8,9,11,12,15,17,19].includes(id)){mat.emissive.set(style.accent);mat.emissiveIntensity=1.2;}\r\n  if([8,14,16].includes(id)){mat.transmission=.45;mat.thickness=unit*.15;mat.ior=1.35;mat.metalness=0;}\r\n  if(id>=57&&id<=62){mat.clearcoat=1;mat.clearcoatRoughness=.035;mat.roughness=.08;mat.iridescence=.9;mat.iridescenceThicknessRange=[100,650];\r\n   if(id===58){mat.transmission=.75;mat.thickness=unit*.04;mat.metalness=0;mat.ior=1.1;}\r\n   if(id===57||id===59){mat.transmission=.5;mat.thickness=unit*.08;mat.metalness=0;mat.ior=1.6;}\r\n   if(id===61){mat.metalness=.9;applySparkle(mat,1);}\r\n   if(id===62&&index===1){mat.emissive.set(style.accent);mat.emissiveIntensity=.5;}\r\n  }\r\n  if(id>=63&&id<=68){mat.metalness=0;mat.clearcoat=id===65?0:1;mat.clearcoatRoughness=.055;mat.roughness=id===65?.98:.16;\r\n   if(id===63){mat.transmission=.55;mat.thickness=unit*.08;mat.ior=1.33;}\r\n   if(id===64&&index===2){mat.transmission=.25;mat.thickness=unit*.06;}\r\n  }\r\n  if(id>=69&&id<=74){mat.metalness=id===74?0:style.metalness;mat.clearcoat=.8;mat.clearcoatRoughness=.06;\r\n   if(index===2&&[69,70,74].includes(id)){mat.emissive.set(style.accent);mat.emissiveIntensity=1.3;}\r\n   if(id===74&&index===0){mat.transmission=.75;mat.thickness=unit*.04;mat.roughness=.045;}\r\n  }\r\n  if(id===20){mat.sheen=1;mat.sheenColor.set(style.accent);}\r\n  if(id===21){mat.color.set(index===1?style.accent:'#edffd3');mat.roughness=.12;mat.metalness=0;}\r\n  if(id===18){mat.metalness=.87;mat.color.set(index===2?'#333d48':color[index]);}\r\n  const mesh=new T.Mesh(g,mat);mesh.name=style.detail+' structural details';mesh.userData={materialDetail:true,ownedGeometry:true,ownedMaterial:true,region};result.add(mesh);\r\n });return result;\r\n}\r\n","creature/source/creator/material-patterns.ts":"/** Built-in procedural surface families. These are local math, so previews work offline. */\r\nexport type SurfaceSample={height:number;tint:number;glow:number;rough:number};\r\nexport type SurfaceProfile={id:number;name:string;roughness:number;metalness:number;relief:number;bump:number;sheen:number;sheenRoughness:number;clearcoat:number;clearcoatRoughness:number};\r\n\r\n// Stable ids 32-55 correspond to the Battle Pass catalogue order in materials-registry.ts.\r\nexport const BUILTIN_SURFACE_PROFILES:readonly SurfaceProfile[]=[\r\n {id:32,name:'Plate Steel',roughness:.34,metalness:.82,relief:.024,bump:.012,sheen:0,sheenRoughness:.7,clearcoat:.35,clearcoatRoughness:.24},\r\n {id:33,name:'Rubber Grip',roughness:.95,metalness:0,relief:.055,bump:.022,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:34,name:'Chain Mail',roughness:.41,metalness:.78,relief:.034,bump:.014,sheen:0,sheenRoughness:.7,clearcoat:.3,clearcoatRoughness:.25},\r\n {id:35,name:'Track Rubber',roughness:.94,metalness:0,relief:.038,bump:.016,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:36,name:'Denim',roughness:.86,metalness:0,relief:.012,bump:.011,sheen:.22,sheenRoughness:.96,clearcoat:0,clearcoatRoughness:.5},\r\n {id:37,name:'Hex Tread',roughness:.82,metalness:.04,relief:.052,bump:.014,sheen:0,sheenRoughness:.9,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:38,name:'Sweatshirt Fleece',roughness:.99,metalness:0,relief:.05,bump:.032,sheen:.74,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n {id:39,name:'Quilted',roughness:.89,metalness:0,relief:.07,bump:.012,sheen:.14,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:40,name:'Speckled',roughness:.96,metalness:0,relief:.03,bump:.028,sheen:.34,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n {id:41,name:'Hammered Bronze',roughness:.39,metalness:.78,relief:.025,bump:.025,sheen:0,sheenRoughness:.7,clearcoat:.28,clearcoatRoughness:.22},\r\n {id:42,name:'Fine Stripe',roughness:.91,metalness:0,relief:.042,bump:.012,sheen:.08,sheenRoughness:.92,clearcoat:0,clearcoatRoughness:.5},\r\n {id:43,name:'Snake Skin',roughness:.68,metalness:.02,relief:.019,bump:.023,sheen:.1,sheenRoughness:.76,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:44,name:'Holey',roughness:.96,metalness:0,relief:.039,bump:.025,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:45,name:'Woven Mat',roughness:.9,metalness:0,relief:.014,bump:.014,sheen:.08,sheenRoughness:.94,clearcoat:0,clearcoatRoughness:.5},\r\n {id:46,name:'Petal',roughness:.51,metalness:0,relief:.025,bump:.009,sheen:.52,sheenRoughness:.72,clearcoat:.12,clearcoatRoughness:.3},\r\n {id:47,name:'Graph Paper',roughness:.93,metalness:0,relief:.012,bump:.017,sheen:.16,sheenRoughness:.97,clearcoat:0,clearcoatRoughness:.5},\r\n {id:48,name:'Bamboo',roughness:.57,metalness:0,relief:.027,bump:.008,sheen:.18,sheenRoughness:.62,clearcoat:.12,clearcoatRoughness:.28},\r\n {id:49,name:'Dragon Scale',roughness:.31,metalness:.16,relief:.043,bump:.01,sheen:.24,sheenRoughness:.42,clearcoat:.72,clearcoatRoughness:.16},\r\n {id:50,name:'Cool Graph Paper',roughness:.56,metalness:.23,relief:.021,bump:.008,sheen:0,sheenRoughness:.8,clearcoat:.12,clearcoatRoughness:.3},\r\n {id:51,name:'Terry Cloth',roughness:.98,metalness:0,relief:.014,bump:.026,sheen:.62,sheenRoughness:.99,clearcoat:0,clearcoatRoughness:.5},\r\n {id:52,name:'Pebble Path',roughness:.77,metalness:0,relief:.038,bump:.018,sheen:0,sheenRoughness:.8,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:53,name:'Wiggles',roughness:.99,metalness:0,relief:.016,bump:.016,sheen:0,sheenRoughness:.95,clearcoat:0,clearcoatRoughness:.5},\r\n {id:54,name:'River Stone',roughness:.72,metalness:.01,relief:.031,bump:.019,sheen:.04,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.32},\r\n {id:55,name:'Moss',roughness:.99,metalness:0,relief:.022,bump:.023,sheen:.38,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n];\r\nconst pixelProfile:SurfaceProfile={id:56,name:'64-bit Skin',roughness:.58,metalness:.12,relief:.018,bump:.01,sheen:0,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.4};\r\n// Extra finishes use stable additive IDs; existing reward IDs retain their meaning.\r\nexport const SPECIAL_SURFACE_PROFILES:readonly SurfaceProfile[]=[\r\n {id:57,name:'Opal Jelly',roughness:.06,metalness:0,relief:.004,bump:.001,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.035},\r\n {id:58,name:'Bubble Glass',roughness:.035,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},\r\n {id:59,name:'Prism Crystal',roughness:.045,metalness:0,relief:.052,bump:.008,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.03},\r\n {id:60,name:'Holographic Foil',roughness:.18,metalness:.88,relief:.022,bump:.013,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.045},\r\n {id:61,name:'Glitter Resin',roughness:.075,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},\r\n {id:62,name:'Galaxy Geode',roughness:.22,metalness:.25,relief:.065,bump:.014,sheen:0,sheenRoughness:.3,clearcoat:.8,clearcoatRoughness:.06},\r\n {id:63,name:'Caustic Slime',roughness:0.055,metalness:0,relief:0.035,bump:0.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:64,name:'Blister Hide',roughness:0.18,metalness:0,relief:0.065,bump:0.023,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:65,name:'Rotten Rind',roughness:0.94,metalness:0,relief:0.05,bump:0.025,sheen:0,sheenRoughness:.6,clearcoat:0,clearcoatRoughness:.07},\r\n {id:66,name:'Parasite Nest',roughness:0.31,metalness:0,relief:0.07,bump:0.027,sheen:0,sheenRoughness:.6,clearcoat:0.6,clearcoatRoughness:.07},\r\n {id:67,name:'Exposed Sinew',roughness:0.2,metalness:0,relief:0.045,bump:0.017,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:68,name:'Abyssal Maw',roughness:0.26,metalness:0,relief:0.065,bump:0.023,sheen:0,sheenRoughness:.6,clearcoat:0.8,clearcoatRoughness:.07},\r\n {id:69,name:'Circuit Alloy',roughness:0.19,metalness:0.83,relief:0.027,bump:0.01,sheen:0,sheenRoughness:.6,clearcoat:0.6,clearcoatRoughness:.07},\r\n {id:70,name:'Servo Armor',roughness:0.34,metalness:0.82,relief:0.055,bump:0.013,sheen:0,sheenRoughness:.6,clearcoat:0.4,clearcoatRoughness:.07},\r\n {id:71,name:'Chrome Rib',roughness:0.09,metalness:0.96,relief:0.05,bump:0.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:72,name:'Carbon Mech',roughness:0.64,metalness:0.18,relief:0.018,bump:0.013,sheen:0,sheenRoughness:.6,clearcoat:0.25,clearcoatRoughness:.07},\r\n {id:73,name:'Hazard Panel',roughness:0.46,metalness:0.55,relief:0.032,bump:0.014,sheen:0,sheenRoughness:.6,clearcoat:0.4,clearcoatRoughness:.07},\r\n {id:74,name:'Reactor Glass',roughness:0.045,metalness:0,relief:0.018,bump:0.004,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n];\r\nconst profiles=new Map([...BUILTIN_SURFACE_PROFILES,pixelProfile,...SPECIAL_SURFACE_PROFILES].map(p=>[p.id,p]));\r\nexport const builtinSurfaceProfile=(id:number)=>profiles.get(id);\r\nconst fract=(x:number)=>x-Math.floor(x),clamp=(x:number)=>Math.max(0,Math.min(1,x));\r\nconst hash=(x:number,y:number)=>fract(Math.sin(x*127.1+y*311.7)*43758.5453);\r\nconst smooth=(a:number,b:number,x:number)=>{const t=clamp((x-a)/(b-a));return t*t*(3-2*t);};\r\nconst triangle=(x:number)=>1-Math.abs(fract(x)*2-1);\r\nconst voronoi=(x:number,y:number)=>{const ix=Math.floor(x),iy=Math.floor(y);let near=9,next=9;for(let j=-1;j<=1;j++)for(let i=-1;i<=1;i++){const px=ix+i+.18+.64*hash(ix+i,iy+j),py=iy+j+.18+.64*hash(iy+j,ix+i+19),d=Math.hypot(x-px,y-py);if(d<near){next=near;near=d;}else if(d<next)next=d;}return{near,gap:next-near};};\r\n\r\n/** High-contrast structural masks are paired with their actual material height and roughness maps. */\r\nexport function sampleBuiltinSurface(id:number,x:number,y:number):SurfaceSample|undefined{\r\n if(!profiles.has(id))return undefined;\r\n x=fract(x);y=fract(y); // Geometry relief and RepeatWrapping must sample the same tiled domain.\r\n const g=Math.sin(x*167.3+Math.sin(y*43)*1.7)*Math.sin(y*151.7+Math.sin(x*37))*0.5+0.5;\r\n const line=(v:number,n:number,w:number)=>1-smooth(w,w*2,Math.abs(fract(v*n)-.5));\r\n let h=.5,t=.5,glow=0,rough=.8;\r\n switch(id){\r\n  case 63:{const c=voronoi(x*7,y*7),sac=1-smooth(.04,.44,c.near),ooze=.5+.5*Math.sin(x*20+Math.sin(y*14)*3);h=.32+sac*.49;t=.13+sac*.67+ooze*.12;rough=.055+sac*.045;break;}\r\n  case 64:{const c=voronoi(x*10,y*9),pustule=1-smooth(.08,.36,c.near),rim=1-smooth(.02,.08,Math.abs(c.near-.33));h=.34+pustule*.56-rim*.08;t=.1+pustule*.8;rough=.13+.24*rim;break;}\r\n  case 65:{const c=voronoi(x*11,y*11),rot=1-smooth(.02,.14,c.gap),mould=hash(Math.floor(x*57),Math.floor(y*57))>.7?1:0;h=.56-rot*.4+mould*.12;t=.12+mould*.49+(1-rot)*.22;rough=.86+mould*.13;break;}\r\n  case 66:{const c=voronoi(x*9,y*9),hole=1-smooth(.1,.22,c.near),lip=1-smooth(.045,.1,Math.abs(c.near-.25));h=.53-hole*.42+lip*.18;t=.47-hole*.45+lip*.42;rough=.22+hole*.52;break;}\r\n  case 67:{const strand=.5+.5*Math.sin(x*95+Math.sin(y*13)*5),cleft=Math.pow(1-strand,8);h=.3+strand*.43-cleft*.15;t=.15+strand*.68;rough=.13+cleft*.35;break;}\r\n  case 68:{const c=voronoi(x*6,y*6),mouth=1-smooth(.06,.26,c.near),rim=1-smooth(.02,.07,Math.abs(c.near-.28)),tooth=hash(Math.floor(x*49),Math.floor(y*49))>.76?rim:0;h=.51-mouth*.47+rim*.26;t=.3-mouth*.28+tooth*.67;rough=.16+mouth*.64;break;}\r\n  case 69:{const wire=Math.max(line(x,12,.028),line(y,12,.025)),junction=1-smooth(.025,.065,Math.hypot(fract(x*6)-.5,fract(y*6)-.5));h=.49+wire*.17+junction*.2;t=.12+wire*.68+junction*.17;glow=wire*.8+junction;rough=.22-wire*.1;break;}\r\n  case 70:{const u=fract(x*4),v=fract(y*5),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.07,edge),bolt=1-smooth(.028,.07,Math.hypot(u-.14,v-.14));h=.68-seam*.45+bolt*.25;t=.56-seam*.5+bolt*.4;rough=.32+seam*.42;break;}\r\n  case 71:{const rib=triangle(x*13),groove=1-smooth(.07,.18,rib);h=.32+.57*rib;t=.17+.78*rib;rough=.09+groove*.3;break;}\r\n  case 72:{const u=fract(x*18),v=fract(y*18),over=(Math.floor(x*18)+Math.floor(y*18))%2,thread=over?Math.sin(u*Math.PI):Math.sin(v*Math.PI);h=.44+thread*.14;t=.12+thread*.69;rough=.64+.12*(1-thread);break;}\r\n  case 73:{const stripe=fract((x+y)*7)<.5?1:0,u=fract(x*3),v=fract(y*3),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.07,edge);h=.58-seam*.34;t=.07+stripe*.74-seam*.07;rough=.41+seam*.32;break;}\r\n  case 74:{const cell=voronoi(x*7,y*7),seam=1-smooth(.02,.07,cell.gap),core=1-smooth(.04,.16,cell.near);h=.52-seam*.12;t=.23+seam*.53+core*.23;glow=seam*.7+core;rough=.04+seam*.07;break;}\r\n\r\n  case 57:{const cloud=.5+.5*Math.sin(x*12+Math.sin(y*9)*2)*Math.cos(y*11);h=.48+cloud*.07;t=.48+cloud*.4;rough=.045+cloud*.025;break;}\r\n  case 58:{const c=voronoi(x*9,y*9),bubble=1-smooth(.1,.24,c.near);h=.49+bubble*.13;t=.7+bubble*.28;rough=.025+bubble*.035;break;}\r\n  case 59:{const c=voronoi(x*8,y*8),facet=Math.floor(c.near*7)/7,crack=1-smooth(.012,.06,c.gap);h=.35+facet*.65-crack*.12;t=.3+facet*.65;rough=.035+crack*.16;break;}\r\n  case 60:{const fold=Math.sin(x*31+Math.sin(y*23)*2)*Math.cos(y*27),ribs=triangle((x+y*.4)*38);h=.5+fold*.27+ribs*.06;t=.1+.8*(.5+.5*fold);rough=.12+ribs*.13;break;}\r\n  case 61:{const fleck=hash(Math.floor(x*83),Math.floor(y*83))>.91?1:0;h=.49+fleck*.035;t=.58+fleck*.4;rough=.075-fleck*.045;break;}\r\n  case 62:{const c=voronoi(x*6,y*6),seam=1-smooth(.025,.1,c.gap),star=hash(Math.floor(x*93),Math.floor(y*93))>.989?1:0;h=.65-seam*.43;t=.06+seam*.73+star*.2;glow=seam*.55+star*.4;rough=.32-seam*.2;break;}\r\n\r\n  case 56:{const ix=Math.floor(x*16),iy=Math.floor(y*16),pixel=Math.floor(hash(ix,iy)*4)/3;h=.35+.3*pixel;t=.1+.8*pixel;rough=.5+.15*(1-pixel);break;}\r\n  case 32:{const u=fract(x*3),v=fract(y*3),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.065,edge),rivet=Math.hypot(u-.5,v-.5),dot=(1-smooth(.035,.075,rivet))*((Math.floor(x*3)+Math.floor(y*3))%2===0?1:.35);h=.69-.34*seam+.28*dot;t=.69-.49*seam+.18*dot;rough=.3+.45*seam;break;}\r\n  case 33:{const d=Math.abs(fract((x+y)*6)-.5),a=Math.abs(fract((x-y)*6)-.5),ridge=Math.max(1-smooth(.045,.12,d),1-smooth(.045,.12,a)),trough=smooth(.16,.34,Math.min(d,a));h=.25+.64*ridge-.1*trough;t=.09+.82*ridge-.08*trough;rough=.99-.16*ridge;break;}\r\n  case 34:{const u=fract(x*7),v=fract(y*7),cx=Math.floor(x*7)+(Math.floor(y*7)%2)*.5+.5,cy=Math.floor(y*7)+.5,r=Math.hypot((x*7-cx)*.72,y*7-cy),ring=1-smooth(.055,.13,Math.abs(r-.31)),hole=1-smooth(.16,.25,r);h=.42+.39*ring-.15*hole;t=.3+.58*ring;rough=.58-.23*ring;void u;void v;break;}\r\n  case 35:{const chevron=triangle((x+y*.48)*9),ridge=1-smooth(.18,.31,Math.abs(chevron-.5));h=.31+.51*ridge;t=.18+.67*ridge;rough=.97-.11*ridge;break;}\r\n  case 36:{const twill=triangle((x+y)*26),cross=triangle((x-y*.12)*5);h=.46+.09*twill+.025*cross;t=.25+.56*twill;rough=.82+g*.12;break;}\r\n  case 37:{const u=fract(x*8),v=fract(y*8),hex=Math.max(Math.abs(u-.5)*.866+Math.abs(v-.5)*.5,Math.abs(v-.5)),ridge=1-smooth(.31,.43,hex);h=.28+.62*ridge;t=.14+.73*ridge;rough=.9-.17*ridge;break;}\r\n  case 38:{const c=voronoi(x*14,y*13),pile=1-smooth(.1,.54,c.near),valley=smooth(.06,.22,c.gap),fiber=Math.sin(x*83+Math.sin(y*41)*1.7)*Math.sin(y*77+Math.sin(x*37))*0.5+0.5;h=.2+.53*pile-.14*valley+.025*fiber;t=.1+.78*pile-.1*valley+.035*fiber;rough=.99;break;}\r\n  case 39:{const u=fract(x*4),v=fract(y*4),diamond=Math.abs(u-.5)+Math.abs(v-.5),seam=smooth(.39,.48,diamond),pad=1-smooth(.08,.43,diamond);h=.28+.62*pad-.21*seam;t=.14+.72*pad-.1*seam;rough=.93-.08*pad;break;}\r\n  case 40:{const d=voronoi(x*19,y*17),dimple=1-smooth(.06,.24,d.near),nap=smooth(.16,.39,d.near),fiber=Math.sin(x*91+Math.sin(y*48))*Math.sin(y*79+Math.sin(x*43));h=.46+.035*g-.14*dimple+.06*nap+.012*fiber;t=.54+.04*g-.27*dimple+.13*nap+.025*fiber;rough=.97-.025*nap;break;}\r\n  case 41:{const c=voronoi(x*11,y*11),pit=1-smooth(.03,.22,c.near),lip=1-smooth(.04,.2,c.gap);h=.55-.29*pit+.15*lip;t=.45-.31*pit+.18*lip;rough=.47-.16*lip+.28*pit;break;}\r\n  case 42:{const strand=triangle((x*8+y*1.7)*5),twist=triangle((x*8-y*1.7)*2);h=.27+.47*strand+.13*twist;t=.16+.74*strand;rough=.91-.08*strand;break;}\r\n  case 43:{const c=voronoi(x*15,y*15),crease=1-smooth(.025,.11,c.gap),grain=Math.sin(x*81+Math.sin(y*43))*Math.sin(y*75);h=.5-.16*crease+grain*.045;t=.38+.16*grain-.22*crease;rough=.72+.19*crease;break;}\r\n  case 44:{const c=voronoi(x*12,y*12),cell=smooth(.06,.37,c.near),chip=(hash(Math.floor(x*12),Math.floor(y*12))>.74?1:0)*cell;h=.34+.4*cell-.24*chip;t=.19+.62*cell-.18*chip;rough=.97;break;}\r\n  case 45:{const a=line(x+y*.12,12,.34),b=line(y-x*.08,12,.34),over=fract(x*12+y*12)<.5?a:b;h=.36+.27*a+.22*b+.12*over;t=.18+.37*a+.42*b;rough=.92-.12*over;break;}\r\n  case 46:{const u=fract(x*5)-.5,v=fract(y*5)-.5,angle=Math.atan2(v,u),r=Math.hypot(u,v),lobe=(1-smooth(.28,.49,r))*(.65+.35*Math.cos(angle*6)),vein=1-smooth(.012,.035,Math.abs(v-Math.sin(u*6)*.035));h=.33+.31*lobe+.12*vein;t=.19+.48*lobe+.22*vein;rough=.6-.17*lobe;break;}\r\n  case 47:{const vertical=line(x,22,.31),horizontal=line(y,22,.31),crossing=vertical*horizontal;h=.41+.12*vertical+.12*horizontal+.13*crossing;t=.26+.27*vertical+.27*horizontal;rough=.95-.08*crossing;break;}\r\n  case 48:{const rib=triangle(x*8),node=1-smooth(.015,.07,Math.abs(fract(y*2)-.5));h=.27+.53*(1-smooth(.28,.48,rib))+.14*node;t=.17+.67*(1-smooth(.3,.47,rib))+.12*node;rough=.68-.15*node;break;}\r\n  case 49:{const u=fract(x*6+(Math.floor(y*7)%2)*.5),v=fract(y*7),scale=Math.hypot((u-.5)*.86,v-.5),rim=1-smooth(.36,.48,scale),seam=1-smooth(.025,.09,Math.abs(v-.5));h=.28+.46*rim-.08*seam;t=.12+.74*rim-.12*seam;rough=.52-.24*rim;break;}\r\n  case 50:{const u=fract(x*14),v=fract(y*14),bar=Math.min(u,1-u,v,1-v),wire=1-smooth(.035,.105,bar),hole=1-wire;h=.26+.56*wire;t=.08+.76*wire;rough=.58-.2*wire;break;}\r\n  case 51:{const u=fract(x*18),v=fract(y*18),stagger=(Math.floor(y*18)%2)*.5,cx=fract(x*18+stagger)-.5,cy=v-.5,r=Math.hypot(cx,cy),loop=1-smooth(.035,.105,Math.abs(r-.27)),center=1-smooth(.01,.08,r);h=.44+.2*loop+.06*center;t=.34+.28*loop;rough=.99;break;}\r\n  case 52:{const c=voronoi(x*7,y*7),stone=1-smooth(.2,.52,c.near),edge=1-smooth(.035,.1,c.gap);h=.26+.49*stone-.1*edge;t=.1+.76*stone-.14*edge;rough=.87-.2*stone;break;}\r\n  case 53:{const q=y*7+Math.sin(x*17+y*4)*.35+Math.sin(x*6-y*13)*.16,ridge=1-smooth(.16,.34,Math.abs(fract(q)-.5));h=.39+.31*ridge+.025*g;t=.3+.38*ridge;rough=.99;break;}\r\n  case 54:{const c=voronoi(x*4,y*4),stone=1-smooth(.31,.75,c.near),seam=1-smooth(.045,.13,c.gap);h=.31+.43*stone-.16*seam;t=.15+.67*stone-.19*seam;rough=.79-.14*stone+.1*seam;break;}\r\n  case 55:{const c=voronoi(x*31,y*31),tuft=1-smooth(.08,.38,c.near),spore=hash(Math.floor(x*31),Math.floor(y*31))>.84?1:0;h=.37+.2*tuft+.07*spore;t=.2+.61*tuft+.12*spore;rough=.99;break;}\r\n }\r\n return{height:h,tint:clamp(t),glow,rough};\r\n}\r\n","creature/source/creator/material-refinement.ts":"import * as T from 'three';\r\nimport {mergeVertices} from 'three/examples/jsm/utils/BufferGeometryUtils.js';\r\n\r\nexport type CoachReliefBudget={remainingTriangles:number;usedTriangles:number;perMeshAddedTriangles:number;perMeshVertices:number};\r\n// Budgets count added triangles per coach, and total final positions per mesh.\r\nexport const createCoachReliefBudget=(limit=240_000,perMeshAddedTriangles=64_000,perMeshVertices=32_000):CoachReliefBudget=>({remainingTriangles:limit,usedTriangles:0,perMeshAddedTriangles,perMeshVertices});\r\nconst isFloat16=(a:{array:ArrayLike<number>;isFloat16BufferAttribute?:boolean})=>!!a.isFloat16BufferAttribute||(()=>{const Float16=(globalThis as any).Float16Array;return !!Float16&&a.array instanceof Float16;})();\r\n\r\nfunction component(a:T.BufferAttribute|T.InterleavedBufferAttribute,i:number,c:number){\r\n if(c===0)return a.getX(i);if(c===1)return a.getY(i);if(c===2)return a.getZ(i);if(c===3)return a.getW(i);\r\n return a.array[i*a.itemSize+c];\r\n}\r\nfunction write(a:T.BufferAttribute,i:number,c:number,v:number){\r\n if(c===0)a.setX(i,v);else if(c===1)a.setY(i,v);else if(c===2)a.setZ(i,v);else if(c===3)a.setW(i,v);else a.array[i*a.itemSize+c]=v;\r\n}\r\nfunction normalizeAttribute(a:T.BufferAttribute,i:number){\r\n if(a.itemSize<3)return;\r\n const x=a.getX(i),y=a.getY(i),z=a.getZ(i),length=Math.hypot(x,y,z);if(length<1e-12)return;\r\n a.setXYZ(i,x/length,y/length,z/length);\r\n}\r\n\r\n/** One linear triangle split. It preserves silhouette, UVs, authored normals and vertex attributes. */\r\nfunction split(source:T.BufferGeometry,maxVertices:number):T.BufferGeometry|undefined{\r\n const index=source.index;if(!index||index.count%3)return undefined;\r\n const edgeIds=new Map<string,number>(),edges:Array<[number,number]>=[],indices:number[]=[];\r\n const midpoint=(a:number,b:number)=>{\r\n  const lo=Math.min(a,b),hi=Math.max(a,b),key=lo+':'+hi,old=edgeIds.get(key);if(old!==undefined)return old;\r\n  const id=source.attributes.position.count+edges.length;edgeIds.set(key,id);edges.push([lo,hi]);return id;\r\n };\r\n const groups=source.groups.length?source.groups:[{start:0,count:index.count,materialIndex:0}],nextGroups:{start:number;count:number;materialIndex:number}[]=[];\r\n for(const group of groups){\r\n  const start=indices.length,end=Math.min(index.count,group.start+group.count);\r\n  for(let j=group.start;j+2<end;j+=3){const a=index.getX(j),b=index.getX(j+1),c=index.getX(j+2),ab=midpoint(a,b),bc=midpoint(b,c),ca=midpoint(c,a);\r\n   indices.push(a,ab,ca,ab,b,bc,ca,bc,c,ab,bc,ca);\r\n  }\r\n  if(indices.length>start)nextGroups.push({start,count:indices.length-start,materialIndex:group.materialIndex??0});\r\n }\r\n if(!edges.length||source.attributes.position.count+edges.length>maxVertices)return undefined;\r\n const out=source.clone();\r\n for(const [name,attribute]of Object.entries(source.attributes)){\r\n  if((attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||isFloat16(attribute))return undefined;\r\n  const count=attribute.count+edges.length,array=new (attribute.array.constructor as any)(count*attribute.itemSize);array.set(attribute.array);\r\n  const target=new T.BufferAttribute(array,attribute.itemSize,attribute.normalized);target.name=attribute.name;target.setUsage((attribute as T.BufferAttribute).usage);target.gpuType=(attribute as T.BufferAttribute).gpuType;\r\n  for(let e=0;e<edges.length;e++){const [a,b]=edges[e],dst=attribute.count+e;\r\n   for(let c=0;c<attribute.itemSize;c++)write(target,dst,c,(component(attribute,a,c)+component(attribute,b,c))*.5);\r\n   if(name==='normal'||name==='tangent')normalizeAttribute(target,dst);\r\n  }\r\n  out.setAttribute(name,target);\r\n }\r\n out.setIndex(indices);out.clearGroups();for(const group of nextGroups)out.addGroup(group.start,group.count,group.materialIndex);\r\n out.morphAttributes=source.morphAttributes;out.morphTargetsRelative=source.morphTargetsRelative;out.userData={...source.userData};\r\n return out;\r\n}\r\n\r\n/** Refine only bounded static roster geometry; unknown animated/morph geometry safely stays untouched. */\r\nexport function refineCoachGeometry(source:T.BufferGeometry,budget:CoachReliefBudget,worldScale=1):{geometry:T.BufferGeometry;addedTriangles:number;addedVertices:number;levels:number}|undefined{\r\n if(!budget||budget.remainingTriangles<=0||Object.values(source.morphAttributes).some(attributes=>attributes.length)||source.attributes.skinIndex||source.attributes.skinWeight)return undefined;\r\n if(source.drawRange.start!==0||(source.index&&source.drawRange.count<source.index.count))return undefined;\r\n for(const attribute of Object.values(source.attributes))if((attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||isFloat16(attribute))return undefined;\r\n let working=source.clone();\r\n if(!working.index){const merged=mergeVertices(working);working.dispose();working=merged;}\r\n if(!working.index||working.index.count%3){working.dispose();return undefined;}\r\n if(!working.attributes.normal)working.computeVertexNormals();\r\n const triangles=working.index.count/3;\r\n // Estimate a 90th-percentile edge so tiny existing triangles stay untouched while broad facets\r\n // receive enough linear splits to reach roughly .025 world units. The total remains tightly capped.\r\n if(triangles>10_000){working.dispose();return undefined;}\r\n const positions=working.attributes.position,edges:number[]=[];for(let j=0;j<working.index.count;j+=3){const a=working.index.getX(j),b=working.index.getX(j+1),c=working.index.getX(j+2);for(const [u,v]of [[a,b],[b,c],[c,a]])edges.push(Math.hypot(positions.getX(u)-positions.getX(v),positions.getY(u)-positions.getY(v),positions.getZ(u)-positions.getZ(v))*worldScale);}\r\n edges.sort((a,b)=>a-b);const p90=edges[Math.floor((edges.length-1)*.9)]??0,desired=Math.min(5,Math.max(0,Math.ceil(Math.log2(p90/.025))));\r\n if(!desired){working.dispose();return undefined;}\r\n const initial=working.attributes.position.count,initialTriangles=triangles;let levels=0;\r\n for(let level=0;level<desired;level++){\r\n  const currentTriangles=working.index!.count/3,nextTriangles=currentTriangles*4,cumulativeAdded=nextTriangles-initialTriangles;\r\n  if(cumulativeAdded>budget.remainingTriangles||cumulativeAdded>budget.perMeshAddedTriangles||nextTriangles>64_000)break;\r\n  const next=split(working,budget.perMeshVertices);if(!next)break;\r\n  working.dispose();working=next;levels++;\r\n }\r\n const addedTriangles=working.index!.count/3-initialTriangles,addedVertices=working.attributes.position.count-initial;\r\n if(!levels){working.dispose();return undefined;}\r\n budget.remainingTriangles-=addedTriangles;budget.usedTriangles+=addedTriangles;\r\n working.userData={...working.userData,coachRelief:{addedTriangles,addedVertices,levels,originalVertices:initial}};\r\n return {geometry:working,addedTriangles,addedVertices,levels};\r\n}\r\n\r\n/** Optional alternate-coach quality path for the original imported material (no recolouring). */\r\nexport function refineCoachMesh(mesh:T.Mesh,budget:CoachReliefBudget){\r\n if(Array.isArray(mesh.material)||(mesh as T.SkinnedMesh).isSkinnedMesh||mesh.geometry.attributes.skinIndex||mesh.geometry.attributes.skinWeight||Object.values(mesh.geometry.morphAttributes).some(attributes=>attributes.length))return undefined;\r\n mesh.updateWorldMatrix(true,false);const result=refineCoachGeometry(mesh.geometry,budget,mesh.matrixWorld.getMaxScaleOnAxis());if(!result)return undefined;\r\n const previous=mesh.geometry;mesh.geometry=result.geometry;if(mesh.userData.ownedGeometry)previous.dispose();mesh.userData.ownedGeometry=true;\r\n return {addedTriangles:result.addedTriangles,addedVertices:result.addedVertices,levels:result.levels};\r\n}\r\n","creature/source/creator/materials-registry.ts":"// Rank 4 runtime material registry: textures, colours and palettes.\r\n// Adding a texture/colour/palette is a new entry (+ files for battle-pass packs) here —\r\n// no other code changes. See plan/PLAN.md \"Rank 4\" and plan/reports/audit-materials.md.\r\nimport {STYLES as LEGACY_STYLES, COLOUR_SOURCE, type MaterialChoice, type Region} from './design';\r\nimport {isGranted, type UnlockKind,migrateSavedCosmetics} from './unlock-store';\r\nimport {isGranted as ledgerGranted} from '../../../unlock-ledger.mjs';\r\nimport {currentCosmeticCoach,cosmeticId} from './unlock-store';\r\nimport PALETTE_DATA from './palettes.json';\r\nimport {TEXTURE_SWAP,FREE_COLOURS} from '../../../battle-pass-rewards.mjs';\r\nimport {FREE_TEXTURE_IDS,RETIRED_TEXTURE_IDS} from './texture-policy.mjs';\r\nimport {RECIPE_KEY} from '../profile';\r\nimport {builtinSurfaceProfile} from './material-patterns';\r\nexport {grantUnlock} from './unlock-store';\r\nexport {FREE_COLOURS};\r\n\r\nexport type UnlockRule = 'default' | 'battle-pass' | 'aura-milestone';\r\nexport type Track = 'chest' | 'quads' | 'glutes' | 'arms' | 'yoga' | 'martial-arts' | 'cardio' | 'meditation';\r\n\r\nexport type TextureDef = {\r\n id: string; displayName: string; unlockRule: UnlockRule;\r\n track?: Track; passLevel?: 1 | 3 | 5; packId?: string; legacy?: boolean;\r\n familyId: number;        // feeds surfaceSample()'s procedural pattern switch in material-language.ts\r\n defaultColorId: string;  // colour used until the player chooses another\r\n};\r\nexport type ColorDef = { id: string; displayName: string; unlockRule: UnlockRule; primary: string; secondary: string; accent: string; packId?: string };\r\n// D32: a palette is a primary/secondary/accent triad (the same roles the tint uses). Aura\r\n// palettes carry unlockAtDay; battle-pass palettes carry `reward` = the slot that grants them\r\n// ('<bossId>:L<n>' or 'food:L<n>', read by battle-pass-rewards.mjs).\r\nexport type PaletteDef = { id: string; displayName: string; tagline: string; unlockRule: UnlockRule; unlockAtDay?: number; reward?: string; colors: [string, string, string]; finish?: 'matte' | 'brushed-metal' | 'polished-metal' | 'glitter' | 'pearl' | 'watercolor' | 'marbled' | 'glaze' };\r\n\r\nconst isUnlocked = (kind: UnlockKind, item: { id: string; unlockRule: UnlockRule },coachId?:string) => item.unlockRule === 'default' || isGranted(kind, item.id,typeof coachId==='string'?coachId:undefined);\r\n// R18 G2: a single-colour def is free exactly when its primary is one of the 15 free hexes (battle-pass-rewards.mjs FREE_COLOURS).\r\nconst colourRule = (c: { primary: string }): UnlockRule => FREE_COLOURS.includes(c.primary.toLowerCase()) ? 'default' : 'battle-pass';\r\nexport const hasCoach64BitSkin = (coachId=currentCosmeticCoach()) => ledgerGranted('boss-skin',cosmeticId(typeof coachId==='string'?coachId:currentCosmeticCoach(),`${typeof coachId==='string'?coachId:currentCosmeticCoach()}-skin`));\r\nexport const isTextureUnlocked = (t: TextureDef,coachId?:string) => !RETIRED_TEXTURE_IDS.includes(t.id)\r\n && (FREE_TEXTURE_IDS.includes(t.id) || isGranted('texture',t.id,coachId))\r\n && (t.id!=='coach-64-bit' || hasCoach64BitSkin(coachId));\r\nexport const isColorUnlocked = (c: ColorDef,coachId?:string) => isUnlocked('color', c,coachId);\r\nexport const isPaletteUnlocked = (p: PaletteDef,coachId?:string) => isUnlocked('palette', p,coachId);\r\n\r\n// Clay remains the safe starter finish. Flat survives only as an internal legacy profile.\r\n// familyId 30/31 have matching cases added to surfaceSample() in material-language.ts.\r\nconst FLAT_BASE = { id: 30, name: 'Flat', realm: '', primary: '#c7c3ce', secondary: '#6d6a75', accent: '#ffffff', emissive: '#000000', roughness: .55, metalness: 0, detail: 'flat' } as const;\r\nconst CLAY_BASE = { id: 31, name: 'Clay', realm: '', primary: '#b7a68e', secondary: '#7a6b57', accent: '#ddcdb3', emissive: '#000000', roughness: .92, metalness: 0, detail: 'clay' } as const;\r\nexport const FLAT_TEXTURE: TextureDef = { id: 'flat', displayName: 'Flat', unlockRule: 'battle-pass', familyId: FLAT_BASE.id, defaultColorId: '#7f7d78' };\r\nconst CLAY_TEXTURE: TextureDef = { id: 'clay', displayName: 'Clay', unlockRule: 'default', familyId: CLAY_BASE.id, defaultColorId: '#c4a77d' };\r\n\r\n// --- The 22 (well, 23: 0-22) existing procedural surface families keep working exactly as\r\n// before for legacy `styles` recipes, and are also exposed as ordinary registry textures +\r\n// their historical colour, `legacy:true`, unlockRule 'default' -- except the 7 that #140 moved\r\n// into the battle pass (LEGACY_TEXTURES, below the battle-pass table they take slots from). ---\r\nconst LEGACY_COLORS: ColorDef[] = LEGACY_STYLES.map(s => ({ id: 'legacy-color-' + s.id, displayName: s.name + ' (original)', unlockRule: colourRule(s), primary: s.primary, secondary: s.secondary, accent: s.accent }));\r\n\r\n// --- Simple default colours: \"basic colours auto\" (audit M7). Any of these tints any texture. ---\r\nconst SIMPLE_COLORS: ColorDef[] = ([\r\n { id: 'default-slate', displayName: 'Slate', unlockRule: 'default', primary: '#8b8f9a', secondary: '#4a4d55', accent: '#e7e9ee' },\r\n { id: 'default-clay', displayName: 'Warm Clay', unlockRule: 'default', primary: '#b7a68e', secondary: '#7a6b57', accent: '#ddcdb3' },\r\n { id: 'default-ruby', displayName: 'Ruby', unlockRule: 'default', primary: '#a23b4a', secondary: '#4f1620', accent: '#f2a3ae' },\r\n { id: 'default-sapphire', displayName: 'Sapphire', unlockRule: 'default', primary: '#2d5aa0', secondary: '#122a4d', accent: '#a9c9f5' },\r\n { id: 'default-moss', displayName: 'Moss', unlockRule: 'default', primary: '#4c7a3f', secondary: '#20351a', accent: '#c3e6a8' },\r\n { id: 'default-gold', displayName: 'Gold', unlockRule: 'default', primary: '#c9a13a', secondary: '#5f4a15', accent: '#ffe9a8' },\r\n { id: 'default-charcoal', displayName: 'Charcoal', unlockRule: 'default', primary: '#333238', secondary: '#131318', accent: '#8d8d96' },\r\n { id: 'default-blush', displayName: 'Blush', unlockRule: 'default', primary: '#d98fa0', secondary: '#7a3d49', accent: '#ffdbe4' },\r\n] as ColorDef[]).map(c => ({ ...c, unlockRule: colourRule(c) }));\r\n\r\n// --- Battle-pass textures: PLAN §6.2 / plan/muse/item-catalog.json (D14: 3 per style, D16: L1/L3/L5).\r\n// Every catalogue option below has a stable built-in procedural pattern and PBR profile.\r\n// Built-in surfaces render locally; unlock policy remains independent from pattern availability. ---\r\nconst SLOT_LEVEL: Record<string, 1 | 3 | 5> = { 'texture-1': 1, 'texture-2': 3, 'texture-3': 5 };\r\nconst BATTLE_PASS_SOURCE: { id: string; name: string; slot: string; track: string; familyId:number }[] = [\r\n { id: 'chest-plate-steel', name: 'Plate Steel', slot: 'texture-1', track: 'chest',familyId:32 },\r\n { id: 'chest-rubber-grip', name: 'Rubber Grip', slot: 'texture-2', track: 'chest',familyId:33 },\r\n { id: 'chest-chain-mail', name: 'Chain Mail', slot: 'texture-3', track: 'chest',familyId:34 },\r\n { id: 'quads-track-rubber', name: 'Track Rubber', slot: 'texture-1', track: 'quads',familyId:35 },\r\n { id: 'quads-denim', name: 'Denim', slot: 'texture-2', track: 'quads',familyId:36 },\r\n { id: 'quads-hex-tread', name: 'Hex Tread', slot: 'texture-3', track: 'quads',familyId:37 },\r\n { id: 'glutes-sweatshirt-fleece', name: 'Sweatshirt Fleece', slot: 'texture-1', track: 'glutes',familyId:38 },\r\n { id: 'glutes-quilted', name: 'Quilted', slot: 'texture-2', track: 'glutes',familyId:39 },\r\n { id: 'glutes-peach', name: 'Speckled', slot: 'texture-3', track: 'glutes',familyId:40 },\r\n { id: 'arms-hammered-bronze', name: 'Hammered Bronze', slot: 'texture-1', track: 'arms',familyId:41 },\r\n { id: 'arms-rope', name: 'Fine Stripe', slot: 'texture-2', track: 'arms',familyId:42 },\r\n { id: 'arms-leather', name: 'Snake Skin', slot: 'texture-3', track: 'arms',familyId:43 },\r\n { id: 'yoga-cork', name: 'Holey', slot: 'texture-1', track: 'yoga',familyId:44 },\r\n { id: 'yoga-woven-mat', name: 'Woven Mat', slot: 'texture-2', track: 'yoga',familyId:45 },\r\n { id: 'yoga-petal', name: 'Petal', slot: 'texture-3', track: 'yoga',familyId:46 },\r\n { id: 'martial-arts-canvas-gi', name: 'Graph Paper', slot: 'texture-1', track: 'martial-arts',familyId:47 },\r\n { id: 'martial-arts-bamboo', name: 'Bamboo', slot: 'texture-2', track: 'martial-arts',familyId:48 },\r\n { id: 'martial-arts-dragon-scale', name: 'Dragon Scale', slot: 'texture-3', track: 'martial-arts',familyId:49 },\r\n { id: 'cardio-mesh', name: 'Cool Graph Paper', slot: 'texture-1', track: 'cardio',familyId:50 },\r\n { id: 'cardio-terry-cloth', name: 'Terry Cloth', slot: 'texture-2', track: 'cardio',familyId:51 },\r\n { id: 'cardio-pebble-path', name: 'Pebble Path', slot: 'texture-3', track: 'cardio',familyId:52 },\r\n { id: 'meditation-sand-garden', name: 'Wiggles', slot: 'texture-1', track: 'meditation',familyId:53 },\r\n { id: 'meditation-river-stone', name: 'River Stone', slot: 'texture-2', track: 'meditation',familyId:54 },\r\n { id: 'meditation-moss', name: 'Moss', slot: 'texture-3', track: 'meditation',familyId:55 },\r\n];\r\n// R18 G1: only FREE_TEXTURE_IDS (texture-policy.mjs) are free; TEXTURE_SWAP now just names which legacy\r\n// texture sits in a battle-pass slot (and drives the #140 grandfather below), it never unlocks anything.\r\nconst SWAPPED_IN = new Map<string, string>(Object.entries(TEXTURE_SWAP).map(([freed, [legacyId]]) => [legacyId as string, freed]));\r\nconst textureRule = (id: string): UnlockRule => FREE_TEXTURE_IDS.includes(id) ? 'default' : 'battle-pass';\r\nconst BATTLE_PASS_TEXTURES: TextureDef[] = BATTLE_PASS_SOURCE.map(t => ({ id: t.id, displayName: t.name, unlockRule: textureRule(t.id), track: t.track as Track, passLevel: SLOT_LEVEL[t.slot], packId: 'pack-' + t.track, familyId: t.familyId, defaultColorId: '#7f7d78' }));\r\nconst LEGACY_TEXTURES: TextureDef[] = LEGACY_STYLES.map(s => {\r\n const id = 'legacy-' + s.id, slot = BATTLE_PASS_TEXTURES.find(t => t.id === SWAPPED_IN.get(id));\r\n return { id, displayName: s.name, unlockRule: textureRule(id), track: slot?.track, passLevel: slot?.passLevel, legacy: true, familyId: s.id, defaultColorId: 'legacy-color-' + s.id };\r\n});\r\n\r\nexport const COACH_64_BIT_TEXTURE:TextureDef={id:'coach-64-bit',displayName:'64-bit Pixel Finish',unlockRule:'battle-pass',familyId:56,defaultColorId:'#7f7d78'};\r\nexport const SPECIAL_TEXTURES:TextureDef[]=[{id:'opal-jelly',displayName:'Opal Jelly',unlockRule:'battle-pass',familyId:57,defaultColorId:'opal-jelly'},{id:'bubble-glass',displayName:'Bubble Glass',unlockRule:'battle-pass',familyId:58,defaultColorId:'bubble-glass'},{id:'prism-crystal',displayName:'Prism Crystal',unlockRule:'battle-pass',familyId:59,defaultColorId:'prism-crystal'},{id:'holo-foil',displayName:'Holographic Foil',unlockRule:'battle-pass',familyId:60,defaultColorId:'holo-foil'},{id:'glitter-resin',displayName:'Glitter Resin',unlockRule:'battle-pass',familyId:61,defaultColorId:'glitter-resin'},{id:'galaxy-geode',displayName:'Galaxy Geode',unlockRule:'battle-pass',familyId:62,defaultColorId:'galaxy-geode'},{id:'caustic-slime',displayName:'Caustic Slime',unlockRule:'battle-pass',familyId:63,defaultColorId:'caustic-slime'},{id:'blister-hide',displayName:'Blister Hide',unlockRule:'battle-pass',familyId:64,defaultColorId:'blister-hide'},{id:'rotten-rind',displayName:'Rotten Rind',unlockRule:'battle-pass',familyId:65,defaultColorId:'rotten-rind'},{id:'parasite-nest',displayName:'Parasite Nest',unlockRule:'battle-pass',familyId:66,defaultColorId:'parasite-nest'},{id:'exposed-sinew',displayName:'Exposed Sinew',unlockRule:'battle-pass',familyId:67,defaultColorId:'exposed-sinew'},{id:'abyssal-maw',displayName:'Abyssal Maw',unlockRule:'battle-pass',familyId:68,defaultColorId:'abyssal-maw'},{id:'circuit-alloy',displayName:'Circuit Alloy',unlockRule:'battle-pass',familyId:69,defaultColorId:'circuit-alloy'},{id:'servo-armor',displayName:'Servo Armor',unlockRule:'battle-pass',familyId:70,defaultColorId:'servo-armor'},{id:'chrome-rib',displayName:'Chrome Rib',unlockRule:'battle-pass',familyId:71,defaultColorId:'chrome-rib'},{id:'carbon-mech',displayName:'Carbon Mech',unlockRule:'battle-pass',familyId:72,defaultColorId:'carbon-mech'},{id:'hazard-panel',displayName:'Hazard Panel',unlockRule:'battle-pass',familyId:73,defaultColorId:'hazard-panel'},{id:'reactor-glass',displayName:'Reactor Glass',unlockRule:'battle-pass',familyId:74,defaultColorId:'reactor-glass'}].filter(t=>!RETIRED_TEXTURE_IDS.includes(t.id));\r\nexport const TEXTURES: TextureDef[] = [...SPECIAL_TEXTURES,CLAY_TEXTURE, ...LEGACY_TEXTURES, ...BATTLE_PASS_TEXTURES,COACH_64_BIT_TEXTURE].filter(t=>!RETIRED_TEXTURE_IDS.includes(t.id));\r\nexport const COLORS: ColorDef[] = [...SIMPLE_COLORS, ...LEGACY_COLORS];\r\n\r\n// --- Palettes: palettes.json next to this file (plan/muse/palettes.json cut to triads, plus the\r\n// D32 battle-pass fill). Adding a palette is a new row there. ---\r\nexport const PALETTES: PaletteDef[] = PALETTE_DATA.map(({ name, ...p }) => ({ ...p, displayName: name }) as PaletteDef);\r\n\r\nexport const findTexture = (id: string) => TEXTURES.find(t => t.id === id);\r\nexport const findColor = (id: string) => COLORS.find(c => c.id === id);\r\nexport const findPalette = (id: string) => PALETTES.find(p => p.id === id);\r\nexport function textureDefaultMetalness(id:string):number|undefined {\r\n const texture=findTexture(id);if(!texture)return undefined;\r\n return builtinSurfaceProfile(texture.familyId)?.metalness??(texture.legacy?LEGACY_STYLES[texture.familyId]?.metalness:texture.id==='clay'?CLAY_BASE.metalness:FLAT_BASE.metalness);\r\n}\r\n\r\nfunction triadFromPalette(p: PaletteDef) { const [primary, secondary, accent] = p.colors; return { primary, secondary, accent, paletteId:p.id }; }\r\n\r\n// R18 G2/G3: a colour channel holds ONE colour. A \"#rrggbb\" colorId is a single hex: free if it is one of\r\n// the 15 FREE_COLOURS, otherwise owned when any owned colour or palette contains it (so unlocking a\r\n// palette gives you its three hexes for the Body/Head/Eyes rows). Existing ids (default-ruby, pal-03, ...) still work.\r\nconst HEX = /^#[0-9a-f]{6}$/i;\r\nconst mix = (hex: string, to: number, t: number) => '#' + [1, 3, 5].map(i => Math.round(parseInt(hex.slice(i, i + 2), 16) * (1 - t) + to * t).toString(16).padStart(2, '0')).join('');\r\nconst hexOwned = (hex: string,coachId?:string) => FREE_COLOURS.includes(hex) || COLORS.some(c => isColorUnlocked(c,coachId) && [c.primary, c.secondary, c.accent].some(h => h.toLowerCase() === hex)) || PALETTES.some(p => isPaletteUnlocked(p,coachId) && p.colors.some(h => h.toLowerCase() === hex));\r\n/** The tint triad for a colour, hex or palette id, or undefined if it doesn't exist or isn't unlocked yet\r\n * (`preview` paints a locked one too — the editor's look-before-you-unlock layer, never saved). */\r\nexport function colorTriad(id: string, preview = false,coachId?:string): { primary: string; secondary: string; accent: string; paletteId?:string } | undefined {\r\n if (HEX.test(id)) { const hex = id.toLowerCase(); return preview || hexOwned(hex,coachId) ? { primary: hex, secondary: mix(hex, 0, .55), accent: mix(hex, 255, .55) } : undefined; }\r\n const c = findColor(id); if (c) return preview || isColorUnlocked(c,coachId) ? { primary: c.primary, secondary: c.secondary, accent: c.accent } : undefined;\r\n const p = findPalette(id); if (p) return preview || isPaletteUnlocked(p,coachId) ? triadFromPalette(p) : undefined;\r\n return undefined;\r\n}\r\n\r\n/** True when a texture/colour/palette id is a registry item the player doesn't own yet (the UI shows just a lock).\r\n * Installed creature skins and unknown ids guard themselves and are never \"locked\" here. */\r\nexport function isLocked(id: string,coachId?:string,kind?:'texture'|'color'): boolean {\r\n if(kind==='color'){\r\n  if(HEX.test(id))return !hexOwned(id.toLowerCase(),coachId);\r\n  const c=findColor(id);if(c)return !isColorUnlocked(c,coachId);\r\n  const p=findPalette(id);return p?!isPaletteUnlocked(p,coachId):false;\r\n }\r\n if(RETIRED_TEXTURE_IDS.includes(id))return true;\r\n const t = findTexture(id); if (t) return !isTextureUnlocked(t,coachId);\r\n if (HEX.test(id)) return !hexOwned(id.toLowerCase(),coachId);\r\n const c = findColor(id); if (c) return !isColorUnlocked(c,coachId);\r\n const p = findPalette(id); if (p) return !isPaletteUnlocked(p,coachId);\r\n return false;\r\n}\r\n\r\n// A saved recipe alone is not proof of a pack grant. Only migrate IDs that were\r\n// explicitly earned in the historical unlock store; all other non-free finishes\r\n// remain preview-only and fall back to Clay when rendered or saved.\r\nexport function migrateEarnedTextureUnlocks(recipe?: { body?:string;materials?: Record<string, { textureId?: string } | undefined> } | null) {\r\n try {\r\n  if (!recipe) return;\r\n  migrateSavedCosmetics(recipe);\r\n } catch { /* no storage or unreadable recipe: nothing to keep */ }\r\n}\r\ntry { const raw = localStorage.getItem(RECIPE_KEY); if (raw) migrateEarnedTextureUnlocks(JSON.parse(raw)); } catch { /* no storage */ }\r\nglobalThis.addEventListener?.('myr5:recipe', event => migrateEarnedTextureUnlocks((event as CustomEvent).detail));\r\n\r\n/**\r\n * The single seam between the old `styles[region]` numeric recipe and the new decoupled\r\n * texture+colour+sparkle+metallic recipe. Called once per region in assemble.ts.\r\n * No `choice` (old recipes, or a region nobody has re-customized) -> renders byte-for-byte\r\n * like today's STYLES[legacyIndex]. A `choice` resolves texture+colour independently and\r\n * always falls back to Flat/its default colour rather than ever throwing or loading nothing.\r\n * `preview` lets a locked texture/colour paint (editor preview only; save-look.ts guards saves).\r\n */\r\n/** G3: the material choice a region really renders with. Body colour drives body, arms, feet, collar and (R25)\r\n * the head; eyes have their own. An old save's separate head colour is ignored. */\r\nexport function regionChoice(materials: Partial<Record<Region, MaterialChoice>> | undefined, region: Region): MaterialChoice | undefined {\r\n const own = materials?.[region], source = materials?.[COLOUR_SOURCE[region]];\r\n return own && source && source !== own ? { ...own, colorId: source.colorId } : own;\r\n}\r\n\r\nexport function resolveRegionMaterial(legacyIndex: number, choice?: MaterialChoice, preview = false,coachId?:string) {\r\n if (!choice) return { ...LEGACY_STYLES[legacyIndex], sparkle: 0, paletteId:undefined as string|undefined };\r\n const texture = findTexture(choice.textureId);\r\n const safeTexture = texture && (preview || isTextureUnlocked(texture,coachId)) && texture.familyId >= 0 ? texture : CLAY_TEXTURE;\r\n const profile=builtinSurfaceProfile(safeTexture.familyId);\r\n const base = safeTexture.legacy ? LEGACY_STYLES[safeTexture.familyId] : safeTexture.id === 'clay' ? CLAY_BASE : profile ? {id:profile.id,name:profile.name,realm:'',primary:'#8b8f9a',secondary:'#4a4d55',accent:'#e7e9ee',emissive:'#000000',roughness:profile.roughness,metalness:profile.metalness,detail:profile.name.toLowerCase().replace(/[^a-z0-9]+/g,'-')} : CLAY_BASE;\r\n const triad = colorTriad(choice.colorId, preview,coachId) ?? colorTriad(safeTexture.defaultColorId, preview,coachId) ?? base;\r\n const metalness = Number.isFinite(choice.metallic) ? Math.max(0, Math.min(1, choice.metallic)) : base.metalness;\r\n const sparkle = Number.isFinite(choice.sparkle) ? Math.max(0, Math.min(1, choice.sparkle)) : 0;\r\n return { ...base, ...triad, paletteId:'paletteId' in triad?triad.paletteId:undefined, metalness, sparkle };\r\n}\r\n","creature/source/creator/palette-finishes.ts":"import * as THREE from 'three';\r\nimport palettesData from './palettes.json';\r\n\r\nexport type FinishKind =\r\n  | 'matte' | 'brushed-metal' | 'polished-metal' | 'glitter'\r\n  | 'pearl' | 'watercolor' | 'marbled' | 'glaze';\r\n\r\nexport const FINISH_ID: Record<FinishKind, number> = {\r\n  matte: 0,\r\n  'brushed-metal': 1,\r\n  'polished-metal': 2,\r\n  glitter: 3,\r\n  pearl: 4,\r\n  watercolor: 6,\r\n  marbled: 7,\r\n  glaze: 8,\r\n};\r\n\r\nconst KINDS = new Set<string>(Object.keys(FINISH_ID));\r\ntype PaletteMeta = { id: string; finish?: unknown; colors: string[] };\r\nconst CATALOG = new Map<string, PaletteMeta>(palettesData.map(p => [p.id,p]));\r\n\r\nfunction meta(id?: string): (PaletteMeta & { finish: FinishKind }) | undefined {\r\n  if (!id) return undefined;\r\n  const p = CATALOG.get(id);\r\n  return p && typeof p.finish === 'string' && KINDS.has(p.finish) ? p as PaletteMeta & {finish: FinishKind} : undefined;\r\n}\r\n\r\nexport function paletteFinish(paletteId?: string): FinishKind | undefined {\r\n  return meta(paletteId)?.finish;\r\n}\r\nexport function isCatalogPalette(paletteId?: string): boolean {\r\n  return !!meta(paletteId);\r\n}\r\n\r\nexport const FINISH_LABELS: Record<FinishKind, string> = {\r\n  matte: 'Matte', 'brushed-metal': 'Brushed metal', 'polished-metal': 'Polished metal',\r\n  glitter: 'Glitter', pearl: 'Pearl', watercolor: 'Watercolor', marbled: 'Marbled', glaze: 'Glaze',\r\n};\r\nexport function finishLabel(id?: string): string {\r\n  const f = paletteFinish(id);\r\n  return f ? FINISH_LABELS[f] : '';\r\n}\r\n\r\nexport function finishSwatchCSS(id?: string): string {\r\n  const p = meta(id); if (!p) return '';\r\n  const [a,b,c] = p.colors;\r\n  switch (p.finish) {\r\n    case 'matte': return '';\r\n    case 'brushed-metal': return `repeating-linear-gradient(100deg,${a} 0 2px,${b} 2px 3px,${c} 3px 5px)`;\r\n    case 'polished-metal': return `linear-gradient(115deg,${a},${c} 38%,#fff 50%,${b} 64%,${a})`;\r\n    case 'glitter': return `radial-gradient(circle at 22% 28%,#fff 0 2%,transparent 3%),radial-gradient(circle at 68% 62%,${c} 0 2%,transparent 3%),linear-gradient(135deg,${a},${b})`;\r\n    case 'pearl': return `linear-gradient(120deg,${a},#fff 36%,${b} 58%,${c})`;\r\n    case 'watercolor': return `radial-gradient(circle at 28% 34%,${a} 0 28%,transparent 55%),radial-gradient(circle at 72% 66%,${c} 0 24%,transparent 58%),linear-gradient(${b},${b})`;\r\n    case 'marbled': return `repeating-linear-gradient(128deg,${a} 0 9px,${b} 10px 17px,${c} 18px 23px)`;\r\n    case 'glaze': return `linear-gradient(160deg,rgba(255,255,255,.72),transparent 34%),linear-gradient(135deg,${a},${b},${c})`;\r\n  }\r\n}\r\n\r\nconst TAU = Math.PI * 2;\r\nconst wrap = (x: number) => x - Math.floor(x);\r\nfunction flatField(u: number, v: number): number {\r\n  u = wrap(u); v = wrap(v);\r\n  const s = 0.5 + 0.5 * Math.sin(TAU * u + 0.9 * Math.sin(TAU * v))\r\n    * Math.cos(TAU * v - 0.7 * Math.sin(TAU * u));\r\n  return wrap(s + 0.22 * Math.sin(TAU * (u - v)));\r\n}\r\nexport function samplePaletteTint(paletteId: string | undefined, tint: number, u: number, v: number, flat = false): number {\r\n  const f = paletteFinish(paletteId);\r\n  if (!f) return tint;\r\n  let t = THREE.MathUtils.clamp(tint, 0, 1);\r\n  u = wrap(u); v = wrap(v);\r\n  if (flat && f !== 'watercolor' && f !== 'marbled') return THREE.MathUtils.clamp(0.72 * flatField(u, v) + 0.28 * t, 0, 1);\r\n  if (f === 'watercolor') {\r\n    const bloom = 0.5 + 0.32 * Math.sin(TAU * u + 1.7 * Math.sin(TAU * v)) + 0.18 * Math.sin(TAU * (2 * u + v) + 0.6);\r\n    const grain = 0.03 * Math.sin(TAU * (9 * u + 5 * v));\r\n    return THREE.MathUtils.clamp(0.84 * bloom + 0.16 * t + grain, 0, 1);\r\n  }\r\n  if (f === 'marbled') {\r\n    const swirl = 0.5 + 0.48 * Math.sin(TAU * (u + 0.35 * Math.sin(TAU * (v + 0.5 * u))) + TAU * v);\r\n    return THREE.MathUtils.clamp(0.86 * swirl + 0.14 * t, 0, 1);\r\n  }\r\n  return t;\r\n}\r\n\r\nexport const FINISH_TINT_GLSL = `\r\nfloat myr5FlatPaletteTint(vec2 uv) {\r\n  uv = fract(uv);\r\n  float s = 0.5 + 0.5 * sin(6.2831853 * uv.x + 0.9 * sin(6.2831853 * uv.y))\r\n    * cos(6.2831853 * uv.y - 0.7 * sin(6.2831853 * uv.x));\r\n  return clamp(0.72 * fract(s + 0.22 * sin(6.2831853 * (uv.x - uv.y))) + 0.14, 0.0, 1.0);\r\n}\r\nfloat myr5FinishTint(float finish, float t, vec2 uv) {\r\n  t = clamp(t, 0.0, 1.0); uv = fract(uv);\r\n  if (finish == 6.0) {\r\n    float bloom = 0.5 + 0.32 * sin(6.2831853 * uv.x + 1.7 * sin(6.2831853 * uv.y))\r\n      + 0.18 * sin(6.2831853 * (2.0 * uv.x + uv.y) + 0.6);\r\n    float grain = 0.03 * sin(6.2831853 * (9.0 * uv.x + 5.0 * uv.y));\r\n    return clamp(0.84 * bloom + 0.16 * t + grain, 0.0, 1.0);\r\n  }\r\n  if (finish == 7.0) {\r\n    float swirl = 0.5 + 0.48 * sin(6.2831853 * (uv.x + 0.35 * sin(6.2831853 * (uv.y + 0.5 * uv.x))) + 6.2831853 * uv.y);\r\n    return clamp(0.86 * swirl + 0.14 * t, 0.0, 1.0);\r\n  }\r\n  return t;\r\n}\r\n`;\r\n\r\ntype Hooked = MeshPhysicalMaterialWithHooks;\r\ntype MeshPhysicalMaterialWithHooks = THREE.MeshPhysicalMaterial & {\r\n  userData: Record<string, any>;\r\n  defines?: Record<string,string>;\r\n};\r\n\r\nfunction addHook(mat: Hooked, kind: 'glitter' | 'brushed-metal'): void {\r\n  mat.userData.__myr5FinishHooks ||= {};\r\n  const hooks = mat.userData.__myr5FinishHooks as Record<string, boolean>;\r\n  if (hooks[kind]) return;\r\n  hooks[kind] = true;\r\n  if (kind === 'glitter') mat.userData.myr5FinishGlitter = true;\r\n  mat.defines={...mat.defines,USE_UV:''};\r\n  const prev = mat.onBeforeCompile?.bind(mat);\r\n  const vary = kind === 'glitter' ? 'vMyr5GlitterUv' : 'vMyr5BrushedUv';\r\n  mat.onBeforeCompile = (shader, renderer) => {\r\n    prev?.(shader, renderer);\r\n    shader.vertexShader = shader.vertexShader\r\n      .replace('#include <common>', `#include <common>\\nvarying vec2 ${vary};`)\r\n      .replace('#include <uv_vertex>', `#include <uv_vertex>\\n${vary} = uv;`);\r\n    shader.fragmentShader = shader.fragmentShader.replace(\r\n      '#include <common>',\r\n      `#include <common>\\nvarying vec2 ${vary};`\r\n    );\r\n    if (kind === 'glitter') {\r\n      shader.fragmentShader = shader.fragmentShader.replace(\r\n        '#include <roughnessmap_fragment>',\r\n        `#include <roughnessmap_fragment>\r\n        {\r\n          vec2 gu = fract(${vary} * 83.0);\r\n          float g = fract(sin(dot(floor(${vary} * 83.0), vec2(12.9898, 78.233))) * 43758.5453);\r\n          float speck = step(0.92, g) * step(0.5, smoothstep(0.0, 0.08, min(gu.x, gu.y)));\r\n          roughnessFactor = mix(roughnessFactor,0.055,speck);\r\n        }`\r\n      );\r\n      shader.fragmentShader = shader.fragmentShader.replace('#include <dithering_fragment>', `{\r\n        float grain=fract(sin(dot(floor(vMyr5GlitterUv*83.0),vec2(12.9898,78.233)))*43758.5453);\r\n        vec3 flakeNormal=normalize(normal+vec3(sin(grain*91.0),cos(grain*73.0),0.0)*0.55);\r\n        float glint=step(0.92,grain)*pow(max(dot(flakeNormal,normalize(vViewPosition)),0.0),42.0);\r\n        gl_FragColor.rgb+=glint*1.8;\r\n      }\\n#include <dithering_fragment>`);\r\n    } else {\r\n      shader.fragmentShader = shader.fragmentShader.replace(\r\n        '#include <roughnessmap_fragment>',\r\n        `#include <roughnessmap_fragment>\r\n        roughnessFactor = clamp(roughnessFactor + 0.055 * sin(6.2831853 * (fract(${vary}.y) * 90.0 + 0.15 * sin(6.2831853 * ${vary}.x))), 0.05, 1.0);`\r\n      );\r\n    }\r\n  };\r\n  const prevKey = mat.customProgramCacheKey?.bind(mat);\r\n  mat.customProgramCacheKey = function () {\r\n    return `${prevKey ? prevKey.call(this) : ''}|myr5-${kind}`;\r\n  };\r\n  mat.needsUpdate = true;\r\n}\r\n\r\nexport function applyPaletteFinish(\r\n  mat: THREE.MeshPhysicalMaterial,\r\n  paletteId?: string,\r\n  palette?: { primary: string; secondary: string; accent: string },\r\n): void {\r\n  const p = meta(paletteId);\r\n  if (!p) return;\r\n  const f = p.finish;\r\n  mat.roughnessMap = null;\r\n  mat.clearcoat = 0; mat.clearcoatRoughness = 0;\r\n  mat.sheen = 0; (mat as any).sheenRoughness = 1; (mat as any).sheenColor?.set?.(0x000000);\r\n  mat.iridescence = 0; mat.iridescenceIOR = 1.3; mat.iridescenceThicknessRange = [100, 400];\r\n  (mat as any).anisotropy = 0;\r\n  mat.transmission = 0; mat.thickness = 0; mat.ior = 1.5;\r\n  switch (f) {\r\n    case 'matte': mat.roughness = 0.92; mat.metalness = 0; break;\r\n    case 'brushed-metal': mat.roughness = 0.44; mat.metalness = 0.82; (mat as any).anisotropy = 0.65; break;\r\n    case 'polished-metal': mat.roughness = 0.16; mat.metalness = 0.92; break;\r\n    case 'glitter': mat.roughness = 0.24; mat.metalness = 0.35; mat.clearcoat=1; mat.clearcoatRoughness=.035; mat.envMapIntensity=1.5; break;\r\n    case 'pearl':\r\n      mat.roughness = 0.3; mat.metalness = 0.08; mat.iridescence = 1;\r\n      mat.iridescenceIOR = 1.35; mat.iridescenceThicknessRange = [120, 420]; break;\r\n    case 'watercolor': mat.roughness = 0.96; mat.metalness = 0; break;\r\n    case 'marbled': mat.roughness = 0.35; mat.metalness = 0.05; break;\r\n    case 'glaze': mat.roughness = 0.24; mat.metalness = 0; mat.clearcoat = 1; mat.clearcoatRoughness = 0.08; break;\r\n  }\r\n  if (f === 'glitter') addHook(mat as Hooked, 'glitter');\r\n  if (f === 'brushed-metal') addHook(mat as Hooked, 'brushed-metal');\r\n  mat.needsUpdate = true;\r\n}\r\n","creature/source/creator/palettes.json":"[\r\n  {\r\n    \"id\": \"pal-01\",\r\n    \"name\": \"Acid Forge\",\r\n    \"tagline\": \"Lime bites oxblood and chrome\",\r\n    \"colors\": [\r\n      \"#C6FF00\",\r\n      \"#4A0E17\",\r\n      \"#C0C7CE\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 5,\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-02\",\r\n    \"name\": \"Neon Afterburn Haze\",\r\n    \"tagline\": \"Orange aqua contrails with violet flare\",\r\n    \"colors\": [\r\n      \"#FF6D00\",\r\n      \"#00E0B0\",\r\n      \"#C800FF\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 10,\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-03\",\r\n    \"name\": \"Petrel Copper\",\r\n    \"tagline\": \"Petrol depths meet intense violet\",\r\n    \"colors\": [\r\n      \"#0E5A61\",\r\n      \"#B87333\",\r\n      \"#6913E7\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 15,\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-04\",\r\n    \"name\": \"Vinaigrette Rust\",\r\n    \"tagline\": \"Oil pigment rust swirl\",\r\n    \"colors\": [\r\n      \"#8A3B12\",\r\n      \"#556B2F\",\r\n      \"#E7A213\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 20,\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-05\",\r\n    \"name\": \"Neon Relic\",\r\n    \"tagline\": \"Hot magenta neon amber on bone\",\r\n    \"colors\": [\r\n      \"#FF009F\",\r\n      \"#FFB500\",\r\n      \"#F2EFE6\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 25,\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-06\",\r\n    \"name\": \"Glacier Ember\",\r\n    \"tagline\": \"Ice teal over ember red\",\r\n    \"colors\": [\r\n      \"#13E7D1\",\r\n      \"#D9381E\",\r\n      \"#2B2530\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 30,\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-07\",\r\n    \"name\": \"Chartreuse Storm\",\r\n    \"tagline\": \"Sludge green meets slate violet\",\r\n    \"colors\": [\r\n      \"#B5C700\",\r\n      \"#3D3654\",\r\n      \"#E85D75\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 35,\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-08\",\r\n    \"name\": \"Saffron Voltage\",\r\n    \"tagline\": \"Saffron arcs on ink blue\",\r\n    \"colors\": [\r\n      \"#FFB000\",\r\n      \"#101D42\",\r\n      \"#00FAA6\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 40,\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-09\",\r\n    \"name\": \"Neon Venom Signal\",\r\n    \"tagline\": \"Poison lime warning under arterial red\",\r\n    \"colors\": [\r\n      \"#A6FF00\",\r\n      \"#FF2442\",\r\n      \"#101828\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 45,\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-10\",\r\n    \"name\": \"Mercury Moss\",\r\n    \"tagline\": \"Silver moss over saturated gold\",\r\n    \"colors\": [\r\n      \"#AEB6BC\",\r\n      \"#5C6B3C\",\r\n      \"#E7A913\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 50,\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-11\",\r\n    \"name\": \"Ultraviolet Tar\",\r\n    \"tagline\": \"Violet glow on tar black\",\r\n    \"colors\": [\r\n      \"#7B2FF7\",\r\n      \"#141414\",\r\n      \"#FFC93C\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 55,\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-12\",\r\n    \"name\": \"Salmon Circuit\",\r\n    \"tagline\": \"Salmon pink on circuit green\",\r\n    \"colors\": [\r\n      \"#FA2200\",\r\n      \"#00A86B\",\r\n      \"#2E2A5C\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 60,\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-13\",\r\n    \"name\": \"Muddy Pop\",\r\n    \"tagline\": \"Ochre mud sparks cerulean\",\r\n    \"colors\": [\r\n      \"#8C6A2F\",\r\n      \"#0081C8\",\r\n      \"#E71362\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-14\",\r\n    \"name\": \"Neon Vapor Crown\",\r\n    \"tagline\": \"Laser pink blue over dark purple\",\r\n    \"colors\": [\r\n      \"#FF00AA\",\r\n      \"#00D0FF\",\r\n      \"#1B0033\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-15\",\r\n    \"name\": \"Brick Vein\",\r\n    \"tagline\": \"Brick red veined in steel\",\r\n    \"colors\": [\r\n      \"#A0342B\",\r\n      \"#8C9BA5\",\r\n      \"#D7E713\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-16\",\r\n    \"name\": \"Peacock Static\",\r\n    \"tagline\": \"Peacock green hums gold\",\r\n    \"colors\": [\r\n      \"#046A5B\",\r\n      \"#FAAF00\",\r\n      \"#5B1E4B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-17\",\r\n    \"name\": \"Foghorn Flame\",\r\n    \"tagline\": \"Fog gray with fire-orange siren\",\r\n    \"colors\": [\r\n      \"#B8C0C2\",\r\n      \"#FA3F00\",\r\n      \"#123C69\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-18\",\r\n    \"name\": \"Toxic Honey\",\r\n    \"tagline\": \"Honey drips on toxic lime\",\r\n    \"colors\": [\r\n      \"#D9A404\",\r\n      \"#9BF00B\",\r\n      \"#4A154B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-19\",\r\n    \"name\": \"Neon Crimson Mirage\",\r\n    \"tagline\": \"Scarlet toxic lime over deep jade\",\r\n    \"colors\": [\r\n      \"#FF1744\",\r\n      \"#76FF03\",\r\n      \"#005E3C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-20\",\r\n    \"name\": \"Opal Drain\",\r\n    \"tagline\": \"Opal sheen on drainpipe green\",\r\n    \"colors\": [\r\n      \"#13E7B9\",\r\n      \"#3E5641\",\r\n      \"#D1495B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-21\",\r\n    \"name\": \"Rust Halo\",\r\n    \"tagline\": \"Rust ringed by mint flash\",\r\n    \"colors\": [\r\n      \"#9C4221\",\r\n      \"#12E898\",\r\n      \"#2C2C54\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-22\",\r\n    \"name\": \"Volt Quartz\",\r\n    \"tagline\": \"Electric yellow on rose quartz\",\r\n    \"colors\": [\r\n      \"#F5E900\",\r\n      \"#E71363\",\r\n      \"#264653\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-23\",\r\n    \"name\": \"Neon Flare Garden\",\r\n    \"tagline\": \"Laser lime pink on teal steel\",\r\n    \"colors\": [\r\n      \"#8CFF00\",\r\n      \"#FF006E\",\r\n      \"#00B8A9\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-24\",\r\n    \"name\": \"Crimson Tidebreak\",\r\n    \"tagline\": \"Crimson breaks on seafoam\",\r\n    \"colors\": [\r\n      \"#B91338\",\r\n      \"#13E7B5\",\r\n      \"#2A1E5C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-25\",\r\n    \"name\": \"Brass Mirage\",\r\n    \"tagline\": \"Brass shimmer dusky plum\",\r\n    \"colors\": [\r\n      \"#C59B42\",\r\n      \"#5A2A52\",\r\n      \"#0D94ED\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-26\",\r\n    \"name\": \"Neon Infrared Bloom\",\r\n    \"tagline\": \"Red heat sliced by frozen cyan\",\r\n    \"colors\": [\r\n      \"#FF0000\",\r\n      \"#00F5FF\",\r\n      \"#5B00B5\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-27\",\r\n    \"name\": \"Ice Magma\",\r\n    \"tagline\": \"Pigmented ice over magma orange\",\r\n    \"colors\": [\r\n      \"#D6F0F0\",\r\n      \"#F45B0F\",\r\n      \"#3D2C8D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-28\",\r\n    \"name\": \"Fern Radio\",\r\n    \"tagline\": \"Fern green static magenta\",\r\n    \"colors\": [\r\n      \"#3F7A3A\",\r\n      \"#E4007C\",\r\n      \"#E7BE13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-29\",\r\n    \"name\": \"Porcelain Fuel\",\r\n    \"tagline\": \"Porcelain white fuel orange\",\r\n    \"colors\": [\r\n      \"#F2F2F0\",\r\n      \"#FF7A1A\",\r\n      \"#14507A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-30\",\r\n    \"name\": \"Neon Photon Alley\",\r\n    \"tagline\": \"Yellow signage aqua and violet glass\",\r\n    \"colors\": [\r\n      \"#FFD300\",\r\n      \"#00FFCC\",\r\n      \"#7A00FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-31\",\r\n    \"name\": \"Neon Solar Flare\",\r\n    \"tagline\": \"Hot yellow ember in plum space\",\r\n    \"colors\": [\r\n      \"#FFEA00\",\r\n      \"#FF3D00\",\r\n      \"#26142B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-32\",\r\n    \"name\": \"Wet Cement Rose\",\r\n    \"tagline\": \"Cement gray wild rose\",\r\n    \"colors\": [\r\n      \"#8D9096\",\r\n      \"#D84A87\",\r\n      \"#F0E130\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-33\",\r\n    \"name\": \"Neon Laser Tropics\",\r\n    \"tagline\": \"Chartreuse beams with scorched orange shadows\",\r\n    \"colors\": [\r\n      \"#C6FF00\",\r\n      \"#FF4D00\",\r\n      \"#001219\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-34\",\r\n    \"name\": \"Copper Fog\",\r\n    \"tagline\": \"Copper glows through fog\",\r\n    \"colors\": [\r\n      \"#B46A30\",\r\n      \"#C4CBCE\",\r\n      \"#B600F5\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L1\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-35\",\r\n    \"name\": \"Laser Moss\",\r\n    \"tagline\": \"Laser cyan on moss\",\r\n    \"colors\": [\r\n      \"#00E5FF\",\r\n      \"#546B2F\",\r\n      \"#FF4FA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L3\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-36\",\r\n    \"name\": \"Bone Ember\",\r\n    \"tagline\": \"Bone white ember orange\",\r\n    \"colors\": [\r\n      \"#EDE8DC\",\r\n      \"#E4572E\",\r\n      \"#BA00FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-37\",\r\n    \"name\": \"Plum Static\",\r\n    \"tagline\": \"Plum noise with electric lime\",\r\n    \"colors\": [\r\n      \"#4A1E4F\",\r\n      \"#B8F000\",\r\n      \"#33B5C7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-38\",\r\n    \"name\": \"Neon Lime Vortex\",\r\n    \"tagline\": \"Acid green swirling through electric blue\",\r\n    \"colors\": [\r\n      \"#BFFF00\",\r\n      \"#00E5FF\",\r\n      \"#12100F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-39\",\r\n    \"name\": \"Clay Command\",\r\n    \"tagline\": \"Terracotta clay cobalt order\",\r\n    \"colors\": [\r\n      \"#C06B55\",\r\n      \"#2E4BC6\",\r\n      \"#EDE6D6\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-40\",\r\n    \"name\": \"Static Cream\",\r\n    \"tagline\": \"Cream noise shock green\",\r\n    \"colors\": [\r\n      \"#F4EDDE\",\r\n      \"#39FF14\",\r\n      \"#4A306D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-41\",\r\n    \"name\": \"Midnight Papaya\",\r\n    \"tagline\": \"Midnight blue papaya burst\",\r\n    \"colors\": [\r\n      \"#191970\",\r\n      \"#FF9E42\",\r\n      \"#13E796\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-42\",\r\n    \"name\": \"Dusty Laser\",\r\n    \"tagline\": \"Dusty mauve laser green\",\r\n    \"colors\": [\r\n      \"#A27E8E\",\r\n      \"#7CFC00\",\r\n      \"#1B3B6F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-43\",\r\n    \"name\": \"Neon Atomic Sunset\",\r\n    \"tagline\": \"Amber fallout glowing beside ultraviolet pink\",\r\n    \"colors\": [\r\n      \"#FF9100\",\r\n      \"#FF00F5\",\r\n      \"#062A2A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-44\",\r\n    \"name\": \"Fern Flame\",\r\n    \"tagline\": \"Cool fern fierce flame\",\r\n    \"colors\": [\r\n      \"#4F7942\",\r\n      \"#FF3D2E\",\r\n      \"#E75A13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-45\",\r\n    \"name\": \"Violet Quarry\",\r\n    \"tagline\": \"Quarry gray violet eruption\",\r\n    \"colors\": [\r\n      \"#7D7F87\",\r\n      \"#8B3DFF\",\r\n      \"#DCE35B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-46\",\r\n    \"name\": \"Cadmium Deep\",\r\n    \"tagline\": \"Cadmium red deep lagoon\",\r\n    \"colors\": [\r\n      \"#D32E12\",\r\n      \"#046E8F\",\r\n      \"#E79E13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-47\",\r\n    \"name\": \"Pigment Riot\",\r\n    \"tagline\": \"Intense blue maroon and hot orange\",\r\n    \"colors\": [\r\n      \"#139FE7\",\r\n      \"#6E1423\",\r\n      \"#FF7300\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-48\",\r\n    \"name\": \"Neon Prism Riot\",\r\n    \"tagline\": \"Sea-glass cyan, magenta bite, lemon flash\",\r\n    \"colors\": [\r\n      \"#00FFC6\",\r\n      \"#FF00B8\",\r\n      \"#F5FF00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-49\",\r\n    \"name\": \"Neon Tropic Surge\",\r\n    \"tagline\": \"Aqua tide, citrus flare, violet thunder\",\r\n    \"colors\": [\r\n      \"#00FF9D\",\r\n      \"#FFB000\",\r\n      \"#D000FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-50\",\r\n    \"name\": \"Ash Honey\",\r\n    \"tagline\": \"Ash gray golden honey\",\r\n    \"colors\": [\r\n      \"#9BA0A6\",\r\n      \"#E0A100\",\r\n      \"#2F6690\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-51\",\r\n    \"name\": \"Cherry Motor\",\r\n    \"tagline\": \"Cherry motor oil with electric blue\",\r\n    \"colors\": [\r\n      \"#B22234\",\r\n      \"#3B2F2F\",\r\n      \"#005CFF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-52\",\r\n    \"name\": \"Glacier Punch\",\r\n    \"tagline\": \"Glacier pigmented hot punch\",\r\n    \"colors\": [\r\n      \"#13A6E7\",\r\n      \"#F52549\",\r\n      \"#3D405B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-53\",\r\n    \"name\": \"Tarnish Bloom\",\r\n    \"tagline\": \"Tarnished teal floral orange\",\r\n    \"colors\": [\r\n      \"#3A7D7B\",\r\n      \"#F4852B\",\r\n      \"#E9D6EC\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L3\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-54\",\r\n    \"name\": \"Umber Signal\",\r\n    \"tagline\": \"Umber earth neon signal\",\r\n    \"colors\": [\r\n      \"#635147\",\r\n      \"#D0FF4B\",\r\n      \"#7A28C0\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-55\",\r\n    \"name\": \"Rose Quartz Coil\",\r\n    \"tagline\": \"Soft rose coil of teal\",\r\n    \"colors\": [\r\n      \"#E7134B\",\r\n      \"#0FA3A3\",\r\n      \"#2B2118\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-56\",\r\n    \"name\": \"Sulfur Deck\",\r\n    \"tagline\": \"Sulfur yellow blue and hot orange\",\r\n    \"colors\": [\r\n      \"#E3D200\",\r\n      \"#274C77\",\r\n      \"#FF4A00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-57\",\r\n    \"name\": \"Neon Ember Static\",\r\n    \"tagline\": \"Orange yellow buzzing with royal voltage\",\r\n    \"colors\": [\r\n      \"#FF5E00\",\r\n      \"#FFE600\",\r\n      \"#3A0CA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-58\",\r\n    \"name\": \"Mango Ash\",\r\n    \"tagline\": \"Mango bright volcanic ash\",\r\n    \"colors\": [\r\n      \"#FFA12B\",\r\n      \"#59595E\",\r\n      \"#64DDBB\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-59\",\r\n    \"name\": \"Steel Petal\",\r\n    \"tagline\": \"Steel gray crushed petals\",\r\n    \"colors\": [\r\n      \"#8A8F98\",\r\n      \"#E71813\",\r\n      \"#D8E26E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-60\",\r\n    \"name\": \"Ninja Cream\",\r\n    \"tagline\": \"Hidden navy cream coral\",\r\n    \"colors\": [\r\n      \"#1E2A4A\",\r\n      \"#F0EBE0\",\r\n      \"#7EEC00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-61\",\r\n    \"name\": \"Tangerine Static\",\r\n    \"tagline\": \"Tangerine buzz grape shadow\",\r\n    \"colors\": [\r\n      \"#F28500\",\r\n      \"#3D2159\",\r\n      \"#202020\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-62\",\r\n    \"name\": \"Sage Pistol\",\r\n    \"tagline\": \"Muted sage pistol pink\",\r\n    \"colors\": [\r\n      \"#9AB5A1\",\r\n      \"#E45C98\",\r\n      \"#33272A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-63\",\r\n    \"name\": \"Lagoon Battery\",\r\n    \"tagline\": \"Lagoon green charged yellow\",\r\n    \"colors\": [\r\n      \"#1D8A7A\",\r\n      \"#FAE000\",\r\n      \"#76305B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L4\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-64\",\r\n    \"name\": \"Wine Cooling\",\r\n    \"tagline\": \"Warm wine cooled by mint\",\r\n    \"colors\": [\r\n      \"#701F3C\",\r\n      \"#13E78C\",\r\n      \"#E8D44D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-65\",\r\n    \"name\": \"Charcoal Tulip\",\r\n    \"tagline\": \"Charcoal tulip flame combo\",\r\n    \"colors\": [\r\n      \"#2E3033\",\r\n      \"#FF4E50\",\r\n      \"#CA00C7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-66\",\r\n    \"name\": \"Neon Acid Harbor\",\r\n    \"tagline\": \"Lime light cutting across magenta docks\",\r\n    \"colors\": [\r\n      \"#39FF14\",\r\n      \"#FF007F\",\r\n      \"#001B2E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-67\",\r\n    \"name\": \"Rustic Neon\",\r\n    \"tagline\": \"Barn red barn lime buzz\",\r\n    \"colors\": [\r\n      \"#8A3324\",\r\n      \"#C1FF33\",\r\n      \"#2D4A6B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L1\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-68\",\r\n    \"name\": \"Alice Warning\",\r\n    \"tagline\": \"Alice blue caution orange\",\r\n    \"colors\": [\r\n      \"#1399E7\",\r\n      \"#FF7E1F\",\r\n      \"#EEF700\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-69\",\r\n    \"name\": \"Smog Candy\",\r\n    \"tagline\": \"Smog beige candy teal\",\r\n    \"colors\": [\r\n      \"#E79813\",\r\n      \"#00C2A8\",\r\n      \"#D8395B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-70\",\r\n    \"name\": \"Violet Furnace\",\r\n    \"tagline\": \"Furnace violet cooled sky\",\r\n    \"colors\": [\r\n      \"#6A2D9E\",\r\n      \"#F57C3B\",\r\n      \"#302012\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-71\",\r\n    \"name\": \"Khaki Storm\",\r\n    \"tagline\": \"Khaki field storm magenta\",\r\n    \"colors\": [\r\n      \"#A89B6E\",\r\n      \"#E0218A\",\r\n      \"#2A6F97\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-72\",\r\n    \"name\": \"Neon Circuit Surge\",\r\n    \"tagline\": \"Turquoise sparks over midnight violet\",\r\n    \"colors\": [\r\n      \"#00F5D4\",\r\n      \"#F72585\",\r\n      \"#0B0321\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L4\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-73\",\r\n    \"name\": \"Cedar Ultraviolet\",\r\n    \"tagline\": \"Cedar wood ultraviolet light\",\r\n    \"colors\": [\r\n      \"#7A4532\",\r\n      \"#8246FF\",\r\n      \"#E9E4D8\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-74\",\r\n    \"name\": \"Frosted Chili\",\r\n    \"tagline\": \"Frost blue chili red\",\r\n    \"colors\": [\r\n      \"#13AAE7\",\r\n      \"#C13A2F\",\r\n      \"#4C5B2C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-75\",\r\n    \"name\": \"Moss Rocket\",\r\n    \"tagline\": \"Moss green rocket pink\",\r\n    \"colors\": [\r\n      \"#5B7042\",\r\n      \"#FA0057\",\r\n      \"#061F5B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-76\",\r\n    \"name\": \"Pewter Punch\",\r\n    \"tagline\": \"Pewter softened hot punch\",\r\n    \"colors\": [\r\n      \"#99A0A8\",\r\n      \"#FF3D7F\",\r\n      \"#164D37\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L1\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-77\",\r\n    \"name\": \"Neon Glacier Riot\",\r\n    \"tagline\": \"Cyan hot pink and radioactive lime\",\r\n    \"colors\": [\r\n      \"#00F0FF\",\r\n      \"#FF00E5\",\r\n      \"#CCFF00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-78\",\r\n    \"name\": \"Cola Lightning\",\r\n    \"tagline\": \"Cola brown lightning yellow\",\r\n    \"colors\": [\r\n      \"#3A2618\",\r\n      \"#F7FF00\",\r\n      \"#FA005E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-79\",\r\n    \"name\": \"Periwinkle Fuel\",\r\n    \"tagline\": \"Soft periwinkle diesel orange\",\r\n    \"colors\": [\r\n      \"#1325E7\",\r\n      \"#E0641E\",\r\n      \"#2F4730\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-80\",\r\n    \"name\": \"Jade Alarm\",\r\n    \"tagline\": \"Jade calm siren red\",\r\n    \"colors\": [\r\n      \"#57A78A\",\r\n      \"#D91E36\",\r\n      \"#E7AF13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-81\",\r\n    \"name\": \"Neon Ultraviolet Pulse\",\r\n    \"tagline\": \"Violet beams with mint-charged current\",\r\n    \"colors\": [\r\n      \"#8C00FF\",\r\n      \"#00FFB2\",\r\n      \"#160021\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-82\",\r\n    \"name\": \"Neon Nightcaller\",\r\n    \"tagline\": \"Cyan violet flame in black-green fog\",\r\n    \"colors\": [\r\n      \"#00FFFF\",\r\n      \"#9400FF\",\r\n      \"#131A00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L1\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-83\",\r\n    \"name\": \"Ivory Shock\",\r\n    \"tagline\": \"Ivory calm violet shock\",\r\n    \"colors\": [\r\n      \"#F0EEE4\",\r\n      \"#7C1FD3\",\r\n      \"#FFB200\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-84\",\r\n    \"name\": \"Neon Ruby Arc\",\r\n    \"tagline\": \"Red-hot signal over green afterglow\",\r\n    \"colors\": [\r\n      \"#FF073A\",\r\n      \"#00FF87\",\r\n      \"#080F1B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L4\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-85\",\r\n    \"name\": \"Maroon Cloud\",\r\n    \"tagline\": \"Maroon heavy cloud blue\",\r\n    \"colors\": [\r\n      \"#6D1E2E\",\r\n      \"#1381E7\",\r\n      \"#E5D352\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L1\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-86\",\r\n    \"name\": \"Chrome Berry\",\r\n    \"tagline\": \"Chrome band over berry\",\r\n    \"colors\": [\r\n      \"#C3C9D1\",\r\n      \"#8E2A5C\",\r\n      \"#FFB347\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-87\",\r\n    \"name\": \"Lentil Neon\",\r\n    \"tagline\": \"Lentil tan electric aqua\",\r\n    \"colors\": [\r\n      \"#B09A6E\",\r\n      \"#00FFF0\",\r\n      \"#5F1B63\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-88\",\r\n    \"name\": \"Neon Cobalt Fuse\",\r\n    \"tagline\": \"Blue ignition wired to blazing yellow\",\r\n    \"colors\": [\r\n      \"#004DFF\",\r\n      \"#FFEE00\",\r\n      \"#0D0221\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-89\",\r\n    \"name\": \"Spruce Bonfire\",\r\n    \"tagline\": \"Dark spruce bonfire orange\",\r\n    \"colors\": [\r\n      \"#23493C\",\r\n      \"#FF7A3D\",\r\n      \"#0C1735\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-90\",\r\n    \"name\": \"Lilac Foundry\",\r\n    \"tagline\": \"Lilac meets foundry steel\",\r\n    \"colors\": [\r\n      \"#9813E7\",\r\n      \"#7B8894\",\r\n      \"#D4522A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-91\",\r\n    \"name\": \"Topaz Battle\",\r\n    \"tagline\": \"Topaz gold navy clash\",\r\n    \"colors\": [\r\n      \"#F0B429\",\r\n      \"#182952\",\r\n      \"#FA007D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-92\",\r\n    \"name\": \"Dusk Celery\",\r\n    \"tagline\": \"Dusky purple celery snap\",\r\n    \"colors\": [\r\n      \"#574B6B\",\r\n      \"#C8D86A\",\r\n      \"#FA2100\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-93\",\r\n    \"name\": \"Tide Copperfuse\",\r\n    \"tagline\": \"Tidal teal copperfuse glow\",\r\n    \"colors\": [\r\n      \"#0F7B7B\",\r\n      \"#C07840\",\r\n      \"#C2F000\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-94\",\r\n    \"name\": \"Punch Driftwood\",\r\n    \"tagline\": \"Fruit punch on driftwood\",\r\n    \"colors\": [\r\n      \"#E63362\",\r\n      \"#6E5B4A\",\r\n      \"#1648EA\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L1\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-95\",\r\n    \"name\": \"Ink Lime\",\r\n    \"tagline\": \"Ink smudge streaked lime\",\r\n    \"colors\": [\r\n      \"#23232A\",\r\n      \"#A8E10C\",\r\n      \"#7E10EA\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-96\",\r\n    \"name\": \"Porcelain Prism\",\r\n    \"tagline\": \"Porcelain violet over tidal blue\",\r\n    \"colors\": [\r\n      \"#F5F1E8\",\r\n      \"#BD05DA\",\r\n      \"#005F73\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-97\",\r\n    \"name\": \"Wasabi Cloud\",\r\n    \"tagline\": \"Wasabi heat cloud gray\",\r\n    \"colors\": [\r\n      \"#9FBF3B\",\r\n      \"#8E94A0\",\r\n      \"#E71363\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-98\",\r\n    \"name\": \"Eggplant Flare\",\r\n    \"tagline\": \"Eggplant deep flare yellow\",\r\n    \"colors\": [\r\n      \"#41284E\",\r\n      \"#FFD23F\",\r\n      \"#5BC0BE\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L3\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-99\",\r\n    \"name\": \"Stone Rose\",\r\n    \"tagline\": \"Stone gray antique rose\",\r\n    \"colors\": [\r\n      \"#8F8A82\",\r\n      \"#C46E8E\",\r\n      \"#2F6DA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-100\",\r\n    \"name\": \"Neon Quasar Pop\",\r\n    \"tagline\": \"Yellow green comet over indigo gravity\",\r\n    \"colors\": [\r\n      \"#FFEF00\",\r\n      \"#00FF3C\",\r\n      \"#2D02B8\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-101\",\r\n    \"name\": \"Arctic Rust\",\r\n    \"tagline\": \"Arctic white rusted orange\",\r\n    \"colors\": [\r\n      \"#E9F2F5\",\r\n      \"#9E4A1D\",\r\n      \"#3E3F8F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-102\",\r\n    \"name\": \"Magenta Selvage\",\r\n    \"tagline\": \"Magenta edge denim blue\",\r\n    \"colors\": [\r\n      \"#D8117E\",\r\n      \"#3B5B8C\",\r\n      \"#E7D313\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-103\",\r\n    \"name\": \"Haze Saffron\",\r\n    \"tagline\": \"Purple haze saffron spark\",\r\n    \"colors\": [\r\n      \"#8467B8\",\r\n      \"#F4A81D\",\r\n      \"#1D4E50\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-104\",\r\n    \"name\": \"Neon Cyber Reed\",\r\n    \"tagline\": \"Green circuitry humming beside lemon static\",\r\n    \"colors\": [\r\n      \"#00FF66\",\r\n      \"#FFEA00\",\r\n      \"#4B0082\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L2\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-105\",\r\n    \"name\": \"Neon Magma Bloom\",\r\n    \"tagline\": \"Molten orange meeting synthetic magenta\",\r\n    \"colors\": [\r\n      \"#FF2E00\",\r\n      \"#F000FF\",\r\n      \"#0A0A0F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-106\",\r\n    \"name\": \"Dune Violet\",\r\n    \"tagline\": \"Warm dune electric violet\",\r\n    \"colors\": [\r\n      \"#C9AE7E\",\r\n      \"#7D2BF0\",\r\n      \"#13E7E3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-107\",\r\n    \"name\": \"Ember Nightshade\",\r\n    \"tagline\": \"Violet shadows ember teal\",\r\n    \"colors\": [\r\n      \"#523090\",\r\n      \"#EF4E23\",\r\n      \"#0C5D68\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L5\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"opal-jelly\",\r\n    \"name\": \"Opal Jelly\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#b3fff0\",\r\n      \"#9767ff\",\r\n      \"#fff1ff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"bubble-glass\",\r\n    \"name\": \"Bubble Glass\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#58eaff\",\r\n      \"#b8fff8\",\r\n      \"#ffffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"prism-crystal\",\r\n    \"name\": \"Prism Crystal\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#b18aff\",\r\n      \"#447bff\",\r\n      \"#fff2ff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"holo-foil\",\r\n    \"name\": \"Holographic Foil\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#ff54de\",\r\n      \"#39e5ff\",\r\n      \"#fff56b\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"glitter-resin\",\r\n    \"name\": \"Glitter Resin\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#ff319c\",\r\n      \"#7025ff\",\r\n      \"#fff6db\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"galaxy-geode\",\r\n    \"name\": \"Galaxy Geode\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#21134e\",\r\n      \"#7b39e8\",\r\n      \"#63ffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"caustic-slime\",\r\n    \"name\": \"Caustic Slime\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#8aa52b\",\r\n      \"#20300b\",\r\n      \"#dcff57\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"blister-hide\",\r\n    \"name\": \"Blister Hide\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#9e435f\",\r\n      \"#2d1727\",\r\n      \"#e7bb72\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"rotten-rind\",\r\n    \"name\": \"Rotten Rind\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#626124\",\r\n      \"#211916\",\r\n      \"#a2b16b\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"parasite-nest\",\r\n    \"name\": \"Parasite Nest\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#786541\",\r\n      \"#151c14\",\r\n      \"#ece2b0\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"exposed-sinew\",\r\n    \"name\": \"Exposed Sinew\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#b72945\",\r\n      \"#300b24\",\r\n      \"#efb5a1\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"abyssal-maw\",\r\n    \"name\": \"Abyssal Maw\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#522045\",\r\n      \"#100d19\",\r\n      \"#d9e6a7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"circuit-alloy\",\r\n    \"name\": \"Circuit Alloy\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#273f50\",\r\n      \"#0a111c\",\r\n      \"#4effdc\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"servo-armor\",\r\n    \"name\": \"Servo Armor\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#617080\",\r\n      \"#141b25\",\r\n      \"#ff6739\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"chrome-rib\",\r\n    \"name\": \"Chrome Rib\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#bfcde1\",\r\n      \"#202f46\",\r\n      \"#ffffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"carbon-mech\",\r\n    \"name\": \"Carbon Mech\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#323d46\",\r\n      \"#080d15\",\r\n      \"#95bac9\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\"\r\n  },\r\n  {\r\n    \"id\": \"hazard-panel\",\r\n    \"name\": \"Hazard Panel\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#edb932\",\r\n      \"#131a20\",\r\n      \"#ffe9a2\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\"\r\n  },\r\n  {\r\n    \"id\": \"reactor-glass\",\r\n    \"name\": \"Reactor Glass\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#48e8ff\",\r\n      \"#063a55\",\r\n      \"#d4ffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  }\r\n]\r\n","creature/source/creator/pupils.ts":"import * as THREE from 'three';\nexport const PUPILS=[['round','Round'],['vertical','Vertical slit'],['horizontal','Horizontal slit'],['oval','Oval'],['diamond','Diamond'],['star','Star'],['heart','Heart'],['cross','Cross']] as const;\nexport type PupilShape=typeof PUPILS[number][0];\nexport function pupilGeometry(type:PupilShape){\n const s=new THREE.Shape();\n if(['round','vertical','horizontal','oval'].includes(type)){\n  const rx=type==='vertical'?.07:type==='horizontal'?.30:.239;\n  const ry=type==='horizontal'?.065:type==='vertical'?.31:type==='oval'?.31:.257;\n  s.absellipse(0,0,rx,ry,0,Math.PI*2,false,0);\n }else if(type==='heart'){\n  s.moveTo(0,-.28);s.bezierCurveTo(-.09,-.18,-.31,.00,-.27,.17);s.bezierCurveTo(-.24,.32,-.07,.32,0,.18);s.bezierCurveTo(.07,.32,.24,.32,.27,.17);s.bezierCurveTo(.31,0,.09,-.18,0,-.28);\n }else{\n  let pts:number[][]=[];\n  if(type==='diamond')pts=[[0,.31],[.21,0],[0,-.31],[-.21,0]];\n  if(type==='star')pts=Array.from({length:10},(_,i)=>{const a=Math.PI/2+i*Math.PI/5,r=i%2?.135:.30;return [Math.cos(a)*r,Math.sin(a)*r];});\n  if(type==='cross')pts=[[-.08,.28],[.08,.28],[.08,.08],[.28,.08],[.28,-.08],[.08,-.08],[.08,-.28],[-.08,-.28],[-.08,-.08],[-.28,-.08],[-.28,.08],[-.08,.08]];\n  pts.forEach((p,i)=>i?s.lineTo(p[0],p[1]):s.moveTo(p[0],p[1]));s.closePath();\n }\n return new THREE.ShapeGeometry(s,40);\n}\n\r\n","creature/source/creator/roster.ts":"// Generated by D:/MYR5 Roster/tools/publish_roster.py. Do not edit by hand.\r\n// @ts-ignore coach-names.mjs is plain JS shared with the app and server.\r\nimport {coachName,EXCLUDED_COACH_IDS} from '../../../coach-names.mjs';\r\nexport type RosterModel={id:string;label:string;group:string;sourceLabel:string};\r\nconst ROSTER_SOURCE:readonly {id:string;label:string;group:string}[]=[{\"id\": \"roster/01-seed-pearo--3d_character_model\", \"label\": \"Seed · Pearo 1\", \"group\": \"Seed · Pearo\"}, {\"id\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\", \"label\": \"Seed · Pearo 2\", \"group\": \"Seed · Pearo\"}, {\"id\": \"roster/01-seed-pearo--white_3d_character_model\", \"label\": \"Seed · Pearo 3\", \"group\": \"Seed · Pearo\"}, {\"id\": \"roster/01-seed-pearo--blank_humanoid_figure_3d_model\", \"label\": \"Seed · Pearo 4\", \"group\": \"Seed · Pearo\"}, {\"id\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\", \"label\": \"Seed · Pearo 5\", \"group\": \"Seed · Pearo\"}, {\"id\": \"roster/02-taper-tallstalk--3d_humanoid_figure\", \"label\": \"Taper · Tallstalk 1\", \"group\": \"Taper · Tallstalk\"}, {\"id\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\", \"label\": \"Taper · Tallstalk 2\", \"group\": \"Taper · Tallstalk\"}, {\"id\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\", \"label\": \"Taper · Tallstalk 3\", \"group\": \"Taper · Tallstalk\"}, {\"id\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\", \"label\": \"Pearl · Orb 1\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/03-pearl-orb-ring--circle_center\", \"label\": \"Pearl · Orb 2\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\", \"label\": \"Pearl · Orb 3\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/03-pearl-orb-ring--robot_3d_model\", \"label\": \"Pearl · Orb 4\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/03-pearl-orb-ring--stylized_3d_character1\", \"label\": \"Pearl · Orb 5\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\", \"label\": \"Pearl · Orb 6\", \"group\": \"Pearl · Orb\"}, {\"id\": \"roster/04-crest-wedge--robot_creature_3d_model\", \"label\": \"Crest · Wedge 1\", \"group\": \"Crest · Wedge\"}, {\"id\": \"roster/04-crest-wedge--stylized_3d_character\", \"label\": \"Crest · Wedge 2\", \"group\": \"Crest · Wedge\"}, {\"id\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\", \"label\": \"Slope · Bobble 1\", \"group\": \"Slope · Bobble\"}, {\"id\": \"roster/06-ridge-triad--geometric_robot_3d_model1\", \"label\": \"Ridge · Triad 1\", \"group\": \"Ridge · Triad\"}, {\"id\": \"roster/06-ridge-triad--robot_3d_model1\", \"label\": \"Ridge · Triad 2\", \"group\": \"Ridge · Triad\"}, {\"id\": \"roster/07-bulb-sphereling--cute_robot_3d_model\", \"label\": \"Bulb · Sphereling 1\", \"group\": \"Bulb · Sphereling\"}, {\"id\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\", \"label\": \"Bulb · Sphereling 2\", \"group\": \"Bulb · Sphereling\"}, {\"id\": \"roster/08-shard-asym--geometric_robot_3d_model\", \"label\": \"Shard · Asym 1\", \"group\": \"Shard · Asym\"}, {\"id\": \"roster/08-shard-asym--robot_3d_model4\", \"label\": \"Shard · Asym 2\", \"group\": \"Shard · Asym\"}, {\"id\": \"roster/08-shard-asym--fantasy_creature_3d_model2\", \"label\": \"Shard · Asym 3\", \"group\": \"Shard · Asym\"}, {\"id\": \"roster/08-shard-asym--stylized_action_figure_3d_model\", \"label\": \"Shard · Asym 4\", \"group\": \"Shard · Asym\"}, {\"id\": \"roster/09-monolith-tanka--3d_robot_model\", \"label\": \"Monolith · Tanka 1\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\", \"label\": \"Monolith · Tanka 2\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/09-monolith-tanka--mini_robot_3d_model\", \"label\": \"Monolith · Tanka 3\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/09-monolith-tanka--robot_3d_model2\", \"label\": \"Monolith · Tanka 4\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/09-monolith-tanka--robot_3d_model3\", \"label\": \"Monolith · Tanka 5\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\", \"label\": \"Monolith · Tanka 6\", \"group\": \"Monolith · Tanka\"}, {\"id\": \"roster/10-petal-wisp--stylized_creature_3d_model\", \"label\": \"Petal · Wisp 1\", \"group\": \"Petal · Wisp\"}, {\"id\": \"roster/10-petal-wisp--ghost_character_3d_model\", \"label\": \"Petal · Wisp 2\", \"group\": \"Petal · Wisp\"}, {\"id\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\", \"label\": \"Petal · Wisp 3\", \"group\": \"Petal · Wisp\"}, {\"id\": \"roster/11-anvil-cask--clay-style_robot_3d_model\", \"label\": \"Anvil · Cask 1\", \"group\": \"Anvil · Cask\"}, {\"id\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\", \"label\": \"Seedpod · Snailslug 1\", \"group\": \"Seedpod · Snailslug\"}, {\"id\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\", \"label\": \"Seedpod · Snailslug 2\", \"group\": \"Seedpod · Snailslug\"}, {\"id\": \"roster/13-fan-split--stylized_3d_character2\", \"label\": \"Fan · Split 1\", \"group\": \"Fan · Split\"}, {\"id\": \"roster/13-fan-split--stylized_toy_3d_model\", \"label\": \"Fan · Split 2\", \"group\": \"Fan · Split\"}, {\"id\": \"roster/14-chisel-spire--cone_head_3d_model\", \"label\": \"Chisel · Spire 1\", \"group\": \"Chisel · Spire\"}, {\"id\": \"roster/14-chisel-spire--low_poly_robot_3d_model\", \"label\": \"Chisel · Spire 2\", \"group\": \"Chisel · Spire\"}, {\"id\": \"roster/15-orbital-coili--robot_character_3d_model\", \"label\": \"Orbital · Coili 1\", \"group\": \"Orbital · Coili\"}, {\"id\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\", \"label\": \"Spade · Arch 1\", \"group\": \"Spade · Arch\"}, {\"id\": \"roster/16-spade-arch--stylized_humanoid_3d_model\", \"label\": \"Spade · Arch 2\", \"group\": \"Spade · Arch\"}, {\"id\": \"roster/17-shellcap-manyarm--3d_character_figurine\", \"label\": \"Shellcap · Manyarm 1\", \"group\": \"Shellcap · Manyarm\"}, {\"id\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\", \"label\": \"Shellcap · Manyarm 2\", \"group\": \"Shellcap · Manyarm\"}, {\"id\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\", \"label\": \"Shellcap · Manyarm 3\", \"group\": \"Shellcap · Manyarm\"}, {\"id\": \"roster/18-quad-all--dragon_creature_3d_model\", \"label\": \"Four-legged 1\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--fantasy_creature_3d_model1\", \"label\": \"Four-legged 2\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--fantasy_creature_3d_model3\", \"label\": \"Four-legged 3\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--four-legged_robot_3d_model\", \"label\": \"Four-legged 4\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--quadruped_robot_3d_model1\", \"label\": \"Four-legged 5\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--quadruped_robot_3d_model\", \"label\": \"Four-legged 6\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--robotic_dog_3d_model\", \"label\": \"Four-legged 7\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--stylized_quadruped_3d_model\", \"label\": \"Four-legged 8\", \"group\": \"Four-legged\"}, {\"id\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\", \"label\": \"Four-legged 9\", \"group\": \"Four-legged\"}, {\"id\": \"roster/19-genie-multi--fantasy_creature_3d_model\", \"label\": \"Genie · Multi 1\", \"group\": \"Genie · Multi\"}, {\"id\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\", \"label\": \"Genie · Multi 2\", \"group\": \"Genie · Multi\"}, {\"id\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\", \"label\": \"Genie · Multi 3\", \"group\": \"Genie · Multi\"}, {\"id\": \"roster/19-genie-multi--stylized_octopus_3d_model\", \"label\": \"Genie · Multi 4\", \"group\": \"Genie · Multi\"}, {\"id\": \"roster/20-lume--robotic_figure_3d_model\", \"label\": \"Lume 1\", \"group\": \"Lume\"}, {\"id\": \"roster/22-curve--stylized_alien_3d_model\", \"label\": \"Curve 1\", \"group\": \"Curve\"}, {\"id\": \"roster/22-curve--stylized_cartoon_figure_3d_model\", \"label\": \"Curve 2\", \"group\": \"Curve\"}, {\"id\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\", \"label\": \"Blob 1\", \"group\": \"Blob\"}, {\"id\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\", \"label\": \"Blob 2\", \"group\": \"Blob\"}, {\"id\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\", \"label\": \"Blob 3\", \"group\": \"Blob\"}, {\"id\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\", \"label\": \"Blob 4\", \"group\": \"Blob\"}, {\"id\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\", \"label\": \"Blob 5\", \"group\": \"Blob\"}];\r\n\r\n// Excluded ids never reach the creator; labels come from the central coach names (the generated\r\n// label is kept as sourceLabel).\r\nexport const rosterModels:readonly RosterModel[]=ROSTER_SOURCE\r\n .filter(model=>!EXCLUDED_COACH_IDS.includes(model.id))\r\n .map(model=>({...model,sourceLabel:model.label,label:coachName(model.id)}));\r\nexport const ROSTER=rosterModels;\r\n","creature/source/creator/skeletal-anatomy.ts":"import * as T from 'three';\r\nimport {EYE_LAYOUTS} from './eye-layouts';\r\nimport {STYLES,type Design,type Region} from './design';\r\nimport {sculptMaterial,materialFor} from './material-language';\r\n\r\nexport function boneSockets(d:Design){\r\n const group=new T.Group();group.name='Empty bone sockets';group.userData.region='head';\r\n for(const [index,[x,y,z,r,yaw]]of EYE_LAYOUTS[d.eyeLayout].eyes.entries()){\r\n  const socket=new T.Group();socket.name='Empty socket '+(index+1);socket.position.set(x,y,z);socket.rotation.y=T.MathUtils.degToRad(yaw);\r\n  // A recessed concave bowl and a continuous ivory rim. No eyeball, iris,\r\n  // pupil, highlight or emissive dot is present, even for mixed recipes.\r\n  const bowlGeo=new T.SphereGeometry(r*.94,32,20,0,Math.PI*2,Math.PI/2,Math.PI/2);bowlGeo.rotateX(Math.PI/2);\r\n  const bowl=new T.Mesh(bowlGeo,new T.MeshStandardMaterial({color:'#0c0907',roughness:1,side:T.DoubleSide}));bowl.name='Recessed empty cavity';bowl.position.z=-r*.06;\r\n  const rim=new T.Mesh(new T.TorusGeometry(r*.94,r*.105,8,32),materialFor(STYLES[7],1));rim.material.vertexColors=false;rim.name='Weathered orbital bone';\r\n  socket.add(bowl,rim);socket.userData.emptySocket=true;group.add(socket);\r\n }\r\n return group;\r\n}\r\n\r\nexport function skeletalStructure(region:Region,d:Design){\r\n const root=new T.Group();root.name='Articulated skeleton '+region;root.userData.region=region;\r\n const mat=new T.MeshStandardMaterial({color:STYLES[7].primary,roughness:.83});\r\n const mesh=(g:T.BufferGeometry,p:number[],scale=[1,1,1])=>{const m=new T.Mesh(g,mat.clone());m.position.fromArray(p);m.scale.fromArray(scale);m.userData.region=region;root.add(m);return m;};\r\n const joint=(p:number[],r=.07)=>mesh(new T.SphereGeometry(r,10,7),p,[1,.78,1]);\r\n const bone=(a:number[],b:number[],r=.055)=>{const from=new T.Vector3(...a),to=new T.Vector3(...b),dir=to.clone().sub(from);const m=mesh(new T.CylinderGeometry(r*.72,r*.9,dir.length(),9),from.clone().add(to).multiplyScalar(.5).toArray());m.quaternion.setFromUnitVectors(new T.Vector3(0,1,0),dir.normalize());joint(a,r*1.6);joint(b,r*1.6);};\r\n if(region==='body'){\r\n  bone([0,.48,-.18],[0,1.5,-.18],.09);\r\n  for(let i=0;i<7;i++){const y=.58+i*.125,w=.3+Math.sin((i+1)/8*Math.PI)*.24;const points=Array.from({length:25},(_,k)=>{const a=k/24*Math.PI*2;return new T.Vector3(Math.cos(a)*w,y+Math.sin(a)*.035,-.08+Math.sin(a)*.35);});mesh(new T.TubeGeometry(new T.CatmullRomCurve3(points,true),32,.031,6,true),[0,0,0]);}\r\n  bone([0,.75,.3],[0,1.41,.27],.043);for(const sign of [-1,1]){bone([0,1.4,-.08],[sign*.51,1.4,-.03],.048);const pelvis=mesh(new T.TorusGeometry(.2,.075,6,12),[sign*.22,.51,-.09],[1,.78,1]);pelvis.rotation.y=sign*.3;}\r\n }else if(region==='collar'){\r\n  for(let i=0;i<3;i++){const ring=mesh(new T.TorusGeometry(.24+i*.075,.043,6,22),[0,1.39+i*.09,-.04]);ring.rotation.x=Math.PI/2;}\r\n }else if(region==='arms'){\r\n  for(const sign of [-1,1]){\r\n   const shoulder=[sign*.52,1.36,-.02],elbow=[sign*.78,1.05,.04],wrist=[sign*.91,.81,.16];bone(shoulder,elbow,.057);bone([elbow[0]-.035,elbow[1],elbow[2]],wrist,.035);bone([elbow[0]+.035,elbow[1],elbow[2]-.025],[wrist[0]+.055,wrist[1],wrist[2]],.028);\r\n   for(let i=0;i<d.fingers;i++){const offset=(i-(d.fingers-1)/2)*.056,a=[wrist[0]+sign*offset,wrist[1]-.08,wrist[2]+.035],b=[a[0]+sign*.06,a[1]-.13,a[2]+.08],c=[b[0]-sign*.025,b[1]-.09,b[2]+.055];bone(wrist,a,.024);bone(a,b,.021);bone(b,c,.017);}\r\n  }\r\n }else if(region==='feet'){\r\n  for(const sign of [-1,1]){const hip=[sign*.34,.58,-.03],ankle=[sign*.39,.2,.04];bone(hip,ankle,.068);for(let i=0;i<d.toes;i++){const x=sign*.39+(i-(d.toes-1)/2)*.072;bone(ankle,[x,.14,.2],.03);bone([x,.14,.2],[x,.105,.38],.025);}}\r\n }\r\n root.updateMatrixWorld(true);root.traverse(o=>{if(o instanceof T.Mesh)sculptMaterial(o,STYLES[7]);});return root;\r\n}\r\n\r\nexport function materialCollar(styleId:number){\r\n const group=new T.Group();group.name='Material collar';group.userData.region='collar';\r\n const style=STYLES[styleId],hard=[3,13,18,19].includes(styleId),geo=new T.TorusGeometry(.52,hard?.15:.18,hard?6:14,hard?12:32);\r\n geo.rotateX(Math.PI/2);geo.scale(1,1,.75);geo.translate(0,1.45,-.03);\r\n const mesh=new T.Mesh(geo,new T.MeshStandardMaterial());group.add(mesh);sculptMaterial(mesh,style,1);return group;\r\n}\r\n\r\nexport function robotStructure(region:Region,d:Design){\r\n const root=new T.Group();root.name='Mechanical '+region;root.userData.region=region;\r\n const armor=new T.MeshPhysicalMaterial({color:'#ad7842',metalness:.87,roughness:.27,clearcoat:.4}),steel=new T.MeshStandardMaterial({color:'#252e3b',metalness:.88,roughness:.3}),light=new T.MeshStandardMaterial({color:'#ffe7a0',emissive:'#ffd169',emissiveIntensity:1.2,roughness:.2});\r\n const mesh=(g:T.BufferGeometry,p:number[],mat=armor)=>{const m=new T.Mesh(g,mat.clone());m.position.fromArray(p);m.userData.region=region;root.add(m);return m;};\r\n const rod=(a:number[],b:number[],radius:number,mat=steel)=>{const from=new T.Vector3(...a),to=new T.Vector3(...b),dir=to.clone().sub(from);const m=mesh(new T.CylinderGeometry(radius,radius,dir.length(),8),from.clone().add(to).multiplyScalar(.5).toArray(),mat as typeof armor);m.quaternion.setFromUnitVectors(new T.Vector3(0,1,0),dir.normalize());};\r\n const hinge=(p:number[],r:number)=>{const m=mesh(new T.CylinderGeometry(r,r,.12,12),p,steel as typeof armor);m.rotation.x=Math.PI/2;const hub=mesh(new T.CylinderGeometry(r*.45,r*.45,.14,8),p);hub.rotation.x=Math.PI/2;};\r\n if(region==='body'){\r\n  const shell=mesh(new T.CylinderGeometry(.51,.42,.93,8),[0,.99,-.09]);shell.scale.z=.79;\r\n  for(const y of [.57,.78,1.03,1.43]){const ring=mesh(new T.TorusGeometry(.45,.035,5,8),[0,y,-.09],steel as typeof armor);ring.rotation.x=Math.PI/2;ring.scale.y=.8;}\r\n  const reactor=mesh(new T.CylinderGeometry(.19,.19,.07,12),[0,1.1,.34],steel as typeof armor);reactor.rotation.x=Math.PI/2;\r\n  const core=mesh(new T.CylinderGeometry(.12,.12,.08,12),[0,1.1,.36],light as typeof armor);core.rotation.x=Math.PI/2;\r\n  for(const sign of [-1,1])for(const y of [.75,1.3]){const rivet=mesh(new T.CylinderGeometry(.036,.036,.1,6),[sign*.29,y,.34],steel as typeof armor);rivet.rotation.x=Math.PI/2;}\r\n }else if(region==='arms'){\r\n  for(const sign of [-1,1]){const shoulder=[sign*.52,1.38,0],elbow=[sign*.77,1.04,.04],wrist=[sign*.91,.8,.17];hinge(shoulder,.16);rod(shoulder,elbow,.108,armor);hinge(elbow,.12);rod(elbow,wrist,.086,armor);rod([elbow[0]+sign*.06,elbow[1],elbow[2]+.07],[wrist[0]+sign*.06,wrist[1],wrist[2]+.07],.027);hinge(wrist,.085);\r\n   mesh(new T.BoxGeometry(.23,.16,.13),[wrist[0],.69,.21]);for(let i=0;i<d.fingers;i++){const x=wrist[0]+(i-(d.fingers-1)/2)*.052,a=[x,.63,.21],b=[x,.52,.27],c=[x,.45,.3];hinge(a,.029);rod(a,b,.023,armor);hinge(b,.026);rod(b,c,.02,armor);}}\r\n }else if(region==='feet'){\r\n  for(const sign of [-1,1]){hinge([sign*.35,.53,-.01],.13);rod([sign*.35,.53,-.01],[sign*.39,.22,.04],.095,armor);hinge([sign*.39,.22,.04],.095);mesh(new T.BoxGeometry(.26,.13,.32),[sign*.39,.13,.15]);for(let i=0;i<d.toes;i++)mesh(new T.BoxGeometry(.055,.085,.13),[sign*.39+(i-(d.toes-1)/2)*.065,.115,.36]);}\r\n }\r\n return root;\r\n}\r\n","creature/source/creator/skin-materials.ts":"import * as T from 'three';\r\nimport {applyPaletteFinish,paletteFinish,FINISH_ID,FINISH_TINT_GLSL} from './palette-finishes';\r\n\r\nexport type SkinMapName='basecolor'|'normal'|'roughness'|'height'|'metalness'|'tintMask'|'ao'|'opacity'|'emissive'|'preview';\r\nexport type InstalledSkin={id:string;displayName:string;track:string;maps:Partial<Record<SkinMapName,Uint8Array>>};\r\nexport type PaletteTriad={primary:string;secondary:string;accent:string;paletteId?:string};\r\n\r\nconst SPACE:Partial<Record<SkinMapName,'srgb'|'linear'>>={basecolor:'srgb',tintMask:'srgb',emissive:'srgb',normal:'linear',roughness:'linear',height:'linear',metalness:'linear',ao:'linear',opacity:'linear'};\r\nexport type SkinTextureCache=Map<Uint8Array,Partial<Record<'srgb'|'linear',Promise<T.Texture>>>>;\r\nfunction textureFrom(bytes:Uint8Array,colorSpace:'srgb'|'linear',owned:Set<T.Texture>):Promise<T.Texture>{\r\n const url=URL.createObjectURL(new Blob([bytes],{type:'image/webp'}));\r\n return new Promise((resolve,reject)=>new T.TextureLoader().load(url,texture=>{URL.revokeObjectURL(url);texture.colorSpace=colorSpace==='srgb'?T.SRGBColorSpace:T.NoColorSpace;texture.wrapS=texture.wrapT=T.RepeatWrapping;texture.magFilter=T.LinearFilter;texture.minFilter=T.LinearMipmapLinearFilter;texture.generateMipmaps=true;texture.needsUpdate=true;owned.add(texture);resolve(texture);},undefined,error=>{URL.revokeObjectURL(url);reject(error||new Error('Skin texture could not be decoded.'));}));\r\n}\r\n\r\nasync function loadTexture(bytes:Uint8Array,colorSpace:'srgb'|'linear',owned:Set<T.Texture>,cache?:SkinTextureCache):Promise<T.Texture>{\r\n if(!cache)return textureFrom(bytes,colorSpace,owned);\r\n let spaces=cache.get(bytes);if(!spaces){spaces={};cache.set(bytes,spaces);}\r\n let pending=spaces[colorSpace];if(!pending){pending=textureFrom(bytes,colorSpace,owned);spaces[colorSpace]=pending;pending.catch(()=>{if(cache.get(bytes)===spaces&&spaces[colorSpace]===pending){delete spaces[colorSpace];if(!spaces.srgb&&!spaces.linear)cache.delete(bytes);}});}\r\n return pending;\r\n}\r\n\r\n/** Decode every supported map before a caller stages changes to any material. */\r\nexport async function loadInstalledSkinTextures(skin:InstalledSkin,owned:Set<T.Texture>,cache?:SkinTextureCache):Promise<Map<SkinMapName,T.Texture>>{\r\n const entries=Object.entries(skin.maps).filter(([name,bytes])=>name!=='preview'&&!!SPACE[name as SkinMapName]&&bytes instanceof Uint8Array) as [SkinMapName,Uint8Array][];\r\n const loaded=new Map<SkinMapName,T.Texture>();\r\n const results=await Promise.allSettled(entries.map(async([name,bytes])=>loaded.set(name,await loadTexture(bytes,SPACE[name]!,owned,cache))));\r\n const failed=results.find((result):result is PromiseRejectedResult=>result.status==='rejected');\r\n if(failed){if(!cache)for(const texture of loaded.values()){owned.delete(texture);texture.dispose();}throw failed.reason;}\r\n return loaded;\r\n}\r\n\r\n/** Apply only maps present in the verified packet. Missing maps retain existing material defaults. */\r\nexport async function applyInstalledSkin(mesh:T.Mesh,skin:InstalledSkin,palette:PaletteTriad,owned:Set<T.Texture>,cache?:SkinTextureCache):Promise<boolean>{\r\n const entries=Object.entries(skin.maps).filter(([name,bytes])=>name!=='preview'&&!!SPACE[name as SkinMapName]&&bytes instanceof Uint8Array) as [SkinMapName,Uint8Array][];\r\n if(!entries.length)return false;\r\n // Some roster models only carry vertex colours. Give them deterministic local\r\n // cylindrical UVs so an installed finish never silently samples one pixel.\r\n if(!mesh.geometry.getAttribute('uv')){const position=mesh.geometry.getAttribute('position');mesh.geometry.computeBoundingBox();const box=mesh.geometry.boundingBox!,center=box.getCenter(new T.Vector3()),height=Math.max(1e-6,box.max.y-box.min.y),uv=new Float32Array(position.count*2);for(let i=0;i<position.count;i++){uv[i*2]=.5+Math.atan2(position.getZ(i)-center.z,position.getX(i)-center.x)/(2*Math.PI);uv[i*2+1]=(position.getY(i)-box.min.y)/height;}mesh.geometry.setAttribute('uv',new T.BufferAttribute(uv,2));}\r\n const loaded=await loadInstalledSkinTextures(skin,owned,cache);\r\n if(!mesh.geometry.getAttribute('uv2')&&mesh.geometry.getAttribute('uv'))mesh.geometry.setAttribute('uv2',mesh.geometry.getAttribute('uv').clone());\r\n for(const raw of Array.isArray(mesh.material)?mesh.material:[mesh.material]){\r\n  const material=raw as T.MeshPhysicalMaterial;\r\n  if((palette.paletteId||loaded.has('basecolor')||loaded.has('tintMask')||loaded.has('emissive'))&&mesh.geometry.getAttribute('uv'))material.defines={...material.defines,USE_UV:''};\r\n  if(loaded.has('normal')){material.normalMap=loaded.get('normal')!;material.normalScale.set(1,1);material.userData.normalConvention='OpenGL +Y';}\r\n  if(loaded.has('roughness'))material.roughnessMap=loaded.get('roughness')!;\r\n  if(loaded.has('height')){material.bumpMap=loaded.get('height')!;material.bumpScale=.018;}\r\n  if(loaded.has('metalness')){material.metalnessMap=loaded.get('metalness')!;material.metalness=1;}\r\n  if(loaded.has('ao'))material.aoMap=loaded.get('ao')!;\r\n  if(loaded.has('opacity')){material.alphaMap=loaded.get('opacity')!;material.transparent=true;material.depthWrite=false;}\r\n  if((palette.paletteId||loaded.has('basecolor')||loaded.has('tintMask')||loaded.has('emissive'))&&mesh.geometry.getAttribute('uv')){\r\n   const fin=paletteFinish(palette.paletteId);\r\n   const previous=material.onBeforeCompile;\r\n   material.onBeforeCompile=(shader,renderer)=>{\r\n    previous.call(material,shader,renderer);\r\n    shader.uniforms.myr5SkinMask={value:loaded.get('tintMask')};\r\n    shader.uniforms.myr5SkinPrimary={value:new T.Color(palette.primary)};\r\n    shader.uniforms.myr5SkinSecondary={value:new T.Color(palette.secondary)};\r\n    shader.uniforms.myr5SkinAccent={value:new T.Color(palette.accent)};\r\n    if(loaded.has('height'))shader.uniforms.myr5SkinHeight={value:loaded.get('height')};\r\n    if(loaded.has('basecolor'))shader.uniforms.myr5SkinBasecolor={value:loaded.get('basecolor')};\r\n    if(loaded.has('emissive'))shader.uniforms.myr5SkinEmissive={value:loaded.get('emissive')};\r\n    shader.vertexShader=shader.vertexShader.replace('#include <common>','#include <common>\\nvarying vec2 vMyr5SkinUv;').replace('#include <uv_vertex>','#include <uv_vertex>\\nvMyr5SkinUv=uv;');\r\n    if(fin)shader.fragmentShader=shader.fragmentShader.replace('#include <common>','#include <common>\\n'+FINISH_TINT_GLSL);\r\n    shader.fragmentShader=shader.fragmentShader.replace('#include <common>','#include <common>\\nuniform sampler2D myr5SkinMask;\\nuniform sampler2D myr5SkinHeight;\\nuniform sampler2D myr5SkinBasecolor;\\nuniform sampler2D myr5SkinEmissive;\\nuniform vec3 myr5SkinPrimary;\\nuniform vec3 myr5SkinSecondary;\\nuniform vec3 myr5SkinAccent;\\nvarying vec2 vMyr5SkinUv;');\r\n    // The texture is tagged SRGBColorSpace, so WebGL's sRGB sampling decodes the mask to linear.\r\n    const tone=loaded.has('tintMask')?'dot(texture2D(myr5SkinMask,vMyr5SkinUv).rgb,vec3(0.2126,0.7152,0.0722))':loaded.has('height')?'texture2D(myr5SkinHeight,vMyr5SkinUv).r':loaded.has('basecolor')?'dot(texture2D(myr5SkinBasecolor,vMyr5SkinUv).rgb,vec3(0.2126,0.7152,0.0722))':'0.5';\r\n    const paletteTone=fin&&fin!=='watercolor'&&fin!=='marbled'&&!loaded.has('tintMask')&&!loaded.has('height')&&!loaded.has('basecolor')?'myr5FlatPaletteTint(vMyr5SkinUv)':tone;\r\n    const layers=palette.paletteId?`float skinTint=clamp(${paletteTone},0.0,1.0);\\n${fin==='watercolor'||fin==='marbled'?`skinTint=myr5FinishTint(${FINISH_ID[fin]}.0,skinTint,vMyr5SkinUv);`:''}\\nvec3 skinPalette=skinTint<0.5?mix(myr5SkinSecondary,myr5SkinPrimary,skinTint*2.0):mix(myr5SkinPrimary,myr5SkinAccent,(skinTint-0.5)*2.0);\\ndiffuseColor.rgb=skinPalette;`:loaded.has('tintMask')?'vec3 skinBase=texture2D(myr5SkinMask,vMyr5SkinUv).rgb;\\nfloat skinMask=dot(skinBase,vec3(0.2126,0.7152,0.0722));\\nvec3 skinPalette=skinMask<0.5?mix(myr5SkinSecondary,myr5SkinPrimary,skinMask*2.0):mix(myr5SkinPrimary,myr5SkinAccent,(skinMask-0.5)*2.0);\\ndiffuseColor.rgb=skinBase*skinPalette;':loaded.has('basecolor')?'diffuseColor.rgb*=texture2D(myr5SkinBasecolor,vMyr5SkinUv).rgb;':'';\r\n    shader.fragmentShader=shader.fragmentShader.replace('#include <color_fragment>','#include <color_fragment>\\n'+layers);\r\n    if(loaded.has('emissive'))shader.fragmentShader=shader.fragmentShader.replace('#include <emissivemap_fragment>','#include <emissivemap_fragment>\\ntotalEmissiveRadiance+=texture2D(myr5SkinEmissive,vMyr5SkinUv).rgb*0.85;');\r\n   };\r\n   const priorKey=material.customProgramCacheKey.bind(material),mapKey=[...loaded.keys()].sort().join(',');material.customProgramCacheKey=()=>priorKey()+'|myr5-installed-skin-palette-v5|'+mapKey+'|'+(palette.paletteId?'palette':'colour')+'|'+(fin??'none');\r\n   material.userData.installedSkinId=skin.id;\r\n  }\r\n  if(palette.paletteId)applyPaletteFinish(material,palette.paletteId,palette); // prior shader hooks stay installed; finish cache key composes via its own tag\r\n  material.needsUpdate=true;\r\n }\r\n return true;\r\n}\r\n","creature/source/creator/swatches.ts":"// Runtime swatch generation (Rank 4): texture pattern previews are small canvases rendered\r\n// from the same procedural surfaceSample() the 3D material uses, cached by family id, never\r\n// shipped as image files. Colour swatches need no canvas at all — a CSS background is enough.\r\nimport {surfaceSample} from './material-language';\r\n\r\nconst cache = new Map<number, string>();\r\nexport function texturePreviewDataURL(familyId: number, size = 40): string {\r\n const cached = cache.get(familyId); if (cached) return cached;\r\n const canvas = document.createElement('canvas'); canvas.width = canvas.height = size;\r\n const ctx = canvas.getContext('2d'); if (!ctx) return '';\r\n const img = ctx.createImageData(size, size);\r\n for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) {\r\n  const s = surfaceSample(familyId, x / size, y / size), v = Math.round(Math.max(0, Math.min(1, s.height)) * 255), i = (y * size + x) * 4;\r\n  img.data[i] = img.data[i + 1] = img.data[i + 2] = v; img.data[i + 3] = 255;\r\n }\r\n ctx.putImageData(img, 0, 0);\r\n const url = canvas.toDataURL('image/png'); cache.set(familyId, url); return url;\r\n}\r\n","creature/source/creator/texture-policy.mjs":"// The only textures available before opening packs. Keep these IDs shared by the\r\n// registry and reward pool so future catalog additions default to pack access.\r\nexport const FREE_TEXTURE_IDS=Object.freeze([\r\n 'clay', 'martial-arts-canvas-gi', 'arms-rope', 'yoga-cork', 'glutes-peach',\r\n]);\r\n// Former choices remain understood by old recipes but cannot be picked, awarded,\r\n// previewed, or saved again. Clay is their closest safe material fallback.\r\n// R25 (Ian's notebook): Original MYR5, Jelly and Bubble Glass leave the catalogue. Saved looks and\r\n// owned grants move to their kept twin; Original MYR5 has none, so it takes the free starter (Clay).\r\nexport const TEXTURE_REPLACEMENTS=Object.freeze({'legacy-0':'clay','legacy-21':'opal-jelly','bubble-glass':'glitter-resin'});\r\nexport const RETIRED_TEXTURE_IDS=Object.freeze(['flat', 'legacy-22',...Object.keys(TEXTURE_REPLACEMENTS)]);\r\n/** The kept twin of a removed texture (grants and saves follow it); any other id is returned as is. */\r\nexport const replacementTextureId=id=>Object.hasOwn(TEXTURE_REPLACEMENTS,id)?TEXTURE_REPLACEMENTS[id]:id;\r\nexport const normalizeTextureId=id=>{const kept=replacementTextureId(id);return RETIRED_TEXTURE_IDS.includes(kept)?'clay':kept;};\r\nexport const normalizeStyleId=id=>id===22?0:id;\r\n","creature/source/creator/track-placements.ts":"// Rank 7b track-placement registry: the approved 50-model / 51-placement creature roster,\r\n// keyed by stable model id (roster.ts `id`). The customizer's Creature list groups and locks by it (#102).\r\n//\r\n// Source of truth: plan/PLAN.md §2b rank 7b; plan/DECISIONS.md D6 (incl. its 22 Sept revision),\r\n// D21, D25; plan/reports/roster-sheet.md; and mom-program-control/outputs/visual-boards/\r\n// ART-AND-CHOICE-IMPLEMENTATION-HANDOFF.md §2 (roster table), §4 (pet/mount/boss rules), §8\r\n// (stable-data contract: displayName/stableId/track/sourceAsset/license/unlockRule/renderRecipe\r\n// separated; petEligible/mountEligible stored explicitly; one model may hold several placements\r\n// without duplicating its asset).\r\n//\r\n// Label -> stable id: matched directly off roster.ts's own `label` field (only cosmetic\r\n// difference from the handoff is \"·\" vs \"/\" as the group/number separator), cross-checked\r\n// against the authoritative GROUPS/BOSSES tables in mom-program-control/tools/\r\n// generate_creature_reference_board.py and generate_creature_expansion_boards.py.\r\n//\r\n// D6 22 Sept revision: Pearl · Orb 3, Pearl · Orb 5 and Shellcap · Manyarm 3 are approved and\r\n// kept (see design.ts REJECTED_BODY_IDS comment). The 4 hidden rejects (Seed · Pearo 3 & 4,\r\n// Monolith · Tanka 4, Four-legged 8) are not in this roster at all -- see design.ts\r\n// REJECTED_BODY_IDS, reused below as the \"no rejected id present\" guard.\r\n//\r\n// unlockRule follows central coach performance access. sectionComplete() remains board metadata. renderRecipe\r\n// is still a placeholder: the production pixel-art recipe is a later rank (handoff §9 step 2).\r\nimport {REJECTED_BODY_IDS} from './design';\r\nimport {ROWS as BOARD_ROWS,TRACKS,LEVELS_PER_BOSS} from '../../../battle-pass-rewards.mjs';\r\nimport {coachAccess} from '../../../performance-progress.mjs';\r\nimport {STARTER_COACH_IDS,EXCLUDED_COACH_IDS} from '../../../performance-catalog.mjs';\r\nimport {coachRequirements} from '../../../achievements-board.mjs';\r\nexport {REJECTED_BODY_IDS};\r\n\r\nexport const TRACK_IDS = ['chest', 'quads', 'glutes', 'arms', 'yoga', 'martial-arts', 'cardio', 'meditation'] as const;\r\nexport type TrackId = typeof TRACK_IDS[number];\r\n\r\nexport type TrackPlacement = {\r\n stableId: string;\r\n displayName: string; // roster.ts label\r\n tracks: readonly TrackId[]; // >1 entry only for the intentional Spade · Arch 1 dual\r\n petEligible: boolean;\r\n mountEligible: boolean;\r\n bossOf?: TrackId; // starting-boss assignment per 06-workout-bosses-and-meditation-pets.png, if any\r\n sourceAsset: string; // GLB path, relative to the repo root\r\n license: string;\r\n unlockRule: 'performance-milestone';\r\n renderRecipe: 'tbd'; // placeholder -- production sprite recipe not authorized yet\r\n};\r\n\r\nconst LICENSE = 'Original artwork -- MYR5 alien pipeline (Tripo Studio capture by Ian, low-poly re-export, Claude eval); no third-party license asserted (see creature/NOTICE.md).';\r\nconst glb = (stableId: string) => `creature/models/${stableId}.glb`;\r\n\r\ntype Row = [stableId: string, displayName: string, tracks: TrackId[], petEligible: boolean, mountEligible: boolean, bossOf?: TrackId];\r\n\r\nconst ROWS: readonly Row[] = [\r\n // --- Meditation pet family (10; handoff §2/§4) ---\r\n ['roster/23-blob-texture-bodies--blob_creature_3d_model', 'Blob 1', ['meditation'], true, false],\r\n ['roster/18-quad-all--dragon_creature_3d_model', 'Four-legged 1', ['meditation'], true, false],\r\n ['roster/18-quad-all--fantasy_creature_3d_model1', 'Four-legged 2', ['meditation'], true, false],\r\n ['roster/18-quad-all--fantasy_creature_3d_model3', 'Four-legged 3', ['meditation'], true, false],\r\n ['roster/18-quad-all--four-legged_robot_3d_model', 'Four-legged 4', ['meditation'], true, false],\r\n ['roster/18-quad-all--quadruped_robot_3d_model1', 'Four-legged 5', ['meditation'], true, false],\r\n ['roster/18-quad-all--quadruped_robot_3d_model', 'Four-legged 6', ['meditation'], true, false],\r\n ['roster/18-quad-all--robotic_dog_3d_model', 'Four-legged 7', ['meditation'], true, true, 'meditation'], // starting boss + mount candidate\r\n ['roster/12-seedpod-snailslug--stylized_slug_3d_model', 'Seedpod · Snailslug 1', ['meditation'], true, false],\r\n ['roster/12-seedpod-snailslug--stylized_worm_3d_model', 'Seedpod · Snailslug 2', ['meditation'], true, false],\r\n // Meditation boss-only -- explicitly NOT pet/mount (handoff §2 classification correction).\r\n ['roster/14-chisel-spire--low_poly_robot_3d_model', 'Chisel · Spire 2', ['meditation'], false, false],\r\n\r\n // --- Cardio ---\r\n ['roster/07-bulb-sphereling--humanoid_robot_3d_model', 'Bulb · Sphereling 2', ['cardio'], false, false],\r\n ['roster/03-pearl-orb-ring--ringed_humanoid_3d_model', 'Pearl · Orb 3', ['cardio'], false, false], // D6 22 Sept revision: approved, kept\r\n ['roster/06-ridge-triad--robot_3d_model1', 'Ridge · Triad 2', ['cardio'], false, false],\r\n ['roster/03-pearl-orb-ring--robot_3d_model', 'Pearl · Orb 4', ['cardio'], false, false],\r\n ['roster/20-lume--robotic_figure_3d_model', 'Lume 1', ['cardio'], false, false, 'cardio'],\r\n\r\n // --- Arms + Shoulders ---\r\n ['roster/02-taper-tallstalk--humanoid_robot_3d_model1', 'Taper · Tallstalk 2', ['arms'], false, false],\r\n ['roster/19-genie-multi--multi-armed_humanoid_3d_model1', 'Genie · Multi 2', ['arms'], false, false],\r\n ['roster/19-genie-multi--multi-armed_humanoid_3d_model', 'Genie · Multi 3', ['arms'], false, false],\r\n ['roster/17-shellcap-manyarm--mushroom_creature_3d_model', 'Shellcap · Manyarm 2', ['arms'], false, false],\r\n ['roster/17-shellcap-manyarm--mushroom_robot_3d_model', 'Shellcap · Manyarm 3', ['arms'], false, false], // D6 22 Sept revision: approved, kept\r\n ['roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model', 'Blob 4', ['arms'], false, false],\r\n ['roster/08-shard-asym--robot_3d_model4', 'Shard · Asym 2', ['arms'], false, false, 'arms'],\r\n\r\n // --- Chest (Spade · Arch 1 is the intentional dual; its martial-arts placement lives here too) ---\r\n ['roster/06-ridge-triad--geometric_robot_3d_model1', 'Ridge · Triad 1', ['chest'], false, false],\r\n ['roster/08-shard-asym--geometric_robot_3d_model', 'Shard · Asym 1', ['chest'], false, false],\r\n ['roster/16-spade-arch--pyramid_head_figure_3d_model', 'Spade · Arch 1', ['chest', 'martial-arts'], false, false],\r\n ['roster/04-crest-wedge--robot_creature_3d_model', 'Crest · Wedge 1', ['chest'], false, false],\r\n ['roster/16-spade-arch--stylized_humanoid_3d_model', 'Spade · Arch 2', ['chest'], false, false, 'chest'],\r\n\r\n // --- Glutes ---\r\n ['roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model', 'Blob 3', ['glutes'], false, false],\r\n ['roster/23-blob-texture-bodies--sand_creature_3d_model', 'Blob 5', ['glutes'], false, false],\r\n ['roster/03-pearl-orb-ring--stylized_3d_character1', 'Pearl · Orb 5', ['glutes'], false, false], // D6 22 Sept revision: approved, kept\r\n ['roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model', 'Pearl · Orb 6', ['glutes'], false, false, 'glutes'],\r\n\r\n // --- Quads ---\r\n ['roster/15-orbital-coili--robot_character_3d_model', 'Orbital · Coili 1', ['quads'], false, false],\r\n ['roster/13-fan-split--stylized_3d_character2', 'Fan · Split 1', ['quads'], false, false],\r\n ['roster/22-curve--stylized_cartoon_figure_3d_model', 'Curve 2', ['quads'], false, false, 'quads'],\r\n\r\n // --- Yoga ---\r\n ['roster/10-petal-wisp--fantasy_creature_3d_model4', 'Petal · Wisp 3', ['yoga'], false, false],\r\n ['roster/19-genie-multi--fantasy_creature_3d_model', 'Genie · Multi 1', ['yoga'], false, false],\r\n ['roster/10-petal-wisp--ghost_character_3d_model', 'Petal · Wisp 2', ['yoga'], false, false],\r\n ['roster/08-shard-asym--stylized_action_figure_3d_model', 'Shard · Asym 4', ['yoga'], false, false],\r\n ['roster/22-curve--stylized_alien_3d_model', 'Curve 1', ['yoga'], false, false],\r\n ['roster/10-petal-wisp--stylized_creature_3d_model', 'Petal · Wisp 1', ['yoga'], false, false],\r\n ['roster/19-genie-multi--stylized_octopus_3d_model', 'Genie · Multi 4', ['yoga'], false, false, 'yoga'],\r\n\r\n // --- Martial Arts (Spade · Arch 1 dual is listed under Chest above, not repeated here) ---\r\n ['roster/09-monolith-tanka--boxy_humanoid_3d_model', 'Monolith · Tanka 2', ['martial-arts'], false, false],\r\n ['roster/11-anvil-cask--clay-style_robot_3d_model', 'Anvil · Cask 1', ['martial-arts'], false, false],\r\n ['roster/05-slope-bobble--clay_humanoid_figure_3d_model', 'Slope · Bobble 1', ['martial-arts'], false, false],\r\n ['roster/14-chisel-spire--cone_head_3d_model', 'Chisel · Spire 1', ['martial-arts'], false, false],\r\n ['roster/08-shard-asym--fantasy_creature_3d_model2', 'Shard · Asym 3', ['martial-arts'], false, false],\r\n ['roster/09-monolith-tanka--mini_robot_3d_model', 'Monolith · Tanka 3', ['martial-arts'], false, false],\r\n ['roster/09-monolith-tanka--robot_3d_model3', 'Monolith · Tanka 5', ['martial-arts'], false, false],\r\n ['roster/13-fan-split--stylized_toy_3d_model', 'Fan · Split 2', ['martial-arts'], false, false, 'martial-arts'],\r\n];\r\n\r\nexport const TRACK_PLACEMENTS: readonly TrackPlacement[] = ROWS.map(\r\n ([stableId, displayName, tracks, petEligible, mountEligible, bossOf]) => ({\r\n  stableId, displayName, tracks, petEligible, mountEligible, bossOf,\r\n  sourceAsset: glb(stableId), license: LICENSE, unlockRule: 'performance-milestone', renderRecipe: 'tbd',\r\n }),\r\n);\r\n\r\n// #102: a section's bodies unlock when that section is complete. `state` is the achievements board's\r\n// progress map, battle-pass.mjs loadProgress() = {bossId: levels beaten}. Ian's default meaning of\r\n// \"complete\" is the one line below: every boss in the section's board row has every level beaten.\r\nconst rowOf=(track:TrackId)=>BOARD_ROWS.find(row=>row.track&&TRACKS[row.track].catalog===track);\r\nexport function sectionComplete(track:TrackId,state:Record<string,number>){\r\n const row=rowOf(track);if(!row)return false;\r\n return Array.from({length:row.bosses},(_,i)=>state[`${row.id}-${i+1}`]??0).every(levels=>levels>=LEVELS_PER_BOSS);\r\n}\r\n/** Section names as the board shows them, in dial order (TRACK_IDS). */\r\nexport const SECTION_NAMES=Object.fromEntries(TRACK_IDS.map(t=>[t,TRACKS[rowOf(t)!.track!].name])) as Record<TrackId,string>;\r\n/** Display the central performance requirement. Old board-progress arguments remain for callers,\r\n * but board sections and previously saved recipes never authorize a coach body. */\r\nexport function bodyLockSection(id:string,_state:Record<string,number>,_tracks:Iterable<string>=[]):string|null{\r\n if(REJECTED_BODY_IDS.has(id)||EXCLUDED_COACH_IDS.includes(id))return 'Unavailable coach';\r\n if(STARTER_COACH_IDS.includes(id)||coachAccess(id))return null;\r\n return coachRequirements(id)?.unlock||'Coach performance milestone required';\r\n}\r\n","creature/source/creator/unlock-store.ts":"import {gameCacheKey,storedGameGeneration,isCurrentGameAccount} from '../../../modules/coach-world/game-generation-client.mjs';\r\n// Cosmetic ownership follows the account/guest and the coach wearing the item.\r\nimport { markPending,markPendingMany } from '../../../unlock-pending.mjs';\r\nimport { performanceOwner } from '../../../performance-progress.mjs';\r\nimport { replacementTextureId } from './texture-policy.mjs';\r\nexport type UnlockKind = 'texture' | 'color' | 'palette';\r\nconst KEY = 'myr5-unlocks-v2';\r\ntype Store = Record<UnlockKind, string[]>;\r\nconst empty = (): Store => ({ texture: [], color: [], palette: [] });\r\nexport const cosmeticId = (coachId: string, id: string) => `coach:${encodeURIComponent(coachId)}:${id}`;\r\n// R25: a grant of a removed texture is a grant of its kept twin (Jelly -> Opal Jelly, Bubble Glass ->\r\n// Glitter Resin). Reads translate old entries, and the next write stores the twin in their place.\r\nconst keptId = (kind: string, id: string) => kind === 'texture' ? replacementTextureId(id) : id;\r\nconst keptScoped = (kind: string, scoped: string) => { const m = /^(coach:[^:]*:)(.*)$/.exec(scoped); return m ? m[1] + keptId(kind, m[2]) : scoped; };\r\nexport function currentCosmeticCoach(): string {\r\n try { return JSON.parse(localStorage.getItem('myr5-recipe-v1') || '{}').body || 'myr5'; } catch { return 'myr5'; }\r\n}\r\nconst ownerKey = () => gameCacheKey(`${KEY}/${encodeURIComponent(performanceOwner())}`);\r\nfunction read(): Store {\r\n try {\r\n  const d = JSON.parse(localStorage.getItem(ownerKey()) || '{}');\r\n  return Object.fromEntries(['texture','color','palette'].map(kind => [kind, Array.isArray(d[kind]) ? [...new Set<string>(d[kind].filter((id: unknown) => typeof id === 'string').map((id: string) => keptScoped(kind, id)))] : []])) as Store;\r\n } catch { return empty(); }\r\n}\r\nfunction write(store: Store) { try { localStorage.setItem(ownerKey(), JSON.stringify(store)); return true; } catch { return false; } }\r\nconst hasScopedGrant = (kind:UnlockKind,id:string,coachId:string) => read()[kind]?.includes(cosmeticId(coachId,keptId(kind,id))) ?? false;\r\n// The original MYR5 coach is the account's shared material showcase. A cosmetic\r\n// earned for any coach is also available there, while every other coach remains\r\n// coach-scoped. This reads the current owner's ledger, so account and guest data\r\n// never cross boundaries.\r\nfunction grantedToAnyCoach(kind: UnlockKind, id: string) {\r\n const normalized=keptId(kind,id);\r\n return (read()[kind]||[]).some(scoped=>{\r\n  const match=/^coach:([^:]+):(.*)$/.exec(scoped);\r\n  if(!match||match[2]!==normalized)return false;\r\n  try{return decodeURIComponent(match[1])!=='myr5';}catch{return false;}\r\n });\r\n}\r\nexport const isGrantedToOriginalCoach = (kind: UnlockKind,id: string) =>\r\n hasScopedGrant(kind,id,'myr5') || grantedToAnyCoach(kind,id);\r\nexport const isGranted = (kind: UnlockKind, id: string, coachId = currentCosmeticCoach()) =>\r\n coachId==='myr5' ? isGrantedToOriginalCoach(kind,id) : hasScopedGrant(kind,id,coachId);\r\nfunction emitCosmeticsUnlocked(granted:{kind:UnlockKind;id:string;coachId:string}[]) {\r\n const EventCtor=globalThis.CustomEvent;\r\n if(typeof globalThis.dispatchEvent!=='function'||typeof EventCtor!=='function'||!granted.length)return;\r\n try{globalThis.dispatchEvent(new EventCtor('myr5:cosmetics-unlocked',{detail:{owner:performanceOwner(),granted}}));}catch{/* The grant remains saved if a listener cannot receive its notice. */}\r\n}\r\nexport function grantUnlock(kind: UnlockKind, id: string, coachId = currentCosmeticCoach()) {\r\n if(!isCurrentGameAccount())return false;\r\n if (!['texture','color','palette'].includes(kind) || typeof id !== 'string' || !id || typeof coachId !== 'string' || !coachId) return false;\r\n const s = read(), scoped = cosmeticId(coachId,keptId(kind,id));\r\n if (s[kind].includes(scoped)) return false;\r\n s[kind].push(scoped); if (!write(s)) return false;\r\n const normalized=keptId(kind,id),granted=[{kind,id:normalized,coachId}];\r\n markPending(kind,scoped);\r\n // Keep the original coach's new-item marker and notify the live 3D reward preview.\r\n if(coachId!=='myr5')markPending(kind,cosmeticId('myr5',normalized));\r\n emitCosmeticsUnlocked(granted);return true;\r\n}\r\nexport const grantedIds = (kind: UnlockKind, coachId = currentCosmeticCoach()) => {\r\n const values=read()[kind]||[],prefix=cosmeticId(coachId,'');\r\n const own=values.filter(id=>id.startsWith(prefix)).map(id=>id.slice(prefix.length));\r\n if(coachId!=='myr5')return own;\r\n const inherited=values.flatMap(scoped=>{const match=/^coach:([^:]+):(.*)$/.exec(scoped);if(!match||!match[2])return[];try{return decodeURIComponent(match[1])==='myr5'?[]:[match[2]];}catch{return[];}});\r\n return [...new Set([...own,...inherited])];\r\n};\r\n// Preserve a saved look without copying historical device-wide grants onto every coach.\r\nexport function migrateSavedCosmetics(recipe?: {body?:string;materials?:Record<string,{textureId?:string;colorId?:string}|undefined>}) {\r\n if((storedGameGeneration()?.gameResetAt??0)>0)return;\r\n const coachId=recipe?.body;if(!coachId)return;\r\n try {\r\n  const migration=`${ownerKey()}/saved-look/${encodeURIComponent(coachId)}`;\r\n  if(localStorage.getItem(migration))return;\r\n  const old=JSON.parse(localStorage.getItem('myr5-unlocks-v1')||'{}');\r\n  for(const choice of Object.values(recipe.materials||{})){\r\n   if(choice?.textureId&&old.texture?.includes(choice.textureId))grantUnlock('texture',choice.textureId,coachId);\r\n   if(choice?.colorId)for(const kind of ['color','palette'] as UnlockKind[])if(old[kind]?.includes(choice.colorId))grantUnlock(kind,choice.colorId,coachId);\r\n  }\r\n  localStorage.setItem(migration,'1');\r\n }catch{/* Legacy storage is optional. */}\r\n}\r\n\r\n// Collection completion writes once per owner, rather than once per cosmetic.\r\nexport const ownerGrantedIds=(kind:UnlockKind)=>{\r\n const values=read()[kind]||[],inherited=values.flatMap(scoped=>{const match=/^coach:([^:]+):(.*)$/.exec(scoped);if(!match||!match[2])return[];try{return decodeURIComponent(match[1])==='myr5'?[]:[cosmeticId('myr5',match[2])];}catch{return[];}});\r\n return [...new Set([...values,...inherited])];\r\n};\r\nexport function grantUnlocks(items:{kind:UnlockKind;id:string;coachId:string}[]){\r\n if(!isCurrentGameAccount())return 0;\r\n const data=read(),sets=Object.fromEntries(['texture','color','palette'].map(kind=>[kind,new Set(data[kind as UnlockKind])])) as Record<UnlockKind,Set<string>>;\r\n const added:{kind:UnlockKind;id:string;coachId:string}[]=[];\r\n for(const item of items){if(!sets[item.kind]||typeof item.id!=='string'||!item.id||typeof item.coachId!=='string'||!item.coachId)continue;const normalized=keptId(item.kind,item.id),id=cosmeticId(item.coachId,normalized);if(sets[item.kind].has(id))continue;sets[item.kind].add(id);data[item.kind].push(id);added.push({kind:item.kind,id:normalized,coachId:item.coachId});}\r\n if(!added.length||!write(data))return 0;\r\n const pending=added.flatMap(({kind,id,coachId})=>[{kind,id:cosmeticId(coachId,id)},...(coachId==='myr5'?[]:[{kind,id:cosmeticId('myr5',id)}])]);\r\n markPendingMany(pending);emitCosmeticsUnlocked(added);return added.length;\r\n}\r\n","creature/source/editor-workbench.ts":"import {coachPresentation} from '../../coach-presentation.mjs';\r\nimport {coachName} from '../../coach-names.mjs';\r\nimport {coachRequirements} from '../../achievements-board.mjs';\r\nimport {paintStyleText} from '../../workout-symbols.mjs';\r\nimport {goldenCoach} from '../../performance-progress.mjs';\r\nimport {PERFORMANCE_KEY,readPerformanceProgress} from '../../performance-progress.mjs';\r\nimport {pathIntroCoachIds} from '../../performance-catalog.mjs';\r\nimport {loadCoachSpritePreviews} from './war-room-coaches';\r\nimport {createStandaloneAccountContext} from '../../standalone-account-context.mjs';\r\nimport {CreatureViewer} from './viewer';\r\nimport {LatestPreview} from './latest-preview';\r\nimport {mountPreviewIdle} from './preview-idle';\r\nimport {GESTURES,type Gesture} from './motion';\r\nimport {REGIONS,PICKER_BODIES,EYE_LAYOUTS,PUPILS,RECIPE_KEY,MOTION_KEY,MAX_IMPORT_BYTES,fresh,importCreature,loadRecipe,motionSettings} from './profile';\r\n\r\nimport {COLOUR_CHANNELS,type Design,type Region,type MaterialChoice} from './creator/design';\r\nimport {EYE_STYLES} from '../../eye-styles.mjs';\r\nimport {TEXTURES,COLORS,PALETTES,isLocked as registryLocked,regionChoice,FREE_COLOURS,resolveRegionMaterial,colorTriad} from './creator/materials-registry';\r\nimport {TRACK_PLACEMENTS,bodyLockSection,sectionComplete} from './creator/track-placements';\r\nimport {isGranted,cosmeticId} from './creator/unlock-store';\r\nimport {sparkle,isUnseen,watchSelect} from '../../unlock-seen.mjs';\r\nimport {noteUnlocked} from '../../unlock-pending.mjs';\r\nimport {saveRecipe,keepOwned,selectOwnedBody,BODY_KEYS} from './save-look';\r\nimport {loadProgress} from '../../battle-pass.mjs';\r\nimport {texturePreviewDataURL} from './creator/swatches';\r\nimport {acceptShipRevealComplete,coachEditorShips,ownedShipIds} from '../../modules/ships/ship-access.mjs';\r\nimport {createInstalledCreatureSkinSource} from '../../modules/materials/installed-creature-skins.mjs';\r\nimport {SHIP_GATE,SHIP_GATE_TOKEN,SHIP_HISTORY_ADMISSION} from '../../modules/ships/ship-scene-domain.mjs';\r\n\r\nimport {localVerifiedBridge} from '../../modules/ships/verified-ship-assets.mjs';\r\nimport {mountShipPreview} from './ship-preview';\r\nimport {SHIP_CATALOG,shipDetails} from '../../modules/ships/ship-catalog.mjs';\r\nimport {FREE_COLOUR_NAMES} from '../../battle-pass-rewards.mjs';\r\nexport {CreatureViewer,GESTURES,importCreature};\r\n// #148: the ship's admission is single-use. A reload can resume this exact editor history entry, but\r\n// navigating here again (including Back/Forward) needs another completed ship reveal and tap.\r\nconst shipReload=performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming|undefined;\r\nconst admitShipEditor=(()=>{\r\n try{\r\n  const admitted=sessionStorage.getItem(SHIP_GATE)===SHIP_GATE_TOKEN;\r\n  sessionStorage.removeItem(SHIP_GATE);\r\n  if(shipReload?.type==='reload'&&history.state?.[SHIP_HISTORY_ADMISSION]===true)return true;\r\n  if(!admitted)return false;\r\n  history.replaceState({...history.state,[SHIP_HISTORY_ADMISSION]:true},'');\r\n  return true;\r\n }catch{return false;}\r\n})();\r\nif(!admitShipEditor){location.replace('/pose.html#select');await new Promise(()=>{});}\r\nconst standaloneAccount=createStandaloneAccountContext();\r\nawait standaloneAccount.refresh();\r\n// Safari can restore this document from its back-forward cache without rerunning the admission\r\n// check above. A restored editor must return through the ship instead of reviving its old state.\r\nwindow.addEventListener('pageshow',event=>{if(event.persisted)location.replace('/pose.html#select');});\r\nconst download=(blob:Blob,name:string)=>{const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),3000);};\r\nconst $=(id:string)=>document.getElementById(id)!;\r\n\r\n// D2 / audit-customizer.md C8: the one mom-approved coach. Finger/toe count and collar fluff\r\n// (Rank 5, handoff §6) are anatomy controls carved only for this body's own rig.\r\nconst MOM_APPROVED_BODY_ID='myr5';\r\nconst MOM_ONLY_FIELD_IDS=['fingersField','toesField','furField','digitsHelp'];\r\nlet recipe:Design=fresh(),undo:Design[]=[],redo:Design[]=[],selected:Region='body',ready=false,activeRange:string|null=null;\r\nlet settings=motionSettings(null),initialError='';\r\ntry{recipe=zeroFinish(loadRecipe(localStorage));settings=motionSettings(localStorage.getItem(MOTION_KEY));}catch{initialError='Your saved coach could not be read. Load a recipe in Files to restore it.';}\r\nlet saved=recipe; // last owned look written to storage; locked imports remain preview-only.\r\n// R27: ownership is per account. Until the verified account is known (cold start, slow or offline account check,\r\n// the bridge's re-check) every earned body reads as locked, so filtering then turned the saved coach into MYR5 in\r\n// memory and the next edit saved that. Show the stored look as the portal does; account-ready applies ownership.\r\nconst ownedLook=(d:Design)=>(window as any).myr5AuthenticatedAccount?keepOwned(d):d;\r\nrecipe=saved=ownedLook(recipe);\r\nlet progress=loadProgress(),bodyLock=(id:string)=>bodyLockSection(id,progress);\r\nconst unlockedFirst=<T,>(items:readonly T[],locked:(x:T)=>boolean):T[]=>[...items.filter(x=>!locked(x)),...items.filter(locked)];\r\n// Body unlocks are derived from section completion, so record them here: first use snapshots, later ones sparkle.\r\nconst noteBodyUnlocks=()=>{const state=loadProgress();noteUnlocked('body',TRACK_PLACEMENTS.filter(p=>p.tracks.some(t=>sectionComplete(t,state))).map(p=>p.stableId));};\r\nnoteBodyUnlocks();window.addEventListener('myr5:battle-pass',noteBodyUnlocks);\r\nconst systemMotion=matchMedia('(prefers-reduced-motion: reduce)');\r\nconst base=document.body.dataset.modelBase?new URL(document.body.dataset.modelBase,location.href).href:new URL('../',import.meta.url).href;\r\nlet viewer:CreatureViewer|undefined;\r\nfunction tell(text:string){$('creatureStatus').textContent=text;}\r\n\r\n// Texture/colour/sparkle/metallic override for the selected part (Rank 4 registry, Rank 5 UI).\r\n// No override -> renders exactly like the legacy `styles[region]` index (old saves keep working).\r\nconst DEFAULT_MATERIAL:MaterialChoice={textureId:'clay',colorId:'#7f7d78',sparkle:0,metallic:0};\r\nfunction zeroFinish(d:Design):Design{return d.materials?{...d,materials:Object.fromEntries(Object.entries(d.materials).map(([r,c])=>[r,c&&{...c,sparkle:0,metallic:0}])) as Design['materials']}:d;} // hoisted: used by the load on L49\r\nfunction materialChoice():MaterialChoice{return shown().materials?.[selected]??DEFAULT_MATERIAL;}\r\nfunction setMaterial(patch:Partial<MaterialChoice>,rangeId:string|null=null){const base=shown();commit({...base,materials:{...base.materials,[selected]:{...materialChoice(),...patch}}},rangeId);}\r\n// #1/#102 Preview locked looks: a design that contains a locked body or locked material paints the\r\n// model from this draft only. It never enters `recipe`, undo/redo or storage -- commit() below is the\r\n// one shared guard every caller routes through (saveRecipe() also rejects locked ids as a backstop).\r\nlet previewDraft:Design|null=null,lastShown='';\r\nconst strip=document.createElement('p');strip.className='preview-strip';strip.hidden=true;document.querySelector('.preview-bay')!.append(strip); // over the LIVE PREVIEW readout, clear of the model\r\nconst previewing=()=>previewDraft!==null;\r\nfunction clearPreview(){previewDraft=null;}\r\nfunction shown():Design{return previewDraft??recipe;}\r\nconst idLocked=(id:string)=>registryLocked(id,shown().body);\r\nconst colorLocked=(id:string)=>registryLocked(id,shown().body,'color');\r\nfunction isLocked(design:Design):boolean{\r\n if(BODY_KEYS.some(key=>bodyLock(design[key])))return true;\r\n const ship=design.shipId??design.coach;if(ship!=='supportive'&&!ownedShipIds().includes(ship))return true;\r\n for(const r of REGIONS){const mc=regionChoice(design.materials,r);if(mc&&(registryLocked(mc.textureId,design.body)||registryLocked(mc.colorId,design.body,'color')))return true;}\r\n return false;\r\n}\r\nfunction previewMessage(design:Design):string{\r\n const body=bodyLock(design.body);if(body)return `Preview only · How to unlock ${PICKER_BODIES.find(b=>b.id===design.body)?.label||'this coach'}: ${body}.`;\r\n for(const region of [selected,...REGIONS.filter(r=>r!==selected)]){const choice=regionChoice(design.materials,region);if(!choice)continue;for(const [id,kind] of [[choice.textureId,'adaptation'],[choice.colorId,'colour']] as const)if(kind==='colour'?colorLocked(id):idLocked(id)){\r\n  const texture=kind==='adaptation'?TEXTURES.find(t=>t.id===id):undefined,palette=PALETTES.find(p=>p.id===id),color=COLORS.find(c=>c.id===id);\r\n  const detail=texture?.id==='coach-64-bit'?'Open the 64-bit Pixel Finish in a texture pack and complete this coach’s boss skin milestone':texture?'Open a texture pack':palette?.unlockRule==='aura-milestone'?`Reach aura day ${palette.unlockAtDay}`:'Earn it through the battle pass';\r\n  return `Preview only · How to unlock ${texture?.displayName||palette?.displayName||color?.displayName||kind}: ${detail}.`;\r\n }}return 'Preview only';\r\n}\r\n// Locked choices remain drafts. Available colors on owned bodies use an earned-material\r\n// baseline, so switching away from a locked preview can save without granting that preview.\r\nfunction pick(patch:Partial<MaterialChoice>){const base=shown();commit({...base,materials:{...base.materials,[selected]:{...(base.materials?.[selected]??DEFAULT_MATERIAL),...patch}}});}\r\n// #139: an Adaptation (texture) covers the whole coach: every part gets the same textureId, colours stay\r\n// per part. A part with no material of its own yet takes the texture's own colour (TextureDef.defaultColorId).\r\nfunction pickTexture(textureId:string){\r\n const base=shown(),texture=TEXTURES.find(t=>t.id===textureId);\r\n const defaultColor=texture&&!colorLocked(texture.defaultColorId)?texture.defaultColorId:DEFAULT_MATERIAL.colorId;\r\n const patch=(r:Region):Partial<MaterialChoice>=>{const current=base.materials?.[r];return {...(!current?{colorId:defaultColor}:{}),textureId};};\r\n commit({...base,materials:Object.fromEntries(REGIONS.map(r=>[r,{...(base.materials?.[r]??DEFAULT_MATERIAL),...patch(r)}])) as Design['materials']});\r\n}\r\nfunction endPreview(){if(!previewing())return false;clearPreview();render('Back to your look');return true;}\r\nfunction syncMaterials(){\r\n const mc=materialChoice();\r\n ($('textureId') as HTMLSelectElement).value=mc.textureId;\r\n for(const button of textureGrid.querySelectorAll<HTMLButtonElement>('[data-texture]'))button.setAttribute('aria-pressed',String(button.dataset.texture===mc.textureId));\r\n const texture=TEXTURES.find(t=>t.id===mc.textureId);\r\n ($('texturePreview') as HTMLImageElement).src=texture?texturePreviewDataURL(texture.familyId):'';\r\n // R20: the toggle shows each part's current colour as a dot; the single grid marks the active part's colour.\r\n const partColour=(c:typeof COLOUR_CHANNELS[number])=>regionChoice(shown().materials,c.regions[0])?.colorId??DEFAULT_MATERIAL.colorId;\r\n for(const b of document.querySelectorAll<HTMLButtonElement>('#colorSwatches [data-part]')){const c=COLOUR_CHANNELS.find(x=>x.id===b.dataset.part)!,on=c===activeChannel;b.setAttribute('aria-checked',String(on));b.tabIndex=on?0:-1;(b.firstElementChild as HTMLElement).style.background=colorTriad(partColour(c),false,shown().body)?.primary??partColour(c);}\r\n for(const b of document.querySelectorAll<HTMLButtonElement>('#colorSwatches [data-color]'))b.setAttribute('aria-pressed',String(b.dataset.color===partColour(activeChannel)));\r\n ($('materialClear') as HTMLButtonElement).disabled=!activeChannel.regions.some(r=>shown().materials?.[r]);\r\n}\r\nfunction syncMomOnly(){\r\n const show=shown().body===MOM_APPROVED_BODY_ID;\r\n for(const id of MOM_ONLY_FIELD_IDS){const el=document.getElementById(id);if(el)el.style.display=show?'':'none';}\r\n}\r\n\r\nconst coachRequirementDialog=document.createElement('dialog');coachRequirementDialog.className='coach-requirement-dialog';coachRequirementDialog.setAttribute('aria-labelledby','coachRequirementTitle');document.body.append(coachRequirementDialog);\r\nfunction showCoachRequirement(id:string){const info=coachRequirements(id);coachRequirementDialog.replaceChildren();const title=document.createElement('h2');title.id='coachRequirementTitle';title.textContent=coachName(id);const text=paintStyleText(document.createElement('p'),info.unlock);const close=document.createElement('button');close.type='button';close.textContent='Close';close.onclick=()=>coachRequirementDialog.close();coachRequirementDialog.append(title,text,close);if(!coachRequirementDialog.open)coachRequirementDialog.showModal();}\r\nfunction syncCoachIdentity(){const id=shown().body,locked=!!bodyLock(id),p=coachPresentation(id);$('coachDisplayName').textContent=coachName(id);$('coachPathSymbol').textContent=p.symbol;$('coachPathSymbol').title=p.track;$('coachUnlock').hidden=!locked;$('coachUnlock').onclick=()=>showCoachRequirement(id);}\r\n\r\nlet cosmeticCoach='';\r\nfunction sync(){\r\n const look=shown();\r\n if(cosmeticCoach!==look.body){cosmeticCoach=look.body;fillTextures();fillColours();}\r\n for(const key of ['body','eyeLayout','fingers','toes','eye','pupil','fur','iris','pupilSize','detail']){const input=$(key) as HTMLInputElement;input.value=String(look[key as keyof Design]);const out=document.getElementById(key+'Value');if(out)out.textContent=Number(input.value).toFixed(2);}\r\n\r\n for(const key of ['blackSclera','colourPupil'] as const)($(key) as HTMLInputElement).checked=look[key]===true;\r\n syncMomOnly();syncCoachIdentity();\r\n for(const button of bodyGrid.querySelectorAll<HTMLButtonElement>('[data-body]'))button.setAttribute('aria-pressed',String(button.dataset.body===look.body));\r\n syncShipRow();\r\n syncMaterials();\r\n syncSkinChoice();\r\n // R18: no unlock hints. The strip says only \"Preview\" (and flags a catalogue entry with no pattern yet).\r\n const notes:string[]=[],mc=look.materials?.[selected];\r\n if(mc?.textureId){const t=TEXTURES.find(x=>x.id===mc.textureId);if(t&&t.familyId<0)notes.push(`${t.displayName} pattern is coming`);}\r\n strip.hidden=!previewing()&&!notes.length;strip.textContent=[...(previewing()?['Preview']:[]),...notes].join(' · ');\r\n ($('undo') as HTMLButtonElement).disabled=!undo.length&&!previewing();($('redo') as HTMLButtonElement).disabled=!redo.length;\r\n}\r\nconst queue=new LatestPreview<{recipe:Design;message:string;preview:boolean}>(async job=>{if(!viewer)throw Error('3D is unavailable.');if(!await viewer.setRecipe(job.recipe,job.preview))throw Error('Preview was interrupted.');},(job,error)=>{\r\n if(error){tell('Could not update the preview. '+(error instanceof Error?error.message:String(error)));return;}\r\n ready=true;($('exportGLB') as HTMLButtonElement).disabled=job.preview;tell(job.message);\r\n});\r\nfunction render(message:string,persist=false){\r\n ready=false;($('exportGLB') as HTMLButtonElement).disabled=true;\r\n if(persist)try{persistSkinSettings();const owned=saveRecipe(localStorage,recipe,saved);message=owned===recipe?'Saved on this device':'Locked looks stay in preview until you unlock them';recipe=saved=owned;persistRecipeShip();window.dispatchEvent(new CustomEvent('myr5:recipe',{detail:recipe}));}catch{message='Storage unavailable. Download your recipe in Files to keep this design.';}\r\n sync();const look=shown();lastShown=JSON.stringify(look);\r\n tell('Updating preview…');queue.request({recipe:look,message,preview:previewing()});\r\n}\r\nfunction commit(next:Design,rangeId:string|null=null){\r\n next=zeroFinish(next);\r\n // Every writer shares this guard: any remaining locked choice stays a draft.\r\n // A completely earned design ends preview before entering undo history and storage.\r\n if(isLocked(next)){previewDraft=next;render(previewMessage(next));return;}\r\n clearPreview();\r\n if(JSON.stringify(next)===JSON.stringify(recipe)){if(JSON.stringify(shown())!==lastShown)render('Back to your look');return;}\r\n if(!rangeId||activeRange!==rangeId){undo.push(recipe);undo=undo.slice(-40);}activeRange=rangeId;redo=[];recipe=next;render('Coach updated',true);\r\n}\r\nfunction options(id:string,entries:ReadonlyArray<readonly [unknown,string]>){for(const [value,label] of entries){const o=document.createElement('option');o.value=String(value);o.textContent=label;$(id).append(o);}}\r\n// Keep the chosen workout introductions and every owned shape ahead of locked shapes. The\r\n// native select provides a name-only fallback; the sprite grid uses the same selection path.\r\nconst orderedBodies=()=>{const introductory=new Set(pathIntroCoachIds(readPerformanceProgress().paths));return [...PICKER_BODIES].sort((a,b)=>Number(!!bodyLock(a.id))-Number(!!bodyLock(b.id))||Number(introductory.has(b.id))-Number(introductory.has(a.id)));};\r\nlet spritePreviews:Awaited<ReturnType<typeof loadCoachSpritePreviews>>|null=null;\r\nconst bodyGrid=document.createElement('div');bodyGrid.className='material-grid coach-sprite-grid';bodyGrid.setAttribute('role','group');bodyGrid.setAttribute('aria-label','Creature shapes');\r\nfunction fillBodySprites(){if(!spritePreviews)return;bodyGrid.replaceChildren();for(const b of orderedBodies()){\r\n const sprite=spritePreviews.sprites.find(s=>s.id===b.id);if(!sprite)continue;\r\n const locked=!!bodyLock(b.id),presentation=coachPresentation(b.id);\r\n const card=document.createElement('div');card.className='coach-choice';card.dataset.locked=String(locked);\r\n const button=document.createElement('button');button.type='button';button.dataset.body=b.id;button.setAttribute('aria-label',`${b.label}, ${locked?'locked preview':'available'}`);button.setAttribute('aria-pressed',String(shown().body===b.id));\r\n const source=spritePreviews.art(sprite),canvas=document.createElement('canvas');canvas.width=source.width;canvas.height=source.height;canvas.getContext('2d')!.drawImage(source,0,0);\r\n const caption=document.createElement('span');caption.textContent=`${presentation.symbol} ${b.label}`;button.append(canvas,caption);button.onclick=()=>{const select=$('body') as HTMLSelectElement;select.value=b.id;select.dispatchEvent(new Event('change',{bubbles:true}));};if(!locked)sparkle(button,'body',b.id);card.append(button);\r\n const state=document.createElement('span');state.className='coach-choice-state';state.textContent=locked?'Locked':'Available';card.append(state);\r\n if(locked){const lock=document.createElement('button');lock.type='button';lock.className='coach-choice-lock';lock.textContent='\\u{1f512}';lock.setAttribute('aria-label',`How to unlock ${b.label}`);lock.onclick=()=>showCoachRequirement(b.id);card.append(lock);}bodyGrid.append(card);\r\n }}\r\nfunction fillBodies(){$('body').replaceChildren();for(const b of orderedBodies()){\r\n const o=document.createElement('option');o.value=b.id;o.textContent=b.label+(bodyLock(b.id)?' (Locked)':'');if(!bodyLock(b.id)&&isUnseen('body',b.id))o.dataset.sparkle=`body:${b.id}`;$('body').append(o);\r\n }fillBodySprites();}\r\noptions('eyeLayout',Object.entries(EYE_LAYOUTS).map(([key,value])=>[key,value.label]));options('eye',EYE_STYLES);options('pupil',PUPILS);\r\nfor(const [id,min,max] of [['fingers',2,6],['toes',1,6]] as const)options(id,Array.from({length:max-min+1},(_,i)=>[i+min,String(i+min)]));\r\n\r\nfunction focusPart(_region:Region,_frame=true){sync();}\r\n\r\nfor(const [id,region] of Object.entries({body:'body',eyeLayout:'eye',eye:'eye',pupil:'eye',iris:'eye',pupilSize:'eye',fingers:'arms',toes:'feet',fur:'collar',detail:'body'}))$(id).addEventListener('focus',()=>focusPart(region as Region));\r\n// Rank 4: texture dropdown (registry-driven) and colour/palette swatch grid, separate axes. #1: locked\r\n// entries keep a lock mark and their unlock source, and picking one previews it (see pick()).\r\nconst textureGrid=document.createElement('div');textureGrid.className='material-grid texture-grid';textureGrid.setAttribute('role','group');textureGrid.setAttribute('aria-label','Adaptation textures');\r\nfunction fillTextures(){$('textureId').replaceChildren();textureGrid.replaceChildren();for(const t of unlockedFirst(TEXTURES,t=>idLocked(t.id))){\r\n const o=document.createElement('option');o.value=t.id;o.textContent=t.displayName;const id=cosmeticId(shown().body,t.id);if(isGranted('texture',t.id,shown().body)&&isUnseen('texture',id))o.dataset.sparkle=`texture:${id}`;$('textureId').append(o);\r\n const locked=idLocked(t.id),button=document.createElement('button'),preview=document.createElement('img'),name=document.createElement('span');button.type='button';button.dataset.texture=t.id;button.setAttribute('aria-label',`${t.displayName}${locked?', locked':''}`);button.setAttribute('aria-pressed',String(materialChoice().textureId===t.id));preview.src=texturePreviewDataURL(t.familyId);preview.alt='';name.textContent=t.displayName;button.append(preview,name);if(locked){const icon=document.createElement('i');icon.className='texture-tile-lock';icon.textContent='🔒';icon.setAttribute('aria-hidden','true');button.append(icon);}button.onclick=()=>{const select=$('textureId') as HTMLSelectElement;select.value=t.id;select.dispatchEvent(new Event('change',{bubbles:true}));};textureGrid.append(button);\r\n}}\r\nfillBodies();fillTextures();\r\n$('panel-textures').querySelector('.panel-heading')!.after(textureGrid); // grid sits under the Textures heading\r\nvoid loadCoachSpritePreviews(document).then(previews=>{\r\n spritePreviews=previews;fillBodySprites();\r\n if(bodyGrid.childElementCount)($('body') as HTMLSelectElement).parentElement!.after(bodyGrid);\r\n}).catch(()=>{}); // Native name-only select remains available if sprite sheets cannot load.\r\nconst goldenToggle=document.createElement('button');goldenToggle.type='button';goldenToggle.textContent='Golden coach';goldenToggle.title='Unlocked by a ten-minute uninterrupted hold';\r\nfunction syncGolden(){goldenToggle.disabled=!goldenCoach(shown().body);goldenToggle.setAttribute('aria-pressed',String(shown().golden!==false&&goldenCoach(shown().body)));}\r\ngoldenToggle.onclick=()=>{if(goldenCoach(shown().body)){commit({...shown(),golden:shown().golden===false});syncGolden();}};$('body').after(goldenToggle);$('body').addEventListener('change',()=>queueMicrotask(syncGolden));window.addEventListener('myr5:performance-progress',()=>{refreshLists();syncGolden();});syncGolden();\r\nwatchSelect($('body') as HTMLSelectElement);watchSelect($('textureId') as HTMLSelectElement);\r\n$('textureId').addEventListener('change',()=>pickTexture(($('textureId') as HTMLSelectElement).value));\r\nconst colorSwatches=[\r\n ...COLORS.map(c=>({kind:'color' as const,id:c.id,name:c.displayName,background:c.primary})),\r\n ...PALETTES.map(p=>({kind:'palette' as const,id:p.id,name:p.displayName,background:`linear-gradient(90deg,${p.colors.join(',')})`})),\r\n];\r\nfunction swatchButton(s:{kind:'color'|'palette';id:string;name:string;background:string},onPick:()=>void){const locked=colorLocked(s.id),b=document.createElement('button');b.type='button';b.dataset.color=s.id;b.title=s.name;b.setAttribute('aria-label',s.name+(locked?', locked':''));b.style.background=s.background;if(locked){b.dataset.locked='';const icon=document.createElement('span');icon.className='swatch-lock';icon.textContent='\\u{1f512}';icon.setAttribute('aria-hidden','true');b.append(icon);}b.onclick=onPick;if(isGranted(s.kind,s.id,shown().body))sparkle(b,s.kind,cosmeticId(shown().body,s.id));return b;}\r\nfunction swatchGrid(grid:HTMLElement,onPick:(id:string)=>void){for(const s of unlockedFirst(colorSwatches,s=>colorLocked(s.id)))grid.append(swatchButton(s,()=>onPick(s.id)));}\r\n// R18 G3 colours; R20: one part toggle (Body/Head/Eyes) over a single colour grid, then a palette grid\r\n// that blends its colours across the selected part. Every colour def plus the free hexes no def starts with.\r\nconst rowColours=[...FREE_COLOURS.map((h,n)=>({id:h,name:FREE_COLOUR_NAMES[n],hex:h})),...COLORS.filter(c=>!FREE_COLOURS.includes(c.primary.toLowerCase())).map(c=>({id:c.id,name:c.displayName,hex:c.primary}))]; // free hexes first, then the locked defs\r\nlet activeChannel:typeof COLOUR_CHANNELS[number]=COLOUR_CHANNELS[0];\r\nfunction setChannel(channel:typeof COLOUR_CHANNELS[number],colorId:string){\r\n const draft=shown(),base=!colorLocked(colorId)&&BODY_KEYS.every(key=>!bodyLock(draft[key]))?keepOwned(draft,recipe):draft;\r\n commit({...base,materials:{...base.materials,...Object.fromEntries(channel.regions.map(r=>[r,{...(base.materials?.[r]??DEFAULT_MATERIAL),colorId}]))}});\r\n}\r\nfunction choosePart(channel:typeof COLOUR_CHANNELS[number]){activeChannel=channel;selected=channel.regions[0];sync();}\r\nconst colorRoot=$('colorSwatches');\r\nfunction fillColours(){colorRoot.replaceChildren();\r\nconst toggle=document.createElement('div');toggle.className='part-toggle';toggle.setAttribute('role','radiogroup');toggle.setAttribute('aria-label','Part to colour');\r\nfor(const channel of COLOUR_CHANNELS){const b=document.createElement('button');b.type='button';b.dataset.part=channel.id;b.setAttribute('role','radio');b.append(document.createElement('i'),channel.label);b.onclick=()=>choosePart(channel);\r\n b.onkeydown=e=>{const n=COLOUR_CHANNELS.indexOf(channel),d=e.key==='ArrowRight'||e.key==='ArrowDown'?1:e.key==='ArrowLeft'||e.key==='ArrowUp'?-1:0;if(!d)return;e.preventDefault();const next=COLOUR_CHANNELS[(n+d+COLOUR_CHANNELS.length)%COLOUR_CHANNELS.length];choosePart(next);(toggle.querySelector(`[data-part=\"${next.id}\"]`) as HTMLElement).focus();};\r\n toggle.append(b);}\r\nconst grid=document.createElement('div');grid.className='material-grid';grid.id='colourGrid';grid.setAttribute('role','group');grid.setAttribute('aria-label','Colours');\r\nfor(const c of unlockedFirst(rowColours,c=>colorLocked(c.id))){const b=swatchButton({kind:'color',id:c.id,name:c.name,background:c.hex},()=>setChannel(activeChannel,c.id));b.dataset.kind='color';grid.append(b);}\r\nconst label=document.createElement('strong');label.textContent='Multicolor palettes';\r\nconst palettes=document.createElement('div');palettes.className='material-grid';palettes.id='paletteGrid';palettes.setAttribute('role','group');palettes.setAttribute('aria-label','Palettes');\r\nfor(const p of unlockedFirst(PALETTES,p=>colorLocked(p.id))){const b=swatchButton({kind:'palette',id:p.id,name:p.displayName,background:`linear-gradient(90deg,${p.colors.join(',')})`},()=>setChannel(activeChannel,p.id));b.dataset.kind='palette';palettes.append(b);}\r\nconst paletteHelp=document.createElement('p');paletteHelp.className='help';paletteHelp.textContent='Map palette colors to the selected texture?s dark, middle and light areas. Other parts keep their own colors.';\r\ncolorRoot.append(toggle,grid,label,palettes,paletteHelp);}\r\nfillColours();\r\n// The Ship tab edits the recipe's existing shipId/shipColor and mirrors it to the arrival scene.\r\nconst shipRow=$('shipColourRow'),shipPick=$('shipPick') as HTMLSelectElement;\r\nconst shipLocked=(id:string)=>id!=='supportive'&&!coachEditorShips().includes(id);\r\nfunction saveShip(patch:{shipId?:Design['coach'];shipColor?:string|null}){\r\n const draft=shown(),base=BODY_KEYS.every(key=>!bodyLock(draft[key]))?keepOwned(draft,recipe):draft;\r\n commit({...base,...patch});\r\n}\r\nfunction persistRecipeShip(){const owner=editorOwner(),ship=recipe.shipId??recipe.coach;if(owner&&(ship==='supportive'||ownedShipIds().includes(ship))){const choice={ownerId:owner,ship,tint:recipe.shipColor??'#ffffff',colorId:rowColours.find(c=>c.hex===recipe.shipColor)?.id};localStorage.setItem(`${SHIP_SETTINGS_KEY}/${owner}`,JSON.stringify(choice));window.dispatchEvent(new CustomEvent('myr5:ship-customization',{detail:choice}));}}\r\nfunction fillShipRow(){\r\n shipPick.replaceChildren(...unlockedFirst(SHIP_CATALOG,c=>shipLocked(c.id)).map(c=>{const o=document.createElement('option');o.value=c.id;o.textContent=(shipLocked(c.id)?'\\u{1f512} ':'')+c.name;return o;}));\r\n shipRow.replaceChildren();const grid=document.createElement('div');grid.className='material-grid';grid.id='shipColourGrid';\r\n const original=document.createElement('button');original.type='button';original.dataset.ship='original';original.textContent='Orig.';original.title='Original ship colours';original.setAttribute('aria-label','Original ship colours');original.onclick=()=>saveShip({shipColor:null});grid.append(original);\r\n for(const c of unlockedFirst(rowColours,c=>colorLocked(c.id))){const b=swatchButton({kind:'color',id:c.id,name:c.name,background:c.hex},()=>{if(colorLocked(c.id)){tell('Locked for your ship too');return;}saveShip({shipColor:c.hex});});b.dataset.hex=c.hex;grid.append(b);}\r\n shipRow.append(grid);syncShipRow();\r\n}\r\nfunction syncShipRow(){const look=shown();shipPick.value=look.shipId??look.coach;const color=look.shipColor??null;for(const b of shipRow.querySelectorAll<HTMLButtonElement>('button')){b.setAttribute('aria-pressed',String(b.dataset.ship==='original'?color===null:b.dataset.hex===color));}queueMicrotask(()=>void previewShip());}\r\nshipPick.onchange=()=>{if(shipLocked(shipPick.value)){const name=SHIP_CATALOG.find(c=>c.id===shipPick.value)?.name||'this ship';tell(`Preview only · How to unlock ${name}: complete its workout milestone.`);syncShipRow();return;}saveShip({shipId:shipPick.value as Design['coach']});};\r\nfillShipRow();\r\nfunction refreshLists(){progress=loadProgress();fillBodies();fillTextures();fillColours();fillShipRow();sync();}window.addEventListener('myr5:battle-pass',refreshLists);\r\nwindow.addEventListener('storage',event=>{if(event.key?.startsWith(PERFORMANCE_KEY+'/')||event.key?.startsWith('myr5-unlocks-v2/'))refreshLists();});\r\n // A pick made while ownership was unknown stayed a preview; once it is owned, save it.\r\n function saveOwnedDraft(){if(!previewDraft||isLocked(previewDraft))return false;const next=previewDraft;clearPreview();commit(next);return true;}\r\n window.addEventListener('myr5:account-ready',()=>{recipe=saved=ownedLook(recipe);cosmeticCoach='';refreshLists();if(!saveOwnedDraft())render('Account cosmetics connected');});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden)saveOwnedDraft();});\r\n // R26: account-bridge clears before every re-verify (focus, visibilitychange), so a clear is often a blink, not a\r\n // sign-out. Keep the saved look and any pending pick: account-ready re-applies ownership and saves an owned pick.\r\n window.addEventListener('myr5:account-cleared',()=>{undo=[];redo=[];cosmeticCoach='';refreshLists();render('Account changed. Showing current ownership.');});\r\n window.addEventListener('myr5:login-ready',()=>void standaloneAccount.refresh());\r\n window.addEventListener('focus',()=>{if(!standaloneAccount.account)void standaloneAccount.refresh();});\r\n$('materialClear').onclick=()=>{const base=shown(),materials={...base.materials};for(const r of activeChannel.regions)delete materials[r];commit({...base,materials:Object.keys(materials).length?materials:undefined});};\r\n\r\nfor(const id of ['body','eyeLayout','fingers','toes','eye','pupil'])$(id).addEventListener('change',()=>{const input=$(id) as HTMLInputElement,value=['fingers','toes'].includes(id)?Number(input.value):input.value;\r\n // #102: a locked body previews like a locked texture (commit()'s guard below catches it). An owned\r\n // body is one of the three ways to leave a preview, so it starts from `recipe` (the last saved\r\n // look), not from `shown()` -- any locked tweaks made mid-preview are discarded, never carried\r\n // into the save. Other fields (face etc.) build on `shown()` so they keep an active preview going.\r\n if(id==='body'){const chosen=String(value),section=bodyLock(chosen);if(!section)clearPreview();\r\n  // Choosing a body always resets head, arms and legs to match it — Rank 5 removed the UI that\r\n  // let them diverge. A recipe saved before this change (with mismatched headFrom/armsFrom/feetFrom)\r\n  // still loads and renders mixed (assemble.ts/parseRecipe are unchanged); picking a body here just\r\n  // normalizes it going forward.\r\n  commit(section?{...shown(),body:chosen,headFrom:chosen,armsFrom:chosen,feetFrom:chosen}:selectOwnedBody(recipe,chosen));return;}\r\n commit({...shown(),[id]:value});});\r\nfor(const id of ['blackSclera','colourPupil'] as const)$(id).addEventListener('change',()=>{const next={...shown()};if(($(id) as HTMLInputElement).checked)next[id]=true;else delete next[id];commit(next);});\r\nfor(const id of ['fur','iris','pupilSize','detail']){\r\n const input=$(id) as HTMLInputElement;\r\n input.addEventListener('input',()=>commit({...shown(),[id]:Number(input.value)},id));\r\n for(const event of ['change','blur','pointercancel'])input.addEventListener(event,()=>{activeRange=null;});\r\n}\r\nconst tabs=[...document.querySelectorAll<HTMLButtonElement>('[data-menu]')];\r\nconst skinTab=document.createElement('button');skinTab.type='button';skinTab.id='tab-skin';skinTab.setAttribute('role','tab');skinTab.setAttribute('aria-controls','panel-skin');skinTab.setAttribute('aria-selected','false');skinTab.tabIndex=-1;skinTab.dataset.menu='skin';skinTab.textContent='Skins';skinTab.hidden=true;\r\nconst skinPanel=document.createElement('div');skinPanel.id='panel-skin';skinPanel.setAttribute('role','tabpanel');skinPanel.setAttribute('aria-labelledby','tab-skin');skinPanel.tabIndex=0;skinPanel.hidden=true;skinPanel.innerHTML='<div class=\"panel-heading\"><h2>Creature skins</h2></div><label>Installed skin<select id=\"skinChoice\"></select></label>';\r\n// #146: Skins and Ship sit after the option tabs, just above Files (Ian's \"Downloads\" tab).\r\nconst filesTab=$('tab-files') as HTMLButtonElement;filesTab.before(skinTab);document.querySelector('.console-scroll')?.append(skinPanel);tabs.splice(tabs.indexOf(filesTab),0,skinTab);\r\nconst shipTab=$('tab-ship') as HTMLButtonElement;\r\nconst shipPanel=$('panel-ship') as HTMLElement;\r\nconst SHIP_SETTINGS_KEY='myr5-ship-customization-v1';\r\nconst SKIN_SETTINGS_PREFIX='myr5-editor-skins-v1/account/';let skinOwner:string|null=null;\r\n// Persist from the current recipe for every edit, including Undo and Redo.\r\nfunction persistSkinSettings(){if(skinOwner)localStorage.setItem(SKIN_SETTINGS_PREFIX+skinOwner,JSON.stringify(Object.fromEntries(REGIONS.flatMap(region=>{const id=recipe.materials?.[region]?.textureId;return id?.startsWith('creature-')?[[region,id]]:[]}))));}\r\nfunction skinSettings(){if(!skinOwner)return{};try{const value=JSON.parse(localStorage.getItem(SKIN_SETTINGS_PREFIX+skinOwner)||'{}');return value&&typeof value==='object'&&!Array.isArray(value)?value:{}}catch{return{}}}\r\nconst editorOwner=()=>{const account=window.myr5AuthenticatedAccount;return typeof account==='string'?account:account?.user?.id;};\r\nfunction syncShipEditor(){shipTab.hidden=false;shipTab.setAttribute('aria-hidden','false');shipTab.tabIndex=shipTab.getAttribute('aria-selected')==='true'?0:-1;fillShipRow();}\r\n// The Ship tab's preview: only while that tab is open, only from this account's verified local ship bytes (never a\r\n// download). The choice itself is saved either way; the preview just shows it.\r\nlet shipPreview:ReturnType<typeof mountShipPreview>|null=null,shipPreviewOwner:string|null=null,shipPreviewEpoch=0;\r\nfunction closeShipPreview(){++shipPreviewEpoch;shipPreview?.dispose();shipPreview=null;shipPreviewOwner=null;}\r\nasync function previewShip(){\r\n const active=!shipTab.hidden&&shipTab.getAttribute(\"aria-selected\")===\"true\";\r\n const owner=editorOwner()||null,ship=shipPick.value,status=$('shipPreviewStatus'),retry=$('shipPreviewRetry') as HTMLButtonElement;\r\n const description=document.getElementById('shipDescription');if(description&&ship)description.textContent=shipDetails(ship).description;\r\n retry.hidden=true;\r\n // Leaving the tab (or the account) frees the preview: the bridge holds every owned ship's bytes.\r\n if(shipPreviewOwner!==owner||!active||!ship||(shipPreview&&!shipPreview.ids().includes(ship)))closeShipPreview();\r\n const run=++shipPreviewEpoch;\r\n if(!active||!ship){status.textContent='';return;}\r\n status.textContent='Loading your ship…';\r\n try{\r\n  if(!shipPreview){\r\n   const bridge=ship==='supportive'?{ownedShipIds:()=>['supportive'],getShipUrl:()=>'/pod/worlds/starter/supportive.glb'}:await localVerifiedBridge().catch(()=>null);\r\n   if(run!==shipPreviewEpoch||(editorOwner()||null)!==owner){bridge?.dispose?.();return;}\r\n   if(!bridge){status.textContent='3D model unavailable on this phone.';return;}\r\n   shipPreview=mountShipPreview($('shipPreview'),bridge);shipPreviewOwner=owner;\r\n  }\r\n  if(!shipPreview.ids().includes(ship)){status.textContent='3D model unavailable on this phone.';return;}\r\n  if(await shipPreview.show(ship,shown().shipColor??'#ffffff')&&run===shipPreviewEpoch)status.textContent='';\r\n }catch{if(run!==shipPreviewEpoch)return;status.textContent='Ship preview unavailable.';retry.hidden=false;}\r\n}\r\n$('shipPreviewRetry').onclick=()=>{closeShipPreview();void previewShip();};\r\nsyncShipEditor();watchSelect(shipPick);watchSelect($('skinChoice') as HTMLSelectElement);window.addEventListener('myr5:ship-scene-ready',event=>{if(acceptShipRevealComplete(event))syncShipEditor()});window.addEventListener('myr5:account-ready',syncShipEditor);window.addEventListener('myr5:account-cleared',syncShipEditor);window.addEventListener('storage',event=>{if(event.key?.startsWith(SHIP_SETTINGS_KEY+'/')||event.key==='myr5-ship-reveal-seen-v1')syncShipEditor()});document.addEventListener('visibilitychange',()=>{if(document.hidden)closeShipPreview();else if(shipTab.getAttribute('aria-selected')==='true')void previewShip();});\r\nlet skinSource:ReturnType<typeof createInstalledCreatureSkinSource>|null=null,pendingSkinSource:ReturnType<typeof createInstalledCreatureSkinSource>|null=null,skinEpoch=0,skinChoices:{id:string;displayName:string}[]=[];\r\nfunction syncSkinChoice(){const choice=$('skinChoice') as HTMLSelectElement,current=recipe.materials?.[selected]?.textureId||'';choice.value=skinChoices.some(s=>s.id===current)?current:'';}\r\nfunction renderSkinChoices(){const select=$('skinChoice') as HTMLSelectElement;select.replaceChildren(...skinChoices.map(skin=>{const option=document.createElement('option');option.value=skin.id;option.textContent=skin.displayName;if(isUnseen('creature-skin',skin.id))option.dataset.sparkle=`creature-skin:${skin.id}`;return option}));select.value='';syncSkinChoice();}\r\nasync function refreshSkinEditor(account=window.myr5AuthenticatedAccount){\r\n const run=++skinEpoch,owner=typeof account==='string'?account:typeof account?.user?.id==='string'?account.user.id:null,ownerChanged=owner!==skinOwner;\r\n const hadAppliedSkin=!ownerChanged&&Object.values(recipe.materials??{}).some(choice=>choice?.textureId.startsWith('creature-'));skinOwner=owner;\r\n if(ownerChanged){undo=[];redo=[];activeRange=null;skinSource?.dispose();skinSource=null;pendingSkinSource?.dispose();pendingSkinSource=null;skinChoices=[];viewer?.clearSkinState();viewer?.setSkinResolver(undefined);if(recipe.materials)recipe={...recipe,materials:Object.fromEntries(Object.entries(recipe.materials).map(([region,choice])=>[region,choice.textureId.startsWith('creature-')?{...choice,textureId:'clay'}:choice])) as Design['materials']};skinTab.hidden=true;skinTab.tabIndex=-1;skinTab.setAttribute('aria-hidden','true');if(skinTab.getAttribute('aria-selected')==='true')openMenu(document.getElementById('tab-body') as HTMLButtonElement);}\r\n renderSkinChoices();if(!owner){pendingSkinSource?.dispose();pendingSkinSource=null;render('Your coach is ready');return;}\r\n pendingSkinSource?.dispose();const source=createInstalledCreatureSkinSource({account});pendingSkinSource=source;tell('Checking installed skins');\r\n let rows:{id:string;displayName:string}[];try{rows=await source.list();}catch{const active=window.myr5AuthenticatedAccount,activeOwner=typeof active==='string'?active:active?.user?.id;if(run!==skinEpoch||activeOwner!==owner||skinOwner!==owner){source.dispose();if(pendingSkinSource===source)pendingSkinSource=null;return;}if(pendingSkinSource===source)pendingSkinSource=null;source.dispose();skinSource?.dispose();skinSource=null;skinChoices=[];viewer?.clearSkinState();viewer?.setSkinResolver(undefined);if(recipe.materials)recipe={...recipe,materials:Object.fromEntries(Object.entries(recipe.materials).map(([region,choice])=>[region,choice.textureId.startsWith('creature-')?{...choice,textureId:'clay'}:choice])) as Design['materials']};skinTab.hidden=true;skinTab.tabIndex=-1;skinTab.setAttribute('aria-hidden','true');if(skinTab.getAttribute('aria-selected')==='true')openMenu(document.getElementById('tab-body') as HTMLButtonElement);renderSkinChoices();render('Could not verify installed skins. Check your connection and try again.');return;}\r\n const active=window.myr5AuthenticatedAccount,activeOwner=typeof active==='string'?active:active?.user?.id;if(run!==skinEpoch||activeOwner!==owner||skinOwner!==owner){source.dispose();if(pendingSkinSource===source)pendingSkinSource=null;return;}if(pendingSkinSource===source)pendingSkinSource=null;\r\n if(!rows.length){source.dispose();skinSource?.dispose();skinSource=null;skinChoices=[];viewer?.clearSkinState();viewer?.setSkinResolver(undefined);if(recipe.materials)recipe={...recipe,materials:Object.fromEntries(Object.entries(recipe.materials).map(([region,choice])=>[region,choice.textureId.startsWith('creature-')?{...choice,textureId:'clay'}:choice])) as Design['materials']};skinTab.hidden=true;skinTab.setAttribute('aria-hidden','true');if(skinTab.getAttribute('aria-selected')==='true')openMenu(document.getElementById('tab-body') as HTMLButtonElement);renderSkinChoices();render(hadAppliedSkin?'No verified installed skins. Check your extra packs and try again.':'Your coach is ready');return;}\r\n const previous=skinSource;skinSource=source;previous?.dispose();skinChoices=rows.map(({id,displayName})=>({id,displayName}));viewer?.setSkinResolver(source.resolve);skinTab.hidden=false;skinTab.tabIndex=-1;skinTab.setAttribute('aria-hidden','false');const remembered=skinSettings();for(const region of REGIONS){const id=remembered[region];if(typeof id==='string'&&skinChoices.some(s=>s.id===id))recipe={...recipe,materials:{...recipe.materials,[region]:{...(recipe.materials?.[region]??DEFAULT_MATERIAL),textureId:id}}};}renderSkinChoices();render('Installed skins ready');\r\n}\r\nskinTab.onclick=()=>openMenu(skinTab);($('skinChoice') as HTMLSelectElement).addEventListener('change',()=>{const id=($('skinChoice') as HTMLSelectElement).value;if(!skinOwner||!skinChoices.some(s=>s.id===id))return;pickTexture(id);});\r\nwindow.addEventListener('myr5:account-ready',event=>void refreshSkinEditor((event as CustomEvent).detail));window.addEventListener('myr5:account-cleared',()=>void refreshSkinEditor(null));window.addEventListener('myr5:battle-pass',()=>void refreshSkinEditor());window.addEventListener('storage',event=>{if(event.key?.startsWith('myr5-battle-pass-ledger-v1/account/')||event.key==='myr5-battle-pass-ledger-v1')void refreshSkinEditor();});\r\nconst installedSections=(event:Event)=>{const detail=(event as CustomEvent).detail;if(!detail||typeof detail!=='object'||typeof detail.ownerId!=='string'||!Array.isArray(detail.sections)||!detail.sections.every((section:unknown)=>typeof section==='string'))return;const active=window.myr5AuthenticatedAccount,owner=typeof active==='string'?active:active?.user?.id;if(owner&&detail.ownerId===owner&&detail.sections.some((section:string)=>section.startsWith('track-')))void refreshSkinEditor(active);};window.addEventListener('myr5:sections-installed',installedSections);\r\nfunction openMenu(tab:HTMLButtonElement,frame=true){if(tab.hidden)return;activeRange=null;for(const b of tabs){const active=b===tab;b.setAttribute('aria-selected',String(active));b.tabIndex=active?0:-1;$(b.getAttribute('aria-controls')!).hidden=!active;}if(tab!==shipTab){shipTab.setAttribute('aria-selected','false');shipPanel.hidden=true;}if(tab.dataset.menu==='face')focusPart('eye',frame);else if(tab.dataset.menu==='body')focusPart('body',frame);else if(tab.dataset.menu==='materials')focusPart(selected,frame);else if(tab.dataset.menu==='skin')syncSkinChoice();void previewShip();(document.querySelector('.console-scroll') as HTMLElement).scrollTop=0;}\r\ntabs.forEach(b=>{b.onclick=()=>openMenu(b);b.onkeydown=event=>{const enabled=tabs.filter(tab=>!tab.hidden),index=enabled.indexOf(b);let next=index;if(event.key==='ArrowRight')next=(index+1)%enabled.length;else if(event.key==='ArrowLeft')next=(index+enabled.length-1)%enabled.length;else if(event.key==='Home')next=0;else if(event.key==='End')next=enabled.length-1;else return;event.preventDefault();openMenu(enabled[next]);enabled[next].focus();};});\r\n// Rank 5: the grid's \"apply to all parts\" copied a legacy style index; with the grid gone, this\r\n// copies the selected part's actual texture+colour+sparkle+metallic choice to every part instead.\r\n$('applyAll').onclick=()=>{const mc=materialChoice();commit({...shown(),materials:Object.fromEntries(REGIONS.map(r=>[r,mc])) as Design['materials']});};\r\n$('undo').onclick=()=>{if(endPreview()||!undo.length)return;activeRange=null;redo.push(recipe);recipe=undo.pop()!;render('Undo applied',true);};\r\n$('redo').onclick=()=>{if(!redo.length)return;activeRange=null;clearPreview();undo.push(recipe);recipe=redo.pop()!;render('Redo applied',true);};\r\n$('original').onclick=()=>{clearPreview();commit(fresh());};\r\n// Done (or the brand link) leaves on the owned look; the preview was never saved.\r\nfor(const a of document.querySelectorAll('.editor-header a,.coach-dock a'))a.addEventListener('click',()=>{endPreview();try{sessionStorage.removeItem(SHIP_GATE);}catch{}});\r\n$('importFile').addEventListener('change',async event=>{const input=event.target as HTMLInputElement,file=input.files?.[0];if(!file)return;try{if(file.size>MAX_IMPORT_BYTES)throw Error('Choose a MYR5 recipe smaller than 64 KB.');clearPreview();commit(importCreature(await file.text()));}catch(error){tell((error as Error).message);}finally{input.value='';}});\r\n$('exportRecipe').onclick=()=>download(new Blob([JSON.stringify(recipe,null,2)],{type:'application/json'}),'myr5-recipe.json');\r\n$('exportGLB').onclick=async()=>{if(!ready||!viewer||previewing())return;try{tell('Preparing your animated model…');download(await viewer.exportGLB(),'myr5-animated.glb');tell('Animated model downloaded');}catch(error){tell((error as Error).message);}};\r\n$('front').onclick=()=>viewer?.resetView();\r\n$('back').onclick=()=>viewer?.resetView(-1);\r\nconst zoom=$('zoom') as HTMLInputElement,setZoom=(z:number)=>{zoom.value=String(viewer?.setZoom(z)??z);};zoom.addEventListener('input',()=>setZoom(Number(zoom.value)));$('zoomIn').onclick=()=>setZoom(Number(zoom.value)+.1);$('zoomOut').onclick=()=>setZoom(Number(zoom.value)-.1);\r\n$('pauseMotion').onclick=()=>{if(!viewer)return;viewer.setPaused(!viewer.paused);$('pauseMotion').textContent=viewer.paused?'Play motion':'Pause motion';$('pauseMotion').setAttribute('aria-pressed',String(viewer.paused));};\r\nfunction applyMotion(){viewer?.setSettings({...settings,reduced:settings.reduced||systemMotion.matches});}\r\nsystemMotion.addEventListener('change',applyMotion);\r\nwindow.addEventListener('storage',event=>{if(event.key===RECIPE_KEY&&event.newValue){try{recipe=saved=ownedLook(importCreature(event.newValue));undo=[];redo=[];activeRange=null;render('Coach updated from another app tab');}catch{tell('An invalid coach update was ignored.');}}});\r\nconst motionIndicator=setInterval(()=>{const current=viewer?.motion?.current;if(!current)return;$('motionLabel').textContent=GESTURES[current].label;},250);\r\nconst disposePreviewIdle=mountPreviewIdle(document.querySelector<HTMLElement>('.preview-bay')!);\r\nwindow.addEventListener('pagehide',()=>{disposePreviewIdle();standaloneAccount.dispose();closeShipPreview();skinEpoch++;skinSource?.dispose();pendingSkinSource?.dispose();skinSource=null;pendingSkinSource=null;window.removeEventListener('myr5:sections-installed',installedSections);clearInterval(motionIndicator);queue.dispose();viewer?.dispose();});\r\n// D34 post-download: listen for body download state from service worker\r\nconst pendingBodyUrls=new Set<string>();let bodyDownloadTimer=0;const originalStatus='Your coach is ready';\r\nnavigator.serviceWorker?.addEventListener('message',({data})=>{\r\n if(data?.type!=='BODY_DOWNLOAD')return;\r\n clearTimeout(bodyDownloadTimer);\r\n if(data.state==='start')pendingBodyUrls.add(data.url);else pendingBodyUrls.delete(data.url);\r\n if(data.state==='unavailable'){tell('This body isn\\'t on this phone yet. Connect to the internet to download it.');bodyDownloadTimer=setTimeout(()=>{tell(originalStatus);},6000);}\r\n else if(pendingBodyUrls.size)tell('Downloading this body…');\r\n else tell(originalStatus);\r\n});\r\ntry{viewer=new CreatureViewer($('creatureStage'),base);applyMotion();viewer.resetView();setZoom(.94);render(initialError||originalStatus);void refreshSkinEditor();(window as any).myr5Companion={get recipe(){return recipe;},get viewer(){return viewer;},get ready(){return ready;},importRecipe:(raw:string)=>commit(importCreature(raw))};}\r\ncatch(error){tell('3D could not start. '+(error as Error).message);}\r\n","creature/source/editor.ts":"export * from './editor-workbench';\r\n","creature/source/idle-cycle.ts":"export const IDLE_INTERVAL=8000;\r\n// Pausing a tour never skips animations or interrupts a live coaching response.\r\nexport class IdleCycle<T extends string> {\r\n index=0;due=0;\r\n constructor(public gestures:readonly T[]){}\r\n next(now:number,eligible:boolean):T|null{\r\n  if(!eligible){this.due=now+IDLE_INTERVAL;return null;}\r\n  if(!this.due){this.due=now+IDLE_INTERVAL;return null;}\r\n  if(now<this.due||!this.gestures.length)return null;\r\n  const id=this.gestures[this.index++%this.gestures.length];this.due=now+IDLE_INTERVAL;return id;\r\n }\r\n}\r\n","creature/source/latest-preview.ts":"// Keep at most one assembly in flight; while it runs, retain only the latest edit.\r\nexport class LatestPreview<T>{\r\n private revision=0;private running=false;private pending:{value:T;revision:number}|null=null;private stopped=false;\r\n constructor(private apply:(value:T)=>Promise<unknown>,private settled:(value:T,error?:unknown)=>void){}\r\n request(value:T){if(this.stopped)return;this.pending={value,revision:++this.revision};void this.drain();}\r\n private async drain(){if(this.running||this.stopped)return;this.running=true;\r\n  while(this.pending&&!this.stopped){const job=this.pending;this.pending=null;let error:unknown;try{await this.apply(job.value);}catch(e){error=e;}\r\n   if(!this.stopped&&job.revision===this.revision)this.settled(job.value,error);\r\n   // Yield between assemblies so a rapid drag can replace the pending request.\r\n   if(this.pending)await new Promise<void>(resolve=>setTimeout(resolve,16));\r\n  }this.running=false;\r\n }\r\n dispose(){this.stopped=true;this.pending=null;this.revision++;}\r\n}\r\n","creature/source/limb-ownership.ts":"/**\r\n * Runtime ownership helpers for source meshes whose authoring cuts put pieces of\r\n * the lower limbs in the arm region. This module deliberately does not delete or\r\n * rewrite source triangles: callers receive triangle-index sets and can bake each\r\n * source triangle exactly once into its selected rig node.\r\n */\r\n\r\nexport type Vec3 = { x: number; y: number; z: number };\r\nexport type Box3Like = { min: Vec3; max: Vec3; isEmpty?: () => boolean };\r\nexport type GeometryLike = {\r\n  getAttribute(name: string): { count: number; getX(i: number): number; getY(i: number): number; getZ(i: number): number };\r\n  index?: { count: number; getX(i: number): number } | null;\r\n};\r\nexport type MeshLike = { geometry: GeometryLike; matrixWorld?: { elements: ArrayLike<number> } };\r\n\r\nexport type OwnershipOptions = {\r\n  /** World-space contact tolerance. Default: max(0.012, 0.008 * feet height). */\r\n  contactTolerance?: number;\r\n  /** Position weld tolerance for identifying shared triangle edges. */\r\n  weldTolerance?: number;\r\n  /** Keep a component in arms if it reaches the body's lower cut boundary. */\r\n  bodyLowerY?: number;\r\n  /** Extend the torso candidate band for a verified source with plates above the hip. */\r\n  bodyUpperFraction?: number;\r\n  /** Raise the small-component cap only for a source with measured larger torso shards. */\r\n  bodyComponentLimit?: number;\r\n  /** Require every corner of a candidate face to lie on the body surface. */\r\n  allVerticesOnBody?: boolean;\r\n};\r\n\r\n/** Copy a complete triangle subset without dropping vertex/morph attributes or reordering faces. */\r\nexport function triangleSubset(source: THREE.BufferGeometry, keep: (triangle: number) => boolean): THREE.BufferGeometry | null {\r\n  const position = source.getAttribute('position'), index = source.index, count = index?.count ?? position.count;\r\n  const remap = new Map<number, number>(), sourceVertices: number[] = [], outIndex: number[] = [];\r\n  for (let i = 0; i + 2 < count; i += 3) {\r\n    if (!keep(i / 3)) continue;\r\n    for (let j = 0; j < 3; j++) {\r\n      const old = index ? index.getX(i + j) : i + j;\r\n      let next = remap.get(old);\r\n      if (next === undefined) { next = sourceVertices.length; remap.set(old, next); sourceVertices.push(old); }\r\n      outIndex.push(next);\r\n    }\r\n  }\r\n  if (!outIndex.length) return null;\r\n  const out = new THREE.BufferGeometry();\r\n  const copyAttribute = (attribute: THREE.BufferAttribute | THREE.InterleavedBufferAttribute) => {\r\n    const interleaved = attribute as THREE.InterleavedBufferAttribute;\r\n    const isInterleaved = (attribute as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute === true;\r\n    const sourceArray = isInterleaved ? interleaved.data.array : attribute.array;\r\n    const stride = isInterleaved ? interleaved.data.stride : attribute.itemSize;\r\n    const offset = isInterleaved ? interleaved.offset : 0;\r\n    const values = new (sourceArray.constructor as any)(sourceVertices.length * attribute.itemSize);\r\n    for (let i = 0; i < sourceVertices.length; i++) for (let component = 0; component < attribute.itemSize; component++)\r\n      values[i * attribute.itemSize + component] = sourceArray[sourceVertices[i] * stride + offset + component];\r\n    return new THREE.BufferAttribute(values, attribute.itemSize, attribute.normalized);\r\n  };\r\n  for (const [name, attribute] of Object.entries(source.attributes)) out.setAttribute(name, copyAttribute(attribute as THREE.BufferAttribute));\r\n  for (const [name, attributes] of Object.entries(source.morphAttributes)) out.morphAttributes[name] = attributes.map(attribute => copyAttribute(attribute as THREE.BufferAttribute));\r\n  out.morphTargetsRelative = source.morphTargetsRelative;\r\n  out.setIndex(outIndex); out.computeBoundingBox(); out.computeBoundingSphere();\r\n  return out;\r\n}\r\n\r\ntype Point = [number, number, number];\r\ntype Triangle = { id: number; p: [Point, Point, Point]; center: Point; low: boolean };\r\n\r\nconst clampTolerance = (value: number) => Math.max(1e-5, value);\r\nconst distSq = (a: Point, b: Point) => (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;\r\nconst midpoint = (a: Point, b: Point): Point => [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];\r\n\r\nfunction worldPoint(mesh: MeshLike, i: number): Point {\r\n  const position = mesh.geometry.getAttribute('position');\r\n  const x = position.getX(i), y = position.getY(i), z = position.getZ(i);\r\n  const e = mesh.matrixWorld?.elements;\r\n  if (!e) return [x, y, z];\r\n  const w = e[3] * x + e[7] * y + e[11] * z + e[15];\r\n  const d = w && w !== 1 ? 1 / w : 1;\r\n  return [\r\n    (e[0] * x + e[4] * y + e[8] * z + e[12]) * d,\r\n    (e[1] * x + e[5] * y + e[9] * z + e[13]) * d,\r\n    (e[2] * x + e[6] * y + e[10] * z + e[14]) * d,\r\n  ];\r\n}\r\n\r\nfunction triangleData(mesh: MeshLike): Triangle[] {\r\n  const g = mesh.geometry, position = g.getAttribute('position'), count = g.index?.count ?? position.count;\r\n  const vertex = (i: number) => g.index ? g.index.getX(i) : i;\r\n  const triangles: Triangle[] = [];\r\n  for (let i = 0; i + 2 < count; i += 3) {\r\n    const p: [Point, Point, Point] = [worldPoint(mesh, vertex(i)), worldPoint(mesh, vertex(i + 1)), worldPoint(mesh, vertex(i + 2))];\r\n    triangles.push({id: i / 3, p, center: [(p[0][0] + p[1][0] + p[2][0]) / 3, (p[0][1] + p[1][1] + p[2][1]) / 3, (p[0][2] + p[1][2] + p[2][2]) / 3,], low: false});\r\n  }\r\n  return triangles;\r\n}\r\n\r\nfunction pointKey(p: Point, eps: number) { return `${Math.round(p[0] / eps)},${Math.round(p[1] / eps)},${Math.round(p[2] / eps)}`; }\r\nfunction edgeKey(a: Point, b: Point, eps: number) {\r\n  const ka = pointKey(a, eps), kb = pointKey(b, eps);\r\n  return ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`;\r\n}\r\n\r\n/** Build face adjacency from shared edges, including duplicated/non-indexed seam vertices. */\r\nfunction components(triangles: Triangle[], weldTolerance: number, eligible?: (triangle: Triangle) => boolean) {\r\n  const eligibleIds = new Set(triangles.filter(t => !eligible || eligible(t)).map(t => t.id));\r\n  const edges = new Map<string, number[]>();\r\n  for (const t of triangles) {\r\n    if (!eligibleIds.has(t.id)) continue;\r\n    for (let i = 0; i < 3; i++) {\r\n      const k = edgeKey(t.p[i], t.p[(i + 1) % 3], weldTolerance);\r\n      const list = edges.get(k);\r\n      if (list) list.push(t.id); else edges.set(k, [t.id]);\r\n    }\r\n  }\r\n  const neighbors = new Map<number, Set<number>>();\r\n  for (const ids of edges.values()) {\r\n    if (ids.length < 2) continue;\r\n    for (const id of ids) {\r\n      let set = neighbors.get(id); if (!set) neighbors.set(id, set = new Set());\r\n      for (const other of ids) if (other !== id) set.add(other);\r\n    }\r\n  }\r\n  const byId = new Map(triangles.map(t => [t.id, t]));\r\n  const seen = new Set<number>(), result: Triangle[][] = [];\r\n  for (const id of eligibleIds) {\r\n    if (seen.has(id)) continue;\r\n    seen.add(id); const queue = [id], group: Triangle[] = [];\r\n    for (let q = 0; q < queue.length; q++) {\r\n      const current = queue[q], triangle = byId.get(current)!; group.push(triangle);\r\n      for (const n of neighbors.get(current) ?? []) if (!seen.has(n)) { seen.add(n); queue.push(n); }\r\n    }\r\n    result.push(group);\r\n  }\r\n  return result;\r\n}\r\n\r\n/** Return face-component triangle ids, optionally restricted to a selected set. */\r\nexport function connectedTriangleComponents(mesh: MeshLike, eligibleIds?: ReadonlySet<number>, weldTolerance?: number): number[][] {\r\n  const triangles = triangleData(mesh);\r\n  if (!triangles.length) return [];\r\n  let min = Infinity, max = -Infinity;\r\n  for (const t of triangles) for (const p of t.p) { min = Math.min(min, p[0]); max = Math.max(max, p[0]); }\r\n  const eps = clampTolerance(weldTolerance ?? Math.max(1e-5, (max - min) * 1e-5));\r\n  return components(triangles, eps, eligibleIds ? t => eligibleIds.has(t.id) : undefined).map(group => group.map(t => t.id));\r\n}\r\n\r\n/** Index only open boundary edges of the feet mesh; interior surface contact is not enough. */\r\nfunction boundarySamples(meshes: Iterable<MeshLike>, cell: number, weldTolerance: number) {\r\n  const edges = new Map<string, {count:number; a:Point; b:Point}>();\r\n  for (const mesh of meshes) for (const t of triangleData(mesh)) for (let i = 0; i < 3; i++) {\r\n    const a = t.p[i], b = t.p[(i + 1) % 3], k = edgeKey(a, b, weldTolerance), row = edges.get(k);\r\n    if (row) row.count++;\r\n    else edges.set(k, {count:1, a, b});\r\n  }\r\n  const grid = new Map<string, Point[]>(), key = (p:Point) => `${Math.floor(p[0]/cell)},${Math.floor(p[1]/cell)},${Math.floor(p[2]/cell)}`;\r\n  let total = 0;\r\n  for (const edge of edges.values()) {\r\n    if (edge.count !== 1) continue;\r\n    // Subsamples tolerate small discrepancies from separate decimation passes.\r\n    for (let i = 0; i <= 4; i++) {\r\n      const t = i / 4, p:Point = [edge.a[0] + (edge.b[0] - edge.a[0]) * t, edge.a[1] + (edge.b[1] - edge.a[1]) * t, edge.a[2] + (edge.b[2] - edge.a[2]) * t];\r\n      const k = key(p), row = grid.get(k); if (row) row.push(p); else grid.set(k, [p]); total++;\r\n    }\r\n  }\r\n  return {grid, cell, total};\r\n}\r\n\r\n/** Index the complete body surface (including closed side walls, not just cut edges). */\r\nfunction surfaceSamples(meshes: Iterable<MeshLike>, cell: number) {\r\n  const grid = new Map<string, Point[]>(), key = (p:Point) => `${Math.floor(p[0]/cell)},${Math.floor(p[1]/cell)},${Math.floor(p[2]/cell)}`;\r\n  let total = 0;\r\n  const add = (p:Point) => {const k=key(p),row=grid.get(k);if(row)row.push(p);else grid.set(k,[p]);total++;};\r\n  for (const mesh of meshes) for (const t of triangleData(mesh)) {\r\n    for (let i=0;i<3;i++) {add(t.p[i]);add(midpoint(t.p[i],t.p[(i+1)%3]));}\r\n    add(t.center);\r\n  }\r\n  return {grid,cell,total};\r\n}\r\n\r\nfunction nearSample(p: Point, index: ReturnType<typeof samples>, tolerance: number) {\r\n  const {grid, cell} = index, cx = Math.floor(p[0] / cell), cy = Math.floor(p[1] / cell), cz = Math.floor(p[2] / cell), limit = tolerance * tolerance;\r\n  for (let x = cx - 1; x <= cx + 1; x++) for (let y = cy - 1; y <= cy + 1; y++) for (let z = cz - 1; z <= cz + 1; z++) {\r\n    for (const q of grid.get(`${x},${y},${z}`) ?? []) if (distSq(p, q) <= limit) return true;\r\n  }\r\n  return false;\r\n}\r\n\r\n/**\r\n * Return triangle ids in an `arms` mesh that should be owned by the feet node.\r\n * A candidate must be below the actual feet-region top and belong to a connected\r\n * low component that contacts the actual feet surface within a small tolerance.\r\n * This keeps ordinary hanging hands and disconnected quadruped front legs in\r\n * arms, while recovering lower-leg pieces cut into arms. No triangle is dropped.\r\n *\r\n * `feetMeshes` must be the visible meshes under the assembled source `feet` group,\r\n * in the same world transform as `mesh`. Pass an empty iterable to disable rescue.\r\n */\r\nexport function armFootTriangles(mesh: MeshLike, feetBox: Box3Like, feetMeshes: Iterable<MeshLike>, options: OwnershipOptions = {}): Set<number> {\r\n  if (feetBox.isEmpty?.()) return new Set();\r\n  const height = Math.max(0, feetBox.max.y - feetBox.min.y);\r\n  const tolerance = clampTolerance(options.contactTolerance ?? Math.max(0.012, height * 0.008));\r\n  const weld = clampTolerance(options.weldTolerance ?? Math.max(1e-5, tolerance * 0.02));\r\n  const footIndex = boundarySamples(feetMeshes, tolerance, weld);\r\n  if (!footIndex.total) return new Set();\r\n\r\n  const arm = triangleData(mesh), top = feetBox.max.y + tolerance;\r\n  for (const t of arm) t.low = t.center[1] <= top;\r\n  const low = arm.filter(t => t.low);\r\n  if (!low.length) return new Set();\r\n\r\n  const seeds = new Set<number>();\r\n  for (const t of low) {\r\n    if (t.p.some(p => nearSample(p, footIndex, tolerance)) ||\r\n        nearSample(midpoint(t.p[0], t.p[1]), footIndex, tolerance) ||\r\n        nearSample(midpoint(t.p[1], t.p[2]), footIndex, tolerance) ||\r\n        nearSample(midpoint(t.p[2], t.p[0]), footIndex, tolerance) ||\r\n        nearSample(t.center, footIndex, tolerance)) seeds.add(t.id);\r\n  }\r\n  if (!seeds.size) return new Set();\r\n\r\n  const selected = new Set<number>();\r\n  // Decide ownership from the complete source component. Building connectivity\r\n  // only from low faces can peel a claw/forearm off an otherwise continuous arm\r\n  // when the low section happens to touch a foot cut at the wrist.\r\n  for (const group of components(arm, weld)) {\r\n    const reachesBodyCut = options.bodyLowerY !== undefined && group.some(t => t.p.some(p => p[1] >= options.bodyLowerY! - tolerance));\r\n    // A complete grounded leg can end at the pelvis cut, too. Ground contact\r\n    // distinguishes it from a hanging claw whose top happens to meet that cut.\r\n    const grounded = group.some(t => t.p.some(p => p[1] <= feetBox.min.y + height * .1));\r\n    if ((!reachesBodyCut || grounded) && group.some(t => seeds.has(t.id)) && group.every(t => t.p.every(p => p[1] <= top)))\r\n      for (const t of group) selected.add(t.id);\r\n  }\r\n  return selected;\r\n}\r\n\r\n/**\r\n * Return small, complete lower-torso components in an arm cut that approach the\r\n * body's full surface, including closed side walls. The lower-body band and\r\n * component-size cap keep hands and continuous arms animated as arms; this is\r\n * for detached pelvis/torso shards that the cut-edge-only check can miss.\r\n */\r\nexport function armBodyTriangles(mesh: MeshLike, bodyBox: Box3Like, bodyMeshes: Iterable<MeshLike>, options: OwnershipOptions = {}): Set<number> {\r\n  if (bodyBox.isEmpty?.()) return new Set();\r\n  const height = Math.max(0, bodyBox.max.y - bodyBox.min.y);\r\n  if (!height) return new Set();\r\n  const tolerance = clampTolerance(options.contactTolerance ?? Math.max(0.018, height * 0.014));\r\n  const weld = clampTolerance(options.weldTolerance ?? Math.max(1e-5, tolerance * 0.02));\r\n  const bodyIndex = surfaceSamples(bodyMeshes, tolerance);\r\n  if (!bodyIndex.total) return new Set();\r\n  const triangles = triangleData(mesh), lowerTop = bodyBox.min.y + height * (options.bodyUpperFraction ?? 0.4);\r\n  if (!triangles.length) return new Set();\r\n  const maximum = Math.min(300, options.bodyComponentLimit ?? Math.max(200, Math.floor(triangles.length * 0.06)));\r\n  const selected = new Set<number>();\r\n  // Keep connected pieces intact. Lower arm-cut scraps are body owned when they\r\n  // sit in the pelvis band and approach any body surface; checking only open\r\n  // cut edges misses pieces against a closed body side wall.\r\n  for (const group of components(triangles, weld)) {\r\n    if (group.length > maximum) continue;\r\n    let minY=Infinity,maxY=-Infinity,nearBody=false;\r\n    for(const t of group) for(const p of t.p){minY=Math.min(minY,p[1]);maxY=Math.max(maxY,p[1]);if(!nearBody&&nearSample(p,bodyIndex,tolerance))nearBody=true;}\r\n    if(nearBody&&minY>=bodyBox.min.y-height*.03&&maxY<=lowerTop)for(const t of group)selected.add(t.id);\r\n  }\r\n  return selected;\r\n}\r\n\r\n/**\r\n * Select only torso-side faces in a narrow hip band when they are welded into a\r\n * larger animated arm component. This keeps the actual arm connected and moving\r\n * while assigning the small faces that overlap the body cut to BodyMotion.\r\n */\r\nexport function armBodyBandTriangles(mesh: MeshLike, bodyBox: Box3Like, bodyMeshes: Iterable<MeshLike>, options: OwnershipOptions = {}): Set<number> {\r\n  if (bodyBox.isEmpty?.()) return new Set();\r\n  const height = Math.max(0, bodyBox.max.y - bodyBox.min.y);\r\n  if (!height) return new Set();\r\n  const tolerance = clampTolerance(options.contactTolerance ?? Math.max(0.018, height * 0.014));\r\n  const bodyIndex = surfaceSamples(bodyMeshes, tolerance);\r\n  if (!bodyIndex.total) return new Set();\r\n  const bottom = bodyBox.min.y - height * 0.03;\r\n  const top = bodyBox.min.y + height * (options.bodyUpperFraction ?? 0.45);\r\n  const selected = new Set<number>();\r\n  for (const triangle of triangleData(mesh)) {\r\n    const points = [...triangle.p, midpoint(triangle.p[0], triangle.p[1]), midpoint(triangle.p[1], triangle.p[2]), midpoint(triangle.p[2], triangle.p[0]), triangle.center];\r\n    const onBody=options.allVerticesOnBody?triangle.p.every(point=>nearSample(point,bodyIndex,tolerance)):points.some(point=>nearSample(point,bodyIndex,tolerance));\r\n    if (triangle.center[1] >= bottom && triangle.center[1] <= top && onBody) selected.add(triangle.id);\r\n  }\r\n  return selected;\r\n}\r\n\r\n/** Arm-cut faces whose centers fall just below a source body's bottom cut; intended for continuous tail shells. */\r\nexport function trianglesBelowBodyCut(mesh: MeshLike, bodyCutY: number, cutTolerance = 0): Set<number> {\r\n  const selected = new Set<number>();\r\n  for (const triangle of triangleData(mesh)) if (triangle.center[1] <= bodyCutY + Math.max(0, cutTolerance)) selected.add(triangle.id);\r\n  return selected;\r\n}\r\n\r\n/** Move only complete, floor-grounded source components below a continuous-tail body cut. */\r\nexport function groundedComponentsBelowBodyCut(mesh: MeshLike, bodyCutY: number, feetBox: Box3Like, cutTolerance = 0): Set<number> {\r\n  const selected = new Set<number>(), triangles = triangleData(mesh), height = feetBox.max.y - feetBox.min.y;\r\n  if (!(height > 0)) return selected;\r\n  for (const component of connectedTriangleComponents(mesh)) {\r\n    let minY = Infinity, maxY = -Infinity;\r\n    for (const id of component) for (const point of triangles[id].p) { minY = Math.min(minY, point[1]); maxY = Math.max(maxY, point[1]); }\r\n    if (minY <= feetBox.min.y + height * .1 && maxY <= bodyCutY + Math.max(0, cutTolerance)) for (const id of component) selected.add(id);\r\n  }\r\n  return selected;\r\n}\r\n\r\n/** Select whole detached foot-cut components that are really side hands. */\r\nexport function detachedHandTriangles(\r\n  mesh: MeshLike,\r\n  feetBox: Box3Like,\r\n  feetMeshes: Iterable<MeshLike>,\r\n  armsBox: Box3Like,\r\n  armsMeshes: Iterable<MeshLike>,\r\n  bodyBox: Box3Like,\r\n  bodyMeshes: Iterable<MeshLike>,\r\n  lowerBodyBox: Box3Like,\r\n  options: {maxComponentTriangles?: number} = {},\r\n): Set<number> {\r\n  if (feetBox.isEmpty?.() || armsBox.isEmpty?.() || bodyBox.isEmpty?.() || lowerBodyBox.isEmpty?.()) return new Set();\r\n  const floor = feetBox.min.y, footHeight = feetBox.max.y - floor;\r\n  if (!(footHeight > 0) || !(bodyBox.max.y > bodyBox.min.y)) return new Set();\r\n  const armContact = armFootTriangles(mesh, armsBox, armsMeshes);\r\n  if (!armContact.size) return new Set();\r\n  const bodyContact = armFootTriangles(mesh, bodyBox, bodyMeshes);\r\n  const triangles = triangleData(mesh), weld = clampTolerance(Math.max(1e-5, footHeight * 0.00016));\r\n  const selected = new Set<number>();\r\n  for (const group of components(triangles, weld)) {\r\n    if (group.length > (options.maxComponentTriangles ?? 600) || !group.some(t => armContact.has(t.id)) || group.some(t => bodyContact.has(t.id))) continue;\r\n    let minY = Infinity, centerX = 0, count = 0;\r\n    for (const triangle of group) for (const p of triangle.p) { minY = Math.min(minY, p[1]); centerX += p[0]; count++; }\r\n    centerX /= count;\r\n    const ungrounded = minY > floor + footHeight * 0.1;\r\n    const outsideLowerBody = centerX < lowerBodyBox.min.x || centerX > lowerBodyBox.max.x;\r\n    if (ungrounded && outsideLowerBody) for (const triangle of group) selected.add(triangle.id);\r\n  }\r\n  return selected;\r\n}\r\n\r\n/**\r\n * Assign complete connected components to left (-1), right (+1), or neutral (0).\r\n * Neutral means the component crosses the centerline or is too close to call.\r\n * The caller must resolve neutral ownership for that source; a valid fused limb\r\n * component can remain animated with a centroid-side fallback when appropriate.\r\n */\r\nexport function connectedComponentSides(mesh: MeshLike, centerX = 0, options: OwnershipOptions = {}): Int8Array {\r\n  const triangles = triangleData(mesh), result = new Int8Array(triangles.length);\r\n  if (!triangles.length) return result;\r\n  // Dense roster meshes exceed mobile browsers' function-argument limits when\r\n  // every vertex is spread into Math.min/max. Scan the bounds at constant stack depth.\r\n  let minX = Infinity, maxX = -Infinity;\r\n  for (const t of triangles) for (const p of t.p) { minX = Math.min(minX, p[0]); maxX = Math.max(maxX, p[0]); }\r\n  const width = maxX - minX;\r\n  const weld = clampTolerance(options.weldTolerance ?? Math.max(1e-5, width * 1e-5));\r\n  const deadband = Math.max(1e-5, width * 0.015);\r\n  for (const group of components(triangles, weld)) {\r\n    let sum = 0, min = Infinity, max = -Infinity, count = 0;\r\n    for (const t of group) for (const p of t.p) { sum += p[0]; min = Math.min(min, p[0]); max = Math.max(max, p[0]); count++; }\r\n    const center = sum / count - centerX;\r\n    const crossesCenter = min < centerX - deadband && max > centerX + deadband;\r\n    let side = crossesCenter || Math.abs(center) <= deadband ? 0 : Math.sign(center);\r\n    if (!side && !crossesCenter && min >= centerX - weld) side = 1;\r\n    else if (!side && !crossesCenter && max <= centerX + weld) side = -1;\r\n    // A fused component spanning the center remains whole and neutral.\r\n    for (const t of group) result[t.id] = side;\r\n  }\r\n  return result;\r\n}\r\nimport * as THREE from 'three';\r\n","creature/source/motion.ts":"import * as T from 'three';\r\nimport type {CreatureRig} from './rig';\r\nexport const GESTURES={\r\n idle:{label:'At ease',duration:7,loop:true},listening:{label:'Listening',duration:4,loop:true},thinking:{label:'Thinking',duration:5,loop:true},speaking:{label:'Speaking',duration:3.6,loop:true},\r\n greet:{label:'Hello',duration:2.6,loop:false},agree:{label:'Yes / understood',duration:1.9,loop:false},correct:{label:'Gentle correction',duration:2.4,loop:false},encourage:{label:'Encouragement',duration:2.8,loop:false},celebrate:{label:'Celebrate',duration:3.2,loop:false},rest:{label:'Take a breath',duration:5,loop:true},laugh:{label:'Giant laugh',duration:3.4,loop:false},swipe:{label:'Platform sweep',duration:1.8,loop:false}\r\n ,awaken:{label:'Pod awakening',duration:4.8,loop:false},ready:{label:'Ready to train',duration:3.3,loop:false},victory:{label:'Effort earned',duration:3.8,loop:false},walk:{label:'Walking',duration:.9,loop:true},wiggle:{label:'Wiggle',duration:.6,loop:true}\r\n} as const;\r\nexport type Gesture=keyof typeof GESTURES;\r\nexport function gestureForCue(key:string){return ({count:'agree',complete:'celebrate',ready:'greet',tracking:'correct',setup:'listening',time:'encourage'} as Record<string,Gesture>)[key]||'speaking';}\r\nexport function samplePose(id:Gesture,t:number){\r\n const d=GESTURES[id].duration,u=t/d,s=Math.sin(u*Math.PI*2),envelope=Math.sin(Math.PI*u)**2;\r\n const p={headX:0,headY:0,headZ:0,bodyX:0,bodyZ:0,bodyY:0,leftX:0,leftZ:0,rightX:0,rightZ:0,breath:0,footLX:0,footRX:0,footLY:0,footRY:0,footLZ:0,footRZ:0};\r\n if(id==='idle'){p.headY=.045*s;p.headZ=.018*Math.sin(u*Math.PI*4);p.breath=.009*s;p.leftX=.02*s;p.rightX=-.015*s;}\r\n if(id==='listening'){p.headZ=-.085*(.5-.5*Math.cos(u*Math.PI*2));p.headX=.03*s;}\r\n if(id==='thinking'){p.headY=.10*s;p.headZ=.055*(1-Math.cos(u*Math.PI*2));p.rightX=-.06*s;}\r\n if(id==='speaking'){p.headX=.035*s;p.headZ=.02*Math.sin(u*Math.PI*4);p.rightX=.13*s;p.rightZ=.07*(1-Math.cos(u*Math.PI*2));p.leftX=-.06*s;}\r\n if(id==='greet'){p.rightZ=.27*envelope;p.rightX=.2*Math.sin(u*Math.PI*8)*envelope;p.headZ=-.06*envelope;}\r\n if(id==='agree'){p.headX=.15*Math.sin(u*Math.PI*4)*envelope;p.rightZ=.05*envelope;}\r\n if(id==='correct'){p.headY=.16*Math.sin(u*Math.PI*4)*envelope;p.rightZ=.12*envelope;}\r\n if(id==='encourage'){p.headX=.10*Math.sin(u*Math.PI*4)*envelope;p.leftZ=-.15*envelope;p.rightZ=.18*envelope;p.bodyZ=.02*s*envelope;}\r\n if(id==='celebrate'){p.leftZ=-.45*envelope;p.rightZ=.33*envelope;p.headZ=.06*s*envelope;p.bodyY=.08*envelope*(.5+.5*Math.sin(u*Math.PI*6));}\r\n if(id==='rest'){p.breath=.015*s;p.headX=.025*s;p.leftX=.025*s;}\r\n if(id==='laugh'){const chuckle=Math.sin(u*Math.PI*14)*envelope;p.headX=-.2*envelope+.12*chuckle;p.headZ=.08*chuckle;p.bodyX=-.05*envelope;p.bodyY=.055*chuckle;p.leftZ=-.18*envelope;p.rightZ=.2*envelope;p.breath=.045*Math.abs(chuckle);}\r\n if(id==='swipe'){const sweep=Math.sin(u*Math.PI)*Math.sin(u*Math.PI);p.bodyZ=-.12*sweep;p.headY=.22*Math.sin(u*Math.PI*2)*sweep;p.rightZ=1.15*Math.sin(u*Math.PI*2)*sweep;p.rightX=-.65*sweep;p.leftZ=-.16*sweep;p.bodyY=-.06*sweep;}\r\n if(id==='awaken'){p.headX=.24*(1-u)*envelope;p.headY=-.14*Math.sin(u*Math.PI*2)*envelope;p.bodyY=.08*envelope;p.leftZ=-.17*envelope;p.rightZ=.27*envelope;p.breath=.028*envelope;}\r\n if(id==='ready'){p.headX=.16*Math.sin(u*Math.PI*2)*envelope;p.leftZ=-.36*envelope;p.rightZ=.42*envelope;p.leftX=-.24*envelope;p.rightX=-.32*envelope;p.bodyY=-.045*envelope;p.breath=.018*envelope;}\r\n if(id==='victory'){p.leftZ=-.65*envelope;p.rightZ=.68*envelope;p.headX=-.12*envelope;p.headZ=.08*Math.sin(u*Math.PI*2)*envelope;p.bodyY=.09*envelope;p.breath=.026*envelope;}\r\n // One stride per foot: feet swing and lift in turn, the body waddles over the planted foot, arms swing opposite.\r\n if(id==='walk'){p.footLX=-.55*s;p.footRX=.55*s;p.footLZ=.18*s;p.footRZ=-.18*s;p.footLY=.16*Math.max(0,s);p.footRY=.16*Math.max(0,-s);p.bodyY=.05*Math.abs(s);p.bodyZ=.09*s;p.headZ=-.05*s;p.leftX=.45*s;p.rightX=-.45*s;p.leftZ=-.08;p.rightZ=.08;}\r\n // Dangling by the head: arms and feet swing fast in opposite directions with a small body twist.\r\n if(id==='wiggle'){const w=Math.sin(u*Math.PI*4);p.leftZ=-.35*w;p.rightZ=.35*w;p.footLZ=.22*w;p.footRZ=-.22*w;p.bodyZ=.08*w;p.headZ=-.04*w;}\r\n return p;\r\n}\r\nexport function createClips(rig:CreatureRig){\r\n return Object.entries(GESTURES).map(([name,meta])=>{\r\n  const count=Math.ceil(meta.duration*24),times=Array.from({length:count+1},(_,i)=>meta.duration*i/count);\r\n  const pose=times.map(t=>samplePose(name as Gesture,t)),tracks:T.KeyframeTrack[]=[];\r\n  const rotations:Record<string,(p:ReturnType<typeof samplePose>)=>number[]>={HeadMotion:p=>[p.headX,p.headY,p.headZ],BodyMotion:p=>[p.bodyX,0,p.bodyZ],ArmLeft:p=>[p.leftX,0,p.leftZ],ArmRight:p=>[p.rightX,0,p.rightZ]};\r\n  for(const [node,get] of Object.entries(rotations)){tracks.push(new T.QuaternionKeyframeTrack(node+'.quaternion',times,pose.flatMap(p=>{const [x,y,z]=get(p);return new T.Quaternion().setFromEuler(new T.Euler(x,y,z)).toArray();})));}\r\n  tracks.push(new T.VectorKeyframeTrack('BodyMotion.position',times,pose.flatMap(p=>[0,rig.rest.BodyMotion.position.y+p.bodyY,0])));\r\n  for(const [node,x,y,z] of [['FootLeft','footLX','footLY','footLZ'],['FootRight','footRX','footRY','footRZ']] as const){const rest=rig.rest[node].position;tracks.push(new T.QuaternionKeyframeTrack(node+'.quaternion',times,pose.flatMap(p=>new T.Quaternion().setFromEuler(new T.Euler(p[x],0,0)).toArray())));tracks.push(new T.VectorKeyframeTrack(node+'.position',times,pose.flatMap(p=>[rest.x,rest.y+p[y],rest.z+p[z]])));}\r\n  tracks.push(new T.VectorKeyframeTrack('BodyMotion.scale',times,pose.flatMap(p=>[1+p.breath*.3,1+p.breath,1+p.breath*.3])));\r\n  for(const node of Object.keys(rig.nodes).filter(n=>n.startsWith('EyeBlink'))){const mid=meta.duration*.55;tracks.push(new T.VectorKeyframeTrack(node+'.scale',[0,mid,mid+.10,mid+.23,meta.duration],[1,1,1,1,1,1,1,.06,1,1,1,1,1,1,1]));}\r\n  return new T.AnimationClip(name,meta.duration,tracks);\r\n });\r\n}\r\nexport class MotionController {\r\n rig:CreatureRig;mixer:T.AnimationMixer;clips:T.AnimationClip[];current:Gesture='idle';action:T.AnimationAction;ambient=true;reduced=false;paused=false;amount=1.5;clock=0;nextBlink=3.4;blinkStart=-100;\r\n actions=new Map<Gesture,T.AnimationAction>();\r\n private rawPose=new Map<string,{position:T.Vector3;quaternion:T.Quaternion;scale:T.Vector3}>();\r\n // #149 meditation: asleep = eyes held shut, no gesture or ambient motion, just a slow 4 s breathing bob (none under reduced motion).\r\n sleeping=false;\r\n constructor(rig:CreatureRig){this.rig=rig;this.clips=createClips(rig);this.mixer=new T.AnimationMixer(rig.root);for(const clip of this.clips){const action=this.mixer.clipAction(clip);const id=clip.name as Gesture;action.setLoop(GESTURES[id].loop?T.LoopRepeat:T.LoopOnce,GESTURES[id].loop?Infinity:1);action.clampWhenFinished=!GESTURES[id].loop;this.actions.set(id,action);}this.action=this.actions.get('idle')!;this.action.play();this.mixer.addEventListener('finished',()=>this.play('idle'));}\r\n play(id:Gesture){if(!Object.hasOwn(GESTURES,id))return;if(this.current===id&&GESTURES[id].loop)return;const next=this.actions.get(id)!;for(const action of this.actions.values())if(action!==this.action&&action!==next)action.stop();if(next===this.action)next.reset().play();else{this.action.fadeOut(.22);next.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(.22).play();}this.action=next;this.current=id;}\r\n update(dt:number){\r\n  if(this.paused)return;dt=Math.min(.05,Math.max(0,dt));this.clock+=dt;\r\n  // AnimationMixer may skip writes when a sampled track value has not changed. Since the pose below\r\n  // is weighted in place, feeding that weighted pose into the next sample can accumulate drift.\r\n  // Restore the last unweighted mixed pose before updating: this preserves constant non-rest\r\n  // plateaus while ensuring motion strength is applied once, even at the supported 1.5x setting.\r\n  for(const [name,node] of Object.entries(this.rig.nodes)){if(name.startsWith('EyeBlink'))continue;const pose=this.rawPose.get(name),rest=this.rig.rest[name];node.position.copy(pose?.position??rest.position);node.quaternion.copy(pose?.quaternion??rest.quaternion);node.scale.copy(pose?.scale??rest.scale);}\r\n  this.mixer.update(dt);\r\n  for(const [name,node] of Object.entries(this.rig.nodes)){if(name.startsWith('EyeBlink'))continue;let pose=this.rawPose.get(name);if(!pose){pose={position:new T.Vector3(),quaternion:new T.Quaternion(),scale:new T.Vector3()};this.rawPose.set(name,pose);}pose.position.copy(node.position);pose.quaternion.copy(node.quaternion);pose.scale.copy(node.scale);}\r\n  // Scale the finished mixed pose from rest, so editor changes never accumulate.\r\n  const strength=this.reduced||this.sleeping?0:(this.current==='idle'&&!this.ambient?0:this.amount);\r\n  for(const [name,node] of Object.entries(this.rig.nodes)){const rest=this.rig.rest[name];if(name.startsWith('EyeBlink'))continue;const position=node.position.clone(),scale=node.scale.clone();node.quaternion.slerpQuaternions(rest.quaternion,node.quaternion.clone(),strength);node.position.copy(rest.position).lerp(position,strength);node.scale.copy(rest.scale).lerp(scale,strength);}\r\n  if(this.clock>=this.nextBlink){this.blinkStart=this.clock;this.nextBlink=this.clock+3.8+Math.random()*2.5;}\r\n  const phase=(this.clock-this.blinkStart)/.23,blink=!this.reduced&&this.ambient&&phase>=0&&phase<=1?Math.sin(phase*Math.PI)**2:0;\r\n  for(const [name,node] of Object.entries(this.rig.nodes))if(name.startsWith('EyeBlink'))node.scale.y=1-.94*(this.sleeping?1:blink);\r\n  if(this.sleeping&&!this.reduced){const b=Math.sin(this.clock*Math.PI/2),body=this.rig.nodes.BodyMotion,rest=this.rig.rest.BodyMotion;if(body){body.position.y=rest.position.y+.03*b;body.scale.set(rest.scale.x*(1+.006*b),rest.scale.y*(1+.02*b),rest.scale.z*(1+.006*b));}}\r\n }\r\n neutral(){this.mixer.stopAllAction();for(const [name,node] of Object.entries(this.rig.nodes)){const r=this.rig.rest[name];node.position.copy(r.position);node.scale.copy(r.scale);node.quaternion.copy(r.quaternion);if(!name.startsWith('EyeBlink')){let pose=this.rawPose.get(name);if(!pose){pose={position:new T.Vector3(),quaternion:new T.Quaternion(),scale:new T.Vector3()};this.rawPose.set(name,pose);}pose.position.copy(r.position);pose.quaternion.copy(r.quaternion);pose.scale.copy(r.scale);}}}\r\n dispose(){this.mixer.stopAllAction();this.mixer.uncacheRoot(this.rig.root);}\r\n}\r\n","creature/source/petal-wisp-ownership.ts":"import * as THREE from 'three';\r\nimport {connectedTriangleComponents, triangleSubset} from './limb-ownership';\r\n\r\nconst IYENG_ARC = 'roster/10-petal-wisp--stylized_creature_3d_model';\r\n\r\n/** Restore Iyeng-Arc's complete opposite fin/arm after the broad foot-cut repair. */\r\nexport function correctPetalWispArmOwnership(source: THREE.Group, sourceId: string): number {\r\n  if (sourceId !== IYENG_ARC) return 0;\r\n  source.updateWorldMatrix(true, true);\r\n  const arms = source.getObjectByName('arms'), feet = source.getObjectByName('feet');\r\n  if (!arms || !feet) return 0;\r\n  let armMaterial: THREE.Material | undefined;\r\n  arms.traverse(o => { if (!armMaterial && o instanceof THREE.Mesh && o.visible && !Array.isArray(o.material)) armMaterial = o.material; });\r\n  if (!armMaterial) return 0;\r\n  const toArms = arms.matrixWorld.clone().invert();\r\n  let recovered = 0;\r\n  const meshes: THREE.Mesh[] = [];\r\n  feet.traverse(o => { if (o instanceof THREE.Mesh && o.visible && !Array.isArray(o.material)) meshes.push(o); });\r\n  for (const mesh of meshes) {\r\n    const position = mesh.geometry.getAttribute('position'), index = mesh.geometry.index, selected = new Set<number>();\r\n    for (const component of connectedTriangleComponents(mesh)) {\r\n      if (component.length !== 581 && component.length !== 732) continue;\r\n      const bounds = new THREE.Box3();\r\n      for (const face of component) for (let k = 0; k < 3; k++) {\r\n        const vertex = index ? index.getX(face * 3 + k) : face * 3 + k;\r\n        bounds.expandByPoint(new THREE.Vector3().fromBufferAttribute(position, vertex).applyMatrix4(mesh.matrixWorld));\r\n      }\r\n      const isArmSection=component.length===581&&bounds.min.x>.59&&bounds.min.x<.62&&bounds.max.x>.88&&bounds.max.x<.92&&bounds.min.y>.67&&bounds.min.y<.70&&bounds.max.y>1.27&&bounds.max.y<1.33;\r\n      const isAttachedFin=component.length===732&&bounds.min.x>.28&&bounds.min.x<.31&&bounds.max.x>.59&&bounds.max.x<.62&&bounds.min.y>.75&&bounds.min.y<.78&&bounds.max.y>1.46&&bounds.max.y<1.48;\r\n      if (isArmSection||isAttachedFin) component.forEach(face => selected.add(face));\r\n    }\r\n    if (!selected.size) continue;\r\n    const original = mesh.geometry, armPart = triangleSubset(original, face => selected.has(face)), remainder = triangleSubset(original, face => !selected.has(face));\r\n    if (!armPart) continue;\r\n    armPart.applyMatrix4(toArms.clone().multiply(mesh.matrixWorld));\r\n    recovered += selected.size;\r\n    if (remainder) { mesh.geometry = remainder; original.dispose(); }\r\n    else { mesh.removeFromParent(); original.dispose(); }\r\n    const recoveredArm = new THREE.Mesh(armPart, armMaterial);\r\n    recoveredArm.name = 'Iyeng-Arc recovered arm section';\r\n    recoveredArm.userData.region = 'arms';\r\n    recoveredArm.userData.petalWispRecoveredArmTriangles = selected.size;\r\n    arms.add(recoveredArm);\r\n    const prior = Number(mesh.userData.recoveredArmFootTriangles) || 0;\r\n    mesh.userData.recoveredArmFootTriangles = Math.max(0, prior - selected.size);\r\n  }\r\n  source.updateWorldMatrix(true, true);\r\n  source.userData.petalWispRecoveredArmTriangles = recovered;\r\n  return recovered;\r\n}\r\n","creature/source/phone-motion.ts":"import {subscribePhoneMotion as subscribe} from '../../character-phone-sensor.mjs';\r\n\r\nexport type PhoneMotionSample={gx:number;gy:number;shake:number;angularSpeed:number;timeSeconds:number};\r\n\r\n/** Keep the sensor connection scoped to the customizer viewer's visible lifetime. */\r\nexport function subscribePhoneMotion(callback:(sample:PhoneMotionSample)=>void){\r\n return subscribe(callback);\r\n}\r\n","creature/source/phone.ts":"import {CreatureViewer} from './viewer';\r\nimport {GESTURES,gestureForCue,type Gesture} from './motion';\r\nimport {loadRecipe,RECIPE_KEY,MOTION_KEY,motionSettings,importCreature,REGIONS} from './profile';\r\nimport type {Cinematic} from './cinematic-shots';\r\nimport {IdleCycle} from './idle-cycle';\r\nimport {createInstalledCreatureSkinSource} from '../../modules/materials/installed-creature-skins.mjs';\r\nexport {importCreature};\r\nconst base=new URL('/creature/',location.origin).href;\r\nconst view=document.getElementById('view');\r\nif(view){\r\n const card=document.createElement('aside');card.className='myr5-companion-card';card.setAttribute('aria-label','Your MYR5 coach');\r\n const stage=document.createElement('div');stage.className='myr5-companion-stage';\r\n const edit=document.createElement('a');edit.href=new URL('index.html',base).href;edit.textContent='Customize MYR5';edit.className='myr5-customize';\r\n const status=document.createElement('span');status.className='myr5-companion-status';status.textContent='Waking up…';status.setAttribute('role','status');\r\n const hide=document.createElement('button');hide.className='myr5-hide';hide.textContent='Hide creature';hide.setAttribute('aria-expanded','true');\r\n card.append(stage,status,edit,hide);view.append(card);\r\n let viewer:CreatureViewer|null=null,previewing:string|null=null,sleeping=false,lastCaption='',lastCueAt=0,holdUntil=0,epoch=0,closed=false,shown=true,voiceActive=false,walking=false,skinSource:ReturnType<typeof createInstalledCreatureSkinSource>|null=null,pendingSkinSource:ReturnType<typeof createInstalledCreatureSkinSource>|null=null,skinOwner:string|null=null,skinNotice:string|null=null,skinEpoch=0;\r\n let yielded=false;\r\n let coachLoadState:'pending'|'ready'|'failed'='pending',coachLoadError:string|null=null;\r\n type DanceLease={host:HTMLElement;parent:Node;next:ChildNode|null;marker:Comment;hidden:boolean;style:string|null;originalViewer:CreatureViewer;viewer:CreatureViewer|null;stage:CreatureViewer['stage'];paused:boolean;awake:boolean;settings:CreatureViewer['settings'];gesture:Gesture;walking:boolean;sleeping:boolean;homeMoving:boolean;homeElapsed:number;lastFrame:any};\r\n let danceLease:DanceLease|null=null;\r\n let danceRestore:DanceLease|null=null;\r\n function restoreDanceViewer(lease:DanceLease){if(!viewer)return;viewer.setSettings(lease.settings);viewer.setStage(lease.stage);viewer.setPaused(lease.paused);viewer.play(lease.gesture);viewer.homeMoving=lease.homeMoving;viewer.homeElapsed=lease.homeElapsed;viewer.setAwake(lease.awake);if(viewer.motion)viewer.motion.sleeping=lease.sleeping;}\r\n function dance({host}:{host?:HTMLElement}={}){\r\n  if(!host||!host.isConnected||closed||gpuBlocked()||!viewer||viewer.disposed||!viewer.rig||card.dataset.ready!=='true'||viewer.renderer.getContext().isContextLost())return false;\r\n  if(danceLease?.host===host&&danceLease.viewer===viewer&&viewer.externalDance)return true;\r\n  if(danceLease&&danceLease.host!==host)endDance();\r\n  if(!danceLease){\r\n   const parent=stage.parentNode;if(!parent)return false;const marker=document.createComment('MYR5 coach return');parent.insertBefore(marker,stage);\r\n   danceLease={host,parent,next:stage.nextSibling,marker,hidden:stage.hidden,style:stage.getAttribute('style'),originalViewer:viewer,viewer:null,stage:viewer.stage,paused:viewer.paused,awake:!!viewer.frame,settings:{...viewer.settings},gesture:viewer.gesture,walking,sleeping,homeMoving:viewer.homeMoving,homeElapsed:viewer.homeElapsed,lastFrame:null};\r\n   stage.hidden=false;stage.style.width='100%';stage.style.height='100%';stage.style.minHeight='0';stage.dataset.danceLease='true';host.append(stage);\r\n  }\r\n  if(!viewer.beginDance()){endDance();return false;}danceLease.viewer=viewer;if(danceLease.lastFrame)viewer.danceFrame(danceLease.lastFrame);return true;\r\n }\r\n function danceFrame(frame:{landmarks?:any[];mirrored?:boolean;active?:boolean;coachYaw?:number;visibleBody?:number}){\r\n  if(!danceLease)return false;\r\n  // A paused sample is a hold, including across a selected-recipe reload.\r\n  if(frame?.active!==false)danceLease.lastFrame=frame;\r\n  return !!viewer&&danceLease.viewer===viewer&&card.dataset.ready==='true'&&viewer.danceFrame(frame);\r\n }\r\n function endDance(){\r\n  const lease=danceLease;if(!lease)return;danceLease=null;\r\n  if(lease.marker.parentNode===lease.parent)lease.parent.insertBefore(stage,lease.marker);else lease.parent.insertBefore(stage,lease.next?.parentNode===lease.parent?lease.next:null);lease.marker.remove();\r\n  stage.hidden=lease.hidden;if(lease.style===null)stage.removeAttribute('style');else stage.setAttribute('style',lease.style);delete stage.dataset.danceLease;\r\n  walking=lease.walking;sleeping=lease.sleeping;\r\n  const same=viewer===lease.originalViewer;viewer?.endDance();\r\n  if(!viewer||card.dataset.ready!=='true')danceRestore=lease;\r\n  if(viewer&&!same)restoreDanceViewer(lease);\r\n  if(viewer?.motion)viewer.motion.sleeping=sleeping;\r\n  viewer?.resize();\r\n }\r\n const gpuBlocked=()=>[...((globalThis as any).__myr5Graphics?.claims||[])].some((c:any)=>!c.keep?.includes('coach'));\r\n const tour=new IdleCycle((Object.keys(GESTURES) as Gesture[]).filter(g=>g!=='walk'&&g!=='wiggle'));\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n function settings(){if(danceLease)return;let s=motionSettings(null);try{s=motionSettings(localStorage.getItem(MOTION_KEY));}catch{}viewer?.setSettings({...s,reduced:s.reduced||reduced.matches});}\r\n async function updateRecipe(raw?:string){const run=++epoch;coachLoadState='pending';coachLoadError=null;card.dataset.ready='false';status.textContent=skinNotice||'Loading coach…';try{if(!viewer){if(gpuBlocked()){yielded=true;return false;}viewer=new CreatureViewer(stage,base,false);settings();}const recipe=raw?importCreature(raw):previewing?importCreature(previewing):loadRecipe(localStorage),active=window.myr5AuthenticatedAccount,owner=typeof active==='string'?active:active?.user?.id;let choices:Record<string,string>={};try{choices=owner?JSON.parse(localStorage.getItem('myr5-editor-skins-v1/account/'+owner)||'{}'):{};}catch{}for(const region of REGIONS){const choice=recipe.materials?.[region];if(choice?.textureId.startsWith('creature-'))choice.textureId='clay';const id=choices?.[region];if(typeof id==='string'&&id.startsWith('creature-'))recipe.materials={...recipe.materials,[region]:{textureId:id,colorId:choice?.colorId??'#7f7d78',sparkle:choice?.sparkle??0,metallic:choice?.metallic??0}};}const loaded=await viewer.setRecipe(recipe);if(run!==epoch||closed)return;if(!loaded){coachLoadState='failed';coachLoadError='Coach model did not finish loading.';return;}if(viewer.motion)viewer.motion.sleeping=sleeping;card.dataset.ready='true';coachLoadState='ready';status.textContent=skinNotice||'MYR5';if(danceLease){dance({host:danceLease.host});return true;}if(danceRestore){restoreDanceViewer(danceRestore);danceRestore=null;return true;}const giant=document.body.dataset.screen==='rest';viewer.setStage(viewer.stage==='overlay'?'overlay':giant?'encounter':'pod');viewer.play(giant?'laugh':'greet');holdUntil=performance.now()+(giant?3700:2800);return true;}catch(error){if(run===epoch&&!closed){coachLoadState='failed';coachLoadError=(error as Error).message||'Coach could not load.';status.textContent=skinNotice||coachLoadError;return false;}}}\r\n async function refreshInstalledSkins(account=window.myr5AuthenticatedAccount){const run=++skinEpoch,owner=typeof account==='string'?account:typeof account?.user?.id==='string'?account.user.id:null,ownerChanged=owner!==skinOwner;skinOwner=owner;if(ownerChanged){skinNotice=null;skinSource?.dispose();skinSource=null;pendingSkinSource?.dispose();pendingSkinSource=null;viewer?.clearSkinState();viewer?.setSkinResolver(undefined);void updateRecipe();}if(!owner){pendingSkinSource?.dispose();pendingSkinSource=null;return;}pendingSkinSource?.dispose();const source=createInstalledCreatureSkinSource({account});pendingSkinSource=source;let rows;try{rows=await source.list();}catch{const active=window.myr5AuthenticatedAccount,activeOwner=typeof active==='string'?active:active?.user?.id;if(closed||run!==skinEpoch||activeOwner!==owner||skinOwner!==owner){source.dispose();if(pendingSkinSource===source)pendingSkinSource=null;return;}if(pendingSkinSource===source)pendingSkinSource=null;source.dispose();skinSource?.dispose();skinSource=null;viewer?.clearSkinState();viewer?.setSkinResolver(undefined);skinNotice='Could not verify installed skins. Check your extra packs and try again.';void updateRecipe();return;}const active=window.myr5AuthenticatedAccount,activeOwner=typeof active==='string'?active:active?.user?.id;if(closed||run!==skinEpoch||activeOwner!==owner||skinOwner!==owner){source.dispose();if(pendingSkinSource===source)pendingSkinSource=null;return;}if(pendingSkinSource===source)pendingSkinSource=null;if(!rows.length){source.dispose();skinSource?.dispose();skinSource=null;viewer?.clearSkinState();viewer?.setSkinResolver(undefined);skinNotice='No verified installed skins. Check your extra packs and try again.';void updateRecipe();return;}const previous=skinSource;skinSource=source;previous?.dispose();skinNotice=null;viewer?.setSkinResolver(source.resolve);void updateRecipe();}\r\n function play(id:Gesture,duration=GESTURES[id]?.duration*1000+300){if(danceLease)return;if(walking&&id!=='walk')return;if(!shown||viewer?.cinematicKind||!Object.hasOwn(GESTURES,id))return;viewer?.play(id);holdUntil=performance.now()+duration;}\r\n // Live cue keys are explicit; user-authored text never becomes executable data.\r\n const cue=(event:Event)=>{if(danceLease)return;const d=(event as CustomEvent).detail;if(!d||typeof d.key!=='string')return;lastCueAt=performance.now();play(gestureForCue(d.key));};\r\n window.addEventListener('myr5:cue',cue);\r\n const response=(event:Event)=>{if(danceLease)return;const detail=(event as CustomEvent).detail,state=detail?.state;const map:Record<string,Gesture>={listening:'listening',thinking:'thinking',speaking:'speaking',idle:'idle'};if(!Object.hasOwn(map,state))return;voiceActive=state==='speaking';if(state==='speaking'&&performance.now()<holdUntil&&viewer?.motion?.current!=='speaking')return;if(state==='idle'&&viewer?.motion?.current!=='speaking'&&performance.now()<holdUntil)return;play(map[state],state==='idle'?0:15000);};\r\n window.addEventListener('myr5:response',response);\r\n // Captions from existing controls also prompt a speaking pose. The explicit\r\n // cue takes precedence, and repeated caption text does not restart gestures.\r\n const caption=document.getElementById('coachCaption');const observer=new MutationObserver(()=>{if(danceLease)return;const text=caption?.textContent||'';if(text&&text!==lastCaption){lastCaption=text;if(performance.now()-lastCueAt>120&&performance.now()>holdUntil)play('speaking',Math.min(14000,1200+text.length*80));}});if(caption)observer.observe(caption,{childList:true,characterData:true,subtree:true});\r\n const timer=setInterval(()=>{if(danceLease)return;const now=performance.now(),busy=closed||!viewer?.motion||card.dataset.ready!=='true'||!shown||document.hidden||!!document.querySelector('dialog[open]')||!!viewer?.cinematicKind||now<holdUntil||voiceActive||document.body.dataset.tracking==='true'||reduced.matches;viewer?.setHomeMotion(!busy);const next=tour.next(now,!busy);if(closed||!viewer?.motion||card.dataset.ready!=='true'||!shown||document.hidden)return;if(next)viewer.play(next);else if(now>holdUntil&&!viewer.cinematicKind&&(voiceActive||document.body.dataset.tracking==='true')){const cue=walking?'walk':voiceActive?'speaking':'listening';if(viewer.motion.current!==cue)viewer.play(cue);}const label=GESTURES[viewer.motion.current].label;if(status.textContent!==(skinNotice||label))status.textContent=skinNotice||label;},200);\r\n const storage=(event:StorageEvent)=>{if(event.key===MOTION_KEY)settings();if(event.key===RECIPE_KEY)void updateRecipe(event.newValue||undefined);if(event.key?.startsWith('myr5-battle-pass-ledger-v1/account/')||event.key==='myr5-battle-pass-ledger-v1')void refreshInstalledSkins();};window.addEventListener('storage',storage);reduced.addEventListener('change',settings);\r\n const accountReady=(event:Event)=>void refreshInstalledSkins((event as CustomEvent).detail);const accountCleared=()=>void refreshInstalledSkins(null),battlePass=()=>void refreshInstalledSkins();const installedSections=(event:Event)=>{const detail=(event as CustomEvent).detail;if(!detail||typeof detail!=='object'||typeof detail.ownerId!=='string'||!Array.isArray(detail.sections)||!detail.sections.every((section:unknown)=>typeof section==='string'))return;const active=window.myr5AuthenticatedAccount,owner=typeof active==='string'?active:active?.user?.id;if(owner&&detail.ownerId===owner&&detail.sections.some((section:string)=>section.startsWith('track-')))void refreshInstalledSkins(active);};window.addEventListener('myr5:account-ready',accountReady);window.addEventListener('myr5:account-cleared',accountCleared);window.addEventListener('myr5:battle-pass',battlePass);window.addEventListener('myr5:sections-installed',installedSections);\r\n const recipeChanged=(event:Event)=>void updateRecipe(JSON.stringify((event as CustomEvent).detail));window.addEventListener('myr5:recipe',recipeChanged);\r\n // The Vault claims the GPU (modules/graphics-lifecycle.mjs): give this context back for real (dispose alone keeps it on iOS)\r\n // and rebuild the coach when the claim ends. myr5Creature calls are null-safe meanwhile.\r\n const graphicsClaim=(event:Event)=>{const held=viewer;if(!held||(event as CustomEvent).detail?.keep?.includes('coach'))return;viewer=null;if(danceLease)danceLease.viewer=null;yielded=true;epoch++;coachLoadState='pending';coachLoadError=null;card.dataset.ready='false';const canvas=held.renderer.domElement;(event as CustomEvent).detail?.wait?.(new Promise<void>(done=>{canvas.addEventListener('webglcontextlost',()=>done(),{once:true});setTimeout(done,250);held.dispose();try{held.renderer.forceContextLoss();}catch{}}));};\r\n const graphicsFree=()=>{if(yielded&&!closed&&!gpuBlocked()){yielded=false;void updateRecipe();}};\r\n window.addEventListener('myr5:graphics-claim',graphicsClaim);window.addEventListener('myr5:graphics-free',graphicsFree);\r\n hide.onclick=()=>{if(danceLease)return;shown=!shown;stage.hidden=!shown;status.hidden=!shown;viewer?.setPaused(!shown);hide.textContent=shown?'Hide creature':'Show creature';hide.setAttribute('aria-expanded',String(shown));};\r\n document.getElementById('start')?.addEventListener('click',()=>play('greet'));document.getElementById('stop')?.addEventListener('click',()=>play('rest',5000));\r\n window.addEventListener('pagehide',()=>{endDance();closed=true;epoch++;skinEpoch++;clearInterval(timer);observer.disconnect();skinSource?.dispose();pendingSkinSource?.dispose();skinSource=null;pendingSkinSource=null;viewer?.dispose();window.removeEventListener('myr5:cue',cue);window.removeEventListener('myr5:response',response);window.removeEventListener('storage',storage);window.removeEventListener('myr5:recipe',recipeChanged);window.removeEventListener('myr5:graphics-claim',graphicsClaim);window.removeEventListener('myr5:graphics-free',graphicsFree);window.removeEventListener('myr5:account-ready',accountReady);window.removeEventListener('myr5:account-cleared',accountCleared);window.removeEventListener('myr5:battle-pass',battlePass);window.removeEventListener('myr5:sections-installed',installedSections);reduced.removeEventListener('change',settings);},{once:true});\r\n // Workout overlay: legs walk while the coach moves across the room; cue gestures wait until it stops.\r\n (window as any).myr5Creature={play,dance,danceFrame,endDance,walk:(on:boolean)=>{if(danceLease)return;if(on===walking)return;walking=on;if(on){holdUntil=0;viewer?.play('walk');}else if(viewer?.motion?.current==='walk')viewer.play('idle');},face:(yaw:number)=>{if(!danceLease)viewer?.face(yaw);},stage:(value:'pod'|'encounter'|'overlay')=>{if(danceLease)return;viewer?.setAwake(true);viewer?.setStage(value);if(value!=='overlay'){walking=false;viewer?.face(0);if(viewer?.motion?.current==='walk')viewer.play('idle');}},park:()=>{if(!danceLease)viewer?.setAwake(false);},cinematic:(kind:Cinematic|null,elapsed=0)=>{if(danceLease)return;viewer?.cinematic(kind,elapsed);holdUntil=performance.now()+(kind?10000:500);},setMaxFps:(fps:number|null)=>{if(!danceLease)viewer?.setMaxFps(fps);},stats:()=>({...viewer?.stats(),coachLoadState,coachLoadError,coachLoadGeneration:epoch,stage:viewer?.stage,cinematic:viewer?.cinematicKind,handsVersion:2,danceLeased:!!danceLease,danceReady:!!danceLease&&danceLease.viewer===viewer&&!!viewer?.externalDance&&card.dataset.ready==='true'})};void updateRecipe();void refreshInstalledSkins();\r\n // #149 meditation. preview: render the saved recipe with these parts swapped in (e.g. the meditation boss body) without\r\n // saving it; null returns to the saved recipe. Resolves false and falls back to the saved coach when that model can't load\r\n // (an optional roster body that isn't on the phone, offline). sleep: eyes shut, still, a slow breathing bob.\r\n async function preview(parts:object|null){if(danceLease)return false;previewing=parts?JSON.stringify({...loadRecipe(localStorage),...parts}):null;const loaded=await updateRecipe();if(loaded===false&&previewing){previewing=null;void updateRecipe();}return loaded!==false;}\r\n Object.assign((window as any).myr5Creature,{preview,sleep:(on:boolean)=>{if(danceLease)return;sleeping=on;if(viewer?.motion)viewer.motion.sleeping=on;}});\r\n window.addEventListener('pageshow',event=>{if(event.persisted)location.reload();});\r\n}\r\n","creature/source/pod-camera.ts":"import * as T from 'three';\r\nimport {regionFrame} from './creator/camera-focus';\r\n\r\n// Eight seconds out, eight seconds back. The cosine eases to a stop at\r\n// each end instead of snapping between the face and the wider coach view.\r\nexport function podCameraFrame(camera:T.PerspectiveCamera,head:T.Box3,body:T.Box3,seconds:number){\r\n const close=regionFrame(camera,head),wide=regionFrame(camera,body);\r\n if(!close||!wide)return null;\r\n const amount=(1-Math.cos(Math.PI*Math.max(0,seconds)/8))/2;\r\n return {target:close.target.lerp(wide.target,amount),position:close.position.lerp(wide.position,amount),maxDistance:Math.max(14,wide.distance*2)};\r\n}\r\n","creature/source/preview-idle.ts":"// Activity is local to the coach window; scrolling the customizer does not cover it again.\r\nexport function mountPreviewIdle(bay:HTMLElement,delay=3000){\r\n let timer:ReturnType<typeof setTimeout>|undefined;\r\n const pointers=new Set<number>(),events=new AbortController();\r\n const schedule=()=>{clearTimeout(timer);if(!pointers.size)timer=setTimeout(()=>bay.dataset.idle='true',delay);};\r\n const show=()=>{bay.dataset.idle='false';schedule();};\r\n bay.addEventListener('pointerdown',event=>{pointers.add(event.pointerId);show();},{signal:events.signal});\r\n bay.addEventListener('pointermove',show,{signal:events.signal});\r\n const release=(event:PointerEvent)=>{if(pointers.delete(event.pointerId))show();};\r\n window.addEventListener('pointerup',release,{signal:events.signal});\r\n window.addEventListener('pointercancel',release,{signal:events.signal});\r\n for(const type of ['wheel','focusin','keydown','input'])bay.addEventListener(type,show,{signal:events.signal});\r\n show();\r\n return ()=>{clearTimeout(timer);events.abort();};\r\n}\r\n","creature/source/profile.ts":"import {fresh,parseRecipe} from './creator/design';\r\nexport {fresh,REGIONS,LABELS,STYLES,PICKER_STYLES,BODIES,PICKER_BODIES} from './creator/design';\r\nexport {EYE_LAYOUTS} from './creator/eye-layouts';\r\nexport {PUPILS} from './creator/pupils';\r\nexport {COACHES} from './creator/coaching';\r\nexport const RECIPE_KEY='myr5-recipe-v1';\r\nexport const MOTION_KEY='myr5-motion-v1';\r\nexport const MAX_IMPORT_BYTES=64*1024;\r\nexport function importCreature(raw:string){\r\n if(typeof raw!=='string'||new TextEncoder().encode(raw).length>MAX_IMPORT_BYTES)throw Error('Choose a MYR5 recipe smaller than 64 KB.');\r\n try {\r\n  const data=JSON.parse(raw);\r\n  if(!data||typeof data!=='object'||Array.isArray(data))throw Error();\r\n  if(data.format!==undefined&&data.format!=='myr5-companion')throw Error();\r\n  if(data.format==='myr5-companion'&&data.version!==1)throw Error();\r\n  return parseRecipe(JSON.stringify(data.format==='myr5-companion'?data.recipe:data));\r\n } catch {throw Error('This is not a supported MYR5 recipe. Export “Save editable recipe · JSON” from the creature creator.');}\r\n}\r\nexport function loadRecipe(storage:Storage){const raw=storage.getItem(RECIPE_KEY);return raw?importCreature(raw):fresh();}\r\nexport function exportCompanion(recipe:unknown){return JSON.stringify({format:'myr5-companion',version:1,rigVersion:1,recipe:importCreature(JSON.stringify(recipe))},null,2);}\r\nexport function motionSettings(raw:string|null){\r\n try {const d=JSON.parse(raw||'{}');return {amount:1.5,reduced:typeof d?.reduced==='boolean'?d.reduced:false,ambient:d?.ambient!==false};}\r\n catch{return {amount:1.5,reduced:false,ambient:true};}\r\n}\r\n","creature/source/quadruped-ownership.ts":"import * as T from 'three';\r\nimport { connectedTriangleComponents } from './limb-ownership';\r\n\r\n/**\r\n * Re-home the lower, source-authored faces of the nine Four-legged coaches into\r\n * four unambiguous animation groups. Roster `arms`/`feet` are usually fore/hind\r\n * pairs, not bilateral humanoid limbs; some of their cut faces also land in\r\n * `body` or `head`. This pass uses the four spatial foot clusters as ownership\r\n * seeds and never drops or duplicates a source triangle.\r\n *\r\n * Call after GLTF load and before region fitting/style wrapping. A consumer can\r\n * route the cloned meshes by `mesh.userData.quadrupedLimbNode` and use the joint\r\n * hints stored on `source.userData.quadrupedLimbHints`.\r\n */\r\n\r\nexport type QuadrupedLimbNode = 'ArmLeft' | 'ArmRight' | 'FootLeft' | 'FootRight';\r\nexport type QuadrupedJointHint = { position: [number, number, number]; side: -1 | 1; fore: boolean };\r\n\r\ntype Point = { x: number; y: number; z: number };\r\ntype Triangle = { mesh: T.Mesh; region: string; id: number; points: [Point, Point, Point]; center: Point };\r\ntype Cluster = { center: Point; samples: number; radius: number; node?: QuadrupedLimbNode; fore?: boolean; side?: -1 | 1 };\r\n\r\nconst REGIONS = ['head', 'body', 'arms', 'feet'] as const;\r\nconst QUADRUPED_IDS = new Set([\r\n  'roster/18-quad-all--dragon_creature_3d_model',\r\n  'roster/18-quad-all--fantasy_creature_3d_model1',\r\n  'roster/18-quad-all--fantasy_creature_3d_model3',\r\n  'roster/18-quad-all--four-legged_robot_3d_model',\r\n  'roster/18-quad-all--quadruped_robot_3d_model1',\r\n  'roster/18-quad-all--quadruped_robot_3d_model',\r\n  'roster/18-quad-all--robotic_dog_3d_model',\r\n  'roster/18-quad-all--stylized_quadruped_3d_model',\r\n  'roster/18-quad-all--wooden_four-legged_robot_3d_model',\r\n  'roster/23-blob-texture-bodies--blob_creature_3d_model',\r\n]);\r\nconst REPAIR_IDS = new Set([\r\n  'roster/18-quad-all--fantasy_creature_3d_model1',\r\n  'roster/18-quad-all--fantasy_creature_3d_model3',\r\n  'roster/18-quad-all--four-legged_robot_3d_model',\r\n  'roster/18-quad-all--quadruped_robot_3d_model',\r\n  'roster/18-quad-all--quadruped_robot_3d_model1',\r\n  'roster/18-quad-all--robotic_dog_3d_model',\r\n  'roster/18-quad-all--wooden_four-legged_robot_3d_model',\r\n  'roster/23-blob-texture-bodies--blob_creature_3d_model',\r\n]);\r\nconst BODY_FRAGMENT_ONLY_IDS = new Set([\r\n  'roster/18-quad-all--quadruped_robot_3d_model',\r\n  'roster/18-quad-all--stylized_quadruped_3d_model',\r\n]);\r\nconst STYLIZED_QUAD_ID = 'roster/18-quad-all--stylized_quadruped_3d_model';\r\nconst WERNER_APRON_ID = 'roster/18-quad-all--quadruped_robot_3d_model1';\r\n\r\nconst sq = (v: number) => v * v;\r\nconst dist2XZ = (a: Point, b: Point) => sq(a.x - b.x) + sq(a.z - b.z);\r\nconst add = (a: Point, b: Point): Point => ({ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z });\r\nconst scale = (a: Point, n: number): Point => ({ x: a.x * n, y: a.y * n, z: a.z * n });\r\n\r\nfunction closestSurfacePoint(meshes: T.Mesh[], point: T.Vector3) {\r\n  const triangle = new T.Triangle(), closest = new T.Vector3(), a = new T.Vector3(), b = new T.Vector3(), c = new T.Vector3();\r\n  let distance = Infinity, result: T.Vector3 | undefined, normal: T.Vector3 | undefined;\r\n  for (const mesh of meshes) {\r\n    const geometry = mesh.geometry, position = geometry.getAttribute('position'), index = geometry.index;\r\n    const count = (index?.count ?? position.count) / 3;\r\n    for (let face = 0; face < count; face++) {\r\n      a.fromBufferAttribute(position, index ? index.getX(face * 3) : face * 3).applyMatrix4(mesh.matrixWorld);\r\n      b.fromBufferAttribute(position, index ? index.getX(face * 3 + 1) : face * 3 + 1).applyMatrix4(mesh.matrixWorld);\r\n      c.fromBufferAttribute(position, index ? index.getX(face * 3 + 2) : face * 3 + 2).applyMatrix4(mesh.matrixWorld);\r\n      triangle.set(a, b, c).closestPointToPoint(point, closest);\r\n      const nextDistance = closest.distanceToSquared(point);\r\n      if (nextDistance < distance) {\r\n        distance = nextDistance; result = closest.clone(); normal = triangle.getNormal(new T.Vector3());\r\n      }\r\n    }\r\n  }\r\n  return result && normal ? { point: result, normal, distance: Math.sqrt(distance) } : undefined;\r\n}\r\n\r\nfunction makeTriangle(mesh: T.Mesh, region: string, id: number): Triangle {\r\n  const g = mesh.geometry, p = g.getAttribute('position'), ix = g.index, at = (i: number) => {\r\n    const n = ix ? ix.getX(i) : i;\r\n    const v = new T.Vector3().fromBufferAttribute(p, n).applyMatrix4(mesh.matrixWorld);\r\n    return { x: v.x, y: v.y, z: v.z };\r\n  };\r\n  const points = [at(id * 3), at(id * 3 + 1), at(id * 3 + 2)] as [Point, Point, Point];\r\n  return { mesh, region, id, points, center: scale(add(add(points[0], points[1]), points[2]), 1 / 3) };\r\n}\r\n\r\nfunction pointKey(p: Point, tolerance: number) {\r\n  return `${Math.round(p.x / tolerance)},${Math.round(p.y / tolerance)},${Math.round(p.z / tolerance)}`;\r\n}\r\nfunction edgeKey(a: Point, b: Point, tolerance: number) {\r\n  const ka = pointKey(a, tolerance), kb = pointKey(b, tolerance);\r\n  return ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`;\r\n}\r\n\r\nfunction lowComponents(triangles: Triangle[], tolerance: number) {\r\n  const edges = new Map<string, number[]>();\r\n  triangles.forEach((tri, i) => { for (let k = 0; k < 3; k++) { const key = edgeKey(tri.points[k], tri.points[(k + 1) % 3], tolerance); const row = edges.get(key); if (row) row.push(i); else edges.set(key, [i]); } });\r\n  const adjacency = triangles.map(() => [] as number[]);\r\n  for (const ids of edges.values()) if (ids.length > 1) for (const a of ids) for (const b of ids) if (a !== b) adjacency[a].push(b);\r\n  const component = new Int32Array(triangles.length).fill(-1), groups: number[][] = [];\r\n  for (let i = 0; i < triangles.length; i++) {\r\n    if (component[i] >= 0) continue;\r\n    const id = groups.length, queue = [i], members: number[] = []; component[i] = id;\r\n    for (let q = 0; q < queue.length; q++) { const at = queue[q]; members.push(at); for (const next of adjacency[at]) if (component[next] < 0) { component[next] = id; queue.push(next); } }\r\n    groups.push(members);\r\n  }\r\n  return { component, groups };\r\n}\r\n\r\nfunction farthestSeeds(points: Point[], k: number): Point[] {\r\n  const mean = points.reduce((sum, p) => add(sum, p), { x: 0, y: 0, z: 0 });\r\n  const origin = scale(mean, 1 / points.length);\r\n  const seeds = [points.reduce((best, p) => dist2XZ(p, origin) > dist2XZ(best, origin) ? p : best, points[0])];\r\n  while (seeds.length < k) {\r\n    const next = points.reduce((best, p) => {\r\n      const d = Math.min(...seeds.map(seed => dist2XZ(p, seed)));\r\n      const bestD = Math.min(...seeds.map(seed => dist2XZ(best, seed)));\r\n      return d > bestD ? p : best;\r\n    }, points[0]);\r\n    seeds.push(next);\r\n  }\r\n  return seeds.map(p => ({ ...p }));\r\n}\r\n\r\nfunction clusterFour(points: Point[]): Cluster[] | null {\r\n  if (points.length < 80) return null;\r\n  const centers = farthestSeeds(points, 4);\r\n  let assignments = new Int8Array(points.length);\r\n  for (let pass = 0; pass < 32; pass++) {\r\n    let changed = false;\r\n    const sums = centers.map(() => ({ x: 0, y: 0, z: 0 })), counts = centers.map(() => 0);\r\n    points.forEach((p, i) => {\r\n      let best = 0, distance = Infinity;\r\n      for (let c = 0; c < centers.length; c++) { const d = dist2XZ(p, centers[c]); if (d < distance) { distance = d; best = c; } }\r\n      if (assignments[i] !== best) changed = true;\r\n      assignments[i] = best; sums[best] = add(sums[best], p); counts[best]++;\r\n    });\r\n    for (let c = 0; c < centers.length; c++) if (counts[c]) centers[c] = scale(sums[c], 1 / counts[c]);\r\n    if (!changed) break;\r\n  }\r\n  const clusters = centers.map((center, c) => {\r\n    let samples = 0, radius = 0;\r\n    points.forEach((p, i) => { if (assignments[i] === c) { samples++; radius = Math.max(radius, Math.sqrt(dist2XZ(p, center))); } });\r\n    return { center, samples, radius };\r\n  });\r\n  const separation = Math.min(...clusters.flatMap((a, i) => clusters.slice(i + 1).map(b => Math.sqrt(dist2XZ(a.center, b.center)))));\r\n  if (clusters.some(c => c.samples < 20) || separation < 0.06) return null;\r\n  return clusters;\r\n}\r\n\r\nfunction triangleSubset(source: T.BufferGeometry, ids: readonly number[]): T.BufferGeometry | null {\r\n  if (!ids.length) return null;\r\n  const position = source.getAttribute('position'), index = source.index, remap = new Map<number, number>(), sourceVertices: number[] = [], outIndex: number[] = [];\r\n  const count = index?.count ?? position.count;\r\n  for (const triangle of ids) {\r\n    const start = triangle * 3;\r\n    if (start + 2 >= count) continue;\r\n    for (let j = 0; j < 3; j++) {\r\n      const old = index ? index.getX(start + j) : start + j;\r\n      let next = remap.get(old);\r\n      if (next === undefined) { next = sourceVertices.length; remap.set(old, next); sourceVertices.push(old); }\r\n      outIndex.push(next);\r\n    }\r\n  }\r\n  if (!outIndex.length) return null;\r\n  const out = new T.BufferGeometry();\r\n  for (const [name, attribute] of Object.entries(source.attributes)) {\r\n    const a = attribute as T.BufferAttribute, values = new (a.array.constructor as any)(sourceVertices.length * a.itemSize);\r\n    for (let i = 0; i < sourceVertices.length; i++) for (let c = 0; c < a.itemSize; c++) values[i * a.itemSize + c] = a.array[sourceVertices[i] * a.itemSize + c];\r\n    out.setAttribute(name, new T.BufferAttribute(values, a.itemSize, a.normalized));\r\n  }\r\n  for (const [name, attributes] of Object.entries(source.morphAttributes)) {\r\n    out.morphAttributes[name] = attributes.map(attribute => {\r\n      const a = attribute as T.BufferAttribute, values = new (a.array.constructor as any)(sourceVertices.length * a.itemSize);\r\n      for (let i = 0; i < sourceVertices.length; i++) for (let c = 0; c < a.itemSize; c++) values[i * a.itemSize + c] = a.array[sourceVertices[i] * a.itemSize + c];\r\n      return new T.BufferAttribute(values, a.itemSize, a.normalized);\r\n    });\r\n  }\r\n  out.morphTargetsRelative = source.morphTargetsRelative;\r\n  out.setIndex(outIndex);\r\n  out.computeBoundingBox(); out.computeBoundingSphere();\r\n  return out;\r\n}\r\n\r\n/** Quadzilla's source has three legs: one left foreleg split across arms/feet, one right foreleg, and one rear-right leg. */\r\nfunction correctStylizedQuadOwnership(source: T.Group, regions: Map<string, T.Mesh[]>): boolean {\r\n  source.updateWorldMatrix(true, true);\r\n  const body = source.getObjectByName('body');\r\n  const head = source.getObjectByName('head');\r\n  const feet = source.getObjectByName('feet');\r\n  const bodyMeshes = regions.get('body') ?? [];\r\n  if (!body || !head || !feet || !bodyMeshes.length) return false;\r\n  const bodyBox = new T.Box3().setFromObject(body), headBox = new T.Box3().setFromObject(head), feetBox = new T.Box3().setFromObject(feet);\r\n  const footHeight = feetBox.max.y - feetBox.min.y;\r\n  // Head reaches the torso cut but is far above the ground; reject if it ever contains a foot-height island.\r\n  if (!(footHeight > 0) || headBox.min.y <= feetBox.min.y + footHeight * .15) return false;\r\n  const bodyMaterial = bodyMeshes.find(mesh => !Array.isArray(mesh.material))?.material;\r\n  if (!bodyMaterial || Array.isArray(bodyMaterial)) return false;\r\n  const specs = [\r\n    { region: 'arms', triangles: 154, center: [ .0982, .9973, .2414], owner: 'body' as const },\r\n    { region: 'arms', triangles: 1461, center: [-.8023, .4035, .2243], owner: 'ArmLeft' as const },\r\n    { region: 'arms', triangles: 2014, center: [ .8086, .4974, .3690], owner: 'ArmRight' as const },\r\n    { region: 'feet', triangles: 467, center: [-.8390, .2011,-.0571], owner: 'ArmLeft' as const },\r\n    { region: 'feet', triangles: 102, center: [-.1132,1.0031,-.1474], owner: 'body' as const },\r\n    { region: 'feet', triangles: 1391, center: [ .2609, .3803,-.5842], owner: 'FootRight' as const },\r\n  ];\r\n  const found = new Set<string>(), targets = new Map<T.Mesh, Map<typeof specs[number]['owner'], number[]>>();\r\n  for (const region of ['arms', 'feet']) for (const mesh of regions.get(region) ?? []) {\r\n    const position = mesh.geometry.getAttribute('position'), index = mesh.geometry.index;\r\n    for (const component of connectedTriangleComponents(mesh)) {\r\n      const box = new T.Box3(), center = new T.Vector3();\r\n      for (const face of component) for (let corner = 0; corner < 3; corner++) {\r\n        const vertex = index ? index.getX(face * 3 + corner) : face * 3 + corner;\r\n        const point = new T.Vector3().fromBufferAttribute(position, vertex).applyMatrix4(mesh.matrixWorld);\r\n        box.expandByPoint(point); center.add(point);\r\n      }\r\n      center.divideScalar(component.length * 3);\r\n      const specIndex = specs.findIndex(spec => spec.region === region && spec.triangles === component.length &&\r\n        Math.abs(center.x - spec.center[0]) < .025 && Math.abs(center.y - spec.center[1]) < .025 && Math.abs(center.z - spec.center[2]) < .025);\r\n      if (specIndex < 0) continue;\r\n      const key = `${region}:${specIndex}`;\r\n      if (found.has(key)) return false;\r\n      found.add(key);\r\n      const owner = specs[specIndex].owner;\r\n      let byOwner = targets.get(mesh); if (!byOwner) targets.set(mesh, byOwner = new Map());\r\n      let ids = byOwner.get(owner); if (!ids) byOwner.set(owner, ids = []); ids.push(...component);\r\n    }\r\n  }\r\n  if (found.size !== specs.length) return false;\r\n  let moved = 0, bodyMoved = 0;\r\n  for (const [mesh, byOwner] of targets) {\r\n    const selected = new Set([...byOwner.values()].flat());\r\n    const count = (mesh.geometry.index?.count ?? mesh.geometry.getAttribute('position').count) / 3;\r\n    const remaining: number[] = []; for (let id = 0; id < count; id++) if (!selected.has(id)) remaining.push(id);\r\n    const old = mesh.geometry, kept = triangleSubset(old, remaining), parent = mesh.parent;\r\n    if (kept) mesh.geometry = kept; else mesh.removeFromParent();\r\n    for (const [owner, ids] of byOwner) {\r\n      const geometry = triangleSubset(old, ids); if (!geometry) continue;\r\n      const part = mesh.clone(false); part.geometry = geometry;\r\n      if (owner === 'body') {\r\n        const relative = body.matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n        relative.decompose(part.position, part.quaternion, part.scale);\r\n        part.name = `${mesh.name || 'Quadzilla seam'} body-owned pelvis`;\r\n        part.material = bodyMaterial;\r\n        part.userData = { ...mesh.userData, quadrupedBodyOwnedTriangles: ids.length };\r\n        body.add(part); bodyMoved += ids.length;\r\n      } else {\r\n        part.name = `${mesh.name || 'Quadzilla leg'} ${owner}`;\r\n        part.userData = { ...mesh.userData, quadrupedLimbNode: owner, rigNodeName: owner };\r\n        parent?.add(part);\r\n      }\r\n      moved += ids.length;\r\n    }\r\n    old.dispose();\r\n  }\r\n  if (moved !== 5589 || bodyMoved !== 256) return false;\r\n  source.updateWorldMatrix(true, true);\r\n  source.userData.quadrupedOwnershipVersion = 1;\r\n  source.userData.quadrupedOwnershipTriangles = moved;\r\n  source.userData.quadrupedOwnershipBodyTriangles = bodyMoved;\r\n  source.userData.quadrupedOwnershipBodyTransferredTriangles = bodyMoved;\r\n  source.userData.quadrupedOwnershipBodyTransferredFromFeet = 102;\r\n  source.userData.quadrupedLimbHints = {\r\n    ArmLeft: { position: [-.8023, 1.05, .2243], side: -1, fore: true },\r\n    ArmRight: { position: [.8086, 1.05, .3690], side: 1, fore: true },\r\n    FootRight: { position: [.2609, 1.05, -.5842], side: 1, fore: false },\r\n  };\r\n  source.userData.quadrupedOwnershipLimbTriangles = { ArmLeft: 1928, ArmRight: 2014, FootLeft: 0, FootRight: 1391 };\r\n  storeMeshJointHints(source, source.userData.quadrupedLimbHints);\r\n  return true;\r\n}\r\n\r\nfunction inferFront(source: T.Group, bodyBox: T.Box3, headBox: T.Box3): { x: number; z: number } {\r\n  const body = bodyBox.getCenter(new T.Vector3()), head = headBox.getCenter(new T.Vector3());\r\n  const anchor = source.getObjectByName('eye_anchor');\r\n  const point = anchor ? anchor.getWorldPosition(new T.Vector3()) : head;\r\n  let x = point.x - body.x, z = point.z - body.z;\r\n  if (Math.hypot(x, z) < 1e-4) { x = head.x - body.x; z = head.z - body.z; }\r\n  if (Math.hypot(x, z) < 1e-4) { x = 0; z = 1; }\r\n  const length = Math.hypot(x, z);\r\n  return { x: x / length, z: z / length };\r\n}\r\n\r\nfunction storeMeshJointHints(source: T.Group, hints: Partial<Record<QuadrupedLimbNode, QuadrupedJointHint>>): void {\r\n  source.updateWorldMatrix(true, true);\r\n  source.traverse(object => {\r\n    if (!(object instanceof T.Mesh)) return;\r\n    const node = object.userData.rigNodeName as QuadrupedLimbNode | undefined;\r\n    const hint = node ? hints[node] : undefined;\r\n    if (!hint) return;\r\n    const local = object.worldToLocal(new T.Vector3(...hint.position));\r\n    object.userData.rigJointLocal = local.toArray();\r\n  });\r\n}\r\n\r\n/** Return false without changing source when four distinct lower-leg clusters cannot be supported. */\r\nexport function correctQuadrupedOwnership(source: T.Group, sourceId: string): boolean {\r\n  if (!QUADRUPED_IDS.has(sourceId) || (!REPAIR_IDS.has(sourceId) && !BODY_FRAGMENT_ONLY_IDS.has(sourceId)) || source.userData.quadrupedOwnershipVersion === 1) return false;\r\n  source.updateWorldMatrix(true, true);\r\n  const regionMeshes = new Map<string, T.Mesh[]>();\r\n  const boxes = new Map<string, T.Box3>();\r\n  for (const name of REGIONS) {\r\n    const region = source.getObjectByName(name), meshes: T.Mesh[] = [], box = new T.Box3();\r\n    region?.traverse(o => { if (o instanceof T.Mesh && o.visible && !Array.isArray(o.material)) { meshes.push(o); box.expandByObject(o); } });\r\n    regionMeshes.set(name, meshes); boxes.set(name, box);\r\n  }\r\n  const feetBox = boxes.get('feet')!, bodyBox = boxes.get('body')!, headBox = boxes.get('head')!;\r\n  if (feetBox.isEmpty() || bodyBox.isEmpty() || headBox.isEmpty()) return false;\r\n  if (sourceId === STYLIZED_QUAD_ID) return correctStylizedQuadOwnership(source, regionMeshes);\r\n  const cutoff = feetBox.max.y + Math.max(0.003, feetBox.getSize(new T.Vector3()).y * 0.003);\r\n  const blobBodyPrepared = sourceId === 'roster/23-blob-texture-bodies--blob_creature_3d_model';\r\n  const allLow: Triangle[] = [];\r\n  for (const [region, meshes] of regionMeshes) {\r\n    // The source assembler first returns Blob64's torso side panels and central\r\n    // pelvis to BODY. Only the four original grounded paw components are animated.\r\n    if (blobBodyPrepared && region !== 'feet') continue;\r\n    for (const mesh of meshes) {\r\n    const g = mesh.geometry, count = (g.index?.count ?? g.getAttribute('position').count) / 3;\r\n    for (let id = 0; id < count; id++) {\r\n      const triangle = makeTriangle(mesh, region, id);\r\n      if (triangle.center.y >= cutoff) continue;\r\n      allLow.push(triangle);\r\n    }\r\n    }\r\n  }\r\n  if (allLow.length < 100) return false;\r\n  const allBox = new T.Box3(); for (const tri of allLow) for (const p of tri.points) allBox.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n  const weld = Math.max(0.001, allBox.getSize(new T.Vector3()).length() * 0.0005), topology = lowComponents(allLow, weld);\r\n  const bodySize = bodyBox.getSize(new T.Vector3()), heldComponents = new Set<number>();\r\n  const bodyAttachmentComponents = new Set<number>(), bodyAttachmentOffsets = new Map<number, T.Vector3>(), bodyAttachmentContacts = new Map<number, T.Vector3>();\r\n  if (sourceId === 'roster/18-quad-all--fantasy_creature_3d_model3'\r\n    || sourceId === 'roster/18-quad-all--quadruped_robot_3d_model') {\r\n    const bodyRegion = source.getObjectByName('body'), bodyMaterial = bodyRegion\r\n      ? regionMeshes.get('body')!.find(mesh => !Array.isArray(mesh.material))?.material : undefined;\r\n    if (!bodyRegion || !bodyMaterial || Array.isArray(bodyMaterial)) return false;\r\n\r\n    const bodyOwned = new Set<Triangle>();\r\n    if (sourceId === 'roster/18-quad-all--fantasy_creature_3d_model3') {\r\n      // Shiva's separate, 75-face undershell sits centrally at the pelvis seam.\r\n      // It is not one of the four grounded paws and follows the torso.\r\n      const feetTriangles = allLow.filter(tri => tri.region === 'feet');\r\n      const feetTopology = lowComponents(feetTriangles, weld), candidates: Triangle[][] = [];\r\n      feetTopology.groups.forEach(members => {\r\n        if (members.length < 50 || members.length > 100) return;\r\n        const box = new T.Box3();\r\n        for (const index of members) for (const p of feetTriangles[index].points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n        const center = box.getCenter(new T.Vector3()), size = box.getSize(new T.Vector3());\r\n        if (Math.abs(center.x - bodyBox.getCenter(new T.Vector3()).x) < 0.15\r\n          && center.y > feetBox.min.y + (feetBox.max.y - feetBox.min.y) * 0.8\r\n          && box.max.y >= feetBox.max.y - (feetBox.max.y - feetBox.min.y) * 0.02\r\n          && size.x < bodySize.x * 0.6 && size.z < bodySize.z * 0.4) {\r\n          candidates.push(members.map(index => feetTriangles[index]));\r\n        }\r\n      });\r\n      if (candidates.length !== 1 || candidates[0].length !== 75) return false;\r\n      for (const tri of candidates[0]) bodyOwned.add(tri);\r\n    } else {\r\n      // Yee's source has a single head-owned triangle crossing the pelvis cut.\r\n      const candidates: Triangle[] = [];\r\n      topology.groups.forEach(members => {\r\n        if (members.length !== 1) return;\r\n        const tri = allLow[members[0]], sizeX = Math.max(...tri.points.map(p => p.x)) - Math.min(...tri.points.map(p => p.x));\r\n        const sizeZ = Math.max(...tri.points.map(p => p.z)) - Math.min(...tri.points.map(p => p.z));\r\n        if (tri.region === 'head' && tri.center.x > 0.15 && Math.abs(tri.center.y - cutoff) < 0.005\r\n          && sizeX < 0.06 && sizeZ < 0.03) candidates.push(tri);\r\n      });\r\n      if (candidates.length !== 1) return false;\r\n      bodyOwned.add(candidates[0]);\r\n    }\r\n\r\n    const feetHeight = feetBox.max.y - feetBox.min.y;\r\n    const groundSeeds = allLow.filter(tri => (tri.region === 'arms' || tri.region === 'feet')\r\n      && !bodyOwned.has(tri) && tri.center.y <= feetBox.min.y + feetHeight * 0.1).map(tri => tri.center);\r\n    const clusters = clusterFour(groundSeeds);\r\n    if (!clusters) return false;\r\n    const bodyCenter = bodyBox.getCenter(new T.Vector3()), front = inferFront(source, bodyBox, headBox);\r\n    const ranked = clusters.map(c => ({ c, foreScore: (c.center.x - bodyCenter.x) * front.x + (c.center.z - bodyCenter.z) * front.z }));\r\n    const fore = [...ranked].sort((a, b) => b.foreScore - a.foreScore);\r\n    const setPair = (pair: typeof ranked, foreleg: boolean) => {\r\n      const [left, right] = [...pair].sort((a, b) => a.c.center.x - b.c.center.x);\r\n      for (const row of [left, right]) {\r\n        row.c.fore = foreleg; row.c.side = row === left ? -1 : 1;\r\n        row.c.node = foreleg ? (row.c.side < 0 ? 'ArmLeft' : 'ArmRight') : (row.c.side < 0 ? 'FootLeft' : 'FootRight');\r\n      }\r\n    };\r\n    setPair(fore.slice(0, 2), true); setPair(fore.slice(2), false);\r\n    const nearestCluster = (p: Point) => clusters.map((c, i) => ({ i, d: dist2XZ(p, c.center) })).sort((a, b) => a.d - b.d)[0].i;\r\n    const targets = new Map<T.Mesh, Map<QuadrupedLimbNode | 'body', number[]>>();\r\n    const assigned: { tri: Triangle; node: QuadrupedLimbNode | 'body' }[] = [];\r\n    const addTarget = (tri: Triangle, node: QuadrupedLimbNode | 'body') => {\r\n      let map = targets.get(tri.mesh); if (!map) targets.set(tri.mesh, map = new Map());\r\n      let ids = map.get(node); if (!ids) map.set(node, ids = []); ids.push(tri.id);\r\n      assigned.push({ tri, node });\r\n    };\r\n    for (const tri of allLow) {\r\n      if (bodyOwned.has(tri)) addTarget(tri, 'body');\r\n      else if (tri.region === 'arms' || tri.region === 'feet') {\r\n        const node = clusters[nearestCluster(tri.center)].node;\r\n        if (!node) return false;\r\n        addTarget(tri, node);\r\n      }\r\n    }\r\n    let moved = 0, bodyTransferred = 0;\r\n    for (const [mesh, byNode] of targets) {\r\n      const selected = new Set([...byNode.values()].flat()), count = (mesh.geometry.index?.count ?? mesh.geometry.getAttribute('position').count) / 3;\r\n      const remaining: number[] = [];\r\n      for (let id = 0; id < count; id++) if (!selected.has(id)) remaining.push(id);\r\n      const old = mesh.geometry, kept = triangleSubset(old, remaining), parent = mesh.parent;\r\n      if (kept) mesh.geometry = kept; else mesh.removeFromParent();\r\n      for (const [node, ids] of byNode) {\r\n        const geometry = triangleSubset(old, ids); if (!geometry) continue;\r\n        const part = mesh.clone(false); part.geometry = geometry;\r\n        if (node === 'body') {\r\n          const relative = bodyRegion.matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n          relative.decompose(part.position, part.quaternion, part.scale);\r\n          part.name = `${mesh.name || 'Quadruped'} body-owned pelvis`; part.material = bodyMaterial;\r\n          part.userData = { ...mesh.userData, quadrupedBodyOwnedTriangles: ids.length };\r\n          bodyRegion.add(part); bodyTransferred += ids.length;\r\n        } else {\r\n          part.name = `${mesh.name || 'Quadruped limb'} ${node}`;\r\n          part.userData = { ...mesh.userData, quadrupedLimbNode: node, rigNodeName: node };\r\n          parent?.add(part);\r\n        }\r\n        moved += ids.length;\r\n      }\r\n      old.dispose();\r\n    }\r\n    if (moved < (sourceId === 'roster/18-quad-all--fantasy_creature_3d_model3' ? 6500 : 5500)\r\n      || bodyTransferred !== (sourceId === 'roster/18-quad-all--fantasy_creature_3d_model3' ? 75 : 1)) return false;\r\n    source.updateWorldMatrix(true, true);\r\n    const hints = {} as Record<QuadrupedLimbNode, QuadrupedJointHint>;\r\n    for (const c of clusters) {\r\n      const points = assigned.filter(row => row.node === c.node && row.tri.region !== 'body' && row.tri.region !== 'head'\r\n        && Math.sqrt(dist2XZ(row.tri.center, c.center)) < Math.max(c.radius * 0.7, 0.08)\r\n        && row.tri.center.y >= cutoff - Math.max(0.08, (cutoff - feetBox.min.y) * 0.18)).map(row => row.tri.center);\r\n      const joint = points.length ? points.reduce((sum, p) => add(sum, p), { x: 0, y: 0, z: 0 }) : { ...c.center, y: cutoff };\r\n      const p = points.length ? scale(joint, 1 / points.length) : joint;\r\n      hints[c.node!] = { position: [p.x, p.y, p.z], side: c.side!, fore: c.fore! };\r\n    }\r\n    source.userData.quadrupedLimbHints = hints; storeMeshJointHints(source, hints);\r\n    source.userData.quadrupedOwnershipVersion = 1;\r\n    source.userData.quadrupedOwnershipTriangles = moved;\r\n    source.userData.quadrupedOwnershipBodyTriangles = bodyTransferred;\r\n    source.userData.quadrupedOwnershipBodyTransferredTriangles = bodyTransferred;\r\n    return true;\r\n  }\r\n  if (BODY_FRAGMENT_ONLY_IDS.has(sourceId)) {\r\n    const candidates: number[] = [];\r\n    topology.groups.forEach((members, id) => {\r\n      const counts: Record<string, number> = {}, box = new T.Box3();\r\n      for (const index of members) {\r\n        const tri = allLow[index]; counts[tri.region] = (counts[tri.region] || 0) + 1;\r\n        for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n      }\r\n      const center = box.getCenter(new T.Vector3()), size = box.getSize(new T.Vector3());\r\n      if (sourceId === 'roster/18-quad-all--quadruped_robot_3d_model'\r\n        && members.length === 1 && counts.head === 1 && center.x > 0.15\r\n        && Math.abs(center.y - cutoff) < 0.005 && size.x < 0.06 && size.z < 0.03) candidates.push(id);\r\n      if (sourceId === 'roster/18-quad-all--stylized_quadruped_3d_model') {\r\n        const bodySideIsland = Math.abs(center.x - bodyBox.getCenter(new T.Vector3()).x) < 0.15\r\n          && Math.abs(center.y - cutoff) < feetBox.getSize(new T.Vector3()).y * 0.08\r\n          && size.x < bodySize.x * 0.55 && size.z < bodySize.z * 0.4;\r\n        if (bodySideIsland && ((members.length === 102 && counts.feet === 102)\r\n          || (members.length === 154 && counts.arms === 154))) candidates.push(id);\r\n      }\r\n    });\r\n    const expected = sourceId === 'roster/18-quad-all--quadruped_robot_3d_model' ? 1 : 2;\r\n    if (candidates.length !== expected) return false;\r\n    const bodyRegion = source.getObjectByName('body'), bodyMaterial = bodyRegion\r\n      ? regionMeshes.get('body')!.find(mesh => !Array.isArray(mesh.material))?.material : undefined;\r\n    if (!bodyRegion || !bodyMaterial || Array.isArray(bodyMaterial)) return false;\r\n    const byMesh = new Map<T.Mesh, number[]>();\r\n    for (const id of candidates) for (const index of topology.groups[id]) {\r\n      const tri = allLow[index], ids = byMesh.get(tri.mesh);\r\n      if (ids) ids.push(tri.id); else byMesh.set(tri.mesh, [tri.id]);\r\n    }\r\n    let moved = 0;\r\n    for (const [mesh, ids] of byMesh) {\r\n      if (!mesh.parent) continue;\r\n      const count = (mesh.geometry.index?.count ?? mesh.geometry.getAttribute('position').count) / 3;\r\n      const selected = new Set(ids), remaining: number[] = [];\r\n      for (let id = 0; id < count; id++) if (!selected.has(id)) remaining.push(id);\r\n      const old = mesh.geometry, parent = mesh.parent, bodyPart = triangleSubset(old, ids), kept = triangleSubset(old, remaining);\r\n      if (!bodyPart) continue;\r\n      const part = mesh.clone(false); part.geometry = bodyPart;\r\n      const relative = bodyRegion.matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n      relative.decompose(part.position, part.quaternion, part.scale);\r\n      part.name = `${mesh.name || 'Quadruped seam'} body-owned lower seam`;\r\n      part.material = bodyMaterial;\r\n      part.userData = { ...mesh.userData, quadrupedBodyOwnedTriangles: ids.length };\r\n      if (kept) mesh.geometry = kept; else mesh.removeFromParent();\r\n      bodyRegion.add(part); old.dispose(); moved += ids.length;\r\n    }\r\n    if (!moved) return false;\r\n    source.userData.quadrupedOwnershipVersion = 1;\r\n    source.userData.quadrupedOwnershipBodyTriangles = moved;\r\n    source.userData.quadrupedOwnershipBodyTransferredTriangles = moved;\r\n    source.userData.quadrupedOwnershipBodyTransferredFromFeet = sourceId === 'roster/18-quad-all--stylized_quadruped_3d_model' ? 102 : 0;\r\n    return true;\r\n  }\r\n  if (sourceId === 'roster/18-quad-all--four-legged_robot_3d_model') {\r\n    const bodyCenter = bodyBox.getCenter(new T.Vector3()), feetHeight = feetBox.max.y - feetBox.min.y;\r\n    const bodyRegion = source.getObjectByName('body'), bodyMaterial = bodyRegion\r\n      ? regionMeshes.get('body')!.find(mesh => !Array.isArray(mesh.material))?.material : undefined;\r\n    if (!bodyRegion || !bodyMaterial || Array.isArray(bodyMaterial) || !(feetHeight > 0)) return false;\r\n    const centralPelvis: number[] = [], limbIsland: number[] = [];\r\n    topology.groups.forEach((members, id) => {\r\n      const counts: Record<string, number> = {}, box = new T.Box3();\r\n      for (const index of members) {\r\n        const tri = allLow[index]; counts[tri.region] = (counts[tri.region] || 0) + 1;\r\n        for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n      }\r\n      const center = box.getCenter(new T.Vector3()), size = box.getSize(new T.Vector3());\r\n        // Tara's isolated, non-grounded 500-face underside is split into two\r\n        // source-region islands at the z seam. Together they span the torso\r\n        // width; neither island reaches a grounded leg, so both belong to body.\r\n        if (members.length >= 200 && members.length <= 280\r\n          && ((counts.feet || 0) === members.length || (counts.arms || 0) === members.length)\r\n          && Math.abs(center.x - bodyCenter.x) < 0.1\r\n          && center.y > feetBox.min.y + feetHeight * 0.8\r\n          && size.x > bodySize.x * 0.8 && size.z > bodySize.z * 0.35) centralPelvis.push(id);\r\n        if (members.length >= 7500 && (counts.feet || 0) >= 5000 && (counts.arms || 0) >= 2500) limbIsland.push(id);\r\n      });\r\n    if (centralPelvis.length !== 2 || limbIsland.length !== 1) return false;\r\n    // The source arm mesh also contains a broad center-front facade that reaches\r\n    // the ground. Its 2,190 connected faces meet the head seam directly, but it\r\n    // is not a fifth leg. Exclude it from paw clustering and keep it with the\r\n    // head shell; body ownership opened a visible seam through the front cone.\r\n    const armTriangles = allLow.filter(tri => tri.region === 'arms');\r\n    const armTopology = lowComponents(armTriangles, weld), centralFrontPanels: Triangle[][] = [];\r\n    armTopology.groups.forEach(members => {\r\n      const box = new T.Box3(), groundNearCenter: Triangle[] = [];\r\n      for (const index of members) {\r\n        const tri = armTriangles[index];\r\n        for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n        if (tri.center.y <= feetBox.min.y + feetHeight * 0.1\r\n          && Math.abs(tri.center.x - bodyCenter.x) < 0.25\r\n          && tri.center.z > bodyCenter.z + bodySize.z * 0.6) groundNearCenter.push(tri);\r\n      }\r\n      const size = box.getSize(new T.Vector3()), center = box.getCenter(new T.Vector3());\r\n      if (members.length >= 2000 && members.length <= 2300 && groundNearCenter.length >= 100\r\n        && Math.abs(center.x - bodyCenter.x) < 0.1 && center.z > bodyCenter.z + bodySize.z * 0.5\r\n        && size.x > bodySize.x * 0.8 && size.z > bodySize.z * 0.8\r\n        && box.min.y <= feetBox.min.y + feetHeight * 0.1 && box.max.y >= cutoff - feetHeight * 0.02) {\r\n        centralFrontPanels.push(members.map(index => armTriangles[index]));\r\n      }\r\n    });\r\n    if (centralFrontPanels.length !== 1) return false;\r\n    const centralFrontTriangles = new Set(centralFrontPanels[0]);\r\n    const groundSeeds: Point[] = [];\r\n    for (let i = 0; i < allLow.length; i++) {\r\n      const tri = allLow[i];\r\n      if (topology.component[i] === limbIsland[0] && (tri.region === 'arms' || tri.region === 'feet')\r\n        && !centralFrontTriangles.has(tri)\r\n        && tri.center.y <= feetBox.min.y + feetHeight * 0.1) groundSeeds.push(tri.center);\r\n    }\r\n    const clusters = clusterFour(groundSeeds);\r\n    if (!clusters) return false;\r\n    const front = inferFront(source, bodyBox, headBox), ranked = clusters.map(c => ({\r\n      c, foreScore: (c.center.x - bodyCenter.x) * front.x + (c.center.z - bodyCenter.z) * front.z,\r\n      sideScore: c.center.x - bodyCenter.x,\r\n    }));\r\n    const fore = [...ranked].sort((a, b) => b.foreScore - a.foreScore);\r\n    const setPair = (pair: typeof ranked, foreleg: boolean) => {\r\n      const [left, right] = [...pair].sort((a, b) => a.sideScore - b.sideScore);\r\n      for (const row of [left, right]) {\r\n        row.c.fore = foreleg; row.c.side = row === left ? -1 : 1;\r\n        row.c.node = foreleg ? (row.c.side < 0 ? 'ArmLeft' : 'ArmRight') : (row.c.side < 0 ? 'FootLeft' : 'FootRight');\r\n      }\r\n    };\r\n    setPair(fore.slice(0, 2), true); setPair(fore.slice(2), false);\r\n    const nearestCluster = (p: Point) => clusters.map((c, i) => ({ i, d: dist2XZ(p, c.center) })).sort((a, b) => a.d - b.d)[0].i;\r\n    const targets = new Map<T.Mesh, Map<QuadrupedLimbNode | 'body' | 'head', number[]>>();\r\n    const addTarget = (tri: Triangle, node: QuadrupedLimbNode | 'body' | 'head') => {\r\n      let meshTargets = targets.get(tri.mesh); if (!meshTargets) targets.set(tri.mesh, meshTargets = new Map());\r\n      let ids = meshTargets.get(node); if (!ids) meshTargets.set(node, ids = []); ids.push(tri.id);\r\n    };\r\n    const centralIds = new Set(centralPelvis);\r\n    for (let i = 0; i < allLow.length; i++) {\r\n      const tri = allLow[i], component = topology.component[i];\r\n      if ((centralIds.has(component) || centralFrontTriangles.has(tri)) && (tri.region === 'arms' || tri.region === 'feet')) {\r\n        addTarget(tri, centralFrontTriangles.has(tri) ? 'head' : 'body');\r\n      }\r\n      else if (component === limbIsland[0] && (tri.region === 'arms' || tri.region === 'feet')) {\r\n        const node = clusters[nearestCluster(tri.center)].node;\r\n        if (!node) return false;\r\n        addTarget(tri, node);\r\n      }\r\n    }\r\n    const headRegion = source.getObjectByName('head'), headMaterial = headRegion\r\n      ? regionMeshes.get('head')!.find(mesh => !Array.isArray(mesh.material))?.material : undefined;\r\n    if (!headRegion || !headMaterial || Array.isArray(headMaterial)) return false;\r\n    let moved = 0, bodyTransferred = 0, headTransferred = 0;\r\n    for (const [mesh, byNode] of targets) {\r\n      const selected = new Set([...byNode.values()].flat()), count = (mesh.geometry.index?.count ?? mesh.geometry.getAttribute('position').count) / 3;\r\n      const remaining: number[] = [];\r\n      for (let id = 0; id < count; id++) if (!selected.has(id)) remaining.push(id);\r\n      const old = mesh.geometry, kept = triangleSubset(old, remaining), parent = mesh.parent;\r\n      if (kept) mesh.geometry = kept; else mesh.removeFromParent();\r\n      for (const [node, ids] of byNode) {\r\n        const geometry = triangleSubset(old, ids); if (!geometry) continue;\r\n        const part = mesh.clone(false); part.geometry = geometry;\r\n        if (node === 'body' || node === 'head') {\r\n          const ownerRegion = node === 'body' ? bodyRegion : headRegion;\r\n          const relative = ownerRegion.matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n          relative.decompose(part.position, part.quaternion, part.scale);\r\n          part.name = `${mesh.name || 'Tara shell'} ${node}-owned central shell`;\r\n          part.material = node === 'body' ? bodyMaterial : headMaterial;\r\n          part.userData = { ...mesh.userData, [node === 'body' ? 'quadrupedBodyOwnedTriangles' : 'quadrupedHeadOwnedTriangles']: ids.length };\r\n          ownerRegion.add(part);\r\n          if (node === 'body') bodyTransferred += ids.length; else headTransferred += ids.length;\r\n        } else {\r\n          part.name = `${mesh.name || 'Tara leg'} ${node}`;\r\n          part.userData = { ...mesh.userData, quadrupedLimbNode: node, rigNodeName: node };\r\n          parent?.add(part);\r\n        }\r\n        moved += ids.length;\r\n      }\r\n      old.dispose();\r\n    }\r\n    if (bodyTransferred < 450 || headTransferred !== 2190 || moved < 7000) return false;\r\n    source.updateWorldMatrix(true, true);\r\n    const hints = {} as Record<QuadrupedLimbNode, QuadrupedJointHint>;\r\n    for (const cluster of clusters) hints[cluster.node!] = {\r\n      position: [cluster.center.x, cutoff, cluster.center.z], side: cluster.side!, fore: cluster.fore!,\r\n    };\r\n    source.userData.quadrupedLimbHints = hints;\r\n    storeMeshJointHints(source, hints);\r\n    source.userData.quadrupedOwnershipVersion = 1;\r\n    source.userData.quadrupedOwnershipTriangles = moved;\r\n    source.userData.quadrupedOwnershipBodyTriangles = bodyTransferred;\r\n    source.userData.quadrupedOwnershipBodyTransferredTriangles = bodyTransferred;\r\n    source.userData.quadrupedOwnershipHeadTriangles = headTransferred;\r\n    source.userData.quadrupedOwnershipHeadTransferredTriangles = headTransferred;\r\n    return true;\r\n  }\r\n  if (sourceId === 'roster/18-quad-all--wooden_four-legged_robot_3d_model') {\r\n    topology.groups.forEach((members, id) => {\r\n      let bodyFaces = 0, armFaces = 0, footFaces = 0; const box = new T.Box3();\r\n      for (const index of members) { const tri = allLow[index]; if (tri.region === 'body') bodyFaces++; if (tri.region === 'arms') armFaces++; if (tri.region === 'feet') footFaces++; for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x,p.y,p.z)); }\r\n      const size = box.getSize(new T.Vector3());\r\n      // The verified wooden model has one continuous rear underside bridging both\r\n      // sides. Preserve it as body-owned instead of cleaving it between hind feet.\r\n      if (bodyFaces > 0 && armFaces > 0 && footFaces > 0 && size.x > bodySize.x * 0.4 && size.z > bodySize.z * 0.7) heldComponents.add(id);\r\n    });\r\n  }\r\n  if (sourceId === 'roster/18-quad-all--robotic_dog_3d_model') {\r\n    const bodyCenter = bodyBox.getCenter(new T.Vector3());\r\n    const front = inferFront(source, bodyBox, headBox);\r\n    topology.groups.forEach((members, id) => {\r\n      let armFaces = 0, otherFaces = 0; const box = new T.Box3();\r\n      for (const index of members) {\r\n        const tri = allLow[index];\r\n        if (tri.region === 'arms') armFaces++; else otherFaces++;\r\n        for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n      }\r\n      const size = box.getSize(new T.Vector3()), center = box.getCenter(new T.Vector3());\r\n      const foreDepth = (center.x - bodyCenter.x) * front.x + (center.z - bodyCenter.z) * front.z;\r\n      // Two source ARM flecks sit inside the chest, immediately below its front\r\n      // edge. They are not appendages: routing them to either foreleg makes them\r\n      // swing visibly under the abdomen. Keep only these small, central components\r\n      // body-owned; the four full limb components remain independently animated.\r\n      if (otherFaces === 0 && armFaces >= 70 && armFaces <= 95\r\n        && Math.abs(center.x - bodyCenter.x) < bodySize.x * 0.3\r\n        && foreDepth > bodySize.z * 0.3\r\n        && center.y > cutoff - feetBox.getSize(new T.Vector3()).y * 0.45\r\n        && size.x < bodySize.x * 0.35 && size.y < bodySize.y * 0.4 && size.z < bodySize.z * 0.4) {\r\n        heldComponents.add(id);\r\n      }\r\n    });\r\n  }\r\n  if (sourceId === 'roster/18-quad-all--quadruped_robot_3d_model1') {\r\n    const bodyCenter = bodyBox.getCenter(new T.Vector3()), front = inferFront(source, bodyBox, headBox);\r\n    topology.groups.forEach((members, id) => {\r\n      const counts: Record<string, number> = {}, box = new T.Box3();\r\n      for (const index of members) {\r\n        const tri = allLow[index]; counts[tri.region] = (counts[tri.region] || 0) + 1;\r\n        for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n      }\r\n      const center = box.getCenter(new T.Vector3()), size = box.getSize(new T.Vector3());\r\n      const frontOffset = (center.x - bodyCenter.x) * front.x + (center.z - bodyCenter.z) * front.z;\r\n      // Werner's isolated 548-face central apron is authored in ARMS, but it is\r\n      // below the torso and has no contact with any of the four grounded limbs.\r\n      // Keep the whole centerline component with BodyMotion instead of letting\r\n      // nearest-paw voting make it swing as a detached fifth appendage.\r\n      if (members.length === 548 && counts.arms === 548\r\n        && Math.abs(center.x - bodyCenter.x) < bodySize.x * 0.08\r\n        && center.y < cutoff * 0.4 && frontOffset > bodySize.z * 0.25\r\n        && size.x < bodySize.x * 0.1 && size.y < feetBox.getSize(new T.Vector3()).y * 0.3\r\n        && size.z < bodySize.z * 0.12) {\r\n        const bodyMeshes = regionMeshes.get('body') ?? [];\r\n        const nearestBody = closestSurfacePoint(bodyMeshes, center);\r\n        if (!nearestBody) return;\r\n        const outward = nearestBody.normal;\r\n        if (outward.dot(center.clone().sub(nearestBody.point)) < 0) outward.negate();\r\n        const half = size.clone().multiplyScalar(0.5);\r\n        const radius = Math.sqrt(sq(outward.x * half.x) + sq(outward.y * half.y) + sq(outward.z * half.z));\r\n        if (!(radius > 0) || nearestBody.distance <= radius * 0.5) return;\r\n        // Keep the orphan orb as a visible belly detail: its center sits at 82% of\r\n        // its directional support radius beyond the measured torso surface, so it\r\n        // intersects slightly without disappearing into the body.\r\n        const contactPoint = nearestBody.point.clone();\r\n        const targetCenter = contactPoint.clone().addScaledVector(outward, radius * 0.82);\r\n        heldComponents.add(id); bodyAttachmentComponents.add(id);\r\n        bodyAttachmentOffsets.set(id, targetCenter.sub(center));\r\n        bodyAttachmentContacts.set(id, contactPoint);\r\n      }\r\n    });\r\n  }\r\n  const seedTriangles: Triangle[] = [];\r\n  for (let i = 0; i < allLow.length; i++) {\r\n    const tri = allLow[i];\r\n    if (!heldComponents.has(topology.component[i]) && (tri.region === 'arms' || tri.region === 'feet') && tri.center.y <= feetBox.min.y + (feetBox.max.y - feetBox.min.y) * 0.68) seedTriangles.push(tri);\r\n  }\r\n  if (allLow.length < 100 || seedTriangles.length < 80) return false;\r\n  const clusters = clusterFour(seedTriangles.map(t => t.center));\r\n  if (!clusters) return false;\r\n\r\n  const centerX = bodyBox.getCenter(new T.Vector3()).x;\r\n  const centerZ = bodyBox.getCenter(new T.Vector3()).z;\r\n  const front = inferFront(source, bodyBox, headBox);\r\n  const ranked = clusters.map(c => ({ c, foreScore: (c.center.x - centerX) * front.x + (c.center.z - centerZ) * front.z, sideScore: c.center.x - centerX }));\r\n  const fore = [...ranked].sort((a, b) => b.foreScore - a.foreScore), rear = fore.slice(2);\r\n  const setPair = (pair: typeof ranked, foreleg: boolean) => {\r\n    const [left, right] = [...pair].sort((a, b) => a.sideScore - b.sideScore);\r\n    for (const row of [left, right]) {\r\n      row.c.fore = foreleg; row.c.side = row === left ? -1 : 1;\r\n      row.c.node = foreleg ? (row.c.side < 0 ? 'ArmLeft' : 'ArmRight') : (row.c.side < 0 ? 'FootLeft' : 'FootRight');\r\n    }\r\n  };\r\n  setPair(fore.slice(0, 2), true); setPair(rear, false);\r\n\r\n  const center = { x: centerX, y: 0, z: centerZ };\r\n  const nearest = (p: Point) => clusters.map((c, i) => ({ i, d: dist2XZ(p, c.center) })).sort((a, b) => a.d - b.d)[0];\r\n  const clusterRadius = Math.max(...clusters.map(c => c.radius));\r\n  const maxAttach = Math.max(0.14, clusterRadius * 1.8);\r\n  const assigned = new Map<Triangle, QuadrupedLimbNode>();\r\n  for (let componentId = 0; componentId < topology.groups.length; componentId++) {\r\n    const memberIndexes = topology.groups[componentId];\r\n    if (heldComponents.has(componentId)) continue;\r\n    const members = memberIndexes.map(i => allLow[i]);\r\n    const sourceLimbFaces = members.filter(tri => tri.region === 'arms' || tri.region === 'feet').length;\r\n    const box = new T.Box3();\r\n    const votes = new Int32Array(clusters.length);\r\n    for (const tri of members) {\r\n      for (const p of tri.points) box.expandByPoint(new T.Vector3(p.x, p.y, p.z));\r\n      votes[nearest(tri.center).i]++;\r\n    }\r\n    const size = box.getSize(new T.Vector3());\r\n    const compactLimb = sourceLimbFaces > 0 && size.x <= bodySize.x * 1.05 && size.z <= bodySize.z * 1.05;\r\n    if (compactLimb) {\r\n      // A physical appendage is one connected source component. Give every one of its\r\n      // triangles the same joint owner; per-face nearest-cluster routing shears long toes\r\n      // and curved limbs at Voronoi boundaries when they pass close to another leg.\r\n      let winner = 0;\r\n      for (let i = 1; i < votes.length; i++) if (votes[i] > votes[winner]) winner = i;\r\n      const node = clusters[winner].node;\r\n      if (!node) return false;\r\n      for (const tri of members) assigned.set(tri, node);\r\n      continue;\r\n    }\r\n    // Fused, broad low shells retain their whole-body ownership where possible. For\r\n    // remaining broad geometry, keep the established contact-aware triangle fallback.\r\n    for (const tri of members) {\r\n      const { i, d } = nearest(tri.center), candidate = clusters[i];\r\n      if (!candidate.node) return false;\r\n      const bodyOwned = tri.region === 'body' || tri.region === 'head';\r\n      if (bodyOwned && Math.sqrt(d) > maxAttach) continue;\r\n      const sideDistance = Math.sqrt(dist2XZ(candidate.center, center));\r\n      if (bodyOwned && sideDistance < 0.08) continue;\r\n      assigned.set(tri, candidate.node);\r\n    }\r\n  }\r\n  const bodyRegion = source.getObjectByName('body');\r\n  const bodyMaterial = bodyRegion ? regionMeshes.get('body')!.find(mesh => !Array.isArray(mesh.material))?.material : undefined;\r\n  const byMesh = new Map<T.Mesh, Map<QuadrupedLimbNode | 'body' | 'bodyAttachment', number[]>>();\r\n  let bodyOwnedTriangles = 0;\r\n  let bodyTransferredTriangles = 0;\r\n  let bodyTransferredFromFeet = 0;\r\n  const addTarget = (tri: Triangle, node: QuadrupedLimbNode | 'body' | 'bodyAttachment') => {\r\n    let targets = byMesh.get(tri.mesh); if (!targets) byMesh.set(tri.mesh, targets = new Map());\r\n    let ids = targets.get(node); if (!ids) targets.set(node, ids = []); ids.push(tri.id);\r\n  };\r\n  for (let i = 0; i < allLow.length; i++) if (heldComponents.has(topology.component[i])) {\r\n    const tri = allLow[i];\r\n    addTarget(tri, bodyAttachmentComponents.has(topology.component[i]) ? 'bodyAttachment' : 'body'); bodyOwnedTriangles++;\r\n    if (tri.region !== 'body') bodyTransferredTriangles++;\r\n    if (tri.region === 'feet') bodyTransferredFromFeet++;\r\n  }\r\n  for (const [tri, node] of assigned) {\r\n    addTarget(tri, node);\r\n  }\r\n  let moved = 0;\r\n  for (const [mesh, targets] of byMesh) {\r\n    const movedIds = new Set([...targets.values()].flat());\r\n    if (!movedIds.size || !mesh.parent) continue;\r\n    const count = (mesh.geometry.index?.count ?? mesh.geometry.getAttribute('position').count) / 3;\r\n    const remaining: number[] = [];\r\n    for (let id = 0; id < count; id++) if (!movedIds.has(id)) remaining.push(id);\r\n    const kept = triangleSubset(mesh.geometry, remaining);\r\n    const old = mesh.geometry, parent = mesh.parent;\r\n    const parts: Array<{ node: QuadrupedLimbNode | 'body'; geometry: T.BufferGeometry }> = [];\r\n    for (const [node, ids] of targets) {\r\n      const geometry = triangleSubset(old, ids);\r\n      if (geometry) parts.push({ node, geometry });\r\n    }\r\n    if (kept) mesh.geometry = kept; else mesh.removeFromParent();\r\n    for (const { node, geometry } of parts) {\r\n      const part = mesh.clone(false); part.geometry = geometry;\r\n      const count = (geometry.index?.count ?? geometry.getAttribute('position').count) / 3;\r\n      if (node === 'body' || node === 'bodyAttachment') {\r\n        if (!bodyRegion || !bodyMaterial) { geometry.dispose(); continue; }\r\n        const isAttachment = node === 'bodyAttachment';\r\n        part.name = isAttachment ? `${mesh.name || 'Quadruped detail'} attached belly detail` : `${mesh.name || 'Quadruped underside'} body-owned underside`;\r\n        part.userData = { ...mesh.userData, quadrupedBodyOwnedTriangles: count };\r\n        source.updateWorldMatrix(true, true); bodyRegion.updateWorldMatrix(true, false);\r\n        const relative = bodyRegion.matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n        relative.decompose(part.position, part.quaternion, part.scale);\r\n        if (isAttachment) {\r\n          const worldOffset = sourceId === WERNER_APRON_ID && count === 548\r\n            ? bodyAttachmentOffsets.values().next().value as T.Vector3 | undefined : undefined;\r\n          const contactPoint = sourceId === WERNER_APRON_ID && count === 548\r\n            ? bodyAttachmentContacts.values().next().value as T.Vector3 | undefined : undefined;\r\n          if (!worldOffset || !contactPoint) { geometry.dispose(); continue; }\r\n          const localOffset = worldOffset.clone().applyMatrix3(new T.Matrix3().setFromMatrix4(bodyRegion.matrixWorld.clone().invert()));\r\n          part.position.add(localOffset);\r\n          part.userData.ownershipAttachmentWorldOffset = worldOffset.toArray();\r\n          part.userData.ownershipAttachmentBodyContactWorldPoint = contactPoint.toArray();\r\n          part.userData.ownershipAttachment = 'body-belly-detail';\r\n          part.material = mesh.material;\r\n        } else part.material = bodyMaterial;\r\n        bodyRegion.add(part);\r\n      } else {\r\n        part.name = `${mesh.name || 'Quadruped limb'} ${node}`;\r\n        part.userData = { ...mesh.userData, quadrupedLimbNode: node, rigNodeName: node };\r\n        parent?.add(part);\r\n      }\r\n      moved += count;\r\n    }\r\n    old.dispose();\r\n  }\r\n  if (!moved) return false;\r\n\r\n  const hints = {} as Record<QuadrupedLimbNode, QuadrupedJointHint>;\r\n  for (const c of clusters) {\r\n    const triangles = assigned;\r\n    const points: Point[] = [];\r\n    for (const [tri, node] of triangles) if (node === c.node && Math.sqrt(dist2XZ(tri.center, c.center)) <= maxAttach && tri.center.y >= cutoff - Math.max(0.08, (cutoff - feetBox.min.y) * 0.18)) points.push(tri.center);\r\n    const joint = points.length ? points.reduce((sum, p) => add(sum, p), { x: 0, y: 0, z: 0 }) : { ...c.center, y: cutoff };\r\n    const p = points.length ? scale(joint, 1 / points.length) : joint;\r\n    hints[c.node!] = { position: [p.x, p.y, p.z], side: c.side!, fore: c.fore! };\r\n  }\r\n  source.userData.quadrupedLimbHints = hints;\r\n  storeMeshJointHints(source, hints);\r\n  source.userData.quadrupedOwnershipVersion = 1;\r\n  source.userData.quadrupedOwnershipTriangles = moved;\r\n  source.userData.quadrupedOwnershipBodyTriangles = bodyOwnedTriangles;\r\n  source.userData.quadrupedOwnershipBodyTransferredTriangles = bodyTransferredTriangles;\r\n  source.userData.quadrupedOwnershipBodyTransferredFromFeet = bodyTransferredFromFeet;\r\n  return true;\r\n}\r\n","creature/source/rescue-model.ts":"import * as T from 'three';\nimport {assembleCreature} from './creator/assemble';\nimport {fresh} from './creator/design';\nimport {createRig,disposeObject} from './rig';\nimport {MotionController} from './motion';\n\n/** Creator assembly and authored procedural rig, with no viewer, renderer, stream or loop. */\nexport async function loadRescueCoach(sourceId:string,{reducedMotion=false,signal}: {reducedMotion?:boolean;signal?:AbortSignal}={}){\n const abort=()=>Object.assign(new Error('Rescue model load aborted.'),{name:'AbortError'});\n if(signal?.aborted)throw abort();\n const recipe=fresh();recipe.golden=false;\n recipe.body=recipe.headFrom=recipe.armsFrom=recipe.feetFrom=sourceId;\n const assembled=await assembleCreature(recipe,'/creature/');\n let rig:ReturnType<typeof createRig>|null=null,motion:MotionController|null=null;\n try{\n  if(signal?.aborted)throw abort();\n  const head=assembled.root.getObjectByName('head');\n  if(!head)throw Error('This coach has no verified head region.');\n  const headBox=new T.Box3().setFromObject(head);\n  if(headBox.isEmpty())throw Error('This coach head anchor is unavailable.');\n  const anchor=headBox.getCenter(new T.Vector3());anchor.z=headBox.max.z;\n  rig=createRig(assembled.root,recipe);rig.root.updateMatrixWorld(true);\n  const target=new T.Object3D();target.name='RescueHeadAnchor';target.position.copy(rig.nodes.HeadMotion.worldToLocal(anchor));rig.nodes.HeadMotion.add(target);\n  const bounds=new T.Box3().setFromObject(rig.root),height=bounds.max.y-bounds.min.y;\n  if(!Number.isFinite(height)||height<=0)throw Error('Coach geometry is empty.');\n  const root=new T.Group();root.name=`rescue-coach:${sourceId}`;root.add(rig.root);rig.root.position.y=-bounds.min.y;root.scale.setScalar(2.3/height);\n  motion=new MotionController(rig);motion.reduced=reducedMotion;motion.amount=.65;motion.ambient=!reducedMotion;\n  const textures=new Set<T.Texture>(assembled.skinTextures);rig.root.traverse((object:any)=>{for(const material of Array.isArray(object.material)?object.material:object.material?[object.material]:[])for(const value of Object.values(material))if((value as T.Texture)?.isTexture)textures.add(value as T.Texture);});\n  let disposed=false;\n  return {root,head:target,headWidth:(headBox.max.x-headBox.min.x)*2.3/height,sourceId,play:(id:any)=>motion!.play(id),update:(dt:number)=>motion!.update(dt),dispose(){if(disposed)return;disposed=true;root.removeFromParent();motion!.dispose();disposeObject(rig!.root);for(const texture of textures)texture.dispose();textures.clear();root.clear();}};\n }catch(error){motion?.dispose();if(rig)disposeObject(rig.root);throw error;}\n finally{assembled.dispose();}\n}\n","creature/source/reward-preview.ts":"import {CreatureViewer} from './viewer';\r\nimport {fresh,REGIONS} from './creator/design';\r\nimport {findTexture,textureDefaultMetalness,findColor,findPalette} from './creator/materials-registry';\r\nimport {isGranted} from './creator/unlock-store';\r\n\r\n// Reuse the real coach and material renderer. This recipe never reaches storage,\r\n// the equipped coach, or the editor's locked-item preview path.\r\nexport function mountOriginalRewardPreview(host:HTMLElement,reward:{kind:'texture'|'color'|'palette';id:string}) {\r\n if(!isGranted(reward.kind,reward.id,'myr5'))throw Error('This cosmetic has not been earned.');\r\n const texture=reward.kind==='texture'?findTexture(reward.id):findTexture('clay');\r\n if(!texture||reward.kind==='palette'&&!findPalette(reward.id)||reward.kind==='color'&&!/^#[0-9a-f]{6}$/i.test(reward.id)&&!findColor(reward.id))throw Error('This cosmetic is unavailable.');\r\n const recipe=fresh(),colorId=reward.kind==='texture'?texture.defaultColorId:reward.id;\r\n recipe.golden=false;\r\n recipe.materials=Object.fromEntries(REGIONS.filter(region=>region!=='eye').map(region=>[region,{textureId:texture.id,colorId,sparkle:0,metallic:textureDefaultMetalness(texture.id)??0}]));\r\n const viewer=new CreatureViewer(host,new URL('../',import.meta.url).href,false);\r\n viewer.renderer.domElement.setAttribute('aria-label',`Original MYR5 coach wearing the earned ${reward.kind}`);\r\n viewer.setSettings({...viewer.settings,reduced:true,amount:0});viewer.setMaxFps(12);viewer.setPaused(true);viewer.setStage('overlay');\r\n const resize=new ResizeObserver(()=>{if(!viewer.disposed&&viewer.rig)viewer.warm();});resize.observe(host);\r\n const ready=viewer.setRecipe(recipe).then(loaded=>{\r\n  if(!loaded||viewer.disposed)return false;\r\n  host.dataset.ready='true';host.dataset.body=recipe.body;host.dataset.kind=reward.kind;host.dataset.rewardId=reward.id;\r\n  viewer.warm();viewer.setAwake(false);return true;\r\n });\r\n return {ready,dispose:()=>{resize.disconnect();viewer.dispose();}};\r\n}\r\n\r\nexport function cosmeticRewardName(kind:string,id:string) {\r\n return (kind==='texture'?findTexture(id):kind==='color'?findColor(id):findPalette(id))?.displayName||id;\r\n}\r\n","creature/source/rig.ts":"import * as T from 'three';\r\nimport {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils.js';\r\nimport {EYE_LAYOUTS} from './creator/eye-layouts';\r\nimport {eyePosition} from './creator/anatomy';\r\nimport type {Design} from './creator/design';\r\nimport {connectedComponentSides,connectedTriangleComponents} from './limb-ownership';\r\n\r\nconst ROUS_SIDE_PLANE_SOURCE='roster/09-monolith-tanka--robot_3d_model2';\r\n\r\n/** Infer the bilateral split from the two largest grounded connected components under a source feet group. */\r\nexport function inferGroundedSidePlaneX(footRegion:T.Object3D){\r\n footRegion.updateWorldMatrix(true,true);const footMeshes:T.Mesh[]=[];footRegion.traverse(o=>{if(o instanceof T.Mesh&&o.visible)footMeshes.push(o);});const footBox=new T.Box3();for(const mesh of footMeshes)footBox.expandByObject(mesh);const height=footBox.max.y-footBox.min.y;if(!Number.isFinite(height)||height<=0)return 0;\r\n const grounded:{triangles:number;centerX:number}[]=[];\r\n for(const mesh of footMeshes)for(const ids of connectedTriangleComponents(mesh)){\r\n  const p=mesh.geometry.getAttribute('position'),index=mesh.geometry.index;let minY=Infinity,minX=Infinity,maxX=-Infinity;\r\n  for(const triangle of ids)for(let k=0;k<3;k++){const v=new T.Vector3().fromBufferAttribute(p,index?index.getX(triangle*3+k):triangle*3+k).applyMatrix4(mesh.matrixWorld);minY=Math.min(minY,v.y);minX=Math.min(minX,v.x);maxX=Math.max(maxX,v.x);}\r\n  if(minY>footBox.min.y+height*.1)continue;grounded.push({triangles:ids.length,centerX:(minX+maxX)/2});\r\n }\r\n grounded.sort((a,b)=>b.triangles-a.triangles);return grounded.length>=2?(grounded[0].centerX+grounded[1].centerX)/2:0;\r\n}\r\n\r\nexport function disposeObject(root:T.Object3D){const geometries=new Set<T.BufferGeometry>(),materials=new Set<T.Material>();root.traverse(o=>{if(o instanceof T.SkinnedMesh)o.skeleton.dispose();if(o instanceof T.Mesh){geometries.add(o.geometry);for(const m of Array.isArray(o.material)?o.material:[o.material])materials.add(m);}});geometries.forEach(g=>g.dispose());materials.forEach(m=>m.dispose());}\r\n\r\n// The source arms and feet contain both sides in one mesh. Partition complete\r\n// triangles at the empty center gap, preserving the creator's world coordinates.\r\nfunction bake(mesh:T.Mesh,side=0,pick?:(triangle:number)=>boolean,componentSides?:Int8Array,sidePlaneX=0){\r\n const input=mesh.geometry.clone();input.applyMatrix4(mesh.matrixWorld);\r\n if(!input.getAttribute('normal'))input.computeVertexNormals();\r\n // A flipX mirror (negative-scale ancestor, e.g. assemble.ts's facing correction) reverses handedness:\r\n // applyMatrix4 above already re-orients the normal attribute correctly, but triangle winding order is\r\n // a vertex-index property it can't touch, so front/back-face culling would go inside-out unless the\r\n // winding is reversed here too (swap the last two vertices of every triangle).\r\n const mirrored=mesh.matrixWorld.determinant()<0;\r\n const pos=input.getAttribute('position'),normal=input.getAttribute('normal'),color=input.getAttribute('color'),uv=input.getAttribute('uv');\r\n const p:number[]=[],n:number[]=[],c:number[]=[],tex:number[]=[],indices:number[]=[],remap=new Map<number,number>();\r\n const count=input.index?.count??pos.count,vertex=(i:number)=>input.index?input.index.getX(i):i;\r\n for(let i=0;i<count;i+=3){const triangleId=i/3;if(pick&&!pick(triangleId))continue;const triangle=[vertex(i),vertex(i+1),vertex(i+2)],x=triangle.reduce((sum,j)=>sum+pos.getX(j),0)/3,componentSide=componentSides?.[triangleId]??0,chosenSide=componentSide|| (x<sidePlaneX?-1:1);if(side&&chosenSide!==side)continue;\r\n  if(mirrored)[triangle[1],triangle[2]]=[triangle[2],triangle[1]];\r\n  for(const j of triangle){if(!remap.has(j)){remap.set(j,p.length/3);p.push(pos.getX(j),pos.getY(j),pos.getZ(j));n.push(normal.getX(j),normal.getY(j),normal.getZ(j));c.push(color?color.getX(j):1,color?color.getY(j):1,color?color.getZ(j):1);tex.push(uv?uv.getX(j):0,uv?uv.getY(j):0);}indices.push(remap.get(j)!);}\r\n }\r\n input.dispose();if(!p.length)return null;\r\n const geometry=new T.BufferGeometry();geometry.setAttribute('position',new T.Float32BufferAttribute(p,3));geometry.setAttribute('normal',new T.Float32BufferAttribute(n,3));geometry.setAttribute('color',new T.Float32BufferAttribute(c,3));geometry.setAttribute('uv',new T.Float32BufferAttribute(tex,2));geometry.setIndex(indices);return geometry;\r\n}\r\n\r\nexport function createRig(source:T.Group,recipe:Design){\r\n const root=new T.Group();root.name='MYR5';root.userData={rigVersion:1,recipe:JSON.parse(JSON.stringify(recipe))};\r\n const nodes:Record<string,T.Group>={};\r\n function pivot(name:string,position:number[],parent=root){const node=new T.Group();node.name=name;node.position.fromArray(position);parent.add(node);nodes[name]=node;return node;}\r\n const fitted=source.userData.pivots as {body:number[];head:number[];arm:number[];foot:number[]}|null|undefined;\r\n const P=fitted??{body:[0,.8,0],head:[0,.75,0],arm:[.55,.48,0],foot:[.35,.35,0]};\r\n const body=pivot('BodyMotion',P.body);\r\n const head=pivot('HeadMotion',P.head,body);\r\n pivot('ArmLeft',[-P.arm[0],P.arm[1],P.arm[2]],body);pivot('ArmRight',P.arm,body);\r\n pivot('FootLeft',[-P.foot[0],P.foot[1],P.foot[2]]);pivot('FootRight',P.foot);\r\n const eyes=EYE_LAYOUTS[recipe.eyeLayout].eyes;\r\n const eyeOffset=(source.userData.eyeOffset as T.Vector3|undefined)??new T.Vector3();\r\n const eyeScale=(source.userData.eyeScale as number|undefined)??1,headY=P.body[1]+P.head[1];\r\n eyes.forEach((eye,i)=>{const [x,y,z]=eyePosition(eye,eyeOffset,eyeScale,source.userData.eyeSurfaceZ as number|undefined);pivot('EyeBlink'+i,[x,y-headY,z],head);});\r\n root.updateMatrixWorld(true);source.updateMatrixWorld(true);\r\n // Assemblies preserve a plane fitted from the matching roster source. A direct source-model rig can\r\n // infer it from its own feet; never borrow another coach's feet for a mixed-source recipe.\r\n const fittedSidePlanes=source.userData.limbSidePlanes as Partial<Record<'arms'|'feet',number>>|undefined;\r\n const directFeetSidePlane=recipe.feetFrom===ROUS_SIDE_PLANE_SOURCE?inferGroundedSidePlaneX(source.getObjectByName('feet')??source):0;\r\n const sidePlaneX=(region:'arms'|'feet')=>Number.isFinite(fittedSidePlanes?.[region])?fittedSidePlanes![region]!:region==='feet'?directFeetSidePlane:recipe.feetFrom===ROUS_SIDE_PLANE_SOURCE?directFeetSidePlane:0;\r\n const buckets=new Map<string,{node:T.Group;material:T.MeshStandardMaterial;geometries:T.BufferGeometry[]}>();\r\n function add(mesh:T.Mesh,node:T.Group,side=0,pick?:(triangle:number)=>boolean,componentSides?:Int8Array,sidePlaneX=0){\r\n  if(Array.isArray(mesh.material))throw Error('This MYR5 mesh uses an unsupported material layout.');\r\n  const geometry=bake(mesh,side,pick,componentSides,sidePlaneX);if(!geometry)return;\r\n  geometry.applyMatrix4(node.matrixWorld.clone().invert());\r\n  const mat=mesh.material as T.MeshStandardMaterial;\r\n  const physical=mat as T.MeshPhysicalMaterial;\r\n  const key=node.name+JSON.stringify([mat.type,mat.color.getHex(),mat.emissive.getHex(),mat.emissiveIntensity,mat.roughness,mat.metalness,mat.opacity,mat.side,mat.map?.uuid,mat.normalMap?.uuid,mat.bumpMap?.uuid,mat.roughnessMap?.uuid,mat.emissiveMap?.uuid,mat.bumpScale,physical.transmission,physical.thickness,physical.ior,physical.clearcoat,physical.sheen,mat.userData.installedSkinId?mat.uuid:null]);\r\n  if(!buckets.has(key)){const material=mat.clone();material.vertexColors=true;material.userData={};if(mat.userData.installedSkinId){material.onBeforeCompile=mat.onBeforeCompile;material.customProgramCacheKey=mat.customProgramCacheKey;material.userData.installedSkinId=mat.userData.installedSkinId;material.userData.normalConvention=mat.userData.normalConvention;}buckets.set(key,{node,material,geometries:[]});}\r\n  buckets.get(key)!.geometries.push(geometry);\r\n }\r\n function visit(obj:T.Object3D,region:string,eyeIndex=0,sourceLimb=false){\r\n  if(!obj.visible)return;\r\n  if(/^Eye [1-8]$/.test(obj.name))eyeIndex=Number(obj.name.split(' ')[1])-1;\r\n  if(obj instanceof T.Mesh){\r\n   const explicitNode=sourceLimb&&typeof obj.userData.rigNodeName==='string'?nodes[obj.userData.rigNodeName]:undefined;\r\n   if(explicitNode){\r\n    explicitNode.userData.explicitSourceLimb=true;\r\n    const localJoint=obj.userData.rigJointLocal;\r\n    if(!explicitNode.userData.explicitJointSet&&Array.isArray(localJoint)&&localJoint.length===3&&localJoint.every(Number.isFinite)){\r\n     const previousWorld=explicitNode.matrixWorld.clone();\r\n     const joint=new T.Vector3().fromArray(localJoint).applyMatrix4(obj.matrixWorld);\r\n     explicitNode.position.copy(explicitNode.parent!.worldToLocal(joint));explicitNode.updateWorldMatrix(true,true);explicitNode.userData.explicitJointSet=true;\r\n     // A mixed recipe may have already supplied geometry to this motion node.\r\n     // Changing its pivot must preserve those surfaces in their rest positions.\r\n     const correction=explicitNode.matrixWorld.clone().invert().multiply(previousWorld);\r\n     for(const bucket of buckets.values())if(bucket.node===explicitNode)for(const geometry of bucket.geometries)geometry.applyMatrix4(correction);\r\n    }\r\n    add(obj,explicitNode);\r\n   }\r\n   else if(region==='arms'){\r\n    const sidePlane=recipe.armsFrom===ROUS_SIDE_PLANE_SOURCE?sidePlaneX('arms'):0,componentSides=recipe.armsFrom!=='myr5'&&sourceLimb?connectedComponentSides(obj,sidePlane):undefined;\r\n    // Keep a fused center-crossing component animated: complete one-sided components\r\n    // retain their component owner, while neutral triangles fall back to their own\r\n    // centroid side inside bake (each face stays intact and no triangles are lost).\r\n    if(componentSides){add(obj,nodes.ArmLeft,-1,i=>componentSides[i]===-1||componentSides[i]===0,componentSides,sidePlane);add(obj,nodes.ArmRight,1,i=>componentSides[i]===1||componentSides[i]===0,componentSides,sidePlane);}\r\n    else{add(obj,nodes.ArmLeft,-1);add(obj,nodes.ArmRight,1);}\r\n   }\r\n   else if(region==='feet'){\r\n    const sidePlane=recipe.feetFrom===ROUS_SIDE_PLANE_SOURCE?sidePlaneX('feet'):0,componentSides=recipe.feetFrom!=='myr5'&&sourceLimb?connectedComponentSides(obj,sidePlane):undefined;\r\n    if(componentSides){add(obj,nodes.FootLeft,-1,i=>componentSides[i]===-1||componentSides[i]===0,componentSides,sidePlane);add(obj,nodes.FootRight,1,i=>componentSides[i]===1||componentSides[i]===0,componentSides,sidePlane);}\r\n    else{add(obj,nodes.FootLeft,-1);add(obj,nodes.FootRight,1);}\r\n   }\r\n   else add(obj,region==='eye'?nodes['EyeBlink'+eyeIndex]:region==='head'?head:body);\r\n  }\r\n  for(const child of obj.children)visit(child,region,eyeIndex,sourceLimb);\r\n }\r\n for(const part of source.children){if(part.name==='Style ornaments'){for(const detail of part.children)visit(detail,detail.userData.region);}else visit(part,part.name,0,part.name==='arms'||part.name==='feet');}\r\n for(const {node,material,geometries} of buckets.values()){\r\n  const combined=mergeGeometries(geometries,false);geometries.forEach(g=>g.dispose());if(!combined)throw Error('Could not assemble this creature.');\r\n  combined.computeBoundingSphere();const mesh=new T.Mesh(combined,material);mesh.name=node.name+'Surface';node.add(mesh);\r\n }\r\n // Rigid parts opened a gap wherever they met the body (neck, ankles) as soon as they turned. The head and\r\n // feet are skinned to BodyMotion instead: a vertex's share of its own part grows with its distance from the\r\n // body's surface, so the seam stays welded to the body and the joint bends rather than separating.\r\n root.updateMatrixWorld(true);\r\n const meshes=(node:T.Object3D)=>node.children.filter((o):o is T.Mesh=>o instanceof T.Mesh);\r\n const boxOf=(node:T.Object3D)=>{const b=new T.Box3();for(const m of meshes(node))b.expandByObject(m);return b;};\r\n // Distance to the body's surface on a voxel grid around `near` (two-pass chamfer transform), Infinity outside it.\r\n function surfaceDistance(anchor:T.Object3D,near:T.Box3,touch:number,reach:number){\r\n  const zone=near.clone().expandByScalar(reach),size=zone.getSize(new T.Vector3()),voxel=Math.max(touch/2,Math.cbrt(size.x*size.y*size.z/4e5)),o=zone.min.clone().subScalar(voxel);\r\n  // One empty voxel of padding on every side lets the sweeps skip bounds checks.\r\n  const nx=Math.ceil(size.x/voxel)+3,ny=Math.ceil(size.y/voxel)+3,nz=Math.ceil(size.z/voxel)+3,field=new Float32Array(nx*ny*nz).fill(Infinity),at=(x:number,y:number,z:number)=>(x*ny+y)*nz+z;\r\n  const t=new T.Box3(),c=[new T.Vector3(),new T.Vector3(),new T.Vector3()],q=new T.Vector3();\r\n  for(const mesh of meshes(anchor)){const p=mesh.geometry.getAttribute('position'),index=mesh.geometry.index,n=index?index.count:p.count;\r\n   for(let i=0;i<n;i+=3){for(let j=0;j<3;j++)c[j].fromBufferAttribute(p,index?index.getX(i+j):i+j).applyMatrix4(mesh.matrixWorld);if(!t.setFromPoints(c).intersectsBox(zone))continue;\r\n    const k=Math.ceil(Math.max(c[0].distanceTo(c[1]),c[1].distanceTo(c[2]),c[2].distanceTo(c[0]))/voxel);\r\n    for(let u=0;u<=k;u++)for(let w=0;u+w<=k;w++){q.copy(c[0]).multiplyScalar(1-(u+w)/k).addScaledVector(c[1],u/k).addScaledVector(c[2],w/k).sub(o).divideScalar(voxel).round();if(q.x>0&&q.y>0&&q.z>0&&q.x<nx-1&&q.y<ny-1&&q.z<nz-1)field[at(q.x,q.y,q.z)]=0;}}}\r\n  const step:number[]=[],cost:number[]=[];for(let x=-1;x<=1;x++)for(let y=-1;y<=1;y++)for(let z=-1;z<=1;z++)if(x<0||(x===0&&(y<0||(y===0&&z<0)))){step.push(at(x,y,z)-at(0,0,0));cost.push(Math.hypot(x,y,z));}\r\n  for(const s of [1,-1])for(let x=s>0?1:nx-2;x>0&&x<nx-1;x+=s)for(let y=s>0?1:ny-2;y>0&&y<ny-1;y+=s)for(let z=s>0?1:nz-2;z>0&&z<nz-1;z+=s){const i=at(x,y,z);let best=field[i];\r\n   for(let k=0;k<13;k++){const d=field[i+s*step[k]]+cost[k];if(d<best)best=d;}field[i]=best;}\r\n  const lookup=(v:T.Vector3)=>{q.copy(v).sub(o).divideScalar(voxel).round();return q.x>=0&&q.y>=0&&q.z>=0&&q.x<nx&&q.y<ny&&q.z<nz?field[at(q.x,q.y,q.z)]*voxel:Infinity;};\r\n  return {lookup,voxel};\r\n }\r\n // Weld `node` to the body: within `touch` of the body a vertex follows BodyMotion, by `touch+band` it\r\n // follows its own pivot. With movePivot the pivot moves onto the seam (the true joint). Parts that don't\r\n // meet the body stay rigid.\r\n function closestAnchor(node:T.Group,touch:number,band:number){\r\n  const near=boxOf(node);let selected:T.Group=body,bestContacts=-1,bestDistance=Infinity;\r\n  if(near.isEmpty())return body;\r\n  for(const candidate of [body,head]){if(boxOf(candidate).isEmpty())continue;const {lookup}=surfaceDistance(candidate,near,touch,touch+band),v=new T.Vector3();let contacts=0,minDistance=Infinity;\r\n   for(const mesh of meshes(node)){const p=mesh.geometry.getAttribute('position');for(let i=0;i<p.count;i++){v.fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld);const d=lookup(v);if(d<touch)contacts++;if(d<minDistance)minDistance=d;}}\r\n   if(contacts>bestContacts||(contacts===bestContacts&&minDistance<bestDistance)){selected=candidate;bestContacts=contacts;bestDistance=minDistance;}\r\n  }\r\n  return selected;\r\n }\r\n function glue(node:T.Group,touch:number,band:number,movePivot=false,anchor:T.Object3D=body){\r\n  const box=boxOf(node),parts=meshes(node);if(box.isEmpty())return;\r\n  const limb=/^(Arm|Foot)/.test(node.name)&&((node.name.startsWith('Arm')?recipe.armsFrom:recipe.feetFrom)!=='myr5'),size=box.getSize(new T.Vector3()),limbExtent=Math.max(size.x,size.y,size.z);\r\n  const {lookup:distance,voxel}=surfaceDistance(anchor,box,touch,touch+band),v=new T.Vector3(),seam=new T.Vector3();let count=0;\r\n  // A coarse grid (very large parts) widens the welded band so voxel rounding never loosens the seam.\r\n  touch=Math.max(touch,2*voxel);const reach=touch+band;\r\n  let highestContact=-Infinity;\r\n  const distances=parts.map(mesh=>{const p=mesh.geometry.getAttribute('position'),d=new Float32Array(p.count);for(let i=0;i<p.count;i++){v.fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld);d[i]=distance(v);if(d[i]<touch)highestContact=Math.max(highestContact,v.y);}return d;});\r\n  parts.forEach((mesh,j)=>{const p=mesh.geometry.getAttribute('position');for(let i=0;i<p.count;i++){v.fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld);if(distances[j][i]<touch&&(!limb||v.y>=highestContact-limbExtent*.15)){seam.add(v);count++;}}});\r\n  if(!count)return;\r\n  const joint=seam.divideScalar(count);\r\n  const weldSize=Math.max(1e-5,limbExtent*1e-4),anchorVertices=new Map<string,T.Vector3[]>(),weldKey=(x:number,y:number,z:number)=>`${x},${y},${z}`;\r\n  if(limb)for(const mesh of meshes(anchor)){const p=mesh.geometry.getAttribute('position');for(let i=0;i<p.count;i++){const point=new T.Vector3().fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld),key=weldKey(Math.round(point.x/weldSize),Math.round(point.y/weldSize),Math.round(point.z/weldSize));const list=anchorVertices.get(key)||[];list.push(point);anchorVertices.set(key,list);}}\r\n  const isCutSeam=(point:T.Vector3)=>{const x=Math.round(point.x/weldSize),y=Math.round(point.y/weldSize),z=Math.round(point.z/weldSize);for(let dx=-1;dx<=1;dx++)for(let dy=-1;dy<=1;dy++)for(let dz=-1;dz<=1;dz++)for(const other of anchorVertices.get(weldKey(x+dx,y+dy,z+dz))||[])if(point.distanceToSquared(other)<=weldSize*weldSize)return true;return false;};\r\n  if(movePivot){const shift=joint.clone().sub(node.getWorldPosition(v));node.position.add(shift);for(const child of node.children)if(child instanceof T.Mesh)child.geometry.translate(-shift.x,-shift.y,-shift.z);else child.position.sub(shift);root.updateMatrixWorld(true);}\r\n  parts.forEach((mesh,j)=>{const g=mesh.geometry,d=distances[j],index=new Uint16Array(d.length*4),w=new Float32Array(d.length*4);\r\n   const connectedToAnchor=new Uint8Array(d.length);\r\n   if(limb)for(const faces of connectedTriangleComponents(mesh)){\r\n    const vertices=new Set<number>();for(const face of faces)for(let k=0;k<3;k++)vertices.add(g.index?g.index.getX(face*3+k):face*3+k);\r\n    if([...vertices].some(i=>d[i]<touch))for(const i of vertices)connectedToAnchor[i]=1;\r\n   }\r\n   // Separate armor, hands and toes follow the limb rigidly. Only components\r\n   // that actually meet the anchor need a blended shoulder/hip seam.\r\n   for(let i=0;i<d.length;i++){v.fromBufferAttribute(g.getAttribute('position'),i).applyMatrix4(mesh.matrixWorld);const distal=limb?T.MathUtils.smoothstep(v.distanceTo(joint),limbExtent*.15,limbExtent*.5):0,own=limb&&!connectedToAnchor[i]?1:limb&&v.distanceTo(joint)<=limbExtent*.35&&isCutSeam(v)?0:Math.max(distal,T.MathUtils.smoothstep(d[i],touch,reach));index[i*4+1]=1;w[i*4]=1-own;w[i*4+1]=own;}\r\n   g.setAttribute('skinIndex',new T.Uint16BufferAttribute(index,4));g.setAttribute('skinWeight',new T.Float32BufferAttribute(w,4));g.computeBoundingSphere();\r\n   const skinned=new T.SkinnedMesh(g,mesh.material);skinned.name=mesh.name;node.remove(mesh);node.add(skinned);skinned.bind(new T.Skeleton([anchor,node] as unknown as T.Bone[]));});\r\n }\r\n const headBox=boxOf(head),headH=headBox.max.y-headBox.min.y,eyeY=Math.min(...Object.keys(nodes).filter(n=>n.startsWith('EyeBlink')).map(n=>nodes[n].getWorldPosition(new T.Vector3()).y));\r\n // The neck blends in below the eyes (they ride the head rigidly), over at most 30% of the head.\r\n glue(head,.06*Math.max(headH,headBox.max.x-headBox.min.x),T.MathUtils.clamp(.6*(eyeY-headBox.min.y),.12*headH,.3*headH),true);\r\n // Roster arms share a narrow, body-weighted shoulder seam; the source MYR5 arms keep their existing pivots.\r\n if(recipe.armsFrom!=='myr5'){\r\n  const arms=[nodes.ArmLeft,nodes.ArmRight],largest=Math.max(...arms.map(arm=>{const size=boxOf(arm).getSize(new T.Vector3());return Math.max(size.x,size.y,size.z);}));\r\n  if(largest>0)for(const arm of arms){const touch=.04*largest,band=.15*largest,anchor=closestAnchor(arm,touch,band);glue(arm,touch,band,true,anchor);}\r\n }\r\n // A foot's scale is its largest side: a leg's length, a flat foot's length.\r\n for(const foot of [nodes.FootLeft,nodes.FootRight]){const size=boxOf(foot).getSize(new T.Vector3()),h=Math.max(size.x,size.y,size.z),touch=.15*h,band=.5*h,anchor=recipe.feetFrom!=='myr5'?closestAnchor(foot,touch,band):body;glue(foot,touch,band,recipe.feetFrom!=='myr5',anchor);}\r\n const rest=Object.fromEntries(Object.entries(nodes).map(([name,node])=>[name,{position:node.position.clone(),scale:node.scale.clone(),quaternion:node.quaternion.clone()}]));\r\n return {root,nodes,rest,recipe};\r\n}\r\nexport type CreatureRig=ReturnType<typeof createRig>;\r\n","creature/source/rous-ownership.ts":"import * as THREE from 'three';\r\nimport {connectedTriangleComponents, triangleSubset} from './limb-ownership';\r\n\r\nconst ROUS = 'roster/09-monolith-tanka--robot_3d_model2';\r\n\r\n/** Recover the exact left arm pieces placed in the body and feet cuts of Rous. */\r\nexport function correctRousArmOwnership(source: THREE.Group, sourceId: string) {\r\n  if (sourceId !== ROUS) return 0;\r\n  source.updateWorldMatrix(true, true);\r\n  const arms = source.getObjectByName('arms'), body = source.getObjectByName('body'), feet = source.getObjectByName('feet');\r\n  if (!arms || !body || !feet) return 0;\r\n  const intoArms = arms.matrixWorld.clone().invert();\r\n  let recovered = 0;\r\n  const extracted: Record<'body' | 'feet', number> = {body: 0, feet: 0};\r\n  const recover = (group: THREE.Object3D, sourceRegion: 'body' | 'feet') => {\r\n    const meshes: THREE.Mesh[] = [];\r\n    group.traverse(o => { if (o instanceof THREE.Mesh && o.visible && !Array.isArray(o.material)) meshes.push(o); });\r\n    for (const mesh of meshes) {\r\n      const position = mesh.geometry.getAttribute('position'), index = mesh.geometry.index;\r\n      const chosen = new Set<number>();\r\n      for (const component of connectedTriangleComponents(mesh)) {\r\n        if (component.length < (sourceRegion === 'body' ? 100 : 500)) continue;\r\n        const bounds = new THREE.Box3(); let centerX = 0, centerY = 0, count = 0;\r\n        for (const face of component) for (let k = 0; k < 3; k++) {\r\n          const vertex = index ? index.getX(face * 3 + k) : face * 3 + k;\r\n          const point = new THREE.Vector3().fromBufferAttribute(position, vertex).applyMatrix4(mesh.matrixWorld);\r\n          bounds.expandByPoint(point); centerX += point.x; centerY += point.y; count++;\r\n        }\r\n        centerX /= count; centerY /= count;\r\n        // The left shoulder and forearm sit wholly outside the source body midpoint.\r\n        // The feet-cut glove is the sole large negative-X component reaching above the floor.\r\n        const isLeftShoulder = sourceRegion === 'body' && centerX < 0 && bounds.max.x < .24 && bounds.min.y > 1.05 && bounds.max.y < 1.9;\r\n        const isLeftGlove = sourceRegion === 'feet' && centerX < 0 && centerY > .5 && bounds.max.y <= 1.081;\r\n        if (isLeftShoulder || isLeftGlove) component.forEach(face => chosen.add(face));\r\n      }\r\n      if (!chosen.size) continue;\r\n      const original = mesh.geometry, movedGeometry = triangleSubset(original, face => chosen.has(face));\r\n      const remaining = triangleSubset(original, face => !chosen.has(face));\r\n      if (!movedGeometry) continue;\r\n      const transform = intoArms.clone().multiply(mesh.matrixWorld);\r\n      movedGeometry.applyMatrix4(transform);\r\n      recovered += chosen.size;\r\n      if (remaining) { mesh.geometry = remaining; original.dispose(); }\r\n      else { mesh.removeFromParent(); original.dispose(); }\r\n      const recoveredMesh = new THREE.Mesh(movedGeometry, mesh.material as THREE.Material);\r\n      recoveredMesh.name = `Rous recovered left arm from ${sourceRegion}`;\r\n      recoveredMesh.userData.region = 'arms';\r\n      recoveredMesh.userData.rousRecoveredArmSource = sourceRegion;\r\n      recoveredMesh.userData.rousRecoveredArmTriangles = chosen.size;\r\n      arms.add(recoveredMesh);\r\n    }\r\n  };\r\n  recover(body, 'body'); recover(feet, 'feet');\r\n  source.updateWorldMatrix(true, true);\r\n  const meshMaterial = (group: THREE.Object3D) => { let result: THREE.Material | undefined; group.traverse(o => { if (!result && o instanceof THREE.Mesh && o.visible && !Array.isArray(o.material)) result = o.material; }); return result; };\r\n  const targets = {body, feet};\r\n  const materials = {body: meshMaterial(body), feet: meshMaterial(feet)};\r\n  const moveArmFragments = (target: 'body' | 'feet') => {\r\n    const targetMaterial = materials[target]; if (!targetMaterial) return;\r\n    const meshes: THREE.Mesh[] = [];\r\n    arms.traverse(o => { if (o instanceof THREE.Mesh && o.visible && !Array.isArray(o.material)) meshes.push(o); });\r\n    for (const mesh of meshes) {\r\n      const position = mesh.geometry.getAttribute('position'), index = mesh.geometry.index, selected = new Set<number>();\r\n      for (const component of connectedTriangleComponents(mesh)) {\r\n        let x = 0, y = 0, count = 0; const bounds = new THREE.Box3();\r\n        for (const face of component) for (let k = 0; k < 3; k++) {\r\n          const vertex = index ? index.getX(face * 3 + k) : face * 3 + k;\r\n          const point = new THREE.Vector3().fromBufferAttribute(position, vertex).applyMatrix4(mesh.matrixWorld);\r\n          x += point.x; y += point.y; count++; bounds.expandByPoint(point);\r\n        }\r\n        x /= count; y /= count;\r\n        const footShard = target === 'feet' && component.length >= 150 && x > 1.5 && x < 1.7 && y < .22 && bounds.max.y < .4;\r\n        const pelvisShard = target === 'body' && component.length >= 20 && component.length <= 50 && x > 1.14 && x < 1.4 && y > 1.08 && y < 1.12 && bounds.max.y < 1.15;\r\n        if (footShard || pelvisShard) component.forEach(face => selected.add(face));\r\n      }\r\n      if (!selected.size) continue;\r\n      const original = mesh.geometry, movedGeometry = triangleSubset(original, face => selected.has(face)), remaining = triangleSubset(original, face => !selected.has(face));\r\n      if (!movedGeometry) continue;\r\n      const transform = targets[target].matrixWorld.clone().invert().multiply(mesh.matrixWorld);\r\n      movedGeometry.applyMatrix4(transform); extracted[target] += selected.size;\r\n      if (remaining) { mesh.geometry = remaining; original.dispose(); } else { mesh.removeFromParent(); original.dispose(); }\r\n      const fragment = new THREE.Mesh(movedGeometry, targetMaterial); fragment.name = `Rous recovered ${target} fragment from arms`;\r\n      fragment.userData.region = target; fragment.userData.rousRecoveredFromArms = target; fragment.userData.rousRecoveredArmTriangles = selected.size;\r\n      targets[target].add(fragment);\r\n    }\r\n  };\r\n  moveArmFragments('body'); moveArmFragments('feet');\r\n  source.updateWorldMatrix(true, true);\r\n  source.userData.rousRecoveredArmTriangles = recovered;\r\n  source.userData.rousExtractedFromArms = extracted;\r\n  return recovered;\r\n}\r\n","creature/source/save-look.ts":"// #1 / #102 trust boundary: the customizer's only writer of the coach recipe. Whatever reaches it\r\n// (a preview, devtools, an imported file) leaves with no locked texture, colour, palette or body,\r\n// so the account sync that reads storage never sees one either. Nothing here grants anything.\r\nimport type {Design,Region,MaterialChoice} from './creator/design';\r\nimport {isLocked as idLocked} from './creator/materials-registry';\r\nimport {bodyLockSection} from './creator/track-placements';\r\nimport {loadProgress,selectedTracks} from '../../battle-pass.mjs';\r\nimport {ownedShipIds} from '../../modules/ships/ship-access.mjs';\r\nimport {RECIPE_KEY} from './profile';\r\nimport {normalizeStyleId,normalizeTextureId} from './creator/texture-policy.mjs';\r\n\r\nconst ownedChoice=(c:MaterialChoice|undefined,coachId:string):c is MaterialChoice=>!!c&&!idLocked(c.textureId,coachId)&&!idLocked(c.colorId,coachId,'color');\r\nexport const BODY_KEYS=['body','headFrom','armsFrom','feetFrom'] as const;\r\n/** An earned body must not inherit another coach's exclusive material grants. */\r\nexport function selectOwnedBody(design:Design,id:Design['body']):Design{\r\n if(bodyLockSection(id,loadProgress()))throw Error('This coach is locked.');\r\n return keepOwned({...design,body:id,headFrom:id,armsFrom:id,feetFrom:id},design);\r\n}\r\n\r\n/** Locked material regions fall back to the last earned choice or original style. Body sources\r\n * must pass the live performance access gate, including bodies present in an older saved recipe.\r\n * The legacy grandfathered argument is accepted only to keep existing editor callers compatible. */\r\nexport function keepOwned(d:Design,lastOwned?:Design,_grandfathered:ReadonlySet<string>=new Set(),progress:Record<string,number>=loadProgress(),tracks:Iterable<string>=selectedTracks()):Design{\r\n const bodyOk=(id?:string):id is string=>!!id&&!bodyLockSection(id,progress,tracks);\r\n let next=d;\r\n const retired=Object.values(d.styles).some(style=>normalizeStyleId(style)!==style)||Object.values(d.materials??{}).some(choice=>choice&&normalizeTextureId(choice.textureId)!==choice.textureId);\r\n if(retired)next={...d,styles:Object.fromEntries(Object.entries(d.styles).map(([region,style])=>[region,normalizeStyleId(style)])) as Design['styles'],materials:d.materials&&Object.fromEntries(Object.entries(d.materials).map(([region,choice])=>[region,choice&&{...choice,textureId:normalizeTextureId(choice.textureId)}])) as Design['materials']};\r\n for(const key of BODY_KEYS)if(!bodyOk(next[key])){const fallback=lastOwned?.[key];next={...next,[key]:bodyOk(fallback)?fallback:'myr5'};}\r\n const ownedShips=new Set(ownedShipIds());\r\n const shipOk=(id:Design['shipId']):id is NonNullable<Design['shipId']>=>!!id&&(id==='supportive'||ownedShips.has(id));\r\n const chosenShip=next.shipId??next.coach;\r\n if(!shipOk(chosenShip)){\r\n  const fallback=lastOwned?.shipId??lastOwned?.coach;\r\n  next={...next,shipId:shipOk(fallback)?fallback:'supportive'};\r\n }\r\n if(next.materials&&!Object.values(next.materials).every(choice=>ownedChoice(choice,next.body))){\r\n  const materials:Design['materials']={};\r\n  for(const [region,choice] of Object.entries(next.materials) as [Region,MaterialChoice][]){const keep=ownedChoice(choice,next.body)?choice:lastOwned?.materials?.[region];if(ownedChoice(keep,next.body))materials[region]=keep;}\r\n  next={...next,materials:Object.keys(materials).length?materials:undefined};\r\n }\r\n return next;\r\n}\r\n\r\nexport function saveRecipe(storage:Storage,next:Design,lastOwned?:Design,_grandfathered?:ReadonlySet<string>){\r\n const owned=keepOwned(next,lastOwned);storage.setItem(RECIPE_KEY,JSON.stringify(owned));return owned;\r\n}\r\n","creature/source/sculpt-hands.py":"\"\"\"Rebuild the five MYR5 finger-count variants in the existing anatomy GLB.\"\"\"\r\nimport bpy, math, sys\r\nfrom pathlib import Path\r\nfrom mathutils import Vector\r\n\r\nroot=Path(__file__).parent/'coach-app'/'creature'/'models'\r\nbpy.ops.object.select_all(action='SELECT');bpy.ops.object.delete(use_global=False)\r\nbpy.ops.import_scene.gltf(filepath=str(root/'anatomy.glb'))\r\nskin=bpy.data.materials.new('MYR5 sculpted skin');skin.diffuse_color=(.20,.048,.32,1);skin.use_nodes=True\r\nbsdf=skin.node_tree.nodes.get('Principled BSDF');bsdf.inputs['Base Color'].default_value=(.20,.048,.32,1);bsdf.inputs['Roughness'].default_value=.48\r\nbsdf.inputs['Subsurface Weight'].default_value=.065\r\n\r\ndef finish(o,parts):\r\n    o.data.materials.clear();o.data.materials.append(skin);parts.append(o)\r\n    return o\r\n\r\ndef ellipsoid(name,center,scale,parts):\r\n    bpy.ops.mesh.primitive_uv_sphere_add(segments=24,ring_count=16,location=center)\r\n    o=bpy.context.object;o.name=name;o.scale=scale\r\n    bpy.ops.object.transform_apply(location=False,rotation=False,scale=True)\r\n    return finish(o,parts)\r\n\r\ndef tube(name,points,radii,parts):\r\n    # Round, smoothly tapered sections along the centerline. Dense enough to\r\n    # preserve finger gaps when the palm and knuckles are fused below.\r\n    curve=bpy.data.curves.new(name,'CURVE');curve.dimensions='3D';curve.resolution_u=10\r\n    curve.bevel_depth=1;curve.bevel_resolution=4;curve.use_fill_caps=True\r\n    spline=curve.splines.new('BEZIER');spline.bezier_points.add(len(points)-1)\r\n    for p,co,r in zip(spline.bezier_points,points,radii):\r\n        p.co=co;p.radius=r;p.handle_left_type='AUTO';p.handle_right_type='AUTO'\r\n    o=bpy.data.objects.new(name,curve);bpy.context.collection.objects.link(o)\r\n    bpy.ops.object.select_all(action='DESELECT');o.select_set(True);bpy.context.view_layer.objects.active=o\r\n    bpy.ops.object.convert(target='MESH');finish(bpy.context.object,parts)\r\n    ellipsoid(name+' pad',points[-1],(radii[-1],)*3,parts)\r\n\r\ndef build(count):\r\n    prior=bpy.data.objects.get('arms_'+str(count))\r\n    if prior:\r\n        for child in list(prior.children_recursive):bpy.data.objects.remove(child,do_unlink=True)\r\n        bpy.data.objects.remove(prior,do_unlink=True)\r\n    parts=[]\r\n    # Both shoulders still meet the original body and animation pivots.\r\n    tube('Left forearm',[(-.58,-.03,1.508),(-.78,-.02,1.28),(-.92,-.14,1.02),(-.94,-.22,.90)],[.155,.13,.108,.095],parts)\r\n    tube('Right forearm',[(.59,-.04,1.518),(.81,-.12,1.35),(.98,-.33,1.44),(1.05,-.48,1.58)],[.16,.14,.112,.10],parts)\r\n    for side in [-1,1]:\r\n        center=Vector((-.94,-.24,.79) if side<0 else (1.065,-.52,1.72))\r\n        direction=-1 if side<0 else 1\r\n        def at(x,z,depth=0):return center+Vector((x,-depth,direction*z))\r\n        # Palm has a rounded heel, dorsal arch and thumb mound, instead of a ball.\r\n        ellipsoid('Palm arch',center,(.174,.090,.160),parts)\r\n        ellipsoid('Palm heel',at(0,-.09,-.004),(.123,.081,.116),parts)\r\n        ellipsoid('Thumb mound',at(-side*.111,-.025,.035),(.093,.076,.115),parts)\r\n        fingers=count-1\r\n        spacing=.275/max(1,fingers-1)\r\n        radius=min(.053,spacing*.35)\r\n        for i in range(fingers):\r\n            fraction=i/max(1,fingers-1)\r\n            x=(fraction-.5)*.275 if fingers>1 else 0\r\n            length=.225+.065*math.sin(fraction*math.pi)\r\n            # Separate rounded knuckles, staggered lengths and a gentle resting curl.\r\n            spread=x*.20\r\n            pts=[at(x,.086),at(x+spread*.25,.17,.008),at(x+spread*.65,.17+length*.48,.035),at(x+spread,.17+length*.85,.076)]\r\n            tube('Finger '+str(i+1),pts,[radius*1.16,radius,radius*.84,radius*.67],parts)\r\n            ellipsoid('Knuckle',at(x,.149,-.018),(radius*1.13,radius*.83,radius*1.15),parts)\r\n            # Short web at the roots leaves clearly open spaces along each shaft.\r\n            if i:\r\n                ellipsoid('Finger web',at(x-spacing*.5,.082), (spacing*.64,.065,.057),parts)\r\n        thumb=[at(-side*.113,-.035,.026),at(-side*.211,.006,.055),at(-side*.253,.083,.102),at(-side*.223,.157,.141)]\r\n        tube('Opposed thumb',thumb,[.076,.065,.052,.040],parts)\r\n    bpy.ops.object.select_all(action='DESELECT')\r\n    for o in parts:o.select_set(True)\r\n    bpy.context.view_layer.objects.active=parts[0];bpy.ops.object.join();mesh=bpy.context.object\r\n    mesh.name='Sculpted palms and tapered fingers'\r\n    def apply(mod):bpy.ops.object.modifier_apply(modifier=mod.name)\r\n    rem=mesh.modifiers.new('Continuous wrist and palm','REMESH');rem.mode='VOXEL';rem.voxel_size=.007;apply(rem)\r\n    smooth=mesh.modifiers.new('Soften anatomy','SMOOTH');smooth.factor=.45;smooth.iterations=4;apply(smooth)\r\n    dec=mesh.modifiers.new('Phone mesh','DECIMATE');dec.ratio=.4;apply(dec)\r\n    for p in mesh.data.polygons:p.use_smooth=True\r\n    parent=bpy.data.objects.new('arms_'+str(count),None);bpy.context.collection.objects.link(parent);mesh.parent=parent\r\n    mesh['anatomyVersion']=2;mesh['digitsPerHand']=count\r\n    print('SCULPTED',count,len(mesh.data.polygons),flush=True)\r\n\r\nfor count in range(2,7):build(count)\r\nbpy.ops.object.select_all(action='DESELECT')\r\nfor o in bpy.context.scene.objects:\r\n    if o.name.startswith('arms_'):\r\n        o.select_set(True)\r\n        for child in o.children_recursive:child.select_set(True)\r\nbpy.ops.export_scene.gltf(filepath=str(root/'hands-v2.glb'),export_format='GLB',use_selection=True,export_yup=True)\r\nprint('HANDS_READY',flush=True)\r\n","creature/source/ship-preview.ts":"// Ship tab preview: the chosen owned ship, turning slowly in its tint. Models come only from the verified local\r\n// bridge (signed, owned, already-downloaded \"Ships and worlds\" bytes as blob: URLs); nothing here downloads.\r\n// Its own small renderer draws only while the panel is on screen.\r\nimport * as T from 'three';\r\nimport {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport {SHIP_FACING,applyShipTint} from '../../modules/ships/ship-scene-domain.mjs';\r\nimport {shipDetails} from '../../modules/ships/ship-catalog.mjs';\r\n\r\nexport type ShipBridge={ownedShipIds():string[];getShipUrl(id:string):string;dispose?():void};\r\n\r\nfunction disposeModel(root:T.Object3D){root.traverse(node=>{const mesh=node as T.Mesh;mesh.geometry?.dispose();for(const m of ([] as T.Material[]).concat(mesh.material||[])){for(const value of Object.values(m))if((value as T.Texture)?.isTexture)(value as T.Texture).dispose();m.dispose();}});}\r\n\r\nexport function mountShipPreview(host:HTMLElement,bridge:ShipBridge){\r\n const canvas=document.createElement('canvas');canvas.className='ship-preview';canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%';canvas.setAttribute('role','img');host.prepend(canvas);\r\n let renderer:T.WebGLRenderer;\r\n try{renderer=new T.WebGLRenderer({canvas,alpha:true,antialias:true});}catch(error){canvas.remove();bridge.dispose?.();throw error;}\r\n renderer.outputColorSpace=T.SRGBColorSpace;renderer.toneMapping=T.ACESFilmicToneMapping;renderer.toneMappingExposure=1.15;renderer.setPixelRatio(Math.min(devicePixelRatio,2));\r\n const scene=new T.Scene(),camera=new T.PerspectiveCamera(34,1,.1,100),turn=new T.Group();\r\n camera.position.set(0,1.4,7);camera.lookAt(0,0,0);scene.add(turn,new T.HemisphereLight(0xe9d9ff,0x23162d,2.5));\r\n const key=new T.DirectionalLight(0xffefca,4.2);key.position.set(-3,5,4);const rim=new T.DirectionalLight(0xb58cff,3.2);rim.position.set(4,2,-3);scene.add(key,rim);\r\n const loader=new GLTFLoader(),models=new Map<string,Promise<T.Object3D>>();\r\n let shown:T.Object3D|null=null,epoch=0,raf=0,disposed=false;\r\n // Same normalization as the arrival scene: centred, longest side 2.25, stern to the viewer then a quarter turn.\r\n function load(id:string){\r\n  let model=models.get(id);\r\n  if(!model){model=loader.loadAsync(bridge.getShipUrl(id)).then(gltf=>{\r\n   const box=new T.Box3().setFromObject(gltf.scene),size=box.getSize(new T.Vector3());gltf.scene.position.sub(box.getCenter(new T.Vector3()));\r\n   const root=new T.Group();root.add(gltf.scene);root.rotation.y=SHIP_FACING;root.scale.setScalar(2.25/(Math.max(size.x,size.y,size.z)||1));\r\n   if(disposed)disposeModel(root);return root;\r\n  });model.catch(()=>models.delete(id));models.set(id,model);}\r\n  return model;\r\n }\r\n function tint(color:string){if(shown)applyShipTint(shown,color==='#ffffff'?null:color);}\r\n const reduced=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n function tick(now:number){\r\n  raf=0;if(disposed||document.hidden)return;\r\n  const w=canvas.clientWidth,h=canvas.clientHeight;\r\n  if(w&&h&&shown){\r\n   if(canvas.width!==Math.round(w*renderer.getPixelRatio())||canvas.height!==Math.round(h*renderer.getPixelRatio())){renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();}\r\n   turn.rotation.y=reduced()?-.32:now/2400;renderer.render(scene,camera);\r\n  }\r\n  raf=requestAnimationFrame(tick);\r\n }\r\n const resume=()=>{if(document.hidden){cancelAnimationFrame(raf);raf=0;return;}if(!disposed&&!raf)raf=requestAnimationFrame(tick);};\r\n document.addEventListener('visibilitychange',resume);resume();\r\n return {\r\n  /** Resolves true once `id` is on screen; false if a later show() or dispose() overtook it. Throws if its bytes fail. */\r\n  async show(id:string,color='#ffffff'){\r\n   const run=++epoch,model=await load(id);if(disposed||run!==epoch)return false;\r\n   if(shown)turn.remove(shown);shown=model;turn.add(model);tint(/^#[0-9a-f]{6}$/i.test(color)?color:'#ffffff');canvas.dataset.ship=id;canvas.setAttribute('aria-label',`${shipDetails(id).name}: ${shipDetails(id).description}`);return true;\r\n  },\r\n  tint:(color:string)=>tint(/^#[0-9a-f]{6}$/i.test(color)?color:'#ffffff'),\r\n  ids:()=>bridge.ownedShipIds(),\r\n  dispose(){if(disposed)return;disposed=true;++epoch;document.removeEventListener('visibilitychange',resume);cancelAnimationFrame(raf);for(const model of models.values())model.then(disposeModel,()=>{});models.clear();renderer.dispose();renderer.forceContextLoss();canvas.remove();bridge.dispose?.();},\r\n };\r\n}\r\n","creature/source/torso-panel-ownership.ts":"import * as T from 'three';\r\nimport {connectedTriangleComponents,triangleSubset} from './limb-ownership';\r\n\r\nconst CASK='roster/11-anvil-cask--clay-style_robot_3d_model';\r\nconst ALIEN='roster/22-curve--stylized_alien_3d_model';\r\nconst HONEYCOMB='roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model';\r\nconst GHOST='roster/10-petal-wisp--ghost_character_3d_model';\r\nconst SPIRIT='roster/10-petal-wisp--fantasy_creature_3d_model4';\r\n\r\n/** Restore torso surfaces removed by the roster's vertical arm cuts and horizontal hip cut. */\r\nexport function correctTorsoPanelOwnership(source:T.Group,id:string){\r\n if(![CASK,ALIEN,HONEYCOMB,GHOST,SPIRIT].includes(id))return 0;\r\n source.updateWorldMatrix(true,true);const body=source.getObjectByName('body'),region=source.getObjectByName(id===CASK?'feet':'arms');if(!body||!region)return 0;\r\n const bodyMeshes:T.Mesh[]=[],meshes:T.Mesh[]=[];body.traverse(o=>{if(o instanceof T.Mesh&&!Array.isArray(o.material))bodyMeshes.push(o);});region.traverse(o=>{if(o instanceof T.Mesh&&!Array.isArray(o.material))meshes.push(o);});if(!bodyMeshes.length)return 0;\r\n const samples:T.Vector3[]=[];for(const m of bodyMeshes){if(m.userData.restoredTorsoPanelTriangles)continue;const p=m.geometry.getAttribute('position');for(let i=0;i<p.count;i++)samples.push(new T.Vector3().fromBufferAttribute(p,i).applyMatrix4(m.matrixWorld));}\r\n const box=new T.Box3().setFromPoints(samples);\r\n // Honeycomb's arm cut goes through the torso at x=±.325. Its actual\r\n // shoulder necks are at x=±.85; retain the complete inner torso wall.\r\n let moved=0;\r\n for(const m of meshes){const g=m.geometry,p=g.getAttribute('position'),idx=g.index,selected=new Set<number>();\r\n  if(id===CASK){for(const component of connectedTriangleComponents(m)){const b=new T.Box3();for(const f of component)for(let k=0;k<3;k++)b.expandByPoint(new T.Vector3().fromBufferAttribute(p,idx?idx.getX(f*3+k):f*3+k).applyMatrix4(m.matrixWorld));if(component.length===379&&b.min.x<-.6&&b.max.x>.6&&b.min.y>.63)for(const f of component)selected.add(f);}}\r\n  else{for(let f=0;f<(idx?.count??p.count)/3;f++){const v=new T.Vector3();for(let k=0;k<3;k++)v.add(new T.Vector3().fromBufferAttribute(p,idx?idx.getX(f*3+k):f*3+k).applyMatrix4(m.matrixWorld));v.divideScalar(3);\r\n   if(id===ALIEN){if(Math.abs(v.x)<.435&&v.y>=.929&&v.y<=1.64)selected.add(f);}\r\n   else if(id===GHOST){if(Math.abs(v.x)<.60&&v.y>=.929&&v.y<1.5)selected.add(f);}\r\n   else if(id===SPIRIT){if(Math.abs(v.x)<.36&&v.y>=.929&&v.y<1.8)selected.add(f);}\r\n   else if(v.y>=box.min.y-.012&&v.y<=box.max.y+.005&&Math.abs(v.x)<.85)selected.add(f);\r\n  }}\r\n  if(!selected.size)continue;const restored=triangleSubset(g,f=>selected.has(f)),remaining=triangleSubset(g,f=>!selected.has(f));if(!restored)continue;\r\n  restored.applyMatrix4(body.matrixWorld.clone().invert().multiply(m.matrixWorld));if(remaining)m.geometry=remaining;else m.removeFromParent();g.dispose();const part=new T.Mesh(restored,bodyMeshes[0].material);part.name='Restored torso panel';part.userData.restoredTorsoPanelTriangles=selected.size;body.add(part);moved+=selected.size;\r\n }\r\n source.updateWorldMatrix(true,true);return moved;\r\n}\r\n","creature/source/viewer.ts":"import * as T from 'three';\r\nimport {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';\r\nimport {RoomEnvironment} from 'three/examples/jsm/environments/RoomEnvironment.js';\r\nimport {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter.js';\r\nimport {clone as cloneSkinned} from 'three/examples/jsm/utils/SkeletonUtils.js';\r\nimport {assembleCreature,type InstalledSkinResolver} from './creator/assemble';\r\nimport {createRig,disposeObject,type CreatureRig} from './rig';\r\nimport {MotionController,type Gesture} from './motion';\r\nimport {importCreature,motionSettings} from './profile';\r\nimport {sampleShot,SHOTS,type Cinematic} from './cinematic-shots';\r\nimport {regionBounds,frameRegion,regionFrame} from './creator/camera-focus';\r\nimport {REGIONS,type Region,type Design} from './creator/design';\r\nimport {podCameraFrame} from './pod-camera';\r\nimport {createCoachPreviewSpace} from '../../coach-preview-space.mjs';\r\nimport {createCharacterPhysics} from '../../character-phone-physics.mjs';\r\nimport {subscribePhoneMotion} from './phone-motion';\r\nimport {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport {SHIP_FACING,applyShipTint} from '../../modules/ships/ship-scene-domain.mjs';\r\nimport {localVerifiedBridge} from '../../modules/ships/verified-ship-assets.mjs';\r\nimport {ownedShipIds,SHIP_IDS} from '../../modules/ships/ship-access.mjs';\r\nimport {projectAvatarPowerAnchors} from '../power-anchors.mjs';\r\nimport {mapDancePose,applyDancePose,measureDanceArmRest} from '../dance-pose.mjs';\r\n\r\nexport const ZOOM_MIN=0,ZOOM_MAX=1;\r\nexport class CreatureViewer {\r\n // The coordinator supplies every Dance sample; RAF only draws the last one.\r\n externalDance=false;dancePose=mapDancePose();danceBounds=new T.Box3();danceVisibleBody=1;private danceSaved:any=null;private danceRestRig:CreatureRig|null=null;private danceArmRest:Record<string,number>={};\r\n beginDance(){\r\n  if(this.disposed||!this.rig||!this.motion||this.renderer.getContext().isContextLost())return false;\r\n  if(this.externalDance)return true;\r\n  const transform=(node:T.Object3D)=>({position:node.position.clone(),quaternion:node.quaternion.clone(),scale:node.scale.clone(),visible:node.visible});\r\n  this.danceSaved={stage:this.stage,focused:this.focused,cinematicKind:this.cinematicKind,settings:{...this.settings},paused:this.paused,awake:!!this.frame,visible:this.visible,homeMoving:this.homeMoving,homeElapsed:this.homeElapsed,gesture:this.gesture,rig:this.rig,root:transform(this.rig.root),nodes:Object.fromEntries(Object.entries(this.rig.nodes).map(([name,node])=>[name,transform(node)])),camera:transform(this.camera),fov:this.camera.fov,target:this.orbit.target.clone(),enabled:this.orbit.enabled,damping:this.orbit.enableDamping,minDistance:this.orbit.minDistance,maxDistance:this.orbit.maxDistance,floor:this.floorObjects.map(o=>o.visible)};\r\n  this.externalDance=true;this.dancePose=mapDancePose();this.cinematicKind=null;this.stage='overlay';this.focused=null;this.homeMoving=false;this.visible=true;this.orbit.enabled=false;this.orbit.enableDamping=false;this.floorObjects.forEach(o=>o.visible=false);\r\n  this.cacheDanceBounds();this.applyDanceFrame();this.resize();this.setAwake(true);return true;\r\n }\r\n danceFrame(frame:{landmarks?:any[];mirrored?:boolean;active?:boolean;coachYaw?:number;visibleBody?:number}){\r\n  if(!this.externalDance||this.disposed||!this.rig||this.renderer.getContext().isContextLost())return false;\r\n  if(frame?.active!==false){this.dancePose=mapDancePose(frame);this.applyDanceFrame();if(Number.isFinite(frame.visibleBody)){const target=T.MathUtils.clamp(frame.visibleBody!,.5,1);if(Math.abs(target-this.danceVisibleBody)>.005){this.danceVisibleBody+=T.MathUtils.clamp(target-this.danceVisibleBody,-.025,.025);this.fitDanceBody();}}}return true;\r\n }\r\n private applyDanceFrame(){applyDancePose(this.rig,this.dancePose,this.bodyBounds.getSize(new T.Vector3()).y||1,this.danceArmRest);}\r\n private cacheDanceBounds(){\r\n  if(!this.rig)return;\r\n  // Bounds are derived once per recipe, covering the full arm rotation sphere.\r\n  // Resizing may change projection; sampled poses never cause a camera zoom.\r\n  applyDancePose(this.rig,mapDancePose());this.danceBounds.copy(this.bodyBounds);\r\n  if(this.danceRestRig!==this.rig){this.danceArmRest=measureDanceArmRest(this.rig);this.danceRestRig=this.rig;}\r\n  const corner=new T.Vector3();\r\n  for(const name of ['ArmLeft','ArmRight']){\r\n   const arm=this.rig.nodes[name],box=new T.Box3().setFromObject(arm),pivot=arm.getWorldPosition(new T.Vector3());if(box.isEmpty())continue;\r\n   let radius=0;for(let i=0;i<8;i++){corner.set(i&1?box.max.x:box.min.x,i&2?box.max.y:box.min.y,i&4?box.max.z:box.min.z);radius=Math.max(radius,corner.distanceTo(pivot));}\r\n   this.danceBounds.union(new T.Box3(pivot.clone().addScalar(-radius),pivot.clone().addScalar(radius)));\r\n  }\r\n  // The arm spheres already reserve the complete reach; avoid a second large\r\n  // shoulder-width gutter that made the saved coach look distant in Dance.\r\n  const height=this.bodyBounds.getSize(new T.Vector3()).y||1;this.danceBounds.expandByVector(new T.Vector3(height*.10,height*.06,height*.025));\r\n }\r\n private fitDanceBody(){\r\n  if(this.danceBounds.isEmpty())return;const box=this.danceBounds.clone(),height=this.bodyBounds.getSize(new T.Vector3()).y||1,fraction=this.danceVisibleBody;\r\n  if(fraction<.995){box.min.y=this.bodyBounds.max.y-height*fraction;box.max.y=this.bodyBounds.max.y+height*.12;}\r\n  const size=box.getSize(new T.Vector3()),center=box.getCenter(new T.Vector3()),half=T.MathUtils.degToRad(36/2),distance=Math.max(size.y/2/Math.tan(half),size.x*fraction/2/(Math.tan(half)*this.camera.aspect))*1.04+size.z/2;\r\n  this.camera.fov=36;this.camera.updateProjectionMatrix();this.orbit.minDistance=.1;this.orbit.maxDistance=distance*2;this.orbit.target.copy(center);this.camera.position.set(center.x,center.y,center.z+distance);this.orbit.update();\r\n }\r\n endDance(){\r\n  const saved=this.danceSaved;if(!saved)return;this.externalDance=false;this.danceSaved=null;\r\n  const restore=(node:T.Object3D,pose:any)=>{node.position.copy(pose.position);node.quaternion.copy(pose.quaternion);node.scale.copy(pose.scale);node.visible=pose.visible;};\r\n  this.stage=saved.stage;this.focused=saved.focused;this.cinematicKind=saved.cinematicKind;this.homeMoving=saved.homeMoving;this.homeElapsed=saved.homeElapsed;this.gesture=saved.gesture;this.visible=saved.visible;this.setSettings(saved.settings);this.setPaused(saved.paused);\r\n  if(this.rig===saved.rig){restore(this.rig!.root,saved.root);for(const [name,pose] of Object.entries(saved.nodes))if(this.rig!.nodes[name])restore(this.rig!.nodes[name],pose);}\r\n  else if(this.rig){applyDancePose(this.rig,mapDancePose());this.motion?.play(saved.gesture);}\r\n  this.floorObjects.forEach((o,i)=>o.visible=saved.floor[i]);this.orbit.enabled=saved.enabled;this.orbit.enableDamping=saved.damping;this.orbit.minDistance=saved.minDistance;this.orbit.maxDistance=saved.maxDistance;restore(this.camera,saved.camera);this.camera.fov=saved.fov;this.camera.updateProjectionMatrix();this.orbit.target.copy(saved.target);this.orbit.update();this.setAwake(saved.awake);\r\n }\r\n regionBoxes=new Map<Region,T.Box3>();focused:Region|null=null;\r\n // #9: 'body' frames the whole creature (every region) plus a little headroom for raised arms and hops;\r\n // side 1 is the front view, -1 the back (kept for later part focus so Back stays Back).\r\n  side=1;zoom=0;\r\n  applyZoom(){if(!this.bodyBounds||this.bodyBounds.isEmpty())return;const bb=this.bodyBounds.clone();bb.max.y+=(bb.max.y-bb.min.y)*.08;const full=regionFrame(this.camera,bb,1.15,this.side);if(!full)return;let box:T.Box3|undefined;for(const b of[this.regionBoxes.get('head'),this.regionBoxes.get('eye')])if(b&&!b.isEmpty())box=box?box.union(b):b.clone();const h=box?regionFrame(this.camera,box,1.45,this.side):null,head=h??full,t=this.zoom,target=full.target.clone().lerp(head.target,t),distance=full.distance+(head.distance*.9-full.distance)*t,dir=this.camera.position.clone().sub(this.orbit.target);dir.length()<1e-6?dir.set(0,0,this.side):dir.normalize();this.orbit.minDistance=Math.min(.7,head.distance*.5);this.orbit.maxDistance=Math.max(14,full.distance*2);this.orbit.target.copy(target);this.camera.position.copy(target).addScaledVector(dir,distance);this.orbit.update();}\r\n  setZoom(z:number){this.zoom=Math.min(ZOOM_MAX,Math.max(ZOOM_MIN,Number.isFinite(z)?z:1));this.applyZoom();return this.zoom;}\r\n  focusRegion(region:Region){this.focused=region;if(region==='body'){const ok=!!this.bodyBounds&&!this.bodyBounds.isEmpty();if(ok){this.camera.position.copy(this.orbit.target).add(new T.Vector3(0,0,this.side));this.applyZoom();}return ok;}const box=this.regionBoxes.get(region);if(!box)return false;return frameRegion(this.camera,this.orbit,box,1.2,this.side);}\r\n homeElapsed=0;homeMoving=false;bodyBounds=new T.Box3();\r\n setHomeMotion(moving:boolean){this.homeMoving=moving;}\r\n homeView(){\r\n  if(this.externalDance||this.interactive||this.stage!=='pod')return;\r\n  const head=this.regionBoxes.get('head');if(!head)return;\r\n  const frame=podCameraFrame(this.camera,head,this.bodyBounds,this.homeElapsed);if(!frame)return;\r\n  this.orbit.minDistance=.3;this.orbit.maxDistance=frame.maxDistance;this.orbit.target.copy(frame.target);this.camera.position.copy(frame.position);this.orbit.update();\r\n }\r\n cinematicKind:Cinematic|null=null;\r\n cinematic(kind:Cinematic|null,elapsed=0){\r\n  if(this.externalDance)return;\r\n  if(!kind){if(!this.cinematicKind)return;this.cinematicKind=null;this.resetStageView();this.play('idle');return;}\r\n  if(!Object.hasOwn(SHOTS,kind)||this.settings.reduced)return;\r\n  if(this.cinematicKind!==kind){this.cinematicKind=kind;this.play(SHOTS[kind].gesture);}\r\n  const [x,y,z,target]=sampleShot(kind,elapsed);this.camera.position.set(x,y,z);this.orbit.target.set(0,target,0);this.camera.fov=36;this.camera.updateProjectionMatrix();this.orbit.update();\r\n }\r\n scene=new T.Scene();camera=new T.PerspectiveCamera(36,1,.1,50);renderer:T.WebGLRenderer;orbit:OrbitControls;rig:CreatureRig|null=null;motion:MotionController|null=null;generation=0;disposed=false;frame=0;last=0;visible=true;settings=motionSettings(null);resizeObserver:ResizeObserver;visibilityObserver:IntersectionObserver;gesture:Gesture='idle';paused=false;tick:FrameRequestCallback=()=>{};stage:'pod'|'encounter'|'overlay'='pod';floorObjects:T.Object3D[]=[];previewSpaceDispose:(()=>void)|null=null;skinResolver?:InstalledSkinResolver;skinTextures=new Set<T.Texture>();maxFps:number|null=null;renders=0;\r\n powerSoles:{index:number;node:T.Object3D;local:T.Vector3}[]=[];powerAnchors:{index:number;x:number;y:number;visible:boolean}[]=[];powerProjectionWorld=new T.Vector3();powerProjectionNdc=new T.Vector3();\r\n phonePhysics=createCharacterPhysics({width:1,height:1,bodyWidth:1,bodyHeight:1,buffer:0,contain:true,tiltExit:true,ship:false});phoneMotionDispose:(()=>void)|null=null;phoneElapsed=0;phonePose='idle';phoneTransientHoldFrames=0;\r\n phoneFloorContact=0;phoneWallBlend=0;phoneWaveLookBlend=0;phoneWalkBlend=0;\r\n phoneShipEpoch=0;phoneShipLoadId:string|null=null;phoneShipUnavailableId:string|null=null;phoneShipBaseScale=1;phoneShipSelectedId:string|null=null;phoneShipTint='#ffffff';phoneShipBridge:{ownedShipIds():string[];getShipUrl(id:string):string;dispose?():void}|null=null;phoneShipRoot:T.Group|null=null;phoneShipModel:T.Object3D|null=null;phoneShipLoader=new GLTFLoader();phoneProjectionCorners=Array.from({length:8},()=>new T.Vector3());\r\n phoneCameraChange=()=>this.phoneScreenSize();\r\n phoneAccountChange=()=>{if(!this.interactive||!this.rig)return;this.resetPhonePhysics();this.disposePhoneShip();this.selectPhoneShip(this.rig.recipe);this.phoneScreenSize();};\r\n constructor(public mount:HTMLElement,public assetBase:string,public interactive=true,skinResolver?:InstalledSkinResolver){this.skinResolver=skinResolver;\r\n  this.renderer=new T.WebGLRenderer({antialias:true,alpha:true,preserveDrawingBuffer:true,powerPreference:'low-power'});\r\n  this.renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));this.renderer.outputColorSpace=T.SRGBColorSpace;this.renderer.toneMapping=T.ACESFilmicToneMapping;\r\n  const environment=new RoomEnvironment(),pmrem=new T.PMREMGenerator(this.renderer);this.scene.environment=pmrem.fromScene(environment,.04).texture;environment.dispose();pmrem.dispose();\r\n  this.renderer.domElement.setAttribute('aria-label','Your animated MYR5 creature');this.renderer.domElement.setAttribute('role','img');mount.append(this.renderer.domElement);\r\n  this.camera.position.set(0,2.65,8.9);this.orbit=new OrbitControls(this.camera,this.renderer.domElement);this.orbit.target.set(0,1.95,0);this.orbit.enableDamping=true;this.orbit.enablePan=false;this.orbit.enableZoom=false;this.orbit.minDistance=6;this.orbit.maxDistance=13;this.orbit.enabled=interactive;this.orbit.maxPolarAngle=Math.PI*.85;this.orbit.addEventListener('change',this.phoneCameraChange);\r\n  this.scene.add(new T.HemisphereLight(0xe5d5ff,0x23152e,interactive?1.65:2));\r\n  const key=new T.DirectionalLight(0xffeedc,interactive?2.8:3);key.position.set(-3,5,5);this.scene.add(key);\r\n  const rim=new T.DirectionalLight(0xb997ff,interactive?1.5:2);rim.position.set(3,4,-3);this.scene.add(rim);\r\n  if(interactive){\r\n   const space=createCoachPreviewSpace(T,{pixelRatio:this.renderer.getPixelRatio()});\r\n   this.scene.add(space.group);this.scene.background=space.background;this.scene.fog=space.fog;\r\n   this.scene.add(this.camera);\r\n   this.previewSpaceDispose=()=>{if(this.scene.background===space.background)this.scene.background=null;if(this.scene.fog===space.fog)this.scene.fog=null;space.dispose();};\r\n   this.camera.far=400;this.camera.updateProjectionMatrix();\r\n   // Fill the side hidden from the key and add a restrained cool edge. Neither casts shadows.\r\n   const fill=new T.DirectionalLight(0xffc78f,1.05);fill.position.set(4,2,4);this.scene.add(fill);\r\n   const edge=new T.DirectionalLight(0x9ac9ec,.75);edge.position.set(-4,3,-4);this.scene.add(edge);\r\n  }\r\n  const floor=new T.Mesh(new T.CylinderGeometry(1.45,1.55,.08,64),new T.MeshStandardMaterial({color:0x241e31,roughness:.5,metalness:.4}));floor.position.y=-.05;this.scene.add(floor);\r\n  const ring=new T.Mesh(new T.TorusGeometry(1.4,.014,6,64),new T.MeshBasicMaterial({color:0xd3b86d}));ring.rotation.x=Math.PI/2;ring.position.y=.005;this.scene.add(ring);\r\n  this.floorObjects=[floor,ring];\r\n  this.resizeObserver=new ResizeObserver(()=>this.resize());this.resizeObserver.observe(mount);this.resize();\r\n  this.visibilityObserver=new IntersectionObserver(entries=>{this.visible=entries[0].isIntersecting;});this.visibilityObserver.observe(mount);\r\n  if(interactive){this.startPhoneMotion();window.addEventListener('myr5:account-ready',this.phoneAccountChange);window.addEventListener('myr5:account-cleared',this.phoneAccountChange);}\r\n  // D43.5: coach-overlay can cap this below the native ~31fps (setMaxFps) while a counted set is\r\n  // tracking slowly, so this WebGL render competes less with MediaPipe on the main thread. Ongoing\r\n  // falls/climbs use the real frame gap; the brief flip and look-down beats advance per rendered frame\r\n  // so a busy renderer still shows both poses before continuing the fall.\r\n  const tick=(now:number)=>{if(this.disposed)return;this.frame=requestAnimationFrame(tick);const gate=this.maxFps?1000/this.maxFps:32,frameGap=Math.max(0,(now-this.last)/1000);if(now-this.last<gate)return;this.last=now;if(!this.visible||document.hidden)return;const motionDt=Math.min(this.maxFps?gate/1000:.05,frameGap);if(this.externalDance)this.applyDanceFrame();else{this.motion?.update(motionDt);this.updatePhonePhysics(Math.min(.5,frameGap));}if(!this.externalDance&&!this.interactive&&this.stage==='pod'&&!this.cinematicKind&&!this.paused&&!this.settings.reduced&&this.settings.amount>0&&this.homeMoving){this.homeElapsed+=motionDt;this.homeView();}this.orbit.update();this.renderer.render(this.scene,this.camera);this.refreshPowerAnchors();this.renders++;};this.tick=tick;this.frame=requestAnimationFrame(this.tick);\r\n }\r\n // Layout size, not getBoundingClientRect(): a portal destination arrives scaled from the wormhole's core (2N), and a\r\n // resize read mid-arrival would keep a thumbnail-sized canvas once it lands (the meditation room's borrowed coach).\r\n resize(){const width=this.mount.clientWidth,height=this.mount.clientHeight;if(width&&height){this.renderer.setSize(width,height,false);this.camera.aspect=width/height;this.camera.updateProjectionMatrix();if(this.externalDance)this.fitDanceBody();else if(!this.cinematicKind){if(this.stage==='overlay')this.fitBody();else if(this.focused&&this.interactive)this.focusRegion(this.focused);else this.homeView();}this.phoneScreenSize();}}\r\n private resetPhonePhysics(){if(this.externalDance||!this.interactive)return;this.phonePhysics.reset();this.phoneElapsed=0;this.phonePose='idle';this.phoneTransientHoldFrames=0;this.phoneFloorContact=0;this.phoneWallBlend=0;this.phoneWaveLookBlend=0;this.phoneWalkBlend=0;this.renderer.domElement.dataset.phonePhase='idle';this.mount.dataset.phoneMotionPhase='idle';this.motion?.play(this.gesture);if(this.rig){this.rig.root.position.set(0,0,0);this.rig.root.quaternion.identity();this.rig.root.visible=true;}if(this.phoneShipRoot)this.phoneShipRoot.visible=false;this.renderer.domElement.dataset.phoneShipReady='false';}\r\n private cachePowerSoles(rig:CreatureRig){return(['FootLeft','FootRight'] as const).flatMap((name,side)=>{const node=rig.nodes[name],bounds=new T.Box3();node.updateMatrix();for(const child of node.children){if(!(child instanceof T.Mesh))continue;const geometry=child.geometry;if(!geometry.boundingBox)geometry.computeBoundingBox();if(geometry.boundingBox){child.updateMatrix();bounds.union(geometry.boundingBox.clone().applyMatrix4(child.matrix));}}if(bounds.isEmpty())return[];return[{index:side===0?27:28,node,local:new T.Vector3((bounds.min.x+bounds.max.x)/2,bounds.min.y,(bounds.min.z+bounds.max.z)/2)}];});}\r\n private refreshPowerAnchors(){this.powerAnchors=projectAvatarPowerAnchors(this.powerSoles,this.camera,!!this.rig?.root.visible,this.powerProjectionWorld,this.powerProjectionNdc);}\r\n private selectPhoneShip(recipe:Design){\r\n  const account=(globalThis as any).myr5AuthenticatedAccount,owner=typeof account==='string'?account:account?.user?.id;\r\n  let saved:{ship?:string;tint?:string}|null=null;\r\n  if(owner)try{saved=JSON.parse(localStorage.getItem(`myr5-ship-customization-v1/${owner}`)||'null');}catch{}\r\n  const available=ownedShipIds(),valid=(id:unknown):id is string=>typeof id==='string'&&SHIP_IDS.includes(id)&&(id==='supportive'||available.includes(id));\r\n  const id=valid(saved?.ship)?saved!.ship:valid(recipe.shipId)?recipe.shipId:valid(recipe.coach)?recipe.coach:null;\r\n  const tint=id&&saved?.ship===id&&/^#[0-9a-f]{6}$/i.test(saved.tint||'')?saved.tint!:id&&recipe.shipId===id&&/^#[0-9a-f]{6}$/i.test(recipe.shipColor||'')?recipe.shipColor!:'#ffffff';\r\n  this.phoneShipSelectedId=id;this.phoneShipTint=tint;\r\n  const width=this.mount.clientWidth||1,height=this.mount.clientHeight||1;\r\n  this.phonePhysics=createCharacterPhysics({width,height,bodyWidth:1,bodyHeight:1,buffer:0,contain:true,tiltExit:true,ship:false});\r\n  this.renderer.domElement.dataset.phoneShip=id||'';this.renderer.domElement.dataset.phoneShipReady='false';\r\n  return id;\r\n }\r\n private phonePhysicsAllowed(){return !this.externalDance&&this.interactive&&this.stage==='pod'&&this.visible&&!document.hidden&&!this.paused&&!this.settings.reduced&&this.settings.amount>0;}\r\n private phoneScreenSize(){\r\n  if(!this.rig)return;\r\n  const width=this.mount.clientWidth,height=this.mount.clientHeight;if(!width||!height)return;\r\n  this.camera.updateMatrixWorld(true);\r\n  const min=this.bodyBounds.min,max=this.bodyBounds.max;let left=Infinity,right=-Infinity,top=Infinity,bottom=-Infinity;\r\n  for(let i=0;i<8;i++){const point=this.phoneProjectionCorners[i]!.set(i&1?max.x:min.x,i&2?max.y:min.y,i&4?max.z:min.z).project(this.camera);const x=(point.x*.5+.5)*width,y=(-point.y*.5+.5)*height;left=Math.min(left,x);right=Math.max(right,x);top=Math.min(top,y);bottom=Math.max(bottom,y);}\r\n  this.phonePhysics.resize(width,height,Math.max(1,right-left),Math.max(1,bottom-top));(this.phonePhysics as any).setRestCenter?.((left+right)/2,(top+bottom)/2);\r\n }\r\n private updatePhonePhysics(dt:number){\r\n  if(!this.interactive||!this.rig||!this.motion)return;\r\n  const active=this.phonePhysicsAllowed();\r\n  this.renderer.domElement.dataset.phonePhase=active?(this.phonePhysics.state.phase||'idle'):'idle';this.mount.dataset.phoneMotionPhase=this.renderer.domElement.dataset.phonePhase;\r\n  if(!active){\r\n   if(this.phonePose!=='idle'||this.phonePhysics.state.phase!=='idle'){this.resetPhonePhysics();}\r\n   this.rig.root.position.set(0,0,0);this.rig.root.quaternion.identity();return;\r\n  }\r\n  const transient=['air-run','look-down'].includes(this.phonePhysics.state.phase);\r\n  const phaseDt=transient?Math.min(.05,dt):dt;\r\n  const holdTransientFrame=transient&&this.phoneTransientHoldFrames>0;\r\n  if(holdTransientFrame)this.phoneTransientHoldFrames--;else this.phonePhysics.step(phaseDt);\r\n  this.phoneElapsed+=holdTransientFrame?0:phaseDt;\r\n  const state=this.phonePhysics.state;\r\n  this.renderer.domElement.dataset.phonePhase=state.phase;this.mount.dataset.phoneMotionPhase=state.phase;\r\n  const contactTarget=['slide','climb','wave','climb-out','recover'].includes(state.phase)?1:0;if(state.phase==='recover')this.phoneFloorContact=1;else this.phoneFloorContact+=(contactTarget-this.phoneFloorContact)*Math.min(1,Math.min(.05,dt)*5);\r\n  if(['climb','wave','climb-out','ship','gone'].includes(state.phase))void this.loadPhoneShip();\r\n  const pose=String(state.pose);\r\n  if(pose!==this.phonePose){\r\n   this.phonePose=pose;const gesture:Gesture=['wiggle','air-run','bounce'].includes(pose)?'wiggle':['walk','slide','recover','climb','ship'].includes(pose)?'walk':['greet','wave'].includes(pose)?'greet':'idle';this.motion.play(gesture);this.motion.update(.05);if(state.phase==='slide')this.motion.action.setEffectiveTimeScale(.55);\r\n  }\r\n  const viewDirection=this.camera.getWorldDirection(new T.Vector3()),right=new T.Vector3(1,0,0).applyQuaternion(this.camera.quaternion),up=new T.Vector3(0,1,0).applyQuaternion(this.camera.quaternion);\r\n  const pivot=this.bodyBounds.getCenter(new T.Vector3()),cameraPosition=this.camera.getWorldPosition(new T.Vector3()),depth=pivot.clone().sub(cameraPosition).dot(viewDirection),unitsPerPixel=2*depth*Math.tan(T.MathUtils.degToRad(this.camera.fov/2))/Math.max(1,this.mount.clientHeight);\r\n  let actorX=state.x,actorY=state.y;\r\n  if(state.phase==='ship'&&state.shipProgress<.36){const t=state.shipProgress/.36;actorX*=1-t;actorY*=1-t;}\r\n  const offset=right.clone().multiplyScalar(actorX*unitsPerPixel).addScaledVector(up,-actorY*unitsPerPixel),rotation=new T.Quaternion().setFromAxisAngle(viewDirection.negate(),state.angle);\r\n  const wallTarget=['climb','wave','climb-out'].includes(state.phase)?1:0,wallEase=1-Math.exp(-Math.min(.05,dt)*5);if(state.phase==='recover')this.phoneWallBlend=0;else this.phoneWallBlend+=(wallTarget-this.phoneWallBlend)*wallEase;\r\n  const waveTarget=state.phase==='wave'?1:0;this.phoneWaveLookBlend+=(waveTarget-this.phoneWaveLookBlend)*wallEase;\r\n  if(this.phoneWallBlend>.001){const platform=this.floorObjects[0]?.getWorldPosition(new T.Vector3())??new T.Vector3(0,-.05,0),towardWall=platform.sub(pivot).normalize(),wallFacing=new T.Quaternion().setFromUnitVectors(new T.Vector3(0,0,1),towardWall),wallRoll=wallFacing.clone().multiply(new T.Quaternion().setFromAxisAngle(new T.Vector3(0,0,1),state.angle));rotation.slerp(wallRoll,this.phoneWallBlend);}\r\n  const walkDistance=Math.abs(state.x),walkTarget=state.phase==='recover'?T.MathUtils.smoothstep(walkDistance,0,Math.max(32,this.bodyBounds.getSize(new T.Vector3()).x*.45)):0;this.phoneWalkBlend+=(walkTarget-this.phoneWalkBlend)*wallEase;\r\n  if(this.phoneWalkBlend>.001){const walkDirection=right.clone().multiplyScalar(state.x>0?-1:1);walkDirection.y=0;if(walkDirection.lengthSq()>.001){walkDirection.normalize();const walkFacing=new T.Quaternion().setFromAxisAngle(new T.Vector3(0,1,0),Math.atan2(walkDirection.x,walkDirection.z));rotation.slerp(walkFacing,this.phoneWalkBlend);}}\r\n  const pivotShift=pivot.clone().sub(pivot.clone().applyQuaternion(rotation));this.rig.root.position.copy(offset).add(pivotShift);this.rig.root.quaternion.copy(rotation);this.rig.root.updateMatrixWorld(true);\r\n  if(this.phoneFloorContact>.001){let lowest=Infinity;for(let i=0;i<8;i++){const point=this.phoneProjectionCorners[i]!.set(i&1?this.bodyBounds.max.x:this.bodyBounds.min.x,i&2?this.bodyBounds.max.y:this.bodyBounds.min.y,i&4?this.bodyBounds.max.z:this.bodyBounds.min.z).applyMatrix4(this.rig.root.matrixWorld);lowest=Math.min(lowest,point.y);}const floor=this.floorObjects[0] as T.Mesh|undefined,floorY=floor?floor.position.y+.04:-.01;this.rig.root.position.y+=(floorY-lowest)*this.phoneFloorContact;this.rig.root.updateMatrixWorld(true);}\r\n  this.rig.root.visible=state.phase!=='gone'&&!(state.phase==='ship'&&state.shipProgress>=.36);\r\n  this.updatePhoneShip(state.phase,state.shipProgress,state,pivot,right,up,unitsPerPixel);\r\n  if(pose==='climb'){\r\n   // Turn toward the floor-wall. Braced forelimbs and alternating pulls keep the quadruped climbing.\r\n   const phase=this.phoneElapsed*8,armL=this.rig.nodes.ArmLeft,armR=this.rig.nodes.ArmRight,footL=this.rig.nodes.FootLeft,footR=this.rig.nodes.FootRight;\r\n   this.rig.nodes.BodyMotion?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.2,0,.035*Math.sin(phase))));\r\n   if(this.rig.nodes.BodyMotion)this.rig.nodes.BodyMotion.position.y-=.055;\r\n   this.rig.nodes.HeadMotion?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.09,0,0)));\r\n   armL?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.52-.12*Math.sin(phase),0,-.18)));\r\n   armR?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.52+.12*Math.sin(phase),0,.18)));\r\n   footL?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.22,0,-.1)));\r\n   footR?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.22,0,.1)));\r\n  }else if(pose==='look-down'){\r\n   this.rig.nodes.HeadMotion?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.38,0,0)));\r\n   for(const node of Object.keys(this.rig.nodes).filter(name=>name.startsWith('EyeBlink')))this.rig.nodes[node]!.scale.y=.06;\r\n  }else if(pose==='fall'){\r\n   const roll=.14*Math.sin(this.phoneElapsed*5);this.rig.nodes.BodyMotion?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.12,0,roll)));\r\n   this.rig.nodes.ArmLeft?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.22,0,-.62)));\r\n   this.rig.nodes.ArmRight?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.22,0,.62)));\r\n   this.rig.nodes.FootLeft?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(.4,0,-.18)));\r\n   this.rig.nodes.FootRight?.quaternion.multiply(new T.Quaternion().setFromEuler(new T.Euler(-.28,0,.18)));\r\n  }\r\n  if(this.phoneWaveLookBlend>.001){const head=this.rig.nodes.HeadMotion;if(head?.parent){this.rig.root.updateMatrixWorld(true);const parentWorld=head.parent.getWorldQuaternion(new T.Quaternion()).invert(),towardCamera=this.camera.getWorldPosition(new T.Vector3()).sub(head.getWorldPosition(new T.Vector3())).normalize().applyQuaternion(parentWorld),lookAtCamera=new T.Quaternion().setFromUnitVectors(new T.Vector3(0,0,1),towardCamera);head.quaternion.slerp(lookAtCamera,this.phoneWaveLookBlend);}}\r\n }\r\n private startPhoneMotion(){if(!this.interactive||this.phoneMotionDispose)return;this.phoneMotionDispose=subscribePhoneMotion(sample=>{if(!this.phonePhysicsAllowed())return;const before=this.phonePhysics.state.phase;this.phonePhysics.sample(sample,sample.timeSeconds);const after=this.phonePhysics.state.phase;if(['air-run','look-down'].includes(after)&&!['air-run','look-down'].includes(before))this.phoneTransientHoldFrames=1;});}\r\n private disposePhoneShip(){++this.phoneShipEpoch;this.phoneShipLoadId=null;this.phoneShipUnavailableId=null;this.phonePhysics.setShipAvailable(false);if(this.phoneShipRoot){this.scene.remove(this.phoneShipRoot);this.disposeShipTree(this.phoneShipRoot);}this.phoneShipRoot=null;this.phoneShipModel=null;this.phoneShipBridge?.dispose?.();this.phoneShipBridge=null;this.renderer.domElement.dataset.phoneShipReady='false';}\r\n private disposeShipTree(root:T.Object3D){const geometries=new Set<T.BufferGeometry>(),materials=new Set<T.Material>(),textures=new Set<T.Texture>();root.traverse(node=>{const mesh=node as T.Mesh;if(mesh.geometry)geometries.add(mesh.geometry);for(const material of(Array.isArray(mesh.material)?mesh.material:[mesh.material]))if(material){materials.add(material);for(const value of Object.values(material))if((value as T.Texture)?.isTexture)textures.add(value as T.Texture);}});textures.forEach(texture=>texture.dispose());materials.forEach(material=>material.dispose());geometries.forEach(geometry=>geometry.dispose());}\r\n private async loadPhoneShip(){\r\n  const ship=this.phoneShipSelectedId;if(!ship||this.phoneShipLoadId===ship||this.phoneShipUnavailableId===ship||this.phoneShipModel)return;\r\n  if(ship!=='supportive'&&!ownedShipIds().includes(ship))return;\r\n  const epoch=++this.phoneShipEpoch;this.phoneShipLoadId=ship;\r\n  let bridge:{ownedShipIds():string[];getShipUrl(id:string):string;dispose?():void}|null=null;\r\n  try{\r\n   bridge=ship==='supportive'?null:await localVerifiedBridge();\r\n   if(this.disposed||epoch!==this.phoneShipEpoch){bridge?.dispose?.();return;}\r\n   if(ship!=='supportive'&&(!bridge||!bridge.ownedShipIds().includes(ship))){bridge?.dispose?.();this.phoneShipLoadId=null;this.phoneShipUnavailableId=ship;return;}\r\n   const url=bridge?bridge.getShipUrl(ship):new URL('/pod/worlds/starter/supportive.glb',location.href).href;\r\n   const gltf=await this.phoneShipLoader.loadAsync(url);\r\n   if(this.disposed||epoch!==this.phoneShipEpoch){this.disposeShipTree(gltf.scene);bridge?.dispose?.();return;}\r\n   if(ship!=='supportive'&&!ownedShipIds().includes(ship)){this.disposeShipTree(gltf.scene);bridge?.dispose?.();this.phoneShipLoadId=null;this.phoneShipUnavailableId=ship;this.phonePhysics.setShipAvailable(false);return;}\r\n   const box=new T.Box3().setFromObject(gltf.scene),size=box.getSize(new T.Vector3()),max=Math.max(size.x,size.y,size.z)||1;\r\n   gltf.scene.position.sub(box.getCenter(new T.Vector3()));\r\n   const root=new T.Group();root.name='verified-phone-coach-ship';root.add(gltf.scene);root.rotation.y=SHIP_FACING;\r\n   const body=this.bodyBounds.getSize(new T.Vector3());this.phoneShipBaseScale=Math.max(.7,body.y*1.5)/max;root.scale.setScalar(this.phoneShipBaseScale);\r\n   if(this.phoneShipTint!=='#ffffff')applyShipTint(root,this.phoneShipTint);\r\n   this.phoneShipBridge=bridge;this.phoneShipModel=gltf.scene;this.phoneShipRoot=root;this.scene.add(root);this.phonePhysics.setShipAvailable(true);\r\n  }catch{if(epoch!==this.phoneShipEpoch){bridge?.dispose?.();return;}this.phoneShipLoadId=null;this.phoneShipUnavailableId=ship;bridge?.dispose?.();this.phoneShipBridge?.dispose?.();this.phoneShipBridge=null;}\r\n }\r\n private updatePhoneShip(phase:string,progress:number,state:{x:number;y:number;angle:number},pivot:T.Vector3,right:T.Vector3,up:T.Vector3,unitsPerPixel:number){\r\n  const selected=this.phoneShipSelectedId;if(selected&&selected!=='supportive'&&!ownedShipIds().includes(selected)){if(this.phoneShipRoot)this.disposePhoneShip();return;}\r\n  const root=this.phoneShipRoot,id=selected;this.renderer.domElement.dataset.phoneShip=id||'';const visible=phase==='ship';this.renderer.domElement.dataset.phoneShipReady=String(!!root&&visible);if(!root)return;root.visible=visible;if(!visible)return;\r\n  const start=this.bodyBounds.getCenter(new T.Vector3()).applyMatrix4(this.rig!.root.matrixWorld),finish=pivot.clone();\r\n  const p=T.MathUtils.clamp(Number.isFinite(progress)?progress:0,0,1),approach=T.MathUtils.clamp(p/.36,0,1),outbound=T.MathUtils.clamp((p-.36)/.64,0,1);\r\n  const gravityX=Math.sin(state.angle),gravityY=Math.cos(state.angle),width=this.mount.clientWidth,height=this.mount.clientHeight;\r\n  const departure=finish.clone().addScaledVector(right,-gravityX*(width*.65)*unitsPerPixel).addScaledVector(up,gravityY*(height*.65)*unitsPerPixel);\r\n  if(p<.36)root.position.copy(start).lerp(finish,approach);else root.position.copy(finish).lerp(departure,outbound);\r\n  const scale=Math.max(.12,1-outbound*.78);root.scale.setScalar(this.phoneShipBaseScale*scale);\r\n  root.rotation.y=SHIP_FACING+Math.sin(outbound*Math.PI)*.28;\r\n }\r\n async setRecipe(raw:unknown,preview=false){\r\n  this.powerSoles=[];this.powerAnchors=[];\r\n  this.resetPhonePhysics();\r\n  this.disposePhoneShip();\r\n  const recipe=importCreature(JSON.stringify(raw)),generation=++this.generation;\r\n  const assembly=await assembleCreature(recipe,this.assetBase,this.skinResolver,preview);let rig:CreatureRig|undefined;\r\n  try{if(this.disposed||generation!==this.generation){assembly.skinTextures.forEach(texture=>texture.dispose());return false;}this.regionBoxes=new Map(REGIONS.map(region=>[region,regionBounds(assembly.root,region)]));if(this.regionBoxes.get('eye')!.isEmpty())this.regionBoxes.set('eye',this.regionBoxes.get('head')!.clone());this.bodyBounds.makeEmpty();for(const box of this.regionBoxes.values())this.bodyBounds.union(box);rig=createRig(assembly.root,recipe);}catch(error){assembly.skinTextures.forEach(texture=>texture.dispose());throw error;}finally{assembly.dispose();}\r\n  if(this.disposed||generation!==this.generation){assembly.skinTextures.forEach(texture=>texture.dispose());return false;}\r\n  if(this.rig){this.motion?.dispose();this.scene.remove(this.rig.root);disposeObject(this.rig.root);}\r\n  this.skinTextures.forEach(texture=>texture.dispose());this.skinTextures=assembly.skinTextures;\r\n  this.rig=rig!;this.powerSoles=this.cachePowerSoles(rig!);this.powerAnchors=[];this.motion=new MotionController(rig!);Object.assign(this.motion,this.settings,{paused:this.paused});this.scene.add(rig!.root);this.motion.play(this.gesture);this.selectPhoneShip(recipe);this.phoneScreenSize();if(this.externalDance){this.cacheDanceBounds();this.applyDanceFrame();this.fitDanceBody();}else if(!this.cinematicKind){if(this.stage==='overlay')this.fitBody();else if(this.interactive&&this.focused)this.focusRegion(this.focused);else this.homeView();}return true;\r\n }\r\n setSkinResolver(resolver?:InstalledSkinResolver){this.skinResolver=resolver;}\r\n clearSkinState(){this.generation++;this.powerSoles=[];this.powerAnchors=[];this.resetPhonePhysics();if(this.rig){this.motion?.dispose();this.scene.remove(this.rig.root);disposeObject(this.rig.root);this.rig=null;this.motion=null;}this.skinTextures.forEach(texture=>texture.dispose());this.skinTextures.clear();this.regionBoxes.clear();this.bodyBounds.makeEmpty();}\r\n play(id:Gesture){if(this.externalDance)return;this.gesture=id;this.motion?.play(id);}\r\n // Workout overlay: turn to face where the coach walks (0 = the camera, π/2 = screen right).\r\n face(yaw:number){if(this.externalDance)return;if(this.rig)this.rig.root.rotation.y=yaw;}\r\n setSettings(settings:ReturnType<typeof motionSettings>){if(this.externalDance)return;this.settings=settings;if(this.motion)Object.assign(this.motion,settings);if(settings.reduced||settings.amount===0){this.resetPhonePhysics();this.homeElapsed=0;if(!this.cinematicKind)this.homeView();}}\r\n setPaused(paused:boolean){if(this.externalDance)return;this.paused=paused;if(this.motion)this.motion.paused=paused;if(paused)this.resetPhonePhysics();}\r\n // D43.5: null/0 restores the native ~31fps gate (see tick()).\r\n setMaxFps(fps:number|null){this.maxFps=fps&&fps>0?fps:null;}\r\n // W2-2Q: asleep, no frame is scheduled at all (the ship view puts the parked app capsule to sleep once it closes).\r\n setAwake(awake:boolean){if(this.disposed||awake===!!this.frame)return;if(awake)this.frame=requestAnimationFrame(this.tick);else{cancelAnimationFrame(this.frame);this.frame=0;}}\r\n resetStageView(){if(this.stage==='overlay'){this.fitBody();return;}const giant=this.stage==='encounter';this.orbit.minDistance=giant?4:6;this.orbit.maxDistance=14;this.camera.fov=giant?32:36;this.camera.position.set(0,giant?2.1:2.65,giant?5.8:8.9);this.orbit.target.set(0,giant?2.25:1.95,0);this.camera.updateProjectionMatrix();this.orbit.update();this.homeView();}\r\n // Workout overlay: the whole body fills the canvas with the feet on its bottom edge (the user's knee line).\r\n fitBody(){const box=this.bodyBounds;if(box.isEmpty())return;const size=box.getSize(new T.Vector3()),center=box.getCenter(new T.Vector3()),half=T.MathUtils.degToRad(this.camera.fov/2),distance=Math.max(size.y/2/Math.tan(half),size.x/2/(Math.tan(half)*this.camera.aspect))+size.z/2;this.orbit.minDistance=.1;this.orbit.maxDistance=distance*2;this.orbit.target.copy(center);this.camera.position.set(center.x,center.y,center.z+distance);this.orbit.update();}\r\n setStage(stage:'pod'|'encounter'|'overlay'){if(this.externalDance)return;if(stage!=='pod')this.resetPhonePhysics();this.stage=stage;this.focused=null;this.homeElapsed=0;this.floorObjects.forEach(o=>o.visible=stage==='pod');if(!this.cinematicKind)this.resetStageView();this.resize();if(stage==='overlay')this.warm();}\r\n // The workout coach waits offstage (display:none, so tick() skips it) until the first rep counts. Its first real frame\r\n // compiled every shader and uploaded every texture and buffer right then, a 1.3-3.4 s main-thread stall in the middle\r\n // of the set that cost the next rep (tests/camera-counting). Draw one hidden frame as soon as it is staged and loaded\r\n // (phone.ts re-stages it after every load), while the set is still starting, so the walk-in only draws.\r\n warm(){if(!this.rig||this.disposed)return;this.renderer.compile(this.scene,this.camera);this.renderer.render(this.scene,this.camera);}\r\n resetView(side=1){this.side=side;return this.focusRegion('body');}\r\n async exportGLB(){\r\n  if(!this.rig||!this.motion)throw Error('Wait for your creature to load.');\r\n  // Export a clean neutral clone and the same reusable clips used in the app.\r\n  const clone=cloneSkinned(this.rig.root);if(this.interactive){clone.position.set(0,0,0);clone.quaternion.identity();}for(const [name,rest] of Object.entries(this.rig.rest)){const node=clone.getObjectByName(name)!;node.position.copy(rest.position);node.quaternion.copy(rest.quaternion);node.scale.copy(rest.scale);}clone.updateMatrixWorld(true);\r\n  const result=await new GLTFExporter().parseAsync(clone,{binary:true,animations:this.motion.clips});return new Blob([result as ArrayBuffer],{type:'model/gltf-binary'});\r\n }\r\n // renders is a cumulative count of actual renderer.render() calls (D43.5's setMaxFps skips both the render\r\n // and this increment) — tests diff two readings over a real wall-clock window to get an actual renders/s.\r\n stats(){return {dance:this.externalDance,danceVisibleBody:this.danceVisibleBody,awake:!!this.frame,gesture:this.motion?.current,phonePhase:this.phonePhysics.state.phase,phoneMotionPhase:this.phonePhysics.state.phase,phoneShip:this.renderer.domElement.dataset.phoneShip,phoneShipReady:this.renderer.domElement.dataset.phoneShipReady==='true',drawCalls:this.renderer.info.render.calls,triangles:this.renderer.info.render.triangles,visible:this.visible,paused:this.paused,renders:this.renders,maxFps:this.maxFps,programs:this.renderer.info.programs?.length??0,contextLost:this.renderer.getContext().isContextLost(),canvas:{width:this.renderer.domElement.width,height:this.renderer.domElement.height},powerAnchors:this.powerAnchors,rigVersion:1,recipe:this.rig?.recipe};}\r\n dispose(){if(this.disposed)return;this.disposed=true;this.generation++;this.powerSoles=[];this.powerAnchors=[];this.phoneMotionDispose?.();this.phoneMotionDispose=null;window.removeEventListener('myr5:account-ready',this.phoneAccountChange);window.removeEventListener('myr5:account-cleared',this.phoneAccountChange);this.disposePhoneShip();cancelAnimationFrame(this.frame);this.resizeObserver.disconnect();this.visibilityObserver.disconnect();this.orbit.removeEventListener('change',this.phoneCameraChange);this.orbit.dispose();this.motion?.dispose();this.skinTextures.forEach(texture=>texture.dispose());this.skinTextures.clear();this.previewSpaceDispose?.();this.previewSpaceDispose=null;disposeObject(this.scene);if(this.scene.background instanceof T.Texture)this.scene.background.dispose();this.scene.environment?.dispose();this.renderer.dispose();this.renderer.domElement.remove();delete this.mount.dataset.phoneMotionPhase;}\r\n}\r\n","creature/source/war-room-coaches.ts":"import {COACHES,pathIntroCoachIds} from '../../performance-catalog.mjs';\r\nimport {goldenCoach,performanceOwner,readPerformanceProgress} from '../../performance-progress.mjs';\r\nimport {parseRecipe,fresh} from './creator/design';\r\nimport {bodyLockSection} from './creator/track-placements';\r\nimport {resolveRegionMaterial,regionChoice} from './creator/materials-registry';\r\n\r\nconst KEY='myr5-war-room-coaches-v2';\r\nconst selectionKey=()=>`${KEY}/${encodeURIComponent(performanceOwner())}`;\r\ntype Sprite={id:string;family:string;kind:'body'|'pet';sheet:string;frame:number[]};\r\ntype Selection={body:string|null;pet:string|null;goldenBody:boolean};\r\nexport async function loadCoachSpritePreviews(doc:Document){\r\n const response=await fetch('/pod/gala-coaches/manifest.json');if(!response.ok)throw Error('Coach sprites are unavailable.');\r\n const manifest=await response.json(),sprites:Sprite[]=manifest.sprites.filter((sprite:Sprite)=>COACHES.some((coach:any)=>coach.id===sprite.id));\r\n const sheets=new Map<string,{image:HTMLImageElement;mask:HTMLImageElement}>();\r\n const image=async(name:string)=>{const img=new Image();img.src='/pod/gala-coaches/'+name;await img.decode();return img;};\r\n await Promise.all(Object.entries(manifest.sheets).map(async([id,s]:[string,any])=>{const [art,mask]=await Promise.all([image(s.image),image(s.mask)]);sheets.set(id,{image:art,mask});}));\r\n const recipe=()=>{try{return parseRecipe(localStorage.getItem('myr5-recipe-v1')||'null');}catch{return fresh();}};\r\n const cache=new Map<string,HTMLCanvasElement>();\r\n function art(sprite:Sprite,silhouette=false,look?:any,golden=false){\r\n  const r=recipe(),skin=look&&(globalThis as any).GalaAvatar?.skinColor?.(look),colours=golden&&goldenCoach(sprite.id)?['#e8bc4d','#ffe4a1','#6b4214']:skin?[skin,skin,'#fff9df']:['body','head','eye'].map(region=>resolveRegionMaterial(r.styles[region],regionChoice(r.materials,region),true,sprite.id).primary);\r\n  const key=JSON.stringify([sprite.id,silhouette,colours]);if(cache.has(key))return cache.get(key)!;\r\n  const [x,y,w,h]=sprite.frame,canvas=doc.createElement('canvas');canvas.width=w;canvas.height=h;\r\n  const ctx=canvas.getContext('2d',{willReadFrequently:true})!,mask=doc.createElement('canvas');mask.width=w;mask.height=h;\r\n  const sheet=sheets.get(sprite.sheet)!;ctx.drawImage(sheet.image,x,y,w,h,0,0,w,h);const pixels=ctx.getImageData(0,0,w,h);\r\n  const mc=mask.getContext('2d',{willReadFrequently:true})!;mc.drawImage(sheet.mask,x,y,w,h,0,0,w,h);const regions=mc.getImageData(0,0,w,h).data;\r\n  const rgb=(hex:string)=>{const n=parseInt(hex.slice(1),16);return [n>>16,n>>8&255,n&255];};\r\n  for(let i=0;i<pixels.data.length;i+=4){if(!pixels.data[i+3])continue;const region=regions[i],tone=region===0?[33,23,46]:rgb(colours[Math.min(2,Math.round(region/64)-1)]);\r\n   const shade=pixels.data[i]/184;for(let c=0;c<3;c++)pixels.data[i+c]=silhouette?35:region===0?tone[c]:Math.min(255,Math.round(tone[c]*shade));}\r\n  ctx.putImageData(pixels,0,0);cache.set(key,canvas);return canvas;\r\n }\r\n return {sprites,art};\r\n}\r\nexport async function loadWarRoomCoaches(doc:Document){\r\n const {sprites,art}=await loadCoachSpritePreviews(doc);\r\n const lock=(id:string)=>bodyLockSection(id,{});\r\n let selection:Selection={body:null,pet:null,goldenBody:false},selectionOwner='';\r\n // Keyed by owner and stored value: the pod reuses this across War Room visits (another page writes the choice).\r\n function readSelection(){const owner=selectionKey();let raw:string|null=null;try{raw=localStorage.getItem(owner);}catch{}if(owner+' '+raw===selectionOwner)return;selectionOwner=owner+' '+raw;selection={body:null,pet:null,goldenBody:false};\r\n  try{const saved=JSON.parse(raw||'null');for(const kind of ['body','pet'] as const)if(sprites.some(s=>s.id===saved?.[kind]&&(kind==='body'||s.kind===kind))&&!lock(saved[kind]))selection[kind]=saved[kind];selection.goldenBody=saved?.goldenBody===true&&!!selection.body&&goldenCoach(selection.body);}catch{}\r\n }\r\n readSelection();\r\n function picker(kind:'body'|'pet',changed:()=>void){\r\n  readSelection();\r\n  const group=doc.createElement('fieldset'),legend=doc.createElement('legend');legend.textContent=kind==='body'?'Coach body':'Coach pets';group.append(legend);group.className='gala-coach-choices';\r\n  const note=doc.createElement('p');note.className='help';note.textContent=kind==='body'?'Choose a coach character, then change its colour in the mirror. Choose Gala body to show your Gala wardrobe.':'Coach pets unlock with their coach bodies.';group.append(note);\r\n  const choose=(id:string|null,golden=false)=>{if(id&&lock(id))return;const next={...selection,[kind]:id,...(kind==='body'?{goldenBody:golden}: {})};try{localStorage.setItem(selectionKey(),JSON.stringify(next));selection=next;changed();}catch{group.setAttribute('aria-label','This choice could not be saved. Storage is unavailable.');}};\r\n  const standard=doc.createElement('button');standard.type='button';standard.textContent=kind==='body'?'Gala body':'Gala pet';standard.setAttribute('aria-pressed',String(!selection[kind]));standard.onclick=()=>choose(null);group.append(standard);\r\n  const introductory=new Set(pathIntroCoachIds(readPerformanceProgress().paths));\r\n  const ordered=[...sprites.filter(s=>kind==='body'||s.kind===kind)].sort((a,b)=>Number(!!lock(a.id))-Number(!!lock(b.id))||Number(introductory.has(b.id))-Number(introductory.has(a.id)));\r\n  for(const sprite of ordered){\r\n   const locked=lock(sprite.id),button=doc.createElement('button'),name=COACHES.find(c=>c.id===sprite.id)?.label||sprite.family;\r\n   button.type='button';button.dataset.coachSprite=sprite.id;button.setAttribute('aria-label',name);button.setAttribute('aria-pressed',String(selection[kind]===sprite.id&&(kind!=='body'||!selection.goldenBody)));\r\n   const source=art(sprite),preview=doc.createElement('canvas');preview.width=source.width;preview.height=source.height;preview.getContext('2d')!.drawImage(source,0,0);\r\n   const label=doc.createElement('span');label.textContent=name;button.append(preview,label);button.onclick=()=>{if(locked){note.textContent=`Preview only · How to unlock ${name}: ${locked}.`;return;}choose(sprite.id);};group.append(button);\r\n   if(kind==='body'&&goldenCoach(sprite.id)&&!locked){const gold=doc.createElement('button');gold.type='button';gold.dataset.coachSprite=sprite.id;gold.dataset.goldenCoach='true';gold.setAttribute('aria-label','Golden '+name);gold.setAttribute('aria-pressed',String(selection.body===sprite.id&&selection.goldenBody));const artPreview=art(sprite,false,undefined,true),goldCanvas=doc.createElement('canvas');goldCanvas.width=artPreview.width;goldCanvas.height=artPreview.height;goldCanvas.getContext('2d')!.drawImage(artPreview,0,0);const text=doc.createElement('span');text.textContent='Golden '+name;gold.append(goldCanvas,text);gold.onclick=()=>choose(sprite.id,true);group.append(gold);}\r\n  }return group;\r\n }\r\n function draw(avatarDraw:any,canvas:HTMLCanvasElement,look:any,options:any={}){\r\n  readSelection();\r\n  const id=options.petOnly?selection.pet:selection.body,sprite=sprites.find(s=>s.id===id);\r\n  if(!sprite||lock(sprite.id)){avatarDraw(canvas,look,options);return;}\r\n  canvas.width=64;canvas.height=96;const ctx=canvas.getContext('2d')!;ctx.imageSmoothingEnabled=false;\r\n  if(options.petOnly)ctx.drawImage(art(sprite,false,look,!options.petOnly&&selection.goldenBody),0,0,48,36,0,60,48,36);else if(sprite.kind==='pet')ctx.drawImage(art(sprite,false,look,!options.petOnly&&selection.goldenBody),0,0,48,36,0,48,64,48);else ctx.drawImage(art(sprite,false,look,!options.petOnly&&selection.goldenBody),0,0);\r\n }\r\n return {picker,draw,get hasBody(){readSelection();return !!selection.body&&!lock(selection.body);},get hasPet(){readSelection();return !!selection.pet&&!lock(selection.pet);}};\r\n}\r\n","creature/source/war-room-gala.ts":"// The War Room's 3D character bay: the player's own 64-bit Gala character (the saved mominc-avatar-v1 look,\r\n// drawn by the shared Gala renderer and performer) stands on the cage room's pedestal. Coach sprites share\r\n// the saved coach's colours and body ownership; no coach models or customizer are loaded. The room's bays open the Gala menus: the weapon\r\n// rack picks the weapon, the animal cages the pet, the mirror the alien features, the centre station the clothes.\r\n// Choices save to this device like every other Gala look change; tiers stay behind the training that earned them.\r\nimport * as T from 'three';\r\nimport {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';\r\nimport {RoomEnvironment} from 'three/examples/jsm/environments/RoomEnvironment.js';\r\nimport {mountCage,cagePacketReady,cageStyle,SECTIONS,type CageSection,type CageViewer} from './cage';\r\nimport {GALA_KEY,loadGala} from '../../pod/identity.mjs';\r\nimport {PERFORMANCE_KEY} from '../../performance-progress.mjs';\r\nimport {loadWarRoomCoaches} from './war-room-coaches';\r\nimport {createGalaEditor} from '../../war-room/gala-editor.js';\r\nimport {effectPanel} from '../../war-room/effect-picker.mjs';\r\n// The war-room page never loads the vault store (it needs the app bundle): queue for the app to drain (the store's drainPending).\r\nconst vaultQueue=(counter:string,key?:string)=>{try{const k='myr5-vault-pending',q=JSON.parse(localStorage.getItem(k)||'[]');q.push([counter,1,key]);localStorage.setItem(k,JSON.stringify(q.slice(-50)));}catch{}};\r\n\r\ntype Look={schema:string;version:number;name:string;dye:number;trim?:number;parts:Record<string,number>;weapon?:{type:string;tier:number}};\r\ntype Section={id:string;label:string;note:string;names:string[];choices:number[]};\r\ntype Bay=Exclude<CageSection,'overview'>|'auras';\r\nconst G=globalThis as any;\r\nexport const MIRROR=['body','skin','face','hair','facial'];\r\nexport const CLOTHES=['headwear','neck','torso','shoulders','arms','hands','legs','feet','held','back'];\r\n// DFO rig axes (v2 look): species morphs sit at the mirror, armour at the centre station.\r\nexport const SPECIES=['build','head','crown','jaw','tail'],ARMOUR=['armor','alloy','shield','base'];\r\nexport const IDLE_WALK_MS=10000;\r\n// Ian 20 Oct: the character stands 30% larger in the bay (was 4.2 units tall).\r\nexport const CHARACTER_SCALE=1.3,CHARACTER_H=4.2*CHARACTER_SCALE;\r\n// Zoom on the part being changed: a sub-menu (or a bay without sub-menus) frames one band of the character.\r\n// [centre, height] as fractions of the billboard height (feet 0); measured on the performer's 160x168 canvas.\r\nexport const ZOOM_BANDS={head:[.71,.32],chest:[.5,.34],hand:[.46,.42],legs:[.3,.32],feet:[.17,.24],body:[.45,.88],wide:[.42,1.15]} as const;\r\nexport type ZoomBand=keyof typeof ZOOM_BANDS;\r\nconst ZOOM_OF:Record<string,ZoomBand>={face:'head',hair:'head',facial:'head',head:'head',crown:'head',jaw:'head',headwear:'head',neck:'head',\r\n torso:'chest',shoulders:'chest',arms:'chest',armor:'chest',back:'chest',hands:'hand',held:'hand',shield:'hand',weapons:'hand',\r\n legs:'legs',tail:'legs',feet:'feet',base:'feet',pet:'wide',pets:'wide',auras:'wide'};\r\n/** The band a sub-menu (part id) or menu-less bay zooms to; null keeps the bay's own framing. Anything else frames the whole body. */\r\nexport function zoomBand(id:string|null):ZoomBand|null{return id==null||id==='mirror'||id==='clothing'?null:ZOOM_OF[id]??'body';}\r\n// The dressing room's (mominc.online/gala) names for the ten silks.\r\nexport const DYE_NAMES=['Nebula velvet','Plasma rose','Tidal silk','Reactor satin','Comet gold','Void blue','Solar copper','Lunar pearl','Obsidian smoke','Glacier mint'];\r\nexport const LABELS:Record<CageSection,{label:string;name:string}>={ pets:{label:'Pets',name:'Animal cages: pet'}, weapons:{label:'Weapons',name:'Weapon rack: weapon'}, mirror:{label:'Mirror',name:'Mirror: alien features'}, clothing:{label:'Clothes',name:'Centre station: clothes'}, overview:{label:'Room',name:'Whole War Room'}, pedestal:{label:'Gala',name:'Your Gala character: face'}};\r\n// Which performer scene plays: idle loops, a face tap zooms in once, and ten idle seconds send the character walking off and back.\r\nexport function sceneClock(mode:{name:'idle'|'face'|'walk';since:number},now:number,idleSince:number,scenes:{name:string;duration:number}[]){\r\n const start=(name:string)=>{let t=0;for(const s of scenes){if(s.name===name)return {t,duration:s.duration};t+=s.duration;}return null;};\r\n const idle=scenes[0].duration;\r\n if(mode.name!=='idle'){const s=start(mode.name)!,elapsed=now-mode.since;if(elapsed<s.duration)return {mode,time:s.t+elapsed};if(mode.name==='walk')idleSince=now;mode={name:'idle',since:now};}\r\n if(now-idleSince>=IDLE_WALK_MS){mode={name:'walk',since:now};return {mode,time:start('walk')!.t,idleSince:now};}\r\n return {mode,time:(now-mode.since)%idle,idleSince};\r\n}\r\n\r\nclass GalaStage implements CageViewer{\r\n scene=new T.Scene();camera=new T.PerspectiveCamera(36,1,.1,80);renderer:T.WebGLRenderer;orbit:OrbitControls;bodyBounds=new T.Box3();floorObjects:T.Object3D[]=[];focused=null;\r\n settings={amount:1,ambient:true,reduced:false};\r\n constructor(public mount:HTMLElement){\r\n  this.renderer=new T.WebGLRenderer({antialias:true,alpha:true,powerPreference:'low-power'});\r\n  this.renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));this.renderer.outputColorSpace=T.SRGBColorSpace;this.renderer.toneMapping=T.ACESFilmicToneMapping;\r\n  const environment=new RoomEnvironment(),pmrem=new T.PMREMGenerator(this.renderer);this.scene.environment=pmrem.fromScene(environment,.04).texture;environment.dispose();pmrem.dispose();\r\n  this.renderer.domElement.setAttribute('role','img');mount.append(this.renderer.domElement);\r\n  this.camera.position.set(0,2.65*CHARACTER_SCALE,8.9*CHARACTER_SCALE);this.orbit=new OrbitControls(this.camera,this.renderer.domElement);this.orbit.target.set(0,1.95*CHARACTER_SCALE,0);this.orbit.enableDamping=true;this.orbit.enablePan=false;this.orbit.enableZoom=false;this.orbit.maxPolarAngle=Math.PI*.85;\r\n  this.scene.add(new T.HemisphereLight(0xe5d5ff,0x23152e,2));const key=new T.DirectionalLight(0xffeedc,3);key.position.set(-3,5,5);this.scene.add(key);\r\n  const floor=new T.Mesh(new T.CylinderGeometry(1.45,1.55,.08,64),new T.MeshStandardMaterial({color:0x241e31,roughness:.5,metalness:.4}));floor.position.y=-.05;this.scene.add(floor);this.floorObjects=[floor];\r\n }\r\n resize(){const width=this.mount.clientWidth,height=this.mount.clientHeight;if(width&&height){this.renderer.setSize(width,height,false);this.camera.aspect=width/height;this.camera.updateProjectionMatrix();}}\r\n}\r\n\r\nexport function mountGalaBay(host:HTMLElement,{tell}:{tell:(text:string)=>void}){\r\n const A=G.GalaAvatar,W=G.GalaWeapons,P=G.GalaPerformance;\r\n if(!A||!W||!P)throw Error('The Gala character renderer is missing.');\r\n cageStyle();\r\n const doc=host.ownerDocument,el=<K extends keyof HTMLElementTagNameMap>(tag:K,text='',cls='')=>{const e=doc.createElement(tag);if(text)e.textContent=text;if(cls)e.className=cls;return e;};\r\n const stageEl=el('div','','gala-bay-stage'),panel=el('section','','gala-bay-panel'),heading=el('h3'),body=el('div');\r\n panel.hidden=true;panel.append(heading,body);host.append(stageEl,panel);\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n const stage=new GalaStage(stageEl);stage.settings.reduced=reduced.matches;\r\n // The character: the performer's 160×168 canvas on a billboard, feet on the pedestal top (y 0).\r\n const canvas=doc.createElement('canvas'),texture=new T.CanvasTexture(canvas);canvas.width=160;canvas.height=168;\r\n texture.magFilter=texture.minFilter=T.NearestFilter;texture.generateMipmaps=false;texture.colorSpace=T.SRGBColorSpace;\r\n const H=CHARACTER_H,Wd=H*160/168,sprite=new T.Mesh(new T.PlaneGeometry(Wd,H),new T.MeshBasicMaterial({map:texture,transparent:true,alphaTest:.1,side:T.DoubleSide}));\r\n sprite.position.y=H/2-H*11/168;sprite.name='gala-character';stage.scene.add(sprite);\r\n stage.bodyBounds.set(new T.Vector3(-Wd*.3,0,-.3),new T.Vector3(Wd*.3,H*144/168,.3));\r\n let saved:Look,look:Look,performer:any,mode:{name:'idle'|'face'|'walk';since:number}={name:'idle',since:performance.now()},idleSince=performance.now(),frame=0,last=0,disposed=false,open:Bay|null=null,openPart:string|null=null;\r\n // The zoom: glide the camera to a band of the character and back to where it was (the bay's framing).\r\n let zoomed:ZoomBand|null=null,home:{p:T.Vector3;t:T.Vector3}|null=null,zoomTween=0,zoomWait=0;\r\n function glide(p:T.Vector3,t:T.Vector3){\r\n  cancelAnimationFrame(zoomTween);zoomTween=0;const camera=stage.camera,orbit=stage.orbit;\r\n  if(reduced.matches){camera.position.copy(p);orbit.target.copy(t);orbit.update();return;}\r\n  const p0=camera.position.clone(),t0=orbit.target.clone(),start=performance.now();\r\n  const step=(now:number)=>{if(disposed)return;const k=Math.min(1,(now-start)/520),e=1-(1-k)**3;camera.position.lerpVectors(p0,p,e);orbit.target.lerpVectors(t0,t,e);orbit.update();zoomTween=k<1?requestAnimationFrame(step):0;};\r\n  zoomTween=requestAnimationFrame(step);\r\n }\r\n function zoomTo(band:ZoomBand|null){\r\n  clearTimeout(zoomWait);zoomWait=0;if(band===zoomed)return;\r\n  const camera=stage.camera;\r\n  if(!band){zoomed=null;if(home)glide(home.p,home.t);home=null;return;}\r\n  if(!home)home={p:camera.position.clone(),t:stage.orbit.target.clone()};\r\n  zoomed=band;const [centre,size]=ZOOM_BANDS[band],h=size*H;\r\n  const target=new T.Vector3(sprite.position.x,centre*H,sprite.position.z),v=T.MathUtils.degToRad(camera.fov/2),hz=Math.atan(Math.tan(v)*camera.aspect);\r\n  // Fit the band into the stage above the bay key row (which covers its bottom), centred in what is left.\r\n  const row=stage.mount.querySelector('.cage-bays') as HTMLElement|null,left=Math.max(.4,1-((row?.offsetHeight||0)+6)/Math.max(1,stage.mount.clientHeight));\r\n  const distance=Math.max(1.2,h/2/(Math.tan(Math.min(v,hz))*(hz<v?1:left))*1.12);const from=home.p.clone().sub(home.t);from.y=Math.max(from.y,0);from.normalize();\r\n  // Shift along the camera's own up (not world y) so a downward-looking shot centres the band too.\r\n  const up=new T.Vector3(0,1,0).addScaledVector(from,-from.y).normalize();target.addScaledVector(up,-distance*Math.tan(v)*(1-left));\r\n  glide(target.clone().addScaledVector(from,distance),target);\r\n }\r\n // A bay change moves the camera itself (the cage glide); forget the old home and zoom once it has settled.\r\n function bayZoom(band:ZoomBand|null){clearTimeout(zoomWait);cancelAnimationFrame(zoomTween);zoomTween=0;\r\n  if(cage&&!reduced.matches){zoomed=null;home=null;if(band)zoomWait=setTimeout(()=>zoomTo(band),680) as unknown as number;} // ponytail: fixed wait for cage.ts's 650 ms glide; expose its tween if it changes\r\n  else if(cage){zoomed=null;home=null;zoomTo(band);}\r\n  else zoomTo(band);\r\n }\r\n let coaches:Awaited<ReturnType<typeof loadWarRoomCoaches>>|null=null;\r\n void loadWarRoomCoaches(doc).then(value=>{if(!disposed){coaches=value;load();}}).catch(()=>{if(!disposed)tell('Coach sprites could not load. Reopen the War Room to retry.');});\r\n function load(){\r\n  let storage:Storage|undefined;try{storage=localStorage;}catch{}\r\n  saved=loadGala(storage,A).look;look=structuredClone(saved);\r\n  const chosen=look.weapon||{type:'rapier',tier:0};look.weapon=W.unlocked(chosen)?chosen:{type:chosen.type,tier:0};\r\n  performer=P.create(look,coaches?{draw:(canvas:any,value:any,options:any)=>coaches!.draw(A.draw,canvas,value,options),hasPet:coaches.hasPet}:{});stage.renderer.domElement.setAttribute('aria-label',`${look.name||'Your Gala character'} with ${W.name(look.weapon)} in the War Room`);\r\n  stage.mount.querySelectorAll<HTMLElement>('[data-cage-section=clothing]').forEach(control=>{control.hidden=!!coaches?.hasBody;});\r\n  if(open)showBay(open);\r\n }\r\n const touch=()=>{idleSince=performance.now();if(mode.name==='walk')mode={name:'idle',since:idleSince};};\r\n function save(next:Look,message:string){\r\n  try{localStorage.setItem(GALA_KEY,JSON.stringify(A.normalize(next)));dispatchEvent(new Event('mominc-avatar-change'));tell(message+' Saved on this device.');}\r\n  catch{tell('This choice could not be saved. Storage is unavailable on this device.');}\r\n }\r\n function picker(section:Section){\r\n  const label=el('label',section.label),select=el('select'),current=saved.parts[section.id];\r\n  const values=section.choices.includes(current)?section.choices:[current,...section.choices];\r\n  for(const value of values){const option=el('option',section.names[value]);option.value=String(value);select.append(option);}\r\n  select.value=String(current);select.dataset.galaPart=section.id;\r\n  select.onchange=()=>{touch();save({...saved,parts:{...saved.parts,[section.id]:Number(select.value)}},`${section.label}: ${section.names[Number(select.value)]}.`);};\r\n  label.append(select);return label;\r\n }\r\n const sections=(ids:string[])=>ids.map(id=>(A.sections as Section[]).find(s=>s.id===id)!).filter(Boolean).map(picker);\r\n function weaponForm(){\r\n  const form=el('div','','gala-weapon'),type=el('select'),tier=el('select'),selected=saved.weapon||{type:'rapier',tier:0};\r\n  type.setAttribute('aria-label','Gala weapon');tier.setAttribute('aria-label','Weapon upgrade');type.dataset.galaWeapon='type';tier.dataset.galaWeapon='tier';\r\n  for(const item of W.types){const option=el('option',item.name);option.value=item.id;type.append(option);}\r\n  type.value=selected.type;\r\n  for(let i=0;i<W.tiers.length;i++){const item={type:selected.type,tier:i},option=el('option');option.value=String(i);option.disabled=!W.unlocked(item);option.textContent=W.tiers[i];tier.append(option);}\r\n  tier.value=String(selected.tier);\r\n  // The weapon already saved is kept as it is (an earned tier survives a guest or offline visit); anything else must be earned.\r\n  const equip=(next:{type:string;tier:number})=>{touch();const kept=next.type===selected.type&&next.tier===selected.tier;if(!kept&&!W.unlocked(next))next={type:next.type,tier:0};save({...saved,weapon:next},W.name(next)+'.');};\r\n  type.onchange=()=>equip({type:type.value,tier:type.value===selected.type?selected.tier:0});tier.onchange=()=>equip({type:type.value,tier:Number(tier.value)});\r\n  const typeLabel=el('label','Weapon'),tierLabel=el('label','Upgrade');typeLabel.append(type);tierLabel.append(tier);\r\n  form.append(typeLabel,tierLabel,el('p','Workout performance earns weapon upgrades.','help'));\r\n  return form;\r\n }\r\n function showBay(section:Bay){\r\n  touch();\r\n  if(section==='pedestal'){if(!reduced.matches)mode={name:'face',since:performance.now()};closeBay();return;}\r\n  // A save re-renders the open menu; keep the control the player was using focused.\r\n  const active=doc.activeElement as HTMLElement|null,keep=panel.contains(active)?active!.dataset:null;\r\n  const fresh=open!==section;\r\n  if(fresh){panel.scrollTop=0;openPart=null;} // a new bay starts at its top, every sub-menu closed\r\n  open=section;panel.hidden=false;panel.dataset.galaBay=section;\r\n  const title=({pets:'Animal cages · Pet',weapons:'Weapon rack · Weapon',mirror:'Mirror · Alien features',clothing:'Centre station · Clothes',auras:'Aura forge · Auras'} as Record<string,string>)[section];\r\n  heading.textContent=title;\r\n  const editor=createGalaEditor({document:doc,avatar:A,weapons:W,look:saved,\r\n   previewDraw:coaches?.hasBody?(canvas:HTMLCanvasElement,previewLook:Look,options:any)=>coaches!.draw(A.draw,canvas,previewLook,options):A.draw,\r\n   onPart:(id:string,value:number,label:string)=>{touch();vaultQueue('gala-part');vaultQueue('gala-slot',id);save({...saved,parts:{...saved.parts,[id]:value}},`${(A.sections as Section[]).find(item=>item.id===id)?.label||label}: ${label}.`);},\r\n   onDye:(value:number)=>{touch();vaultQueue('gala-slot','dye');save({...saved,dye:value},`Silk: ${DYE_NAMES[value]}.`);},\r\n   onTrim:(value:number)=>{touch();vaultQueue('gala-slot','trim');save({...saved,trim:value},`Trim: ${DYE_NAMES[value]}.`);},\r\n   onWeapon:(value:{type:string;tier:number})=>{touch();vaultQueue('gala-slot','weapon');const previous=saved.weapon||{type:'rapier',tier:0};if((value.type!==previous.type||value.tier!==previous.tier)&&!W.unlocked(value))value={type:value.type,tier:0};save({...saved,weapon:value},W.name(value)+'.');},\r\n   locked:(value:{type:string;tier:number})=>!W.unlocked(value),\r\n   openPart,onToggle:(id:string|null)=>{touch();openPart=id;zoomTo(zoomBand(id)??zoomBand(section));}\r\n  });\r\n  // Coach bodies/pets sit in a collapsed block (the Gala wardrobe stays first); its Gala tile shows the rig.\r\n  const coachBlock=(kind:'body'|'pet')=>{const group=coaches!.picker(kind,load),wrap=el('details','','gala-coach-block'),summary=el('summary',kind==='body'?'Coach bodies':'Coach pets');wrap.append(summary,group);\r\n   const own=group.querySelector('button:not([data-coach-sprite])');if(own){const c=el('canvas');c.className='gala-option-art is-tall';A.draw(c,saved,{base:false,weapon:false,companion:kind==='pet',petOnly:kind==='pet',prop:false});own.prepend(c);}\r\n   if(group.querySelector('button:not([data-coach-sprite])[aria-pressed=\"false\"]'))wrap.open=true;return wrap;};\r\n  if(section==='auras')body.replaceChildren(effectPanel(doc,{avatar:A,look:saved}));\r\n  else if(section==='pets')body.replaceChildren(...editor.parts(['pet']),...(coaches?[coachBlock('pet')]:[]));\r\n  else if(section==='weapons')body.replaceChildren(editor.weapons());\r\n  else if(section==='mirror')body.replaceChildren(...editor.parts(coaches?.hasBody?['skin']:[...MIRROR,...SPECIES]),...(coaches?[coachBlock('body')]:[]));\r\n  else if(coaches?.hasBody){\r\n   body.replaceChildren(el('p','Coach characters do not wear Gala clothes. Change their colour at the mirror.','help'));\r\n  }\r\n  else{\r\n   body.replaceChildren(...editor.parts([...CLOTHES,...ARMOUR]),editor.dye(),editor.trim());\r\n  }\r\n  if(fresh)bayZoom(zoomBand(section));\r\n  if(keep?.galaPart)(body.querySelector(`[data-gala-part=\"${keep.galaPart}\"]`) as HTMLElement|null)?.focus();\r\n  if(keep?.galaWeapon)(body.querySelector(`[data-gala-weapon=\"${keep.galaWeapon}\"]`) as HTMLElement|null)?.focus();\r\n }\r\n function closeBay(){open=null;openPart=null;panel.hidden=true;body.replaceChildren();bayZoom(null);}\r\n // Without the downloaded room (or if it fails), the same menus sit under the character on the plain floor disc.\r\n let cage:ReturnType<typeof mountCage>|null=null;\r\n function flatBays(){\r\n  const row=el('div','','cage-bays');row.setAttribute('role','group');row.setAttribute('aria-label','War Room sections');\r\n  for(const s of SECTIONS){if(s.id==='overview'||s.id==='pedestal')continue;const b=el('button',LABELS[s.id].label);b.type='button';b.setAttribute('aria-label',LABELS[s.id].name);b.dataset.cageSection=s.id;b.classList.add('bay-terminal-line');b.onclick=()=>{for(const x of row.children)x.setAttribute('aria-pressed',String(x===b));if(s.id==='overview')closeBay();else showBay(s.id);};row.append(b);}\r\n  stageEl.append(row);row.querySelectorAll<HTMLElement>('[data-cage-section=clothing]').forEach(control=>{control.hidden=!!coaches?.hasBody;});\r\n }\r\n void cagePacketReady().then(have=>{\r\n  if(disposed)return;\r\n  if(!have){flatBays();tell('Download the 3D cage: Install → Downloads.');return;}\r\n  cage=mountCage(stage,{openTab:()=>false,showBay:()=>{},closeBay,tell,bay:showBay,labels:LABELS,volumes:{pedestal:[[.02,.64,-.01,.22,.20,.24,0]],clothing:[[.06,.3,-.31,.18,.12,.1,0]]}});\r\n  stage.mount.querySelectorAll('[data-cage-section=overview],[data-cage-section=pedestal]').forEach(el=>el.remove());\r\n  void cage.ready.then(ok=>{if(!disposed){if(!ok)flatBays();load();}});\r\n });\r\n const tick=(now:number)=>{\r\n  if(disposed)return;frame=requestAnimationFrame(tick);if(now-last<32||doc.hidden)return;last=now;\r\n  sprite.rotation.y=Math.atan2(stage.camera.position.x-sprite.position.x,stage.camera.position.z-sprite.position.z);\r\n  // The billboard faces the camera; the rig inside it turns with the orbit, so the room shows every side of the character.\r\n  const facing={yaw:(A.DEFAULT_YAW??0)-sprite.rotation.y};\r\n  if(reduced.matches)performer.paint(canvas,0,true,facing);\r\n  else{const clock=sceneClock(mode,now,idleSince,performer.scenes);mode=clock.mode;idleSince=clock.idleSince??idleSince;performer.paint(canvas,clock.time,false,facing);}\r\n  texture.needsUpdate=true;stage.orbit.update();stage.renderer.render(stage.scene,stage.camera);\r\n };\r\n const resize=new ResizeObserver(()=>stage.resize());resize.observe(stageEl);stage.resize();\r\n const onStorage=(event:StorageEvent)=>{if(event.key===GALA_KEY)load();else if(event.key?.startsWith(PERFORMANCE_KEY+'/')&&open)showBay(open);},onReduced=()=>{stage.settings.reduced=reduced.matches;};\r\n const onCoachProgress=()=>{if(open)showBay(open);};\r\n stage.renderer.domElement.addEventListener('pointerdown',touch);\r\n addEventListener('mominc-avatar-change',load);addEventListener('storage',onStorage);addEventListener('myr5:performance-progress',onCoachProgress);reduced.addEventListener('change',onReduced);\r\n load();frame=requestAnimationFrame(tick);\r\n return {stage,get cage(){return cage;},get mode(){return mode.name;},get zoom(){return zoomed;},get openPart(){return openPart;},showBay,closeBay,\r\n  dispose(){if(disposed)return;disposed=true;cancelAnimationFrame(frame);cancelAnimationFrame(zoomTween);clearTimeout(zoomWait);cage?.dispose();resize.disconnect();removeEventListener('mominc-avatar-change',load);removeEventListener('storage',onStorage);removeEventListener('myr5:performance-progress',onCoachProgress);reduced.removeEventListener('change',onReduced);\r\n   texture.dispose();sprite.geometry.dispose();(sprite.material as T.Material).dispose();stage.orbit.dispose();stage.scene.environment?.dispose();stage.renderer.dispose();host.replaceChildren();}};\r\n}\r\n","creature/source/weapon-wall.mjs":"import {authFetch} from '../../auth-client.mjs';\r\nimport {authTransitions} from '../../auth-transition.mjs';\r\nimport {createWarRoomApi} from '../../war-room/account-api.mjs';\r\n\r\nexport const GALA_WEAPONS=Object.freeze([\r\n ['rapier','Rapier'],['greatsword','Greatsword'],['dagger','Dagger'],['sabre','Sabre'],['axe','Axe'],\r\n ['hammer','Hammer'],['mace','Mace'],['flail','Flail'],['spear','Spear'],['trident','Trident'],\r\n ['halberd','Halberd'],['scythe','Scythe'],['bow','Bow'],['crossbow','Crossbow'],['chakram','Chakram'],\r\n ['gauntlets','Gauntlets'],['staff','Staff'],['wand','Wand'],['tome','Tome'],['cannon','Cannon']\r\n]);\r\nconst TYPES=new Set(GALA_WEAPONS.map(([type])=>type));\r\nconst ownerOf=account=>typeof account==='string'?account:account?.user?.id;\r\nconst validState=state=>state&&Number.isSafeInteger(state.revision)&&state.revision>=0&&\r\n TYPES.has(state.loadout?.type)&&Number.isInteger(state.loadout?.tier)&&\r\n state.loadout.tier>=0&&state.loadout.tier<=20;\r\n\r\nexport function createWeaponWallController({\r\n request=authFetch,transitions=authTransitions(),\r\n getAccount=()=>globalThis.window?.myr5AuthenticatedAccount\r\n}={}){\r\n const client=createWarRoomApi({request,transitions}),listeners=new Set();\r\n let generation=0,disposed=false,pending=false,owner=null;\r\n let snapshot={mode:'locked',message:'Sign in to view your War Room loadout.',loadout:null,revision:null};\r\n const set=next=>{snapshot={...snapshot,...next};for(const listener of listeners)listener({...snapshot});};\r\n const reset=()=>{generation++;pending=false;owner=null;client.clear();set({mode:'locked',message:'Account changed. Refresh the weapon wall.',loadout:null,revision:null});};\r\n const unsubscribe=transitions.subscribe(reset);\r\n const current=(run,ticket,expectedOwner)=>{\r\n  if(disposed||run!==generation||ownerOf(getAccount())!==expectedOwner)return false;\r\n  try{transitions.assertCurrent(ticket);return true;}catch{return false;}\r\n };\r\n async function refresh(message){\r\n  if(disposed)return;\r\n  const run=++generation,expectedOwner=ownerOf(getAccount());\r\n  pending=false;owner=null;client.clear();\r\n  if(!expectedOwner){set({mode:'locked',message:'Sign in to view your War Room loadout.',loadout:null,revision:null});return;}\r\n  let ticket;\r\n  try{ticket=transitions.capture();}catch{set({mode:'unavailable',message:'Account sync is unavailable. Refresh to retry.',loadout:null,revision:null});return;}\r\n  set({mode:'loading',message:'Checking War Room access…',loadout:null,revision:null});\r\n  try{\r\n   const room=await client.api('/api/war-room');\r\n   if(!current(run,ticket,expectedOwner))return;\r\n   if(room.targetAccountId!==expectedOwner||!validState(room.state))throw Error('War Room returned an invalid account loadout.');\r\n   owner=expectedOwner;\r\n   set({mode:'ready',message:message||'Saved Gala loadout for this account.',loadout:{...room.state.loadout},revision:room.state.revision});\r\n  }catch(error){\r\n   if(!current(run,ticket,expectedOwner))return;\r\n   const unauthenticated=error.status===401;\r\n   set({mode:unauthenticated?'locked':'unavailable',\r\n    message:unauthenticated?'Sign in to view your War Room loadout.':error.message||'War Room unavailable. Refresh to retry.',\r\n    loadout:null,revision:null});\r\n  }\r\n }\r\n async function save(type,tier){\r\n  if(disposed||pending||snapshot.mode!=='ready'||!owner||ownerOf(getAccount())!==owner)return false;\r\n  if(!TYPES.has(type)||!Number.isInteger(tier)||tier<0||tier>20){\r\n   set({message:'Choose a valid Gala weapon and tier from 0 to 20.'});return false;\r\n  }\r\n  const run=generation,expectedOwner=owner,revision=snapshot.revision;\r\n  let ticket;\r\n  try{ticket=transitions.capture();}catch{reset();return false;}\r\n  pending=true;set({mode:'saving',message:'Saving Gala loadout…'});\r\n  try{\r\n   const reply=await client.api('/api/war-room/loadout',{method:'PUT',headers:{'Content-Type':'application/json'},\r\n    body:JSON.stringify({revision,loadout:{type,tier}})});\r\n   if(!current(run,ticket,expectedOwner))return false;\r\n   if(!validState(reply.state))throw Error('War Room returned an invalid loadout.');\r\n   set({mode:'ready',message:'Gala loadout saved.',loadout:{...reply.state.loadout},revision:reply.state.revision});\r\n   return true;\r\n  }catch(error){\r\n   if(!current(run,ticket,expectedOwner))return false;\r\n   if(error.status===409){await refresh('War Room changed on another device. Review the refreshed loadout before saving.');return false;}\r\n   owner=null;client.clear();\r\n   set({mode:error.status===401?'locked':'unavailable',\r\n    message:error.message||'Could not save. Refresh to retry.',loadout:null,revision:null});\r\n   return false;\r\n  }finally{if(run===generation)pending=false;}\r\n }\r\n return {\r\n  getState:()=>({...snapshot,loadout:snapshot.loadout&&{...snapshot.loadout}}),\r\n  subscribe(listener){listeners.add(listener);listener({...snapshot});return()=>listeners.delete(listener);},\r\n  refresh,save,\r\n  dispose(){if(disposed)return;disposed=true;generation++;listeners.clear();unsubscribe?.();client.dispose();}\r\n };\r\n}\r\n\r\n// The cage mounts this after its own scene admission; the server still requires the signed-in owner.\r\nexport function mountWeaponWall({host,request,transitions,getAccount,eventTarget=globalThis.window}){\r\n if(!host?.ownerDocument)throw Error('Weapon wall needs a host element.');\r\n const doc=host.ownerDocument,controller=createWeaponWallController({request,transitions,getAccount});\r\n const root=doc.createElement('section'),heading=doc.createElement('h2'),status=doc.createElement('p');\r\n const type=doc.createElement('select'),tier=doc.createElement('input'),save=doc.createElement('button');\r\n const refresh=doc.createElement('button'),link=doc.createElement('a');\r\n root.className='cage-weapon-wall';heading.textContent='Weapon wall';status.setAttribute('role','status');\r\n type.setAttribute('aria-label','Gala weapon');\r\n for(const [id,label] of GALA_WEAPONS){const option=doc.createElement('option');option.value=id;option.textContent=label;type.append(option);}\r\n tier.type='number';tier.min='0';tier.max='20';tier.step='1';tier.setAttribute('aria-label','Saved tier');\r\n save.type='button';save.textContent='Save loadout';refresh.type='button';refresh.textContent='Refresh';\r\n link.href='/war-room/';link.textContent='Open War Room';\r\n root.append(heading,status,type,tier,save,refresh,link);host.append(root);\r\n const unsubscribe=controller.subscribe(state=>{\r\n  status.textContent=state.message;const editable=state.mode==='ready';\r\n  type.disabled=tier.disabled=save.disabled=!editable;\r\n  refresh.disabled=state.mode==='loading'||state.mode==='saving';\r\n  if(state.loadout){type.value=state.loadout.type;tier.value=String(state.loadout.tier);}\r\n });\r\n const onSave=()=>void controller.save(type.value,tier.value?Number(tier.value):NaN);\r\n const onRefresh=()=>void controller.refresh();\r\n save.addEventListener('click',onSave);refresh.addEventListener('click',onRefresh);\r\n eventTarget?.addEventListener('myr5:account-ready',onRefresh);\r\n eventTarget?.addEventListener('myr5:account-cleared',onRefresh);\r\n void controller.refresh();\r\n return {controller,dispose(){\r\n  unsubscribe();controller.dispose();\r\n  save.removeEventListener('click',onSave);refresh.removeEventListener('click',onRefresh);\r\n  eventTarget?.removeEventListener('myr5:account-ready',onRefresh);\r\n  eventTarget?.removeEventListener('myr5:account-cleared',onRefresh);root.remove();\r\n }};\r\n}\r\n","daily-guide.mjs":"// How to Play opens by itself once per local calendar day, the first time the app opens that day,\r\n// separately for each signed-in account and for the guest on this device.\r\nexport const DAILY_GUIDE_KEY='myr5-how-to-play-day-v1';\r\nexport const localDay=(date=new Date())=>`${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')}`;\r\nexport const guideOwner=(account=globalThis.myr5AuthenticatedAccount)=>{const id=account?.user?.id;return typeof id==='string'&&id?'account:'+id:'guest';};\r\nconst keyFor=owner=>`${DAILY_GUIDE_KEY}/${owner}`;\r\nexport function dailyGuideDue({owner=guideOwner(),date=new Date(),storage=globalThis.localStorage}={}){\r\n try{return storage.getItem(keyFor(owner))!==localDay(date);}catch{return false;}\r\n}\r\nexport function markDailyGuide({owner=guideOwner(),date=new Date(),storage=globalThis.localStorage}={}){\r\n try{storage.setItem(keyFor(owner),localDay(date));return true;}catch{return false;}\r\n}\r\n","db/schema.ts":"import {sql} from 'drizzle-orm';\r\nimport {sqliteTable,text,integer,real,index,uniqueIndex,primaryKey,check} from 'drizzle-orm/sqlite-core';\r\nexport const profiles=sqliteTable('profiles',{userId:text('user_id').primaryKey(),data:text('data').notNull().default('{}'),revision:integer('revision').notNull().default(0),updatedAt:integer('updated_at').notNull()});\r\nexport const workouts=sqliteTable('workouts',{\r\n id:text('id').primaryKey(),userId:text('user_id').notNull(),mode:text('mode').notNull(),goal:real('goal').notNull(),startedAt:integer('started_at').notNull(),completedAt:integer('completed_at'),value:real('value'),active:real('active'),\r\n source:text('source').notNull().default('server'),clientWorkoutId:text('client_workout_id'),competitiveStatus:text('competitive_status'),\r\n},t=>[\r\n index('workouts_user_completed').on(t.userId,t.completedAt),\r\n uniqueIndex('ux_workouts_acct_cwid').on(t.userId,t.clientWorkoutId).where(sql`${t.clientWorkoutId} IS NOT NULL`),\r\n index('ix_workouts_authority').on(t.userId,t.source,t.completedAt),\r\n index('ix_workouts_rank').on(t.userId,t.competitiveStatus,t.mode,t.completedAt),\r\n check('workouts_source',sql`${t.source} IN ('server','guest_import')`),\r\n check('workouts_competitive_status',sql`${t.competitiveStatus} IS NULL OR ${t.competitiveStatus} IN ('accepted','pending','not_eligible')`),\r\n]);\r\nexport const meals=sqliteTable('meals',{id:text('id').primaryKey(),userId:text('user_id').notNull(),name:text('name').notNull(),portion:text('portion').notNull(),calories:real('calories'),protein:real('protein'),carbs:real('carbs'),fat:real('fat'),micros:text('micros').notNull().default('{}'),nutritionSource:text('nutrition_source'),eatenAt:text('eaten_at').notNull(),createdAt:integer('created_at').notNull()},t=>[index('meals_user_date').on(t.userId,t.eatenAt)]);\r\nexport const reminders=sqliteTable('reminders',{id:text('id').primaryKey(),userId:text('user_id').notNull(),kind:text('kind').notNull(),time:text('time').notNull(),timezone:text('timezone').notNull(),enabled:integer('enabled').notNull().default(1),quietStart:text('quiet_start').notNull().default('22:00'),quietEnd:text('quiet_end').notNull().default('07:00'),tone:text('tone').notNull().default('direct'),daysPerWeek:integer('days_per_week').notNull().default(7)},t=>[index('reminders_user').on(t.userId)]);\r\nexport const subscriptions=sqliteTable('subscriptions',{endpoint:text('endpoint').primaryKey(),userId:text('user_id').notNull(),data:text('data').notNull(),createdAt:integer('created_at').notNull()},t=>[index('subscriptions_user').on(t.userId)]);\r\nexport const releasePushDeliveries=sqliteTable('release_push_deliveries',{endpoint:text('endpoint').notNull().references(()=>subscriptions.endpoint,{onDelete:'cascade'}),releaseId:text('release_id').notNull(),status:text('status').notNull(),updatedAt:integer('updated_at').notNull()},t=>[primaryKey({columns:[t.endpoint,t.releaseId]})]);\r\nexport const deliveries=sqliteTable('deliveries',{reminderId:text('reminder_id').notNull(),day:text('day').notNull(),endpoint:text('endpoint').notNull(),status:text('status').notNull(),updatedAt:integer('updated_at').notNull()},t=>[primaryKey({columns:[t.reminderId,t.day,t.endpoint]})]);\r\nexport const armieLetterDeliveries=sqliteTable('armie_letter_deliveries',{userId:text('user_id').notNull(),day:text('day').notNull(),letterId:text('letter_id').notNull(),sentAt:integer('sent_at').notNull()},t=>[primaryKey({columns:[t.userId,t.day,t.letterId]})]);\r\nexport const system=sqliteTable('system',{key:text('key').primaryKey(),value:text('value').notNull()});\r\nexport const loginDays=sqliteTable('login_days',{userId:text('user_id').notNull(),day:integer('day').notNull(),loggedAt:integer('logged_at').notNull()},t=>[primaryKey({columns:[t.userId,t.day]})]);\r\nexport const breathingSessions=sqliteTable('breathing_sessions',{id:text('id').primaryKey(),userId:text('user_id').notNull(),startedAt:integer('started_at').notNull(),completedAt:integer('completed_at')},t=>[index('breathing_user_started').on(t.userId,t.startedAt),index('breathing_user_completed').on(t.userId,t.completedAt)]);\r\nexport const scoreboardInvites=sqliteTable('scoreboard_invites',{ownerId:text('owner_id').primaryKey(),tokenHash:text('token_hash').notNull().unique(),createdAt:integer('created_at').notNull(),expiresAt:integer('expires_at').notNull()});\r\nexport const scoreboardLinks=sqliteTable('scoreboard_links',{id:text('id').primaryKey(),userA:text('user_a').notNull(),userB:text('user_b').notNull(),createdAt:integer('created_at').notNull()},t=>[index('scoreboard_links_a').on(t.userA),index('scoreboard_links_b').on(t.userB),uniqueIndex('scoreboard_links_pair').on(t.userA,t.userB)]);\r\nexport const onboarding=sqliteTable('onboarding',{userId:text('user_id').primaryKey(),data:text('data').notNull(),startDay:text('start_day').notNull(),completedAt:integer('completed_at').notNull(),updatedAt:integer('updated_at').notNull(),revision:integer('revision').notNull().default(1),writeToken:text('write_token').notNull()});\r\n\r\nexport const installDrafts=sqliteTable('install_drafts',{tokenHash:text('token_hash').primaryKey(),data:text('data').notNull(),creator:text('creator').notNull(),expiresAt:integer('expires_at').notNull()},t=>[index('install_drafts_expiry').on(t.expiresAt),index('install_drafts_creator').on(t.creator)]);\r\n\r\nexport const accountIdentities=sqliteTable('account_identities',{clerkId:text('clerk_id').primaryKey(),ownerId:text('owner_id').notNull().unique(),createdAt:integer('created_at').notNull()});\r\n\r\nexport const releaseSubscribers=sqliteTable('release_subscribers',{userId:text('user_id').primaryKey(),email:text('email').notNull(),enabled:integer('enabled').notNull().default(0),confirmHash:text('confirm_hash'),expiresAt:integer('expires_at'),unsubscribeToken:text('unsubscribe_token').notNull().unique(),updatedAt:integer('updated_at').notNull(),confirmedAt:integer('confirmed_at'),lastRelease:text('last_release')},t=>[index('release_subscribers_confirmation').on(t.confirmHash)]);\r\nexport const releaseDeliveries=sqliteTable('release_deliveries',{userId:text('user_id').notNull(),releaseId:text('release_id').notNull(),status:text('status').notNull(),updatedAt:integer('updated_at').notNull()},t=>[primaryKey({columns:[t.userId,t.releaseId]})]);\r\nexport const galaRuns=sqliteTable('gala_runs',{id:text('id').primaryKey(),tokenHash:text('token_hash').notNull(),creator:text('creator').notNull(),startedAt:integer('started_at').notNull(),djName:text('dj_name'),moniker:text('moniker'),djAt:integer('dj_at'),galaAt:integer('gala_at'),lilAt:integer('lil_at'),corgiAt:integer('corgi_at'),handAt:integer('hand_at'),armieAt:integer('armie_at'),completedAt:integer('completed_at'),durationMs:integer('duration_ms'),installedAt:integer('installed_at'),joinedAt:integer('joined_at'),publicName:text('public_name').unique()},t=>[index('gala_runs_creator_started').on(t.creator,t.startedAt),index('gala_runs_leaderboard').on(t.joinedAt,t.durationMs)]);\r\nexport const coachArmyCompletions=sqliteTable('coach_army_completions',{dataEpoch:integer('data_epoch').notNull().default(1),eventId:text('event_id').primaryKey(),userId:text('user_id').notNull(),completedAt:integer('completed_at').notNull(),receivedAt:integer('received_at').notNull()},t=>[index('coach_army_completions_user').on(t.userId)]);\r\nexport const accountEntitlements=sqliteTable('account_entitlements',{dataEpoch:integer('data_epoch').notNull().default(1),userId:text('user_id').primaryKey(),coachArmyStatus:text('coach_army_status').notNull().default('locked'),coachArmyCompletedAt:integer('coach_army_completed_at'),coachArmyEventId:text('coach_army_event_id'),updatedAt:integer('updated_at').notNull()});\r\nexport const accountPackEntitlements=sqliteTable('account_pack_entitlements',{userId:text('user_id').notNull(),packId:text('pack_id').notNull(),status:text('status').notNull(),grantedAt:integer('granted_at').notNull()},t=>[primaryKey({columns:[t.userId,t.packId]}),index('account_pack_entitlements_user').on(t.userId)]);\r\nexport const coachArmyRuns=sqliteTable('coach_army_runs',{dataEpoch:integer('data_epoch').notNull().default(1),id:text('id').primaryKey(),accountId:text('account_id').notNull(),startedAt:integer('started_at').notNull(),completedAt:integer('completed_at'),completionEventId:text('completion_event_id'),updatedAt:integer('updated_at').notNull()},t=>[index('coach_army_runs_account').on(t.accountId,t.startedAt)]);\r\nexport const coachArmyRunEvents=sqliteTable('coach_army_run_events',{dataEpoch:integer('data_epoch').notNull().default(1),eventId:text('event_id').primaryKey(),runId:text('run_id').notNull(),accountId:text('account_id').notNull(),sequence:integer('sequence').notNull(),stage:text('stage').notNull(),source:text('source').notNull(),receivedAt:integer('received_at').notNull()},t=>[index('coach_army_run_events_run').on(t.runId,t.sequence),uniqueIndex('coach_army_run_sequence').on(t.runId,t.sequence)]);\r\nexport const coachArmyCompletionOutbox=sqliteTable('coach_army_completion_outbox',{dataEpoch:integer('data_epoch').notNull().default(1),eventId:text('event_id').primaryKey(),runId:text('run_id').notNull(),accountId:text('account_id').notNull(),completedAt:integer('completed_at').notNull(),status:text('status').notNull().default('pending'),attempts:integer('attempts').notNull().default(0),lastError:text('last_error'),deliveredAt:integer('delivered_at'),updatedAt:integer('updated_at').notNull()},t=>[index('coach_army_completion_outbox_pending').on(t.status,t.updatedAt)]);\r\nexport const coachArmyDjscratchChallenges=sqliteTable('coach_army_djscratch_challenges',{dataEpoch:integer('data_epoch').notNull().default(1),id:text('id').primaryKey(),runId:text('run_id').notNull(),accountId:text('account_id').notNull(),expiresAt:integer('expires_at').notNull(),nextStep:integer('next_step').notNull().default(0),status:text('status').notNull().default('active'),createdAt:integer('created_at').notNull(),completedAt:integer('completed_at'),updatedAt:integer('updated_at').notNull()},t=>[index('coach_army_djscratch_challenges_run').on(t.runId,t.accountId,t.status)]);\r\nexport const goals=sqliteTable('goals',{id:text('id').primaryKey(),userId:text('user_id').notNull(),title:text('title').notNull(),note:text('note').notNull().default(''),status:text('status').notNull().default('active'),createdAt:integer('created_at').notNull(),updatedAt:integer('updated_at').notNull()},t=>[index('goals_user_updated').on(t.userId,t.updatedAt)]);\r\nexport const warRoomArsenals=sqliteTable('war_room_arsenals',{userId:text('user_id').primaryKey(),loadout:text('loadout').notNull(),recipes:text('recipes').notNull().default('[]'),revision:integer('revision').notNull().default(0),updatedAt:integer('updated_at').notNull()});\r\n\r\n// Retained account generation and deletion receipts fence delayed requests.\r\nexport const accountDataEpochs=sqliteTable('account_data_epochs',{\r\n ownerId:text('owner_id').primaryKey().notNull(),\r\n epoch:integer('epoch').notNull().default(1),\r\n updatedAt:integer('updated_at').notNull(),\r\n},t=>[check('account_data_epochs_epoch',sql`typeof(${t.epoch}) = 'integer' AND ${t.epoch} BETWEEN 1 AND 9007199254740991`),check('account_data_epochs_updated',sql`typeof(${t.updatedAt}) = 'integer' AND ${t.updatedAt} >= 0`)]);\r\nexport const accountDataDeletions=sqliteTable('account_data_deletions',{\r\n ownerId:text('owner_id').notNull().references(()=>accountDataEpochs.ownerId),\r\n deletedEpoch:integer('deleted_epoch').notNull(),\r\n deletedAt:integer('deleted_at').notNull(),\r\n},t=>[primaryKey({columns:[t.ownerId,t.deletedEpoch]}),check('account_data_deletions_epoch',sql`typeof(${t.deletedEpoch}) = 'integer' AND ${t.deletedEpoch} BETWEEN 1 AND 9007199254740990`),check('account_data_deletions_time',sql`typeof(${t.deletedAt}) = 'integer' AND ${t.deletedAt} >= 0`)]);\r\n\r\nexport const workoutImports=sqliteTable('workout_imports',{\r\n workoutId:text('workout_id').primaryKey().notNull().references(()=>workouts.id,{onDelete:'cascade'}),\r\n idempotencyKey:text('idempotency_key').notNull(),fingerprint:text('fingerprint').notNull(),digestVersion:integer('digest_version').notNull(),accountDataEpoch:integer('account_data_epoch').notNull(),createdAt:integer('created_at').notNull(),\r\n},t=>[\r\n check('workout_imports_key',sql`length(${t.idempotencyKey})=64 AND ${t.idempotencyKey} NOT GLOB '*[^0-9a-f]*'`),\r\n check('workout_imports_fingerprint',sql`length(${t.fingerprint})=64 AND ${t.fingerprint} NOT GLOB '*[^0-9a-f]*'`),\r\n check('workout_imports_version',sql`typeof(${t.digestVersion})='integer' AND ${t.digestVersion} BETWEEN 1 AND 9007199254740991`),\r\n check('workout_imports_epoch',sql`typeof(${t.accountDataEpoch})='integer' AND ${t.accountDataEpoch} BETWEEN 1 AND 9007199254740991`),\r\n check('workout_imports_created',sql`typeof(${t.createdAt})='integer' AND ${t.createdAt} BETWEEN 0 AND 9007199254740991`),\r\n]);\r\nexport const competitiveDecisions=sqliteTable('competitive_decisions',{\r\n workoutId:text('workout_id').notNull().references(()=>workouts.id,{onDelete:'cascade'}),seq:integer('seq').notNull(),status:text('status').notNull(),reason:text('reason').notNull(),createdAt:integer('created_at').notNull(),\r\n},t=>[\r\n primaryKey({columns:[t.workoutId,t.seq]}),\r\n check('competitive_decisions_seq',sql`typeof(${t.seq})='integer' AND ${t.seq} BETWEEN 1 AND 9007199254740991`),\r\n check('competitive_decisions_status',sql`${t.status} IN ('accepted','pending','not_eligible')`),\r\n check('competitive_decisions_reason',sql`length(${t.reason}) BETWEEN 1 AND 128`),\r\n check('competitive_decisions_created',sql`typeof(${t.createdAt})='integer' AND ${t.createdAt} BETWEEN 0 AND 9007199254740991`),\r\n]);\r\n\r\nexport const coachArmyBindings=sqliteTable('coach_army_bindings',{dataEpoch:integer('data_epoch').notNull().default(1),bindingId:text('binding_id').primaryKey().notNull(),runId:text('run_id').notNull(),accountId:text('account_id').notNull(),eventId:text('event_id').notNull().unique(),claimHash:text('claim_hash').notNull().unique(),completedAt:integer('completed_at').notNull(),expiresAt:integer('expires_at').notNull(),claimedAt:integer('claimed_at'),createdAt:integer('created_at').notNull()},t=>[index('coach_army_bindings_account').on(t.accountId,t.expiresAt)]);\r\n","demo-poses.mjs":"// Small procedural teaching figures. Coordinates are metres, y points up.\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {applyExerciseDemo} from './exercise-demo.mjs';\r\nexport function demoPose(mode,t=0){\r\n const p={head:[0,.85,0],neck:[0,.65,0],ls:[-.23,.57,0],rs:[.23,.57,0],le:[-.31,.18,0],re:[.31,.18,0],lw:[-.3,-.13,0],rw:[.3,-.13,0],lh:[-.15,0,0],rh:[.15,0,0],lk:[-.17,-.46,0],rk:[.17,-.46,0],la:[-.17,-.95,0],ra:[.17,-.95,0]};\r\n const wave=Math.sin(t*4),rise=Math.max(0,Math.sin(t*3.6));\r\n if(mode==='tree'){p.lk=[-.52,-.2,0];p.la=[.12,-.38,0];p.le=[-.3,.32,0];p.re=[.3,.32,0];p.lw=[-.025,.48,.08];p.rw=[.025,.48,.08];}\r\n if(mode==='warrior'){p.lk=[-.65,-.4,0];p.la=[-.65,-.95,0];p.rk=[.46,-.43,0];p.ra=[.82,-.95,0];p.le=[-.58,.57,0];p.re=[.58,.57,0];p.lw=[-.94,.57,0];p.rw=[.94,.57,0];}\r\n if(mode==='horse'){p.lk=[-.53,-.25,0];p.rk=[.53,-.25,0];p.la=[-.53,-.79,0];p.ra=[.53,-.79,0];p.le=[-.3,.24,.05];p.re=[.3,.24,.05];p.lw=[-.03,.42,.15];p.rw=[.03,.42,.15];for(const v of Object.values(p))v[1]-=.15;}\r\n if(mode==='boxing'){for(const [side,sign,phase] of [['l',-1,wave],['r',1,-wave]]){const punch=Math.max(0,phase);p[side+'e']=[sign*.28,.37+punch*.16,.2+punch*.22];p[side+'w']=[sign*.2,.55,.25+punch*.65];}p.la=[-.32,-.95,.1];p.ra=[.32,-.95,-.17];}\r\n if(mode==='jogging'){for(const [side,sign,phase] of [['l',-1,wave],['r',1,-wave]]){const lift=Math.max(0,phase);p[side+'k']=[sign*.17,-.46+lift*.4,lift*.36];p[side+'a']=[sign*.17,-.95+lift*.42,.06];p[side+'e']=[sign*.32,.22,-phase*.18];p[side+'w']=[sign*.27,.42,-phase*.32];}for(const v of Object.values(p))v[1]+=.035*Math.abs(wave);}\r\n if(mode==='jumping'){for(const v of Object.values(p))v[1]+=.3*rise;p.le[0]-=.1*rise;p.re[0]+=.1*rise;p.lw[1]+=.45*rise;p.rw[1]+=.45*rise;}\r\n return EXERCISES[mode]?applyExerciseDemo(p,EXERCISES[mode],t):p;\r\n}\r\nexport const DEMO_BONES=[['head','neck'],['ls','rs'],['neck','lh'],['neck','rh'],['lh','rh'],['ls','le'],['le','lw'],['rs','re'],['re','rw'],['lh','lk'],['lk','la'],['rh','rk'],['rk','ra']];\r\n","device-notifications.mjs":"// A browser subscription alone does not prove it belongs to the signed-in account.\r\nexport function notificationBinding(api){\r\n let binding=null,checkedAt=0;\r\n return {\r\n  ready(user){return !!user&&binding?.user===user&&Date.now()-checkedAt<90000;},\r\n  forget(){binding=null;checkedAt=0;},\r\n  async verify(account,subscription){\r\n   if(!account?.onboarding||!subscription){this.forget();return false;}\r\n   const user=account.user.id,endpoint=subscription.endpoint;\r\n   if(binding?.user===user&&binding.endpoint===endpoint&&Date.now()-checkedAt<60000)return true;\r\n   this.forget();await api('/api/push/subscribe','POST',subscription.toJSON());binding={user,endpoint};checkedAt=Date.now();return true;\r\n  }\r\n };\r\n}\r\n","drizzle.config.ts":"import {defineConfig} from 'drizzle-kit';\r\nexport default defineConfig({schema:'./db/schema.ts',out:'./drizzle',dialect:'sqlite'});\r\n","drizzle/0000_new_cammi.sql":"CREATE TABLE `deliveries` (\r\n\t`reminder_id` text NOT NULL,\r\n\t`day` text NOT NULL,\r\n\t`endpoint` text NOT NULL,\r\n\t`status` text NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\tPRIMARY KEY(`reminder_id`, `day`, `endpoint`)\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `meals` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`user_id` text NOT NULL,\r\n\t`name` text NOT NULL,\r\n\t`portion` text NOT NULL,\r\n\t`calories` real,\r\n\t`protein` real,\r\n\t`carbs` real,\r\n\t`fat` real,\r\n\t`eaten_at` text NOT NULL,\r\n\t`created_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `meals_user_date` ON `meals` (`user_id`,`eaten_at`);--> statement-breakpoint\r\nCREATE TABLE `profiles` (\r\n\t`user_id` text PRIMARY KEY NOT NULL,\r\n\t`data` text DEFAULT '{}' NOT NULL,\r\n\t`revision` integer DEFAULT 0 NOT NULL,\r\n\t`updated_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `reminders` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`user_id` text NOT NULL,\r\n\t`kind` text NOT NULL,\r\n\t`time` text NOT NULL,\r\n\t`timezone` text NOT NULL,\r\n\t`enabled` integer DEFAULT 1 NOT NULL,\r\n\t`quiet_start` text DEFAULT '22:00' NOT NULL,\r\n\t`quiet_end` text DEFAULT '07:00' NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `reminders_user` ON `reminders` (`user_id`);--> statement-breakpoint\r\nCREATE TABLE `subscriptions` (\r\n\t`endpoint` text PRIMARY KEY NOT NULL,\r\n\t`user_id` text NOT NULL,\r\n\t`data` text NOT NULL,\r\n\t`created_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `subscriptions_user` ON `subscriptions` (`user_id`);--> statement-breakpoint\r\nCREATE TABLE `system` (\r\n\t`key` text PRIMARY KEY NOT NULL,\r\n\t`value` text NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `workouts` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`user_id` text NOT NULL,\r\n\t`mode` text NOT NULL,\r\n\t`goal` real NOT NULL,\r\n\t`started_at` integer NOT NULL,\r\n\t`completed_at` integer,\r\n\t`value` real,\r\n\t`active` real\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `workouts_user_completed` ON `workouts` (`user_id`,`completed_at`);","drizzle/0001_careful_impossible_man.sql":"ALTER TABLE `reminders` ADD `tone` text DEFAULT 'direct' NOT NULL;--> statement-breakpoint\r\nALTER TABLE `reminders` ADD `days_per_week` integer DEFAULT 7 NOT NULL;","drizzle/0002_peaceful_living_lightning.sql":"CREATE TABLE `onboarding` (\r\n\t`user_id` text PRIMARY KEY NOT NULL,\r\n\t`data` text NOT NULL,\r\n\t`start_day` text NOT NULL,\r\n\t`completed_at` integer NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\t`revision` integer DEFAULT 1 NOT NULL,\r\n\t`write_token` text NOT NULL\r\n);\r\n","drizzle/0003_dashing_meltdown.sql":"CREATE TABLE `install_drafts` (\r\n\t`token_hash` text PRIMARY KEY NOT NULL,\r\n\t`data` text NOT NULL,\r\n\t`creator` text NOT NULL,\r\n\t`expires_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `install_drafts_expiry` ON `install_drafts` (`expires_at`);--> statement-breakpoint\r\nCREATE INDEX `install_drafts_creator` ON `install_drafts` (`creator`);","drizzle/0004_burly_boomerang.sql":"ALTER TABLE `meals` ADD `micros` text DEFAULT '{}' NOT NULL;--> statement-breakpoint\r\nALTER TABLE `meals` ADD `nutrition_source` text;","drizzle/0005_nice_the_enforcers.sql":"CREATE TABLE `account_identities` (\r\n\t`clerk_id` text PRIMARY KEY NOT NULL,\r\n\t`owner_id` text NOT NULL,\r\n\t`created_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX `account_identities_owner_id_unique` ON `account_identities` (`owner_id`);","drizzle/0006_flawless_slayback.sql":"CREATE TABLE `release_deliveries` (\r\n\t`user_id` text NOT NULL,\r\n\t`release_id` text NOT NULL,\r\n\t`status` text NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\tPRIMARY KEY(`user_id`, `release_id`)\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `release_subscribers` (\r\n\t`user_id` text PRIMARY KEY NOT NULL,\r\n\t`email` text NOT NULL,\r\n\t`enabled` integer DEFAULT 0 NOT NULL,\r\n\t`confirm_hash` text,\r\n\t`expires_at` integer,\r\n\t`unsubscribe_token` text NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\t`confirmed_at` integer,\r\n\t`last_release` text\r\n);\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX `release_subscribers_unsubscribe_token_unique` ON `release_subscribers` (`unsubscribe_token`);--> statement-breakpoint\r\nCREATE INDEX `release_subscribers_confirmation` ON `release_subscribers` (`confirm_hash`);","drizzle/0007_tan_mesmero.sql":"CREATE TABLE `gala_runs` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`token_hash` text NOT NULL,\r\n\t`creator` text NOT NULL,\r\n\t`started_at` integer NOT NULL,\r\n\t`dj_name` text,\r\n\t`moniker` text,\r\n\t`dj_at` integer,\r\n\t`gala_at` integer,\r\n\t`lil_at` integer,\r\n\t`corgi_at` integer,\r\n\t`hand_at` integer,\r\n\t`armie_at` integer,\r\n\t`completed_at` integer,\r\n\t`duration_ms` integer,\r\n\t`installed_at` integer,\r\n\t`joined_at` integer,\r\n\t`public_name` text\r\n);\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX `gala_runs_public_name_unique` ON `gala_runs` (`public_name`);--> statement-breakpoint\r\nCREATE INDEX `gala_runs_creator_started` ON `gala_runs` (`creator`,`started_at`);--> statement-breakpoint\r\nCREATE INDEX `gala_runs_leaderboard` ON `gala_runs` (`joined_at`,`duration_ms`);","drizzle/0008_giant_charles_xavier.sql":"CREATE TABLE `scoreboard_invites` (\r\n\t`owner_id` text PRIMARY KEY NOT NULL,\r\n\t`token_hash` text NOT NULL,\r\n\t`created_at` integer NOT NULL,\r\n\t`expires_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX `scoreboard_invites_token_hash_unique` ON `scoreboard_invites` (`token_hash`);--> statement-breakpoint\r\nCREATE TABLE `scoreboard_links` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`user_a` text NOT NULL,\r\n\t`user_b` text NOT NULL,\r\n\t`created_at` integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `scoreboard_links_a` ON `scoreboard_links` (`user_a`);--> statement-breakpoint\r\nCREATE INDEX `scoreboard_links_b` ON `scoreboard_links` (`user_b`);--> statement-breakpoint\r\nCREATE UNIQUE INDEX `scoreboard_links_pair` ON `scoreboard_links` (`user_a`,`user_b`);","drizzle/0009_uneven_rhino.sql":"CREATE TABLE `breathing_sessions` (\r\n\t`id` text PRIMARY KEY NOT NULL,\r\n\t`user_id` text NOT NULL,\r\n\t`started_at` integer NOT NULL,\r\n\t`completed_at` integer\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX `breathing_user_started` ON `breathing_sessions` (`user_id`,`started_at`);--> statement-breakpoint\r\nCREATE INDEX `breathing_user_completed` ON `breathing_sessions` (`user_id`,`completed_at`);--> statement-breakpoint\r\nCREATE TABLE `login_days` (\r\n\t`user_id` text NOT NULL,\r\n\t`day` integer NOT NULL,\r\n\t`logged_at` integer NOT NULL,\r\n\tPRIMARY KEY(`user_id`, `day`)\r\n);\r\n","drizzle/0010_fixed_wrecker.sql":"CREATE TABLE `release_push_deliveries` (\r\n\t`endpoint` text NOT NULL,\r\n\t`release_id` text NOT NULL,\r\n\t`status` text NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\tPRIMARY KEY(`endpoint`, `release_id`),\r\n\tFOREIGN KEY (`endpoint`) REFERENCES `subscriptions`(`endpoint`) ON UPDATE no action ON DELETE cascade\r\n);\r\n","drizzle/0011_coach_army_entitlements.sql":"CREATE TABLE `coach_army_completions` (`event_id` text PRIMARY KEY NOT NULL, `user_id` text NOT NULL, `completed_at` integer NOT NULL, `received_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_completions_user` ON `coach_army_completions` (`user_id`);\r\n--> statement-breakpoint\r\nCREATE TABLE `account_entitlements` (`user_id` text PRIMARY KEY NOT NULL, `coach_army_status` text NOT NULL DEFAULT 'locked', `coach_army_completed_at` integer, `coach_army_event_id` text, `updated_at` integer NOT NULL);\r\n","drizzle/0012_coach_army_bindings.sql":"CREATE TABLE `coach_army_bindings` (`binding_id` text PRIMARY KEY NOT NULL, `run_id` text NOT NULL, `account_id` text NOT NULL, `event_id` text NOT NULL UNIQUE, `claim_hash` text NOT NULL UNIQUE, `completed_at` integer NOT NULL, `expires_at` integer NOT NULL, `claimed_at` integer, `created_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_bindings_account` ON `coach_army_bindings` (`account_id`, `expires_at`);\r\n","drizzle/0013_account_pack_entitlements.sql":"CREATE TABLE `account_pack_entitlements` (`user_id` text NOT NULL, `pack_id` text NOT NULL, `status` text NOT NULL, `granted_at` integer NOT NULL, PRIMARY KEY(`user_id`, `pack_id`));\r\n--> statement-breakpoint\r\nCREATE INDEX `account_pack_entitlements_user` ON `account_pack_entitlements` (`user_id`);\r\n","drizzle/0014_coach_army_authenticated_runs.sql":"CREATE TABLE `coach_army_runs` (`id` text PRIMARY KEY NOT NULL, `account_id` text NOT NULL, `started_at` integer NOT NULL, `completed_at` integer, `completion_event_id` text, `updated_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_runs_account` ON `coach_army_runs` (`account_id`, `started_at`);\r\n--> statement-breakpoint\r\nCREATE TABLE `coach_army_run_events` (`event_id` text PRIMARY KEY NOT NULL, `run_id` text NOT NULL, `account_id` text NOT NULL, `sequence` integer NOT NULL, `stage` text NOT NULL, `source` text NOT NULL, `received_at` integer NOT NULL, UNIQUE(`run_id`, `sequence`));\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_run_events_run` ON `coach_army_run_events` (`run_id`, `sequence`);\r\n--> statement-breakpoint\r\nCREATE TABLE `coach_army_completion_outbox` (`event_id` text PRIMARY KEY NOT NULL, `run_id` text NOT NULL, `account_id` text NOT NULL, `completed_at` integer NOT NULL, `status` text NOT NULL DEFAULT 'pending', `attempts` integer NOT NULL DEFAULT 0, `last_error` text, `delivered_at` integer, `updated_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_completion_outbox_pending` ON `coach_army_completion_outbox` (`status`, `updated_at`);\r\n","drizzle/0015_goals.sql":"CREATE TABLE `goals` (`id` text PRIMARY KEY NOT NULL, `user_id` text NOT NULL, `title` text NOT NULL, `note` text NOT NULL DEFAULT '', `status` text NOT NULL DEFAULT 'active', `created_at` integer NOT NULL, `updated_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `goals_user_updated` ON `goals` (`user_id`,`updated_at`);\r\n","drizzle/0016_war_room_arsenal.sql":"CREATE TABLE `war_room_arsenals` (`user_id` text PRIMARY KEY NOT NULL, `loadout` text NOT NULL, `recipes` text NOT NULL DEFAULT '[]', `revision` integer NOT NULL DEFAULT 0, `updated_at` integer NOT NULL);\r\n","drizzle/0017_coach_army_djscratch_verifier.sql":"CREATE TABLE `coach_army_djscratch_challenges` (`id` text PRIMARY KEY NOT NULL, `run_id` text NOT NULL, `account_id` text NOT NULL, `expires_at` integer NOT NULL, `next_step` integer NOT NULL DEFAULT 0, `status` text NOT NULL DEFAULT 'active', `created_at` integer NOT NULL, `completed_at` integer, `updated_at` integer NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX `coach_army_djscratch_challenges_run` ON `coach_army_djscratch_challenges` (`run_id`, `account_id`, `status`);\r\n","drizzle/0018_account_data_epochs.sql":"CREATE TABLE `account_data_epochs` (\r\n\t`owner_id` text PRIMARY KEY NOT NULL,\r\n\t`epoch` integer DEFAULT 1 NOT NULL,\r\n\t`updated_at` integer NOT NULL,\r\n\tCONSTRAINT \"account_data_epochs_epoch\" CHECK(typeof(\"account_data_epochs\".\"epoch\") = 'integer' AND \"account_data_epochs\".\"epoch\" BETWEEN 1 AND 9007199254740991),\r\n\tCONSTRAINT \"account_data_epochs_updated\" CHECK(typeof(\"account_data_epochs\".\"updated_at\") = 'integer' AND \"account_data_epochs\".\"updated_at\" >= 0)\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `account_data_deletions` (\r\n\t`owner_id` text NOT NULL,\r\n\t`deleted_epoch` integer NOT NULL,\r\n\t`deleted_at` integer NOT NULL,\r\n\tPRIMARY KEY(`owner_id`, `deleted_epoch`),\r\n\tFOREIGN KEY (`owner_id`) REFERENCES `account_data_epochs`(`owner_id`) ON UPDATE no action ON DELETE no action,\r\n\tCONSTRAINT \"account_data_deletions_epoch\" CHECK(typeof(\"account_data_deletions\".\"deleted_epoch\") = 'integer' AND \"account_data_deletions\".\"deleted_epoch\" BETWEEN 1 AND 9007199254740990),\r\n\tCONSTRAINT \"account_data_deletions_time\" CHECK(typeof(\"account_data_deletions\".\"deleted_at\") = 'integer' AND \"account_data_deletions\".\"deleted_at\" >= 0)\r\n);\r\n","drizzle/0019_workout_import_trust.sql":"CREATE TABLE `competitive_decisions` (\r\n\t`workout_id` text NOT NULL,\r\n\t`seq` integer NOT NULL,\r\n\t`status` text NOT NULL,\r\n\t`reason` text NOT NULL,\r\n\t`created_at` integer NOT NULL,\r\n\tPRIMARY KEY(`workout_id`, `seq`),\r\n\tFOREIGN KEY (`workout_id`) REFERENCES `workouts`(`id`) ON UPDATE no action ON DELETE cascade,\r\n\tCONSTRAINT \"competitive_decisions_seq\" CHECK(typeof(\"competitive_decisions\".\"seq\")='integer' AND \"competitive_decisions\".\"seq\" BETWEEN 1 AND 9007199254740991),\r\n\tCONSTRAINT \"competitive_decisions_status\" CHECK(\"competitive_decisions\".\"status\" IN ('accepted','pending','not_eligible')),\r\n\tCONSTRAINT \"competitive_decisions_reason\" CHECK(length(\"competitive_decisions\".\"reason\") BETWEEN 1 AND 128),\r\n\tCONSTRAINT \"competitive_decisions_created\" CHECK(typeof(\"competitive_decisions\".\"created_at\")='integer' AND \"competitive_decisions\".\"created_at\" BETWEEN 0 AND 9007199254740991)\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE `workout_imports` (\r\n\t`workout_id` text PRIMARY KEY NOT NULL,\r\n\t`idempotency_key` text NOT NULL,\r\n\t`fingerprint` text NOT NULL,\r\n\t`digest_version` integer NOT NULL,\r\n\t`account_data_epoch` integer NOT NULL,\r\n\t`created_at` integer NOT NULL,\r\n\tFOREIGN KEY (`workout_id`) REFERENCES `workouts`(`id`) ON UPDATE no action ON DELETE cascade,\r\n\tCONSTRAINT \"workout_imports_key\" CHECK(length(\"workout_imports\".\"idempotency_key\")=64 AND \"workout_imports\".\"idempotency_key\" NOT GLOB '*[^0-9a-f]*'),\r\n\tCONSTRAINT \"workout_imports_fingerprint\" CHECK(length(\"workout_imports\".\"fingerprint\")=64 AND \"workout_imports\".\"fingerprint\" NOT GLOB '*[^0-9a-f]*'),\r\n\tCONSTRAINT \"workout_imports_version\" CHECK(typeof(\"workout_imports\".\"digest_version\")='integer' AND \"workout_imports\".\"digest_version\" BETWEEN 1 AND 9007199254740991),\r\n\tCONSTRAINT \"workout_imports_epoch\" CHECK(typeof(\"workout_imports\".\"account_data_epoch\")='integer' AND \"workout_imports\".\"account_data_epoch\" BETWEEN 1 AND 9007199254740991),\r\n\tCONSTRAINT \"workout_imports_created\" CHECK(typeof(\"workout_imports\".\"created_at\")='integer' AND \"workout_imports\".\"created_at\" BETWEEN 0 AND 9007199254740991)\r\n);\r\n--> statement-breakpoint\r\nALTER TABLE workouts ADD COLUMN source TEXT NOT NULL DEFAULT 'server' CONSTRAINT workouts_source CHECK(source IN ('server','guest_import'));\r\n--> statement-breakpoint\r\nALTER TABLE workouts ADD COLUMN client_workout_id TEXT;\r\n--> statement-breakpoint\r\nALTER TABLE workouts ADD COLUMN competitive_status TEXT CONSTRAINT workouts_competitive_status CHECK(competitive_status IS NULL OR competitive_status IN ('accepted','pending','not_eligible'));\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX `ux_workouts_acct_cwid` ON `workouts` (`user_id`,`client_workout_id`) WHERE \"workouts\".\"client_workout_id\" IS NOT NULL;--> statement-breakpoint\r\nCREATE INDEX `ix_workouts_authority` ON `workouts` (`user_id`,`source`,`completed_at`);--> statement-breakpoint\r\nCREATE INDEX `ix_workouts_rank` ON `workouts` (`user_id`,`competitive_status`,`mode`,`completed_at`);\r\n--> statement-breakpoint\r\nCREATE TRIGGER workouts_status_insert_guard BEFORE INSERT ON workouts\r\nWHEN NEW.competitive_status IS NOT NULL OR EXISTS(SELECT 1 FROM workouts WHERE id=NEW.id)\r\nBEGIN SELECT RAISE(ABORT,'workout_insert_authority'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workouts_source_insert_guard BEFORE INSERT ON workouts\r\nWHEN (NEW.source='server' AND NEW.client_workout_id IS NOT NULL)\r\n OR (NEW.source='guest_import' AND (NEW.client_workout_id IS NULL OR length(NEW.client_workout_id)=0 OR NEW.completed_at IS NULL))\r\nBEGIN SELECT RAISE(ABORT,'workout_source_shape'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workouts_authority_immutable BEFORE UPDATE ON workouts\r\nWHEN NEW.source IS NOT OLD.source OR NEW.client_workout_id IS NOT OLD.client_workout_id\r\n OR NEW.id IS NOT OLD.id OR NEW.user_id IS NOT OLD.user_id\r\nBEGIN SELECT RAISE(ABORT,'workout_authority_immutable'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workouts_completion_immutable BEFORE UPDATE ON workouts\r\nWHEN OLD.completed_at IS NOT NULL AND (NEW.completed_at IS NOT OLD.completed_at OR NEW.mode IS NOT OLD.mode\r\n OR NEW.goal IS NOT OLD.goal OR NEW.started_at IS NOT OLD.started_at OR NEW.value IS NOT OLD.value OR NEW.active IS NOT OLD.active)\r\nBEGIN SELECT RAISE(ABORT,'workout_completion_immutable'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER cd_no_update BEFORE UPDATE ON competitive_decisions\r\nBEGIN SELECT RAISE(ABORT,'append_only'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER cd_no_delete BEFORE DELETE ON competitive_decisions\r\nWHEN EXISTS(SELECT 1 FROM workouts WHERE id=OLD.workout_id)\r\nBEGIN SELECT RAISE(ABORT,'append_only'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER cd_insert_guard BEFORE INSERT ON competitive_decisions\r\nWHEN NOT EXISTS(SELECT 1 FROM workouts WHERE id=NEW.workout_id AND completed_at IS NOT NULL)\r\n OR NEW.seq != COALESCE((SELECT MAX(seq)+1 FROM competitive_decisions WHERE workout_id=NEW.workout_id),1)\r\n OR EXISTS(SELECT 1 FROM competitive_decisions WHERE workout_id=NEW.workout_id AND status='not_eligible')\r\n OR (EXISTS(SELECT 1 FROM workouts WHERE id=NEW.workout_id AND source='guest_import') AND NEW.status!='not_eligible')\r\nBEGIN SELECT RAISE(ABORT,'invalid_competitive_decision'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER w_status_guard BEFORE UPDATE OF competitive_status ON workouts\r\nWHEN NEW.competitive_status IS NOT (SELECT status FROM competitive_decisions WHERE workout_id=NEW.id ORDER BY seq DESC LIMIT 1)\r\nBEGIN SELECT RAISE(ABORT,'competitive_cache_guard'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER cd_sync AFTER INSERT ON competitive_decisions\r\nBEGIN UPDATE workouts SET competitive_status=NEW.status WHERE id=NEW.workout_id; END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_server_completed_insert AFTER INSERT ON workouts\r\nWHEN NEW.source='server' AND NEW.completed_at IS NOT NULL\r\nBEGIN INSERT INTO competitive_decisions(workout_id,seq,status,reason,created_at) VALUES(NEW.id,1,'pending','server_completion',NEW.completed_at); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_server_completed_update AFTER UPDATE OF completed_at ON workouts\r\nWHEN OLD.completed_at IS NULL AND NEW.completed_at IS NOT NULL AND NEW.source='server'\r\nBEGIN INSERT INTO competitive_decisions(workout_id,seq,status,reason,created_at) VALUES(NEW.id,1,'pending','server_completion',NEW.completed_at); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_guest_completed_insert AFTER INSERT ON workouts\r\nWHEN NEW.source='guest_import'\r\nBEGIN INSERT INTO competitive_decisions(workout_id,seq,status,reason,created_at) VALUES(NEW.id,1,'not_eligible','guest_import',NEW.completed_at); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_imports_insert_guard BEFORE INSERT ON workout_imports\r\nWHEN NOT EXISTS(SELECT 1 FROM workouts WHERE id=NEW.workout_id AND source='guest_import' AND completed_at IS NOT NULL)\r\n OR EXISTS(SELECT 1 FROM workout_imports WHERE workout_id=NEW.workout_id)\r\n OR NEW.account_data_epoch IS NOT COALESCE((SELECT e.epoch FROM account_data_epochs e JOIN workouts w ON w.user_id=e.owner_id WHERE w.id=NEW.workout_id),1)\r\nBEGIN SELECT RAISE(ABORT,'invalid_workout_import'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_imports_no_update BEFORE UPDATE ON workout_imports\r\nBEGIN SELECT RAISE(ABORT,'immutable_import'); END;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workout_imports_no_delete BEFORE DELETE ON workout_imports\r\nWHEN EXISTS(SELECT 1 FROM workouts WHERE id=OLD.workout_id)\r\nBEGIN SELECT RAISE(ABORT,'immutable_import'); END;\r\n--> statement-breakpoint\r\nINSERT INTO competitive_decisions(workout_id,seq,status,reason,created_at)\r\nSELECT w.id,1,'pending','legacy_server_completion',CASE WHEN typeof(w.completed_at)='integer' AND w.completed_at BETWEEN 0 AND 9007199254740991 THEN w.completed_at ELSE 0 END FROM workouts w\r\nWHERE w.source='server' AND w.completed_at IS NOT NULL\r\nAND NOT EXISTS(SELECT 1 FROM competitive_decisions d WHERE d.workout_id=w.id);\r\n","drizzle/0020_coach_army_data_epochs.sql":"ALTER TABLE `account_entitlements` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_completion_outbox` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_completions` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_djscratch_challenges` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_run_events` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_runs` ADD `data_epoch` integer DEFAULT 1 NOT NULL;--> statement-breakpoint\r\nALTER TABLE `coach_army_bindings` ADD `data_epoch` integer DEFAULT 1 NOT NULL;","drizzle/0021_armie_letter_deliveries.sql":"CREATE TABLE `armie_letter_deliveries` (`user_id` text NOT NULL, `day` text NOT NULL, `letter_id` text NOT NULL, `sent_at` integer NOT NULL, PRIMARY KEY(`user_id`, `day`, `letter_id`));\r\n","drizzle/0022_performance_progression.sql":"ALTER TABLE `workouts` ADD COLUMN `performance_snapshot` text;\r\n--> statement-breakpoint\r\nCREATE TRIGGER workouts_performance_immutable BEFORE UPDATE OF performance_snapshot ON workouts\r\nWHEN OLD.completed_at IS NOT NULL AND NEW.performance_snapshot IS NOT OLD.performance_snapshot\r\nBEGIN SELECT RAISE(ABORT,'workout_performance_immutable'); END;\r\n","drizzle/0025_spotify_connection.sql":"CREATE TABLE IF NOT EXISTS spotify_connections (\r\n  user_id text PRIMARY KEY NOT NULL,\r\n  spotify_user_id text NOT NULL,\r\n  display_name text,\r\n  access_token_enc text NOT NULL,\r\n  refresh_token_enc text NOT NULL,\r\n  expires_at integer NOT NULL,\r\n  scopes text NOT NULL,\r\n  data_epoch integer NOT NULL,\r\n  updated_at integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE TABLE IF NOT EXISTS spotify_oauth_states (\r\n  state_hash text PRIMARY KEY NOT NULL,\r\n  cookie_hash text NOT NULL,\r\n  user_id text NOT NULL,\r\n  verifier_enc text NOT NULL,\r\n  data_epoch integer NOT NULL,\r\n  expires_at integer NOT NULL\r\n);\r\n--> statement-breakpoint\r\nCREATE INDEX IF NOT EXISTS spotify_oauth_states_user ON spotify_oauth_states(user_id);\r\n","drizzle/0026_workout_paths.sql":"CREATE TABLE IF NOT EXISTS workout_path_choices (\r\n  user_id text PRIMARY KEY NOT NULL,\r\n  data_epoch integer NOT NULL,\r\n  paths text NOT NULL,\r\n  created_at integer NOT NULL\r\n);\r\n","drizzle/0027_coach_world.sql":"CREATE TABLE world_assertions (id TEXT PRIMARY KEY NOT NULL, ok INTEGER NOT NULL CHECK(ok=1));\r\n--> statement-breakpoint\r\nCREATE TABLE world_bases (owner TEXT PRIMARY KEY NOT NULL, epoch INTEGER NOT NULL, public_id TEXT UNIQUE NOT NULL, revision INTEGER NOT NULL, base TEXT NOT NULL, charge INTEGER NOT NULL DEFAULT 0 CHECK(charge>=0 AND charge<=10), created_at INTEGER NOT NULL);\r\n--> statement-breakpoint\r\nCREATE TABLE world_requests (owner TEXT NOT NULL, epoch INTEGER NOT NULL, id TEXT NOT NULL, hash TEXT NOT NULL, response TEXT NOT NULL, PRIMARY KEY(owner,epoch,id));\r\n--> statement-breakpoint\r\nCREATE TABLE world_sources (owner TEXT NOT NULL, epoch INTEGER NOT NULL, source_id TEXT NOT NULL, decision TEXT NOT NULL, day TEXT NOT NULL, track TEXT, PRIMARY KEY(owner,epoch,source_id));\r\n--> statement-breakpoint\r\nCREATE TABLE world_grants (owner TEXT NOT NULL, epoch INTEGER NOT NULL, day TEXT NOT NULL, track TEXT NOT NULL, PRIMARY KEY(owner,epoch,day,track));\r\n--> statement-breakpoint\r\nCREATE TABLE world_friends (a TEXT NOT NULL,b TEXT NOT NULL, location INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(a,b));\r\n--> statement-breakpoint\r\nCREATE TABLE world_friend_codes (code TEXT PRIMARY KEY NOT NULL, owner TEXT NOT NULL, epoch INTEGER NOT NULL, expires_at INTEGER NOT NULL);\r\n--> statement-breakpoint\r\nCREATE TABLE world_walks (owner TEXT PRIMARY KEY NOT NULL, epoch INTEGER NOT NULL, id TEXT NOT NULL, nonce TEXT NOT NULL, seq INTEGER NOT NULL, state TEXT NOT NULL, updated_at INTEGER NOT NULL);\r\n--> statement-breakpoint\r\nCREATE TABLE world_presence (owner TEXT PRIMARY KEY NOT NULL, epoch INTEGER NOT NULL, site_id TEXT NOT NULL, seq INTEGER NOT NULL, latitude REAL NOT NULL, longitude REAL NOT NULL, accuracy REAL NOT NULL, updated_at INTEGER NOT NULL);\r\n--> statement-breakpoint\r\nCREATE TABLE world_sectors (id TEXT PRIMARY KEY NOT NULL, site_id TEXT, kind TEXT NOT NULL DEFAULT 'hub' CHECK(kind IN ('hub','territory')), latitude REAL NOT NULL, longitude REAL NOT NULL, controller TEXT, revision INTEGER NOT NULL DEFAULT 1, generation INTEGER NOT NULL DEFAULT 1, closes_at INTEGER, pause_until INTEGER, grace_until INTEGER NOT NULL DEFAULT 0, phase TEXT NOT NULL DEFAULT 'stable', changed_at INTEGER NOT NULL, conflict_at INTEGER NOT NULL, enabled INTEGER NOT NULL DEFAULT 1);\r\n--> statement-breakpoint\r\nCREATE TABLE world_influence (id TEXT PRIMARY KEY NOT NULL, owner TEXT NOT NULL, epoch INTEGER NOT NULL, day TEXT NOT NULL, sector_id TEXT NOT NULL, faction TEXT NOT NULL, kind TEXT NOT NULL, points INTEGER NOT NULL, accepted_at INTEGER NOT NULL, effective_at INTEGER, expires_at INTEGER NOT NULL, UNIQUE(owner,epoch,day,sector_id,kind));\r\n--> statement-breakpoint\r\nCREATE INDEX world_influence_sector ON world_influence(sector_id,accepted_at,expires_at);\r\n--> statement-breakpoint\r\nCREATE TABLE world_encounters (id TEXT PRIMARY KEY NOT NULL, a TEXT NOT NULL, ae INTEGER NOT NULL, b TEXT NOT NULL, be INTEGER NOT NULL, site_id TEXT NOT NULL, anchor TEXT NOT NULL, sector_id TEXT NOT NULL, generation INTEGER NOT NULL, status TEXT NOT NULL, revision INTEGER NOT NULL, a_reserved INTEGER NOT NULL, b_reserved INTEGER NOT NULL, expires_at INTEGER NOT NULL, due_at INTEGER, admitted_at INTEGER, winner TEXT, faction_a TEXT NOT NULL, faction_b TEXT NOT NULL);\r\n--> statement-breakpoint\r\nCREATE INDEX world_encounters_party ON world_encounters(a,b,status);\r\n--> statement-breakpoint\r\nCREATE TABLE world_visits (owner TEXT NOT NULL,epoch INTEGER NOT NULL,day TEXT NOT NULL,site_id TEXT NOT NULL,item_id TEXT NOT NULL,PRIMARY KEY(owner,epoch,day,site_id,item_id));\r\n\r\n--> statement-breakpoint\r\nCREATE INDEX world_sector_bounds ON world_sectors(latitude,longitude,id);\r\n\r\n--> statement-breakpoint\r\nCREATE INDEX world_sector_due ON world_sectors(closes_at,pause_until);\r\n","drizzle/0028_world_location_drops.sql":"CREATE TABLE world_drop_locations(owner TEXT NOT NULL,epoch INTEGER NOT NULL,kind TEXT NOT NULL CHECK(kind IN ('home','work','gym')),latitude REAL NOT NULL,longitude REAL NOT NULL,version TEXT NOT NULL,updated_at INTEGER NOT NULL,PRIMARY KEY(owner,epoch,kind));\r\n--> statement-breakpoint\r\nCREATE TABLE world_drop_challenges(owner TEXT NOT NULL,epoch INTEGER NOT NULL,kind TEXT NOT NULL,id TEXT NOT NULL,nonce TEXT NOT NULL,location_version TEXT NOT NULL,issued_at INTEGER NOT NULL,expires_at INTEGER NOT NULL,first_at INTEGER,first_sample INTEGER,last_sample INTEGER,seq INTEGER NOT NULL DEFAULT 0,PRIMARY KEY(owner,epoch,kind));\r\n--> statement-breakpoint\r\nCREATE TABLE world_drop_claims(owner TEXT NOT NULL,epoch INTEGER NOT NULL,id TEXT NOT NULL,kind TEXT NOT NULL,claimed_at INTEGER NOT NULL,xp REAL NOT NULL DEFAULT 0,reward TEXT NOT NULL,PRIMARY KEY(owner,epoch,id));\r\n--> statement-breakpoint\r\nCREATE INDEX world_drop_claim_cooldown ON world_drop_claims(owner,epoch,kind,claimed_at);\r\n","drizzle/0029_world_zones.sql":"CREATE TABLE world_zones (id TEXT PRIMARY KEY NOT NULL, controller TEXT NOT NULL, claimed_by TEXT NOT NULL, claimed_at INTEGER NOT NULL, revision INTEGER NOT NULL DEFAULT 1);\r\n--> statement-breakpoint\r\nCREATE TABLE world_zone_npc (zone_id TEXT NOT NULL, idx INTEGER NOT NULL CHECK(idx>=0 AND idx<3), defeated_by TEXT NOT NULL, at INTEGER NOT NULL, PRIMARY KEY(zone_id, idx));\r\n--> statement-breakpoint\r\nCREATE TABLE world_last_rewarded (owner TEXT PRIMARY KEY NOT NULL, epoch INTEGER NOT NULL, lat REAL NOT NULL, lng REAL NOT NULL, at INTEGER NOT NULL);\r\n\r\n--> statement-breakpoint\r\nCREATE TABLE world_neutral_zones (id TEXT PRIMARY KEY NOT NULL, kind TEXT NOT NULL DEFAULT 'nature');\r\n","drizzle/0030_world_wild.sql":"CREATE TABLE world_wild_encounters (id TEXT PRIMARY KEY NOT NULL, owner TEXT NOT NULL, epoch INTEGER NOT NULL, spawn_id TEXT NOT NULL, res8 TEXT NOT NULL, bucket INTEGER NOT NULL, tier TEXT NOT NULL, unit_id TEXT NOT NULL, zone_id TEXT NOT NULL, lat REAL NOT NULL, lng REAL NOT NULL, facing INTEGER NOT NULL, napping INTEGER NOT NULL CHECK(napping IN (0,1)), spawn_expires INTEGER NOT NULL, seed TEXT NOT NULL, started_at INTEGER NOT NULL, expires_at INTEGER NOT NULL, status TEXT NOT NULL CHECK(status IN ('active','caught','fled','ended','expired','locked')), darts_used INTEGER NOT NULL DEFAULT 0, sedation REAL NOT NULL DEFAULT 0, sedation_at INTEGER NOT NULL, spook TEXT NOT NULL, peak_spook REAL NOT NULL DEFAULT 0, trail TEXT NOT NULL DEFAULT '[]', last_sampled_at INTEGER NOT NULL DEFAULT 0, quality_bonus REAL NOT NULL DEFAULT 0, flag TEXT, ended_at INTEGER);\r\n--> statement-breakpoint\r\nCREATE INDEX world_wild_encounters_owner ON world_wild_encounters(owner, epoch, status);\r\n--> statement-breakpoint\r\nCREATE INDEX world_wild_encounters_cell ON world_wild_encounters(owner, epoch, res8, bucket);\r\n--> statement-breakpoint\r\nCREATE TABLE world_wild_daily (owner TEXT NOT NULL, epoch INTEGER NOT NULL, day TEXT NOT NULL, encounters INTEGER NOT NULL DEFAULT 0, blue INTEGER NOT NULL DEFAULT 0, orange INTEGER NOT NULL DEFAULT 0, gold INTEGER NOT NULL DEFAULT 0, iridescent INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(owner, epoch, day));\r\n--> statement-breakpoint\r\nCREATE TABLE world_wild_seen (owner TEXT NOT NULL, epoch INTEGER NOT NULL, spawn_id TEXT NOT NULL, until_at INTEGER NOT NULL, PRIMARY KEY(owner, epoch, spawn_id));\r\n--> statement-breakpoint\r\nCREATE TABLE world_wild_lockout (owner TEXT PRIMARY KEY NOT NULL, until_at INTEGER NOT NULL);\r\n","drizzle/0031_game_generation.sql":"-- Separate from account deletion epochs. Applying this migration does NOT reset data.\nCREATE TABLE game_generation (\n id INTEGER PRIMARY KEY CHECK(id=1),\n generation INTEGER NOT NULL CHECK(typeof(generation)='integer' AND generation BETWEEN 1 AND 9007199254740991),\n reset_at INTEGER NOT NULL CHECK(typeof(reset_at)='integer' AND reset_at BETWEEN 0 AND 9007199254740991)\n);\n--> statement-breakpoint\nINSERT INTO game_generation(id,generation,reset_at) VALUES(1,1,0);\n--> statement-breakpoint\nCREATE TABLE game_generation_assertions(id TEXT PRIMARY KEY NOT NULL, ok INTEGER NOT NULL CHECK(ok=1));\n--> statement-breakpoint\nCREATE TABLE game_generation_resets(\n reset_id TEXT PRIMARY KEY NOT NULL,\n previous_generation INTEGER NOT NULL UNIQUE CHECK(typeof(previous_generation)='integer' AND previous_generation BETWEEN 1 AND 9007199254740990),\n generation INTEGER NOT NULL UNIQUE CHECK(typeof(generation)='integer' AND generation=previous_generation+1),\n reset_at INTEGER NOT NULL CHECK(typeof(reset_at)='integer' AND reset_at BETWEEN 1 AND 9007199254740991),\n request TEXT NOT NULL,\n evidence TEXT NOT NULL\n);\n--> statement-breakpoint\nCREATE TRIGGER game_generation_resets_no_update BEFORE UPDATE ON game_generation_resets\nBEGIN SELECT RAISE(ABORT,'game_reset_receipt_immutable'); END;\n--> statement-breakpoint\nCREATE TRIGGER game_generation_resets_no_delete BEFORE DELETE ON game_generation_resets\nBEGIN SELECT RAISE(ABORT,'game_reset_receipt_immutable'); END;\n","drizzle/0032_run_level_manifests.sql":"CREATE TABLE IF NOT EXISTS world_boss_run_manifests(\n owner TEXT NOT NULL,epoch INTEGER NOT NULL,run_id TEXT NOT NULL,manifest TEXT NOT NULL,\n PRIMARY KEY(owner,epoch,run_id)\n);\n--> statement-breakpoint\nCREATE TRIGGER IF NOT EXISTS world_boss_run_manifests_immutable BEFORE UPDATE ON world_boss_run_manifests\nBEGIN SELECT RAISE(ABORT,'boss_manifest_immutable'); END;\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS world_boss_level_clear_receipts(\n owner TEXT NOT NULL,epoch INTEGER NOT NULL,run_id TEXT NOT NULL,\n level INTEGER NOT NULL CHECK(typeof(level)='integer' AND level BETWEEN 1 AND 5),\n clear_ordinal INTEGER NOT NULL CHECK(typeof(clear_ordinal)='integer' AND clear_ordinal>=0),\n level_receipt_id TEXT NOT NULL,fingerprint TEXT NOT NULL CHECK(length(fingerprint)=64),result TEXT NOT NULL,\n run_revision INTEGER NOT NULL,ledger_revision INTEGER NOT NULL,accepted_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,run_id,level),UNIQUE(owner,epoch,clear_ordinal),UNIQUE(owner,epoch,level_receipt_id)\n);\n--> statement-breakpoint\nCREATE TRIGGER IF NOT EXISTS world_boss_level_clear_receipts_immutable BEFORE UPDATE ON world_boss_level_clear_receipts\nBEGIN SELECT RAISE(ABORT,'boss_receipt_immutable'); END;\n","drizzle/0033_rescue_hearts.sql":"CREATE TABLE IF NOT EXISTS world_coach_rescues (\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, canonical_coach_id TEXT NOT NULL,\n boss_receipt_id TEXT NOT NULL, run_id TEXT NOT NULL, encounter_id TEXT NOT NULL,\n policy_id TEXT NOT NULL CHECK(policy_id='rescue-six-v1'), rescued_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,canonical_coach_id)\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS world_run_rescue_manifests (\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, run_id TEXT NOT NULL,\n manifest TEXT NOT NULL, manifest_hash TEXT NOT NULL,\n PRIMARY KEY(owner,epoch,run_id)\n);\n--> statement-breakpoint\nCREATE TRIGGER IF NOT EXISTS world_run_rescue_manifest_immutable BEFORE UPDATE ON world_run_rescue_manifests\n BEGIN SELECT RAISE(ABORT,'immutable rescue manifest'); END;\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS world_run_rescue_level_receipts (\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, run_id TEXT NOT NULL,\n level INTEGER NOT NULL CHECK(level BETWEEN 1 AND 5),\n level_receipt_id TEXT NOT NULL, fingerprint TEXT NOT NULL, result TEXT NOT NULL,\n accepted_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,run_id,level), UNIQUE(owner,epoch,level_receipt_id)\n);\n","drizzle/meta/0000_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"889c84fc-a27b-488d-9384-b4138f9a8a5b\",\r\n  \"prevId\": \"00000000-0000-0000-0000-000000000000\",\r\n  \"tables\": {\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0001_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"7c4b3ae0-f6b2-4d4c-b074-3cc5a28f2a81\",\r\n  \"prevId\": \"889c84fc-a27b-488d-9384-b4138f9a8a5b\",\r\n  \"tables\": {\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0002_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"9bab1ce5-58cb-44cb-b884-cdc921351a09\",\r\n  \"prevId\": \"7c4b3ae0-f6b2-4d4c-b074-3cc5a28f2a81\",\r\n  \"tables\": {\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0003_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"0b745918-cb51-4786-bdbc-8e05caf060e3\",\r\n  \"prevId\": \"9bab1ce5-58cb-44cb-b884-cdc921351a09\",\r\n  \"tables\": {\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0004_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"d1d2c454-78ff-436f-bef7-b1c815eb547a\",\r\n  \"prevId\": \"0b745918-cb51-4786-bdbc-8e05caf060e3\",\r\n  \"tables\": {\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0005_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"e6a81be3-3b7e-4425-99f7-2fd41cc736a4\",\r\n  \"prevId\": \"d1d2c454-78ff-436f-bef7-b1c815eb547a\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0006_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"5a4def95-bebf-4826-ab6f-7a498b83007f\",\r\n  \"prevId\": \"e6a81be3-3b7e-4425-99f7-2fd41cc736a4\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0007_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"6d57b017-25e7-495c-8eed-4f6e6853d0ff\",\r\n  \"prevId\": \"5a4def95-bebf-4826-ab6f-7a498b83007f\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0008_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"71f0d1f2-e5b3-4367-b3ad-8e35969ac17a\",\r\n  \"prevId\": \"6d57b017-25e7-495c-8eed-4f6e6853d0ff\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0009_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"4b095e68-4966-4b8b-9b04-14b6f305c558\",\r\n  \"prevId\": \"71f0d1f2-e5b3-4367-b3ad-8e35969ac17a\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"breathing_sessions\": {\r\n      \"name\": \"breathing_sessions\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"breathing_user_started\": {\r\n          \"name\": \"breathing_user_started\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"breathing_user_completed\": {\r\n          \"name\": \"breathing_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"login_days\": {\r\n      \"name\": \"login_days\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"logged_at\": {\r\n          \"name\": \"logged_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"login_days_user_id_day_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"day\"\r\n          ],\r\n          \"name\": \"login_days_user_id_day_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0010_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"41c63ca6-f9b8-43de-b967-d7e34c593cc6\",\r\n  \"prevId\": \"4b095e68-4966-4b8b-9b04-14b6f305c558\",\r\n  \"tables\": {\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"breathing_sessions\": {\r\n      \"name\": \"breathing_sessions\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"breathing_user_started\": {\r\n          \"name\": \"breathing_user_started\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"breathing_user_completed\": {\r\n          \"name\": \"breathing_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"login_days\": {\r\n      \"name\": \"login_days\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"logged_at\": {\r\n          \"name\": \"logged_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"login_days_user_id_day_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"day\"\r\n          ],\r\n          \"name\": \"login_days_user_id_day_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_push_deliveries\": {\r\n      \"name\": \"release_push_deliveries\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\": {\r\n          \"name\": \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\",\r\n          \"tableFrom\": \"release_push_deliveries\",\r\n          \"tableTo\": \"subscriptions\",\r\n          \"columnsFrom\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"release_push_deliveries_endpoint_release_id_pk\": {\r\n          \"columns\": [\r\n            \"endpoint\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_push_deliveries_endpoint_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0018_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"84a22af4-94f5-4dd8-bdc3-e1cd2f98e51d\",\r\n  \"prevId\": \"41c63ca6-f9b8-43de-b967-d7e34c593cc6\",\r\n  \"tables\": {\r\n    \"account_data_deletions\": {\r\n      \"name\": \"account_data_deletions\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_epoch\": {\r\n          \"name\": \"deleted_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_at\": {\r\n          \"name\": \"deleted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\": {\r\n          \"name\": \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\",\r\n          \"tableFrom\": \"account_data_deletions\",\r\n          \"tableTo\": \"account_data_epochs\",\r\n          \"columnsFrom\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"onDelete\": \"no action\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"account_data_deletions_owner_id_deleted_epoch_pk\": {\r\n          \"columns\": [\r\n            \"owner_id\",\r\n            \"deleted_epoch\"\r\n          ],\r\n          \"name\": \"account_data_deletions_owner_id_deleted_epoch_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_deletions_epoch\": {\r\n          \"name\": \"account_data_deletions_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_epoch\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_epoch\\\" BETWEEN 1 AND 9007199254740990\"\r\n        },\r\n        \"account_data_deletions_time\": {\r\n          \"name\": \"account_data_deletions_time\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_at\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_data_epochs\": {\r\n      \"name\": \"account_data_epochs\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"epoch\": {\r\n          \"name\": \"epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_epochs_epoch\": {\r\n          \"name\": \"account_data_epochs_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"epoch\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"epoch\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"account_data_epochs_updated\": {\r\n          \"name\": \"account_data_epochs_updated\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"updated_at\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"updated_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_entitlements\": {\r\n      \"name\": \"account_entitlements\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_status\": {\r\n          \"name\": \"coach_army_status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'locked'\"\r\n        },\r\n        \"coach_army_completed_at\": {\r\n          \"name\": \"coach_army_completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_event_id\": {\r\n          \"name\": \"coach_army_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_pack_entitlements\": {\r\n      \"name\": \"account_pack_entitlements\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"pack_id\": {\r\n          \"name\": \"pack_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"granted_at\": {\r\n          \"name\": \"granted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_pack_entitlements_user\": {\r\n          \"name\": \"account_pack_entitlements_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"account_pack_entitlements_user_id_pack_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"pack_id\"\r\n          ],\r\n          \"name\": \"account_pack_entitlements_user_id_pack_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"breathing_sessions\": {\r\n      \"name\": \"breathing_sessions\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"breathing_user_started\": {\r\n          \"name\": \"breathing_user_started\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"breathing_user_completed\": {\r\n          \"name\": \"breathing_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completion_outbox\": {\r\n      \"name\": \"coach_army_completion_outbox\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'pending'\"\r\n        },\r\n        \"attempts\": {\r\n          \"name\": \"attempts\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"last_error\": {\r\n          \"name\": \"last_error\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"delivered_at\": {\r\n          \"name\": \"delivered_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completion_outbox_pending\": {\r\n          \"name\": \"coach_army_completion_outbox_pending\",\r\n          \"columns\": [\r\n            \"status\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completions\": {\r\n      \"name\": \"coach_army_completions\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completions_user\": {\r\n          \"name\": \"coach_army_completions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_djscratch_challenges\": {\r\n      \"name\": \"coach_army_djscratch_challenges\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"next_step\": {\r\n          \"name\": \"next_step\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_djscratch_challenges_run\": {\r\n          \"name\": \"coach_army_djscratch_challenges_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"account_id\",\r\n            \"status\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_run_events\": {\r\n      \"name\": \"coach_army_run_events\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"sequence\": {\r\n          \"name\": \"sequence\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"stage\": {\r\n          \"name\": \"stage\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"source\": {\r\n          \"name\": \"source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_run_events_run\": {\r\n          \"name\": \"coach_army_run_events_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"coach_army_run_sequence\": {\r\n          \"name\": \"coach_army_run_sequence\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_runs\": {\r\n      \"name\": \"coach_army_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completion_event_id\": {\r\n          \"name\": \"completion_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_runs_account\": {\r\n          \"name\": \"coach_army_runs_account\",\r\n          \"columns\": [\r\n            \"account_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"goals\": {\r\n      \"name\": \"goals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"title\": {\r\n          \"name\": \"title\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"note\": {\r\n          \"name\": \"note\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"''\"\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"goals_user_updated\": {\r\n          \"name\": \"goals_user_updated\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"login_days\": {\r\n      \"name\": \"login_days\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"logged_at\": {\r\n          \"name\": \"logged_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"login_days_user_id_day_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"day\"\r\n          ],\r\n          \"name\": \"login_days_user_id_day_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_push_deliveries\": {\r\n      \"name\": \"release_push_deliveries\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\": {\r\n          \"name\": \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\",\r\n          \"tableFrom\": \"release_push_deliveries\",\r\n          \"tableTo\": \"subscriptions\",\r\n          \"columnsFrom\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"release_push_deliveries_endpoint_release_id_pk\": {\r\n          \"columns\": [\r\n            \"endpoint\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_push_deliveries_endpoint_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"war_room_arsenals\": {\r\n      \"name\": \"war_room_arsenals\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"loadout\": {\r\n          \"name\": \"loadout\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"recipes\": {\r\n          \"name\": \"recipes\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'[]'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/0019_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"76552492-8c55-4843-8f66-cb2d77f687e3\",\r\n  \"prevId\": \"84a22af4-94f5-4dd8-bdc3-e1cd2f98e51d\",\r\n  \"tables\": {\r\n    \"account_data_deletions\": {\r\n      \"name\": \"account_data_deletions\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_epoch\": {\r\n          \"name\": \"deleted_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_at\": {\r\n          \"name\": \"deleted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\": {\r\n          \"name\": \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\",\r\n          \"tableFrom\": \"account_data_deletions\",\r\n          \"tableTo\": \"account_data_epochs\",\r\n          \"columnsFrom\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"onDelete\": \"no action\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"account_data_deletions_owner_id_deleted_epoch_pk\": {\r\n          \"columns\": [\r\n            \"owner_id\",\r\n            \"deleted_epoch\"\r\n          ],\r\n          \"name\": \"account_data_deletions_owner_id_deleted_epoch_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_deletions_epoch\": {\r\n          \"name\": \"account_data_deletions_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_epoch\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_epoch\\\" BETWEEN 1 AND 9007199254740990\"\r\n        },\r\n        \"account_data_deletions_time\": {\r\n          \"name\": \"account_data_deletions_time\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_at\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_data_epochs\": {\r\n      \"name\": \"account_data_epochs\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"epoch\": {\r\n          \"name\": \"epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_epochs_epoch\": {\r\n          \"name\": \"account_data_epochs_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"epoch\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"epoch\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"account_data_epochs_updated\": {\r\n          \"name\": \"account_data_epochs_updated\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"updated_at\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"updated_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_entitlements\": {\r\n      \"name\": \"account_entitlements\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_status\": {\r\n          \"name\": \"coach_army_status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'locked'\"\r\n        },\r\n        \"coach_army_completed_at\": {\r\n          \"name\": \"coach_army_completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_event_id\": {\r\n          \"name\": \"coach_army_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_pack_entitlements\": {\r\n      \"name\": \"account_pack_entitlements\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"pack_id\": {\r\n          \"name\": \"pack_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"granted_at\": {\r\n          \"name\": \"granted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_pack_entitlements_user\": {\r\n          \"name\": \"account_pack_entitlements_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"account_pack_entitlements_user_id_pack_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"pack_id\"\r\n          ],\r\n          \"name\": \"account_pack_entitlements_user_id_pack_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"breathing_sessions\": {\r\n      \"name\": \"breathing_sessions\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"breathing_user_started\": {\r\n          \"name\": \"breathing_user_started\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"breathing_user_completed\": {\r\n          \"name\": \"breathing_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completion_outbox\": {\r\n      \"name\": \"coach_army_completion_outbox\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'pending'\"\r\n        },\r\n        \"attempts\": {\r\n          \"name\": \"attempts\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"last_error\": {\r\n          \"name\": \"last_error\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"delivered_at\": {\r\n          \"name\": \"delivered_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completion_outbox_pending\": {\r\n          \"name\": \"coach_army_completion_outbox_pending\",\r\n          \"columns\": [\r\n            \"status\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completions\": {\r\n      \"name\": \"coach_army_completions\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completions_user\": {\r\n          \"name\": \"coach_army_completions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_djscratch_challenges\": {\r\n      \"name\": \"coach_army_djscratch_challenges\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"next_step\": {\r\n          \"name\": \"next_step\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_djscratch_challenges_run\": {\r\n          \"name\": \"coach_army_djscratch_challenges_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"account_id\",\r\n            \"status\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_run_events\": {\r\n      \"name\": \"coach_army_run_events\",\r\n      \"columns\": {\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"sequence\": {\r\n          \"name\": \"sequence\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"stage\": {\r\n          \"name\": \"stage\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"source\": {\r\n          \"name\": \"source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_run_events_run\": {\r\n          \"name\": \"coach_army_run_events_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"coach_army_run_sequence\": {\r\n          \"name\": \"coach_army_run_sequence\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_runs\": {\r\n      \"name\": \"coach_army_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completion_event_id\": {\r\n          \"name\": \"completion_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_runs_account\": {\r\n          \"name\": \"coach_army_runs_account\",\r\n          \"columns\": [\r\n            \"account_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"competitive_decisions\": {\r\n      \"name\": \"competitive_decisions\",\r\n      \"columns\": {\r\n        \"workout_id\": {\r\n          \"name\": \"workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"seq\": {\r\n          \"name\": \"seq\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"reason\": {\r\n          \"name\": \"reason\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"competitive_decisions_workout_id_workouts_id_fk\": {\r\n          \"name\": \"competitive_decisions_workout_id_workouts_id_fk\",\r\n          \"tableFrom\": \"competitive_decisions\",\r\n          \"tableTo\": \"workouts\",\r\n          \"columnsFrom\": [\r\n            \"workout_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"id\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"competitive_decisions_workout_id_seq_pk\": {\r\n          \"columns\": [\r\n            \"workout_id\",\r\n            \"seq\"\r\n          ],\r\n          \"name\": \"competitive_decisions_workout_id_seq_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"competitive_decisions_seq\": {\r\n          \"name\": \"competitive_decisions_seq\",\r\n          \"value\": \"typeof(\\\"competitive_decisions\\\".\\\"seq\\\")='integer' AND \\\"competitive_decisions\\\".\\\"seq\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"competitive_decisions_status\": {\r\n          \"name\": \"competitive_decisions_status\",\r\n          \"value\": \"\\\"competitive_decisions\\\".\\\"status\\\" IN ('accepted','pending','not_eligible')\"\r\n        },\r\n        \"competitive_decisions_reason\": {\r\n          \"name\": \"competitive_decisions_reason\",\r\n          \"value\": \"length(\\\"competitive_decisions\\\".\\\"reason\\\") BETWEEN 1 AND 128\"\r\n        },\r\n        \"competitive_decisions_created\": {\r\n          \"name\": \"competitive_decisions_created\",\r\n          \"value\": \"typeof(\\\"competitive_decisions\\\".\\\"created_at\\\")='integer' AND \\\"competitive_decisions\\\".\\\"created_at\\\" BETWEEN 0 AND 9007199254740991\"\r\n        }\r\n      }\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"goals\": {\r\n      \"name\": \"goals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"title\": {\r\n          \"name\": \"title\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"note\": {\r\n          \"name\": \"note\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"''\"\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"goals_user_updated\": {\r\n          \"name\": \"goals_user_updated\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"login_days\": {\r\n      \"name\": \"login_days\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"logged_at\": {\r\n          \"name\": \"logged_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"login_days_user_id_day_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"day\"\r\n          ],\r\n          \"name\": \"login_days_user_id_day_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_push_deliveries\": {\r\n      \"name\": \"release_push_deliveries\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\": {\r\n          \"name\": \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\",\r\n          \"tableFrom\": \"release_push_deliveries\",\r\n          \"tableTo\": \"subscriptions\",\r\n          \"columnsFrom\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"release_push_deliveries_endpoint_release_id_pk\": {\r\n          \"columns\": [\r\n            \"endpoint\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_push_deliveries_endpoint_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"war_room_arsenals\": {\r\n      \"name\": \"war_room_arsenals\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"loadout\": {\r\n          \"name\": \"loadout\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"recipes\": {\r\n          \"name\": \"recipes\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'[]'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workout_imports\": {\r\n      \"name\": \"workout_imports\",\r\n      \"columns\": {\r\n        \"workout_id\": {\r\n          \"name\": \"workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"idempotency_key\": {\r\n          \"name\": \"idempotency_key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fingerprint\": {\r\n          \"name\": \"fingerprint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"digest_version\": {\r\n          \"name\": \"digest_version\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_data_epoch\": {\r\n          \"name\": \"account_data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"workout_imports_workout_id_workouts_id_fk\": {\r\n          \"name\": \"workout_imports_workout_id_workouts_id_fk\",\r\n          \"tableFrom\": \"workout_imports\",\r\n          \"tableTo\": \"workouts\",\r\n          \"columnsFrom\": [\r\n            \"workout_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"id\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"workout_imports_key\": {\r\n          \"name\": \"workout_imports_key\",\r\n          \"value\": \"length(\\\"workout_imports\\\".\\\"idempotency_key\\\")=64 AND \\\"workout_imports\\\".\\\"idempotency_key\\\" NOT GLOB '*[^0-9a-f]*'\"\r\n        },\r\n        \"workout_imports_fingerprint\": {\r\n          \"name\": \"workout_imports_fingerprint\",\r\n          \"value\": \"length(\\\"workout_imports\\\".\\\"fingerprint\\\")=64 AND \\\"workout_imports\\\".\\\"fingerprint\\\" NOT GLOB '*[^0-9a-f]*'\"\r\n        },\r\n        \"workout_imports_version\": {\r\n          \"name\": \"workout_imports_version\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"digest_version\\\")='integer' AND \\\"workout_imports\\\".\\\"digest_version\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"workout_imports_epoch\": {\r\n          \"name\": \"workout_imports_epoch\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"account_data_epoch\\\")='integer' AND \\\"workout_imports\\\".\\\"account_data_epoch\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"workout_imports_created\": {\r\n          \"name\": \"workout_imports_created\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"created_at\\\")='integer' AND \\\"workout_imports\\\".\\\"created_at\\\" BETWEEN 0 AND 9007199254740991\"\r\n        }\r\n      }\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"source\": {\r\n          \"name\": \"source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'server'\"\r\n        },\r\n        \"client_workout_id\": {\r\n          \"name\": \"client_workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"competitive_status\": {\r\n          \"name\": \"competitive_status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"ux_workouts_acct_cwid\": {\r\n          \"name\": \"ux_workouts_acct_cwid\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"client_workout_id\"\r\n          ],\r\n          \"isUnique\": true,\r\n          \"where\": \"\\\"workouts\\\".\\\"client_workout_id\\\" IS NOT NULL\"\r\n        },\r\n        \"ix_workouts_authority\": {\r\n          \"name\": \"ix_workouts_authority\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"source\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"ix_workouts_rank\": {\r\n          \"name\": \"ix_workouts_rank\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"competitive_status\",\r\n            \"mode\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"workouts_source\": {\r\n          \"name\": \"workouts_source\",\r\n          \"value\": \"\\\"workouts\\\".\\\"source\\\" IN ('server','guest_import')\"\r\n        },\r\n        \"workouts_competitive_status\": {\r\n          \"name\": \"workouts_competitive_status\",\r\n          \"value\": \"\\\"workouts\\\".\\\"competitive_status\\\" IS NULL OR \\\"workouts\\\".\\\"competitive_status\\\" IN ('accepted','pending','not_eligible')\"\r\n        }\r\n      }\r\n    },\r\n    \"coach_army_bindings\": {\r\n      \"name\": \"coach_army_bindings\",\r\n      \"columns\": {\r\n        \"binding_id\": {\r\n          \"name\": \"binding_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"claim_hash\": {\r\n          \"name\": \"claim_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"claimed_at\": {\r\n          \"name\": \"claimed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_bindings_event_id_unique\": {\r\n          \"name\": \"coach_army_bindings_event_id_unique\",\r\n          \"columns\": [\r\n            \"event_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"coach_army_bindings_claim_hash_unique\": {\r\n          \"name\": \"coach_army_bindings_claim_hash_unique\",\r\n          \"columns\": [\r\n            \"claim_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"coach_army_bindings_account\": {\r\n          \"name\": \"coach_army_bindings_account\",\r\n          \"columns\": [\r\n            \"account_id\",\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}\r\n","drizzle/meta/0020_snapshot.json":"{\r\n  \"version\": \"6\",\r\n  \"dialect\": \"sqlite\",\r\n  \"id\": \"1afb8f4f-5343-403d-80d0-0ee85f492116\",\r\n  \"prevId\": \"76552492-8c55-4843-8f66-cb2d77f687e3\",\r\n  \"tables\": {\r\n    \"account_data_deletions\": {\r\n      \"name\": \"account_data_deletions\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_epoch\": {\r\n          \"name\": \"deleted_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"deleted_at\": {\r\n          \"name\": \"deleted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\": {\r\n          \"name\": \"account_data_deletions_owner_id_account_data_epochs_owner_id_fk\",\r\n          \"tableFrom\": \"account_data_deletions\",\r\n          \"tableTo\": \"account_data_epochs\",\r\n          \"columnsFrom\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"onDelete\": \"no action\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"account_data_deletions_owner_id_deleted_epoch_pk\": {\r\n          \"columns\": [\r\n            \"owner_id\",\r\n            \"deleted_epoch\"\r\n          ],\r\n          \"name\": \"account_data_deletions_owner_id_deleted_epoch_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_deletions_epoch\": {\r\n          \"name\": \"account_data_deletions_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_epoch\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_epoch\\\" BETWEEN 1 AND 9007199254740990\"\r\n        },\r\n        \"account_data_deletions_time\": {\r\n          \"name\": \"account_data_deletions_time\",\r\n          \"value\": \"typeof(\\\"account_data_deletions\\\".\\\"deleted_at\\\") = 'integer' AND \\\"account_data_deletions\\\".\\\"deleted_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_data_epochs\": {\r\n      \"name\": \"account_data_epochs\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"epoch\": {\r\n          \"name\": \"epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"account_data_epochs_epoch\": {\r\n          \"name\": \"account_data_epochs_epoch\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"epoch\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"epoch\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"account_data_epochs_updated\": {\r\n          \"name\": \"account_data_epochs_updated\",\r\n          \"value\": \"typeof(\\\"account_data_epochs\\\".\\\"updated_at\\\") = 'integer' AND \\\"account_data_epochs\\\".\\\"updated_at\\\" >= 0\"\r\n        }\r\n      }\r\n    },\r\n    \"account_entitlements\": {\r\n      \"name\": \"account_entitlements\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_status\": {\r\n          \"name\": \"coach_army_status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'locked'\"\r\n        },\r\n        \"coach_army_completed_at\": {\r\n          \"name\": \"coach_army_completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"coach_army_event_id\": {\r\n          \"name\": \"coach_army_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_identities\": {\r\n      \"name\": \"account_identities\",\r\n      \"columns\": {\r\n        \"clerk_id\": {\r\n          \"name\": \"clerk_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_identities_owner_id_unique\": {\r\n          \"name\": \"account_identities_owner_id_unique\",\r\n          \"columns\": [\r\n            \"owner_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"account_pack_entitlements\": {\r\n      \"name\": \"account_pack_entitlements\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"pack_id\": {\r\n          \"name\": \"pack_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"granted_at\": {\r\n          \"name\": \"granted_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"account_pack_entitlements_user\": {\r\n          \"name\": \"account_pack_entitlements_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"account_pack_entitlements_user_id_pack_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"pack_id\"\r\n          ],\r\n          \"name\": \"account_pack_entitlements_user_id_pack_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"breathing_sessions\": {\r\n      \"name\": \"breathing_sessions\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"breathing_user_started\": {\r\n          \"name\": \"breathing_user_started\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"breathing_user_completed\": {\r\n          \"name\": \"breathing_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_bindings\": {\r\n      \"name\": \"coach_army_bindings\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"binding_id\": {\r\n          \"name\": \"binding_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"claim_hash\": {\r\n          \"name\": \"claim_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"claimed_at\": {\r\n          \"name\": \"claimed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_bindings_event_id_unique\": {\r\n          \"name\": \"coach_army_bindings_event_id_unique\",\r\n          \"columns\": [\r\n            \"event_id\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"coach_army_bindings_claim_hash_unique\": {\r\n          \"name\": \"coach_army_bindings_claim_hash_unique\",\r\n          \"columns\": [\r\n            \"claim_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"coach_army_bindings_account\": {\r\n          \"name\": \"coach_army_bindings_account\",\r\n          \"columns\": [\r\n            \"account_id\",\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completion_outbox\": {\r\n      \"name\": \"coach_army_completion_outbox\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'pending'\"\r\n        },\r\n        \"attempts\": {\r\n          \"name\": \"attempts\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"last_error\": {\r\n          \"name\": \"last_error\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"delivered_at\": {\r\n          \"name\": \"delivered_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completion_outbox_pending\": {\r\n          \"name\": \"coach_army_completion_outbox_pending\",\r\n          \"columns\": [\r\n            \"status\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_completions\": {\r\n      \"name\": \"coach_army_completions\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_completions_user\": {\r\n          \"name\": \"coach_army_completions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_djscratch_challenges\": {\r\n      \"name\": \"coach_army_djscratch_challenges\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"next_step\": {\r\n          \"name\": \"next_step\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_djscratch_challenges_run\": {\r\n          \"name\": \"coach_army_djscratch_challenges_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"account_id\",\r\n            \"status\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_run_events\": {\r\n      \"name\": \"coach_army_run_events\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"event_id\": {\r\n          \"name\": \"event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"run_id\": {\r\n          \"name\": \"run_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"sequence\": {\r\n          \"name\": \"sequence\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"stage\": {\r\n          \"name\": \"stage\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"source\": {\r\n          \"name\": \"source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"received_at\": {\r\n          \"name\": \"received_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_run_events_run\": {\r\n          \"name\": \"coach_army_run_events_run\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"coach_army_run_sequence\": {\r\n          \"name\": \"coach_army_run_sequence\",\r\n          \"columns\": [\r\n            \"run_id\",\r\n            \"sequence\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"coach_army_runs\": {\r\n      \"name\": \"coach_army_runs\",\r\n      \"columns\": {\r\n        \"data_epoch\": {\r\n          \"name\": \"data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_id\": {\r\n          \"name\": \"account_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completion_event_id\": {\r\n          \"name\": \"completion_event_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"coach_army_runs_account\": {\r\n          \"name\": \"coach_army_runs_account\",\r\n          \"columns\": [\r\n            \"account_id\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"competitive_decisions\": {\r\n      \"name\": \"competitive_decisions\",\r\n      \"columns\": {\r\n        \"workout_id\": {\r\n          \"name\": \"workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"seq\": {\r\n          \"name\": \"seq\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"reason\": {\r\n          \"name\": \"reason\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"competitive_decisions_workout_id_workouts_id_fk\": {\r\n          \"name\": \"competitive_decisions_workout_id_workouts_id_fk\",\r\n          \"tableFrom\": \"competitive_decisions\",\r\n          \"tableTo\": \"workouts\",\r\n          \"columnsFrom\": [\r\n            \"workout_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"id\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"competitive_decisions_workout_id_seq_pk\": {\r\n          \"columns\": [\r\n            \"workout_id\",\r\n            \"seq\"\r\n          ],\r\n          \"name\": \"competitive_decisions_workout_id_seq_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"competitive_decisions_seq\": {\r\n          \"name\": \"competitive_decisions_seq\",\r\n          \"value\": \"typeof(\\\"competitive_decisions\\\".\\\"seq\\\")='integer' AND \\\"competitive_decisions\\\".\\\"seq\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"competitive_decisions_status\": {\r\n          \"name\": \"competitive_decisions_status\",\r\n          \"value\": \"\\\"competitive_decisions\\\".\\\"status\\\" IN ('accepted','pending','not_eligible')\"\r\n        },\r\n        \"competitive_decisions_reason\": {\r\n          \"name\": \"competitive_decisions_reason\",\r\n          \"value\": \"length(\\\"competitive_decisions\\\".\\\"reason\\\") BETWEEN 1 AND 128\"\r\n        },\r\n        \"competitive_decisions_created\": {\r\n          \"name\": \"competitive_decisions_created\",\r\n          \"value\": \"typeof(\\\"competitive_decisions\\\".\\\"created_at\\\")='integer' AND \\\"competitive_decisions\\\".\\\"created_at\\\" BETWEEN 0 AND 9007199254740991\"\r\n        }\r\n      }\r\n    },\r\n    \"deliveries\": {\r\n      \"name\": \"deliveries\",\r\n      \"columns\": {\r\n        \"reminder_id\": {\r\n          \"name\": \"reminder_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"deliveries_reminder_id_day_endpoint_pk\": {\r\n          \"columns\": [\r\n            \"reminder_id\",\r\n            \"day\",\r\n            \"endpoint\"\r\n          ],\r\n          \"name\": \"deliveries_reminder_id_day_endpoint_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"gala_runs\": {\r\n      \"name\": \"gala_runs\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_name\": {\r\n          \"name\": \"dj_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"moniker\": {\r\n          \"name\": \"moniker\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"dj_at\": {\r\n          \"name\": \"dj_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"gala_at\": {\r\n          \"name\": \"gala_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"lil_at\": {\r\n          \"name\": \"lil_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"corgi_at\": {\r\n          \"name\": \"corgi_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"hand_at\": {\r\n          \"name\": \"hand_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"armie_at\": {\r\n          \"name\": \"armie_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"duration_ms\": {\r\n          \"name\": \"duration_ms\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"installed_at\": {\r\n          \"name\": \"installed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"joined_at\": {\r\n          \"name\": \"joined_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"public_name\": {\r\n          \"name\": \"public_name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"gala_runs_public_name_unique\": {\r\n          \"name\": \"gala_runs_public_name_unique\",\r\n          \"columns\": [\r\n            \"public_name\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"gala_runs_creator_started\": {\r\n          \"name\": \"gala_runs_creator_started\",\r\n          \"columns\": [\r\n            \"creator\",\r\n            \"started_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"gala_runs_leaderboard\": {\r\n          \"name\": \"gala_runs_leaderboard\",\r\n          \"columns\": [\r\n            \"joined_at\",\r\n            \"duration_ms\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"goals\": {\r\n      \"name\": \"goals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"title\": {\r\n          \"name\": \"title\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"note\": {\r\n          \"name\": \"note\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"''\"\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'active'\"\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"goals_user_updated\": {\r\n          \"name\": \"goals_user_updated\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"updated_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"install_drafts\": {\r\n      \"name\": \"install_drafts\",\r\n      \"columns\": {\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"creator\": {\r\n          \"name\": \"creator\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"install_drafts_expiry\": {\r\n          \"name\": \"install_drafts_expiry\",\r\n          \"columns\": [\r\n            \"expires_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"install_drafts_creator\": {\r\n          \"name\": \"install_drafts_creator\",\r\n          \"columns\": [\r\n            \"creator\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"login_days\": {\r\n      \"name\": \"login_days\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"day\": {\r\n          \"name\": \"day\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"logged_at\": {\r\n          \"name\": \"logged_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"login_days_user_id_day_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"day\"\r\n          ],\r\n          \"name\": \"login_days_user_id_day_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"meals\": {\r\n      \"name\": \"meals\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"name\": {\r\n          \"name\": \"name\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"portion\": {\r\n          \"name\": \"portion\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"calories\": {\r\n          \"name\": \"calories\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"protein\": {\r\n          \"name\": \"protein\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"carbs\": {\r\n          \"name\": \"carbs\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fat\": {\r\n          \"name\": \"fat\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"micros\": {\r\n          \"name\": \"micros\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"nutrition_source\": {\r\n          \"name\": \"nutrition_source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"eaten_at\": {\r\n          \"name\": \"eaten_at\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"meals_user_date\": {\r\n          \"name\": \"meals_user_date\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"eaten_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"onboarding\": {\r\n      \"name\": \"onboarding\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"start_day\": {\r\n          \"name\": \"start_day\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"write_token\": {\r\n          \"name\": \"write_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"profiles\": {\r\n      \"name\": \"profiles\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'{}'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_deliveries\": {\r\n      \"name\": \"release_deliveries\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {\r\n        \"release_deliveries_user_id_release_id_pk\": {\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_deliveries_user_id_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_push_deliveries\": {\r\n      \"name\": \"release_push_deliveries\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"release_id\": {\r\n          \"name\": \"release_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"status\": {\r\n          \"name\": \"status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\": {\r\n          \"name\": \"release_push_deliveries_endpoint_subscriptions_endpoint_fk\",\r\n          \"tableFrom\": \"release_push_deliveries\",\r\n          \"tableTo\": \"subscriptions\",\r\n          \"columnsFrom\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"endpoint\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {\r\n        \"release_push_deliveries_endpoint_release_id_pk\": {\r\n          \"columns\": [\r\n            \"endpoint\",\r\n            \"release_id\"\r\n          ],\r\n          \"name\": \"release_push_deliveries_endpoint_release_id_pk\"\r\n        }\r\n      },\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"release_subscribers\": {\r\n      \"name\": \"release_subscribers\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"email\": {\r\n          \"name\": \"email\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"confirm_hash\": {\r\n          \"name\": \"confirm_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"unsubscribe_token\": {\r\n          \"name\": \"unsubscribe_token\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"confirmed_at\": {\r\n          \"name\": \"confirmed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"last_release\": {\r\n          \"name\": \"last_release\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"release_subscribers_unsubscribe_token_unique\": {\r\n          \"name\": \"release_subscribers_unsubscribe_token_unique\",\r\n          \"columns\": [\r\n            \"unsubscribe_token\"\r\n          ],\r\n          \"isUnique\": true\r\n        },\r\n        \"release_subscribers_confirmation\": {\r\n          \"name\": \"release_subscribers_confirmation\",\r\n          \"columns\": [\r\n            \"confirm_hash\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"reminders\": {\r\n      \"name\": \"reminders\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"kind\": {\r\n          \"name\": \"kind\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"time\": {\r\n          \"name\": \"time\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"timezone\": {\r\n          \"name\": \"timezone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"enabled\": {\r\n          \"name\": \"enabled\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 1\r\n        },\r\n        \"quiet_start\": {\r\n          \"name\": \"quiet_start\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'22:00'\"\r\n        },\r\n        \"quiet_end\": {\r\n          \"name\": \"quiet_end\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'07:00'\"\r\n        },\r\n        \"tone\": {\r\n          \"name\": \"tone\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'direct'\"\r\n        },\r\n        \"days_per_week\": {\r\n          \"name\": \"days_per_week\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 7\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"reminders_user\": {\r\n          \"name\": \"reminders_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_invites\": {\r\n      \"name\": \"scoreboard_invites\",\r\n      \"columns\": {\r\n        \"owner_id\": {\r\n          \"name\": \"owner_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"token_hash\": {\r\n          \"name\": \"token_hash\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"expires_at\": {\r\n          \"name\": \"expires_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_invites_token_hash_unique\": {\r\n          \"name\": \"scoreboard_invites_token_hash_unique\",\r\n          \"columns\": [\r\n            \"token_hash\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"scoreboard_links\": {\r\n      \"name\": \"scoreboard_links\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_a\": {\r\n          \"name\": \"user_a\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_b\": {\r\n          \"name\": \"user_b\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"scoreboard_links_a\": {\r\n          \"name\": \"scoreboard_links_a\",\r\n          \"columns\": [\r\n            \"user_a\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_b\": {\r\n          \"name\": \"scoreboard_links_b\",\r\n          \"columns\": [\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"scoreboard_links_pair\": {\r\n          \"name\": \"scoreboard_links_pair\",\r\n          \"columns\": [\r\n            \"user_a\",\r\n            \"user_b\"\r\n          ],\r\n          \"isUnique\": true\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"subscriptions\": {\r\n      \"name\": \"subscriptions\",\r\n      \"columns\": {\r\n        \"endpoint\": {\r\n          \"name\": \"endpoint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"data\": {\r\n          \"name\": \"data\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"subscriptions_user\": {\r\n          \"name\": \"subscriptions_user\",\r\n          \"columns\": [\r\n            \"user_id\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"system\": {\r\n      \"name\": \"system\",\r\n      \"columns\": {\r\n        \"key\": {\r\n          \"name\": \"key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"war_room_arsenals\": {\r\n      \"name\": \"war_room_arsenals\",\r\n      \"columns\": {\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"loadout\": {\r\n          \"name\": \"loadout\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"recipes\": {\r\n          \"name\": \"recipes\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'[]'\"\r\n        },\r\n        \"revision\": {\r\n          \"name\": \"revision\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": 0\r\n        },\r\n        \"updated_at\": {\r\n          \"name\": \"updated_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {}\r\n    },\r\n    \"workout_imports\": {\r\n      \"name\": \"workout_imports\",\r\n      \"columns\": {\r\n        \"workout_id\": {\r\n          \"name\": \"workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"idempotency_key\": {\r\n          \"name\": \"idempotency_key\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"fingerprint\": {\r\n          \"name\": \"fingerprint\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"digest_version\": {\r\n          \"name\": \"digest_version\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"account_data_epoch\": {\r\n          \"name\": \"account_data_epoch\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"created_at\": {\r\n          \"name\": \"created_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {},\r\n      \"foreignKeys\": {\r\n        \"workout_imports_workout_id_workouts_id_fk\": {\r\n          \"name\": \"workout_imports_workout_id_workouts_id_fk\",\r\n          \"tableFrom\": \"workout_imports\",\r\n          \"tableTo\": \"workouts\",\r\n          \"columnsFrom\": [\r\n            \"workout_id\"\r\n          ],\r\n          \"columnsTo\": [\r\n            \"id\"\r\n          ],\r\n          \"onDelete\": \"cascade\",\r\n          \"onUpdate\": \"no action\"\r\n        }\r\n      },\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"workout_imports_key\": {\r\n          \"name\": \"workout_imports_key\",\r\n          \"value\": \"length(\\\"workout_imports\\\".\\\"idempotency_key\\\")=64 AND \\\"workout_imports\\\".\\\"idempotency_key\\\" NOT GLOB '*[^0-9a-f]*'\"\r\n        },\r\n        \"workout_imports_fingerprint\": {\r\n          \"name\": \"workout_imports_fingerprint\",\r\n          \"value\": \"length(\\\"workout_imports\\\".\\\"fingerprint\\\")=64 AND \\\"workout_imports\\\".\\\"fingerprint\\\" NOT GLOB '*[^0-9a-f]*'\"\r\n        },\r\n        \"workout_imports_version\": {\r\n          \"name\": \"workout_imports_version\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"digest_version\\\")='integer' AND \\\"workout_imports\\\".\\\"digest_version\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"workout_imports_epoch\": {\r\n          \"name\": \"workout_imports_epoch\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"account_data_epoch\\\")='integer' AND \\\"workout_imports\\\".\\\"account_data_epoch\\\" BETWEEN 1 AND 9007199254740991\"\r\n        },\r\n        \"workout_imports_created\": {\r\n          \"name\": \"workout_imports_created\",\r\n          \"value\": \"typeof(\\\"workout_imports\\\".\\\"created_at\\\")='integer' AND \\\"workout_imports\\\".\\\"created_at\\\" BETWEEN 0 AND 9007199254740991\"\r\n        }\r\n      }\r\n    },\r\n    \"workouts\": {\r\n      \"name\": \"workouts\",\r\n      \"columns\": {\r\n        \"id\": {\r\n          \"name\": \"id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": true,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"user_id\": {\r\n          \"name\": \"user_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"mode\": {\r\n          \"name\": \"mode\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"goal\": {\r\n          \"name\": \"goal\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"started_at\": {\r\n          \"name\": \"started_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false\r\n        },\r\n        \"completed_at\": {\r\n          \"name\": \"completed_at\",\r\n          \"type\": \"integer\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"value\": {\r\n          \"name\": \"value\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"active\": {\r\n          \"name\": \"active\",\r\n          \"type\": \"real\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"source\": {\r\n          \"name\": \"source\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": true,\r\n          \"autoincrement\": false,\r\n          \"default\": \"'server'\"\r\n        },\r\n        \"client_workout_id\": {\r\n          \"name\": \"client_workout_id\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        },\r\n        \"competitive_status\": {\r\n          \"name\": \"competitive_status\",\r\n          \"type\": \"text\",\r\n          \"primaryKey\": false,\r\n          \"notNull\": false,\r\n          \"autoincrement\": false\r\n        }\r\n      },\r\n      \"indexes\": {\r\n        \"workouts_user_completed\": {\r\n          \"name\": \"workouts_user_completed\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"ux_workouts_acct_cwid\": {\r\n          \"name\": \"ux_workouts_acct_cwid\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"client_workout_id\"\r\n          ],\r\n          \"isUnique\": true,\r\n          \"where\": \"\\\"workouts\\\".\\\"client_workout_id\\\" IS NOT NULL\"\r\n        },\r\n        \"ix_workouts_authority\": {\r\n          \"name\": \"ix_workouts_authority\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"source\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        },\r\n        \"ix_workouts_rank\": {\r\n          \"name\": \"ix_workouts_rank\",\r\n          \"columns\": [\r\n            \"user_id\",\r\n            \"competitive_status\",\r\n            \"mode\",\r\n            \"completed_at\"\r\n          ],\r\n          \"isUnique\": false\r\n        }\r\n      },\r\n      \"foreignKeys\": {},\r\n      \"compositePrimaryKeys\": {},\r\n      \"uniqueConstraints\": {},\r\n      \"checkConstraints\": {\r\n        \"workouts_source\": {\r\n          \"name\": \"workouts_source\",\r\n          \"value\": \"\\\"workouts\\\".\\\"source\\\" IN ('server','guest_import')\"\r\n        },\r\n        \"workouts_competitive_status\": {\r\n          \"name\": \"workouts_competitive_status\",\r\n          \"value\": \"\\\"workouts\\\".\\\"competitive_status\\\" IS NULL OR \\\"workouts\\\".\\\"competitive_status\\\" IN ('accepted','pending','not_eligible')\"\r\n        }\r\n      }\r\n    }\r\n  },\r\n  \"views\": {},\r\n  \"enums\": {},\r\n  \"_meta\": {\r\n    \"schemas\": {},\r\n    \"tables\": {},\r\n    \"columns\": {}\r\n  },\r\n  \"internal\": {\r\n    \"indexes\": {}\r\n  }\r\n}","drizzle/meta/_journal.json":"{\r\n  \"version\": \"7\",\r\n  \"dialect\": \"sqlite\",\r\n  \"entries\": [\r\n    {\r\n      \"idx\": 0,\r\n      \"version\": \"6\",\r\n      \"when\": 1788977299750,\r\n      \"tag\": \"0000_new_cammi\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 1,\r\n      \"version\": \"6\",\r\n      \"when\": 1788987027951,\r\n      \"tag\": \"0001_careful_impossible_man\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 2,\r\n      \"version\": \"6\",\r\n      \"when\": 1788988831094,\r\n      \"tag\": \"0002_peaceful_living_lightning\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 3,\r\n      \"version\": \"6\",\r\n      \"when\": 1788997926956,\r\n      \"tag\": \"0003_dashing_meltdown\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 4,\r\n      \"version\": \"6\",\r\n      \"when\": 1789000724491,\r\n      \"tag\": \"0004_burly_boomerang\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 5,\r\n      \"version\": \"6\",\r\n      \"when\": 1789004382220,\r\n      \"tag\": \"0005_nice_the_enforcers\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 6,\r\n      \"version\": \"6\",\r\n      \"when\": 1789014708507,\r\n      \"tag\": \"0006_flawless_slayback\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 7,\r\n      \"version\": \"6\",\r\n      \"when\": 1789015990548,\r\n      \"tag\": \"0007_tan_mesmero\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 8,\r\n      \"version\": \"6\",\r\n      \"when\": 1789017138908,\r\n      \"tag\": \"0008_giant_charles_xavier\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 9,\r\n      \"version\": \"6\",\r\n      \"when\": 1789026398058,\r\n      \"tag\": \"0009_uneven_rhino\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 10,\r\n      \"version\": \"6\",\r\n      \"when\": 1789057331946,\r\n      \"tag\": \"0010_fixed_wrecker\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 11,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060000000,\r\n      \"tag\": \"0011_coach_army_entitlements\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 12,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060100000,\r\n      \"tag\": \"0012_coach_army_bindings\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 13,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060200000,\r\n      \"tag\": \"0013_account_pack_entitlements\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 14,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060300000,\r\n      \"tag\": \"0014_coach_army_authenticated_runs\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 15,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060400000,\r\n      \"tag\": \"0015_goals\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 16,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060500000,\r\n      \"tag\": \"0016_war_room_arsenal\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 17,\r\n      \"version\": \"6\",\r\n      \"when\": 1789060600000,\r\n      \"tag\": \"0017_coach_army_djscratch_verifier\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 18,\r\n      \"version\": \"6\",\r\n      \"when\": 1790031159443,\r\n      \"tag\": \"0018_account_data_epochs\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 19,\r\n      \"version\": \"6\",\r\n      \"when\": 1790035909178,\r\n      \"tag\": \"0019_workout_import_trust\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 20,\r\n      \"version\": \"6\",\r\n      \"when\": 1790039380366,\r\n      \"tag\": \"0020_coach_army_data_epochs\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 21,\r\n      \"version\": \"6\",\r\n      \"when\": 1790700000000,\r\n      \"tag\": \"0021_armie_letter_deliveries\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 22,\r\n      \"version\": \"6\",\r\n      \"when\": 1790700000001,\r\n      \"tag\": \"0022_performance_progression\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 23,\r\n      \"version\": \"6\",\r\n      \"when\": 1791244800000,\r\n      \"tag\": \"0025_spotify_connection\",\r\n      \"breakpoints\": true\r\n    },\r\n    {\r\n      \"idx\": 24,\r\n      \"version\": \"6\",\r\n      \"when\": 1791244800001,\r\n      \"tag\": \"0026_workout_paths\",\r\n      \"breakpoints\": true\r\n    }\r\n  ]\r\n}\r\n","drop-pod-opening.mjs":"﻿// Drop-pod pack opening: a full-screen space drop in the style of FPS pack openings (Siege Alpha Packs, Apex).\r\n// sky -> drop -> landed (steaming in a dirt mound) -> tap the pod -> opened (lid pops, beam) -> reveal card.\r\n// Served unbundled (external in scripts/build.mjs and scripts/dev.mjs) so three.js is only fetched when a pack\r\n// is opened (same pattern as food/pyramid-scanner.mjs). pods/drop-pod-<tier>.glb drop in with no code change;\r\n// until they exist the procedural canister below is the pod.\r\nexport const PHASES=Object.freeze(['sky','drop','landed','opened','reveal']);\r\nexport const startPhase=({reduced=false}={})=>reduced?'landed':'sky';\r\nconst EDGES={sky:{fall:'drop',land:'landed'},drop:{land:'landed'},landed:{tap:'opened'},opened:{revealed:'reveal',fail:'landed'}};\r\n// Pure state machine (unit-tested in node). skip jumps straight to the reveal; restart is \"Open another\".\r\nexport function advance(phase,event){\r\n if(event==='skip')return 'reveal';\r\n if(event==='restart')return phase==='reveal'?'sky':phase;\r\n return EDGES[phase]?.[event]??phase;\r\n}\r\n// Seconds: the sky, the fall, then the impact settling before the \"Tap the pod\" prompt appears.\r\nexport const TIERS=Object.freeze({\r\n uncommon:{color:'#76e356',sky:1.0,fall:2.1,settle:1.3,shake:.34,dust:1,sparks:1,rocks:10,rumble:0},\r\n rare:{color:'#4bafff',sky:1.0,fall:2.4,settle:1.5,shake:.52,dust:1.3,sparks:1.4,rocks:14,rumble:.03},\r\n legendary:{color:'#ff9c36',sky:1.8,fall:3.0,settle:1.5,shake:.85,dust:1.8,sparks:2.4,rocks:22,rumble:.07,gold:true,big:true,aftershock:.45},\r\n // Secret (from secret achievements): the longest build-up and biggest hit, a holographic kettlebell pod and holo sparks.\r\n secret:{color:'#b388ff',sky:2.4,fall:3.4,settle:1.7,shake:1,dust:2,sparks:3,rocks:26,rumble:.1,holo:true,big:true,aftershock:.55}\r\n});\r\nexport const promptAt=tier=>{const c=TIERS[tier]||TIERS.uncommon;return c.sky+c.fall+c.settle;};\r\n\r\nconst CSS=`\r\ndialog.drop-pod:focus{outline:none}\r\ndialog.drop-pod{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:0;border:0;background:#02030a;color:#fff7df;font:500 16px/1.4 system-ui;overflow:hidden;touch-action:none;user-select:none;-webkit-user-select:none}\r\ndialog.drop-pod::backdrop{background:#02030a}\r\n.drop-pod canvas{position:absolute;inset:0;width:100%;height:100%;display:block}\r\n.drop-pod-flash{position:absolute;inset:0;background:radial-gradient(circle at 50% 64%,#fff 0,color-mix(in srgb,var(--pack) 65%,#fff) 14%,color-mix(in srgb,var(--pack) 50%,transparent) 34%,transparent 62%);opacity:0;pointer-events:none}\r\n.drop-pod-flash.go{animation:dp-flash .4s ease-out;--k:.9}.drop-pod-flash.pop{animation:dp-flash .3s ease-out;--k:.5}\r\n@keyframes dp-flash{0%{opacity:var(--k,1)}100%{opacity:0}}\r\n.drop-pod-caption{position:absolute;left:0;right:0;top:calc(14% + env(safe-area-inset-top,0px));text-align:center;font:900 13px system-ui;letter-spacing:.42em;text-transform:uppercase;color:var(--pack);text-shadow:0 0 14px var(--pack);opacity:0;transition:opacity .4s;pointer-events:none}\r\n.drop-pod[data-phase=sky] .drop-pod-caption,.drop-pod[data-phase=drop] .drop-pod-caption{opacity:.95;animation:dp-blink .5s steps(2) infinite}\r\n.drop-pod[data-phase=drop] .drop-pod-caption{opacity:.7}\r\n@keyframes dp-blink{50%{opacity:.35}}\r\n.drop-pod-skip{position:absolute;top:calc(12px + env(safe-area-inset-top,0px));right:12px;min-height:44px;min-width:64px;border:1px solid #fff7df80;border-radius:22px;background:#0008;color:#fff7df;font:800 13px system-ui;letter-spacing:.1em;text-transform:uppercase;padding:0 16px;cursor:pointer;z-index:3}\r\n.drop-pod[data-phase=reveal] .drop-pod-skip{display:none}\r\n.drop-pod-hit{position:absolute;left:50%;top:60%;width:200px;height:260px;transform:translate(-50%,-50%);border:0;border-radius:50%;background:transparent;cursor:pointer;display:none;z-index:2;-webkit-tap-highlight-color:transparent}\r\n.drop-pod.ready .drop-pod-hit{display:block}\r\n.drop-pod-hit::after{content:\"\";position:absolute;inset:6%;border:3px solid var(--pack);border-radius:50%;box-shadow:0 0 22px var(--pack),inset 0 0 22px color-mix(in srgb,var(--pack) 45%,transparent);animation:dp-ring 1.1s ease-in-out infinite}\r\n@keyframes dp-ring{0%,100%{transform:scale(.92);opacity:.5}50%{transform:scale(1.04);opacity:1}}\r\n.drop-pod-prompt{position:absolute;left:0;right:0;bottom:calc(6% + env(safe-area-inset-bottom,0px));text-align:center;font:900 22px system-ui;letter-spacing:.2em;text-transform:uppercase;color:#fff7df;text-shadow:0 0 18px var(--pack),0 2px 0 #000;opacity:0;pointer-events:none;transition:opacity .25s}\r\n.drop-pod.ready .drop-pod-prompt{opacity:1;animation:dp-pulse 1s ease-in-out infinite}\r\n.drop-pod-prompt small{display:block;margin-top:6px;font:700 12px system-ui;letter-spacing:.12em;color:#ffb4a8;text-shadow:none;min-height:1em}\r\n@keyframes dp-pulse{50%{transform:scale(1.06)}}\r\n.drop-pod-card{position:absolute;left:50%;top:9%;width:min(calc(100vw - 36px),340px);max-height:44%;overflow:auto;box-sizing:border-box;transform:translate(-50%,46vh) scale(.5);opacity:0;pointer-events:none;text-align:center;border:2px solid var(--pack);border-radius:20px;background:radial-gradient(circle at 50% 20%,color-mix(in srgb,var(--pack) 30%,#21152e),#100d1b 75%);box-shadow:0 0 50px color-mix(in srgb,var(--pack) 55%,transparent),0 18px 40px #000b;padding:16px 16px 18px;transition:transform .9s cubic-bezier(.2,1.25,.3,1),opacity .35s}\r\n.drop-pod[data-phase=reveal] .drop-pod-card{transform:translate(-50%,0) scale(1);opacity:1;pointer-events:auto}\r\n.drop-pod-card .eyebrow{margin:0;color:var(--pack);font:800 12px system-ui;letter-spacing:.24em;text-transform:uppercase}\r\n.drop-pod-card strong{display:block;margin-top:6px;font:900 24px/1.15 system-ui;color:#fff7df;text-shadow:0 0 18px var(--pack)}\r\n.drop-pod-card small{display:block;opacity:.85;text-transform:uppercase;letter-spacing:.12em;font-size:12px;margin-top:4px}\r\n.drop-pod-item+.drop-pod-item{margin-top:12px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--pack) 40%,transparent)}\r\n.drop-pod-swatches{display:flex;justify-content:center;gap:6px;margin-top:10px}.drop-pod-swatches i{width:30px;height:30px;border:2px solid #fff7df;border-radius:5px}\r\n.drop-pod[data-reward-type=texture]:not([data-tier=secret]) .drop-pod-card::before{content:\"\";position:absolute;inset:-60%;z-index:-1;background:conic-gradient(from 0deg,transparent 0 10%,#ffd36a55 12%,transparent 16% 35%,#ffd36a44 37%,transparent 41% 60%,#ffd36a55 62%,transparent 66% 85%,#ffd36a44 87%,transparent 91%);animation:dp-spin 9s linear infinite}\r\n.drop-pod[data-reward-type=texture]:not([data-tier=secret]) .drop-pod-card{overflow:visible;isolation:isolate}\r\n.drop-pod[data-reward-type=texture]:not([data-tier=secret]) .drop-pod-card strong{background:linear-gradient(100deg,#fff7df 20%,#ffd36a 45%,#fff 50%,#ffd36a 55%,#fff7df 80%);background-size:250% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:none;animation:dp-shimmer 2.6s linear infinite}\r\n@keyframes dp-spin{to{transform:rotate(1turn)}}@keyframes dp-shimmer{to{background-position:-250% 0}}\r\n.drop-pod[data-tier=secret] .drop-pod-card{border-color:transparent;background:radial-gradient(circle at 50% 20%,#3a2160,#0d0b1c 75%) padding-box,linear-gradient(120deg,#3ff5ff,#b388ff,#ff4bd8,#ffd36a,#3ff5ff) border-box;background-size:auto,300% 100%;animation:dp-iri 4s linear infinite;box-shadow:0 0 50px #b388ff88,0 0 90px #3ff5ff33,0 18px 40px #000b}\r\n.drop-pod[data-tier=secret] .drop-pod-card .eyebrow{font:900 30px/1 system-ui;letter-spacing:.32em;margin:2px 0 4px;background:linear-gradient(100deg,#3ff5ff,#b388ff 30%,#ff4bd8 55%,#ffd36a 80%,#3ff5ff);background-size:250% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:dp-shimmer 3s linear infinite;filter:drop-shadow(0 0 10px #b388ffaa)}\r\n.drop-pod[data-tier=secret] .drop-pod-caption{background:linear-gradient(90deg,#3ff5ff,#ff4bd8,#ffd36a,#3ff5ff);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:none;filter:drop-shadow(0 0 8px #b388ff)}\r\n@keyframes dp-iri{to{background-position:0 0,-300% 0}}\r\n.drop-pod-actions{position:absolute;left:0;right:0;bottom:calc(5% + env(safe-area-inset-bottom,0px));display:flex;gap:10px;justify-content:center;opacity:0;pointer-events:none;transition:opacity .4s .5s}\r\n.drop-pod[data-phase=reveal] .drop-pod-actions{opacity:1;pointer-events:auto}\r\n.drop-pod-actions button{min-height:48px;min-width:120px;border:2px solid var(--pack);border-radius:14px;background:var(--pack);color:#15101c;font:800 15px system-ui;padding:8px 20px;cursor:pointer}\r\n.drop-pod-actions button.secondary{background:#0009;color:#fff7df}\r\n.drop-pod button:focus-visible{outline:3px solid #fff;outline-offset:3px}.drop-pod .drop-pod-hit:focus-visible{outline:none}\r\n.drop-pod-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\r\n@media(prefers-reduced-motion:reduce){.drop-pod-flash.go,.drop-pod-flash.pop{animation-duration:.15s}.drop-pod-card{transition:opacity .2s;transform:translate(-50%,0) scale(1)}.drop-pod[data-reward-type=texture]:not([data-tier=secret]) .drop-pod-card::before,.drop-pod[data-reward-type=texture]:not([data-tier=secret]) .drop-pod-card strong,.drop-pod[data-tier=secret] .drop-pod-card,.drop-pod[data-tier=secret] .drop-pod-card .eyebrow{animation:none}.drop-pod-hit::after,.drop-pod.ready .drop-pod-prompt{animation:none}}\r\n`;\r\n\r\nconst clamp=(v,a=0,b=1)=>Math.min(b,Math.max(a,v)),lerp=(a,b,k)=>a+(b-a)*k,smooth=k=>k*k*(3-2*k);\r\nconst easeOutBack=k=>{const c=1.9;return 1+(c+1)*Math.pow(k-1,3)+c*Math.pow(k-1,2);};\r\nconst rnd=(a=0,b=1)=>a+Math.random()*(b-a);\r\nconst hash=(x,y)=>{const s=Math.sin(x*127.1+y*311.7)*43758.5453;return s-Math.floor(s);};\r\nconst vnoise=(x,y)=>{const ix=Math.floor(x),iy=Math.floor(y),fx=smooth(x-ix),fy=smooth(y-iy);return lerp(lerp(hash(ix,iy),hash(ix+1,iy),fx),lerp(hash(ix,iy+1),hash(ix+1,iy+1),fx),fy);};\r\nconst MOUND_R=3.7;\r\n// Height of the dirt mound the pod lands in (also where thrown rocks and steam vents sit).\r\nconst moundHeight=(r,theta)=>{\r\n const s=clamp((r-.62)/(MOUND_R-.62)),base=(r<.62?1:1.05*Math.pow(1-s,1.4))+.3*Math.exp(-Math.pow((r-1.5)/.55,2));\r\n return Math.max(0,base*(.72+.5*vnoise(theta*2.2+r*1.3,r*2.1))+.1*(vnoise(theta*6,r*5)-.5)*(1-s));\r\n};\r\n\r\nconst VERT=`attribute float aSize;attribute float aAlpha;attribute vec3 aColor;uniform float uScale;varying float vA;varying vec3 vC;\r\nvoid main(){vec4 mv=modelViewMatrix*vec4(position,1.);gl_Position=projectionMatrix*mv;gl_PointSize=max(.0,aSize*uScale/max(.1,-mv.z));vA=aAlpha;vC=aColor;}`;\r\nconst FRAG=`uniform sampler2D uTex;varying float vA;varying vec3 vC;\r\nvoid main(){float a=texture2D(uTex,gl_PointCoord).a*vA;if(a<.004)discard;gl_FragColor=vec4(vC,a);\r\n#include <colorspace_fragment>\r\n}`;\r\nconst BEAM_VERT=`varying vec2 vUv;varying float vE;void main(){vUv=uv;vec4 mv=modelViewMatrix*vec4(position,1.);vE=abs(dot(normalize(normalMatrix*normal),normalize(-mv.xyz)));gl_Position=projectionMatrix*mv;}`;\r\nconst BEAM_FRAG=`uniform vec3 uColor;uniform float uOpacity;uniform float uPow;varying vec2 vUv;varying float vE;\r\nvoid main(){float fade=pow(clamp(1.-vUv.y,0.,1.),uPow);float edge=pow(clamp(vE,0.,1.),1.4);float a=fade*edge*uOpacity;gl_FragColor=vec4(mix(vec3(1.),uColor,clamp(vUv.y*1.2,0.,1.))*1.15,a);\r\n#include <colorspace_fragment>\r\n}`;\r\n\r\nexport function glowTexture(THREE,inner='255,255,255',stops=[[0,1],[.25,.55],[.6,.14],[1,0]]){\r\n const c=document.createElement('canvas');c.width=c.height=128;const x=c.getContext('2d'),g=x.createRadialGradient(64,64,0,64,64,64);\r\n for(const [k,a] of stops)g.addColorStop(k,`rgba(${inner},${a})`);x.fillStyle=g;x.fillRect(0,0,128,128);\r\n const t=new THREE.CanvasTexture(c);t.colorSpace=THREE.SRGBColorSpace;return t;\r\n}\r\n// A lumpy puff: several soft blobs so steam and dust do not read as perfect discs.\r\nfunction puffTexture(THREE){\r\n const c=document.createElement('canvas');c.width=c.height=128;const x=c.getContext('2d');\r\n for(let i=0;i<9;i++){const px=64+rnd(-20,20),py=64+rnd(-20,20),r=rnd(26,44),g=x.createRadialGradient(px,py,0,px,py,r);g.addColorStop(0,'rgba(255,255,255,.55)');g.addColorStop(.6,'rgba(255,255,255,.18)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,128,128);}\r\n const t=new THREE.CanvasTexture(c);t.colorSpace=THREE.SRGBColorSpace;return t;\r\n}\r\n\r\nclass Pool{\r\n constructor(THREE,n,{additive=false,tex,uniforms}){\r\n  this.n=n;this.i=0;this.THREE=THREE;this.gravity=0;this.drag=0;this.wobble=0;\r\n  this.pos=new Float32Array(n*3);this.vel=new Float32Array(n*3);this.size=new Float32Array(n);this.alpha=new Float32Array(n);this.col=new Float32Array(n*3);\r\n  this.age=new Float32Array(n).fill(9);this.life=new Float32Array(n).fill(1);this.s0=new Float32Array(n);this.s1=new Float32Array(n);this.a0=new Float32Array(n);this.ph=new Float32Array(n);\r\n  const geo=this.geo=new THREE.BufferGeometry();\r\n  for(const [name,arr,k] of [['position',this.pos,3],['aSize',this.size,1],['aAlpha',this.alpha,1],['aColor',this.col,3]])geo.setAttribute(name,new THREE.BufferAttribute(arr,k).setUsage(THREE.DynamicDrawUsage));\r\n  this.mat=new THREE.ShaderMaterial({vertexShader:VERT,fragmentShader:FRAG,uniforms:{uTex:{value:tex},uScale:uniforms.uScale},transparent:true,depthWrite:false,blending:additive?THREE.AdditiveBlending:THREE.NormalBlending});\r\n  this.points=new THREE.Points(geo,this.mat);this.points.frustumCulled=false;\r\n }\r\n emit(x,y,z,vx,vy,vz,life,s0,s1,color,alpha){\r\n  const i=this.i;this.i=(i+1)%this.n;const p=i*3;\r\n  this.pos[p]=x;this.pos[p+1]=y;this.pos[p+2]=z;this.vel[p]=vx;this.vel[p+1]=vy;this.vel[p+2]=vz;\r\n  this.life[i]=life;this.age[i]=0;this.s0[i]=s0;this.s1[i]=s1;this.a0[i]=alpha;this.ph[i]=Math.random()*6.28;\r\n  this.col[p]=color.r;this.col[p+1]=color.g;this.col[p+2]=color.b;\r\n }\r\n update(dt){\r\n  const {pos,vel,age,life,size,alpha,s0,s1,a0,ph}=this,dr=Math.exp(-this.drag*dt);\r\n  for(let i=0;i<this.n;i++){\r\n   if(age[i]>=life[i]){alpha[i]=0;size[i]=0;continue;}\r\n   age[i]+=dt;const k=Math.min(1,age[i]/life[i]),p=i*3;\r\n   vel[p+1]+=this.gravity*dt;vel[p]*=dr;vel[p+1]*=dr;vel[p+2]*=dr;\r\n   if(this.wobble)vel[p]+=Math.sin(age[i]*2.1+ph[i])*this.wobble*dt;\r\n   pos[p]+=vel[p]*dt;pos[p+1]+=vel[p+1]*dt;pos[p+2]+=vel[p+2]*dt;\r\n   size[i]=lerp(s0[i],s1[i],k);alpha[i]=a0[i]*Math.min(1,k*9)*Math.pow(1-k,1.25);\r\n  }\r\n  for(const name of ['position','aSize','aAlpha','aColor'])this.geo.getAttribute(name).needsUpdate=true;\r\n }\r\n}\r\n\r\n// The GLBs are untextured, so the pod's look is set here: dark metal in the tier colour with a glowing fresnel rim so it\r\n// reads at night (Legendary gold); Secret is a hologram (iridescent rim, rising scanlines, ~75% opacity, flicker).\r\nfunction tierMaterial(THREE,name){\r\n const c=new THREE.Color(TIERS[name]?.color||TIERS.uncommon.color),gold=name==='legendary';\r\n const m=new THREE.MeshStandardMaterial({color:gold?new THREE.Color(0xb07818):c.clone().multiplyScalar(.13),metalness:gold?.85:.7,roughness:gold?.3:.35,emissive:c,emissiveIntensity:.02});\r\n m.onBeforeCompile=sh=>{sh.uniforms.uRim={value:c.clone().multiplyScalar(gold?.9:.6)};\r\n  sh.fragmentShader='uniform vec3 uRim;\\n'+sh.fragmentShader.replace('#include <emissivemap_fragment>','#include <emissivemap_fragment>\\ntotalEmissiveRadiance+=uRim*pow(1.-abs(dot(normalize(vNormal),normalize(vViewPosition))),2.2);');};\r\n return m;\r\n}\r\nconst HOLO_VERT=`uniform float uTime;varying vec3 vN;varying vec3 vV;varying float vY;\r\nvoid main(){vec3 p=position;float g=step(.93,fract(sin(floor(uTime*9.)*91.7)*4375.5));p.x+=g*.03*sin(position.y*60.+uTime*80.);\r\nvec4 mv=modelViewMatrix*vec4(p,1.);vN=normalize(normalMatrix*normal);vV=normalize(-mv.xyz);vY=(modelMatrix*vec4(p,1.)).y;gl_Position=projectionMatrix*mv;}`;\r\nconst HOLO_FRAG=`uniform float uTime;uniform float uOpacity;uniform float uRimOnly;varying vec3 vN;varying vec3 vV;varying float vY;\r\nvec3 iri(float h){h=fract(h)*3.;vec3 a=vec3(.25,.95,1.),b=vec3(1.,.3,.85),c=vec3(1.,.82,.35);return h<1.?mix(a,b,h):h<2.?mix(b,c,h-1.):mix(c,a,h-2.);}\r\nvoid main(){float f=1.-abs(dot(normalize(vN),normalize(vV)));float rim=pow(f,1.6);\r\nvec3 hue=iri(f*1.3+vY*.25+uTime*.12);\r\nfloat scan=smoothstep(.55,1.,sin((vY*22.-uTime*3.)*3.1416)*.5+.5);\r\nfloat fine=.85+.15*sin((vY*140.-uTime*9.)*3.1416);\r\nfloat flick=.9+.06*sin(uTime*31.)+.04*sin(uTime*17.3);float g=step(.95,fract(sin(floor(uTime*9.)*91.7)*4375.5));flick*=1.-.35*g;\r\nvec3 col=(mix(vec3(.16,.05,.4),hue,.45+.55*rim)*(.7+.8*rim)+hue*scan*.55)*fine*flick;\r\nfloat a=uRimOnly>.5?rim*flick:uOpacity*(.85+.15*rim+.15*scan)*flick;\r\ngl_FragColor=vec4(col,a);\r\n#include <colorspace_fragment>\r\n}`;\r\nfunction holoMaterials(THREE,uTime){\r\n const mk=(rimOnly,extra)=>new THREE.ShaderMaterial({vertexShader:HOLO_VERT,fragmentShader:HOLO_FRAG,uniforms:{uTime,uOpacity:{value:.75},uRimOnly:{value:rimOnly}},transparent:true,...extra});\r\n return {body:mk(0,{depthWrite:true}),glow:mk(1,{depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.BackSide})};\r\n}\r\nfunction loadGlb(THREE,tier){\r\n return (async()=>{\r\n  try{\r\n   const {GLTFLoader}=await import('three/addons/loaders/GLTFLoader.js');\r\n   const gltf=await Promise.race([new GLTFLoader().loadAsync(new URL(`./pods/drop-pod-${tier}.glb`,import.meta.url).href),new Promise((_,no)=>setTimeout(no,7000))]);\r\n   const model=gltf.scene,box=new THREE.Box3().setFromObject(model),size=box.getSize(new THREE.Vector3()),mid=box.getCenter(new THREE.Vector3());\r\n   const big=Math.max(size.x,size.y,size.z);if(!(big>0))return null;\r\n   // Normalise by the largest side so a pod modelled lying down keeps its pose; base sits on y=0.\r\n   const k=2.4/big;model.scale.setScalar(k);model.position.set(-mid.x*k,-box.min.y*k,-mid.z*k);\r\n   const holder=new THREE.Group();holder.add(model);holder.userData={top:size.y*k,radius:Math.min(size.x,size.z)*k*.5};return holder;\r\n  }catch{return null;}\r\n })();\r\n}\r\n\r\n// The procedural pod: chunky canister, swept fins, tier-colour paint and glowing seams, hinged lid. Origin at its base, 2.4 tall.\r\nfunction buildFallbackPod(THREE,tier){\r\n const color=new THREE.Color(tier.color),group=new THREE.Group(),parts={};\r\n const hull=new THREE.MeshStandardMaterial({vertexColors:true,metalness:.82,roughness:.42});\r\n const dark=new THREE.MeshStandardMaterial({color:0x1b2029,metalness:.85,roughness:.5});\r\n const paint=new THREE.MeshStandardMaterial({color,metalness:.55,roughness:.38,emissive:color,emissiveIntensity:.07});\r\n const glow=parts.glow=new THREE.MeshStandardMaterial({color:0x111111,emissive:color,emissiveIntensity:1.6,metalness:0,roughness:.6});\r\n const profile=[[0,0],[.34,0],[.46,.07],[.55,.3],[.61,.7],[.63,1.15],[.61,1.6],[.56,1.92],[.5,2.1],[.46,2.14]].map(([r,y])=>new THREE.Vector2(r,y));\r\n const body=new THREE.LatheGeometry(profile,40),pos=body.attributes.position,cols=new Float32Array(pos.count*3),metal=new THREE.Color(0x56627a),char=new THREE.Color(0x120d0c),scorch=new THREE.Color();\r\n for(let i=0;i<pos.count;i++){const y=pos.getY(i),k=clamp((y-.15)/1.1)+.12*(vnoise(pos.getX(i)*6,y*5+pos.getZ(i)*4)-.5);scorch.copy(char).lerp(metal,clamp(k));cols[i*3]=scorch.r;cols[i*3+1]=scorch.g;cols[i*3+2]=scorch.b;}\r\n body.setAttribute('color',new THREE.BufferAttribute(cols,3));\r\n group.add(new THREE.Mesh(body,hull));\r\n // engine bell, bands, seams\r\n const bell=new THREE.Mesh(new THREE.CylinderGeometry(.3,.2,.28,24,1,true),dark);bell.position.y=-.06;group.add(bell);\r\n const nozzle=parts.nozzle=new THREE.Mesh(new THREE.CircleGeometry(.22,24),new THREE.MeshBasicMaterial({color:0xffb060}));nozzle.rotation.x=Math.PI/2;nozzle.position.y=-.2;group.add(nozzle);\r\n for(const [y,r,tube,m] of [[.2,.55,.06,dark],[1.85,.59,.07,dark],[.62,.625,.028,glow],[1.42,.625,.028,glow],[1.08,.64,.05,paint]]){const t=new THREE.Mesh(new THREE.TorusGeometry(r,tube,10,48),m);t.rotation.x=Math.PI/2;t.position.y=y;group.add(t);}\r\n for(let i=0;i<8;i++){const a=i/8*Math.PI*2+.2,s=new THREE.Mesh(new THREE.BoxGeometry(.035,.74,.03),glow);s.position.set(Math.cos(a)*.625,1.02,Math.sin(a)*.625);s.rotation.y=-a;group.add(s);}\r\n for(let i=0;i<6;i++){const a=i/6*Math.PI*2,p=new THREE.Mesh(new THREE.BoxGeometry(.28,.5,.05),dark);p.position.set(Math.cos(a)*.625,.75+(i%2)*.55,Math.sin(a)*.625);p.rotation.y=-a;group.add(p);}\r\n // porthole\r\n const win=new THREE.Mesh(new THREE.CircleGeometry(.14,20),new THREE.MeshBasicMaterial({color}));win.position.set(0,1.22,.658);group.add(win);\r\n const rim=new THREE.Mesh(new THREE.TorusGeometry(.16,.03,8,24),dark);rim.position.copy(win.position);group.add(rim);\r\n // fins\r\n const fs=new THREE.Shape();fs.moveTo(.5,.18);fs.lineTo(1.12,-.2);fs.lineTo(1.18,.04);fs.lineTo(.68,1.02);fs.lineTo(.56,1.02);fs.closePath();\r\n const fin=new THREE.ExtrudeGeometry(fs,{depth:.07,bevelEnabled:true,bevelSize:.02,bevelThickness:.02,bevelSegments:1});fin.translate(0,0,-.035);\r\n for(let i=0;i<4;i++){const f=new THREE.Mesh(fin,paint),tip=new THREE.Mesh(new THREE.BoxGeometry(.05,.05,.1),glow);tip.position.set(1.15,-.08,0);const h=new THREE.Group();h.add(f,tip);h.rotation.y=i/4*Math.PI*2+Math.PI/4;group.add(h);}\r\n // hatch core (the light inside) and hinged lid\r\n const core=parts.core=new THREE.Mesh(new THREE.CircleGeometry(.42,28),new THREE.MeshBasicMaterial({color:new THREE.Color(1,1,1).lerp(color,.35)}));core.rotation.x=-Math.PI/2;core.position.y=2.1;group.add(core);\r\n const hinge=parts.hinge=new THREE.Group();hinge.position.set(-.46,2.13,0);group.add(hinge);\r\n const lidProfile=[[0,.0],[.46,.0],[.47,.06],[.43,.14],[.3,.24],[.15,.31],[0,.34]].map(([r,y])=>new THREE.Vector2(r,y));\r\n const lid=new THREE.Mesh(new THREE.LatheGeometry(lidProfile,36),paint);lid.position.x=.46;hinge.add(lid);\r\n const lidRing=new THREE.Mesh(new THREE.TorusGeometry(.4,.025,8,32),glow);lidRing.rotation.x=Math.PI/2;lidRing.position.set(.46,.04,0);hinge.add(lidRing);\r\n const hook=new THREE.Mesh(new THREE.BoxGeometry(.12,.1,.2),dark);hook.position.set(-.02,.02,0);hinge.add(hook);\r\n const handle=new THREE.Mesh(new THREE.TorusGeometry(.09,.025,8,16,Math.PI),dark);handle.position.set(.46,.33,0);hinge.add(handle);\r\n group.userData=parts;return group;\r\n}\r\n\r\nexport function buildTerrain(THREE,tier){\r\n const g=new THREE.Group(),color=new THREE.Color(tier.color);\r\n const size=170,seg=70,geo=new THREE.PlaneGeometry(size,size,seg,seg);geo.rotateX(-Math.PI/2);\r\n const p=geo.attributes.position,cols=new Float32Array(p.count*3),c=new THREE.Color();\r\n for(let i=0;i<p.count;i++){const x=p.getX(i),z=p.getZ(i),r=Math.hypot(x,z),h=(vnoise(x*.09,z*.09)-.5)*1.4*clamp((r-5)/12)+(vnoise(x*.4,z*.4)-.5)*.12*clamp((r-4)/6);p.setY(i,h);c.set(0x2c2430).lerp(new THREE.Color(0x3a2e33),vnoise(x*.2+7,z*.2));c.multiplyScalar(.85+.3*vnoise(x*1.3,z*1.3));cols[i*3]=c.r;cols[i*3+1]=c.g;cols[i*3+2]=c.b;}\r\n geo.setAttribute('color',new THREE.BufferAttribute(cols,3));geo.computeVertexNormals();\r\n g.add(new THREE.Mesh(geo,new THREE.MeshStandardMaterial({vertexColors:true,roughness:.96,metalness:0})));\r\n // mound\r\n const rings=26,segs=72,pts=[],idx=[],mc=[];\r\n for(let ri=0;ri<=rings;ri++){const r=ri===0?0:Math.pow(ri/rings,.85)*MOUND_R;for(let si=0;si<segs;si++){const th=si/segs*Math.PI*2,rr=r*(1+.08*(vnoise(th*3,r)-.5)),h=ri===rings?0:moundHeight(rr,th)+(ri===rings?0:0);pts.push(Math.cos(th)*rr,h,Math.sin(th)*rr);const k=clamp(h/1.2);const cc=new THREE.Color(0x4a3426).lerp(new THREE.Color(0x9c7a58),k*.7+.3*vnoise(th*9,r*7));mc.push(cc.r,cc.g,cc.b);}}\r\n for(let ri=0;ri<rings;ri++)for(let si=0;si<segs;si++){const a=ri*segs+si,b=ri*segs+(si+1)%segs,c2=(ri+1)*segs+si,d=(ri+1)*segs+(si+1)%segs;idx.push(a,c2,b,b,c2,d);}\r\n const mg=new THREE.BufferGeometry();mg.setAttribute('position',new THREE.Float32BufferAttribute(pts,3));mg.setAttribute('color',new THREE.Float32BufferAttribute(mc,3));mg.setIndex(idx);mg.computeVertexNormals();\r\n const mound=new THREE.Mesh(mg,new THREE.MeshStandardMaterial({vertexColors:true,roughness:1,flatShading:true}));mound.position.y=.02;mound.scale.y=.001;mound.visible=false;g.add(mound);\r\n // scorch under the impact\r\n const sc=document.createElement('canvas');sc.width=sc.height=128;const sx=sc.getContext('2d'),sg=sx.createRadialGradient(64,64,6,64,64,64);sg.addColorStop(0,'rgba(0,0,0,.95)');sg.addColorStop(.5,'rgba(10,6,4,.75)');sg.addColorStop(1,'rgba(0,0,0,0)');sx.fillStyle=sg;sx.fillRect(0,0,128,128);\r\n const scorch=new THREE.Mesh(new THREE.PlaneGeometry(10,10),new THREE.MeshBasicMaterial({map:new THREE.CanvasTexture(sc),transparent:true,depthWrite:false,opacity:0}));scorch.rotation.x=-Math.PI/2;scorch.position.y=.03;g.add(scorch);\r\n // far ridge so the horizon has depth\r\n const ridge=new THREE.CylinderGeometry(70,70,9,96,1,true),rp=ridge.attributes.position;\r\n for(let i=0;i<rp.count;i++)if(rp.getY(i)>0){const a=Math.atan2(rp.getZ(i),rp.getX(i));rp.setY(i,3+vnoise(a*5,1)*9+vnoise(a*17,3)*3);}\r\n const rm=new THREE.Mesh(ridge,new THREE.MeshBasicMaterial({color:0x0b0f24,side:THREE.BackSide,fog:true}));rm.position.y=-3.5;g.add(rm);\r\n const rim=new THREE.Mesh(new THREE.CylinderGeometry(52,52,5,80,1,true),new THREE.MeshBasicMaterial({color:0x0f1330,side:THREE.BackSide}));\r\n const rp2=rim.geometry.attributes.position;for(let i=0;i<rp2.count;i++)if(rp2.getY(i)>0){const a=Math.atan2(rp2.getZ(i),rp2.getX(i));rp2.setY(i,1.5+vnoise(a*8,9)*5);}\r\n rim.position.y=-2.4;g.add(rim);\r\n return {group:g,mound,scorch};\r\n}\r\n\r\nexport function makeSky(THREE,tier){\r\n const c=document.createElement('canvas');c.width=4;c.height=512;const x=c.getContext('2d'),g=x.createLinearGradient(0,0,0,512),tc=new THREE.Color(tier.color);\r\n const tint=(k,base)=>'#'+new THREE.Color(base).lerp(tc,k).getHexString();\r\n g.addColorStop(0,'#01020a');g.addColorStop(.45,'#070b24');g.addColorStop(.72,tint(.14,0x141a4a));g.addColorStop(.86,tint(.28,0x2a2150));g.addColorStop(1,'#1a1228');x.fillStyle=g;x.fillRect(0,0,4,512);\r\n const t=new THREE.CanvasTexture(c);t.colorSpace=THREE.SRGBColorSpace;return t;\r\n}\r\n\r\nexport function makeStars(THREE,uniforms){\r\n const layers=[];\r\n for(const [n,R,size,tw] of [[800,95,1.5,.3],[360,70,2.3,.5],[120,45,3.6,.8]]){\r\n  const pos=new Float32Array(n*3),sz=new Float32Array(n),ph=new Float32Array(n),col=new Float32Array(n*3),cc=new THREE.Color();\r\n  for(let i=0;i<n;i++){const u=Math.random(),th=Math.random()*Math.PI*2,y=Math.pow(u,.8)*.97+.03,r=Math.sqrt(1-y*y);pos[i*3]=Math.cos(th)*r*R;pos[i*3+1]=y*R*.75;pos[i*3+2]=-Math.abs(Math.sin(th))*r*R*.9-R*.12;sz[i]=size*rnd(.5,1.2);ph[i]=Math.random()*6.28;cc.setHSL(rnd(.55,.68)*(Math.random()<.2?.13:1),.5,rnd(.8,.95));col.set([cc.r,cc.g,cc.b],i*3);}\r\n  const geo=new THREE.BufferGeometry();geo.setAttribute('position',new THREE.BufferAttribute(pos,3));geo.setAttribute('aSize',new THREE.BufferAttribute(sz,1));geo.setAttribute('aPh',new THREE.BufferAttribute(ph,1));geo.setAttribute('aColor',new THREE.BufferAttribute(col,3));\r\n  const mat=new THREE.ShaderMaterial({uniforms:{uTime:uniforms.uTime,uDpr:uniforms.uDpr,uTw:{value:tw}},transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,\r\n   vertexShader:`attribute float aSize;attribute float aPh;attribute vec3 aColor;uniform float uTime;uniform float uDpr;uniform float uTw;varying vec3 vC;varying float vT;void main(){vT=.65+.35*sin(uTime*(1.5+aPh*.4)+aPh*9.)*uTw/.5*.5+(1.-uTw)*.1;vC=aColor;gl_PointSize=aSize*uDpr*(.85+.3*vT);gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`,\r\n   fragmentShader:`varying vec3 vC;varying float vT;void main(){float d=length(gl_PointCoord-.5)*2.;float a=smoothstep(1.,0.,d);a*=a;gl_FragColor=vec4(vC*(.8+.5*vT),a);\r\n#include <colorspace_fragment>\r\n}`});\r\n  const pts=new THREE.Points(geo,mat);pts.frustumCulled=false;pts.renderOrder=-3;layers.push({pts,depth:R});\r\n }\r\n return layers;\r\n}\r\n\r\nfunction createWorld(THREE,renderer,tierName,{reduced,onEvent}){\r\n const tier=TIERS[tierName]||TIERS.uncommon,color=new THREE.Color(tier.color),gold=new THREE.Color(0xffd36a),white=new THREE.Color(0xfff1c8),hot=new THREE.Color(0xff8a3a);\r\n // Spark colour: gold flecks for Legendary, holographic cyan/violet/magenta/gold for Secret, else white-to-tier.\r\n const holoHues=[0x3ff5ff,0xb388ff,0xff4bd8,0xffd36a].map(h=>new THREE.Color(h));\r\n const sparkCol=(mix=.9)=>tier.holo?holoHues[(Math.random()*4)|0]:tier.gold&&Math.random()<.6?gold:white.clone().lerp(color,Math.random()*mix);\r\n const scene=new THREE.Scene(),aspect0=Math.max(.3,innerWidth/innerHeight);\r\n const camera=new THREE.PerspectiveCamera(50,aspect0,.1,400);\r\n const uniforms={uScale:{value:1},uTime:{value:0},uDpr:{value:renderer.getPixelRatio()}};\r\n const owned=[];const own=o=>(owned.push(o),o);\r\n scene.background=own(makeSky(THREE,tier));\r\n scene.fog=new THREE.Fog(0x1a1228,28,95);\r\n // lighting: cool moon rim from behind-left, tier rim from behind-right, tier point light on the pod, warm impact light\r\n scene.add(new THREE.HemisphereLight(0x4a5aa0,0x2a1a1c,.75));\r\n const moon=new THREE.DirectionalLight(0xa9c2ff,2.2);moon.position.set(-9,11,-9);scene.add(moon);\r\n const rimR=new THREE.DirectionalLight(color.clone().lerp(white,.35),.45);rimR.position.set(9,5,-7);scene.add(rimR);\r\n const front=new THREE.DirectionalLight(0x6a7ab8,.55);front.position.set(2,3,10);scene.add(front);\r\n const podLight=new THREE.PointLight(color.clone().lerp(white,.4),0,16,1.6);podLight.position.set(0,2.2,2.6);scene.add(podLight);\r\n const impactLight=new THREE.PointLight(0xff8a3a,0,14,1.8);impactLight.position.set(0,.6,1.6);scene.add(impactLight);\r\n const hatchLight=new THREE.PointLight(color,0,12,1.4);scene.add(hatchLight);\r\n // environment reflections so the metal reads: dark sky, a tier-coloured strip and a cool softbox\r\n {const env=new THREE.Scene(),sky=new THREE.Mesh(new THREE.SphereGeometry(20,24,12),new THREE.MeshBasicMaterial({color:0x10142c,side:THREE.BackSide}));env.add(sky);\r\n  for(const [x,y,z,w,h,c] of [[-8,8,-6,10,3,0xa9c2ff],[9,3,-5,2.5,10,color.getHex()],[0,12,4,14,2,0x5566aa],[0,2,10,12,3,0x333a60]]){const b=new THREE.Mesh(new THREE.PlaneGeometry(w,h),new THREE.MeshBasicMaterial({color:c,side:THREE.DoubleSide}));b.position.set(x,y,z);b.lookAt(0,0,0);env.add(b);}\r\n  const pm=new THREE.PMREMGenerator(renderer);scene.environment=own(pm.fromScene(env,.03).texture);scene.environmentIntensity=.9;pm.dispose();env.traverse(o=>{o.geometry?.dispose();o.material?.dispose();});}\r\n // sky dressing\r\n const stars=makeStars(THREE,uniforms),starGroup=new THREE.Group();for(const l of stars)starGroup.add(l.pts);scene.add(starGroup);\r\n const nebTex=own(glowTexture(THREE,'255,255,255',[[0,.55],[.35,.28],[.7,.08],[1,0]]));\r\n const nebula=(tier.holo?[[-24,24,-85,135,color,.75],[30,44,-95,120,new THREE.Color(0x2fe0ff),.55],[2,62,-80,140,new THREE.Color(0xff4bd8),.32],[-40,52,-90,90,new THREE.Color(0x3ff5ff),.3]]:[[-22,26,-85,110,color,.5],[30,48,-95,95,new THREE.Color(0x5a3cff),.38],[0,60,-80,120,new THREE.Color(0x2fd0c0),.18]]).map(([x,y,z,s,col,o])=>{const sp=new THREE.Sprite(new THREE.SpriteMaterial({map:nebTex,color:col,transparent:true,opacity:o,depthWrite:false,blending:THREE.AdditiveBlending,fog:false}));sp.position.set(x,y,z);sp.scale.set(s,s*.7,1);sp.renderOrder=-2;scene.add(sp);return {sp,o};});\r\n const moonTex=own(glowTexture(THREE,'200,220,255',[[0,1],[.18,.95],[.22,.35],[.6,.08],[1,0]]));\r\n const moonSp=new THREE.Sprite(new THREE.SpriteMaterial({map:moonTex,transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,fog:false}));moonSp.position.set(-30,34,-85);moonSp.scale.setScalar(30);scene.add(moonSp);\r\n const terrain=buildTerrain(THREE,tier);scene.add(terrain.group);\r\n // particles\r\n const glow=own(glowTexture(THREE)),puff=own(puffTexture(THREE));\r\n const fire=new Pool(THREE,700,{additive:true,tex:glow,uniforms}),sparks=new Pool(THREE,500,{additive:true,tex:glow,uniforms}),smoke=new Pool(THREE,500,{tex:puff,uniforms}),dust=new Pool(THREE,420,{tex:puff,uniforms}),steam=new Pool(THREE,520,{tex:puff,uniforms}),embers=new Pool(THREE,300,{additive:true,tex:glow,uniforms});\r\n sparks.gravity=-9;sparks.drag=.35;dust.drag=1.4;dust.gravity=-.2;steam.wobble=.5;steam.drag=.25;smoke.drag=.6;fire.drag=1.2;embers.drag=.15;embers.wobble=1;\r\n for(const [pool,order] of [[smoke,1],[dust,2],[steam,3],[fire,4],[sparks,5],[embers,6]]){pool.points.renderOrder=order;scene.add(pool.points);}\r\n // pod\r\n const podRoot=new THREE.Group(),podVisual=new THREE.Group();podRoot.add(podVisual);scene.add(podRoot);\r\n let parts=null,glbMode=false,hatchY=2.1,coreScale=1;\r\n const fallback=buildFallbackPod(THREE,tier);podVisual.add(fallback);parts=fallback.userData;\r\n const hatchGlow=new THREE.Sprite(new THREE.SpriteMaterial({map:glow,color:new THREE.Color(1,1,1).lerp(color,.4),transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,opacity:0}));hatchGlow.scale.setScalar(2.4);scene.add(hatchGlow);\r\n const heat=new THREE.Sprite(new THREE.SpriteMaterial({map:glow,color:hot.clone().lerp(color,.25),transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,opacity:0}));scene.add(heat);\r\n const trailMat=new THREE.ShaderMaterial({vertexShader:BEAM_VERT,fragmentShader:BEAM_FRAG,uniforms:{uColor:{value:color.clone()},uOpacity:{value:0},uPow:{value:1.4}},transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide});\r\n const cone=new THREE.ConeGeometry(.6,1,20,1,true);cone.translate(0,.5,0);const trail=new THREE.Mesh(cone,trailMat);trail.visible=false;scene.add(trail);\r\n const beamMat=new THREE.ShaderMaterial({vertexShader:BEAM_VERT,fragmentShader:BEAM_FRAG,uniforms:{uColor:{value:color.clone()},uOpacity:{value:0},uPow:{value:.8}},transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide});\r\n const cyl=new THREE.CylinderGeometry(.85,.5,1,28,1,true);cyl.translate(0,.5,0);const beam=new THREE.Mesh(cyl,beamMat);beam.visible=false;scene.add(beam);\r\n const beamMat2=beamMat.clone();beamMat2.uniforms={uColor:{value:white.clone().lerp(color,.3)},uOpacity:{value:0},uPow:{value:.6}};const cyl2=new THREE.CylinderGeometry(.34,.18,1,20,1,true);cyl2.translate(0,.5,0);const beam2=new THREE.Mesh(cyl2,beamMat2);beam2.visible=false;scene.add(beam2);\r\n const ringMat=new THREE.MeshBasicMaterial({color:white.clone().lerp(color,.4),transparent:true,opacity:0,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide});\r\n const ringGeo=new THREE.RingGeometry(.9,1,72),shock=[0,1].map(()=>{const m=new THREE.Mesh(ringGeo,ringMat.clone());m.rotation.x=-Math.PI/2;m.position.y=.12;m.visible=false;scene.add(m);return {m,t0:0};});\r\n // rocks\r\n const rockGeo=new THREE.DodecahedronGeometry(1,0),rockMat=new THREE.MeshStandardMaterial({color:0x4a3828,roughness:1,flatShading:true}),rocks=[];\r\n for(let i=0;i<tier.rocks;i++){const m=new THREE.Mesh(rockGeo,rockMat),s=rnd(.07,.2)*(tier.big?1.3:1);m.scale.set(s,s*rnd(.6,1),s*rnd(.8,1.1));m.visible=false;scene.add(m);rocks.push({m,v:new THREE.Vector3(),s,bounce:0});}\r\n\r\n // timeline\r\n const T_FALL=tier.sky,T_LAND=tier.sky+tier.fall,T_PROMPT=T_LAND+tier.settle;\r\n const START=new THREE.Vector3(1.8,11.5,-2.5),END=new THREE.Vector3(0,-1.02,.1),dirFall=END.clone().sub(START).normalize();\r\n const qFall=new THREE.Quaternion(),qLand=new THREE.Quaternion().setFromEuler(new THREE.Euler(.06,0,-.15)),qSpin=new THREE.Quaternion(),Y=new THREE.Vector3(0,1,0),down=new THREE.Vector3(0,-1,0);\r\n qFall.setFromUnitVectors(down,dirFall);\r\n let t=0,last=performance.now(),tapT=null,gate=false,popped=false,landed=false,silent=false,done=false,failed=false,prevPos=START.clone(),pop0=0,landT=0,revealed=false;\r\n const shakes=[];const addShake=(at,amp,decay=4.4)=>{if(!reduced)shakes.push({at,amp,decay});};\r\n const lookY=new THREE.Vector3(),camBase=new THREE.Vector3(),tmp=new THREE.Vector3(),hatchPos=new THREE.Vector3();\r\n const fit=()=>Math.max(1,.46/Math.max(.2,camera.aspect));\r\n const sh=(tt,s)=>Math.sin(tt*41+s)*.6+Math.sin(tt*27.3+s*2.3)*.4;\r\n const hatchWorld=out=>parts?.core?podRoot.localToWorld(out.set(0,hatchY,0).applyMatrix4(podVisual.matrix)):out.set(0,2.1,0);\r\n podRoot.position.copy(START);podRoot.visible=false;\r\n\r\n function setUniformsSize(){uniforms.uScale.value=innerHeight*renderer.getPixelRatio()/(2*Math.tan(THREE.MathUtils.degToRad(camera.fov/2)));uniforms.uDpr.value=renderer.getPixelRatio();}\r\n function resize(){const w=innerWidth,h=innerHeight;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();setUniformsSize();}\r\n\r\n function impact(){\r\n  landed=true;landT=t;podRoot.visible=true;podRoot.position.copy(END);\r\n  terrain.mound.visible=true;\r\n  if(!silent){\r\n   onEvent('impact');addShake(t,tier.shake,4.2);if(tier.aftershock)addShake(t+.5,tier.aftershock,5);\r\n   impactLight.intensity=140;\r\n   shock[0].m.visible=true;shock[0].t0=t;shock[1].m.visible=!!tier.big;shock[1].t0=t+.16;\r\n   const nd=Math.round(95*tier.dust);\r\n   for(let i=0;i<nd;i++){const a=Math.random()*6.28,sp=rnd(2,6.5)*(tier.big?1.2:1);dust.emit(Math.cos(a)*.8,.25,Math.sin(a)*.8,Math.cos(a)*sp,rnd(.4,2.6),Math.sin(a)*sp,rnd(1.6,2.8),rnd(.7,1.2),rnd(2.4,4)*(tier.big?1.25:1),new THREE.Color().setRGB(.47,.37,.3).lerp(color,.05).multiplyScalar(rnd(.8,1.15)),.62);}\r\n   for(let i=0;i<40*tier.dust;i++){dust.emit(rnd(-.5,.5),.3,rnd(-.5,.5),rnd(-.8,.8),rnd(3,7.5),rnd(-.8,.8),rnd(1.4,2.4),rnd(.7,1),rnd(2,3.4),new THREE.Color().setRGB(.5,.4,.33).multiplyScalar(rnd(.8,1.1)),.55);}\r\n   const ns=Math.round(70*tier.sparks);\r\n   for(let i=0;i<ns;i++){const a=Math.random()*6.28,sp=rnd(4,12),c=sparkCol();sparks.emit(Math.cos(a)*.5,.4,Math.sin(a)*.5,Math.cos(a)*sp,rnd(3,10),Math.sin(a)*sp,rnd(.6,1.5),rnd(.1,.2),.02,c,1);}\r\n   for(const r of rocks){r.m.visible=true;const a=Math.random()*6.28,sp=rnd(4.5,10);r.m.position.set(Math.cos(a)*1.5,.4,Math.sin(a)*1.5);r.v.set(Math.cos(a)*sp,rnd(4,9),Math.sin(a)*sp);r.bounce=0;}\r\n  }else for(const r of rocks){const a=Math.random()*6.28,d=rnd(MOUND_R*.9,MOUND_R+3.5);r.m.visible=true;r.m.position.set(Math.cos(a)*d,r.s*.5,Math.sin(a)*d);r.bounce=9;}\r\n  terrain.mound.userData.t0=t;\r\n  silent=false;\r\n }\r\n\r\n function pop(){\r\n  popped=true;pop0=t;onEvent('pop');const quiet=silent;silent=false;\r\n  hatchWorld(hatchPos);\r\n  if(!quiet){\r\n   addShake(t,tier.big?.4:.26,6);\r\n   for(let i=0;i<80;i++){const a=Math.random()*6.28,sp=rnd(.3,2.2);steam.emit(hatchPos.x+Math.cos(a)*.3,hatchPos.y,hatchPos.z+Math.sin(a)*.3,Math.cos(a)*sp,rnd(3.5,8),Math.sin(a)*sp,rnd(1.6,3),rnd(.6,1),rnd(2.5,4.5),new THREE.Color(.9,.93,1),.55);}\r\n   const ns=Math.round(60*tier.sparks);for(let i=0;i<ns;i++){const a=Math.random()*6.28,sp=rnd(1,5);sparks.emit(hatchPos.x,hatchPos.y,hatchPos.z,Math.cos(a)*sp,rnd(4,11),Math.sin(a)*sp,rnd(.8,1.8),rnd(.08,.16),.02,sparkCol(.8),1);}\r\n  }\r\n }\r\n\r\n function emitTrail(a,b,speedK){\r\n  const dist=a.distanceTo(b),n=Math.min(40,Math.ceil(dist/.14)+2);\r\n  for(let i=0;i<n;i++){\r\n   const k=i/n,x=lerp(a.x,b.x,k)-dirFall.x*1.1,y=lerp(a.y,b.y,k)-dirFall.y*1.1,z=lerp(a.z,b.z,k)-dirFall.z*1.1,c=Math.random()<.45?white.clone().lerp(hot,Math.random()*.6):color.clone().lerp(white,Math.random()*.4);\r\n   fire.emit(x,y,z,-dirFall.x*rnd(.5,3)+rnd(-.4,.4),-dirFall.y*rnd(.5,3)+rnd(-.4,.4),-dirFall.z*rnd(.5,3)+rnd(-.4,.4),rnd(.45,.95),rnd(.5,.85)*(1+speedK*.4),.08,c,.95);\r\n   if(Math.random()<.5)smoke.emit(x,y,z,rnd(-.4,.4),rnd(-.2,.5),rnd(-.4,.4),rnd(1.2,1.9),rnd(.5,.8),rnd(1.4,2.2),new THREE.Color(.2,.18,.24).lerp(color,.12),.3);\r\n   if(Math.random()<.18)sparks.emit(x,y,z,rnd(-2,2)-dirFall.x*3,rnd(-2,2)-dirFall.y*3,rnd(-2,2)-dirFall.z*3,rnd(.3,.8),rnd(.09,.15),.02,tier.holo?sparkCol():tier.gold?gold:white,1);\r\n  }\r\n }\r\n\r\n let steamAcc=0,emberAcc=0;\r\n const ventPts=[[-1.15,.42],[1.25,.28],[.35,1.15],[-.55,-.95],[.9,-.7]].map(([x,z])=>{const r=Math.hypot(x,z),th=Math.atan2(z,x);return [x,moundHeight(r,th)+.06,z];});\r\n function steamTick(dt,rate){\r\n  steamAcc+=dt*rate;\r\n  while(steamAcc>1){steamAcc--;\r\n   let x,y,z;if(Math.random()<.55){const v=ventPts[(Math.random()*ventPts.length)|0];x=v[0]+rnd(-.15,.15);y=v[1];z=v[2]+rnd(-.15,.15);}else{const a=Math.random()*6.28,r=rnd(.6,1.9);x=Math.cos(a)*r;z=Math.sin(a)*r;y=moundHeight(r,a)+.06;}\r\n   steam.emit(x,y,z,rnd(-.15,.15),rnd(.8,1.7),rnd(-.1,.2),rnd(2.2,3.6),rnd(.6,1),rnd(2.2,3.6),new THREE.Color(.8,.84,.93).lerp(color,.12),.4);\r\n  }\r\n }\r\n\r\n function update(){\r\n  const now=performance.now(),dt=Math.min(.05,(now-last)/1000);last=now;\r\n  t=(t0real===null?(t0real=now,0):(now-t0real)/1000)+skipOffset;\r\n  uniforms.uTime.value=t;\r\n  // ---- events\r\n  if(!landed&&t>=T_LAND)impact();\r\n  const u=tapT===null?-1:t-tapT;\r\n  if(tapT!==null&&!popped&&gate&&u>=.5){pop();if(fast){pop0=t-2;fast=false;}}\r\n  const pu=popped?t-pop0:-1;\r\n  if(popped&&!revealed&&pu>=1.25){revealed=true;onEvent('revealed');}\r\n  // ---- pod\r\n  const s=clamp((t-T_FALL)/tier.fall),p=Math.pow(s,1.85);\r\n  if(!landed){\r\n   podRoot.visible=t>=T_FALL;\r\n   podRoot.position.lerpVectors(START,END,p);\r\n   qSpin.setFromAxisAngle(Y,(1-p)*Math.PI*4);podRoot.quaternion.copy(qFall).multiply(qSpin);\r\n   if(podRoot.visible){\r\n    const speedK=p;emitTrail(prevPos,podRoot.position,speedK);\r\n    trail.visible=true;trail.position.copy(podRoot.position).addScaledVector(dirFall,-.9);\r\n    tmp.copy(dirFall).negate();trail.quaternion.setFromUnitVectors(Y,tmp);const len=1.5+7*Math.min(1,p*1.4);trail.scale.set(.4+.8*p,len,.4+.8*p);\r\n    trailMat.uniforms.uOpacity.value=.55+.35*p;\r\n    heat.visible=true;heat.position.copy(podRoot.position).addScaledVector(dirFall,.1);heat.material.opacity=.7+.3*Math.sin(t*40);heat.scale.setScalar(2.2+3.4*p);\r\n   }\r\n   prevPos.copy(podRoot.position);\r\n   // the engine glows hot during re-entry, panels tinted by heat\r\n   if(parts?.glow)parts.glow.emissiveIntensity=2+2*p;\r\n  }else{\r\n   const lt=t-landT;podRoot.position.copy(END);\r\n   podRoot.quaternion.slerp(qLand,clamp(dt*14));\r\n   if(trail.visible){trailMat.uniforms.uOpacity.value=Math.max(0,.9-lt*3.5);if(lt>.3)trail.visible=false;}\r\n   if(heat.visible){heat.material.opacity=Math.max(0,1-lt*4);heat.scale.setScalar(4+lt*10);if(lt>.25)heat.visible=false;}\r\n   // settle: seams cool from white-hot to tier glow and pulse\r\n   if(parts?.glow&&u<.5)parts.glow.emissiveIntensity=lerp(5,1.3,clamp(lt/1.6))+.4*Math.sin(t*3.2)*clamp(lt-1);\r\n   if(parts?.nozzle)parts.nozzle.material.color.lerp(new THREE.Color(0x201510),clamp(dt*1.2));\r\n  }\r\n  // mound grows on impact\r\n  if(landed){const mk=clamp((t-landT)/.4);terrain.mound.scale.y=Math.max(.001,silent?1:easeOutBack(mk));terrain.scorch.material.opacity=clamp((t-landT)/.5)*.9;}\r\n  // shockwaves\r\n  for(const sw of shock){if(!sw.m.visible)continue;const k=clamp((t-sw.t0)/.75);if(t<sw.t0){sw.m.scale.setScalar(.001);continue;}sw.m.scale.setScalar(.8+k*8);sw.m.material.opacity=(1-k)*(1-k)*.8;if(k>=1)sw.m.visible=false;}\r\n  // rocks\r\n  for(const r of rocks){if(!r.m.visible||r.bounce>=3)continue;r.v.y-=14*dt;r.m.position.addScaledVector(r.v,dt);r.m.rotation.x+=r.v.x*dt;r.m.rotation.z+=r.v.z*dt;\r\n   const rr=Math.hypot(r.m.position.x,r.m.position.z),gy=(rr<MOUND_R?moundHeight(rr,Math.atan2(r.m.position.z,r.m.position.x))*terrain.mound.scale.y:0)+r.s*.4;\r\n   if(r.m.position.y<gy){if(rr<1.5){const a=Math.atan2(r.m.position.z,r.m.position.x);r.m.position.x=Math.cos(a)*1.6;r.m.position.z=Math.sin(a)*1.6;}r.m.position.y=gy;r.v.y*=-.35;r.v.x*=.5;r.v.z*=.5;r.bounce++;}}\r\n  // impact + pod lights\r\n  impactLight.intensity=Math.max(0,impactLight.intensity*Math.exp(-dt*5.5));\r\n  const pulse=.5+.5*Math.sin(t*3.4);\r\n  podLight.intensity=!landed?(podRoot.visible?18+16*p:0):lerp(18,26,clamp((t-landT)/1.2))+6*pulse+(u>=0?20*clamp(u):0);\r\n  podLight.position.set(0,2.4,3);\r\n  if(!landed&&podRoot.visible)podLight.position.copy(podRoot.position).add(tmp.set(0,0,3));\r\n  // rattle, lid, beam\r\n  podVisual.position.set(0,0,0);podVisual.rotation.set(0,0,0);\r\n  if(tapT!==null&&u>=0&&!popped&&!reduced){const k=Math.min(1,u/.5),a=.016+.03*k;podVisual.position.set(rnd(-a,a),0,rnd(-a,a));podVisual.rotation.z=rnd(-a,a)*.5;if(parts?.glow)parts.glow.emissiveIntensity=lerp(1.4,6,k);steamTick(dt,50);if(Math.random()<.6)sparks.emit(rnd(-.5,.5),1.2+rnd(0,.8),rnd(-.1,.5)+.5,rnd(-1,1),rnd(1,3),rnd(0,2),rnd(.3,.7),.08,.02,white,1);}\r\n  podVisual.updateMatrix();\r\n  hatchWorld(hatchPos);\r\n  if(parts?.hinge){parts.hinge.rotation.z=popped?2.05*(1-Math.exp(-9*pu)*Math.cos(15*pu)):0;}\r\n  if(parts?.core)parts.core.visible=glbMode?popped:true;\r\n  if(glbMode&&parts?.core)parts.core.scale.setScalar(popped?clamp(pu*4)*coreScale:.001);\r\n  if(popped){\r\n   const k=clamp(pu/.5),grow=1-Math.pow(1-k,3);\r\n   beam.visible=beam2.visible=true;beam.position.copy(hatchPos);beam2.position.copy(hatchPos);\r\n   const flick=.92+.08*Math.sin(t*23)+.05*Math.sin(t*9.7);\r\n   beam.scale.set(1,18*grow,1);beam2.scale.set(1,18*grow,1);\r\n   beamMat.uniforms.uOpacity.value=.5*grow*flick;beamMat2.uniforms.uOpacity.value=.6*grow*flick;\r\n   hatchLight.position.copy(hatchPos).add(tmp.set(0,.6,.8));hatchLight.intensity=(55+25*flick)*grow;\r\n   hatchGlow.position.copy(hatchPos);hatchGlow.material.opacity=.9*grow*flick;hatchGlow.scale.setScalar(2.4+.4*Math.sin(t*14)+(1-grow)*1.5);\r\n   emberAcc+=dt*(tier.big?90:55);\r\n   while(emberAcc>1){emberAcc--;const a=Math.random()*6.28,r=rnd(0,.4);embers.emit(hatchPos.x+Math.cos(a)*r,hatchPos.y+.1,hatchPos.z+Math.sin(a)*r,rnd(-.3,.3),rnd(2.2,5.5),rnd(-.3,.3),rnd(1.4,2.6),rnd(.08,.18),.02,tier.holo||tier.gold?sparkCol(.7):white.clone().lerp(color,.7),1);}\r\n  }\r\n  // steam: continuous, loops forever once landed\r\n  if(landed&&!(tapT!==null&&!popped))steamTick(dt,(popped?44:30)*clamp((t-landT)/.9)*(silent?1:1));\r\n  // ---- camera\r\n  const asp=Math.max(.2,camera.aspect),f=fit(),lt2=clamp(t/T_LAND);\r\n  let camY=2.4,camZ=8.6*f,lk=lerp(6,1.5,smooth(lt2));\r\n  if(landed){lk=1.5;camBase.x=Math.sin(t*.28)*.22*(1-clamp(pu/1));}else camBase.x=0;\r\n  if(popped){const k=smooth(clamp(pu/1.5));camZ=lerp(8.6,8,k)*f;camY=lerp(2.4,2.5,k);lk=lerp(1.5,2.1,k);}\r\n  camera.position.set(camBase.x,camY,camZ);lookY.set(0,lk,0);\r\n  // tier rumble builds while the pod is incoming (Rare and Legendary)\r\n  let amp=0,fov=50;\r\n  if(!landed&&tier.rumble&&!reduced)amp+=tier.rumble*smooth(clamp(t/T_LAND))*(t>=T_FALL?1.6:.5);\r\n  for(let i=shakes.length-1;i>=0;i--){const sk=shakes[i],age=t-sk.at;if(age<0)continue;const a=sk.amp*Math.exp(-age*sk.decay);if(a<.004&&age>.2)shakes.splice(i,1);else{amp+=a;}}\r\n  camera.position.x+=sh(t,1)*amp*.8;camera.position.y+=sh(t,7)*amp*.8;camera.position.z+=sh(t,13)*amp*.4;\r\n  camera.lookAt(lookY);camera.rotateZ(sh(t,19)*amp*.07);\r\n  fov=50-amp*5;if(Math.abs(fov-camera.fov)>.01){camera.fov=fov;camera.updateProjectionMatrix();setUniformsSize();}\r\n  // sky motion: stars drift, layers parallax against the camera's pan\r\n  starGroup.rotation.y=t*.006;for(const [i,l] of stars.entries()){l.pts.position.y=(lk-1.5)*(i+1)*.6;l.pts.rotation.y=t*.004*(i+1);}\r\n  for(const n of nebula){n.sp.material.opacity=n.o*(1+(tier.big?.35:.12)*Math.sin(t*.6+n.o*9))*(1+(!landed?.0:0));}\r\n  if(tier.big&&!landed&&t>T_FALL){nebula[0].sp.material.opacity=nebula[0].o*(1.4+.4*Math.sin(t*9));}\r\n  // ---- particles\r\n  for(const pool of [fire,sparks,smoke,dust,steam,embers])pool.update(dt);\r\n  renderer.render(scene,camera);\r\n }\r\n let t0real=null,skipOffset=0,fast=false;\r\n resize();\r\n const world={\r\n  update,resize,tier,pools:{fire,sparks,smoke,dust,steam,embers},\r\n  get t(){return t;},get landed(){return landed;},get promptReady(){return landed&&t>=T_PROMPT&&tapT===null;},get tapped(){return tapT!==null;},\r\n  tap(){if(tapT===null){tapT=t;gate=false;}},\r\n  release(){gate=true;},\r\n  setRevealColor(hex){const c=new THREE.Color(hex);beamMat.uniforms.uColor.value.copy(c);beamMat2.uniforms.uColor.value.copy(white.clone().lerp(c,.3));hatchLight.color.copy(c);},\r\n  cancelTap(){tapT=null;gate=false;},\r\n  skipToPrompt(){silent=true;if(t<T_PROMPT)skipOffset+=T_PROMPT-t+.001;},\r\n  skipToOpen(){fast=true;if(!popped)silent=true;if(popped){pop0=Math.min(pop0,t-2);fast=false;}},\r\n  get popped(){return popped;},\r\n  // Where the pod is on screen in CSS px (for the tap target): centre and pixels per world unit.\r\n  podScreen(){tmp.set(0,(hatchY-END.y)/2,0).add(podRoot.position).project(camera);const d=camera.position.distanceTo(podRoot.position);return {x:(tmp.x*.5+.5)*innerWidth,y:(-tmp.y*.5+.5)*innerHeight,k:innerHeight/(2*Math.tan(THREE.MathUtils.degToRad(camera.fov/2)))/d};},\r\n  get silent(){return silent;},\r\n  useModel(model){\r\n   if(tapT!==null||glbMode||!parts)return;\r\n   if(tier.holo){const h=holoMaterials(THREE,uniforms.uTime),shells=[];model.traverse(o=>{if(o.isMesh){o.material=h.body;o.renderOrder=7;const s=new THREE.Mesh(o.geometry,h.glow);s.scale.setScalar(1.025);s.renderOrder=8;shells.push([o,s]);}});for(const [o,s] of shells)o.add(s);}\r\n   else{const m=tierMaterial(THREE,tierName);model.traverse(o=>{if(o.isMesh)o.material=m;});}\r\n   const core=parts.core;glbMode=true;podVisual.remove(fallback);podVisual.add(model);\r\n   // Faked hatch: a small glowing disc sunk into the top (the light, beam and sprite sell the opening).\r\n   const c=core.clone();c.material=core.material.clone();model.add(c);hatchY=model.userData.top*.96;coreScale=clamp(model.userData.radius*.45/.42,.25,.8);c.position.y=hatchY;c.rotation.x=-Math.PI/2;\r\n   parts={core:c,glow:null,nozzle:null,hinge:null};\r\n   // A mesh/node named \"lid\" or \"hatch\" is the real lid: hinge it on its lower -x edge; otherwise the glowing disc fakes the opening.\r\n   let lid=null;model.traverse(o=>{if(!lid&&/^(lid|hatch)/i.test(o.name))lid=o;});\r\n   if(lid){\r\n    podRoot.updateMatrixWorld(true);const box=new THREE.Box3().setFromObject(lid).applyMatrix4(podVisual.matrixWorld.clone().invert());\r\n    if(!box.isEmpty()){const hinge=new THREE.Group();hinge.position.set(box.min.x,box.min.y,(box.min.z+box.max.z)/2);podVisual.add(hinge);hinge.updateMatrixWorld(true);hinge.attach(lid);parts.hinge=hinge;c.position.set((box.min.x+box.max.x)/2,box.min.y,(box.min.z+box.max.z)/2);}\r\n   }\r\n  },\r\n  dispose(){\r\n   scene.traverse(o=>{o.geometry?.dispose();const m=o.material;if(m)for(const x of Array.isArray(m)?m:[m]){x.map?.dispose();x.dispose();}});\r\n   for(const o of owned)o.dispose?.();for(const l of stars)l.pts.geometry.dispose();\r\n   scene.environment=null;scene.background=null;\r\n  }\r\n };\r\n return world;\r\n}\r\n\r\n// Plays one pack's sequence full screen. open() runs the pack at the moment the pod is tapped and resolves to the\r\n// reveal items [{title,detail,colors}] (or null when it could not be saved: the pod stays closed, tap to retry).\r\n// hasNext is a boolean or a function; onNext() may return the next {tier,open,hasNext,onNext}; the sequence restarts from the sky with it.\r\nexport async function playDropPod(config){\r\n let {tier,open,hasNext,onNext}=config;let getRevealType=config.revealType,getRevealColor=config.revealColor;const onExit=config.onExit;\r\n const reduced=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches===true;\r\n const style=document.createElement('style');style.textContent=CSS;document.head.append(style);\r\n const dlg=document.createElement('dialog');dlg.className='drop-pod';dlg.setAttribute('aria-label','Opening reward pack');\r\n dlg.innerHTML=`<canvas aria-hidden=\"true\"></canvas><div class=\"drop-pod-flash\"></div><p class=\"drop-pod-caption\" aria-hidden=\"true\">Incoming drop pod</p>\r\n<button type=\"button\" class=\"drop-pod-skip\" data-skip>Skip</button>\r\n<button type=\"button\" class=\"drop-pod-hit\" data-hit aria-label=\"Open the drop pod\"></button>\r\n<p class=\"drop-pod-prompt\" aria-hidden=\"true\">Tap the pod<small data-error></small></p>\r\n<section class=\"drop-pod-card\" aria-live=\"polite\" aria-atomic=\"true\"><p class=\"eyebrow\" data-eyebrow></p><div data-items></div></section>\r\n<div class=\"drop-pod-actions\"><button type=\"button\" class=\"secondary\" data-exit>Exit</button><button type=\"button\" data-next>Open another</button></div>`;\r\n document.body.append(dlg);\r\n const $=sel=>dlg.querySelector(sel),flash=$('.drop-pod-flash');\r\n let phase=startPhase({reduced}),world=null,renderer=null,releaseGpu=null,raf=0,disposed=false,gen=0,fullscreenTried=false,items=null,pending=null,THREE=null;\r\n const go=event=>{phase=advance(phase,event);dlg.dataset.phase=phase;};\r\n const setTier=name=>{const t=TIERS[name]?name:'uncommon';dlg.dataset.tier=t;dlg.style.setProperty('--pack',TIERS[t].color);$('.drop-pod-caption').textContent=t==='secret'?'Secret transmission':'Incoming drop pod';};\r\n const applyRewardPresentation=targetWorld=>{\r\n  if(typeof getRevealType!=='function')return;\r\n  dlg.dataset.rewardType=getRevealType();const color=typeof getRevealColor==='function'?getRevealColor():null;\r\n  if(color){dlg.style.setProperty('--pack',color);targetWorld?.setRevealColor(color);}\r\n };\r\n setTier(tier);dlg.dataset.phase=phase;\r\n dlg.tabIndex=-1;dlg.showModal();dlg.focus({preventScroll:true});\r\n // Fullscreen where allowed (not iOS Safari); the fixed 100vw x 100dvh dialog is the fallback.\r\n if(!fullscreenTried){fullscreenTried=true;try{dlg.requestFullscreen?.({navigationUI:'hide'})?.catch?.(()=>{});}catch{}}\r\n const leaveFullscreen=()=>{try{if(document.fullscreenElement)document.exitFullscreen().catch(()=>{});}catch{}};\r\n\r\n function exit(){\r\n  if(disposed)return;disposed=true;cancelAnimationFrame(raf);removeEventListener('resize',onResize);\r\n  world?.dispose();renderer?.dispose();renderer?.forceContextLoss?.();releaseGpu?.(renderer?.domElement);releaseGpu=null;leaveFullscreen();dlg.close();dlg.remove();style.remove();\r\n  try{onExit?.();}catch(error){console.error(error);}\r\n }\r\n const onResize=()=>world?.resize();addEventListener('resize',onResize);\r\n dlg.addEventListener('cancel',event=>{event.preventDefault();exit();});\r\n\r\n function showItems(list){\r\n  const wrap=$('[data-items]');wrap.replaceChildren();$('[data-eyebrow]').textContent=dlg.dataset.tier==='secret'?'SECRET':({texture:'Texture reward','multiple-colors':'Multiple colors','single-color':'Color reward'}[dlg.dataset.rewardType]||'Cosmetic rewards');\r\n  if(!list.length){const s=document.createElement('strong');s.textContent='Collection complete';const d=document.createElement('small');d.textContent='All cosmetics for your unlocked coaches are collected.';const item=document.createElement('div');item.className='drop-pod-item';item.append(s,d);wrap.append(item);}\r\n  for(const entry of list){\r\n   const item=document.createElement('div');item.className='drop-pod-item';const s=document.createElement('strong');s.textContent=`${entry.title} unlocked!`;const d=document.createElement('small');d.textContent=entry.detail||'';item.append(s,d);\r\n   if(entry.colors?.length){const sw=document.createElement('div');sw.className='drop-pod-swatches';for(const c of entry.colors){const i=document.createElement('i');i.style.background=c;sw.append(i);}item.append(sw);}\r\n   wrap.append(item);\r\n  }\r\n }\r\n function afterReveal(){\r\n  if(disposed||phase!=='reveal')return;\r\n  const next=$('[data-next]'),more=typeof hasNext==='function'?hasNext():!!hasNext;next.hidden=!more;\r\n  setTimeout(()=>{if(!disposed)(more?next:$('[data-exit]')).focus();},900);\r\n }\r\n\r\n async function tap(){\r\n  if(phase!=='landed'||!world||world.tapped)return;\r\n  const mine=gen;go('tap');dlg.classList.remove('ready');$('[data-error]').textContent='';world.tap();\r\n  let result=null;try{result=await open();}catch(error){console.error(error);}\r\n  if(disposed||mine!==gen)return;\r\n  if(!result){go('fail');world.cancelTap();dlg.classList.add('ready');$('[data-error]').textContent='Could not save this pack. Tap the pod to try again.';$('[data-hit]').focus();return;}\r\n  // The reward is now durably saved and granted. Color the reveal from that\r\n  // result while leaving the pod's already-played flight at its pack tier.\r\n  applyRewardPresentation(world);\r\n  items=result;world.release();\r\n }\r\n $('[data-hit]').onclick=tap;\r\n $('[data-exit]').onclick=exit;\r\n $('[data-next]').onclick=async()=>{\r\n  const nextConfig=await onNext?.();if(!nextConfig||disposed)return;\r\n  ({tier,open,hasNext,onNext}=nextConfig);getRevealType=nextConfig.revealType;getRevealColor=nextConfig.revealColor;go('restart');await begin();\r\n };\r\n // Skip: straight to the reveal. The pack still opens (once) through open().\r\n $('[data-skip]').onclick=async()=>{\r\n  if(!world||phase==='reveal')return;\r\n  if(phase==='opened'){world.skipToOpen();return;}\r\n  world.skipToPrompt();flash.classList.remove('go');\r\n  if(phase==='sky'||phase==='drop'){go('land');}\r\n  dlg.classList.remove('ready');await tap();\r\n  if(world&&!world.tapped)return;\r\n  world?.skipToOpen();\r\n };\r\n\r\n function frame(){\r\n  if(disposed)return;raf=requestAnimationFrame(frame);\r\n  world.update();\r\n  {const ps=world.podScreen(),hit=$('[data-hit]');hit.style.left=ps.x+'px';hit.style.top=ps.y+'px';hit.style.width=Math.min(innerWidth*.8,2.5*ps.k)+'px';hit.style.height=Math.min(innerHeight*.5,2.6*ps.k)+'px';}\r\n  if(phase==='sky'&&world.t>=world.tier.sky)go('fall');\r\n  if(world.landed&&(phase==='sky'||phase==='drop'))go('land');\r\n  if(phase==='landed'&&world.promptReady&&!dlg.classList.contains('ready')){dlg.classList.add('ready');$('[data-hit]').focus({preventScroll:true});}\r\n  if(phase==='opened'&&items&&worldRevealed){\r\n   showItems(items);items=null;go('revealed');afterReveal();\r\n  }\r\n }\r\n let worldRevealed=false;\r\n\r\n async function begin(){\r\n  const mine=++gen;dlg.classList.remove('ready');worldRevealed=false;items=null;setTier(tier);delete dlg.dataset.rewardType;dlg.dataset.phase=phase;$('[data-error]').textContent='';\r\n  world?.dispose();world=null;\r\n  if(!THREE){\r\n   try{THREE=await import('three');}catch(error){console.error(error);}\r\n   if(disposed)return;\r\n  }\r\n  if(mine!==gen)return;\r\n  if(!THREE){await fallbackReveal();return;}\r\n  if(!renderer){\r\n   // Full-screen: every other app renderer (3D coach, portal board) gives its context back until the pod closes.\r\n   if(!releaseGpu){releaseGpu=(await import('./modules/graphics-lifecycle.mjs')).claimGraphics('drop-pod');await releaseGpu.ready;if(disposed||mine!==gen)return;}\r\n   try{renderer=new THREE.WebGLRenderer({canvas:$('canvas'),antialias:true,alpha:false,powerPreference:'high-performance'});}catch(error){console.error(error);renderer=null;}\r\n   if(!renderer){await fallbackReveal();return;}\r\n   renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.15;\r\n  }\r\n  world=createWorld(THREE,renderer,tier,{reduced,onEvent:event=>{\r\n   if(event==='impact'&&!reduced){flash.classList.remove('go','pop');void flash.offsetWidth;flash.classList.add('go');}\r\n   if(event==='pop'&&!reduced&&!world.silent){flash.classList.remove('go','pop');void flash.offsetWidth;flash.classList.add('pop');}\r\n   if(event==='revealed')worldRevealed=true;\r\n  }});\r\n  if(reduced){world.skipToPrompt();}\r\n  loadGlb(THREE,TIERS[tier]?tier:'uncommon').then(model=>{if(model&&!disposed&&mine===gen&&world)world.useModel(model);});\r\n  cancelAnimationFrame(raf);raf=requestAnimationFrame(frame);\r\n }\r\n // No WebGL: skip the 3D and still open the pack, so a pack is never stuck.\r\n async function fallbackReveal(){\r\n  dlg.dataset.phase='landed';phase='landed';let result=null;try{result=await open();}catch(error){console.error(error);}\r\n  if(disposed)return;if(result)applyRewardPresentation(null);showItems(result||[]);phase='opened';go('revealed');afterReveal();\r\n }\r\n await begin();\r\n return {exit,state:()=>phase,get world(){return world;}};\r\n}\r\n","exercise-demo.mjs":"// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Schematic movement previews, with the selected supports/arm positions shown.\r\nexport function applyExerciseDemo(p,m,t){\r\n const slow=m.minCycle||m.dwell?1.1:2.1,q=(1-Math.cos(t*slow))/2;\r\n const armsUp=()=>{p.le=[-.29,.96,0];p.re=[.29,.96,0];p.lw=[-.23,1.37,0];p.rw=[.23,1.37,0];};\r\n const d=m.detector;\r\n if(d==='pushup'||d==='plank'){\r\n  const bend=d==='plank'?0:q,inc=m.incline?.45:0,lower=m.support==='knees';\r\n  for(const [side,z] of [['l',-.12],['r',.12]]){\r\n   p[side+'s']=[-.56,.04-bend*.27+inc,z];p[side+'h']=[.25,-.04+inc*.4,z];\r\n   p[side+'k']=[.58,lower?-.48:-.12,z];p[side+'a']=[.91,m.decline?.15:lower?-.38:-.28,z];\r\n   p[side+'w']=[-.58,-.51+inc,m.narrow?z*.2:m.wide?z*2.5:z];\r\n   p[side+'e']=m.forearm?[-.57,-.5,z]:[-.57+bend*.18,-.25-bend*.04+inc,z];\r\n  }\r\n  p.head=[-.85,.1-q*.27*(d==='pushup')+inc,0];p.neck=[-.68,.04-q*.27*(d==='pushup')+inc,0];\r\n }else if(d==='squat'||d==='split'){\r\n  const drop=(m.travel?.22:.4)*q;\r\n  for(const key of ['head','neck','ls','rs','le','re','lw','rw','lh','rh'])p[key][1]-=drop;\r\n  if(d==='split'){\r\n   const lead=m.side==='right'?'r':'l',back=lead==='l'?'r':'l';p[lead+'k']=[-.6,-.43-drop*.4,.04];p[lead+'a']=[-.6,-.95,.04];p[back+'k']=[.45,-.47-drop*.5,-.05];p[back+'a']=[.8,-.95,-.05];\r\n  }else{const width=m.wide?.34:.18;p.lk=[-width-.13*q,-.46-drop*.5,.25*q];p.rk=[width+.13*q,-.46-drop*.5,.25*q];p.la=[-width,-.95,0];p.ra=[width,-.95,0];}\r\n  p.le=[-.3,.24-drop,.25];p.re=[.3,.24-drop,.25];p.lw=[-.15,.36-drop,.4];p.rw=[.15,.36-drop,.4];\r\n }else if(d==='hinge'){\r\n  const bend=q*(m.bend?.45:.9),cos=Math.cos(bend),sin=Math.sin(bend);\r\n  for(const key of ['head','neck','ls','rs','le','re','lw','rw']){const y=p[key][1];p[key][1]=y*cos;p[key][2]=y*sin;}\r\n  if(m.arms==='chest'){p.lw=[.1,.37*cos,.37*sin+.1];p.rw=[-.1,.37*cos,.37*sin+.1];}\r\n }else if(d==='bridge'){\r\n  for(const [side,z] of [['l',-.14],['r',.14]]){p[side+'s']=[-.67,-.53,z];p[side+'h']=[0,-.5+q*.37,z];p[side+'k']=[.38,.02,z];p[side+'a']=[.66,-.53,z];p[side+'e']=[-.4,-.54,z*1.4];p[side+'w']=[-.1,-.54,z*1.7];}p.head=[-.93,-.47,0];p.neck=[-.78,-.5,0];\r\n }else if(d==='sideplank'){\r\n  const lower=m.side==='left'?'l':'r',upper=lower==='l'?'r':'l';\r\n  p[lower+'s']=[-.5,-.03,0];p[upper+'s']=[-.5,.32,0];p[lower+'h']=[.2,-.18,0];p[upper+'h']=[.2,.08,0];\r\n  p[lower+'e']=[-.5,-.3,0];p[lower+'w']=[-.48,-.55,0];p[upper+'e']=[-.5,.64,0];p[upper+'w']=[-.5,.96,0];\r\n  for(const side of [lower,upper]){p[side+'k']=[.57,-.31,side===lower?0:.08];p[side+'a']=[m.support==='knees'?.25:.94,-.53,side===lower?0:.08];}\r\n  p.neck=[-.65,.18,0];p.head=[-.87,.21,0];\r\n }else if(d==='raise'){\r\n  const a=q*(m.overhead?Math.PI:Math.PI/2);\r\n  for(const [side,sign] of [['l',-1],['r',1]])for(const [joint,length] of [['e',.36],['w',.73]])p[side+joint]=[sign*(.23+(m.plane==='front'?0:Math.sin(a)*length)),.57-Math.cos(a)*length,m.plane==='front'?Math.sin(a)*length:0];\r\n }else if(d==='press'){\r\n  for(const [side,sign] of [['l',-1],['r',1]]){p[side+'e']=[sign*(.56-.28*q),.53+.4*q,0];p[side+'w']=[sign*(.56-.31*q),.89+.43*q,0];}\r\n }else if(d==='balance'){\r\n  const lift=m.low?.15:.4;p.lk=[-.17-(m.kneeLift?0:.3),-.46+lift,m.kneeLift?.45:0];p.la=m.kneeLift?[-.17,-.63,.48]:[.12,m.low?-.78:-.4,0];\r\n  p.lw=[-.02,.45,.16];p.rw=[.02,.45,.16];p.le=[-.32,.22,.07];p.re=[.32,.22,.07];if(m.overhead)armsUp();\r\n }else if(d==='yoga'||d==='stance'){\r\n  const pose=m.pose??'horse';\r\n  if(pose==='salute')armsUp();\r\n  if(pose==='chair'){for(const key of ['head','neck','ls','rs','le','re','lw','rw','lh','rh'])p[key][1]-=.28;p.lk=[-.17,-.55,.3];p.rk=[.17,-.55,.3];armsUp();for(const key of ['le','re','lw','rw'])p[key][1]-=.28;}\r\n  if(pose==='wall-sit'){for(const key of ['head','neck','ls','rs','le','re','lw','rw','lh','rh'])p[key][1]-=.45;p.lk=[-.17,-.55,.45];p.rk=[.17,-.55,.45];}\r\n  if(['warrior','warrior-one'].includes(pose)||m.side){const lead=m.side==='right'?'r':'l',back=lead==='l'?'r':'l',sign=lead==='l'?-1:1;p[lead+'k']=[sign*.66,-.33,0];p[lead+'a']=[sign*.66,-.85,0];p[back+'k']=[-sign*.46,-.43,0];p[back+'a']=[-sign*.82,-.88,0];if(pose==='warrior'){p.le=[-.6,.57,0];p.re=[.6,.57,0];p.lw=[-.98,.57,0];p.rw=[.98,.57,0];}else if(pose==='warrior-one')armsUp();}\r\n  else if(pose==='horse'||pose==='goddess'){\r\n   const deep=m.high?.06:m.low?.35:.23;p.lk=[-.56,-.44+deep,0];p.rk=[.56,-.44+deep,0];p.la=[-.56,-.94+deep,0];p.ra=[.56,-.94+deep,0];\r\n   for(const v of Object.values(p))v[1]-=deep;\r\n   if(pose==='goddess'){p.le=[-.58,.48-deep,0];p.re=[.58,.48-deep,0];p.lw=[-.58,.84-deep,0];p.rw=[.58,.84-deep,0];}\r\n   else{p.lw=[-.06,.42-deep,.1];p.rw=[.06,.42-deep,.1];p.le=[-.34,.22-deep,0];p.re=[.34,.22-deep,0];}\r\n  }\r\n }else if(d==='boxing'){\r\n  for(const [side,sign,phase] of [['l',-1,Math.sin(t*(m.double?5:2.5))],['r',1,-Math.sin(t*2.5)]]){const punch=m.side&&m.side!==({l:'left',r:'right'}[side])?0:Math.max(0,phase);p[side+'e']=[sign*.28,.37+punch*.16,.2+punch*.22];p[side+'w']=[sign*.2,.55,.25+punch*.65];}p.la=[-.32,-.95,.1];p.ra=[.32,-.95,-.17];\r\n }else if(d==='march'){\r\n  for(const [side,sign,phase] of [['l',-1,Math.sin(t*(m.id==='jogging'?4:2))],['r',1,-Math.sin(t*(m.id==='jogging'?4:2))]]){const lift=Math.max(0,phase)*(m.lift?.48:m.id==='march'?.2:.32);p[side+'k']=[sign*.17,-.46+lift,lift];p[side+'a']=[sign*.17,-.95+lift,.06];p[side+'e']=[sign*.32,.22,-phase*.18];p[side+'w']=[sign*.27,.42,-phase*.32];}\r\n }else if(d==='jack'){\r\n  const swing=m.step?(1-Math.cos(t*2))/2:q;for(const [side,sign] of [['l',-1],['r',1]]){const a=swing*2.8;p[side+'e']=[sign*(.23+Math.sin(a)*.36),.57-Math.cos(a)*.36,0];p[side+'w']=[sign*(.23+Math.sin(a)*.73),.57-Math.cos(a)*.73,0];const step=!m.step||((Math.floor(t/Math.PI)%2===0)===(side==='l'));p[side+'a']=[sign*(.17+(step?.48*swing:0)),-.95,0];p[side+'k']=[sign*(.17+(step?.24*swing:0)),-.46,0];}\r\n }\r\n return p;\r\n}\r\n","exercise-gestures.mjs":"// Gesture proposals are separate from workout counting. Feet are never used.\r\nconst distance=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);\r\nexport function handShape(raw,aspect=1){\r\n if(!raw||raw.length<21||raw.some(p=>!Number.isFinite(p.x)||!Number.isFinite(p.y)||p.x<0||p.x>1||p.y<0||p.y>1))return null;\r\n const p=raw.map(v=>({x:v.x*aspect,y:v.y})),scale=distance(p[0],p[9]);if(scale<.025)return null;\r\n const extended=[8,12,16,20].map(tip=>distance(p[tip],p[0])>distance(p[tip-2],p[0])*1.25);\r\n const curled=[8,12,16,20].map(tip=>distance(p[tip],p[0])<distance(p[tip-2],p[0])*1.12);\r\n return {wrist:p[0],tip:p[8],base:p[6],fist:curled.every(Boolean),point:extended[0]&&curled.slice(1).every(Boolean),thumb:curled.every(Boolean)&&p[4].y<p[3].y-scale*.2&&p[4].y<p[5].y-scale*.35&&distance(p[4],p[0])>scale*1.15};\r\n}\r\nexport class ExerciseRecognizer {\r\n constructor(){this.pumpSide=0;this.pumps=[];this.last=0;}\r\n update(raw,hands,t,aspect=1){\r\n  const shapes=hands.map(h=>handShape(h,aspect)).filter(Boolean),thumb=shapes.some(h=>h.thumb);\r\n  if(t-this.last>850){this.pumps=[];this.pumpSide=0;}this.last=t;\r\n  if(!raw||raw.length<25)return {mode:null,thumb};\r\n  const p=raw.map(v=>({...v,x:v.x*aspect})),valid=ids=>ids.every(i=>p[i]&&p[i].visibility>=.5&&raw[i].x>=0&&raw[i].x<=1&&raw[i].y>=0&&raw[i].y<=1);\r\n  if(!valid([11,12,13,14,15,16,23,24]))return {mode:null,thumb};\r\n  const scale=(distance(p[11],p[23])+distance(p[12],p[24]))/2;if(scale<.06)return {mode:null,thumb};\r\n  const close=(a,b,n)=>distance(a,b)<scale*n;\r\n  for(const [s,e,w,otherW] of [[11,13,15,16],[12,14,16,15]]){\r\n   const target={x:(p[s].x+p[e].x)/2,y:(p[s].y+p[e].y)/2};\r\n   if(p[e].y<p[s].y+.12*scale&&p[w].y<p[s].y-.25*scale&&shapes.some(h=>h.point&&close(h.wrist,p[otherW],.65)&&close(h.tip,target,.75)&&distance(h.tip,target)<distance(h.base,target)))return {mode:'pushup',thumb};\r\n  }\r\n  const above=p[15].y<p[11].y-.45*scale&&p[16].y<p[12].y-.45*scale;\r\n  let mode=null;\r\n  if(above)mode=close(p[15],p[16],.5)?'tree':'jumping';\r\n  else if(close(p[15],p[23],.42)&&close(p[16],p[24],.42))mode='squat';\r\n  else if(Math.abs(p[15].y-p[11].y)<.25*scale&&Math.abs(p[16].y-p[12].y)<.25*scale&&Math.abs(p[15].x-p[16].x)>2.3*scale)mode='warrior';\r\n  else if(close(p[15],p[16],.35)&&p[15].y>Math.min(p[11].y,p[12].y)&&p[15].y<(p[11].y+p[23].y)/2)mode='horse';\r\n  else if(shapes.filter(h=>h.fist&&!h.thumb).length>=2&&close(p[15],p[11],.8)&&close(p[16],p[12],.8))mode='boxing';\r\n  const delta=(p[15].y-p[16].y)/scale,side=delta>.45?1:delta<-.45?-1:0;\r\n  if(side&&side!==this.pumpSide){this.pumpSide=side;this.pumps.push(t);}this.pumps=this.pumps.filter(n=>t-n<3000);\r\n  if(!mode&&this.pumps.length>=4)mode='jogging';\r\n  return {mode,thumb};\r\n }\r\n}\r\nexport class GestureConfirmation {\r\n constructor(){this.reset();}\r\n reset(){this.candidate=null;this.since=0;this.pending=null;this.expires=0;this.thumbSince=null;this.last=null;this.blocked=null;}\r\n update({mode=null,thumb=false},t){\r\n  const gap=this.last!==null&&t-this.last>850;this.last=t;\r\n  if(gap){this.candidate=null;this.thumbSince=null;}\r\n  if(!mode)this.blocked=null;\r\n  if(this.pending){\r\n   if(t>=this.expires){this.blocked=this.pending;this.pending=null;this.thumbSince=null;return {event:'expired',progress:0};}\r\n   if(thumb){this.thumbSince??=t;const progress=Math.min(1,(t-this.thumbSince)/1400);if(progress===1){const id=this.pending;this.reset();this.blocked=id;return {event:'confirmed',mode:id,progress:1};}return {event:'holding',mode:this.pending,progress};}\r\n   this.thumbSince=null;return {event:'pending',mode:this.pending,progress:0};\r\n  }\r\n  if(!mode||thumb||mode===this.blocked){this.candidate=null;return {event:'idle',progress:0};}\r\n  if(mode!==this.candidate){this.candidate=mode;this.since=t;}\r\n  if(t-this.since>=650){this.pending=mode;this.expires=t+12000;this.candidate=null;return {event:'proposed',mode,progress:0};}\r\n  return {event:'recognizing',mode,progress:0};\r\n }\r\n}\r\n","exercise-library.mjs":"// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Original movement recipes on MediaPipe landmarks. See movement-research.md.\r\n// Difficulty is a suggested progression, never a personal ability assessment.\r\n/** Game keys on the console. Display names live here only; ids (jogging, plank-flight, dance, cover) stay internal. Types index PATH_SYMBOLS. */\r\nexport const GAMES = Object.freeze({\r\n  jogging:{name:'Subway Burpees',types:['cardio','legs']},\r\n  'plank-flight':{name:'Jet Plank Joyride',types:['core','balance']},\r\n  cover:{name:'Crouching Crisis',types:['legs','boxing']},\r\n  dance:{name:'Dance, Dance, and Obey',types:['cardio','martial-arts']}\r\n});\r\nexport const FOCUS_GROUPS = [\r\n  {id:'chest',name:'Chest',description:'Push-up progressions'},\r\n  {id:'legs',name:'Legs',description:'Squats and split squats'},\r\n  {id:'hips',name:'Hips & glutes',description:'Hinges and bridges'},\r\n  {id:'core',name:'Core',description:'Plank holds'},\r\n  {id:'shoulders',name:'Shoulders',description:'Controlled arm movement'},\r\n  {id:'balance',name:'Balance',description:'Single-leg yoga holds'},\r\n  {id:'yoga',name:'Yoga',description:'Standing poses'},\r\n  {id:'stances',name:'Martial stances',description:'Controlled stance holds'},\r\n  {id:'boxing',name:'Boxing',description:'Slow shadowboxing'},\r\n  {id:'cardio',name:'Cardio',description:'Marches and jacks'}\r\n];\r\nconst rows=[];\r\nfunction add(group,id,name,detector,options={}){\r\n  const kind=options.kind??(['plank','sideplank','balance','yoga','stance'].includes(detector)?'hold':detector==='boxing'?'pace':detector==='march'?'steps':'reps');\r\n  const view=options.view??(['pushup','hinge','bridge','plank','split'].includes(detector)?'side':'front');\r\n  const measurement=kind==='hold'?'Pose hold':kind==='pace'?'Active hand time':kind==='steps'?'Knee lifts':'Repetitions';\r\n  const limits=options.limits??'Counts the visible movement; does not grade technique.';\r\n  const camera=view==='side'?'Camera beside you.':view==='angle'?'Camera at a slight angle.':'Face the camera.';\r\n  rows.push({id,group,name,detector,kind,view,measurement,limits,difficulty:rows.filter(r=>r.group===group).length+1,defaultGoal:kind==='hold'||kind==='pace'?15:5,evidence:'rule-based; camera validation pending',source:'mediapipe',...options,hint:`${camera} ${options.cue??'Keep the joints used for this movement visible.'}`});\r\n}\r\nconst pushLimits='Counts elbow bend and return. Hand shape, support stability and chest depth are not verified.';\r\nfor(const [id,name,extra] of [\r\n ['knee-pushup','Knee push-up',{support:'knees',cue:'Knees down; show shoulder, elbow, wrist and hip.'}],\r\n ['high-incline-pushup','High incline push-up',{incline:true,cue:'Hands on a stable high surface; show your full arm and torso.'}],\r\n ['low-incline-pushup','Low incline push-up',{incline:true,cue:'Hands on a stable lower surface; show your full arm and torso.'}],\r\n ['pushup','Regular push-up',{}],\r\n ['wide-pushup','Wide push-up',{wide:true}],\r\n ['slow-pushup','Slow push-up',{minCycle:1.2}],\r\n ['diamond-pushup','Diamond push-up',{narrow:true}],\r\n ['decline-pushup','Feet-elevated push-up',{decline:true,cue:'Feet on a stable low surface; show your full arm and torso.'}]\r\n])add('chest',id,name,'pushup',{cue:'Show shoulder, elbow, wrist and hip. Start with your arm extended.',limits:pushLimits,...extra});\r\nfor(const [id,name,extra] of [\r\n ['shallow-squat','Shallow squat',{travel:.18}],['squat','Bodyweight squat',{}],['wide-squat','Wide squat',{wide:true}],\r\n ['pause-squat','Pause squat',{dwell:.8}],['slow-squat','Slow squat',{minCycle:1.2}],\r\n ['split-left','Split squat · left',{detector:'split',side:'left'}],['split-right','Split squat · right',{detector:'split',side:'right'}],\r\n ['wall-sit','Wall sit',{detector:'yoga',pose:'wall-sit',kind:'hold',view:'side',cue:'Back on a wall, thighs level. Show shoulder, hip and knee.',limits:'Times the hip angle and thigh level. Wall contact and knee position are not tracked.'}]\r\n])add('legs',id,name,extra.detector??'squat',{cue:extra.side?'Show the front leg from hip to knee. Start tall.':'Show shoulders and hips. Start standing tall.',...extra});\r\nfor(const [id,name,detector,extra] of [\r\n ['small-hinge','Small hip hinge','hinge',{bend:25}],['hip-hinge','Hip hinge','hinge',{}],['good-morning','Bodyweight good morning','hinge',{arms:'chest'}],\r\n ['glute-bridge','Glute bridge','bridge',{}],['pause-bridge','Pause glute bridge','bridge',{dwell:.8}]\r\n])add('hips',id,name,detector,{cue:detector==='bridge'?'Lie on your back, knees bent. Show shoulder, hip and knee. Start with hips down.':'Show shoulder, hip and knee. Start upright; hinge slowly.',limits:'Tracks hip movement. Back curvature and muscle engagement are not verified.',...extra});\r\nfor(const [id,name,extra] of [\r\n ['knee-plank','Knee plank',{support:'knees'}],['high-plank','High plank',{}],['forearm-plank','Forearm plank',{forearm:true}],\r\n ['side-knee-left','Side knee plank · left',{side:'left',support:'knees'}],['side-knee-right','Side knee plank · right',{side:'right',support:'knees'}],\r\n ['side-plank-left','Side plank · left',{side:'left'}],['side-plank-right','Side plank · right',{side:'right'}]\r\n])add('core',id,name,extra.side?'sideplank':'plank',{view:extra.side?'front':'side',cue:extra.side?'Show both shoulders, hips and the supporting knee. Stack your shoulders.':'Show shoulder, elbow, wrist, hip and supporting knee.',limits:'Times the shoulder–hip–knee line. Feet, floor contact and loading are not tracked.',...extra});\r\nfor(const [id,name,detector,extra] of [\r\n ['front-raise','Front arm raise','raise',{view:'side',plane:'front'}],['lateral-raise','Lateral arm raise','raise',{}],\r\n ['overhead-reach','Overhead reach','raise',{overhead:true}],['standing-press','Standing arm press','press',{}],['slow-press','Slow arm press','press',{minCycle:1.2}]\r\n])add('shoulders',id,name,detector,{cue:'Keep both shoulders, elbows, wrists and hips visible. Use controlled, unloaded movement.',...extra});\r\nfor(const [id,name,extra] of [\r\n ['knee-balance','Knee-lift balance',{kneeLift:true}],['low-tree','Low tree pose',{low:true}],['tree','Tree pose',{}],['overhead-tree','Tree · arms overhead',{overhead:true}]\r\n])add('balance',id,name,'balance',{cue:'Show both hips and knees. Either leg works; keep a support nearby if needed.',limits:'Times the visible leg shape. Balance, foot pressure and support use are not verified.',...extra});\r\nfor(const [id,name,pose] of [['mountain','Mountain','mountain'],['salute','Upward salute','salute'],['chair','Chair pose','chair'],['warrior-one','Warrior I','warrior-one'],['warrior','Warrior II','warrior'],['goddess','Goddess pose','goddess']])\r\n add('yoga',id,name,'yoga',{pose,view:['chair','warrior-one'].includes(pose)?'side':'front',cue:'Show arms, hips and knees. Hold only a comfortable position.',limits:'Times broad pose shape. Foot rotation and joint alignment are not graded.'});\r\nfor(const [id,name,extra] of [['high-horse','High horse stance',{high:true}],['horse','Horse stance',{}],['low-horse','Lower horse stance',{low:true}],['front-stance-left','Front stance · left',{side:'left'}],['front-stance-right','Front stance · right',{side:'right'}]])\r\n add('stances',id,name,'stance',{view:extra.side?'side':'front',cue:'Keep hips and knees visible. Move slowly into a comfortable stance.',limits:'Times stance shape only. This is not martial-arts technique instruction.',...extra});\r\nfor(const [id,name,extra] of [['jab-left','Left straight practice',{side:'left'}],['jab-right','Right straight practice',{side:'right'}],['boxing','Alternating straights',{}],['double-jab','Double-jab practice',{double:true}]])\r\n add('boxing',id,name,'boxing',{view:'angle',cue:'Show shoulders, elbows, wrists and hips. Practice slow, controlled punches into empty space.',limits:'Tracks active hand time and relative pace. Punch type, power, accuracy and guard are not verified.',...extra});\r\nfor(const [id,name,detector,extra] of [['march','Easy march','march',{}],['high-march','High-knee march','march',{lift:.4}],['jogging',GAMES.jogging.name,'march',{lift:.08,runner:true,endless:true,cue:'The route moves automatically. Crouch deeply to duck, raise both arms to jump, and lean left or right to change lanes. Keep your upper body visible.'}],['step-jack','Step jack','jack',{step:true}],['jumping-jack','Jumping jack','jack',{}]])\r\n add('cardio',id,name,detector,{cue:detector==='jack'?'Show your upper body and knees. Start with arms down and knees close together.':'Show shoulders, hips and both knees. Start standing tall.',limits:detector==='jack'?'Counts arm-and-knee opening cycles. Feet, airtime and landings are not tracked.':'Counts knee lifts. Footfalls and impact are not verified.',...extra});\r\nadd('core','plank-flight',GAMES['plank-flight'].name,'plank-flight',{kind:'hold',view:'front',flight:true,standalone:true,endless:true,cue:'Phone faces you from a low stable stand. Keep both hands down, shoulders, arms, hips and knees visible. Rise/lower comfortably; lean left/right to fly.',limits:'Estimates face-on height and lean. Floor contact, arm loading and technique are not measured.'});\r\nexport const EXERCISES=Object.freeze(Object.fromEntries(rows.map(r=>[r.id,Object.freeze(r)])));\r\nexport const GROUP_EXERCISES=Object.freeze(Object.fromEntries(FOCUS_GROUPS.map(g=>[g.id,Object.freeze(rows.filter(r=>r.group===g.id&&!r.standalone))])));\r\nexport const EXERCISE_COUNT=rows.length;\r\nexport function focusFor(id){return EXERCISES[id]?.group??'cardio';}\r\nexport function exerciseAt(group,index){const choices=GROUP_EXERCISES[group]??GROUP_EXERCISES.chest;return choices[Math.max(0,Math.min(choices.length-1,Math.round(Number(index)||0)))];}\r\n","eye-styles.mjs":"// Eye styles (\"Expression\" in the Face tab). Plain JS so the recipe validator on the server shares it.\r\n// Every style is a free starter choice: expressions have never been part of the unlock catalogue.\r\nexport const EYE_STYLES=Object.freeze([\r\n ['open','Curious'],['sleepy','Unimpressed'],['wide','Wide awake'],\r\n ['anime','Anime'],['squinty','Squinty'],['bloodshot','Bloodshot'],['blind','Blind'],\r\n]);\r\nexport const EYE_STYLE_IDS=Object.freeze(EYE_STYLES.map(([id])=>id));\r\n","flight-score.mjs":"// Flight points belong to this arcade run, independently of workout XP and unlocks.\r\nexport function createFlightScore(){\r\n let hits=0,score=0,multiplier=1;\r\n const read=()=>({hits,score,multiplier});\r\n return {read,hit(){hits++;score+=multiplier;multiplier++;return read();},miss(){multiplier=1;return read();},reset(){hits=0;score=0;multiplier=1;return read();}};\r\n}\r\n","flight-scroll.mjs":"// Negative above the center, positive below it; quadratic acceleration at the edges.\r\nexport function flightScrollSpeed(clientY,top,height){\r\n if(!Number.isFinite(clientY)||!(height>0))return 0;\r\n const offset=Math.max(-1,Math.min(1,2*(clientY-top)/height-1));\r\n return Math.sign(offset)*offset*offset*1600;\r\n}\r\n\r\nexport function mountFlightScroll(viewport,{requestFrame=requestAnimationFrame,cancelFrame=cancelAnimationFrame,now=()=>performance.now()}={}){\r\n let pointerY=null,frame=0,last=0;\r\n const tick=time=>{\r\n  if(pointerY===null)return;\r\n  const rect=viewport.getBoundingClientRect(),dt=Math.min(50,Math.max(0,time-last));last=time;\r\n  viewport.scrollTop+=flightScrollSpeed(pointerY,rect.top,rect.height)*dt/1000;\r\n  frame=requestFrame(tick);\r\n };\r\n return {\r\n  aim(clientY){pointerY=clientY;if(!frame){last=now();frame=requestFrame(tick);}},\r\n  stop(){pointerY=null;if(frame)cancelFrame(frame);frame=0;},\r\n };\r\n}\r\n\r\n// R-1017 one achievements page: tuning knobs for real phones.\r\nexport const FLIGHT_TUNING={\r\n hyperMinMs:650,      // shortest hyper-speed jump\r\n hyperMaxMs:1700,     // longest jump (the whole 250-level route)\r\n hyperPxPerMs:28,     // cruise speed before the min/max clamp\r\n coachLine:.6,        // on open the golden coach's feet sit at this share of the screen; the ship rests just under them\r\n landLine:.5,         // a jumped-to section lands with its marker at this share of the screen\r\n};\r\n// Section menu: 0..chapters-1 are the flight systems (system 1 at the bottom of the page), the last one is the coaches.\r\nexport function flightSections(chapters){\r\n return [...Array.from({length:chapters},(_,chapter)=>({id:`system-${chapter+1}`,chapter})),{id:'coaches',chapter:null}];\r\n}\r\n// scrollTop that puts a point `y` px down the scroller's content at `line` of its height, clamped to the scroll range.\r\nexport function sectionScrollTop(y,viewportHeight,scrollHeight,line=FLIGHT_TUNING.landLine){\r\n return Math.round(Math.max(0,Math.min(scrollHeight-viewportHeight,y-viewportHeight*line)));\r\n}\r\nexport function hyperDuration(distance,t=FLIGHT_TUNING){\r\n return Math.round(Math.max(t.hyperMinMs,Math.min(t.hyperMaxMs,Math.abs(distance)/t.hyperPxPerMs)));\r\n}\r\n// Ease in-out (cubic): a punch out of the start, full warp in the middle, a brake into the section.\r\nexport const hyperEase=u=>u<.5?4*u*u*u:1-(-2*u+2)**3/2;\r\n","flip-display.css":".flip-display{--tile-width:48px;--tile-height:68px;--digit-size:51px;display:inline-flex;align-items:center;gap:5px;font:750 var(--digit-size)/1 ui-monospace,monospace;font-variant-numeric:tabular-nums;letter-spacing:-2px;color:#ffe4a6;vertical-align:middle}\r\n.flip-tile{position:relative;display:block;width:var(--tile-width);height:var(--tile-height);perspective:340px;border:1px solid #7d6584;border-radius:7px;background:#201529;box-shadow:0 4px 0 #130c1a,0 6px 9px #0b061970,inset 0 1px #fff2}\r\n.flip-tile:after{content:\"\";position:absolute;z-index:5;left:-2px;right:-2px;top:calc(50% - 1px);height:2px;background:#130c1a;box-shadow:-1px 0 #b4979c,1px 0 #b4979c;pointer-events:none}\r\n.flip-half{position:absolute;left:0;width:100%;height:50%;overflow:hidden;backface-visibility:hidden;transform-style:preserve-3d}\r\n.flip-half>span{position:absolute;left:0;display:flex;align-items:center;justify-content:center;width:100%;height:var(--tile-height)}\r\n.flip-top,.flip-falling{top:0;border-radius:6px 6px 0 0;background:linear-gradient(#45334c,#2c2034);transform-origin:50% 100%}\r\n.flip-bottom,.flip-landing{bottom:0;border-radius:0 0 6px 6px;background:linear-gradient(#39283f,#4a354c);transform-origin:50% 0}\r\n.flip-bottom>span,.flip-landing>span{bottom:0}\r\n.flip-falling,.flip-landing{display:none;z-index:3}\r\n.is-flipping .flip-falling{display:block;animation:flap-fall 180ms ease-in forwards}\r\n.is-flipping .flip-landing{display:block;animation:flap-land 200ms 180ms cubic-bezier(.15,.65,.25,1.15) both}\r\n.flip-colon{width:13px;text-align:center;color:#ae8faf;letter-spacing:0;font-size:32px;padding-bottom:6px}\r\n@keyframes flap-fall{to{transform:rotateX(-90deg);filter:brightness(.45)}}\r\n@keyframes flap-land{from{transform:rotateX(90deg);filter:brightness(.5)}to{transform:rotateX(0);filter:brightness(1)}}\r\n.simple-counter{gap:18px;flex-wrap:wrap;padding-top:12px}\r\n.simple-counter .counter-readout{min-width:0}\r\n.simple-counter #setNumber{font-size:11px;letter-spacing:1.8px;margin-bottom:9px}\r\n.simple-counter #primary{font-size:var(--digit-size);letter-spacing:-2px;color:#ffe4a6}\r\n.counter-target{display:flex;align-items:baseline;gap:7px;margin-top:11px;color:#bca5c8;font-size:13px}\r\n.counter-target #primaryLabel{font-size:14px;color:#e9d7eb}\r\n.counter-target #goalValue{font-size:14px;font-weight:600;letter-spacing:0;color:#e9d7eb}\r\n.counter-target .target-divider{width:1px;height:11px;background:#816389;margin:0 3px}\r\n.set-actions{flex:1;min-width:115px}.set-actions #stop{font-size:13px;min-height:40px}\r\n.rest-clock #restTime{--tile-width:32px;--tile-height:47px;--digit-size:32px;display:inline-flex;font:750 var(--digit-size)/1 ui-monospace,monospace;letter-spacing:-1px;margin-top:8px;gap:3px}\r\n.rest-clock .flip-colon{font-size:25px;width:8px}.rest-clock small{font-size:11px}\r\n.rest-bottom{flex-wrap:wrap}.rest-bottom>div:last-child{flex:1}\r\n@media(max-width:480px){.simple-counter .set-actions{flex-basis:100%;flex-direction:row;gap:10px}.set-actions .primary-action{flex:1}.set-actions #stop{min-width:70px}.flip-display{--tile-width:43px;--tile-height:61px;--digit-size:46px}.rest-bottom>div:last-child{min-width:125px}}\r\n@media(max-width:350px){.flip-display{--tile-width:39px;--tile-height:57px;--digit-size:42px}.rest-clock #restTime{--tile-width:29px;--tile-height:43px;--digit-size:29px}.rest-bottom{gap:12px}}\r\n@media(prefers-reduced-motion:reduce){.is-flipping .flip-falling{display:none}.is-flipping .flip-landing{display:block;transform:none}}\r\n","flip-display.mjs":"const displays=new WeakMap();\r\nconst whole=value=>Number.isFinite(Number(value))?Math.max(0,Math.floor(Number(value))):0;\r\nexport const countDigits=value=>String(whole(value)).padStart(2,'0');\r\nexport const clockDigits=value=>`${String(Math.floor(whole(value)/60)).padStart(2,'0')}:${String(whole(value)%60).padStart(2,'0')}`;\r\n\r\nfunction tile(character){\r\n const cell=document.createElement('span');\r\n cell.className=character===':'?'flip-colon':'flip-tile';\r\n if(character===':'){cell.textContent=character;return cell;}\r\n for(const name of ['top','bottom','falling','landing']){\r\n  const half=document.createElement('span'),ink=document.createElement('span');\r\n  half.className=`flip-half flip-${name}`;ink.textContent=character;half.append(ink);cell.append(half);\r\n }\r\n cell.value=character;\r\n return cell;\r\n}\r\nfunction settle(cell){\r\n clearTimeout(cell.timer);cell.classList.remove('is-flipping');\r\n for(const half of cell.children)half.firstChild.textContent=cell.value;\r\n}\r\nfunction turn(cell,next,animate){\r\n if(cell.value===next)return;\r\n settle(cell);const previous=cell.value;cell.value=next;\r\n if(!animate){settle(cell);return;}\r\n const [top,bottom,falling,landing]=cell.children;\r\n top.firstChild.textContent=landing.firstChild.textContent=next;\r\n bottom.firstChild.textContent=falling.firstChild.textContent=previous;\r\n // Restart only a changing digit; pose updates never restart the same second.\r\n void cell.offsetWidth;cell.classList.add('is-flipping');\r\n cell.timer=setTimeout(()=>settle(cell),420);\r\n}\r\nexport function setFlipValue(element,value,label=''){\r\n const text=String(value),format=text.includes(':')?'clock':'count';\r\n let state=displays.get(element);\r\n if(state?.text===text&&state.label===label)return;\r\n element.classList.add('flip-display');element.setAttribute('role','img');element.setAttribute('aria-label',`${text} ${label}`.trim());\r\n const animate=!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches&&!document.hidden;\r\n if(!state||state.format!==format||state.text.length!==text.length){\r\n  const previous=state?.format===format&&format==='count'&&state.text.length<=text.length?state.text.padStart(text.length,'0'):text;\r\n  for(const cell of state?.cells||[])clearTimeout(cell.timer);\r\n  const cells=[...previous].map(tile);for(const cell of cells)cell.setAttribute('aria-hidden','true');\r\n  element.replaceChildren(...cells);state={cells,format};\r\n }\r\n [...text].forEach((character,index)=>{if(character!==':')turn(state.cells[index],character,animate);});\r\n state.text=text;state.label=label;displays.set(element,state);\r\n}\r\n","focus-library.css":"/* Movement library and hologram. The workout selector board lives in pod/control-board.css. */\r\n.workout-console .set-actions .primary-action{min-height:66px;justify-content:center}.set-actions .primary-action strong{font-size:18px}\r\n.library-filter{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:14px}\r\n.library-filter select{font:16px Arial,sans-serif;max-width:65%;padding:12px;color:#eefcf9;background:#21332f;border:1px solid #9ac7b9;border-radius:8px}\r\n#library [hidden]{display:none!important}\r\n#library[data-preview=true] .library-header,#library[data-preview=true] .library-filter,#library[data-preview=true] #libraryCount,#library[data-preview=true] .library-footer,#library[data-preview=true] .holo-tools,#library[data-preview=true] #hologramPanel>.library-caption{display:none}\r\n#library[data-preview=true] .glass-shell{min-height:100dvh;padding:16px;gap:0}\r\n#library[data-preview=true] #hologramPanel{min-height:calc(100dvh - 32px);gap:12px;position:relative}\r\n#library[data-preview=true] .holo-heading{position:relative;z-index:3;gap:14px}\r\n#library[data-preview=true] .holo-heading h2{margin:0;font:700 22px/1.3 Arial,sans-serif;color:#f2fff8}\r\n#library[data-preview=true] #backLibrary{min-width:44px;min-height:44px;padding:6px 12px;background:#112922d9}\r\n#library[data-preview=true] #holoStage{min-height:240px;max-height:340px;flex:1 0 240px;border-radius:18px}\r\n#library[data-preview=true] #useHologram{min-height:62px;font-size:20px;flex:none}\r\n.holo-setup{background:#fff0c9;color:#2c2533;border:2px solid #e0b778;border-radius:12px;padding:18px;font:16px/1.45 Arial,sans-serif;box-shadow:0 4px 0 #55384e}\r\n.holo-setup-block+.holo-setup-block{margin-top:18px;padding-top:16px;border-top:1px solid #a9916d}\r\n#library .holo-setup h3{font:700 13px/1.4 Arial,sans-serif;text-transform:uppercase;letter-spacing:1px;color:#61475c;margin:0 0 8px}\r\n#library .holo-setup p{margin:6px 0;color:#352a36}\r\n#library .holo-setup .holo-position{font-size:22px;line-height:1.25;font-weight:700}\r\n.holo-joints{display:flex;gap:7px;flex-wrap:wrap;padding:0;margin:10px 0;list-style:none}\r\n.holo-joints li{background:#412f4d;color:#fff2d8;border-radius:5px;padding:7px 10px;font-size:17px;font-weight:700}\r\n#library .holo-setup .holo-no-feet{border-top:1px solid #a9916d;padding-top:12px;margin-top:16px;font-weight:700;color:#31644d}\r\n@media(min-width:720px){#library[data-preview=true] #hologramPanel{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);grid-template-rows:auto 1fr auto;gap:18px;align-content:center}.holo-heading{grid-column:1/-1}#library[data-preview=true] #holoStage{grid-column:1;grid-row:2/4;min-height:420px;max-height:none}#library[data-preview=true] .holo-setup{grid-column:2;grid-row:2;align-self:center}#library[data-preview=true] #useHologram{grid-column:2;grid-row:3}}\r\n#library .card-kind,#library .card-number,#library .holo-link,#libraryCount{font-size:13px!important}\r\n#library .movement-cards{align-content:start}\r\n#library .movement-cards{flex:none;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}\r\n#library .movement-card{padding:0;overflow:visible;border:0;background:none;box-shadow:none;backdrop-filter:none}\r\n#library .card-select{display:block;position:relative;padding:0!important;aspect-ratio:1;border:1px solid #aaf8e94d!important;border-radius:16px;overflow:hidden;background:radial-gradient(ellipse at 50% 85%,#69f9dd20,transparent 65%),#0c1720!important}\r\n#library .card-select img{display:block;width:100%;height:100%;object-fit:contain}\r\n#library .card-select[aria-pressed=true]{border-color:#9fffe8!important;box-shadow:inset 0 0 0 2px #9fffe8!important}\r\n#library .card-select:focus-visible{outline:3px solid #aaffef;outline-offset:3px}\r\n#library .library-header h1{margin:0}\r\n#library #gestureArea>summary{min-height:44px;display:flex;align-items:center;gap:8px;cursor:pointer;font:14px/1.4 Arial,sans-serif}\r\n#library #gestureArea>summary::before{content:'+';font-size:20px}\r\n#library #gestureArea[open]>summary::before{content:'−'}\r\n#library .holo-setup{padding:14px;font-size:14px}\r\n#library .holo-setup-block+.holo-setup-block{margin-top:12px;padding-top:12px}\r\n#library .holo-setup .holo-position{font-size:20px}\r\n#library .holo-joints li{font-size:14px;padding:6px 8px}\r\n#library .holo-no-feet{font-size:14px}\r\n.workout-console #status:empty,.launch-panel p:empty,.setup-shell p:empty,.install-card p:empty,.rest-feedback:empty{display:none}\r\n\r\n.app-update-banner,.reminders-moved{position:fixed;bottom:max(12px,env(safe-area-inset-bottom));left:12px;right:12px;z-index:9999;display:flex;gap:10px;flex-wrap:wrap;align-items:center;padding:16px;background:#163e34;color:#eefff5;border:2px solid #a6d4bb;border-radius:12px;box-shadow:0 8px 30px #0009;font:16px/1.4 Arial,sans-serif}.app-update-banner[hidden],.reminders-moved[hidden]{display:none}.app-update-banner button,.reminders-moved button{font:14px Arial,sans-serif;min-height:44px;padding:10px}.release-settings{margin-top:20px;border-top:1px solid #917885;padding-top:8px;font:16px/1.5 Arial,sans-serif}.release-settings li{margin:8px 0}.release-settings input{width:100%;padding:12px;font-size:16px}.release-settings button{min-height:44px;margin:8px 8px 8px 0}\r\n/* The reminders-moved notice (launch.mjs): one compact row at the top, above the pod's update banner (pod.css, top:128px). */\r\n.reminders-moved{top:max(12px,env(safe-area-inset-top));bottom:auto;flex-wrap:nowrap;padding:10px}.reminders-moved [data-on]{flex:1;text-align:left}\r\n.training-rewards{padding:10px 12px;margin:8px 0 12px;border:1px solid #729b9a;background:#14282dcc;color:#e5fff6}.training-rewards p{margin:0;font-size:1rem}.training-reward-weapons{display:flex;gap:20px;flex-wrap:wrap}.training-reward-weapons>div{display:flex;align-items:center;gap:8px;min-width:130px;font-size:.875rem}.training-reward-weapons canvas{width:30px;height:54px;image-rendering:pixelated}.training-rewards small{font-size:.875rem;color:#bcd7d3}\r\n.library-kind{display:flex;gap:12px;align-items:center;margin:12px 0}.library-kind select{min-height:44px;max-width:100%;font:inherit}.movement-card .card-select{height:auto;display:flex;flex-direction:column}.movement-level{display:block;padding:8px 10px;color:#fff;font-size:14px}#libraryEmpty{padding:18px;line-height:1.5}\r\n#library .movement-card .card-select{display:flex;flex-direction:column;aspect-ratio:auto;height:auto}#library .card-select img{height:auto;aspect-ratio:1}#library[data-preview=true] .library-kind{display:none}.library-kind select{padding:12px;color:#eefcf9;background:#21332f;border:1px solid #9ac7b9;border-radius:8px}\r\n\r\n/* Full-height hologram terminal: guidance is etched on the glass, below the figure. */\r\n#library[data-preview=true]{overflow:hidden}\r\n#library[data-preview=true] .glass-shell{height:100dvh;min-height:0;overflow:hidden;box-sizing:border-box;padding:10px 12px}\r\n#library[data-preview=true] #hologramPanel{display:flex;flex-direction:column;min-height:0;height:100%;gap:6px}\r\n#library[data-preview=true] .holo-heading{flex:none;min-height:44px;gap:8px}\r\n#library[data-preview=true] .holo-heading h2{font-size:clamp(15px,4vw,19px);line-height:1.2}\r\n#library[data-preview=true] #holoStage{position:relative;flex:1 1 auto;min-height:0;max-height:none;width:100%;border-radius:14px}\r\n#library[data-preview=true] #holoStage canvas{z-index:1}\r\n#library[data-preview=true] #holoStage .holo-setup{position:absolute;z-index:3;left:8px;right:8px;bottom:8px;display:grid;grid-template-columns:1fr 1fr;gap:5px 10px;padding:8px 10px;background:linear-gradient(90deg,#081e29ef,#0b2931e8);border:1px solid #7beed57d;border-radius:5px;box-shadow:0 0 16px #2bdac229;color:#bfffe9;font:11px/1.25 ui-monospace,SFMono-Regular,Consolas,monospace;pointer-events:none}\r\n#library[data-preview=true] #holoStage .holo-setup-block+.holo-setup-block{margin:0;padding:0;border:0}\r\n#library[data-preview=true] #holoStage .holo-setup h3{margin:0 0 3px;color:#58eac9;font:700 10px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em}\r\n#library[data-preview=true] #holoStage .holo-setup p{margin:2px 0;color:#c9f8ef}\r\n#library[data-preview=true] #holoStage .holo-setup .holo-position{font-size:11px;line-height:1.25;color:#f0fff7}\r\n#library[data-preview=true] #holoStage .holo-joints{gap:3px;margin:2px 0 3px;padding:0}\r\n#library[data-preview=true] #holoStage .holo-joints li{padding:2px 4px;border:1px solid #73eac288;border-radius:2px;background:#07372e;color:#e0fff2;font:10px/1.15 ui-monospace,SFMono-Regular,Consolas,monospace}\r\n#library[data-preview=true] #holoStage .holo-no-feet{grid-column:1/-1;margin:0;padding:2px 0 0;border-top:1px solid #5ab8a966;font-size:10px;color:#91ebd1}\r\n#library[data-preview=true] #useHologram{flex:none;min-height:52px;font-size:18px}\r\n@media(min-width:720px){#library[data-preview=true] #hologramPanel{display:flex}#library[data-preview=true] #holoStage{grid-column:auto;grid-row:auto;min-height:0;max-height:none}#library[data-preview=true] #useHologram{grid-column:auto;grid-row:auto}}\r\n@media(max-height:700px){#library[data-preview=true] .glass-shell{padding:6px 10px}#library[data-preview=true] #hologramPanel{gap:4px}#library[data-preview=true] #useHologram{min-height:48px}#library[data-preview=true] #holoStage .holo-setup{padding:5px 7px;bottom:6px}#library[data-preview=true] #holoStage .holo-no-feet{display:none}}\r\n/* The shared portal housing stops above its hardware dock. Keep the terminal inside\r\n   that face and let only the movement card grid scroll on short phones. */\r\n#library[data-route]{overflow:hidden}\r\n#library[data-route] .glass-shell{box-sizing:border-box;height:100%;min-height:0}\r\n#library[data-route][data-preview=false] .movement-cards{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}\r\n#library[data-route][data-preview=true] .glass-shell{height:100%;min-height:0}\r\n#library[data-route][data-preview=true] #hologramPanel{height:100%;min-height:0}\r\n\r\n/* Shared metal terminals leave room for Back and Begin on phone holograms. */\r\n#library.portal-framed.has-pod-chrome .glass-shell{padding-top:54px!important;padding-bottom:38px!important}\r\n","food-live.css":".food-panel .food-camera{display:flex;width:100%;gap:12px;min-height:72px;font-size:18px}\r\n.food-panel h3{margin:10px 0;font-size:18px}.food-panel form{gap:18px}.food-panel .macro-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.food-panel .nutrient{min-width:0}.food-panel .macro-grid .nutrient{padding:14px 10px;border:1px solid #987d8b;border-radius:6px;background:#231d2b}.food-panel .nutrient span{display:block;font-size:14px;color:#e4d1df}.food-panel .nutrient output{display:block;font:700 20px/1.6 var(--pixel-font);color:#ffdaad;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.food-panel .micro-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 22px}.food-panel .micro-grid .nutrient{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid #96788666}.food-panel .micro-grid output{font-size:16px;text-align:right}.food-panel .nutrient-note,.food-panel .food-source{font-size:14px}.food-panel #mealConfirmation{display:grid;gap:12px}.food-panel summary{min-height:44px;align-content:center;cursor:pointer;font-size:14px}.food-panel #mealSaveControls>.main-action{width:100%;margin:14px 0 6px}.food-panel .manual-nutrition{grid-template-columns:1fr 1fr}.food-panel .scan-actions:has(>[hidden]):not(:has(>:not([hidden]))){display:none}.food-panel #mealStatus:empty{display:none}.food-panel #foodSuggestions button{font-size:14px}.food-panel .food-source{overflow-wrap:anywhere}.food-panel .saved-nutrients{display:grid;gap:8px;margin-top:10px}.food-panel .saved-nutrients p{font-size:14px;margin:0}.launch-panel .reminder-live{color:#d5edce;font-size:16px;font-variant-numeric:tabular-nums}.launch-panel .reminder-live[data-state=waiting]{color:#f1d1a6}.launch-panel .reminder-next{font-size:14px;color:#e7d49f}.launch-panel .reminder-tuning label{font-size:14px}.launch-panel .message-preview small{font-size:12px}\r\n@media(max-width:520px){.food-panel .macro-grid{grid-template-columns:1fr 1fr}.food-panel .micro-grid{gap:0 16px}.food-panel .micro-grid .nutrient{display:block}.food-panel .micro-grid output{text-align:left}.food-panel .macro-grid .nutrient{padding:10px}.food-panel .fields{grid-template-columns:minmax(0,1fr) 100px}}\r\n@media(max-width:340px){.food-panel .fields,.food-panel .manual-nutrition{grid-template-columns:1fr}.food-panel .micro-grid{column-gap:8px}}\r\n.food-panel .food-sheet{display:contents}.food-panel #cancelMeal{width:100%;margin:0 0 6px}.food-panel #foodDial{display:grid;gap:6px}.food-panel #foodDial:focus{outline:none}.food-panel .food-dial-head{display:flex;justify-content:space-between;align-items:center;gap:12px}.food-panel .food-dial-head h3{margin:0}.food-panel #foodDial p{margin:0}.food-panel .food-dial-big{font:700 26px/1.3 var(--pixel-font);color:#ffcd87;font-variant-numeric:tabular-nums}.food-panel .food-dial-list{margin:4px 0 0;padding:0;list-style:none;display:grid;gap:6px;font-size:15px}.food-panel .food-dial-list li{padding:8px 0;border-bottom:1px solid #59416680}\r\n/* #3 (W3-3A): the floating pyramid IS the Food menu. launch.mjs adds .pyramid-mode while the 3D scanner is up; the room\r\n   and pyramid fill the screen, and the photo flow, meal form and dial cards ride in a bottom sheet over the room. */\r\n#mealsPanel.pyramid-mode{position:fixed;inset:0;width:100%;height:100vh;height:100dvh;max-width:none;max-height:none;margin:0;padding:0;border:0;border-radius:0;overflow:hidden;background:#bba16e}\r\n#mealsPanel.pyramid-mode>header{position:absolute;top:0;left:0;right:0;z-index:4;margin:0;padding:max(12px,env(safe-area-inset-top)) 16px 14px;background:linear-gradient(#21172ff0 62%,#21172f00)}\r\n#mealsPanel.pyramid-mode #pyramidScanner{position:absolute;inset:0;width:auto;height:auto;margin:0;border-radius:0}\r\n#mealsPanel.pyramid-mode #foodCamera,#mealsPanel.pyramid-mode #coachNutritionTargets,#mealsPanel.pyramid-mode:not([data-dial=today]) .recent-meals,#mealsPanel.pyramid-mode .food-sheet>:is(#foodStatus,#mealStatus):empty{display:none}\r\n#mealsPanel.pyramid-mode .food-sheet{position:absolute;left:0;right:0;bottom:0;z-index:3;display:flex;flex-direction:column;gap:8px;max-height:58%;overflow:auto;overscroll-behavior:contain;padding:0 12px max(12px,env(safe-area-inset-bottom));box-sizing:border-box;pointer-events:none}\r\n#mealsPanel.pyramid-mode .food-sheet>*{position:relative;z-index:1;margin:0;pointer-events:auto}\r\n#mealsPanel.pyramid-mode .food-sheet>:is(#foodDial,#foodStatus,#mealStatus,.recent-meals),#mealsPanel.pyramid-mode #mealForm:has(#mealConfirmation:not([hidden])){padding:12px 14px;border:1px solid #8b679c;border-radius:14px;background:#21172ff2;box-shadow:0 6px 24px #0007}\r\n#mealsPanel.pyramid-mode .food-sheet>:is(#foodStatus,#mealStatus){padding:8px 14px;color:#f4e7fb}\r\n#mealsPanel.pyramid-mode #mealForm{gap:10px}\r\n/* The Macros/Micros lists stay as the screen-reader copy of the numbers on the pyramid's screens. */\r\n#mealsPanel.pyramid-mode #mealForm>section{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\r\n/* #31: the lens opens the existing photo flow full screen, under the header and the sheet's controls. */\r\n#mealsPanel.pyramid-mode #mealScanStage{position:fixed;inset:0;z-index:0;margin:0;border:0;border-radius:0}\r\n#mealsPanel.pyramid-mode #mealScanStage .meal-preview{height:100%;max-height:none;min-height:0;margin:0;object-fit:cover}\r\n/* The scan read-out must show without the optional pocket-hardware.css skin (it only loads for unlocked accounts). */\r\n#mealsPanel.pyramid-mode .meal-scan-overlay{position:absolute;inset:0;pointer-events:none;color:#c3ffec;font:12px/1.5 var(--pixel-font,ui-monospace),monospace;text-shadow:0 1px 3px #000}\r\n#mealsPanel.pyramid-mode .scan-heading{position:absolute;left:12px;right:12px;top:calc(max(12px,env(safe-area-inset-top)) + 62px);display:flex;justify-content:space-between;gap:8px;letter-spacing:1px}\r\n#mealsPanel.pyramid-mode .scan-readout{position:absolute;left:12px;right:12px;top:calc(max(12px,env(safe-area-inset-top)) + 90px);bottom:auto;padding:8px 10px;border-left:2px solid #9dffe1;background:#001b21d9}\r\n#mealsPanel.pyramid-mode .scan-readout>span{display:block}\r\n#mealsPanel.pyramid-mode .scan-actions{justify-content:center}\r\n#mealsPanel.pyramid-mode #mealConfirmation{gap:8px}#mealsPanel.pyramid-mode #mealConfirmation .fields{gap:10px}#mealsPanel.pyramid-mode #mealForm input,#mealsPanel.pyramid-mode #mealForm select{padding:9px 11px}#mealsPanel.pyramid-mode #nutritionBasis{margin:0}\r\n#mealsPanel.pyramid-mode #mealSaveControls:not([hidden]){display:grid;grid-template-columns:1fr 1fr;gap:8px}#mealsPanel.pyramid-mode #mealSaveControls>:not(button){grid-column:1/-1}#mealsPanel.pyramid-mode #mealSaveControls>button{width:100%;margin:0}#mealsPanel.pyramid-mode .food-source{margin:0;font-size:12px}\r\n/* Release 5: as a route Food stops at the bottom bar's top edge (routes.mjs keeps the bar inside the open route); opened\r\n   from the portal it fits the metal frame's window (portal.css .portal-framed sets the box and --face-*), and so does the\r\n   full-screen photo flow. */\r\n#mealsPanel.pyramid-mode[data-route]{height:calc(100vh - var(--myr5-bar));height:calc(100dvh - var(--myr5-bar))}\r\n#mealsPanel.pyramid-mode[data-route] #mealScanStage{bottom:var(--myr5-bar)}\r\n#mealsPanel.pyramid-mode.portal-framed #mealScanStage{inset:auto;left:var(--face-left);top:var(--face-top);width:var(--face-width);height:var(--face-height)}\r\n.reminders-hatch-knob{display:none}\r\n","food-worker.mjs":"let classifier;\r\nconst MODEL='onnx-community/swin-finetuned-food101-ONNX',REVISION='e5e50bfc6425aa546f3b4421ca8bd79d0dd610b8';\r\nself.onmessage=async({data})=>{\r\n try{\r\n  if(!classifier){\r\n   postMessage({type:'progress',stage:'loading',text:'Loading the recognition model on your device…'});\r\n   // transformers.min.js is the self-contained browser build (onnxruntime bundled). transformers.web.js imports the\r\n   // bare specifiers \"onnxruntime-common\"/\"onnxruntime-web\" (a bundler input), which a browser worker cannot resolve.\r\n   const {pipeline,env}=await import('https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1/dist/transformers.min.js');\r\n   env.allowLocalModels=false;env.backends.onnx.wasm.numThreads=1;\r\n   classifier=await pipeline('image-classification',MODEL,{revision:REVISION,device:'wasm',dtype:'q8',progress_callback:p=>{if(p.status==='progress'&&Number.isFinite(p.progress))postMessage({type:'progress',stage:'loading',progress:p.progress,text:`Model file download · ${Math.round(p.progress)}%`});}});\r\n  }\r\n  postMessage({type:'progress',stage:'analyzing',text:'Calculating image features and comparing food matches…'});\r\n  const ranked=await classifier(data.image,{top_k:4});\r\n  const uncertain=ranked[0].score<.5||ranked[0].score<2*ranked[1].score;\r\n  postMessage({type:'result',items:ranked[0].score<.2?[]:ranked.map(r=>({...r,label:r.label.replaceAll('_',' ')})),uncertain});\r\n }catch(error){console.warn('Food recognition failed',error);classifier=null;postMessage({type:'error',text:'Photo recognition could not finish. Try again on Wi-Fi or type the food name.'});}\r\n};\r\n","food/nutrition-fdc.json":"{\r\n \"apple pie\": {\r\n  \"fdcId\": 2707995,\r\n  \"description\": \"Pie, apple\",\r\n  \"servingName\": \"1 regular slice\",\r\n  \"servingG\": 150,\r\n  \"kcal\": 444,\r\n  \"proteinG\": 4.1,\r\n  \"fatG\": 22.9,\r\n  \"carbsG\": 56.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 43.5,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 60,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"baby back ribs\": {\r\n  \"fdcId\": 2705894,\r\n  \"description\": \"Pork, ribs\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 343,\r\n  \"proteinG\": 21.9,\r\n  \"fatG\": 20.4,\r\n  \"carbsG\": 16.4,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.69,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 1.08,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"baklava\": {\r\n  \"fdcId\": 2708044,\r\n  \"description\": \"Baklava\",\r\n  \"servingName\": \"1 piece\",\r\n  \"servingG\": 80,\r\n  \"kcal\": 352,\r\n  \"proteinG\": 5.3,\r\n  \"fatG\": 23.5,\r\n  \"carbsG\": 30,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 125.6,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.49,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"beef carpaccio\": {\r\n  \"fdcId\": 2705822,\r\n  \"description\": \"Beef, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 312,\r\n  \"proteinG\": 36.6,\r\n  \"fatG\": 17.6,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 3.08,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 9.45,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"beef tartare\": {\r\n  \"fdcId\": 2706394,\r\n  \"description\": \"Steak tartare\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 225,\r\n  \"kcal\": 486,\r\n  \"proteinG\": 39.3,\r\n  \"fatG\": 35.1,\r\n  \"carbsG\": 0.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 4.57,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 47.25,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"beet salad\": {\r\n  \"fdcId\": 2709875,\r\n  \"description\": \"Beets, fresh, cooked, fat added\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 175,\r\n  \"kcal\": 117,\r\n  \"proteinG\": 2.9,\r\n  \"fatG\": 4.9,\r\n  \"carbsG\": 16.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 162.75,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 7.35,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"beignets\": {\r\n  \"fdcId\": 2708071,\r\n  \"description\": \"Beignet\",\r\n  \"servingName\": \"3 beignets\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 563,\r\n  \"proteinG\": 7.4,\r\n  \"fatG\": 27.2,\r\n  \"carbsG\": 72.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 129.6,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.71,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"bibimbap\": {\r\n  \"fdcId\": 2708950,\r\n  \"description\": \"Bibimbap, Korean\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 162,\r\n  \"kcal\": 123,\r\n  \"proteinG\": 8.9,\r\n  \"fatG\": 4,\r\n  \"carbsG\": 13.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 183.06,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 17.01,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"bread pudding\": {\r\n  \"fdcId\": 2705680,\r\n  \"description\": \"Pudding, bread\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 200,\r\n  \"kcal\": 312,\r\n  \"proteinG\": 11.2,\r\n  \"fatG\": 9.7,\r\n  \"carbsG\": 47.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.44,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 154,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"breakfast burrito\": {\r\n  \"fdcId\": 170787,\r\n  \"description\": \"Fast foods, breakfast burrito, with egg, cheese, and sausage\",\r\n  \"servingName\": \"1 burrito\",\r\n  \"servingG\": 109,\r\n  \"kcal\": 302,\r\n  \"proteinG\": 12.1,\r\n  \"fatG\": 17,\r\n  \"carbsG\": 25,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.81,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 68.67,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"bruschetta\": {\r\n  \"fdcId\": 2707652,\r\n  \"description\": \"Bruschetta\",\r\n  \"servingName\": \"3 slices\",\r\n  \"servingG\": 129,\r\n  \"kcal\": 221,\r\n  \"proteinG\": 5.5,\r\n  \"fatG\": 9.8,\r\n  \"carbsG\": 27.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.27,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 58.05,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"caesar salad\": {\r\n  \"fdcId\": 2709591,\r\n  \"description\": \"Caesar salad, with romaine, no dressing\",\r\n  \"servingName\": \"1 Wendy's side salad\",\r\n  \"servingG\": 113,\r\n  \"kcal\": 87,\r\n  \"proteinG\": 4.5,\r\n  \"fatG\": 3.9,\r\n  \"carbsG\": 8.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 449.74,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 55.37,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"cannoli\": {\r\n  \"fdcId\": 2708029,\r\n  \"description\": \"Cream puff, eclair, custard or cream filled, NS as to icing\",\r\n  \"servingName\": \"1 cream puff (3-1/2\\\" x 2\\\")\",\r\n  \"servingG\": 112,\r\n  \"kcal\": 314,\r\n  \"proteinG\": 4.7,\r\n  \"fatG\": 27.3,\r\n  \"carbsG\": 12.4,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 266.56,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 31.36,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"caprese salad\": {\r\n  \"fdcId\": 2705818,\r\n  \"description\": \"Mozzarella cheese, tomato, and basil, with oil and vinegar dressing\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 222,\r\n  \"proteinG\": 11.5,\r\n  \"fatG\": 17.4,\r\n  \"carbsG\": 6.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.74,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 142.4,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"carrot cake\": {\r\n  \"fdcId\": 2707859,\r\n  \"description\": \"Cake or cupcake, carrot\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 120,\r\n  \"kcal\": 449,\r\n  \"proteinG\": 3,\r\n  \"fatG\": 21,\r\n  \"carbsG\": 62.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.25,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 120,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"ceviche\": {\r\n  \"fdcId\": 2706463,\r\n  \"description\": \"Ceviche\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 250,\r\n  \"kcal\": 158,\r\n  \"proteinG\": 26.3,\r\n  \"fatG\": 1.9,\r\n  \"carbsG\": 9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 3.75,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 12.75,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"cheesecake\": {\r\n  \"fdcId\": 2707861,\r\n  \"description\": \"Cheesecake, plain\",\r\n  \"servingName\": \"1 piece/slice, any size\",\r\n  \"servingG\": 175,\r\n  \"kcal\": 698,\r\n  \"proteinG\": 9.3,\r\n  \"fatG\": 46.4,\r\n  \"carbsG\": 60.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 386.75,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 36.75,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"cheese plate\": {\r\n  \"fdcId\": 2705709,\r\n  \"description\": \"Cheese, Cheddar\",\r\n  \"servingName\": \"1 cup, diced\",\r\n  \"servingG\": 132,\r\n  \"kcal\": 540,\r\n  \"proteinG\": 30.8,\r\n  \"fatG\": 44.9,\r\n  \"carbsG\": 3.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.4,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 417.12,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"chicken curry\": {\r\n  \"fdcId\": 2706437,\r\n  \"description\": \"Chicken curry\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 240,\r\n  \"kcal\": 257,\r\n  \"proteinG\": 15.6,\r\n  \"fatG\": 15.6,\r\n  \"carbsG\": 15.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 21.36,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.46,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"chicken quesadilla\": {\r\n  \"fdcId\": 2708593,\r\n  \"description\": \"Quesadilla, chicken\",\r\n  \"servingName\": \"1 full circle\",\r\n  \"servingG\": 140,\r\n  \"kcal\": 402,\r\n  \"proteinG\": 20.2,\r\n  \"fatG\": 18.2,\r\n  \"carbsG\": 38.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 75.6,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.35,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"chicken wings\": {\r\n  \"fdcId\": 2706060,\r\n  \"description\": \"Chicken wing, rotisserie\",\r\n  \"servingName\": \"4 pieces\",\r\n  \"servingG\": 140,\r\n  \"kcal\": 360,\r\n  \"proteinG\": 32.8,\r\n  \"fatG\": 25.3,\r\n  \"carbsG\": 0.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.63,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 36.4,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"chocolate cake\": {\r\n  \"fdcId\": 2707871,\r\n  \"description\": \"Cake, chocolate, flourless\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 120,\r\n  \"kcal\": 492,\r\n  \"proteinG\": 5.8,\r\n  \"fatG\": 35.3,\r\n  \"carbsG\": 45.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 214.8,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.24,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"chocolate mousse\": {\r\n  \"fdcId\": 168777,\r\n  \"description\": \"Desserts, mousse, chocolate, prepared-from-recipe\",\r\n  \"servingName\": \"0.5 cup\",\r\n  \"servingG\": 202,\r\n  \"kcal\": 455,\r\n  \"proteinG\": 8.4,\r\n  \"fatG\": 32.3,\r\n  \"carbsG\": 32.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.95,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 282.8,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"churros\": {\r\n  \"fdcId\": 2708070,\r\n  \"description\": \"Churros\",\r\n  \"servingName\": \"3 regulars\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 566,\r\n  \"proteinG\": 8.2,\r\n  \"fatG\": 30,\r\n  \"carbsG\": 66,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 143.1,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.92,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"clam chowder\": {\r\n  \"fdcId\": 2707138,\r\n  \"description\": \"Soup, Manhattan clam chowder\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 86,\r\n  \"proteinG\": 6.1,\r\n  \"fatG\": 1.2,\r\n  \"carbsG\": 12.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 3.09,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 11.27,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"club sandwich\": {\r\n  \"fdcId\": 2706993,\r\n  \"description\": \"Club sandwich on white\",\r\n  \"servingName\": \"1 sandwich, any size\",\r\n  \"servingG\": 130,\r\n  \"kcal\": 289,\r\n  \"proteinG\": 19.6,\r\n  \"fatG\": 9.7,\r\n  \"carbsG\": 30,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 66.3,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.38,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"crab cakes\": {\r\n  \"fdcId\": 2706549,\r\n  \"description\": \"Crab, cake\",\r\n  \"servingName\": \"1 cake or patty\",\r\n  \"servingG\": 85,\r\n  \"kcal\": 182,\r\n  \"proteinG\": 12.2,\r\n  \"fatG\": 12.7,\r\n  \"carbsG\": 3.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.03,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.31,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"creme brulee\": {\r\n  \"fdcId\": 2705684,\r\n  \"description\": \"Creme brulee\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 260,\r\n  \"kcal\": 294,\r\n  \"proteinG\": 10.6,\r\n  \"fatG\": 7.4,\r\n  \"carbsG\": 46.4,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.88,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 174.2,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"croque madame\": {\r\n  \"fdcId\": 2707335,\r\n  \"description\": \"Egg white sandwich, with cheese\",\r\n  \"servingName\": \"1 regular\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 265,\r\n  \"proteinG\": 15,\r\n  \"fatG\": 10.1,\r\n  \"carbsG\": 28.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 60.75,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.36,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"cup cakes\": {\r\n  \"fdcId\": 2707853,\r\n  \"description\": \"Cake or cupcake, NFS\",\r\n  \"servingName\": \"1 piece/slice, any size\",\r\n  \"servingG\": 175,\r\n  \"kcal\": 616,\r\n  \"proteinG\": 5,\r\n  \"fatG\": 25.8,\r\n  \"carbsG\": 93.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.52,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 38.5,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"deviled eggs\": {\r\n  \"fdcId\": 2707181,\r\n  \"description\": \"Egg, deviled\",\r\n  \"servingName\": \"5 eggs\",\r\n  \"servingG\": 150,\r\n  \"kcal\": 297,\r\n  \"proteinG\": 17.3,\r\n  \"fatG\": 23.9,\r\n  \"carbsG\": 1.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.53,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 205.5,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"donuts\": {\r\n  \"fdcId\": 2708062,\r\n  \"description\": \"Doughnut, NFS\",\r\n  \"servingName\": \"1 doughnut\",\r\n  \"servingG\": 75,\r\n  \"kcal\": 320,\r\n  \"proteinG\": 4.1,\r\n  \"fatG\": 17.1,\r\n  \"carbsG\": 37.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 72,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.22,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"dumplings\": {\r\n  \"fdcId\": 2706543,\r\n  \"description\": \"Chicken or turkey with dumplings\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 244,\r\n  \"kcal\": 351,\r\n  \"proteinG\": 38.3,\r\n  \"fatG\": 13.5,\r\n  \"carbsG\": 19.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.56,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 39.04,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"edamame\": {\r\n  \"fdcId\": 2707436,\r\n  \"description\": \"Edamame, cooked\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 224,\r\n  \"proteinG\": 18.5,\r\n  \"fatG\": 12.1,\r\n  \"carbsG\": 13.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 457.6,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.66,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"eggs benedict\": {\r\n  \"fdcId\": 2707180,\r\n  \"description\": \"Egg, Benedict\",\r\n  \"servingName\": \"1 egg\",\r\n  \"servingG\": 155,\r\n  \"kcal\": 446,\r\n  \"proteinG\": 21.4,\r\n  \"fatG\": 34.3,\r\n  \"carbsG\": 13.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 359.6,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.76,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"escargots\": {\r\n  \"fdcId\": 167744,\r\n  \"description\": \"Mollusks, snail, raw\",\r\n  \"servingName\": \"3 oz\",\r\n  \"servingG\": 85,\r\n  \"kcal\": 77,\r\n  \"proteinG\": 13.7,\r\n  \"fatG\": 1.2,\r\n  \"carbsG\": 1.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 4.25,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.43,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"falafel\": {\r\n  \"fdcId\": 2707408,\r\n  \"description\": \"Falafel\",\r\n  \"servingName\": \"9 patties\",\r\n  \"servingG\": 153,\r\n  \"kcal\": 786,\r\n  \"proteinG\": 12.7,\r\n  \"fatG\": 63,\r\n  \"carbsG\": 44.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 11.84,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 120.87,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"filet mignon\": {\r\n  \"fdcId\": 2705829,\r\n  \"description\": \"Beef, steak, ribeye, lean and fat eaten\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 390,\r\n  \"proteinG\": 31.7,\r\n  \"fatG\": 29.2,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.81,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 8.1,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"fish and chips\": {\r\n  \"fdcId\": 2706244,\r\n  \"description\": \"Fish, cod, fried\",\r\n  \"servingName\": \"1 small/regular fillet\",\r\n  \"servingG\": 140,\r\n  \"kcal\": 304,\r\n  \"proteinG\": 18.9,\r\n  \"fatG\": 17.5,\r\n  \"carbsG\": 16.4,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.93,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.72,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"foie gras\": {\r\n  \"fdcId\": 172929,\r\n  \"description\": \"Pate, goose liver, smoked, canned\",\r\n  \"servingName\": \"5 oz\",\r\n  \"servingG\": 142,\r\n  \"kcal\": 656,\r\n  \"proteinG\": 16.2,\r\n  \"fatG\": 62.3,\r\n  \"carbsG\": 6.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 13.35,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 1421.42,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"french fries\": {\r\n  \"fdcId\": 2709456,\r\n  \"description\": \"Potato, french fries, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 60,\r\n  \"kcal\": 135,\r\n  \"proteinG\": 1.5,\r\n  \"fatG\": 8.4,\r\n  \"carbsG\": 13.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.25,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 4.68,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"french onion soup\": {\r\n  \"fdcId\": 2710108,\r\n  \"description\": \"Soup, French onion\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 265,\r\n  \"proteinG\": 13.4,\r\n  \"fatG\": 11,\r\n  \"carbsG\": 29,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.47,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 61.25,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"french toast\": {\r\n  \"fdcId\": 2708331,\r\n  \"description\": \"French toast, NFS\",\r\n  \"servingName\": \"1 slice, any size\",\r\n  \"servingG\": 65,\r\n  \"kcal\": 177,\r\n  \"proteinG\": 6.7,\r\n  \"fatG\": 7.4,\r\n  \"carbsG\": 21,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 45.5,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.27,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"fried calamari\": {\r\n  \"fdcId\": 171982,\r\n  \"description\": \"Mollusks, squid, mixed species, cooked, fried\",\r\n  \"servingName\": \"3 oz\",\r\n  \"servingG\": 85,\r\n  \"kcal\": 149,\r\n  \"proteinG\": 15.2,\r\n  \"fatG\": 6.4,\r\n  \"carbsG\": 6.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.05,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 3.57,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"fried rice\": {\r\n  \"fdcId\": 2708951,\r\n  \"description\": \"Rice, fried, meatless\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 166,\r\n  \"kcal\": 289,\r\n  \"proteinG\": 6.4,\r\n  \"fatG\": 5.3,\r\n  \"carbsG\": 54,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 6.31,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 36.52,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"frozen yogurt\": {\r\n  \"fdcId\": 2705451,\r\n  \"description\": \"Frozen yogurt, NFS\",\r\n  \"servingName\": \"1 scoop\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 203,\r\n  \"proteinG\": 4.8,\r\n  \"fatG\": 5.8,\r\n  \"carbsG\": 34.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 78.4,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.11,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"garlic bread\": {\r\n  \"fdcId\": 2707626,\r\n  \"description\": \"Garlic bread, NFS\",\r\n  \"servingName\": \"1 mini baguette (about 9\\\" long)\",\r\n  \"servingG\": 185,\r\n  \"kcal\": 646,\r\n  \"proteinG\": 15.4,\r\n  \"fatG\": 30.7,\r\n  \"carbsG\": 77,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 308.95,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.18,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"gnocchi\": {\r\n  \"fdcId\": 2708721,\r\n  \"description\": \"Gnocchi, cheese\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 70,\r\n  \"kcal\": 125,\r\n  \"proteinG\": 6,\r\n  \"fatG\": 8.4,\r\n  \"carbsG\": 6.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 92.4,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.22,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"greek salad\": {\r\n  \"fdcId\": 2709830,\r\n  \"description\": \"Greek Salad, no dressing\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 105,\r\n  \"kcal\": 50,\r\n  \"proteinG\": 2.9,\r\n  \"fatG\": 2.4,\r\n  \"carbsG\": 4.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 252,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 37.8,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"grilled cheese sandwich\": {\r\n  \"fdcId\": 2705794,\r\n  \"description\": \"Cheese sandwich, NFS\",\r\n  \"servingName\": \"1 sandwich\",\r\n  \"servingG\": 102,\r\n  \"kcal\": 303,\r\n  \"proteinG\": 12.7,\r\n  \"fatG\": 13.6,\r\n  \"carbsG\": 32.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.62,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 72.42,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"grilled salmon\": {\r\n  \"fdcId\": 2706284,\r\n  \"description\": \"Fish, salmon, raw\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 254,\r\n  \"proteinG\": 27.6,\r\n  \"fatG\": 15.1,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 4.67,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 14.85,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"guacamole\": {\r\n  \"fdcId\": 2709307,\r\n  \"description\": \"Guacamole, NFS\",\r\n  \"servingName\": \"2 tablespoon\",\r\n  \"servingG\": 30,\r\n  \"kcal\": 47,\r\n  \"proteinG\": 0.6,\r\n  \"fatG\": 4.3,\r\n  \"carbsG\": 2.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 23.7,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 0.6,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"gyoza\": {\r\n  \"fdcId\": 2708705,\r\n  \"description\": \"Wonton, dumpling or pot sticker, fried\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 75,\r\n  \"kcal\": 144,\r\n  \"proteinG\": 6.6,\r\n  \"fatG\": 8.3,\r\n  \"carbsG\": 10.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.21,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 6.6,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"hamburger\": {\r\n  \"fdcId\": 2706920,\r\n  \"description\": \"Hamburger, NFS\",\r\n  \"servingName\": \"1 hamburger\",\r\n  \"servingG\": 145,\r\n  \"kcal\": 418,\r\n  \"proteinG\": 25.4,\r\n  \"fatG\": 20.9,\r\n  \"carbsG\": 30,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.36,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 73.95,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"hot and sour soup\": {\r\n  \"fdcId\": 2707136,\r\n  \"description\": \"Soup, hot and sour\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 96,\r\n  \"proteinG\": 6.3,\r\n  \"fatG\": 3,\r\n  \"carbsG\": 10.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.25,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 0.96,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"hot dog\": {\r\n  \"fdcId\": 2706757,\r\n  \"description\": \"Frankfurters or hot dogs and sauerkraut\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 147,\r\n  \"kcal\": 291,\r\n  \"proteinG\": 11.1,\r\n  \"fatG\": 25.4,\r\n  \"carbsG\": 5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.75,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 7.5,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"huevos rancheros\": {\r\n  \"fdcId\": 2707191,\r\n  \"description\": \"Huevos rancheros\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 163,\r\n  \"proteinG\": 9,\r\n  \"fatG\": 9,\r\n  \"carbsG\": 11.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.5,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.05,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"hummus\": {\r\n  \"fdcId\": 2707402,\r\n  \"description\": \"Hummus, plain\",\r\n  \"servingName\": \"2 tablespoon\",\r\n  \"servingG\": 30,\r\n  \"kcal\": 73,\r\n  \"proteinG\": 2.2,\r\n  \"fatG\": 5.1,\r\n  \"carbsG\": 4.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 0.52,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 10.8,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"ice cream\": {\r\n  \"fdcId\": 2705629,\r\n  \"description\": \"Ice cream, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 298,\r\n  \"proteinG\": 4.8,\r\n  \"fatG\": 15.3,\r\n  \"carbsG\": 35.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.51,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 151.2,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"lasagna\": {\r\n  \"fdcId\": 2708750,\r\n  \"description\": \"Lasagna with meat\",\r\n  \"servingName\": \"1 piece (1/6 of 8\\\" square, approx 2-1/2\\\" x 4\\\")\",\r\n  \"servingG\": 206,\r\n  \"kcal\": 286,\r\n  \"proteinG\": 15.3,\r\n  \"fatG\": 10.2,\r\n  \"carbsG\": 33.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.71,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.02,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"lobster bisque\": {\r\n  \"fdcId\": 2707140,\r\n  \"description\": \"Soup, bisque\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 255,\r\n  \"kcal\": 191,\r\n  \"proteinG\": 5.8,\r\n  \"fatG\": 12.3,\r\n  \"carbsG\": 15,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.38,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 255,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"lobster roll sandwich\": {\r\n  \"fdcId\": 2706825,\r\n  \"description\": \"Lobster salad\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 182,\r\n  \"kcal\": 340,\r\n  \"proteinG\": 18.7,\r\n  \"fatG\": 28,\r\n  \"carbsG\": 2.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.4,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.28,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"macaroni and cheese\": {\r\n  \"fdcId\": 2708811,\r\n  \"description\": \"Macaroni or noodles with cheese\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 230,\r\n  \"kcal\": 513,\r\n  \"proteinG\": 20,\r\n  \"fatG\": 24.2,\r\n  \"carbsG\": 53.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 243.8,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.62,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"macarons\": {\r\n  \"fdcId\": 2707941,\r\n  \"description\": \"Cookie, meringue\",\r\n  \"servingName\": \"12 cookies\",\r\n  \"servingG\": 60,\r\n  \"kcal\": 210,\r\n  \"proteinG\": 3.4,\r\n  \"fatG\": 0.2,\r\n  \"carbsG\": 48.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.02,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 1.2,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"miso soup\": {\r\n  \"fdcId\": 2707455,\r\n  \"description\": \"Soup, miso or tofu\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 59,\r\n  \"proteinG\": 5.4,\r\n  \"fatG\": 2.1,\r\n  \"carbsG\": 5.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 29.4,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 4.66,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"mussels\": {\r\n  \"fdcId\": 2706350,\r\n  \"description\": \"Mussels\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 147,\r\n  \"proteinG\": 20.3,\r\n  \"fatG\": 3.8,\r\n  \"carbsG\": 6.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 17.43,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 54,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"nachos\": {\r\n  \"fdcId\": 2708576,\r\n  \"description\": \"Nachos, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 358,\r\n  \"proteinG\": 10.3,\r\n  \"fatG\": 21.4,\r\n  \"carbsG\": 32.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.57,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.92,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"omelette\": {\r\n  \"fdcId\": 2707198,\r\n  \"description\": \"Egg omelet or scrambled egg, NS as to fat\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 250,\r\n  \"proteinG\": 15.6,\r\n  \"fatG\": 20.3,\r\n  \"carbsG\": 1.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.03,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 260.55,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"onion rings\": {\r\n  \"fdcId\": 2708245,\r\n  \"description\": \"Onion flavored rings\",\r\n  \"servingName\": \"1 large single serving bag\",\r\n  \"servingG\": 85,\r\n  \"kcal\": 424,\r\n  \"proteinG\": 6.5,\r\n  \"fatG\": 19.2,\r\n  \"carbsG\": 55.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 93.5,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.29,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"oysters\": {\r\n  \"fdcId\": 2706351,\r\n  \"description\": \"Oysters, raw\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 225,\r\n  \"kcal\": 115,\r\n  \"proteinG\": 12.8,\r\n  \"fatG\": 3.8,\r\n  \"carbsG\": 6.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 19.69,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.91,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"pad thai\": {\r\n  \"fdcId\": 2708804,\r\n  \"description\": \"Pad Thai, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 200,\r\n  \"kcal\": 308,\r\n  \"proteinG\": 16.3,\r\n  \"fatG\": 15,\r\n  \"carbsG\": 28.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.26,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 48,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"paella\": {\r\n  \"fdcId\": 2706711,\r\n  \"description\": \"Paella with seafood\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 240,\r\n  \"kcal\": 343,\r\n  \"proteinG\": 19.3,\r\n  \"fatG\": 11.4,\r\n  \"carbsG\": 39.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.98,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 25.44,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"pancakes\": {\r\n  \"fdcId\": 2708294,\r\n  \"description\": \"Pancakes, NFS\",\r\n  \"servingName\": \"1 extra large pancake\",\r\n  \"servingG\": 150,\r\n  \"kcal\": 423,\r\n  \"proteinG\": 11.1,\r\n  \"fatG\": 18.1,\r\n  \"carbsG\": 53,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.57,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.72,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"panna cotta\": {\r\n  \"fdcId\": 2705682,\r\n  \"description\": \"Custard\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 260,\r\n  \"kcal\": 252,\r\n  \"proteinG\": 13.2,\r\n  \"fatG\": 9.5,\r\n  \"carbsG\": 28.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.07,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 213.2,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n },\r\n \"peking duck\": {\r\n  \"fdcId\": 2706141,\r\n  \"description\": \"Duck, Peking\",\r\n  \"servingName\": \"1 cup, diced, cooked\",\r\n  \"servingG\": 140,\r\n  \"kcal\": 216,\r\n  \"proteinG\": 7.4,\r\n  \"fatG\": 16.5,\r\n  \"carbsG\": 9.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 17.08,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.29,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"pho\": {\r\n  \"fdcId\": 2707124,\r\n  \"description\": \"Soup, pho, with meat\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 189,\r\n  \"proteinG\": 14.2,\r\n  \"fatG\": 8.2,\r\n  \"carbsG\": 13.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.25,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 14.7,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"pizza\": {\r\n  \"fdcId\": 2708614,\r\n  \"description\": \"Pizza, cheese, from restaurant or fast food, NS as to type of crust\",\r\n  \"servingName\": \"1 personal size pizza (5-7\\\" diameter)\",\r\n  \"servingG\": 175,\r\n  \"kcal\": 466,\r\n  \"proteinG\": 19.9,\r\n  \"fatG\": 17,\r\n  \"carbsG\": 58.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 162.75,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.74,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"pork chop\": {\r\n  \"fdcId\": 2705866,\r\n  \"description\": \"Pork, chop, NS as to fat eaten\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 247,\r\n  \"proteinG\": 37.1,\r\n  \"fatG\": 10.2,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.82,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 0.81,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"poutine\": {\r\n  \"fdcId\": 2709456,\r\n  \"description\": \"Potato, french fries, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 60,\r\n  \"kcal\": 135,\r\n  \"proteinG\": 1.5,\r\n  \"fatG\": 8.4,\r\n  \"carbsG\": 13.9,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.25,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 4.68,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"prime rib\": {\r\n  \"fdcId\": 168679,\r\n  \"description\": \"Beef, rib, whole (ribs 6-12), separable lean and fat, trimmed to 1/8\\\" fat, prime, cooked, roasted\",\r\n  \"servingName\": \"3 oz\",\r\n  \"servingG\": 85,\r\n  \"kcal\": 340,\r\n  \"proteinG\": 19.2,\r\n  \"fatG\": 28.6,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.18,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 5.95,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"pulled pork sandwich\": {\r\n  \"fdcId\": 173344,\r\n  \"description\": \"Pulled pork in barbecue sauce\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 249,\r\n  \"kcal\": 418,\r\n  \"proteinG\": 32.8,\r\n  \"fatG\": 11,\r\n  \"carbsG\": 46.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.02,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.22,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"ramen\": {\r\n  \"fdcId\": 2709153,\r\n  \"description\": \"Ramen bowl, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 245,\r\n  \"kcal\": 311,\r\n  \"proteinG\": 17.5,\r\n  \"fatG\": 11.2,\r\n  \"carbsG\": 34.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.78,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 112.7,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"ravioli\": {\r\n  \"fdcId\": 2708760,\r\n  \"description\": \"Ravioli, NS as to filling, no sauce\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 296,\r\n  \"proteinG\": 16.5,\r\n  \"fatG\": 11.2,\r\n  \"carbsG\": 31.8,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.02,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 60.8,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"red velvet cake\": {\r\n  \"fdcId\": 2707884,\r\n  \"description\": \"Cake or cupcake, red velvet\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 120,\r\n  \"kcal\": 426,\r\n  \"proteinG\": 3.3,\r\n  \"fatG\": 17.5,\r\n  \"carbsG\": 65.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.39,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 27.6,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"risotto\": {\r\n  \"fdcId\": 2708403,\r\n  \"description\": \"Rice, white, cooked, NS as to fat\",\r\n  \"servingName\": \"1 cup, cooked\",\r\n  \"servingG\": 163,\r\n  \"kcal\": 210,\r\n  \"proteinG\": 4.4,\r\n  \"fatG\": 0.5,\r\n  \"carbsG\": 45.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 94.54,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 0.07,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"samosa\": {\r\n  \"fdcId\": 2708730,\r\n  \"description\": \"Samosa\",\r\n  \"servingName\": \"1 regular/large\",\r\n  \"servingG\": 100,\r\n  \"kcal\": 310,\r\n  \"proteinG\": 5.1,\r\n  \"fatG\": 17.5,\r\n  \"carbsG\": 33.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 2.38,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 44,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"sashimi\": {\r\n  \"fdcId\": 2706223,\r\n  \"description\": \"Fish, raw\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 127,\r\n  \"proteinG\": 23.2,\r\n  \"fatG\": 3.2,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.94,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 1.76,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"scallops\": {\r\n  \"fdcId\": 2706357,\r\n  \"description\": \"Scallops, grilled\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 146,\r\n  \"proteinG\": 20.7,\r\n  \"fatG\": 3.9,\r\n  \"carbsG\": 5.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.05,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 20.25,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"seaweed salad\": {\r\n  \"fdcId\": 2710100,\r\n  \"description\": \"Seaweed, pickled\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 100,\r\n  \"kcal\": 54,\r\n  \"proteinG\": 2.9,\r\n  \"fatG\": 0.4,\r\n  \"carbsG\": 11.4,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 108,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 8.7,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"shrimp and grits\": {\r\n  \"fdcId\": 2706360,\r\n  \"description\": \"Shrimp, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 197,\r\n  \"proteinG\": 20.9,\r\n  \"fatG\": 9,\r\n  \"carbsG\": 6.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.39,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.25,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"spaghetti bolognese\": {\r\n  \"fdcId\": 2706470,\r\n  \"description\": \"Spaghetti sauce with meat\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 260,\r\n  \"kcal\": 234,\r\n  \"proteinG\": 15.4,\r\n  \"fatG\": 11.3,\r\n  \"carbsG\": 17,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 1.33,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 5.12,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"spaghetti carbonara\": {\r\n  \"fdcId\": 2708861,\r\n  \"description\": \"Pasta with cream sauce and meat, restaurant\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 250,\r\n  \"kcal\": 533,\r\n  \"proteinG\": 14.2,\r\n  \"fatG\": 35.3,\r\n  \"carbsG\": 38.5,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.63,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 3.63,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"spring rolls\": {\r\n  \"fdcId\": 2708700,\r\n  \"description\": \"Egg roll, meatless\",\r\n  \"servingName\": \"1 egg roll\",\r\n  \"servingG\": 64,\r\n  \"kcal\": 173,\r\n  \"proteinG\": 3.6,\r\n  \"fatG\": 9,\r\n  \"carbsG\": 19.2,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 42.88,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.37,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"steak\": {\r\n  \"fdcId\": 2705824,\r\n  \"description\": \"Beef, steak, NFS\",\r\n  \"servingName\": \"1 regular\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 366,\r\n  \"proteinG\": 43.2,\r\n  \"fatG\": 20.6,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 3.26,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 0.42,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"strawberry shortcake\": {\r\n  \"fdcId\": 2707896,\r\n  \"description\": \"Cake, strawberry shortcake\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 150,\r\n  \"kcal\": 321,\r\n  \"proteinG\": 5,\r\n  \"fatG\": 7.1,\r\n  \"carbsG\": 60.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin C\",\r\n    \"amount\": 18.6,\r\n    \"unit\": \"mg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 43.5,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"sushi\": {\r\n  \"fdcId\": 2708959,\r\n  \"description\": \"Sushi, NFS\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 150,\r\n  \"kcal\": 141,\r\n  \"proteinG\": 4.4,\r\n  \"fatG\": 1,\r\n  \"carbsG\": 27.6,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.15,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Folate\",\r\n    \"amount\": 7.5,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"tacos\": {\r\n  \"fdcId\": 2708514,\r\n  \"description\": \"Taco, NFS\",\r\n  \"servingName\": \"1 large\",\r\n  \"servingG\": 160,\r\n  \"kcal\": 394,\r\n  \"proteinG\": 24.5,\r\n  \"fatG\": 21.7,\r\n  \"carbsG\": 25,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.03,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 1.49,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"takoyaki\": {\r\n  \"fdcId\": 2706331,\r\n  \"description\": \"Octopus\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 305,\r\n  \"proteinG\": 17.9,\r\n  \"fatG\": 17.5,\r\n  \"carbsG\": 18.1,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 17.48,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin E\",\r\n    \"amount\": 4.27,\r\n    \"unit\": \"mg\"\r\n   }\r\n  ]\r\n },\r\n \"tiramisu\": {\r\n  \"fdcId\": 2705702,\r\n  \"description\": \"Tiramisu\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 145,\r\n  \"kcal\": 512,\r\n  \"proteinG\": 8.2,\r\n  \"fatG\": 34.6,\r\n  \"carbsG\": 42.7,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 268.25,\r\n    \"unit\": \"µg RAE\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 0.29,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"tuna tartare\": {\r\n  \"fdcId\": 2706308,\r\n  \"description\": \"Fish, tuna, raw\",\r\n  \"servingName\": \"1 cup\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 147,\r\n  \"proteinG\": 32.9,\r\n  \"fatG\": 0.7,\r\n  \"carbsG\": 0,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.81,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin D\",\r\n    \"amount\": 2.3,\r\n    \"unit\": \"µg\"\r\n   }\r\n  ]\r\n },\r\n \"waffles\": {\r\n  \"fdcId\": 2708312,\r\n  \"description\": \"Waffle, NFS\",\r\n  \"servingName\": \"1 thick / Belgian waffle\",\r\n  \"servingG\": 135,\r\n  \"kcal\": 417,\r\n  \"proteinG\": 10,\r\n  \"fatG\": 12.8,\r\n  \"carbsG\": 65.3,\r\n  \"vitamins\": [\r\n   {\r\n    \"name\": \"Vitamin B12\",\r\n    \"amount\": 2.19,\r\n    \"unit\": \"µg\"\r\n   },\r\n   {\r\n    \"name\": \"Vitamin A\",\r\n    \"amount\": 591.3,\r\n    \"unit\": \"µg RAE\"\r\n   }\r\n  ]\r\n }\r\n}\r\n","food/pyramid-scanner.mjs":"// Food pyramid 3D scanner. #3: the floating pyramid IS the Food menu — this lazy three.js scene fills the\r\n// Food dialog (launch.mjs puts it in .pyramid-mode; a WebGL or model failure falls back to the plain panel).\r\n// The lens is the camera (#31); tap a screen to zoom it (#32, #13); the dials are Log by hand / Water / Today\r\n// (D36); a half-turn spin or a clear swipe flips the screens between the last meal and today's totals (#35).\r\n// Three.js is imported only when the panel opens (same pattern as hologram.mjs); the GLB is fetched then too.\r\n// Served unbundled (external in scripts/build.mjs): no .ts imports and no build-time defines here.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {pyramidTiles,mealNutrients,todayTiles,foodPhotoToday} from './pyramid-tiles.mjs';\r\nimport {claimGraphics} from '../modules/graphics-lifecycle.mjs';\r\nimport {offAxis} from '../modules/portal/peer.mjs'; // #135: tilt looks round the pyramid through the portal window\r\nexport {pyramidTiles} from './pyramid-tiles.mjs';\r\n\r\n// Reuse the lightweight striped wall + paper notices from\r\n// C:/Users/ianmy/Documents/Codex/2026-09-22/co/work/mominc-girlfriend-fix/tv/channels/girlfriend.html.\r\n// These are the original game's CSS/DOM treatment; no raster art or external request is needed.\r\n// #38: the poster and note sit in the top corners, sized to stay whole at 375x812 beside the pyramid's tip.\r\nconst ROOM_STYLE_ID='pyramidScannerRoomStyle';\r\nconst ROOM_CSS=`\r\n#pyramidScanner{position:relative;width:100%;height:230px;border-radius:10px;overflow:hidden;margin-bottom:12px;touch-action:none;isolation:isolate;background:#bba16e!important}\r\n#pyramidScanner [hidden]{display:none!important}\r\n#pyramidScanner .pyramid-room{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none}\r\n#pyramidScanner .dg-paper-wall{position:absolute;inset:0;background:repeating-linear-gradient(90deg,transparent 0 139px,#604c4133 140px 142px),repeating-linear-gradient(0deg,#b8a477 0 79px,#c3b181 80px 82px);border:14px solid #665044}\r\n#pyramidScanner .dg-paper-wall:after{content:'';position:absolute;inset:0;opacity:.22;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Cpath fill='%23fff' filter='url(%23p)' opacity='.7' d='M0 0h180v180H0z'/%3E%3C/svg%3E\")}\r\n#pyramidScanner .dg-pyramid-walker{position:absolute;bottom:0;left:0;height:100%;width:auto;max-width:none;z-index:1;animation:dg-pyramid-cross 11s linear infinite;filter:drop-shadow(2px 4px 2px #30251977)}\r\n@keyframes dg-pyramid-cross{0%,50%{left:0;transform:translateX(-110%) rotate(-2deg)}61%{left:25%;transform:translateX(-50%) rotate(2deg)}78%{left:75%;transform:translateX(-50%) rotate(-2deg)}95%,100%{left:100%;transform:translateX(10%) rotate(2deg)}}\r\n@media(prefers-reduced-motion:reduce){#pyramidScanner .dg-pyramid-walker{animation:none;transform:translateX(-110%)}}\r\n#pyramidScanner .paper-poster{position:absolute;z-index:1;top:clamp(74px,10%,120px);left:5%;width:min(33%,220px);box-sizing:border-box;padding:13px 9px;background:#e5d4a2;border:2px solid #ad9769;box-shadow:5px 5px #76614955;text-align:center;transform:rotate(-3deg);color:#4c3255;font:700 clamp(12px,3.3vw,20px)/1.25 Georgia,serif}\r\n#pyramidScanner .paper-poster:before{content:'';position:absolute;width:45%;height:15px;left:27%;top:-9px;background:#e2c388bb;transform:rotate(3deg)}\r\n#pyramidScanner .paper-poster small{display:block;font:9px monospace;letter-spacing:.1em;margin-top:10px}\r\n#pyramidScanner .paper-note{position:absolute;z-index:1;right:5%;top:calc(clamp(74px,10%,120px) + 14px);max-width:34%;box-sizing:border-box;background:#dbc68a;color:#463149;border-left:5px solid #79558a;padding:10px;font:11px/1.45 monospace;transform:rotate(3deg)}\r\n#pyramidScanner .pyramid-loading{position:absolute;inset:0;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#463149cc;font:700 12px/1.3 monospace;text-align:center;opacity:1;transition:opacity .35s;pointer-events:none}\r\n#pyramidScanner .pyramid-loading[data-out]{opacity:0}\r\n#pyramidScanner .pyramid-loading svg{width:88px;height:88px;opacity:.6}\r\n#pyramidScanner canvas{position:absolute;inset:0;z-index:2}\r\n#pyramidScanner .pyramid-ui{position:absolute;inset:0;z-index:3;pointer-events:none;overflow:hidden}\r\n#pyramidScanner :where(.pyramid-ui>button,.pyramid-ui>.pyramid-question){position:absolute;left:0;top:0;margin:0;pointer-events:auto;cursor:pointer;font:700 13px/1 system-ui,-apple-system,sans-serif;letter-spacing:.04em}\r\n#pyramidScanner .pyramid-tag{min-height:44px;padding:0 13px;border:1px solid #fff9;border-radius:999px;background:#1d1428e0;color:#fff4dc;white-space:nowrap;box-shadow:0 2px 8px #0006}\r\n#pyramidScanner .pyramid-tag[data-on]{background:#7fe6ff;color:#10121a;border-color:#dff9ff}\r\n#pyramidScanner .pyramid-tag[data-away]:not(:focus-visible){opacity:0;pointer-events:none}\r\n#pyramidScanner .pyramid-tag[data-pulse]{animation:pyramidPulseTag 2.1s ease-in-out infinite}\r\n@keyframes pyramidPulseTag{0%,100%{box-shadow:0 2px 8px #0006}50%{box-shadow:0 2px 8px #0006,0 0 16px 4px #7fe6ffcc}}\r\n#pyramidScanner .pyramid-question{white-space:normal;max-width:min(84vw,300px);text-align:center;line-height:1.3;pointer-events:none}\r\n/* #3/#37: while the pyramid is asking, the correction sheet stays logically open (meal-nutrition.mjs still\r\n   searches and computes nutrients for the guess, so the numbers fly in and the eventual Food match is\r\n   prefilled) but visually closed, so the pyramid — not this sheet — is the whole Food menu until resolved. */\r\n#mealsPanel.pyramid-mode[data-asking] #mealConfirmation,#mealsPanel.pyramid-mode[data-asking] #mealSaveControls{display:none!important}\r\n/* Release 6: while asking, the wall's poster and note step down below the question's Yes/Type it/Fix row (reframe() sets --ask-clear). */\r\n#mealsPanel[data-asking] #pyramidScanner .paper-poster{top:var(--ask-clear,120px)}#mealsPanel[data-asking] #pyramidScanner .paper-note{top:calc(var(--ask-clear,120px) + 14px)}\r\n#pyramidScanner .pyramid-screen-key{min-height:0;padding:0;border:0;border-radius:6px;background:none;opacity:0;pointer-events:none!important}\r\n#pyramidScanner .pyramid-screen-key:focus-visible{opacity:1;outline:3px solid #ffb24d;outline-offset:2px}\r\n#pyramidScanner .pyramid-flip{left:50%;top:auto;bottom:14px;translate:-50% 0;display:flex;white-space:nowrap;align-items:center;gap:9px;min-height:40px;padding:0 15px;border:1px solid #fff8;border-radius:999px;background:#1d1428e0;color:#b9a9c6;box-shadow:0 2px 8px #0006}\r\n#pyramidScanner .pyramid-flip span[data-on]{color:#fff4dc}\r\n#pyramidScanner .pyramid-flip i{position:relative;width:30px;height:14px;border-radius:7px;background:#ffffff38}\r\n#pyramidScanner .pyramid-flip i:after{content:'';position:absolute;top:2px;left:2px;width:10px;height:10px;border-radius:50%;background:#7fe6ff;transition:transform .25s}\r\n#pyramidScanner .pyramid-flip[data-mode=today] i:after{transform:translateX(16px)}\r\n#pyramidScanner .pyramid-zoom{left:12px;right:12px;top:var(--zoom-y,50%);translate:0 -50%;display:grid;justify-items:center;gap:10px;padding:26px 14px;border:3px solid #2a2320;border-radius:22px;color:#2a2320;text-align:center;box-shadow:0 14px 44px #000b}\r\n#pyramidScanner .pyramid-zoom small{font:800 clamp(18px,5.5vw,28px)/1 system-ui,-apple-system,sans-serif;letter-spacing:.14em}\r\n#pyramidScanner .pyramid-zoom strong{max-width:100%;font:900 clamp(46px,16vw,110px)/1.05 system-ui,-apple-system,sans-serif;overflow-wrap:anywhere}\r\n#pyramidScanner .pyramid-zoom[data-key=name] strong{font-size:clamp(30px,10vw,64px)}\r\n#pyramidScanner .pyramid-zoom em{font:600 13px/1 system-ui,-apple-system,sans-serif;font-style:normal;opacity:.7}\r\n#pyramidScanner .pyramid-fly{position:absolute;left:0;top:0;padding:5px 9px;border:2px solid #2a2320;border-radius:9px;color:#2a2320;font:800 15px/1 system-ui,-apple-system,sans-serif;white-space:nowrap;pointer-events:none;box-shadow:0 3px 12px #0006}\r\n@media (prefers-reduced-motion:reduce){\r\n #pyramidScanner .pyramid-flip i:after{transition:none}\r\n #pyramidScanner .pyramid-loading{transition:none}\r\n #pyramidScanner .pyramid-tag[data-pulse]{animation:none;box-shadow:0 2px 8px #0006,0 0 16px 4px #7fe6ffcc}\r\n}\r\n`;\r\nlet roomStyle=null,roomStyleUsers=0;\r\nfunction acquireRoomStyle(){\r\n if(!roomStyle){roomStyle=document.getElementById(ROOM_STYLE_ID)||document.createElement('style');roomStyle.id=ROOM_STYLE_ID;roomStyle.textContent=ROOM_CSS;if(!roomStyle.isConnected)document.head.append(roomStyle);}\r\n roomStyleUsers++;\r\n return ()=>{roomStyleUsers=Math.max(0,roomStyleUsers-1);if(!roomStyleUsers){roomStyle?.remove();roomStyle=null;}};\r\n}\r\n\r\nconst TILES=[ // [mesh name, tileState key, small label, tile background]\r\n ['screen_name','name','FOOD','#7fcf5a'],\r\n ['screen_calories','calories','CALORIES','#f4d35e'],\r\n ['screen_protein','protein','PROTEIN','#4fd1c5'],\r\n ['screen_fat','fat','FAT','#f4978e'],\r\n ['screen_carbs','carbs','CARBS','#7fcf5a'],\r\n ['screen_vitamins','vitamins','VITAMINS','#c9a7eb'],\r\n];\r\nconst DIALS=['Log by hand','Water','Today']; // D36: knob_0..2\r\nconst TAU=Math.PI*2,FOV=35,TAN=Math.tan(FOV*Math.PI/360);\r\nconst FLIP_SPACE=64; // px kept under the pyramid for the meal/today indicator\r\nconst INERTIA=0.94;  // per-frame spin decay after release\r\nconst FOCUS=0.7;     // top share of the model holding the lens, screens and dials (the base may hang behind the sheet)\r\nconst FLY_MS=700;    // one number's flight from the lens to its screen\r\n\r\nfunction roundedRectPath(ctx,w,h,r){\r\n if(ctx.roundRect){ctx.beginPath();ctx.roundRect(0,0,w,h,r);return;}\r\n ctx.beginPath();ctx.moveTo(r,0);ctx.arcTo(w,0,w,h,r);ctx.arcTo(w,h,0,h,r);ctx.arcTo(0,h,0,0,r);ctx.arcTo(0,0,w,0,r);ctx.closePath();\r\n}\r\nfunction paintTile(canvas,bg,label,value){\r\n const ctx=canvas.getContext('2d'),w=canvas.width,h=canvas.height;\r\n ctx.clearRect(0,0,w,h);ctx.fillStyle=bg;roundedRectPath(ctx,w,h,Math.min(w,h)*0.16);ctx.fill();\r\n ctx.fillStyle='#2a2320';ctx.textAlign='center';ctx.textBaseline='middle';\r\n ctx.font=`700 ${Math.round(h*0.15)}px system-ui,-apple-system,sans-serif`;ctx.fillText(label,w/2,h*0.24);\r\n let size=h*0.33;\r\n for(;;){ctx.font=`800 ${Math.round(size)}px system-ui,-apple-system,sans-serif`;if(ctx.measureText(value).width<=w*0.86||size<=h*0.13)break;size-=2;}\r\n ctx.fillText(value,w/2,h*0.65);\r\n}\r\n// UV (0,0)=bottom-left, (1,1)=top-right per the asset contract: find corners by UV,\r\n// not by buffer index order, since glTF export may reindex/triangulate the quad.\r\nfunction quadCorners(mesh){\r\n const pos=mesh.geometry.attributes.position,uv=mesh.geometry.attributes.uv;\r\n const pick=(ux,uy)=>{let best=0,bestD=Infinity;for(let i=0;i<uv.count;i++){const d=(uv.getX(i)-ux)**2+(uv.getY(i)-uy)**2;if(d<bestD){bestD=d;best=i;}}return new THREE.Vector3().fromBufferAttribute(pos,best);};\r\n return {bl:pick(0,0),br:pick(1,0),tl:pick(0,1),tr:pick(1,1)};\r\n}\r\nfunction softDotTexture(tint=1){\r\n const c=document.createElement('canvas');c.width=c.height=64;const ctx=c.getContext('2d');\r\n const g=ctx.createRadialGradient(32,32,0,32,32,32);g.addColorStop(0,`rgba(255,255,255,${tint})`);g.addColorStop(1,'rgba(255,255,255,0)');\r\n ctx.fillStyle=g;ctx.fillRect(0,0,64,64);return new THREE.CanvasTexture(c);\r\n}\r\n\r\n// anchor: an element already in the (core, always-loaded) food panel markup that this lazy-loaded scanner\r\n// mounts itself just before. Building the host, its overlay controls and their styling here, instead of\r\n// shipping them in launch-shell.mjs/food-live.css, keeps three.js and its container out of the core bundle.\r\n// getMeals: the saved /api/meals rows (newest first) or null; onDial(index,on): a dial was pressed;\r\n// frame(): {top,bottom} px of the host the pyramid should fit between (the Food header and bottom sheet).\r\nexport async function mountPyramidScanner(anchor,{getNutrition=()=>({name:null,nutrients:null}),getMeals=()=>null,onDial=()=>{},frame=null,signal}={}){\r\n const cancelled=()=>new DOMException('Pyramid scanner closed.','AbortError');\r\n if(signal?.aborted)throw cancelled();\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n let disposed=false,raf=0,observer,timer,pet=null,petAsked=false,ready=false;\r\n let meal=pyramidTiles(null,null),today=todayTiles(null),mode=0,dial=-1,typing=false,uncertain=false,guessName='';\r\n let zoomKey=null,zoomReturn=null,flyArmed=false,flyTimer=0,spin=null;\r\n const flights=[]; // numbers flying from the lens to the screens\r\n const pending=new Set(); // tile keys blank while their number is flying in\r\n const download=new AbortController();\r\n const releaseRoomStyle=acquireRoomStyle();\r\n const host=document.createElement('div');host.id='pyramidScanner';host.dataset.loading='';\r\n // #39: a ghosted wireframe stand-in fills the model's spot while the GLB downloads, with a rising percent when\r\n // the response carries a Content-Length; it fades out (instantly under reduced motion) once the model is ready.\r\n host.innerHTML='<div class=\"pyramid-room\" aria-hidden=\"true\"><div class=\"dg-paper-wall\"></div><img class=\"dg-pyramid-walker\" src=\"/food/drgf-paper-character.png\" alt=\"\" draggable=\"false\"><div class=\"paper-poster\">CARED FOR.<br>CORRECTED.<br>PROVIDED FOR.<small>A MOM INC. WORKPLACE</small></div><div class=\"paper-note\">READ THE SOURCE.<br>KEEP THE LABEL.</div><div class=\"pyramid-loading\"><svg viewBox=\"0 0 100 90\" aria-hidden=\"true\"><polygon points=\"50,6 92,82 8,82\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"4\"/><polyline points=\"8,82 50,40 92,82\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-dasharray=\"4 4\"/><line x1=\"50\" y1=\"6\" x2=\"50\" y2=\"40\" stroke=\"currentColor\" stroke-width=\"2\" stroke-dasharray=\"4 4\"/></svg><span class=\"pyramid-loading-text\">Loading the pyramid</span></div></div><div class=\"pyramid-ui\" hidden></div>';\r\n const ui=host.querySelector('.pyramid-ui');\r\n const loading=host.querySelector('.pyramid-loading'),loadingText=host.querySelector('.pyramid-loading-text');\r\n const control=(className,text)=>{const b=document.createElement('button');b.type='button';b.className=className;if(text)b.textContent=text;ui.append(b);return b;};\r\n // Labels ride the lens and dials (projected every frame) and are their keyboard/touch targets too.\r\n const lensTag=control('pyramid-tag','Scan food');lensTag.setAttribute('aria-label','Scan food: take a food photo');\r\n const dialTags=DIALS.map(text=>control('pyramid-tag',text));\r\n for(const tag of dialTags.slice(1))tag.setAttribute('aria-pressed','false');\r\n // #37: an unsure result asks above a clean Yes/Type it/Fix row near the lens (a flat 2D banner, not baked into\r\n // the FOOD screen's 3D texture, so its layout never fights the model's geometry). #36: Type it is the\r\n // always-available manual-entry fallback.\r\n const questionTag=document.createElement('div');questionTag.className='pyramid-tag pyramid-question';questionTag.setAttribute('role','status');questionTag.hidden=true;ui.append(questionTag);\r\n const yesTag=control('pyramid-tag','Yes');yesTag.hidden=true;\r\n const fixTag=control('pyramid-tag','Fix');fixTag.hidden=true;\r\n const typeItTag=control('pyramid-tag','Type it');typeItTag.hidden=true;\r\n // Invisible keys over the six screens so a keyboard can zoom them too (pointer taps use the raycast).\r\n const screenKeys=Object.fromEntries(TILES.map(([meshName])=>[meshName,control('pyramid-screen-key')]));\r\n const flip=control('pyramid-flip');flip.innerHTML='<span>LAST MEAL</span><i></i><span>TODAY</span>';\r\n const zoom=control('pyramid-zoom');zoom.hidden=true;\r\n anchor.before(host);\r\n const scene=new THREE.Scene(),stage=new THREE.Group(),pivot=new THREE.Group();\r\n scene.add(stage);stage.add(pivot);\r\n scene.add(new THREE.AmbientLight(0xffffff,0.95));\r\n const key=new THREE.DirectionalLight(0xffffff,0.75);key.position.set(0.6,1.4,1.2);scene.add(key);\r\n let renderer;\r\n // The hidden 3D coach gives its context back first (the portal round Food keeps its board); it returns when Food closes.\r\n const releaseGpu=claimGraphics('pyramid',{keep:['portal']});await releaseGpu.ready;\r\n if(signal?.aborted){releaseGpu();host.remove();releaseRoomStyle();throw cancelled();}\r\n try{renderer=new THREE.WebGLRenderer({alpha:true,antialias:true,powerPreference:'low-power'});}\r\n catch(error){releaseGpu();host.remove();releaseRoomStyle();throw error;}\r\n renderer.setPixelRatio(Math.min(window.devicePixelRatio||1,1.5));renderer.setClearColor(0x000000,0);\r\n renderer.domElement.style.cssText='display:block;width:100%;height:100%;touch-action:none';renderer.domElement.setAttribute('aria-hidden','true');host.append(renderer.domElement);\r\n // Looks straight down -z; reframe() slides it back and down/up so the pyramid fills the free space.\r\n const camera=new THREE.PerspectiveCamera(FOV,1,0.01,40);camera.position.set(0,0.05,2.2);scene.add(camera);\r\n const raycaster=new THREE.Raycaster();\r\n const dialogEl=host.closest('dialog');\r\n\r\n const screens={},knobs=[];let lens=null,lensHalo=null,lensFlashT=-10,lensRadius=0.03,body=null,modelSize=null;\r\n const steamTex=softDotTexture(0.9);\r\n const steamPool=Array.from({length:24},()=>({sprite:new THREE.Sprite(new THREE.SpriteMaterial({map:steamTex,transparent:true,opacity:0,depthWrite:false,color:0xf3ece8})),age:0,life:0,vel:new THREE.Vector3(),pos:new THREE.Vector3()}));\r\n for(const p of steamPool){p.sprite.visible=false;p.sprite.scale.setScalar(0.012);}\r\n\r\n function disposeMat(mat){for(const m of [mat].flat())if(m){for(const v of Object.values(m))if(v?.isTexture)v.dispose();m.dispose();}}\r\n function disposeTree(root){root.traverse(node=>{node.geometry?.dispose();if(node.material)disposeMat(node.material);});}\r\n function dispose(){\r\n  if(disposed)return;disposed=true;cancelAnimationFrame(raf);observer?.disconnect();\r\n  releaseRoomStyle();delete dialogEl?.dataset.asking;pet?.dispose?.();window.removeEventListener('myr5:food-photo-ready',wakePet);\r\n  signal?.removeEventListener('abort',dispose);download.abort();clearTimeout(timer);clearTimeout(flyTimer);\r\n  window.removeEventListener('myr5:meal-nutrition',onNutrition);window.removeEventListener('myr5:food-selected',onSelected);window.removeEventListener('myr5:food-reset',onReset);\r\n  dialogEl?.removeEventListener('cancel',onCancel);\r\n  const dom=renderer.domElement;dom.removeEventListener('pointerdown',onDown);dom.removeEventListener('pointermove',onMove);dom.removeEventListener('pointerup',onUp);dom.removeEventListener('pointercancel',onUp);dom.removeEventListener('click',onClick);\r\n  disposeTree(scene);\r\n  for(const p of steamPool)if(!p.sprite.parent)p.sprite.material.dispose();\r\n  steamTex.dispose();renderer.dispose();renderer.forceContextLoss();host.remove();releaseGpu(renderer.domElement);\r\n }\r\n\r\n // ---- Screens: \"last meal\" face (the meal being logged, else the newest saved one) or today's totals.\r\n function faces(){\r\n  const current=getNutrition(),items=getMeals(),last=items?.[0];\r\n  meal=current?.name!=null?pyramidTiles(current.name===''&&typing?'TYPE YOUR FOOD':current.name,current.nutrients)\r\n   :last?pyramidTiles(last.name,mealNutrients(last)):pyramidTiles(null,null);\r\n  today=todayTiles(items);\r\n }\r\n const tileText=key=>mode?today[key]:pending.has(key)?'':meal[key];\r\n function paint([meshName,key,label,bg]){\r\n  const s=screens[meshName],value=tileText(key);\r\n  screenKeys[meshName].setAttribute('aria-label',`${label}: ${value||'arriving'}. Zoom in`);\r\n  if(!s)return;paintTile(s.canvas,bg,label,value);s.tex.needsUpdate=true;\r\n }\r\n function repaint(){for(const tile of TILES)paint(tile);if(zoomKey)fillZoom();}\r\n // #37: question + Yes/Type it/Fix travel together; a resolved or abandoned guess clears all four at once\r\n // and lets the (already-computed) correction sheet show again.\r\n function hideAsk(){questionTag.hidden=yesTag.hidden=fixTag.hidden=typeItTag.hidden=true;lensTag.hidden=!lens;delete dialogEl?.dataset.asking;}\r\n function onNutrition(){\r\n  if(disposed)return;\r\n  if(dial===0&&getNutrition()?.name==null)setDial(-1);\r\n  if(getNutrition()?.name==null){uncertain=false;hideAsk();} // #36/#37: a saved/cleared meal drops the ask\r\n  faces();if(!flyArmed)repaint();else if(meal.calories!=='—')fly();\r\n }\r\n // #31: a scan result (or a picked suggestion) blanks the screens, then its numbers fly out of the lens onto them.\r\n // #36/#37: no name (a scan error, a cancel or \"no clear food match\") offers Type it instead; an unsure result\r\n // asks first, with the question and Yes/Type it/Fix all near the lens (#3: the pyramid stays the whole menu —\r\n // the correction sheet itself opens only once Yes, Fix or Type it resolves the guess).\r\n function onSelected(e){\r\n  if(disposed)return;\r\n  const name=e?.detail?.name;\r\n  if(!name){uncertain=false;hideAsk();lensTag.hidden=true;typeItTag.hidden=false;return;} // error/cancel/no-match: the old guess is void, Type it remains (in the lens label's freed slot)\r\n  uncertain=!!e.detail.uncertain;\r\n  if(uncertain){guessName=name;questionTag.textContent=`Looks like… ${name}?`;dialogEl?.setAttribute('data-asking','');}\r\n  else delete dialogEl?.dataset.asking;\r\n  questionTag.hidden=yesTag.hidden=fixTag.hidden=typeItTag.hidden=!uncertain;\r\n  lensTag.hidden=uncertain; // frees its slot for the question/row, which take over the lens's job of triggering a rescan\r\n  typing=false;flyArmed=true;clearFlights();for(const [,k] of TILES)pending.add(k);\r\n  if(mode){spin=null;velocity=0;setRotation(pivot.rotation.y+TAU);} // same pose, other face: the result lands on \"last meal\"\r\n  faces();repaint();clearTimeout(flyTimer);flyTimer=setTimeout(fly,2500); // no nutrition match: fly the name anyway\r\n }\r\n function onReset(){\r\n  if(disposed)return;\r\n  typing=false;flyArmed=false;uncertain=false;hideAsk();\r\n  clearFlights();clearTimeout(flyTimer);pending.clear();faces();repaint();\r\n }\r\n window.addEventListener('myr5:meal-nutrition',onNutrition);window.addEventListener('myr5:food-selected',onSelected);window.addEventListener('myr5:food-reset',onReset);\r\n\r\n const tmp=new THREE.Vector3(),tmp2=new THREE.Vector3(),toCam=new THREE.Vector3();\r\n let W=1,H=1;\r\n function toScreen(world){tmp.copy(world).project(camera);return {x:(tmp.x+1)/2*W,y:(1-tmp.y)/2*H};}\r\n function screenRect(meshName){\r\n  const s=screens[meshName];if(!s)return null;\r\n  let x0=Infinity,y0=Infinity,x1=-Infinity,y1=-Infinity;\r\n  for(const c of s.corners){const p=toScreen(tmp2.copy(c).applyMatrix4(s.mesh.matrixWorld));x0=Math.min(x0,p.x);y0=Math.min(y0,p.y);x1=Math.max(x1,p.x);y1=Math.max(y1,p.y);}\r\n  return {x:x0,y:y0,w:x1-x0,h:y1-y0};\r\n }\r\n\r\n function fly(){\r\n  if(disposed||!flyArmed)return;flyArmed=false;clearTimeout(flyTimer);\r\n  if(reduced||!lens){pending.clear();repaint();return;}\r\n  clearFlights();const now=performance.now();\r\n  TILES.forEach((tile,i)=>{\r\n   const chip=document.createElement('span');chip.className='pyramid-fly';chip.textContent=meal[tile[1]];chip.style.background=tile[3];chip.style.opacity='0';chip.setAttribute('aria-hidden','true');ui.append(chip);\r\n   flights.push({chip,tile,start:now+i*110});\r\n  });\r\n }\r\n // Each frame: chips arc from the lens to their screen's current spot (the pyramid may be reframing), then land.\r\n function moveFlights(now){\r\n  if(!flights.length)return;\r\n  const from=lens&&toScreen(lens.getWorldPosition(tmp2));\r\n  for(let i=flights.length-1;i>=0;i--){\r\n   const f=flights[i],t=(now-f.start)/FLY_MS;if(t<0)continue;\r\n   const to=screenRect(f.tile[0]);\r\n   if(t>=1||!to||!from){f.chip.remove();flights.splice(i,1);pending.delete(f.tile[1]);paint(f.tile);continue;}\r\n   const e=t<.5?2*t*t:1-(-2*t+2)**2/2,x=from.x+(to.x+to.w/2-from.x)*e,y=from.y+(to.y+to.h/2-from.y)*e-Math.sin(t*Math.PI)*70;\r\n   f.chip.style.opacity=String(Math.min(1,t*6));f.chip.style.transform=`translate(${Math.round(x)}px,${Math.round(y)}px) translate(-50%,-50%) scale(${(1.15-.55*e).toFixed(3)})`;\r\n  }\r\n }\r\n function clearFlights(){for(const f of flights)f.chip.remove();flights.length=0;}\r\n\r\n // ---- #32/#13: tap a screen to zoom it across the width; tap again (or Escape) to return.\r\n function fillZoom(){\r\n  const [,key,label,bg]=TILES.find(t=>t[0]===zoomKey),small=document.createElement('small'),strong=document.createElement('strong'),hint=document.createElement('em');\r\n  small.textContent=label;strong.textContent=tileText(key)||'…';hint.textContent='Tap to return';\r\n  zoom.dataset.key=key;zoom.style.background=bg;zoom.replaceChildren(small,strong,hint);\r\n }\r\n function openZoom(meshName){\r\n  const from=screenRect(meshName);zoomKey=meshName;fillZoom();zoom.hidden=false;\r\n  zoomReturn=ui.contains(document.activeElement)?document.activeElement:null;zoom.focus({preventScroll:true});\r\n  if(reduced||!from)return;\r\n  const z=zoom.getBoundingClientRect(),h=host.getBoundingClientRect(),dx=h.left+from.x+from.w/2-(z.left+z.width/2),dy=h.top+from.y+from.h/2-(z.top+z.height/2);\r\n  zoom.animate([{transform:`translate(${dx}px,${dy}px) scale(${Math.max(.05,from.w/z.width)})`,opacity:.6},{transform:'none',opacity:1}],{duration:280,easing:'cubic-bezier(.2,0,.2,1)'});\r\n }\r\n function closeZoom(){if(!zoomKey)return;zoomKey=null;zoom.hidden=true;zoomReturn?.focus({preventScroll:true});zoomReturn=null;}\r\n function onCancel(e){if(zoomKey){e.preventDefault();closeZoom();}}\r\n dialogEl?.addEventListener('cancel',onCancel);\r\n zoom.onclick=closeZoom;\r\n for(const [meshName] of TILES)screenKeys[meshName].onclick=()=>openZoom(meshName);\r\n\r\n // ---- #35: the pose picks the face. Each half-turn past the back flips it (the swap happens out of sight),\r\n // so a full spin shows the other face; a spin that crosses the back or a clear swipe glides on to the front.\r\n function syncMode(){\r\n  const m=Math.round(pivot.rotation.y/TAU)&1;if(m===mode)return;\r\n  mode=m;repaint();flip.dataset.mode=mode?'today':'meal';\r\n  const [a,b]=flip.querySelectorAll('span');a.toggleAttribute('data-on',!mode);b.toggleAttribute('data-on',!!mode);\r\n  flip.setAttribute('aria-label',mode?'Screens show today’s totals. Switch to the last meal':'Screens show the last meal. Switch to today’s totals');\r\n }\r\n function setRotation(y){pivot.rotation.y=y;syncMode();}\r\n function spinTo(to){velocity=0;if(reduced){spin=null;setRotation(to);return;}const from=pivot.rotation.y;spin={from,to,t0:performance.now(),ms:Math.min(1100,380+Math.abs(to-from)*110)};}\r\n flip.onclick=()=>spinTo(Math.round(pivot.rotation.y/TAU)*TAU+TAU);\r\n mode=1;syncMode(); // initialise the indicator's labels (mode 0 = last meal)\r\n\r\n // ---- Photo days: Dr. Girlfriend's pocket holds a paper friend (arcade/pyramid-jump, lazy and never core).\r\n const apex=()=>toScreen(tmp2.set(0,modelSize.y/2,0).applyMatrix4(stage.matrixWorld));\r\n function wakePet(){\r\n  if(disposed||petAsked||!ready||!foodPhotoToday())return;petAsked=true;\r\n  import('../arcade/pyramid-jump/pet.mjs').then(m=>{if(!disposed)pet=m.mountPet({host,walker:host.querySelector('.dg-pyramid-walker'),playAt:flip,apex,getMeals,dialog:dialogEl});}).catch(()=>{petAsked=false;});\r\n }\r\n\r\n // ---- Knobs: glow, wiggle and steam feedback (unchanged), now each with a job (D36).\r\n function spawnSteam(k){\r\n  if(reduced)return;\r\n  for(let n=0;n<5;n++){\r\n   const p=steamPool.shift();steamPool.push(p);\r\n   p.pos.copy(k.basePos).addScaledVector(k.axis,0.01);\r\n   p.vel.copy(k.axis).multiplyScalar(0.055).add(new THREE.Vector3((Math.random()-0.5)*0.018,(Math.random()-0.5)*0.018,(Math.random()-0.5)*0.018));\r\n   p.age=0;p.life=0.85+Math.random()*0.3;p.sprite.visible=true;p.sprite.scale.setScalar(0.012);p.sprite.material.opacity=0.55;p.sprite.position.copy(p.pos);\r\n  }\r\n }\r\n function updateSteam(dt){\r\n  for(const p of steamPool){\r\n   if(!p.sprite.visible)continue;\r\n   p.age+=dt;if(p.age>p.life){p.sprite.visible=false;continue;}\r\n   p.vel.y+=dt*0.05;p.pos.addScaledVector(p.vel,dt);p.sprite.position.copy(p.pos);\r\n   const f=p.age/p.life;p.sprite.material.opacity=0.55*(1-f);p.sprite.scale.setScalar(0.012+f*0.02);\r\n  }\r\n }\r\n // One dial lit at a time (launch.mjs also calls this when a dial's panel closes).\r\n function setDial(i){dial=i;for(const k of knobs)k.on=k.index===i;dialTags.forEach((tag,n)=>{tag.toggleAttribute('data-on',n===i);if(n)tag.setAttribute('aria-pressed',String(n===i));});}\r\n function pressDial(i){\r\n  // Log by hand always opens; Water and Today toggle their cards.\r\n  const on=i===0||dial!==i,k=knobs.find(k=>k.index===i);setDial(on?i:-1);\r\n  if(k){k.tapT=performance.now();spawnSteam(k);}\r\n  if(i===0&&on){typing=true;uncertain=false;hideAsk();} // typing in by hand settles any \"unsure\" question\r\n  onDial(i,on);\r\n }\r\n dialTags.forEach((tag,i)=>tag.onclick=()=>pressDial(i));\r\n // #37: Yes/Fix both settle the guess through the normal food-selected path (same as a confident result), which\r\n // opens the correction sheet with that name searched and re-hides the question/tags via onSelected above.\r\n // Fix additionally surfaces the alternates, retry and status text the worker already prepared but held back.\r\n const confirmGuess=()=>window.dispatchEvent(new CustomEvent('myr5:food-selected',{detail:{name:guessName,uncertain:false}}));\r\n yesTag.onclick=confirmGuess; // #37: keep the guess, drop the \"?\"\r\n fixTag.onclick=()=>{\r\n  const suggestions=document.getElementById('foodSuggestions'),retry=document.getElementById('recognizeFood'),status=document.getElementById('foodStatus');\r\n  if(suggestions)suggestions.hidden=false;\r\n  if(retry)retry.hidden=false;\r\n  if(status)status.textContent='Check the match & portion';\r\n  confirmGuess();\r\n };\r\n typeItTag.onclick=()=>{\r\n  // #36/#37: blank the guess first — launch.mjs's Log by hand only resets the name when the sheet was still\r\n  // closed, which an unsure result's (visually hidden, logically open) sheet no longer is.\r\n  window.dispatchEvent(new CustomEvent('myr5:food-selected',{detail:{name:''}}));\r\n  pressDial(0); // opens Log by hand, which focuses #mealName\r\n };\r\n function scan(){lensFlashT=performance.now();document.getElementById('foodCamera')?.click();}\r\n lensTag.onclick=scan;\r\n function animateKnob(k,now){\r\n  const dt=(now-k.tapT)/1000;\r\n  if(!reduced&&k.tapT>0&&dt<1.1){const decay=Math.exp(-dt*4),ang=decay*Math.sin(dt*26)*0.5,pop=decay*Math.sin(dt*26)*0.002;\r\n   k.mesh.quaternion.setFromAxisAngle(k.axis,ang);k.mesh.position.copy(k.basePos).addScaledVector(k.axis,pop);\r\n  }else{k.mesh.quaternion.identity();k.mesh.position.copy(k.basePos);}\r\n }\r\n function pulseGlow(now){\r\n  const t=now/1000;\r\n  for(const k of knobs){\r\n   const mat=k.mesh.material;if(!mat.emissive)continue;\r\n   const idle=(k.on?0.32:0.12)+(reduced?0:Math.sin(t*2+k.mesh.id)*0.08),flashDt=(now-k.tapT)/1000,flash=!reduced&&flashDt>=0&&flashDt<0.3?(1-flashDt/0.3)*1.6:0;\r\n   mat.emissive.set(k.on?0x7fe6ff:0x3fb6d8);mat.emissiveIntensity=Math.max(0.05,idle)+flash;\r\n  }\r\n  // #34: nothing logged and no scan running (the meal face reads \"—\") pulses the lens harder, inviting a scan.\r\n  const empty=meal.calories==='—';\r\n  lensTag.toggleAttribute('data-pulse',empty);\r\n  if(lensHalo){\r\n   const amp=empty?0.35:0.12,idle=reduced?0.3+(empty?amp:0):0.3+Math.sin(t*2.4)*amp;\r\n   const flashDt=(now-lensFlashT)/1000,flash=!reduced&&flashDt>=0&&flashDt<0.3?(1-flashDt/0.3)*0.9:0;\r\n   lensHalo.material.opacity=Math.max(0,Math.min(1,idle+flash));\r\n  }\r\n }\r\n\r\n // ---- Pointer: drag spins (as before), a tap hits the lens, a screen or a dial.\r\n let dragging=false,downX=0,downY=0,downT=0,downRot=0,lastX=0,lastT=0,speed=0,moved=false,velocity=0,tapAt=null;\r\n function onDown(e){renderer.domElement.setPointerCapture?.(e.pointerId);dragging=true;tapAt=null;moved=false;spin=null;downX=lastX=e.clientX;downY=e.clientY;downT=lastT=performance.now();downRot=pivot.rotation.y;velocity=0;speed=0;}\r\n function onMove(e){\r\n  if(!dragging)return;\r\n  const now=performance.now(),dx=e.clientX-lastX;speed=dx/Math.max(1,now-lastT);lastX=e.clientX;lastT=now;\r\n  if(Math.abs(e.clientX-downX)>6||Math.abs(e.clientY-downY)>6)moved=true;\r\n  if(moved){setRotation(pivot.rotation.y+dx*0.009);velocity=reduced?0:dx*0.009;}\r\n }\r\n function lensWorldSphere(){const p=new THREE.Vector3();lens.getWorldPosition(p);const s=new THREE.Vector3();lens.getWorldScale(s);return new THREE.Sphere(p,lensRadius*s.x);}\r\n function handleTap(cx,cy){\r\n  if(zoomKey){closeZoom();return;}\r\n  const rect=renderer.domElement.getBoundingClientRect();\r\n  const ndc=new THREE.Vector2(((cx-rect.left)/rect.width)*2-1,-((cy-rect.top)/rect.height)*2+1);\r\n  camera.updateMatrixWorld();raycaster.setFromCamera(ndc,camera);\r\n  if(lens&&raycaster.ray.intersectSphere(lensWorldSphere(),new THREE.Vector3())){scan();return;}\r\n  // The body only blocks what is clearly behind it (a dial or screen on the far side), not a knob set into its skin.\r\n  const hits=raycaster.intersectObjects([body,...knobs.map(k=>k.mesh),...Object.values(screens).map(s=>s.mesh)].filter(Boolean),false),near=hits.find(h=>h.object!==body);\r\n  if(!near||hits[0].object===body&&near.distance-hits[0].distance>0.06)return;\r\n  const hit=near.object;\r\n  const knob=knobs.find(k=>k.mesh===hit);if(knob){pressDial(knob.index);return;}\r\n  const screen=Object.keys(screens).find(n=>screens[n].mesh===hit);if(screen)openZoom(screen);\r\n }\r\n function onUp(e){\r\n  if(!dragging)return;dragging=false;\r\n  const dt=performance.now()-downT,dx=e.clientX-downX,dist=Math.hypot(dx,e.clientY-downY);\r\n  // A tap acts on the click that follows: acting on pointerup would let a touch's click land on what the tap opened\r\n  // (the zoom card under the finger would close at once).\r\n  if(!moved&&dist<6&&dt<400&&e.type==='pointerup'){tapAt={x:e.clientX,y:e.clientY};return;}\r\n  if(!moved)return;\r\n  const swipe=Math.abs(dx)>50&&Math.abs(dx)>2*Math.abs(e.clientY-downY)&&Math.abs(speed)>0.5&&performance.now()-lastT<80;\r\n  const coast=pivot.rotation.y+velocity/(1-INERTIA),crossed=Math.round(coast/TAU)!==Math.round(downRot/TAU);\r\n  if(!swipe&&!crossed)return;\r\n  const dir=Math.sign(swipe?dx:coast-downRot)||1,turns=pivot.rotation.y/TAU;\r\n  spinTo((dir>0?Math.ceil(turns+1e-6):Math.floor(turns-1e-6))*TAU);\r\n }\r\n const dom=renderer.domElement;\r\n function onClick(){if(!tapAt)return;const {x,y}=tapAt;tapAt=null;handleTap(x,y);}\r\n dom.addEventListener('pointerdown',onDown);dom.addEventListener('pointermove',onMove);dom.addEventListener('pointerup',onUp);dom.addEventListener('pointercancel',onUp);dom.addEventListener('click',onClick);\r\n\r\n // ---- Framing: fit the pyramid between the Food header and its bottom sheet (portrait phones included).\r\n let fitGoal=null,fitNow=null,frameTop=6;\r\n function reframe(){\r\n  if(disposed)return;\r\n  W=Math.max(host.clientWidth,1);H=Math.max(host.clientHeight,1); // layout size: the portal's arrival scale must not shrink the canvas\r\n  renderer.setSize(W,H,false);camera.aspect=W/H;camera.updateProjectionMatrix();\r\n  for(const tag of [lensTag,questionTag,yesTag,fixTag,typeItTag,...dialTags])tag.labelWidth=tag.offsetWidth;\r\n  const f=frame?.()||{top:0,bottom:H},top=frameTop=Math.max(0,f.top)+6,base=Math.min(H,f.bottom),bottom=base-FLIP_SPACE,middle=(top+bottom)/2;\r\n  flip.style.bottom=`${Math.max(8,H-base+12)}px`;host.style.setProperty('--ask-clear',`${top+108}px`); // under the ask row (placeUI: frameTop+56, 44px tall)\r\n  zoom.style.setProperty('--zoom-y',`${Math.max(top,middle)}px`);\r\n  if(!modelSize)return;\r\n  // px per world unit: the whole model at full width when it fits; when the sheet is up, keep the lens, screens\r\n  // and dials whole at that size and let the base hang behind the sheet.\r\n  const space=Math.max(60,bottom-top),ppuW=W/(modelSize.x*1.3);let ppu=Math.min(ppuW,space/(modelSize.y*1.08)),aim=0;\r\n  if(ppu<ppuW&&base<H-32){ppu=Math.min(ppuW,space/(modelSize.y*FOCUS*1.08));aim=modelSize.y*(0.5-FOCUS/2);}\r\n  fitGoal={dist:H/(2*TAN*ppu),y:aim+(middle-H/2)/ppu};\r\n  if(reduced||!fitNow)fitNow={...fitGoal};\r\n }\r\n // Labels follow the lens and knobs; a knob turned to the back hides its label (focus still shows it).\r\n function place(tag,x,y){const w=tag.labelWidth||0;tag.style.transform=`translate(${Math.round(Math.min(Math.max(x-w/2,6),W-w-6))}px,${Math.round(Math.max(frameTop,Math.min(y,H-46)))}px)`;}\r\n function placeUI(){\r\n  // #37: the question and its row anchor to the top of the safe area (not the lens) — the lens's own screen\r\n  // position swings with the model's fit (how much room the correction sheet leaves it), so anything hung off\r\n  // it risks landing on top of itself; frameTop is stable, and stays clear of the header by construction.\r\n  const askX=lens?toScreen(lens.getWorldPosition(tmp2)).x:W/2;\r\n  if(uncertain){\r\n   place(questionTag,askX,frameTop+2);\r\n   place(yesTag,askX-104,frameTop+56);place(typeItTag,askX,frameTop+56);place(fixTag,askX+104,frameTop+56);\r\n  }else if(!typeItTag.hidden)place(typeItTag,askX,frameTop+2); // #36: a plain error/no-match offers just Type it\r\n  if(lens){\r\n   const p=toScreen(lens.getWorldPosition(tmp2));\r\n   if(!lensTag.hidden)place(lensTag,p.x,p.y-58);\r\n  }\r\n  for(const k of knobs){\r\n   k.mesh.getWorldPosition(tmp2);const p=toScreen(tmp2);\r\n   const axis=toCam.copy(k.axis).transformDirection(k.mesh.parent.matrixWorld),facing=axis.dot(tmp.copy(camera.position).sub(tmp2).normalize());\r\n   dialTags[k.index].toggleAttribute('data-away',facing<-0.1);place(dialTags[k.index],p.x,p.y+14);\r\n  }\r\n  for(const [meshName] of TILES){const r=screenRect(meshName),keyEl=screenKeys[meshName];if(!r){keyEl.hidden=true;continue;}keyEl.style.transform=`translate(${Math.round(r.x)}px,${Math.round(r.y)}px)`;keyEl.style.width=`${Math.round(r.w)}px`;keyEl.style.height=`${Math.round(r.h)}px`;}\r\n }\r\n\r\n signal?.addEventListener('abort',dispose,{once:true});\r\n try{\r\n  timer=setTimeout(()=>download.abort(),20000);let bytes;\r\n  try{\r\n   const response=await fetch('/food/pyramid-scanner.glb',{signal:download.signal});if(!response.ok)throw new Error('Pyramid model unavailable.');\r\n   // #39: read the stream by hand for a percent readout; a response with no Content-Length just skips the number.\r\n   const total=Number(response.headers.get('Content-Length'))||0,reader=response.body?.getReader();\r\n   if(reader){\r\n    const chunks=[];let received=0;\r\n    for(;;){\r\n     const {done,value}=await reader.read();if(done)break;\r\n     chunks.push(value);received+=value.length;\r\n     if(total&&loadingText)loadingText.textContent=`Loading the pyramid · ${Math.round(received/total*100)}%`;\r\n    }\r\n    bytes=await new Blob(chunks).arrayBuffer();\r\n   }else bytes=await response.arrayBuffer();\r\n  }finally{clearTimeout(timer);}\r\n  if(disposed)throw cancelled();\r\n  const loader=new GLTFLoader();loader.setMeshoptDecoder(MeshoptDecoder);\r\n  const gltf=await loader.parseAsync(bytes,'');const model=gltf.scene;\r\n  // GLTF texture decoding cannot be aborted. Discard a late result without\r\n  // mounting anything or reviving the closed renderer/listeners.\r\n  if(disposed){disposeTree(model);throw cancelled();}\r\n  const box=new THREE.Box3().setFromObject(model),size=box.getSize(new THREE.Vector3()),center=box.getCenter(new THREE.Vector3()),scale=1.6/Math.max(size.x,size.y,size.z);\r\n  const offset=new THREE.Group();offset.position.copy(center).multiplyScalar(-1);offset.add(model);\r\n  const normalized=new THREE.Group();normalized.scale.setScalar(scale);normalized.add(offset);pivot.add(normalized);\r\n  modelSize=size.multiplyScalar(scale);\r\n  // Knob coordinates and their steam share the model's local coordinate space.\r\n  // Add effects only after measuring the actual model for its camera framing.\r\n  for(const p of steamPool)model.add(p.sprite);\r\n  body=model.getObjectByName('body')||null;\r\n\r\n  for(const [meshName] of TILES){\r\n   const mesh=model.getObjectByName(meshName);if(!mesh)continue;\r\n   const {bl,br,tl,tr}=quadCorners(mesh),w=bl.distanceTo(br)||1,h=bl.distanceTo(tl)||1,targetH=220;\r\n   const canvas=document.createElement('canvas');canvas.height=targetH;canvas.width=Math.max(64,Math.round(targetH*(w/h)));\r\n   // glTF UVs start at the image's top-left (GLTFLoader never flips), so the canvas must not be flipped either.\r\n   const tex=new THREE.CanvasTexture(canvas);tex.colorSpace=THREE.SRGBColorSpace;tex.flipY=false;\r\n   // polygonOffset: the screen quad sits flush against the body mesh (both in the original GLB) and z-fights\r\n   // it without this — negative factor/units pull the screen a hair closer to the camera for clean edges.\r\n   mesh.material=new THREE.MeshBasicMaterial({map:tex,side:THREE.FrontSide,polygonOffset:true,polygonOffsetFactor:-4,polygonOffsetUnits:-4});\r\n   screens[meshName]={canvas,tex,mesh,corners:[bl,br,tl,tr]};\r\n  }\r\n\r\n  for(let i=0;i<3;i++){\r\n   const knob=model.getObjectByName(`knob_${i}`);if(!knob){dialTags[i].dataset.away='';continue;}\r\n   knob.material=Array.isArray(knob.material)?knob.material.map(m=>m.clone()):knob.material.clone();\r\n   const axis=new THREE.Vector3(...(knob.userData.axis||[0,0,1])).normalize();\r\n   knobs.push({index:i,mesh:knob,axis,basePos:knob.position.clone(),tapT:-10,on:false});\r\n  }\r\n\r\n  lens=model.getObjectByName('lens');\r\n  if(lens){\r\n   lensRadius=lens.userData.radius||0.03;\r\n   lensHalo=new THREE.Sprite(new THREE.SpriteMaterial({map:steamTex,color:0x8ef2ff,blending:THREE.AdditiveBlending,depthWrite:false,transparent:true,opacity:0.3}));\r\n   lensHalo.scale.setScalar(lensRadius*2.6);lens.add(lensHalo);\r\n  }else lensTag.hidden=true;\r\n\r\n  faces();repaint();\r\n  delete host.dataset.loading;ui.hidden=false;ready=true;wakePet();window.addEventListener('myr5:food-photo-ready',wakePet);\r\n  if(loading){if(reduced)loading.remove();else{loading.dataset.out='';loading.addEventListener('transitionend',()=>loading.remove(),{once:true});}}\r\n  observer=new ResizeObserver(reframe);observer.observe(host);reframe();\r\n  let last=performance.now();\r\n  function animate(now){\r\n   if(disposed)return;\r\n   // The opaque full-screen photo/scan stage covers the pyramid while a scan runs: no render or label writes under it\r\n   // (numbers still flying out of the lens keep moving).\r\n   const scanStage=document.getElementById('mealScanStage');\r\n   if(scanStage&&!scanStage.hidden&&!flights.length){last=now;raf=requestAnimationFrame(animate);return;}\r\n   const dt=Math.min((now-last)/1000,0.05);last=now;\r\n   if(spin){const t=Math.min(1,(now-spin.t0)/spin.ms);setRotation(spin.from+(spin.to-spin.from)*(1-(1-t)**3));if(t>=1)spin=null;}\r\n   else if(!dragging&&velocity){setRotation(pivot.rotation.y+velocity);velocity*=INERTIA;if(Math.abs(velocity)<1e-4)velocity=0;}\r\n   if(!reduced){const t=now/1000;stage.position.y=Math.sin(t*1.1)*0.035;stage.rotation.z=Math.sin(t*0.7)*0.035;stage.rotation.x=Math.sin(t*0.5)*0.02;}\r\n   if(fitNow){const ease=reduced?1:0.2;fitNow.dist+=(fitGoal.dist-fitNow.dist)*ease;fitNow.y+=(fitGoal.y-fitNow.y)*ease;camera.position.set(0,fitNow.y,fitNow.dist);offAxis(camera,fitNow.dist);}\r\n   for(const k of knobs)animateKnob(k,now);\r\n   updateSteam(dt);pulseGlow(now);\r\n   renderer.render(scene,camera);placeUI();moveFlights(now);raf=requestAnimationFrame(animate);\r\n  }\r\n  raf=requestAnimationFrame(animate);\r\n  return {dispose,reframe,setDial,refresh(){if(!disposed){faces();repaint();}}};\r\n }catch(error){dispose();throw error;}\r\n}\r\n","food/pyramid-tiles.mjs":"// Pure scan-result formatting, kept independent of WebGL for unit tests.\r\nconst VITAMIN_DV={vitaminA:900,vitaminC:90,vitaminD:20,vitaminE:15,vitaminB12:2.4,folate:400};\r\nconst VITAMIN_LABEL={vitaminA:'A',vitaminC:'C',vitaminD:'D',vitaminE:'E',vitaminB12:'B12',folate:'B9'};\r\nconst DASH='—';\r\nconst has=v=>typeof v==='number'&&Number.isFinite(v);\r\n\r\nexport function pyramidTiles(scan,nutrients){\r\n if(scan==null)return {name:'SCAN A MEAL',calories:DASH,protein:DASH,fat:DASH,carbs:DASH,vitamins:DASH};\r\n const round=(v,d=0)=>{const m=10**d;return Math.round(v*m)/m;};\r\n const amount=k=>has(nutrients?.[k])?`${round(nutrients[k],k==='calories'?0:1)} ${k==='calories'?'kcal':'g'}`:DASH;\r\n const top=Object.entries(VITAMIN_DV)\r\n  .map(([k,dv])=>[VITAMIN_LABEL[k],has(nutrients?.[k])?nutrients[k]/dv:-1])\r\n  .filter(([,pct])=>pct>0).sort((a,b)=>b[1]-a[1]).slice(0,2).map(([label])=>label);\r\n return {name:scan||'Try again',calories:amount('calories'),protein:amount('protein'),fat:amount('fat'),carbs:amount('carbs'),vitamins:top.length?top.join(' '):DASH};\r\n}\r\n\r\n// A saved /api/meals row -> the flat nutrient object pyramidTiles reads (D1 returns micros as JSON text).\r\nexport function mealNutrients(item){\r\n let micros=item?.micros;\r\n if(typeof micros==='string'){try{micros=JSON.parse(micros);}catch{micros=null;}}\r\n return {...(micros||{}),calories:item?.calories,protein:item?.protein,fat:item?.fat,carbs:item?.carbs};\r\n}\r\n\r\n// Meals eaten on `now`'s local calendar day; null when the list isn't available (signed out, offline).\r\nexport function mealsOn(items,now=new Date()){\r\n if(!Array.isArray(items))return null;\r\n const day=now.toDateString();\r\n return items.filter(item=>new Date(item.eaten_at).toDateString()===day);\r\n}\r\n\r\n// #35: the screens' \"today\" face — summed macros (0 when nothing is logged yet) and the top two vitamins.\r\nexport function todayTiles(items,now=new Date()){\r\n const meals=mealsOn(items,now);\r\n if(!meals)return pyramidTiles('TODAY',null);\r\n const total={calories:0,protein:0,fat:0,carbs:0};\r\n for(const item of meals)for(const [k,v] of Object.entries(mealNutrients(item)))if(has(v))total[k]=(total[k]||0)+v;\r\n return pyramidTiles(meals.length?`TODAY · ${meals.length} MEAL${meals.length===1?'':'S'}`:'TODAY',total);\r\n}\r\n\r\n// Pocket pet (arcade/pyramid-jump): only on days the user took a food photo. meal-scanner sets the flag on\r\n// 'myr5:food-photo-ready'; the vault's own days['food-scan'] counts too (same local-day string).\r\nconst FOOD_PHOTO_KEY='myr5-food-photo-day',localDay=(d=new Date())=>`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;\r\nexport function markFoodPhotoToday(store=globalThis.localStorage,now=new Date()){try{store.setItem(FOOD_PHOTO_KEY,localDay(now));}catch{/* private mode: the pet just stays away */}}\r\nexport function foodPhotoToday(store=globalThis.localStorage,now=new Date()){\r\n try{const day=localDay(now);return store.getItem(FOOD_PHOTO_KEY)===day||JSON.parse(store.getItem('myr5-vault-v1')||'{}').days?.['food-scan']===day;}catch{return false;}\r\n}\r\n","full-workout-ui.mjs":"// FULL WORKOUT dropdown + runner. The circuit logic is in full-workout.mjs; every exercise runs through\r\n// the normal camera workout (app.mjs start()), so reps/holds, XP, locks and rest rewards are unchanged.\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {FULL_WORKOUT,CIRCUIT_KNOBS,readKnobs,circuitSeconds,circuitStyles,buildFullWorkout,styleLadder,maxLevel,sectionPicks} from './full-workout.mjs';\r\nimport {rotary} from './pod/hardware.mjs';\r\nimport {ROPE,makeRope,stepRope,rotate,ropePath} from './rope.mjs';\r\nimport {subscribePhoneMotion,requestPhoneMotion} from './character-phone-sensor.mjs';\r\n\r\nconst esc=s=>String(s).replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\r\nconst clock=s=>`${Math.floor(s/60)}:${String(s%60).padStart(2,'0')}`;\r\nconst wait=ms=>new Promise(r=>setTimeout(r,ms));\r\n\r\n/** deps: canUse(id)->bool, reason(id)->string, select(id,goal), start(), leaveRest(), voice, storage;\r\n *  circuit hooks: setCircuit({target,restSeconds}|null), freeze(bool), left()->reps/seconds left|null, restEnd()->ms */\r\nexport function mountFullWorkout({canUse,reason,select,start,leaveRest,setCircuit,freeze,left,restEnd,preload,voice,storage=globalThis.localStorage}){\r\n const details=document.createElement('details');details.id='podFullWorkout';details.className='pod-goals';\r\n details.innerHTML='<summary aria-label=\"Full workout circuit\">CIRCUIT BOARD · <span data-fw-status></span></summary><div class=\"pod-goals-menu fw-menu\"></div>';\r\n details.open=true;(document.getElementById('status')||document.getElementById('podGoals')).after(details); // lives in the console below BEGIN, open by default\r\n const menu=details.querySelector('.fw-menu'),status=details.querySelector('[data-fw-status]');\r\n const fitBoard=()=>{if(!details.open)return;const top=menu.getBoundingClientRect().top,dock=document.getElementById('coachDock')?.getBoundingClientRect(),bottom=dock&&dock.height&&dock.top>top?Math.min(innerHeight,dock.top):innerHeight;menu.style.setProperty('--fw-height',`${Math.min(550,Math.max(300,bottom-top-8))}px`);};\r\n const hud=document.createElement('section');hud.id='fullWorkoutHud';hud.hidden=true;hud.setAttribute('aria-live','polite');document.body.append(hud);\r\n // Camera-only mode hides every other body child, so the HUD rides inside the camera stage while it is up.\r\n new MutationObserver(()=>{const stage=document.getElementById('cameraWorkout'),target=document.body.dataset.cameraWorkout&&stage?stage:document.body;if(hud.parentNode!==target)target.append(hud);hud.inert=false;}).observe(document.body,{attributes:true,attributeFilter:['data-camera-workout']});\r\n\r\n // Cable identity is independent of socket identity. Legacy powered sections restore identity wires.\r\n const styles=circuitStyles(),valid=new Set(styles.map(s=>s.id));\r\n const load=()=>{try{const v=JSON.parse(storage.getItem(FULL_WORKOUT.storageKey));if(v&&typeof v==='object'&&!Array.isArray(v)){\r\n  const cables={},occupied=new Set();\r\n  const old=Array.isArray(v.powered)?Object.fromEntries(v.powered.map(id=>[id,id])):{};\r\n  for(const [wire,socket] of Object.entries(v.cables&&typeof v.cables==='object'?v.cables:old))if(valid.has(wire)&&valid.has(socket)&&!occupied.has(socket)){cables[wire]=socket;occupied.add(socket);}\r\n  return {cables,powered:new Set(Object.values(cables)),levels:v.levels&&typeof v.levels==='object'?v.levels:{},difficulties:v.difficulties&&typeof v.difficulties==='object'?v.difficulties:{...v.levels}};\r\n }}catch{}return {cables:{},powered:new Set(),levels:{},difficulties:{}};};\r\n const st=load(),knobs=readKnobs(storage);\r\n const saveKnobs=()=>{try{storage.setItem(CIRCUIT_KNOBS.storageKey,JSON.stringify(knobs));}catch{}};\r\n const KNOB_ROWS=[['reps','REPS',''],['time','TIME','s'],['rest','REST','s']];\r\n const save=()=>{try{storage.setItem(FULL_WORKOUT.storageKey,JSON.stringify({powered:[...st.powered],levels:st.levels,difficulties:st.difficulties,cables:st.cables}));}catch{}};\r\n const cap=id=>maxLevel(id,canUse);\r\n const lvl=id=>Math.min(st.levels[id]??0,cap(id));\r\n const difficulty=id=>Math.max(1,Math.min(st.difficulties[id]||st.levels[id]||1,cap(id)||1));\r\n const picks=()=>sectionPicks({powered:st.powered,levels:st.levels,canUse});\r\n const selection=picks;\r\n const detent=kind=>{window.dispatchEvent(new CustomEvent('myr5:hardware-detent',{detail:{kind}}));try{navigator.vibrate?.(kind==='lever'?14:8);}catch{}};\r\n const reduced=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const connected=new Set(); // cable IDs whose tips have clicked into their mapped sockets\r\n const wireFor=id=>Object.keys(st.cables).find(wire=>st.cables[wire]===id);\r\n // The library's canonical exercise posters, also rendered by menu.mjs.\r\n const icon=id=>`<img src=\"/models/previews/${esc(id)}.png\" alt=\"\" aria-hidden=\"true\" width=\"32\" height=\"32\" loading=\"lazy\">`;\r\n function paintHead(){\r\n  const n=picks().length;status.textContent=`${n} section${n===1?'':'s'}`;\r\n  const c=menu.querySelector('.fw-count');if(c)c.textContent=n?`${n} section${n===1?'':'s'} in the circuit`:'Plug a cable into a section to build your circuit';\r\n  const s=menu.querySelector('[data-fw-start]');if(s){s.disabled=!n;s.textContent='START FULL WORKOUT'+(n?` · ${n} · ${clock(circuitSeconds(picks(),knobs))}`:'');}\r\n  menu.querySelector('.fw-hub')?.setAttribute('data-lit',String(connected.size>0));\r\n }\r\n function paintLadder(id){\r\n  const sec=menu.querySelector(`.fw-style[data-style=\"${id}\"]`);if(!sec)return;const lv=difficulty(id),l=styleLadder(id),dial=sec.querySelector('[data-dial]');\r\n  sec.querySelector('[data-enable]').checked=lvl(id)>0;\r\n  dial.value=lv;dial.setAttribute('aria-valuetext',`${lv}: ${l[lv-1].name}`);sec.style.setProperty('--angle',`${-135+(lv-1)/Math.max(1,l.length-1)*270}deg`);\r\n  sec.querySelector('.fw-level').textContent=lv;sec.querySelectorAll('.fw-hologram').forEach(e=>e.classList.toggle('selected',Number(e.dataset.level)===lv));\r\n  paintHead();\r\n }\r\n function render(){\r\n  const top=menu.scrollTop;\r\n  menu.innerHTML=`<div class=\"fw-body\"><svg class=\"fw-wires\" aria-hidden=\"true\">${styles.map(s=>`<g class=\"fw-cord\" data-w=\"${s.id}\"><path class=\"fw-c-sh\"/><path class=\"fw-c-jk\"/><path class=\"fw-c-hl\"/><path class=\"fw-c-pl\"/></g>`).join('')}</svg>\r\n   <div class=\"fw-power\"><i class=\"fw-screw l\"></i><div class=\"fw-pw-l\"><span class=\"fw-plate\" aria-hidden=\"true\">&#9889;</span><p class=\"fw-count\"></p></div><button type=\"button\" class=\"fw-hub\" data-hub aria-label=\"Power all sections\" title=\"Power all sections\" data-lit=\"false\"><span aria-hidden=\"true\">&#9211;</span></button><i class=\"fw-screw r\"></i>\r\n   </div><div class=\"fw-knobs\">${KNOB_ROWS.map(([k,label])=>`<div class=\"fw-knob\"><span class=\"fw-knob-l\">${label}</span><div class=\"fw-rotary\" data-knob=\"${k}\" role=\"slider\" tabindex=\"0\" aria-label=\"${label}\" aria-valuemin=\"0\" aria-valuemax=\"${CIRCUIT_KNOBS[k].length-1}\"><span class=\"cb-ticks\" aria-hidden=\"true\"></span><i aria-hidden=\"true\"></i></div><output class=\"fw-knob-v\" data-knob-v=\"${k}\"></output></div>`).join('')}</div><div class=\"fw-grid\">${styles.map(style=>{\r\n    const max=cap(style.id),lv=lvl(style.id),hint=max===0?(reason(style.exercises[0].id)||'Locked'):'';\r\n    return `<section class=\"fw-style\" data-style=\"${style.id}\" aria-label=\"${esc(style.name)}\" data-power=\"off\"${max===0?' data-capped':''}>\r\n     <div class=\"fw-section-head\" title=\"${esc(hint||style.name)}\"><i data-anchor=\"${style.id}\" aria-hidden=\"true\"></i><i class=\"fw-socket\" data-socket=\"${style.id}\" aria-label=\"${esc(style.name)} socket${max===0?', exercises locked':''}\" data-locked=\"${max===0}\" title=\"${esc(hint||style.name+' socket')}\"></i></div>\r\n     <label class=\"fw-enable\" title=\"${esc(hint||style.name)}\"><input type=\"checkbox\" data-enable aria-label=\"${esc(style.name)} enabled\"${lv?' checked':''}${max===0?' disabled':''}><i class=\"fw-lamp\" aria-hidden=\"true\"></i><i class=\"fw-tog\" aria-hidden=\"true\">${max===0?'<b class=\"fw-guard\">&#128274;</b>':''}</i></label>\r\n     <div class=\"fw-dial-wrap\"><div class=\"fw-holograms\" aria-hidden=\"true\">${style.exercises.map((e,i)=>`<span class=\"fw-hologram${i>=max?' fw-locked':''}\" data-level=\"${i+1}\" style=\"--a:${-135+i/Math.max(1,style.exercises.length-1)*270}deg\" title=\"${esc(e.name)}\">${icon(e.id)}</span>`).join('')}</div><input class=\"fw-dial\" type=\"range\" data-dial min=\"1\" max=\"${Math.max(1,max)}\" step=\"1\" value=\"${difficulty(style.id)}\" aria-label=\"${esc(style.name)} difficulty\"${max===0?' disabled':''}><output class=\"fw-level\" aria-hidden=\"true\"></output></div>\r\n    </section>`;}).join('')}</div>\r\n   <button type=\"button\" class=\"fw-start\" data-fw-start>START FULL WORKOUT</button>\r\n   ${styles.map((s,i)=>`<button type=\"button\" class=\"fw-plug\" data-plug=\"${s.id}\" aria-pressed=\"${!!st.cables[s.id]}\" aria-label=\"Cable ${i+1}\" title=\"Cable ${i+1}: drag to any unlocked socket\"><i aria-hidden=\"true\"></i></button>`).join('')}\r\n  </div>`;\r\n  for(const s of styles){paintLadder(s.id);paintPower(s.id);}\r\n  for(const [k,label,unit] of KNOB_ROWS){const el=menu.querySelector(`[data-knob=\"${k}\"]`),vals=CIRCUIT_KNOBS[k],out=menu.querySelector(`[data-knob-v=\"${k}\"]`),show=i=>{out.textContent=vals[i]+unit;};\r\n   const p=rotary(el,{count:()=>vals.length,value:()=>vals.indexOf(knobs[k]),preview:show,commit:i=>{knobs[k]=vals[i];saveKnobs();show(i);p(i);paintHead();},locked:()=>false});\r\n   const i=vals.indexOf(knobs[k]);el.setAttribute('aria-valuenow',String(i));show(i);p(i);}\r\n  paintHead();\r\n  menu.scrollTop=top;fitBoard();rig.rebind();\r\n }\r\n\r\n // ---- power cables: one hub, one verlet rope per section, gravity from the app's tilt source ----\r\n const rig=(()=>{\r\n  const R={};let body,pos,raf=0,unsub=null,tilt=null,tiltAt=-1e9,grav={gx:0,gy:1},last=0,plugs={},paths={};\r\n  const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);\r\n  const measure=()=>{\r\n   body=menu.querySelector('.fw-body');if(!body)return false;const b=body.getBoundingClientRect(),c=e=>{const r=e.getBoundingClientRect();return {x:r.left+r.width/2-b.left,y:r.top+r.height/2-b.top};};\r\n   // Keep the animated SVG backing surface as small as the scrollport, even on long circuits.\r\n   // Its viewBox retains body coordinates so ropes and socket hit testing stay aligned.\r\n   const svg=body.querySelector('.fw-wires'),y=menu.getBoundingClientRect().top+menu.clientTop-b.top,h=menu.clientHeight;\r\n   svg.style.top=`${y}px`;svg.style.height=`${h}px`;svg.setAttribute('viewBox',`0 ${y} ${b.width} ${h}`);\r\n   pos={anchor:{},sock:{},loose:{},b};\r\n   for(const [i,s] of styles.entries()){pos.anchor[s.id]=c(body.querySelector(`[data-anchor=\"${s.id}\"]`));const e=body.querySelector(`[data-socket=\"${s.id}\"]`);pos.sock[s.id]=e&&e.offsetParent?c(e):null;const r=e.closest('.fw-style').getBoundingClientRect();pos.loose[s.id]={x:r.left-b.left+18+(i%3)*9,y:r.bottom-b.top-16-(i%2)*10};}return true;};\r\n  const rebind=()=>{plugs={};paths={};for(const s of styles){plugs[s.id]=menu.querySelector(`[data-plug=\"${s.id}\"]`);paths[s.id]=menu.querySelector(`[data-w=\"${s.id}\"]`);}\r\n   if(details.open){if(!measure())return;if(reduced())settle();else if(!raf)loop();}};\r\n  const slackOf=i=>ROPE.plugSlack+(i%5)*ROPE.slackSpread;\r\n  const state=(s,i)=>R[s.id]??=(()=>{const hub=pos.anchor[s.id],looseLen=46+(i%3)*7,sock=pos.sock[st.cables[s.id]],on=!!sock;\r\n   const c={i,looseLen,len:looseLen,rope:makeRope(hub.x,hub.y,looseLen),pin:null,drag:null};\r\n   if(on){c.pin={...sock};c.len=dist(hub,sock)*slackOf(i);c.rope=makeRope(hub.x,hub.y,c.len);c.rope.pts.forEach((p,j,a)=>{const f=j/(a.length-1);p.x=p.px=hub.x+(sock.x-hub.x)*f;p.y=p.py=hub.y+(sock.y-hub.y)*f;});connected.add(s.id);paintPower(st.cables[s.id]);}\r\n   return c;})();\r\n  function tick(dt,t,instant){\r\n   const target=tilt&&t-tiltAt<1500?tilt:{gx:Math.sin(t/1000*Math.PI*2*ROPE.swayHz)*ROPE.sway,gy:1};\r\n   const k=instant?1:Math.min(1,dt*10);grav={gx:grav.gx+(target.gx-grav.gx)*k,gy:grav.gy+(target.gy-grav.gy)*k};\r\n   styles.forEach((s,i)=>{\r\n    const c=state(s,i),socketId=st.cables[s.id],sock=pos.sock[socketId],hub=pos.anchor[s.id],powered=!!sock;let end=null,len=c.looseLen;\r\n    if(c.drag){end={x:c.drag.cx-pos.b.left,y:c.drag.cy-pos.b.top};len=Math.max(c.looseLen,dist(hub,end)*1.03+8);c.pin=null;}\r\n    else if(powered){\r\n     c.pin??={...c.rope.pts.at(-1)};const f=instant?1:Math.min(1,dt*14);c.pin.x+=(sock.x-c.pin.x)*f;c.pin.y+=(sock.y-c.pin.y)*f;\r\n     if(dist(c.pin,sock)<1.5){c.pin={...sock};if(!connected.has(s.id)){connected.add(s.id);home(socketId);}}\r\n     end=c.pin;len=Math.max(dist(hub,end)*slackOf(i),dist(hub,end)+6);\r\n    }else{c.pin=null;const park=pos.loose[s.id];end={x:park.x+grav.gx*6,y:park.y};len=Math.max(c.looseLen,dist(hub,end)*1.15);if(connected.delete(s.id))styles.forEach(s=>paintPower(s.id));}\r\n    c.len+=(len-c.len)*(instant?1:Math.min(1,dt*10));if(end)c.len=Math.max(c.len,dist(hub,end)+2);\r\n    const g=powered&&!c.drag?grav:rotate(grav,((i>>1)*2+(i&1))*ROPE.fanDeg);\r\n    stepRope(c.rope,dt,g,{anchor:hub,end,length:c.len});\r\n    // Each loose plug rests beside its own switch. Connected cables can cross to any socket.\r\n    const W=pos.b.width,pts=c.rope.pts;\r\n    for(let j=1;j<pts.length-(end?1:0);j++){const q=pts[j],lo=5,hi=W-5;\r\n     q.x=Math.min(hi,Math.max(lo,q.x));\r\n     q.y=Math.max(0,Math.min(pos.b.height,q.y));\r\n    }\r\n\r\n   });\r\n  }\r\n  function draw(){\r\n   styles.forEach(s=>{const c=R[s.id];if(!c)return;const pts=c.rope.pts,e=pts.at(-1),q=pts.at(-2),a=Math.atan2(e.y-q.y,e.x-q.x)*180/Math.PI-90,d=ropePath(pts),g=paths[s.id];\r\n    g?.querySelectorAll('path').forEach(pa=>pa.setAttribute('d',d));g?.classList.toggle('on',connected.has(s.id));\r\n    const p=plugs[s.id];if(p)p.dataset.connectedSocket=st.cables[s.id]||'';if(p)p.style.transform=`translate(${(e.x-22).toFixed(1)}px,${(e.y-22).toFixed(1)}px) rotate(${a.toFixed(1)}deg)`;});\r\n  }\r\n  function frame(now){raf=0;if(!details.open||!body?.isConnected)return;const dt=Math.min(1/30,(now-(last||now))/1000||1/60);last=now;\r\n   if(!measure())return;edgeScroll();tick(dt,now,false);draw();raf=requestAnimationFrame(frame);}\r\n  function loop(){last=0;raf=requestAnimationFrame(frame);}\r\n  function settle(){if(!measure())return;grav={gx:0,gy:1};for(let i=0;i<ROPE.settleSteps;i++)tick(1/60,0,true);draw();}\r\n  const edgeScroll=()=>{const d=styles.map(s=>R[s.id]?.drag).find(Boolean);if(!d)return;const r=menu.getBoundingClientRect();if(d.cy<r.top+40)menu.scrollTop-=10;else if(d.cy>r.bottom-40)menu.scrollTop+=10;};\r\n  function home(id){ // the plug clicked in: spark, click sound, lamps power up in sequence\r\n   detent('lever');const sec=menu.querySelector(`.fw-style[data-style=\"${id}\"]`),sock=menu.querySelector(`[data-socket=\"${id}\"]`);\r\n   paintPower(id);sec?.classList.add('fw-pulse');sock?.classList.add('fw-spark');setTimeout(()=>{sec?.classList.remove('fw-pulse');sock?.classList.remove('fw-spark');},1300);}\r\n  return {R,rebind,\r\n   open(){if(unsub)return;fitBoard();settle();unsub=subscribePhoneMotion(g=>{tilt={gx:g.gx,gy:g.gy};tiltAt=performance.now();});if(!reduced()&&!raf)loop();},\r\n   close(){unsub?.();unsub=null;tilt=null;cancelAnimationFrame(raf);raf=0;},\r\n   resettle(snap=false){if(!details.open)return;if(reduced()||snap)settle();else{measure();if(!raf)loop();}},\r\n   dragStart(id,e){const c=R[id];if(!c)return;c.drag={cx:e.clientX,cy:e.clientY,sx:e.clientX,sy:e.clientY,moved:false};if(reduced())settle();},\r\n   dragMove(id,e){const d=R[id]?.drag;if(!d)return;d.cx=e.clientX;d.cy=e.clientY;if(Math.hypot(d.cx-d.sx,d.cy-d.sy)>6)d.moved=true;if(reduced())settle();},\r\n   dragEnd(id){const c=R[id],d=c?.drag;if(!d)return null;measure();const end={x:d.cx-pos.b.left,y:d.cy-pos.b.top};c.drag=null;c.pin={...end};const nearest=Object.entries(pos.sock).filter(([,p])=>p).sort((a,b)=>dist(end,a[1])-dist(end,b[1]))[0];return {moved:d.moved,socket:nearest&&dist(end,nearest[1])<32?nearest[0]:null};}};\r\n })();\r\n const paintPower=id=>{const sec=menu.querySelector(`.fw-style[data-style=\"${id}\"]`);if(sec)sec.dataset.power=connected.has(wireFor(id))?'on':'off';paintHead();};\r\n function connect(wire,socket){\r\n  const occupant=socket&&wireFor(socket);\r\n  if(occupant&&occupant!==wire){delete st.cables[occupant];connected.delete(occupant);}\r\n  delete st.cables[wire];connected.delete(wire);\r\n  if(socket)st.cables[wire]=socket;\r\n  st.powered=new Set(Object.values(st.cables));save();detent(socket?'switch':'lever');\r\n  for(const s of styles){paintPower(s.id);paintLadder(s.id);const p=menu.querySelector(`[data-plug=\"${s.id}\"]`);p?.setAttribute('aria-pressed',String(!!st.cables[s.id]));if(p)p.dataset.connectedSocket=st.cables[s.id]||'';}\r\n  rig.resettle(true);\r\n }\r\n function autoPlug(wire){if(st.cables[wire]){connect(wire,null);return;}\r\n  const socket=styles.find(s=>s.id===wire&&cap(s.id)>0&&!st.powered.has(s.id))||styles.find(s=>cap(s.id)>0&&!st.powered.has(s.id));if(socket)connect(wire,socket.id);\r\n }\r\n menu.addEventListener('click',event=>{\r\n  const b=event.target.closest('button');if(!b)return;\r\n  if(b.matches('[data-fw-start]'))begin();\r\n  else if(b.matches('[data-hub]')){const open=styles.filter(s=>cap(s.id)>0),all=open.every(s=>st.powered.has(s.id));\r\n   if(all)Object.keys(st.cables).forEach(wire=>connect(wire,null));\r\n   else for(const socket of open)if(!st.powered.has(socket.id)){const wire=styles.find(s=>!st.cables[s.id]);if(wire)connect(wire.id,socket.id);}\r\n  }else if(b.matches('.fw-plug')&&event.detail===0)autoPlug(b.dataset.plug);\r\n });\r\n menu.addEventListener('pointerdown',event=>{const b=event.target.closest('.fw-plug');if(!b)return;event.preventDefault();b.setPointerCapture?.(event.pointerId);rig.dragStart(b.dataset.plug,event);});\r\n menu.addEventListener('pointermove',event=>{const b=event.target.closest('.fw-plug');if(b)rig.dragMove(b.dataset.plug,event);});\r\n const release=event=>{const b=event.target.closest?.('.fw-plug');if(!b)return;const id=b.dataset.plug,r=rig.dragEnd(id);if(!r)return;if(event.type==='pointercancel'){rig.resettle();return;}if(r.moved)connect(id,r.socket);else autoPlug(id);};\r\n menu.addEventListener('pointerup',release);menu.addEventListener('pointercancel',release);\r\n const setControl=event=>{const i=event.target.closest('[data-enable],[data-dial]');if(!i||i.disabled)return;const id=i.closest('.fw-style').dataset.style;\r\n  if(i.matches('[data-dial]')){st.difficulties[id]=Math.max(1,Math.min(Number(i.value),cap(id)));if(lvl(id)>0)st.levels[id]=st.difficulties[id];}\r\n  else st.levels[id]=i.checked?difficulty(id):0;\r\n  save();detent('switch');paintLadder(id);\r\n };\r\n menu.addEventListener('input',setControl);\r\n // A real rotary gesture: the holograms mark the exercise levels around each metal dial.\r\n const turnDial=event=>{const dial=event.target.closest('[data-dial]');if(!dial||dial.disabled)return;event.preventDefault();\r\n  const r=dial.getBoundingClientRect(),angle=Math.max(-135,Math.min(135,Math.atan2(event.clientX-r.left-r.width/2,r.top+r.height/2-event.clientY)*180/Math.PI)),id=dial.closest('.fw-style').dataset.style;\r\n  dial.value=Math.min(cap(id),1+Math.round((angle+135)/270*(styleLadder(id).length-1)));dial.dispatchEvent(new Event('input',{bubbles:true}));\r\n };\r\n menu.addEventListener('pointerdown',event=>{const dial=event.target.closest('[data-dial]');if(!dial||dial.disabled)return;dial.setPointerCapture?.(event.pointerId);turnDial(event);});\r\n menu.addEventListener('pointermove',event=>{if(event.buttons&&event.target.matches('[data-dial]'))turnDial(event);});\r\n menu.addEventListener('toggle',()=>rig.resettle(),true);\r\n menu.addEventListener('scroll',()=>rig.resettle(),{passive:true});\r\n window.addEventListener('resize',()=>{fitBoard();rig.resettle();});\r\n details.addEventListener('keydown',event=>{if(event.key==='Escape'&&details.open){details.open=false;details.querySelector('summary').focus();}});\r\n // the board is part of the console now, so tapping elsewhere (BEGIN, dials) no longer folds it\r\n const goals=document.getElementById('podGoals');\r\n details.addEventListener('toggle',()=>{if(details.open){goals.open=false;rig.open();}else rig.close();});goals.addEventListener('toggle',()=>{if(goals.open)details.open=false;});\r\n details.querySelector('summary').addEventListener('click',()=>{try{if(!localStorage.getItem('myr5.tiltPermission'))void requestPhoneMotion();}catch{}});\r\n\r\n // ---- runner ----\r\n let run=null,lastPose=0,monitor=0;\r\n const nameOf=id=>EXERCISES[id]?.name??id;\r\n const badge=id=>`<span class=\"fw-workout-symbol\" role=\"img\" aria-label=\"${esc(nameOf(id))}\" title=\"${esc(nameOf(id))}\">${icon(id)}</span>`;\r\n const paint=html=>{hud.innerHTML=html;hud.hidden=false;};\r\n const onRest=()=>document.body.dataset.screen==='rest';\r\n const bar=()=>{const exTotal=run.steps.filter(s=>s.type==='exercise').length,n=run.done.length+1;paint(`<div class=\"fw-top\"><b>FULL WORKOUT ${n}/${exTotal}</b>${badge(run.steps[run.i].id)}<button type=\"button\" data-fw-end>END</button></div><div class=\"fw-left\" data-fw-left aria-hidden=\"true\" hidden></div><div class=\"fw-lost\" data-fw-lost hidden>STEP BACK INTO VIEW</div>`);hud.dataset.mode='exercise';};\r\n function finish(reasonText){\r\n  if(!run)return;const r=run;run=null;clearInterval(monitor);window.myr5PowerChips?.clear?.();setCircuit?.(null);freeze?.(false);\r\n  const xp=r.done.reduce((t,d)=>t+(d.xp||0),0),secs=Math.round((Date.now()-r.t0)/1000);\r\n  hud.dataset.mode='summary';\r\n  paint(`<div class=\"fw-panel\"><h2>${reasonText==='ended'?'Workout ended':'Full workout done'}</h2>\r\n   <p class=\"fw-big\">${r.done.length} of ${r.steps.filter(s=>s.type==='exercise').length} exercises · ${clock(secs)}${xp?` · +${xp} XP`:''}</p>\r\n   <ol>${r.done.map(d=>`<li>${badge(d.id)} <span>${Math.round(d.value)}${d.unit}</span></li>`).join('')||'<li>No sets finished.</li>'}</ol>\r\n   <button type=\"button\" data-fw-close>Done</button></div>`);\r\n  if(reasonText!=='ended')voice?.say('Full workout complete. Great work.',{interrupt:true});\r\n }\r\n hud.addEventListener('click',event=>{\r\n  const b=event.target.closest('button');if(!b)return;\r\n  if('fwEnd' in b.dataset){const r=run;document.getElementById('stop')?.click();if(run===r)finish('ended');}\r\n  if('fwClose' in b.dataset){hud.hidden=true;hud.innerHTML='';if(document.body.dataset.screen==='rest')void leaveRest?.();}\r\n  if('fwSkip' in b.dataset&&run)run.skip=true;\r\n  if('fwResume' in b.dataset&&run){lastPose=Date.now();bar();}\r\n  if('fwSkipStep' in b.dataset&&run){run.skipStep=true;document.getElementById('stop')?.click();}\r\n });\r\n const unitOf=id=>{const k=EXERCISES[id]?.kind;return k==='hold'||k==='pace'?'s':k==='steps'?' steps':' reps';};\r\n window.addEventListener('myr5:pose',event=>{if(event.detail?.points)lastPose=Date.now();});\r\n window.addEventListener('myr5:rest-end',()=>{if(run)run.restOver=true;});\r\n\r\n // 4 Hz HUD heartbeat during a set: huge remaining count (voice on the last 3) and lost-tracking handling.\r\n function tickHud(){\r\n  const r=run;if(!r||hud.dataset.mode!=='exercise')return;\r\n  const n=left?.(),el=hud.querySelector('[data-fw-left]'),lostEl=hud.querySelector('[data-fw-lost]');\r\n  if(el){el.hidden=n==null;if(n!=null&&el.textContent!==String(n))el.textContent=n;}\r\n  if(n!=null&&n!==r.lastLeft){r.lastLeft=n;if(n>=1&&n<=3)voice?.say(String(n),{interrupt:true});}\r\n  const since=Date.now()-lastPose;\r\n  if(document.body.dataset.tracking==='true'&&since>2000){\r\n   if(!r.lost){r.lost=true;freeze?.(true);}\r\n   if(lostEl)lostEl.hidden=false;\r\n   if(since>20000){hud.dataset.mode='paused';paint(`<div class=\"fw-panel\"><h2>Circuit paused</h2><p class=\"fw-big\">We lost you</p><p class=\"fw-sub\">Step back into view, then resume.</p><button type=\"button\" data-fw-resume>RESUME</button><button type=\"button\" data-fw-skip-step>SKIP</button> <button type=\"button\" data-fw-end>End workout</button></div>`);}\r\n  }else if(r.lost){r.lost=false;freeze?.(false);if(lostEl)lostEl.hidden=true;}\r\n }\r\n // Done stinger (1 s) on top of the camera while the pod swings into rest.\r\n async function stinger(){hud.dataset.mode='stinger';paint('<div class=\"fw-stinger\">DONE</div>');try{navigator.vibrate?.([40,50,40]);}catch{}await wait(1000);}\r\n\r\n // Rest is the pod's own rest screen (flow.restUntil, set from the REST knob). The circuit only watches it:\r\n // at 5 s left it shows \"Next up\" + voice, counts 3-2-1, then leaves rest. No second timer.\r\n async function playBreak(r,step){\r\n  r.restPending=false;r.skip=false;r.restOver=false;\r\n  // myr5:set-complete fires before the wormhole into rest finishes, so wait for the rest screen to really open.\r\n  for(let t=0;t<17&&!onRest()&&run===r;t++)await wait(150);\r\n  const inRest=onRest(),end=inRest&&restEnd?.()||Date.now()+step.seconds*1000;\r\n  let said=false,last=null;\r\n  while(run===r&&!r.skip&&!(inRest&&r.restOver)&&Date.now()<end){\r\n   const secs=Math.ceil((end-Date.now())/1000);\r\n   if(!inRest||secs<=5){\r\n    if(!said){said=true;voice?.say(`${step.between==='styles'?'Style change. ':''}Next up, ${nameOf(step.next.id)}.`,{interrupt:true});}\r\n    hud.dataset.mode='break';\r\n    paint(`<div class=\"fw-panel\"><p class=\"fw-eyebrow\">${step.between==='styles'?'STYLE BREAK':'BREATHER'}</p><div class=\"fw-clock\" role=\"timer\">${clock(secs)}</div>\r\n     <p>Next up</p><p class=\"fw-big\">${badge(step.next.id)}</p><p class=\"fw-sub\">${r.steps.find(s=>s.id===step.next.id).goal}${unitOf(step.next.id)}</p>\r\n     <button type=\"button\" data-fw-skip>Skip rest</button> <button type=\"button\" data-fw-end>End workout</button></div>`);\r\n    if(inRest&&secs<=3&&secs!==last){last=secs;voice?.say(String(secs),{interrupt:true});}\r\n   }else{hud.dataset.mode='rest';hud.hidden=true;}\r\n   await wait(250);\r\n  }\r\n  if(run!==r)return;\r\n  if(onRest()){await leaveRest?.();for(let t=0;t<40&&onRest();t++)await wait(150);}\r\n  voice?.say('Go.',{interrupt:true});\r\n }\r\n async function play(r){\r\n  while(run===r&&r.i<r.steps.length){\r\n   const step=r.steps[r.i];\r\n   if(step.type==='break'){if(r.restPending)await playBreak(r,step);if(run!==r)return;r.i++;continue;}\r\n   bar();window.myr5PowerChips?.set?.(r.steps.filter(s=>s.type==='exercise').map(s=>({id:s.id,name:nameOf(s.id)})),r.done.length);try{preload?.(step.id);}catch{}lastPose=Date.now();r.lost=false;r.lastLeft=null;setCircuit?.({target:step.goal,restSeconds:r.knobs.rest});select(step.id,step.goal);\r\n   const completed=new Promise(res=>{r.onSet=res;}),aborted=new Promise(res=>{r.onAbort=()=>res(null);}),result=await Promise.race([Promise.resolve(start()).then(()=>completed),aborted]);\r\n   if(run!==r)return;\r\n   if(!result){if(r.skipStep){r.skipStep=false;r.i++;continue;}finish('ended');return;}\r\n   r.done.push({id:step.id,value:result.value,xp:result.xp,unit:unitOf(step.id)});r.restPending=true;r.i++;window.myr5PowerChips?.set?.(r.steps.filter(s=>s.type==='exercise').map(s=>({id:s.id,name:nameOf(s.id)})),r.done.length);\r\n   if(r.i<r.steps.length)await stinger();\r\n  }\r\n  if(run===r)finish('done');\r\n }\r\n function begin(){\r\n  if(run||document.body.dataset.tracking==='true'||!selection().length)return;\r\n  const steps=buildFullWorkout(selection(),{seed:Date.now(),circuit:{...knobs}});\r\n  details.open=false;run={steps,i:0,done:[],t0:Date.now(),knobs:{...knobs}};clearInterval(monitor);monitor=setInterval(tickHud,250);void play(run);\r\n }\r\n window.addEventListener('myr5:set-complete',event=>{if(run&&event.detail?.mode===run.steps[run.i]?.id)run.onSet?.(event.detail);});\r\n window.addEventListener('myr5:workout-stopped',()=>{if(run&&run.steps[run.i]?.type==='exercise')run.onAbort?.();});\r\n for(const name of ['myr5:performance-progress','myr5:account-ready'])window.addEventListener(name,render);\r\n render();\r\n return {render,selection,get running(){return !!run;}};\r\n}\r\n","full-workout.mjs":"// FULL WORKOUT (circuit) mode: pure logic only (no DOM) so it is unit tested directly.\r\n// One of each checked exercise, grouped into style blocks (a style = a focus group: boxing, legs, ...),\r\n// shuffled, with a long rest between style blocks and a short one between exercises in a block.\r\nimport {EXERCISES,FOCUS_GROUPS} from './exercise-library.mjs';\r\n\r\n// TUNING KNOBS -- real phones need tuning; change only here.\r\nexport const FULL_WORKOUT={\r\n shuffleStyles:true,             // false = keep the library's style order\r\n storageKey:'myr5-full-workout-v1',\r\n};\r\n\r\n/** Every exercise a circuit may use. Endless runner modes (Subway Burpees) are not a counted set. */\r\nexport const circuitExercises=()=>Object.values(EXERCISES).filter(e=>!e.endless);\r\nexport const circuitStyles=()=>FOCUS_GROUPS.map(g=>({id:g.id,name:g.name,exercises:circuitExercises().filter(e=>e.group===g.id)})).filter(g=>g.exercises.length);\r\n\r\n// Circuit knobs (detents only). REPS applies to rep exercises; TIME to holds and cardio; REST is the one rest between steps.\r\nexport const CIRCUIT_KNOBS={reps:[5,10,15,20],time:[30,60,90],rest:[30,60,90],defaults:{reps:10,time:60,rest:60},storageKey:'myr5CircuitKnobs'};\r\nexport function readKnobs(storage=globalThis.localStorage){\r\n let v={};try{v=JSON.parse(storage.getItem(CIRCUIT_KNOBS.storageKey))||{};}catch{}\r\n return Object.fromEntries(['reps','time','rest'].map(k=>[k,CIRCUIT_KNOBS[k].includes(v[k])?v[k]:CIRCUIT_KNOBS.defaults[k]]));\r\n}\r\nexport const goalFor=(id,k=CIRCUIT_KNOBS.defaults)=>EXERCISES[id]?.kind==='reps'?k.reps:k.time;\r\n// Whole-circuit estimate for the START button: work per step + one rest between steps. A rep is guessed at 3 s.\r\nexport const circuitSeconds=(ids,k)=>ids.length?ids.reduce((t,id)=>t+(EXERCISES[id]?.kind==='reps'?k.reps*3:k.time),0)+(ids.length-1)*k.rest:0;\r\n\r\n/** Small seeded PRNG (mulberry32): same seed, same circuit. */\r\nexport function seededRandom(seed){let a=(Number(seed)||0)>>>0;return()=>{a=a+0x6D2B79F5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};}\r\nexport function shuffle(list,random=Math.random){const a=[...list];for(let i=a.length-1;i>0;i--){const j=Math.floor(random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;}\r\n\r\n/** Steps: {type:'exercise',id,style,goal} and {type:'break',seconds,between:'styles'|'exercises',next:{id,style}}. */\r\nexport function buildFullWorkout(ids,{seed=Date.now(),knobs=FULL_WORKOUT,circuit=CIRCUIT_KNOBS.defaults}={}){\r\n const random=seededRandom(seed),chosen=[...new Set(ids)].filter(id=>EXERCISES[id]&&!EXERCISES[id].endless);\r\n const byStyle=new Map();\r\n for(const id of chosen){const style=EXERCISES[id].group;if(!byStyle.has(style))byStyle.set(style,[]);byStyle.get(style).push(id);}\r\n let styles=[...byStyle.keys()];\r\n if(knobs.shuffleStyles)styles=shuffle(styles,random);\r\n const flat=[];\r\n for(const style of styles)for(const id of shuffle(byStyle.get(style),random))flat.push({type:'exercise',id,style,goal:goalFor(id,circuit)});\r\n const steps=[];\r\n flat.forEach((step,i)=>{\r\n  if(i){const styleChange=flat[i-1].style!==step.style;steps.push({type:'break',between:styleChange?'styles':'exercises',seconds:circuit.rest,next:{id:step.id,style:step.style}});}\r\n  steps.push(step);\r\n });\r\n return steps;\r\n}\r\n\r\n// ---- power + level ladder ----\r\n// A style (focus group) is powered by its plug. Inside a powered style the exercises are a level ladder, easiest first\r\n// (library order). Level L means switches 1..L are engaged; the circuit uses the exercise at switch L, one per style.\r\nexport const styleLadder=style=>circuitExercises().filter(e=>e.group===style);\r\n/** Highest level reachable: the leading run of unlocked exercises (a locked rung kills every rung above it). */\r\nexport function maxLevel(style,canUse){const l=styleLadder(style);let n=0;while(n<l.length&&canUse(l[n].id))n++;return n;}\r\n/** Tap switch k (1-based): an off switch engages 1..k; an engaged switch drops the ladder to k-1. Dead switches (above max) do nothing. */\r\nexport function tapLevel(level,k,max=Infinity){if(k>max||k<1)return level;return k<=level?k-1:k;}\r\n/** One exercise per powered style at its engaged level; unpowered or level-0 styles contribute none. */\r\nexport function sectionPicks({powered,levels,canUse}){\r\n const out=[];\r\n for(const style of powered){const l=styleLadder(style),lv=Math.min(levels[style]??0,maxLevel(style,canUse));if(lv>=1)out.push(l[lv-1].id);}\r\n return out;\r\n}\r\n","gala-handoff.mjs":"import {isInstalled} from './install-context.mjs';\r\nconst KEY='myr5-gala-run-v1';\r\nconst valid=data=>data?.version===1&&/^[0-9a-f-]{36}$/.test(data.id)&&/^[a-f0-9]{64}$/.test(data.token);\r\nexport function decodeGala(raw){if(typeof raw!=='string'||raw.length>18000)throw Error('This Gala invitation is invalid.');let data;try{data=JSON.parse(decodeURIComponent(escape(atob(raw))));}catch{throw Error('This Gala invitation is unreadable.');}if(!valid(data))throw Error('This Gala invitation is invalid.');return data;}\r\nfunction importIdentity(data){if(window.MBS_DJ?.valid(data.dj))localStorage.setItem('mbs-dj-identity-v1',JSON.stringify(data.dj));if(data.avatar&&window.GalaAvatar){try{localStorage.setItem('mominc-avatar-v1',JSON.stringify(window.GalaAvatar.normalize(data.avatar)));window.dispatchEvent(new Event('mominc-avatar-change'));}catch{}}}\r\nexport function captureGala(){const params=new URLSearchParams(location.hash.slice(1)),raw=params.get('gala');if(raw){params.delete('gala');history.replaceState(null,'',location.pathname+location.search+(params.size?'#'+params:''));const data=decodeGala(raw);localStorage.setItem(KEY,JSON.stringify(data));importIdentity(data);return data;}try{const data=JSON.parse(localStorage.getItem(KEY));return valid(data)?data:null;}catch{return null;}}\r\nasync function request(path,data,token){const r=await fetch('/api/gala/'+path,{method:data?'POST':'GET',credentials:'same-origin',cache:'no-store',headers:{...(data?{'Content-Type':'application/json'}:{}),...(token?{Authorization:'Bearer '+token}:{})},body:data?JSON.stringify(data):undefined,signal:AbortSignal.timeout(15000)});const value=await r.json();if(!r.ok)throw Error(value.error||'Your Gala run could not connect. Retry while connected.');return value;}\r\nexport async function prepareGalaInstall(){const data=captureGala();if(data)await request('install-draft',{id:data.id,token:data.token});return data;}\r\nasync function restoreGala(){let data=captureGala();if(!data){data=(await request('install-draft')).data;if(valid(data))localStorage.setItem(KEY,JSON.stringify(data));else data=null;}if(data)importIdentity(data);return data;}\r\nexport async function saveInstalledRun(){if(!isInstalled())return null;const data=await restoreGala();if(!data)return null;const value=await request('runs/'+data.id+'/install',{installed:true},data.token);if(value.djName&&!window.MBS_DJ?.read()){const base=value.djName.split(' · ')[0],dj={version:1,base,moniker:value.moniker,parts:base.split(' ').slice(0,3),chosenAt:value.startedAt,completedAt:value.startedAt};while(dj.parts.length<3)dj.parts.push('DJ');if(window.MBS_DJ?.valid(dj)){localStorage.setItem('mbs-dj-identity-v1',JSON.stringify(dj));window.dispatchEvent(new Event('mominc-avatar-change'));}}return value;}\r\nexport async function mountGalaReturn(){let data,error='';try{data=isInstalled()?await restoreGala():captureGala();}catch(e){error=e.message;}if(!data&&!error)return;let panel=document.getElementById('galaReturn');if(panel)return;panel=document.createElement('aside');panel.id='galaReturn';panel.style.cssText='padding:14px;margin:12px 0;background:#111d28;color:#acffe0;border:2px outset #648d99;font:14px/1.5 monospace';panel.innerHTML='<strong>GOON GALA · RUN ARCHIVE</strong><p role=\"status\"></p><a href=\"https://mominc.online/gala/terminal/\" style=\"color:#befaff\">Return to the Gala terminal →</a><button type=\"button\" style=\"margin-left:10px;min-height:40px\">Retry saving</button>';(document.querySelector('.install-card')||document.getElementById('accountContent')||document.body).append(panel);const message=panel.querySelector('p'),retry=panel.querySelector('button');async function save(){try{retry.disabled=true;await prepareGalaInstall();const run=await saveInstalledRun();message.textContent=run?(run.completedAt?'App received. Your run is saved. Join the rankings at the Gala terminal.':'App received. Finish your remaining run clearances at the Gala terminal.'):'Install and open MYR5 from its app icon to save this run.';retry.hidden=!!run;}catch(e){message.textContent=e.message;retry.hidden=false;}finally{retry.disabled=false;}}retry.onclick=save;window.addEventListener('online',save);if(error)message.textContent=error;else save();}\r\n","gesture-controls.mjs":"import {ExerciseRecognizer,GestureConfirmation} from './exercise-gestures.mjs';\r\nimport {openCamera,cameraFacing,widestZoom} from './camera.mjs';\r\n// The workout's pinned tracker, same-origin (scripts/mediapipe.mjs). A template keeps esbuild from resolving it.\r\nconst MEDIAPIPE='/vendor/mediapipe/0.10.14';\r\nconst timeout=(promise,ms,message)=>{let t;return Promise.race([promise,new Promise((_,reject)=>{t=setTimeout(()=>reject(new Error(message)),ms);})]).finally(()=>clearTimeout(t));};\r\nexport class HandControl {\r\n constructor(options){Object.assign(this,options);this.recognizer=new ExerciseRecognizer();this.confirmation=new GestureConfirmation();this.running=false;this.models=[];this.frame=0;this.lastVideo=-1;this.lastInference=-Infinity;}\r\n // Test seams: the constructor options replace these (no real camera or CDN import under node).\r\n openStream(){return openCamera(this.camera,20);}\r\n importVision(){return import(`${MEDIAPIPE}/vision_bundle.mjs`);}\r\n async start(){\r\n  this.stop();this.running=true;const generation=this.generation=(this.generation||0)+1;const active=()=>this.running&&this.generation===generation;\r\n  try{\r\n   const stream=await this.openStream();if(!active()){stream.getTracks().forEach(t=>t.stop());return;}this.stream=stream;this.video.srcObject=stream;\r\n   // A camera taken by iOS or another app ends or mutes its track; that is a stop with a visible reason.\r\n   const track=stream.getVideoTracks()[0];this.track=track;this.onTrackLost=()=>{if(!active())return;this.stop();this.onError('Camera was interrupted. Tap start to try again.');};\r\n   track.addEventListener('ended',this.onTrackLost);track.addEventListener('mute',this.onTrackLost);\r\n   await timeout(this.video.play(),10000,'No camera frames. Try the other camera.');if(!active())return;\r\n   if(this.useWidestZoom!==false)await widestZoom(track);if(!active())return;\r\n   this.video.style.transform=cameraFacing(stream.getVideoTracks()[0],this.camera)==='user'?'scaleX(-1)':'none';this.video.hidden=false;\r\n   this.message('Loading gesture recognition…');\r\n   const api=await timeout(this.importVision(),20000,'Gesture library download timed out.');if(!active())return;\r\n   const files=await timeout(api.FilesetResolver.forVisionTasks(MEDIAPIPE+'/wasm'),20000,'Gesture runtime download timed out.');if(!active())return;\r\n   const create=async(Type,modelAssetPath,options)=>{let expired=false;const loading=Type.createFromOptions(files,{baseOptions:{modelAssetPath,delegate:'CPU'},runningMode:'VIDEO',...options});loading.then(model=>{if(!active()||expired)model.close();},()=>{});try{const model=await timeout(loading,60000,'Gesture model loading timed out.');if(active())this.models.push(model);return model;}catch(error){expired=true;throw error;}};\r\n   if(this.trackPose!==false){const pose=await create(api.PoseLandmarker,MEDIAPIPE+'/pose_landmarker_lite.task',{numPoses:1});if(!active())return;this.pose=pose;}\r\n   const hand=await create(api.HandLandmarker,MEDIAPIPE+'/hand_landmarker.task',{numHands:2,minHandDetectionConfidence:.6,minTrackingConfidence:.6});if(!active())return;this.hand=hand;\r\n   this.message('Make an exercise gesture, then hold thumbs up to confirm.');this.loop(generation);\r\n  }catch(error){if(this.generation===generation)this.stop();throw error;}\r\n }\r\n stop(){this.running=false;this.generation=(this.generation||0)+1;cancelAnimationFrame(this.frame);this.track?.removeEventListener('ended',this.onTrackLost);this.track?.removeEventListener('mute',this.onTrackLost);this.track=null;this.lastVideo=-1;this.lastInference=-Infinity;this.models.splice(0).forEach(m=>{try{m.close();}catch{}});this.pose=null;this.hand=null;this.stream?.getTracks().forEach(t=>t.stop());this.stream=null;this.video.pause();this.video.srcObject=null;this.video.hidden=true;this.confirmation.reset();}\r\n loop(generation=this.generation){\r\n  if(!this.running||generation!==this.generation)return;\r\n  try{\r\n   const now=performance.now();\r\n   // Two small trackers run only in this optional menu, at most eight passes/s.\r\n   if(now-this.lastInference>=1000/(this.inferenceHz||8)&&this.video.readyState>=2&&this.video.currentTime!==this.lastVideo){\r\n    this.lastInference=now;this.lastVideo=this.video.currentTime;\r\n    const pose=this.pose?.detectForVideo(this.video,now),hands=this.hand.detectForVideo(this.video,now);\r\n    this.onRaw?.({landmarks:hands.landmarks,worldLandmarks:hands.worldLandmarks,handedness:hands.handedness,pose:pose?.landmarks,ts:now,frameId:this.video.currentTime});\r\n    if(this.trackPose!==false){\r\n    const input=this.recognizer.update(pose.landmarks[0],hands.landmarks,now,this.video.videoWidth/this.video.videoHeight);\r\n    this.onGesture(this.confirmation.update(input,now));\r\n    }\r\n   }\r\n   if(this.running)this.frame=requestAnimationFrame(()=>this.loop(generation));\r\n  }catch(error){this.stop();this.onError('Gesture tracking stopped. '+error.message);}\r\n }\r\n}\r\n","guest-history-choice.mjs":"import {IMPORT_UPLOAD_MANIFEST} from './local-coach/import-upload-manifest.mjs';\r\n\r\n// Opening/dismissing history records no choice and starts no upload. The user\r\n// selects concrete workouts and a destination shown by this render.\r\nexport function mountGuestHistoryChoice({host,adapter,getAccount,transitions,onChange=()=>{}}){\r\n let generation=0;\r\n const clear=()=>{generation++;host.replaceChildren();adapter.stop();};\r\n const unsubscribe=transitions.subscribe(clear);\r\n const element=(tag,text)=>{const node=document.createElement(tag);if(text!==undefined)node.textContent=text;return node;};\r\n async function render(){\r\n  const run=++generation,displayed=getAccount(),rows=await adapter.history();if(run!==generation)return;\r\n  host.replaceChildren();if(!rows.length)return;\r\n  host.append(element('h3','Choose how to keep device history'));\r\n  host.append(element('p','Signing in does not upload these workouts. Imported history stays outside rankings and rewards.'));\r\n  const status=element('p');status.setAttribute('role','status');\r\n  const options=element('div'),selected=new Set(),buttons=[];\r\n  for(const row of rows){\r\n   const line=element('label'),checkbox=element('input');checkbox.type='checkbox';checkbox.disabled=!row.selectable;\r\n   checkbox.addEventListener('change',()=>{if(checkbox.checked)selected.add(row.workout.clientWorkoutId);else selected.delete(row.workout.clientWorkoutId);});\r\n   const label=`${row.workout.metadata?.name||row.workout.mode} · ${new Date(row.workout.completedAt).toLocaleString()} · ${row.status.replaceAll('_',' ')}`;\r\n   line.append(checkbox,document.createTextNode(label));options.append(line,element('br'));\r\n  }\r\n  host.append(options,status);\r\n  async function act(work){\r\n   if(run!==generation)return;buttons.forEach(button=>button.disabled=true);\r\n   try{await work();if(run!==generation)return;await render();onChange();}\r\n   catch(error){if(run===generation)status.textContent=error.message;}\r\n   finally{if(run===generation)buttons.forEach(button=>button.disabled=false);}\r\n  }\r\n  const button=(label,work)=>{const node=element('button',label);node.type='button';node.addEventListener('click',()=>void act(work));buttons.push(node);host.append(node);return node;};\r\n  const choice=kind=>{if(selected.size<1||selected.size>100)throw Error('Select between one and 100 workouts.');return adapter.choose(kind,[...selected],displayed);};\r\n  button('Keep selected workouts local',()=>choice('keep_local'));\r\n  if(displayed?.user?.id){\r\n   host.append(element('p',`Import destination: ${displayed.user.email||displayed.user.id}`));\r\n   button('Save import choice for selected workouts',()=>choice('import'));\r\n   button('Check for account deletion',()=>adapter.reconcileDeletedTarget(displayed));\r\n   if(IMPORT_UPLOAD_MANIFEST.uploadsEnabled===true){\r\n    button('Import saved choices',()=>adapter.upload(displayed));\r\n    const parked=rows.filter(row=>row.status==='parked'&&row.item?.targetAccountId===displayed.user.id&&row.item.targetDataEpoch===displayed.dataEpoch).map(row=>row.item.claimId);\r\n    if(parked.length)button('Resume paused imports',()=>adapter.upload(displayed,{resumeParkedClaimIds:parked}));\r\n   }else host.append(element('p','Uploads are disabled in this build. Your choice is saved only on this device.'));\r\n  }else host.append(element('p','Sign in to choose an import destination.'));\r\n }\r\n return {render,close(){clear();unsubscribe();}};\r\n}\r\n","hand-companion.css":".avatar-dialog{width:min(94vw,560px)}.avatar-dialog h2{font-size:1.2rem}.avatar-dialog p,.avatar-dialog a,.avatar-dialog summary,.avatar-dialog button{font-size:1rem}.gala-import{margin:12px 0;padding:12px;border:1px solid #b08bc4;border-radius:12px}.gala-import a{display:block;margin-top:12px}.hand-subsection{display:grid;grid-template-columns:96px minmax(0,1fr);gap:16px;align-items:center;margin-top:24px;padding-top:20px;border-top:1px solid #a57aad66}.hand-subsection img{width:96px;height:120px;object-fit:contain;background:#1c1229;border-radius:14px}.hand-subsection small{font-size:.875rem;color:#dbb1ea}.hand-subsection h3{font-size:1.3rem;margin:4px 0}.hand-subsection p{line-height:1.4;margin:6px 0 12px}.hand-subsection button{width:100%}\r\n.avatar-dialog.hand-editing{width:min(1200px,100vw);max-width:100vw;height:96dvh;max-height:100dvh;padding:12px;overflow:hidden}.hand-editing>header{height:44px;margin:0}.hand-editing #handEditorPanel{height:calc(100% - 44px);display:grid;grid-template-rows:44px minmax(0,1fr)}.hand-editor-bar{display:flex;align-items:center;justify-content:space-between;gap:12px}.hand-editor-bar span{font-size:.875rem;color:#d8c1e3}#handEditorFrame{display:block;width:100%;height:100%;min-height:0;border:0;border-radius:14px;background:#6427a5}.hand-editing #identitySummary{display:none}\r\n.rest-hand{position:absolute;left:calc(50% + 36px);bottom:15%;width:clamp(95px,29vw,165px);height:clamp(115px,34vw,190px);z-index:8;pointer-events:none}#restHandMount,#restHandMount canvas{width:100%;height:100%;display:block}#restHandFallback{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}#restHandFallback[hidden]{display:none}.hand-team{display:grid;grid-template-columns:1fr auto;align-items:center;gap:7px 12px;padding:12px 0 0;font-size:.875rem;color:#e6c8ee}.hand-team progress{width:100%;height:9px;accent-color:#d99bff}.hand-team small{font-size:.875rem;color:#e6c8ee}.hand-team button{font-size:.875rem}.rest-feedback{min-height:2.8em}#identitySummary[hidden],#handEditorPanel[hidden]{display:none}\r\n@media(max-width:600px){.avatar-dialog.hand-editing{margin:0;width:100vw;height:100dvh;border-radius:0;padding:8px}.hand-editing>header{height:42px}.hand-editing #handEditorPanel{height:calc(100% - 42px);grid-template-rows:42px minmax(0,1fr)}.hand-editor-bar span{display:none}.hand-subsection{grid-template-columns:75px minmax(0,1fr);gap:12px}.hand-subsection img{width:75px}}\r\n","hand-companion.mjs":"const HAND_KEY='handborne-recipe-v4';\r\nexport function initHandCompanion(){\r\n const $=id=>document.getElementById(id);let companion=null,loading=null,inRest=false,disposed=false;\r\n function preview(){let family=11;try{const design=JSON.parse(localStorage.getItem(HAND_KEY));if(Number.isInteger(design?.sections?.palm)&&design.sections.palm>=0&&design.sections.palm<23)family=design.sections.palm;}catch{}\r\n  for(const id of ['identityHandPreview','restHandFallback'])$(id).src='/handborne/previews/family-'+String(family).padStart(2,'0')+'.png?v=5';\r\n }\r\n function status(text){$('handTeamStatus').textContent=text;const failed=/could not|unavailable/i.test(text);$('retryHand').hidden=!failed;$('restHandFallback').hidden=!failed;}\r\n async function load(){if(companion){companion.setActive(inRest);return;}if(loading)return loading;\r\n  loading=(async()=>{try{const {createHandCompanion}=await import('/handborne/companion.mjs');if(disposed||!inRest)return;companion=createHandCompanion($('restHandMount'),{onStatus:status});companion.setActive(inRest);}catch{status('3D hand unavailable. Your hand can still help with team strikes.');}finally{loading=null;}})();return loading;\r\n }\r\n function edit(){$('identitySummary').hidden=true;$('handEditorPanel').hidden=false;$('identity').classList.add('hand-editing');$('handEditorFrame').src='/handborne/index.html?embed=1';$('backToAvatar').focus();}\r\n function back(){ $('handEditorFrame').removeAttribute('src');$('handEditorPanel').hidden=true;$('identitySummary').hidden=false;$('identity').classList.remove('hand-editing');preview();if(inRest)companion?.refresh(); }\r\n $('customizeHand').addEventListener('click',edit);$('backToAvatar').addEventListener('click',()=>{back();$('customizeHand').focus();});$('identity').addEventListener('close',back);\r\n $('retryHand').addEventListener('click',()=>companion?companion.refresh():load());\r\n const changed=event=>{if(event.key===HAND_KEY||event.key===null)preview();};\r\n const message=event=>{if(event.origin!==location.origin||event.source!==$('handEditorFrame').contentWindow||event.data?.type!=='handborne:changed')return;preview();};\r\n window.addEventListener('storage',changed);window.addEventListener('message',message);preview();\r\n return {edit,enter(){inRest=true;preview();$('handCharge').value=0;$('handChargeLabel').textContent='Team strike in 3 taps';void load();},leave(){inRest=false;companion?.setActive(false);},hit(hit){companion?.gesture(hit.assisted?'fist-bump':hit.blocked?'high-five':'point-at-you');$('handCharge').value=hit.charge;$('handChargeLabel').textContent=hit.assisted?'Team strike!':`Team strike in ${3-hit.charge} ${hit.charge===2?'tap':'taps'}`;$('handTeamStatus').textContent=hit.assisted?(hit.blocked?'Helping Hand strikes. Coach blocks!':'Helping Hand doubles your strike!'):'Helping Hand is charging up.';},dispose(){disposed=true;companion?.dispose();window.removeEventListener('storage',changed);window.removeEventListener('message',message);}};\r\n}\r\n","handborne/index.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><meta name=\"theme-color\" content=\"#6427a5\"><title>Helping Hand · Customizer</title><meta name=\"description\" content=\"Customize your Helping Hand with a live preview, creature styles, nails, scales and gestures.\">  <script type=\"module\" src=\"/audio/standalone-sound.mjs\"></script>  <script src=\"/public-entry.mjs\" data-entry=\"/handborne\" data-module=\"/handborne/assets/hand-entry-DiWC2DxB.js\"></script>\r\n  <link rel=\"stylesheet\" crossorigin href=\"/handborne/assets/hand-entry-BUSUnr2n.css\">\r\n<link rel=\"stylesheet\" href=\"/shader-effects.css\"><script type=\"module\" src=\"/shader-entry.mjs\"></script><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><div id=\"root\"></div></body></html>\r\n","handborne/source/app/anatomy-comparison.tsx":"'use client';\r\nimport { HandViewer } from './hand-viewer';\r\nimport { useState } from 'react';\r\nimport { Switch } from '@/components/ui/switch';\r\nimport { REGIONS } from './catalog';\r\nimport type { Selection } from './recipe';\r\nexport const ANATOMY_STUDIES=[\r\n  {id:7,name:'Skeletal',description:'Open metacarpals, joint heads and slender bone shafts.'},\r\n  {id:18,name:'Mechanical',description:'Segment housings, hinge pins and a palm chassis.'},\r\n  {id:13,name:'Stone',description:'Broad palm slabs, faceted fingers and heavy knuckles.'},\r\n];\r\nexport const SOFT_STUDIES=[\r\n  {id:20,name:'Fluffy',description:'Curved fur strands, a soft undercoat and small round nails.'},\r\n  {id:21,name:'Jelly',description:'Glossy lime jelly with a translucent body and trapped bubbles.'},\r\n  {id:22,name:'Baby',description:'A smaller hand with short, plump fingers and soft wrist rolls.'},\r\n];\r\nexport function AnatomyComparison({variant='anatomy'}:{variant?:'anatomy'|'soft'}){\r\n  const soft=variant==='soft',studies=soft?SOFT_STUDIES:ANATOMY_STUDIES;\r\n  const [gray,setGray]=useState(!soft);\r\n  return <main className=\"anatomy-comparison\">\r\n    <header className=\"comparison-heading\"><div><p>HELPING HAND / {soft?'SOFT FORMS':'ANATOMY STUDIES'}</p><h1>{soft?'Fluffy. Jelly. Little.':'Three different structures.'}</h1></div><a href=\"/handborne/\">Back to customizer</a></header>\r\n    <div className=\"comparison-options\"><p className=\"comparison-note\">Same camera and lighting. Drag to inspect{soft?' — the baby hand is smaller in real scale':''}.</p><label className=\"gray-control\">Plain gray<Switch aria-label=\"Show all hands without colors or translucency\" checked={gray} onCheckedChange={setGray} /></label></div>\r\n    <div className=\"comparison-grid\">{studies.map(study=><section className=\"comparison-card\" key={study.id}>\r\n      <div className=\"comparison-canvas\"><HandViewer selection={Object.fromEntries(REGIONS.map(r=>[r.id,study.id])) as Selection} poseId=\"relaxed\" nailShape=\"family\" animate={false} gray={gray} selectedRegion=\"fingers\" exploded={false} onSelectRegion={()=>{}} /></div>\r\n      <div className=\"comparison-caption\"><h2>{study.name}</h2><p>{study.description}</p><a href={'/handborne/?anatomy='+study.id}>Customize this hand →</a></div>\r\n    </section>)}</div><a className=\"comparison-other\" href={'/handborne/?compare='+(soft?'anatomy':'soft')}>{soft?'Compare skeletal, mechanical & stone':'Compare fluffy, jelly & baby'} →</a>\r\n  </main>;\r\n}\r\n","handborne/source/app/browser-entry.tsx":"import {createRoot} from 'react-dom/client';\r\nimport Home from './page';\r\nimport './globals.css';\r\nimport './design-studio.css';\r\ncreateRoot(document.getElementById('root')!).render(<Home />);\r\n","handborne/source/app/catalog.ts":"import {BUILTIN_SURFACE_PROFILES,SPECIAL_SURFACE_PROFILES,builtinSurfaceProfile} from './material-patterns';\r\nimport palettes from './palettes.json';\r\nexport const REGIONS = [\r\n  { id: 'nails', label: 'Nails', short: 'Nails', description: 'Five claw and nail forms' },\r\n  { id: 'fingertips', label: 'Finger tips', short: 'Tips', description: 'Distal pads and final phalanges' },\r\n  { id: 'fingers', label: 'Fingers & knuckles', short: 'Fingers', description: 'Joined finger shafts, bases and knuckles' },\r\n  { id: 'palm', label: 'Palm', short: 'Palm', description: 'The inner hand surface' },\r\n  { id: 'back_of_hand', label: 'Back of hand', short: 'Back', description: 'The dorsal hand surface' },\r\n  { id: 'wrist', label: 'Wrist', short: 'Wrist', description: 'Stump, cuff and transition' },\r\n] as const;\r\n\r\nexport type RegionId = (typeof REGIONS)[number]['id'];\r\n\r\nexport type StyleFamily = {\r\n  id: number;\r\n  name: string;\r\n  realm: string;\r\n  primary: string;\r\n  secondary: string;\r\n  accent: string;\r\n  emissive: string;\r\n  roughness: number;\r\n  metalness: number;\r\n  paletteId?:string;\r\n  detail: string | 'clean' | 'vine' | 'caps' | 'plates' | 'scales' | 'spines' | 'coral' | 'bone' | 'mist' | 'flame' | 'halo' | 'void' | 'eyes' | 'rock' | 'crystal' | 'magma' | 'ice' | 'storm' | 'gears' | 'neon' | 'fur' | 'jelly' | 'baby';\r\n};\r\n\r\nconst LEGACY_STYLES: StyleFamily[] = [\r\n  { id: 0, name: 'Mortal', realm: 'Baseline · Earth', primary: '#b9856f', secondary: '#714c42', accent: '#f0c0a3', emissive: '#000000', roughness: 0.72, metalness: 0.02, detail: 'clean' },\r\n  { id: 1, name: 'Verdant', realm: 'Wildroot · Dimension 12', primary: '#3f7448', secondary: '#1e3b2b', accent: '#b8dd6e', emissive: '#172d13', roughness: 0.88, metalness: 0, detail: 'vine' },\r\n  { id: 2, name: 'Mycelial', realm: 'Sporesea · Dimension 09', primary: '#d5c7ae', secondary: '#776b82', accent: '#f59ec4', emissive: '#6d234a', roughness: 0.92, metalness: 0, detail: 'caps' },\r\n  { id: 3, name: 'Chitin', realm: 'Carapace · Dimension 31', primary: '#4f263d', secondary: '#180f20', accent: '#d17663', emissive: '#240716', roughness: 0.3, metalness: 0.28, detail: 'plates' },\r\n  { id: 4, name: 'Reptilian', realm: 'Scalehold · Dimension 04', primary: '#5f7a3f', secondary: '#283621', accent: '#cfb85c', emissive: '#101c08', roughness: 0.76, metalness: 0.04, detail: 'scales' },\r\n  { id: 5, name: 'Abyssal', realm: 'Deep Trench · Dimension 88', primary: '#132a42', secondary: '#06111f', accent: '#4edfd3', emissive: '#0b756f', roughness: 0.34, metalness: 0.12, detail: 'spines' },\r\n  { id: 6, name: 'Coral', realm: 'Living Reef · Dimension 22', primary: '#e46867', secondary: '#70344c', accent: '#ffd27a', emissive: '#8b2438', roughness: 0.83, metalness: 0, detail: 'coral' },\r\n  { id: 7, name: 'Skeletal', realm: 'Ossuary · Dimension 00', primary: '#d8d0af', secondary: '#80785f', accent: '#fff7d1', emissive: '#17160e', roughness: 0.9, metalness: 0, detail: 'bone' },\r\n  { id: 8, name: 'Spectral', realm: 'Veil · Dimension 13', primary: '#8ed9cd', secondary: '#294c58', accent: '#d5fff8', emissive: '#248e86', roughness: 0.18, metalness: 0.04, detail: 'mist' },\r\n  { id: 9, name: 'Infernal', realm: 'Cinder Court · Dimension 66', primary: '#7b211e', secondary: '#260b0a', accent: '#ff8a2a', emissive: '#d33812', roughness: 0.58, metalness: 0.1, detail: 'flame' },\r\n  { id: 10, name: 'Celestial', realm: 'High Orbit · Dimension 07', primary: '#e6dca4', secondary: '#786da7', accent: '#fffbd7', emissive: '#8d74d6', roughness: 0.24, metalness: 0.5, detail: 'halo' },\r\n  { id: 11, name: 'Voidborn', realm: 'Null Expanse · Dimension ∅', primary: '#16101f', secondary: '#060409', accent: '#9f64ff', emissive: '#482285', roughness: 0.16, metalness: 0.36, detail: 'void' },\r\n  { id: 12, name: 'Eldritch', realm: 'Watcher Fold · Dimension 47', primary: '#6b426c', secondary: '#252038', accent: '#b6ef63', emissive: '#4e7628', roughness: 0.64, metalness: 0.04, detail: 'eyes' },\r\n  { id: 13, name: 'Stone Golem', realm: 'Deep Strata · Dimension 19', primary: '#6f7068', secondary: '#393b37', accent: '#b7a37d', emissive: '#171811', roughness: 0.98, metalness: 0, detail: 'rock' },\r\n  { id: 14, name: 'Crystal', realm: 'Prism Vault · Dimension 52', primary: '#896bc2', secondary: '#302e66', accent: '#e0c8ff', emissive: '#5a3da8', roughness: 0.12, metalness: 0.3, detail: 'crystal' },\r\n  { id: 15, name: 'Magma', realm: 'Mantle · Dimension 03', primary: '#4a1713', secondary: '#140706', accent: '#ffb22f', emissive: '#e44712', roughness: 0.78, metalness: 0.06, detail: 'magma' },\r\n  { id: 16, name: 'Glacial', realm: 'White Silence · Dimension 71', primary: '#93c9df', secondary: '#315a76', accent: '#ebfdff', emissive: '#3c93bb', roughness: 0.2, metalness: 0.14, detail: 'ice' },\r\n  { id: 17, name: 'Stormcharged', realm: 'Tempest Ring · Dimension 28', primary: '#48536e', secondary: '#1a2236', accent: '#c3f7ff', emissive: '#3ebde0', roughness: 0.4, metalness: 0.32, detail: 'storm' },\r\n  { id: 18, name: 'Clockwork Robot', realm: 'Brass Meridian · Dimension 14', primary: '#9a6a37', secondary: '#33251d', accent: '#efd08c', emissive: '#5b2c12', roughness: 0.32, metalness: 0.82, detail: 'gears' },\r\n  { id: 19, name: 'Neon Synth', realm: 'Aftergrid · Dimension 20', primary: '#251d54', secondary: '#0b0921', accent: '#ff5bd7', emissive: '#b519ac', roughness: 0.22, metalness: 0.55, detail: 'neon' },\r\n  { id: 20, name: 'Fluffy', realm: 'Cloudburrow · Soft forms', primary: '#d6a987', secondary: '#bb8969', accent: '#ffe4c3', emissive: '#000000', roughness: .88, metalness: 0, detail: 'fur' },\r\n  { id: 21, name: 'Jelly', realm: 'Lime Lagoon · Soft forms', primary: '#a7ec69', secondary: '#f1ffd4', accent: '#c9ff90', emissive: '#000000', roughness: .13, metalness: 0, detail: 'jelly' },\r\n  { id: 22, name: 'Baby', realm: 'Little One · Soft forms', primary: '#efb398', secondary: '#d68e7c', accent: '#ffe2d5', emissive: '#000000', roughness: .52, metalness: 0, detail: 'baby' },\r\n];\r\n\r\nconst specialPaletteIds=[\"opal-jelly\", \"bubble-glass\", \"prism-crystal\", \"holo-foil\", \"glitter-resin\", \"galaxy-geode\",\"caustic-slime\",\"blister-hide\",\"rotten-rind\",\"parasite-nest\",\"exposed-sinew\",\"abyssal-maw\",\"circuit-alloy\",\"servo-armor\",\"chrome-rib\",\"carbon-mech\",\"hazard-panel\",\"reactor-glass\"];\r\nexport const STYLES:StyleFamily[]=[...LEGACY_STYLES,\r\n ...[{id:30,name:'Flat',roughness:.55,metalness:0},{id:31,name:'Clay',roughness:.92,metalness:0},...BUILTIN_SURFACE_PROFILES,builtinSurfaceProfile(56)!,...SPECIAL_SURFACE_PROFILES].map(p=>{\r\n  const palette=palettes.find(row=>row.id===specialPaletteIds[p.id-57]);\r\n  const [primary,secondary,accent]=palette?.colors??['#8b8f9a','#4a4d55','#e7e9ee'];\r\n  return{id:p.id,name:p.name,realm:'Surface finish',primary,secondary,accent,emissive:'#000000',roughness:p.roughness,metalness:p.metalness,detail:p.name.toLowerCase(),paletteId:palette?.id};\r\n })];\r\nexport const styleFor=(id:number)=>STYLES.find(style=>style.id===id)!;\r\nexport const hasStyle=(id:number)=>STYLES.some(style=>style.id===id);\r\nexport const DEFAULT_SELECTION: Record<RegionId, number> = {\r\n  nails: 14,\r\n  fingertips: 5,\r\n  fingers: 12,\r\n  palm: 3,\r\n  back_of_hand: 17,\r\n  wrist: 18,\r\n};\r\n\r\nexport const PURE_PRESETS = STYLES.filter(style=>![8,11].includes(style.id)).map((style) => ({\r\n  name: style.name,\r\n  description: `A pure ${style.name.toLowerCase()} hand`,\r\n  values: Object.fromEntries(REGIONS.map((region) => [region.id, style.id])) as Record<RegionId, number>,\r\n}));\r\n\r\n\r\n// Legacy IDs stay importable; retired looks are excluded from new picks.\r\nexport const PICKER_STYLES=STYLES.filter(style=>![8,11].includes(style.id)).sort((a,b)=>Number(b.id>=63)-Number(a.id>=63)||Number(b.id>=57)-Number(a.id>=57));\r\n","handborne/source/app/companion.ts":"import * as THREE from 'three';\r\nimport {RoomEnvironment} from 'three/examples/jsm/environments/RoomEnvironment.js';\r\nimport {assemble} from './hand-model';\r\nimport {bindHand,applyHandPose,type HandRig} from './pose-rig';\r\nimport {getPose,mixPoses} from './poses';\r\nimport {parseDesign,type HandDesign} from './recipe';\r\nimport {DEFAULT_SELECTION} from './catalog';\r\nimport {DEFAULT_NAIL_SHAPE} from './nails';\r\nexport {parseDesign};\r\nexport const HAND_KEY='handborne-recipe-v4';\r\nexport function readHand(storage:Pick<Storage,'getItem'>):HandDesign {\r\n  try{const value=storage.getItem(HAND_KEY);if(value)return parseDesign(value);}catch{}\r\n  return {sections:{...DEFAULT_SELECTION},pose:'relaxed',nailShape:DEFAULT_NAIL_SHAPE};\r\n}\r\nexport function createHandCompanion(host:HTMLDivElement,{storage=localStorage,onStatus=(_text:string)=>{},cropWrist=false}={}){\r\n  let disposed=false,revision=0,frame=0,rig:HandRig|null=null,hand:THREE.Group|null=null;\r\n  let currentPose=getPose('relaxed'),transition:{from:ReturnType<typeof getPose>;to:ReturnType<typeof getPose>;start:number}|null=null;\r\n  let gestureUntil=0,lastFrame=0,active=true;\r\n  const reduced=window.matchMedia('(prefers-reduced-motion: reduce)');\r\n  const renderer=new THREE.WebGLRenderer({alpha:true,antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,1.35));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.4;\r\n  renderer.domElement.setAttribute('role','img');renderer.domElement.setAttribute('aria-label','Your customized Helping Hand beside your Gala avatar');host.appendChild(renderer.domElement);\r\n  const scene=new THREE.Scene(),root=new THREE.Group();scene.add(root);\r\n  const environment=new RoomEnvironment(),pmrem=new THREE.PMREMGenerator(renderer);scene.environment=pmrem.fromScene(environment,.04).texture;environment.dispose();pmrem.dispose();\r\n  const camera=new THREE.PerspectiveCamera(34,1,.005,3);camera.position.set(.045,.08,.39);camera.lookAt(0,.035,0);\r\n  if(cropWrist){camera.position.set(.035,.10,.25);camera.lookAt(0,.085,0);renderer.domElement.setAttribute('aria-label','Your customized DJ hand');}\r\n  scene.add(new THREE.HemisphereLight('#fff4e0','#655179',2));\r\n  const key=new THREE.DirectionalLight('#fff2ce',3.6);key.position.set(.3,.3,.4);scene.add(key);\r\n  const rim=new THREE.DirectionalLight('#d899ff',2.5);rim.position.set(-.3,.15,-.2);scene.add(rim);\r\n  const resize=()=>{const w=Math.max(host.clientWidth,1),h=Math.max(host.clientHeight,1);renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();};\r\n  const observer=new ResizeObserver(resize);observer.observe(host);resize();\r\n  function release(){if(hand)hand.removeFromParent();rig?.dispose();rig=null;hand=null;}\r\n  async function refresh(){\r\n    const run=++revision;onStatus('Your hand is arriving…');\r\n    try{const design=readHand(storage),next=await assemble(design.sections,design.nailShape,design.scalePattern);\r\n      if(disposed||run!==revision){next.traverse(o=>{if(o instanceof THREE.Mesh&&o.userData.ownedGeometry)o.geometry.dispose();});return;}\r\n      release();hand=next;rig=bindHand(next);root.add(next);currentPose=getPose('relaxed');transition=null;applyHandPose(rig,currentPose);onStatus('Helping Hand ready');\r\n    }catch{if(!disposed&&run===revision)onStatus('Hand could not load. Tap Retry hand.');}\r\n  }\r\n  function gesture(id:string){if(disposed)return;transition={from:currentPose,to:getPose(id),start:performance.now()};gestureUntil=performance.now()+1500;}\r\n  function tick(now:number){if(disposed)return;frame=requestAnimationFrame(tick);if(!active||document.hidden||now-lastFrame<40)return;lastFrame=now;\r\n    if(transition&&rig){const t=reduced.matches?1:Math.min(1,(now-transition.start)/220);currentPose=mixPoses(transition.from,transition.to,t);applyHandPose(rig,currentPose);if(t===1)transition=null;}\r\n    if(gestureUntil&&now>gestureUntil){gestureUntil=0;transition={from:currentPose,to:getPose('relaxed'),start:now};}\r\n    root.position.y=reduced.matches?0:Math.sin(now/850)*.003;root.rotation.y=reduced.matches?0:Math.sin(now/1400)*.08;\r\n    renderer.render(scene,camera);\r\n  }\r\n  const changed=(event:StorageEvent)=>{if(event.key===HAND_KEY||event.key===null)void refresh();};window.addEventListener('storage',changed);\r\n  frame=requestAnimationFrame(tick);void refresh();\r\n  return {refresh,gesture,setActive(value:boolean){active=value;},dispose(){if(disposed)return;disposed=true;revision++;cancelAnimationFrame(frame);observer.disconnect();window.removeEventListener('storage',changed);release();scene.environment?.dispose();renderer.dispose();renderer.domElement.remove();}};\r\n}\r\n","handborne/source/app/design-studio.css":".design-studio{height:100dvh;min-height:0;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;padding:12px;background:linear-gradient(150deg,#b784df,#6427a5 65%,#351055);gap:12px}\r\n.design-studio .topbar{min-height:64px;padding:8px 14px;background:#fff3d6;border:2px solid #2a0a44;border-radius:14px;box-shadow:0 4px 0 #2a0a44;gap:12px;flex-wrap:nowrap}\r\n.design-studio .brand-copy h1{font-size:clamp(1rem,2.3vw,1.65rem);margin-top:4px}.design-studio .brand-copy p{font-size:.875rem}\r\n.design-studio .creator-grid{height:100%;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) minmax(310px,370px);gap:14px;padding:0}\r\n.design-studio .viewport-panel{height:100%;min-height:0;grid-column:auto;grid-row:auto;order:0;border-radius:20px;box-shadow:0 5px 0 #250b3c}\r\n.design-studio .viewport-chrome{top:18px;left:18px}.design-studio .orbit-hint{top:50px;left:18px;font-size:.875rem;letter-spacing:0;text-transform:none}.design-studio .view-tools{bottom:18px;left:14px;right:14px;justify-content:center;gap:8px}\r\n.design-controls{min-height:0;min-width:0;border:2px solid #2a0a44;border-radius:16px;overflow:hidden;background:#fff6e3;box-shadow:0 5px 0 #250b3c}\r\n.design-tabs{height:100%;gap:0}.design-tabs [data-slot=tabs-list]{width:100%;height:52px;min-height:52px;padding:5px;border-radius:0;background:#efdae9;border-bottom:2px solid #c5a2d6;gap:2px}.design-tabs [data-slot=tabs-trigger]{font-size:1rem;padding:8px 7px;color:#4a1a86}.design-tabs [data-slot=tabs-trigger][data-active]{background:#4a1a86;color:#fff6e3;border-radius:9px}\r\n.control-scroll{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;scrollbar-width:thin}.design-heading h2{font-family:var(--font-display);font-size:1.35rem;margin:0 0 4px}.design-heading p{font-size:1rem;line-height:1.25;color:#654773;margin:0 0 14px}\r\n.scope-switch{display:flex;gap:8px;margin-bottom:12px}.scope-switch>*{flex:1}.scope-switch [aria-pressed=true]{background:#4a1a86;color:#fff6e3}.part-target{display:flex;align-items:end;gap:8px;margin-bottom:12px}.part-target label{flex:1;font-size:1rem}.part-target [data-slot=native-select-wrapper]{width:100%}\r\n.design-studio .style-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:0}.design-studio .style-grid button{position:relative;border:2px solid #d5bbdf;border-radius:10px;background:#21142b;padding:0;overflow:hidden;min-height:110px}.design-studio .style-grid button.is-active{border-color:#4a1a86;box-shadow:0 0 0 2px #ffd36e}.design-studio .style-grid img{display:block;width:100%;height:95px;object-fit:contain}.design-studio .tile-label{display:block;position:static;padding:5px 2px;background:#fff3d6;color:#351055;font-size:.875rem;line-height:1.1;text-align:center;white-space:normal}\r\n.design-studio .hand-shape-buttons,.pose-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:14px}.pose-grid button{white-space:normal;height:auto;min-height:46px}.design-studio [aria-pressed=true]{border-color:#4a1a86}.pose-grid [aria-pressed=true],.design-studio .nail-shapes [aria-pressed=true]{background:#4a1a86;color:#fff6e3}.design-studio .nail-shop{margin-bottom:20px}.file-actions{display:grid;gap:12px}.control-note{font-size:1rem;line-height:1.3}.control-note button{text-decoration:underline}.save-indicator{font-size:.875rem;white-space:nowrap;color:#4a1a86}.design-studio .gray-control{margin:14px 0}\r\n@media(max-width:760px){.design-studio{padding:8px;gap:8px}.design-studio .topbar{min-height:62px;padding:6px 10px}.design-studio .top-actions{gap:4px;flex-wrap:nowrap}.design-studio .top-actions button{width:36px;min-width:36px;padding:6px}.design-studio .top-actions button span{display:none}.save-indicator{display:none}.design-studio .creator-grid{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(190px,43%) minmax(0,1fr);gap:9px}.design-studio .viewport-panel{height:100%;min-height:0}.design-studio .view-tools{bottom:9px}.design-studio .viewport-chrome{top:10px;left:12px}.design-studio .orbit-hint{top:37px;left:12px;font-size:.875rem}.control-scroll{padding:12px}.design-heading p{margin-bottom:10px}.design-studio .style-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.design-studio .style-grid img{height:74px}.design-studio .style-grid button{min-height:100px}.design-studio .tile-label{font-size:.875rem}.design-tabs [data-slot=tabs-list]{height:48px;min-height:48px}.design-tabs [data-slot=tabs-trigger]{padding:7px 5px}}\r\n@media(max-width:350px){.design-studio .style-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.design-studio .brand-copy p{font-size:.75rem}.design-studio .topbar{padding-inline:6px}}\r\n@media(max-height:550px) and (min-width:600px){.design-studio .creator-grid{grid-template-columns:minmax(0,1fr) minmax(290px,42%);grid-template-rows:minmax(0,1fr)}.design-studio .topbar{min-height:48px}.design-studio .brand-copy p{display:none}}\r\n","handborne/source/app/globals.css":"@import 'tailwindcss';\r\n@import 'tw-animate-css';\r\n@import 'shadcn/tailwind.css';\r\n@custom-variant dark (&:is(.dark *));\r\n@font-face{font-family:'Barlow Condensed';src:url('/handborne/fonts/barlow-400.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n@font-face{font-family:'Barlow Condensed';src:url('/handborne/fonts/barlow-500.ttf') format('truetype');font-weight:500;font-style:normal;font-display:swap}\r\n@font-face{font-family:'Barlow Condensed';src:url('/handborne/fonts/barlow-600.ttf') format('truetype');font-weight:600;font-style:normal;font-display:swap}\r\n@font-face{font-family:'Barlow Condensed';src:url('/handborne/fonts/barlow-700.ttf') format('truetype');font-weight:700;font-style:normal;font-display:swap}\r\n@font-face{font-family:'Bowlby One SC';src:url('/handborne/fonts/bowlby.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n@font-face{font-family:'Anton';src:url('/handborne/fonts/anton.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n@font-face{font-family:'VT323';src:url('/handborne/fonts/vt323.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}\r\n@theme inline {\r\n  --color-background:var(--background);--color-foreground:var(--foreground);\r\n  --font-sans:var(--font-geist-sans);--font-mono:var(--font-geist-mono);\r\n  --color-primary:var(--primary);--color-primary-foreground:var(--primary-foreground);\r\n  --color-secondary:var(--secondary);--color-secondary-foreground:var(--secondary-foreground);\r\n  --color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);\r\n  --color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);\r\n  --color-card:var(--card);--color-card-foreground:var(--foreground);\r\n  --color-popover:var(--popover);--color-popover-foreground:var(--foreground);\r\n  --color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);\r\n  --color-destructive:var(--destructive);--radius-lg:.7rem;--radius-md:.5rem;--radius-sm:.35rem;--radius-xl:1rem;\r\n}\r\n:root {\r\n  --font-geist-sans:'Barlow Condensed';--font-geist-mono:'VT323';--font-headline:'Bowlby One SC';--font-display:'Anton';\r\n  --background:#fdf6e3;--foreground:#2a0a44;--card:#fff9ec;--popover:#fff9ec;\r\n  --primary:#7a2fc4;--primary-foreground:#fff9ec;--secondary:#ffd36e;--secondary-foreground:#2a0a44;\r\n  --muted:#edddec;--muted-foreground:#654773;--accent:#ffd36e;--accent-foreground:#2a0a44;\r\n  --destructive:#b91c51;--border:#4a1a86;--input:#7a2fc4;--ring:#ff2f7a;\r\n  --pur:#7a2fc4;--pur-d:#4a1a86;--pur-dd:#2a0a44;--gold:#ffd36e;--cream:#fdf6e3;--hot:#ff2f7a;--sig:#39ff9a;\r\n}\r\n*{box-sizing:border-box}\r\nhtml,body{min-height:100%;margin:0}\r\nbody{background:var(--background);color:var(--foreground);font-family:var(--font-geist-sans),Arial,sans-serif;font-size:1.125rem}\r\nbutton,input,textarea,select{font:inherit}button{cursor:pointer}button:disabled{cursor:default;opacity:.45}\r\nbutton:focus-visible,a:focus-visible{outline:3px solid var(--hot);outline-offset:3px}\r\n[data-slot=button]{min-height:2.65rem;font-size:1rem;border-radius:8px}\r\n.creator-shell{min-height:100vh;background:radial-gradient(120% 100% at 50% -15%,#fff7e6 0%,#ffe6b8 30%,#e9b6f0 65%,#b06fe0 100%)}\r\n.mom-ribbon{min-height:28px;padding:4px 18px;background:repeating-linear-gradient(45deg,var(--pur) 0 16px,var(--pur-d) 16px 32px);color:var(--gold);font:400 .875rem/1.4 var(--font-display),Impact,sans-serif;letter-spacing:.12em;text-align:center}\r\n.mom-ribbon span{margin-left:30px}\r\n.topbar{min-height:96px;display:flex;align-items:center;gap:24px;padding:12px 24px 16px;position:relative;z-index:5}\r\n.brand-copy p,.panel-kicker{margin:0;color:var(--pur-d);font:400 .875rem/1.4 var(--font-display),Impact,sans-serif;letter-spacing:.10em}\r\n.brand-copy p{color:#b91958}\r\n.brand-copy h1{margin:6px 0 0;font:400 clamp(1.4rem,2.6vw,2rem)/1.05 var(--font-headline),'Arial Black',sans-serif;letter-spacing:-.025em;text-shadow:2px 2px 0 var(--gold),4px 4px 0 #ae70d4}\r\n.brand-copy sup{font-family:Arial,sans-serif;font-size:.4em;vertical-align:super;text-shadow:none;margin-left:3px}\r\n.five-finger-badge{color:var(--pur-dd);background:var(--gold);border:2px solid var(--pur-dd);padding:7px 10px;transform:rotate(-3deg);white-space:nowrap;font-family:var(--font-display),Impact,sans-serif;font-size:.875rem;box-shadow:3px 3px 0 var(--pur)}\r\n.top-actions{margin-left:auto;display:flex;gap:8px;align-items:center}\r\n.top-actions [data-slot=button]{border:2px solid var(--pur-dd);box-shadow:0 3px 0 var(--pur-dd);font-weight:600}\r\n.top-actions [data-slot=button]:active{transform:translateY(2px);box-shadow:0 1px 0 var(--pur-dd)}\r\n.creator-grid{height:calc(100dvh - 124px);min-height:660px;display:grid;grid-template-columns:274px minmax(320px,1fr) 336px;gap:18px;padding:0 18px 20px}\r\n.region-panel,.style-panel{padding:20px 16px;background:var(--cream);overflow-y:auto;scrollbar-width:thin;scrollbar-color:#a66ad0 #f3e3eb;border:3px solid var(--pur-dd);border-radius:14px;box-shadow:5px 5px 0 var(--pur-dd)}\r\n.region-panel h2{font:400 1.45rem/1.2 var(--font-headline),'Arial Black',sans-serif;margin:10px 0 8px;letter-spacing:-.035em}\r\n.panel-intro{font-size:1rem;line-height:1.3;margin:0 0 20px;color:#654773}\r\n.region-list{display:grid;gap:10px}\r\n.region-card{border:2px solid #d4bde0;border-radius:9px;padding:2px 6px 2px 10px;background:#fffaf1;transition:background .15s}\r\n.region-card.is-active{border-color:var(--pur-dd);background:var(--gold);box-shadow:3px 3px 0 var(--pur)}\r\n.region-card-heading{display:flex;align-items:center;gap:4px}\r\n.region-pick{display:flex;gap:10px;align-items:center;flex:1;text-align:left;min-height:62px;background:none;border:0;padding:3px 0;color:inherit}\r\n.region-index{font:400 1rem var(--font-display),Impact,sans-serif;color:var(--pur);align-self:flex-start;padding-top:8px}\r\n.region-name{display:flex;flex-direction:column;gap:1px}.region-name strong{font-size:1rem;font-weight:700;line-height:1.12}.region-name small{font-size:.875rem;color:#654773}\r\n.region-card-heading [data-slot=button]{color:#796086;min-width:32px;width:32px}.region-card-heading .is-locked{color:var(--pur-dd)}\r\n.recipe-actions{display:flex;gap:8px;margin-top:20px}.recipe-actions>*{flex:1}\r\n.viewport-panel{position:relative;isolation:isolate;overflow:hidden;min-width:0;border:3px solid var(--pur-dd);border-radius:28px;background:radial-gradient(ellipse at 50% 35%,#32243e 0,#17101f 45%,#090610 80%);box-shadow:5px 5px 0 var(--pur-dd),inset 0 0 80px #000a}\r\n.hand-viewer-wrap,.hand-canvas{width:100%;height:100%;min-height:0;position:relative}\r\n.hand-canvas{cursor:grab}.hand-canvas:active{cursor:grabbing}.hand-canvas canvas{width:100%;height:100%;display:block;touch-action:none}\r\n.protective-glass{position:absolute;inset:7px;z-index:1;pointer-events:none;border:8px solid #2b1b33;border-radius:22px;background:linear-gradient(112deg,#fff0b30a,transparent 34%,transparent 72%,#9e6ed007);box-shadow:0 0 0 1px #a0783f,0 0 0 4px #0a0610,inset 1px 2px 2px #fff1b563,inset -2px -2px 3px #080613,inset 0 0 48px #ae7fc01c;overflow:hidden}\r\n.glass-edge{position:absolute;inset:5px;border:1px solid #e5c29638;border-radius:11px;box-shadow:inset 0 0 0 2px #16092175,inset 0 0 24px #ecd8ff0a}\r\n.glass-reflection{position:absolute;inset:-20%;transform:rotate(-24deg);background:linear-gradient(90deg,transparent 13%,#f3e1ff04 14%,#fff8e00d 19%,#fff8e006 25%,transparent 26%,transparent 29%,#fff9ee0a 29.4%,transparent 30%)}\r\n.reflection-secondary{transform:rotate(-24deg) translateX(76%);opacity:.45}\r\n.glass-fastener{position:absolute;width:8px;height:8px;border-radius:50%;background:linear-gradient(130deg,#e4bc72,#302136 55%,#a17948);box-shadow:0 0 0 2px #100a15,inset 0 1px #fff0b666}\r\n.fastener-tl{top:2px;left:2px}.fastener-tr{top:2px;right:2px}.fastener-bl{bottom:2px;left:2px}.fastener-br{bottom:2px;right:2px}\r\n.glass-seal{position:absolute;right:15px;top:20px;font:400 .75rem var(--font-display),Impact,sans-serif;letter-spacing:.1em;color:#d8bc88}\r\n.viewport-chrome{position:absolute;left:30px;top:31px;z-index:2;pointer-events:none}\r\n.live-badge{background:#0b0f0a;color:var(--sig);border:2px solid #000;height:30px;padding:4px 10px;letter-spacing:.08em;font:400 1.05rem var(--font-geist-mono),monospace;box-shadow:inset 0 1px 4px #000}\r\n.live-dot{height:6px;width:6px;border-radius:50%;background:var(--sig);margin-right:4px;box-shadow:0 0 9px #39ff9a66}\r\n.view-tools{position:absolute;left:20px;right:20px;bottom:94px;z-index:2;display:flex;justify-content:center;gap:8px;align-items:center}\r\n.view-tools [data-slot=button],.explode-control{color:var(--cream);background:linear-gradient(#34273e,#18111e);border:2px solid #07040a;box-shadow:inset 0 1px 0 #ffffff26,0 3px 0 #08040c}\r\n.view-tools [data-slot=button]{border-radius:50%}\r\n.explode-control{display:flex;align-items:center;gap:8px;padding:9px 11px;border-radius:8px;font-size:.875rem;line-height:1.2}\r\n.explode-control>svg{height:16px;width:16px}\r\n.explode-control [data-slot=switch][data-checked]{background:var(--sig)}\r\n.explode-control [data-slot=switch-thumb]{background:#fff9ec}\r\n.specimen-label{position:absolute;left:30px;right:30px;bottom:30px;z-index:2;pointer-events:none;text-align:center}\r\n.specimen-label span{display:flex;flex-direction:column;gap:5px}\r\n.specimen-label small{font:400 .75rem var(--font-display),Impact,sans-serif;color:#d9bfeb;letter-spacing:.09em}\r\n.specimen-label strong{font:400 clamp(1rem,1.6vw,1.25rem) var(--font-headline),'Arial Black',sans-serif;color:var(--gold);text-shadow:2px 2px 0 #4a1a86}\r\n.orbit-hint{position:absolute;left:30px;top:63px;color:#bba5c8;font-size:.875rem;pointer-events:none;z-index:2}\r\n.model-status{position:absolute;z-index:3;left:50%;top:50%;transform:translate(-50%,-50%);width:min(85%,380px);padding:16px;background:#100a1af2;color:var(--cream);border:2px solid #8553b0;border-radius:10px;font-size:1rem;text-align:center}\r\n.model-status.is-error{border-color:var(--hot)}.model-status button{display:block;margin:12px auto 0;padding:8px 20px;border-radius:6px;background:var(--gold);color:var(--pur-dd)}\r\n.style-heading h2{margin:9px 0 7px;font:400 1.55rem/1.15 var(--font-headline),'Arial Black',sans-serif;letter-spacing:-.04em;text-shadow:2px 2px 0 var(--gold)}\r\n.style-heading p{color:#654773;font-size:1rem;margin:0}\r\n.slider-block{padding-top:16px}.slider-actions{display:grid;grid-template-columns:38px 1fr 38px;gap:10px;align-items:center;text-align:center}\r\n.slider-actions [data-slot=button]{width:38px;min-height:38px;height:38px;border:2px solid var(--pur-dd);background:#fffaf1}\r\n.selected-style-label{font-size:1rem;font-weight:700;color:var(--pur)}\r\n.style-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;padding:16px 0 20px}\r\n.style-grid button{overflow:hidden;min-width:0;border-radius:7px;background:#f9ebf3;border:2px solid #baa0ce;padding:0;color:var(--pur-dd);text-align:left;transition:transform .15s}\r\n.style-grid button:hover{border-color:var(--pur);transform:translateY(-2px)}.style-grid button.is-active{border-color:var(--pur-dd);background:var(--gold);box-shadow:3px 3px 0 var(--pur)}\r\n.style-grid img{width:100%;height:80px;object-fit:cover;display:block;background:#120c1a}\r\n.tile-label{display:block;padding:5px 3px;font-size:.875rem;font-weight:600;line-height:1.1;overflow-wrap:anywhere;min-height:40px}\r\n.nail-shop{margin:18px 0 0;padding:12px 10px 10px;border:2px solid var(--pur-dd);border-radius:9px;background:#f5e4ee;min-width:0}\r\n.nail-shop legend{font:400 1rem var(--font-display),Impact,sans-serif;padding:0 5px;color:var(--pur)}\r\n.nail-shapes{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}\r\n.nail-shapes button{font-size:.875rem;font-weight:600;line-height:1.1;min-height:38px;padding:6px 3px;border:1px solid #9d79b5;border-radius:5px;background:#fffaf1}\r\n.nail-shapes button.is-active{background:var(--pur);color:var(--cream);border-color:var(--pur-dd);box-shadow:0 2px 0 var(--pur-dd)}\r\n.hand-shape-shop{margin:0 0 22px;padding:12px 10px;border:2px solid var(--pur-dd);border-radius:9px;background:#f5e4ee;min-width:0}\r\n.hand-shape-shop legend{font:400 1rem var(--font-display),Impact,sans-serif;padding:0 5px;color:var(--pur)}\r\n.shape-target{display:flex;align-items:center;gap:8px;margin-bottom:12px;font-size:.875rem;font-weight:600}.shape-target>span{flex-shrink:0}.shape-target>div{flex:1;min-width:0}.shape-target select{width:100%;min-width:0;height:38px;font-size:.875rem;background:#fffaf1;border-color:#9d79b5}\r\n.hand-shape-buttons{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}.hand-shape-buttons [data-slot=button]{min-width:0;min-height:42px;height:auto;padding:7px 3px;font-size:.875rem;font-weight:600;line-height:1.2;white-space:normal;overflow-wrap:anywhere;border:1px solid #9d79b5;border-radius:5px;background:#fffaf1;color:var(--pur-dd)}\r\n.hand-shape-buttons [aria-pressed=true]{background:var(--pur);color:var(--cream);border-color:var(--pur-dd);box-shadow:0 2px 0 var(--pur-dd)}.hand-shape-shop p{margin:10px 0 0;font-size:.875rem;line-height:1.35;color:#654773}\r\n.scale-shop{margin:0 0 22px;padding:12px 10px;border:2px solid var(--pur-dd);border-radius:9px;background:#fff0c7;min-width:0}\r\n.scale-shop legend{font:400 1rem var(--font-display),Impact,sans-serif;padding:0 5px;color:var(--pur)}\r\n.scale-buttons{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}.scale-buttons [data-slot=button]{flex-direction:column;gap:5px;min-width:0;min-height:62px;height:auto;padding:8px 3px;font-size:.875rem;border:1px solid #9d79b5;border-radius:5px;background:#fffaf1;color:var(--pur-dd)}\r\n.scale-buttons [aria-pressed=true]{background:var(--pur);color:var(--cream);border-color:var(--pur-dd);box-shadow:0 2px 0 var(--pur-dd)}.scale-shop p{margin:10px 0 0;font-size:.875rem;line-height:1.35;color:#654773}\r\n.scale-icon{display:block;width:20px;height:20px;background:currentColor;opacity:.9}.scale-none{border:2px solid currentColor;background:linear-gradient(135deg,transparent 46%,currentColor 47% 54%,transparent 55%);border-radius:50%}.scale-round{border-radius:50%}.scale-diamond{clip-path:polygon(50% 0,92% 50%,50% 100%,8% 50%)}.scale-hex{clip-path:polygon(25% 7%,75% 7%,100% 50%,75% 93%,25% 93%,0 50%)}.scale-shield{clip-path:polygon(12% 0,88% 0,100% 45%,50% 100%,0 45%)}\r\n.apply-all{width:100%;font:400 1rem var(--font-display),Impact,sans-serif!important;white-space:normal!important;height:auto!important;line-height:1.35;padding:11px!important;background:var(--gold)!important;border:2px solid var(--pur-dd)!important;box-shadow:0 3px 0 var(--pur-dd)}\r\n.preset-label{display:block;font-size:1rem;margin:20px 0 7px;font-weight:600}.preset-select{width:100%!important}.preset-select select{height:44px;background:#fffaf1;font-size:1rem;color:var(--pur-dd);border:2px solid var(--pur)}\r\n.creator-dialog{background:var(--cream)!important;max-width:min(94vw,520px)!important;max-height:90dvh;overflow:auto;border:3px solid var(--pur-dd)!important;padding:24px!important;box-shadow:7px 7px 0 var(--pur);color:var(--pur-dd)!important}\r\n.creator-dialog [data-slot=dialog-title]{font:400 1.3rem/1.3 var(--font-headline),'Arial Black',sans-serif}.creator-dialog [data-slot=dialog-description]{font-size:1rem;line-height:1.4;color:#654773}\r\n.export-options,.recipe-editor{display:grid;gap:12px}.export-options [data-slot=button],.recipe-editor [data-slot=button]{height:46px;white-space:normal}\r\n.library-download{padding:12px;text-align:center;border:2px solid var(--pur);border-radius:8px;color:var(--pur);font-size:1rem;font-weight:600}\r\n.export-note{font-size:.875rem;line-height:1.4;color:#654773;margin:0}\r\n.recipe-editor label{font-size:1rem}.recipe-editor textarea{font-family:monospace;font-size:.875rem;line-height:1.5;background:#fffaf1}\r\n.feedback-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:100;background:var(--pur-dd);color:var(--gold);padding:14px 20px;border:2px solid var(--gold);border-radius:10px;box-shadow:0 8px 40px #0005;max-width:90vw;font-size:1rem}\r\n@media(max-width:1200px){.creator-grid{grid-template-columns:240px minmax(280px,1fr) 294px;gap:12px;padding:0 12px 16px}.region-panel,.style-panel{padding:18px 12px}.five-finger-badge{display:none}.topbar{gap:12px}.style-grid{gap:6px}.style-grid img{height:72px}.glass-seal{top:59px;right:14px;letter-spacing:.02em}.orbit-hint{top:75px}.explode-control>svg{display:none}}\r\n@media(max-width:980px){\r\n  .topbar{min-height:88px;padding:12px 16px 16px;gap:12px;flex-wrap:wrap}.brand-copy h1{font-size:1.55rem}\r\n  .top-actions{gap:6px}.top-actions button{min-width:38px}.top-actions button span{display:none}.top-actions button svg{width:17px}\r\n  .creator-grid{height:auto;min-height:0;grid-template-columns:1fr 1fr}\r\n  .viewport-panel{grid-column:1/-1;grid-row:1;height:62dvh;min-height:440px}.region-panel,.style-panel{overflow:visible}\r\n  .style-grid img{height:100px}.glass-seal{top:21px;right:16px}.orbit-hint{top:63px}.specimen-label strong{font-size:1.25rem}\r\n}\r\n@media(max-width:600px){\r\n  .mom-ribbon{font-size:.75rem;letter-spacing:.06em}.mom-ribbon span{margin-left:12px}\r\n  .topbar{gap:8px;min-height:96px}.brand-copy{flex:1}.brand-copy h1{font-size:1.3rem}.brand-copy p{font-size:.75rem}\r\n  .top-actions{margin-left:0}.top-actions [data-slot=button]{width:34px;min-width:34px;min-height:36px;height:36px;padding:7px}\r\n  .creator-grid{display:flex;flex-direction:column;padding:0 10px 16px;gap:16px}.viewport-panel{order:0;height:54dvh;min-height:400px}.region-panel{order:1}.style-panel{order:2}\r\n  .region-list{grid-template-columns:1fr 1fr;gap:9px}.region-index{display:none}.region-card{padding:3px 6px 3px 8px}.region-pick{min-height:62px}.region-name strong{font-size:1rem}\r\n  .style-grid{grid-template-columns:repeat(5,minmax(0,1fr))}.style-grid img{height:96px}\r\n  .protective-glass{inset:5px;border-width:6px;border-radius:19px}.viewport-chrome{top:26px;left:24px}.glass-seal{top:18px;right:12px;font-size:.75rem}\r\n  .orbit-hint{top:59px;left:24px}.specimen-label{left:20px;right:20px;bottom:27px}.specimen-label strong{font-size:1.05rem}.specimen-label small{font-size:.75rem;letter-spacing:.025em}.view-tools{bottom:90px}\r\n}\r\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}\r\n.anatomy-link{display:block;padding:11px 12px;margin:0 0 20px;background:#2a0a44;color:#ffd36e;border-radius:8px;font-weight:600;font-size:1rem;line-height:1.3}\r\n.gray-control{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding:10px 12px;border:1px solid #a581bb;border-radius:8px;font-size:1rem}\r\n.size-note{font-size:1rem;line-height:1.4;padding:12px;background:#fff1ca;color:#412c15;border-radius:8px;margin-top:12px}\r\n.anatomy-comparison{min-height:100vh;background:#f2efe8;padding:28px clamp(16px,3vw,44px);color:#26252a}\r\n.comparison-heading{display:flex;align-items:center;justify-content:space-between;gap:20px}.comparison-heading p{margin:0 0 8px;font:400 .875rem var(--font-display);letter-spacing:.08em;color:#655378}.comparison-heading h1{font:400 clamp(1.5rem,3vw,2.3rem)/1.2 var(--font-headline);margin:0}.comparison-heading>a{color:#4a1a86;text-decoration:underline;white-space:nowrap;font-size:1rem}\r\n.comparison-note{font-size:1.125rem;color:#65616c;margin:16px 0 24px}\r\n.comparison-options{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;margin-bottom:20px}.comparison-options .comparison-note{margin:0}.comparison-options .gray-control{margin:0;gap:20px}.comparison-other{display:inline-block;color:#4a1a86;text-decoration:underline;margin-top:24px;font-size:1rem}\r\n.comparison-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px}\r\n.comparison-card{overflow:hidden;background:#fff;border:1px solid #d0cbd4;border-radius:14px}\r\n.comparison-canvas{height:60dvh;min-height:420px;max-height:680px;background:radial-gradient(ellipse at 50% 40%,#303236,#15171b 75%)}\r\n.comparison-caption{padding:18px 20px 22px}.comparison-caption h2{font:400 1.35rem var(--font-headline);margin:0 0 7px}.comparison-caption p{font-size:1rem;line-height:1.4;color:#625d68;margin:0 0 16px;min-height:2.8em}.comparison-caption a{display:block;padding:10px 14px;background:#4a1a86;color:#fff;border-radius:7px;text-align:center;font-size:1rem;font-weight:600}\r\n@media(max-width:880px){.comparison-grid{grid-template-columns:1fr}.comparison-canvas{height:60dvh;min-height:430px}.comparison-heading{align-items:flex-start;flex-direction:column}.comparison-caption p{min-height:0}}\r\n","handborne/source/app/hand-model.ts":"import * as THREE from 'three';\r\nimport {GLTFLoader,type GLTF} from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/examples/jsm/libs/meshopt_decoder.module.js';\r\nimport {REGIONS,STYLES,styleFor} from './catalog';\r\nimport {recipeCode,type Selection} from './recipe';\r\nimport {replaceNailShape} from './nails';\r\nimport {addHandScales} from './scales';\r\nimport {sculptMaterial,growMaterial} from './material-language';\r\nconst files=new Map<number,Promise<GLTF>>();\r\nfunction family(id:number) {\r\n  id=id>=30?(id===57||id===58||id===61||id===63||id===74?21:id===59||id===62?14:0):id;\r\n  if(!files.has(id)) files.set(id,new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync('/handborne/models/family-'+String(id).padStart(2,'0')+'.glb?v=5').catch(error=>{files.delete(id);throw error;}));\r\n  return files.get(id)!;\r\n}\r\nexport async function assemble(selection:Selection,nailShape:string,scalePattern='none') {\r\n  const group=new THREE.Group();group.name='Handborne_Creature';\r\n  group.userData={recipe:recipeCode(selection),units:'metres',fingers:5};\r\n  const parts=await Promise.all(REGIONS.map(async r=>{\r\n    const gltf=await family(selection[r.id]);\r\n    const source=gltf.scene.getObjectByName(r.id);\r\n    if(!source)throw new Error('Missing '+r.label+' module');\r\n    const part=source.clone(true);part.name=r.id;\r\n    if(r.id==='nails')replaceNailShape(part,nailShape,selection.nails);\r\n    part.userData={...part.userData,region:r.id,style:styleFor(selection[r.id]).name,style_id:selection[r.id]};\r\n    part.updateMatrixWorld(true);\r\n    part.traverse(obj=>{if(obj instanceof THREE.Mesh){obj.userData.region=r.id;obj.castShadow=true;obj.receiveShadow=true;\r\n      if(selection[r.id]===20){const old=obj.material as THREE.MeshStandardMaterial,m=new THREE.MeshPhysicalMaterial();THREE.MeshStandardMaterial.prototype.copy.call(m,old);m.sheen=1;m.sheenRoughness=.9;m.sheenColor.set('#ffe5c9');obj.material=m;obj.userData.ownedMaterial=true;}\r\n      else sculptMaterial(obj,styleFor(selection[r.id]),.075);\r\n    }});\r\n    part.add(growMaterial(part as THREE.Group,styleFor(selection[r.id]),r.id,.075,1,true));\r\n    return part;\r\n  }));\r\n  group.add(...parts);addHandScales(group,scalePattern);return group;\r\n}\r\n","handborne/source/app/hand-shapes.ts":"import { REGIONS, type RegionId } from './catalog.ts';\r\nimport type { Selection } from './recipe.ts';\r\n\r\nexport const HAND_SHAPES = [\r\n  { id: 'original', name: 'Original', styleId: 0 },\r\n  { id: 'skeletal', name: 'Skeletal', styleId: 7 },\r\n  { id: 'mechanical', name: 'Mechanical', styleId: 18 },\r\n  { id: 'stone', name: 'Stone', styleId: 13 },\r\n  { id: 'fluffy', name: 'Fluffy', styleId: 20 },\r\n  { id: 'jelly', name: 'Jelly', styleId: 21 },\r\n  { id: 'baby', name: 'Baby', styleId: 22 },\r\n] as const;\r\nexport type ShapeScope = 'hand' | 'section';\r\n\r\nexport function selectHandShape(selection: Selection, shapeId: string, scope: ShapeScope, region: RegionId, locks: readonly RegionId[]): Selection {\r\n  const shape = HAND_SHAPES.find(item => item.id === shapeId);\r\n  if (!shape) throw new Error('Choose a valid hand shape.');\r\n  if (scope !== 'hand' && scope !== 'section') throw new Error('Choose a valid shape target.');\r\n  if (!REGIONS.some(item => item.id === region)) throw new Error('Choose a valid section.');\r\n  return Object.fromEntries(REGIONS.map(item => [\r\n    item.id,\r\n    (scope === 'hand' || item.id === region) && !locks.includes(item.id) ? shape.styleId : selection[item.id],\r\n  ])) as Selection;\r\n}\r\n\r\nexport function activeHandShape(selection: Selection, scope: ShapeScope, region: RegionId): string | null {\r\n  const values = scope === 'hand' ? Object.values(selection) : [selection[region]];\r\n  return HAND_SHAPES.find(shape => values.every(id => id === shape.styleId))?.id ?? null;\r\n}\r\n","handborne/source/app/hand-viewer.tsx":"'use client';\r\n\r\nimport { assemble } from './hand-model';\r\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\r\nimport * as THREE from 'three';\r\nimport { GLTFLoader, type GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';\r\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';\r\nimport { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';\r\nimport { REGIONS, STYLES, styleFor, type RegionId } from './catalog';\r\nimport { recipeCode, designCode, type Selection } from './recipe';\r\nimport { getPose, mixPoses, type Pose } from './poses';\r\nimport { bindHand, applyHandPose, type HandRig } from './pose-rig';\r\nimport { replaceNailShape } from './nails';\r\nimport { specimenMotion } from './motion';\r\nimport { addHandScales } from './scales';\r\n\r\nexport type HandViewerHandle = { exportFile:(format:'glb'|'obj'|'png')=>Promise<void>; resetView:()=>void; flipView:()=>void; };\r\ntype Props = {selection:Selection;poseId:string;nailShape:string;scalePattern?:string;animate:boolean;gray?:boolean;selectedRegion:RegionId;exploded:boolean;onSelectRegion:(r:RegionId)=>void;onReady?:(code:string)=>void;};\r\nfunction download(blob:Blob,name:string) {\r\n  const url=URL.createObjectURL(blob); const a=document.createElement('a');\r\n  a.href=url;a.download=name;document.body.appendChild(a);a.click();a.remove();\r\n  window.setTimeout(()=>URL.revokeObjectURL(url),3000);\r\n}\r\nfunction disposeDisplay(group:THREE.Group) {\r\n  group.traverse(obj=>{if(obj instanceof THREE.Mesh && obj.userData.displayMaterial) {\r\n    const mats=Array.isArray(obj.material)?obj.material:[obj.material];mats.forEach(m=>m.dispose());\r\n  }});\r\n}\r\n\r\nexport const HandViewer=forwardRef<HandViewerHandle,Props>(function HandViewer({selection,poseId,nailShape,scalePattern='none',animate,gray=false,selectedRegion,exploded,onSelectRegion,onReady},ref) {\r\n  const host=useRef<HTMLDivElement>(null);\r\n  const sceneRef=useRef<THREE.Scene|null>(null),handRef=useRef<THREE.Group|null>(null);\r\n  const floatingRoot=useRef<THREE.Group|null>(null);\r\n  const cameraRef=useRef<THREE.PerspectiveCamera|null>(null),rendererRef=useRef<THREE.WebGLRenderer|null>(null),controlsRef=useRef<OrbitControls|null>(null);\r\n  const current=useRef({selection,poseId,nailShape,scalePattern,animate,gray,selectedRegion,exploded,onSelectRegion,onReady});current.current={selection,poseId,nailShape,scalePattern,animate,gray,selectedRegion,exploded,onSelectRegion,onReady};\r\n  const rigRef=useRef<HandRig|null>(null),loadedStyle=useRef(''),displayedPose=useRef(getPose(poseId));\r\n  const transition=useRef<{from:Pose;to:Pose;start:number;duration:number;last:number}|null>(null);\r\n  const [status,setStatus]=useState('Loading hand…'),[error,setError]=useState(false),[retry,setRetry]=useState(0);\r\n  const selectionKey=recipeCode(selection)+'/'+nailShape+'/'+scalePattern;\r\n\r\n  function decorate() {\r\n    sceneRef.current?.traverse(obj=>{\r\n      if(obj instanceof THREE.Light){obj.userData.originalColor??=obj.color.clone();obj.color.copy(current.current.gray?new THREE.Color('#ffffff'):obj.userData.originalColor);}\r\n      if(obj instanceof THREE.HemisphereLight){obj.userData.originalGroundColor??=obj.groundColor.clone();obj.groundColor.copy(current.current.gray?new THREE.Color('#202020'):obj.userData.originalGroundColor);}\r\n    });\r\n    const hand=handRef.current;if(!hand)return;\r\n    for(const part of hand.children) {\r\n      const region=part.userData.region as RegionId;\r\n      const offsets:Record<RegionId,[number,number,number]>={nails:[0,.018,.014],fingertips:[0,.012,0],fingers:[0,.005,0],palm:[0,0,-.020],back_of_hand:[0,0,.020],wrist:[0,-.020,0]};\r\n      part.position.fromArray(current.current.exploded?offsets[region]:[0,0,0]);\r\n      part.traverse(obj=>{\r\n        if(!(obj instanceof THREE.Mesh))return;\r\n        if(!obj.userData.displayMaterial) {\r\n          obj.userData.sourceMaterials=Array.isArray(obj.material)?obj.material:[obj.material];\r\n          obj.material=Array.isArray(obj.material)?obj.material.map(m=>m.clone()):obj.material.clone();\r\n          obj.userData.displayMaterial=true;\r\n          const m=Array.isArray(obj.material)?obj.material[0]:obj.material;\r\n          if(m instanceof THREE.MeshStandardMaterial)obj.userData.originalEmissive=m.emissive.clone();\r\n        }\r\n        const mats=Array.isArray(obj.material)?obj.material:[obj.material];\r\n        for(const [i,m]of mats.entries())if(m instanceof THREE.MeshStandardMaterial) {\r\n          const source=obj.userData.sourceMaterials[i];m.copy(source);m.onBeforeCompile=source.onBeforeCompile;m.customProgramCacheKey=source.customProgramCacheKey;\r\n          if(current.current.gray){m.color.set('#92979c');m.emissive.set(0);m.map=null;m.normalMap=null;m.roughnessMap=null;m.metalnessMap=null;m.roughness=.66;m.metalness=0;}\r\n          if(current.current.gray&&m instanceof THREE.MeshPhysicalMaterial){m.transmission=0;m.thickness=0;m.clearcoat=0;m.sheen=0;m.iridescence=0;}\r\n          if(!current.current.gray&&region===current.current.selectedRegion)m.emissive.add(new THREE.Color('#081b14'));\r\n          m.needsUpdate=true;\r\n        }\r\n      });\r\n    }\r\n  }\r\n  useEffect(()=>{decorate();},[selectedRegion,exploded,gray]);\r\n  useEffect(()=>{\r\n    if(!rigRef.current)return;\r\n    transition.current={from:displayedPose.current,to:getPose(poseId),start:performance.now(),duration:window.matchMedia('(prefers-reduced-motion: reduce)').matches?0:1100,last:0};\r\n  },[poseId]);\r\n  useEffect(()=>{\r\n    let cancelled=false;setStatus('Loading selected forms…');setError(false);\r\n    assemble(selection,nailShape,scalePattern).then(hand=>{\r\n      if(cancelled||!floatingRoot.current){hand.traverse(o=>{if(o instanceof THREE.Mesh&&o.userData.ownedGeometry)o.geometry.dispose();});return;}\r\n      if(handRef.current){handRef.current.removeFromParent();disposeDisplay(handRef.current);rigRef.current?.dispose();}\r\n      // Bind in rest-space before attaching the independent, moving display parent.\r\n      handRef.current=hand;rigRef.current=bindHand(hand);floatingRoot.current.add(hand);loadedStyle.current=selectionKey;\r\n      displayedPose.current=getPose(current.current.poseId);transition.current=null;applyHandPose(rigRef.current,displayedPose.current);\r\n      decorate();setStatus('');current.current.onReady?.(designCode({sections:selection,pose:current.current.poseId,nailShape,scalePattern}));\r\n    }).catch(()=>{if(!cancelled){setError(true);setStatus('Could not load this hand. Check your connection and retry.');}});\r\n    return ()=>{cancelled=true;};\r\n  },[selectionKey,retry]);\r\n\r\n  useImperativeHandle(ref,()=>({\r\n    async exportFile(format) {\r\n      if(format==='png') {\r\n        const renderer=rendererRef.current,scene=sceneRef.current,camera=cameraRef.current;\r\n        if(!renderer||!scene||!camera)throw new Error('The preview is still loading.');\r\n        renderer.render(scene,camera);\r\n        const blob=await new Promise<Blob|null>(resolve=>renderer.domElement.toBlob(resolve,'image/png'));\r\n        if(!blob)throw new Error('Could not capture the preview.');\r\n        download(blob,'handborne-preview.png');return;\r\n      }\r\n      const chosen={sections:current.current.selection,pose:current.current.poseId,nailShape:current.current.nailShape,scalePattern:current.current.scalePattern};\r\n      const hand=await assemble(chosen.sections,chosen.nailShape,chosen.scalePattern),exportRig=bindHand(hand);applyHandPose(exportRig,getPose(chosen.pose));hand.userData.recipe=designCode(chosen);hand.userData.nail_shape=chosen.nailShape;hand.updateMatrixWorld(true);\r\n      try{\r\n      if(format==='glb') {\r\n        const {GLTFExporter}=await import('three/examples/jsm/exporters/GLTFExporter.js');\r\n        const result=await new GLTFExporter().parseAsync(hand,{binary:true,onlyVisible:true});\r\n        if(!(result instanceof ArrayBuffer))throw new Error('GLB export failed.');\r\n        download(new Blob([result],{type:'model/gltf-binary'}),'handborne-'+chosen.pose+'.glb');\r\n      } else {\r\n        const {OBJExporter}=await import('three/examples/jsm/exporters/OBJExporter.js');\r\n        const {zipSync,strToU8}=await import('three/examples/jsm/libs/fflate.module.js');\r\n        let mtl='# Handborne material library\\n';const seen=new Set<string>();\r\n        hand.traverse(obj=>{\r\n          if(!(obj instanceof THREE.Mesh))return;\r\n          for(const m of (Array.isArray(obj.material)?obj.material:[obj.material]))if(m instanceof THREE.MeshStandardMaterial&&!seen.has(m.name)){\r\n            seen.add(m.name);\r\n            const style=styleFor(m.userData.materialStyle)||STYLES.find(s=>m.name.startsWith(s.name.toLowerCase().replace(/[^a-z0-9]+/g,'-')+'_'));\r\n            const c=m.map&&style?new THREE.Color(style.primary):m.color;\r\n            mtl+='newmtl '+m.name+'\\nKd '+[c.r,c.g,c.b].join(' ')+'\\nKs 0.25 0.25 0.25\\nNs '+Math.round((1-m.roughness)*200)+'\\nd 1\\n\\n';\r\n          }\r\n        });\r\n        const obj='mtllib handborne.mtl\\n'+new OBJExporter().parse(hand);\r\n        const archive=zipSync({'handborne.obj':strToU8(obj),'handborne.mtl':strToU8(mtl),'recipe.json':strToU8(JSON.stringify({version:4,...chosen},null,2)),'README.txt':strToU8('OBJ preserves the selected pose, nail shape, raised scale pattern, anatomical mesh, physical relief and base material colors. Use GLB for embedded albedo and microrelief normal maps. This is a baked static pose, not an animation skeleton. Curved module interfaces remain open; not print-ready.')});\r\n        download(new Blob([archive as unknown as BlobPart],{type:'application/zip'}),'handborne-'+chosen.pose+'-obj.zip');\r\n      }\r\n      }finally{exportRig.dispose();}\r\n    },\r\n    resetView(){const camera=cameraRef.current;if(camera){const fit=Math.max(1,.62/camera.aspect);camera.position.set(.055*fit,.035+.06*fit,.36*fit);}controlsRef.current?.target.set(0,.035,0);controlsRef.current?.update();},\r\n    flipView(){const camera=cameraRef.current;if(camera){camera.position.x*=-1;camera.position.z*=-1;controlsRef.current?.update();}},\r\n  }),[]);\r\n\r\n  useEffect(()=>{\r\n    const mount=host.current;if(!mount)return;\r\n    let renderer:THREE.WebGLRenderer;\r\n    try{renderer=new THREE.WebGLRenderer({antialias:true,alpha:true,preserveDrawingBuffer:true});}\r\n    catch {setError(true);setStatus('3D graphics are unavailable. Try a browser with hardware acceleration.');return;}\r\n    renderer.setPixelRatio(Math.min(window.devicePixelRatio,1.75));renderer.outputColorSpace=THREE.SRGBColorSpace;\r\n    renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.12;\r\n    renderer.shadowMap.enabled=true;renderer.shadowMap.type=THREE.PCFSoftShadowMap;\r\n    mount.appendChild(renderer.domElement);rendererRef.current=renderer;\r\n    const scene=new THREE.Scene();sceneRef.current=scene;\r\n    const floatGroup=new THREE.Group();floatGroup.name='Specimen motion';scene.add(floatGroup);floatingRoot.current=floatGroup;\r\n    const camera=new THREE.PerspectiveCamera(34,1,.005,5);camera.position.set(.055,.095,.36);cameraRef.current=camera;\r\n    const controls=new OrbitControls(camera,renderer.domElement);controls.target.set(0,.035,0);controls.enableDamping=true;\r\n    controls.enablePan=false;controls.minDistance=.20;controls.maxDistance=.95;controlsRef.current=controls;\r\n    const pmrem=new THREE.PMREMGenerator(renderer);const room=new RoomEnvironment();const env=pmrem.fromScene(room,.04);scene.environment=env.texture;\r\n    scene.environmentIntensity=.55;\r\n    scene.add(new THREE.HemisphereLight('#c6e8ef','#15212b',.65));\r\n    const key=new THREE.DirectionalLight('#ffead6',3.2);key.position.set(.22,.25,.30);key.castShadow=true;\r\n    key.shadow.mapSize.set(1024,1024);key.shadow.camera.left=-.18;key.shadow.camera.right=.18;key.shadow.camera.top=.25;key.shadow.camera.bottom=-.16;key.shadow.camera.near=.01;key.shadow.camera.far=1;key.shadow.bias=-.00005;key.shadow.normalBias=.0006;key.shadow.radius=3;scene.add(key);\r\n    const rim=new THREE.DirectionalLight('#9adeee',2.1);rim.position.set(-.28,.16,-.16);scene.add(rim);\r\n    const fill=new THREE.DirectionalLight('#abc3e9',.65);fill.position.set(-.3,.03,.2);scene.add(fill);\r\n    const chamber=new THREE.Group();chamber.name='Observation chamber';scene.add(chamber);\r\n    const platform=new THREE.Mesh(new THREE.CylinderGeometry(.105,.11,.012,80),new THREE.MeshStandardMaterial({color:'#17242d',metalness:.75,roughness:.28}));\r\n    platform.position.y=-.083;platform.receiveShadow=true;chamber.add(platform);\r\n    const ring=new THREE.Mesh(new THREE.TorusGeometry(.101,.00065,8,100),new THREE.MeshBasicMaterial({color:'#ffd36e'}));\r\n    ring.rotation.x=Math.PI/2;ring.position.y=-.076;chamber.add(ring);\r\n    const floor=new THREE.Mesh(new THREE.PlaneGeometry(.9,.9),new THREE.MeshStandardMaterial({color:'#09151c',roughness:.54,metalness:.25}));\r\n    floor.rotation.x=-Math.PI/2;floor.position.y=-.09;floor.receiveShadow=true;chamber.add(floor);\r\n    let start=[0,0];const down=(e:PointerEvent)=>{start=[e.clientX,e.clientY];};\r\n    const up=(e:PointerEvent)=>{\r\n      if(!handRef.current||Math.hypot(e.clientX-start[0],e.clientY-start[1])>6)return;\r\n      const rect=renderer.domElement.getBoundingClientRect();const point=new THREE.Vector2((e.clientX-rect.left)/rect.width*2-1,-(e.clientY-rect.top)/rect.height*2+1);\r\n      const ray=new THREE.Raycaster();ray.setFromCamera(point,camera);\r\n      const hit=ray.intersectObject(handRef.current,true)[0];if(hit?.object.userData.region)current.current.onSelectRegion(hit.object.userData.region);\r\n    };\r\n    renderer.domElement.addEventListener('pointerdown',down);renderer.domElement.addEventListener('pointerup',up);\r\n    let firstSize=true;\r\n    const resize=()=>{const w=mount.clientWidth,h=mount.clientHeight;renderer.setSize(w,h,false);camera.aspect=w/Math.max(h,1);camera.updateProjectionMatrix();if(firstSize&&w>0&&h>0){const fit=Math.max(1,.62/camera.aspect);camera.position.set(.055*fit,.035+.06*fit,.36*fit);firstSize=false;}};\r\n    const observer=new ResizeObserver(resize);observer.observe(mount);resize();\r\n    let frame=0,motionTime=0,lastTime=performance.now();const tick=()=>{\r\n      const change=transition.current,now=performance.now();\r\n      if(current.current.animate&&!document.hidden)motionTime+=Math.min((now-lastTime)/1000,.05);lastTime=now;\r\n      const motion=specimenMotion(motionTime);floatGroup.position.fromArray(motion.position);floatGroup.rotation.set(...motion.rotation);\r\n      if(change&&rigRef.current&&(now-change.last>28||change.duration===0)){\r\n        const t=change.duration===0?1:Math.min(1,(now-change.start)/change.duration);change.last=now;\r\n        displayedPose.current=mixPoses(change.from,change.to,t*t*(3-2*t));applyHandPose(rigRef.current,displayedPose.current);\r\n        if(t===1){transition.current=null;if(loadedStyle.current===recipeCode(current.current.selection)+'/'+current.current.nailShape+'/'+current.current.scalePattern)current.current.onReady?.(designCode({sections:current.current.selection,pose:change.to.id,nailShape:current.current.nailShape,scalePattern:current.current.scalePattern}));}\r\n      }\r\n      controls.update();renderer.render(scene,camera);frame=requestAnimationFrame(tick);\r\n    };tick();\r\n    return ()=>{\r\n      cancelAnimationFrame(frame);observer.disconnect();controls.dispose();env.dispose();pmrem.dispose();room.dispose();\r\n      chamber.traverse(obj=>{if(obj instanceof THREE.Mesh){obj.geometry.dispose();for(const material of(Array.isArray(obj.material)?obj.material:[obj.material]))material.dispose();}});key.shadow.dispose();if(handRef.current)disposeDisplay(handRef.current);rigRef.current?.dispose();rigRef.current=null;\r\n      renderer.dispose();if(renderer.domElement.parentElement===mount)mount.removeChild(renderer.domElement);\r\n      sceneRef.current=null;rendererRef.current=null;floatingRoot.current=null;\r\n    };\r\n  },[]);\r\n  return <div className=\"hand-viewer-wrap\"><div ref={host} className=\"hand-canvas\" role=\"img\" aria-label=\"Five-finger 3D hand. Drag to rotate, scroll to zoom, or tap a section.\" />\r\n    {status&&<div className={'model-status '+(error?'is-error':'')} role=\"status\">{status}{error&&<button onClick={()=>setRetry(v=>v+1)}>Retry</button>}</div>}\r\n  </div>;\r\n});\r\n","handborne/source/app/layout.tsx":"import type { Metadata } from 'next';\r\nimport './globals.css';\r\n\r\nexport const metadata: Metadata = {\r\n  title: 'Helping Hand — The Custom Shop',\r\n  description:\r\n    'Customize your Helping Hand with twenty-three creature styles, six joined sections and eight nail shapes. Your hand. Your way.',\r\n};\r\n\r\nexport default function RootLayout({\r\n  children,\r\n}: Readonly<{\r\n  children: React.ReactNode;\r\n}>) {\r\n  return (\r\n    <html lang=\"en\">\r\n      <body\r\n        className=\"antialiased\"\r\n      >\r\n        {children}\r\n      </body>\r\n    </html>\r\n  );\r\n}\r\n","handborne/source/app/material-access.ts":"import {DEFAULT_SELECTION,type RegionId,styleFor} from './catalog';\r\nimport type {Selection} from './recipe';\r\nconst owned=new Set<number>();\r\nexport const isHandStyleUnlocked=(id:number)=>id<57||owned.has(id);\r\nexport const assertHandStylesUnlocked=(sections:Selection)=>{for(const id of Object.values(sections))if(!isHandStyleUnlocked(id))throw Error(styleFor(id).name+' is locked. Earn its texture and color in Coach packs.');};\r\nexport const availableHandSections=(sections:Selection):Selection=>Object.fromEntries(Object.entries(sections).map(([region,id])=>[region,isHandStyleUnlocked(id)?id:DEFAULT_SELECTION[region as RegionId]])) as Selection;\r\nexport function subscribeHandUnlocks(changed:()=>void){\r\n const origin='https://myr5.mominc.online',frame=document.createElement('iframe');\r\n frame.src=origin+'/handborne/unlocks.html';frame.hidden=true;frame.title='Coach material ownership';\r\n const request=()=>frame.contentWindow?.postMessage({type:'handborne:unlock-request'},origin);\r\n const receive=(event:MessageEvent)=>{if(event.origin!==origin||event.source!==frame.contentWindow||event.data?.type!=='handborne:unlock-state'||!Array.isArray(event.data.styles))return;\r\n  owned.clear();for(const id of event.data.styles)if(Number.isInteger(id)&&id>=57&&id<=74)owned.add(id);changed();};\r\n frame.addEventListener('load',request);window.addEventListener('message',receive);window.addEventListener('focus',request);document.body.appendChild(frame);\r\n return()=>{window.removeEventListener('message',receive);window.removeEventListener('focus',request);frame.remove();owned.clear();};\r\n}\r\n","handborne/source/app/material-fixture.ts":"export * as THREE from 'three';\r\nexport {assemble} from './hand-model';\r\nexport {REGIONS,STYLES} from './catalog';\r\nexport {bindHand,applyHandPose} from './pose-rig';\r\nexport {getPose} from './poses';\r\nexport {DEFAULT_NAIL_SHAPE} from './nails';\r\n","handborne/source/app/material-language.ts":"import * as T from 'three';\r\nimport {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils.js';\r\nimport {builtinSurfaceProfile,sampleBuiltinSurface} from './material-patterns';\r\nimport {refineCoachGeometry,type CoachReliefBudget} from './material-refinement';\r\nimport {samplePaletteTint,applyPaletteFinish} from './palette-finishes';\r\n\r\n// This material language is shared verbatim by Coach and Helping Hand. Maps are\r\n// ordinary glTF-compatible PBR textures, so the appearance also survives export.\r\nexport const MATERIAL_REVISION='material-horror-robot-2026-10-05-r1';\r\nexport const MATERIAL_NOTES=[\r\n 'Fine pores, palm folds and soft satin skin.',\r\n 'Twisted roots, bark fissures and fresh leaf growth.',\r\n 'Soft mushroom caps, radial gills and a branching mycelium web.',\r\n 'Overlapping lacquered carapace plates with dark flexible seams.',\r\n 'Raised overlapping scales, belly scutes and a serrated ridge.',\r\n 'Deep ocean hide, swept fins and luminous sensory pearls.',\r\n 'Porous reef limestone, branching antlers and hollow polyps.',\r\n 'Weathered ivory, joint ends and hollow skeletal anatomy.',\r\n 'Translucent mist glass with flowing veils and a pale inner glow.',\r\n 'Charred hide, swept horns and ember-lit fractures.',\r\n 'Pearlescent ceramic, engraved gold and orbiting halo segments.',\r\n 'Obsidian shells with violet fracture edges and star-like inclusions.',\r\n 'Folded flesh, coiled tendrils and luminous sensory nodules.',\r\n 'Angular stone slabs, chipped strata and deep mineral seams.',\r\n 'Faceted amethyst clusters with reflective faces and mineral inclusions.',\r\n 'Black basalt crust over glowing orange lava fissures.',\r\n 'Translucent blue ice, frost veins and sharp icicle clusters.',\r\n 'Storm-dark armor with branching electric channels and conductor fins.',\r\n 'Rigid brass armor, steel hinge joints, rivets and exposed toothed gears.',\r\n 'Machined violet panels, recessed circuits and pink illuminated rails.',\r\n 'Dense tapered fur tufts, a soft undercoat and contrasting guard hairs.',\r\n 'Translucent gelatin, a wet clear coat and bubbles suspended inside.',\r\n 'Plump soft forms, warm blush and delicate skin folds.'\r\n] as const;\r\ntype Style={id:number;primary:string;secondary:string;accent:string;emissive:string;roughness:number;metalness:number;detail:string;paletteId?:string};\r\nconst clamp=T.MathUtils.clamp,fract=(x:number)=>x-Math.floor(x);\r\nconst hash=(x:number,y:number)=>fract(Math.sin(x*127.1+y*311.7)*43758.5453);\r\nfunction cells(x:number,y:number){\r\n const ix=Math.floor(x),iy=Math.floor(y);let a=10,b=10;\r\n for(let j=-1;j<=1;j++)for(let i=-1;i<=1;i++){\r\n  const px=ix+i+.2+.6*hash(ix+i,iy+j),py=iy+j+.2+.6*hash(iy+j,ix+i+19);\r\n  const d=Math.hypot(x-px,y-py);if(d<a){b=a;a=d;}else if(d<b)b=d;\r\n }\r\n return {pit:a,seam:b-a};\r\n}\r\nexport function surfaceSample(id:number,x:number,y:number){\r\n const builtIn=sampleBuiltinSurface(id,x,y);if(builtIn)return builtIn;\r\n const c=cells(x*5,y*5),grain=hash(Math.floor(x*180),Math.floor(y*180));\r\n const wave=Math.sin(x*18+Math.sin(y*9)*2),vein=Math.pow(1-Math.abs(Math.sin(x*12+Math.sin(y*7)*2)),9);\r\n const seam=1-T.MathUtils.smoothstep(c.seam,.025,.12),spot=1-T.MathUtils.smoothstep(c.pit,.06,.19);\r\n const sx=fract(x*10+(Math.floor(y*12)%2)*.5)-.5,sy=fract(y*12)-.5;\r\n const scale=clamp(1-Math.pow(Math.abs(sx)*1.9,3)-Math.pow(Math.abs(sy)*1.9,4),0,1);\r\n let h=.5,t=.5,glow=0,rough=.7;\r\n switch(id){\r\n  case 0:h=.42+grain*.12+wave*.015;t=.54+grain*.07;rough=.61+grain*.2;break;\r\n  case 1:h=.4+wave*.19+vein*.25;t=.28+vein*.42+grain*.13;rough=.91;break;\r\n  case 2:h=.6-spot*.3+Math.sin(Math.atan2(sy,sx)*24)*.04;t=.7-spot*.4;glow=spot*.16;rough=.87;break;\r\n  case 3:h=scale*.75;t=.22+scale*.65;rough=.18+(.9-scale)*.4;break;\r\n  case 4:h=scale*.6+grain*.12;t=.22+scale*.52+wave*.08;rough=.56+grain*.2;break;\r\n  case 5:h=.55+wave*.12-spot*.23;t=.15+spot*.8;glow=spot;rough=.32;break;\r\n  case 6:h=.75-spot*.65-grain*.12;t=.44+spot*.4+grain*.1;rough=.93;break;\r\n  case 7:h=.55+Math.sin(y*35)*.09-grain*.12-seam*.06;t=.75-grain*.2-seam*.35;rough=.83;break;\r\n  case 8:h=.5+wave*.035;t=.7+wave*.12;glow=vein*.35;rough=.12;break;\r\n  case 9:h=.48+wave*.14+grain*.12;t=.15+grain*.13+vein*.3;glow=vein*.4;rough=.83;break;\r\n  case 10:h=.55+vein*.08;t=.74+vein*.23;glow=vein*.22;rough=.18+grain*.07;break;\r\n  case 11:h=.55-seam*.3;t=.03+seam*.48;glow=seam*.8;rough=.14;break;\r\n  case 12:h=.5+wave*.19+spot*.18;t=.25+spot*.65;glow=spot*.4;rough=.39;break;\r\n  case 13:h=.62-seam*.52+Math.floor(grain*4)*.035;t=.32+grain*.2-seam*.26;rough=.98;break;\r\n  case 14:h=.45+Math.floor(c.pit*8)*.05;t=.32+c.pit*.45;rough=.09;break;\r\n  case 15:{const lava=cells(x*2+Math.sin(y*5)*.3,y*2+Math.sin(x*4)*.3),crack=1-T.MathUtils.smoothstep(lava.seam,.015,.065);h=.72-crack*.65+grain*.1;t=.015+crack*.88;glow=crack;rough=.95-crack*.7;break;}\r\n  case 16:h=.5+seam*.14;t=.72+seam*.22;rough=.13+seam*.48;break;\r\n  case 17:{const bolt=Math.pow(1-Math.abs(Math.sin(x*7+Math.sin(y*22)*.25+Math.sin(y*9))),24);h=.54-bolt*.2;t=.13+bolt*.82;glow=bolt;rough=.38;break;}\r\n  case 18:{const cut=Math.min(fract(x*5),fract(y*5));h=cut<.06?.12:.68+grain*.03;t=cut<.06?.05:.43+grain*.16;rough=.24+grain*.25;break;}\r\n  case 19:{const a=fract(x*6),b=fract(y*6),switcher=hash(Math.floor(x*6),Math.floor(y*6));const rail=(Math.abs(a-.5)<.025&&b>.2&&switcher>.35)||(Math.abs(b-.5)<.025&&a<.6&&switcher<.65)?1:0;h=.5-rail*.2;t=.08+rail*.9;glow=rail;rough=.25;break;}\r\n  case 20:h=.5+Math.sin(x*160+Math.sin(y*25))* .2;t=.58+grain*.2;rough=.98;break;\r\n  case 21:h=.5+wave*.018;t=.7+wave*.07;rough=.07;break;\r\n  case 22:h=.5+grain*.035+wave*.01;t=.67+wave*.02;rough=.51;break;\r\n  // Rank 4 additions: Flat (no mask - literally constant) and Clay (soft procedural noise,\r\n  // PLAN §6.1) so the runtime tint system has an always-unlocked, no-download starting pair.\r\n  case 30:h=.5;t=.5;rough=.55;break;\r\n  case 31:h=.5+grain*.05+wave*.008;t=.5+grain*.03;rough=.92;break;\r\n }\r\n return {height:h,tint:clamp(t,0,1),glow,rough};\r\n}\r\nconst maps=new Map<string,{map:T.DataTexture;bump:T.DataTexture;rough:T.DataTexture;glow:T.DataTexture}>();\r\nfunction materialMaps(style:Style){\r\n const key=[MATERIAL_REVISION,style.id,style.primary,style.secondary,style.accent,style.paletteId??''].join('|');if(maps.has(key))return maps.get(key)!;\r\n const size=192,buffers=Array.from({length:4},()=>new Uint8Array(size*size*4));\r\n const dark=new T.Color(style.secondary),base=new T.Color(style.primary),light=new T.Color(style.accent),color=new T.Color(),profile=builtinSurfaceProfile(style.id);\r\n for(let y=0;y<size;y++)for(let x=0;x<size;x++){\r\n  const s=surfaceSample(style.id,x/size,y/size),i=(y*size+x)*4;\r\n  const tint=samplePaletteTint(style.paletteId,s.tint,x/size,y/size,style.id===30); // Palette stops follow the existing texture tones; watercolor/marbled add a spatial finish tint.\r\n  color.copy(tint<.5?dark:base).lerp(tint<.5?base:light,tint<.5?tint*2:(tint-.5)*2);\r\n  const roughnessMap=profile?clamp(.9+(s.rough-profile.roughness)*.75,0,1):s.rough;\r\n  for(let k=0;k<3;k++){buffers[0][i+k]=Math.round([color.r,color.g,color.b][k]*255);buffers[1][i+k]=s.height*255;buffers[2][i+k]=roughnessMap*255;buffers[3][i+k]=s.glow*255;}\r\n  for(const b of buffers)b[i+3]=255;\r\n }\r\n const textures=buffers.map(bytes=>{const tex=new T.DataTexture(bytes,size,size,T.RGBAFormat);tex.wrapS=tex.wrapT=T.RepeatWrapping;tex.magFilter=T.LinearFilter;tex.minFilter=T.LinearMipmapLinearFilter;tex.generateMipmaps=true;tex.needsUpdate=true;return tex;});\r\n const result={map:textures[0],bump:textures[1],rough:textures[2],glow:textures[3]};maps.set(key,result);return result;\r\n}\r\n/** Colour-only path for irises and other parts whose geometry must stay intact. */\r\nexport function applyPaletteSurface(material:T.MeshStandardMaterial,palette:{primary:string;secondary:string;accent:string;paletteId?:string;id?:number}){\r\n if(!palette.paletteId)return;\r\n material.map=materialMaps({...palette,id:palette.id??30,emissive:'#000000',roughness:.55,metalness:0,detail:'palette'}).map;\r\n material.color.set('white');material.needsUpdate=true;\r\n applyPaletteFinish(material as T.MeshPhysicalMaterial,palette.paletteId,palette);\r\n}\r\nexport function materialFor(style:Style,unit:number,original?:T.MeshStandardMaterial){\r\n const tex=materialMaps(style),id=style.id;\r\n const profile=builtinSurfaceProfile(id);\r\n const mat=new T.MeshPhysicalMaterial({name:style.detail+' · '+MATERIAL_REVISION,color:'white',map:tex.map,bumpMap:tex.bump,bumpScale:unit*(profile?.bump??([8,21,22].includes(id)?.0005:.007)),roughnessMap:tex.rough,roughness:profile?.roughness??1,metalness:style.metalness,vertexColors:true,envMapIntensity:[18,19,14].includes(id)?1.3:1});\r\n if(original){mat.side=original.side;mat.polygonOffset=original.polygonOffset;mat.polygonOffsetFactor=original.polygonOffsetFactor;}\r\n if([3,4,10,11,14,18,19,21].includes(id)){mat.clearcoat=id===21?1:.6;mat.clearcoatRoughness=id===21?.055:.19;}\r\n if([8,14,16,21].includes(id)){\r\n  mat.transmission=id===21?.8:id===8?.62:id===16?.54:.28;mat.thickness=unit*(id===21?.36:.16);mat.ior=id===21?1.36:id===16?1.31:1.46;\r\n  mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*(id===21?1.4:2);mat.metalness=0;mat.roughnessMap=null;mat.roughness=id===21?.075:id===16?.16:.12;\r\n }\r\n if([5,8,9,10,11,12,15,17,19].includes(id)){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=id===15?2.1:id===17?1.5:.85;}\r\n if(id===20){mat.sheen=1;mat.sheenRoughness=.96;mat.sheenColor.set(style.accent);mat.metalness=0;}\r\n if(id===0||id===22){mat.sheen=.22;mat.sheenColor.set('#ffc9b1');}\r\n if(profile){mat.sheen=profile.sheen;mat.sheenRoughness=profile.sheenRoughness;mat.sheenColor.set(style.accent);mat.clearcoat=Math.max(mat.clearcoat,profile.clearcoat);mat.clearcoatRoughness=profile.clearcoatRoughness;}\r\n if(id>=57&&id<=62){\r\n  mat.envMapIntensity=1.65;mat.iridescence=id===60?1:id===57?.85:.3;\r\n  mat.iridescenceThicknessRange=id===60?[100,750]:[140,480];\r\n  if([57,58,59,61].includes(id)){\r\n   mat.transmission=id===58?.97:id===59?.84:.88;mat.thickness=unit*(id===59?.32:.48);\r\n   mat.ior=id===59?1.8:id===58?1.46:1.38;mat.metalness=0;\r\n   mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*3.5;\r\n   mat.roughnessMap=null;mat.roughness=profile!.roughness;\r\n   mat.dispersion=id===59?.75:.2;\r\n  }\r\n  if(id===62){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=.65;}\r\n }\r\n if(id===63||id===74){mat.transmission=id===63?.48:.82;mat.thickness=unit*.32;mat.ior=id===63?1.33:1.52;mat.metalness=0;mat.attenuationColor.set(style.primary);mat.attenuationDistance=unit*2.4;mat.roughnessMap=null;mat.roughness=profile!.roughness;}\r\n if([69,70,74].includes(id)){mat.emissive.set(style.accent);mat.emissiveMap=tex.glow;mat.emissiveIntensity=id===74?1.1:.75;}\r\n mat.flatShading=[13,14,59].includes(id);mat.userData.materialStyle=id;\r\n const optics={transmission:mat.transmission,thickness:mat.thickness,ior:mat.ior,roughness:mat.roughness,metalness:mat.metalness,iridescence:mat.iridescence,iridescenceThicknessRange:mat.iridescenceThicknessRange};\r\n applyPaletteFinish(mat,style.paletteId,style);\r\n // Color finishes coat a clear volume; they must not erase the chosen jelly/glass texture.\r\n if(style.paletteId&&optics.transmission>0){Object.assign(mat,optics);mat.clearcoat=1;mat.clearcoatRoughness=.035;}\r\n if(id===60){mat.metalness=.88;mat.iridescence=1;mat.iridescenceThicknessRange=[100,750];mat.roughness=.16;}\r\n if(id===61||id===62)applySparkle(mat,.85);\r\n return mat;\r\n}\r\n\r\n// Sparkle flakes stay attached to the UV surface and flash as the viewing angle changes.\r\nexport function applySparkle(mat:T.MeshPhysicalMaterial,sparkle:number){\r\n const amount=clamp(sparkle,0,1);if(amount<=0||mat.userData.myr5FinishGlitter)return;\r\n if(mat.userData.myr5Sparkle)return;mat.userData.myr5Sparkle=amount;\r\n const previous=mat.onBeforeCompile;(mat as T.MeshPhysicalMaterial & {defines?:Record<string,string>}).defines={...(mat as T.MeshPhysicalMaterial & {defines?:Record<string,string>}).defines,USE_UV:''};\r\n mat.onBeforeCompile=(shader,renderer)=>{\r\n  previous?.call(mat,shader,renderer);\r\n  shader.uniforms.uSparkle={value:amount};\r\n  shader.vertexShader=shader.vertexShader.replace('#include <common>','#include <common>\\nvarying vec2 vMyr5SparkleUv;').replace('#include <uv_vertex>','#include <uv_vertex>\\nvMyr5SparkleUv=uv;');\r\n  shader.fragmentShader=shader.fragmentShader\r\n   .replace('#include <common>','uniform float uSparkle;\\nvarying vec2 vMyr5SparkleUv;\\n#include <common>')\r\n   .replace('#include <dithering_fragment>','float mySparkleGrain=fract(sin(dot(floor(vMyr5SparkleUv*83.0),vec2(12.9898,78.233)))*43758.5453);\\nvec3 myFlakeNormal=normalize(normal+vec3(sin(mySparkleGrain*91.0),cos(mySparkleGrain*73.0),0.0)*.55);\\nfloat myGlint=pow(max(dot(myFlakeNormal,normalize(vViewPosition)),0.0),42.0);\\ngl_FragColor.rgb+=step(0.94,mySparkleGrain)*myGlint*uSparkle*2.2;\\n#include <dithering_fragment>');\r\n };\r\n const priorKey=mat.customProgramCacheKey.bind(mat);mat.customProgramCacheKey=()=>priorKey()+'|myr5-sparkle-v3|'+amount.toFixed(3);\r\n mat.needsUpdate=true;\r\n}\r\n\r\nfunction meshWorldSpan(g:T.BufferGeometry,matrix:T.Matrix4){\r\n if(!g.boundingBox)g.computeBoundingBox();\r\n const size=g.boundingBox!.clone().applyMatrix4(matrix).getSize(new T.Vector3());\r\n return Math.max(size.x,size.y,size.z,1e-6);\r\n}\r\n\r\nexport function authoredUvScale(g:T.BufferGeometry,matrix:T.Matrix4){\r\n const uv=g.attributes.uv,p=g.attributes.position,idx=g.index;if(!uv||uv.itemSize<2)return 1.8;\r\n let worldArea=0,uvArea=0;const a=new T.Vector3(),b=new T.Vector3(),c=new T.Vector3(),ab=new T.Vector3(),ac=new T.Vector3();\r\n const count=idx?.count??p.count;\r\n for(let j=0;j+2<count;j+=3){const ia=idx?idx.getX(j):j,ib=idx?idx.getX(j+1):j+1,ic=idx?idx.getX(j+2):j+2;\r\n  a.fromBufferAttribute(p,ia).applyMatrix4(matrix);b.fromBufferAttribute(p,ib).applyMatrix4(matrix);c.fromBufferAttribute(p,ic).applyMatrix4(matrix);\r\n  ab.subVectors(b,a);ac.subVectors(c,a);worldArea+=ab.cross(ac).length()*.5;\r\n  const ux=uv.getX(ib)-uv.getX(ia),uy=uv.getY(ib)-uv.getY(ia),vx=uv.getX(ic)-uv.getX(ia),vy=uv.getY(ic)-uv.getY(ia);uvArea+=Math.abs(ux*vy-uy*vx)*.5;\r\n }\r\n if(!Number.isFinite(worldArea)||!Number.isFinite(uvArea)||worldArea<=1e-10||uvArea<=1e-10)return 1.8;\r\n // Divide physical UV density by the mesh span. A ten-times larger imported\r\n // sculpt should show the same sized pattern, not 10x more tiles that mipmap away.\r\n const span=meshWorldSpan(g,matrix);\r\n return T.MathUtils.clamp(Math.sqrt(worldArea/uvArea)*1.8/Math.max(1,span/2),.5,6);\r\n}\r\n\r\n// R25: a vertex can only carry relief that is coarser than the mesh. Point-sampling a finer pattern\r\n// (Fine Stripe has 40 strands per tile) at sparse roster vertices aliased into lumpy patches, so the\r\n// displacement fades out between a quarter and half of the pattern's finest wavelength; the bump and\r\n// colour maps still draw the full detail. Wavelength = finest axis, Schmitt-triggered against noise.\r\nconst reliefWavelengths=new Map<number,number>();\r\nfunction reliefWavelength(id:number){\r\n let w=reliefWavelengths.get(id);if(w!==undefined)return w;w=Infinity;const n=2048;\r\n for(const axis of [0,1]){const h=new Float32Array(n);let lo=1,hi=0;for(let i=0;i<n;i++){h[i]=surfaceSample(id,axis?.37:i/n,axis?i/n:.37).height;lo=Math.min(lo,h[i]);hi=Math.max(hi,h[i]);}\r\n  const mid=(lo+hi)/2,band=(hi-lo)*.2;let side=0,crossings=0;for(const value of h){const next=value>mid+band?1:value<mid-band?-1:side;if(side&&next!==side)crossings++;side=next;}\r\n  if(crossings)w=Math.min(w,2/crossings);}\r\n reliefWavelengths.set(id,w);return w;\r\n}\r\nfunction meanEdge(g:T.BufferGeometry,matrix:T.Matrix4){\r\n const p=g.attributes.position,idx=g.index,count=idx?.count??p.count,stride=Math.max(1,Math.floor(count/3/4000))*3,a=new T.Vector3(),b=new T.Vector3();let sum=0,edges=0;\r\n for(let j=0;j+2<count;j+=stride)for(let k=0;k<3;k++){a.fromBufferAttribute(p,idx?idx.getX(j+k):j+k).applyMatrix4(matrix);b.fromBufferAttribute(p,idx?idx.getX(j+(k+1)%3):j+(k+1)%3).applyMatrix4(matrix);sum+=a.distanceTo(b);edges++;}\r\n return edges?sum/edges:0;\r\n}\r\nexport function reliefResolve(id:number,uvEdge:number){const w=reliefWavelength(id);return 1-T.MathUtils.smoothstep(uvEdge,w/4,w/2);}\r\n\r\nexport function sculptMaterial(mesh:T.Mesh,style:Style,unit=1,amount=1,coachRelief?:CoachReliefBudget){\r\n if(Array.isArray(mesh.material)||!mesh.geometry.attributes.position)return;\r\n // The higher-density authored-UV path is deliberately limited to known static meshes.\r\n // Keep animated, morph-target, interleaved and unsupported Float16 geometry on the\r\n // established path until their attributes can be refined without changing semantics.\r\n if(coachRelief&&((mesh as T.SkinnedMesh).isSkinnedMesh||mesh.geometry.attributes.skinIndex||mesh.geometry.attributes.skinWeight||Object.values(mesh.geometry.morphAttributes).some(attributes=>attributes.length)||Object.values(mesh.geometry.attributes).some(attribute=>(attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||(attribute as T.BufferAttribute & {isFloat16BufferAttribute?:boolean}).isFloat16BufferAttribute||((globalThis as any).Float16Array&&attribute.array instanceof (globalThis as any).Float16Array))))coachRelief=undefined;\r\n const old=mesh.geometry;\r\n // Keep Original MYR5's established single-pass geometry, UVs and JS-double displacement path exact.\r\n if(!coachRelief){\r\n  const g=old.clone();if(!g.attributes.normal)g.computeVertexNormals();\r\n  const p=g.attributes.position,n=g.attributes.normal,uv=new Float32Array(p.count*2),colors=new Float32Array(p.count*3);\r\n  mesh.updateWorldMatrix(true,false);const normalMatrix=new T.Matrix3().getNormalMatrix(mesh.matrixWorld),inv=mesh.matrixWorld.clone().invert(),point=new T.Vector3(),normal=new T.Vector3();\r\n  const id=style.id,relief=unit*(builtinSurfaceProfile(id)?.relief??([0,8,20,21,22].includes(id)?.0018:id===13?.037:id===18?.018:.019));\r\n  for(let i=0;i<p.count;i++){\r\n   point.fromBufferAttribute(p,i).applyMatrix4(mesh.matrixWorld);normal.fromBufferAttribute(n,i).applyMatrix3(normalMatrix).normalize();\r\n   const x=point.x/unit,y=point.y/unit,z=point.z/unit;\r\n   const u=(Math.abs(normal.z)>.45?x:z)*1.8+.5,v=y*1.8+.5,s=surfaceSample(id,u,v);uv[i*2]=u;uv[i*2+1]=v;\r\n   let edge=1;for(const [sx,sy,sz,r]of mesh.userData.eyeSockets??[])edge*=T.MathUtils.smoothstep(Math.hypot(point.x-sx,point.y-sy,point.z-sz),r*1.04,r*1.35);\r\n   point.addScaledVector(normal,(s.height-.5)*relief*amount*edge).applyMatrix4(inv);p.setXYZ(i,point.x,point.y,point.z);\r\n   const shade=.91+.09*Math.sin(x*3+y*4+z*2);colors.set([shade,shade,shade],i*3);\r\n  }\r\n  g.setAttribute('uv',new T.BufferAttribute(uv,2));g.setAttribute('color',new T.BufferAttribute(colors,3));g.computeVertexNormals();g.computeBoundingBox();g.computeBoundingSphere();\r\n  const oldMat=mesh.material as T.MeshStandardMaterial;mesh.material=materialFor(style,unit,oldMat);mesh.geometry=g;\r\n  if(mesh.userData.ownedGeometry)old.dispose();mesh.userData.ownedGeometry=true;mesh.userData.ownedMaterial=true;return;\r\n }\r\n mesh.updateWorldMatrix(true,false);const worldScale=mesh.matrixWorld.getMaxScaleOnAxis(),refined=refineCoachGeometry(old,coachRelief,worldScale),g=refined?.geometry??old.clone();if(!g.attributes.normal)g.computeVertexNormals();\r\n // Keep imported smoothing while adding only the geometric normal change caused by relief.\r\n // This avoids faceting the source coach while making shallow height fields catch light.\r\n const authoredNormals=new Float32Array(g.attributes.normal.array as ArrayLike<number>);g.computeVertexNormals();const geometricBefore=new Float32Array(g.attributes.normal.array as ArrayLike<number>);g.attributes.normal.array.set(authoredNormals);g.attributes.normal.needsUpdate=true;\r\n const p=g.attributes.position,n=g.attributes.normal,uv=new Float32Array(p.count*2),colors=new Float32Array(p.count*3),worldPositions=new Float32Array(p.count*3),worldOffsets=new Float32Array(p.count*3);\r\n const vertexKeys=coachRelief?new Array<string>(p.count):undefined;\r\n const coordinateUnit=Math.max(unit,meshWorldSpan(g,mesh.matrixWorld)/2);\r\n const resolvable=reliefResolve(style.id,meanEdge(g,mesh.matrixWorld)*1.8/coordinateUnit);\r\n const normalMatrix=new T.Matrix3().getNormalMatrix(mesh.matrixWorld),inv=mesh.matrixWorld.clone().invert(),point=new T.Vector3(),normal=new T.Vector3();\r\n const id=style.id,relief=Math.min(unit*(builtinSurfaceProfile(id)?.relief??([0,8,20,21,22].includes(id)?.0018:id===13?.037:id===18?.018:.019)),meshWorldSpan(g,mesh.matrixWorld)*.015);\r\n const seamOffsets=coachRelief?new Map<string,{x:number;y:number;z:number;count:number}>():undefined,seamKey=(x:number,y:number,z:number)=>`${Math.round(x*1e5)},${Math.round(y*1e5)},${Math.round(z*1e5)}`;\r\n for(let i=0;i<p.count;i++){\r\n  const localX=p.getX(i),localY=p.getY(i),localZ=p.getZ(i);point.set(localX,localY,localZ).applyMatrix4(mesh.matrixWorld);normal.fromBufferAttribute(n,i).applyMatrix3(normalMatrix).normalize();\r\n  const x=point.x/coordinateUnit,y=point.y/coordinateUnit,z=point.z/coordinateUnit;\r\n  // R25: every species uses Original MYR5's object projection. Authored (Tripo atlas) UVs are cut into\r\n  // islands with their own rotation and density, so directional patterns such as Fine Stripe turned\r\n  // into a patchwork across the belly. The projection is fixed on the mesh and periodic for the surfaces.\r\n  const u=(Math.abs(normal.z)>.45?x:z)*1.8+.5,v=y*1.8+.5,s=surfaceSample(id,u,v);\r\n  uv[i*2]=u;uv[i*2+1]=v;worldPositions[i*3]=point.x;worldPositions[i*3+1]=point.y;worldPositions[i*3+2]=point.z;\r\n  let edge=1;for(const [sx,sy,sz,r]of mesh.userData.eyeSockets??[])edge*=T.MathUtils.smoothstep(Math.hypot(point.x-sx,point.y-sy,point.z-sz),r*1.04,r*1.35);\r\n  const offset=(s.height-.5)*relief*amount*edge*resolvable,dx=normal.x*offset,dy=normal.y*offset,dz=normal.z*offset;worldOffsets[i*3]=dx;worldOffsets[i*3+1]=dy;worldOffsets[i*3+2]=dz;\r\n  if(seamOffsets){const key=seamKey(localX,localY,localZ);vertexKeys![i]=key;const group=seamOffsets.get(key)??{x:0,y:0,z:0,count:0};group.x+=dx;group.y+=dy;group.z+=dz;group.count++;seamOffsets.set(key,group);}\r\n  const shade=.91+.09*Math.sin(x*3+y*4+z*2);colors.set([shade,shade,shade],i*3);\r\n }\r\n for(let i=0;i<p.count;i++){\r\n  const x=worldPositions[i*3],y=worldPositions[i*3+1],z=worldPositions[i*3+2];let dx=worldOffsets[i*3],dy=worldOffsets[i*3+1],dz=worldOffsets[i*3+2];\r\n  if(seamOffsets){const group=seamOffsets.get(vertexKeys![i]);if(group&&group.count>1){dx=group.x/group.count;dy=group.y/group.count;dz=group.z/group.count;}}\r\n  point.set(x+dx,y+dy,z+dz).applyMatrix4(inv);p.setXYZ(i,point.x,point.y,point.z);\r\n }\r\n g.computeVertexNormals();const geometricAfter=g.attributes.normal;\r\n for(let i=0;i<p.count;i++){const j=i*3,x=authoredNormals[j]+geometricAfter.array[j]-geometricBefore[j],y=authoredNormals[j+1]+geometricAfter.array[j+1]-geometricBefore[j+1],z=authoredNormals[j+2]+geometricAfter.array[j+2]-geometricBefore[j+2],length=Math.hypot(x,y,z)||1;geometricAfter.setXYZ(i,x/length,y/length,z/length);}\r\n g.setAttribute('uv',new T.BufferAttribute(uv,2));g.setAttribute('color',new T.BufferAttribute(colors,3));g.computeBoundingBox();g.computeBoundingSphere();\r\n const oldMat=mesh.material as T.MeshStandardMaterial;mesh.material=materialFor(style,unit,oldMat);mesh.geometry=g;\r\n if(mesh.userData.ownedGeometry)old.dispose();mesh.userData.ownedGeometry=true;mesh.userData.ownedMaterial=true;\r\n}\r\n\r\ntype Triangle={a:T.Vector3;b:T.Vector3;c:T.Vector3;normal:T.Vector3;total:number};\r\nfunction surfaceTriangles(group:T.Group){\r\n group.updateWorldMatrix(true,true);const inv=group.matrixWorld.clone().invert(),triangles:Triangle[]=[],bounds=new T.Box3();let total=0;\r\n group.traverseVisible(obj=>{if(!(obj instanceof T.Mesh)||obj.userData.materialDetail)return;const g=obj.geometry,p=g.attributes.position,ix=g.index;if(!p)return;\r\n  const matrix=inv.clone().multiply(obj.matrixWorld),normalMatrix=new T.Matrix3().getNormalMatrix(matrix),authored=g.attributes.normal,count=ix?.count??p.count;\r\n  // Surface sampling needs area coverage, not every triangle of the dense fur assets.\r\n  const stride=Math.max(1,Math.ceil(count/24000))*3;\r\n  for(let j=0;j+2<count;j+=stride){const ids=[0,1,2].map(k=>ix?ix.getX(j+k):j+k),[a,b,c]=ids.map(i=>new T.Vector3().fromBufferAttribute(p,i).applyMatrix4(matrix));const cross=b.clone().sub(a).cross(c.clone().sub(a)),area=cross.length()*.5;if(area<1e-10)continue;const normal=authored?ids.reduce((sum,i)=>sum.add(new T.Vector3().fromBufferAttribute(authored,i).applyMatrix3(normalMatrix)),new T.Vector3()).normalize():cross.normalize();if(normal.lengthSq()<1e-8)normal.copy(cross).normalize();total+=area;bounds.expandByPoint(a);bounds.expandByPoint(b);bounds.expandByPoint(c);triangles.push({a,b,c,normal,total});}\r\n });return {triangles,total,bounds};\r\n}\r\nexport function growMaterial(group:T.Group,style:Style,region:string,unit=1,amount=1,hand=false){\r\n const result=new T.Group();result.name='Material sculpture '+style.detail;result.userData.region=region;\r\n if(region==='eye'||region==='nails'||[0,7,22].includes(style.id))return result;\r\n const {triangles,total,bounds}=surfaceTriangles(group);if(!triangles.length)return result;\r\n let seed=style.id*9173+region.length*419;const rand=()=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/4294967296;};\r\n const id=style.id;if(hand&&id===20)return result;\r\n const baseCount=id===20?2100:[3,4].includes(id)?110:id===21?32:42;\r\n const count=Math.round((id>=63?(id===66?60:id===68?22:id===72?12:38):id>=57&&id<=62?(id===61?180:id===58?55:36):baseCount)*(region==='head'||region==='palm'||region==='back_of_hand'?1:region==='body'?.8:.5));\r\n const batches:T.BufferGeometry[][]=[[],[],[]],normal=new T.Vector3(),point=new T.Vector3(),up=new T.Vector3(0,1,0);\r\n const span=bounds.getSize(new T.Vector3()),featureUnit=unit*T.MathUtils.clamp(Math.cbrt(Math.max(1e-6,span.x*span.y*span.z))/2.2,1,2.2);\r\n function add(g:T.BufferGeometry,pos:T.Vector3,q:T.Quaternion,scale:T.Vector3,material=0){g.applyMatrix4(new T.Matrix4().compose(pos,q,scale));if(!g.attributes.uv)g.setAttribute('uv',new T.Float32BufferAttribute(new Float32Array(g.attributes.position.count*2),2));if(g.index){const expanded=g.toNonIndexed();g.dispose();g=expanded;}batches[material].push(g);}\r\n const color=[style.primary,style.accent,style.secondary];\r\n for(let k=0;k<count;k++){\r\n  const pick=rand()*total;let lo=0,hi=triangles.length-1;while(lo<hi){const m=(lo+hi)>>1;if(triangles[m].total<pick)lo=m+1;else hi=m;}\r\n  const {a,b,c}=triangles[lo],u=Math.sqrt(rand()),v=rand();point.copy(a).multiplyScalar(1-u).addScaledVector(b,u*(1-v)).addScaledVector(c,u*v);normal.copy(triangles[lo].normal);\r\n  let socket=false;group.traverse(o=>{for(const [x,y,z,r]of o.userData.eyeSockets??[])if(point.distanceTo(new T.Vector3(x,y,z))<r*1.48)socket=true;});\r\n  if(socket||(!hand&&region==='feet'&&normal.y<-.2))continue;\r\n  const larger=[2,3,9,13,14,16].includes(id)?1.7:1;\r\n  const q=new T.Quaternion().setFromUnitVectors(up,normal),s=featureUnit*amount*(.055+rand()*.075)*larger,center=point.clone(),size=new T.Vector3(s,s,s);\r\n  const geometry=(g:T.BufferGeometry,offset=0,scale=size,mat=0)=>add(g,center.clone().addScaledVector(normal,offset+s*.12),q,scale,mat);\r\n  switch(id){\r\n   case 63:geometry(new T.SphereGeometry(.8,10,7),-s*.3,new T.Vector3(s,s*.6,s),k%3);break;\r\n   case 64:geometry(new T.SphereGeometry(.7,10,7),-s*.12,new T.Vector3(s*.8,s*.65,s*.8),k%3);if(k%3===0)geometry(new T.TorusGeometry(.45,.13,5,10).rotateX(Math.PI/2),s*.05,size,0);break;\r\n   case 65:geometry(new T.DodecahedronGeometry(.6,0),0,new T.Vector3(s,s*.45,s),k%3);geometry(new T.SphereGeometry(.22,6,4),s*.4,size,2);break;\r\n   case 66:{const curve=new T.CatmullRomCurve3([new T.Vector3(-.7,0,0),new T.Vector3(-.25,.35,.25),new T.Vector3(.3,.2,-.15),new T.Vector3(.65,.55,.1)]);geometry(new T.TubeGeometry(curve,7,.15,5,false),s*.06,new T.Vector3(s,s,s),k%3);break;}\r\n   case 67:{const curve=new T.CatmullRomCurve3([new T.Vector3(-.6,0,0),new T.Vector3(-.15,.3,.15),new T.Vector3(.5,.55,-.1)]);geometry(new T.TubeGeometry(curve,6,.12,5,false),s*.1,new T.Vector3(s*.75,s*1.4,s),k%3);break;}\r\n   case 68:{geometry(new T.CylinderGeometry(.48,.38,.045,12),s*.12,size,1);geometry(new T.TorusGeometry(.53,.13,6,12).rotateX(Math.PI/2),s*.17,size,0);for(let t=0;t<6;t++){const a=t*Math.PI/3,offset=new T.Vector3(Math.cos(a)*s*.34,s*.32,Math.sin(a)*s*.34).applyQuaternion(q);add(new T.ConeGeometry(.08,.34,5),center.clone().add(offset),q,size,2);}break;}\r\n   case 69:geometry(new T.BoxGeometry(.65,.13,.85),s*.04,size,0);geometry(new T.BoxGeometry(.08,.05,1.15),s*.14,size,2);geometry(new T.CylinderGeometry(.12,.12,.1,8),s*.18,size,2);break;\r\n   case 70:geometry(new T.BoxGeometry(1.25,.18,.85),s*.1,size,0);geometry(new T.CylinderGeometry(.17,.17,.19,8),s*.27,size,1);geometry(new T.BoxGeometry(.9,.05,.12),s*.25,size,2);break;\r\n   case 71:geometry(new T.CylinderGeometry(.15,.15,1.7,8).rotateZ(Math.PI/2),s*.13,size,0);geometry(new T.BoxGeometry(.2,.11,1.05),s*.12,size,1);break;\r\n   case 72:geometry(new T.BoxGeometry(.8,.1,.5),s*.07,size,1);geometry(new T.CylinderGeometry(.1,.1,.12,6),s*.16,size,0);break;\r\n   case 73:geometry(new T.BoxGeometry(1,.15,1),s*.1,size,0);geometry(new T.BoxGeometry(.08,.025,1.1).rotateY(Math.PI/4),s*.2,size,1);break;\r\n   case 74:geometry(new T.SphereGeometry(.32,8,6),-s*.05,size,2);geometry(new T.TorusGeometry(.45,.07,5,12).rotateX(Math.PI/2),s*.07,size,0);break;\r\n   case 57:geometry(new T.OctahedronGeometry(.55,1),-s*.3,new T.Vector3(s*.6,s*.2,s*.6),k%3);break;\r\n   case 58:geometry(new T.SphereGeometry(.55,10,7),-s*.4,new T.Vector3(s*.45,s*.65,s*.45),k%3);break;\r\n   case 59:case 62:geometry(new T.CylinderGeometry(0,.32,1.7,6),s*.42,size,k%3);break;\r\n   case 61:geometry(new T.OctahedronGeometry(.22),-s*.2,new T.Vector3(s,s*.18,s),k%3);break;\r\n   case 1:{ // Roots and leaves have separate silhouettes, not generic spikes.\r\n    const path=new T.CatmullRomCurve3([new T.Vector3(0,-.1,0),new T.Vector3(.18,.5,0),new T.Vector3(-.15,1.3,.1),new T.Vector3(.35,2,.1)]);\r\n    geometry(new T.TubeGeometry(path,8,.14,5,false),0,size,2);geometry(new T.SphereGeometry(1,8,5),s*1.1,new T.Vector3(s*.55,s*.9,s*.12),1);break;}\r\n   case 2:geometry(new T.CylinderGeometry(.13,.19,1,6),s*.25,new T.Vector3(s,s,s),2);geometry(new T.SphereGeometry(1,12,8,0,Math.PI*2,0,Math.PI/2),s*.8,new T.Vector3(s*.8,s*.38,s*.8),k%3===0?1:0);geometry(new T.ConeGeometry(.73,.12,16),s*.77,size,2);break;\r\n   case 3:geometry(new T.SphereGeometry(1,6,4),s*.08,new T.Vector3(s*.86,s*.26,s*1.3),0);geometry(new T.ConeGeometry(.19,.8,5),s*.34,size,1);break;\r\n   case 4:geometry(new T.SphereGeometry(1,5,3),s*.015,new T.Vector3(s*.68,s*.17,s),k%6===0?1:0);break;\r\n   case 5:geometry(new T.ConeGeometry(.18,2.5,4),s*.6,new T.Vector3(s,s,s*.5),2);geometry(new T.SphereGeometry(.22,8,5),s*1.7,size,1);break;\r\n   case 6:{geometry(new T.CylinderGeometry(.19,.32,1.9,7),s*.55,size,0);for(const sign of [-1,1]){const branchQ=q.clone().multiply(new T.Quaternion().setFromAxisAngle(new T.Vector3(0,0,1),sign*.62));add(new T.CylinderGeometry(.12,.2,1.2,6),center.clone().addScaledVector(normal,s),branchQ,size,1);}geometry(new T.TorusGeometry(.2,.08,5,9),s*1.5,size,2);break;}\r\n   case 8:geometry(new T.SphereGeometry(1,9,6),s*.3,new T.Vector3(s*.3,s*1.9,s*.13),1);break;\r\n   case 9:{const curve=new T.CatmullRomCurve3([new T.Vector3(0,-.15,0),new T.Vector3(.1,.7,0),new T.Vector3(.6,1.5,0)]);const g=new T.TubeGeometry(curve,9,.24,7,false),p=g.attributes.position;for(let j=0;j<p.count;j++){const t=Math.floor(j/8)/9,at=curve.getPointAt(t),v=new T.Vector3().fromBufferAttribute(p,j).sub(at).multiplyScalar(1-t*.97).add(at);p.setXYZ(j,v.x,v.y,v.z);}g.computeVertexNormals();geometry(g,0,size,2);break;}\r\n   case 10:geometry(new T.TorusGeometry(.65,.075,5,18,Math.PI*1.55),s*.3,size,1);geometry(new T.OctahedronGeometry(.35),s*.4,size,0);break;\r\n   case 11:geometry(new T.OctahedronGeometry(1),s*.02,new T.Vector3(s*.7,s*.28,s*.85),2);geometry(new T.OctahedronGeometry(.11),s*.5,size,1);break;\r\n   case 12:geometry(new T.TorusGeometry(.65,.18,7,13,Math.PI*1.8),s*.25,size,0);geometry(new T.SphereGeometry(.28,8,6),s*.4,size,1);break;\r\n   case 13:geometry(new T.DodecahedronGeometry(1,0),s*.05,new T.Vector3(s*1.3,s*.38,s*.9),k%4===0?1:0);break;\r\n   case 14:case 16:geometry(new T.CylinderGeometry(0,.36,2.3,id===14?6:5),s*.72,new T.Vector3(s,s,s),k%3===0?1:0);geometry(new T.CylinderGeometry(.28,.34,.65,6),s*.14,size,2);break;\r\n   case 15:geometry(new T.DodecahedronGeometry(1,0),s*.025,new T.Vector3(s*1.25,s*.22,s),2);break;\r\n   case 17:geometry(new T.BoxGeometry(.12,1.7,.9),s*.45,size,2);geometry(new T.CylinderGeometry(.08,.08,1.4,5),s*.4,size,1);break;\r\n   case 18:{geometry(new T.CylinderGeometry(.76,.88,.18,8),s*.12,size,0);geometry(new T.TorusGeometry(.38,.1,5,14),s*.26,size,2);for(let r=0;r<6;r++){const angle=r*Math.PI/3,offset=new T.Vector3(Math.cos(angle)*s*.63,s*.29,Math.sin(angle)*s*.63).applyQuaternion(q);add(new T.CylinderGeometry(.08,.08,.12,6),center.clone().add(offset),q,size,1);}if(k%3===0)geometry(new T.CylinderGeometry(.46,.46,.23,12),s*.32,size,2);break;}\r\n   case 19:geometry(new T.BoxGeometry(1.1,.19,1.65),s*.06,size,2);geometry(new T.BoxGeometry(.075,.035,1.35),s*.18,size,1);break;\r\n   case 20:{const lean=.3+rand()*.6,curve=new T.CatmullRomCurve3([new T.Vector3(0,-.1,0),new T.Vector3(.15,.6,.1),new T.Vector3(lean,1.3,.25),new T.Vector3(lean*1.4,1.65,.3)]);const g=new T.TubeGeometry(curve,5,.035,3,false),p=g.attributes.position;for(let j=0;j<p.count;j++){const t=Math.floor(j/4)/5,at=curve.getPointAt(t),v=new T.Vector3().fromBufferAttribute(p,j).sub(at).multiplyScalar(1-t*.96).add(at);p.setXYZ(j,v.x,v.y,v.z);}g.computeVertexNormals();geometry(g,0,size,k%4===0?1:k%5===0?2:0);break;}\r\n   case 21:geometry(new T.SphereGeometry(1,10,7),-s*.72,new T.Vector3(s*.3,s*.43,s*.3),k%4===0?1:2);break;\r\n  }\r\n }\r\n batches.forEach((geos,index)=>{if(!geos.length)return;const g=mergeGeometries(geos,false);geos.forEach(g=>g.dispose());if(!g)return;\r\n  const mat=new T.MeshPhysicalMaterial({color:color[index],roughness:[14,16,21].includes(id)?.12:id===20?.95:style.roughness,metalness:style.metalness,clearcoat:[3,18,19,21].includes(id)?.7:0});\r\n  if(index===1&&[5,8,9,11,12,15,17,19].includes(id)){mat.emissive.set(style.accent);mat.emissiveIntensity=1.2;}\r\n  if([8,14,16].includes(id)){mat.transmission=.45;mat.thickness=unit*.15;mat.ior=1.35;mat.metalness=0;}\r\n  if(id>=57&&id<=62){mat.clearcoat=1;mat.clearcoatRoughness=.035;mat.roughness=.08;mat.iridescence=.9;mat.iridescenceThicknessRange=[100,650];\r\n   if(id===58){mat.transmission=.75;mat.thickness=unit*.04;mat.metalness=0;mat.ior=1.1;}\r\n   if(id===57||id===59){mat.transmission=.5;mat.thickness=unit*.08;mat.metalness=0;mat.ior=1.6;}\r\n   if(id===61){mat.metalness=.9;applySparkle(mat,1);}\r\n   if(id===62&&index===1){mat.emissive.set(style.accent);mat.emissiveIntensity=.5;}\r\n  }\r\n  if(id>=63&&id<=68){mat.metalness=0;mat.clearcoat=id===65?0:1;mat.clearcoatRoughness=.055;mat.roughness=id===65?.98:.16;\r\n   if(id===63){mat.transmission=.55;mat.thickness=unit*.08;mat.ior=1.33;}\r\n   if(id===64&&index===2){mat.transmission=.25;mat.thickness=unit*.06;}\r\n  }\r\n  if(id>=69&&id<=74){mat.metalness=id===74?0:style.metalness;mat.clearcoat=.8;mat.clearcoatRoughness=.06;\r\n   if(index===2&&[69,70,74].includes(id)){mat.emissive.set(style.accent);mat.emissiveIntensity=1.3;}\r\n   if(id===74&&index===0){mat.transmission=.75;mat.thickness=unit*.04;mat.roughness=.045;}\r\n  }\r\n  if(id===20){mat.sheen=1;mat.sheenColor.set(style.accent);}\r\n  if(id===21){mat.color.set(index===1?style.accent:'#edffd3');mat.roughness=.12;mat.metalness=0;}\r\n  if(id===18){mat.metalness=.87;mat.color.set(index===2?'#333d48':color[index]);}\r\n  const mesh=new T.Mesh(g,mat);mesh.name=style.detail+' structural details';mesh.userData={materialDetail:true,ownedGeometry:true,ownedMaterial:true,region};result.add(mesh);\r\n });return result;\r\n}\r\n","handborne/source/app/material-patterns.ts":"/** Built-in procedural surface families. These are local math, so previews work offline. */\r\nexport type SurfaceSample={height:number;tint:number;glow:number;rough:number};\r\nexport type SurfaceProfile={id:number;name:string;roughness:number;metalness:number;relief:number;bump:number;sheen:number;sheenRoughness:number;clearcoat:number;clearcoatRoughness:number};\r\n\r\n// Stable ids 32-55 correspond to the Battle Pass catalogue order in materials-registry.ts.\r\nexport const BUILTIN_SURFACE_PROFILES:readonly SurfaceProfile[]=[\r\n {id:32,name:'Plate Steel',roughness:.34,metalness:.82,relief:.024,bump:.012,sheen:0,sheenRoughness:.7,clearcoat:.35,clearcoatRoughness:.24},\r\n {id:33,name:'Rubber Grip',roughness:.95,metalness:0,relief:.055,bump:.022,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:34,name:'Chain Mail',roughness:.41,metalness:.78,relief:.034,bump:.014,sheen:0,sheenRoughness:.7,clearcoat:.3,clearcoatRoughness:.25},\r\n {id:35,name:'Track Rubber',roughness:.94,metalness:0,relief:.038,bump:.016,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:36,name:'Denim',roughness:.86,metalness:0,relief:.012,bump:.011,sheen:.22,sheenRoughness:.96,clearcoat:0,clearcoatRoughness:.5},\r\n {id:37,name:'Hex Tread',roughness:.82,metalness:.04,relief:.052,bump:.014,sheen:0,sheenRoughness:.9,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:38,name:'Sweatshirt Fleece',roughness:.99,metalness:0,relief:.05,bump:.032,sheen:.74,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n {id:39,name:'Quilted',roughness:.89,metalness:0,relief:.07,bump:.012,sheen:.14,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:40,name:'Speckled',roughness:.96,metalness:0,relief:.03,bump:.028,sheen:.34,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n {id:41,name:'Hammered Bronze',roughness:.39,metalness:.78,relief:.025,bump:.025,sheen:0,sheenRoughness:.7,clearcoat:.28,clearcoatRoughness:.22},\r\n {id:42,name:'Fine Stripe',roughness:.91,metalness:0,relief:.042,bump:.012,sheen:.08,sheenRoughness:.92,clearcoat:0,clearcoatRoughness:.5},\r\n {id:43,name:'Snake Skin',roughness:.68,metalness:.02,relief:.019,bump:.023,sheen:.1,sheenRoughness:.76,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:44,name:'Holey',roughness:.96,metalness:0,relief:.039,bump:.025,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},\r\n {id:45,name:'Woven Mat',roughness:.9,metalness:0,relief:.014,bump:.014,sheen:.08,sheenRoughness:.94,clearcoat:0,clearcoatRoughness:.5},\r\n {id:46,name:'Petal',roughness:.51,metalness:0,relief:.025,bump:.009,sheen:.52,sheenRoughness:.72,clearcoat:.12,clearcoatRoughness:.3},\r\n {id:47,name:'Graph Paper',roughness:.93,metalness:0,relief:.012,bump:.017,sheen:.16,sheenRoughness:.97,clearcoat:0,clearcoatRoughness:.5},\r\n {id:48,name:'Bamboo',roughness:.57,metalness:0,relief:.027,bump:.008,sheen:.18,sheenRoughness:.62,clearcoat:.12,clearcoatRoughness:.28},\r\n {id:49,name:'Dragon Scale',roughness:.31,metalness:.16,relief:.043,bump:.01,sheen:.24,sheenRoughness:.42,clearcoat:.72,clearcoatRoughness:.16},\r\n {id:50,name:'Cool Graph Paper',roughness:.56,metalness:.23,relief:.021,bump:.008,sheen:0,sheenRoughness:.8,clearcoat:.12,clearcoatRoughness:.3},\r\n {id:51,name:'Terry Cloth',roughness:.98,metalness:0,relief:.014,bump:.026,sheen:.62,sheenRoughness:.99,clearcoat:0,clearcoatRoughness:.5},\r\n {id:52,name:'Pebble Path',roughness:.77,metalness:0,relief:.038,bump:.018,sheen:0,sheenRoughness:.8,clearcoat:.08,clearcoatRoughness:.4},\r\n {id:53,name:'Wiggles',roughness:.99,metalness:0,relief:.016,bump:.016,sheen:0,sheenRoughness:.95,clearcoat:0,clearcoatRoughness:.5},\r\n {id:54,name:'River Stone',roughness:.72,metalness:.01,relief:.031,bump:.019,sheen:.04,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.32},\r\n {id:55,name:'Moss',roughness:.99,metalness:0,relief:.022,bump:.023,sheen:.38,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},\r\n];\r\nconst pixelProfile:SurfaceProfile={id:56,name:'64-bit Skin',roughness:.58,metalness:.12,relief:.018,bump:.01,sheen:0,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.4};\r\n// Extra finishes use stable additive IDs; existing reward IDs retain their meaning.\r\nexport const SPECIAL_SURFACE_PROFILES:readonly SurfaceProfile[]=[\r\n {id:57,name:'Opal Jelly',roughness:.06,metalness:0,relief:.004,bump:.001,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.035},\r\n {id:58,name:'Bubble Glass',roughness:.035,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},\r\n {id:59,name:'Prism Crystal',roughness:.045,metalness:0,relief:.052,bump:.008,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.03},\r\n {id:60,name:'Holographic Foil',roughness:.18,metalness:.88,relief:.022,bump:.013,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.045},\r\n {id:61,name:'Glitter Resin',roughness:.075,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},\r\n {id:62,name:'Galaxy Geode',roughness:.22,metalness:.25,relief:.065,bump:.014,sheen:0,sheenRoughness:.3,clearcoat:.8,clearcoatRoughness:.06},\r\n {id:63,name:'Caustic Slime',roughness:0.055,metalness:0,relief:0.035,bump:0.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:64,name:'Blister Hide',roughness:0.18,metalness:0,relief:0.065,bump:0.023,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:65,name:'Rotten Rind',roughness:0.94,metalness:0,relief:0.05,bump:0.025,sheen:0,sheenRoughness:.6,clearcoat:0,clearcoatRoughness:.07},\r\n {id:66,name:'Parasite Nest',roughness:0.31,metalness:0,relief:0.07,bump:0.027,sheen:0,sheenRoughness:.6,clearcoat:0.6,clearcoatRoughness:.07},\r\n {id:67,name:'Exposed Sinew',roughness:0.2,metalness:0,relief:0.045,bump:0.017,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:68,name:'Abyssal Maw',roughness:0.26,metalness:0,relief:0.065,bump:0.023,sheen:0,sheenRoughness:.6,clearcoat:0.8,clearcoatRoughness:.07},\r\n {id:69,name:'Circuit Alloy',roughness:0.19,metalness:0.83,relief:0.027,bump:0.01,sheen:0,sheenRoughness:.6,clearcoat:0.6,clearcoatRoughness:.07},\r\n {id:70,name:'Servo Armor',roughness:0.34,metalness:0.82,relief:0.055,bump:0.013,sheen:0,sheenRoughness:.6,clearcoat:0.4,clearcoatRoughness:.07},\r\n {id:71,name:'Chrome Rib',roughness:0.09,metalness:0.96,relief:0.05,bump:0.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n {id:72,name:'Carbon Mech',roughness:0.64,metalness:0.18,relief:0.018,bump:0.013,sheen:0,sheenRoughness:.6,clearcoat:0.25,clearcoatRoughness:.07},\r\n {id:73,name:'Hazard Panel',roughness:0.46,metalness:0.55,relief:0.032,bump:0.014,sheen:0,sheenRoughness:.6,clearcoat:0.4,clearcoatRoughness:.07},\r\n {id:74,name:'Reactor Glass',roughness:0.045,metalness:0,relief:0.018,bump:0.004,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},\r\n];\r\nconst profiles=new Map([...BUILTIN_SURFACE_PROFILES,pixelProfile,...SPECIAL_SURFACE_PROFILES].map(p=>[p.id,p]));\r\nexport const builtinSurfaceProfile=(id:number)=>profiles.get(id);\r\nconst fract=(x:number)=>x-Math.floor(x),clamp=(x:number)=>Math.max(0,Math.min(1,x));\r\nconst hash=(x:number,y:number)=>fract(Math.sin(x*127.1+y*311.7)*43758.5453);\r\nconst smooth=(a:number,b:number,x:number)=>{const t=clamp((x-a)/(b-a));return t*t*(3-2*t);};\r\nconst triangle=(x:number)=>1-Math.abs(fract(x)*2-1);\r\nconst voronoi=(x:number,y:number)=>{const ix=Math.floor(x),iy=Math.floor(y);let near=9,next=9;for(let j=-1;j<=1;j++)for(let i=-1;i<=1;i++){const px=ix+i+.18+.64*hash(ix+i,iy+j),py=iy+j+.18+.64*hash(iy+j,ix+i+19),d=Math.hypot(x-px,y-py);if(d<near){next=near;near=d;}else if(d<next)next=d;}return{near,gap:next-near};};\r\n\r\n/** High-contrast structural masks are paired with their actual material height and roughness maps. */\r\nexport function sampleBuiltinSurface(id:number,x:number,y:number):SurfaceSample|undefined{\r\n if(!profiles.has(id))return undefined;\r\n x=fract(x);y=fract(y); // Geometry relief and RepeatWrapping must sample the same tiled domain.\r\n const g=Math.sin(x*167.3+Math.sin(y*43)*1.7)*Math.sin(y*151.7+Math.sin(x*37))*0.5+0.5;\r\n const line=(v:number,n:number,w:number)=>1-smooth(w,w*2,Math.abs(fract(v*n)-.5));\r\n let h=.5,t=.5,glow=0,rough=.8;\r\n switch(id){\r\n  case 63:{const c=voronoi(x*7,y*7),sac=1-smooth(.04,.44,c.near),ooze=.5+.5*Math.sin(x*20+Math.sin(y*14)*3);h=.32+sac*.49;t=.13+sac*.67+ooze*.12;rough=.055+sac*.045;break;}\r\n  case 64:{const c=voronoi(x*10,y*9),pustule=1-smooth(.08,.36,c.near),rim=1-smooth(.02,.08,Math.abs(c.near-.33));h=.34+pustule*.56-rim*.08;t=.1+pustule*.8;rough=.13+.24*rim;break;}\r\n  case 65:{const c=voronoi(x*11,y*11),rot=1-smooth(.02,.14,c.gap),mould=hash(Math.floor(x*57),Math.floor(y*57))>.7?1:0;h=.56-rot*.4+mould*.12;t=.12+mould*.49+(1-rot)*.22;rough=.86+mould*.13;break;}\r\n  case 66:{const c=voronoi(x*9,y*9),hole=1-smooth(.1,.22,c.near),lip=1-smooth(.045,.1,Math.abs(c.near-.25));h=.53-hole*.42+lip*.18;t=.47-hole*.45+lip*.42;rough=.22+hole*.52;break;}\r\n  case 67:{const strand=.5+.5*Math.sin(x*95+Math.sin(y*13)*5),cleft=Math.pow(1-strand,8);h=.3+strand*.43-cleft*.15;t=.15+strand*.68;rough=.13+cleft*.35;break;}\r\n  case 68:{const c=voronoi(x*6,y*6),mouth=1-smooth(.06,.26,c.near),rim=1-smooth(.02,.07,Math.abs(c.near-.28)),tooth=hash(Math.floor(x*49),Math.floor(y*49))>.76?rim:0;h=.51-mouth*.47+rim*.26;t=.3-mouth*.28+tooth*.67;rough=.16+mouth*.64;break;}\r\n  case 69:{const wire=Math.max(line(x,12,.028),line(y,12,.025)),junction=1-smooth(.025,.065,Math.hypot(fract(x*6)-.5,fract(y*6)-.5));h=.49+wire*.17+junction*.2;t=.12+wire*.68+junction*.17;glow=wire*.8+junction;rough=.22-wire*.1;break;}\r\n  case 70:{const u=fract(x*4),v=fract(y*5),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.07,edge),bolt=1-smooth(.028,.07,Math.hypot(u-.14,v-.14));h=.68-seam*.45+bolt*.25;t=.56-seam*.5+bolt*.4;rough=.32+seam*.42;break;}\r\n  case 71:{const rib=triangle(x*13),groove=1-smooth(.07,.18,rib);h=.32+.57*rib;t=.17+.78*rib;rough=.09+groove*.3;break;}\r\n  case 72:{const u=fract(x*18),v=fract(y*18),over=(Math.floor(x*18)+Math.floor(y*18))%2,thread=over?Math.sin(u*Math.PI):Math.sin(v*Math.PI);h=.44+thread*.14;t=.12+thread*.69;rough=.64+.12*(1-thread);break;}\r\n  case 73:{const stripe=fract((x+y)*7)<.5?1:0,u=fract(x*3),v=fract(y*3),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.07,edge);h=.58-seam*.34;t=.07+stripe*.74-seam*.07;rough=.41+seam*.32;break;}\r\n  case 74:{const cell=voronoi(x*7,y*7),seam=1-smooth(.02,.07,cell.gap),core=1-smooth(.04,.16,cell.near);h=.52-seam*.12;t=.23+seam*.53+core*.23;glow=seam*.7+core;rough=.04+seam*.07;break;}\r\n\r\n  case 57:{const cloud=.5+.5*Math.sin(x*12+Math.sin(y*9)*2)*Math.cos(y*11);h=.48+cloud*.07;t=.48+cloud*.4;rough=.045+cloud*.025;break;}\r\n  case 58:{const c=voronoi(x*9,y*9),bubble=1-smooth(.1,.24,c.near);h=.49+bubble*.13;t=.7+bubble*.28;rough=.025+bubble*.035;break;}\r\n  case 59:{const c=voronoi(x*8,y*8),facet=Math.floor(c.near*7)/7,crack=1-smooth(.012,.06,c.gap);h=.35+facet*.65-crack*.12;t=.3+facet*.65;rough=.035+crack*.16;break;}\r\n  case 60:{const fold=Math.sin(x*31+Math.sin(y*23)*2)*Math.cos(y*27),ribs=triangle((x+y*.4)*38);h=.5+fold*.27+ribs*.06;t=.1+.8*(.5+.5*fold);rough=.12+ribs*.13;break;}\r\n  case 61:{const fleck=hash(Math.floor(x*83),Math.floor(y*83))>.91?1:0;h=.49+fleck*.035;t=.58+fleck*.4;rough=.075-fleck*.045;break;}\r\n  case 62:{const c=voronoi(x*6,y*6),seam=1-smooth(.025,.1,c.gap),star=hash(Math.floor(x*93),Math.floor(y*93))>.989?1:0;h=.65-seam*.43;t=.06+seam*.73+star*.2;glow=seam*.55+star*.4;rough=.32-seam*.2;break;}\r\n\r\n  case 56:{const ix=Math.floor(x*16),iy=Math.floor(y*16),pixel=Math.floor(hash(ix,iy)*4)/3;h=.35+.3*pixel;t=.1+.8*pixel;rough=.5+.15*(1-pixel);break;}\r\n  case 32:{const u=fract(x*3),v=fract(y*3),edge=Math.min(u,1-u,v,1-v),seam=1-smooth(.025,.065,edge),rivet=Math.hypot(u-.5,v-.5),dot=(1-smooth(.035,.075,rivet))*((Math.floor(x*3)+Math.floor(y*3))%2===0?1:.35);h=.69-.34*seam+.28*dot;t=.69-.49*seam+.18*dot;rough=.3+.45*seam;break;}\r\n  case 33:{const d=Math.abs(fract((x+y)*6)-.5),a=Math.abs(fract((x-y)*6)-.5),ridge=Math.max(1-smooth(.045,.12,d),1-smooth(.045,.12,a)),trough=smooth(.16,.34,Math.min(d,a));h=.25+.64*ridge-.1*trough;t=.09+.82*ridge-.08*trough;rough=.99-.16*ridge;break;}\r\n  case 34:{const u=fract(x*7),v=fract(y*7),cx=Math.floor(x*7)+(Math.floor(y*7)%2)*.5+.5,cy=Math.floor(y*7)+.5,r=Math.hypot((x*7-cx)*.72,y*7-cy),ring=1-smooth(.055,.13,Math.abs(r-.31)),hole=1-smooth(.16,.25,r);h=.42+.39*ring-.15*hole;t=.3+.58*ring;rough=.58-.23*ring;void u;void v;break;}\r\n  case 35:{const chevron=triangle((x+y*.48)*9),ridge=1-smooth(.18,.31,Math.abs(chevron-.5));h=.31+.51*ridge;t=.18+.67*ridge;rough=.97-.11*ridge;break;}\r\n  case 36:{const twill=triangle((x+y)*26),cross=triangle((x-y*.12)*5);h=.46+.09*twill+.025*cross;t=.25+.56*twill;rough=.82+g*.12;break;}\r\n  case 37:{const u=fract(x*8),v=fract(y*8),hex=Math.max(Math.abs(u-.5)*.866+Math.abs(v-.5)*.5,Math.abs(v-.5)),ridge=1-smooth(.31,.43,hex);h=.28+.62*ridge;t=.14+.73*ridge;rough=.9-.17*ridge;break;}\r\n  case 38:{const c=voronoi(x*14,y*13),pile=1-smooth(.1,.54,c.near),valley=smooth(.06,.22,c.gap),fiber=Math.sin(x*83+Math.sin(y*41)*1.7)*Math.sin(y*77+Math.sin(x*37))*0.5+0.5;h=.2+.53*pile-.14*valley+.025*fiber;t=.1+.78*pile-.1*valley+.035*fiber;rough=.99;break;}\r\n  case 39:{const u=fract(x*4),v=fract(y*4),diamond=Math.abs(u-.5)+Math.abs(v-.5),seam=smooth(.39,.48,diamond),pad=1-smooth(.08,.43,diamond);h=.28+.62*pad-.21*seam;t=.14+.72*pad-.1*seam;rough=.93-.08*pad;break;}\r\n  case 40:{const d=voronoi(x*19,y*17),dimple=1-smooth(.06,.24,d.near),nap=smooth(.16,.39,d.near),fiber=Math.sin(x*91+Math.sin(y*48))*Math.sin(y*79+Math.sin(x*43));h=.46+.035*g-.14*dimple+.06*nap+.012*fiber;t=.54+.04*g-.27*dimple+.13*nap+.025*fiber;rough=.97-.025*nap;break;}\r\n  case 41:{const c=voronoi(x*11,y*11),pit=1-smooth(.03,.22,c.near),lip=1-smooth(.04,.2,c.gap);h=.55-.29*pit+.15*lip;t=.45-.31*pit+.18*lip;rough=.47-.16*lip+.28*pit;break;}\r\n  case 42:{const strand=triangle((x*8+y*1.7)*5),twist=triangle((x*8-y*1.7)*2);h=.27+.47*strand+.13*twist;t=.16+.74*strand;rough=.91-.08*strand;break;}\r\n  case 43:{const c=voronoi(x*15,y*15),crease=1-smooth(.025,.11,c.gap),grain=Math.sin(x*81+Math.sin(y*43))*Math.sin(y*75);h=.5-.16*crease+grain*.045;t=.38+.16*grain-.22*crease;rough=.72+.19*crease;break;}\r\n  case 44:{const c=voronoi(x*12,y*12),cell=smooth(.06,.37,c.near),chip=(hash(Math.floor(x*12),Math.floor(y*12))>.74?1:0)*cell;h=.34+.4*cell-.24*chip;t=.19+.62*cell-.18*chip;rough=.97;break;}\r\n  case 45:{const a=line(x+y*.12,12,.34),b=line(y-x*.08,12,.34),over=fract(x*12+y*12)<.5?a:b;h=.36+.27*a+.22*b+.12*over;t=.18+.37*a+.42*b;rough=.92-.12*over;break;}\r\n  case 46:{const u=fract(x*5)-.5,v=fract(y*5)-.5,angle=Math.atan2(v,u),r=Math.hypot(u,v),lobe=(1-smooth(.28,.49,r))*(.65+.35*Math.cos(angle*6)),vein=1-smooth(.012,.035,Math.abs(v-Math.sin(u*6)*.035));h=.33+.31*lobe+.12*vein;t=.19+.48*lobe+.22*vein;rough=.6-.17*lobe;break;}\r\n  case 47:{const vertical=line(x,22,.31),horizontal=line(y,22,.31),crossing=vertical*horizontal;h=.41+.12*vertical+.12*horizontal+.13*crossing;t=.26+.27*vertical+.27*horizontal;rough=.95-.08*crossing;break;}\r\n  case 48:{const rib=triangle(x*8),node=1-smooth(.015,.07,Math.abs(fract(y*2)-.5));h=.27+.53*(1-smooth(.28,.48,rib))+.14*node;t=.17+.67*(1-smooth(.3,.47,rib))+.12*node;rough=.68-.15*node;break;}\r\n  case 49:{const u=fract(x*6+(Math.floor(y*7)%2)*.5),v=fract(y*7),scale=Math.hypot((u-.5)*.86,v-.5),rim=1-smooth(.36,.48,scale),seam=1-smooth(.025,.09,Math.abs(v-.5));h=.28+.46*rim-.08*seam;t=.12+.74*rim-.12*seam;rough=.52-.24*rim;break;}\r\n  case 50:{const u=fract(x*14),v=fract(y*14),bar=Math.min(u,1-u,v,1-v),wire=1-smooth(.035,.105,bar),hole=1-wire;h=.26+.56*wire;t=.08+.76*wire;rough=.58-.2*wire;break;}\r\n  case 51:{const u=fract(x*18),v=fract(y*18),stagger=(Math.floor(y*18)%2)*.5,cx=fract(x*18+stagger)-.5,cy=v-.5,r=Math.hypot(cx,cy),loop=1-smooth(.035,.105,Math.abs(r-.27)),center=1-smooth(.01,.08,r);h=.44+.2*loop+.06*center;t=.34+.28*loop;rough=.99;break;}\r\n  case 52:{const c=voronoi(x*7,y*7),stone=1-smooth(.2,.52,c.near),edge=1-smooth(.035,.1,c.gap);h=.26+.49*stone-.1*edge;t=.1+.76*stone-.14*edge;rough=.87-.2*stone;break;}\r\n  case 53:{const q=y*7+Math.sin(x*17+y*4)*.35+Math.sin(x*6-y*13)*.16,ridge=1-smooth(.16,.34,Math.abs(fract(q)-.5));h=.39+.31*ridge+.025*g;t=.3+.38*ridge;rough=.99;break;}\r\n  case 54:{const c=voronoi(x*4,y*4),stone=1-smooth(.31,.75,c.near),seam=1-smooth(.045,.13,c.gap);h=.31+.43*stone-.16*seam;t=.15+.67*stone-.19*seam;rough=.79-.14*stone+.1*seam;break;}\r\n  case 55:{const c=voronoi(x*31,y*31),tuft=1-smooth(.08,.38,c.near),spore=hash(Math.floor(x*31),Math.floor(y*31))>.84?1:0;h=.37+.2*tuft+.07*spore;t=.2+.61*tuft+.12*spore;rough=.99;break;}\r\n }\r\n return{height:h,tint:clamp(t),glow,rough};\r\n}\r\n","handborne/source/app/material-refinement.ts":"import * as T from 'three';\r\nimport {mergeVertices} from 'three/examples/jsm/utils/BufferGeometryUtils.js';\r\n\r\nexport type CoachReliefBudget={remainingTriangles:number;usedTriangles:number;perMeshAddedTriangles:number;perMeshVertices:number};\r\n// Budgets count added triangles per coach, and total final positions per mesh.\r\nexport const createCoachReliefBudget=(limit=240_000,perMeshAddedTriangles=64_000,perMeshVertices=32_000):CoachReliefBudget=>({remainingTriangles:limit,usedTriangles:0,perMeshAddedTriangles,perMeshVertices});\r\nconst isFloat16=(a:{array:ArrayLike<number>;isFloat16BufferAttribute?:boolean})=>!!a.isFloat16BufferAttribute||(()=>{const Float16=(globalThis as any).Float16Array;return !!Float16&&a.array instanceof Float16;})();\r\n\r\nfunction component(a:T.BufferAttribute|T.InterleavedBufferAttribute,i:number,c:number){\r\n if(c===0)return a.getX(i);if(c===1)return a.getY(i);if(c===2)return a.getZ(i);if(c===3)return a.getW(i);\r\n return a.array[i*a.itemSize+c];\r\n}\r\nfunction write(a:T.BufferAttribute,i:number,c:number,v:number){\r\n if(c===0)a.setX(i,v);else if(c===1)a.setY(i,v);else if(c===2)a.setZ(i,v);else if(c===3)a.setW(i,v);else a.array[i*a.itemSize+c]=v;\r\n}\r\nfunction normalizeAttribute(a:T.BufferAttribute,i:number){\r\n if(a.itemSize<3)return;\r\n const x=a.getX(i),y=a.getY(i),z=a.getZ(i),length=Math.hypot(x,y,z);if(length<1e-12)return;\r\n a.setXYZ(i,x/length,y/length,z/length);\r\n}\r\n\r\n/** One linear triangle split. It preserves silhouette, UVs, authored normals and vertex attributes. */\r\nfunction split(source:T.BufferGeometry,maxVertices:number):T.BufferGeometry|undefined{\r\n const index=source.index;if(!index||index.count%3)return undefined;\r\n const edgeIds=new Map<string,number>(),edges:Array<[number,number]>=[],indices:number[]=[];\r\n const midpoint=(a:number,b:number)=>{\r\n  const lo=Math.min(a,b),hi=Math.max(a,b),key=lo+':'+hi,old=edgeIds.get(key);if(old!==undefined)return old;\r\n  const id=source.attributes.position.count+edges.length;edgeIds.set(key,id);edges.push([lo,hi]);return id;\r\n };\r\n const groups=source.groups.length?source.groups:[{start:0,count:index.count,materialIndex:0}],nextGroups:{start:number;count:number;materialIndex:number}[]=[];\r\n for(const group of groups){\r\n  const start=indices.length,end=Math.min(index.count,group.start+group.count);\r\n  for(let j=group.start;j+2<end;j+=3){const a=index.getX(j),b=index.getX(j+1),c=index.getX(j+2),ab=midpoint(a,b),bc=midpoint(b,c),ca=midpoint(c,a);\r\n   indices.push(a,ab,ca,ab,b,bc,ca,bc,c,ab,bc,ca);\r\n  }\r\n  if(indices.length>start)nextGroups.push({start,count:indices.length-start,materialIndex:group.materialIndex??0});\r\n }\r\n if(!edges.length||source.attributes.position.count+edges.length>maxVertices)return undefined;\r\n const out=source.clone();\r\n for(const [name,attribute]of Object.entries(source.attributes)){\r\n  if((attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||isFloat16(attribute))return undefined;\r\n  const count=attribute.count+edges.length,array=new (attribute.array.constructor as any)(count*attribute.itemSize);array.set(attribute.array);\r\n  const target=new T.BufferAttribute(array,attribute.itemSize,attribute.normalized);target.name=attribute.name;target.setUsage((attribute as T.BufferAttribute).usage);target.gpuType=(attribute as T.BufferAttribute).gpuType;\r\n  for(let e=0;e<edges.length;e++){const [a,b]=edges[e],dst=attribute.count+e;\r\n   for(let c=0;c<attribute.itemSize;c++)write(target,dst,c,(component(attribute,a,c)+component(attribute,b,c))*.5);\r\n   if(name==='normal'||name==='tangent')normalizeAttribute(target,dst);\r\n  }\r\n  out.setAttribute(name,target);\r\n }\r\n out.setIndex(indices);out.clearGroups();for(const group of nextGroups)out.addGroup(group.start,group.count,group.materialIndex);\r\n out.morphAttributes=source.morphAttributes;out.morphTargetsRelative=source.morphTargetsRelative;out.userData={...source.userData};\r\n return out;\r\n}\r\n\r\n/** Refine only bounded static roster geometry; unknown animated/morph geometry safely stays untouched. */\r\nexport function refineCoachGeometry(source:T.BufferGeometry,budget:CoachReliefBudget,worldScale=1):{geometry:T.BufferGeometry;addedTriangles:number;addedVertices:number;levels:number}|undefined{\r\n if(!budget||budget.remainingTriangles<=0||Object.values(source.morphAttributes).some(attributes=>attributes.length)||source.attributes.skinIndex||source.attributes.skinWeight)return undefined;\r\n if(source.drawRange.start!==0||(source.index&&source.drawRange.count<source.index.count))return undefined;\r\n for(const attribute of Object.values(source.attributes))if((attribute as T.InterleavedBufferAttribute).isInterleavedBufferAttribute||isFloat16(attribute))return undefined;\r\n let working=source.clone();\r\n if(!working.index){const merged=mergeVertices(working);working.dispose();working=merged;}\r\n if(!working.index||working.index.count%3){working.dispose();return undefined;}\r\n if(!working.attributes.normal)working.computeVertexNormals();\r\n const triangles=working.index.count/3;\r\n // Estimate a 90th-percentile edge so tiny existing triangles stay untouched while broad facets\r\n // receive enough linear splits to reach roughly .025 world units. The total remains tightly capped.\r\n if(triangles>10_000){working.dispose();return undefined;}\r\n const positions=working.attributes.position,edges:number[]=[];for(let j=0;j<working.index.count;j+=3){const a=working.index.getX(j),b=working.index.getX(j+1),c=working.index.getX(j+2);for(const [u,v]of [[a,b],[b,c],[c,a]])edges.push(Math.hypot(positions.getX(u)-positions.getX(v),positions.getY(u)-positions.getY(v),positions.getZ(u)-positions.getZ(v))*worldScale);}\r\n edges.sort((a,b)=>a-b);const p90=edges[Math.floor((edges.length-1)*.9)]??0,desired=Math.min(5,Math.max(0,Math.ceil(Math.log2(p90/.025))));\r\n if(!desired){working.dispose();return undefined;}\r\n const initial=working.attributes.position.count,initialTriangles=triangles;let levels=0;\r\n for(let level=0;level<desired;level++){\r\n  const currentTriangles=working.index!.count/3,nextTriangles=currentTriangles*4,cumulativeAdded=nextTriangles-initialTriangles;\r\n  if(cumulativeAdded>budget.remainingTriangles||cumulativeAdded>budget.perMeshAddedTriangles||nextTriangles>64_000)break;\r\n  const next=split(working,budget.perMeshVertices);if(!next)break;\r\n  working.dispose();working=next;levels++;\r\n }\r\n const addedTriangles=working.index!.count/3-initialTriangles,addedVertices=working.attributes.position.count-initial;\r\n if(!levels){working.dispose();return undefined;}\r\n budget.remainingTriangles-=addedTriangles;budget.usedTriangles+=addedTriangles;\r\n working.userData={...working.userData,coachRelief:{addedTriangles,addedVertices,levels,originalVertices:initial}};\r\n return {geometry:working,addedTriangles,addedVertices,levels};\r\n}\r\n\r\n/** Optional alternate-coach quality path for the original imported material (no recolouring). */\r\nexport function refineCoachMesh(mesh:T.Mesh,budget:CoachReliefBudget){\r\n if(Array.isArray(mesh.material)||(mesh as T.SkinnedMesh).isSkinnedMesh||mesh.geometry.attributes.skinIndex||mesh.geometry.attributes.skinWeight||Object.values(mesh.geometry.morphAttributes).some(attributes=>attributes.length))return undefined;\r\n mesh.updateWorldMatrix(true,false);const result=refineCoachGeometry(mesh.geometry,budget,mesh.matrixWorld.getMaxScaleOnAxis());if(!result)return undefined;\r\n const previous=mesh.geometry;mesh.geometry=result.geometry;if(mesh.userData.ownedGeometry)previous.dispose();mesh.userData.ownedGeometry=true;\r\n return {addedTriangles:result.addedTriangles,addedVertices:result.addedVertices,levels:result.levels};\r\n}\r\n","handborne/source/app/motion.ts":"import { POSES } from './poses.ts';\r\nexport const POSE_INTERVAL_MS=15_000;\r\nexport const nextPoseId=(id:string)=>POSES[(POSES.findIndex(p=>p.id===id)+1)%POSES.length].id;\r\nexport function specimenMotion(seconds:number){\r\n  return {position:[.0018*Math.sin(seconds*.57),.0028*Math.sin(seconds*.79),.0012*Math.sin(seconds*.43)] as [number,number,number],rotation:[.018*Math.sin(seconds*.41),.035*Math.sin(seconds*.32),.014*Math.sin(seconds*.53)] as [number,number,number]};\r\n}\r\nexport function schedulePoseCycle(advance:()=>void,schedule=globalThis.setTimeout,cancel=globalThis.clearTimeout){\r\n  let stopped=false,timer:ReturnType<typeof setTimeout>;\r\n  const tick=()=>{if(stopped)return;advance();if(!stopped)timer=schedule(tick,POSE_INTERVAL_MS);};\r\n  timer=schedule(tick,POSE_INTERVAL_MS);\r\n  return()=>{stopped=true;cancel(timer);};\r\n}\r\n","handborne/source/app/nail-shapes.json":"[\r\n  {\"id\":\"short\",\"name\":\"Short round\",\"extension\":0,\"tip\":0.73,\"taper\":1.8,\"curve\":0,\"width\":1},\r\n  {\"id\":\"square\",\"name\":\"Square\",\"extension\":0.004,\"tip\":0.98,\"taper\":3,\"curve\":0,\"width\":1.04},\r\n  {\"id\":\"almond\",\"name\":\"Almond\",\"extension\":0.006,\"tip\":0.12,\"taper\":1.65,\"curve\":0,\"width\":1},\r\n  {\"id\":\"coffin\",\"name\":\"Coffin\",\"extension\":0.01,\"tip\":0.52,\"taper\":1.4,\"curve\":0,\"width\":1.05},\r\n  {\"id\":\"stiletto\",\"name\":\"Stiletto\",\"extension\":0.014,\"tip\":0.025,\"taper\":1.15,\"curve\":0.0008,\"width\":0.95},\r\n  {\"id\":\"talon\",\"name\":\"Talon\",\"extension\":0.014,\"tip\":0.025,\"taper\":1.4,\"curve\":0.007,\"width\":1.05},\r\n  {\"id\":\"hook\",\"name\":\"Hooked claw\",\"extension\":0.01,\"tip\":0.025,\"taper\":1.6,\"curve\":0.014,\"width\":1.12},\r\n  {\"id\":\"spade\",\"name\":\"Spade\",\"extension\":0.006,\"tip\":0.06,\"taper\":3.4,\"curve\":0.001,\"width\":1.3}\r\n]\r\n","handborne/source/app/nails.ts":"import * as THREE from 'three';\r\nimport shapes from './nail-shapes.json' with {type:'json'};\r\nimport { DIGITS, type Digit } from './poses.ts';\r\nimport { REST_SOURCE, DIGIT_RADII, BABY_RIG_SCALE } from './pose-rig.ts';\r\nexport const NAIL_SHAPES=shapes;\r\nexport const DEFAULT_NAIL_SHAPE='family';\r\nexport function validateNailShape(value:unknown):string {\r\n  if(value===DEFAULT_NAIL_SHAPE||shapes.some(s=>s.id===value))return value as string;\r\n  throw new Error('Choose a valid nail shape.');\r\n}\r\nfunction curve(digit:Digit,t:number){\r\n  const points=REST_SOURCE[digit].map(([x,y,z])=>new THREE.Vector3(x,z,-y));\r\n  const u=Math.min(t*3,3-1e-8),i=Math.floor(u),a=u-i;\r\n  const p0=points[Math.max(0,i-1)],p1=points[i],p2=points[i+1],p3=points[Math.min(3,i+2)];\r\n  return p1.clone().multiplyScalar(2).addScaledVector(p0,-a+2*a*a-a*a*a).addScaledVector(p1,-5*a*a+3*a*a*a).addScaledVector(p2,a+4*a*a-3*a*a*a).addScaledVector(p3,-a*a+a*a*a).multiplyScalar(.5);\r\n}\r\nexport function nailGeometry(digit:Digit,shapeId:string,styleId:number){\r\n  const shape=shapes.find(s=>s.id===shapeId);if(!shape)throw new Error('Unknown nail shape');\r\n  const positions:number[]=[],uv:number[]=[],indices:number[]=[],rings=24,sides=20,row=sides+1,layer=(rings+1)*row;\r\n  const radius=DIGIT_RADII[DIGITS.indexOf(digit)];\r\n  for(let bottom=0;bottom<2;bottom++)for(let j=0;j<=rings;j++){\r\n    const u=j/rings,t=.78+u*.195,p=curve(digit,t),tangent=curve(digit,t+.002).sub(curve(digit,t-.002)).normalize();\r\n    const dorsal=new THREE.Vector3(0,0,1);dorsal.addScaledVector(tangent,-dorsal.dot(tangent)).normalize();\r\n    const side=tangent.clone().cross(dorsal).normalize();\r\n    const growth=Math.max(0,(u-.32)/.68),width=radius*.72*shape.width*(.8+.2*Math.sin(Math.PI*u))*(1-(1-shape.tip)*Math.pow(u,shape.taper));\r\n    p.addScaledVector(tangent,shape.extension*growth*growth).addScaledVector(dorsal,-shape.curve*Math.pow(growth,3));\r\n    for(let k=0;k<=sides;k++){\r\n      const v=k/sides*2-1;\r\n      const ridge=styleId===0?0:.00022*Math.sin(v*Math.PI*(3+styleId%5))*Math.sin(Math.PI*u);\r\n      const point=p.clone().addScaledVector(side,width*v).addScaledVector(dorsal,radius*(1-.31*t)*(.87-.18*v*v)+.00055-bottom*.0007+ridge);\r\n      positions.push(point.x,point.y,point.z);uv.push(k/sides,u);\r\n    }\r\n  }\r\n  const quad=(a:number,b:number,c:number,d:number)=>indices.push(a,b,c,a,c,d);\r\n  for(let j=0;j<rings;j++)for(let k=0;k<sides;k++){\r\n    const a=j*row+k;quad(a,a+1,a+row+1,a+row);quad(a+layer,a+row+layer,a+row+1+layer,a+1+layer);\r\n  }\r\n  for(let j=0;j<rings;j++){\r\n    const a=j*row,b=a+sides;quad(a,a+row,a+row+layer,a+layer);quad(b,b+layer,b+row+layer,b+row);\r\n  }\r\n  for(let k=0;k<sides;k++){quad(k,k+layer,k+1+layer,k+1);const a=rings*row+k;quad(a,a+1,a+1+layer,a+layer);}\r\n  const geometry=new THREE.BufferGeometry();geometry.setAttribute('position',new THREE.Float32BufferAttribute(positions,3));geometry.setAttribute('uv',new THREE.Float32BufferAttribute(uv,2));geometry.setIndex(indices);geometry.computeVertexNormals();\r\n  if(styleId===22)geometry.scale(BABY_RIG_SCALE.x,BABY_RIG_SCALE.y,BABY_RIG_SCALE.z);\r\n  return geometry;\r\n}\r\nexport function replaceNailShape(part:THREE.Object3D,shapeId:string,styleId:number){\r\n  validateNailShape(shapeId);if(shapeId===DEFAULT_NAIL_SHAPE)return;\r\n  const materials=new Map<Digit,THREE.Material|THREE.Material[]>();\r\n  part.traverse(obj=>{if(obj instanceof THREE.Mesh&&obj.userData.digit&&obj.userData.source_part?.endsWith('_claw'))materials.set(obj.userData.digit,obj.material);});\r\n  part.clear();\r\n  for(const digit of DIGITS){\r\n    const material=materials.get(digit);if(!material)throw new Error('Missing nail material for '+digit);\r\n    const mesh=new THREE.Mesh(nailGeometry(digit,shapeId,styleId),material);\r\n    mesh.name=digit+'_'+shapeId;mesh.userData={region:'nails',digit,source_part:digit+'_claw',nail_shape:shapeId,ownedGeometry:true,...(styleId===22?{rig_profile:'baby'}:{})};mesh.castShadow=true;mesh.receiveShadow=true;part.add(mesh);\r\n  }\r\n}\r\n","handborne/source/app/page.tsx":"'use client';\r\n\r\nimport { useCallback, useEffect, useRef, useState } from 'react';\r\nimport { ChevronLeft, ChevronRight, Dices, Download, RotateCcw, Undo2, Redo2, Lock, Unlock, Save, Layers3, Focus, Rotate3D, Play, Pause } from 'lucide-react';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';\r\nimport { Switch } from '@/components/ui/switch';\r\nimport { NativeSelect, NativeSelectOption } from '@/components/ui/native-select';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { DEFAULT_SELECTION, REGIONS, STYLES, styleFor, PICKER_STYLES, type RegionId } from './catalog';\r\nimport { parseDesign, designCode, randomize, type Selection, type HandDesign } from './recipe';\r\nimport { DEFAULT_POSE, POSES } from './poses';\r\nimport { DEFAULT_NAIL_SHAPE, NAIL_SHAPES } from './nails';\r\nimport { nextPoseId, schedulePoseCycle } from './motion';\r\nimport { registerHandTools } from './web-tools';\r\nimport { HandViewer, type HandViewerHandle } from './hand-viewer';\r\nimport {MATERIAL_NOTES} from './material-language';\r\nimport {isHandStyleUnlocked,assertHandStylesUnlocked,availableHandSections,subscribeHandUnlocks} from './material-access';\r\nimport {surfaceSwatch} from './swatches';\r\nimport { AnatomyComparison, ANATOMY_STUDIES, SOFT_STUDIES } from './anatomy-comparison';\r\nimport { HAND_SHAPES, activeHandShape, selectHandShape, type ShapeScope } from './hand-shapes';\r\nimport { SCALE_PATTERNS } from './scale-patterns';\r\n\r\ntype History = {past:HandDesign[];present:HandDesign;future:HandDesign[]};\r\nconst number = (n:number) => String(n+1).padStart(2,'0');\r\nexport default function Home() {\r\n  const [selectedRegion,setSelectedRegion]=useState<RegionId>('palm');\r\n  const [tab,setTab]=useState('look'),[scope,setScope]=useState<'hand'|'part'>('hand');\r\n  const [,setOwnershipRevision]=useState(0);\r\n  const [saved,setSaved]=useState(false),[embedded,setEmbedded]=useState(false);\r\n  const chooseRegion=(region:RegionId)=>{setSelectedRegion(region);setScope('part');setTab('look');};\r\n  const [shapeScope,setShapeScope]=useState<ShapeScope>('hand');\r\n  const [history,setHistory]=useState<History>({past:[],present:{sections:{...DEFAULT_SELECTION},pose:DEFAULT_POSE,nailShape:DEFAULT_NAIL_SHAPE},future:[]});\r\n  const [animate,setAnimate]=useState(false),[visible,setVisible]=useState(true),[cycleRevision,setCycleRevision]=useState(0);\r\n  const [locks,setLocks]=useState<RegionId[]>([]);\r\n  const [exploded,setExploded]=useState(false);\r\n  const [dialog,setDialog]=useState<'export'|'recipe'|null>(null);\r\n  const [recipeInput,setRecipeInput]=useState('');\r\n  const [message,setMessage]=useState('');\r\n  const [busy,setBusy]=useState(false);\r\n  const [restored,setRestored]=useState(false);\r\n  const [readyCode,setReadyCode]=useState('');\r\n  const [comparison,setComparison]=useState<'anatomy'|'soft'|null>(null),[gray,setGray]=useState(false);\r\n  const viewer=useRef<HandViewerHandle>(null);\r\n  const design=history.present,selection=design.sections;\r\n  const code=designCode(design);\r\n  const style=styleFor(selection[selectedRegion]);\r\n  const selectedShape=activeHandShape(selection,shapeScope,selectedRegion);\r\n  const shapeLocked=shapeScope==='hand'?locks.length===REGIONS.length:locks.includes(selectedRegion);\r\n  const current=useRef(design);current.current=design;\r\n  const restoreAfterOwnership=useRef<HandDesign|null>(null);\r\n  const pending=useRef<{code:string;resolve:()=>void;reject:(e:Error)=>void;timer:ReturnType<typeof setTimeout>}|null>(null);\r\n  const commit=useCallback((sections:Selection,nextPose?:string,nextNailShape?:string,nextScalePattern?:string)=>{\r\n    assertHandStylesUnlocked(sections);restoreAfterOwnership.current=null;\r\n    if(nextPose)setCycleRevision(v=>v+1);\r\n    setHistory(h=>{const pattern=nextScalePattern??h.present.scalePattern??'none';const next={sections,pose:nextPose??h.present.pose,nailShape:nextNailShape??h.present.nailShape,...(pattern==='none'?{}:{scalePattern:pattern})};return designCode(h.present)===designCode(next)?h:{past:[...h.past,h.present].slice(-80),present:next,future:[]};});\r\n  },[]);\r\n  const setStyle=(region:RegionId,value:number)=>commit({...selection,[region]:styleFor(value).id});\r\n  const applyAll=(id:number)=>commit(Object.fromEntries(REGIONS.map(r=>[r.id,locks.includes(r.id)?selection[r.id]:id])) as Selection);\r\n  const applyShape=(shapeId:string)=>{\r\n    const next=selectHandShape(selection,shapeId,shapeScope,selectedRegion,locks);\r\n    commit(next);setGray(false);\r\n    if(locks.length&&shapeScope==='hand')setMessage('Shape applied. Locked sections were kept.');\r\n  };\r\n  const undo=()=>setHistory(h=>h.past.length?{past:h.past.slice(0,-1),present:h.past[h.past.length-1],future:[h.present,...h.future]}:h);\r\n  const redo=()=>setHistory(h=>h.future.length?{past:[...h.past,h.present],present:h.future[0],future:h.future.slice(1)}:h);\r\n  const roll=()=>{commit(randomize(selection,locks,crypto.getRandomValues(new Uint32Array(1))[0]));setMessage(locks.length===REGIONS.length?'Unlock a section to remix.':'A fresh mix. All yours.');};\r\n  const onReady=useCallback((value:string)=>{\r\n    setReadyCode(value);\r\n    if(pending.current?.code===value){clearTimeout(pending.current.timer);pending.current.resolve();pending.current=null;}\r\n  },[]);\r\n  useEffect(()=>{\r\n    let previous=current.current;\r\n    try{const saved=localStorage.getItem('handborne-recipe-v4')??localStorage.getItem('handborne-recipe-v3')??localStorage.getItem('handborne-recipe-v2')??localStorage.getItem('handborne-recipe-v1');if(saved){previous=parseDesign(saved);restoreAfterOwnership.current=previous;previous={...previous,sections:availableHandSections(previous.sections)};setHistory({past:[],present:previous,future:[]});}}catch{}\r\n    const query=new URLSearchParams(window.location.search),study=Number(query.get('anatomy'));\r\n    const compare=query.get('compare');setComparison(compare==='anatomy'||compare==='soft'?compare:null);\r\n    if(query.has('anatomy')&&[...ANATOMY_STUDIES,...SOFT_STUDIES].some(s=>s.id===study)){\r\n      setHistory({past:[previous],present:{sections:Object.fromEntries(REGIONS.map(r=>[r.id,study])) as Selection,pose:DEFAULT_POSE,nailShape:DEFAULT_NAIL_SHAPE},future:[]});setGray(ANATOMY_STUDIES.some(s=>s.id===study));\r\n    }\r\n    setEmbedded(window.parent!==window);\r\n    const preference=window.matchMedia('(prefers-reduced-motion: reduce)');setAnimate(false);\r\n    const reduce=()=>{if(preference.matches)setAnimate(false);};preference.addEventListener('change',reduce);\r\n    const visibility=()=>setVisible(!document.hidden);visibility();document.addEventListener('visibilitychange',visibility);\r\n    setRestored(true);\r\n    return()=>{preference.removeEventListener('change',reduce);document.removeEventListener('visibilitychange',visibility);};\r\n  },[]);\r\n  useEffect(()=>subscribeHandUnlocks(()=>{setOwnershipRevision(n=>n+1);setHistory(h=>{const saved=restoreAfterOwnership.current;restoreAfterOwnership.current=null;const design=saved??h.present;return {...h,present:{...design,sections:availableHandSections(design.sections)}};});}),[]);\r\n  useEffect(()=>{if(!restored)return;try{localStorage.setItem('handborne-recipe-v4',JSON.stringify({version:4,...design}));setSaved(true);window.parent.postMessage({type:'handborne:changed'},window.location.origin);}catch{setSaved(false);setMessage('Device storage is full. Save a recipe to keep this hand.');}},[design,restored]);\r\n  useEffect(()=>{const update=(event:StorageEvent)=>{if(event.key==='handborne-recipe-v4'&&event.newValue)try{const next=parseDesign(event.newValue);next.sections=availableHandSections(next.sections);setHistory(h=>({...h,present:next}));}catch{}};window.addEventListener('storage',update);return()=>window.removeEventListener('storage',update);},[]);\r\n  useEffect(()=>{\r\n    if(!animate||!restored||!visible||dialog||busy||comparison)return;\r\n    return schedulePoseCycle(()=>{if(!pending.current)setHistory(h=>({...h,present:{...h.present,pose:nextPoseId(h.present.pose)}}));});\r\n  },[animate,restored,visible,dialog,busy,cycleRevision,comparison]);\r\n  useEffect(()=>{if(!message)return;const timer=setTimeout(()=>setMessage(''),5500);return()=>clearTimeout(timer);},[message]);\r\n  useEffect(()=>{if(comparison)return;return registerHandTools(()=>current.current,async next=>{\r\n    assertHandStylesUnlocked(next.sections);\r\n    if(designCode(current.current)===designCode(next))return;\r\n    if(pending.current){clearTimeout(pending.current.timer);pending.current.reject(new Error('Superseded by another recipe.'));}\r\n    await new Promise<void>((resolve,reject)=>{\r\n      const timer=setTimeout(()=>{pending.current=null;reject(new Error('Hand loading timed out.'));},30000);\r\n      pending.current={code:designCode(next),resolve,reject,timer};commit(next.sections,next.pose,next.nailShape,next.scalePattern??'none');\r\n    });\r\n  });},[commit,comparison]);\r\n  async function exportFile(format:'glb'|'obj'|'png') {\r\n    if(!viewer.current)return;setBusy(true);\r\n    try{await viewer.current.exportFile(format);setMessage('Download ready.');}catch(error){setMessage(error instanceof Error?error.message:'Export failed.');}finally{setBusy(false);}\r\n  }\r\n  function saveRecipe(){\r\n    const blob=new Blob([JSON.stringify({version:4,...design},null,2)],{type:'application/json'});\r\n    const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download='handborne-recipe.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),2000);\r\n    setMessage('Recipe saved.');\r\n  }\r\n  function loadRecipe(){try{const next=parseDesign(recipeInput);commit(next.sections,next.pose,next.nailShape,next.scalePattern??'none');setDialog(null);setMessage('Your hand is back.');}catch(error){setMessage(error instanceof Error?error.message:'Invalid recipe.');}}\r\n\r\n  if(comparison)return <AnatomyComparison variant={comparison} />;\r\n  return <main className={'creator-shell design-studio'+(embedded?' embedded':'')}>\r\n    <header className=\"topbar\">\r\n      <div className=\"brand-copy\"><p>{embedded?'YOUR GALA AVATAR':'MOM INC. CUSTOM SHOP'}</p><h1>HELPING HAND<sup>™</sup></h1></div>\r\n      <div className=\"top-actions\">\r\n        <Button variant=\"ghost\" size=\"icon\" aria-label=\"Undo\" disabled={!history.past.length} onClick={undo}><Undo2 /></Button>\r\n        <Button variant=\"ghost\" size=\"icon\" aria-label=\"Redo\" disabled={!history.future.length} onClick={redo}><Redo2 /></Button>\r\n        <Button variant=\"outline\" onClick={roll}><Dices /><span>Remix</span></Button>\r\n        <span className=\"save-indicator\" role=\"status\">{saved?'Saved on device':'Not saved yet'}</span>\r\n      </div>\r\n    </header>\r\n    <section className=\"creator-grid\">\r\n      <section className=\"viewport-panel\" aria-label=\"Live 3D hand preview\">\r\n        <div className=\"viewport-chrome\"><Badge className=\"live-badge\"><span className=\"live-dot\" />{readyCode===code?'LIVE PREVIEW':'UPDATING…'}</Badge></div>\r\n        <HandViewer ref={viewer} selection={selection} poseId={design.pose} nailShape={design.nailShape} scalePattern={design.scalePattern} animate={animate&&visible&&!dialog&&!busy} gray={gray} selectedRegion={selectedRegion} exploded={exploded} onSelectRegion={chooseRegion} onReady={onReady} />\r\n        <div className=\"view-tools\">\r\n          <Button variant=\"outline\" aria-label={animate?'Pause animation':'Play animation'} title={animate?'Pause animation':'Play animation'} size=\"icon\" onClick={()=>setAnimate(v=>!v)}>{animate?<Pause />:<Play />}</Button>\r\n          <Button variant=\"outline\" aria-label=\"Reset camera\" title=\"Reset camera\" size=\"icon\" onClick={()=>viewer.current?.resetView()}><Focus /></Button>\r\n          <Button variant=\"outline\" aria-label=\"View other side\" title=\"View other side\" size=\"icon\" onClick={()=>viewer.current?.flipView()}><Rotate3D /></Button>\r\n          <label className=\"explode-control\">Parts<Switch aria-label=\"Separate hand parts\" checked={exploded} onCheckedChange={setExploded} /></label>\r\n        </div>\r\n        <p className=\"orbit-hint\">Drag to turn · Tap a part to edit</p>\r\n      </section>\r\n      <aside className=\"design-controls\" aria-label=\"Hand design controls\">\r\n        <Tabs value={tab} onValueChange={value=>setTab(String(value))} className=\"design-tabs\">\r\n          <TabsList aria-label=\"Customize your hand\">{[['look','Look'],['shape','Shape'],['details','Details'],['motion','Motion'],['files','Save']].map(([id,label])=><TabsTrigger key={id} value={id}>{label}</TabsTrigger>)}</TabsList>\r\n          <div className=\"control-scroll\">\r\n            <TabsContent value=\"look\">\r\n              <div className=\"design-heading\"><h2>Choose a look</h2><p>{MATERIAL_NOTES[style.id]??style.name}</p></div>\r\n              <div className=\"scope-switch\"><Button variant=\"outline\" aria-pressed={scope==='hand'} onClick={()=>setScope('hand')}>Whole hand</Button><Button variant=\"outline\" aria-pressed={scope==='part'} onClick={()=>setScope('part')}>One part</Button></div>\r\n              {scope==='part'&&<div className=\"part-target\"><label htmlFor=\"part-target\">Part<NativeSelect id=\"part-target\" value={selectedRegion} onChange={e=>setSelectedRegion(e.target.value as RegionId)}>{REGIONS.map(r=><NativeSelectOption key={r.id} value={r.id}>{r.label}</NativeSelectOption>)}</NativeSelect></label><Button variant=\"outline\" size=\"icon\" aria-label={(locks.includes(selectedRegion)?'Unlock ':'Lock ')+REGIONS.find(r=>r.id===selectedRegion)?.label} aria-pressed={locks.includes(selectedRegion)} onClick={()=>setLocks(list=>list.includes(selectedRegion)?list.filter(id=>id!==selectedRegion):[...list,selectedRegion])}>{locks.includes(selectedRegion)?<Lock />:<Unlock />}</Button></div>}\r\n              <div className=\"style-grid\" aria-label=\"Creature families\">{PICKER_STYLES.map(item=><button key={item.id} aria-label={item.name+(isHandStyleUnlocked(item.id)?'':' - Locked')} title={isHandStyleUnlocked(item.id)?item.name:'Earn this texture and color in Coach packs'} disabled={!isHandStyleUnlocked(item.id)||(scope==='part'?locks.includes(selectedRegion):locks.length===REGIONS.length)} aria-pressed={scope==='hand'?Object.values(selection).every(id=>id===item.id):style.id===item.id} className={(scope==='hand'?Object.values(selection).every(id=>id===item.id):style.id===item.id)?'is-active':''} onClick={()=>scope==='hand'?applyAll(item.id):setStyle(selectedRegion,item.id)}><img src={surfaceSwatch(item.id,item.primary,item.secondary,item.accent)} alt=\"\" loading=\"lazy\" /><span className=\"tile-label\">{!isHandStyleUnlocked(item.id)&&<Lock size={12} aria-hidden=\"true\" />}{item.name}</span></button>)}</div>\r\n              <p className=\"control-note\">New horror and robotic looks unlock through texture and color packs in <a href=\"https://myr5.mominc.online\" target=\"_blank\" rel=\"noopener\">Coach</a>. Return here after earning both.</p>\r\n              {!!locks.length&&<p className=\"control-note\">{locks.length} locked {locks.length===1?'part stays':'parts stay'} when you remix or change the whole hand. <button onClick={()=>setLocks([])}>Unlock all</button></p>}\r\n            </TabsContent>\r\n            <TabsContent value=\"shape\">\r\n              <div className=\"design-heading\"><h2>Hand shape</h2><p>Shape presets include their creature look.</p></div>\r\n              <label className=\"shape-target\" htmlFor=\"shape-target\">Apply to<NativeSelect id=\"shape-target\" value={shapeScope} onChange={e=>setShapeScope(e.target.value as ShapeScope)}><NativeSelectOption value=\"hand\">Whole hand</NativeSelectOption><NativeSelectOption value=\"section\">{REGIONS.find(r=>r.id===selectedRegion)?.label}</NativeSelectOption></NativeSelect></label>\r\n              <div className=\"hand-shape-buttons\">{HAND_SHAPES.map(shape=><Button key={shape.id} variant=\"outline\" aria-pressed={selectedShape===shape.id} disabled={shapeLocked} onClick={()=>applyShape(shape.id)}>{shape.name}</Button>)}</div>\r\n              <label className=\"gray-control\">Plain gray preview<Switch checked={gray} onCheckedChange={setGray} aria-label=\"Show shape without textures\" /></label>\r\n            </TabsContent>\r\n            <TabsContent value=\"details\">\r\n              <div className=\"design-heading\"><h2>Nails & scales</h2><p>Change the small details of your hand.</p></div>\r\n              <fieldset className=\"nail-shop\"><legend>Nail shape</legend><div className=\"nail-shapes\">{[{id:DEFAULT_NAIL_SHAPE,name:'Creature default'},...NAIL_SHAPES].map(shape=><button key={shape.id} aria-pressed={design.nailShape===shape.id} onClick={()=>commit(selection,undefined,shape.id)}>{shape.name}</button>)}</div></fieldset>\r\n              <fieldset className=\"scale-shop\"><legend>Raised scales</legend><div className=\"scale-buttons\">{SCALE_PATTERNS.map(pattern=><Button key={pattern.id} variant=\"outline\" aria-pressed={(design.scalePattern??'none')===pattern.id} onClick={()=>commit(selection,undefined,undefined,pattern.id)}><span className={'scale-icon scale-'+pattern.id} aria-hidden=\"true\" />{pattern.name}</Button>)}</div></fieldset>\r\n            </TabsContent>\r\n            <TabsContent value=\"motion\">\r\n              <div className=\"design-heading\"><h2>Try a gesture</h2><p>Hold a pose while you edit, or play the collection.</p></div>\r\n              <label className=\"gray-control\">Play gestures<Switch checked={animate} onCheckedChange={setAnimate} aria-label=\"Play gestures\" /></label>\r\n              <div className=\"pose-grid\">{POSES.map(pose=><Button key={pose.id} variant=\"outline\" aria-pressed={design.pose===pose.id} onClick={()=>{setAnimate(false);commit(selection,pose.id);}}>{pose.name}</Button>)}</div>\r\n            </TabsContent>\r\n            <TabsContent value=\"files\">\r\n              <div className=\"design-heading\"><h2>Keep your hand</h2><p>Edits save on this device. Take a recipe to another device.</p></div>\r\n              <div className=\"file-actions\"><Button onClick={()=>{setRecipeInput(code);setDialog('recipe');}}><Save />Save or load recipe</Button><Button variant=\"outline\" onClick={()=>setDialog('export')}><Download />Export hand</Button><Button variant=\"outline\" onClick={()=>{commit({...DEFAULT_SELECTION},DEFAULT_POSE,DEFAULT_NAIL_SHAPE,'none');setLocks([]);setExploded(false);setMessage('Starter hand restored. Undo brings your design back.');}}><RotateCcw />Reset hand</Button></div>\r\n            </TabsContent>\r\n          </div>\r\n        </Tabs>\r\n      </aside>\r\n    </section>\r\n\r\n    <Dialog open={dialog!==null} onOpenChange={open=>{if(!open)setDialog(null);}}>\r\n      <DialogContent className=\"creator-dialog\">\r\n        <DialogHeader><DialogTitle>{dialog==='export'?'Export your creature':'Save or load a recipe'}</DialogTitle>\r\n          <DialogDescription>{dialog==='export'?'Take home this hand, just as it is. Or collect the complete creature library.':'Your recipe keeps all six parts, nail shape, scale texture and hand position. Saved on this device; older recipes still work.'}</DialogDescription></DialogHeader>\r\n        {dialog==='export'?<div className=\"export-options\">\r\n          <Button disabled={busy||readyCode!==code} onClick={()=>exportFile('glb')}><Download /> Download hand · GLB</Button>\r\n          <Button variant=\"outline\" disabled={busy||readyCode!==code} onClick={()=>exportFile('obj')}><Download /> Download hand · OBJ + MTL</Button>\r\n          <Button variant=\"outline\" disabled={busy||readyCode!==code} onClick={()=>exportFile('png')}>Save preview · PNG</Button>\r\n          <a className=\"library-download\" href=\"https://mominc.online/handborne/downloads/handborne-sections.zip\" download>Download original sections · OBJ + MTL</a>\r\n          <a className=\"library-download\" href=\"https://mominc.online/handborne/downloads/handborne-complete-hands.zip\" download>Download original complete hands · GLB</a>\r\n          <p className=\"export-note\">Download hand includes your chosen scales and nails. The complete library contains the original base families. GLB includes detailed textures. GLB and OBJ bake the selected pose into the mesh; they do not include an animation skeleton. Curved section joins remain open surfaces, not a print-ready union.</p>\r\n        </div>:<div className=\"recipe-editor\">\r\n          <label htmlFor=\"recipe-text\">Recipe code or JSON</label>\r\n          <Textarea id=\"recipe-text\" value={recipeInput} onChange={e=>setRecipeInput(e.target.value)} rows={4} />\r\n          <Button onClick={loadRecipe}>Load recipe</Button>\r\n          <Button variant=\"outline\" onClick={saveRecipe}>Download current recipe</Button>\r\n          <Button variant=\"ghost\" onClick={async()=>{try{await navigator.clipboard.writeText(code);setMessage('Recipe code copied.');}catch{setRecipeInput(code);setMessage('Select and copy the code from the field.');}}}>Copy current code</Button>\r\n        </div>}\r\n      </DialogContent>\r\n    </Dialog>\r\n    {message&&<div className=\"feedback-toast\" role=\"status\">{message}</div>}\r\n  </main>;\r\n}\r\n","handborne/source/app/palette-finishes.ts":"import * as THREE from 'three';\r\nimport palettesData from './palettes.json';\r\n\r\nexport type FinishKind =\r\n  | 'matte' | 'brushed-metal' | 'polished-metal' | 'glitter'\r\n  | 'pearl' | 'watercolor' | 'marbled' | 'glaze';\r\n\r\nexport const FINISH_ID: Record<FinishKind, number> = {\r\n  matte: 0,\r\n  'brushed-metal': 1,\r\n  'polished-metal': 2,\r\n  glitter: 3,\r\n  pearl: 4,\r\n  watercolor: 6,\r\n  marbled: 7,\r\n  glaze: 8,\r\n};\r\n\r\nconst KINDS = new Set<string>(Object.keys(FINISH_ID));\r\ntype PaletteMeta = { id: string; finish?: unknown; colors: string[] };\r\nconst CATALOG = new Map<string, PaletteMeta>(palettesData.map(p => [p.id,p]));\r\n\r\nfunction meta(id?: string): (PaletteMeta & { finish: FinishKind }) | undefined {\r\n  if (!id) return undefined;\r\n  const p = CATALOG.get(id);\r\n  return p && typeof p.finish === 'string' && KINDS.has(p.finish) ? p as PaletteMeta & {finish: FinishKind} : undefined;\r\n}\r\n\r\nexport function paletteFinish(paletteId?: string): FinishKind | undefined {\r\n  return meta(paletteId)?.finish;\r\n}\r\nexport function isCatalogPalette(paletteId?: string): boolean {\r\n  return !!meta(paletteId);\r\n}\r\n\r\nexport const FINISH_LABELS: Record<FinishKind, string> = {\r\n  matte: 'Matte', 'brushed-metal': 'Brushed metal', 'polished-metal': 'Polished metal',\r\n  glitter: 'Glitter', pearl: 'Pearl', watercolor: 'Watercolor', marbled: 'Marbled', glaze: 'Glaze',\r\n};\r\nexport function finishLabel(id?: string): string {\r\n  const f = paletteFinish(id);\r\n  return f ? FINISH_LABELS[f] : '';\r\n}\r\n\r\nexport function finishSwatchCSS(id?: string): string {\r\n  const p = meta(id); if (!p) return '';\r\n  const [a,b,c] = p.colors;\r\n  switch (p.finish) {\r\n    case 'matte': return '';\r\n    case 'brushed-metal': return `repeating-linear-gradient(100deg,${a} 0 2px,${b} 2px 3px,${c} 3px 5px)`;\r\n    case 'polished-metal': return `linear-gradient(115deg,${a},${c} 38%,#fff 50%,${b} 64%,${a})`;\r\n    case 'glitter': return `radial-gradient(circle at 22% 28%,#fff 0 2%,transparent 3%),radial-gradient(circle at 68% 62%,${c} 0 2%,transparent 3%),linear-gradient(135deg,${a},${b})`;\r\n    case 'pearl': return `linear-gradient(120deg,${a},#fff 36%,${b} 58%,${c})`;\r\n    case 'watercolor': return `radial-gradient(circle at 28% 34%,${a} 0 28%,transparent 55%),radial-gradient(circle at 72% 66%,${c} 0 24%,transparent 58%),linear-gradient(${b},${b})`;\r\n    case 'marbled': return `repeating-linear-gradient(128deg,${a} 0 9px,${b} 10px 17px,${c} 18px 23px)`;\r\n    case 'glaze': return `linear-gradient(160deg,rgba(255,255,255,.72),transparent 34%),linear-gradient(135deg,${a},${b},${c})`;\r\n  }\r\n}\r\n\r\nconst TAU = Math.PI * 2;\r\nconst wrap = (x: number) => x - Math.floor(x);\r\nfunction flatField(u: number, v: number): number {\r\n  u = wrap(u); v = wrap(v);\r\n  const s = 0.5 + 0.5 * Math.sin(TAU * u + 0.9 * Math.sin(TAU * v))\r\n    * Math.cos(TAU * v - 0.7 * Math.sin(TAU * u));\r\n  return wrap(s + 0.22 * Math.sin(TAU * (u - v)));\r\n}\r\nexport function samplePaletteTint(paletteId: string | undefined, tint: number, u: number, v: number, flat = false): number {\r\n  const f = paletteFinish(paletteId);\r\n  if (!f) return tint;\r\n  let t = THREE.MathUtils.clamp(tint, 0, 1);\r\n  u = wrap(u); v = wrap(v);\r\n  if (flat && f !== 'watercolor' && f !== 'marbled') return THREE.MathUtils.clamp(0.72 * flatField(u, v) + 0.28 * t, 0, 1);\r\n  if (f === 'watercolor') {\r\n    const bloom = 0.5 + 0.32 * Math.sin(TAU * u + 1.7 * Math.sin(TAU * v)) + 0.18 * Math.sin(TAU * (2 * u + v) + 0.6);\r\n    const grain = 0.03 * Math.sin(TAU * (9 * u + 5 * v));\r\n    return THREE.MathUtils.clamp(0.84 * bloom + 0.16 * t + grain, 0, 1);\r\n  }\r\n  if (f === 'marbled') {\r\n    const swirl = 0.5 + 0.48 * Math.sin(TAU * (u + 0.35 * Math.sin(TAU * (v + 0.5 * u))) + TAU * v);\r\n    return THREE.MathUtils.clamp(0.86 * swirl + 0.14 * t, 0, 1);\r\n  }\r\n  return t;\r\n}\r\n\r\nexport const FINISH_TINT_GLSL = `\r\nfloat myr5FlatPaletteTint(vec2 uv) {\r\n  uv = fract(uv);\r\n  float s = 0.5 + 0.5 * sin(6.2831853 * uv.x + 0.9 * sin(6.2831853 * uv.y))\r\n    * cos(6.2831853 * uv.y - 0.7 * sin(6.2831853 * uv.x));\r\n  return clamp(0.72 * fract(s + 0.22 * sin(6.2831853 * (uv.x - uv.y))) + 0.14, 0.0, 1.0);\r\n}\r\nfloat myr5FinishTint(float finish, float t, vec2 uv) {\r\n  t = clamp(t, 0.0, 1.0); uv = fract(uv);\r\n  if (finish == 6.0) {\r\n    float bloom = 0.5 + 0.32 * sin(6.2831853 * uv.x + 1.7 * sin(6.2831853 * uv.y))\r\n      + 0.18 * sin(6.2831853 * (2.0 * uv.x + uv.y) + 0.6);\r\n    float grain = 0.03 * sin(6.2831853 * (9.0 * uv.x + 5.0 * uv.y));\r\n    return clamp(0.84 * bloom + 0.16 * t + grain, 0.0, 1.0);\r\n  }\r\n  if (finish == 7.0) {\r\n    float swirl = 0.5 + 0.48 * sin(6.2831853 * (uv.x + 0.35 * sin(6.2831853 * (uv.y + 0.5 * uv.x))) + 6.2831853 * uv.y);\r\n    return clamp(0.86 * swirl + 0.14 * t, 0.0, 1.0);\r\n  }\r\n  return t;\r\n}\r\n`;\r\n\r\ntype Hooked = MeshPhysicalMaterialWithHooks;\r\ntype MeshPhysicalMaterialWithHooks = THREE.MeshPhysicalMaterial & {\r\n  userData: Record<string, any>;\r\n  defines?: Record<string,string>;\r\n};\r\n\r\nfunction addHook(mat: Hooked, kind: 'glitter' | 'brushed-metal'): void {\r\n  mat.userData.__myr5FinishHooks ||= {};\r\n  const hooks = mat.userData.__myr5FinishHooks as Record<string, boolean>;\r\n  if (hooks[kind]) return;\r\n  hooks[kind] = true;\r\n  if (kind === 'glitter') mat.userData.myr5FinishGlitter = true;\r\n  mat.defines={...mat.defines,USE_UV:''};\r\n  const prev = mat.onBeforeCompile?.bind(mat);\r\n  const vary = kind === 'glitter' ? 'vMyr5GlitterUv' : 'vMyr5BrushedUv';\r\n  mat.onBeforeCompile = (shader, renderer) => {\r\n    prev?.(shader, renderer);\r\n    shader.vertexShader = shader.vertexShader\r\n      .replace('#include <common>', `#include <common>\\nvarying vec2 ${vary};`)\r\n      .replace('#include <uv_vertex>', `#include <uv_vertex>\\n${vary} = uv;`);\r\n    shader.fragmentShader = shader.fragmentShader.replace(\r\n      '#include <common>',\r\n      `#include <common>\\nvarying vec2 ${vary};`\r\n    );\r\n    if (kind === 'glitter') {\r\n      shader.fragmentShader = shader.fragmentShader.replace(\r\n        '#include <roughnessmap_fragment>',\r\n        `#include <roughnessmap_fragment>\r\n        {\r\n          vec2 gu = fract(${vary} * 83.0);\r\n          float g = fract(sin(dot(floor(${vary} * 83.0), vec2(12.9898, 78.233))) * 43758.5453);\r\n          float speck = step(0.92, g) * step(0.5, smoothstep(0.0, 0.08, min(gu.x, gu.y)));\r\n          roughnessFactor = mix(roughnessFactor,0.055,speck);\r\n        }`\r\n      );\r\n      shader.fragmentShader = shader.fragmentShader.replace('#include <dithering_fragment>', `{\r\n        float grain=fract(sin(dot(floor(vMyr5GlitterUv*83.0),vec2(12.9898,78.233)))*43758.5453);\r\n        vec3 flakeNormal=normalize(normal+vec3(sin(grain*91.0),cos(grain*73.0),0.0)*0.55);\r\n        float glint=step(0.92,grain)*pow(max(dot(flakeNormal,normalize(vViewPosition)),0.0),42.0);\r\n        gl_FragColor.rgb+=glint*1.8;\r\n      }\\n#include <dithering_fragment>`);\r\n    } else {\r\n      shader.fragmentShader = shader.fragmentShader.replace(\r\n        '#include <roughnessmap_fragment>',\r\n        `#include <roughnessmap_fragment>\r\n        roughnessFactor = clamp(roughnessFactor + 0.055 * sin(6.2831853 * (fract(${vary}.y) * 90.0 + 0.15 * sin(6.2831853 * ${vary}.x))), 0.05, 1.0);`\r\n      );\r\n    }\r\n  };\r\n  const prevKey = mat.customProgramCacheKey?.bind(mat);\r\n  mat.customProgramCacheKey = function () {\r\n    return `${prevKey ? prevKey.call(this) : ''}|myr5-${kind}`;\r\n  };\r\n  mat.needsUpdate = true;\r\n}\r\n\r\nexport function applyPaletteFinish(\r\n  mat: THREE.MeshPhysicalMaterial,\r\n  paletteId?: string,\r\n  palette?: { primary: string; secondary: string; accent: string },\r\n): void {\r\n  const p = meta(paletteId);\r\n  if (!p) return;\r\n  const f = p.finish;\r\n  mat.roughnessMap = null;\r\n  mat.clearcoat = 0; mat.clearcoatRoughness = 0;\r\n  mat.sheen = 0; (mat as any).sheenRoughness = 1; (mat as any).sheenColor?.set?.(0x000000);\r\n  mat.iridescence = 0; mat.iridescenceIOR = 1.3; mat.iridescenceThicknessRange = [100, 400];\r\n  (mat as any).anisotropy = 0;\r\n  mat.transmission = 0; mat.thickness = 0; mat.ior = 1.5;\r\n  switch (f) {\r\n    case 'matte': mat.roughness = 0.92; mat.metalness = 0; break;\r\n    case 'brushed-metal': mat.roughness = 0.44; mat.metalness = 0.82; (mat as any).anisotropy = 0.65; break;\r\n    case 'polished-metal': mat.roughness = 0.16; mat.metalness = 0.92; break;\r\n    case 'glitter': mat.roughness = 0.24; mat.metalness = 0.35; mat.clearcoat=1; mat.clearcoatRoughness=.035; mat.envMapIntensity=1.5; break;\r\n    case 'pearl':\r\n      mat.roughness = 0.3; mat.metalness = 0.08; mat.iridescence = 1;\r\n      mat.iridescenceIOR = 1.35; mat.iridescenceThicknessRange = [120, 420]; break;\r\n    case 'watercolor': mat.roughness = 0.96; mat.metalness = 0; break;\r\n    case 'marbled': mat.roughness = 0.35; mat.metalness = 0.05; break;\r\n    case 'glaze': mat.roughness = 0.24; mat.metalness = 0; mat.clearcoat = 1; mat.clearcoatRoughness = 0.08; break;\r\n  }\r\n  if (f === 'glitter') addHook(mat as Hooked, 'glitter');\r\n  if (f === 'brushed-metal') addHook(mat as Hooked, 'brushed-metal');\r\n  mat.needsUpdate = true;\r\n}\r\n","handborne/source/app/palettes.json":"[\r\n  {\r\n    \"id\": \"pal-01\",\r\n    \"name\": \"Acid Forge\",\r\n    \"tagline\": \"Lime bites oxblood and chrome\",\r\n    \"colors\": [\r\n      \"#C6FF00\",\r\n      \"#4A0E17\",\r\n      \"#C0C7CE\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 5,\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-02\",\r\n    \"name\": \"Neon Afterburn Haze\",\r\n    \"tagline\": \"Orange aqua contrails with violet flare\",\r\n    \"colors\": [\r\n      \"#FF6D00\",\r\n      \"#00E0B0\",\r\n      \"#C800FF\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 10,\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-03\",\r\n    \"name\": \"Petrel Copper\",\r\n    \"tagline\": \"Petrol depths meet intense violet\",\r\n    \"colors\": [\r\n      \"#0E5A61\",\r\n      \"#B87333\",\r\n      \"#6913E7\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 15,\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-04\",\r\n    \"name\": \"Vinaigrette Rust\",\r\n    \"tagline\": \"Oil pigment rust swirl\",\r\n    \"colors\": [\r\n      \"#8A3B12\",\r\n      \"#556B2F\",\r\n      \"#E7A213\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 20,\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-05\",\r\n    \"name\": \"Neon Relic\",\r\n    \"tagline\": \"Hot magenta neon amber on bone\",\r\n    \"colors\": [\r\n      \"#FF009F\",\r\n      \"#FFB500\",\r\n      \"#F2EFE6\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 25,\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-06\",\r\n    \"name\": \"Glacier Ember\",\r\n    \"tagline\": \"Ice teal over ember red\",\r\n    \"colors\": [\r\n      \"#13E7D1\",\r\n      \"#D9381E\",\r\n      \"#2B2530\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 30,\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-07\",\r\n    \"name\": \"Chartreuse Storm\",\r\n    \"tagline\": \"Sludge green meets slate violet\",\r\n    \"colors\": [\r\n      \"#B5C700\",\r\n      \"#3D3654\",\r\n      \"#E85D75\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 35,\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-08\",\r\n    \"name\": \"Saffron Voltage\",\r\n    \"tagline\": \"Saffron arcs on ink blue\",\r\n    \"colors\": [\r\n      \"#FFB000\",\r\n      \"#101D42\",\r\n      \"#00FAA6\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 40,\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-09\",\r\n    \"name\": \"Neon Venom Signal\",\r\n    \"tagline\": \"Poison lime warning under arterial red\",\r\n    \"colors\": [\r\n      \"#A6FF00\",\r\n      \"#FF2442\",\r\n      \"#101828\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 45,\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-10\",\r\n    \"name\": \"Mercury Moss\",\r\n    \"tagline\": \"Silver moss over saturated gold\",\r\n    \"colors\": [\r\n      \"#AEB6BC\",\r\n      \"#5C6B3C\",\r\n      \"#E7A913\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 50,\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-11\",\r\n    \"name\": \"Ultraviolet Tar\",\r\n    \"tagline\": \"Violet glow on tar black\",\r\n    \"colors\": [\r\n      \"#7B2FF7\",\r\n      \"#141414\",\r\n      \"#FFC93C\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 55,\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-12\",\r\n    \"name\": \"Salmon Circuit\",\r\n    \"tagline\": \"Salmon pink on circuit green\",\r\n    \"colors\": [\r\n      \"#FA2200\",\r\n      \"#00A86B\",\r\n      \"#2E2A5C\"\r\n    ],\r\n    \"unlockRule\": \"aura-milestone\",\r\n    \"unlockAtDay\": 60,\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-13\",\r\n    \"name\": \"Muddy Pop\",\r\n    \"tagline\": \"Ochre mud sparks cerulean\",\r\n    \"colors\": [\r\n      \"#8C6A2F\",\r\n      \"#0081C8\",\r\n      \"#E71362\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-14\",\r\n    \"name\": \"Neon Vapor Crown\",\r\n    \"tagline\": \"Laser pink blue over dark purple\",\r\n    \"colors\": [\r\n      \"#FF00AA\",\r\n      \"#00D0FF\",\r\n      \"#1B0033\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-15\",\r\n    \"name\": \"Brick Vein\",\r\n    \"tagline\": \"Brick red veined in steel\",\r\n    \"colors\": [\r\n      \"#A0342B\",\r\n      \"#8C9BA5\",\r\n      \"#D7E713\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-2:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-16\",\r\n    \"name\": \"Peacock Static\",\r\n    \"tagline\": \"Peacock green hums gold\",\r\n    \"colors\": [\r\n      \"#046A5B\",\r\n      \"#FAAF00\",\r\n      \"#5B1E4B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-17\",\r\n    \"name\": \"Foghorn Flame\",\r\n    \"tagline\": \"Fog gray with fire-orange siren\",\r\n    \"colors\": [\r\n      \"#B8C0C2\",\r\n      \"#FA3F00\",\r\n      \"#123C69\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-18\",\r\n    \"name\": \"Toxic Honey\",\r\n    \"tagline\": \"Honey drips on toxic lime\",\r\n    \"colors\": [\r\n      \"#D9A404\",\r\n      \"#9BF00B\",\r\n      \"#4A154B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-3:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-19\",\r\n    \"name\": \"Neon Crimson Mirage\",\r\n    \"tagline\": \"Scarlet toxic lime over deep jade\",\r\n    \"colors\": [\r\n      \"#FF1744\",\r\n      \"#76FF03\",\r\n      \"#005E3C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-20\",\r\n    \"name\": \"Opal Drain\",\r\n    \"tagline\": \"Opal sheen on drainpipe green\",\r\n    \"colors\": [\r\n      \"#13E7B9\",\r\n      \"#3E5641\",\r\n      \"#D1495B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-21\",\r\n    \"name\": \"Rust Halo\",\r\n    \"tagline\": \"Rust ringed by mint flash\",\r\n    \"colors\": [\r\n      \"#9C4221\",\r\n      \"#12E898\",\r\n      \"#2C2C54\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-22\",\r\n    \"name\": \"Volt Quartz\",\r\n    \"tagline\": \"Electric yellow on rose quartz\",\r\n    \"colors\": [\r\n      \"#F5E900\",\r\n      \"#E71363\",\r\n      \"#264653\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-23\",\r\n    \"name\": \"Neon Flare Garden\",\r\n    \"tagline\": \"Laser lime pink on teal steel\",\r\n    \"colors\": [\r\n      \"#8CFF00\",\r\n      \"#FF006E\",\r\n      \"#00B8A9\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-24\",\r\n    \"name\": \"Crimson Tidebreak\",\r\n    \"tagline\": \"Crimson breaks on seafoam\",\r\n    \"colors\": [\r\n      \"#B91338\",\r\n      \"#13E7B5\",\r\n      \"#2A1E5C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-5:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-25\",\r\n    \"name\": \"Brass Mirage\",\r\n    \"tagline\": \"Brass shimmer dusky plum\",\r\n    \"colors\": [\r\n      \"#C59B42\",\r\n      \"#5A2A52\",\r\n      \"#0D94ED\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-26\",\r\n    \"name\": \"Neon Infrared Bloom\",\r\n    \"tagline\": \"Red heat sliced by frozen cyan\",\r\n    \"colors\": [\r\n      \"#FF0000\",\r\n      \"#00F5FF\",\r\n      \"#5B00B5\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-27\",\r\n    \"name\": \"Ice Magma\",\r\n    \"tagline\": \"Pigmented ice over magma orange\",\r\n    \"colors\": [\r\n      \"#D6F0F0\",\r\n      \"#F45B0F\",\r\n      \"#3D2C8D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"strider-6:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-28\",\r\n    \"name\": \"Fern Radio\",\r\n    \"tagline\": \"Fern green static magenta\",\r\n    \"colors\": [\r\n      \"#3F7A3A\",\r\n      \"#E4007C\",\r\n      \"#E7BE13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-29\",\r\n    \"name\": \"Porcelain Fuel\",\r\n    \"tagline\": \"Porcelain white fuel orange\",\r\n    \"colors\": [\r\n      \"#F2F2F0\",\r\n      \"#FF7A1A\",\r\n      \"#14507A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-30\",\r\n    \"name\": \"Neon Photon Alley\",\r\n    \"tagline\": \"Yellow signage aqua and violet glass\",\r\n    \"colors\": [\r\n      \"#FFD300\",\r\n      \"#00FFCC\",\r\n      \"#7A00FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-2:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-31\",\r\n    \"name\": \"Neon Solar Flare\",\r\n    \"tagline\": \"Hot yellow ember in plum space\",\r\n    \"colors\": [\r\n      \"#FFEA00\",\r\n      \"#FF3D00\",\r\n      \"#26142B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-32\",\r\n    \"name\": \"Wet Cement Rose\",\r\n    \"tagline\": \"Cement gray wild rose\",\r\n    \"colors\": [\r\n      \"#8D9096\",\r\n      \"#D84A87\",\r\n      \"#F0E130\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-33\",\r\n    \"name\": \"Neon Laser Tropics\",\r\n    \"tagline\": \"Chartreuse beams with scorched orange shadows\",\r\n    \"colors\": [\r\n      \"#C6FF00\",\r\n      \"#FF4D00\",\r\n      \"#001219\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-3:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-34\",\r\n    \"name\": \"Copper Fog\",\r\n    \"tagline\": \"Copper glows through fog\",\r\n    \"colors\": [\r\n      \"#B46A30\",\r\n      \"#C4CBCE\",\r\n      \"#B600F5\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L1\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-35\",\r\n    \"name\": \"Laser Moss\",\r\n    \"tagline\": \"Laser cyan on moss\",\r\n    \"colors\": [\r\n      \"#00E5FF\",\r\n      \"#546B2F\",\r\n      \"#FF4FA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L3\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-36\",\r\n    \"name\": \"Bone Ember\",\r\n    \"tagline\": \"Bone white ember orange\",\r\n    \"colors\": [\r\n      \"#EDE8DC\",\r\n      \"#E4572E\",\r\n      \"#BA00FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-4:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-37\",\r\n    \"name\": \"Plum Static\",\r\n    \"tagline\": \"Plum noise with electric lime\",\r\n    \"colors\": [\r\n      \"#4A1E4F\",\r\n      \"#B8F000\",\r\n      \"#33B5C7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-38\",\r\n    \"name\": \"Neon Lime Vortex\",\r\n    \"tagline\": \"Acid green swirling through electric blue\",\r\n    \"colors\": [\r\n      \"#BFFF00\",\r\n      \"#00E5FF\",\r\n      \"#12100F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-39\",\r\n    \"name\": \"Clay Command\",\r\n    \"tagline\": \"Terracotta clay cobalt order\",\r\n    \"colors\": [\r\n      \"#C06B55\",\r\n      \"#2E4BC6\",\r\n      \"#EDE6D6\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"ringer-5:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-40\",\r\n    \"name\": \"Static Cream\",\r\n    \"tagline\": \"Cream noise shock green\",\r\n    \"colors\": [\r\n      \"#F4EDDE\",\r\n      \"#39FF14\",\r\n      \"#4A306D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-41\",\r\n    \"name\": \"Midnight Papaya\",\r\n    \"tagline\": \"Midnight blue papaya burst\",\r\n    \"colors\": [\r\n      \"#191970\",\r\n      \"#FF9E42\",\r\n      \"#13E796\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-42\",\r\n    \"name\": \"Dusty Laser\",\r\n    \"tagline\": \"Dusty mauve laser green\",\r\n    \"colors\": [\r\n      \"#A27E8E\",\r\n      \"#7CFC00\",\r\n      \"#1B3B6F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-43\",\r\n    \"name\": \"Neon Atomic Sunset\",\r\n    \"tagline\": \"Amber fallout glowing beside ultraviolet pink\",\r\n    \"colors\": [\r\n      \"#FF9100\",\r\n      \"#FF00F5\",\r\n      \"#062A2A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-44\",\r\n    \"name\": \"Fern Flame\",\r\n    \"tagline\": \"Cool fern fierce flame\",\r\n    \"colors\": [\r\n      \"#4F7942\",\r\n      \"#FF3D2E\",\r\n      \"#E75A13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-45\",\r\n    \"name\": \"Violet Quarry\",\r\n    \"tagline\": \"Quarry gray violet eruption\",\r\n    \"colors\": [\r\n      \"#7D7F87\",\r\n      \"#8B3DFF\",\r\n      \"#DCE35B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-46\",\r\n    \"name\": \"Cadmium Deep\",\r\n    \"tagline\": \"Cadmium red deep lagoon\",\r\n    \"colors\": [\r\n      \"#D32E12\",\r\n      \"#046E8F\",\r\n      \"#E79E13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-47\",\r\n    \"name\": \"Pigment Riot\",\r\n    \"tagline\": \"Intense blue maroon and hot orange\",\r\n    \"colors\": [\r\n      \"#139FE7\",\r\n      \"#6E1423\",\r\n      \"#FF7300\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-48\",\r\n    \"name\": \"Neon Prism Riot\",\r\n    \"tagline\": \"Sea-glass cyan, magenta bite, lemon flash\",\r\n    \"colors\": [\r\n      \"#00FFC6\",\r\n      \"#FF00B8\",\r\n      \"#F5FF00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-4:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-49\",\r\n    \"name\": \"Neon Tropic Surge\",\r\n    \"tagline\": \"Aqua tide, citrus flare, violet thunder\",\r\n    \"colors\": [\r\n      \"#00FF9D\",\r\n      \"#FFB000\",\r\n      \"#D000FF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-50\",\r\n    \"name\": \"Ash Honey\",\r\n    \"tagline\": \"Ash gray golden honey\",\r\n    \"colors\": [\r\n      \"#9BA0A6\",\r\n      \"#E0A100\",\r\n      \"#2F6690\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-51\",\r\n    \"name\": \"Cherry Motor\",\r\n    \"tagline\": \"Cherry motor oil with electric blue\",\r\n    \"colors\": [\r\n      \"#B22234\",\r\n      \"#3B2F2F\",\r\n      \"#005CFF\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"manyarm-5:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-52\",\r\n    \"name\": \"Glacier Punch\",\r\n    \"tagline\": \"Glacier pigmented hot punch\",\r\n    \"colors\": [\r\n      \"#13A6E7\",\r\n      \"#F52549\",\r\n      \"#3D405B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-53\",\r\n    \"name\": \"Tarnish Bloom\",\r\n    \"tagline\": \"Tarnished teal floral orange\",\r\n    \"colors\": [\r\n      \"#3A7D7B\",\r\n      \"#F4852B\",\r\n      \"#E9D6EC\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L3\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-54\",\r\n    \"name\": \"Umber Signal\",\r\n    \"tagline\": \"Umber earth neon signal\",\r\n    \"colors\": [\r\n      \"#635147\",\r\n      \"#D0FF4B\",\r\n      \"#7A28C0\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-55\",\r\n    \"name\": \"Rose Quartz Coil\",\r\n    \"tagline\": \"Soft rose coil of teal\",\r\n    \"colors\": [\r\n      \"#E7134B\",\r\n      \"#0FA3A3\",\r\n      \"#2B2118\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-56\",\r\n    \"name\": \"Sulfur Deck\",\r\n    \"tagline\": \"Sulfur yellow blue and hot orange\",\r\n    \"colors\": [\r\n      \"#E3D200\",\r\n      \"#274C77\",\r\n      \"#FF4A00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-57\",\r\n    \"name\": \"Neon Ember Static\",\r\n    \"tagline\": \"Orange yellow buzzing with royal voltage\",\r\n    \"colors\": [\r\n      \"#FF5E00\",\r\n      \"#FFE600\",\r\n      \"#3A0CA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-3:L4\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-58\",\r\n    \"name\": \"Mango Ash\",\r\n    \"tagline\": \"Mango bright volcanic ash\",\r\n    \"colors\": [\r\n      \"#FFA12B\",\r\n      \"#59595E\",\r\n      \"#64DDBB\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-59\",\r\n    \"name\": \"Steel Petal\",\r\n    \"tagline\": \"Steel gray crushed petals\",\r\n    \"colors\": [\r\n      \"#8A8F98\",\r\n      \"#E71813\",\r\n      \"#D8E26E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-60\",\r\n    \"name\": \"Ninja Cream\",\r\n    \"tagline\": \"Hidden navy cream coral\",\r\n    \"colors\": [\r\n      \"#1E2A4A\",\r\n      \"#F0EBE0\",\r\n      \"#7EEC00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-4:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-61\",\r\n    \"name\": \"Tangerine Static\",\r\n    \"tagline\": \"Tangerine buzz grape shadow\",\r\n    \"colors\": [\r\n      \"#F28500\",\r\n      \"#3D2159\",\r\n      \"#202020\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L1\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-62\",\r\n    \"name\": \"Sage Pistol\",\r\n    \"tagline\": \"Muted sage pistol pink\",\r\n    \"colors\": [\r\n      \"#9AB5A1\",\r\n      \"#E45C98\",\r\n      \"#33272A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-63\",\r\n    \"name\": \"Lagoon Battery\",\r\n    \"tagline\": \"Lagoon green charged yellow\",\r\n    \"colors\": [\r\n      \"#1D8A7A\",\r\n      \"#FAE000\",\r\n      \"#76305B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"wedge-5:L4\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-64\",\r\n    \"name\": \"Wine Cooling\",\r\n    \"tagline\": \"Warm wine cooled by mint\",\r\n    \"colors\": [\r\n      \"#701F3C\",\r\n      \"#13E78C\",\r\n      \"#E8D44D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-65\",\r\n    \"name\": \"Charcoal Tulip\",\r\n    \"tagline\": \"Charcoal tulip flame combo\",\r\n    \"colors\": [\r\n      \"#2E3033\",\r\n      \"#FF4E50\",\r\n      \"#CA00C7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-66\",\r\n    \"name\": \"Neon Acid Harbor\",\r\n    \"tagline\": \"Lime light cutting across magenta docks\",\r\n    \"colors\": [\r\n      \"#39FF14\",\r\n      \"#FF007F\",\r\n      \"#001B2E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"warden-1:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-67\",\r\n    \"name\": \"Rustic Neon\",\r\n    \"tagline\": \"Barn red barn lime buzz\",\r\n    \"colors\": [\r\n      \"#8A3324\",\r\n      \"#C1FF33\",\r\n      \"#2D4A6B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L1\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-68\",\r\n    \"name\": \"Alice Warning\",\r\n    \"tagline\": \"Alice blue caution orange\",\r\n    \"colors\": [\r\n      \"#1399E7\",\r\n      \"#FF7E1F\",\r\n      \"#EEF700\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-69\",\r\n    \"name\": \"Smog Candy\",\r\n    \"tagline\": \"Smog beige candy teal\",\r\n    \"colors\": [\r\n      \"#E79813\",\r\n      \"#00C2A8\",\r\n      \"#D8395B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-2:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-70\",\r\n    \"name\": \"Violet Furnace\",\r\n    \"tagline\": \"Furnace violet cooled sky\",\r\n    \"colors\": [\r\n      \"#6A2D9E\",\r\n      \"#F57C3B\",\r\n      \"#302012\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-71\",\r\n    \"name\": \"Khaki Storm\",\r\n    \"tagline\": \"Khaki field storm magenta\",\r\n    \"colors\": [\r\n      \"#A89B6E\",\r\n      \"#E0218A\",\r\n      \"#2A6F97\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-72\",\r\n    \"name\": \"Neon Circuit Surge\",\r\n    \"tagline\": \"Turquoise sparks over midnight violet\",\r\n    \"colors\": [\r\n      \"#00F5D4\",\r\n      \"#F72585\",\r\n      \"#0B0321\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-3:L4\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-73\",\r\n    \"name\": \"Cedar Ultraviolet\",\r\n    \"tagline\": \"Cedar wood ultraviolet light\",\r\n    \"colors\": [\r\n      \"#7A4532\",\r\n      \"#8246FF\",\r\n      \"#E9E4D8\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-74\",\r\n    \"name\": \"Frosted Chili\",\r\n    \"tagline\": \"Frost blue chili red\",\r\n    \"colors\": [\r\n      \"#13AAE7\",\r\n      \"#C13A2F\",\r\n      \"#4C5B2C\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-75\",\r\n    \"name\": \"Moss Rocket\",\r\n    \"tagline\": \"Moss green rocket pink\",\r\n    \"colors\": [\r\n      \"#5B7042\",\r\n      \"#FA0057\",\r\n      \"#061F5B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"blob-4:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-76\",\r\n    \"name\": \"Pewter Punch\",\r\n    \"tagline\": \"Pewter softened hot punch\",\r\n    \"colors\": [\r\n      \"#99A0A8\",\r\n      \"#FF3D7F\",\r\n      \"#164D37\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L1\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-77\",\r\n    \"name\": \"Neon Glacier Riot\",\r\n    \"tagline\": \"Cyan hot pink and radioactive lime\",\r\n    \"colors\": [\r\n      \"#00F0FF\",\r\n      \"#FF00E5\",\r\n      \"#CCFF00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L3\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-78\",\r\n    \"name\": \"Cola Lightning\",\r\n    \"tagline\": \"Cola brown lightning yellow\",\r\n    \"colors\": [\r\n      \"#3A2618\",\r\n      \"#F7FF00\",\r\n      \"#FA005E\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-2:L4\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-79\",\r\n    \"name\": \"Periwinkle Fuel\",\r\n    \"tagline\": \"Soft periwinkle diesel orange\",\r\n    \"colors\": [\r\n      \"#1325E7\",\r\n      \"#E0641E\",\r\n      \"#2F4730\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-80\",\r\n    \"name\": \"Jade Alarm\",\r\n    \"tagline\": \"Jade calm siren red\",\r\n    \"colors\": [\r\n      \"#57A78A\",\r\n      \"#D91E36\",\r\n      \"#E7AF13\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L3\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-81\",\r\n    \"name\": \"Neon Ultraviolet Pulse\",\r\n    \"tagline\": \"Violet beams with mint-charged current\",\r\n    \"colors\": [\r\n      \"#8C00FF\",\r\n      \"#00FFB2\",\r\n      \"#160021\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"cap-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-82\",\r\n    \"name\": \"Neon Nightcaller\",\r\n    \"tagline\": \"Cyan violet flame in black-green fog\",\r\n    \"colors\": [\r\n      \"#00FFFF\",\r\n      \"#9400FF\",\r\n      \"#131A00\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L1\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-83\",\r\n    \"name\": \"Ivory Shock\",\r\n    \"tagline\": \"Ivory calm violet shock\",\r\n    \"colors\": [\r\n      \"#F0EEE4\",\r\n      \"#7C1FD3\",\r\n      \"#FFB200\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-84\",\r\n    \"name\": \"Neon Ruby Arc\",\r\n    \"tagline\": \"Red-hot signal over green afterglow\",\r\n    \"colors\": [\r\n      \"#FF073A\",\r\n      \"#00FF87\",\r\n      \"#080F1B\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-2:L4\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-85\",\r\n    \"name\": \"Maroon Cloud\",\r\n    \"tagline\": \"Maroon heavy cloud blue\",\r\n    \"colors\": [\r\n      \"#6D1E2E\",\r\n      \"#1381E7\",\r\n      \"#E5D352\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L1\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-86\",\r\n    \"name\": \"Chrome Berry\",\r\n    \"tagline\": \"Chrome band over berry\",\r\n    \"colors\": [\r\n      \"#C3C9D1\",\r\n      \"#8E2A5C\",\r\n      \"#FFB347\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-87\",\r\n    \"name\": \"Lentil Neon\",\r\n    \"tagline\": \"Lentil tan electric aqua\",\r\n    \"colors\": [\r\n      \"#B09A6E\",\r\n      \"#00FFF0\",\r\n      \"#5F1B63\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-3:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-88\",\r\n    \"name\": \"Neon Cobalt Fuse\",\r\n    \"tagline\": \"Blue ignition wired to blazing yellow\",\r\n    \"colors\": [\r\n      \"#004DFF\",\r\n      \"#FFEE00\",\r\n      \"#0D0221\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-89\",\r\n    \"name\": \"Spruce Bonfire\",\r\n    \"tagline\": \"Dark spruce bonfire orange\",\r\n    \"colors\": [\r\n      \"#23493C\",\r\n      \"#FF7A3D\",\r\n      \"#0C1735\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-90\",\r\n    \"name\": \"Lilac Foundry\",\r\n    \"tagline\": \"Lilac meets foundry steel\",\r\n    \"colors\": [\r\n      \"#9813E7\",\r\n      \"#7B8894\",\r\n      \"#D4522A\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"stalk-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-91\",\r\n    \"name\": \"Topaz Battle\",\r\n    \"tagline\": \"Topaz gold navy clash\",\r\n    \"colors\": [\r\n      \"#F0B429\",\r\n      \"#182952\",\r\n      \"#FA007D\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L1\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-92\",\r\n    \"name\": \"Dusk Celery\",\r\n    \"tagline\": \"Dusky purple celery snap\",\r\n    \"colors\": [\r\n      \"#574B6B\",\r\n      \"#C8D86A\",\r\n      \"#FA2100\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L3\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-93\",\r\n    \"name\": \"Tide Copperfuse\",\r\n    \"tagline\": \"Tidal teal copperfuse glow\",\r\n    \"colors\": [\r\n      \"#0F7B7B\",\r\n      \"#C07840\",\r\n      \"#C2F000\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-2:L4\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-94\",\r\n    \"name\": \"Punch Driftwood\",\r\n    \"tagline\": \"Fruit punch on driftwood\",\r\n    \"colors\": [\r\n      \"#E63362\",\r\n      \"#6E5B4A\",\r\n      \"#1648EA\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L1\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"pal-95\",\r\n    \"name\": \"Ink Lime\",\r\n    \"tagline\": \"Ink smudge streaked lime\",\r\n    \"colors\": [\r\n      \"#23232A\",\r\n      \"#A8E10C\",\r\n      \"#7E10EA\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L3\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-96\",\r\n    \"name\": \"Porcelain Prism\",\r\n    \"tagline\": \"Porcelain violet over tidal blue\",\r\n    \"colors\": [\r\n      \"#F5F1E8\",\r\n      \"#BD05DA\",\r\n      \"#005F73\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-3:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-97\",\r\n    \"name\": \"Wasabi Cloud\",\r\n    \"tagline\": \"Wasabi heat cloud gray\",\r\n    \"colors\": [\r\n      \"#9FBF3B\",\r\n      \"#8E94A0\",\r\n      \"#E71363\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L1\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-98\",\r\n    \"name\": \"Eggplant Flare\",\r\n    \"tagline\": \"Eggplant deep flare yellow\",\r\n    \"colors\": [\r\n      \"#41284E\",\r\n      \"#FFD23F\",\r\n      \"#5BC0BE\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L3\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-99\",\r\n    \"name\": \"Stone Rose\",\r\n    \"tagline\": \"Stone gray antique rose\",\r\n    \"colors\": [\r\n      \"#8F8A82\",\r\n      \"#C46E8E\",\r\n      \"#2F6DA3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"tanka-4:L4\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-100\",\r\n    \"name\": \"Neon Quasar Pop\",\r\n    \"tagline\": \"Yellow green comet over indigo gravity\",\r\n    \"colors\": [\r\n      \"#FFEF00\",\r\n      \"#00FF3C\",\r\n      \"#2D02B8\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L1\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"pal-101\",\r\n    \"name\": \"Arctic Rust\",\r\n    \"tagline\": \"Arctic white rusted orange\",\r\n    \"colors\": [\r\n      \"#E9F2F5\",\r\n      \"#9E4A1D\",\r\n      \"#3E3F8F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L3\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-102\",\r\n    \"name\": \"Magenta Selvage\",\r\n    \"tagline\": \"Magenta edge denim blue\",\r\n    \"colors\": [\r\n      \"#D8117E\",\r\n      \"#3B5B8C\",\r\n      \"#E7D313\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"lume-1:L4\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"pal-103\",\r\n    \"name\": \"Haze Saffron\",\r\n    \"tagline\": \"Purple haze saffron spark\",\r\n    \"colors\": [\r\n      \"#8467B8\",\r\n      \"#F4A81D\",\r\n      \"#1D4E50\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L1\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"pal-104\",\r\n    \"name\": \"Neon Cyber Reed\",\r\n    \"tagline\": \"Green circuitry humming beside lemon static\",\r\n    \"colors\": [\r\n      \"#00FF66\",\r\n      \"#FFEA00\",\r\n      \"#4B0082\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L2\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"pal-105\",\r\n    \"name\": \"Neon Magma Bloom\",\r\n    \"tagline\": \"Molten orange meeting synthetic magenta\",\r\n    \"colors\": [\r\n      \"#FF2E00\",\r\n      \"#F000FF\",\r\n      \"#0A0A0F\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L3\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"pal-106\",\r\n    \"name\": \"Dune Violet\",\r\n    \"tagline\": \"Warm dune electric violet\",\r\n    \"colors\": [\r\n      \"#C9AE7E\",\r\n      \"#7D2BF0\",\r\n      \"#13E7E3\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L4\",\r\n    \"finish\": \"marbled\"\r\n  },\r\n  {\r\n    \"id\": \"pal-107\",\r\n    \"name\": \"Ember Nightshade\",\r\n    \"tagline\": \"Violet shadows ember teal\",\r\n    \"colors\": [\r\n      \"#523090\",\r\n      \"#EF4E23\",\r\n      \"#0C5D68\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"reward\": \"food:L5\",\r\n    \"finish\": \"watercolor\"\r\n  },\r\n  {\r\n    \"id\": \"opal-jelly\",\r\n    \"name\": \"Opal Jelly\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#b3fff0\",\r\n      \"#9767ff\",\r\n      \"#fff1ff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"bubble-glass\",\r\n    \"name\": \"Bubble Glass\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#58eaff\",\r\n      \"#b8fff8\",\r\n      \"#ffffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"prism-crystal\",\r\n    \"name\": \"Prism Crystal\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#b18aff\",\r\n      \"#447bff\",\r\n      \"#fff2ff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"holo-foil\",\r\n    \"name\": \"Holographic Foil\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#ff54de\",\r\n      \"#39e5ff\",\r\n      \"#fff56b\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"pearl\"\r\n  },\r\n  {\r\n    \"id\": \"glitter-resin\",\r\n    \"name\": \"Glitter Resin\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#ff319c\",\r\n      \"#7025ff\",\r\n      \"#fff6db\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"galaxy-geode\",\r\n    \"name\": \"Galaxy Geode\",\r\n    \"tagline\": \"Shifting highlights and luminous color\",\r\n    \"colors\": [\r\n      \"#21134e\",\r\n      \"#7b39e8\",\r\n      \"#63ffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glitter\"\r\n  },\r\n  {\r\n    \"id\": \"caustic-slime\",\r\n    \"name\": \"Caustic Slime\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#8aa52b\",\r\n      \"#20300b\",\r\n      \"#dcff57\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"blister-hide\",\r\n    \"name\": \"Blister Hide\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#9e435f\",\r\n      \"#2d1727\",\r\n      \"#e7bb72\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"rotten-rind\",\r\n    \"name\": \"Rotten Rind\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#626124\",\r\n      \"#211916\",\r\n      \"#a2b16b\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"matte\"\r\n  },\r\n  {\r\n    \"id\": \"parasite-nest\",\r\n    \"name\": \"Parasite Nest\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#786541\",\r\n      \"#151c14\",\r\n      \"#ece2b0\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"exposed-sinew\",\r\n    \"name\": \"Exposed Sinew\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#b72945\",\r\n      \"#300b24\",\r\n      \"#efb5a1\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"abyssal-maw\",\r\n    \"name\": \"Abyssal Maw\",\r\n    \"tagline\": \"Organic horror surface\",\r\n    \"colors\": [\r\n      \"#522045\",\r\n      \"#100d19\",\r\n      \"#d9e6a7\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  },\r\n  {\r\n    \"id\": \"circuit-alloy\",\r\n    \"name\": \"Circuit Alloy\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#273f50\",\r\n      \"#0a111c\",\r\n      \"#4effdc\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"servo-armor\",\r\n    \"name\": \"Servo Armor\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#617080\",\r\n      \"#141b25\",\r\n      \"#ff6739\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"brushed-metal\"\r\n  },\r\n  {\r\n    \"id\": \"chrome-rib\",\r\n    \"name\": \"Chrome Rib\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#bfcde1\",\r\n      \"#202f46\",\r\n      \"#ffffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"polished-metal\"\r\n  },\r\n  {\r\n    \"id\": \"carbon-mech\",\r\n    \"name\": \"Carbon Mech\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#323d46\",\r\n      \"#080d15\",\r\n      \"#95bac9\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\"\r\n  },\r\n  {\r\n    \"id\": \"hazard-panel\",\r\n    \"name\": \"Hazard Panel\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#edb932\",\r\n      \"#131a20\",\r\n      \"#ffe9a2\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\"\r\n  },\r\n  {\r\n    \"id\": \"reactor-glass\",\r\n    \"name\": \"Reactor Glass\",\r\n    \"tagline\": \"Machine surface\",\r\n    \"colors\": [\r\n      \"#48e8ff\",\r\n      \"#063a55\",\r\n      \"#d4ffff\"\r\n    ],\r\n    \"unlockRule\": \"battle-pass\",\r\n    \"finish\": \"glaze\"\r\n  }\r\n]\r\n","handborne/source/app/pose-rig.ts":"import * as THREE from 'three';\r\nimport { DIGITS, type Digit, type Pose } from './poses.ts';\r\n\r\n// Rest centerlines are shared with the anatomical sculpt (converted to Y-up).\r\nexport const REST_SOURCE:Record<Digit,number[][]>={\r\n index:[[-.028,0,.048],[-.032,.003,.086],[-.033,.010,.112],[-.032,.018,.132]],\r\n middle:[[-.008,-.001,.054],[-.009,.002,.097],[-.009,.011,.126],[-.008,.021,.148]],\r\n ring:[[.013,0,.050],[.015,.004,.090],[.017,.014,.115],[.018,.025,.135]],\r\n pinky:[[.031,.002,.039],[.038,.006,.069],[.042,.015,.089],[.043,.026,.105]],\r\n thumb:[[-.028,.010,-.021],[-.049,.016,.003],[-.067,.020,.023],[-.077,.028,.044]],\r\n};\r\nexport const DIGIT_RADII=[.0092,.0098,.0089,.0073,.0108];\r\nconst radii=DIGIT_RADII;\r\nconst axisX=new THREE.Vector3(1,0,0),axisY=new THREE.Vector3(0,1,0),axisZ=new THREE.Vector3(0,0,1);\r\nconst rest=DIGITS.map(d=>REST_SOURCE[d].map(([x,y,z])=>new THREE.Vector3(x,z,-y)));\r\nexport const BABY_RIG_SCALE=new THREE.Vector3(.78,.40,.95);\r\ntype RigProfile='standard'|'baby';\r\nconst makeBones=(landmarks:THREE.Vector3[][])=>landmarks.map(points=>points.slice(0,3).map((p,i)=>{\r\n  const dir=points[i+1].clone().sub(p),length=dir.length();dir.normalize();\r\n  const side=dir.clone().cross(axisZ).normalize(),back=side.clone().cross(dir).normalize();\r\n  const quaternion=new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(side,dir,back));\r\n  const inverse=new THREE.Matrix4().compose(p,quaternion,new THREE.Vector3(1,1,1)).invert();\r\n  return {p,dir,length,quaternion,inverse};\r\n}));\r\nconst profileBones={standard:makeBones(rest),baby:makeBones(rest.map(points=>points.map(p=>p.clone().multiply(BABY_RIG_SCALE))))};\r\nconst smooth=(x:number)=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};\r\nexport function poseMatrices(pose:Pose,profile:RigProfile='standard'):THREE.Matrix4[] {\r\n  const bones=profileBones[profile];\r\n  const matrices=[new THREE.Matrix4()],rad=Math.PI/180;\r\n  for(let d=0;d<5;d++){\r\n    const angles=pose.fingers[DIGITS[d]];let previousQ=new THREE.Quaternion(),previousP=new THREE.Vector3();\r\n    for(let i=0;i<3;i++){\r\n      const bone=bones[d][i];let q:THREE.Quaternion,p:THREE.Vector3;\r\n      if(i===0){\r\n        q=new THREE.Quaternion().setFromAxisAngle(axisZ,angles[3]*rad);\r\n        if(d===4)q.multiply(new THREE.Quaternion().setFromAxisAngle(axisY,-pose.opposition*rad));\r\n        q.multiply(bone.quaternion);p=bone.p.clone();\r\n      }else{\r\n        q=previousQ.clone().multiply(bones[d][i-1].quaternion.clone().invert().multiply(bone.quaternion));\r\n        p=previousP.clone().add(new THREE.Vector3(0,bones[d][i-1].length,0).applyQuaternion(previousQ));\r\n      }\r\n      q.multiply(new THREE.Quaternion().setFromAxisAngle(axisX,-angles[i]*rad));\r\n      matrices.push(new THREE.Matrix4().compose(p,q,new THREE.Vector3(1,1,1)).multiply(bone.inverse));\r\n      previousQ=q;previousP=p;\r\n    }\r\n  }\r\n  const rotation=new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...pose.rotation.map(v=>v*rad) as [number,number,number]));\r\n  return matrices.map(m=>rotation.clone().multiply(m));\r\n}\r\nexport function fingerTip(pose:Pose,digit:Digit):THREE.Vector3 {\r\n  const index=DIGITS.indexOf(digit);return rest[index][3].clone().applyMatrix4(poseMatrices(pose)[index*3+3]);\r\n}\r\n\r\nfunction influences(x:number,y:number,z:number,profile:RigProfile='standard'):[number,number][] {\r\n  const bones=profileBones[profile],size=profile==='baby'?.46:1;\r\n  const radii=DIGIT_RADII.map(r=>r*(profile==='baby'?.86:1));\r\n  if(y<-.033*(profile==='baby'?BABY_RIG_SCALE.y:1))return [[0,1]];\r\n  const candidates:{score:number;gain:number;d:number;i:number;t:number}[]=[];\r\n  for(let d=0;d<5;d++){\r\n    let min=Infinity,segment=0,t=0;\r\n    for(let i=0;i<3;i++){\r\n      const b=bones[d][i],dx=x-b.p.x,dy=y-b.p.y,dz=z-b.p.z;\r\n      const u=Math.max(0,Math.min(1,(dx*b.dir.x+dy*b.dir.y+dz*b.dir.z)/b.length));\r\n      const ex=dx-b.dir.x*u*b.length,ey=dy-b.dir.y*u*b.length,ez=dz-b.dir.z*u*b.length;\r\n      const dist=ex*ex+ey*ey+ez*ez;if(dist<min){min=dist;segment=i;t=u;}\r\n    }\r\n    const base=bones[d][0],along=(x-base.p.x)*base.dir.x+(y-base.p.y)*base.dir.y+(z-base.p.z)*base.dir.z;\r\n    const gain=smooth((along+.007*size)/(.020*size))*(1-smooth((Math.sqrt(min)/radii[d]-1.45)/1.1));\r\n    candidates.push({score:1/Math.pow(.08+min/(radii[d]*radii[d]),4),gain,d,i:segment,t});\r\n  }\r\n  candidates.sort((a,b)=>b.score-a.score);\r\n  const chosen=candidates.slice(0,2),total=chosen.reduce((s,c)=>s+c.score,0),weights=new Map<number,number>();\r\n  const add=(id:number,w:number)=>weights.set(id,(weights.get(id)??0)+w);\r\n  for(const c of chosen){\r\n    const fraction=c.score/total,active=fraction*c.gain,id=c.d*3+c.i+1;add(0,fraction-active);\r\n    const previous=c.i>0?.5*(1-smooth(c.t/.20)):0,next=c.i<2?.5*smooth((c.t-.80)/.20):0;\r\n    add(id,active*(1-previous-next));if(previous)add(id-1,active*previous);if(next)add(id+1,active*next);\r\n  }\r\n  const result=[...weights].sort((a,b)=>b[1]-a[1]).slice(0,4),sum=result.reduce((s,[,w])=>s+w,0);\r\n  return result.map(([id,w])=>[id,w/sum]);\r\n}\r\ntype BoundMesh={mesh:THREE.Mesh;profile:RigProfile;positions:Float32Array;normals:Float32Array;ids:Uint8Array;weights:Float32Array;inverse:THREE.Matrix4;inverseNormal:THREE.Matrix3};\r\nexport type HandRig={group:THREE.Group;meshes:BoundMesh[];dispose:()=>void};\r\nexport function bindHand(group:THREE.Group):HandRig {\r\n  group.updateMatrixWorld(true);const bound:BoundMesh[]=[];\r\n  group.traverse(obj=>{\r\n    if(!(obj instanceof THREE.Mesh))return;\r\n    const original=obj.geometry;obj.geometry=original.clone();if(obj.userData.ownedGeometry)original.dispose();obj.userData.posedGeometry=true;\r\n    const pos=obj.geometry.getAttribute('position'),normal=obj.geometry.getAttribute('normal');\r\n    const positions=new Float32Array(pos.count*3),normals=new Float32Array(pos.count*3),ids=new Uint8Array(pos.count*4),weights=new Float32Array(pos.count*4);\r\n    const worldNormal=new THREE.Matrix3().getNormalMatrix(obj.matrixWorld),p=new THREE.Vector3(),n=new THREE.Vector3();\r\n    const profile:RigProfile=obj.userData.rig_profile==='baby'?'baby':'standard';\r\n    // Every strand or scale shares its attachment weights, so fingers cannot tear it apart.\r\n    const furRoots=obj.geometry.getAttribute('_fur_root')??obj.geometry.getAttribute('_surface_root');\r\n    let rootX=NaN,rootY=NaN,rootZ=NaN,rootWeights:[number,number][]=[];\r\n    for(let i=0;i<pos.count;i++){\r\n      p.fromBufferAttribute(pos,i).applyMatrix4(obj.matrixWorld);n.fromBufferAttribute(normal,i).applyNormalMatrix(worldNormal);\r\n      p.toArray(positions,i*3);n.toArray(normals,i*3);\r\n      const nailDigit=obj.userData.region==='nails'?DIGITS.indexOf(obj.userData.digit):-1;\r\n      const explicitBone=obj.userData.rig_bone;\r\n      if(furRoots){\r\n        const x=furRoots.getX(i),y=furRoots.getY(i),z=furRoots.getZ(i);\r\n        if(x!==rootX||y!==rootY||z!==rootZ){rootX=x;rootY=y;rootZ=z;rootWeights=influences(x,y,z,profile);}\r\n      }\r\n      const vertexWeights=Number.isInteger(explicitBone)&&explicitBone>=0&&explicitBone<=15?[[explicitBone,1]]:nailDigit>=0?[[nailDigit*3+3,1]]:furRoots?rootWeights:influences(p.x,p.y,p.z,profile);\r\n      for(const[j,[id,w]]of vertexWeights.entries()){ids[i*4+j]=id;weights[i*4+j]=w;}\r\n    }\r\n    bound.push({mesh:obj,profile,positions,normals,ids,weights,inverse:obj.matrixWorld.clone().invert(),inverseNormal:worldNormal.clone().invert()});\r\n  });\r\n  return {group,meshes:bound,dispose(){const materials=new Set<THREE.Material>();for(const item of bound){item.mesh.geometry.dispose();if(item.mesh.userData.ownedMaterial){for(const m of Array.isArray(item.mesh.material)?item.mesh.material:[item.mesh.material])materials.add(m);for(const m of item.mesh.userData.sourceMaterials??[])materials.add(m);}}materials.forEach(m=>m.dispose());}};\r\n}\r\nexport function applyHandPose(rig:HandRig,pose:Pose) {\r\n  const profileMatrices={standard:poseMatrices(pose).map(m=>m.elements),baby:poseMatrices(pose,'baby').map(m=>m.elements)},min=new THREE.Vector3(Infinity,Infinity,Infinity),max=new THREE.Vector3(-Infinity,-Infinity,-Infinity);\r\n  const p=new THREE.Vector3(),n=new THREE.Vector3();\r\n  for(const bound of rig.meshes){\r\n    const {mesh,positions,normals,ids,weights}=bound,pos=mesh.geometry.getAttribute('position'),normal=mesh.geometry.getAttribute('normal');\r\n    const matrices=profileMatrices[bound.profile];\r\n    for(let i=0;i<pos.count;i++){\r\n      const k=i*3,x=positions[k],y=positions[k+1],z=positions[k+2],nx=normals[k],ny=normals[k+1],nz=normals[k+2];\r\n      let px=0,py=0,pz=0,ox=0,oy=0,oz=0;\r\n      for(let j=0;j<4;j++){\r\n        const w=weights[i*4+j];if(!w)continue;const m=matrices[ids[i*4+j]];\r\n        px+=w*(m[0]*x+m[4]*y+m[8]*z+m[12]);py+=w*(m[1]*x+m[5]*y+m[9]*z+m[13]);pz+=w*(m[2]*x+m[6]*y+m[10]*z+m[14]);\r\n        ox+=w*(m[0]*nx+m[4]*ny+m[8]*nz);oy+=w*(m[1]*nx+m[5]*ny+m[9]*nz);oz+=w*(m[2]*nx+m[6]*ny+m[10]*nz);\r\n      }\r\n      p.set(px,py,pz);min.min(p);max.max(p);p.applyMatrix4(bound.inverse);pos.setXYZ(i,p.x,p.y,p.z);\r\n      n.set(ox,oy,oz).applyMatrix3(bound.inverseNormal).normalize();normal.setXYZ(i,n.x,n.y,n.z);\r\n    }\r\n    pos.needsUpdate=true;normal.needsUpdate=true;mesh.geometry.computeBoundingBox();mesh.geometry.computeBoundingSphere();\r\n    // Exporters regenerate tangents from the posed normals and preserved UVs.\r\n    mesh.geometry.deleteAttribute('tangent');\r\n  }\r\n  const center=min.add(max).multiplyScalar(.5);rig.group.position.set(-center.x,.035-center.y,-center.z);\r\n  rig.group.userData.pose=pose.id;rig.group.userData.pose_name=pose.name;rig.group.updateMatrixWorld(true);\r\n}\r\n","handborne/source/app/poses.ts":"export const DIGITS = ['index','middle','ring','pinky','thumb'] as const;\r\nexport type Digit = typeof DIGITS[number];\r\nexport type FingerPose = [number,number,number,number]; // MCP, PIP, DIP flexion; spread, degrees\r\nexport type Pose = { id:string; name:string; description:string; fingers:Record<Digit,FingerPose>; opposition:number; rotation:[number,number,number] };\r\nconst relaxed:FingerPose=[0,0,0,0],straight:FingerPose=[-3,-8,-7,0],fold:FingerPose=[72,88,42,0];\r\nconst thumbIn:FingerPose=[0,6,8,-60];\r\nfunction pose(id:string,name:string,description:string,fingers:Partial<Record<Digit,FingerPose>>={},opposition=0,rotation:[number,number,number]=[0,0,0]):Pose {\r\n  return {id,name,description,fingers:{index:[...relaxed],middle:[...relaxed],ring:[...relaxed],pinky:[...relaxed],thumb:[...relaxed],...fingers},opposition,rotation};\r\n}\r\nconst fist={index:fold,middle:fold,ring:fold,pinky:fold,thumb:thumbIn};\r\nexport const POSES:Pose[]=[\r\n  pose('relaxed','Relaxed','The original relaxed, open hand.'),\r\n  pose('high-five','High Five','An open palm with all five fingers spread.',{index:[-3,-8,-7,8],middle:straight,ring:[-3,-8,-7,-7],pinky:[-3,-8,-7,-13],thumb:[-6,-6,-5,12]},0,[0,180,0]),\r\n  pose('fist-bump','Fist Bump','Closed fingers, with the knuckles leading.',fist,10,[58,0,-12]),\r\n  pose('peace','Peace / Victory','Index and middle fingers form a V.',{...fist,index:[-3,-8,-7,12],middle:[-3,-8,-7,-7]},10),\r\n  pose('rock-on','Rock On','Index and pinky up, thumb over the folded fingers.',{...fist,index:[-3,-8,-7,6],pinky:[-3,-8,-7,-10]},10),\r\n  pose('i-love-you','I Love You','Thumb, index and pinky extended.',{index:straight,middle:fold,ring:fold,pinky:straight,thumb:[-4,-5,-6,8]}),\r\n  pose('web-shooter','Web Shooter','Middle and ring fingers press toward the palm.',{index:[-3,-8,-7,5],middle:[65,80,58,0],ring:[65,80,58,0],pinky:[-3,-8,-7,-8],thumb:[-8,-6,-7,15]},0,[-8,180,-18]),\r\n  pose('vulcan','Vulcan Salute','Two paired fingers, separated down the middle.',{index:[-3,-8,-7,6],middle:[-3,-8,-7,12],ring:[-3,-8,-7,-11],pinky:[-3,-8,-7,-5],thumb:[-4,-4,-4,10]}),\r\n  pose('thumbs-up','Thumbs Up','A closed fist with the thumb held up.',{...fist,thumb:[-5,-8,-7,0]},0,[0,0,-51]),\r\n  pose('thumbs-down','Thumbs Down','A closed fist with the thumb pointing down.',{...fist,thumb:[-5,-8,-7,0]},0,[0,0,129]),\r\n  pose('finger-gun','Finger Gun','Index forward, thumb raised, other fingers curled.',{...fist,index:straight,thumb:[-8,-7,-5,13]},0,[0,0,-70]),\r\n  pose('point-up','Point Up','One index finger raised.',{...fist,index:straight},10),\r\n  pose('point-at-you','Point at You','An index finger aimed through the glass.',{...fist,index:straight},10,[72,-12,0]),\r\n  pose('ok','OK Sign','Thumb and index form a circle.',{index:[35,72,46,4],middle:[-2,-6,-4,-1],ring:[3,0,0,-5],pinky:[6,3,0,-12],thumb:[-14.5,26.4,36.8,-38]},10.9,[0,55,0]),\r\n  pose('pinch','Pinch','Thumb and index meet in a small precision pinch.',{index:[40,62,40,2],middle:[45,52,28,0],ring:[53,60,30,0],pinky:[60,65,32,0],thumb:[-.7,10.1,31.6,-35.3]},14.9,[0,55,0]),\r\n  pose('crossed-fingers','Fingers Crossed','Index and middle crossed for good luck.',{...fist,index:[-9,-3,-5,-11],middle:[8,0,-3,17]},10),\r\n  pose('pinky-promise','Pinky Promise','A single pinky extended from a loose fist.',{...fist,pinky:[-3,-8,-7,-7]},10),\r\n  pose('hang-loose','Hang Loose / Shaka','Thumb and pinky out; the middle three fingers curled.',{...fist,pinky:[-3,-8,-7,-10],thumb:[-7,-8,-6,12]},0,[0,0,-25]),\r\n  pose('scout-salute','Three-finger Salute','Index, middle and ring raised together.',{...fist,index:[-3,-8,-7,-2],middle:straight,ring:[-3,-8,-7,2]},10),\r\n  pose('force-grip','Force Grip','A natural, open grasp with all fingers curved.',{index:[26,32,17,9],middle:[32,35,19,2],ring:[38,39,21,-5],pinky:[43,42,24,-12],thumb:[8,13,10,5]},20,[-10,-20,8]),\r\n];\r\nexport const DEFAULT_POSE='relaxed';\r\nexport function getPose(id:string):Pose { const found=POSES.find(p=>p.id===id);if(!found)throw new Error('Unknown hand pose.');return found; }\r\nexport function validatePose(value:unknown):string {if(typeof value!=='string')throw new Error('Choose a valid hand pose.');getPose(value);return value;}\r\nexport function mixPoses(a:Pose,b:Pose,t:number):Pose {\r\n  if(t>=1)return b;\r\n  const lerp=(x:number,y:number)=>x+(y-x)*t;\r\n  return {...b,fingers:Object.fromEntries(DIGITS.map(d=>[d,a.fingers[d].map((v,i)=>lerp(v,b.fingers[d][i]))])) as Record<Digit,FingerPose>,opposition:lerp(a.opposition,b.opposition),rotation:a.rotation.map((v,i)=>v+((b.rotation[i]-v+540)%360-180)*t) as Pose['rotation']};\r\n}\r\n","handborne/source/app/recipe.ts":"import {isHandStyleUnlocked} from './material-access';\r\nimport { DEFAULT_SELECTION, REGIONS, STYLES, hasStyle, PICKER_STYLES, type RegionId } from './catalog.ts';\r\nimport { DEFAULT_POSE, POSES, validatePose } from './poses.ts';\r\nimport { DEFAULT_NAIL_SHAPE, NAIL_SHAPES, validateNailShape } from './nails.ts';\r\nimport { SCALE_PATTERNS, validateScalePattern } from './scale-patterns.ts';\r\n\r\nexport type Selection = Record<RegionId, number>;\r\nexport type HandDesign = {sections:Selection;pose:string;nailShape:string;scalePattern?:string};\r\nconst legacyRegions=['nails','fingertips','middle_sections','knuckles','palm','back_of_hand','wrist'];\r\nconst nailIds=[DEFAULT_NAIL_SHAPE,...NAIL_SHAPES.map(s=>s.id)];\r\nconst two=(n:number)=>String(n).padStart(2,'0');\r\nexport const recipeCode=(sections:Selection)=>'HB3-'+REGIONS.map(r=>two(sections[r.id]+1)).join('-')+'-01-01';\r\nexport function designCode(design:HandDesign){\r\n  const core=recipeCode(design.sections).split('-').slice(0,7).join('-')+'-'+two(POSES.findIndex(p=>p.id===validatePose(design.pose))+1)+'-'+two(nailIds.indexOf(validateNailShape(design.nailShape))+1);\r\n  const pattern=validateScalePattern(design.scalePattern??'none');\r\n  return pattern==='none'?core:core.replace('HB3-','HB4-')+'-'+two(SCALE_PATTERNS.findIndex(s=>s.id===pattern)+1);\r\n}\r\nexport function validateSelection(value: unknown): Selection {\r\n  if (!value || typeof value !== 'object') throw new Error('A recipe must contain all six sections.');\r\n  const source = value as Record<string, unknown>;\r\n  if (Object.keys(source).length !== 6) throw new Error('A recipe needs exactly six sections.');\r\n  const result = {} as Selection;\r\n  for (const { id } of REGIONS) {\r\n    if (typeof source[id] !== 'number' || !Number.isInteger(source[id]) || source[id] < 0 || !hasStyle(source[id] as number)) throw new Error(`Invalid style for ${id}. Choose 1–${STYLES.length}.`);\r\n    result[id] = source[id] as number;\r\n  }\r\n  return result;\r\n}\r\nfunction migrateLegacy(value:unknown):Selection {\r\n  if(!value||typeof value!=='object'||Object.keys(value).length!==7)throw new Error('Older recipes need seven original sections.');\r\n  const v=value as Record<string,unknown>;\r\n  for(const id of legacyRegions)if(typeof v[id]!=='number'||!Number.isInteger(v[id])||(v[id] as number)<0||(v[id] as number)>19)throw new Error('Invalid legacy style.');\r\n  // The old finger-shaft choice now styles the joined finger/knuckle surface.\r\n  return validateSelection({nails:v.nails,fingertips:v.fingertips,fingers:v.middle_sections,palm:v.palm,back_of_hand:v.back_of_hand,wrist:v.wrist});\r\n}\r\nexport function parseDesign(text:string):HandDesign {\r\n  const input=text.trim();\r\n  if(input.startsWith('HB3-')||input.startsWith('HB4-')){\r\n    const scales=input.startsWith('HB4-');\r\n    if(!(scales?/^HB4(?:-\\d{2}){9}$/:/^HB3(?:-\\d{2}){8}$/).test(input))throw new Error('Invalid recipe: six styles, a pose, nails and (HB4) scales are required.');\r\n    const v=input.split('-').slice(1).map(Number);\r\n    if(!POSES[v[6]-1]||!nailIds[v[7]-1])throw new Error('Invalid pose or nail shape in recipe.');\r\n    const pattern=scales?validateScalePattern(SCALE_PATTERNS[v[8]-1]?.id):'none';\r\n    return {sections:validateSelection(Object.fromEntries(REGIONS.map((r,i)=>[r.id,v[i]-1]))),pose:POSES[v[6]-1].id,nailShape:nailIds[v[7]-1],...(pattern==='none'?{}:{scalePattern:pattern})};\r\n  }\r\n  if(input.startsWith('HB1-')||input.startsWith('HB2-')){\r\n    const v=input.split('-'),version=v.shift();\r\n    if(v.length!==(version==='HB1'?7:8)||v.some(n=>!/^(0[1-9]|1[0-9]|20)$/.test(n)))throw new Error('Invalid older recipe code.');\r\n    return {sections:migrateLegacy(Object.fromEntries(legacyRegions.map((r,i)=>[r,Number(v[i])-1]))),pose:version==='HB1'?DEFAULT_POSE:POSES[Number(v[7])-1].id,nailShape:DEFAULT_NAIL_SHAPE};\r\n  }\r\n  let parsed:{version:number;sections:unknown;pose?:unknown;nailShape?:unknown;scalePattern?:unknown};\r\n  try{parsed=JSON.parse(input);}catch{throw new Error('Paste a Handborne recipe code or recipe JSON.');}\r\n  if(!parsed||![1,2,3,4].includes(parsed.version))throw new Error('Unsupported recipe version.');\r\n  const pattern=parsed.version===4?validateScalePattern(parsed.scalePattern??'none'):'none';\r\n  return {sections:parsed.version>=3?validateSelection(parsed.sections):migrateLegacy(parsed.sections),pose:parsed.version===1?DEFAULT_POSE:validatePose(parsed.pose),nailShape:parsed.version>=3?validateNailShape(parsed.nailShape):DEFAULT_NAIL_SHAPE,...(pattern==='none'?{}:{scalePattern:pattern})};\r\n}\r\nexport function parseRecipe(text:string):Selection{return parseDesign(text).sections;}\r\nexport function randomize(sections: Selection, locks: RegionId[], seed: number): Selection {\r\n  let state = seed >>> 0;\r\n  const next = { ...sections };\r\n  for (const { id } of REGIONS) {\r\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\r\n    if (!locks.includes(id)) {const choices=PICKER_STYLES.filter(style=>style.id!==sections[id]&&isHandStyleUnlocked(style.id));next[id]=choices[state%choices.length].id;}\r\n  }\r\n  return next;\r\n}\r\nexport const emptyLocks: RegionId[] = [];\r\nexport const initialSelection: Selection = { ...DEFAULT_SELECTION };\r\n","handborne/source/app/scale-patterns.ts":"export const SCALE_PATTERNS = [\r\n  {id:'none',name:'Off'},\r\n  {id:'round',name:'Round'},\r\n  {id:'diamond',name:'Diamond'},\r\n  {id:'hex',name:'Hex'},\r\n  {id:'shield',name:'Shield'},\r\n] as const;\r\nexport type ScalePattern = typeof SCALE_PATTERNS[number]['id'];\r\nexport function validateScalePattern(value:unknown):ScalePattern {\r\n  if(typeof value!=='string'||!SCALE_PATTERNS.some(s=>s.id===value))throw new Error('Choose Off, Round, Diamond, Hex or Shield scales.');\r\n  return value as ScalePattern;\r\n}\r\n","handborne/source/app/scales.ts":"import * as THREE from 'three';\r\nimport { validateScalePattern, type ScalePattern } from './scale-patterns.ts';\r\n\r\ntype Point = [number,number];\r\nconst outlines:Record<Exclude<ScalePattern,'none'>,Point[]>={\r\n  round:Array.from({length:12},(_,i)=>[Math.cos(i*Math.PI/6),Math.sin(i*Math.PI/6)*1.08]),\r\n  diamond:[[0,1.25],[-.87,0],[0,-1.25],[.87,0]],\r\n  hex:Array.from({length:6},(_,i)=>[Math.cos(i*Math.PI/3),Math.sin(i*Math.PI/3)]),\r\n  shield:[[0,1.35],[-.88,.30],[-.88,-.65],[-.55,-.95],[.55,-.95],[.88,-.65],[.88,.30]],\r\n};\r\n\r\n/** Sample a staggered lattice on each surface, then add real, separately rooted plates.\r\n * Source meshes and their shared geometry/materials are never modified.\r\n * The same routine is used for the live hand and its GLB/OBJ exports. */\r\nexport function addHandScales(group:THREE.Group,value:string='none') {\r\n  const pattern=validateScalePattern(value);group.userData.scale_pattern=pattern;\r\n  if(pattern==='none')return;\r\n  group.updateMatrixWorld(true);\r\n  const sources:THREE.Mesh[]=[];\r\n  group.traverse(o=>{\r\n    if(!(o instanceof THREE.Mesh)||o.userData.region==='nails'||o.userData.trapped_bubble||o.geometry.hasAttribute('_fur_root')||o.userData.surface_pattern)return;\r\n    if(String(o.userData.source_part??'').endsWith('_sculpt')||o.userData.structure_piece)sources.push(o);\r\n  });\r\n  // One spatial hash across module boundaries avoids double-stamping their seams.\r\n  const occupied=new Map<string,THREE.Vector3[]>(),cell=.0024;\r\n  const accept=(p:THREE.Vector3,distance:number)=>{\r\n    const x=Math.floor(p.x/cell),y=Math.floor(p.y/cell),z=Math.floor(p.z/cell),range=Math.ceil(distance/cell);\r\n    for(let dx=-range;dx<=range;dx++)for(let dy=-range;dy<=range;dy++)for(let dz=-range;dz<=range;dz++){\r\n      const near=occupied.get(`${x+dx},${y+dy},${z+dz}`);\r\n      if(near?.some(other=>other.distanceToSquared(p)<distance*distance))return false;\r\n    }\r\n    const key=`${x},${y},${z}`;if(!occupied.has(key))occupied.set(key,[]);occupied.get(key)!.push(p.clone());return true;\r\n  };\r\n  for(const source of sources){\r\n    if(!source.parent)continue;\r\n    const geometry=source.geometry,position=geometry.getAttribute('position'),normal=geometry.getAttribute('normal'),uv=geometry.getAttribute('uv'),index=geometry.index;\r\n    if(!position||!normal)continue;\r\n    const small=source.userData.rig_profile==='baby',spacing=small?.0031:.0056,rowHeight=spacing*.87;\r\n    const radius=spacing*.49,height=spacing*(pattern==='round'?.15:pattern==='hex'?.20:pattern==='diamond'?.24:.28);\r\n    const inverse=source.parent.matrixWorld.clone().invert(),worldNormal=new THREE.Matrix3().getNormalMatrix(source.matrixWorld);\r\n    const vertices: number[]=[],roots:number[]=[],uvs:number[]=[],indices:number[]=[];\r\n    const a=new THREE.Vector3(),b=new THREE.Vector3(),c=new THREE.Vector3(),face=new THREE.Vector3(),edge=new THREE.Vector3();\r\n    const point=new THREE.Vector3(),n=new THREE.Vector3(),na=new THREE.Vector3(),nb=new THREE.Vector3(),nc=new THREE.Vector3();\r\n    const forward=new THREE.Vector3(),side=new THREE.Vector3(),v=new THREE.Vector3(),outline=outlines[pattern];\r\n    let count=0;\r\n    const stamp=(u:number,w:number)=>{\r\n      // Project the finger-length direction onto the surface; fall back at end caps.\r\n      forward.set(0,1,0).addScaledVector(n,-n.y);\r\n      if(forward.lengthSq()<.05)forward.set(0,0,1).addScaledVector(n,-n.z);\r\n      forward.normalize();side.crossVectors(forward,n).normalize();\r\n      const base=vertices.length/3,size=outline.length;\r\n      const vertex=(x:number,y:number,z:number)=>{\r\n        v.copy(point).addScaledVector(side,x*radius).addScaledVector(forward,y*radius).addScaledVector(n,z).applyMatrix4(inverse);\r\n        vertices.push(v.x,v.y,v.z);roots.push(point.x,point.y,point.z);uvs.push(u+x*.012,w+y*.012);\r\n      };\r\n      for(const [x,y]of outline)vertex(x,y,-height*.12);\r\n      const inset=pattern==='round'?.60:pattern==='hex'?.78:pattern==='diamond'?.26:.64;\r\n      for(const [x,y]of outline)vertex(x*inset,y*inset,height*(pattern==='shield'?(.60+.23*y):.78));\r\n      vertex(0,pattern==='shield'?.18:0,height*(pattern==='hex'?.78:1));\r\n      for(let j=0;j<size;j++){\r\n        const k=(j+1)%size;\r\n        indices.push(base+j,base+k,base+size+k,base+j,base+size+k,base+size+j,base+size+j,base+size+k,base+size*2);\r\n      }\r\n      count++;\r\n    };\r\n    const total=index?index.count:position.count;\r\n    for(let triangle=0;triangle<total;triangle+=3){\r\n      const ia=index?index.getX(triangle):triangle,ib=index?index.getX(triangle+1):triangle+1,ic=index?index.getX(triangle+2):triangle+2;\r\n      a.fromBufferAttribute(position,ia).applyMatrix4(source.matrixWorld);b.fromBufferAttribute(position,ib).applyMatrix4(source.matrixWorld);c.fromBufferAttribute(position,ic).applyMatrix4(source.matrixWorld);\r\n      face.subVectors(b,a).cross(edge.subVectors(c,a));\r\n      const abs=[Math.abs(face.x),Math.abs(face.y),Math.abs(face.z)],axis=abs.indexOf(Math.max(...abs));\r\n      // Y is the row coordinate for side faces, keeping plates aligned along fingers.\r\n      const h=axis===0?2:0,k=axis===1?2:1;\r\n      const ax=a.getComponent(h),ay=a.getComponent(k),bx=b.getComponent(h),by=b.getComponent(k),cx=c.getComponent(h),cy=c.getComponent(k);\r\n      const denominator=(by-cy)*(ax-cx)+(cx-bx)*(ay-cy);if(Math.abs(denominator)<1e-15)continue;\r\n      const y0=Math.ceil(Math.min(ay,by,cy)/rowHeight),y1=Math.floor(Math.max(ay,by,cy)/rowHeight);\r\n      for(let row=y0;row<=y1;row++){\r\n        const y=row*rowHeight,offset=(Math.abs(row)%2)*.5;\r\n        const x0=Math.ceil(Math.min(ax,bx,cx)/spacing-offset),x1=Math.floor(Math.max(ax,bx,cx)/spacing-offset);\r\n        for(let col=x0;col<=x1;col++){\r\n          const x=(col+offset)*spacing,wa=((by-cy)*(x-cx)+(cx-bx)*(y-cy))/denominator,wb=((cy-ay)*(x-cx)+(ax-cx)*(y-cy))/denominator,wc=1-wa-wb;\r\n          if(wa<-.00001||wb<-.00001||wc<-.00001)continue;\r\n          point.copy(a).multiplyScalar(wa).addScaledVector(b,wb).addScaledVector(c,wc);\r\n          if(!accept(point,spacing*.55))continue;\r\n          na.fromBufferAttribute(normal,ia);nb.fromBufferAttribute(normal,ib);nc.fromBufferAttribute(normal,ic);\r\n          n.copy(na).multiplyScalar(wa).addScaledVector(nb,wb).addScaledVector(nc,wc).applyNormalMatrix(worldNormal);\r\n          stamp(uv?uv.getX(ia)*wa+uv.getX(ib)*wb+uv.getX(ic)*wc:0,uv?uv.getY(ia)*wa+uv.getY(ib)*wb+uv.getY(ic)*wc:0);\r\n        }\r\n      }\r\n    }\r\n    if(!count)continue;\r\n    const relief=new THREE.BufferGeometry();relief.setAttribute('position',new THREE.Float32BufferAttribute(vertices,3));relief.setAttribute('_surface_root',new THREE.Float32BufferAttribute(roots,3));relief.setAttribute('uv',new THREE.Float32BufferAttribute(uvs,2));relief.setIndex(indices);relief.computeVertexNormals();\r\n    // Flat facets on the angular patterns; rounded scales keep a soft dome.\r\n    let finalGeometry=relief;\r\n    if(pattern!=='round'){finalGeometry=relief.toNonIndexed();finalGeometry.computeVertexNormals();relief.dispose();}\r\n    const mesh=new THREE.Mesh(finalGeometry,Array.isArray(source.material)?source.material[0]:source.material);\r\n    mesh.name=source.name+'_'+pattern+'_scales';mesh.castShadow=true;mesh.receiveShadow=true;\r\n    mesh.userData={region:source.userData.region,rig_profile:source.userData.rig_profile,rig_bone:source.userData.rig_bone,surface_pattern:pattern,scale_count:count,ownedGeometry:true};\r\n    source.parent.add(mesh);\r\n  }\r\n}\r\n","handborne/source/app/style-slider.tsx":"'use client';\r\nimport { Field } from '@base-ui/react/field';\r\nimport { Slider } from '@/components/ui/slider';\r\nimport { STYLES, styleFor,PICKER_STYLES } from './catalog';\r\n\r\nexport function StyleSlider({label,value,onChange,className}:{label:string;value:number;onChange:(value:number)=>void;className?:string}) {\r\n  return <Field.Root>\r\n    <Field.Label className=\"sr-only\">{label} — {styleFor(value).name}</Field.Label>\r\n    <Slider min={1} max={PICKER_STYLES.length} step={1} largeStep={5} value={[Math.max(0,PICKER_STYLES.findIndex(s=>s.id===value))+1]} onValueChange={v=>onChange(PICKER_STYLES[Number(Array.isArray(v)?v[0]:v)-1].id)} className={className} />\r\n  </Field.Root>;\r\n}\r\n","handborne/source/app/swatches.ts":"// Runtime swatch generation (Rank 4): texture pattern previews are small canvases rendered\r\n// from the same procedural surfaceSample() the 3D material uses, cached by family id, never\r\n// shipped as image files. Colour swatches need no canvas at all — a CSS background is enough.\r\nimport {surfaceSample} from './material-language';\r\nimport * as T from 'three';\r\n\r\nconst colored=new Map<string,string>();\r\nexport function surfaceSwatch(id:number,primary:string,secondary:string,accent:string):string{\r\n const key=[id,primary,secondary,accent].join('|');if(colored.has(key))return colored.get(key)!;\r\n const canvas=document.createElement('canvas');canvas.width=canvas.height=80;\r\n const ctx=canvas.getContext('2d');if(!ctx)return '';\r\n const image=ctx.createImageData(80,80),a=new T.Color(secondary),b=new T.Color(primary),c=new T.Color(accent),color=new T.Color();\r\n for(let y=0;y<80;y++)for(let x=0;x<80;x++){\r\n  const sample=surfaceSample(id,x/80,y/80),t=sample.tint;\r\n  color.copy(t<.5?a:b).lerp(t<.5?b:c,t<.5?t*2:(t-.5)*2).convertLinearToSRGB();\r\n  const i=(y*80+x)*4;image.data.set([color.r*255,color.g*255,color.b*255,255],i);\r\n }\r\n ctx.putImageData(image,0,0);const url=canvas.toDataURL();colored.set(key,url);return url;\r\n}\r\n\r\nconst cache = new Map<number, string>();\r\nexport function texturePreviewDataURL(familyId: number, size = 40): string {\r\n const cached = cache.get(familyId); if (cached) return cached;\r\n const canvas = document.createElement('canvas'); canvas.width = canvas.height = size;\r\n const ctx = canvas.getContext('2d'); if (!ctx) return '';\r\n const img = ctx.createImageData(size, size);\r\n for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) {\r\n  const s = surfaceSample(familyId, x / size, y / size), v = Math.round(Math.max(0, Math.min(1, s.height)) * 255), i = (y * size + x) * 4;\r\n  img.data[i] = img.data[i + 1] = img.data[i + 2] = v; img.data[i + 3] = 255;\r\n }\r\n ctx.putImageData(img, 0, 0);\r\n const url = canvas.toDataURL('image/png'); cache.set(familyId, url); return url;\r\n}\r\n","handborne/source/app/unlock-bridge.ts":"import {authFetch,loadLogin} from '../../../auth-client.mjs';\r\nimport {isGranted,currentCosmeticCoach} from '../../../creature/source/creator/unlock-store';\r\nimport {STYLES} from './catalog';\r\nconst allowed=new Set(['https://mominc.online','https://www.mominc.online','https://myr5.mominc.online']);\r\nlet pending=false;\r\nwindow.addEventListener('message',async event=>{\r\n if(event.source!==window.parent||!allowed.has(event.origin)||event.data?.type!=='handborne:unlock-request'||pending)return;\r\n pending=true;\r\n try{\r\n  let account=null;try{if(localStorage.getItem('myr5-login-provider')==='clerk')await loadLogin();const res=await authFetch('/api/account',{guestSkip:true});if(res.ok)account=await res.json();}catch{}\r\n  (globalThis as any).myr5AuthenticatedAccount=account;\r\n  const coach=currentCosmeticCoach(),styles=STYLES.filter(s=>s.id>=57&&s.paletteId&&isGranted('texture',s.paletteId,coach)&&isGranted('palette',s.paletteId,coach)).map(s=>s.id);\r\n  window.parent.postMessage({type:'handborne:unlock-state',styles},event.origin);\r\n }finally{pending=false;}\r\n});\r\n","handborne/source/app/web-tools.ts":"import { REGIONS, STYLES, type RegionId } from './catalog';\r\nimport { validateSelection, designCode, type HandDesign } from './recipe';\r\nimport { POSES, validatePose } from './poses';\r\nimport { DEFAULT_NAIL_SHAPE, NAIL_SHAPES, validateNailShape } from './nails';\r\nimport { SCALE_PATTERNS, validateScalePattern } from './scale-patterns';\r\ntype Tool = {name:string;title:string;description:string;inputSchema:object;annotations:{readOnlyHint:boolean};execute:(input:unknown)=>unknown};\r\nexport function registerHandTools(read:()=>HandDesign, apply:(s:HandDesign)=>Promise<void>) {\r\n  const context=(document as Document & {modelContext?:{registerTool:(tool:Tool,options:{signal:AbortSignal})=>unknown}}).modelContext;\r\n  if(!context?.registerTool) return () => {};\r\n  const lifecycle=new AbortController();\r\n  const tools:Tool[]=[\r\n    {name:'read_hand_recipe',title:'Read hand recipe',description:'Read the six sections, nail shape, raised scales, current pose and restorable recipe.',inputSchema:{type:'object',properties:{},additionalProperties:false},annotations:{readOnlyHint:true},execute:()=>({...read(),code:designCode(read()),styles:STYLES.map(s=>({id:s.id,name:s.name})),poses:POSES.map(p=>({id:p.id,name:p.name})),nailShapes:[{id:DEFAULT_NAIL_SHAPE,name:'Creature default'},...NAIL_SHAPES.map(s=>({id:s.id,name:s.name}))],scalePatterns:SCALE_PATTERNS,regions:REGIONS})},\r\n    {name:'configure_hand_sections',title:'Configure hand sections',description:`Apply all six section styles, preserving the nail shape and current pose. Style IDs are zero-based, 0 through ${Math.max(...STYLES.map(s=>s.id))}.`,inputSchema:{type:'object',properties:{sections:{type:'object',properties:Object.fromEntries(REGIONS.map(r=>[r.id,{type:'integer',minimum:0,maximum:Math.max(...STYLES.map(s=>s.id))}])),required:REGIONS.map(r=>r.id),additionalProperties:false}},required:['sections'],additionalProperties:false},annotations:{readOnlyHint:false},async execute(input){if(!input||typeof input!=='object'||Object.keys(input).some(k=>k!=='sections'))throw new Error('Expected sections.');const sections=validateSelection((input as {sections:Record<RegionId,number>}).sections),next={...read(),sections};await apply(next);return {...next,code:designCode(next)};}},\r\n    {name:'configure_hand_pose',title:'Change hand pose',description:'Pose the visible hand while preserving styles and nails. Automatic posing resumes after 15 seconds if playback is enabled.',inputSchema:{type:'object',properties:{pose:{type:'string',enum:POSES.map(p=>p.id)}},required:['pose'],additionalProperties:false},annotations:{readOnlyHint:false},async execute(input){if(!input||typeof input!=='object'||Object.keys(input).some(k=>k!=='pose'))throw new Error('Expected a pose ID.');const pose=validatePose((input as {pose:unknown}).pose),next={...read(),pose};await apply(next);return {...next,code:designCode(next)};}},\r\n    {name:'configure_nail_shape',title:'Change nail shape',description:'Change the five physical nails while preserving the creature materials and pose.',inputSchema:{type:'object',properties:{nailShape:{type:'string',enum:[DEFAULT_NAIL_SHAPE,...NAIL_SHAPES.map(s=>s.id)]}},required:['nailShape'],additionalProperties:false},annotations:{readOnlyHint:false},async execute(input){if(!input||typeof input!=='object'||Object.keys(input).some(k=>k!=='nailShape'))throw new Error('Expected a nail shape.');const nailShape=validateNailShape((input as {nailShape:unknown}).nailShape),next={...read(),nailShape};await apply(next);return {...next,code:designCode(next)};}},\r\n  ];\r\n  tools.push({name:'configure_scale_pattern',title:'Change raised scales',description:'Add round, diamond, hexagonal or shield-shaped scale geometry across the hand, or turn it off. Keeps hand shape, materials, nails and pose.',inputSchema:{type:'object',properties:{scalePattern:{type:'string',enum:SCALE_PATTERNS.map(s=>s.id)}},required:['scalePattern'],additionalProperties:false},annotations:{readOnlyHint:false},async execute(input){if(!input||typeof input!=='object'||Object.keys(input).some(k=>k!=='scalePattern'))throw new Error('Expected a scale pattern.');const scalePattern=validateScalePattern((input as {scalePattern:unknown}).scalePattern),next={...read(),scalePattern};await apply(next);return {...next,code:designCode(next)};}});\r\n  for(const tool of tools) {try{Promise.resolve(context.registerTool(tool,{signal:lifecycle.signal})).catch(()=>{});}catch{}}\r\n  return ()=>lifecycle.abort();\r\n}\r\n","handborne/source/components/ui/accordion.tsx":"import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronDownIcon, ChevronUpIcon } from 'lucide-react';\r\n\r\nfunction Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {\r\n  return (\r\n    <AccordionPrimitive.Root\r\n      data-slot=\"accordion\"\r\n      className={cn('flex w-full flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {\r\n  return (\r\n    <AccordionPrimitive.Item\r\n      data-slot=\"accordion-item\"\r\n      className={cn('not-last:border-b', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AccordionTrigger({\r\n  className,\r\n  children,\r\n  ...props\r\n}: AccordionPrimitive.Trigger.Props) {\r\n  return (\r\n    <AccordionPrimitive.Header className=\"flex\">\r\n      <AccordionPrimitive.Trigger\r\n        data-slot=\"accordion-trigger\"\r\n        className={cn(\r\n          'focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-lg py-2.5 text-left text-sm font-medium hover:underline focus-visible:ring-3 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none aria-disabled:pointer-events-none aria-disabled:opacity-50',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        {children}\r\n        <ChevronDownIcon\r\n          data-slot=\"accordion-trigger-icon\"\r\n          className=\"pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden\"\r\n        />\r\n        <ChevronUpIcon\r\n          data-slot=\"accordion-trigger-icon\"\r\n          className=\"pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline\"\r\n        />\r\n      </AccordionPrimitive.Trigger>\r\n    </AccordionPrimitive.Header>\r\n  );\r\n}\r\n\r\nfunction AccordionContent({\r\n  className,\r\n  children,\r\n  ...props\r\n}: AccordionPrimitive.Panel.Props) {\r\n  return (\r\n    <AccordionPrimitive.Panel\r\n      data-slot=\"accordion-content\"\r\n      className=\"data-open:animate-accordion-down data-closed:animate-accordion-up text-sm overflow-hidden\"\r\n      {...props}\r\n    >\r\n      <div\r\n        className={cn(\r\n          'pt-0 pb-2.5 h-(--accordion-panel-height) data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',\r\n          className,\r\n        )}\r\n      >\r\n        {children}\r\n      </div>\r\n    </AccordionPrimitive.Panel>\r\n  );\r\n}\r\n\r\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\r\n","handborne/source/components/ui/alert-dialog.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\n\r\nfunction AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {\r\n  return <AlertDialogPrimitive.Root data-slot=\"alert-dialog\" {...props} />;\r\n}\r\n\r\nfunction AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {\r\n  return (\r\n    <AlertDialogPrimitive.Trigger data-slot=\"alert-dialog-trigger\" {...props} />\r\n  );\r\n}\r\n\r\nfunction AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {\r\n  return (\r\n    <AlertDialogPrimitive.Portal data-slot=\"alert-dialog-portal\" {...props} />\r\n  );\r\n}\r\n\r\nfunction AlertDialogOverlay({\r\n  className,\r\n  ...props\r\n}: AlertDialogPrimitive.Backdrop.Props) {\r\n  return (\r\n    <AlertDialogPrimitive.Backdrop\r\n      data-slot=\"alert-dialog-overlay\"\r\n      className={cn(\r\n        'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogContent({\r\n  className,\r\n  size = 'default',\r\n  ...props\r\n}: AlertDialogPrimitive.Popup.Props & {\r\n  size?: 'default' | 'sm';\r\n}) {\r\n  return (\r\n    <AlertDialogPortal>\r\n      <AlertDialogOverlay />\r\n      <AlertDialogPrimitive.Popup\r\n        data-slot=\"alert-dialog-content\"\r\n        data-size={size}\r\n        className={cn(\r\n          'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/10 gap-4 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none',\r\n          className,\r\n        )}\r\n        {...props}\r\n      />\r\n    </AlertDialogPortal>\r\n  );\r\n}\r\n\r\nfunction AlertDialogHeader({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-dialog-header\"\r\n      className={cn(\r\n        'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogFooter({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-dialog-footer\"\r\n      className={cn(\r\n        'bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogMedia({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-dialog-media\"\r\n      className={cn(\r\n        \"bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogTitle({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {\r\n  return (\r\n    <AlertDialogPrimitive.Title\r\n      data-slot=\"alert-dialog-title\"\r\n      className={cn(\r\n        'text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2 cn-font-heading',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogDescription({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {\r\n  return (\r\n    <AlertDialogPrimitive.Description\r\n      data-slot=\"alert-dialog-description\"\r\n      className={cn(\r\n        'text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogAction({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof Button>) {\r\n  return (\r\n    <Button\r\n      data-slot=\"alert-dialog-action\"\r\n      className={cn(className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDialogCancel({\r\n  className,\r\n  variant = 'outline',\r\n  size = 'default',\r\n  ...props\r\n}: AlertDialogPrimitive.Close.Props &\r\n  Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {\r\n  return (\r\n    <AlertDialogPrimitive.Close\r\n      data-slot=\"alert-dialog-cancel\"\r\n      className={cn(className)}\r\n      render={<Button variant={variant} size={size} />}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  AlertDialog,\r\n  AlertDialogAction,\r\n  AlertDialogCancel,\r\n  AlertDialogContent,\r\n  AlertDialogDescription,\r\n  AlertDialogFooter,\r\n  AlertDialogHeader,\r\n  AlertDialogMedia,\r\n  AlertDialogOverlay,\r\n  AlertDialogPortal,\r\n  AlertDialogTitle,\r\n  AlertDialogTrigger,\r\n};\r\n","handborne/source/components/ui/alert.tsx":"import * as React from 'react';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst alertVariants = cva(\r\n  \"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4\",\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-card text-card-foreground',\r\n        destructive:\r\n          'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Alert({\r\n  className,\r\n  variant,\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert\"\r\n      role=\"alert\"\r\n      className={cn(alertVariants({ variant }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-title\"\r\n      className={cn(\r\n        'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertDescription({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-description\"\r\n      className={cn(\r\n        'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AlertAction({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"alert-action\"\r\n      className={cn('absolute top-2 right-2', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Alert, AlertTitle, AlertDescription, AlertAction };\r\n","handborne/source/components/ui/aspect-ratio.tsx":"import { cn } from '@/lib/utils';\r\n\r\nfunction AspectRatio({\r\n  ratio,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'> & { ratio: number }) {\r\n  return (\r\n    <div\r\n      data-slot=\"aspect-ratio\"\r\n      style={\r\n        {\r\n          '--ratio': ratio,\r\n        } as React.CSSProperties\r\n      }\r\n      className={cn('relative aspect-(--ratio)', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { AspectRatio };\r\n","handborne/source/components/ui/attachment.tsx":"import * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\n\r\nconst attachmentVariants = cva(\r\n  'rounded-xl w-fit focus-within:ring-1 focus-within:ring-ring/50 group/attachment relative flex max-w-full min-w-0 shrink-0 flex-wrap border bg-card text-card-foreground transition-colors has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed',\r\n  {\r\n    variants: {\r\n      size: {\r\n        default:\r\n          'gap-2 has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2 text-sm',\r\n        sm: 'gap-2.5 has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5 text-xs',\r\n        xs: 'gap-1.5 has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1 text-xs rounded-lg',\r\n      },\r\n      orientation: {\r\n        horizontal: 'min-w-40 items-center',\r\n        vertical: 'w-24 has-data-[slot=attachment-content]:w-30 flex-col',\r\n      },\r\n    },\r\n  },\r\n);\r\n\r\nfunction Attachment({\r\n  className,\r\n  state = 'done',\r\n  size = 'default',\r\n  orientation = 'horizontal',\r\n  ...props\r\n}: React.ComponentProps<'div'> &\r\n  VariantProps<typeof attachmentVariants> & {\r\n    state?: 'idle' | 'uploading' | 'processing' | 'error' | 'done';\r\n  }) {\r\n  return (\r\n    <div\r\n      data-slot=\"attachment\"\r\n      data-state={state}\r\n      data-size={size}\r\n      data-orientation={orientation}\r\n      className={cn(attachmentVariants({ size, orientation }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst attachmentMediaVariants = cva(\r\n  \"bg-muted text-foreground w-10 rounded-lg group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md [&_svg:not([class*='size-'])]:size-4 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5 group-data-[orientation=vertical]/attachment:w-full group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! relative flex aspect-square shrink-0 items-center justify-center overflow-hidden group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive [&_svg]:pointer-events-none\",\r\n  {\r\n    variants: {\r\n      variant: {\r\n        icon: '',\r\n        image:\r\n          'opacity-60 group-data-[state=idle]/attachment:opacity-100 group-data-[state=done]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'icon',\r\n    },\r\n  },\r\n);\r\n\r\nfunction AttachmentMedia({\r\n  className,\r\n  variant = 'icon',\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof attachmentMediaVariants>) {\r\n  return (\r\n    <div\r\n      data-slot=\"attachment-media\"\r\n      data-variant={variant}\r\n      className={cn(attachmentMediaVariants({ variant }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentContent({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"attachment-content\"\r\n      className={cn(\r\n        'leading-tight group-data-[orientation=vertical]/attachment:px-1 max-w-full min-w-0 flex-1',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentTitle({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"attachment-title\"\r\n      className={cn(\r\n        'font-medium block max-w-full min-w-0 truncate group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentDescription({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"attachment-description\"\r\n      className={cn(\r\n        'mt-0.5 text-xs block min-w-0 truncate text-muted-foreground group-data-[state=error]/attachment:text-destructive/80',\r\n        'max-w-full',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentActions({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"attachment-actions\"\r\n      className={cn(\r\n        'group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:right-3 relative z-20 group-data-[orientation=vertical]/attachment:gap-1 flex shrink-0 items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentAction({\r\n  className,\r\n  variant,\r\n  size = 'icon-xs',\r\n  ...props\r\n}: React.ComponentProps<typeof Button>) {\r\n  return (\r\n    <Button\r\n      data-slot=\"attachment-action\"\r\n      variant={variant ?? 'ghost'}\r\n      size={size}\r\n      className={cn(className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AttachmentTrigger({\r\n  className,\r\n  render,\r\n  type,\r\n  ...props\r\n}: useRender.ComponentProps<'button'>) {\r\n  return useRender({\r\n    defaultTagName: 'button',\r\n    props: mergeProps<'button'>(\r\n      {\r\n        type: render ? type : (type ?? 'button'),\r\n        className: cn('absolute inset-0 z-10 outline-none', className),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'attachment-trigger',\r\n    },\r\n  });\r\n}\r\n\r\nfunction AttachmentGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"attachment-group\"\r\n      className={cn(\r\n        'gap-3 scroll-px-1 py-1 flex min-w-0 scroll-fade-x snap-x snap-mandatory scrollbar-none overflow-x-auto overscroll-x-contain *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Attachment,\r\n  AttachmentGroup,\r\n  AttachmentMedia,\r\n  AttachmentContent,\r\n  AttachmentTitle,\r\n  AttachmentDescription,\r\n  AttachmentActions,\r\n  AttachmentAction,\r\n  AttachmentTrigger,\r\n};\r\n","handborne/source/components/ui/avatar.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Avatar as AvatarPrimitive } from '@base-ui/react/avatar';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Avatar({\r\n  className,\r\n  size = 'default',\r\n  ...props\r\n}: AvatarPrimitive.Root.Props & {\r\n  size?: 'default' | 'sm' | 'lg';\r\n}) {\r\n  return (\r\n    <AvatarPrimitive.Root\r\n      data-slot=\"avatar\"\r\n      data-size={size}\r\n      className={cn(\r\n        'size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {\r\n  return (\r\n    <AvatarPrimitive.Image\r\n      data-slot=\"avatar-image\"\r\n      className={cn(\r\n        'rounded-full aspect-square size-full object-cover',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AvatarFallback({\r\n  className,\r\n  ...props\r\n}: AvatarPrimitive.Fallback.Props) {\r\n  return (\r\n    <AvatarPrimitive.Fallback\r\n      data-slot=\"avatar-fallback\"\r\n      className={cn(\r\n        'bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"avatar-badge\"\r\n      className={cn(\r\n        'bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none',\r\n        'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',\r\n        'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',\r\n        'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"avatar-group\"\r\n      className={cn(\r\n        'group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction AvatarGroupCount({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"avatar-group-count\"\r\n      className={cn(\r\n        'bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 relative flex shrink-0 items-center justify-center ring-2 ring-background',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Avatar,\r\n  AvatarImage,\r\n  AvatarFallback,\r\n  AvatarGroup,\r\n  AvatarGroupCount,\r\n  AvatarBadge,\r\n};\r\n","handborne/source/components/ui/badge.tsx":"import { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst badgeVariants = cva(\r\n  'group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',\r\n        secondary:\r\n          'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',\r\n        destructive:\r\n          'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',\r\n        outline:\r\n          'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',\r\n        ghost:\r\n          'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',\r\n        link: 'text-primary underline-offset-4 hover:underline',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Badge({\r\n  className,\r\n  variant = 'default',\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'span'> & VariantProps<typeof badgeVariants>) {\r\n  return useRender({\r\n    defaultTagName: 'span',\r\n    props: mergeProps<'span'>(\r\n      {\r\n        className: cn(badgeVariants({ variant }), className),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'badge',\r\n      variant,\r\n    },\r\n  });\r\n}\r\n\r\nexport { Badge, badgeVariants };\r\n","handborne/source/components/ui/breadcrumb.tsx":"import * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronRightIcon, MoreHorizontalIcon } from 'lucide-react';\r\n\r\nfunction Breadcrumb({ className, ...props }: React.ComponentProps<'nav'>) {\r\n  return (\r\n    <nav\r\n      aria-label=\"breadcrumb\"\r\n      data-slot=\"breadcrumb\"\r\n      className={cn(className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {\r\n  return (\r\n    <ol\r\n      data-slot=\"breadcrumb-list\"\r\n      className={cn(\r\n        'text-muted-foreground gap-1.5 text-sm flex flex-wrap items-center wrap-break-word',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {\r\n  return (\r\n    <li\r\n      data-slot=\"breadcrumb-item\"\r\n      className={cn('gap-1 inline-flex items-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction BreadcrumbLink({\r\n  className,\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'a'>) {\r\n  return useRender({\r\n    defaultTagName: 'a',\r\n    props: mergeProps<'a'>(\r\n      {\r\n        className: cn('hover:text-foreground transition-colors', className),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'breadcrumb-link',\r\n    },\r\n  });\r\n}\r\n\r\nfunction BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"breadcrumb-page\"\r\n      role=\"link\"\r\n      aria-disabled=\"true\"\r\n      aria-current=\"page\"\r\n      className={cn('text-foreground font-normal', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction BreadcrumbSeparator({\r\n  children,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'li'>) {\r\n  return (\r\n    <li\r\n      data-slot=\"breadcrumb-separator\"\r\n      role=\"presentation\"\r\n      aria-hidden=\"true\"\r\n      className={cn('[&>svg]:size-3.5', className)}\r\n      {...props}\r\n    >\r\n      {children ?? <ChevronRightIcon className=\"cn-rtl-flip\" />}\r\n    </li>\r\n  );\r\n}\r\n\r\nfunction BreadcrumbEllipsis({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"breadcrumb-ellipsis\"\r\n      role=\"presentation\"\r\n      aria-hidden=\"true\"\r\n      className={cn(\r\n        'size-5 [&>svg]:size-4 flex items-center justify-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <MoreHorizontalIcon />\r\n      <span className=\"sr-only\">More</span>\r\n    </span>\r\n  );\r\n}\r\n\r\nexport {\r\n  Breadcrumb,\r\n  BreadcrumbList,\r\n  BreadcrumbItem,\r\n  BreadcrumbLink,\r\n  BreadcrumbPage,\r\n  BreadcrumbSeparator,\r\n  BreadcrumbEllipsis,\r\n};\r\n","handborne/source/components/ui/bubble.tsx":"import * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction BubbleGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"bubble-group\"\r\n      className={cn('gap-2 flex min-w-0 flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst bubbleVariants = cva(\r\n  'gap-1 data-[align=end]:self-end max-w-[80%] data-[variant=ghost]:max-w-full group-data-[align=end]/message:self-end group/bubble relative flex w-fit min-w-0 flex-col',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default:\r\n          '*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80',\r\n        secondary:\r\n          '*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]',\r\n        muted:\r\n          '*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]',\r\n        tinted:\r\n          '*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]',\r\n        outline:\r\n          '*:data-[slot=bubble-content]:bg-background *:data-[slot=bubble-content]:border-border [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30',\r\n        ghost:\r\n          '*:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50 border-none',\r\n        destructive:\r\n          '*:data-[slot=bubble-content]:bg-destructive/10 dark:*:data-[slot=bubble-content]:bg-destructive/20 *:data-[slot=bubble-content]:text-destructive [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Bubble({\r\n  variant = 'default',\r\n  align = 'start',\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'> &\r\n  VariantProps<typeof bubbleVariants> & {\r\n    align?: 'start' | 'end';\r\n  }) {\r\n  return (\r\n    <div\r\n      data-slot=\"bubble\"\r\n      data-variant={variant}\r\n      data-align={align}\r\n      className={cn(bubbleVariants({ variant }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction BubbleContent({\r\n  className,\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'div'>) {\r\n  return useRender({\r\n    defaultTagName: 'div',\r\n    props: mergeProps<'div'>(\r\n      {\r\n        className: cn(\r\n          'rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50 group-data-[align=end]/bubble:self-end w-fit max-w-full min-w-0 overflow-hidden wrap-break-word [button]:text-left [button,a]:transition-colors',\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'bubble-content',\r\n    },\r\n  });\r\n}\r\n\r\nconst bubbleReactionsVariants = cva(\r\n  'rounded-full ring-3 ring-card bg-muted shrink-0 gap-1 px-1.5 py-0.5 has-[button]:p-0 text-sm absolute z-10 flex w-fit items-center justify-center',\r\n  {\r\n    variants: {\r\n      side: {\r\n        top: 'top-0 -translate-y-3/4',\r\n        bottom: 'bottom-0 translate-y-3/4',\r\n      },\r\n      align: {\r\n        start: 'left-3',\r\n        end: 'right-3',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      side: 'bottom',\r\n      align: 'end',\r\n    },\r\n  },\r\n);\r\n\r\nfunction BubbleReactions({\r\n  side = 'bottom',\r\n  align = 'end',\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  align?: 'start' | 'end';\r\n  side?: 'top' | 'bottom';\r\n}) {\r\n  return (\r\n    <div\r\n      data-slot=\"bubble-reactions\"\r\n      data-align={align}\r\n      data-side={side}\r\n      className={cn(bubbleReactionsVariants({ side, align }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { BubbleGroup, Bubble, BubbleContent, BubbleReactions };\r\n","handborne/source/components/ui/button-group.tsx":"import { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Separator } from '@/components/ui/separator';\r\n\r\nconst buttonGroupVariants = cva(\r\n  \"has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1\",\r\n  {\r\n    variants: {\r\n      orientation: {\r\n        horizontal:\r\n          '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! *:data-slot:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0',\r\n        vertical:\r\n          '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! flex-col *:data-slot:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      orientation: 'horizontal',\r\n    },\r\n  },\r\n);\r\n\r\nfunction ButtonGroup({\r\n  className,\r\n  orientation,\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) {\r\n  return (\r\n    <div\r\n      role=\"group\"\r\n      data-slot=\"button-group\"\r\n      data-orientation={orientation}\r\n      className={cn(buttonGroupVariants({ orientation }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ButtonGroupText({\r\n  className,\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'div'>) {\r\n  return useRender({\r\n    defaultTagName: 'div',\r\n    props: mergeProps<'div'>(\r\n      {\r\n        className: cn(\r\n          \"bg-muted gap-2 rounded-lg border px-2.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none\",\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'button-group-text',\r\n    },\r\n  });\r\n}\r\n\r\nfunction ButtonGroupSeparator({\r\n  className,\r\n  orientation = 'vertical',\r\n  ...props\r\n}: React.ComponentProps<typeof Separator>) {\r\n  return (\r\n    <Separator\r\n      data-slot=\"button-group-separator\"\r\n      orientation={orientation}\r\n      className={cn(\r\n        'bg-input relative self-stretch data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  ButtonGroup,\r\n  ButtonGroupSeparator,\r\n  ButtonGroupText,\r\n  buttonGroupVariants,\r\n};\r\n","handborne/source/components/ui/button.tsx":"import { Button as ButtonPrimitive } from '@base-ui/react/button';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst buttonVariants = cva(\r\n  \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-primary text-primary-foreground hover:bg-primary/80',\r\n        outline:\r\n          'border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',\r\n        secondary:\r\n          'bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',\r\n        ghost:\r\n          'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50',\r\n        destructive:\r\n          'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',\r\n        link: 'text-primary underline-offset-4 hover:underline',\r\n      },\r\n      size: {\r\n        default:\r\n          'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\r\n        xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\r\n        sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\r\n        lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\r\n        icon: 'size-8',\r\n        'icon-xs':\r\n          \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\r\n        'icon-sm':\r\n          'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',\r\n        'icon-lg': 'size-9',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n      size: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Button({\r\n  className,\r\n  variant = 'default',\r\n  size = 'default',\r\n  ...props\r\n}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {\r\n  return (\r\n    <ButtonPrimitive\r\n      data-slot=\"button\"\r\n      className={cn(buttonVariants({ variant, size, className }))}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Button, buttonVariants };\r\n","handborne/source/components/ui/calendar.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport {\r\n  DayPicker,\r\n  getDefaultClassNames,\r\n  type DayButton,\r\n  type Locale,\r\n} from 'react-day-picker';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button, buttonVariants } from '@/components/ui/button';\r\nimport {\r\n  ChevronLeftIcon,\r\n  ChevronRightIcon,\r\n  ChevronDownIcon,\r\n} from 'lucide-react';\r\n\r\nfunction Calendar({\r\n  className,\r\n  classNames,\r\n  showOutsideDays = true,\r\n  captionLayout = 'label',\r\n  buttonVariant = 'ghost',\r\n  locale,\r\n  formatters,\r\n  components,\r\n  ...props\r\n}: React.ComponentProps<typeof DayPicker> & {\r\n  buttonVariant?: React.ComponentProps<typeof Button>['variant'];\r\n}) {\r\n  const defaultClassNames = getDefaultClassNames();\r\n\r\n  return (\r\n    <DayPicker\r\n      showOutsideDays={showOutsideDays}\r\n      className={cn(\r\n        'p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] group/calendar bg-background in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',\r\n        String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\r\n        String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\r\n        className,\r\n      )}\r\n      captionLayout={captionLayout}\r\n      locale={locale}\r\n      formatters={{\r\n        formatMonthDropdown: (date) =>\r\n          date.toLocaleString(locale?.code, { month: 'short' }),\r\n        ...formatters,\r\n      }}\r\n      classNames={{\r\n        root: cn('w-fit', defaultClassNames.root),\r\n        months: cn(\r\n          'relative flex flex-col gap-4 md:flex-row',\r\n          defaultClassNames.months,\r\n        ),\r\n        month: cn('flex w-full flex-col gap-4', defaultClassNames.month),\r\n        nav: cn(\r\n          'absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1',\r\n          defaultClassNames.nav,\r\n        ),\r\n        button_previous: cn(\r\n          buttonVariants({ variant: buttonVariant }),\r\n          'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\r\n          defaultClassNames.button_previous,\r\n        ),\r\n        button_next: cn(\r\n          buttonVariants({ variant: buttonVariant }),\r\n          'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\r\n          defaultClassNames.button_next,\r\n        ),\r\n        month_caption: cn(\r\n          'flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)',\r\n          defaultClassNames.month_caption,\r\n        ),\r\n        dropdowns: cn(\r\n          'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium',\r\n          defaultClassNames.dropdowns,\r\n        ),\r\n        dropdown_root: cn(\r\n          'cn-calendar-dropdown-root relative rounded-(--cell-radius)',\r\n          defaultClassNames.dropdown_root,\r\n        ),\r\n        dropdown: cn(\r\n          'absolute inset-0 bg-popover opacity-0',\r\n          defaultClassNames.dropdown,\r\n        ),\r\n        caption_label: cn(\r\n          'font-medium select-none',\r\n          captionLayout === 'label'\r\n            ? 'cn-calendar-caption text-sm'\r\n            : 'cn-calendar-caption-label flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground',\r\n          defaultClassNames.caption_label,\r\n        ),\r\n        month_grid: cn('w-full border-collapse', defaultClassNames.month_grid),\r\n        weekdays: cn('flex', defaultClassNames.weekdays),\r\n        weekday: cn(\r\n          'flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none',\r\n          defaultClassNames.weekday,\r\n        ),\r\n        week: cn('mt-2 flex w-full', defaultClassNames.week),\r\n        week_number_header: cn(\r\n          'w-(--cell-size) select-none',\r\n          defaultClassNames.week_number_header,\r\n        ),\r\n        week_number: cn(\r\n          'text-[0.8rem] text-muted-foreground select-none',\r\n          defaultClassNames.week_number,\r\n        ),\r\n        day: cn(\r\n          'group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',\r\n          props.showWeekNumber\r\n            ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)'\r\n            : '[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)',\r\n          defaultClassNames.day,\r\n        ),\r\n        range_start: cn(\r\n          'relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted',\r\n          defaultClassNames.range_start,\r\n        ),\r\n        range_middle: cn('rounded-none', defaultClassNames.range_middle),\r\n        range_end: cn(\r\n          'relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted',\r\n          defaultClassNames.range_end,\r\n        ),\r\n        today: cn(\r\n          'rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none',\r\n          defaultClassNames.today,\r\n        ),\r\n        outside: cn(\r\n          'text-muted-foreground aria-selected:text-muted-foreground',\r\n          defaultClassNames.outside,\r\n        ),\r\n        disabled: cn(\r\n          'text-muted-foreground opacity-50',\r\n          defaultClassNames.disabled,\r\n        ),\r\n        hidden: cn('invisible', defaultClassNames.hidden),\r\n        ...classNames,\r\n      }}\r\n      components={{\r\n        Root: ({ className, rootRef, ...props }) => {\r\n          return (\r\n            <div\r\n              data-slot=\"calendar\"\r\n              ref={rootRef}\r\n              className={cn(className)}\r\n              {...props}\r\n            />\r\n          );\r\n        },\r\n        Chevron: ({ className, orientation, ...props }) => {\r\n          if (orientation === 'left') {\r\n            return (\r\n              <ChevronLeftIcon\r\n                className={cn('cn-rtl-flip size-4', className)}\r\n                {...props}\r\n              />\r\n            );\r\n          }\r\n\r\n          if (orientation === 'right') {\r\n            return (\r\n              <ChevronRightIcon\r\n                className={cn('cn-rtl-flip size-4', className)}\r\n                {...props}\r\n              />\r\n            );\r\n          }\r\n\r\n          return (\r\n            <ChevronDownIcon className={cn('size-4', className)} {...props} />\r\n          );\r\n        },\r\n        DayButton: ({ ...props }) => (\r\n          <CalendarDayButton locale={locale} {...props} />\r\n        ),\r\n        WeekNumber: ({ children, ...props }) => {\r\n          return (\r\n            <td {...props}>\r\n              <div className=\"flex size-(--cell-size) items-center justify-center text-center\">\r\n                {children}\r\n              </div>\r\n            </td>\r\n          );\r\n        },\r\n        ...components,\r\n      }}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CalendarDayButton({\r\n  className,\r\n  day,\r\n  modifiers,\r\n  locale,\r\n  ...props\r\n}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {\r\n  const defaultClassNames = getDefaultClassNames();\r\n\r\n  const ref = React.useRef<HTMLButtonElement>(null);\r\n  React.useEffect(() => {\r\n    if (modifiers.focused) ref.current?.focus();\r\n  }, [modifiers.focused]);\r\n\r\n  return (\r\n    <Button\r\n      variant=\"ghost\"\r\n      size=\"icon\"\r\n      data-day={day.date.toLocaleDateString(locale?.code)}\r\n      data-selected-single={\r\n        modifiers.selected &&\r\n        !modifiers.range_start &&\r\n        !modifiers.range_end &&\r\n        !modifiers.range_middle\r\n      }\r\n      data-range-start={modifiers.range_start}\r\n      data-range-end={modifiers.range_end}\r\n      data-range-middle={modifiers.range_middle}\r\n      className={cn(\r\n        'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70',\r\n        defaultClassNames.day,\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Calendar, CalendarDayButton };\r\n","handborne/source/components/ui/card.tsx":"import * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Card({\r\n  className,\r\n  size = 'default',\r\n  ...props\r\n}: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {\r\n  return (\r\n    <div\r\n      data-slot=\"card\"\r\n      data-size={size}\r\n      className={cn(\r\n        'group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-header\"\r\n      className={cn(\r\n        'group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardTitle({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-title\"\r\n      className={cn(\r\n        'font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardDescription({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-description\"\r\n      className={cn('text-sm text-muted-foreground', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardAction({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-action\"\r\n      className={cn(\r\n        'col-start-2 row-span-2 row-start-1 self-start justify-self-end',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-content\"\r\n      className={cn('px-(--card-spacing)', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CardFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"card-footer\"\r\n      className={cn(\r\n        'flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Card,\r\n  CardHeader,\r\n  CardFooter,\r\n  CardTitle,\r\n  CardAction,\r\n  CardDescription,\r\n  CardContent,\r\n};\r\n","handborne/source/components/ui/carousel.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport useEmblaCarousel, {\r\n  type UseEmblaCarouselType,\r\n} from 'embla-carousel-react';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';\r\n\r\ntype CarouselApi = UseEmblaCarouselType[1];\r\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\r\ntype CarouselOptions = UseCarouselParameters[0];\r\ntype CarouselPlugin = UseCarouselParameters[1];\r\n\r\ntype CarouselProps = {\r\n  opts?: CarouselOptions;\r\n  plugins?: CarouselPlugin;\r\n  orientation?: 'horizontal' | 'vertical';\r\n  setApi?: (api: CarouselApi) => void;\r\n};\r\n\r\ntype CarouselContextProps = {\r\n  carouselRef: ReturnType<typeof useEmblaCarousel>[0];\r\n  api: ReturnType<typeof useEmblaCarousel>[1];\r\n  scrollPrev: () => void;\r\n  scrollNext: () => void;\r\n  canScrollPrev: boolean;\r\n  canScrollNext: boolean;\r\n} & CarouselProps;\r\n\r\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\r\n\r\nfunction useCarousel() {\r\n  const context = React.useContext(CarouselContext);\r\n\r\n  if (!context) {\r\n    throw new Error('useCarousel must be used within a <Carousel />');\r\n  }\r\n\r\n  return context;\r\n}\r\n\r\nfunction Carousel({\r\n  orientation = 'horizontal',\r\n  opts,\r\n  setApi,\r\n  plugins,\r\n  className,\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<'div'> & CarouselProps) {\r\n  const [carouselRef, api] = useEmblaCarousel(\r\n    {\r\n      ...opts,\r\n      axis: orientation === 'horizontal' ? 'x' : 'y',\r\n    },\r\n    plugins,\r\n  );\r\n  const [canScrollPrev, setCanScrollPrev] = React.useState(false);\r\n  const [canScrollNext, setCanScrollNext] = React.useState(false);\r\n\r\n  const onSelect = React.useCallback((api: CarouselApi) => {\r\n    if (!api) return;\r\n    setCanScrollPrev(api.canScrollPrev());\r\n    setCanScrollNext(api.canScrollNext());\r\n  }, []);\r\n\r\n  const scrollPrev = React.useCallback(() => {\r\n    api?.scrollPrev();\r\n  }, [api]);\r\n\r\n  const scrollNext = React.useCallback(() => {\r\n    api?.scrollNext();\r\n  }, [api]);\r\n\r\n  const handleKeyDown = React.useCallback(\r\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\r\n      if (event.key === 'ArrowLeft') {\r\n        event.preventDefault();\r\n        scrollPrev();\r\n      } else if (event.key === 'ArrowRight') {\r\n        event.preventDefault();\r\n        scrollNext();\r\n      }\r\n    },\r\n    [scrollPrev, scrollNext],\r\n  );\r\n\r\n  React.useEffect(() => {\r\n    if (!api || !setApi) return;\r\n    setApi(api);\r\n  }, [api, setApi]);\r\n\r\n  React.useEffect(() => {\r\n    if (!api) return;\r\n    onSelect(api);\r\n    api.on('reInit', onSelect);\r\n    api.on('select', onSelect);\r\n\r\n    return () => {\r\n      api?.off('select', onSelect);\r\n    };\r\n  }, [api, onSelect]);\r\n\r\n  return (\r\n    <CarouselContext.Provider\r\n      value={{\r\n        carouselRef,\r\n        api: api,\r\n        opts,\r\n        orientation:\r\n          orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),\r\n        scrollPrev,\r\n        scrollNext,\r\n        canScrollPrev,\r\n        canScrollNext,\r\n      }}\r\n    >\r\n      <div\r\n        onKeyDownCapture={handleKeyDown}\r\n        className={cn('relative', className)}\r\n        role=\"region\"\r\n        aria-roledescription=\"carousel\"\r\n        data-slot=\"carousel\"\r\n        {...props}\r\n      >\r\n        {children}\r\n      </div>\r\n    </CarouselContext.Provider>\r\n  );\r\n}\r\n\r\nfunction CarouselContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  const { carouselRef, orientation } = useCarousel();\r\n\r\n  return (\r\n    <div\r\n      ref={carouselRef}\r\n      className=\"overflow-hidden\"\r\n      data-slot=\"carousel-content\"\r\n    >\r\n      <div\r\n        className={cn(\r\n          'flex',\r\n          orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\r\n          className,\r\n        )}\r\n        {...props}\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction CarouselItem({ className, ...props }: React.ComponentProps<'div'>) {\r\n  const { orientation } = useCarousel();\r\n\r\n  return (\r\n    <div\r\n      role=\"group\"\r\n      aria-roledescription=\"slide\"\r\n      data-slot=\"carousel-item\"\r\n      className={cn(\r\n        'min-w-0 shrink-0 grow-0 basis-full',\r\n        orientation === 'horizontal' ? 'pl-4' : 'pt-4',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CarouselPrevious({\r\n  className,\r\n  variant = 'outline',\r\n  size = 'icon-sm',\r\n  ...props\r\n}: React.ComponentProps<typeof Button>) {\r\n  const { orientation, scrollPrev, canScrollPrev } = useCarousel();\r\n\r\n  return (\r\n    <Button\r\n      data-slot=\"carousel-previous\"\r\n      variant={variant}\r\n      size={size}\r\n      className={cn(\r\n        'rounded-full absolute touch-manipulation',\r\n        orientation === 'horizontal'\r\n          ? 'inset-y-0 -left-12 my-auto'\r\n          : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\r\n        className,\r\n      )}\r\n      disabled={!canScrollPrev}\r\n      onClick={scrollPrev}\r\n      {...props}\r\n    >\r\n      <ChevronLeftIcon className=\"cn-rtl-flip\" />\r\n      <span className=\"sr-only\">Previous slide</span>\r\n    </Button>\r\n  );\r\n}\r\n\r\nfunction CarouselNext({\r\n  className,\r\n  variant = 'outline',\r\n  size = 'icon-sm',\r\n  ...props\r\n}: React.ComponentProps<typeof Button>) {\r\n  const { orientation, scrollNext, canScrollNext } = useCarousel();\r\n\r\n  return (\r\n    <Button\r\n      data-slot=\"carousel-next\"\r\n      variant={variant}\r\n      size={size}\r\n      className={cn(\r\n        'rounded-full absolute touch-manipulation',\r\n        orientation === 'horizontal'\r\n          ? 'inset-y-0 -right-12 my-auto'\r\n          : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\r\n        className,\r\n      )}\r\n      disabled={!canScrollNext}\r\n      onClick={scrollNext}\r\n      {...props}\r\n    >\r\n      <ChevronRightIcon className=\"cn-rtl-flip\" />\r\n      <span className=\"sr-only\">Next slide</span>\r\n    </Button>\r\n  );\r\n}\r\n\r\nexport {\r\n  type CarouselApi,\r\n  Carousel,\r\n  CarouselContent,\r\n  CarouselItem,\r\n  CarouselPrevious,\r\n  CarouselNext,\r\n  useCarousel,\r\n};\r\n","handborne/source/components/ui/chart.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport * as RechartsPrimitive from 'recharts';\r\nimport type { TooltipValueType } from 'recharts';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\n// Format: { THEME_NAME: CSS_SELECTOR }\r\nconst THEMES = { light: '', dark: '.dark' } as const;\r\n\r\nconst INITIAL_DIMENSION = { width: 320, height: 200 } as const;\r\ntype TooltipNameType = number | string;\r\n\r\nexport type ChartConfig = Record<\r\n  string,\r\n  {\r\n    label?: React.ReactNode;\r\n    icon?: React.ComponentType;\r\n  } & (\r\n    | { color?: string; theme?: never }\r\n    | { color?: never; theme: Record<keyof typeof THEMES, string> }\r\n  )\r\n>;\r\n\r\ntype ChartContextProps = {\r\n  config: ChartConfig;\r\n};\r\n\r\nconst ChartContext = React.createContext<ChartContextProps | null>(null);\r\n\r\nfunction useChart() {\r\n  const context = React.useContext(ChartContext);\r\n\r\n  if (!context) {\r\n    throw new Error('useChart must be used within a <ChartContainer />');\r\n  }\r\n\r\n  return context;\r\n}\r\n\r\nfunction ChartContainer({\r\n  id,\r\n  className,\r\n  children,\r\n  config,\r\n  initialDimension = INITIAL_DIMENSION,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  config: ChartConfig;\r\n  children: React.ComponentProps<\r\n    typeof RechartsPrimitive.ResponsiveContainer\r\n  >['children'];\r\n  initialDimension?: {\r\n    width: number;\r\n    height: number;\r\n  };\r\n}) {\r\n  const uniqueId = React.useId();\r\n  const chartId = `chart-${id ?? uniqueId.replace(/:/g, '')}`;\r\n\r\n  return (\r\n    <ChartContext.Provider value={{ config }}>\r\n      <div\r\n        data-slot=\"chart\"\r\n        data-chart={chartId}\r\n        className={cn(\r\n          \"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden\",\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        <ChartStyle id={chartId} config={config} />\r\n        <RechartsPrimitive.ResponsiveContainer\r\n          initialDimension={initialDimension}\r\n        >\r\n          {children}\r\n        </RechartsPrimitive.ResponsiveContainer>\r\n      </div>\r\n    </ChartContext.Provider>\r\n  );\r\n}\r\n\r\nconst ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {\r\n  const colorConfig = Object.entries(config).filter(\r\n    ([, config]) => config.theme ?? config.color,\r\n  );\r\n\r\n  if (!colorConfig.length) {\r\n    return null;\r\n  }\r\n\r\n  return (\r\n    <style\r\n      dangerouslySetInnerHTML={{\r\n        __html: Object.entries(THEMES)\r\n          .map(\r\n            ([theme, prefix]) => `\r\n${prefix} [data-chart=${id}] {\r\n${colorConfig\r\n  .map(([key, itemConfig]) => {\r\n    const color =\r\n      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??\r\n      itemConfig.color;\r\n    return color ? `  --color-${key}: ${color};` : null;\r\n  })\r\n  .join('\\n')}\r\n}\r\n`,\r\n          )\r\n          .join('\\n'),\r\n      }}\r\n    />\r\n  );\r\n};\r\n\r\nconst ChartTooltip = RechartsPrimitive.Tooltip;\r\n\r\nfunction ChartTooltipContent({\r\n  active,\r\n  payload,\r\n  className,\r\n  indicator = 'dot',\r\n  hideLabel = false,\r\n  hideIndicator = false,\r\n  label,\r\n  labelFormatter,\r\n  labelClassName,\r\n  formatter,\r\n  color,\r\n  nameKey,\r\n  labelKey,\r\n}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &\r\n  React.ComponentProps<'div'> & {\r\n    hideLabel?: boolean;\r\n    hideIndicator?: boolean;\r\n    indicator?: 'line' | 'dot' | 'dashed';\r\n    nameKey?: string;\r\n    labelKey?: string;\r\n  } & Omit<\r\n    RechartsPrimitive.DefaultTooltipContentProps<\r\n      TooltipValueType,\r\n      TooltipNameType\r\n    >,\r\n    'accessibilityLayer'\r\n  >) {\r\n  const { config } = useChart();\r\n\r\n  const tooltipLabel = React.useMemo(() => {\r\n    if (hideLabel || !payload?.length) {\r\n      return null;\r\n    }\r\n\r\n    const [item] = payload;\r\n    const key = `${labelKey ?? item?.dataKey ?? item?.name ?? 'value'}`;\r\n    const itemConfig = getPayloadConfigFromPayload(config, item, key);\r\n    const value =\r\n      !labelKey && typeof label === 'string'\r\n        ? (config[label]?.label ?? label)\r\n        : itemConfig?.label;\r\n\r\n    if (labelFormatter) {\r\n      return (\r\n        <div className={cn('font-medium', labelClassName)}>\r\n          {labelFormatter(value, payload)}\r\n        </div>\r\n      );\r\n    }\r\n\r\n    if (!value) {\r\n      return null;\r\n    }\r\n\r\n    return <div className={cn('font-medium', labelClassName)}>{value}</div>;\r\n  }, [\r\n    label,\r\n    labelFormatter,\r\n    payload,\r\n    hideLabel,\r\n    labelClassName,\r\n    config,\r\n    labelKey,\r\n  ]);\r\n\r\n  if (!active || !payload?.length) {\r\n    return null;\r\n  }\r\n\r\n  const nestLabel = payload.length === 1 && indicator !== 'dot';\r\n\r\n  return (\r\n    <div\r\n      className={cn(\r\n        'border-border/50 bg-background gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl grid min-w-32 items-start',\r\n        className,\r\n      )}\r\n    >\r\n      {!nestLabel ? tooltipLabel : null}\r\n      <div className=\"grid gap-1.5\">\r\n        {payload\r\n          .filter((item) => item.type !== 'none')\r\n          .map((item, index) => {\r\n            const key = `${nameKey ?? item.name ?? item.dataKey ?? 'value'}`;\r\n            const itemConfig = getPayloadConfigFromPayload(config, item, key);\r\n            const indicatorColor = color ?? item.payload?.fill ?? item.color;\r\n\r\n            return (\r\n              <div\r\n                key={index}\r\n                className={cn(\r\n                  'flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground',\r\n                  indicator === 'dot' && 'items-center',\r\n                )}\r\n              >\r\n                {formatter && item?.value !== undefined && item.name ? (\r\n                  formatter(item.value, item.name, item, index, item.payload)\r\n                ) : (\r\n                  <>\r\n                    {itemConfig?.icon ? (\r\n                      <itemConfig.icon />\r\n                    ) : (\r\n                      !hideIndicator && (\r\n                        <div\r\n                          className={cn(\r\n                            'shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)',\r\n                            {\r\n                              'h-2.5 w-2.5': indicator === 'dot',\r\n                              'w-1': indicator === 'line',\r\n                              'w-0 border-[1.5px] border-dashed bg-transparent':\r\n                                indicator === 'dashed',\r\n                              'my-0.5': nestLabel && indicator === 'dashed',\r\n                            },\r\n                          )}\r\n                          style={\r\n                            {\r\n                              '--color-bg': indicatorColor,\r\n                              '--color-border': indicatorColor,\r\n                            } as React.CSSProperties\r\n                          }\r\n                        />\r\n                      )\r\n                    )}\r\n                    <div\r\n                      className={cn(\r\n                        'flex flex-1 justify-between leading-none',\r\n                        nestLabel ? 'items-end' : 'items-center',\r\n                      )}\r\n                    >\r\n                      <div className=\"grid gap-1.5\">\r\n                        {nestLabel ? tooltipLabel : null}\r\n                        <span className=\"text-muted-foreground\">\r\n                          {itemConfig?.label ?? item.name}\r\n                        </span>\r\n                      </div>\r\n                      {item.value != null && (\r\n                        <span className=\"font-mono font-medium text-foreground tabular-nums\">\r\n                          {typeof item.value === 'number'\r\n                            ? item.value.toLocaleString()\r\n                            : String(item.value)}\r\n                        </span>\r\n                      )}\r\n                    </div>\r\n                  </>\r\n                )}\r\n              </div>\r\n            );\r\n          })}\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n\r\nconst ChartLegend = RechartsPrimitive.Legend;\r\n\r\nfunction ChartLegendContent({\r\n  className,\r\n  hideIcon = false,\r\n  payload,\r\n  verticalAlign = 'bottom',\r\n  nameKey,\r\n}: React.ComponentProps<'div'> & {\r\n  hideIcon?: boolean;\r\n  nameKey?: string;\r\n} & RechartsPrimitive.DefaultLegendContentProps) {\r\n  const { config } = useChart();\r\n\r\n  if (!payload?.length) {\r\n    return null;\r\n  }\r\n\r\n  return (\r\n    <div\r\n      className={cn(\r\n        'flex items-center justify-center gap-4',\r\n        verticalAlign === 'top' ? 'pb-3' : 'pt-3',\r\n        className,\r\n      )}\r\n    >\r\n      {payload\r\n        .filter((item) => item.type !== 'none')\r\n        .map((item, index) => {\r\n          const key = `${nameKey ?? item.dataKey ?? 'value'}`;\r\n          const itemConfig = getPayloadConfigFromPayload(config, item, key);\r\n\r\n          return (\r\n            <div\r\n              key={index}\r\n              className={cn(\r\n                'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground',\r\n              )}\r\n            >\r\n              {itemConfig?.icon && !hideIcon ? (\r\n                <itemConfig.icon />\r\n              ) : (\r\n                <div\r\n                  className=\"h-2 w-2 shrink-0 rounded-[2px]\"\r\n                  style={{\r\n                    backgroundColor: item.color,\r\n                  }}\r\n                />\r\n              )}\r\n              {itemConfig?.label}\r\n            </div>\r\n          );\r\n        })}\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction getPayloadConfigFromPayload(\r\n  config: ChartConfig,\r\n  payload: unknown,\r\n  key: string,\r\n) {\r\n  if (typeof payload !== 'object' || payload === null) {\r\n    return undefined;\r\n  }\r\n\r\n  const payloadPayload =\r\n    'payload' in payload &&\r\n    typeof payload.payload === 'object' &&\r\n    payload.payload !== null\r\n      ? payload.payload\r\n      : undefined;\r\n\r\n  let configLabelKey: string = key;\r\n\r\n  if (\r\n    key in payload &&\r\n    typeof payload[key as keyof typeof payload] === 'string'\r\n  ) {\r\n    configLabelKey = payload[key as keyof typeof payload] as string;\r\n  } else if (\r\n    payloadPayload &&\r\n    key in payloadPayload &&\r\n    typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'\r\n  ) {\r\n    configLabelKey = payloadPayload[\r\n      key as keyof typeof payloadPayload\r\n    ] as string;\r\n  }\r\n\r\n  return configLabelKey in config ? config[configLabelKey] : config[key];\r\n}\r\n\r\nexport {\r\n  ChartContainer,\r\n  ChartTooltip,\r\n  ChartTooltipContent,\r\n  ChartLegend,\r\n  ChartLegendContent,\r\n  ChartStyle,\r\n};\r\n","handborne/source/components/ui/checkbox.tsx":"'use client';\r\n\r\nimport { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { CheckIcon } from 'lucide-react';\r\n\r\nfunction Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {\r\n  return (\r\n    <CheckboxPrimitive.Root\r\n      data-slot=\"checkbox\"\r\n      className={cn(\r\n        'peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <CheckboxPrimitive.Indicator\r\n        data-slot=\"checkbox-indicator\"\r\n        className=\"grid place-content-center text-current transition-none [&>svg]:size-3.5\"\r\n      >\r\n        <CheckIcon />\r\n      </CheckboxPrimitive.Indicator>\r\n    </CheckboxPrimitive.Root>\r\n  );\r\n}\r\n\r\nexport { Checkbox };\r\n","handborne/source/components/ui/collapsible.tsx":"'use client';\r\n\r\nimport { Collapsible as CollapsiblePrimitive } from '@base-ui/react/collapsible';\r\n\r\nfunction Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {\r\n  return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\r\n}\r\n\r\nfunction CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {\r\n  return (\r\n    <CollapsiblePrimitive.Trigger data-slot=\"collapsible-trigger\" {...props} />\r\n  );\r\n}\r\n\r\nfunction CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {\r\n  return (\r\n    <CollapsiblePrimitive.Panel data-slot=\"collapsible-content\" {...props} />\r\n  );\r\n}\r\n\r\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\r\n","handborne/source/components/ui/combobox.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Combobox as ComboboxPrimitive } from '@base-ui/react';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport {\r\n  InputGroup,\r\n  InputGroupAddon,\r\n  InputGroupButton,\r\n  InputGroupInput,\r\n} from '@/components/ui/input-group';\r\nimport { ChevronDownIcon, XIcon, CheckIcon } from 'lucide-react';\r\n\r\nconst Combobox = ComboboxPrimitive.Root;\r\n\r\nfunction ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {\r\n  return <ComboboxPrimitive.Value data-slot=\"combobox-value\" {...props} />;\r\n}\r\n\r\nfunction ComboboxTrigger({\r\n  className,\r\n  children,\r\n  ...props\r\n}: ComboboxPrimitive.Trigger.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Trigger\r\n      data-slot=\"combobox-trigger\"\r\n      className={cn(\"[&_svg:not([class*='size-'])]:size-4\", className)}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <ChevronDownIcon className=\"text-muted-foreground size-4 pointer-events-none\" />\r\n    </ComboboxPrimitive.Trigger>\r\n  );\r\n}\r\n\r\nfunction ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Clear\r\n      data-slot=\"combobox-clear\"\r\n      render={<InputGroupButton variant=\"ghost\" size=\"icon-xs\" />}\r\n      className={cn(className)}\r\n      {...props}\r\n    >\r\n      <XIcon className=\"pointer-events-none\" />\r\n    </ComboboxPrimitive.Clear>\r\n  );\r\n}\r\n\r\nfunction ComboboxInput({\r\n  className,\r\n  children,\r\n  disabled = false,\r\n  showTrigger = true,\r\n  showClear = false,\r\n  ...props\r\n}: ComboboxPrimitive.Input.Props & {\r\n  showTrigger?: boolean;\r\n  showClear?: boolean;\r\n}) {\r\n  return (\r\n    <InputGroup className={cn('w-auto', className)}>\r\n      <ComboboxPrimitive.Input\r\n        render={<InputGroupInput disabled={disabled} />}\r\n        {...props}\r\n      />\r\n      <InputGroupAddon align=\"inline-end\">\r\n        {showTrigger && (\r\n          <InputGroupButton\r\n            size=\"icon-xs\"\r\n            variant=\"ghost\"\r\n            render={<ComboboxTrigger />}\r\n            data-slot=\"input-group-button\"\r\n            className=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\r\n            disabled={disabled}\r\n          />\r\n        )}\r\n        {showClear && <ComboboxClear disabled={disabled} />}\r\n      </InputGroupAddon>\r\n      {children}\r\n    </InputGroup>\r\n  );\r\n}\r\n\r\nfunction ComboboxContent({\r\n  className,\r\n  side = 'bottom',\r\n  sideOffset = 6,\r\n  align = 'start',\r\n  alignOffset = 0,\r\n  anchor,\r\n  ...props\r\n}: ComboboxPrimitive.Popup.Props &\r\n  Pick<\r\n    ComboboxPrimitive.Positioner.Props,\r\n    'side' | 'align' | 'sideOffset' | 'alignOffset' | 'anchor'\r\n  >) {\r\n  return (\r\n    <ComboboxPrimitive.Portal>\r\n      <ComboboxPrimitive.Positioner\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        anchor={anchor}\r\n        className=\"isolate z-50\"\r\n      >\r\n        <ComboboxPrimitive.Popup\r\n          data-slot=\"combobox-content\"\r\n          data-chips={!!anchor}\r\n          className={cn(\r\n            'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:border-input/30 overflow-hidden rounded-lg shadow-md ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:shadow-none data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 cn-menu-target cn-menu-translucent group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n      </ComboboxPrimitive.Positioner>\r\n    </ComboboxPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {\r\n  return (\r\n    <ComboboxPrimitive.List\r\n      data-slot=\"combobox-list\"\r\n      className={cn(\r\n        'no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 p-1 data-empty:p-0 overflow-y-auto overscroll-contain',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxItem({\r\n  className,\r\n  children,\r\n  ...props\r\n}: ComboboxPrimitive.Item.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Item\r\n      data-slot=\"combobox-item\"\r\n      className={cn(\r\n        \"data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-2 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <ComboboxPrimitive.ItemIndicator\r\n        render={\r\n          <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\" />\r\n        }\r\n      >\r\n        <CheckIcon className=\"pointer-events-none\" />\r\n      </ComboboxPrimitive.ItemIndicator>\r\n    </ComboboxPrimitive.Item>\r\n  );\r\n}\r\n\r\nfunction ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Group\r\n      data-slot=\"combobox-group\"\r\n      className={cn(className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxLabel({\r\n  className,\r\n  ...props\r\n}: ComboboxPrimitive.GroupLabel.Props) {\r\n  return (\r\n    <ComboboxPrimitive.GroupLabel\r\n      data-slot=\"combobox-label\"\r\n      className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Collection data-slot=\"combobox-collection\" {...props} />\r\n  );\r\n}\r\n\r\nfunction ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Empty\r\n      data-slot=\"combobox-empty\"\r\n      className={cn(\r\n        'text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxSeparator({\r\n  className,\r\n  ...props\r\n}: ComboboxPrimitive.Separator.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Separator\r\n      data-slot=\"combobox-separator\"\r\n      className={cn('bg-border -mx-1 my-1 h-px', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxChips({\r\n  className,\r\n  ...props\r\n}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &\r\n  ComboboxPrimitive.Chips.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Chips\r\n      data-slot=\"combobox-chips\"\r\n      className={cn(\r\n        'dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-8 flex-wrap items-center gap-1 rounded-lg border bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:ring-3 has-aria-invalid:ring-3 has-data-[slot=combobox-chip]:px-1',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ComboboxChip({\r\n  className,\r\n  children,\r\n  showRemove = true,\r\n  ...props\r\n}: ComboboxPrimitive.Chip.Props & {\r\n  showRemove?: boolean;\r\n}) {\r\n  return (\r\n    <ComboboxPrimitive.Chip\r\n      data-slot=\"combobox-chip\"\r\n      className={cn(\r\n        'bg-muted text-foreground flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm px-1.5 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      {showRemove && (\r\n        <ComboboxPrimitive.ChipRemove\r\n          render={<Button variant=\"ghost\" size=\"icon-xs\" />}\r\n          className=\"-ml-1 opacity-50 hover:opacity-100\"\r\n          data-slot=\"combobox-chip-remove\"\r\n        >\r\n          <XIcon className=\"pointer-events-none\" />\r\n        </ComboboxPrimitive.ChipRemove>\r\n      )}\r\n    </ComboboxPrimitive.Chip>\r\n  );\r\n}\r\n\r\nfunction ComboboxChipsInput({\r\n  className,\r\n  ...props\r\n}: ComboboxPrimitive.Input.Props) {\r\n  return (\r\n    <ComboboxPrimitive.Input\r\n      data-slot=\"combobox-chip-input\"\r\n      className={cn('min-w-16 flex-1 outline-none', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction useComboboxAnchor() {\r\n  return React.useRef<HTMLDivElement | null>(null);\r\n}\r\n\r\nexport {\r\n  Combobox,\r\n  ComboboxInput,\r\n  ComboboxContent,\r\n  ComboboxList,\r\n  ComboboxItem,\r\n  ComboboxGroup,\r\n  ComboboxLabel,\r\n  ComboboxCollection,\r\n  ComboboxEmpty,\r\n  ComboboxSeparator,\r\n  ComboboxChips,\r\n  ComboboxChip,\r\n  ComboboxChipsInput,\r\n  ComboboxTrigger,\r\n  ComboboxValue,\r\n  useComboboxAnchor,\r\n};\r\n","handborne/source/components/ui/command.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Command as CommandPrimitive } from 'cmdk';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport {\r\n  Dialog,\r\n  DialogContent,\r\n  DialogDescription,\r\n  DialogHeader,\r\n  DialogTitle,\r\n} from '@/components/ui/dialog';\r\nimport { InputGroup, InputGroupAddon } from '@/components/ui/input-group';\r\nimport { SearchIcon, CheckIcon } from 'lucide-react';\r\n\r\nfunction Command({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive>) {\r\n  return (\r\n    <CommandPrimitive\r\n      data-slot=\"command\"\r\n      className={cn(\r\n        'bg-popover text-popover-foreground rounded-xl! p-1 flex size-full flex-col overflow-hidden',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandDialog({\r\n  title = 'Command Palette',\r\n  description = 'Search for a command to run...',\r\n  children,\r\n  className,\r\n  showCloseButton = false,\r\n  ...props\r\n}: Omit<React.ComponentProps<typeof Dialog>, 'children'> & {\r\n  title?: string;\r\n  description?: string;\r\n  className?: string;\r\n  showCloseButton?: boolean;\r\n  children: React.ReactNode;\r\n}) {\r\n  return (\r\n    <Dialog {...props}>\r\n      <DialogHeader className=\"sr-only\">\r\n        <DialogTitle>{title}</DialogTitle>\r\n        <DialogDescription>{description}</DialogDescription>\r\n      </DialogHeader>\r\n      <DialogContent\r\n        className={cn(\r\n          'rounded-xl! top-1/3 translate-y-0 overflow-hidden p-0',\r\n          className,\r\n        )}\r\n        showCloseButton={showCloseButton}\r\n      >\r\n        {children}\r\n      </DialogContent>\r\n    </Dialog>\r\n  );\r\n}\r\n\r\nfunction CommandInput({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\r\n  return (\r\n    <div data-slot=\"command-input-wrapper\" className=\"p-1 pb-0\">\r\n      <InputGroup className=\"bg-input/30 border-input/30 h-8! rounded-lg! shadow-none! *:data-[slot=input-group-addon]:pl-2!\">\r\n        <CommandPrimitive.Input\r\n          data-slot=\"command-input\"\r\n          className={cn(\r\n            'w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n        <InputGroupAddon>\r\n          <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\r\n        </InputGroupAddon>\r\n      </InputGroup>\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction CommandList({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.List>) {\r\n  return (\r\n    <CommandPrimitive.List\r\n      data-slot=\"command-list\"\r\n      className={cn(\r\n        'no-scrollbar max-h-72 scroll-py-1 outline-none overflow-x-hidden overflow-y-auto',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandEmpty({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Empty>) {\r\n  return (\r\n    <CommandPrimitive.Empty\r\n      data-slot=\"command-empty\"\r\n      className={cn('py-6 text-center text-sm', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandGroup({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\r\n  return (\r\n    <CommandPrimitive.Group\r\n      data-slot=\"command-group\"\r\n      className={cn(\r\n        'text-foreground **:[[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandSeparator({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\r\n  return (\r\n    <CommandPrimitive.Separator\r\n      data-slot=\"command-separator\"\r\n      className={cn('bg-border -mx-1 h-px', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandItem({\r\n  className,\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Item>) {\r\n  return (\r\n    <CommandPrimitive.Item\r\n      data-slot=\"command-item\"\r\n      className={cn(\r\n        \"data-selected:bg-muted data-selected:text-foreground data-selected:*:[svg]:text-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! [&_svg:not([class*='size-'])]:size-4 group/command-item data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <CheckIcon className=\"ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100\" />\r\n    </CommandPrimitive.Item>\r\n  );\r\n}\r\n\r\nfunction CommandShortcut({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"command-shortcut\"\r\n      className={cn(\r\n        'text-muted-foreground group-data-selected/command-item:text-foreground ml-auto text-xs tracking-widest',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Command,\r\n  CommandDialog,\r\n  CommandInput,\r\n  CommandList,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandItem,\r\n  CommandShortcut,\r\n  CommandSeparator,\r\n};\r\n","handborne/source/components/ui/context-menu.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { ContextMenu as ContextMenuPrimitive } from '@base-ui/react/context-menu';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronRightIcon, CheckIcon } from 'lucide-react';\r\n\r\nfunction ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {\r\n  return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\r\n}\r\n\r\nfunction ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.Portal data-slot=\"context-menu-portal\" {...props} />\r\n  );\r\n}\r\n\r\nfunction ContextMenuTrigger({\r\n  className,\r\n  ...props\r\n}: ContextMenuPrimitive.Trigger.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.Trigger\r\n      data-slot=\"context-menu-trigger\"\r\n      className={cn('select-none', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuContent({\r\n  className,\r\n  align = 'start',\r\n  alignOffset = 4,\r\n  side = 'right',\r\n  sideOffset = 0,\r\n  ...props\r\n}: ContextMenuPrimitive.Popup.Props &\r\n  Pick<\r\n    ContextMenuPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset'\r\n  >) {\r\n  return (\r\n    <ContextMenuPrimitive.Portal>\r\n      <ContextMenuPrimitive.Positioner\r\n        className=\"isolate z-50 outline-none\"\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n      >\r\n        <ContextMenuPrimitive.Popup\r\n          data-slot=\"context-menu-content\"\r\n          className={cn(\r\n            'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-36 rounded-lg p-1 shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 cn-menu-target cn-menu-translucent z-50 max-h-(--available-height) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n      </ContextMenuPrimitive.Positioner>\r\n    </ContextMenuPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />\r\n  );\r\n}\r\n\r\nfunction ContextMenuLabel({\r\n  className,\r\n  inset,\r\n  ...props\r\n}: ContextMenuPrimitive.GroupLabel.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <ContextMenuPrimitive.GroupLabel\r\n      data-slot=\"context-menu-label\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        'text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuItem({\r\n  className,\r\n  inset,\r\n  variant = 'default',\r\n  ...props\r\n}: ContextMenuPrimitive.Item.Props & {\r\n  inset?: boolean;\r\n  variant?: 'default' | 'destructive';\r\n}) {\r\n  return (\r\n    <ContextMenuPrimitive.Item\r\n      data-slot=\"context-menu-item\"\r\n      data-inset={inset}\r\n      data-variant={variant}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive focus:*:[svg]:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/context-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.SubmenuRoot data-slot=\"context-menu-sub\" {...props} />\r\n  );\r\n}\r\n\r\nfunction ContextMenuSubTrigger({\r\n  className,\r\n  inset,\r\n  children,\r\n  ...props\r\n}: ContextMenuPrimitive.SubmenuTrigger.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <ContextMenuPrimitive.SubmenuTrigger\r\n      data-slot=\"context-menu-sub-trigger\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <ChevronRightIcon className=\"cn-rtl-flip ml-auto\" />\r\n    </ContextMenuPrimitive.SubmenuTrigger>\r\n  );\r\n}\r\n\r\nfunction ContextMenuSubContent({\r\n  ...props\r\n}: React.ComponentProps<typeof ContextMenuContent>) {\r\n  return (\r\n    <ContextMenuContent\r\n      data-slot=\"context-menu-sub-content\"\r\n      className=\"shadow-lg cn-menu-target cn-menu-translucent\"\r\n      side=\"right\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuCheckboxItem({\r\n  className,\r\n  children,\r\n  checked,\r\n  inset,\r\n  ...props\r\n}: ContextMenuPrimitive.CheckboxItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <ContextMenuPrimitive.CheckboxItem\r\n      data-slot=\"context-menu-checkbox-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      checked={checked}\r\n      {...props}\r\n    >\r\n      <span className=\"absolute right-2 pointer-events-none\">\r\n        <ContextMenuPrimitive.CheckboxItemIndicator>\r\n          <CheckIcon />\r\n        </ContextMenuPrimitive.CheckboxItemIndicator>\r\n      </span>\r\n      {children}\r\n    </ContextMenuPrimitive.CheckboxItem>\r\n  );\r\n}\r\n\r\nfunction ContextMenuRadioGroup({\r\n  ...props\r\n}: ContextMenuPrimitive.RadioGroup.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.RadioGroup\r\n      data-slot=\"context-menu-radio-group\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuRadioItem({\r\n  className,\r\n  children,\r\n  inset,\r\n  ...props\r\n}: ContextMenuPrimitive.RadioItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <ContextMenuPrimitive.RadioItem\r\n      data-slot=\"context-menu-radio-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <span className=\"absolute right-2 pointer-events-none\">\r\n        <ContextMenuPrimitive.RadioItemIndicator>\r\n          <CheckIcon />\r\n        </ContextMenuPrimitive.RadioItemIndicator>\r\n      </span>\r\n      {children}\r\n    </ContextMenuPrimitive.RadioItem>\r\n  );\r\n}\r\n\r\nfunction ContextMenuSeparator({\r\n  className,\r\n  ...props\r\n}: ContextMenuPrimitive.Separator.Props) {\r\n  return (\r\n    <ContextMenuPrimitive.Separator\r\n      data-slot=\"context-menu-separator\"\r\n      className={cn('bg-border -mx-1 my-1 h-px', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ContextMenuShortcut({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"context-menu-shortcut\"\r\n      className={cn(\r\n        'text-muted-foreground group-focus/context-menu-item:text-accent-foreground ml-auto text-xs tracking-widest',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  ContextMenu,\r\n  ContextMenuTrigger,\r\n  ContextMenuContent,\r\n  ContextMenuItem,\r\n  ContextMenuCheckboxItem,\r\n  ContextMenuRadioItem,\r\n  ContextMenuLabel,\r\n  ContextMenuSeparator,\r\n  ContextMenuShortcut,\r\n  ContextMenuGroup,\r\n  ContextMenuPortal,\r\n  ContextMenuSub,\r\n  ContextMenuSubContent,\r\n  ContextMenuSubTrigger,\r\n  ContextMenuRadioGroup,\r\n};\r\n","handborne/source/components/ui/dialog.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Dialog as DialogPrimitive } from '@base-ui/react/dialog';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { XIcon } from 'lucide-react';\r\n\r\nfunction Dialog({ ...props }: DialogPrimitive.Root.Props) {\r\n  return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\r\n}\r\n\r\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\r\n  return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\r\n}\r\n\r\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\r\n  return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\r\n}\r\n\r\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\r\n  return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\r\n}\r\n\r\nfunction DialogOverlay({\r\n  className,\r\n  ...props\r\n}: DialogPrimitive.Backdrop.Props) {\r\n  return (\r\n    <DialogPrimitive.Backdrop\r\n      data-slot=\"dialog-overlay\"\r\n      className={cn(\r\n        'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DialogContent({\r\n  className,\r\n  children,\r\n  showCloseButton = true,\r\n  ...props\r\n}: DialogPrimitive.Popup.Props & {\r\n  showCloseButton?: boolean;\r\n}) {\r\n  return (\r\n    <DialogPortal>\r\n      <DialogOverlay />\r\n      <DialogPrimitive.Popup\r\n        data-slot=\"dialog-content\"\r\n        className={cn(\r\n          'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        {children}\r\n        {showCloseButton && (\r\n          <DialogPrimitive.Close\r\n            data-slot=\"dialog-close\"\r\n            render={\r\n              <Button\r\n                variant=\"ghost\"\r\n                className=\"absolute top-2 right-2\"\r\n                size=\"icon-sm\"\r\n              />\r\n            }\r\n          >\r\n            <XIcon />\r\n            <span className=\"sr-only\">Close</span>\r\n          </DialogPrimitive.Close>\r\n        )}\r\n      </DialogPrimitive.Popup>\r\n    </DialogPortal>\r\n  );\r\n}\r\n\r\nfunction DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"dialog-header\"\r\n      className={cn('flex flex-col gap-2', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DialogFooter({\r\n  className,\r\n  showCloseButton = false,\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  showCloseButton?: boolean;\r\n}) {\r\n  return (\r\n    <div\r\n      data-slot=\"dialog-footer\"\r\n      className={cn(\r\n        '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      {showCloseButton && (\r\n        <DialogPrimitive.Close render={<Button variant=\"outline\" />}>\r\n          Close\r\n        </DialogPrimitive.Close>\r\n      )}\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\r\n  return (\r\n    <DialogPrimitive.Title\r\n      data-slot=\"dialog-title\"\r\n      className={cn(\r\n        'font-heading text-base leading-none font-medium',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DialogDescription({\r\n  className,\r\n  ...props\r\n}: DialogPrimitive.Description.Props) {\r\n  return (\r\n    <DialogPrimitive.Description\r\n      data-slot=\"dialog-description\"\r\n      className={cn(\r\n        'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Dialog,\r\n  DialogClose,\r\n  DialogContent,\r\n  DialogDescription,\r\n  DialogFooter,\r\n  DialogHeader,\r\n  DialogOverlay,\r\n  DialogPortal,\r\n  DialogTitle,\r\n  DialogTrigger,\r\n};\r\n","handborne/source/components/ui/direction.tsx":"'use client';\r\n\r\nexport {\r\n  DirectionProvider,\r\n  useDirection,\r\n} from '@base-ui/react/direction-provider';\r\n","handborne/source/components/ui/drawer.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Drawer as DrawerPrimitive } from '@base-ui/react/drawer';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\ntype DrawerContextProps = {\r\n  hasSnapPoints: boolean;\r\n  modal: DrawerPrimitive.Root.Props['modal'];\r\n  showSwipeHandle: boolean;\r\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props['swipeDirection']>;\r\n};\r\n\r\nconst DrawerContext = React.createContext<DrawerContextProps | null>(null);\r\n\r\nfunction useDrawer() {\r\n  const context = React.useContext(DrawerContext);\r\n\r\n  if (!context) {\r\n    throw new Error('useDrawer must be used within a Drawer.');\r\n  }\r\n\r\n  return context;\r\n}\r\n\r\nfunction Drawer({\r\n  modal = true,\r\n  showSwipeHandle = false,\r\n  snapPoints,\r\n  swipeDirection = 'down',\r\n  ...props\r\n}: DrawerPrimitive.Root.Props & {\r\n  showSwipeHandle?: boolean;\r\n}) {\r\n  const hasSnapPoints = snapPoints != null && snapPoints.length > 0;\r\n  const contextValue = React.useMemo(\r\n    () => ({ hasSnapPoints, modal, showSwipeHandle, swipeDirection }),\r\n    [hasSnapPoints, modal, showSwipeHandle, swipeDirection],\r\n  );\r\n\r\n  return (\r\n    <DrawerContext.Provider value={contextValue}>\r\n      <DrawerPrimitive.Root\r\n        data-slot=\"drawer\"\r\n        modal={modal}\r\n        snapPoints={snapPoints}\r\n        swipeDirection={swipeDirection}\r\n        {...props}\r\n      />\r\n    </DrawerContext.Provider>\r\n  );\r\n}\r\n\r\nfunction DrawerTrigger({ ...props }: DrawerPrimitive.Trigger.Props) {\r\n  return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\r\n}\r\n\r\nfunction DrawerPortal({ ...props }: DrawerPrimitive.Portal.Props) {\r\n  return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\r\n}\r\n\r\nfunction DrawerClose({ ...props }: DrawerPrimitive.Close.Props) {\r\n  return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\r\n}\r\n\r\nfunction DrawerOverlay({\r\n  className,\r\n  ...props\r\n}: DrawerPrimitive.Backdrop.Props) {\r\n  return (\r\n    <DrawerPrimitive.Backdrop\r\n      data-slot=\"drawer-overlay\"\r\n      className={cn(\r\n        'bg-black/10 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50 min-h-dvh opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] select-none data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-snap-points:[--drawer-overlay-min-opacity:0.5] data-starting-style:opacity-0 data-swiping:duration-0 supports-[-webkit-touch-callout:none]:absolute',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DrawerSwipeHandle({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"drawer-swipe-handle\"\r\n      aria-hidden=\"true\"\r\n      className={cn(\r\n        'after:block after:shrink-0 after:rounded-full after:bg-muted group-data-[swipe-axis=y]/drawer-popup:h-3 group-data-[swipe-axis=y]/drawer-popup:w-full group-data-[swipe-axis=y]/drawer-popup:justify-center group-data-[swipe-axis=y]/drawer-popup:after:h-1 group-data-[swipe-axis=y]/drawer-popup:after:w-24 group-data-[swipe-direction=down]/drawer-popup:items-end group-data-[swipe-direction=up]/drawer-popup:items-start group-data-[swipe-axis=x]/drawer-popup:h-full group-data-[swipe-axis=x]/drawer-popup:w-3 group-data-[swipe-axis=x]/drawer-popup:items-center group-data-[swipe-axis=x]/drawer-popup:after:h-24 group-data-[swipe-axis=x]/drawer-popup:after:w-1 group-data-[swipe-direction=left]/drawer-popup:justify-start group-data-[swipe-direction=right]/drawer-popup:justify-end relative z-10 flex shrink-0 cursor-grab transition-opacity duration-200 group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-[swipe-direction=left]/drawer-popup:order-last group-data-[swipe-direction=up]/drawer-popup:order-last active:cursor-grabbing',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DrawerContent({\r\n  className,\r\n  children,\r\n  ...props\r\n}: DrawerPrimitive.Popup.Props) {\r\n  const { hasSnapPoints, modal, showSwipeHandle, swipeDirection } = useDrawer();\r\n  const swipeAxis =\r\n    swipeDirection === 'down' || swipeDirection === 'up' ? 'y' : 'x';\r\n\r\n  return (\r\n    <DrawerPortal data-slot=\"drawer-portal\">\r\n      {modal === true && (\r\n        <DrawerOverlay data-snap-points={hasSnapPoints ? '' : undefined} />\r\n      )}\r\n      <DrawerPrimitive.Viewport\r\n        data-slot=\"drawer-viewport\"\r\n        data-modal={modal}\r\n        className=\"pointer-events-none fixed inset-0 z-50 select-none data-[modal=true]:pointer-events-auto\"\r\n      >\r\n        <DrawerPrimitive.Popup\r\n          data-slot=\"drawer-popup\"\r\n          data-swipe-axis={swipeAxis}\r\n          data-snap-points={hasSnapPoints ? '' : undefined}\r\n          className={cn(\r\n            // Base.\r\n            'bg-popover text-popover-foreground text-sm data-[swipe-direction=down]:rounded-t-xl data-[swipe-direction=down]:border-t data-[swipe-direction=left]:rounded-r-xl data-[swipe-direction=left]:border-r data-[swipe-direction=right]:rounded-l-xl data-[swipe-direction=right]:border-l data-[swipe-direction=up]:rounded-b-xl data-[swipe-direction=up]:border-b group/drawer-popup pointer-events-auto fixed z-50 m-(--drawer-inset,0px) flex h-(--drawer-content-height) max-h-(--drawer-content-max-height,none) min-h-0 w-(--drawer-content-width,auto) transform-[translate3d(var(--translate-x,0px),var(--translate-y,0px),0)_scale(var(--stack-scale))] flex-col transition-[transform,height,opacity,filter] duration-450 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform outline-none select-none [interpolate-size:allow-keywords]',\r\n            // Nested.\r\n            'data-nested-drawer-open:overflow-hidden data-nested-drawer-open:brightness-95',\r\n            // Bleed.\r\n            'after:pointer-events-none after:absolute after:bg-(--drawer-bleed-background,var(--color-popover)) data-[swipe-axis=x]:after:inset-y-0 data-[swipe-axis=x]:after:w-(--bleed) data-[swipe-axis=y]:after:inset-x-0 data-[swipe-axis=y]:after:h-(--bleed) data-[swipe-direction=down]:after:top-full data-[swipe-direction=left]:after:right-full data-[swipe-direction=right]:after:left-full data-[swipe-direction=up]:after:bottom-full',\r\n            // Sizing.\r\n            '[--drawer-content-height:var(--drawer-height,auto)] data-[swipe-axis=x]:[--drawer-content-width:75%] data-[swipe-axis=y]:[--drawer-content-max-height:calc(100dvh-6rem)] data-[swipe-axis=y]:data-snap-points:[--drawer-content-height:100dvh] data-[swipe-axis=x]:sm:[--drawer-content-width:24rem]',\r\n            // Stack.\r\n            '[--bleed:3rem] [--peek:1rem] [--stack-height:var(--drawer-frontmost-height,var(--drawer-height,0px))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-scale-base:max(0,calc(1-(var(--nested-drawers)*var(--stack-step))))] [--stack-scale:clamp(0,calc(var(--stack-scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--stack-shrink:calc(1-var(--stack-scale))] [--stack-step:0.05]',\r\n            // Transitions.\r\n            'data-ending-style:transform-(--closed-transform) data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-starting-style:transform-(--closed-transform) data-swiping:duration-0 data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)]',\r\n            // Axis: y.\r\n            'data-[swipe-axis=y]:inset-x-0 data-[swipe-axis=y]:data-nested-drawer-open:h-(--stack-height)',\r\n            // Axis: x.\r\n            'data-[swipe-axis=x]:inset-y-0 data-[swipe-axis=x]:flex-row',\r\n            // Direction: down.\r\n            'data-[swipe-direction=down]:bottom-0 data-[swipe-direction=down]:origin-bottom data-[swipe-direction=down]:[--closed-transform:translate3d(0,calc(100%+var(--drawer-inset,0px)+2px),0)] data-[swipe-direction=down]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--stack-shrink)*var(--stack-height)))]',\r\n            // Direction: up.\r\n            'data-[swipe-direction=up]:top-0 data-[swipe-direction=up]:origin-top data-[swipe-direction=up]:[--closed-transform:translate3d(0,calc(-100%-var(--drawer-inset,0px)-2px),0)] data-[swipe-direction=up]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)+var(--stack-peek-offset)+(var(--stack-shrink)*var(--stack-height)))]',\r\n            // Direction: left.\r\n            'data-[swipe-direction=left]:left-0 data-[swipe-direction=left]:origin-left data-[swipe-direction=left]:[--closed-transform:translate3d(calc(-100%-var(--drawer-inset,0px)-2px),0,0)] data-[swipe-direction=left]:[--translate-x:calc(var(--drawer-swipe-movement-x)+var(--stack-peek-offset)+(var(--stack-shrink)*100%))]',\r\n            // Direction: right.\r\n            'data-[swipe-direction=right]:right-0 data-[swipe-direction=right]:origin-right data-[swipe-direction=right]:[--closed-transform:translate3d(calc(100%+var(--drawer-inset,0px)+2px),0,0)] data-[swipe-direction=right]:[--translate-x:calc(var(--drawer-swipe-movement-x)-var(--stack-peek-offset)-(var(--stack-shrink)*100%))]',\r\n            className,\r\n          )}\r\n          {...props}\r\n        >\r\n          {showSwipeHandle && <DrawerSwipeHandle />}\r\n          <DrawerPrimitive.Content\r\n            data-slot=\"drawer-content\"\r\n            className={cn(\r\n              'flex min-h-0 flex-1 flex-col overflow-hidden overscroll-contain rounded-[inherit] transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] select-text group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-swiping/drawer-popup:select-none',\r\n            )}\r\n          >\r\n            {children}\r\n          </DrawerPrimitive.Content>\r\n        </DrawerPrimitive.Popup>\r\n      </DrawerPrimitive.Viewport>\r\n    </DrawerPortal>\r\n  );\r\n}\r\n\r\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"drawer-header\"\r\n      className={cn(\r\n        'gap-0.5 p-4 pb-0 md:gap-0.5 md:text-left flex shrink-0 flex-col group-data-[swipe-axis=y]/drawer-popup:text-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"drawer-footer\"\r\n      className={cn('gap-2 p-4 pt-0 mt-auto flex shrink-0 flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DrawerTitle({ className, ...props }: DrawerPrimitive.Title.Props) {\r\n  return (\r\n    <DrawerPrimitive.Title\r\n      data-slot=\"drawer-title\"\r\n      className={cn(\r\n        'text-foreground text-base font-medium cn-font-heading',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DrawerDescription({\r\n  className,\r\n  ...props\r\n}: DrawerPrimitive.Description.Props) {\r\n  return (\r\n    <DrawerPrimitive.Description\r\n      data-slot=\"drawer-description\"\r\n      className={cn('text-muted-foreground text-sm text-balance', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Drawer,\r\n  DrawerPortal,\r\n  DrawerOverlay,\r\n  DrawerSwipeHandle,\r\n  DrawerTrigger,\r\n  DrawerClose,\r\n  DrawerContent,\r\n  DrawerHeader,\r\n  DrawerFooter,\r\n  DrawerTitle,\r\n  DrawerDescription,\r\n};\r\n","handborne/source/components/ui/dropdown-menu.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Menu as MenuPrimitive } from '@base-ui/react/menu';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronRightIcon, CheckIcon } from 'lucide-react';\r\n\r\nfunction DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {\r\n  return <MenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {\r\n  return <MenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {\r\n  return <MenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuContent({\r\n  align = 'start',\r\n  alignOffset = 0,\r\n  side = 'bottom',\r\n  sideOffset = 4,\r\n  className,\r\n  ...props\r\n}: MenuPrimitive.Popup.Props &\r\n  Pick<\r\n    MenuPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset'\r\n  >) {\r\n  return (\r\n    <MenuPrimitive.Portal>\r\n      <MenuPrimitive.Positioner\r\n        className=\"isolate z-50 outline-none\"\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n      >\r\n        <MenuPrimitive.Popup\r\n          data-slot=\"dropdown-menu-content\"\r\n          className={cn(\r\n            'z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n      </MenuPrimitive.Positioner>\r\n    </MenuPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {\r\n  return <MenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuLabel({\r\n  className,\r\n  inset,\r\n  ...props\r\n}: MenuPrimitive.GroupLabel.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.GroupLabel\r\n      data-slot=\"dropdown-menu-label\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DropdownMenuItem({\r\n  className,\r\n  inset,\r\n  variant = 'default',\r\n  ...props\r\n}: MenuPrimitive.Item.Props & {\r\n  inset?: boolean;\r\n  variant?: 'default' | 'destructive';\r\n}) {\r\n  return (\r\n    <MenuPrimitive.Item\r\n      data-slot=\"dropdown-menu-item\"\r\n      data-inset={inset}\r\n      data-variant={variant}\r\n      className={cn(\r\n        \"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {\r\n  return <MenuPrimitive.SubmenuRoot data-slot=\"dropdown-menu-sub\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuSubTrigger({\r\n  className,\r\n  inset,\r\n  children,\r\n  ...props\r\n}: MenuPrimitive.SubmenuTrigger.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.SubmenuTrigger\r\n      data-slot=\"dropdown-menu-sub-trigger\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <ChevronRightIcon className=\"ml-auto\" />\r\n    </MenuPrimitive.SubmenuTrigger>\r\n  );\r\n}\r\n\r\nfunction DropdownMenuSubContent({\r\n  align = 'start',\r\n  alignOffset = -3,\r\n  side = 'right',\r\n  sideOffset = 0,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuContent>) {\r\n  return (\r\n    <DropdownMenuContent\r\n      data-slot=\"dropdown-menu-sub-content\"\r\n      className={cn(\r\n        'w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\r\n        className,\r\n      )}\r\n      align={align}\r\n      alignOffset={alignOffset}\r\n      side={side}\r\n      sideOffset={sideOffset}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DropdownMenuCheckboxItem({\r\n  className,\r\n  children,\r\n  checked,\r\n  inset,\r\n  ...props\r\n}: MenuPrimitive.CheckboxItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.CheckboxItem\r\n      data-slot=\"dropdown-menu-checkbox-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      checked={checked}\r\n      {...props}\r\n    >\r\n      <span\r\n        className=\"pointer-events-none absolute right-2 flex items-center justify-center\"\r\n        data-slot=\"dropdown-menu-checkbox-item-indicator\"\r\n      >\r\n        <MenuPrimitive.CheckboxItemIndicator>\r\n          <CheckIcon />\r\n        </MenuPrimitive.CheckboxItemIndicator>\r\n      </span>\r\n      {children}\r\n    </MenuPrimitive.CheckboxItem>\r\n  );\r\n}\r\n\r\nfunction DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {\r\n  return (\r\n    <MenuPrimitive.RadioGroup\r\n      data-slot=\"dropdown-menu-radio-group\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DropdownMenuRadioItem({\r\n  className,\r\n  children,\r\n  inset,\r\n  ...props\r\n}: MenuPrimitive.RadioItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.RadioItem\r\n      data-slot=\"dropdown-menu-radio-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <span\r\n        className=\"pointer-events-none absolute right-2 flex items-center justify-center\"\r\n        data-slot=\"dropdown-menu-radio-item-indicator\"\r\n      >\r\n        <MenuPrimitive.RadioItemIndicator>\r\n          <CheckIcon />\r\n        </MenuPrimitive.RadioItemIndicator>\r\n      </span>\r\n      {children}\r\n    </MenuPrimitive.RadioItem>\r\n  );\r\n}\r\n\r\nfunction DropdownMenuSeparator({\r\n  className,\r\n  ...props\r\n}: MenuPrimitive.Separator.Props) {\r\n  return (\r\n    <MenuPrimitive.Separator\r\n      data-slot=\"dropdown-menu-separator\"\r\n      className={cn('-mx-1 my-1 h-px bg-border', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction DropdownMenuShortcut({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"dropdown-menu-shortcut\"\r\n      className={cn(\r\n        'ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  DropdownMenu,\r\n  DropdownMenuPortal,\r\n  DropdownMenuTrigger,\r\n  DropdownMenuContent,\r\n  DropdownMenuGroup,\r\n  DropdownMenuLabel,\r\n  DropdownMenuItem,\r\n  DropdownMenuCheckboxItem,\r\n  DropdownMenuRadioGroup,\r\n  DropdownMenuRadioItem,\r\n  DropdownMenuSeparator,\r\n  DropdownMenuShortcut,\r\n  DropdownMenuSub,\r\n  DropdownMenuSubTrigger,\r\n  DropdownMenuSubContent,\r\n};\r\n","handborne/source/components/ui/empty.tsx":"import { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Empty({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty\"\r\n      className={cn(\r\n        'gap-4 rounded-xl border-dashed p-6 flex w-full min-w-0 flex-1 flex-col items-center justify-center text-center text-balance',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty-header\"\r\n      className={cn('gap-2 flex max-w-sm flex-col items-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst emptyMediaVariants = cva(\r\n  'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-transparent',\r\n        icon: \"bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-4\",\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction EmptyMedia({\r\n  className,\r\n  variant = 'default',\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty-icon\"\r\n      data-variant={variant}\r\n      className={cn(emptyMediaVariants({ variant, className }))}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty-title\"\r\n      className={cn(\r\n        'text-sm font-medium tracking-tight cn-font-heading',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty-description\"\r\n      className={cn(\r\n        'text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"empty-content\"\r\n      className={cn(\r\n        'gap-2.5 text-sm flex w-full max-w-sm min-w-0 flex-col items-center text-balance',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Empty,\r\n  EmptyHeader,\r\n  EmptyTitle,\r\n  EmptyDescription,\r\n  EmptyContent,\r\n  EmptyMedia,\r\n};\r\n","handborne/source/components/ui/field.tsx":"'use client';\r\n\r\nimport { useMemo } from 'react';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Separator } from '@/components/ui/separator';\r\n\r\nfunction FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {\r\n  return (\r\n    <fieldset\r\n      data-slot=\"field-set\"\r\n      className={cn(\r\n        'gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldLegend({\r\n  className,\r\n  variant = 'legend',\r\n  ...props\r\n}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {\r\n  return (\r\n    <legend\r\n      data-slot=\"field-legend\"\r\n      data-variant={variant}\r\n      className={cn(\r\n        'mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"field-group\"\r\n      className={cn(\r\n        'gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4 group/field-group @container/field-group flex w-full flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst fieldVariants = cva(\r\n  'data-[invalid=true]:text-destructive gap-2 group/field flex w-full',\r\n  {\r\n    variants: {\r\n      orientation: {\r\n        vertical: 'flex-col *:w-full [&>.sr-only]:w-auto',\r\n        horizontal:\r\n          'flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',\r\n        responsive:\r\n          'flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      orientation: 'vertical',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Field({\r\n  className,\r\n  orientation = 'vertical',\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {\r\n  return (\r\n    <div\r\n      role=\"group\"\r\n      data-slot=\"field\"\r\n      data-orientation={orientation}\r\n      className={cn(fieldVariants({ orientation }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"field-content\"\r\n      className={cn(\r\n        'gap-0.5 group/field-content flex flex-1 flex-col leading-snug',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldLabel({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof Label>) {\r\n  return (\r\n    <Label\r\n      data-slot=\"field-label\"\r\n      className={cn(\r\n        'has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 *:data-[slot=field]:p-2.5 group/field-label peer/field-label flex w-fit',\r\n        'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"field-label\"\r\n      className={cn(\r\n        'gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {\r\n  return (\r\n    <p\r\n      data-slot=\"field-description\"\r\n      className={cn(\r\n        'text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-data-horizontal/field:text-balance',\r\n        'last:mt-0 nth-last-2:-mt-1',\r\n        '[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction FieldSeparator({\r\n  children,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  children?: React.ReactNode;\r\n}) {\r\n  return (\r\n    <div\r\n      data-slot=\"field-separator\"\r\n      data-content={!!children}\r\n      className={cn(\r\n        '-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <Separator className=\"absolute inset-0 top-1/2\" />\r\n      {children && (\r\n        <span\r\n          className=\"text-muted-foreground px-2 relative mx-auto block w-fit bg-background\"\r\n          data-slot=\"field-separator-content\"\r\n        >\r\n          {children}\r\n        </span>\r\n      )}\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction FieldError({\r\n  className,\r\n  children,\r\n  errors,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  errors?: Array<{ message?: string } | undefined>;\r\n}) {\r\n  const content = useMemo(() => {\r\n    if (children) {\r\n      return children;\r\n    }\r\n\r\n    if (!errors?.length) {\r\n      return null;\r\n    }\r\n\r\n    const uniqueErrors = [\r\n      ...new Map(errors.map((error) => [error?.message, error])).values(),\r\n    ];\r\n\r\n    if (uniqueErrors?.length == 1) {\r\n      return uniqueErrors[0]?.message;\r\n    }\r\n\r\n    return (\r\n      <ul className=\"ml-4 flex list-disc flex-col gap-1\">\r\n        {uniqueErrors.map(\r\n          (error, index) =>\r\n            error?.message && <li key={index}>{error.message}</li>,\r\n        )}\r\n      </ul>\r\n    );\r\n  }, [children, errors]);\r\n\r\n  if (!content) {\r\n    return null;\r\n  }\r\n\r\n  return (\r\n    <div\r\n      role=\"alert\"\r\n      data-slot=\"field-error\"\r\n      className={cn('text-destructive text-sm font-normal', className)}\r\n      {...props}\r\n    >\r\n      {content}\r\n    </div>\r\n  );\r\n}\r\n\r\nexport {\r\n  Field,\r\n  FieldLabel,\r\n  FieldDescription,\r\n  FieldError,\r\n  FieldGroup,\r\n  FieldLegend,\r\n  FieldSeparator,\r\n  FieldSet,\r\n  FieldContent,\r\n  FieldTitle,\r\n};\r\n","handborne/source/components/ui/hover-card.tsx":"'use client';\r\n\r\nimport { PreviewCard as PreviewCardPrimitive } from '@base-ui/react/preview-card';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {\r\n  return <PreviewCardPrimitive.Root data-slot=\"hover-card\" {...props} />;\r\n}\r\n\r\nfunction HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {\r\n  return (\r\n    <PreviewCardPrimitive.Trigger data-slot=\"hover-card-trigger\" {...props} />\r\n  );\r\n}\r\n\r\nfunction HoverCardContent({\r\n  className,\r\n  side = 'bottom',\r\n  sideOffset = 4,\r\n  align = 'center',\r\n  alignOffset = 4,\r\n  ...props\r\n}: PreviewCardPrimitive.Popup.Props &\r\n  Pick<\r\n    PreviewCardPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset'\r\n  >) {\r\n  return (\r\n    <PreviewCardPrimitive.Portal data-slot=\"hover-card-portal\">\r\n      <PreviewCardPrimitive.Positioner\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        className=\"isolate z-50\"\r\n      >\r\n        <PreviewCardPrimitive.Popup\r\n          data-slot=\"hover-card-content\"\r\n          className={cn(\r\n            'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground w-64 rounded-lg p-2.5 text-sm shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 origin-(--transform-origin) outline-hidden',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n      </PreviewCardPrimitive.Positioner>\r\n    </PreviewCardPrimitive.Portal>\r\n  );\r\n}\r\n\r\nexport { HoverCard, HoverCardTrigger, HoverCardContent };\r\n","handborne/source/components/ui/input-group.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Textarea } from '@/components/ui/textarea';\r\n\r\nfunction InputGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"input-group\"\r\n      role=\"group\"\r\n      className={cn(\r\n        'border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-lg border transition-colors in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst inputGroupAddonVariants = cva(\r\n  \"text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none\",\r\n  {\r\n    variants: {\r\n      align: {\r\n        'inline-start':\r\n          'pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem] order-first',\r\n        'inline-end':\r\n          'pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem] order-last',\r\n        'block-start':\r\n          'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',\r\n        'block-end':\r\n          'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      align: 'inline-start',\r\n    },\r\n  },\r\n);\r\n\r\nfunction InputGroupAddon({\r\n  className,\r\n  align = 'inline-start',\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {\r\n  return (\r\n    <div\r\n      role=\"group\"\r\n      data-slot=\"input-group-addon\"\r\n      data-align={align}\r\n      className={cn(inputGroupAddonVariants({ align }), className)}\r\n      onClick={(e) => {\r\n        if ((e.target as HTMLElement).closest('button')) {\r\n          return;\r\n        }\r\n        e.currentTarget.parentElement?.querySelector('input')?.focus();\r\n      }}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst inputGroupButtonVariants = cva(\r\n  'gap-2 text-sm flex items-center shadow-none',\r\n  {\r\n    variants: {\r\n      size: {\r\n        xs: \"h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5\",\r\n        sm: '',\r\n        'icon-xs':\r\n          'size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0',\r\n        'icon-sm': 'size-8 p-0 has-[>svg]:p-0',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      size: 'xs',\r\n    },\r\n  },\r\n);\r\n\r\nfunction InputGroupButton({\r\n  className,\r\n  type = 'button',\r\n  variant = 'ghost',\r\n  size = 'xs',\r\n  ...props\r\n}: Omit<React.ComponentProps<typeof Button>, 'size' | 'type'> &\r\n  VariantProps<typeof inputGroupButtonVariants> & {\r\n    type?: 'button' | 'submit' | 'reset';\r\n  }) {\r\n  return (\r\n    <Button\r\n      type={type}\r\n      data-size={size}\r\n      variant={variant}\r\n      className={cn(inputGroupButtonVariants({ size }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      className={cn(\r\n        \"text-muted-foreground gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction InputGroupInput({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'input'>) {\r\n  return (\r\n    <Input\r\n      data-slot=\"input-group-control\"\r\n      className={cn(\r\n        'rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction InputGroupTextarea({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'textarea'>) {\r\n  return (\r\n    <Textarea\r\n      data-slot=\"input-group-control\"\r\n      className={cn(\r\n        'rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1 resize-none',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  InputGroup,\r\n  InputGroupAddon,\r\n  InputGroupButton,\r\n  InputGroupText,\r\n  InputGroupInput,\r\n  InputGroupTextarea,\r\n};\r\n","handborne/source/components/ui/input-otp.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { OTPInput, OTPInputContext } from 'input-otp';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { MinusIcon } from 'lucide-react';\r\n\r\nfunction InputOTP({\r\n  className,\r\n  containerClassName,\r\n  ...props\r\n}: React.ComponentProps<typeof OTPInput> & {\r\n  containerClassName?: string;\r\n}) {\r\n  return (\r\n    <OTPInput\r\n      data-slot=\"input-otp\"\r\n      containerClassName={cn(\r\n        'cn-input-otp flex items-center has-disabled:opacity-50',\r\n        containerClassName,\r\n      )}\r\n      spellCheck={false}\r\n      className={cn('disabled:cursor-not-allowed', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"input-otp-group\"\r\n      className={cn(\r\n        'has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-lg has-aria-invalid:ring-3 flex items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction InputOTPSlot({\r\n  index,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  index: number;\r\n}) {\r\n  const inputOTPContext = React.useContext(OTPInputContext);\r\n  const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};\r\n\r\n  return (\r\n    <div\r\n      data-slot=\"input-otp-slot\"\r\n      data-active={isActive}\r\n      className={cn(\r\n        'dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-8 border-y border-r text-sm transition-all outline-none first:rounded-l-lg first:border-l last:rounded-r-lg data-[active=true]:ring-3 relative flex items-center justify-center data-[active=true]:z-10',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {char}\r\n      {hasFakeCaret && (\r\n        <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\r\n          <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000\" />\r\n        </div>\r\n      )}\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"input-otp-separator\"\r\n      className=\"[&_svg:not([class*='size-'])]:size-4 flex items-center\"\r\n      role=\"separator\"\r\n      {...props}\r\n    >\r\n      <MinusIcon />\r\n    </div>\r\n  );\r\n}\r\n\r\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };\r\n","handborne/source/components/ui/input.tsx":"import * as React from 'react';\r\nimport { Input as InputPrimitive } from '@base-ui/react/input';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Input({ className, type, ...props }: React.ComponentProps<'input'>) {\r\n  return (\r\n    <InputPrimitive\r\n      type={type}\r\n      data-slot=\"input\"\r\n      className={cn(\r\n        'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Input };\r\n","handborne/source/components/ui/item.tsx":"import * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Separator } from '@/components/ui/separator';\r\n\r\nfunction ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      role=\"list\"\r\n      data-slot=\"item-group\"\r\n      className={cn(\r\n        'gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2 group/item-group flex w-full flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemSeparator({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof Separator>) {\r\n  return (\r\n    <Separator\r\n      data-slot=\"item-separator\"\r\n      orientation=\"horizontal\"\r\n      className={cn('my-2', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst itemVariants = cva(\r\n  '[a]:hover:bg-muted rounded-lg border text-sm group/item flex w-full flex-wrap items-center transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'border-transparent',\r\n        outline: 'border-border',\r\n        muted: 'bg-muted/50 border-transparent',\r\n      },\r\n      size: {\r\n        default: 'gap-2.5 px-3 py-2.5',\r\n        sm: 'gap-2.5 px-3 py-2.5',\r\n        xs: 'gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n      size: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Item({\r\n  className,\r\n  variant = 'default',\r\n  size = 'default',\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'div'> & VariantProps<typeof itemVariants>) {\r\n  return useRender({\r\n    defaultTagName: 'div',\r\n    props: mergeProps<'div'>(\r\n      {\r\n        className: cn(itemVariants({ variant, size, className })),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'item',\r\n      variant,\r\n      size,\r\n    },\r\n  });\r\n}\r\n\r\nconst itemMediaVariants = cva(\r\n  'gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-transparent',\r\n        icon: \"[&_svg:not([class*='size-'])]:size-4\",\r\n        image:\r\n          'size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction ItemMedia({\r\n  className,\r\n  variant = 'default',\r\n  ...props\r\n}: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-media\"\r\n      data-variant={variant}\r\n      className={cn(itemMediaVariants({ variant, className }))}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-content\"\r\n      className={cn(\r\n        'gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-title\"\r\n      className={cn(\r\n        'gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {\r\n  return (\r\n    <p\r\n      data-slot=\"item-description\"\r\n      className={cn(\r\n        'text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemActions({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-actions\"\r\n      className={cn('gap-2 flex items-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-header\"\r\n      className={cn(\r\n        'gap-2 flex basis-full items-center justify-between',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"item-footer\"\r\n      className={cn(\r\n        'gap-2 flex basis-full items-center justify-between',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Item,\r\n  ItemMedia,\r\n  ItemContent,\r\n  ItemActions,\r\n  ItemGroup,\r\n  ItemSeparator,\r\n  ItemTitle,\r\n  ItemDescription,\r\n  ItemHeader,\r\n  ItemFooter,\r\n};\r\n","handborne/source/components/ui/kbd.tsx":"import { cn } from '@/lib/utils';\r\n\r\nfunction Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {\r\n  return (\r\n    <kbd\r\n      data-slot=\"kbd\"\r\n      className={cn(\r\n        \"bg-muted text-muted-foreground in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 h-5 w-fit min-w-5 gap-1 rounded-sm px-1 font-sans text-xs font-medium [&_svg:not([class*='size-'])]:size-3 pointer-events-none inline-flex items-center justify-center select-none\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <kbd\r\n      data-slot=\"kbd-group\"\r\n      className={cn('gap-1 inline-flex items-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Kbd, KbdGroup };\r\n","handborne/source/components/ui/label.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Label({ className, ...props }: React.ComponentProps<'label'>) {\r\n  return (\r\n    <label\r\n      data-slot=\"label\"\r\n      className={cn(\r\n        'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Label };\r\n","handborne/source/components/ui/marker.tsx":"import * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst markerVariants = cva(\r\n  \"gap-2 text-sm text-muted-foreground [a]:hover:text-foreground [a]:underline-offset-3 [a]:underline [&_svg:not([class*='size-'])]:size-4 min-h-4 text-left group/marker relative flex w-full items-center\",\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: '',\r\n        separator:\r\n          'before:h-px before:min-w-0 before:flex-1 before:bg-border after:h-px after:min-w-0 after:flex-1 after:bg-border before:mr-1 after:ml-1',\r\n        border: 'border-b border-border pb-2',\r\n      },\r\n    },\r\n  },\r\n);\r\n\r\nfunction Marker({\r\n  className,\r\n  variant = 'default',\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'div'> & VariantProps<typeof markerVariants>) {\r\n  return useRender({\r\n    defaultTagName: 'div',\r\n    props: mergeProps<'div'>(\r\n      {\r\n        className: cn(markerVariants({ variant, className })),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'marker',\r\n      variant,\r\n    },\r\n  });\r\n}\r\n\r\nfunction MarkerIcon({ className, ...props }: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"marker-icon\"\r\n      aria-hidden=\"true\"\r\n      className={cn(\r\n        \"size-4 [&_svg:not([class*='size-'])]:size-4 shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MarkerContent({ className, ...props }: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      data-slot=\"marker-content\"\r\n      className={cn(\r\n        'group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center *:[a]:hover:text-foreground *:[a]:underline *:[a]:underline-offset-3 min-w-0 wrap-break-word',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Marker, MarkerIcon, MarkerContent, markerVariants };\r\n","handborne/source/components/ui/menubar.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Menu as MenuPrimitive } from '@base-ui/react/menu';\r\nimport { Menubar as MenubarPrimitive } from '@base-ui/react/menubar';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport {\r\n  DropdownMenu,\r\n  DropdownMenuContent,\r\n  DropdownMenuGroup,\r\n  DropdownMenuItem,\r\n  DropdownMenuLabel,\r\n  DropdownMenuPortal,\r\n  DropdownMenuRadioGroup,\r\n  DropdownMenuSeparator,\r\n  DropdownMenuShortcut,\r\n  DropdownMenuSub,\r\n  DropdownMenuSubContent,\r\n  DropdownMenuSubTrigger,\r\n  DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu';\r\nimport { CheckIcon } from 'lucide-react';\r\n\r\nfunction Menubar({ className, ...props }: MenubarPrimitive.Props) {\r\n  return (\r\n    <MenubarPrimitive\r\n      data-slot=\"menubar\"\r\n      className={cn(\r\n        'h-8 gap-0.5 rounded-lg border p-[3px] flex items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {\r\n  return <DropdownMenu data-slot=\"menubar-menu\" {...props} />;\r\n}\r\n\r\nfunction MenubarGroup({\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuGroup>) {\r\n  return <DropdownMenuGroup data-slot=\"menubar-group\" {...props} />;\r\n}\r\n\r\nfunction MenubarPortal({\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuPortal>) {\r\n  return <DropdownMenuPortal data-slot=\"menubar-portal\" {...props} />;\r\n}\r\n\r\nfunction MenubarTrigger({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuTrigger>) {\r\n  return (\r\n    <DropdownMenuTrigger\r\n      data-slot=\"menubar-trigger\"\r\n      className={cn(\r\n        'hover:bg-muted aria-expanded:bg-muted rounded-sm px-1.5 py-[2px] text-sm font-medium flex items-center outline-hidden select-none',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarContent({\r\n  className,\r\n  align = 'start',\r\n  alignOffset = -4,\r\n  sideOffset = 8,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuContent>) {\r\n  return (\r\n    <DropdownMenuContent\r\n      data-slot=\"menubar-content\"\r\n      align={align}\r\n      alignOffset={alignOffset}\r\n      sideOffset={sideOffset}\r\n      className={cn(\r\n        'bg-popover text-popover-foreground data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg p-1 shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 cn-menu-target cn-menu-translucent',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarItem({\r\n  className,\r\n  inset,\r\n  variant = 'default',\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuItem>) {\r\n  return (\r\n    <DropdownMenuItem\r\n      data-slot=\"menubar-item\"\r\n      data-inset={inset}\r\n      data-variant={variant}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive! not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-disabled:opacity-50 data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/menubar-item\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarCheckboxItem({\r\n  className,\r\n  children,\r\n  checked,\r\n  inset,\r\n  ...props\r\n}: MenuPrimitive.CheckboxItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.CheckboxItem\r\n      data-slot=\"menubar-checkbox-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        'focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm data-inset:pl-7 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',\r\n        className,\r\n      )}\r\n      checked={checked}\r\n      {...props}\r\n    >\r\n      <span className=\"left-1.5 size-4 [&_svg:not([class*='size-'])]:size-4 pointer-events-none absolute flex items-center justify-center\">\r\n        <MenuPrimitive.CheckboxItemIndicator>\r\n          <CheckIcon />\r\n        </MenuPrimitive.CheckboxItemIndicator>\r\n      </span>\r\n      {children}\r\n    </MenuPrimitive.CheckboxItem>\r\n  );\r\n}\r\n\r\nfunction MenubarRadioGroup({\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuRadioGroup>) {\r\n  return <DropdownMenuRadioGroup data-slot=\"menubar-radio-group\" {...props} />;\r\n}\r\n\r\nfunction MenubarRadioItem({\r\n  className,\r\n  children,\r\n  inset,\r\n  ...props\r\n}: MenuPrimitive.RadioItem.Props & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <MenuPrimitive.RadioItem\r\n      data-slot=\"menubar-radio-item\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm data-disabled:opacity-50 data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <span className=\"left-1.5 size-4 [&_svg:not([class*='size-'])]:size-4 pointer-events-none absolute flex items-center justify-center\">\r\n        <MenuPrimitive.RadioItemIndicator>\r\n          <CheckIcon />\r\n        </MenuPrimitive.RadioItemIndicator>\r\n      </span>\r\n      {children}\r\n    </MenuPrimitive.RadioItem>\r\n  );\r\n}\r\n\r\nfunction MenubarLabel({\r\n  className,\r\n  inset,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuLabel> & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <DropdownMenuLabel\r\n      data-slot=\"menubar-label\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        'px-1.5 py-1 text-sm font-medium data-inset:pl-7',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarSeparator({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuSeparator>) {\r\n  return (\r\n    <DropdownMenuSeparator\r\n      data-slot=\"menubar-separator\"\r\n      className={cn('bg-border -mx-1 my-1 h-px', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarShortcut({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuShortcut>) {\r\n  return (\r\n    <DropdownMenuShortcut\r\n      data-slot=\"menubar-shortcut\"\r\n      className={cn(\r\n        'text-muted-foreground group-focus/menubar-item:text-accent-foreground text-xs tracking-widest ml-auto',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarSub({\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuSub>) {\r\n  return <DropdownMenuSub data-slot=\"menubar-sub\" {...props} />;\r\n}\r\n\r\nfunction MenubarSubTrigger({\r\n  className,\r\n  inset,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuSubTrigger> & {\r\n  inset?: boolean;\r\n}) {\r\n  return (\r\n    <DropdownMenuSubTrigger\r\n      data-slot=\"menubar-sub-trigger\"\r\n      data-inset={inset}\r\n      className={cn(\r\n        \"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MenubarSubContent({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof DropdownMenuSubContent>) {\r\n  return (\r\n    <DropdownMenuSubContent\r\n      data-slot=\"menubar-sub-content\"\r\n      className={cn(\r\n        'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-32 rounded-lg p-1 shadow-lg ring-1 duration-100 cn-menu-target cn-menu-translucent',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Menubar,\r\n  MenubarPortal,\r\n  MenubarMenu,\r\n  MenubarTrigger,\r\n  MenubarContent,\r\n  MenubarGroup,\r\n  MenubarSeparator,\r\n  MenubarLabel,\r\n  MenubarItem,\r\n  MenubarShortcut,\r\n  MenubarCheckboxItem,\r\n  MenubarRadioGroup,\r\n  MenubarRadioItem,\r\n  MenubarSub,\r\n  MenubarSubTrigger,\r\n  MenubarSubContent,\r\n};\r\n","handborne/source/components/ui/message-scroller.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport {\r\n  MessageScroller as MessageScrollerPrimitive,\r\n  useMessageScroller,\r\n  useMessageScrollerScrollable,\r\n  useMessageScrollerVisibility,\r\n} from '@shadcn/react/message-scroller';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { ArrowDownIcon } from 'lucide-react';\r\n\r\nfunction MessageScrollerProvider(\r\n  props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>,\r\n) {\r\n  return <MessageScrollerPrimitive.Provider {...props} />;\r\n}\r\n\r\nfunction MessageScroller({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {\r\n  return (\r\n    <MessageScrollerPrimitive.Root\r\n      data-slot=\"message-scroller\"\r\n      className={cn(\r\n        'group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageScrollerViewport({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {\r\n  return (\r\n    <MessageScrollerPrimitive.Viewport\r\n      data-slot=\"message-scroller-viewport\"\r\n      className={cn(\r\n        'size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageScrollerContent({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {\r\n  return (\r\n    <MessageScrollerPrimitive.Content\r\n      data-slot=\"message-scroller-content\"\r\n      className={cn('gap-6 flex h-max min-h-full flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageScrollerItem({\r\n  className,\r\n  scrollAnchor = false,\r\n  ...props\r\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {\r\n  return (\r\n    <MessageScrollerPrimitive.Item\r\n      data-slot=\"message-scroller-item\"\r\n      scrollAnchor={scrollAnchor}\r\n      className={cn(\r\n        'min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageScrollerButton({\r\n  direction = 'end',\r\n  className,\r\n  children,\r\n  render,\r\n  variant = 'secondary',\r\n  size = 'icon-sm',\r\n  ...props\r\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &\r\n  Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {\r\n  return (\r\n    <MessageScrollerPrimitive.Button\r\n      data-slot=\"message-scroller-button\"\r\n      data-direction={direction}\r\n      data-variant={variant}\r\n      data-size={size}\r\n      direction={direction}\r\n      className={cn(\r\n        'absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180',\r\n        className,\r\n      )}\r\n      render={render ?? <Button variant={variant} size={size} />}\r\n      {...props}\r\n    >\r\n      {children ?? (\r\n        <>\r\n          <ArrowDownIcon />\r\n          <span className=\"sr-only\">\r\n            {direction === 'end' ? 'Scroll to end' : 'Scroll to start'}\r\n          </span>\r\n        </>\r\n      )}\r\n    </MessageScrollerPrimitive.Button>\r\n  );\r\n}\r\n\r\nexport {\r\n  MessageScrollerProvider,\r\n  MessageScroller,\r\n  MessageScrollerViewport,\r\n  MessageScrollerContent,\r\n  MessageScrollerItem,\r\n  MessageScrollerButton,\r\n  useMessageScroller,\r\n  useMessageScrollerScrollable,\r\n  useMessageScrollerVisibility,\r\n};\r\n","handborne/source/components/ui/message.tsx":"import * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction MessageGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"message-group\"\r\n      className={cn('gap-2 flex min-w-0 flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction Message({\r\n  className,\r\n  align = 'start',\r\n  ...props\r\n}: React.ComponentProps<'div'> & { align?: 'start' | 'end' }) {\r\n  return (\r\n    <div\r\n      data-slot=\"message\"\r\n      data-align={align}\r\n      className={cn(\r\n        'text-sm gap-2 group/message relative flex w-full min-w-0 data-[align=end]:flex-row-reverse',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageAvatar({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"message-avatar\"\r\n      className={cn(\r\n        'min-w-8 group-has-data-[slot=message-footer]/message:-translate-y-8 flex w-fit shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"message-content\"\r\n      className={cn(\r\n        'gap-2.5 group-data-[align=end]/message:*:data-slot:self-end flex w-full min-w-0 flex-col wrap-break-word',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"message-header\"\r\n      className={cn(\r\n        'text-xs font-medium text-muted-foreground px-3 group-has-data-[variant=ghost]/message:px-0 flex max-w-full min-w-0 items-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction MessageFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"message-footer\"\r\n      className={cn(\r\n        'text-xs font-medium text-muted-foreground px-3 group-has-data-[variant=ghost]/message:px-0 flex max-w-full min-w-0 items-center group-data-[align=end]/message:justify-end',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  MessageGroup,\r\n  Message,\r\n  MessageAvatar,\r\n  MessageContent,\r\n  MessageFooter,\r\n  MessageHeader,\r\n};\r\n","handborne/source/components/ui/native-select.tsx":"import * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronDownIcon } from 'lucide-react';\r\n\r\ntype NativeSelectProps = Omit<React.ComponentProps<'select'>, 'size'> & {\r\n  size?: 'sm' | 'default';\r\n};\r\n\r\nfunction NativeSelect({\r\n  className,\r\n  size = 'default',\r\n  ...props\r\n}: NativeSelectProps) {\r\n  return (\r\n    <div\r\n      className={cn(\r\n        'group/native-select relative w-fit has-[select:disabled]:opacity-50',\r\n        className,\r\n      )}\r\n      data-slot=\"native-select-wrapper\"\r\n      data-size={size}\r\n    >\r\n      <select\r\n        data-slot=\"native-select\"\r\n        data-size={size}\r\n        className=\"border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-lg border bg-transparent py-1 pr-8 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-[size=sm]:py-0.5 outline-none disabled:pointer-events-none disabled:cursor-not-allowed\"\r\n        {...props}\r\n      />\r\n      <ChevronDownIcon\r\n        className=\"text-muted-foreground top-1/2 right-2.5 size-4 -translate-y-1/2 pointer-events-none absolute select-none\"\r\n        aria-hidden=\"true\"\r\n        data-slot=\"native-select-icon\"\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction NativeSelectOption({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'option'>) {\r\n  return (\r\n    <option\r\n      data-slot=\"native-select-option\"\r\n      className={cn('bg-[Canvas] text-[CanvasText]', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction NativeSelectOptGroup({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'optgroup'>) {\r\n  return (\r\n    <optgroup\r\n      data-slot=\"native-select-optgroup\"\r\n      className={cn('bg-[Canvas] text-[CanvasText]', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { NativeSelect, NativeSelectOptGroup, NativeSelectOption };\r\n","handborne/source/components/ui/navigation-menu.tsx":"import { NavigationMenu as NavigationMenuPrimitive } from '@base-ui/react/navigation-menu';\r\nimport { cva } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronDownIcon } from 'lucide-react';\r\n\r\nfunction NavigationMenu({\r\n  align = 'start',\r\n  className,\r\n  children,\r\n  ...props\r\n}: NavigationMenuPrimitive.Root.Props &\r\n  Pick<NavigationMenuPrimitive.Positioner.Props, 'align'>) {\r\n  return (\r\n    <NavigationMenuPrimitive.Root\r\n      data-slot=\"navigation-menu\"\r\n      className={cn(\r\n        'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <NavigationMenuPositioner align={align} />\r\n    </NavigationMenuPrimitive.Root>\r\n  );\r\n}\r\n\r\nfunction NavigationMenuList({\r\n  className,\r\n  ...props\r\n}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {\r\n  return (\r\n    <NavigationMenuPrimitive.List\r\n      data-slot=\"navigation-menu-list\"\r\n      className={cn(\r\n        'gap-0 group flex flex-1 list-none items-center justify-center',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction NavigationMenuItem({\r\n  className,\r\n  ...props\r\n}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {\r\n  return (\r\n    <NavigationMenuPrimitive.Item\r\n      data-slot=\"navigation-menu-item\"\r\n      className={cn('relative', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst navigationMenuTriggerStyle = cva(\r\n  'hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all focus-visible:ring-3 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center outline-none disabled:pointer-events-none',\r\n);\r\n\r\nfunction NavigationMenuTrigger({\r\n  className,\r\n  children,\r\n  ...props\r\n}: NavigationMenuPrimitive.Trigger.Props) {\r\n  return (\r\n    <NavigationMenuPrimitive.Trigger\r\n      data-slot=\"navigation-menu-trigger\"\r\n      className={cn(navigationMenuTriggerStyle(), 'group', className)}\r\n      {...props}\r\n    >\r\n      {children}{' '}\r\n      <ChevronDownIcon\r\n        className=\"relative top-px ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180\"\r\n        aria-hidden=\"true\"\r\n      />\r\n    </NavigationMenuPrimitive.Trigger>\r\n  );\r\n}\r\n\r\nfunction NavigationMenuContent({\r\n  className,\r\n  ...props\r\n}: NavigationMenuPrimitive.Content.Props) {\r\n  return (\r\n    <NavigationMenuPrimitive.Content\r\n      data-slot=\"navigation-menu-content\"\r\n      className={cn(\r\n        'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:ring-foreground/10 p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto transition-[opacity,transform,translate] duration-[0.35s] data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction NavigationMenuPositioner({\r\n  className,\r\n  side = 'bottom',\r\n  sideOffset = 8,\r\n  align = 'start',\r\n  alignOffset = 0,\r\n  ...props\r\n}: NavigationMenuPrimitive.Positioner.Props) {\r\n  return (\r\n    <NavigationMenuPrimitive.Portal>\r\n      <NavigationMenuPrimitive.Positioner\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        className={cn(\r\n          'ease-[cubic-bezier(0.22,1,0.36,1)] data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0 isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] data-instant:transition-none',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        <NavigationMenuPrimitive.Popup className=\"bg-popover text-popover-foreground ring-foreground/10 rounded-lg shadow ring-1 outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0 data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)]\">\r\n          <NavigationMenuPrimitive.Viewport className=\"relative size-full overflow-hidden\" />\r\n        </NavigationMenuPrimitive.Popup>\r\n      </NavigationMenuPrimitive.Positioner>\r\n    </NavigationMenuPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction NavigationMenuLink({\r\n  className,\r\n  ...props\r\n}: NavigationMenuPrimitive.Link.Props) {\r\n  return (\r\n    <NavigationMenuPrimitive.Link\r\n      data-slot=\"navigation-menu-link\"\r\n      className={cn(\r\n        \"data-active:focus:bg-muted data-active:hover:bg-muted data-active:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none focus-visible:ring-3 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction NavigationMenuIndicator({\r\n  className,\r\n  ...props\r\n}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {\r\n  return (\r\n    <NavigationMenuPrimitive.Icon\r\n      data-slot=\"navigation-menu-indicator\"\r\n      className={cn(\r\n        'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <div className=\"bg-border rounded-tl-sm shadow-md relative top-[60%] h-2 w-2 rotate-45\" />\r\n    </NavigationMenuPrimitive.Icon>\r\n  );\r\n}\r\n\r\nexport {\r\n  NavigationMenu,\r\n  NavigationMenuContent,\r\n  NavigationMenuIndicator,\r\n  NavigationMenuItem,\r\n  NavigationMenuLink,\r\n  NavigationMenuList,\r\n  NavigationMenuTrigger,\r\n  navigationMenuTriggerStyle,\r\n  NavigationMenuPositioner,\r\n};\r\n","handborne/source/components/ui/pagination.tsx":"import * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport {\r\n  ChevronLeftIcon,\r\n  ChevronRightIcon,\r\n  MoreHorizontalIcon,\r\n} from 'lucide-react';\r\n\r\nfunction Pagination({ className, ...props }: React.ComponentProps<'nav'>) {\r\n  return (\r\n    <nav\r\n      role=\"navigation\"\r\n      aria-label=\"pagination\"\r\n      data-slot=\"pagination\"\r\n      className={cn('mx-auto flex w-full justify-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction PaginationContent({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'ul'>) {\r\n  return (\r\n    <ul\r\n      data-slot=\"pagination-content\"\r\n      className={cn('gap-0.5 flex items-center', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction PaginationItem({ ...props }: React.ComponentProps<'li'>) {\r\n  return <li data-slot=\"pagination-item\" {...props} />;\r\n}\r\n\r\ntype PaginationLinkProps = {\r\n  isActive?: boolean;\r\n} & Pick<React.ComponentProps<typeof Button>, 'size'> &\r\n  React.ComponentProps<'a'>;\r\n\r\nfunction PaginationLink({\r\n  className,\r\n  isActive,\r\n  size = 'icon',\r\n  ...props\r\n}: PaginationLinkProps) {\r\n  return (\r\n    <Button\r\n      variant={isActive ? 'outline' : 'ghost'}\r\n      size={size}\r\n      className={cn(className)}\r\n      nativeButton={false}\r\n      render={\r\n        <a\r\n          aria-current={isActive ? 'page' : undefined}\r\n          data-slot=\"pagination-link\"\r\n          data-active={isActive}\r\n          {...props}\r\n        />\r\n      }\r\n    />\r\n  );\r\n}\r\n\r\nfunction PaginationPrevious({\r\n  className,\r\n  text = 'Previous',\r\n  ...props\r\n}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {\r\n  return (\r\n    <PaginationLink\r\n      aria-label=\"Go to previous page\"\r\n      size=\"default\"\r\n      className={cn('pl-1.5!', className)}\r\n      {...props}\r\n    >\r\n      <ChevronLeftIcon data-icon=\"inline-start\" className=\"cn-rtl-flip\" />\r\n      <span className=\"hidden sm:block\">{text}</span>\r\n    </PaginationLink>\r\n  );\r\n}\r\n\r\nfunction PaginationNext({\r\n  className,\r\n  text = 'Next',\r\n  ...props\r\n}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {\r\n  return (\r\n    <PaginationLink\r\n      aria-label=\"Go to next page\"\r\n      size=\"default\"\r\n      className={cn('pr-1.5!', className)}\r\n      {...props}\r\n    >\r\n      <span className=\"hidden sm:block\">{text}</span>\r\n      <ChevronRightIcon data-icon=\"inline-end\" className=\"cn-rtl-flip\" />\r\n    </PaginationLink>\r\n  );\r\n}\r\n\r\nfunction PaginationEllipsis({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'span'>) {\r\n  return (\r\n    <span\r\n      aria-hidden\r\n      data-slot=\"pagination-ellipsis\"\r\n      className={cn(\r\n        \"size-8 [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <MoreHorizontalIcon />\r\n      <span className=\"sr-only\">More pages</span>\r\n    </span>\r\n  );\r\n}\r\n\r\nexport {\r\n  Pagination,\r\n  PaginationContent,\r\n  PaginationEllipsis,\r\n  PaginationItem,\r\n  PaginationLink,\r\n  PaginationNext,\r\n  PaginationPrevious,\r\n};\r\n","handborne/source/components/ui/popover.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Popover as PopoverPrimitive } from '@base-ui/react/popover';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Popover({ ...props }: PopoverPrimitive.Root.Props) {\r\n  return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\r\n}\r\n\r\nfunction PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {\r\n  return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\r\n}\r\n\r\nfunction PopoverContent({\r\n  className,\r\n  align = 'center',\r\n  alignOffset = 0,\r\n  side = 'bottom',\r\n  sideOffset = 4,\r\n  ...props\r\n}: PopoverPrimitive.Popup.Props &\r\n  Pick<\r\n    PopoverPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset'\r\n  >) {\r\n  return (\r\n    <PopoverPrimitive.Portal>\r\n      <PopoverPrimitive.Positioner\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        className=\"isolate z-50\"\r\n      >\r\n        <PopoverPrimitive.Popup\r\n          data-slot=\"popover-content\"\r\n          className={cn(\r\n            'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 w-72 origin-(--transform-origin) outline-hidden',\r\n            className,\r\n          )}\r\n          {...props}\r\n        />\r\n      </PopoverPrimitive.Positioner>\r\n    </PopoverPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction PopoverHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"popover-header\"\r\n      className={cn('flex flex-col gap-0.5 text-sm', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {\r\n  return (\r\n    <PopoverPrimitive.Title\r\n      data-slot=\"popover-title\"\r\n      className={cn('font-medium', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction PopoverDescription({\r\n  className,\r\n  ...props\r\n}: PopoverPrimitive.Description.Props) {\r\n  return (\r\n    <PopoverPrimitive.Description\r\n      data-slot=\"popover-description\"\r\n      className={cn('text-muted-foreground', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Popover,\r\n  PopoverContent,\r\n  PopoverDescription,\r\n  PopoverHeader,\r\n  PopoverTitle,\r\n  PopoverTrigger,\r\n};\r\n","handborne/source/components/ui/progress.tsx":"'use client';\r\n\r\nimport { Progress as ProgressPrimitive } from '@base-ui/react/progress';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Progress({\r\n  className,\r\n  children,\r\n  value,\r\n  ...props\r\n}: ProgressPrimitive.Root.Props) {\r\n  return (\r\n    <ProgressPrimitive.Root\r\n      value={value}\r\n      data-slot=\"progress\"\r\n      className={cn('flex flex-wrap gap-3', className)}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <ProgressTrack>\r\n        <ProgressIndicator />\r\n      </ProgressTrack>\r\n    </ProgressPrimitive.Root>\r\n  );\r\n}\r\n\r\nfunction ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {\r\n  return (\r\n    <ProgressPrimitive.Track\r\n      className={cn(\r\n        'bg-muted h-1 rounded-full relative flex w-full items-center overflow-x-hidden',\r\n        className,\r\n      )}\r\n      data-slot=\"progress-track\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ProgressIndicator({\r\n  className,\r\n  ...props\r\n}: ProgressPrimitive.Indicator.Props) {\r\n  return (\r\n    <ProgressPrimitive.Indicator\r\n      data-slot=\"progress-indicator\"\r\n      className={cn('bg-primary h-full transition-all', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {\r\n  return (\r\n    <ProgressPrimitive.Label\r\n      className={cn('text-sm font-medium', className)}\r\n      data-slot=\"progress-label\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {\r\n  return (\r\n    <ProgressPrimitive.Value\r\n      className={cn(\r\n        'text-muted-foreground ml-auto text-sm tabular-nums',\r\n        className,\r\n      )}\r\n      data-slot=\"progress-value\"\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Progress,\r\n  ProgressTrack,\r\n  ProgressIndicator,\r\n  ProgressLabel,\r\n  ProgressValue,\r\n};\r\n","handborne/source/components/ui/radio-group.tsx":"'use client';\r\n\r\nimport { Radio as RadioPrimitive } from '@base-ui/react/radio';\r\nimport { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {\r\n  return (\r\n    <RadioGroupPrimitive\r\n      data-slot=\"radio-group\"\r\n      className={cn('grid gap-2 w-full', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {\r\n  return (\r\n    <RadioPrimitive.Root\r\n      data-slot=\"radio-group-item\"\r\n      className={cn(\r\n        'border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 dark:aria-invalid:border-destructive/50 flex size-4 rounded-full focus-visible:ring-3 aria-invalid:ring-3 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input group-has-[:focus-visible]/field-label:data-checked:border-primary group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <RadioPrimitive.Indicator\r\n        data-slot=\"radio-group-indicator\"\r\n        className=\"flex size-4 items-center justify-center\"\r\n      >\r\n        <span className=\"bg-primary-foreground absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full\" />\r\n      </RadioPrimitive.Indicator>\r\n    </RadioPrimitive.Root>\r\n  );\r\n}\r\n\r\nexport { RadioGroup, RadioGroupItem };\r\n","handborne/source/components/ui/resizable.tsx":"'use client';\r\n\r\nimport * as ResizablePrimitive from 'react-resizable-panels';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction ResizablePanelGroup({\r\n  className,\r\n  ...props\r\n}: ResizablePrimitive.GroupProps) {\r\n  return (\r\n    <ResizablePrimitive.Group\r\n      data-slot=\"resizable-panel-group\"\r\n      className={cn(\r\n        'flex h-full w-full aria-[orientation=vertical]:flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {\r\n  return <ResizablePrimitive.Panel data-slot=\"resizable-panel\" {...props} />;\r\n}\r\n\r\nfunction ResizableHandle({\r\n  withHandle,\r\n  className,\r\n  ...props\r\n}: ResizablePrimitive.SeparatorProps & {\r\n  withHandle?: boolean;\r\n}) {\r\n  return (\r\n    <ResizablePrimitive.Separator\r\n      data-slot=\"resizable-handle\"\r\n      className={cn(\r\n        'relative flex w-px items-center justify-center bg-border ring-offset-background after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {withHandle && (\r\n        <div className=\"bg-border h-6 w-1 rounded-lg z-10 flex shrink-0\" />\r\n      )}\r\n    </ResizablePrimitive.Separator>\r\n  );\r\n}\r\n\r\nexport { ResizableHandle, ResizablePanel, ResizablePanelGroup };\r\n","handborne/source/components/ui/scroll-area.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { ScrollArea as ScrollAreaPrimitive } from '@base-ui/react/scroll-area';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction ScrollArea({\r\n  className,\r\n  children,\r\n  ...props\r\n}: ScrollAreaPrimitive.Root.Props) {\r\n  return (\r\n    <ScrollAreaPrimitive.Root\r\n      data-slot=\"scroll-area\"\r\n      className={cn('relative', className)}\r\n      {...props}\r\n    >\r\n      <ScrollAreaPrimitive.Viewport\r\n        data-slot=\"scroll-area-viewport\"\r\n        className=\"size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1\"\r\n      >\r\n        {children}\r\n      </ScrollAreaPrimitive.Viewport>\r\n      <ScrollBar />\r\n      <ScrollAreaPrimitive.Corner />\r\n    </ScrollAreaPrimitive.Root>\r\n  );\r\n}\r\n\r\nfunction ScrollBar({\r\n  className,\r\n  orientation = 'vertical',\r\n  ...props\r\n}: ScrollAreaPrimitive.Scrollbar.Props) {\r\n  return (\r\n    <ScrollAreaPrimitive.Scrollbar\r\n      data-slot=\"scroll-area-scrollbar\"\r\n      data-orientation={orientation}\r\n      orientation={orientation}\r\n      className={cn(\r\n        'data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <ScrollAreaPrimitive.Thumb\r\n        data-slot=\"scroll-area-thumb\"\r\n        className=\"rounded-full relative flex-1 bg-border\"\r\n      />\r\n    </ScrollAreaPrimitive.Scrollbar>\r\n  );\r\n}\r\n\r\nexport { ScrollArea, ScrollBar };\r\n","handborne/source/components/ui/select.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Select as SelectPrimitive } from '@base-ui/react/select';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { ChevronDownIcon, CheckIcon, ChevronUpIcon } from 'lucide-react';\r\n\r\nconst Select = SelectPrimitive.Root;\r\n\r\nfunction SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {\r\n  return (\r\n    <SelectPrimitive.Group\r\n      data-slot=\"select-group\"\r\n      className={cn('scroll-my-1 p-1', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {\r\n  return (\r\n    <SelectPrimitive.Value\r\n      data-slot=\"select-value\"\r\n      className={cn('flex flex-1 text-left', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SelectTrigger({\r\n  className,\r\n  size = 'default',\r\n  children,\r\n  ...props\r\n}: SelectPrimitive.Trigger.Props & {\r\n  size?: 'sm' | 'default';\r\n}) {\r\n  return (\r\n    <SelectPrimitive.Trigger\r\n      data-slot=\"select-trigger\"\r\n      data-size={size}\r\n      className={cn(\r\n        \"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n      <SelectPrimitive.Icon\r\n        render={\r\n          <ChevronDownIcon className=\"pointer-events-none size-4 text-muted-foreground\" />\r\n        }\r\n      />\r\n    </SelectPrimitive.Trigger>\r\n  );\r\n}\r\n\r\nfunction SelectContent({\r\n  className,\r\n  children,\r\n  side = 'bottom',\r\n  sideOffset = 4,\r\n  align = 'center',\r\n  alignOffset = 0,\r\n  alignItemWithTrigger = true,\r\n  ...props\r\n}: SelectPrimitive.Popup.Props &\r\n  Pick<\r\n    SelectPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\r\n  >) {\r\n  return (\r\n    <SelectPrimitive.Portal>\r\n      <SelectPrimitive.Positioner\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        alignItemWithTrigger={alignItemWithTrigger}\r\n        className=\"isolate z-50\"\r\n      >\r\n        <SelectPrimitive.Popup\r\n          data-slot=\"select-content\"\r\n          data-align-trigger={alignItemWithTrigger}\r\n          className={cn(\r\n            'relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\r\n            className,\r\n          )}\r\n          {...props}\r\n        >\r\n          <SelectScrollUpButton />\r\n          <SelectPrimitive.List>{children}</SelectPrimitive.List>\r\n          <SelectScrollDownButton />\r\n        </SelectPrimitive.Popup>\r\n      </SelectPrimitive.Positioner>\r\n    </SelectPrimitive.Portal>\r\n  );\r\n}\r\n\r\nfunction SelectLabel({\r\n  className,\r\n  ...props\r\n}: SelectPrimitive.GroupLabel.Props) {\r\n  return (\r\n    <SelectPrimitive.GroupLabel\r\n      data-slot=\"select-label\"\r\n      className={cn('px-1.5 py-1 text-xs text-muted-foreground', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SelectItem({\r\n  className,\r\n  children,\r\n  ...props\r\n}: SelectPrimitive.Item.Props) {\r\n  return (\r\n    <SelectPrimitive.Item\r\n      data-slot=\"select-item\"\r\n      className={cn(\r\n        \"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <SelectPrimitive.ItemText className=\"flex flex-1 shrink-0 gap-2 whitespace-nowrap\">\r\n        {children}\r\n      </SelectPrimitive.ItemText>\r\n      <SelectPrimitive.ItemIndicator\r\n        render={\r\n          <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\" />\r\n        }\r\n      >\r\n        <CheckIcon className=\"pointer-events-none\" />\r\n      </SelectPrimitive.ItemIndicator>\r\n    </SelectPrimitive.Item>\r\n  );\r\n}\r\n\r\nfunction SelectSeparator({\r\n  className,\r\n  ...props\r\n}: SelectPrimitive.Separator.Props) {\r\n  return (\r\n    <SelectPrimitive.Separator\r\n      data-slot=\"select-separator\"\r\n      className={cn('pointer-events-none -mx-1 my-1 h-px bg-border', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SelectScrollUpButton({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\r\n  return (\r\n    <SelectPrimitive.ScrollUpArrow\r\n      data-slot=\"select-scroll-up-button\"\r\n      className={cn(\r\n        \"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <ChevronUpIcon />\r\n    </SelectPrimitive.ScrollUpArrow>\r\n  );\r\n}\r\n\r\nfunction SelectScrollDownButton({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\r\n  return (\r\n    <SelectPrimitive.ScrollDownArrow\r\n      data-slot=\"select-scroll-down-button\"\r\n      className={cn(\r\n        \"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <ChevronDownIcon />\r\n    </SelectPrimitive.ScrollDownArrow>\r\n  );\r\n}\r\n\r\nexport {\r\n  Select,\r\n  SelectContent,\r\n  SelectGroup,\r\n  SelectItem,\r\n  SelectLabel,\r\n  SelectScrollDownButton,\r\n  SelectScrollUpButton,\r\n  SelectSeparator,\r\n  SelectTrigger,\r\n  SelectValue,\r\n};\r\n","handborne/source/components/ui/separator.tsx":"'use client';\r\n\r\nimport { Separator as SeparatorPrimitive } from '@base-ui/react/separator';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Separator({\r\n  className,\r\n  orientation = 'horizontal',\r\n  ...props\r\n}: SeparatorPrimitive.Props) {\r\n  return (\r\n    <SeparatorPrimitive\r\n      data-slot=\"separator\"\r\n      orientation={orientation}\r\n      className={cn(\r\n        'shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Separator };\r\n","handborne/source/components/ui/sheet.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Dialog as SheetPrimitive } from '@base-ui/react/dialog';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { XIcon } from 'lucide-react';\r\n\r\nfunction Sheet({ ...props }: SheetPrimitive.Root.Props) {\r\n  return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />;\r\n}\r\n\r\nfunction SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {\r\n  return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\r\n}\r\n\r\nfunction SheetClose({ ...props }: SheetPrimitive.Close.Props) {\r\n  return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />;\r\n}\r\n\r\nfunction SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {\r\n  return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />;\r\n}\r\n\r\nfunction SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {\r\n  return (\r\n    <SheetPrimitive.Backdrop\r\n      data-slot=\"sheet-overlay\"\r\n      className={cn(\r\n        'bg-black/10 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SheetContent({\r\n  className,\r\n  children,\r\n  side = 'right',\r\n  showCloseButton = true,\r\n  ...props\r\n}: SheetPrimitive.Popup.Props & {\r\n  side?: 'top' | 'right' | 'bottom' | 'left';\r\n  showCloseButton?: boolean;\r\n}) {\r\n  return (\r\n    <SheetPortal>\r\n      <SheetOverlay />\r\n      <SheetPrimitive.Popup\r\n        data-slot=\"sheet-content\"\r\n        data-side={side}\r\n        className={cn(\r\n          'bg-popover text-popover-foreground fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem]',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        {children}\r\n        {showCloseButton && (\r\n          <SheetPrimitive.Close\r\n            data-slot=\"sheet-close\"\r\n            render={\r\n              <Button\r\n                variant=\"ghost\"\r\n                className=\"absolute top-3 right-3\"\r\n                size=\"icon-sm\"\r\n              />\r\n            }\r\n          >\r\n            <XIcon />\r\n            <span className=\"sr-only\">Close</span>\r\n          </SheetPrimitive.Close>\r\n        )}\r\n      </SheetPrimitive.Popup>\r\n    </SheetPortal>\r\n  );\r\n}\r\n\r\nfunction SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sheet-header\"\r\n      className={cn('gap-0.5 p-4 flex flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sheet-footer\"\r\n      className={cn('gap-2 p-4 mt-auto flex flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {\r\n  return (\r\n    <SheetPrimitive.Title\r\n      data-slot=\"sheet-title\"\r\n      className={cn(\r\n        'text-foreground text-base font-medium cn-font-heading',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SheetDescription({\r\n  className,\r\n  ...props\r\n}: SheetPrimitive.Description.Props) {\r\n  return (\r\n    <SheetPrimitive.Description\r\n      data-slot=\"sheet-description\"\r\n      className={cn('text-muted-foreground text-sm', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Sheet,\r\n  SheetTrigger,\r\n  SheetClose,\r\n  SheetContent,\r\n  SheetHeader,\r\n  SheetFooter,\r\n  SheetTitle,\r\n  SheetDescription,\r\n};\r\n","handborne/source/components/ui/sidebar.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { mergeProps } from '@base-ui/react/merge-props';\r\nimport { useRender } from '@base-ui/react/use-render';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { useIsMobile } from '@/hooks/use-mobile';\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Separator } from '@/components/ui/separator';\r\nimport {\r\n  Sheet,\r\n  SheetContent,\r\n  SheetDescription,\r\n  SheetHeader,\r\n  SheetTitle,\r\n} from '@/components/ui/sheet';\r\nimport { Skeleton } from '@/components/ui/skeleton';\r\nimport {\r\n  Tooltip,\r\n  TooltipContent,\r\n  TooltipTrigger,\r\n} from '@/components/ui/tooltip';\r\nimport { PanelLeftIcon } from 'lucide-react';\r\n\r\nconst SIDEBAR_COOKIE_NAME = 'sidebar_state';\r\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\r\nconst SIDEBAR_WIDTH = '16rem';\r\nconst SIDEBAR_WIDTH_MOBILE = '18rem';\r\nconst SIDEBAR_WIDTH_ICON = '3rem';\r\nconst SIDEBAR_KEYBOARD_SHORTCUT = 'b';\r\n\r\ntype SidebarContextProps = {\r\n  state: 'expanded' | 'collapsed';\r\n  open: boolean;\r\n  setOpen: (open: boolean) => void;\r\n  openMobile: boolean;\r\n  setOpenMobile: (open: boolean) => void;\r\n  isMobile: boolean;\r\n  toggleSidebar: () => void;\r\n};\r\n\r\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null);\r\n\r\nfunction useSidebar() {\r\n  const context = React.useContext(SidebarContext);\r\n  if (!context) {\r\n    throw new Error('useSidebar must be used within a SidebarProvider.');\r\n  }\r\n\r\n  return context;\r\n}\r\n\r\nfunction SidebarProvider({\r\n  defaultOpen = true,\r\n  open: openProp,\r\n  onOpenChange: setOpenProp,\r\n  className,\r\n  style,\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  defaultOpen?: boolean;\r\n  open?: boolean;\r\n  onOpenChange?: (open: boolean) => void;\r\n}) {\r\n  const isMobile = useIsMobile();\r\n  const [openMobile, setOpenMobile] = React.useState(false);\r\n\r\n  // This is the internal state of the sidebar.\r\n  // We use openProp and setOpenProp for control from outside the component.\r\n  const [_open, _setOpen] = React.useState(defaultOpen);\r\n  const open = openProp ?? _open;\r\n  const setOpen = React.useCallback(\r\n    (value: boolean | ((value: boolean) => boolean)) => {\r\n      const openState = typeof value === 'function' ? value(open) : value;\r\n      if (setOpenProp) {\r\n        setOpenProp(openState);\r\n      } else {\r\n        _setOpen(openState);\r\n      }\r\n\r\n      // This sets the cookie to keep the sidebar state.\r\n      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\r\n    },\r\n    [setOpenProp, open],\r\n  );\r\n\r\n  // Helper to toggle the sidebar.\r\n  const toggleSidebar = React.useCallback(() => {\r\n    return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);\r\n  }, [isMobile, setOpen, setOpenMobile]);\r\n\r\n  // Adds a keyboard shortcut to toggle the sidebar.\r\n  React.useEffect(() => {\r\n    const handleKeyDown = (event: KeyboardEvent) => {\r\n      if (\r\n        event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\r\n        (event.metaKey || event.ctrlKey)\r\n      ) {\r\n        event.preventDefault();\r\n        toggleSidebar();\r\n      }\r\n    };\r\n\r\n    window.addEventListener('keydown', handleKeyDown);\r\n    return () => window.removeEventListener('keydown', handleKeyDown);\r\n  }, [toggleSidebar]);\r\n\r\n  // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\r\n  // This makes it easier to style the sidebar with Tailwind classes.\r\n  const state = open ? 'expanded' : 'collapsed';\r\n\r\n  const contextValue = React.useMemo<SidebarContextProps>(\r\n    () => ({\r\n      state,\r\n      open,\r\n      setOpen,\r\n      isMobile,\r\n      openMobile,\r\n      setOpenMobile,\r\n      toggleSidebar,\r\n    }),\r\n    [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],\r\n  );\r\n\r\n  return (\r\n    <SidebarContext.Provider value={contextValue}>\r\n      <div\r\n        data-slot=\"sidebar-wrapper\"\r\n        style={\r\n          {\r\n            '--sidebar-width': SIDEBAR_WIDTH,\r\n            '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,\r\n            ...style,\r\n          } as React.CSSProperties\r\n        }\r\n        className={cn(\r\n          'group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        {children}\r\n      </div>\r\n    </SidebarContext.Provider>\r\n  );\r\n}\r\n\r\nfunction Sidebar({\r\n  side = 'left',\r\n  variant = 'sidebar',\r\n  collapsible = 'offcanvas',\r\n  className,\r\n  children,\r\n  dir,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  side?: 'left' | 'right';\r\n  variant?: 'sidebar' | 'floating' | 'inset';\r\n  collapsible?: 'offcanvas' | 'icon' | 'none';\r\n}) {\r\n  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\r\n\r\n  if (collapsible === 'none') {\r\n    return (\r\n      <div\r\n        data-slot=\"sidebar\"\r\n        className={cn(\r\n          'flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        {children}\r\n      </div>\r\n    );\r\n  }\r\n\r\n  if (isMobile) {\r\n    return (\r\n      <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\r\n        <SheetContent\r\n          dir={dir}\r\n          data-sidebar=\"sidebar\"\r\n          data-slot=\"sidebar\"\r\n          data-mobile=\"true\"\r\n          className=\"w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\r\n          style={\r\n            {\r\n              '--sidebar-width': SIDEBAR_WIDTH_MOBILE,\r\n            } as React.CSSProperties\r\n          }\r\n          side={side}\r\n        >\r\n          <SheetHeader className=\"sr-only\">\r\n            <SheetTitle>Sidebar</SheetTitle>\r\n            <SheetDescription>Displays the mobile sidebar.</SheetDescription>\r\n          </SheetHeader>\r\n          <div className=\"flex h-full w-full flex-col\">{children}</div>\r\n        </SheetContent>\r\n      </Sheet>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <div\r\n      className=\"group peer hidden text-sidebar-foreground md:block\"\r\n      data-state={state}\r\n      data-collapsible={state === 'collapsed' ? collapsible : ''}\r\n      data-variant={variant}\r\n      data-side={side}\r\n      data-slot=\"sidebar\"\r\n    >\r\n      {/* This is what handles the sidebar gap on desktop */}\r\n      <div\r\n        data-slot=\"sidebar-gap\"\r\n        className={cn(\r\n          'transition-[width] duration-200 ease-linear relative w-(--sidebar-width) bg-transparent',\r\n          'group-data-[collapsible=offcanvas]:w-0',\r\n          'group-data-[side=right]:rotate-180',\r\n          variant === 'floating' || variant === 'inset'\r\n            ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\r\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\r\n        )}\r\n      />\r\n      <div\r\n        data-slot=\"sidebar-container\"\r\n        data-side={side}\r\n        className={cn(\r\n          'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex',\r\n          // Adjust the padding for floating and inset variants.\r\n          variant === 'floating' || variant === 'inset'\r\n            ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\r\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\r\n          className,\r\n        )}\r\n        {...props}\r\n      >\r\n        <div\r\n          data-sidebar=\"sidebar\"\r\n          data-slot=\"sidebar-inner\"\r\n          className=\"bg-sidebar group-data-[variant=floating]:ring-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 flex size-full flex-col\"\r\n        >\r\n          {children}\r\n        </div>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction SidebarTrigger({\r\n  className,\r\n  onClick,\r\n  ...props\r\n}: React.ComponentProps<typeof Button>) {\r\n  const { toggleSidebar } = useSidebar();\r\n\r\n  return (\r\n    <Button\r\n      data-sidebar=\"trigger\"\r\n      data-slot=\"sidebar-trigger\"\r\n      variant=\"ghost\"\r\n      size=\"icon-sm\"\r\n      className={cn(className)}\r\n      onClick={(event) => {\r\n        onClick?.(event);\r\n        toggleSidebar();\r\n      }}\r\n      {...props}\r\n    >\r\n      <PanelLeftIcon className=\"cn-rtl-flip\" />\r\n      <span className=\"sr-only\">Toggle Sidebar</span>\r\n    </Button>\r\n  );\r\n}\r\n\r\nfunction SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {\r\n  const { toggleSidebar } = useSidebar();\r\n\r\n  return (\r\n    <button\r\n      data-sidebar=\"rail\"\r\n      data-slot=\"sidebar-rail\"\r\n      aria-label=\"Toggle Sidebar\"\r\n      tabIndex={-1}\r\n      onClick={toggleSidebar}\r\n      title=\"Toggle Sidebar\"\r\n      className={cn(\r\n        'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2',\r\n        'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\r\n        '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\r\n        'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar',\r\n        '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\r\n        '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {\r\n  return (\r\n    <main\r\n      data-slot=\"sidebar-inset\"\r\n      className={cn(\r\n        'bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 relative flex w-full flex-1 flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarInput({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof Input>) {\r\n  return (\r\n    <Input\r\n      data-slot=\"sidebar-input\"\r\n      data-sidebar=\"input\"\r\n      className={cn('bg-background h-8 w-full shadow-none', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-header\"\r\n      data-sidebar=\"header\"\r\n      className={cn('gap-2 p-2 flex flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-footer\"\r\n      data-sidebar=\"footer\"\r\n      className={cn('gap-2 p-2 flex flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarSeparator({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<typeof Separator>) {\r\n  return (\r\n    <Separator\r\n      data-slot=\"sidebar-separator\"\r\n      data-sidebar=\"separator\"\r\n      className={cn('bg-sidebar-border mx-2 w-auto', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-content\"\r\n      data-sidebar=\"content\"\r\n      className={cn(\r\n        'no-scrollbar gap-0 flex min-h-0 flex-1 flex-col overflow-auto group-data-[collapsible=icon]:overflow-hidden',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-group\"\r\n      data-sidebar=\"group\"\r\n      className={cn('p-2 relative flex w-full min-w-0 flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarGroupLabel({\r\n  className,\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'div'> & React.ComponentProps<'div'>) {\r\n  return useRender({\r\n    defaultTagName: 'div',\r\n    props: mergeProps<'div'>(\r\n      {\r\n        className: cn(\r\n          'text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 flex shrink-0 items-center outline-hidden [&>svg]:shrink-0',\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'sidebar-group-label',\r\n      sidebar: 'group-label',\r\n    },\r\n  });\r\n}\r\n\r\nfunction SidebarGroupAction({\r\n  className,\r\n  render,\r\n  ...props\r\n}: useRender.ComponentProps<'button'> & React.ComponentProps<'button'>) {\r\n  return useRender({\r\n    defaultTagName: 'button',\r\n    props: mergeProps<'button'>(\r\n      {\r\n        className: cn(\r\n          'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 w-5 rounded-md p-0 focus-visible:ring-2 [&>svg]:size-4 flex aspect-square items-center justify-center outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 md:after:hidden [&>svg]:shrink-0',\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'sidebar-group-action',\r\n      sidebar: 'group-action',\r\n    },\r\n  });\r\n}\r\n\r\nfunction SidebarGroupContent({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-group-content\"\r\n      data-sidebar=\"group-content\"\r\n      className={cn('text-sm w-full', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {\r\n  return (\r\n    <ul\r\n      data-slot=\"sidebar-menu\"\r\n      data-sidebar=\"menu\"\r\n      className={cn('gap-0 flex w-full min-w-0 flex-col', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {\r\n  return (\r\n    <li\r\n      data-slot=\"sidebar-menu-item\"\r\n      data-sidebar=\"menu-item\"\r\n      className={cn('group/menu-item relative', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst sidebarMenuButtonVariants = cva(\r\n  'ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left text-sm transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button group/menu-button flex w-full items-center overflow-hidden outline-hidden disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',\r\n        outline:\r\n          'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)]',\r\n      },\r\n      size: {\r\n        default: 'h-8 text-sm',\r\n        sm: 'h-7 text-xs',\r\n        lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n      size: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction SidebarMenuButton({\r\n  render,\r\n  isActive = false,\r\n  variant = 'default',\r\n  size = 'default',\r\n  tooltip,\r\n  className,\r\n  ...props\r\n}: useRender.ComponentProps<'button'> &\r\n  React.ComponentProps<'button'> & {\r\n    isActive?: boolean;\r\n    tooltip?: string | React.ComponentProps<typeof TooltipContent>;\r\n  } & VariantProps<typeof sidebarMenuButtonVariants>) {\r\n  const { isMobile, state } = useSidebar();\r\n  const comp = useRender({\r\n    defaultTagName: 'button',\r\n    props: mergeProps<'button'>(\r\n      {\r\n        className: cn(sidebarMenuButtonVariants({ variant, size }), className),\r\n      },\r\n      props,\r\n    ),\r\n    render: !tooltip ? render : <TooltipTrigger render={render} />,\r\n    state: {\r\n      slot: 'sidebar-menu-button',\r\n      sidebar: 'menu-button',\r\n      size,\r\n      active: isActive,\r\n    },\r\n  });\r\n\r\n  if (!tooltip) {\r\n    return comp;\r\n  }\r\n\r\n  if (typeof tooltip === 'string') {\r\n    tooltip = {\r\n      children: tooltip,\r\n    };\r\n  }\r\n\r\n  return (\r\n    <Tooltip>\r\n      {comp}\r\n      <TooltipContent\r\n        side=\"right\"\r\n        align=\"center\"\r\n        hidden={state !== 'collapsed' || isMobile}\r\n        {...tooltip}\r\n      />\r\n    </Tooltip>\r\n  );\r\n}\r\n\r\nfunction SidebarMenuAction({\r\n  className,\r\n  render,\r\n  showOnHover = false,\r\n  ...props\r\n}: useRender.ComponentProps<'button'> &\r\n  React.ComponentProps<'button'> & {\r\n    showOnHover?: boolean;\r\n  }) {\r\n  return useRender({\r\n    defaultTagName: 'button',\r\n    props: mergeProps<'button'>(\r\n      {\r\n        className: cn(\r\n          'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 aspect-square w-5 rounded-md p-0 peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 focus-visible:ring-2 [&>svg]:size-4 flex items-center justify-center outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 md:after:hidden [&>svg]:shrink-0',\r\n          showOnHover &&\r\n            'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0',\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'sidebar-menu-action',\r\n      sidebar: 'menu-action',\r\n    },\r\n  });\r\n}\r\n\r\nfunction SidebarMenuBadge({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-menu-badge\"\r\n      data-sidebar=\"menu-badge\"\r\n      className={cn(\r\n        'text-sidebar-foreground peer-hover/menu-button:text-sidebar-accent-foreground peer-data-active/menu-button:text-sidebar-accent-foreground pointer-events-none absolute right-1 h-5 min-w-5 rounded-md px-1 text-xs font-medium peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 flex items-center justify-center tabular-nums select-none group-data-[collapsible=icon]:hidden',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarMenuSkeleton({\r\n  className,\r\n  showIcon = false,\r\n  ...props\r\n}: React.ComponentProps<'div'> & {\r\n  showIcon?: boolean;\r\n}) {\r\n  // Random width between 50 to 90%.\r\n  const [width] = React.useState(() => {\r\n    return `${Math.floor(Math.random() * 40) + 50}%`;\r\n  });\r\n\r\n  return (\r\n    <div\r\n      data-slot=\"sidebar-menu-skeleton\"\r\n      data-sidebar=\"menu-skeleton\"\r\n      className={cn('h-8 gap-2 rounded-md px-2 flex items-center', className)}\r\n      {...props}\r\n    >\r\n      {showIcon && (\r\n        <Skeleton\r\n          className=\"size-4 rounded-md\"\r\n          data-sidebar=\"menu-skeleton-icon\"\r\n        />\r\n      )}\r\n      <Skeleton\r\n        className=\"h-4 max-w-(--skeleton-width) flex-1\"\r\n        data-sidebar=\"menu-skeleton-text\"\r\n        style={\r\n          {\r\n            '--skeleton-width': width,\r\n          } as React.CSSProperties\r\n        }\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {\r\n  return (\r\n    <ul\r\n      data-slot=\"sidebar-menu-sub\"\r\n      data-sidebar=\"menu-sub\"\r\n      className={cn(\r\n        'border-sidebar-border mx-3.5 translate-x-px gap-1 border-l px-2.5 py-0.5 group-data-[collapsible=icon]:hidden flex min-w-0 flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarMenuSubItem({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'li'>) {\r\n  return (\r\n    <li\r\n      data-slot=\"sidebar-menu-sub-item\"\r\n      data-sidebar=\"menu-sub-item\"\r\n      className={cn('group/menu-sub-item relative', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction SidebarMenuSubButton({\r\n  render,\r\n  size = 'md',\r\n  isActive = false,\r\n  className,\r\n  ...props\r\n}: useRender.ComponentProps<'a'> &\r\n  React.ComponentProps<'a'> & {\r\n    size?: 'sm' | 'md';\r\n    isActive?: boolean;\r\n  }) {\r\n  return useRender({\r\n    defaultTagName: 'a',\r\n    props: mergeProps<'a'>(\r\n      {\r\n        className: cn(\r\n          'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 focus-visible:ring-2 data-[size=md]:text-sm data-[size=sm]:text-xs [&>svg]:size-4 flex min-w-0 -translate-x-px items-center overflow-hidden outline-hidden group-data-[collapsible=icon]:hidden disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:shrink-0',\r\n          className,\r\n        ),\r\n      },\r\n      props,\r\n    ),\r\n    render,\r\n    state: {\r\n      slot: 'sidebar-menu-sub-button',\r\n      sidebar: 'menu-sub-button',\r\n      size,\r\n      active: isActive,\r\n    },\r\n  });\r\n}\r\n\r\nexport {\r\n  Sidebar,\r\n  SidebarContent,\r\n  SidebarFooter,\r\n  SidebarGroup,\r\n  SidebarGroupAction,\r\n  SidebarGroupContent,\r\n  SidebarGroupLabel,\r\n  SidebarHeader,\r\n  SidebarInput,\r\n  SidebarInset,\r\n  SidebarMenu,\r\n  SidebarMenuAction,\r\n  SidebarMenuBadge,\r\n  SidebarMenuButton,\r\n  SidebarMenuItem,\r\n  SidebarMenuSkeleton,\r\n  SidebarMenuSub,\r\n  SidebarMenuSubButton,\r\n  SidebarMenuSubItem,\r\n  SidebarProvider,\r\n  SidebarRail,\r\n  SidebarSeparator,\r\n  SidebarTrigger,\r\n  useSidebar,\r\n};\r\n","handborne/source/components/ui/skeleton.tsx":"import { cn } from '@/lib/utils';\r\n\r\nfunction Skeleton({ className, ...props }: React.ComponentProps<'div'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"skeleton\"\r\n      className={cn('animate-pulse rounded-md bg-muted', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Skeleton };\r\n","handborne/source/components/ui/slider.tsx":"import { Slider as SliderPrimitive } from '@base-ui/react/slider';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Slider({\r\n  className,\r\n  defaultValue,\r\n  value,\r\n  min = 0,\r\n  max = 100,\r\n  ...props\r\n}: SliderPrimitive.Root.Props) {\r\n  const _values = Array.isArray(value)\r\n    ? value\r\n    : Array.isArray(defaultValue)\r\n      ? defaultValue\r\n      : [min, max];\r\n\r\n  return (\r\n    <SliderPrimitive.Root\r\n      className={cn('data-horizontal:w-full data-vertical:h-full', className)}\r\n      data-slot=\"slider\"\r\n      defaultValue={defaultValue}\r\n      value={value}\r\n      min={min}\r\n      max={max}\r\n      thumbAlignment=\"edge\"\r\n      {...props}\r\n    >\r\n      <SliderPrimitive.Control className=\"data-vertical:min-h-40 relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:w-auto data-vertical:flex-col\">\r\n        <SliderPrimitive.Track\r\n          data-slot=\"slider-track\"\r\n          className=\"bg-muted rounded-full data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1 relative grow overflow-hidden select-none\"\r\n        >\r\n          <SliderPrimitive.Indicator\r\n            data-slot=\"slider-range\"\r\n            className=\"bg-primary select-none data-horizontal:h-full data-vertical:w-full\"\r\n          />\r\n        </SliderPrimitive.Track>\r\n        {Array.from({ length: _values.length }, (_, index) => (\r\n          <SliderPrimitive.Thumb\r\n            data-slot=\"slider-thumb\"\r\n            key={index}\r\n            className=\"border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 block shrink-0 select-none disabled:pointer-events-none disabled:opacity-50\"\r\n          />\r\n        ))}\r\n      </SliderPrimitive.Control>\r\n    </SliderPrimitive.Root>\r\n  );\r\n}\r\n\r\nexport { Slider };\r\n","handborne/source/components/ui/spinner.tsx":"import { cn } from '@/lib/utils';\r\nimport { Loader2Icon } from 'lucide-react';\r\n\r\nfunction Spinner({ className, ...props }: React.ComponentProps<'svg'>) {\r\n  return (\r\n    <Loader2Icon\r\n      data-slot=\"spinner\"\r\n      role=\"status\"\r\n      aria-label=\"Loading\"\r\n      className={cn('size-4 animate-spin', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Spinner };\r\n","handborne/source/components/ui/switch.tsx":"'use client';\r\n\r\nimport { Switch as SwitchPrimitive } from '@base-ui/react/switch';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Switch({\r\n  className,\r\n  size = 'default',\r\n  ...props\r\n}: SwitchPrimitive.Root.Props & {\r\n  size?: 'sm' | 'default';\r\n}) {\r\n  return (\r\n    <SwitchPrimitive.Root\r\n      data-slot=\"switch\"\r\n      data-size={size}\r\n      className={cn(\r\n        'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <SwitchPrimitive.Thumb\r\n        data-slot=\"switch-thumb\"\r\n        className=\"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground\"\r\n      />\r\n    </SwitchPrimitive.Root>\r\n  );\r\n}\r\n\r\nexport { Switch };\r\n","handborne/source/components/ui/table.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Table({ className, ...props }: React.ComponentProps<'table'>) {\r\n  return (\r\n    <div\r\n      data-slot=\"table-container\"\r\n      className=\"relative w-full overflow-x-auto\"\r\n    >\r\n      <table\r\n        data-slot=\"table\"\r\n        className={cn('w-full caption-bottom text-sm', className)}\r\n        {...props}\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {\r\n  return (\r\n    <thead\r\n      data-slot=\"table-header\"\r\n      className={cn('[&_tr]:border-b', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {\r\n  return (\r\n    <tbody\r\n      data-slot=\"table-body\"\r\n      className={cn('[&_tr:last-child]:border-0', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {\r\n  return (\r\n    <tfoot\r\n      data-slot=\"table-footer\"\r\n      className={cn(\r\n        'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableRow({ className, ...props }: React.ComponentProps<'tr'>) {\r\n  return (\r\n    <tr\r\n      data-slot=\"table-row\"\r\n      className={cn(\r\n        'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors has-aria-expanded:bg-muted/50',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableHead({ className, ...props }: React.ComponentProps<'th'>) {\r\n  return (\r\n    <th\r\n      data-slot=\"table-head\"\r\n      className={cn(\r\n        'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableCell({ className, ...props }: React.ComponentProps<'td'>) {\r\n  return (\r\n    <td\r\n      data-slot=\"table-cell\"\r\n      className={cn(\r\n        'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TableCaption({\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<'caption'>) {\r\n  return (\r\n    <caption\r\n      data-slot=\"table-caption\"\r\n      className={cn('text-muted-foreground mt-4 text-sm', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Table,\r\n  TableHeader,\r\n  TableBody,\r\n  TableFooter,\r\n  TableHead,\r\n  TableRow,\r\n  TableCell,\r\n  TableCaption,\r\n};\r\n","handborne/source/components/ui/tabs.tsx":"'use client';\r\n\r\nimport { Tabs as TabsPrimitive } from '@base-ui/react/tabs';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Tabs({\r\n  className,\r\n  orientation = 'horizontal',\r\n  ...props\r\n}: TabsPrimitive.Root.Props) {\r\n  return (\r\n    <TabsPrimitive.Root\r\n      data-slot=\"tabs\"\r\n      data-orientation={orientation}\r\n      className={cn(\r\n        'group/tabs flex gap-2 data-horizontal:flex-col',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nconst tabsListVariants = cva(\r\n  'group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none',\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-muted',\r\n        line: 'gap-1 bg-transparent',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction TabsList({\r\n  className,\r\n  variant = 'default',\r\n  ...props\r\n}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {\r\n  return (\r\n    <TabsPrimitive.List\r\n      data-slot=\"tabs-list\"\r\n      data-variant={variant}\r\n      className={cn(tabsListVariants({ variant }), className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {\r\n  return (\r\n    <TabsPrimitive.Tab\r\n      data-slot=\"tabs-trigger\"\r\n      className={cn(\r\n        \"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n        'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',\r\n        'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',\r\n        'after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {\r\n  return (\r\n    <TabsPrimitive.Panel\r\n      data-slot=\"tabs-content\"\r\n      className={cn('flex-1 text-sm outline-none', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };\r\n","handborne/source/components/ui/textarea.tsx":"import * as React from 'react';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {\r\n  return (\r\n    <textarea\r\n      data-slot=\"textarea\"\r\n      className={cn(\r\n        'flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Textarea };\r\n","handborne/source/components/ui/toast.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Toast as ToastPrimitive } from '@base-ui/react/toast';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { Button } from '@/components/ui/button';\r\nimport {\r\n  XIcon,\r\n  CircleCheckIcon,\r\n  InfoIcon,\r\n  TriangleAlertIcon,\r\n  OctagonXIcon,\r\n  Loader2Icon,\r\n} from 'lucide-react';\r\n\r\nconst toast = ToastPrimitive.createToastManager();\r\n\r\nfunction ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {\r\n  return <ToastPrimitive.Provider {...props} />;\r\n}\r\n\r\nfunction ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {\r\n  return <ToastPrimitive.Portal data-slot=\"toast-portal\" {...props} />;\r\n}\r\n\r\nfunction ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {\r\n  return (\r\n    <ToastPrimitive.Viewport\r\n      data-slot=\"toast-viewport\"\r\n      className={cn(\r\n        'pointer-events-none fixed inset-x-4 bottom-4 z-50 mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction Toast({ className, ...props }: ToastPrimitive.Root.Props) {\r\n  return (\r\n    <ToastPrimitive.Root\r\n      data-slot=\"toast\"\r\n      className={cn(\r\n        'rounded-2xl group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50',\r\n        '[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]',\r\n        'h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]',\r\n        \"after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']\",\r\n        'data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]',\r\n        'data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]',\r\n        '[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]',\r\n        'data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]',\r\n        'data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]',\r\n        'data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]',\r\n        'data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]',\r\n        'data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]',\r\n        'data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]',\r\n        'data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]',\r\n        'data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {\r\n  return (\r\n    <ToastPrimitive.Content\r\n      data-slot=\"toast-content\"\r\n      className={cn(\r\n        'flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100',\r\n        className,\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {\r\n  return (\r\n    <ToastPrimitive.Title\r\n      data-slot=\"toast-title\"\r\n      className={cn('text-sm font-medium', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ToastDescription({\r\n  className,\r\n  ...props\r\n}: ToastPrimitive.Description.Props) {\r\n  return (\r\n    <ToastPrimitive.Description\r\n      data-slot=\"toast-description\"\r\n      className={cn('text-sm text-muted-foreground', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ToastAction({\r\n  className,\r\n  render = <Button variant=\"outline\" size=\"sm\" />,\r\n  ...props\r\n}: ToastPrimitive.Action.Props) {\r\n  return (\r\n    <ToastPrimitive.Action\r\n      data-slot=\"toast-action\"\r\n      render={render}\r\n      className={cn('shrink-0', className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction ToastClose({\r\n  className,\r\n  children,\r\n  render = <Button variant=\"ghost\" size=\"icon-sm\" />,\r\n  ...props\r\n}: ToastPrimitive.Close.Props) {\r\n  return (\r\n    <ToastPrimitive.Close\r\n      data-slot=\"toast-close\"\r\n      aria-label=\"Close toast\"\r\n      render={render}\r\n      className={cn(\r\n        \"relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground\",\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children ?? <XIcon aria-hidden=\"true\" />}\r\n    </ToastPrimitive.Close>\r\n  );\r\n}\r\n\r\nfunction ToastIcon({ type }: { type: string | undefined }) {\r\n  let icon: React.ReactNode = null;\r\n\r\n  if (type === 'success') {\r\n    icon = <CircleCheckIcon aria-hidden=\"true\" />;\r\n  }\r\n\r\n  if (type === 'info') {\r\n    icon = <InfoIcon aria-hidden=\"true\" />;\r\n  }\r\n\r\n  if (type === 'warning') {\r\n    icon = <TriangleAlertIcon aria-hidden=\"true\" />;\r\n  }\r\n\r\n  if (type === 'error') {\r\n    icon = <OctagonXIcon className=\"text-destructive\" aria-hidden=\"true\" />;\r\n  }\r\n\r\n  if (type === 'loading') {\r\n    icon = <Loader2Icon className=\"animate-spin\" aria-hidden=\"true\" />;\r\n  }\r\n\r\n  if (!icon) {\r\n    return null;\r\n  }\r\n\r\n  return (\r\n    <span\r\n      data-slot=\"toast-icon\"\r\n      className=\"shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\"\r\n    >\r\n      {icon}\r\n    </span>\r\n  );\r\n}\r\n\r\nfunction ToastList() {\r\n  const { toasts } = ToastPrimitive.useToastManager();\r\n\r\n  return toasts.map((toastItem) => (\r\n    <Toast key={toastItem.id} toast={toastItem}>\r\n      <ToastContent>\r\n        <ToastIcon type={toastItem.type} />\r\n        <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\r\n          <ToastTitle />\r\n          <ToastDescription />\r\n        </div>\r\n        <ToastAction />\r\n        <ToastClose />\r\n      </ToastContent>\r\n    </Toast>\r\n  ));\r\n}\r\n\r\nfunction Toaster({\r\n  children,\r\n  toastManager = toast,\r\n  ...props\r\n}: ToastPrimitive.Provider.Props) {\r\n  return (\r\n    <ToastProvider toastManager={toastManager} {...props}>\r\n      {children}\r\n      <ToastPortal>\r\n        <ToastViewport>\r\n          <ToastList />\r\n        </ToastViewport>\r\n      </ToastPortal>\r\n    </ToastProvider>\r\n  );\r\n}\r\n\r\nconst createToastManager = ToastPrimitive.createToastManager;\r\nconst useToastManager = ToastPrimitive.useToastManager;\r\n\r\nexport {\r\n  Toaster,\r\n  Toast,\r\n  ToastAction,\r\n  ToastClose,\r\n  ToastContent,\r\n  ToastDescription,\r\n  ToastPortal,\r\n  ToastProvider,\r\n  ToastTitle,\r\n  ToastViewport,\r\n  createToastManager,\r\n  toast,\r\n  useToastManager,\r\n};\r\n","handborne/source/components/ui/toggle-group.tsx":"'use client';\r\n\r\nimport * as React from 'react';\r\nimport { Toggle as TogglePrimitive } from '@base-ui/react/toggle';\r\nimport { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group';\r\nimport { type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { toggleVariants } from '@/components/ui/toggle';\r\n\r\nconst ToggleGroupContext = React.createContext<\r\n  VariantProps<typeof toggleVariants> & {\r\n    spacing?: number;\r\n    orientation?: 'horizontal' | 'vertical';\r\n  }\r\n>({\r\n  size: 'default',\r\n  variant: 'default',\r\n  spacing: 2,\r\n  orientation: 'horizontal',\r\n});\r\n\r\nfunction ToggleGroup({\r\n  className,\r\n  variant,\r\n  size,\r\n  spacing = 2,\r\n  orientation = 'horizontal',\r\n  children,\r\n  ...props\r\n}: ToggleGroupPrimitive.Props &\r\n  VariantProps<typeof toggleVariants> & {\r\n    spacing?: number;\r\n    orientation?: 'horizontal' | 'vertical';\r\n  }) {\r\n  return (\r\n    <ToggleGroupPrimitive\r\n      data-slot=\"toggle-group\"\r\n      data-variant={variant}\r\n      data-size={size}\r\n      data-spacing={spacing}\r\n      data-orientation={orientation}\r\n      style={{ '--gap': spacing } as React.CSSProperties}\r\n      className={cn(\r\n        'rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-vertical:flex-col data-vertical:items-stretch',\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      <ToggleGroupContext.Provider\r\n        value={{ variant, size, spacing, orientation }}\r\n      >\r\n        {children}\r\n      </ToggleGroupContext.Provider>\r\n    </ToggleGroupPrimitive>\r\n  );\r\n}\r\n\r\nfunction ToggleGroupItem({\r\n  className,\r\n  children,\r\n  variant = 'default',\r\n  size = 'default',\r\n  ...props\r\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\r\n  const context = React.useContext(ToggleGroupContext);\r\n\r\n  return (\r\n    <TogglePrimitive\r\n      data-slot=\"toggle-group-item\"\r\n      data-variant={context.variant || variant}\r\n      data-size={context.size || size}\r\n      data-spacing={context.spacing}\r\n      className={cn(\r\n        'group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg shrink-0 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t',\r\n        toggleVariants({\r\n          variant: context.variant || variant,\r\n          size: context.size || size,\r\n        }),\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {children}\r\n    </TogglePrimitive>\r\n  );\r\n}\r\n\r\nexport { ToggleGroup, ToggleGroupItem };\r\n","handborne/source/components/ui/toggle.tsx":"'use client';\r\n\r\nimport { Toggle as TogglePrimitive } from '@base-ui/react/toggle';\r\nimport { cva, type VariantProps } from 'class-variance-authority';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst toggleVariants = cva(\r\n  \"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-lg text-sm font-medium transition-all [&_svg:not([class*='size-'])]:size-4 group/toggle inline-flex items-center justify-center whitespace-nowrap outline-none hover:bg-muted focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n  {\r\n    variants: {\r\n      variant: {\r\n        default: 'bg-transparent',\r\n        outline: 'border-input hover:bg-muted border bg-transparent',\r\n      },\r\n      size: {\r\n        default:\r\n          'h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\r\n        sm: \"h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\r\n        lg: 'h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\r\n      },\r\n    },\r\n    defaultVariants: {\r\n      variant: 'default',\r\n      size: 'default',\r\n    },\r\n  },\r\n);\r\n\r\nfunction Toggle({\r\n  className,\r\n  variant = 'default',\r\n  size = 'default',\r\n  ...props\r\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\r\n  return (\r\n    <TogglePrimitive\r\n      data-slot=\"toggle\"\r\n      className={cn(toggleVariants({ variant, size, className }))}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Toggle, toggleVariants };\r\n","handborne/source/components/ui/tooltip.tsx":"'use client';\r\n\r\nimport { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';\r\n\r\nimport { cn } from '@/lib/utils';\r\n\r\nfunction TooltipProvider({\r\n  delay = 0,\r\n  ...props\r\n}: TooltipPrimitive.Provider.Props) {\r\n  return (\r\n    <TooltipPrimitive.Provider\r\n      data-slot=\"tooltip-provider\"\r\n      delay={delay}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction Tooltip({ ...props }: TooltipPrimitive.Root.Props) {\r\n  return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />;\r\n}\r\n\r\nfunction TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {\r\n  return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\r\n}\r\n\r\nfunction TooltipContent({\r\n  className,\r\n  side = 'top',\r\n  sideOffset = 4,\r\n  align = 'center',\r\n  alignOffset = 0,\r\n  children,\r\n  ...props\r\n}: TooltipPrimitive.Popup.Props &\r\n  Pick<\r\n    TooltipPrimitive.Positioner.Props,\r\n    'align' | 'alignOffset' | 'side' | 'sideOffset'\r\n  >) {\r\n  return (\r\n    <TooltipPrimitive.Portal>\r\n      <TooltipPrimitive.Positioner\r\n        align={align}\r\n        alignOffset={alignOffset}\r\n        side={side}\r\n        sideOffset={sideOffset}\r\n        className=\"isolate z-50\"\r\n      >\r\n        <TooltipPrimitive.Popup\r\n          data-slot=\"tooltip-content\"\r\n          className={cn(\r\n            'z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\r\n            className,\r\n          )}\r\n          {...props}\r\n        >\r\n          {children}\r\n          <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5\" />\r\n        </TooltipPrimitive.Popup>\r\n      </TooltipPrimitive.Positioner>\r\n    </TooltipPrimitive.Portal>\r\n  );\r\n}\r\n\r\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\r\n","handborne/source/hand-entry.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><meta name=\"theme-color\" content=\"#6427a5\"><title>Helping Hand · Customizer</title><meta name=\"description\" content=\"Customize your Helping Hand with a live preview, creature styles, nails, scales and gestures.\"></head><body><div id=\"root\"></div><script type=\"module\" src=\"/app/browser-entry.tsx\"></script></body></html>\r\n","handborne/source/hooks/use-mobile.ts":"import * as React from 'react';\r\n\r\nconst MOBILE_BREAKPOINT = 768;\r\n\r\nexport function useIsMobile() {\r\n  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(\r\n    undefined,\r\n  );\r\n\r\n  React.useEffect(() => {\r\n    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\r\n    const onChange = () => {\r\n      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\r\n    };\r\n    mql.addEventListener('change', onChange);\r\n    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\r\n    return () => mql.removeEventListener('change', onChange);\r\n  }, []);\r\n\r\n  return !!isMobile;\r\n}\r\n","handborne/source/lib/utils.ts":"import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n  return twMerge(clsx(inputs));\r\n}\r\n","handborne/source/next-env.d.ts":"import \"vinext/types\";\r\nimport \"./.next/types/routes.d.ts\";\r\n\r\n// NOTE: This file should not be edited\r\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\r\n","handborne/source/package-lock.json":"{\r\n  \"name\": \"sites-project\",\r\n  \"version\": \"0.1.0\",\r\n  \"lockfileVersion\": 3,\r\n  \"requires\": true,\r\n  \"packages\": {\r\n    \"\": {\r\n      \"name\": \"sites-project\",\r\n      \"version\": \"0.1.0\",\r\n      \"dependencies\": {\r\n        \"@base-ui/react\": \"1.7.0\",\r\n        \"@shadcn/react\": \"0.3.0\",\r\n        \"class-variance-authority\": \"0.7.1\",\r\n        \"clsx\": \"2.1.1\",\r\n        \"cmdk\": \"1.1.1\",\r\n        \"date-fns\": \"4.1.0\",\r\n        \"embla-carousel-react\": \"8.5.2\",\r\n        \"input-otp\": \"1.4.2\",\r\n        \"lucide-react\": \"1.31.0\",\r\n        \"react\": \"19.2.6\",\r\n        \"react-day-picker\": \"9.8.1\",\r\n        \"react-dom\": \"19.2.6\",\r\n        \"react-resizable-panels\": \"4.5.8\",\r\n        \"react-server-dom-webpack\": \"19.2.6\",\r\n        \"recharts\": \"3.8.0\",\r\n        \"shadcn\": \"4.18.0\",\r\n        \"tailwind-merge\": \"3.6.0\",\r\n        \"three\": \"^0.180.0\",\r\n        \"tw-animate-css\": \"1.4.0\",\r\n        \"vinext\": \"1.0.0-beta.5\"\r\n      },\r\n      \"devDependencies\": {\r\n        \"@cloudflare/vite-plugin\": \"1.37.1\",\r\n        \"@cloudflare/workers-types\": \"4.20260515.1\",\r\n        \"@openai/sites-vite-plugin\": \"0.2.0\",\r\n        \"@tailwindcss/postcss\": \"4.2.1\",\r\n        \"@types/node\": \"22.19.19\",\r\n        \"@types/react\": \"19.2.14\",\r\n        \"@types/react-dom\": \"19.2.3\",\r\n        \"@types/three\": \"^0.180.0\",\r\n        \"@vitejs/plugin-react\": \"6.0.2\",\r\n        \"@vitejs/plugin-rsc\": \"0.5.26\",\r\n        \"oxfmt\": \"0.61.0\",\r\n        \"oxlint\": \"1.76.0\",\r\n        \"oxlint-tsgolint\": \"7.0.2001\",\r\n        \"tailwindcss\": \"4.2.1\",\r\n        \"typescript\": \"5.9.3\",\r\n        \"vite\": \"8.0.13\",\r\n        \"wrangler\": \"4.92.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.13.0\"\r\n      }\r\n    },\r\n    \"node_modules/@alloc/quick-lru\": {\r\n      \"version\": \"5.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz\",\r\n      \"integrity\": \"sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/code-frame\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-validator-identifier\": \"^7.29.7\",\r\n        \"js-tokens\": \"^4.0.0\",\r\n        \"picocolors\": \"^1.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/compat-data\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/core\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/code-frame\": \"^7.29.7\",\r\n        \"@babel/generator\": \"^7.29.7\",\r\n        \"@babel/helper-compilation-targets\": \"^7.29.7\",\r\n        \"@babel/helper-module-transforms\": \"^7.29.7\",\r\n        \"@babel/helpers\": \"^7.29.7\",\r\n        \"@babel/parser\": \"^7.29.7\",\r\n        \"@babel/template\": \"^7.29.7\",\r\n        \"@babel/traverse\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\",\r\n        \"@jridgewell/remapping\": \"^2.3.5\",\r\n        \"convert-source-map\": \"^2.0.0\",\r\n        \"debug\": \"^4.1.0\",\r\n        \"gensync\": \"^1.0.0-beta.2\",\r\n        \"json5\": \"^2.2.3\",\r\n        \"semver\": \"^6.3.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/babel\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/generator\": {\r\n      \"version\": \"7.29.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz\",\r\n      \"integrity\": \"sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/parser\": \"^7.29.8\",\r\n        \"@babel/types\": \"^7.29.8\",\r\n        \"@jridgewell/gen-mapping\": \"^0.3.12\",\r\n        \"@jridgewell/trace-mapping\": \"^0.3.28\",\r\n        \"jsesc\": \"^3.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-annotate-as-pure\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-compilation-targets\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/compat-data\": \"^7.29.7\",\r\n        \"@babel/helper-validator-option\": \"^7.29.7\",\r\n        \"browserslist\": \"^4.24.0\",\r\n        \"lru-cache\": \"^5.1.1\",\r\n        \"semver\": \"^6.3.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-create-class-features-plugin\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-annotate-as-pure\": \"^7.29.7\",\r\n        \"@babel/helper-member-expression-to-functions\": \"^7.29.7\",\r\n        \"@babel/helper-optimise-call-expression\": \"^7.29.7\",\r\n        \"@babel/helper-replace-supers\": \"^7.29.7\",\r\n        \"@babel/helper-skip-transparent-expression-wrappers\": \"^7.29.7\",\r\n        \"@babel/traverse\": \"^7.29.7\",\r\n        \"semver\": \"^6.3.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-globals\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-member-expression-to-functions\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/traverse\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-module-imports\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/traverse\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-module-transforms\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-module-imports\": \"^7.29.7\",\r\n        \"@babel/helper-validator-identifier\": \"^7.29.7\",\r\n        \"@babel/traverse\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-optimise-call-expression\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-plugin-utils\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-replace-supers\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-member-expression-to-functions\": \"^7.29.7\",\r\n        \"@babel/helper-optimise-call-expression\": \"^7.29.7\",\r\n        \"@babel/traverse\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-skip-transparent-expression-wrappers\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/traverse\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-string-parser\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-validator-identifier\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helper-validator-option\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/helpers\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/template\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/parser\": {\r\n      \"version\": \"7.29.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz\",\r\n      \"integrity\": \"sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/types\": \"^7.29.8\"\r\n      },\r\n      \"bin\": {\r\n        \"parser\": \"bin/babel-parser.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/plugin-syntax-jsx\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-plugin-utils\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0-0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/plugin-syntax-typescript\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-plugin-utils\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0-0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/plugin-transform-modules-commonjs\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-module-transforms\": \"^7.29.7\",\r\n        \"@babel/helper-plugin-utils\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0-0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/plugin-transform-typescript\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-annotate-as-pure\": \"^7.29.7\",\r\n        \"@babel/helper-create-class-features-plugin\": \"^7.29.7\",\r\n        \"@babel/helper-plugin-utils\": \"^7.29.7\",\r\n        \"@babel/helper-skip-transparent-expression-wrappers\": \"^7.29.7\",\r\n        \"@babel/plugin-syntax-typescript\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0-0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/preset-typescript\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-plugin-utils\": \"^7.29.7\",\r\n        \"@babel/helper-validator-option\": \"^7.29.7\",\r\n        \"@babel/plugin-syntax-jsx\": \"^7.29.7\",\r\n        \"@babel/plugin-transform-modules-commonjs\": \"^7.29.7\",\r\n        \"@babel/plugin-transform-typescript\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@babel/core\": \"^7.0.0-0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/runtime\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/template\": {\r\n      \"version\": \"7.29.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz\",\r\n      \"integrity\": \"sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/code-frame\": \"^7.29.7\",\r\n        \"@babel/parser\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/traverse\": {\r\n      \"version\": \"7.29.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz\",\r\n      \"integrity\": \"sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/code-frame\": \"^7.29.7\",\r\n        \"@babel/generator\": \"^7.29.8\",\r\n        \"@babel/helper-globals\": \"^7.29.7\",\r\n        \"@babel/parser\": \"^7.29.8\",\r\n        \"@babel/template\": \"^7.29.7\",\r\n        \"@babel/types\": \"^7.29.8\",\r\n        \"debug\": \"^4.3.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@babel/types\": {\r\n      \"version\": \"7.29.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz\",\r\n      \"integrity\": \"sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/helper-string-parser\": \"^7.29.7\",\r\n        \"@babel/helper-validator-identifier\": \"^7.29.7\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@base-ui/react\": {\r\n      \"version\": \"1.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@base-ui/react/-/react-1.7.0.tgz\",\r\n      \"integrity\": \"sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/runtime\": \"^7.29.2\",\r\n        \"@base-ui/utils\": \"0.3.2\",\r\n        \"@floating-ui/react-dom\": \"^2.1.9\",\r\n        \"@floating-ui/utils\": \"^0.2.12\",\r\n        \"use-sync-external-store\": \"^1.6.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/mui-org\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@date-fns/tz\": \"^1.2.0\",\r\n        \"@types/react\": \"^17 || ^18 || ^19\",\r\n        \"date-fns\": \"^4.0.0\",\r\n        \"react\": \"^17 || ^18 || ^19\",\r\n        \"react-dom\": \"^17 || ^18 || ^19\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@date-fns/tz\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"date-fns\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@base-ui/utils\": {\r\n      \"version\": \"0.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@base-ui/utils/-/utils-0.3.2.tgz\",\r\n      \"integrity\": \"sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/runtime\": \"^7.29.2\",\r\n        \"@floating-ui/utils\": \"^0.2.12\",\r\n        \"reselect\": \"^5.2.0\",\r\n        \"use-sync-external-store\": \"^1.6.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"^17 || ^18 || ^19\",\r\n        \"react\": \"^17 || ^18 || ^19\",\r\n        \"react-dom\": \"^17 || ^18 || ^19\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/kv-asset-handler\": {\r\n      \"version\": \"0.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz\",\r\n      \"integrity\": \"sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/unenv-preset\": {\r\n      \"version\": \"2.16.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz\",\r\n      \"integrity\": \"sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"peerDependencies\": {\r\n        \"unenv\": \"2.0.0-rc.24\",\r\n        \"workerd\": \">1.20260305.0 <2.0.0-0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"workerd\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/vite-plugin\": {\r\n      \"version\": \"1.37.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/vite-plugin/-/vite-plugin-1.37.1.tgz\",\r\n      \"integrity\": \"sha512-FldhsmkXyLsX3yI+p0aJDo/kGyY69pDdynz2JjcuH3eCiQ+G9XaY3CrN+UYEfCbWv8CiR2wL95lNg0fT/HfwWA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@cloudflare/unenv-preset\": \"2.16.1\",\r\n        \"miniflare\": \"4.20260515.0\",\r\n        \"unenv\": \"2.0.0-rc.24\",\r\n        \"wrangler\": \"4.92.0\",\r\n        \"ws\": \"8.18.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"vite\": \"^6.1.0 || ^7.0.0 || ^8.0.0\",\r\n        \"wrangler\": \"^4.92.0\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-darwin-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-Wtw44el2pNbzixvTkWdfeBDTrQwQbJRz7/JUvPKV27I0pQWXbhNJPpM8cstq/pbrU5AGcA/HjFH6yPMRTIRKig==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-darwin-arm64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-X8EqkZej6FfmhF9AVAQ3FhyQRr9acS4RcDunMU2YiuxKHF1IU8zzH3vY30/POaG+rUu9vGDp/VgUl49VPenHJQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-linux-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-CDC89QxQ7Y7t7RG1Jd9vj/qolE1sQRkI2OSEuV5BMJi0vW/gV4OVG6xjpdK3b1OYnSWDzF7NpvlR5Yg86q7k4g==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-linux-arm64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-WxbW/PToYES4fvHXzsr/5qOiETQs/Z9iZ0mjSZAiEwq5cMLZemzGN0COx+uFb9OvQwzh6Pg159qPFnw3+i9FuA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-windows-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-WmV/iv+MHjYsvkcMVzpM2B5/mf06UUkdpVhZrtMfV9graWjBGPYFvE/eab8748RPVGKh1Xe1vXofLzDSwc08lA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workers-types\": {\r\n      \"version\": \"4.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-ruz+9tcG2iIKq5mXU2F00lL6KF5d4QfbAk7fNgvvqf5BelxDXaieyZmSnvA4sQBuemUstt4AEDN9CmJKmfCsDw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\"\r\n    },\r\n    \"node_modules/@cspotcode/source-map-support\": {\r\n      \"version\": \"0.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz\",\r\n      \"integrity\": \"sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/trace-mapping\": \"0.3.9\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping\": {\r\n      \"version\": \"0.3.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz\",\r\n      \"integrity\": \"sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/resolve-uri\": \"^3.0.3\",\r\n        \"@jridgewell/sourcemap-codec\": \"^1.4.10\"\r\n      }\r\n    },\r\n    \"node_modules/@date-fns/tz\": {\r\n      \"version\": \"1.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz\",\r\n      \"integrity\": \"sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@dimforge/rapier3d-compat\": {\r\n      \"version\": \"0.12.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz\",\r\n      \"integrity\": \"sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx\": {\r\n      \"version\": \"1.75.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.75.1.tgz\",\r\n      \"integrity\": \"sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"@dotenvx/primitives\": \"^0.8.0\",\r\n        \"commander\": \"^11.1.0\",\r\n        \"conf\": \"^10.2.0\",\r\n        \"dotenv\": \"^17.2.1\",\r\n        \"enquirer\": \"^2.4.1\",\r\n        \"env-paths\": \"^2.2.1\",\r\n        \"execa\": \"^5.1.1\",\r\n        \"fdir\": \"^6.2.0\",\r\n        \"ignore\": \"^5.3.0\",\r\n        \"object-treeify\": \"1.1.33\",\r\n        \"open\": \"^8.4.2\",\r\n        \"picomatch\": \"^4.0.4\",\r\n        \"systeminformation\": \"^5.22.11\",\r\n        \"undici\": \"^7.11.0\",\r\n        \"which\": \"^4.0.0\",\r\n        \"yocto-spinner\": \"^1.1.0\"\r\n      },\r\n      \"bin\": {\r\n        \"dotenvx\": \"src/cli/dotenvx.js\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://dotenvx.com\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/commander\": {\r\n      \"version\": \"11.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/commander/-/commander-11.1.0.tgz\",\r\n      \"integrity\": \"sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/define-lazy-prop\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/execa\": {\r\n      \"version\": \"5.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/execa/-/execa-5.1.1.tgz\",\r\n      \"integrity\": \"sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"cross-spawn\": \"^7.0.3\",\r\n        \"get-stream\": \"^6.0.0\",\r\n        \"human-signals\": \"^2.1.0\",\r\n        \"is-stream\": \"^2.0.0\",\r\n        \"merge-stream\": \"^2.0.0\",\r\n        \"npm-run-path\": \"^4.0.1\",\r\n        \"onetime\": \"^5.1.2\",\r\n        \"signal-exit\": \"^3.0.3\",\r\n        \"strip-final-newline\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sindresorhus/execa?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/get-stream\": {\r\n      \"version\": \"6.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz\",\r\n      \"integrity\": \"sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/human-signals\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=10.17.0\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/is-docker\": {\r\n      \"version\": \"2.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz\",\r\n      \"integrity\": \"sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"is-docker\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/is-stream\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/is-wsl\": {\r\n      \"version\": \"2.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz\",\r\n      \"integrity\": \"sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-docker\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/npm-run-path\": {\r\n      \"version\": \"4.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz\",\r\n      \"integrity\": \"sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"path-key\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/open\": {\r\n      \"version\": \"8.4.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/open/-/open-8.4.2.tgz\",\r\n      \"integrity\": \"sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"define-lazy-prop\": \"^2.0.0\",\r\n        \"is-docker\": \"^2.1.1\",\r\n        \"is-wsl\": \"^2.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/signal-exit\": {\r\n      \"version\": \"3.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz\",\r\n      \"integrity\": \"sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/@dotenvx/dotenvx/node_modules/strip-final-newline\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/@dotenvx/primitives\": {\r\n      \"version\": \"0.8.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@dotenvx/primitives/-/primitives-0.8.0.tgz\",\r\n      \"integrity\": \"sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==\",\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@emnapi/core\": {\r\n      \"version\": \"1.10.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz\",\r\n      \"integrity\": \"sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/wasi-threads\": \"1.2.1\",\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@emnapi/runtime\": {\r\n      \"version\": \"1.11.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz\",\r\n      \"integrity\": \"sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@emnapi/wasi-threads\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/aix-ppc64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"aix\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-arm\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/darwin-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/darwin-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/freebsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/freebsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-arm\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-ia32\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-loong64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==\",\r\n      \"cpu\": [\r\n        \"loong64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-mips64el\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==\",\r\n      \"cpu\": [\r\n        \"mips64el\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-ppc64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-riscv64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-s390x\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/netbsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/netbsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openbsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openbsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openharmony-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/sunos-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"sunos\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-ia32\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@floating-ui/core\": {\r\n      \"version\": \"1.8.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz\",\r\n      \"integrity\": \"sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@floating-ui/utils\": \"^0.2.12\"\r\n      }\r\n    },\r\n    \"node_modules/@floating-ui/dom\": {\r\n      \"version\": \"1.8.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz\",\r\n      \"integrity\": \"sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@floating-ui/core\": \"^1.8.0\",\r\n        \"@floating-ui/utils\": \"^0.2.12\"\r\n      }\r\n    },\r\n    \"node_modules/@floating-ui/react-dom\": {\r\n      \"version\": \"2.1.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz\",\r\n      \"integrity\": \"sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@floating-ui/dom\": \"^1.8.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \">=16.8.0\",\r\n        \"react-dom\": \">=16.8.0\"\r\n      }\r\n    },\r\n    \"node_modules/@floating-ui/utils\": {\r\n      \"version\": \"0.2.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz\",\r\n      \"integrity\": \"sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@hono/node-server\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"hono\": \"^4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/colour\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-darwin-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-darwin-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-darwin-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-darwin-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-darwin-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-darwin-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-arm\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-ppc64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-riscv64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-s390x\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linuxmusl-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linuxmusl-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-arm\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-arm\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-ppc64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-ppc64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-riscv64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-riscv64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-s390x\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-s390x\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linuxmusl-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linuxmusl-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linuxmusl-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linuxmusl-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-wasm32\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==\",\r\n      \"cpu\": [\r\n        \"wasm32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later AND MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/runtime\": \"^1.7.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-ia32\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/gen-mapping\": {\r\n      \"version\": \"0.3.13\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz\",\r\n      \"integrity\": \"sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/sourcemap-codec\": \"^1.5.0\",\r\n        \"@jridgewell/trace-mapping\": \"^0.3.24\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/remapping\": {\r\n      \"version\": \"2.3.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz\",\r\n      \"integrity\": \"sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/gen-mapping\": \"^0.3.5\",\r\n        \"@jridgewell/trace-mapping\": \"^0.3.24\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/resolve-uri\": {\r\n      \"version\": \"3.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz\",\r\n      \"integrity\": \"sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/source-map\": {\r\n      \"version\": \"0.3.11\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz\",\r\n      \"integrity\": \"sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@jridgewell/gen-mapping\": \"^0.3.5\",\r\n        \"@jridgewell/trace-mapping\": \"^0.3.25\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/sourcemap-codec\": {\r\n      \"version\": \"1.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz\",\r\n      \"integrity\": \"sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@jridgewell/trace-mapping\": {\r\n      \"version\": \"0.3.31\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz\",\r\n      \"integrity\": \"sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/resolve-uri\": \"^3.1.0\",\r\n        \"@jridgewell/sourcemap-codec\": \"^1.4.14\"\r\n      }\r\n    },\r\n    \"node_modules/@modelcontextprotocol/sdk\": {\r\n      \"version\": \"1.30.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz\",\r\n      \"integrity\": \"sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@hono/node-server\": \"^1.19.9 || ^2.0.5\",\r\n        \"ajv\": \"^8.17.1\",\r\n        \"ajv-formats\": \"^3.0.1\",\r\n        \"content-type\": \"^1.0.5\",\r\n        \"cors\": \"^2.8.5\",\r\n        \"cross-spawn\": \"^7.0.5\",\r\n        \"eventsource\": \"^3.0.2\",\r\n        \"eventsource-parser\": \"^3.0.0\",\r\n        \"express\": \"^5.2.1\",\r\n        \"express-rate-limit\": \"^8.2.1\",\r\n        \"hono\": \"^4.11.4\",\r\n        \"jose\": \"^6.1.3\",\r\n        \"json-schema-typed\": \"^8.0.2\",\r\n        \"pkce-challenge\": \"^5.0.0\",\r\n        \"raw-body\": \"^3.0.0\",\r\n        \"zod\": \"^3.25 || ^4.0\",\r\n        \"zod-to-json-schema\": \"^3.25.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@cfworker/json-schema\": \"^4.1.1\",\r\n        \"zod\": \"^3.25 || ^4.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@cfworker/json-schema\": {\r\n          \"optional\": true\r\n        },\r\n        \"zod\": {\r\n          \"optional\": false\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@napi-rs/wasm-runtime\": {\r\n      \"version\": \"1.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz\",\r\n      \"integrity\": \"sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@tybys/wasm-util\": \"^0.10.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || ^22.13.0 || >=23.5.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/Brooooooklyn\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@emnapi/core\": \"^1.7.1 || ^2.0.0-alpha.4\",\r\n        \"@emnapi/runtime\": \"^1.7.1 || ^2.0.0-alpha.4\"\r\n      }\r\n    },\r\n    \"node_modules/@nodelib/fs.scandir\": {\r\n      \"version\": \"2.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz\",\r\n      \"integrity\": \"sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@nodelib/fs.stat\": \"2.0.5\",\r\n        \"run-parallel\": \"^1.1.9\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/@nodelib/fs.stat\": {\r\n      \"version\": \"2.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz\",\r\n      \"integrity\": \"sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/@nodelib/fs.walk\": {\r\n      \"version\": \"1.2.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz\",\r\n      \"integrity\": \"sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@nodelib/fs.scandir\": \"2.1.5\",\r\n        \"fastq\": \"^1.6.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/@openai/sites-vite-plugin\": {\r\n      \"version\": \"0.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@openai/sites-vite-plugin/-/sites-vite-plugin-0.2.0.tgz\",\r\n      \"integrity\": \"sha512-4ThLMW9eaf2gd+SqBwHmqBONKBAbQ2BuOFev7mHq7Y2W5mm/rfVJOSjcgQIsY2PqdXxXNZ4Yd0+s0DlSUYehag==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=22.13.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"vite\": \"^8.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxc-project/types\": {\r\n      \"version\": \"0.130.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz\",\r\n      \"integrity\": \"sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/Boshen\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-android-arm-eabi\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-BaS+1OVvg9sr+Xav0+KdWedQRcAzrdoEcwMZeqoc2F6ieC1s/t5eM35YQoRPQ7vAqkZ+p3tbQb1r9I9mrV5oGA==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-android-arm64\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-of8atAV0M1egGcVOMbgZCvc10sFOP3ayQBNQV5h5G3fNq8gACdEswfFk9bzGrdbM23rtg0Coxi7np7oPLcueNw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-darwin-arm64\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-7l8+5ov4BGwtAcmpzvEik/TG3bciwyw/S3e6j5GKH7pcQqcgCVxD3AuJeP6upto+SOTBKQ4wrrdbMt0gq8fHSQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-darwin-x64\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-Fnz4dDDXBb7udk+DmwelNjxbD6yptyxwCqwCH2ebo4RVLxVsRfFsn/AHJC49KIltPrVokamGv4SSOsiV50DTxQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-freebsd-x64\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-mddOebKNCP+AucmzfNsk3jgbr681qAUvgMqi865GW5gWLJ/AnzXbvjQRrny0e++NAN8aphav/aRSrfFxNsNjpA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-arm-gnueabihf\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-svx59iYL+DbaZGZUIoice4W0CjRXGExnbz7Re+awIb60rVxBS2KrU7Hnlx+nZYanLGLpjneUEgo/VFEKkSZAyQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-arm-musleabihf\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-BYK9MPJPCf6d+fLKMTruThmEyCtHzQ1zLcsrTlUVkmnoXIaHAbfpeLYQwX1tkjs7W11dyzoi6HFvKcdnvX1zNg==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-arm64-gnu\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-QUaCNLq2/EC6G5ljOuFanl9Lgw6ZWp4co7rs4+KOMUzbGfA4Lq58FHRjjF9sVIG+93XSbo343MxFATrOU1qctA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-arm64-musl\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-S6uvJ6MXnRXl+zTs0CARNDvkE+cymj0EVWEKKsyKnlLlqTyQJBjw5s4D2pSIOZc+S46cy4STefzcr/sm0VzVPA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-ppc64-gnu\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-6VDlRcytvZG6UlSIdAFKDLbppo9tvPxrWzle6vHldYFMeuDPQEfMKrkwezp7FaBq1wik9ra554ZZeRPsyIkFpg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-riscv64-gnu\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-KkBTYbzExpbmn15XjKPLu2fRV2PVlq+KWt+brad5rwIa03vdYoaDRWiS7raHII/dCTR6Ro4UpYUCH4t6lif4WQ==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-riscv64-musl\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-69tzIq7sJLVB9dxYYtvMzcSSsnZHSO+U2U19O2RqDqgj6+Q4O7HjSXdaszbcgqzhsUwzSH7z5kWvk8nmf6BHTg==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-s390x-gnu\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-Oqi/N0OvtOVXsPKAOOhKgGH3msRYF8BLJaNBbWiupRiKoKVyc8JRKPCfarkQJC+RgP9U8raUKLe+bNwd0HUMiA==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-x64-gnu\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-3TKwv/ed4uwJSemAA8P9XcoqETpjQI4waquF9UilhA9Mn/dhr1PdUEXWlL74mtc6ZNfmKPA9+NEJm01nRF8CVA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-linux-x64-musl\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-uFso4u4nLkVSlMCpgjyvWV60Gt7GvDQHnk1mmRxHIkZTMB0ljpUKwCD9FYGgN9H97x2wYl0UwEjgRZaPIuhEhw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-openharmony-arm64\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-keGLkzeOvkMpNmPp4hffXWpfoSsY6e1K8++KXD4mSSfxdvM8q9QUDsYY689TB1k6Co832DZn1MnaaVx6cIBMWQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-win32-arm64-msvc\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-VzsAISkFxmNhJ5LBDEL9VuH6tJsVJMtqYit2LyIUf/HLnsCe4Pg9SMOjjVQzGWt0bnpyfJ94CrqTqcpNZzK+ug==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-win32-ia32-msvc\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-xv4t7yzwJoYaLB6Zv28B3W+j7brEjsyv50rLTAQgmxJzddce9fAMCxed8dSAkbWES0zz2J29nYK5FaTuD2YBHg==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxfmt/binding-win32-x64-msvc\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-6EZXFkqOwxdDYjIn3TSNnPk3ST5E5GiYd4FiM6UF/mCL/LZSfr6D6UygTfW3R1PCQP2quCKpCEGRlij8E3VYbg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/darwin-arm64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/darwin-x64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/linux-arm64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/linux-x64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/win32-arm64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint-tsgolint/win32-x64\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ]\r\n    },\r\n    \"node_modules/@oxlint/binding-android-arm-eabi\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-ZHIE5Zt9AsPDcY4nOlofXt0YfneEeo+QrKMPcPzLf2Z6Q8VtV2W73d7SFJ920WUwyik783u/doKCs3KXdwG+7w==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-android-arm64\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-shm/ngQilHK6bs+ElJWa4oHfNj5vL1Gl/iVEJldTQjpr0/67oSgr0KUpbmcnLig5Fo0v/l6j2567A7TOL89ONA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-darwin-arm64\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-rvJmrAPKSQ9aWJ6wIS6CK2tJjwzfW0ApQH9qokq6sfDvmHwoyIHxHFMq7z7i7GiV6fdE6s8qvBqWKPTu8RmT6Q==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-darwin-x64\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-U/zYdb7VYKGY6pA9Vd2rYl9O/HlCylcOlb5PGPvVLtg+oLGsk6H3XGKEMHKyqD3nmmtmlmwb/8SwU2vfSAtvMw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-freebsd-x64\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-WvKG9CAriuo0XNiFzpXjDngUZcRGFNpaK2kLyMUsnJlShxkT96u+BpJQ3KqdQwGOrvI14L6V8bAwXwAYNNY6Jg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-qJ5+RH99TqFRq3UCDxkW0zJJu9c+OAHFY72vGlxZLEpuO+MpKo3POgqb8sYipL9KYm8XY6ofb0HsOuvY6hQNqQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-arm-musleabihf\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-PvPCVptkgVARsucgIqFQQcSmJ6xc6GtnVB5bRBekRahTc9eObMtjHfMjy5M+C2tHt5UCMttWM9RuSk/H9NqYeg==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-arm64-gnu\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-3KeFDx8Bu4HPAXbuHZOr/oHvN+QT+JQhMw/NYPz7Z071xLSsG27Jfh9PIQVEY7hk1I+jr43ExqRIeJ6VKk2yLw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-arm64-musl\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-oPFkkKTgl0K/EIg9fQ8oA3IGcI05/Mq1en04iFa41mmNPT+6KEiByVazTOZZJiHMBBrbsns1YJ2e1Scqwzesjw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-ppc64-gnu\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-gN7yZ0eqflA5Fhf1wvHxGUltIV3FsvmB1zhNMDEK9vSHhc7E6qg9CuPeBgPZab66Tjzq6w6kHAtNEvnTHf4cyw==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-riscv64-gnu\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-S/HqMbn22mQrjtErUxEoS/a55u8kIeXvreIxiJu5G7Le3UecEd6SQZxrDIpuhtgaFnsY/nVra3ytP+pRljDilA==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-riscv64-musl\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-ZIga3097VJZolGZk6SrIAUokIGfRkxRlhiHDUznZptGBfwrhD7pNfD1rzEzsCwvk/1DX0A1bLz+liuNh5QKIVQ==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-s390x-gnu\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-ZGiiA7pFzMJSyMWYZTVlPgbTsx+Vl8ihLGMIujPwaslUF7kIPPWAbVmAlTc+9lWDV+DCiB8Ikixu+lSHeOIIWQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-x64-gnu\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-JLiy5WuvEBFTT6ErIFV35SLzi0R7Iri6MKU6dZbTxfIx8pndbbPs3Mj780nMipBFcPkti+okAPOJ9POKkHFEgg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-linux-x64-musl\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-z7lgKQtbo/I1NIe8G5NHLesxJDv0tRSUWTpXKb9Pm3E9nKFKfO4IOSDtFroKgXtOYb0jQbcdH+0wzTyMXVes+A==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-openharmony-arm64\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-JOjKymIpb9QcYfEhZsN6h4V9Ivd474W38cNIBRv6bg2TbIvogbMTH0Mg6YWW9TiRDqfcX+/Hyfsbo5vcSE5guQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-win32-arm64-msvc\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-pqDWZiwcmByWUEm1NFUBNiT6aentCcaoMWJv0HbXEmuYermJ4sg8ppVrshubYP2MZ6SHccJJcpr6x469PuDFIw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-win32-ia32-msvc\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-Ba0O659kgMv6pwO3z9PdO+K3aMxQRaw9HnG+e6AtOfgwcKFvYilciQYBoUBmxfQvOCKZe1SwjMkuB542NkuDMQ==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxlint/binding-win32-x64-msvc\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-5qcirPHO8nKfkoowEVWtpAoVTcYDy6g0UT0NGic450Qv8J2NrOqg4uQ8QppRP4MDTC7Xx47lbZnmadTH03CGGA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/colors\": {\r\n      \"version\": \"4.1.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz\",\r\n      \"integrity\": \"sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"kleur\": \"^4.1.5\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/dumper\": {\r\n      \"version\": \"0.6.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz\",\r\n      \"integrity\": \"sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/colors\": \"^4.1.5\",\r\n        \"@sindresorhus/is\": \"^7.0.2\",\r\n        \"supports-color\": \"^10.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/dumper/node_modules/supports-color\": {\r\n      \"version\": \"10.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz\",\r\n      \"integrity\": \"sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/supports-color?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/exception\": {\r\n      \"version\": \"1.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz\",\r\n      \"integrity\": \"sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@radix-ui/primitive\": {\r\n      \"version\": \"1.1.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz\",\r\n      \"integrity\": \"sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@radix-ui/react-compose-refs\": {\r\n      \"version\": \"1.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz\",\r\n      \"integrity\": \"sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-context\": {\r\n      \"version\": \"1.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz\",\r\n      \"integrity\": \"sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-dialog\": {\r\n      \"version\": \"1.1.23\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz\",\r\n      \"integrity\": \"sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/primitive\": \"1.1.7\",\r\n        \"@radix-ui/react-compose-refs\": \"1.1.5\",\r\n        \"@radix-ui/react-context\": \"1.2.2\",\r\n        \"@radix-ui/react-dismissable-layer\": \"1.1.19\",\r\n        \"@radix-ui/react-focus-guards\": \"1.1.6\",\r\n        \"@radix-ui/react-focus-scope\": \"1.1.16\",\r\n        \"@radix-ui/react-id\": \"1.1.4\",\r\n        \"@radix-ui/react-portal\": \"1.1.17\",\r\n        \"@radix-ui/react-presence\": \"1.1.10\",\r\n        \"@radix-ui/react-primitive\": \"2.1.10\",\r\n        \"@radix-ui/react-slot\": \"1.3.3\",\r\n        \"@radix-ui/react-use-controllable-state\": \"1.2.6\",\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\",\r\n        \"aria-hidden\": \"^1.2.4\",\r\n        \"react-remove-scroll\": \"^2.7.2\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-dismissable-layer\": {\r\n      \"version\": \"1.1.19\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz\",\r\n      \"integrity\": \"sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/primitive\": \"1.1.7\",\r\n        \"@radix-ui/react-compose-refs\": \"1.1.5\",\r\n        \"@radix-ui/react-primitive\": \"2.1.10\",\r\n        \"@radix-ui/react-use-callback-ref\": \"1.1.4\",\r\n        \"@radix-ui/react-use-effect-event\": \"0.0.5\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-focus-guards\": {\r\n      \"version\": \"1.1.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz\",\r\n      \"integrity\": \"sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-focus-scope\": {\r\n      \"version\": \"1.1.16\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz\",\r\n      \"integrity\": \"sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-compose-refs\": \"1.1.5\",\r\n        \"@radix-ui/react-primitive\": \"2.1.10\",\r\n        \"@radix-ui/react-use-callback-ref\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-id\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-portal\": {\r\n      \"version\": \"1.1.17\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz\",\r\n      \"integrity\": \"sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-primitive\": \"2.1.10\",\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-presence\": {\r\n      \"version\": \"1.1.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz\",\r\n      \"integrity\": \"sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-primitive\": {\r\n      \"version\": \"2.1.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz\",\r\n      \"integrity\": \"sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-slot\": \"1.3.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"@types/react-dom\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-slot\": {\r\n      \"version\": \"1.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz\",\r\n      \"integrity\": \"sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-compose-refs\": \"1.1.5\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-use-callback-ref\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-use-controllable-state\": {\r\n      \"version\": \"1.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz\",\r\n      \"integrity\": \"sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/primitive\": \"1.1.7\",\r\n        \"@radix-ui/react-use-effect-event\": \"0.0.5\",\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-use-effect-event\": {\r\n      \"version\": \"0.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz\",\r\n      \"integrity\": \"sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-use-layout-effect\": \"1.1.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@radix-ui/react-use-layout-effect\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@reduxjs/toolkit\": {\r\n      \"version\": \"2.12.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz\",\r\n      \"integrity\": \"sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@standard-schema/spec\": \"^1.0.0\",\r\n        \"@standard-schema/utils\": \"^0.3.0\",\r\n        \"immer\": \"^11.0.0\",\r\n        \"redux\": \"^5.0.1\",\r\n        \"redux-thunk\": \"^3.1.0\",\r\n        \"reselect\": \"^5.1.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.9.0 || ^17.0.0 || ^18 || ^19\",\r\n        \"react-redux\": \"^7.2.1 || ^8.1.3 || ^9.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"react\": {\r\n          \"optional\": true\r\n        },\r\n        \"react-redux\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@reduxjs/toolkit/node_modules/immer\": {\r\n      \"version\": \"11.1.18\",\r\n      \"resolved\": \"https://registry.npmjs.org/immer/-/immer-11.1.18.tgz\",\r\n      \"integrity\": \"sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/immer\"\r\n      }\r\n    },\r\n    \"node_modules/@resvg/resvg-wasm\": {\r\n      \"version\": \"2.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.4.0.tgz\",\r\n      \"integrity\": \"sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==\",\r\n      \"license\": \"MPL-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">= 10\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-android-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-darwin-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-darwin-x64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-freebsd-x64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm64-musl\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-ppc64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-s390x-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-x64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-x64-musl\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-openharmony-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-wasm32-wasi\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==\",\r\n      \"cpu\": [\r\n        \"wasm32\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/core\": \"1.10.0\",\r\n        \"@emnapi/runtime\": \"1.10.0\",\r\n        \"@napi-rs/wasm-runtime\": \"^1.1.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime\": {\r\n      \"version\": \"1.10.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz\",\r\n      \"integrity\": \"sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-win32-arm64-msvc\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-win32-x64-msvc\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/pluginutils\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@sec-ant/readable-stream\": {\r\n      \"version\": \"0.4.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz\",\r\n      \"integrity\": \"sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@shadcn/react\": {\r\n      \"version\": \"0.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@shadcn/react/-/react-0.3.0.tgz\",\r\n      \"integrity\": \"sha512-iKN0NuYe850VDHlxEfvppFrpa7CMV3zArTHusXlaSmeFeQhohGbIqclv6WwPTs/44I8EkXQpcuRt6sXEVKkWqQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \">=19\",\r\n        \"react\": \">=19\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@shuding/opentype.js\": {\r\n      \"version\": \"1.4.0-beta.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz\",\r\n      \"integrity\": \"sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fflate\": \"^0.7.3\",\r\n        \"string.prototype.codepointat\": \"^0.2.1\"\r\n      },\r\n      \"bin\": {\r\n        \"ot\": \"bin/ot\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 8.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@sindresorhus/is\": {\r\n      \"version\": \"7.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz\",\r\n      \"integrity\": \"sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sindresorhus/is?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/@sindresorhus/merge-streams\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/@speed-highlight/core\": {\r\n      \"version\": \"1.2.24\",\r\n      \"resolved\": \"https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.24.tgz\",\r\n      \"integrity\": \"sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==\",\r\n      \"dev\": true,\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    \"node_modules/@standard-schema/spec\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@standard-schema/utils\": {\r\n      \"version\": \"0.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz\",\r\n      \"integrity\": \"sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@tailwindcss/node\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/remapping\": \"^2.3.5\",\r\n        \"enhanced-resolve\": \"^5.19.0\",\r\n        \"jiti\": \"^2.6.1\",\r\n        \"lightningcss\": \"1.31.1\",\r\n        \"magic-string\": \"^0.30.21\",\r\n        \"source-map-js\": \"^1.2.1\",\r\n        \"tailwindcss\": \"4.2.1\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@tailwindcss/oxide-android-arm64\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-darwin-arm64\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-darwin-x64\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-freebsd-x64\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-linux-arm-gnueabihf\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-linux-arm64-gnu\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-linux-arm64-musl\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-linux-x64-gnu\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-linux-x64-musl\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-wasm32-wasi\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-win32-arm64-msvc\": \"4.2.1\",\r\n        \"@tailwindcss/oxide-win32-x64-msvc\": \"4.2.1\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-android-arm64\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-darwin-arm64\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-darwin-x64\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-freebsd-x64\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-linux-arm64-gnu\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-linux-arm64-musl\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-linux-x64-gnu\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-linux-x64-musl\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-wasm32-wasi\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==\",\r\n      \"bundleDependencies\": [\r\n        \"@napi-rs/wasm-runtime\",\r\n        \"@emnapi/core\",\r\n        \"@emnapi/runtime\",\r\n        \"@tybys/wasm-util\",\r\n        \"@emnapi/wasi-threads\",\r\n        \"tslib\"\r\n      ],\r\n      \"cpu\": [\r\n        \"wasm32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/core\": \"^1.8.1\",\r\n        \"@emnapi/runtime\": \"^1.8.1\",\r\n        \"@emnapi/wasi-threads\": \"^1.1.0\",\r\n        \"@napi-rs/wasm-runtime\": \"^1.1.1\",\r\n        \"@tybys/wasm-util\": \"^0.10.1\",\r\n        \"tslib\": \"^2.8.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-win32-arm64-msvc\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/oxide-win32-x64-msvc\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 20\"\r\n      }\r\n    },\r\n    \"node_modules/@tailwindcss/postcss\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@alloc/quick-lru\": \"^5.2.0\",\r\n        \"@tailwindcss/node\": \"4.2.1\",\r\n        \"@tailwindcss/oxide\": \"4.2.1\",\r\n        \"postcss\": \"^8.5.6\",\r\n        \"tailwindcss\": \"4.2.1\"\r\n      }\r\n    },\r\n    \"node_modules/@ts-morph/common\": {\r\n      \"version\": \"0.27.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz\",\r\n      \"integrity\": \"sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fast-glob\": \"^3.3.3\",\r\n        \"minimatch\": \"^10.0.1\",\r\n        \"path-browserify\": \"^1.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/@tweenjs/tween.js\": {\r\n      \"version\": \"23.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz\",\r\n      \"integrity\": \"sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@tybys/wasm-util\": {\r\n      \"version\": \"0.10.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz\",\r\n      \"integrity\": \"sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==\",\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@types/d3-array\": {\r\n      \"version\": \"3.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz\",\r\n      \"integrity\": \"sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/d3-color\": {\r\n      \"version\": \"3.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz\",\r\n      \"integrity\": \"sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/d3-ease\": {\r\n      \"version\": \"3.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz\",\r\n      \"integrity\": \"sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/d3-interpolate\": {\r\n      \"version\": \"3.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz\",\r\n      \"integrity\": \"sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@types/d3-color\": \"*\"\r\n      }\r\n    },\r\n    \"node_modules/@types/d3-path\": {\r\n      \"version\": \"3.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz\",\r\n      \"integrity\": \"sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/d3-scale\": {\r\n      \"version\": \"4.0.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz\",\r\n      \"integrity\": \"sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@types/d3-time\": \"*\"\r\n      }\r\n    },\r\n    \"node_modules/@types/d3-shape\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@types/d3-path\": \"*\"\r\n      }\r\n    },\r\n    \"node_modules/@types/d3-time\": {\r\n      \"version\": \"3.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz\",\r\n      \"integrity\": \"sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/d3-timer\": {\r\n      \"version\": \"3.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz\",\r\n      \"integrity\": \"sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/estree\": {\r\n      \"version\": \"1.0.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz\",\r\n      \"integrity\": \"sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/json-schema\": {\r\n      \"version\": \"7.0.15\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz\",\r\n      \"integrity\": \"sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@types/node\": {\r\n      \"version\": \"22.19.19\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz\",\r\n      \"integrity\": \"sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"undici-types\": \"~6.21.0\"\r\n      }\r\n    },\r\n    \"node_modules/@types/react\": {\r\n      \"version\": \"19.2.14\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz\",\r\n      \"integrity\": \"sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"csstype\": \"^3.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/@types/react-dom\": {\r\n      \"version\": \"19.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz\",\r\n      \"integrity\": \"sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"^19.2.0\"\r\n      }\r\n    },\r\n    \"node_modules/@types/stats.js\": {\r\n      \"version\": \"0.17.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz\",\r\n      \"integrity\": \"sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/three\": {\r\n      \"version\": \"0.180.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/three/-/three-0.180.0.tgz\",\r\n      \"integrity\": \"sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@dimforge/rapier3d-compat\": \"~0.12.0\",\r\n        \"@tweenjs/tween.js\": \"~23.1.3\",\r\n        \"@types/stats.js\": \"*\",\r\n        \"@types/webxr\": \"*\",\r\n        \"@webgpu/types\": \"*\",\r\n        \"fflate\": \"~0.8.2\",\r\n        \"meshoptimizer\": \"~0.22.0\"\r\n      }\r\n    },\r\n    \"node_modules/@types/three/node_modules/fflate\": {\r\n      \"version\": \"0.8.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz\",\r\n      \"integrity\": \"sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/use-sync-external-store\": {\r\n      \"version\": \"0.0.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz\",\r\n      \"integrity\": \"sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/validate-npm-package-name\": {\r\n      \"version\": \"4.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-4.0.2.tgz\",\r\n      \"integrity\": \"sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@types/webxr\": {\r\n      \"version\": \"0.5.24\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz\",\r\n      \"integrity\": \"sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@unpic/core\": {\r\n      \"version\": \"1.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@unpic/core/-/core-1.0.3.tgz\",\r\n      \"integrity\": \"sha512-aum9YNVUGso7MjGLD0Rp/08kywCGLqZ03/q6VQBFFakDBOXWEc8D4kPGcZ8v5wEnGRex3lE+++bOuucBp3KJ/w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"unpic\": \"^4.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/@unpic/react\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@unpic/react/-/react-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-5RmRfELwTF8w+4zjtQGqjpvX+RU2VLvis3xDCS1O2uWk0PZN2cvatL+3/KAR3mshAuRrkFGTX1XwyAezSXaoCA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@unpic/core\": \"^1.0.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"next\": \"^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0\",\r\n        \"react\": \"^17.0.0 || ^18.0.0 || ^19.0.0\",\r\n        \"react-dom\": \"^17.0.0 || ^18.0.0 || ^19.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"next\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@vercel/og\": {\r\n      \"version\": \"0.8.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@vercel/og/-/og-0.8.6.tgz\",\r\n      \"integrity\": \"sha512-hBcWIOppZV14bi+eAmCZj8Elj8hVSUZJTpf1lgGBhVD85pervzQ1poM/qYfFUlPraYSZYP+ASg6To5BwYmUSGQ==\",\r\n      \"license\": \"MPL-2.0\",\r\n      \"dependencies\": {\r\n        \"@resvg/resvg-wasm\": \"2.4.0\",\r\n        \"satori\": \"0.16.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@vinext/types\": {\r\n      \"version\": \"1.0.0-beta.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@vinext/types/-/types-1.0.0-beta.2.tgz\",\r\n      \"integrity\": \"sha512-kbKY/H1RWlf7yox4G6Vwk1JeM6x0Tv1EAv28f3tDYkK3brq+89vp5eZHbdw3L/pZMbAhG4oFyhT9C976y+jkhQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=22\"\r\n      }\r\n    },\r\n    \"node_modules/@vitejs/plugin-react\": {\r\n      \"version\": \"6.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz\",\r\n      \"integrity\": \"sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@rolldown/pluginutils\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@rolldown/plugin-babel\": \"^0.1.7 || ^0.2.0\",\r\n        \"babel-plugin-react-compiler\": \"^1.0.0\",\r\n        \"vite\": \"^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@rolldown/plugin-babel\": {\r\n          \"optional\": true\r\n        },\r\n        \"babel-plugin-react-compiler\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@vitejs/plugin-rsc\": {\r\n      \"version\": \"0.5.26\",\r\n      \"resolved\": \"https://registry.npmjs.org/@vitejs/plugin-rsc/-/plugin-rsc-0.5.26.tgz\",\r\n      \"integrity\": \"sha512-T8W8ODEutblw9qXQB512LDPyv1tAbJRD/Gf0QEGsAoydl4nxEtIrghnhoI9oLY9R+7aw+cLk1ZEltxWHWf4aHw==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@rolldown/pluginutils\": \"1.0.0-rc.18\",\r\n        \"es-module-lexer\": \"^2.1.0\",\r\n        \"estree-walker\": \"^3.0.3\",\r\n        \"magic-string\": \"^0.30.21\",\r\n        \"srvx\": \"^0.11.15\",\r\n        \"strip-literal\": \"^3.1.0\",\r\n        \"turbo-stream\": \"^3.2.0\",\r\n        \"vitefu\": \"^1.1.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"*\",\r\n        \"react-dom\": \"*\",\r\n        \"react-server-dom-webpack\": \"*\",\r\n        \"vite\": \"*\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"react-server-dom-webpack\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@vitejs/plugin-rsc/node_modules/@rolldown/pluginutils\": {\r\n      \"version\": \"1.0.0-rc.18\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz\",\r\n      \"integrity\": \"sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@webassemblyjs/ast\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/helper-numbers\": \"1.13.2\",\r\n        \"@webassemblyjs/helper-wasm-bytecode\": \"1.13.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/floating-point-hex-parser\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@webassemblyjs/helper-api-error\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@webassemblyjs/helper-buffer\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@webassemblyjs/helper-numbers\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/floating-point-hex-parser\": \"1.13.2\",\r\n        \"@webassemblyjs/helper-api-error\": \"1.13.2\",\r\n        \"@xtuc/long\": \"4.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/helper-wasm-bytecode\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@webassemblyjs/helper-wasm-section\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-buffer\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-wasm-bytecode\": \"1.13.2\",\r\n        \"@webassemblyjs/wasm-gen\": \"1.14.1\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/ieee754\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@xtuc/ieee754\": \"^1.2.0\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/leb128\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@xtuc/long\": \"4.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/utf8\": {\r\n      \"version\": \"1.13.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz\",\r\n      \"integrity\": \"sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@webassemblyjs/wasm-edit\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-buffer\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-wasm-bytecode\": \"1.13.2\",\r\n        \"@webassemblyjs/helper-wasm-section\": \"1.14.1\",\r\n        \"@webassemblyjs/wasm-gen\": \"1.14.1\",\r\n        \"@webassemblyjs/wasm-opt\": \"1.14.1\",\r\n        \"@webassemblyjs/wasm-parser\": \"1.14.1\",\r\n        \"@webassemblyjs/wast-printer\": \"1.14.1\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/wasm-gen\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-wasm-bytecode\": \"1.13.2\",\r\n        \"@webassemblyjs/ieee754\": \"1.13.2\",\r\n        \"@webassemblyjs/leb128\": \"1.13.2\",\r\n        \"@webassemblyjs/utf8\": \"1.13.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/wasm-opt\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-buffer\": \"1.14.1\",\r\n        \"@webassemblyjs/wasm-gen\": \"1.14.1\",\r\n        \"@webassemblyjs/wasm-parser\": \"1.14.1\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/wasm-parser\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@webassemblyjs/helper-api-error\": \"1.13.2\",\r\n        \"@webassemblyjs/helper-wasm-bytecode\": \"1.13.2\",\r\n        \"@webassemblyjs/ieee754\": \"1.13.2\",\r\n        \"@webassemblyjs/leb128\": \"1.13.2\",\r\n        \"@webassemblyjs/utf8\": \"1.13.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webassemblyjs/wast-printer\": {\r\n      \"version\": \"1.14.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz\",\r\n      \"integrity\": \"sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@webassemblyjs/ast\": \"1.14.1\",\r\n        \"@xtuc/long\": \"4.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/@webgpu/types\": {\r\n      \"version\": \"0.1.72\",\r\n      \"resolved\": \"https://registry.npmjs.org/@webgpu/types/-/types-0.1.72.tgz\",\r\n      \"integrity\": \"sha512-0cF7RFM2edNoiIS1ODJp0/Gzv4/xSXhwoR0YCza+OWpJWtn4wmo9DvK91aLlH9+uUnwIriP7ZiC3WitmyhuzBw==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@xtuc/ieee754\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/@xtuc/long\": {\r\n      \"version\": \"4.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz\",\r\n      \"integrity\": \"sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/accepts\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mime-types\": \"^3.0.0\",\r\n        \"negotiator\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/acorn\": {\r\n      \"version\": \"8.18.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz\",\r\n      \"integrity\": \"sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"acorn\": \"bin/acorn\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/acorn-loose\": {\r\n      \"version\": \"8.5.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.5.2.tgz\",\r\n      \"integrity\": \"sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"acorn\": \"^8.15.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/ajv\": {\r\n      \"version\": \"8.20.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz\",\r\n      \"integrity\": \"sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fast-deep-equal\": \"^3.1.3\",\r\n        \"fast-uri\": \"^3.0.1\",\r\n        \"json-schema-traverse\": \"^1.0.0\",\r\n        \"require-from-string\": \"^2.0.2\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/epoberezkin\"\r\n      }\r\n    },\r\n    \"node_modules/ajv-formats\": {\r\n      \"version\": \"3.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz\",\r\n      \"integrity\": \"sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"ajv\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/ajv-keywords\": {\r\n      \"version\": \"5.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz\",\r\n      \"integrity\": \"sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"fast-deep-equal\": \"^3.1.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"ajv\": \"^8.8.2\"\r\n      }\r\n    },\r\n    \"node_modules/ansi-colors\": {\r\n      \"version\": \"4.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz\",\r\n      \"integrity\": \"sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/ansi-regex\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/argparse\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==\",\r\n      \"license\": \"Python-2.0\"\r\n    },\r\n    \"node_modules/aria-hidden\": {\r\n      \"version\": \"1.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz\",\r\n      \"integrity\": \"sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/ast-types\": {\r\n      \"version\": \"0.16.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/ast-types/-/ast-types-0.16.3.tgz\",\r\n      \"integrity\": \"sha512-FvWoWYfSCM6kRxCSH+MGLHIKKGRL6A6AW7Zek2O32REPQRdg131428uRTKMBYAeRd3XXAaHDS60Wpri7CdKDrA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/atomically\": {\r\n      \"version\": \"1.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz\",\r\n      \"integrity\": \"sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/balanced-match\": {\r\n      \"version\": \"4.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz\",\r\n      \"integrity\": \"sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \"18 || 20 || >=22\"\r\n      }\r\n    },\r\n    \"node_modules/base64-js\": {\r\n      \"version\": \"0.0.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz\",\r\n      \"integrity\": \"sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/baseline-browser-mapping\": {\r\n      \"version\": \"2.11.21\",\r\n      \"resolved\": \"https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz\",\r\n      \"integrity\": \"sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"bin\": {\r\n        \"baseline-browser-mapping\": \"dist/cli.cjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/blake3-wasm\": {\r\n      \"version\": \"2.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz\",\r\n      \"integrity\": \"sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/body-parser\": {\r\n      \"version\": \"2.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz\",\r\n      \"integrity\": \"sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"bytes\": \"^3.1.2\",\r\n        \"content-type\": \"^2.0.0\",\r\n        \"debug\": \"^4.4.3\",\r\n        \"http-errors\": \"^2.0.1\",\r\n        \"iconv-lite\": \"^0.7.2\",\r\n        \"on-finished\": \"^2.4.1\",\r\n        \"qs\": \"^6.15.2\",\r\n        \"raw-body\": \"^3.0.2\",\r\n        \"type-is\": \"^2.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/body-parser/node_modules/content-type\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/brace-expansion\": {\r\n      \"version\": \"5.0.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz\",\r\n      \"integrity\": \"sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"balanced-match\": \"^4.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"20 || >=22\"\r\n      }\r\n    },\r\n    \"node_modules/braces\": {\r\n      \"version\": \"3.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/braces/-/braces-3.0.3.tgz\",\r\n      \"integrity\": \"sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fill-range\": \"^7.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/browserslist\": {\r\n      \"version\": \"4.28.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz\",\r\n      \"integrity\": \"sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/browserslist\"\r\n        },\r\n        {\r\n          \"type\": \"tidelift\",\r\n          \"url\": \"https://tidelift.com/funding/github/npm/browserslist\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"baseline-browser-mapping\": \"^2.11.20\",\r\n        \"caniuse-lite\": \"^1.0.30001810\",\r\n        \"electron-to-chromium\": \"^1.5.420\",\r\n        \"node-releases\": \"^2.0.54\",\r\n        \"update-browserslist-db\": \"^1.3.2\"\r\n      },\r\n      \"bin\": {\r\n        \"browserslist\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7\"\r\n      }\r\n    },\r\n    \"node_modules/buffer-from\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/bundle-name\": {\r\n      \"version\": \"4.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz\",\r\n      \"integrity\": \"sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"run-applescript\": \"^7.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/bytes\": {\r\n      \"version\": \"3.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz\",\r\n      \"integrity\": \"sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/call-bind-apply-helpers\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"function-bind\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/call-bound\": {\r\n      \"version\": \"1.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz\",\r\n      \"integrity\": \"sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.2\",\r\n        \"get-intrinsic\": \"^1.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/callsites\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/camelize\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/caniuse-lite\": {\r\n      \"version\": \"1.0.30001810\",\r\n      \"resolved\": \"https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz\",\r\n      \"integrity\": \"sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/browserslist\"\r\n        },\r\n        {\r\n          \"type\": \"tidelift\",\r\n          \"url\": \"https://tidelift.com/funding/github/npm/caniuse-lite\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"CC-BY-4.0\"\r\n    },\r\n    \"node_modules/chalk\": {\r\n      \"version\": \"5.6.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz\",\r\n      \"integrity\": \"sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \"^12.17.0 || ^14.13 || >=16.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/chalk?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/chrome-trace-event\": {\r\n      \"version\": \"1.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz\",\r\n      \"integrity\": \"sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"engines\": {\r\n        \"node\": \">=6.0\"\r\n      }\r\n    },\r\n    \"node_modules/class-variance-authority\": {\r\n      \"version\": \"0.7.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz\",\r\n      \"integrity\": \"sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"clsx\": \"^2.1.1\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://polar.sh/cva\"\r\n      }\r\n    },\r\n    \"node_modules/cli-cursor\": {\r\n      \"version\": \"5.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz\",\r\n      \"integrity\": \"sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"restore-cursor\": \"^5.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/cli-spinners\": {\r\n      \"version\": \"2.9.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz\",\r\n      \"integrity\": \"sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/clsx\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/cmdk\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@radix-ui/react-compose-refs\": \"^1.1.1\",\r\n        \"@radix-ui/react-dialog\": \"^1.1.6\",\r\n        \"@radix-ui/react-id\": \"^1.1.0\",\r\n        \"@radix-ui/react-primitive\": \"^2.0.2\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^18 || ^19 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^18 || ^19 || ^19.0.0-rc\"\r\n      }\r\n    },\r\n    \"node_modules/code-block-writer\": {\r\n      \"version\": \"13.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz\",\r\n      \"integrity\": \"sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/color-name\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/commander\": {\r\n      \"version\": \"14.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/commander/-/commander-14.0.3.tgz\",\r\n      \"integrity\": \"sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      }\r\n    },\r\n    \"node_modules/conf\": {\r\n      \"version\": \"10.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/conf/-/conf-10.2.0.tgz\",\r\n      \"integrity\": \"sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ajv\": \"^8.6.3\",\r\n        \"ajv-formats\": \"^2.1.1\",\r\n        \"atomically\": \"^1.7.0\",\r\n        \"debounce-fn\": \"^4.0.0\",\r\n        \"dot-prop\": \"^6.0.1\",\r\n        \"env-paths\": \"^2.2.1\",\r\n        \"json-schema-typed\": \"^7.0.3\",\r\n        \"onetime\": \"^5.1.2\",\r\n        \"pkg-up\": \"^3.1.0\",\r\n        \"semver\": \"^7.3.5\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/conf/node_modules/ajv-formats\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"ajv\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/conf/node_modules/json-schema-typed\": {\r\n      \"version\": \"7.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz\",\r\n      \"integrity\": \"sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==\",\r\n      \"license\": \"BSD-2-Clause\"\r\n    },\r\n    \"node_modules/conf/node_modules/semver\": {\r\n      \"version\": \"7.8.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/semver/-/semver-7.8.5.tgz\",\r\n      \"integrity\": \"sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==\",\r\n      \"license\": \"ISC\",\r\n      \"bin\": {\r\n        \"semver\": \"bin/semver.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/content-disposition\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/content-type\": {\r\n      \"version\": \"1.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz\",\r\n      \"integrity\": \"sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/convert-source-map\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/cookie\": {\r\n      \"version\": \"0.7.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz\",\r\n      \"integrity\": \"sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/cookie-signature\": {\r\n      \"version\": \"1.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz\",\r\n      \"integrity\": \"sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.6.0\"\r\n      }\r\n    },\r\n    \"node_modules/cors\": {\r\n      \"version\": \"2.8.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/cors/-/cors-2.8.6.tgz\",\r\n      \"integrity\": \"sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"object-assign\": \"^4\",\r\n        \"vary\": \"^1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.10\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/cosmiconfig\": {\r\n      \"version\": \"9.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz\",\r\n      \"integrity\": \"sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"env-paths\": \"^2.2.1\",\r\n        \"import-fresh\": \"^3.3.0\",\r\n        \"js-yaml\": \"^4.1.0\",\r\n        \"parse-json\": \"^5.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/d-fischer\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"typescript\": \">=4.9.5\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"typescript\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/cross-spawn\": {\r\n      \"version\": \"7.0.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz\",\r\n      \"integrity\": \"sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"path-key\": \"^3.1.0\",\r\n        \"shebang-command\": \"^2.0.0\",\r\n        \"which\": \"^2.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/cross-spawn/node_modules/isexe\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/cross-spawn/node_modules/which\": {\r\n      \"version\": \"2.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/which/-/which-2.0.2.tgz\",\r\n      \"integrity\": \"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"isexe\": \"^2.0.0\"\r\n      },\r\n      \"bin\": {\r\n        \"node-which\": \"bin/node-which\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/css-background-parser\": {\r\n      \"version\": \"0.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz\",\r\n      \"integrity\": \"sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/css-box-shadow\": {\r\n      \"version\": \"1.0.0-3\",\r\n      \"resolved\": \"https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz\",\r\n      \"integrity\": \"sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/css-color-keywords\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/css-gradient-parser\": {\r\n      \"version\": \"0.0.16\",\r\n      \"resolved\": \"https://registry.npmjs.org/css-gradient-parser/-/css-gradient-parser-0.0.16.tgz\",\r\n      \"integrity\": \"sha512-3O5QdqgFRUbXvK1x5INf1YkBz1UKSWqrd63vWsum8MNHDBYD5urm3QtxZbKU259OrEXNM26lP/MPY3d1IGkBgA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/css-to-react-native\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"camelize\": \"^1.0.0\",\r\n        \"css-color-keywords\": \"^1.0.0\",\r\n        \"postcss-value-parser\": \"^4.0.2\"\r\n      }\r\n    },\r\n    \"node_modules/cssesc\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"cssesc\": \"bin/cssesc\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/csstype\": {\r\n      \"version\": \"3.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz\",\r\n      \"integrity\": \"sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/d3-array\": {\r\n      \"version\": \"3.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz\",\r\n      \"integrity\": \"sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"internmap\": \"1 - 2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-color\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-ease\": {\r\n      \"version\": \"3.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz\",\r\n      \"integrity\": \"sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-format\": {\r\n      \"version\": \"3.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz\",\r\n      \"integrity\": \"sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-interpolate\": {\r\n      \"version\": \"3.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz\",\r\n      \"integrity\": \"sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"d3-color\": \"1 - 3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-path\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-scale\": {\r\n      \"version\": \"4.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz\",\r\n      \"integrity\": \"sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"d3-array\": \"2.10.0 - 3\",\r\n        \"d3-format\": \"1 - 3\",\r\n        \"d3-interpolate\": \"1.2.0 - 3\",\r\n        \"d3-time\": \"2.1.1 - 3\",\r\n        \"d3-time-format\": \"2 - 4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-shape\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"d3-path\": \"^3.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-time\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"d3-array\": \"2 - 3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-time-format\": {\r\n      \"version\": \"4.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz\",\r\n      \"integrity\": \"sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"d3-time\": \"1 - 3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/d3-timer\": {\r\n      \"version\": \"3.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz\",\r\n      \"integrity\": \"sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/date-fns\": {\r\n      \"version\": \"4.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz\",\r\n      \"integrity\": \"sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/kossnocorp\"\r\n      }\r\n    },\r\n    \"node_modules/date-fns-jalali\": {\r\n      \"version\": \"4.1.0-0\",\r\n      \"resolved\": \"https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz\",\r\n      \"integrity\": \"sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/debounce-fn\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mimic-fn\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/debug\": {\r\n      \"version\": \"4.4.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\r\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ms\": \"^2.1.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"supports-color\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/decimal.js-light\": {\r\n      \"version\": \"2.5.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz\",\r\n      \"integrity\": \"sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/dedent\": {\r\n      \"version\": \"1.7.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz\",\r\n      \"integrity\": \"sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"babel-plugin-macros\": \"^3.1.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"babel-plugin-macros\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/deepmerge\": {\r\n      \"version\": \"4.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz\",\r\n      \"integrity\": \"sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/default-browser\": {\r\n      \"version\": \"5.5.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz\",\r\n      \"integrity\": \"sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"bundle-name\": \"^4.1.0\",\r\n        \"default-browser-id\": \"^5.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/default-browser-id\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/define-lazy-prop\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/depd\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/depd/-/depd-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/detect-libc\": {\r\n      \"version\": \"2.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz\",\r\n      \"integrity\": \"sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/detect-node-es\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/diff\": {\r\n      \"version\": \"8.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/diff/-/diff-8.0.4.tgz\",\r\n      \"integrity\": \"sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=0.3.1\"\r\n      }\r\n    },\r\n    \"node_modules/dot-prop\": {\r\n      \"version\": \"6.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz\",\r\n      \"integrity\": \"sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-obj\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/dotenv\": {\r\n      \"version\": \"17.4.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz\",\r\n      \"integrity\": \"sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==\",\r\n      \"license\": \"BSD-2-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://dotenvx.com\"\r\n      }\r\n    },\r\n    \"node_modules/dunder-proto\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.1\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"gopd\": \"^1.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/ee-first\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/electron-to-chromium\": {\r\n      \"version\": \"1.5.422\",\r\n      \"resolved\": \"https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz\",\r\n      \"integrity\": \"sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/embla-carousel\": {\r\n      \"version\": \"8.5.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.5.2.tgz\",\r\n      \"integrity\": \"sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/embla-carousel-react\": {\r\n      \"version\": \"8.5.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.5.2.tgz\",\r\n      \"integrity\": \"sha512-Tmx+uY3MqseIGdwp0ScyUuxpBgx5jX1f7od4Cm5mDwg/dptEiTKf9xp6tw0lZN2VA9JbnVMl/aikmbc53c6QFA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"embla-carousel\": \"8.5.2\",\r\n        \"embla-carousel-reactive-utils\": \"8.5.2\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      }\r\n    },\r\n    \"node_modules/embla-carousel-reactive-utils\": {\r\n      \"version\": \"8.5.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.5.2.tgz\",\r\n      \"integrity\": \"sha512-QC8/hYSK/pEmqEdU1IO5O+XNc/Ptmmq7uCB44vKplgLKhB/l0+yvYx0+Cv0sF6Ena8Srld5vUErZkT+yTahtDg==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"embla-carousel\": \"8.5.2\"\r\n      }\r\n    },\r\n    \"node_modules/emoji-regex\": {\r\n      \"version\": \"10.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz\",\r\n      \"integrity\": \"sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/emoji-regex-xs\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/encodeurl\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/enhanced-resolve\": {\r\n      \"version\": \"5.24.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz\",\r\n      \"integrity\": \"sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"graceful-fs\": \"^4.2.4\",\r\n        \"tapable\": \"^2.3.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10.13.0\"\r\n      }\r\n    },\r\n    \"node_modules/enquirer\": {\r\n      \"version\": \"2.4.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz\",\r\n      \"integrity\": \"sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-colors\": \"^4.1.1\",\r\n        \"strip-ansi\": \"^6.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8.6\"\r\n      }\r\n    },\r\n    \"node_modules/env-paths\": {\r\n      \"version\": \"2.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz\",\r\n      \"integrity\": \"sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/error-ex\": {\r\n      \"version\": \"1.3.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz\",\r\n      \"integrity\": \"sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-arrayish\": \"^0.2.1\"\r\n      }\r\n    },\r\n    \"node_modules/error-stack-parser-es\": {\r\n      \"version\": \"1.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz\",\r\n      \"integrity\": \"sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/antfu\"\r\n      }\r\n    },\r\n    \"node_modules/es-define-property\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-errors\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-module-lexer\": {\r\n      \"version\": \"2.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz\",\r\n      \"integrity\": \"sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/es-object-atoms\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-toolkit\": {\r\n      \"version\": \"1.52.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz\",\r\n      \"integrity\": \"sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==\",\r\n      \"license\": \"MIT\",\r\n      \"workspaces\": [\r\n        \"docs\",\r\n        \"benchmarks\",\r\n        \"tests/types\",\r\n        \"tests/browser-compat\"\r\n      ]\r\n    },\r\n    \"node_modules/esbuild\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==\",\r\n      \"devOptional\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"esbuild\": \"bin/esbuild\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@esbuild/aix-ppc64\": \"0.27.3\",\r\n        \"@esbuild/android-arm\": \"0.27.3\",\r\n        \"@esbuild/android-arm64\": \"0.27.3\",\r\n        \"@esbuild/android-x64\": \"0.27.3\",\r\n        \"@esbuild/darwin-arm64\": \"0.27.3\",\r\n        \"@esbuild/darwin-x64\": \"0.27.3\",\r\n        \"@esbuild/freebsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/freebsd-x64\": \"0.27.3\",\r\n        \"@esbuild/linux-arm\": \"0.27.3\",\r\n        \"@esbuild/linux-arm64\": \"0.27.3\",\r\n        \"@esbuild/linux-ia32\": \"0.27.3\",\r\n        \"@esbuild/linux-loong64\": \"0.27.3\",\r\n        \"@esbuild/linux-mips64el\": \"0.27.3\",\r\n        \"@esbuild/linux-ppc64\": \"0.27.3\",\r\n        \"@esbuild/linux-riscv64\": \"0.27.3\",\r\n        \"@esbuild/linux-s390x\": \"0.27.3\",\r\n        \"@esbuild/linux-x64\": \"0.27.3\",\r\n        \"@esbuild/netbsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/netbsd-x64\": \"0.27.3\",\r\n        \"@esbuild/openbsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/openbsd-x64\": \"0.27.3\",\r\n        \"@esbuild/openharmony-arm64\": \"0.27.3\",\r\n        \"@esbuild/sunos-x64\": \"0.27.3\",\r\n        \"@esbuild/win32-arm64\": \"0.27.3\",\r\n        \"@esbuild/win32-ia32\": \"0.27.3\",\r\n        \"@esbuild/win32-x64\": \"0.27.3\"\r\n      }\r\n    },\r\n    \"node_modules/escalade\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/escape-html\": {\r\n      \"version\": \"1.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz\",\r\n      \"integrity\": \"sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/esprima\": {\r\n      \"version\": \"4.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz\",\r\n      \"integrity\": \"sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==\",\r\n      \"license\": \"BSD-2-Clause\",\r\n      \"bin\": {\r\n        \"esparse\": \"bin/esparse.js\",\r\n        \"esvalidate\": \"bin/esvalidate.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/estree-walker\": {\r\n      \"version\": \"3.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz\",\r\n      \"integrity\": \"sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@types/estree\": \"^1.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/etag\": {\r\n      \"version\": \"1.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/etag/-/etag-1.8.1.tgz\",\r\n      \"integrity\": \"sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/eventemitter3\": {\r\n      \"version\": \"5.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz\",\r\n      \"integrity\": \"sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/events\": {\r\n      \"version\": \"3.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/events/-/events-3.3.0.tgz\",\r\n      \"integrity\": \"sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"engines\": {\r\n        \"node\": \">=0.8.x\"\r\n      }\r\n    },\r\n    \"node_modules/eventsource\": {\r\n      \"version\": \"3.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz\",\r\n      \"integrity\": \"sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"eventsource-parser\": \"^3.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/eventsource-parser\": {\r\n      \"version\": \"3.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz\",\r\n      \"integrity\": \"sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/execa\": {\r\n      \"version\": \"9.6.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/execa/-/execa-9.6.1.tgz\",\r\n      \"integrity\": \"sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@sindresorhus/merge-streams\": \"^4.0.0\",\r\n        \"cross-spawn\": \"^7.0.6\",\r\n        \"figures\": \"^6.1.0\",\r\n        \"get-stream\": \"^9.0.0\",\r\n        \"human-signals\": \"^8.0.1\",\r\n        \"is-plain-obj\": \"^4.1.0\",\r\n        \"is-stream\": \"^4.0.1\",\r\n        \"npm-run-path\": \"^6.0.0\",\r\n        \"pretty-ms\": \"^9.2.0\",\r\n        \"signal-exit\": \"^4.1.0\",\r\n        \"strip-final-newline\": \"^4.0.0\",\r\n        \"yoctocolors\": \"^2.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^18.19.0 || >=20.5.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sindresorhus/execa?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/express\": {\r\n      \"version\": \"5.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/express/-/express-5.2.1.tgz\",\r\n      \"integrity\": \"sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"accepts\": \"^2.0.0\",\r\n        \"body-parser\": \"^2.2.1\",\r\n        \"content-disposition\": \"^1.0.0\",\r\n        \"content-type\": \"^1.0.5\",\r\n        \"cookie\": \"^0.7.1\",\r\n        \"cookie-signature\": \"^1.2.1\",\r\n        \"debug\": \"^4.4.0\",\r\n        \"depd\": \"^2.0.0\",\r\n        \"encodeurl\": \"^2.0.0\",\r\n        \"escape-html\": \"^1.0.3\",\r\n        \"etag\": \"^1.8.1\",\r\n        \"finalhandler\": \"^2.1.0\",\r\n        \"fresh\": \"^2.0.0\",\r\n        \"http-errors\": \"^2.0.0\",\r\n        \"merge-descriptors\": \"^2.0.0\",\r\n        \"mime-types\": \"^3.0.0\",\r\n        \"on-finished\": \"^2.4.1\",\r\n        \"once\": \"^1.4.0\",\r\n        \"parseurl\": \"^1.3.3\",\r\n        \"proxy-addr\": \"^2.0.7\",\r\n        \"qs\": \"^6.14.0\",\r\n        \"range-parser\": \"^1.2.1\",\r\n        \"router\": \"^2.2.0\",\r\n        \"send\": \"^1.1.0\",\r\n        \"serve-static\": \"^2.2.0\",\r\n        \"statuses\": \"^2.0.1\",\r\n        \"type-is\": \"^2.0.1\",\r\n        \"vary\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/express-rate-limit\": {\r\n      \"version\": \"8.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz\",\r\n      \"integrity\": \"sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"debug\": \"^4.4.3\",\r\n        \"ip-address\": \"^10.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/express-rate-limit\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"express\": \">= 4.11\"\r\n      }\r\n    },\r\n    \"node_modules/fast-deep-equal\": {\r\n      \"version\": \"3.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz\",\r\n      \"integrity\": \"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/fast-glob\": {\r\n      \"version\": \"3.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz\",\r\n      \"integrity\": \"sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@nodelib/fs.stat\": \"^2.0.2\",\r\n        \"@nodelib/fs.walk\": \"^1.2.3\",\r\n        \"glob-parent\": \"^5.1.2\",\r\n        \"merge2\": \"^1.3.0\",\r\n        \"micromatch\": \"^4.0.8\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8.6.0\"\r\n      }\r\n    },\r\n    \"node_modules/fast-uri\": {\r\n      \"version\": \"3.1.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz\",\r\n      \"integrity\": \"sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/fastify\"\r\n        },\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/fastify\"\r\n        }\r\n      ],\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/fastq\": {\r\n      \"version\": \"1.20.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz\",\r\n      \"integrity\": \"sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"reusify\": \"^1.0.4\"\r\n      }\r\n    },\r\n    \"node_modules/fdir\": {\r\n      \"version\": \"6.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz\",\r\n      \"integrity\": \"sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"picomatch\": \"^3 || ^4\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"picomatch\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/fflate\": {\r\n      \"version\": \"0.7.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/fflate/-/fflate-0.7.5.tgz\",\r\n      \"integrity\": \"sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/figures\": {\r\n      \"version\": \"6.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/figures/-/figures-6.1.0.tgz\",\r\n      \"integrity\": \"sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-unicode-supported\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/fill-range\": {\r\n      \"version\": \"7.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz\",\r\n      \"integrity\": \"sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"to-regex-range\": \"^5.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/finalhandler\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"debug\": \"^4.4.0\",\r\n        \"encodeurl\": \"^2.0.0\",\r\n        \"escape-html\": \"^1.0.3\",\r\n        \"on-finished\": \"^2.4.1\",\r\n        \"parseurl\": \"^1.3.3\",\r\n        \"statuses\": \"^2.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/find-up\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"locate-path\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/forwarded\": {\r\n      \"version\": \"0.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz\",\r\n      \"integrity\": \"sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/fresh\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/fs-extra\": {\r\n      \"version\": \"11.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz\",\r\n      \"integrity\": \"sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"graceful-fs\": \"^4.2.0\",\r\n        \"jsonfile\": \"^6.0.1\",\r\n        \"universalify\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.14\"\r\n      }\r\n    },\r\n    \"node_modules/fsevents\": {\r\n      \"version\": \"2.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz\",\r\n      \"integrity\": \"sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==\",\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^8.16.0 || ^10.6.0 || >=11.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/function-bind\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/fuzzysort\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/gensync\": {\r\n      \"version\": \"1.0.0-beta.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz\",\r\n      \"integrity\": \"sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/get-east-asian-width\": {\r\n      \"version\": \"1.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz\",\r\n      \"integrity\": \"sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/get-intrinsic\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.2\",\r\n        \"es-define-property\": \"^1.0.1\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"es-object-atoms\": \"^1.1.1\",\r\n        \"function-bind\": \"^1.1.2\",\r\n        \"get-proto\": \"^1.0.1\",\r\n        \"gopd\": \"^1.2.0\",\r\n        \"has-symbols\": \"^1.1.0\",\r\n        \"hasown\": \"^2.0.2\",\r\n        \"math-intrinsics\": \"^1.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/get-nonce\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/get-own-enumerable-keys\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-own-enumerable-keys/-/get-own-enumerable-keys-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=14.16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/get-proto\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"dunder-proto\": \"^1.0.1\",\r\n        \"es-object-atoms\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/get-stream\": {\r\n      \"version\": \"9.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz\",\r\n      \"integrity\": \"sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@sec-ant/readable-stream\": \"^0.4.1\",\r\n        \"is-stream\": \"^4.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/glob-parent\": {\r\n      \"version\": \"5.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz\",\r\n      \"integrity\": \"sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"is-glob\": \"^4.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 6\"\r\n      }\r\n    },\r\n    \"node_modules/gopd\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/graceful-fs\": {\r\n      \"version\": \"4.2.11\",\r\n      \"resolved\": \"https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz\",\r\n      \"integrity\": \"sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/has-flag\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/has-symbols\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/hasown\": {\r\n      \"version\": \"2.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz\",\r\n      \"integrity\": \"sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"function-bind\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/hex-rgb\": {\r\n      \"version\": \"4.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz\",\r\n      \"integrity\": \"sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/hono\": {\r\n      \"version\": \"4.13.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/hono/-/hono-4.13.7.tgz\",\r\n      \"integrity\": \"sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=16.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/http-errors\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"depd\": \"~2.0.0\",\r\n        \"inherits\": \"~2.0.4\",\r\n        \"setprototypeof\": \"~1.2.0\",\r\n        \"statuses\": \"~2.0.2\",\r\n        \"toidentifier\": \"~1.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/human-signals\": {\r\n      \"version\": \"8.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz\",\r\n      \"integrity\": \"sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==\",\r\n      \"license\": \"Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=18.18.0\"\r\n      }\r\n    },\r\n    \"node_modules/iconv-lite\": {\r\n      \"version\": \"0.7.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz\",\r\n      \"integrity\": \"sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"safer-buffer\": \">= 2.1.2 < 3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/ignore\": {\r\n      \"version\": \"5.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz\",\r\n      \"integrity\": \"sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 4\"\r\n      }\r\n    },\r\n    \"node_modules/image-size\": {\r\n      \"version\": \"2.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz\",\r\n      \"integrity\": \"sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"image-size\": \"bin/image-size.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16.x\"\r\n      }\r\n    },\r\n    \"node_modules/immer\": {\r\n      \"version\": \"10.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/immer/-/immer-10.2.0.tgz\",\r\n      \"integrity\": \"sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/immer\"\r\n      }\r\n    },\r\n    \"node_modules/import-fresh\": {\r\n      \"version\": \"3.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz\",\r\n      \"integrity\": \"sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"parent-module\": \"^1.0.0\",\r\n        \"resolve-from\": \"^4.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/inherits\": {\r\n      \"version\": \"2.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz\",\r\n      \"integrity\": \"sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/input-otp\": {\r\n      \"version\": \"1.4.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz\",\r\n      \"integrity\": \"sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc\",\r\n        \"react-dom\": \"^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      }\r\n    },\r\n    \"node_modules/internmap\": {\r\n      \"version\": \"2.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz\",\r\n      \"integrity\": \"sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/ip-address\": {\r\n      \"version\": \"10.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz\",\r\n      \"integrity\": \"sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 12\"\r\n      }\r\n    },\r\n    \"node_modules/ipaddr.js\": {\r\n      \"version\": \"1.9.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz\",\r\n      \"integrity\": \"sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.10\"\r\n      }\r\n    },\r\n    \"node_modules/is-arrayish\": {\r\n      \"version\": \"0.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz\",\r\n      \"integrity\": \"sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/is-docker\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"is-docker\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^12.20.0 || ^14.13.1 || >=16.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-extglob\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/is-glob\": {\r\n      \"version\": \"4.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz\",\r\n      \"integrity\": \"sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-extglob\": \"^2.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/is-in-ssh\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-inside-container\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-docker\": \"^3.0.0\"\r\n      },\r\n      \"bin\": {\r\n        \"is-inside-container\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-interactive\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-number\": {\r\n      \"version\": \"7.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz\",\r\n      \"integrity\": \"sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/is-obj\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/is-plain-obj\": {\r\n      \"version\": \"4.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz\",\r\n      \"integrity\": \"sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-promise\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/is-regexp\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-stream\": {\r\n      \"version\": \"4.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz\",\r\n      \"integrity\": \"sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-unicode-supported\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/is-wsl\": {\r\n      \"version\": \"3.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz\",\r\n      \"integrity\": \"sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-inside-container\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/isexe\": {\r\n      \"version\": \"3.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz\",\r\n      \"integrity\": \"sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==\",\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/jest-worker\": {\r\n      \"version\": \"27.5.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz\",\r\n      \"integrity\": \"sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@types/node\": \"*\",\r\n        \"merge-stream\": \"^2.0.0\",\r\n        \"supports-color\": \"^8.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 10.13.0\"\r\n      }\r\n    },\r\n    \"node_modules/jiti\": {\r\n      \"version\": \"2.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz\",\r\n      \"integrity\": \"sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"jiti\": \"lib/jiti-cli.mjs\"\r\n      }\r\n    },\r\n    \"node_modules/jose\": {\r\n      \"version\": \"6.2.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/jose/-/jose-6.2.12.tgz\",\r\n      \"integrity\": \"sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/panva\"\r\n      }\r\n    },\r\n    \"node_modules/js-tokens\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/js-yaml\": {\r\n      \"version\": \"4.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz\",\r\n      \"integrity\": \"sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/puzrin\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/nodeca\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"argparse\": \"^2.0.1\"\r\n      },\r\n      \"bin\": {\r\n        \"js-yaml\": \"bin/js-yaml.js\"\r\n      }\r\n    },\r\n    \"node_modules/jsesc\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"jsesc\": \"bin/jsesc\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/json-parse-even-better-errors\": {\r\n      \"version\": \"2.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz\",\r\n      \"integrity\": \"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/json-schema-traverse\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/json-schema-typed\": {\r\n      \"version\": \"8.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz\",\r\n      \"integrity\": \"sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==\",\r\n      \"license\": \"BSD-2-Clause\"\r\n    },\r\n    \"node_modules/json5\": {\r\n      \"version\": \"2.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/json5/-/json5-2.2.3.tgz\",\r\n      \"integrity\": \"sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"json5\": \"lib/cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/jsonfile\": {\r\n      \"version\": \"6.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz\",\r\n      \"integrity\": \"sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"universalify\": \"^2.0.0\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"graceful-fs\": \"^4.1.6\"\r\n      }\r\n    },\r\n    \"node_modules/kleur\": {\r\n      \"version\": \"4.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz\",\r\n      \"integrity\": \"sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"dependencies\": {\r\n        \"detect-libc\": \"^2.0.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"lightningcss-android-arm64\": \"1.31.1\",\r\n        \"lightningcss-darwin-arm64\": \"1.31.1\",\r\n        \"lightningcss-darwin-x64\": \"1.31.1\",\r\n        \"lightningcss-freebsd-x64\": \"1.31.1\",\r\n        \"lightningcss-linux-arm-gnueabihf\": \"1.31.1\",\r\n        \"lightningcss-linux-arm64-gnu\": \"1.31.1\",\r\n        \"lightningcss-linux-arm64-musl\": \"1.31.1\",\r\n        \"lightningcss-linux-x64-gnu\": \"1.31.1\",\r\n        \"lightningcss-linux-x64-musl\": \"1.31.1\",\r\n        \"lightningcss-win32-arm64-msvc\": \"1.31.1\",\r\n        \"lightningcss-win32-x64-msvc\": \"1.31.1\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-android-arm64\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-darwin-arm64\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-darwin-x64\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-freebsd-x64\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm64-gnu\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm64-musl\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-x64-gnu\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-x64-musl\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-win32-arm64-msvc\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-win32-x64-msvc\": {\r\n      \"version\": \"1.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz\",\r\n      \"integrity\": \"sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/linebreak\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"base64-js\": \"0.0.8\",\r\n        \"unicode-trie\": \"^2.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/lines-and-columns\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/locate-path\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"p-locate\": \"^3.0.0\",\r\n        \"path-exists\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/log-symbols\": {\r\n      \"version\": \"6.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz\",\r\n      \"integrity\": \"sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"chalk\": \"^5.3.0\",\r\n        \"is-unicode-supported\": \"^1.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/log-symbols/node_modules/is-unicode-supported\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/lru-cache\": {\r\n      \"version\": \"5.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz\",\r\n      \"integrity\": \"sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"yallist\": \"^3.0.2\"\r\n      }\r\n    },\r\n    \"node_modules/lucide-react\": {\r\n      \"version\": \"1.31.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lucide-react/-/lucide-react-1.31.0.tgz\",\r\n      \"integrity\": \"sha512-G8u2eEtoHUnUa9f8lbvqDhCiORMnYLdUEo06EEG9MQvHQrInKcX3Pa2TH39MM5qyzRcWETxB0+aOwAPI1g1kEg==\",\r\n      \"license\": \"ISC\",\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/magic-string\": {\r\n      \"version\": \"0.30.21\",\r\n      \"resolved\": \"https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz\",\r\n      \"integrity\": \"sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/sourcemap-codec\": \"^1.5.5\"\r\n      }\r\n    },\r\n    \"node_modules/math-intrinsics\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/media-typer\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/merge-descriptors\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/merge-stream\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/merge2\": {\r\n      \"version\": \"1.4.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz\",\r\n      \"integrity\": \"sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 8\"\r\n      }\r\n    },\r\n    \"node_modules/meshoptimizer\": {\r\n      \"version\": \"0.22.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz\",\r\n      \"integrity\": \"sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/micromatch\": {\r\n      \"version\": \"4.0.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz\",\r\n      \"integrity\": \"sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"braces\": \"^3.0.3\",\r\n        \"picomatch\": \"^2.3.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8.6\"\r\n      }\r\n    },\r\n    \"node_modules/micromatch/node_modules/picomatch\": {\r\n      \"version\": \"2.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz\",\r\n      \"integrity\": \"sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8.6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/jonschlinkert\"\r\n      }\r\n    },\r\n    \"node_modules/mime-db\": {\r\n      \"version\": \"1.54.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz\",\r\n      \"integrity\": \"sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/mime-types\": {\r\n      \"version\": \"3.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz\",\r\n      \"integrity\": \"sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mime-db\": \"^1.54.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/mimic-fn\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/mimic-function\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/miniflare\": {\r\n      \"version\": \"4.20260515.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/miniflare/-/miniflare-4.20260515.0.tgz\",\r\n      \"integrity\": \"sha512-2j0oQWizk1Eu4Cm8tDX7Z+Nsjd0nebIj1TQcQ+Oy1QKeo0Ay9+bdn8wfLAtOj9znDCybDCUlnS1+nYvKXEdfNg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@cspotcode/source-map-support\": \"0.8.1\",\r\n        \"sharp\": \"^0.34.5\",\r\n        \"undici\": \"7.24.8\",\r\n        \"workerd\": \"1.20260515.1\",\r\n        \"ws\": \"8.18.0\",\r\n        \"youch\": \"4.1.0-beta.10\"\r\n      },\r\n      \"bin\": {\r\n        \"miniflare\": \"bootstrap.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/minimatch\": {\r\n      \"version\": \"10.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz\",\r\n      \"integrity\": \"sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==\",\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"dependencies\": {\r\n        \"brace-expansion\": \"^5.0.8\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"18 || 20 || >=22\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/isaacs\"\r\n      }\r\n    },\r\n    \"node_modules/minimist\": {\r\n      \"version\": \"1.2.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz\",\r\n      \"integrity\": \"sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/minimizer-webpack-plugin\": {\r\n      \"version\": \"5.9.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.9.0.tgz\",\r\n      \"integrity\": \"sha512-OASqv+dewv8vjkOBjqMOHvxUaXhtGBLZAXq7JbmYE8TSzBvOswiGq5JcBdc8ypuLL/8YUT1OEyupga6/iqdyTA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@jridgewell/trace-mapping\": \"^0.3.31\",\r\n        \"jest-worker\": \"^27.4.5\",\r\n        \"schema-utils\": \"^4.3.3\",\r\n        \"terser\": \"^5.51.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 10.13.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/webpack\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"webpack\": \"^5.1.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@minify-html/node\": {\r\n          \"optional\": true\r\n        },\r\n        \"@napi-rs/image\": {\r\n          \"optional\": true\r\n        },\r\n        \"@swc/core\": {\r\n          \"optional\": true\r\n        },\r\n        \"@swc/css\": {\r\n          \"optional\": true\r\n        },\r\n        \"@swc/html\": {\r\n          \"optional\": true\r\n        },\r\n        \"clean-css\": {\r\n          \"optional\": true\r\n        },\r\n        \"cssnano\": {\r\n          \"optional\": true\r\n        },\r\n        \"csso\": {\r\n          \"optional\": true\r\n        },\r\n        \"esbuild\": {\r\n          \"optional\": true\r\n        },\r\n        \"html-minifier-terser\": {\r\n          \"optional\": true\r\n        },\r\n        \"imagemin\": {\r\n          \"optional\": true\r\n        },\r\n        \"lightningcss\": {\r\n          \"optional\": true\r\n        },\r\n        \"postcss\": {\r\n          \"optional\": true\r\n        },\r\n        \"sharp\": {\r\n          \"optional\": true\r\n        },\r\n        \"svgo\": {\r\n          \"optional\": true\r\n        },\r\n        \"uglify-js\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/ms\": {\r\n      \"version\": \"2.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\r\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/nanoid\": {\r\n      \"version\": \"3.3.18\",\r\n      \"resolved\": \"https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz\",\r\n      \"integrity\": \"sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"nanoid\": \"bin/nanoid.cjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^10 || ^12 || ^13.7 || ^14 || >=15.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/negotiator\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"content-type\": \"^2.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/negotiator/node_modules/content-type\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/neo-async\": {\r\n      \"version\": \"2.6.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz\",\r\n      \"integrity\": \"sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/node-releases\": {\r\n      \"version\": \"2.0.54\",\r\n      \"resolved\": \"https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz\",\r\n      \"integrity\": \"sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/npm-run-path\": {\r\n      \"version\": \"6.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz\",\r\n      \"integrity\": \"sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"path-key\": \"^4.0.0\",\r\n        \"unicorn-magic\": \"^0.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/npm-run-path/node_modules/path-key\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/object-assign\": {\r\n      \"version\": \"4.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz\",\r\n      \"integrity\": \"sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/object-inspect\": {\r\n      \"version\": \"1.13.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz\",\r\n      \"integrity\": \"sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/object-treeify\": {\r\n      \"version\": \"1.1.33\",\r\n      \"resolved\": \"https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz\",\r\n      \"integrity\": \"sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 10\"\r\n      }\r\n    },\r\n    \"node_modules/on-finished\": {\r\n      \"version\": \"2.4.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz\",\r\n      \"integrity\": \"sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ee-first\": \"1.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/once\": {\r\n      \"version\": \"1.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/once/-/once-1.4.0.tgz\",\r\n      \"integrity\": \"sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"wrappy\": \"1\"\r\n      }\r\n    },\r\n    \"node_modules/onetime\": {\r\n      \"version\": \"5.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz\",\r\n      \"integrity\": \"sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mimic-fn\": \"^2.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/onetime/node_modules/mimic-fn\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/open\": {\r\n      \"version\": \"11.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/open/-/open-11.0.2.tgz\",\r\n      \"integrity\": \"sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"default-browser\": \"^5.5.1\",\r\n        \"define-lazy-prop\": \"^3.0.0\",\r\n        \"is-in-ssh\": \"^1.0.0\",\r\n        \"is-inside-container\": \"^1.0.0\",\r\n        \"powershell-utils\": \"^0.2.1\",\r\n        \"wsl-utils\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/ora\": {\r\n      \"version\": \"8.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ora/-/ora-8.2.0.tgz\",\r\n      \"integrity\": \"sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"chalk\": \"^5.3.0\",\r\n        \"cli-cursor\": \"^5.0.0\",\r\n        \"cli-spinners\": \"^2.9.2\",\r\n        \"is-interactive\": \"^2.0.0\",\r\n        \"is-unicode-supported\": \"^2.0.0\",\r\n        \"log-symbols\": \"^6.0.0\",\r\n        \"stdin-discarder\": \"^0.2.2\",\r\n        \"string-width\": \"^7.2.0\",\r\n        \"strip-ansi\": \"^7.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/ora/node_modules/ansi-regex\": {\r\n      \"version\": \"6.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz\",\r\n      \"integrity\": \"sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/ansi-regex?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/ora/node_modules/strip-ansi\": {\r\n      \"version\": \"7.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz\",\r\n      \"integrity\": \"sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-regex\": \"^6.2.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/strip-ansi?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/oxfmt\": {\r\n      \"version\": \"0.61.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/oxfmt/-/oxfmt-0.61.0.tgz\",\r\n      \"integrity\": \"sha512-DxdHBEMYpcEnHoUHjjOigUqV2TYKsvxLwUPXnVYBjgFdqrcQ/91OtwubtZ2PUodCs3sStI8R5Qw3fKNGK4e8wQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"tinypool\": \"2.1.0\"\r\n      },\r\n      \"bin\": {\r\n        \"oxfmt\": \"bin/oxfmt\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/Boshen\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@oxfmt/binding-android-arm-eabi\": \"0.61.0\",\r\n        \"@oxfmt/binding-android-arm64\": \"0.61.0\",\r\n        \"@oxfmt/binding-darwin-arm64\": \"0.61.0\",\r\n        \"@oxfmt/binding-darwin-x64\": \"0.61.0\",\r\n        \"@oxfmt/binding-freebsd-x64\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-arm-gnueabihf\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-arm-musleabihf\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-arm64-gnu\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-arm64-musl\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-ppc64-gnu\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-riscv64-gnu\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-riscv64-musl\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-s390x-gnu\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-x64-gnu\": \"0.61.0\",\r\n        \"@oxfmt/binding-linux-x64-musl\": \"0.61.0\",\r\n        \"@oxfmt/binding-openharmony-arm64\": \"0.61.0\",\r\n        \"@oxfmt/binding-win32-arm64-msvc\": \"0.61.0\",\r\n        \"@oxfmt/binding-win32-ia32-msvc\": \"0.61.0\",\r\n        \"@oxfmt/binding-win32-x64-msvc\": \"0.61.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"svelte\": \"^5.0.0\",\r\n        \"vite-plus\": \"*\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"svelte\": {\r\n          \"optional\": true\r\n        },\r\n        \"vite-plus\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/oxlint\": {\r\n      \"version\": \"1.76.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/oxlint/-/oxlint-1.76.0.tgz\",\r\n      \"integrity\": \"sha512-6QoFioEU4fNdiUx/2Eo6TRd6NG7H7njnRCz8rhB66cZmMHDTqcm1Rjvl8Wry+ZTQMBAmyb4Mlf62Mk5X+eHSOw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"oxlint\": \"bin/oxlint\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/Boshen\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@oxlint/binding-android-arm-eabi\": \"1.76.0\",\r\n        \"@oxlint/binding-android-arm64\": \"1.76.0\",\r\n        \"@oxlint/binding-darwin-arm64\": \"1.76.0\",\r\n        \"@oxlint/binding-darwin-x64\": \"1.76.0\",\r\n        \"@oxlint/binding-freebsd-x64\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-arm-gnueabihf\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-arm-musleabihf\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-arm64-gnu\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-arm64-musl\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-ppc64-gnu\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-riscv64-gnu\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-riscv64-musl\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-s390x-gnu\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-x64-gnu\": \"1.76.0\",\r\n        \"@oxlint/binding-linux-x64-musl\": \"1.76.0\",\r\n        \"@oxlint/binding-openharmony-arm64\": \"1.76.0\",\r\n        \"@oxlint/binding-win32-arm64-msvc\": \"1.76.0\",\r\n        \"@oxlint/binding-win32-ia32-msvc\": \"1.76.0\",\r\n        \"@oxlint/binding-win32-x64-msvc\": \"1.76.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"oxlint-tsgolint\": \">=7.0.2001\",\r\n        \"vite-plus\": \"*\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"oxlint-tsgolint\": {\r\n          \"optional\": true\r\n        },\r\n        \"vite-plus\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/oxlint-tsgolint\": {\r\n      \"version\": \"7.0.2001\",\r\n      \"resolved\": \"https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-7.0.2001.tgz\",\r\n      \"integrity\": \"sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"tsgolint\": \"bin/tsgolint.js\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@oxlint-tsgolint/darwin-arm64\": \"7.0.2001\",\r\n        \"@oxlint-tsgolint/darwin-x64\": \"7.0.2001\",\r\n        \"@oxlint-tsgolint/linux-arm64\": \"7.0.2001\",\r\n        \"@oxlint-tsgolint/linux-x64\": \"7.0.2001\",\r\n        \"@oxlint-tsgolint/win32-arm64\": \"7.0.2001\",\r\n        \"@oxlint-tsgolint/win32-x64\": \"7.0.2001\"\r\n      }\r\n    },\r\n    \"node_modules/p-limit\": {\r\n      \"version\": \"2.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz\",\r\n      \"integrity\": \"sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"p-try\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/p-locate\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"p-limit\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/p-try\": {\r\n      \"version\": \"2.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz\",\r\n      \"integrity\": \"sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/pako\": {\r\n      \"version\": \"0.2.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/pako/-/pako-0.2.9.tgz\",\r\n      \"integrity\": \"sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/parent-module\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"callsites\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/parse-css-color\": {\r\n      \"version\": \"0.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz\",\r\n      \"integrity\": \"sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"color-name\": \"^1.1.4\",\r\n        \"hex-rgb\": \"^4.1.0\"\r\n      }\r\n    },\r\n    \"node_modules/parse-json\": {\r\n      \"version\": \"5.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz\",\r\n      \"integrity\": \"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/code-frame\": \"^7.0.0\",\r\n        \"error-ex\": \"^1.3.1\",\r\n        \"json-parse-even-better-errors\": \"^2.3.0\",\r\n        \"lines-and-columns\": \"^1.1.6\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/parse-ms\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/parseurl\": {\r\n      \"version\": \"1.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz\",\r\n      \"integrity\": \"sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/path-browserify\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/path-exists\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/path-key\": {\r\n      \"version\": \"3.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz\",\r\n      \"integrity\": \"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/path-to-regexp\": {\r\n      \"version\": \"8.4.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz\",\r\n      \"integrity\": \"sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/pathe\": {\r\n      \"version\": \"2.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz\",\r\n      \"integrity\": \"sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/picocolors\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/picomatch\": {\r\n      \"version\": \"4.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz\",\r\n      \"integrity\": \"sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/jonschlinkert\"\r\n      }\r\n    },\r\n    \"node_modules/pkce-challenge\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=16.20.0\"\r\n      }\r\n    },\r\n    \"node_modules/pkg-up\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"find-up\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/postcss\": {\r\n      \"version\": \"8.5.28\",\r\n      \"resolved\": \"https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz\",\r\n      \"integrity\": \"sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/postcss/\"\r\n        },\r\n        {\r\n          \"type\": \"tidelift\",\r\n          \"url\": \"https://tidelift.com/funding/github/npm/postcss\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"nanoid\": \"^3.3.18\",\r\n        \"picocolors\": \"^1.1.1\",\r\n        \"source-map-js\": \"^1.2.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^10 || ^12 || >=14\"\r\n      }\r\n    },\r\n    \"node_modules/postcss-selector-parser\": {\r\n      \"version\": \"7.1.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz\",\r\n      \"integrity\": \"sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"cssesc\": \"^3.0.0\",\r\n        \"util-deprecate\": \"^1.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/postcss-value-parser\": {\r\n      \"version\": \"4.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz\",\r\n      \"integrity\": \"sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/powershell-utils\": {\r\n      \"version\": \"0.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz\",\r\n      \"integrity\": \"sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/pretty-ms\": {\r\n      \"version\": \"9.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.1.tgz\",\r\n      \"integrity\": \"sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"parse-ms\": \"^4.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/prompts\": {\r\n      \"version\": \"2.4.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz\",\r\n      \"integrity\": \"sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"kleur\": \"^3.0.3\",\r\n        \"sisteransi\": \"^1.0.5\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 6\"\r\n      }\r\n    },\r\n    \"node_modules/prompts/node_modules/kleur\": {\r\n      \"version\": \"3.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz\",\r\n      \"integrity\": \"sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/proxy-addr\": {\r\n      \"version\": \"2.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz\",\r\n      \"integrity\": \"sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"forwarded\": \"0.2.0\",\r\n        \"ipaddr.js\": \"1.9.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.10\"\r\n      }\r\n    },\r\n    \"node_modules/qs\": {\r\n      \"version\": \"6.16.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/qs/-/qs-6.16.0.tgz\",\r\n      \"integrity\": \"sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"es-define-property\": \"^1.0.1\",\r\n        \"side-channel\": \"^1.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/queue-microtask\": {\r\n      \"version\": \"1.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz\",\r\n      \"integrity\": \"sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/feross\"\r\n        },\r\n        {\r\n          \"type\": \"patreon\",\r\n          \"url\": \"https://www.patreon.com/feross\"\r\n        },\r\n        {\r\n          \"type\": \"consulting\",\r\n          \"url\": \"https://feross.org/support\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/range-parser\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/raw-body\": {\r\n      \"version\": \"3.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz\",\r\n      \"integrity\": \"sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"bytes\": \"~3.1.2\",\r\n        \"http-errors\": \"~2.0.1\",\r\n        \"iconv-lite\": \"~0.7.0\",\r\n        \"unpipe\": \"~1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.10\"\r\n      }\r\n    },\r\n    \"node_modules/react\": {\r\n      \"version\": \"19.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/react/-/react-19.2.6.tgz\",\r\n      \"integrity\": \"sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/react-day-picker\": {\r\n      \"version\": \"9.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.8.1.tgz\",\r\n      \"integrity\": \"sha512-kMcLrp3PfN/asVJayVv82IjF3iLOOxuH5TNFWezX6lS/T8iVRFPTETpHl3TUSTH99IDMZLubdNPJr++rQctkEw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@date-fns/tz\": \"^1.2.0\",\r\n        \"date-fns\": \"^4.1.0\",\r\n        \"date-fns-jalali\": \"^4.1.0-0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"individual\",\r\n        \"url\": \"https://github.com/sponsors/gpbl\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \">=16.8.0\"\r\n      }\r\n    },\r\n    \"node_modules/react-dom\": {\r\n      \"version\": \"19.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz\",\r\n      \"integrity\": \"sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"scheduler\": \"^0.27.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^19.2.6\"\r\n      }\r\n    },\r\n    \"node_modules/react-is\": {\r\n      \"version\": \"19.2.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz\",\r\n      \"integrity\": \"sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/react-redux\": {\r\n      \"version\": \"9.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz\",\r\n      \"integrity\": \"sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@types/use-sync-external-store\": \"^0.0.6\",\r\n        \"use-sync-external-store\": \"^1.4.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"^18.2.25 || ^19\",\r\n        \"react\": \"^18.0 || ^19\",\r\n        \"redux\": \"^5.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        },\r\n        \"redux\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/react-remove-scroll\": {\r\n      \"version\": \"2.7.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz\",\r\n      \"integrity\": \"sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"react-remove-scroll-bar\": \"^2.3.7\",\r\n        \"react-style-singleton\": \"^2.2.3\",\r\n        \"tslib\": \"^2.1.0\",\r\n        \"use-callback-ref\": \"^1.3.3\",\r\n        \"use-sidecar\": \"^1.1.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/react-remove-scroll-bar\": {\r\n      \"version\": \"2.3.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz\",\r\n      \"integrity\": \"sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"react-style-singleton\": \"^2.2.2\",\r\n        \"tslib\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/react-resizable-panels\": {\r\n      \"version\": \"4.5.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.5.8.tgz\",\r\n      \"integrity\": \"sha512-X2S5YoYWbjd7Ove6e6T/kzOrjiUD6ccz55a+XW0H3JXbrPb+Gmz7YRAJy4ysOkua/U5jSOG9SoySbebMBjtQJQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"react\": \"^18.0.0 || ^19.0.0\",\r\n        \"react-dom\": \"^18.0.0 || ^19.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/react-server-dom-webpack\": {\r\n      \"version\": \"19.2.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-server-dom-webpack/-/react-server-dom-webpack-19.2.6.tgz\",\r\n      \"integrity\": \"sha512-762YXjBPc6hw2V16k4x1sdnw0mxghcSPzoiOhefGYjiTguJLCImGBUz6EjznPIfqKhWgLtpaQMlBhp66N8dKrw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"acorn-loose\": \"^8.3.0\",\r\n        \"neo-async\": \"^2.6.1\",\r\n        \"webpack-sources\": \"^3.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^19.2.6\",\r\n        \"react-dom\": \"^19.2.6\",\r\n        \"webpack\": \"^5.59.0\"\r\n      }\r\n    },\r\n    \"node_modules/react-style-singleton\": {\r\n      \"version\": \"2.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz\",\r\n      \"integrity\": \"sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"get-nonce\": \"^1.0.0\",\r\n        \"tslib\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/recast\": {\r\n      \"version\": \"0.23.21\",\r\n      \"resolved\": \"https://registry.npmjs.org/recast/-/recast-0.23.21.tgz\",\r\n      \"integrity\": \"sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ast-types\": \"^0.16.1\",\r\n        \"esprima\": \"~4.0.0\",\r\n        \"source-map\": \"~0.6.1\",\r\n        \"tiny-invariant\": \"^1.3.3\",\r\n        \"tslib\": \"^2.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 4\"\r\n      }\r\n    },\r\n    \"node_modules/recharts\": {\r\n      \"version\": \"3.8.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz\",\r\n      \"integrity\": \"sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==\",\r\n      \"license\": \"MIT\",\r\n      \"workspaces\": [\r\n        \"www\"\r\n      ],\r\n      \"dependencies\": {\r\n        \"@reduxjs/toolkit\": \"^1.9.0 || 2.x.x\",\r\n        \"clsx\": \"^2.1.1\",\r\n        \"decimal.js-light\": \"^2.5.1\",\r\n        \"es-toolkit\": \"^1.39.3\",\r\n        \"eventemitter3\": \"^5.0.1\",\r\n        \"immer\": \"^10.1.1\",\r\n        \"react-redux\": \"8.x.x || 9.x.x\",\r\n        \"reselect\": \"5.1.1\",\r\n        \"tiny-invariant\": \"^1.3.3\",\r\n        \"use-sync-external-store\": \"^1.2.2\",\r\n        \"victory-vendor\": \"^37.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\r\n        \"react-dom\": \"^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\r\n        \"react-is\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/recharts/node_modules/reselect\": {\r\n      \"version\": \"5.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz\",\r\n      \"integrity\": \"sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/redux\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/redux/-/redux-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/redux-thunk\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"redux\": \"^5.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/require-from-string\": {\r\n      \"version\": \"2.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz\",\r\n      \"integrity\": \"sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/reselect\": {\r\n      \"version\": \"5.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/reselect/-/reselect-5.3.0.tgz\",\r\n      \"integrity\": \"sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/resolve-from\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/restore-cursor\": {\r\n      \"version\": \"5.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz\",\r\n      \"integrity\": \"sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"onetime\": \"^7.0.0\",\r\n        \"signal-exit\": \"^4.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/restore-cursor/node_modules/onetime\": {\r\n      \"version\": \"7.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz\",\r\n      \"integrity\": \"sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mimic-function\": \"^5.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/reusify\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"iojs\": \">=1.0.0\",\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/rolldown\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@oxc-project/types\": \"=0.130.0\",\r\n        \"@rolldown/pluginutils\": \"^1.0.0\"\r\n      },\r\n      \"bin\": {\r\n        \"rolldown\": \"bin/cli.mjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@rolldown/binding-android-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-darwin-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-darwin-x64\": \"1.0.1\",\r\n        \"@rolldown/binding-freebsd-x64\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm-gnueabihf\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm64-musl\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-ppc64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-s390x-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-x64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-x64-musl\": \"1.0.1\",\r\n        \"@rolldown/binding-openharmony-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-wasm32-wasi\": \"1.0.1\",\r\n        \"@rolldown/binding-win32-arm64-msvc\": \"1.0.1\",\r\n        \"@rolldown/binding-win32-x64-msvc\": \"1.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/router\": {\r\n      \"version\": \"2.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/router/-/router-2.2.0.tgz\",\r\n      \"integrity\": \"sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"debug\": \"^4.4.0\",\r\n        \"depd\": \"^2.0.0\",\r\n        \"is-promise\": \"^4.0.0\",\r\n        \"parseurl\": \"^1.3.3\",\r\n        \"path-to-regexp\": \"^8.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      }\r\n    },\r\n    \"node_modules/run-applescript\": {\r\n      \"version\": \"7.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz\",\r\n      \"integrity\": \"sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/run-parallel\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/feross\"\r\n        },\r\n        {\r\n          \"type\": \"patreon\",\r\n          \"url\": \"https://www.patreon.com/feross\"\r\n        },\r\n        {\r\n          \"type\": \"consulting\",\r\n          \"url\": \"https://feross.org/support\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"queue-microtask\": \"^1.2.2\"\r\n      }\r\n    },\r\n    \"node_modules/safer-buffer\": {\r\n      \"version\": \"2.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz\",\r\n      \"integrity\": \"sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/satori\": {\r\n      \"version\": \"0.16.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/satori/-/satori-0.16.0.tgz\",\r\n      \"integrity\": \"sha512-ZvHN3ygzZ8FuxjSNB+mKBiF/NIoqHzlBGbD0MJiT+MvSsFOvotnWOhdTjxKzhHRT2wPC1QbhLzx2q/Y83VhfYQ==\",\r\n      \"license\": \"MPL-2.0\",\r\n      \"dependencies\": {\r\n        \"@shuding/opentype.js\": \"1.4.0-beta.0\",\r\n        \"css-background-parser\": \"^0.1.0\",\r\n        \"css-box-shadow\": \"1.0.0-3\",\r\n        \"css-gradient-parser\": \"^0.0.16\",\r\n        \"css-to-react-native\": \"^3.0.0\",\r\n        \"emoji-regex-xs\": \"^2.0.1\",\r\n        \"escape-html\": \"^1.0.3\",\r\n        \"linebreak\": \"^1.1.0\",\r\n        \"parse-css-color\": \"^0.2.1\",\r\n        \"postcss-value-parser\": \"^4.2.0\",\r\n        \"yoga-layout\": \"^3.2.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/scheduler\": {\r\n      \"version\": \"0.27.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz\",\r\n      \"integrity\": \"sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/schema-utils\": {\r\n      \"version\": \"4.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz\",\r\n      \"integrity\": \"sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@types/json-schema\": \"^7.0.9\",\r\n        \"ajv\": \"^8.9.0\",\r\n        \"ajv-formats\": \"^2.1.1\",\r\n        \"ajv-keywords\": \"^5.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 10.13.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/webpack\"\r\n      }\r\n    },\r\n    \"node_modules/schema-utils/node_modules/ajv-formats\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"ajv\": \"^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"ajv\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/semver\": {\r\n      \"version\": \"6.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/semver/-/semver-6.3.1.tgz\",\r\n      \"integrity\": \"sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==\",\r\n      \"license\": \"ISC\",\r\n      \"bin\": {\r\n        \"semver\": \"bin/semver.js\"\r\n      }\r\n    },\r\n    \"node_modules/send\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/send/-/send-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"debug\": \"^4.4.3\",\r\n        \"encodeurl\": \"^2.0.0\",\r\n        \"escape-html\": \"^1.0.3\",\r\n        \"etag\": \"^1.8.1\",\r\n        \"fresh\": \"^2.0.0\",\r\n        \"http-errors\": \"^2.0.1\",\r\n        \"mime-types\": \"^3.0.2\",\r\n        \"ms\": \"^2.1.3\",\r\n        \"on-finished\": \"^2.4.1\",\r\n        \"range-parser\": \"^1.2.1\",\r\n        \"statuses\": \"^2.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/serve-static\": {\r\n      \"version\": \"2.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz\",\r\n      \"integrity\": \"sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"encodeurl\": \"^2.0.0\",\r\n        \"escape-html\": \"^1.0.3\",\r\n        \"parseurl\": \"^1.3.3\",\r\n        \"send\": \"^1.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/setprototypeof\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/shadcn\": {\r\n      \"version\": \"4.18.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/shadcn/-/shadcn-4.18.0.tgz\",\r\n      \"integrity\": \"sha512-tUFZgkYmfVNQVm3xX7lhSzOvDsp+O14ac5dwgXIr5mIsr79ISueb/Mu+ZtWMz0DH6v77u4eYyvbQ9TTMpSn3aw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@babel/core\": \"^7.28.0\",\r\n        \"@babel/parser\": \"^7.28.0\",\r\n        \"@babel/plugin-transform-typescript\": \"^7.28.0\",\r\n        \"@babel/preset-typescript\": \"^7.27.1\",\r\n        \"@dotenvx/dotenvx\": \"^1.48.4\",\r\n        \"@modelcontextprotocol/sdk\": \"^1.26.0\",\r\n        \"@types/validate-npm-package-name\": \"^4.0.2\",\r\n        \"browserslist\": \"^4.26.2\",\r\n        \"commander\": \"^14.0.0\",\r\n        \"cosmiconfig\": \"^9.0.0\",\r\n        \"dedent\": \"^1.6.0\",\r\n        \"deepmerge\": \"^4.3.1\",\r\n        \"diff\": \"^8.0.2\",\r\n        \"execa\": \"^9.6.0\",\r\n        \"fast-glob\": \"^3.3.3\",\r\n        \"fs-extra\": \"^11.3.1\",\r\n        \"fuzzysort\": \"^3.1.0\",\r\n        \"kleur\": \"^4.1.5\",\r\n        \"open\": \"^11.0.0\",\r\n        \"ora\": \"^8.2.0\",\r\n        \"postcss\": \"^8.5.6\",\r\n        \"postcss-selector-parser\": \"^7.1.0\",\r\n        \"prompts\": \"^2.4.2\",\r\n        \"recast\": \"^0.23.11\",\r\n        \"socks\": \"^2.8.8\",\r\n        \"stringify-object\": \"^5.0.0\",\r\n        \"tailwind-merge\": \"^3.0.1\",\r\n        \"ts-morph\": \"^26.0.0\",\r\n        \"tsconfig-paths\": \"^4.2.0\",\r\n        \"undici\": \"^7.27.2\",\r\n        \"validate-npm-package-name\": \"^7.0.1\",\r\n        \"zod\": \"^3.24.1\",\r\n        \"zod-to-json-schema\": \"^3.24.6\"\r\n      },\r\n      \"bin\": {\r\n        \"shadcn\": \"dist/index.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20.18.1\"\r\n      }\r\n    },\r\n    \"node_modules/shadcn/node_modules/undici\": {\r\n      \"version\": \"7.29.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/undici/-/undici-7.29.1.tgz\",\r\n      \"integrity\": \"sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20.18.1\"\r\n      }\r\n    },\r\n    \"node_modules/sharp\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"@img/colour\": \"^1.0.0\",\r\n        \"detect-libc\": \"^2.1.2\",\r\n        \"semver\": \"^7.7.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-darwin-arm64\": \"0.34.5\",\r\n        \"@img/sharp-darwin-x64\": \"0.34.5\",\r\n        \"@img/sharp-libvips-darwin-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-darwin-x64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-arm\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-ppc64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-riscv64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-s390x\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-x64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linuxmusl-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linuxmusl-x64\": \"1.2.4\",\r\n        \"@img/sharp-linux-arm\": \"0.34.5\",\r\n        \"@img/sharp-linux-arm64\": \"0.34.5\",\r\n        \"@img/sharp-linux-ppc64\": \"0.34.5\",\r\n        \"@img/sharp-linux-riscv64\": \"0.34.5\",\r\n        \"@img/sharp-linux-s390x\": \"0.34.5\",\r\n        \"@img/sharp-linux-x64\": \"0.34.5\",\r\n        \"@img/sharp-linuxmusl-arm64\": \"0.34.5\",\r\n        \"@img/sharp-linuxmusl-x64\": \"0.34.5\",\r\n        \"@img/sharp-wasm32\": \"0.34.5\",\r\n        \"@img/sharp-win32-arm64\": \"0.34.5\",\r\n        \"@img/sharp-win32-ia32\": \"0.34.5\",\r\n        \"@img/sharp-win32-x64\": \"0.34.5\"\r\n      }\r\n    },\r\n    \"node_modules/sharp/node_modules/semver\": {\r\n      \"version\": \"7.8.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/semver/-/semver-7.8.5.tgz\",\r\n      \"integrity\": \"sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\",\r\n      \"bin\": {\r\n        \"semver\": \"bin/semver.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/shebang-command\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"shebang-regex\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/shebang-regex\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"object-inspect\": \"^1.13.4\",\r\n        \"side-channel-list\": \"^1.0.1\",\r\n        \"side-channel-map\": \"^1.0.1\",\r\n        \"side-channel-weakmap\": \"^1.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-list\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"object-inspect\": \"^1.13.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-map\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bound\": \"^1.0.2\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"get-intrinsic\": \"^1.2.5\",\r\n        \"object-inspect\": \"^1.13.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-weakmap\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bound\": \"^1.0.2\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"get-intrinsic\": \"^1.2.5\",\r\n        \"object-inspect\": \"^1.13.3\",\r\n        \"side-channel-map\": \"^1.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/signal-exit\": {\r\n      \"version\": \"4.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz\",\r\n      \"integrity\": \"sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=14\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/isaacs\"\r\n      }\r\n    },\r\n    \"node_modules/sisteransi\": {\r\n      \"version\": \"1.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz\",\r\n      \"integrity\": \"sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/smart-buffer\": {\r\n      \"version\": \"4.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz\",\r\n      \"integrity\": \"sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 6.0.0\",\r\n        \"npm\": \">= 3.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/socks\": {\r\n      \"version\": \"2.8.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/socks/-/socks-2.8.10.tgz\",\r\n      \"integrity\": \"sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ip-address\": \"^10.1.1\",\r\n        \"smart-buffer\": \"^4.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 10.0.0\",\r\n        \"npm\": \">= 3.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/source-map\": {\r\n      \"version\": \"0.6.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz\",\r\n      \"integrity\": \"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/source-map-js\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/source-map-support\": {\r\n      \"version\": \"0.5.21\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz\",\r\n      \"integrity\": \"sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"buffer-from\": \"^1.0.0\",\r\n        \"source-map\": \"^0.6.0\"\r\n      }\r\n    },\r\n    \"node_modules/srvx\": {\r\n      \"version\": \"0.11.22\",\r\n      \"resolved\": \"https://registry.npmjs.org/srvx/-/srvx-0.11.22.tgz\",\r\n      \"integrity\": \"sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"srvx\": \"bin/srvx.mjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20.16.0\"\r\n      }\r\n    },\r\n    \"node_modules/statuses\": {\r\n      \"version\": \"2.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz\",\r\n      \"integrity\": \"sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/stdin-discarder\": {\r\n      \"version\": \"0.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz\",\r\n      \"integrity\": \"sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/string-width\": {\r\n      \"version\": \"7.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz\",\r\n      \"integrity\": \"sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"emoji-regex\": \"^10.3.0\",\r\n        \"get-east-asian-width\": \"^1.0.0\",\r\n        \"strip-ansi\": \"^7.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/string-width/node_modules/ansi-regex\": {\r\n      \"version\": \"6.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz\",\r\n      \"integrity\": \"sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/ansi-regex?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/string-width/node_modules/strip-ansi\": {\r\n      \"version\": \"7.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz\",\r\n      \"integrity\": \"sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-regex\": \"^6.2.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/strip-ansi?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/string.prototype.codepointat\": {\r\n      \"version\": \"0.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz\",\r\n      \"integrity\": \"sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/stringify-object\": {\r\n      \"version\": \"5.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/stringify-object/-/stringify-object-5.0.0.tgz\",\r\n      \"integrity\": \"sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==\",\r\n      \"license\": \"BSD-2-Clause\",\r\n      \"dependencies\": {\r\n        \"get-own-enumerable-keys\": \"^1.0.0\",\r\n        \"is-obj\": \"^3.0.0\",\r\n        \"is-regexp\": \"^3.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/yeoman/stringify-object?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/stringify-object/node_modules/is-obj\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/strip-ansi\": {\r\n      \"version\": \"6.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz\",\r\n      \"integrity\": \"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-regex\": \"^5.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/strip-bom\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=4\"\r\n      }\r\n    },\r\n    \"node_modules/strip-final-newline\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/strip-literal\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"js-tokens\": \"^9.0.1\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/antfu\"\r\n      }\r\n    },\r\n    \"node_modules/strip-literal/node_modules/js-tokens\": {\r\n      \"version\": \"9.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz\",\r\n      \"integrity\": \"sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/supports-color\": {\r\n      \"version\": \"8.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz\",\r\n      \"integrity\": \"sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"has-flag\": \"^4.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/supports-color?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/systeminformation\": {\r\n      \"version\": \"5.33.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.8.tgz\",\r\n      \"integrity\": \"sha512-v4F6OGYGh7wDvV68YmjOmZwGixV9A/GQ7d2b84t0UF4CaOy9jipNWIJDkHqYDYiTPuiojqlwVQd0hfUKOUN7tQ==\",\r\n      \"license\": \"MIT\",\r\n      \"os\": [\r\n        \"darwin\",\r\n        \"linux\",\r\n        \"win32\",\r\n        \"freebsd\",\r\n        \"openbsd\",\r\n        \"netbsd\",\r\n        \"sunos\",\r\n        \"android\"\r\n      ],\r\n      \"bin\": {\r\n        \"systeminformation\": \"lib/cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"Buy me a coffee\",\r\n        \"url\": \"https://www.buymeacoffee.com/systeminfo\"\r\n      }\r\n    },\r\n    \"node_modules/tailwind-merge\": {\r\n      \"version\": \"3.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz\",\r\n      \"integrity\": \"sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/dcastil\"\r\n      }\r\n    },\r\n    \"node_modules/tailwindcss\": {\r\n      \"version\": \"4.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz\",\r\n      \"integrity\": \"sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tapable\": {\r\n      \"version\": \"2.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz\",\r\n      \"integrity\": \"sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/webpack\"\r\n      }\r\n    },\r\n    \"node_modules/terser\": {\r\n      \"version\": \"5.51.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/terser/-/terser-5.51.2.tgz\",\r\n      \"integrity\": \"sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==\",\r\n      \"license\": \"BSD-2-Clause\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@jridgewell/source-map\": \"^0.3.3\",\r\n        \"acorn\": \"^8.15.0\",\r\n        \"commander\": \"^2.20.0\",\r\n        \"source-map-support\": \"~0.5.20\"\r\n      },\r\n      \"bin\": {\r\n        \"terser\": \"bin/terser\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/terser/node_modules/commander\": {\r\n      \"version\": \"2.20.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/commander/-/commander-2.20.3.tgz\",\r\n      \"integrity\": \"sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true\r\n    },\r\n    \"node_modules/three\": {\r\n      \"version\": \"0.180.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/three/-/three-0.180.0.tgz\",\r\n      \"integrity\": \"sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tiny-inflate\": {\r\n      \"version\": \"1.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz\",\r\n      \"integrity\": \"sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tiny-invariant\": {\r\n      \"version\": \"1.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz\",\r\n      \"integrity\": \"sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tinyglobby\": {\r\n      \"version\": \"0.2.17\",\r\n      \"resolved\": \"https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz\",\r\n      \"integrity\": \"sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fdir\": \"^6.5.0\",\r\n        \"picomatch\": \"^4.0.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/SuperchupuDev\"\r\n      }\r\n    },\r\n    \"node_modules/tinypool\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \"^20.0.0 || >=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/to-regex-range\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-number\": \"^7.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8.0\"\r\n      }\r\n    },\r\n    \"node_modules/toidentifier\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.6\"\r\n      }\r\n    },\r\n    \"node_modules/ts-morph\": {\r\n      \"version\": \"26.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz\",\r\n      \"integrity\": \"sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@ts-morph/common\": \"~0.27.0\",\r\n        \"code-block-writer\": \"^13.0.3\"\r\n      }\r\n    },\r\n    \"node_modules/tsconfig-paths\": {\r\n      \"version\": \"4.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz\",\r\n      \"integrity\": \"sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"json5\": \"^2.2.2\",\r\n        \"minimist\": \"^1.2.6\",\r\n        \"strip-bom\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/tslib\": {\r\n      \"version\": \"2.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz\",\r\n      \"integrity\": \"sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==\",\r\n      \"license\": \"0BSD\"\r\n    },\r\n    \"node_modules/turbo-stream\": {\r\n      \"version\": \"3.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/turbo-stream/-/turbo-stream-3.2.1.tgz\",\r\n      \"integrity\": \"sha512-BaX0eWz3IrypOVDFqPv3VGD6uZZW8y3oFAOJqZQ+oopmOyQ8/xNm7IMwAJFvxax2DBa8zSk+/AeqA6D6b1PXGg==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tw-animate-css\": {\r\n      \"version\": \"1.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz\",\r\n      \"integrity\": \"sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/Wombosvideo\"\r\n      }\r\n    },\r\n    \"node_modules/type-is\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"content-type\": \"^2.0.0\",\r\n        \"media-typer\": \"^1.1.0\",\r\n        \"mime-types\": \"^3.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/type-is/node_modules/content-type\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/typescript\": {\r\n      \"version\": \"5.9.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz\",\r\n      \"integrity\": \"sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"bin\": {\r\n        \"tsc\": \"bin/tsc\",\r\n        \"tsserver\": \"bin/tsserver\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.17\"\r\n      }\r\n    },\r\n    \"node_modules/undici\": {\r\n      \"version\": \"7.24.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/undici/-/undici-7.24.8.tgz\",\r\n      \"integrity\": \"sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20.18.1\"\r\n      }\r\n    },\r\n    \"node_modules/undici-types\": {\r\n      \"version\": \"6.21.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz\",\r\n      \"integrity\": \"sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/unenv\": {\r\n      \"version\": \"2.0.0-rc.24\",\r\n      \"resolved\": \"https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz\",\r\n      \"integrity\": \"sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"pathe\": \"^2.0.3\"\r\n      }\r\n    },\r\n    \"node_modules/unicode-trie\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"pako\": \"^0.2.5\",\r\n        \"tiny-inflate\": \"^1.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/unicorn-magic\": {\r\n      \"version\": \"0.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz\",\r\n      \"integrity\": \"sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/universalify\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 10.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/unpic\": {\r\n      \"version\": \"4.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/unpic/-/unpic-4.2.2.tgz\",\r\n      \"integrity\": \"sha512-z6T2ScMgRV2y2H8MwwhY5xHZWXhUx/YxtOCGJwfURSl7ypVy4HpLIMWoIZKnnxQa/RKzM0kg8hUh0paIrpLfvw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/unpipe\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/update-browserslist-db\": {\r\n      \"version\": \"1.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz\",\r\n      \"integrity\": \"sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==\",\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/browserslist\"\r\n        },\r\n        {\r\n          \"type\": \"tidelift\",\r\n          \"url\": \"https://tidelift.com/funding/github/npm/browserslist\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"escalade\": \"^3.2.0\",\r\n        \"picocolors\": \"^1.1.1\"\r\n      },\r\n      \"bin\": {\r\n        \"update-browserslist-db\": \"cli.js\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"browserslist\": \">= 4.21.0\"\r\n      }\r\n    },\r\n    \"node_modules/use-callback-ref\": {\r\n      \"version\": \"1.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz\",\r\n      \"integrity\": \"sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/use-sidecar\": {\r\n      \"version\": \"1.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz\",\r\n      \"integrity\": \"sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"detect-node-es\": \"^1.1.0\",\r\n        \"tslib\": \"^2.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/react\": \"*\",\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/react\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/use-sync-external-store\": {\r\n      \"version\": \"1.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz\",\r\n      \"integrity\": \"sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==\",\r\n      \"license\": \"MIT\",\r\n      \"peerDependencies\": {\r\n        \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/util-deprecate\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/validate-npm-package-name\": {\r\n      \"version\": \"7.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz\",\r\n      \"integrity\": \"sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \"^20.17.0 || >=22.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/vary\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/vary/-/vary-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/victory-vendor\": {\r\n      \"version\": \"37.3.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz\",\r\n      \"integrity\": \"sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==\",\r\n      \"license\": \"MIT AND ISC\",\r\n      \"dependencies\": {\r\n        \"@types/d3-array\": \"^3.0.3\",\r\n        \"@types/d3-ease\": \"^3.0.0\",\r\n        \"@types/d3-interpolate\": \"^3.0.1\",\r\n        \"@types/d3-scale\": \"^4.0.2\",\r\n        \"@types/d3-shape\": \"^3.1.0\",\r\n        \"@types/d3-time\": \"^3.0.0\",\r\n        \"@types/d3-timer\": \"^3.0.0\",\r\n        \"d3-array\": \"^3.1.6\",\r\n        \"d3-ease\": \"^3.0.1\",\r\n        \"d3-interpolate\": \"^3.0.1\",\r\n        \"d3-scale\": \"^4.0.2\",\r\n        \"d3-shape\": \"^3.1.0\",\r\n        \"d3-time\": \"^3.0.0\",\r\n        \"d3-timer\": \"^3.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/vinext\": {\r\n      \"version\": \"1.0.0-beta.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/vinext/-/vinext-1.0.0-beta.5.tgz\",\r\n      \"integrity\": \"sha512-c0I1UB70/pGwD9y4/iXWrGfz/LnowSbkQpUw+lK9t08tes5WHX7Vl0sZAY4MIzGaVCyvrD4/xZo7HWpMje6SwA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@unpic/react\": \"^1.0.2\",\r\n        \"@vercel/og\": \"^0.8.6\",\r\n        \"@vinext/types\": \"^1.0.0-beta.2\",\r\n        \"image-size\": \"2.0.2\",\r\n        \"ipaddr.js\": \"^2.1.0\",\r\n        \"magic-string\": \"^0.30.21\",\r\n        \"vite-plugin-commonjs\": \"^0.10.4\",\r\n        \"web-vitals\": \"^4.2.4\"\r\n      },\r\n      \"bin\": {\r\n        \"vinext\": \"dist/cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@mdx-js/rollup\": \"^3.0.0\",\r\n        \"@vitejs/plugin-react\": \"^5.1.4 || ^6.0.0\",\r\n        \"@vitejs/plugin-rsc\": \"^0.5.26\",\r\n        \"react\": \"^19.2.6\",\r\n        \"react-dom\": \"^19.2.6\",\r\n        \"react-server-dom-webpack\": \"^19.2.6\",\r\n        \"vite\": \"^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@mdx-js/rollup\": {\r\n          \"optional\": true\r\n        },\r\n        \"@vitejs/plugin-rsc\": {\r\n          \"optional\": true\r\n        },\r\n        \"react-server-dom-webpack\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/vinext/node_modules/ipaddr.js\": {\r\n      \"version\": \"2.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz\",\r\n      \"integrity\": \"sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 10\"\r\n      }\r\n    },\r\n    \"node_modules/vite\": {\r\n      \"version\": \"8.0.13\",\r\n      \"resolved\": \"https://registry.npmjs.org/vite/-/vite-8.0.13.tgz\",\r\n      \"integrity\": \"sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"lightningcss\": \"^1.32.0\",\r\n        \"picomatch\": \"^4.0.4\",\r\n        \"postcss\": \"^8.5.14\",\r\n        \"rolldown\": \"1.0.1\",\r\n        \"tinyglobby\": \"^0.2.16\"\r\n      },\r\n      \"bin\": {\r\n        \"vite\": \"bin/vite.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/vitejs/vite?sponsor=1\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"fsevents\": \"~2.3.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/node\": \"^20.19.0 || >=22.12.0\",\r\n        \"@vitejs/devtools\": \"^0.1.18\",\r\n        \"esbuild\": \"^0.27.0 || ^0.28.0\",\r\n        \"jiti\": \">=1.21.0\",\r\n        \"less\": \"^4.0.0\",\r\n        \"sass\": \"^1.70.0\",\r\n        \"sass-embedded\": \"^1.70.0\",\r\n        \"stylus\": \">=0.54.8\",\r\n        \"sugarss\": \"^5.0.0\",\r\n        \"terser\": \"^5.16.0\",\r\n        \"tsx\": \"^4.8.1\",\r\n        \"yaml\": \"^2.4.2\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/node\": {\r\n          \"optional\": true\r\n        },\r\n        \"@vitejs/devtools\": {\r\n          \"optional\": true\r\n        },\r\n        \"esbuild\": {\r\n          \"optional\": true\r\n        },\r\n        \"jiti\": {\r\n          \"optional\": true\r\n        },\r\n        \"less\": {\r\n          \"optional\": true\r\n        },\r\n        \"sass\": {\r\n          \"optional\": true\r\n        },\r\n        \"sass-embedded\": {\r\n          \"optional\": true\r\n        },\r\n        \"stylus\": {\r\n          \"optional\": true\r\n        },\r\n        \"sugarss\": {\r\n          \"optional\": true\r\n        },\r\n        \"terser\": {\r\n          \"optional\": true\r\n        },\r\n        \"tsx\": {\r\n          \"optional\": true\r\n        },\r\n        \"yaml\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/vite-plugin-commonjs\": {\r\n      \"version\": \"0.10.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz\",\r\n      \"integrity\": \"sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"acorn\": \"^8.12.1\",\r\n        \"magic-string\": \"^0.30.11\",\r\n        \"vite-plugin-dynamic-import\": \"^1.6.0\"\r\n      }\r\n    },\r\n    \"node_modules/vite-plugin-dynamic-import\": {\r\n      \"version\": \"1.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.6.0.tgz\",\r\n      \"integrity\": \"sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"acorn\": \"^8.12.1\",\r\n        \"es-module-lexer\": \"^1.5.4\",\r\n        \"fast-glob\": \"^3.3.2\",\r\n        \"magic-string\": \"^0.30.11\"\r\n      }\r\n    },\r\n    \"node_modules/vite-plugin-dynamic-import/node_modules/es-module-lexer\": {\r\n      \"version\": \"1.7.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz\",\r\n      \"integrity\": \"sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==\",\r\n      \"license\": \"MPL-2.0\",\r\n      \"dependencies\": {\r\n        \"detect-libc\": \"^2.0.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"lightningcss-android-arm64\": \"1.33.0\",\r\n        \"lightningcss-darwin-arm64\": \"1.33.0\",\r\n        \"lightningcss-darwin-x64\": \"1.33.0\",\r\n        \"lightningcss-freebsd-x64\": \"1.33.0\",\r\n        \"lightningcss-linux-arm-gnueabihf\": \"1.33.0\",\r\n        \"lightningcss-linux-arm64-gnu\": \"1.33.0\",\r\n        \"lightningcss-linux-arm64-musl\": \"1.33.0\",\r\n        \"lightningcss-linux-x64-gnu\": \"1.33.0\",\r\n        \"lightningcss-linux-x64-musl\": \"1.33.0\",\r\n        \"lightningcss-win32-arm64-msvc\": \"1.33.0\",\r\n        \"lightningcss-win32-x64-msvc\": \"1.33.0\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-android-arm64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-darwin-arm64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-darwin-x64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-freebsd-x64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-linux-arm64-gnu\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-linux-arm64-musl\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-linux-x64-gnu\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-linux-x64-musl\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-win32-arm64-msvc\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vite/node_modules/lightningcss-win32-x64-msvc\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/vitefu\": {\r\n      \"version\": \"1.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz\",\r\n      \"integrity\": \"sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==\",\r\n      \"devOptional\": true,\r\n      \"license\": \"MIT\",\r\n      \"workspaces\": [\r\n        \"tests/deps/*\",\r\n        \"tests/projects/*\",\r\n        \"tests/projects/workspace/packages/*\"\r\n      ],\r\n      \"peerDependencies\": {\r\n        \"vite\": \"^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"vite\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/watchpack\": {\r\n      \"version\": \"2.5.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz\",\r\n      \"integrity\": \"sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"graceful-fs\": \"^4.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10.13.0\"\r\n      }\r\n    },\r\n    \"node_modules/web-vitals\": {\r\n      \"version\": \"4.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz\",\r\n      \"integrity\": \"sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==\",\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/webpack\": {\r\n      \"version\": \"5.110.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/webpack/-/webpack-5.110.3.tgz\",\r\n      \"integrity\": \"sha512-GuizBzRvo9YPpyoNMf3ag7AzxbaW85qrRSqTha345KyJbAFPt3/cMzBM0h+RWg7SK/7DdzRLINP3LvQ0hvr4hg==\",\r\n      \"license\": \"MIT\",\r\n      \"peer\": true,\r\n      \"dependencies\": {\r\n        \"@types/estree\": \"^1.0.8\",\r\n        \"@types/json-schema\": \"^7.0.15\",\r\n        \"@webassemblyjs/ast\": \"^1.14.1\",\r\n        \"@webassemblyjs/wasm-edit\": \"^1.14.1\",\r\n        \"@webassemblyjs/wasm-parser\": \"^1.14.1\",\r\n        \"acorn\": \"^8.16.0\",\r\n        \"browserslist\": \"^4.28.1\",\r\n        \"chrome-trace-event\": \"^1.0.2\",\r\n        \"enhanced-resolve\": \"^5.24.4\",\r\n        \"es-module-lexer\": \"^2.1.0\",\r\n        \"events\": \"^3.2.0\",\r\n        \"graceful-fs\": \"^4.2.11\",\r\n        \"mime-db\": \"^1.54.0\",\r\n        \"minimizer-webpack-plugin\": \"^5.7.0\",\r\n        \"neo-async\": \"^2.6.2\",\r\n        \"schema-utils\": \"^4.3.3\",\r\n        \"tapable\": \"^2.3.0\",\r\n        \"watchpack\": \"^2.5.2\",\r\n        \"webpack-sources\": \"^3.5.1\"\r\n      },\r\n      \"bin\": {\r\n        \"webpack\": \"bin/webpack.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10.13.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/webpack\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"webpack-cli\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/webpack-sources\": {\r\n      \"version\": \"3.5.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz\",\r\n      \"integrity\": \"sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10.13.0\"\r\n      }\r\n    },\r\n    \"node_modules/which\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/which/-/which-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"isexe\": \"^3.1.1\"\r\n      },\r\n      \"bin\": {\r\n        \"node-which\": \"bin/which.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^16.13.0 || >=18.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/workerd\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/workerd/-/workerd-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-MjKOJLcvU45xXedQowvuiHtJTxu4WTHYQeIlF7YmjuqhiI6dImTFxWCEoRQHiskztxuVSNEmdO7/0UfDu6OMnQ==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"bin\": {\r\n        \"workerd\": \"bin/workerd\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@cloudflare/workerd-darwin-64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-darwin-arm64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-linux-64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-linux-arm64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-windows-64\": \"1.20260515.1\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler\": {\r\n      \"version\": \"4.92.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/wrangler/-/wrangler-4.92.0.tgz\",\r\n      \"integrity\": \"sha512-/DKpQHPxkuZbQsO9dFW2700VTD/4DSZMHjy92fO/frNoDRi/zQsFCAd2ONCV6TGqcUoXcP3D8Bo2gj/L4M0qQQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"@cloudflare/kv-asset-handler\": \"0.5.0\",\r\n        \"@cloudflare/unenv-preset\": \"2.16.1\",\r\n        \"blake3-wasm\": \"2.1.5\",\r\n        \"esbuild\": \"0.27.3\",\r\n        \"miniflare\": \"4.20260515.0\",\r\n        \"path-to-regexp\": \"6.3.0\",\r\n        \"unenv\": \"2.0.0-rc.24\",\r\n        \"workerd\": \"1.20260515.1\"\r\n      },\r\n      \"bin\": {\r\n        \"wrangler\": \"bin/wrangler.js\",\r\n        \"wrangler2\": \"bin/wrangler.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"fsevents\": \"~2.3.2\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@cloudflare/workers-types\": \"^4.20260515.1\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@cloudflare/workers-types\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/path-to-regexp\": {\r\n      \"version\": \"6.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz\",\r\n      \"integrity\": \"sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/wrappy\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/ws\": {\r\n      \"version\": \"8.18.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ws/-/ws-8.18.0.tgz\",\r\n      \"integrity\": \"sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"bufferutil\": \"^4.0.1\",\r\n        \"utf-8-validate\": \">=5.0.2\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"bufferutil\": {\r\n          \"optional\": true\r\n        },\r\n        \"utf-8-validate\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/wsl-utils\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"is-wsl\": \"^3.1.0\",\r\n        \"powershell-utils\": \"^0.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/wsl-utils/node_modules/powershell-utils\": {\r\n      \"version\": \"0.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz\",\r\n      \"integrity\": \"sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/yallist\": {\r\n      \"version\": \"3.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz\",\r\n      \"integrity\": \"sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/yocto-spinner\": {\r\n      \"version\": \"1.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-1.2.2.tgz\",\r\n      \"integrity\": \"sha512-DODGl1wJjA/s5pnJFKau9lIYHT81lnhob1i3e1TjxZRxEhWRKl74nTbWE6H5KlkViQQTo/Z29YFdxzTZAMY3ng==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"yoctocolors\": \"^2.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18.19\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/yoctocolors\": {\r\n      \"version\": \"2.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz\",\r\n      \"integrity\": \"sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/yoga-layout\": {\r\n      \"version\": \"3.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz\",\r\n      \"integrity\": \"sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/youch\": {\r\n      \"version\": \"4.1.0-beta.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz\",\r\n      \"integrity\": \"sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/colors\": \"^4.1.5\",\r\n        \"@poppinss/dumper\": \"^0.6.4\",\r\n        \"@speed-highlight/core\": \"^1.2.7\",\r\n        \"cookie\": \"^1.0.2\",\r\n        \"youch-core\": \"^0.3.3\"\r\n      }\r\n    },\r\n    \"node_modules/youch-core\": {\r\n      \"version\": \"0.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz\",\r\n      \"integrity\": \"sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/exception\": \"^1.2.2\",\r\n        \"error-stack-parser-es\": \"^1.0.5\"\r\n      }\r\n    },\r\n    \"node_modules/youch/node_modules/cookie\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/zod\": {\r\n      \"version\": \"3.25.76\",\r\n      \"resolved\": \"https://registry.npmjs.org/zod/-/zod-3.25.76.tgz\",\r\n      \"integrity\": \"sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/colinhacks\"\r\n      }\r\n    },\r\n    \"node_modules/zod-to-json-schema\": {\r\n      \"version\": \"3.25.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz\",\r\n      \"integrity\": \"sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==\",\r\n      \"license\": \"ISC\",\r\n      \"peerDependencies\": {\r\n        \"zod\": \"^3.25.28 || ^4\"\r\n      }\r\n    }\r\n  }\r\n}\r\n","handborne/source/package.json":"{\r\n  \"name\": \"sites-project\",\r\n  \"version\": \"0.1.0\",\r\n  \"private\": true,\r\n  \"engines\": {\r\n    \"node\": \">=22.13.0\"\r\n  },\r\n  \"scripts\": {\r\n    \"predev\": \"node scripts/build-downloads.mjs\",\r\n    \"dev\": \"vinext dev\",\r\n    \"build\": \"node scripts/build.mjs\",\r\n    \"start\": \"node scripts/serve.mjs\",\r\n    \"lint\": \"oxlint\",\r\n    \"format\": \"oxfmt\"\r\n  },\r\n  \"dependencies\": {\r\n    \"@base-ui/react\": \"1.7.0\",\r\n    \"@shadcn/react\": \"0.3.0\",\r\n    \"class-variance-authority\": \"0.7.1\",\r\n    \"clsx\": \"2.1.1\",\r\n    \"cmdk\": \"1.1.1\",\r\n    \"date-fns\": \"4.1.0\",\r\n    \"embla-carousel-react\": \"8.5.2\",\r\n    \"input-otp\": \"1.4.2\",\r\n    \"lucide-react\": \"1.31.0\",\r\n    \"react\": \"19.2.6\",\r\n    \"react-day-picker\": \"9.8.1\",\r\n    \"react-dom\": \"19.2.6\",\r\n    \"react-resizable-panels\": \"4.5.8\",\r\n    \"react-server-dom-webpack\": \"19.2.6\",\r\n    \"recharts\": \"3.8.0\",\r\n    \"shadcn\": \"4.18.0\",\r\n    \"tailwind-merge\": \"3.6.0\",\r\n    \"three\": \"^0.180.0\",\r\n    \"tw-animate-css\": \"1.4.0\",\r\n    \"vinext\": \"1.0.0-beta.5\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"@cloudflare/vite-plugin\": \"1.37.1\",\r\n    \"@cloudflare/workers-types\": \"4.20260515.1\",\r\n    \"@openai/sites-vite-plugin\": \"0.2.0\",\r\n    \"@tailwindcss/postcss\": \"4.2.1\",\r\n    \"@types/node\": \"22.19.19\",\r\n    \"@types/react\": \"19.2.14\",\r\n    \"@types/react-dom\": \"19.2.3\",\r\n    \"@types/three\": \"^0.180.0\",\r\n    \"@vitejs/plugin-react\": \"6.0.2\",\r\n    \"@vitejs/plugin-rsc\": \"0.5.26\",\r\n    \"oxfmt\": \"0.61.0\",\r\n    \"oxlint\": \"1.76.0\",\r\n    \"oxlint-tsgolint\": \"7.0.2001\",\r\n    \"tailwindcss\": \"4.2.1\",\r\n    \"typescript\": \"5.9.3\",\r\n    \"vite\": \"8.0.13\",\r\n    \"wrangler\": \"4.92.0\"\r\n  },\r\n  \"type\": \"module\"\r\n}\r\n","handborne/source/scripts/build-embedded.mjs":"import {build} from 'vite';\r\nimport tailwindcss from '@tailwindcss/postcss';\r\nimport react from '@vitejs/plugin-react';\r\nimport {rename,readFile,writeFile,cp,mkdir,readdir} from 'node:fs/promises';\r\nimport {fileURLToPath} from 'node:url';\r\nawait build({configFile:false,base:'/handborne/',resolve:{alias:{'@':fileURLToPath(new URL('../',import.meta.url))}},css:{postcss:{plugins:[tailwindcss()]}},plugins:[react()],build:{copyPublicDir:false,outDir:'dist/embedded',rollupOptions:{input:'hand-entry.html'}}});\r\nawait rename('dist/embedded/hand-entry.html','dist/embedded/index.html');\r\nconst {build:bundle}=await import('esbuild');\r\nawait bundle({entryPoints:['app/companion.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'dist/embedded/companion.mjs'});\r\nawait bundle({entryPoints:['app/unlock-bridge.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'dist/embedded/unlock-bridge.mjs'});\r\n// Assets are already published beside the recovered authoring source.\r\nfor(const dir of ['fonts','previews'])await cp('../'+dir,'dist/embedded/'+dir,{recursive:true});\r\nawait mkdir('dist/embedded/models',{recursive:true});\r\nfor(const name of await readdir('../models'))if(/^family-\\d+\\.glb$/.test(name))await cp('../models/'+name,'dist/embedded/models/'+name);\r\nconsole.log('Hand editor and companion ready.');\r\n","handborne/source/tsconfig.json":"{\r\n  \"compilerOptions\": {\r\n    \"target\": \"ES2017\",\r\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\r\n    \"allowJs\": true,\r\n    \"allowImportingTsExtensions\": true,\r\n    \"skipLibCheck\": true,\r\n    \"strict\": true,\r\n    \"noEmit\": true,\r\n    \"esModuleInterop\": true,\r\n    \"module\": \"esnext\",\r\n    \"moduleResolution\": \"bundler\",\r\n    \"types\": [\"node\", \"@cloudflare/workers-types\", \"vinext/types\"],\r\n    \"resolveJsonModule\": true,\r\n    \"isolatedModules\": true,\r\n    \"jsx\": \"react-jsx\",\r\n    \"incremental\": true,\r\n    \"paths\": {\r\n      \"@/*\": [\"./*\"]\r\n    }\r\n  },\r\n  \"include\": [\r\n    \"next-env.d.ts\",\r\n    \"**/*.ts\",\r\n    \"**/*.tsx\",\r\n    \".next/types/**/*.ts\",\r\n    \".next/dev/types/**/*.ts\",\r\n    \"**/*.mts\"\r\n  ],\r\n  \"exclude\": [\"node_modules\"]\r\n}\r\n","handborne/unlock-bridge.mjs":"var ze=Object.defineProperty;var O=(e,a)=>()=>(e&&(a=e(e=0)),a);var je=(e,a)=>{for(var r in a)ze(e,r,{get:a[r],enumerable:!0})};function x(e=globalThis.localStorage){try{let a=JSON.parse(e?.getItem(H)||\"null\");return U(a)?a:null}catch{return null}}function K(e,a=globalThis.localStorage){let r=p??x(a);return r?.gameGeneration>1?`${e}/game/${r.gameGeneration}`:e}function X(){if(!p)throw Object.assign(Error(\"Game metadata is still loading.\"),{code:\"game_generation_required\"});return p.gameGeneration}function He(e=globalThis.localStorage){if(!e)return;let a=Array.from({length:e.length},(r,t)=>e.key(t));for(let r of a)/^(?:myr5-roguelite-preview-v1\\/|myr5-world-run-outbox-v1\\/)/.test(r)&&e.removeItem(r)}function W(e,{storage:a=globalThis.localStorage}={}){if(!U(e))throw b();let r=p??x(a);if(r&&e.gameGeneration<r.gameGeneration)throw b(r);let t={gameGeneration:e.gameGeneration,gameResetAt:e.gameResetAt};if(r&&r.gameGeneration===t.gameGeneration&&r.gameResetAt!==t.gameResetAt)throw b(r);if(p=t,(!r||r.gameGeneration!==t.gameGeneration)&&(t.gameResetAt>0&&He(a),a?.setItem(H,JSON.stringify(t)),r)){for(let o of qe)o(t);globalThis.dispatchEvent?.(new CustomEvent(\"myr5:game-generation-changed\",{detail:t}))}return t}async function me({fetchImpl:e,signal:a,storage:r=globalThis.localStorage,force:t=!1}={}){if(e||(e=(await Promise.resolve().then(()=>($(),ge))).authFetch),E&&!t)return E;let o=(async()=>{let s=await e(\"/api/game-generation\",{signal:a}),i=await s.json();if(!s.ok)throw Object.assign(Error(i.error||\"Game metadata unavailable.\"),{status:s.status});return W(i,{storage:r})})();E=o;try{return await o}finally{E===o&&(E=null)}}function D(e,a,{storage:r=globalThis.localStorage}={}){if(!U(e)||p&&e.gameGeneration<p.gameGeneration)throw b(p);let t=a!=null&&e.gameGeneration!==a;if(W(e,{storage:r}),t)throw b(e);return e}var H,p,E,qe,U,b,ue,he,T=O(()=>{H=\"myr5-game-generation-v1\",p=null,E=null,qe=new Set,U=e=>Number.isSafeInteger(e?.gameGeneration)&&e.gameGeneration>=1&&Number.isSafeInteger(e?.gameResetAt)&&e.gameResetAt>=0,b=e=>Object.assign(Error(\"Game progress changed. Reopen the game.\"),{code:\"game_generation_mismatch\",status:409,...e});ue=e=>{if(!Number.isSafeInteger(e)||e<1)throw b();return{\"X-MYR5-Game-Generation\":String(e)}};he=e=>/^\\/api\\/(?:account(?:\\?|$)|world(?:\\/|\\?|$)|war-room(?:\\/|\\?|$))/.test(e);typeof globalThis.window==\"object\"&&typeof globalThis.addEventListener==\"function\"&&globalThis.addEventListener(\"storage\",e=>{if(!(e.key!==H||!e.newValue))try{W(JSON.parse(e.newValue))}catch{}})});function Ke({storage:e,events:a,channelFactory:r,randomUUID:t}){let o,s=0,i=0,m=new AbortController,u,c,d=!1,l,g=new Set,R=()=>{for(let n of g)try{n()}catch{}},L=()=>{s++,i++,m.abort(),u?.abort(),m=new AbortController,R()},k=()=>{throw d||(d=!0,L()),new w(\"auth_unavailable\")},Be=()=>{try{let n=e.getItem(y);return I(n)?n:k()}catch{return k()}},M=()=>{if(d)throw new w(\"auth_unavailable\");let n=Be();return n!==o&&(o=n,L()),n},j=()=>{if(d)throw new w(\"auth_unavailable\");L();try{let n=t();return!I(n)||(e.setItem(y,n),e.getItem(y)!==n)?k():(o=n,c.postMessage({type:\"transition\",token:n}),n)}catch{return k()}},le=n=>{try{n.key===y||n.key===null?M():n.key===Ue&&j()}catch{}},Pe=()=>{try{M()}catch{}};try{if(!e||!a?.addEventListener||typeof r!=\"function\"||typeof t!=\"function\")throw Error();if(o=e.getItem(y),o===null){if(o=t(),!I(o))throw Error();e.setItem(y,o),o=e.getItem(y)}if(!I(o)||(c=r(\"myr5-auth-transition-v1\"),!c?.addEventListener||!c?.postMessage))throw Error();c.addEventListener(\"message\",Pe),c.addEventListener(\"messageerror\",()=>{try{k()}catch{}}),a.addEventListener(\"storage\",le)}catch{d=!0,m.abort()}let ce=()=>{let n=M();return Object.freeze({token:n,revision:s,signal:m.signal})},de=n=>{try{return!!n&&M()===n.token&&n.revision===s&&!n.signal.aborted&&(n.refreshGeneration===void 0||n.refreshGeneration===i)}catch{return!1}};return Object.freeze({capture:ce,isCurrent:de,invalidate:j,assertCurrent(n){if(!de(n))throw new w(d?\"auth_unavailable\":\"auth_transition\")},beginRefresh(){let n=ce();return u?.abort(),u=new AbortController,Object.freeze({...n,refreshGeneration:++i,signal:u.signal})},observeIdentity(n,Ne,Ge){let q=JSON.stringify([n,Ne??null,Ge??null]);if(l===void 0){l=q;return}l!==q&&(l=q,j())},subscribe(n){return g.add(n),()=>g.delete(n)},close(){d||(d=!0,L()),a?.removeEventListener?.(\"storage\",le),c?.close?.()}})}function C(){if(!J){let e;try{e=globalThis.localStorage}catch{}J=Ke({storage:e,events:globalThis.window,channelFactory:typeof globalThis.BroadcastChannel==\"function\"?a=>new BroadcastChannel(a):void 0,randomUUID:()=>globalThis.crypto.randomUUID()})}return J}var y,Ue,I,w,J,pe=O(()=>{y=\"myr5-auth-transition-v1\",Ue=\"myr5-login-provider\",I=e=>typeof e==\"string\"&&/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(e),w=class extends Error{constructor(a=\"auth_transition\"){super(a===\"auth_unavailable\"?\"Account synchronization is unavailable. Reload to retry.\":\"Account changed. Refresh to continue.\"),this.name=\"AuthTransitionError\",this.code=a}}});function fe(e,a=\"/pose.html\"){if(typeof e!=\"string\"||!e.startsWith(\"/\")||e.startsWith(\"//\")||/[\\\\\\r\\n]/.test(e))return a;let r=new URL(e,\"https://coach.invalid\");return r.origin===\"https://coach.invalid\"&&!/^\\/(signin|signout|callback)/.test(r.pathname)?r.pathname+r.search+r.hash:a}var be=O(()=>{});var ge={};je(ge,{authFetch:()=>N,authSettings:()=>V,loadLogin:()=>A,signOut:()=>Xe});function V({force:e=!1}={}){e&&(B++,F=null);let a=B;return F??=(async()=>{let r=await fetch(`/api/auth/config?t=${Date.now()}`,{credentials:\"same-origin\",cache:\"no-store\",signal:AbortSignal.timeout(5e3)});if(!r.ok)throw Object.assign(Error(\"Login could not connect. Please retry.\"),{status:r.status,code:\"auth-config\"});return r.json()})().then(r=>(B===a&&(r.enabled?F=r:F=null),r)).catch(r=>{throw B===a&&(F=null),r})}function _e(e,a){return new Promise((r,t)=>{let o=document.createElement(\"script\");o.src=e,o.crossOrigin=\"anonymous\",a&&(o.dataset.clerkPublishableKey=a),o.onload=()=>{r()},o.onerror=()=>t(Error(\"Login could not load. Check your connection and retry.\")),document.head.append(o)})}function A(){if(f)return f;let e=++_,a=null,r=()=>{a&&(clearTimeout(a),a=null)},t=new Promise((o,s)=>{let i=Object.assign(new Error(\"Account timed out\"),{code:\"account_timeout\"}),m=(async()=>{try{let u;try{if(u=await V(),_!==e)throw i}catch(R){throw r(),R}if(!u.enabled)return r(),null;let c=new URL(u.frontend).origin;if(await _e(c+\"/npm/@clerk/ui@1/dist/ui.browser.js\"),_!==e||(await _e(c+\"/npm/@clerk/clerk-js@6/dist/clerk.browser.js\",u.publishableKey),_!==e))throw i;let d=window.Clerk;if(await d.load({ui:{ClerkUI:window.__internal_ClerkUICtor}}),_!==e)throw i;let l=d;C().observeIdentity(\"clerk\",l.session?.id,l.user?.id),P&&(P(),P=null);let g=()=>{_===e&&C().observeIdentity(\"clerk\",l.session?.id,l.user?.id)};P=l.addListener(g),ye||(window.dispatchEvent(new Event(\"myr5:login-ready\")),ye=!0),o(l)}catch(u){r(),s(u)}})();a=setTimeout(()=>{f===t&&(_++,r(),s(i))},2e4),m.then(o,s).finally(r)});return f=t,f.then(o=>{r(),o===null&&f===t&&(f=null)},()=>{r(),f===t&&(f=null)}),t}function Y(e,a,r=5e3){return new Promise((t,o)=>{let s=!1,i=(c,d)=>{s||(s=!0,clearTimeout(u),a.removeEventListener(\"abort\",m),c(d))},m=()=>i(o,Object.assign(Error(\"Account changed. Refresh to continue.\"),{code:\"auth_transition\"})),u=setTimeout(()=>i(o,Object.assign(Error(\"Account service timed out. Please retry.\"),{status:503,code:\"account_timeout\"})),r);a.addEventListener(\"abort\",m,{once:!0}),a.aborted&&m(),Promise.resolve(e).then(c=>i(t,c),c=>i(o,c))})}async function N(e,a={}){let r=C(),t=r.capture(),o=new Headers(a.headers),s=he(e),i=null;if(s){let l=o.get(\"X-MYR5-Game-Generation\");if(l!==null)i=/^[1-9][0-9]*$/.test(l)?Number(l):null;else{try{i=X()}catch{await me({fetchImpl:N,signal:a.signal}),i=X()}for(let[g,R]of Object.entries(ue(i)))o.set(g,R)}}let m=await Y(V(),t.signal);r.assertCurrent(t);let u=localStorage.getItem(\"myr5-login-provider\");if(a.guestSkip&&u!==\"clerk\")return new Response(JSON.stringify({error:\"Sign in to save your progress.\"}),{status:401,headers:{\"Content-Type\":\"application/json\"}});if(m.enabled&&u===\"clerk\"){let l=await Y(A(),t.signal,2e4);r.assertCurrent(t),r.observeIdentity(\"clerk\",l?.session?.id,l?.user?.id),r.assertCurrent(t);let g=await Y(l?.session?.getToken(),t.signal);if(r.observeIdentity(\"clerk\",l?.session?.id,l?.user?.id),r.assertCurrent(t),!g)throw Object.assign(Error(\"Sign in to Coach again.\"),{status:401});o.set(\"Authorization\",\"Bearer \"+g)}r.assertCurrent(t);let c=AbortSignal.any([t.signal,...a.signal?[a.signal]:[],AbortSignal.timeout(8e3)]),d=await fetch(e,{...a,signal:c,headers:o,credentials:\"same-origin\",cache:\"no-store\"});if(r.assertCurrent(t),s||[\"/api/workouts/complete\",\"/api/breathing/complete\"].includes(e)){let l=await d.clone().json();l.gameGeneration!=null&&(d.ok||l.code===\"game_generation_mismatch\")?D(l,s?i:void 0):s&&d.ok&&D(l,i)}return d}async function Xe(e=\"/pose.html\"){try{C().invalidate()}catch{}let a,r;try{a=localStorage.getItem(\"myr5-login-provider\")}catch{}try{await Promise.race([(async()=>{await(window.Clerk||(a===\"clerk\"?await A():null))?.signOut()})(),new Promise(t=>{r=setTimeout(t,3e3)})])}catch{}finally{clearTimeout(r);try{localStorage.removeItem(\"myr5-login-provider\")}catch{}location.assign(fe(e))}}var F,B,f,_,P,ye,$=O(()=>{T();pe();be();B=0,f=null,_=0,P=null,ye=!1});$();T();T();var ke=Object.freeze({jogging:{name:\"Subway Burpees\",types:[\"cardio\",\"legs\"]},\"plank-flight\":{name:\"Jet Plank Joyride\",types:[\"core\",\"balance\"]},cover:{name:\"Crouching Crisis\",types:[\"legs\",\"boxing\"]},dance:{name:\"Dance, Dance, and Obey\",types:[\"cardio\",\"martial-arts\"]}}),We=[{id:\"chest\",name:\"Chest\",description:\"Push-up progressions\"},{id:\"legs\",name:\"Legs\",description:\"Squats and split squats\"},{id:\"hips\",name:\"Hips & glutes\",description:\"Hinges and bridges\"},{id:\"core\",name:\"Core\",description:\"Plank holds\"},{id:\"shoulders\",name:\"Shoulders\",description:\"Controlled arm movement\"},{id:\"balance\",name:\"Balance\",description:\"Single-leg yoga holds\"},{id:\"yoga\",name:\"Yoga\",description:\"Standing poses\"},{id:\"stances\",name:\"Martial stances\",description:\"Controlled stance holds\"},{id:\"boxing\",name:\"Boxing\",description:\"Slow shadowboxing\"},{id:\"cardio\",name:\"Cardio\",description:\"Marches and jacks\"}],v=[];function h(e,a,r,t,o={}){let s=o.kind??([\"plank\",\"sideplank\",\"balance\",\"yoga\",\"stance\"].includes(t)?\"hold\":t===\"boxing\"?\"pace\":t===\"march\"?\"steps\":\"reps\"),i=o.view??([\"pushup\",\"hinge\",\"bridge\",\"plank\",\"split\"].includes(t)?\"side\":\"front\"),m=s===\"hold\"?\"Pose hold\":s===\"pace\"?\"Active hand time\":s===\"steps\"?\"Knee lifts\":\"Repetitions\",u=o.limits??\"Counts the visible movement; does not grade technique.\",c=i===\"side\"?\"Camera beside you.\":i===\"angle\"?\"Camera at a slight angle.\":\"Face the camera.\";v.push({id:a,group:e,name:r,detector:t,kind:s,view:i,measurement:m,limits:u,difficulty:v.filter(d=>d.group===e).length+1,defaultGoal:s===\"hold\"||s===\"pace\"?15:5,evidence:\"rule-based; camera validation pending\",source:\"mediapipe\",...o,hint:`${c} ${o.cue??\"Keep the joints used for this movement visible.\"}`})}var $e=\"Counts elbow bend and return. Hand shape, support stability and chest depth are not verified.\";for(let[e,a,r]of[[\"knee-pushup\",\"Knee push-up\",{support:\"knees\",cue:\"Knees down; show shoulder, elbow, wrist and hip.\"}],[\"high-incline-pushup\",\"High incline push-up\",{incline:!0,cue:\"Hands on a stable high surface; show your full arm and torso.\"}],[\"low-incline-pushup\",\"Low incline push-up\",{incline:!0,cue:\"Hands on a stable lower surface; show your full arm and torso.\"}],[\"pushup\",\"Regular push-up\",{}],[\"wide-pushup\",\"Wide push-up\",{wide:!0}],[\"slow-pushup\",\"Slow push-up\",{minCycle:1.2}],[\"diamond-pushup\",\"Diamond push-up\",{narrow:!0}],[\"decline-pushup\",\"Feet-elevated push-up\",{decline:!0,cue:\"Feet on a stable low surface; show your full arm and torso.\"}]])h(\"chest\",e,a,\"pushup\",{cue:\"Show shoulder, elbow, wrist and hip. Start with your arm extended.\",limits:$e,...r});for(let[e,a,r]of[[\"shallow-squat\",\"Shallow squat\",{travel:.18}],[\"squat\",\"Bodyweight squat\",{}],[\"wide-squat\",\"Wide squat\",{wide:!0}],[\"pause-squat\",\"Pause squat\",{dwell:.8}],[\"slow-squat\",\"Slow squat\",{minCycle:1.2}],[\"split-left\",\"Split squat \\xB7 left\",{detector:\"split\",side:\"left\"}],[\"split-right\",\"Split squat \\xB7 right\",{detector:\"split\",side:\"right\"}],[\"wall-sit\",\"Wall sit\",{detector:\"yoga\",pose:\"wall-sit\",kind:\"hold\",view:\"side\",cue:\"Back on a wall, thighs level. Show shoulder, hip and knee.\",limits:\"Times the hip angle and thigh level. Wall contact and knee position are not tracked.\"}]])h(\"legs\",e,a,r.detector??\"squat\",{cue:r.side?\"Show the front leg from hip to knee. Start tall.\":\"Show shoulders and hips. Start standing tall.\",...r});for(let[e,a,r,t]of[[\"small-hinge\",\"Small hip hinge\",\"hinge\",{bend:25}],[\"hip-hinge\",\"Hip hinge\",\"hinge\",{}],[\"good-morning\",\"Bodyweight good morning\",\"hinge\",{arms:\"chest\"}],[\"glute-bridge\",\"Glute bridge\",\"bridge\",{}],[\"pause-bridge\",\"Pause glute bridge\",\"bridge\",{dwell:.8}]])h(\"hips\",e,a,r,{cue:r===\"bridge\"?\"Lie on your back, knees bent. Show shoulder, hip and knee. Start with hips down.\":\"Show shoulder, hip and knee. Start upright; hinge slowly.\",limits:\"Tracks hip movement. Back curvature and muscle engagement are not verified.\",...t});for(let[e,a,r]of[[\"knee-plank\",\"Knee plank\",{support:\"knees\"}],[\"high-plank\",\"High plank\",{}],[\"forearm-plank\",\"Forearm plank\",{forearm:!0}],[\"side-knee-left\",\"Side knee plank \\xB7 left\",{side:\"left\",support:\"knees\"}],[\"side-knee-right\",\"Side knee plank \\xB7 right\",{side:\"right\",support:\"knees\"}],[\"side-plank-left\",\"Side plank \\xB7 left\",{side:\"left\"}],[\"side-plank-right\",\"Side plank \\xB7 right\",{side:\"right\"}]])h(\"core\",e,a,r.side?\"sideplank\":\"plank\",{view:r.side?\"front\":\"side\",cue:r.side?\"Show both shoulders, hips and the supporting knee. Stack your shoulders.\":\"Show shoulder, elbow, wrist, hip and supporting knee.\",limits:\"Times the shoulder\\u2013hip\\u2013knee line. Feet, floor contact and loading are not tracked.\",...r});for(let[e,a,r,t]of[[\"front-raise\",\"Front arm raise\",\"raise\",{view:\"side\",plane:\"front\"}],[\"lateral-raise\",\"Lateral arm raise\",\"raise\",{}],[\"overhead-reach\",\"Overhead reach\",\"raise\",{overhead:!0}],[\"standing-press\",\"Standing arm press\",\"press\",{}],[\"slow-press\",\"Slow arm press\",\"press\",{minCycle:1.2}]])h(\"shoulders\",e,a,r,{cue:\"Keep both shoulders, elbows, wrists and hips visible. Use controlled, unloaded movement.\",...t});for(let[e,a,r]of[[\"knee-balance\",\"Knee-lift balance\",{kneeLift:!0}],[\"low-tree\",\"Low tree pose\",{low:!0}],[\"tree\",\"Tree pose\",{}],[\"overhead-tree\",\"Tree \\xB7 arms overhead\",{overhead:!0}]])h(\"balance\",e,a,\"balance\",{cue:\"Show both hips and knees. Either leg works; keep a support nearby if needed.\",limits:\"Times the visible leg shape. Balance, foot pressure and support use are not verified.\",...r});for(let[e,a,r]of[[\"mountain\",\"Mountain\",\"mountain\"],[\"salute\",\"Upward salute\",\"salute\"],[\"chair\",\"Chair pose\",\"chair\"],[\"warrior-one\",\"Warrior I\",\"warrior-one\"],[\"warrior\",\"Warrior II\",\"warrior\"],[\"goddess\",\"Goddess pose\",\"goddess\"]])h(\"yoga\",e,a,\"yoga\",{pose:r,view:[\"chair\",\"warrior-one\"].includes(r)?\"side\":\"front\",cue:\"Show arms, hips and knees. Hold only a comfortable position.\",limits:\"Times broad pose shape. Foot rotation and joint alignment are not graded.\"});for(let[e,a,r]of[[\"high-horse\",\"High horse stance\",{high:!0}],[\"horse\",\"Horse stance\",{}],[\"low-horse\",\"Lower horse stance\",{low:!0}],[\"front-stance-left\",\"Front stance \\xB7 left\",{side:\"left\"}],[\"front-stance-right\",\"Front stance \\xB7 right\",{side:\"right\"}]])h(\"stances\",e,a,\"stance\",{view:r.side?\"side\":\"front\",cue:\"Keep hips and knees visible. Move slowly into a comfortable stance.\",limits:\"Times stance shape only. This is not martial-arts technique instruction.\",...r});for(let[e,a,r]of[[\"jab-left\",\"Left straight practice\",{side:\"left\"}],[\"jab-right\",\"Right straight practice\",{side:\"right\"}],[\"boxing\",\"Alternating straights\",{}],[\"double-jab\",\"Double-jab practice\",{double:!0}]])h(\"boxing\",e,a,\"boxing\",{view:\"angle\",cue:\"Show shoulders, elbows, wrists and hips. Practice slow, controlled punches into empty space.\",limits:\"Tracks active hand time and relative pace. Punch type, power, accuracy and guard are not verified.\",...r});for(let[e,a,r,t]of[[\"march\",\"Easy march\",\"march\",{}],[\"high-march\",\"High-knee march\",\"march\",{lift:.4}],[\"jogging\",ke.jogging.name,\"march\",{lift:.08,runner:!0,endless:!0,cue:\"The route moves automatically. Crouch deeply to duck, raise both arms to jump, and lean left or right to change lanes. Keep your upper body visible.\"}],[\"step-jack\",\"Step jack\",\"jack\",{step:!0}],[\"jumping-jack\",\"Jumping jack\",\"jack\",{}]])h(\"cardio\",e,a,r,{cue:r===\"jack\"?\"Show your upper body and knees. Start with arms down and knees close together.\":\"Show shoulders, hips and both knees. Start standing tall.\",limits:r===\"jack\"?\"Counts arm-and-knee opening cycles. Feet, airtime and landings are not tracked.\":\"Counts knee lifts. Footfalls and impact are not verified.\",...t});h(\"core\",\"plank-flight\",ke[\"plank-flight\"].name,\"plank-flight\",{kind:\"hold\",view:\"front\",flight:!0,standalone:!0,endless:!0,cue:\"Phone faces you from a low stable stand. Keep both hands down, shoulders, arms, hips and knees visible. Rise/lower comfortably; lean left/right to fly.\",limits:\"Estimates face-on height and lean. Floor contact, arm loading and technique are not measured.\"});var we=Object.freeze(Object.fromEntries(v.map(e=>[e.id,Object.freeze(e)]))),Je=Object.freeze(Object.fromEntries(We.map(e=>[e.id,Object.freeze(v.filter(a=>a.group===e.id&&!a.standalone))]))),Oa=v.length;var Se=Object.freeze([\"roster/21-flyer--winged_humanoid_3d_model\",\"roster/18-quad-all--wooden_robot_3d_model\"]),Ye=[[\"myr5\",\"original\",[\"MYR5 Jacked LaLAN\",\"Jack LaLanne\",\"jacked strength\",\"local area network\"],[\"MYR5 AT-Lift Prime\",\"Charles Atlas\",\"lift\",\"model designation\"]],[\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"arms\",[\"Arnoid Press\",\"Arnold Schwarzenegger\",\"Arnold press\",\"android\"],[\"ScotTorque Curl\",\"Larry Scott\",\"curl\",\"motor torque\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"arms\",[\"Ferri-Delt L0U\",\"Lou Ferrigno\",\"delt\",\"robot designation\"],[\"R0N Curlman\",\"Ronnie Coleman\",\"curl\",\"engine manifold\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model\",\"arms\",[\"Frank Delt-Zan3\",\"Frank Zane\",\"delt\",\"magnetic coil\"],[\"Cutlerator Curl\",\"Jay Cutler\",\"curl\",\"accelerator\"]],[\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"arms\",[\"KAI-nematic Raise\",\"Kai Greene\",\"raise\",\"kinematic mechanism\"],[\"Philament Heave Press\",\"Phil Heath\",\"press\",\"electrical filament\"]],[\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"arms\",[\"Wheel-er Servo-Shrug\",\"Flex Wheeler\",\"shrug\",\"servo wheel\"]],[\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"arms\",[\"Ree-volts Raise\",\"Steve Reeves\",\"raise\",\"voltage\"]],[\"08-shard-asym--robot_3d_model4\",\"arms\",[\"S3RG Oliv-Armature\",\"Sergio Oliva\",\"arm\",\"motor armature\"]],[\"17-shellcap-manyarm--3d_character_figurine\",\"arms\",[\"Delt-alinn Bailey\",\"Dana Linn Bailey\",\"delt\",\"Delta designation\"]],[\"06-ridge-triad--geometric_robot_3d_model1\",\"chest\",[\"Oliva-C Pec\",\"Sergio Oliva\",\"pec\",\"vacuum drive\"]],[\"08-shard-asym--geometric_robot_3d_model\",\"chest\",[\"Pec-n0ld Reactor\",\"Arnold Schwarzenegger\",\"pec\",\"reactor\"]],[\"16-spade-arch--pyramid_head_figure_3d_model\",\"chest\",[\"Franx Pec-Zane\",\"Frank Zane\",\"pec\",\"motor drive\"],[\"Y4TES Yoke\",\"Dorian Yates\",\"press\",\"powered yoke\"]],[\"04-crest-wedge--robot_creature_3d_model\",\"chest\",[\"L0U Ferri-pecctor\",\"Lou Ferrigno\",\"pec\",\"reactor\"],[\"Wheeler Wheel-Bench\",\"Flex Wheeler\",\"bench\",\"drive wheel\"]],[\"16-spade-arch--stylized_humanoid_3d_model\",\"chest\",[\"Cutlerator Press\",\"Jay Cutler\",\"press\",\"accelerator\"]],[\"04-crest-wedge--stylized_3d_character\",\"chest\",[\"Gr33ne Grid-Press\",\"Kai Greene\",\"press\",\"power grid designation\"]],[\"09-monolith-tanka--3d_robot_model\",\"chest\",[\"chris BenchSTEAD\",\"Chris Bumstead\",\"bench\",\"robot\"]],[\"09-monolith-tanka--white_horned_robot_3d_model\",\"chest\",[\"Cavalier-EX Chest\",\"Jeff Cavaliere\",\"chest\",\"exercise model EX\"]],[\"15-orbital-coili--robot_character_3d_model\",\"quads\",[\"t-OHM Platzma\",\"Tom Platz\",\"squat\",\"plasma\"]],[\"13-fan-split--stylized_3d_character2\",\"quads\",[\"Quad-Cutler LEGS THO\",\"Jay Cutler\",\"quad\",\"circuit\"],[\"BumSTEADy Squat\",\"Chris Bumstead\",\"squat\",\"steady-state control\"]],[\"22-curve--stylized_cartoon_figure_3d_model\",\"quads\",[\"Kai Gr33ne Lunge\",\"Kai Greene\",\"lunge\",\"gear designation\"]],[\"02-taper-tallstalk--3d_humanoid_figure\",\"quads\",[\"Hane-Yoke Lunge\",\"Lee Haney\",\"lunge\",\"powered yoke\"],[\"Stern-on Squat\",\"Erin Stern\",\"squat\",\"power-on switch\"]],[\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"quads\",[\"Oli-Volt Lunge\",\"Sergio Oliva\",\"lunge\",\"voltage\"],[\"Y4TES Yoke-Squat\",\"Dorian Yates\",\"squat\",\"robot designation\"]],[\"18-quad-all--wooden_four-legged_robot_3d_model\",\"quads\",[\"Priest-L33 Squat\",\"Lee Priest\",\"squat\",\"piston\"]],[\"18-quad-all--stylized_quadruped_3d_model\",\"quads\",[\"Quadzilla Platz\",\"Tom Platz\",\"quads\",\"kaiju creature\"],[\"SternShift Squat\",\"Erin Stern\",\"squat\",\"gear shift\"]],[\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"glutes\",[\"Con-Thrust-Era\",\"Bret Contreras\",\"hip thrust\",\"thruster\"]],[\"23-blob-texture-bodies--sand_creature_3d_model\",\"glutes\",[\"Tingularity Thrust\",\"Chloe Ting\",\"hip thrust\",\"singularity\"]],[\"03-pearl-orb-ring--stylized_3d_character1\",\"glutes\",[\"Gyrvanium Glute-Drive\",\"Caroline Girvan\",\"glute\",\"futuristic alloy\"],[\"Jill-iON Hinge-Flex\",\"Jillian Michaels\",\"hip hinge\",\"ion drive\"]],[\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"glutes\",[\"Trac-ION Hip-Thrust\",\"Tracy Anderson\",\"hip thrust\",\"ion drive\"],[\"Simmonics Bridge-Servo\",\"Whitney Simmons\",\"bridge\",\"electronics servo\"]],[\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"glutes\",[\"Robo-ertson Bridge\",\"Heather Robertson\",\"bridge\",\"robot\"],[\"Hearn-ess ExoHinge\",\"Katy Hearn\",\"hinge\",\"exosuit harness\"]],[\"03-pearl-orb-ring--circle_center\",\"glutes\",[\"Con-Terra Coil-Bridge\",\"Bret Contreras\",\"bridge\",\"planetary coil\"],[\"Cyb-Ho Holo-Bridge\",\"Cassey Ho\",\"bridge\",\"cyborg hologram\"]],[\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"glutes\",[\"Chlo-Turbo Thrust\",\"Chloe Ting\",\"thrust\",\"turbine\"]],[\"10-petal-wisp--fantasy_creature_3d_model4\",\"yoga\",[\"Adriene A-Sana Mk II\",\"Adriene Mishler\",\"asana\",\"model revision\"]],[\"19-genie-multi--fantasy_creature_3d_model\",\"yoga\",[\"Mat-Gregor Matrix\",\"Kino MacGregor\",\"mat\",\"simulation matrix\"]],[\"10-petal-wisp--ghost_character_3d_model\",\"yoga\",[\"Warri0r-ner Drive\",\"Dylan Werner\",\"warrior pose\",\"motor designation\"],[\"Kathryn Bend-1g\",\"Kathryn Budig\",\"bend\",\"robot\"]],[\"08-shard-asym--stylized_action_figure_3d_model\",\"yoga\",[\"Yee-lastic Flow\",\"Rodney Yee\",\"yoga flow\",\"elastic mechanics\"]],[\"22-curve--stylized_alien_3d_model\",\"yoga\",[\"Strom Flex Current\",\"Max Strom\",\"stretch\",\"electric current\"]],[\"10-petal-wisp--stylized_creature_3d_model\",\"yoga\",[\"Iyeng-Arc Inversion\",\"B. K. S. Iyengar\",\"inversion\",\"powered arc\"]],[\"19-genie-multi--stylized_octopus_3d_model\",\"yoga\",[\"Yee Gyro-Yogi\",\"Rodney Yee\",\"yoga\",\"gyroscope\"]],[\"01-seed-pearo--cute_alien_figure_3d_model\",\"yoga\",[\"Wern-Axis Flow\",\"Dylan Werner\",\"flow\",\"robotic axis\"],[\"Mat-Gregor Drive\",\"Kino MacGregor\",\"mat practice\",\"motor drive\"]],[\"01-seed-pearo--white_3d_character_model\",\"yoga\",[\"Budig-ital Balance\",\"Kathryn Budig\",\"balance\",\"digital\"],[\"Corn Kernel Cobra\",\"Seane Corn\",\"cobra pose\",\"computing kernel\"]],[\"09-monolith-tanka--boxy_humanoid_3d_model\",\"martial-arts\",[\"Bruce L3300\",\"Bruce Lee\",\"kick\",\"alphanumeric call sign\"],[\"GSP-Drive\",\"Georges St-Pierre\",\"spar\",\"motor drive\"]],[\"11-anvil-cask--clay-style_robot_3d_model\",\"martial-arts\",[\"Anderson Sil-VOLT\",\"Anderson Silva\",\"strike\",\"voltage\"],[\"r0nda Rous-Bot\",\"Ronda Rousey\",\"armbar\",\"robot\"]],[\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"martial-arts\",[\"C0N0R McG-Gear\",\"Conor McGregor\",\"kick\",\"gear transmission\"],[\"Nor-Rotor Punch\",\"Chuck Norris\",\"punch\",\"rotor\"]],[\"14-chisel-spire--cone_head_3d_model\",\"martial-arts\",[\"LiDAR Jett-Jab\",\"Jet Li\",\"jab\",\"laser ranging\"],[\"Jaa-Bot Jab\",\"Tony Jaa\",\"jab\",\"robot\"]],[\"08-shard-asym--fantasy_creature_3d_model2\",\"martial-arts\",[\"Waters0nic\",\"Michelle Waterson\",\"kick\",\"sonic drive\"],[\"Amanda Nu-NEURAL\",\"Amanda Nunes\",\"strike\",\"neural implant\"]],[\"09-monolith-tanka--mini_robot_3d_model\",\"martial-arts\",[\"Machi-Droid-a\",\"Lyoto Machida\",\"chop\",\"android\"],[\"Bas Rut-TORque\",\"Bas Rutten\",\"kick\",\"motor torque\"]],[\"09-monolith-tanka--robot_3d_model3\",\"martial-arts\",[\"Israel Ade-SYNC-ya\",\"Israel Adesanya\",\"strike\",\"synchronization\"],[\"Shev-Servo Sweep\",\"Valentina Shevchenko\",\"sweep\",\"servo\"]],[\"13-fan-split--stylized_toy_3d_model\",\"martial-arts\",[\"BlankSaber Kick\",\"Billy Blanks\",\"kick\",\"energy saber\"],[\"Wilsonic Kick\",\"Don Wilson\",\"kick\",\"sonic drive\"]],[\"09-monolith-tanka--robot_3d_model2\",\"martial-arts\",[\"Rous-Torque Throw\",\"Ronda Rousey\",\"throw\",\"motor torque\"],[\"Jackie Chain-Kick\",\"Jackie Chan\",\"kick\",\"chain drive\"]],[\"07-bulb-sphereling--humanoid_robot_3d_model\",\"cardio\",[\"B0LT Sprint-Drive\",\"Usain Bolt\",\"sprint\",\"electrical bolt drive\"]],[\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"cardio\",[\"Fon-Dynamo Footwork\",\"Jane Fonda\",\"footwork\",\"dynamo\"],[\"Simmonics Sweatstream\",\"Richard Simmons\",\"aerobic sweat\",\"electronics stream\"]],[\"06-ridge-triad--robot_3d_model1\",\"cardio\",[\"M0 Farah Fastlane\",\"Mo Farah\",\"fast run\",\"electric capacitance\"]],[\"03-pearl-orb-ring--robot_3d_model\",\"cardio\",[\"Feli-X Footdrive\",\"Allyson Felix\",\"footwork\",\"X-model drive\"],[\"Rad-Clutch Run\",\"Paula Radcliffe\",\"run\",\"motor clutch\"]],[\"20-lume--robotic_figure_3d_model\",\"cardio\",[\"Pre-Font-AI-ne Pace\",\"Steve Prefontaine\",\"pace\",\"AI running model\"],[\"Ohm-wens Run\",\"Jesse Owens\",\"run\",\"electrical resistance\"]],[\"07-bulb-sphereling--cute_robot_3d_model\",\"cardio\",[\"Billy Blanx Beam\",\"Billy Blanks\",\"boot camp\",\"waveform\"],[\"GebreShell Runloop\",\"Haile Gebrselassie\",\"run\",\"software shell loop\"]],[\"01-seed-pearo--blank_toy_figure_3d_model\",\"cardio\",[\"Phelpeller Pulse\",\"Michael Phelps\",\"cardio pulse\",\"propeller\"],[\"Fon-Droid Footwork\",\"Jane Fonda\",\"footwork\",\"android\"]],[\"18-quad-all--dragon_creature_3d_model\",\"meditation\",[\"H0F-Lung\",\"Wim Hof\",\"breathing\",\"oxygen-code lung\"]],[\"18-quad-all--fantasy_creature_3d_model1\",\"meditation\",[\"Max Ohm-Strom Breath\",\"Max Strom\",\"breathing\",\"electrical resistance\"],[\"Yee-uro Zen\",\"Rodney Yee\",\"mindful movement\",\"neural interface\"]],[\"18-quad-all--fantasy_creature_3d_model3\",\"meditation\",[\"Shiva Rea-spatory\",\"Shiva Rea\",\"respiration\",\"airlock\"]],[\"18-quad-all--four-legged_robot_3d_model\",\"meditation\",[\"Tara-5tile5-Standby\",\"Tara Stiles\",\"breathing\",\"computer standby\"]],[\"18-quad-all--quadruped_robot_3d_model1\",\"meditation\",[\"Werner Wind0wn\",\"Dylan Werner\",\"wind-down practice\",\"alphanumeric standby mode\"]],[\"18-quad-all--quadruped_robot_3d_model\",\"meditation\",[\"Yee Holo-Om\",\"Rodney Yee\",\"mindful movement\",\"hologram\"]],[\"18-quad-all--robotic_dog_3d_model\",\"meditation\",[\"Budig-ital Breath\",\"Kathryn Budig\",\"breathing\",\"digital\"]],[\"12-seedpod-snailslug--stylized_slug_3d_model\",\"meditation\",[\"H0F Hold-Circuit\",\"Wim Hof\",\"breath hold\",\"circuit and model code\"],[\"Ohm-Strom Still\",\"Max Strom\",\"stillness\",\"electrical resistance\"]],[\"12-seedpod-snailslug--stylized_worm_3d_model\",\"meditation\",[\"Pilat-OS Calm-Circuit\",\"Joseph Pilates\",\"mindful breathing\",\"operating system circuit\"]],[\"14-chisel-spire--low_poly_robot_3d_model\",\"meditation\",[\"Rea-Spire Relay\",\"Shiva Rea\",\"respiration\",\"air relay\"]],[\"01-seed-pearo--3d_character_model\",\"meditation\",[\"McKe-O2 Nose-Cone\",\"Patrick McKeown\",\"nasal breathing\",\"rocket nose cone\"],[\"Vertu-al Vent\",\"Shona Vertue\",\"ventilation\",\"virtual reality\"]],[\"23-blob-texture-bodies--blob_creature_3d_model\",\"meditation\",[\"Blob 1 \\xB7 H0F Hold-Drive\",\"Wim Hof\",\"breath hold\",\"air drive and model code\"]],[\"01-seed-pearo--blank_humanoid_figure_3d_model\",\"meditation\",[\"Corn Kernel Om\",\"Seane Corn\",\"meditation\",\"computing kernel\"],[\"Kino-Kinetic Breath\",\"Kino MacGregor\",\"breathing\",\"kinematics\"]]],Re=Object.freeze(Object.fromEntries(Ye.map(([e,a,...r])=>[e===\"myr5\"?e:`roster/${e}`,Object.freeze({track:a,names:Object.freeze(r.map(([t,o,s,i])=>Object.freeze({name:t,person:o,workout:s,scifi:i})))})])));var Da=Object.freeze(Object.fromEntries(Object.entries(Re).map(([e,a])=>[e,Object.freeze(a.names.map(r=>r.name))])));function Ve(e){let a=Re[e];if(!a)throw RangeError(`No coach name for ${e}.`);return a}var Q=(e,a=!1)=>{let r=Ve(e).names;return r[a?1:0]?.name??r[0].name};var G=Object.freeze({sprintRounds:5,gentleActiveSeconds:300,paceActiveSeconds:300,meditationDays:Object.freeze([3,6,9,12])}),Qe=[{id:\"myr5\",sourceLabel:\"Original MYR5\"},{id:\"roster/01-seed-pearo--3d_character_model\",sourceLabel:\"Seed \\xB7 Pearo 1\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 2\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 5\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",sourceLabel:\"Taper \\xB7 Tallstalk 1\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",sourceLabel:\"Taper \\xB7 Tallstalk 2\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",sourceLabel:\"Taper \\xB7 Tallstalk 3\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 1\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--circle_center\",sourceLabel:\"Pearl \\xB7 Orb 2\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 3\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 4\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",sourceLabel:\"Pearl \\xB7 Orb 5\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 6\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",sourceLabel:\"Crest \\xB7 Wedge 1\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",sourceLabel:\"Crest \\xB7 Wedge 2\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",sourceLabel:\"Slope \\xB7 Bobble 1\",group:\"Slope \\xB7 Bobble\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 1\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 2\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 1\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 2\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",sourceLabel:\"Shard \\xB7 Asym 1\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--robot_3d_model4\",sourceLabel:\"Shard \\xB7 Asym 2\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",sourceLabel:\"Shard \\xB7 Asym 3\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",sourceLabel:\"Shard \\xB7 Asym 4\",group:\"Shard \\xB7 Asym\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",sourceLabel:\"Monolith \\xB7 Tanka 1\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 2\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 3\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",sourceLabel:\"Monolith \\xB7 Tanka 5\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 6\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 1\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 2\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",sourceLabel:\"Petal \\xB7 Wisp 3\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",sourceLabel:\"Anvil \\xB7 Cask 1\",group:\"Anvil \\xB7 Cask\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 1\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 2\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/13-fan-split--stylized_3d_character2\",sourceLabel:\"Fan \\xB7 Split 1\",group:\"Fan \\xB7 Split\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",sourceLabel:\"Fan \\xB7 Split 2\",group:\"Fan \\xB7 Split\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 1\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 2\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",sourceLabel:\"Orbital \\xB7 Coili 1\",group:\"Orbital \\xB7 Coili\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",sourceLabel:\"Spade \\xB7 Arch 1\",group:\"Spade \\xB7 Arch\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",sourceLabel:\"Spade \\xB7 Arch 2\",group:\"Spade \\xB7 Arch\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",sourceLabel:\"Shellcap \\xB7 Manyarm 1\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 2\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 3\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",sourceLabel:\"Four-legged 1\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",sourceLabel:\"Four-legged 2\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",sourceLabel:\"Four-legged 3\",group:\"Four-legged\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",sourceLabel:\"Four-legged 4\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",sourceLabel:\"Four-legged 5\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",sourceLabel:\"Four-legged 6\",group:\"Four-legged\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",sourceLabel:\"Four-legged 7\",group:\"Four-legged\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",sourceLabel:\"Four-legged 9\",group:\"Four-legged\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",sourceLabel:\"Genie \\xB7 Multi 1\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",sourceLabel:\"Genie \\xB7 Multi 2\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",sourceLabel:\"Genie \\xB7 Multi 3\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",sourceLabel:\"Genie \\xB7 Multi 4\",group:\"Genie \\xB7 Multi\"},{id:\"roster/20-lume--robotic_figure_3d_model\",sourceLabel:\"Lume 1\",group:\"Lume\"},{id:\"roster/22-curve--stylized_alien_3d_model\",sourceLabel:\"Curve 1\",group:\"Curve\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",sourceLabel:\"Curve 2\",group:\"Curve\"},{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",sourceLabel:\"Blob 1\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",sourceLabel:\"Blob 2\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",sourceLabel:\"Blob 3\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",sourceLabel:\"Blob 4\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",sourceLabel:\"Blob 5\",group:\"Blob\"}],Ze=Qe.map(e=>({...e,label:Q(e.id)})),ea=[{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/20-lume--robotic_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"expert\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/08-shard-asym--robot_3d_model4\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",tracks:[\"chest\",\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",tracks:[\"chest\"],difficulty:\"expert\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",tracks:[\"glutes\"],difficulty:\"hard\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",tracks:[\"glutes\"],difficulty:\"expert\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",tracks:[\"quads\"],difficulty:\"easy\"},{id:\"roster/13-fan-split--stylized_3d_character2\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_alien_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",tracks:[\"yoga\"],difficulty:\"expert\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/01-seed-pearo--3d_character_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",tracks:[\"quads\"],difficulty:\"expert\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--circle_center\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",tracks:[\"chest\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"hard\"}],aa={chest:[\"chest\"],quads:[\"legs\"],glutes:[\"hips\"],arms:[\"shoulders\"],yoga:[\"core\",\"balance\",\"yoga\"],\"martial-arts\":[\"stances\",\"boxing\"],cardio:[\"cardio\"],meditation:[\"meditation\"]},Ee=Object.freeze([\"chest\",\"quads\",\"glutes\",\"arms-shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),Pa=Object.freeze({arms:\"arms-shoulders\"}),ra=e=>e===\"arms-shoulders\"?\"arms\":e;var Ce=Object.freeze([\"myr5\",\"roster/23-blob-texture-bodies--blob_creature_3d_model\"]);var Fe=ea.filter(e=>!Ce.includes(e.id)).map(e=>({...e,name:Q(e.id),groups:e.tracks.flatMap(a=>aa[a])})),ta=Object.freeze(Object.fromEntries(Ee.map(e=>[e,Fe.find(a=>a.tracks[0]===ra(e)&&a.difficulty===\"easy\").id])));var oa=Object.freeze([\"easy\",\"medium\",\"hard\",\"expert\"]),ve=e=>Object.freeze(e.map(a=>Object.freeze(a.map(r=>r*10)))),Ga=ve([[4,8,16],[8,12,20],[12,16,24],[16,20,28]]),za=ve([[1,2,4],[2,3,5],[3,4,6],[4,5,7]]);var na=15;var ja=Object.freeze({uncommon:1,rare:2,legendary:3,secret:3}),qa=Object.freeze({uncommon:Object.freeze({color:90,\"64-bit\":7,texture:3}),rare:Object.freeze({color:80,\"64-bit\":15,texture:5}),legendary:Object.freeze({color:70,\"64-bit\":20,texture:10}),secret:Object.freeze({color:60,\"64-bit\":20,texture:20})}),sa=2,Le=500*10,S=Object.freeze({sessions:120,activeMinutes:30,expertXpPerMinute:280,meditationMultiplier:sa,trioBonus:Le}),ia=S.sessions*(S.activeMinutes*S.expertXpPerMinute*S.meditationMultiplier+S.trioBonus),Ha=S.expertXpPerMinute*na/60;var Me=250,Z=Me-1,Ae=40,la=ia/10,Ua=Object.freeze(Array.from({length:Me},(e,a)=>10*(Ae*a+Math.floor((la-Ae*Z)*a*(a-1)/(Z*(Z-1))))));function ee(e=globalThis.localStorage,a=globalThis.myr5AuthenticatedAccount){let r=a?.user?.id;if(r)return`account:${r}${a.dataEpoch!=null?`:${a.dataEpoch}`:\"\"}`;if(!e)return\"guest:unavailable\";let t=\"myr5-local-guest-owner-v1\";try{let o=e.getItem(t);return o||(o=globalThis.crypto?.randomUUID?.()||`guest-${Date.now()}-${Math.random()}`,e.setItem(t,o)),`guest:${o}`}catch{return\"guest:unavailable\"}}var cr={sprint:G.sprintRounds,gentle:G.gentleActiveSeconds,pace:G.paceActiveSeconds};var ca=Object.freeze([\"creature-skin\",\"ship\",\"reward-pack\"]),mr=new Set([...ca,\"texture\",\"color\",\"palette\",\"boss-skin\"]);var gr=Object.freeze([\"clay\",\"martial-arts-canvas-gi\",\"arms-rope\",\"yoga-cork\",\"glutes-peach\"]),ae=Object.freeze({\"legacy-0\":\"clay\",\"legacy-21\":\"opal-jelly\",\"bubble-glass\":\"glitter-resin\"}),pr=Object.freeze([\"flat\",\"legacy-22\",...Object.keys(ae)]),Oe=e=>Object.hasOwn(ae,e)?ae[e]:e;var da=\"myr5-unlocks-v2\",ua=()=>({texture:[],color:[],palette:[]}),ma=(e,a)=>`coach:${encodeURIComponent(e)}:${a}`,re=(e,a)=>e===\"texture\"?Oe(a):a,ha=(e,a)=>{let r=/^(coach:[^:]*:)(.*)$/.exec(a);return r?r[1]+re(e,r[2]):a};function te(){try{return JSON.parse(localStorage.getItem(\"myr5-recipe-v1\")||\"{}\").body||\"myr5\"}catch{return\"myr5\"}}var ga=()=>K(`${da}/${encodeURIComponent(ee())}`);function xe(){try{let e=JSON.parse(localStorage.getItem(ga())||\"{}\");return Object.fromEntries([\"texture\",\"color\",\"palette\"].map(a=>[a,Array.isArray(e[a])?[...new Set(e[a].filter(r=>typeof r==\"string\").map(r=>ha(a,r)))]:[]]))}catch{return ua()}}var De=(e,a,r)=>xe()[e]?.includes(ma(r,re(e,a)))??!1;function pa(e,a){let r=re(e,a);return(xe()[e]||[]).some(t=>{let o=/^coach:([^:]+):(.*)$/.exec(t);if(!o||o[2]!==r)return!1;try{return decodeURIComponent(o[1])!==\"myr5\"}catch{return!1}})}var fa=(e,a)=>De(e,a,\"myr5\")||pa(e,a),oe=(e,a,r=te())=>r===\"myr5\"?fa(e,a):De(e,a,r);var ne=[{id:32,name:\"Plate Steel\",roughness:.34,metalness:.82,relief:.024,bump:.012,sheen:0,sheenRoughness:.7,clearcoat:.35,clearcoatRoughness:.24},{id:33,name:\"Rubber Grip\",roughness:.95,metalness:0,relief:.055,bump:.022,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:34,name:\"Chain Mail\",roughness:.41,metalness:.78,relief:.034,bump:.014,sheen:0,sheenRoughness:.7,clearcoat:.3,clearcoatRoughness:.25},{id:35,name:\"Track Rubber\",roughness:.94,metalness:0,relief:.038,bump:.016,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:36,name:\"Denim\",roughness:.86,metalness:0,relief:.012,bump:.011,sheen:.22,sheenRoughness:.96,clearcoat:0,clearcoatRoughness:.5},{id:37,name:\"Hex Tread\",roughness:.82,metalness:.04,relief:.052,bump:.014,sheen:0,sheenRoughness:.9,clearcoat:.08,clearcoatRoughness:.4},{id:38,name:\"Sweatshirt Fleece\",roughness:.99,metalness:0,relief:.05,bump:.032,sheen:.74,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},{id:39,name:\"Quilted\",roughness:.89,metalness:0,relief:.07,bump:.012,sheen:.14,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:40,name:\"Speckled\",roughness:.96,metalness:0,relief:.03,bump:.028,sheen:.34,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},{id:41,name:\"Hammered Bronze\",roughness:.39,metalness:.78,relief:.025,bump:.025,sheen:0,sheenRoughness:.7,clearcoat:.28,clearcoatRoughness:.22},{id:42,name:\"Fine Stripe\",roughness:.91,metalness:0,relief:.042,bump:.012,sheen:.08,sheenRoughness:.92,clearcoat:0,clearcoatRoughness:.5},{id:43,name:\"Snake Skin\",roughness:.68,metalness:.02,relief:.019,bump:.023,sheen:.1,sheenRoughness:.76,clearcoat:.08,clearcoatRoughness:.4},{id:44,name:\"Holey\",roughness:.96,metalness:0,relief:.039,bump:.025,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:45,name:\"Woven Mat\",roughness:.9,metalness:0,relief:.014,bump:.014,sheen:.08,sheenRoughness:.94,clearcoat:0,clearcoatRoughness:.5},{id:46,name:\"Petal\",roughness:.51,metalness:0,relief:.025,bump:.009,sheen:.52,sheenRoughness:.72,clearcoat:.12,clearcoatRoughness:.3},{id:47,name:\"Graph Paper\",roughness:.93,metalness:0,relief:.012,bump:.017,sheen:.16,sheenRoughness:.97,clearcoat:0,clearcoatRoughness:.5},{id:48,name:\"Bamboo\",roughness:.57,metalness:0,relief:.027,bump:.008,sheen:.18,sheenRoughness:.62,clearcoat:.12,clearcoatRoughness:.28},{id:49,name:\"Dragon Scale\",roughness:.31,metalness:.16,relief:.043,bump:.01,sheen:.24,sheenRoughness:.42,clearcoat:.72,clearcoatRoughness:.16},{id:50,name:\"Cool Graph Paper\",roughness:.56,metalness:.23,relief:.021,bump:.008,sheen:0,sheenRoughness:.8,clearcoat:.12,clearcoatRoughness:.3},{id:51,name:\"Terry Cloth\",roughness:.98,metalness:0,relief:.014,bump:.026,sheen:.62,sheenRoughness:.99,clearcoat:0,clearcoatRoughness:.5},{id:52,name:\"Pebble Path\",roughness:.77,metalness:0,relief:.038,bump:.018,sheen:0,sheenRoughness:.8,clearcoat:.08,clearcoatRoughness:.4},{id:53,name:\"Wiggles\",roughness:.99,metalness:0,relief:.016,bump:.016,sheen:0,sheenRoughness:.95,clearcoat:0,clearcoatRoughness:.5},{id:54,name:\"River Stone\",roughness:.72,metalness:.01,relief:.031,bump:.019,sheen:.04,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.32},{id:55,name:\"Moss\",roughness:.99,metalness:0,relief:.022,bump:.023,sheen:.38,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5}],ba={id:56,name:\"64-bit Skin\",roughness:.58,metalness:.12,relief:.018,bump:.01,sheen:0,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.4},se=[{id:57,name:\"Opal Jelly\",roughness:.06,metalness:0,relief:.004,bump:.001,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.035},{id:58,name:\"Bubble Glass\",roughness:.035,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},{id:59,name:\"Prism Crystal\",roughness:.045,metalness:0,relief:.052,bump:.008,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.03},{id:60,name:\"Holographic Foil\",roughness:.18,metalness:.88,relief:.022,bump:.013,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.045},{id:61,name:\"Glitter Resin\",roughness:.075,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},{id:62,name:\"Galaxy Geode\",roughness:.22,metalness:.25,relief:.065,bump:.014,sheen:0,sheenRoughness:.3,clearcoat:.8,clearcoatRoughness:.06},{id:63,name:\"Caustic Slime\",roughness:.055,metalness:0,relief:.035,bump:.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:64,name:\"Blister Hide\",roughness:.18,metalness:0,relief:.065,bump:.023,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:65,name:\"Rotten Rind\",roughness:.94,metalness:0,relief:.05,bump:.025,sheen:0,sheenRoughness:.6,clearcoat:0,clearcoatRoughness:.07},{id:66,name:\"Parasite Nest\",roughness:.31,metalness:0,relief:.07,bump:.027,sheen:0,sheenRoughness:.6,clearcoat:.6,clearcoatRoughness:.07},{id:67,name:\"Exposed Sinew\",roughness:.2,metalness:0,relief:.045,bump:.017,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:68,name:\"Abyssal Maw\",roughness:.26,metalness:0,relief:.065,bump:.023,sheen:0,sheenRoughness:.6,clearcoat:.8,clearcoatRoughness:.07},{id:69,name:\"Circuit Alloy\",roughness:.19,metalness:.83,relief:.027,bump:.01,sheen:0,sheenRoughness:.6,clearcoat:.6,clearcoatRoughness:.07},{id:70,name:\"Servo Armor\",roughness:.34,metalness:.82,relief:.055,bump:.013,sheen:0,sheenRoughness:.6,clearcoat:.4,clearcoatRoughness:.07},{id:71,name:\"Chrome Rib\",roughness:.09,metalness:.96,relief:.05,bump:.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:72,name:\"Carbon Mech\",roughness:.64,metalness:.18,relief:.018,bump:.013,sheen:0,sheenRoughness:.6,clearcoat:.25,clearcoatRoughness:.07},{id:73,name:\"Hazard Panel\",roughness:.46,metalness:.55,relief:.032,bump:.014,sheen:0,sheenRoughness:.6,clearcoat:.4,clearcoatRoughness:.07},{id:74,name:\"Reactor Glass\",roughness:.045,metalness:0,relief:.018,bump:.004,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07}],ya=new Map([...ne,ba,...se].map(e=>[e.id,e])),Te=e=>ya.get(e);var Ie=[{id:\"pal-01\",name:\"Acid Forge\",tagline:\"Lime bites oxblood and chrome\",colors:[\"#C6FF00\",\"#4A0E17\",\"#C0C7CE\"],unlockRule:\"aura-milestone\",unlockAtDay:5,finish:\"glitter\"},{id:\"pal-02\",name:\"Neon Afterburn Haze\",tagline:\"Orange aqua contrails with violet flare\",colors:[\"#FF6D00\",\"#00E0B0\",\"#C800FF\"],unlockRule:\"aura-milestone\",unlockAtDay:10,finish:\"brushed-metal\"},{id:\"pal-03\",name:\"Petrel Copper\",tagline:\"Petrol depths meet intense violet\",colors:[\"#0E5A61\",\"#B87333\",\"#6913E7\"],unlockRule:\"aura-milestone\",unlockAtDay:15,finish:\"polished-metal\"},{id:\"pal-04\",name:\"Vinaigrette Rust\",tagline:\"Oil pigment rust swirl\",colors:[\"#8A3B12\",\"#556B2F\",\"#E7A213\"],unlockRule:\"aura-milestone\",unlockAtDay:20,finish:\"marbled\"},{id:\"pal-05\",name:\"Neon Relic\",tagline:\"Hot magenta neon amber on bone\",colors:[\"#FF009F\",\"#FFB500\",\"#F2EFE6\"],unlockRule:\"aura-milestone\",unlockAtDay:25,finish:\"glitter\"},{id:\"pal-06\",name:\"Glacier Ember\",tagline:\"Ice teal over ember red\",colors:[\"#13E7D1\",\"#D9381E\",\"#2B2530\"],unlockRule:\"aura-milestone\",unlockAtDay:30,finish:\"pearl\"},{id:\"pal-07\",name:\"Chartreuse Storm\",tagline:\"Sludge green meets slate violet\",colors:[\"#B5C700\",\"#3D3654\",\"#E85D75\"],unlockRule:\"aura-milestone\",unlockAtDay:35,finish:\"matte\"},{id:\"pal-08\",name:\"Saffron Voltage\",tagline:\"Saffron arcs on ink blue\",colors:[\"#FFB000\",\"#101D42\",\"#00FAA6\"],unlockRule:\"aura-milestone\",unlockAtDay:40,finish:\"polished-metal\"},{id:\"pal-09\",name:\"Neon Venom Signal\",tagline:\"Poison lime warning under arterial red\",colors:[\"#A6FF00\",\"#FF2442\",\"#101828\"],unlockRule:\"aura-milestone\",unlockAtDay:45,finish:\"watercolor\"},{id:\"pal-10\",name:\"Mercury Moss\",tagline:\"Silver moss over saturated gold\",colors:[\"#AEB6BC\",\"#5C6B3C\",\"#E7A913\"],unlockRule:\"aura-milestone\",unlockAtDay:50,finish:\"brushed-metal\"},{id:\"pal-11\",name:\"Ultraviolet Tar\",tagline:\"Violet glow on tar black\",colors:[\"#7B2FF7\",\"#141414\",\"#FFC93C\"],unlockRule:\"aura-milestone\",unlockAtDay:55,finish:\"glaze\"},{id:\"pal-12\",name:\"Salmon Circuit\",tagline:\"Salmon pink on circuit green\",colors:[\"#FA2200\",\"#00A86B\",\"#2E2A5C\"],unlockRule:\"aura-milestone\",unlockAtDay:60,finish:\"pearl\"},{id:\"pal-13\",name:\"Muddy Pop\",tagline:\"Ochre mud sparks cerulean\",colors:[\"#8C6A2F\",\"#0081C8\",\"#E71362\"],unlockRule:\"battle-pass\",reward:\"strider-2:L1\",finish:\"matte\"},{id:\"pal-14\",name:\"Neon Vapor Crown\",tagline:\"Laser pink blue over dark purple\",colors:[\"#FF00AA\",\"#00D0FF\",\"#1B0033\"],unlockRule:\"battle-pass\",reward:\"strider-2:L3\",finish:\"watercolor\"},{id:\"pal-15\",name:\"Brick Vein\",tagline:\"Brick red veined in steel\",colors:[\"#A0342B\",\"#8C9BA5\",\"#D7E713\"],unlockRule:\"battle-pass\",reward:\"strider-2:L4\",finish:\"marbled\"},{id:\"pal-16\",name:\"Peacock Static\",tagline:\"Peacock green hums gold\",colors:[\"#046A5B\",\"#FAAF00\",\"#5B1E4B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L1\",finish:\"polished-metal\"},{id:\"pal-17\",name:\"Foghorn Flame\",tagline:\"Fog gray with fire-orange siren\",colors:[\"#B8C0C2\",\"#FA3F00\",\"#123C69\"],unlockRule:\"battle-pass\",reward:\"strider-3:L3\",finish:\"brushed-metal\"},{id:\"pal-18\",name:\"Toxic Honey\",tagline:\"Honey drips on toxic lime\",colors:[\"#D9A404\",\"#9BF00B\",\"#4A154B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L4\",finish:\"glaze\"},{id:\"pal-19\",name:\"Neon Crimson Mirage\",tagline:\"Scarlet toxic lime over deep jade\",colors:[\"#FF1744\",\"#76FF03\",\"#005E3C\"],unlockRule:\"battle-pass\",reward:\"strider-4:L1\",finish:\"matte\"},{id:\"pal-20\",name:\"Opal Drain\",tagline:\"Opal sheen on drainpipe green\",colors:[\"#13E7B9\",\"#3E5641\",\"#D1495B\"],unlockRule:\"battle-pass\",reward:\"strider-4:L3\",finish:\"pearl\"},{id:\"pal-21\",name:\"Rust Halo\",tagline:\"Rust ringed by mint flash\",colors:[\"#9C4221\",\"#12E898\",\"#2C2C54\"],unlockRule:\"battle-pass\",reward:\"strider-4:L4\",finish:\"brushed-metal\"},{id:\"pal-22\",name:\"Volt Quartz\",tagline:\"Electric yellow on rose quartz\",colors:[\"#F5E900\",\"#E71363\",\"#264653\"],unlockRule:\"battle-pass\",reward:\"strider-5:L1\",finish:\"glitter\"},{id:\"pal-23\",name:\"Neon Flare Garden\",tagline:\"Laser lime pink on teal steel\",colors:[\"#8CFF00\",\"#FF006E\",\"#00B8A9\"],unlockRule:\"battle-pass\",reward:\"strider-5:L3\",finish:\"matte\"},{id:\"pal-24\",name:\"Crimson Tidebreak\",tagline:\"Crimson breaks on seafoam\",colors:[\"#B91338\",\"#13E7B5\",\"#2A1E5C\"],unlockRule:\"battle-pass\",reward:\"strider-5:L4\",finish:\"watercolor\"},{id:\"pal-25\",name:\"Brass Mirage\",tagline:\"Brass shimmer dusky plum\",colors:[\"#C59B42\",\"#5A2A52\",\"#0D94ED\"],unlockRule:\"battle-pass\",reward:\"strider-6:L1\",finish:\"polished-metal\"},{id:\"pal-26\",name:\"Neon Infrared Bloom\",tagline:\"Red heat sliced by frozen cyan\",colors:[\"#FF0000\",\"#00F5FF\",\"#5B00B5\"],unlockRule:\"battle-pass\",reward:\"strider-6:L3\",finish:\"glaze\"},{id:\"pal-27\",name:\"Ice Magma\",tagline:\"Pigmented ice over magma orange\",colors:[\"#D6F0F0\",\"#F45B0F\",\"#3D2C8D\"],unlockRule:\"battle-pass\",reward:\"strider-6:L4\",finish:\"marbled\"},{id:\"pal-28\",name:\"Fern Radio\",tagline:\"Fern green static magenta\",colors:[\"#3F7A3A\",\"#E4007C\",\"#E7BE13\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L1\",finish:\"matte\"},{id:\"pal-29\",name:\"Porcelain Fuel\",tagline:\"Porcelain white fuel orange\",colors:[\"#F2F2F0\",\"#FF7A1A\",\"#14507A\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L3\",finish:\"glaze\"},{id:\"pal-30\",name:\"Neon Photon Alley\",tagline:\"Yellow signage aqua and violet glass\",colors:[\"#FFD300\",\"#00FFCC\",\"#7A00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L4\",finish:\"brushed-metal\"},{id:\"pal-31\",name:\"Neon Solar Flare\",tagline:\"Hot yellow ember in plum space\",colors:[\"#FFEA00\",\"#FF3D00\",\"#26142B\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L1\",finish:\"polished-metal\"},{id:\"pal-32\",name:\"Wet Cement Rose\",tagline:\"Cement gray wild rose\",colors:[\"#8D9096\",\"#D84A87\",\"#F0E130\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L3\",finish:\"matte\"},{id:\"pal-33\",name:\"Neon Laser Tropics\",tagline:\"Chartreuse beams with scorched orange shadows\",colors:[\"#C6FF00\",\"#FF4D00\",\"#001219\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L4\",finish:\"watercolor\"},{id:\"pal-34\",name:\"Copper Fog\",tagline:\"Copper glows through fog\",colors:[\"#B46A30\",\"#C4CBCE\",\"#B600F5\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L1\",finish:\"brushed-metal\"},{id:\"pal-35\",name:\"Laser Moss\",tagline:\"Laser cyan on moss\",colors:[\"#00E5FF\",\"#546B2F\",\"#FF4FA3\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L3\",finish:\"glitter\"},{id:\"pal-36\",name:\"Bone Ember\",tagline:\"Bone white ember orange\",colors:[\"#EDE8DC\",\"#E4572E\",\"#BA00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L4\",finish:\"matte\"},{id:\"pal-37\",name:\"Plum Static\",tagline:\"Plum noise with electric lime\",colors:[\"#4A1E4F\",\"#B8F000\",\"#33B5C7\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L1\",finish:\"glaze\"},{id:\"pal-38\",name:\"Neon Lime Vortex\",tagline:\"Acid green swirling through electric blue\",colors:[\"#BFFF00\",\"#00E5FF\",\"#12100F\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L3\",finish:\"pearl\"},{id:\"pal-39\",name:\"Clay Command\",tagline:\"Terracotta clay cobalt order\",colors:[\"#C06B55\",\"#2E4BC6\",\"#EDE6D6\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L4\",finish:\"marbled\"},{id:\"pal-40\",name:\"Static Cream\",tagline:\"Cream noise shock green\",colors:[\"#F4EDDE\",\"#39FF14\",\"#4A306D\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L1\",finish:\"glitter\"},{id:\"pal-41\",name:\"Midnight Papaya\",tagline:\"Midnight blue papaya burst\",colors:[\"#191970\",\"#FF9E42\",\"#13E796\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L3\",finish:\"polished-metal\"},{id:\"pal-42\",name:\"Dusty Laser\",tagline:\"Dusty mauve laser green\",colors:[\"#A27E8E\",\"#7CFC00\",\"#1B3B6F\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L4\",finish:\"matte\"},{id:\"pal-43\",name:\"Neon Atomic Sunset\",tagline:\"Amber fallout glowing beside ultraviolet pink\",colors:[\"#FF9100\",\"#FF00F5\",\"#062A2A\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L1\",finish:\"glaze\"},{id:\"pal-44\",name:\"Fern Flame\",tagline:\"Cool fern fierce flame\",colors:[\"#4F7942\",\"#FF3D2E\",\"#E75A13\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L3\",finish:\"brushed-metal\"},{id:\"pal-45\",name:\"Violet Quarry\",tagline:\"Quarry gray violet eruption\",colors:[\"#7D7F87\",\"#8B3DFF\",\"#DCE35B\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L4\",finish:\"matte\"},{id:\"pal-46\",name:\"Cadmium Deep\",tagline:\"Cadmium red deep lagoon\",colors:[\"#D32E12\",\"#046E8F\",\"#E79E13\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L1\",finish:\"polished-metal\"},{id:\"pal-47\",name:\"Pigment Riot\",tagline:\"Intense blue maroon and hot orange\",colors:[\"#139FE7\",\"#6E1423\",\"#FF7300\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L3\",finish:\"watercolor\"},{id:\"pal-48\",name:\"Neon Prism Riot\",tagline:\"Sea-glass cyan, magenta bite, lemon flash\",colors:[\"#00FFC6\",\"#FF00B8\",\"#F5FF00\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L4\",finish:\"pearl\"},{id:\"pal-49\",name:\"Neon Tropic Surge\",tagline:\"Aqua tide, citrus flare, violet thunder\",colors:[\"#00FF9D\",\"#FFB000\",\"#D000FF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L1\",finish:\"glitter\"},{id:\"pal-50\",name:\"Ash Honey\",tagline:\"Ash gray golden honey\",colors:[\"#9BA0A6\",\"#E0A100\",\"#2F6690\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L3\",finish:\"matte\"},{id:\"pal-51\",name:\"Cherry Motor\",tagline:\"Cherry motor oil with electric blue\",colors:[\"#B22234\",\"#3B2F2F\",\"#005CFF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L4\",finish:\"brushed-metal\"},{id:\"pal-52\",name:\"Glacier Punch\",tagline:\"Glacier pigmented hot punch\",colors:[\"#13A6E7\",\"#F52549\",\"#3D405B\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L1\",finish:\"glaze\"},{id:\"pal-53\",name:\"Tarnish Bloom\",tagline:\"Tarnished teal floral orange\",colors:[\"#3A7D7B\",\"#F4852B\",\"#E9D6EC\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L3\",finish:\"marbled\"},{id:\"pal-54\",name:\"Umber Signal\",tagline:\"Umber earth neon signal\",colors:[\"#635147\",\"#D0FF4B\",\"#7A28C0\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L4\",finish:\"matte\"},{id:\"pal-55\",name:\"Rose Quartz Coil\",tagline:\"Soft rose coil of teal\",colors:[\"#E7134B\",\"#0FA3A3\",\"#2B2118\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L1\",finish:\"pearl\"},{id:\"pal-56\",name:\"Sulfur Deck\",tagline:\"Sulfur yellow blue and hot orange\",colors:[\"#E3D200\",\"#274C77\",\"#FF4A00\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L3\",finish:\"polished-metal\"},{id:\"pal-57\",name:\"Neon Ember Static\",tagline:\"Orange yellow buzzing with royal voltage\",colors:[\"#FF5E00\",\"#FFE600\",\"#3A0CA3\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L4\",finish:\"watercolor\"},{id:\"pal-58\",name:\"Mango Ash\",tagline:\"Mango bright volcanic ash\",colors:[\"#FFA12B\",\"#59595E\",\"#64DDBB\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L1\",finish:\"matte\"},{id:\"pal-59\",name:\"Steel Petal\",tagline:\"Steel gray crushed petals\",colors:[\"#8A8F98\",\"#E71813\",\"#D8E26E\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L3\",finish:\"brushed-metal\"},{id:\"pal-60\",name:\"Ninja Cream\",tagline:\"Hidden navy cream coral\",colors:[\"#1E2A4A\",\"#F0EBE0\",\"#7EEC00\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L4\",finish:\"glaze\"},{id:\"pal-61\",name:\"Tangerine Static\",tagline:\"Tangerine buzz grape shadow\",colors:[\"#F28500\",\"#3D2159\",\"#202020\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L1\",finish:\"glitter\"},{id:\"pal-62\",name:\"Sage Pistol\",tagline:\"Muted sage pistol pink\",colors:[\"#9AB5A1\",\"#E45C98\",\"#33272A\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L3\",finish:\"matte\"},{id:\"pal-63\",name:\"Lagoon Battery\",tagline:\"Lagoon green charged yellow\",colors:[\"#1D8A7A\",\"#FAE000\",\"#76305B\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L4\",finish:\"polished-metal\"},{id:\"pal-64\",name:\"Wine Cooling\",tagline:\"Warm wine cooled by mint\",colors:[\"#701F3C\",\"#13E78C\",\"#E8D44D\"],unlockRule:\"battle-pass\",reward:\"warden-1:L1\",finish:\"pearl\"},{id:\"pal-65\",name:\"Charcoal Tulip\",tagline:\"Charcoal tulip flame combo\",colors:[\"#2E3033\",\"#FF4E50\",\"#CA00C7\"],unlockRule:\"battle-pass\",reward:\"warden-1:L3\",finish:\"matte\"},{id:\"pal-66\",name:\"Neon Acid Harbor\",tagline:\"Lime light cutting across magenta docks\",colors:[\"#39FF14\",\"#FF007F\",\"#001B2E\"],unlockRule:\"battle-pass\",reward:\"warden-1:L4\",finish:\"pearl\"},{id:\"pal-67\",name:\"Rustic Neon\",tagline:\"Barn red barn lime buzz\",colors:[\"#8A3324\",\"#C1FF33\",\"#2D4A6B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L1\",finish:\"marbled\"},{id:\"pal-68\",name:\"Alice Warning\",tagline:\"Alice blue caution orange\",colors:[\"#1399E7\",\"#FF7E1F\",\"#EEF700\"],unlockRule:\"battle-pass\",reward:\"blob-2:L3\",finish:\"watercolor\"},{id:\"pal-69\",name:\"Smog Candy\",tagline:\"Smog beige candy teal\",colors:[\"#E79813\",\"#00C2A8\",\"#D8395B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L4\",finish:\"matte\"},{id:\"pal-70\",name:\"Violet Furnace\",tagline:\"Furnace violet cooled sky\",colors:[\"#6A2D9E\",\"#F57C3B\",\"#302012\"],unlockRule:\"battle-pass\",reward:\"blob-3:L1\",finish:\"glaze\"},{id:\"pal-71\",name:\"Khaki Storm\",tagline:\"Khaki field storm magenta\",colors:[\"#A89B6E\",\"#E0218A\",\"#2A6F97\"],unlockRule:\"battle-pass\",reward:\"blob-3:L3\",finish:\"brushed-metal\"},{id:\"pal-72\",name:\"Neon Circuit Surge\",tagline:\"Turquoise sparks over midnight violet\",colors:[\"#00F5D4\",\"#F72585\",\"#0B0321\"],unlockRule:\"battle-pass\",reward:\"blob-3:L4\",finish:\"glitter\"},{id:\"pal-73\",name:\"Cedar Ultraviolet\",tagline:\"Cedar wood ultraviolet light\",colors:[\"#7A4532\",\"#8246FF\",\"#E9E4D8\"],unlockRule:\"battle-pass\",reward:\"blob-4:L1\",finish:\"matte\"},{id:\"pal-74\",name:\"Frosted Chili\",tagline:\"Frost blue chili red\",colors:[\"#13AAE7\",\"#C13A2F\",\"#4C5B2C\"],unlockRule:\"battle-pass\",reward:\"blob-4:L3\",finish:\"polished-metal\"},{id:\"pal-75\",name:\"Moss Rocket\",tagline:\"Moss green rocket pink\",colors:[\"#5B7042\",\"#FA0057\",\"#061F5B\"],unlockRule:\"battle-pass\",reward:\"blob-4:L4\",finish:\"matte\"},{id:\"pal-76\",name:\"Pewter Punch\",tagline:\"Pewter softened hot punch\",colors:[\"#99A0A8\",\"#FF3D7F\",\"#164D37\"],unlockRule:\"battle-pass\",reward:\"cap-2:L1\",finish:\"brushed-metal\"},{id:\"pal-77\",name:\"Neon Glacier Riot\",tagline:\"Cyan hot pink and radioactive lime\",colors:[\"#00F0FF\",\"#FF00E5\",\"#CCFF00\"],unlockRule:\"battle-pass\",reward:\"cap-2:L3\",finish:\"watercolor\"},{id:\"pal-78\",name:\"Cola Lightning\",tagline:\"Cola brown lightning yellow\",colors:[\"#3A2618\",\"#F7FF00\",\"#FA005E\"],unlockRule:\"battle-pass\",reward:\"cap-2:L4\",finish:\"glaze\"},{id:\"pal-79\",name:\"Periwinkle Fuel\",tagline:\"Soft periwinkle diesel orange\",colors:[\"#1325E7\",\"#E0641E\",\"#2F4730\"],unlockRule:\"battle-pass\",reward:\"cap-3:L1\",finish:\"matte\"},{id:\"pal-80\",name:\"Jade Alarm\",tagline:\"Jade calm siren red\",colors:[\"#57A78A\",\"#D91E36\",\"#E7AF13\"],unlockRule:\"battle-pass\",reward:\"cap-3:L3\",finish:\"pearl\"},{id:\"pal-81\",name:\"Neon Ultraviolet Pulse\",tagline:\"Violet beams with mint-charged current\",colors:[\"#8C00FF\",\"#00FFB2\",\"#160021\"],unlockRule:\"battle-pass\",reward:\"cap-3:L4\",finish:\"matte\"},{id:\"pal-82\",name:\"Neon Nightcaller\",tagline:\"Cyan violet flame in black-green fog\",colors:[\"#00FFFF\",\"#9400FF\",\"#131A00\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L1\",finish:\"marbled\"},{id:\"pal-83\",name:\"Ivory Shock\",tagline:\"Ivory calm violet shock\",colors:[\"#F0EEE4\",\"#7C1FD3\",\"#FFB200\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L3\",finish:\"glaze\"},{id:\"pal-84\",name:\"Neon Ruby Arc\",tagline:\"Red-hot signal over green afterglow\",colors:[\"#FF073A\",\"#00FF87\",\"#080F1B\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L4\",finish:\"glitter\"},{id:\"pal-85\",name:\"Maroon Cloud\",tagline:\"Maroon heavy cloud blue\",colors:[\"#6D1E2E\",\"#1381E7\",\"#E5D352\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L1\",finish:\"watercolor\"},{id:\"pal-86\",name:\"Chrome Berry\",tagline:\"Chrome band over berry\",colors:[\"#C3C9D1\",\"#8E2A5C\",\"#FFB347\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L3\",finish:\"polished-metal\"},{id:\"pal-87\",name:\"Lentil Neon\",tagline:\"Lentil tan electric aqua\",colors:[\"#B09A6E\",\"#00FFF0\",\"#5F1B63\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L4\",finish:\"matte\"},{id:\"pal-88\",name:\"Neon Cobalt Fuse\",tagline:\"Blue ignition wired to blazing yellow\",colors:[\"#004DFF\",\"#FFEE00\",\"#0D0221\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L1\",finish:\"glaze\"},{id:\"pal-89\",name:\"Spruce Bonfire\",tagline:\"Dark spruce bonfire orange\",colors:[\"#23493C\",\"#FF7A3D\",\"#0C1735\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L3\",finish:\"matte\"},{id:\"pal-90\",name:\"Lilac Foundry\",tagline:\"Lilac meets foundry steel\",colors:[\"#9813E7\",\"#7B8894\",\"#D4522A\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L4\",finish:\"brushed-metal\"},{id:\"pal-91\",name:\"Topaz Battle\",tagline:\"Topaz gold navy clash\",colors:[\"#F0B429\",\"#182952\",\"#FA007D\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L1\",finish:\"polished-metal\"},{id:\"pal-92\",name:\"Dusk Celery\",tagline:\"Dusky purple celery snap\",colors:[\"#574B6B\",\"#C8D86A\",\"#FA2100\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L3\",finish:\"matte\"},{id:\"pal-93\",name:\"Tide Copperfuse\",tagline:\"Tidal teal copperfuse glow\",colors:[\"#0F7B7B\",\"#C07840\",\"#C2F000\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L4\",finish:\"pearl\"},{id:\"pal-94\",name:\"Punch Driftwood\",tagline:\"Fruit punch on driftwood\",colors:[\"#E63362\",\"#6E5B4A\",\"#1648EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L1\",finish:\"watercolor\"},{id:\"pal-95\",name:\"Ink Lime\",tagline:\"Ink smudge streaked lime\",colors:[\"#23232A\",\"#A8E10C\",\"#7E10EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L3\",finish:\"glaze\"},{id:\"pal-96\",name:\"Porcelain Prism\",tagline:\"Porcelain violet over tidal blue\",colors:[\"#F5F1E8\",\"#BD05DA\",\"#005F73\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L4\",finish:\"marbled\"},{id:\"pal-97\",name:\"Wasabi Cloud\",tagline:\"Wasabi heat cloud gray\",colors:[\"#9FBF3B\",\"#8E94A0\",\"#E71363\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L1\",finish:\"matte\"},{id:\"pal-98\",name:\"Eggplant Flare\",tagline:\"Eggplant deep flare yellow\",colors:[\"#41284E\",\"#FFD23F\",\"#5BC0BE\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L3\",finish:\"glitter\"},{id:\"pal-99\",name:\"Stone Rose\",tagline:\"Stone gray antique rose\",colors:[\"#8F8A82\",\"#C46E8E\",\"#2F6DA3\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L4\",finish:\"brushed-metal\"},{id:\"pal-100\",name:\"Neon Quasar Pop\",tagline:\"Yellow green comet over indigo gravity\",colors:[\"#FFEF00\",\"#00FF3C\",\"#2D02B8\"],unlockRule:\"battle-pass\",reward:\"lume-1:L1\",finish:\"glaze\"},{id:\"pal-101\",name:\"Arctic Rust\",tagline:\"Arctic white rusted orange\",colors:[\"#E9F2F5\",\"#9E4A1D\",\"#3E3F8F\"],unlockRule:\"battle-pass\",reward:\"lume-1:L3\",finish:\"brushed-metal\"},{id:\"pal-102\",name:\"Magenta Selvage\",tagline:\"Magenta edge denim blue\",colors:[\"#D8117E\",\"#3B5B8C\",\"#E7D313\"],unlockRule:\"battle-pass\",reward:\"lume-1:L4\",finish:\"matte\"},{id:\"pal-103\",name:\"Haze Saffron\",tagline:\"Purple haze saffron spark\",colors:[\"#8467B8\",\"#F4A81D\",\"#1D4E50\"],unlockRule:\"battle-pass\",reward:\"food:L1\",finish:\"pearl\"},{id:\"pal-104\",name:\"Neon Cyber Reed\",tagline:\"Green circuitry humming beside lemon static\",colors:[\"#00FF66\",\"#FFEA00\",\"#4B0082\"],unlockRule:\"battle-pass\",reward:\"food:L2\",finish:\"glitter\"},{id:\"pal-105\",name:\"Neon Magma Bloom\",tagline:\"Molten orange meeting synthetic magenta\",colors:[\"#FF2E00\",\"#F000FF\",\"#0A0A0F\"],unlockRule:\"battle-pass\",reward:\"food:L3\",finish:\"polished-metal\"},{id:\"pal-106\",name:\"Dune Violet\",tagline:\"Warm dune electric violet\",colors:[\"#C9AE7E\",\"#7D2BF0\",\"#13E7E3\"],unlockRule:\"battle-pass\",reward:\"food:L4\",finish:\"marbled\"},{id:\"pal-107\",name:\"Ember Nightshade\",tagline:\"Violet shadows ember teal\",colors:[\"#523090\",\"#EF4E23\",\"#0C5D68\"],unlockRule:\"battle-pass\",reward:\"food:L5\",finish:\"watercolor\"},{id:\"opal-jelly\",name:\"Opal Jelly\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b3fff0\",\"#9767ff\",\"#fff1ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"bubble-glass\",name:\"Bubble Glass\",tagline:\"Shifting highlights and luminous color\",colors:[\"#58eaff\",\"#b8fff8\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"prism-crystal\",name:\"Prism Crystal\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b18aff\",\"#447bff\",\"#fff2ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"holo-foil\",name:\"Holographic Foil\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff54de\",\"#39e5ff\",\"#fff56b\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"glitter-resin\",name:\"Glitter Resin\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff319c\",\"#7025ff\",\"#fff6db\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"galaxy-geode\",name:\"Galaxy Geode\",tagline:\"Shifting highlights and luminous color\",colors:[\"#21134e\",\"#7b39e8\",\"#63ffff\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"caustic-slime\",name:\"Caustic Slime\",tagline:\"Organic horror surface\",colors:[\"#8aa52b\",\"#20300b\",\"#dcff57\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"blister-hide\",name:\"Blister Hide\",tagline:\"Organic horror surface\",colors:[\"#9e435f\",\"#2d1727\",\"#e7bb72\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"rotten-rind\",name:\"Rotten Rind\",tagline:\"Organic horror surface\",colors:[\"#626124\",\"#211916\",\"#a2b16b\"],unlockRule:\"battle-pass\",finish:\"matte\"},{id:\"parasite-nest\",name:\"Parasite Nest\",tagline:\"Organic horror surface\",colors:[\"#786541\",\"#151c14\",\"#ece2b0\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"exposed-sinew\",name:\"Exposed Sinew\",tagline:\"Organic horror surface\",colors:[\"#b72945\",\"#300b24\",\"#efb5a1\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"abyssal-maw\",name:\"Abyssal Maw\",tagline:\"Organic horror surface\",colors:[\"#522045\",\"#100d19\",\"#d9e6a7\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"circuit-alloy\",name:\"Circuit Alloy\",tagline:\"Machine surface\",colors:[\"#273f50\",\"#0a111c\",\"#4effdc\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"servo-armor\",name:\"Servo Armor\",tagline:\"Machine surface\",colors:[\"#617080\",\"#141b25\",\"#ff6739\"],unlockRule:\"battle-pass\",finish:\"brushed-metal\"},{id:\"chrome-rib\",name:\"Chrome Rib\",tagline:\"Machine surface\",colors:[\"#bfcde1\",\"#202f46\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"carbon-mech\",name:\"Carbon Mech\",tagline:\"Machine surface\",colors:[\"#323d46\",\"#080d15\",\"#95bac9\"],unlockRule:\"battle-pass\"},{id:\"hazard-panel\",name:\"Hazard Panel\",tagline:\"Machine surface\",colors:[\"#edb932\",\"#131a20\",\"#ffe9a2\"],unlockRule:\"battle-pass\"},{id:\"reactor-glass\",name:\"Reactor Glass\",tagline:\"Machine surface\",colors:[\"#48e8ff\",\"#063a55\",\"#d4ffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"}];var ka=[{id:\"nails\",label:\"Nails\",short:\"Nails\",description:\"Five claw and nail forms\"},{id:\"fingertips\",label:\"Finger tips\",short:\"Tips\",description:\"Distal pads and final phalanges\"},{id:\"fingers\",label:\"Fingers & knuckles\",short:\"Fingers\",description:\"Joined finger shafts, bases and knuckles\"},{id:\"palm\",label:\"Palm\",short:\"Palm\",description:\"The inner hand surface\"},{id:\"back_of_hand\",label:\"Back of hand\",short:\"Back\",description:\"The dorsal hand surface\"},{id:\"wrist\",label:\"Wrist\",short:\"Wrist\",description:\"Stump, cuff and transition\"}],wa=[{id:0,name:\"Mortal\",realm:\"Baseline \\xB7 Earth\",primary:\"#b9856f\",secondary:\"#714c42\",accent:\"#f0c0a3\",emissive:\"#000000\",roughness:.72,metalness:.02,detail:\"clean\"},{id:1,name:\"Verdant\",realm:\"Wildroot \\xB7 Dimension 12\",primary:\"#3f7448\",secondary:\"#1e3b2b\",accent:\"#b8dd6e\",emissive:\"#172d13\",roughness:.88,metalness:0,detail:\"vine\"},{id:2,name:\"Mycelial\",realm:\"Sporesea \\xB7 Dimension 09\",primary:\"#d5c7ae\",secondary:\"#776b82\",accent:\"#f59ec4\",emissive:\"#6d234a\",roughness:.92,metalness:0,detail:\"caps\"},{id:3,name:\"Chitin\",realm:\"Carapace \\xB7 Dimension 31\",primary:\"#4f263d\",secondary:\"#180f20\",accent:\"#d17663\",emissive:\"#240716\",roughness:.3,metalness:.28,detail:\"plates\"},{id:4,name:\"Reptilian\",realm:\"Scalehold \\xB7 Dimension 04\",primary:\"#5f7a3f\",secondary:\"#283621\",accent:\"#cfb85c\",emissive:\"#101c08\",roughness:.76,metalness:.04,detail:\"scales\"},{id:5,name:\"Abyssal\",realm:\"Deep Trench \\xB7 Dimension 88\",primary:\"#132a42\",secondary:\"#06111f\",accent:\"#4edfd3\",emissive:\"#0b756f\",roughness:.34,metalness:.12,detail:\"spines\"},{id:6,name:\"Coral\",realm:\"Living Reef \\xB7 Dimension 22\",primary:\"#e46867\",secondary:\"#70344c\",accent:\"#ffd27a\",emissive:\"#8b2438\",roughness:.83,metalness:0,detail:\"coral\"},{id:7,name:\"Skeletal\",realm:\"Ossuary \\xB7 Dimension 00\",primary:\"#d8d0af\",secondary:\"#80785f\",accent:\"#fff7d1\",emissive:\"#17160e\",roughness:.9,metalness:0,detail:\"bone\"},{id:8,name:\"Spectral\",realm:\"Veil \\xB7 Dimension 13\",primary:\"#8ed9cd\",secondary:\"#294c58\",accent:\"#d5fff8\",emissive:\"#248e86\",roughness:.18,metalness:.04,detail:\"mist\"},{id:9,name:\"Infernal\",realm:\"Cinder Court \\xB7 Dimension 66\",primary:\"#7b211e\",secondary:\"#260b0a\",accent:\"#ff8a2a\",emissive:\"#d33812\",roughness:.58,metalness:.1,detail:\"flame\"},{id:10,name:\"Celestial\",realm:\"High Orbit \\xB7 Dimension 07\",primary:\"#e6dca4\",secondary:\"#786da7\",accent:\"#fffbd7\",emissive:\"#8d74d6\",roughness:.24,metalness:.5,detail:\"halo\"},{id:11,name:\"Voidborn\",realm:\"Null Expanse \\xB7 Dimension \\u2205\",primary:\"#16101f\",secondary:\"#060409\",accent:\"#9f64ff\",emissive:\"#482285\",roughness:.16,metalness:.36,detail:\"void\"},{id:12,name:\"Eldritch\",realm:\"Watcher Fold \\xB7 Dimension 47\",primary:\"#6b426c\",secondary:\"#252038\",accent:\"#b6ef63\",emissive:\"#4e7628\",roughness:.64,metalness:.04,detail:\"eyes\"},{id:13,name:\"Stone Golem\",realm:\"Deep Strata \\xB7 Dimension 19\",primary:\"#6f7068\",secondary:\"#393b37\",accent:\"#b7a37d\",emissive:\"#171811\",roughness:.98,metalness:0,detail:\"rock\"},{id:14,name:\"Crystal\",realm:\"Prism Vault \\xB7 Dimension 52\",primary:\"#896bc2\",secondary:\"#302e66\",accent:\"#e0c8ff\",emissive:\"#5a3da8\",roughness:.12,metalness:.3,detail:\"crystal\"},{id:15,name:\"Magma\",realm:\"Mantle \\xB7 Dimension 03\",primary:\"#4a1713\",secondary:\"#140706\",accent:\"#ffb22f\",emissive:\"#e44712\",roughness:.78,metalness:.06,detail:\"magma\"},{id:16,name:\"Glacial\",realm:\"White Silence \\xB7 Dimension 71\",primary:\"#93c9df\",secondary:\"#315a76\",accent:\"#ebfdff\",emissive:\"#3c93bb\",roughness:.2,metalness:.14,detail:\"ice\"},{id:17,name:\"Stormcharged\",realm:\"Tempest Ring \\xB7 Dimension 28\",primary:\"#48536e\",secondary:\"#1a2236\",accent:\"#c3f7ff\",emissive:\"#3ebde0\",roughness:.4,metalness:.32,detail:\"storm\"},{id:18,name:\"Clockwork Robot\",realm:\"Brass Meridian \\xB7 Dimension 14\",primary:\"#9a6a37\",secondary:\"#33251d\",accent:\"#efd08c\",emissive:\"#5b2c12\",roughness:.32,metalness:.82,detail:\"gears\"},{id:19,name:\"Neon Synth\",realm:\"Aftergrid \\xB7 Dimension 20\",primary:\"#251d54\",secondary:\"#0b0921\",accent:\"#ff5bd7\",emissive:\"#b519ac\",roughness:.22,metalness:.55,detail:\"neon\"},{id:20,name:\"Fluffy\",realm:\"Cloudburrow \\xB7 Soft forms\",primary:\"#d6a987\",secondary:\"#bb8969\",accent:\"#ffe4c3\",emissive:\"#000000\",roughness:.88,metalness:0,detail:\"fur\"},{id:21,name:\"Jelly\",realm:\"Lime Lagoon \\xB7 Soft forms\",primary:\"#a7ec69\",secondary:\"#f1ffd4\",accent:\"#c9ff90\",emissive:\"#000000\",roughness:.13,metalness:0,detail:\"jelly\"},{id:22,name:\"Baby\",realm:\"Little One \\xB7 Soft forms\",primary:\"#efb398\",secondary:\"#d68e7c\",accent:\"#ffe2d5\",emissive:\"#000000\",roughness:.52,metalness:0,detail:\"baby\"}],Sa=[\"opal-jelly\",\"bubble-glass\",\"prism-crystal\",\"holo-foil\",\"glitter-resin\",\"galaxy-geode\",\"caustic-slime\",\"blister-hide\",\"rotten-rind\",\"parasite-nest\",\"exposed-sinew\",\"abyssal-maw\",\"circuit-alloy\",\"servo-armor\",\"chrome-rib\",\"carbon-mech\",\"hazard-panel\",\"reactor-glass\"],z=[...wa,...[{id:30,name:\"Flat\",roughness:.55,metalness:0},{id:31,name:\"Clay\",roughness:.92,metalness:0},...ne,Te(56),...se].map(e=>{let a=Ie.find(s=>s.id===Sa[e.id-57]),[r,t,o]=a?.colors??[\"#8b8f9a\",\"#4a4d55\",\"#e7e9ee\"];return{id:e.id,name:e.name,realm:\"Surface finish\",primary:r,secondary:t,accent:o,emissive:\"#000000\",roughness:e.roughness,metalness:e.metalness,detail:e.name.toLowerCase(),paletteId:a?.id}})];var Lr=z.filter(e=>![8,11].includes(e.id)).map(e=>({name:e.name,description:`A pure ${e.name.toLowerCase()} hand`,values:Object.fromEntries(ka.map(a=>[a.id,e.id]))})),Mr=z.filter(e=>![8,11].includes(e.id)).sort((e,a)=>+(a.id>=63)-+(e.id>=63)||+(a.id>=57)-+(e.id>=57));var Ra=new Set([\"https://mominc.online\",\"https://www.mominc.online\",\"https://myr5.mominc.online\"]),ie=!1;window.addEventListener(\"message\",async e=>{if(!(e.source!==window.parent||!Ra.has(e.origin)||e.data?.type!==\"handborne:unlock-request\"||ie)){ie=!0;try{let a=null;try{localStorage.getItem(\"myr5-login-provider\")===\"clerk\"&&await A();let o=await N(\"/api/account\",{guestSkip:!0});o.ok&&(a=await o.json())}catch{}globalThis.myr5AuthenticatedAccount=a;let r=te(),t=z.filter(o=>o.id>=57&&o.paletteId&&oe(\"texture\",o.paletteId,r)&&oe(\"palette\",o.paletteId,r)).map(o=>o.id);window.parent.postMessage({type:\"handborne:unlock-state\",styles:t},e.origin)}finally{ie=!1}}});\n","handborne/unlocks.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Coach material ownership</title><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><script type=\"module\" src=\"/handborne/unlock-bridge.mjs\"></script></body></html>\r\n","hardware-launch.css":":root{--pixel-font:ui-monospace,'Cascadia Mono',monospace}\r\n.panel-heading>span,.calibration-grid label,.goal-control label{font-size:11px}.panel-heading>small,.signal-control>small,.manual-button,.slider-scale{font-size:12px}.goal-control output{font-size:16px}.calibration-grid{align-items:center}.dial-controls button{min-width:36px;min-height:44px}.exercise-control{padding-right:8px}.exercise-control>strong{font-size:17px}.rocker-switch{min-width:64px;min-height:72px}.signal-control>small{max-width:140px;margin:auto;line-height:1.5}.coach-line #coachCaption{font-size:14px}\r\n.workout-console #hud{justify-content:center;text-align:center;padding:14px 8px;margin:0 0 16px;background:#1c1820!important;border:2px solid #211a26;border-radius:5px;box-shadow:inset 2px 3px 8px #0009,0 1px #aa899e}.counter-target{justify-content:center}.workout-console #primary{--tile-width:43px;--tile-height:60px;--digit-size:45px}.workout-console #status{font-size:13px}.workout-console .set-actions{flex-basis:auto}.set-actions .primary-action small{font-size:11px}.set-actions .primary-action span{font-size:10px}.workout-console #stop{font-size:13px}\r\n/* W2-2A: the bar's hardware skin; its layout lives in launch.css. */\r\n#coachDock.coach-dock{border-top:3px solid #a38e9a;background:repeating-linear-gradient(0deg,#d9c5d604 0 1px,transparent 1px 3px),linear-gradient(125deg,#564754,#322938 60%,#635163);box-shadow:inset 0 2px #d7bdcf66,0 -5px 14px #08060c66}\r\n#coachDock.coach-dock button:not(.dock-portal){position:relative;flex-direction:column;gap:5px;font:700 10px var(--pixel-font);letter-spacing:0;border:2px solid #a89e98;border-top-color:#e8dfc5;border-left-color:#d1c5b3;border-radius:6px;color:#2b303c;background:linear-gradient(135deg,#eee9d7,#c7c9ba 70%,#a5afa4);box-shadow:inset 0 2px #fff9,inset -2px -3px #72817a66,0 3px #475050,0 4px #13111a;transition:transform .08s,box-shadow .08s}\r\n#coachDock.coach-dock button:not(.dock-portal):before{content:'';width:18px;height:6px;border:1px solid #27232d;background:#574a4b;border-radius:2px;box-shadow:inset 0 1px 2px #0008}\r\n#coachDock.coach-dock button[data-panel=meals]{background:linear-gradient(135deg,#e7bd7f,#bc8c55);border-color:#edc796;color:#32232a}#coachDock.coach-dock button[data-panel=reminders]{background:linear-gradient(135deg,#85b5aa,#4f847e);border-color:#a9c7b7;color:#172f30}#coachDock.coach-dock button[data-panel=install]{background:linear-gradient(135deg,#ae9bab,#74677f);border-color:#b7a6b8;color:#fff2d5}\r\n#coachDock.coach-dock button:not(.dock-portal):is(:active,[aria-current=page]){transform:translateY(3px);box-shadow:inset 1px 3px 5px #39444366,0 1px #475050,0 1px #13111a}#coachDock.coach-dock button[aria-current=page]:before{background:#eab56d;box-shadow:0 0 7px #ffc875aa}\r\n.launch-panel{border:3px solid #9c8394;border-top-color:#c6aeba;border-bottom-color:#4d3c51;border-radius:9px;background:repeating-linear-gradient(0deg,#e9cfdf05 0 1px,transparent 1px 3px),linear-gradient(125deg,#574354,#33293d 56%,#46384b);box-shadow:inset 0 2px #e7cedb66,0 6px #1e1725,8px 15px 50px #000a;padding:24px}.launch-panel header{padding-bottom:16px;border-bottom:2px solid #231b2a;box-shadow:0 1px #a58b9866}.launch-panel h2{font:750 24px var(--pixel-font);color:#f6dfb7}.launch-panel h3{font:700 18px var(--pixel-font);color:#f6dfb7}.launch-panel label{color:#e6d2dd}\r\n.launch-panel button,.launch-panel .action-link{border:2px solid #b297a8;border-top-color:#d1bccc;border-bottom-color:#71596f;border-radius:5px;background:linear-gradient(135deg,#93798d,#5f4c64);box-shadow:inset 0 2px #efd7e355,inset -2px -3px #34253566,0 4px #251b2d,0 6px #17111d;color:#fff0d7;font:650 14px var(--pixel-font);min-height:48px;transition:transform .08s,box-shadow .08s}\r\n.launch-panel button:active:not(:disabled),.launch-panel .action-link:active{transform:translateY(3px);box-shadow:inset 1px 3px 6px #24182d88,0 1px #251b2d}.launch-panel .main-action{border-color:#e9c18c;background:linear-gradient(#e6bc7c,#ba814b);color:#2e222a;box-shadow:inset 0 2px #fff0c5,inset -2px -3px #76503166,0 4px #583c30,0 6px #1c1420}.launch-panel header button{min-height:44px;font-size:12px}\r\n.launch-panel input,.launch-panel select{background:#201b27;border:2px solid #201728;border-bottom-color:#997d90;box-shadow:inset 2px 3px 5px #0007;border-radius:4px;color:#f7e8d0}.launch-panel input:focus-visible,.launch-panel select:focus-visible{outline:3px solid #f1c482;outline-offset:3px}.launch-panel input[type=file]{padding:8px}.launch-panel input::file-selector-button{margin-right:10px;border:2px solid #c1bba4;border-radius:4px;background:linear-gradient(#ede9d6,#b7bdad);box-shadow:0 3px #687467;color:#273b39;padding:10px;cursor:pointer}\r\n.launch-panel .status{border:2px solid #23192a;border-bottom-color:#957788;background:#221c29;color:#e9d7ba;box-shadow:inset 1px 2px 4px #0006;border-radius:4px;font-size:14px}.launch-panel .hint{color:#d2bccb}.launch-panel .entry{border-color:#8d728466;align-items:center}.launch-panel .entry small{color:#cdb7ca}.launch-panel .nutrition{margin:14px 0}.launch-panel form>.main-action{margin-top:7px}\r\n.switch-plate{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px;border:2px solid #977c8a;border-radius:6px;background:linear-gradient(130deg,#71586b,#3d3045);box-shadow:inset 0 1px #d5b9c966,0 3px #201625;margin:15px 0}.switch-plate>div{flex:1}.switch-plate strong{display:block;font:700 14px var(--pixel-font);color:#f0dabb}.switch-plate small{display:block;font-size:12px;color:#ceb8cb;margin-top:5px}.switch-plate .actions{margin-top:12px}\r\n.launch-panel .hardware-toggle{display:inline-flex;align-items:center;justify-content:space-between;gap:10px;min-width:102px;min-height:48px;padding:7px 10px 7px 7px;background:linear-gradient(#332738,#211927);border:2px solid #a48a99;box-shadow:inset 2px 3px 6px #000b,0 2px #b298a766;color:#d3bdcf;border-radius:6px;font-size:12px;flex-shrink:0}\r\n.hardware-toggle:before{content:'';width:32px;height:30px;border:2px solid #c8b7ae;border-radius:4px;background:repeating-linear-gradient(90deg,#b4a4a6 0 4px,#8d7d88 4px 5px);box-shadow:inset 0 2px #e5d7ca,2px 3px 3px #0009;transition:transform .12s}.launch-panel .hardware-toggle[aria-checked=true]{flex-direction:row-reverse;color:#fee1a0;background:linear-gradient(#6e573d,#423126)}.hardware-toggle[aria-checked=true]:before{background:repeating-linear-gradient(90deg,#e2b775 0 4px,#b58d59 4px 5px);border-color:#f1cf94}\r\n.launch-panel .switch-label{display:flex;align-items:center;justify-content:space-between;gap:15px;padding:12px 0;font:650 14px var(--pixel-font)}.launch-panel input[type=checkbox][role=switch]{appearance:none;position:relative;flex-shrink:0;width:76px;height:40px;margin:0;border:3px solid #211827;border-radius:5px;background:linear-gradient(#2e2536,#19151e);box-shadow:0 0 0 1px #a08796,inset 2px 2px 5px #0007;cursor:pointer;padding:0}.launch-panel input[type=checkbox][role=switch]:before{content:'';position:absolute;inset:3px auto 3px 3px;width:28px;background:repeating-linear-gradient(90deg,#b1a0a8 0 4px,#8e7f8c 4px 5px);border:1px solid #d4c5c4;box-shadow:1px 2px 3px #0009;border-radius:2px;transition:transform .12s}.launch-panel input[type=checkbox][role=switch]:checked{background:#6c4e30}.launch-panel input[type=checkbox][role=switch]:checked:before{transform:translateX(34px);background:repeating-linear-gradient(90deg,#e7bd7c 0 4px,#bf905f 4px 5px);border-color:#f6d899}\r\n.nutrition-switch summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:50px;font-size:14px;cursor:pointer;list-style:none}.nutrition-switch summary:after{content:'OFF';display:flex;align-items:center;justify-content:center;width:65px;height:34px;background:linear-gradient(#8a7485,#58465d);border:3px solid #25192c;border-radius:4px;box-shadow:inset 0 2px #bca3b4,0 2px #ad93a755;font:700 12px var(--pixel-font);color:#eddae6}.nutrition-switch[open] summary:after{content:'ON';background:linear-gradient(#a98454,#705034);color:#ffe9b8;box-shadow:inset 0 -2px #dcb27b,0 2px #ad93a755}.reminder-entry{flex-wrap:wrap}.reminder-entry>.entry-copy{flex:1;min-width:160px}.reminder-controls{display:flex;gap:9px;align-items:center}.reminder-controls>button:last-child{min-width:44px;padding:8px;font-size:12px}.reminder-entry[data-enabled=false] .entry-copy{opacity:.65}\r\n@media(min-width:900px) and (pointer:fine){body{max-width:940px}.pod-page{padding:0 38px 25px}.containment-bay .containment-pod{height:420px}.calibration-grid{padding:24px}.exercise-dial{width:112px;height:112px}.panel-heading>strong{font-size:25px}.exercise-control>strong{font-size:21px}.rocker-switch{width:78px;min-height:82px}.coach-line #coachCaption{font-size:18px}.workout-console #primary{--tile-width:50px;--tile-height:70px;--digit-size:53px}}\r\n@media(max-width:420px){.launch-panel{padding:18px 14px}.switch-plate{padding:11px;gap:8px}.switch-plate strong{font-size:13px}.launch-panel .hardware-toggle{min-width:91px;gap:7px}.launch-panel .entry{gap:10px}.panel-heading>strong{font-size:17px}.calibration-grid label{font-size:10px}.exercise-dial{width:76px;height:76px}.dial-controls{gap:6px}.dial-controls button{min-width:30px}}\r\n","hologram.mjs":"// Three.js is imported only on opening a model. AGPL-3.0-or-later app wrapper.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\r\nimport {demoPose,DEMO_BONES} from './demo-poses.mjs';\r\nimport {coachFetch} from './coach-net.mjs';\r\nimport {claimGraphics} from './modules/graphics-lifecycle.mjs';\r\nexport async function createHologram(host,name,{still=false}={}){\r\n  let renderer,releaseGpu=null,controls,frame=0,observer,mixer,model,animateDemo=null,demoTime=0,disposed=false,playing=true;\r\n  const scene=new THREE.Scene(),pivot=new THREE.Group();scene.add(pivot);\r\n  function disposeObject(object){object.traverse(node=>{node.geometry?.dispose();if(node.material)for(const mat of [node.material].flat()){for(const value of Object.values(mat))if(value?.isTexture)value.dispose();mat.dispose();}});}\r\n  function dispose(){if(disposed)return;disposed=true;cancelAnimationFrame(frame);observer?.disconnect();controls?.dispose();mixer?.stopAllAction();if(model)mixer?.uncacheRoot(model);disposeObject(scene);renderer?.dispose();renderer?.forceContextLoss();renderer?.domElement.remove();releaseGpu?.(renderer?.domElement);releaseGpu=null;}\r\n  // The hidden portal board yields (the 3D coach stays beside the demo); it rebuilds once this closes.\r\n  releaseGpu=claimGraphics('hologram',{keep:['coach']});await releaseGpu.ready;\r\n  try{\r\n    renderer=new THREE.WebGLRenderer({alpha:true,antialias:true,powerPreference:'low-power'});renderer.setPixelRatio(Math.min(window.devicePixelRatio||1,1.5));renderer.setClearColor(0x000000,0);host.append(renderer.domElement);\r\n    const camera=new THREE.PerspectiveCamera(38,1,.01,100);controls=new OrbitControls(camera,renderer.domElement);controls.enablePan=false;controls.enableZoom=false;controls.enableDamping=true;controls.minDistance=1.5;controls.maxDistance=12;controls.minPolarAngle=.15;controls.maxPolarAngle=Math.PI-.15;\r\n    // Offline or slow: the procedural wireframe figure below stands in for the downloaded model.\r\n    const got=['squat','pushup'].includes(name)?await coachFetch('/models/'+name+'.glb',{},{timeoutMs:20000,parse:'arrayBuffer',local:true}):null;\r\n    if(got?.ok){\r\n    const bytes=got.data;\r\n    const gltf=await new GLTFLoader().parseAsync(bytes,'');model=gltf.scene;\r\n    // Fit using the first animation pose, not the rig's rest pose.\r\n    mixer=new THREE.AnimationMixer(model);gltf.animations.forEach(clip=>mixer.clipAction(clip).play());mixer.update(0);model.updateMatrixWorld(true);\r\n    const box=new THREE.Box3().setFromObject(model),size=box.getSize(new THREE.Vector3()),center=box.getCenter(new THREE.Vector3()),scale=2/Math.max(size.x,size.y,size.z);\r\n    const offset=new THREE.Group();offset.position.copy(center).multiplyScalar(-1);offset.add(model);const normalized=new THREE.Group();normalized.scale.setScalar(scale);normalized.add(offset);pivot.add(normalized);\r\n    model.traverse(node=>{if(node.isMesh){const old=[node.material].flat();node.material=new THREE.MeshBasicMaterial({color:0x9afae4,wireframe:true,transparent:true,opacity:.72});for(const mat of old){for(const value of Object.values(mat))if(value?.isTexture)value.dispose();mat.dispose();}node.frustumCulled=false;}});\r\n    }else{\r\n      model=new THREE.Group();pivot.add(model);\r\n      const material=new THREE.MeshBasicMaterial({color:0x9afae4,wireframe:true,transparent:true,opacity:.8});\r\n      const joints=Object.fromEntries(Object.keys(demoPose(name)).map(key=>{const joint=new THREE.Mesh(new THREE.SphereGeometry(key==='head'?.13:.047,12,8),material);model.add(joint);return [key,joint];}));\r\n      const bones=DEMO_BONES.map(()=>{const mesh=new THREE.Mesh(new THREE.CylinderGeometry(.035,.045,1,8,4),material);model.add(mesh);return mesh;});\r\n      const up=new THREE.Vector3(0,1,0),a=new THREE.Vector3(),b=new THREE.Vector3(),direction=new THREE.Vector3();\r\n      animateDemo=t=>{const points=demoPose(name,t);for(const [key,joint] of Object.entries(joints))joint.position.fromArray(points[key]);DEMO_BONES.forEach(([from,to],i)=>{a.fromArray(points[from]);b.fromArray(points[to]);const mesh=bones[i];direction.subVectors(b,a);mesh.position.copy(a).add(b).multiplyScalar(.5);mesh.scale.y=direction.length();mesh.quaternion.setFromUnitVectors(up,direction.normalize());});};animateDemo(0);\r\n    }\r\n    const ring=new THREE.Mesh(new THREE.RingGeometry(1.02,1.035,64),new THREE.MeshBasicMaterial({color:0x8cf6e0,transparent:true,opacity:.35,side:THREE.DoubleSide}));ring.rotation.x=-Math.PI/2;ring.position.y=-1.05;scene.add(ring);\r\n    function reset(){pivot.rotation.set(0,['boxing','jogging'].includes(name)?-.45:0,0);camera.position.set(0,.15,Math.max(4.8,3.6/(host.clientWidth/Math.max(host.clientHeight,1))));controls.target.set(0,0,0);controls.update();}\r\n    function resize(){const r=host.getBoundingClientRect();renderer.setSize(Math.max(r.width,1),Math.max(r.height,1),false);camera.aspect=r.width/Math.max(r.height,1);camera.updateProjectionMatrix();}\r\n    observer=new ResizeObserver(resize);observer.observe(host);reset();resize();let last=performance.now();\r\n    function draw(now){if(disposed)return;const dt=Math.min((now-last)/1000,.06);last=now;if(playing){mixer?.update(dt);demoTime+=dt;animateDemo?.(demoTime);}controls.update();renderer.render(scene,camera);frame=requestAnimationFrame(draw);}if(!still)frame=requestAnimationFrame(draw);\r\n    return {dispose,reset,capturePose:async time=>{mixer?.setTime(time);animateDemo?.(time);controls.update();renderer.render(scene,camera);return new Promise(resolve=>renderer.domElement.toBlob(resolve,'image/png'));},toggle:()=>playing=!playing,rotate:(x,y)=>{pivot.rotation.y+=x;pivot.rotation.x=THREE.MathUtils.clamp(pivot.rotation.x+y,-1.2,1.2);}};\r\n  }catch(error){dispose();throw error;}\r\n}\r\n","install-context.mjs":"export function isInstalled(win=window){return win.matchMedia('(display-mode: standalone)').matches||win.navigator.standalone===true;}\r\nexport function setupAllowed(win=window){\r\n if(isInstalled(win)){try{win.sessionStorage.setItem('myr5-in-app-setup','1');}catch{}return true;}\r\n // Keep an in-app sign-in return usable if the identity provider changes its window context.\r\n try{return win.sessionStorage.getItem('myr5-in-app-setup')==='1';}catch{return false;}\r\n}\r\n","install-transfer.mjs":"import {readIncomingCoach,saveIncomingCoach} from './pending-coach.mjs';\r\nexport async function prepareInstall(data,fetcher=fetch){\r\n const response=await fetcher('/api/install-draft',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({data})});\r\n if(!response.ok)throw Error('Your coach could not be prepared for installation. Please retry.');\r\n}\r\nexport async function restoreInstall(fetcher=fetch,session=sessionStorage,local=localStorage,{signal}={}){\r\n const response=await fetcher('/api/install-draft',{credentials:'same-origin',cache:'no-store',signal});\r\n if(!response.ok)throw Object.assign(Error('Could not bring your coach into the app. Check your connection and retry.'),{status:response.status});\r\n const {data}=await response.json();signal?.throwIfAborted();\r\n if(data){saveIncomingCoach(data,session,local);await fetcher('/api/install-draft',{method:'DELETE',credentials:'same-origin',signal});return data;}\r\n return readIncomingCoach(session,local);\r\n}\r\n","install.css":":root{color-scheme:dark;font-family:Arial,sans-serif;color:#f5e4ba;background:#21172f}*{box-sizing:border-box}body{margin:0;min-height:100dvh;display:grid;place-items:center;padding:max(24px,env(safe-area-inset-top)) 20px max(24px,env(safe-area-inset-bottom));background:radial-gradient(ellipse at top,#54335d,#21172f 65%)}.install-card{width:min(100%,440px);text-align:center;padding:28px 24px;background:#2b2036;border:2px solid #ae9060;border-radius:24px;box-shadow:0 14px 50px #0b061770}.brand{font-size:.875rem;font-weight:700;letter-spacing:.2em;color:#d6b77d;margin-bottom:22px}.app-icon{border-radius:22px;display:block;margin:0 auto 22px}h1{font-size:2rem;line-height:1.1;margin:0 0 18px}p{font-size:1rem;line-height:1.5}button,.continue{width:100%;min-height:52px;border-radius:12px;font:700 1rem Arial;display:grid;place-content:center;text-decoration:none;padding:14px 18px}button{margin-top:24px;background:#e7c988;color:#21172f;border:2px solid #ffebb7;cursor:pointer}.continue{color:#b8e9cf;margin-top:10px;border:1px solid #65756e}button:focus-visible,a:focus-visible{outline:3px solid #b8e9cf;outline-offset:4px}.status,.note{font-size:.875rem;color:#d5c6b7}.note{border-top:1px solid #76614b;padding-top:16px;margin-bottom:0}#installGuide{text-align:left;border-radius:12px;background:#17101f;padding:18px;margin-top:18px}#installGuide[hidden]{display:none}h2{font-size:1.125rem;margin:0 0 12px}ol{padding-left:22px;margin:0}li{line-height:1.5;margin:8px 0}#installHelp{display:block;color:#b8e9cf;font-size:.875rem;margin-top:14px}#installHelp[hidden]{display:none}\r\n\r\n[hidden]{display:none!important}button:disabled{opacity:.6;cursor:wait}\r\n","install.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><meta name=\"referrer\" content=\"no-referrer\"><meta name=\"theme-color\" content=\"#21172f\"><meta name=\"apple-mobile-web-app-capable\" content=\"yes\"><title>Install your Coach · MOM INC</title><link rel=\"manifest\" href=\"/manifest.webmanifest?v=myr5-alien-v1\"><link rel=\"icon\" type=\"image/png\" href=\"/icons/myr5-alien-192.png\"><link rel=\"apple-touch-icon\" href=\"/icons/myr5-alien-apple.png\"><link rel=\"stylesheet\" href=\"/install.css\"><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main class=\"install-card\"><div class=\"brand\">MOM INC</div><img class=\"app-icon\" src=\"/icons/myr5-alien-192.png\" alt=\"Original purple MYR5 alien\" width=\"96\" height=\"96\"><h1>Install Coach</h1><p class=\"status\">Install MYR5, then open it from your home screen.</p><p id=\"coachSaved\" role=\"status\"></p><button id=\"installCoach\" type=\"button\" disabled>Loading…</button><p id=\"installStatus\" class=\"status\" role=\"status\"></p><section id=\"installGuide\" hidden aria-labelledby=\"guideTitle\"><h2 id=\"guideTitle\">Add to your home screen</h2><ol id=\"installSteps\"></ol><a id=\"installHelp\" href=\"https://support.apple.com/guide/iphone/open-as-web-app-iphea86e5236/ios\" target=\"_blank\" rel=\"noopener noreferrer\" hidden>Apple’s home-screen guide</a></section><noscript>Enable JavaScript to bring your coach into the app.</noscript></main><script type=\"module\" src=\"/install.mjs?v=quick-install-v3\"></script></body></html>\r\n","install.mjs":"import {decodeHandoff} from './onboarding-domain.mjs';\r\nimport {captureGala,mountGalaReturn,saveInstalledRun,prepareGalaInstall} from './gala-handoff.mjs';\r\ntry{captureGala();mountGalaReturn();}catch{}\r\nimport {readIncomingCoach,saveIncomingCoach} from './pending-coach.mjs';\r\nimport {isInstalled,setupAllowed} from './install-context.mjs';\r\nimport {prepareInstall} from './install-transfer.mjs';\r\nimport {prepareOfflineApp} from './offline-install.mjs';\r\nconst button=document.getElementById('installCoach'),status=document.getElementById('installStatus'),saved=document.getElementById('coachSaved');\r\nconst ios=/iPad|iPhone|iPod/.test(navigator.userAgent)||(navigator.platform==='MacIntel'&&navigator.maxTouchPoints>1),android=/Android/i.test(navigator.userAgent);\r\nlet prompt=null,ready=false;\r\nfunction guide(){\r\n const steps=ios?['Tap Share, then Add to Home Screen.','Leave Open as Web App on if shown, then tap Add.','Open the MYR5 icon on your home screen.']:android?['Open the ⋮ menu and tap Install app or Add to Home screen.','Confirm, then open MYR5 from your home screen.']:['Use your browser’s install icon beside the address bar, or its Install app menu item.','Open MYR5 from your apps.'];\r\n const list=document.getElementById('installSteps');list.replaceChildren(...steps.map(text=>{const li=document.createElement('li');li.textContent=text;return li;}));\r\n document.getElementById('installHelp').hidden=!ios;document.getElementById('installGuide').hidden=false;\r\n status.textContent=ios?'Add Coach, then open its home-screen icon.':'Install Coach, then open it from your apps. If installation is unavailable, use Chrome or Edge.';\r\n}\r\nfunction installed(){prompt=null;button.hidden=true;document.getElementById('installGuide').hidden=true;status.textContent='Installed. Open MYR5 to start.';}\r\nwindow.addEventListener('beforeinstallprompt',event=>{event.preventDefault();prompt=event;if(ready){button.textContent='Install Coach';status.textContent='Ready to install';}});\r\nwindow.addEventListener('appinstalled',installed);\r\nbutton.onclick=async()=>{if(!ready){await prepare();return;}if(!prompt){guide();return;}const current=prompt;prompt=null;try{await current.prompt();const choice=await current.userChoice;if(choice.outcome==='accepted')installed();else{status.textContent='Install cancelled';}}catch{guide();}};\r\nasync function prepare(){\r\n button.disabled=true;\r\n try{\r\n  status.textContent='Saving Coach on this device…';\r\n  try{await prepareGalaInstall();}catch{mountGalaReturn();}\r\n  const raw=new URLSearchParams(location.hash.slice(1)).get('coach'),incoming=raw?saveIncomingCoach(decodeHandoff(raw)):readIncomingCoach();\r\n  if(incoming){await prepareInstall(incoming);saved.textContent='Coach saved';}\r\n  if(raw)history.replaceState(null,'',location.pathname+location.search);\r\n  await prepareOfflineApp();\r\n  ready=true;button.disabled=false;button.textContent=ios?'Add Coach to Home Screen':'Install Coach';status.textContent='';\r\n  if(ios)guide();else setTimeout(()=>{if(!prompt)guide();},2500); // no install prompt (desktop/unsupported browser): show the steps instead of a dead button\r\n }catch(error){status.textContent=error.message;button.textContent='Retry';button.disabled=false;}\r\n}\r\nif(isInstalled()){await saveInstalledRun().catch(()=>{});setupAllowed();location.replace('/onboarding.html?from=install'+location.hash);}else await prepare();\r\n","launch-bootstrap.mjs":"export function showStartupFailure(document,error){\r\n const setup=document.getElementById('coachSetupGate');if(setup?.open)setup.close();\r\n let gate=document.getElementById('coachStartupRecovery');\r\n if(!gate){gate=document.createElement('dialog');gate.id='coachStartupRecovery';document.body.append(gate);}\r\n gate.setAttribute('aria-label','Reconnect Coach');\r\n gate.style.cssText='position:fixed;inset:0;margin:0;width:100vw;height:100dvh;max-width:none;max-height:none;border:0;z-index:2147483646;background:#17111ef5;display:grid;place-content:center;padding:28px;color:#f5e4ba;font:18px/1.6 Arial;text-align:center';\r\n gate.replaceChildren();\r\n const heading=document.createElement('h1'),message=document.createElement('p'),retry=document.createElement('a');\r\n heading.textContent='Reconnect Coach';message.textContent='Your saved coach is safe. Reload the app to reconnect.';\r\n retry.textContent='Reload Coach';retry.href='/pose.html?reconnect='+Date.now();retry.style.cssText='color:#b8e9cf;padding:14px;min-height:48px';\r\n gate.append(heading,message,retry);gate.hidden=false;if(!gate.open)gate.showModal();\r\n console.error('Coach startup failed',error);\r\n}\r\nexport async function bootCoach({load,document,timeout=30000}){\r\n const timer=setTimeout(()=>showStartupFailure(document,new Error('Startup timed out.')),timeout);\r\n try{await load();const recovery=document.getElementById('coachStartupRecovery');if(recovery){recovery.close();recovery.hidden=true;}return true;}catch(error){showStartupFailure(document,error);return false;}finally{clearTimeout(timer);}\r\n}\r\nif(typeof document!=='undefined'){\r\n const runtime=new URL('./launch-runtime.mjs',import.meta.url);runtime.search=new URL(import.meta.url).search;\r\n void bootCoach({document,load:()=>import(/* @vite-ignore */ runtime.href)});\r\n}\r\n","launch-shell.mjs":"export function mountLaunch(){document.body.insertAdjacentHTML('beforeend',`\r\n<span id=\"syncBadge\" class=\"sync-badge\" role=\"status\">Connecting…</span>\r\n<nav id=\"coachDock\" class=\"coach-dock ship-control-deck\" aria-label=\"Coach tools\"><button type=\"button\" class=\"dock-continue\" hidden></button><button type=\"button\" class=\"dock-control dock-settings\" data-route=\"customizeCoach\" aria-label=\"Customize coach\" title=\"Customize coach\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><circle cx=\"12\" cy=\"12\" r=\"6.5\"/><path d=\"M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9 7 7M17 17l2.1 2.1M4.9 19.1 7 17M17 7l2.1-2.1\"/></svg></span></button><button type=\"button\" class=\"dock-control dock-food\" data-panel=\"meals\" data-route=\"food\" aria-label=\"Food\" title=\"Food\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 8h4l2-3h6l2 3h4v11H3z\"/><circle cx=\"12\" cy=\"13\" r=\"3.5\"/></svg></span></button><button type=\"button\" class=\"dock-portal\" data-route=\"portal\" aria-label=\"Portal or workout pod\" title=\"Portal or workout pod\"><svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 11.5 12 4l9 7.5M5.5 10v10h13V10M10 20v-6h4v6\"/></svg><span aria-hidden=\"true\">PORTAL</span></button><button type=\"button\" class=\"dock-control dock-scoreboard\" data-panel=\"account\" data-route=\"scoreboard\" aria-label=\"Classroom leaderboard\" title=\"Classroom leaderboard\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"8\" r=\"3\"/><circle cx=\"17\" cy=\"9\" r=\"2.5\"/><path d=\"M3 20c0-4 3-6 6-6s6 2 6 6M15 14c3 0 6 1.5 6 5\"/></svg></span></button><button type=\"button\" class=\"dock-control dock-achievements\" data-route=\"achievements\" aria-label=\"Achievements\" title=\"Achievements\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 4h10v5a5 5 0 0 1-10 0zM7 6H3c0 3 1 4 4 4M17 6h4c0 3-1 4-4 4M12 14v4M8 21h8\"/></svg></span></button><button type=\"button\" class=\"dock-control dock-pod\" data-panel=\"reminders\" data-route=\"reminders\" aria-label=\"Reminders computer\" title=\"Reminders computer\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A15 15 0 0 1 3 6a2 2 0 0 1 2-2z\"/></svg></span></button><span class=\"dock-live\" role=\"status\" aria-live=\"polite\"></span></nav>\r\n<dialog id=\"historyPanel\" class=\"launch-panel\" aria-labelledby=\"historyTitle\"><header><h2 id=\"historyTitle\">History</h2><button data-close>Close</button></header><p class=\"hint\">Completed workouts saved on this device.</p><p id=\"localHistoryStatus\" role=\"status\"></p><div id=\"localHistoryList\"></div></dialog>\r\n<dialog id=\"mealsPanel\" class=\"launch-panel food-panel\" aria-labelledby=\"mealsTitle\"><header><h2 id=\"mealsTitle\">Food</h2><button data-close>Close</button></header>\r\n<button id=\"foodCamera\" class=\"main-action food-camera\" aria-label=\"Take a food photo\"><svg viewBox=\"0 0 24 24\" width=\"30\" height=\"30\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><path d=\"M8 5l2-2h4l2 2h4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2z\"/><circle cx=\"12\" cy=\"12\" r=\"4\"/></svg><span>Camera</span></button>\r\n<input id=\"foodPhoto\" type=\"file\" accept=\"image/jpeg,image/png,image/webp\" capture=\"environment\" hidden>\r\n<div class=\"food-sheet\"><section id=\"foodDial\" class=\"food-dial\" tabindex=\"-1\" aria-labelledby=\"foodDialTitle\" hidden><div class=\"food-dial-head\"><h3 id=\"foodDialTitle\"></h3><button type=\"button\" id=\"foodDialBack\">Back</button></div><div id=\"foodDialBody\"></div></section>\r\n<p id=\"foodStatus\" class=\"hint\" role=\"status\"></p>\r\n<div id=\"mealScanStage\" class=\"meal-scan-stage\" hidden><img id=\"foodPreview\" class=\"meal-preview\" alt=\"Your selected meal\"><div id=\"mealScanOverlay\" class=\"meal-scan-overlay\" data-state=\"ready\"><div class=\"scan-grid\" aria-hidden=\"true\"></div><div class=\"scan-reticle\" aria-hidden=\"true\"></div><div class=\"scan-beam\" aria-hidden=\"true\"></div><div class=\"scan-heading\"><span id=\"scanPhase\">FOOD SCANNER</span><time id=\"scanElapsed\" aria-label=\"Elapsed scan time\">00:00</time></div><div class=\"scan-readout\"><span id=\"scanDetail\"></span><progress id=\"scanProgress\" max=\"100\" aria-label=\"Current model file download\" hidden></progress></div></div></div>\r\n<div class=\"actions scan-actions\"><button id=\"recognizeFood\" hidden>Retry scan</button><button id=\"cancelFoodScan\" hidden>Stop scan</button></div>\r\n<div id=\"foodSuggestions\" class=\"actions\" hidden></div>\r\n<form id=\"mealForm\">\r\n <div id=\"mealConfirmation\" hidden><div class=\"fields\"><label>Food<input name=\"name\" id=\"mealName\" required maxlength=\"160\" autocomplete=\"off\"></label><label>Portion (g)<input id=\"mealGrams\" type=\"number\" min=\"1\" max=\"2000\" step=\"1\" value=\"100\" required></label></div><label id=\"foodReferenceLabel\">Food match<select id=\"foodReference\"></select></label><p id=\"nutritionBasis\" class=\"hint\" role=\"status\">Check food & portion</p></div>\r\n <section aria-labelledby=\"macroTitle\"><h3 id=\"macroTitle\">Macros</h3><div class=\"macro-grid\"><div class=\"nutrient\"><span>Calories</span><output id=\"nutrient-calories\">—</output></div><div class=\"nutrient\"><span>Protein</span><output id=\"nutrient-protein\">—</output></div><div class=\"nutrient\"><span>Carbs</span><output id=\"nutrient-carbs\">—</output></div><div class=\"nutrient\"><span>Fat</span><output id=\"nutrient-fat\">—</output></div></div></section>\r\n <section aria-labelledby=\"microTitle\"><h3 id=\"microTitle\">Micros</h3><div class=\"micro-grid\"><div class=\"nutrient\"><span>Calcium</span><output id=\"nutrient-calcium\">—</output></div><div class=\"nutrient\"><span>Iron</span><output id=\"nutrient-iron\">—</output></div><div class=\"nutrient\"><span>Magnesium</span><output id=\"nutrient-magnesium\">—</output></div><div class=\"nutrient\"><span>Potassium</span><output id=\"nutrient-potassium\">—</output></div><div class=\"nutrient\"><span>Sodium</span><output id=\"nutrient-sodium\">—</output></div><div class=\"nutrient\"><span>Zinc</span><output id=\"nutrient-zinc\">—</output></div><div class=\"nutrient\"><span>Vitamin A</span><output id=\"nutrient-vitaminA\">—</output></div><div class=\"nutrient\"><span>Vitamin C</span><output id=\"nutrient-vitaminC\">—</output></div><div class=\"nutrient\"><span>Vitamin D</span><output id=\"nutrient-vitaminD\">—</output></div><div class=\"nutrient\"><span>Vitamin E</span><output id=\"nutrient-vitaminE\">—</output></div><div class=\"nutrient\"><span>Vitamin B12</span><output id=\"nutrient-vitaminB12\">—</output></div><div class=\"nutrient\"><span>Folate</span><output id=\"nutrient-folate\">—</output></div></div><p class=\"hint nutrient-note\">— unavailable</p></section>\r\n <div id=\"mealSaveControls\" hidden><details id=\"nutritionAdjust\"><summary>Edit nutrition & time</summary><label>Eaten at<input name=\"eatenAt\" type=\"datetime-local\" required></label><div class=\"nutrition manual-nutrition\"><label>Calories (kcal)<input name=\"calories\" type=\"number\" min=\"0\" max=\"20000\" step=\"any\"></label><label>Protein (g)<input name=\"protein\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Carbs (g)<input name=\"carbs\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Fat (g)<input name=\"fat\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Calcium (mg)<input name=\"calcium\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Iron (mg)<input name=\"iron\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Magnesium (mg)<input name=\"magnesium\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Potassium (mg)<input name=\"potassium\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Sodium (mg)<input name=\"sodium\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Zinc (mg)<input name=\"zinc\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Vitamin A (µg RAE)<input name=\"vitaminA\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Vitamin C (mg)<input name=\"vitaminC\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Vitamin D (µg)<input name=\"vitaminD\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Vitamin E (mg)<input name=\"vitaminE\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Vitamin B12 (µg)<input name=\"vitaminB12\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label><label>Folate (µg)<input name=\"folate\" type=\"number\" min=\"0\" max=\"2000000\" step=\"any\"></label></div></details><input name=\"portion\" type=\"hidden\" value=\"100 g\"><button type=\"submit\" id=\"saveMeal\" class=\"main-action\">Save meal</button><button type=\"button\" id=\"cancelMeal\">Cancel</button><p class=\"hint food-source\">Nutrition: <a href=\"https://fdc.nal.usda.gov/\" target=\"_blank\" rel=\"noreferrer\">USDA FoodData Central</a> (public domain) for recognized dishes, <a href=\"https://www.ars.usda.gov/northeast-area/beltsville-md-bhnrc/beltsville-human-nutrition-research-center/methods-and-application-of-food-composition-laboratory/mafcl-site-pages/sr11-sr28/\" target=\"_blank\" rel=\"noreferrer\">USDA SR28</a> estimates otherwise. Photos stay on this device.</p></div>\r\n</form><p id=\"mealStatus\" role=\"status\"></p><details class=\"recent-meals\"><summary>Recent meals</summary><div id=\"mealList\"></div></details></div></dialog>\r\n<dialog id=\"remindersPanel\" class=\"launch-panel\" aria-labelledby=\"remindersTitle\"><header><h2 id=\"remindersTitle\">Reminders</h2><button data-close>Close</button></header><p id=\"pushStatus\" class=\"status\" role=\"status\">Connecting…</p><p id=\"reminderLiveStatus\" class=\"reminder-live\" role=\"status\"></p><section class=\"tv-face\" aria-label=\"Reminder controls\"><div class=\"switch-plate\"><div><strong>THIS DEVICE</strong></div><button id=\"notificationSwitch\" class=\"hardware-toggle\" role=\"switch\" aria-checked=\"false\" aria-label=\"Reminders and app updates on this device\" disabled>OFF</button></div><div class=\"tv-times\"><label id=\"timesLabel\">PER DAY</label><div id=\"timesDial\" class=\"tv-dial\" role=\"slider\" tabindex=\"0\" aria-labelledby=\"timesLabel\" aria-valuemin=\"0\" aria-valuemax=\"5\" aria-valuenow=\"1\" aria-valuetext=\"1 a day\"><b class=\"tv-ticks\" aria-hidden=\"true\"><s></s><s></s><s></s><s></s><s></s><s></s></b><i aria-hidden=\"true\"></i></div><output id=\"timesValue\" for=\"timesDial\">1/day</output></div><div class=\"tv-quiet\" role=\"group\" aria-label=\"Quiet hours\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 14A8 8 0 1 1 10 4a6 6 0 0 0 10 10z\"/></svg><div class=\"tv-quiet-track\"><i class=\"tv-quiet-fill\" aria-hidden=\"true\"></i><input id=\"quietFrom\" type=\"range\" min=\"0\" max=\"47\" step=\"1\" value=\"20\" aria-label=\"Quiet hours start\"><input id=\"quietTo\" type=\"range\" min=\"0\" max=\"47\" step=\"1\" value=\"38\" aria-label=\"Quiet hours end\"></div><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M19 5l-2 2M7 17l-2 2\"/></svg><output id=\"quietValue\" for=\"quietFrom quietTo\"></output></div><div class=\"tv-tones\" role=\"radiogroup\" aria-label=\"Tone\"><button type=\"button\" role=\"radio\" data-tone=\"gentle\" aria-label=\"Gentle\" aria-checked=\"false\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M8 10h.01M16 10h.01M8 14.5q4 3 8 0\"/></svg></button><button type=\"button\" role=\"radio\" data-tone=\"direct\" aria-label=\"Direct\" aria-checked=\"true\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M8 10h.01M16 10h.01M8.5 15.5h7\"/></svg></button><button type=\"button\" role=\"radio\" data-tone=\"cheeky\" aria-label=\"Cheeky\" aria-checked=\"false\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M8 10h.01M15 9.5l2 1M8 14.5h8M13 14.5v3a2 2 0 0 0 3 0v-3\"/></svg></button><button type=\"submit\" form=\"reminderForm\" id=\"tvSave\" class=\"tv-save\">SET</button></div></section><p class=\"reminder-live tv-status\" id=\"reminderStatus\" role=\"status\"></p><div id=\"reminderHatch\" class=\"tv-hatch\"><div class=\"actions\"><button id=\"testPush\">Send a test</button></div><details><summary>Notification help</summary><p>Get reminders and a download notification for each app update, even when Coach is closed. iPhone: install Coach first, then allow notifications.</p></details><form id=\"reminderForm\"><div class=\"fields\"><label>Reminder<select name=\"kind\"><option value=\"workout\">Workout</option><option value=\"meal\">Meal logging</option><option value=\"water\">Water</option><option value=\"motivation\">Encouragement</option></select></label><label>At<input name=\"time\" type=\"time\" required value=\"17:00\"></label></div><section class=\"reminder-tuning\" aria-label=\"MOM reminder controls\"><input type=\"hidden\" name=\"tone\" value=\"direct\"><input type=\"hidden\" name=\"timesPerDay\" value=\"1\"><div class=\"consistency-control\"><label for=\"consistencySlider\">FREQUENCY</label><output id=\"consistencyValue\" for=\"consistencySlider\">Every day</output><input id=\"consistencySlider\" type=\"range\" min=\"0\" max=\"3\" step=\"1\" value=\"3\" aria-valuetext=\"Every day\"><div class=\"cadence-scale\" aria-hidden=\"true\"><span>1 / WK</span><span>3 / WK</span><span>5 / WK</span><span>DAILY</span></div><input type=\"hidden\" name=\"daysPerWeek\" value=\"7\"></div><div class=\"message-preview\"><small>MOM WILL SAY</small><p id=\"reminderPreview\"></p></div></section><label>Time zone<input name=\"timezone\" required></label><div class=\"fields\"><label>Quiet hours begin<input name=\"quietStart\" type=\"time\" required value=\"22:00\"></label><label>Quiet hours end<input name=\"quietEnd\" type=\"time\" required value=\"07:00\"></label></div><label class=\"switch-label\">Reminder on<input name=\"enabled\" type=\"checkbox\" role=\"switch\" checked></label><button type=\"submit\" id=\"saveReminder\" class=\"main-action\">Save reminder</button><button type=\"button\" id=\"cancelReminderEdit\" hidden>Cancel edit</button></form><div id=\"reminderList\"></div></div></dialog>\r\n<dialog id=\"accountPanel\" class=\"launch-panel accomplishments-panel\" aria-labelledby=\"accountTitle\">\r\n <header><div><h2 id=\"accountTitle\">Crew scoreboard</h2></div><button data-close>Done</button></header>\r\n <a id=\"signIn\" class=\"action-link main-action\" href=\"/signin.html?return_to=%2Fpose.html\" target=\"_top\">Sign in to Coach</a>\r\n <div id=\"accountContent\" hidden>\r\n  <section class=\"crew-invites\" aria-labelledby=\"crewInviteTitle\"><h3 id=\"crewInviteTitle\">Invite friends</h3><div id=\"scoreboardCrew\" class=\"scoreboard-crew\"></div>\r\n   <details id=\"crewInviteControls\"><summary>Add friends</summary><p>Shares your name, Gala look and best sets. Disconnect any time.</p><button type=\"button\" id=\"createCrewInvite\">Create invite code</button><button type=\"button\" id=\"revokeCrewInvite\" hidden>Cancel invite</button>\r\n    <div id=\"crewInviteOutput\" hidden><label>One-use code<input id=\"crewInviteCode\" readonly autocomplete=\"off\" spellcheck=\"false\"></label><button type=\"button\" id=\"copyCrewInvite\">Copy code</button></div>\r\n    <form id=\"crewJoinForm\"><label>Have a friend’s code?<input id=\"crewJoinCode\" autocomplete=\"off\" spellcheck=\"false\" placeholder=\"Paste their invite code\" maxlength=\"40\" required></label><button type=\"submit\" id=\"crewJoin\">Connect & share best sets</button></form>\r\n   </details><p id=\"crewStatus\" role=\"status\"></p>\r\n  </section>\r\n  <div class=\"progress-summary\"><div><output id=\"accountLevel\">Level 1</output><span id=\"accountSets\">0 completed sets</span></div><button id=\"refreshAccount\" aria-label=\"Refresh your records\">↻ Refresh</button></div>\r\n  <div class=\"scoreboard-heading\"><div><h3 id=\"scoreboardGroup\">Chest</h3><p id=\"scoreboardSubtitle\">Push-up progressions</p></div><div class=\"medal-key\" aria-label=\"Rank colors\"><span data-medal=\"gold\">1 Gold</span><span data-medal=\"silver\">2 Silver</span><span data-medal=\"bronze\">3 Bronze</span></div></div>\r\n  <p id=\"recordsStatus\" class=\"hint\" role=\"status\">Loading your records…</p><div id=\"boardCanvas\"><div id=\"workoutList\"></div><div id=\"boardMovingPen\" aria-hidden=\"true\" hidden><i class=\"marker-eraser\"></i><i class=\"marker-tip\"></i></div></div>\r\n  <section class=\"unlock-board\" aria-labelledby=\"unlockTitle\"><h3 id=\"unlockTitle\">Game unlocks</h3><ul id=\"rewardList\"></ul></section>\r\n </div>\r\n <details class=\"account-settings\"><summary>Account & appearance</summary><p id=\"accountName\"></p><p id=\"accountStatus\" class=\"hint\" role=\"status\"></p>\r\n  <div id=\"accountSettingsContent\" hidden><div class=\"actions\"><button id=\"saveProfile\">Save appearance</button><button id=\"restoreProfile\">Restore appearance</button></div>\r\n   <h3>Your data</h3><div class=\"actions\"><button id=\"exportData\">Download my data</button><a id=\"signOut\" class=\"action-link\" href=\"/pose.html\" target=\"_top\">Sign out</a></div>\r\n   <details><summary>Delete Coach data</summary><p>This deletes your Coach progress, meals, reminders and saved appearance from this account.</p><label>Type DELETE<input id=\"deleteConfirm\" autocomplete=\"off\"></label><button id=\"deleteAccount\" class=\"danger\">Delete Coach data</button></details>\r\n  </div><p class=\"hint\"><a href=\"/privacy.html\">Privacy</a> · <a href=\"/source.json.gz\" download>App source (.json.gz)</a> · <a href=\"/LICENSE\">License</a></p>\r\n </details>\r\n <nav id=\"scoreboardMarkers\" class=\"marker-tray\" aria-label=\"Exercise focus markers\"></nav>\r\n</dialog>\r\n<dialog id=\"installPanel\" class=\"launch-panel\" aria-labelledby=\"installTitle\"><header><h2 id=\"installTitle\">Install Coach</h2><button data-close>Close</button></header><p id=\"installStatus\" class=\"status\" role=\"status\"></p><button id=\"installApp\" class=\"main-action\" hidden>Add to home screen</button><details><summary>Install steps</summary><p>iPhone: Safari → Share → Add to Home Screen.</p><p>Android: browser menu → Install app.</p></details><h3>Coach voice</h3><p>Deep robot voice</p><p class=\"hint\">Download for offline voice.</p><button id=\"downloadVoice\">Download voice</button><p id=\"downloadStatus\" role=\"status\"></p><button id=\"applyUpdate\" hidden>Install ready update</button></dialog>\r\n`);{const sound=document.getElementById('toggleVoice')?.closest('.cb-part');if(sound){sound.classList.add('dock-sound');sound.querySelector('button').classList.add('dock-control');document.getElementById('coachDock').append(sound);}}for(const close of document.querySelectorAll('[data-close]'))close.onclick=()=>close.closest('dialog').close();for(const button of document.querySelectorAll('[data-panel]')){const panel=document.getElementById(button.dataset.panel+'Panel');button.setAttribute('aria-haspopup','dialog');button.setAttribute('aria-expanded','false');panel.addEventListener('close',()=>button.setAttribute('aria-expanded','false'));button.onclick=(event)=>{if(panel.open){event.stopPropagation();panel.close();button.setAttribute('aria-expanded','false');return;}document.getElementById('stop')?.click();panel.showModal();button.setAttribute('aria-expanded','true');};}}\r\n","launch.css":"/* W2-2A (#5): one bottom bar on every route (routes.mjs). Fixed to the bottom; inside a route's dialog while it is\r\n   open (a modal makes everything outside it inert), so it is styled by id + all:unset to shrug off dialog button rules\r\n   (and !important where a dialog forces one, e.g. the Settings terminal's square corners). */\r\n:root{--myr5-bar:calc(88px + env(safe-area-inset-bottom))} /* R20: one dock console height for every page (portal.css draws it); 64px on landscape phones, below */\r\n@media(orientation:landscape) and (max-height:500px){:root{--myr5-bar:calc(64px + env(safe-area-inset-bottom))}} /* R20: a lower console on landscape phones, every page alike */\r\n#coachDock.coach-dock{position:fixed;inset:auto 0 0;z-index:40;box-sizing:border-box;width:auto;height:var(--myr5-bar);margin:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr)) 58px repeat(3,minmax(0,1fr));align-items:center;gap:3px;padding:0 6px env(safe-area-inset-bottom);border:0;border-top:1px solid #685077;border-radius:0;background:#171123f2;box-shadow:0 -6px 18px #08050f99;font:600 12px/1.1 system-ui,sans-serif}@media(max-width:400px){#coachDock.coach-dock{font-size:11px}}\r\n#coachDock.coach-dock button{all:unset;box-sizing:border-box;display:flex;align-items:center;justify-content:center;min-width:0;height:46px;padding:0 1px;border:1px solid #70547c;border-radius:9px!important;background:#30223e;color:#f2deab;font:inherit;text-align:center;hyphens:auto;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;transition:box-shadow .15s,background-color .15s}\r\n#coachDock.coach-dock button:focus-visible{outline:3px solid #ffb24d;outline-offset:2px}\r\n#coachDock.coach-dock button[aria-current=page]{color:#fff7e0;border-color:#ffb24d;background:#4d305f;box-shadow:0 0 0 1px #ffb24d,0 0 14px #ffb24d80}\r\n#coachDock.coach-dock .dock-portal{justify-self:center;width:56px;height:56px;padding:0;border:0;border-radius:50%!important;color:#fff;letter-spacing:.3px;background:radial-gradient(circle,#1c1428 0 60%,transparent 62%),conic-gradient(#ff5f1f,#ffff33,#39ff14,#1f51ff,#b026ff,#ff10f0,#ff5f1f);box-shadow:0 0 12px #b026ff80}\r\n#coachDock.coach-dock .dock-portal[aria-current=page]{background:radial-gradient(circle,#2c1d3d 0 60%,transparent 62%),conic-gradient(#ff5f1f,#ffff33,#39ff14,#1f51ff,#b026ff,#ff10f0,#ff5f1f);box-shadow:0 0 0 2px #fff,0 0 18px #ff10f0,0 0 30px #1f51ffaa}\r\n#coachDock .dock-live{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\r\n#coachDock.coach-dock[data-hint]:before{content:attr(data-hint);position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%);padding:8px 14px;border-radius:999px;background:#1c1428f2;border:1px solid #ffffff44;color:#f5eaff;white-space:nowrap;font-size:13px;box-shadow:0 4px 16px #0008}\r\n/* #19: a Continue chip above the bar, same pill look as the [data-hint] hint above. It sits higher than that\r\n   hint (+40px, not +10px): on the quilt the metal frame leaves only ~16px clear above the bar including its\r\n   rail, not enough for a real button, so the chip floats over the framed glass clear of the frame and rail. */\r\n#coachDock.coach-dock .dock-continue{position:absolute;left:50%;bottom:calc(100% + 40px);transform:translateX(-50%);padding:8px 14px;border-radius:999px;background:#1c1428f2;border:1px solid #ffffff44;color:#f5eaff;white-space:nowrap;font-size:13px;font-weight:600;box-shadow:0 4px 16px #0008;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}\r\n#coachDock.coach-dock .dock-continue:focus-visible{outline:3px solid #ffb24d;outline-offset:2px}\r\n#coachDock.coach-dock .dock-continue[hidden]{display:none}\r\n/* Only explicit active-game phases may hide the shared shell (shell.css). */\r\nbody:has(#coachDock){padding-bottom:calc(var(--myr5-bar) + 12px)}\r\nbody:has(#coachDock) .armie-inbox-launcher,body:has(#coachDock) .app-update-banner{bottom:calc(var(--myr5-bar) + 10px)}\r\n/* A route's dialog is sized to leave the bar showing (full-screen scenes stop at its top edge). */\r\ndialog[data-route][open]{bottom:var(--myr5-bar);max-height:calc(100dvh - var(--myr5-bar) - 16px)}\r\ndialog[data-route][open]:is(.ach-board,.meditation-panel,.ship-view,#library){height:calc(100dvh - var(--myr5-bar));max-height:none}\r\n.launch-panel button:focus-visible{outline:3px solid #ffb24d;outline-offset:3px}\r\n.launch-panel{color:#f7eefc;background:#21172f;border:1px solid #8b679c;border-radius:16px;width:min(620px,calc(100% - 24px));max-height:85dvh;padding:22px;box-sizing:border-box;font:16px/1.5 system-ui}.launch-panel::backdrop{background:#08050fc9}.launch-panel header{display:flex;justify-content:space-between;align-items:center;gap:16px;margin-bottom:20px}.launch-panel h2{font-size:24px;margin:0;color:#f4d49b}.launch-panel h3{font-size:18px;margin:24px 0 10px}.launch-panel p{color:#d8c9e0;margin:10px 0}.launch-panel button,.launch-panel .action-link{font:inherit;min-height:44px;border:1px solid #886797;border-radius:8px;background:#382548;color:#fcecd0;padding:9px 14px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;justify-content:center}.launch-panel button:disabled{opacity:.5;cursor:wait}.launch-panel .main-action{background:#eea954;color:#231429;border-color:#ffc779;font-weight:650}.launch-panel form{display:grid;gap:14px}.launch-panel label{display:grid;gap:6px;color:#e7d7ed;font-size:14px}.launch-panel input,.launch-panel select{font:inherit;font-size:16px;box-sizing:border-box;max-width:100%;width:100%;border:1px solid #77568a;border-radius:7px;background:#140d20;color:#fff3e1;padding:11px}.launch-panel .fields{display:grid;grid-template-columns:1fr 1fr;gap:12px}.launch-panel .actions{display:flex;gap:8px;flex-wrap:wrap}.launch-panel .hint{font-size:14px;color:#beaccb}.launch-panel .status{padding:10px 12px;border-left:3px solid #efa952;background:#31223e;min-height:22px}.launch-panel .entry{padding:14px 0;border-bottom:1px solid #594166;display:flex;justify-content:space-between;gap:16px;align-items:center}.launch-panel .entry small{display:block;color:#c9b5d3;font-size:14px}.launch-panel img.meal-preview{display:block;width:100%;height:180px;object-fit:contain;background:#120b1b;border-radius:8px;margin:12px 0}.launch-panel [hidden]{display:none!important}.launch-panel .nutrition{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.launch-panel a{color:#ffd59d}.launch-panel output{font-size:22px;font-weight:700;color:#ffcd87}.sync-badge{font:12px system-ui;color:#d6b6e3;display:block;text-align:center;padding:4px}.launch-panel progress{width:100%;accent-color:#eea954}.launch-panel .reward{padding:14px;border:1px solid #72527c;border-radius:10px;margin:10px 0;background:#2c1e3c}.launch-panel .reward[data-unlocked=true]{border-color:#dfa858}.launch-panel .danger{border-color:#c88489;color:#ffd5d8}\r\n@media(max-width:420px){.launch-panel{padding:16px}.launch-panel .nutrition{grid-template-columns:1fr 1fr}.launch-panel .fields{grid-template-columns:1fr}}\r\n.progress-eyebrow{display:block;font:11px ui-monospace,monospace;letter-spacing:2px;color:#bba3c6;margin-bottom:5px}\r\n.accomplishments-panel header{margin-bottom:18px;gap:10px}.accomplishments-panel h2{font-size:clamp(21px,6vw,27px);letter-spacing:-.6px}\r\n.progress-summary{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:16px;border-bottom:1px solid #644774}\r\n.progress-summary output,.progress-summary #accountSets{display:block}.progress-summary #accountSets{font-size:13px;color:#c7b0d2;margin-top:3px}.progress-summary button{font-size:13px}\r\n.accomplishments-panel .record-intro{margin:16px 0 5px}.accomplishments-panel #recordsStatus{font-size:13px}.accomplishments-panel #recordsStatus:empty{display:none}\r\n.record-board{margin-top:22px}.record-heading{display:flex;justify-content:space-between;align-items:baseline;gap:12px;border-bottom:2px solid #80618c;padding-bottom:8px}\r\n.record-heading h3{margin:0;font-size:17px;color:#f5d49e}.record-heading>span{font-size:12px;color:#c1aaca;text-align:right;font-variant-numeric:tabular-nums}\r\n.record-board table{width:100%;border-collapse:collapse;table-layout:fixed;font-variant-numeric:tabular-nums;font-size:14px}\r\n.record-board thead th{font-size:10px;text-transform:uppercase;letter-spacing:.7px;font-weight:500;color:#b59bc2;padding:9px 4px 5px;text-align:right}\r\n.record-board thead th:first-child{font-size:0;width:27px}.record-board thead th:nth-child(2){text-align:left;width:41%}.record-board thead th:last-child{width:35px}\r\n.record-board td,.record-board tbody th{padding:12px 4px;border-bottom:1px solid #52395e;text-align:right}.record-board tbody th{text-align:left;font-weight:500;overflow-wrap:anywhere}\r\n.record-board .record-rank{text-align:left;font:12px ui-monospace,monospace;color:#b39bbb}.record-board .record-best{font-size:19px;font-weight:700;letter-spacing:-.6px;color:#ffdb9a}\r\n.record-board tbody td:last-child{font-size:13px;color:#c9b3d4}.record-board tr[data-record=false]{color:#b39bbb}.record-board tr[data-record=false] .record-best{color:#967e9f}\r\n.unlock-board{margin-top:26px}.unlock-board h3{font-size:17px;margin:0 0 6px;color:#f5d49e}.unlock-board ul{padding:0;list-style:none;margin:0}\r\n.unlock-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 0;border-bottom:1px solid #52395e;font-size:14px}.unlock-row>span:last-child{font-size:12px;color:#bda6c9}.unlock-row[data-unlocked=true]>span:last-child{color:#f5cf87}\r\n.account-settings{margin-top:25px;padding-top:8px;border-top:1px solid #6c4b7b}.account-settings summary{cursor:pointer;min-height:44px;align-content:center;font-size:14px;color:#cebad9}.account-settings p{overflow-wrap:anywhere}.account-settings .actions{margin:12px 0}.account-settings .hint{font-size:13px}\r\n/* D34 post-download package: the Downloads menu (its own full-screen page), progress strip, Install section and body note; see post-download.mjs. */\r\n.downloads-menu{position:fixed;inset:0;margin:0;width:100vw;height:100dvh;max-width:none;max-height:none;box-sizing:border-box;padding:0;border:0;background:#163e34;color:#eefff5;font:16px/1.45 Arial,sans-serif;flex-direction:column}\r\n.downloads-menu[open]{display:flex}.downloads-menu::backdrop{background:#163e34}\r\n.downloads-page{flex:1;min-height:0;overflow-y:auto;width:min(620px,100%);margin:0 auto;padding:max(20px,env(safe-area-inset-top)) 16px 12px;box-sizing:border-box}\r\n.downloads-menu h2{margin:0 0 8px;font-size:26px;line-height:1.2}.downloads-menu p{margin:0 0 12px}.downloads-menu .hint{color:#cfe9dc}\r\n.downloads-menu fieldset{border:0;margin:0;padding:0;min-width:0}.downloads-menu legend{padding:0;margin-bottom:4px;font:700 13px Arial,sans-serif;letter-spacing:1px;text-transform:uppercase;color:#cfe9dc}\r\n.download-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;min-height:56px;padding:10px 0;border-bottom:1px solid #2f6b5a;cursor:pointer}\r\n.download-row strong{font-size:17px}.download-row small{display:block;font-size:14px;line-height:1.35;color:#cfe9dc}.download-row b{font-size:14px;white-space:nowrap;font-variant-numeric:tabular-nums}.download-row[data-saved=true] b{color:#b8f0cf}\r\n.download-row em{display:inline-block;margin-left:8px;padding:1px 8px;border-radius:99px;background:#b8f0cf;color:#10281f;font:700 12px/1.5 Arial,sans-serif;font-style:normal;vertical-align:2px}.download-row em[data-lock]{background:transparent;color:#f2deab;border:1px solid #f2deab}.download-row em[hidden]{display:none}\r\n.download-row input{appearance:none;flex:none;position:relative;width:48px;height:28px;margin:0;border:2px solid #a6d4bb;border-radius:99px;background:#0f2a23;cursor:pointer}\r\n.download-row input::before{content:'';position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#a6d4bb;transition:transform .15s}\r\n.download-row input:checked{background:#b8f0cf;border-color:#b8f0cf}.download-row input:checked::before{transform:translateX(20px);background:#10281f}.download-row input:indeterminate{border-color:#b8f0cf}.download-row input:indeterminate::before{transform:translateX(10px);background:#b8f0cf}\r\n.download-row input:disabled{opacity:.6;cursor:default}.download-row input:focus-visible,.downloads-menu button:focus-visible,.downloads-menu summary:focus-visible{outline:3px solid #fff;outline-offset:3px}\r\n.download-bodies details{margin-left:20px}.download-bodies summary{min-height:44px;align-content:center;cursor:pointer;color:#cfe9dc}.download-bodies details .download-row{min-height:48px}.download-bodies details strong{font-size:16px}\r\n.downloads-footer{padding:12px 16px max(16px,env(safe-area-inset-bottom));border-top:2px solid #a6d4bb;background:#10302a}.downloads-footer>*{max-width:588px;margin-left:auto;margin-right:auto}\r\n.downloads-footer [data-total]{margin-bottom:10px;font-weight:700}.downloads-footer .actions{display:flex;gap:10px}\r\n.downloads-footer .actions button{min-height:48px;padding:10px 18px;border-radius:10px;font:700 16px Arial,sans-serif}\r\n.downloads-footer [data-download]{flex:1 1 60%;background:#b8f0cf;color:#10281f;border:0}.downloads-footer [data-download]:disabled{opacity:.55}.downloads-footer [data-download][hidden]{display:none}.downloads-footer [data-later]{flex:1 1 30%;background:transparent;color:#eefff5;border:2px solid #a6d4bb}\r\n.downloads-status{margin-bottom:10px}.downloads-status[hidden]{display:none}.downloads-status p{margin:0 0 6px}.downloads-status span{display:block;font-size:14px}\r\n.downloads-status button{min-height:44px;margin-top:6px;padding:8px 14px;border:2px solid #a6d4bb;border-radius:9px;background:transparent;color:#eefff5;font:700 14px Arial,sans-serif}.downloads-status button[hidden]{display:none}\r\n@media (prefers-reduced-motion:reduce){.download-row input::before{transition:none}}\r\n.full-download-bar button:focus-visible{outline:3px solid #fff;outline-offset:3px}\r\n.full-download-bar{position:fixed;top:max(8px,env(safe-area-inset-top));left:12px;right:12px;z-index:9998;display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;padding:10px 12px;background:#163e34;color:#eefff5;border:2px solid #a6d4bb;border-radius:12px;box-shadow:0 8px 30px #0009;font:14px/1.3 Arial,sans-serif}\r\n/* Not over the quilt either: the portal makes the page behind it inert, so its Pause would look tappable and not be. Settings > Downloads shows the same progress. */\r\n.full-download-bar[hidden],body[data-tracking=true] .full-download-bar,body[data-screen=rest] .full-download-bar,body:has(#portalHome:not([hidden])) .full-download-bar{display:none}\r\n.full-download-bar p{margin:0;flex:1 1 100%}.full-download-bar span{flex:1}.full-download-bar button{min-height:44px;padding:8px 12px;font:14px Arial,sans-serif}\r\n.full-download-bar progress,.full-download-settings progress,.downloads-status progress{display:block;width:100%;height:10px;accent-color:#b8f0cf}.full-download-bar progress[hidden],.full-download-settings progress[hidden],.downloads-status progress[hidden]{display:none}\r\n.full-download-settings{margin-top:20px;border-top:1px solid #917885;padding-top:8px;font:16px/1.5 Arial,sans-serif}.full-download-settings span{display:block;font-size:14px}\r\n.post-download-sections{margin-top:18px;padding-top:14px;border-top:1px solid #917885}.post-download-sections[hidden]{display:none}.post-download-sections h3{margin:0 0 6px}.post-download-sections p{margin:0 0 8px}.post-download-sections [data-actions]{display:flex;flex-wrap:wrap;gap:8px}.post-download-sections button{min-height:44px;padding:8px 12px;border:1px solid #a6d4bb;border-radius:9px;background:#244f43;color:#eefff5;font:700 14px Arial,sans-serif}.post-download-sections button:focus-visible{outline:3px solid #fff;outline-offset:3px}.post-download-sections progress{display:block;width:100%;height:10px;accent-color:#b8f0cf}.post-download-sections progress[hidden]{display:none}\r\n.full-download-settings [data-open]{display:block;min-height:44px;margin:4px 0 8px}.full-download-settings [data-toggle][hidden]{display:none}\r\n.body-download-note{position:fixed;left:50%;top:max(12px,env(safe-area-inset-top));transform:translateX(-50%);z-index:9999;pointer-events:none;width:max-content;max-width:calc(100vw - 32px);box-sizing:border-box;margin:0;padding:10px 16px;border:2px solid #a6d4bb;border-radius:18px;background:#163e34;color:#eefff5;font:14px/1.3 Arial,sans-serif;text-align:center}.body-download-note[hidden]{display:none}\r\n","launch.mjs":"import {initSpotifyTerminal} from './spotify-terminal.mjs';\r\nimport {mountCockpit} from './pod/cockpit.mjs';\r\nimport {mountCoachPathPicker} from './coach-path-picker.mjs';\r\nimport {recordDailyActivity,readPerformanceProgress,localDay,mergeVerifiedPerformance} from './performance-progress.mjs';\r\nimport {createPerformanceAccountSync} from './performance-account-sync.mjs';\r\nimport {cosmeticLevel} from './progression-rules.mjs';\r\nimport {createAccountReadinessRefresh} from './account-readiness-client.mjs';\r\nimport {createImportAccountAdapter} from './local-coach/import-account-adapter.mjs';\r\nimport {mountGuestHistoryChoice} from './guest-history-choice.mjs';\r\nimport {authTransitions} from './auth-transition.mjs';\r\nimport {createAccountWorkoutSync,accountPendingKey,validateAccountDeletionReceipt} from './account-workout-sync.mjs';\r\nimport {VOICE_MANIFEST,VOICE_CACHE} from './robot-audio.mjs';\r\nimport {initAppUpdates} from './app-updates.mjs';\r\nimport {mountPostDownload} from './post-download.mjs';\r\nimport {mountCoachHub,mountSatcomFrame} from './coach-hub.mjs';\r\nimport {authFetch,signOut} from './auth-client.mjs';\r\nimport {mountCreatureSkinRewardReveal} from './creature-skin-reward-reveal.mjs';\r\nimport {mountRewardPacks} from './reward-pack-ui.mjs';\r\nimport {mountCosmeticRewardReveal} from './cosmetic-reward-reveal.mjs';\r\n\r\nimport {mountGalaReturn} from './gala-handoff.mjs';\r\nimport {mountMeditation} from './meditation.mjs';\r\nimport {applyCoachAccount,applyLocalCoach,clearCoachAccount} from './coach-profile.mjs';\r\nimport {openLocalCoach} from './local-coach-runtime.mjs';\r\nimport {mountLaunch} from './launch-shell.mjs?v=quick-install-v1';\r\nimport {mountRemindersComputer} from './modules/rooms/reminders-computer.mjs';\r\nimport {mountScoreboard} from './scoreboard.mjs';\r\nimport {mountMealNutrition} from './meal-nutrition.mjs';\r\nimport {MACROS,MICROS,displayNutrient} from './nutrition.mjs';\r\nimport {mealsOn,todayTiles} from './food/pyramid-tiles.mjs';\r\nimport {mountLiveReminders} from './reminder-live.mjs';\r\nimport {notificationBinding} from './device-notifications.mjs';\r\nimport {mountMealScanner} from './meal-scanner.mjs';\r\nimport {mountReminderControls} from './reminder-controls.mjs';\r\nimport {CADENCE_LABELS,spreadTimes,createReminderSaver} from './reminder-settings.mjs';\r\nimport {hasPackGrant} from './packs/pack-entitlements.mjs';\r\nimport {createPackGrantCache} from './packs/pack-grant-cache.mjs';\r\nimport {subscribePush,staleSubscription} from './push-subscribe.mjs';\r\nconst accountTransitions=authTransitions();\r\nmountLaunch();\r\nconst cockpit=mountCockpit({onService:async()=>{\r\n cockpit?.lock('actuation');\r\n try{\r\n  const moduleURL=new URL('/modules/portal/portal.mjs',location.href).href;\r\n  const {mountPortal}=await import(/* @vite-ignore */ moduleURL);\r\n  const portal=await mountPortal({visible:false});\r\n  const drawer=document.getElementById('portalHome');\r\n  if(drawer){drawer.dataset.serviceDrawer='true';drawer.setAttribute('aria-label','Material service drawer');}\r\n  portal.show();\r\n }catch(error){\r\n  const status=document.querySelector('.cockpit-announcement');\r\n  if(status)status.textContent='The service board could not open. Check your connection and try again.';\r\n  console.warn('Coach service board failed',error);\r\n }\r\n}});\r\ninitSpotifyTerminal({request:authFetch,getAccount:()=>account});\r\nmountRemindersComputer();\r\nmountCreatureSkinRewardReveal();\r\nmountCosmeticRewardReveal();\r\nmountRewardPacks();\r\nmountCoachHub({api});\r\nmountGalaReturn();\r\nmountMeditation({api,onComplete:refresh,getAccount:()=>account});\r\nmountMealScanner();\r\nconst mealNutrition=mountMealNutrition();\r\nconst reminderControls=mountReminderControls();\r\nwindow.addEventListener('myr5:reminder-defaults',e=>reminderControls.load(e.detail.tone,e.detail.days));\r\nlet editingReminder=null;\r\nconst $=id=>document.getElementById(id),keys=['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1','myr5-pod-power-v1','handborne-recipe-v4','mbs-dj-identity-v1'];\r\nlet account=null,revision=0,registration=null,installPrompt=null,reminderSnapshot=null,deviceBusy=false,packGrantCache=null;\r\nwindow.addEventListener('myr5:login-ready',()=>{queueMicrotask(()=>{if(account===null && !accountTransitionBusy){void refresh().catch(()=>{});} });});\r\nlet localHistoryRepository=null,guestHistoryChoice=null,accountTransitionBusy=false;\r\nmountCoachPathPicker({getAccount:()=>account,savePaths:async(paths,expected)=>{\r\n if(accountTransitionBusy||account?.user?.id!==expected.user.id||account?.dataEpoch!==expected.dataEpoch)throw Error('Account changed. Refresh and try again.');\r\n return api('/api/performance/paths','POST',{paths},{'X-Target-Account':expected.user.id,'X-Expected-Data-Epoch':String(expected.dataEpoch)});\r\n},onChosen:()=>{void refresh();}});\r\nlet expansionMounted=false;\r\nasync function mountVerifiedExpansion(value){\r\n if(expansionMounted||!hasPackGrant(value,'mom-paper-tear')||!window.myr5WorkoutOwner)return;\r\n expansionMounted=true;\r\n try{\r\n  const {mountExpansion}=await import('./modules/new/expansion-entry.mjs');\r\n  const result=mountExpansion({account:value,workoutOwner:window.myr5WorkoutOwner});\r\n  if(!result)expansionMounted=false;\r\n }catch(error){expansionMounted=false;console.warn('Optional expansion unavailable',error);}\r\n}\r\nwindow.addEventListener('myr5:account-ready',event=>{void mountVerifiedExpansion(event.detail);});\r\nwindow.addEventListener('myr5:workout-owner-ready',()=>{const owner=account||packGrantCache?.active();if(owner)void mountVerifiedExpansion(owner);});\r\nconst scoreboard=mountScoreboard({api,getAccount:()=>account});\r\nconst deviceBinding=notificationBinding(api);\r\nconst liveReminders=mountLiveReminders({refresh,read:reminders,getAccount:()=>account,deviceReady:()=>deviceBinding.ready(account?.user.id)});\r\nconst set=(id,text)=>$(id).textContent=text;\r\nexport async function api(path,method='GET',data,headers={}){const ticket=accountTransitions.capture(),assertions={...headers};if(!['GET','HEAD'].includes(method)&&(/^\\/api\\/(?:reminders(?:\\/|$)|push\\/|profile$|goals(?:\\/|$)|meals(?:\\/|$)|onboarding$|breathing\\/|scoreboard(?:\\/|$))/.test(path)||path==='/api/updates/subscription')){const captured=account;const has=name=>Object.keys(assertions).some(key=>key.toLowerCase()===name.toLowerCase());if((!has('X-Target-Account')||!has('X-Expected-Data-Epoch'))&&(!captured?.user?.id||!Number.isSafeInteger(captured.dataEpoch)||captured.dataEpoch<1))throw Error('Refresh your account before saving.');if(!has('X-Target-Account'))assertions['X-Target-Account']=captured.user.id;if(!has('X-Expected-Data-Epoch'))assertions['X-Expected-Data-Epoch']=String(captured.dataEpoch);}const response=await authFetch(path,{method,guestSkip:method==='GET'&&/^\\/api\\/(?:account\\?core=1|updates\\/subscription|reminders(?:\\/plan)?|meals)$/.test(path),signal:ticket.signal,credentials:'same-origin',headers:{...(data?{'Content-Type':'application/json'}:{}),...assertions},body:data?JSON.stringify(data):undefined,cache:'no-store'});accountTransitions.assertCurrent(ticket);if(response.redirected||!response.headers.get('content-type')?.includes('application/json'))throw Error('Sign in to Coach again to reconnect.');const value=await response.json();accountTransitions.assertCurrent(ticket);if(!response.ok)throw Object.assign(Error(value.error||'Please try again.'),{status:response.status,code:value.code});return value;}\r\nconst button=(text,fn)=>{const b=document.createElement('button');b.type='button';b.textContent=text;b.onclick=fn;return b;};\r\nfunction download(data,name,type='application/json'){const a=document.createElement('a'),url=URL.createObjectURL(new Blob([data],{type}));a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),10000);}\r\nfunction publishProgress(p){\r\n if(account)mergeVerifiedPerformance({...account,progress:p});\r\n window.coachProgress=p;if(account)account.progress=p;window.dispatchEvent(new CustomEvent('myr5:account-progress',{detail:p}));\r\n const earned=readPerformanceProgress();set('accountLevel',`Level ${cosmeticLevel(earned.totalXp).level}`);set('accountSets',`${p.completedSets} completed sets`);$('rewardList').replaceChildren();\r\n for(const [label,count] of [['Coaches',earned.coaches.length],['Golden coaches',earned.goldenCoaches.length],['Weapon families',Object.keys(earned.weapons).length],['Ships',earned.ships.length]]){const row=document.createElement('li');row.className='unlock-row';const name=document.createElement('span'),value=document.createElement('span');name.textContent=label;value.textContent=String(count);row.append(name,value);$('rewardList').append(row);}\r\n}\r\nconst optionalReadiness=createAccountReadinessRefresh({api,transitions:accountTransitions,getAccount:()=>account,apply:observation=>{if(!account)return;Object.assign(account,observation);const push=account.push;set('pushStatus',push.environment==='preview'?'Local preview · open the live app to connect notifications.':push.schedulerActive&&(!push.status||['ready','available'].includes(push.status))?'Online reminder sender is running. Enable notifications on each device.':'The reminder sender is connecting or temporarily unavailable. Try again shortly.');liveReminders.render();}});\r\nasync function refresh(){if(accountTransitionBusy)return null;let ticket;try{ticket=accountTransitions.beginRefresh();const value=await api('/api/account?core=1');if(!accountTransitions.isCurrent(ticket))return null;if(account&&(account.user.id!==value.user.id||account.dataEpoch!==value.dataEpoch)){accountTransitions.invalidate();return refresh();}account=value;revision=value.revision;packGrantCache?.confirm(value);mergeVerifiedPerformance(value);applyCoachAccount(value);$('signIn').hidden=true;$('accountContent').hidden=false;$('accountSettingsContent').hidden=false;set('accountName',value.user.provider==='clerk'?(window.Clerk?.user?.primaryEmailAddress?.emailAddress||value.user.email):value.user.email);publishProgress(value.progress);set('accountStatus','Progress synced');set('syncBadge','Synced');set('pushStatus',value.push.environment==='preview'?'Local preview · open the live app to connect notifications.':value.push.schedulerActive&&(!value.push.status||['ready','available'].includes(value.push.status))?'Online reminder sender is running. Enable notifications on each device.':'The reminder sender is connecting or temporarily unavailable. Try again shortly.');void optionalReadiness.refresh();await syncDeviceSwitch();if(!accountTransitions.isCurrent(ticket))return null;await flushSets();if(!accountTransitions.isCurrent(ticket))return null;return value;}catch(e){if(!ticket||!accountTransitions.isCurrent(ticket))return null;if(e.status===401){account=null;packGrantCache?.deactivate();scoreboard.clear();clearCoachAccount();$('signIn').hidden=false;$('accountContent').hidden=true;$('accountSettingsContent').hidden=true;$('workoutList').replaceChildren();$('mealList').replaceChildren();$('reminderList').replaceChildren();reminderSnapshot=null;liveReminders.update([]);await syncDeviceSwitch();if(!accountTransitions.isCurrent(ticket))return null;set('accountName','Sign in to sync');set('syncBadge','Sign in to save progress');}else {if(account)account.push={...account.push,schedulerActive:false};liveReminders.render();set('syncBadge',navigator.onLine?e.message:'Offline · reconnect to sync');if(account)account={...account,stale:true};else {const cached=packGrantCache?.active();if(cached)void mountVerifiedExpansion(cached);}}return null;}}\r\nlet accountStorage;try{accountStorage=localStorage;}catch{}\r\npackGrantCache=createPackGrantCache({storage:accountStorage});\r\nconst accountWorkoutSync=createAccountWorkoutSync({storage:accountStorage,api,transitions:accountTransitions,getAccount:()=>account,publishProgress});\r\nconst performanceAccountSync=createPerformanceAccountSync({storage:accountStorage,api,transitions:accountTransitions,getAccount:()=>account,maxUploads:120,getRows:async()=>{localHistoryRepository??=await openLocalCoach();return localHistoryRepository.forOwner(localHistoryRepository.guestOwnerId).listWorkouts();},onAccount:value=>publishProgress(value.progress)});\r\nwindow.addEventListener('myr5:local-history-refresh',()=>void flushSets());\r\nconst pendingKey=accountPendingKey;\r\nconst pending=user=>accountWorkoutSync.pending(user);\r\nasync function flushSets(){if(accountTransitionBusy||!account)return;let ticket;try{ticket=accountTransitions.capture();const result=await performanceAccountSync.flush();accountTransitions.assertCurrent(ticket);await accountWorkoutSync.flush();if(result?.failed?.length)set('syncBadge','Workout saved on this device · account sync will retry.');}catch(error){if(!ticket||!accountTransitions.isCurrent(ticket))return;if(error.code!=='auth_transition'&&error.code!=='account_scope_changed')set('syncBadge',error.message);}}\r\nwindow.coachAccount={refresh,async start(mode,goal){if(accountTransitionBusy)throw Error('Account is changing.');const ticket=accountTransitions.capture(),ready=account||await refresh();accountTransitions.assertCurrent(ticket);if(ready!==account)throw Error('Account changed. Refresh to continue.');if(!ready)throw Error('Sign in from Progress before starting a saved workout.');if(!ready.onboarding)throw Error('Complete your coach setup first.');return accountWorkoutSync.start(mode,goal);},async complete(data){if(accountTransitionBusy)throw Error('Account is changing.');return accountWorkoutSync.complete(data);}};\r\naccountTransitions.subscribe(()=>{account=null;scoreboard.clear();clearCoachAccount();$('signIn').hidden=false;$('accountContent').hidden=true;$('accountSettingsContent').hidden=true;$('workoutList').replaceChildren();$('mealList').replaceChildren();mealItems=null;$('reminderList').replaceChildren();reminderSnapshot=null;liveReminders.update([]);});\r\nconst localDate=()=>{const d=new Date();return new Date(d.getTime()-d.getTimezoneOffset()*60000).toISOString().slice(0,16);};\r\n$('mealForm').elements.eatenAt.value=localDate();$('reminderForm').elements.timezone.value=Intl.DateTimeFormat().resolvedOptions().timeZone;\r\nconst GUEST_MEALS='myr5-guest-meals-v1';const guestMeals={list(){try{return JSON.parse(localStorage.getItem(GUEST_MEALS)||'[]')}catch{return[]}},save(items){localStorage.setItem(GUEST_MEALS,JSON.stringify(items.slice(0,100)))}};\r\nasync function meals(){try{const items=account?(await api('/api/meals')).items:guestMeals.list();mealItems=items;$('mealList').replaceChildren();if(!items.length)set('mealList','No meals logged yet.');for(const item of items){\r\n const row=document.createElement('div');row.className='entry';const text=document.createElement('details'),name=document.createElement('summary');name.textContent=item.name;text.append(name);\r\n const detail=document.createElement('small');detail.textContent=item.portion+' · '+new Date(item.eaten_at).toLocaleString();text.append(detail);\r\n const breakdown=document.createElement('div');breakdown.className='saved-nutrients';const micros=typeof item.micros==='string'?JSON.parse(item.micros):item.micros||{};\r\n for(const [label,keys,values] of [['Macros',MACROS,item],['Micros',MICROS,micros]]){const p=document.createElement('p');p.textContent=label+': '+keys.map(([k,title,unit])=>title+' '+displayNutrient(values[k],unit)).join(' · ');breakdown.append(p);}text.append(breakdown);\r\n row.append(text,button('Remove',async()=>{try{if(account)await api('/api/meals/'+item.id,'DELETE');else guestMeals.save(mealItems.filter(m=>m.id!==item.id));await meals();}catch(e){set('mealStatus',e.message);}}));$('mealList').append(row);}}catch(e){mealItems=null;set('mealList',e.message);}finally{pyramidScanner?.refresh();if($('mealsPanel').dataset.dial)renderFoodDial();}}\r\n$('mealForm').onsubmit=async e=>{e.preventDefault();const form=e.target,submit=form.querySelector('[type=submit]');submit.disabled=true;$('foodCamera').disabled=true;const d={...Object.fromEntries(new FormData(form)),...mealNutrition.data()};try{d.id=crypto.randomUUID();d.eatenAt=new Date(d.eatenAt).toISOString();const rewardAccount=account,rewardDay=localDay(Date.parse(d.eatenAt));if(account){await api('/api/meals','POST',d);set('mealStatus','Meal saved');}else{guestMeals.save([{id:d.id,name:d.name,portion:d.portion,eaten_at:d.eatenAt,calories:d.calories,protein:d.protein,carbs:d.carbs,fat:d.fat,micros:Object.fromEntries(MICROS.map(([k])=>[k,d[k]]))},...guestMeals.list()]);set('mealStatus','Meal saved on this phone. Sign in to sync it.');}recordDailyActivity('food',{id:d.id,day:rewardDay,startedAt:Date.parse(d.eatenAt),completedAt:Date.now()},{account:rewardAccount});mealNutrition.reset();form.elements.eatenAt.value=localDate();$('foodStatus').textContent='';await meals();if(account)void refresh();}catch(err){set('mealStatus',err.message);}finally{$('mealStatus').scrollIntoView?.({block:'nearest'});submit.disabled=false;$('foodCamera').disabled=false;}};\r\nasync function reminders(){try{\r\n const {items}=await api('/api/reminders');const snapshot=JSON.stringify(items);if(snapshot===reminderSnapshot){liveReminders.update(items);return;}reminderSnapshot=snapshot;$('reminderList').replaceChildren();if(!items.length)set('reminderList','No reminders saved yet.');\r\n for(const r of items){\r\n  const row=document.createElement('div');row.className='entry reminder-entry';row.dataset.enabled=String(!!r.enabled);\r\n  const text=document.createElement('div');text.className='entry-copy';text.textContent=r.kind[0].toUpperCase()+r.kind.slice(1)+' · '+r.time;\r\n  const hint=document.createElement('small');hint.textContent=(CADENCE_LABELS[r.days_per_week??7])+' · '+(r.tone||'direct')+' · '+r.timezone+' · Quiet '+r.quiet_start+'–'+r.quiet_end;text.append(hint);\r\n  const next=document.createElement('small');next.id='reminder-next-'+r.id;next.className='reminder-next';text.append(next);\r\n  const controls=document.createElement('div');controls.className='reminder-controls';\r\n  const toggle=button(r.enabled?'ON':'OFF',async()=>{toggle.disabled=true;try{await api('/api/reminders/'+r.id,'PATCH',{enabled:!r.enabled});r.enabled=r.enabled?0:1;toggle.textContent=r.enabled?'ON':'OFF';toggle.setAttribute('aria-checked',String(!!r.enabled));row.dataset.enabled=String(!!r.enabled);set('reminderStatus',r.enabled?'Reminder resumed.':'Reminder paused.');liveReminders.update(items);}catch(e){set('reminderStatus',e.message);}finally{toggle.disabled=false;}});\r\n  toggle.className='hardware-toggle';toggle.setAttribute('role','switch');toggle.setAttribute('aria-label',r.kind+' reminder at '+r.time);toggle.setAttribute('aria-checked',String(!!r.enabled));\r\n  controls.append(toggle,button('Edit',()=>{editingReminder=r.id;const form=$('reminderForm');form.dataset.editing='1';reminderSaver.reset();for(const name of ['kind','time','timezone'])form.elements[name].value=r[name];form.elements.quietStart.value=r.quiet_start;form.elements.quietEnd.value=r.quiet_end;form.elements.enabled.checked=!!r.enabled;reminderControls.load(r.tone,r.days_per_week);set('saveReminder','Save changes');$('cancelReminderEdit').hidden=false;set('reminderStatus','Editing reminder');form.scrollIntoView({block:'start'});form.elements.time.focus({preventScroll:true});}),button('Remove',async()=>{try{await api('/api/reminders/'+r.id,'DELETE');if(editingReminder===r.id)resetReminderEdit();await reminders();}catch(e){set('reminderStatus',e.message);}}));row.append(text,controls);$('reminderList').append(row);\r\n }\r\nliveReminders.update(items);\r\n}catch(e){reminderSnapshot=null;set('reminderList',e.message);liveReminders.update([]);}}\r\nfunction resetReminderEdit(){editingReminder=null;delete $('reminderForm').dataset.editing;reminderSaver.reset();set('saveReminder','Save reminder');$('cancelReminderEdit').hidden=true;$('reminderForm').reset();$('reminderForm').elements.timezone.value=Intl.DateTimeFormat().resolvedOptions().timeZone;reminderControls.load();}\r\n$('cancelReminderEdit').onclick=()=>{resetReminderEdit();set('reminderStatus','Ready for a new reminder.');};\r\nconst reminderSaver=createReminderSaver((id,row)=>api(id?'/api/reminders/'+id:'/api/reminders',id?'PUT':'POST',row));\r\n$('reminderForm').onsubmit=async e=>{e.preventDefault();const form=e.target,b=form.querySelector('[type=submit]');if(form.dataset.busy)return;form.dataset.busy='1';b.disabled=true;$('tvSave').disabled=true;$('cancelReminderEdit').disabled=true;try{const data=Object.fromEntries(new FormData(form));data.daysPerWeek=Number(data.daysPerWeek);data.enabled=form.elements.enabled.checked;const perDay=editingReminder?1:Number(data.timesPerDay??1);delete data.timesPerDay;\r\n if(!editingReminder&&perDay===0){set('reminderStatus','Dial is OFF. Nothing set.');return;}\r\n const times=editingReminder?[data.time]:spreadTimes(perDay,data.quietStart,data.quietEnd,data.time);\r\n if(!await reminderSaver.save({times,editingId:editingReminder,data,existing:JSON.parse(reminderSnapshot||'[]').length}))return;\r\n void import('./modules/vault/vault-store.mjs').then(m=>m.bump('reminder-set')).catch(()=>{});set('reminderStatus',account?.push.schedulerActive?'Reminder saved.':'Reminder saved; delivery starts once the online sender is connected.');resetReminderEdit();await reminders();}catch(err){set('reminderStatus',err.message);}finally{delete form.dataset.busy;b.disabled=false;$('tvSave').disabled=false;$('cancelReminderEdit').disabled=false;}};\r\n// R8: a phone that turned reminders on before the Cloudflare move still holds a subscription under the old VAPID key.\r\nconst movedNotice=document.createElement('aside'),MOVED_SEEN='myr5-reminders-moved-v1';movedNotice.className='reminders-moved';movedNotice.setAttribute('role','status');movedNotice.hidden=true;\r\nmovedNotice.innerHTML='<button type=\"button\" data-on>Reminders moved to a new server. Tap to turn them back on.</button><button type=\"button\" data-later aria-label=\"Dismiss reminders notice\">Later</button>';document.body.append(movedNotice);\r\nconst movedSeen=()=>{try{return accountStorage?.getItem(MOVED_SEEN)==='1';}catch{return false;}},dismissMoved=()=>{movedNotice.hidden=true;try{accountStorage?.setItem(MOVED_SEEN,'1');}catch{}};\r\nmovedNotice.querySelector('[data-later]').onclick=dismissMoved;\r\nmovedNotice.querySelector('[data-on]').onclick=()=>{dismissMoved();$('notificationSwitch').click();if(!$('remindersPanel').open)$('remindersPanel').showModal();};\r\nasync function syncDeviceSwitch(){\r\n if(deviceBusy)return;\r\n let ticket;try{\r\n  ticket=accountTransitions.capture();\r\n  const supported='Notification'in window&&'PushManager'in window&&'serviceWorker'in navigator;\r\n  const reg=registration||await navigator.serviceWorker?.getRegistration(),sub=await reg?.pushManager?.getSubscription(),stale=supported&&!!account&&staleSubscription(sub,account.push.publicKey),on=supported&&!!sub&&!stale&&Notification.permission==='granted';\r\n  if(!accountTransitions.isCurrent(ticket))return;\r\n  movedNotice.hidden=!(stale&&Notification.permission!=='denied'&&!movedSeen());\r\n  $('notificationSwitch').setAttribute('aria-checked',String(on));set('notificationSwitch',on?'ON':'OFF');\r\n  $('notificationSwitch').disabled=!account||!supported||(!on&&!account.push.configured);$('testPush').disabled=!on;\r\n  if(on&&account?.push.configured)await deviceBinding.verify(account,sub);else deviceBinding.forget();\r\n  if(!accountTransitions.isCurrent(ticket))return;\r\n  set('pushStatus',!supported?'Install Coach to enable notifications.':!account?'Sign in to enable reminders.':Notification.permission==='denied'?'Allow notifications for Coach in your browser or phone settings.':!account.push.configured?'Notification service is reconnecting.':!account.push.schedulerActive?'Reconnecting… Reminders saved.':on?'Reminders and app update notifications on':'Turn on reminders and app updates for this device.');\r\n  liveReminders.render();\r\n }catch{if(ticket&&!accountTransitions.isCurrent(ticket))return;deviceBinding.forget();$('testPush').disabled=true;set('pushStatus','Reconnecting…');liveReminders.render();}\r\n}\r\n$('notificationSwitch').onclick=async()=>{\r\n const toggle=$('notificationSwitch'),turnOff=toggle.getAttribute('aria-checked')==='true';deviceBusy=true;deviceBinding.forget();toggle.disabled=true;\r\n try{\r\n  if(!('Notification'in window)||!('PushManager'in window))throw Error('Install Coach on your home screen and use a browser that supports notifications.');\r\n  if(turnOff){const reg=registration||await navigator.serviceWorker.ready,sub=await reg.pushManager.getSubscription();if(sub){await api('/api/push/unsubscribe','POST',{endpoint:sub.endpoint});await sub.unsubscribe();}set('pushStatus','Reminders are off on this device.');}\r\n  else{\r\n   if(account?.push.environment==='preview')throw Error('Notifications connect in the live app: myr5.mominc.online.');if(!account?.push.configured)throw Error('The notification sender could not connect. Retry while online.');\r\n   if(await Notification.requestPermission()!=='granted')throw Error('Notifications were not allowed. Enable them in your phone settings.');\r\n   const reg=registration||await navigator.serviceWorker.ready,{subscription:sub,fresh}=await subscribePush(reg,account.push.publicKey);\r\n   try{await api('/api/push/subscribe','POST',sub.toJSON());}catch(e){if(fresh)await sub.unsubscribe();throw e;}\r\n   set('pushStatus','This device is connected. Send a test to check delivery.');\r\n  }\r\n }catch(e){deviceBusy=false;await syncDeviceSwitch();set('pushStatus',e.message);return;}finally{deviceBusy=false;}await syncDeviceSwitch();\r\n};\r\n$('testPush').onclick=async()=>{try{const reg=registration||await navigator.serviceWorker?.getRegistration(),sub=await reg?.pushManager?.getSubscription();if(!sub)throw Error('Turn reminders on for this device first.');await api('/api/push/test','POST',{endpoint:sub.endpoint});set('pushStatus','The notification service accepted the test. Check your phone.');}catch(e){set('pushStatus',e.message);}};\r\nasync function workouts(){return scoreboard.refresh();}\r\nasync function localHistory(){try{localHistoryRepository??=await openLocalCoach();const scope=localHistoryRepository.forOwner(localHistoryRepository.guestOwnerId),items=(await scope.listWorkouts()).filter(item=>item.status==='completed').sort((a,b)=>(b.completedAt??0)-(a.completedAt??0)),list=$('localHistoryList');list.replaceChildren();set('localHistoryStatus',items.length?`${items.length} completed workout${items.length===1?'':'s'} on this device.`:'No completed workouts on this device yet.');for(const item of items){const row=document.createElement('article'),title=document.createElement('strong'),detail=document.createElement('p');row.className='entry';title.textContent=item.metadata?.name||item.mode;detail.textContent=`${item.completion?.value??0} · ${new Date(item.completedAt).toLocaleString()}`;row.append(title,detail);list.append(row);}if(!guestHistoryChoice){const host=document.createElement('section');$('historyPanel').append(host);const adapter=createImportAccountAdapter({repository:scope,request:authFetch,transitions:accountTransitions,getAccount:()=>account});guestHistoryChoice=mountGuestHistoryChoice({host,adapter,getAccount:()=>account,transitions:accountTransitions});}await guestHistoryChoice.render();}catch(error){set('localHistoryStatus',error.message);}}\r\n$('saveProfile').onclick = async () => {\r\n  try {\r\n    const ticket = accountTransitions.capture();\r\n    if (!account) throw new Error('No account');\r\n    const owner = account.user.id;\r\n    const dataEpoch = account.dataEpoch;\r\n    const data = Object.fromEntries(keys.map(k => [k, localStorage.getItem(k)]).filter(([, v]) => v != null));\r\n    const result = await api('/api/profile', 'PUT', { revision: revision, data });\r\n    // Update global revision before any further checks\r\n\r\n    accountTransitions.assertCurrent(ticket);\r\n    if (!account || account.user.id !== owner || account.dataEpoch !== dataEpoch) {\r\n      throw new Error('Account changed');\r\n    }\r\n    revision = result.revision;\r\n    const baseline = JSON.stringify(data);\r\n    if (localStorage.getItem('myr5-coach-owner') === owner) {\r\n      localStorage.setItem('myr5-synced-appearance', baseline);\r\n    }\r\n    await refresh();\r\n    set('accountStatus', 'Appearance saved to your account.');\r\n  } catch (e) {\r\n    set('accountStatus', e.message);\r\n  }\r\n};\r\n$('restoreProfile').onclick=async()=>{try{const ticket=accountTransitions.capture();const originalOwner=account?.user?.id;const originalEpoch=account?.dataEpoch;if(!originalOwner||originalEpoch===undefined){throw new Error('No account');}const value = await refresh();if (!value) return;if(account==null){throw new Error('Account missing after refresh');}accountTransitions.assertCurrent(ticket); if(account?.user?.id !== originalOwner || account?.dataEpoch !== originalEpoch) { throw new Error(\"Account changed\"); } if(value.user.id!==originalOwner||value.dataEpoch!==originalEpoch){throw new Error(\"Stale account\");}const profile=value.profile;if(!profile){throw new Error('Profile missing');}for(const key of keys){const raw=profile[key];if(raw!=null){localStorage.setItem(key,raw);window.dispatchEvent(new StorageEvent('storage',{key,newValue:raw}));}else{localStorage.removeItem(key);window.dispatchEvent(new StorageEvent('storage',{key,newValue:null}));}}localStorage.setItem('myr5-coach-owner',value.user.id);const snapshot=Object.fromEntries(keys.map(k=>[k,localStorage.getItem(k)]).filter(([,v])=>v!=null));localStorage.setItem('myr5-synced-appearance',JSON.stringify(snapshot));window.dispatchEvent(new Event('mominc-avatar-change'));set('accountStatus','Saved appearance restored.');}catch(e){set('accountStatus',e.message);}}\r\n$('refreshAccount').onclick=async()=>{await refresh();await workouts();};\r\n$('exportData').onclick=async()=>{try{download(JSON.stringify(await api('/api/export'),null,2),'myr5-data.json');}catch(e){set('accountStatus',e.message);}};\r\n$('signOut').onclick=async e=>{e.preventDefault();const target=account;accountTransitionBusy=true;try{try{accountTransitions.invalidate();}catch{}packGrantCache?.deactivate();void (async()=>{try{const sub=await registration?.pushManager.getSubscription();if(sub&&target){try{await api('/api/push/unsubscribe','POST',{endpoint:sub.endpoint},{'X-Target-Account':target.user.id,'X-Expected-Data-Epoch':String(target.dataEpoch)});}catch{}try{await sub.unsubscribe();}catch{}}}catch{}})();for(const k of [...keys,'myr5-workout-progress-v1','myr5-coach-owner','myr5-synced-appearance'])try{localStorage.removeItem(k);}catch{}try{clearCoachAccount();}catch{}await signOut();}catch(err){set('accountStatus',err.message);}finally{accountTransitionBusy=false;}};\r\n$('deleteAccount').onclick=async()=>{const captured=account;accountTransitionBusy=true;try{accountTransitions.invalidate();const target=captured&&Number.isSafeInteger(captured.dataEpoch)?captured:await api('/api/account?core=1');const receipt=await api('/api/account','DELETE',{confirm:$('deleteConfirm').value,expectedDataEpoch:target.dataEpoch},{'X-Target-Account':target.user.id});const through=validateAccountDeletionReceipt(receipt,target.user.id,target.dataEpoch);packGrantCache?.remove(target.user.id);accountWorkoutSync.forgetDeletedAccount(target.user.id,through);if(!receipt.alreadyDeleted)for(const k of [...keys,'myr5-workout-progress-v1','myr5-coach-owner','myr5-synced-appearance'])localStorage.removeItem(k);clearCoachAccount();location.reload();}catch(e){set('accountStatus',e.message);}finally{accountTransitionBusy=false;}};\r\nwindow.addEventListener('beforeinstallprompt',e=>{e.preventDefault();installPrompt=e;$('installApp').hidden=false;set('installStatus','Coach is ready to install on your home screen.');});window.addEventListener('appinstalled',()=>{set('installStatus','Coach is installed.');$('installApp').hidden=true;});\r\nset('installStatus',matchMedia('(display-mode: standalone)').matches?'Coach is running as an installed app.':'Install Coach for a full-screen training pod.');\r\n$('installApp').onclick=async()=>{if(installPrompt){await installPrompt.prompt();await installPrompt.userChoice;installPrompt=null;$('installApp').hidden=true;}};\r\ntry{localStorage.setItem('myr5-voice-style','robot');}catch{}\r\n$('downloadVoice').onclick=async()=>{const b=$('downloadVoice');b.disabled=true;try{const manifest=await(await fetch(VOICE_MANIFEST)).json(),urls=Object.values(manifest.phrases),cache=await caches.open(VOICE_CACHE);let i=0;await cache.add(VOICE_MANIFEST);for(const url of urls){if(!await cache.match(url)){const response=await fetch(url);if(!response.ok)throw Error('Download interrupted. Tap again to resume.');await cache.put(url,response);}set('downloadStatus',`Downloaded ${++i} of ${urls.length} voice clips.`);}set('downloadStatus','Robot voice pack is available offline on this device.');}catch(e){set('downloadStatus',e.message);}finally{b.disabled=false;}};\r\ninitAppUpdates({api,applyButton:$('applyUpdate'),onRegistration:reg=>{registration=reg;syncDeviceSwitch();},onBeforeUpdate:async()=>{await flushSets();if(account&&pending(account.user.id).length)throw Error('Your set is still syncing. Reconnect before updating.');}});\r\nmountPostDownload({host:$('installPanel')});\r\n// #120: the same military-satcom frame as Settings, generalised (mountSatcomFrame in coach-hub.mjs).\r\n// The Downloads menu only exists once mountPostDownload has built it (skipped if service workers\r\n// or caches aren't available, e.g. some test harnesses).\r\nmountSatcomFrame($('installPanel'),{title:'DOWNLOAD LINK'});\r\nconst downloadsMenu=$('downloadsMenu');if(downloadsMenu)mountSatcomFrame(downloadsMenu,{title:'DOWNLOAD LINK'});\r\nlet pyramidScanner=null,pyramidRequest=null,pyramidGen=0,pyramidBroken=false,mealItems=null;\r\n// #3 (W3-3A): Food opens as one full-screen pyramid scene (.pyramid-mode). A WebGL or model failure drops the class,\r\n// leaving the plain panel with its Camera button and Macros/Micros lists.\r\nasync function mountPyramid(){\r\n if(pyramidScanner||pyramidRequest)return;\r\n const run=++pyramidGen,request=new AbortController();pyramidRequest=request;\r\n try{\r\n  const {mountPyramidScanner}=await import('./food/pyramid-scanner.mjs');if(run!==pyramidGen||!$('mealsPanel').open)return;\r\n  const instance=await mountPyramidScanner($('foodCamera'),{getNutrition:mealNutrition.snapshot,getMeals:()=>mealItems,onDial:foodDial,frame:foodFrame,signal:request.signal});if(run!==pyramidGen||!$('mealsPanel').open){instance.dispose();return;}\r\n  pyramidScanner=instance;pyramidBroken=false;$('mealsPanel').classList.add('pyramid-mode');$('pyramidStarter')?.remove();\r\n }catch(error){if(!request.signal.aborted){pyramidBroken=true;$('mealsPanel').classList.remove('pyramid-mode');console.warn('Pyramid scanner unavailable',error);void offerPyramidStarter();}}\r\n finally{if(pyramidRequest===request)pyramidRequest=null;}\r\n}\r\n// W2-2O: the pyramid model is Starter-pack art. When it isn't on this phone (offline, never downloaded; not a WebGL\r\n// failure), the plain Food panel (Camera, Macros/Micros) offers the pack; online, the service worker fetches it on demand.\r\nasync function offerPyramidStarter(){\r\n if(navigator.onLine||await globalThis.caches?.match('/food/pyramid-scanner.glb').catch(()=>null)||$('pyramidStarter'))return;\r\n const offer=document.createElement('p');offer.id='pyramidStarter';offer.className='hint';\r\n offer.append('The Food pyramid comes with the Starter pack. ',button('Download the Starter pack',()=>window.myr5Packs?.open('starter')));\r\n $('foodCamera').after(offer);\r\n}\r\nfunction releasePyramid(){pyramidGen++;pyramidRequest?.abort();pyramidRequest=null;pyramidScanner?.dispose();pyramidScanner=null;}\r\n// The pyramid fits between the Food header and whatever the bottom sheet is showing.\r\nfunction foodFrame(){const panel=$('mealsPanel'),dock=document.getElementById('coachDock');let bottom=panel.querySelector('.food-sheet').offsetTop;\r\n // Framed fullscreen Food carries the dock inside the dialog: end the pyramid's frame (and the flip switch) above it.\r\n if(dock?.offsetHeight){const p=panel.getBoundingClientRect(),d=dock.getBoundingClientRect();if(d.top>p.top&&d.top<p.bottom)bottom=Math.min(bottom,(d.top-p.top)*panel.offsetHeight/(p.height||1));}\r\n return {top:panel.querySelector('header').offsetHeight,bottom};} // layout px: unaffected by the portal's arrival scale\r\nnew ResizeObserver(()=>pyramidScanner?.reframe()).observe($('mealsPanel').querySelector('.food-sheet'));\r\n// D36: the pyramid's dials. knob_0 opens the existing manual entry; knob_1/knob_2 toggle the Water and Today cards.\r\nlet foodDialReturn=null;\r\nfunction foodDial(index,on){\r\n if(index===0){setFoodDial(null);if($('mealConfirmation').hidden)window.dispatchEvent(new CustomEvent('myr5:food-selected',{detail:{name:''}}));$('mealName').focus();return;}\r\n setFoodDial(on?['log','water','today'][index]:null);\r\n}\r\nfunction setFoodDial(name){\r\n const panel=$('mealsPanel');\r\n if(name&&!panel.dataset.dial)foodDialReturn=document.activeElement;\r\n if(name)panel.dataset.dial=name;else delete panel.dataset.dial;\r\n $('foodDial').hidden=!name;\r\n if(name){renderFoodDial();$('foodDial').focus({preventScroll:true});return;}\r\n if(foodDialReturn?.isConnected&&panel.open)foodDialReturn.focus({preventScroll:true});foodDialReturn=null;\r\n}\r\nfunction renderFoodDial(){\r\n const name=$('mealsPanel').dataset.dial,targets=window.coachPlan?.targets,line=(text,className)=>{const p=document.createElement('p');p.textContent=text;if(className)p.className=className;return p;};\r\n if(name==='water'){\r\n  // No water log exists yet (no /api/logs): the existing water UI is the daily target and a water reminder.\r\n  set('foodDialTitle','Water');\r\n  $('foodDialBody').replaceChildren(line(targets?.waterOz?`${targets.waterOz} oz`:'—','food-dial-big'),line(targets?.waterOz?'Your water target for today.':'Finish Coach setup to get a daily water target.','hint'),button('Set a water reminder',()=>{$('reminderForm').elements.kind.value='water';document.querySelector('[data-panel=\"reminders\"]')?.click();}));\r\n }else if(name==='today'){\r\n  set('foodDialTitle','Today');\r\n  const eaten=mealsOn(mealItems);\r\n  const t=todayTiles(mealItems),list=document.createElement('ul');list.className='food-dial-list';\r\n  for(const item of eaten){const li=document.createElement('li');li.textContent=`${item.name} · ${new Date(item.eaten_at).toLocaleTimeString([],{hour:'numeric',minute:'2-digit'})} · ${displayNutrient(item.calories,'kcal')}`;list.append(li);}\r\n  $('foodDialBody').replaceChildren(line(t.calories,'food-dial-big'),line(`Protein ${t.protein} · Carbs ${t.carbs} · Fat ${t.fat}${targets?.proteinGrams?` · target ${targets.proteinGrams} g protein`:''}`),eaten.length?list:line('No meals logged today yet. Tap the lens to scan one.','hint'));\r\n }\r\n}\r\n$('foodDialBack').onclick=()=>{pyramidScanner?.setDial(-1);setFoodDial(null);};\r\n// Clears the meal in progress (and any scan left on screen) so the pyramid is back to its idle face.\r\n$('cancelMeal').onclick=()=>{if(!$('cancelFoodScan').hidden)$('cancelFoodScan').click();window.dispatchEvent(new Event('myr5:food-reset'));for(const id of ['mealScanStage','foodSuggestions','recognizeFood'])$(id).hidden=true;set('foodStatus','');};\r\n$('mealsPanel').addEventListener('close',()=>{releasePyramid();setFoodDial(null);});window.addEventListener('pagehide',releasePyramid);\r\n// Router/portal hook (same contract as myr5Menus.ship): opens Food and returns its <dialog>.\r\nwindow.myr5Menus={...window.myr5Menus,food:()=>{\r\n if(document.body.dataset.cameraWorkout==='true'||document.body.dataset.tracking==='true')return null; // #40: no meals over a workout\r\n if(!$('mealsPanel').open)document.querySelector('[data-panel=\"meals\"]')?.click();return $('mealsPanel');\r\n}};\r\n for(const button of document.querySelectorAll('[data-panel]'))button.addEventListener('click',async()=>{if(button.dataset.panel==='history')await localHistory();if(button.dataset.panel==='meals'){$('mealsPanel').classList.toggle('pyramid-mode',!pyramidBroken);void mountPyramid();await meals();}if(button.dataset.panel==='reminders')await liveReminders.sync();if(button.dataset.panel==='account'){await refresh();await workouts();}});\r\n window.addEventListener('myr5:local-history-refresh',localHistory);window.addEventListener('pagehide',()=>{guestHistoryChoice?.close();localHistoryRepository?.close();},{once:true});\r\nconst coachDayTimer=setInterval(()=>{if(!document.hidden)refresh();},60000);window.addEventListener('pagehide',()=>clearInterval(coachDayTimer));\r\nwindow.addEventListener('online',refresh);document.addEventListener('visibilitychange',()=>{if(!document.hidden)refresh();});\r\n await applyLocalCoach();{const cached=packGrantCache?.active();if(cached)void mountVerifiedExpansion(cached);}void refresh();const panel=new URLSearchParams(location.search).get('panel');window.myr5Routes.boot(panel);\r\n","level-map-domain.mjs":"// Presentation rules only. XP thresholds and rewards stay owned by progression-rules and battle-pass.\r\nexport const CHAPTER_SIZE=10;\r\nexport const CHAPTER_COUNT=25;\r\nconst WORLDS=[\r\n ['The first signal','Faint moon'],['Dustwake','Broken satellites'],['Pale orbit','Ice rings'],['Ion garden','Blue aurora'],['Beacon belt','Twin moons'],\r\n ['Amber crossing','Gas halo'],['The outer reach','Meteor river'],['Violet frontier','Crystal world'],['Aster passage','Silver rings'],['Solar forge','Young star'],\r\n ['Helios rise','Gold corona'],['The wide expanse','Planet chain'],['Cinder sea','Ash world'],['Neon divide','Artificial orbit'],['The crimson arc','Red giant'],\r\n ['Eventide','Shadow planet'],['The iron constellation','Orbital foundry'],['Hyperion','Binary suns'],['Last daylight','Supergiant'],['The dark current','Gravity tide'],\r\n ['Blackglass gate','Mirror world'],['The fracture','Dimensional seam'],['Singularity rim','Accretion disc'],['Beyond the veil','Star rift'],['The final horizon','Cosmic dawn']\r\n];\r\nexport function chapterForLevel(level){return Math.max(0,Math.min(CHAPTER_COUNT-1,Math.floor((level-1)/CHAPTER_SIZE)));}\r\nexport function chapterLevels(chapter,thresholds){\r\n const page=Math.max(0,Math.min(CHAPTER_COUNT-1,chapter));\r\n return thresholds.slice(page*CHAPTER_SIZE,(page+1)*CHAPTER_SIZE).map((xp,index)=>({level:page*CHAPTER_SIZE+index+1,xp,tier:(index+1)%10===0?'Legendary':(index+1)%5===0?'Rare':'Uncommon'}));\r\n}\r\nexport function chapterWorld(chapter){const page=Math.max(0,Math.min(CHAPTER_COUNT-1,chapter));return {name:WORLDS[page][0],body:WORLDS[page][1],intensity:page/(CHAPTER_COUNT-1),page};}\r\n// Coordinates are percentages of one flight sector, ordered from its highest level down.\r\n// Every sector meets the next at x=50 so the 250 rewards form one continuous route.\r\nexport function chapterConstellation(chapter){\r\n const page=Math.max(0,Math.min(CHAPTER_COUNT-1,chapter));\r\n const pattern=[27,56,78,47,19,51,79,42,24,66];\r\n const nodes=pattern.map((base,index)=>({x:Math.max(16,Math.min(84,(page%2?100-base:base)+((page*7+index*3)%9)-4)),y:8+index*9.3}));\r\n const main=`M 50 0 ${nodes.map(({x,y})=>`L ${x} ${y.toFixed(1)}`).join(' ')} L 50 100`;\r\n const branchIndex=4,anchor=nodes[branchIndex],coach={x:anchor.x<50?83:17,y:49};\r\n const branch=`M ${anchor.x} ${anchor.y.toFixed(1)} L ${coach.x} ${coach.y}`;\r\n return {nodes,main,branch,coach};\r\n}\r\nexport function chapterCoach(chapter,requirements){\r\n if(!requirements.length)return null;\r\n return requirements[Math.min(requirements.length-1,Math.round(chapter*(requirements.length-1)/(CHAPTER_COUNT-1)))];\r\n}\r\n","level-map-scene.mjs":"// A small arcade canvas with run-local points; it never writes workout XP or unlocks.\r\nimport {createFlightScore} from './flight-score.mjs';\r\nimport {asteroid,rock} from './space-rocks.mjs';\r\nconst SHIPS=['supportive','direct','analytical','playful','calm','mom'];\r\nconst clamp=(v,min,max)=>Math.max(min,Math.min(max,v));\r\nconst sprite=[\r\n '.......11.......','......1221......','......1221......','.....123321.....',\r\n '....12344321....','...1234554321...','..123456654321..','1123456776543211',\r\n '1123456776543211','..123456654321..','...1234554321...','....12344321....',\r\n '.....123321.....','......1881......','.....8.88.8.....','....8..88..8....'\r\n];\r\nconst colors={supportive:['#142b40','#70b4db','#b9edff','#f4e6b2','#f37f42','#ffe4a0','#fff8de','#e66638'],direct:['#302644','#b59ce8','#e6caff','#ffdfad','#d95f51','#ff9c65','#fff4db','#e65d39'],analytical:['#183a40','#60d3cc','#b8fff0','#e1e8ff','#6b8fea','#afd3ff','#fff8de','#e66638'],playful:['#3d2445','#fa9ccf','#ffd7ec','#ffecae','#fc7d87','#ffc484','#fff8de','#e66638'],calm:['#173a36','#7dbea7','#c6f5d7','#f8e8be','#8ecbc6','#d7fff0','#fff8de','#e66638'],mom:['#402824','#dba579','#f7d5ad','#fff0c9','#d86d50','#ffbc7f','#fff8de','#e66638']};\r\nfunction drawShip(ctx,x,y,style,t){\r\n const palette=colors[SHIPS.includes(style)?style:'supportive'],pixel=3;\r\n ctx.save();ctx.translate(Math.round(x-24),Math.round(y-24));\r\n // Each selected catalog hull has its own readable silhouette at this small resolution.\r\n ctx.fillStyle=palette[1];\r\n if(style==='supportive'){for(const px of [1,37]){ctx.fillRect(px,19,10,12);ctx.fillRect(px+2,31,6,9);}}\r\n if(style==='direct'){ctx.fillRect(0,28,15,5);ctx.fillRect(33,28,15,5);ctx.fillRect(3,23,10,6);ctx.fillRect(35,23,10,6);}\r\n if(style==='analytical'){ctx.fillRect(4,32,40,4);ctx.fillRect(4,25,4,11);ctx.fillRect(40,25,4,11);ctx.fillRect(12,36,24,5);}\r\n if(style==='playful'){ctx.fillRect(4,3,5,26);ctx.fillRect(39,3,5,26);ctx.fillRect(0,30,11,10);ctx.fillRect(37,30,11,10);}\r\n if(style==='calm'){ctx.fillRect(9,12,30,28);ctx.fillRect(13,7,22,5);ctx.fillRect(13,40,22,5);}\r\n if(style==='mom'){ctx.fillRect(2,18,44,22);ctx.fillRect(17,6,14,15);ctx.fillRect(4,10,9,13);ctx.fillRect(35,10,9,13);}\r\n for(let row=0;row<16;row++)for(let col=0;col<16;col++){\r\n  const code=Number(sprite[row][col]);if(!code)continue;\r\n  ctx.fillStyle=palette[code-1]||palette[0];ctx.fillRect(col*pixel,row*pixel,pixel,pixel);\r\n }\r\n ctx.fillStyle=t%360<180?'#f58b40':'#ffe5a0';ctx.fillRect(19,48,4,8);ctx.fillRect(27,48,4,8);ctx.restore();\r\n}\r\nexport function mountLevelMapScene(canvas,{ship='supportive',reducedMotion=false,onScore=()=>{}}={}){\r\n const points=createFlightScore();\r\n const publish=()=>onScore(points.read());publish();\r\n const ctx=canvas.getContext('2d');if(!ctx)return {start(){},stop(){},startFiring(){},stopFiring(){},fire(){},aim(){},setShip(){},resize(){},setWarp(){}};\r\n let running=false,raf=0,w=1,h=1,last=0,time=0,style=ship,aimX=.5,aimY=.72,x=.5,y=.72,firing=false,shotClock=0;\r\n const clampX=(xVal)=>{if(w<100)return xVal;const maxR=9,minX=(maxR/w)+0.08,maxX=1-minX;return Math.max(minX,Math.min(maxX,xVal));};\r\n const asteroids=Array.from({length:9},(_,i)=>({x:.12+.76*((i*37+19)%97)/100,y:((i*29+8)%103)/100,r:3+i%4*2,s:.000035+i%5*.000016,hit:0}));\r\n // R-1017: the constellation page's dark lit-rim rocks join the flight as bigger, slower targets (both pages' asteroids, one game).\r\n for(let i=0;i<4;i++)asteroids.push({x:.18+.64*((i*41+7)%89)/89,y:((i*31+50)%101)/100,s:.00002+i%3*.00001,hit:0,...asteroid(8,13),poly:true});\r\n // Hyper-speed: warp streaks while the page jumps to a section; warp is the scroll speed in px/ms (+ = flying up).\r\n let warp=0;const streaks=Array.from({length:42},(_,i)=>({x:((i*53+11)%97)/97,y:((i*71+5)%89)/89,l:.4+(i*37%10)/10}));\r\n const lasers=[];const blasts=[];\r\n // Kenney Space Shooter Remastered, CC0: https://kenney.nl/assets/space-shooter-remastered\r\n const meteors=['/pod/worlds/kenney-meteor-grey-tiny1.png','/pod/worlds/kenney-meteor-brown-tiny1.png'].map(src=>{const img=new Image();img.src=src;img.onload=()=>{if(!running)draw();};return img;});\r\n const resize=()=>{const rect=canvas.getBoundingClientRect(),dpr=Math.min(devicePixelRatio||1,2);w=Math.max(1,rect.width);h=Math.max(1,rect.height);canvas.width=Math.round(w*dpr);canvas.height=Math.round(h*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);draw();};\r\n function draw(){ctx.clearRect(0,0,w,h);const now=time;\r\n  for(let index=0;index<asteroids.length;index++){const a=asteroids[index];if(a.hit>0)continue;const px=a.x*w,py=a.y*h,img=meteors[index%meteors.length];if(a.poly){rock(ctx,px,py,a.r,a.rot+a.vr*now/1000,a.verts,.9);continue;}if(img.complete&&img.naturalWidth){ctx.drawImage(img,px-a.r,py-a.r,a.r*2,a.r*2);}else{ctx.fillStyle='#aabcca';ctx.fillRect(px-a.r,py-a.r,a.r*2,a.r*2);}}\r\n  for(const l of lasers){ctx.strokeStyle='#b9faff';ctx.lineWidth=2;ctx.shadowBlur=12;ctx.shadowColor='#60e6ff';ctx.beginPath();ctx.moveTo(l.x*w,l.y*h);ctx.lineTo(l.x*w,(l.y-.08)*h);ctx.stroke();ctx.shadowBlur=0;}\r\n  for(const b of blasts){ctx.strokeStyle=`rgba(255,190,105,${Math.max(0,1-b.age/380)})`;ctx.lineWidth=3;ctx.beginPath();ctx.arc(b.x*w,b.y*h,3+b.age/24,0,Math.PI*2);ctx.stroke();}\r\n  if(warp){const k=Math.min(1,Math.abs(warp)/40);ctx.strokeStyle='#cfefff';ctx.lineWidth=1.5;ctx.globalAlpha=.25+.6*k;ctx.beginPath();\r\n   for(const s of streaks){const sx=s.x*w,sy=s.y*h,len=(30+h*.5*k)*s.l;ctx.moveTo(sx,sy);ctx.lineTo(sx,sy-Math.sign(warp)*len);}\r\n   ctx.stroke();ctx.globalAlpha=1;}\r\n  drawShip(ctx,x*w,y*h,style,now);\r\n  if(warp){ctx.fillStyle='#ffb25a';ctx.globalAlpha=.85;const flame=10+Math.min(60,Math.abs(warp)*1.5);ctx.fillRect(x*w-7,y*h+(warp>0?24:-24-flame),14,flame);ctx.globalAlpha=1;}\r\n }\r\n function frame(now){if(!running)return;const dt=Math.min(40,Math.max(0,now-last));last=now;time+=dt;\r\n  // Streaks rush past opposite the jump; asteroids are left behind (they recycle off the edge without breaking the multiplier).\r\n  if(warp){for(const s of streaks){s.y=((s.y+warp*dt/h*1.6)%1+1)%1;}for(const a of asteroids){if(a.hit<=0){a.y+=warp*dt/h;if(a.y>1.04||a.y<-.1){a.y=a.y>1?-.08:1;a.x=clampX((a.x+.37)%1);}}}}\r\n  x+=(aimX-x)*Math.min(1,dt*.04);y+=(aimY-y)*Math.min(1,dt*.04);\r\n  if(firing){shotClock+=dt;while(shotClock>=125){lasers.push({x,y:y-.04});shotClock-=125;}}\r\n  for(const a of asteroids){if(a.hit>0){a.hit-=dt;continue;}if(!reducedMotion){a.y+=a.s*dt;if(a.y>1.05){a.y=-.08;a.x=clampX((a.x+.37)%1);points.miss();publish();}}}\r\n  for(let i=lasers.length-1;i>=0;i--){const l=lasers[i];l.y-=dt*.0016;if(l.y<-.1){lasers.splice(i,1);continue;}const hit=asteroids.find(a=>a.hit<=0&&Math.abs(a.x-l.x)*w<a.r+5&&Math.abs(a.y-l.y)*h<a.r+10);if(hit){hit.hit=700;blasts.push({x:hit.x,y:hit.y,age:0});hit.y=-.08;hit.x=clampX((hit.x+.37)%1);lasers.splice(i,1);points.hit();publish();}}\r\n  for(let i=blasts.length-1;i>=0;i--){blasts[i].age+=dt;if(blasts[i].age>380)blasts.splice(i,1);}\r\n  draw();raf=requestAnimationFrame(frame);\r\n }\r\n function start(){resize();if(running||document.hidden)return;running=true;last=performance.now();raf=requestAnimationFrame(frame);}\r\n function stopFiring(){firing=false;shotClock=0;}\r\n function stop(){stopFiring();running=false;if(raf)cancelAnimationFrame(raf);raf=0;}\r\n function fire(){if(document.hidden)return;lasers.push({x,y:y-.04});if(!running)draw();}\r\n return {start,stop,stopFiring,resetScore(){points.reset();publish();},startFiring(){if(document.hidden)return;firing=true;shotClock=0;fire();},resize,setWarp(value){warp=reducedMotion?0:value;},aim(clientX,clientY){const r=canvas.getBoundingClientRect();aimX=clamp((clientX-r.left)/r.width,.1,.9);aimY=clamp((clientY-r.top)/r.height,.14,.88);x=aimX;y=aimY;draw();},fire,setShip(value){style=value;draw();}};\r\n}\r\n","local-coach/browser-runtime.mjs":"import {openLocalCoach,LocalCoachStorageError} from './repository.mjs';\r\n\r\nexport {openLocalCoach,LocalCoachStorageError};\r\n\r\nexport function accountProbeFallsBackToLocal(error){\r\n if(error instanceof LocalCoachStorageError)return false;\r\n const status=Number(error?.status);if(status>=400&&status<500)return false;\r\n return error instanceof TypeError||error?.name==='TimeoutError'||error?.code==='optional_probe_timeout'||status>=500&&status<=599;\r\n}\r\nasync function boundedOptionalProbe(load,{timeoutMs=4000,setTimeoutFn=setTimeout,clearTimeoutFn=clearTimeout}={}){\r\n if(!Number.isFinite(timeoutMs)||timeoutMs<=0)throw RangeError('Invalid optional probe deadline.');\r\n const controller=new AbortController();let timer;\r\n try{return await Promise.race([\r\n  Promise.resolve().then(()=>load({signal:controller.signal})),\r\n  new Promise((_,reject)=>{timer=setTimeoutFn(()=>{const error=Object.assign(Error('Optional account service timed out.'),{code:'optional_probe_timeout',status:503});controller.abort(error);reject(error);},timeoutMs);}),\r\n ]);}finally{clearTimeoutFn(timer);}\r\n}\r\nexport async function probeOptionalAccount(load,options){try{return await boundedOptionalProbe(load,options);}catch(error){if(error?.status===401&&error?.code!=='auth-config'||accountProbeFallsBackToLocal(error))return null;throw error;}}\r\nexport async function probeOptionalTransfer(load,options){try{return await boundedOptionalProbe(load,options);}catch(error){if(accountProbeFallsBackToLocal(error))return null;throw error;}}\r\n\r\nconst HEARTBEAT_MS=20_000,STALE_MS=60_000,CHANNEL='myr5-local-workout-lease-v1';\r\nconst defaultChannel=name=>typeof BroadcastChannel==='function'?new BroadcastChannel(name):null;\r\nconst delay=ms=>new Promise(resolve=>setTimeout(resolve,ms));\r\nconst json=value=>{try{return JSON.parse(value||'null');}catch{return null;}};\r\n\r\nexport async function openGuestWorkoutAdapter({\r\n exerciseKeys,\r\n open=openLocalCoach,\r\n storage=globalThis.localStorage,\r\n channelFactory=defaultChannel,\r\n now=()=>Date.now(),\r\n setIntervalFn=globalThis.setInterval?.bind(globalThis),\r\n clearIntervalFn=globalThis.clearInterval?.bind(globalThis),\r\n wait=delay,\r\n heartbeatMs=HEARTBEAT_MS,\r\n staleMs=STALE_MS,\r\n tabId=globalThis.crypto?.randomUUID?.(),\r\n}={}){\r\n if(!tabId)throw new LocalCoachStorageError('missing-randomness','Secure tab identifiers are unavailable.',{recoverable:false});\r\n const repository=await open({exerciseKeys}),scope=repository.forOwner(repository.guestOwnerId);\r\n const key=`myr5-local-workout-heartbeat:${scope.ownerId}:${scope.deviceId}`,channel=channelFactory?.(CHANNEL);\r\n const peers=new Map();let timer=0,disposed=false,held=false,active=null,leaseToken=null,fence=0;\r\n const current=value=>value&&Number.isFinite(value.at)&&value.at>now()-staleMs;\r\n const fresh=value=>current(value)&&value.tabId!==tabId;\r\n const readHeartbeat=()=>{try{return json(storage?.getItem(key));}catch{return null;}};\r\n const foreignHeartbeat=()=>{const stored=readHeartbeat();return fresh(stored)&&stored.type==='heartbeat'||[...peers.values()].some(value=>fresh(value)&&value.type==='heartbeat');};\r\n const foreign=()=>fresh(readHeartbeat())||[...peers.values()].some(fresh);\r\n const removeHeartbeat=token=>{try{const value=readHeartbeat();if(value?.tabId===tabId&&(!token||value.token===token))storage?.removeItem(key);}catch{}};\r\n const loseLease=()=>{if(timer)clearIntervalFn?.(timer);timer=0;held=false;const token=leaseToken;leaseToken=null;removeHeartbeat(token);};\r\n const writeHeartbeat=()=>{if(!held||disposed||!leaseToken)return false;const stored=readHeartbeat();if(fresh(stored)||[...peers.values()].some(value=>fresh(value)&&value.type==='heartbeat')){loseLease();return false;}const value={type:'heartbeat',tabId,token:leaseToken,at:now(),ownerId:scope.ownerId,deviceId:scope.deviceId};try{storage?.setItem(key,JSON.stringify(value));}catch{loseLease();return false;}if(readHeartbeat()?.token!==leaseToken){loseLease();return false;}channel?.postMessage?.(value);return true;};\r\n const receive=event=>{const value=event?.data;if(value?.ownerId!==scope.ownerId||value?.deviceId!==scope.deviceId||value.tabId===tabId)return;if(value.type==='release'){if(!value.token||peers.get(value.tabId)?.token===value.token)peers.delete(value.tabId);}else if(value.type==='heartbeat'||value.type==='claim'||value.type==='probe'){peers.set(value.tabId,value);if(value.type==='probe'&&held)writeHeartbeat();}};\r\n channel?.addEventListener?.('message',receive);\r\n const acquire=async()=>{\r\n  if(disposed)throw Error('Local workout storage is closed.');if(held){try{assertLease();return true;}catch{return false;}}\r\n  channel?.postMessage?.({type:'probe',tabId,at:now(),ownerId:scope.ownerId,deviceId:scope.deviceId});await wait(25);\r\n  if(foreignHeartbeat())return false;\r\n  const token=`${tabId}:${now()}:${++fence}`,claim={type:'claim',tabId,token,at:now(),ownerId:scope.ownerId,deviceId:scope.deviceId};\r\n  const beforeClaim=readHeartbeat();if(fresh(beforeClaim)&&beforeClaim.type==='heartbeat')return false;\r\n  try{storage?.setItem(key,JSON.stringify(claim));}catch{return false;}channel?.postMessage?.(claim);await wait(25);\r\n  if(foreignHeartbeat()){removeHeartbeat(token);return false;}\r\n  const stored=readHeartbeat(),contenders=[claim,...[...peers.values()].filter(value=>fresh(value)&&value.type==='claim'),...(fresh(stored)&&stored.type==='claim'?[stored]:[])];\r\n  contenders.sort((a,b)=>String(a.tabId).localeCompare(String(b.tabId))||String(a.token).localeCompare(String(b.token)));\r\n  if(contenders[0]?.token!==token){removeHeartbeat(token);return false;}\r\n  const value={...claim,type:'heartbeat',at:now()};try{storage?.setItem(key,JSON.stringify(value));}catch{return false;}channel?.postMessage?.(value);await wait(25);\r\n  const owner=readHeartbeat();if(owner?.type!=='heartbeat'||owner.tabId!==tabId||owner.token!==token||foreignHeartbeat()){removeHeartbeat(token);return false;}\r\n  try{await scope.claimWorkoutLease(token);}catch(error){removeHeartbeat(token);throw error;}\r\n  const confirmed=readHeartbeat();if(confirmed?.type!=='heartbeat'||confirmed.tabId!==tabId||confirmed.token!==token||foreignHeartbeat()){removeHeartbeat(token);return false;}\r\n  leaseToken=token;held=true;timer=setIntervalFn?.(writeHeartbeat,heartbeatMs)??0;return true;\r\n };\r\n const assertLease=()=>{const owner=readHeartbeat(),blocked=disposed||!held||owner?.type!=='heartbeat'||owner.tabId!==tabId||owner.token!==leaseToken||!current(owner)||[...peers.values()].some(value=>fresh(value)&&value.type==='heartbeat');if(blocked){loseLease();throw new LocalCoachStorageError('lease','This workout is active in another tab.',{recoverable:true});}return leaseToken;};\r\n const release=()=>{const token=leaseToken;loseLease();channel?.postMessage?.({type:'release',tabId,token,at:now(),ownerId:scope.ownerId,deviceId:scope.deviceId});};\r\n try{const bootForeign=foreign();if(!bootForeign&&await acquire()){try{await scope.interruptActiveWorkouts({leaseToken:assertLease()});}finally{release();}}}\r\n catch(error){release();channel?.removeEventListener?.('message',receive);channel?.close?.();repository.close();throw error;}\r\n async function resumable(mode){const rows=await scope.listWorkouts();return rows.filter(row=>row.status==='paused'&&(mode===undefined||row.mode===mode)&&row.metadata?.control==='manual').at(-1)??null;}\r\n // #19: the newest paused/interrupted row started today (any control). Only a manual row truly resumes its\r\n // progress -- through paused()/resumable() above, gated by beginSet's pausedLocal check -- a paused or\r\n // interrupted camera row just restarts the same movement from zero; the Continue chip still surfaces it so\r\n // the cut-short set is not forgotten.\r\n async function unfinishedToday(){\r\n  const rows=await scope.listWorkouts(),today=new Date(now());\r\n  const sameDay=t=>{const d=new Date(t);return d.getFullYear()===today.getFullYear()&&d.getMonth()===today.getMonth()&&d.getDate()===today.getDate();};\r\n  return rows.filter(row=>(row.status==='paused'||row.status==='interrupted')&&sameDay(row.startedAt)).at(-1)??null;\r\n }\r\n return Object.freeze({\r\n  ownerId:scope.ownerId,deviceId:scope.deviceId,\r\n  hasForeignLease:foreign,\r\n  async paused(mode){return resumable(mode);},\r\n  async unfinished(){return unfinishedToday();},\r\n  async start({mode,goal,restSeconds,progress={},metadata={},control='camera'}){\r\n   if(!await acquire())throw new LocalCoachStorageError('lease','This workout is active in another tab.',{recoverable:true});const token=assertLease();\r\n   try{if(control==='manual'){const paused=await resumable(mode);if(paused){await scope.resumeWorkout(paused.id,{leaseToken:token});active={id:paused.id,control};return {...paused,status:'active',resumed:true};}}const workout=await scope.startWorkout({mode,goal,restSeconds,progress,metadata:{...metadata,control}},{leaseToken:token});active={id:workout.id,control};return workout;}catch(error){release();throw error;}\r\n  },\r\n  async update(id,progress){const token=assertLease();return scope.updateWorkout(id,{progress},{leaseToken:token});},\r\n  async pause(id,progress){const token=assertLease();if(progress)await scope.updateWorkout(id,{progress},{leaseToken:token});const workout=await scope.pauseWorkout(id,{leaseToken:token});active={id,control:'manual'};return workout;},\r\n  async resume(id){const token=assertLease(),workout=await scope.resumeWorkout(id,{leaseToken:token});active={id,control:'manual'};return workout;},\r\n  async complete(id,completion){const token=assertLease(),result=await scope.completeWorkout(id,completion,{leaseToken:token});active=null;release();return {local:true,queued:!!result.outbox,workout:result.workout,duplicate:result.duplicate};},\r\n  async interrupt(id,progress){const token=assertLease();if(progress)await scope.updateWorkout(id,{progress},{leaseToken:token});const workout=await scope.interruptWorkout(id,{leaseToken:token});active=null;release();return workout;},\r\n  async history(){return (await scope.listWorkouts()).filter(row=>row.status==='completed').sort((a,b)=>(b.completedAt??0)-(a.completedAt??0)||b.id.localeCompare(a.id));},\r\n  async listPerformanceWorkouts(){return (await scope.listWorkouts()).filter(row=>row.status==='completed'&&row.progress?.performance).sort((a,b)=>(a.completedAt??0)-(b.completedAt??0)||a.id.localeCompare(b.id));},\r\n  snapshot(){return {held,active,foreign:foreign()};},\r\n  close(){if(disposed)return;disposed=true;release();channel?.removeEventListener?.('message',receive);channel?.close?.();repository.close();},\r\n });\r\n}\r\n","local-coach/import-account-adapter.mjs":"import {createImportDrain} from './import-sync.mjs';\r\nimport {assignmentStatus} from './import-assignment.mjs';\r\nimport {IMPORT_UPLOAD_MANIFEST} from './import-upload-manifest.mjs';\r\n\r\nconst fail=code=>{throw Object.assign(Error('Refresh the selected account before continuing.'),{code});};\r\nconst target=account=>{\r\n if(typeof account?.user?.id!=='string'||!account.user.id||!Number.isSafeInteger(account.dataEpoch)||account.dataEpoch<1)fail('account_scope_unavailable');\r\n return {owner:account.user.id,epoch:account.dataEpoch};\r\n};\r\n// This is the sole browser adapter that turns an authenticated core response\r\n// into local deletion evidence. It accepts no caller-supplied deletion proof.\r\nexport function createImportAccountAdapter({repository,request,transitions,getAccount,manifest=IMPORT_UPLOAD_MANIFEST,now=()=>Date.now(),randomUUID=()=>crypto.randomUUID()}){\r\n async function loadAccount({signal}={}){\r\n  const response=await request('/api/account?core=1',{method:'GET',signal,cache:'no-store',credentials:'same-origin'});\r\n  if(response.redirected||!response.headers.get('content-type')?.includes('application/json'))fail('invalid_account_response');\r\n  const value=await response.json();\r\n  if(!response.ok)throw Object.assign(Error('Account could not be checked.'),{status:response.status});\r\n  target(value);return value;\r\n }\r\n const current=(ticket,expected)=>{\r\n  transitions.assertCurrent(ticket);const selected=target(getAccount());\r\n  if(selected.owner!==expected.owner||selected.epoch!==expected.epoch)fail('account_scope_changed');\r\n };\r\n async function fresh(ticket,expected){\r\n  current(ticket,expected);const value=await loadAccount({signal:ticket.signal});current(ticket,expected);\r\n  const live=target(value);if(live.owner!==expected.owner||live.epoch!==expected.epoch)fail('account_scope_changed');return value;\r\n }\r\n const drain=createImportDrain({repository,transition:transitions,manifest,now,loadAccount,postImport:async input=>{\r\n  const response=await request(input.path,{method:'POST',headers:input.headers,body:input.body,signal:input.signal,cache:'no-store',credentials:'same-origin'});\r\n  let body=null;if(!response.redirected&&response.headers.get('content-type')?.includes('application/json'))try{body=await response.json();}catch{}\r\n  return {status:response.status,body,retryAfter:response.headers.get('Retry-After')};\r\n }});\r\n return Object.freeze({\r\n  stop:()=>drain.stop(),\r\n  async history(){\r\n   const [workouts,state]=await Promise.all([repository.listWorkouts(),repository.listImportAssignments()]);\r\n   const observedAt=now();\r\n   return workouts.filter(row=>row.status==='completed').map(workout=>{\r\n    const head=state.heads.find(row=>row.clientWorkoutId===workout.clientWorkoutId),item=head&&state.items.find(row=>row.claimId===head.claimId);\r\n    return {workout,selectable:!head||head.status==='released',item,status:item?assignmentStatus(state,item.claimId,observedAt):'unassigned'};\r\n   });\r\n  },\r\n  async choose(kind,clientWorkoutIds,displayedAccount){\r\n   if(!['import','keep_local'].includes(kind)||!Array.isArray(clientWorkoutIds)||clientWorkoutIds.length<1||clientWorkoutIds.length>100||new Set(clientWorkoutIds).size!==clientWorkoutIds.length)fail('invalid_choice');\r\n   const selectedIds=[...clientWorkoutIds],expected=kind==='import'?target(displayedAccount):null,ticket=expected?transitions.capture():null;\r\n   if(expected)await fresh(ticket,expected);\r\n   const input={decisionId:randomUUID(),kind,selections:selectedIds.map(clientWorkoutId=>({clientWorkoutId,claimId:randomUUID(),itemId:randomUUID()})),...(expected?{targetAccountId:expected.owner,targetDataEpoch:expected.epoch}:{})};\r\n   const prepared=await repository.prepareImportDecision(input);if(expected)current(ticket,expected);\r\n   const result=await repository.commitImportDecision(prepared);if(expected)current(ticket,expected);\r\n   return result;\r\n  },\r\n  async reconcileDeletedTarget(displayedAccount){\r\n   const expected=target(displayedAccount),ticket=transitions.capture(),account=await fresh(ticket,expected),proof=account.deletionEvidence;\r\n   if(!proof||proof.ownerId!==expected.owner||proof.currentDataEpoch!==expected.epoch||!Number.isSafeInteger(proof.deletedThroughEpoch)||proof.deletedThroughEpoch!==expected.epoch-1)fail('invalid_deletion_evidence');\r\n   if(proof.deletedThroughEpoch===0)return 0;\r\n   const state=await repository.listImportAssignments();current(ticket,expected);let count=0;\r\n   for(const item of state.items){\r\n    if(item.kind!=='import'||item.targetAccountId!==expected.owner||item.targetDataEpoch>proof.deletedThroughEpoch)continue;\r\n    current(ticket,expected);\r\n    const result=await repository.releaseImportAssignment({claimId:item.claimId,generation:item.generation,ownerId:proof.ownerId,currentDataEpoch:proof.currentDataEpoch,deletedThroughEpoch:proof.deletedThroughEpoch});\r\n    if(!result.duplicate)count++;\r\n   }\r\n   current(ticket,expected);return count;\r\n  },\r\n  async upload(displayedAccount,{resumeParkedClaimIds=[]}={}){\r\n   // Fail closed before auth, IndexedDB or transport when the build is disabled.\r\n   if(manifest.uploadsEnabled!==true)return {stopped:'disabled',posted:0,imported:0};\r\n   const expected=target(displayedAccount),ticket=transitions.capture();await fresh(ticket,expected);\r\n   const result=await drain.run({targetAccountId:expected.owner,resumeParkedClaimIds});current(ticket,expected);return result;\r\n  },\r\n });\r\n}\r\n","local-coach/import-assignment.mjs":"import {isAttemptEvent,validateAttemptEvents,projectImportAttempts} from './import-attempts.mjs';\r\n// Pure policy for a single owner's guest-import history. The caller must load\r\n// validated source workouts and commit returned changes in one transaction.\r\n// Epochs/proofs come from an authenticated adapter: shape checks are NOT auth.\r\nexport class ImportAssignmentError extends Error {\r\n  constructor(code, message = code) { super(message); this.name = 'ImportAssignmentError'; this.code = code; }\r\n}\r\nconst policyFailures = new WeakSet();\r\nconst fail = (code, message) => { const error = new ImportAssignmentError(code, message); policyFailures.add(error); throw error; };\r\nconst own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);\r\nconst opaque = (value, name) => {\r\n  if (typeof value !== 'string' || !value || value.length > 512 || value.trim() !== value) fail('invalid-record', name + ' must be a canonical opaque id');\r\n  return value;\r\n};\r\nconst positive = (value, name) => {\r\n  if (!Number.isSafeInteger(value) || value < 1) fail('invalid-record', name + ' must be a positive safe integer');\r\n  return value;\r\n};\r\nconst owned = new WeakSet();\r\nconst freeze = value => {\r\n  if (value && typeof value === 'object' && !Object.isFrozen(value)) {\r\n    for (const key of Object.getOwnPropertyNames(value)) freeze(value[key]);\r\n    Object.freeze(value);\r\n    owned.add(value);\r\n  }\r\n  return value;\r\n};\r\nconst canonicalJSON = value => {\r\n  try {\r\n  const ancestors = new Set();\r\n  let remaining = 131072;\r\n  const count = text => {\r\n    remaining -= text.length;\r\n    if (remaining < 0) fail('invalid-record', 'canonical record including its envelope exceeds the local record limit');\r\n    return text;\r\n  };\r\n  const visit = (input, depth = 0) => {\r\n    if (depth > 64) fail('invalid-record', 'snapshot is too deep');\r\n    if (input === null || typeof input === 'string' || typeof input === 'boolean') return count(JSON.stringify(input));\r\n    if (typeof input === 'number' && Number.isFinite(input)) return count(JSON.stringify(input));\r\n    if (!input || typeof input !== 'object' || ancestors.has(input)) fail('invalid-record', 'snapshot must be JSON');\r\n    const array = Array.isArray(input);\r\n    const descriptors = Object.getOwnPropertyDescriptors(input);\r\n    if (Object.getOwnPropertySymbols(descriptors).length) fail('invalid-record');\r\n    const keys = Object.keys(descriptors).filter(key => descriptors[key].enumerable);\r\n    const length = array ? descriptors.length?.value : 0;\r\n    if (array) {\r\n      if (Object.getPrototypeOf(input) !== Array.prototype) fail('invalid-record', 'snapshot arrays must be plain arrays');\r\n      if (!Number.isSafeInteger(length) || length > remaining || keys.length !== length || keys.some((key, index) => key !== String(index))) fail('invalid-record', 'snapshot arrays must be dense and bounded');\r\n    } else if (![Object.prototype, null].includes(Object.getPrototypeOf(input))) fail('invalid-record', 'snapshot must contain plain objects');\r\n    if (Object.keys(descriptors).length !== keys.length + (array ? 1 : 0) || keys.some(key => !own(descriptors[key], 'value'))) fail('invalid-record', 'snapshot must contain data properties');\r\n    count(array ? '[]' : '{}');\r\n    remaining -= Math.max(0, keys.length - 1);\r\n    if (remaining < 0) fail('invalid-record', 'snapshot exceeds the local record limit');\r\n    ancestors.add(input);\r\n    const parts = [];\r\n    for (const key of array ? keys : keys.sort()) {\r\n      parts.push((array ? '' : count(JSON.stringify(key) + ':')) + visit(descriptors[key].value, depth + 1));\r\n    }\r\n    const result = (array ? '[' : '{') + parts.join(',') + (array ? ']' : '}');\r\n    ancestors.delete(input);\r\n    return result;\r\n  };\r\n  const result = visit(value);\r\n  if (result.length > 131072) fail('invalid-record', 'canonical record including its envelope exceeds the local record limit');\r\n  return result;\r\n  } catch (error) {\r\n    if (policyFailures.has(error)) throw error;\r\n    fail('invalid-record');\r\n  }\r\n};\r\n// Capture own data descriptors without invoking input accessors. Validate the\r\n// detached record only; callers cannot change a discriminator between reads.\r\nconst dataRecord = raw => {\r\n  try {\r\n    if (!raw || typeof raw !== 'object' || Array.isArray(raw) ||\r\n        ![Object.prototype, null].includes(Object.getPrototypeOf(raw))) fail('invalid-record');\r\n    const descriptors = Object.getOwnPropertyDescriptors(raw), copy = Object.create(null);\r\n    if (Object.getOwnPropertySymbols(descriptors).length) fail('invalid-record');\r\n    for (const [key, descriptor] of Object.entries(descriptors)) {\r\n      if (!own(descriptor, 'value') || !descriptor.enumerable) fail('invalid-record');\r\n      copy[key] = descriptor.value;\r\n    }\r\n    return copy;\r\n  } catch (error) {\r\n    if (policyFailures.has(error)) throw error;\r\n    fail('invalid-record');\r\n  }\r\n};\r\nconst itemKey = item => JSON.stringify([item.sourceOwnerId, item.clientWorkoutId]);\r\nconst stateOf = state => {\r\n  try {\r\n    if (!owned.has(state)) state = structuredClone(state);\r\n    if (!state || !Array.isArray(state.heads) || !Array.isArray(state.items) || !Array.isArray(state.events)) fail('invalid-state');\r\n    const ids = new Set(), headKeys = new Set();\r\n    for (const item of state.items) {\r\n      opaque(item.itemId, 'itemId'); opaque(item.claimId, 'claimId'); positive(item.generation, 'generation');\r\n      if (ids.has(item.itemId) || ids.has(item.claimId) || item.itemId === item.claimId) fail('invalid-state');\r\n      ids.add(item.itemId); ids.add(item.claimId);\r\n    }\r\n    for (const head of state.heads) {\r\n      const key = itemKey(head), item = state.items.find(candidate => candidate.claimId === head.claimId);\r\n      if (headKeys.has(key) || !['active', 'released', 'keep_local'].includes(head.status) || !item ||\r\n          item.generation !== head.generation || itemKey(item) !== key || item.itemId !== head.itemId) fail('invalid-state');\r\n      headKeys.add(key);\r\n    }\r\n    for (const event of state.events) {\r\n      const item = state.items.find(candidate => candidate.claimId === event.claimId);\r\n      if (!item || item.kind !== 'import' || (!['imported', 'retry', 'target_deleted', 'deletion_evidence'].includes(event.type) && !isAttemptEvent(event)) || event.generation !== item.generation) fail('invalid-state');\r\n      if (['target_deleted', 'deletion_evidence'].includes(event.type)) {\r\n        opaque(event.targetAccountId, 'targetAccountId');\r\n        positive(event.deletedThroughEpoch, 'deletedThroughEpoch');\r\n        positive(event.currentDataEpoch, 'currentDataEpoch');\r\n        if (event.targetAccountId !== item.targetAccountId ||\r\n            !(item.targetDataEpoch <= event.deletedThroughEpoch && event.deletedThroughEpoch < event.currentDataEpoch)) fail('invalid-state');\r\n      }\r\n    }\r\n    validateAttemptEvents(state.events);\r\n    return state;\r\n  } catch { fail('invalid-state'); }\r\n};\r\nconst deletionWatermark = (state, targetAccountId) => state.events\r\n  .filter(event => ['target_deleted', 'deletion_evidence'].includes(event.type) && event.targetAccountId === targetAccountId)\r\n  .reduce((maximum, event) => Math.max(maximum, event.deletedThroughEpoch), 0);\r\nexport const emptyImportAssignmentState = () => freeze({heads: [], items: [], events: []});\r\nconst readClaim = raw => {\r\n  raw = dataRecord(raw);\r\n  if (!raw || typeof raw !== 'object' || Array.isArray(raw) || raw.completed !== true || !['import', 'keep_local'].includes(raw.kind)) fail('invalid-record');\r\n  const fields = ['decisionId','sourceOwnerId','sourceDeviceId','clientWorkoutId','claimId','itemId','kind','completed'];\r\n  if (raw.kind === 'import') fields.push('targetAccountId','targetDataEpoch','fingerprint','snapshot');\r\n  const prepared = raw.kind === 'import' && (own(raw,'digestVersion') || own(raw,'idempotencyKey'));\r\n  if (prepared) {\r\n    fields.push('digestVersion','idempotencyKey');\r\n    if (![1,2].includes(raw.digestVersion) || typeof raw.idempotencyKey !== 'string' || !/^[0-9a-f]{64}$/.test(raw.idempotencyKey)) fail('invalid-record');\r\n  }\r\n  if (Object.keys(raw).some(key => !fields.includes(key))) fail('invalid-record', 'unexpected claim field');\r\n  const base = Object.fromEntries(fields.filter(key => !['completed','targetDataEpoch','fingerprint','snapshot','digestVersion','idempotencyKey'].includes(key)).map(key => [key, opaque(raw[key], key)]));\r\n  if (!/^guest:\\S+$/.test(base.sourceOwnerId) || base.claimId === base.itemId) fail('invalid-record');\r\n  if (raw.kind === 'keep_local') return {...base, decision: canonicalJSON(base)};\r\n  if (typeof raw.fingerprint !== 'string' || !/^[0-9a-f]{64}$/.test(raw.fingerprint) || !own(raw, 'snapshot')) fail('invalid-record');\r\n  const normalized = {...base, targetDataEpoch: positive(raw.targetDataEpoch, 'targetDataEpoch'),\r\n    fingerprint: raw.fingerprint, snapshot: JSON.parse(canonicalJSON(raw.snapshot)),\r\n    ...(prepared ? {digestVersion:raw.digestVersion,idempotencyKey:raw.idempotencyKey} : {})};\r\n  return {...normalized, decision: canonicalJSON(normalized)};\r\n};\r\nexport const claimAssignment = (state, input) => {\r\n  state = stateOf(state);\r\n  const claim = readClaim(input), prior = state.items.find(item => item.sourceOwnerId === claim.sourceOwnerId && item.decisionId === claim.decisionId && item.clientWorkoutId === claim.clientWorkoutId);\r\n  if (prior) {\r\n    if (prior.decision !== claim.decision) fail('decision-conflict');\r\n    return freeze({state, item: prior, duplicate: true});\r\n  }\r\n  if (state.items.some(item => [item.itemId, item.claimId].includes(claim.itemId) || [item.itemId, item.claimId].includes(claim.claimId))) fail('id-conflict');\r\n  const key = itemKey(claim), previous = state.heads.find(head => itemKey(head) === key);\r\n  if (previous && previous.status !== 'released') fail('covered');\r\n  if (claim.kind === 'import') {\r\n    const watermark = deletionWatermark(state, claim.targetAccountId);\r\n    if (claim.targetDataEpoch <= watermark) fail('deleted-target-epoch');\r\n  }\r\n  const generation = state.items.filter(item => itemKey(item) === key).reduce((maximum, item) => Math.max(maximum, item.generation), 0) + 1;\r\n  positive(generation, 'generation');\r\n  const item = freeze({...claim, generation});\r\n  const head = freeze({sourceOwnerId: claim.sourceOwnerId, sourceDeviceId: claim.sourceDeviceId, clientWorkoutId: claim.clientWorkoutId,\r\n    claimId: claim.claimId, itemId: claim.itemId, generation, status: claim.kind === 'keep_local' ? 'keep_local' : 'active'});\r\n  return freeze({state: {\r\n    heads: previous ? state.heads.map(candidate => itemKey(candidate) === key ? head : candidate) : [...state.heads, head],\r\n    items: [...state.items, item], events: state.events,\r\n  }, item, duplicate: false});\r\n};\r\nexport const assignmentStatus = (state, claimId, now) => {\r\n  state = stateOf(state);\r\n  const item = state.items.find(candidate => candidate.claimId === claimId);\r\n  if (!item) fail('not-found');\r\n  if (item.kind === 'keep_local') return 'keep_local';\r\n  if (item.targetDataEpoch <= deletionWatermark(state, item.targetAccountId)) return 'target_deleted';\r\n  if (state.events.some(event=>event.claimId===claimId&&event.type==='target_deleted')) return 'target_deleted';\r\n  return projectImportAttempts(state.events.filter(event=>event.claimId===claimId),now).status;\r\n};\r\nexport const recordImportOutcome = (state, callback) => {\r\n  state = stateOf(state);\r\n  callback = dataRecord(callback);\r\n  if (!callback || !['imported','retry'].includes(callback.type)) fail('invalid-record');\r\n  const item = state.items.find(candidate => candidate.claimId === callback.claimId);\r\n  if (!item || item.kind !== 'import' || item.generation !== callback.generation ||\r\n      item.sourceOwnerId !== callback.sourceOwnerId || item.sourceDeviceId !== callback.sourceDeviceId ||\r\n      item.clientWorkoutId !== callback.clientWorkoutId) fail('not-found');\r\n  const status = assignmentStatus(state, item.claimId);\r\n  if (status === 'target_deleted' || status === 'imported' || ['conflict','rejected'].includes(status) || status === callback.type) return freeze({state, item, duplicate: true});\r\n  if (own(item,'digestVersion')) fail('invalid-record', 'Prepared import outcomes require a committed attempt.');\r\n  const event = freeze({type: callback.type, claimId: item.claimId, generation: item.generation});\r\n  return freeze({state: {...state, events: [...state.events, event]}, item, duplicate: false});\r\n};\r\nexport const recordImported = (state, callback) => recordImportOutcome(state, {...dataRecord(callback), type: 'imported'});\r\nexport const releaseAssignment = (state, proof) => {\r\n  state = stateOf(state);\r\n  proof = dataRecord(proof);\r\n  if (!proof || typeof proof !== 'object') fail('invalid-record');\r\n  const item = state.items.find(candidate => candidate.claimId === proof.claimId);\r\n  const generation = positive(proof.generation, 'generation'), ownerId = opaque(proof.ownerId, 'ownerId');\r\n  const currentDataEpoch = positive(proof.currentDataEpoch, 'currentDataEpoch'), deletedThroughEpoch = positive(proof.deletedThroughEpoch, 'deletedThroughEpoch');\r\n  if (!item || item.kind !== 'import' || item.generation !== generation || item.targetAccountId !== ownerId ||\r\n      !(item.targetDataEpoch <= deletedThroughEpoch && deletedThroughEpoch < currentDataEpoch)) fail('invalid-deletion-proof');\r\n  const old = state.events.find(event => event.type === 'target_deleted' && event.claimId === item.claimId);\r\n  const maximum = state.events.filter(event => event.claimId === item.claimId && ['target_deleted','deletion_evidence'].includes(event.type))\r\n    .reduce((value, event) => Math.max(value, event.deletedThroughEpoch), 0);\r\n  if (old && maximum >= deletedThroughEpoch) return freeze({state, item, duplicate: true});\r\n  const event = freeze({type: old ? 'deletion_evidence' : 'target_deleted', claimId: item.claimId, generation,\r\n    targetAccountId: ownerId, currentDataEpoch, deletedThroughEpoch});\r\n  const key = itemKey(item), heads = state.heads.map(head => itemKey(head) === key && head.claimId === item.claimId && head.generation === generation\r\n    ? freeze({...head, status: 'released'}) : head);\r\n  return freeze({state: {...state, heads, events: [...state.events, event]}, item, duplicate: false});\r\n};\r\n","local-coach/import-attempts.mjs":"import {validateImportSnapshot,canonicalImportKey} from '../workout-import-codec.mjs';\r\nexport const IMPORT_ATTEMPT_STALE_MS=30000;\r\nconst own=(value,key)=>Object.prototype.hasOwnProperty.call(value,key);\r\nconst invalid=()=>{throw Error('Invalid import attempt record.');};\r\nconst id=value=>{if(typeof value!=='string'||!value||value.length>512||value.trim()!==value)invalid();};\r\nconst integer=(value,min=0)=>{if(!Number.isSafeInteger(value)||value<min)invalid();};\r\nfunction data(raw){\r\n if(!raw||typeof raw!=='object'||Array.isArray(raw)||![null,Object.prototype].includes(Object.getPrototypeOf(raw)))invalid();\r\n const descriptors=Object.getOwnPropertyDescriptors(raw),out={};\r\n if(Reflect.ownKeys(descriptors).some(key=>typeof key!=='string'||!own(descriptors[key],'value')||!descriptors[key].enumerable))invalid();\r\n for(const [key,descriptor]of Object.entries(descriptors))Object.defineProperty(out,key,{value:descriptor.value,enumerable:true});\r\n return out;\r\n}\r\nconst exact=(record,keys)=>{if(Object.keys(record).length!==keys.length||keys.some(key=>!own(record,key)))invalid();};\r\nconst resultTypes=['imported','retryable_failure','parked','conflict','rejected'];\r\nexport function isAttemptEvent(event){return ['retry_budget_resumed','submission_started','retryable_failure','parked','conflict','rejected'].includes(event.type)||event.type==='imported'&&own(event,'attemptId');}\r\nfunction captureBegin(raw){\r\n const v=data(raw);exact(v,['claimId','generation','attemptId','startedAt']);\r\n id(v.claimId);id(v.attemptId);integer(v.generation,1);integer(v.startedAt);\r\n integer(v.startedAt+IMPORT_ATTEMPT_STALE_MS);\r\n return {type:'submission_started',claimId:v.claimId,generation:v.generation,attemptId:v.attemptId,startedAt:v.startedAt,expiresAt:v.startedAt+IMPORT_ATTEMPT_STALE_MS};\r\n}\r\nfunction captureResult(raw){\r\n const v=data(raw);id(v.claimId);id(v.attemptId);integer(v.generation,1);integer(v.at);\r\n if(!resultTypes.includes(v.type))invalid();\r\n const keys=['claimId','generation','attemptId','type','at'];\r\n if(v.type==='imported'){\r\n  keys.push('serverWorkoutId','httpStatus');id(v.serverWorkoutId);if(![200,201].includes(v.httpStatus))invalid();\r\n }else{\r\n  keys.push('category');\r\n  const categories={retryable_failure:['offline','timeout','rate_limited','server_unavailable','unknown_response'],parked:['signed_out','target_mismatch','feature_disabled','retry_exhausted','epoch_mismatch'],conflict:['fingerprint_conflict'],rejected:['invalid_payload','policy_rejected']};\r\n  if(!categories[v.type].includes(v.category))invalid();\r\n  if(own(v,'httpStatus')){keys.push('httpStatus');integer(v.httpStatus,100);if(v.httpStatus>599)invalid();}\r\n  if(v.type==='retryable_failure'){keys.push('retryAt');integer(v.retryAt,v.at);if(v.retryAt-v.at>86400000)invalid();}\r\n }\r\n exact(v,keys);return Object.fromEntries(keys.map(key=>[key,v[key]]));\r\n}\r\nconst same=(a,b)=>JSON.stringify(a)===JSON.stringify(b);\r\n// Relational validation makes persisted malformed/orphan results fail closed.\r\nexport function validateAttemptEvents(events){\r\n const starts=new Map(),results=new Set();\r\n for(const event of events){\r\n  if(!isAttemptEvent(event))continue;\r\n  if(event.type==='retry_budget_resumed'){\r\n   exact(event,['type','claimId','generation','resumeId','at']);id(event.claimId);id(event.resumeId);integer(event.generation,1);integer(event.at);\r\n   const index=events.indexOf(event),next=events[index+1];\r\n   if(!next||next.type!=='submission_started'||next.attemptId!==event.resumeId||next.claimId!==event.claimId||next.generation!==event.generation||next.startedAt!==event.at||projectImportAttempts(events.slice(0,index).filter(e=>e.claimId===event.claimId),event.at).status!=='parked')invalid();\r\n  }else if(event.type==='submission_started'){\r\n   const {type,expiresAt,attemptLimit,...input}=event,normalized=captureBegin(input);\r\n   if(attemptLimit!==undefined){integer(attemptLimit,1);if(attemptLimit>5)invalid();normalized.attemptLimit=attemptLimit;}\r\n   if(!same(event,normalized)||starts.has(event.attemptId))invalid();starts.set(event.attemptId,event);\r\n  }else{\r\n   const normalized=captureResult(event),start=starts.get(event.attemptId);\r\n   // Property order is not semantic for caller/re-hydrated result records.\r\n   if(Object.keys(event).length!==Object.keys(normalized).length||!start||results.has(event.attemptId)||start.claimId!==event.claimId||start.generation!==event.generation||event.at<start.startedAt)invalid();\r\n   results.add(event.attemptId);\r\n  }\r\n }\r\n}\r\nfunction projectWithoutBudget(events,now){\r\n if(events.some(e=>e.type==='imported'))return {status:'imported'};\r\n const terminal=events.find(e=>e.type==='conflict'||e.type==='rejected');if(terminal)return {status:terminal.type};\r\n const latest=events.filter(e=>e.type==='submission_started').at(-1);\r\n if(!latest)return {status:events.some(e=>e.type==='retry')?'retry':'pending'};\r\n const result=events.find(e=>e.attemptId===latest.attemptId&&e.type!=='submission_started');\r\n if(!result)return {status:now!==undefined&&now>=latest.expiresAt?'retryable':'in_flight',attemptId:latest.attemptId,retryAt:latest.expiresAt};\r\n return {status:result.type==='retryable_failure'?'retryable':result.type,attemptId:latest.attemptId,...(result.type==='retryable_failure'?{retryAt:result.retryAt}:{})};\r\n}\r\nexport function importRetryBudget(events){\r\n const boundary=events.findLastIndex(e=>e.type==='retry_budget_resumed');\r\n const starts=events.slice(boundary+1).filter(e=>e.type==='submission_started');\r\n return {attemptNumber:starts.length,attemptLimit:starts[0]?.attemptLimit??3};\r\n}\r\nexport function projectImportAttempts(events,now){\r\n const projection=projectWithoutBudget(events,now),budget=importRetryBudget(events);\r\n if(projection.status==='retryable'&&budget.attemptNumber>=budget.attemptLimit)return {...budget,status:'parked',category:'retry_exhausted'};\r\n return {...projection,...budget};\r\n}\r\nfunction preparedItem(state,claimId,ownerId,deviceId){\r\n const item=state.items.find(i=>i.claimId===claimId);\r\n if(!ownerId.startsWith('guest:')||!item||item.kind!=='import'||item.sourceOwnerId!==ownerId||item.sourceDeviceId!==deviceId)invalid();\r\n const header=state.decisions.find(d=>d.decisionId===item.decisionId);\r\n if(!header||header.preparationVersion!==1||header.kind!=='import'||header.sourceOwnerId!==ownerId||header.sourceDeviceId!==deviceId||!Array.isArray(header.selections)||header.selections.length<1||header.selections.length>100)invalid();\r\n exact(header,['sourceOwnerId','decisionId','sourceDeviceId','kind','targetAccountId','targetDataEpoch','preparationVersion','selections']);\r\n const rows=state.items.filter(i=>i.decisionId===header.decisionId),selected=new Set(),identities=new Set();\r\n if(rows.length!==header.selections.length)invalid();\r\n for(const s of header.selections){\r\n  exact(s,['clientWorkoutId','claimId','itemId']);id(s.clientWorkoutId);id(s.claimId);id(s.itemId);\r\n  if(selected.has(s.clientWorkoutId)||identities.has(s.claimId)||identities.has(s.itemId)||s.claimId===s.itemId)invalid();\r\n  selected.add(s.clientWorkoutId);identities.add(s.claimId);identities.add(s.itemId);\r\n  const row=rows.find(i=>i.clientWorkoutId===s.clientWorkoutId&&i.claimId===s.claimId&&i.itemId===s.itemId);\r\n  if(!row||row.kind!=='import'||row.sourceOwnerId!==ownerId||row.sourceDeviceId!==deviceId||row.targetAccountId!==header.targetAccountId||row.targetDataEpoch!==header.targetDataEpoch||![1,2].includes(row.digestVersion)||typeof row.idempotencyKey!=='string'||!(/^[a-f0-9]{64}$/).test(row.idempotencyKey))invalid();\r\n  validateImportSnapshot(row.snapshot);if(row.snapshot.clientWorkoutId!==row.clientWorkoutId)invalid();\r\n  canonicalImportKey({digestVersion:row.digestVersion,targetAccountId:row.targetAccountId,targetDataEpoch:row.targetDataEpoch,clientWorkoutId:row.clientWorkoutId,fingerprint:row.fingerprint});\r\n }\r\n return item;\r\n}\r\nexport function createImportAttempts({db,ownerId,deviceId,tables,read,persist,transact,readOperation,status,invalid:storageInvalid}){\r\n const guard=fn=>{try{return fn();}catch{storageInvalid('Invalid import attempt or incomplete prepared decision.');}};\r\n const inspect=(state,item,now)=>{\r\n  const authoritative=status(state,item.claimId);\r\n  if(['target_deleted','keep_local','imported','conflict','rejected'].includes(authoritative))return {status:authoritative};\r\n  return projectImportAttempts(state.events.filter(e=>e.claimId===item.claimId),now);\r\n };\r\n return Object.freeze({\r\n  async beginImportAttempt(input,{resumeParked=false,attemptLimit=3}={}){\r\n   guard(()=>{integer(attemptLimit,1);if(attemptLimit>5)invalid();});\r\n   const event=guard(()=>captureBegin(input));\r\n   return transact('Import attempt start',tables,async()=>{\r\n    const state=await read(),item=guard(()=>preparedItem(state,event.claimId,ownerId,deviceId));\r\n    if(item.generation!==event.generation)storageInvalid('Import attempt generation mismatch.');\r\n    // Exact retry may observe a terminal outcome; it never authorizes another POST.\r\n    const prior=state.events.find(e=>e.type==='submission_started'&&e.attemptId===event.attemptId);\r\n    if(prior){const {attemptLimit:_limit,...identity}=prior;if(!same(identity,event))storageInvalid('Attempt identity cannot change.');return {duplicate:true,item,...inspect(state,item,event.startedAt)};}\r\n    const lastTime=state.events.reduce((latest,e)=>e.claimId===item.claimId?Math.max(latest,e.at??e.startedAt??0):latest,0);\r\n    if(event.startedAt<lastTime)storageInvalid('Attempt time cannot precede prior observations.');\r\n    const current=inspect(state,item,event.startedAt),head=state.heads.find(h=>h.claimId===item.claimId&&h.generation===item.generation&&h.clientWorkoutId===item.clientWorkoutId);\r\n    if(!head||head.status!=='active'||current.status==='parked'&&resumeParked!==true||['target_deleted','imported','conflict','rejected','in_flight'].includes(current.status)||current.retryAt>event.startedAt)storageInvalid('Import attempt is not eligible.');\r\n    const events=state.events.filter(e=>e.claimId===item.claimId),budget=importRetryBudget(events);\r\n    const resuming=current.status==='parked'&&resumeParked===true;\r\n    const limit=resuming||budget.attemptNumber===0?attemptLimit:budget.attemptLimit;\r\n    if(!resuming&&budget.attemptNumber>=limit)storageInvalid('Import retry budget is exhausted.');\r\n    const resume=resuming?[{type:'retry_budget_resumed',claimId:item.claimId,generation:item.generation,resumeId:event.attemptId,at:event.startedAt}]:[];\r\n    const after={...state,events:[...state.events,...resume,{...event,attemptLimit:limit}]};guard(()=>validateAttemptEvents(after.events));await persist(state,after);\r\n    return {duplicate:false,item,...inspect(after,item,event.startedAt)};\r\n   });\r\n  },\r\n  async recordImportAttemptResult(input){\r\n   const event=guard(()=>captureResult(input));\r\n   return transact('Import attempt result',tables,async()=>{\r\n    const state=await read(),item=guard(()=>preparedItem(state,event.claimId,ownerId,deviceId));\r\n    if(item.generation!==event.generation)storageInvalid('Import attempt generation mismatch.');\r\n    // Validate existing state even if a duplicate will be returned.\r\n    guard(()=>validateAttemptEvents(state.events));status(state,item.claimId);\r\n    const start=state.events.find(e=>e.type==='submission_started'&&e.attemptId===event.attemptId);\r\n    if(!start||start.claimId!==event.claimId||start.generation!==event.generation||event.at<start.startedAt)storageInvalid('Result must match a committed attempt.');\r\n    const prior=state.events.find(e=>e.attemptId===event.attemptId&&e.type!=='submission_started');\r\n    if(prior){if(!same(captureResult(prior),event))storageInvalid('An attempt result cannot change.');return {duplicate:true,item,...inspect(state,item,event.at)};}\r\n    const after={...state,events:[...state.events,event]};guard(()=>validateAttemptEvents(after.events));await persist(state,after);\r\n    return {duplicate:false,item,...inspect(after,item,event.at)};\r\n   });\r\n  },\r\n  async importAttemptState(claimId,now){\r\n   guard(()=>{id(claimId);integer(now);});\r\n   return readOperation('Import attempt status',()=>db.transaction('r',...tables,async()=>{const state=await read(),item=guard(()=>preparedItem(state,claimId,ownerId,deviceId));return {item,...inspect(state,item,now)};}));\r\n  },\r\n });\r\n}\r\n","local-coach/import-ledger.mjs":"import {createImportAttempts} from './import-attempts.mjs';\r\nimport {createImportPreparation} from './import-preparation.mjs';\r\nimport {ImportAssignmentError,claimAssignment,recordImportOutcome,releaseAssignment,assignmentStatus} from './import-assignment.mjs';\r\n\r\n// Layout version is independent of the unchanged v1 workout row schema.\r\nexport const IMPORT_LEDGER_STORES=Object.freeze({\r\n guestHistoryDecisions:'[sourceOwnerId+decisionId],sourceOwnerId',\r\n guestHistoryItems:'&itemId,&claimId,&[sourceOwnerId+decisionId+clientWorkoutId],&[sourceOwnerId+clientWorkoutId+generation],sourceOwnerId',\r\n guestHistoryEvents:'++sequence,sourceOwnerId,claimId',\r\n guestHistoryAssignmentHeads:'[sourceOwnerId+clientWorkoutId],sourceOwnerId',\r\n});\r\nexport const importLedgerTables=db=>Object.keys(IMPORT_LEDGER_STORES).map(name=>db.table(name));\r\nexport async function readImportLedger(db,sourceOwnerId,StorageError){\r\n const read=async name=>(await db.table(name).where('sourceOwnerId').equals(sourceOwnerId).toArray()).map(row=>{if(row.schemaVersion!==1)throw new StorageError('invalid-record','Unsupported import ledger row schema.',{recoverable:true});const {schemaVersion,...value}=row;return value;});\r\n const [decisions,items,eventRows,heads]=await Promise.all(Object.keys(IMPORT_LEDGER_STORES).map(read));\r\n const compare=(a,b)=>a<b?-1:a>b?1:0;\r\n decisions.sort((a,b)=>compare(a.sourceOwnerId,b.sourceOwnerId)||compare(a.decisionId,b.decisionId));\r\n heads.sort((a,b)=>compare(a.sourceOwnerId,b.sourceOwnerId)||compare(a.clientWorkoutId,b.clientWorkoutId));\r\n items.sort((a,b)=>compare(a.sourceOwnerId,b.sourceOwnerId)||compare(a.clientWorkoutId,b.clientWorkoutId)||a.generation-b.generation);\r\n return {decisions,heads,items,events:eventRows.sort((a,b)=>a.sequence-b.sequence).map(({sequence,sourceOwnerId,...event})=>event)};\r\n}\r\nexport async function deleteImportLedger(db,sourceOwnerId){\r\n const counts={};\r\n for(const name of Object.keys(IMPORT_LEDGER_STORES))counts[name]=await db.table(name).where('sourceOwnerId').equals(sourceOwnerId).delete();\r\n return counts;\r\n}\r\nexport function createImportLedger({db,ownerId,deviceId,getWorkout,transact,StorageError,classifyError}){\r\n const tables=importLedgerTables(db);\r\n const policy=fn=>{try{return fn();}catch(error){if(error instanceof ImportAssignmentError)throw new StorageError(error.code,error.message,{cause:error,recoverable:true});throw error;}};\r\n const invalid=message=>{throw new StorageError('invalid-record',message,{recoverable:true});};\r\n // getWorkout must only read the workouts table and open no nested write\r\n // transaction. Repository supplies its internal owner-scoped validator.\r\n const readOperation=async(label,fn)=>{try{return await fn();}catch(error){throw classifyError(error,label);}};\r\n const source=async clientWorkoutId=>{\r\n  const workout=await getWorkout(clientWorkoutId);\r\n  if(!ownerId.startsWith('guest:')||!workout||workout.status!=='completed'||workout.id!==clientWorkoutId||workout.id!==workout.clientWorkoutId||workout.ownerId!==ownerId||workout.deviceId!==deviceId)invalid('Import source must be a completed workout owned by this guest and device.');\r\n  return workout;\r\n };\r\n const persist=async(before,after)=>{\r\n  // Assert append-only policy output before any writes, including all head keys.\r\n  const same=(a,b)=>JSON.stringify(a)===JSON.stringify(b),headKey=head=>JSON.stringify([head.sourceOwnerId,head.clientWorkoutId]);\r\n  for(const key of ['items','events'])if(after[key].length<before[key].length||before[key].some((entry,index)=>!same(entry,after[key][index])))invalid('Import policy attempted to change immutable history.');\r\n  const nextKeys=after.heads.map(headKey);\r\n  if(after.heads.length<before.heads.length||new Set(nextKeys).size!==nextKeys.length||before.heads.some(head=>!nextKeys.includes(headKey(head))))invalid('Import policy attempted to remove an assignment head.');\r\n  if(after.items.slice(before.items.length).some(item=>item.sourceOwnerId!==ownerId)||after.heads.some(head=>head.sourceOwnerId!==ownerId))invalid('Import policy output has a mismatched source owner.');\r\n  // Immutable rows are appended only; head replacement is the sole mutation.\r\n  for(const item of after.items.slice(before.items.length))await db.guestHistoryItems.add({...item,schemaVersion:1});\r\n  for(const event of after.events.slice(before.events.length))await db.guestHistoryEvents.add({...event,sourceOwnerId:ownerId,schemaVersion:1});\r\n  for(const head of after.heads){\r\n   const old=before.heads.find(candidate=>candidate.sourceOwnerId===head.sourceOwnerId&&candidate.clientWorkoutId===head.clientWorkoutId);\r\n   if(JSON.stringify(old)!==JSON.stringify(head))await db.guestHistoryAssignmentHeads.put({...head,schemaVersion:1});\r\n  }\r\n };\r\n const transition=(label,fn)=>transact(label,tables,async()=>{\r\n  const before=await readImportLedger(db,ownerId,StorageError),result=policy(()=>fn(before));\r\n  await persist(before,result.state);\r\n  return {item:result.item,duplicate:result.duplicate,status:assignmentStatus(result.state,result.item.claimId)};\r\n });\r\n return Object.freeze({\r\n  ...createImportAttempts({db,ownerId,deviceId,tables,read:()=>readImportLedger(db,ownerId,StorageError),persist,transact,readOperation,status:assignmentStatus,invalid}),\r\n  ...createImportPreparation({db,ownerId,deviceId,tables,source,persist,transact,read:()=>readImportLedger(db,ownerId,StorageError),policy,invalid,readOperation,StorageError}),\r\n  // Call before any asynchronous hashing. The revision is rechecked inside the\r\n  // claim transaction. This legacy API cannot create prepared upload metadata;\r\n  // callers must use prepareImportDecision for codec-bound decisions.\r\n  async prepareImportAssignment(clientWorkoutId){\r\n   return readOperation('Import assignment preparation',()=>db.transaction('r',db.workouts,async()=>{const workout=await source(clientWorkoutId);return {workout,sourceRevision:JSON.stringify(workout)};}));\r\n  },\r\n  async claimImportAssignment(input,{sourceRevision}={}){\r\n   // First capture/validate input through policy, without reading raw accessors.\r\n   const {generation,decision,...captured}=policy(()=>claimAssignment({heads:[],items:[],events:[]},input)).item;\r\n   if(Object.hasOwn(captured,'digestVersion')||Object.hasOwn(captured,'idempotencyKey'))invalid('Prepared import metadata requires prepareImportDecision.');\r\n   const claim={...captured,completed:true};\r\n   if(claim.sourceOwnerId!==ownerId||claim.sourceDeviceId!==deviceId)invalid('Import source scope does not match.');\r\n   return transact('Import assignment claim',[db.workouts,...tables],async()=>{\r\n    const before=await readImportLedger(db,ownerId,StorageError),result=policy(()=>claimAssignment(before,claim));\r\n    // Exact historical retries return the original item even if its source has\r\n    // since changed. The policy rejects any altered retry before this return.\r\n    if(result.duplicate)return {item:result.item,duplicate:true,status:assignmentStatus(result.state,result.item.claimId)};\r\n    if(typeof sourceRevision!=='string')invalid('A prepared source revision is required.');\r\n    const workout=await source(claim.clientWorkoutId);\r\n    if(JSON.stringify(workout)!==sourceRevision)invalid('Import source changed after preparation.');\r\n    const header={sourceOwnerId:ownerId,decisionId:claim.decisionId,sourceDeviceId:deviceId,kind:claim.kind,...(claim.kind==='import'?{targetAccountId:claim.targetAccountId,targetDataEpoch:claim.targetDataEpoch}:{})};\r\n    const stored=await db.guestHistoryDecisions.get([ownerId,claim.decisionId]);\r\n    const prior=stored?(({schemaVersion,...header})=>header)(stored):null;\r\n    if(prior&&JSON.stringify(prior)!==JSON.stringify(header))invalid('Decision identity cannot change its target or choice.');\r\n    if(!prior)await db.guestHistoryDecisions.add({...header,schemaVersion:1});\r\n    await persist(before,result.state);\r\n    return {item:result.item,duplicate:result.duplicate,status:assignmentStatus(result.state,result.item.claimId)};\r\n   });\r\n  },\r\n  // Only a future authenticated adapter may supply deletion evidence. Shape\r\n  // validation and IndexedDB transactions do not authenticate a server response.\r\n  async releaseImportAssignment(proof){return transition('Import assignment release',state=>releaseAssignment(state,proof));},\r\n  async recordImportAssignmentOutcome(callback){return transition('Import assignment outcome',state=>recordImportOutcome(state,callback));},\r\n  async listImportAssignments(){return readOperation('Import assignment history',()=>db.transaction('r',...tables,async()=>readImportLedger(db,ownerId,StorageError)));},\r\n });\r\n}\r\n","local-coach/import-preparation.mjs":"import {WorkoutImportCodecError,snapshotFromCompletedWorkout,prepareWorkoutImport} from '../workout-import-codec.mjs';\r\nimport {claimAssignment,assignmentStatus} from './import-assignment.mjs';\r\n\r\n// No network and no asynchronous crypto within an IndexedDB transaction.\r\n// Tokens are process-local capabilities, not serialized caller-supplied claims.\r\nexport function createImportPreparation({db,ownerId,deviceId,tables,source,persist,transact,read,policy,invalid,readOperation,StorageError}) {\r\n const tokens=new WeakMap(),same=(a,b)=>JSON.stringify(a)===JSON.stringify(b);\r\n const own=(v,k)=>Object.prototype.hasOwnProperty.call(v,k);\r\n const record=(raw,keys)=>{\r\n  if(!raw||typeof raw!=='object'||Array.isArray(raw)||![null,Object.prototype].includes(Object.getPrototypeOf(raw)))invalid('Expected own data fields.');\r\n  const descriptors=Object.getOwnPropertyDescriptors(raw);\r\n  if(Reflect.ownKeys(descriptors).length!==keys.length||keys.some(key=>!own(descriptors,key)||!own(descriptors[key],'value')||!descriptors[key].enumerable))invalid('Unexpected or accessor choice field.');\r\n  return Object.fromEntries(keys.map(key=>[key,descriptors[key].value]));\r\n };\r\n const id=value=>{if(typeof value!=='string'||!value||value.length>512||value.trim()!==value)invalid('Invalid choice identifier.');return value;};\r\n const captureUnsafe=raw=>{\r\n  // Inspect kind as data before selecting the exact supported input shape.\r\n  const d=raw&&Object.getOwnPropertyDescriptor(raw,'kind');\r\n  if(!d||!own(d,'value')||!['import','keep_local'].includes(d.value))invalid('Choose import or keep_local.');\r\n  const input=record(raw,['decisionId','kind','selections',...(d.value==='import'?['targetAccountId','targetDataEpoch']:[])]);\r\n  id(input.decisionId);\r\n  if(!Array.isArray(input.selections)||Object.getPrototypeOf(input.selections)!==Array.prototype||input.selections.length<1||input.selections.length>100)invalid('Select between one and 100 workouts.');\r\n  const descriptors=Object.getOwnPropertyDescriptors(input.selections),length=input.selections.length;\r\n  if(Reflect.ownKeys(descriptors).length!==length+1)invalid('Selections must be dense data.');\r\n  const selections=[];\r\n  for(let i=0;i<length;i++){\r\n   const descriptor=descriptors[i];if(!descriptor||!own(descriptor,'value'))invalid('Selections must be dense data.');\r\n   const entry=record(descriptor.value,['clientWorkoutId','claimId','itemId']);\r\n   for(const key of Object.keys(entry))id(entry[key]);\r\n   selections.push(entry);\r\n  }\r\n  selections.sort((a,b)=>a.clientWorkoutId<b.clientWorkoutId?-1:a.clientWorkoutId>b.clientWorkoutId?1:0);\r\n  if(new Set(selections.map(s=>s.clientWorkoutId)).size!==length||new Set(selections.flatMap(s=>[s.claimId,s.itemId])).size!==length*2)invalid('Choice selections and identities must be unique.');\r\n  if(input.kind==='import'){\r\n   id(input.targetAccountId);\r\n   if(!Number.isSafeInteger(input.targetDataEpoch)||input.targetDataEpoch<1)invalid('Invalid target epoch.');\r\n  }\r\n  return {sourceOwnerId:ownerId,decisionId:input.decisionId,sourceDeviceId:deviceId,kind:input.kind,\r\n   ...(input.kind==='import'?{targetAccountId:input.targetAccountId,targetDataEpoch:input.targetDataEpoch}:{}),preparationVersion:1,selections};\r\n };\r\n const capture=raw=>{try{return captureUnsafe(raw);}catch{invalid('Invalid explicit import choice.');}};\r\n const matching=(before,header)=>{\r\n  const existing=before.decisions.find(row=>row.decisionId===header.decisionId);\r\n  if(!existing)return null;\r\n  if(!same(existing,header))invalid('Decision identity cannot change its selection, target or choice.');\r\n  const items=before.items.filter(row=>row.decisionId===header.decisionId);\r\n  if(items.length!==header.selections.length||header.selections.some(s=>!items.some(item=>item.clientWorkoutId===s.clientWorkoutId&&item.claimId===s.claimId&&item.itemId===s.itemId&&item.kind===header.kind)))invalid('Stored decision is incomplete.');\r\n  return items;\r\n };\r\n const output=(state,items,duplicate)=>({items:items.map(item=>({item,status:assignmentStatus(state,item.claimId)})),duplicate});\r\n return Object.freeze({\r\n  async prepareImportDecision(input){\r\n   return readOperation('Import decision preparation',async()=>{\r\n   try{\r\n   const header=capture(input);\r\n   if(!ownerId.startsWith('guest:'))invalid('Only guest history can be selected.');\r\n   const prepared=await db.transaction('r',db.workouts,...tables,async()=>{\r\n    const before=await read(),prior=matching(before,header);\r\n    if(prior)return {header,retry:true};\r\n    const rows=[];\r\n    for(const selection of header.selections){\r\n     const workout=await source(selection.clientWorkoutId);\r\n     const head=before.heads.find(h=>h.clientWorkoutId===selection.clientWorkoutId)??null;\r\n     if(head&&head.status!=='released')invalid('Selected workout is already covered.');\r\n     rows.push({selection,workout,sourceRevision:JSON.stringify(workout),headRevision:JSON.stringify(head)});\r\n    }\r\n    return {header,rows};\r\n   });\r\n   if(!prepared.retry){\r\n    prepared.claims=[];\r\n    for(const row of prepared.rows){\r\n     const {selections,preparationVersion,...choice}=header;\r\n     const derived=header.kind==='import'?await prepareWorkoutImport(snapshotFromCompletedWorkout(row.workout),{targetAccountId:header.targetAccountId,targetDataEpoch:header.targetDataEpoch}):{};\r\n     prepared.claims.push({...choice,...row.selection,completed:true,...derived});\r\n    }\r\n   }\r\n   const token=Object.freeze({decisionId:header.decisionId,count:header.selections.length,kind:header.kind});\r\n   tokens.set(token,prepared);return token;\r\n   }catch(error){\r\n    if(error instanceof WorkoutImportCodecError)throw new StorageError(error.code,error.message,{cause:error,recoverable:true});\r\n    throw error;\r\n   }\r\n   });\r\n  },\r\n  async commitImportDecision(token){\r\n   const prepared=tokens.get(token);if(!prepared)invalid('Use a preparation token from this owner scope.');\r\n   return transact('Import decision',[db.workouts,...tables],async()=>{\r\n    const before=await read(),prior=matching(before,prepared.header);\r\n    if(prior)return output(before,prior,true);\r\n    if(prepared.retry)invalid('The prepared historical decision no longer exists.');\r\n    let state=before;const items=[];\r\n    for(let i=0;i<prepared.rows.length;i++){\r\n     const row=prepared.rows[i],workout=await source(row.selection.clientWorkoutId);\r\n     const head=before.heads.find(h=>h.clientWorkoutId===row.selection.clientWorkoutId)??null;\r\n     if(JSON.stringify(workout)!==row.sourceRevision||JSON.stringify(head)!==row.headRevision)invalid('Source or assignment changed after preparation.');\r\n     const result=policy(()=>claimAssignment(state,prepared.claims[i]));\r\n     if(result.duplicate)invalid('Decision has incomplete historical claims.');\r\n     state=result.state;items.push(result.item);\r\n    }\r\n    await db.guestHistoryDecisions.add({...prepared.header,schemaVersion:1});\r\n    await persist(before,state);\r\n    return output(state,items,false);\r\n   });\r\n  },\r\n });\r\n}\r\n","local-coach/import-sync.mjs":"import {IMPORT_UPLOAD_MANIFEST} from './import-upload-manifest.mjs';\r\nconst terminal=new Set(['imported','target_deleted','conflict','rejected','keep_local']);\r\nconst own=(v,k)=>Object.prototype.hasOwnProperty.call(v,k);\r\nconst safeRecord=value=>{\r\n if(!value||typeof value!=='object'||Array.isArray(value))throw Error('Invalid response');\r\n const descriptors=Object.getOwnPropertyDescriptors(value),out={};\r\n for(const key of Reflect.ownKeys(descriptors)){const d=descriptors[key];if(typeof key!=='string'||!own(d,'value')||!d.enumerable)throw Error('Invalid response');Object.defineProperty(out,key,{value:d.value,enumerable:true});}\r\n return out;\r\n};\r\nfunction success(reply,item){\r\n try{\r\n  if(![200,201].includes(reply.status))return false;\r\n  const body=safeRecord(reply.body),receipt=safeRecord(body.receipt),workout=safeRecord(body.workout);\r\n  const keys=['targetAccountId','targetDataEpoch','digestVersion','fingerprint','idempotencyKey'];\r\n  if(Object.keys(receipt).length!==keys.length||keys.some(key=>receipt[key]!==item[key]))return false;\r\n  if(typeof workout.id!=='string'||!workout.id||workout.id.length>512||workout.user_id!==item.targetAccountId||workout.client_workout_id!==item.clientWorkoutId||workout.source!=='guest_import'||workout.competitive_status!=='not_eligible')return false;\r\n  const fields={mode:'mode',goal:'goal',started_at:'startedAt',completed_at:'completedAt',value:'value',active:'activeSeconds'};\r\n  return Object.entries(fields).every(([key,source])=>workout[key]===item.snapshot[source])?workout.id:null;\r\n }catch{return false;}\r\n}\r\nconst abortError=()=>Object.assign(Error('Import run interrupted.'),{code:'interrupted'});\r\nconst sleep=(ms,signal)=>new Promise((resolve,reject)=>{\r\n if(signal.aborted){reject(abortError());return;}\r\n const end=()=>{clearTimeout(timer);signal.removeEventListener('abort',abort);resolve();};\r\n const abort=()=>{clearTimeout(timer);signal.removeEventListener('abort',abort);reject(abortError());};\r\n const timer=setTimeout(end,ms);signal.addEventListener('abort',abort,{once:true});\r\n});\r\n// loadAccount must authenticate and bypass caches; postImport is an injected\r\n// transport, not global fetch. Both must honor signal, but a deadline also races\r\n// their full promise so hung auth acquisition cannot hold this run indefinitely.\r\nexport function createImportDrain({repository,loadAccount,postImport,transition,\r\n manifest=IMPORT_UPLOAD_MANIFEST,now=()=>Date.now(),randomUUID=()=>crypto.randomUUID(),\r\n wait=sleep,random=Math.random,requestTimeoutMs=8000,maxAttemptsPerItem=3,maxItems=100,maxInlineDelayMs=1000}={}){\r\n if(!Number.isSafeInteger(requestTimeoutMs)||requestTimeoutMs<1||requestTimeoutMs>10000||!Number.isSafeInteger(maxAttemptsPerItem)||maxAttemptsPerItem<1||maxAttemptsPerItem>5||!Number.isSafeInteger(maxItems)||maxItems<1||maxItems>100||!Number.isSafeInteger(maxInlineDelayMs)||maxInlineDelayMs<0||maxInlineDelayMs>5000)throw Error('Invalid import run limits.');\r\n let running=false,active=null;\r\n const enabled=()=>manifest?.version===1&&manifest?.uploadsEnabled===true;\r\n async function bounded(fn,signal){\r\n  const controller=new AbortController();let timer,onAbort;\r\n  const stopped=new Promise((_,reject)=>{\r\n   onAbort=()=>{controller.abort();reject(abortError());};\r\n   if(signal.aborted){onAbort();return;}\r\n   signal.addEventListener('abort',onAbort,{once:true});\r\n   timer=setTimeout(()=>{controller.abort();reject(Object.assign(Error('Import request timed out.'),{code:'timeout'}));},requestTimeoutMs);\r\n  });\r\n  try{return await Promise.race([Promise.resolve().then(()=>{if(signal.aborted)throw abortError();return fn(controller.signal);}),stopped]);}\r\n  finally{clearTimeout(timer);signal.removeEventListener('abort',onAbort);}\r\n }\r\n const retryDelay=(reply,attempt)=>{\r\n  let seconds;try{const raw=reply?.retryAfter;if(typeof raw==='string'){if(/^\\d+(?:\\.\\d+)?$/.test(raw))seconds=Number(raw);else{const deadline=Date.parse(raw);if(Number.isFinite(deadline))seconds=Math.max(0,(deadline-now())/1000);}}}catch{}\r\n  if(Number.isFinite(seconds))return Math.min(86400000,Math.max(0,Math.ceil(seconds*1000)));\r\n  return Math.min(30000,Math.ceil(250*2**attempt*(1+Math.min(1,Math.max(0,random())))));\r\n };\r\n return Object.freeze({\r\n  stop(){active?.abort();},\r\n  async run({targetAccountId,resumeParkedClaimIds=[]}={}){\r\n   if(!enabled())return {stopped:'disabled',posted:0,imported:0};\r\n   if(running)return {stopped:'busy',posted:0,imported:0};\r\n   if(typeof targetAccountId!=='string'||!targetAccountId||!Array.isArray(resumeParkedClaimIds)||resumeParkedClaimIds.some(id=>typeof id!=='string'))throw Error('Choose an immutable target and explicit resume claims.');\r\n   running=true;const controller=new AbortController();active=controller;\r\n   const summary={posted:0,imported:0,parked:0,retryAt:null,stopped:'complete'};\r\n   let unsubscribe=()=>{},runTicket;\r\n   const current=ticket=>!controller.signal.aborted&&enabled()&&transition.isCurrent(runTicket)&&transition.isCurrent(ticket);\r\n   try{\r\n    try{runTicket=transition.capture();unsubscribe=transition.subscribe(()=>controller.abort());if(!transition.isCurrent(runTicket))throw abortError();}catch{summary.stopped='identity_unavailable';return summary;}\r\n    const ledger=await repository.listImportAssignments();\r\n    const candidates=ledger.items.filter(item=>item.kind==='import'&&item.targetAccountId===targetAccountId&&[1,2].includes(item.digestVersion)&&typeof item.idempotencyKey==='string');\r\n    const resume=new Set(resumeParkedClaimIds);let processedItems=0;\r\n    for(const selected of candidates){\r\n     if(!current(runTicket)){summary.stopped='identity_changed';break;}\r\n     let projection=await repository.importAttemptState(selected.claimId,now());\r\n     if(terminal.has(projection.status)||projection.status==='in_flight'||projection.retryAt>now()||projection.status==='parked'&&!resume.has(selected.claimId))continue;\r\n     if(processedItems++>=maxItems)break;\r\n     for(let count=0;count<maxAttemptsPerItem;count++){\r\n      if(!current(runTicket)){summary.stopped='identity_changed';return summary;}\r\n      projection=await repository.importAttemptState(selected.claimId,now());\r\n      if(terminal.has(projection.status)||projection.status==='in_flight'||projection.retryAt>now()||projection.status==='parked'&&(count>0||!resume.has(selected.claimId)))break;\r\n      let ticket;try{ticket=transition.capture();}catch{summary.stopped='identity_unavailable';return summary;}\r\n      let account,preflightCategory,preflightFailure;\r\n      try{account=await bounded(signal=>loadAccount({signal,cache:'no-store'}),controller.signal);}\r\n      catch(error){if(!current(ticket)){summary.stopped='identity_changed';return summary;}if(error?.status===401)preflightCategory='signed_out';else preflightFailure=error?.code==='timeout'?'timeout':'offline';}\r\n      if(!current(ticket)){summary.stopped='identity_changed';return summary;}\r\n      if(!preflightCategory&&!preflightFailure){\r\n       try{const value=safeRecord(account),user=safeRecord(value.user);if(user.id!==selected.targetAccountId)preflightCategory='target_mismatch';else if(!Number.isSafeInteger(value.dataEpoch)||value.dataEpoch<1)preflightFailure='unknown_response';else if(value.dataEpoch!==selected.targetDataEpoch)preflightCategory='epoch_mismatch';}\r\n       catch{preflightFailure='unknown_response';}\r\n      }\r\n      const startedAt=now(),attemptId=randomUUID();let attempt;\r\n      try{attempt=await repository.beginImportAttempt({claimId:selected.claimId,generation:selected.generation,attemptId,startedAt},{resumeParked:count===0&&resume.has(selected.claimId),attemptLimit:maxAttemptsPerItem});}\r\n      catch{summary.stopped='storage_or_contention';return summary;}\r\n      if(attempt.duplicate||attempt.status!=='in_flight')break;\r\n      const item=attempt.item;\r\n      const observe=details=>repository.recordImportAttemptResult({claimId:item.claimId,generation:item.generation,attemptId,at:Math.max(startedAt,now()),...details});\r\n      if(!current(ticket)){await observe({type:'parked',category:'target_mismatch'});summary.parked++;summary.stopped='identity_changed';return summary;}\r\n      if(preflightCategory){await observe({type:'parked',category:preflightCategory});summary.parked++;summary.stopped=preflightCategory;return summary;}\r\n      let reply,errorCategory=preflightFailure;\r\n      if(!preflightFailure){\r\n       // Nothing after this equality check can choose a different target/body.\r\n       const request=Object.freeze({path:'/api/workouts/import',method:'POST',cache:'no-store',headers:Object.freeze({'Content-Type':'application/json','X-Target-Account':item.targetAccountId,'Idempotency-Key':item.idempotencyKey}),body:JSON.stringify({digestVersion:item.digestVersion,targetDataEpoch:item.targetDataEpoch,snapshot:item.snapshot,fingerprint:item.fingerprint})});\r\n       try{summary.posted++;reply=safeRecord(await bounded(signal=>postImport({...request,signal}),controller.signal));}\r\n       catch(error){errorCategory=error?.code==='timeout'?'timeout':'offline';}\r\n      }\r\n      if(!current(ticket)){await observe({type:'parked',category:'target_mismatch'});summary.parked++;summary.stopped='identity_changed';return summary;}\r\n      const serverWorkoutId=success(reply??{},item);\r\n      if(serverWorkoutId){await observe({type:'imported',httpStatus:reply.status,serverWorkoutId});summary.imported++;break;}\r\n      let status=reply?.status,code;try{code=safeRecord(reply.body).code;}catch{}\r\n      if(!Number.isInteger(status)||status<100||status>599)status=undefined;\r\n      const withStatus=status===undefined?{}:{httpStatus:status};\r\n      const park=status===401?'signed_out':status===404?'feature_disabled':status===409&&['target_mismatch','target_account_mismatch'].includes(code)?'target_mismatch':status===409&&code==='target_epoch_mismatch'?'epoch_mismatch':null;\r\n      if(park){await observe({type:'parked',category:park,...withStatus});summary.parked++;summary.stopped=park;return summary;}\r\n      if(status===409&&code==='fingerprint_conflict'){await observe({type:'conflict',category:'fingerprint_conflict',...withStatus});break;}\r\n      const rejectionCodes=['invalid_workout_import','invalid_snapshot','invalid_key_input','invalid_idempotency_key','invalid_fingerprint','unsupported_digest_version'];\r\n      if([403,422].includes(status)&&code==='policy_rejected'){await observe({type:'rejected',category:'policy_rejected',...withStatus});break;}\r\n      if([400,422].includes(status)&&rejectionCodes.includes(code)){await observe({type:'rejected',category:'invalid_payload',...withStatus});break;}\r\n      if(attempt.attemptNumber>=attempt.attemptLimit){await observe({type:'parked',category:'retry_exhausted',...withStatus});summary.parked++;break;}\r\n      const delay=retryDelay(reply,attempt.attemptNumber-1),retryAt=Math.max(startedAt,now())+delay;\r\n      await observe({type:'retryable_failure',category:errorCategory??(status===429?'rate_limited':status>=500?'server_unavailable':'unknown_response'),retryAt,...withStatus});\r\n      summary.retryAt=summary.retryAt===null?retryAt:Math.min(summary.retryAt,retryAt);\r\n      if(delay>maxInlineDelayMs)break;\r\n      try{await wait(delay,controller.signal);}catch{summary.stopped='identity_changed';return summary;}\r\n     }\r\n    }\r\n    return summary;\r\n   }finally{unsubscribe();if(active===controller)active=null;running=false;}\r\n  },\r\n });\r\n}\r\n","local-coach/import-upload-manifest.mjs":"// No production caller enables uploads. Synthetic tests may inject an explicit\r\n// version-1 manifest; activation requires a separate reviewed integration.\r\nexport const IMPORT_UPLOAD_MANIFEST=Object.freeze({version:1,uploadsEnabled:false});\r\n","local-coach/manual-clock.mjs":"export class ManualActiveClock{\r\n constructor({now=()=>performance.now(),visible=()=>!document.hidden}={}){this.now=now;this.visible=visible;this.elapsed=0;this.last=null;this.running=false;}\r\n start(elapsed=0){this.elapsed=Math.max(0,Number(elapsed)||0);this.last=this.now();this.running=true;return this.elapsed;}\r\n sample(){const current=this.now();if(this.running&&this.visible()&&this.last!==null)this.elapsed+=Math.max(0,(current-this.last)/1000);this.last=current;return this.elapsed;}\r\n pause(){this.sample();this.running=false;return this.elapsed;}\r\n resume(){this.last=this.now();this.running=true;return this.elapsed;}\r\n}\r\n\r\nexport class ManualStartGate{\r\n constructor(){this.pending=Promise.resolve();this.pausePending=null;}\r\n run(task){this.pausePending=null;this.pending=Promise.resolve().then(task);return this.pending;}\r\n pause(task){this.pausePending??=this.pending.then(task);return this.pausePending;}\r\n}\r\n","local-coach/repository.mjs":"import Dexie from 'dexie';\r\nimport {z} from 'zod';\r\nimport {IMPORT_LEDGER_STORES,importLedgerTables,readImportLedger,deleteImportLedger,createImportLedger} from './import-ledger.mjs';\r\nimport {EXERCISES,validateOnboarding} from '../onboarding-domain.mjs';\r\n\r\n// Tuning knob: slow first launches (cold IndexedDB, throttled CPU, parallel tabs) need well over 3 s to open. Real failures (VersionError, blocked upgrade, quota) still reject immediately or at this deadline.\r\nexport const LOCAL_COACH_OPEN_TIMEOUT_MS=15000;\r\nexport const LOCAL_COACH_SCHEMA_VERSION=1;\r\nexport const LOCAL_COACH_LAYOUT_VERSION=2;\r\nexport const LOCAL_COACH_EXPORT_VERSION=1;\r\nexport const DEFAULT_LOCAL_COACH_DB='myr5-local-coach';\r\n\r\nconst TimestampSchema=z.number().int().nonnegative();\r\nconst OwnerIdSchema=z.string().min(1).max(512).regex(/^(guest|account):\\S+$/,'Owner must have a non-empty guest or account scope.');\r\nconst DeviceIdSchema=z.string().min(1).max(128);\r\nconst IdentifierSchema=z.string().min(1).max(128);\r\nconst LeaseTokenSchema=z.string().min(1).max(512);\r\nconst JsonValueSchema=z.lazy(()=>z.union([z.string(),z.number().finite(),z.boolean(),z.null(),z.array(JsonValueSchema),z.record(z.string(),JsonValueSchema)]));\r\nconst JsonObjectSchema=z.record(z.string(),JsonValueSchema).refine(value=>JSON.stringify(value).length<=131072,'JSON object exceeds the 128 KiB local-record limit.');\r\nconst IntakeSchema=z.unknown().transform((value,context)=>{\r\n try{return validateOnboarding(value);}catch(error){context.addIssue({code:'custom',message:error instanceof Error?error.message:'Invalid coach setup.'});return z.NEVER;}\r\n});\r\nconst SettingsSchema=JsonObjectSchema;\r\nconst StartDaySchema=z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/,'Start day must be a local calendar date.');\r\nconst WorkoutStatusSchema=z.enum(['active','paused','completed','interrupted']);\r\nconst ModeSchema=z.string().min(1).max(80);\r\nconst WorkoutStartSchema=z.object({\r\n clientWorkoutId:IdentifierSchema.optional(),\r\n mode:ModeSchema,\r\n goal:z.number().int().min(1).max(100000),\r\n restSeconds:z.number().int().min(15).max(180).default(60),\r\n progress:JsonObjectSchema.default({}),\r\n metadata:JsonObjectSchema.default({}),\r\n});\r\nconst WorkoutPatchSchema=z.object({\r\n progress:JsonObjectSchema.optional(),\r\n metadata:JsonObjectSchema.optional(),\r\n});\r\nconst CompletionSchema=z.object({\r\n value:z.number().finite().min(0).max(100000),\r\n activeSeconds:z.number().finite().min(0).max(7200).optional(),\r\n elapsedSeconds:z.number().finite().min(0).max(7200).optional(),\r\n earned:z.boolean().optional(),\r\n progress:JsonObjectSchema.optional(),\r\n metadata:JsonObjectSchema.optional(),\r\n});\r\n\r\nconst MetaRecordSchema=z.object({key:z.string(),ownerId:OwnerIdSchema.optional(),value:JsonValueSchema,updatedAt:TimestampSchema,schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION)});\r\nconst IntakeRecordSchema=z.object({ownerId:OwnerIdSchema,deviceId:DeviceIdSchema,value:IntakeSchema,updatedAt:TimestampSchema,schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION)});\r\nconst SettingsRecordSchema=z.object({ownerId:OwnerIdSchema,deviceId:DeviceIdSchema,value:SettingsSchema,updatedAt:TimestampSchema,schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION)});\r\nconst WorkoutRecordSchema=z.object({\r\n id:IdentifierSchema,\r\n clientWorkoutId:IdentifierSchema,\r\n ownerId:OwnerIdSchema,\r\n deviceId:DeviceIdSchema,\r\n mode:ModeSchema,\r\n goal:z.number().int().min(1).max(100000),\r\n restSeconds:z.number().int().min(15).max(180),\r\n status:WorkoutStatusSchema,\r\n progress:JsonObjectSchema,\r\n metadata:JsonObjectSchema,\r\n completion:CompletionSchema.optional(),\r\n startedAt:TimestampSchema,\r\n updatedAt:TimestampSchema,\r\n completedAt:TimestampSchema.optional(),\r\n interruptedAt:TimestampSchema.optional(),\r\n schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION),\r\n});\r\nconst EventRecordSchema=z.object({id:IdentifierSchema,workoutId:IdentifierSchema,ownerId:OwnerIdSchema,deviceId:DeviceIdSchema,sequence:z.number().int().positive(),type:z.enum(['started','updated','paused','resumed','completed','interrupted']),payload:JsonObjectSchema,createdAt:TimestampSchema,schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION)});\r\nconst OutboxRecordSchema=z.object({id:IdentifierSchema,workoutId:IdentifierSchema,clientWorkoutId:IdentifierSchema,ownerId:OwnerIdSchema,deviceId:DeviceIdSchema,type:z.literal('workout.completed'),state:z.enum(['pending','sent','rejected']),payload:JsonObjectSchema,createdAt:TimestampSchema,updatedAt:TimestampSchema,schemaVersion:z.literal(LOCAL_COACH_SCHEMA_VERSION)});\r\nconst LegacyPendingSetSchema=z.object({id:z.union([z.string(),z.number()]),value:z.number().finite().min(0).max(100000),active:z.number().finite().min(0).max(7200).optional()}).passthrough();\r\nconst LegacyMigrationSchema=z.object({sourceKey:z.string().min(1).max(512),intake:z.unknown().optional(),settings:SettingsSchema.optional(),pendingSets:z.array(LegacyPendingSetSchema).max(10000).optional()});\r\n\r\nconst parse=(schema,value,label)=>{\r\n const result=schema.safeParse(value);\r\n if(result.success)return result.data;\r\n throw new LocalCoachStorageError('invalid-record',`${label} failed validation.`,{cause:result.error,recoverable:true});\r\n};\r\n\r\nexport class LocalCoachStorageError extends Error{\r\n constructor(code,message,{cause,recoverable=false}={}){super(message,{cause});this.name='LocalCoachStorageError';this.code=code;this.recoverable=recoverable;}\r\n}\r\n\r\nexport function classifyLocalCoachError(error,operation='Local coach storage'){\r\n if(error instanceof LocalCoachStorageError)return error;\r\n const name=String(error?.name??'');\r\n if(name==='QuotaExceededError')return new LocalCoachStorageError('quota',`${operation} could not be saved because device storage is full.`,{cause:error,recoverable:true});\r\n if(name==='VersionError'||name==='UpgradeError')return new LocalCoachStorageError('migration',`${operation} could not open the local coach database.`,{cause:error,recoverable:true});\r\n if(name==='AbortError'||name==='TransactionInactiveError')return new LocalCoachStorageError('interrupted-write',`${operation} was interrupted and was not committed.`,{cause:error,recoverable:true});\r\n return new LocalCoachStorageError('storage',`${operation} failed.`,{cause:error,recoverable:true});\r\n}\r\n\r\nfunction createId(cryptoObject){\r\n if(typeof cryptoObject?.randomUUID==='function')return cryptoObject.randomUUID();\r\n throw new LocalCoachStorageError('missing-randomness','Secure local identifiers are unavailable.',{recoverable:false});\r\n}\r\n\r\nexport async function openLocalCoach({\r\n name=DEFAULT_LOCAL_COACH_DB,\r\n openTimeoutMs=LOCAL_COACH_OPEN_TIMEOUT_MS,\r\n indexedDB=globalThis.indexedDB,\r\n IDBKeyRange=globalThis.IDBKeyRange,\r\n cryptoObject=globalThis.crypto,\r\n now=()=>Date.now(),\r\n exerciseKeys=Object.keys(EXERCISES),\r\n}={}){\r\n if(!Number.isSafeInteger(openTimeoutMs)||openTimeoutMs<1||openTimeoutMs>60000)throw new LocalCoachStorageError('invalid-record','Local storage open timeout must be between 1 and 60000 milliseconds.',{recoverable:true});\r\n if(!indexedDB||!IDBKeyRange)throw new LocalCoachStorageError('unavailable','Durable local storage is unavailable in this browser.',{recoverable:true});\r\n const modes=new Set(exerciseKeys);\r\n const db=new Dexie(name,{indexedDB,IDBKeyRange});\r\n db.version(1).stores({\r\n  meta:'&key,ownerId,updatedAt',\r\n  intake:'&ownerId,deviceId,updatedAt',\r\n  settings:'&ownerId,deviceId,updatedAt',\r\n  workouts:'&id,&clientWorkoutId,ownerId,[ownerId+status],[ownerId+startedAt],status,updatedAt',\r\n  workoutEvents:'&id,workoutId,[ownerId+workoutId],[workoutId+sequence],ownerId,createdAt',\r\n  outbox:'&id,&workoutId,ownerId,[ownerId+state],state,updatedAt',\r\n });\r\n db.version(LOCAL_COACH_LAYOUT_VERSION).stores(IMPORT_LEDGER_STORES);\r\n let openExpired=false,openTimer;\r\n const openDeadlineError=new LocalCoachStorageError('migration','Local storage is taking longer than expected. Retry; close other app tabs if upgrade is blocked.',{recoverable:true});\r\n try{\r\n  await Promise.race([\r\n   db.open().then(()=>{if(openExpired){db.close({disableAutoOpen:true});throw openDeadlineError;}}),\r\n   new Promise((resolve,reject)=>{openTimer=setTimeout(()=>{openExpired=true;reject(openDeadlineError);db.close({disableAutoOpen:true});},openTimeoutMs);}),\r\n  ]);\r\n }catch(error){db.close({disableAutoOpen:true});throw classifyLocalCoachError(error,'Local coach database');}\r\n finally{clearTimeout(openTimer);}\r\n\r\n const readMeta=async key=>{\r\n  const row=await db.meta.get(key);\r\n  return row?parse(MetaRecordSchema,row,'Local metadata'):null;\r\n };\r\n const ensureIdentity=async()=>{\r\n  try{return await db.transaction('rw',db.meta,async()=>{\r\n   const existingDevice=await readMeta('device-id');\r\n   const existingGuest=await readMeta('guest-owner-id');\r\n   const timestamp=now();\r\n   const deviceId=existingDevice?.value??createId(cryptoObject);\r\n   const guestOwnerId=existingGuest?.value??`guest:${createId(cryptoObject)}`;\r\n   parse(DeviceIdSchema,deviceId,'Device identifier');parse(OwnerIdSchema,guestOwnerId,'Guest owner identifier');\r\n   if(!existingDevice)await db.meta.add(parse(MetaRecordSchema,{key:'device-id',value:deviceId,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Device metadata'));\r\n   if(!existingGuest)await db.meta.add(parse(MetaRecordSchema,{key:'guest-owner-id',ownerId:guestOwnerId,value:guestOwnerId,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Guest metadata'));\r\n   return {deviceId,guestOwnerId};\r\n  });}catch(error){throw classifyLocalCoachError(error,'Local coach identity');}\r\n };\r\n const identity=await ensureIdentity();\r\n\r\n const appendEvent=async(workout,type,payload={})=>{\r\n  const sequence=await db.workoutEvents.where('workoutId').equals(workout.id).count()+1;\r\n  const event=parse(EventRecordSchema,{id:createId(cryptoObject),workoutId:workout.id,ownerId:workout.ownerId,deviceId:workout.deviceId,sequence,type,payload,createdAt:now(),schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Workout event');\r\n  await db.workoutEvents.add(event);return event;\r\n };\r\n\r\n const forOwner=ownerValue=>{\r\n  const ownerId=parse(OwnerIdSchema,ownerValue,'Owner identifier');\r\n  const leaseKey=`workout-lease:${JSON.stringify([ownerId,identity.deviceId])}`;\r\n  const getWorkout=async id=>{\r\n   const row=await db.workouts.get(parse(IdentifierSchema,id,'Workout identifier'));\r\n   if(!row||row.ownerId!==ownerId)return null;\r\n   return parse(WorkoutRecordSchema,row,'Workout');\r\n  };\r\n  const transact=async(operation,tables,fn)=>{\r\n   try{return await db.transaction('rw',...tables,fn);}catch(error){throw classifyLocalCoachError(error,operation);}\r\n  };\r\n  const assertWorkoutLease=async token=>{if(token===undefined)return;const expected=parse(LeaseTokenSchema,token,'Workout lease token'),row=await db.meta.get(leaseKey),lease=row?parse(MetaRecordSchema,row,'Workout lease record').value:null;if(!lease||lease.token!==expected)throw new LocalCoachStorageError('lease','This workout is active in another tab.',{recoverable:true});};\r\n  return Object.freeze({\r\n   ...createImportLedger({db,ownerId,deviceId:identity.deviceId,getWorkout,transact,StorageError:LocalCoachStorageError,classifyError:classifyLocalCoachError}),\r\n   ownerId,\r\n   deviceId:identity.deviceId,\r\n   async claimWorkoutLease(token){const value={token:parse(LeaseTokenSchema,token,'Workout lease token')};return transact('Workout lease claim',[db.meta],async()=>{const row=parse(MetaRecordSchema,{key:leaseKey,ownerId,value,updatedAt:now(),schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Workout lease record');await db.meta.put(row);return value;});},\r\n   async saveIntake(value){\r\n    const intake=parse(IntakeSchema,value,'Coach intake'),timestamp=now();\r\n    const row=parse(IntakeRecordSchema,{ownerId,deviceId:identity.deviceId,value:intake,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach intake record');\r\n    try{await db.intake.put(row);return row.value;}catch(error){throw classifyLocalCoachError(error,'Coach intake');}\r\n   },\r\n   async getIntake(){try{const row=await db.intake.get(ownerId);return row?parse(IntakeRecordSchema,row,'Coach intake record').value:null;}catch(error){throw classifyLocalCoachError(error,'Coach intake');}},\r\n   async saveSettings(value){\r\n    const settings=parse(SettingsSchema,value,'Coach settings'),timestamp=now();\r\n    const row=parse(SettingsRecordSchema,{ownerId,deviceId:identity.deviceId,value:settings,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach settings record');\r\n    try{await db.settings.put(row);return row.value;}catch(error){throw classifyLocalCoachError(error,'Coach settings');}\r\n   },\r\n    async getSettings(){try{const row=await db.settings.get(ownerId);return row?parse(SettingsRecordSchema,row,'Coach settings record').value:null;}catch(error){throw classifyLocalCoachError(error,'Coach settings');}},\r\n    async saveSetup(value,{startDay}={}){\r\n     const intake=parse(IntakeSchema,value,'Coach intake'),requestedDay=parse(StartDaySchema,startDay,'Coach start day');\r\n     return transact('Coach setup',[db.intake,db.settings],async()=>{\r\n      const timestamp=now(),existing=await db.settings.get(ownerId),prior=existing?parse(SettingsRecordSchema,existing,'Coach settings record').value:{};\r\n      const settings=parse(SettingsSchema,{...prior,startDay:prior.startDay??requestedDay},'Coach settings');\r\n      const intakeRow=parse(IntakeRecordSchema,{ownerId,deviceId:identity.deviceId,value:intake,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach intake record');\r\n      const settingsRow=parse(SettingsRecordSchema,{ownerId,deviceId:identity.deviceId,value:settings,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach settings record');\r\n      await db.intake.put(intakeRow);await db.settings.put(settingsRow);return {intake:intakeRow.value,settings:settingsRow.value};\r\n     });\r\n    },\r\n   async startWorkout(input,{leaseToken}={}){\r\n    const parsed=parse(WorkoutStartSchema,input,'Workout start');\r\n    if(!modes.has(parsed.mode))throw new LocalCoachStorageError('invalid-record','Workout mode is not supported.',{recoverable:true});\r\n    // The client identifier is the durable local primary key by design. The\r\n    // server receives this same immutable value for idempotent reconciliation.\r\n    const timestamp=now(),id=parsed.clientWorkoutId??createId(cryptoObject);\r\n    const row=parse(WorkoutRecordSchema,{id,clientWorkoutId:id,ownerId,deviceId:identity.deviceId,mode:parsed.mode,goal:parsed.goal,restSeconds:parsed.restSeconds,status:'active',progress:parsed.progress,metadata:parsed.metadata,startedAt:timestamp,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Workout');\r\n    return transact('Workout start',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);await db.workouts.add(row);await appendEvent(row,'started',{mode:row.mode,goal:row.goal,restSeconds:row.restSeconds});return row;});\r\n   },\r\n   async updateWorkout(id,patch,{leaseToken}={}){\r\n    const parsed=parse(WorkoutPatchSchema,patch,'Workout update');\r\n    return transact('Workout update',[db.meta,db.workouts,db.workoutEvents],async()=>{\r\n     await assertWorkoutLease(leaseToken);\r\n     const current=await getWorkout(id);if(!current)throw new LocalCoachStorageError('not-found','Workout was not found for this owner.',{recoverable:true});\r\n     if(current.status==='completed')throw new LocalCoachStorageError('already-completed','Completed workouts cannot be changed.',{recoverable:true});\r\n     const updated=parse(WorkoutRecordSchema,{...current,...parsed,updatedAt:now()},'Workout');\r\n     await db.workouts.put(updated);await appendEvent(updated,'updated',parsed);return updated;\r\n    });\r\n   },\r\n   async pauseWorkout(id,{leaseToken}={}){return transact('Workout pause',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);const current=await getWorkout(id);if(!current)throw new LocalCoachStorageError('not-found','Workout was not found for this owner.',{recoverable:true});if(current.status!=='active')return current;const updated=parse(WorkoutRecordSchema,{...current,status:'paused',updatedAt:now()},'Workout');await db.workouts.put(updated);await appendEvent(updated,'paused');return updated;});},\r\n   async resumeWorkout(id,{leaseToken}={}){return transact('Workout resume',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);const current=await getWorkout(id);if(!current)throw new LocalCoachStorageError('not-found','Workout was not found for this owner.',{recoverable:true});if(!['paused','interrupted'].includes(current.status))return current;const {interruptedAt:_interruptedAt,...resumable}=current;const updated=parse(WorkoutRecordSchema,{...resumable,status:'active',updatedAt:now()},'Workout');await db.workouts.put(updated);await appendEvent(updated,'resumed');return updated;});},\r\n   async completeWorkout(id,completion,{queueForSync=true,leaseToken}={}){\r\n    const parsed=parse(CompletionSchema,completion,'Workout completion');\r\n    return transact('Workout completion',[db.meta,db.workouts,db.workoutEvents,db.outbox],async()=>{\r\n     await assertWorkoutLease(leaseToken);\r\n     const current=await getWorkout(id);if(!current)throw new LocalCoachStorageError('not-found','Workout was not found for this owner.',{recoverable:true});\r\n     if(current.status==='completed'){const existing=await db.outbox.where('workoutId').equals(current.id).first();return {workout:current,outbox:existing?parse(OutboxRecordSchema,existing,'Sync outbox record'):null,duplicate:true};}\r\n     const timestamp=now();\r\n     const updated=parse(WorkoutRecordSchema,{...current,status:'completed',completion:parsed,progress:parsed.progress??current.progress,metadata:parsed.metadata??current.metadata,completedAt:timestamp,updatedAt:timestamp},'Workout');\r\n     await db.workouts.put(updated);await appendEvent(updated,'completed',{value:parsed.value,activeSeconds:parsed.activeSeconds??null,elapsedSeconds:parsed.elapsedSeconds??null,earned:parsed.earned??null});\r\n     let outbox=null;\r\n     if(queueForSync){outbox=parse(OutboxRecordSchema,{id:createId(cryptoObject),workoutId:updated.id,clientWorkoutId:updated.clientWorkoutId,ownerId,deviceId:identity.deviceId,type:'workout.completed',state:'pending',payload:{workout:updated},createdAt:timestamp,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Sync outbox record');await db.outbox.add(outbox);}\r\n     return {workout:updated,outbox,duplicate:false};\r\n    });\r\n   },\r\n    async recoverInterruptedWorkouts({leaseToken}={}){return transact('Workout recovery',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);const rows=await db.workouts.where('[ownerId+status]').anyOf([[ownerId,'active'],[ownerId,'paused']]).toArray();const recovered=[];for(const raw of rows){const current=parse(WorkoutRecordSchema,raw,'Workout'),timestamp=now(),updated=parse(WorkoutRecordSchema,{...current,status:'interrupted',interruptedAt:timestamp,updatedAt:timestamp},'Workout');await db.workouts.put(updated);await appendEvent(updated,'interrupted',{previousStatus:current.status});recovered.push(updated);}return recovered;});},\r\n    async interruptActiveWorkouts({leaseToken}={}){return transact('Active workout recovery',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);const rows=await db.workouts.where('[ownerId+status]').equals([ownerId,'active']).toArray(),recovered=[];for(const raw of rows){const current=parse(WorkoutRecordSchema,raw,'Workout'),timestamp=now(),updated=parse(WorkoutRecordSchema,{...current,status:'interrupted',interruptedAt:timestamp,updatedAt:timestamp},'Workout');await db.workouts.put(updated);await appendEvent(updated,'interrupted',{previousStatus:current.status});recovered.push(updated);}return recovered;});},\r\n    async interruptWorkout(id,{leaseToken}={}){return transact('Workout interruption',[db.meta,db.workouts,db.workoutEvents],async()=>{await assertWorkoutLease(leaseToken);const current=await getWorkout(id);if(!current)throw new LocalCoachStorageError('not-found','Workout was not found for this owner.',{recoverable:true});if(current.status!=='active')return current;const timestamp=now(),updated=parse(WorkoutRecordSchema,{...current,status:'interrupted',interruptedAt:timestamp,updatedAt:timestamp},'Workout');await db.workouts.put(updated);await appendEvent(updated,'interrupted',{previousStatus:current.status});return updated;});},\r\n   async getWorkout(id){try{return await getWorkout(id);}catch(error){throw classifyLocalCoachError(error,'Workout read');}},\r\n   async listWorkouts(){try{return (await db.workouts.where('ownerId').equals(ownerId).sortBy('startedAt')).map(row=>parse(WorkoutRecordSchema,row,'Workout'));}catch(error){throw classifyLocalCoachError(error,'Workout history');}},\r\n   async listEvents(workoutId){try{return (await db.workoutEvents.where('[ownerId+workoutId]').equals([ownerId,workoutId]).sortBy('sequence')).map(row=>parse(EventRecordSchema,row,'Workout event'));}catch(error){throw classifyLocalCoachError(error,'Workout events');}},\r\n   async listOutbox({state}={}){try{const rows=state?await db.outbox.where('[ownerId+state]').equals([ownerId,state]).toArray():await db.outbox.where('ownerId').equals(ownerId).toArray();return rows.map(row=>parse(OutboxRecordSchema,row,'Sync outbox record')).sort((a,b)=>a.createdAt-b.createdAt||a.id.localeCompare(b.id));}catch(error){throw classifyLocalCoachError(error,'Sync outbox');}},\r\n   async markOutbox(id,state){\r\n    const next=parse(z.enum(['sent','rejected']),state,'Outbox state');\r\n    return transact('Sync outbox update',[db.outbox],async()=>{const row=await db.outbox.get(id);if(!row||row.ownerId!==ownerId)throw new LocalCoachStorageError('not-found','Sync record was not found for this owner.',{recoverable:true});const updated=parse(OutboxRecordSchema,{...row,state:next,updatedAt:now()},'Sync outbox record');await db.outbox.put(updated);return updated;});\r\n   },\r\n   async exportLocalData(){\r\n    try{return await db.transaction('r',db.intake,db.settings,db.workouts,db.workoutEvents,db.outbox,...importLedgerTables(db),async()=>{\r\n     const intake=await db.intake.get(ownerId),settings=await db.settings.get(ownerId),workouts=await db.workouts.where('ownerId').equals(ownerId).toArray(),events=await db.workoutEvents.where('ownerId').equals(ownerId).toArray(),outbox=await db.outbox.where('ownerId').equals(ownerId).toArray();\r\n     return {importAssignments:await readImportLedger(db,ownerId,LocalCoachStorageError),exportVersion:LOCAL_COACH_EXPORT_VERSION,schemaVersion:LOCAL_COACH_SCHEMA_VERSION,ownerId,deviceId:identity.deviceId,exportedAt:now(),intake:intake?parse(IntakeRecordSchema,intake,'Coach intake record'):null,settings:settings?parse(SettingsRecordSchema,settings,'Coach settings record'):null,workouts:workouts.map(row=>parse(WorkoutRecordSchema,row,'Workout')).sort((a,b)=>a.startedAt-b.startedAt||a.id.localeCompare(b.id)),events:events.map(row=>parse(EventRecordSchema,row,'Workout event')).sort((a,b)=>a.createdAt-b.createdAt||a.id.localeCompare(b.id)),outbox:outbox.map(row=>parse(OutboxRecordSchema,row,'Sync outbox record')).sort((a,b)=>a.createdAt-b.createdAt||a.id.localeCompare(b.id))};\r\n    });}catch(error){throw classifyLocalCoachError(error,'Local coach export');}\r\n   },\r\n   async deleteLocalData(){return transact('Local coach deletion',[db.meta,db.intake,db.settings,db.workouts,db.workoutEvents,db.outbox,...importLedgerTables(db)],async()=>{const metadataKeys=(await db.meta.where('ownerId').equals(ownerId).primaryKeys()).filter(key=>key!=='guest-owner-id');const counts={importAssignments:await deleteImportLedger(db,ownerId),intake:await db.intake.where('ownerId').equals(ownerId).delete(),settings:await db.settings.where('ownerId').equals(ownerId).delete(),workouts:await db.workouts.where('ownerId').equals(ownerId).delete(),events:await db.workoutEvents.where('ownerId').equals(ownerId).delete(),outbox:await db.outbox.where('ownerId').equals(ownerId).delete(),metadata:metadataKeys.length};if(metadataKeys.length)await db.meta.bulkDelete(metadataKeys);return counts;});},\r\n   async importLegacyLocalState(input){\r\n    const legacy=parse(LegacyMigrationSchema,input,'Legacy local state'),markerKey=`legacy:${JSON.stringify([ownerId,legacy.sourceKey])}`;\r\n    return transact('Legacy local migration',[db.meta,db.intake,db.settings,db.outbox],async()=>{\r\n     if(await db.meta.get(markerKey))return {imported:false,reason:'already-imported'};\r\n     const timestamp=now();\r\n     if(legacy.intake!==undefined){const value=parse(IntakeSchema,legacy.intake,'Legacy coach intake');await db.intake.put(parse(IntakeRecordSchema,{ownerId,deviceId:identity.deviceId,value,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach intake record'));}\r\n     if(legacy.settings!==undefined)await db.settings.put(parse(SettingsRecordSchema,{ownerId,deviceId:identity.deviceId,value:legacy.settings,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Coach settings record'));\r\n     let pending=0;\r\n     for(const item of legacy.pendingSets??[]){const workoutId=createId(cryptoObject),id=createId(cryptoObject);const row=parse(OutboxRecordSchema,{id,workoutId,clientWorkoutId:workoutId,ownerId,deviceId:identity.deviceId,type:'workout.completed',state:'pending',payload:{legacy:true,legacyId:String(item.id),value:item.value,activeSeconds:item.active??null},createdAt:timestamp,updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Legacy sync outbox record');await db.outbox.put(row);pending++;}\r\n     await db.meta.put(parse(MetaRecordSchema,{key:markerKey,ownerId,value:{sourceKey:legacy.sourceKey,importedAt:timestamp,pending},updatedAt:timestamp,schemaVersion:LOCAL_COACH_SCHEMA_VERSION},'Migration metadata'));\r\n     return {imported:true,pending};\r\n    });\r\n   },\r\n  });\r\n };\r\n\r\n return Object.freeze({\r\n  schemaVersion:LOCAL_COACH_SCHEMA_VERSION,\r\n  layoutVersion:LOCAL_COACH_LAYOUT_VERSION,\r\n  deviceId:identity.deviceId,\r\n  guestOwnerId:identity.guestOwnerId,\r\n  forOwner,\r\n  close(){db.close({disableAutoOpen:true});},\r\n });\r\n}\r\n","manifest.webmanifest":"{\"id\":\"/pose.html\",\"name\":\"MYR5 Coach\",\"short_name\":\"MYR5\",\"description\":\"Your MYR5 training pod, meals and progress.\",\"start_url\":\"/pose\",\"scope\":\"/\",\"display\":\"standalone\",\"orientation\":\"portrait-primary\",\"background_color\":\"#21172f\",\"theme_color\":\"#21172f\",\"icons\":[{\"src\":\"/icons/myr5-alien-192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\",\"purpose\":\"any\"},{\"src\":\"/icons/myr5-alien-512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\",\"purpose\":\"any\"},{\"src\":\"/icons/myr5-alien-maskable.png\",\"sizes\":\"512x512\",\"type\":\"image/png\",\"purpose\":\"maskable\"}]}\r\n","meal-nutrition.mjs":"import {NUTRIENTS,findFoods,portionNutrition,displayNutrient} from './nutrition.mjs';\r\nimport FDC from './food/nutrition-fdc.json' with {type:'json'};\r\n// #R9-FOOD: the Food-101 scanner labels (and typed text that exactly matches one) skip the SR28 fuzzy\r\n// search below and use this USDA FoodData Central table instead -- one real \"as eaten\" match per label\r\n// rather than a guessed word-match. Reshaped into portionNutrition()'s per-100g \"food\" shape so the same\r\n// grams scaler/renderer this file already has handles it; only the two vitamins FDC gave us are filled in.\r\nconst VITAMIN_KEY={'Vitamin A':'vitaminA','Vitamin C':'vitaminC','Vitamin D':'vitaminD','Vitamin E':'vitaminE','Vitamin B12':'vitaminB12',Folate:'folate'};\r\nfunction fdcFood(entry){\r\n const per100=v=>v==null?null:v*100/entry.servingG;\r\n const food={id:`fdc:${entry.fdcId}`,name:entry.description,calories:per100(entry.kcal),protein:per100(entry.proteinG),carbs:per100(entry.carbsG),fat:per100(entry.fatG)};\r\n for(const v of entry.vitamins)food[VITAMIN_KEY[v.name]]=per100(v.amount);\r\n return food;\r\n}\r\nexport function mountMealNutrition(){\r\n const $=id=>document.getElementById(id),form=$('mealForm');let foods=null,reference=null,generation=0,mode='estimate',queryTimer;\r\n const data=()=>{const nutrients=Object.fromEntries(NUTRIENTS.map(([k])=>[k,form.elements[k].value===''?null:Number(form.elements[k].value)]));const source=reference?.id?.startsWith('fdc:')?`USDA FDC ${reference.id.slice(4)}`:`USDA SR28 ${reference?.id}`;return {...nutrients,micros:Object.fromEntries(NUTRIENTS.slice(4).map(([k])=>[k,nutrients[k]])),nutritionSource:mode==='estimate'&&reference?source:'User entered'};};\r\n const snapshot=()=>({name:$('mealConfirmation').hidden?null:$('mealName').value,nutrients:data()});\r\n function render(){const values=data();for(const [k,,unit]of NUTRIENTS)$('nutrient-'+k).textContent=displayNutrient(values[k],unit);window.dispatchEvent(new CustomEvent('myr5:meal-nutrition',{detail:snapshot()}));}\r\n function calculate(){mode='estimate';const grams=Number($('mealGrams').value),values=portionNutrition(reference,grams);for(const [k]of NUTRIENTS)form.elements[k].value=values[k]??'';form.elements.portion.value=`${grams} g`;$('nutritionBasis').textContent=reference?(reference.id?.startsWith('fdc:')?`USDA FDC · ${grams} g`:`Estimate · ${grams} g`):'No match. Enter the food or label values.';render();}\r\n async function search(query){\r\n  const run=++generation,exact=FDC[query.trim().toLowerCase()];\r\n  reference=null;$('foodReference').replaceChildren();\r\n  if(exact){reference=fdcFood(exact);$('mealGrams').value=exact.servingG;$('foodReferenceLabel').hidden=true;calculate();return;}\r\n  calculate();\r\n  try{foods??=(await import('./nutrition-data.mjs')).default;if(run!==generation)return;const matches=findFoods(foods,query);for(const f of matches){const o=document.createElement('option');o.value=f.id;o.textContent=f.name;$('foodReference').append(o);}reference=matches[0]||null;$('foodReferenceLabel').hidden=!matches.length;calculate();}catch{if(run===generation)$('nutritionBasis').textContent='Food reference could not load. Reconnect or enter nutrition from its label.';}\r\n }\r\n function open(name=''){clearTimeout(queryTimer);$('mealConfirmation').hidden=false;$('mealSaveControls').hidden=false;$('mealName').value=name;$('mealGrams').value=100;$('nutritionAdjust').open=false;search(name);}\r\n function reset(){generation++;clearTimeout(queryTimer);reference=null;form.reset();const now=new Date();form.elements.eatenAt.value=new Date(now.getTime()-now.getTimezoneOffset()*60000).toISOString().slice(0,16);$('mealConfirmation').hidden=true;$('mealSaveControls').hidden=true;for(const [k]of NUTRIENTS)form.elements[k].value='';render();}\r\n $('foodReference').onchange=()=>{reference=foods?.find(f=>f.id===$('foodReference').value)||null;calculate();};\r\n $('mealGrams').oninput=calculate;\r\n $('mealName').oninput=()=>{clearTimeout(queryTimer);generation++;reference=null;calculate();queryTimer=setTimeout(()=>search($('mealName').value),250);};\r\n for(const [k]of NUTRIENTS)form.elements[k].oninput=()=>{mode='manual';$('nutritionBasis').textContent='Your label values';render();};\r\n window.addEventListener('myr5:food-selected',e=>open(e.detail?.name||''));\r\n window.addEventListener('myr5:food-reset',reset);\r\n window.addEventListener('pagehide',()=>{generation++;clearTimeout(queryTimer);});\r\n return {data,reset,snapshot};\r\n}\r\n","meal-scanner.mjs":"import {markFoodPhotoToday} from './food/pyramid-tiles.mjs';\r\nexport function mountMealScanner(){\r\n const $=id=>document.getElementById(id),stage=$('mealScanStage'),overlay=$('mealScanOverlay'),preview=$('foodPreview'),trigger=$('recognizeFood');\r\n let worker=null,photoUrl=null,selected=null,generation=0,timer=null,started=0;\r\n const status=text=>$('foodStatus').textContent=text;\r\n function stopClock(){clearInterval(timer);timer=null;trigger.disabled=false;$('cancelFoodScan').hidden=true;stage.removeAttribute('aria-busy');}\r\n function cancel(message){generation++;worker?.terminate();worker=null;stopClock();overlay.dataset.state='ready';$('scanProgress').hidden=true;if(message){$('scanPhase').textContent='SCAN PAUSED';status(message);}}\r\n function phase(state,title,detail){overlay.hidden=false;overlay.dataset.state=state;$('scanPhase').textContent=title;$('scanDetail').textContent=detail;}\r\n const notify=(type,detail)=>window.dispatchEvent(new CustomEvent(type,{detail}));\r\n function error(text){stopClock();phase('error','SCAN INTERRUPTED',text);stage.hidden=true;$('scanProgress').hidden=true;trigger.hidden=false;status(text);worker?.terminate();worker=null;notify('myr5:food-selected',{name:''});}\r\n $('foodCamera').onclick=()=>$('foodPhoto').click();\r\n $('foodPhoto').onchange=()=>{\r\n  if(!$('foodPhoto').files[0])return;\r\n  cancel();notify('myr5:food-reset');trigger.hidden=true;if(photoUrl)URL.revokeObjectURL(photoUrl);photoUrl=null;selected=null;preview.removeAttribute('src');stage.hidden=true;$('foodSuggestions').replaceChildren();$('foodSuggestions').hidden=true;\r\n  const file=$('foodPhoto').files[0];if(!file){status('Choose a photo to begin.');return;}\r\n  if(file.size>15*1024*1024){status('Choose a photo smaller than 15 MB.');return;}\r\n  if(!['image/jpeg','image/png','image/webp'].includes(file.type)){status('Choose a JPEG, PNG or WebP photo.');return;}\r\n  selected=file;photoUrl=URL.createObjectURL(file);preview.src=photoUrl;preview.hidden=false;stage.hidden=false;$('scanElapsed').textContent='00:00';\r\n  phase('ready','FOOD SCANNER','Reading your photo');return trigger.onclick();\r\n };\r\n $('cancelFoodScan').onclick=()=>{cancel('Scan stopped. Retry or confirm the food below.');trigger.hidden=false;notify('myr5:food-selected',{name:''});};\r\n $('mealsPanel').addEventListener('close',()=>{if(timer){cancel('Scan paused. Tap Retry scan to continue.');trigger.hidden=false;}});\r\n trigger.onclick=async()=>{\r\n  if(!selected){status('Choose a meal photo first.');return;}\r\n  const run=++generation;clearInterval(timer);trigger.hidden=true;trigger.disabled=true;stage.setAttribute('aria-busy','true');$('cancelFoodScan').hidden=false;$('foodSuggestions').replaceChildren();started=performance.now();\r\n  const tick=()=>{const s=Math.floor((performance.now()-started)/1000);$('scanElapsed').textContent=`${String(Math.floor(s/60)).padStart(2,'0')}:${String(s%60).padStart(2,'0')}`;};tick();timer=setInterval(tick,1000);\r\n  phase('preparing','PREPARING PHOTO','Reading image on this device');status('Preparing your photo…');\r\n  try{\r\n   const bitmap=await createImageBitmap(selected);if(run!==generation){bitmap.close();return;}\r\n   const canvas=document.createElement('canvas'),ratio=Math.min(1,768/Math.max(bitmap.width,bitmap.height));canvas.width=Math.max(1,Math.round(bitmap.width*ratio));canvas.height=Math.max(1,Math.round(bitmap.height*ratio));canvas.getContext('2d').drawImage(bitmap,0,0,canvas.width,canvas.height);bitmap.close();\r\n   markFoodPhotoToday();notify('myr5:food-photo-ready'); // accepted, decoded photo: step out of the portal before recognition\r\n   worker??=new Worker('/food-worker.mjs',{type:'module'});\r\n   worker.onerror=()=>{if(run===generation)error('Recognition is unavailable in this browser. Type the meal name below.');};\r\n   worker.onmessage=({data})=>{\r\n    if(run!==generation)return;\r\n    if(data.type==='progress'){\r\n     phase(data.stage||'analyzing',data.stage==='loading'?'LOADING FOOD MODEL':'ANALYZING FOOD',data.text);status(data.text);\r\n     const p=$('scanProgress');p.hidden=!(data.stage==='loading'&&Number.isFinite(data.progress));if(!p.hidden)p.value=data.progress;\r\n    }else if(data.type==='error')error(data.text);\r\n    else if(data.type==='result'){\r\n     stopClock();$('scanProgress').hidden=true;\r\n     const best=data.items[0],unsure=!!(best&&data.uncertain);if(best)void import('./modules/vault/vault-store.mjs').then(m=>m.bump('food-scan')).catch(()=>{});\r\n     phase('result',data.uncertain?'CHECK THE MATCHES':'SCAN COMPLETE',best?`${best.label} · ${(best.score*100).toFixed(1)}% match score`:'No clear food match');\r\n     stage.hidden=true;\r\n     // #3/#37: a real guess with a question mark keeps the pyramid full size — the sheet, retry and candidates\r\n     // below stay closed until the pyramid's Fix or Type it opens them. No guess at all (empty items) shows them\r\n     // at once, same as before.\r\n     trigger.hidden=unsure||!data.uncertain;status(unsure?'':data.uncertain?'Check the match & portion':'Check food & portion');\r\n     notify('myr5:food-selected',{name:best?.label||'',uncertain:unsure,score:best?.score});\r\n     $('foodSuggestions').hidden=unsure||!data.uncertain;\r\n     $('foodSuggestions').replaceChildren(...data.items.map(item=>{const b=document.createElement('button');b.type='button';b.textContent=`${item.label} · ${(item.score*100).toFixed(1)}%`;b.setAttribute('aria-label',`${item.label}, ${(item.score*100).toFixed(1)} percent model match score`);b.onclick=()=>{notify('myr5:food-selected',{name:item.label,uncertain:false,score:item.score});};return b;}));\r\n    }\r\n   };\r\n   worker.postMessage({image:canvas.toDataURL('image/jpeg',.85)});\r\n  }catch{if(run===generation)error('Could not read that photo. Try a JPEG or PNG, or enter the meal below.');}\r\n };\r\n window.addEventListener('pagehide',()=>{cancel();if(photoUrl)URL.revokeObjectURL(photoUrl);photoUrl=null;});\r\n}\r\n","meditation-backgrounds.mjs":"// D28: meditation backgrounds are the \"wonders\" pack entries in plan/asset-manifest.json\r\n// (pack \"wonders\", files backgrounds/clean/<id>.webp). The full 48 ship through the signed pack host,\r\n// never core. Six starters ship on the site (pod/worlds/starter/) in the optional Starter download (W2-2O),\r\n// fetched on demand when missing: todaysBackground() asks a caller-supplied lookup for today's pack copy and\r\n// falls back to today's starter. This module fetches nothing itself.\r\n// ponytail: no default pack-store lookup yet (no live pack host); wire one when packs ship.\r\nexport const WONDERS_PACK = 'wonders';\r\n// Stems of every pack:\"wonders\" entry in plan/asset-manifest.json (48, checked 2026-09-22).\r\nexport const WONDER_BACKGROUNDS = Object.freeze(`angkor-wat-a angkor-wat-b big-ben-a big-ben-b bora-bora chichen-itza-a chichen-itza-b\r\nchrist-the-redeemer-a christ-the-redeemer-b colosseum-a colosseum-b colossus-of-rhodes dead-sea eiffel-tower-a eiffel-tower-b\r\ngalapagos-islands grand-canyon great-pyramid-of-giza-a great-pyramid-of-giza-b great-wall-of-china-a great-wall-of-china-b\r\nhanging-gardens-of-babylon kyoto lighthouse-of-alexandria machu-picchu-a machu-picchu-b maldives mausoleum-at-halicarnassus\r\nmount-fuji-a mount-fuji-b new-york-a new-york-b niagara-falls-a niagara-falls-b northern-lights petra santorini-a santorini-b\r\nstatue-of-zeus-at-olympia sydney-opera-house-a sydney-opera-house-b taj-mahal-a taj-mahal-b temple-of-artemis-at-ephesus tokyo\r\ntropical-island venice-a venice-b`.split(/\\s+/));\r\nexport const wonderAssetPath = id => `backgrounds/clean/${id}.webp`;\r\nexport const STARTER_WONDERS = Object.freeze(['great-wall-of-china-a', 'great-pyramid-of-giza-a', 'machu-picchu-a', 'taj-mahal-a', 'colosseum-a', 'mount-fuji-a']);\r\nexport const starterWonderUrl = id => `/pod/worlds/starter/${id}.webp`;\r\n\r\nexport function backgroundForDay(ids = WONDER_BACKGROUNDS, day = Math.floor(Date.now() / 86400000)) {\r\n return ids?.length ? ids[((Math.floor(day) % ids.length) + ids.length) % ids.length] : null;\r\n}\r\n\r\n// lookup: optional async ({pack, path, id}) => local URL string | null.\r\nexport async function resolveBackground(id, lookup) {\r\n if (!id || !lookup) return null;\r\n try {\r\n  const url = await lookup({pack: WONDERS_PACK, path: wonderAssetPath(id), id});\r\n  return typeof url === 'string' && url ? url : null;\r\n } catch {\r\n  return null;\r\n }\r\n}\r\n\r\n// Today's wonder: the downloaded pack copy (any of the 48) when the lookup has it, else today's starter.\r\nexport async function todaysBackground(lookup, day = Math.floor(Date.now() / 86400000)) {\r\n const id = backgroundForDay(WONDER_BACKGROUNDS, day), url = await resolveBackground(id, lookup);\r\n if (url) return {id, url};\r\n const starter = backgroundForDay(STARTER_WONDERS, day);\r\n return {id: starter, url: starterWonderUrl(starter)};\r\n}\r\n\r\n// Mean colour of the image's bottom pixel row, so the page below the landmark continues it.\r\nexport function averageRgb(data) {\r\n const sum = [0, 0, 0];\r\n for (let i = 0; i < data.length; i += 4) for (let c = 0; c < 3; c++) sum[c] += data[i + c];\r\n const n = Math.max(1, data.length / 4);\r\n return `rgb(${sum.map(v => Math.round(v / n)).join(',')})`;\r\n}\r\n","meditation.css":".meditation-entry{display:grid;grid-template-columns:40px 1fr;text-align:left;gap:3px 12px;width:100%;min-height:78px;padding:16px;margin:14px 0;border:2px outset #b7cdc4;border-radius:12px;background:linear-gradient(120deg,#4e7772,#2e444d);color:#f2ebd3;cursor:pointer}.meditation-entry>span{grid-row:span 2;font-size:35px;align-self:center}.meditation-entry strong{font:20px Georgia,serif}.meditation-entry small{font-size:12px}.meditation-panel{overflow:auto;background:linear-gradient(#173338,#101b29)!important;color:#def4e8!important;max-width:560px}.meditation-panel header{color:#e1eed7}.meditation-eyebrow{font:10px monospace;letter-spacing:.12em;color:#92b9ad;text-align:center}.meditation-stage{height:min(40dvh,300px);position:relative;display:grid;place-items:center;isolation:isolate}.meditation-character{border:0!important;background:none!important;box-shadow:none!important;position:relative;z-index:2;width:150px;height:210px;cursor:pointer}.meditation-character canvas{height:100%;max-width:100%;image-rendering:pixelated}.breathing-ring{position:absolute;width:220px;height:220px;border:1px solid #9edece;border-radius:50%;box-shadow:0 0 50px #7ff7cc20,inset 0 0 25px #8cefd411;animation:meditation-breathe 8s ease-in-out infinite}.meditation-speech{text-align:center;min-height:50px;font:20px/1.4 Georgia,serif}.meditation-hint{text-align:center;font-size:12px;color:#8eafa8}.breathing-paused .breathing-ring{animation-play-state:paused}.meditation-stage[data-annoyed=\"2\"] .meditation-character{rotate:-3deg}.meditation-stage[data-annoyed=\"3\"] .meditation-character{rotate:4deg;filter:sepia(.2)}.meditation-stage[data-annoyed=\"4\"] .meditation-character{rotate:-5deg;filter:sepia(.5) saturate(1.7)}.meditation-can{position:absolute;z-index:3;width:42px;height:58px;top:67%;left:64%;background:linear-gradient(90deg,#a2aaad,#f3f4e6,#a7b3b3);border-radius:4px 4px 10px 10px;border-top:7px solid #c5ff4b;display:grid;place-content:center;opacity:0;color:#47235f;font:12px monospace}.meditation-can i{position:absolute;left:3px;right:3px;top:-5px;height:4px;background:#dfffbc}.meditation-powder{position:absolute;z-index:4;color:#d9ffc1;font:30px monospace;top:30%;left:55%;opacity:0}.snorting .meditation-can{animation:meditation-can 1.8s ease-in-out forwards}.snorting .meditation-character{animation:meditation-snort 1.8s ease-in-out forwards}.snorting .meditation-powder{animation:meditation-powder 1.8s ease-in-out forwards}.meditation-blackout{position:fixed;inset:0;pointer-events:none;z-index:100;display:none;background:transparent}.blacking-out .meditation-blackout{display:block;animation:meditation-blackout 1.2s linear forwards}.meditation-blackout i{position:absolute;left:0;right:0;top:calc(var(--strip)*12.5%);height:13%;background:#000;transform:translateX(-100%);animation:meditation-shutter .8s calc(var(--strip)*.045s) forwards}.meditation-blackout i:nth-child(even){transform:translateX(100%)}\r\n@keyframes meditation-breathe{0%,100%{transform:scale(.75);opacity:.45}50%{transform:scale(1.08);opacity:1}}@keyframes meditation-can{0%{opacity:0;transform:translateY(60px)}25%{opacity:1;transform:translateY(0)}65%,100%{opacity:1;transform:translate(-44px,-90px) rotate(-65deg)}}@keyframes meditation-snort{0%,45%{transform:none}70%{transform:translate(12px,-4px) rotate(13deg)}100%{transform:translate(0,5px) rotate(-7deg)}}@keyframes meditation-powder{0%,45%,100%{opacity:0}65%{opacity:1;transform:translate(0,5px)}85%{opacity:.2;transform:translate(-30px,-15px) scale(.3)}}@keyframes meditation-shutter{to{transform:translateX(0)}}@keyframes meditation-blackout{0%{background:#ecfff060}35%{background:transparent}80%,100%{background:black}}@media(prefers-reduced-motion:reduce){.breathing-ring,.snorting .meditation-character,.snorting .meditation-can,.snorting .meditation-powder{animation:none}.snorting .meditation-can{opacity:1}.blacking-out .meditation-blackout{animation:none;background:black}.meditation-blackout i{animation:none;transform:none}}\r\n.breathing-session{display:grid;gap:8px;padding:12px}.breathing-session button{min-height:44px;font-size:1rem}.breathing-session progress{width:100%;accent-color:#9cffcb}.breathing-session p{margin:0;font-size:.875rem;color:#cdf1df}\r\n.breath-modes,.breath-run{display:grid;gap:10px}.breath-mode-card{display:grid!important;gap:4px;justify-content:start!important;justify-items:start;text-align:left;padding:12px!important;border:1px solid #4e7772!important;border-radius:10px;background:#132b2ee6!important;color:#def4e8!important}.breath-mode-card strong{font-size:1rem}.breath-mode-card small{color:#a9d2c6;font-size:.8rem}.breath-seated-notice{color:#ffd699!important;font-weight:650}.breath-phase{min-height:1.3em;font-weight:650;font-size:1rem!important;color:#eafff2!important}.breath-actions{display:flex;gap:8px}.breath-actions button{flex:1}.breath-exit{background:#5a2530!important;border-color:#d27a86!important;font-weight:650}.breath-stance-link{justify-self:start;background:none!important;font-size:.875rem!important}.breath-note{color:#9cbdb4!important;font-size:.75rem!important}\r\n.meditation-panel:has([data-breath-run][data-phase=\"hold\"]) .breathing-ring{animation-play-state:paused}\r\n/* D28 default layout: a full-height room. The day's wonder (~0.86:1 tile) sits uncropped across\r\n   the top and fades into a colour sampled from its own bottom edge, where the breathing UI lives.\r\n   No downloaded art: the original neutral gradient is the placeholder. The header stays pinned so\r\n   Close is always reachable (Esc also closes). */\r\n.meditation-panel{width:min(560px,100%);max-width:none;height:100dvh;max-height:none;margin:0 auto;padding:0 0 24px;border:0;border-radius:0}\r\n.meditation-panel header{position:sticky;top:0;z-index:5;height:0;margin:0;padding:0 12px;align-items:flex-start}.meditation-panel header>*{margin-top:12px!important}.meditation-panel header h2{text-shadow:0 1px 3px #000}.meditation-panel header button{background:#101b29d9}\r\n.meditation-panel [data-meditation-scene]>:not(.meditation-stage){margin-inline:16px}.meditation-panel .breathing-session{padding-inline:0}.meditation-panel:not(.has-wonder-art) .meditation-stage{margin-top:64px}\r\n.meditation-panel.has-wonder-art{background:var(--wonder-grey,var(--wonder-base,#101b29))!important;transition:background-color 2.5s ease}.meditation-panel.has-wonder-art.meditation-colour{background:var(--wonder-base,#101b29)!important}\r\n.meditation-panel.has-wonder-art .meditation-stage{height:clamp(300px,52dvh,520px);background:none}.meditation-panel.has-wonder-art .meditation-near{place-items:end center;padding-bottom:8px}\r\n.meditation-panel.has-wonder-art .meditation-far{background:linear-gradient(transparent 72%,var(--wonder-base,#101b29)),var(--wonder-url) center bottom/cover no-repeat;image-rendering:pixelated}\r\n.meditation-panel.has-wonder-art .meditation-character{height:170px}.meditation-panel.has-wonder-art .breathing-ring{width:180px;height:180px}.meditation-panel.has-wonder-art .meditation-speech{text-shadow:0 1px 3px #000}\r\n/* #149 (D46): the black-and-white room. Three peering layers slide by depth while dragged (meditation.mjs sets --peer-x/y):\r\n   far = the day's wonder, mid = the big sleeping coach, near = your seated character, the ring and the caption.\r\n   A full session fades the colour back in; stopping early lets the coach leap out through the water. */\r\n.meditation-stage{overflow:hidden;touch-action:pan-y}.meditation-layer{position:absolute;inset:0;--depth:1;translate:calc(var(--peer-x,0px)*var(--depth)) calc(var(--peer-y,0px)*var(--depth));transition:translate .5s ease-out}\r\n.meditation-far{--depth:.3;scale:1.05}.meditation-mid{--depth:.6;z-index:1}.meditation-near{z-index:2;display:grid;place-items:center}\r\n.meditation-panel:not(.has-wonder-art) .meditation-far{background:inherit;image-rendering:pixelated}\r\n.meditation-coach{position:absolute;left:-12%;right:-12%;bottom:14%;height:72%;transform-origin:50% 100%;transition:scale .3s ease-in,translate 3s linear}.coach-wander .meditation-coach{translate:135% 0}\r\n.meditation-coach .myr5-companion-card{position:absolute!important;inset:0!important;width:auto!important;height:auto!important;border:0!important;background:none!important;backdrop-filter:none!important;box-shadow:none!important;overflow:visible!important;pointer-events:none}.meditation-coach .myr5-companion-card>:not(.myr5-companion-stage){display:none!important}.meditation-coach .myr5-companion-stage{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;background:none!important}.meditation-coach canvas{width:100%!important;height:100%!important}\r\n.meditation-character canvas{transform-origin:50% 100%;animation:meditation-sit 8s ease-in-out infinite}.breathing-paused .meditation-character canvas{animation-play-state:paused}\r\n.meditation-panel .meditation-near .meditation-speech{position:absolute;left:12px;right:12px;bottom:1%;z-index:4;min-height:0;margin:0;text-shadow:0 1px 3px #000}\r\n@keyframes meditation-sit{50%{scale:1.03}}@keyframes meditation-smack{0%,100%{transform:none}20%{transform:translate(-10px,5px) rotate(-1.2deg)}40%{transform:translate(9px,-6px) rotate(1deg)}60%{transform:translate(-6px,3px)}80%{transform:translate(4px,-2px)}}@keyframes meditation-flash{from{opacity:.85}to{opacity:0}}\r\n@media(prefers-reduced-motion:reduce){.meditation-layer{translate:none}.meditation-character canvas{animation:none}}\r\n/* W2-2O: offered when today's starter wonder isn't on this phone (the Starter pack). */\r\n.meditation-starter{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:8px;margin:0 16px 14px;font-size:14px;text-align:center}\r\n.meditation-starter[hidden]{display:none}\r\n.meditation-starter button{min-height:44px}\r\n/* The still room occupies the entire viewport. Wonder art returns only after a completed session. */\r\n.meditation-panel,.meditation-panel.has-wonder-art{position:fixed;inset:0;box-sizing:border-box;width:100vw;max-width:none;height:100dvh;max-height:none;margin:0;padding:0;overflow:hidden;background:#000!important;color:#fff!important}\r\n.meditation-panel::backdrop{background:#000}\r\n.meditation-panel.meditation-colour.has-wonder-art{background:var(--wonder-base,#000)!important}\r\n.meditation-panel header{position:absolute;top:0;left:0;right:0;height:auto;z-index:8;padding:max(10px,env(safe-area-inset-top)) 14px 0;box-sizing:border-box}\r\n.meditation-panel header>*{margin-top:0!important}\r\n.meditation-panel header h2{font:700 15px/1.2 ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:#fff}\r\n.meditation-panel header button{border:2px solid #fff;border-radius:0;background:#000;color:#fff}\r\n.meditation-panel [data-meditation-scene]{position:relative;width:100%;height:100%;min-height:0;background:#000}\r\n.meditation-panel .meditation-stage,.meditation-panel.has-wonder-art .meditation-stage,.meditation-panel:not(.has-wonder-art) .meditation-stage{width:100%;height:100%;margin:0;background:#000}\r\n.meditation-panel .meditation-far,.meditation-panel.has-wonder-art .meditation-far{background:#000!important}\r\n.meditation-panel.meditation-colour.has-wonder-art .meditation-far{background:linear-gradient(transparent 72%,var(--wonder-base,#000)),var(--wonder-url) center bottom/cover no-repeat!important}\r\n.meditation-panel .meditation-near,.meditation-panel.has-wonder-art .meditation-near{place-items:center;padding:0}\r\n.meditation-panel .meditation-character,.meditation-panel.has-wonder-art .meditation-character{width:clamp(115px,34vw,170px);height:210px}\r\n.meditation-panel .breathing-ring,.meditation-panel.has-wonder-art .breathing-ring{width:180px;height:180px;border-color:#fff;box-shadow:0 0 0 2px #000,0 0 0 3px #fff}\r\n.meditation-panel .meditation-coach{bottom:26%;height:52%}\r\n.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{bottom:22%;padding:0 12px;color:#fff;font:700 17px/1.35 ui-monospace,monospace;text-shadow:2px 2px #000}\r\n.meditation-panel .breathing-session{position:absolute;z-index:6;left:0;right:0;bottom:max(10px,env(safe-area-inset-bottom));box-sizing:border-box;width:min(560px,100%);margin:auto;padding:0 16px;color:#fff}\r\n.meditation-panel .breathing-session p,.meditation-panel .breath-note,.meditation-panel .breath-phase,.meditation-panel .breath-seated-notice{color:#fff!important}\r\n.meditation-panel .breath-run{padding:10px;border:2px solid #fff;background:#000e}\r\n.meditation-panel .breath-actions button,.meditation-panel .breath-stance-link,.meditation-panel [data-breath-pause]{border:2px solid #fff;border-radius:0;background:#000;color:#fff}\r\n.meditation-panel .breathing-session progress{accent-color:#fff}\r\n.meditation-panel [data-breath-pause]{position:absolute;z-index:7;right:16px;bottom:160px}\r\n.meditation-panel .meditation-starter{position:absolute;z-index:6;left:12px;right:12px;bottom:110px;margin:0;padding:6px;border:1px solid #fff;background:#000;color:#fff;font:12px ui-monospace,monospace}\r\n.meditation-panel .meditation-starter button{border:1px solid #fff;border-radius:0;background:#000;color:#fff}\r\n/* Two square pixel thought bubbles flank the character. They remain real focusable buttons. */\r\n.meditation-panel .breath-modes{position:absolute;z-index:5;inset:0;display:block;pointer-events:none}\r\n.meditation-panel .breath-modes[hidden]{display:none}\r\n.meditation-panel .breath-mode-card{position:absolute;top:37%;width:clamp(110px,30vw,166px);min-height:130px;box-sizing:border-box;padding:9px!important;border:3px solid #fff!important;border-radius:0!important;background:#000!important;color:#fff!important;box-shadow:5px 5px 0 #777!important;image-rendering:pixelated;pointer-events:auto;font:11px/1.25 ui-monospace,monospace!important;overflow-wrap:anywhere}\r\n.meditation-panel .breath-mode-card:first-child{left:2%}\r\n.meditation-panel .breath-mode-card:last-child{right:2%}\r\n.meditation-panel .breath-mode-card::after{content:'';position:absolute;top:67%;width:10px;height:10px;background:#000;border:3px solid #fff;box-shadow:5px 5px 0 #777}\r\n.meditation-panel .breath-mode-card:first-child::after{left:calc(100% + 6px)}\r\n.meditation-panel .breath-mode-card:last-child::after{right:calc(100% + 6px)}\r\n.meditation-panel .breath-mode-card strong{font:700 13px/1.2 ui-monospace,monospace}\r\n.meditation-panel .breath-mode-card small{color:#fff;font:10px/1.25 ui-monospace,monospace}\r\n.meditation-panel .breath-mode-card:focus-visible{outline:3px solid #fff;outline-offset:5px}\r\n.meditation-panel.meditation-colour .breath-mode-card{border-color:#fff!important}\r\n@media(max-height:650px){.meditation-panel .breath-mode-card{top:28%;min-height:110px}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{bottom:28%}}\r\n\r\n/* Keep the active track time readable near the top third of the still room. */\r\n.meditation-panel .breath-run:not([hidden]) [data-status]{position:fixed;top:33dvh;left:12px;right:12px;z-index:7;margin:0;text-align:center;pointer-events:none}\r\n\r\n/* Pixel room: landmarks, a low platform, the sleeping four-legged coach and the selected 2D pose. */\r\n.meditation-panel .meditation-far{background:#000!important}\r\n.meditation-panel .room-art{position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated}\r\n.meditation-panel .meditation-mid{z-index:1}\r\n.meditation-panel .meditation-platform{position:absolute;left:8%;right:8%;bottom:10%;height:22%;z-index:0}\r\n.meditation-panel .meditation-platform svg{width:100%;height:100%;overflow:visible;image-rendering:pixelated}\r\n.meditation-panel .meditation-coach{z-index:1;left:3%;right:3%;bottom:23%;height:48%;opacity:.6;filter:grayscale(1)}\r\n.meditation-panel .coach-pixel{position:absolute;z-index:1;left:-10%;right:-10%;bottom:18%;width:120%;height:58%;opacity:.82;filter:grayscale(1);image-rendering:pixelated}\r\n.meditation-panel .meditation-coach .myr5-companion-card{opacity:.14;mix-blend-mode:screen}\r\n.meditation-panel .meditation-near{z-index:2}\r\n.meditation-panel .breathing-orbit{position:absolute;left:50%;top:43%;width:min(68vw,380px);aspect-ratio:1;transform:translate(-50%,-50%);display:grid;place-items:center;z-index:1;pointer-events:none}\r\n.meditation-panel .breathing-ring{position:absolute;inset:0;width:auto;height:auto;box-sizing:border-box;border:4px solid #fff;border-radius:50%;box-shadow:0 0 0 5px #000,0 0 0 8px #fff;animation:none;transform:scale(calc(.72 + var(--breath-progress,.5)*.38));transition:transform .22s linear,opacity .22s linear;opacity:.92}\r\n.meditation-panel .breath-count{position:relative;z-index:2;font:900 clamp(72px,20vw,148px)/.9 ui-monospace,monospace;letter-spacing:-.08em;color:#fff;text-shadow:5px 5px #000,-2px -2px #000;transform:translateY(-8%)}\r\n.meditation-panel [data-session-clock]{position:absolute;z-index:3;bottom:17%;padding:5px 13px;border:3px solid #fff;background:#000;color:#fff;font:700 clamp(18px,5vw,28px)/1 ui-monospace,monospace;letter-spacing:.05em;box-shadow:4px 4px #777}\r\n.meditation-panel .meditation-character{position:absolute;z-index:3;left:50%;bottom:8%;transform:translateX(-50%);width:clamp(145px,42vw,220px);height:clamp(200px,34vh,270px);padding:0!important;image-rendering:pixelated}\r\n.meditation-panel .meditation-character canvas{display:none}\r\n.meditation-panel .meditation-character .pose{display:none;position:absolute;inset:0}\r\n.meditation-panel .meditation-character .pose svg{width:100%;height:100%;image-rendering:pixelated;filter:drop-shadow(5px 5px 0 #000)}\r\n.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{bottom:21%;z-index:5;max-width:700px;margin:0 auto;padding:7px 16px;border:2px solid #fff;background:#000;color:#fff;font:700 clamp(14px,4vw,21px)/1.35 ui-monospace,monospace;text-align:center;text-shadow:none;box-shadow:4px 4px #777}\r\n.meditation-panel .breathing-session{bottom:max(12px,env(safe-area-inset-bottom));padding-inline:12px}\r\n.meditation-panel .breath-run:not([hidden]) [data-status]{top:calc(50% - min(34vw,190px) - 42px);font:700 16px ui-monospace,monospace;text-shadow:2px 2px #000}\r\n.meditation-panel .breath-mode-card[data-mode=\"gentle-guided\"]{left:50%;right:auto;top:55%;width:min(72vw,290px);min-height:126px;transform:translateX(-50%)}\r\n.meditation-panel .breath-mode-card[data-mode=\"gentle-guided\"]::after{display:none}\r\n.meditation-panel.breathing-paused .breathing-ring{transition:none}\r\n@media(max-height:650px){.meditation-panel .breathing-orbit{top:40%;width:min(54vh,280px)}.meditation-panel .meditation-character{bottom:5%;height:clamp(150px,29vh,200px)}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{bottom:19%}.meditation-panel .breath-mode-card{top:29%}.meditation-panel .breath-mode-card[data-mode=\"gentle-guided\"]{top:52%}}\r\n@media(prefers-reduced-motion:reduce){.meditation-panel .breathing-ring{transform:scale(.9);transition:none}}\r\n\r\n/* Final room composition: scenery at the back, the coach above a platform, and all choices below. */\r\n.meditation-panel .room-art{z-index:0;filter:grayscale(1);opacity:.9}\r\n.meditation-panel .room-pixels{position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;filter:grayscale(1) contrast(1.65);opacity:.34;mix-blend-mode:screen}.meditation-panel:not(.meditation-colour) .room-pixels{display:none}\r\n.meditation-panel .coach-pixel{left:-7%;bottom:18%;width:114%;height:59%;opacity:.9;z-index:1;filter:grayscale(1) contrast(1.1)}\r\n.meditation-panel .meditation-coach{z-index:2;left:-17%;right:-17%;bottom:8%;height:82%;opacity:.34}\r\n.meditation-panel .meditation-coach .myr5-companion-card{opacity:1;mix-blend-mode:normal}\r\n.meditation-panel .breathing-orbit{top:37%;width:min(76vw,410px);z-index:3}\r\n.meditation-panel .breathing-ring{position:absolute;inset:0;width:auto!important;height:auto!important;display:block;transform:scale(calc(.72 + var(--breath-progress,.5)*.38));transform-origin:center;}\r\n.meditation-panel .breath-count{position:absolute;left:0;right:0;top:calc(50% - 82px);display:grid;place-items:center;transform:none;font-size:clamp(72px,20vw,142px);line-height:.8;z-index:3}\r\n.meditation-panel [data-session-clock]{left:50%;right:auto;top:50%;bottom:auto;transform:translate(-50%,-50%);font-size:clamp(18px,5vw,26px);z-index:4;white-space:nowrap}\r\n.meditation-panel .breath-direction{position:absolute;left:calc(50% + 58px);top:50%;transform:translateY(-50%);z-index:4;color:#fff;font:900 11px/1 ui-monospace,monospace;font-style:normal;letter-spacing:.08em;white-space:nowrap;text-shadow:2px 2px #000}\r\n.meditation-panel .meditation-character{left:50%;bottom:36%;height:clamp(174px,25vh,215px);width:clamp(138px,36vw,195px);z-index:4;transform:translateX(-50%)}\r\n.meditation-panel .meditation-character[data-expression=\"focused\"]{filter:brightness(.8)}\r\n.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:64%;bottom:auto;z-index:5;max-width:min(700px,90vw);font-size:clamp(12px,3.7vw,19px)}\r\n.meditation-panel .breathing-session{position:absolute;left:0;right:0;bottom:max(8px,env(safe-area-inset-bottom));z-index:7;width:100%;max-width:760px;padding:0 12px;margin:auto;display:grid;gap:5px;box-sizing:border-box;pointer-events:none}\r\n.meditation-panel .breath-run:not([hidden]){display:grid;grid-template-columns:1fr;gap:5px;padding:7px 9px;border:2px solid #fff;background:#000e;pointer-events:auto}\r\n.meditation-panel .breath-run:not([hidden]) .breath-phase{min-height:1em;max-height:1.3em;overflow:hidden;font:700 12px/1.25 ui-monospace,monospace!important;text-align:center}\r\n.meditation-panel .breathing-session progress{height:8px}\r\n.meditation-panel .breath-actions{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}\r\n.meditation-panel .breath-actions button{min-height:42px!important;padding:4px 8px!important;font:700 13px ui-monospace,monospace!important}\r\n.meditation-panel .breath-actions [data-breath-pause]{position:static;right:auto;bottom:auto;z-index:auto;min-height:42px!important}\r\n.meditation-panel .breath-exit{background:#000!important;border:2px solid #fff!important;border-radius:0!important;color:#fff!important}\r\n.meditation-panel .breath-actions [data-skip-hold]:not([hidden]){grid-column:1/-1}\r\n.meditation-panel .breath-actions [data-retry]:not([hidden]){grid-column:1/-1}\r\n.meditation-panel .breath-modes{position:static;inset:auto;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;width:100%;margin:auto;pointer-events:auto;z-index:6}\r\n.meditation-panel .breath-modes[hidden]{display:none}\r\n.meditation-panel .breath-mode-card{position:relative;inset:auto;transform:none;width:auto;min-height:85px;padding:8px!important;border:3px solid #fff!important;background:#000e!important;box-shadow:4px 4px #777!important;pointer-events:auto}\r\n.meditation-panel [data-meditation-scene]>.breathing-session{margin-inline:0!important;width:100%;max-width:760px}\r\n.meditation-panel .breath-mode-card,.meditation-panel .breath-actions button{box-sizing:border-box;min-width:0!important;width:100%!important}\r\n.meditation-panel .breath-mode-card:first-child,.meditation-panel .breath-mode-card:last-child{left:auto;right:auto}\r\n.meditation-panel .breath-mode-card::after{display:none}\r\n.meditation-panel .breath-mode-card strong{font-size:clamp(12px,3.2vw,16px)}\r\n.meditation-panel .breath-mode-card small{font-size:clamp(9px,2.5vw,12px)}\r\n.meditation-panel .breath-seated-notice{font-size:9px!important;line-height:1.15}\r\n.meditation-panel .breathing-session [data-status]{position:static;top:auto;left:auto;right:auto;z-index:auto;margin:0;text-align:center;font:10px ui-monospace,monospace;pointer-events:none}\r\n.meditation-panel .breath-note{display:block;max-width:760px;margin:0 auto!important;padding:2px 5px;font:10px/1.2 ui-monospace,monospace!important;text-align:center;pointer-events:none;text-shadow:1px 1px #000}\r\n@media(max-height:700px){.meditation-panel .breathing-orbit{top:29%;width:min(48vh,240px)}.meditation-panel .breath-count{top:calc(50% - 65px);font-size:clamp(58px,18vw,100px)}.meditation-panel [data-session-clock]{font-size:17px;padding:4px 8px}.meditation-panel .breath-direction{left:calc(50% + 48px);top:50%;font-size:9px}.meditation-panel .meditation-character{bottom:31%;height:min(20vh,140px)}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:64%}.meditation-panel .breath-mode-card{min-height:65px;padding:5px!important}.meditation-panel .breath-note{font-size:8px!important}}\r\n@media (orientation:portrait) and (min-height:701px) and (max-height:780px){.meditation-panel .meditation-character{bottom:39%}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:61%}}\r\n@media(max-width:340px){.meditation-panel .breath-modes{width:100%;gap:5px}.meditation-panel .breath-mode-card{padding:5px!important}.meditation-panel .breath-mode-card small{font-size:8px}}\r\n@media(orientation:landscape) and (max-height:600px){\r\n .meditation-panel [data-meditation-scene]>.breathing-session{left:52%;right:2%;bottom:3%;width:46%;max-width:none;margin-inline:0!important;padding:0;gap:4px}\r\n .meditation-panel .breathing-orbit{left:27%;top:33%;width:min(42vh,220px)}\r\n .meditation-panel .breath-count{top:calc(50% - 56px);font-size:clamp(54px,13vh,78px)}\r\n .meditation-panel [data-session-clock]{font-size:16px;padding:4px 8px}\r\n .meditation-panel .breath-direction{left:calc(50% + 43px);top:50%;font-size:8px}\r\n .meditation-panel .meditation-character{left:27%;bottom:3%;height:min(40vh,145px);width:min(26vw,165px)}\r\n .meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:20%;left:52%;right:3%;width:45%;max-width:none;padding:5px 8px;font-size:12px;line-height:1.2}\r\n .meditation-panel .breathing-session{left:52%;right:2%;bottom:3%;width:46%;max-width:none;padding:0;gap:4px}\r\n .meditation-panel .breath-run:not([hidden]){gap:4px;padding:5px 7px}\r\n .meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:10px!important}\r\n .meditation-panel .breathing-session progress{height:6px}\r\n .meditation-panel .breath-actions{gap:4px}\r\n .meditation-panel .breath-actions button{min-height:38px!important;padding:3px 5px!important;font-size:11px!important}\r\n .meditation-panel .breath-modes{gap:5px}\r\n .meditation-panel .breath-mode-card{min-height:66px;padding:5px!important}\r\n .meditation-panel .breath-mode-card small{font-size:9px}\r\n .meditation-panel .breath-seated-notice{font-size:7px!important}\r\n .meditation-panel .breathing-session [data-status]{font-size:8px}\r\n .meditation-panel .breath-note{font-size:8px!important;line-height:1.05}\r\n}\r\n@media(prefers-reduced-motion:reduce){.meditation-panel .breathing-ring{transform:scale(.9)!important;transition:none!important}}\r\n\r\n/* User-directed scene update: own Gala sprite, forest silhouette and a genuinely phase-driven stop ring. */\r\n.meditation-panel [data-meditation-scene]{container-type:size;container-name:meditation-scene}\r\n.meditation-panel .meditation-character{left:25%;bottom:44%;height:clamp(118px,20vh,150px);width:auto;min-width:90px;transform:translateX(-50%);z-index:6}\r\n.meditation-panel .meditation-character canvas{display:block;width:auto;height:100%;max-width:none;image-rendering:pixelated;filter:drop-shadow(4px 4px #000)}\r\n.meditation-panel .meditation-character .pose{display:none!important}\r\n.meditation-panel .forest-canopy{position:absolute;left:-3%;right:-3%;bottom:19%;height:24%;z-index:1;pointer-events:none;filter:grayscale(1);image-rendering:pixelated}\r\n.meditation-panel .forest-canopy svg{display:block;width:100%;height:100%;image-rendering:pixelated;filter:drop-shadow(0 -3px #fff)}\r\n.meditation-panel .meditation-coach{left:-21%;right:-21%;bottom:4%;height:94%;opacity:.42;filter:grayscale(1) contrast(1.22)}\r\n.meditation-panel .meditation-coach .myr5-companion-card{opacity:1;mix-blend-mode:screen}\r\n.meditation-panel .coach-pixel{left:-12%;right:-12%;bottom:7%;width:124%;height:76%;opacity:.82;z-index:1;filter:grayscale(1) contrast(1.3)}\r\n.meditation-panel .breathing-orbit{top:36%;width:min(70vw,370px);z-index:5}\r\n.meditation-panel .breathing-ring{transform:scale(var(--breath-scale,.91))!important;transition:transform .06s linear,opacity .06s linear!important;will-change:transform}\r\n.meditation-panel .breathing-ring-stop{position:absolute;inset:0;z-index:2;border:0!important;border-radius:50%!important;background:transparent!important;box-shadow:none!important;padding:0!important;transform:scale(var(--breath-scale,.91));transform-origin:center;cursor:pointer;pointer-events:auto}\r\n.meditation-panel .breathing-ring-stop[hidden]{display:none}\r\n.meditation-panel .breathing-ring-stop:focus-visible{outline:4px solid #fff!important;outline-offset:8px!important}\r\n.meditation-panel .breath-count,.meditation-panel [data-session-clock],.meditation-panel .breath-direction{pointer-events:none}\r\n.meditation-panel .breath-count{z-index:4}\r\n.meditation-panel [data-session-clock]{z-index:5}\r\n.meditation-panel .breath-direction{z-index:5}\r\n.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{font-size:clamp(28px,7vw,38px)!important;line-height:1.13!important;max-width:min(94vw,900px);padding:10px 13px;z-index:6}\r\n.meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:clamp(20px,5vw,26px)!important;line-height:1.14;max-height:none;overflow:visible}\r\n.meditation-panel .breath-modes{grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch}\r\n.meditation-panel .breath-mode-card{min-height:76px}\r\n.meditation-panel .breath-mode-card strong{font-size:clamp(14px,3.8vw,19px)}\r\n.meditation-panel .breath-mode-card small{font-size:clamp(11px,3vw,15px)}\r\n.meditation-panel .breath-note{font-size:12px!important;line-height:1.2}\r\n.meditation-panel .breath-run:not([hidden]) .breath-seated-notice{font-size:13px!important;line-height:1.15}\r\n.meditation-panel .breath-actions button{font-size:14px!important}\r\n.meditation-panel .meditation-coach{transform-origin:50% 95%}\r\n@media(prefers-reduced-motion:reduce){.meditation-panel .breathing-ring{transition:transform .06s linear,opacity .06s linear!important;animation:none!important}}\r\n@container meditation-scene (orientation: portrait) and (max-height: 780px){\r\n .meditation-panel .breathing-orbit{top:29%;width:min(62vw,230px)}\r\n .meditation-panel .breath-count{top:calc(50% - 60px);font-size:clamp(52px,16vw,82px)}\r\n .meditation-panel [data-session-clock]{font-size:17px;padding:4px 8px}\r\n .meditation-panel .meditation-character{left:22%;bottom:44%;height:min(18cqh,134px);min-width:84px}\r\n .meditation-panel .forest-canopy{bottom:20%;height:42%}\r\n .meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:52%;bottom:auto;left:36%;right:3%;width:auto;box-sizing:border-box;font-size:clamp(27.75px,7.4vw,34px)!important;line-height:1.07!important}\r\n .meditation-panel .breathing-session{bottom:max(5px,env(safe-area-inset-bottom));gap:3px;padding-inline:8px}\r\n .meditation-panel .breath-modes{gap:5px}\r\n .meditation-panel .breath-mode-card{min-height:62px;padding:5px!important}\r\n .meditation-panel .breath-mode-card strong{font-size:14px}\r\n .meditation-panel .breath-mode-card small{font-size:11px}\r\n .meditation-panel .breath-run:not([hidden]){gap:3px;padding:5px 7px}\r\n .meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:24px!important}\r\n .meditation-panel .breath-actions button{min-height:36px!important;font-size:12px!important}\r\n .meditation-panel .breath-note{font-size:10px!important}\r\n}\r\n@container meditation-scene (orientation: portrait) and (max-height: 660px){\r\n .meditation-panel .breathing-orbit{top:25%;width:min(56vw,175px)}\r\n .meditation-panel .breath-count{top:calc(50% - 48px);font-size:clamp(42px,14vw,68px)}\r\n .meditation-panel .breath-direction{left:calc(50% + 40px);font-size:8px}\r\n .meditation-panel .meditation-character{left:22%;bottom:44%;height:min(16cqh,102px);min-width:70px}\r\n .meditation-panel .forest-canopy{bottom:20%;height:30%}\r\n .meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:43%;left:36%;right:3%;width:auto;box-sizing:border-box;font-size:clamp(27.75px,7.4vw,30px)!important;max-height:none;overflow:visible}\r\n .meditation-panel .breath-mode-card{min-height:50px;padding:3px!important}\r\n .meditation-panel .breath-mode-card small,.meditation-panel .breath-note{font-size:9px!important}\r\n .meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:24px!important}\r\n .meditation-panel .breath-actions button{min-height:32px!important;font-size:11px!important}\r\n}\r\n@container meditation-scene (orientation: landscape) and (max-height: 600px){\r\n .meditation-panel .breathing-orbit{top:34%;width:min(40cqh,205px)}\r\n .meditation-panel .meditation-character{left:27%;bottom:3%;height:min(38cqh,130px);width:auto}\r\n .meditation-panel .forest-canopy{left:0;right:48%;bottom:4%;height:25%}\r\n .meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:18%;left:52%;right:3%;width:auto;box-sizing:border-box;font-size:clamp(24px,5vh,31px)!important;line-height:1.05!important}\r\n .meditation-panel [data-meditation-scene]>.breathing-session{left:52%;right:2%;bottom:2%;width:46%;max-width:none;padding:0;gap:3px}\r\n .meditation-panel .breath-modes{gap:4px}\r\n .meditation-panel .breath-mode-card{min-height:46px;padding:3px!important}\r\n .meditation-panel .breath-mode-card small{font-size:10px}\r\n .meditation-panel .breath-phase{font-size:20px!important}\r\n .meditation-panel .breath-note{font-size:9px!important}\r\n}\r\n\r\n/* Pause holds the exact painted breath phase; tapping the ring remains a small, smooth target. */\r\n.meditation-panel.breathing-paused .breathing-ring,.meditation-panel.breathing-paused .breathing-ring-stop{transition:none!important}\r\n@container meditation-scene (orientation: portrait) and (min-height: 781px){.meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:24px!important}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{font-size:clamp(28px,7.4vw,38px)!important;box-sizing:border-box}}\r\n@container meditation-scene (orientation: landscape) and (max-height: 600px){.meditation-panel .meditation-character{left:12%}.meditation-panel .breath-run:not([hidden]) .breath-phase{font-size:20px!important}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{width:auto;box-sizing:border-box}}\r\n\r\n@container meditation-scene (orientation: portrait){.meditation-panel .meditation-character{left:21%}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{left:39%;font-family:system-ui,sans-serif!important}}\r\n@container meditation-scene (orientation: landscape) and (max-height: 600px){.meditation-panel .breathing-orbit{width:min(52cqh,245px)}.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{font-family:system-ui,sans-serif!important}}\r\n\r\n@container meditation-scene (orientation: portrait) and (min-height: 781px){.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:49%}}\r\n@container meditation-scene (orientation: portrait) and (min-height: 661px) and (max-height: 780px){.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:47%}}\r\n@container meditation-scene (orientation: landscape) and (max-height: 600px){.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:22%}}\r\n\r\n@container meditation-scene (orientation: landscape) and (max-height: 600px){.meditation-panel .meditation-speech,.meditation-panel .meditation-near .meditation-speech{top:58px;padding-block:0;line-height:1.2!important}}\r\n\r\n.meditation-panel .room-pixels{display:none!important}.meditation-panel.meditation-colour.has-wonder-art .meditation-far{background:#000!important}\r\n.meditation-panel .room-art{display:none!important}\r\n.meditation-panel .forest-canopy{display:none!important}\r\n.meditation-panel .meditation-far{background:url('/pod/worlds/meditation-waterfall.png') center bottom / cover no-repeat!important;image-rendering:pixelated!important}\r\n.meditation-panel.meditation-colour.has-wonder-art .meditation-far{background:url('/pod/worlds/meditation-waterfall.png') center bottom / cover no-repeat!important;image-rendering:pixelated!important}\r\n.meditation-panel.has-wonder-art .meditation-far{background:url('/pod/worlds/meditation-waterfall.png') center bottom / cover no-repeat!important;image-rendering:pixelated!important}\r\n.meditation-panel .meditation-mid{opacity:.75!important}\r\n.meditation-panel .breath-hud{position:absolute;left:0;right:0;bottom:12%;display:grid;place-items:center;z-index:5;pointer-events:none}\r\n.meditation-panel .breath-hud .breath-count{font-size:clamp(48px,14vw,84px);top:auto!important;bottom:auto!important;position:static!important}\r\n.meditation-panel .breath-hud .breathing-ring-stop{position:static;width:44px;height:44px;pointer-events:auto;background:none;border:0;padding:0;margin:0}\r\n.meditation-panel .meditation-character{left:25%;bottom:6% !important;height:20vh!important}\r\n.meditation-panel .meditation-coach{z-index:1!important}\r\n/* R18 A3: grey veil over the whole stage (a sibling, so nothing inherits a filter); a hole centred on the character grows with each completed breath. */\r\n@property --reveal-r{syntax:'<length>';inherits:false;initial-value:0px}\r\n.meditation-grey{position:absolute;inset:0;z-index:3;pointer-events:none;--cx:50%;--cy:60%;--reveal-r:0px;-webkit-backdrop-filter:grayscale(1);backdrop-filter:grayscale(1);-webkit-mask-image:radial-gradient(circle at var(--cx) var(--cy),transparent calc(var(--reveal-r) - 28px),#000 var(--reveal-r));mask-image:radial-gradient(circle at var(--cx) var(--cy),transparent calc(var(--reveal-r) - 28px),#000 var(--reveal-r));transition:--reveal-r 1.2s ease-out,opacity 2.5s ease}\r\n.meditation-colour .meditation-grey{opacity:0}\r\n/* R18 A4: seated warning, full-scene, fades out on Accept */\r\n.breath-seated-overlay{position:absolute;inset:0;z-index:20;display:grid;place-items:center;padding:24px;background:#050a12f2;transition:opacity .6s ease}.breath-seated-overlay[hidden]{display:none}.breath-seated-overlay.fading{opacity:0;pointer-events:none}\r\n.breath-seated-overlay>div{display:grid;gap:20px;max-width:360px;justify-items:center;text-align:center}.meditation-panel .breath-seated-overlay .breath-seated-notice{font-size:17px!important;line-height:1.45}.breath-seated-overlay button{min-height:48px;padding:0 36px;border:2px solid #fff;border-radius:10px;background:#0b1c26;color:#fff;font:700 17px Georgia,serif;cursor:pointer}\r\n/* R18 A5: speech bubbles fade in (.5 s), stay 4 s, fade out (.5 s) */\r\n.meditation-panel .meditation-speech{opacity:0}.meditation-panel .meditation-speech.bubble{animation:bubble 5s ease forwards}@keyframes bubble{0%{opacity:0}10%,90%{opacity:1}100%{opacity:0}}\r\n/* R18 A6: the four-legged coach behind the waterfall leaps out through the water at the viewer (700 ms), then the dialog closes */\r\n.meditation-panel.coach-leap .meditation-mid{z-index:4;opacity:1!important}.meditation-panel.coach-leap .meditation-coach{animation:coach-leap .7s cubic-bezier(.4,0,.8,.6) forwards}.meditation-panel.coach-leap .meditation-far{animation:coach-splash .7s ease-out}\r\n@keyframes coach-leap{0%{transform:translateY(0) scale(1);opacity:.45}35%{transform:translateY(-8%) scale(1.9);opacity:.85}70%{transform:translateY(4%) scale(3.6);opacity:.95}100%{transform:translateY(14%) scale(6);opacity:0}}@keyframes coach-splash{40%{filter:brightness(1.7) saturate(1.3)}}\r\n/* R18 A3: while a session runs the controls sit at the top, so the seated character (the centre of the colour circle) and the counter stay visible. */\r\n@media(orientation:portrait){.meditation-panel:has([data-breath-run]:not([hidden])) .breathing-session{top:56px;bottom:auto}}\r\n/* R18 polish: a clear scene. No platform diamond; the coach is a solid dark shadow; the counter is a small chip; the top panel fades after Accept. */\r\n.meditation-panel .meditation-platform{display:none}\r\n.meditation-panel .meditation-coach{left:14%;right:14%;bottom:34%;height:36%;filter:brightness(0);opacity:.45}\r\n.meditation-panel .meditation-coach .coach-pixel{filter:brightness(0)}\r\n.meditation-panel .breath-hud{left:auto;right:5%;width:auto;bottom:3%;display:flex;gap:8px;align-items:center;justify-content:flex-end;pointer-events:none}\r\n.meditation-panel .breath-hud .breath-count{position:static!important;transform:none!important;font:700 14px/1 ui-monospace,monospace!important;letter-spacing:0;text-shadow:1px 1px #000;order:2}\r\n.meditation-panel .breath-hud [data-session-clock]{position:static!important;transform:none!important;font-size:14px!important;padding:3px 8px;box-shadow:none;order:1}\r\n.meditation-panel .breath-hud .breath-direction{position:static!important;transform:none!important;font-size:11px!important;order:3}\r\n.meditation-panel .breath-hud .breathing-ring-stop{order:4;width:auto;height:auto;padding:3px 8px;border:2px solid #fff;background:#000;color:#fff;font:700 12px ui-monospace,monospace;transform:none!important}.meditation-panel .breath-hud .breathing-ring-stop::before{content:\"Exit\"}\r\n.meditation-panel .breathing-session{transition:opacity .6s ease}.meditation-panel.hud-calm .breathing-session{opacity:.07;pointer-events:none}\r\n/* leap splash: a burst of water drops from the middle of the falls */\r\n.meditation-panel.coach-leap .meditation-stage::before{content:'';position:absolute;left:50%;top:48%;width:10px;height:10px;margin:-5px;border-radius:50%;z-index:6;pointer-events:none;background:#e9fbff;box-shadow:-60px -30px #bff3ff,60px -30px #e9fbff,-90px 10px #7fe3f5,90px 10px #bff3ff,-40px 50px #e9fbff,40px 50px #7fe3f5,0 -70px #bff3ff,-20px 80px #bff3ff,25px -55px #e9fbff,-75px -65px #7fe3f5,80px -60px #bff3ff;animation:coach-drops .7s ease-out forwards}\r\n@keyframes coach-drops{from{transform:scale(.2);opacity:1}to{transform:scale(3.2);opacity:0}}\r\n.meditation-panel.coach-leap .breathing-session{opacity:0;pointer-events:none}\r\n/* in the routed room the dock sits inside the panel: lift the chip and the character above it */\r\n/* R20: the room now ends at the 88px console's top edge (R19's 64px bar overlapped its last 24px): lift by 40px to\r\n   keep the chip and character where they stood on screen. */\r\n.meditation-panel:has(>#coachDock) .breath-hud{bottom:calc(40px + env(safe-area-inset-bottom) + 6px)}.meditation-panel:has(>#coachDock) .meditation-character{bottom:calc(6% + 40px + env(safe-area-inset-bottom))!important}\r\n@media(orientation:landscape){.meditation-panel .breath-hud{left:30%;right:auto}}.meditation-panel .breath-hud .breathing-ring-stop{min-width:44px;min-height:32px}\r\n\r\n/* R20 meditation: one Begin button, a settle countdown, the steady in/out cue, and a per-tick colour pulse. */\r\n.meditation-grey{transition:--reveal-r 60ms linear,opacity 2.5s ease}\r\n/* Begin sits bottom-right, above the chip, clear of the seated character at bottom-left */\r\n.meditation-panel .breathing-session .breath-modes{display:grid;grid-template-columns:1fr;justify-items:end}\r\n.meditation-panel .breathing-session .breath-modes[hidden]{display:none}\r\n.meditation-panel .breath-begin{min-height:56px;width:min(46vw,190px)!important;padding:0 20px;border:3px solid #fff;border-radius:0;background:#000;color:#fff;box-shadow:4px 4px #777;font:700 22px ui-monospace,monospace;letter-spacing:.12em;pointer-events:auto;cursor:pointer}\r\n.meditation-panel .breath-begin:focus-visible{outline:3px solid #fff;outline-offset:5px}\r\n.meditation-panel .breath-settle,.meditation-panel .breath-cue{position:absolute;left:50%;right:auto;width:max-content;transform:translateX(-50%);top:30%;z-index:5;margin:0;pointer-events:none;color:#fff;text-align:center;text-shadow:3px 3px #000}\r\n.meditation-panel .breath-settle{display:flex;justify-content:center;align-items:baseline;gap:12px}\r\n.meditation-panel .breath-settle[hidden],.meditation-panel .breath-cue[hidden]{display:none}\r\n.meditation-panel .breath-settle small{font:700 16px ui-monospace,monospace;letter-spacing:.2em;text-transform:uppercase}\r\n.meditation-panel .breath-settle b{font:900 min(72px,18vw,11vh)/1 ui-monospace,monospace}\r\n.meditation-panel .breath-cue{font:700 min(42px,8.5vw,8vh)/1 ui-monospace,monospace;opacity:1;animation:none}\r\n@media(max-height:700px){.meditation-panel .breath-settle,.meditation-panel .breath-cue{top:22%}}\r\n@media(orientation:landscape){.meditation-panel .breath-settle,.meditation-panel .breath-cue{left:27%;top:30%}}\r\n.meditation-panel:has([data-breath-modes]:not([hidden])) .breath-hud{visibility:hidden} /* start screen: the idle 3:30 chip sat under the safety note */\r\n/* R20: the portal's \"Tilt to look around\" tab sits on the dock's top edge, left; lift the counter chip and Begin clear of it */\r\n/* R20 frame: portrait only; in landscape the HUD and Begin sit right of the tab (left:30%+) and the shorter room has no 36px to spare. */\r\n@media(orientation:portrait){.meditation-panel:has(>.portal-peer-ui>.portal-tilt-chip) .breath-hud{translate:0 -36px}.meditation-panel:has(>.portal-peer-ui>.portal-tilt-chip) .breathing-session .breath-modes{translate:0 -44px}}\r\n","meditation.mjs":"import {mountTubFlight} from './arcade/tub-flight/game.mjs';\r\nimport {mountBreathing,bubble} from './breathing.mjs';\r\nimport {todaysBackground,averageRgb} from './meditation-backgrounds.mjs';\r\n// #149 (D46): the meditation track's starting boss and only mount, Four-legged 7, sleeps big behind you.\r\nexport const MEDITATION_COACH='roster/18-quad-all--robotic_dog_3d_model';\r\nconst SLEEP_YAW=-.9; // resting three-quarters on, head toward screen left, behind your character\r\nexport function mountMeditation({api,onComplete,getAccount,backgroundLookup}={}){\r\n const open=document.createElement('button');open.type='button';open.className='meditation-entry';open.innerHTML='<span>◌</span><strong>Meditation</strong><small aria-hidden=\"true\">→</small>';document.querySelector('.crew-footer').after(open);\r\n // #149: the room is three peering layers (data-peer-depth): far = the day's wonder, mid = the big sleeping coach,\r\n // near = your seated character, the ring and the caption. All black and white until a full session brings the colour back.\r\n const dialog=document.createElement('dialog');dialog.className='launch-panel meditation-panel';dialog.setAttribute('aria-labelledby','meditation-title');dialog.innerHTML='<header><h2 id=\"meditation-title\">The still room</h2><button data-meditation-close>Close</button></header><section data-meditation-scene><div class=\"meditation-stage\"><div class=\"meditation-layer meditation-far\" data-peer-depth=\"far\" aria-hidden=\"true\"></div><div class=\"meditation-layer meditation-mid\" data-peer-depth=\"mid\" aria-hidden=\"true\"><div class=\"meditation-platform\"></div><div class=\"meditation-coach\"></div></div><div class=\"meditation-layer meditation-near\" data-peer-depth=\"near\"><button class=\"meditation-character\" aria-label=\"Tap your Gala character\"><canvas width=\"64\" height=\"96\"></canvas></button><div class=\"forest-canopy\" aria-hidden=\"true\"></div><div class=\"meditation-can\" aria-hidden=\"true\"><b>FUEL</b><i></i></div><div class=\"meditation-powder\" aria-hidden=\"true\">· · · · ·</div><p class=\"meditation-speech\" role=\"status\">Breathe in. Breathe out.</p><p class=\"breath-cue\" data-breath-cue aria-hidden=\"true\" hidden></p><div class=\"breath-settle\" data-settle-countdown aria-hidden=\"true\" hidden><small>Settle</small><b></b></div></div><div class=\"meditation-grey\" aria-hidden=\"true\"></div></div><button data-breath-pause type=\"button\">Pause</button></section><section data-meditation-arcade hidden></section><div class=\"meditation-blackout\" aria-hidden=\"true\">'+Array.from({length:8},(_,i)=>'<i style=\"--strip:'+i+'\"></i>').join('')+'</div>';document.body.append(dialog);\r\n const scene=dialog.querySelector('[data-meditation-scene]'),stage=dialog.querySelector('.meditation-stage'),coachBox=dialog.querySelector('.meditation-coach'),character=dialog.querySelector('.meditation-character'),speech=dialog.querySelector('.meditation-speech'),arcade=dialog.querySelector('[data-meditation-arcade]'),breathPause=dialog.querySelector('[data-breath-pause]');let taps=0,game=null,timers=[],transitioning=false,epoch=0,borrowed=null,drag=null;\r\n // The room art is deliberately drawn from simple SVG shapes so it remains bundled, crisp, and offline.\r\n const far=dialog.querySelector('.meditation-far');far.innerHTML='<svg class=\"room-art\" viewBox=\"0 0 800 900\" preserveAspectRatio=\"xMidYMid slice\" aria-hidden=\"true\"><g fill=\"#050505\" stroke=\"#fff\" stroke-width=\"5\" stroke-linejoin=\"miter\"><path d=\"M0 650h800v250H0z\"/><path d=\"M36 612h36v-32h22v-36h22v-39h23v-37h26v37h22v39h22v36h34v32h22v288H36zM229 612h32v-35h20v-37h21v-43h24v-41h27v41h23v43h21v37h19v35h29v288H229zM419 612h35v-33h21v-38h22v-45h24v-42h27v42h23v45h22v38h21v33h32v288H419zM617 612h31v-34h19v-36h22v-42h24v-39h25v39h22v42h20v36h24v34h24v288H617z\"/></g><path d=\"M0 650h800M0 735h800M0 820h800\" stroke=\"#fff\" stroke-width=\"4\" stroke-dasharray=\"5 21\"/><g fill=\"#fff\"><path d=\"M95 140h28v28H95zM240 225h18v18h-18zM655 112h28v28h-28zM530 310h16v16h-16zM378 95h20v20h-20z\"/></g><circle cx=\"680\" cy=\"220\" r=\"58\" fill=\"none\" stroke=\"#fff\" stroke-width=\"5\"/></svg>';\r\n const platform=dialog.querySelector('.meditation-platform');platform.innerHTML='<svg viewBox=\"0 0 800 230\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><path d=\"M28 118 400 12l372 106-372 100z\" fill=\"#050505\" stroke=\"#fff\" stroke-width=\"8\"/><path d=\"M130 118 400 45l270 73-270 75z\" fill=\"none\" stroke=\"#777\" stroke-width=\"5\" stroke-dasharray=\"12 9\"/></svg>';\r\n const coachArt=document.createElementNS('http://www.w3.org/2000/svg','svg');coachArt.setAttribute('viewBox','0 0 800 500');coachArt.setAttribute('aria-hidden','true');coachArt.classList.add('coach-pixel');coachArt.innerHTML='<g fill=\"#050505\" stroke=\"#fff\" stroke-width=\"14\" stroke-linejoin=\"round\"><path d=\"M188 178 270 120 542 112 632 168 682 270 612 326 244 328 158 273z\"/><path d=\"m232 292-25 148h70l56-134M330 305l8 142h70l26-137M530 304l-2 143h68l37-143M615 293l62 139h67l-73-195\"/><path d=\"m173 194-86-70-55 22 91 132\"/><path d=\"m600 176 92-85 62 24-110 129\"/><path d=\"m608 176 35-78 68-10 46 43-35 75z\"/></g><g fill=\"#fff\"><path d=\"M682 139h25v25h-25zM731 139h25v25h-25z\"/></g>';\r\n coachBox.prepend(coachArt);character.innerHTML='<canvas width=\"64\" height=\"96\"></canvas>';\r\n const canopy=dialog.querySelector('.forest-canopy');canopy.innerHTML='<svg viewBox=\"0 0 800 180\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><path d=\"M0 126h800v54H0z\" fill=\"#000\"/><g fill=\"#000\" stroke=\"#fff\" stroke-width=\"6\" stroke-linejoin=\"miter\"><path d=\"M46 151h28v-24h20v-21h18V83h18V59h17v24h18v23h20v21h27v29zM237 151h24v-25h19v-22h18V79h18V53h18v26h18v25h19v22h25v25zM430 151h28v-24h19v-23h17V74h20V47h18v27h20v30h17v23h25v24zM622 151h24v-23h18v-22h17V79h20V57h17v22h18v27h19v22h29v23z\"/></g><path d=\"M157 119v54m171-53v54m214-55v55m157-52v52\" stroke=\"#fff\" stroke-width=\"7\"/><path d=\"M157 139 135 122m22 10 20-18m151 43-22-18m22 4 21-18m192 42-22-20m22 5 23-19m134 34-21-18m21 4 21-18\" stroke=\"#fff\" stroke-width=\"5\"/><path d=\"M0 165h800\" stroke=\"#fff\" stroke-width=\"4\" stroke-dasharray=\"8 18\"/></svg>';\r\n const stopCircle=document.createElement('button'),count=document.createElement('b'),sessionClock=document.createElement('small'),direction=document.createElement('i');count.className='breath-count';count.dataset.breathCount='';count.textContent='—';sessionClock.dataset.sessionClock='';sessionClock.textContent='3:30';direction.className='breath-direction';direction.dataset.breathDirection='';direction.textContent='SETTLE';stopCircle.type='button';stopCircle.className='breathing-ring-stop';stopCircle.dataset.breathingStop='';stopCircle.dataset.breathExit='';stopCircle.setAttribute('aria-label','Stop the breathing session and leave the room');stopCircle.hidden=true;const hud=document.createElement('div');hud.className='breath-hud';hud.append(count,sessionClock,direction,stopCircle);dialog.querySelector('.meditation-near').append(hud);\r\n const reducedMotion=matchMedia('(prefers-reduced-motion: reduce)');\r\n const later=(fn,ms)=>{const id=setTimeout(fn,ms);timers.push(id);};\r\n // Cancelled with the room: reset() clears the timer, so the awaiting sequence simply never resumes.\r\n const wait=ms=>new Promise(resolve=>later(resolve,ms));\r\n const say=line=>bubble(speech,line);\r\n function portrait(){character.dataset.expression=taps>=3?'focused':'calm';try{const avatar=window.GalaAvatar,canvas=character.querySelector('canvas');let look=avatar.defaultLook;try{look=avatar.normalize(JSON.parse(localStorage.getItem('mominc-avatar-v1')));}catch{}avatar.draw(canvas,look,{base:false,weapon:false,prop:false,blink:false,pose:{meditate:true}});}catch{}}\r\n\r\n function reset(){epoch++;timers.forEach(clearTimeout);timers=[];game?.dispose();game=null;taps=0;transitioning=false;scene.hidden=false;arcade.hidden=true;dialog.classList.remove('snorting','blacking-out','meditation-colour','coach-leap','coach-wander');peer(0,0);stage.dataset.annoyed='0';character.disabled=false;dialog.classList.remove('breathing-paused');breathPause.textContent='Pause';say('Breathe in. Breathe out.');portrait();}\r\n // D28: the day's \"wonders\" pack background when a caller-supplied lookup has it downloaded,\r\n // otherwise today's bundled starter wonder; the neutral gradient only shows if the image fails.\r\n async function applyBackground(){\r\n  const requestEpoch=epoch,{id,url}=await todaysBackground(backgroundLookup);let art=null;\r\n  if(url)try{const img=new Image();img.src=url;await img.decode();if(requestEpoch!==epoch||!dialog.open)return;const base=bottomColour(img);art={url:`url(\"${url}\")`,base,grey:base&&grey(base)};pixelateWonder(img);}catch{}\r\n  if(requestEpoch!==epoch||!dialog.open)return;\r\n   dialog.dataset.wonder=art?id:'';dialog.classList.toggle('has-wonder-art',!!art);starter.hidden=!!art;\r\n  for(const key of ['url','base','grey'])art?.[key]?dialog.style.setProperty('--wonder-'+key,art[key]):dialog.style.removeProperty('--wonder-'+key);\r\n }\r\n function pixelateWonder(img){try{const canvas=document.createElement('canvas');canvas.className='room-pixels';canvas.width=56;canvas.height=64;const ctx=canvas.getContext('2d',{willReadFrequently:true});ctx.imageSmoothingEnabled=false;ctx.drawImage(img,0,0,canvas.width,canvas.height);const pixels=ctx.getImageData(0,0,canvas.width,canvas.height);for(let i=0;i<pixels.data.length;i+=4){const y=Math.round(.2126*pixels.data[i]+.7152*pixels.data[i+1]+.0722*pixels.data[i+2]);const v=y<70?0:y<135?88:y<200?176:255;pixels.data[i]=pixels.data[i+1]=pixels.data[i+2]=v;}ctx.putImageData(pixels,0,0);far.prepend(canvas);}catch{}}\r\n // W2-2O: the starter wonders are Starter-pack art; without today's (offline, not downloaded) the plain room shows and the pack is offered.\r\n const starter=document.createElement('p'),get=document.createElement('button');starter.className='meditation-starter';starter.hidden=true;\r\n get.type='button';get.textContent='Download the Starter pack';get.onclick=()=>window.myr5Packs?.open('starter');\r\n starter.append('Backgrounds come with the Starter pack. ',get);stage.after(starter); // below the room: the caption now sits inside its near layer (#149)\r\n function bottomColour(img){try{const c=document.createElement('canvas');c.width=img.naturalWidth;c.height=1;const g=c.getContext('2d');g.drawImage(img,0,img.naturalHeight-1,img.naturalWidth,1,0,0,img.naturalWidth,1);return averageRgb(g.getImageData(0,0,c.width,1).data);}catch{return null;}}\r\n // The panel below the stage continues the wonder's bottom edge; while the room is black and white, in grey (grayscale() weights).\r\n function grey(rgb){const [r,g,b]=rgb.match(/\\d+/g).map(Number),y=Math.round(.2126*r+.7152*g+.0722*b);return `rgb(${y},${y},${y})`;}\r\n // The big sleeping coach: borrow the live companion card (creature/assets/phone.js, loaded here if the pod hasn't yet) into\r\n // the mid layer and show the meditation boss built on the user's own recipe, asleep. pod.mjs re-homes the card on any #view\r\n // change unless body.dataset.shipView is set, so the room holds that flag while it has the card, as the ship view does.\r\n async function borrowCoach(run){\r\n  let card=document.querySelector('.myr5-companion-card');\r\n  if(!card&&document.getElementById('view')){\r\n   if(!document.querySelector('link[href=\"/creature/phone.css\"]')){const link=document.createElement('link');link.rel='stylesheet';link.href='/creature/phone.css';document.head.append(link);}\r\n   try{await import('/creature/assets/phone.js');}catch{}card=document.querySelector('.myr5-companion-card');\r\n  }\r\n  const creature=window.myr5Creature;if(run!==epoch||!dialog.open||!card||!creature?.preview)return;\r\n  borrowed={card,home:card.parentElement,stage:creature.stats?.().stage,flag:document.body.dataset.shipView??''};\r\n  document.body.dataset.shipView='true';coachBox.append(card);card.hidden=true;coachArt.style.display='';creature.stage('overlay');creature.sleep(true);\r\n  let shown=false;try{shown=await creature.preview({body:MEDITATION_COACH,headFrom:MEDITATION_COACH,armsFrom:MEDITATION_COACH,feetFrom:MEDITATION_COACH});}catch{}\r\n  if(run===epoch&&dialog.open){if(shown){coachArt.style.display='none';card.hidden=false;creature.walk?.(false);creature.sleep?.(true);creature.face?.(SLEEP_YAW);creature.stage?.('overlay');creature.walk?.(false);creature.sleep?.(true);creature.face?.(SLEEP_YAW);}else{coachArt.style.display='';card.hidden=true;}}\r\n  // If the model is offline, the drawn four-legged room figure remains visible; never substitute the user's coach.\r\n }\r\n function releaseCoach(){\r\n  if(!borrowed)return;const {card,home,stage:was,flag}=borrowed,creature=window.myr5Creature;borrowed=null;card.hidden=false;\r\n  creature?.walk?.(false);creature?.face?.(0);creature?.sleep?.(false);void creature?.preview?.(null)?.catch?.(()=>{});\r\n  document.body.dataset.shipView=flag;(home?.isConnected?home:document.getElementById('coachMount')||document.getElementById('view')||document.body).append(card);\r\n  if(was)creature?.stage?.(was);\r\n }\r\n // A full session: colour fades back into every layer (CSS, ~2.5 s), then the coach wakes and wanders off screen right.\r\n async function wake(){\r\n  dialog.classList.add('meditation-colour');say('The colour comes back.');await wait(2500);\r\n  const creature=borrowed&&window.myr5Creature;creature?.sleep?.(false);creature?.face?.(Math.PI/2);creature?.walk?.(true);\r\n  dialog.classList.add('coach-wander');say('Your coach wakes up and wanders off. Well breathed.');await wait(3000);creature?.walk?.(false);\r\n }\r\n // D46 \"should still have the peering effect, even if slight\": drag and the layers slide by depth (far .3, mid .6, near 1, in CSS).\r\n // ponytail: drag only; tilt can drive the same --peer-x/--peer-y on the stage (2N's wrapper) when it lands.\r\n function peer(x,y){stage.style.setProperty('--peer-x',x+'px');stage.style.setProperty('--peer-y',y+'px');}\r\n const clamp=v=>Math.max(-24,Math.min(24,v*.25));\r\n stage.addEventListener('pointerdown',event=>{if(reducedMotion.matches)return;drag={x:event.clientX,y:event.clientY};stage.classList.add('peering');});\r\n stage.addEventListener('pointermove',event=>{if(drag)peer(clamp(event.clientX-drag.x),clamp(event.clientY-drag.y));});\r\n for(const type of ['pointerup','pointercancel','pointerleave'])stage.addEventListener(type,()=>{if(!drag)return;drag=null;stage.classList.remove('peering');peer(0,0);});\r\n open.onclick=()=>{document.getElementById('stop')?.click();reset();void applyBackground();dialog.showModal();void borrowCoach(epoch);};\r\n dialog.querySelector('[data-meditation-close]').onclick=()=>dialog.close();dialog.addEventListener('close',()=>{reset();releaseCoach();open.focus();});\r\n breathPause.onclick=()=>{const paused=dialog.classList.toggle('breathing-paused');breathPause.textContent=paused?'Resume':'Pause';};\r\n character.onclick=()=>{if(transitioning)return;taps++;stage.dataset.annoyed=String(Math.min(taps,5));portrait();const lines=['I am concentrating.','That is not part of the meditation.','You. Again.','One more poke. See what happens.','Fine. Emergency fuel.'];bubble(speech,lines[Math.min(taps-1,4)]);if(taps<5)return;transitioning=true;character.disabled=true;dialog.classList.add('snorting');later(()=>dialog.classList.add('blacking-out'),1800);later(()=>{scene.hidden=true;arcade.hidden=false;game=mountTubFlight(arcade,{title:'TUB FLIGHT',onComplete:()=>{const link=document.createElement('a');link.className='primary-action';link.href='https://mominc.online/games/fuel/#fuelFlight=myr5-eight-pipes';link.target='_blank';link.rel='noopener';link.textContent='Your tub is full. Open Goon Fuel →';arcade.append(link);}});dialog.classList.remove('snorting','blacking-out');transitioning=false;arcade.querySelector('canvas').focus();},3000);};\r\n mountBreathing({dialog,scene,pause:breathPause,api,getAccount,onComplete,onSessionComplete:()=>{if(dialog.open)void wake();},onEarlyExit:async({isCurrent=()=>true}={})=>{const run=epoch;dialog.classList.add('coach-leap');await wait(700);if(run!==epoch||!dialog.open)return;dialog.classList.remove('coach-leap');if(isCurrent())dialog.close();}});\r\n window.addEventListener('pagehide',()=>{epoch++;timers.forEach(clearTimeout);timers=[];game?.dispose();game=null;if(dialog.open)dialog.close();else releaseCoach();});\r\n}\r\n","menu-lifecycle.mjs":"// Shared menu lifetime boundary. Route code calls enter() after adoption and leave() on finish.\r\n// Native close, hidden panels, and pagehide are covered as fallbacks for legacy openers.\r\nconst isElement=value=>!!value&&typeof value==='object'&&typeof value.querySelectorAll==='function';\r\n\r\nexport function mountMenuLifecycle({doc=globalThis.document,win=globalThis.window,routes=win?.myr5Routes?.ROUTES}={}){\r\n if(!doc||!win)throw new Error('Menu lifecycle needs a document and window.');\r\n const routeEntries=Object.entries(routes||{}).filter(([,route])=>route?.dialog);\r\n const resources=new Map(),states=new Map(),frames=new Map();\r\n let disposed=false;\r\n const dialogs=()=>[...doc.querySelectorAll('dialog')];\r\n function key(owner){\r\n  if(typeof owner==='string')return owner;\r\n  if(!owner)return '';\r\n  if(owner.dataset?.route)return owner.dataset.route;\r\n  const match=routeEntries.find(([,route])=>owner.matches?.(route.dialog));\r\n  return match?.[0]||owner.id||owner;\r\n }\r\n function element(owner){\r\n  if(isElement(owner))return owner;\r\n  const route=routes?.[owner];\r\n  if(route?.dialog)return doc.querySelector(route.dialog);\r\n  return typeof owner==='string'?doc.getElementById?.(owner):null;\r\n }\r\n function state(owner){\r\n  const id=key(owner);\r\n  if(!states.has(id)){\r\n   const root=element(owner);\r\n   states.set(id,{active:root?.tagName==='DIALOG'?!!root.open:root?root.hidden!==true:true});\r\n  }\r\n  return states.get(id);\r\n }\r\n function items(root,selector){return [...(root?.matches?.(selector)?[root]:[]),...(root?.querySelectorAll?.(selector)||[])];}\r\n function pauseMedia(root){for(const media of items(root,'audio,video'))try{media.pause?.();}catch{}}\r\n function suspendFrame(frame){\r\n  if(frames.has(frame)){\r\n   const saved=frames.get(frame),src=frame.getAttribute?.('src'),srcdoc=frame.getAttribute?.('srcdoc');\r\n   if(src==null&&srcdoc==null){frames.delete(frame);return;}\r\n   if(srcdoc!=null){saved.srcdoc=srcdoc;frame.removeAttribute('srcdoc');}\r\n   if(src!=null&&src!=='about:blank'){saved.src=src;frame.setAttribute('src','about:blank');}\r\n   return;\r\n  }\r\n  const src=frame.getAttribute?.('src'),srcdoc=frame.getAttribute?.('srcdoc');\r\n  if(src==null&&srcdoc==null)return;\r\n  frames.set(frame,{src,srcdoc});\r\n  frame.removeAttribute('srcdoc');\r\n  frame.setAttribute('src','about:blank');\r\n }\r\n function restoreFrames(root){\r\n  for(const frame of items(root,'iframe')){\r\n   if(hidden(frame))continue;\r\n   const saved=frames.get(frame);if(!saved)continue;\r\n   frames.delete(frame);\r\n   frame.removeAttribute('src');\r\n   if(saved.srcdoc!=null)frame.setAttribute('srcdoc',saved.srcdoc);\r\n   if(saved.src!=null)frame.setAttribute('src',saved.src);\r\n  }\r\n }\r\n function suspend(root){pauseMedia(root);for(const frame of items(root,'iframe'))suspendFrame(frame);}\r\n function hidden(frame){\r\n  if(doc.hidden||frame.closest?.('[hidden]'))return true;\r\n  const dialog=frame.closest?.('dialog');\r\n  return !!dialog&&!dialog.open||!!frame.getClientRects&&!frame.getClientRects().length;\r\n }\r\n function scanHidden(){for(const frame of doc.querySelectorAll('iframe'))if(hidden(frame))suspendFrame(frame);}\r\n function event(owner,reason){\r\n  const detail={owner:key(owner),reason,element:element(owner)};\r\n  const EventType=win.CustomEvent||globalThis.CustomEvent;\r\n  if(EventType)win.dispatchEvent(new EventType('myr5:menu-leave',{detail}));\r\n }\r\n function leave(owner,reason='route-leave'){\r\n  if(disposed)return;\r\n  const id=key(owner),entry=state(id);if(!entry.active)return;\r\n  entry.active=false;\r\n  const root=element(owner);if(root)suspend(root);\r\n  for(const resource of resources.get(id)||[])if(resource.active){resource.active=false;try{resource.dispose?.(reason);}catch(error){win.console?.error?.('Menu resource disposal failed',error);}}\r\n  event(owner,reason);\r\n }\r\n function enter(owner){\r\n  if(disposed)return;\r\n  if(doc.hidden){const root=element(owner);if(root?.open)root.close();return;}\r\n  const id=key(owner),entry=state(id);entry.active=true;\r\n  const root=element(owner);if(root)restoreFrames(root);\r\n  for(const resource of resources.get(id)||[])if(!resource.active){resource.active=true;try{resource.reopen?.();}catch(error){win.console?.error?.('Menu resource reopen failed',error);}}\r\n }\r\n function closeInactive(current){\r\n  if(disposed)return;\r\n  const active=element(current);\r\n  for(const dialog of dialogs())if(dialog.open&&dialog!==active){leave(dialog,'route-switch');dialog.close();}\r\n  scanHidden();\r\n }\r\n function stopAll(reason='page-hidden'){\r\n  if(disposed)return;\r\n  for(const dialog of dialogs())if(dialog.open){leave(dialog,reason);dialog.close();}\r\n  for(const [id,entry] of states)if(entry.active)leave(id,reason);\r\n  for(const media of doc.querySelectorAll('audio,video'))try{media.pause?.();}catch{}\r\n  for(const frame of doc.querySelectorAll('iframe'))suspendFrame(frame);\r\n }\r\n function registerMenuResource(owner,{dispose,reopen}={}){\r\n  const id=key(owner),record={dispose,reopen,active:state(id).active};\r\n  if(!resources.has(id))resources.set(id,new Set());\r\n  resources.get(id).add(record);\r\n  if(!record.active)try{dispose?.('registered-inactive');}catch(error){win.console?.error?.('Menu resource disposal failed',error);}\r\n  return ()=>resources.get(id)?.delete(record);\r\n }\r\n const onClose=e=>{if(e.target?.tagName==='DIALOG'&&!e.target.open)leave(e.target,'dialog-close');};\r\n const onVisibility=()=>{if(doc.hidden)stopAll('page-hidden');};\r\n const onPageHide=()=>stopAll('pagehide');\r\n doc.addEventListener('close',onClose,true);\r\n doc.addEventListener('visibilitychange',onVisibility);\r\n win.addEventListener('pagehide',onPageHide);\r\n const Observer=win.MutationObserver||globalThis.MutationObserver;\r\n const observer=Observer?new Observer(records=>{\r\n  for(const record of records){\r\n   if(record.type==='attributes'&&record.target?.tagName==='DIALOG'&&record.attributeName==='open'&&record.target.open)enter(record.target);\r\n  }\r\n  scanHidden();\r\n }):null;\r\n observer?.observe(doc.documentElement||doc,{subtree:true,childList:true,attributes:true,attributeFilter:['open','hidden','src','srcdoc']});\r\n scanHidden();\r\n const api={enter,leave,closeInactive,stopAll,registerMenuResource,scanHidden,dispose(){if(disposed)return;stopAll('lifecycle-dispose');disposed=true;observer?.disconnect();doc.removeEventListener('close',onClose,true);doc.removeEventListener('visibilitychange',onVisibility);win.removeEventListener('pagehide',onPageHide);resources.clear();states.clear();frames.clear();}};\r\n return api;\r\n}\r\n","menu.css":"/* Fixed glass panels: depth belongs to the surface; rotation belongs to models. */\r\n.voice-row{display:flex;align-items:center;gap:10px;margin:6px 0}.voice-row button{background:#24443d;color:#d8fff0;padding:7px 12px;min-height:40px}.voice-row span{font-size:10px;color:#c1d6d2}#coachCaption{font-size:12px;color:#afffe6;margin:4px 0;line-height:1.35}#confirmProgress{width:100%;height:6px;accent-color:#9affdf;margin-top:10px}.intro-cue{padding:12px;border:1px solid #aaffdd44;border-radius:12px;background:#193c35b0;color:#e1fff5;font-size:13px;line-height:1.5}#holoStage{max-height:420px}#handHelp summary{font-size:12px;color:#c8efe6;padding:6px 0}\r\n.library-launch{width:100%;display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:8px;background:#31253e;color:#f3e8ff;text-align:left}\r\n.library-launch span{font-size:10px;font-weight:400;color:#d8c6e9}\r\n#library{padding:0;border:0;width:min(900px,100vw);max-width:100vw;height:100dvh;max-height:100dvh;margin:auto;background:radial-gradient(ellipse at 15% 0%,#34404d99,transparent 55%),radial-gradient(ellipse at 90% 90%,#52316199,transparent 50%),#10141cf2;color:#f5faff;overflow:auto}\r\n#library::backdrop{background:#080b14bd;backdrop-filter:blur(8px)}\r\n#handVideo{position:fixed;inset:0;width:100%;height:100%;object-fit:cover;opacity:.32;pointer-events:none}\r\n.glass-shell{position:relative;z-index:1;padding:20px;max-width:900px;margin:auto;min-height:100%;display:flex;flex-direction:column;gap:16px}\r\n.library-header{display:flex;align-items:center;justify-content:space-between;gap:14px;flex:none}.library-header small{color:#8cebe2;font-size:10px;letter-spacing:2px}.library-header h1{font-size:24px;letter-spacing:-.8px;margin:5px 0 0;font-weight:650}\r\n#library button{min-height:52px;border:1px solid #bae9f14d;background:linear-gradient(135deg,#d2eaf91e,#b6bdd909),#172131b5;color:#f5faff;border-radius:14px;font-size:14px;box-shadow:inset 0 1px #f2ffff14,0 8px 24px #0002;backdrop-filter:blur(14px);cursor:pointer;touch-action:manipulation}\r\n#library button:focus-visible{outline:3px solid #aaffef;outline-offset:3px}#library button:hover{border-color:#b4f8ecb3}#library button.hand-hover{border-color:#b4f8ec;box-shadow:0 0 0 3px #aaffef33,inset 0 1px #fff3}#library button.hand-down{background:#3c736ac9;color:#fff}\r\n#closeLibrary{min-width:66px}.movement-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;flex:1;align-content:center}\r\n.movement-card{padding:15px 14px;border:1px solid #bde7f638;border-radius:19px;background:linear-gradient(145deg,#d7e9ff13,#a3c2e804),#142031a1;box-shadow:inset 0 1px #fff1,0 12px 24px #0002;backdrop-filter:blur(16px);min-width:0}\r\n.movement-card .card-select{width:100%;text-align:left;min-height:65px!important;background:transparent!important;border:none!important;box-shadow:none!important;padding:0 5px!important;backdrop-filter:none!important}.card-number{display:block;font-size:10px;color:#88c6c6;font-weight:400;letter-spacing:1px;margin-bottom:9px}.card-title{display:block;font-size:18px;line-height:1.15}.card-kind{font-size:11px;font-weight:400;color:#c4d4e7;display:block;margin-top:7px}\r\n.movement-card .holo-link{width:100%;font-size:12px!important;margin-top:10px;min-height:48px!important;color:#aaf7ed!important}\r\n.library-footer{flex:none;border-top:1px solid #c1e2ff22;padding-top:13px}.hand-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.hand-row button{padding:10px 15px}#handState{font-size:12px;color:#a2e9dd;max-width:40ch}.library-footer p,.library-caption{font-size:12px;line-height:1.5;color:#bccddd;margin:9px 0 0}\r\n.library-pages{display:flex;align-items:center;gap:18px;justify-content:space-between}.library-pages button{min-width:100px}.library-pages span{color:#b9d6dc;font-size:12px}\r\n#startFromLibrary{margin-bottom:12px}.card-select[aria-pressed=true] .card-title{color:#9fffe8}.card-select[aria-pressed=true] .card-kind::before{content:'Selected · ';color:#9fffe8}\r\n#handCursor{position:fixed;width:28px;height:28px;z-index:100;pointer-events:none;border:2px solid #b3fff1;border-radius:50%;transform:translate(-50%,-50%);background:#aaffef30;box-shadow:0 0 20px #aaffef55}#handCursor.pinched{width:18px;height:18px;background:#b3fff1}\r\n#hologramPanel{flex:1;display:flex;flex-direction:column;gap:10px}.holo-heading{display:flex;align-items:center;gap:16px}.holo-heading button{min-width:70px}.holo-heading h2{font-size:18px;font-weight:600}\r\n#holoStage{position:relative;flex:1;min-height:230px;overflow:hidden;border:1px solid #b9ecfa33;border-radius:24px;touch-action:none;background:radial-gradient(ellipse at 50% 85%,#69f9dd1a,transparent 55%),linear-gradient(#b4e9ff0c 1px,transparent 1px),linear-gradient(90deg,#b4e9ff0c 1px,transparent 1px),#0c17207d;background-size:auto,32px 32px,32px 32px,auto;cursor:grab}\r\n#holoStage canvas{position:absolute;inset:0;width:100%;height:100%;pointer-events:auto}#holoStatus{position:absolute;inset:16px;z-index:2;font-size:13px;color:#c5ebe9;pointer-events:none}.holo-tools{display:flex;gap:8px;flex-wrap:wrap}.holo-tools button{flex:1;padding:8px;min-width:52px}#holoPlay,#holoReset{flex:2}.use-movement{width:100%;background:#426f6588!important;border-color:#aaffef88!important}\r\n@media(max-width:480px){.glass-shell{padding:16px;gap:13px}.library-header h1{font-size:22px}.movement-cards{gap:9px}.movement-card{padding:10px}.card-title{font-size:16px}.card-kind{font-size:11px}#handState{font-size:11px}.library-launch span{max-width:135px;text-align:right}}\r\n@media(orientation:landscape) and (min-width:700px){.movement-cards{grid-template-columns:repeat(4,minmax(0,1fr))}.glass-shell{padding:18px 24px}#holoStage{min-height:280px}}\r\n@media(prefers-reduced-transparency:reduce){#library button,.movement-card{background:#182636;backdrop-filter:none}#handVideo{opacity:.1}}\r\n","menu.mjs":"// Fixed glass library with spoken proposals and a hologram before every start.\r\nimport {EXERCISES,FOCUS_GROUPS,GROUP_EXERCISES,focusFor} from './exercise-library.mjs';\r\nimport {movementSetup} from './movement-setup.mjs';\r\nimport {WORKOUT_KINDS,workoutKind,workoutChoices,workoutLevel} from './workout-levels.mjs';\r\nconst $=id=>document.getElementById(id);\r\nexport function initLibrary({movements,onOpen,onSelect,onStart,camera,movement,voice,canUse=()=>true,onLocked}){\r\n const dialog=$('library');let hands=null,viewer=null,viewerGeneration=0,handGeneration=0,introGeneration=0,introTimer=null,resolveWait=null,introducing=false,selected='squat',page=0,filter='legs',kind='reps';\r\n const kindLabel=document.createElement('label');kindLabel.className='library-kind';kindLabel.textContent='Training type';const kindSelect=document.createElement('select');kindSelect.id='libraryKind';for(const item of WORKOUT_KINDS){const o=document.createElement('option');o.value=item.id;o.textContent=item.name;kindSelect.append(o);}kindLabel.append(kindSelect);$('libraryFocus').closest('label').before(kindLabel);kindSelect.value=kind;kindSelect.onchange=()=>{kind=kindSelect.value;page=0;paginate();};\r\n // The setup is terminal text on the hologram glass, never a separate panel.\r\n const setupPanel=$('hologramPanel').querySelector('.holo-setup');$('holoStage').append(setupPanel);\r\n for(const g of FOCUS_GROUPS){const o=document.createElement('option');o.value=g.id;o.textContent=g.name;$('libraryFocus').append(o);}\r\n $('libraryFocus').addEventListener('change',()=>{filter=$('libraryFocus').value;page=0;paginate();});\r\n const speak=(text,options={})=>voice.say(text,options);\r\n function cancelIntro(){introGeneration++;introducing=false;clearTimeout(introTimer);resolveWait?.();resolveWait=null;voice.cancel();$('introCue').hidden=true;}\r\n function releaseViewer(){viewerGeneration++;viewer?.dispose();viewer=null;}\r\n function stopHands(message='Touch controls ready'){handGeneration++;hands?.stop();hands=null;$('toggleHands').disabled=false;$('toggleHands').textContent='Enable exercise gestures';$('toggleHands').setAttribute('aria-pressed','false');$('handState').textContent=message;$('confirmProgress').value=0;}\r\n function selection(id){if(id==='jumping')id='jumping-jack';selected=id;$('startFromLibrary').textContent='Start '+movements[id].name;for(const button of $('movementCards').querySelectorAll('[data-movement]'))button.setAttribute('aria-pressed',String(button.dataset.movement===id));}\r\n function refreshAccess(){for(const option of $('libraryFocus').options){const first=GROUP_EXERCISES[option.value]?.[0];const allowed=!!first&&canUse(first.id);option.disabled=!allowed;option.textContent=FOCUS_GROUPS.find(group=>group.id===option.value)?.name+(allowed?'':' · locked');}for(const button of $('movementCards').querySelectorAll('[data-movement]')){const allowed=canUse(button.dataset.movement);button.disabled=!allowed;button.setAttribute('aria-disabled',String(!allowed));}if(!GROUP_EXERCISES[filter]?.some(exercise=>canUse(exercise.id))){const first=FOCUS_GROUPS.find(group=>GROUP_EXERCISES[group.id]?.some(exercise=>canUse(exercise.id)));if(first){filter=first.id;$('libraryFocus').value=filter;page=0;}}paginate();}\r\n function home(){delete dialog.dataset.workoutBegin;dialog.dataset.preview='false';filter=focusFor(selected);kind=workoutKind(selected);kindSelect.value=kind;$('libraryFocus').value=filter;page=Math.max(0,Math.floor(workoutChoices(filter,kind).findIndex(m=>m.id===selected)/4));paginate();cancelIntro();releaseViewer();$('movementCards').hidden=false;$('startFromLibrary').hidden=true;$('hologramPanel').hidden=true;$('gestureArea').hidden=false;dialog.scrollTop=0;if(dialog.open)$('movementCards').querySelector(`[data-movement=\"${selected}\"]`)?.focus();}\r\n for(const [id,m] of Object.entries(EXERCISES)){\r\n  const card=document.createElement('article');card.className='movement-card';card.dataset.group=focusFor(id);card.dataset.kind=workoutKind(id);const level=workoutLevel(id);card.dataset.difficulty=level.difficulty;\r\n  const button=document.createElement('button');button.className='card-select';button.dataset.movement=id;button.setAttribute('aria-pressed','false');\r\n  button.setAttribute('aria-label',`${level.label} ${m.name}, ${level.kind}`);button.title=m.name;\r\n  const poster=document.createElement('img');poster.src=`/models/previews/${id}.png`;poster.alt='';poster.width=512;poster.height=512;poster.loading='lazy';poster.decoding='async';button.append(poster);\r\n  const title=document.createElement('strong');title.className='movement-level';title.textContent=`${level.label} \\u00b7 ${m.name}`;button.append(title);button.addEventListener('click',()=>{cancelIntro();showModel(id);});card.append(button);$('movementCards').append(card);\r\n }\r\n function paginate(){const cards=[...$('movementCards').children].filter(c=>c.dataset.group===filter&&c.dataset.kind===kind&&canUse(c.querySelector('[data-movement]').dataset.movement));const pages=Math.max(1,Math.ceil(cards.length/4));page=Math.max(0,Math.min(page,pages-1));for(const card of $('movementCards').children)card.hidden=true;cards.slice(page*4,page*4+4).forEach(c=>c.hidden=false);$('pageNumber').textContent=(page+1)+' / '+pages;$('previousPage').disabled=page===0;$('nextPage').disabled=page===pages-1;$('libraryPages').hidden=pages===1;let empty=document.getElementById('libraryEmpty');if(!empty){empty=document.createElement('p');empty.id='libraryEmpty';empty.setAttribute('role','status');$('movementCards').after(empty);}empty.textContent=`No available ${WORKOUT_KINDS.find(k=>k.id===kind).name.toLowerCase()} in this focus. Choose another focus or unlock this path.`;empty.hidden=cards.length>0;}\r\n $('previousPage').addEventListener('click',()=>{page--;paginate();});$('nextPage').addEventListener('click',()=>{page++;paginate();});\r\n $('libraryFocus').value=filter;paginate();\r\n async function showModel(id){\r\n  if(id==='jumping')id='jumping-jack';\r\n  if(!canUse(id))return false;\r\n  const setup=movementSetup(EXERCISES[id]);\r\n  $('holoCameraPosition').textContent=setup.position;$('holoCameraPlacement').textContent=setup.placement;\r\n  $('holoFrameNote').textContent=setup.framing;\r\n  $('holoVisibleJoints').replaceChildren(...setup.joints.map(name=>{const li=document.createElement('li');li.textContent=name;return li;}));\r\n  $('holoStage').setAttribute('aria-label',`${movements[id].name} example. Camera: ${setup.position}. ${setup.placement}. Keep ${setup.joints.join(', ')} visible. ${setup.framing}. Drag to rotate.`);\r\n  dialog.dataset.preview='true';stopHands();releaseViewer();const run=viewerGeneration;selection(id);onSelect(id);$('movementCards').hidden=true;$('libraryPages').hidden=true;$('startFromLibrary').hidden=true;$('gestureArea').hidden=true;$('hologramPanel').hidden=false;$('holoName').textContent=`${workoutLevel(id).label} \\u00b7 ${movements[id].name}`;$('holoStatus').hidden=false;$('holoStatus').textContent='Loading hologram…';$('holoPlay').textContent='Pause animation';$('useHologram').disabled=false;$('useHologram').textContent='Begin';dialog.scrollTop=0;\r\n  $('backLibrary').focus();\r\n  try{const {createHologram}=await import('./hologram.mjs');if(run!==viewerGeneration||!dialog.open)return false;\r\n   const instance=await createHologram($('holoStage'),id);if(run!==viewerGeneration||!dialog.open){instance.dispose();return false;}viewer=instance;$('holoStatus').hidden=true;$('useHologram').disabled=false;return true;\r\n  }catch(error){if(run===viewerGeneration){$('holoStatus').textContent='Hologram could not load. '+error.message;$('useHologram').textContent='Begin';$('useHologram').disabled=false;}return false;}\r\n }\r\n function begin(){if(!canUse(selected))return;cancelIntro();stopHands();releaseViewer();dialog.dataset.workoutBegin='true';dialog.close();onStart();}\r\n async function introduce(id=movement()){\r\n  if(id==='jumping')id='jumping-jack';\r\n  if(!canUse(id))return false;\r\n  void import('./modules/vault/vault-store.mjs').then(m=>m.bump('library-open',1,{key:id})).catch(()=>{});onOpen();cancelIntro();stopHands();selection(id);onSelect(id);if(!dialog.open)dialog.showModal();introducing=true;\r\n  await showModel(id);\r\n }\r\n function gesture(event){\r\n  $('confirmProgress').value=event.progress;\r\n  if(event.mode&&['proposed','holding','pending','confirmed'].includes(event.event)&&!canUse(event.mode)){\r\n   const message='That workout path is locked. Choose one of your available paths.';\r\n   if(event.event==='confirmed')stopHands(message);else $('handState').textContent=message;\r\n   return;\r\n  }\r\n  if(event.event==='proposed'){selection(event.mode);$('handState').textContent=movements[event.mode].name+'? Hold thumbs up to confirm.';}\r\n  else if(event.event==='holding')$('handState').textContent=`Hold thumbs up… ${Math.round(event.progress*100)}%`;\r\n  else if(event.event==='pending')$('handState').textContent=movements[event.mode].name+'? Hold thumbs up for a moment.';\r\n  else if(event.event==='confirmed'){stopHands();introduce(event.mode);}\r\n  else if(event.event==='expired'){$('handState').textContent='Selection expired. Make another gesture.';speak('Selection cancelled. Make another gesture.',{interrupt:true});}\r\n  else if(event.event==='idle')$('handState').textContent='Make an exercise gesture.';\r\n }\r\n $('toggleHands').addEventListener('click',async()=>{\r\n  if(hands){stopHands();speak('Exercise gestures off.',{interrupt:true});return;}\r\n  const run=++handGeneration;$('toggleHands').disabled=true;$('handState').textContent='Opening exercise gestures…';speak('Exercise gestures. Show your gesture, then hold thumbs up to confirm.',{interrupt:true});\r\n  try{const {HandControl}=await import('./gesture-controls.mjs');if(run!==handGeneration||!dialog.open)return;\r\n   const control=new HandControl({video:$('handVideo'),camera:camera(),message:text=>$('handState').textContent=text,onGesture:gesture,onError:text=>stopHands(text)});hands=control;await control.start();\r\n   if(run!==handGeneration||!dialog.open){control.stop();return;}$('toggleHands').disabled=false;$('toggleHands').textContent='Disable exercise gestures';$('toggleHands').setAttribute('aria-pressed','true');\r\n  }catch(error){if(run===handGeneration){stopHands(error.message);speak('Gesture tracking unavailable. Touch controls are ready.',{interrupt:true});}}\r\n });\r\n $('openLibrary').addEventListener('click',()=>{onOpen();const current=movement(),first=Object.keys(EXERCISES).find(id=>canUse(id));if(!first){onLocked?.();return;}selection(canUse(current)?current:first);home();refreshAccess();dialog.showModal();speak('Choose your movement.',{interrupt:true});});\r\n $('startFromLibrary').addEventListener('click',()=>introduce(selected));\r\n $('closeLibrary').addEventListener('click',()=>{cancelIntro();dialog.close();speak('Library closed.',{interrupt:true});});\r\n $('backLibrary').addEventListener('click',()=>{home();speak('Movement library.',{interrupt:true});});\r\n $('useHologram').addEventListener('click',()=>begin());\r\n $('holoReset').addEventListener('click',()=>{viewer?.reset();if(!introducing)speak('View reset.');}); $('holoPlay').addEventListener('click',()=>{if(viewer){if(introducing)cancelIntro();const playing=viewer.toggle();$('holoPlay').textContent=playing?'Pause animation':'Play animation';$('useHologram').textContent='Begin';speak(playing?'Example playing.':'Example paused.');}});\r\n dialog.addEventListener('cancel',cancelIntro);\r\n dialog.addEventListener('close',()=>{stopHands();releaseViewer();$('openLibrary').focus();});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden&&dialog.open){cancelIntro();stopHands('Gestures paused');releaseViewer();home();}});\r\n window.addEventListener('pagehide',()=>{cancelIntro();stopHands();releaseViewer();});\r\n window.addEventListener('myr5:performance-progress',refreshAccess);window.addEventListener('myr5:account-ready',refreshAccess);refreshAccess();\r\n return {introduce,refreshAccess};\r\n}\r\n","modules/coach-flight/account.mjs":"// Profile polling can announce a cleared account while already signed out.\r\n// Compare identity/epoch so a guest flight survives that harmless refresh.\r\nexport const flightAccountStamp=win=>({id:win?.myr5AuthenticatedAccount?.user?.id??null,epoch:win?.myr5AuthenticatedAccount?.dataEpoch??null});\r\nexport function flightAccountChanged(before,win,type){const after=flightAccountStamp(win);return type==='myr5:auth-transition'||before.id!==after.id||before.epoch!==after.epoch;}\r\n","modules/coach-flight/base-sprawl.mjs":"// This is a visual overlay over the saved base. The builder and its road graph own all edits.\r\nimport {createTerrain} from '../coach-world/terrain.mjs';\r\nimport {createBaseMeshes,spawnTile,SCALE} from '../coach-world-rts/base-meshes.mjs';\r\n\r\nconst OUTPOST_POSITIONS={gym:{x:27,z:-20,edge:24},work:{x:-27,z:20,edge:0}};\r\n\r\nexport function pathPlan(base){\r\n const terrain=createTerrain(base);\r\n const paths=[...terrain.roads].sort((a,b)=>a-b).map(k=>{\r\n  const cell=terrain.cells[k];return {x:cell.x,y:cell.y,kind:cell.kind};\r\n });\r\n const outposts=Object.keys(OUTPOST_POSITIONS).map(slot=>base.outposts?.[slot]).filter(p=>p?.buildings&&p.faction);\r\n const outpostSignatures=Object.fromEntries(outposts.map(p=>{\r\n  const buildings=Object.entries(p.buildings).sort(([a],[b])=>a.localeCompare(b))\r\n   .map(([id,b])=>`${id}:${b.level}:${b.x}:${b.y}:${b.track??''}`).join(',');\r\n  return [p.slot,`${base.seed}:${p.slot}:${p.faction}:${p.place?.latitude}:${p.place?.longitude}:${buildings}`];\r\n }));\r\n return {paths,outposts,pathSignature:terrain.signature,outpostSignatures,\r\n  signature:`${terrain.signature}|${Object.values(outpostSignatures).join('|')}`};\r\n}\r\n\r\nexport function createBaseSprawl(T,{getBase,scale=SCALE}={}){\r\n const group=new T.Group();group.name='base-path-connections';let signature='',pathSignature='',pathPart=null,dead=false;\r\n const outpostParts=new Map();\r\n function buildPaths(plan){\r\n  pathPart?.dispose();const pathGroup=new T.Group(),parts=[];pathGroup.name='base-paths';group.add(pathGroup);\r\n  const sp=spawnTile(scale),ordinary=plan.paths.filter(p=>p.kind!=='bridge'),bridges=plan.paths.filter(p=>p.kind==='bridge');\r\n  const pathMaterial=new T.MeshLambertMaterial({color:0x98a398,side:T.DoubleSide,flatShading:true});\r\n  function tiles(cells,geometry,y,name){\r\n   if(!cells.length){geometry.dispose();return;}\r\n   const mesh=new T.InstancedMesh(geometry,pathMaterial,cells.length),d=new T.Object3D();mesh.name=name;\r\n   cells.forEach((p,i)=>{d.position.set((p.x+.5-sp.x)*scale,y,(p.y+.5-sp.y)*scale);d.updateMatrix();mesh.setMatrixAt(i,d.matrix);});\r\n   mesh.instanceMatrix.needsUpdate=true;pathGroup.add(mesh);parts.push({dispose(){mesh.dispose();geometry.dispose();}});\r\n  }\r\n  tiles(ordinary,new T.PlaneGeometry(.85*scale,.85*scale).rotateX(-Math.PI/2),.055,'base-path-tiles');\r\n  // A raised bridge is only needed on the water cells supplied by the existing road graph.\r\n  tiles(bridges,new T.BoxGeometry(.85*scale,.12,.85*scale),.075,'base-bridge-tiles');\r\n  parts.push({dispose(){pathMaterial.dispose();}});\r\n  pathPart={dispose(){for(const part of parts)part.dispose();pathGroup.removeFromParent();pathGroup.clear();}};\r\n  pathSignature=plan.pathSignature;\r\n }\r\n function buildOutpost(outpost){\r\n   const pos=OUTPOST_POSITIONS[outpost.slot];if(!pos)return null;\r\n   const outpostGroup=new T.Group(),parts=[];outpostGroup.name=`outpost-${outpost.slot}`;group.add(outpostGroup);\r\n   const sp=spawnTile(scale);\r\n   const base=getBase(),x=(12-sp.x)*scale+pos.x,z=(12-sp.y)*scale+pos.z;\r\n   const pad=new T.Mesh(new T.CylinderGeometry(10,11,.3,12),new T.MeshLambertMaterial({color:0x98a398,flatShading:true}));\r\n   pad.name=`outpost-${outpost.slot}-pad`;pad.position.set(x,-.05,z);outpostGroup.add(pad);\r\n   parts.push({dispose(){pad.geometry.dispose();pad.material.dispose();}});\r\n   const buildings=createBaseMeshes(T,{...base,seed:`${base.seed}:${outpost.slot}`,faction:outpost.faction,buildings:outpost.buildings},{nearRadius:1e6,resolution:[320,180]});\r\n   // createBaseMeshes renders level-zero sites as foundation meshes. An outpost is a\r\n   // miniature of what has actually been built; omit those foundations here only.\r\n   for(const mesh of [...buildings.group.children]){\r\n    const id=mesh.name.slice(4);\r\n    if(mesh.name.startsWith('rts-')&&(outpost.buildings[id]?.level??0)<=0){mesh.removeFromParent();mesh.geometry?.dispose();}\r\n   }\r\n   buildings.group.name=`outpost-${outpost.slot}-buildings`;buildings.group.scale.setScalar(.3);buildings.group.position.set(x,0,z);\r\n   outpostGroup.add(buildings.group);parts.push(buildings);\r\n   const start={x:(pos.edge-sp.x)*scale,z:(12-sp.y)*scale},length=Math.hypot(x-start.x,z-start.z);\r\n   const trail=new T.Mesh(new T.BoxGeometry(1.1*scale,.08,length),new T.MeshLambertMaterial({color:0x98a398,flatShading:true}));\r\n   trail.name=`outpost-${outpost.slot}-trail`;trail.position.set((start.x+x)/2,.04,(start.z+z)/2);trail.rotation.y=Math.atan2(x-start.x,z-start.z);\r\n   outpostGroup.add(trail);parts.push({dispose(){trail.geometry.dispose();trail.material.dispose();}});\r\n   return {dispose(){for(const part of parts)part.dispose();outpostGroup.removeFromParent();outpostGroup.clear();}};\r\n }\r\n function rebuild(plan){\r\n  if(plan.pathSignature!==pathSignature)buildPaths(plan);\r\n  for(const [slot,old] of outpostParts){if(old.signature!==plan.outpostSignatures[slot]){old.dispose();outpostParts.delete(slot);}}\r\n  for(const outpost of plan.outposts){if(outpostParts.has(outpost.slot))continue;\r\n   const part=buildOutpost(outpost);if(part)outpostParts.set(outpost.slot,{signature:plan.outpostSignatures[outpost.slot],dispose:part.dispose});}\r\n  signature=plan.signature;\r\n }\r\n let last=-Infinity;\r\n function update(t=0){if(dead||t-last<500)return;last=t;const plan=pathPlan(getBase());if(plan.signature!==signature)rebuild(plan);}\r\n rebuild(pathPlan(getBase()));\r\n return {group,update,dispose(){if(dead)return;dead=true;pathPart?.dispose();for(const part of outpostParts.values())part.dispose();outpostParts.clear();group.removeFromParent();}};\r\n}\r\n","modules/coach-flight/boarding.mjs":"import {localVerifiedBridge} from '../ships/verified-ship-assets.mjs';\r\nimport {SHIP_STYLES,applyShipTint} from '../ships/ship-scene-domain.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {layoutBase} from '../coach-world-rts/base-meshes.mjs';\r\nexport function shipIdentity(base){const id=SHIP_STYLES.includes(base?.selectedShip)?base.selectedShip:'supportive';return {id,variant:base?.ships?.[id]?.variant||0,faction:base?.faction||'chest',tint:base?.shipColor||FACTION_BY_ID[base?.faction]?.color||null};}\r\nexport async function loadPilotShip(T,base,{alive=()=>true}={}){\r\n const identity=shipIdentity(base);let bridge=null,ship=null;\r\n try{bridge=await localVerifiedBridge();if(!alive()){bridge?.dispose();return null;}\r\n  const url=bridge?.ownedShipIds().includes(identity.id)?bridge.getShipUrl(identity.id):identity.id==='supportive'?'/pod/worlds/starter/supportive.glb':null;\r\n  if(url){const [{GLTFLoader},{MeshoptDecoder}]=await Promise.all([import('/vendor/three/GLTFLoader.js'),import('/vendor/three/meshopt_decoder.module.js')]);ship=(await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url)).scene;}\r\n }catch{/* Missing local pack keeps an honest schematic hull; no pack download. */}\r\n if(!alive()){ship?.traverse(o=>{o.geometry?.dispose();for(const m of [].concat(o.material||[]))m.dispose?.();});bridge?.dispose();return null;}\r\n if(!ship){ship=new T.Group();ship.name='schematic-'+identity.id;const m=new T.MeshLambertMaterial({color:0x7897aa,flatShading:true});const body=new T.Mesh(new T.ConeGeometry(.9,3,4),m);body.rotation.x=-Math.PI/2;ship.add(body);const wing=new T.Mesh(new T.BoxGeometry(4,.15,1.3),m);ship.add(wing);const cockpit=new T.Mesh(new T.BoxGeometry(.8,.5,1),new T.MeshBasicMaterial({color:0x8ce8e4}));cockpit.position.y=.6;ship.add(cockpit);ship.userData.schematic=true;}\r\n const size=new T.Box3().setFromObject(ship).getSize(new T.Vector3());ship.scale.multiplyScalar(6/Math.max(size.x,size.y,size.z,1));applyShipTint(ship,identity.tint);\r\n ship.userData.identity=identity;return {ship,identity,dispose(){ship.removeFromParent();const geos=new Set(),mats=new Set();ship.traverse(o=>{if(o.geometry)geos.add(o.geometry);for(const m of [].concat(o.material||[]))mats.add(m);});geos.forEach(g=>g.dispose());mats.forEach(m=>{for(const key of ['map','normalMap','emissiveMap'])m[key]?.dispose();m.dispose();});bridge?.dispose();}};\r\n}\r\nexport function attachBoarding(T,{scene,parking=scene,camera,canvas,root,getBase,onBoard}){\r\n let disposed=false,asset=null,placed=false,down=null;const ac=new AbortController(),doc=root.ownerDocument;\r\n const button=doc.createElement('button');button.textContent='Board ship';button.dataset.boardShip='';button.style.cssText='position:absolute;left:50%;bottom:130px;transform:translateX(-50%);z-index:4;min-height:48px';root.append(button);button.disabled=true;\r\n const ray=new T.Raycaster();\r\n void loadPilotShip(T,getBase(),{alive:()=>!disposed}).then(a=>{if(!a)return;if(disposed){a.dispose();return;}asset=a;const pad=layoutBase(getBase()).find(b=>b.kind==='landing');a.ship.position.set(pad?.x??12,2,pad?.z??-8);parking.add(a.ship);placed=true;button.disabled=false;button.textContent=`Board ${a.identity.id}${a.ship.userData.schematic?' · schematic':''}`;});\r\n function board(){if(disposed||!asset)return;button.disabled=true;try{doc.defaultView?.screen?.orientation?.lock?.('landscape')?.catch?.(()=>{});}catch{}onBoard?.();}\r\n button.onclick=board;\r\n canvas.addEventListener('pointerdown',e=>{down={x:e.clientX,y:e.clientY};},{signal:ac.signal});\r\n canvas.addEventListener('pointerup',e=>{if(!asset||!down||Math.hypot(e.clientX-down.x,e.clientY-down.y)>8)return;const r=canvas.getBoundingClientRect();ray.setFromCamera({x:(e.clientX-r.left)/r.width*2-1,y:1-(e.clientY-r.top)/r.height*2},camera);if(ray.intersectObject(asset.ship,true).length)board();down=null;},{signal:ac.signal});\r\n return {update(){},dispose(){disposed=true;ac.abort();asset?.dispose();button.remove();}};\r\n}\r\n","modules/coach-flight/city-scene.mjs":"import {createWorldLayer,BOUNDS} from '../coach-world-rts/world-layer.mjs';\r\nimport {ringCells,toLocal} from '../coach-world/world-tiles.mjs';\r\nimport {coveredCell} from './coverage.mjs';\r\n// Moving geographic residency. Each layer owns one 3x3 decoded ring and is disposed on rebase.\r\nexport const cityCovered=p=>!!p&&p.lng>=BOUNDS[0]+.01&&p.lng<=BOUNDS[2]-.01&&p.lat>=BOUNDS[1]+.01&&p.lat<=BOUNDS[3]-.01&&ringCells(p.lat,p.lng).every(coveredCell);\r\nexport async function loadCityScene(T,{origin,base,signal,fixture=false}={}){\r\n if(!cityCovered(origin))throw new Error('Detailed flight is available over Las Vegas only.');\r\n const group=new T.Group();group.name='flight-vegas';let dead=false,layer=null,clearance=450,exclusions=[];\r\n const dispose=()=>{if(dead)return;dead=true;layer?.dispose();group.removeFromParent();const gs=new Set(),ms=new Set();group.traverse(o=>{if(o.geometry)gs.add(o.geometry);for(const m of [].concat(o.material||[]))ms.add(m);});gs.forEach(g=>g.dispose());ms.forEach(m=>m.dispose());};\r\n const abort=()=>dispose();signal?.addEventListener('abort',abort,{once:true});\r\n try{\r\n  if(!fixture){const cells=ringCells(origin.lat,origin.lng),c=cells[4],r=await fetch(`/world/v1/vec/${c.z}/${c.x}/${c.y}.mvt.gz`,{signal,credentials:'omit'});if(!r.ok)throw new Error('Vegas map data unavailable. Retry or return to base.');\r\n   layer=createWorldLayer(T,{center:{x:0,z:0},scale:1,resolution:[640,360],compress:false,flight:true,getBase:()=>({...base,home:{latitude:origin.lat,longitude:origin.lng,consent:true}})});group.add(layer.group);layer.setFade(1);const result=await layer.ready;if(!result||dead)throw new Error('Vegas terrain did not load.');clearance=result.clearance;exclusions=result.exclusions;\r\n  }else{\r\n   const ground=new T.Mesh(new T.PlaneGeometry(1600,1600),new T.MeshLambertMaterial({color:0x78644a}));ground.rotation.x=-Math.PI/2;group.add(ground);\r\n   const geo=new T.BoxGeometry(18,1,20),mat=new T.MeshLambertMaterial({color:0x8eab9e,flatShading:true}),blocks=new T.InstancedMesh(geo,mat,90),d=new T.Object3D();for(let i=0;i<90;i++){const h=15+(i*37%90);d.position.set((i%10-5)*95,h/2,(Math.floor(i/10)-4)*95);d.scale.set(1,h,1);d.updateMatrix();blocks.setMatrixAt(i,d.matrix);}group.add(blocks);\r\n  }\r\n  // Coarse desert continues beyond detailed tile edges; no distant street simulation.\r\n  const sand=new T.Mesh(new T.PlaneGeometry(18000,18000),new T.MeshLambertMaterial({color:0x92745b}));sand.rotation.x=-Math.PI/2;sand.position.y=-55;group.add(sand);\r\n  const port=new T.Group();port.name='vegas-hyperspace-port';const metal=new T.MeshLambertMaterial({color:0x8b93a8,flatShading:true}),glow=new T.MeshBasicMaterial({color:0x82e8dd});const ring=new T.Mesh(new T.TorusGeometry(36,5,4,8),metal),light=new T.Mesh(new T.TorusGeometry(30,1.4,3,16),glow);port.add(ring,light);port.position.set(0,65,-180);const foot=new T.Mesh(new T.BoxGeometry(105,8,50),metal);foot.position.y=-55;port.add(foot);group.add(port);\r\n  const mg=new T.ConeGeometry(1,1,5),mm=new T.MeshLambertMaterial({color:0x6d5970,flatShading:true}),mountains=new T.InstancedMesh(mg,mm,28),d=new T.Object3D();for(let i=0;i<28;i++){const a=i/28*Math.PI*2;d.position.set(Math.cos(a)*4200,200,Math.sin(a)*4200);d.scale.set(450+i%3*180,500+i%4*140,600);d.updateMatrix();mountains.setMatrixAt(i,d.matrix);}group.add(mountains);\r\n  // V6: the far ring (real DEM out to ~10 km) replaces the coarse sand plane and the 28 cone mountains once it lands; they stay as the fallback\r\n  layer?.ringReady?.then(r=>{if(r&&!dead){sand.visible=false;mountains.visible=false;}});\r\n  // Macro landmark proxy stays visible even outside the nearest-building residency ring.\r\n  const stratAt=toLocal(origin,36.1475,-115.1566),tower=new T.Group();tower.name='strat-landmark-proxy';const shaft=new T.Mesh(new T.CylinderGeometry(5,10,260,6),metal);shaft.position.y=130;tower.add(shaft);const crown=new T.Mesh(new T.CylinderGeometry(26,18,30,8),metal);crown.position.y=270;tower.add(crown);const antenna=new T.Mesh(new T.CylinderGeometry(1,3,65,5),glow);antenna.position.y=317.5;tower.add(antenna);tower.position.set(stratAt.x,layer?.heightAt(stratAt.x,stratAt.z)||0,stratAt.z);group.add(tower);\r\n  // Optical hub beams are 900m tall but are not solid obstacles. Clearance comes from the\r\n  // full DEM/building envelope, plus the physical STRAT proxy, rather than visual effects.\r\n  const floor=Math.max(450,clearance,tower.position.y+350+70);\r\n  signal?.removeEventListener('abort',abort);return {group,origin:{...origin},floor,exclusions,heightAt:(x,z)=>layer?.heightAt(x,z)||0,update(t,camera){layer?.update(t,450,camera);light.rotation.z=t*.0001;},dispose};\r\n }catch(e){signal?.removeEventListener('abort',abort);dispose();throw e;}\r\n}\r\n","modules/coach-flight/cockpit-3d.mjs":"// Physical 3D cockpit controls (flight yoke + throttle dial) drawn into the EXISTING flight WebGLRenderer.\r\n// No second canvas/context: view.mjs calls render(renderer) right after the world render; this pass only clears depth.\r\n// Scene units are CSS pixels of the flight canvas box (1 unit = 1 css px, y up), orthographic, so a control placed in a\r\n// DOM hit-rect lands exactly on it. Depth comes from tilting each rig toward the viewer (top faces + rims show).\r\n// Contract with cockpit.mjs: ui.controlRects() -> {yoke,throttle:{x,y,width,height}} relative to the canvas/stage box,\r\n// ui.controlState() -> {yaw,pull,throttle,targetThrottle,locked}. Both fall back to ui.input / a default layout.\r\n// Budget: ~22 draw calls, ~5k triangles. Everything is built once at design size and only re-scaled on resize.\r\n\r\nconst TILT = 0.5;            // rad the control panels lean toward the pilot\r\nconst SWEEP = Math.PI * 1.5; // dial travel, 270 deg\r\nconst clamp = (v, a, b) => Math.max(a, Math.min(b, v));\r\nconst num = (v, d = 0) => (Number.isFinite(v) ? v : d);\r\n\r\nfunction springStep(s, target, dt, omega, zeta) {\r\n  const n = Math.max(1, Math.ceil(dt / 0.008)), h = dt / n;\r\n  for (let i = 0; i < n; i++) {\r\n    s.v += (omega * omega * (target - s.x) - 2 * zeta * omega * s.v) * h;\r\n    s.x += s.v * h;\r\n  }\r\n}\r\n\r\nfunction roundedRect(T, w, h, r) {\r\n  const s = new T.Shape(), x = -w / 2, y = -h / 2;\r\n  s.moveTo(x + r, y); s.lineTo(x + w - r, y); s.quadraticCurveTo(x + w, y, x + w, y + r);\r\n  s.lineTo(x + w, y + h - r); s.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\r\n  s.lineTo(x + r, y + h); s.quadraticCurveTo(x, y + h, x, y + h - r);\r\n  s.lineTo(x, y + r); s.quadraticCurveTo(x, y, x + r, y);\r\n  return s;\r\n}\r\n\r\nfunction gearShape(T, teeth, ro, ri) {\r\n  const s = new T.Shape(), w = (Math.PI * 2) / teeth;\r\n  for (let i = 0; i < teeth; i++) {\r\n    for (const [k, r] of [[0, ri], [0.15, ro], [0.45, ro], [0.6, ri]]) {\r\n      const a = (i + k) * w, x = Math.cos(a) * r, y = Math.sin(a) * r;\r\n      if (i === 0 && k === 0) s.moveTo(x, y); else s.lineTo(x, y);\r\n    }\r\n  }\r\n  s.closePath();\r\n  return s;\r\n}\r\n\r\n// Deterministic brushed-metal streaks (no canvas/DOM needed): bright/dark rows with fine grain.\r\nfunction brushedTexture(T) {\r\n  const W = 64, H = 64, data = new Uint8Array(W * H * 4);\r\n  let seed = 1337;\r\n  const rnd = () => ((seed = (seed * 1664525 + 1013904223) >>> 0) / 4294967296);\r\n  for (let y = 0; y < H; y++) {\r\n    const row = 150 + rnd() * 80;\r\n    for (let x = 0; x < W; x++) {\r\n      const v = clamp(row + (rnd() - 0.5) * 26, 0, 255), i = (y * W + x) * 4;\r\n      data[i] = data[i + 1] = data[i + 2] = v; data[i + 3] = 255;\r\n    }\r\n  }\r\n  const t = new T.DataTexture(data, W, H, T.RGBAFormat);\r\n  t.wrapS = t.wrapT = T.RepeatWrapping;\r\n  t.repeat.set(1 / 40, 1 / 40);\r\n  t.magFilter = T.LinearFilter; t.minFilter = T.LinearFilter;\r\n  t.needsUpdate = true;\r\n  return t;\r\n}\r\n\r\nexport function createCockpit3D(T, {ui, tilt = TILT} = {}) {\r\n  const scene = new T.Scene(), camera = new T.OrthographicCamera(0, 1, 1, 0, 1, 1500);\r\n  camera.position.z = 700;\r\n  const owned = []; // disposables\r\n  const own = (o) => { owned.push(o); return o; };\r\n  const tex = own(brushedTexture(T));\r\n  const std = (color, metalness, roughness, brushed = true) => own(new T.MeshStandardMaterial({\r\n    color, metalness, roughness, ...(brushed ? {roughnessMap: tex, bumpMap: tex, bumpScale: 0.8} : {}),\r\n  }));\r\n  const steel = std(0xa6b1bc, 0.6, 0.5), dark = std(0x4a535c, 0.65, 0.42), chrome = std(0xc3ccd3, 0.8, 0.3, false);\r\n  const rubber = std(0x222830, 0.05, 0.8, false), dialSteel = std(0x5b656f, 0.8, 0.34);\r\n  const amber = own(new T.MeshBasicMaterial({color: 0xffc247})), cyan = own(new T.MeshBasicMaterial({color: 0x59e3ff}));\r\n  const tickMat = own(new T.MeshBasicMaterial({color: 0xc9d3d9}));\r\n  const hazard = std(0xe0b020, 0.4, 0.5, false);\r\n  const plungerMat = own(new T.MeshStandardMaterial({color: 0xb5301d, metalness: 0.3, roughness: 0.4, emissive: 0xff5a1a, emissiveIntensity: 0.05}));\r\n  const geo = (g) => own(g);\r\n  const mesh = (g, m, parent, x = 0, y = 0, z = 0) => {\r\n    const o = new T.Mesh(geo(g), m); o.position.set(x, y, z); parent.add(o); return o;\r\n  };\r\n  const instanced = (g, m, parent, mats) => {\r\n    const o = new T.InstancedMesh(geo(g), m, mats.length);\r\n    mats.forEach((mx, i) => o.setMatrixAt(i, mx)); o.instanceMatrix.needsUpdate = true; parent.add(o); return o;\r\n  };\r\n  const at = (x, y, z, sx = 1, sy = 1, sz = 1) => new T.Matrix4().compose(\r\n    new T.Vector3(x, y, z), new T.Quaternion(), new T.Vector3(sx, sy, sz));\r\n\r\n  // Lights (independent of the world scene); dimmed when the cockpit is locked.\r\n  const hemi = new T.HemisphereLight(0xdfeaf4, 0x232a31, 1.5), key = new T.DirectionalLight(0xfff1dc, 3.2);\r\n  const rim = new T.DirectionalLight(0x7fd6ff, 1.1);\r\n  key.position.set(-250, 420, 600); rim.position.set(420, 120, 260);\r\n  scene.add(hemi, key, rim);\r\n  const lightBase = [hemi.intensity, key.intensity, rim.intensity];\r\n\r\n  // ---------- yoke ----------\r\n  const ySlot = new T.Group(), yRig = new T.Group();\r\n  yRig.rotation.x = tilt; ySlot.add(yRig); scene.add(ySlot);\r\n  const plateGeo = new T.ExtrudeGeometry(roundedRect(T, 186, 76, 14), {\r\n    depth: 8, bevelEnabled: true, bevelThickness: 2, bevelSize: 2, bevelSegments: 2, curveSegments: 4});\r\n  plateGeo.rotateX(-Math.PI / 2); plateGeo.translate(0, -10, 0);\r\n  mesh(plateGeo, steel, yRig);\r\n  instanced(new T.CylinderGeometry(4.5, 4.5, 3, 10), chrome, yRig,\r\n    [-78, 78].flatMap((x) => [-28, 28].map((z) => at(x, 0.5, z))));\r\n  mesh(new T.LatheGeometry(Array.from({length: 22}, (_, i) => {\r\n    const y = (i / 21) * 34; return new T.Vector2(19 - y * 0.12 + Math.sin((y / 34) * Math.PI * 8) * 2.6, y);\r\n  }), 14), rubber, yRig);\r\n  instanced(new T.CylinderGeometry(16, 16, 14, 16).rotateZ(Math.PI / 2), dark, yRig, [at(-34, 10, 0), at(34, 10, 0)]);\r\n  const pivot = new T.Group(); pivot.position.set(0, 10, 0); yRig.add(pivot);\r\n  const lean = new T.Group(); pivot.add(lean);\r\n  mesh(new T.CylinderGeometry(10, 12, 120, 14).translate(0, 60, 0), dark, lean);\r\n  mesh(new T.SphereGeometry(11, 12, 8), dark, lean, 0, 120, 0);\r\n  mesh(new T.CylinderGeometry(8, 8, 34, 12).rotateX(Math.PI / 2).translate(0, 0, 17), dark, lean, 0, 120, 0);\r\n  const wheel = new T.Group(); wheel.position.set(0, 120, 34); lean.add(wheel);\r\n  mesh(new T.CylinderGeometry(17, 17, 14, 20).rotateX(Math.PI / 2), steel, wheel, 0, 0, 3);\r\n  const horn = new T.CatmullRomCurve3([[-76, 52], [-80, 16], [-68, -26], [-38, -52], [0, -58], [38, -52], [68, -26], [80, 16], [76, 52]]\r\n    .map(([x, y]) => new T.Vector3(x, y, 0)));\r\n  mesh(new T.TubeGeometry(horn, 40, 8, 8), steel, wheel, 0, 0, 3);\r\n  mesh(new T.CylinderGeometry(5.5, 5.5, 58, 10).translate(0, -29, 0), steel, wheel, 0, 0, 3);\r\n  instanced(new T.CylinderGeometry(8.5, 8.5, 46, 12), rubber, wheel, [at(-78, 30, 3), at(78, 30, 3)]);\r\n  instanced(new T.SphereGeometry(8.5, 10, 6), rubber, wheel, [at(-78, 53, 3), at(78, 53, 3)]);\r\n\r\n  // ---------- throttle ----------\r\n  const tSlot = new T.Group(), tRig = new T.Group();\r\n  tRig.rotation.x = tilt; tSlot.add(tRig); scene.add(tSlot);\r\n  mesh(new T.ExtrudeGeometry(roundedRect(T, 176, 176, 18), {\r\n    depth: 10, bevelEnabled: true, bevelThickness: 2, bevelSize: 2, bevelSegments: 2, curveSegments: 4}).translate(0, 0, -12), steel, tRig);\r\n  instanced(new T.CylinderGeometry(5, 5, 3, 10).rotateX(Math.PI / 2), chrome, tRig,\r\n    [-74, 74].flatMap((x) => [-74, 74].map((y) => at(x, y, 1.5))));\r\n  mesh(new T.TorusGeometry(81, 3, 8, 40).translate(0, 0, 1), dark, tRig);\r\n  const angleOf = (t) => (0.5 - clamp(t, 0, 1)) * SWEEP;\r\n  const ticks = Array.from({length: 21}, (_, i) => {\r\n    const t = i / 20, major = i % 4 === 0, len = major ? 12 : 6, m = new T.Matrix4();\r\n    return m.compose(new T.Vector3(-Math.sin(angleOf(t)) * (58 + len / 2), Math.cos(angleOf(t)) * (58 + len / 2), 1),\r\n      new T.Quaternion().setFromAxisAngle(new T.Vector3(0, 0, 1), angleOf(t)), new T.Vector3(major ? 1.4 : 1, len, 1));\r\n  });\r\n  instanced(new T.BoxGeometry(3, 1, 2), tickMat, tRig, ticks);\r\n  const targetArm = new T.Group(); tRig.add(targetArm);\r\n  mesh(new T.OctahedronGeometry(5, 0).scale(1, 1.4, 0.6), cyan, targetArm, 0, 75, 3);\r\n  mesh(new T.CylinderGeometry(52, 52, 8, 24).rotateX(Math.PI / 2), dark, tRig, 0, 0, 2);\r\n  const dial = new T.Group(); tRig.add(dial);\r\n  mesh(new T.ExtrudeGeometry(gearShape(T, 32, 44, 41), {\r\n    depth: 26, bevelEnabled: true, bevelThickness: 1, bevelSize: 0.8, bevelSegments: 1, curveSegments: 1}).translate(0, 0, 4), dialSteel, dial);\r\n  mesh(new T.CylinderGeometry(36, 36, 3, 28).rotateX(Math.PI / 2), steel, dial, 0, 0, 32);\r\n  mesh(new T.CylinderGeometry(6, 6, 3, 10).rotateX(Math.PI / 2), chrome, dial, 0, 0, 34);\r\n  mesh(new T.BoxGeometry(6, 22, 2), amber, dial, 0, 24, 33.5);\r\n\r\n  // ---------- launch panel: two toggle switches (ENGINE POWER, BOOST ARM) + heavy BOOST plunger ----------\r\n  const RED = new T.Color(0xff3a2a), GREEN = new T.Color(0x4dff7a), LEVER_OFF = 0.75, LEVER_ON = -1.05;\r\n  function buildSwitch() {\r\n    const slot = new T.Group(), rig = new T.Group();\r\n    rig.rotation.x = tilt; slot.add(rig); scene.add(slot);\r\n    mesh(new T.ExtrudeGeometry(roundedRect(T, 70, 100, 10), {\r\n      depth: 8, bevelEnabled: true, bevelThickness: 1.5, bevelSize: 1.5, bevelSegments: 1, curveSegments: 3}).translate(0, 0, -10), steel, rig);\r\n    instanced(new T.CylinderGeometry(3.5, 3.5, 2.5, 8).rotateX(Math.PI / 2), chrome, rig,\r\n      [-27, 27].flatMap((x) => [-42, 42].map((y) => at(x, y, 0.5))));\r\n    mesh(new T.CylinderGeometry(19, 19, 8, 20).rotateX(Math.PI / 2), dark, rig, 0, -8, 3);\r\n    const lamp = own(new T.MeshBasicMaterial({color: 0xff3a2a}));\r\n    mesh(new T.CylinderGeometry(7, 7, 3, 12).rotateX(Math.PI / 2), lamp, rig, 0, 34, 1.5);\r\n    mesh(new T.TorusGeometry(8.5, 1.8, 6, 14), dark, rig, 0, 34, 1.5);\r\n    const lever = new T.Group(); lever.position.set(0, -8, 6); rig.add(lever);\r\n    mesh(new T.CylinderGeometry(4.5, 5.5, 40, 10).rotateX(Math.PI / 2).translate(0, 0, 20), chrome, lever);\r\n    mesh(new T.SphereGeometry(8.5, 12, 8), rubber, lever, 0, 0, 42);\r\n    return {slot, rig, lever, lamp};\r\n  }\r\n  const swA = buildSwitch(), swB = buildSwitch(), switches = [swA, swB];\r\n  const bSlot = new T.Group(), bRig = new T.Group();\r\n  bRig.rotation.x = tilt; bSlot.add(bRig); scene.add(bSlot);\r\n  mesh(new T.ExtrudeGeometry(roundedRect(T, 116, 116, 14), {\r\n    depth: 8, bevelEnabled: true, bevelThickness: 1.5, bevelSize: 1.5, bevelSegments: 1, curveSegments: 3}).translate(0, 0, -10), steel, bRig);\r\n  instanced(new T.CylinderGeometry(4.5, 4.5, 3, 8).rotateX(Math.PI / 2), chrome, bRig,\r\n    [-48, 48].flatMap((x) => [-48, 48].map((y) => at(x, y, 0.5))));\r\n  mesh(new T.TorusGeometry(42, 5, 8, 28).translate(0, 0, 3), hazard, bRig);\r\n  mesh(new T.CylinderGeometry(37, 37, 8, 24).rotateX(Math.PI / 2), dark, bRig, 0, 0, 2);\r\n  const plunger = new T.Group(); bRig.add(plunger);\r\n  mesh(new T.CylinderGeometry(29, 31, 16, 24).rotateX(Math.PI / 2), plungerMat, plunger, 0, 0, 10);\r\n  mesh(new T.TorusGeometry(28, 2.5, 6, 24).translate(0, 0, 18), chrome, plunger);\r\n\r\n  // ---------- fit: scale each rig so its swept envelope sits inside its DOM rect ----------\r\n  const box = new T.Box3(), env = new T.Box3();\r\n  const state = {yaw: {x: 0, v: 0}, pull: {x: 0, v: 0}, thr: {x: 0, v: 0}, tgt: {x: 0, v: 0}, dim: 1,\r\n    sw: [{x: 0, v: 0}, {x: 0, v: 0}], plg: {x: 0, v: 0}, glow: 0};\r\n  function applyPose() {\r\n    wheel.rotation.z = state.yaw.x; lean.rotation.z = state.yaw.x * 0.1; pivot.rotation.x = state.pull.x * 0.32;\r\n    dial.rotation.z = angleOf(state.thr.x); targetArm.rotation.z = angleOf(state.tgt.x);\r\n    switches.forEach((w, i) => {\r\n      const on = clamp(state.sw[i].x, -0.1, 1.1);\r\n      w.lever.rotation.x = LEVER_OFF + on * (LEVER_ON - LEVER_OFF); w.lamp.color.lerpColors(RED, GREEN, clamp(on, 0, 1));\r\n    });\r\n    plunger.position.z = -state.plg.x * 10; plungerMat.emissiveIntensity = 0.05 + 0.9 * state.glow;\r\n  }\r\n  function envelope(slot, poses) {\r\n    env.makeEmpty();\r\n    for (const [y, p, t, sw, plg] of poses) {\r\n      state.yaw.x = y; state.pull.x = p; state.thr.x = t; state.tgt.x = t; state.sw[0].x = state.sw[1].x = sw ?? 0; state.plg.x = plg ?? 0; applyPose();\r\n      slot.updateMatrixWorld(true); env.union(box.setFromObject(slot, true));\r\n    }\r\n    state.yaw.x = state.pull.x = state.thr.x = state.tgt.x = state.sw[0].x = state.sw[1].x = state.plg.x = 0; applyPose();\r\n    return {w: env.max.x - env.min.x, h: env.max.y - env.min.y, cx: (env.max.x + env.min.x) / 2, cy: (env.max.y + env.min.y) / 2};\r\n  }\r\n  const fits = {\r\n    yoke: envelope(ySlot, [-1, 0, 1].flatMap((y) => [-1, 0, 1].map((p) => [y, p, 0]))),\r\n    throttle: envelope(tSlot, [[0, 0, 0], [0, 0, 1]]),\r\n    sw0: envelope(swA.slot, [[0, 0, 0, 0], [0, 0, 0, 1]]), sw1: envelope(swB.slot, [[0, 0, 0, 0], [0, 0, 0, 1]]),\r\n    boost: envelope(bSlot, [[0, 0, 0, 0, 0], [0, 0, 0, 0, 1]]),\r\n  };\r\n  const slots = {yoke: ySlot, throttle: tSlot, sw0: swA.slot, sw1: swB.slot, boost: bSlot};\r\n  const launchKeys = ['sw0', 'sw1', 'boost'], KEYS = Object.keys(slots);\r\n\r\n  // ---------- layout / per-frame ----------\r\n  let W = 0, H = 0, launchOn = false, glowT = 0, rects = null, rectClock = 0, disposed = false, layoutCount = 0;\r\n  const norm = (r) => (r && Number.isFinite(num(r.x ?? r.left, NaN)) && num(r.width, 0) > 8 && num(r.height, 0) > 8\r\n    ? {x: r.x ?? r.left, y: r.y ?? r.top, width: r.width, height: r.height} : null);\r\n  function readRects() {\r\n    const r = ui?.controlRects?.();\r\n    if (r) return {yoke: norm(r.yoke), throttle: norm(r.throttle), sw0: norm(r.launchSwitches?.[0]), sw1: norm(r.launchSwitches?.[1]), boost: norm(r.boost)};\r\n    const s = Math.min(H * 0.42, W * 0.3), g = 12; // no ui rects: yoke/throttle corners, no launch panel\r\n    return {yoke: norm({x: g, y: H - s - g, width: s, height: s}), throttle: norm({x: W - s - g, y: H - s - g, width: s, height: s}), sw0: null, sw1: null, boost: null};\r\n  }\r\n  const same = (a, b) => (!a && !b) || (a && b && ['x', 'y', 'width', 'height'].every((k) => Math.abs(a[k] - b[k]) < 0.5));\r\n  const sameAll = (a, b) => KEYS.every((k) => same(a?.[k], b?.[k]));\r\n  function layout(next) {\r\n    rects = next; layoutCount++;\r\n    for (const k of KEYS) {\r\n      const r = rects[k], slot = slots[k], f = fits[k];\r\n      slot.visible = !!r && (!launchKeys.includes(k) || launchOn);\r\n      if (!r) continue;\r\n      const s = Math.min(r.width / f.w, r.height / f.h) * 0.98;\r\n      slot.scale.setScalar(s);\r\n      slot.position.set(r.x + r.width / 2 - s * f.cx, H - (r.y + r.height / 2) - s * f.cy, 0);\r\n    }\r\n  }\r\n  function resize(w, h) {\r\n    if (disposed) return;\r\n    if (!(w > 0 && h > 0)) {\r\n      const r = ui?.stage?.getBoundingClientRect?.() ?? ui?.canvas?.getBoundingClientRect?.();\r\n      w = r?.width; h = r?.height;\r\n    }\r\n    if (!(w > 0 && h > 0)) return;\r\n    W = w; H = h;\r\n    camera.left = 0; camera.right = W; camera.bottom = 0; camera.top = H; camera.updateProjectionMatrix();\r\n    layout(readRects());\r\n  }\r\n  const readState = () => {\r\n    const s = ui?.controlState?.() ?? {}, i = ui?.input ?? {};\r\n    return {yaw: num(s.yaw ?? i.yaw), pull: num(s.pull ?? i.pull), throttle: num(s.throttle ?? i.throttle),\r\n      targetThrottle: num(s.targetThrottle ?? s.throttle ?? i.throttle), locked: !!s.locked,\r\n      switches: [!!s.launchSwitches?.[0], !!s.launchSwitches?.[1]], available: s.launchAvailable ?? false,\r\n      ready: s.launchReady ?? ui?.launchReady?.() ?? false, pressed: !!s.boostPressed, menu: !!s.menuOpen};\r\n  };\r\n  function update(dt) {\r\n    if (disposed || !W) return;\r\n    dt = clamp(num(dt), 0, 0.1);\r\n    if ((rectClock += dt) > 0.4) { rectClock = 0; const next = readRects(); if (!sameAll(next, rects)) layout(next); }\r\n    const s = readState(), fast = s.locked;\r\n    // Launch panel exists only while launching is possible: city/world, not busy/locked, no menu (no overlap with checkpoint/fx panels).\r\n    const show = s.available && !s.locked && !s.menu;\r\n    if (show !== launchOn) { launchOn = show; if (rects) layout(rects); }\r\n    s.switches.forEach((on, i) => springStep(state.sw[i], on ? 1 : 0, dt, 34, 0.55));\r\n    springStep(state.plg, s.pressed ? 1 : 0, dt, 40, 0.8);\r\n    glowT += dt; state.glow = s.ready ? 0.55 + 0.45 * Math.sin(glowT * 6) : 0;\r\n    springStep(state.yaw, fast ? 0 : clamp(s.yaw, -1, 1), dt, fast ? 38 : 16, fast ? 1 : 0.8);\r\n    springStep(state.pull, fast ? 0 : clamp(s.pull, -1, 1), dt, fast ? 38 : 16, fast ? 1 : 0.8);\r\n    springStep(state.thr, clamp(s.throttle, 0, 1), dt, fast ? 40 : 11, 1);\r\n    springStep(state.tgt, fast ? 0 : clamp(s.targetThrottle, 0, 1), dt, fast ? 40 : 18, 1);\r\n    state.dim += ((s.locked ? 0.55 : 1) - state.dim) * Math.min(1, dt * 8);\r\n    hemi.intensity = lightBase[0] * state.dim; key.intensity = lightBase[1] * state.dim; rim.intensity = lightBase[2] * state.dim;\r\n    applyPose();\r\n  }\r\n  // Draws after the world pass: keep the world colour, drop its depth, restore autoClear for the next world frame.\r\n  function render(renderer) {\r\n    if (disposed || !W || !renderer) return;\r\n    const auto = renderer.autoClear;\r\n    try { renderer.autoClear = false; renderer.clearDepth(); renderer.render(scene, camera); } finally { renderer.autoClear = auto; }\r\n  }\r\n  const cssBounds = (slot) => {\r\n    slot.updateMatrixWorld(true); box.setFromObject(slot, true);\r\n    return {x: box.min.x, y: H - box.max.y, width: box.max.x - box.min.x, height: box.max.y - box.min.y};\r\n  };\r\n  function triangles() {\r\n    let n = 0;\r\n    scene.traverse((o) => {\r\n      if (!o.isMesh) return;\r\n      const g = o.geometry, c = (g.index ? g.index.count : g.attributes.position.count) / 3;\r\n      n += c * (o.isInstancedMesh ? o.count : 1);\r\n    });\r\n    return n;\r\n  }\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    for (const o of owned) o.dispose?.();\r\n    scene.traverse((o) => { if (o.isInstancedMesh) o.dispose(); });\r\n    scene.clear();\r\n  }\r\n  resize();\r\n  return {\r\n    scene, camera, group: scene, resize, update, render, dispose,\r\n    get size() { return {width: W, height: H}; },\r\n    get layouts() { return layoutCount; },\r\n    rects: () => (rects ? {yoke: rects.yoke && {...rects.yoke}, throttle: rects.throttle && {...rects.throttle}} : null),\r\n    bounds: () => ({yoke: cssBounds(ySlot), throttle: cssBounds(tSlot)}),\r\n    pose: () => ({yaw: state.yaw.x, pull: state.pull.x, throttle: state.thr.x, target: state.tgt.x, dim: state.dim,\r\n      wheelRoll: wheel.rotation.z, columnLean: lean.rotation.z, columnPitch: pivot.rotation.x, dialAngle: dial.rotation.z}),\r\n    parts: {yoke: {slot: ySlot, rig: yRig, pivot, lean, wheel}, throttle: {slot: tSlot, rig: tRig, dial, targetArm}},\r\n    stats: () => ({draws: scene.children.filter((c) => c.isGroup).reduce((n, g) => { let k = 0; g.traverse((o) => { if (o.isMesh) k++; }); return n + k; }, 0), triangles: triangles()}),\r\n    hasLaunch: launchKeys.every((k) => !!slots[k]),\r\n    get launchVisible() { return launchOn; },\r\n    get disposed() { return disposed; },\r\n  };\r\n}\r\n","modules/coach-flight/cockpit.mjs":"// Pixel cockpit owns only DOM/input. It never creates a graphics context; the 3D yoke/dial/launch-switch meshes align to ui.controlRects().\r\nconst SHAPES={'chest':3,'quads':8,'glutes':64,'arms-shoulders':6,'yoga':12,'martial-arts':10,'cardio':64};\r\nexport function framePoints(faction){const n=SHAPES[faction]||6,star=['yoga','martial-arts'].includes(faction),points=Array.from({length:n},(_,i)=>{const a=-Math.PI/2+i*Math.PI*2/n,r=star&&i%2?.78:1;return [Math.cos(a)*r,Math.sin(a)*r];}),xs=points.map(p=>p[0]),ys=points.map(p=>p[1]),x0=Math.min(...xs),y0=Math.min(...ys),w=Math.max(...xs)-x0,h=Math.max(...ys)-y0;return points.map(([x,y])=>`${2+96*(x-x0)/w},${3+94*(y-y0)/h}`).join(' ');}\r\nconst SLEW=.45,clamp=(v,a=-1,b=1)=>Math.max(a,Math.min(b,v));let uid=0;\r\n// Engraved keycap icons (no words on chrome); names live in aria-label/title.\r\nconst svg=d=>`<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">${d}</svg>`;\r\nconst ICON={look:svg('<path d=\"M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>'),nav:svg('<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M15.5 8.5l-2 5-5 2 2-5z\"/>'),land:svg('<path d=\"M12 3v11M7 9l5 5 5-5M4 20h16\"/>'),exit:svg('<path d=\"M6 6l12 12M18 6L6 18\"/>'),home:svg('<path d=\"M12 21s-6-6.2-6-11a6 6 0 0112 0c0 4.8-6 11-6 11z\"/><circle cx=\"12\" cy=\"10\" r=\"2\"/>'),yoke:svg('<path d=\"M3 9h6l3 3 3-3h6M12 12v8\"/>'),power:svg('<path d=\"M12 3v8\"/><path d=\"M6.3 7.5a8 8 0 1011.4 0\"/>'),arm:svg('<path d=\"M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z\"/><path d=\"M9 12l2 2 4-4\"/>'),boost:svg('<path d=\"M6 13l6-6 6 6M6 19l6-6 6 6\"/>')};\r\nexport function createCockpit(host,{color='#73dbcb',faction='chest',ship='supportive',onAction=()=>{}}={}){\r\n const doc=host.ownerDocument,ac=new AbortController(),root=doc.createElement('section'),gid='cf-metal-'+(++uid);root.dataset.coachFlight='';root.setAttribute('aria-label','Ship flight');root.style.setProperty('--hull',color);\r\n root.innerHTML=`<style>\r\n [data-coach-flight]{position:fixed;inset:0;z-index:2147483100;background:#07131b;color:#ecf8ed;touch-action:none;font:14px monospace;overflow:hidden;--hull:#73dbcb;--metal:linear-gradient(180deg,#56656ce6,#26333ae6 45%,#1a252ce6 55%,#3b4950e6)}\r\n [data-coach-flight] *{box-sizing:border-box}[data-coach-flight] button,[data-coach-flight] input,[data-coach-flight] select{font:inherit;color:#efffee;background:#13222d;border:2px solid var(--hull);border-radius:3px;min-height:44px;padding:8px;touch-action:none}\r\n [data-flight-stage]{position:absolute;inset:0;overflow:hidden;background:#263a48}[data-flight-stage] canvas{width:100%;height:100%;image-rendering:pixelated;display:block}\r\n [data-flight-frame]{position:absolute;top:48px;left:12px;width:calc(100% - 24px);height:calc(100% - 168px);pointer-events:none;overflow:visible;filter:drop-shadow(3px 4px 0 #02080c)}\r\n [data-flight-top]{position:absolute;z-index:80;top:env(safe-area-inset-top,0px);left:12px;right:12px;display:flex;align-items:center;gap:8px;height:46px}[data-flight-top] span{flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-shadow:1px 1px #07131b}[data-flight-top] button{min-height:44px;min-width:44px;padding:4px 9px}\r\n [data-flight-top] button,[data-flight-instruments] button{display:grid;place-items:center;color:#cfe3e2;background:var(--metal);border:1px solid #8fa1a6;box-shadow:inset 0 1px #d6e2e3,inset 0 -3px #0b1419,0 3px 0 #02080c;transition:transform 60ms,box-shadow 60ms;touch-action:manipulation}\r\n [data-flight-top] svg,[data-flight-instruments] svg{filter:drop-shadow(0 1px 0 #ffffff55) drop-shadow(0 -1px 0 #000a)}\r\n [data-flight-top] button:active,[data-flight-instruments] button:active{transform:translateY(3px);box-shadow:inset 0 2px 4px #000b,0 0 0 #02080c}\r\n [data-flight-top] button[aria-pressed=true]{color:var(--hull);transform:translateY(2px);box-shadow:inset 0 2px 5px #000c,0 1px 0 #02080c}\r\n [data-coach-flight] button:disabled{opacity:.5;filter:grayscale(.7)}[data-coach-flight] button:focus-visible{outline:2px solid var(--hull);outline-offset:2px}\r\n [data-flight-rail]{position:absolute;left:0;right:0;height:10px;background:linear-gradient(180deg,#7d8e94,#3a474e 40%,#1b252b 60%,#56666c);box-shadow:0 2px 0 #02080c;pointer-events:none}[data-flight-rail=top]{top:calc(env(safe-area-inset-top,0px) + 46px);height:4px}[data-flight-rail=bottom]{bottom:0;height:calc(6px + env(safe-area-inset-bottom,0px))}\r\n [data-flight-bottom]{position:absolute;bottom:env(safe-area-inset-bottom,0px);left:8px;right:8px;height:116px;display:flex;align-items:flex-end;gap:8px;justify-content:space-between;pointer-events:none}\r\n [data-flight-stick],[data-flight-dial]{position:relative;flex:0 0 auto;width:140px;height:108px;background:transparent;touch-action:none;pointer-events:auto;border-radius:12px;outline-offset:-3px}\r\n [data-flight-dial]{width:112px;display:block}[data-flight-dial]:focus-within{outline:2px solid var(--hull)}[data-flight-stick][data-active],[data-flight-dial][data-active]{outline:1px dashed #ffffff40}\r\n [data-flight-stick] b,[data-flight-dial] b{position:absolute;left:50%;bottom:4px;transform:translateX(-50%);font-size:11px;white-space:nowrap;padding:1px 6px;background:var(--metal);border:1px solid #8fa1a6;border-radius:3px;color:#dff4ef;pointer-events:none}\r\n [data-flight-throttle]{position:absolute;inset:0;width:100%;height:100%;opacity:0;pointer-events:none;margin:0;padding:0!important;min-height:0!important}\r\n [data-flight-instruments]{display:grid;gap:3px;text-align:center;max-width:46%;margin-bottom:6px;padding:6px 10px;background:var(--metal);border:2px solid #7f9196;border-radius:6px;box-shadow:inset 0 1px #d4e0e2,inset 0 -2px #091116,0 3px 0 #02080c;pointer-events:auto}[data-flight-instruments] b{color:var(--hull);font-size:13px}[data-flight-instruments] span{font-size:11px;color:#b7d7da}[data-flight-instruments] button{min-height:40px;min-width:56px;justify-self:center;padding:4px 14px}\r\n [data-flight-message]{position:absolute;top:54px;left:20%;right:20%;text-align:center;text-shadow:2px 2px #07131b;pointer-events:none;white-space:pre-line}\r\n [data-flight-menu]{position:absolute;inset:48px 16px 120px;z-index:4;background:#101d29f5;border:2px solid var(--hull);padding:14px;overflow:auto;touch-action:pan-y}[data-flight-menu][hidden]{display:none}[data-flight-menu] button{margin:3px;touch-action:manipulation}[data-flight-menu] input{width:100%;touch-action:manipulation}[data-flight-menu] p{line-height:1.4}[data-flight-menu] h3{margin:8px 0}[data-flight-rotate]{position:absolute;right:18px;top:90px;background:#152631bd;padding:6px;pointer-events:none;font-size:12px;display:none}\r\n [data-coach-flight][data-locked] [data-flight-stick] b,[data-coach-flight][data-locked] [data-flight-dial] b{opacity:.45}\r\n [data-flight-launch]{position:absolute;left:50%;bottom:calc(env(safe-area-inset-bottom,0px) + 150px);transform:translateX(-50%);display:flex;align-items:center;gap:8px;padding:5px 8px;background:var(--metal);border:2px solid #7f9196;border-radius:8px;box-shadow:inset 0 1px #d4e0e2,inset 0 -2px #091116,0 3px 0 #02080c;pointer-events:auto;z-index:2}[data-flight-launch][hidden]{display:none}\r\n [data-flight-launch] [data-launch-switch],[data-flight-launch] [data-action=boost]{position:relative;min-width:44px;min-height:44px;padding:0;touch-action:manipulation;color:#cfe3e2;border:1px solid #8fa1a6;border-radius:6px;box-shadow:inset 0 1px #d6e2e3,inset 0 -3px #0b1419,0 3px 0 #02080c}\r\n [data-flight-launch] [data-launch-switch]{width:46px;height:56px;background:radial-gradient(circle at 50% 22px,#05090c 5px,#3b4950 6px,#1a252c 9px,transparent 10px),var(--metal)}\r\n [data-flight-launch] [data-launch-switch]::after{content:'';position:absolute;left:50%;top:4px;width:12px;height:18px;margin-left:-6px;border-radius:6px 6px 3px 3px;background:linear-gradient(90deg,#5d6b70,#f4fbf8 45%,#7d8e94);box-shadow:0 2px 0 #02080c;transform-origin:50% 100%;transform:perspective(70px) rotateX(180deg);transition:transform 110ms ease-out}\r\n [data-flight-launch] [data-launch-switch][aria-checked=true]::after{transform:perspective(70px) rotateX(18deg)}[data-flight-launch] [data-launch-switch][aria-checked=true]{color:var(--hull);border-color:var(--hull);box-shadow:inset 0 1px #d6e2e3,inset 0 -3px #0b1419,0 3px 0 #02080c,0 0 8px var(--hull)}\r\n [data-flight-launch] [data-launch-switch] svg{position:absolute;left:50%;bottom:3px;width:13px;height:13px;transform:translateX(-50%)}\r\n [data-flight-launch] [data-action=boost]{display:grid;place-items:center;width:72px;height:56px;background:radial-gradient(circle,#7a2a12,#3a140a 70%);border-color:#d07a4a}[data-flight-launch] [data-action=boost]:not(:disabled){color:#fff4dc;background:radial-gradient(circle,#ff8a3d,#a83a12 70%);box-shadow:inset 0 1px #ffd9b0,inset 0 -3px #4a1606,0 3px 0 #02080c,0 0 12px #ff7a2a}[data-flight-launch] [data-action=boost]:not(:disabled):active{transform:translateY(3px);box-shadow:inset 0 2px 4px #000b}\r\n [data-coach-flight][data-cockpit3d] [data-flight-launch] [data-launch-switch],[data-coach-flight][data-cockpit3d] [data-flight-launch] [data-action=boost]{background:transparent;border-color:transparent;box-shadow:none}[data-coach-flight][data-cockpit3d] [data-flight-launch] [data-launch-switch]::after{display:none}[data-coach-flight][data-cockpit3d] [data-flight-launch]{background:transparent;box-shadow:none;border-color:#7f919640}\r\n @media(orientation:portrait){[data-flight-rotate]{display:block}[data-flight-message]{left:12%;right:12%}[data-flight-top] span{font-size:11px}[data-flight-stick]{width:min(30vw,120px)}[data-flight-dial]{width:min(26vw,100px)}[data-flight-instruments]{max-width:40%;padding:4px 6px}[data-flight-instruments] span{font-size:10px}}\r\n </style><div data-flight-stage><canvas aria-label=\"First person ship view\"></canvas></div>\r\n <svg data-flight-frame viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><defs><linearGradient id=\"${gid}\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#f4fbf8\" stop-opacity=\".75\"/><stop offset=\".35\" stop-color=\"#fff\" stop-opacity=\".08\"/><stop offset=\".65\" stop-color=\"#000\" stop-opacity=\".18\"/><stop offset=\"1\" stop-color=\"#000\" stop-opacity=\".6\"/></linearGradient></defs>\r\n <polygon fill=\"none\" stroke=\"#050c11\" stroke-width=\"17\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\"/><polygon fill=\"none\" stroke=\"#5d6b70\" stroke-width=\"13\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\"/><polygon fill=\"none\" stroke=\"var(--hull)\" stroke-width=\"9\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\"/><polygon fill=\"none\" stroke=\"url(#${gid})\" stroke-width=\"9\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\"/><polygon fill=\"none\" stroke=\"#e6f1ee\" stroke-width=\"1.2\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\"/></svg>\r\n <i data-flight-rail=\"top\" aria-hidden=\"true\"></i><i data-flight-rail=\"bottom\" aria-hidden=\"true\"></i>\r\n <header data-flight-top><span data-flight-title></span><button data-action=\"look\" aria-label=\"Toggle phone look\" title=\"Phone look\" aria-pressed=\"false\">${ICON.look}</button><button data-action=\"nav\" aria-label=\"Navigation\" title=\"Navigation\">${ICON.nav}</button><button data-action=\"land\" aria-label=\"Land\" title=\"Land\">${ICON.land}</button><button data-action=\"exit\" aria-label=\"Exit flight\" title=\"Exit flight\">${ICON.exit}</button></header>\r\n <div data-flight-message role=\"status\" aria-live=\"polite\"></div><div data-flight-rotate>Rotate sideways for a wider cockpit</div>\r\n <div data-flight-bottom><div data-flight-stick role=\"group\" aria-label=\"Steer left and right; pull up or push down to pitch\"><b aria-hidden=\"true\">${ICON.yoke}</b></div><div data-flight-instruments><b data-flight-reading>BOARDING</b><span data-flight-hint>Engine + arm, then boost · throttle to fly</span><button data-action=\"home\" aria-label=\"Vegas beacon\" title=\"Vegas beacon\">${ICON.home}</button></div><label data-flight-dial><input data-flight-throttle type=\"range\" min=\"0\" max=\"100\" value=\"0\" aria-label=\"Forward throttle\"><b data-flight-thrust>THRUST 0%</b></label></div><div data-flight-launch role=\"group\" aria-label=\"Space launch\"><button type=\"button\" role=\"switch\" aria-checked=\"false\" data-launch-switch=\"engine\" aria-label=\"Engine power\" title=\"Engine power\">${ICON.power}</button><button type=\"button\" role=\"switch\" aria-checked=\"false\" data-launch-switch=\"arm\" aria-label=\"Boost arm\" title=\"Boost arm\">${ICON.arm}</button><button type=\"button\" data-action=\"boost\" aria-label=\"Boost to space\" title=\"Boost to space\" disabled>${ICON.boost}</button></div><div data-flight-menu hidden></div>`;\r\n const pts=framePoints(faction);root.querySelectorAll('[data-flight-frame] polygon').forEach(p=>p.setAttribute('points',pts));const stage=root.querySelector('[data-flight-stage]');\r\n root.querySelector('[data-flight-title]').textContent=`MYR5 / ${String(ship).toUpperCase()}`;host.append(root);\r\n const stick=root.querySelector('[data-flight-stick]'),dial=root.querySelector('[data-flight-dial]'),throttle=root.querySelector('[data-flight-throttle]'),thrust=root.querySelector('[data-flight-thrust]'),menu=root.querySelector('[data-flight-menu]'),launch=root.querySelector('[data-flight-launch]'),switches=[...launch.querySelectorAll('[data-launch-switch]')],boost=launch.querySelector('[data-action=boost]');\r\n // input.throttle is the ACTUAL (slewed) value; target is where the heavy dial was set.\r\n const input={yaw:0,pull:0,throttle:0},keys=new Set();let target=0,stickId=null,dialDrag=null,locked=false,launchAvailable=true;const armed=[false,false];\r\n const blocked=()=>locked||!menu.hidden;\r\n // Space launch: two flip switches + BOOST; independent of yoke/dial. Any reset() disarms both.\r\n const launchReady=()=>armed[0]&&armed[1]&&launchAvailable&&!blocked();\r\n function syncLaunch(){switches.forEach((b,i)=>b.setAttribute('aria-checked',String(armed[i])));boost.disabled=!launchReady();launch.hidden=!launchAvailable||blocked();}\r\n function capture(el,id){try{el.setPointerCapture?.(id);}catch{}}\r\n function release(el,id){if(id!=null&&el.hasPointerCapture?.(id))el.releasePointerCapture(id);delete el.dataset.active;}\r\n function neutral(){const s=stickId;stickId=null;release(stick,s);input.yaw=input.pull=0;}\r\n function setTarget(v){target=blocked()?0:clamp(v,0,1);throttle.value=String(Math.round(target*100));}\r\n function reset(){neutral();const d=dialDrag;dialDrag=null;release(dial,d?.id);keys.clear();target=input.throttle=0;throttle.value='0';thrust.textContent='THRUST 0%';armed.fill(false);syncLaunch();}\r\n function position(e){const r=stick.getBoundingClientRect(),x=clamp((e.clientX-r.left-r.width/2)/32),y=clamp((e.clientY-r.top-r.height/2)/28);input.yaw=-x;input.pull=-y;}\r\n stick.addEventListener('pointerdown',e=>{if(blocked()||stickId!=null)return;e.preventDefault();stickId=e.pointerId;capture(stick,stickId);stick.dataset.active='';position(e);},{signal:ac.signal});\r\n stick.addEventListener('pointermove',e=>{if(e.pointerId!==stickId)return;if(blocked()){neutral();return;}position(e);},{signal:ac.signal});\r\n for(const type of ['pointerup','pointercancel','lostpointercapture'])stick.addEventListener(type,e=>{if(e.pointerId===stickId)neutral();},{signal:ac.signal});\r\n // Dial drag is relative to where the finger lands (no jump): up/right raises target, ~1.4 zone heights for full travel.\r\n dial.addEventListener('pointerdown',e=>{if(blocked()||dialDrag)return;e.preventDefault();dialDrag={id:e.pointerId,x:e.clientX,y:e.clientY,from:target,travel:Math.max(90,dial.getBoundingClientRect().height*1.4)};capture(dial,e.pointerId);dial.dataset.active='';},{signal:ac.signal});\r\n dial.addEventListener('pointermove',e=>{if(e.pointerId!==dialDrag?.id)return;if(blocked()){const d=dialDrag;dialDrag=null;release(dial,d.id);return;}setTarget(dialDrag.from+((e.clientX-dialDrag.x)-(e.clientY-dialDrag.y))/dialDrag.travel);},{signal:ac.signal});\r\n for(const type of ['pointerup','pointercancel','lostpointercapture'])dial.addEventListener(type,e=>{if(e.pointerId===dialDrag?.id){dialDrag=null;delete dial.dataset.active;}},{signal:ac.signal});\r\n dial.addEventListener('click',e=>e.preventDefault(),{signal:ac.signal});\r\n throttle.oninput=()=>setTarget(Number(throttle.value)/100);\r\n for(const b of root.querySelectorAll('[data-action]'))b.onclick=()=>{reset();onAction(b.dataset.action);};\r\n switches.forEach((b,i)=>b.onclick=()=>{if(blocked())return;armed[i]=!armed[i];syncLaunch();});\r\n // BOOST skips the generic reset so the view can confirm launchReady(); launchSpace() then locks (which resets).\r\n boost.onclick=()=>{if(launchReady())onAction('boost');};\r\n const win=doc.defaultView;win?.addEventListener('blur',reset,{signal:ac.signal});doc.addEventListener('visibilitychange',reset,{signal:ac.signal});\r\n const KEYS={ArrowLeft:1,ArrowRight:1,ArrowUp:1,ArrowDown:1};\r\n function steerKeys(){if(stickId!=null)return;input.yaw=(keys.has('ArrowLeft')?1:0)-(keys.has('ArrowRight')?1:0);input.pull=(keys.has('ArrowUp')?1:0)-(keys.has('ArrowDown')?1:0);}\r\n win?.addEventListener('keydown',e=>{if(blocked()||['INPUT','TEXTAREA','SELECT'].includes(e.target?.tagName))return;if(KEYS[e.key]){e.preventDefault();keys.add(e.key);steerKeys();}if(e.key==='w')setTarget(.65);},{signal:ac.signal});\r\n win?.addEventListener('keyup',e=>{if(KEYS[e.key]&&keys.delete(e.key))steerKeys();if(e.key==='w')setTarget(0);},{signal:ac.signal});\r\n const local=(el,s)=>{const r=el.getBoundingClientRect();return {x:r.left-s.left,y:r.top-s.top,width:r.width,height:r.height};};\r\n return {root,stage,canvas:stage.querySelector('canvas'),menu,input,reset,\r\n // Heavy dial: actual chases target at most SLEW units/s, easing in over the last ~35%.\r\n update(dt){dt=Number.isFinite(dt)&&dt>0?Math.min(dt,.25):0;if(locked)target=0;const d=target-input.throttle;if(!d)return;const step=SLEW*Math.min(1,.3+Math.abs(d)*2)*dt;input.throttle=Math.abs(d)<=step?target:input.throttle+Math.sign(d)*step;thrust.textContent=`THRUST ${Math.round(input.throttle*100)}%`;},\r\n controlRects(){const s=stage.getBoundingClientRect();return {yoke:local(stick,s),throttle:local(dial,s),launchSwitches:launch.hidden?null:switches.map(b=>local(b,s)),boost:launch.hidden?null:local(boost,s)};},\r\n controlState(){return {yaw:input.yaw,pull:input.pull,throttle:input.throttle,targetThrottle:target,locked,menuOpen:!menu.hidden,launchSwitches:[...armed],launchAvailable,launchReady:launchReady()};},\r\n launchReady,setLaunchAvailable(v){launchAvailable=!!v;if(!launchAvailable)armed.fill(false);syncLaunch();},\r\n message(t){root.querySelector('[data-flight-message]').textContent=t;},reading(t){root.querySelector('[data-flight-reading]').textContent=t;},hint(t){root.querySelector('[data-flight-hint]').textContent=t;},\r\n lock(v){locked=!!v;reset();if(locked)root.dataset.locked='';else delete root.dataset.locked;throttle.disabled=locked;},\r\n openMenu(){reset();menu.hidden=false;root.dataset.menu='';syncLaunch();},closeMenu(){menu.hidden=true;delete root.dataset.menu;reset();},\r\n dispose(){reset();ac.abort();root.remove();}};\r\n}\r\n","modules/coach-flight/core.mjs":"/** Pure flight domain, metres (not AR compression), seconds and radians.\r\n * No GPS, base mutation, rendering, timers or reward/presence interfaces.\r\n * Hull identity is renderer-owned: six coach hull IDs are NOT seven factions.\r\n */\r\nexport const VEGAS_BOUNDS = Object.freeze([-115.45, 35.95, -114.95, 36.35]);\r\nexport const VEGAS = Object.freeze({lat: 36.1147, lng: -115.1728});\r\nconst RAD = Math.PI / 180, METRES = 6378137 * RAD, SECTOR_SIZE = 1500;\r\nconst finite = (v, fallback = 0) => Number.isFinite(v) ? v : fallback;\r\nconst clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));\r\nconst seconds = dt => clamp(finite(dt), 0, 0.1);\r\nconst wrap = angle => Math.atan2(Math.sin(angle), Math.cos(angle));\r\nconst validGeo = p => p && Number.isFinite(p.lat) && Number.isFinite(p.lng)\r\n  && Math.abs(p.lat) <= 90 && Math.abs(p.lng) <= 180;\r\nfunction assertGeo(p) {\r\n  if (!validGeo(p)) throw new TypeError('Expected finite {lat,lng} geographic coordinates.');\r\n}\r\nfunction longitudeScale(origin) {\r\n  assertGeo(origin);\r\n  const scale = METRES * Math.cos(origin.lat * RAD);\r\n  if (scale < 1e-6) throw new RangeError('A local metre frame cannot be anchored at a pole.');\r\n  return scale;\r\n}\r\nexport function covered(point) {\r\n  return !!validGeo(point) && point.lng >= VEGAS_BOUNDS[0] && point.lng <= VEGAS_BOUNDS[2]\r\n    && point.lat >= VEGAS_BOUNDS[1] && point.lat <= VEGAS_BOUNDS[3];\r\n}\r\n/** Local city tangent frame. Use a separate globe mapping for world/orbital flight. */\r\nexport function geoToLocal(point, origin = VEGAS) {\r\n  assertGeo(point);\r\n  const scale = longitudeScale(origin);\r\n  let delta = point.lng - origin.lng;\r\n  if (delta > 180) delta -= 360;\r\n  if (delta < -180) delta += 360;\r\n  return {x: delta * scale, z: (origin.lat - point.lat) * METRES};\r\n}\r\nexport function localToGeo(point, origin = VEGAS) {\r\n  const scale = longitudeScale(origin);\r\n  if (!Number.isFinite(point?.x) || !Number.isFinite(point?.z))\r\n    throw new TypeError('Expected finite local {x,z} metres.');\r\n  const lat = origin.lat - point.z / METRES;\r\n  if (Math.abs(lat) > 90) throw new RangeError('Local frame extends past a pole.');\r\n  const lng = ((origin.lng + point.x / scale + 180) % 360 + 360) % 360 - 180;\r\n  return {lat, lng};\r\n}\r\n/** Local {x,z} points; north=0, west=+PI/2, east=-PI/2. Coincident=0. */\r\nexport function headingTo(from, to) {\r\n  if (![from?.x, from?.z, to?.x, to?.z].every(Number.isFinite))\r\n    throw new TypeError('Heading requires two local {x,z} points.');\r\n  const dx = to.x - from.x, dz = to.z - from.z;\r\n  return dx === 0 && dz === 0 ? 0 : Math.atan2(-dx, -dz);\r\n}\r\n/** opts.floor is height ABOVE the caller's city ground datum, not sea level.\r\n * Caller supplies validated terrain/obstacle clearance. No manual descent.\r\n * opts.response (1/s, default 2.5) and yawRate (rad/s, default 1.2) tune handling.\r\n * Missing/nonfinite input is neutral. Large/background dt cannot advance >100ms.\r\n * opts.brake (1/s, opt-in): speed above maxSpeed is NOT clamped instantly; it decays toward\r\n * the throttle target at this rate (true braking). Omitted = legacy instant clamp to maxSpeed.\r\n */\r\nexport function stepCruise(pose = {}, input = {}, dt = 0, opts = {}) {\r\n  const t = seconds(dt), floor = Math.max(0, finite(opts.floor, 450));\r\n  const max = Math.max(0, finite(opts.maxSpeed, 100)), brake = finite(opts.brake);\r\n  const speed = brake > 0 ? Math.max(0, finite(pose.speed)) : clamp(finite(pose.speed), 0, max);\r\n  const target = clamp(finite(input.throttle), 0, 1) * max;\r\n  const rate = brake > 0 && speed > target ? brake : Math.max(0.01, finite(opts.response, 2.5));\r\n  const decay = Math.exp(-rate * t), nextSpeed = target + (speed - target) * decay;\r\n  // Analytic straight-line distance preserves braking/acceleration across frame rates.\r\n  const distance = target * t + (speed - target) * (-Math.expm1(-rate * t)) / rate;\r\n  const yaw = wrap(finite(pose.yaw));\r\n  const turn = clamp(finite(input.yaw), -1, 1) * Math.max(0, finite(opts.yawRate, 1.2)) * t;\r\n  return {x: finite(pose.x) - Math.sin(yaw + turn / 2) * distance,\r\n    y: Math.max(floor, finite(pose.y, floor)),\r\n    z: finite(pose.z) - Math.cos(yaw + turn / 2) * distance,\r\n    yaw: wrap(yaw + turn), speed: nextSpeed};\r\n}\r\n/** progress is normalized 0..1. Upward pull >=.75 for .65s commits once.\r\n * Release resets progress; the machine owns the additional session launch latch.\r\n */\r\nexport function advancePull(progress, pull, dt) {\r\n  if (finite(pull) < 0.75) return {progress: 0, launch: false};\r\n  const previous = clamp(finite(progress), 0, 1);\r\n  const next = Math.min(1, previous + seconds(dt) / 0.65);\r\n  return {progress: next, launch: previous < 1 && next >= 1};\r\n}\r\n/** Versioned 1.5km grid about the fixed Vegas origin; never depends on camera rebase.\r\n * Separate from H3 claims, biome boundaries and render tiles. Caller owns deadband.\r\n */\r\nexport function sectorAt(point) {\r\n  const local = geoToLocal(point, VEGAS);\r\n  const column = Math.floor(local.x / SECTOR_SIZE), row = Math.floor(local.z / SECTOR_SIZE);\r\n  return {id: `flight:v1:${column}:${row}`, center: localToGeo({\r\n    x: (column + 0.5) * SECTOR_SIZE, z: (row + 0.5) * SECTOR_SIZE}, VEGAS)};\r\n}\r\n\r\n/** Explicit controller contract (state strings are lowercase):\r\n * landed --board--> boarding --takeoff*--> takeoff --cruise*--> cruise\r\n * cruise --checkpoint/jump--> checkpoint/jump --cruise*+ready--> cruise\r\n * cruise/world --space--> space-launch --portal*--> space-portal --ready*--> space\r\n * space/world/docked --reentry--> reentry --cruise* or world* + ready--> cruise/world\r\n * cruise --land--> land-pin --landing*--> landing --landed*+ready--> landed\r\n * space --warning--> territory-warning --intercept*--> intercept\r\n * intercept --turn*--> guided-turn --resume*--> space\r\n * territory-warning --resume*--> space (retreated before enforcement).\r\n * space --dock--> docking --docked*+ready--> docked --undock--> space (MOM Station)\r\n * docked --reentry--> reentry (Fiery re-entry / NAV jump); cancel in docking -> space.\r\n * land/space/jump/warning/board are invalid from docking/docked.\r\n * '*' requires {token: machine.token} captured WHEN THE PHASE STARTS. All ready\r\n * completions also require ready:true: controller attests destination resources,\r\n * valid altitude envelope AND a rendered frame hidden under opaque cover.\r\n * Reentry cruise requires covered destination; world never enables landing.\r\n * send('reentry', {destination:{lat,lng}}) defaults to VEGAS. send('jump',\r\n * {destination, confirmed:true}) accepts supported, explicitly confirmed pins.\r\n * send('landing', {token,destination,confirmed:true}) validates the chosen pin.\r\n * All coordinates are virtual. No event authorizes physical presence or GPS writes.\r\n * cancel restores phase origin (an enforced orbital turn must finish or exit);\r\n * exit/account-change/context-loss reset to landed.\r\n * Each accepted event invalidates the old token, including cancel/exit. Invalid or\r\n * stale events are inert. send(type,payload) or send({type,...payload}) returns\r\n * {accepted,state,token,reason?}. Getter state is a string; token is a monotonic int.\r\n * Parent resets throttle on handoff and owns scene/pose rollback, cleanup, safe\r\n * landed checkpoint, permission checks and paused time. This module owns no I/O.\r\n */\r\nexport function createFlightMachine() {\r\n  let state = 'landed', token = 0, origin = 'landed', launchUsed = false;\r\n  let destination = null;\r\n  const result = (accepted, reason) => ({accepted, state, token, ...(reason ? {reason} : {})});\r\n  const enter = next => {state = next; token++; return result(true);};\r\n  const completion = new Set(['takeoff', 'cruise', 'portal', 'ready', 'world',\r\n    'landing', 'landed', 'intercept', 'turn', 'resume', 'docked']);\r\n  return Object.freeze({\r\n    get state() {return state;},\r\n    get token() {return token;},\r\n    send(type, payload = {}) {\r\n      const event = typeof type === 'string' ? {...payload, type} : type;\r\n      if (!event || typeof event.type !== 'string') return result(false, 'invalid-event');\r\n      const name = event.type;\r\n      if (event.token !== undefined && event.token !== token) return result(false, 'stale-token');\r\n      if (['exit', 'account-change', 'context-loss'].includes(name)) {\r\n        origin = 'landed'; destination = null; launchUsed = false;\r\n        return enter('landed');\r\n      }\r\n      if (name === 'cancel') {\r\n        if (['intercept', 'guided-turn'].includes(state)) return result(false, 'turn-required');\r\n        const next = origin;\r\n        destination = null;\r\n        return enter(next);\r\n      }\r\n      if (completion.has(name) && event.token !== token) return result(false, 'token-required');\r\n      if (state === 'landed' && name === 'board') {origin = 'landed'; return enter('boarding');}\r\n      if (state === 'boarding' && name === 'takeoff') return enter('takeoff');\r\n      if (name === 'cruise' && ['takeoff', 'checkpoint', 'jump', 'reentry'].includes(state)) {\r\n        if (state !== 'takeoff' && event.ready !== true) return result(false, 'not-ready');\r\n        if (state === 'reentry' && !covered(destination)) return result(false, 'vegas-only');\r\n        if (state === 'takeoff' || state === 'reentry') launchUsed = false;\r\n        origin = 'cruise'; destination = null; return enter('cruise');\r\n      }\r\n      if (state === 'cruise' && ['checkpoint', 'jump', 'land'].includes(name)) {\r\n        if (name === 'jump') {\r\n          if (event.confirmed !== true || !covered(event.destination))\r\n            return result(false, 'confirmed-vegas-pin-required');\r\n          destination = {lat: event.destination.lat, lng: event.destination.lng};\r\n        }\r\n        origin = 'cruise'; return enter(name === 'land' ? 'land-pin' : name);\r\n      }\r\n      if (state === 'land-pin' && name === 'landing') {\r\n        if (event.confirmed !== true || !covered(event.destination))\r\n          return result(false, 'confirmed-vegas-pin-required');\r\n        return enter('landing');\r\n      }\r\n      if (state === 'landing' && name === 'landed') {\r\n        if (event.ready !== true) return result(false, 'not-ready');\r\n        origin = 'landed'; launchUsed = false; return enter('landed');\r\n      }\r\n      if (['cruise', 'world'].includes(state) && name === 'space') {\r\n        if (launchUsed) return result(false, 'launch-already-used');\r\n        launchUsed = true; origin = state; return enter('space-launch');\r\n      }\r\n      if (state === 'space-launch' && name === 'portal') return enter('space-portal');\r\n      if (state === 'space-portal' && name === 'ready') {\r\n        if (event.ready !== true) return result(false, 'not-ready');\r\n        origin = 'space'; return enter('space');\r\n      }\r\n      if (state === 'space' && name === 'dock') {origin = 'space'; return enter('docking');}\r\n      if (state === 'docking' && name === 'docked') {\r\n        if (event.ready !== true) return result(false, 'not-ready');\r\n        origin = 'space'; return enter('docked');\r\n      }\r\n      if (state === 'docked' && name === 'undock') {origin = 'space'; return enter('space');}\r\n      if (['space', 'world', 'docked'].includes(state) && name === 'reentry') {\r\n        const point = event.destination ?? VEGAS;\r\n        if (!validGeo(point)) return result(false, 'invalid-destination');\r\n        destination = {lat: point.lat, lng: point.lng}; origin = state;\r\n        return enter('reentry');\r\n      }\r\n      if (state === 'reentry' && name === 'world') {\r\n        if (event.ready !== true) return result(false, 'not-ready');\r\n        destination = null; origin = 'world'; launchUsed = false; return enter('world');\r\n      }\r\n      if (state === 'space' && name === 'warning') {origin = 'space'; return enter('territory-warning');}\r\n      if (state === 'territory-warning' && name === 'intercept') return enter('intercept');\r\n      if (state === 'intercept' && name === 'turn') return enter('guided-turn');\r\n      if (['territory-warning', 'guided-turn'].includes(state) && name === 'resume') {\r\n        origin = 'space'; return enter('space');\r\n      }\r\n      return result(false, 'invalid-transition');\r\n    }\r\n  });\r\n}\r\n","modules/coach-flight/coverage.mjs":"// Generated by scripts/build-flight-coverage.mjs from world/v1; regenerate when the bake changes.\r\nexport const bakeVersion=\"v1-20261018\";\r\nconst rows={\"11750\":[[25656,25746]],\"11751\":[[25656,25746]],\"11752\":[[25656,25746]],\"11753\":[[25656,25746]],\"11754\":[[25656,25746]],\"11755\":[[25656,25746]],\"11756\":[[25656,25746]],\"11757\":[[25656,25746]],\"11758\":[[25656,25746]],\"11759\":[[25656,25746]],\"11760\":[[25656,25746]],\"11761\":[[25656,25746]],\"11762\":[[25656,25746]],\"11763\":[[25656,25746]],\"11764\":[[25656,25746]],\"11765\":[[25656,25746]],\"11766\":[[25656,25746]],\"11767\":[[25656,25746]],\"11768\":[[25656,25746]],\"11769\":[[25656,25746]],\"11770\":[[25656,25746]],\"11771\":[[25656,25746]],\"11772\":[[25656,25746]],\"11773\":[[25656,25746]],\"11774\":[[25656,25746]],\"11775\":[[25656,25746]],\"11776\":[[25656,25746]],\"11777\":[[25656,25746]],\"11778\":[[25656,25746]],\"11779\":[[25656,25746]],\"11780\":[[25656,25746]],\"11781\":[[25656,25746]],\"11782\":[[25656,25746]],\"11783\":[[25656,25746]],\"11784\":[[25656,25746]],\"11785\":[[25656,25746]],\"11786\":[[25656,25746]],\"11787\":[[25656,25746]],\"11788\":[[25656,25746]],\"11789\":[[25656,25746]],\"11790\":[[25656,25746]],\"11791\":[[25656,25746]],\"11792\":[[25656,25746]],\"11793\":[[25656,25746]],\"11794\":[[25656,25746]],\"11795\":[[25656,25746]],\"11796\":[[25656,25746]],\"11797\":[[25656,25746]],\"11798\":[[25656,25746]],\"11799\":[[25656,25746]],\"11800\":[[25656,25746]],\"11801\":[[25656,25746]],\"11802\":[[25656,25746]],\"11803\":[[25656,25746]],\"11804\":[[25656,25746]],\"11805\":[[25656,25746]],\"11806\":[[25656,25746]],\"11807\":[[25656,25746]],\"11808\":[[25656,25746]],\"11809\":[[25656,25746]],\"11810\":[[25656,25746]],\"11811\":[[25656,25746]],\"11812\":[[25656,25746]],\"11813\":[[25656,25746]],\"11814\":[[25656,25746]],\"11815\":[[25656,25746]],\"11816\":[[25656,25746]],\"11817\":[[25656,25746]],\"11818\":[[25656,25746]],\"11819\":[[25656,25746]],\"11820\":[[25656,25746]],\"11821\":[[25656,25746]],\"11822\":[[25656,25746]],\"11823\":[[25656,25746]],\"11824\":[[25656,25746]],\"11825\":[[25656,25746]],\"11826\":[[25656,25746]],\"11827\":[[25656,25746]],\"11828\":[[25656,25746]],\"11829\":[[25656,25746]],\"11830\":[[25656,25746]],\"11831\":[[25656,25746]],\"11832\":[[25656,25746]],\"11833\":[[25656,25746]],\"11834\":[[25656,25746]],\"11835\":[[25659,25659],[25661,25746]],\"11836\":[[25657,25659],[25661,25746]],\"11837\":[[25656,25657],[25661,25746]],\"11838\":[[25663,25746]],\"11839\":[[25656,25657],[25663,25746]],\"11840\":[[25656,25656],[25658,25746]],\"11841\":[[25658,25660],[25662,25746]]};\r\nexport const coveredCell=c=>c.z===16&&!!rows[c.x]?.some(([a,b])=>c.y>=a&&c.y<=b);\r\n","modules/coach-flight/dev.html":"<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>MYR5 Flight</title></head><body style=\"margin:0;background:#07131b\"><script type=\"module\">\r\nimport {mountFlight} from './view.mjs';import {freshBase} from '../coach-world/state.mjs';\r\nconst q=new URLSearchParams(location.search),base=freshBase('flight-preview');base.faction=q.get('faction')||'chest';window.flight=mountFlight({host:document.body,getBase:()=>base,owner:'flight-preview',deps:{fixture:q.get('fixture')==='1'},onExit:info=>{document.body.textContent=info?.landingPin?'Choose a landing pin in the main app.':'Flight closed.';}});\r\n// ?space=1 launches into orbit once boarding ends; ?pose=x,y,z,yaw,pitch then places the ship (pitch is applied only if view.mjs reads pose.pitch). ?station=1 is reserved.\r\nconst root=()=>document.querySelector('[data-coach-flight]'),until=async(test,ms=20000)=>{for(const end=performance.now()+ms;performance.now()<end;await new Promise(r=>setTimeout(r,100)))if(test())return true;return false;};\r\nwindow.flight.ready.then(async ok=>{\r\n if(!ok||!(q.get('space')==='1'||q.get('pose')))return;\r\n await until(()=>root()?.dataset.busy==='false');const f=root()?.__flight;if(!f)return;\r\n if(q.get('space')==='1'){await f.launchSpace();await until(()=>root()?.dataset.mode==='space'&&root()?.dataset.busy==='false');}\r\n const [x,y,z,yaw=0,pitch]=(q.get('pose')||'').split(',').map(Number);\r\n if([x,y,z].every(Number.isFinite))f.pose={x,y,z,yaw:Number.isFinite(yaw)?yaw:0,speed:0,...(Number.isFinite(pitch)?{pitch}:{})};\r\n});\r\n</script></body></html>\r\n","modules/coach-flight/ecology.mjs":"// Deterministic regional ecology for MYR5 flight (R81 flight lane, staged: not yet in a checkout). No imports, no renderer,\r\n// no network, no timers. Pure functions plus one THREE builder; the parent owns scene/camera/renderer and calls exactly:\r\n//   biomeAt({lat,lng}, {townAreaMi2=null, townId=null}) -> {id, faction, areaMi2, seed, plant, rock, color}\r\n//   planEcology({origin, sectorId, seed=5, radius=600, count=180, exclusions=[]}) -> {plants, rocks, signals, biome, ...}\r\n//   createEcology(T, {plan, scale=1}) -> {group, update(dt), dispose(), stats}\r\n// SEMANTICS\r\n//  - Regions are a jittered global grid in degrees (rows of fixed latitude step, columns sized per row so cells are ~square in\r\n//    metres) resolved by nearest jittered centre (Voronoi). Nominal cell area is 2 mi^2; a wilderness cell may pair with its east\r\n//    neighbour (same id, suffix '+') only when the two MEASURED Voronoi areas sum to <= 5 mi^2. areaMi2 is measured by sampling,\r\n//    never assumed from spacing. Nothing depends on the viewer, origin, chunk or the player's own faction: faction, plant family,\r\n//    rock family and palette hash from the region id alone.\r\n//  - Towns smaller than 4 mi^2 (cannot hold two nominal biomes) use an explicit coordinate partition: an unjittered grid of\r\n//    k = max(2, ceil(area/1.5)) sub-biomes, each 0.8*area/k, so any town footprint spans >= 2 sub-biome ids and 4-neighbours\r\n//    always get different plant families (2x2 checkerboard). Towns >= 4 mi^2 fall through to the wilderness partition.\r\n//  - Placement samples global geographic cells in INDEPENDENT hash domains (plants, rocks, signals, fauna) keyed by world seed +\r\n//    cell row/col; every instance has a stable id and geographic lat/lng, so the same plant appears at the same global place from\r\n//    any origin or chunk. Local x/z are metres in the origin tangent frame (east=+x, south=+z) matching core.geoToLocal.\r\n//    sectorId is a pass-through label and never seeds anything (chunks must agree on borders).\r\n//  - exclusions: circles {x,z,radius} or rects {x0,x1,z0,z1} in local metres (roads, water, pads, saved bases). Malformed entries\r\n//    are ignored. Signals are broad decorative habitat/NPC-site indicators (decorative:true); they carry no reward, catch or spawn\r\n//    authority and must never be used to create catches client-side.\r\n// PERFORMANCE\r\n//  - planEcology: ~2k cell hashes for radius 600; biome lookups are 9-neighbour Voronoi with per-id cached area sampling (256\r\n//    samples once per region). No allocation beyond the returned records. Candidates beyond `count` are dropped farthest-first.\r\n//  - createEcology: <= 7 draw calls (4 plant families, rocks, signal rings, birds), all InstancedMesh with baked vertex shading on\r\n//    MeshBasicMaterial (no lights). Default maxInstances 120 keeps the worst case under 12k triangles (see TRIS). update(dt) only\r\n//    moves the bird instances and allocates nothing.\r\nexport const NOMINAL_MI2 = 2, WILD_MAX_MI2 = 5, MI2 = 2589988.11, FAMILIES = Object.freeze(['spiral', 'mushroom', 'shard', 'reed']);\r\nexport const ROCKS = Object.freeze(['boulder', 'spire', 'slab']), SUBTYPES = Object.freeze(['lush', 'barren', 'rocky']);\r\nexport const FACTIONS = Object.freeze(['chest', 'quads', 'glutes', 'arms-shoulders', 'yoga', 'martial-arts', 'cardio']);\r\n// Ground/plant/rock tints copied from modules/coach-world/palettes.mjs BASE so flight regions match RTS biome tints.\r\nconst TINT = {chest: ['#9a5a38', '#ff8a4d', '#6b3a2a'], quads: ['#8a8440', '#ffd24a', '#6a6a52'], glutes: ['#8a4a7a', '#ff7fd0', '#6a3a66'],\r\n  'arms-shoulders': ['#4a6a9a', '#6fb2ff', '#3a4a6a'], yoga: ['#3a8a78', '#7dffcf', '#4a6a62'], 'martial-arts': ['#7a3a3a', '#ff4a4a', '#3a2a30'],\r\n  cardio: ['#3a7f8a', '#5ff0ff', '#4a6a78']};\r\nconst RAD = Math.PI / 180, METRES = 6378137 * RAD, NOMINAL_W = Math.sqrt(NOMINAL_MI2 * MI2), JITTER = 0.22, DOMAIN = {region: 11, pair: 23, plant: 37, rock: 53, signal: 71, fauna: 89, town: 97};\r\nconst validGeo = p => !!p && Number.isFinite(p.lat) && Number.isFinite(p.lng) && Math.abs(p.lat) <= 90 && Math.abs(p.lng) <= 180;\r\nfunction assertGeo(p) { if (!validGeo(p)) throw new TypeError('Expected finite {lat,lng} geographic coordinates.'); }\r\nconst cosLat = lat => Math.max(0.05, Math.cos(lat * RAD));\r\nconst hex = c => [1, 3, 5].map(i => parseInt(c.slice(i, i + 2), 16));\r\nconst toHex = v => '#' + v.map(n => Math.round(Math.min(255, Math.max(0, n))).toString(16).padStart(2, '0')).join('');\r\nconst mixHex = (a, b, t) => toHex(hex(a).map((n, i) => n + (hex(b)[i] - n) * t));\r\n/** 32-bit integer hash; every placement/region decision goes through this with an explicit domain. */\r\nexport function hash32(domain, a, b, c = 0, d = 0) {\r\n  let h = Math.imul(domain | 0, 0x9E3779B1) ^ 0x85EBCA6B;\r\n  for (const v of [a, b, c, d]) { h = Math.imul(h ^ (v | 0), 0xC2B2AE35); h ^= h >>> 15; h = Math.imul(h, 0x27D4EB2F); h ^= h >>> 13; }\r\n  return (h ^ h >>> 16) >>> 0;\r\n}\r\nconst unit = (...args) => hash32(...args) / 4294967296;\r\nfunction strHash(s) { let h = 2166136261; for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 16777619); return h >>> 0; }\r\nconst sm = t => t * t * (3 - 2 * t), lerp = (a, b, t) => a + (b - a) * t;\r\n/** Smooth low-frequency field in cell space (clusters, clearings); separate per domain. */\r\nfunction field(domain, x, y, seed) {\r\n  const ix = Math.floor(x), iy = Math.floor(y), fx = sm(x - ix), fy = sm(y - iy), H = (i, j) => unit(domain + 1, ix + i, iy + j, seed);\r\n  return lerp(lerp(H(0, 0), H(1, 0), fx), lerp(H(0, 1), H(1, 1), fx), fy);\r\n}\r\n// ---- regions ----------------------------------------------------------------------------------------------------------------\r\nconst rowStep = w => w / METRES, colStep = (w, rowLat) => w / (METRES * cosLat(rowLat));\r\nfunction cellCentre(w, r, c, jitter, salt) {\r\n  const dLat = rowStep(w), rowLat = (r + 0.5) * dLat, dLng = colStep(w, rowLat);\r\n  const lat = (r + 0.5 + (unit(DOMAIN.region, r, c, salt, 1) - 0.5) * 2 * jitter) * dLat;\r\n  const lng = (c + 0.5 + (unit(DOMAIN.region, r, c, salt, 2) - 0.5) * 2 * jitter) * dLng;\r\n  return {r, c, lat, lng, dLat, dLng, rowLat};\r\n}\r\n/** Nearest jittered centre among the 3x3 neighbourhood of a point's own cell, measured in metres. */\r\nfunction nearestCell(w, lat, lng, jitter, salt) {\r\n  const dLat = rowStep(w), r0 = Math.floor(lat / dLat), k = cosLat(lat);\r\n  let best = null, bestD = Infinity;\r\n  for (let r = r0 - 1; r <= r0 + 1; r++) {\r\n    const c0 = Math.floor(lng / colStep(w, (r + 0.5) * dLat));\r\n    for (let c = c0 - 1; c <= c0 + 1; c++) {\r\n      const cell = cellCentre(w, r, c, jitter, salt), dz = (cell.lat - lat) * METRES, dx = (cell.lng - lng) * METRES * k, d = dx * dx + dz * dz;\r\n      if (d < bestD) { bestD = d; best = cell; }\r\n    }\r\n  }\r\n  return best;\r\n}\r\nconst areaCache = new Map();\r\n/** Measured Voronoi area (mi^2) of one cell: 16x16 samples over a 2x2-cell window around its nominal box. */\r\nfunction measureArea(w, r, c, jitter, salt) {\r\n  const key = `${w}:${r}:${c}:${salt}`;\r\n  if (areaCache.has(key)) return areaCache.get(key);\r\n  const dLat = rowStep(w), rowLat = (r + 0.5) * dLat, dLng = colStep(w, rowLat), N = 16;\r\n  let hits = 0;\r\n  for (let i = 0; i < N; i++) for (let j = 0; j < N; j++) {\r\n    const lat = (r - 0.5 + (i + 0.5) * 2 / N) * dLat, lng = (c - 0.5 + (j + 0.5) * 2 / N) * dLng, n = nearestCell(w, lat, lng, jitter, salt);\r\n    if (n.r === r && n.c === c) hits++;\r\n  }\r\n  const cellM2 = dLat * METRES * dLng * METRES * cosLat(rowLat), area = hits / (N * N) * 4 * cellM2 / MI2;\r\n  if (areaCache.size > 4096) areaCache.clear();\r\n  areaCache.set(key, area);\r\n  return area;\r\n}\r\nfunction describe(id, plant, extra) {\r\n  const seed = strHash(id), faction = FACTIONS[seed % FACTIONS.length], sub = SUBTYPES[(seed >>> 8) % SUBTYPES.length];\r\n  const rock = ROCKS[(seed >>> 16) % ROCKS.length], t = TINT[faction], secondary = FAMILIES[(FAMILIES.indexOf(plant) + 1 + (seed >>> 4) % 3) % 4];\r\n  const tone = sub === 'barren' ? ['#d8c8a0', 0.35, 0.6, 0.35] : sub === 'rocky' ? ['#202020', 0.3, 0.2, 0.3] : [null, 0, 0, 0];\r\n  const color = {ground: tone[0] ? mixHex(t[0], tone[0], tone[1]) : t[0], plant: tone[0] ? mixHex(t[1], tone[0], tone[2]) : t[1],\r\n    rock: tone[0] ? mixHex(t[2], tone[0], tone[3]) : t[2]};\r\n  return Object.freeze({id, faction, seed, plant, secondary, rock, subtype: sub, color: Object.freeze(color), ...extra});\r\n}\r\nexport function biomeAt(point, {townAreaMi2 = null, townId = null} = {}) {\r\n  assertGeo(point);\r\n  const town = Number.isFinite(townAreaMi2) && townAreaMi2 > 0 && townAreaMi2 < 2 * NOMINAL_MI2 ? townAreaMi2 : null;\r\n  if (town) {\r\n    const k = Math.max(2, Math.ceil(town / 1.5)), subMi2 = 0.8 * town / k, w = Math.sqrt(subMi2 * MI2), name = String(townId ?? 'town');\r\n    const dLat = rowStep(w), r = Math.floor(point.lat / dLat), c = Math.floor(point.lng / colStep(w, (r + 0.5) * dLat));\r\n    const id = `t:${name}:${r}:${c}`, base = strHash(name), plant = FAMILIES[(base + (r & 1) + 2 * (c & 1)) % 4];\r\n    return describe(id, plant, {areaMi2: subMi2, town: name, kind: 'town'});\r\n  }\r\n  const cell = nearestCell(NOMINAL_W, point.lat, point.lng, JITTER, 0);\r\n  let {r, c} = cell, area = measureArea(NOMINAL_W, r, c, JITTER, 0), merged = false;\r\n  const left = c & 1 ? c - 1 : c, wantsPair = unit(DOMAIN.pair, r, left, 0) < 0.35;\r\n  if (wantsPair) {\r\n    const sum = measureArea(NOMINAL_W, r, left, JITTER, 0) + measureArea(NOMINAL_W, r, left + 1, JITTER, 0);\r\n    if (sum <= WILD_MAX_MI2) { merged = true; c = left; area = sum; }\r\n  }\r\n  const id = `w:${r}:${c}${merged ? '+' : ''}`, plant = FAMILIES[hash32(DOMAIN.region, r, c, merged ? 1 : 0, 7) % 4];\r\n  return describe(id, plant, {areaMi2: area, kind: merged ? 'wilderness' : 'region', merged});\r\n}\r\n// ---- placement --------------------------------------------------------------------------------------------------------------\r\nconst GRID = {plant: {w: 28, p: 0.16, domain: DOMAIN.plant}, rock: {w: 46, p: 0.12, domain: DOMAIN.rock}, signal: {w: 170, p: 0.32, domain: DOMAIN.signal}};\r\nconst SIGNAL_KINDS = ['habitat', 'nest', 'tracks', 'npc-site', 'grazing'];\r\nfunction toLocal(point, origin, k) {\r\n  let d = point.lng - origin.lng; if (d > 180) d -= 360; if (d < -180) d += 360;\r\n  return {x: d * METRES * k, z: (origin.lat - point.lat) * METRES};\r\n}\r\nfunction excludedBy(exclusions, p) {\r\n  for (const e of exclusions) {\r\n    if (!e) continue;\r\n    if (Number.isFinite(e.radius)) { const dx = p.x - e.x, dz = p.z - e.z; if (dx * dx + dz * dz < e.radius * e.radius) return true; }\r\n    else if (Number.isFinite(e.x0) && p.x >= e.x0 && p.x <= e.x1 && p.z >= e.z0 && p.z <= e.z1) return true;\r\n  }\r\n  return false;\r\n}\r\n/** Walk every grid cell of one domain within `radius` of origin, yielding deterministic candidate records. */\r\nfunction scatter(kind, origin, seed, radius, townOpts, make) {\r\n  const {w, p, domain} = GRID[kind], k = cosLat(origin.lat), dLat = rowStep(w), out = [];\r\n  const rLo = Math.floor((origin.lat - radius / METRES) / dLat), rHi = Math.floor((origin.lat + radius / METRES) / dLat);\r\n  for (let r = rLo; r <= rHi; r++) {\r\n    const rowLat = (r + 0.5) * dLat, dLng = colStep(w, rowLat), span = radius / (METRES * k);\r\n    const cLo = Math.floor((origin.lng - span) / dLng), cHi = Math.floor((origin.lng + span) / dLng);\r\n    for (let c = cLo; c <= cHi; c++) {\r\n      const u = unit(domain, r, c, seed, 0), density = field(domain, r / 7, c / 7, seed);\r\n      if (u > p * (0.35 + 1.3 * density)) continue;\r\n      const lat = (r + 0.1 + 0.8 * unit(domain, r, c, seed, 1)) * dLat, lng = (c + 0.1 + 0.8 * unit(domain, r, c, seed, 2)) * dLng;\r\n      const local = toLocal({lat, lng}, origin, k), d2 = local.x * local.x + local.z * local.z;\r\n      if (d2 > radius * radius) continue;\r\n      const biome = biomeAt({lat, lng}, townOpts);\r\n      out.push(make({id: `${kind[0]}:${r}:${c}`, x: local.x, z: local.z, lat, lng, biome: biome.id, faction: biome.faction, d2}, biome, r, c));\r\n    }\r\n  }\r\n  return out.sort((a, b) => a.d2 - b.d2 || (a.id < b.id ? -1 : 1));\r\n}\r\nconst strip = list => list.map(({d2, ...rest}) => rest);\r\nexport function planEcology({origin, sectorId = null, seed = 5, radius = 600, count = 180, exclusions = [], town = {}} = {}) {\r\n  assertGeo(origin);\r\n  if (!Number.isFinite(radius) || radius <= 0 || radius > 5000) throw new RangeError('radius must be 0 < r <= 5000 metres.');\r\n  const ex = Array.isArray(exclusions) ? exclusions : [], keep = p => !excludedBy(ex, p), s = seed | 0, n = Math.max(0, count | 0);\r\n  const plants = scatter('plant', origin, s, radius, town, (rec, b, r, c) => {\r\n    const v = unit(DOMAIN.plant, r, c, s, 3), hero = v < 0.03;\r\n    return {...rec, family: unit(DOMAIN.plant, r, c, s, 4) < 0.78 ? b.plant : b.secondary, scale: hero ? 2.2 : 0.7 + 0.6 * unit(DOMAIN.plant, r, c, s, 5),\r\n      rot: unit(DOMAIN.plant, r, c, s, 6) * Math.PI * 2, variant: hero ? 'hero' : 'common', color: b.color.plant};\r\n  }).filter(keep).slice(0, n);\r\n  const rocks = scatter('rock', origin, s, radius, town, (rec, b, r, c) => ({...rec, family: b.rock,\r\n    scale: 0.6 + 1.1 * unit(DOMAIN.rock, r, c, s, 5), rot: unit(DOMAIN.rock, r, c, s, 6) * Math.PI * 2, color: b.color.rock})).filter(keep).slice(0, Math.ceil(n / 2));\r\n  const signals = scatter('signal', origin, s, radius, town, (rec, b, r, c) => ({...rec, kind: SIGNAL_KINDS[hash32(DOMAIN.signal, r, c, s, 7) % SIGNAL_KINDS.length],\r\n    radius: 20 + 30 * unit(DOMAIN.signal, r, c, s, 8), decorative: true, authority: 'none'})).filter(keep).slice(0, 24);\r\n  return {origin: {lat: origin.lat, lng: origin.lng}, sector: sectorId, seed: s, radius, biome: biomeAt(origin, town),\r\n    plants: strip(plants), rocks: strip(rocks), signals: strip(signals)};\r\n}\r\n// ---- THREE builder ----------------------------------------------------------------------------------------------------------\r\n/** Triangle-list builder with height-baked vertex shading; instance colour multiplies these vertex colours. */\r\nfunction mesh3(T, tris, dark = 0.5) {\r\n  const pos = new Float32Array(tris.length * 3), col = new Float32Array(tris.length * 3);\r\n  let lo = Infinity, hi = -Infinity;\r\n  for (const v of tris) { lo = Math.min(lo, v[1]); hi = Math.max(hi, v[1]); }\r\n  tris.forEach((v, i) => { pos.set(v, i * 3); const t = dark + (1 - dark) * ((v[1] - lo) / Math.max(1e-6, hi - lo)); col.set([t, t, t], i * 3); });\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3)); g.computeVertexNormals();\r\n  return g;\r\n}\r\nconst ring = (n, rad, y, twist = 0, cx = 0, cz = 0) => Array.from({length: n}, (_, i) => { const a = twist + i / n * Math.PI * 2; return [cx + Math.cos(a) * rad, y, cz + Math.sin(a) * rad]; });\r\nfunction tube(out, a, b) { for (let i = 0; i < a.length; i++) { const j = (i + 1) % a.length; out.push(a[i], b[i], b[j], a[i], b[j], a[j]); } }\r\nfunction fan(out, centre, r, flip = false) { for (let i = 0; i < r.length; i++) { const j = (i + 1) % r.length; out.push(centre, flip ? r[i] : r[j], flip ? r[j] : r[i]); } }\r\nconst SHAPES = {\r\n  spiral(T) { // tapering 5-gon trunk + helical ribbon: 20 + 5 + 20 = 45 tris\r\n    const t = [], r0 = ring(5, 0.32, 0), r1 = ring(5, 0.2, 1.6, 0.4), r2 = ring(5, 0.08, 3.2, 0.8);\r\n    tube(t, r0, r1); tube(t, r1, r2); fan(t, [0, 3.3, 0], r2);\r\n    for (let i = 0; i < 10; i++) { const a = i * 0.9, b = a + 0.9, y = 0.5 + i * 0.26, y2 = y + 0.26, w = 0.55 - i * 0.03;\r\n      const p = k => [Math.cos(k) * w, 0, Math.sin(k) * w];\r\n      const A = [Math.cos(a) * 0.2, y, Math.sin(a) * 0.2], B = [Math.cos(b) * 0.2, y2, Math.sin(b) * 0.2], C = p(b), D = p(a);\r\n      C[1] = y2 + 0.15; D[1] = y + 0.15; t.push(A, B, C, A, C, D); }\r\n    return mesh3(T, t, 0.45);\r\n  },\r\n  mushroom(T) { // 6-gon stem (24) + 8-gon cap rings (32) + top/under fans (16) = 72 tris\r\n    const t = [], s0 = ring(6, 0.18, 0), s1 = ring(6, 0.14, 0.9), s2 = ring(6, 0.22, 1.7), c0 = ring(8, 1.1, 1.75), c1 = ring(8, 0.8, 2.25), c2 = ring(8, 0.3, 2.6);\r\n    tube(t, s0, s1); tube(t, s1, s2); tube(t, c0, c1); tube(t, c1, c2); fan(t, [0, 2.7, 0], c2); fan(t, [0, 1.65, 0], c0, true);\r\n    return mesh3(T, t, 0.5);\r\n  },\r\n  shard(T) { // three leaning 4-gon crystals, 8 tris each = 24 tris\r\n    const t = [];\r\n    [[0, 0, 2.6, 0.3], [0.5, 0.2, 1.7, 0.22], [-0.45, -0.25, 1.3, 0.18]].forEach(([cx, cz, h, w], i) => {\r\n      const base = ring(4, w, 0, i, cx, cz), top = [cx + 0.2 * i, h, cz - 0.15 * i]; fan(t, top, base, true); fan(t, [cx, -0.3, cz], base);\r\n    });\r\n    return mesh3(T, t, 0.35);\r\n  },\r\n  reed(T) { // five thin blades, 2 tris each = 10 tris (DoubleSide material)\r\n    const t = [];\r\n    for (let i = 0; i < 5; i++) { const a = i * 1.26, x = Math.cos(a) * 0.25, z = Math.sin(a) * 0.25, h = 1.6 + 0.4 * (i % 3), lean = 0.35;\r\n      t.push([x - 0.06, 0, z], [x + 0.06, 0, z], [x + lean * Math.cos(a), h, z + lean * Math.sin(a)], [x - 0.06, 0, z], [x + lean * Math.cos(a), h, z + lean * Math.sin(a)], [x - 0.04, h * 0.5, z]); }\r\n    return mesh3(T, t, 0.55);\r\n  },\r\n  rock(T) { // deterministically dented icosahedron = 20 tris\r\n    const g = new T.IcosahedronGeometry(1, 0), p = g.attributes.position, t = [];\r\n    for (let i = 0; i < p.count; i++) { const v = [p.getX(i), p.getY(i) * 0.7 + 0.5, p.getZ(i)], k = 0.8 + 0.35 * unit(DOMAIN.rock, Math.round(v[0] * 97), Math.round(v[2] * 89), 0); t.push([v[0] * k, v[1], v[2] * k]); }\r\n    g.dispose(); return mesh3(T, t, 0.4);\r\n  },\r\n  signal(T) { // flat 10-segment habitat ring = 20 tris\r\n    const t = []; tube(t, ring(10, 1, 0.05), ring(10, 0.75, 0.05)); return mesh3(T, t, 1);\r\n  },\r\n  bird(T) { return mesh3(T, [[-1, 0, 0], [0, 0.15, 0.3], [0, 0, -0.3], [0, 0, -0.3], [0, 0.15, 0.3], [1, 0, 0]], 0.3); },\r\n};\r\nexport const TRIS = Object.freeze({spiral: 45, mushroom: 72, shard: 24, reed: 10, rock: 20, signal: 20, bird: 2});\r\nconst SIZE = {spiral: 1, mushroom: 0.8, shard: 0.9, reed: 0.9, boulder: 1, spire: [0.7, 2.2, 0.7], slab: [1.8, 0.4, 1.3]};\r\nexport function createEcology(T, {plan, scale = 1, maxInstances = 120, birds = 12} = {}) {\r\n  if (!T?.InstancedMesh || !plan?.plants) throw new TypeError('createEcology needs the THREE namespace and a planEcology() result.');\r\n  const group = new T.Group(), dummy = new T.Object3D(), tint = new T.Color(), meshes = [], stats = {draws: 0, triangles: 0, instances: 0};\r\n  group.name = 'ecology';\r\n  const mat = new T.MeshBasicMaterial({vertexColors: true, side: T.DoubleSide});\r\n  const add = (name, geometry, items, place) => {\r\n    if (!items.length) { geometry.dispose(); return; }\r\n    const m = new T.InstancedMesh(geometry, mat, items.length); m.name = name; m.frustumCulled = false;\r\n    items.forEach((it, i) => { place(it, i); dummy.updateMatrix(); m.setMatrixAt(i, dummy.matrix); m.setColorAt(i, tint.set(it.color || '#ffffff')); });\r\n    m.instanceMatrix.needsUpdate = true; if (m.instanceColor) m.instanceColor.needsUpdate = true;\r\n    group.add(m); meshes.push(m); stats.draws++; stats.triangles += items.length * geometry.attributes.position.count / 3; stats.instances += items.length;\r\n  };\r\n  const budget = Math.max(0, maxInstances | 0), plants = plan.plants.slice(0, Math.ceil(budget * 0.75)), rocks = plan.rocks.slice(0, budget - plants.length);\r\n  for (const family of FAMILIES) add(family, SHAPES[family](T), plants.filter(p => p.family === family), (p, i) => {\r\n    dummy.position.set(p.x * scale, (p.y || 0) * scale, p.z * scale); dummy.rotation.set(0, p.rot, 0); const s = p.scale * scale * SIZE[family]; dummy.scale.set(s, s, s);\r\n  });\r\n  add('rocks', SHAPES.rock(T), rocks, r => {\r\n    const k = SIZE[r.family] || 1, v = Array.isArray(k) ? k : [k, k, k], s = r.scale * scale;\r\n    dummy.position.set(r.x * scale, (r.y || 0) * scale, r.z * scale); dummy.rotation.set(0, r.rot, 0); dummy.scale.set(v[0] * s, v[1] * s, v[2] * s);\r\n  });\r\n  add('signals', SHAPES.signal(T), (plan.signals || []).slice(0, 24), s => {\r\n    dummy.position.set(s.x * scale, (s.y || 0) * scale + .1, s.z * scale); dummy.rotation.set(0, 0, 0); dummy.scale.set(s.radius * scale, 1, s.radius * scale);\r\n    s.color = s.color || mixHex(TINT[s.faction]?.[1] || '#ffffff', '#ffffff', 0.4);\r\n  });\r\n  const flock = Array.from({length: Math.max(0, Math.min(40, birds | 0))}, (_, i) => ({i, a: unit(DOMAIN.fauna, plan.seed, i, 0) * Math.PI * 2,\r\n    r: (60 + 140 * unit(DOMAIN.fauna, plan.seed, i, 1)) * scale, y: (40 + 50 * unit(DOMAIN.fauna, plan.seed, i, 2)) * scale, w: 0.25 + 0.2 * unit(DOMAIN.fauna, plan.seed, i, 3),\r\n    color: TINT[plan.biome?.faction]?.[2] || '#333333'}));\r\n  add('birds', SHAPES.bird(T), flock, b => { dummy.position.set(Math.cos(b.a) * b.r, b.y, Math.sin(b.a) * b.r); dummy.rotation.set(0, -b.a, 0); dummy.scale.set(1.2 * scale, scale, 1.2 * scale); });\r\n  const birdMesh = meshes.find(m => m.name === 'birds'); let time = 0;\r\n  function update(dt = 0) { // ambient flock orbit + wing flap; no allocation\r\n    if (!birdMesh) return; time += Math.min(0.1, Math.max(0, dt || 0));\r\n    flock.forEach((b, i) => { const a = b.a + time * b.w, flap = 0.6 + 0.4 * Math.sin(time * 9 + b.i);\r\n      dummy.position.set(Math.cos(a) * b.r, b.y + Math.sin(time * 0.7 + b.i) * 2 * scale, Math.sin(a) * b.r); dummy.rotation.set(0, -a, 0);\r\n      dummy.scale.set(1.2 * scale, scale * flap, 1.2 * scale); dummy.updateMatrix(); birdMesh.setMatrixAt(i, dummy.matrix); });\r\n    birdMesh.instanceMatrix.needsUpdate = true;\r\n  }\r\n  function dispose() { for (const m of meshes) { group.remove(m); m.geometry.dispose(); m.dispose?.(); } mat.dispose(); group.parent?.remove(group); meshes.length = 0; }\r\n  return {group, update, dispose, stats};\r\n}\r\n","modules/coach-flight/look.mjs":"// Opt-in, rotation-only cockpit look. Ship yaw/pitch and flight pose remain controller-owned.\r\nimport {createPoseSource} from '../coach-world-rts/pose-source.mjs';\r\n\r\nexport function createFlightLook(T,{win=globalThis,signal}={}){\r\n let source=null,enabled=false,neutral=null,generation=0,disposed=false;\r\n const stop=()=>{generation++;enabled=false;neutral=null;source?.dispose();source=null;};\r\n const reset=()=>{neutral=null;};\r\n const onVisibility=()=>{if(win.document?.hidden)reset();};\r\n win.document?.addEventListener?.('visibilitychange',onVisibility);\r\n win.addEventListener?.('orientationchange',reset);\r\n const dispose=()=>{\r\n  if(disposed)return;disposed=true;stop();\r\n  win.document?.removeEventListener?.('visibilitychange',onVisibility);\r\n  win.removeEventListener?.('orientationchange',reset);\r\n  signal?.removeEventListener?.('abort',dispose);\r\n };\r\n if(signal?.aborted)dispose();else signal?.addEventListener?.('abort',dispose,{once:true});\r\n return {\r\n  get enabled(){return enabled;},\r\n  // The permission calls run before the first await, inside the caller's tap.\r\n  async toggle(){\r\n   if(disposed)return false;\r\n   if(source){stop();return false;}\r\n   const candidate=createPoseSource({win}),ticket=++generation;source=candidate;\r\n   const permission=await candidate.requestPermission();\r\n   if(disposed||ticket!==generation||source!==candidate)return false;\r\n   if(permission!=='granted'){stop();return false;}\r\n   candidate.begin(permission);enabled=true;neutral=null;return true;\r\n  },\r\n  apply(camera){\r\n   if(!enabled||!camera?.quaternion)return false;\r\n   const pose=source?.pose();\r\n   if(pose?.source!=='imu3dof'){\r\n    if(source?.mode==='drag')stop(); // permission granted but no usable sensor arrived\r\n    return false;\r\n   }\r\n   const raw=pose.quaternion;\r\n   if(!raw?.every(Number.isFinite))return false;\r\n   const sensor=new T.Quaternion(...raw).normalize();\r\n   if(!neutral){neutral=sensor.clone();return false;}\r\n   camera.quaternion.multiply(neutral.clone().invert().multiply(sensor)).normalize();\r\n   return true;\r\n  },\r\n  reset,dispose,\r\n };\r\n}\r\n","modules/coach-flight/navigation.mjs":"import {WORLD_SITES} from '../coach-world/sites.mjs';\r\nimport {cityCovered} from './city-scene.mjs';\r\nimport {normalizePlace} from './travel.mjs';\r\n// Explicit address searches only. No autocomplete/background location lookup.\r\nlet lastSearch=0;\r\nexport async function searchAddress(query,{signal,fetchImpl=fetch}={}){\r\n const q=String(query).trim();if(q.length<3||q.length>180)throw new Error('Enter an address or place name.');\r\n if(Date.now()-lastSearch<1100)throw new Error('Wait a moment before searching again.');lastSearch=Date.now();\r\n const local=publicDestinations().filter(p=>p.label.toLowerCase().includes(q.toLowerCase()));if(local.length)return local.map(normalizePlace);\r\n const r=await fetchImpl('/api/flight/address',{method:'POST',signal,credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({address:q})});if(!r.ok)throw new Error('Address search unavailable. Pick a map pin instead.');\r\n return (await r.json()).places.map(normalizePlace).filter(cityCovered);\r\n}\r\nexport function publicDestinations(){return WORLD_SITES.map(s=>({source:'site',id:s.id,label:s.name||s.label||s.id,lat:s.latitude,lng:s.longitude})).filter(cityCovered);}\r\nexport function navigationMenu(ui,{store,current,home,onJump,onClose,signal,getFriends}={}){\r\n const doc=ui.root.ownerDocument,menu=ui.menu;ui.openMenu();menu.replaceChildren();let gone=false;\r\n const ac=new AbortController(),clean=()=>{gone=true;ac.abort();ui.closeMenu();};signal?.addEventListener('abort',clean,{once:true});\r\n const node=(tag,text)=>{const n=doc.createElement(tag);if(text!=null)n.textContent=text;return n;};\r\n const msg=node('p','Choose a place to preview and jump. Only Las Vegas has detailed flight and landing.');msg.setAttribute('role','status');\r\n const close=node('button','Back to flight');close.onclick=()=>{clean();onClose?.();};menu.append(close,node('h3','NAVIGATION'),msg);\r\n const button=(text,fn,parent=menu)=>{const b=node('button',text);b.onclick=fn;parent.append(b);return b;};\r\n function preview(p){if(gone)return;ui.reset();menu.replaceChildren(close,node('h3',p.label),node('p',`${p.lat?.toFixed(4)??''} ${p.lng?.toFixed(4)??''} · virtual destination`));\r\n  button('Jump here',async()=>{clean();await onJump(p);});button('Save location',()=>{try{store.save({...p,source:p.source==='home'?'pin':p.source});msg.textContent='Saved.';list();}catch(e){if(e.code==='SLOTS_FULL'){menu.append(node('p','Choose one of the ten slots to replace:'));store.list().forEach((old,i)=>button(`${i+1}. ${old?.label||'Empty'}`,()=>{store.save({...p,source:p.source==='home'?'pin':p.source},i);list();}));}else{menu.append(node('p',e.message));}}});button('Back to places',list);}\r\n function list(){if(gone)return;menu.replaceChildren(close,node('h3','NAVIGATION'),msg);\r\n  const form=node('form'),search=node('input');search.placeholder='Street address, Las Vegas, NV';search.setAttribute('aria-label','Address or place');search.maxLength=100;const go=node('button','Search address');go.type='submit';form.append(search,go);form.onsubmit=async e=>{e.preventDefault();go.disabled=true;try{const found=await searchAddress(search.value,{signal:ac.signal});if(gone)return;results.replaceChildren();found.forEach(p=>button(p.label,()=>preview(p),results));msg.textContent=found.length?'Select the intended address.':'No match. Try a nearby place or use the landing map.';}catch(e){if(!gone)msg.textContent=e.message;}finally{go.disabled=false;}};menu.append(form);const results=node('div');menu.append(results);const credit=node('a','Address approximation: US Census Geocoder');credit.href='https://www.census.gov/programs-surveys/geography/technical-documentation/complete-technical-documentation/census-geocoder.html';credit.target='_blank';credit.rel='noopener';menu.append(credit);\r\n  button('Home / Vegas approach',()=>preview({...home,source:'home',label:'Home / Vegas approach'}));button('Save current position',()=>preview({...current(),source:'pin',label:'Saved flight position'}));\r\n  menu.append(node('h3','SAVED LOCATIONS · 10 SLOTS'));store.list().forEach((p,i)=>{const row=node('div');menu.append(row);if(!p){row.append(node('span',`${i+1}. Empty`));return;}button(`${i+1}. ${p.label}`,()=>preview(p),row);button('Rename',()=>{const input=node('input');input.value=p.label;input.setAttribute('aria-label','Location name');row.append(input);button('Save name',()=>{try{store.rename(i,input.value);list();}catch(e){msg.textContent=e.message;}},row);},row);button('Delete',()=>{store.remove(i);list();},row);});\r\n  menu.append(node('h3','VISITED / NPC SITES'));const places=[...store.history(),...publicDestinations()];const seen=new Set();for(const p of places){const id=p.source+':'+p.id;if(seen.has(id))continue;seen.add(id);button(p.label,()=>preview(p));}\r\n  if(getFriends)button('Refresh shared friend destinations',async()=>{msg.textContent='Checking current sharing permissions…';try{const friends=await getFriends();if(gone)return;for(const f of friends||[]){if(!f.home?.consent||!cityCovered({lat:f.home.latitude,lng:f.home.longitude}))continue;const b=button(f.name||f.publicId||'Shared friend',()=>preview({source:'friend',id:f.publicId,friendRef:f.publicId,label:f.name||'Shared friend base'}));b.dataset.friend='';}msg.textContent='Only currently shared bases in Vegas can be visited.';}catch(e){msg.textContent='Shared destinations unavailable.';}});\r\n }\r\n list();return {dispose:clean};\r\n}\r\n","modules/coach-flight/perf-overlay.mjs":"// ?perf=1 evidence overlay: frame pacing + renderer counters, DOM only (no renderer, no timers).\r\n// The caller ticks it once per frame; text is rebuilt at most every PAINT_MS.\r\nconst WINDOW = 60;        // frame deltas kept\r\nconst PAINT_MS = 500;     // text refresh cadence\r\nconst MISS_MS = 40;       // a frame longer than this is a missed frame\r\nconst GAP_MS = 2000;      // longer gaps are background/hidden-tab pauses, not frames\r\n// ponytail: sort-on-paint (60 floats, every 500 ms) is cheap; a streaming quantile sketch only if WINDOW grows.\r\n\r\nconst quantileIndex = (n, q) => Math.max(0, Math.min(n - 1, Math.ceil(q * n) - 1));\r\n\r\nexport function createPerfOverlay(host, {getStats, contextLabel = 'contexts'} = {}) {\r\n  const node = host.ownerDocument.createElement('div');\r\n  node.setAttribute('data-flight-perf', '');\r\n  node.setAttribute('aria-hidden', 'true');\r\n  node.style.cssText = 'position:absolute;left:8px;top:8px;z-index:5;pointer-events:none;'\r\n    + 'font:11px monospace;color:#efffee;background:#07131bcc;padding:4px 6px;white-space:pre';\r\n  host.append(node);\r\n\r\n  const dts = new Float64Array(WINDOW), sorted = new Float64Array(WINDOW);\r\n  let count = 0, head = 0, prevMs = null, lastPaintMs = null, disposed = false;\r\n\r\n  function paint() {\r\n    const s = sorted.subarray(0, count);\r\n    s.set(dts.subarray(0, count));\r\n    s.sort();\r\n    let misses = 0;\r\n    for (let i = 0; i < count; i++) if (dts[i] > MISS_MS) misses++;\r\n    const st = getStats() || {};\r\n    node.textContent = `dt p50 ${s[quantileIndex(count, 0.5)].toFixed(1)} p95 ${s[quantileIndex(count, 0.95)].toFixed(1)}`\r\n      + ` | miss ${(misses / count * 100).toFixed(1)}% (>${MISS_MS}ms)`\r\n      + ` | calls ${st.calls ?? 0} tris ${st.triangles ?? 0}`\r\n      + ` | geo ${st.geometries ?? 0} tex ${st.textures ?? 0} | ${contextLabel} ${st.contexts ?? 0}`;\r\n  }\r\n\r\n  return {\r\n    reset() {\r\n      count = 0; head = 0; prevMs = null; lastPaintMs = null;\r\n    },\r\n    tick(nowMs) {\r\n      if (disposed) return;\r\n      if (prevMs !== null) {\r\n        const dt = nowMs - prevMs;\r\n        if (dt > 0 && dt < GAP_MS) {\r\n          dts[head] = dt;\r\n          head = (head + 1) % WINDOW;\r\n          if (count < WINDOW) count++;\r\n        }\r\n      }\r\n      prevMs = nowMs;\r\n      lastPaintMs ??= nowMs;\r\n      if (count > 0 && nowMs - lastPaintMs >= PAINT_MS) {\r\n        lastPaintMs = nowMs;\r\n        paint();\r\n      }\r\n    },\r\n    dispose() {\r\n      disposed = true;\r\n      node.remove();\r\n    },\r\n  };\r\n}\r\n","modules/coach-flight/space-scene.mjs":"// Orbital + compressed-world scenery for the flight cockpit (R81 flight lane, staged: not yet in a checkout).\r\n// createSpaceScene(T,{seed,mode,color,station}) -> {group,update(dt,pose,range),setMode(mode),dispose(),vegasDirection(out)}\r\n// INTEGRATION: the parent owns renderer, camera, scene.add(group), pose and mode. T is the shared THREE namespace (no import here).\r\n//  - 'space': camera starts at SPACE_START, looks at the origin; the globe is static (Vegas never moves). Needs camera.far >= 1500.\r\n//  - 'world': camera starts at WORLD_START (35 m over local ground y=0); features wrap every WORLD_WRAP m around pose.x/z, so the\r\n//    world loops seamlessly; the ground, sky and curvature follow the pose. Needs camera.far >= 3000. Pose y is the camera height.\r\n//  - 'space' also holds the one destination, the original MOM station (station-mesh.mjs): fixed at station.pos, no drift, two LOD levels\r\n//    (far 3 draws / near 6 draws). update(dt,pose,range) hands range ({phase,...} from stationRange) to the station's gates and docking lights.\r\n//  - Everything is vertex-coloured MeshBasic with baked sun light, no lights/textures/postprocessing. Active mode is < 20 draws and\r\n//    < 20k triangles (see sceneStats). Call update(dt,pose) once per frame; it allocates nothing.\r\n//  - Globe landmass is original coarse polygons (not downloaded data); Vegas is lat 36.1147 lng -115.1728, yawed to face the start view.\r\nimport {STATION} from './station.mjs';\r\nimport {createStationMesh} from './station-mesh.mjs';\r\nexport const SPACE_RADIUS = 95;\r\nexport const SPACE_START = Object.freeze({x: 0, y: 110, z: 230});\r\nexport const WORLD_START = Object.freeze({x: 0, y: 35, z: 0});\r\nexport const WORLD_WRAP = 3200;\r\n\r\nconst R = SPACE_RADIUS, RAD = Math.PI / 180, VEGAS = {lat: 36.1147, lng: -115.1728};\r\nexport const GLOBE_YAW_DEG = -18 - VEGAS.lng; // adds to longitude so Vegas sits ~19 deg off the start view axis, north up\r\nconst WR = 3200, GR = 2500, DOME = 2700, TAU = Math.PI * 2; // world: compressed-globe radius, ground radius, sky radius\r\nconst fract = x => x - Math.floor(x), clamp01 = x => Math.min(1, Math.max(0, x));\r\nconst h3 = (x, y, z) => fract(Math.sin(x * 127.1 + y * 311.7 + z * 74.7) * 43758.5453);\r\nconst sm = t => t * t * (3 - 2 * t), lerp = (a, b, t) => a + (b - a) * t;\r\nfunction noise3(x, y, z) {\r\n  const ix = Math.floor(x), iy = Math.floor(y), iz = Math.floor(z), fx = sm(x - ix), fy = sm(y - iy), fz = sm(z - iz);\r\n  const H = (i, j, k) => h3(ix + i, iy + j, iz + k);\r\n  return lerp(lerp(lerp(H(0, 0, 0), H(1, 0, 0), fx), lerp(H(0, 1, 0), H(1, 1, 0), fx), fy),\r\n    lerp(lerp(H(0, 0, 1), H(1, 0, 1), fx), lerp(H(0, 1, 1), H(1, 1, 1), fx), fy), fz);\r\n}\r\nfunction mulberry32(a) {\r\n  return () => {a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296;};\r\n}\r\nconst mix = (a, b, t) => [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)];\r\nconst SUN = (() => {const l = Math.hypot(-.45, .55, .7); return [-.45 / l, .55 / l, .7 / l];})();\r\nconst shade = (x, y, z) => .2 + .8 * Math.max(0, x * SUN[0] + y * SUN[1] + z * SUN[2]);\r\n\r\n/** Unit vector for lat/lng degrees; y = north pole, lng 0 = +z, east = +x. */\r\nexport function latLngToDir(lat, lng, out = {x: 0, y: 0, z: 0}) {\r\n  const c = Math.cos(lat * RAD);\r\n  out.x = c * Math.sin(lng * RAD); out.y = Math.sin(lat * RAD); out.z = c * Math.cos(lng * RAD);\r\n  return out;\r\n}\r\n/** Inverse of the globe mapping INCLUDING the Vegas yaw: scene direction -> real lat/lng. */\r\nexport function dirToLatLng(d) {\r\n  const l = Math.hypot(d.x, d.y, d.z) || 1;\r\n  let lng = Math.atan2(d.x, d.z) / RAD - GLOBE_YAW_DEG;\r\n  lng = ((lng + 540) % 360) - 180;\r\n  return {lat: Math.asin(d.y / l) / RAD, lng};\r\n}\r\n\r\n// Authored coarse landmasses, flat [lng,lat,...] loops (rough on purpose: low-poly cells are 5 degrees).\r\nconst P = s => s.trim().split(/[\\s,]+/).map(Number);\r\nconst LAND = [\r\n  P(`-166 68 -156 71 -141 70 -128 70 -115 68 -95 72 -85 69 -82 63 -93 60 -94 57 -85 55 -79 52 -78 60 -70 59 -64 60 -60 55 -56 52 -66 45\r\n     -70 43 -74 40 -76 35 -81 31 -80 26 -83 29 -88 30 -94 29 -97 26 -97 21 -91 19 -87 21 -88 16 -83 15 -83 10 -79 9 -85 10 -88 13 -94 16\r\n     -105 20 -108 25 -113 31 -117 32 -121 35 -124 40 -124 47 -130 54 -138 59 -148 60 -153 57 -162 55 -158 58 -165 62`),\r\n  P(`-73 78 -60 82 -30 83 -18 78 -20 70 -30 68 -42 60 -50 64 -55 70 -68 76`), // Greenland\r\n  P(`-78 8 -72 12 -62 11 -52 5 -50 0 -44 -2 -35 -5 -39 -14 -41 -22 -48 -26 -53 -34 -58 -38 -65 -41 -66 -47 -69 -52 -72 -54 -75 -50\r\n     -73 -40 -71 -30 -70 -18 -76 -14 -81 -5 -80 0 -77 4`),\r\n  P(`-9 43 -2 43.5 -1 46 -4 48 2 51 5 53 8 55 9 57 11 56 12 54 18 55 22 55 24 57 24 60 30 60 33 66 40 67 44 68 54 69 60 69 70 73 80 73\r\n     100 77 112 74 130 71 150 71 170 70 180 68 180 65 175 63 165 60 163 58 160 54 156 51 156 57 150 59 142 59 137 54 141 48 133 43\r\n     129 41 129 37 129 35 126 34 126 37 124 39 122 40 118 39 121 37 119 35 122 31 121 28 117 23 110 21 108 21 106 18 109 12 105 9\r\n     103 10 100 13 99 8 104 1 101 3 98 8 98 16 94 18 92 22 88 22 85 20 80 15 78 8 76 10 73 17 72 21 68 24 66 25 58 25 56 27 50 30\r\n     48 30 50 26 52 24 56 24 58 20 52 16 44 12 43 16 39 22 35 28 34 30 36 36 28 37 26 40 23 40 22 37 20 40 19 42 14 45 13 45 12 44\r\n     16 41 16 38 12 42 8 44 3 43 0 39 -2 37 -6 36 -9 37`),\r\n  P(`-17 15 -16 22 -10 30 -6 35 10 37 11 33 20 31 32 31 35 28 43 12 51 12 48 5 40 -3 40 -15 35 -24 32 -29 26 -34 18 -34 14 -22 12 -6 9 4 5 6 -8 4 -13 8`),\r\n  P(`114 -22 122 -18 130 -12 137 -12 141 -11 146 -19 153 -26 150 -37 140 -38 131 -31 115 -34`), // Australia\r\n  P(`5 58 5 62 14 68 25 71 30 70 28 66 24 65 22 61 18 60 16 56 12 56 10 59`), // Scandinavia\r\n  P(`-5 50 1 51 2 53 -2 57 -5 58 -6 55 -3 53`), P(`130 31 135 34 140 36 142 40 141 45 144 43 140 38 136 36 131 34`), // UK, Japan\r\n  P(`109 1 117 7 119 1 116 -4 110 -3`), P(`95 5 104 -2 106 -6 100 -1`), P(`44 -25 47 -25 50 -15 49 -12 44 -17`), // Borneo, Sumatra, Madagascar\r\n  P(`-24 64 -14 65 -14 64 -22 63`), P(`172 -35 178 -38 174 -42 168 -46 167 -44 172 -40`), // Iceland, NZ\r\n];\r\nconst DESERT = [[-14, 30, 17, 31], [38, 55, 17, 30], [-118, -104, 29, 38], [92, 112, 38, 46], [122, 142, -30, -20], [16, 26, -26, -18]];\r\nfunction inPoly(p, x, y) {\r\n  let c = false;\r\n  for (let i = 0, j = p.length - 2; i < p.length; j = i, i += 2)\r\n    if ((p[i + 1] > y) !== (p[j + 1] > y) && x < (p[j] - p[i]) * (y - p[i + 1]) / (p[j + 1] - p[i + 1]) + p[i]) c = !c;\r\n  return c;\r\n}\r\n/** True over a landmass (Antarctica below 68 S counts). */\r\nexport function landAt(lat, lng) {\r\n  if (lat < -68) return true;\r\n  lng = ((lng + 540) % 360) - 180;\r\n  for (const p of LAND) if (inPoly(p, lng, lat)) return true;\r\n  return false;\r\n}\r\nconst SNOW = [.92, .95, 1], SAND = [.82, .66, .38], SAND2 = [.7, .5, .3], TAIGA = [.2, .36, .28], TUNDRA = [.45, .5, .4],\r\n  JUNGLE = [.08, .42, .16], GRASS = [.3, .52, .2], FOREST = [.15, .38, .2], PEAK = [.78, .76, .75];\r\nfunction biome(lat, lng, n) {\r\n  const a = Math.abs(lat);\r\n  if (a > 68) return SNOW;\r\n  for (const b of DESERT) if (lng >= b[0] && lng <= b[1] && lat >= b[2] && lat <= b[3]) return mix(SAND, SAND2, n);\r\n  if (a > 55) return mix(TAIGA, TUNDRA, n);\r\n  return a < 14 ? mix(JUNGLE, GRASS, n) : mix(GRASS, FOREST, n);\r\n}\r\n\r\n/** Draw calls and triangles of whatever is currently visible under root (instances counted). */\r\nexport function sceneStats(root) {\r\n  let draws = 0, triangles = 0;\r\n  (function walk(o) {\r\n    if (!o.visible) return;\r\n    if (o.isMesh || o.isPoints) {\r\n      draws++;\r\n      if (o.isMesh) triangles += (o.geometry.index ? o.geometry.index.count : o.geometry.attributes.position.count) / 3 * (o.isInstancedMesh ? o.count : 1);\r\n    }\r\n    o.children.forEach(walk);\r\n  })(root);\r\n  return {draws, triangles};\r\n}\r\n\r\nfunction buildGlobe(T) {\r\n  const W = 72, H = 36, N = (W + 1) * (H + 1), V = new Float32Array(N * 3), landV = new Uint8Array(N), hgt = new Float32Array(N), d = {x: 0, y: 0, z: 0};\r\n  for (let j = 0; j <= H; j++) for (let i = 0; i <= W; i++) {\r\n    const k = j * (W + 1) + i, lat = 90 - 180 * j / H, lng = -180 + 360 * i / W, land = landAt(lat, lng) ? 1 : 0;\r\n    latLngToDir(lat, lng, d);\r\n    hgt[k] = land ? .5 + 3.2 * Math.max(0, noise3(d.x * 6, d.y * 6, d.z * 6) - .38) : 0; // noise in the natural frame: ranges stay on their land\r\n    landV[k] = land;\r\n    latLngToDir(lat, lng + GLOBE_YAW_DEG, d);\r\n    V[k * 3] = d.x * (R + hgt[k]); V[k * 3 + 1] = d.y * (R + hgt[k]); V[k * 3 + 2] = d.z * (R + hgt[k]);\r\n  }\r\n  const pos = new Float32Array(W * H * 18), col = new Float32Array(W * H * 18);\r\n  let o = 0;\r\n  const tri = (a, b, c, base) => {\r\n    const ax = V[a * 3], ay = V[a * 3 + 1], az = V[a * 3 + 2], ux = V[b * 3] - ax, uy = V[b * 3 + 1] - ay, uz = V[b * 3 + 2] - az,\r\n      vx = V[c * 3] - ax, vy = V[c * 3 + 1] - ay, vz = V[c * 3 + 2] - az;\r\n    let nx = uy * vz - uz * vy, ny = uz * vx - ux * vz, nz = ux * vy - uy * vx, l = Math.hypot(nx, ny, nz);\r\n    if (l < 1e-9) {nx = ax; ny = ay; nz = az; l = Math.hypot(nx, ny, nz);}\r\n    const s = shade(nx / l, ny / l, nz / l);\r\n    for (const q of [a, b, c]) {\r\n      pos[o] = V[q * 3]; pos[o + 1] = V[q * 3 + 1]; pos[o + 2] = V[q * 3 + 2];\r\n      col[o] = base[0] * s; col[o + 1] = base[1] * s; col[o + 2] = base[2] * s + (1 - s) * .05; o += 3;\r\n    }\r\n  };\r\n  for (let j = 0; j < H; j++) for (let i = 0; i < W; i++) {\r\n    const a = j * (W + 1) + i, b = a + 1, c = a + W + 1, e = c + 1, lat = 90 - 180 * (j + .5) / H, lng = -180 + 360 * (i + .5) / W;\r\n    const votes = landV[a] + landV[b] + landV[c] + landV[e] + (landAt(lat, lng) ? 1 : 0), n = h3(i, j, 3);\r\n    let base;\r\n    if (votes >= 3) base = (hgt[a] + hgt[b] + hgt[c] + hgt[e]) / 4 > 2.5 ? mix(PEAK, biome(lat, lng, n), .25) : biome(lat, lng, n);\r\n    else base = Math.abs(lat) > 72 ? SNOW : votes ? mix([.1, .45, .55], [.07, .32, .5], n) : mix([.03, .14, .36], [.05, .2, .42], n);\r\n    tri(a, c, b, base); tri(b, c, e, base);\r\n  }\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  g.computeBoundingSphere();\r\n  return g;\r\n}\r\nfunction buildClouds(T, rnd, clusters, per) {\r\n  const n = clusters * per, pos = new Float32Array(n * 54), col = new Float32Array(n * 54);\r\n  let o = 0;\r\n  for (let c = 0; c < clusters; c++) {\r\n    const z = 2 * rnd() - 1, phi = TAU * rnd(), s = Math.sqrt(1 - z * z), cx = s * Math.cos(phi), cy = z, cz = s * Math.sin(phi);\r\n    for (let i = 0; i < per; i++) {\r\n      let dx = cx + (rnd() - .5) * .2, dy = cy + (rnd() - .5) * .1, dz = cz + (rnd() - .5) * .2;\r\n      const l = Math.hypot(dx, dy, dz), el = Math.hypot(dx, dz) || 1;\r\n      dx /= l; dy /= l; dz /= l;\r\n      const tx = dz / el, tz = -dx / el, ux = dy * tz, uy = dz * tx - dx * tz, uz = -dy * tx, size = 2.2 + 3.4 * rnd(), sh = shade(dx, dy, dz) * .9 + .1; // t = east, u = dir x t\r\n      const ring = [], hub = [dx * (R + 2.5), dy * (R + 2.5), dz * (R + 2.5)];\r\n      for (let k = 0; k < 6; k++) {\r\n        const a = k * TAU / 6, rx = size * 1.7 * (.7 + .5 * rnd()), ry = size * .7 * (.7 + .5 * rnd());\r\n        ring.push([hub[0] + tx * Math.cos(a) * rx + ux * Math.sin(a) * ry, hub[1] + uy * Math.sin(a) * ry, hub[2] + tz * Math.cos(a) * rx + uz * Math.sin(a) * ry]);\r\n      }\r\n      for (let k = 0; k < 6; k++) for (const v of [hub, ring[k], ring[(k + 1) % 6]]) {\r\n        pos[o] = v[0]; pos[o + 1] = v[1]; pos[o + 2] = v[2]; col[o] = sh; col[o + 1] = sh; col[o + 2] = sh * .98 + .02; o += 3;\r\n      }\r\n    }\r\n  }\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  return g;\r\n}\r\n/** Non-indexed copy with one flat colour per face: colour(centreY, |normalY|) -> [r,g,b], then sun-shaded. */\r\nfunction facets(T, geo, colour) {\r\n  const g = geo.index ? geo.toNonIndexed() : geo, p = g.attributes.position, col = new Float32Array(p.count * 3), a = new T.Vector3(), b = new T.Vector3(), c = new T.Vector3();\r\n  for (let k = 0; k < p.count; k += 3) {\r\n    a.fromBufferAttribute(p, k); b.fromBufferAttribute(p, k + 1); c.fromBufferAttribute(p, k + 2);\r\n    const n = b.clone().sub(a).cross(c.clone().sub(a)).normalize(), y = (a.y + b.y + c.y) / 3, s = .55 + .45 * shade(n.x, n.y, n.z), rgb = colour(y, Math.abs(n.y));\r\n    for (let q = 0; q < 3; q++) col.set([rgb[0] * s, rgb[1] * s, rgb[2] * s], (k + q) * 3);\r\n  }\r\n  g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  if (g !== geo) geo.dispose();\r\n  return g;\r\n}\r\n/** Merge parts [[BufferGeometry,[r,g,b]]...] into one non-indexed vertex-coloured geometry. */\r\nfunction mergeColored(T, parts) {\r\n  const ps = parts.map(([g]) => g.index ? g.toNonIndexed() : g), n = ps.reduce((s, g) => s + g.attributes.position.count, 0);\r\n  const pos = new Float32Array(n * 3), col = new Float32Array(n * 3);\r\n  let o = 0;\r\n  ps.forEach((g, i) => {\r\n    const a = g.attributes.position, c = parts[i][1];\r\n    for (let k = 0; k < a.count; k++, o += 3) {\r\n      pos[o] = a.getX(k); pos[o + 1] = a.getY(k); pos[o + 2] = a.getZ(k);\r\n      col[o] = c[0] * (.75 + .25 * a.getY(k)); col[o + 1] = c[1] * (.75 + .25 * a.getY(k)); col[o + 2] = c[2] * (.75 + .25 * a.getY(k));\r\n    }\r\n    g.dispose(); parts[i][0].dispose();\r\n  });\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  return g;\r\n}\r\nconst ramp = (pal, t) => {const f = fract(t) * pal.length, i = Math.floor(f);\r\n  return mix(pal[i], pal[(i + 1) % pal.length], f - i);};\r\n/** Faceted planet (+ optional ring) in its own coarse bands, sun baked in. spec: r, pal, bands, rough, caps, ring:[in,out,tilt]. */\r\nfunction buildPlanet(T, spec) {\r\n  const g = new T.IcosahedronGeometry(spec.r, 3), p = g.attributes.position, col = new Float32Array(p.count * 3), v = [0, 0, 0];\r\n  for (let k = 0; k < p.count; k++) {\r\n    const x = p.getX(k) / spec.r, y = p.getY(k) / spec.r, z = p.getZ(k) / spec.r, bump = 1 + spec.rough * (noise3(x * 4 + 9, y * 4, z * 4) - .5);\r\n    p.setXYZ(k, x * spec.r * bump, y * spec.r * bump, z * spec.r * bump);\r\n  }\r\n  for (let k = 0; k < p.count; k += 3) { // flat shaded per face\r\n    v[0] = v[1] = v[2] = 0;\r\n    for (let q = 0; q < 3; q++) {v[0] += p.getX(k + q); v[1] += p.getY(k + q); v[2] += p.getZ(k + q);}\r\n    const l = Math.hypot(v[0], v[1], v[2]) || 1, nx = v[0] / l, ny = v[1] / l, nz = v[2] / l;\r\n    let c = ramp(spec.pal, ny * spec.bands * .25 + .5 + .08 * noise3(nx * 3, ny * 9, nz * 3));\r\n    if (spec.caps && Math.abs(ny) > .84) c = mix(c, SNOW, .85);\r\n    const s = shade(nx, ny, nz);\r\n    for (let q = 0; q < 3; q++) {col[(k + q) * 3] = c[0] * s; col[(k + q) * 3 + 1] = c[1] * s; col[(k + q) * 3 + 2] = c[2] * s;}\r\n  }\r\n  g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  if (!spec.ring) return {body: g};\r\n  const [ri, ro, tilt] = spec.ring, rg = new T.RingGeometry(ri, ro, 48, 1), rp = rg.attributes.position, rc = new Float32Array(rp.count * 3);\r\n  for (let k = 0; k < rp.count; k++) {\r\n    const t = (Math.hypot(rp.getX(k), rp.getY(k)) - ri) / (ro - ri), b = (.55 + .45 * Math.sin(t * 38)) * (t > .52 && t < .58 ? .15 : 1);\r\n    rc[k * 3] = .85 * b; rc[k * 3 + 1] = .74 * b; rc[k * 3 + 2] = .58 * b;\r\n  }\r\n  rg.setAttribute('color', new T.BufferAttribute(rc, 3)); rg.rotateX(-Math.PI / 2); rg.rotateZ(tilt);\r\n  return {body: g, ring: rg};\r\n}\r\nconst PLANETS = [\r\n  {r: 46, pos: [-420, 90, -380], pal: [[.9, .62, .3], [.96, .86, .62], [.78, .45, .22], [.92, .74, .5]], bands: 9, rough: .03, ring: [64, 108, .4]},\r\n  {r: 28, pos: [380, 150, -300], pal: [[.55, .78, .95], [.8, .93, 1], [.4, .62, .9]], bands: 4, rough: .08, caps: true},\r\n  {r: 22, pos: [250, -40, -520], pal: [[.75, .3, .18], [.55, .2, .14], [.85, .5, .3]], bands: 3, rough: .22},\r\n  {r: 20, pos: [-300, -140, 260], pal: [[.55, .3, .75], [.35, .75, .5], [.7, .45, .85]], bands: 6, rough: .14},\r\n  {r: 9, pos: [150, 60, -120], pal: [[.62, .62, .66], [.45, .45, .5]], bands: 2, rough: .3}, // moon\r\n];\r\n// V4 space fill (Daggerfall look): the empty +z side (behind the start view) and overhead. Planets and the hull below are\r\n// local gpt-oss-20b cards (validated: budget, bounds, colours, start->port clearance), reviewed; the squadron slots are ours.\r\n// Everything here is ONE merged vertex-coloured draw ('far-fill'), static, kept FAR_CLEAR from the start->port flight line.\r\nexport const FAR_PLANETS = [\r\n  {r: 32, pos: [-120,150,460], pal: [[0.2,0.8,0.9],[0.4,0.7,1],[0.1,0.5,0.6]], bands: 5, rough: 0.15}, // Aetheric Veil,\r\n  {r: 26, pos: [80,500,150], pal: [[0.9,0.2,0.3],[0.8,0.1,0.4],[0.7,0.05,0.5]], bands: 6, rough: 0.12}, // Crimson Nebula\r\n];\r\n// three V squadrons of 6: [centre, heading yaw deg, ship scale]\r\nexport const SQUADRONS = [[[-260, 240, 420], 160, 2.6], [[230, 330, 380], 210, 2.2], [[60, 470, 120], 95, 1.8]];\r\nexport const FAR_CLEAR = 70, START_PORT = Object.freeze([[0, 110, 230], [40, 130, 110]]);\r\nfunction buildFarHull(T) {\r\n  const P = [], C = [];\r\n  const tri = (a, b, c, col) => { P.push(...a, ...b, ...c); C.push(...col, ...col, ...col); };\r\n  const quad = (a, b, c, d, col) => { tri(a, b, c, col); tri(a, c, d, col); };\r\n\r\n  // prism: rear rectangle at x0 (y0..y1, z -w0..w0) to front rectangle at x1 (y2..y3, z -w1..w1);\r\n  // 6 faces, colours per face\r\n  const prism = (\r\n    x0, y0, y1, w0,\r\n    x1, y2, y3, w1,\r\n    top, side, bottom, back, front\r\n  ) => {\r\n    const a = [x0, y0, -w0], b = [x0, y1, -w0], c = [x0, y1, w0], d = [x0, y0, w0];\r\n    const e = [x1, y2, -w1], f = [x1, y3, -w1], g = [x1, y3, w1], h = [x1, y2, w1];\r\n\r\n    quad(b, c, g, f, top);          // front\r\n    quad(a, e, h, d, bottom);       // back\r\n    quad(a, b, f, e, side);         // left\r\n    quad(d, h, g, c, side);         // right\r\n    quad(a, d, c, b, back);         // top\r\n    quad(e, f, g, h, front);        // bottom\r\n  };\r\n\r\n  // Colors (RGB 0..1)\r\n  const hullTop = [0.33, 0.38, 0.58];\r\n  const hullSide = [0.21, 0.24, 0.41];\r\n  const hullBottom = [0.13, 0.15, 0.27];\r\n  const stripeCol = [1, 0.7, 0.16];\r\n  const windowCol = [0.55, 0.94, 1];\r\n  const engineGlow = [1, 0.48, 0.23];\r\n\r\n  // 1. Main hull\r\n  prism(\r\n    -4, -0.8, 0.8, 1.2,\r\n     5, -0.15, 0.15, 0.2,\r\n    hullTop, hullSide, hullBottom, hullSide, hullSide\r\n  );\r\n\r\n  // 2. Orange stripe (slightly wider than hull)\r\n  prism(\r\n    -3.5, -0.15, 0.15, 1.4,\r\n     3, -0.15, 0.15, 0.5,\r\n    stripeCol, stripeCol, stripeCol, stripeCol, stripeCol\r\n  );\r\n\r\n  // 3. Bridge tower (front face is window)\r\n  prism(\r\n    -3.5, 0.6, 1.5, 0.5,\r\n    -1.5, 0.6, 1.5, 0.5,\r\n    hullTop, hullSide, hullBottom, hullSide, windowCol\r\n  );\r\n\r\n  // 4. Engine block (back face is engine glow)\r\n  prism(\r\n    -5, -0.6, 0.6, 0.9,\r\n    -4, -0.6, 0.6, 0.9,\r\n    hullTop, hullSide, hullBottom, engineGlow, hullSide\r\n  );\r\n\r\n  const geo = new T.BufferGeometry();\r\n  geo.setAttribute('position', new T.BufferAttribute(new Float32Array(P), 3));\r\n  geo.setAttribute('color', new T.BufferAttribute(new Float32Array(C), 3));\r\n  return geo;\r\n}\r\nconst SQUAD = [[0, 0, 0], [-14, -2, -11], [-14, -2, 11], [-28, -4, -22], [-28, -4, 22], [-42, 3, 0]]; // V formation, nose +x\r\nfunction buildFarFill(T) {\r\n  const parts = FAR_PLANETS.map(spec => buildPlanet(T, spec).body.translate(...spec.pos)), hull = buildFarHull(T), m = new T.Matrix4(), q = new T.Quaternion(), e = new T.Euler(), v = new T.Vector3(), sc = new T.Vector3();\r\n  for (const [c, yaw, k] of SQUADRONS) for (const [ox, oy, oz] of SQUAD) {\r\n    e.set(0, yaw * RAD, 0); q.setFromEuler(e); v.set(ox * k / 2.2, oy * k / 2.2, oz * k / 2.2).applyQuaternion(q).add(new T.Vector3(...c));\r\n    parts.push(hull.clone().applyMatrix4(m.compose(v, q, sc.setScalar(k))));\r\n  }\r\n  hull.dispose();\r\n  const n = parts.reduce((s, g) => s + g.attributes.position.count, 0), pos = new Float32Array(n * 3), col = new Float32Array(n * 3);\r\n  let o = 0;\r\n  for (const g of parts) {pos.set(g.attributes.position.array, o); col.set(g.attributes.color.array, o); o += g.attributes.position.count * 3; g.dispose();}\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n  return g;\r\n}\r\nconst SKY_PLANET = {r: 330, pos: [-1300, 650, -1700], pal: [[.55, .3, .7], [.9, .55, .75], [.35, .25, .6]], bands: 8, rough: .03, ring: [470, 760, .5]};\r\n\r\nconst FAR = Object.freeze({phase: 'far'});\r\nexport function createSpaceScene(T, {seed = 5, mode = 'space', color = 0x85d5c3, station: stationSpec = STATION} = {}) {\r\n  const rnd = mulberry32(seed | 0), own = [], inst = [], tint = new T.Color(color);\r\n  const keep = o => (own.push(o), o);\r\n  const put = (parent, Cls, geo, mat, name) => {keep(geo); keep(mat); const o = new Cls(geo, mat); o.name = name; parent.add(o); return o;};\r\n  const basic = (o = {}) => new T.MeshBasicMaterial({vertexColors: true, fog: false, ...o});\r\n  const add = (parent, geo, mat, name) => put(parent, T.Mesh, geo, mat, name);\r\n  const group = new T.Group(), space = new T.Group(), world = new T.Group();\r\n  group.name = 'myr5-space-scene'; space.name = 'space'; world.name = 'world'; group.add(space, world);\r\n  let dead = false;\r\n  const vdir = latLngToDir(VEGAS.lat, VEGAS.lng + GLOBE_YAW_DEG);\r\n\r\n  // ---- space group\r\n  add(space, buildGlobe(T), basic(), 'earth');\r\n  const clouds = add(space, buildClouds(T, rnd, 34, 5), basic({transparent: true, opacity: .55, depthWrite: false, side: T.DoubleSide}), 'clouds');\r\n  add(space, new T.SphereGeometry(R + 4.5, 28, 14), new T.MeshBasicMaterial({color: tint, fog: false, transparent: true, opacity: .45, side: T.BackSide, blending: T.AdditiveBlending, depthWrite: false}), 'atmosphere-halo');\r\n  add(space, new T.SphereGeometry(R + 1.6, 24, 14), new T.MeshBasicMaterial({color: tint, fog: false, transparent: true, opacity: .1, depthWrite: false}), 'atmosphere-haze');\r\n  const sp = new Float32Array(900 * 3), sc = new Float32Array(900 * 3);\r\n  for (let i = 0; i < 900; i++) {\r\n    const z = 2 * rnd() - 1, phi = TAU * rnd(), s = Math.sqrt(1 - z * z), b = .45 + .55 * rnd() ** 2, w = rnd();\r\n    sp[i * 3] = 650 * s * Math.cos(phi); sp[i * 3 + 1] = 650 * z; sp[i * 3 + 2] = 650 * s * Math.sin(phi);\r\n    sc[i * 3] = b * (.8 + .2 * w); sc[i * 3 + 1] = b * .9; sc[i * 3 + 2] = b * (1 - .25 * w);\r\n  }\r\n  const sg = new T.BufferGeometry();\r\n  sg.setAttribute('position', new T.BufferAttribute(sp, 3)); sg.setAttribute('color', new T.BufferAttribute(sc, 3));\r\n  const stars = put(space, T.Points, sg, new T.PointsMaterial({size: 2, sizeAttenuation: false, vertexColors: true, fog: false, depthWrite: false}), 'stars');\r\n  stars.frustumCulled = false; stars.renderOrder = -1;\r\n  const planetMat = basic(), ringMat = basic({transparent: true, opacity: .75, side: T.DoubleSide, depthWrite: false});\r\n  const bodyOf = (parent, spec, name) => {\r\n    const {body, ring} = buildPlanet(T, spec), m = add(parent, body, planetMat, name);\r\n    m.position.set(...spec.pos);\r\n    if (ring) add(parent, ring, ringMat, name + '-ring').position.set(...spec.pos);\r\n  };\r\n  PLANETS.forEach((s, i) => bodyOf(space, s, 'planet-' + i));\r\n  add(space, buildFarFill(T), planetMat, 'far-fill'); // V4: one static draw\r\n  // Vegas beacon (oriented along vdir, fixed to the globe) + the MOM station.\r\n  const beacon = new T.Group(), up = new T.Vector3(0, 1, 0), vv = new T.Vector3(vdir.x, vdir.y, vdir.z);\r\n  beacon.name = 'vegas-beacon'; beacon.position.copy(vv).multiplyScalar(R + .4); beacon.quaternion.setFromUnitVectors(up, vv); space.add(beacon);\r\n  const beamMat = new T.MeshBasicMaterial({color: tint, fog: false, transparent: true, opacity: .6, blending: T.AdditiveBlending, depthWrite: false, side: T.DoubleSide});\r\n  add(beacon, new T.CylinderGeometry(.2, .9, 26, 6, 1, true).translate(0, 13, 0), beamMat, 'beacon-beam');\r\n  const gem = add(beacon, new T.OctahedronGeometry(1.8), new T.MeshBasicMaterial({color: 0xffffff, fog: false}), 'beacon-gem'); gem.position.y = 28;\r\n  const pulse = add(beacon, new T.RingGeometry(2.2, 3.1, 24).rotateX(-Math.PI / 2), new T.MeshBasicMaterial({color: tint, fog: false, transparent: true, opacity: .8, blending: T.AdditiveBlending, depthWrite: false, side: T.DoubleSide}), 'beacon-ring');\r\n  pulse.position.y = .3;\r\n  const station = createStationMesh(T, {station: stationSpec, color});\r\n  station.group.position.set(stationSpec.pos.x, stationSpec.pos.y, stationSpec.pos.z); space.add(station.group);\r\n\r\n  // ---- world group\r\n  const sky = new T.SphereGeometry(DOME, 24, 12), sa = sky.attributes.position, skyCol = new Float32Array(sa.count * 3);\r\n  for (let k = 0; k < sa.count; k++) {\r\n    const y = sa.getY(k) / DOME, c = y < 0 ? [.55, .7, .78] : mix(mix([.7, .83, .88], [.3, .55, .82], clamp01(y * 3)), [.1, .12, .4], clamp01((y - .25) * 1.6));\r\n    skyCol.set(mix(c, [tint.r, tint.g, tint.b], .08), k * 3);\r\n  }\r\n  sky.setAttribute('color', new T.BufferAttribute(skyCol, 3));\r\n  const dome = add(world, sky, basic({side: T.BackSide, depthWrite: false}), 'sky'); dome.renderOrder = -2; dome.frustumCulled = false;\r\n  const skyPlanet = new T.Group(); skyPlanet.name = 'sky-planet'; skyPlanet.userData.centre = SKY_PLANET.pos; world.add(skyPlanet);\r\n  bodyOf(skyPlanet, {...SKY_PLANET, pos: [0, 0, 0]}, 'sky-planet'); skyPlanet.position.set(...SKY_PLANET.pos);\r\n  const K = 14, S = 48, gp = new Float32Array((K * S + 1) * 3), gc = new Float32Array((K * S + 1) * 3), gi = [];\r\n  const idx = (k, s) => k === 0 ? 0 : 1 + (k - 1) * S + (s % S);\r\n  for (let k = 0; k <= K; k++) for (let s = 0; s < (k ? S : 1); s++) {\r\n    const r = GR * (k / K) ** 1.8, a = s * TAU / S, q = idx(k, s), t = clamp01(r / GR) ** .8, c = mix(mix([.04, .26, .38], [.07, .34, .46], h3(k, s, 1)), [.6, .76, .82], t * t);\r\n    gp.set([r * Math.cos(a), -r * r / (2 * WR), r * Math.sin(a)], q * 3); gc.set(c, q * 3);\r\n    if (k) {gi.push(idx(k - 1, s), idx(k, s + 1), idx(k, s)); if (k > 1) gi.push(idx(k - 1, s), idx(k - 1, s + 1), idx(k, s + 1));}\r\n  }\r\n  const gg = new T.BufferGeometry();\r\n  gg.setAttribute('position', new T.BufferAttribute(gp, 3)); gg.setAttribute('color', new T.BufferAttribute(gc, 3)); gg.setIndex(gi);\r\n  const ground = add(world, gg, basic(), 'ground'); ground.frustumCulled = false;\r\n\r\n  const sets = []; // wrapped instanced feature sets: p = base xyz, s = scale xyz, wind = m/s along x (clouds)\r\n  const feature = (name, geo, mat, n, fill, tintFn) => {\r\n    const mesh = new T.InstancedMesh(keep(geo), keep(mat), n), set = {mesh, n, p: new Float32Array(n * 3), s: new Float32Array(n * 3), wind: null};\r\n    mesh.name = name; mesh.frustumCulled = false; fill(set);\r\n    for (let i = 0; i < n; i++) mesh.setColorAt(i, tintFn(i));\r\n    mesh.instanceColor.needsUpdate = true; world.add(mesh); inst.push(mesh); sets.push(set); return set;\r\n  };\r\n  const span = () => (rnd() - .5) * (WORLD_WRAP - 400), c3 = (a, b, c) => new T.Color(a, b, c);\r\n  const isles = Array.from({length: 18}, () => ({x: span(), z: span()}));\r\n  const patchPal = [[.2, .55, .25], [.32, .62, .3], [.55, .35, .6], [.15, .5, .5], [.7, .6, .35]], patchTint = [];\r\n  feature('land-patches', new T.CircleGeometry(1, 6).rotateX(-Math.PI / 2), new T.MeshBasicMaterial({fog: false, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2}), isles.length * 8, set => {\r\n    isles.forEach((m, i) => {\r\n      for (let k = 0; k < 8; k++) {\r\n        const j = i * 8 + k, a = rnd() * TAU, d = k ? 20 + rnd() * 110 : 0, r = k ? 40 + rnd() * 70 : 170 + rnd() * 90;\r\n        set.p.set([m.x + Math.cos(a) * d, .4 + k * .01, m.z + Math.sin(a) * d], j * 3); set.s.set([r, 1, r], j * 3);\r\n        patchTint[j] = k ? patchPal[Math.floor(rnd() * 5)] : [.82, .74, .5];\r\n      }\r\n    });\r\n  }, i => c3(...patchTint[i]));\r\n  const rangeTint = [[.62, .56, .5], [.55, .5, .62], [.7, .62, .55]];\r\n  feature('mountains', facets(T, new T.ConeGeometry(1, 1, 5, 1).translate(0, .5, 0), (y, ny) => y > .6 ? SNOW : mix([.62, .55, .5], [.82, .72, .6], ny)), new T.MeshBasicMaterial({vertexColors: true, fog: false}), 96, set => {\r\n    for (let r = 0; r < 8; r++) {\r\n      const cx = span(), cz = span(), a = rnd() * TAU;\r\n      for (let k = 0; k < 12; k++) {\r\n        const j = r * 12 + k, along = (k - 6) * 140 + (rnd() - .5) * 80, side = (rnd() - .5) * 200, w = 70 + rnd() * 100;\r\n        set.p.set([cx + Math.cos(a) * along - Math.sin(a) * side, 0, cz + Math.sin(a) * along + Math.cos(a) * side], j * 3); set.s.set([w, 120 + rnd() * 220, w], j * 3);\r\n      }\r\n    }\r\n  }, i => c3(...rangeTint[i % 3]));\r\n  const clearOf = (set, r) => { // keep the WORLD_START spawn clear: nothing may grow out of the camera\r\n    for (let i = 0; i < set.n; i++) {\r\n      const x = set.p[i * 3], z = set.p[i * 3 + 2], d = Math.hypot(x, z);\r\n      if (d < r) {set.p[i * 3] = d ? x * r / d : r; set.p[i * 3 + 2] = d ? z * r / d : 0;}\r\n    }\r\n  };\r\n  clearOf(sets[1], 650);\r\n  const treeTint = [[.2, .8, .7], [.9, .3, .7], [.6, .9, .3], [.55, .4, .95]];\r\n  const tree = mergeColored(T, [[new T.CylinderGeometry(.12, .2, 1, 4, 1, true).translate(0, .5, 0), [.4, .3, .35]], [new T.ConeGeometry(.6, .8, 5).translate(0, 1.2, 0), [1, 1, 1]]]);\r\n  feature('alien-forest', tree, new T.MeshBasicMaterial({vertexColors: true, fog: false}), 24 * 13, set => {\r\n    for (let g = 0; g < 24; g++) {\r\n      const m = isles[g % isles.length], gx = m.x + (rnd() - .5) * 500, gz = m.z + (rnd() - .5) * 500;\r\n      for (let k = 0; k < 13; k++) {\r\n        const j = g * 13 + k, h = 12 + rnd() * 16, a = rnd() * TAU, d = rnd() * 130;\r\n        set.p.set([gx + Math.cos(a) * d, .5, gz + Math.sin(a) * d], j * 3); set.s.set([h * .7, h, h * .7], j * 3);\r\n      }\r\n    }\r\n  }, i => c3(...treeTint[Math.floor(i / 13) % 4]));\r\n  clearOf(sets[2], 40);\r\n  feature('world-clouds', facets(T, new T.IcosahedronGeometry(1, 0), (y, ny) => mix([.66, .74, .88], [1, 1, 1], ny)), new T.MeshBasicMaterial({vertexColors: true, fog: false, transparent: true, opacity: .88, depthWrite: false}), 44, set => {\r\n    set.wind = new Float32Array(44);\r\n    for (let i = 0; i < 44; i++) {\r\n      set.p.set([span(), 140 + rnd() * 280, span()], i * 3); set.s.set([90 + rnd() * 130, 14 + rnd() * 12, 60 + rnd() * 70], i * 3); set.wind[i] = 1.5 + rnd() * 2.5;\r\n    }\r\n  }, () => c3(1, 1, 1));\r\n\r\n  // ---- per-frame state (all scratch preallocated)\r\n  const v = new T.Vector3(), q = new T.Quaternion(), ax = new T.Vector3(), sc3 = new T.Vector3(), m4 = new T.Matrix4();\r\n  let t = 0, cur = 'space', last = {x: NaN, z: NaN};\r\n  const wrap = d => d - WORLD_WRAP * Math.round(d / WORLD_WRAP);\r\n  function layout(set, cx, cz) {\r\n    for (let i = 0; i < set.n; i++) {\r\n      const dx = wrap(set.p[i * 3] - cx), dz = wrap(set.p[i * 3 + 2] - cz), d = Math.hypot(dx, dz);\r\n      v.set(cx + dx, set.p[i * 3 + 1] - d * d / (2 * WR), cz + dz);\r\n      q.identity(); if (d > 1e-3) q.setFromAxisAngle(ax.set(dz / d, 0, -dx / d), Math.atan(d / WR)); // stand upright on the curved ground\r\n      const edge = clamp01((WORLD_WRAP / 2 - Math.max(Math.abs(dx), Math.abs(dz))) / 320), k = edge * edge * (3 - 2 * edge); // grow in/out at the wrap seam: no popping\r\n      m4.compose(v, q, sc3.set(set.s[i * 3] * k, set.s[i * 3 + 1] * k, set.s[i * 3 + 2] * k));\r\n      set.mesh.setMatrixAt(i, m4);\r\n    }\r\n    set.mesh.instanceMatrix.needsUpdate = true;\r\n  }\r\n  function setMode(next) {\r\n    if (next !== 'space' && next !== 'world') throw new RangeError(`Unknown scene mode \"${next}\".`);\r\n    cur = next; space.visible = next === 'space'; world.visible = next === 'world'; last.x = NaN;\r\n  }\r\n  setMode(mode);\r\n  return {\r\n    group,\r\n    get mode() {return cur;},\r\n    setMode,\r\n    update(dt, pose, range) {\r\n      if (dead) return;\r\n      const s = Math.min(Math.max(Number.isFinite(dt) ? dt : 0, 0), .1), px = Number.isFinite(pose?.x) ? pose.x : 0, py = Number.isFinite(pose?.y) ? pose.y : 0, pz = Number.isFinite(pose?.z) ? pose.z : 0;\r\n      t += s;\r\n      if (cur === 'space') {\r\n        stars.position.set(px, py, pz); clouds.rotation.y += s * .006;\r\n        const k = .5 + .5 * Math.sin(t * 2.4);\r\n        gem.scale.setScalar(.8 + .5 * k); gem.rotation.y += s * 1.5; pulse.scale.setScalar(1 + k * .9); beamMat.opacity = .35 + .35 * k; pulse.material.opacity = .9 - .6 * k;\r\n        station.update(s, range ?? FAR);\r\n        return;\r\n      }\r\n      dome.position.set(px, py, pz); skyPlanet.position.set(px + SKY_PLANET.pos[0], py + SKY_PLANET.pos[1], pz + SKY_PLANET.pos[2]);\r\n      ground.position.set(px, 0, pz);\r\n      const cl = sets[3];\r\n      for (let i = 0; i < cl.n; i++) cl.p[i * 3] += cl.wind[i] * s;\r\n      const moved = px !== last.x || pz !== last.z;\r\n      last.x = px; last.z = pz;\r\n      for (let i = 0; i < sets.length; i++) if (moved || i === 3) layout(sets[i], px, pz);\r\n    },\r\n    vegasDirection(out = {x: 0, y: 0, z: 0}) {out.x = vdir.x; out.y = vdir.y; out.z = vdir.z; return out;},\r\n    dispose() {\r\n      if (dead) return;\r\n      dead = true; group.removeFromParent(); station.dispose();\r\n      inst.forEach(o => o.dispose()); new Set(own).forEach(o => o.dispose()); own.length = inst.length = sets.length = 0;\r\n    },\r\n  };\r\n}\r\n","modules/coach-flight/station-hud.mjs":"// DOM marker for the MOM station, drawn over the cockpit stage (survey.mjs pattern: pointer-events:none, never touches the canvas).\r\n// createStationHud(T,{host,station}) -> {update(camera,range,phase),hide(),dispose()}; phase = 'far'|'approach'|'ready'|'docking'|'docked'.\r\n// The stage is clipped by cockpit.mjs to a faction polygon (stage.style.clipPath), so the marker is placed against THAT polygon, not the stage\r\n// rectangle: the whole text block (label + phase + arrow, plus a margin for the cockpit frame stroke) must lie inside the polygon, otherwise it\r\n// slides to the nearest spot where it does. A label that cannot fit on one line wraps (MOM STATION / · 123u); a tiny aperture falls back to the arrow.\r\n// projectedBox / projectedHeightPx are the test probe: a bbox of the box's 8 corners in buffer pixels. It is a bound, not raster coverage:\r\n// corners at or behind the near plane make it unbounded (heightPx = Infinity, behind > 0) instead of a fake finite number.\r\nconst PHASE_TEXT = {approach: 'APPROACH', ready: 'DOCK READY', docking: 'DOCKING', docked: 'DOCKED'};\r\nconst STEP = 6, MARGIN = 8, MODES = ['wide', 'narrow', 'glyph']; // grid step (px); frame stroke margin (px); label layouts, widest first\r\n\r\nexport function projectedBox(camera, box, bufferHeight) {\r\n  camera.updateMatrixWorld();\r\n  const view = camera.matrixWorldInverse, out = {heightPx: 0, widthPx: 0, behind: 0, offscreen: false};\r\n  let x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity, seen = 0;\r\n  const v = new camera.position.constructor();\r\n  for (let i = 0; i < 8; i++) {\r\n    v.set(i & 1 ? box.max.x : box.min.x, i & 2 ? box.max.y : box.min.y, i & 4 ? box.max.z : box.min.z);\r\n    const depth = -v.clone().applyMatrix4(view).z;\r\n    if (depth <= camera.near) {out.behind++; continue;}\r\n    v.project(camera); seen++;\r\n    x0 = Math.min(x0, v.x); x1 = Math.max(x1, v.x); y0 = Math.min(y0, v.y); y1 = Math.max(y1, v.y);\r\n  }\r\n  if (out.behind) {out.heightPx = Infinity; out.widthPx = Infinity; return out;}\r\n  out.heightPx = (y1 - y0) / 2 * bufferHeight; out.widthPx = (x1 - x0) / 2 * bufferHeight * camera.aspect;\r\n  out.offscreen = seen > 0 && (x1 < -1 || x0 > 1 || y1 < -1 || y0 > 1);\r\n  return out;\r\n}\r\nexport const projectedHeightPx = (camera, box, bufferHeight) => projectedBox(camera, box, bufferHeight).heightPx;\r\n\r\n/** The stage's clip polygon in layout px, flat [x0,y0,x1,y1,...]; no clip-path means the whole rectangle. */\r\nexport function clipPolygon(clipPath, w, h) {\r\n  const pts = [...String(clipPath || '').matchAll(/(-?[\\d.]+)(%|px)?\\s+(-?[\\d.]+)(%|px)?/g)];\r\n  if (!pts.length) return [0, 0, w, 0, w, h, 0, h];\r\n  return pts.flatMap(m => [m[2] === 'px' ? +m[1] : +m[1] * w / 100, m[4] === 'px' ? +m[3] : +m[3] * h / 100]);\r\n}\r\nexport function insidePolygon(poly, x, y) { // even-odd ray cast, works for the concave star frames too\r\n  let hit = false;\r\n  for (let i = 0, j = poly.length - 2; i < poly.length; j = i, i += 2) {\r\n    const xi = poly[i], yi = poly[i + 1], xj = poly[j], yj = poly[j + 1];\r\n    if ((yi > y) !== (yj > y) && x < (xj - xi) * (y - yi) / (yj - yi) + xi) hit = !hit;\r\n  }\r\n  return hit;\r\n}\r\n/** True when the whole rectangle lies inside the polygon (perimeter samples + no polygon vertex poking into it). */\r\nexport function rectInPolygon(poly, x0, y0, x1, y1, step = STEP) {\r\n  if (!insidePolygon(poly, x0, y0) || !insidePolygon(poly, x1, y0) || !insidePolygon(poly, x1, y1) || !insidePolygon(poly, x0, y1)) return false;\r\n  for (let x = x0 + step; x < x1; x += step) if (!insidePolygon(poly, x, y0) || !insidePolygon(poly, x, y1)) return false;\r\n  for (let y = y0 + step; y < y1; y += step) if (!insidePolygon(poly, x0, y) || !insidePolygon(poly, x1, y)) return false;\r\n  for (let i = 0; i < poly.length; i += 2) if (poly[i] > x0 && poly[i] < x1 && poly[i + 1] > y0 && poly[i + 1] < y1) return false;\r\n  return true;\r\n}\r\n\r\nexport function createStationHud(T, {host, station}) {\r\n  const doc = host.ownerDocument, overlay = doc.createElement('div'), marker = doc.createElement('div'), glyph = doc.createElement('span'), label = doc.createElement('span');\r\n  const nameEl = doc.createElement('span'), rangeEl = doc.createElement('span'), phaseText = doc.createElement('span');\r\n  overlay.dataset.stationHud = '';\r\n  overlay.style.cssText = 'position:absolute;inset:0;pointer-events:none;z-index:2;overflow:hidden';\r\n  // The marker is centred on its left/top; the arrow at its foot points at the station. Parts are inline-block so the label can wrap between them.\r\n  marker.style.cssText = 'position:absolute;display:none;transform:translate(-50%,-50%);width:max-content;text-align:center;font:12px monospace;color:#efffe3;text-shadow:1px 1px #091522';\r\n  label.style.cssText = 'display:block;padding:2px 6px;border:2px solid var(--hull,#73dbcb);background:#13222de8;box-sizing:content-box';\r\n  for (const el of [nameEl, rangeEl]) el.style.cssText = 'display:inline-block;white-space:nowrap';\r\n  phaseText.style.cssText = 'display:block;margin-top:2px;color:var(--hull,#73dbcb);font-weight:bold;white-space:nowrap';\r\n  glyph.style.cssText = 'display:block;width:18px;height:18px;margin:0 auto;font-size:18px;line-height:18px;color:var(--hull,#73dbcb);text-shadow:1px 1px #091522';\r\n  glyph.setAttribute('aria-hidden', 'true'); glyph.textContent = '▲';\r\n  label.append(nameEl, doc.createTextNode(' '), rangeEl);\r\n  marker.append(label, phaseText, glyph); overlay.append(marker); host.append(overlay);\r\n  const centre = new T.Vector3(station.pos.x, station.pos.y, station.pos.z), above = new T.Vector3(station.pos.x, station.pos.y + station.hullRadius * 2, station.pos.z); // over the mast tip\r\n  const eye = new T.Vector3(), forward = new T.Vector3(), view = new T.Vector3(), projected = new T.Vector3();\r\n  const sizes = MODES.map(() => ({w: 0, h: 0})), masks = new Map();\r\n  let disposed = false, sizeKey = '', mode = '', rangeText = '', extra = '', angle = NaN, shown = false;\r\n  nameEl.textContent = 'MOM STATION';\r\n\r\n  const applyMode = id => { // layout only; the caller measures\r\n    label.style.display = id === 'glyph' ? 'none' : 'block';\r\n    label.style.whiteSpace = id === 'narrow' ? 'normal' : 'nowrap';\r\n    label.style.width = id === 'narrow' ? 'min-content' : '';\r\n    phaseText.style.display = id !== 'glyph' && extra ? 'block' : 'none';\r\n  };\r\n  const measure = () => { // forced layout, but only when the words' length changes\r\n    for (let i = 0; i < MODES.length; i++) {applyMode(MODES[i]); sizes[i].w = marker.offsetWidth; sizes[i].h = marker.offsetHeight;}\r\n    mode = '';\r\n  };\r\n  const maskFor = (poly, w, h, i, clip) => { // grid of block CENTRES where the whole block (+ margin) lies inside the clip polygon\r\n    const key = `${w}x${h}|${clip}|${i}|${sizes[i].w}x${sizes[i].h}`;\r\n    let mask = masks.get(key);\r\n    if (!mask) {\r\n      const gw = Math.floor(w / STEP) + 1, gh = Math.floor(h / STEP) + 1, hx = sizes[i].w / 2 + MARGIN, hy = sizes[i].h / 2 + MARGIN;\r\n      mask = {gw, gh, ok: new Uint8Array(gw * gh)};\r\n      for (let gy = 0; gy < gh; gy++) for (let gx = 0; gx < gw; gx++) {\r\n        const cx = gx * STEP, cy = gy * STEP;\r\n        if (rectInPolygon(poly, cx - hx, cy - hy, cx + hx, cy + hy)) mask.ok[gy * gw + gx] = 1;\r\n      }\r\n      if (masks.size > 24) masks.clear();\r\n      masks.set(key, mask);\r\n    }\r\n    return mask;\r\n  };\r\n  // best centre for mode i: the wanted spot if the block fits there, else the nearest allowed grid cell not touching the station disc\r\n  const place = (poly, w, h, i, clip, wx, wy, ex, ey, er, out) => {\r\n    const hx = sizes[i].w / 2 + MARGIN, hy = sizes[i].h / 2 + MARGIN, clear = (cx, cy) => {\r\n      if (!(er > 0)) return true;\r\n      const dx = Math.max(Math.abs(cx - ex) - sizes[i].w / 2, 0), dy = Math.max(Math.abs(cy - ey) - sizes[i].h / 2, 0);\r\n      return dx * dx + dy * dy >= er * er;\r\n    };\r\n    if (wx >= 0 && wx <= w && wy >= 0 && wy <= h && clear(wx, wy) && rectInPolygon(poly, wx - hx, wy - hy, wx + hx, wy + hy)) {out.x = wx; out.y = wy; out.d = 0; return true;}\r\n    const mask = maskFor(poly, w, h, i, clip);\r\n    let best = Infinity;\r\n    for (let gy = 0; gy < mask.gh; gy++) for (let gx = 0; gx < mask.gw; gx++) {\r\n      if (!mask.ok[gy * mask.gw + gx]) continue;\r\n      const cx = gx * STEP, cy = gy * STEP, d = (cx - wx) ** 2 + (cy - wy) ** 2;\r\n      if (d < best && clear(cx, cy)) {best = d; out.x = cx; out.y = cy;}\r\n    }\r\n    out.d = Math.sqrt(best);\r\n    return best < Infinity;\r\n  };\r\n  const spot = {x: 0, y: 0, d: 0}, pick = {x: 0, y: 0, d: 0};\r\n\r\n  return {\r\n    hide() {marker.style.display = 'none'; shown = false;},\r\n    update(camera, range, phase) {\r\n      if (disposed || !camera || !range) return;\r\n      const w = host.offsetWidth, h = host.offsetHeight;\r\n      if (!w || !h) return;\r\n      camera.updateMatrixWorld(); camera.getWorldPosition(eye); camera.getWorldDirection(forward);\r\n      view.copy(centre).sub(eye); projected.copy(centre).project(camera);\r\n      // Full-screen flight keeps the exterior visible on both sides of the metal bars.\r\n      // Only the HUD's placement area is inset, so edge arrows cannot cover real controls.\r\n      const root = host.closest('[data-coach-flight]'), bounds = host.getBoundingClientRect();\r\n      const header = root?.querySelector('[data-flight-top]')?.getBoundingClientRect();\r\n      const controls = root?.querySelector('[data-flight-bottom]')?.getBoundingClientRect();\r\n      const top = header ? Math.min(h / 2, Math.max(76, header.bottom - bounds.top + 12)) : 0;\r\n      const bottom = controls ? Math.max(top + 1, controls.top - bounds.top - 8) : h;\r\n      const clip = root ? `polygon(8px ${top}px, ${w - 8}px ${top}px, ${w - 8}px ${bottom}px, 8px ${bottom}px)` : host.style.clipPath;\r\n      const depth = view.dot(forward), poly = clipPolygon(clip, w, h);\r\n      const sx = (projected.x * .5 + .5) * w, sy = (-projected.y * .5 + .5) * h;\r\n      const ahead = depth > 0 && projected.z >= -1 && projected.z <= 1, on = ahead && insidePolygon(poly, sx, sy);\r\n      if (!shown) {marker.style.display = 'block'; shown = true;} // measuring needs layout\r\n      const nextRange = `· ${Math.round(range.portDistance)}u`, nextExtra = PHASE_TEXT[phase] || '', nextKey = `${nextRange.length}|${nextExtra}`;\r\n      if (nextRange !== rangeText || nextExtra !== extra) {\r\n        rangeText = nextRange; extra = nextExtra; rangeEl.textContent = rangeText; phaseText.textContent = extra;\r\n        marker.setAttribute('aria-label', `MOM STATION ${rangeText}${extra ? `. ${extra}` : ''}`);\r\n        if (nextKey !== sizeKey) {sizeKey = nextKey; measure();}\r\n      }\r\n      // where to put it: over the mast tip when the station is in the aperture, else toward the station's side (camera-space x/y keep the side\r\n      // right even when it is behind us)\r\n      let wx, wy, er = 0;\r\n      if (on) {\r\n        projected.copy(above).project(camera);\r\n        wx = (projected.x * .5 + .5) * w; wy = (-projected.y * .5 + .5) * h; er = Math.min(12 * camera.projectionMatrix.elements[5] * h / 2 / depth, .45 * Math.min(w, h));\r\n      } else {\r\n        view.copy(centre).applyMatrix4(camera.matrixWorldInverse);\r\n        let dx = view.x, dy = -view.y;\r\n        if (Math.hypot(dx, dy) < 1e-6) dy = -1;\r\n        const l = Math.hypot(dx, dy) / Math.max(w, h);\r\n        wx = w / 2 + dx / l; wy = h / 2 + dy / l; // a far point on the station's side; the nearest allowed spot to it is on the aperture edge\r\n      }\r\n      // wide label if it fits, the wrapped label if that sits clearly closer, the bare arrow only when no label fits anywhere\r\n      let chosen = -1;\r\n      for (let i = 0; i < MODES.length - 1; i++) {\r\n        if (!place(poly, w, h, i, clip, wx, on ? wy - sizes[i].h / 2 - 4 : wy, sx, sy, on ? er : 0, pick)) continue;\r\n        if (chosen < 0 || pick.d + 40 < spot.d) {chosen = i; spot.x = pick.x; spot.y = pick.y; spot.d = pick.d;}\r\n        if (spot.d < 1) break; // fits where it wants to be\r\n      }\r\n      if (chosen < 0 && place(poly, w, h, MODES.length - 1, clip, wx, on ? wy - sizes[MODES.length - 1].h / 2 - 4 : wy, sx, sy, on ? er : 0, pick)) {chosen = MODES.length - 1; spot.x = pick.x; spot.y = pick.y;}\r\n      if (chosen < 0) {marker.style.display = 'none'; shown = false; marker.dataset.placed = 'none'; return;} // no allowed spot at all (degenerate aperture)\r\n      if (MODES[chosen] !== mode) {mode = MODES[chosen]; applyMode(mode);}\r\n      const turn = on ? Math.atan2(sx - spot.x, -(sy - spot.y)) : Math.atan2(wx - w / 2, -(wy - h / 2)), deg = Math.round(turn * 180 / Math.PI);\r\n      if (deg !== angle) {angle = deg; glyph.style.transform = `rotate(${deg}deg)`;}\r\n      marker.style.left = `${spot.x}px`; marker.style.top = `${spot.y}px`;\r\n      marker.dataset.offscreen = String(!on); marker.dataset.turn = String(deg); marker.dataset.mode = mode; marker.dataset.placed = 'ok';\r\n    },\r\n    dispose() {if (disposed) return; disposed = true; overlay.remove();},\r\n  };\r\n}\r\n","modules/coach-flight/station-mesh.mjs":"// Original MOM station for the flight scene's space group (R82 base + station lane).\r\n// createStationMesh(T,{station,color}) -> {group,lod,update(dt,range),debugKey(on),dispose()}\r\n//  - The caller positions group at station.pos. The group NEVER rotates: hull, spine, hangar, gates and docking lights are fixed in world\r\n//    coordinates and the port faces (stationPort - station.pos). Only the decorative wheel ring and the running lights turn (spinRate).\r\n//  - Vertex-coloured MeshBasic with the scene's baked sun, 0 textures, no lights. The \"tell\": spine + crossbar, a spoked wheel, a glowing port.\r\n//  - THREE.LOD, 2 levels: far (>= 60 u) = hull+spine, wheel, running lights (3 draws); near adds hangar, 3 guide gates, docking lights (6 draws).\r\n//  - The hull is hand-built (no lathe): its rim wall is open across the port wedge and a boxed bay sits INSIDE the solid hull, so the mouth is a true recess.\r\nimport {stationPort} from './station.mjs';\r\nexport const STATION_LOD_NEAR = 60;\r\nconst TAU = Math.PI * 2, clamp01 = x => Math.min(1, Math.max(0, x)), mixc = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];\r\nconst SUN = (() => {const l = Math.hypot(-.45, .55, .7); return [-.45 / l, .55 / l, .7 / l];})(); // same sun as space-scene shade()\r\nconst FAR_STEEL = [.16, .2, .32], PORT_STEEL = [.5, .56, .72], SPINE = [.62, .68, .8], DARK = [.2, .23, .3];\r\nconst GATE_AT = [12, 26, 38]; // metres out from the port along its axis (farthest last); every gate is outside captureRadius\r\n\r\n/** Horizontal port heading: stationPort(station) minus station.pos, never a hard-coded axis. */\r\nfunction portDir(station) {\r\n  const port = stationPort(station), dx = port.x - station.pos.x, dz = port.z - station.pos.z, l = Math.hypot(dx, dz) || 1;\r\n  return [dx / l, dz / l];\r\n}\r\n\r\nexport function createStationMesh(T, {station, color = 0x85d5c3, reducedMotion} = {}) {\r\n  const hr = station.hullRadius, k = hr / 7.5, po = station.portOffset, spin = station.spinRate;\r\n  const reduced = reducedMotion ?? !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;\r\n  const [pdx, pdz] = portDir(station), yaw = Math.atan2(pdx, pdz), cy = Math.cos(yaw), sy = Math.sin(yaw);\r\n  const rot = (x, z) => [x * cy + z * sy, -x * sy + z * cy]; // local (+z = port) -> world, same as geometry.rotateY(yaw)\r\n  const sun = [SUN[0] * cy - SUN[2] * sy, SUN[1], SUN[0] * sy + SUN[2] * cy]; // world sun expressed in the local frame\r\n  const fc = new T.Color(color), F = [fc.r, fc.g, fc.b], GLOW = mixc(F, [1, 1, 1], .4);\r\n  const own = [], keep = o => (own.push(o), o);\r\n\r\n  // ---- painted, merged, flat-shaded geometry\r\n  const a = new T.Vector3(), b = new T.Vector3(), c = new T.Vector3(), n = new T.Vector3(), m = new T.Vector3();\r\n  // flat colour per face; the shape's own vertex normals say which way the face looks. `ref` (+ inward) re-orients hand-built triangles:\r\n  // the normal is flipped to point away from (or, inward, toward) that local point, so the baked sun never lights a face from behind.\r\n  function paint(acc, geo, {tone, shift = [0, 0, 0], ref, inward}) {\r\n    const g = geo.index ? geo.toNonIndexed() : geo, p = g.attributes.position, nn = g.attributes.normal;\r\n    for (let i = 0; i < p.count; i += 3) {\r\n      a.fromBufferAttribute(p, i); b.fromBufferAttribute(p, i + 1); c.fromBufferAttribute(p, i + 2);\r\n      n.fromBufferAttribute(nn, i); n.add(m.fromBufferAttribute(nn, i + 1)).add(m.fromBufferAttribute(nn, i + 2)).normalize();\r\n      m.set(...shift).multiplyScalar(k);\r\n      for (const v of [a, b, c]) v.multiplyScalar(k).add(m);\r\n      const x = (a.x + b.x + c.x) / 3, y = (a.y + b.y + c.y) / 3, z = (a.z + b.z + c.z) / 3;\r\n      if (ref && (n.x * (x - ref[0] * k) + n.y * (y - ref[1] * k) + n.z * (z - ref[2] * k) < 0) !== !!inward) n.negate();\r\n      const rgb = tone(x, y, z, n.x, n.y, n.z), s = rgb[3] ? 1 : .5 + .5 * (.2 + .8 * Math.max(0, n.x * sun[0] + n.y * sun[1] + n.z * sun[2]));\r\n      for (const v of [a, b, c]) acc.p.push(v.x, v.y, v.z), acc.c.push(rgb[0] * s, rgb[1] * s, rgb[2] * s);\r\n    }\r\n    if (g !== geo) g.dispose();\r\n    geo.dispose();\r\n  }\r\n  const finish = acc => {\r\n    const g = keep(new T.BufferGeometry());\r\n    g.setAttribute('position', new T.BufferAttribute(new Float32Array(acc.p), 3)); g.setAttribute('color', new T.BufferAttribute(new Float32Array(acc.c), 3));\r\n    return g.rotateY(yaw);\r\n  };\r\n  const acc = () => ({p: [], c: []});\r\n\r\n  // ---- hull: a real 36-gon dish whose rim wall is OPEN across the port wedge (-20..+20 deg). Behind that opening sits a boxed bay (floor, roof,\r\n  // two side walls, back wall) fully inside the solid hull, so the mouth is a true recess: you see dark walls and depth, not a plate behind the skin.\r\n  const PROFILE = [[0, 1.9], [2.6, 1.85], [5.6, 1.6], [7.5, 1.25], [7.5, 1], [7.5, -1], [7.5, -1.25], [6.3, -1.55], [3, -1.65], [0, -1.75]];\r\n  const SEG = 36, WEDGE = [34, 35, 0, 1], BAY_H = 1, BAY_BACK = 3.4, MOUTH_X = 7.5 * Math.sin(Math.PI / 9), MOUTH_Z = 7.5 * Math.cos(Math.PI / 9);\r\n  function trisToGeometry(list) { // list = flat xyz triples; degenerate (pole) triangles are dropped so no normal is NaN\r\n    const out = [];\r\n    for (let i = 0; i < list.length; i += 9) {\r\n      a.set(list[i], list[i + 1], list[i + 2]); b.set(list[i + 3], list[i + 4], list[i + 5]); c.set(list[i + 6], list[i + 7], list[i + 8]);\r\n      if (n.subVectors(b, a).cross(m.subVectors(c, a)).lengthSq() > 1e-10) for (let q = 0; q < 9; q++) out.push(list[i + q]);\r\n    }\r\n    const g = new T.BufferGeometry();\r\n    g.setAttribute('position', new T.BufferAttribute(new Float32Array(out), 3)); g.computeVertexNormals();\r\n    return g;\r\n  }\r\n  const quads = list => (p, q, r, s) => list.push(...p, ...q, ...r, ...p, ...r, ...s);\r\n  const ring = (r, y, i) => [Math.sin(i * TAU / SEG) * r, y, Math.cos(i * TAU / SEG) * r];\r\n  const shellList = [], bayList = [];\r\n  {\r\n    const quad = quads(shellList);\r\n    for (let j = 0; j < PROFILE.length - 1; j++) for (let i = 0; i < SEG; i++) {\r\n      if (j === 4 && WEDGE.includes(i)) continue; // the middle band of the rim wall is the opening\r\n      const [r0, y0] = PROFILE[j], [r1, y1] = PROFILE[j + 1];\r\n      quad(ring(r0, y0, i), ring(r0, y0, i + 1), ring(r1, y1, i + 1), ring(r1, y1, i));\r\n    }\r\n    const bay = quads(bayList);\r\n    for (const y of [BAY_H, -BAY_H]) for (const i of WEDGE) {const p0 = ring(7.5, y, i), p1 = ring(7.5, y, i + 1); bay(p0, p1, [p1[0], y, BAY_BACK], [p0[0], y, BAY_BACK]);}\r\n    for (const s of [-1, 1]) bay([s * MOUTH_X, BAY_H, MOUTH_Z], [s * MOUTH_X, -BAY_H, MOUTH_Z], [s * MOUTH_X, -BAY_H, BAY_BACK], [s * MOUTH_X, BAY_H, BAY_BACK]);\r\n    bay([-MOUTH_X, BAY_H, BAY_BACK], [MOUTH_X, BAY_H, BAY_BACK], [MOUTH_X, -BAY_H, BAY_BACK], [-MOUTH_X, -BAY_H, BAY_BACK]);\r\n  }\r\n  const hullTone = (x, y, z) => {\r\n    const r = Math.hypot(x, z), yy = y / k, ang = Math.atan2(x, z), seg = Math.floor((ang + Math.PI) / (TAU / SEG));\r\n    if (r > .96 * hr && Math.abs(yy) <= 1.3) {\r\n      if (Math.abs(yy) > .95 && Math.abs(x) <= MOUTH_X * k + .1 && z > 0) return [...F.map(v => v * .85), 1]; // lit lintel + sill: the restrained frame\r\n      if (Math.abs(yy) <= .95) { // lit band: 8-shade ramp, brightest at the port, darkest on the far side, with every other panel a dark window strip\r\n        const j = Math.round(7 * (1 - Math.abs(ang) / Math.PI));\r\n        return [...F.map(v => v * (.25 + .75 * j / 7) * (seg % 2 ? .55 : 1)), 1];\r\n      }\r\n      return mixc(DARK, FAR_STEEL, .5); // rim collars above and below the band stay dark\r\n    }\r\n    const lift = y > 0 ? .12 : -.1; // top deck a step lighter than the keel side\r\n    return mixc(FAR_STEEL, PORT_STEEL, Math.round(clamp01(.5 + .5 * z / hr + lift) * 4) / 4).map(v => v * (seg % 3 === 0 ? .88 : 1)); // 5-step gradient + panel seams\r\n  };\r\n  const bayTone = (x, y, z, nx, ny, nz) => nz > .9 // back wall: dim glow only (restrained), the rest dark steel\r\n    ? [...mixc([.03, .04, .06], F, .22 + .2 * clamp01(1 - Math.hypot(x / (MOUTH_X * k), y / (BAY_H * k)))).map(v => v * .6), 1] : mixc([.02, .03, .05], [.08, .1, .14], clamp01(.5 + z / (8 * k)));\r\n  const hullAcc = acc(), up = (x, y, z) => mixc(PORT_STEEL, SPINE, clamp01(y / k - 1.8) / 2).map(v => v * (Math.floor((Math.atan2(x, z) + Math.PI) / (TAU / 12)) % 2 ? .86 : 1));\r\n  paint(hullAcc, trisToGeometry(shellList), {tone: hullTone, ref: [0, 0, 0]});\r\n  paint(hullAcc, trisToGeometry(bayList), {tone: bayTone, ref: [0, 0, 5.4], inward: true});\r\n  paint(hullAcc, new T.CylinderGeometry(4.3, 5, 1, 18).translate(0, 2.3, 0), {tone: up}); // deck stack: two stepped drums, then the mast\r\n  paint(hullAcc, new T.CylinderGeometry(2.2, 2.8, 1.2, 12).translate(0, 3.3, 0), {tone: up});\r\n  const spineParts = (to, f) => { // mast, yard arms, beacon, keel pod + spire; the far level calls it fatter (f > 1) so they still read as pixels at 100+ u\r\n    paint(to, new T.CylinderGeometry(.16 * f, .5 * f, 9.6, 6).translate(0, 8.75, 0), {tone: (x, y) => mixc(SPINE, F, clamp01((y / k - 4) / 9) * .5)});\r\n    paint(to, new T.BoxGeometry(5.2, .28 * f, .28 * f).translate(0, 9.5, 0), {tone: x => Math.abs(x) > 2.2 * k ? [...GLOW, 1] : SPINE});\r\n    paint(to, new T.BoxGeometry(3.2, .22 * f, .22 * f).translate(0, 11.6, 0), {tone: x => Math.abs(x) > 1.2 * k ? [...GLOW, 1] : SPINE});\r\n    paint(to, new T.ConeGeometry(.5 * f, 1.2 * f, 6).translate(0, 14.15, 0), {tone: () => [...GLOW, 1]});\r\n    paint(to, new T.CylinderGeometry(2.4, 1.1 * f, 2.6, 10).translate(0, -2.95, 0), {tone: (x, y, z) => mixc(DARK, FAR_STEEL, clamp01(.5 + .5 * z / hr))}); // keel pod + spire\r\n    paint(to, new T.ConeGeometry(.9 * f, 4.2, 8).rotateX(Math.PI).translate(0, -6.35, 0), {tone: (x, y) => y / k < -7.4 ? [...F.map(v => v * .9), 1] : SPINE});\r\n  };\r\n  for (const ang of [-2.4, -.95, .95, 2.4]) paint(hullAcc, new T.BoxGeometry(1.3, 6, 1.3).translate(0, .8, 7.2).rotateY(ang), { // buttress towers, clear of the mouth\r\n    tone: (x, y, z, nx, ny) => ny > .9 ? [...F.map(v => v * .9), 1] : mixc(DARK, PORT_STEEL, clamp01((y / k + 2) / 6))});\r\n  const farAcc = {p: hullAcc.p.slice(), c: hullAcc.c.slice()}; // base (hull, bay, decks, towers) shared by both levels; only the spine parts differ\r\n  spineParts(hullAcc, 1); spineParts(farAcc, 1.8);\r\n  const wheelAcc = acc(), wheelFarAcc = acc();\r\n  for (const [wacc, f] of [[wheelAcc, 1], [wheelFarAcc, 1.7]]) {\r\n    paint(wacc, new T.TorusGeometry(9.4, .32 * f, 5, 36).rotateX(Math.PI / 2).translate(0, 3, 0), {\r\n      tone: (x, y, z) => Math.floor((Math.atan2(x, z) + Math.PI) / (TAU / 36)) % 3 ? DARK : [...F.map(v => v * .95), 1]});\r\n    for (const [w, d] of [[18.4, .4], [.4, 18.4]]) paint(wacc, new T.BoxGeometry(w, .14 * f, d).translate(0, 3, 0), {tone: () => DARK});\r\n  }\r\n  const hangarAcc = acc(), glow = v => [...F.map(q => q * v), 1];\r\n  for (const s of [-1, 1]) paint(hangarAcc, new T.BoxGeometry(.22, 2.1, .4).translate(s * (MOUTH_X + .02), 0, MOUTH_Z - .1), {tone: () => glow(.8)}); // lit jambs (lintel and sill are lit in the hull)\r\n  for (const x of [-1.4, 0, 1.4]) paint(hangarAcc, new T.BoxGeometry(.14, .04, MOUTH_Z - BAY_BACK - .4).translate(x, -BAY_H + .03, (MOUTH_Z + BAY_BACK) / 2), {tone: () => glow(x ? .4 : .6)}); // deck guide lines\r\n  for (const [z, v] of [[4.5, .2], [5.8, .34]]) { // two lit rib frames inside the bay (full loops, dimmer the deeper): parallax cues that make it read as a tunnel\r\n    const bar = (w, h, x, y) => paint(hangarAcc, new T.BoxGeometry(w, h, .16).translate(x, y, z), {tone: () => glow(v)});\r\n    bar(MOUTH_X * 2 - .14, .1, 0, BAY_H - .07); bar(MOUTH_X * 2 - .14, .1, 0, -BAY_H + .07); bar(.1, BAY_H * 2 - .14, -MOUTH_X + .07, 0); bar(.1, BAY_H * 2 - .14, MOUTH_X - .07, 0);\r\n  }\r\n  paint(hangarAcc, new T.PlaneGeometry(MOUTH_X * 1.1, BAY_H * 1.0, 4, 2), {shift: [0, 0, BAY_BACK + .06], tone: (x, y) => glow(.12 + .3 * clamp01(1 - Math.hypot(x / (MOUTH_X * k * .55), y / (BAY_H * k * .5))))}); // back-wall glow patch: small, dim core\r\n\r\n  // ---- Points: running lights (rim), guide gates (port axis), docking lights (hangar mouth). Colour attributes are preallocated and rewritten in place.\r\n  function pointSet(list, size) {\r\n    const pos = new Float32Array(list.length * 3), col = new Float32Array(list.length * 3);\r\n    list.forEach(([x, y, z], i) => {const [wx, wz] = rot(x, z); pos.set([wx, y, wz], i * 3);});\r\n    const g = keep(new T.BufferGeometry());\r\n    g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));\r\n    return {geo: g, col, mat: keep(new T.PointsMaterial({size, sizeAttenuation: false, vertexColors: true, fog: false})), n: list.length};\r\n  }\r\n  const rim = Array.from({length: 10}, (_, i) => [Math.sin(i * TAU / 10) * 7.6 * k, .05 * k, Math.cos(i * TAU / 10) * 7.6 * k]);\r\n  const gates = GATE_AT.flatMap(d => Array.from({length: 6}, (_, i) => [Math.sin(i * TAU / 6) * 3, Math.cos(i * TAU / 6) * 3, po + d]));\r\n  const mouth = [[-MOUTH_X, -BAY_H, MOUTH_Z + .15], [0, -BAY_H, MOUTH_Z + .55], [MOUTH_X, -BAY_H, MOUTH_Z + .15], [-MOUTH_X, 0, MOUTH_Z + .15], [MOUTH_X, 0, MOUTH_Z + .15], [-MOUTH_X, BAY_H, MOUTH_Z + .15], [0, BAY_H, MOUTH_Z + .55], [MOUTH_X, BAY_H, MOUTH_Z + .15]].map(([x, y, z]) => [x * k, y * k, z * k]);\r\n  const run = pointSet(rim, 3), gate = pointSet(gates, 3), dock = pointSet(mouth, 4);\r\n  const fill = (set, i, rgb, v = 1) => set.col.set([rgb[0] * v, rgb[1] * v, rgb[2] * v], i * 3);\r\n  for (let i = 0; i < run.n; i++) fill(run, i, F, i % 2 ? .55 : 1);\r\n  const shared = {hull: finish(hullAcc), hullFar: finish(farAcc), wheel: finish(wheelAcc), wheelFar: finish(wheelFarAcc), hangar: finish(hangarAcc)};\r\n  const mat = () => keep(new T.MeshBasicMaterial({vertexColors: true, fog: false, side: T.DoubleSide})), mats = {hull: mat(), wheel: mat(), hangar: mat()};\r\n  mats.hullFar = mats.hull; mats.wheelFar = mats.wheel;\r\n  const mesh = (key, name) => {const m = new T.Mesh(shared[key], mats[key]); m.name = name; return m;};\r\n  const points = (set, name) => {const p = new T.Points(set.geo, set.mat); p.name = name; return p;};\r\n\r\n  const near = new T.Group(), far = new T.Group(), group = new T.Group(), lod = new T.LOD();\r\n  near.name = 'mom-station-near'; far.name = 'mom-station-far'; group.name = 'mom-station'; lod.name = 'mom-station-lod';\r\n  const turning = []; // wheel meshes + running-light points: the only things that move\r\n  for (const [level, sfx] of [[near, ''], [far, 'Far']]) { // the far level carries a fatter mast/keel/ring (same shape, ~1.8x tube) so they survive 1 px/u\r\n    const wheel = mesh('wheel' + sfx, 'mom-station-wheel'), lights = points(run, 'mom-station-lights');\r\n    level.add(mesh('hull' + sfx, 'mom-station-hull'), wheel, lights); turning.push(wheel, lights);\r\n  }\r\n  near.add(mesh('hangar', 'mom-station-hangar'), points(gate, 'mom-station-gates'), points(dock, 'mom-station-dock'));\r\n  near.visible = false; // until the first render's LOD.update; keeps pre-render sceneStats on the cheap level\r\n  lod.addLevel(near, 0); lod.addLevel(far, STATION_LOD_NEAR, .15); group.add(lod);\r\n\r\n  // ---- per-frame state: numbers and preallocated buffers only\r\n  let t = 0, state = -1, dead = false;\r\n  const paintGates = mode => { // 0 dim, 1 pulse running in toward the port, 2 steady half, 3 fully lit\r\n    for (let g = 0; g < GATE_AT.length; g++) {\r\n      const v = mode === 0 ? .1 : mode === 2 ? .65 : mode === 3 ? 1 : .3 + .7 * Math.max(0, 1 - Math.abs((t * 1.6) % 3 - (2 - g)));\r\n      for (let i = 0; i < 6; i++) fill(gate, g * 6 + i, F, v);\r\n    }\r\n    gate.geo.attributes.color.needsUpdate = true;\r\n  };\r\n  const paintDock = v => {for (let i = 0; i < dock.n; i++) fill(dock, i, v === 2 ? GLOW : F, v === 2 ? 1 : v === 1 ? .45 : .12); dock.geo.attributes.color.needsUpdate = true;};\r\n  const stateOf = phase => phase === 'far' || phase == null ? 0 : phase === 'approach' ? 1 : 2; // capture / ready / docking / docked all light fully\r\n  function update(dt, range) {\r\n    if (dead) return;\r\n    const s = Math.min(Math.max(Number.isFinite(dt) ? dt : 0, 0), .1), next = stateOf(range?.phase);\r\n    t += s;\r\n    if (!reduced) for (let i = 0; i < turning.length; i++) turning[i].rotation.y += spin * s;\r\n    if (next === 1 && !reduced) paintGates(1);\r\n    else if (next !== state) paintGates(next === 0 ? 0 : next === 1 ? 2 : 3);\r\n    if (next !== state) paintDock(next);\r\n    state = next;\r\n  }\r\n  update(0, null);\r\n\r\n  const keyed = []; // [material, saved vertexColors]\r\n  function debugKey(on) { // test hook: flat magenta everywhere so a pixel count can prove the station is on screen\r\n    group.traverse(o => {\r\n      const m = o.material;\r\n      if (!m) return;\r\n      if (on && !keyed.some(r => r[0] === m)) keyed.push([m, m.vertexColors, m.color.getHex()]);\r\n      for (const r of keyed) if (r[0] === m) {m.vertexColors = !on && r[1]; m.color.setHex(on ? 0xff00ff : r[2]); m.needsUpdate = true;}\r\n    });\r\n  }\r\n  group.userData.debugKey = debugKey;\r\n  return {group, lod, update, debugKey, dispose() {if (dead) return; dead = true; new Set(own).forEach(o => o.dispose());}};\r\n}\r\n","modules/coach-flight/station.mjs":"/** MOM Station: the one orbital destination. Pure domain: no THREE, DOM, I/O or timers.\r\n * Space frame = scene units \"u\" (compressed, NOT metres), origin = globe centre, +y north.\r\n * Pose {x,y,z,yaw,pitch,speed}; yaw per core headingTo (north -z = 0, west = +PI/2).\r\n * The station is not a geo place: no lat/lng, never enters travel.mjs. Fixed in world space.\r\n */\r\nimport {headingTo, stepCruise} from './core.mjs';\r\nexport const STATION = Object.freeze({\r\n  id: 'mom-station', label: 'MOM Station',\r\n  pos: Object.freeze({x: 40, y: 130, z: 110}),\r\n  // Face the launch approach (SPACE_START x0,z230), so pilots see the entrance without crossing the hull.\r\n  portDirection: Object.freeze({x: -1, z: 3}),\r\n  hullRadius: 7.5, portOffset: 14,\r\n  captureRadius: 10, approachRadius: 150, slowRadius: 40, minSpeed: 12, dockBack: 6, undockBack: 20,\r\n  dockMs: 2500, spinRate: 0.05,\r\n});\r\n/** Space pilot envelope shared with view.mjs: altitude band (u), climb rate (u/s per full pull),\r\n * cruise top speed (u/s) and braking response (1/s) used when the station cap is below current speed. */\r\nexport const SPACE_FLIGHT = Object.freeze({floor: 105, ceiling: 320, climb: 35, maxSpeed: 95, brake: 3});\r\nconst finite = v => Number.isFinite(v) ? v : 0;\r\nconst wrap = a => Math.atan2(Math.sin(a), Math.cos(a));\r\nconst point = p => ({x: finite(p?.x), y: finite(p?.y), z: finite(p?.z)});\r\n/** Unit horizontal normal from the hull centre toward the arrival side. */\r\nfunction normal(station) {\r\n  const x = station.portDirection?.x ?? -station.pos.x, z = station.portDirection?.z ?? -station.pos.z, len = Math.hypot(x, z) || 1;\r\n  return {x: x / len, y: 0, z: z / len};\r\n}\r\nconst along = (station, back) => {\r\n  const n = normal(station), port = stationPort(station);\r\n  return {x: port.x + n.x * back, y: port.y, z: port.z + n.z * back};\r\n};\r\nexport function stationPort(station = STATION) {\r\n  const n = normal(station), {pos, portOffset} = station;\r\n  return {x: pos.x + n.x * portOffset, y: pos.y, z: pos.z + n.z * portOffset};\r\n}\r\nexport function stationRange(pose, station = STATION) {\r\n  const p = point(pose), port = stationPort(station), pos = station.pos;\r\n  const distance = Math.hypot(pos.x - p.x, pos.y - p.y, pos.z - p.z);\r\n  const portDistance = Math.hypot(port.x - p.x, port.y - p.y, port.z - p.z);\r\n  const bearing = wrap(headingTo(p, port) - finite(pose?.yaw));\r\n  const elevation = Math.atan2(port.y - p.y, Math.hypot(port.x - p.x, port.z - p.z));\r\n  const phase = portDistance <= station.captureRadius ? 'capture'\r\n    : portDistance <= station.approachRadius ? 'approach' : 'far';\r\n  return {distance, portDistance, bearing, elevation, phase};\r\n}\r\nexport function approachSpeedCap(portDistance, station = STATION, maxSpeed = 95) {\r\n  const d = finite(portDistance), {approachRadius: far, slowRadius: near, minSpeed} = station;\r\n  const top = Math.max(minSpeed, finite(maxSpeed));\r\n  if (d >= far) return top;\r\n  if (d <= near) return minSpeed;\r\n  return minSpeed + (top - minSpeed) * (d - near) / (far - near);\r\n}\r\n/** True iff the segment prev->next passes within captureRadius of the port (endpoints included). */\r\nexport function sweptCapture(prev, next, station = STATION) {\r\n  const a = point(prev), b = point(next), c = stationPort(station);\r\n  const dx = b.x - a.x, dy = b.y - a.y, dz = b.z - a.z, len2 = dx * dx + dy * dy + dz * dz;\r\n  const t = len2 > 0 ? Math.min(1, Math.max(0, ((c.x - a.x) * dx + (c.y - a.y) * dy + (c.z - a.z) * dz) / len2)) : 0;\r\n  return Math.hypot(a.x + dx * t - c.x, a.y + dy * t - c.y, a.z + dz * t - c.z) <= station.captureRadius;\r\n}\r\nexport function dockPose(station = STATION) {\r\n  const p = along(station, station.dockBack);\r\n  return {...p, yaw: headingTo(p, station.pos), pitch: 0};\r\n}\r\nexport function undockPose(station = STATION) {\r\n  const p = along(station, station.undockBack);\r\n  return {...p, yaw: wrap(headingTo(p, station.pos) + Math.PI), pitch: 0, speed: 0};\r\n}\r\n/** DOCK tap recheck: a swept latch alone never docks; the ship must be inside the capture\r\n * sphere and at or below approach minimum speed at the moment of the tap. */\r\nexport function canDock(pose, station = STATION) {\r\n  return stationRange(pose, station).portDistance <= station.captureRadius\r\n    && finite(pose?.speed) <= station.minSpeed + 1e-9;\r\n}\r\n/** Hull keep-out (hullRadius + 2 u skin): pushes the pilot radially out and keeps speed, so a\r\n * line that grazes the hull slides around it instead of passing through or dead-stopping.\r\n * (SPACE_START sits behind the station; the straight line to the port passes ~8.2 u from centre.) */\r\nexport function clearHull(pose, station = STATION) {\r\n  const {pos, hullRadius} = station, min = hullRadius + 2;\r\n  const dx = finite(pose.x) - pos.x, dy = finite(pose.y) - pos.y, dz = finite(pose.z) - pos.z;\r\n  const len = Math.hypot(dx, dy, dz);\r\n  if (len >= min) return pose;\r\n  const n = len > 1e-9 ? {x: dx / len, y: dy / len, z: dz / len} : normal(station);\r\n  return {...pose, x: pos.x + n.x * min, y: pos.y + n.y * min, z: pos.z + n.z * min};\r\n}\r\n/** One space-cruise step under the station governor: true braking toward the approach cap\r\n * (never an instant cap), hover target (0) once captured, then the stick's altitude pull. */\r\nexport function stationCruise(pose, input, dt, captured = false, station = STATION) {\r\n  const range = stationRange(pose, station), f = SPACE_FLIGHT;\r\n  const next = stepCruise(pose, input, dt, {floor: f.floor, brake: f.brake,\r\n    maxSpeed: captured && range.portDistance <= station.captureRadius ? 0 : approachSpeedCap(range.portDistance, station, f.maxSpeed)});\r\n  const t = Math.min(0.1, Math.max(0, finite(dt))), pull = Math.max(-1, Math.min(1, finite(input?.pull)));\r\n  next.y = Math.max(f.floor, Math.min(f.ceiling, next.y + pull * f.climb * t));\r\n  return next;\r\n}\r\n","modules/coach-flight/survey.mjs":"// Flight markers (R1-C11): coaches + zone NPCs near the flight origin. Tap = fly beside it, face it, then the usual landing-pin exit.\r\n// Coach markers are visual hints from the same deterministic AR NPC seed; zone NPCs come from the server zones DTO (defeated ones skipped).\r\nimport {ringCells,toLocal} from '../coach-world/world-tiles.mjs';\r\nimport {spawnNpcs,besidePoint} from '../coach-world-rts/ar-npcs.mjs';\r\nimport {PALETTE,HOLO_HUES,PIPS,rollTier} from '../coach-world-rts/wild-rarity.mjs';\r\nimport {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\nimport {npcSpots,npcCandidates,zoneCenter} from '../coach-world/zones.mjs';\r\nimport {WORLD_SITES} from '../coach-world/sites.mjs';\r\nimport {localToGeo,geoToLocal} from './core.mjs';\r\n\r\nconst MAX_MARKERS=6,MAX_ZONE=4,ZONE_BAKE='r58-1',ZONE_RING='#ff5ad1';\r\n// Art: pets sheet = whole four-legged sprite; bodies sheet 'myr5' frame [0,0,64,96] cropped to its top 44 px = the alien's head.\r\nconst BODIES='/pod/gala-coaches/bodies.png',MYR5_HEAD={src:BODIES,sheet:[512,768],frame:[0,0,64,44],k:.7};\r\nconst petArt=id=>{const s=FOUR_LEGGED_SOURCES.find(p=>p.sourceCoachId===id);return s?{src:s.atlasPath,sheet:[240,72],frame:s.frame,k:.9}:null;};\r\nexport const rarityColor=tier=>PALETTE[tier]||'#'+HOLO_HUES[0].toString(16).padStart(6,'0');\r\nconst geoFrom=(local,centre)=>localToGeo(local,centre);\r\n\r\nexport function surveyAreas(origin){\r\n if(!Number.isFinite(origin?.lat)||!Number.isFinite(origin?.lng))return [];\r\n const cells=ringCells(origin.lat,origin.lng),size=2**cells[0].z;\r\n const areas=cells.map(cell=>{\r\n  const centre={lng:(cell.x+.5)/size*360-180,lat:Math.atan(Math.sinh(Math.PI*(1-2*(cell.y+.5)/size)))*180/Math.PI};\r\n  const npcs=spawnNpcs(cell),npc=npcs.find(n=>n.rare)||npcs[0],rare=!!npc?.rare;\r\n  const at=npc?geoFrom(npc,centre):centre,beside=npc?geoFrom(besidePoint(npc),centre):centre;\r\n  const tier=rare?rollTier(npc.id,'park'):null,local=toLocal(origin,at.lat,at.lng);\r\n  return {source:'site',id:`flight-survey:${cell.z}:${cell.x}:${cell.y}`,label:npc?.name||'NPC area',lat:at.lat,lng:at.lng,beside,\r\n   kind:rare?'four-legged':'head',tier,ring:rare?rarityColor(tier):npc?.look?.tint||'#d9eab9',art:(rare&&petArt(npc.source))||MYR5_HEAD,\r\n   x:local.x,z:local.z,rare,distance:Math.hypot(local.x,local.z),\r\n   npc:npc&&{id:npc.id,name:npc.name,rare,level:npc.level,look:npc.look,source:npc.source,heightM:npc.heightM,tier:npc.tier,cell:{z:cell.z,x:cell.x,y:cell.y}}}; // everything AR needs to stand the same coach 3 m in front of you\r\n }).sort((a,b)=>a.distance-b.distance);\r\n const rare=areas.filter(a=>a.rare),ordinary=areas.filter(a=>!a.rare);\r\n return [...rare.slice(0,3),...ordinary.slice(0,MAX_MARKERS-Math.min(3,rare.length)),\r\n  ...rare.slice(3)].slice(0,MAX_MARKERS);\r\n}\r\n\r\n// zones DTO (GET /api/world/zones) -> nearest undefeated zone NPC markers. Positions are client-side (same geometry as the server).\r\nexport function zoneAreas(origin,dto){\r\n const out=[];\r\n for(const zone of dto?.zones||[]){\r\n  const live=new Set((zone.npc||[]).filter(n=>!n.defeated).map(n=>n.idx));if(!live.size)continue;\r\n  const c=zoneCenter(zone.id),centre={lat:c.latitude,lng:c.longitude};\r\n  for(const s of npcSpots(zone.id,npcCandidates(zone.id,WORLD_SITES),ZONE_BAKE)){\r\n   if(!live.has(s.idx))continue;const at={lat:s.latitude,lng:s.longitude},local=toLocal(origin,at.lat,at.lng);\r\n   out.push({source:'site',id:`flight-zone:${zone.id}:${s.idx}`,label:'Zone NPC',lat:at.lat,lng:at.lng,beside:geoFrom(besidePoint(geoToLocal(at,centre)),centre),\r\n    kind:'zone',tier:null,ring:ZONE_RING,art:null,x:local.x,z:local.z,rare:false,distance:Math.hypot(local.x,local.z)});\r\n  }\r\n }\r\n return out.sort((a,b)=>a.distance-b.distance).slice(0,MAX_ZONE);\r\n}\r\n\r\nfunction markerButton(doc,area,onSelect){\r\n const b=doc.createElement('button'),el=css=>{const n=doc.createElement('span');n.style.cssText=css;b.append(n);return n;};\r\n b.type='button';b.dataset.areaId=area.id;b.dataset.markerKind=area.kind;if(area.tier)b.dataset.tier=area.tier;\r\n b.setAttribute('aria-label',`${area.label}${area.tier?`, ${area.tier} four-legged coach`:''}. Fly over and land beside.`);\r\n const zone=area.kind==='zone';\r\n b.style.cssText=`position:absolute;display:none;transform:translate(-50%,-50%)${zone?' rotate(45deg)':''};width:50px;height:50px;min-width:44px;min-height:44px;padding:0;border:3px solid ${area.ring};border-radius:${zone?'6px':'50%'};background:#13222de8;box-shadow:0 0 9px ${area.ring};pointer-events:auto;touch-action:manipulation;overflow:visible`;\r\n const centre='position:absolute;left:50%;top:50%;pointer-events:none;';\r\n if(area.art){const {src,sheet:[W,H],frame:[x,y,w,h],k}=area.art;\r\n  el(`${centre}transform:translate(-50%,-50%);width:${w*k}px;height:${h*k}px;background:url(${src}) -${x*k}px -${y*k}px/${W*k}px ${H*k}px no-repeat;image-rendering:pixelated`);}\r\n else el(`${centre}transform:translate(-50%,-50%) rotate(-45deg);font:bold 13px monospace;color:${area.ring}`).textContent='NPC';\r\n if(area.tier)el(`position:absolute;right:-7px;bottom:-7px;padding:0 3px;border-radius:6px;background:#0d0d14;color:${area.ring};font:9px monospace;pointer-events:none`).textContent='◆'.repeat(PIPS[area.tier]||1);\r\n if(!zone)el('position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:3px;max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:10px monospace;color:#efffe3;text-shadow:1px 1px #091522;pointer-events:none').textContent=area.label;\r\n b.onclick=()=>onSelect({source:area.source,id:area.id,label:area.label,lat:area.lat,lng:area.lng,beside:{...area.beside},kind:area.kind});\r\n return b;\r\n}\r\n\r\nexport function createFlightSurvey(T,{host,origin,onSelect=()=>{}}={}){\r\n const doc=host.ownerDocument,overlay=doc.createElement('div');\r\n overlay.dataset.flightSurvey='';\r\n overlay.style.cssText='position:absolute;inset:0;pointer-events:none;z-index:2';\r\n host.append(overlay);\r\n let items=[];\r\n const add=areas=>areas.map(area=>{const button=markerButton(doc,area,onSelect);overlay.append(button);return {area,button,point:new T.Vector3(area.x,0,area.z)};});\r\n items=add(surveyAreas(origin));\r\n let disposed=false;const eye=new T.Vector3(),forward=new T.Vector3(),view=new T.Vector3(),projected=new T.Vector3();\r\n return {\r\n  setZones(dto){if(disposed)return;for(const it of items.filter(i=>i.area.kind==='zone')){it.button.onclick=null;it.button.remove();}items=[...items.filter(i=>i.area.kind!=='zone'),...add(zoneAreas(origin,dto))];},\r\n  hide(){for(const {button} of items)button.style.display='none';},\r\n  update(camera){\r\n   if(disposed||!camera)return;\r\n   const rect=host.getBoundingClientRect();if(!rect.width||!rect.height)return;\r\n   camera.updateMatrixWorld();camera.getWorldPosition(eye);camera.getWorldDirection(forward);\r\n   const shown=[];for(const {button,point} of items){\r\n    view.copy(point).sub(eye);projected.copy(point).project(camera);\r\n    const px=(projected.x*.5+.5)*rect.width,py=(-projected.y*.5+.5)*rect.height;\r\n    const visible=view.dot(forward)>0&&projected.z>=-1&&projected.z<=1&&Math.abs(projected.x)<=.8&&Math.abs(projected.y)<=.8&&!shown.some(p=>Math.abs(p.x-px)<60&&Math.abs(p.y-py)<70);\r\n    button.style.display=visible?'block':'none';\r\n    if(visible){shown.push({x:px,y:py});button.style.left=`${(projected.x*.5+.5)*100}%`;button.style.top=`${(-projected.y*.5+.5)*100}%`;}\r\n   }\r\n  },\r\n  dispose(){if(disposed)return;disposed=true;for(const {button} of items)button.onclick=null;overlay.remove();},\r\n };\r\n}\r\n","modules/coach-flight/transitions.mjs":"// Exterior flight transitions + MOM police / Mommy & Alicia encounters (R81 flight lane, staged: not yet in a checkout).\r\n// createFlightEffects(host,{color,reduced,timing,timers}) -> {play(kind,{ready,signal,onChoice,onCover,retry}), dispose()}\r\n//\r\n// INTEGRATION (parent owns renderer, cockpit frame, camera, scene swap, flight state, pause/background handling):\r\n//  - host is the STAGE div inside the cockpit glass. Overlays are appended INSIDE it (position:absolute; inset:0; z-index:60),\r\n//    never in the cockpit frame, so the ship/frame identity stays untouched. No renderer, canvas, network or globals except timers.\r\n//  - play() returns a promise. It resolves once the overlay has fully covered the stage, `ready` has FULFILLED and the overlay has\r\n//    faded out: {kind, covered:true, choice?:'business'|'pleasure'|'world-domination', skipped?:boolean}. It rejects with an\r\n//    AbortError DOMException on signal abort, user Return button (err.userReturn===true), dispose() or being superseded.\r\n//  - kinds: 'warp' (exterior pixel tunnel, outbound portal look), 'reentry' (pixel plasma rim + heat + cloud/haze cover; exterior\r\n//    only, NOT a portal and no tunnel), 'checkpoint' (MOM police UFO, waits for Business / Pleasure / World domination, then\r\n//    onChoice(id)), 'boundary' (Mommy & Alicia warning; completes automatically, the optional OK tap only skips the remaining text).\r\n//  - Frame zero: the overlay is attached with data-phase=\"pre\" (every child visibility:hidden, nothing painted). Pixels start only\r\n//    after the first beat (timing.beat), so the presented cockpit/exterior frame is the unchanged previous frame.\r\n//  - onCover() fires once, when the stage is fully opaque; swap the scene (scale/origin/camera) under it. `ready` may be created\r\n//    before or after that; the reveal never starts before it has fulfilled. While ready is pending the cover is held opaque with\r\n//    honest \"Loading\" text and a 44px Return button. If ready rejects the text says so; Retry appears only when you supply\r\n//    retry() (it must return a NEW promise: a rejected promise cannot be retried), Return rejects with AbortError for the parent.\r\n//  - Mommy/Alicia are text-labelled pixel placeholders: no character art or memories are assumed. Swap in real sprites later.\r\n//  - reduced:true => same beats and same enforcement, but steady matte cover: no flashing, shake, streaks or scaling animation.\r\nconst DEFAULT_COLOR = '#73dbcb';\r\nexport const TIMING = Object.freeze({beat: 40, warp: 2300, reentry: 3300, boundary: 1700, ufo: 800, reply: 1100, cover: 300, reveal: 280, slow: 5000});\r\n/** Longest unattended effect (excludes waiting for a choice or for ready): beat + run + cover + reveal. */\r\nexport const effectDuration = (kind, t = TIMING) => t.beat + (t[kind === 'checkpoint' ? 'ufo' : kind] ?? 0) + (kind === 'checkpoint' ? t.reply : 0) + t.cover + t.reveal;\r\nexport const CHOICES = Object.freeze([\r\n  {id: 'business', label: 'Business', reply: 'Business. Thrilling. Carry on.'},\r\n  {id: 'pleasure', label: 'Pleasure', reply: 'Pleasure. Keep it family friendly.'},\r\n  {id: 'world-domination', label: 'World domination', reply: 'Bold. A permit is pending. Proceed.'},\r\n]);\r\nconst KINDS = new Set(['warp', 'reentry', 'checkpoint', 'boundary']);\r\nconst safeColor = c => typeof c === 'string' && /^(#[0-9a-f]{3,8}|[a-z]{3,20}|(rgb|hsl)a?\\([\\d\\s.,%/-]{3,40}\\))$/i.test(c.trim()) ? c.trim() : DEFAULT_COLOR;\r\n\r\nexport const CSS = `\r\n.fx{position:absolute;inset:0;z-index:60;overflow:hidden;pointer-events:auto;touch-action:none;font:700 14px/1.35 ui-monospace,Menlo,Consolas,monospace;color:#f4fbff;--c:#73dbcb;--rv:280ms}\r\n.fx[data-phase=pre]>*{visibility:hidden}\r\n.fx *{box-sizing:border-box}\r\n.fx-wash{position:absolute;inset:0;background:#02060c;opacity:0}\r\n.fx[data-kind=warp] .fx-wash{opacity:.55;background:radial-gradient(circle,#00131c 0,#02060c 75%)}\r\n.fx[data-kind=reentry] .fx-wash{opacity:.18;background:#3a0d02}\r\n.fx[data-phase=cover] .fx-wash,.fx[data-phase=hold] .fx-wash,.fx[data-phase=error] .fx-wash,.fx[data-phase=reveal] .fx-wash{opacity:1;background:#02060c;transition:opacity 280ms steps(4)}\r\n.fx[data-kind=reentry][data-phase=cover] .fx-wash,.fx[data-kind=reentry][data-phase=hold] .fx-wash,.fx[data-kind=reentry][data-phase=error] .fx-wash,.fx[data-kind=reentry][data-phase=reveal] .fx-wash{background:#f1d9c4}\r\n.fx[data-phase=reveal]{opacity:0;transition:opacity var(--rv) steps(5)}\r\n.fx-ring{position:absolute;left:50%;top:50%;width:26%;aspect-ratio:1;margin:-13% 0 0 -13%;border:6px solid var(--c);animation:fx-ring 1.2s steps(10) infinite;animation-delay:calc(var(--i) * -.15s)}\r\n.fx-ring:nth-child(2n){border-color:#fff}.fx-ring:nth-child(3n){border-color:#ff5bd6}\r\n.fx-core{position:absolute;left:50%;top:50%;width:14px;height:14px;margin:-7px;background:#fff;box-shadow:0 0 0 7px var(--c);animation:fx-core .3s steps(2) infinite}\r\n@keyframes fx-ring{from{transform:scale(.15);opacity:0}15%{opacity:1}to{transform:scale(3.4);opacity:0}}\r\n@keyframes fx-core{50%{transform:scale(1.6)}}\r\n.fx-rim{position:absolute;inset:0;box-shadow:inset 0 0 0 8px #ffd34a,inset 0 0 0 20px #ff8a1f,inset 0 0 0 34px #e8420f,inset 0 0 0 52px #8c1a0a88;animation:fx-flick .24s steps(2) infinite}\r\n@keyframes fx-flick{50%{box-shadow:inset 0 0 0 12px #fff0a0,inset 0 0 0 26px #ff9a2c,inset 0 0 0 42px #e8420f,inset 0 0 0 64px #8c1a0a88}}\r\n.fx-streak{position:absolute;top:0;width:8px;height:56px;background:linear-gradient(#fff2a0,#ff7a1a);animation:fx-streak .5s steps(6) infinite;animation-delay:calc(var(--i) * -.07s)}\r\n@keyframes fx-streak{from{transform:translateY(-120%)}to{transform:translateY(calc(var(--h,100vh) + 100%))}}\r\n.fx-cloud{position:absolute;left:var(--x);top:100%;animation:fx-cloud calc(var(--d) * .55) steps(14) both;animation-delay:calc(var(--d) * .4 + var(--i) * var(--d) * .04)}\r\n@keyframes fx-cloud{from{transform:translateY(0)}to{transform:translateY(-130vh)}}\r\n.fx-lights{position:absolute;inset:0;background:linear-gradient(90deg,#ffd52a66 50%,#a64dff66 50%);animation:fx-siren .5s steps(1) infinite}\r\n@keyframes fx-siren{50%{background:linear-gradient(90deg,#a64dff66 50%,#ffd52a66 50%)}}\r\n.fx-ufo{position:absolute;left:50%;top:7%;margin-left:-34px;animation:fx-ufo .8s steps(8) both,fx-bob 1s steps(2) .8s infinite}\r\n@keyframes fx-ufo{from{transform:translateY(-200px)}to{transform:translateY(0)}}\r\n@keyframes fx-bob{50%{transform:translateY(4px)}}\r\n.fx-pair{position:absolute;left:0;right:0;top:30%;display:flex;justify-content:center;gap:28px}\r\n.fx-who{display:grid;justify-items:center;gap:4px;font-size:10px;letter-spacing:.1em}\r\n.fx-panel{position:absolute;left:50%;top:calc(50% - 24px);width:min(calc(100% - 48px),420px);max-height:max(100px,calc(100% - 188px));transform:translate(-50%,-50%);display:grid;gap:8px;padding:10px 12px;overflow:auto;overscroll-behavior:contain;touch-action:pan-y;background:linear-gradient(155deg,#28363bf5,#060b14f5);border:4px solid var(--c);box-shadow:inset 0 1px #d6e2e366,5px 5px 0 #000a}\r\n.fx-panel h2{margin:0;font:inherit;color:var(--c);letter-spacing:.08em}.fx-panel p{margin:0}\r\n.fx-btns{display:flex;flex-wrap:wrap;gap:8px}\r\n.fx-btn{min-height:44px;min-width:44px;flex:1 1 auto;padding:8px 12px;font:inherit;color:#04121a;background:var(--c);border:0;box-shadow:0 4px #0006;cursor:pointer;touch-action:manipulation}\r\n.fx-btn:focus-visible{outline:3px solid #fff;outline-offset:2px}.fx-panel[hidden]{display:none}\r\n.fx[data-reduced] *{animation:none!important;transition:none!important}\r\n.fx[data-reduced] .fx-ring{transform:scale(calc(.4 + var(--i) * .45));opacity:.8}\r\n.fx[data-reduced] .fx-lights{background:#4a3a6a99}\r\n.fx[data-reduced] .fx-streak,.fx[data-reduced] .fx-cloud{display:none}\r\n.fx[data-reduced] .fx-rim{box-shadow:inset 0 0 0 14px #ff8a1f,inset 0 0 0 34px #8c1a0a88}`;\r\nconst PAL = {d: '#7fe8ff', w: '#fff', g: '#aab4c4', G: '#5d6679', r: '#ff3b3b', b: '#4b7bff', y: '#ffd52a', h: '#8c4fd1', H: '#2fb8c9', f: '#f1c9a5', e: '#201428', m: '#c0504d', c: '#c7d2df', s: '#ffd52a', S: '#6f8cff'};\r\nconst UFO = ['......ddddd......', '.....dwdddddd....', '..gggggggggggggg.', '.gGgGgGgGgGgGgGg.', 'gGgrgGgbgGgygGgGg', '.GGGGGGGGGGGGGGG.', '...GG.......GG...'];\r\nconst CLOUD = ['...ww.......', '.wwwwww.ww.', 'wwwwwwwwwww', 'wwwwwwwwwww', 'cccccccccc.'];\r\nconst BUST = ['..hhhh..', '.hhhhhh.', '.hffffh.', '.hefefh.', '.hffffh.', '..ffmf..', '.ssssss.', 'ssssssss'];\r\n\r\nexport function createFlightEffects(host, {color = DEFAULT_COLOR, reduced = false, timing = {}, timers = {}} = {}) {\r\n  if (!host || typeof host.appendChild !== 'function' || !host.ownerDocument) throw new TypeError('createFlightEffects needs a stage element.');\r\n  const doc = host.ownerDocument, t = {...TIMING, ...timing}, tint = safeColor(color);\r\n  const setT = timers.setTimeout || ((f, ms) => globalThis.setTimeout(f, ms)), clearT = timers.clearTimeout || (id => globalThis.clearTimeout(id));\r\n  let active = null, disposed = false;\r\n\r\n  const el = (tag, cls, attrs, text) => {\r\n    const n = doc.createElement(tag);\r\n    if (cls) n.setAttribute('class', cls);\r\n    for (const k in attrs) n.setAttribute(k, attrs[k]);\r\n    if (text != null) n.textContent = text;\r\n    return n;\r\n  };\r\n  const sprite = (rows, scale, pal) => { // pixel art as one element: a box-shadow per pixel\r\n    const shadows = [];\r\n    rows.forEach((row, y) => [...row].forEach((ch, x) => ch !== '.' && shadows.push(`${x * scale}px ${y * scale}px 0 0 ${pal[ch]}`)));\r\n    const box = el('div', '', {style: `width:${rows[0].length * scale}px;height:${rows.length * scale}px`, 'aria-hidden': 'true'});\r\n    box.appendChild(el('div', '', {style: `width:${scale}px;height:${scale}px;box-shadow:${shadows.join(',')}`}));\r\n    return box;\r\n  };\r\n  const abortError = (message, extra) => {\r\n    const e = typeof DOMException === 'function' ? new DOMException(message, 'AbortError') : Object.assign(new Error(message), {name: 'AbortError'});\r\n    return Object.assign(e, extra);\r\n  };\r\n\r\n  function start(kind, o) {\r\n    const timersLive = new Set(), listeners = [], prevFocus = doc.activeElement;\r\n    let done = false, cancel, covered = false;\r\n    const cancelled = new Promise((_, rej) => {cancel = e => {if (!done) rej(e);};});\r\n    cancelled.catch(() => {});\r\n    const wait = ms => Promise.race([new Promise(r => {const id = setT(() => {timersLive.delete(id); r();}, Math.max(0, ms)); timersLive.add(id);}), cancelled]);\r\n    const on = (n, type, fn) => {n.addEventListener(type, fn); listeners.push([n, type, fn]);};\r\n    const press = n => Promise.race([new Promise(r => on(n, 'click', () => r())), cancelled]);\r\n\r\n    // ---- DOM\r\n    const root = el('div', 'fx', {'data-kind': kind, 'data-phase': 'pre', style: `--c:${tint};--d:${t[kind] ?? 0}ms`, 'aria-live': 'off'});\r\n    if (reduced) root.setAttribute('data-reduced', '1');\r\n    const css = el('style'); css.textContent = CSS; root.appendChild(css);\r\n    root.appendChild(el('div', 'fx-wash', {'aria-hidden': 'true'}));\r\n    const deco = (cls, extra) => root.appendChild(el('div', cls, {'aria-hidden': 'true', ...extra}));\r\n    if (kind === 'warp') {\r\n      for (let i = 0; i < 8; i++) deco('fx-ring', {style: `--i:${i}`});\r\n      deco('fx-core');\r\n    } else if (kind === 'reentry') {\r\n      deco('fx-rim');\r\n      for (let i = 0; i < 20; i++) deco('fx-streak', {style: `--i:${i};--h:100vh;${i % 2 ? 'right' : 'left'}:${1 + (i * 5) % 7}%;height:${34 + (i * 17) % 40}px`});\r\n      for (let i = 0; i < 7; i++) {\r\n        const cloud = root.appendChild(el('div', 'fx-cloud', {'aria-hidden': 'true', style: `--i:${i};--x:${(i * 29) % 70 - 8}%`}));\r\n        cloud.appendChild(sprite(CLOUD, 14, PAL));\r\n      }\r\n    } else {\r\n      deco('fx-lights');\r\n      const ufo = root.appendChild(el('div', 'fx-ufo')); ufo.appendChild(sprite(UFO, 4, PAL));\r\n      if (kind === 'boundary') {\r\n        const pair = root.appendChild(el('div', 'fx-pair'));\r\n        for (const [name, hair, shirt] of [['MOMMY', PAL.h, PAL.s], ['ALICIA', PAL.H, PAL.S]]) {\r\n          const who = pair.appendChild(el('div', 'fx-who'));\r\n          who.appendChild(sprite(BUST, 6, {...PAL, h: hair, s: shirt})); who.appendChild(el('span', '', {}, name));\r\n        }\r\n      }\r\n    }\r\n    const panel = root.appendChild(el('div', 'fx-panel', kind === 'checkpoint' ? {role: 'dialog', 'aria-label': 'MOM police checkpoint'} : {}));\r\n    panel.setAttribute('hidden', '');\r\n    const head = panel.appendChild(el('h2', '', {}, kind === 'checkpoint' ? 'MOM POLICE' : kind === 'boundary' ? 'MOMMY & ALICIA' : 'FLIGHT SYSTEMS'));\r\n    const status = panel.appendChild(el('p', '', {role: 'status', 'aria-live': 'polite'}));\r\n    const btns = panel.appendChild(el('div', 'fx-btns'));\r\n    const button = (label, extra) => btns.appendChild(el('button', 'fx-btn', {type: 'button', ...extra}, label));\r\n    const say = text => {status.textContent = text; panel.removeAttribute('hidden');};\r\n    const phase = p => root.setAttribute('data-phase', p);\r\n    const focus = n => n.focus && n.focus({preventScroll: true});\r\n    const clearButtons = () => {while (btns.firstChild) btns.removeChild(btns.firstChild);};\r\n    const returnButton = () => {const b = button('Return', {'data-fx': 'return'}); on(b, 'click', () => cancel(abortError('Returned by the player.', {userReturn: true}))); return b;};\r\n\r\n    const track = p => {\r\n      const s = {state: 'pending', err: undefined};\r\n      s.p = Promise.resolve(p).then(() => {s.state = 'ok'; return s;}, e => {s.state = 'fail'; s.err = e; return s;});\r\n      return s;\r\n    };\r\n    const cleanup = () => {\r\n      done = true;\r\n      for (const id of timersLive) clearT(id);\r\n      timersLive.clear();\r\n      for (const [n, type, fn] of listeners) n.removeEventListener(type, fn);\r\n      listeners.length = 0;\r\n      o.signal?.removeEventListener('abort', onAbort);\r\n      if (root.parentNode) root.parentNode.removeChild(root);\r\n      if (prevFocus && prevFocus.isConnected !== false && prevFocus.focus) prevFocus.focus({preventScroll: true});\r\n    };\r\n    const onAbort = () => cancel(abortError('Flight effect aborted.', {cause: o.signal.reason}));\r\n    o.signal?.addEventListener('abort', onAbort);\r\n\r\n    let s = track(o.ready); // observed from frame zero so an early rejection is never unhandled\r\n    async function flow() {\r\n      host.appendChild(root); // phase \"pre\": nothing painted yet\r\n      await wait(t.beat);\r\n      phase('run');\r\n      let result = {kind};\r\n      if (kind === 'warp' || kind === 'reentry') {\r\n        await wait(t[kind]);\r\n      } else if (kind === 'boundary') {\r\n        say('Leaving permitted territory. Turning you back toward Las Vegas.');\r\n        const ok = button('OK', {'data-fx': 'ok'});\r\n        const tapped = press(ok).then(() => true);\r\n        result.skipped = await Promise.race([wait(t.boundary).then(() => false), tapped]);\r\n      } else {\r\n        await wait(t.ufo);\r\n        say('Sector checkpoint. State the purpose of your travel.');\r\n        const picks = CHOICES.map(c => {const b = button(c.label, {'data-choice': c.id}); return press(b).then(() => c);});\r\n        focus(btns.firstChild);\r\n        const choice = await Promise.race(picks);\r\n        clearButtons();\r\n        result.choice = choice.id;\r\n        try {\r\n          const r = o.onChoice && o.onChoice(choice.id);\r\n          if (r && typeof r.catch === 'function') r.catch(() => {});\r\n        } catch { /* the parent's handler must not stall the encounter */ }\r\n        say(choice.reply);\r\n        await wait(t.reply);\r\n      }\r\n      clearButtons();\r\n      phase('cover');\r\n      await wait(reduced ? 0 : t.cover);\r\n      covered = true;\r\n      try {o.onCover && o.onCover();} catch { /* parent swap errors surface through ready */ }\r\n      result.covered = covered;\r\n\r\n      const holdSlow = () => {\r\n        phase('hold'); head.textContent = 'FLIGHT SYSTEMS'; say('Loading destination...'); clearButtons();\r\n        const hint = setT(() => {timersLive.delete(hint); if (s.state === 'pending') say('Still loading. You can return to where you were.');}, t.slow);\r\n        timersLive.add(hint);\r\n        focus(returnButton());\r\n      };\r\n      if (s.state === 'pending') holdSlow();\r\n      await Promise.race([s.p, cancelled]);\r\n      while (s.state === 'fail') {\r\n        phase('error'); clearButtons(); head.textContent = 'FLIGHT SYSTEMS';\r\n        say(o.retry ? 'The destination failed to load. Retry or return.' : 'The destination failed to load. Return to where you were.');\r\n        let retry;\r\n        if (o.retry) retry = button('Retry', {'data-fx': 'retry'});\r\n        const back = returnButton();\r\n        focus(retry || back);\r\n        if (retry) {\r\n          await press(retry);\r\n          s = track(Promise.resolve().then(() => o.retry()));\r\n          holdSlow();\r\n          await Promise.race([s.p, cancelled]);\r\n        } else {\r\n          await cancelled; // only Return (or abort/dispose) leaves this state\r\n        }\r\n      }\r\n      clearButtons();\r\n      phase('reveal');\r\n      await wait(reduced ? 0 : t.reveal);\r\n      return result;\r\n    }\r\n    const promise = flow().finally(cleanup);\r\n    return {promise, cancel};\r\n  }\r\n\r\n  return {\r\n    play(kind, o = {}) {\r\n      if (disposed) return Promise.reject(abortError('Flight effects were disposed.'));\r\n      if (!KINDS.has(kind)) return Promise.reject(new TypeError(`Unknown flight effect \"${kind}\".`));\r\n      if (o.signal?.aborted) return Promise.reject(abortError('Flight effect aborted before it started.', {cause: o.signal.reason}));\r\n      if (active) active.cancel(abortError('Superseded by another flight effect.'));\r\n      const run = start(kind, o);\r\n      active = run;\r\n      const clear = () => {if (active === run) active = null;};\r\n      run.promise.then(clear, clear);\r\n      return run.promise;\r\n    },\r\n    dispose() {\r\n      disposed = true;\r\n      if (active) active.cancel(abortError('Flight effects were disposed.'));\r\n      active = null;\r\n    },\r\n  };\r\n}\r\n","modules/coach-flight/travel.mjs":"/** Private, local travel schema 1. No base imports, GPS, network or implicit storage.\r\n * Coordinates are virtual travel targets, NEVER PhysicalPresence evidence.\r\n * Source/id pairs identify places; owner scopes the whole store. Friend references\r\n * must be re-resolved with current authorization by the controller on every entry.\r\n */\r\nconst SOURCES = new Set(['place', 'address', 'home', 'friend', 'npc', 'site', 'pin', 'visited', 'landmark']);\r\nconst SLOT_COUNT = 10, HISTORY_LIMIT = 32;\r\nconst object = value => !!value && typeof value === 'object' && !Array.isArray(value);\r\nconst copy = value => value === null ? null : {...value};\r\nfunction fail(code, message) {const error = new Error(message); error.code = code; throw error;}\r\nfunction text(value, name, max = 160) {\r\n  if (typeof value !== 'string' || !value.trim() || value.trim().length > max)\r\n    fail('INVALID_PLACE', `Expected nonempty ${name} (at most ${max} characters).`);\r\n  return value.trim();\r\n}\r\nconst identity = place => JSON.stringify([place.source, place.id]);\r\n\r\n/** Canonical {schema:1,source,id,label,generationVersion,provenance:'virtual-travel',\r\n * lat,lng}; friend records instead carry friendRef and optional baseRef, NO coords.\r\n * source defaults to 'place'; latitude/longitude aliases support address adapters.\r\n * NPC/site IDs must be persistent IDs. Coordinate places may omit id; one is derived.\r\n * Extra input fields (including coords, home, GPS accuracy and timestamps) are dropped.\r\n * Invalid input throws with code INVALID_PLACE. No silent coordinate coercion/fallback.\r\n */\r\nexport function normalizePlace(value) {\r\n  if (!object(value)) fail('INVALID_PLACE', 'Expected a place object.');\r\n  if (value.schema !== undefined && value.schema !== 1) fail('INVALID_PLACE', 'Unsupported place schema.');\r\n  const source = value.source ?? 'place';\r\n  if (!SOURCES.has(source)) fail('INVALID_PLACE', 'Unknown place source.');\r\n  const generationVersion = value.generationVersion ?? 1;\r\n  if (!Number.isSafeInteger(generationVersion) || generationVersion < 1)\r\n    fail('INVALID_PLACE', 'Invalid generation version.');\r\n  let location, id;\r\n  if (source === 'friend') {\r\n    const friendRef = text(value.friendRef, 'friend reference');\r\n    location = {friendRef};\r\n    if (value.baseRef !== undefined) location.baseRef = text(value.baseRef, 'base reference');\r\n    id = text(value.id ?? JSON.stringify([friendRef, location.baseRef ?? null]), 'place ID');\r\n  } else {\r\n    const lat = value.lat ?? value.latitude, lng = value.lng ?? value.longitude;\r\n    if (!Number.isFinite(lat) || !Number.isFinite(lng) || Math.abs(lat) > 90 || Math.abs(lng) > 180)\r\n      fail('INVALID_PLACE', 'Expected valid numeric lat/lng.');\r\n    location = {lat, lng};\r\n    if (['npc', 'site'].includes(source) && value.id === undefined)\r\n      fail('INVALID_PLACE', 'NPC and site places need a persistent ID.');\r\n    id = text(value.id ?? `${lat},${lng}`, 'place ID');\r\n  }\r\n  return {schema: 1, source, id, label: text(value.label ?? (source === 'friend' ? 'Friend base' : id), 'label', 120),\r\n    generationVersion, provenance: 'virtual-travel', ...location};\r\n}\r\n\r\n/** list() returns exactly ten entries (null = empty), indexed 0..9.\r\n * save(place) uses the first empty slot; full throws SLOTS_FULL without changing data.\r\n * save(place,slot) explicitly replaces that slot, returns its index. No implicit dedup\r\n * or eviction. Home is supplied separately by the base adapter, not a favourite slot.\r\n * remove(slot) returns whether occupied; rename(slot,label) returns updated place.\r\n * visit(place) returns place and maintains bounded MRU history independently of slots.\r\n * clear() clears ONLY this owner's travel key. Outputs are detached copies.\r\n * storage implements getItem/setItem, optionally removeItem (localStorage shape).\r\n * Without storage this is in-memory. Read/JSON/schema failures start empty in memory\r\n * and do not write. Write failure throws STORAGE_WRITE_FAILED and preserves memory.\r\n * No account sync or multi-tab merge is claimed; controller recreates on owner change.\r\n */\r\nexport function createTravelStore({storage, owner = 'guest'} = {}) {\r\n  const ownerId = text(owner, 'owner', 200);\r\n  const key = `myr5:flight:travel:v1:${encodeURIComponent(ownerId)}`;\r\n  const empty = () => ({schema: 1, owner: ownerId, slots: Array(SLOT_COUNT).fill(null), history: []});\r\n  let data = empty();\r\n  try {\r\n    const raw = storage?.getItem(key);\r\n    if (raw != null) {\r\n      if (typeof raw !== 'string' || raw.length > 100000) throw new Error('Invalid travel payload.');\r\n      const parsed = JSON.parse(raw);\r\n      if (!object(parsed) || parsed.schema !== 1 || parsed.owner !== ownerId\r\n        || !Array.isArray(parsed.slots) || parsed.slots.length !== SLOT_COUNT\r\n        || !Array.isArray(parsed.history) || parsed.history.length > HISTORY_LIMIT)\r\n        throw new Error('Invalid travel schema.');\r\n      const slots = parsed.slots.map(p => p === null ? null : normalizePlace(p));\r\n      if (slots.some(p => p?.source === 'home')) throw new Error('Home does not consume a slot.');\r\n      data = {schema: 1, owner: ownerId, slots, history: parsed.history.map(normalizePlace)};\r\n    }\r\n  } catch { /* Separate corrupt travel cannot reset, read or write base state. */ }\r\n  function commit(next, remove = false) {\r\n    try {\r\n      if (storage) {\r\n        if (remove && typeof storage.removeItem === 'function') storage.removeItem(key);\r\n        else storage.setItem(key, JSON.stringify(next));\r\n      }\r\n    } catch {fail('STORAGE_WRITE_FAILED', 'Travel changes could not be saved.');}\r\n    data = next;\r\n  }\r\n  function slotIndex(slot) {\r\n    if (!Number.isInteger(slot) || slot < 0 || slot >= SLOT_COUNT)\r\n      fail('INVALID_SLOT', 'Choose a favourite slot from 0 to 9.');\r\n    return slot;\r\n  }\r\n  return Object.freeze({\r\n    list: () => data.slots.map(copy),\r\n    history: () => data.history.map(copy),\r\n    save(place, slot) {\r\n      const normalized = normalizePlace(place);\r\n      if (normalized.source === 'home') fail('HOME_SEPARATE', 'Home is available separately from favourites.');\r\n      const index = slot === undefined ? data.slots.indexOf(null) : slotIndex(slot);\r\n      if (index < 0) fail('SLOTS_FULL', 'All ten favourites are occupied. Choose a slot to replace.');\r\n      const slots = data.slots.slice(); slots[index] = normalized;\r\n      commit({...data, slots}); return index;\r\n    },\r\n    remove(slot) {\r\n      const index = slotIndex(slot), occupied = data.slots[index] !== null;\r\n      if (occupied) {const slots = data.slots.slice(); slots[index] = null; commit({...data, slots});}\r\n      return occupied;\r\n    },\r\n    rename(slot, label) {\r\n      const index = slotIndex(slot);\r\n      if (!data.slots[index]) fail('EMPTY_SLOT', 'Choose an occupied favourite.');\r\n      const place = normalizePlace({...data.slots[index], label});\r\n      const slots = data.slots.slice(); slots[index] = place;\r\n      commit({...data, slots}); return copy(place);\r\n    },\r\n    visit(place) {\r\n      const normalized = normalizePlace(place), id = identity(normalized);\r\n      const history = [normalized, ...data.history.filter(p => identity(p) !== id)].slice(0, HISTORY_LIMIT);\r\n      commit({...data, history}); return copy(normalized);\r\n    },\r\n    clear() {commit(empty(), true);}\r\n  });\r\n}\r\n\r\n/** No geocoding/network: case/accent-insensitive all-word match over caller-provided\r\n * label/id/source. Empty query returns []. Up to 20 results, sites before history,\r\n * deduplicated by source/id. Invalid records skipped; at most 512 per input examined.\r\n * Address results retain normalized numeric lat/lng; unconfirmed text is never a pin.\r\n */\r\nexport function searchLocalPlaces(query, {sites = [], history = []} = {}) {\r\n  if (typeof query !== 'string' || !query.trim() || query.length > 200) return [];\r\n  const fold = s => s.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\r\n  const words = fold(query.trim()).split(/\\s+/), results = [], seen = new Set();\r\n  for (const list of [sites, history]) {\r\n    if (!Array.isArray(list)) continue;\r\n    for (const raw of list.slice(0, 512)) {\r\n      let place;\r\n      try {place = normalizePlace(raw);} catch {continue;}\r\n      const id = identity(place), haystack = fold(`${place.label} ${place.id} ${place.source}`);\r\n      if (!seen.has(id) && words.every(word => haystack.includes(word))) {\r\n        seen.add(id); results.push(place);\r\n        if (results.length === 20) return results;\r\n      }\r\n    }\r\n  }\r\n  return results;\r\n}\r\n","modules/coach-flight/view.mjs":"import {claimGraphics,releaseGraphics} from '../graphics-lifecycle.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {createCockpit} from './cockpit.mjs';\r\nimport {shipIdentity} from './boarding.mjs';\r\nimport {loadCityScene,cityCovered} from './city-scene.mjs';\r\nimport {VEGAS,stepCruise,localToGeo,geoToLocal,headingTo,sectorAt,createFlightMachine} from './core.mjs';\r\nimport {createTravelStore,normalizePlace} from './travel.mjs';\r\nimport {createSpaceScene,SPACE_START} from './space-scene.mjs';\r\nimport {createFlightEffects} from './transitions.mjs';\r\nimport {createEcology,planEcology} from './ecology.mjs';\r\nimport {navigationMenu} from './navigation.mjs';\r\nimport {createFlightLook} from './look.mjs';\r\nimport {createFlightSurvey} from './survey.mjs';\r\nimport {flightAccountStamp,flightAccountChanged} from './account.mjs';\r\nimport {STATION,stationPort,stationRange,sweptCapture,dockPose,undockPose,canDock,clearHull,stationCruise} from './station.mjs';\r\nimport {createStationHud} from './station-hud.mjs';\r\nimport {createPerfOverlay} from './perf-overlay.mjs';\r\nimport {createCockpit3D} from './cockpit-3d.mjs';\r\nimport {useFogLaw} from '../coach-world-rts/fog-law.mjs';\r\nimport {createAlienSky} from '../coach-world-rts/sky-alien.mjs';\r\n// V1: city sky and fog share one colour (DFO: the fog colour IS the sky's horizon), and the shared fog law closes to it over the rim\r\n// V5: valley haze from the eye (DFO hazeAt): floor 120 m, 260 m falloff, clear inside 400 m of the cockpit.\r\n// V6: the far ring reaches ~25 km (DFO hazes its ring to ~28 km), so the rim closes at 24 km and the camera/sky sit past it.\r\nconst CITY_FAR=26000,CITY_SKY=0x89769b,CITY_FOG={hold:.85,rim:[14000,24000],haze:{extinction:1/9000,floor:120,falloff:260,clear:400}};\r\n// The default Vegas pin lies on a sector corner. Allow some travel before an encounter;\r\n// checkpointOrigin is geographic and stays stable through local map-stream rebases.\r\nconst PORT=stationPort(),CHECKPOINT_BUFFER=80;\r\nexport const flightEnabled=win=>new URLSearchParams(win?.location?.search||'').get('flight')!=='0';\r\nconst abortError=()=>new DOMException('Flight cancelled','AbortError');\r\nconst wrap=a=>Math.atan2(Math.sin(a),Math.cos(a));\r\nconst clamp1=v=>Math.max(-1,Math.min(1,Number.isFinite(v)?v:0));\r\n// Suggested shell seam (shell owner may replace the contract): pure phase from facts. Only live free flight is 'active'.\r\nexport function flightPhase({closed=false,error=false,menu=false,hidden=false,loading=false,closeReason='exit',loadReason='loading'}={}){\r\n if(closed)return {phase:'closed',reason:closeReason};if(error)return {phase:'error',reason:'error'};if(menu)return {phase:'paused',reason:'menu'};if(hidden)return {phase:'paused',reason:'hidden'};if(loading)return {phase:'loading',reason:loadReason};return {phase:'active',reason:'flying'};\r\n}\r\nlet flightInstances=0;\r\nexport function mountFlight({host,getBase,pin=VEGAS,owner='guest',getFriends,getZones,onExit=()=>{},signal,deps={}}){\r\n const doc=host.ownerDocument,win=doc.defaultView??globalThis,base=getBase(),identity=shipIdentity(base),home=cityCovered(pin)?{...pin}:{...VEGAS};\r\n const ui=createCockpit(host,{color:FACTION_BY_ID[identity.faction]?.color,faction:identity.faction,ship:identity.id,onAction:action}),ac=new AbortController(),machine=createFlightMachine();\r\n let storage;try{storage=win.localStorage;}catch{}const store=createTravelStore({storage,owner}),reduced=win.matchMedia?.('(prefers-reduced-motion: reduce)').matches??false;\r\n const effects=createFlightEffects(ui.stage,{color:FACTION_BY_ID[identity.faction]?.color,reduced});\r\n let T,renderer,scene,camera,sky,cockpit3d,claim,city,ecology,space,nav,look,survey,stationHud,perf,raf=0,disposed=false,busy=true,mode='city',last=0,turnUntil=0,spaceCooldown=0,launchAvail=false,sequence=0,origin={...home},lastSector=sectorAt(home).id,checkpointOrigin={...home},streaming=false,streamRetry=0;\r\n let nose=0,bank=0,pose={x:0,y:4,z:0,yaw:0,speed:0},pitch=-.95,orbitAngle=0,orbitLat=.35,launchAt=0,launchElapsed=0,readyResolve,turnHeading=0,starting=false;\r\n // MOM Station: captured = swept arming latch (DOCK shown); docking = ease in progress; docked = card open, input locked.\r\n let captured=false,docked=false,docking=null,dockButton=null,range=null;const prevSpace={x:0,y:0,z:0};\r\n const ready=new Promise(r=>{readyResolve=r;});\r\n // Phase seam: coalesced to one microtask per tick, deduped on phase+mode, delivered on document; shell trusts instanceId only.\r\n const instanceId=`flight-${Date.now().toString(36)}-${(++flightInstances).toString(36)}-${Math.random().toString(36).slice(2,8)}`;let phaseKey='',phaseNow='loading',phaseQueued=false,armed=false,closeReason='exit';ui.root.dataset.flightInstance=instanceId;ui.root.dataset.flightPhase='loading';\r\n function syncPhase(force){if(phaseQueued&&!force)return;if(force){flushPhase();return;}phaseQueued=true;queueMicrotask(flushPhase);}\r\n function flushPhase(){phaseQueued=false;const loadReason=starting?'starting':launchAt?'takeoff':docking?'docking':String(machine.state||'busy');\r\n  const {phase,reason}=flightPhase({closed:disposed,error:ui.root.dataset.error==='1',menu:!ui.menu.hidden,hidden:!!doc.hidden,loading:starting||busy||!!launchAt||!!docking||!renderer,closeReason,loadReason});\r\n  phaseNow=phase;ui.root.dataset.flightPhase=phase;ui.root.dataset.flightInstance=instanceId;if(!armed)return;const key=`${phase}|${mode}`;if(key===phaseKey)return;phaseKey=key;\r\n  doc.dispatchEvent(new win.CustomEvent('myr5:flight-phase',{detail:{source:'coach-flight',instanceId,phase,mode,reason}}));}\r\n const alive=()=>!disposed&&!ac.signal.aborted;\r\n const state=()=>({instanceId,flightPhase:phaseNow,mode,busy,phase:machine.state,pose:{...pose},origin:{...origin},location:mode==='city'?localToGeo(pose,origin):null,stats:renderer?{calls:renderer.info.render.calls,tris:renderer.info.render.triangles}:null,captured,docked});\r\n function complete(e,data={}){return machine.send(e,{token:machine.token,ready:true,...data});}\r\n function message(t){if(alive())ui.message(t);}\r\n function current(){return mode==='city'?{...localToGeo(pose,origin),label:'Flight position',source:'pin'}:{...home,label:'Vegas approach',source:'pin'};}\r\n function lock(v){busy=v;ui.lock(v);if(v)survey?.hide();syncPhase();}\r\n function dispose(info,why){if(disposed)return;disposed=true;closeReason=why||(info?.graphicsInterrupted?'context-loss':info?.landingPin?'land':'exit');armed=true;syncPhase(true);sequence++;ac.abort();cancelAnimationFrame(raf);nav?.dispose();survey?.dispose();stationHud?.dispose();perf?.dispose();docking=null;look?.dispose();effects.dispose();cockpit3d?.dispose();cockpit3d=null;city?.dispose();ecology?.dispose();space?.dispose();sky?.dispose();ui.dispose();\r\n  if(renderer){const rd=renderer;renderer=null;void releaseGraphics(ui.canvas,rd.getContext(),()=>{rd.dispose();rd.forceContextLoss();});}claim?.();signal?.removeEventListener('abort',externalExit);readyResolve(false);onExit(info);\r\n }\r\n function externalExit(){dispose(undefined,'external');}signal?.addEventListener('abort',externalExit,{once:true});\r\n const accountAtStart=flightAccountStamp(win);for(const e of ['myr5:account-cleared','myr5:auth-transition','myr5:account-ready'])win.addEventListener(e,event=>{if(flightAccountChanged(accountAtStart,win,event.type))externalExit();},{signal:ac.signal});\r\n doc.addEventListener('visibilitychange',()=>{ui.reset();perf?.reset?.();last=0;syncPhase();},{signal:ac.signal});\r\n function render(){if(renderer&&scene&&camera){camera.updateMatrixWorld();if(sky){const on=mode==='city';if(on){sky.group.position.copy(camera.position);sky.update(performance.now()/1000,90);const h=sky.colors.horizon;scene.fog?.color.setRGB(h[0],h[1],h[2],T.SRGBColorSpace);}sky.group.visible=on;}const info=renderer.info;info.autoReset=false;info.reset();try{renderer.render(scene,camera);cockpit3d?.render(renderer);}finally{info.autoReset=true;}}}\r\n async function exteriorTransition(kind,work,commit){let cancelled=false,covered;const cover=new Promise(r=>{covered=r;});const prepared=Promise.all([Promise.resolve(work),cover]).then(async([value])=>{if(!alive()||cancelled)throw abortError();commit(value);render();await new Promise(r=>requestAnimationFrame(r));return true;});try{await effects.play(kind,{ready:prepared,onCover:()=>covered(),signal:ac.signal});}finally{cancelled=true;covered();}}\r\n async function cloudSwap(work,commit){const cover=doc.createElement('div');cover.style.cssText='position:absolute;inset:0;background:#9c9eac;z-index:3;display:grid;place-items:center;color:#17222d';cover.textContent='Charting sector…';ui.stage.append(cover);try{const data=await work;if(!alive())throw abortError();commit(data);render();await new Promise(r=>requestAnimationFrame(r));}finally{cover.remove();}}\r\n async function cityAt(point,{kind='warp',checkpoint=false}={}){\r\n  if((busy&&!(docked&&machine.state==='docked'))||!cityCovered(point))return;const seq=++sequence,oldMode=mode;lock(true);ui.closeMenu();message(kind==='reentry'?'Atmospheric entry · Las Vegas':'Preparing destination…');\r\n  let next=null,committed=false,cancelled=false;\r\n  try{\r\n   if(kind==='reentry')machine.send('reentry',{destination:point});else if(checkpoint)machine.send('checkpoint');else machine.send('jump',{destination:point,confirmed:true});\r\n   const work=loadCityScene(T,{origin:point,base,signal:ac.signal,fixture:deps.fixture}).then(async c=>{next=c;if(!alive()||cancelled||seq!==sequence){c.dispose();throw abortError();}if(point.source==='friend'){const fresh=(await getFriends?.())?.find(f=>f.publicId===point.friendRef);if(!fresh?.home?.consent||Math.abs(fresh.home.latitude-point.lat)>.0001||Math.abs(fresh.home.longitude-point.lng)>.0001)throw new Error('Friend sharing changed. Choose the destination again.');}if(cancelled||!alive()){c.dispose();throw abortError();}return c;});\r\n   const commit=c=>{if(!alive()||seq!==sequence)throw abortError();const old=city;city=c;scene.add(c.group);origin={...point};pose={x:0,y:c.floor,z:0,yaw:pose.yaw,speed:0};mode='city';if(space)space.group.visible=false;scene.background=new T.Color(CITY_SKY);scene.fog=new T.FogExp2(CITY_SKY,.00022);pitch=-.95;camera.position.set(pose.x,pose.y,pose.z);camera.rotation.set(pitch,pose.yaw,0);c.update(performance.now(),camera);refreshEcology();render();committed=true;complete('cruise');clearDock();old?.dispose();lastSector=sectorAt(point).id;checkpointOrigin={...point};};\r\n   if(kind==='stream')await cloudSwap(work,commit);else await exteriorTransition(kind,work,commit);\r\n   if(!alive()||seq!==sequence)return;store.visit(normalizePlace({...point,source:point.source||'pin',label:point.label||'Visited Vegas area'}));message('Las Vegas · flip ENGINE POWER + BOOST ARM, then BOOST');\r\n  }catch(e){cancelled=true;if(next&&!committed)next.dispose();if(alive()){if(!committed){mode=oldMode;machine.send('cancel');}message(e.name==='AbortError'?'Flight resumed.':e.message);}}\r\n  finally{if(alive()&&seq===sequence){if(docked)dockCard();else lock(false);}}\r\n }\r\n // R1-C11: marker tap = the usual warp fly-to, beside the coach (3 m), nose toward it, then the normal landing-pin exit.\r\n async function landBeside(p){const spot={...p.beside,label:`Beside ${p.label}`,source:'site',id:p.id};await cityAt(spot,{kind:'warp'});if(!alive()||mode!=='city'||busy||origin.lat!==spot.lat||origin.lng!==spot.lng)return;pose.yaw=headingTo(pose,geoToLocal(p,origin));camera.rotation.set(pitch,pose.yaw,0);render();machine.send('land');dispose({landingPin:spot,landBeside:{pin:{lat:spot.lat,lng:spot.lng},name:p.label,target:p.npc?{...p.npc}:{id:`coach-${p.id}`,name:p.label},coach:{lat:p.lat,lng:p.lng},heading:pose.yaw,toast:`Tap DROP to land beside ${p.label}`}});}\r\n function refreshEcology(){ecology?.dispose();ecology=null;survey?.dispose();survey=createFlightSurvey(T,{host:ui.stage,origin,onSelect:p=>{if(!busy&&mode==='city')void landBeside(p);}});{const s=survey,at=origin;void Promise.resolve(getZones?.(at)).then(dto=>{if(dto&&alive()&&survey===s)s.setZones(dto);}).catch(()=>{});}try{const plan=planEcology({origin,sectorId:sectorAt(origin).id,seed:5,radius:650,count:120,exclusions:[{x:0,z:0,radius:100},...(city?.exclusions||[])]});for(const p of [...plan.plants,...plan.rocks,...plan.signals])p.y=city?.heightAt(p.x,p.z)||0;ecology=createEcology(T,{plan});scene.add(ecology.group);}catch(e){console.warn('flight ecology',e);}}\r\n async function streamCity(){if(streaming||busy||mode!=='city')return;streaming=true;const seq=sequence,point=localToGeo(pose,origin);let next;try{next=await loadCityScene(T,{origin:point,base,signal:ac.signal,fixture:deps.fixture});if(!alive()||busy||mode!=='city'||sequence!==seq){next.dispose();return;}const here=localToGeo(pose,origin),p=geoToLocal(here,point),old=city;city=next;scene.add(next.group);origin={...point};pose.x=p.x;pose.z=p.z;pose.y=Math.max(pose.y,next.floor);refreshEcology();old.dispose();}catch(e){next?.dispose();streamRetry=performance.now()+5000;if(alive())message('Map streaming paused · holding within loaded territory');}finally{streaming=false;}}\r\n async function launchSpace(){if(busy||!['city','world'].includes(mode))return;const old=mode,seq=++sequence;let committed=false;lock(true);if(!machine.send('space').accepted){lock(false);return;}message('Space launch · hold course');\r\n  try{complete('portal');await exteriorTransition('warp',true,()=>{if(!alive())throw abortError();space.setMode('space');mode='space';space.group.visible=true;scene.background=new T.Color(0x030717);scene.fog=null;pose={...SPACE_START,yaw:headingTo(SPACE_START,STATION.pos),speed:0};pitch=Math.atan2(STATION.pos.y-SPACE_START.y,Math.hypot(STATION.pos.x-SPACE_START.x,STATION.pos.z-SPACE_START.z));clearDock();Object.assign(prevSpace,SPACE_START);camera.up.set(0,1,0);camera.position.set(pose.x,pose.y,pose.z);camera.rotation.set(pitch,pose.yaw,0);render();complete('ready');committed=true;city?.dispose();city=null;ecology?.dispose();ecology=null;});if(!alive()||seq!==sequence)return;ui.hint('Steer toward MOM Station - pull to adjust altitude');message('MOM Station ahead - approach the lit hangar');spaceCooldown=performance.now()+5000;}\r\n  catch(e){if(alive()){if(!committed){mode=old;machine.send('cancel');}message(e.message);}}finally{if(alive()&&seq===sequence)lock(false);}\r\n }\r\n async function enterWorld(){if(busy||mode!=='space')return;const seq=++sequence;let committed=false;lock(true);machine.send('reentry',{destination:home});\r\n  try{await exteriorTransition('reentry',true,()=>{if(!alive())throw abortError();space.setMode('space');mode='world';space.group.visible=true;orbitAngle=1.1;orbitLat=.35;scene.background=new T.Color(0x101b3d);scene.fog=null;worldCamera();render();complete('world');committed=true;});if(!alive()||seq!==sequence)return;message('WORLD CRUISE · only Vegas is landable');}\r\n  catch(e){if(alive()){if(!committed){mode='space';machine.send('cancel');}message(e.message);}}finally{if(alive()&&seq===sequence)lock(false);}\r\n }\r\n function worldCamera(){const radius=132,c=Math.cos(orbitLat),p=new T.Vector3(Math.cos(orbitAngle)*c,Math.sin(orbitLat),Math.sin(orbitAngle)*c);camera.position.copy(p).multiplyScalar(radius);camera.up.copy(p);const target=p.clone().multiplyScalar(94).add(new T.Vector3(-Math.sin(orbitAngle),0,Math.cos(orbitAngle)).multiplyScalar(20));camera.lookAt(target);}\r\n async function boundary(){if(busy||mode!=='space')return;const seq=++sequence;lock(true);message('Leaving permitted territory · Mommy and Alicia inbound');machine.send('warning');complete('intercept');\r\n  try{await effects.play('boundary',{signal:ac.signal});if(!alive()||seq!==sequence)return;complete('turn');const target=space.vegasDirection();turnHeading=headingTo(pose,{x:target.x*180,z:target.z*180});pose.speed=0;turnUntil=performance.now()+1800;message('Mommy & Alicia: Las Vegas is that way.');await new Promise((resolve,reject)=>{const tick=()=>{if(!alive()){reject(abortError());return;}if(performance.now()>=turnUntil){resolve();return;}requestAnimationFrame(tick);};tick();});if(!alive()||seq!==sequence)return;pose.yaw=turnHeading;complete('resume');spaceCooldown=performance.now()+10000;}\r\n  catch(e){if(alive())message(e.message);}finally{if(alive()&&seq===sequence)lock(false);}\r\n }\r\n async function jump(place){try{let target=place;if(place.source==='friend'){const fresh=(await getFriends?.())?.find(f=>f.publicId===place.friendRef);if(!fresh?.home?.consent)throw new Error('Friend location sharing is no longer available.');target={...place,lat:fresh.home.latitude,lng:fresh.home.longitude};}if(!cityCovered(target))throw new Error('Only Las Vegas destinations are available.');await cityAt(target,{kind:mode==='city'?'warp':'reentry'});}catch(e){message(e.message);if(docked)dockCard();else lock(false);}}\r\n function action(a){if(a==='exit'){dispose();return;}if(a==='retry'){void initialize();return;}if(a==='nav'&&docked&&machine.state==='docked'){dockNav();return;}if(busy)return;if(a==='boost'){if(!ui.launchReady?.()){message('Flip ENGINE POWER and BOOST ARM first');return;}void launchSpace();return;}if(a==='dock'){dock();return;}if(a==='look'){void look?.toggle().then(enabled=>{if(alive()){ui.root.querySelector('[data-action=\"look\"]').setAttribute('aria-pressed',String(enabled));message(enabled?'Phone look enabled · steer with the stick':'Drag the viewport to look around');}});return;}if(a==='nav'){lock(true);nav=navigationMenu(ui,{store,current,home,getFriends,signal:ac.signal,onJump:async p=>{lock(false);await jump(p);},onClose:()=>lock(false)});return;}\r\n  if(a==='home'){if(mode!=='city'){chooseReturn();return;}pose.yaw=headingTo(pose,geoToLocal(home,origin));message('Facing your Vegas approach beacon.');return;}\r\n  if(a==='land'){if(mode!=='city'){message('Vegas is the only landing area. Choose your return route.');chooseReturn();return;}machine.send('land');dispose({landingPin:current()});}\r\n }\r\n function chooseReturn(){ui.openMenu();const m=ui.menu;m.replaceChildren();for(const [label,fn]of [['Fiery re-entry to Las Vegas',()=>cityAt(home,{kind:'reentry'})],['Skim around the globe',enterWorld],['Keep flying',()=>{}]]){const b=doc.createElement('button');b.textContent=label;if(mode==='world'&&label==='Skim around the globe')b.disabled=true;b.onclick=()=>{ui.closeMenu();lock(false);void fn();};m.append(b);}lock(true);}\r\n function showDock(){if(!dockButton){dockButton=doc.createElement('button');dockButton.dataset.action='dock';dockButton.textContent='DOCK';dockButton.style.cssText='position:absolute;left:50%;transform:translateX(-50%);z-index:4;min-width:110px';dockButton.onclick=()=>{ui.reset();action('dock');};}ui.stage.append(dockButton);placeDock();}\r\n // Above the control band: measured from the DOM control rects, else a safe fixed band.\r\n function placeDock(){if(!dockButton)return;const r=ui.controlRects?.(),tops=[r?.yoke,r?.throttle].filter(Boolean).map(v=>v.y),h=ui.stage.getBoundingClientRect().height;dockButton.style.bottom=tops.length&&h?`${Math.max(8,Math.round(h-Math.min(...tops)+10))}px`:'calc(128px + env(safe-area-inset-bottom,0px))';}\r\n function hideDock(){dockButton?.remove();}\r\n function clearDock(){captured=false;docked=false;docking=null;delete ui.root.dataset.docked;hideDock();}\r\n // DOCK tap rechecks space/speed: a swept latch alone never docks. Reduced motion completes on the next visible frame.\r\n function dock(){if(busy||docked||docking||!captured||mode!=='space')return;if(!canDock(pose)){message(stationRange(pose).portDistance>STATION.captureRadius?'Too far from the port · fly back inside the ring':'Too fast · slow to dock');return;}\r\n  lock(true);if(!machine.send('dock').accepted){lock(false);return;}hideDock();message('Docking · MOM Station');docking={from:{...pose,pitch},to:dockPose(),elapsed:0};}\r\n function stepDocking(dt){docking.elapsed+=dt;const dur=reduced?0:STATION.dockMs,k=dur?Math.min(1,docking.elapsed*1000/dur):1,e=k*k*(3-2*k),a=docking.from,b=docking.to;pose={x:a.x+(b.x-a.x)*e,y:a.y+(b.y-a.y)*e,z:a.z+(b.z-a.z)*e,yaw:wrap(a.yaw+wrap(b.yaw-a.yaw)*e),speed:0};pitch=a.pitch+(b.pitch-a.pitch)*e;if(k>=1)finishDock();}\r\n function finishDock(){const b=docking.to;docking=null;pose={x:b.x,y:b.y,z:b.z,yaw:b.yaw,speed:0};pitch=0;if(!complete('docked').accepted){machine.send('cancel');lock(false);return;}docked=true;ui.root.dataset.docked='1';message('Docked · MOM Station');dockCard();}\r\n function dockCard(){if(!alive()||!docked||machine.state!=='docked')return;lock(true);ui.openMenu();const m=ui.menu;m.replaceChildren();const h=doc.createElement('h3');h.textContent='MOM STATION · DOCKED';m.append(h);for(const [label,fn]of [['Undock',undock],['NAV',dockNav],['Fiery re-entry to Las Vegas',()=>void cityAt(home,{kind:'reentry'})]]){const b=doc.createElement('button');b.textContent=label;b.onclick=fn;m.append(b);}}\r\n // NAV from the dock: Back returns to the card; a jump goes through the machine's docked->reentry edge.\r\n function dockNav(){if(!docked||machine.state!=='docked')return;nav=navigationMenu(ui,{store,current,home,getFriends,signal:ac.signal,onJump:p=>jump(p),onClose:dockCard});}\r\n function undock(){if(!docked||!alive()||!machine.send('undock').accepted)return;const u=undockPose();pose={x:u.x,y:u.y,z:u.z,yaw:u.yaw,speed:0};pitch=0;clearDock();Object.assign(prevSpace,{x:u.x,y:u.y,z:u.z});ui.closeMenu();lock(false);message('Undocked · MOM Station behind you');}\r\n function resize(){if(!renderer)return;const r=ui.stage.getBoundingClientRect(),scale=Math.min(1,640/Math.max(r.width,1),360/Math.max(r.height,1));renderer.setSize(Math.max(1,Math.round(r.width*scale)),Math.max(1,Math.round(r.height*scale)),false);camera.aspect=r.width/Math.max(r.height,1);camera.updateProjectionMatrix();cockpit3d?.resize();placeDock();}\r\n let dragging=null;ui.canvas.addEventListener('pointerdown',e=>{if(busy)return;dragging={id:e.pointerId,x:e.clientX,y:e.clientY};ui.canvas.setPointerCapture?.(e.pointerId);},{signal:ac.signal});ui.canvas.addEventListener('pointermove',e=>{if(!dragging||dragging.id!==e.pointerId||busy)return;pose.yaw=wrap(pose.yaw-(e.clientX-dragging.x)*.003);pitch=Math.max(-1.1,Math.min(.3,pitch-(e.clientY-dragging.y)*.002));dragging={id:e.pointerId,x:e.clientX,y:e.clientY};},{signal:ac.signal});for(const e of ['pointerup','pointercancel'])ui.canvas.addEventListener(e,()=>{dragging=null;},{signal:ac.signal});\r\n // Yoke feel on the camera only (never pose/pitch/floor): push looks down, pull looks up, yaw banks. Neutral when busy/docking/world.\r\n function feel(dt){const on=!busy&&!docking&&(mode==='city'||mode==='space'),k=1-Math.exp(-dt*6);nose+=((on?clamp1(ui.input.pull):0)*.1-nose)*k;bank+=((on?clamp1(ui.input.yaw):0)*.1-bank)*k;if(mode==='world')return;camera.rotation.x+=nose;camera.rotation.z+=bank;}\r\n function frame(t){if(!alive())return;raf=requestAnimationFrame(frame);if(doc.hidden){last=0;return;}if(last&&t-last<32)return;const dt=last?Math.min(.1,(t-last)/1000):0;last=t;ui.update?.(dt);\r\n  if(mode==='city'){\r\n   if(launchAt){launchElapsed+=dt;const k=Math.min(1,launchElapsed/2.2);pose.y=4+(city.floor-4)*(k*k*(3-2*k));if(k>=1){launchAt=0;complete('cruise');lock(false);message('Las Vegas · flip ENGINE POWER + BOOST ARM, then BOOST');}}\r\n   else if(!busy){const next=stepCruise(pose,ui.input,dt,{floor:city.floor,maxSpeed:85});if(cityCovered(localToGeo(next,origin))&&Math.hypot(next.x,next.z)<500)pose=next;else{pose.speed=0;pose.yaw=headingTo(pose,{x:0,z:0});message('Flight coverage boundary · waiting for mapped territory');ui.reset();}}\r\n   camera.up.set(0,1,0);camera.position.set(pose.x,pose.y,pose.z);camera.rotation.order='YXZ';camera.rotation.set(pitch,pose.yaw,0);city?.update(t,camera);\r\n   if(!busy&&!launchAt){const point=localToGeo(pose,origin),id=sectorAt(point).id,travel=geoToLocal(point,checkpointOrigin);if(id!==lastSector&&Math.hypot(travel.x,travel.z)>=CHECKPOINT_BUFFER)void cityAt(point,{kind:'checkpoint',checkpoint:true});else if(Math.hypot(pose.x,pose.z)>250&&t>streamRetry)void streamCity();}\r\n  }else if(mode==='space'){\r\n   if(docking)stepDocking(dt);\r\n   else if(!busy){pose=stationCruise(pose,ui.input,dt,captured);const len=Math.hypot(pose.x,pose.y,pose.z);if(len<125){const k=125/len;pose.x*=k;pose.y*=k;pose.z*=k;}if(len>650){const limit=Math.sqrt(Math.max(1,650*650-pose.y*pose.y)),xz=Math.hypot(pose.x,pose.z)||1;pose.x*=limit/xz;pose.z*=limit/xz;pose.speed=0;if(t>spaceCooldown)void boundary();}else if(len>570)message('Approaching permitted territory boundary');pose=clearHull(pose);if(!captured&&sweptCapture(prevSpace,pose)){captured=true;showDock();message('Dock ready · tap DOCK');}}\r\n   range=stationRange(pose);if(captured&&!docked&&!docking){if(range.portDistance>STATION.captureRadius){captured=false;hideDock();}else dockButton.disabled=!canDock(pose);}Object.assign(prevSpace,{x:pose.x,y:pose.y,z:pose.z});\r\n   camera.up.set(0,1,0);camera.position.set(pose.x,pose.y,pose.z);camera.rotation.order='YXZ';if(t<turnUntil){pose.yaw=wrap(pose.yaw+wrap(turnHeading-pose.yaw)*Math.min(1,dt*4));pose.speed=0;ui.reset();pitch=-.25;}camera.rotation.set(pitch,pose.yaw,0);\r\n  }else if(mode==='world'){if(!busy){orbitAngle+=dt*ui.input.throttle*.057;orbitLat=Math.max(-1.2,Math.min(1.2,orbitLat+ui.input.yaw*dt*.15));}worldCamera();}\r\n  {const avail=mode==='city'||mode==='world';if(avail!==launchAvail){launchAvail=avail;ui.setLaunchAvailable?.(avail);}}\r\n  space?.update(dt,mode==='world'?camera.position:pose,mode==='space'?range:undefined);ecology?.update?.(dt);if(!busy)look?.apply(camera);feel(dt);if(mode==='city'&&!busy)survey?.update(camera);else survey?.hide();cockpit3d?.update(dt);render();if(mode==='space'&&range)stationHud?.update(camera,range,docked?'docked':docking?'docking':captured?'ready':range.phase);else stationHud?.hide();perf?.tick(t);\r\n  ui.reading(mode==='city'?`${Math.round(pose.y)}m · ${Math.round(pose.speed)}m/s`:mode==='world'?'WORLD CRUISE · VEGAS ONLY LANDING':docked?'DOCKED · MOM STATION':docking?'DOCKING · MOM STATION':captured?'DOCK READY · MOM STATION':`SPACE · STATION ${Math.round(range.portDistance)}u${Math.abs(PORT.y-pose.y)>=3?` · ${PORT.y>pose.y?'UP':'DOWN'} ${Math.round(Math.abs(PORT.y-pose.y))}u`:''}`);ui.root.dataset.mode=mode;ui.root.dataset.busy=String(busy);\r\n }\r\n ui.canvas.addEventListener('webglcontextlost',e=>{e.preventDefault();if(alive()){machine.send('context-loss');dispose({graphicsInterrupted:true});}},{signal:ac.signal});\r\n const ro=new ResizeObserver(resize);ro.observe(ui.stage);ac.signal.addEventListener('abort',()=>ro.disconnect(),{once:true});\r\n async function initialize(){if(!alive()||starting)return;starting=true;try{ui.closeMenu();ui.root.removeAttribute('data-error');syncPhase();ui.lock(true);message('Preparing Las Vegas…');if(!renderer){T=deps.T||await import('/vendor/three/three.module.js');if(!alive())return;claim=claimGraphics('coach-flight');await claim.ready;if(!alive())return;\r\n  renderer=new T.WebGLRenderer({canvas:ui.canvas,antialias:false,powerPreference:'low-power'});renderer.setPixelRatio(1);scene=new T.Scene();scene.background=new T.Color(CITY_SKY);scene.fog=new T.FogExp2(CITY_SKY,.00022);camera=new T.PerspectiveCamera(65,1,1,CITY_FAR);useFogLaw(T,scene,CITY_FOG);camera.rotation.order='YXZ';look=createFlightLook(T,{win,signal:ac.signal});\r\n  scene.add(new T.AmbientLight(0xb3d6ed,1.8));const sun=new T.DirectionalLight(0xffd4a5,2);sun.position.set(-200,600,100);scene.add(sun);space=createSpaceScene(T,{seed:5});scene.add(space.group);\r\n  // V3: the RTS 360 deg dome over the city (real sun at the pin, device clock), scaled out past the buildings and depth-tested behind them; the fog closes to its horizon\r\n  try{sky=createAlienSky(T,{seed:5,faction:identity.faction,occlude:true,ridges:false,lat:home.lat,lng:home.lng,reducedMotion:reduced});sky.group.scale.setScalar((CITY_FAR-1000)/88);scene.add(sky.group);}catch(e){sky=null;console.warn('flight sky',e);}space.group.visible=false;stationHud=createStationHud(T,{host:ui.stage,station:STATION});try{cockpit3d=createCockpit3D(T,{ui});if(cockpit3d.hasLaunch)ui.root.dataset.cockpit3d='1';else delete ui.root.dataset.cockpit3d;}catch(e){cockpit3d=null;delete ui.root.dataset.cockpit3d;console.warn('flight cockpit 3d',e);}\r\n  // ?perf=1: counts are renderer.info + DOM canvas count (NOT live WebGL contexts); desktop counts are not phone timing.\r\n  if(new URLSearchParams(win.location.search).get('perf')==='1')perf=createPerfOverlay(ui.stage,{contextLabel:'canvases',getStats:()=>renderer?{calls:renderer.info.render.calls,triangles:renderer.info.render.triangles,geometries:renderer.info.memory.geometries,textures:renderer.info.memory.textures,contexts:doc.querySelectorAll('canvas').length}:null});}\r\n  city=await loadCityScene(T,{origin,base,signal:ac.signal,fixture:deps.fixture});if(!alive()){city.dispose();return;}scene.add(city.group);refreshEcology();resize();machine.send('board');complete('takeoff');launchAt=1;launchElapsed=0;camera.position.set(pose.x,pose.y,pose.z);camera.rotation.set(pitch,pose.yaw,0);city.update(performance.now(),camera);render();ui.root.dataset.ready='1';ui.root.__flight={get cockpit3d(){return cockpit3d;},state,action,launchSpace,enterWorld,jump,cityAt,dispose,dock,undock,range:()=>stationRange(pose),captured:()=>captured,ui,store,scene,camera,renderer,get pose(){return pose;},set pose(v){if(Number.isFinite(v.pitch))pitch=Math.max(-1.1,Math.min(.3,v.pitch));const {pitch:ignored,...position}=v;pose={...pose,...position};}};readyResolve(true);raf=requestAnimationFrame(frame);\r\n }catch(e){if(alive()){message(e.message);ui.root.dataset.error='1';syncPhase();ui.openMenu();ui.menu.replaceChildren();for(const [label,a]of [['Retry flight','retry'],['Return to base','exit']]){const b=doc.createElement('button');b.textContent=label;b.onclick=()=>action(a);ui.menu.append(b);}readyResolve(false);}}finally{starting=false;syncPhase();}}\r\n if(!cityCovered(pin)){ui.lock(true);message('Your home is outside the available flight map. Choose the Las Vegas approach to try flight.');ui.openMenu();for(const [label,fn]of [['Start at Las Vegas approach',()=>void initialize()],['Return to base',()=>dispose()]]){const b=doc.createElement('button');b.textContent=label;b.onclick=fn;ui.menu.append(b);}}else void initialize();\r\n if(win.MutationObserver){const mo=new win.MutationObserver(()=>syncPhase());mo.observe(ui.menu,{attributes:true,attributeFilter:['hidden']});ac.signal.addEventListener('abort',()=>mo.disconnect(),{once:true});}\r\n queueMicrotask(()=>{armed=true;if(!disposed)syncPhase(true);});\r\n return {ready,dispose,state,instanceId,phase:()=>phaseNow};\r\n}\r\n","modules/coach-world-rts/ar-anchor.mjs":"// R4: pins the AR base to the player's home. All maths run on this device; the home coordinates are never sent, stored or drawn.\r\n// createAnchor({home,geo,compass,poseYaw,absolute,now}) -> {start(),update(),offset(),yaw(),distance(),status,dispose()}\r\n// offset() = metres east/south of home (three axes: +x east, +z south). The home sits at -offset from the viewer.\r\nimport {localOffset} from '../coach-world/public-geography.mjs';\r\nimport {createOneEuro,smoothHeading,normDeg} from './geo-follow.mjs';\r\n\r\nexport const AR_SCALE=3.2,AHEAD_M=8,REANCHOR_M=50,MAX_FIX_ACC_M=80;\r\nexport const validHome=h=>!!h&&h.consent===true&&Number.isFinite(h.latitude)&&Number.isFinite(h.longitude);\r\nexport const needsReanchor=(prev,next,limit=REANCHOR_M)=>!!prev&&Math.hypot(next.x-prev.x,next.z-prev.z)>limit;\r\nexport const chipText=(status,dist)=>status==='anchored'?`Anchored at home · ${Math.round(dist)} m away`:status==='locating'?'Finding your spot… · placed ahead':'No home set · placed ahead';\r\n\r\nexport function createAnchor({home,geo=globalThis.navigator?.geolocation,compass=null,poseYaw=()=>0,absolute=()=>false,now=()=>performance.now()}={}){\r\n const has=validHome(home);\r\n let status=has?'locating':'none',watch=null,off=null,raw=null,yawDeg=null,disposed=false;\r\n let fx=createOneEuro(),fz=createOneEuro();\r\n function fix(p){\r\n  if(disposed||!has||!(p?.coords?.accuracy<=MAX_FIX_ACC_M||p?.coords?.accuracy==null))return;\r\n  const o=localOffset(home,p.coords.longitude,p.coords.latitude),t=now();\r\n  if(!Number.isFinite(o.x)||!Number.isFinite(o.z))return;\r\n  if(!off||needsReanchor(raw,o)){fx=createOneEuro();fz=createOneEuro();off={x:fx.filter(o.x,t),z:fz.filter(o.z,t)};}else off={x:fx.filter(o.x,t),z:fz.filter(o.z,t)};\r\n  raw=o;status='anchored';\r\n }\r\n const api={\r\n  get status(){return status;},\r\n  start(){\r\n   if(!has||watch!=null||!geo?.watchPosition)return api;\r\n   watch=geo.watchPosition(fix,()=>{if(!off)status='locating';},{enableHighAccuracy:true,maximumAge:2000,timeout:20000});return api;\r\n  },\r\n  fix, // exposed for tests and the dev page\r\n  // call once per frame: absolute pose is already east-aligned (yaw 0); otherwise north in the pose frame = pose yaw + compass heading\r\n  update(){\r\n   if(absolute()){yawDeg=0;return;}\r\n   const h=compass?.heading;if(!Number.isFinite(h))return;\r\n   const d=normDeg(poseYaw()*180/Math.PI+h);yawDeg=smoothHeading(yawDeg,d);\r\n  },\r\n  offset:()=>off,\r\n  yaw:()=>(yawDeg==null?0:yawDeg*Math.PI/180),\r\n  distance:()=>off?Math.hypot(off.x,off.z):0,\r\n  dispose(){disposed=true;if(watch!=null)geo?.clearWatch?.(watch);watch=null;off=raw=null;},\r\n };\r\n return api;\r\n}\r\n","modules/coach-world-rts/ar-catch.mjs":"// AR rare-coach catch contract, shared by the client (what it sends and checks) and the server / guest reducer (what it trusts).\r\n//   client -> POST /api/world  {type:'ar-catch', cell:{z:16,x,y}, npcId, playMs}   (guest: the same action through the local reducer)\r\n//   server -> arCatchEntry(input,now) rebuilds the ledger entry from the deterministic spawn (never from client data), then\r\n//             applyBaseAction(base,{type:'wild-catch',entry},{now,wildServer:true}) - the existing reducer, idempotent per entry.id.\r\n// TRUST LIMIT: the mini game runs on the phone. The server can prove the rare exists in that cell (it replays spawnNpcs) and rebuild the entry itself, but playMs is\r\n// only what the client DECLARES - a modified client can claim any value, replay the game offline or skip it. The floor stops accidental/instant claims, not cheating;\r\n// real anti-cheat needs a server-timed session (start/finish) or per-account rate caps. Rares are cosmetic-tier four-legged companions, so this was accepted for now.\r\nimport {spawnNpcs} from './ar-npc-spawn.mjs';\r\nimport {wildEntryFor} from './capture-minigame.mjs';\r\n\r\nexport const AR_CATCH_ACTION='ar-catch',AR_CATCH_MIN_PLAY_MS=6000,AR_CATCH_ZOOM=16;\r\nconst int=(v,lo,hi)=>Number.isSafeInteger(v)&&v>=lo&&v<=hi;\r\n/** input = {cell,npcId,playMs}. Returns {entry,npc}; throws RangeError(code) with a stable code the API can return. */\r\nexport function arCatchEntry(input,now=Date.now()){\r\n const cell=input?.cell,npcId=input?.npcId;\r\n if(!cell||cell.z!==AR_CATCH_ZOOM||!int(cell.x,0,2**AR_CATCH_ZOOM-1)||!int(cell.y,0,2**AR_CATCH_ZOOM-1))throw new RangeError('ar_catch_cell_invalid');\r\n if(typeof npcId!=='string'||npcId.length>100)throw new RangeError('ar_catch_npc_invalid');\r\n // playMs is client-declared (see TRUST LIMIT)\r\n if(!(Number.isFinite(input.playMs)&&input.playMs>=AR_CATCH_MIN_PLAY_MS)) throw new RangeError('ar_catch_too_fast');\r\n const npc=spawnNpcs({z:cell.z,x:cell.x,y:cell.y}).find(n=>n.id===npcId&&n.rare);\r\n const entry=npc&&wildEntryFor(npc,now);\r\n if(!entry)throw new RangeError('ar_catch_unknown_npc');\r\n return {entry,npc};\r\n}\r\nexport const arCatchAction=(npc,{playMs})=>({type:AR_CATCH_ACTION,cell:{z:npc.cell.z,x:npc.cell.x,y:npc.cell.y},npcId:npc.id,playMs:Math.round(playMs)});\r\nexport const ownsCatch=(client,entry)=>!!client?.snapshot?.base?.wild?.some?.(w=>w.id===entry.id);\r\n/** Sends the catch and trusts only the saved base that comes back: no entry in base.wild = not saved. */\r\nexport async function commitArCatch(client,npc,entry,{playMs}){\r\n if(!npc?.cell)throw new Error('This coach cannot be caught from here.');\r\n await client.command(arCatchAction(npc,{playMs}));\r\n if(!ownsCatch(client,entry))throw new Error('Your catch was not saved. Try again.');\r\n}\r\n\r\n/** The world client AR talk/catch uses. shared = the host's client (never disposed here) or create() makes one we own. get() resolves a LOADED facade; dispose() closes what we own. */\r\nexport function createNpcClientGetter({shared=null,create,onWorldChange,alive=()=>true}){\r\n let p=null,own=null,closed=false;const ok=()=>!closed&&alive();\r\n const get=()=>p??=(async()=>{\r\n  let c=shared,mine=false;\r\n  if(!c){c=await create();mine=true;own=c;}\r\n  try{if(!c.snapshot)await c.load();if(!ok())throw Error('closed');}catch(e){if(mine){c.dispose?.();own=null;}throw e;}\r\n  return {get snapshot(){return c.snapshot;},get authenticated(){return c.authenticated;},async command(a){const s=await c.command(a);try{onWorldChange?.(s??c.snapshot);}catch{}return s;},dispose(){if(mine)c.dispose?.();}};\r\n })().catch(e=>{p=null;throw e;});\r\n return {get,dispose(){closed=true;try{own?.dispose?.();}catch{}own=null;}};\r\n}\r\n","modules/coach-world-rts/ar-npc-spawn.mjs":"// AR NPC spawn: pure (no THREE, no DOM) so the server can replay it. The same pin cell always gives the same crowd - ids, rares, bodies and tiers.\r\n// ar-catch.mjs uses this to check a claimed rare really stands in the claimed cell.\r\nimport {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\nimport {lookFor} from './npc-looks.mjs';\r\nimport {rollTier} from './wild-rarity.mjs';\r\nimport {coachHeightM} from './ar-scale.mjs';\r\n\r\nexport const NPC_MIN_M=12,NPC_MAX_M=24,LAND_OFFSET_M=3,RARE_P=1/8,RARE_H_M=3.4,RING_M=120; // RING_M = world-layer R_RING (tests pin them together)\r\n// Same FNV as wild-forest hashSeed (copied so the server does not pull in the 3D forest); a test keeps them identical.\r\nexport const hashSeed=(cell,seed=0)=>{let h=2166136261^seed;for(const ch of `${cell?.z}/${cell?.x}/${cell?.y}`){h^=ch.charCodeAt(0);h=Math.imul(h,16777619);}return h>>>0;};\r\nconst NAMES=['Zib','Glorp','Vex','Nubb','Quill','Morx','Plim','Taz','Okk','Brin','Yull','Fenn','Squee','Dorb'];\r\nconst rng=seed=>{let a=seed>>>0;return()=>{a=a+0x6D2B79F5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};};\r\n// Lane C body heights vs the player (ar-scale coachHeightM = crewHeightFor at arScale 1, unitHeight 1: NPCs live in unscaled pin metres): fake level 1-5 -> 0.85-2.9 m.\r\nexport const npcHeightM=level=>coachHeightM(Math.min(5,Math.max(1,level)));\r\n\r\n// Deterministic per pin cell: 6-10 NPCs, uniform by area in the ring [12, 24) m about the pin (integration: closer than R_CLEAR so Ian can find them), >= gap apart, 1 in 8 a four-legged rare (3.4 m).\r\nexport function spawnNpcs(pinCell,{rClear=0,rRing=RING_M,rMin=NPC_MIN_M,rMax=NPC_MAX_M,gap=2.5,heightFor=npcHeightM}={}){\r\n const seed=hashSeed(pinCell),r=rng(seed),n=6+Math.floor(r()*5),lo=Math.max(rMin,rClear),hi=Math.min(rMax,rRing),out=[];\r\n for(let tries=0;out.length<n&&tries<n*30;tries++){\r\n  const a=r()*Math.PI*2,d=Math.sqrt(lo*lo+r()*(hi*hi-lo*lo)),x=Math.cos(a)*d,z=Math.sin(a)*d;\r\n  if(out.some(o=>Math.hypot(o.x-x,o.z-z)<gap))continue;\r\n  const k=out.length,rare=r()<RARE_P,level=1+Math.floor(r()*5),src=rare?FOUR_LEGGED_SOURCES[(seed+k)%FOUR_LEGGED_SOURCES.length]:null;\r\n  const id=`npc-${seed.toString(36)}-${k}`;\r\n  out.push({id,x,z,rare,level,look:lookFor(seed,k),source:src?.sourceCoachId||null,tier:rare?rollTier(id,'park'):null,\r\n   name:rare?src.sourceLabel:`${NAMES[(seed+k*5)%NAMES.length]}-${10+(seed>>>8)%90}`,heightM:rare?RARE_H_M:heightFor(level)});\r\n }\r\n return out;\r\n}\r\n// Where \"Land beside\" puts the pin: m metres from the NPC toward the viewer (origin by default).\r\nexport const besidePoint=(npc,from={x:0,z:0},m=LAND_OFFSET_M)=>{const dx=from.x-npc.x,dz=from.z-npc.z,d=Math.hypot(dx,dz)||1;return {x:npc.x+dx/d*m,z:npc.z+dz/d*m};};\r\n","modules/coach-world-rts/ar-npcs.css":"/* Lane I: NPC chip in the AR walkthrough root (hud.css pixel look, no new fonts). */\r\n.cw-npc-chip{position:absolute;left:50%;top:calc(56px + env(safe-area-inset-top,0px));transform:translateX(-50%);z-index:5;display:flex;align-items:center;gap:8px;\r\n max-width:calc(100% - 24px);padding:8px 8px 8px 12px;background:#0b0f14e6;color:#fff;border:2px solid var(--npc-accent,#ffd23a);border-radius:12px;\r\n box-shadow:0 0 0 2px #000,inset 4px 0 0 var(--npc-tint,#a070c0);font:600 14px/1.2 \"Pixelify Sans\",m5x7,system-ui,sans-serif;touch-action:manipulation}\r\n.cw-npc-chip-txt{display:flex;flex-direction:column;min-width:0}\r\n.cw-npc-chip-txt b{font-size:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n.cw-npc-chip-txt span{opacity:.8;font-size:12px;white-space:nowrap}\r\n/* buttons inherit the walk HUD bezel ([data-rts-walk-root] button in walk-hud.mjs) */\r\n[data-rts-walk-root] .cw-npc-chip .cw-npc-x{min-width:48px}\r\n/* Talk / Catch prompt: the chip's action button */\r\n.cw-npc-chip [data-npc-act]{min-width:88px;min-height:48px}\r\n","modules/coach-world-rts/ar-npcs.mjs":"// Lane I (D6/D7): city NPCs in AR. Every NPC that is not a defined game character is the MYR5 alien wearing a Cover Crisis look\r\n// (npc-looks LOOKS: tint + Cover pattern + accent rim through the atlas mask). No GLB, no new GL context: one Sprite per NPC (1 draw each, <= 10).\r\n// attachArNpcs(T,{scene,camera,canvas,world,getSheet,getFactionSheet,pinCell,scale,onLandBeside}) -> {npcs,update(t),tapAt(x,y),dispose()}\r\n//  world = the group the NPCs live in (pin-AR: arRoot, metres about the pin); scale = metres per world unit (1 for arRoot).\r\n//  Tap = pointerup within TAP_PX of its pointerdown (drag-look keeps working: we never stop propagation). Chip -> \"Land beside\" -> onLandBeside({x,z,name,id}),\r\n//  a point LAND_OFFSET_M from the NPC toward the viewer, in world-local metres. The host (pin-drop) exits AR and pre-places the pin there; it never auto-DROPs.\r\n//  Within ENGAGE_M (horizontal, camera to NPC) a tap - or the \"Talk\"/\"Catch\" prompt that appears on its own - acts instead: a regular NPC opens openNpcEncounter({npc,root,onClose})\r\n//  (./npc-encounters.mjs, or deps.openNpcEncounter); a four-legged rare starts the capture mini game (./capture-minigame.mjs) and, on a win, commitCatch(entry) saves it (isCaught(entry) hides owned rares).\r\nimport {createCrewBillboards} from './crew-billboards.mjs';\r\nimport {spawnNpcs,besidePoint,npcHeightM,NPC_MIN_M,NPC_MAX_M,LAND_OFFSET_M,RARE_P,RARE_H_M} from './ar-npc-spawn.mjs';\r\nimport {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\nimport {lookFor,applyLook,coverPainters} from './npc-looks.mjs';\r\nimport {rollTier} from './wild-rarity.mjs';\r\nimport {startCapture,wildEntryFor} from './capture-minigame.mjs';\r\nimport {commitArCatch,ownsCatch} from './ar-catch.mjs';\r\nimport {makeTexture} from '../../arcade/cover/textures.mjs';\r\n\r\nexport const MIN_PX=12,MARK_PX=8,TAP_PX=8,TURN_MS=2800,FOCUS_M=3,ENGAGE_M=6,SLOP_PX=48,NEAR_MS=250;\r\nexport {NPC_MIN_M,NPC_MAX_M,LAND_OFFSET_M,RARE_P,RARE_H_M,npcHeightM,spawnNpcs,besidePoint};\r\n// Nearest NPC sprite under the NDC point, or null.\r\nexport function pickNpc(T,camera,sprites,ndc){\r\n if(!sprites.length)return null;const ray=new T.Raycaster();camera.updateMatrixWorld();ray.setFromCamera({x:ndc[0],y:ndc[1]},camera);\r\n const hit=ray.intersectObjects(sprites,false).find(h=>h.object.userData.npc);return hit?hit.object.userData.npc:null;\r\n}\r\n\r\n// Finger slop: the NPC whose body centre (npcs[i].at = world point) projects nearest the tap, within slopPx css px and in front of the camera. A 12 px sprite is not a fair target for a thumb.\r\nexport function pickNear(T,camera,list,cx,cy,rect,slopPx=SLOP_PX){\r\n camera.updateMatrixWorld();const v=new T.Vector3();let best=null,bd=slopPx;\r\n for(const {npc,at} of list){\r\n  v.copy(at).applyMatrix4(camera.matrixWorldInverse);if(v.z>=0)continue; // behind the camera\r\n  v.copy(at).project(camera);const sx=rect.left+(v.x+1)/2*rect.width,sy=rect.top+(1-v.y)/2*rect.height,d=Math.hypot(sx-cx,sy-cy);\r\n  if(d<=bd){bd=d;best=npc;}\r\n }\r\n return best;\r\n}\r\n\r\nfunction ensureCss(doc){const href=new URL('./ar-npcs.css',import.meta.url).href;if([...doc.querySelectorAll('link[data-cw-npcs]')].some(l=>l.href===href))return;const l=doc.createElement('link');l.rel='stylesheet';l.href=href;l.dataset.cwNpcs='';doc.head.append(l);}\r\n\r\nexport function attachArNpcs(T,{scene,camera,canvas,world=scene,getSheet,getFactionSheet,pinCell,scale=1,focus=null,onLandBeside,onTap,heightFor,openNpcEncounter,commitCatch,isCaught,onCaught,client,getClient,now=()=>performance.now(),deps={}}){\r\n const npcs=spawnNpcs(pinCell,heightFor?{heightFor}:{}),hf=heightFor||npcHeightM;\r\n // R1-C14: focus={id,name,...} = the coach you tapped in flight / Land beside: one extra NPC, put FOCUS_M in front of the camera on the first update (ring NPCs untouched).\r\n // Land beside carries rare/source/look/level/tier/heightM through; whatever is missing is derived (a rare always has a four-legged body, its own height and tier).\r\n let focusN=null;if(focus?.id){const lv=focus.level||3,sd=[...focus.id].reduce((h,c)=>Math.imul(h,31)+c.charCodeAt(0)>>>0,7),rare=!!focus.rare,src=rare?(FOUR_LEGGED_SOURCES.find(q=>q.sourceCoachId===focus.source)||FOUR_LEGGED_SOURCES[sd%FOUR_LEGGED_SOURCES.length]):null;\r\n  focusN={id:focus.id,x:0,z:-FOCUS_M,rare,level:lv,look:focus.look||lookFor(sd,0),source:src?.sourceCoachId||null,tier:rare?focus.tier||rollTier(focus.id,'park'):null,cell:focus.cell||null,name:focus.name||src?.sourceLabel||'Coach',heightM:focus.heightM||(rare?RARE_H_M:hf(lv)),focus:true};npcs.push(focusN);}\r\n // The same coach can also stand in this cell's ring (flight lands you in the cell it spawned in): the focus copy replaces it, one coach = one sprite = one id.\r\n if(focusN)for(let i=npcs.length;i--;)if(npcs[i]!==focusN&&npcs[i].id===focusN.id)npcs.splice(i,1);\r\n // Owned rares do not stand in the street again (the ledger entry id is stable per NPC).\r\n if(isCaught){for(let i=npcs.length;i--;){const e=npcs[i].rare&&wildEntryFor(npcs[i]);if(e&&isCaught(e))npcs.splice(i,1);}if(focusN&&!npcs.includes(focusN))focusN=null;}\r\n for(const n of npcs)if(!n.focus)n.cell??={z:pinCell.z,x:pinCell.x,y:pinCell.y}; // the cell this NPC was spawned from: the server replays spawnNpcs(cell) to verify a catch (a focus coach must bring its own: Land beside / flight carry it)\r\n const byId=new Map(npcs.map(n=>[n.id,n])),wraps=new Map(),painters=coverPainters(T,makeTexture);\r\n const units=npcs.map((n,i)=>({id:n.id,worker:true,level:1,x:n.x/scale,y:n.z/scale,scale:n.heightM/scale,color:n.look.tint,sheet:n.id,source:'npc',turnAt:-1e9,i}));\r\n // Per-NPC sheet = the real sheet (myr5, or bodies-pets for rares) whose dye is the look; crew-billboards' pick() reads atlas.units[source].clips.\r\n function sheetFor(id){\r\n  const n=byId.get(id);if(!n)return null;const S=n.rare?getFactionSheet?.('bodies-pets'):getSheet?.();if(!S?.atlas)return null;\r\n  const w=wraps.get(id);if(w?.S===S)return w.sheet;\r\n  const ud=n.rare?S.atlas.units?.[n.source]:null,clips=ud?.clips||S.atlas.clips;if(!clips)return null;\r\n  const sheet={atlas:{...S.atlas,units:{npc:{clips,strike:ud?.strike||null}}},fx:S.fx,dye:()=>applyLook(S,n.look,{painters})};\r\n  wraps.set(id,{S,sheet});return sheet;\r\n }\r\n const crew=(deps.createCrew||createCrewBillboards)(T,{units:()=>units,getSheet,getFactionSheet:sheetFor,getBase:()=>null,scale:1,origin:{x:.5,y:.5},unitHeight:1,now});\r\n crew.group.name='ar-npcs';crew.group.scale.setScalar(1);world.add(crew.group);\r\n let tagged=false,chip=null,chipFor=null,active=null,down=null;const dismissed=new Set();const ac=new AbortController(),sig={signal:ac.signal},cam=new T.Vector3(),pos=new T.Vector3();\r\n const sprites=()=>crew.group.children.filter(o=>o.isSprite&&o.userData.npc);\r\n function tag(){ // crew sprites are created on first update; match each to its NPC by position (looks layers are off at level 1)\r\n  const list=crew.group.children.filter(o=>o.isSprite);if(list.length<units.length)return;\r\n  for(const s of list){let best=null,bd=1e9;for(const u of units){const d=Math.hypot(s.position.x-u.x,s.position.z-u.y);if(d<bd){bd=d;best=u;}}if(best&&bd<.5)s.userData.npc=byId.get(best.id);}\r\n  tagged=true;\r\n }\r\n // Idle facing: a 1 mm step toward the viewer every TURN_MS, swung +-0.5 rad, so the sprite turns slowly to face you (crew facing is relative to the group origin = the pin).\r\n function face(t){for(const u of units){if(t-u.turnAt<TURN_MS+u.i*170)continue;u.turnAt=t;const a=Math.atan2(-u.y,-u.x)+Math.sin(t/4000+u.i)*.5;u.x+=Math.cos(a)*1e-3;u.y+=Math.sin(a)*1e-3;}}\r\n function toLocal(v){crew.group.updateWorldMatrix(true,false);return crew.group.worldToLocal(v);} // the host moves arRoot/npcRoot this frame, before a render refreshes matrixWorld\r\n function distTo(n){camera.getWorldPosition(cam);toLocal(cam);return Math.hypot(cam.x-n.x/scale,cam.z-n.z/scale)*scale;}\r\n function placeFocus(){ // camera forward on the ground, FOCUS_M ahead of the camera, in crew-local metres\r\n  if(!focusN||focusN.placed)return;focusN.placed=true;camera.updateMatrixWorld();camera.getWorldPosition(cam);const f=new T.Vector3();camera.getWorldDirection(f);const a=cam.clone().add(f);toLocal(cam);toLocal(a);let dx=a.x-cam.x,dz=a.z-cam.z;const l=Math.hypot(dx,dz);if(l<1e-3){dx=0;dz=-1;}else{dx/=l;dz/=l;}\r\n  focusN.x=(cam.x+dx*FOCUS_M/scale)*scale;focusN.z=(cam.z+dz*FOCUS_M/scale)*scale;const u=units.find(q=>q.id===focusN.id);u.x=focusN.x/scale;u.y=focusN.z/scale;\r\n }\r\n function hideChip(){chip?.remove();chip=null;chipFor=null;}\r\n const inReach=n=>distTo(n)<=ENGAGE_M,verbFor=n=>n.rare?'Catch':'Talk';\r\n // One chip, two shapes: far = \"Land beside\" (the old flow); in reach = the Talk/Catch action. `note` replaces the action when the encounter cannot start.\r\n function showChip(n,note=''){\r\n  hideChip();const doc=canvas.ownerDocument,root=canvas.parentElement;if(!doc||!root)return;ensureCss(doc);\r\n  const near=inReach(n),c=chip=doc.createElement('div');chipFor=near?n.id:null;c.className='cw-npc-chip';c.dataset.npcChip=n.id;c.setAttribute('role','dialog');c.setAttribute('aria-label',n.name);\r\n  c.innerHTML='<div class=\"cw-npc-chip-txt\"><b></b><span></span></div>'+(near&&!note?`<button type=\"button\" data-npc-act>${verbFor(n)}</button>`:near?'':'<button type=\"button\" data-land-beside>Land beside</button>')+'<button type=\"button\" class=\"cw-npc-x\" data-npc-close aria-label=\"Close\">✕</button>';\r\n  c.style.setProperty('--npc-tint',n.look.tint);c.style.setProperty('--npc-accent',n.look.accent);\r\n  c.querySelector('b').textContent=n.name;c.querySelector('span').textContent=note||`${n.look.name} · ${Math.round(distTo(n))} m`;\r\n  c.querySelector('[data-npc-close]').onclick=()=>{if(near)dismissed.add(n.id);hideChip();};\r\n  const land=c.querySelector('[data-land-beside]'),act=c.querySelector('[data-npc-act]');\r\n  if(land)land.onclick=()=>{hideChip();camera.getWorldPosition(cam);toLocal(cam);onLandBeside?.({...besidePoint(n,{x:cam.x*scale,z:cam.z*scale}),id:n.id,name:n.name});};\r\n  if(act)act.onclick=()=>engage(n);\r\n  root.append(c);\r\n }\r\n // ---- engage: regular NPC -> host conversation UI; rare -> capture mini game. One at a time; taps/prompt are ignored while it is open. ----\r\n // The host's world client (getClient() resolves a LOADED one; the walkthrough owns and disposes it). Needed before any talk/catch so the shop and the ledger read real saved state.\r\n let clientP=null;const ensureClient=()=>clientP??=Promise.resolve(client||getClient?.()).then(c=>{if(!c)throw Error('no world client');return c;}).catch(e=>{clientP=null;throw e;});\r\n const wantsClient=!!(client||getClient);\r\n function engage(n){\r\n  if(active||!inReach(n))return false;hideChip();const root=canvas.parentElement,doc=canvas.ownerDocument;if(!root)return false;\r\n  const mine=active={close:()=>{}},release=()=>{if(active===mine)active=null;},gone=()=>active!==mine,fail=(why,e)=>{if(e)console.warn('coach-world npc encounter',e);release();showChip(n,why);};\r\n  const run=async()=>{\r\n   let c=null;if(wantsClient){try{c=await ensureClient();}catch(e){return fail(\"Can't reach your base\",e);}}if(gone())return;\r\n   if(n.rare){\r\n    const commit=commitCatch||(c&&n.cell?(entry,o)=>commitArCatch(c,n,entry,o):null);\r\n    if(!commit)return fail('Catching is not ready yet');\r\n    const owned=e=>!!(isCaught?.(e)||c&&ownsCatch(c,e)),e0=wildEntryFor(n);\r\n    if(e0&&owned(e0)){retire(n);return fail('Already in your base');}\r\n    const h=(deps.startCapture||startCapture)(n,{root,doc,commit,isCaught:owned,onSaved:entry=>{retire(n);try{onCaught?.(n,entry);}catch{}},onClose:release});\r\n    mine.close=()=>h?.close?.();return;\r\n   }\r\n   const open=deps.openNpcEncounter||openNpcEncounter||(await (deps.loadEncounters||(()=>import('./npc-encounters.mjs')))()).openNpcEncounter;\r\n   if(typeof open!=='function')throw Error('npc encounters unavailable');if(gone())return;\r\n   const h=open({npc:{...n,distanceM:distTo(n)},root,client:c||undefined,onClose:release});\r\n   mine.close=()=>(h?.dispose||h?.close)?.call(h); // npc-encounters returns {element,refresh,dispose}\r\n  };\r\n  run().catch(e=>fail(n.rare?\"Can't catch right now\":\"Can't talk right now\",e));\r\n  return true;\r\n }\r\n function retire(n){ // caught: the rare leaves the street\r\n  n.caught=true;const i=units.findIndex(u=>u.id===n.id);if(i>=0)units.splice(i,1);const j=npcs.indexOf(n);if(j>=0)npcs.splice(j,1);\r\n  mg.setDrawRange(0,units.length);\r\n }\r\n // The nearest NPC in reach gets a Talk/Catch prompt without a precise tap (the camera can hang above its head; a thumb cannot hit a 12 px sprite).\r\n let nearAt=-1e9;\r\n function nearPrompt(t){\r\n  if(active||t-nearAt<NEAR_MS)return;nearAt=t;\r\n  let best=null,bd=ENGAGE_M;for(const n of npcs){const d=distTo(n);if(d>ENGAGE_M)dismissed.delete(n.id);else if(d<=bd&&!dismissed.has(n.id)){bd=d;best=n;}}\r\n  if(!best){if(chipFor)hideChip();return;}\r\n  if(chipFor!==best.id&&!(chip&&!chipFor))showChip(best); // a chip the user opened for a far NPC stays until they close it\r\n }\r\n function tapAt(cx,cy){\r\n  const rc=canvas.getBoundingClientRect(),ndc=[((cx-rc.left)/rc.width)*2-1,-((cy-rc.top)/rc.height)*2+1];\r\n  if(active)return null;\r\n  crew.group.updateWorldMatrix(true,true);let n=pickNpc(T,camera,sprites(),ndc);\r\n  if(!n){const reach=npcs.filter(inReach).map(q=>({npc:q,at:crew.group.localToWorld(new T.Vector3(q.x/scale,q.heightM/scale/2,q.z/scale))}));n=pickNear(T,camera,reach,cx,cy,rc);}\r\n  onTap?.(n);if(!n){hideChip();return null;}\r\n  if(inReach(n))engage(n);else showChip(n);\r\n  return n;\r\n }\r\n canvas.addEventListener('pointerdown',e=>{down=[e.clientX,e.clientY];},sig);\r\n canvas.addEventListener('pointerup',e=>{const d=down;down=null;if(d&&Math.hypot(e.clientX-d[0],e.clientY-d[1])<TAP_PX)tapAt(e.clientX,e.clientY);},sig);\r\n canvas.addEventListener('pointercancel',()=>{down=null;},sig);\r\n // Findable: every NPC keeps >= MIN_PX drawing-buffer px tall (C's minimum angular size; inside D0 = 25 m the distance compression is 1:1),\r\n // and one Points draw puts a pixel chevron MARK_PX above every head, drawn over the city (depthTest off).\r\n const mk=new Float32Array(units.length*3),mg=new T.BufferGeometry();mg.setAttribute('position',new T.BufferAttribute(mk,3));\r\n const cv=new Uint8Array(8*8*4),V=['........','.##..##.','.#Y##Y#.','..#YY#..','...##...','........','........','........'];\r\n V.forEach((row,y)=>[...row].forEach((c,x)=>{if(c==='.')return;const o=((7-y)*8+x)*4;cv.set(c==='Y'?[255,224,64,255]:[20,12,30,255],o);}));\r\n const mtex=new T.DataTexture(cv,8,8,T.RGBAFormat);mtex.magFilter=mtex.minFilter=T.NearestFilter;mtex.generateMipmaps=false;mtex.needsUpdate=true;\r\n const marks=new T.Points(mg,new T.PointsMaterial({map:mtex,size:MARK_PX,sizeAttenuation:false,transparent:true,alphaTest:.5,depthTest:false,depthWrite:false,fog:false,toneMapped:false}));\r\n marks.name='ar-npc-marks';marks.renderOrder=20;marks.frustumCulled=false;world.add(marks); // sibling of crew.group (same frame), so crew's own children stay sprites only\r\n function sizeAndMark(){\r\n  camera.getWorldPosition(cam);toLocal(cam);const hpx=Math.max(1,canvas.height||canvas.clientHeight||1),k=2*Math.tan((camera.fov||63)*Math.PI/360)/hpx;\r\n  units.forEach((u,i)=>{const n=byId.get(u.id),d=Math.hypot(cam.x-u.x,cam.z-u.y)*scale,h=Math.max(n.heightM,MIN_PX*d*k);u.scale=h/scale;mk.set([u.x,h/scale+.35/scale,u.y],i*3);});\r\n  for(let i=units.length;i<mk.length/3;i++)mk.set([0,-1e4,0],i*3); // retired (caught) NPCs lose their chevron\r\n  mg.attributes.position.needsUpdate=true;\r\n }\r\n return {npcs,group:crew.group,marks,tapAt,\r\n  update(t=now()){placeFocus();face(t);sizeAndMark();const s=crew.update();if(!tagged)tag();nearPrompt(t);return s;},\r\n  engage:id=>{const n=byId.get(id);return !!n&&engage(n);},\r\n  dispose(){ac.abort();hideChip();const a=active;active=null;try{a?.close();}catch{}marks.removeFromParent();mg.dispose();marks.material.dispose();mtex.dispose();crew.dispose();wraps.clear();},\r\n };\r\n}\r\n","modules/coach-world-rts/ar-scale.mjs":"// Lane C (R81): AR scale from what the phone can actually show.\r\n// How far Ian can see: a thing is readable at ~10 rendered px. px = h/d * (Hpx/2)/tan(vfov/2).\r\n// iPhone portrait, 844 CSS px / PS1 2 = 422 px tall, vfov 63 deg -> a 6 m house is 10 px at ~200 m, and 17 px at 120 m.\r\n// So the rendered world ends at DH = 120 m: real distance d maps to f(d) (1:1 inside D0 = 25 m, slope 1 at D0, asymptote DH).\r\n// Bearings are kept (AR still points the right way); only distance shrinks, so far houses read 2.6x bigger at 300 m, 20x at 2.6 km.\r\n// Body heights are in real metres against the player (1.7 m): level 1 + minions = half, max level + four-legged = double.\r\nimport {AR_SCALE} from './ar-anchor.mjs';\r\n\r\nexport const CAM_VFOV_DEFAULT=63,VFOV_MIN=55,VFOV_MAX=75,CAM_HFOV_LANDSCAPE=69,D0=25,DH=120,USER_H=1.7,MAX_LEVEL=6,ARSCALE_MIN=1,ARSCALE_MAX=5;\r\nconst KV='cw-ar-vfov',KS='cw-ar-scale';\r\nconst store=()=>{try{return globalThis.localStorage??null;}catch{return null;}};\r\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\r\nconst num=v=>v==null||v===''?NaN:Number(v);\r\n\r\n// camera vertical FOV (portrait long axis) in degrees, calibrated per phone in Settings\r\nexport function readVfov(){const v=num(store()?.getItem(KV));return Number.isFinite(v)?clamp(v,VFOV_MIN,VFOV_MAX):CAM_VFOV_DEFAULT;}\r\nexport function saveVfov(v){const c=clamp(Number(v)||CAM_VFOV_DEFAULT,VFOV_MIN,VFOV_MAX);try{store()?.setItem(KV,String(c));}catch{}return c;}\r\n// passthrough FOV: portrait = calibrated vertical; landscape = vertical derived from the 69 deg horizontal of the main camera\r\nexport const passthroughFov=aspect=>aspect>=1?2*Math.atan(Math.tan(CAM_HFOV_LANDSCAPE*Math.PI/360)/aspect)*180/Math.PI:readVfov();\r\n\r\n// base size in AR: ?arscale= wins, then the Settings slider, then AR_SCALE (3.2)\r\nexport function readArScale(search=globalThis.location?.search??''){\r\n const q=num(new URLSearchParams(search).get('arscale'));if(Number.isFinite(q)&&q>0)return clamp(q,ARSCALE_MIN,ARSCALE_MAX);\r\n const v=num(store()?.getItem(KS));return Number.isFinite(v)&&v>0?clamp(v,ARSCALE_MIN,ARSCALE_MAX):AR_SCALE;\r\n}\r\nexport function saveArScale(v){const c=clamp(Number(v)||AR_SCALE,ARSCALE_MIN,ARSCALE_MAX);try{store()?.setItem(KS,String(c));}catch{}return c;}\r\n\r\n// real ground distance (m) -> rendered distance (m)\r\nexport const compress=(d,d0=D0,dh=DH)=>d<=d0?d:d0+(dh-d0)*(1-Math.exp(-(d-d0)/(dh-d0)));\r\n// tallest a building may render at real distance d (~17 deg above the horizon), so the pulled-in skyline never towers\r\nexport const heightCap=d=>.30*compress(d);\r\n\r\n// body height in metres. minion (basic worker) 0.85, four-legged 3.4, coaches 0.85 (L1) -> 3.4 (max) linear.\r\nexport function coachHeightM(level=1,maxLevel=MAX_LEVEL,{rare=false,minion=false,userH=USER_H}={}){\r\n if(rare)return 2*userH;if(minion)return .5*userH;\r\n const t=maxLevel>1?clamp((level-1)/(maxLevel-1),0,1):0;return userH*(.5+1.5*t);\r\n}\r\n// crew-billboards heightFor for AR: returns the sprite multiplier (replaces u.scale) so unitHeight*mult*arScale = coachHeightM.\r\n// Dots: no sheet = the myr5 basic worker (minion); 'bodies-pets' = four-legged.\r\nexport const crewHeightFor=(arScale,unitHeight=1.68)=>u=>coachHeightM(u.level||1,MAX_LEVEL,{rare:u.sheet==='bodies-pets',minion:!u.sheet})/(unitHeight*arScale);\r\n\r\n// vertex chunk: declarations (prepended) + world-space radial remap about the camera\r\nexport const GLSL_COMPRESS=`uniform float uCwD0;uniform float uCwDH;\r\nfloat cwCompress(float d){return d<=uCwD0?d:uCwD0+(uCwDH-uCwD0)*(1.-exp(-(d-uCwD0)/(uCwDH-uCwD0)));}\r\nvec3 cwCompressW(vec3 w){vec2 v=w.xz-cameraPosition.xz;float L=length(v);if(L>uCwD0)w.xz=cameraPosition.xz+v*(cwCompress(L)/L);return w;}\r\n`;\r\n\r\n// Settings sliders (lane G's [data-ar-knobs] placeholder). Values apply the next time AR opens.\r\nexport function renderKnobs(host){\r\n const box=host?.matches?.('[data-ar-knobs]')?host:host?.querySelector?.('[data-ar-knobs]');if(!box)return null;\r\n const doc=box.ownerDocument;box.textContent='';\r\n const row=(label,min,max,step,value,fmt,save)=>{const l=doc.createElement('label');l.style.cssText='display:flex;gap:8px;align-items:center;justify-content:space-between';\r\n  const t=doc.createElement('span'),i=doc.createElement('input');i.type='range';i.min=min;i.max=max;i.step=step;i.value=value;t.textContent=`${label} ${fmt(value)}`;\r\n  i.oninput=()=>{t.textContent=`${label} ${fmt(save(i.value))}`;};l.append(t,i);box.append(l);return i;};\r\n const fov=row('Camera FOV',VFOV_MIN,VFOV_MAX,1,readVfov(),v=>`${Math.round(v)}°`,saveVfov);fov.dataset.arVfov='';\r\n const sc=row('AR scale',ARSCALE_MIN,ARSCALE_MAX,.1,readArScale(''),v=>`${(+v).toFixed(1)}x`,saveArScale);sc.dataset.arScale='';\r\n const note=doc.createElement('small');note.textContent='Applies next time you peer. Match a doorway 3 m away to calibrate FOV.';box.append(note);\r\n return {fov,scale:sc};\r\n}\r\n","modules/coach-world-rts/atlas/bodies-arms-shoulders.json":"{\"unit\":\"bodies-arms-shoulders\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":63.8,\"image\":\"bodies-arms-shoulders.png\",\"mask\":\"bodies-arms-shoulders-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1600],\"faction\":\"arms-shoulders\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,34.0],[21.3,31.8],[21.0,29.0],[17.7,27.1],[13.5,27.3]]}},\"units\":{\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\":{\"key\":\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"kind\":\"m\",\"ppu\":8.25,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,34.0],[21.3,31.8],[21.0,29.0],[17.7,27.1],[13.5,27.3]]},\"roster/08-shard-asym--robot_3d_model4\":{\"key\":\"08-shard-asym--robot_3d_model4\",\"kind\":\"m\",\"ppu\":4.62,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.5,32.7],[19.7,30.9],[18.8,29.1],[16.2,28.3],[13.5,28.9]]},\"roster/17-shellcap-manyarm--3d_character_figurine\":{\"key\":\"17-shellcap-manyarm--3d_character_figurine\",\"kind\":\"m\",\"ppu\":5.98,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,33.2],[20.3,31.3],[19.6,29.1],[16.8,27.9],[13.5,28.3]]},\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\":{\"key\":\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"kind\":\"m\",\"ppu\":3.85,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,32.4],[19.4,30.8],[18.3,29.2],[15.9,28.6],[13.5,29.3]]},\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\":{\"key\":\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"kind\":\"m\",\"ppu\":5.8,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,33.1],[20.2,31.2],[19.5,29.1],[16.7,27.9],[13.5,28.4]]},\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\":{\"key\":\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"kind\":\"m\",\"ppu\":7.41,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.5,33.7],[20.9,31.6],[20.5,29.0],[17.4,27.4],[13.5,27.7]]},\"roster/19-genie-multi--multi-armed_humanoid_3d_model\":{\"key\":\"19-genie-multi--multi-armed_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":7.62,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.4,33.7],[21.0,31.7],[20.6,29.0],[17.5,27.4],[13.6,27.6]]},\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\":{\"key\":\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"kind\":\"m\",\"ppu\":8.66,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1400,32,40],[32,1400,32,40]],[[0,1440,32,40],[32,1440,32,40]],[[0,1480,32,40],[32,1480,32,40]],[[0,1520,32,40],[32,1520,32,40]],[[0,1560,32,40],[32,1560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1400,32,40],[96,1400,32,40],[128,1400,32,40],[160,1400,32,40],[192,1400,32,40],[224,1400,32,40]],[[64,1440,32,40],[96,1440,32,40],[128,1440,32,40],[160,1440,32,40],[192,1440,32,40],[224,1440,32,40]],[[64,1480,32,40],[96,1480,32,40],[128,1480,32,40],[160,1480,32,40],[192,1480,32,40],[224,1480,32,40]],[[64,1520,32,40],[96,1520,32,40],[128,1520,32,40],[160,1520,32,40],[192,1520,32,40],[224,1520,32,40]],[[64,1560,32,40],[96,1560,32,40],[128,1560,32,40],[160,1560,32,40],[192,1560,32,40],[224,1560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1400,32,40],[288,1400,32,40],[320,1400,32,40],[352,1400,32,40]],[[256,1440,32,40],[288,1440,32,40],[320,1440,32,40],[352,1440,32,40]],[[256,1480,32,40],[288,1480,32,40],[320,1480,32,40],[352,1480,32,40]],[[256,1520,32,40],[288,1520,32,40],[320,1520,32,40],[352,1520,32,40]],[[256,1560,32,40],[288,1560,32,40],[320,1560,32,40],[352,1560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1400,32,40],[416,1400,32,40],[448,1400,32,40]],[[384,1440,32,40],[416,1440,32,40],[448,1440,32,40]],[[384,1480,32,40],[416,1480,32,40],[448,1480,32,40]],[[384,1520,32,40],[416,1520,32,40],[448,1520,32,40]],[[384,1560,32,40],[416,1560,32,40],[448,1560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1400,32,40],[512,1400,32,40]],[[480,1440,32,40],[512,1440,32,40]],[[480,1480,32,40],[512,1480,32,40]],[[480,1520,32,40],[512,1520,32,40]],[[480,1560,32,40],[512,1560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1400,32,40],[576,1400,32,40],[608,1400,32,40],[640,1400,32,40]],[[544,1440,32,40],[576,1440,32,40],[608,1440,32,40],[640,1440,32,40]],[[544,1480,32,40],[576,1480,32,40],[608,1480,32,40],[640,1480,32,40]],[[544,1520,32,40],[576,1520,32,40],[608,1520,32,40],[640,1520,32,40]],[[544,1560,32,40],[576,1560,32,40],[608,1560,32,40],[640,1560,32,40]]]}},\"strike\":[[18.5,34.1],[21.4,31.9],[21.2,29.0],[17.9,27.0],[13.5,27.2]]}}}","modules/coach-world-rts/atlas/bodies-cardio.json":"{\"unit\":\"bodies-cardio\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":42.8,\"image\":\"bodies-cardio.png\",\"mask\":\"bodies-cardio-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1400],\"faction\":\"cardio\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,34.1],[21.4,31.9],[21.1,29.0],[17.9,27.0],[13.5,27.2]]}},\"units\":{\"roster/01-seed-pearo--blank_toy_figure_3d_model\":{\"key\":\"01-seed-pearo--blank_toy_figure_3d_model\",\"kind\":\"m\",\"ppu\":8.57,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,34.1],[21.4,31.9],[21.1,29.0],[17.9,27.0],[13.5,27.2]]},\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\":{\"key\":\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":7.67,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.4,33.8],[20.9,31.7],[20.6,29.1],[17.6,27.4],[13.6,27.6]]},\"roster/03-pearl-orb-ring--robot_3d_model\":{\"key\":\"03-pearl-orb-ring--robot_3d_model\",\"kind\":\"m\",\"ppu\":7.47,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,33.7],[21.0,31.6],[20.5,29.0],[17.4,27.4],[13.5,27.7]]},\"roster/06-ridge-triad--robot_3d_model1\":{\"key\":\"06-ridge-triad--robot_3d_model1\",\"kind\":\"m\",\"ppu\":7.64,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.3,33.7],[20.8,31.8],[20.6,29.2],[17.6,27.4],[13.7,27.6]]},\"roster/07-bulb-sphereling--cute_robot_3d_model\":{\"key\":\"07-bulb-sphereling--cute_robot_3d_model\",\"kind\":\"m\",\"ppu\":7.74,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,33.8],[21.1,31.7],[20.7,29.0],[17.5,27.3],[13.5,27.6]]},\"roster/07-bulb-sphereling--humanoid_robot_3d_model\":{\"key\":\"07-bulb-sphereling--humanoid_robot_3d_model\",\"kind\":\"m\",\"ppu\":10.4,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.3,34.7],[22.1,32.4],[22.2,29.0],[18.8,26.5],[13.7,26.4]]},\"roster/20-lume--robotic_figure_3d_model\":{\"key\":\"20-lume--robotic_figure_3d_model\",\"kind\":\"m\",\"ppu\":7.7,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.3,33.8],[20.9,31.8],[20.6,29.1],[17.6,27.4],[13.7,27.6]]}}}","modules/coach-world-rts/atlas/bodies-chest.json":"{\"unit\":\"bodies-chest\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":45.7,\"image\":\"bodies-chest.png\",\"mask\":\"bodies-chest-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1600],\"faction\":\"chest\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,32.9],[20.0,31.1],[19.1,29.1],[16.4,28.1],[13.5,28.7]]}},\"units\":{\"roster/04-crest-wedge--robot_creature_3d_model\":{\"key\":\"04-crest-wedge--robot_creature_3d_model\",\"kind\":\"m\",\"ppu\":5.15,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,32.9],[20.0,31.1],[19.1,29.1],[16.4,28.1],[13.5,28.7]]},\"roster/04-crest-wedge--stylized_3d_character\":{\"key\":\"04-crest-wedge--stylized_3d_character\",\"kind\":\"m\",\"ppu\":8.53,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.5,34.1],[21.4,31.9],[21.1,29.0],[17.9,27.0],[13.5,27.2]]},\"roster/06-ridge-triad--geometric_robot_3d_model1\":{\"key\":\"06-ridge-triad--geometric_robot_3d_model1\",\"kind\":\"m\",\"ppu\":5.33,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,32.9],[20.0,31.1],[19.2,29.1],[16.5,28.1],[13.5,28.6]]},\"roster/08-shard-asym--geometric_robot_3d_model\":{\"key\":\"08-shard-asym--geometric_robot_3d_model\",\"kind\":\"m\",\"ppu\":8.13,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.4,33.9],[21.2,31.8],[20.9,29.0],[17.7,27.2],[13.6,27.4]]},\"roster/09-monolith-tanka--3d_robot_model\":{\"key\":\"09-monolith-tanka--3d_robot_model\",\"kind\":\"m\",\"ppu\":4.91,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,32.8],[19.9,31.0],[18.9,29.1],[16.3,28.2],[13.5,28.8]]},\"roster/09-monolith-tanka--white_horned_robot_3d_model\":{\"key\":\"09-monolith-tanka--white_horned_robot_3d_model\",\"kind\":\"m\",\"ppu\":6.57,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.5,33.4],[20.6,31.4],[19.9,29.0],[17.0,27.7],[13.5,28.1]]},\"roster/16-spade-arch--pyramid_head_figure_3d_model\":{\"key\":\"16-spade-arch--pyramid_head_figure_3d_model\",\"kind\":\"m\",\"ppu\":7.21,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.5,33.6],[20.8,31.6],[20.3,29.0],[17.3,27.5],[13.5,27.8]]},\"roster/16-spade-arch--stylized_humanoid_3d_model\":{\"key\":\"16-spade-arch--stylized_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":6.94,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1400,32,40],[32,1400,32,40]],[[0,1440,32,40],[32,1440,32,40]],[[0,1480,32,40],[32,1480,32,40]],[[0,1520,32,40],[32,1520,32,40]],[[0,1560,32,40],[32,1560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1400,32,40],[96,1400,32,40],[128,1400,32,40],[160,1400,32,40],[192,1400,32,40],[224,1400,32,40]],[[64,1440,32,40],[96,1440,32,40],[128,1440,32,40],[160,1440,32,40],[192,1440,32,40],[224,1440,32,40]],[[64,1480,32,40],[96,1480,32,40],[128,1480,32,40],[160,1480,32,40],[192,1480,32,40],[224,1480,32,40]],[[64,1520,32,40],[96,1520,32,40],[128,1520,32,40],[160,1520,32,40],[192,1520,32,40],[224,1520,32,40]],[[64,1560,32,40],[96,1560,32,40],[128,1560,32,40],[160,1560,32,40],[192,1560,32,40],[224,1560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1400,32,40],[288,1400,32,40],[320,1400,32,40],[352,1400,32,40]],[[256,1440,32,40],[288,1440,32,40],[320,1440,32,40],[352,1440,32,40]],[[256,1480,32,40],[288,1480,32,40],[320,1480,32,40],[352,1480,32,40]],[[256,1520,32,40],[288,1520,32,40],[320,1520,32,40],[352,1520,32,40]],[[256,1560,32,40],[288,1560,32,40],[320,1560,32,40],[352,1560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1400,32,40],[416,1400,32,40],[448,1400,32,40]],[[384,1440,32,40],[416,1440,32,40],[448,1440,32,40]],[[384,1480,32,40],[416,1480,32,40],[448,1480,32,40]],[[384,1520,32,40],[416,1520,32,40],[448,1520,32,40]],[[384,1560,32,40],[416,1560,32,40],[448,1560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1400,32,40],[512,1400,32,40]],[[480,1440,32,40],[512,1440,32,40]],[[480,1480,32,40],[512,1480,32,40]],[[480,1520,32,40],[512,1520,32,40]],[[480,1560,32,40],[512,1560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1400,32,40],[576,1400,32,40],[608,1400,32,40],[640,1400,32,40]],[[544,1440,32,40],[576,1440,32,40],[608,1440,32,40],[640,1440,32,40]],[[544,1480,32,40],[576,1480,32,40],[608,1480,32,40],[640,1480,32,40]],[[544,1520,32,40],[576,1520,32,40],[608,1520,32,40],[640,1520,32,40]],[[544,1560,32,40],[576,1560,32,40],[608,1560,32,40],[640,1560,32,40]]]}},\"strike\":[[18.5,33.5],[20.7,31.5],[20.2,29.0],[17.2,27.5],[13.5,27.9]]}}}","modules/coach-world-rts/atlas/bodies-glutes.json":"{\"unit\":\"bodies-glutes\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":52.2,\"image\":\"bodies-glutes.png\",\"mask\":\"bodies-glutes-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1400],\"faction\":\"glutes\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,32.8],[19.9,31.0],[19.0,29.1],[16.3,28.2],[13.5,28.8]]}},\"units\":{\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\":{\"key\":\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":4.96,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,32.8],[19.9,31.0],[19.0,29.1],[16.3,28.2],[13.5,28.8]]},\"roster/03-pearl-orb-ring--circle_center\":{\"key\":\"03-pearl-orb-ring--circle_center\",\"kind\":\"m\",\"ppu\":7.67,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.4,33.8],[20.9,31.7],[20.6,29.1],[17.6,27.4],[13.6,27.6]]},\"roster/03-pearl-orb-ring--stylized_3d_character1\":{\"key\":\"03-pearl-orb-ring--stylized_3d_character1\",\"kind\":\"m\",\"ppu\":7.06,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,33.5],[20.8,31.5],[20.2,29.0],[17.2,27.5],[13.5,27.9]]},\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\":{\"key\":\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"kind\":\"m\",\"ppu\":8.46,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,34.0],[21.4,31.9],[21.1,29.0],[17.8,27.0],[13.5,27.2]]},\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\":{\"key\":\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"kind\":\"m\",\"ppu\":3.81,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.2,32.4],[19.2,30.9],[18.3,29.4],[16.0,28.7],[13.8,29.3]]},\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\":{\"key\":\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":6.03,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.5,33.2],[20.3,31.3],[19.6,29.1],[16.8,27.8],[13.5,28.3]]},\"roster/23-blob-texture-bodies--sand_creature_3d_model\":{\"key\":\"23-blob-texture-bodies--sand_creature_3d_model\",\"kind\":\"m\",\"ppu\":5.37,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.5,32.9],[20.1,31.1],[19.2,29.1],[16.5,28.1],[13.5,28.6]]}}}","modules/coach-world-rts/atlas/bodies-martial-arts.json":"{\"unit\":\"bodies-martial-arts\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":52.3,\"image\":\"bodies-martial-arts.png\",\"mask\":\"bodies-martial-arts-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1600],\"faction\":\"martial-arts\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,32.8],[19.9,31.0],[19.1,29.1],[16.4,28.1],[13.5,28.7]]}},\"units\":{\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\":{\"key\":\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"kind\":\"m\",\"ppu\":5.09,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,32.8],[19.9,31.0],[19.1,29.1],[16.4,28.1],[13.5,28.7]]},\"roster/08-shard-asym--fantasy_creature_3d_model2\":{\"key\":\"08-shard-asym--fantasy_creature_3d_model2\",\"kind\":\"m\",\"ppu\":6.84,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.3,33.5],[20.5,31.6],[20.1,29.2],[17.3,27.7],[13.7,28.0]]},\"roster/09-monolith-tanka--boxy_humanoid_3d_model\":{\"key\":\"09-monolith-tanka--boxy_humanoid_3d_model\",\"kind\":\"m\",\"ppu\":8.5,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.2,34.1],[21.2,32.0],[21.1,29.2],[18.1,27.2],[13.8,27.2]]},\"roster/09-monolith-tanka--mini_robot_3d_model\":{\"key\":\"09-monolith-tanka--mini_robot_3d_model\",\"kind\":\"m\",\"ppu\":7.02,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,33.5],[20.8,31.5],[20.2,29.0],[17.2,27.5],[13.5,27.9]]},\"roster/09-monolith-tanka--robot_3d_model3\":{\"key\":\"09-monolith-tanka--robot_3d_model3\",\"kind\":\"m\",\"ppu\":6.88,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,33.5],[20.7,31.5],[20.1,29.0],[17.1,27.6],[13.5,27.9]]},\"roster/11-anvil-cask--clay-style_robot_3d_model\":{\"key\":\"11-anvil-cask--clay-style_robot_3d_model\",\"kind\":\"m\",\"ppu\":4.68,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.5,32.7],[19.8,30.9],[18.8,29.1],[16.2,28.3],[13.5,28.9]]},\"roster/13-fan-split--stylized_toy_3d_model\":{\"key\":\"13-fan-split--stylized_toy_3d_model\",\"kind\":\"m\",\"ppu\":7.19,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.5,33.6],[20.8,31.5],[20.3,29.0],[17.3,27.5],[13.5,27.8]]},\"roster/14-chisel-spire--cone_head_3d_model\":{\"key\":\"14-chisel-spire--cone_head_3d_model\",\"kind\":\"m\",\"ppu\":9.82,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1400,32,40],[32,1400,32,40]],[[0,1440,32,40],[32,1440,32,40]],[[0,1480,32,40],[32,1480,32,40]],[[0,1520,32,40],[32,1520,32,40]],[[0,1560,32,40],[32,1560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1400,32,40],[96,1400,32,40],[128,1400,32,40],[160,1400,32,40],[192,1400,32,40],[224,1400,32,40]],[[64,1440,32,40],[96,1440,32,40],[128,1440,32,40],[160,1440,32,40],[192,1440,32,40],[224,1440,32,40]],[[64,1480,32,40],[96,1480,32,40],[128,1480,32,40],[160,1480,32,40],[192,1480,32,40],[224,1480,32,40]],[[64,1520,32,40],[96,1520,32,40],[128,1520,32,40],[160,1520,32,40],[192,1520,32,40],[224,1520,32,40]],[[64,1560,32,40],[96,1560,32,40],[128,1560,32,40],[160,1560,32,40],[192,1560,32,40],[224,1560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1400,32,40],[288,1400,32,40],[320,1400,32,40],[352,1400,32,40]],[[256,1440,32,40],[288,1440,32,40],[320,1440,32,40],[352,1440,32,40]],[[256,1480,32,40],[288,1480,32,40],[320,1480,32,40],[352,1480,32,40]],[[256,1520,32,40],[288,1520,32,40],[320,1520,32,40],[352,1520,32,40]],[[256,1560,32,40],[288,1560,32,40],[320,1560,32,40],[352,1560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1400,32,40],[416,1400,32,40],[448,1400,32,40]],[[384,1440,32,40],[416,1440,32,40],[448,1440,32,40]],[[384,1480,32,40],[416,1480,32,40],[448,1480,32,40]],[[384,1520,32,40],[416,1520,32,40],[448,1520,32,40]],[[384,1560,32,40],[416,1560,32,40],[448,1560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1400,32,40],[512,1400,32,40]],[[480,1440,32,40],[512,1440,32,40]],[[480,1480,32,40],[512,1480,32,40]],[[480,1520,32,40],[512,1520,32,40]],[[480,1560,32,40],[512,1560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1400,32,40],[576,1400,32,40],[608,1400,32,40],[640,1400,32,40]],[[544,1440,32,40],[576,1440,32,40],[608,1440,32,40],[640,1440,32,40]],[[544,1480,32,40],[576,1480,32,40],[608,1480,32,40],[640,1480,32,40]],[[544,1520,32,40],[576,1520,32,40],[608,1520,32,40],[640,1520,32,40]],[[544,1560,32,40],[576,1560,32,40],[608,1560,32,40],[640,1560,32,40]]]}},\"strike\":[[18.5,34.5],[22.0,32.2],[21.9,28.9],[18.4,26.6],[13.5,26.6]]}}}","modules/coach-world-rts/atlas/bodies-neutral.json":"{\"unit\":\"bodies-neutral\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":58.1,\"image\":\"bodies-neutral.png\",\"mask\":\"bodies-neutral-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1000],\"faction\":\"neutral\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,34.1],[21.5,31.9],[21.2,28.9],[17.9,27.0],[13.5,27.1]]}},\"units\":{\"roster/01-seed-pearo--3d_character_model\":{\"key\":\"01-seed-pearo--3d_character_model\",\"kind\":\"m\",\"ppu\":8.67,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,34.1],[21.5,31.9],[21.2,28.9],[17.9,27.0],[13.5,27.1]]},\"roster/12-seedpod-snailslug--stylized_slug_3d_model\":{\"key\":\"12-seedpod-snailslug--stylized_slug_3d_model\",\"kind\":\"m\",\"ppu\":4.78,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.2,32.7],[19.6,31.1],[18.9,29.3],[16.5,28.4],[13.8,28.9]]},\"roster/12-seedpod-snailslug--stylized_worm_3d_model\":{\"key\":\"12-seedpod-snailslug--stylized_worm_3d_model\",\"kind\":\"m\",\"ppu\":4.14,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.0,32.5],[19.2,31.1],[18.5,29.5],[16.3,28.7],[14.0,29.2]]},\"roster/14-chisel-spire--low_poly_robot_3d_model\":{\"key\":\"14-chisel-spire--low_poly_robot_3d_model\",\"kind\":\"m\",\"ppu\":7.05,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,33.5],[20.8,31.5],[20.2,29.0],[17.2,27.5],[13.5,27.9]]},\"roster/23-blob-texture-bodies--blob_creature_3d_model\":{\"key\":\"23-blob-texture-bodies--blob_creature_3d_model\",\"kind\":\"m\",\"ppu\":3.92,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.0,32.4],[19.1,31.0],[18.4,29.5],[16.3,28.8],[14.0,29.3]]}}}","modules/coach-world-rts/atlas/bodies-pets.json":"{\"unit\":\"bodies-pets\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":45.1,\"image\":\"bodies-pets.png\",\"mask\":\"bodies-pets-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1600],\"faction\":\"pets\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[17.9,32.7],[19.0,31.3],[18.4,29.9],[16.3,29.1],[14.1,29.6]]}},\"units\":{\"roster/18-quad-all--dragon_creature_3d_model\":{\"key\":\"18-quad-all--dragon_creature_3d_model\",\"kind\":\"m\",\"ppu\":4.58,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[17.9,32.7],[19.0,31.3],[18.4,29.9],[16.3,29.1],[14.1,29.6]]},\"roster/18-quad-all--fantasy_creature_3d_model1\":{\"key\":\"18-quad-all--fantasy_creature_3d_model1\",\"kind\":\"m\",\"ppu\":4.84,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.3,32.8],[19.4,31.2],[18.5,29.6],[16.1,28.9],[13.7,29.5]]},\"roster/18-quad-all--fantasy_creature_3d_model3\":{\"key\":\"18-quad-all--fantasy_creature_3d_model3\",\"kind\":\"m\",\"ppu\":4.89,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[17.9,32.8],[19.1,31.4],[18.5,29.9],[16.4,29.1],[14.1,29.5]]},\"roster/18-quad-all--four-legged_robot_3d_model\":{\"key\":\"18-quad-all--four-legged_robot_3d_model\",\"kind\":\"m\",\"ppu\":4.42,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.0,32.6],[19.0,31.3],[18.3,29.8],[16.2,29.1],[14.0,29.6]]},\"roster/18-quad-all--quadruped_robot_3d_model1\":{\"key\":\"18-quad-all--quadruped_robot_3d_model1\",\"kind\":\"m\",\"ppu\":3.17,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[17.9,32.2],[18.5,31.0],[17.6,29.8],[15.8,29.4],[14.1,30.0]]},\"roster/18-quad-all--quadruped_robot_3d_model\":{\"key\":\"18-quad-all--quadruped_robot_3d_model\",\"kind\":\"m\",\"ppu\":6.33,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.0,33.4],[19.7,31.8],[19.3,29.9],[16.9,28.7],[14.0,29.0]]},\"roster/18-quad-all--robotic_dog_3d_model\":{\"key\":\"18-quad-all--robotic_dog_3d_model\",\"kind\":\"m\",\"ppu\":2.95,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[17.9,32.1],[18.4,30.9],[17.5,29.8],[15.7,29.5],[14.1,30.1]]},\"roster/18-quad-all--wooden_four-legged_robot_3d_model\":{\"key\":\"18-quad-all--wooden_four-legged_robot_3d_model\",\"kind\":\"m\",\"ppu\":1.95,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1400,32,40],[32,1400,32,40]],[[0,1440,32,40],[32,1440,32,40]],[[0,1480,32,40],[32,1480,32,40]],[[0,1520,32,40],[32,1520,32,40]],[[0,1560,32,40],[32,1560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1400,32,40],[96,1400,32,40],[128,1400,32,40],[160,1400,32,40],[192,1400,32,40],[224,1400,32,40]],[[64,1440,32,40],[96,1440,32,40],[128,1440,32,40],[160,1440,32,40],[192,1440,32,40],[224,1440,32,40]],[[64,1480,32,40],[96,1480,32,40],[128,1480,32,40],[160,1480,32,40],[192,1480,32,40],[224,1480,32,40]],[[64,1520,32,40],[96,1520,32,40],[128,1520,32,40],[160,1520,32,40],[192,1520,32,40],[224,1520,32,40]],[[64,1560,32,40],[96,1560,32,40],[128,1560,32,40],[160,1560,32,40],[192,1560,32,40],[224,1560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1400,32,40],[288,1400,32,40],[320,1400,32,40],[352,1400,32,40]],[[256,1440,32,40],[288,1440,32,40],[320,1440,32,40],[352,1440,32,40]],[[256,1480,32,40],[288,1480,32,40],[320,1480,32,40],[352,1480,32,40]],[[256,1520,32,40],[288,1520,32,40],[320,1520,32,40],[352,1520,32,40]],[[256,1560,32,40],[288,1560,32,40],[320,1560,32,40],[352,1560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1400,32,40],[416,1400,32,40],[448,1400,32,40]],[[384,1440,32,40],[416,1440,32,40],[448,1440,32,40]],[[384,1480,32,40],[416,1480,32,40],[448,1480,32,40]],[[384,1520,32,40],[416,1520,32,40],[448,1520,32,40]],[[384,1560,32,40],[416,1560,32,40],[448,1560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1400,32,40],[512,1400,32,40]],[[480,1440,32,40],[512,1440,32,40]],[[480,1480,32,40],[512,1480,32,40]],[[480,1520,32,40],[512,1520,32,40]],[[480,1560,32,40],[512,1560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1400,32,40],[576,1400,32,40],[608,1400,32,40],[640,1400,32,40]],[[544,1440,32,40],[576,1440,32,40],[608,1440,32,40],[640,1440,32,40]],[[544,1480,32,40],[576,1480,32,40],[608,1480,32,40],[640,1480,32,40]],[[544,1520,32,40],[576,1520,32,40],[608,1520,32,40],[640,1520,32,40]],[[544,1560,32,40],[576,1560,32,40],[608,1560,32,40],[640,1560,32,40]]]}},\"strike\":[[18.0,31.7],[18.1,30.6],[17.0,29.7],[15.3,29.6],[14.0,30.4]]}}}","modules/coach-world-rts/atlas/bodies-quads.json":"{\"unit\":\"bodies-quads\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":60.0,\"image\":\"bodies-quads.png\",\"mask\":\"bodies-quads-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1000],\"faction\":\"quads\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,34.0],[21.4,31.9],[21.1,29.0],[17.8,27.1],[13.5,27.3]]}},\"units\":{\"roster/02-taper-tallstalk--3d_humanoid_figure\":{\"key\":\"02-taper-tallstalk--3d_humanoid_figure\",\"kind\":\"m\",\"ppu\":8.42,\"teamObjs\":[\"body_mesh\",\"head_mesh\",\"arms_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,34.0],[21.4,31.9],[21.1,29.0],[17.8,27.1],[13.5,27.3]]},\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\":{\"key\":\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"kind\":\"m\",\"ppu\":8.92,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.5,34.2],[21.6,32.0],[21.4,28.9],[18.0,26.9],[13.5,27.0]]},\"roster/13-fan-split--stylized_3d_character2\":{\"key\":\"13-fan-split--stylized_3d_character2\",\"kind\":\"m\",\"ppu\":6.63,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,33.4],[20.6,31.4],[20.0,29.0],[17.0,27.6],[13.5,28.1]]},\"roster/15-orbital-coili--robot_character_3d_model\":{\"key\":\"15-orbital-coili--robot_character_3d_model\",\"kind\":\"m\",\"ppu\":8.34,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,34.0],[21.3,31.8],[21.0,29.0],[17.8,27.1],[13.5,27.3]]},\"roster/22-curve--stylized_cartoon_figure_3d_model\":{\"key\":\"22-curve--stylized_cartoon_figure_3d_model\",\"kind\":\"m\",\"ppu\":8.94,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,34.2],[21.6,32.0],[21.4,28.9],[18.0,26.9],[13.5,27.0]]}}}","modules/coach-world-rts/atlas/bodies-yoga.json":"{\"unit\":\"bodies-yoga\",\"cell\":[32,40],\"anchor\":[16,31],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[165,160,170],\"teamEdges\":[70,130,190],\"teamPct\":44.5,\"image\":\"bodies-yoga.png\",\"mask\":\"bodies-yoga-mask.png\",\"idle\":{\"bobPx\":0,\"bobMs\":1},\"sheet\":[672,1600],\"faction\":\"yoga\",\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"fx\":{\"spark\":{\"sheet\":\"bodies-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[18.5,34.0],[21.4,31.8],[21.1,29.0],[17.8,27.1],[13.5,27.3]]}},\"units\":{\"roster/01-seed-pearo--cute_alien_figure_3d_model\":{\"key\":\"01-seed-pearo--cute_alien_figure_3d_model\",\"kind\":\"m\",\"ppu\":8.42,\"teamObjs\":[\"body_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,0,32,40],[32,0,32,40]],[[0,40,32,40],[32,40,32,40]],[[0,80,32,40],[32,80,32,40]],[[0,120,32,40],[32,120,32,40]],[[0,160,32,40],[32,160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,0,32,40],[96,0,32,40],[128,0,32,40],[160,0,32,40],[192,0,32,40],[224,0,32,40]],[[64,40,32,40],[96,40,32,40],[128,40,32,40],[160,40,32,40],[192,40,32,40],[224,40,32,40]],[[64,80,32,40],[96,80,32,40],[128,80,32,40],[160,80,32,40],[192,80,32,40],[224,80,32,40]],[[64,120,32,40],[96,120,32,40],[128,120,32,40],[160,120,32,40],[192,120,32,40],[224,120,32,40]],[[64,160,32,40],[96,160,32,40],[128,160,32,40],[160,160,32,40],[192,160,32,40],[224,160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,0,32,40],[288,0,32,40],[320,0,32,40],[352,0,32,40]],[[256,40,32,40],[288,40,32,40],[320,40,32,40],[352,40,32,40]],[[256,80,32,40],[288,80,32,40],[320,80,32,40],[352,80,32,40]],[[256,120,32,40],[288,120,32,40],[320,120,32,40],[352,120,32,40]],[[256,160,32,40],[288,160,32,40],[320,160,32,40],[352,160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,0,32,40],[416,0,32,40],[448,0,32,40]],[[384,40,32,40],[416,40,32,40],[448,40,32,40]],[[384,80,32,40],[416,80,32,40],[448,80,32,40]],[[384,120,32,40],[416,120,32,40],[448,120,32,40]],[[384,160,32,40],[416,160,32,40],[448,160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,0,32,40],[512,0,32,40]],[[480,40,32,40],[512,40,32,40]],[[480,80,32,40],[512,80,32,40]],[[480,120,32,40],[512,120,32,40]],[[480,160,32,40],[512,160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,0,32,40],[576,0,32,40],[608,0,32,40],[640,0,32,40]],[[544,40,32,40],[576,40,32,40],[608,40,32,40],[640,40,32,40]],[[544,80,32,40],[576,80,32,40],[608,80,32,40],[640,80,32,40]],[[544,120,32,40],[576,120,32,40],[608,120,32,40],[640,120,32,40]],[[544,160,32,40],[576,160,32,40],[608,160,32,40],[640,160,32,40]]]}},\"strike\":[[18.5,34.0],[21.4,31.8],[21.1,29.0],[17.8,27.1],[13.5,27.3]]},\"roster/08-shard-asym--stylized_action_figure_3d_model\":{\"key\":\"08-shard-asym--stylized_action_figure_3d_model\",\"kind\":\"m\",\"ppu\":7.12,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,200,32,40],[32,200,32,40]],[[0,240,32,40],[32,240,32,40]],[[0,280,32,40],[32,280,32,40]],[[0,320,32,40],[32,320,32,40]],[[0,360,32,40],[32,360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,200,32,40],[96,200,32,40],[128,200,32,40],[160,200,32,40],[192,200,32,40],[224,200,32,40]],[[64,240,32,40],[96,240,32,40],[128,240,32,40],[160,240,32,40],[192,240,32,40],[224,240,32,40]],[[64,280,32,40],[96,280,32,40],[128,280,32,40],[160,280,32,40],[192,280,32,40],[224,280,32,40]],[[64,320,32,40],[96,320,32,40],[128,320,32,40],[160,320,32,40],[192,320,32,40],[224,320,32,40]],[[64,360,32,40],[96,360,32,40],[128,360,32,40],[160,360,32,40],[192,360,32,40],[224,360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,200,32,40],[288,200,32,40],[320,200,32,40],[352,200,32,40]],[[256,240,32,40],[288,240,32,40],[320,240,32,40],[352,240,32,40]],[[256,280,32,40],[288,280,32,40],[320,280,32,40],[352,280,32,40]],[[256,320,32,40],[288,320,32,40],[320,320,32,40],[352,320,32,40]],[[256,360,32,40],[288,360,32,40],[320,360,32,40],[352,360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,200,32,40],[416,200,32,40],[448,200,32,40]],[[384,240,32,40],[416,240,32,40],[448,240,32,40]],[[384,280,32,40],[416,280,32,40],[448,280,32,40]],[[384,320,32,40],[416,320,32,40],[448,320,32,40]],[[384,360,32,40],[416,360,32,40],[448,360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,200,32,40],[512,200,32,40]],[[480,240,32,40],[512,240,32,40]],[[480,280,32,40],[512,280,32,40]],[[480,320,32,40],[512,320,32,40]],[[480,360,32,40],[512,360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,200,32,40],[576,200,32,40],[608,200,32,40],[640,200,32,40]],[[544,240,32,40],[576,240,32,40],[608,240,32,40],[640,240,32,40]],[[544,280,32,40],[576,280,32,40],[608,280,32,40],[640,280,32,40]],[[544,320,32,40],[576,320,32,40],[608,320,32,40],[640,320,32,40]],[[544,360,32,40],[576,360,32,40],[608,360,32,40],[640,360,32,40]]]}},\"strike\":[[18.5,33.6],[20.8,31.5],[20.3,29.0],[17.2,27.5],[13.5,27.8]]},\"roster/10-petal-wisp--stylized_creature_3d_model\":{\"key\":\"10-petal-wisp--stylized_creature_3d_model\",\"kind\":\"m\",\"ppu\":8.0,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,400,32,40],[32,400,32,40]],[[0,440,32,40],[32,440,32,40]],[[0,480,32,40],[32,480,32,40]],[[0,520,32,40],[32,520,32,40]],[[0,560,32,40],[32,560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,400,32,40],[96,400,32,40],[128,400,32,40],[160,400,32,40],[192,400,32,40],[224,400,32,40]],[[64,440,32,40],[96,440,32,40],[128,440,32,40],[160,440,32,40],[192,440,32,40],[224,440,32,40]],[[64,480,32,40],[96,480,32,40],[128,480,32,40],[160,480,32,40],[192,480,32,40],[224,480,32,40]],[[64,520,32,40],[96,520,32,40],[128,520,32,40],[160,520,32,40],[192,520,32,40],[224,520,32,40]],[[64,560,32,40],[96,560,32,40],[128,560,32,40],[160,560,32,40],[192,560,32,40],[224,560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,400,32,40],[288,400,32,40],[320,400,32,40],[352,400,32,40]],[[256,440,32,40],[288,440,32,40],[320,440,32,40],[352,440,32,40]],[[256,480,32,40],[288,480,32,40],[320,480,32,40],[352,480,32,40]],[[256,520,32,40],[288,520,32,40],[320,520,32,40],[352,520,32,40]],[[256,560,32,40],[288,560,32,40],[320,560,32,40],[352,560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,400,32,40],[416,400,32,40],[448,400,32,40]],[[384,440,32,40],[416,440,32,40],[448,440,32,40]],[[384,480,32,40],[416,480,32,40],[448,480,32,40]],[[384,520,32,40],[416,520,32,40],[448,520,32,40]],[[384,560,32,40],[416,560,32,40],[448,560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,400,32,40],[512,400,32,40]],[[480,440,32,40],[512,440,32,40]],[[480,480,32,40],[512,480,32,40]],[[480,520,32,40],[512,520,32,40]],[[480,560,32,40],[512,560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,400,32,40],[576,400,32,40],[608,400,32,40],[640,400,32,40]],[[544,440,32,40],[576,440,32,40],[608,440,32,40],[640,440,32,40]],[[544,480,32,40],[576,480,32,40],[608,480,32,40],[640,480,32,40]],[[544,520,32,40],[576,520,32,40],[608,520,32,40],[640,520,32,40]],[[544,560,32,40],[576,560,32,40],[608,560,32,40],[640,560,32,40]]]}},\"strike\":[[18.5,33.9],[21.2,31.7],[20.8,29.0],[17.6,27.2],[13.5,27.4]]},\"roster/10-petal-wisp--ghost_character_3d_model\":{\"key\":\"10-petal-wisp--ghost_character_3d_model\",\"kind\":\"m\",\"ppu\":7.24,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,600,32,40],[32,600,32,40]],[[0,640,32,40],[32,640,32,40]],[[0,680,32,40],[32,680,32,40]],[[0,720,32,40],[32,720,32,40]],[[0,760,32,40],[32,760,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,600,32,40],[96,600,32,40],[128,600,32,40],[160,600,32,40],[192,600,32,40],[224,600,32,40]],[[64,640,32,40],[96,640,32,40],[128,640,32,40],[160,640,32,40],[192,640,32,40],[224,640,32,40]],[[64,680,32,40],[96,680,32,40],[128,680,32,40],[160,680,32,40],[192,680,32,40],[224,680,32,40]],[[64,720,32,40],[96,720,32,40],[128,720,32,40],[160,720,32,40],[192,720,32,40],[224,720,32,40]],[[64,760,32,40],[96,760,32,40],[128,760,32,40],[160,760,32,40],[192,760,32,40],[224,760,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,600,32,40],[288,600,32,40],[320,600,32,40],[352,600,32,40]],[[256,640,32,40],[288,640,32,40],[320,640,32,40],[352,640,32,40]],[[256,680,32,40],[288,680,32,40],[320,680,32,40],[352,680,32,40]],[[256,720,32,40],[288,720,32,40],[320,720,32,40],[352,720,32,40]],[[256,760,32,40],[288,760,32,40],[320,760,32,40],[352,760,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,600,32,40],[416,600,32,40],[448,600,32,40]],[[384,640,32,40],[416,640,32,40],[448,640,32,40]],[[384,680,32,40],[416,680,32,40],[448,680,32,40]],[[384,720,32,40],[416,720,32,40],[448,720,32,40]],[[384,760,32,40],[416,760,32,40],[448,760,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,600,32,40],[512,600,32,40]],[[480,640,32,40],[512,640,32,40]],[[480,680,32,40],[512,680,32,40]],[[480,720,32,40],[512,720,32,40]],[[480,760,32,40],[512,760,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,600,32,40],[576,600,32,40],[608,600,32,40],[640,600,32,40]],[[544,640,32,40],[576,640,32,40],[608,640,32,40],[640,640,32,40]],[[544,680,32,40],[576,680,32,40],[608,680,32,40],[640,680,32,40]],[[544,720,32,40],[576,720,32,40],[608,720,32,40],[640,720,32,40]],[[544,760,32,40],[576,760,32,40],[608,760,32,40],[640,760,32,40]]]}},\"strike\":[[18.5,33.6],[20.9,31.6],[20.3,29.0],[17.3,27.4],[13.5,27.8]]},\"roster/10-petal-wisp--fantasy_creature_3d_model4\":{\"key\":\"10-petal-wisp--fantasy_creature_3d_model4\",\"kind\":\"m\",\"ppu\":9.75,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,800,32,40],[32,800,32,40]],[[0,840,32,40],[32,840,32,40]],[[0,880,32,40],[32,880,32,40]],[[0,920,32,40],[32,920,32,40]],[[0,960,32,40],[32,960,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,800,32,40],[96,800,32,40],[128,800,32,40],[160,800,32,40],[192,800,32,40],[224,800,32,40]],[[64,840,32,40],[96,840,32,40],[128,840,32,40],[160,840,32,40],[192,840,32,40],[224,840,32,40]],[[64,880,32,40],[96,880,32,40],[128,880,32,40],[160,880,32,40],[192,880,32,40],[224,880,32,40]],[[64,920,32,40],[96,920,32,40],[128,920,32,40],[160,920,32,40],[192,920,32,40],[224,920,32,40]],[[64,960,32,40],[96,960,32,40],[128,960,32,40],[160,960,32,40],[192,960,32,40],[224,960,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,800,32,40],[288,800,32,40],[320,800,32,40],[352,800,32,40]],[[256,840,32,40],[288,840,32,40],[320,840,32,40],[352,840,32,40]],[[256,880,32,40],[288,880,32,40],[320,880,32,40],[352,880,32,40]],[[256,920,32,40],[288,920,32,40],[320,920,32,40],[352,920,32,40]],[[256,960,32,40],[288,960,32,40],[320,960,32,40],[352,960,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,800,32,40],[416,800,32,40],[448,800,32,40]],[[384,840,32,40],[416,840,32,40],[448,840,32,40]],[[384,880,32,40],[416,880,32,40],[448,880,32,40]],[[384,920,32,40],[416,920,32,40],[448,920,32,40]],[[384,960,32,40],[416,960,32,40],[448,960,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,800,32,40],[512,800,32,40]],[[480,840,32,40],[512,840,32,40]],[[480,880,32,40],[512,880,32,40]],[[480,920,32,40],[512,920,32,40]],[[480,960,32,40],[512,960,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,800,32,40],[576,800,32,40],[608,800,32,40],[640,800,32,40]],[[544,840,32,40],[576,840,32,40],[608,840,32,40],[640,840,32,40]],[[544,880,32,40],[576,880,32,40],[608,880,32,40],[640,880,32,40]],[[544,920,32,40],[576,920,32,40],[608,920,32,40],[640,920,32,40]],[[544,960,32,40],[576,960,32,40],[608,960,32,40],[640,960,32,40]]]}},\"strike\":[[18.5,34.5],[21.9,32.2],[21.9,28.9],[18.4,26.6],[13.5,26.7]]},\"roster/19-genie-multi--fantasy_creature_3d_model\":{\"key\":\"19-genie-multi--fantasy_creature_3d_model\",\"kind\":\"m\",\"ppu\":7.46,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1000,32,40],[32,1000,32,40]],[[0,1040,32,40],[32,1040,32,40]],[[0,1080,32,40],[32,1080,32,40]],[[0,1120,32,40],[32,1120,32,40]],[[0,1160,32,40],[32,1160,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1000,32,40],[96,1000,32,40],[128,1000,32,40],[160,1000,32,40],[192,1000,32,40],[224,1000,32,40]],[[64,1040,32,40],[96,1040,32,40],[128,1040,32,40],[160,1040,32,40],[192,1040,32,40],[224,1040,32,40]],[[64,1080,32,40],[96,1080,32,40],[128,1080,32,40],[160,1080,32,40],[192,1080,32,40],[224,1080,32,40]],[[64,1120,32,40],[96,1120,32,40],[128,1120,32,40],[160,1120,32,40],[192,1120,32,40],[224,1120,32,40]],[[64,1160,32,40],[96,1160,32,40],[128,1160,32,40],[160,1160,32,40],[192,1160,32,40],[224,1160,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1000,32,40],[288,1000,32,40],[320,1000,32,40],[352,1000,32,40]],[[256,1040,32,40],[288,1040,32,40],[320,1040,32,40],[352,1040,32,40]],[[256,1080,32,40],[288,1080,32,40],[320,1080,32,40],[352,1080,32,40]],[[256,1120,32,40],[288,1120,32,40],[320,1120,32,40],[352,1120,32,40]],[[256,1160,32,40],[288,1160,32,40],[320,1160,32,40],[352,1160,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1000,32,40],[416,1000,32,40],[448,1000,32,40]],[[384,1040,32,40],[416,1040,32,40],[448,1040,32,40]],[[384,1080,32,40],[416,1080,32,40],[448,1080,32,40]],[[384,1120,32,40],[416,1120,32,40],[448,1120,32,40]],[[384,1160,32,40],[416,1160,32,40],[448,1160,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1000,32,40],[512,1000,32,40]],[[480,1040,32,40],[512,1040,32,40]],[[480,1080,32,40],[512,1080,32,40]],[[480,1120,32,40],[512,1120,32,40]],[[480,1160,32,40],[512,1160,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1000,32,40],[576,1000,32,40],[608,1000,32,40],[640,1000,32,40]],[[544,1040,32,40],[576,1040,32,40],[608,1040,32,40],[640,1040,32,40]],[[544,1080,32,40],[576,1080,32,40],[608,1080,32,40],[640,1080,32,40]],[[544,1120,32,40],[576,1120,32,40],[608,1120,32,40],[640,1120,32,40]],[[544,1160,32,40],[576,1160,32,40],[608,1160,32,40],[640,1160,32,40]]]}},\"strike\":[[18.5,33.7],[21.0,31.6],[20.5,29.0],[17.4,27.4],[13.5,27.7]]},\"roster/19-genie-multi--stylized_octopus_3d_model\":{\"key\":\"19-genie-multi--stylized_octopus_3d_model\",\"kind\":\"m\",\"ppu\":7.37,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1200,32,40],[32,1200,32,40]],[[0,1240,32,40],[32,1240,32,40]],[[0,1280,32,40],[32,1280,32,40]],[[0,1320,32,40],[32,1320,32,40]],[[0,1360,32,40],[32,1360,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1200,32,40],[96,1200,32,40],[128,1200,32,40],[160,1200,32,40],[192,1200,32,40],[224,1200,32,40]],[[64,1240,32,40],[96,1240,32,40],[128,1240,32,40],[160,1240,32,40],[192,1240,32,40],[224,1240,32,40]],[[64,1280,32,40],[96,1280,32,40],[128,1280,32,40],[160,1280,32,40],[192,1280,32,40],[224,1280,32,40]],[[64,1320,32,40],[96,1320,32,40],[128,1320,32,40],[160,1320,32,40],[192,1320,32,40],[224,1320,32,40]],[[64,1360,32,40],[96,1360,32,40],[128,1360,32,40],[160,1360,32,40],[192,1360,32,40],[224,1360,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1200,32,40],[288,1200,32,40],[320,1200,32,40],[352,1200,32,40]],[[256,1240,32,40],[288,1240,32,40],[320,1240,32,40],[352,1240,32,40]],[[256,1280,32,40],[288,1280,32,40],[320,1280,32,40],[352,1280,32,40]],[[256,1320,32,40],[288,1320,32,40],[320,1320,32,40],[352,1320,32,40]],[[256,1360,32,40],[288,1360,32,40],[320,1360,32,40],[352,1360,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1200,32,40],[416,1200,32,40],[448,1200,32,40]],[[384,1240,32,40],[416,1240,32,40],[448,1240,32,40]],[[384,1280,32,40],[416,1280,32,40],[448,1280,32,40]],[[384,1320,32,40],[416,1320,32,40],[448,1320,32,40]],[[384,1360,32,40],[416,1360,32,40],[448,1360,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1200,32,40],[512,1200,32,40]],[[480,1240,32,40],[512,1240,32,40]],[[480,1280,32,40],[512,1280,32,40]],[[480,1320,32,40],[512,1320,32,40]],[[480,1360,32,40],[512,1360,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1200,32,40],[576,1200,32,40],[608,1200,32,40],[640,1200,32,40]],[[544,1240,32,40],[576,1240,32,40],[608,1240,32,40],[640,1240,32,40]],[[544,1280,32,40],[576,1280,32,40],[608,1280,32,40],[640,1280,32,40]],[[544,1320,32,40],[576,1320,32,40],[608,1320,32,40],[640,1320,32,40]],[[544,1360,32,40],[576,1360,32,40],[608,1360,32,40],[640,1360,32,40]]]}},\"strike\":[[18.5,33.7],[20.9,31.6],[20.4,29.0],[17.3,27.4],[13.5,27.7]]},\"roster/22-curve--stylized_alien_3d_model\":{\"key\":\"22-curve--stylized_alien_3d_model\",\"kind\":\"m\",\"ppu\":7.83,\"teamObjs\":[\"body_mesh\",\"head_mesh\"],\"actions\":{\"idle\":{\"frames\":2,\"ms\":[504,504],\"rects\":[[[0,1400,32,40],[32,1400,32,40]],[[0,1440,32,40],[32,1440,32,40]],[[0,1480,32,40],[32,1480,32,40]],[[0,1520,32,40],[32,1520,32,40]],[[0,1560,32,40],[32,1560,32,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[64,1400,32,40],[96,1400,32,40],[128,1400,32,40],[160,1400,32,40],[192,1400,32,40],[224,1400,32,40]],[[64,1440,32,40],[96,1440,32,40],[128,1440,32,40],[160,1440,32,40],[192,1440,32,40],[224,1440,32,40]],[[64,1480,32,40],[96,1480,32,40],[128,1480,32,40],[160,1480,32,40],[192,1480,32,40],[224,1480,32,40]],[[64,1520,32,40],[96,1520,32,40],[128,1520,32,40],[160,1520,32,40],[192,1520,32,40],[224,1520,32,40]],[[64,1560,32,40],[96,1560,32,40],[128,1560,32,40],[160,1560,32,40],[192,1560,32,40],[224,1560,32,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[256,1400,32,40],[288,1400,32,40],[320,1400,32,40],[352,1400,32,40]],[[256,1440,32,40],[288,1440,32,40],[320,1440,32,40],[352,1440,32,40]],[[256,1480,32,40],[288,1480,32,40],[320,1480,32,40],[352,1480,32,40]],[[256,1520,32,40],[288,1520,32,40],[320,1520,32,40],[352,1520,32,40]],[[256,1560,32,40],[288,1560,32,40],[320,1560,32,40],[352,1560,32,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[384,1400,32,40],[416,1400,32,40],[448,1400,32,40]],[[384,1440,32,40],[416,1440,32,40],[448,1440,32,40]],[[384,1480,32,40],[416,1480,32,40],[448,1480,32,40]],[[384,1520,32,40],[416,1520,32,40],[448,1520,32,40]],[[384,1560,32,40],[416,1560,32,40],[448,1560,32,40]]]},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[480,1400,32,40],[512,1400,32,40]],[[480,1440,32,40],[512,1440,32,40]],[[480,1480,32,40],[512,1480,32,40]],[[480,1520,32,40],[512,1520,32,40]],[[480,1560,32,40],[512,1560,32,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[544,1400,32,40],[576,1400,32,40],[608,1400,32,40],[640,1400,32,40]],[[544,1440,32,40],[576,1440,32,40],[608,1440,32,40],[640,1440,32,40]],[[544,1480,32,40],[576,1480,32,40],[608,1480,32,40],[640,1480,32,40]],[[544,1520,32,40],[576,1520,32,40],[608,1520,32,40],[640,1520,32,40]],[[544,1560,32,40],[576,1560,32,40],[608,1560,32,40],[640,1560,32,40]]]}},\"strike\":[[18.5,33.8],[21.1,31.7],[20.7,29.0],[17.5,27.3],[13.5,27.5]]}}}","modules/coach-world-rts/atlas/myr5.json":"{\"unit\":\"myr5\",\"cell\":[28,40],\"anchor\":[14,33],\"maskDiv\":4,\"tickMs\":42,\"tiltDeg\":48,\"stored\":[\"S\",\"SE\",\"E\",\"NE\",\"N\"],\"dirs\":{\"S\":[0,0],\"SE\":[1,0],\"E\":[2,0],\"NE\":[3,0],\"N\":[4,0],\"NW\":[3,1],\"W\":[2,1],\"SW\":[1,1]},\"baseTint\":[150,90,200],\"actions\":{\"idle\":{\"frames\":1,\"ms\":[1008],\"rects\":[[[0,0,28,40]],[[0,40,28,40]],[[0,80,28,40]],[[0,120,28,40]],[[0,160,28,40]]]},\"walk\":{\"frames\":6,\"ms\":[168,168,126,168,168,126],\"rects\":[[[28,0,28,40],[56,0,28,40],[84,0,28,40],[112,0,28,40],[140,0,28,40],[168,0,28,40]],[[28,40,28,40],[56,40,28,40],[84,40,28,40],[112,40,28,40],[140,40,28,40],[168,40,28,40]],[[28,80,28,40],[56,80,28,40],[84,80,28,40],[112,80,28,40],[140,80,28,40],[168,80,28,40]],[[28,120,28,40],[56,120,28,40],[84,120,28,40],[112,120,28,40],[140,120,28,40],[168,120,28,40]],[[28,160,28,40],[56,160,28,40],[84,160,28,40],[112,160,28,40],[140,160,28,40],[168,160,28,40]]]},\"work\":{\"frames\":4,\"ms\":[168,42,84,168],\"rects\":[[[196,0,28,40],[224,0,28,40],[252,0,28,40],[280,0,28,40]],[[196,40,28,40],[224,40,28,40],[252,40,28,40],[280,40,28,40]],[[196,80,28,40],[224,80,28,40],[252,80,28,40],[280,80,28,40]],[[196,120,28,40],[224,120,28,40],[252,120,28,40],[280,120,28,40]],[[196,160,28,40],[224,160,28,40],[252,160,28,40],[280,160,28,40]]]},\"attack\":{\"frames\":3,\"ms\":[168,84,168],\"rects\":[[[308,0,28,40],[336,0,28,40],[364,0,28,40]],[[308,40,28,40],[336,40,28,40],[364,40,28,40]],[[308,80,28,40],[336,80,28,40],[364,80,28,40]],[[308,120,28,40],[336,120,28,40],[364,120,28,40]],[[308,160,28,40],[336,160,28,40],[364,160,28,40]]],\"strike\":1},\"hit\":{\"frames\":2,\"ms\":[84,126],\"rects\":[[[392,0,28,40],[420,0,28,40]],[[392,40,28,40],[420,40,28,40]],[[392,80,28,40],[420,80,28,40]],[[392,120,28,40],[420,120,28,40]],[[392,160,28,40],[420,160,28,40]]]},\"death\":{\"frames\":4,\"ms\":[126,126,126,588],\"rects\":[[[448,0,28,40],[476,0,28,40],[504,0,28,40],[532,0,28,40]],[[448,40,28,40],[476,40,28,40],[504,40,28,40],[532,40,28,40]],[[448,80,28,40],[476,80,28,40],[504,80,28,40],[532,80,28,40]],[[448,120,28,40],[476,120,28,40],[504,120,28,40],[532,120,28,40]],[[448,160,28,40],[476,160,28,40],[504,160,28,40],[532,160,28,40]]]}},\"teamPct\":55.8,\"teamEdges\":[55,85,111],\"teamRamps\":{\"red\":[[60,8,10],[140,20,24],[225,48,44],[255,140,120]],\"blue\":[[10,24,80],[24,60,170],[60,120,245],[150,200,255]],\"green\":[[8,50,14],[20,120,34],[50,200,70],[160,255,150]]},\"idle\":{\"bobPx\":1,\"bobMs\":1260},\"sheet\":[560,200],\"carry\":{\"sheet\":\"myr5-carry.png\",\"frames\":6,\"ms\":[168,168,126,168,168,126],\"layer\":[\"below\",\"below\",\"below\",\"above\",\"above\"],\"note\":\"draw with the walk frame of the same index; row = stored facing\"},\"fx\":{\"spark\":{\"sheet\":\"myr5-fx.png\",\"size\":[12,12],\"center\":[6,6],\"rects\":[[0,0,12,12],[12,0,12,12],[24,0,12,12]],\"ms\":[42,42,84],\"startMs\":210,\"strike\":[[24.4,40.7],[28.1,33.9],[23.5,27.4],[13.4,24.9],[3.6,27.9]],\"note\":\"strike = point in cell px per stored facing; mirror x about anchor x (x' = 2*anchor[0]-x)\"}}}\r\n","modules/coach-world-rts/aura-sprites.mjs":"// modules/coach-world-rts/aura-sprites.mjs\r\n// Reuses the arcade/power aura sheets (no new art). auraFrames(id) -> Promise<64px canvases>; auraFramesSync(id) -> array now ([] until loaded, kicks the load).\r\n// auraTextures(T,id) -> shared CanvasTextures (one per frame) for 3D sprites, [] until loaded.\r\nimport { AURA_OPTIONS } from '../../arcade/power/aura-options.mjs';\r\nimport { FACTION_AURA } from './unit-levels.mjs';\r\n\r\nconst CACHE = new Map(), DONE = new Map(), TEX = new Map();\r\n\r\nexport function auraFrames(optionId) {\r\n  if (CACHE.has(optionId)) return CACHE.get(optionId);\r\n  const opt = AURA_OPTIONS.find(o => o.id === optionId);\r\n  const finish = frames => { DONE.set(optionId, frames); return frames; };\r\n  const promise = !opt?.asset || typeof Image === 'undefined' ? Promise.resolve(finish([])) : new Promise(resolve => {\r\n    const img = new Image();\r\n    img.onload = () => {\r\n      const cols = opt.columns || 1, rows = opt.rows || 1, cw = img.width / cols, ch = img.height / rows, frames = [];\r\n      for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {\r\n        const canvas = document.createElement('canvas');\r\n        canvas.width = canvas.height = 64;\r\n        const ctx = canvas.getContext('2d');\r\n        ctx.imageSmoothingEnabled = false;\r\n        ctx.drawImage(img, c * cw, r * ch, cw, ch, 0, 0, 64, 64);\r\n        frames.push(canvas);\r\n      }\r\n      resolve(finish(frames));\r\n    };\r\n    img.onerror = () => resolve(finish([]));\r\n    img.src = new URL(opt.asset, new URL('../../arcade/power/assets/', import.meta.url)).href;\n  });\r\n  CACHE.set(optionId, promise);\r\n  return promise;\r\n}\r\n\r\nexport function auraFramesSync(optionId) {\r\n  auraFrames(optionId);\r\n  return DONE.get(optionId) || [];\r\n}\r\n\r\nexport function auraTextures(T, optionId) {\r\n  const frames = auraFramesSync(optionId);\r\n  if (!frames.length) return [];\r\n  if (!TEX.has(optionId)) {\r\n    TEX.set(optionId, frames.map(f => {\r\n      const t = new T.CanvasTexture(f);\r\n      t.magFilter = t.minFilter = T.NearestFilter; t.generateMipmaps = false; t.colorSpace = T.SRGBColorSpace;\r\n      return t;\r\n    }));\r\n  }\r\n  return TEX.get(optionId);\r\n}\r\n\r\nexport function drawAura2d(ctx, frames, x, y, size, tick) {\r\n  if (!Array.isArray(frames) || frames.length === 0) return;\r\n  ctx.save();\r\n  ctx.globalCompositeOperation = 'lighter';\r\n  ctx.imageSmoothingEnabled = false;\r\n  ctx.drawImage(frames[Math.floor(tick) % frames.length], x - size / 2, y - size / 2, size, size);\r\n  ctx.restore();\r\n}\r\n\r\nexport const auraIdFor = faction => FACTION_AURA[faction] ?? null;\r\n","modules/coach-world-rts/base-detail.mjs":"export const DETAIL_KINDS = Object.freeze(['hq','hall','care','observatory','habitat','hangar','production','expedition']);\r\n\r\nconst windowsByKind = {\r\n  hq: [0,2,4,6,8,10],\r\n  hall: [0,1,2,3,4,5],\r\n  care: [0,1,2,2,3,4],\r\n  observatory: [0,1,2,2,3,4],\r\n  habitat: [0,1,2,2,3,4],\r\n  hangar: [0,0,0,0,0,0],\r\n  production: [0,0,0,0,0,0],\r\n  expedition: [0,0,0,0,0,0]\r\n};\r\n\r\nconst padsByKind = {\r\n  hq: [0,0,0,0,0,0],\r\n  hall: [0,0,0,0,0,0],\r\n  care: [0,0,0,0,0,0],\r\n  observatory: [0,0,0,0,0,0],\r\n  habitat: [0,0,0,0,0,0],\r\n  hangar: [0,1,1,2,2,3],\r\n  production: [0,0,1,1,2,2],\r\n  expedition: [0,0,1,1,2,2]\r\n};\r\n\r\nconst decalsByKind = {\r\n  hq: [0,1,2,3,4,5],\r\n  hall: [0,1,2,3,4,5],\r\n  care: [0,1,2,3,4,5],\r\n  observatory: [0,1,2,3,4,5],\r\n  habitat: [0,1,2,3,4,5],\r\n  hangar: [0,1,2,3,4,5],\r\n  production: [0,1,2,3,4,5],\r\n  expedition: [0,1,2,3,4,5]\r\n};\r\n\r\nconst propsByKind = {\r\n  hq: [0,2,4,6,9,12],\r\n  hall: [0,2,4,6,9,12],\r\n  care: [0,2,4,6,9,12],\r\n  observatory: [0,2,4,6,9,12],\r\n  habitat: [0,2,4,6,9,12],\r\n  hangar: [0,2,4,6,9,12],\r\n  production: [0,2,4,6,9,12],\r\n  expedition: [0,2,4,6,9,12]\r\n};\r\n\r\nconst antennaKinds = new Set(['hq','observatory']);\r\n\r\nexport const PROP_TABLE = Object.freeze(DETAIL_KINDS.reduce((acc,k) => {\r\n  acc[k] = Array.from({length:6}, (_,lvl)=>({\r\n    windows: windowsByKind[k][lvl],\r\n    pads: padsByKind[k][lvl],\r\n    decals: decalsByKind[k][lvl],\r\n    props: propsByKind[k][lvl],\r\n    antenna: lvl>=3 && antennaKinds.has(k),\r\n    blink: lvl>=3 && antennaKinds.has(k),\r\n    door: k==='hangar' && lvl>=2\r\n  }));\r\n  return acc;\r\n}, {}));\r\n\r\nexport function propsFor(kind, level, {seed=0}={}) {\r\n  const lvl = typeof level === 'number' && !isNaN(level) ? Math.max(0,Math.min(5,Math.floor(level))) : 0;\r\n  const entry = PROP_TABLE[kind]?.[lvl];\r\n  if (!entry) return [];\r\n  const res=[];\r\n  for(let i=0;i<entry.windows;i++)res.push({type:'window',i,lit:true});\r\n  for(let i=0;i<entry.decals;i++)res.push({type:'decal',i,variant:(i+seed)%3});\r\n  for(let i=0;i<entry.props;i++)res.push({type:'prop',i,variant:(i*7+seed)%5});\r\n  if(entry.antenna)res.push({type:'antenna',blink:entry.blink});\r\n  for(let i=0;i<entry.pads;i++)res.push({type:'pad',i});\r\n  if(entry.door)res.push({type:'door',open:true});\r\n  return res;\r\n}\r\n\r\nexport function litWindowCount(kind, level) {\r\n  const lvl = typeof level === 'number' && !isNaN(level) ? Math.max(0,Math.min(5,Math.floor(level))) : 0;\r\n  return PROP_TABLE[kind]?.[lvl]?.windows ?? 0;\r\n}\r\n\r\nexport function blinkPhase(t, period=1.6) {\r\n  const p = ((t/period)%1+1)%1;\r\n  return p<0.15?1-p/0.15:0;\r\n}\r\n","modules/coach-world-rts/base-meshes.mjs":"// W8a near-field base: chunky low-poly buildings (one merged mesh each, <=300 tris) under the PS1 material.\r\n// Family primitive follows the faction policy: occupied-worker = stacked boxes, seeded-warp = cylinder (+ torus ring at level>=3),\r\n// consumed-seed = half-sphere dome + wedge buttress. Level adds roof props (antenna, crown, pylons, gem) so upgrades read at a glance.\r\n// Texture per (faction,kind,level): the existing 64x64 building sprite on the front faces + an 8-row palette strip sampled from it for the sides.\r\n// Far buildings go to the existing sprite billboard layer.\r\nimport {BUILDINGS,FACTION_BY_ID,DEFENSE_KINDS} from '../coach-world/catalog.mjs';\r\nimport {rasterSprite} from '../coach-world/pixel-art.mjs';\r\nimport {createPs1Material} from './ps1-material.mjs';\r\nimport {partsForDefense} from './tower-art.mjs';\r\nimport {partsForKind,hashSeed} from './building-art.mjs';\r\nconst DEFENSE=['tower','wall','generator'],MESH_KINDS=[...DEFENSE_KINDS,'silo','yard','landing'],DEFENSE_COLORS={color:'#8f9dab',dark:'#4a5663',accent:'#7fe3ff'}; // R7 sites never move the spawn\r\n\r\nexport const MAX_TRIS=300,SCALE=1.6,MIN_CLEAR_M=4,HQ_TARGET_M=9;\r\nexport const heightOf=level=>level<=0?.3:1+.4*level;\r\nconst spriteKind=(kind,level)=>level===0?'foundation':['hq','hall','production','expedition','care','observatory','habitat','hangar'].includes(kind)?kind:'building';\r\nconst SPR=64,STRIP=8; // texture rows: 0..7 strip (v bottom), then the sprite (SPR rows; 64 for small kinds, up to 128 for the HQ)\r\n\r\n// Standing tile: a free point at least 4 m from every footprint whose HQ distance is closest to ~9 m. Pure; depends only on the fixed catalog layout.\r\nlet spawnCache=new Map();\r\nexport function spawnTile(scale=SCALE){\r\n if(spawnCache.has(scale))return spawnCache.get(scale);\r\n const need=MIN_CLEAR_M/scale,hq=BUILDINGS.find(b=>b.id==='hq');let best=null,fallback=null;\r\n for(let x=0;x<=24;x+=.5)for(let y=0;y<=24;y+=.5){\r\n  let c=1e9;for(const b of BUILDINGS)if(!DEFENSE.includes(b.kind))c=Math.min(c,Math.hypot(Math.max(b.x-x,0,x-b.x-b.w),Math.max(b.y-y,0,y-b.y-b.h)));\r\n  const d=Math.hypot(x-hq.x-hq.w/2,y-hq.y-hq.h/2)*scale;\r\n  if(!fallback||c>fallback.c)fallback={x,y,c};\r\n  if(c>=need&&(!best||Math.abs(d-HQ_TARGET_M)<best.s))best={x,y,s:Math.abs(d-HQ_TARGET_M)};\r\n }\r\n const r=best??fallback;spawnCache.set(scale,r);return r;\r\n}\r\n// Yaw (radians) for the world group so the HQ is straight ahead (-Z) when the viewer faces forward.\r\nexport function worldYaw(scale=SCALE){\r\n const s=spawnTile(scale),hq=BUILDINGS.find(b=>b.id==='hq');return Math.PI-Math.atan2(hq.x+hq.w/2-s.x,hq.y+hq.h/2-s.y);\r\n}\r\n\r\n// Pure layout: base -> placed buildings in metres around the viewer (unrotated; +z = tile +y).\r\nexport function layoutBase(base,{scale=SCALE,origin=spawnTile(scale)}={}){\r\n return BUILDINGS.map(def=>{\r\n  const b={...def,...base.buildings?.[def.id]},level=b.level??1,faction=FACTION_BY_ID[b.track]?b.track:FACTION_BY_ID[base.faction]?base.faction:'chest';\r\n  const x=(b.x+b.w/2-origin.x)*scale,z=(b.y+b.h/2-origin.y)*scale;\r\n  return {id:def.id,kind:def.kind,faction,level,seed:hashSeed(`${base.seed??''}:${def.id}`),x,z,w:b.w*scale,d:b.h*scale,height:heightOf(level)*scale,dist:Math.hypot(x,z)};\r\n });\r\n}\r\n\r\nfunction partsFor(T,policy,b){\r\n const {w,d,height,level}=b;\r\n if(level>0){const k=partsForKind(T,b.kind,{w,d,height,level,policy,seed:b.seed,faction:b.faction});if(k)return k;}\r\n const r=Math.min(w,d)*.45,P=[],u=height/ (1+.4*Math.max(level,1)); // u = scale factor in metres per tile\r\n if(level<=0)return [{g:new T.BoxGeometry(w*.9,height,d*.9),y:height/2}];\r\n if(policy==='seeded-warp'){\r\n  P.push({g:new T.CylinderGeometry(r*.85,r,height,12,1),y:height/2});\r\n  if(level>=3)P.push({g:new T.TorusGeometry(r*1.1,.12*u,6,12),y:height*.6,rx:Math.PI/2});\r\n }else if(policy==='consumed-seed'){\r\n  P.push({g:new T.SphereGeometry(r,10,6,0,Math.PI*2,0,Math.PI/2),y:0,sy:height/r});\r\n  P.push({g:new T.CylinderGeometry(r*.3,r*.3,height*.6,3,1),x:r*.95,y:height*.3});\r\n }else{\r\n  const n=level<=1?2:level<=3?3:4;\r\n  for(let i=0;i<n;i++){const s=1-.12*i;P.push({g:new T.BoxGeometry(w*.9*s,height/n,d*.9*s),y:height/n*(i+.5)});}\r\n }\r\n // roof props: visible upgrades (accent coloured)\r\n const top=height,k=.5*u;\r\n if(level>=2)P.push({g:new T.CylinderGeometry(.04*u,.04*u,.9*k*2,3,1),y:top+.45*k*2,accent:1});\r\n if(level>=3)P.push({g:new T.ConeGeometry(.35*u,.5*u,6,1),y:top+.25*u,accent:1});\r\n if(level>=4)for(const s of [-1,1])P.push({g:new T.BoxGeometry(.18*u,.9*u,.18*u),x:s*r*.8,y:height+.2*u,accent:1});\r\n if(level>=5)P.push({g:new T.OctahedronGeometry(.3*u,0),y:top+1.1*u,accent:1});\r\n return P;\r\n}\r\n\r\n// Bake parts into one non-indexed geometry (position/normal/uv/color). Front faces (+Z) map to the sprite, everything else to the strip.\r\nexport function bake(T,parts,{w,height},{color,dark,accent},flat=false,spr=SPR){const TEX_H=spr+STRIP;\r\n const pos=[],nor=[],uv=[],col=[],m=new T.Matrix4(),n3=new T.Matrix3(),v=new T.Vector3(),c=new T.Color(),cLo=new T.Color(dark),cHi=new T.Color(color),cAc=new T.Color(accent);\r\n for(const p of parts){\r\n  const g=p.g.index?p.g.toNonIndexed():p.g;\r\n  m.compose(new T.Vector3(p.x??0,p.y??0,p.z??0),new T.Quaternion().setFromEuler(new T.Euler(p.rx??0,0,0)),new T.Vector3(1,p.sy??1,1));n3.getNormalMatrix(m);\r\n  const P=g.attributes.position,N=g.attributes.normal;\r\n  for(let i=0;i<P.count;i++){\r\n   v.fromBufferAttribute(P,i).applyMatrix4(m);const x=v.x,y=v.y;pos.push(x,y,v.z);\r\n   v.fromBufferAttribute(N,i).applyMatrix3(n3).normalize();nor.push(v.x,v.y,v.z);\r\n   const t=Math.max(0,Math.min(1,y/height));\r\n   if(!flat&&v.z>.35&&!p.accent)uv.push(Math.max(0,Math.min(1,x/(w*.9)+.5)),(STRIP+t*spr)/TEX_H);else uv.push(.5,(.5+(STRIP-1)*t)/TEX_H);\r\n   if(p.accent)c.copy(cAc);else{c.copy(cLo).lerp(cHi,Math.min(1,t*1.3));if(v.y>.7)c.lerp(cAc,.35);}\r\n   col.push(c.r,c.g,c.b);\r\n  }\r\n  if(g!==p.g)g.dispose();p.g.dispose();\r\n }\r\n const geo=new T.BufferGeometry();\r\n geo.setAttribute('position',new T.Float32BufferAttribute(pos,3));geo.setAttribute('normal',new T.Float32BufferAttribute(nor,3));\r\n geo.setAttribute('uv',new T.Float32BufferAttribute(uv,2));geo.setAttribute('color',new T.Float32BufferAttribute(col,3));\r\n return geo;\r\n}\r\n\r\nconst rgb=h=>[1,3,5].map(i=>parseInt(h.slice(i,i+2),16));\r\n// (64|96|128)x(+8) RGBA texture rows bottom->top: palette strip (8 bands sampled bottom to top from the sprite) then the sprite, transparent sprite cells have alpha 0 (cutout).\r\nexport function spriteTexture(T,raster,dark){\r\n const SPR=raster.width||64,TEX_H=SPR+STRIP,d=new Uint8Array(SPR*TEX_H*4),bg=rgb(dark),pal=raster.palette.map((p,i)=>i?rgb(p):null);\r\n const band=Array.from({length:STRIP},()=>[0,0,0,0]);\r\n for(let y=0;y<SPR;y++)for(let x=0;x<SPR;x++){\r\n  const idx=raster.pixels[y*SPR+x],col=idx?pal[idx]:bg,row=STRIP+(SPR-1-y),o=(row*SPR+x)*4;\r\n  d[o]=col[0];d[o+1]=col[1];d[o+2]=col[2];d[o+3]=idx?255:0; // transparent sprite cells are cut out (alphaTest), not filled\r\n  if(idx){const b=band[Math.min(STRIP-1,Math.floor((SPR-1-y)/(SPR/STRIP)))];b[0]+=col[0];b[1]+=col[1];b[2]+=col[2];b[3]++;}\r\n }\r\n for(let r=0;r<STRIP;r++){const b=band[r],n=b[3]||1,col=b[3]?[b[0]/n,b[1]/n,b[2]/n]:bg;for(let x=0;x<SPR;x++){const o=(r*SPR+x)*4;d[o]=col[0]*.8;d[o+1]=col[1]*.8;d[o+2]=col[2]*.8;d[o+3]=255;}}\r\n const t=new T.DataTexture(d,SPR,TEX_H,T.RGBAFormat);t.magFilter=t.minFilter=T.NearestFilter;t.generateMipmaps=false;t.colorSpace=T.SRGBColorSpace;t.needsUpdate=true;return t;\r\n}\r\n\r\n// Returns {group,near,far,triangles,dispose}. `layer` (createPixelBillboards result) receives the far-field sprites.\r\nexport function createBaseMeshes(T,base,{layer=null,nearRadius=18,createMaterial=createPs1Material,resolution,raster=rasterSprite}={}){\r\n const group=new T.Group(),materials=new Map(),near=[],far=[],triangles={},own=[];group.name='rts-base-meshes';\r\n const layout=layoutBase(base),wallAt=(x,z)=>layout.find(o=>o.kind==='wall'&&o.level>0&&Math.hypot(o.x-x,o.z-z)<.1);\r\n for(const b of layout){\r\n  if(MESH_KINDS.includes(b.kind)){ // real 3D meshes (never the hut sprite); unbuilt sites draw nothing (base3d shows a plot outline on demand)\r\n   if(b.level<=0)continue;\r\n   let mat=materials.get('defense');\r\n   if(!mat){const map=new T.DataTexture(new Uint8Array(16).fill(255),2,2,T.RGBAFormat);map.needsUpdate=true;mat=createMaterial(T,{map,resolution,alphaTest:.5});materials.set('defense',mat);own.push(map,mat);}\r\n   const links=b.kind==='wall'?[[1,0],[-1,0],[0,1],[0,-1]].filter(([lx,lz])=>wallAt(b.x+lx*b.w,b.z+lz*b.d)):[];\r\n   const geo=bake(T,partsForDefense(T,b.kind,b.level,links),b,DEFENSE_COLORS,true),mesh=new T.Mesh(geo,mat);\r\n   mesh.position.set(b.x,0,b.z);mesh.name=`rts-${b.id}`;group.add(mesh);near.push(b.id);triangles[b.id]=geo.attributes.position.count/3;continue;\r\n  }\r\n  const variant=Math.max(0,Math.min(4,b.level-1)),kind=spriteKind(b.kind,b.level);\r\n  if(b.dist>nearRadius){\r\n   const d={faction:b.faction,kind,variant,x:b.x,z:b.z,height:b.height*1.5+1.5};\r\n   far.push({id:b.id,...d});layer?.add(d);continue;\r\n  }\r\n  const f=FACTION_BY_ID[b.faction],key=`${f.id}/${kind}/${variant}`;\r\n  const sprite=raster(f.id,kind,variant);let mat=materials.get(key);\r\n  if(!mat){const map=spriteTexture(T,sprite,f.dark);mat=createMaterial(T,{map,resolution,alphaTest:.5});materials.set(key,mat);own.push(map,mat);}\r\n  const geo=bake(T,partsFor(T,f.policy,b),b,f,false,sprite.width||SPR),mesh=new T.Mesh(geo,mat);\r\n  mesh.position.set(b.x,0,b.z);mesh.rotation.y=Math.atan2(-b.x,-b.z); // front faces the viewer\r\n  mesh.name=`rts-${b.id}`;group.add(mesh);near.push(b.id);triangles[b.id]=geo.attributes.position.count/3;\r\n }\r\n return {group,near,far,triangles,materials:[...materials].filter(([k])=>k!=='defense').map(([,m])=>m), // sprite-textured families only\r\n  setResolution(w,h){for(const m of materials.values())m.userData.ps1?.setResolution(w,h);},\r\n  dispose(){group.traverse(o=>o.geometry?.dispose());own.forEach(o=>o.dispose());group.removeFromParent();group.clear();materials.clear();}};\r\n}\r\n","modules/coach-world-rts/base3d.mjs":"// R52 3D main base: the Base tab drawn like the 360 virtual base (same PS1 meshes, billboard crew, fog) from an orbit camera.\n// mountBase3d(wrap,{getBase,getCoachBase=getBase,getPresentationScope,sceneMode,rts,signal,deps}) -> {dispose,recenter,setZoom,addToWorld,onFrame,ready,stats}.\n// The 2D canvas under `wrap` stays mounted and stepping (it owns crew + runtime + selection); this view only draws it and forwards taps through the S12\n// methods on sceneMode (tapTile / selectTiles). The 2D canvas is hidden only once WebGL is up, so any failure leaves the 2D map visible.\n//\n// SHIPS SLOT (lane ships3d.mjs): `handle.addToWorld(obj3d)` adds to the base world group (metres, +x = tile +x, +z = tile +y, origin = spawnTile()).\n// `handle.center` = {x,z} of the 24x24 map centre in those metres; ships fly around it. `handle.onFrame((t,dtMs)=>{})` returns an unsubscribe and runs\n// before each render. `handle.ready` resolves {T,world,scene,camera,center} when three is live (addToWorld before that is queued, not lost).\n// Optional siblings (written by other lanes, imported if present): terrain3d.mjs createTerrain3d(T,base,{scale,heightAt,groundCanvas})->{group,dispose};\n// base-detail.mjs createBaseDetail(T,base,{scale,origin,meshes})->{group?,update?(t),dispose?}.\nimport {claimGraphics,releaseGraphics} from '../graphics-lifecycle.mjs';\nimport {createPixelBillboards,disposePixelObject,fitPixelRenderer} from '../coach-world/sprite-billboards.mjs';\nimport {createDragMove,movable} from './drag-move.mjs';\nimport {createTerrain,drawTerrain} from '../coach-world/terrain.mjs';\nimport {createTouchGesture} from '../coach-world/touchselection.mjs';\nimport {BUILDINGS,DEFENSE_KINDS} from '../coach-world/catalog.mjs';\nimport {createBaseMeshes,layoutBase,spawnTile,worldYaw,SCALE} from './base-meshes.mjs';\nimport {createBaseSprawl} from '../coach-flight/base-sprawl.mjs';\nimport {createCrewBillboards} from './crew-billboards.mjs';\nimport {placeNodes,createNodes3d} from './resource-nodes.mjs';\nconst nodeSpots=base=>{try{return placeNodes(base).flatMap(p=>p.gas?[p.node,p.gas.node]:[p.node]);}catch{return[];}};\nimport {createPs1Material,addFade} from './ps1-material.mjs';\nimport {BIOMES,NODE_SHAPES,shapeGeometry} from './biomes.mjs';\nimport {createAlienSky} from './sky-alien.mjs';\nimport {createSkyCritters} from './sky-critters.mjs';\nimport {updateSpriteTint} from './sprite-shade.mjs';\nimport {pickBuildingId} from './building-pick.mjs';\nimport {createOutline} from './pixel-outline.mjs';\nimport {createCoachPedestals} from './coach-pedestals.mjs';\nimport {useFogLaw,veilFar} from './fog-law.mjs';\n\nexport const FRAME_MS=33,FAR_PLANE=260,ZOOM=[.75,2.6],ZOOM0=1.4,DEGRADE_MS=24,DEGRADE_WINDOW=90,MIN_SHORT=220;\n// plant instance fraction by zoom: 2.6 -> all, default 1.4 -> 74%, min .75 -> 39%, floor 35%\nexport const LOOKS_ZOOM=1; // crew glow/aura/trail sprites only from zoom 1 in (zoomed out they are a few px and cost 1-3 draws per unit)\nexport const propLod=zoom=>clamp(zoom/1.9,.35,1);\nexport const ps1Scale=(w,h)=>Math.min(3,Math.max(1,Math.min(w,h)/400)); // R81: finer pixels (was /300, min 1.25)\n// Frame-time guard: +.5 scale (coarser) per step, never taking the internal short side under MIN_SHORT (and never finer than base).\nexport const effScale=(w,h,extra)=>{const b=ps1Scale(w,h);return Math.max(b,Math.min(b+extra,Math.min(w,h)/MIN_SHORT));};\nexport const nextDegrade=(avgMs,w,h,extra)=>avgMs>DEGRADE_MS&&effScale(w,h,extra+.5)>effScale(w,h,extra)?extra+.5:extra;\n// V5 valley haze (scene units, 1 m = SCALE): floor 25 m over the base plane, 120 m falloff, clear inside 30 m of the focus.\nexport const BASE_HAZE=Object.freeze({extinction:1/(1400*1.6),floor:25*1.6,falloff:120*1.6,clear:30*1.6});\nexport const PITCH0=50*Math.PI/180,PITCH_SPAN=15*Math.PI/180,DIST0=30*SCALE,FOG=0x25333c,CENTER_TILE=12,PAN_MAX=10*SCALE;\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\nconst optional=spec=>import(spec).catch(()=>null); // non-literal: the build must not bundle or require these\n\n// Pure orbit maths (tested): camera position for a pivot, yaw/pitch (radians) and distance.\nexport const orbitPosition=(pivot,yaw,pitch,dist)=>[pivot[0]+dist*Math.sin(yaw)*Math.cos(pitch),pivot[1]+dist*Math.sin(pitch),pivot[2]+dist*Math.cos(yaw)*Math.cos(pitch)];\nexport const distanceFor=(zoom,range=ZOOM)=>DIST0/clamp(zoom,range[0],range[1]);\n// R58 continuous zoom (default ON; kill switch ?world=0 / localStorage cw-world='0'): zoom floor .02 (~1000 ft), pinch is already multiplicative = log space.\nexport const FADE_ZOOM=[.09,.18]; // base dissolves between zoom .18 (crisp) and .09 (gone): a short band, so little time in the speckled half-state\nexport const ZOOM_WORLD=[.02,2.6]; // .02: ~+-700 ft across a phone at the 1.6 units/m base scale\nexport const worldFlag=(g=globalThis)=>{try{return !(/[?&]world=0\\b/.test(g.location?.search||'')||g.localStorage?.getItem('cw-world')==='0');}catch{return true;}};\nconst smooth=(a,b,x)=>{const t=clamp((x-a)/(b-a),0,1);return t*t*(3-2*t);};\n// Pure per-zoom effects: z01 (log position in the range), base opacity f (1 = crisp base, dissolves .55 -> .3), camera far, fog near/far.\n// fog lerps from the far-zoom values to the R57 values as f -> 1, so a crisp base looks exactly like R57.\nexport function zoomFx(zoom,range=ZOOM_WORLD){\n const z=clamp(zoom,range[0],range[1]),d=distanceFor(z,range),z01=(Math.log(z)-Math.log(range[0]))/(Math.log(range[1])-Math.log(range[0])),f=smooth(Math.log(FADE_ZOOM[0]),Math.log(FADE_ZOOM[1]),Math.log(z)),k=(a,b)=>a+(b-a)*f;\n return {d,z01,f,far:Math.max(FAR_PLANE,3*d),fogNear:k(d*.9,d+20),fogFar:k(d*2.2,d+20+19*SCALE)};\n}\n// Ring window: a world layer with {minDist,maxDist} fades in/out over 15% either side of its range (dithered by the alpha hash).\nexport const ringFade=(d,min=0,max=Infinity)=>(min>0?smooth(min*.85,min*1.15,d):1)*(Number.isFinite(max)?1-smooth(max*.85,max*1.15,d):1);\n// SC2-style long lens; distance (not fov) frames the base: ~10 tiles across a phone at zoom 1.\nexport const fovFor=()=>35;\n\n// Screen point (ndc) -> tile {x,y} (fractional) on the ground plane; null above the horizon.\nexport function pickTile(T,camera,ndc,world,origin=spawnTile()){\n const ray=new T.Raycaster();ray.setFromCamera({x:ndc[0],y:ndc[1]},camera);\n const {origin:o,direction:d}=ray.ray;if(d.y>=-1e-6)return null;\n const hit=o.clone().addScaledVector(d,-o.y/d.y),l=world.worldToLocal(hit);\n return {x:clamp(l.x/SCALE+origin.x,0,23.99),y:clamp(l.z/SCALE+origin.y,0,23.99)};\n}\n\n// Pixel \"1/5\" (or SITE) tag under each building: 3x5 bitmap font, no antialiasing, drawn by drawTags().\n// bitmap font below (labelPixels). Exported for tests.\nconst FONT={'0':'111101101101111','1':'010110010010111','2':'111001111100111','3':'111001111001111','4':'101101111001001','5':'111100111001111','6':'111100111101111','7':'111001001001001','8':'111101111101111','9':'111101111001111','/':'001001010100100',S:'111100111001111',I:'111010010010111',T:'111010010010010',E:'111100110100111'};\nexport const labelText=b=>b.level<=0?'SITE':`${b.level}/5`;\nexport function labelPixels(text){ // -> {w,h,on:[[x,y]...]} text pixels inside a 1 px padded box\n const on=[];let x=2;for(const ch of text){const g=FONT[ch]||'';for(let i=0;i<15;i++)if(g[i]==='1')on.push([x+i%3,2+Math.floor(i/3)]);x+=4;}\n return {w:x+1,h:9,on}; // 1 px border + 1 px pad each side\n}\n// Tags are drawn on a 2D overlay canvas at device resolution: 1 texel = 1 CSS px (2 device px at DPR 2), whole-pixel positions, no filtering.\n// tagPlan decides which tags show: SITE only when selected or zoom >= 1.6, level tags fade out below zoom .9 (selected stays), and colliding\n// tags keep the selected, else the nearer, one. items: [{id,level,depth,w,h,x,y}] (x = centre, y = top, CSS px).\nexport function tagPlan(items,{zoom=1,selectedId=null}={}){\n const keep=[];\n for(const it of [...items].sort((a,b)=>(b.id===selectedId)-(a.id===selectedId)||a.depth-b.depth)){\n  const sel=it.id===selectedId;\n  if(!sel&&(it.level<=0?zoom<1.6:zoom<.9))continue;\n  if(keep.some(k=>Math.abs(k.x-it.x)*2<k.w+it.w+4&&k.y<it.y+it.h+2&&it.y<k.y+k.h+2))continue;\n  keep.push(it);\n }\n return keep;\n}\nfunction skyDome(T){\n const g=new T.SphereGeometry(90,12,8),p=g.attributes.position,c=[],top=new T.Color(0x1d2c55),mid=new T.Color(0x7d6fa8),low=new T.Color(0x25333c),k=new T.Color();\n for(let i=0;i<p.count;i++){const y=p.getY(i)/90;k.copy(y>0?mid.clone().lerp(top,Math.min(1,y*1.6)):low.clone().lerp(mid,Math.max(0,1+y*4)));c.push(k.r,k.g,k.b);}\n g.setAttribute('color',new T.Float32BufferAttribute(c,3));\n const m=new T.Mesh(g,new T.MeshBasicMaterial({vertexColors:true,side:T.BackSide,fog:false,depthWrite:false}));m.renderOrder=-1;m.name='rts-sky';return m;\n}\n// Fallback edge hider when terrain3d.mjs is absent: fog-coloured floor + a square alpha fade that is opaque just past the ground edge.\nfunction edgeFade(T,center){\n const g=new T.Group(),floor=new T.Mesh(new T.PlaneGeometry(600,600),new T.MeshBasicMaterial({color:FOG,fog:false}));floor.rotation.x=-Math.PI/2;floor.position.set(center.x,-.06,center.z);\n const px=new Uint8Array(128*128*4);\n for(let j=0;j<128;j++)for(let i=0;i<128;i++){const d=Math.hypot(i-63.5,j-63.5)/64*40,a=clamp((d-9)/6,0,1),o=(j*128+i)*4;px[o]=0x25;px[o+1]=0x33;px[o+2]=0x3c;px[o+3]=Math.round(a*a*(3-2*a)*255);}\n const tex=new T.DataTexture(px,128,128,T.RGBAFormat);tex.colorSpace=T.SRGBColorSpace;tex.magFilter=tex.minFilter=T.LinearFilter;tex.generateMipmaps=false;tex.needsUpdate=true;\n const fade=new T.Mesh(new T.PlaneGeometry(80,80),new T.MeshBasicMaterial({map:tex,transparent:true,fog:false,depthWrite:false}));fade.rotation.x=-Math.PI/2;fade.position.set(center.x,.04,center.z);fade.renderOrder=2;\n g.add(floor,fade);g.name='rts-edge-fade';return g;\n}\n\n// Dummy world ring (S3): ground plane + 3x3 cells of blocks (centre cell empty = the base), one InstancedMesh so it is 2 draw calls.\nexport function dummyRing(T){\n const g=new T.Group(),CELL=120,mat=createPs1Material(T,{vertexColors:false}),gmat=createPs1Material(T,{vertexColors:false});\n const ground=new T.Mesh(new T.PlaneGeometry(CELL*30,CELL*30),gmat);ground.rotation.x=-Math.PI/2;gmat.color.set(0x3a4a3c);g.add(ground);\n const spots=[];for(let i=-1;i<=1;i++)for(let j=-1;j<=1;j++){if(!i&&!j)continue;for(let k=0;k<8;k++){const a=Math.abs(Math.sin((i*7+j*3+k*1.7)*12.9898)*43758.5453)%1,b=Math.abs(Math.sin((i*5+j*11+k*2.3)*78.233)*43758.5453)%1;spots.push([i*CELL+(a-.5)*CELL*.9,j*CELL+(b-.5)*CELL*.9,6+a*30]);}}\n const box=new T.InstancedMesh(new T.BoxGeometry(1,1,1),mat,spots.length),M=new T.Matrix4();mat.color.set(0x6b2fa0);\n spots.forEach(([x,z,h],n)=>{M.compose(new T.Vector3(x,h/2,z),new T.Quaternion(),new T.Vector3(10,h,10));box.setMatrixAt(n,M);});\n box.frustumCulled=false;g.add(box);\n return {group:g,setFade(v){mat.userData.ps1.setFade(v);gmat.userData.ps1.setFade(v);},dispose(){g.removeFromParent();box.geometry.dispose();mat.dispose();ground.geometry.dispose();gmat.dispose();}};\n}\n// ?perf=1 overlay: avg/max frame ms over the last second, draw calls, triangles, JS heap (n/a on iOS Safari).\nfunction createPerfOverlay(wrap){\n const el=wrap.ownerDocument.createElement('pre');el.style.cssText='position:absolute;left:4px;top:4px;z-index:9;margin:0;padding:3px 5px;background:#000a;color:#7f7;font:11px/1.2 monospace;pointer-events:none';el.dataset.perf='';wrap.append(el);\n let n=0,sum=0,max=0,timer=0;\n return {add(dt){n++;sum+=dt;max=Math.max(max,dt);},\n  start(get){timer=setInterval(()=>{const i=get()||{},h=performance.memory?.usedJSHeapSize;el.textContent=`frame ${n?(sum/n).toFixed(1):'-'} avg / ${max.toFixed(1)} max ms\\ncalls ${i.calls??'-'}  tris ${i.tris??'-'}\\nheap ${h?(h/1048576).toFixed(0)+' MB':'n/a'}`;n=sum=max=0;},1000);},\n  stop(){clearInterval(timer);el.remove();}};\n}\n\n// controlShips (R81 lane A): hangar ships are air troops you can tap-select and order. Default on only for the player's own base view (a sceneMode with\n// clearSelection, i.e. canvas-scene); the raid's tapper has none, so a target's ships stay scenery.\nexport function mountBase3d(wrap,{getBase,getCoachBase=getBase,getPresentationScope,sceneMode,rts,signal,command,toast,crewScale=1,controlShips=typeof sceneMode?.clearSelection==='function',deps={}}){\n let rtsMod=null;const doc=wrap.ownerDocument??document,D={\n  loadThree:()=>import('/vendor/three/three.module.js'),\n  loadSheet:()=>import('./runtime.mjs').then(m=>{rtsMod=m;return m.loadSheet();}),\n  factionSheet:n=>rtsMod?.factionSheet(n),\n  createRenderer:(T,canvas)=>new T.WebGLRenderer({canvas,alpha:true,antialias:false}),\n  fit:fitPixelRenderer,billboardCanvas:null,groundCanvas:(terrain)=>{const c=doc.createElement('canvas');c.width=c.height=768;drawTerrain(c.getContext('2d'),terrain,{unit:32,ox:0,oy:0});return c;},loadTerrain3d:()=>optional('./terrain3d.mjs'),loadDetail:()=>optional('./base-detail.mjs'),...deps};\n const worldOn=deps.world??worldFlag(),Z=worldOn?ZOOM_WORLD:ZOOM,layers=[],layerQ=[];let fx=null;\n const view={yaw:0,pitch:PITCH0,zoom:ZOOM0,pan:[0,0]},frameHooks=new Set(),queued=[],st={frames:0,ms:0,max:0,win:0,winMs:0};\n const perf=/[?&]perf=1\\b/.test(globalThis.location?.search||'')?createPerfOverlay(wrap):null;\n let disposed=false,S=null,resolveReady,firstRes;const ready=new Promise(r=>{resolveReady=r;}),firstFrame=new Promise(r=>{firstRes=r;}); // both settle once: ready -> {T,..}|null, firstFrame -> true after the first render, false if disposed/failed first\n const dead=()=>disposed||signal?.aborted;\n const abort=new AbortController(),opts={signal:abort.signal};\n const canvas2d=wrap.querySelector('canvas'),canvas=doc.createElement('canvas');\n canvas.setAttribute('aria-label','Your base in 3D. Tap a building, or hold then drag to select troops. Drag to orbit, pinch to zoom.');\n canvas.dataset.base3d='';canvas.style.cssText='position:absolute;touch-action:none;outline:none;z-index:1;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent';\n if(getComputedStyle(wrap).position==='static')wrap.style.position='relative';\n const rectEl=doc.createElement('div');rectEl.style.cssText='position:absolute;display:none;pointer-events:none;border:1px solid #fff0a6;background:#d5dfa61a;z-index:2';\n const tagCanvas=doc.createElement('canvas');tagCanvas.setAttribute('aria-hidden','true');tagCanvas.style.cssText='position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:2';\n (canvas2d?.after?canvas2d.after(canvas,tagCanvas,rectEl):wrap.append(canvas,tagCanvas,rectEl));\n\n const claim=claimGraphics('coach-world');\n\n // ---------- input (copied from canvas-scene.mjs: hold = box select, drag = orbit, pinch = zoom, two-finger drag = pan) ----------\n const pointers=new Map(),local=e=>{const r=wrap.getBoundingClientRect();return {x:e.clientX-r.left,y:e.clientY-r.top};};\n let pinch=0,mid=null;\n let dmOn=false,armed=null; // drag-move active: a long-press that started on a built idle building moves it instead of box-selecting\n const dm=command?createDragMove({getBase,command,toast}):null;\n const gesture=createTouchGesture({onHold:()=>{if(dmOn)return;try{navigator.vibrate?.(12);}catch{}\n  const st=gesture.state,id=dm&&S&&st?moverAt(st.start):null,tile=id?tileAt(st.start):null;\n  if(id&&tile&&dm.begin(id,tile)){dmOn=true;canvas.dataset.troopGesture='move';showGhost(dm.hover(tile));return;}\n  canvas.dataset.troopGesture='select';}});\n function moverAt(p){ // built, idle building under the finger: sprite hit first (billboards stand above their footprint), else the footprint tile\n  if(!S)return null;const base=getBase(),ray=new S.T.Raycaster(),n=ndcOf(p);ray.setFromCamera({x:n[0],y:n[1]},S.camera);\n  const id=pickBuildingId(ray,S.build.meshes,S.build.layer,base);\n  if(id&&movable(base,id))return id;\n  const t=tileAt(p);return t?Object.keys(base.buildings).find(k=>{const d=BUILDINGS.find(x=>x.id===k),b=base.buildings[k];return movable(base,k)&&t.x>=b.x&&t.x<b.x+d.w&&t.y>=b.y&&t.y<b.y+d.h;})||null:null;\n }\n function showGhost(g){ // translucent footprint: green = valid drop, red = snaps back\n  if(!S)return;if(!S.ghost){const m=new S.T.Mesh(new S.T.PlaneGeometry(1,1),new S.T.MeshBasicMaterial({color:0x57e08a,transparent:true,opacity:.5,depthTest:false,fog:false,side:S.T.DoubleSide}));m.rotation.x=-Math.PI/2;m.renderOrder=4;\n   const e=new S.T.LineSegments(new S.T.EdgesGeometry(new S.T.PlaneGeometry(1,1)),new S.T.LineBasicMaterial({color:0xffffff,depthTest:false,fog:false}));e.name='edge';m.add(e);S.ghost=m;S.world.add(m);}\n  const m=S.ghost;m.visible=!!g;if(!g)return;m.material.color.set(g.ok?0x57e08a:g.reason?0xff4a4a:0xf0d890);m.scale.set(g.w*SCALE,g.h*SCALE,1);m.userData.g=g;\n  m.position.set((g.x+g.w/2-S.sp.x)*SCALE,(S.build?.t3?S.build.height(g.x+g.w/2,g.y+g.h/2):0)+.12,(g.y+g.h/2-S.sp.y)*SCALE);\n }\n function endMove(drop){\n  dmOn=false;const g=S?.ghost?.userData.g;if(S?.ghost)S.ghost.visible=false;delete canvas.dataset.troopGesture;\n  if(!drop||!dm.active){dm.cancel();return;}\n  dm.drop().then(r=>{if(r?.ok&&S)S.slide={id:r.id??g?.id,dx:(r.from.x-r.x)*SCALE,dz:(r.from.y-r.y)*SCALE,t0:0,key:S.key};}).catch(()=>{});\n }\n const ndcOf=p=>{const r=canvas.getBoundingClientRect(),w=wrap.getBoundingClientRect();return [((p.x+w.left-r.left)/r.width)*2-1,-(((p.y+w.top-r.top)/r.height)*2-1)];};\n const tileAt=p=>S?pickTile(S.T,S.camera,ndcOf(p),S.world,S.sp):null;\n function buildingAt(p){ // tap on a building mesh selects it (a roof would otherwise hit the ground behind it)\n  if(!S)return null;const ray=new S.T.Raycaster();ray.setFromCamera({x:ndcOf(p)[0],y:ndcOf(p)[1]},S.camera);\n  const id=pickBuildingId(ray,S.build.meshes,S.build.layer,getBase()),def=BUILDINGS.find(d=>d.id===id);\n  const at=def&&getBase()?.buildings?.[def.id];return def?{x:(at?.x??def.x)+def.w/2,y:(at?.y??def.y)+def.h/2}:null; // saved plot, not the catalog default (placed/moved buildings live elsewhere)\n }\n function unitAt(p){ // tap a troop (nearest sprite within 28 css px) -> select it, which opens the troop panel + command card\n  if(!S||!rts?.getUnitDots)return false;const W=wrap.getBoundingClientRect(),C=canvas.getBoundingClientRect(),v=new S.T.Vector3();S.camera.updateMatrixWorld(true);let best=null,bd=18; // 18 css px: a looser radius grabbed wandering troops on most ground taps, and then every tap rallied instead of building\n  for(const u of rts.getUnitDots()){v.set((u.x+.5-S.sp.x)*SCALE,(S.build?.t3?S.build.height(u.x,u.y):0)+.55*SCALE,(u.y+.5-S.sp.y)*SCALE);S.world.localToWorld(v);v.project(S.camera);if(v.z>1)continue;\n   const d=Math.hypot(C.left-W.left+(v.x*.5+.5)*C.width-p.x,C.top-W.top+(.5-v.y*.5)*C.height-p.y);if(d<bd){bd=d;best=u;}}\n  if(!best)return false;S.ships?.select([]);const c={x:best.x+.5,y:best.y+.5};sceneMode?.selectTiles?.(c,c,.45);return true;\n }\n // tap a mineral formation / gas vent -> sceneMode.resourceSelect({faction,kind}) (root HUD opens that hall's HQ harvest controls). Never touches selection, camera or orders;\n // placement (ghost / armed move) and an armed order mode keep the tap, and a sceneMode without resourceSelect (raid) ignores nodes.\n function resourceTap(p){\n  if(!S?.build?.nodes||typeof sceneMode?.resourceSelect!=='function'||S.ghost?.visible||armed||dmOn)return false;\n  try{if(sceneMode.getOrderState?.().mode)return false;}catch{}\n  const ray=new S.T.Raycaster(),n=ndcOf(p);ray.setFromCamera({x:n[0],y:n[1]},S.camera);S.world.updateMatrixWorld(true);\n  const hit=S.build.nodes.pick(ray.ray.clone().applyMatrix4(new S.T.Matrix4().copy(S.world.matrixWorld).invert()));\n  if(!hit)return false;sceneMode.resourceSelect(hit);return true;\n }\n // ---- R81 air troops: ship picking + orders (ships3d.mjs). The ground selection lives in sceneMode; ships keep their own and are merged into\n // sceneMode.getSelectedUnits (patched below) so the HUD selection panel lists them with Move / Hold / Stop. ----\n const shipProject=(v,size)=>{const W=wrap.getBoundingClientRect(),C=canvas.getBoundingClientRect(),toPx=q=>({x:C.left-W.left+(q.x*.5+.5)*C.width,y:C.top-W.top+(.5-q.y*.5)*C.height});\n  const top=v.clone().add(new S.T.Vector3(0,size*.5,0));v.project(S.camera);if(v.z>1)return null;const a=toPx(v),b=toPx(top.project(S.camera));return {...a,r:Math.hypot(a.x-b.x,a.y-b.y)};};\n function shipsInRect(a,b){if(!S?.ships||!controlShips)return [];const x0=Math.min(a.x,b.x),x1=Math.max(a.x,b.x),y0=Math.min(a.y,b.y),y1=Math.max(a.y,b.y);S.camera.updateMatrixWorld(true);\n  return S.ships.ships.filter(s=>{const q=shipProject(s.pivot.getWorldPosition(new S.T.Vector3()),s.size);return q&&q.x>=x0&&q.x<=x1&&q.y>=y0&&q.y<=y1;}).map(s=>s.id);}\n // tap with ships: tap a ship = select it (tap the only selected ship again = deselect); ships selected + tap ground = fly there (selected crew still get the tap)\n // R81 integration: placement (ghost / BUILD HERE / armed Move) always wins over ships, and with no ship selected a ship only takes a tap\n // inside its own screen radius (no 26 px slop) and never over a building (Ian R79: \"cannot place/build/move\").\n function shipTap(p){\n  if(!S?.ships||!controlShips)return false;if(S.ghost?.visible||armed||dmOn)return false;\n  const sh=S.ships,was=sh.selected();S.camera.updateMatrixWorld(true);const id=sh.pick(shipProject,p,was.length?26:0);\n  if(id&&!was.length&&S.build&&buildingAt(p))return false;\n  if(id){sh.select(was.length===1&&was[0]===id?[]:[id]);smOrig.clearSelection?.call(sceneMode);return true;}\n  if(!was.length)return false;const t=tileAt(p);if(!t)return true;\n  sh.order(was,{x:(t.x-S.sp.x)*SCALE,z:(t.y-S.sp.y)*SCALE});canvas.dataset.shipOrder=`${t.x.toFixed(2)},${t.y.toFixed(2)}`;\n  let crew=[];try{crew=smOrig.getSelectedUnits?.call(sceneMode)||[];}catch{}if(crew.length)sceneMode?.tapTile?.(t);\n  return true;\n }\n const smOrig={getSelectedUnits:sceneMode?.getSelectedUnits,deselect:sceneMode?.deselect,clearSelection:sceneMode?.clearSelection};\n function patchSceneMode(){\n  if(!controlShips||!sceneMode||typeof sceneMode!=='object')return;\n  sceneMode.getSelectedUnits=function(){let a=[];try{a=smOrig.getSelectedUnits?.call(sceneMode)||[];}catch{}return S?.ships?[...a,...S.ships.getSelected()]:a;};\n  sceneMode.deselect=function(ids){S?.ships?.deselect(ids);return smOrig.deselect?.call(sceneMode,ids);};\n  sceneMode.clearSelection=function(){S?.ships?.select([]);return smOrig.clearSelection?.call(sceneMode);};\n }\n function unpatchSceneMode(){if(!controlShips||!sceneMode||typeof sceneMode!=='object')return;for(const k of Object.keys(smOrig)){if(smOrig[k])sceneMode[k]=smOrig[k];else delete sceneMode[k];}}\n canvas.addEventListener('contextmenu',e=>e.preventDefault(),opts);\n // Taps are handled on pointerup. Cancel the touch's compat click, else it lands on whatever that tap just opened under the finger\n // (the taller R83 build tray: its TECH NETWORK button / tiles caught the click and closed the tray again -> \"cannot place/build\").\n canvas.addEventListener('touchend',e=>{if(e.cancelable)e.preventDefault();},{...opts,passive:false});\n canvas.addEventListener('pointerdown',e=>{try{canvas.setPointerCapture?.(e.pointerId);}catch{/* pointer already gone: still handle the touch */}const p=local(e);pointers.set(e.pointerId,p);\n  if(pointers.size===1){gesture.down(p);if(armed&&S&&dm){const id=armed,b=getBase()?.buildings?.[id],d=BUILDINGS.find(x=>x.id===id),t=tileAt(p);armed=null;delete canvas.dataset.armMove;if(b&&d&&t&&dm.begin(id,{x:b.x+d.w/2,y:b.y+d.h/2})){dmOn=true;canvas.dataset.troopGesture='move';showGhost(dm.hover(t));}}}else{if(dmOn)endMove(false);gesture.pinch();rectEl.style.display='none';const [a,b]=[...pointers.values()];pinch=Math.hypot(a.x-b.x,a.y-b.y);mid={x:(a.x+b.x)/2,y:(a.y+b.y)/2};}},opts);\n canvas.addEventListener('pointermove',e=>{if(!pointers.has(e.pointerId))return;const p=local(e);pointers.set(e.pointerId,p);\n  if(pointers.size>=2){if(dmOn)endMove(false);gesture.pinch();const [a,b]=[...pointers.values()],d=Math.hypot(a.x-b.x,a.y-b.y),m={x:(a.x+b.x)/2,y:(a.y+b.y)/2};\n   if(pinch)view.zoom=clamp(view.zoom*d/pinch,...Z);pinch=d;if(mid)panBy(m.x-mid.x,m.y-mid.y);mid=m;return;}\n  const mv=gesture.move(p);if(!mv)return;\n  if(dmOn){const t=tileAt(p);if(t)showGhost(dm.hover(t));return;}\n  if(mv.mode==='pan'){view.yaw-=mv.dx*.01;view.pitch=clamp(view.pitch-mv.dy*.005,PITCH0-PITCH_SPAN,PITCH0+PITCH_SPAN);}\n  else if(mv.mode==='select'){const g=gesture.state,x=Math.min(g.start.x,g.point.x),y=Math.min(g.start.y,g.point.y);Object.assign(rectEl.style,{display:'block',left:x+'px',top:y+'px',width:Math.abs(g.start.x-g.point.x)+'px',height:Math.abs(g.start.y-g.point.y)+'px'});}},opts);\n const end=e=>{const count=pointers.size;pointers.delete(e.pointerId);rectEl.style.display='none';\n  if(e.type==='pointercancel'){if(dmOn)endMove(false);gesture.cancel();pinch=0;mid=null;delete canvas.dataset.troopGesture;return;}\n  if(count!==1){gesture.pinch();pinch=0;mid=null;return;}\n  if(dmOn){gesture.up();endMove(true);return;}\n  const g=gesture.up();delete canvas.dataset.troopGesture;if(!g||!S)return;\n  if(g.mode==='select'){S.ships?.select(shipsInRect(g.start,g.point));const a=tileAt(g.start),b=tileAt(g.point);if(a&&b)sceneMode?.selectTiles?.(a,b,g.moved?.5:1.5);return;}\n  if(g.mode!=='pending'||g.moved)return;\n  const p=local(e);if(shipTap(p))return;const hitB=buildingAt(p);if(!hitB&&resourceTap(p))return;if(!hitB&&unitAt(p))return;const tile=hitB||tileAt(p); /* a building under the finger wins over crew standing on it (else crew on the Landing Zone eat every tap) */if(tile)sceneMode?.tapTile?.(tile);};\n canvas.addEventListener('pointerup',end,opts);canvas.addEventListener('pointercancel',end,opts);\n function panBy(dx,dy){ // ground follows the fingers; px -> metres at the pivot distance\n  const h=Math.max(1,wrap.getBoundingClientRect().height),k=2*distanceFor(view.zoom,Z)*Math.tan((S?.camera.fov??60)*Math.PI/360)/h,s=Math.sin(view.yaw),c=Math.cos(view.yaw);\n  view.pan[0]=clamp(view.pan[0]-dx*k*c-dy*k*s,-PAN_MAX,PAN_MAX);view.pan[1]=clamp(view.pan[1]+dx*k*s-dy*k*c,-PAN_MAX,PAN_MAX);\n }\n\n // ---------- scene ----------\n async function setup(){\n  await claim.ready;if(dead())return;\n  const [T,terrainMod,detailMod,sheet]=await Promise.all([D.loadThree(),D.loadTerrain3d(),D.loadDetail(),Promise.resolve().then(D.loadSheet).catch(()=>null)]);if(dead())return;\n  const base0=getBase();if(!base0){teardown();return;}\n  const renderer=D.createRenderer(T,canvas),scene=new T.Scene(),camera=new T.PerspectiveCamera(60,1,.1,FAR_PLANE),sp=spawnTile();\n  scene.background=new T.Color(FOG);scene.fog=new T.Fog(FOG,60,100); // near/far follow the camera distance every frame (fog only starts past the map edge)\n  // V1: DFO fog law (radial distance, .85 hold, rim close at fog far); horizon colour = FOG = the sky's low band.\n  // V5: + valley height haze (DFO hazeAt) measured from the view's ground focus, and the weather density weather-fx writes.\n  const fogOpts=useFogLaw(T,scene,{haze:BASE_HAZE,origin:new T.Vector3()});\n  const sun=new T.DirectionalLight(0xfff4e0,3.2);sun.position.set(-6,12,5);scene.add(new T.AmbientLight(0xffffff,1.5),new T.HemisphereLight(0xcfd8ff,0x4a5a48,1.4),sun);\n  const world=new T.Group();world.rotation.y=worldYaw();world.name='rts-base3d-world';\n  const center={x:(CENTER_TILE-sp.x)*SCALE,z:(CENTER_TILE-sp.y)*SCALE},shipSlot=new T.Group();shipSlot.name='rts-ships-slot';\n  const sky=skyDome(T);scene.add(sky,world);world.add(shipSlot);world.updateMatrixWorld(true);\n  const pivot=world.localToWorld(new T.Vector3(center.x,0,center.z)).toArray();\n  S={sky,T,renderer,scene,camera,world,sp,center,pivot,shipSlot,degrade:0,build:null,crew:null,detail:null,key:'',raf:0,done:false,last:-1e9,ore:null,rings:[],sheet,terrainMod,detailMod};\n  // R81 lane F: alien sky (follows the camera, full strength from the orbit cam) + sky critters around the base centre; lane D: depth-edge outline (off at degrade >= 2)\n  {const seed=[...String(base0.seed??1)].reduce((h,c)=>Math.imul(h,31)+c.charCodeAt(0)|0,7);\n   try{S.skySeed=seed;S.skyFaction=base0.faction;S.alien=createAlienSky(T,{seed,faction:base0.faction,reducedMotion:globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches});scene.add(S.alien.group);S.critters=createSkyCritters(T,{seed,scale:1.6});S.critters.mesh.position.set(center.x,0,center.z);world.add(S.critters.mesh);}catch(e){console.warn('coach-world sky',e);}\n   S.outline=createOutline(T,renderer,{retro:/[?&]retro=1\\b/.test(globalThis.location?.search||'')});} // V9: ?retro=1 = our 256-colour palette pass\n  const resolution=[320,180];\n  function build(base){ // everything that depends on the saved base; rebuilt when a building changes\n   if(S.alien&&S.skyFaction!==base.faction){S.alien.dispose();S.skyFaction=base.faction;S.alien=createAlienSky(T,{seed:S.skySeed,faction:base.faction,reducedMotion:globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches});scene.add(S.alien.group);}\n   const b={disposed:false,objs:[]},layer=createPixelBillboards(T,D.billboardCanvas?{getCanvas:D.billboardCanvas}:undefined),meshes=createBaseMeshes(T,base,{layer,resolution,nearRadius:0}) /* all buildings as sprite billboards: recognisable from above, like the 2D map */,terrain=createTerrain(base);\n   const gc=D.groundCanvas(terrain);\n   let t3=null;try{t3=terrainMod?.createTerrain3d?.(T,base,{scale:SCALE,heightAt:terrain.heightAt,groundCanvas:gc,avoid:nodeSpots(base),...(worldOn?{fadeColor:BIOMES[base.faction]?.ground}:{})});}catch(e){console.warn('coach-world terrain3d',e);}\n   let ground;\n   if(t3?.group){ground=t3.group;ground.position.set(center.x,0,center.z);}\n   else{\n    const tex=new T.CanvasTexture(gc);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.colorSpace=T.SRGBColorSpace;\n    ground=new T.Group();const plane=new T.Mesh(new T.PlaneGeometry(24*SCALE,24*SCALE,12,12),createPs1Material(T,{map:tex,vertexColors:false,resolution}));\n    plane.rotation.x=-Math.PI/2;plane.position.set(center.x,0,center.z);ground.add(plane,edgeFade(T,center));b.flat=plane;\n   }\n   let detail=null;try{detail=detailMod?.createBaseDetail?.(T,base,{scale:SCALE,origin:sp,meshes});}catch(e){console.warn('coach-world base-detail',e);}\n   let nodes=null;try{nodes=createNodes3d(T,{biomes:{NODE_SHAPES,shapeGeometry},getBase,scale:SCALE,origin:sp,ground:t3?(t3.relief?t3.relief:(x,y)=>terrain.heightAt(x,y)*.35*SCALE):null}); /* mineral formations + gas vents per hall; pickable */}catch(e){console.warn('coach-world resource nodes',e);}\n   world.add(ground,meshes.group,layer.group);const tags=layoutBase(base).map(l=>({id:l.id,level:l.level,p:[l.x,.1,l.z]}));if(detail?.group)world.add(detail.group);if(nodes)world.add(nodes.group);\n   let sprawl=null;try{if(controlShips&&new URLSearchParams(globalThis.location?.search||'').get('flight')!=='0'){sprawl=createBaseSprawl(T,{getBase});world.add(sprawl.group);}}catch(e){console.warn('base paths',e);}\n   Object.assign(b,{tags,layer,meshes,t3,ground,detail,nodes,terrain,sprawl,height:t3?(t3.relief?t3.relief:(x,y)=>terrain.heightAt(x,y)*.35*SCALE):()=>0});\n   return b;\n  }\n  function unbuild(b){\n   if(!b)return;b.sprawl?.dispose();b.t3?.dispose?.();b.detail?.dispose?.();b.detail?.group?.removeFromParent();\n   b.nodes?.dispose();if(b.ground)disposePixelObject(b.ground);\n   b.meshes.dispose();b.layer.dispose();\n  }\n  const keyOf=base=>`${base.seed}:${base.faction}:`+Object.values(base.buildings||{}).map(x=>`${x.id}:${x.x}:${x.y}:${x.level}`).join('|');\n  S.build=build(base0);S.key=keyOf(base0);\n  if(rts?.getUnitDots){\n   S.crew=createCrewBillboards(T,{units:()=>rts.getUnitDots(),getSheet:()=>sheet,getFactionSheet:n=>D.factionSheet?.(n),getBase:getCoachBase,scale:SCALE,origin:sp,unitHeight:1.3*SCALE*crewScale,isWorking:rts.isWorking?u=>rts.isWorking(u.id):null,showLooks:()=>view.zoom>=LOOKS_ZOOM,selectedIds:()=>{let a=[];try{a=sceneMode?.getSelectedUnits?.()||[];}catch{}return a;}});world.add(S.crew.group);\n  }\n  if(controlShips&&getPresentationScope){\n   const scope=getPresentationScope(),scopeKey=JSON.stringify([scope?.owner,scope?.epoch,scope?.gameGeneration]);\n   if(typeof scope?.owner==='string'&&scope.owner&&(scope.epoch==='guest'||Number.isSafeInteger(scope.epoch)&&scope.epoch>0)){\n    const storageKey='myr5-coach-pedestal-seen-v1/'+encodeURIComponent(scopeKey);\n    let seen=[];try{const saved=JSON.parse(globalThis.localStorage?.getItem(storageKey)||'[]');if(Array.isArray(saved))seen=saved.filter(id=>typeof id==='string');}catch{}\n    const seenIds=new Set(seen),current=()=>!dead()&&JSON.stringify([getPresentationScope()?.owner,getPresentationScope()?.epoch,getPresentationScope()?.gameGeneration])===scopeKey;\n    const panel=doc.createElement('div');panel.className='cw-pedestal-notice';panel.dataset.pedestalNotice='';panel.setAttribute('role','status');\n    Object.assign(panel.style,{position:'absolute',left:'8px',bottom:'8px',zIndex:'8',maxWidth:'calc(100% - 16px)',padding:'6px 9px',background:'#101820e8',color:'#f3e4bd',font:'700 12px/1.4 system-ui',borderRadius:'6px'});\n    const overview=doc.createElement('button');overview.type='button';overview.dataset.pedestalOverview='';overview.textContent='Coach pedestals';\n    const message=doc.createElement('span');message.dataset.pedestalMessage='';message.style.marginLeft='8px';\n    const skip=doc.createElement('button');skip.type='button';skip.textContent='Reveal now';skip.hidden=true;skip.onclick=()=>S?.pedestals?.skipReveal();\n    panel.append(overview,message,skip);wrap.append(panel);S.pedestalPanel=panel;\n    let cameraBefore=null;\n    overview.onclick=()=>{const rect=wrap.getBoundingClientRect(),aspect=rect.width/Math.max(1,rect.height);view.pan=[0,0];view.zoom=clamp(.56*Math.min(1,aspect),...Z);view.yaw=0;};\n    S.pedestals=createCoachPedestals(T,{getBase:()=>current()?getCoachBase():null,origin:sp,scale:SCALE,heightAt:(x,y)=>S?.build?.height(x,y)??0,\n     seenCoachIds:seenIds,reducedMotion:globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches===true,\n     onReveal(row){if(!current())return;cameraBefore??={pan:[...view.pan],zoom:view.zoom,yaw:view.yaw};\n      const p=world.localToWorld(new T.Vector3((row.coordinate.xTile-sp.x)*SCALE,0,(row.coordinate.yTile-sp.y)*SCALE));\n      view.pan=[p.x-pivot[0],p.z-pivot[2]];view.zoom=1;message.textContent='Revealing '+row.canonicalLabel;skip.hidden=false;},\n     onRevealed(row){if(!current())return;seenIds.add(row.coachId);try{globalThis.localStorage?.setItem(storageKey,JSON.stringify([...seenIds]));}catch{}\n      message.textContent=row.canonicalLabel+' statue unlocked';skip.hidden=true;\n      if(cameraBefore){Object.assign(view,cameraBefore);cameraBefore=null;}}\n    });world.add(S.pedestals.group);\n   }\n  }\n  const ringGeo=new T.RingGeometry(.3*SCALE,.4*SCALE,12),ringMat=new T.MeshBasicMaterial({color:0xfff0a6,side:T.DoubleSide,fog:false,depthTest:false,transparent:true});\n  S.ringGeo=ringGeo;S.ringMat=ringMat;\n  const resize=()=>{if(S&&!S.done){const s=D.fit(renderer,camera,wrap,effScale(wrap.getBoundingClientRect().width,wrap.getBoundingClientRect().height,S.degrade));camera.fov=fovFor(s.width/s.height);camera.updateProjectionMatrix();S.build?.flat?.material.userData.ps1?.setResolution(s.width,s.height);S.build?.meshes.setResolution(s.width,s.height);S.ps1Mats?.forEach(m=>m.userData.ps1.setResolution(s.width,s.height));S.res=[s.width,s.height];S.outline.resize(s.width,s.height);S.outline.enabled=S.degrade<2;}};\n  S.resize=resize;S.observer=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;S.observer?.observe(wrap);resize();\n  if(canvas2d)canvas2d.style.visibility='hidden';\n  for(const o of queued.splice(0))world.add(o);\n  // ---- R58 world layer + base dissolve ----\n  function mountLayer(factory){\n   try{const l=factory(T,{center,scale:SCALE,resolution:S.res||resolution,getBase});if(!l?.group)return;l.group.position.y=Math.min(l.group.position.y,-.05);world.add(l.group);layers.push(l);l.setFade?.(0);l.group.visible=false;fadeKey='';}catch(e){console.warn('coach-world world layer',e);}\n  }\n  let fadeKey='';S.mountLayer=mountLayer;\n  function applyFade(f,d){\n   // the world layer sits behind the dissolving base, so it stays opaque (no dither holes); base: every material (PS1 or not: terrain3d, sprites, crew) takes the alpha-hash fade uniform. Skip the helper meshes (rings, ghost, plots).\n   const k=f.toFixed(3)+S.key+(S.ps1Mats?.length||0)+(S.buildN||0)+world.children.length;\n   if(k!==fadeKey){fadeKey=k;\n    const skip=new Set([S.ghost,S.plots,...S.rings].filter(Boolean)),layerGroups=new Set(layers.map(l=>l.group));\n    const walk=o=>{if(skip.has(o)||layerGroups.has(o)||o===S.sky)return;const m=o.isMesh||o.isSprite||o.isPoints||o.isLine?o.material:null;\n     for(const x of Array.isArray(m)?m:m?[m]:[]){if(!x.isShaderMaterial)addFade(x).setFade(f);}\n     for(const c of o.children)walk(c);};\n    walk(world);}\n   const hide=f<=.001; // fully dissolved: stop drawing the base at all (its triangles are the biggest cost at mid zoom)\n   if(hide!==!!S.baseHidden){S.baseHidden=hide;const lg=new Set(layers.map(l=>l.group));for(const o of world.children){if(lg.has(o)||o===S.shipSlot)continue;if(hide&&o.visible){o.visible=false;(S.hidByFade??=new Set()).add(o);}else if(!hide&&S.hidByFade?.has(o)){o.visible=true;S.hidByFade.delete(o);}}}\n   for(const l of layers){const v=((f<.995||d>DIST0/.7)?1:0)*ringFade(d,l.minDist,l.maxDist);l.setFade?.(v);l.group.visible=v>.001;l.update?.(performance.now(),d,camera);}\n  }\n  for(const f of layerQ.splice(0))mountLayer(f);\n  if(worldOn){ // glue module from the world lanes (optional); else a dummy 3x3 ring so the zoom range is testable\n   optional('./world-layer.mjs').then(m=>{if(S&&!S.done)mountLayer(m?.createWorldLayer||dummyRing);}).catch(()=>{if(S&&!S.done)mountLayer(dummyRing);});\n  }\n  perf?.start(()=>({calls:renderer.info.render.calls,tris:renderer.info.render.triangles}));\n  let keyAt=0;\n  const frame=t=>{\n   if(S.done)return;S.raf=requestAnimationFrame(frame);if(doc.hidden||t-S.last<FRAME_MS)return;const dt=t-S.last;S.last=t;const t0=performance.now();\n   if(worldOn&&fx){ // R58: fully zoomed out = the personal base is unloaded (disposed), rebuilt on the way back in (hysteresis, invisible: base alpha is 0 below z01 .3)\n    const un=S.unloaded?fx.z01<.18:fx.z01<.08,b=!un&&S.unloaded?getBase():null;\n    if(un&&!S.unloaded){unbuild(S.build);S.build=null;S.unloaded=true;if(S.crew)S.crew.group.visible=false;}\n    else if(b){S.build=build(b);S.key=keyOf(b);S.unloaded=false;S.buildN=(S.buildN||0)+1;if(S.res)S.build.meshes.setResolution(...S.res),S.build.flat?.material.userData.ps1?.setResolution(...S.res);if(S.crew)S.crew.group.visible=true;}\n   }\n   if(t-keyAt>500&&!S.unloaded){keyAt=t;const base=getBase();if(base){const k=keyOf(base);if(k!==S.key){S.key=k;unbuild(S.build);S.build=build(base);if(S.res)S.build.meshes.setResolution(...S.res),S.build.flat?.material.userData.ps1?.setResolution(...S.res);}}}\n   if(!S.unloaded){S.crew?.update();S.pedestals?.update(t);if(S.pedestals){const p=S.pedestals.status();wrap.dataset.pedestalCount=String(p.earnedCount);wrap.dataset.pedestalSlots=String(p.slotCount);wrap.dataset.pedestalLoaded=String(p.loadedCount);wrap.dataset.pedestalReveal=p.currentReveal?.coachId??'';const button=S.pedestalPanel?.querySelector('[data-pedestal-overview]');if(button)button.textContent=`Coach pedestals (${p.earnedCount}/65)`;if(p.failedCount&&!p.currentReveal){const notice=S.pedestalPanel?.querySelector('[data-pedestal-message]');if(notice)notice.textContent='Coach art unavailable; reopen to retry.';}}}\n   if(S.slide&&S.build){ // a moved building glides from its old tile once the rebuilt base shows it at the new one (sprite + tag)\n    const sl=S.slide;if(!sl.t0&&S.key!==sl.key){sl.t0=t;const i=S.build.meshes.far.findIndex(f=>f.id===sl.id);sl.sprite=i<0?S.build.meshes.group.getObjectByName('rts-'+sl.id):S.build.layer.group.children[i];sl.label=S.build.tags.find(t=>t.id===sl.id);sl.home=[sl.sprite?.position.x,sl.sprite?.position.z];sl.lhome=[sl.label?.p[0],sl.label?.p[2]];}\n    if(sl.t0){const k=1-clamp((t-sl.t0)/260,0,1),e=k*k;if(sl.sprite)sl.sprite.position.set(sl.home[0]+sl.dx*e,sl.sprite.position.y,sl.home[1]+sl.dz*e);if(sl.label){sl.label.p[0]=sl.lhome[0]+sl.dx*e;sl.label.p[2]=sl.lhome[1]+sl.dz*e;}if(!k)S.slide=null;}\n   }\n   if(S.build?.t3&&S.crew)for(const s of S.crew.group.children)s.position.y=S.build.height((s.position.x/SCALE)+sp.x-.5,(s.position.z/SCALE)+sp.y-.5);\n   S.build?.sprawl?.update(t);S.build?.detail?.update?.(t);S.build?.nodes?.update(t/1000);S.build?.t3?.propLod?.(propLod(view.zoom)); // perf: thinner plant field as the camera pulls back (merged-shape instances are the base view's biggest tri cost)\n   for(const fn of frameHooks)try{fn(t,dt);}catch(e){console.warn('coach-world base3d hook',e);}\n   plots();\n   const d=distanceFor(view.zoom,Z);\n   if(worldOn){fx=zoomFx(view.zoom);const veil=(1-fx.f)*layers.reduce((v,l)=>Math.max(v,l.veil||0),0);scene.fog.near=fx.fogNear;scene.fog.far=veilFar(fx.fogNear,fx.fogFar,veil,20*SCALE);if(camera.far!==fx.far){camera.far=fx.far;camera.updateProjectionMatrix();}applyFade(fx.f,d);}\n   else{scene.fog.near=d+20;scene.fog.far=scene.fog.near+19*SCALE;}\n   const pv=[pivot[0]+view.pan[0],pivot[1],pivot[2]+view.pan[1]],pos=orbitPosition(pv,view.yaw,view.pitch,d);\n   fogOpts.origin.set(...pv);\n   for(const l of layers)l.scout?.((pv[0]-l.group.position.x)/SCALE,(pv[2]-l.group.position.z)/SCALE,d/SCALE); // V2: scouting = looking at it up close\n   if(api.cameraOverride)api.cameraOverride(camera);else{camera.position.set(...pos);camera.lookAt(...pv);} // raid pod-top camera hook (drop-pod-scene.mjs createCamToggle)\n   if(worldOn){S.sky.position.copy(camera.position);S.sky.scale.setScalar(Math.max(1,1.5*d/90));}\n   if(S.alien){camera.getWorldDirection(S.fwd??=new T.Vector3());const top=Math.asin(clamp(S.fwd.y,-1,1))*180/Math.PI+camera.fov/2; // the orbit cam looks down: sky only when the top edge clears the horizon (raid pod cam), else 0 draws\n    S.alien.group.position.copy(camera.position);S.alien.update(t/1000,top>-2?90:0);}S.critters?.update(t/1000);updateSpriteTint(); // V8: sprites follow the time of day\n   if(S.build?.tags&&!(fx&&fx.z01<.3))drawTags();else if(fx)tagCtx?.clearRect?.(0,0,tagCanvas.width,tagCanvas.height);\n   S.outline.render(scene,camera);if(!S.first){S.first=1;firstRes(true);}\n   const ms=performance.now()-t0;perf?.add(dt);st.frames++;st.ms+=ms;st.max=Math.max(st.max,ms);st.win++;st.winMs+=ms;if(st.win>=DEGRADE_WINDOW){const r=wrap.getBoundingClientRect(),d=nextDegrade(st.winMs/st.win,r.width,r.height,S.degrade);st.win=st.winMs=0;if(d!==S.degrade){S.degrade=d;resize();}}\n  };\n  const tagCtx=tagCanvas.getContext?.('2d'),bmps=new Map();\n  function plots(){ // faint outline of unbuilt defense plots, only while the Defend tray is open or one is selected\n   if(!S.build)return;let sel=null;try{sel=sceneMode?.selectedId?.()??null;}catch{}\n   const tray=doc.querySelector?.('.cw-rts-tray.is-open [data-tray-tab=\"defend\"][aria-selected=\"true\"]'),base=getBase();\n   const ids=base?BUILDINGS.filter(d=>[...DEFENSE_KINDS,'silo','yard','landing'].includes(d.kind)&&!((base.buildings?.[d.id]?.level)>0)&&(tray||d.id===sel)):[],key=ids.map(d=>d.id).join();\n   if(S.plotKey===key)return;S.plotKey=key;S.plots?.removeFromParent();S.plots?.geometry.dispose();S.plots=null;if(!ids.length)return;\n   const pts=[];for(const d of ids){const b=base.buildings?.[d.id]||d,x0=(b.x-sp.x)*SCALE,z0=(b.y-sp.y)*SCALE,x1=x0+d.w*SCALE,z1=z0+d.h*SCALE,y=.08;pts.push(x0,y,z0,x1,y,z0,x1,y,z0,x1,y,z1,x1,y,z1,x0,y,z1,x0,y,z1,x0,y,z0);}\n   const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(pts,3));\n   S.plots=new T.LineSegments(g,new T.LineBasicMaterial({color:0x9fd8e8,transparent:true,opacity:.45,fog:false,depthTest:false}));S.plots.renderOrder=3;world.add(S.plots);\n  }\n  function drawTags(){ // see tagPlan\n   if(!tagCtx||!tagCtx.clearRect)return;const dpr=Math.max(1,Math.round(Math.min(globalThis.devicePixelRatio||1,3))),W=wrap.getBoundingClientRect(),C=canvas.getBoundingClientRect(),cw=Math.round(W.width*dpr),ch=Math.round(W.height*dpr);\n   if(tagCanvas.width!==cw||tagCanvas.height!==ch){tagCanvas.width=cw;tagCanvas.height=ch;}\n   tagCtx.clearRect(0,0,cw,ch);tagCtx.imageSmoothingEnabled=false;camera.updateMatrixWorld(true);\n   const v=new T.Vector3(),items=[];\n   for(const t of S.build.tags){v.fromArray(t.p);world.localToWorld(v);const depth=-v.clone().applyMatrix4(camera.matrixWorldInverse).z;if(depth<=0)continue;v.project(camera);\n    const text=labelText(t),px=labelPixels(text);items.push({id:t.id,level:t.level,text,depth,w:px.w,h:px.h,x:C.left-W.left+(v.x*.5+.5)*C.width,y:C.top-W.top+(.5-v.y*.5)*C.height});}\n   let sel=null;try{sel=sceneMode?.selectedId?.()??null;}catch{}\n   for(const it of tagPlan(items,{zoom:view.zoom,selectedId:sel})){\n    const key=it.text,site=it.level<=0;let bmp=bmps.get(key);\n    if(!bmp){const px=labelPixels(key);bmp=doc.createElement('canvas');bmp.width=px.w;bmp.height=px.h;const x=bmp.getContext('2d');\n     x.fillStyle='#09171e';x.fillRect(0,0,px.w,px.h);x.fillStyle='#2b4650';x.fillRect(0,0,px.w,1);x.fillRect(0,px.h-1,px.w,1);x.fillRect(0,0,1,px.h);x.fillRect(px.w-1,0,1,px.h);\n     x.fillStyle=site?'#b7c8c5':'#f0d890';for(const [i,j] of px.on)x.fillRect(i,j,1,1);bmps.set(key,bmp);}\n    tagCtx.drawImage(bmp,Math.round(it.x*dpr)-Math.floor(it.w*dpr/2),Math.round(it.y*dpr),it.w*dpr,it.h*dpr);\n   }\n  }\n  // Ship GLBs arrive async with smooth PBR materials: swap them to the PS1 material (vertex snap + dither) like the rest of the world.\n  let psAt=0;S.ps1Mats=[];\n  function ps1Ships(t){\n   if(t-psAt<400)return;psAt=t;\n   world.traverse(o=>{const m=o.isMesh&&o.material;if(!m||m.userData.ps1||m.isMeshBasicMaterial||!(m.isMeshStandardMaterial||m.isMeshPhysicalMaterial))return;\n    const n=createPs1Material(T,{map:m.map||null,vertexColors:!!o.geometry.attributes.color,resolution:S.res||resolution});if(!m.map)n.color.copy(m.color);n.userData.swapped=1;\n    S.ps1Mats.push(n);o.material=n;m.dispose();});\n  }\n  S.unbuild=unbuild;\n  wrap.dataset.base3d='1';frame(0);\n  resolveReady({T,world,scene,camera,center});\n  optional('./ships3d.mjs').then(m=>{if(m?.mountShips3d&&S&&!S.done){S.stopShips=m.mountShips3d(T,api,{getBase,scale:SCALE*2});S.ships=S.stopShips.ships;patchSceneMode();S.stopPs1=api.onFrame(ps1Ships);}}).catch(e=>console.warn('coach-world ships3d',e));\n }\n setup().catch(e=>{console.warn('coach-world base3d',e);resolveReady(null);teardown();if(canvas2d)canvas2d.style.visibility='';});\n\n function teardown(){\n  resolveReady(null);firstRes(false); // no-op when already settled: a disposed handle must never leave ready/firstFrame pending\n  const s=S;S=null;abort.abort();canvas.remove();tagCanvas.remove();rectEl.remove();delete wrap.dataset.base3d;for(const key of ['pedestalCount','pedestalSlots','pedestalLoaded','pedestalReveal'])delete wrap.dataset[key];\n  if(canvas2d)canvas2d.style.visibility='';\n  perf?.stop();\n  if(s){\n   s.done=true;cancelAnimationFrame(s.raf);s.observer?.disconnect();\n   try{unpatchSceneMode();s.stopShips?.();s.stopPs1?.();}catch{}s.crew?.dispose();s.pedestals?.dispose();s.pedestalPanel?.remove();s.unbuild?.(s.build);s.ringGeo?.dispose();s.ringMat?.dispose();s.alien?.dispose();s.critters?.dispose();s.outline?.dispose();disposePixelObject(s.scene);\n   const rd=s.renderer;void releaseGraphics(canvas,rd.getContext?.(),()=>{rd.dispose();rd.forceContextLoss?.();});\n  }\n  claim();\n }\n const onAbort=()=>dispose();signal?.addEventListener('abort',onAbort,{once:true});\n function dispose(){if(disposed)return;disposed=true;signal?.removeEventListener('abort',onAbort);abort.abort();gesture.dispose?.();pointers.clear();frameHooks.clear();teardown();}\n\n const api={\n  dispose,ready,\n  armMove(id){const b=getBase()?.buildings?.[id];if(!dm||!movable(getBase(),id)||!b)return false;armed=id;canvas.dataset.armMove=id;return true;},\n  cancelMove(){armed=null;delete canvas.dataset.armMove;},\n  preview(g){if(!dmOn)showGhost(g||null);}, // placement ghost from the HUD {id,x,y,w,h,ok,reason}; null hides it\n  get armedMove(){return armed;},\n  recenter(){Object.assign(view,{yaw:0,pitch:PITCH0,zoom:ZOOM0,pan:[0,0]});},\n    setZoom(z){view.zoom=clamp(z,...Z);},\n  addWorldLayer(factory){if(!worldOn)return;if(S?.mountLayer)S.mountLayer(factory);else layerQ.push(factory);},\n  addToWorld(obj){if(S)S.world.add(obj);else queued.push(obj);},\n  onFrame(fn){frameHooks.add(fn);return()=>frameHooks.delete(fn);},\n  get center(){return S?.center??{x:(CENTER_TILE-spawnTile().x)*SCALE,z:(CENTER_TILE-spawnTile().y)*SCALE};},\n  get ships(){return S?.ships??null;}, // R81 air troops handle (ships3d) for the HUD/tests\n  stats:()=>({frames:st.frames,avgMs:st.frames?st.ms/st.frames:0,maxMs:st.max,...(S?{calls:S.renderer.info?.render?.calls,tris:S.renderer.info?.render?.triangles}:{})}),\n  get worldOn(){return worldOn;},get fx(){return fx;},\n  get view(){return view;},get firstFrame(){return firstFrame;},\n  get cam(){return {pan:view.pan,pitch:view.pitch,dist:distanceFor(view.zoom,Z),aspect:wrap.clientWidth/Math.max(1,wrap.clientHeight)};},\n };\n if(globalThis.document)optional('./weather-fx.mjs').then(m=>m?.attachWeather?.(api)).catch(()=>{}); // R58: weather over the base and world (own ?weather= override)\n wrap.__base3d=api; // debug/test handle\n return api;\n}\n","modules/coach-world-rts/battle-sim.mjs":"import {BUILDING_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {buildingFunction,DEFENSE_SYSTEM_KINDS} from '../coach-world/base-systems.mjs';\r\n// W4 battle sim P1. Pure integer, deterministic, shared by Node, Worker and browser. No Math.random/Date/trig, no decimals in logic.\r\n// Units: position 1/256 tile, HP/damage tenths, time = ticks (10 Hz). Both sides act on the same snapshot and damage lands together, so side order never matters.\r\nexport const RULES_VERSIONS=[2,3,4,5,6]; // 3 adds role t (defense tower, immobile); 4 adds input.abilities (faction cooldown cannon); 5 adds role a (air ship); 6 adds infrastructure; older logs replay unchanged\r\nexport const TICK_MS=100,MAX_UNITS=8,FIGHT_TICKS=600,MAX_TICKS=750,CHECK_EVERY=50;\r\nexport const W=20*256,H=12*256,PAD_Y=[768,1536,2304],PAD_X=512,RADIUS=180,SEP_MAX=12;\r\n// role: hp, armor, atk, cooldown ticks, range, speed (1/256 tile per tick), supply, tag, weapon kind. Atk/armor/hp in tenths of HP.\r\nexport const ROLES={\r\n g:{name:'grunt',hp:1250,ar:0,atk:60,cd:8,rng:320,spd:56,sup:1,tag:'soft',wpn:'rapid',acq:512},\r\n s:{name:'scout',hp:820,ar:0,atk:40,cd:6,rng:320,spd:90,sup:1,tag:'nimble',wpn:'rapid',acq:1024,aa:true},\r\n h:{name:'hauler',hp:1000,ar:10,atk:0,cd:0,rng:0,spd:51,sup:2,tag:'plated',wpn:null,acq:0},\r\n b:{name:'boss',hp:4000,ar:10,atk:140,cd:12,rng:384,spd:46,sup:3,tag:'plated',wpn:'burst',acq:512},\r\n t:{name:'tower',hp:3000,ar:20,atk:80,cd:10,rng:512,spd:0,sup:1,tag:'plated',wpn:'bolt',acq:512,el:'ice',aa:true}, // v3 only; placed from tiles, never moves\r\n a:{name:'ship',hp:900,ar:0,atk:45,cd:8,rng:384,spd:90,sup:2,tag:'nimble',wpn:'rapid',acq:1024,air:true}, // v5 only; flies: no separation push, only aa ground roles (scout, tower) or other ships can hit it\r\n};\r\nexport const BONUS={burst:{plated:40},rapid:{nimble:20}};\r\nexport const MAX_TOWERS=3;\r\n// v5: who may shoot whom. Air shoots anything; ground needs aa to hit air. Before v5 no air exists, so this is always true there.\r\nexport const canHit=(a,b)=>!b.air||!!a.air||!!a.aa;\r\nconst WPN_STEP=10,ARM_STEP=10,PVP_CAP=2,FLOOR=5;\r\n// v4 raid cooldown cannon. One per faction: an AOE (radius tiles*256) of integer effects. Durations in ticks (10 Hz), dmg in tenths of HP, pct are whole percents.\r\n// Fields: dmg instant damage, dot {v per 5 ticks}, slow/haste pct speed, shred armor removed, stun ticks, push knockback distance (1/256 tile), heal tenths. who: 'foe' | 'ally'.\r\nexport const ABILITY_RADIUS=640,ABILITY_COOLDOWN=300,ABILITY_CHARGES=2,ABILITY_WARMUP=20,ABILITY_LEAD=6;\r\nexport const ABILITIES=Object.freeze({\r\n chest:{name:'Pressforge Lance',who:'foe',dmg:300,shred:10,dur:50},          // red laser: burst + armor shred\r\n 'arms-shoulders':{name:'Orbit Slam',who:'foe',push:768,stun:8,dur:8},     // gold laser: knockback + brief stun\r\n quads:{name:'Quake Drum',who:'foe',dmg:100,stun:20,dur:20},                // rocks: stun\r\n glutes:{name:'Felspell Cloud',who:'foe',dot:15,dur:60},                    // poison: damage over time (15 per 0.5 s for 6 s = 180)\r\n yoga:{name:'Stillwave Frost',who:'foe',dmg:250,slow:40,dur:30},          // ice: 25 HP + 40% slow for 3 s\r\n 'martial-arts':{name:'Moltguard Blaze',who:'foe',dmg:150,dot:10,shred:10,dur:50}, // fire: burn + armor melt\r\n cardio:{name:'Pulseway Surge',who:'ally',haste:50,heal:100,dur:80},        // electric: own troops +50% speed/attack rate and a small heal\r\n});\r\n// Pure rules check for the UI and the validator: ok if side may cast at `tick` given earlier casts.\r\nexport function abilityState(abilities,side,tick){\r\n const mine=(abilities||[]).filter(a=>(a.side??0)===side);\r\n const last=mine.reduce((m,a)=>Math.max(m,a.tick),-1e9),left=ABILITY_CHARGES-mine.length,cd=Math.max(0,last+ABILITY_COOLDOWN-tick);\r\n return {charges:left,cooldown:cd,ready:left>0&&cd===0&&tick>=ABILITY_WARMUP};\r\n}\r\nconst modOf=(u,k,T)=>{const m=u.m&&u.m[k];return m&&m.until>T?m.v:0;};\r\nconst applyMod=(u,k,v,until)=>{const m=u.m[k];if(!m||m.until<=until||m.v<v)u.m[k]={v:Math.max(v,m?m.v:0),until:Math.max(until,m?m.until:0)};};\r\nconst idiv=(a,b)=>Math.trunc(a/b);\r\nexport function isqrt(n){if(n<2)return n;let x=n,y=(x+1)>>1;while(y<x){x=y;y=(x+idiv(n,x))>>1;}return x;}\r\nexport function makeRng(seed){let s=(seed>>>0)||0x9e3779b9;return n=>{s^=s<<13;s>>>=0;s^=s>>>17;s^=s<<5;s>>>=0;return s%n;};}\r\n// SC2 damage: max(0.5, atk + bonus - armor) in tenths\r\nexport const damage=(atk,wpn,tag,armor)=>Math.max(FLOOR,atk+(BONUS[wpn]?.[tag]??0)-armor);\r\n\r\nfunction check(input){\r\n const {rulesVersion,seed,rosters,pads}=input||{};\r\n if(!RULES_VERSIONS.includes(rulesVersion))throw new Error('unsupported rulesVersion');\r\n if(!Number.isInteger(seed))throw new Error('bad seed');\r\n if(!Array.isArray(rosters)||rosters.length!==2||!Array.isArray(pads)||pads.length!==2)throw new Error('bad sides');\r\n const systems=input.systems;\r\n if(rulesVersion===6){\r\n  if(!Array.isArray(systems)||systems.length!==2)throw new Error('bad systems');\r\n  for(const side of systems){\r\n   if(!Array.isArray(side)||side.length>32)throw new Error('bad systems');\r\n   const seen=new Set();\r\n   for(const p of side){\r\n    const d=BUILDING_BY_ID[p?.id];\r\n    if(!d||!DEFENSE_SYSTEM_KINDS.includes(d.kind)||p.kind!==d.kind||seen.has(p.id)||!Number.isInteger(p.level)||p.level<1||p.level>5||!Number.isInteger(p.x)||!Number.isInteger(p.y)||p.x<0||p.x>W||p.y<0||p.y>H)throw new Error('bad systems');\r\n    seen.add(p.id);\r\n   }\r\n  }\r\n }else if(systems!==undefined)throw new Error('bad systems version');\r\n const ab=input.abilities;\r\n if(ab!==undefined){\r\n  if(rulesVersion<4||!Array.isArray(ab)||ab.length>2*ABILITY_CHARGES)throw new Error('bad abilities');\r\n  const seen=[[],[]];\r\n  for(const a of ab){\r\n   const sd=a?.side??0;\r\n   if(!a||!ABILITIES[a.faction]||(sd!==0&&sd!==1)||!Number.isInteger(a.tick)||a.tick<0||a.tick>=MAX_TICKS||!Number.isInteger(a.x)||!Number.isInteger(a.y)||a.x<0||a.x>W||a.y<0||a.y>H)throw new Error('bad abilities');\r\n   const prev=seen[sd];\r\n   if(prev.length>=ABILITY_CHARGES||a.tick<ABILITY_WARMUP||(prev.length&&a.tick-prev.at(-1)<ABILITY_COOLDOWN))throw new Error('bad abilities'); // must be in tick order per side\r\n   prev.push(a.tick);\r\n  }\r\n }\r\n for(let s=0;s<2;s++){\r\n  const r=rosters[s];\r\n  if(typeof r?.u!=='string'||r.u.length<1||r.u.length>MAX_UNITS||[...r.u].some(c=>!ROLES[c]||(c==='a'&&rulesVersion<5)))throw new Error('bad roster');\r\n  const nt=[...r.u].filter(c=>c==='t').length;\r\n  if(nt&&(rulesVersion<3||nt>MAX_TOWERS||nt===r.u.length))throw new Error('bad towers');\r\n  if(r.towers!==undefined&&(rulesVersion<3||!Array.isArray(r.towers)||r.towers.length!==nt||r.towers.some(p=>!Array.isArray(p)||p.length!==2||!Number.isInteger(p[0])||!Number.isInteger(p[1])||p[0]<0||p[0]>W||p[1]<0||p[1]>H)))throw new Error('bad towers');\r\n  if(!Number.isInteger(r.w)||!Number.isInteger(r.a)||r.w<0||r.a<0||r.w>9||r.a>9)throw new Error('bad upgrades');\r\n  if(!Array.isArray(pads[s])||pads[s].length<1||pads[s].length>3||pads[s].some(p=>!Number.isInteger(p)||p<0||p>2))throw new Error('bad pads');\r\n }\r\n}\r\n\r\nexport function simulate(input){\r\n check(input);\r\n const {seed,rosters,pads}=input,rnd=makeRng(seed),abil=input.abilities||[];\r\n // PvP upgrade lead capped at +2 (the ahead side is reduced to the cap)\r\n const up=[{w:rosters[0].w,a:rosters[0].a},{w:rosters[1].w,a:rosters[1].a}];\r\n for(const k of ['w','a']){if(up[0][k]-up[1][k]>PVP_CAP)up[0][k]=up[1][k]+PVP_CAP;if(up[1][k]-up[0][k]>PVP_CAP)up[1][k]=up[0][k]+PVP_CAP;}\r\n const systems=input.rulesVersion===6?input.systems.map(side=>side.map(p=>({...p,e:buildingFunction({buildings:{[p.id]:{level:p.level}}},p.id).effects,spent:false}))):[[],[]];\r\n const sum=(side,key)=>systems[side].reduce((n,p)=>n+(p.e[key]||0),0);\r\n const units=[];\r\n for(let s=0;s<2;s++)for(let i=0;i<rosters[s].u.length;i++){\r\n  const def=ROLES[rosters[s].u[i]],pl=pads[s],col=idiv(i,pl.length);\r\n  let dx=PAD_X+col*RADIUS+rnd(65),dy=rnd(129)-64,x=s?W-dx:dx,y=PAD_Y[pl[i%pl.length]]+dy;\r\n  if(def.spd===0){ // tower: tile placement from rosters[s].towers, else a default line in front of the base edge\r\n   const k=[...rosters[s].u.slice(0,i)].filter(c=>c==='t').length,at=rosters[s].towers?.[k];\r\n   x=at?at[0]:(s?W-1536:1536);y=at?at[1]:PAD_Y[k%3];\r\n  }\r\n  const u={id:s*MAX_UNITS+i,side:s,i,def,x,y,hp:def.hp,max:def.hp,\r\n   cd:def.cd?rnd(def.cd):0,tgt:-1,hitBy:-1,hitT:-99,alive:true,m:{},\r\n   atk:def.atk?def.atk+up[s].w*WPN_STEP:0,ar:def.ar+up[s].a*ARM_STEP};\r\n  if(input.rulesVersion===6){\r\n   u.hp+=sum(s,'garrisonHp');u.ar+=sum(s,'garrisonArmor');\r\n   if(def.name==='tower'){\r\n    const k=[...rosters[s].u.slice(0,i)].filter(c=>c==='t').length,p=systems[s].filter(p=>p.kind==='tower')[k];\r\n    if(p){u.hp+=p.e.towerHpBonus;u.atk+=p.e.towerDamageBonus;}\r\n    u.atk=idiv(u.atk*(100+sum(s,'defenseDamagePct')),100);\r\n   }\r\n   u.max=u.hp;\r\n  }\r\n  units.push(u);\r\n }\r\n const n=units.length,byId=new Array(2*MAX_UNITS).fill(null);for(const u of units)byId[u.id]=u;\r\n const events=[],checksums=[],trace=[];\r\n const snap=()=>{const a=new Int32Array(2*MAX_UNITS*3);for(const u of units){a[u.id*3]=u.x;a[u.id*3+1]=u.y;a[u.id*3+2]=u.alive?u.hp:0;}trace.push(a);};\r\n const checksum=()=>{let h=0x811c9dc5;const mix=v=>{for(let k=0;k<4;k++){h=Math.imul(h^((v>>>(k*8))&255),16777619)>>>0;}};\r\n  for(const u of units){mix(u.id);mix(u.x);mix(u.y);mix(u.hp);mix(u.cd);mix(u.alive?1:0);}return h.toString(16).padStart(8,'0');};\r\n const d2=(a,b)=>{const dx=a.x-b.x,dy=a.y-b.y;return dx*dx+dy*dy;};\r\n snap();\r\n let end=null;\r\n for(let T=0;T<MAX_TICKS&&!end;T++){\r\n  const ot=T>=FIGHT_TICKS?WPN_STEP*(1+idiv(T-FIGHT_TICKS,CHECK_EVERY)):0;\r\n  const mx=new Array(n).fill(0),my=new Array(n).fill(0),dmg=new Array(n).fill(0),heal=new Array(n).fill(0),hitBy=new Array(n).fill(-1);\r\n  const pending=[]; // attacks this tick\r\n  const t1=T+1;\r\n  // v6 infrastructure resolves on the same snapshot as troops. Capture the garrison to disable it.\r\n  for(let side=0;side<2;side++)for(const p of systems[side]){\r\n   const e=p.e;if(!e.range||p.spent)continue;\r\n   const nearby=units.filter(u=>u.alive&&u.side===(p.kind==='beacon'?side:1-side)&&(!e.groundOnly||!u.def.air)&&d2(p,u)<=e.range*e.range&&(!e.minRange||d2(p,u)>=e.minRange*e.minRange));\r\n   if(!nearby.length||(e.period&&T%e.period!==0))continue;\r\n   if(p.kind==='beacon'){\r\n    for(const u of nearby){if(u.hp>=u.max)continue;heal[units.indexOf(u)]+=e.heal;events.push({t:t1,k:'heal',a:-1,b:u.id,d:e.heal,source:{id:p.id,kind:p.kind,x:p.x,y:p.y}});}\r\n    continue;\r\n   }\r\n   nearby.sort((a,b)=>d2(p,a)-d2(p,b)||a.id-b.id);\r\n   const target=nearby[0],targets=e.splashRadius?units.filter(u=>u.alive&&u.side!==side&&!u.def.air&&d2(target,u)<=e.splashRadius*e.splashRadius):[target];\r\n   for(const u of targets){const raw=idiv(e.damage*(100+sum(side,'defenseDamagePct')),100),d=p.kind==='mortar'?damage(raw,'burst',u.def.tag,Math.max(0,u.ar-modOf(u,'shred',T))):raw;dmg[units.indexOf(u)]+=d;events.push({t:t1,k:'zap',b:u.id,d,source:{id:p.id,kind:p.kind,x:p.x,y:p.y}});if(e.slowPct)applyMod(u,'slow',e.slowPct,T+e.slowTicks);}\r\n   if(e.charges)p.spent=true;\r\n  }\r\n  for(const ab of abil){ // v4 cannon: lands at the start of its tick, affects units alive on the snapshot\r\n   if(ab.tick!==T)continue;\r\n   const sd=ab.side??0,def=ABILITIES[ab.faction],tgtSide=def.who==='ally'?sd:1-sd;let hits=0;\r\n   for(let k=0;k<n;k++){const u=units[k];if(!u.alive||u.side!==tgtSide)continue;\r\n    const dx=u.x-ab.x,dy=u.y-ab.y,dd=dx*dx+dy*dy;if(dd>ABILITY_RADIUS*ABILITY_RADIUS)continue;hits++;\r\n    const until=T+def.dur;\r\n    if(def.dmg){dmg[k]+=def.dmg;events.push({t:t1,k:'zap',b:u.id,d:def.dmg});}\r\n    if(def.heal){heal[k]+=def.heal;events.push({t:t1,k:'heal',a:-1,b:u.id,d:def.heal});}\r\n    if(def.slow)applyMod(u,'slow',def.slow,until);\r\n    if(def.haste)applyMod(u,'haste',def.haste,until);\r\n    if(def.shred)applyMod(u,'shred',def.shred,until);\r\n    if(def.dot)applyMod(u,'dot',def.dot,until);\r\n    if(def.stun)applyMod(u,'stun',1,T+def.stun);\r\n    if(def.push&&u.def.spd){const dist=isqrt(dd),px=dist?idiv(dx*def.push,dist):def.push,py=dist?idiv(dy*def.push,dist):0;\r\n     u.x=Math.min(W,Math.max(0,u.x+px));u.y=Math.min(H,Math.max(0,u.y+py));}\r\n   }\r\n   events.push({t:t1,k:'ability',f:ab.faction,s:sd,x:ab.x,y:ab.y,n:hits});\r\n  }\r\n  if(T%5===4)for(let k=0;k<n;k++){const u=units[k],d=u.alive?modOf(u,'dot',T):0;if(d){dmg[k]+=d;events.push({t:t1,k:'zap',b:u.id,d});}}\r\n  for(let k=0;k<n;k++){\r\n   const u=units[k];if(!u.alive)continue;\r\n   if(u.cd>0){u.cd--;if(u.cd>0&&T%2===0&&modOf(u,'haste',T))u.cd--;}\r\n   if(modOf(u,'stun',T)){u.tgt=-1;continue;}\r\n   let target=null,follow=false;\r\n   if(!u.def.atk){ // hauler: trail the lowest-id armed ally\r\n    target=units.find(v=>v.alive&&v.side===u.side&&v.def.atk)||null;follow=true;\r\n   }else{\r\n    const cur=u.tgt>=0?byId[u.tgt]:null;\r\n    if(cur?.alive&&T%5!==0&&canHit(u.def,cur.def))target=cur;\r\n    else{\r\n     const foes=units.filter(v=>v.alive&&v.side!==u.side&&canHit(u.def,v.def));\r\n     const atkr=u.hitBy>=0&&T-u.hitT<=u.def.cd?byId[u.hitBy]:null;\r\n     if(atkr?.alive&&canHit(u.def,atkr.def)&&d2(u,atkr)<=u.def.rng*u.def.rng)target=atkr;\r\n     if(!target)target=foes.find(v=>d2(u,v)<=(u.def.rng+u.def.acq)**2)||null;\r\n     if(!target){let best=-1;for(const v of foes){const d=d2(u,v);if(best<0||d<best){best=d;target=v;}}}\r\n    }\r\n    u.tgt=target?target.id:-1;\r\n   }\r\n   if(!target)continue;\r\n   const dist2=d2(u,target),want=follow?768:u.def.rng,dist=isqrt(dist2);\r\n   if(dist<=want){\r\n    if(!follow&&u.cd===0){u.cd=u.def.cd;pending.push([u,target]);}\r\n   }else{\r\n    const pct=Math.max(10,100-modOf(u,'slow',T)+modOf(u,'haste',T)),spd=pct===100?u.def.spd:idiv(u.def.spd*pct,100),step=Math.min(spd,dist-want+16);\r\n    mx[k]=idiv((target.x-u.x)*step,dist);my[k]=idiv((target.y-u.y)*step,dist);\r\n   }\r\n  }\r\n  for(let k=0;k<n;k++){const u=units[k];u.x+=mx[k];u.y+=my[k];}\r\n  for(let a=0;a<n;a++){ // separation, additive so pair order is irrelevant\r\n   mx[a]=0;my[a]=0;\r\n  }\r\n  for(let a=0;a<n;a++){const p=units[a];if(!p.alive)continue;\r\n   for(let b=a+1;b<n;b++){const q=units[b];if(!q.alive||p.def.air||q.def.air)continue;\r\n    const dx=q.x-p.x,dy=q.y-p.y,dd=dx*dx+dy*dy;if(dd>=RADIUS*RADIUS)continue;\r\n    const dist=isqrt(dd),push=Math.min(SEP_MAX,idiv(RADIUS-dist+3,4));\r\n    if(dist===0){my[a]-=push;my[b]+=push;}\r\n    else{const px=idiv(dx*push,dist),py=idiv(dy*push,dist);mx[a]-=px;my[a]-=py;mx[b]+=px;my[b]+=py;}\r\n   }}\r\n  for(let k=0;k<n;k++){const u=units[k];if(!u.alive||!u.def.spd)continue;u.x=Math.min(W,Math.max(0,u.x+mx[k]));u.y=Math.min(H,Math.max(0,u.y+my[k]));}\r\n  const t=T+1;\r\n  for(const [a,b] of pending){\r\n   const d=damage(a.atk+ot,a.def.wpn,b.def.tag,Math.max(0,b.ar-modOf(b,'shred',T)));\r\n   dmg[units.indexOf(b)]+=d;const bi=units.indexOf(b);if(hitBy[bi]<0)hitBy[bi]=a.id;\r\n   events.push({t,k:'hit',a:a.id,b:b.id,d});\r\n  }\r\n  if(T%10===9)for(const h of units){if(!h.alive||h.def.name!=='hauler')continue;\r\n   for(let k=0;k<n;k++){const v=units[k];if(v!==h&&v.alive&&v.hp<v.max&&v.side===h.side&&d2(h,v)<=512*512){heal[k]+=10;events.push({t,k:'heal',a:h.id,b:v.id,d:10});}}}\r\n  for(let k=0;k<n;k++){const u=units[k];if(!u.alive)continue;\r\n   if(dmg[k]){u.hp-=dmg[k];u.hitBy=hitBy[k];u.hitT=T;}\r\n   if(u.hp<=0){u.hp=0;u.alive=false;events.push({t,k:'death',u:u.id});}\r\n   else if(heal[k])u.hp=Math.min(u.max,u.hp+heal[k]);\r\n  }\r\n  snap();\r\n  if(t%CHECK_EVERY===0)checksums.push(checksum());\r\n  const alive=[0,0];for(const u of units)if(u.alive)alive[u.side]++;\r\n  if(!alive[0]||!alive[1]||t===MAX_TICKS){\r\n   const frac=s=>{let hp=0,mxp=0;for(const u of units)if(u.side===s){hp+=u.hp*u.def.sup;mxp+=u.max*u.def.sup;}return idiv(hp*1000,mxp);};\r\n   const fa=frac(0),fb=frac(1);\r\n   const winner=!alive[0]&&!alive[1]?'draw':!alive[1]?'a':!alive[0]?'b':fa>fb?'a':fb>fa?'b':'draw';\r\n   end={winner,ticks:t,a:fa,b:fb};\r\n  }\r\n }\r\n return {events,checksums,result:end,trace};\r\n}\r\n\r\n// Input-only log: events are regenerated by re-simulating.\r\nexport function makeLog(input,out){\r\n return {v:1,rulesVersion:input.rulesVersion,seed:input.seed,rosters:input.rosters,pads:input.pads,...(input.systems?{systems:input.systems}:{}),...(input.abilities?{abilities:input.abilities}:{}),checksums:out.checksums,result:out.result};\r\n}\r\nexport const logBytes=log=>new TextEncoder().encode(JSON.stringify(log)).length;\r\n// Re-sim a log. ok=false (checksum or result mismatch, or bad input) means show the banner only: \"replay unavailable\".\r\nexport function replay(log){\r\n try{\r\n  const out=simulate(log);\r\n  const ok=JSON.stringify(out.checksums)===JSON.stringify(log.checksums)&&JSON.stringify(out.result)===JSON.stringify(log.result);\r\n  return ok?{ok:true,sim:out}:{ok:false,sim:null,message:'replay unavailable'};\r\n }catch{return {ok:false,sim:null,message:'replay unavailable'};}\r\n}\r\n// FNV-1a over a string (golden fixtures compare events by digest).\r\nexport function digest(str){let h=0x811c9dc5;for(let i=0;i<str.length;i++)h=Math.imul(h^str.charCodeAt(i),16777619)>>>0;return h.toString(16).padStart(8,'0');}\r\n","modules/coach-world-rts/battle-view.mjs":"// W4 battle playback. The sim (battle-sim.mjs) is authoritative; this only schedules visuals on the 42 ms animation clock.\r\n// Sim tick k happens at battle time k*100 ms. Playback time = battle time / speed (1x or 4x). Client-only: no fetch, no storage, no reward.\r\nimport {simulate,replay,TICK_MS,MAX_UNITS,W,H,ROLES,RULES_VERSIONS} from './battle-sim.mjs';\r\nimport {createClock} from './runtime.mjs';\r\nimport {TICK} from './clips.mjs';\r\nimport {createElementFx} from './element-fx.mjs';\r\nimport {FACTIONS} from '../coach-world/catalog.mjs';\r\n\r\nexport const SPEEDS=[1,4];\r\nconst ORDER={attack:0,hit:1,heal:2,zap:2,ability:2,death:3};\r\n// Windup of an atlas attack clip: ms from clip start to the strike frame (0 when the atlas has no attack clip yet, i.e. the stub).\r\nexport function windupMs(clip){if(!clip?.ticks?.length)return 0;const s=clip.strike??0;return clip.ticks.slice(0,s).reduce((a,b)=>a+b,0)*TICK;}\r\n\r\n// V1-C4: 24x4 HP bar under each unit: dark track in a 1px black border, green fill, red below 33%. Plain fillRect, no allocation.\r\nfunction hpBar(ctx,x,y,frac){const w=24,f=Math.max(0,Math.min(1,frac));ctx.fillStyle='#000';ctx.fillRect(x-w/2-1,y-1,w+2,6);ctx.fillStyle='#0b0d10';ctx.fillRect(x-w/2,y,w,4);ctx.fillStyle=f<.33?'#e0382a':'#2fdc4a';ctx.fillRect(x-w/2,y,Math.round(w*f),4);}\r\n\r\n// Events -> timed visual actions, sorted by time. `attack` starts the windup so the hit frame lands at the sim hit time.\r\n// windup: {roleChar: ms}. late=true when the sim hit is sooner than the windup allows (clip starts at 0 and the hit is still on time).\r\nexport function schedule(events,roleOf,{speed=1,windup={}}={}){\r\n if(!SPEEDS.includes(speed))throw new Error('speed must be 1 or 4');\r\n const out=[];\r\n for(const e of events){\r\n  const at=e.t*TICK_MS/speed;\r\n  if(e.k==='hit'){\r\n   const w=(windup[roleOf(e.a)]??0)/speed;\r\n   out.push({at:Math.max(0,at-w),hitAt:at,kind:'attack',u:e.a,late:at-w<0});\r\n   out.push({at,hitAt:at,kind:'hit',u:e.a,b:e.b,d:e.d});\r\n  }else out.push({at,hitAt:at,kind:e.k,u:e.u??e.a,b:e.b,d:e.d,...(e.source?{source:e.source}:{}),...(e.k==='ability'?{ev:e}:{})});\r\n }\r\n return out.map((x,i)=>[x,i]).sort((p,q)=>p[0].at-q[0].at||ORDER[p[0].kind]-ORDER[q[0].kind]||p[1]-q[1]).map(p=>p[0]);\r\n}\r\n\r\n// Practice rosters carry no faction, so each unit takes one by id (the enemy side is offset) and every faction's element shows up in a battle.\r\nexport const factionOfUnit=id=>FACTIONS[((id%MAX_UNITS)+(id>=MAX_UNITS?3:0))%FACTIONS.length].id;\r\n\r\n// rts: runtime object (onEvent/fx stubs). Missing methods are tolerated so the view works before art lands.\r\nexport function createPlayback(sim,{roster,rts=null,speed=1,windup={},viewer='a',onEnd,onHit,onAbility,onAction,fx=null,factionOf=factionOfUnit,hidden=()=>typeof document!=='undefined'&&document.hidden}={}){\r\n const roleOf=id=>roster[id]??'g';\r\n let list=schedule(sim.events,roleOf,{speed,windup}),cursor=0,over=false;\r\n const clock=createClock(),hp=new Map(),dead=new Set(),fired=[];\r\n const unitId=id=>'battle-'+id;\r\n const maxHp=id=>sim.trace[0]?.[id*3+2]||ROLES[roleOf(id)].hp;\r\n const posAt=(id,t)=>{const a=sim.trace[Math.min(sim.trace.length-1,t)];return [a[id*3],a[id*3+1]];};\r\n const pb={\r\n  clock,speed,fired,hp,dead,fx,factionOf,result:sim.result,banner:null,\r\n  get playMs(){return clock.ticks*TICK;},\r\n  setSpeed(s){if(!SPEEDS.includes(s))throw new Error('speed must be 1 or 4');const was=(pb.playMs+pb.offset)*speed;speed=s;pb.speed=s;list=schedule(sim.events,roleOf,{speed,windup});cursor=0;\r\n   // rebase the clock to the same battle time and re-fire nothing already shown\r\n   const now=was/speed;while(cursor<list.length&&list[cursor].at<now)cursor++;pb.offset=now-pb.playMs;},\r\n  offset:0,\r\n  // v4 cannon: swap in a re-simulated run whose input only adds abilities at ticks after now. The prefix is identical, so already-shown events stay shown.\r\n  swap(ns){sim=ns;pb.result=ns.result;list=schedule(sim.events,roleOf,{speed,windup});const t=pb.playMs+pb.offset;cursor=0;while(cursor<list.length&&list[cursor].at<=t)cursor++;},\r\n  get tick(){return Math.floor((pb.playMs+pb.offset)*speed/TICK_MS);},\r\n  // advance on a rAF time; fires everything due. Hidden tab pauses (clock drops the backlog), at most MAX_TICKS clock ticks per frame.\r\n  step(now){\r\n   if(over)return 0;\r\n   const n=clock.advance(now,hidden());const t=pb.playMs+pb.offset;\r\n   while(cursor<list.length&&list[cursor].at<=t){const a=list[cursor++];fire(a,t);}\r\n   if(cursor>=list.length&&t>=sim.result.ticks*TICK_MS/speed){over=true;pb.banner=bannerFor(sim.result,viewer);onEnd?.(sim.result,pb.banner);}\r\n   return n;\r\n  },\r\n  get over(){return over;},\r\n  // interpolated unit positions at the current battle time (render only)\r\n  units(){\r\n   const bt=(pb.playMs+pb.offset+clock.alpha*TICK)*speed/TICK_MS,k=Math.floor(bt),f=bt-k,out=[];\r\n   for(const id of Object.keys(roster).map(Number)){\r\n    const [x0,y0]=posAt(id,k),[x1,y1]=posAt(id,k+1);\r\n    const hpTick=Math.min(sim.trace.length-1,Math.floor((pb.playMs+pb.offset)*speed/TICK_MS)),h=sim.trace[hpTick][id*3+2];\r\n    out.push({id,role:roleOf(id),side:id<MAX_UNITS?0:1,x:(x0+(x1-x0)*f)/256,y:(y0+(y1-y0)*f)/256,hp:h,max:maxHp(id),dead:dead.has(id)});\r\n   }\r\n   return out;\r\n  },\r\n  draw(ctx,w=ctx.canvas?.width||320,h=ctx.canvas?.height||192){\r\n   const s=Math.min(w/(W/256),h/(H/256)),ox=(w-W/256*s)/2,oy=(h-H/256*s)/2;\r\n   ctx.fillStyle='#10131a';ctx.fillRect(0,0,w,h);\r\n   for(const u of pb.units()){\r\n    const x=Math.round(ox+u.x*s),y=Math.round(oy+u.y*s),r=Math.max(3,Math.round(s*.35));\r\n    if(u.dead){ctx.fillStyle='#555';ctx.fillRect(x-r,y,r*2,2);continue;} // tap-out: lies down, no blood\r\n    const air=ROLES[u.role]?.air,ay=air?y-r*3:y; // v5 ship: flies r*3 above its ground shadow\r\n    if(air){ctx.fillStyle='#0008';ctx.fillRect(x-r,y-1,r*2,2);}\r\n    ctx.fillStyle=u.side?'#e0603a':'#3aa0e0';if(air){ctx.fillRect(x-r*1.5,ay-r,r*3,r);ctx.fillRect(x-r*.5,ay-r*2,r,r*2);}else ctx.fillRect(x-r,y-r*2,r*2,r*2);\r\n    ctx.fillStyle=FACTIONS.find(f=>f.id===factionOf(u.id)).color;ctx.fillRect(x-r,ay-r*2,r*2,Math.max(2,r*.7)); // faction cap\r\n    hpBar(ctx,x,ay-r*2-7,u.hp/u.max);\r\n   }\r\n   fx?.draw(ctx,{ox,oy,scale:s});\r\n   if(pb.banner){ctx.fillStyle='#000a';ctx.fillRect(0,h/2-16,w,32);ctx.fillStyle='#fff';ctx.font='16px monospace';ctx.textAlign='center';ctx.fillText(pb.banner,w/2,h/2+6);}\r\n  },\r\n };\r\n function fire(a,t){\r\n  const rec={kind:a.kind,u:a.u,b:a.b,at:a.at,hitAt:a.hitAt,firedAt:Math.ceil(t/TICK)*TICK,...(a.source?{source:a.source}:{})};fired.push(rec);\r\n  onAction?.(rec);\r\n  if(a.kind==='attack')rts?.onEvent?.({type:'attack',unitId:unitId(a.u),on:true});\r\n  else if(a.kind==='hit'){\r\n   rts?.onEvent?.({type:'hit',unitId:unitId(a.b),on:true});\r\n   const cur=hp.has(a.b)?hp.get(a.b):maxHp(a.b);hp.set(a.b,Math.max(0,cur-a.d));\r\n   const [x,y]=posAt(a.b,Math.round(a.hitAt*speed/TICK_MS));rts?.fx?.spark?.(x/256,y/256);onHit?.(a);\r\n   const [fx0,fy0]=posAt(a.u,Math.round(a.hitAt*speed/TICK_MS));fx?.fire({x:fx0/256,y:fy0/256-.35},{x:x/256,y:y/256-.35},factionOf(a.u));\r\n  }else if(a.kind==='heal'){const cur=hp.has(a.b)?hp.get(a.b):maxHp(a.b);hp.set(a.b,Math.min(maxHp(a.b),cur+a.d));}\r\n  else if(a.kind==='zap'){const cur=hp.has(a.b)?hp.get(a.b):maxHp(a.b);hp.set(a.b,Math.max(0,cur-a.d));}\r\n  else if(a.kind==='ability')onAbility?.(a.ev);\r\n  else if(a.kind==='death'){dead.add(a.u);hp.set(a.u,0);rts?.onEvent?.({type:'ko',unitId:unitId(a.u),on:true});}\r\n }\r\n return pb;\r\n}\r\n\r\nexport function bannerFor(result,viewer='a'){return result.winner==='draw'?'DRAW':result.winner===viewer?'VICTORY':'DEFEAT';}\r\nexport const rosterMap=rosters=>{const m={};for(let s=0;s<2;s++)[...rosters[s].u].forEach((c,i)=>{m[s*MAX_UNITS+i]=c;});return m;};\r\n\r\n// Play a recorded input-only log. Checksum mismatch -> result banner only.\r\nexport function playLog(log,opts={}){\r\n const r=replay(log);\r\n if(!r.ok)return {playback:null,banner:bannerFor(log.result,opts.viewer)+' (replay unavailable)',message:r.message};\r\n return {playback:createPlayback(r.sim,{...opts,roster:rosterMap(log.rosters)}),banner:null};\r\n}\r\n\r\n// Scripted NPC armies for practice. Deterministic per tier; client-only.\r\nexport const NPC_ARMIES={easy:{w:0,a:0,u:'gggs'},normal:{w:0,a:0,u:'gggsbh'},hard:{w:1,a:1,u:'ggssbbhh'}};\r\n// Practice battle vs a scripted NPC: simulates locally, never calls the server, grants nothing.\r\nexport function startPractice({roster={w:0,a:0,u:'gggsbh'},tier='normal',seed=1,pads=[0,1,2],rts=null,speed=1,windup,onEnd,fx=null}={}){\r\n const npc=NPC_ARMIES[tier];if(!npc)throw new Error('unknown tier');\r\n const input={rulesVersion:RULES_VERSIONS.at(-1),seed,rosters:[roster,npc],pads:[pads,[0,1,2]],systems:[[],[]]},sim=simulate(input);\r\n return {input,sim,reward:null,playback:createPlayback(sim,{roster:rosterMap(input.rosters),rts,speed,windup,onEnd,fx})};\r\n}\r\n\r\n// rAF driver for a canvas. Returns dispose().\r\nexport function runPractice(canvas,opts={}){\r\n const p=startPractice({fx:typeof Image!=='undefined'?createElementFx():null,...opts}),ctx=canvas.getContext('2d');let id=0,stop=false;\r\n const frame=t=>{if(stop)return;p.playback.step(t);p.playback.draw(ctx,canvas.width,canvas.height);id=requestAnimationFrame(frame);};\r\n id=requestAnimationFrame(frame);\r\n return {practice:p,dispose(){stop=true;cancelAnimationFrame(id);}};\r\n}\r\n","modules/coach-world-rts/biomes.mjs":"import { FACTIONS } from '../coach-world/catalog.mjs';\r\n\r\nexport const BIOMES = Object.freeze({\r\n  \"chest\": {\r\n    surface: { angle: .65, beds: 3, warp: 2.2, mineral: \"#a97954\" },\r\n    atmosphere: { horizon: \"#81504b\", haze: \"#3e3046\", zenith: \"#101424\", dust: \"#c67b63\" },\r\n    relief: 0.7,\r\n    roughness: 0.55, amp: 0.9,\r\n    plants: [\"shard\", \"ember\", \"reed\"],\r\n    rocks: [\"plate\", \"slab\"],\r\n    landmark: { shape: \"slab\", scale: [0.7, 1.0] },\r\n    ridge: \"slab\",\r\n    ground: \"#9a5a38\",\r\n    plant: { shape: \"shard\", count: 60, scale: [0.3, 0.6], color: \"#ff8a4d\" },\r\n    rock: { shape: \"plate\", count: 15, color: \"#6b3a2a\" }\r\n  },\r\n  \"quads\": {\r\n    surface: { angle: .12, beds: 4, warp: 1.1, mineral: \"#a29460\" },\r\n    atmosphere: { horizon: \"#79704f\", haze: \"#3b4144\", zenith: \"#111927\", dust: \"#b9a273\" },\r\n    relief: 0.5,\r\n    roughness: 0.45, amp: 0.5,\r\n    plants: [\"stalk\", \"reed\", \"ribbon\"],\r\n    rocks: [\"pearl\", \"drum\"],\r\n    landmark: { shape: \"drum\", scale: [0.7, 1.0] },\r\n    ridge: \"drum\",\r\n    ground: \"#8a8440\",\r\n    plant: { shape: \"stalk\", count: 70, scale: [0.3, 0.6], color: \"#ffd24a\" },\r\n    rock: { shape: \"pearl\", count: 12, color: \"#6a6a52\" }\r\n  },\r\n  \"glutes\": {\r\n    surface: { angle: -.5, beds: 2, warp: 3.3, mineral: \"#a07c9a\" },\r\n    atmosphere: { horizon: \"#795477\", haze: \"#3a304f\", zenith: \"#17132b\", dust: \"#bc86b8\" },\r\n    relief: 0.8,\r\n    roughness: 0.6, amp: 1.1,\r\n    plants: [\"bubble\", \"pulse\", \"stalk\"],\r\n    rocks: [\"dune\", \"pearl\"],\r\n    landmark: { shape: \"dune\", scale: [0.7, 1.0] },\r\n    ridge: \"mound\",\r\n    ground: \"#8a4a7a\",\r\n    plant: { shape: \"bubble\", count: 55, scale: [0.3, 0.6], color: \"#ff7fd0\" },\r\n    rock: { shape: \"dune\", count: 18, color: \"#6a3a66\" }\r\n  },\r\n  \"arms-shoulders\": {\r\n    surface: { angle: 1.4, beds: 4, warp: .8, mineral: \"#8299b7\" },\r\n    atmosphere: { horizon: \"#526c8b\", haze: \"#2b3c59\", zenith: \"#0d172a\", dust: \"#83a5cc\" },\r\n    relief: 0.6,\r\n    roughness: 0.5, amp: 0.8,\r\n    plants: [\"reed\", \"stalk\", \"shard\"],\r\n    rocks: [\"drum\", \"plate\"],\r\n    landmark: { shape: \"slab\", scale: [0.7, 1.0] },\r\n    ridge: \"plate\",\r\n    ground: \"#4a6a9a\",\r\n    plant: { shape: \"reed\", count: 65, scale: [0.3, 0.6], color: \"#6fb2ff\" },\r\n    rock: { shape: \"drum\", count: 10, color: \"#3a4a6a\" }\r\n  },\r\n  \"yoga\": {\r\n    surface: { angle: -.25, beds: 2, warp: 4, mineral: \"#83aa99\" },\r\n    atmosphere: { horizon: \"#507d79\", haze: \"#2d4653\", zenith: \"#101c2e\", dust: \"#86c4b1\" },\r\n    relief: 0.4,\r\n    roughness: 0.4, amp: 0.6,\r\n    plants: [\"ribbon\", \"bubble\", \"reed\"],\r\n    rocks: [\"scorch\", \"dune\"],\r\n    landmark: { shape: \"ring\", scale: [0.7, 1.0] },\r\n    ridge: \"dune\",\r\n    ground: \"#3a8a78\",\r\n    plant: { shape: \"ribbon\", count: 80, scale: [0.3, 0.6], color: \"#7dffcf\" },\r\n    rock: { shape: \"scorch\", count: 14, color: \"#4a6a62\" }\r\n  },\r\n  \"martial-arts\": {\r\n    surface: { angle: .95, beds: 5, warp: 1.7, mineral: \"#a47570\" },\r\n    atmosphere: { horizon: \"#80504e\", haze: \"#452c43\", zenith: \"#1c1327\", dust: \"#cd8170\" },\r\n    relief: 0.9,\r\n    roughness: 0.65, amp: 1.2,\r\n    plants: [\"ember\", \"shard\", \"pulse\"],\r\n    rocks: [\"slab\", \"scorch\"],\r\n    landmark: { shape: \"slab\", scale: [0.7, 1.0] },\r\n    ridge: \"ring\",\r\n    ground: \"#7a3a3a\",\r\n    plant: { shape: \"ember\", count: 50, scale: [0.3, 0.6], color: \"#ff4a4a\" },\r\n    rock: { shape: \"slab\", count: 20, color: \"#3a2a30\" }\r\n  },\r\n  \"cardio\": {\r\n    surface: { angle: -.8, beds: 3, warp: 2.7, mineral: \"#7faab5\" },\r\n    atmosphere: { horizon: \"#4e7d8a\", haze: \"#2b425e\", zenith: \"#10192e\", dust: \"#81c2d0\" },\r\n    relief: 0.3,\r\n    roughness: 0.35, amp: 0.3,\r\n    plants: [\"pulse\", \"ribbon\", \"bubble\"],\r\n    rocks: [\"ring\", \"pearl\"],\r\n    landmark: { shape: \"ring\", scale: [0.7, 1.0] },\r\n    ridge: \"scorch\",\r\n    ground: \"#3a7f8a\",\r\n    plant: { shape: \"pulse\", count: 75, scale: [0.3, 0.6], color: \"#5ff0ff\" },\r\n    rock: { shape: \"ring\", count: 11, color: \"#4a6a78\" }\r\n  }\r\n});\r\n\r\n// R81 lane E: seeded value-noise fBm. noise2(seed)(x,z) is in [0,1); fbm sums 3 octaves (0.12/0.25/0.5 per tile) with per-biome gain (roughness).\r\nconst lat = (ix, iz, seed) => {\r\n  let n = (Math.imul(ix, 374761393) + Math.imul(iz, 668265263) + Math.imul(seed | 0, 1274126177)) | 0;\r\n  n = Math.imul(n ^ (n >>> 13), 1274126177);\r\n  return (((n ^ (n >>> 16)) >>> 0) / 4294967296);\r\n};\r\nconst fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);\r\nexport const noise2 = (seed) => (x, z) => {\r\n  const x0 = Math.floor(x), z0 = Math.floor(z), u = fade(x - x0), v = fade(z - z0);\r\n  const a = lat(x0, z0, seed), b = lat(x0 + 1, z0, seed), c = lat(x0, z0 + 1, seed), d = lat(x0 + 1, z0 + 1, seed);\r\n  return (a + (b - a) * u) * (1 - v) + (c + (d - c) * u) * v;\r\n};\r\nexport const FBM_FREQ = [0.12, 0.25, 0.5];\r\nexport const fbm = (n, x, z, gain = 0.5) => {\r\n  let sum = 0, norm = 0, g = 1;\r\n  for (const f of FBM_FREQ) { sum += g * n(x * f, z * f); norm += g; g *= gain; }\r\n  return Math.min(0.999999, sum / norm);\r\n};\r\nconst sstep = (a, b, x) => { const t = Math.max(0, Math.min(1, (x - a) / (b - a))); return t * t * (3 - 2 * t); };\r\n// Prop density: period ~4 tiles, none under .3, smoothstep(.35,.7) above; plus one forced clearing per 12x12 quadrant so a 24x24 base always has clearings.\r\nexport const densityField = (seed) => {\r\n  const n = noise2(seed + 7919), cl = [0, 1, 2, 3].map((i) => [(i % 2) * 12 + 3 + lat(i, 1, seed) * 6, ((i >> 1) * 12) + 3 + lat(i, 2, seed) * 6]);\r\n  return (tx, tz) => {\r\n    const d = n(tx / 4, tz / 4), dist = Math.min(...cl.map(([cx, cz]) => Math.hypot(tx - cx, tz - cz))), w = d < 0.3 ? 0 : sstep(0.35, 0.7, d);\r\n    return w * sstep(0.8, 2.4, dist);\r\n  };\r\n};\r\nexport const SUBS = Object.freeze([\"lush\", \"barren\", \"rocky\"]);\r\n// V1-C7: ground overlay colours. Plates sit in the base zone, creep on the outer ring where raids arrive.\r\nexport const GROUND_DETAIL = Object.freeze({ plate: \"#3b4550\", creep: \"#7a3a4a\", creepRim: \"#a8586a\" });\r\n\r\nexport const NODE_SHAPES = Object.freeze({\r\n  \"chest\":   { shape: \"shard\",   color: \"#c9a77f\", glow: \"#e6b86f\" },\r\n  \"quads\":   { shape: \"stalk\",   color: \"#a1c9a7\", glow: \"#b8d9be\" },\r\n  \"glutes\":  { shape: \"bubble\",  color: \"#a7c9c9\", glow: \"#c6e0df\" },\r\n  \"arms-shoulders\": { shape: \"reed\",   color: \"#c9a7a7\", glow: \"#d8bfbf\" },\r\n  \"yoga\":    { shape: \"ribbon\",  color: \"#a7c9a7\", glow: \"#bfe0be\" },\r\n  \"martial-arts\": { shape: \"ember\",  color: \"#c9a7c9\", glow: \"#e6b8e6\" },\r\n  \"cardio\":  { shape: \"pulse\",   color: \"#a7c9c9\", glow: \"#bfe0df\" }\r\n});\r\n\r\nexport function shapeGeometry(T, shape, scale = 1) {\r\n  const r = 0.5 * scale;\r\n  switch (shape) {\r\n    case \"shard\":\r\n      return new T.ConeGeometry(r, 1.2 * scale, 4, 1);\r\n    case \"stalk\":\r\n      return new T.CylinderGeometry(0.12 * scale, 0.12 * scale, 1.4 * scale, 5, 1);\r\n    case \"bubble\":\r\n      return new T.IcosahedronGeometry(r, 0);\r\n    case \"reed\":\r\n      return new T.CylinderGeometry(0.05 * scale, 0.05 * scale, 1.8 * scale, 4, 1);\r\n    case \"ribbon\":\r\n      return new T.TorusGeometry(0.4 * scale, 0.1 * scale, 3, 6);\r\n    case \"ember\":\r\n      return new T.ConeGeometry(0.4 * scale, 0.6 * scale, 5, 1);\r\n    case \"pulse\":\r\n      return new T.TorusGeometry(0.45 * scale, 0.05 * scale, 3, 8);\r\n    case \"plate\":\r\n      return new T.BoxGeometry(1.2 * scale, 0.35 * scale, 0.9 * scale);\r\n    case \"slab\":\r\n      return new T.BoxGeometry(0.5 * scale, 1.1 * scale, 0.8 * scale);\r\n    case \"drum\":\r\n      return new T.CylinderGeometry(0.4 * scale, 0.45 * scale, 0.7 * scale, 6, 1);\r\n    case \"dune\":\r\n      return new T.IcosahedronGeometry(r, 0).scale(1.6, 0.5, 1.2);\r\n    case \"scorch\":\r\n      return new T.ConeGeometry(0.5 * scale, 0.5 * scale, 7, 1);\r\n    case \"ring\":\r\n      return new T.TorusGeometry(0.4 * scale, 0.18 * scale, 4, 7).rotateX(Math.PI / 2);\r\n    default:\r\n      return new T.IcosahedronGeometry(r, 0);\r\n  }\r\n}\r\n\r\nexport function biomeOf(cell, base) {\r\n  const b = BIOMES[cell.faction];\r\n  if (b !== undefined) return b;\r\n  const bb = BIOMES[base.faction];\r\n  return bb !== undefined ? bb : undefined;\r\n}\r\n","modules/coach-world-rts/building-art.mjs":"// R81 distinct buildings: one part builder per building KIND (hq, hall, care, observatory, habitat, hangar, production, expedition).\r\n// Each kind has ONE fixed \"tell\" (marked tell:true, always present whatever the policy/seed/level) plus 2-3 knobs drawn from a per-building seed.\r\n// The faction policy only changes edge treatment: occupied-worker = boxes, seeded-warp = round drums, consumed-seed = domed hemispheres.\r\n// All five levels change massing and add functional machinery; faction crests retain the original MYR5 motifs.\r\n// Defense, silo, yard and landing stay in tower-art.mjs. Every kind stays under ~300 triangles at level 5 (MAX_TRIS in base-meshes.mjs).\r\nimport {ART_DIRECTION} from '../coach-world/pixel-art.mjs';\r\n\r\nexport const BUILDING_ART_KINDS=Object.freeze(['hq','hall','care','observatory','habitat','hangar','production','expedition']);\r\nexport const styleOf=policy=>policy==='seeded-warp'?'round':policy==='consumed-seed'?'dome':'box';\r\n\r\n// Deterministic knob source: rnd(seed,i) in [0,1), pick(seed,i,n) in 0..n-1.\r\nexport const rnd=(seed,i)=>{let h=Math.imul((seed>>>0)^Math.imul(i+1,0x9e3779b1),0x85ebca6b);h^=h>>>13;h=Math.imul(h,0xc2b2ae35);h^=h>>>16;return (h>>>0)/4294967296;};\r\nexport const pick=(seed,i,n)=>Math.min(n-1,Math.floor(rnd(seed,i)*n));\r\nexport const hashSeed=str=>{let h=2166136261;for(let i=0;i<str.length;i++){h^=str.charCodeAt(i);h=Math.imul(h,16777619);}return h>>>0;};\r\n\r\nconst xf=(g,{sx=1,sy=1,sz=1,rx=0,ry=0,rz=0,x=0,y=0,z=0}={})=>{if(sx!==1||sy!==1||sz!==1)g.scale(sx,sy,sz);if(rx)g.rotateX(rx);if(ry)g.rotateY(ry);if(rz)g.rotateZ(rz);g.translate(x,y,z);return g;};\r\n\r\n// A mass of the kind's edge style sitting on y0 with the given footprint. seg = radial segments for rounded drums.\r\nfunction block(T,style,x,y0,z,w,h,d,seg=8){\r\n if(style==='round')return {g:xf(new T.CylinderGeometry(.5,.5,1,seg,1),{sx:w,sy:h,sz:d,x,y:y0+h/2,z})};\r\n if(style==='dome')return {g:xf(new T.SphereGeometry(.5,6,2,0,Math.PI*2,0,Math.PI/2),{sx:w,sy:h*2,sz:d,x,y:y0,z})};\r\n return {g:xf(new T.BoxGeometry(w,h,d),{x,y:y0+h/2,z})};\r\n}\r\n// Triangular roof prism along x with the apex up, base at y0.\r\nconst prism=(T,len,wid,hgt,x,y0,z)=>{const g=new T.CylinderGeometry(.5,.5,1,3,1);g.rotateY(Math.PI/2);g.rotateZ(Math.PI/2);g.scale(len,hgt/.75,wid/.866);g.translate(x,y0+hgt/3,z);return {g};};\r\nconst box=(T,w,h,d,o)=>({g:xf(new T.BoxGeometry(w,h,d),o)});\r\nconst hemi=(T,r,h,ws,hs,o)=>({g:xf(new T.SphereGeometry(r,ws,hs,0,Math.PI*2,0,Math.PI/2),{sy:h/r,...o})});\r\nconst accent=p=>(p.accent=1,p),tell=p=>(p.tell=true,p);\r\n\r\n// Upgrade machinery is staged by function, not a shared antenna/cone/gem stack.\r\n// Coordinates are fractions of the allocated footprint; modules cost 12-24 tris.\r\nexport const TIER_MODULES=Object.freeze({\r\n hq:['dispatch wing','command bridge','relay bank','shield vault'],\r\n hall:['entry buttress','training gallery','ceremony balcony','faction archive'],\r\n care:['coolant tank','pantry annex','heat exchanger','exhaust scrubber'],\r\n observatory:['instrument deck','counterweight','receiver bank','tracking console'],\r\n habitat:['airlock','crew pod','life support','upper sleeping pod'],\r\n hangar:['fuel locker','service dock','crane housing','flight control'],\r\n production:['feed hopper','assembly wing','press bank','dispatch bay'],\r\n expedition:['power cradle','loading rail','launch controller','payload magazine']\r\n});\r\nfunction tierMachines(T,P,c,kind){\r\n const {w,d,H,level:L,faction}=c;\r\n const places={\r\n  hq:[[.33,.22,.28,.27,.27],[-.28,-.23,.34,.28,.3],[.28,-.3,.26,.45,.22],[0,.25,.38,.5,.28]],\r\n  hall:[[-.32,.22,.22,.28,.28],[.31,-.2,.25,.4,.28],[0,.31,.42,.32,.2],[-.26,-.28,.26,.62,.26]],\r\n  care:[[-.28,.23,.25,.4,.25],[.26,.24,.3,.32,.25],[0,-.31,.32,.65,.2],[.27,-.24,.22,.85,.22]],\r\n  observatory:[[0,0,.56,.3,.56],[-.31,-.2,.22,.48,.25],[.28,.23,.26,.5,.26],[-.25,.25,.26,.7,.23]],\r\n  habitat:[[-.33,.28,.25,.32,.24],[.3,.25,.28,.52,.28],[0,-.28,.26,.62,.23],[0,.12,.3,.86,.28]],\r\n  hangar:[[-.31,-.26,.24,.5,.25],[.29,.2,.24,.28,.27],[-.3,.23,.24,.78,.22],[.3,-.25,.26,.9,.25]],\r\n  production:[[-.29,.26,.28,.52,.23],[0,-.3,.65,.4,.23],[.28,.23,.24,.73,.27],[-.28,-.25,.26,.88,.26]],\r\n  expedition:[[-.29,.24,.28,.28,.28],[.1,-.27,.55,.32,.22],[.29,.23,.25,.62,.25],[-.28,-.25,.28,.7,.26]]\r\n };\r\n for(let i=0;i<L-1;i++){\r\n  const [x,z,bw,bh,bd]=places[kind][i];\r\n  let part=box(T,w*bw,H*bh,d*bd,{x:x*w,y:H*bh/2,z:z*d});\r\n  if(kind==='care'&&(i===0||i===3))part=block(T,'round',x*w,0,z*d,w*bw,H*bh,d*bd,6);\r\n  if(kind==='habitat'&&(i===1||i===3))part=block(T,'dome',x*w,0,z*d,w*bw,H*bh,d*bd,6);\r\n  if(kind==='observatory'&&i>=2)part=hemi(T,w*bw*.5,H*bh*.45,6,2,{x:x*w,y:H*bh*.55,z:z*d,rx:-.45});\r\n  if(kind==='production'&&i===2)part={g:xf(new T.CylinderGeometry(w*bw*.5,w*bw*.5,d*bd,6,1),{rx:Math.PI/2,x:x*w,y:H*bh,z:z*d})};\r\n  if(kind==='expedition'&&i===1)part=box(T,w*bw,H*.12,d*bd,{rz:.35,x:x*w,y:H*bh,z:z*d});\r\n  P.push({...part,accent:i===3?1:0,machine:TIER_MODULES[kind][i]});\r\n }\r\n // Small roof crests carry all seven faction motifs without a high-poly prop budget.\r\n const anchors={hq:[.12,.481],hall:[.25,.37],care:[.22,.12],observatory:[.25,.421],habitat:[.2,.25],hangar:[.2,.08],production:[.25,.2],expedition:[.07,.451]};\r\n const [cy,cz]=anchors[kind],shape=ART_DIRECTION[faction]?.shape||'wedge',y=H*cy,z=d*cz,r=Math.min(w,d)*.07;\r\n let points;\r\n if(shape==='ring'){\r\n  const v=[];for(let i=0;i<6;i++){const a=i*Math.PI/3,b=(i+1)*Math.PI/3;for(const [angle,rad] of [[a,r],[b,r],[a,r*.55],[b,r],[b,r*.55],[a,r*.55]])v.push(Math.cos(angle)*rad,y+Math.sin(angle)*rad,z);}\r\n  const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(v,3));g.computeVertexNormals();P.push({g,accent:1,tell:true});return;\r\n }\r\n const shapes={wedge:[[0,1],[1,-.5],[0,-1],[-1,-.5]],pylon:[[0,1.5],[.7,.5],[.7,-.6],[0,-1.2],[-.7,-.6],[-.7,.5]],pod:[[0,1],[1,.4],[.7,-1],[-.7,-1],[-1,.4]],canopy:[[-1,-.6],[-1,.5],[0,1],[1,.5],[1,-.6],[0,0]],curl:[[-.8,-1],[0,-.5],[.8,.5],[0,1],[-.5,.5],[.2,.4]],fan:[[-1,-.5],[-.9,.5],[-.4,1],[0,.6],[.4,1],[.9,.5],[1,-.5]]};\r\n points=shapes[shape]||shapes.wedge;const v=[];\r\n for(let i=0;i<points.length;i++){const a=points[i],b=points[(i+1)%points.length];v.push(0,y,z+.02,...[b[0]*r,y+b[1]*r,z],...[a[0]*r,y+a[1]*r,z]);}\r\n const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(v,3));g.computeVertexNormals();P.push({g,accent:1,tell:true});\r\n}\r\n\r\n// ---- hq: fat central dome on a wide plinth with side wings. Knobs: wing count 2/3/4, dome squash, crown style (ring/spire/fan).\r\nfunction hq(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.96,fd=d*.96,wings=2+pick(seed,0,3),squash=.7+rnd(seed,1)*.45,crownStyle=pick(seed,2,3);\r\n P.push(block(T,style,0,0,0,fw,H*.2,fd,8));\r\n const coreTop=H*(L>=4?.7:.58);\r\n P.push(block(T,style==='dome'?'round':style,0,H*.2,0,fw*.42,coreTop-H*.2,fd*.42,8));\r\n const r0=Math.min(fw,fd)*.27;\r\n P.push(tell(hemi(T,r0,r0*squash*1.4,6,3,{y:coreTop})));\r\n const topY=coreTop+r0*squash*1.4,n=Math.min(wings,L>=3?wings:L>=2?Math.min(3,wings):2);\r\n const spots=[[fw*.35,0,.3,.5],[-fw*.35,0,.3,.5],[0,-fd*.36,.5,.24],[0,fd*.38,.5,.2]];\r\n for(let i=0;i<n;i++){const [sx,sz,ww,dd]=spots[i];P.push(tell(block(T,style,sx,H*.2,sz,fw*(i<2?.27:ww),H*.3,fd*(i<2?dd:.24),6)));}\r\n if(L>=3){\r\n  if(crownStyle===0)P.push(accent(tell({g:xf(new T.TorusGeometry(r0*1.05,.06*u,3,6),{rx:Math.PI/2,y:topY+.1*u})})));\r\n  else if(crownStyle===1)P.push(accent(tell({g:xf(new T.ConeGeometry(.22*u,1.2*u,5,1),{y:topY+.55*u})})));\r\n  else for(const a of [-.45,0,.45])P.push(accent(tell({g:xf(new T.BoxGeometry(.06*u,.9*u,.06*u),{rz:a,x:Math.sin(-a)*.4*u,y:topY+.4*u})})));\r\n }\r\n return P;}\r\n\r\n// ---- hall: stacked tiers under a faction roof piece. Knobs: tier count, base flare, roof piece (by faction shape).\r\nfunction hall(c){const {T,style,w,d,H,u,level:L,seed,faction}=c,P=[],fw=w*.9,fd=d*.9,shape=ART_DIRECTION[faction]?.shape||'wedge';\r\n const tiers=Math.min(4,2+pick(seed,0,2)+(L>=4?1:0)),flare=.84+rnd(seed,1)*.14,bh=H*.72/tiers;let y=0,s=1;\r\n P.push(block(T,style,0,0,0,fw*flare+.0,Math.min(.14*H,bh*.6),fd*flare,8)); // base flare skirt\r\n y=Math.min(.14*H,bh*.6);\r\n for(let i=0;i<tiers;i++){P.push(block(T,style,0,y,0,fw*s*.82,bh,fd*s*.82,8));y+=bh;s*=.84;}\r\n const r=Math.min(fw,fd)*.3*s*1.1,ry=y;\r\n const t=p=>P.push(accent(tell(p)));\r\n if(shape==='wedge')t({g:xf(prism(T,r*2.6,r*2,r*1.5,0,0,0).g,{y:ry})});\r\n else if(shape==='pylon')t({g:xf(new T.SphereGeometry(r*.8,6,4),{sy:1.6,y:ry+r*1.1})});\r\n else if(shape==='pod'){t({g:xf(new T.SphereGeometry(r*.8,6,3),{y:ry+r*.7})});t({g:xf(new T.SphereGeometry(r*.5,6,3),{y:ry+r*1.7})});}\r\n else if(shape==='canopy'){t({g:xf(new T.CylinderGeometry(r*.12,r*.12,r*1.2,4,1),{y:ry+r*.6})});t({g:xf(new T.ConeGeometry(r*1.5,r*.6,8,1),{y:ry+r*1.35})});}\r\n else if(shape==='curl')t({g:xf(new T.TorusGeometry(r*.9,r*.16,3,6,Math.PI*1.5),{rz:Math.PI*.25,y:ry+r*1.2})});\r\n else if(shape==='fan')for(const a of [-.6,0,.6])t({g:xf(new T.BoxGeometry(r*.35,r*1.7,r*.2),{rz:a,x:-Math.sin(a)*r*.7,y:ry+r*.8})});\r\n else t({g:xf(new T.TorusGeometry(r*1.05,r*.18,3,8),{rx:Math.PI/2,y:ry+r*1.4})}); // ring: floating halo\r\n // level: an extra story/skirt wing, not just props\r\n if(L>=3)P.push(block(T,style,fw*.4,0,fd*.25,fw*.22,H*.3,fd*.3,6));\r\n return P;}\r\n\r\n// ---- care (Fuel kitchen): low long shed with a tall off-centre chimney + steam. Knobs: stack count 1-3, awning side, roof pitch.\r\nfunction care(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.92,fd=d*.92,side=rnd(seed,4)<.5?-1:1,pitch=H*(.1+.18*rnd(seed,1)),stacks=Math.min(1+pick(seed,2,3),L>=3?3:L>=2?2:1),sh=H*.42;\r\n P.push(block(T,style,0,0,-fd*.16,fw,sh,fd*.55,8));\r\n if(style!=='dome')P.push(prism(T,fw*1.02,fd*.62,pitch,0,sh,-fd*.16));\r\n const x0=(rnd(seed,3)<.5?-1:1)*fw*.3;\r\n for(let i=0;i<stacks;i++){const hh=H*(i?.85:.85+.5*rnd(seed,5))-sh*.2,x=x0+(i?-Math.sign(x0)*fw*.22*i:0);\r\n  const stack={g:xf(new T.CylinderGeometry(.11*u,.13*u,hh,6,1),{x,y:sh*.7+hh/2,z:-fd*.28})};P.push(i?stack:tell(stack));\r\n  if(i===0){P.push(accent(tell({g:xf(new T.OctahedronGeometry(.17*u,0),{x:x+.04*u,y:sh*.7+hh+.22*u,z:-fd*.28})})));P.push(accent({g:xf(new T.OctahedronGeometry(.11*u,0),{x:x+.2*u,y:sh*.7+hh+.55*u,z:-fd*.28})}));}}\r\n P.push(accent(tell({g:xf(new T.BoxGeometry(fw*.5,.05*u,fd*.3),{rx:.35,x:side*fw*.22,y:sh*.8,z:fd*.28})}))); // awning\r\n if(L>=3)P.push(block(T,style,-side*fw*.32,0,fd*.28,fw*.3,sh*.7,fd*.28,6)); // annex shed\r\n return P;}\r\n\r\n// ---- observatory: squat drum with a tilted telescope tube and an open dish. Knobs: tube angle 20-60 deg, tube length, dish diameter.\r\nfunction observatory(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.84,fd=d*.84,a=(20+40*rnd(seed,2))*Math.PI/180,len=H*(.55+.55*rnd(seed,1)),dish=fw*(.28+.24*rnd(seed,0));\r\n const dh=H*.46;P.push(block(T,style==='dome'?'round':style,0,0,0,fw,dh,fd,10));let top=dh;\r\n if(L>=2){P.push(block(T,style==='dome'?'round':style,0,dh,0,fw*.62,H*.2,fd*.62,8));top=dh+H*.2;}\r\n P.push(accent(tell({g:xf(new T.SphereGeometry(dish/2,8,3,0,Math.PI*2,0,Math.PI/2),{rx:Math.PI,sy:.55,x:-fw*.2,y:top+dish*.3,z:fd*.1})}))); // open dish, concave up\r\n const tube=(s)=>{const g=new T.CylinderGeometry(.08*u,.11*u,len,6,1);g.rotateZ(-a*s);g.translate(fw*.1*s+Math.sin(a*s)*len/2,top+Math.cos(a)*len/2,-fd*.05);return tell({g});};\r\n P.push(tube(1));P.push(accent(tell({g:xf(new T.OctahedronGeometry(.12*u,0),{x:fw*.1+Math.sin(a)*len,y:top+Math.cos(a)*len,z:-fd*.05})})));\r\n if(L>=3){const t=tube(-1);t.tell=false;P.push(t);}\r\n return P;}\r\n\r\n// ---- habitat (crew depot): a staggered row of small pods linked by a tube. Knobs: pod count 2-4, pod radius, link height.\r\nfunction habitat(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.94,fd=d*.9,n=Math.max(2,Math.min(4,2+pick(seed,0,2)+(L>=3?1:0))),rp=Math.min(fw/(2*n)*.96,fd*.3)*(.85+.15*rnd(seed,1)),link=H*(.12+.2*rnd(seed,2)),hp=H*.55;\r\n for(let i=0;i<n;i++){const x=-fw/2+rp+i*((fw-2*rp)/(n-1)),z=(i%2?1:-1)*fd*.13;\r\n  P.push(style==='box'?block(T,'box',x,0,z,rp*1.9,hp,rp*1.9):style==='round'?block(T,'round',x,0,z,rp*2,hp,rp*2,7):hemi(T,rp,hp,7,2,{x,z}));\r\n  P[P.length-1].tell=true;\r\n  if(L>=4)P.push(accent({g:xf(new T.ConeGeometry(.1*u,.3*u,4,1),{x,y:hp+.15*u,z})}));}\r\n P.push(accent(tell({g:xf(new T.CylinderGeometry(.07*u,.07*u,fw*.92,5,1),{rz:Math.PI/2,y:link})}))); // the link tube\r\n return P;}\r\n\r\n// ---- hangar (starport): wide low pad, open front gantry arch, landing circle and ships parked by tier. Knobs: arch width, mast count, pad radius.\r\nfunction hangar(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.94,fd=d*.9,sh=H*.4,archW=fw*(.42+.3*rnd(seed,0)),masts=1+pick(seed,1,3),padR=Math.min(fd*.26,fw*.18)*(.8+.3*rnd(seed,2));\r\n P.push(block(T,style,0,0,-fd*.2,fw,sh,fd*.55,8));\r\n const ah=H*(.5+.35*rnd(seed,3)),px=archW/2;\r\n for(const s of [-1,1])P.push(tell(box(T,.14*u,ah,.14*u,{x:s*px,y:ah/2,z:fd*.32})));\r\n P.push(tell(box(T,archW+.2*u,.12*u,.16*u,{y:ah,z:fd*.32})));\r\n P.push(accent(tell({g:xf(new T.CylinderGeometry(padR,padR,.05*u,10,1),{y:sh+.03*u,z:-fd*.2})}))); // landing circle\r\n for(let i=0;i<masts;i++)P.push(box(T,.07*u,H*(.7+.3*rnd(seed,4+i)),.07*u,{x:masts===1?fw*.45:-fw*.45+i*(fw*.9)/(masts-1),y:H*.4,z:-fd*.4}));\r\n const ships=L>=5?3:L>=4?2:L>=2?1:0;\r\n for(let i=0;i<ships;i++){const sx=(i-(ships-1)/2)*padR*1.5;P.push(accent({g:xf(new T.ConeGeometry(.12*u,.5*u,4,1),{rx:Math.PI/2,x:sx,y:sh+.16*u,z:-fd*.2})}));P.push(accent({g:xf(new T.BoxGeometry(.4*u,.03*u,.14*u),{x:sx,y:sh+.15*u,z:-fd*.2-.08*u})}));}\r\n return P;}\r\n\r\n// ---- production (barracks): long block with a side door ramp and a front banner pole. Knobs: length ratio 1.2-1.8, ramp side, banner height.\r\nfunction production(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.92,fd=d*.92,rr=1.2+.6*rnd(seed,0),dep=Math.max(fd*.4,fd*.62*1.2/rr),side=rnd(seed,1)<.5?-1:1,bh=H*(1+.5*rnd(seed,2)),mh=H*.5;\r\n P.push(block(T,style,0,0,-fd*.08,fw,mh,dep,8));\r\n if(style!=='dome')P.push(prism(T,fw*1.02,dep*1.04,H*.16,0,mh,-fd*.08));\r\n const rise=mh*.7,run=fw*.3,ang=Math.atan2(rise,run),len=Math.hypot(rise,run);\r\n P.push(tell({g:xf(new T.BoxGeometry(len,.05*u,dep*.45),{rz:-side*ang,x:side*(fw/2+run/2),y:rise/2,z:-fd*.08+dep*.12})})); // door ramp\r\n const px=-side*fw*.42;\r\n P.push(tell({g:xf(new T.CylinderGeometry(.035*u,.035*u,bh,4,1),{x:px,y:bh/2,z:fd*.32})}));\r\n P.push(accent(tell(box(T,.42*u,.26*u,.03*u,{x:px+.22*u,y:bh-.14*u,z:fd*.32}))));\r\n if(L>=3)P.push(block(T,style,0,0,-fd*.08-dep*.5-fd*.16,fw*.7,mh*.8,fd*.28,6)); // rear wing\r\n return P;}\r\n\r\n// ---- expedition (launch post): a slim rail leaning out of a low platform with a cradle bulb. Knobs: lean 30-70 deg, rail length, bulb size.\r\nfunction expedition(c){const {T,style,w,d,H,u,level:L,seed}=c,P=[],fw=w*.9,fd=d*.9,phi=(35+35*rnd(seed,0))*Math.PI/180,len=H*(1.1+.7*rnd(seed,1)),bulb=(.15+.12*rnd(seed,2))*u*1.6,ph=H*.14,bx=-fw*.28,by=ph;\r\n const bar=(x0,y0,x1,y1,z,th)=>{const l=Math.hypot(x1-x0,y1-y0),g=new T.BoxGeometry(th,l,th);g.rotateZ(Math.atan2(y1-y0,x1-x0)-Math.PI/2);g.translate((x0+x1)/2,(y0+y1)/2,z);return {g};};\r\n const rail=(z,l,th)=>bar(bx,by,bx+Math.cos(phi)*l,by+Math.sin(phi)*l,z,th);\r\n P.push(block(T,style==='dome'?'round':style,0,0,0,fw,ph,fd,8));\r\n P.push(tell(rail(0,len,.14*u)));\r\n P.push(accent(tell({g:xf(new T.SphereGeometry(bulb,6,4),{x:bx+Math.cos(phi)*len,y:by+Math.sin(phi)*len})})));\r\n P.push(bar(bx+fw*.34,by,bx+Math.cos(phi)*len*.55,by+Math.sin(phi)*len*.55,0,.07*u)); // brace\r\n if(L>=3)P.push(rail(.34*u,len*.8,.1*u));\r\n if(L>=5)P.push(rail(-.34*u,len*.8,.1*u));\r\n return P;}\r\n\r\nconst KINDS={hq,hall,care,observatory,habitat,hangar,production,expedition};\r\n// -> array of {g,accent?,tell?} (geometry already placed, base at y=0, front toward +z) or null for kinds this module does not draw.\r\nexport function partsForKind(T,kind,{w,d,height,level,policy,seed=0,faction}={}){\r\n const fn=KINDS[kind];if(!fn)return null;\r\n const L=Math.max(1,Math.min(5,level|0)),growth=.76+.04*L;\r\n const c={T,style:styleOf(policy),w:w*growth,d:d*growth,H:height,u:height/(1+.4*L),level:L,seed:seed>>>0,faction};\r\n const P=fn(c);tierMachines(T,P,c,kind);\r\n // Keep ramps, rails and seeded variations within the original tile allocation.\r\n const bounds=new T.Box3();for(const p of P){p.g.computeBoundingBox();bounds.union(p.g.boundingBox);}\r\n const sx=Math.min(1,w*.49/Math.max(Math.abs(bounds.min.x),Math.abs(bounds.max.x))),sz=Math.min(1,d*.49/Math.max(Math.abs(bounds.min.z),Math.abs(bounds.max.z)));\r\n for(const p of P)p.g.scale(sx,1,sz);\r\n return P;\r\n}\r\n","modules/coach-world-rts/building-icon.mjs":"// Tray/menu icon = the building's OWN near-field 3D art (same part builders base3d renders), rasterized to a small RGBA buffer.\r\n// Pure JS (z-buffered triangles, no canvas) so it is testable in node; cached per id+level+faction. Callers pass the three module T.\r\nimport {BUILDING_BY_ID,FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {bake} from './base-meshes.mjs';\r\nimport {partsForKind,BUILDING_ART_KINDS,hashSeed} from './building-art.mjs';\r\nimport {partsForDefense} from './tower-art.mjs';\r\nexport const ICON_PX=48,SCALE=1.6,DEFENSE_COLORS={color:'#8f9dab',dark:'#4a5663',accent:'#7fe3ff'};\r\nconst cache=new Map();\r\n// level = the level the card would build/upgrade to (1..5)\r\nexport function buildingIcon(T,id,level=1,faction='chest'){\r\n const def=BUILDING_BY_ID[id];if(!def)return null;level=Math.max(1,Math.min(5,level|0));\r\n const f=FACTION_BY_ID[def.track]||FACTION_BY_ID[faction]||FACTION_BY_ID.chest,key=`${id}/${level}/${f.id}`;\r\n if(cache.has(key))return cache.get(key);\r\n const w=def.w*SCALE,d=def.h*SCALE,height=(1+.4*level)*SCALE;let parts,colors=f;\r\n if(BUILDING_ART_KINDS.includes(def.kind))parts=partsForKind(T,def.kind,{w,d,height,level,policy:f.policy,seed:hashSeed(`:${id}`),faction:f.id});\r\n else{parts=partsForDefense(T,def.kind,level);colors=DEFENSE_COLORS;}\r\n const out=parts?raster(bake(T,parts,{w,height},colors,true)):null;cache.set(key,out);return out;\r\n}\r\nfunction raster(g){\r\n const N=ICON_PX,p=g.attributes.position,n=g.attributes.normal,c=g.attributes.color,px=new Uint8ClampedArray(N*N*4),zb=new Float32Array(N*N).fill(-1e9);\r\n const sx=i=>p.getX(i)+p.getZ(i)*.4,sy=i=>p.getY(i)-p.getZ(i)*.3,dz=i=>p.getZ(i)*.6+p.getY(i)*.2; // 3/4 view; larger dz = nearer\r\n let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;for(let i=0;i<p.count;i++){x0=Math.min(x0,sx(i));x1=Math.max(x1,sx(i));y0=Math.min(y0,sy(i));y1=Math.max(y1,sy(i));}\r\n const k=(N-4)/Math.max(x1-x0,y1-y0,1e-3),ox=(N-(x1-x0)*k)/2,oy=(N-(y1-y0)*k)/2,X=i=>ox+(sx(i)-x0)*k,Y=i=>N-oy-(sy(i)-y0)*k;\r\n for(let t=0;t<p.count;t+=3){\r\n  const a=[X(t),Y(t)],b=[X(t+1),Y(t+1)],e=[X(t+2),Y(t+2)],area=(b[0]-a[0])*(e[1]-a[1])-(b[1]-a[1])*(e[0]-a[0]);if(Math.abs(area)<1e-6)continue;\r\n  const lit=.55+.45*Math.max(0,(-n.getX(t)*.4+n.getY(t)*.8+n.getZ(t)*.45)),[r,gg,bl]=[c.getX(t),c.getY(t),c.getZ(t)];\r\n  for(let y=Math.max(0,Math.floor(Math.min(a[1],b[1],e[1])));y<=Math.min(N-1,Math.ceil(Math.max(a[1],b[1],e[1])));y++)for(let x=Math.max(0,Math.floor(Math.min(a[0],b[0],e[0])));x<=Math.min(N-1,Math.ceil(Math.max(a[0],b[0],e[0])));x++){\r\n   const qx=x+.5,qy=y+.5,w0=((b[0]-qx)*(e[1]-qy)-(b[1]-qy)*(e[0]-qx))/area,w1=((e[0]-qx)*(a[1]-qy)-(e[1]-qy)*(a[0]-qx))/area,w2=1-w0-w1;\r\n   if(w0<-.02||w1<-.02||w2<-.02)continue;const z=w0*dz(t)+w1*dz(t+1)+w2*dz(t+2),o=y*N+x;if(z<=zb[o])continue;zb[o]=z;\r\n   px[o*4]=r*255*lit;px[o*4+1]=gg*255*lit;px[o*4+2]=bl*255*lit;px[o*4+3]=255;\r\n  }\r\n }\r\n g.dispose();return {width:N,height:N,data:px};\r\n}\r\n","modules/coach-world-rts/building-pick.mjs":"// Raycasts must include the sprite layer, and ignore its transparent padding.\r\nconst alphaCache=new WeakMap();\r\nexport function opaqueBuildingHit(hit){\r\n const image=hit.object?.material?.map?.image,uv=hit.uv;if(!image||!uv)return true;\r\n let data=alphaCache.get(image);if(!data){try{data=image.data||image.getContext?.('2d')?.getImageData(0,0,image.width,image.height).data;if(data)alphaCache.set(image,data);}catch{return true;}}\r\n if(!data)return true;\r\n const x=Math.min(image.width-1,Math.max(0,Math.floor(uv.x*image.width))),y=Math.min(image.height-1,Math.max(0,Math.floor((1-uv.y)*image.height)));\r\n return data[(y*image.width+x)*4+3]>15;\r\n}\r\nexport function pickBuildingId(ray,meshes,layer,base){\r\n const sprites=layer?.group?.children||[],geometry=meshes?.group?.children||[];\r\n for(const hit of ray.intersectObjects([...geometry,...sprites],false)){\r\n  const i=sprites.indexOf(hit.object),id=i>=0?meshes.far[i]?.id:hit.object.name?.replace(/^rts-/,'');\r\n  if(id&&base?.buildings?.[id]?.level>0&&opaqueBuildingHit(hit))return id;\r\n }\r\n return null;\r\n}\r\n","modules/coach-world-rts/cannon-fx.mjs":"// R59 raid cooldown cannon visual, reused by the wild-coach tranquilizer (mode 'sleep').\r\n// fireCannon(T, world, {from, to, faction, mode, radius, scale, origin, flightMs, reduced, onFrame, now}) -> {update(now)->alive, dispose, group, impactAt}\r\n// from/to: tile-centre coordinates (same space as the crew billboards); radius in tiles; metres = (tile - origin) * scale like fx3d.\r\n// Pass onFrame (base3d handle.onFrame) to self-drive and auto-dispose; otherwise call update(now) yourself.\r\nimport { ENGINE_REDUCED } from './elements.mjs';\r\nimport { SCALE, spawnTile } from './base-meshes.mjs';\r\n\r\n// colour + particle behaviour per faction element (ice/slow looks come from the pale blue-green nausea and the white-blue electric)\r\nexport const CANNON_STYLE = Object.freeze({\r\n  chest: { color: '#ff4a3a', bits: 'spark' },\r\n  'arms-shoulders': { color: '#ffd36b', bits: 'spark' },\r\n  quads: { color: '#b89a6a', bits: 'chunk' },\r\n  glutes: { color: '#8dff4a', bits: 'puff' },\r\n  yoga: { color: '#9fe3ff', bits: 'shard' }, // pale-blue frost ball + ring, falling ice shards\r\n  'martial-arts': { color: '#ff7a2a', bits: 'ember' },\r\n  cardio: { color: '#5fe8ff', bits: 'spark' },\r\n  sleep: { color: '#a58bff', bits: 'zzz' }\r\n});\r\n\r\nexport function fireCannon(T, world, { from, to, faction = 'chest', mode = 'damage', radius = 1.5, scale = SCALE, origin = spawnTile(), flightMs = 600, reduced = ENGINE_REDUCED(), onFrame = null, now = () => performance.now() } = {}) {\r\n  const st = CANNON_STYLE[mode === 'sleep' ? 'sleep' : faction] || CANNON_STYLE.chest, col = new T.Color(st.color);\r\n  const M = p => ({ x: (p.x - origin.x) * scale, z: (p.y - origin.y) * scale });\r\n  const a = M(from), b = M(to), R = radius * scale, Y = .5 * scale, t0 = now();\r\n  const impactAt = reduced ? 150 : flightMs, total = reduced ? 500 : flightMs + 1100;\r\n  const group = new T.Group(); group.name = 'rts-cannon-fx';\r\n  const mat = (o = {}) => new T.MeshBasicMaterial({ color: col, transparent: true, blending: T.AdditiveBlending, depthWrite: false, depthTest: false, fog: false, toneMapped: false, side: T.DoubleSide, ...o });\r\n  const glow = size => { const s = new T.Sprite(new T.SpriteMaterial({ color: col, transparent: true, blending: T.AdditiveBlending, depthWrite: false, depthTest: false, fog: false, toneMapped: false })); s.scale.set(size, size, 1); s.renderOrder = 7; return s; };\r\n  const ring = new T.Mesh(new T.RingGeometry(.86, 1, 40), mat()), disc = new T.Mesh(new T.CircleGeometry(1, 32), mat({ opacity: 0 }));\r\n  for (const m of [ring, disc]) { m.rotation.x = -Math.PI / 2; m.position.set(b.x, .06 * scale, b.z); m.renderOrder = 6; m.visible = false; }\r\n  const flash = glow(R * 1.6); flash.position.set(b.x, Y, b.z); flash.visible = false;\r\n  group.add(ring, disc, flash);\r\n  let charge = null, ball = null, beam = null, trail = [];\r\n  const bits = [];\r\n  if (!reduced) {\r\n    charge = glow(.3 * scale); charge.position.set(a.x, Y + .3 * scale, a.z);\r\n    ball = glow(.45 * scale); ball.visible = false;\r\n    beam = new T.Line(new T.BufferGeometry().setFromPoints([new T.Vector3(a.x, Y, a.z), new T.Vector3(b.x, Y, b.z)]), new T.LineBasicMaterial({ color: col, transparent: true, blending: T.AdditiveBlending, depthTest: false, fog: false })); beam.renderOrder = 6;\r\n    group.add(charge, ball, beam);\r\n    for (let i = 0; i < 6; i++) { const g = glow(.22 * scale); g.visible = false; trail.push(g); group.add(g); }\r\n    const N = st.bits === 'zzz' ? 8 : 18;\r\n    for (let i = 0; i < N; i++) {\r\n      const ang = i / N * Math.PI * 2, sp = (.5 + (i * 37 % 10) / 10) * R * 1.2, o = (st.bits === 'chunk' || st.bits === 'shard') ? new T.Mesh(new T.BoxGeometry(.1 * scale, .1 * scale, .1 * scale), mat({ blending: T.NormalBlending })) : glow((st.bits === 'puff' || st.bits === 'zzz' ? .5 : .16) * scale);\r\n      o.visible = false; group.add(o); bits.push({ o, vx: Math.cos(ang) * sp, vz: Math.sin(ang) * sp, vy: st.bits === 'puff' || st.bits === 'zzz' ? .5 * scale : (1.6 + (i % 4) * .4) * scale });\r\n    }\r\n  }\r\n  world.add(group);\r\n  let dead = false;\r\n  const dispose = () => { if (dead) return; dead = true; off?.(); group.removeFromParent(); group.traverse(o => { o.geometry?.dispose(); o.material?.dispose(); }); };\r\n  const lerp = (p, q, k) => p + (q - p) * k;\r\n  const ballAt = k => ({ x: lerp(a.x, b.x, k), y: Y + .3 * scale + Math.sin(Math.PI * k) * 1.6 * scale, z: lerp(a.z, b.z, k) }); // arc\r\n  function update(t = now()) {\r\n    if (dead) return false;\r\n    const el = t - t0;\r\n    if (el >= total) { dispose(); return false; }\r\n    if (!reduced && el < impactAt) {\r\n      const k = el / flightMs, c = Math.min(1, k * 4); // first quarter: charge glow grows at the pod, then the ball leaves\r\n      charge.scale.setScalar(scale * (.3 + .6 * c)); charge.material.opacity = k > .25 ? Math.max(0, 1 - (k - .25) * 4) : 1;\r\n      if (k > .2) { const p = ballAt((k - .2) / .8); ball.visible = true; ball.position.set(p.x, p.y, p.z); ball.scale.setScalar(scale * (.45 + .1 * Math.sin(el * .04))); }\r\n      trail.forEach((g, i) => { const kk = (k - .2) / .8 - (i + 1) * .05; g.visible = kk > 0; if (kk > 0) { const p = ballAt(kk); g.position.set(p.x, p.y, p.z); g.material.opacity = 1 - (i + 1) / 7; } });\r\n      beam.material.opacity = Math.max(0, .5 * (1 - k));\r\n    } else {\r\n      const p = Math.min(1, (el - impactAt) / (total - impactAt));\r\n      if (!reduced) { ball.visible = false; charge.visible = false; beam.visible = false; trail.forEach(g => { g.visible = false; }); }\r\n      flash.visible = true; flash.material.opacity = Math.max(0, 1 - p * (reduced ? 1 : 5));\r\n      ring.visible = disc.visible = true;\r\n      ring.scale.setScalar(R * (reduced ? 1 : Math.min(1, p * 3.5) || .01)); ring.material.opacity = 1 - p * .6;\r\n      disc.scale.setScalar(R); disc.material.opacity = .22 * (1 - p) * (.7 + .3 * Math.sin(el * .012));\r\n      const s = (el - impactAt) / 1000;\r\n      for (const q of bits) { q.o.visible = true; q.o.position.set(b.x + q.vx * s * .6, Y + q.vy * s - (q.o.isSprite && st.bits !== 'puff' && st.bits !== 'zzz' ? 2.4 * scale * s * s : 0), b.z + q.vz * s * .6); if (q.o.material) q.o.material.opacity = 1 - p; if (st.bits === 'chunk' || st.bits === 'shard') q.o.rotation.set(s * 6, s * 5, 0); }\r\n    }\r\n    return true;\r\n  }\r\n  const off = onFrame ? onFrame(t => { update(); }) : null;\r\n  return { group, update, dispose, impactAt, get done() { return dead; } };\r\n}\r\n","modules/coach-world-rts/capture-minigame.css":"/* AR rare-coach capture mini game (hud.css pixel look). The whole overlay is the hold pad. */\r\n.cw-cap{position:absolute;inset:0;z-index:8;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:calc(20px + env(safe-area-inset-top,0px)) 16px calc(20px + env(safe-area-inset-bottom,0px));\r\n background:#05080cd9;color:#fff;touch-action:none;user-select:none;-webkit-user-select:none;font:600 15px/1.25 \"Pixelify Sans\",m5x7,system-ui,sans-serif;--cap-tier:#ffd23a}\r\n.cw-cap-head{display:flex;align-items:center;gap:10px;max-width:calc(100% - 72px);font-size:18px}\r\n.cw-cap-head b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\r\n.cw-cap-tier{padding:2px 8px;border:2px solid var(--cap-tier);border-radius:8px;color:var(--cap-tier);font-size:12px;text-transform:uppercase}\r\n.cw-cap-track{position:relative;width:64px;height:min(52vh,420px);background:#0b0f14;border:3px solid #fff;border-radius:10px;box-shadow:0 0 0 3px #000;overflow:hidden}\r\n.cw-cap-zone{position:absolute;left:0;right:0;background:var(--cap-tier);opacity:.55;border-top:3px solid var(--cap-tier);border-bottom:3px solid var(--cap-tier);box-sizing:border-box}\r\n.cw-cap-cursor{position:absolute;left:4px;right:4px;height:14px;margin-top:-7px;background:#fff;border:3px solid #000;border-radius:7px;box-sizing:border-box}\r\n.cw-cap[data-in-zone=\"true\"] .cw-cap-cursor{background:var(--cap-tier)}\r\n.cw-cap-bar{width:min(70vw,260px);height:14px;background:#0b0f14;border:3px solid #fff;border-radius:8px;box-shadow:0 0 0 3px #000;overflow:hidden}\r\n.cw-cap-fill{height:100%;background:var(--cap-tier);transition:width .08s linear}\r\n.cw-cap-msg{min-height:20px;text-align:center;max-width:90%}\r\n.cw-cap-row{display:flex;gap:10px;min-height:48px}\r\n.cw-cap-row button[hidden]{display:none}\r\n.cw-cap-x{position:absolute;top:calc(10px + env(safe-area-inset-top,0px));right:12px;min-width:48px;min-height:48px}\r\n@media (prefers-reduced-motion:reduce){.cw-cap-fill{transition:none}}\r\n","modules/coach-world-rts/capture-minigame.mjs":"// AR rare-coach capture: a fishing mini game. HOLD to lift the cursor, RELEASE and it sinks; keep it inside the moving tier-coloured zone to fill the bar.\r\n//  createFishing()        pure physics, advanced by step(dtMs,holding); no clock, no DOM, seeded rng -> deterministic.\r\n//  wildEntryFor(npc,now)  the base.wild ledger entry for a caught rare (shape = modules/coach-world/state.mjs validWildEntry; id is stable per NPC so a repeat save is a no-op).\r\n//  createCaptureSession() win/lose/retry/cancel state machine; a win calls commit(entry) EXACTLY ONCE (a failed save retries the same entry, never the game).\r\n//  startCapture()         session + overlay on `root`; owns rAF, hold input, background pause and cleanup.\r\n// The game never writes a save itself: the host passes commit(entry) (the world client's authoritative catch route) and isCaught(entry).\r\nimport {TIERS,PALETTE,HOLO_HUES,rollTier} from './wild-rarity.mjs';\r\nimport {UNIT_CATALOG} from '../coach-world/unit-catalog.mjs';\r\n\r\nexport const STEP_MAX_MS=50,TIME_LIMIT_S=45,GRACE_S=1.5,START_PROGRESS=.18,MISS_EASE_S=.9,MISS_FLOOR=.3,CATCHUP_X=1.6,CATCHUP_K=.55,CURSOR_UP=.85,CURSOR_DOWN=.7,CURSOR_LAG_S=.12,AR_ZONE_ID='ar-npc';\r\n// Per tier: zone height (share of the track), zone speed (track/s, always well under CURSOR_UP so it can be followed), fill and drain per second.\r\n// A clean run takes ~8-12 s (gain ~.08-.1/s from START_PROGRESS) so the zone is seen moving; drain is about half the fill rate.\r\n// Missed target: the drain eases in over MISS_EASE_S (starts at MISS_FLOOR of full) and a zone more than CATCHUP_X zone-heights away slows to CATCHUP_K, so a slip is recoverable.\r\nexport const CONFIG=Object.freeze({\r\n white:{zone:.36,speed:.12,gain:.100,loss:.050},green:{zone:.32,speed:.15,gain:.095,loss:.052},blue:{zone:.28,speed:.18,gain:.090,loss:.054},\r\n orange:{zone:.24,speed:.22,gain:.086,loss:.056},gold:{zone:.20,speed:.26,gain:.083,loss:.058},iridescent:{zone:.17,speed:.30,gain:.080,loss:.060},\r\n});\r\nexport const tierOf=npc=>TIERS.includes(npc?.tier)?npc.tier:rollTier(npc?.id||'npc','park'); // same roll as the flight survey marker\r\nexport const tierColor=(tier,ms=0)=>PALETTE[tier]||'#'+HOLO_HUES[Math.floor(ms/180)%HOLO_HUES.length].toString(16).padStart(6,'0');\r\n\r\nconst hash=s=>{let h=2166136261;for(const c of String(s)){h^=c.charCodeAt(0);h=Math.imul(h,16777619);}return h>>>0;};\r\nconst rngFor=seed=>{let a=seed>>>0;return()=>{a=a+0x6D2B79F5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};};\r\nconst clamp=(v,lo,hi)=>Math.min(hi,Math.max(lo,v));\r\n\r\nexport function createFishing({tier='white',seed='seed',attempt=0}={}){\r\n const cfg=CONFIG[tier]||CONFIG.white,r=rngFor(hash(`${seed}|${tier}|${attempt}`)),lo=cfg.zone/2,hi=1-cfg.zone/2;\r\n const s={tier,pos:.6,vel:0,zone:cfg.zone,zoneC:.6,progress:START_PROGRESS,t:0,inZone:false,miss:0,status:'playing'};\r\n let goal=lo+r()*(hi-lo),speed=cfg.speed*(.5+.5*r()),wait=0;\r\n return {state:s,\r\n  step(dtMs,holding){\r\n   if(s.status!=='playing')return s;\r\n   const dt=clamp(dtMs,0,STEP_MAX_MS)/1000;s.t+=dt;\r\n   s.vel+=((holding?-CURSOR_UP:CURSOR_DOWN)-s.vel)*Math.min(1,dt/CURSOR_LAG_S);s.pos+=s.vel*dt;\r\n   if(s.pos<0||s.pos>1){s.pos=clamp(s.pos,0,1);s.vel=0;}\r\n   if(wait>0)wait-=dt;else{const d=goal-s.zoneC,m=speed*dt*(Math.abs(s.pos-s.zoneC)>CATCHUP_X*s.zone?CATCHUP_K:1);if(Math.abs(d)<=m){s.zoneC=goal;goal=lo+r()*(hi-lo);speed=cfg.speed*(.5+.5*r());wait=r()*.4;}else s.zoneC+=Math.sign(d)*m;}\r\n   s.inZone=Math.abs(s.pos-s.zoneC)<=s.zone/2;\r\n   s.miss=s.inZone?0:s.miss+dt;\r\n   s.progress=clamp(s.progress+(s.inZone?cfg.gain*dt:s.t>GRACE_S?-cfg.loss*dt*Math.min(1,MISS_FLOOR+s.miss/MISS_EASE_S*(1-MISS_FLOOR)):0),0,1);\r\n   if(s.progress>=1)s.status='won';else if(s.progress<=0||s.t>=TIME_LIMIT_S)s.status='lost';\r\n   return s;\r\n  }};\r\n}\r\n\r\n// The caught rare lands in base.wild as one of the four-legged companion units that wears the same body (sourceCoachId); among those the pick is stable per NPC.\r\nexport function unitIdFor(npc){\r\n const pool=UNIT_CATALOG.filter(u=>u.family==='Four-legged'&&u.sourceCoachId===npc?.source);\r\n return pool.length?pool[hash(npc.id)%pool.length].id:null;\r\n}\r\nconst safeId=id=>'ar-'+String(id).replace(/[^A-Za-z0-9:_-]/g,'_').slice(0,90);\r\nexport function wildEntryFor(npc,now=Date.now()){\r\n const unitId=unitIdFor(npc);if(!npc?.rare||!unitId)return null;\r\n return {id:safeId(npc.id),unitId,tier:tierOf(npc),seed:hash(`${npc.id}|${npc.level}`).toString(16),caughtAt:now,zoneId:AR_ZONE_ID};\r\n}\r\n\r\nconst TERMINAL=new Set(['saved','cancelled','owned']);\r\nexport function createCaptureSession({npc,commit,isCaught,now=()=>Date.now(),onChange,onSaved}){\r\n const tier=tierOf(npc);let entry=null,attempt=0,game=null,holding=false,paused=false,saving=null,playMs=0,phase='ready',error=null,cancelled=false;\r\n const emit=()=>{try{onChange?.(api.snapshot());}catch{}};\r\n const set=p=>{phase=p;emit();};\r\n const fresh=()=>{game=createFishing({tier,seed:npc.id,attempt});holding=false;paused=false;playMs=0;};\r\n const build=()=>{entry=wildEntryFor(npc,now());if(!entry){error='This coach cannot be caught.';phase='unavailable';return;}if(isCaught?.(entry)===true){phase='owned';return;}fresh();};\r\n function save(){ // single flight: one commit at a time, never again once it succeeded\r\n  if(saving||phase==='saved')return saving;\r\n  set('saving');error=null;\r\n  saving=Promise.resolve().then(()=>commit(entry,{playMs:Math.round(playMs)})).then(()=>{saving=null;set('saved');try{onSaved?.(entry);}catch{}},e=>{saving=null;error=e?.message||'Could not save your catch.';set('save-failed');});\r\n  return saving;\r\n }\r\n const api={\r\n  get phase(){return phase;},get entry(){return entry;},\r\n  press(){if(TERMINAL.has(phase))return;if(phase==='ready')set('playing');holding=true;emit();},\r\n  release(){holding=false;emit();},\r\n  pause(){if(phase==='playing'){paused=true;holding=false;emit();}},\r\n  resume(){if(paused){paused=false;emit();}},\r\n  step(dtMs){\r\n   if(phase!=='playing'||paused)return;\r\n   playMs+=Math.min(Math.max(dtMs,0),STEP_MAX_MS);const s=game.step(dtMs,holding); // playMs = time actually played (paused/background time excluded); the server checks it\r\n   if(s.status==='won'){holding=false;void save();}else if(s.status==='lost')set('lost');else emit();\r\n  },\r\n  retry(){if(phase!=='lost')return;attempt++;fresh();set('ready');},\r\n  retrySave(){if(phase==='save-failed')return save();},\r\n  cancel(){if(cancelled)return;cancelled=true;holding=false;if(phase!=='saved'&&phase!=='saving')phase='cancelled';emit();}, // a commit already in flight still finishes and reports through onSaved\r\n  snapshot(){const s=game?.state;return {phase,tier,paused,holding,error,name:npc.name,pos:s?.pos??0,zoneC:s?.zoneC??.5,zone:s?.zone??CONFIG[tier].zone,progress:s?.progress??0,inZone:!!s?.inZone,attempt,timeLeft:Math.max(0,TIME_LIMIT_S-(s?.t??0))};},\r\n };\r\n build();return api;\r\n}\r\n\r\nfunction ensureCss(doc){if(!doc.head||!doc.querySelectorAll)return;const href=new URL('./capture-minigame.css',import.meta.url).href;if([...doc.querySelectorAll('link[data-cw-capture]')].some(l=>l.href===href))return;const l=doc.createElement('link');l.rel='stylesheet';l.href=href;l.dataset.cwCapture='';doc.head.append(l);}\r\n\r\nconst HINT={ready:'Hold to rise, let go to sink. Keep the cursor in the zone.',playing:'Stay in the zone',lost:'It slipped away. Try again?',saving:'Saving your catch…',saved:'',owned:'Already in your base.','save-failed':'Could not save. Your catch is safe, try again.',unavailable:'This coach cannot be caught.'};\r\n/** Overlay on `root`; returns {session,close()}. onClose fires once, whatever ended it (cancel, done, host dispose). */\r\nexport function startCapture(npc,{root,doc=root.ownerDocument,commit,isCaught,onSaved,onClose,now=()=>performance.now(),raf=cb=>requestAnimationFrame(cb),caf=id=>cancelAnimationFrame(id)}){\r\n ensureCss(doc);\r\n const session=createCaptureSession({npc,commit,isCaught,onSaved,onChange:render}),ac=new AbortController(),sig={signal:ac.signal};\r\n const el=(tag,cls,parent,text)=>{const e=doc.createElement(tag);if(cls)e.className=cls;if(text!=null)e.textContent=text;parent?.append(e);return e;};\r\n const ov=el('div','cw-cap');ov.dataset.capture=npc.id;ov.setAttribute('role','dialog');ov.setAttribute('aria-label',`Catch ${npc.name}`);\r\n const head=el('div','cw-cap-head',ov),title=el('b','',head,`Catch ${npc.name}`),pill=el('span','cw-cap-tier',head);\r\n const track=el('div','cw-cap-track',ov),zoneEl=el('div','cw-cap-zone',track),cursor=el('div','cw-cap-cursor',track),bar=el('div','cw-cap-bar',ov),fill=el('div','cw-cap-fill',bar);\r\n const msg=el('div','cw-cap-msg',ov),row=el('div','cw-cap-row',ov);\r\n const btn=(label,attr,fn)=>{const b=el('button','',row,label);b.type='button';b.dataset[attr]='';b.hidden=true;b.onclick=fn;return b;};\r\n const again=btn('Try again','capRetry',()=>{session.retry();}),resave=btn('Save again','capResave',()=>{void session.retrySave();}),done=btn('Done','capDone',()=>close());\r\n const x=el('button','cw-cap-x',ov,'✕');x.type='button';x.dataset.capClose='';x.setAttribute('aria-label','Cancel');x.onclick=()=>close();\r\n let closed=false,raf0=0,last=null,ms=0;\r\n function render(sn=session.snapshot()){\r\n  if(closed)return;\r\n  const col=tierColor(sn.tier,ms);ov.style.setProperty('--cap-tier',col);pill.textContent=sn.tier;\r\n  zoneEl.style.top=`${(sn.zoneC-sn.zone/2)*100}%`;zoneEl.style.height=`${sn.zone*100}%`;\r\n  cursor.style.top=`${sn.pos*100}%`;ov.dataset.state=sn.phase;ov.dataset.inZone=String(sn.inZone);\r\n  fill.style.width=`${Math.round(sn.progress*100)}%`;\r\n  msg.textContent=sn.paused?'Paused':sn.error&&sn.phase==='save-failed'?HINT['save-failed']:HINT[sn.phase]??'';\r\n  again.hidden=sn.phase!=='lost';resave.hidden=sn.phase!=='save-failed';done.hidden=!['saved','owned','unavailable'].includes(sn.phase);\r\n  if(sn.phase==='saved')msg.textContent=`${sn.name} joined your base!`;\r\n }\r\n function loop(t){if(closed)return;raf0=raf(loop);if(last!=null)session.step(Math.min(t-last,STEP_MAX_MS));ms=t;last=t;if(session.phase==='playing')render();}\r\n const press=e=>{if(e?.target?.closest?.('button'))return;e?.preventDefault?.();ov.setPointerCapture?.(e?.pointerId);session.press();},release=()=>session.release();\r\n ov.addEventListener('pointerdown',press,sig);for(const n of ['pointerup','pointercancel','lostpointercapture'])ov.addEventListener(n,release,sig);\r\n doc.addEventListener('keydown',e=>{if(e.code==='Space'){e.preventDefault?.();if(!e.repeat)session.press();}else if(e.key==='Escape')close();},sig);\r\n doc.addEventListener('keyup',e=>{if(e.code==='Space')session.release();},sig);\r\n const bg=()=>{if(doc.hidden){session.pause();}else{last=null;session.resume();}};\r\n doc.addEventListener('visibilitychange',bg,sig);doc.defaultView?.addEventListener?.('blur',()=>session.pause(),sig);doc.defaultView?.addEventListener?.('focus',()=>{last=null;session.resume();},sig);\r\n function close(){if(closed)return;closed=true;ac.abort();caf(raf0);session.cancel();ov.remove();try{onClose?.(session.phase);}catch{}}\r\n root.append(ov);render();raf0=raf(loop);\r\n return {session,close,el:ov};\r\n}\r\n","modules/coach-world-rts/change-ledger.mjs":"// V2 change ledger: what each area looked like when the player last saw it (Fog of Change \"changed since your last visit\").\r\n// Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v2/card.mjs G), validated (persistence, owner isolation, corrupt/throwing storage, cap); review fix: corrupt = empty.\r\n// createChangeLedger({storage,owner,key,max}) -> {check(id,sig) 'new'|'same'|'changed', see(id,sig), forget(id), snapshot()}\r\n// ES module exporting a single factory function.\r\n// No external imports are used.\r\n\r\nexport function createChangeLedger({\r\n  storage,\r\n  owner = 'guest',\r\n  key = 'cw-change-ledger',\r\n  max = 200\r\n} = {}) {\r\n  // The internal map of id -> signature, preserving insertion order.\r\n  const ledger = new Map();\r\n\r\n  // Helper to load from storage once.\r\n  (function load() {\r\n    if (!storage || typeof storage.getItem !== 'function') return;\r\n    try {\r\n      const raw = storage.getItem(key + ':' + owner);\r\n      if (!raw) return;\r\n      const parsed = JSON.parse(raw);\r\n      if (!Array.isArray(parsed)) throw new Error('Not an array');\r\n      for (const item of parsed) {\r\n        if (\r\n          !Array.isArray(item) ||\r\n          typeof item[0] !== 'string' ||\r\n          typeof item[1] !== 'string'\r\n        ) {\r\n          throw new Error('Invalid pair');\r\n        }\r\n        ledger.set(item[0], item[1]);\r\n      }\r\n    } catch (_) {\r\n      ledger.clear(); // review fix: corrupt = empty, never a half-loaded ledger\r\n    }\r\n  })();\r\n\r\n  // Persist the current state to storage if possible.\r\n  function persist() {\r\n    if (!storage || typeof storage.setItem !== 'function') return;\r\n    try {\r\n      const arr = Array.from(ledger.entries());\r\n      storage.setItem(key + ':' + owner, JSON.stringify(arr));\r\n    } catch (_) {\r\n      // Ignore persistence errors silently.\r\n    }\r\n  }\r\n\r\n  // Public API\r\n  return {\r\n    /**\r\n     * Check the status of an area id against a given signature.\r\n     * @param {string} id - The area identifier.\r\n     * @param {string} sig - The current data signature.\r\n     * @returns {'new'|'same'|'changed'}\r\n     */\r\n    check(id, sig) {\r\n      if (!ledger.has(id)) return 'new';\r\n      const stored = ledger.get(id);\r\n      return stored === sig ? 'same' : 'changed';\r\n    },\r\n\r\n    /**\r\n     * Record that the player has seen this id with the given signature.\r\n     * Moves the entry to the newest position.\r\n     * @param {string} id - The area identifier.\r\n     * @param {string} sig - The current data signature.\r\n     */\r\n    see(id, sig) {\r\n      if (ledger.has(id)) ledger.delete(id); // remove old position\r\n      ledger.set(id, sig);\r\n      // Enforce max size: drop oldest entries first.\r\n      while (ledger.size > max) {\r\n        const oldestKey = ledger.keys().next().value;\r\n        ledger.delete(oldestKey);\r\n      }\r\n      persist();\r\n    },\r\n\r\n    /**\r\n     * Forget an id from the ledger entirely.\r\n     * @param {string} id - The area identifier to remove.\r\n     */\r\n    forget(id) {\r\n      if (ledger.delete(id)) persist();\r\n    },\r\n\r\n    /**\r\n     * Return a plain object copy of the current ledger state.\r\n     * Keys are ids, values are signatures.\r\n     * @returns {{[id:string]: string}}\r\n     */\r\n    snapshot() {\r\n      const obj = {};\r\n      for (const [k, v] of ledger.entries()) {\r\n        obj[k] = v;\r\n      }\r\n      return obj;\r\n    }\r\n  };\r\n}\r\n","modules/coach-world-rts/clips.mjs":"// W1 clips: atlas normaliser (pilot rects/ms format + plan cw-rts-atlas/1), heading sectors, tick->frame.\r\nexport const TICK=42;\r\n// sector 0=N clockwise; heading 0..255 (0=N,64=E,128=S,192=W)\r\nexport const SECTORS=['N','NE','E','SE','S','SW','W','NW'];\r\nexport const headingOf=(dx,dy)=>((Math.round(Math.atan2(dx,-dy)/(2*Math.PI)*256)%256)+256)%256;\r\nexport const sectorOf=h=>Math.floor((h+16)/32)%8;\r\nexport function turnToward(cur,want){const d=((want-cur+12)%8)-4;return d===0?cur:(cur+(d>0?1:-1)+8)%8;} // ponytail: 180deg flips choose +1; fine\r\nconst DIRS={S:[0,0],SE:[1,0],E:[2,0],NE:[3,0],N:[4,0],NW:[3,1],W:[2,1],SW:[1,1]};\r\nexport const facingFor=(sector,dirs=DIRS)=>{const [stored,mirror]=dirs[SECTORS[sector]];return {stored,mirror:!!mirror};};\r\nexport function normalizeAtlas(j){\r\n const [cw,ch]=j.cell,clips={};\r\n if(j.actions){for(const [n,a] of Object.entries(j.actions))clips[n]={frames:a.frames,ticks:a.ms.map(m=>Math.max(1,Math.round(m/TICK))),rects:a.rects,strike:a.strike??(n==='work'?2:undefined)};}\r\n else for(const [n,c] of Object.entries(j.clips)){const ticks=c.ticks||Array(c.frames).fill(1);clips[n]={frames:c.frames,ticks:c.ticks?ticks:[],strideTiles:c.strideTiles,strike:c.strike,\r\n   rects:j.order.map((_,f)=>Array.from({length:c.frames},(__,i)=>[(f*c.frames+i)*cw,c.row*ch,cw,ch]))};}\r\n const dirs=j.dirs||Object.fromEntries(Object.entries(j.mirror||{}).map(([k,v])=>[k,[j.order.indexOf(v),1]]).concat(j.order.map((o,i)=>[o,[i,0]])));\r\n const units=j.units&&Object.fromEntries(Object.entries(j.units).map(([id,u])=>[id,{clips:normalizeAtlas({...j,units:undefined,actions:u.actions}).clips,strike:u.strike}])); // per-unit clips + strike points (faction sheets)\r\n return {units,faction:j.faction,unit:j.unit,cell:[cw,ch],anchor:j.anchor,teamEdges:j.teamEdges,teamRamps:j.teamRamps,carry:j.carry,fx:j.fx,dirs,clips,baseTint:j.baseTint||[150,90,200],maskDiv:j.maskDiv||j.maskScale&&Math.round(1/j.maskScale)||4,idle:j.idle||{bobPx:0,bobMs:1},image:j.image,mask:j.mask,stride:j.actions?.walk?.strideTiles??j.clips?.walk?.strideTiles??0.5};\r\n}\r\nexport const walkFrame=(distTiles,frames,stride=0.5)=>((Math.floor(distTiles/stride*frames)%frames)+frames)%frames;\r\n// frame of a tick-timed clip after `elapsed` ticks (loops)\r\nexport function clipFrame(clip,elapsed,loop=true){const total=clip.ticks.reduce((a,b)=>a+b,0);let t=loop?elapsed%total:Math.min(elapsed,total-1);for(let i=0;i<clip.frames;i++){if(t<clip.ticks[i])return i;t-=clip.ticks[i];}return clip.frames-1;}\r\n","modules/coach-world-rts/coach-motion.mjs":"// Ported 1:1 from creature/source/motion.ts samplePose (walk .9 s loop, idle 7 s loop). Units: bodyY/footY = fraction of body height, rotations = radians.\r\nexport const WALK_S=.9,IDLE_S=7,FADE_S=.22;\r\nconst blank=()=>({headX:0,headY:0,headZ:0,bodyX:0,bodyZ:0,bodyY:0,leftX:0,leftZ:0,rightX:0,rightZ:0,breath:0,footLX:0,footRX:0,footLY:0,footRY:0,footLZ:0,footRZ:0,scaleY:1,pitch:0});\r\nexport function walkPose(t){\r\n const s=Math.sin(t/WALK_S*Math.PI*2),p=blank();\r\n p.footLX=-.55*s;p.footRX=.55*s;p.footLZ=.18*s;p.footRZ=-.18*s;p.footLY=.16*Math.max(0,s);p.footRY=.16*Math.max(0,-s);\r\n p.bodyY=.05*Math.abs(s);p.bodyZ=.09*s;p.headZ=-.05*s;p.leftX=.45*s;p.rightX=-.45*s;p.leftZ=-.08;p.rightZ=.08;\r\n return p;\r\n}\r\nexport function idlePose(t){\r\n const u=t/IDLE_S,s=Math.sin(u*Math.PI*2),p=blank();\r\n p.headY=.045*s;p.headZ=.018*Math.sin(u*Math.PI*4);p.breath=.009*s;p.leftX=.02*s;p.rightX=-.015*s;\r\n return p;\r\n}\r\n// Four-legged units drop their body on the walk beat and never pitch forward.\r\nexport function squatPose(t){\r\n const s=Math.abs(Math.sin(t/WALK_S*Math.PI*2)),p=blank();\r\n p.bodyY=-.12*s;p.scaleY=1-.15*s;p.pitch=0;\r\n return p;\r\n}\r\n// scale = sprite height in px. Call after translating to the feet; bob/tilt/squash pivot on the feet.\r\nexport function applyPose2d(ctx,pose,scale){\r\n if(!pose)return;\r\n ctx.translate(0,-pose.bodyY*scale);ctx.rotate(pose.bodyZ);if(pose.scaleY!==1)ctx.scale(1,pose.scaleY);\r\n}\r\nexport const blendPose=(a,b,k)=>{const o={};for(const key in b)o[key]=a[key]+(b[key]-a[key])*k;return o;};\r\nconst POSES={walk:walkPose,idle:idlePose,squat:squatPose};\r\n// One mixer per unit: sample(name,t,now) crossfades FADE_S seconds from the previous clip's pose when the clip name changes.\r\nexport function createPoseMixer(fade=FADE_S){\r\n let name=null,from=null,at=0,last=null;\r\n return {sample(next,t,now){\r\n  const cur=POSES[next](t);\r\n  if(name!==null&&next!==name){from=last;at=now;}\r\n  name=next;\r\n  const k=from?Math.min(1,(now-at)/fade):1;\r\n  if(k>=1)from=null;\r\n  return last=from?blendPose(from,cur,k):cur;\r\n }};\r\n}\r\n","modules/coach-world-rts/coach-pedestals.mjs":"﻿import {CANONICAL_COACHES,PEDESTAL_SLOTS} from '../coach-world/coach-manifest.mjs';\nimport {BASIC_WORKER,FOUR_LEGGED_SOURCES,UNIT_BY_ID} from '../coach-world/unit-catalog.mjs';\nimport {STARTER_COACH_IDS} from '../../performance-catalog.mjs';\n\nconst STARTERS=new Set(STARTER_COACH_IDS);\nconst COACH_BY_ID=new Map(CANONICAL_COACHES.map(row=>[row.coachId,row]));\nconst ATLAS_BY_VISUAL=new Map();\nfor(const row of CANONICAL_COACHES){\n const unit=row.sourceVisualId===BASIC_WORKER.id?BASIC_WORKER:UNIT_BY_ID[row.sourceVisualId];\n if(unit?.atlasPath&&Array.isArray(unit.frame))ATLAS_BY_VISUAL.set(row.coachId,{atlasPath:unit.atlasPath,frame:[...unit.frame]});\n else{\n  const pet=FOUR_LEGGED_SOURCES.find(source=>`${source.atlasPath}#frame=${source.frame.join(',')}`===row.sourceVisualId);\n  if(pet)ATLAS_BY_VISUAL.set(row.coachId,{atlasPath:pet.atlasPath,frame:[...pet.frame]});\n }\n}\n\n/** Fixed manifest layout plus exact server-owned canonical access. This function never mutates base. */\nexport function projectCoachPedestals(base){\n const access=new Set(Array.isArray(base?.coachAccess)?base.coachAccess.filter(id=>typeof id==='string'&&COACH_BY_ID.has(id)):[]);\n return PEDESTAL_SLOTS.map(slot=>{\n  const coachId=slot.assignedCoachId,definition=coachId?COACH_BY_ID.get(coachId):null,earned=!!definition&&access.has(coachId);\n  return {slotId:slot.slotId,rail:slot.rail,railOrdinal:slot.railOrdinal,coordinate:{...slot.coordinate},coachId:earned?coachId:null,\n   assignedCoachId:coachId,canonicalLabel:earned?definition.canonicalLabel:null,earned,starter:earned&&STARTERS.has(coachId),visual:earned?ATLAS_BY_VISUAL.get(coachId)??null:null};\n });\n}\n\nconst PAD_SIZE=.92,PAD_HEIGHT=.16,REVEAL_MS=1500,DUST_COUNT=28;\nconst clamp=(n,a,b)=>Math.max(a,Math.min(b,n));\nconst seenHas=(seen,id)=>typeof seen==='function'?seen(id):seen?.has?.(id)===true;\nconst dimensions=(frame,scale)=>{const h=1.2*scale,w=h*frame[2]/frame[3],fit=Math.min(1,1.1*scale/w);return [w*fit,h*fit];};\nfunction uvFor(frame,width,height){\n const [x,y,w,h]=frame,u0=x/width,u1=(x+w)/width,v0=1-y/height,v1=1-(y+h)/height;\n return [u0,v0,u1,v0,u0,v1,u1,v1];\n}\nfunction hash(value){let h=2166136261;for(const ch of value){h^=ch.charCodeAt(0);h=Math.imul(h,16777619);}return h>>>0;}\n\n/**\n * Terrain-seated, atlas-backed coach statues for the home-world. Call update(t)\n * from the scene frame hook; t is its requestAnimationFrame timestamp in ms.\n */\nexport function createCoachPedestals(T,{getBase=()=>null,origin={x:0,y:0},scale=1,heightAt=()=>0,now=()=>performance.now(),onReveal=()=>{},onRevealed=()=>{},seenCoachIds=new Set(),reducedMotion=false,textureLoader=null}={}){\n if(!T?.Group||!T?.InstancedMesh||!T?.BoxGeometry||!T?.MeshStandardMaterial)throw new TypeError('Coach pedestals need Three.js mesh primitives.');\n if(typeof getBase!=='function'||typeof heightAt!=='function'||!Number.isFinite(scale)||scale<=0)throw new TypeError('Coach pedestals need a base source, terrain sampler and positive scale.');\n const group=new T.Group();group.name='coach-pedestals';\n const padGeometry=new T.BoxGeometry(PAD_SIZE*scale,PAD_HEIGHT*scale,PAD_SIZE*scale),padMaterial=new T.MeshStandardMaterial({color:0x77746d,roughness:.92,metalness:0});\n const pads=new T.InstancedMesh(padGeometry,padMaterial,PEDESTAL_SLOTS.length),dummy=new T.Object3D();pads.name='coach-pedestal-pads';pads.instanceMatrix?.setUsage?.(T.DynamicDrawUsage);group.add(pads);\n const statueBodyGeometry=new T.CylinderGeometry(.2*scale,.29*scale,.7*scale,6,1),statueHeadGeometry=new T.IcosahedronGeometry(.19*scale,0),statueMaterial=new T.MeshStandardMaterial({color:0xaaa69d,roughness:1,metalness:0});\n const atlasRecords=new Map(),statues=new Map(),sessionDone=new Set(),disposedTextures=new Set();let disposed=false,lastTime=null,current=null,projected=[],frameTime=0;\n const disposeTexture=texture=>{if(texture&&!disposedTextures.has(texture)){disposedTextures.add(texture);texture.dispose?.();}};\n const padY=PAD_HEIGHT*scale/2;\n function worldPoint(row){\n  const xTile=row.coordinate.xTile,yTile=row.coordinate.yTile;\n  const x=(xTile-(origin.x??0))*scale,z=(yTile-(origin.y??0))*scale;\n  const terrain=heightAt(xTile,yTile);return {x,z,y:Number.isFinite(terrain)?terrain:0,xTile,yTile};\n }\n function statueGroup(row){\n  const g=new T.Group();g.name=`coach-statue:${row.coachId}`;\n  const body=new T.Mesh(statueBodyGeometry,statueMaterial);body.position.y=.43*scale;g.add(body);\n  const head=new T.Mesh(statueHeadGeometry,statueMaterial);head.position.y=.98*scale;g.add(head);\n  return g;\n }\n function loadAtlas(path){\n  if(atlasRecords.has(path))return atlasRecords.get(path);\n  const record={path,texture:null,material:null,ready:false,failed:false,pending:false,statues:[]};atlasRecords.set(path,record);\n  requestAtlas(record);return record;\n }\n function requestAtlas(record){\n  if(disposed||record.pending||record.ready)return;record.pending=true;record.failed=false;\n  if(!textureLoader&&typeof T.TextureLoader!=='function'){record.pending=false;record.failed=true;for(const statue of record.statues)statue.visualFailed='texture-loader-unavailable';return;}\n  try{\n   const loader=textureLoader??new T.TextureLoader();\n   const texture=loader.load(record.path,loaded=>{\n    const tex=loaded??texture;\n    if(disposed){disposeTexture(tex);return;}\n    record.pending=false;\n    const image=tex.image,width=image?.naturalWidth??image?.width,height=image?.naturalHeight??image?.height;\n    if(!Number.isFinite(width)||!Number.isFinite(height)||width<=0||height<=0){record.failed=true;for(const statue of record.statues)statue.visualFailed='invalid-atlas-dimensions';return;}\n    tex.magFilter=T.NearestFilter;tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.colorSpace=T.SRGBColorSpace;tex.needsUpdate=true;\n    record.texture=tex;record.material=new T.MeshBasicMaterial({map:tex,color:0xaaa69d,transparent:true,alphaTest:.05,depthWrite:true,toneMapped:false,side:T.DoubleSide});\n    record.material.onBeforeCompile=shader=>{shader.fragmentShader=shader.fragmentShader.replace('#include <color_fragment>','#include <color_fragment>\\nfloat coachStoneLuma=dot(diffuseColor.rgb,vec3(0.299,0.587,0.114));diffuseColor.rgb=mix(diffuseColor.rgb,vec3(coachStoneLuma),0.9)*vec3(0.82,0.80,0.75);');};\n    record.material.customProgramCacheKey=()=> 'coach-pedestal-stone-v1';\n    for(const statue of record.statues){\n     if(attachAtlas(statue,record,width,height)){statue.visualReady=true;statue.visualFailed=null;for(const part of statue.fallbackParts)part.visible=false;}\n     else{statue.visualFailed='crop-out-of-bounds';statue.visualReady=false;}\n    }\n    record.ready=true;\n   },undefined,()=>{if(disposed){disposeTexture(texture);return;}record.pending=false;record.failed=true;for(const statue of record.statues){statue.visualReady=false;statue.visualFailed='atlas-load-failed';}});\n   record.texture=texture;\n  }catch{record.pending=false;record.failed=true;for(const statue of record.statues){statue.visualReady=false;statue.visualFailed='atlas-load-failed';}}\n }\n function attachAtlas(statue,record,width,height){\n  if(statue.atlasMesh||!record.material)return;\n  const frame=statue.row.visual?.frame;if(!frame||frame[0]<0||frame[1]<0||frame[0]+frame[2]>width||frame[1]+frame[3]>height)return false;\n  const [w,h]=dimensions(frame,scale),geometry=new T.PlaneGeometry(w,h),uv=uvFor(frame,width,height);\n  geometry.setAttribute('uv',new T.Float32BufferAttribute(uv,2));\n  const mesh=new T.Mesh(geometry,record.material);mesh.name=`coach-atlas:${statue.row.coachId}`;mesh.position.y=h/2+PAD_HEIGHT*scale/2;statue.atlasMesh=mesh;statue.group.add(mesh);statue.atlasGeometry=geometry;return true;\n }\n function makeStatue(row){\n  if(statues.has(row.coachId))return statues.get(row.coachId);\n  const g=statueGroup(row),p=worldPoint(row),facing=Math.atan2(-p.x,-p.z),statue={row,group:g,point:p,facing,atlasMesh:null,atlasGeometry:null,visualReady:!row.visual,visualFailed:null,visible:false,disposed:false,fallbackParts:[...g.children]};\n  g.position.set(p.x,p.y+padY,p.z);g.rotation.y=facing;g.visible=row.starter||seenHas(seenCoachIds,row.coachId);group.add(g);statues.set(row.coachId,statue);\n  if(row.visual?.atlasPath){const record=loadAtlas(row.visual.atlasPath);record.statues.push(statue);if(record.ready&&record.texture?.image){const image=record.texture.image;if(attachAtlas(statue,record,image.naturalWidth??image.width,image.naturalHeight??image.height)){statue.visualReady=true;for(const part of statue.fallbackParts)part.visible=false;}else statue.visualFailed='crop-out-of-bounds';}else if(record.failed)statue.visualFailed='atlas-load-failed';}\n  else statue.visualReady=true;\n  return statue;\n }\n function refreshPads(){\n  PEDESTAL_SLOTS.forEach((slot,index)=>{const point=worldPoint(slot);dummy.position.set(point.x,point.y+padY,point.z);dummy.rotation.set(0,0,0);dummy.scale.set(1,1,1);dummy.updateMatrix();pads.setMatrixAt(index,dummy.matrix);});\n  pads.instanceMatrix.needsUpdate=true;\n }\n function reconcile(){\n  const next=projectCoachPedestals(getBase());projected=next;\n  const earned=new Set(next.filter(row=>row.earned).map(row=>row.coachId));\n  if(current&&!earned.has(current.row.coachId)){current.dust?.parent?.remove(current.dust);current.dust?.geometry?.dispose?.();current.dust?.material?.dispose?.();current=null;}\n  for(const [id,statue] of statues)if(!earned.has(id)){\n   statue.disposed=true;const record=statue.row.visual?.atlasPath&&atlasRecords.get(statue.row.visual.atlasPath);if(record)record.statues=record.statues.filter(item=>item!==statue);\n   group.remove(statue.group);statue.atlasGeometry?.dispose?.();statues.delete(id);\n  }\n  for(const row of next)if(row.earned)makeStatue(row);\n }\n function currentReady(){return !!current&&statues.get(current.row.coachId)?.visualReady===true;}\n function finishReveal(){\n  if(!current||!currentReady())return false;\n  const statue=statues.get(current.row.coachId);if(!statue)return false;statue.visible=true;statue.group.visible=true;\n  statue.group.scale.set(1,1,1);current.dust?.parent?.remove(current.dust);current.dust?.geometry?.dispose?.();current.dust?.material?.dispose?.();\n  const row=current.row;sessionDone.add(row.coachId);current=null;try{onRevealed(row);}catch{}return true;\n }\n function startReveal(){\n  if(current||disposed)return;\n  const next=projected.find(row=>{const statue=statues.get(row.coachId);return row.earned&&!row.starter&&!seenHas(seenCoachIds,row.coachId)&&!sessionDone.has(row.coachId)&&statue?.visualReady===true;});\n  if(!next)return;\n  const statue=statues.get(next.coachId);if(!statue)return;\n  current={row:next,elapsed:0,progress:0,dust:null};statue.group.visible=true;statue.visible=true;statue.group.scale.set(.02,.02,.02);\n  try{onReveal(next);}catch{}\n  if(reducedMotion){current.progress=1;finishReveal();return;}\n  if(T.Points&&T.BufferGeometry&&T.Float32BufferAttribute&&T.PointsMaterial){\n   const positions=new Float32Array(DUST_COUNT*3),seed=hash(next.coachId);\n   for(let i=0;i<DUST_COUNT;i++){const a=((seed+i*2654435761)>>>0)/4294967296*Math.PI*2,r=(.2+((seed>>>(i%24))&255)/255*.45)*scale;positions[i*3]=statue.point.x+Math.cos(a)*r;positions[i*3+1]=statue.point.y+padY+(i%5)*.07*scale;positions[i*3+2]=statue.point.z+Math.sin(a)*r;}\n   const geometry=new T.BufferGeometry();geometry.setAttribute('position',new T.Float32BufferAttribute(positions,3));\n   const material=new T.PointsMaterial({color:0xd6d0c3,size:.08*scale,transparent:true,opacity:.9,depthWrite:false});\n   current.dust=new T.Points(geometry,material);current.dust.name=`coach-reveal-dust:${next.coachId}`;group.add(current.dust);\n  }\n }\n function status(){\n  const errors=[...statues.values()].filter(s=>s.visualFailed).map(s=>({coachId:s.row.coachId,atlasPath:s.row.visual?.atlasPath??null,error:s.visualFailed}));\n  return {earnedCount:projected.filter(row=>row.earned).length,slotCount:PEDESTAL_SLOTS.length,loadedCount:[...statues.values()].filter(s=>s.visualReady).length,failedCount:errors.length,errors,\n   currentReveal:current?{coachId:current.row.coachId}:null};\n }\n function update(t=now()){\n  if(disposed)return status();\n  if(!Number.isFinite(t))t=now();\n  const delta=lastTime===null?0:clamp(t-lastTime,0,100);lastTime=t;frameTime=t;\n  reconcile();\n  if(current){\n   current.elapsed=Math.min(REVEAL_MS,current.elapsed+delta);current.progress=REVEAL_MS?current.elapsed/REVEAL_MS:1;\n   const statue=statues.get(current.row.coachId),p=current.progress;\n   if(statue){const scaleNow=.02+.98*(1-Math.pow(1-p,3));statue.group.scale.set(scaleNow,scaleNow,scaleNow);}\n   if(current.dust){current.dust.material.opacity=.9*(1-p);const attr=current.dust.geometry.attributes.position,positions=attr.array;\n    for(let i=0;i<DUST_COUNT;i++)positions[i*3+1]+=.0015*scale*delta;attr.needsUpdate=true;}\n   if(p>=1)finishReveal();\n  }\n  startReveal();return status();\n }\n function skipReveal(){if(!current||!currentReady())return false;current.elapsed=REVEAL_MS;current.progress=1;return finishReveal();}\n reconcile();refreshPads();startReveal();\n return {group,update,status,skipReveal,dispose(){\n  if(disposed)return;disposed=true;\n  if(current?.dust){group.remove(current.dust);current.dust.geometry.dispose?.();current.dust.material.dispose?.();}current=null;\n  for(const statue of statues.values()){statue.atlasGeometry?.dispose?.();group.remove(statue.group);}statues.clear();\n  for(const record of atlasRecords.values()){record.material?.dispose?.();disposeTexture(record.texture);}\n  atlasRecords.clear();padGeometry.dispose();padMaterial.dispose();statueBodyGeometry.dispose();statueHeadGeometry.dispose();statueMaterial.dispose();group.removeFromParent?.();group.clear?.();\n }};\n}\r\n","modules/coach-world-rts/construction.mjs":"// W3 construction sequences per race family. Progress is derived ONLY from serverNow() + base job snapshots (no wall-clock timers):\r\n// each step diffs jobs per building -> appeared: start | start/due/builder changed: restart | vanished at targetLevel: completion | vanished otherwise: cancel.\r\n// zero-duration jobs (dueAt<=startedAt) emit completion only. Original pixel art, drawn in code (provenance: construction.mjs).\r\nimport {MAX_LEVEL} from '../coach-world/catalog.mjs';\r\nexport const FLASH_TICKS=1,WARP_SWEEP_TICKS=20,MOUND_POSES=8,MOUND_TICKS_PER_POSE=2,WELD_EVERY=[10,12];\r\nexport const FAMILY={'occupied-worker':'occupied','seeded-warp':'warp','consumed-seed':'mound'};\r\nexport const progressOf=(job,now)=>{const d=job.dueAt-job.startedAt;return d>0?Math.max(0,Math.min(1,(now-job.startedAt)/d)):1;};\r\n// plan stages: stakes at 0, frame at 1/3, shell at 2/3, done at 1\r\nexport const stageOf=p=>p>=1?'done':p>=2/3?'shell':p>=1/3?'frame':'stakes';\r\nconst builderVisual=job=>{const m=/^crew-(\\d+)$/.exec(job?.builderId||'');return m?+m[1]-1:null;};\r\nconst kindOf=b=>['hq','hall','production','expedition'].includes(b.kind)?b.kind:b.kind==='care'?'shop':'building';\r\nconst mk=(w,h)=>typeof OffscreenCanvas!=='undefined'?new OffscreenCanvas(w,h):Object.assign(document.createElement('canvas'),{width:w,height:h});\r\nconst hash=s=>{let h=7;for(const c of String(s))h=(h*31+c.charCodeAt(0))>>>0;return h;};\r\n// default sprite transforms (cached by sprite object): 1px outline of alpha>=.5, 60%-desaturated copy, white silhouette\r\nexport function canvasArt(){\r\n const cache=new WeakMap(),pix=(s,fn)=>{const c=mk(s.width,s.height),o=c.getContext('2d');o.drawImage(s,0,0);const d=o.getImageData(0,0,s.width,s.height);fn(d.data,s.width,s.height);o.putImageData(d,0,0);return c;};\r\n const memo=(s,k,fn)=>{let m=cache.get(s);if(!m)cache.set(s,m={});return m[k]||(m[k]=fn());};\r\n return {\r\n  outline:(s,color='#ffe2aa')=>memo(s,'o'+color,()=>pix(s,(p,w,h)=>{const a=p.slice(),on=(x,y)=>x>=0&&y>=0&&x<w&&y<h&&a[(y*w+x)*4+3]>=128,[r,g,b]=[1,3,5].map(i=>parseInt(color.slice(i,i+2),16));\r\n   for(let y=0;y<h;y++)for(let x=0;x<w;x++){const i=(y*w+x)*4,edge=on(x,y)&&!(on(x-1,y)&&on(x+1,y)&&on(x,y-1)&&on(x,y+1));p[i]=r;p[i+1]=g;p[i+2]=b;p[i+3]=edge?255:0;}})),\r\n  desat:s=>memo(s,'d',()=>pix(s,p=>{for(let i=0;i<p.length;i+=4){const l=.3*p[i]+.59*p[i+1]+.11*p[i+2];for(let c=0;c<3;c++)p[i+c]=p[i+c]*.4+l*.6;}})),\r\n  bounds:s=>memo(s,'b',()=>{const d=mk(s.width,s.height),o=d.getContext('2d');o.drawImage(s,0,0);const p=o.getImageData(0,0,s.width,s.height).data;let y0=s.height,y1=0;for(let y=0;y<s.height;y++)for(let x=0;x<s.width;x++)if(p[(y*s.width+x)*4+3]>=128){if(y<y0)y0=y;y1=y;}return y1>=y0?[y0,y1+1]:[0,s.height];}),\r\n  white:s=>memo(s,'w',()=>pix(s,p=>{for(let i=0;i<p.length;i+=4){p[i]=p[i+1]=p[i+2]=255;}})),\r\n };\r\n}\r\n\r\nexport function createConstruction({rts={},sfx=null,ownerId=()=>null,reduced=()=>false,getSprite=null,art=null,onComplete=null}={}){\r\n const c={effects:new Map(),done:new Map(),seen:new Set(),listeners:onComplete?[onComplete]:[],now:0,hydrated:false,sfx,getSprite,art,events:[]};\r\n const tick=()=>rts.clock?.ticks??0;\r\n if(!getSprite&&(typeof document!=='undefined'||typeof OffscreenCanvas!=='undefined')){import('../coach-world/pixel-art.mjs').then(m=>{if(!c.getSprite)c.getSprite=m.getSpriteCanvas;}).catch(()=>{});}\r\n const A=()=>c.art||(c.art=canvasArt());\r\n c.onComplete=fn=>{c.listeners.push(fn);return()=>{c.listeners=c.listeners.filter(l=>l!==fn);};};\r\n const builderEv=(job,on)=>{const id=builderVisual(job);if(id!=null)rts.onEvent?.({type:'build',unitId:id,on});};\r\n function complete(b,job,family){\r\n  const id=`${ownerId()}:${b.id}:${job.targetLevel}:${job.dueAt}`;\r\n  if(c.seen.has(id))return null;c.seen.add(id);if(c.seen.size>64)c.seen.delete(c.seen.values().next().value);\r\n  const ev={id,buildingId:b.id,level:job.targetLevel,previousLevel:job.targetLevel-1,family,faction:job.track||b.track};\r\n  c.events.push(ev);if(c.events.length>16)c.events.shift();\r\n  c.done.set(b.id,{at:tick(),family,level:job.targetLevel,burst:false,ev});\r\n  if(c.hydrated)c.sfx?.(job.targetLevel>1?'level-up':'build-complete'); // new build -> 2-note fifth, upgrade -> arpeggio\r\n  for(const l of c.listeners){try{l(ev);}catch{}}\r\n  return ev;\r\n }\r\n function end(id,b){ // job gone: completion if the building reached targetLevel, else cancel every effect\r\n  const e=c.effects.get(id);if(!e)return;c.effects.delete(id);builderEv(e.job,false);\r\n  if(b&&(b.level??0)>=e.job.targetLevel)complete(b,e.job,e.family);\r\n }\r\n c.step=(base,now,n=1)=>{\r\n  if(Number.isFinite(now))c.now=now;\r\n  const bs=base?.buildings||{};\r\n  for(const id of [...c.effects.keys()]){const b=bs[id];if(!b||!b.job||`${b.job.startedAt}:${b.job.dueAt}:${b.job.builderId}:${b.job.targetLevel}`!==c.effects.get(id).key)end(id,b);}\r\n  for(const b of Object.values(bs)){\r\n   const j=b.job;if(!j||!Number.isFinite(j.startedAt)||!Number.isFinite(j.dueAt))continue;\r\n   const family=FAMILY[j.policy]||'occupied';\r\n   if(j.dueAt<=j.startedAt){complete({...b,job:null},j,family);continue;} // zero-duration: completion only\r\n   if(c.effects.has(b.id))continue;\r\n   const t=tick();\r\n   c.effects.set(b.id,{key:`${j.startedAt}:${j.dueAt}:${j.builderId}:${j.targetLevel}`,job:j,family,bStart:t,nextWeld:t+WELD_EVERY[0],welds:0,spark:false,level:b.level??0,kind:b.kind});\r\n   if(c.hydrated)c.sfx?.('build-start');\r\n   builderEv(j,true);\r\n  }\r\n  c.hydrated=true;\r\n  const t=tick();\r\n  for(const e of c.effects.values()){\r\n   if(e.family==='occupied'){\r\n    if(t>=e.nextWeld){e.spark=true;e.welds++;e.nextWeld=t+WELD_EVERY[e.welds%2];}\r\n    const v=builderVisual(e.job);if(v!=null&&t%10===0&&!rts.isWorking?.(v))builderEv(e.job,true); // re-assert Build after walking to the site\r\n   }\r\n  }\r\n  for(const [id,d] of c.done)if(t-d.at>FLASH_TICKS)c.done.delete(id);\r\n };\r\n const geom=(b,x,y,size)=>{const w=size*(b.w||3),h=size*(b.h||3),pixels=w<48?32:64*Math.max(1,Math.round(w/64));return {x,y,w,h,pixels,left:Math.round(x+w/2-pixels/2),top:Math.round(y+h-pixels),cx:Math.round(x+w/2),cy:Math.round(y+h*.8)};};\r\n const label=(ctx,b,f,g,level)=>{ctx.font='bold 8px monospace';const t=level===0?'SITE':`${level}/${MAX_LEVEL}`,w=ctx.measureText(t).width+6;ctx.fillStyle='#09171ee8';ctx.fillRect(Math.round(g.x+g.w/2-w/2),Math.round(g.y+g.h-3),w,11);ctx.fillStyle=level===0?'#b7c8c5':f.accent;ctx.textAlign='center';ctx.fillText(t,Math.round(g.x+g.w/2),Math.round(g.y+g.h+5));ctx.textAlign='start';};\r\n function stakes(ctx,g,f){ // 4 dark 2x2 corner stakes + dotted outline\r\n  const x0=Math.round(g.x),y0=Math.round(g.y+g.h-g.pixels*.45),x1=Math.round(g.x+g.w)-2,y1=Math.round(g.y+g.h)-2;\r\n  ctx.fillStyle=f.accent;ctx.globalAlpha=.7;for(let i=x0+4;i<x1-2;i+=4){ctx.fillRect(i,y0,1,1);ctx.fillRect(i,y1,1,1);}for(let j=y0+4;j<y1-2;j+=4){ctx.fillRect(x0,j,1,1);ctx.fillRect(x1,j,1,1);}ctx.globalAlpha=1;\r\n  ctx.fillStyle='#1a1410';for(const [px,py] of [[x0,y0],[x1,y0],[x0,y1],[x1,y1]])ctx.fillRect(px,py,2,2);\r\n }\r\n function scaffold(ctx,g,f,t){ // 2 diagonal scaffold lines (1px steps) over the sprite box\r\n  ctx.fillStyle=f.accent;ctx.globalAlpha=.6;const s=g.pixels;for(let i=0;i<s;i+=2){ctx.fillRect(g.left+i,g.top+s-1-i,1,1);ctx.fillRect(g.left+s-1-i,g.top+s-1-i,1,1);}ctx.globalAlpha=1;\r\n }\r\n function creep(ctx,g,f,p,seed){ // organic ground spread: seeded dots inside an ellipse that grows with progress\r\n  const rx=(g.w/2+6)*(.25+.75*p),ry=rx*.38;let h=seed;ctx.fillStyle=f.dark;ctx.globalAlpha=.55;\r\n  for(let i=0,n=Math.floor(6+p*44);i<n;i++){h=(h*1103515245+12345)>>>0;const a=(h&1023)/1023*Math.PI*2,r=Math.sqrt(((h>>10)&1023)/1023);ctx.fillRect(Math.round(g.cx+Math.cos(a)*r*rx),Math.round(g.cy-2+Math.sin(a)*r*ry),2,1+(h>>20&1));}\r\n  ctx.fillStyle=f.color;ctx.globalAlpha=.35;for(let i=0;i<12;i++){h=(h*1103515245+12345)>>>0;const a=(h&1023)/1023*Math.PI*2;ctx.fillRect(Math.round(g.cx+Math.cos(a)*rx*.9),Math.round(g.cy-2+Math.sin(a)*ry*.9),1,1);}\r\n  ctx.globalAlpha=1;\r\n }\r\n function mound(ctx,g,f,t,scale){ // pulsing seed mound, 8 poses x 2 ticks\r\n  const pose=Math.floor(t/MOUND_TICKS_PER_POSE)%MOUND_POSES,k=1+(reduced()?0:.1*Math.sin(pose/MOUND_POSES*Math.PI*2)),r=g.pixels*.26*scale*k;\r\n  ctx.fillStyle=f.dark;ctx.beginPath();ctx.ellipse(g.cx,g.cy-r*.25,r,r*.62,0,0,Math.PI*2);ctx.fill();\r\n  ctx.fillStyle=f.color;ctx.beginPath();ctx.ellipse(g.cx,g.cy-r*.45,r*.6,r*.34,0,0,Math.PI*2);ctx.fill();\r\n  ctx.fillStyle=f.accent;ctx.fillRect(g.cx-1,Math.round(g.cy-r*.55),2,2);\r\n }\r\n function resonator(ctx,g,f,t){ // planted diamond, pulses\r\n  const s=3+(Math.floor(t/4)%2);ctx.fillStyle=f.dark;ctx.fillRect(g.cx-1,g.cy-8,2,8);ctx.fillStyle=f.accent;ctx.beginPath();ctx.moveTo(g.cx,g.cy-8-s);ctx.lineTo(g.cx+s,g.cy-8);ctx.lineTo(g.cx,g.cy-8+s);ctx.lineTo(g.cx-s,g.cy-8);ctx.fill();\r\n }\r\n function drawEffect(ctx,b,f,g,e){\r\n  const t=tick()-e.bStart,p=progressOf(e.job,c.now),stage=stageOf(p),a=c.art||A(),lvl=Math.max(0,Math.min(4,e.job.targetLevel-1)),kind=kindOf(b);\r\n  const baseKind=(e.level??0)===0?'foundation':kind,baseLvl=(e.level??0)===0?0:Math.max(0,Math.min(4,e.level-1));\r\n  const target=c.getSprite(f.id,kind,lvl);\r\n  ctx.imageSmoothingEnabled=false;\r\n  ctx.drawImage(c.getSprite(f.id,baseKind,baseLvl),g.left,g.top,g.pixels,g.pixels);\r\n  if(e.family==='mound')creep(ctx,g,f,p,hash(b.id));\r\n  if(e.family==='occupied'){\r\n   stakes(ctx,g,f);\r\n   if(stage!=='stakes'){ctx.drawImage(a.outline(target,f.accent),g.left,g.top,g.pixels,g.pixels);scaffold(ctx,g,f,t);}\r\n   if(stage==='shell'||stage==='done'){ctx.globalAlpha=.6;ctx.drawImage(a.desat(target),g.left,g.top,g.pixels,g.pixels);ctx.globalAlpha=1;}\r\n   if(e.spark){e.spark=false;rts.fx?.spark(g.cx+((e.welds%3)-1)*g.pixels*.15,g.top+g.pixels*.55,reduced()?1:3);}\r\n  }else if(e.family==='warp'){\r\n   resonator(ctx,g,f,t);\r\n   const H=target.height||g.pixels,k=g.pixels/H,[y0,y1]=a.bounds?.(target)||[0,H],rh=Math.round((y1-y0)*p); // reveal bottom-up inside the sprite's own content band\r\n   if(rh>0){const sy=y1-rh;ctx.globalAlpha=.85;ctx.drawImage(target,0,sy,target.width||H,rh,g.left,Math.round(g.top+sy*k),g.pixels,Math.max(1,Math.round(rh*k)));ctx.globalAlpha=1;\r\n    const sw=(t%WARP_SWEEP_TICKS)/WARP_SWEEP_TICKS,ly=Math.round(g.top+(y1-rh*(reduced()?1:sw))*k);ctx.fillStyle=f.accent;ctx.globalAlpha=.8;ctx.fillRect(g.left+2,ly,g.pixels-4,1);ctx.globalAlpha=.3;ctx.fillRect(g.left+2,ly+1,g.pixels-4,2);ctx.globalAlpha=1;}\r\n  }else{ // mound: stage swaps stakes -> mound, frame -> outline emerging, shell -> desaturated body\r\n   mound(ctx,g,f,t,stage==='stakes'?1:stage==='frame'?.8:.55);\r\n   if(stage==='frame'){ctx.globalAlpha=.7;ctx.drawImage(a.outline(target,f.accent),g.left,g.top,g.pixels,g.pixels);ctx.globalAlpha=1;}\r\n   if(stage==='shell'||stage==='done'){ctx.globalAlpha=.6;ctx.drawImage(a.desat(target),g.left,g.top,g.pixels,g.pixels);ctx.globalAlpha=1;}\r\n  }\r\n  label(ctx,b,f,g,e.level??0);\r\n }\r\n function drawDone(ctx,b,f,g,d){\r\n  const kind=kindOf(b),spr=c.getSprite(f.id,kind,Math.max(0,Math.min(4,d.level-1)));\r\n  ctx.imageSmoothingEnabled=false;ctx.drawImage(spr,g.left,g.top,g.pixels,g.pixels);\r\n  ctx.globalAlpha=.85;ctx.drawImage((c.art||A()).white(spr),g.left,g.top,g.pixels,g.pixels);ctx.globalAlpha=1; // 1-tick white completion flash\r\n  if(!d.burst){d.burst=true;rts.fx?.spark(g.cx,g.top+g.pixels*.5,d.family==='mound'?8:d.family==='warp'?5:3);} // spawn burst\r\n  label(ctx,b,f,g,d.level);\r\n }\r\n // seam: rts.drawBuilding(ctx,b,f,x,y,size) -> true when handled; false falls back to the legacy sprite\r\n c.drawBuilding=(ctx,b,f,x,y,size)=>{\r\n  const e=c.effects.get(b.id),d=!e&&c.done.get(b.id);if((!e&&!d)||!c.getSprite)return false;\r\n  const g=geom(b,x,y,size);\r\n  ctx.save();try{e?drawEffect(ctx,b,f,g,e):drawDone(ctx,b,f,g,d);}finally{ctx.restore();}\r\n  return true;\r\n };\r\n c.dispose=()=>{for(const e of c.effects.values())builderEv(e.job,false);c.effects.clear();c.done.clear();c.listeners.length=0;};\r\n return c;\r\n}\r\n","modules/coach-world-rts/crew-billboards.mjs":"// W8a living base: the atlas workers as camera-facing pixel billboards, driven by the same runtime state (getUnitDots) the 2D map uses.\r\n// Facing is relative to the viewer (a unit walking away shows its back). Stationary near the ore node = work clip + strike spark.\r\nimport {headingOf,sectorOf,facingFor,walkFrame,clipFrame,TICK,SECTORS} from './clips.mjs';\r\nimport {paintShip,shipFrame,SHIP_STATES,SHIP_CLIP_MS} from './ship-roster.mjs';\r\nimport {placeNode} from './mining.mjs';\r\nimport {lookFor,FACTION_AURA} from './unit-levels.mjs';\r\nimport {auraTextures} from './aura-sprites.mjs';\r\nimport {createTrail3d} from './unit-trail.mjs';\r\nimport {shadow} from './fx.mjs';\r\nimport {shadeSprite} from './sprite-shade.mjs'; // V8: time-of-day tint + fog (one wrapped line below)\r\n\r\n// Soft faction-colour glow (generated, not art) behind level 2+ units.\r\nlet glowTex=null;\r\nconst glowTexture=T=>{if(glowTex)return glowTex;const c=document.createElement('canvas');c.width=c.height=32;const x=c.getContext('2d'),g=x.createRadialGradient(16,16,0,16,16,16);g.addColorStop(0,'#fff');g.addColorStop(1,'#0000');x.fillStyle=g;x.fillRect(0,0,32,32);glowTex=new T.CanvasTexture(c);glowTex.colorSpace=T.SRGBColorSpace;return glowTex;};\r\n\r\nconst W=48,H=48,AX=24,AY=38,AIR_LIFT=2.5; // canvas big enough for the 28x40 cell plus the spark; anchor = feet\r\nconst sum=c=>c.ticks.reduce((a,b)=>a+b,0);\r\n// V1-C4 selection marks: a flat green ring (1.2x footprint, ~2px stroke) under each selected unit and a 24x4 green/red HP bar above it.\r\nconst RING_R=17,BAR_LIFT=7; // ring outer radius in sprite px (cell half-width 14 x 1.2); bar sits 7 bar-heights above the feet, just over the head\r\nexport function createCrewBillboards(T,{units,getSheet,getFactionSheet,getBase,scale,origin,unitHeight=scale*1.05,workRadius=2.6,isWorking=null,poseFor=null,now=()=>performance.now(),heightFor=null,showLooks=()=>true,selectedIds=null}){\r\n // heightFor(u) -> size multiplier replacing u.scale (AR passes ar-scale crewHeightFor: real metres vs the player); base view passes nothing.\r\n // selectedIds() -> the selected units (objects with instanceId); base view passes sceneMode selection, others pass nothing.\r\n const group=new T.Group(),st=new Map();group.name='rts-crew';let node=null,nodeAt=0;\r\n const pw=unitHeight/40,mult=u=>heightFor?heightFor(u):(u.scale||1);\r\n const ringGeo=new T.RingGeometry(.88,1,32),ringMat=new T.MeshBasicMaterial({color:0x54ff7c,side:T.DoubleSide,fog:false,toneMapped:false,depthTest:false,transparent:true});\r\n function make(){\r\n  const canvas=document.createElement('canvas');canvas.width=W;canvas.height=H;const tex=new T.CanvasTexture(canvas);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.colorSpace=T.SRGBColorSpace;\r\n  const mat=shadeSprite(new T.SpriteMaterial({map:tex,transparent:true,alphaTest:.06,toneMapped:false})),sprite=new T.Sprite(mat);sprite.center.set(AX/W,(H-AY)/H);sprite.scale.set(W*pw,H*pw,1);group.add(sprite);\r\n  return {sprite,canvas,ctx:canvas.getContext('2d'),tex,mat,x:null,y:null,lastMove:-1e9,dist:0,key:'',since:0,mode:''};\r\n }\r\n // WS-K: a dot with {sheet,source} uses its faction body sheet when that sheet is loaded and has the unit; otherwise the myr5 sheet (current art).\r\n function pick(u,base){const fs=u.sheet&&getFactionSheet?.(u.sheet),ud=fs?.atlas?.units?.[u.source];return ud?{sheet:fs,clips:ud.clips,strike:ud.strike}:{sheet:base,clips:base?.atlas?.clips,strike:null};}\r\n function draw(s,sheet,color,name,sector,tick,clips,strikes,elapsed=null){\r\n  const A=sheet.atlas,dye=sheet.dye(color);if(!dye)return;\r\n  const clip=clips[name]||clips.idle,{stored,mirror}=facingFor(sector,A.dirs),since=elapsed==null?tick-s.since:Math.max(0,elapsed/TICK);\r\n  const fr=name==='walk'?walkFrame(s.dist,clip.frames,A.stride):clip.frames>1?clipFrame(clip,since,['idle','work'].includes(name)):0;\r\n  const sp=A.fx?.spark;let spark=-1;\r\n  if(name==='work'&&sp&&sheet.fx){const el=((since%sum(clip))*TICK)-sp.startMs;let acc=0;if(el>=0)for(let k=0;k<sp.ms.length;k++){acc+=sp.ms[k];if(el<acc){spark=k;break;}}}\r\n  const key=`${A.unit}|${name}|${stored}|${mirror}|${fr}|${spark}|${color}`;if(key===s.key)return;s.key=key;\r\n  const c=s.ctx,[rx,ry,rw,rh]=clip.rects[stored][fr];c.clearRect(0,0,W,H);c.imageSmoothingEnabled=false;shadow(c,AX,AY+4,30,'#000d');c.save();c.translate(AX,AY);if(mirror)c.scale(-1,1);\r\n  c.drawImage(dye,rx,ry,rw,rh,-A.anchor[0],-A.anchor[1],rw,rh);\r\n  if(spark>=0){const [px,py]=(strikes||sp.strike)[stored],[a,b,w,h]=sp.rects[spark];c.drawImage(sheet.fx,a,b,w,h,px-A.anchor[0]-sp.center[0],py-A.anchor[1]-sp.center[1],w,h);}\r\n  c.restore();s.tex.needsUpdate=true;\r\n }\r\n // Level looks (unit-levels lookFor): 2 glow, 3 aura behind the unit, 4 neon ribbon trail, 5 second aura. Sprites hang off the unit's feet via `center`, because base3d re-snaps every child's y to the terrain each frame (the trail mesh sits at the map origin, so its points subtract that snapped y).\r\n function fx(s,key,make){return s[key]||(s[key]=make());}\r\n function layer(s,key,tex,size,color,order){\r\n  const sp=fx(s,key,()=>{const m=new T.SpriteMaterial({transparent:true,blending:T.AdditiveBlending,depthWrite:false,toneMapped:false,fog:false,color}),q=new T.Sprite(m);q.center.set(.5,.3);q.renderOrder=order;group.add(q);return q;});\r\n  sp.visible=true;sp.position.x=s.sprite.position.x;sp.position.z=s.sprite.position.z;sp.scale.set(size,size,1);\r\n  if(tex&&sp.material.map!==tex){sp.material.map=tex;sp.material.needsUpdate=true;}\r\n  return sp;\r\n }\r\n function looks(s,u,wx,wz,moving,tick){\r\n  const hide=k=>{if(s[k])s[k].visible=false;};\r\n  if(!showLooks()){hide('glow');hide('aura');hide('aura2');if(s.trail)s.trail.mesh.visible=false;return;}if(s.trail)s.trail.mesh.visible=true; // perf LOD (base view zoomed out): sprite only\r\n  const look=lookFor(u.level||1),H=unitHeight*mult(u);\r\n  if(look.tint&&!look.aura)layer(s,'glow',glowTexture(T),H*1.3,u.color,-2).material.opacity=.5;else hide('glow');\r\n  const fr=Math.floor(tick/1);\r\n  const a=look.aura&&auraTextures(T,FACTION_AURA[u.faction]);\r\n  if(a?.length)layer(s,'aura',a[fr%a.length],H*1.9,0xffffff,-1);else hide('aura');\r\n  const b=look.second&&auraTextures(T,look.second);\r\n  if(b?.length)layer(s,'aura2',b[Math.floor(fr*.7)%b.length],H*2.3,0xffffff,-1);else hide('aura2');\r\n  if(look.trail){\r\n   const tr=fx(s,'trail',()=>{const t3=createTrail3d(T,{color:u.color,width:H*.35});group.add(t3.mesh);return t3;});\r\n   tr.push({x:wx,y:s.sprite.position.y-tr.mesh.position.y+H*.12,z:wz},moving?.2*scale/3:-1);\r\n  }else if(s.trail){s.trail.dispose();s.trail=null;}\r\n }\r\n function dropLooks(s){for(const k of ['glow','aura','aura2']){if(s[k]){group.remove(s[k]);s[k].material.dispose();}}s.trail?.dispose();\r\n  if(s.ring)group.remove(s.ring); // shared geometry/material, disposed once in dispose()\r\n  if(s.bar){group.remove(s.bar);s.bar.material.map.dispose();s.bar.material.dispose();}}\r\n // Selection marks: made once per unit, then only shown/hidden, moved, and the 24x4 bar redrawn when its fill width changes (never per frame otherwise).\r\n function marks(s,u,wx,wz,k,on){\r\n  if(!on){if(s.ring)s.ring.visible=false;if(s.bar)s.bar.visible=false;return;}\r\n  if(!s.ring){s.ring=new T.Mesh(ringGeo,ringMat);s.ring.rotation.x=-Math.PI/2;s.ring.renderOrder=3;group.add(s.ring);}\r\n  s.ring.visible=true;s.ring.position.set(wx,0,wz);s.ring.scale.set(RING_R*k,RING_R*k*.6,1);\r\n  if(!s.bar){const canvas=document.createElement('canvas');canvas.width=24;canvas.height=4;const tex=new T.CanvasTexture(canvas);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;\r\n   s.bar=new T.Sprite(new T.SpriteMaterial({map:tex,transparent:true,depthTest:false,toneMapped:false,fog:false}));s.bar.center.set(.5,-BAR_LIFT);s.bar.renderOrder=3;s.barCtx={c:canvas.getContext('2d'),tex,fill:-1};group.add(s.bar);}\r\n  s.bar.visible=true;s.bar.position.set(wx,0,wz);s.bar.scale.set(24*k,4*k,1);\r\n  const frac=Number.isFinite(u.hp)&&u.maxHp>0?Math.max(0,Math.min(1,u.hp/u.maxHp)):1,fill=Math.round(frac*22),b=s.barCtx;\r\n  if(fill!==b.fill){b.fill=fill;const c=b.c;c.fillStyle='#000';c.fillRect(0,0,24,4);c.fillStyle='#0b0d10';c.fillRect(1,1,22,2);c.fillStyle=frac<.33?'#e0382a':'#2fdc4a';c.fillRect(1,1,fill,2);b.tex.needsUpdate=true;}\r\n }\r\n const isSel=(sel,u)=>{if(sel)for(const x of sel)if(x.instanceId===u.id||x.instanceId===u.instanceId||x.selectionId===u.id)return true;return false;};\r\n return {group,\r\n  update(){\r\n   const sheet=getSheet?.(),t=now(),tick=t/TICK,list=(units?.()||[]).filter(u=>u.worker),sel=selectedIds?.()||null;\r\n   if(t-nodeAt>1000){nodeAt=t;try{node=placeNode(getBase())?.node||null;}catch{node=null;}}\r\n   const seen=new Set();\r\n   for(const u of list){\r\n    seen.add(u.id);let s=st.get(u.id);if(!s){s=make();st.set(u.id,s);}\r\n    const dx=u.x-(s.x??u.x),dy=u.y-(s.y??u.y),d=Math.hypot(dx,dy);\r\n    if(d>1e-4){s.dist+=d;s.heading=headingOf(dx,dy);s.lastMove=t;}\r\n    s.x=u.x;s.y=u.y;{const k=pw*mult(u);s.sprite.scale.set(W*k,H*k,1);s.sprite.center.y=(H-AY)/H-(u.air?AIR_LIFT:0);} // air units (raid ships) hang above their ground point; base3d snaps y to the terrain, the anchor lift survives that // roster size by tier\r\n    const wx=(u.x+.5-origin.x)*scale,wz=(u.y+.5-origin.y)*scale;s.sprite.position.set(wx,0,wz);// atlas frames already bake the coach waddle (bob+tilt); a runtime bob/roll doubled it\r\n    const moving=t-s.lastMove<160,working=!moving&&(isWorking?!!isWorking(u):node&&Math.hypot(u.x-node.x,u.y-node.y)<=workRadius), // isWorking (base3d: the rts runtime's real work state) replaces the 'standing near the ore' guess, so only assigned miners/builders swing\r\n    name=u.clip||(moving?'walk':working?'work':'idle');\r\n    if(name!==s.mode){s.mode=name;s.since=tick;}\r\n    const phi=headingOf(u.x+.5-origin.x,u.y+.5-origin.y),sector=sectorOf((((s.heading??128)-phi)%256+256)%256);\r\n    if(u.air&&u.role==='ship'){\r\n     // the side's actual selected hull (raid-appearance shipId/shipVariant), painted natively and animated per frame; team colour as trim\r\n     const state=SHIP_STATES.includes(name)?name:moving?'fly':'idle',time=SHIP_CLIP_MS[state]?(u.clipElapsed??(t-s.since*TICK)):t,dir=SECTORS[sector];\r\n     const key=`ship:${u.shipId}:${u.shipVariant}:${u.shipTier}:${u.color}:${dir}:${state}:${shipFrame(state,time)}`;if(key!==s.key){s.key=key;paintShip({canvas:s.canvas,id:u.shipId,variant:u.shipVariant|0,tier:u.shipTier||1,trim:u.color,direction:dir,state,time});s.tex.needsUpdate=true;}\r\n    }else{const p=pick(u,sheet);if(p.sheet)draw(s,p.sheet,u.color,name,sector,tick,p.clips,p.strike,u.clipElapsed??null);}\r\n    looks(s,u,wx,wz,name==='walk',tick);\r\n    marks(s,u,wx,wz,pw*mult(u),isSel(sel,u));\r\n   }\r\n   for(const [id,s] of st)if(!seen.has(id)){group.remove(s.sprite);s.mat.dispose();s.tex.dispose();dropLooks(s);st.delete(id);}\r\n   return {units:st.size,node};\r\n  },\r\n  dispose(){for(const s of st.values()){s.mat.dispose();s.tex.dispose();dropLooks(s);}st.clear();ringGeo.dispose();ringMat.dispose();group.removeFromParent();group.clear();},\r\n };\r\n}\r\n","modules/coach-world-rts/drag-move.mjs":"// R53 drag-move: pure validity + a tiny controller. base3d owns gestures and the ghost; this owns \"can it go there\" and the `move` command.\r\n// The overlap/layout/entrance rules are copies of coach-world/state.mjs (private there); the server re-checks every move.\r\nimport {BUILDING_BY_ID} from '../coach-world/catalog.mjs';\r\nconst HUD='./hud.mjs'; // non-literal: keeps the legacy bundle from pulling the whole HUD\r\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\r\nconst overlap=(a,b)=>{const x=BUILDING_BY_ID[a.id],y=BUILDING_BY_ID[b.id];return a.x<b.x+y.w&&a.x+x.w>b.x&&a.y<b.y+y.h&&a.y+x.h>b.y;};\r\nconst live=buildings=>Object.values(buildings).filter(b=>b.level>0||b.job);\r\nfunction layoutValid(buildings){const rows=live(buildings);for(let i=0;i<rows.length;i++){const b=rows[i],d=BUILDING_BY_ID[b.id];if(!d||b.x<0||b.y<0||b.x>24-d.w||b.y>24-d.h)return false;for(let j=0;j<i;j++)if(overlap(b,rows[j]))return false;}return true;}\r\nfunction reachableEntrances(buildings){\r\n const blocked=new Set(),rows=live(buildings);\r\n for(const b of rows){const d=BUILDING_BY_ID[b.id];for(let y=b.y;y<b.y+d.h;y++)for(let x=b.x;x<b.x+d.w;x++)blocked.add(y*24+x);}\r\n const q=[0],seen=new Set(q);\r\n for(let i=0;i<q.length;i++){const n=q[i],x=n%24,y=Math.floor(n/24);for(const [a,c] of [[x-1,y],[x+1,y],[x,y-1],[x,y+1]]){const k=c*24+a;if(a>=0&&a<24&&c>=0&&c<24&&!blocked.has(k)&&!seen.has(k)){seen.add(k);q.push(k);}}}\r\n return rows.every(b=>{const d=BUILDING_BY_ID[b.id];\r\n  for(let x=b.x;x<b.x+d.w;x++)if(seen.has((b.y-1)*24+x)||seen.has((b.y+d.h)*24+x))return true;\r\n  for(let y=b.y;y<b.y+d.h;y++)if(seen.has(y*24+b.x-1)||seen.has(y*24+b.x+d.w))return true;\r\n  return false;});\r\n}\r\nexport const movable=(base,id)=>{const b=base?.buildings?.[id];return !!b&&b.level>0&&!b.job&&!!BUILDING_BY_ID[id];};\r\n// -> {ok,reason}. reason '' = same spot (nothing to send).\r\nexport function moveCheck(base,id,x,y,site=false){\r\n if(!(site?(base?.buildings?.[id]&&!base.buildings[id].job&&BUILDING_BY_ID[id]):movable(base,id)))return {ok:false,reason:'Choose an idle building.'};\r\n const b=base.buildings[id],d=BUILDING_BY_ID[id];\r\n if(!Number.isInteger(x)||!Number.isInteger(y)||x<0||y<0||x>24-d.w||y>24-d.h)return {ok:false,reason:'Choose a valid plot.'};\r\n if(x===b.x&&y===b.y)return {ok:false,reason:''};\r\n const next={...base.buildings,[id]:{...b,x,y,level:Math.max(1,b.level|0)}};\r\n if(!layoutValid(next))return {ok:false,reason:'That spot is taken.'};\r\n if(!reachableEntrances(next))return {ok:false,reason:'Leave every building a reachable entrance.'};\r\n return {ok:true,reason:''};\r\n}\r\n// R58: place a not-yet-built site on the tapped spot (server move allows level 0; the layout is checked as if it were built).\r\nexport const siteCheck=(base,id,x,y)=>moveCheck(base,id,x,y,true);\r\n// Controller: begin(id,tile) on long-press, hover(tile) as the finger moves (tile is fractional), drop() on release, cancel().\r\n// hover/begin return the ghost {id,x,y,w,h,ok,reason} (x,y = top-left tile). drop() resolves {id,sent,ok,reason,x,y,from}.\r\nexport function createDragMove({getBase,command,toast=m=>import(HUD).then(h=>h.toast(m,{kind:'error'})).catch(()=>{})}){\r\n let s=null;\r\n const ghost=()=>{const d=BUILDING_BY_ID[s.id],c=moveCheck(getBase(),s.id,s.x,s.y);return {id:s.id,x:s.x,y:s.y,w:d.w,h:d.h,ok:c.ok,reason:c.reason};};\r\n return {\r\n  get active(){return !!s;},\r\n  begin(id,tile){const base=getBase();if(!movable(base,id))return null;const b=base.buildings[id];s={id,from:{x:b.x,y:b.y},off:{x:tile.x-b.x,y:tile.y-b.y},x:b.x,y:b.y};return ghost();},\r\n  hover(tile){if(!s)return null;const d=BUILDING_BY_ID[s.id];s.x=clamp(Math.round(tile.x-s.off.x),0,24-d.w);s.y=clamp(Math.round(tile.y-s.off.y),0,24-d.h);return ghost();},\r\n  cancel(){s=null;},\r\n  async drop(){\r\n   if(!s)return null;const {id,x,y,from}=s,g=ghost();s=null;\r\n   if(!g.ok){if(g.reason)toast(g.reason);return {id,sent:false,ok:false,reason:g.reason,x,y,from};}\r\n   const r=await command({type:'move',buildingId:id,x,y});\r\n   const b=getBase()?.buildings?.[id],ok=!!b&&b.x===x&&b.y===y; // command() swallows server errors into the status line: confirm from the saved base\r\n   if(!ok)toast(r?.busy?'Still saving - try the move again.':r?.error?.message||'Could not move that building.');\r\n   return {id,sent:true,ok,reason:ok?'':'rejected',x,y,from};\r\n  },\r\n };\r\n}\r\n","modules/coach-world-rts/drop-pod-scene.mjs":"// R55 raid lane A: the Commander drop pod lands on a tapped tile of a base3d world, then a pod-top camera gives a look-around view.\r\n// playDrop(T,world,{tile,scale,onLanded}) -> {dispose,pod,group}; podCam(camera,poseSource) -> {update,dispose}; createCamToggle(base3d,camera,poseSource) -> {toggle,set,mode,dispose}.\r\n// Copied from drop-pod-opening.mjs: loadGlb normalise (largest side, base on y=0), fall curve p^1.85 + spin, fire/smoke trail, impact dust, mound. PS1 material on the pod.\r\nimport {createPs1Material} from './ps1-material.mjs';\r\nimport {spawnTile,SCALE} from './base-meshes.mjs';\r\n\r\nexport const POD_CAM_Y=2.5; // metres per scale unit above the pod base\r\nexport const tileToLocal=(tile,scale=SCALE,origin=spawnTile(scale))=>({x:(tile.x+.5-origin.x)*scale,z:(tile.y+.5-origin.y)*scale});\r\n// Same ease as the pack opening: slow start, hard finish. s,p in 0..1.\r\nexport const dropProgress=s=>Math.pow(Math.min(1,Math.max(0,s)),1.85);\r\nlet current=null; // the most recent live pod, podCam's default anchor\r\n\r\nconst rnd=(a,b)=>a+Math.random()*(b-a);\r\nfunction loadPod(T,url){\r\n return (async()=>{\r\n  try{\r\n   const [{GLTFLoader},{MeshoptDecoder}]=await Promise.all([import('/vendor/three/GLTFLoader.js'),import('/vendor/three/meshopt_decoder.module.js')]);\r\n   const gltf=await Promise.race([new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url),new Promise((_,no)=>setTimeout(no,7000))]);\r\n   const model=gltf.scene,box=new T.Box3().setFromObject(model),size=box.getSize(new T.Vector3()),mid=box.getCenter(new T.Vector3()),big=Math.max(size.x,size.y,size.z);\r\n   if(!(big>0))return null;\r\n   const k=1/big;model.scale.setScalar(k);model.position.set(-mid.x*k,-box.min.y*k,-mid.z*k);\r\n   const holder=new T.Group();holder.add(model);holder.userData={top:size.y*k,radius:Math.min(size.x,size.z)*k*.5};return holder;\r\n  }catch{return null;}\r\n })();\r\n}\r\nfunction glowTex(T){\r\n const c=document.createElement('canvas');c.width=c.height=32;const x=c.getContext('2d'),g=x.createRadialGradient(16,16,0,16,16,16);\r\n g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.4,'rgba(255,255,255,.5)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,32,32);return new T.CanvasTexture(c);\r\n}\r\n// Tiny CPU particle pool: dead particles are parked far below the map.\r\nfunction pool(T,n,tex,{additive,size}){\r\n const pos=new Float32Array(n*3).fill(-1e4),col=new Float32Array(n*3),life=new Float32Array(n),vel=new Float32Array(n*3),g=new T.BufferGeometry();\r\n g.setAttribute('position',new T.BufferAttribute(pos,3));g.setAttribute('color',new T.BufferAttribute(col,3));\r\n const m=new T.PointsMaterial({size,map:tex,vertexColors:true,transparent:true,depthWrite:false,fog:false,blending:additive?T.AdditiveBlending:T.NormalBlending});\r\n const pts=new T.Points(g,m);pts.frustumCulled=false;let head=0;\r\n return {pts,emit(x,y,z,vx,vy,vz,l,r,gr,b){const i=head++%n;pos.set([x,y,z],i*3);vel.set([vx,vy,vz],i*3);col.set([r,gr,b],i*3);life[i]=l;g.attributes.color.needsUpdate=true;},\r\n  step(dt,grav){for(let i=0;i<n;i++){if(life[i]<=0)continue;life[i]-=dt;if(life[i]<=0){pos[i*3+1]=-1e4;continue;}vel[i*3+1]-=grav*dt;for(let k=0;k<3;k++)pos[i*3+k]+=vel[i*3+k]*dt;}g.attributes.position.needsUpdate=true;},\r\n  dispose(){g.dispose();m.dispose();}};\r\n}\r\n\r\nexport function playDrop(T,world,{tile,scale=SCALE,onLanded=()=>{},origin,tier='rare',fallMs=1500,settleMs=450,height=2.1,heightAt,resolution,url,color=0x4bafff,now=()=>performance.now(),raf=f=>requestAnimationFrame(f),caf=id=>cancelAnimationFrame(id)}={}){\r\n const L=tileToLocal(tile,scale,origin),gy=heightAt?heightAt(tile.x,tile.y):0,h=height*scale;\r\n const END=new T.Vector3(L.x,gy,L.z),START=END.clone().add(new T.Vector3(-5*scale,26*scale,-8*scale)),dir=END.clone().sub(START).normalize();\r\n const group=new T.Group();group.name='rts-drop-pod';world.add(group);\r\n const pod=new T.Group();pod.name='rts-pod';pod.visible=false;group.add(pod);\r\n const tex=glowTex(T),fire=pool(T,260,tex,{additive:true,size:.7*scale}),smoke=pool(T,200,tex,{additive:false,size:1.1*scale});group.add(smoke.pts,fire.pts);\r\n const fc=new T.Color(color),own=[tex]; // fallback pod: a chunky capsule, swapped for the GLB when it loads\r\n const fb=new T.Mesh(new T.CapsuleGeometry(.3*h,.9*h,4,10),createPs1Material(T,{vertexColors:false,resolution}));fb.material.color.set(color);fb.position.y=.75*h;pod.add(fb);\r\n const podFrame=new T.Group();pod.add(podFrame);\r\n // mound (flattened rubble cone) + scorch disc, grow on impact\r\n const mound=new T.Mesh(new T.ConeGeometry(1.5*scale,.5*scale,9,1),createPs1Material(T,{vertexColors:false,resolution}));mound.material.color.set(0x4a3426);mound.position.set(END.x,gy+.18*scale,END.z);mound.scale.setScalar(.001);mound.visible=false;group.add(mound);\r\n const scorch=new T.Mesh(new T.CircleGeometry(2.2*scale,16),new T.MeshBasicMaterial({color:0x080504,transparent:true,opacity:.7,depthWrite:false}));scorch.rotation.x=-Math.PI/2;scorch.position.set(END.x,gy+.03,END.z);scorch.visible=false;group.add(scorch);\r\n let disposed=false,landed=false,rafId=0,t0=now(),last=t0,landAt=0,told=false;\r\n loadPod(T,url||new URL(`../../pods/drop-pod-${tier}.glb`,import.meta.url).href).then(holder=>{\r\n  if(!holder||disposed)return;\r\n  // The GLB is untextured grey (the pack opening paints it with tierMaterial): dark faction-tinted hull + faction emissive, additive rim shell, bright hatch core.\r\n  const meshes=[];holder.traverse(o=>{if(o.isMesh)meshes.push(o);});\r\n  const rimMat=new T.MeshBasicMaterial({color:fc.clone().multiplyScalar(.8),side:T.BackSide,blending:T.AdditiveBlending,transparent:true,opacity:.28,depthWrite:false,fog:false});own.push(rimMat);\r\n  for(const o of meshes){const m=o.material,n=createPs1Material(T,{map:m.map||null,vertexColors:!!o.geometry.attributes.color,resolution});\r\n   n.color.copy(m.map?m.color:fc.clone().multiplyScalar(.28).lerp(new T.Color(0x56627a),.35));n.emissive.copy(fc).multiplyScalar(.22);o.material=n;own.push(n);m.dispose?.();\r\n   const sh=new T.Mesh(o.geometry,rimMat);sh.scale.setScalar(1.045);o.add(sh);}\r\n  const core=new T.Mesh(new T.CircleGeometry((holder.userData.radius||.3)*.4,20),new T.MeshBasicMaterial({color:new T.Color(1,1,1).lerp(fc,.55),fog:false}));core.rotation.x=-Math.PI/2;core.position.y=(holder.userData.top||1)*.97;holder.add(core);own.push(core.geometry,core.material);\r\n  holder.scale.setScalar(h);pod.remove(fb);fb.geometry.dispose();fb.material.dispose();podFrame.add(holder);\r\n });\r\n const glowMat=()=>new T.SpriteMaterial({map:tex,color:fc,blending:T.AdditiveBlending,transparent:true,depthWrite:false,fog:false});\r\n const thruster=new T.Sprite(glowMat());thruster.scale.setScalar(3.2*h);thruster.position.y=-.15*h;pod.add(thruster);own.push(thruster.material);\r\n const flash=new T.Sprite(glowMat());flash.material.color.set(0xffffff).lerp(fc,.3);flash.position.set(END.x,gy+.5*scale,END.z);flash.visible=false;group.add(flash);own.push(flash.material);\r\n const ring=new T.Mesh(new T.RingGeometry(.85,1,40),new T.MeshBasicMaterial({color:fc.clone().lerp(new T.Color(1,1,1),.5),side:T.DoubleSide,blending:T.AdditiveBlending,transparent:true,depthWrite:false,fog:false}));ring.rotation.x=-Math.PI/2;ring.position.set(END.x,gy+.08,END.z);ring.visible=false;group.add(ring);own.push(ring.geometry,ring.material);\r\n const spin=new T.Quaternion(),Y=new T.Vector3(0,1,0),prev=new T.Vector3();\r\n function impact(){\r\n  landed=true;landAt=now();pod.position.copy(END);mound.visible=scorch.visible=flash.visible=ring.visible=true;thruster.visible=false;\r\n  for(let i=0;i<70;i++){const a=Math.random()*6.28,sp=rnd(1.5,5)*scale;smoke.emit(END.x+Math.cos(a)*.5*scale,gy+.3*scale,END.z+Math.sin(a)*.5*scale,Math.cos(a)*sp,rnd(.3,2)*scale,Math.sin(a)*sp,rnd(.8,1.6),.5,.42,.36);}\r\n  for(let i=0;i<45;i++){const a=Math.random()*6.28,sp=rnd(3,9)*scale;fire.emit(END.x,gy+.4*scale,END.z,Math.cos(a)*sp,rnd(2,8)*scale,Math.sin(a)*sp,rnd(.3,.8),1,.7,.3);}\r\n }\r\n function tick(){\r\n  if(disposed)return;rafId=raf(tick);\r\n  const t=now(),dt=Math.min(.05,(t-last)/1000);last=t;\r\n  if(!landed){\r\n   const p=dropProgress((t-t0)/fallMs);pod.visible=true;prev.copy(pod.position);pod.position.lerpVectors(START,END,p);\r\n   spin.setFromAxisAngle(Y,(1-p)*Math.PI*4);pod.quaternion.copy(spin);\r\n   const n=Math.min(14,Math.ceil(prev.distanceTo(pod.position)/(.5*scale))+1);\r\n   for(let i=0;i<n;i++){const k=i/n,x=prev.x+(pod.position.x-prev.x)*k,y=prev.y+(pod.position.y-prev.y)*k,z=prev.z+(pod.position.z-prev.z)*k;\r\n    fire.emit(x-dir.x*h*.5,y-dir.y*h*.5,z-dir.z*h*.5,rnd(-.4,.4)*scale,rnd(-.4,.4)*scale,rnd(-.4,.4)*scale,rnd(.3,.6),.5+fc.r*.5,.5+fc.g*.5*rnd(.6,1),.3+fc.b*.7);\r\n    if(Math.random()<.5)smoke.emit(x,y,z,rnd(-.3,.3)*scale,rnd(-.2,.4)*scale,rnd(-.3,.3)*scale,rnd(.9,1.5),.3,.28,.32);}\r\n   if(t-t0>=fallMs)impact();\r\n  }else{\r\n   const g=Math.min(1,(t-landAt)/500),f=Math.min(1,(t-landAt)/600);mound.scale.set(g,g,g);ring.scale.setScalar(.3*scale+9*scale*f);ring.material.opacity=1-f;flash.scale.setScalar((5+6*f)*scale);flash.material.opacity=Math.max(0,1-f*1.6);if(f>=1){ring.visible=flash.visible=false;}pod.quaternion.slerp(spin.identity(),Math.min(1,dt*14));\r\n   if(Math.random()<dt*14)smoke.emit(END.x+rnd(-.9,.9)*scale,gy+.35*scale,END.z+rnd(-.9,.9)*scale,rnd(-.1,.1),rnd(.5,1.2)*scale,rnd(-.1,.1),rnd(1.5,2.5),.55,.58,.62); // steam\r\n   if(!told&&t-landAt>=settleMs){told=true;try{onLanded({tile,position:[END.x,END.y,END.z],top:END.y+h,pod});}catch(e){console.warn('drop-pod-scene onLanded',e);}}\r\n  }\r\n  fire.step(dt,2*scale);smoke.step(dt,-.2*scale);\r\n }\r\n rafId=raf(tick);\r\n const api={group,pod,get landed(){return landed;},podTop:()=>new T.Vector3(END.x,END.y+h,END.z),dispose(){\r\n  if(disposed)return;disposed=true;caf(rafId);if(current===pod)current=null;world.remove(group);fire.dispose();smoke.dispose();\r\n  group.traverse(o=>{if(o.isMesh){o.geometry?.dispose();o.material?.dispose?.();}});own.forEach(o=>o.dispose?.());}};\r\n current=pod;return api;\r\n}\r\n\r\n// Pod-top camera: each update() puts `camera` POD_CAM_Y*scale above the pod base and aims it with poseSource.pose().quaternion (drag/gyro look).\r\n// `anchor` defaults to the pod of the latest playDrop. Plain maths on the camera (position.set / quaternion.set), so it works with a fake camera in node.\r\nexport function podCam(camera,poseSource,{anchor,scale=SCALE}={}){\r\n let off=false;\r\n return {\r\n  update(){\r\n   const a=anchor||current;if(off||!a)return false;\r\n   a.updateWorldMatrix?.(true,false);const e=a.matrixWorld.elements;\r\n   camera.position.set(e[12],e[13]+POD_CAM_Y*scale,e[14]);camera.quaternion.set(...poseSource.pose().quaternion);camera.updateMatrixWorld?.();return true;\r\n  },\r\n  dispose(){off=true;},\r\n };\r\n}\r\n\r\n// Switch the raid between base3d's top-down orbit camera and the pod-top camera. base3d calls `cameraOverride(camera)` instead of its orbit when set.\r\n// In pod mode a drag (pointermove, captured before base3d's orbit) turns the head; taps still reach base3d so tile taps raycast through the pod camera.\r\nexport function createCamToggle(base3d,camera,poseSource,{el,scale=SCALE}={}){\r\n let mode='top',cam=null,off=null;\r\n const target=()=>el||globalThis.document?.querySelector('[data-base3d] canvas');\r\n const api={\r\n  get mode(){return mode;},\r\n  set(m){\r\n   if(m===mode)return mode;off?.();off=null;cam?.dispose();cam=null;mode=m;\r\n   if(m==='pod'){\r\n    if(poseSource.mode==='idle')poseSource.begin('denied');\r\n    cam=podCam(camera,poseSource,{scale});base3d.cameraOverride=()=>cam.update();\r\n    const t=target();let lx=0,ly=0,down=false;\r\n    const d=e=>{down=true;lx=e.clientX;ly=e.clientY;},u=()=>{down=false;},mv=e=>{if(!down)return;poseSource.drag(e.clientX-lx,e.clientY-ly);lx=e.clientX;ly=e.clientY;e.stopImmediatePropagation();};\r\n    if(t){t.addEventListener('pointerdown',d,true);t.addEventListener('pointerup',u,true);t.addEventListener('pointercancel',u,true);t.addEventListener('pointermove',mv,true);\r\n     off=()=>{for(const [n,f] of [['pointerdown',d],['pointerup',u],['pointercancel',u],['pointermove',mv]])t.removeEventListener(n,f,true);};}\r\n   }else base3d.cameraOverride=null;\r\n   return mode;\r\n  },\r\n  toggle(){return api.set(mode==='top'?'pod':'top');},\r\n  dispose(){api.set('top');},\r\n };\r\n return api;\r\n}\r\n","modules/coach-world-rts/dust-motes.mjs":"// V5 dust motes (Daggerfall look). Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v5/card.mjs D), validated (contract, determinism,\r\n// headless WebGL compile + render, 1 draw) and reviewed (point-size clamp added). Wrap-box idea from DFO Online windWisps.js / precipitation.js (MIT).\r\n// createDustMotes(T,{count,box,size,color,seed,opacity}) -> {object,update(t,centre),setWind(x,z),setOpacity(a),dispose(),stats()}\r\nexport function createDustMotes(T, opts = {}) {\r\n  const {\r\n    count = 400,\r\n    box = 40,\r\n    size = 3,\r\n    color = [0.9, 0.75, 0.55],\r\n    seed = 1,\r\n    opacity = 0.55\r\n  } = opts;\r\n\r\n  // ---------- PRNG ----------\r\n  function lcg(seed) {\r\n    let state = seed >>> 0;\r\n    return () => {\r\n      state = (state * 1664525 + 1013904223) >>> 0;\r\n      return state / 4294967296; // [0,1)\r\n    };\r\n  }\r\n  const rand = lcg(seed);\r\n\r\n  // ---------- Geometry ----------\r\n  const positions = new Float32Array(count * 3);\r\n  const aSeeds   = new Float32Array(count);\r\n  for (let i = 0; i < count; ++i) {\r\n    const idx = i * 3;\r\n    positions[idx]     = rand() * box;          // x\r\n    positions[idx + 1] = rand() * box;          // y\r\n    positions[idx + 2] = rand() * box;          // z\r\n    aSeeds[i]          = rand();                // seed attribute\r\n  }\r\n  const geometry = new T.BufferGeometry();\r\n  geometry.setAttribute('position', new T.Float32BufferAttribute(positions, 3));\r\n  geometry.setAttribute('aSeed',     new T.Float32BufferAttribute(aSeeds, 1));\r\n\r\n  // ---------- Shaders ----------\r\n  const vertexShader = `\r\n    uniform float uTime;\r\n    uniform vec3  uCentre;\r\n    uniform vec3  uWind;\r\n    uniform float uBox;\r\n    uniform float uSize;\r\n\r\n    attribute float aSeed;\r\n    varying float vLife;\r\n    varying float vEdgeFade;\r\n\r\n    void main() {\r\n      // Wrap position around the box\r\n      vec3 p = mod(position + uWind * uTime - uCentre + uBox * 0.5, uBox) + uCentre - uBox * 0.5;\r\n\r\n      // Bobbing motion\r\n      p.y += 0.4 * sin(uTime * 0.7 + aSeed * 40.0);\r\n\r\n      // Life cycle\r\n      vLife = sin(fract(uTime * 0.08 + aSeed * 7.0) * 3.141592653589793);\r\n\r\n      // Edge fade to avoid popping at box faces\r\n      vec3 d = p - uCentre;\r\n      float maxdxz = max(abs(d.x), abs(d.z));\r\n      vEdgeFade = 1.0 - smoothstep(0.35 * uBox, 0.5 * uBox, maxdxz);\r\n\r\n      // Perspective attenuation of point size\r\n      vec4 mvPosition = modelViewMatrix * vec4(p, 1.0);\r\n      gl_Position = projectionMatrix * mvPosition;\r\n      float dist = length(mvPosition.xyz);\r\n      gl_PointSize = clamp((uSize * 10.0) / dist, 1.0, 6.0); // 10 units reference distance, 1..6 px (review fix: the card skipped the clamp)\r\n    }\r\n  `;\r\n\r\n  const fragmentShader = `\r\n    uniform vec3 uColor;\r\n    uniform float uOpacity;\r\n\r\n    varying float vLife;\r\n    varying float vEdgeFade;\r\n\r\n    void main() {\r\n      gl_FragColor = vec4(uColor, uOpacity * vLife * vEdgeFade);\r\n    }\r\n  `;\r\n\r\n  // ---------- Material ----------\r\n  const material = new T.ShaderMaterial({\r\n    uniforms: {\r\n      uTime:     { value: 0 },\r\n      uCentre:   { value: new T.Vector3() },\r\n      uWind:     { value: new T.Vector3(0.6, 0, 0.25) },\r\n      uColor:    { value: new T.Color(...color) },\r\n      uOpacity:  { value: opacity },\r\n      uBox:      { value: box },\r\n      uSize:     { value: size }\r\n    },\r\n    vertexShader,\r\n    fragmentShader,\r\n    transparent: true,\r\n    depthWrite: false,\r\n    fog: false\r\n  });\r\n\r\n  // ---------- Points ----------\r\n  const points = new T.Points(geometry, material);\r\n  points.frustumCulled = false;\r\n  points.name = 'dust-motes';\r\n\r\n  // ---------- Return API ----------\r\n  return {\r\n    object: points,\r\n\r\n    update(timeSeconds, centreVector3) {\r\n      material.uniforms.uTime.value = timeSeconds;\r\n      material.uniforms.uCentre.value.copy(centreVector3);\r\n    },\r\n\r\n    setWind(x, z) {\r\n      material.uniforms.uWind.value.set(x, 0, z);\r\n    },\r\n\r\n    setOpacity(a) {\r\n      material.uniforms.uOpacity.value = a;\r\n    },\r\n\r\n    dispose() {\r\n      geometry.dispose();\r\n      material.dispose();\r\n    },\r\n\r\n    stats() { return { calls: 1, count }; }\r\n  };\r\n}\r\n","modules/coach-world-rts/element-fx.mjs":"import { effectSpec, ENGINE_REDUCED } from './elements.mjs';\r\n\r\n// 2D element attacks. fire(from,to,factionOrTower) in tile units; draw(ctx,{ox,oy,scale}) maps tiles to px.\r\nexport function createElementFx({ cap = 64, now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now()) } = {}) {\r\n  const effects = [], sheets = new Map();\r\n  const sheet = url => {\r\n    if (typeof Image === 'undefined') return null;\r\n    if (!sheets.has(url)) { const i = new Image(); i.src = url; sheets.set(url, i); }\r\n    return sheets.get(url);\r\n  };\r\n  const rgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16)).join(',');\r\n\r\n  function fire(from, to, factionOrTower) {\r\n    const spec = effectSpec(factionOrTower, ENGINE_REDUCED());\r\n    if (!spec) return false;\r\n    effects.push({ ...spec, from, to, start: now(), img: spec.type === 'sheet' ? sheet(spec.url) : null });\r\n    while (effects.length > cap) effects.shift();\r\n    return true;\r\n  }\r\n  function step() {\r\n    const t = now();\r\n    for (let i = effects.length - 1; i >= 0; --i) if (t - effects[i].start > effects[i].duration) effects.splice(i, 1);\r\n  }\r\n  function draw(ctx, tr) {\r\n    step();\r\n    const { ox = 0, oy = 0, scale = 1 } = tr || {}, X = x => x * scale + ox, Y = y => y * scale + oy, t0 = now();\r\n    ctx.save();\r\n    for (const e of effects) {\r\n      const t = t0 - e.start, p = Math.min(1, t / e.duration), alpha = 1 - p;\r\n      if (e.type === 'laser') {\r\n        ctx.lineCap = 'round';\r\n        for (const [w, a] of [[4, alpha * 0.35], [2, alpha]]) {\r\n          ctx.strokeStyle = `rgba(${rgb(e.color)},${a})`; ctx.lineWidth = w;\r\n          ctx.beginPath(); ctx.moveTo(X(e.from.x), Y(e.from.y)); ctx.lineTo(X(e.to.x), Y(e.to.y)); ctx.stroke();\r\n        }\r\n      } else if (e.type === 'rocks') {\r\n        const dx = e.to.x - e.from.x, dy = e.to.y - e.from.y, len = Math.hypot(dx, dy) || 1, arc = Math.sin(Math.PI * p) * 18 / scale;\r\n        const px = e.from.x + dx * p - (dy / len) * arc, py = e.from.y + dy * p + (dx / len) * arc;\r\n        ctx.fillStyle = `rgba(${rgb(e.color)},${t > e.duration - 100 ? (e.duration - t) / 100 : 1})`;\r\n        for (let i = 0; i < 3; ++i) ctx.fillRect(X(px) + (i - 1) * 4, Y(py), 3, 3);\r\n      } else if (e.type === 'flash') {\r\n        ctx.globalAlpha = alpha; ctx.fillStyle = e.color; ctx.beginPath(); ctx.arc(X(e.to.x), Y(e.to.y), 6, 0, Math.PI * 2); ctx.fill();\r\n      } else if (e.img?.complete && e.img.naturalWidth) {\r\n        const i = Math.min(e.frames - 1, Math.floor(t / e.frameDuration)), cw = e.img.width / e.cols, ch = e.img.height / e.rows;\r\n        ctx.imageSmoothingEnabled = false; ctx.globalCompositeOperation = 'lighter';\r\n        ctx.drawImage(e.img, (i % e.cols) * cw, Math.floor(i / e.cols) * ch, cw, ch, X(e.to.x) - 20, Y(e.to.y) - 20, 40, 40);\r\n      }\r\n    }\r\n    ctx.restore();\r\n  }\r\n  return { fire, draw, step, count: () => effects.length };\r\n}\r\n","modules/coach-world-rts/elements.mjs":"export const ELEMENTS = Object.freeze({\r\n  chest: { id: 'laser', color: '#ff4a3a' },\r\n  'arms-shoulders': { id: 'laser', color: '#ffd36b' },\r\n  quads: { id: 'rocks' },\r\n  glutes: { id: 'poison', sheet: '17_felspell_spritesheet.png', cols: 10, rows: 10 },\r\n  yoga: { id: 'nausea', sheet: '13_vortex_spritesheet.png', cols: 8, rows: 8 },\r\n  'martial-arts': { id: 'fire', sheet: '11_fire_spritesheet.png', cols: 8, rows: 8 },\r\n  cardio: { id: 'electric', sheet: 'pixelated_tilesheet.png', cols: 4, rows: 2 },\r\n  tower: { id: 'ice', sheet: '../../hold/assets/19_freezing_spritesheet.png', cols: 10, rows: 10 }\r\n});\r\n\r\nexport function elementFor(faction) {\r\n  return ELEMENTS[faction] ?? null;\r\n}\r\n\r\nexport const ELEMENT_IDS = Object.freeze(\r\n  [...new Set(Object.values(ELEMENTS).map(e => e.id))]\r\n);\r\n\r\n// Shared by element-fx (2D) and fx3d (3D): what to play for a faction/tower. Reduced motion collapses everything to a 150 ms flash.\r\nexport function effectSpec(factionOrTower, reduced = false) {\r\n  const e = elementFor(factionOrTower);\r\n  if (!e) return null;\r\n  const color = e.color || '#ffffff';\r\n  if (reduced) return { id: e.id, type: 'flash', color, duration: 150 };\r\n  if (e.id === 'laser') return { id: e.id, type: 'laser', color, duration: 150 };\r\n  if (e.id === 'rocks') return { id: e.id, type: 'rocks', color: '#8a7a64', duration: 400 };\r\n  const frames = e.cols * e.rows, duration = Math.min(frames * 42, 1000); // 42 ms/frame, long sheets squeezed to 1 s so hits don't pile up\r\n  return { id: e.id, type: 'sheet', color, cols: e.cols, rows: e.rows, frames, frameDuration: duration / frames, duration, url: new URL(e.sheet, new URL('../../arcade/power/assets/', import.meta.url)).href };\n}\r\nexport const ENGINE_REDUCED = () => !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\r\n","modules/coach-world-rts/facility-controls.css":".cw-facility-controls{position:absolute;inset:98px 6px 205px;z-index:42;pointer-events:auto;overflow:auto;overscroll-behavior:contain;padding:10px;background:#111b22f5;color:#eef6f5;border:2px solid var(--cw-faction,#91d8be);font:14px/1.3 ui-monospace,monospace;box-shadow:0 8px 30px #0009}\r\n.cw-facility-controls[hidden]{display:none}.cw-facility-controls header{display:flex;align-items:center;justify-content:space-between;gap:8px;position:sticky;top:-10px;background:#111b22;z-index:1}.cw-facility-controls h2{font-size:17px;margin:4px 0}.cw-facility-controls h3{font-size:14px;margin:14px 0 6px;color:#b9ecdd}.cw-facility-controls button{min-width:56px;min-height:44px;padding:6px 9px;color:inherit;background:#273e49;border:1px solid #70949b;cursor:pointer;font:inherit}.cw-facility-controls button[aria-disabled=true]{opacity:.65}.cw-facility-controls small{display:block;font-size:11px;color:#b3c8cf}.cw-facility-offer,.cw-facility-production li{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 0;border-bottom:1px solid #35434a}.cw-facility-production{padding-left:20px}.cw-facility-worker{border-bottom:1px solid #35434a;padding:8px 0}.cw-facility-worker>div{display:flex;gap:5px;margin-top:5px}.cw-facility-budget{padding:6px;border:1px solid #5d8981;color:#d2f9e2}.cw-facility-note{min-height:20px;color:#ffe1a3}@media(orientation:landscape){.cw-facility-controls{inset:98px 8px 80px 25%;font-size:13px}}\r\n\r\n.cw-facility-controls,.cw-facility-controls *{box-sizing:border-box}.cw-facility-offer>span,.cw-facility-production li>span{min-width:0;overflow-wrap:anywhere}.cw-facility-offer>button,.cw-facility-production li>button{flex-shrink:0}.cw-facility-controls h2{min-width:0;overflow-wrap:anywhere}\r\n","modules/coach-world-rts/facility-controls.mjs":"import {BUILDING_BY_ID,FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {UNIT_CATALOG} from '../coach-world/expedition-catalog.mjs';\r\nimport {productionQueue,supplyStatus,trainingAvailability} from '../coach-world/production.mjs';\r\nimport {economyState,harvestQuote} from '../coach-world/rts-economy.mjs';\r\nimport {harvestResourceLabel as resourceLabel,resourceDisplayText as resourceText} from '../coach-world/resource-labels.mjs';\r\n\r\nconst esc=value=>String(value??'').replace(/[&<>\"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\r\nexport function mountFacilityControls(root,{getBase,getBuildingId,command,serverNow=Date.now}={}){\r\n const doc=root.ownerDocument,el=doc.createElement('section');el.className='cw-facility-controls';el.hidden=true;el.setAttribute('role','dialog');el.setAttribute('aria-label','Building controls');\r\n const css=doc.createElement('link');css.rel='stylesheet';css.href=new URL('./facility-controls.css',import.meta.url).href;root.append(css,el);\r\n let activeId=null,nodeFaction=null,busy=false,signature='',note='',tech=null,disposed=false;\r\n const techReady=import('../coach-world/rts-tech.mjs').then(m=>{tech=m;signature='';update();}).catch(()=>{});\r\n function clock(){for(const n of el.querySelectorAll('[data-facility-due]')){const s=Math.max(0,Math.ceil((Number(n.dataset.facilityDue)-serverNow())/1000));n.textContent=s?`${Math.floor(s/60)}m ${String(s%60).padStart(2,'0')}s`:'Ready';}}\r\n const button=(label,action,why='')=>`<button type=\"button\" data-facility-action=\"${esc(JSON.stringify(action))}\" ${busy?'disabled':''} ${why?`aria-disabled=\"true\" data-why=\"${esc(why)}\"`:''}>${esc(label)}</button>`;\r\n function renderTech(base,id){\r\n  if(!tech)return '<p>Loading facility upgrades…</p>';\r\n  const p=tech.researchPanel(base,id,serverNow()),h=p.health;let html=h.max?`<p>Structure HP ${h.hp}/${h.max}${h.offline?' · OFFLINE':''}</p>`:'';\r\n  if(p.job)html+=`<p>${esc(p.job.kind==='addon'?'Building add-on':p.job.id)} · <span data-facility-due=\"${p.job.dueAt}\"></span> ${button('Cancel',{type:'rts-cancel',facilityId:id})}</p>`;\r\n  for(const r of p.research){const q=r.quote;html+=`<div class=\"cw-facility-offer\"><span><b>${esc(r.label)} ${r.level}/3</b><small>${esc(r.effect)} · ${q.cost} Protein · ${q.gasCost} Creatine · ${Math.ceil(q.durationMs/1000)}s${q.allowed?'':` · ${esc(resourceText(q.reason))}`}</small></span>${button('Research',{type:'rts-research',facilityId:id,techId:r.id},q.allowed?'':q.reason)}</div>`;}\r\n  for(const a of p.addons){const q=a.quote;html+=`<div class=\"cw-facility-offer\"><span><b>${esc(a.label)}${a.owned?' · Built':''}</b><small>${esc(a.effect)} · ${q.cost??a.mineral} Protein · ${q.gasCost??a.gas} Creatine${q.reason?` · ${esc(resourceText(q.reason))}`:''}</small></span>${button(a.owned?'Built':'Build add-on',{type:'rts-addon',facilityId:id,addonId:a.id},q.allowed?'':q.reason)}</div>`;}\r\n  if(h.damaged){const q=p.repair;html+=h.repair?`<p>Repairing · <span data-facility-due=\"${h.repair.dueAt}\"></span> ${button('Cancel repair',{type:'rts-cancel',facilityId:id})}</p>`:`<div class=\"cw-facility-offer\"><span><b>Repair structure</b><small>${q.cost??'—'} Protein · ${Math.ceil((q.durationMs??0)/1000)}s${q.reason?` · ${esc(resourceText(q.reason))}`:''}</small></span>${button('Repair',{type:'rts-repair',buildingId:id},q.allowed?'':q.reason)}</div>`;}\r\n  if(id==='hq')html+=`<h3>NPC defense drill</h3><p>Costs 5 Glycogen. Server-simulated combat can damage structures; repair them here afterward. No XP or wallet rewards.</p>${button('Run drill',{type:'rts-defense-drill',difficulty:'easy'})}${base.rtsDefense?.lastResult?`<p>Last drill: ${esc(base.rtsDefense.lastResult.winner==='b'?'Defense held':base.rtsDefense.lastResult.winner==='a'?'Attackers won':'Draw')}. Structures damaged: ${Object.keys(base.rtsDefense.lastResult.damage).length}. Select a damaged building to repair it.</p>`:''}`;\r\n  return html;\r\n }\r\n function update(){\r\n  if(disposed||el.hidden)return;const base=getBase();if(!base)return;const id=activeId??getBuildingId(),def=BUILDING_BY_ID[id];if(!def)return;\r\n  const faction=nodeFaction??def.track??base.faction,e=economyState(base),s=supplyStatus(base),key=JSON.stringify([id,faction,base.materials,e,base.productionQueues,base.training,base.rtsTech,base.rtsDefense,base.fuel,base.buildings,busy,note,!!tech]);\r\n  if(key===signature){clock();return;}signature=key;\r\n  let body=`<p class=\"cw-facility-budget\">Protein ${base.materials[faction]??0} · Creatine ${e.gas} · Training capacity ${s.used}+${s.reserved}/${s.cap}</p>`;\r\n  if(def.kind==='production'){\r\n   body+='<h3>Train companions</h3>';\r\n   for(const unit of UNIT_CATALOG.filter(u=>u.trainable&&u.buildingId===id)){const q=trainingAvailability(base,unit.id);body+=`<div class=\"cw-facility-offer\"><span><b>${esc(unit.label)}</b><small>${q.materialCost??q.unitMaterialCost} Protein · ${Math.ceil((q.trainingMs??q.unitTrainingMs)/1000)}s · ${unit.supply} Training capacity${!q.allowed?` · ${esc(resourceText(q.reason))}`:''}</small></span>${button('Train',{type:'train-unit',unitId:unit.id},q.allowed?'':q.reason)}</div>`;}\r\n   const q=productionQueue(base,id);body+=`<h3>Production ${q.used}/${q.slots}</h3><ol class=\"cw-facility-production\">`;\r\n   for(const j of q.items)body+=`<li><span>${esc(j.label)} <small data-facility-due=\"${j.dueAt}\"></small></span>${button('Cancel',{type:'cancel-production',queueId:j.queueId})}</li>`;\r\n   body+='</ol>';\r\n  }\r\n  if(id==='hq'){\r\n   body+=`<h3>${esc(FACTION_BY_ID[faction].label)} deposits</h3><p>Protein ${e.nodes[faction].minerals} · Creatine ${e.nodes[faction].gas}. Creatine requires a power generator.</p>`;\r\n   for(let n=1;n<=base.workers;n++){const workerId=`crew-${n}`,a=e.assignments[workerId],mq=harvestQuote(base,workerId,'minerals',faction),gq=harvestQuote(base,workerId,'gas',faction);body+=`<div class=\"cw-facility-worker\"><b>Builder ${n}</b><small>${a?`${esc(a.kind)} · ${esc(FACTION_BY_ID[a.faction].label)}`:'Idle'}</small><div>${button('Protein',{type:'gather',workerId,kind:'minerals',faction},mq.allowed?'':mq.reason)}${button('Creatine',{type:'gather',workerId,kind:'gas',faction},gq.allowed?'':gq.reason)}${a?button('Recall',{type:'stop-gather',workerId}):''}</div></div>`;}\r\n  }\r\n  body+=renderTech(base,id);\r\n  el.innerHTML=`<header><h2>${esc(def.label)}</h2><button type=\"button\" data-facility-close aria-label=\"Close building controls\">×</button></header>${body}<p role=\"status\" class=\"cw-facility-note\">${esc(note)}</p>`;\r\n  el.querySelector('[data-facility-close]').onclick=close;\r\n  for(const b of el.querySelectorAll('[data-facility-action]'))b.onclick=()=>{if(b.dataset.why){note=resourceText(b.dataset.why);signature='';update();return;}void send(JSON.parse(b.dataset.facilityAction));};\r\n  clock();\r\n }\r\n async function send(action){if(busy||disposed)return false;busy=true;note='Saving…';signature='';update();try{const result=await command(action);if(result?.ok===false)throw result.error??Error(result.busy?'Another save is running.':'Action was refused.');note='Saved.';return true;}catch(e){note=resourceText(e.message)||'Could not save.';return false;}finally{busy=false;signature='';update();}}\r\n function open(id=getBuildingId(),options={}){activeId=id;nodeFaction=options.faction??null;note='';el.hidden=false;signature='';update();el.querySelector('[data-facility-close]')?.focus();return techReady;}\r\n function close(){el.hidden=true;activeId=null;signature='';}\r\n return {element:el,open,close,update,send,dispose(){disposed=true;el.remove();css.remove();}};\r\n}\r\n","modules/coach-world-rts/far-ring.mjs":"// V6 far ring (Daggerfall look): the land past the streamed cells, out to the horizon, from coarse DEM tiles, so the world reads to the edge.\r\n// Ported from DFO Online src/render/farRing.js @ e3054735 (MIT, coach-world/vendor/dfo-LICENSE.txt): one vertex per DEM pixel around the\r\n// eye, normals/shade by clamped central differences, a hole punched under the live terrain, a round rim, hazed by the shared fog law (V1, so\r\n// the ring and the ground beside it fog identically: DFO's FAR-SEA1 seam cannot happen). Changes: Terrarium z9/z10 instead of WOODS.WLD,\r\n// world-terrain's height ramp and PS1 ground material instead of overworldTint, no sea layer (Vegas has none), and REAL depth instead of\r\n// DFO's depthRange(1,1): the ring keeps its own ridge-over-ridge occlusion (it is seen from a cockpit), so instead the hole is one ring\r\n// cell smaller than the live terrain and the ring sits SINK metres lower, and the real terrain always wins the overlap.\r\n// buildFarRing({...}) is pure (tests); createFarRing(T,{...}) -> {group,stats,dispose}.\r\nimport {toLocal} from '../coach-world/world-tiles.mjs';\r\nimport {groundRamp} from './world-terrain.mjs';\r\nexport const RING_SINK=4; // metres under the live terrain at the overlap\r\nconst RAD=Math.PI/180;\r\nexport const pixelToLatLng=(px,py,z)=>{const n=256*2**z;return {lng:px/n*360-180,lat:Math.atan(Math.sinh(Math.PI*(1-2*py/n)))/RAD};};\r\nexport const latLngToPixel=(lat,lng,z)=>{const n=256*2**z,r=lat*RAD;return {px:(lng+180)/360*n,py:(1-Math.asinh(Math.tan(r))/Math.PI)/2*n};};\r\n// DEM tiles (z/x/y, 256 px) a ring of radiusPx around origin needs.\r\nexport function ringTiles(origin,z,radiusPx){const {px,py}=latLngToPixel(origin.lat,origin.lng,z),t=[];\r\n for(let ty=Math.floor((py-radiusPx)/256);ty<=Math.floor((py+radiusPx)/256);ty++)for(let tx=Math.floor((px-radiusPx)/256);tx<=Math.floor((px+radiusPx)/256);tx++)t.push([tx,ty]);return t;}\r\n\r\n// dem: Map \"tx,ty\" -> Float32Array(256*256) metres (missing tiles leave holes). hole: {x0,z0,x1,z1} metres (the live terrain footprint).\r\n// under(x,z): the live terrain height (metres, exaggerated) where it exists; ring nodes in the overlap band are clamped below it.\r\nexport function buildFarRing({dem,origin,z,radiusPx,hole=null,h0=0,exaggeration=1.5,sink=RING_SINK,under=null}){\r\n const {px,py}=latLngToPixel(origin.lat,origin.lng,z),cx=Math.round(px),cy=Math.round(py),R=radiusPx,W=2*R+1;\r\n const pos=new Float32Array(W*W*3),col=new Float32Array(W*W*3),ok=new Uint8Array(W*W),hgt=new Float32Array(W*W),tmp=[0,0,0];\r\n const elev=(gx,gy)=>{const t=dem.get(Math.floor(gx/256)+','+Math.floor(gy/256));return t?t[(gy&255)*256+(gx&255)]:NaN;};\r\n for(let j=0;j<W;j++)for(let i=0;i<W;i++){const gx=cx-R+i,gy=cy-R+j,k=j*W+i,e=elev(gx,gy);if(!Number.isFinite(e))continue;\r\n  const {lat,lng}=pixelToLatLng(gx,gy,z),p=toLocal(origin,lat,lng),y=(e-h0)*exaggeration;ok[k]=1;hgt[k]=y;pos.set([p.x,y-sink,p.z],k*3);}\r\n const step=40075016.686*Math.cos(origin.lat*RAD)/(256*2**z); // metres per ring cell (web-mercator pixel at the origin; never read from nodes that may be missing)\r\n if(hole&&under)for(let k=0;k<W*W;k++){if(!ok[k])continue;const x=pos[k*3],zz=pos[k*3+2]; // overlap band: a coarse sample never pokes through the fine ground\r\n  if(x>hole.x0-step&&x<hole.x1+step&&zz>hole.z0-step&&zz<hole.z1+step){const t=under(Math.min(hole.x1,Math.max(hole.x0,x)),Math.min(hole.z1,Math.max(hole.z0,zz)));if(Number.isFinite(t))pos[k*3+1]=Math.min(pos[k*3+1],t-sink);}}\r\n const at=(i,j)=>{i=Math.max(0,Math.min(W-1,i));j=Math.max(0,Math.min(W-1,j));const k=j*W+i;return ok[k]?hgt[k]:NaN;};\r\n for(let j=0;j<W;j++)for(let i=0;i<W;i++){const k=j*W+i;if(!ok[k])continue;const y=hgt[k],c=v=>Number.isFinite(v)?v:y; // DFO: clamped central differences\r\n  const sx=(c(at(i+1,j))-c(at(i-1,j)))/(2*step),sz=(c(at(i,j+1))-c(at(i,j-1)))/(2*step),shade=1/Math.sqrt(1+sx*sx+sz*sz),s=.55+.45*shade;\r\n  groundRamp(y,tmp);col.set([tmp[0]*s,tmp[1]*s,tmp[2]*s],k*3);}\r\n // hole: the live footprint shrunk by one ring cell (the ring overlaps under the terrain edge); rim: a circle of radius R cells\r\n const inset=hole?{x0:hole.x0+step,z0:hole.z0+step,x1:hole.x1-step,z1:hole.z1-step}:null,idx=[];let punched=0;\r\n for(let j=0;j<W-1;j++)for(let i=0;i<W-1;i++){const a=j*W+i,b=a+1,c=a+W,d=c+1;if(!(ok[a]&&ok[b]&&ok[c]&&ok[d]))continue;\r\n  if(Math.hypot(i+.5-R,j+.5-R)>R)continue;\r\n  const mx=(pos[a*3]+pos[d*3])/2,mz=(pos[a*3+2]+pos[d*3+2])/2;if(inset&&mx>inset.x0&&mx<inset.x1&&mz>inset.z0&&mz<inset.z1){punched++;continue;}\r\n  idx.push(a,c,b,b,c,d);} // wound to face +y (z grows south, x east)\r\n return {positions:pos,colors:col,index:idx,step,radiusM:R*step,size:W,punched,tris:idx.length/3};\r\n}\r\n\r\nexport function createFarRing(T,{material,...opts}){\r\n const r=buildFarRing(opts),g=new T.BufferGeometry();\r\n g.setAttribute('position',new T.BufferAttribute(r.positions,3));g.setAttribute('color',new T.BufferAttribute(r.colors,3));\r\n g.setIndex(new T.BufferAttribute(Uint32Array.from(r.index),1));g.computeVertexNormals();g.computeBoundingSphere();\r\n const mesh=new T.Mesh(g,material);mesh.name='far-ring';mesh.raycast=()=>{}; // scenery only: never takes a tap\r\n const group=new T.Group();group.name='far-ring-root';group.add(mesh);\r\n return {group,mesh,stats:{tris:r.tris,radiusM:r.radiusM,step:r.step,punched:r.punched},dispose(){g.dispose();group.removeFromParent();}};\r\n}\r\n","modules/coach-world-rts/flag.mjs":"// Client-only visual flag for the RTS layer. ON by default (Ian 17 Oct). Kill switch: ?rts=0 (remembered on this\r\n// device as localStorage cw-rts=0 so War Room round trips stay off), ?rts=1 clears it.\r\n// Never authorises a server mutation (server gates on WORLD_RTS=1).\r\nexport function rtsFlag(){\r\n let q=null;try{q=new URLSearchParams(globalThis.location?.search||'').get('rts');}catch{}\r\n try{const ls=globalThis.localStorage;if(q==='0')ls?.setItem?.('cw-rts','0');else if(q==='1')ls?.removeItem?.('cw-rts');}catch{}\r\n if(q==='0')return false;if(q==='1')return true;\r\n try{return globalThis.localStorage?.getItem('cw-rts')!=='0';}catch{return true;}\r\n}\r\n// R52 3D main base. ON by default (with the RTS layer). Kill switch: ?rts3d=0 (remembered as localStorage cw-rts3d=0), ?rts3d=1 clears it.\r\nexport function rts3dFlag(){\r\n let q=null;try{q=new URLSearchParams(globalThis.location?.search||'').get('rts3d');}catch{}\r\n try{const ls=globalThis.localStorage;if(q==='0')ls?.setItem?.('cw-rts3d','0');else if(q==='1')ls?.removeItem?.('cw-rts3d');}catch{}\r\n if(q==='0')return false;if(q==='1')return true;\r\n try{return globalThis.localStorage?.getItem('cw-rts3d')!=='0';}catch{return true;}\r\n}\r\n","modules/coach-world-rts/fog-banks.mjs":"// V2 Fog of Change banks (Daggerfall look): one instanced draw of soft drifting cards with DFO's ordered dissolve (bayer4, MIT, DFO Online\r\n// src/render/orderedDither.js @e3054735; see coach-world/vendor/dfo-LICENSE.txt). Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v2/card.mjs F),\r\n// validated (contract, roll-in/clear timing, cap, determinism, headless WebGL compile + render, 1 draw); state half rewritten on review.\r\n// createFogBanks(T,{max,color,opacity,seed,fadeNear,fadeFar,clearSeconds}) -> {object,setBanks([{id,x,y,z,r}]),clear(id),has(id),update(t,cam),dispose(),stats()}\r\nexport function createFogBanks(T, opts = {}) {\r\n  const {\r\n    max = 64,\r\n    color = [0.62, 0.66, 0.7],\r\n    opacity = 0.85,\r\n    seed = 1,\r\n    fadeNear = 600,\r\n    fadeFar = 2400,\r\n    clearSeconds = 1.6\r\n  } = opts;\r\n\r\n  // ---------- PRNG ----------\r\n  let _seed = seed;\r\n  function rand() {\r\n    const x = Math.sin(_seed++) * 43758.5453123;\r\n    return x - Math.floor(x);\r\n  }\r\n\r\n  // ---------- Geometry & Material ----------\r\n  const geom = new T.PlaneGeometry(1, 1);\r\n\r\n  const material = new T.ShaderMaterial({\r\n    transparent: true,\r\n    depthWrite: false,\r\n    fog: false,\r\n    uniforms: {\r\n      uTime: { value: 0 },\r\n      uColor: { value: new T.Color(...color) },\r\n      uOpacity: { value: opacity },\r\n      uFadeNear: { value: fadeNear },\r\n      uFadeFar: { value: fadeFar }\r\n    },\r\n    vertexShader: `\r\n      uniform float uTime;\r\n      attribute vec4 aCard;\r\n      attribute float aPhase, aClear;\r\n      varying vec2 vUv;\r\n      varying float vClear, vDist, vPhase;\r\n\r\n      void main() {\r\n        vec3 c = aCard.xyz;\r\n        c.x += sin(uTime*0.07 + aPhase*6.283185307179586)*aCard.w*0.12;\r\n        c.z += cos(uTime*0.05 + aPhase*4.0)*aCard.w*0.1;\r\n\r\n        float s = length(modelMatrix[0].xyz);\r\n        vec4 mv = modelViewMatrix * vec4(c, 1.0);\r\n        mv.xy += position.xy * aCard.w * s * vec2(1.0, 0.55);\r\n\r\n        gl_Position = projectionMatrix * mv;\r\n        vUv = uv;\r\n        vClear = aClear;\r\n        vDist = -mv.z;\r\n        vPhase = aPhase;\r\n      }\r\n    `,\r\n    fragmentShader: `\r\n      varying vec2 vUv;\r\n      varying float vClear, vDist, vPhase;\r\n\r\n      uniform vec3 uColor;\r\n      uniform float uOpacity;\r\n      uniform float uFadeNear;\r\n      uniform float uFadeFar;\r\n      uniform float uTime;\r\n\r\n      float bayer4(vec2 p) {\r\n        int x = int(mod(p.x, 4.0)), y = int(mod(p.y, 4.0));\r\n        int z = x ^ y;\r\n        return float(((z & 1) << 3) | ((y & 1) << 2) | (z & 2) | ((y >> 1) & 1)) / 16.0;\r\n      }\r\n\r\n      void main() {\r\n        float d = length(vUv - 0.5) * 2.0;\r\n        float soft = 1.0 - smoothstep(0.35, 1.0, d);\r\n        float lobes = 0.75 + 0.25 * sin(atan(vUv.y - 0.5, vUv.x - 0.5) * 3.0 + vPhase * 6.283185307179586 + uTime * 0.1);\r\n        float a = soft * lobes * uOpacity * (1.0 - smoothstep(uFadeNear, uFadeFar, vDist));\r\n\r\n        if (vClear > 0.0 && bayer4(gl_FragCoord.xy) + 0.03125 >= 1.0 - vClear) discard;\r\n        if (a < 0.01) discard;\r\n\r\n        gl_FragColor = vec4(uColor, a);\r\n      }\r\n    `\r\n  });\r\n\r\n  // ---------- Instanced Mesh ----------\r\n  const mesh = new T.InstancedMesh(geom, material, max);\r\n  mesh.name = 'fog-banks';\r\n  mesh.frustumCulled = false;\r\n  mesh.renderOrder = 2;\r\n  mesh.count = 0;\r\n\r\n  // ---------- Instance Attributes ----------\r\n  const aCardAttr = new T.InstancedBufferAttribute(new Float32Array(max * 4), 4);\r\n  const aPhaseAttr = new T.InstancedBufferAttribute(new Float32Array(max), 1);\r\n  const aClearAttr = new T.InstancedBufferAttribute(new Float32Array(max), 1);\r\n  aClearAttr.setUsage(T.DynamicDrawUsage);\r\n\r\n  geom.setAttribute('aCard', aCardAttr);\r\n  geom.setAttribute('aPhase', aPhaseAttr);\r\n  geom.setAttribute('aClear', aClearAttr);\r\n\r\n  // ---------- State ----------  (review rewrite: cards live on their bank and are packed compactly whenever the set changes;\r\n  // the card version scattered offsets in metres not radii, leaked slots on removal and stored phases in radians)\r\n  const banks = new Map();          // id -> {cards: [[x,y,z,w,phase]], target, clear}\r\n  let prevTime = null, dirty = false;\r\n  function pack() {\r\n    let n = 0;\r\n    for (const bank of banks.values()) for (const c of bank.cards) {\r\n      aCardAttr.array.set(c.slice(0, 4), n * 4); aPhaseAttr.array[n] = c[4]; aClearAttr.array[n] = bank.clear; n++;\r\n    }\r\n    mesh.count = n; aCardAttr.needsUpdate = aPhaseAttr.needsUpdate = aClearAttr.needsUpdate = true; dirty = false;\r\n  }\r\n  const used = () => { let n = 0; for (const b of banks.values()) n += b.cards.length; return n; };\r\n  function setBanks(list) {\r\n    const keep = new Set();\r\n    for (const { id, x, y = 0, z, r } of list) {\r\n      keep.add(id);\r\n      const bank = banks.get(id);\r\n      if (bank) { bank.target = 0; continue; }\r\n      const n = Math.max(1, Math.min(Math.round(r / 40), 6)), cards = [];\r\n      for (let i = 0, room = max - used(); i < n && i < room; i++) {\r\n        const ox = (rand() * 1.2 - 0.6) * r, oz = (rand() * 1.2 - 0.6) * r, size = r * (0.7 + 0.4 * rand());\r\n        cards.push([x + ox, y + size * 0.25, z + oz, size, rand()]);\r\n      }\r\n      if (cards.length) { banks.set(id, { cards, target: 0, clear: 1 }); dirty = true; } // a new bank rolls in from clear\r\n    }\r\n    for (const [id, bank] of banks) if (!keep.has(id)) bank.target = 1;\r\n  }\r\n  function clear(id) { const bank = banks.get(id); if (bank) bank.target = 1; }\r\n  function has(id) { return banks.has(id); }\r\n  function update(t, camera) {\r\n    material.uniforms.uTime.value = t;\r\n    const dt = prevTime === null ? 0 : Math.max(0, Math.min(t - prevTime, 0.25));\r\n    prevTime = t;\r\n    let moved = false;\r\n    for (const [id, bank] of banks) {\r\n      if (bank.clear === bank.target) continue;\r\n      const step = dt / clearSeconds;\r\n      bank.clear = bank.target > bank.clear ? Math.min(bank.target, bank.clear + step) : Math.max(bank.target, bank.clear - step);\r\n      moved = true;\r\n      if (bank.target === 1 && bank.clear >= 1) { banks.delete(id); dirty = true; }\r\n    }\r\n    if (dirty) { pack(); return; }\r\n    if (!moved) return;\r\n    let n = 0;\r\n    for (const bank of banks.values()) for (let k = 0; k < bank.cards.length; k++) aClearAttr.array[n++] = bank.clear;\r\n    aClearAttr.needsUpdate = true;\r\n  }\r\n  function dispose() {\r\n    geom.dispose();\r\n    material.dispose();\r\n  }\r\n\r\n  function stats() {\r\n    return { calls: mesh.count > 0 ? 1 : 0, banks: banks.size, cards: mesh.count };\r\n  }\r\n\r\n  return {\r\n    object: mesh,\r\n    setBanks,\r\n    clear,\r\n    has,\r\n    update,\r\n    dispose,\r\n    stats\r\n  };\r\n}\r\n","modules/coach-world-rts/fog-law.mjs":"// Portions from DFO Online (MIT, (c) 2026 Lattymoy) src/render/horizonHaze.js + src/render/fogGlsl.js + src/render/hazeGlsl.js\r\n// (V5: hazeMean/hazeAt height haze) + src/world/weather.js (V5: FOG_SETTINGS exp densities) @e3054735;\r\n// DFU-derived parts (MIT, (c) 2009-2023 Daggerfall Workshop). See vendor/dfo-LICENSE.txt.\r\n// adapted: Three.js ShaderChunk override (fog_* chunks), radial eye distance from mvPosition, exp2 weather fog feeds the same hold/rim law.\r\n//\r\n// V1 shared fog law. Every built-in material (Lambert/Basic/Points/Sprite/PS1) fogs through one chunk:\r\n//   mix = min( min(ramp, hold) + (1-hold) * smoothstep(rimStart, rimEnd, d), 1 )     (DFO horizonHaze)\r\n//   ramp = linear (fogNear..fogFar) for THREE.Fog, 1-exp(-(density*d)^2) for THREE.FogExp2 (DFO weatherFogAt modes 1 / 3)\r\n// d is the radial eye distance (DFO fogs by length(worldPos-eye)), not view depth. The law is off (three's own fog, bit for bit)\r\n// unless the scene being rendered opted in with useFogLaw(): one shared uniform object, switched on in scene.onBeforeRender\r\n// and off again in onAfterRender, so other three scenes in the app never change.\r\n// V5 adds two transmittances on top, as DFO's ringHaze does: 1-(1-mix)*tWeather*tHaze, both measured from an ORIGIN (the eye in\r\n// flight, the view's ground focus in the RTS view, like DFO's travel view): weather exp2 tW=exp(-(density*run)^2) and the height\r\n// haze tH=hazeAt (an exponential layer over a floor height: valleys fill, ridges stand clear).\r\n// V9 retro shading (ours; DFO/DFU have no such table): the law's fog factor is posterised to cwFogR.x steps (64) through DFO's bayer4\r\n// ordered dither (orderedDither.js @e3054735, MIT), so distance falls off in visible pixel bands, and a near \"ColorBoost\" (saturation and\r\n// a little contrast, strength cwFogR.y) fades out with the fog so close ground pops and far ground recedes. Both only under the law.\r\nexport const RETRO_STEPS=64,COLOR_BOOST=.55;\r\nexport const HOLD=.85,RIM_START=.6; // DFO RING_HAZE_HOLD; RIM_START is DFO's RING_RIM_START = .6 * RING_RIM_END\r\n\r\n// DFO horizonHaze, with the ramp passed in so exp2 weather fog can take the same hold and rim.\r\nconst HAZE_GLSL=`\r\nfloat cwHorizonHaze(float ramp, float d) {\r\n  return min(min(ramp, cwFogP.y) + (1.0 - cwFogP.y) * smoothstep(cwFogP.z, cwFogP.w, d), 1.0);\r\n}\r\nfloat cwBayer4(vec2 p) { // DFO bayer4 (bit-interleaved 4x4), 0..15/16\r\n  int x = int(mod(p.x, 4.0)), y = int(mod(p.y, 4.0));\r\n  int z = x ^ y;\r\n  return float(((z & 1) << 3) | ((y & 1) << 2) | (z & 2) | ((y >> 1) & 1)) / 16.0;\r\n}\r\nfloat cwHazeMean(float a, float b) { // DFO hazeMean: mean density over a ray from height a to b (in falloffs over the floor)\r\n  float lo = min(a, b), hi = max(a, b);\r\n  if (hi <= 0.0) return 1.0;\r\n  float span = hi - lo;\r\n  if (span < 1e-4) return exp(-max(lo, 0.0));\r\n  float lo0 = max(lo, 0.0);\r\n  return ((lo0 - lo) + (exp(-lo0) - exp(-hi))) / span;\r\n}\r\nfloat cwAtmos(vec3 p) { // weather x height-haze transmittance from the origin to the view-space point p\r\n  vec3 r = p - cwFogO.xyz; float run = length(r), t = 1.0;\r\n  if (cwFogW.x > 0.0) { float w = cwFogW.x * run; t *= exp(-w * w); }\r\n  if (cwFogW.y > 0.0) { float h = run - cwFogU.w; if (h > 0.0) t *= exp(-cwFogW.y * h * cwHazeMean((cwFogO.w - cwFogW.z) * cwFogW.w, (cwFogO.w + dot(r, cwFogU.xyz) - cwFogW.z) * cwFogW.w)); }\r\n  return t;\r\n}`;\r\nexport const FOG_CHUNKS={\r\n fog_pars_vertex:`\r\n#ifdef USE_FOG\r\n\tvarying float vFogDepth;\r\n\tvarying vec3 vCwFogPos;\r\n#endif\r\n`,\r\n fog_vertex:`\r\n#ifdef USE_FOG\r\n\tvFogDepth = - mvPosition.z;\r\n\tvCwFogPos = mvPosition.xyz; // view-space point: linear, so it interpolates exactly over big triangles (a length would not)\r\n#endif\r\n`,\r\n fog_pars_fragment:`\r\n#ifdef USE_FOG\r\n\tuniform vec3 fogColor;\r\n\tvarying float vFogDepth;\r\n\tvarying vec3 vCwFogPos;\r\n\tuniform vec4 cwFogP; // x on, y hold, z rim start, w rim end (scene units)\r\n\tuniform vec4 cwFogW; // V5: x weather exp2 density, y haze floor extinction, z haze floor world y, w 1/haze falloff (all per scene unit)\r\n\tuniform vec4 cwFogO; // V5: xyz origin in view space, w origin world y\r\n\tuniform vec4 cwFogU; // V5: xyz world up in view space, w haze clear start\r\n\tuniform vec4 cwFogR; // V9: x ramp steps (0 = smooth), y ColorBoost strength\r\n\t#ifdef FOG_EXP2\r\n\t\tuniform float fogDensity;\r\n\t#else\r\n\t\tuniform float fogNear;\r\n\t\tuniform float fogFar;\r\n\t#endif\r\n${HAZE_GLSL}\r\n#endif\r\n`,\r\n fog_fragment:`\r\n#ifdef USE_FOG\r\n\t#ifdef FOG_EXP2\r\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\r\n\t\tif ( cwFogP.x > 0.5 ) { float cwD = length( vCwFogPos ), cwF = fogDensity * cwD; fogFactor = cwHorizonHaze( 1.0 - exp( - cwF * cwF ), cwD ); }\r\n\t#else\r\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\r\n\t\tif ( cwFogP.x > 0.5 ) { float cwD = length( vCwFogPos ); fogFactor = cwHorizonHaze( clamp( ( cwD - fogNear ) / max( fogFar - fogNear, 1.0 ), 0.0, 1.0 ), cwD ); }\r\n\t#endif\r\n\tif ( cwFogP.x > 0.5 ) {\r\n\t\tfogFactor = 1.0 - ( 1.0 - fogFactor ) * cwAtmos( vCwFogPos );\r\n\t\tif ( cwFogR.x > 0.5 ) fogFactor = clamp( floor( fogFactor * cwFogR.x + cwBayer4( gl_FragCoord.xy ) ) / cwFogR.x, 0.0, 1.0 ); // 64-step dithered ramp\r\n\t\tif ( cwFogR.y > 0.0 ) { float cwLum = dot( gl_FragColor.rgb, vec3( 0.299, 0.587, 0.114 ) ); vec3 cwBoost = clamp( ( mix( vec3( cwLum ), gl_FragColor.rgb, 1.3 ) - 0.5 ) * 1.08 + 0.5, 0.0, 1.0 );\r\n\t\t\tgl_FragColor.rgb = mix( gl_FragColor.rgb, cwBoost, cwFogR.y * ( 1.0 - fogFactor ) ); } // ColorBoost near, gone in the fog\r\n\t}\r\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\r\n#endif\r\n`};\r\n\r\n// One shared value object. A plain {x,y,z,w} (not a Vector4) survives three's UniformsUtils.clone by reference, so every\r\n// material compiled after installFogLaw reads the same object and one write per render updates them all.\r\nexport const cwFog={x:0,y:HOLD,z:0,w:1},cwFogW={x:0,y:0,z:0,w:1},cwFogO={x:0,y:0,z:0,w:0},cwFogU={x:0,y:1,z:0,w:0},cwFogR={x:0,y:0,z:0,w:0};\r\n\r\nexport function installFogLaw(T){\r\n if(T.ShaderChunk.__cwFog)return false;\r\n Object.assign(T.ShaderChunk,FOG_CHUNKS);T.ShaderChunk.__cwFog=true;\r\n const add=u=>{if(u&&'fogColor' in u&&!u.cwFogP)Object.assign(u,{cwFogP:{value:cwFog},cwFogW:{value:cwFogW},cwFogO:{value:cwFogO},cwFogU:{value:cwFogU},cwFogR:{value:cwFogR}});};\r\n add(T.UniformsLib?.fog);for(const k of Object.keys(T.ShaderLib||{}))add(T.ShaderLib[k].uniforms);\r\n return true;\r\n}\r\n\r\n// Opt a scene in. opts: {hold, rim:[start,end]} (scene units) or rim omitted = [max(near,.6*far), far] each render (THREE.Fog only).\r\n// V5: weather (exp2 density per scene unit, 0 none), haze {extinction, floor, falloff, clear} (per scene unit / world y) or null,\r\n// origin (world Vector3 the atmosphere is measured from; null = the eye). Returns the live opts object; callers change it any time.\r\nexport function useFogLaw(T,scene,opts={}){\r\n installFogLaw(T);const o=Object.assign({hold:HOLD,rim:null,weather:0,haze:null,origin:null,retro:{steps:RETRO_STEPS,boost:COLOR_BOOST}},opts),pb=scene.onBeforeRender,pa=scene.onAfterRender,v=new T.Vector3();\r\n scene.onBeforeRender=(...a)=>{pb?.apply(scene,a);const f=scene.fog,cam=a[2];cwFog.x=f?1:0;cwFog.y=o.hold;\r\n  const [s,e]=o.rim||(f?.isFog?[Math.max(f.near,RIM_START*f.far),f.far]:[1e9,1e9+1]);cwFog.z=s;cwFog.w=e;\r\n  const h=o.haze;cwFogR.x=o.retro?o.retro.steps|0:0;cwFogR.y=o.retro?+o.retro.boost||0:0;cwFogW.x=Math.max(0,+o.weather||0);cwFogW.y=h?h.extinction:0;cwFogW.z=h?h.floor:0;cwFogW.w=h?1/h.falloff:1;cwFogU.w=h?.clear||0;\r\n  if(cam?.matrixWorldInverse){const M=cam.matrixWorldInverse;\r\n   if(o.origin){v.copy(o.origin);cwFogO.w=v.y;v.applyMatrix4(M);}else{v.set(0,0,0);cwFogO.w=cam.getWorldPosition?cam.getWorldPosition(v).y:0;v.set(0,0,0);}\r\n   cwFogO.x=v.x;cwFogO.y=v.y;cwFogO.z=v.z;v.set(0,1,0).transformDirection(M);cwFogU.x=v.x;cwFogU.y=v.y;cwFogU.z=v.z;}};\r\n scene.onAfterRender=(...a)=>{cwFog.x=0;pa?.apply(scene,a);};\r\n return scene.userData.cwFog=o;\r\n}\r\n// JS twin of the V9 ramp: posterise f to `steps` with a bayer threshold t (0..15/16).\r\nexport const retroRamp=(f,t,steps=RETRO_STEPS)=>steps?Math.min(1,Math.max(0,Math.floor(f*steps+t)/steps)):f;\r\n// JS twins of cwHazeMean / cwAtmos (tests and CPU checks). oy/py: world heights of origin and point, run: their distance.\r\nexport function hazeMean(a,b){const lo=Math.min(a,b),hi=Math.max(a,b);if(hi<=0)return 1;const span=hi-lo;if(span<1e-4)return Math.exp(-Math.max(lo,0));const lo0=Math.max(lo,0);return ((lo0-lo)+(Math.exp(-lo0)-Math.exp(-hi)))/span;}\r\nexport function atmosTransmit(run,oy,py,{weather=0,haze=null}={}){\r\n let t=Math.exp(-((weather*run)**2));\r\n if(haze){const h=run-(haze.clear||0);if(h>0)t*=Math.exp(-haze.extinction*h*hazeMean((oy-haze.floor)/haze.falloff,(py-haze.floor)/haze.falloff));}\r\n return t;\r\n}\r\n\r\n// JS twin of the fragment law (tests and CPU checks). fog: {near,far} (linear) or {density} (exp2).\r\nexport function fogLawMix(d,fog,{hold=HOLD,rim=null}={}){\r\n const sm=(a,b,x)=>{const t=Math.min(1,Math.max(0,(x-a)/(b-a||1e-9)));return t*t*(3-2*t);};\r\n const ramp=fog.density!=null?1-Math.exp(-((fog.density*d)**2)):Math.min(1,Math.max(0,(d-fog.near)/Math.max(fog.far-fog.near,1)));\r\n const [s,e]=rim||(fog.density!=null?[1e9,1e9+1]:[Math.max(fog.near,RIM_START*fog.far),fog.far]);\r\n return Math.min(Math.min(ramp,hold)+(1-hold)*sm(s,e,d),1);\r\n}\r\n\r\n// Load veil (ours): fog far pulled toward near while the world owes parts, eased so it recedes smoothly as they land.\r\nexport const veilFar=(near,far,veil,close)=>far+(Math.min(far,near+close)-far)*Math.min(1,Math.max(0,veil));\r\n","modules/coach-world-rts/fx.mjs":"// W1 fx: shadow, SC-style selection ring, segmented health bar, spark particles (cap 64). Pure canvas, no state besides particles.\r\nexport const MAX_PARTICLES=64;\r\nexport function shadow(ctx,x,y,w,fill='#0008'){ctx.fillStyle=fill;ctx.beginPath();ctx.ellipse(x,y,w/2,Math.max(2,w/5),0,0,Math.PI*2);ctx.fill();}\r\nexport function ring(ctx,x,y,w,color,selected){ctx.lineWidth=selected?1.5:1;ctx.strokeStyle=selected?'#54ff7c':color;ctx.globalAlpha=selected?1:.45;ctx.beginPath();ctx.ellipse(x,y,w/2,Math.max(2.5,w/4),0,0,Math.PI*2);ctx.stroke();ctx.globalAlpha=1;}\r\n// segments of 3 px per 1 px gap, SC1 order: green>yellow>red by fill\r\nexport function healthBar(ctx,x,y,w,frac){const n=Math.max(2,Math.floor(w/3)),on=Math.ceil(Math.max(0,Math.min(1,frac))*n),sw=w/n;ctx.fillStyle='#000';ctx.fillRect(Math.round(x-w/2-1),y-1,w+2,5);ctx.fillStyle=frac>.66?'#2fdc4a':frac>.33?'#e8d02a':'#e0382a';for(let i=0;i<on;i++)ctx.fillRect(Math.round(x-w/2+i*sw),y,Math.max(1,Math.floor(sw)-1),3);ctx.fillStyle='#000';for(let i=on;i<n;i++)ctx.fillRect(Math.round(x-w/2+i*sw),y,Math.max(1,Math.floor(sw)-1),3);}\r\nexport function createFx({onSpark,reduced=()=>false}={}){\r\n const parts=[];\r\n return {parts,\r\n  spark(x,y,n=4){onSpark?.(x,y);if(reduced())n=1;for(let i=0;i<n;i++){if(parts.length>=MAX_PARTICLES)parts.shift();parts.push({x,y,vx:(Math.random()-.5)*40,vy:-20-Math.random()*30,life:8});}},\r\n  step(){for(const p of parts){p.x+=p.vx*.042;p.y+=p.vy*.042;p.vy+=4;p.life--;}for(let i=parts.length-1;i>=0;i--)if(parts[i].life<=0)parts.splice(i,1);},\r\n  draw(ctx){ctx.fillStyle='#ffe28a';for(const p of parts)ctx.fillRect(Math.round(p.x),Math.round(p.y),2,2);},\r\n };\r\n}\r\n","modules/coach-world-rts/fx3d.mjs":"// R53 element attacks in the 3D base. Same specs as 2D (elements.mjs effectSpec).\r\n// createFx3d(T,{scale,origin,cap}) -> {group,fire(fromTile,toTile,factionOrTower),update(now),count(),dispose}  (tile coords in, metres out)\r\n// mountFx3d(handle,{scale,origin}) wires it through the base3d handle: addToWorld + onFrame, and exposes handle.fx3d for callers (raid, practice).\r\nimport { effectSpec, ENGINE_REDUCED } from './elements.mjs';\r\nimport { SCALE, spawnTile } from './base-meshes.mjs';\r\n\r\nexport function createFx3d(T, { scale = 3, origin = { x: 0, y: 0 }, cap = 64, now = () => performance.now(), reduced = ENGINE_REDUCED } = {}) {\r\n  const group = new T.Group(), live = [], bases = new Map();\r\n  group.name = 'rts-element-fx';\r\n  const W = (p) => ({ x: (p.x + .5 - origin.x) * scale, z: (p.y + .5 - origin.y) * scale }), Y = .6 * scale;\r\n  const colour = c => new T.Color(c);\r\n  const mat = (c, extra) => new T.MeshBasicMaterial({ color: colour(c), transparent: true, blending: T.AdditiveBlending, depthWrite: false, depthTest: false, fog: false, toneMapped: false, side: T.DoubleSide, ...extra });\r\n  const glow = (c, size) => { const s = new T.Sprite(new T.SpriteMaterial({ color: colour(c), transparent: true, blending: T.AdditiveBlending, depthWrite: false, depthTest: false, fog: false, toneMapped: false })); s.scale.set(size, size, 1); s.renderOrder = 6; return s; };\r\n  function sheetTexture(e) { // one clone per effect so frame offsets are independent; loaded lazily and never throws without a DOM\r\n    let b = bases.get(e.url);\r\n    if (b === undefined) { try { b = new T.TextureLoader().load(e.url); b.magFilter = b.minFilter = T.NearestFilter; b.generateMipmaps = false; b.colorSpace = T.SRGBColorSpace; } catch { b = null; } bases.set(e.url, b); }\r\n    return b?.image ? b : null;\r\n  }\r\n\r\n  function fire(from, to, factionOrTower) {\r\n    const spec = effectSpec(factionOrTower, reduced());\r\n    if (!spec) return false;\r\n    const a = W(from), b = W(to), e = { ...spec, a, b, start: now(), objs: [] };\r\n    if (spec.type === 'laser') {\r\n      const g = new T.BufferGeometry().setFromPoints([new T.Vector3(a.x, Y, a.z), new T.Vector3(b.x, Y, b.z)]);\r\n      const core = new T.Line(g, new T.LineBasicMaterial({ color: colour(spec.color), transparent: true, blending: T.AdditiveBlending, depthTest: false, fog: false }));\r\n      const dx = b.x - a.x, dz = b.z - a.z, l = Math.hypot(dx, dz) || 1, w = .12 * scale, px = -dz / l * w, pz = dx / l * w;\r\n      const q = new T.BufferGeometry().setFromPoints([[a.x + px, a.z + pz], [a.x - px, a.z - pz], [b.x + px, b.z + pz], [a.x - px, a.z - pz], [b.x - px, b.z - pz], [b.x + px, b.z + pz]].map(([x, z]) => new T.Vector3(x, Y, z)));\r\n      const strip = new T.Mesh(q, mat(spec.color)); core.renderOrder = strip.renderOrder = 6; strip.material.opacity = .35;\r\n      const end = glow(spec.color, .9 * scale); end.position.set(b.x, Y, b.z);\r\n      e.objs.push(core, strip, end);\r\n    } else if (spec.type === 'rocks') {\r\n      for (let i = 0; i < 3; i++) { const m = new T.Mesh(new T.BoxGeometry(.14 * scale, .14 * scale, .14 * scale), new T.MeshBasicMaterial({ color: colour(spec.color), transparent: true, fog: false })); m.renderOrder = 6; e.objs.push(m); }\r\n    } else if (spec.type === 'flash') {\r\n      const f = glow(spec.color, scale); f.position.set(b.x, Y, b.z); e.objs.push(f);\r\n    } else {\r\n      const s = new T.Sprite(new T.SpriteMaterial({ transparent: true, blending: T.AdditiveBlending, depthWrite: false, depthTest: false, fog: false, toneMapped: false }));\r\n      s.scale.set(2 * scale, 2 * scale, 1); s.position.set(b.x, Y + .3 * scale, b.z); s.renderOrder = 6; s.visible = false; e.objs.push(s);\r\n    }\r\n    group.add(...e.objs); live.push(e);\r\n    while (live.length > cap) kill(live.shift());\r\n    return true;\r\n  }\r\n  function kill(e) { for (const o of e.objs) { o.removeFromParent(); o.geometry?.dispose(); o.material?.map?.dispose?.(); o.material?.dispose(); } }\r\n\r\n  function update(t = now()) {\r\n    for (let i = live.length - 1; i >= 0; i--) {\r\n      const e = live[i], el = t - e.start, p = Math.min(1, el / e.duration);\r\n      if (el > e.duration) { kill(e); live.splice(i, 1); continue; }\r\n      if (e.type === 'laser') { e.objs[0].material.opacity = 1 - p; e.objs[1].material.opacity = .35 * (1 - p); e.objs[2].material.opacity = 1 - p; }\r\n      else if (e.type === 'flash') e.objs[0].material.opacity = 1 - p;\r\n      else if (e.type === 'rocks') {\r\n        const dx = e.b.x - e.a.x, dz = e.b.z - e.a.z, l = Math.hypot(dx, dz) || 1, arc = Math.sin(Math.PI * p) * 1.2 * scale;\r\n        e.objs.forEach((m, i2) => { const k = Math.min(1, Math.max(0, p - (i2 - 1) * .04)); m.position.set(e.a.x + dx * k - dz / l * arc * .3, Y + Math.sin(Math.PI * k) * arc * .6, e.a.z + dz * k + dx / l * arc * .3); m.rotation.set(el * .01 * (i2 + 1), el * .008, 0); m.material.opacity = el > e.duration - 100 ? (e.duration - el) / 100 : 1; });\r\n      } else {\r\n        const s = e.objs[0], b = e.tex || (e.tex = sheetTexture(e));\r\n        if (!b) continue;\r\n        if (!s.material.map) { s.material.map = b.clone(); s.material.map.needsUpdate = true; s.material.map.repeat.set(1 / e.cols, 1 / e.rows); s.material.needsUpdate = true; s.visible = true; }\r\n        const f = Math.min(e.frames - 1, Math.floor(el / e.frameDuration));\r\n        s.material.map.offset.set((f % e.cols) / e.cols, 1 - (Math.floor(f / e.cols) + 1) / e.rows);\r\n      }\r\n    }\r\n  }\r\n  return { group, fire, update, count: () => live.length, dispose() { while (live.length) kill(live.pop()); group.removeFromParent(); for (const b of bases.values()) b?.dispose(); bases.clear(); } };\r\n}\r\n\r\nexport function mountFx3d(handle, { scale = SCALE, origin = spawnTile(), cap } = {}) {\r\n  let fx = null, off = null, gone = false;\r\n  handle.ready.then(r => {\r\n    if (gone || !r) return;\r\n    fx = createFx3d(r.T, { scale, origin, cap });\r\n    handle.addToWorld(fx.group); handle.fx3d = fx; off = handle.onFrame(t => fx.update());\r\n  });\r\n  return () => { gone = true; off?.(); fx?.dispose(); if (handle.fx3d === fx) delete handle.fx3d; };\r\n}\r\n","modules/coach-world-rts/geo-follow.mjs":"// W7 geo-follow (client). attachFollow(map,{subscribe,signal}) rides the ONE existing map-view watcher (S4 fan-out).\r\n// Never calls watchPosition, never prompts: the marker appears only after a fix arrives, i.e. after the user started a walk.\r\nimport {FACTIONS} from '../coach-world/catalog.mjs';\r\nimport {sectorOf,facingFor,walkFrame} from './clips.mjs';\r\n\r\nexport const CFG=Object.freeze({minCutoff:.8,beta:.01,dCutoff:1,maxAcc:50,extrapMs:1500,easeMs:300,staleMs:30000,walkSpeed:.7,headingEma:.15,headingDead:2,zoom:16.75,pitch:58,strideM:1.5});\r\nconst M_LAT=111320,RAD=Math.PI/180;\r\n\r\n// ---- math (pure) ----\r\nexport const normDeg=d=>((d%360)+360)%360;\r\nexport const angleDelta=(from,to)=>{const d=normDeg(to-from);return d>180?d-360:d;}; // signed shortest turn, -180..180\r\n// circular EMA with dead band: wrap-safe (359 -> 1 turns +2, never spins), jitter under `dead` degrees is ignored\r\nexport function smoothHeading(cur,target,k=CFG.headingEma,dead=CFG.headingDead){\r\n if(!Number.isFinite(target))return cur;if(!Number.isFinite(cur))return normDeg(target);\r\n const d=angleDelta(cur,target);return Math.abs(d)<dead?cur:normDeg(cur+d*k);\r\n}\r\n// iOS: webkitCompassHeading is already clockwise from north. Android absolute: alpha is counter-clockwise, add screen rotation.\r\nexport function compassHeading(ev,screenAngle=0){\r\n if(Number.isFinite(ev?.webkitCompassHeading))return normDeg(ev.webkitCompassHeading);\r\n if(ev?.absolute!==false&&Number.isFinite(ev?.alpha))return normDeg(360-ev.alpha+screenAngle);\r\n return null;\r\n}\r\nexport function createOneEuro({minCutoff=CFG.minCutoff,beta=CFG.beta,dCutoff=CFG.dCutoff}={}){\r\n let x=null,dx=0,t=null;const a=(fc,dt)=>1/(1+1/(2*Math.PI*fc)/dt);\r\n return {filter(v,time,mc=minCutoff){\r\n  if(x==null||time<=t){x=v;t=time;return v;}\r\n  const dt=Math.max((time-t)/1000,.001);dx+=a(dCutoff,dt)*((v-x)/dt-dx);x+=a(mc+beta*Math.abs(dx),dt)*(v-x);t=time;return x;}};\r\n}\r\nexport function circlePolygon(lat,lng,r,n=48){\r\n const dl=r/M_LAT,dg=r/(M_LAT*Math.cos(lat*RAD)||1),ring=[];\r\n for(let i=0;i<=n;i++){const a=i/n*2*Math.PI;ring.push([lng+Math.sin(a)*dg,lat+Math.cos(a)*dl]);}\r\n return {type:'Polygon',coordinates:[ring]};\r\n}\r\n\r\n// ---- track: filter + stationary hold + capped extrapolation + 300 ms ease ----\r\n// ponytail: stationary = RMS spread of last 8 fixes under half the reported accuracy; hold the centroid. At 1 Hz a 0.8 Hz one-euro\r\n// alone barely smooths, and jitter under the accuracy radius is not movement. Costs ~half a window of lag at poor accuracy.\r\nexport function createTrack(cfg={}){\r\n const C={...CFG,...cfg};let o=null,fx=null,fy=null,base=null,off=null,win=[],lastT=null,moveHeading=null,dist=0,last=null,weak=false;\r\n const toXY=(lat,lng)=>[(lng-o.lng)*M_LAT*Math.cos(o.lat*RAD),(lat-o.lat)*M_LAT];\r\n const toLL=(x,y)=>({latitude:o.lat+y/M_LAT,longitude:o.lng+x/(M_LAT*Math.cos(o.lat*RAD))});\r\n function raw(now){ // extrapolated position, never farther than the accuracy circle from the last filtered fix\r\n  let dt=Math.min(Math.max(now-base.t,0),C.extrapMs)/1000,dx=base.vx*dt,dy=base.vy*dt;const d=Math.hypot(dx,dy);\r\n  if(d>base.acc){dx*=base.acc/d;dy*=base.acc/d;}return [base.x+dx,base.y+dy];\r\n }\r\n const api={\r\n  get weak(){return weak;},get moveHeading(){return moveHeading;},get distance(){return dist;},get speed(){return base?Math.hypot(base.vx,base.vy):0;},get lastFixAt(){return lastT;},get hasFix(){return !!base;},\r\n  push(fix){\r\n   const lat=fix.latitude??fix.lat,lng=fix.longitude??fix.lng,acc=fix.accuracy,t=fix.sampledAt??Date.now();\r\n   if(!Number.isFinite(lat)||!Number.isFinite(lng)||!Number.isFinite(acc))return false;\r\n   lastT=t;weak=acc>C.maxAcc;if(weak)return false; // worse than 50 m: counts as a signal, never moves or shows the avatar\r\n   if(!o){o={lat,lng};fx=createOneEuro(C);fy=createOneEuro(C);}\r\n   const [x,y]=toXY(lat,lng),before=base?api.at(t):null;win.push([x,y,t]);if(win.length>8)win.shift();\r\n   const cx=win.reduce((s,p)=>s+p[0],0)/win.length,cy=win.reduce((s,p)=>s+p[1],0)/win.length,rms=Math.sqrt(win.reduce((s,p)=>s+(p[0]-cx)**2+(p[1]-cy)**2,0)/win.length);\r\n   const h=win.length>>1,A=win.slice(0,h),B=win.slice(h),mean=(a,i)=>a.reduce((s,p)=>s+p[i],0)/a.length,\r\n    span=win.length>=4?Math.max(.001,(mean(B,2)-mean(A,2))/1000):1,wx=win.length>=4?(mean(B,0)-mean(A,0))/span:0,wy=win.length>=4?(mean(B,1)-mean(A,1))/span:0,drift=Math.hypot(wx,wy); // m/s between window halves: jitter cancels, walking does not\r\n   const r=Math.min(8,Math.max(3,acc*.5)),w=win.length<4||rms>=r||fix.speed>C.walkSpeed?1:Math.min(1,Math.max(0,(drift-.8)/.8)),still=w<.5; // w: 0 = hold centroid, 1 = trust raw\r\n   const nx=fx.filter(cx+(x-cx)*w,t,.3+(C.minCutoff-.3)*w),ny=fy.filter(cy+(y-cy)*w,t,.3+(C.minCutoff-.3)*w);\r\n   let vx=0,vy=0;\r\n   if(base&&!still){vx=wx*w;vy=wy*w;dist+=Math.hypot(nx-base.x,ny-base.y);} // velocity from the window drift (smooth), not from fix-to-fix deltas (noise)\r\n   const sp=Number.isFinite(fix.speed)?fix.speed:Math.hypot(vx,vy);\r\n   if(sp>=C.walkSpeed&&(Math.hypot(vx,vy)>.05||Number.isFinite(fix.heading))){moveHeading=Number.isFinite(fix.heading)&&fix.speed>C.walkSpeed?normDeg(fix.heading):normDeg(Math.atan2(vx,vy)/RAD);}else moveHeading=null;\r\n   base={x:nx,y:ny,t,vx,vy,acc};\r\n   if(before){const [rx,ry]=raw(t);off={dx:before.x-rx,dy:before.y-ry,t};} // ease from where the avatar WAS drawn to where it should be\r\n   return true;\r\n  },\r\n  at(now){\r\n   if(!base)return null;let [x,y]=raw(now),e=0;\r\n   if(off){e=1-Math.min(Math.max((now-off.t)/C.easeMs,0),1);x+=off.dx*e;y+=off.dy*e;}\r\n   return {...toLL(x,y),x,y,accuracy:base.acc,stale:now-lastT>C.staleMs};\r\n  },\r\n };\r\n return api;\r\n}\r\n\r\n// ---- compass ----\r\nexport function createCompass(win=globalThis){\r\n let on=false,h=null,listeners=[],cb=null;\r\n const evName=()=>('ondeviceorientationabsolute' in win)?'deviceorientationabsolute':'deviceorientation';\r\n const handler=e=>{const v=compassHeading(e,win.screen?.orientation?.angle||0);if(v!=null){h=v;cb?.(v);}};\r\n return {\r\n  get active(){return on;},get heading(){return h;},\r\n  supported:()=>typeof win.DeviceOrientationEvent!=='undefined',\r\n  // must be called from a user gesture on iOS\r\n  async request(onHeading){\r\n   if(on)return true;cb=onHeading;\r\n   const D=win.DeviceOrientationEvent;\r\n   if(typeof D?.requestPermission==='function'){let r;try{r=await D.requestPermission();}catch{return false;}if(r!=='granted')return false;}\r\n   if(on)return true;const n=evName();win.addEventListener(n,handler);listeners.push(n);on=true;return true;\r\n  },\r\n  dispose(){for(const n of listeners)win.removeEventListener(n,handler);listeners=[];on=false;cb=null;},\r\n };\r\n}\r\n\r\n// ---- avatar images from the worker atlas (faction-dyed, 3x nearest-neighbour) ----\r\nconst SCALE=3,ICON_OFFSET_Y=-13*SCALE/2; // atlas anchor is (14,33) in a 28x40 cell: foot sits 13 px below the icon centre\r\nfunction frameImage(sheet,color,sector,frame){\r\n const A=sheet.atlas,sprite=sheet.dye(color);if(!sprite)return null;\r\n const {stored,mirror}=facingFor(sector,A.dirs),clip=frame<0?A.clips.idle:A.clips.walk,[rx,ry,rw,rh]=clip.rects[stored][frame<0?0:frame%clip.frames];\r\n const c=Object.assign(document.createElement('canvas'),{width:rw*SCALE,height:rh*SCALE}),g=c.getContext('2d');g.imageSmoothingEnabled=false;\r\n if(mirror){g.translate(c.width,0);g.scale(-1,1);}g.drawImage(sprite,rx,ry,rw,rh,0,0,c.width,c.height);\r\n return g.getImageData(0,0,c.width,c.height);\r\n}\r\nfunction arrowImage(sector){ // fallback when the atlas is missing: pixel arrow pointing along the sector\r\n const s=48,c=Object.assign(document.createElement('canvas'),{width:s,height:s}),g=c.getContext('2d');g.translate(s/2,s/2);g.rotate(sector*Math.PI/4);\r\n g.fillStyle='#ffd34e';g.strokeStyle='#142c32';g.lineWidth=4;g.beginPath();g.moveTo(0,-18);g.lineTo(12,14);g.lineTo(0,8);g.lineTo(-12,14);g.closePath();g.stroke();g.fill();return g.getImageData(0,0,s,s);\r\n}\r\n\r\nconst TEXT={\r\n denied:'Location is blocked. Turn it on in your browser settings to see yourself on the map.',\r\n stale:'Lost your location signal. Still looking...',\r\n weak:'GPS is weak here. Your marker will return when the signal improves.',\r\n resumed:'Signal is back.',\r\n compassOff:'Compass access was not granted. The marker will face the way you walk.',\r\n};\r\n\r\nexport async function attachFollow(map,{subscribe,signal}={},deps={}){\r\n if(signal?.aborted||typeof subscribe!=='function')return undefined;\r\n const win=deps.win||globalThis,doc=deps.doc||globalThis.document,nav=deps.nav||globalThis.navigator,\r\n  now=deps.now||(()=>Date.now()),raf=deps.raf||(f=>requestAnimationFrame(f)),caf=deps.caf||(h=>cancelAnimationFrame(h)),\r\n  load=deps.loadSheet||(async()=>(await import('./runtime.mjs')).loadSheet()),color=deps.color||FACTIONS[0].color;\r\n let sheet=null;try{sheet=await load();}catch{sheet=null;}\r\n if(signal?.aborted)return undefined; // abort won the race while the atlas loaded: mount nothing\r\n\r\n const track=createTrack(),compass=createCompass(win),cleanups=[];let disposed=false,following=true,headingUp=false,heading=null,wl=null,wlBusy=false,\r\n  loopId=0,shown=null,lastKey='',visible=true,firstCamera=true,notice='',noticeUntil=0,permState='prompt',wasStale=false;\r\n const container=map.getContainer?.();\r\n\r\n // UI\r\n const ui=doc.createElement('div');ui.className='cw-follow-ui';ui.style.cssText='position:absolute;left:8px;bottom:28px;z-index:3;display:flex;flex-direction:column;gap:6px;align-items:flex-start;font:12px/1.3 system-ui,sans-serif;pointer-events:none';\r\n const msg=doc.createElement('p');msg.setAttribute('role','status');msg.style.cssText='margin:0;padding:4px 8px;border-radius:8px;background:#142c32e6;color:#f5efd4;max-width:230px;display:none';\r\n const row=doc.createElement('div');row.style.cssText='display:flex;gap:6px;pointer-events:auto';\r\n const btn=(label,aria)=>{const b=doc.createElement('button');b.type='button';b.textContent=label;b.setAttribute('aria-label',aria);b.style.cssText='min-height:36px;padding:4px 10px;border:2px solid #142c32;border-radius:8px;background:#e8e7b0;color:#142c32;font:inherit;font-weight:700';return b;};\r\n const bRecenter=btn('Recenter','Recenter on me'),bHead=btn('North up','Toggle heading-up map'),bCompass=btn('Compass','Enable compass');\r\n bRecenter.hidden=true;bCompass.hidden=!compass.supported();row.append(bRecenter,bHead,bCompass);ui.append(msg,row);container?.append?.(ui);\r\n const say=(k,ms=0)=>{notice=k;noticeUntil=ms?now()+ms:0;render();};\r\n function render(){\r\n  let k=notice;if(permState==='denied')k='denied';else if(track.hasFix&&shown?.stale)k='stale';else if(track.weak&&!track.hasFix)k='weak';\r\n  msg.textContent=k?TEXT[k]:'';msg.style.display=k?'block':'none';\r\n }\r\n\r\n // layers (re-installed on every style.load: setStyle by the pixel-style feature wipes them)\r\n function install(){\r\n  if(disposed)return;\r\n  try{\r\n   if(!map.getSource('cw-me-acc')){\r\n    map.addSource('cw-me-acc',{type:'geojson',data:{type:'FeatureCollection',features:[]}});\r\n    map.addLayer({id:'cw-me-acc-fill',type:'fill',source:'cw-me-acc',paint:{'fill-color':'#5ec8ff','fill-opacity':.16}});\r\n    map.addLayer({id:'cw-me-acc-line',type:'line',source:'cw-me-acc',paint:{'line-color':'#5ec8ff','line-width':1.5,'line-opacity':.7}});\r\n   }\r\n   if(!map.getSource('cw-me')){\r\n    map.addSource('cw-me',{type:'geojson',data:{type:'FeatureCollection',features:[]}});\r\n    map.addLayer({id:'cw-me',type:'symbol',source:'cw-me',layout:{'icon-image':['get','img'],'icon-anchor':'center','icon-offset':[0,ICON_OFFSET_Y],'icon-allow-overlap':true,'icon-ignore-placement':true,'icon-pitch-alignment':'viewport','icon-rotation-alignment':'viewport'}});\r\n   }\r\n   lastKey='';paint(true);\r\n  }catch(e){console.warn('coach-world geo-follow layers',e);}\r\n }\r\n function imageFor(sector,fr){\r\n  const key=`cw-me-${sheet?'a':'f'}-${sector}-${sheet?fr:0}`;\r\n  if(!map.hasImage(key)){const img=sheet?frameImage(sheet,color,sector,fr):arrowImage(sector);if(img)map.addImage(key,img,{pixelRatio:2});else return null;}\r\n  return key;\r\n }\r\n function paint(force){\r\n  if(!shown||shown.hidden)return;\r\n  const bearing=map.getBearing?.()||0,hd=heading??0,sector=sectorOf(Math.round(normDeg(hd-bearing)/360*256)%256),\r\n   moving=track.speed>=CFG.walkSpeed,fr=moving?walkFrame(track.distance,6,CFG.strideM):-1,img=imageFor(sector,fr);\r\n  const key=`${shown.latitude.toFixed(7)},${shown.longitude.toFixed(7)},${img},${Math.round(shown.accuracy)}`;if(!force&&key===lastKey)return;lastKey=key;\r\n  map.getSource('cw-me')?.setData({type:'FeatureCollection',features:[{type:'Feature',geometry:{type:'Point',coordinates:[shown.longitude,shown.latitude]},properties:{img}}]});\r\n  map.getSource('cw-me-acc')?.setData({type:'FeatureCollection',features:[{type:'Feature',geometry:circlePolygon(shown.latitude,shown.longitude,shown.accuracy),properties:{}}]});\r\n }\r\n\r\n // follow camera\r\n const FIX_MS=1000;\r\n function camera(){\r\n  if(!following||!shown||shown.hidden)return;\r\n  const o={center:[shown.longitude,shown.latitude],duration:firstCamera?0:FIX_MS,easing:t=>t,essential:true};\r\n  if(firstCamera){o.zoom=CFG.zoom;o.pitch=CFG.pitch;firstCamera=false;}\r\n  if(headingUp&&heading!=null)o.bearing=heading;\r\n  map.easeTo(o);\r\n }\r\n function setFollow(v){following=v;bRecenter.hidden=v;if(v){firstCamera=true;camera();lockWake();}else unlockWake();}\r\n const onMoveStart=e=>{if(e?.originalEvent&&following)setFollow(false);}; // user gesture breaks follow; our own easeTo has no originalEvent\r\n map.on('movestart',onMoveStart);cleanups.push(()=>map.off?.('movestart',onMoveStart));\r\n const onRotate=()=>paint(true);map.on('rotate',onRotate);cleanups.push(()=>map.off?.('rotate',onRotate));\r\n map.on('style.load',install);cleanups.push(()=>map.off?.('style.load',install));\r\n // setStyle(url) with diffing removes our sources WITHOUT a style.load; styledata fires in both paths and install() is idempotent\r\n map.on('styledata',install);cleanups.push(()=>map.off?.('styledata',install));\r\n\r\n // heading source: GPS course when walking, otherwise the compass (only after the user tapped the chip)\r\n function pickHeading(){\r\n  const t=track.moveHeading??(compass.active?compass.heading:null);\r\n  if(t!=null)heading=smoothHeading(heading,t);\r\n }\r\n bCompass.onclick=async()=>{const ok=await compass.request(v=>{if(!disposed&&track.moveHeading==null){heading=smoothHeading(heading,v);if(headingUp&&following)camera();paint(true);}});if(disposed)return;bCompass.hidden=ok;if(!ok)say('compassOff',0);};\r\n bHead.onclick=async()=>{headingUp=!headingUp;bHead.textContent=headingUp?'Heading up':'North up';if(headingUp){if(!compass.active&&compass.supported())await bCompass.onclick();if(!disposed&&heading!=null)map.easeTo({bearing:heading,duration:300,essential:true});}else map.easeTo({bearing:0,duration:300,essential:true});};\r\n\r\n // wake lock: only while following, released on hidden/unfollow/dispose\r\n async function lockWake(){\r\n  if(disposed||wl||wlBusy||!following||!visible||!nav?.wakeLock?.request||!track.hasFix)return;\r\n  wlBusy=true;\r\n  try{const s=await nav.wakeLock.request('screen');wlBusy=false;if(disposed||!following||!visible){s.release?.().catch?.(()=>{});return;}wl=s;s.addEventListener?.('release',()=>{if(wl===s)wl=null;});}\r\n  catch{wlBusy=false;}\r\n }\r\n function unlockWake(){const s=wl;wl=null;try{s?.release?.()?.catch?.(()=>{});}catch{}}\r\n const onVis=()=>{visible=!doc.hidden;if(!visible){unlockWake();if(loopId){caf(loopId);loopId=0;}}else{lockWake();startLoop();}};\r\n doc.addEventListener('visibilitychange',onVis);cleanups.push(()=>doc.removeEventListener('visibilitychange',onVis));\r\n\r\n // permission state (read-only query, never prompts)\r\n try{\r\n  const p=await nav?.permissions?.query?.({name:'geolocation'});\r\n  if(disposed||signal?.aborted)return dispose2();\r\n  if(p){permState=p.state;const ch=()=>{permState=p.state;render();};p.addEventListener?.('change',ch);cleanups.push(()=>p.removeEventListener?.('change',ch));render();}\r\n }catch{}\r\n if(signal?.aborted)return dispose2();\r\n\r\n // fixes\r\n const unsub=subscribe(fix=>{\r\n  if(disposed)return;\r\n  const ok=track.push({...fix,sampledAt:fix.sampledAt??now()});\r\n  if(!ok){if(track.weak)render();return;}\r\n  if(wasStale){wasStale=false;say('resumed',3000);}\r\n  if(permState==='denied'){permState='granted';}\r\n  tick();pickHeading();camera();lockWake();startLoop();\r\n });\r\n cleanups.push(()=>{try{unsub?.();}catch{}});\r\n\r\n function tick(){\r\n  const t=now();shown=track.at(t);if(!shown)return;\r\n  if(shown.stale&&!wasStale)wasStale=true;\r\n  if(notice&&noticeUntil&&t>noticeUntil){notice='';}\r\n  render();paint(false);\r\n }\r\n function startLoop(){\r\n  if(loopId||disposed||!visible)return;\r\n  const f=()=>{loopId=0;if(disposed||!visible)return;tick();loopId=raf(f);};loopId=raf(f);\r\n }\r\n\r\n function dispose(){\r\n  if(disposed)return;disposed=true;if(loopId)caf(loopId);loopId=0;unlockWake();compass.dispose();\r\n  for(const c of cleanups.splice(0)){try{c();}catch(e){console.warn('coach-world geo-follow',e);}}\r\n  ui.remove();\r\n  try{for(const id of ['cw-me','cw-me-acc-line','cw-me-acc-fill'])if(map.getLayer?.(id))map.removeLayer(id);for(const id of ['cw-me','cw-me-acc'])if(map.getSource?.(id))map.removeSource(id);}catch{}\r\n }\r\n function dispose2(){dispose();return undefined;}\r\n signal?.addEventListener('abort',dispose,{once:true});cleanups.push(()=>signal?.removeEventListener('abort',dispose));\r\n install(); // addSource works on a style that is still settling (map-view attaches inside its own load handler)\r\n render();\r\n return dispose;\r\n}\r\n","modules/coach-world-rts/ground-atlas.mjs":"// V7 ground atlas (Daggerfall look): 6 classes x 4 variants of 16 px pixel-art tiles, procedural (no copied art). The local gpt-oss-20b card\r\n// (D:/outputs/daggerfall-1019/v7/card.mjs H) failed 5x on seamless tiling; written by the orchestrator against the same validator; border lattice\r\n// shared per class so every variant meets every other one without a seam.\r\nexport const GROUND_CLASSES = ['sand','scrub','rock','grass','urban','water'];\r\nexport const GROUND_PALETTES = {sand:['#9e8052','#b39462','#c7a873','#d8bb86'],scrub:['#9e8052','#b39462','#c7a873','#6f6a3e','#556036','#8a7550'],rock:['#4f4337','#6e5e4c','#7d6b57','#8f7d67','#a39079'],\r\n grass:['#3f5a2a','#4f6b34','#5d7c3a','#6f8f45','#86a352'],urban:['#665538','#8f7a56','#a38c63','#b59c70','#c4ab7e'],water:['#2f5a6e','#3a6d84','#4a829a','#8fc3d4']};\r\nconst BAYER=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];\r\nfunction mulberry32(a){return()=>{a=a+0x6D2B79F5|0;let t=Math.imul(a^a>>>15,1|a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};}\r\n// value noise on a lattice of `cells` per 16 px that wraps every 16 px, so every tile (and every variant pair) meets seamlessly\r\n// border lattice points (i==0 or j==0, which the wrap also uses at i==cells) come from the CLASS rng, interior ones from the variant rng,\r\n// so any variant meets any other variant of its class without a seam.\r\nfunction periodicNoise(rnd,cells,edge){const L=Array.from({length:cells*cells},(_,k)=>(k%cells===0||k<cells)?edge():rnd()),at=(i,j)=>L[((j%cells)+cells)%cells*cells+((i%cells)+cells)%cells],s=t=>t*t*(3-2*t);\r\n return (x,y)=>{const u=x/16*cells,v=y/16*cells,i=Math.floor(u),j=Math.floor(v),fx=s(u-i),fy=s(v-j);\r\n  return (at(i,j)*(1-fx)+at(i+1,j)*fx)*(1-fy)+(at(i,j+1)*(1-fx)+at(i+1,j+1)*fx)*fy;};}\r\nconst hex=h=>[parseInt(h.slice(1,3),16),parseInt(h.slice(3,5),16),parseInt(h.slice(5,7),16)];\r\n// per class: base palette indices the noise ramps across, and wrapped features painted on top\r\nconst LOOK={\r\n sand:{base:[0,1,2,3],noise:(n,x,y)=>.55*n(x,y)+.45*(.5+.5*Math.sin((x+y)/16*Math.PI*2*2)),feat:(r,put)=>{for(let k=0;k<4;k++)put(r()*16|0,r()*16|0,3);}},\r\n scrub:{base:[0,1,2],noise:(n,x,y)=>n(x,y),feat:(r,put)=>{for(let k=0;k<3;k++){const x=r()*16|0,y=r()*16|0,c=r()<.5?3:4;put(x,y,c);put(x+1,y,c);put(x,y+1,c);if(r()<.6)put(x+1,y+1,4);}for(let k=0;k<2;k++)put(r()*16|0,r()*16|0,5);}},\r\n rock:{base:[1,2,3,4],noise:(n,x,y)=>n(x,y),feat:(r,put)=>{let x=r()*16|0,y=r()*16|0;for(let k=0;k<7;k++){put(x,y,0);x+=r()<.5?1:0;y+=1;}}},\r\n grass:{base:[0,1,2,3],noise:(n,x,y)=>n(x,y),feat:(r,put)=>{for(let k=0;k<8;k++)put(r()*16|0,r()*16|0,4);}},\r\n urban:{base:[1,2,3],noise:(n,x,y)=>.35+.3*n(x,y),feat:(r,put)=>{let x=r()*16|0,y=r()*16|0;for(let k=0;k<5;k++){put(x,y,0);x+=1;y+=r()<.4?1:0;}for(let k=0;k<3;k++)put(r()*16|0,r()*16|0,4);}},\r\n water:{base:[0,1,2],noise:(n,x,y)=>.6*n(x,y)+.4*(.5+.5*Math.sin(y/16*Math.PI*2*3)),feat:(r,put)=>{for(let k=0;k<3;k++)put(r()*16|0,r()*16|0,3);}},\r\n};\r\nexport function paintGroundAtlas({seed=1}={}){\r\n const w=64,h=96,tile=16,data=new Uint8Array(w*h*4);\r\n GROUND_CLASSES.forEach((cls,row)=>{const pal=GROUND_PALETTES[cls].map(hex),look=LOOK[cls];\r\n  for(let v=0;v<4;v++){const r=mulberry32((Math.imul(seed,0x9e3779b1)^Math.imul(row+1,0x85ebca6b)^Math.imul(v+1,0xc2b2ae35))>>>0),e=mulberry32((Math.imul(seed,0x9e3779b1)^Math.imul(row+1,0x85ebca6b))>>>0),n1=periodicNoise(r,4,e),n2=periodicNoise(r,8,e);\r\n   const n=(x,y)=>.7*n1(x,y)+.3*n2(x,y),idx=new Uint8Array(256);\r\n   for(let y=0;y<16;y++)for(let x=0;x<16;x++){const t=Math.min(.999,Math.max(0,look.noise(n,x,y)+((BAYER[(y&3)*4+(x&3)]+.5)/16-.5)*.35));idx[y*16+x]=look.base[Math.floor(t*look.base.length)];}\r\n   look.feat(r,(x,y,c)=>{idx[((y%16)+16)%16*16+((x%16)+16)%16]=c;});\r\n   for(let y=0;y<16;y++)for(let x=0;x<16;x++){const o=((row*16+y)*w+v*16+x)*4,c=pal[idx[y*16+x]];data[o]=c[0];data[o+1]=c[1];data[o+2]=c[2];data[o+3]=255;}}\r\n });\r\n return {w,h,tile,variants:4,data};\r\n}\r\n","modules/coach-world-rts/ground-flora.mjs":"// V7 per-tile flora roll (after DFU layoutNature). Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v7/card.mjs I), passed its validator first try\r\n// (determinism per tile, clean transitions, no flora on urban/water, slope sink, cap keeps the same first plants).\r\n// ES module – no imports\r\nexport function layoutGroundFlora(opts) {\r\n    const { classes, W, x0, z0, tile, heightAt, seed = 1, max = 1500 } = opts;\r\n\r\n    // Helper: seeded mulberry32 PRNG\r\n    function prng(seedVal) {\r\n        let t = seedVal + 0x6D2B79F5;\r\n        return () => {\r\n            t |= 0; t += (t << 15) | (t >>> 17);\r\n            t ^= t + ((t >> 7) ^ (t << 3));\r\n            const r = Math.imul(t, 0xE7037ED1);\r\n            return (r >>> 0) / 4294967296;\r\n        };\r\n    }\r\n\r\n    // Helper: hash seed with i,j\r\n    function makeSeed(i, j) {\r\n        let h = seed;\r\n        h = Math.imul(h ^ i, 0x5bd1e995);\r\n        h = Math.imul(h ^ j, 0x5bd1e995);\r\n        return (h >>> 0);\r\n    }\r\n\r\n    // Probability thresholds per class\r\n    const probs = {\r\n        0: { shrub: 0.02 },                     // sand\r\n        1: { shrub: 0.14, rock: 0.03 },         // scrub\r\n        2: { boulder: 0.07 },                   // rock\r\n        3: { bush: 0.06 },                      // grass\r\n        4: {},                                   // urban\r\n        5: {}                                    // water\r\n    };\r\n\r\n    const kindMap = { shrub: 0, rock: 1, boulder: 2, bush: 3 };\r\n    const scaleRange = {\r\n        0: [0.8, 1.6],   // shrub\r\n        1: [0.5, 1.0],   // rock\r\n        2: [1.5, 3.0],   // boulder\r\n        3: [1.0, 1.8]    // bush\r\n    };\r\n\r\n    const data = new Float32Array(max * 5);\r\n    let count = 0;\r\n\r\n    for (let j = 0; j < W - 1 && count < max; ++j) {\r\n        for (let i = 0; i < W - 1 && count < max; ++i) {\r\n            // Determine tile class – all four corners must match\r\n            const c00 = classes[j * W + i];\r\n            const c10 = classes[j * W + (i + 1)];\r\n            const c01 = classes[(j + 1) * W + i];\r\n            const c11 = classes[(j + 1) * W + (i + 1)];\r\n            if (!(c00 === c10 && c10 === c01 && c01 === c11)) continue;\r\n            const tileClass = c00;\r\n\r\n            // Get probability thresholds for this class\r\n            const probsForClass = probs[tileClass];\r\n            if (!probsForClass) continue; // no flora\r\n\r\n            // Seed PRNG for this tile\r\n            const rng = prng(makeSeed(i, j));\r\n\r\n            // Roll to decide kind\r\n            let roll = rng();\r\n            let chosenKind = null;\r\n            let cumulative = 0;\r\n            for (const [k, p] of Object.entries(probsForClass)) {\r\n                cumulative += p;\r\n                if (roll < cumulative) {\r\n                    chosenKind = kindMap[k];\r\n                    break;\r\n                }\r\n            }\r\n            if (chosenKind === null) continue; // no flora this tile\r\n\r\n            // Random point inside tile with 10% margin\r\n            const margin = 0.1 * tile;\r\n            const px = x0 + i * tile + margin + rng() * (tile - 2 * margin);\r\n            const pz = z0 + j * tile + margin + rng() * (tile - 2 * margin);\r\n\r\n            // Height and sink\r\n            const hCenter = heightAt(px, pz);\r\n            const hX1 = heightAt(px + 1, pz);\r\n            const hX2 = heightAt(px - 1, pz);\r\n            const hZ1 = heightAt(px, pz + 1);\r\n            const hZ2 = heightAt(px, pz - 1);\r\n            const slope = Math.max(Math.abs(hX1 - hX2), Math.abs(hZ1 - hZ2)) / 2;\r\n            const sink = slope * 0.6;\r\n            const py = hCenter - sink;\r\n\r\n            // Scale\r\n            const [minS, maxS] = scaleRange[chosenKind];\r\n            const scale = minS + rng() * (maxS - minS);\r\n\r\n            // Store instance\r\n            const idx = count * 5;\r\n            data[idx] = px;\r\n            data[idx + 1] = py;\r\n            data[idx + 2] = pz;\r\n            data[idx + 3] = scale;\r\n            data[idx + 4] = chosenKind;\r\n\r\n            ++count;\r\n        }\r\n    }\r\n\r\n    return { count, data: data.subarray(0, count * 5) };\r\n}\r\n","modules/coach-world-rts/ground-tiles.mjs":"// V7 ground detail (Daggerfall look): a pixel-art tile ground with marching-squares-style transitions, a per-tile flora roll and near\r\n// grass tufts, over the 9-cell world ring (base view and flight; AR keeps its own budgets).\r\n// - Classes per CORNER of a 64-tiles-per-cell grid (~7.7 m tiles, DFO's ~6.4 m look): water polygon > park/grass landuse = grass >\r\n//   residential/commercial/industrial = urban > slope > SLOPE_ROCK = rock > hashed value noise = scrub or sand (DFU's weight idea, our data).\r\n// - The ground shader (terrain material, near the focus only, vertex colour beyond, as DFO's copy map asks) picks, per atlas texel, the\r\n//   tile corner with the highest jittered bilinear weight: two-class tiles cut along a dithered curve, the marching-squares shapes at\r\n//   pixel-art resolution, with no 56-record lookup table to author. Atlas = ground-atlas.mjs (procedural), slope shading kept from vColor.\r\n// - Flora: ground-flora.mjs (per-tile roll, local card), one instanced draw. Tufts: DFO grassPixelArt sheet (copied, MIT), one instanced draw.\r\nimport {GROUND_CLASSES,paintGroundAtlas} from './ground-atlas.mjs';\r\nimport {layoutGroundFlora} from './ground-flora.mjs';\r\nimport {createPs1Material} from './ps1-material.mjs';\r\nimport {buildTuftSheet,PX_VARIANTS,PX_TUFT_W,PX_TUFT_H} from '../coach-world/vendor/dfo-grass-pixel-art.mjs';\r\nexport const TILES_PER_CELL=64,GROUND_NEAR=350,SLOPE_ROCK=.35,SCRUB_AT=.58,MAX_FLORA=1500,MAX_TUFTS=3000,TUFT_NEAR=180,TUFT_H=1.3;\r\nexport const G={sand:0,scrub:1,rock:2,grass:3,urban:4,water:5};\r\nconst LAND_TO_GROUND=[G.urban,G.urban,G.urban,G.grass,G.grass,-1]; // LAND_CLASSES order: residential commercial industrial park grass other\r\nconst FLORA_COLOR=[[.44,.42,.24],[.54,.46,.31],[.49,.42,.34],[.31,.42,.2]]; // shrub rock boulder bush\r\nconst FLORA_SHAPE=[[1,.7,1],[1,.55,1],[1,.7,.9],[1,.9,1]];\r\nconst hash2=(x,y)=>{let h=Math.imul(x|0,374761393)+Math.imul(y|0,668265263)|0;h=Math.imul(h^(h>>>13),1274126177);return ((h^(h>>>16))>>>0)/4294967296;};\r\nfunction vnoise(x,y){const i=Math.floor(x),j=Math.floor(y),fx=x-i,fy=y-j,u=fx*fx*(3-2*fx),v=fy*fy*(3-2*fy);\r\n return (hash2(i,j)*(1-u)+hash2(i+1,j)*u)*(1-v)+(hash2(i,j+1)*(1-u)+hash2(i+1,j+1)*u)*v;}\r\n\r\n// O(1) bilinear height from the cells' raw DEM grids (x exaggeration). The RTIN walker (terrain.heightAtM) costs ~3 us a call; the slope test\r\n// needs ~150k samples, so it reads the grids instead (same data the mesh was built from).\r\nexport function gridSampler(cells,exag=1){return (x,z)=>{const c=cells.find(q=>x>=q.minX&&x<=q.minX+q.size&&z>=q.minZ&&z<=q.minZ+q.size);if(!c||!c.heights)return NaN;\r\n const N=c.grid,st=c.size/(N-1),u=Math.min(N-1.001,Math.max(0,(x-c.minX)/st)),v=Math.min(N-1.001,Math.max(0,(z-c.minZ)/st)),i=u|0,j=v|0,fx=u-i,fy=v-j,h=c.heights;\r\n return ((h[j*N+i]*(1-fx)+h[j*N+i+1]*fx)*(1-fy)+(h[(j+1)*N+i]*(1-fx)+h[(j+1)*N+i+1]*fx)*fy)*exag;};}\r\n// cells: world-layer tcells (minX,minZ,size,grid,heights); decoded: per-cell tile data (landuse/water triangles, local metres); heightAt(x,z) exaggerated metres.\r\nexport function classifyGround({cells,decoded,heightAt,tilesPerCell=TILES_PER_CELL}){\r\n const x0=Math.min(...cells.map(c=>c.minX)),z0=Math.min(...cells.map(c=>c.minZ)),x1=Math.max(...cells.map(c=>c.minX+c.size)),z1=Math.max(...cells.map(c=>c.minZ+c.size));\r\n const tile=cells[0].size/tilesPerCell,W=Math.round(Math.max(x1-x0,z1-z0)/tile)+1,cls=new Uint8Array(W*W),set=new Int8Array(W*W).fill(-1);\r\n const raster=(pos,idx,from,to,val,prio)=>{for(let k=from;k+2<to;k+=3){const a=idx[k]*2,b=idx[k+1]*2,c=idx[k+2]*2,ax=pos[a],az=pos[a+1],bx=pos[b],bz=pos[b+1],cx=pos[c],cz=pos[c+1];\r\n  const d=(bz-cz)*(ax-cx)+(cx-bx)*(az-cz);if(Math.abs(d)<1e-9)continue;\r\n  const i0=Math.max(0,Math.ceil((Math.min(ax,bx,cx)-x0)/tile)),i1=Math.min(W-1,Math.floor((Math.max(ax,bx,cx)-x0)/tile)),j0=Math.max(0,Math.ceil((Math.min(az,bz,cz)-z0)/tile)),j1=Math.min(W-1,Math.floor((Math.max(az,bz,cz)-z0)/tile));\r\n  for(let j=j0;j<=j1;j++)for(let i=i0;i<=i1;i++){const x=x0+i*tile,z=z0+j*tile,l1=((bz-cz)*(x-cx)+(cx-bx)*(z-cz))/d,l2=((cz-az)*(x-cx)+(ax-cx)*(z-cz))/d;\r\n   if(l1>=-1e-9&&l2>=-1e-9&&l1+l2<=1+1e-9&&set[j*W+i]<prio){set[j*W+i]=prio;cls[j*W+i]=val;}}}};\r\n for(const m of decoded){if(!m||m.missing)continue;const L=m.landuse;\r\n  if(L?.cls)for(let p=0;p<L.cls.length;p++){const g=LAND_TO_GROUND[L.cls[p]];if(g>=0)raster(L.pos,L.idx,L.start[p],L.start[p+1],g,g===G.grass?2:1);}\r\n  const Wt=m.water;if(Wt?.start)for(let p=0;p+1<Wt.start.length;p++)raster(Wt.pos,Wt.idx,Wt.start[p],Wt.start[p+1],G.water,3);}\r\n for(let j=0;j<W;j++)for(let i=0;i<W;i++){const k=j*W+i;if(set[k]>=0)continue;const x=x0+i*tile,z=z0+j*tile,e=tile;\r\n  const s=Math.max(Math.abs(heightAt(x+e,z)-heightAt(x-e,z)),Math.abs(heightAt(x,z+e)-heightAt(x,z-e)))/(2*e);\r\n  cls[k]=s>SLOPE_ROCK?G.rock:(.65*vnoise(x/60,z/60)+.35*vnoise(x/17+31,z/17+7))>SCRUB_AT?G.scrub:G.sand;}\r\n return {cls,W,x0,z0,tile};\r\n}\r\n\r\n// Patch the terrain material (already PS1 + ground noise). Uniform values live in `u` so the caller can move the focus every frame.\r\nexport function addGroundTiles(T,material,{grid,atlas,near=GROUND_NEAR}){\r\n const clsTex=new T.DataTexture(grid.cls,grid.W,grid.W,T.RedFormat,T.UnsignedByteType);clsTex.magFilter=clsTex.minFilter=T.NearestFilter;clsTex.generateMipmaps=false;clsTex.needsUpdate=true;\r\n const atlasTex=new T.DataTexture(atlas.data,atlas.w,atlas.h,T.RGBAFormat);atlasTex.magFilter=atlasTex.minFilter=T.NearestFilter;atlasTex.generateMipmaps=false;atlasTex.needsUpdate=true; // raw bytes, like the vertex-colour ramp\r\n const u={uGroundCls:{value:clsTex},uGroundAtlas:{value:atlasTex},uGroundO:{value:new T.Vector2(grid.x0,grid.z0)},uGroundTile:{value:grid.tile},uGroundW:{value:grid.W},\r\n  uGroundFocus:{value:new T.Vector2(0,0)},uGroundNear:{value:near},uGroundClasses:{value:GROUND_CLASSES.length}};\r\n const prev=material.onBeforeCompile,key=material.customProgramCacheKey;\r\n material.onBeforeCompile=(shader,r)=>{prev?.(shader,r);Object.assign(shader.uniforms,u);\r\n  shader.vertexShader=shader.vertexShader.replace('#include <common>','#include <common>\\nvarying vec2 vGroundXZ;').replace('#include <begin_vertex>','#include <begin_vertex>\\nvGroundXZ=transformed.xz;');\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <common>',`#include <common>\r\nvarying vec2 vGroundXZ;uniform sampler2D uGroundCls,uGroundAtlas;uniform vec2 uGroundO,uGroundFocus;uniform float uGroundTile,uGroundW,uGroundNear,uGroundClasses;\r\nfloat gHash(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}\r\nfloat gCls(vec2 c){return floor(texture2D(uGroundCls,(c+.5)/uGroundW).r*255.+.5);}`).replace('#include <color_fragment>',`#include <color_fragment>\r\n{float k=1.0-smoothstep(uGroundNear*.75,uGroundNear,distance(vGroundXZ,uGroundFocus));vec2 g=(vGroundXZ-uGroundO)/uGroundTile;\r\n if(k>0.0&&g.x>=0.&&g.y>=0.&&g.x<uGroundW-1.&&g.y<uGroundW-1.){vec2 ti=floor(g),tx=floor(fract(g)*16.),t=(tx+.5)/16.;\r\n  vec4 w=vec4((1.-t.x)*(1.-t.y),t.x*(1.-t.y),(1.-t.x)*t.y,t.x*t.y)+(vec4(gHash(ti*16.+tx),gHash(ti*16.+tx+3.1),gHash(ti*16.+tx+7.7),gHash(ti*16.+tx+11.3))-.5)*.32;\r\n  float c=gCls(ti),b=w.x;if(w.y>b){b=w.y;c=gCls(ti+vec2(1.,0.));}if(w.z>b){b=w.z;c=gCls(ti+vec2(0.,1.));}if(w.w>b){c=gCls(ti+vec2(1.,1.));}\r\n  float v=floor(gHash(ti+.37)*4.);vec3 a=texture2D(uGroundAtlas,vec2((v+t.x)/4.,(c+t.y)/uGroundClasses)).rgb;\r\n  float sh=clamp(dot(diffuseColor.rgb,vec3(.299,.587,.114))/.515,.55,1.15);diffuseColor.rgb=mix(diffuseColor.rgb,a*sh,k);}}`);};\r\n material.customProgramCacheKey=()=>`${key.call(material)}-gt`;material.needsUpdate=true; // a no-op before the first draw (world-layer patches before add)\r\n return {uniforms:u,dispose(){clsTex.dispose();atlasTex.dispose();}};\r\n}\r\n\r\n// rollHeight: the fast grid sampler for the per-tile roll (slope of every tile); heightAt: the rendered surface, used only to seat the plants.\r\nexport function createGroundFlora(T,{grid,heightAt,rollHeight=heightAt,seed=1,max=MAX_FLORA,resolution}){\r\n const r=layoutGroundFlora({classes:grid.cls,W:grid.W,x0:grid.x0,z0:grid.z0,tile:grid.tile,heightAt:rollHeight,seed,max});\r\n if(rollHeight!==heightAt)for(let k=0;k<r.count;k++){const o=k*5,x=r.data[o],z=r.data[o+2];r.data[o+1]=heightAt(x,z)-(rollHeight(x,z)-r.data[o+1]);} // keep the card's slope sink\r\n const geo=new T.OctahedronGeometry(.5,0).translate(0,.35,0),mat=createPs1Material(T,{vertexColors:false,resolution}),mesh=new T.InstancedMesh(geo,mat,Math.max(1,r.count));\r\n const m=new T.Matrix4(),q=new T.Quaternion(),s=new T.Vector3(),p=new T.Vector3(),c=new T.Color(),up=new T.Vector3(0,1,0);\r\n for(let k=0;k<r.count;k++){const [x,y,z,sc,kind]=r.data.subarray(k*5,k*5+5),sh=FLORA_SHAPE[kind],col=FLORA_COLOR[kind],j=.85+.3*hash2(k,7);\r\n  mesh.setMatrixAt(k,m.compose(p.set(x,y,z),q.setFromAxisAngle(up,hash2(k,3)*6.28),s.set(sc*sh[0],sc*sh[1],sc*sh[2])));mesh.setColorAt(k,c.setRGB(col[0]*j,col[1]*j,col[2]*j));}\r\n mesh.count=r.count;mesh.name='ground-flora';mesh.raycast=()=>{};mesh.computeBoundingSphere();\r\n return {mesh,count:r.count,dispose(){geo.dispose();mat.dispose();mesh.dispose?.();}};\r\n}\r\n\r\n// Near grass tufts on grass tiles, nearest the home first; camera-facing (y-axis) cards, alpha-tested DFO tuft sprites, faded past TUFT_NEAR of the focus.\r\nexport function createGrassTufts(T,{grid,heightAt,max=MAX_TUFTS,focus}){\r\n const spots=[],{cls,W,x0,z0,tile}=grid;\r\n for(let j=0;j<W-1;j++)for(let i=0;i<W-1;i++){if(cls[j*W+i]!==G.grass||cls[j*W+i+1]!==G.grass||cls[(j+1)*W+i]!==G.grass||cls[(j+1)*W+i+1]!==G.grass)continue;\r\n  for(let n=0;n<5;n++){const x=x0+(i+.1+.8*hash2(i*5+n,j))*tile,z=z0+(j+.1+.8*hash2(j*5+n,i+91))*tile;spots.push([x,z,hash2(i+n,j*3)]);}}\r\n spots.sort((a,b)=>Math.hypot(a[0],a[1])-Math.hypot(b[0],b[1]));const n=Math.min(max,spots.length);if(!n)return null;\r\n const sheet=buildTuftSheet(),tex=new T.DataTexture(sheet.data,sheet.width,sheet.height,T.RGBAFormat);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.flipY=false;tex.needsUpdate=true;\r\n const geo=new T.InstancedBufferGeometry(),quad=new T.PlaneGeometry(1,1).translate(0,.5,0);geo.index=quad.index;geo.setAttribute('position',quad.getAttribute('position'));geo.setAttribute('uv',quad.getAttribute('uv'));\r\n const at=new Float32Array(n*4);for(let k=0;k<n;k++){const [x,z,h]=spots[k];at.set([x,heightAt(x,z)-.05,z,Math.floor(h*PX_VARIANTS)],k*4);}\r\n geo.setAttribute('aTuft',new T.InstancedBufferAttribute(at,4));geo.instanceCount=n;\r\n const mat=new T.ShaderMaterial({uniforms:{uSheet:{value:tex},uFocus:{value:focus},uNear:{value:TUFT_NEAR},uH:{value:TUFT_H},uVariants:{value:PX_VARIANTS},uAspect:{value:PX_TUFT_W/PX_TUFT_H}},\r\n  vertexShader:`attribute vec4 aTuft;uniform vec2 uFocus;uniform float uNear,uH,uAspect;varying vec2 vUv;varying float vV,vFade;\r\nvoid main(){vUv=uv;vV=aTuft.w;vFade=1.-smoothstep(uNear*.7,uNear,distance(aTuft.xz,uFocus));\r\n vec4 c=modelViewMatrix*vec4(aTuft.xyz,1.);float s=length(modelMatrix[0].xyz);c.xy+=vec2(position.x*uH*uAspect,position.y*uH)*s; // y-axis billboard\r\n gl_Position=projectionMatrix*c;if(vFade<=0.)gl_Position=vec4(2.,2.,2.,1.);}`,\r\n  fragmentShader:`uniform sampler2D uSheet;uniform float uVariants;varying vec2 vUv;varying float vV,vFade;\r\nfloat b4(vec2 p){int x=int(mod(p.x,4.)),y=int(mod(p.y,4.)),z=x^y;return float(((z&1)<<3)|((y&1)<<2)|(z&2)|((y>>1)&1))/16.;}\r\nvoid main(){vec4 s=texture2D(uSheet,vec2((vV+vUv.x)/uVariants,1.-vUv.y));if(s.a<.5||b4(gl_FragCoord.xy)>=vFade)discard;\r\n float t=s.r;gl_FragColor=vec4(mix(vec3(.18,.27,.11),vec3(.55,.68,.3),t),1.);}`});\r\n const mesh=new T.Mesh(geo,mat);mesh.name='grass-tufts';mesh.frustumCulled=false;mesh.raycast=()=>{};\r\n return {mesh,count:n,dispose(){geo.dispose();quad.dispose();mat.dispose();tex.dispose();}};\r\n}\r\n\r\nexport function createGroundDetail(T,{terrain,cells,decoded,seed=1,resolution,flora=true,tufts=true}){\r\n const g0=cells.every(c=>c.heights)?gridSampler(cells,terrain.exaggeration):terrain.heightAtM,fast=(x,z)=>{const h=g0(x,z);return Number.isFinite(h)?h:0;},grid=classifyGround({cells,decoded,heightAt:fast}),atlas=paintGroundAtlas({seed:1}),tiles=addGroundTiles(T,terrain.material,{grid,atlas});\r\n const group=new T.Group();group.name='ground-detail';group.scale.copy(terrain.group.scale);const focus=tiles.uniforms.uGroundFocus.value;\r\n const fl=flora?createGroundFlora(T,{grid,heightAt:terrain.heightAtM,rollHeight:fast,seed,resolution}):null,tu=tufts?createGrassTufts(T,{grid,heightAt:terrain.heightAtM,focus}):null;\r\n if(fl)group.add(fl.mesh);if(tu)group.add(tu.mesh);\r\n const counts=[0,0,0,0,0,0];for(const c of grid.cls)counts[c]++;\r\n return {group,grid,focus,stats:{tiles:(grid.W-1)**2,classes:Object.fromEntries(GROUND_CLASSES.map((n,i)=>[n,counts[i]])),flora:fl?.count||0,tufts:tu?.count||0},\r\n  setFocus(x,z){focus.set(x,z);},dispose(){tiles.dispose();fl?.dispose();tu?.dispose();group.removeFromParent();}};\r\n}\r\n","modules/coach-world-rts/herd-atlas.mjs":"// V8 herd sprite sheet (goat, hopper; 5 drawn dirs x walk,walk,graze; 24x16 cells), procedural pixel art. The local gpt-oss-20b card\r\n// (D:/outputs/daggerfall-1019/v8/card.mjs J) failed 5x on proportions/frames; written by the orchestrator against the same validator.\r\nexport const HERD_SPECIES = ['goat','hopper'];\r\nconst PAL=[['#2a1d17','#a77b4f','#c99a63','#e6c99a','#e8e0c8','#111111'],['#2a1d17','#8f7a62','#b39b7d','#ddd0b8','#111111','#d48c8c']];\r\nconst hex=h=>[1,3,5].map(i=>parseInt(h.slice(i,i+2),16));\r\n// colour slots per species: 0 outline, 1 dark coat, 2 light coat, 3 belly, 4 horn|eye, 5 eye|ear\r\nfunction cell(){const m=new Int8Array(24*16).fill(-1);\r\n const px=(x,y,c)=>{x=Math.round(x);y=Math.round(y);if(x>=0&&x<24&&y>=0&&y<16)m[y*24+x]=c;};\r\n const rect=(x0,y0,x1,y1,c)=>{for(let y=y0;y<=y1;y++)for(let x=x0;x<=x1;x++)px(x,y,c);};\r\n const oval=(cx,cy,rx,ry,c,belly=-1)=>{for(let y=Math.floor(cy-ry);y<=Math.ceil(cy+ry);y++)for(let x=Math.floor(cx-rx);x<=Math.ceil(cx+rx);x++){const d=((x-cx)/rx)**2+((y-cy)/ry)**2;if(d<=1)px(x,y,belly>=0&&y>cy+ry*.35?belly:(y<cy-ry*.3?2:c));}};\r\n return {m,px,rect,oval};}\r\n// outline: every empty pixel 4-adjacent to a filled one becomes slot 0 (puts the feet' outline on row 15)\r\nfunction outline(m){const o=Int8Array.from(m);for(let y=0;y<16;y++)for(let x=0;x<24;x++){if(m[y*24+x]>=0)continue;\r\n if((x>0&&m[y*24+x-1]>=0)||(x<23&&m[y*24+x+1]>=0)||(y>0&&m[(y-1)*24+x]>=0)||(y<15&&m[(y+1)*24+x]>=0))o[y*24+x]=0;}return o;}\r\nconst leg=(c,x,y0,len,col=1)=>c.rect(x,y0,x,y0+len-1,col);\r\n// legs end on row 14 (len to 14), a lifted leg ends on row 13; the outline adds row 15 under planted feet\r\nfunction goat(dir,frame){const c=cell(),g=frame===2,lift=(i)=>frame===0?(i%2===0):frame===1?(i%2===1):false;\r\n if(dir===2){c.oval(10,8,6,3,1,3);c.px(4,7,1);c.px(3,6,1); // body + tail\r\n  const legs=[6,8,12,14];legs.forEach((x,i)=>leg(c,x+(lift(i)?(i<2?-1:1):0),11,lift(i)?3:4));\r\n  if(g){c.rect(15,8,16,10,1);c.rect(17,10,19,12,2);c.px(19,11,5);c.px(16,8,4);c.px(17,7,4);} // head down\r\n  else{c.rect(15,5,16,8,1);c.rect(16,3,19,6,2);c.px(18,4,5);c.px(15,2,4);c.px(14,1,4);c.px(15,3,4);c.px(13,2,4);} // neck, head, horns curl back\r\n }else if(dir===0||dir===4){const f=dir===0;c.oval(12,8,4,3.5,1,f?3:-1);\r\n  [10,11,13,14].forEach((x,i)=>leg(c,x,11,lift(i)?3:4));\r\n  if(g){c.rect(10,10,14,12,2);if(f){c.px(11,11,5);c.px(13,11,5);}c.px(9,9,4);c.px(15,9,4);}\r\n  else{c.rect(10,2,14,5,f?2:1);if(f){c.px(11,4,5);c.px(13,4,5);}c.px(9,1,4);c.px(8,2,4);c.px(15,1,4);c.px(16,2,4);if(!f)c.px(12,11,2);}\r\n }else{const front=dir===1;c.oval(11,8,5,3.2,1,front?3:-1);\r\n  [8,10,13,15].forEach((x,i)=>leg(c,x+(lift(i)?1:0),11,lift(i)?3:4));\r\n  const hx=front?15:16;if(g){c.rect(hx,9,hx+2,12,2);if(front)c.px(hx+1,10,5);}else{c.rect(hx,3,hx+2,6,front?2:1);if(front)c.px(hx+1,4,5);c.px(hx-1,2,4);c.px(hx,1,4);c.px(hx+3,2,4);}\r\n  if(!front){c.px(5,7,1);}\r\n }\r\n return outline(c.m);}\r\nfunction hopper(dir,frame){const c=cell(),g=frame===2,hop=frame===1;\r\n if(dir===2){c.oval(10,10,4,2.5,1,3);c.px(6,9,3); // body + tail puff\r\n  leg(c,8,12,hop?2:3,1);c.rect(7,14,9,14,1);if(hop)c.rect(7,13,9,13,1); // big hind foot\r\n  leg(c,13,12,hop?3:2,1);\r\n  if(g){c.rect(13,10,15,12,2);c.px(15,11,4);c.rect(12,7,12,9,2);c.px(12,8,5);}\r\n  else{c.rect(13,7,15,9,2);c.px(15,8,4);c.rect(13,2,13,6,2);c.rect(14,3,14,6,2);c.px(13,4,5);}\r\n }else if(dir===0||dir===4){const f=dir===0;c.oval(12,10,3,3,1,f?3:-1);const ly=hop?13:14,ry=hop?14:13;leg(c,11,13,ly-12,1);leg(c,13,13,ry-12,1);c.rect(10,ly,11,ly,1);c.rect(13,ry,14,ry,1); // one foot lifted, alternating\r\n  if(g){c.rect(11,11,13,13,2);if(f){c.px(11,12,4);c.px(13,12,4);}}\r\n  else{c.rect(11,6,13,8,2);if(f){c.px(11,7,4);c.px(13,7,4);}c.rect(10,1,10,5,2);c.rect(14,1,14,5,2);if(f){c.px(10,3,5);c.px(14,3,5);}}\r\n }else{const front=dir===1;c.oval(11,10,3.5,2.6,1,front?3:-1);leg(c,9,12,hop?2:3,1);c.rect(8,14,10,14,1);leg(c,13,12,hop?3:2,1);\r\n  const hx=front?13:14;if(g){c.rect(hx,10,hx+2,12,2);if(front)c.px(hx+2,11,4);}else{c.rect(hx,6,hx+2,8,2);if(front)c.px(hx+2,7,4);c.rect(hx,1,hx,5,2);c.rect(hx+1,2,hx+1,5,2);if(front)c.px(hx,3,5);}\r\n }\r\n return outline(c.m);}\r\nexport function paintHerdAtlas({seed=1}={}){\r\n const w=360,h=32,data=new Uint8Array(w*h*4);\r\n [goat,hopper].forEach((paint,s)=>{const pal=PAL[s].map(hex);for(let d=0;d<5;d++)for(let f=0;f<3;f++){const m=paint(d,f),ox=(d*3+f)*24,oy=s*16;\r\n  for(let y=0;y<16;y++)for(let x=0;x<24;x++){const k=m[y*24+x];if(k<0)continue;const o=((oy+y)*w+ox+x)*4,c=pal[k];data[o]=c[0];data[o+1]=c[1];data[o+2]=c[2];data[o+3]=255;}}});\r\n return {w,h,cellW:24,cellH:16,dirs:5,frames:3,species:HERD_SPECIES,data};\r\n}\r\n","modules/coach-world-rts/herds.mjs":"// V8 ground animals (Daggerfall look): herds of desert goats and hoppers on V7's open-ground tiles (sand/scrub for goats, grass/scrub for\r\n// hoppers). They graze, wander round a slowly drifting herd centre and bolt away from the view focus (the thing you are looking at), with\r\n// wild-forest's tween law (from -> to, smoothstep over dur). 8 facings from 5 drawn directions + mirrors (clips.mjs facingFor, DFO\r\n// mobileOrientation), facing relative to the camera; frames at DFO's ANIMAL_FPS 5 (8 while fleeing). One instanced draw, y-axis\r\n// billboards, fogged by the shared law and tinted by the time of day (sprite-shade). Atlas: herd-atlas.mjs (5 dirs x 3 frames x 2 species).\r\nimport {paintHerdAtlas} from './herd-atlas.mjs';\r\nimport {sectorOf,facingFor} from './clips.mjs';\r\nimport {G} from './ground-tiles.mjs';\r\nimport {SPRITE_TINT} from './sprite-shade.mjs';\r\n// len: sprite length in metres, exaggerated ~2x like the forest critters so they read from the RTS camera\r\nexport const HERDS={goat:{herds:4,size:[4,7],tiles:[G.sand,G.scrub],speed:1.1,len:5.5},hopper:{herds:3,size:[3,5],tiles:[G.grass,G.scrub],speed:2.2,len:4}};\r\nexport const FLEE_R=55,FLEE_D=[25,40],FLEE_SPEED=3.2,CALM_CAM=650,WANDER_R=12,HERD_DRIFT_S=24,ANIMAL_FPS=5,FLEE_FPS=8,STEP_HZ=10;\r\nconst SPECIES=['goat','hopper'];\r\nfunction mulberry32(a){return()=>{a=a+0x6D2B79F5|0;let t=Math.imul(a^a>>>15,1|a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};}\r\nexport const animalAt=(a,now,out={x:0,z:0,k:1})=>{const k=Math.min(1,Math.max(0,(now-a.t0)/a.dur)),e=k*k*(3-2*k);out.x=a.fx+(a.tx-a.fx)*e;out.z=a.fz+(a.tz-a.fz)*e;out.k=k;return out;};\r\nconst P={x:0,z:0,k:0};\r\nfunction moveTo(a,x,z,now,speed,mode){const p=animalAt(a,now,P);a.fx=p.x;a.fz=p.z;a.tx=x;a.tz=z;a.t0=now;a.dur=Math.max(.3,Math.hypot(x-p.x,z-p.z)/speed);a.mode=mode;\r\n if(Math.hypot(x-p.x,z-p.z)>.05)a.heading=Math.atan2(x-p.x,-(z-p.z));}\r\n// one decision for one animal at time now (s): flee the focus if it is close and the camera is low enough to matter, else graze / wander.\r\nexport function stepAnimal(a,herd,now,focus,camD,r){\r\n const p=animalAt(a,now,P),d=focus?Math.hypot(p.x-focus.x,p.z-focus.z):Infinity;\r\n if(d<FLEE_R&&camD<CALM_CAM){if(a.mode!=='flee'||p.k>=1){const dx=(p.x-focus.x)/(d||1),dz=(p.z-focus.z)/(d||1),j=(r()-.5)*.8,c=Math.cos(j),s=Math.sin(j),L=FLEE_D[0]+r()*(FLEE_D[1]-FLEE_D[0]);\r\n  moveTo(a,p.x+(dx*c-dz*s)*L,p.z+(dx*s+dz*c)*L,now,FLEE_SPEED*herd.spec.speed,'flee');}return a.mode;}\r\n if(p.k<1&&a.mode!=='graze')return a.mode;\r\n if(a.mode==='graze'&&now<a.until)return a.mode;\r\n if(r()<.6){a.fx=a.tx=p.x;a.fz=a.tz=p.z;a.t0=now;a.dur=1;a.mode='graze';a.until=now+2+r()*4;return a.mode;}\r\n const ang=r()*6.283,rr=r()*WANDER_R;moveTo(a,herd.x+Math.cos(ang)*rr,herd.z+Math.sin(ang)*rr,now,herd.spec.speed,'walk');return a.mode;\r\n}\r\n// herds on open tiles: a herd centre is a tile centre whose 5x5 neighbourhood is all in the species' tile set\r\nexport function placeHerds(grid,seed=1){const r=mulberry32(seed>>>0||1),{cls,W,x0,z0,tile}=grid,herds=[];\r\n const ok=(i,j,set)=>{for(let b=-2;b<=2;b++)for(let a=-2;a<=2;a++){const c=cls[(j+b)*W+i+a];if(c===undefined||!set.includes(c))return false;}return true;};\r\n SPECIES.forEach((name,s)=>{const spec=HERDS[name];for(let h=0,tries=0;h<spec.herds&&tries<400;tries++){const i=2+Math.floor(r()*(W-5)),j=2+Math.floor(r()*(W-5));if(!ok(i,j,spec.tiles))continue;\r\n  const x=x0+(i+.5)*tile,z=z0+(j+.5)*tile;if(herds.some(q=>Math.hypot(q.x-x,q.z-z)<60))continue;const n=spec.size[0]+Math.floor(r()*(spec.size[1]-spec.size[0]+1)),animals=[];\r\n  for(let k=0;k<n;k++){const ax=x+(r()-.5)*10,az=z+(r()-.5)*10;animals.push({species:s,fx:ax,fz:az,tx:ax,tz:az,t0:0,dur:1,mode:'graze',until:r()*3,heading:r()*6.283,phase:r(),scale:.85+r()*.3});}\r\n  herds.push({species:s,spec,x,z,hx:x,hz:z,animals,next:HERD_DRIFT_S*r()});h++;}});\r\n return herds;}\r\n\r\nexport function createHerds(T,{grid,heightAt,seed=1,scale=1}){\r\n const herds=placeHerds(grid,seed),all=herds.flatMap(h=>h.animals.map(a=>({a,h}))),n=all.length;if(!n)return null;\r\n const atlas=paintHerdAtlas(),tex=new T.DataTexture(atlas.data,atlas.w,atlas.h,T.RGBAFormat);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.colorSpace=T.SRGBColorSpace;tex.needsUpdate=true;\r\n const geo=new T.InstancedBufferGeometry(),quad=new T.PlaneGeometry(1,1).translate(0,.5,0);geo.index=quad.index;geo.setAttribute('position',quad.getAttribute('position'));geo.setAttribute('uv',quad.getAttribute('uv'));\r\n const aPos=new T.InstancedBufferAttribute(new Float32Array(n*4),4).setUsage(T.DynamicDrawUsage),aCell=new T.InstancedBufferAttribute(new Float32Array(n*4),4).setUsage(T.DynamicDrawUsage);\r\n geo.setAttribute('aPos',aPos);geo.setAttribute('aCell',aCell);geo.instanceCount=n;\r\n const mat=new T.ShaderMaterial({fog:true,transparent:false,uniforms:T.UniformsUtils.merge([T.UniformsLib.fog,{uAtlas:{value:null},uCell:{value:new T.Vector2(atlas.cellW/atlas.w,atlas.cellH/atlas.h)}}]),\r\n  vertexShader:`#include <common>\r\n#include <fog_pars_vertex>\r\nattribute vec4 aPos,aCell;varying vec2 vUv;\r\nvoid main(){float m=aCell.w;vUv=vec2(m>.5?1.-uv.x:uv.x,uv.y);vUv=vec2((aCell.y*3.+aCell.z+vUv.x),aCell.x+1.-vUv.y);\r\n float s=length(modelMatrix[0].xyz);vec4 mvPosition=modelViewMatrix*vec4(aPos.xyz,1.);mvPosition.xy+=vec2(position.x*aPos.w*1.5,position.y*aPos.w)*s;\r\n gl_Position=projectionMatrix*mvPosition;\r\n#include <fog_vertex>\r\n}`,\r\n  fragmentShader:`#include <common>\r\n#include <fog_pars_fragment>\r\nuniform sampler2D uAtlas;uniform vec2 uCell;uniform vec3 cwSpriteTint;varying vec2 vUv;\r\nvoid main(){vec4 c=texture2D(uAtlas,vUv*uCell);if(c.a<.5)discard;gl_FragColor=vec4(c.rgb*cwSpriteTint,1.);\r\n#include <colorspace_fragment>\r\n#include <fog_fragment>\r\n}`});\r\n mat.uniforms.uAtlas.value=tex;mat.uniforms.cwSpriteTint=SPRITE_TINT;\r\n const mesh=new T.Mesh(geo,mat);mesh.name='herds';mesh.frustumCulled=false;mesh.raycast=()=>{};\r\n const group=new T.Group();group.name='herds-root';group.scale.setScalar(scale);group.add(mesh);\r\n const r=mulberry32((seed^0x51ed)>>>0),cam=new T.Vector3(),fwd=new T.Vector3();let lastStep=-1,stats={animals:n,herds:herds.length,fleeing:0};\r\n function update(t,focus,camera,camD=Infinity){ // t seconds; focus {x,z} local metres; camera for facing\r\n  if(t-lastStep>=1/STEP_HZ){lastStep=t;let f=0;for(const h of herds){if(t>=h.next){h.next=t+HERD_DRIFT_S;const ang=r()*6.283;h.x=h.hx+Math.cos(ang)*20;h.z=h.hz+Math.sin(ang)*20;}\r\n    for(const a of h.animals)if(stepAnimal(a,h,t,focus,camD,r)==='flee')f++;}stats.fleeing=f;}\r\n  let camYaw=0;if(camera){camera.getWorldDirection(fwd);camYaw=Math.atan2(fwd.x,-fwd.z);}\r\n  for(let i=0;i<n;i++){const {a}=all[i],p=animalAt(a,t,P),y=heightAt(p.x,p.z),rel=a.heading-camYaw,h256=((Math.round(rel/(2*Math.PI)*256)%256)+256)%256,{stored,mirror}=facingFor(sectorOf(h256));\r\n   const moving=p.k<1&&a.mode!=='graze',fps=a.mode==='flee'?FLEE_FPS:ANIMAL_FPS,frame=moving?Math.floor(t*fps+a.phase*2)%2:2,len=(a.species?HERDS.hopper:HERDS.goat).len*a.scale;\r\n   aPos.array.set([p.x,y,p.z,len/1.5],i*4);aCell.array.set([a.species,stored,frame,mirror?1:0],i*4);}\r\n  aPos.needsUpdate=aCell.needsUpdate=true;\r\n }\r\n return {group,mesh,herds,stats,update,dispose(){geo.dispose();quad.dispose();mat.dispose();tex.dispose();group.removeFromParent();}};\r\n}\r\n","modules/coach-world-rts/hubs-kit.mjs":"// R74 Gym Hubs: the big gyms of the Vegas bake (chosen at BAKE time from OSM, names stripped) stand as monolith towers ~300 m tall\r\n// with a glowing crown and a sky beam, so they read across the whole city. Generic label only. Never raidable (userData.layer='hub').\r\n// Data: /world/v1/hubs.json = {v:1,height,hubs:[{id,lat,lon,r}]} (also a 'hubs' point layer inside the z13-16 tiles).\r\nimport {toLocal} from '../coach-world/world-tiles.mjs';\r\nimport {createPs1Material} from './ps1-material.mjs';\r\n\r\nexport const HUB_HEIGHT=300,HUB_LABEL='Gym Hub',HUBS_URL='/world/v1/hubs.json',HUB_COLORS=Object.freeze({wall:'#1d2433',trim:'#38e8ff',glow:'#7ff6ff'});\r\nexport const MAX_HUB_TRIS=400,MAX_RANGE_M=40000;\r\n\r\n// strict parse: only id/lat/lon/r survive, so a name/brand that sneaks into the data can never reach the UI.\r\nexport function parseHubs(json){\r\n const rows=Array.isArray(json?.hubs)?json.hubs:[];\r\n return rows.filter(h=>Number.isFinite(h?.lat)&&Number.isFinite(h?.lon)&&Math.abs(h.lat)<=90&&Math.abs(h.lon)<=180).slice(0,200)\r\n  .map((h,i)=>({id:Number.isInteger(h.id)?h.id:i+1,lat:h.lat,lon:h.lon,r:Math.max(8,Math.min(40,Number(h.r)||16))}));\r\n}\r\nlet cached=null;\r\nexport function loadHubs(fetchImpl=globalThis.fetch?.bind(globalThis)){\r\n if(!fetchImpl)return Promise.resolve([]);\r\n return cached??=fetchImpl(HUBS_URL,{credentials:'omit',referrerPolicy:'no-referrer'}).then(r=>r.ok?r.json():null).then(parseHubs).catch(()=>{cached=null;return [];});\r\n}\r\nexport const resetHubCache=()=>{cached=null;};\r\n// hubs -> metres about origin {lat,lng}; far ones are dropped\r\nexport const localHubs=(hubs,origin,range=MAX_RANGE_M)=>hubs.map(h=>({...h,...toLocal(origin,h.lat,h.lon)})).filter(h=>Math.hypot(h.x,h.z)<=range);\r\nconst M=111320,RAD=Math.PI/180;\r\n// nearest hub as {hub,dist,bearing} (bearing degrees clockwise from north), or null\r\nexport function nearestHub(hubs,from){\r\n let best=null;\r\n for(const h of hubs){const dx=(h.lon-from.lng)*M*Math.cos(from.lat*RAD),dy=(h.lat-from.lat)*M,d=Math.hypot(dx,dy);if(!best||d<best.dist)best={hub:h,dist:d,bearing:(Math.atan2(dx,dy)/RAD+360)%360};}\r\n return best;\r\n}\r\n\r\nfunction labelSprite(T){\r\n if(typeof document==='undefined')return null;\r\n const c=document.createElement('canvas');c.width=256;c.height=64;const g=c.getContext('2d');if(!g)return null;\r\n g.fillStyle='#04080dcc';g.fillRect(0,8,256,48);g.strokeStyle=HUB_COLORS.trim;g.lineWidth=3;g.strokeRect(2,10,252,44);\r\n g.fillStyle=HUB_COLORS.glow;g.font='bold 30px monospace';g.textAlign='center';g.textBaseline='middle';g.fillText(HUB_LABEL.toUpperCase(),128,33);\r\n const t=new T.CanvasTexture(c);t.magFilter=t.minFilter=T.NearestFilter;t.generateMipmaps=false;\r\n const m=new T.SpriteMaterial({map:t,fog:false,depthWrite:false,transparent:true}),s=new T.Sprite(m);s.scale.set(120,30,1);s.name='hub-label';return s;\r\n}\r\n\r\n// hubs: [{id,x,z,r}] in metres about the world-layer root (root carries `scale`). One merged tower geometry per hub shape (shared), instanced by Mesh per hub.\r\nexport function createHubs(T,{hubs=[],scale=1,resolution=[320,180],height=HUB_HEIGHT,labels=true}={}){\r\n const group=new T.Group();group.name='gym-hubs';group.userData.layer='hub';group.scale.setScalar(scale);\r\n const parts=[],mark=o=>{o.userData.layer='hub';return o;};\r\n // monolith: tapering 4-sided slab (unit radius, unit height) + two glow bands + crown shard\r\n const tower=new T.CylinderGeometry(.62,1,1,4,1);tower.rotateY(Math.PI/4);tower.translate(0,.5,0);\r\n const col=new Float32Array(tower.attributes.position.count*3),lo=new T.Color(HUB_COLORS.wall),hi=new T.Color('#5a3a99');\r\n for(let i=0;i<col.length/3;i++){const t=tower.attributes.position.getY(i),c=lo.clone().lerp(hi,t*t);col.set([c.r,c.g,c.b],i*3);}\r\n tower.setAttribute('color',new T.BufferAttribute(col,3));\r\n const wall=createPs1Material(T,{vertexColors:true,resolution});\r\n const glow=new T.MeshBasicMaterial({color:HUB_COLORS.trim,fog:false}),beamMat=new T.MeshBasicMaterial({color:HUB_COLORS.glow,transparent:true,opacity:.55,blending:T.AdditiveBlending,depthWrite:false,fog:false});\r\n const band=new T.CylinderGeometry(1.02,1.02,.012,4,1);band.rotateY(Math.PI/4);\r\n const crownGeo=new T.OctahedronGeometry(1,0),beamGeo=new T.CylinderGeometry(.5,.5,1,6,1,true);beamGeo.translate(0,.5,0);\r\n parts.push(tower,band,crownGeo,beamGeo,wall,glow,beamMat);\r\n const crowns=[],beams=[];\r\n for(const h of hubs){\r\n  const r=1.5*Math.max(8,Math.min(40,h.r||16)),hub=mark(new T.Group());hub.name=`gym-hub-${h.id}`;hub.position.set(h.x,-3,h.z);\r\n  const body=mark(new T.Mesh(tower,wall));body.scale.set(r,height+3,r);hub.add(body);\r\n  for(const f of [.45,.8]){const b=mark(new T.Mesh(band,glow));b.scale.set(r*(1-.38*f),(height+3)*1,r*(1-.38*f));b.position.y=(height+3)*f;hub.add(b);}\r\n  const crown=mark(new T.Mesh(crownGeo,glow));crown.scale.set(r*.38,r*.9,r*.38);crown.position.y=height+3+r*.9;hub.add(crown);crowns.push(crown);\r\n  const beam=mark(new T.Mesh(beamGeo,beamMat));beam.scale.set(r*.3,900,r*.3);beam.position.y=height+3;hub.add(beam);beams.push(beam);\r\n  if(labels){const s=labelSprite(T);if(s){mark(s).position.y=height+3+r*2.4+20;hub.add(s);parts.push(s.material.map,s.material);}}\r\n  group.add(hub);\r\n }\r\n return {group,count:hubs.length,trisPerHub:(tower.index?tower.index.count:tower.attributes.position.count)/3,\r\n  update(t=0){const k=.5+.5*Math.sin(t*2);beamMat.opacity=.35+.3*k;for(const c of crowns){c.rotation.y=t*1.2;}},\r\n  dispose(){group.removeFromParent();parts.forEach(p=>p.dispose?.());group.clear();}};\r\n}\r\n","modules/coach-world-rts/hubs-map.mjs":"// R74 2D map: Gym Hub icons (glowing diamond + generic \"Gym Hub\" label) on the MapLibre map. Attached through RTS_MAP_FEATURES (map-view.mjs seam S4).\r\nimport {loadHubs,HUB_LABEL} from './hubs-kit.mjs';\r\nconst SRC='cw-gym-hubs',LAYERS=['cw-gym-hubs-glow','cw-gym-hubs','cw-gym-hubs-label'];\r\nexport const hubGeoJSON=hubs=>({type:'FeatureCollection',features:hubs.map(h=>({type:'Feature',id:h.id,geometry:{type:'Point',coordinates:[h.lon,h.lat]},properties:{label:HUB_LABEL,kind:'hub'}}))});\r\nexport async function attachHubs(map,{signal}={},deps={}){\r\n const hubs=await (deps.loadHubs||loadHubs)();if(signal?.aborted||!hubs.length)return;\r\n map.addSource(SRC,{type:'geojson',data:hubGeoJSON(hubs)});\r\n map.addLayer({id:LAYERS[0],type:'circle',source:SRC,paint:{'circle-radius':['interpolate',['linear'],['zoom'],8,9,14,22],'circle-color':'#38e8ff','circle-opacity':.22,'circle-blur':.8}});\r\n map.addLayer({id:LAYERS[1],type:'circle',source:SRC,paint:{'circle-radius':['interpolate',['linear'],['zoom'],8,3.5,14,7],'circle-color':'#7ff6ff','circle-stroke-color':'#1d2433','circle-stroke-width':2}});\r\n map.addLayer({id:LAYERS[2],type:'symbol',source:SRC,minzoom:10,layout:{'text-field':['get','label'],'text-size':12,'text-offset':[0,1.4],'text-anchor':'top','text-allow-overlap':false},paint:{'text-color':'#c9fbff','text-halo-color':'#04080d','text-halo-width':1.5}});\r\n return ()=>{try{for(const id of LAYERS.slice().reverse())if(map.getLayer?.(id))map.removeLayer(id);if(map.getSource?.(SRC))map.removeSource(SRC);}catch{/* map already removed */}};\r\n}\r\n","modules/coach-world-rts/hud-collapse.css":"/* R52 collapsible command console (Clash-of-Clans style). Loaded by hud.mjs after hud.css.\r\n   Expanded: small chevron handle above the full bezel. Collapsed (.is-collapsed on .cw-rts-console): 40 px bar =\r\n   handle + 3 mini buttons (Build, Attack, Sheet); the card/portrait/minimap are hidden but stay in the DOM.\r\n   Build tray: slides up above the bar with 4 tabs and 64 px tiles. Class names avoid hud.css ones (.cw-rts-mini is the minimap canvas). */\r\n.cw-rts .cw-rts-console{transition:transform 200ms ease}\r\n\r\n.cw-rts .cw-rts-handle{position:relative;display:block;flex:none;width:48px;height:20px;margin:0 auto 4px;padding:0;background:#1b222a;border:2px solid #4a5662;color:transparent;pointer-events:auto;touch-action:manipulation}\r\n.cw-rts .cw-rts-handle::before{content:\"\";position:absolute;width:10px;height:10px;border-right:2px solid #dfe8ee;border-bottom:2px solid #dfe8ee;top:50%;left:50%;margin:-7px 0 0 -5px;transform:rotate(45deg)}\r\n.cw-rts .cw-rts-console.is-collapsed .cw-rts-handle::before{transform:rotate(-135deg);margin-top:-3px}\r\n\r\n.cw-rts .cw-rts-mini-row{display:none;gap:6px;flex:none}\r\n.cw-rts .cw-rts-mini-btn{width:48px;height:32px;min-width:48px;padding:0;display:flex;align-items:center;justify-content:center;background:#1b222a;border:2px solid #4a5662;color:#dfe8ee;touch-action:manipulation}\r\n.cw-rts .cw-rts-mini-btn img{width:20px;height:20px;image-rendering:pixelated}\r\n.cw-rts .cw-rts-mini-btn:active,.cw-rts .cw-rts-mini-btn[aria-pressed=true]{background:var(--cw-faction,#7fd4ff);color:#000}\r\n.cw-rts .cw-rts-mini-btn:focus-visible,.cw-rts .cw-rts-handle:focus-visible{outline:2px solid #fff;outline-offset:-4px}\r\n\r\n.cw-rts .cw-rts-console.is-collapsed .cw-rts-bezel{box-sizing:border-box;height:40px;overflow:hidden;border:0;border-image:none;background:#10161a;border-top:2px solid var(--cw-faction,#7fd4ff);display:flex;align-items:center;justify-content:center;gap:6px;padding:0 8px}\r\n.cw-rts .cw-rts-console.is-collapsed .cw-rts-bezel > .cw-rts-inner{display:none}\r\n.cw-rts .cw-rts-console.is-collapsed .cw-rts-handle{height:40px;margin:0}\r\n.cw-rts .cw-rts-console.is-collapsed .cw-rts-mini-row{display:flex}\r\n\r\n.cw-rts .cw-rts-tray{position:absolute;left:0;right:0;bottom:100%;background:rgba(10,16,20,.94);border-top:2px solid var(--cw-faction,#7fd4ff);transform:translateY(12px);opacity:0;pointer-events:none;transition:transform 200ms ease,opacity 200ms ease}\r\n.cw-rts .cw-rts-tray.is-open{transform:translateY(0);opacity:1;pointer-events:auto}\r\n\r\n.cw-rts .cw-rts-tray-tabs{display:flex}\r\n.cw-rts .cw-rts-tray-tab{flex:1;height:36px;padding:0;background:none;border:0;border-bottom:2px solid transparent;color:#dfe8ee;opacity:.7;font-size:11px;text-transform:uppercase;letter-spacing:.08em;touch-action:manipulation}\r\n.cw-rts .cw-rts-tray-tab[aria-selected=\"true\"]{opacity:1;border-bottom-color:var(--cw-faction,#7fd4ff)}\r\n\r\n.cw-rts .cw-rts-tray-row{display:flex;gap:8px;overflow-x:auto;overscroll-behavior-x:contain;padding:8px;scroll-snap-type:x proximity;scrollbar-width:none}\r\n.cw-rts .cw-rts-tray-row::-webkit-scrollbar{display:none}\r\n.cw-rts .cw-rts-tray-tile{flex:none;width:64px;height:64px;display:flex;flex-direction:column;justify-content:space-between;align-items:center;padding:4px;background:rgba(12,22,28,.85);border:2px solid rgba(255,255,255,.12);color:#dfe8ee;scroll-snap-align:start;touch-action:manipulation}\r\n.cw-rts .cw-rts-tray-tile canvas{width:32px;height:32px;image-rendering:pixelated}\r\n.cw-rts .cw-rts-tray-tile b{font-size:10px;font-weight:400;font-variant-numeric:tabular-nums}\r\n.cw-rts .cw-rts-tray-tile.is-locked{filter:grayscale(.6) brightness(.7);position:relative}\r\n.cw-rts .cw-rts-tray-tile.is-locked::after{content:\"\\1F512\";position:absolute;right:2px;top:2px;font-size:10px}\r\n\r\n@media (prefers-reduced-motion:reduce){\r\n  .cw-rts .cw-rts-console,.cw-rts .cw-rts-tray{transition:none}\r\n}\r\n/* build fix: placement bar (ghost on the plot + BUILD HERE) sits where the tray opens */\r\n.cw-rts .cw-rts-place{position:absolute;left:0;right:0;bottom:100%;display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px;background:rgba(10,16,20,.94);border-top:2px solid var(--cw-faction,#7fd4ff);z-index:4;pointer-events:auto}\r\n.cw-rts .cw-rts-place[hidden]{display:none}\r\n.cw-rts .cw-rts-place-msg{flex:1 1 100%;margin:0;color:#dfe8ee;font-size:13px;text-align:center}\r\n.cw-rts .cw-rts-place-msg.is-error{color:#ff8a7a}\r\n.cw-rts .cw-rts-place-go{flex:1;min-height:52px;font-size:20px;letter-spacing:.06em;background:#57e08a;color:#08130c;border:0;touch-action:manipulation}\r\n.cw-rts .cw-rts-place-go[aria-disabled=\"true\"]{background:#4a5460;color:#c9d2d8}\r\n.cw-rts .cw-rts-place-x{min-height:52px;padding:0 14px;background:none;color:#dfe8ee;border:2px solid rgba(255,255,255,.25);touch-action:manipulation}\r\n.cw-rts .cw-rts-tray.is-open{z-index:4}\r\n.cw-rts .cw-rts-toast{z-index:5}\r\n","modules/coach-world-rts/hud.css":"/* W5 minimal RTS HUD. Active only under .cw-rts (added by mountHud); legacy layout is untouched otherwise. */\r\n@font-face{font-family:'CW Pixelify';src:url(ui/pixelify-sans.woff2) format('woff2');font-weight:400 700;font-display:swap}\r\n@font-face{font-family:'CW m5x7';src:url(ui/m5x7.woff2) format('woff2');font-display:swap}\r\n\r\n.cw-rts{--chrome:84px;--px:'CW Pixelify',ui-monospace,monospace;--num:'CW m5x7',ui-monospace,monospace;width:100vw;height:100dvh;max-width:100vw;max-height:100dvh;border:0;border-radius:0;inset:0;overflow:hidden}\r\n.cw-rts .cw-shell{position:static;min-height:0}\r\n/* legacy chrome shrinks to the Close button + tab bar; both stay reachable and focusable */\r\n.cw-rts .cw-header{position:fixed;top:0;right:0;z-index:7;padding:0;border:0;background:none}\r\n.cw-rts .cw-header>div{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}\r\n.cw-rts .cw-header [data-close]{width:48px;height:44px;min-height:44px;padding:0;margin:0;font:16px var(--num);border-radius:0;background:#14181ddd}\r\n.cw-rts .cw-tabs{position:fixed;top:44px;left:0;right:0;z-index:6;padding:0 6px;gap:4px;background:#0d1217cc}\r\n.cw-one-map .cw-tabs{display:none}\r\n.cw-one-map .cw-cam{margin-left:auto;margin-right:8px;min-height:44px}.cw-header{gap:8px}\r\n.cw-rts .cw-tabs button{flex:1;min-height:44px;font:400 16px var(--px)}\r\n.cw-rts [data-identity],.cw-rts .cw-status{position:fixed;left:-9999px;top:auto}\r\n.cw-rts .cw-content{padding:0}\r\n.cw-rts .cw-base-layout{display:block}\r\n.cw-rts .cw-base-main{display:contents}\r\n.cw-rts .cw-scene-wrap{position:fixed;left:0;right:0;top:var(--chrome);bottom:var(--cw-console-h,0px);border:0;border-radius:0;z-index:0}\r\n.cw-rts .cw-base-canvas{width:100%;height:100%}\r\n.cw-rts .cw-scene-tag{display:none}\r\n/* Center moved into the HUD (top-strip button + minimap long-press): the in-canvas button sat on a top-right SITE pad. Still clickable via the proxy. */\r\n.cw-rts .cw-recenter{display:none}\r\n\r\n/* top strip: read-only vitals */\r\n.cw-rts-hud{position:fixed;inset:0;z-index:5;pointer-events:none;font:16px var(--num)}\r\n.cw-rts .cw-rts-top{position:fixed;top:0;left:0;right:var(--cw-hdr-w,224px);height:44px;display:flex;align-items:stretch;gap:2px;background:#0d1217e6;border-bottom:2px solid var(--cw-faction);color:#dfe8ee;pointer-events:auto;justify-content:flex-end}\r\n    .cw-rts .cw-rts-vital{flex:0 0 auto;display:flex;align-items:center;gap:4px;min-width:0}\r\n.cw-rts .cw-rts-vital span{font:400 16px var(--px);color:var(--cw-faction)}\r\n    .cw-rts .cw-rts-num{font:14px var(--num);font-weight:400}\r\n    .cw-rts .cw-rts-glyph-fuel{display:inline-block;width:16px;height:16px;background:#ff8c2a;flex:none}\r\n    .cw-rts .cw-rts-glyph-calm{display:inline-block;width:16px;height:16px;background:#6fd46a;flex:none}\r\n    .cw-rts .cw-rts-glyph-charge{display:inline-block;width:16px;height:16px;background:#4fd8ff;flex:none}\r\n.cw-rts .cw-rts-flip-btn{flex:0 0 48px;min-height:44px;padding:0;border:0;border-radius:0;background:#1b222a;color:#dfe8ee;font:16px var(--num)}\r\n.cw-rts .cw-rts-flip-btn[aria-pressed=true]{background:var(--cw-faction);color:#000}\r\n\r\n/* console: per-family 9-slice bezel. Border is 24px so the 96px art slices at 24; content overlaps the empty part with a negative margin. */\r\n.cw-rts .cw-rts-console{--bz:url(ui/bezel-occupied.png);position:fixed;left:0;right:0;bottom:0;padding:0 0 var(--myr5-bar,env(safe-area-inset-bottom,0px));pointer-events:none;display:flex;justify-content:center;background:linear-gradient(#1a1f26,#0d1014);box-shadow:inset 0 3px 0 #3d4a57}\r\n.cw-rts .cw-rts-console::before{content:'';position:absolute;top:0;left:0;right:0;height:6px;background:repeating-linear-gradient(45deg,#f5c400 0 8px,#111 8px 16px)}\r\n.cw-rts .cw-rts-console[data-family=warp]{--bz:url(ui/bezel-warp.png)}\r\n.cw-rts .cw-rts-console[data-family=seed]{--bz:url(ui/bezel-seed.png)}\r\n.cw-rts .cw-rts-bezel{pointer-events:auto;width:min(100%,560px);border:24px solid transparent;border-image:var(--bz) 24 fill/24px/0 stretch;image-rendering:pixelated;color:#dfe8ee;position:relative}\r\n/* V5-C1: SC-style corner rivets, overlay only (pointer-events:none, no clip-path) */\r\n.cw-rts .cw-rts-bezel::after{content:'';position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle,#c9d4dc 0 1.5px,#1d252d 2px 3px,#0000 3.5px) 4px 4px/8px 8px no-repeat,radial-gradient(circle,#c9d4dc 0 1.5px,#1d252d 2px 3px,#0000 3.5px) right 4px top 4px/8px 8px no-repeat,radial-gradient(circle,#c9d4dc 0 1.5px,#1d252d 2px 3px,#0000 3.5px) left 4px bottom 4px/8px 8px no-repeat,radial-gradient(circle,#c9d4dc 0 1.5px,#1d252d 2px 3px,#0000 3.5px) right 4px bottom 4px/8px 8px no-repeat}\r\n.cw-rts .cw-rts-inner{display:flex;align-items:center;gap:6px;margin:-14px}\r\n.cw-rts .cw-rts-mini-wrap{position:relative;flex:0 0 auto;width:var(--sz,96px);height:var(--sz,96px);box-sizing:border-box;border:2px solid #3d4a57;border-radius:14px;overflow:hidden;background:#031a0a;box-shadow:0 0 6px #33ff6655,inset 0 0 10px #000,inset 0 0 0 2px #000}\r\n.cw-rts .cw-rts-mini-wrap::after{content:'';position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(0deg,#0000 0 1px,#0006 1px 2px)}\r\n.cw-rts .cw-rts-mini{display:block;width:100%;height:100%;image-rendering:pixelated;touch-action:manipulation;cursor:pointer}\r\n.cw-rts .cw-rts-portrait{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;text-align:center;overflow:hidden}\r\n.cw-rts .cw-rts-portrait canvas{width:64px;height:64px;image-rendering:pixelated;background:#0b0f12;border:2px solid var(--cw-faction)}\r\n.cw-rts .is-compact .cw-rts-portrait canvas{width:48px;height:48px}\r\n.cw-rts .cw-rts-pname{font:400 12px/1.05 var(--px);white-space:normal;overflow-wrap:anywhere;max-width:100%;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\r\n.cw-rts .cw-rts-pdetail{font:16px var(--num);color:#b8c4cc}\r\n.cw-rts .cw-rts-burst canvas{animation:cw-rts-burst .24s steps(3)}\r\n@keyframes cw-rts-burst{0%{filter:brightness(3)}50%{filter:brightness(1.8)}100%{filter:none}}\r\n.cw-rts .cw-rts-card{flex:0 0 auto;display:grid;grid-template-columns:repeat(3,56px);grid-auto-rows:56px;gap:4px}\r\n.cw-rts-resources{position:absolute;top:calc(var(--chrome,84px) + 2px);left:4px;right:4px;color:#d8f4df;background:#101a20e8;text-align:center;font:11px/20px ui-monospace,monospace;pointer-events:none}\r\n.cw-rts-control-groups{position:absolute;top:calc(var(--chrome,84px) + 25px);left:5px;display:flex;gap:4px;pointer-events:auto}.cw-rts-control-groups button{min-width:44px;min-height:44px;background:#16372bde;color:#c6f6dc;border:1px solid #74a68d;font:13px ui-monospace,monospace}\r\n.cw-rts .cw-rts-building-context{display:block;width:100%;max-width:100%;padding:2px 3px;box-sizing:border-box;overflow-wrap:anywhere;color:#dfe8ee;background:#0d1217e6;border:1px solid #4a5662;font:9px/1.25 var(--num);white-space:normal;}\r\n.cw-rts .is-compact .cw-rts-card{grid-template-columns:repeat(3,48px);grid-auto-rows:48px;gap:2px}\r\n/* compact (landscape): V4-C6 trims the empty 24px strip above the 3x3 grid (top margin -48, bottom flush) so the world band grows by 24px */\r\n.cw-rts .is-compact .cw-rts-inner{margin:-48px 0 -24px}\r\n.cw-rts .cw-rts-cmd{min-width:48px;min-height:48px;width:100%;height:100%;padding:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;background:#1b222a;border:2px solid #4a5662;border-radius:0;color:#dfe8ee;font:16px/1 var(--num);image-rendering:pixelated}\r\n.cw-rts .cw-rts-cmd img{width:32px;height:32px;image-rendering:pixelated;filter:drop-shadow(0 0 3px #54ff7c)}\r\n.cw-rts .is-compact .cw-rts-cmd img{width:16px;height:16px}\r\n.cw-rts .cw-rts-cmd:active{background:var(--cw-faction);color:#000}\r\n.cw-rts .cw-rts-cmd:disabled{opacity:.4}\r\n.cw-rts .cw-rts-cmd{box-shadow:inset 0 1px 0 #3a4a3a,inset 0 -1px 0 #000,inset 0 0 0 2px #0b1014}\r\n.cw-rts .cw-rts-cmd:hover{background:#26303a;border-color:#6b7a88}\r\n.cw-rts .cw-rts-cmd[aria-pressed=true]{border-color:var(--cw-faction)}\r\n.cw-rts .cw-rts-cmd:focus-visible,.cw-rts-mini:focus-visible,.cw-rts-flip-btn:focus-visible{outline:2px solid #fff;outline-offset:-4px}\r\n\r\n/* left-hand flip mirrors the console (C17) */\r\n.cw-rts .is-flipped .cw-rts-inner{flex-direction:row-reverse}\r\n\r\n/* toast over the canvas, text only (sound-off safe) */\r\n.cw-rts .cw-rts-toast{position:absolute;bottom:100%;left:0;right:0;margin:0 0 8px;text-align:center;font:16px var(--num);color:#fff;text-shadow:0 1px 0 #000,0 0 4px #000;opacity:0;transition:opacity .3s;pointer-events:none}\r\n.cw-rts .cw-rts-toast.is-on{opacity:1}\r\n\r\n/* Base sheet drawer holds the whole legacy World UI; scrolls, Close is sticky, top strip + tab bar stay visible above it */\r\n.cw-rts .cw-rts-sheet{display:none;position:fixed;left:0;right:0;bottom:0;top:var(--chrome);z-index:4;background:#101e24;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;border-top:2px solid var(--cw-faction);padding-bottom:var(--myr5-bar,0px)}\r\n.cw-rts-open .cw-rts-sheet{display:block}\r\n.cw-rts-open .cw-rts-console{display:none}\r\n.cw-rts .cw-rts-sheet-head{position:sticky;top:0;z-index:2;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;background:#0d1217;border-bottom:2px solid var(--cw-faction)}\r\n.cw-rts .cw-rts-sheet-head h3{margin:0;font:400 16px var(--px);letter-spacing:1px}\r\n.cw-rts .cw-rts-sheet-close{min-width:72px;min-height:48px;border-radius:0;font:16px var(--num)}\r\n.cw-rts .cw-rts-sheet-body{padding:12px 12px calc(24px + env(safe-area-inset-bottom,0px))}\r\n\r\n@media(max-height:500px) and (orientation:landscape){\r\n .cw-rts{--chrome:84px}\r\n   .cw-rts .cw-tabs{position:fixed;right:48px;left:auto;top:0;width:min(60%,300px);height:44px;background:none;z-index:8}\r\n   .cw-rts .cw-tabs button{min-height:44px}\r\n    .cw-rts-top{right:calc(48px + min(60%,300px))}\r\n    .cw-rts .cw-rts-num{font-size:12px}\r\n .cw-rts{--chrome:40px}\r\n}\r\n@media(prefers-reduced-motion:reduce){.cw-rts-burst canvas,.cw-rts-toast{animation:none;transition:none}}\r\n\r\n/* ---- Wave 2 full HUD ---- */\r\n.cw-rts .cw-rts-pbtn{padding:0;border:0;background:none;border-radius:0;min-height:0;min-width:0;line-height:0}\r\n.cw-rts .cw-rts-pbtn:focus-visible{outline:2px solid #fff;outline-offset:2px}\r\n.cw-rts .cw-rts-hp{display:flex;gap:1px;width:64px;height:6px;margin:2px 0;background:#000;padding:1px;box-sizing:content-box}\r\n.cw-rts .cw-rts-hp i{flex:1;background:#1a2228}\r\n.cw-rts .cw-rts-hp i.g{background:#2fdc4a}.cw-rts .cw-rts-hp i.y{background:#e8d02a}.cw-rts .cw-rts-hp i.r{background:#e0382a}\r\n.cw-rts .cw-rts-chips{display:flex;gap:2px;flex-wrap:wrap;justify-content:center;max-width:64px}\r\n.cw-rts .cw-rts-chips b{width:8px;height:8px;border:1px solid #2fdc4a;background:var(--c)}\r\n.cw-rts .cw-rts-chips:empty{display:none}\r\n.cw-rts .cw-rts-queue{display:flex;gap:3px;margin-top:2px}\r\n.cw-rts .cw-rts-queue[hidden]{display:none}\r\n.cw-rts .cw-rts-slot{position:relative;width:26px;min-width:26px;min-height:26px;height:26px;padding:0;border:2px solid #4a5662;border-radius:0;background:#0b0f12;overflow:hidden}\r\n.cw-rts .cw-rts-slot i{position:absolute;left:0;bottom:0;top:0;width:0;background:var(--cw-faction);opacity:.85}\r\n.cw-rts .cw-rts-slot:disabled{opacity:.35}\r\n.cw-rts .cw-rts-slot:focus-visible{outline:2px solid #fff;outline-offset:-4px}\r\n.cw-rts .cw-rts-card[hidden]{display:none}\r\n.cw-rts .cw-rts-bmenu{display:grid;grid-template-columns:none;grid-template-rows:repeat(3,56px);grid-auto-flow:column;grid-auto-columns:56px;width:176px;overflow-x:auto;overscroll-behavior:contain;scroll-snap-type:x proximity}\r\n.cw-rts .is-compact .cw-rts-bmenu{grid-template-rows:repeat(3,48px);grid-auto-columns:48px;width:152px}\r\n.cw-rts .cw-rts-tile{scroll-snap-align:start;font-size:16px}\r\n.cw-rts .cw-rts-tile canvas{width:32px;height:32px;image-rendering:pixelated}\r\n.cw-rts .is-compact .cw-rts-tile canvas{width:24px;height:24px}\r\n.cw-rts .cw-rts-cmd[aria-disabled=true]{opacity:.4}\r\n.cw-rts .cw-rts-cmd.is-primary{border-color:#f0c64a;background:#3a3010;color:#ffe28a;animation:cw-rts-pulse 1s steps(2) infinite}\r\n@keyframes cw-rts-pulse{50%{background:#5a4a14}}\r\n/* alert line: red for errors */\r\n.cw-rts .cw-rts-toast.is-error{color:#ff8a7a}\r\n/* low fuel (A5 4f): blinking red FUEL chip, vignette */\r\n.cw-rts-hud.is-lowfuel::before{content:'';position:fixed;inset:0;pointer-events:none;box-shadow:inset 0 0 90px 30px #000a;z-index:-1}\r\n.cw-rts-hud.is-lowfuel .cw-rts-vital:first-child b{color:#ff5a4a;animation:cw-rts-blink 1s steps(2) infinite}\r\n@keyframes cw-rts-blink{50%{opacity:.25}}\r\n@media(prefers-reduced-motion:reduce){.cw-rts .cw-rts-cmd.is-primary,.cw-rts-hud.is-lowfuel .cw-rts-vital:first-child b{animation:none}}\r\n\r\n/* ---- W6 level-up overlay (levelup.mjs). Over the canvas only; never blocks input. ---- */\r\n.cw-rts-lvl{position:fixed;inset:0;pointer-events:none;z-index:3}\r\n.cw-rts-lvl canvas{position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated}\r\n.cw-rts-lvl-dim{position:absolute;inset:0;background:#000;opacity:0;transition:opacity .3s}\r\n.cw-rts-lvl.is-dim .cw-rts-lvl-dim{opacity:.2}\r\n.cw-rts-lvl-flash{position:absolute;inset:0;background:#fff;opacity:0}\r\n.cw-rts-lvl-flash.go{animation:cw-lvl-flash 84ms steps(2)}\r\n@keyframes cw-lvl-flash{0%,100%{opacity:0}30%,70%{opacity:.85}}\r\n.cw-rts-lvl-ring{position:absolute;left:50%;top:38%;width:24px;height:24px;margin:-12px 0 0 -12px;border:3px solid var(--cw-faction,#fff);border-radius:50%;opacity:0}\r\n.cw-rts-lvl-ring.go{animation:cw-lvl-ring .5s ease-out}\r\n@keyframes cw-lvl-ring{0%{transform:scale(1);opacity:1}100%{transform:scale(12);opacity:0}}\r\n.cw-rts-lvl-banner{position:absolute;left:0;right:0;top:34%;padding:6px 8px;text-align:center;font:400 20px var(--px,monospace);color:#fff;background:#0d1217cc;border-top:2px solid var(--cw-faction,#fff);border-bottom:2px solid var(--cw-faction,#fff);text-shadow:0 2px 0 #000;opacity:0}\r\n.cw-rts-lvl-banner:not(:empty){opacity:1}\r\n.cw-rts-lvl-banner.go{animation:cw-lvl-reveal .4s steps(6)}\r\n@keyframes cw-lvl-reveal{0%{transform:scale(.6)}60%{transform:scale(1.1)}100%{transform:scale(1)}}\r\n@media(prefers-reduced-motion:reduce){.cw-rts-lvl-flash,.cw-rts-lvl-ring,.cw-rts-lvl-banner.go{animation:none}.cw-rts-lvl-dim{transition:none}}\r\n.cw-rts .cw-rts-pmove{margin-top:3px;min-height:36px;min-width:60px;padding:0 8px;font:16px var(--num);color:#fff0a6;background:#17222a;border:2px solid #f0d890;border-radius:0;touch-action:manipulation}\r\n.cw-rts .cw-rts-pmove[hidden]{display:none}\r\n.cw-rts .cw-rts-pmove[aria-pressed=\"true\"]{background:#f0d890;color:#0b0f12}\r\n\r\n/* polish G: header Peer + gear, faction chips under the top strip, settings sheet knobs */\r\n.cw-rts .cw-header .cw-cam,.cw-rts .cw-header .cw-gear{height:44px;min-height:44px;margin:0;padding:0 8px;border:0;border-radius:0;background:#14181ddd;color:#dfe8ee;font:16px var(--num);min-width:48px}\r\n.cw-rts .cw-header .cw-cam{font:400 14px var(--px)}\r\n.cw-rts .cw-header .cw-gear{font-size:22px;line-height:1}\r\n.cw-rts .cw-header .cw-cam:focus-visible,.cw-rts .cw-header .cw-gear:focus-visible{outline:2px solid #fff;outline-offset:-4px}\r\n.cw-rts .cw-rts-factions{position:fixed;top:44px;left:0;right:0;height:32px;display:flex;gap:3px;padding:0 4px;overflow-x:auto;background:#0d1217cc;pointer-events:auto;scrollbar-width:none;z-index:1}\r\n.cw-rts .cw-rts-factions::-webkit-scrollbar{display:none}\r\n.cw-rts .cw-rts-fac{flex:1 0 44px;min-width:44px;height:32px;min-height:32px;padding:0 4px;display:flex;align-items:center;justify-content:center;gap:4px;background:#1b222a;border:2px solid #36424d;border-radius:0;color:#dfe8ee;font:14px var(--px)}\r\n.cw-rts .cw-rts-fac i{width:6px;height:6px;background:var(--fc);flex:none}\r\n.cw-rts .cw-rts-fac[aria-pressed=true]{border-color:var(--fc);background:color-mix(in srgb,var(--fc) 28%,#0d1217)}\r\n.cw-rts .cw-rts-fac:focus-visible{outline:2px solid #fff;outline-offset:-4px}\r\n.cw-rts.cw-rts-open .cw-rts-factions{display:none}\r\n.cw-rts .cw-rts-knobs:empty{display:none}\r\n.cw-rts-knobs{padding:8px 0;color:#dfe8ee}\r\n/* polish G: three vitals + centre + flip must fit beside Peer/gear/close at 375 px: stack label over number */\r\n.cw-rts .cw-rts-top{right:var(--cw-hdr-w,224px)}\r\n    .cw-rts .cw-rts-vital{flex-direction:column;gap:0;line-height:1;justify-content:center}\r\n.cw-rts .cw-rts-vital span{font-size:11px}\r\n.cw-rts .cw-rts-top .cw-rts-flip-btn{flex:0 0 44px}\r\n/* V2-C5: SC-style counters (green numerals, icon kept) and dark plate seams; FUEL/CALM/LAUNCH labels stay as aria-labels */\r\n.cw-rts .cw-rts-vital>.cw-rts-num{color:#8dff6a;text-shadow:0 0 4px #2f8f1a,1px 1px 0 #051005}\r\n.cw-rts .cw-rts-top>*{box-shadow:inset 2px 0 0 #05080a}\r\n.cw-rts .cw-rts-top{box-shadow:inset 0 -3px 0 #05080a}\r\n.cw-rts .cw-rts-console{background:radial-gradient(circle 3px at 8px 8px,#c9d3dd 0,#5b6b7c 1.5px,#0000 2.5px),radial-gradient(circle 3px at calc(100% - 8px) 8px,#c9d3dd 0,#5b6b7c 1.5px,#0000 2.5px),radial-gradient(circle 3px at 8px calc(100% - 8px),#c9d3dd 0,#5b6b7c 1.5px,#0000 2.5px),radial-gradient(circle 3px at calc(100% - 8px) calc(100% - 8px),#c9d3dd 0,#5b6b7c 1.5px,#0000 2.5px),radial-gradient(ellipse 45% 90% at 50% 0,#27313b,#0000 100%),linear-gradient(#2b3442,#0c1016);box-shadow:inset 0 3px 0 #3d4a57,inset 0 1px 0 #5b6b7c}\r\n/* V2-C8 portrait console rebalance (390x844 only): minimap 72px, command card 3x48px cells with 2px gaps, centre box takes the rest (>=140px). Build menu and landscape unchanged. */\r\n@media (orientation:portrait){\r\n .cw-rts .cw-rts-inner{margin:-14px -20px}\r\n .cw-rts .cw-rts-inner>.cw-rts-mini-wrap{width:72px;height:72px}\r\n .cw-rts .cw-rts-card:not(.cw-rts-bmenu){grid-template-columns:repeat(3,48px);grid-auto-rows:48px;gap:2px}\r\n .cw-rts .cw-rts-card:not(.cw-rts-bmenu) .cw-rts-cmd img{width:24px;height:24px}\r\n}\r\n/* Inside the cockpit shell the World dialog IS the shell window (fixed at the shell insets, transforms forced off), so the\r\n   HUD layers anchor to it instead of the raw viewport: on a notched phone the viewport-fixed tabs/top strip sat under the\r\n   top rail and the console under the dock. The dialog already ends above the dock, so the console drops the --myr5-bar pad. */\r\nhtml.shell-ready .cw-rts :is(.cw-header,.cw-tabs,.cw-scene-wrap,.cw-rts-hud,.cw-rts-top,.cw-rts-console,.cw-rts-sheet,.cw-rts-factions,.cw-rts-selpanel,.cw-rts-lvl){position:absolute}\r\nhtml.shell-ready .cw-rts .cw-rts-console{padding-bottom:0}\r\nhtml.shell-ready .cw-rts .cw-rts-sheet{padding-bottom:0}\r\nhtml.shell-ready .cw-rts-hud.is-lowfuel::before{position:absolute}\r\n/* The shell hides in-dialog closers (its own exit key closes World), so the invisible Close leaves the header row and the\r\n   vitals strip gets its 56px back (Centre key was pushed off the left edge at 390). Collapsed bar fits the shell's 44px keys. */\r\nhtml.shell-ready dialog[data-shell-exit].cw-rts{--cw-hdr-w:160px}\r\nhtml.shell-ready dialog[data-shell-exit].cw-rts .cw-header [data-close]{display:none}\r\nhtml.shell-ready .cw-rts .cw-rts-console.is-collapsed .cw-rts-bezel{height:48px}\r\n/* Landscape shell window is ~275px tall: a full-width console (>=150px for the 3x3 card) would leave the map a sliver.\r\n   The console sits in the bottom-right corner at bezel width, the map fills the window left of it (HQ centred), and the tray /\r\n   placement bar / selection panel open to the LEFT of the console instead of above it. */\r\n@media(max-height:500px) and (orientation:landscape){\r\n html.shell-ready .cw-rts .cw-scene-wrap{bottom:0;right:var(--cw-console-w,340px)}\r\n html.shell-ready .cw-rts .cw-rts-console{left:auto;right:0;top:76px;align-items:flex-end}\r\n html.shell-ready .cw-rts .cw-rts-bezel{width:auto}\r\n html.shell-ready .cw-rts :is(.cw-rts-tray,.cw-rts-place){left:auto;right:100%;bottom:0;max-height:calc(100% + 64px);overflow-y:auto;box-sizing:border-box;width:min(560px,calc(100vw - var(--shell-left,0px) - var(--shell-right,0px) - var(--cw-console-w,340px)))}\r\n html.shell-ready .cw-rts .cw-rts-toast{left:auto;right:0;width:max-content;max-width:calc(100vw - var(--shell-left,0px) - var(--shell-right,0px) - 16px)}\r\n html.shell-ready .cw-rts .cw-rts-selpanel{right:calc(var(--cw-console-w,340px) + 4px);bottom:4px;max-height:calc(100% - 84px)}\r\n}\r\n/* R99: at 320 the well-inset HUD is too narrow for vital labels; keep the numbers, give the portrait room. */\r\n@media(max-width:359px){html.shell-ready:not([data-game-active=true]) .cw-rts .cw-rts-vital span{display:none}}\n\n/* Player shortcuts retain the selection-sensitive 3x3 command card below. */\n.cw-rts .cw-rts-console{flex-direction:column;align-items:center}\n.cw-rts .cw-rts-quickbar{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:3px;width:min(100%,560px);box-sizing:border-box;padding:4px;background:#10161af2;pointer-events:auto}\n.cw-rts .cw-rts-quickbar button{min-width:0;min-height:44px;padding:4px;color:#dfe8ee;background:#1b222a;border:1px solid #607184;font:600 12px/1.2 system-ui;touch-action:manipulation}\n.cw-rts .cw-rts-quickbar [data-rts-quick=run]{grid-column:span 2;color:#07140c;background:#8aefae}\n.cw-rts .cw-rts-quickbar button:focus-visible,.cw-run-loadout button:focus-visible{outline:2px solid #fff;outline-offset:-4px}\n.cw-rts .cw-rts-tray-tile.cw-rts-tray-tile{width:116px;height:auto;min-height:144px;gap:4px;justify-content:flex-start}\n.cw-rts .cw-rts-tray-reason{white-space:normal;line-height:1.3;color:#ffc39d}\n.cw-run-loadout{box-sizing:border-box;width:min(440px,calc(100vw - 16px));max-width:none;max-height:calc(100dvh - 24px);padding:16px;margin:auto;overflow:auto;overscroll-behavior:contain;background:#101c26;color:#edf6fc;border:2px solid #7fd4ff;font:14px/1.45 system-ui;pointer-events:auto}\n.cw-run-loadout::backdrop{background:#02070bd9}\n.cw-run-loadout h2{margin:0 0 8px;font-size:22px}.cw-run-loadout h3{font-size:15px}\n.cw-run-weapons{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}\n.cw-run-loadout button{min-width:0;min-height:48px;padding:8px;background:#223445;border:1px solid #718da4;color:#edf6fc;font:inherit;touch-action:manipulation}\n.cw-run-weapons button[aria-pressed=true]{background:#7fd4ff;color:#08131c;border-color:#fff}\n.cw-run-loadout button:disabled{opacity:.55}\n.cw-run-summary{padding:8px;background:#203627;color:#d1ffdc}\n.cw-run-relics label{display:flex;align-items:center;gap:10px;min-height:48px;margin:4px 0;padding:6px;background:#1a2a38;cursor:pointer}\n.cw-run-relics input{width:22px;height:22px;flex:none;accent-color:#7fd4ff}\n.cw-run-actions{display:flex;gap:8px;position:sticky;bottom:-16px;padding:10px 0;background:#101c26}.cw-run-actions button{flex:1}\n.cw-run-actions [data-run-confirm]{background:#8aefae;color:#07140c}.cw-run-status{min-height:2.8em;color:#ffdb9a}\n.cw-run-unmapped{color:#c1cbd5;font-size:12px}.cw-run-unmapped:empty{display:none}\n.cw-run-heart select{width:100%;min-height:48px;background:#1a2a38;color:#e8f5ff;border:1px solid #8aefae;padding:8px;font:inherit}.cw-run-heart-description{color:#d1ffdc}\n.cw-run-character{position:relative;float:right;width:72px;height:96px;margin:0 0 8px 8px}.cw-run-character canvas{width:64px;height:96px;image-rendering:pixelated}.cw-run-insert-heart{position:absolute;left:26px;top:40px;color:#ff6788;text-shadow:0 1px #240a16;font-size:15px;animation:cw-heart-insert .35s ease-out}@keyframes cw-heart-insert{from{transform:translateY(-20px) scale(1.5);opacity:0}to{transform:none;opacity:1}}@media(prefers-reduced-motion:reduce){.cw-run-insert-heart{animation:none}}\n@media(max-width:359px){.cw-rts .cw-rts-building-context{max-height:58px;overflow-y:auto}.cw-rts .cw-rts-pdetail{font-size:12px}}\n@media(max-height:500px) and (orientation:landscape){.cw-rts .cw-rts-quickbar button{flex:none;min-height:44px;padding:6px 10px}.cw-rts .cw-rts-quickbar{display:flex;overflow-x:auto;max-width:340px}}\n\r\n","modules/coach-world-rts/hud.mjs":"// W5 StarCraft-style phone HUD (Wave 1 minimal + Wave 2 full: unit/building panels, build queue, build menu page, alerts). mountHud(panel,host,{getBase,serverNow,command,sceneMode,alive,ownerId})\r\n//   -> {update(snapshot),dispose(),toast(msg)}. Legacy R47 DOM is kept: the HUD only proxies .click() on the existing controls,\r\n//   so command() stays the single mutation path. Throws when the flag is off so the seam falls back to the legacy layout.\r\nimport {BUILDINGS,BUILDING_BY_ID,DEFENSE_KINDS,FACTION_BY_ID,FACTIONS,MAX_LEVEL} from '../coach-world/catalog.mjs';\r\nimport {UNIT_BY_ID,unitRoster} from '../coach-world/expedition-catalog.mjs';\r\nimport {facingFor} from './clips.mjs';\r\nimport {occupancy} from '../coach-world/navigation.mjs';\r\nimport {getSpriteCanvas} from '../coach-world/pixel-art.mjs';\r\nimport {rtsFlag} from './flag.mjs';\r\nimport {constructionQuote,bestTrack} from '../coach-world/state.mjs';\r\nimport {trainingQuote,baseSystems} from '../coach-world/base-systems.mjs';\r\nimport {productionQueue,supplyStatus,trainingAvailability} from '../coach-world/production.mjs';\r\nimport {economyState,harvestReadout} from '../coach-world/rts-economy.mjs';\r\nconst TRIP_LABEL={outbound:'out',working:'mining',returning:'back',depositing:'deposit'};\r\nimport {mountFacilityControls} from './facility-controls.mjs';\r\nimport {mountSelectionPanel} from './selection-panel.mjs';\r\nimport {mountUnlocksPanel} from './unlocks-panel.mjs';\r\nimport {unitLevel} from './unit-levels.mjs';\r\nimport {movable,siteCheck} from './drag-move.mjs';\r\nimport {createMinimap, ZOOMS} from './minimap.mjs';\r\nimport {renderKnobs} from './ar-scale.mjs';\r\nimport {mountTechTerminal} from './terminal.mjs';\r\nimport {techNode} from './tech-tree.mjs';\r\nimport {UNIT_BY_ID as WORLD_UNITS} from '../coach-world/unit-catalog.mjs';\r\nimport {harvestResourceLabel as resourceLabel,resourceDisplayText as resourceText} from '../coach-world/resource-labels.mjs';\r\nimport {drawUnitPortrait} from './unit-portrait.mjs';\nimport {mountRunLoadoutPanel} from './run-loadout-panel.mjs';\n\r\nconst FAMILY={'occupied-worker':'occupied','seeded-warp':'warp','consumed-seed':'seed'};\r\nconst FLIP_KEY='cw-rts-flip';\r\nconst MENU_KEY='cw-rts-menu';\r\n// Eco leads with the free Landing Zone and the halls (first hall is free); Defend lists every defense row (newer kinds were missing from all tabs).\r\nconst TRAY_TABS=[['eco','Eco',d=>['hq','landing','care','habitat','hangar'].includes(d.id)||d.kind==='hall'],['train','Train',d=>d.kind==='production'],['defend','Defend',d=>[...DEFENSE_KINDS,'silo','yard'].includes(d.kind)],['special','Special',d=>d.kind==='observatory'||d.kind==='expedition']];\r\nconst safeQuote=(b,d,track)=>{try{return constructionQuote(b,d.id,track??d.track);}catch{return null;}};\r\nconst ICON=n=>new URL(`./ui/icons/${n}.png`,import.meta.url).href;\r\nlet active=null;\r\n// Tile icon = the building's own near-field 3D art at the level the card would build/upgrade to (building-icon.mjs); old per-kind sprite until three is loaded.\r\nlet iconLib=null,iconLoading=null,iconReady=()=>{};\r\nfunction iconCanvas(d,lvl,fac){\r\n const c=mk('canvas');c.width=c.height=48;const g=c.getContext('2d'),f=FACTION_BY_ID[d.track]||fac,next=Math.min(MAX_LEVEL,(lvl||0)+1);\r\n try{const px=iconLib&&iconLib.icon(d.id,next,f.id);if(px){g.putImageData(new ImageData(px.data,px.width,px.height),0,0);return c;}}catch{/* fall back to sprite */}\r\n iconLoading||=Promise.all([import('/vendor/three/three.module.js'),import('./building-icon.mjs')]).then(([T,m])=>{iconLib={icon:(id,l,fid)=>m.buildingIcon(T,id,l,fid)};iconReady();}).catch(()=>{});\r\n try{g.imageSmoothingEnabled=false;g.drawImage(getSpriteCanvas(f.id,d.kind,Math.max(0,(lvl||1)-1)),0,0,48,48);}catch{/* no sprite */}\r\n return c;\r\n}\r\n// W6 hook: levelup.mjs calls hud.toast(). No-op when no HUD is mounted.\r\nexport function toast(message,opts){return active?active.toast(message,opts):false;}\r\n// W6 hook: flash a building's minimap cell. No-op when no HUD is mounted.\r\nexport function ping(buildingId){return active?active.ping(buildingId):false;}\r\nconst fmtLeft=ms=>{const s=Math.max(0,Math.ceil(ms/1000));return s>=3600?`${Math.floor(s/3600)}h${Math.floor(s%3600/60)}m`:s>=60?`${Math.ceil(s/60)}m`:`${s}s`;};\r\nlet sheetP=null; // unit atlas for the worker portrait; null result = fall back to squares\r\nconst loadAtlas=()=>sheetP||=import('./runtime.mjs').then(m=>m.loadSheet()).catch(()=>null);\r\n\r\n// Dummy-safe: runtime.mjs may be absent or a stub. Resolves to a getUnitDots() function or null.\r\n// R74: nearest Gym Hub for the minimap edge marker (nameless data; null when the bake has no hubs or the fetch fails)\r\nasync function loadHubMark(base){try{const m=await import('./hubs-kit.mjs'),h=base?.home,from=h&&Number.isFinite(h.latitude)?{lat:h.latitude,lng:h.longitude}:{lat:36.1147,lng:-115.1728};return m.nearestHub(await m.loadHubs(),from);}catch{return null;}}\r\nasync function loadDots(){try{const m=await import('./runtime.mjs');return typeof m.getUnitDots==='function'?m.getUnitDots:null;}catch{return null;}}\r\n\r\nconst mk=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n;};\r\nconst store=()=>{try{return globalThis.localStorage;}catch{return null;}};\r\n\r\nexport function mountHud(panel,host,ctx={}){\r\n if(!rtsFlag())throw new Error('rts flag off');\r\n const {getBase=()=>null,alive=()=>true,serverNow=Date.now}=ctx;\r\n const listeners=[],timers=[],moved=[];let observer=null,disposed=false,getDots=null,hubMark=null,toastTimer=0,lastSel='',lastFuelLow=false,lowFuel=false,sheetOpen=false,menuOpen=false,sheetAtlas=null,pingId='',pingUntil=0,longPressed=false,lastKind='',levelup=null;\r\n const on=(t,type,fn,o)=>{t.addEventListener(type,fn,o);listeners.push(()=>t.removeEventListener(type,fn,o));};\r\n const q=s=>host.querySelector(s);\r\n const btnByText=re=>[...host.querySelectorAll('[data-sheet] button,[data-expedition] button')].find(b=>re.test(b.textContent.trim()));\r\n\r\n // ---- legacy proxies: card button -> existing control ------------------------------------------------\r\n const BUILD_RE=/^(Build this site|Upgrade to level)/,CANCEL_RE=/^Cancel construction/;\r\n const ACT={\r\n upgrade:{icon:'up',label:'Upgrade',find:()=>btnByText(BUILD_RE),why:()=>{const b=base(),d=selection().def,s=b?.buildings?.[d?.id];return s?.job?'Construction in progress':(b?.fuel||0)<5?'Not enough Glycogen':(s?.level??1)>=MAX_LEVEL?'Maximum level reached':btnByText(BUILD_RE)?.title||'Requirements not met';}},\r\n cancel:{icon:'x',label:'Cancel',find:()=>btnByText(CANCEL_RE),why:()=>'Nothing to cancel'},\r\n  build:{icon:'up',label:'Build',find:()=>{const d=selection().def,b=base(),track=d?.track||bestTrack(b,d?.id),q0=safeQuote(b,d,track),s=b?.buildings?.[d?.id];return {disabled:!q0?.allowed||!!s?.job,click:()=>{if(!q0?.allowed){showToast(resourceText(q0?.reason)||'Site is not ready to build',{kind:'error'});return;}startPlace(d.id,null);}};},why:()=> 'Site is not ready to build'},\r\n research:{icon:'list',label:'Manage',find:()=>({disabled:false,click:()=>facility.open(selection().def?.id||'hq')})},\r\n train:{icon:'train',label:'Train',find:()=>{const d=selection().def,b=base();if(d?.kind!=='production')return null;const unit=unitRoster(b).find(u=>u.trainable&&u.buildingId===d.id&&u.unlocked);if(!unit)return {disabled:true,click:()=>showToast('Upgrade this production facility first',{kind:'error'})};const q=trainingAvailability(b,unit.id);return {disabled:!q.allowed,click:()=>{if(!q.allowed){showToast(resourceText(q.reason),{kind:'error'});return;}const p=ctx.command?.({type:'train-unit',unitId:unit.id});if(p?.then)p.then(r=>{if(r?.ok===false)showToast(failMsg(r),{kind:'error'});});}};},why:()=> 'Train a companion'},\r\n  greet:{icon:'wave',label:'Greet',find:()=>q('[data-greet]'),why:()=>'Your crew has been greeted today'},\r\n  feed:{icon:'fuel',label:'Nutrition',find:()=>q('[data-feed]'),why:()=>(base()?.fuel||0)>=100?'Glycogen is full':'Log food to earn a Meal pack'},\r\n  calm:{icon:'breath',label:'Composure',find:()=>q('[data-meditate]'),why:()=>'Complete meditation to earn Composure'},\r\n  rest:{icon:'zz',label:'Rest',find:()=>q('[data-rest]')},\r\n  troops:{icon:'group',label:'Troops',find:()=>q('[data-select-troops]'),why:()=>'Troops not ready'},\r\n  move:{icon:'move',label:'Move',sel:true,find:()=>({disabled:false,click:()=>{ctx.sceneMode?.setOrderMode?.('move');showToast('Tap empty ground to move selected troops',{ms:3000});}})},\r\n  patrol:{icon:'move',label:'Patrol',sel:true,find:()=>({disabled:false,click:()=>{ctx.sceneMode?.setOrderMode?.('patrol');showToast('Tap both ends of the patrol route');}})},\r\n  holdposition:{icon:'group',label:'Hold',sel:true,find:()=>({disabled:false,click:()=>ctx.sceneMode?.issueOrder?.('holdposition')})},\r\n  buildingMove:{icon:'move',label:'Move',find:()=>({disabled:false,click:()=>moveBuilding()})},\r\n  stop:{icon:'x',label:'Stop',sel:true,find:()=>({disabled:false,click:()=>ctx.sceneMode?.issueOrder?.('stop')})},\r\n  rally:{icon:'flag',label:'Rally',find:()=>q('[data-rally-hq]'),why:()=>'Select troops first'},\r\n  hq:{icon:'door',label:'HQ',find:()=>q('[data-enter-war-room]')},\r\n  sheet:{icon:'list',label:'Sheet',find:()=>null,open:true},\r\n  launch:{icon:'ship',label:'Launch',find:()=>null,open:true},\r\n  ships:{icon:'ship',label:'Ships',find:()=>({disabled:false,click:()=>{const def=BUILDINGS.find(d=>d.kind==='hangar'),index=BUILDINGS.indexOf(def);if(index>=0)host.querySelectorAll('[data-building-list] button')[index]?.click();openFacility();}})},\r\n  systems:{icon:'list',label:'Systems',find:()=>null,open:true},\r\n };\r\n\r\n // ---- DOM ---------------------------------------------------------------------------------------------\r\n for(const f of ['hud.css','hud-collapse.css','selection-panel.css','terminal.css']){const href=new URL(`./${f}`,import.meta.url).href;if(![...document.querySelectorAll('link[data-cw-rts-hud]')].some(l=>l.href===href)){const l=mk('link');l.rel='stylesheet';l.href=href;l.dataset.cwRtsHud='';document.head.append(l);}}\r\n const root=mk('div','cw-rts-hud');root.dataset.hud='';\r\n    const top=mk('div','cw-rts-top');top.setAttribute('aria-label','Station vitals');\r\n    const ctr=mk('button','cw-rts-flip-btn cw-rts-center','◎');ctr.type='button';ctr.setAttribute('aria-label','Center the view');ctr.dataset.rtsCenter='';top.append(ctr);\r\n    const flip=mk('button','cw-rts-flip-btn','⇄');flip.type='button';flip.setAttribute('aria-label','Swap console to the other hand');flip.dataset.rtsFlip='';top.append(flip);\r\n    const vit={};\r\n    for(const [k,l] of [['fuel','GLYCOGEN'],['calm','COMPOSURE'],['charge','READINESS']]){\r\n        const c=mk('div','cw-rts-vital');\r\n        c.setAttribute('role','group');c.setAttribute('aria-label',l);\r\n        const i=mk('i','cw-rts-glyph cw-rts-glyph-'+k,'');\r\n        c.append(i);\r\n        vit[k]=mk('b','cw-rts-num','--');\r\n        c.append(vit[k]);\r\n        top.append(c);\r\n    }\r\n const mini=mk('canvas','cw-rts-mini');mini.width=mini.height=96;mini.setAttribute('role','button');mini.setAttribute('aria-label','Minimap. Tap to zoom.');mini.tabIndex=0;\r\n const miniWrap=mk('div','cw-rts-mini-wrap');miniWrap.append(mini);\r\n const portrait=mk('div','cw-rts-portrait');const pc=mk('canvas');pc.width=pc.height=64;const pname=mk('span','cw-rts-pname');const pdot=mk('span','cw-rts-pdetail');\r\n const hp=mk('div','cw-rts-hp');hp.setAttribute('role','img');for(let i=0;i<10;i++)hp.append(mk('i'));\r\n const chips=mk('div','cw-rts-chips');const queue=mk('div','cw-rts-queue');queue.setAttribute('role','group');queue.setAttribute('aria-label','Build queue');\r\n const pbtn=mk('button','cw-rts-pbtn');pbtn.type='button';pbtn.setAttribute('aria-label','Open build menu');pbtn.append(pc);\r\n const pmove=mk('button','cw-rts-pmove','MOVE');pmove.type='button';pmove.dataset.pmove='';pmove.hidden=true;pmove.setAttribute('aria-label','Move this building');\r\n portrait.append(pbtn,pname,pdot,hp,chips,queue,pmove);\r\n const card=mk('div','cw-rts-card');card.setAttribute('role','group');card.setAttribute('aria-label','Commands');\r\n const menu=mk('div','cw-rts-card cw-rts-bmenu');menu.setAttribute('role','group');menu.setAttribute('aria-label','Build menu');menu.hidden=true;\r\n const handle=mk('button','cw-rts-handle');handle.type='button';handle.dataset.rtsHandle='';\r\n const miniRow=mk('div','cw-rts-mini-row');const miniBtns={};\r\n for(const [id,label,icon] of [['build','Build','up'],['attack','Attack','flag'],['sheet','Sheet','list']]){const b=mk('button','cw-rts-mini-btn');b.type='button';b.dataset.mini=id;b.setAttribute('aria-label',label);const im=mk('img');im.src=ICON(icon);im.alt='';im.width=im.height=20;b.append(im);miniRow.append(b);miniBtns[id]=b;}\r\n const tray=mk('div','cw-rts-tray');tray.setAttribute('role','region');tray.setAttribute('aria-label','Build tray');const trayTabs=mk('div','cw-rts-tray-tabs');trayTabs.setAttribute('role','tablist');const trayRow=mk('div','cw-rts-tray-row');tray.append(trayTabs,trayRow);\r\n const place=mk('div','cw-rts-place');place.dataset.place='';place.hidden=true;place.setAttribute('role','group');place.setAttribute('aria-label','Place building');\r\n const placeMsg=mk('p','cw-rts-place-msg'),placeGo=mk('button','cw-rts-place-go','BUILD HERE'),placeX=mk('button','cw-rts-place-x','Cancel');placeGo.type=placeX.type='button';placeGo.dataset.placeGo='';placeX.dataset.placeCancel='';place.append(placeMsg,placeGo,placeX);\r\n const selBox=mk('div','cw-rts-centre');selBox.hidden=true; // V1-C2 centre selection box (single unit); replaces the portrait slot while shown\r\n const bezel=mk('div','cw-rts-bezel');const inner=mk('div','cw-rts-inner');inner.append(miniWrap,portrait,selBox,card,menu);bezel.append(handle,miniRow,inner);\r\n const toastEl=mk('p','cw-rts-toast');toastEl.setAttribute('role','status');toastEl.setAttribute('aria-live','polite');\r\n const consoleEl=mk('div','cw-rts-console');consoleEl.dataset.family='occupied';consoleEl.append(tray,place,toastEl,bezel); // toast after tray/place so errors are never painted under them\r\n // Faction chips (polish G): the race picker on the main screen; a tap proxies the legacy select + button so command() stays the single mutation path.\r\n const facs=mk('div','cw-rts-factions');facs.setAttribute('role','group');facs.setAttribute('aria-label','Civilization');const facBtns=new Map();\r\n for(const f of FACTIONS){const b=mk('button','cw-rts-fac');b.type='button';b.dataset.fac=f.id;b.style.setProperty('--fc',f.color);b.setAttribute('aria-label',f.label+' / '+f.name);b.setAttribute('aria-pressed','false');\r\n  b.append(mk('i'),mk('span','',f.label.replace(/[^A-Za-z]/g,'').slice(0,2).toUpperCase()));\r\n  b.onclick=()=>{const sel=q('[data-faction]'),go=q('[data-change-faction]');if(!sel||!go||b.getAttribute('aria-pressed')==='true')return;sel.value=f.id;go.click();};facs.append(b);facBtns.set(f.id,b);}\r\n root.append(top,facs,consoleEl);\r\n\r\n // Base sheet: lives inside host so the legacy host.querySelector(...) calls still resolve. Everything except the canvas moves in.\r\n const sheet=mk('div','cw-rts-sheet');sheet.dataset.rtsSheet='';sheet.setAttribute('role','region');sheet.setAttribute('aria-label','Settings');\r\n const sheetHead=mk('div','cw-rts-sheet-head');const sheetTitle=mk('h3','','COMMAND TERMINAL');const sheetClose=mk('button','cw-rts-sheet-close','Close');sheetClose.type='button';sheetClose.dataset.sheetClose='';sheetClose.setAttribute('aria-label','Close settings');sheetHead.append(sheetTitle,sheetClose);\r\n const sheetBody=mk('div','cw-rts-sheet-body');sheet.append(sheetHead,sheetBody);\r\n const main=host.querySelector('.cw-base-main'),side=host.querySelector('.cw-base-side'),wrap=host.querySelector('.cw-scene-wrap');\r\n const mv=n=>{if(!n||!n.parentNode)return;moved.push([n,n.parentNode,n.nextSibling]);sheetBody.append(n);};\r\n if(main)for(const n of [...main.children])if(n!==wrap)mv(n);\r\n mv(side);\r\n const knobs=mk('div','cw-rts-knobs');knobs.dataset.arKnobs='';sheetBody.append(knobs); // lane C renders the AR sliders here\r\n const startBtn=mk('button','cw-rts-start-over','Start over');startBtn.type='button';startBtn.dataset.startOver='';startBtn.setAttribute('aria-pressed','false');sheetBody.append(startBtn);\r\n host.append(sheet);panel.append(root);panel.classList.add('cw-rts');\r\n const facility=mountFacilityControls(root,{getBase:()=>base(),getBuildingId:()=>selection().def?.id||'hq',command:ctx.command,serverNow});\n const quickbar=mk('nav','cw-rts-quickbar');quickbar.setAttribute('aria-label','Base and run controls');\n const quickButtons={};\n for(const [id,label] of [['build','Build'],['army','Army'],['research','Research'],['harvest','Harvest'],['energy','Energy'],['loadout','Loadout'],['run','Start / Resume Run']]){const b=mk('button','',label);b.type='button';b.dataset.rtsQuick=id;quickbar.append(b);quickButtons[id]=b;}\n consoleEl.prepend(quickbar);\n const runPanel=mountRunLoadoutPanel({getBase,getAccount:ctx.getAccount,createRunClient:ctx.createRunClient,navigate:ctx.navigate,mountHeartSelection:ctx.mountRunHeartSelection,onStatus:active=>{quickButtons.run.textContent=active?'Resume Run':'Start Run';}});\n on(quickButtons.build,'click',()=>{trayTab='eco';setTray(!trayOpen);});\n on(quickButtons.army,'click',()=>{const d=selection().def,b=base(),target=d?.kind==='production'&&b?.buildings?.[d.id]?.level>0?d:BUILDINGS.find(x=>x.kind==='production'&&x.track===b?.faction&&b?.buildings?.[x.id]?.level>0);if(target)facility.open(target.id);else{trayTab='train';setTray(true);showToast('Build a faction hall, then its production facility.');}});\n on(quickButtons.research,'click',()=>facility.open(selection().def?.id||'hq'));\n on(quickButtons.harvest,'click',()=>facility.open('hq'));\n on(quickButtons.energy,'click',()=>{const ro=harvestReadout(base());showToast(`Operating energy ${fmtLeft(ro.bankOperatingMs)}. Accepted run movement powers worker trips; deposits earn materials.`,{ms:6500});});\n for(const id of ['loadout','run'])on(quickButtons[id],'click',()=>void runPanel.open());\n ctx.sceneMode?.setResourceHandler?.(value=>facility.open('hq',{faction:typeof value==='string'?value:value?.faction}));\r\n const resources=mk('div','cw-rts-resources');resources.setAttribute('aria-live','polite');root.append(resources);\r\n const controlGroups=mk('div','cw-rts-control-groups');\r\n for(let group=1;group<=3;group++){const b=mk('button','',`G${group}`);b.type='button';b.dataset.controlGroup=String(group);b.setAttribute('aria-label',`Group ${group}: tap to recall, hold to store selected units`);let held=false,holdTimer=0;\r\n  on(b,'pointerdown',()=>{held=false;holdTimer=setTimeout(()=>{held=true;ctx.sceneMode?.assignControlGroup?.(group);showToast(`Group ${group} stored`);},500);});\r\n  for(const event of ['pointerup','pointercancel','pointerleave'])on(b,event,()=>clearTimeout(holdTimer));\r\n  on(b,'click',()=>{if(held){held=false;return;}const result=ctx.sceneMode?.recallControlGroup?.(group);if(result===false){ctx.sceneMode?.assignControlGroup?.(group);showToast(`Group ${group} stored. Hold to replace.`);}});\r\n  timers.push({to:()=>clearTimeout(holdTimer)});controlGroups.append(b);\r\n }root.append(controlGroups);\r\n\r\n // ---- state helpers -----------------------------------------------------------------------------------\r\n const base=()=>getBase()||null;\r\n const faction=()=>FACTION_BY_ID[base()?.faction]||FACTIONS[0];\r\n function selection(){\r\n  const t=q('[data-troop-status]')?.textContent||'',m=/^(\\d+) troops selected/.exec(t);\r\n  if(m&&+m[1]>0)return {kind:'units',count:+m[1]};\r\n  const i=[...host.querySelectorAll('[data-building-list] button')].findIndex(b=>b.classList.contains('is-selected'));\r\n  return {kind:'building',def:BUILDINGS[i<0?0:i]};\r\n }\r\n const small=()=>{const pb=parseFloat(getComputedStyle(consoleEl).paddingBottom)||0;return (innerHeight-pb)<700;};\r\n function layout(){const c=small();consoleEl.classList.toggle('is-compact',c);root.classList.toggle('is-compact',c);mini.style.setProperty('--sz',(c?72:96)+'px');panel.style.setProperty('--cw-console-h',consoleEl.offsetHeight+'px');panel.style.setProperty('--cw-console-w',consoleEl.offsetWidth+'px');} // map rect = between tabs and console\r\n\r\n function cardIds(sel){\r\n  const units=sel.kind==='units';\r\n  const sceneUnits=ctx.sceneMode?.getSelectedUnits?.()||[],allAir=sceneUnits.length>0&&sceneUnits.every(u=>u.air);\r\n  if((units||sceneUnits.length)&&!allAir)return ['move','patrol','holdposition','stop','troops','rally','sheet','hq'];\r\n  if((units||sceneUnits.length)&&allAir)return ['move','stop','ships','sheet'];\r\n  if(!units){const d=sel.def,s=base()?.buildings?.[d?.id],facility=d?.kind==='hq'?'hq':d?.kind==='production'?'train':d?.kind==='expedition'?'launch':d?.kind==='hangar'?'ships':'sheet';return [s?.job?'cancel':s?.level===0?'build':'upgrade','buildingMove','research',facility,'greet','feed','calm','rest','troops'];}\r\n  return ['rally','move','feed','greet','calm','rest','troops','upgrade','hq'];\r\n }\r\n const btns=new Map();\r\n function buildingContext(d,b){\n  const s=b?.buildings?.[d?.id];let text;\r\n  if(s?.job)text=`${d.label} · L${s.level} → L${s.job.targetLevel} · construction running`;\r\n  else{const track=d?.track||bestTrack(b,d?.id),q0=safeQuote(b,d,track),need=(q0?.prerequisites||[]).filter(p=>!p.met).map(p=>`${BUILDING_BY_ID[p.buildingId]?.label||p.buildingId} L${p.level}`).join(', ');text=s?.level>=MAX_LEVEL?`${d.label} · MAX L${MAX_LEVEL}`:`${d.label} · ${s?.level===0?'Build':'Next level'} ${q0?.cost??'--'} ${FACTION_BY_ID[q0?.track]?.label||'matching'} Protein · ${q0?.fuelCost??5} Glycogen · ${Math.ceil((q0?.durationMs||0)/60000)} min${need?` · Need ${need}`:q0?.reason?` · ${resourceText(q0.reason)}`:' · Ready'}`;}\r\n  if(d.kind==='production'){const rows=unitRoster(b).filter(u=>u.trainable&&u.faction===d.track),pending=rows.find(u=>u.training),unit=rows.find(u=>u.unlocked&&!u.owned),next=rows.find(u=>!u.unlocked);text+=pending?` · TRAIN: ${pending.label} in progress`:unit?` · TRAIN: ${unit.label} / ${trainingQuote(b,unit).materialCost} ${FACTION_BY_ID[unit.faction].label} Protein / ${Math.ceil(trainingQuote(b,unit).trainingMs/60000)} min`:next?` · TRAIN: needs production L${next.requiredLevel}`:' · TRAIN: all companions owned';}\r\n  if(b?.blueprintEntitlements?.owned?.includes(`blueprint:${d.id}`))text+=' · Blueprint recovered';\n  return text;\n }\r\n function renderCard(sel){\r\n  const ids=cardIds(sel),sig=ids.join();\r\n  const oldPace=portrait.querySelector('[data-hq-trip-speed]');\r\n  if(sel.kind==='building'&&sel.def?.kind==='hq'){\r\n   const b=base(),level=b?.buildings?.hq?.level??0,now=baseSystems(b).movementSpeedPct;\r\n   const next=level<MAX_LEVEL?baseSystems({...b,buildings:{...b.buildings,hq:{...b.buildings.hq,level:level+1}}}).movementSpeedPct:now;\r\n   const pace=oldPace||mk('small','cw-rts-building-context');pace.dataset.hqTripSpeed='';\r\n   pace.textContent=`Trip speed +${now}%${next>now?` · next level +${next}%, after current trips return`:''}`;\r\n   if(!pace.isConnected)portrait.insertBefore(pace,hp);\r\n  }else oldPace?.remove();\r\n  const oldContext=portrait.querySelector('[data-building-context]');\r\n  if(sel.kind==='building'){\r\n   const meta=oldContext||mk('small','cw-rts-building-context');meta.dataset.buildingContext='';meta.textContent=buildingContext(sel.def,base());meta.title=meta.textContent;meta.setAttribute('aria-label',meta.textContent);\r\n   if(!meta.isConnected)portrait.insertBefore(meta,hp);\r\n  }else oldContext?.remove();\r\n  if(card.dataset.sig!==sig){\r\n   card.replaceChildren();btns.clear();card.dataset.sig=sig;\r\n   for(const id of ids){const a=ACT[id],b=mk('button','cw-rts-cmd');b.type='button';b.dataset.cmd=id;if(a.sel)b.dataset.selCmd=id;\r\n    const img=mk('img');img.src=ICON(a.icon);img.alt='';img.width=img.height=16;const label=a.label;b.append(img,mk('span','',label));b.setAttribute('aria-label',label);\r\n    b.onclick=()=>{if(a.open){setSheet(true);if(id==='research')terminal.openTree();else if(id!=='sheet')openFacility();return;}const t=a.find();if(t&&!t.disabled)t.click();else if(t?.disabled&&t instanceof Element)showToast(a.why?.()||'Not available',{kind:'error'});else if(t?.click)t.click();else showToast(a.why?.()||'Not available',{kind:'error'});};btns.set(id,b);card.append(b);}\r\n  }\r\n  for(const [id,b] of btns){const a=ACT[id],t=a.open?true:a.find(),dis=!t||t.disabled===true;b.setAttribute('aria-disabled',String(dis)); // stays tappable: the tap explains why in the alert line\r\n   if(id==='train'){const label=a.label;b.querySelector('span').textContent=label;b.setAttribute('aria-label',label);}\r\n   b.classList.toggle('is-primary',id==='feed'&&lowFuel);\r\n   if(id==='rest')b.setAttribute('aria-pressed',String(!!base()?.rest));\r\n   if(id==='troops')b.setAttribute('aria-pressed',String(t?.getAttribute?.('aria-pressed')==='true'));\r\n   if(id==='upgrade'&&t)b.querySelector('span').textContent=/^Build/.test(t.textContent.trim())?'Build':'Upgrade';if(id==='build')b.setAttribute('aria-disabled',String(!t||t.disabled));}\r\n }\r\n function setHp(frac,label){const n=Math.ceil(Math.max(0,Math.min(1,frac))*10),col=frac>.66?'g':frac>.33?'y':'r';[...hp.children].forEach((c,i)=>{c.className=i<n?col:'';});hp.setAttribute('aria-label',label);}\r\n function drawWorker(g,sh,color){ // idle S frame of the unit atlas, team-dyed, integer nearest-neighbour scale\r\n  const A=sh.atlas,sprite=sh.dye(color||faction().color),clip=A.clips?.idle;if(!sprite||!clip)return false;\r\n  const {stored,mirror}=facingFor(4,A.dirs),[rx,ry,rw,rh]=clip.rects[stored][0],k=Math.max(1,Math.min(2,Math.floor(Math.min(60/rw,60/rh))));\r\n  g.save();g.imageSmoothingEnabled=false;g.translate(32,0);if(mirror)g.scale(-1,1);g.drawImage(sprite,rx,ry,rw,rh,-rw*k/2,62-rh*k,rw*k,rh*k);g.restore();return true;}\r\n function renderPortrait(sel){\r\n  const b=base(),key=sel.kind==='units'?`u${sel.count}:${sheetAtlas?1:0}:${faction().id}`:`b${sel.def?.id}:${b?.buildings?.[sel.def?.id]?.level}`,ctx2=pc.getContext('2d');\r\n  pname.textContent=sel.kind==='units'?'TROOPS':(sel.def?.label||'').toUpperCase();\r\n  const lvl=b?.buildings?.[sel.def?.id]?.level;pdot.textContent=sel.kind==='units'?`${sel.count} selected`:lvl===0?'Unbuilt':`Level ${lvl??1}/${MAX_LEVEL}`;\r\n  // HP: units report 100% until the server sends hp; buildings are permanent so the bar shows level (honest, nothing is destroyed)\r\n  if(sel.kind==='units')setHp(1,'Troop health 100%');else setHp((lvl||0)/MAX_LEVEL,`Level ${lvl||0} of ${MAX_LEVEL}`);\r\n  chips.replaceChildren();if(sel.kind==='units'&&sel.count>1)for(let i=0;i<Math.min(sel.count,6);i++){const c=mk('b','g');c.style.setProperty('--c',faction().color);chips.append(c);}\r\n  {const mvOk=sel.kind==='building'&&movable(base(),sel.def?.id);pmove.hidden=!mvOk;pmove.setAttribute('aria-pressed',String(!!mvOk&&wrap?.__base3d?.armedMove===sel.def.id));}\r\n  if(key===lastSel)return;lastSel=key;\r\n  ctx2.clearRect(0,0,64,64);ctx2.imageSmoothingEnabled=false;\r\n  try{if(sel.kind==='units'){if(!(sheetAtlas&&drawWorker(ctx2,sheetAtlas))){ctx2.fillStyle=faction().color;for(let i=0;i<Math.min(sel.count,6);i++)ctx2.fillRect(8+(i%3)*18,14+Math.floor(i/3)*20,12,12);}}\r\n   else{const def=sel.def,f=FACTION_BY_ID[def.track]||faction();ctx2.drawImage(getSpriteCanvas(f.id,def.kind,Math.max(0,(lvl||1)-1)),0,0,64,64);}}catch{/* sprite unavailable: empty slot */}\r\n  portrait.classList.remove('cw-rts-burst');void portrait.offsetWidth;portrait.classList.add('cw-rts-burst');\r\n }\r\n\r\n // ---- selection panel (R53): works off sceneMode so it is identical in the 3D base and ?rts3d=0 ------------\r\n const factionOf=u=>FACTION_BY_ID[u.faction||u.section]||faction();\r\n const selPanel=mountSelectionPanel(root,{cardHost:selBox,getSelected:()=>ctx.sceneMode?.getSelectedUnits?.()||[],deselect:ids=>ctx.sceneMode?.deselect?.(ids),\r\n  getLevel:u=>{try{return unitLevel(u,base());}catch{return 1;}},\r\n  drawPortrait:(g,u,o={})=>{g.imageSmoothingEnabled=false;const col=factionOf(u).color;if(!(sheetAtlas&&drawWorker(g,sheetAtlas,col))){g.fillStyle=col;g.fillRect(16,16,32,32);}const def=u.definition||WORLD_UNITS[u.unitId||u.id];if(def)return drawUnitPortrait(g,def,{base:base(),scale:1.5,animate:!!o.animate,time:o.time}).catch(()=>{});},});\r\n\r\n // Next unlocks in the Sheet (levels.mjs UNLOCKS + faction switches). Self-refreshing; base read lazily (declared above).\r\n const unlocksPanel=mountUnlocksPanel(sheetBody,{getBase:()=>base()});\r\n const terminal=mountTechTerminal(sheetBody,{getBase:base,getSelected:()=>selection().def?.id||'hq',\r\n  select:id=>{const i=BUILDINGS.findIndex(d=>d.id===id);[...host.querySelectorAll('[data-building-list] button')][i]?.click();},\r\n  operate:()=>{setSheet(true);terminal.openTree();}});\r\n const archive=mk('details','cw-tech-archive'),archiveSummary=mk('summary','','CREW ARCHIVE / 71 VARIANTS'),archiveBody=mk('div');archive.append(archiveSummary,archiveBody);terminal.root.after(archive);\r\n let roster=null,archiveSeq=0;\r\n on(archive,'toggle',()=>{const seq=++archiveSeq;if(!archive.open){roster?.dispose();roster=null;return;}import('./roster-terminal.mjs').then(m=>{if(disposed||!archive.open||seq!==archiveSeq)return;roster?.dispose();roster=m.mountRosterTerminal(archiveBody,{getBase:base});}).catch(()=>showToast('Crew archive could not load',{kind:'error'}));});\r\n const shipArchive=mk('details','cw-tech-archive cw-ship-archive'),shipSummary=mk('summary','','SHIP ARCHIVE / 6 HULLS'),shipArchiveBody=mk('div');shipArchive.append(shipSummary,shipArchiveBody);archive.after(shipArchive);\r\n let shipRoster=null,shipArchiveSeq=0;\r\n on(shipArchive,'toggle',()=>{const seq=++shipArchiveSeq;if(!shipArchive.open){shipRoster?.dispose();shipRoster=null;return;}import('./ship-roster-terminal.mjs').then(m=>{if(disposed||!shipArchive.open||seq!==shipArchiveSeq)return;shipRoster?.dispose();shipRoster=m.mountShipRosterTerminal(shipArchiveBody,{getBase:base});}).catch(()=>showToast('Ship archive could not load',{kind:'error'}));});\r\n function openFacility(){\r\n  const sel=selection(),target=sel.def?.kind==='expedition'?q('[data-expedition]'):q('[data-sheet]');\r\n  if(sel.def?.kind==='expedition'){const f=target?.querySelector('select[aria-label=\"Expedition civilization\"]');if(f&&f.value!==sel.def.track){f.value=sel.def.track;f.dispatchEvent(new Event('change',{bubbles:true}));}}\r\n  setSheet(true);target?.scrollIntoView({block:'start'});target?.querySelector('button:not(:disabled)')?.focus({preventScroll:true});\r\n }\r\n const trayHead=mk('div','cw-rts-tray-head'),trayInfo=mk('span','','ENGINEERING / FABRICATION'),network=mk('button','','TECH NETWORK');network.type='button';network.dataset.techNetwork='';\r\n network.onclick=()=>{setTray(false);setSheet(true);terminal.openTree();};trayHead.append(trayInfo,network);tray.prepend(trayHead);\r\n\r\n // ---- build queue (SC-style slots; progress from server time) --------------------------------------------\r\n function queueItems(sel){\r\n  const b=base(),out=[];if(sel.kind!=='building'||!b)return out;const id=sel.def.id,j=b.buildings?.[id]?.job;\r\n  if(j)out.push({kind:'build',startedAt:j.startedAt,dueAt:j.dueAt,label:`L${b.buildings[id].level}>L${j.targetLevel}`});\r\n  for(const t of productionQueue(b,id).items)out.push({kind:'train',unitId:t.unitId,queueId:t.queueId,startedAt:t.startedAt,dueAt:t.dueAt,label:t.label});\r\n  return out;\r\n }\r\n let qSig='',qItems=[];\r\n function renderQueue(sel){\r\n  qItems=queueItems(sel).slice(0,5);const slots=sel.def?.kind==='production'?5:3,sig=slots+qItems.map(i=>i.queueId||i.kind+i.label).join('|');\r\n  if(sig!==qSig){qSig=sig;queue.replaceChildren();\r\n   for(let i=0;i<slots;i++){const it=qItems[i],s=mk('button','cw-rts-slot');s.type='button';\r\n    if(it){s.append(mk('i'));s.onclick=()=>{if(it.kind==='train'){if(typeof ctx.command!=='function'){showToast('Cannot cancel right now',{kind:'error'});return;}const p=ctx.command({type:it.queueId?'cancel-production':'cancel-training',unitId:it.unitId,...(it.queueId?{queueId:it.queueId}:{})});if(p?.then)p.then(r=>{if(r?.ok===false)showToast(failMsg(r),{kind:'error'});});return;}const t=btnByText(CANCEL_RE);if(t&&!t.disabled)t.click();else showToast('Cannot cancel right now',{kind:'error'});};}\r\n    else{s.disabled=true;s.setAttribute('aria-label','Empty queue slot');}\r\n    queue.append(s);}}\r\n  const now=serverNow();let head='';\r\n  qItems.forEach((it,i)=>{const f=Math.max(0,Math.min(1,(now-it.startedAt)/Math.max(1,it.dueAt-it.startedAt))),s=queue.children[i],left=fmtLeft(it.dueAt-now);\r\n   s.firstChild.style.width=Math.round(f*100)+'%';s.setAttribute('aria-label',`Cancel ${it.kind==='build'?'construction':'training'} ${it.label}, ${Math.round(f*100)}%, ${left} left`);if(i===0)head=`${it.label} ${Math.round(f*100)}% ${left}`;});\r\n  queue.hidden=!qItems.length;if(head)pdot.textContent=head;\r\n }\r\n\r\n // R58 Move button: arm the 3D base, then the next touch picks the building up (ghost follows the finger, release drops it).\r\n function moveBuilding(){\r\n  const s=selection(),b3=wrap?.__base3d,id=s.def?.id;\r\n  if(s.kind!=='building'||!movable(base(),id)){showToast(base()?.buildings?.[id]?.job?'Under construction - wait or cancel first':'Build it first, then you can move it',{kind:'error'});return;}\r\n  if(!b3?.armMove){showToast('Hold the building, then drag it',{ms:2500});return;}\r\n  if(b3.armedMove===id){b3.cancelMove();showToast('Move cancelled',{ms:1500});return;}\r\n  b3.armMove(id);showToast('Drag or tap where it goes. Tap Move again to cancel.',{ms:4000});\r\n }\r\n\r\n // ---- build menu: command-card page (A5 4d) ---------------------------------------------------------------\r\n function setMenu(v){menuOpen=v;card.hidden=v;menu.hidden=!v;if(v)renderMenu();else menu.replaceChildren();}\r\n function renderMenu(){\r\n  const b=base();menu.replaceChildren();\r\n  const back=mk('button','cw-rts-cmd');back.type='button';back.setAttribute('aria-label','Close build menu');back.dataset.menuBack='';const bi=mk('img');bi.src=ICON('x');bi.alt='';bi.width=bi.height=16;back.append(bi,mk('span','','Back'));back.onclick=()=>setMenu(false);menu.append(back);\r\n  BUILDINGS.forEach((d,i)=>{const lvl=b?.buildings?.[d.id]?.level,t=mk('button','cw-rts-cmd cw-rts-tile');t.type='button';t.dataset.build=d.id;t.setAttribute('aria-label',`${d.label}, ${lvl===0?'unbuilt site':`level ${lvl}`}`);\r\n   const c=iconCanvas(d,lvl,faction());\r\n   t.append(c,mk('span','',lvl===0?'SITE':`L${lvl??1}`));\r\n   t.onclick=()=>{[...host.querySelectorAll('[data-building-list] button')][i]?.click();setMenu(false);if(lvl===0&&ctx.command){startPlace(d.id,null);return;}showToast(`${d.label} selected`,{ms:2000});};menu.append(t);});\r\n }\r\n on(pbtn,'click',()=>setMenu(!menuOpen));on(pmove,'click',()=>{moveBuilding();renderPortrait(selection());});\r\n // R58: tap an empty plot/ground in the base -> the build tray opens for that spot (picking an unbuilt site places it there)\r\n ctx.sceneMode?.setEmptyTap?.(spot=>{if(disposed)return;if(placing&&spot){placeAt(spot);return;}setTray(true);pendingSpot=spot;showToast(spot?'Pick a building for this spot':'Pick what to build',{ms:2500});});\r\n\r\n // ---- placement (build fix): pick an unbuilt site -> ghost on the plot + BUILD HERE. Tap the map to move the ghost.\r\n // BUILD HERE = move (when the plot changed) then build, each awaited; a refused command shows the server's reason.\r\n let placing=null,placeBusy=false;\r\n const b3=()=>wrap?.__base3d;\r\n function placeState(){\r\n  const b=base(),d=BUILDING_BY_ID[placing.id],s=b?.buildings?.[placing.id];if(!b||!d||!s||s.level!==0)return null;\r\n  const c=siteCheck(b,d.id,placing.x,placing.y),q=safeQuote(b,d),spotOk=c.ok||c.reason==='',ok=spotOk&&!!q?.allowed&&!s.job;\r\n  return {d,ok,why:!spotOk?c.reason:s.job?'Construction in progress':q&&!q.allowed?q.reason:'',cost:q?.cost??0};\r\n }\r\n function renderPlace(){\r\n  place.hidden=!placing;if(!placing){try{b3()?.preview?.(null);}catch{}liftToast();return;}\r\n  const p=placeState();if(!p){stopPlace();return;}\r\n  placeMsg.textContent=p.ok?`${p.d.label}: ${p.cost?p.cost+' Protein':'free'}. Tap the map to move it.`:resourceText(p.why);placeMsg.classList.toggle('is-error',!p.ok);\r\n  placeGo.disabled=placeBusy;placeGo.setAttribute('aria-disabled',String(!p.ok));placeGo.textContent=placeBusy?'BUILDING...':'BUILD HERE';\r\n  try{b3()?.preview?.({id:p.d.id,x:placing.x,y:placing.y,w:p.d.w,h:p.d.h,ok:p.ok,reason:p.ok?'':p.why||'x'});}catch{}\r\n  liftToast();\r\n }\r\n function placeAt(spot){const d=BUILDING_BY_ID[placing.id];placing.x=Math.max(0,Math.min(24-d.w,Math.round(spot.x-d.w/2)));placing.y=Math.max(0,Math.min(24-d.h,Math.round(spot.y-d.h/2)));renderPlace();}\r\n function startPlace(id,spot){\r\n  const s=base()?.buildings?.[id];if(!s)return;try{ctx.sceneMode?.clearSelection?.();}catch{}\r\n  placing={id,x:s.x,y:s.y};if(spot)placeAt(spot);else renderPlace();\r\n }\r\n function stopPlace(){placing=null;placeBusy=false;renderPlace();}\r\n const failMsg=r=>resourceText(r?.busy?'Still saving - tap BUILD HERE again.':r?.error?.message||'The server refused that build.');\r\n async function confirmPlace(){\r\n  if(!placing||placeBusy)return;const p=placeState();if(!p){stopPlace();return;}if(!p.ok){showToast(p.why||'Cannot build here',{kind:'error'});return;}\r\n  const {id,x,y}=placing,s=base().buildings[id];placeBusy=true;renderPlace();\r\n  try{\r\n   if(s.x!==x||s.y!==y){const r=await ctx.command({type:'move',buildingId:id,x,y});if(!r?.ok){showToast(failMsg(r),{kind:'error'});return;}}\r\n   const r=await ctx.command({type:'build',buildingId:id,track:bestTrack(base(),id)});if(!r?.ok){showToast(failMsg(r),{kind:'error'});return;}\r\n   placing=null;showToast(`${p.d.label} is going up!`,{ms:3000});\r\n  }catch(e){showToast(e?.message||'Build failed.',{kind:'error'});}\r\n  finally{placeBusy=false;renderPlace();}\r\n }\r\n on(placeGo,'click',()=>{void confirmPlace();});on(placeX,'click',()=>{stopPlace();showToast('Placement cancelled',{ms:1500});});\r\n // toast sits above whichever panel is open over the console (tray / place bar)\r\n function liftToast(){const h=trayOpen?tray.offsetHeight:!place.hidden?place.offsetHeight:0;toastEl.style.marginBottom=h?h+8+'px':'';}\r\n\r\n // ---- collapsible console (CoC-style): handle -> 40 px bar; Build tray slides up above it -------------------\r\n let trayOpen=false,trayTab='eco',traySignature='';\r\n function layoutCollapsed(v){consoleEl.classList.toggle('is-collapsed',v);handle.setAttribute('aria-expanded',String(!v));handle.setAttribute('aria-label',v?'Expand command menu':'Collapse command menu');setTray(false);layout();}\r\n let pendingSpot=null; // tile the player tapped to open the tray; a picked unbuilt site is placed there\r\n function setTray(v){trayOpen=v;if(!v)pendingSpot=null;else{try{ctx.sceneMode?.clearSelection?.();}catch{}} // selected troops would turn the next ground tap into a rally and their panel covers the tray\r\n tray.classList.toggle('is-open',v);miniBtns.build.setAttribute('aria-pressed',String(v));if(v){if(placing)stopPlace();renderTray();}liftToast();}\r\n iconReady=()=>{if(disposed)return;traySignature='';if(trayOpen)renderTray();if(menuOpen)renderMenu();}; // art library finished loading: repaint with real icons\r\n function renderTray(){\r\n  const b=base();\r\n  // Keep a button under the finger while the HUD refreshes. Replacing identical\r\n  // tiles during a touch/scroll detaches the target before pointerup arrives.\r\n  const signature=JSON.stringify([trayTab,b?.faction,b?.materials,b?.buildings,b?.fuel,b?.rest,b?.workers,b?.seedWorkers,b?.blueprintEntitlements,b?.blueprintPolicyVersion,b?.rtsEconomy,b?.rtsTech,b?.productionQueues,!!sheetAtlas]);\n  if(signature===traySignature)return;traySignature=signature;\r\n  const ready=BUILDINGS.filter(d=>b?.buildings?.[d.id]?.level===0&&safeQuote(b,d)?.allowed);\n  trayInfo.textContent=`BUILD · ${ready.length} ready sites · Free Landing Zone, then first Hall. Costs use the matching wallet.`;\n  if(!trayTabs.childElementCount)for(const [id,label] of TRAY_TABS){const t=mk('button','cw-rts-tray-tab');t.type='button';t.dataset.trayTab=id;t.setAttribute('role','tab');t.textContent=label;on(t,'click',()=>{trayTab=id;renderTray();});trayTabs.append(t);}\r\n  for(const t of trayTabs.children)t.setAttribute('aria-selected',String(t.dataset.trayTab===trayTab));\r\n  const pick=TRAY_TABS.find(([id])=>id===trayTab)[2];trayRow.replaceChildren();\r\n  // R77: the next free, unbuilt step (HQ -> Landing Zone) leads the tray; catalog order otherwise.\r\n  const free=([d])=>{const q=b?safeQuote(b,d):null;return q?.allowed&&b.buildings?.[d.id]?.level===0?(q.cost===0?0:1):2;};\n  BUILDINGS.map((d,i)=>[d,i]).filter(([d])=>pick(d)).sort((x,y)=>free(x)-free(y)||x[1]-y[1]).forEach(([d,i])=>{\r\n   const quote=b?safeQuote(b,d):null,cost=quote?.cost??'--',lvl=b?.buildings?.[d.id]?.level,locked=lvl===0&&!!quote&&!quote.allowed;\r\n   const t=mk('button','cw-rts-tray-tile');t.type='button';t.dataset.trayBuild=d.id;t.title=d.label;if(locked)t.classList.add('is-locked');\r\n   t.setAttribute('aria-label',`${d.label}, ${lvl===0?'unbuilt site':`level ${lvl??1}`}, cost ${cost}${locked?', locked':''}`);\r\n   const c=iconCanvas(d,lvl,faction());\r\n   const tech=b?techNode(b,d):null;\r\n   t.append(c,mk('span','cw-rts-tray-name',d.label),mk('b','',lvl>=MAX_LEVEL?'MAX':cost===0?'FREE':`${cost} Protein`),mk('small','cw-rts-tray-state',b?.buildings?.[d.id]?.job?'BUILDING':locked?'LOCKED':lvl>0?`L${lvl} → L${Math.min(MAX_LEVEL,lvl+1)}`:'READY'));\r\n   const paidTrack=quote?.track||d.track||b?.faction;t.append(mk('small','cw-rts-tray-wallet',`${FACTION_BY_ID[paidTrack]?.label||'Matching'} / ${b?.materials?.[paidTrack]??0} Protein held`));\r\n   if(locked){t.title=`${d.label}: ${resourceText(quote.reason)}`;t.append(mk('small','cw-rts-tray-reason',resourceText(quote.reason)));}else if(tech)t.title=`${d.label}: ${tech.role}`;\n   t.onclick=()=>{if(locked){showToast(resourceText(quote.reason)||'Locked',{kind:'error'});return;}[...host.querySelectorAll('[data-building-list] button')][i]?.click();const spot=pendingSpot;setTray(false);\r\n    if(lvl===0&&ctx.command){startPlace(d.id,spot);return;}\r\n    showToast(`${d.label} selected`,{ms:2500});};\r\n   trayRow.append(t);});\r\n }\r\n on(handle,'click',()=>{const v=!consoleEl.classList.contains('is-collapsed');layoutCollapsed(v);try{store()?.setItem(MENU_KEY,v?'collapsed':'open');}catch{/* private mode */}});\r\n on(miniBtns.build,'click',()=>setTray(!trayOpen));\r\n on(miniBtns.attack,'click',()=>{if(ctx.onRaid){ctx.onRaid();return;}const t=ACT.troops.find();if(t&&!t.disabled)t.click();else showToast(ACT.troops.why(),{kind:'error'});});\r\n on(miniBtns.sheet,'click',()=>setSheet(true));\r\n try{layoutCollapsed(store()?.getItem(MENU_KEY)==='collapsed');}catch{/* default expanded */}\r\n\r\n // ---- minimap: CRT map of the real area (minimap.mjs); the old 24x24 plot is the monochrome offline fallback ------\r\n const mono=a=>`rgb(${[3,26,10].map((v,i)=>Math.round(v+([51,255,102][i]-v)*a)).join()})`;\r\n function plot(g,{blink}){\r\n  const b=base();if(!b)return;const t=performance.now();\r\n  try{g.fillStyle=mono(.2);for(const c of occupancy(b)){g.fillRect((c%24)*4,Math.floor(c/24)*4,4,4);}}catch{/* no terrain */}\r\n  for(const d of BUILDINGS){const s=b.buildings?.[d.id];if(!s)continue;const x=(s.x??d.x)*4,y=(s.y??d.y)*4,w=d.w*4,h=d.h*4;\r\n   if(s.level===0){g.strokeStyle=mono(.6);g.lineWidth=1;g.strokeRect(x+.5,y+.5,w-1,h-1);}else{g.fillStyle=s.job&&!blink?mono(.1):mono(d.id==='hq'?1:.6);g.fillRect(x,y,w,h);}\r\n   if(pingId===d.id&&t<pingUntil&&blink){g.fillStyle=mono(1);g.fillRect(x,y,w,h);}}\r\n }\r\nfunction overlay(g,{blink,map,zoom}){\r\n  if(hubMark){const a=hubMark.bearing*Math.PI/180,x=48+Math.sin(a)*41,y=48-Math.cos(a)*41;g.fillStyle=mono(0);g.beginPath();g.moveTo(x,y-6);g.lineTo(x+6,y);g.lineTo(x,y+6);g.lineTo(x-6,y);g.fill();g.fillStyle=mono(1);g.beginPath();g.moveTo(x,y-4);g.lineTo(x+4,y);g.lineTo(x,y+4);g.lineTo(x-4,y);g.fill();}\r\nconst k=map?96/ZOOMS[zoom].span:2.5;\r\nif(getDots){\r\n  let dots=[];\r\n  try{dots=getDots()||[];}catch{}\r\n  for(const u of dots){\r\n    const fillStyle = String(u.id).startsWith('wild:') ? '#ff4040' : '#54ff7c';\r\n    g.fillStyle=fillStyle;\r\n    const px=48+(u.x+0.5-12)*1.6*k;\r\n    const py=48+(u.y+0.5-12)*1.6*k;\r\n    g.fillRect(Math.round(px)-1,Math.round(py)-1,2,2);\r\n  }\r\n}\r\nconst cam = wrap?.__base3d?.cam;\r\nif(cam){\r\n  const hh = cam.dist*Math.tan(Math.PI/6)/Math.sin(cam.pitch);\r\n  const hw = hh*cam.aspect;\r\n  const cx=48+cam.pan[0]*k;\r\n  const cy=48+cam.pan[1]*k;\r\n  // ponytail: ignores yaw; axis-aligned footprint\r\n  let x0 = cx - hw*k;\r\n  let y0 = cy - hh*k;\r\n  let x1 = cx + hw*k;\r\n  let y1 = cy + hh*k;\r\n  if(x0 < 0) x0 = 0;\r\n  if(y0 < 0) y0 = 0;\r\n  if(x1 > 96) x1 = 96;\r\n  if(y1 > 96) y1 = 96;\r\n  let w = x1 - x0;\r\n  let h = y1 - y0;\r\n  if(w < 6){\r\n    let cxcl = (x0 + x1)/2;\r\n    cxcl = Math.max(3, Math.min(93, cxcl));\r\n    x0 = cxcl - 3;\r\n    x1 = cxcl + 3;\r\n    w = 6;\r\n  }\r\n  if(h < 6){\r\n    let cycl = (y0 + y1)/2;\r\n    cycl = Math.max(3, Math.min(93, cycl));\r\n    y0 = cycl - 3;\r\n    y1 = cycl + 3;\r\n    h = 6;\r\n  }\r\n  const x0r = Math.round(x0);\r\n  const y0r = Math.round(y0);\r\n  const wr = Math.round(w);\r\n  const hr = Math.round(h);\r\n  g.strokeStyle='#031a0a';\r\n  g.lineWidth=1;\r\n  g.strokeRect(x0r-0.5, y0r-0.5, wr+1, hr+1);\r\n  g.strokeStyle='#ffffff';\r\n  g.strokeRect(x0r+0.5, y0r+0.5, wr-1, hr-1);\r\n}\r\n}\r\n let miniMap=null;\r\n Promise.all([import('./world-layer.mjs'),import('../coach-world/world-tiles.mjs')]).then(([wl,wt])=>{if(disposed)return;\r\n  let homeP=null;const getHome=()=>homeP||=wl.pickHome(base());\r\n  miniMap=createMinimap(mini,{getHome,tiles:wt.createWorldTiles(),ringCells:wt.ringCells,fallback:plot,overlay});miniMap.draw();}).catch(()=>{});\r\n function drawMini(){\r\n  if(disposed||!alive())return;if(miniMap){miniMap.draw();return;}\r\n  const g=mini.getContext('2d'),blink=Math.floor(performance.now()/250)%2===0;g.fillStyle=mono(0);g.fillRect(0,0,96,96);plot(g,{blink});overlay(g,{blink,map:false});\r\n }\r\n const zoomMap=()=>{if(!miniMap)return;miniMap.setZoom((miniMap.zoom+1)%3);mini.setAttribute('aria-label','Minimap zoom '+(miniMap.zoom+1)+' of 3. Tap to zoom.');};\r\n const center=()=>q('[data-center]')?.click();\r\n let lpTimer=0;on(mini,'pointerdown',()=>{longPressed=false;clearTimeout(lpTimer);lpTimer=setTimeout(()=>{longPressed=true;center();showToast('View centered',{ms:1500});},500);});\r\n for(const ev of ['pointerup','pointercancel','pointerleave'])on(mini,ev,()=>clearTimeout(lpTimer));timers.push({to:()=>clearTimeout(lpTimer)});\r\n on(mini,'click',()=>{if(longPressed){longPressed=false;return;}zoomMap();});on(ctr,'click',center);on(mini,'keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();zoomMap();}});\r\n\r\n // ---- sheet / flip / toast ---------------------------------------------------------------------------\r\n function setSheet(v){sheetOpen=v;panel.classList.toggle('cw-rts-open',v);if(v){renderKnobs(knobs);sheetClose.focus?.({preventScroll:true});}else{sheetBody.scrollTop=0;archive.open=false;archiveSeq++;roster?.dispose();roster=null;shipArchive.open=false;shipArchiveSeq++;shipRoster?.dispose();shipRoster=null;}}\r\n on(sheetClose,'click',()=>setSheet(false));\r\n const setFlip=v=>{root.classList.toggle('is-flipped',v);flip.setAttribute('aria-pressed',String(v));try{store()?.setItem(FLIP_KEY,v?'1':'0');}catch{/* private mode */}};\r\n try{root.classList.toggle('is-flipped',store()?.getItem(FLIP_KEY)==='1');}catch{/* default right hand */}\r\n flip.setAttribute('aria-pressed',String(root.classList.contains('is-flipped')));\r\n on(flip,'click',()=>setFlip(!root.classList.contains('is-flipped')));\r\n // Start over (R57): first tap arms for 5 s, second tap empties THIS owner's base via command(); nothing else is touched.\r\n let armTimer=0;const disarm=()=>{clearTimeout(armTimer);armTimer=0;startBtn.textContent='Start over';startBtn.setAttribute('aria-pressed','false');};\r\n timers.push({to:()=>clearTimeout(armTimer)});\r\n on(startBtn,'click',()=>{\r\n  if(!armTimer){startBtn.textContent='Tap again to empty YOUR base (5 s)';startBtn.setAttribute('aria-pressed','true');armTimer=setTimeout(disarm,5000);return;}\r\n  disarm();\r\n  Promise.resolve().then(()=>{if(!ctx.command)throw Error('Base reset is unavailable.');return ctx.command({type:'start-over',confirm:'START OVER'});}).then(r=>{if(r?.ok===false)showToast(r.busy?'Another save is still running. Try again in a moment.':r.error?.message||'Base reset failed.',{kind:'error'});else showToast('Base reset. Build your first hall - free');},e=>showToast(e?.message||'Base reset failed.',{kind:'error'}));\r\n });\r\n function showToast(message,{ms=4000,kind=''}={}){if(disposed)return false;toastEl.textContent=String(message);toastEl.classList.toggle('is-error',kind==='error');toastEl.classList.add('is-on');clearTimeout(toastTimer);toastTimer=setTimeout(()=>toastEl.classList.remove('is-on'),ms);return true;}\r\n\r\n // ---- refresh -----------------------------------------------------------------------------------------\r\n function refresh(){\r\n  if(disposed||!alive())return;const b=base();layout();facility.update();runPanel.update();\n  if(b){const f=faction();consoleEl.dataset.family=FAMILY[f.policy]||'occupied';consoleEl.style.setProperty('--cw-faction',f.color);\r\n   vit.fuel.textContent=String(Math.round(b.fuel||0));vit.calm.textContent=String(Math.round(b.calm||0));vit.charge.textContent=String(b.launchCharge||0);\r\n   const supply=supplyStatus(b),eco=economyState(b);const ro=harvestReadout(b),trips=Object.entries(ro.workers).filter(([,w])=>w.phase!=='idle').map(([id,w])=>`${id} ${TRIP_LABEL[w.phase]}${w.carried?` ${w.carried.amount}`:''}`);resources.textContent=`Protein ${b.materials?.[b.faction]??0} · Creatine ${eco.gas} · Training capacity ${supply.used}+${supply.reserved}/${supply.cap} · Operating energy ${fmtLeft(ro.bankOperatingMs)}${trips.length?' · '+trips.join(' · '):''}`;\n   for(const [id,bt] of facBtns)bt.setAttribute('aria-pressed',String(id===b.faction));\r\n   const low=(b.fuel||0)<20;lowFuel=low;root.classList.toggle('is-lowfuel',low);if(low&&!lastFuelLow)showToast('Glycogen low: crew is running on fumes.',{kind:'error'});lastFuelLow=low;}\r\n  levelup?.observe(b);\r\n  const sel=selection();if(sel.kind!==lastKind){if(sel.kind==='units')showToast('Tap empty ground to rally',{ms:2500});if(menuOpen)setMenu(false);lastKind=sel.kind;}\r\n  if(placing&&!placeBusy)renderPlace();\r\n  selPanel.update();terminal.update();roster?.update();shipRoster?.update();renderCard(sel);renderPortrait(sel);renderQueue(sel);if(menuOpen)renderMenu();if(trayOpen)renderTray();\r\n }\r\n let queued=false;const schedule=()=>{if(queued||disposed)return;queued=true;queueMicrotask(()=>{queued=false;refresh();});};\r\n observer=new MutationObserver(schedule);observer.observe(sheetBody,{subtree:true,childList:true,characterData:true,attributes:true,attributeFilter:['class','disabled','aria-pressed']});\r\n on(globalThis,'resize',schedule);on(globalThis,'orientationchange',schedule);\r\n if(typeof ResizeObserver!=='undefined'){const ro=new ResizeObserver(schedule);ro.observe(consoleEl);timers.push({ro});}\r\n timers.push(setInterval(()=>{drawMini();if(!disposed&&alive()){renderQueue(selection());selPanel.update();}},250));\r\n loadHubMark(base()).then(h=>{if(!disposed&&h){hubMark=h;mini.setAttribute('aria-label','Minimap. Gym Hub '+Math.round(h.dist/1609)+' miles away. Tap to zoom.');drawMini();}});\r\n loadDots().then(f=>{if(!disposed){getDots=f;drawMini();}});\r\n loadAtlas().then(sh=>{if(!disposed&&sh){sheetAtlas=sh;lastSel='';selPanel.update(true);refresh();}});\r\n refresh();drawMini();\r\n\r\n const api={\r\n  update(){refresh();drawMini();},setSheet,openRunLoadout:()=>runPanel.open(),miniMap:()=>miniMap,\n  toast:showToast,alert:(m,o)=>showToast(m,{kind:'error',...o}),\r\n  ping(id){pingId=id;pingUntil=performance.now()+1500;drawMini();return !disposed;},\r\n  dispose(){\r\n   if(disposed)return;disposed=true;runPanel.dispose();try{ctx.sceneMode?.setEmptyTap?.(null);b3()?.preview?.(null);}catch{}facility.dispose();ctx.sceneMode?.setResourceHandler?.(null);levelup?.dispose();levelup=null;selPanel.dispose();roster?.dispose();roster=null;shipRoster?.dispose();shipRoster=null;terminal.dispose();unlocksPanel.dispose();clearTimeout(toastTimer);for(const t of timers)t.ro?t.ro.disconnect():t.to?t.to():clearInterval(t);observer?.disconnect();for(const off of listeners)off();listeners.length=0;\n   for(const [n,parent,next] of moved.reverse())parent.insertBefore(n,next&&next.parentNode===parent?next:null);\r\n   miniMap?.dispose();sheet.remove();root.remove();panel.classList.remove('cw-rts','cw-rts-open');panel.style.removeProperty('--cw-console-h');if(active===api)active=null;\r\n  },\r\n };\r\n // W6: level-up sequences. Loaded lazily; a failure leaves the HUD fully working.\r\n import('./levelup.mjs').then(m=>{if(disposed)return;levelup=m.createLevelUp({ownerId:ctx.ownerId||(()=>null),hud:api,mount:root,nudge:wrap,cheer:e=>globalThis.__cwRts?.onEvent?.({type:'levelup',unitId:e.key})});refresh();}).catch(()=>{});\r\n active=api;return api;\r\n}\r\n","modules/coach-world-rts/insects.mjs":"// V8 insects (fireflies, gnats, beetles, dust bugs) as one Points draw in a wrap box. Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v8/card.mjs K,\r\n// validated: contract, determinism, swarms, WebGL compile + render, 1 draw); motion block rewritten on review (wrap sign, drift over time, heights, size).\r\n// ES module – no imports, uses the passed THREE namespace (T)\r\nexport function createInsects(T, opts = {}) {\r\n  // ---------- Options ----------\r\n  const {\r\n    counts = { firefly: 60, gnat: 90, beetle: 50, dust: 70 },\r\n    box = 50,\r\n    size = 2.5,\r\n    seed = 1\r\n  } = opts;\r\n\r\n  const total =\r\n    counts.firefly + counts.gnat + counts.beetle + counts.dust;\r\n\r\n  // ---------- Simple deterministic PRNG ----------\r\n  function mulberry32(a) {\r\n    return function () {\r\n      let t = (a += 0x6d2b79f5);\r\n      t = Math.imul(t ^ (t >>> 15), t | 1);\r\n      t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\r\n      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\r\n    };\r\n  }\r\n  const rand = mulberry32(seed);\r\n\r\n  // ---------- Generate geometry ----------\r\n  const positions = new Float32Array(total * 3);\r\n  const aFamily = new Float32Array(total); // 0 firefly, 1 gnat, 2 beetle, 3 dust\r\n  const aSeed   = new Float32Array(total); // 0..1\r\n\r\n  let idx = 0;\r\n\r\n  // Helper to push a point\r\n  function addPoint(x, y, z, family) {\r\n    positions[idx * 3]     = x;\r\n    positions[idx * 3 + 1] = y;\r\n    positions[idx * 3 + 2] = z;\r\n    aFamily[idx]           = family;\r\n    aSeed[idx]             = rand(); // per particle seed\r\n    idx++;\r\n  }\r\n\r\n  // Fireflies – random in box\r\n  for (let i = 0; i < counts.firefly; i++) {\r\n    addPoint(rand() * box, rand() * box, rand() * box, 0);\r\n  }\r\n\r\n  // Gnats – clustered around 6 swarm centres\r\n  const swarmCenters = [];\r\n  for (let s = 0; s < 6; s++) {\r\n    swarmCenters.push([rand() * box, rand() * box, rand() * box]);\r\n  }\r\n  for (let i = 0; i < counts.gnat; i++) {\r\n    const center = swarmCenters[Math.floor(rand() * 6)];\r\n    const offset = [\r\n      (rand() - 0.5) * 3.0, // within ±1.5\r\n      (rand() - 0.5) * 3.0,\r\n      (rand() - 0.5) * 3.0\r\n    ];\r\n    addPoint(\r\n      center[0] + offset[0],\r\n      center[1] + offset[1],\r\n      center[2] + offset[2],\r\n      1\r\n    );\r\n  }\r\n\r\n  // Beetles – random in box\r\n  for (let i = 0; i < counts.beetle; i++) {\r\n    addPoint(rand() * box, rand() * box, rand() * box, 2);\r\n  }\r\n\r\n  // Dust – random in box\r\n  for (let i = 0; i < counts.dust; i++) {\r\n    addPoint(rand() * box, rand() * box, rand() * box, 3);\r\n  }\r\n\r\n  const geometry = new T.BufferGeometry();\r\n  geometry.setAttribute('position', new T.Float32BufferAttribute(positions, 3));\r\n  geometry.setAttribute('aFamily', new T.Float32BufferAttribute(aFamily, 1));\r\n  geometry.setAttribute('aSeed',   new T.Float32BufferAttribute(aSeed,   1));\r\n\r\n  // ---------- Shader ----------\r\n  const vertexShader = `\r\n    uniform float uTime;\r\n    uniform vec3 uCentre;\r\n    uniform vec3 uWind;\r\n    uniform float uBox;\r\n    uniform float uSize;\r\n    uniform float uFade;\r\n    uniform float uNight;\r\n\r\n    attribute float aFamily; // 0 firefly, 1 gnat, 2 beetle, 3 dust\r\n    attribute float aSeed;   // 0..1\r\n\r\n    varying vec4 vColor;\r\n\r\n    void main() {\r\n      // review rewrite (the card wrapped by +uCentre, never moved the drift with time and never applied the per-family height):\r\n      // drift (units/s) runs inside the wrap so the field streams past and refills; height/jitter are added after the wrap.\r\n      float family = aFamily, seed = aSeed;\r\n      vec3 dir = normalize(vec3(cos(seed*30.0), 0.0, sin(seed*30.0)) + 1e-5);\r\n      vec3 drift = family < 0.5 ? dir*0.25 : family < 1.5 ? vec3(0.0) : family < 2.5 ? dir*0.4 : uWind*0.6;\r\n      vec3 p = position + drift*uTime;\r\n      p.xz = mod(p.xz - uCentre.xz + uBox*0.5, uBox) + uCentre.xz - uBox*0.5;\r\n      float hy = fract(position.y/uBox);\r\n      if (family < 0.5) { p.y = uCentre.y + 0.6 + 1.6*hy + 0.3*sin(uTime*0.9 + seed*30.0); p.x += 0.4*cos(uTime*0.6+seed*20.0); p.z += 0.4*sin(uTime*0.6+seed*20.0); }\r\n      else if (family < 1.5) { p.y = uCentre.y + 0.8 + 0.6*hy; p += 0.35*vec3(sin(uTime*7.0+seed*91.0), sin(uTime*9.0+seed*37.0), cos(uTime*8.0+seed*53.0)); }\r\n      else if (family < 2.5) { p.y = uCentre.y + 0.05; }\r\n      else { p.y = uCentre.y + 0.1 + 0.5*abs(sin(uTime*2.0 + seed*20.0)); }\r\n      vec3 finalPos = p;\r\n      // Fade at box edges (X,Z)\r\n      float fadeFactor = 1.0 - smoothstep(0.35*uBox, 0.5*uBox,\r\n        max(abs(finalPos.x - uCentre.x), abs(finalPos.z - uCentre.z)));\r\n\r\n      // Size scaling\r\n      float pointSize = uSize;\r\n      if (family < 0.5) pointSize *= 1.4; // firefly\r\n\r\n      vec4 mvp = modelViewMatrix * vec4(finalPos, 1.0);\r\n      gl_PointSize = clamp(pointSize * 10.0 / max(-mvp.z, 0.1), 1.0, 5.0); // perspective (size px at 10 units), 1..5 px\r\n\r\n      // Colour and alpha\r\n      vec3 colour;\r\n      float alpha = 1.0;\r\n      if (family < 0.5) { // firefly\r\n        float vPulse = 0.5 + 0.5*sin(uTime*(1.5+seed)+seed*50.0);\r\n        colour = mix(vec3(0.75,1.0,0.3), vec3(1.0,1.0,0.75), vPulse);\r\n        alpha = 0.4 + 0.6*vPulse;\r\n      } else if (family < 1.5) { // gnat\r\n        colour = vec3(0.12,0.11,0.10);\r\n        alpha = 1.0 - 0.5*uNight;\r\n      } else if (family < 2.5) { // beetle\r\n        colour = vec3(0.18,0.32,0.45);\r\n        alpha = 1.0;\r\n      } else { // dust\r\n        colour = vec3(0.62,0.52,0.38);\r\n        alpha = 1.0 - 0.5*uNight;\r\n      }\r\n\r\n      // Night factor for firefly only\r\n      if (family < 0.5) alpha *= uNight;\r\n\r\n      vColor = vec4(colour, alpha * fadeFactor);\r\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(finalPos, 1.0);\r\n    }\r\n  `;\r\n\r\n  const fragmentShader = `\r\n    varying vec4 vColor;\r\n    void main() {\r\n      if (vColor.a < 0.02) discard;\r\n      gl_FragColor = vColor;\r\n    }\r\n  `;\r\n\r\n  // ---------- Material ----------\r\n  const material = new T.ShaderMaterial({\r\n    uniforms: {\r\n      uTime:   { value: 0 },\r\n      uCentre:{ value: new T.Vector3(0,0,0) },\r\n      uWind:  { value: new T.Vector3(0.7,0,-0.5) },\r\n      uNight: { value: 0 },\r\n      uBox:   { value: box },\r\n      uSize:  { value: size },\r\n      uFade:  { value: 1 }\r\n    },\r\n    vertexShader,\r\n    fragmentShader,\r\n    transparent: true,\r\n    depthWrite: false,\r\n    fog: false\r\n  });\r\n\r\n  // ---------- Points ----------\r\n  const points = new T.Points(geometry, material);\r\n  points.frustumCulled = false;\r\n  points.name = 'insects';\r\n\r\n  // ---------- API ----------\r\n  return {\r\n    object: points,\r\n\r\n    update(timeSeconds, centreVector3) {\r\n      material.uniforms.uTime.value   = timeSeconds;\r\n      material.uniforms.uCentre.value.copy(centreVector3);\r\n    },\r\n\r\n    setNight(n) { material.uniforms.uNight.value = n; },\r\n\r\n    setWind(x, z) { material.uniforms.uWind.value.set(x, 0, z); },\r\n\r\n    dispose() {\r\n      geometry.dispose();\r\n      material.dispose();\r\n    },\r\n\r\n    stats() {\r\n      return { calls: 1, counts };\r\n    }\r\n  };\r\n}\r\n","modules/coach-world-rts/levelup.mjs":"// W6 level-up / upgrade feedback. createLevelUp({ownerId,...}) -> {observe(base),dispose(),plays,pending}.\r\n// observe() is fed every snapshot (hud.mjs does this). The first snapshot per owner is a silent baseline (never fires on first load).\r\n// Seen state = per-owner high-water map in localStorage `cw-rts-lvl:<owner>` {buildingId|unit:<id>: highest level celebrated}.\r\n// Visuals: DOM overlay + fx.mjs particles; text goes through hud.toast(); sound through sfx.mjs with a local stinger fallback.\r\nimport {BUILDINGS,MAX_LEVEL} from '../coach-world/catalog.mjs';\r\nimport {UNLOCKS} from '../coach-world/levels.mjs';\r\nimport {accountLevel} from '../coach-world/state.mjs';\r\nimport {UNIT_BY_ID} from '../coach-world/expedition-catalog.mjs';\r\nimport {createFx} from './fx.mjs';\r\n\r\nexport const GAP_MS=500,SEQ_MS=2500,UNIT_MS=1500;\r\nconst UNLOCK_LABEL={flag:'Claim flag',gym:'Gym base',work:'Work base','npc-raids':'NPC raids'};\r\nconst KEY=o=>`cw-rts-lvl:${o}`;\r\n\r\n// Beat plan (A11 4.3 timings). Reduced motion keeps sound + toast + portrait line only (no dim, flash, nudge, burst, ring, reveal).\r\nexport function planSequence({kind='building',reduced=false}={}){\r\n if(kind==='unit')return reduced?[{t:100,type:'sound'},{t:300,type:'toast'},{t:UNIT_MS,type:'settle'}]\r\n  :[{t:0,type:'flash',ms:84},{t:100,type:'burst',n:12},{t:100,type:'sound'},{t:300,type:'toast'},{t:600,type:'line'},{t:UNIT_MS,type:'settle'}];\r\n if(kind==='unlock')return reduced?[{t:500,type:'sound'},{t:900,type:'toast'},{t:1200,type:'line'},{t:SEQ_MS,type:'settle'}]\r\n  :[{t:0,type:'dim'},{t:300,type:'flash',ms:84},{t:380,type:'reveal'},{t:450,type:'burst',n:12},{t:500,type:'sound'},{t:900,type:'toast'},{t:1200,type:'line'},{t:SEQ_MS,type:'settle'}];\r\n return reduced?[{t:500,type:'sound'},{t:900,type:'toast'},{t:1200,type:'line'},{t:1500,type:'ping'},{t:SEQ_MS,type:'settle'}]\r\n  :[{t:0,type:'dim'},{t:300,type:'flash',ms:84},{t:300,type:'nudge',px:2},{t:380,type:'reveal'},{t:450,type:'burst',n:16},{t:450,type:'ring'},{t:500,type:'sound'},{t:700,type:'cheer'},{t:900,type:'toast'},{t:1200,type:'line'},{t:1500,type:'ping'},{t:SEQ_MS,type:'settle'}];\r\n}\r\n\r\nconst memStore=()=>{const m=new Map();return {getItem:k=>m.get(k)??null,setItem:(k,v)=>void m.set(k,v)};};\r\nconst defaultStore=()=>{try{return globalThis.localStorage||memStore();}catch{return memStore();}};\r\nconst defaultReduced=()=>{try{return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches;}catch{return false;}};\r\n\r\n// Two-note rising fifth (building) / quick pair (unit), +1 semitone per level. Prefers a real sfx.mjs cue when one exists.\r\nasync function defaultCue(kind,level){\r\n try{\r\n  const sfx=await import('./sfx.mjs');if(sfx.play(kind==='unit'?'unit-ready':'level-up'))return true;\r\n  const {physicalSound:s}=await import('../../audio/physical-sound.mjs');if(s.muted||!s.volume)return false;\r\n  const f=523*2**((Math.max(1,level)-1)/12);s.unlock?.();\r\n  s.voice?.('levelup',.6,x=>{s.tone(x,f,.2,.12,'triangle',f);s.tone(x,f*1.5,.35,.12,'triangle',f*1.5,.14);});return true;\r\n }catch{return false;}\r\n}\r\n\r\nexport function createLevelUp({\r\n ownerId=()=>null,store=defaultStore(),reduced=defaultReduced,schedule=(fn,ms)=>{const t=setTimeout(fn,ms);return ()=>clearTimeout(t);},now=()=>performance.now(),\r\n hud=null,cue=defaultCue,cheer=null,mount=null,nudge=null,\r\n}={}){\r\n let owner=null,seen={},hydrated=false,disposed=false,running=false,cancelCur=null,cancelGap=null,unlocked=new Set();\r\n const queue=[],due=new Map(),fired=new Set(),plays=[];\r\n const load=o=>{try{const v=JSON.parse(store.getItem(KEY(o))||'{}');return v&&typeof v==='object'&&!Array.isArray(v)?v:{};}catch{return {};}};\r\n const save=()=>{try{store.setItem(KEY(owner),JSON.stringify(seen));}catch{/* private mode: memory only */}};\r\n const label=e=>e.kind==='unit'?(UNIT_BY_ID[e.key]?.label||e.key):e.kind==='unlock'?(UNLOCK_LABEL[e.key]||e.key):(BUILDINGS.find(b=>b.id===e.key)?.label||e.key);\r\n\r\n // ---- overlay (DOM + fx.mjs particles) ----\r\n let ui=null;\r\n const overlay=()=>{\r\n  if(ui||!mount||typeof document==='undefined')return ui;\r\n  const el=document.createElement('div');el.className='cw-rts-lvl';el.setAttribute('aria-hidden','true');\r\n  const mkd=c=>{const d=document.createElement('div');d.className=c;el.append(d);return d;};\r\n  const dim=mkd('cw-rts-lvl-dim'),flash=mkd('cw-rts-lvl-flash'),ring=mkd('cw-rts-lvl-ring'),banner=mkd('cw-rts-lvl-banner');\r\n  const cv=document.createElement('canvas');cv.width=Math.ceil((globalThis.innerWidth||375)/2);cv.height=Math.ceil((globalThis.innerHeight||812)/2);el.append(cv);\r\n  mount.append(el);ui={el,dim,flash,ring,banner,cv,fx:createFx({reduced}),timer:0};return ui;\r\n };\r\n const pump=u=>{if(u.timer)return;const g=u.cv.getContext('2d');u.timer=setInterval(()=>{u.fx.step();g.clearRect(0,0,u.cv.width,u.cv.height);u.fx.draw(g);if(!u.fx.parts.length){clearInterval(u.timer);u.timer=0;}},42);};\r\n const clearUi=()=>{if(!ui)return;clearInterval(ui.timer);ui.timer=0;ui.fx.parts.length=0;ui.el.className='cw-rts-lvl';ui.banner.textContent='';if(nudge)nudge.style.transform='';};\r\n\r\n function run(e,beat){\r\n  const u=overlay(),name=label(e),lvl=e.level;\r\n  switch(beat.type){\r\n   case 'dim':u?.el.classList.add('is-dim');break;\r\n   case 'flash':if(u){u.flash.classList.remove('go');void u.flash.offsetWidth;u.flash.classList.add('go');}break;\r\n   case 'nudge':if(nudge){nudge.style.transform=`translate(${beat.px}px,${-beat.px}px)`;schedule(()=>{nudge.style.transform='';},84);}break;\r\n   case 'reveal':if(u){u.banner.textContent=e.kind==='unit'?name:e.kind==='unlock'?`UNLOCKED: ${name}`:`${name.toUpperCase()}  L${lvl-1} > L${lvl}${lvl>=MAX_LEVEL?' MAX':''}`;u.banner.classList.remove('go');void u.banner.offsetWidth;u.banner.classList.add('go');}break;\r\n   case 'burst':if(u){const cx=u.cv.width/2,cy=u.cv.height*.38;for(let i=0;i<beat.n;i+=4)u.fx.spark(cx+(i-beat.n/2)*3,cy,4);pump(u);}break;\r\n   case 'ring':u?.ring.classList.add('go');break;\r\n   case 'sound':Promise.resolve(cue(e.kind,lvl)).catch(()=>{});break;\r\n   case 'cheer':try{cheer?.(e);}catch{/* optional */}break;\r\n   case 'toast':hud?.toast?.(e.kind==='unit'?`${name} ready`:e.kind==='unlock'?`UNLOCKED: ${name}`:`Construction complete: ${name} L${lvl}`,{ms:2500});break;\r\n   case 'line':if(u&&!u.banner.textContent)u.banner.textContent=name;u?.banner.classList.add('line');break;\r\n   case 'ping':if(e.kind==='building')hud?.ping?.(e.key);break;\r\n   case 'settle':clearUi();break;\r\n  }\r\n }\r\n function next(){\r\n  cancelGap=null;if(disposed||running||!queue.length)return;\r\n  const e=queue.shift(),plan=planSequence({kind:e.kind,reduced:reduced()}),end=plan[plan.length-1].t,start=now(),cancels=[];\r\n  running=true;plays.push({id:e.id,kind:e.kind,start,end:start+end,reduced:reduced(),beats:plan.map(b=>b.type)});\r\n  for(const b of plan)cancels.push(schedule(()=>{if(!disposed)run(e,b);},b.t));\r\n  cancels.push(schedule(()=>{running=false;cancelCur=null;cancelGap=schedule(next,GAP_MS);},end));\r\n  cancelCur=()=>cancels.forEach(c=>c?.());\r\n }\r\n\r\n function baseline(base){\r\n  seen=load(owner);due.clear();fired.clear();queue.length=0;cancelCur?.();cancelGap?.();running=false;clearUi();\r\n  for(const [id,b] of Object.entries(base.buildings||{}))seen[id]=Math.max(seen[id]??0,b.level||0);\r\n  for(const id of Object.keys(base.units||{}))seen[`unit:${id}`]=1;\r\n  const lvl=accountLevel(base);unlocked=new Set(UNLOCKS.filter(u=>lvl>=u.level).map(u=>u.id));\r\n  save();hydrated=true;\r\n }\r\n function observe(base){\r\n  if(disposed||!base)return [];\r\n  const o=ownerId();if(!hydrated||o!==owner){owner=o;baseline(base);for(const [id,b] of Object.entries(base.buildings||{}))if(b.job)due.set(id,b.job.dueAt);return [];}\r\n  const out=[];\r\n  for(const [id,b] of Object.entries(base.buildings||{})){\r\n   if(b.job)due.set(id,b.job.dueAt);\r\n   if((b.level||0)>(seen[id]??0)){seen[id]=b.level;out.push({kind:'building',key:id,level:b.level,id:`${owner}:${id}:${b.level}:${due.get(id)??0}`});due.delete(id);}\r\n  }\r\n  const lvl=accountLevel(base);\r\n  // Unlock banners queue here but stay out of the return value (building/unit events only).\r\n  const unlockOut=[];for(const u of UNLOCKS)if(lvl>=u.level&&!unlocked.has(u.id)){unlocked.add(u.id);unlockOut.push({kind:'unlock',key:u.id,level:u.level,id:`${owner}:unlock:${u.id}`});}\r\n  for(const id of Object.keys(base.units||{})){const k=`unit:${id}`;if(!(seen[k]>=1)){seen[k]=1;out.push({kind:'unit',key:id,level:1,id:`${owner}:unit:${id}:1`});}}\r\n  const fresh=out.filter(e=>!fired.has(e.id)),extra=unlockOut.filter(e=>!fired.has(e.id));if(!fresh.length&&!extra.length)return [];\r\n  for(const e of [...fresh,...extra]){fired.add(e.id);queue.push(e);}\r\n  save();if(!running&&!cancelGap)next();return fresh;\r\n }\r\n return {\r\n  observe,plays,get pending(){return queue.length;},\r\n  dispose(){disposed=true;cancelCur?.();cancelGap?.();queue.length=0;clearUi();ui?.el.remove();ui=null;},\r\n };\r\n}\r\n","modules/coach-world-rts/minimap.mjs":"// CRT minimap (polish lane G). One-hue green terminal view of the real map around the player's OWN base.\r\n// 1-px dots only: roads are 1-px lines, buildings single pixels, water/park sparse dither, base = blinking pixel at the centre.\r\n// Zoom = three levels (tap cycles). Data comes from the same world tiles the 3D layer uses (IndexedDB cache dedups), 2D canvas only, no GL.\r\n// createMinimap(canvas,{getHome,tiles,ringCells,zooms,fallback,overlay}) -> {draw(),setZoom(i),zoom,ready(i),dispose()}\r\nexport const ZOOMS=[{z:16,span:500},{z:15,span:1000},{z:14,span:2000}]; // tile zoom, metres across the square\r\nexport const BG=[3,26,10],FG=[51,255,102]; // #031a0a on #33ff66\r\nconst ROAD_SKIP=7;     // footway\r\nconst ROAD_MAX_FAR=4;  // z14: motorway..tertiary only\r\nconst mix=a=>BG.map((b,i)=>Math.round(b+(FG[i]-b)*a));\r\n\r\n// models: decoded world-tile cells. Returns RGBA bytes (size*size*4), centre of the square = origin (the base).\r\nexport function rasterize(models,{size=96,span=500,roadMax=9}={}){\r\n const px=new Uint8ClampedArray(size*size*4),k=size/span,h=size/2;\r\n for(let i=0;i<size*size;i++){px[i*4]=BG[0];px[i*4+1]=BG[1];px[i*4+2]=BG[2];px[i*4+3]=255;}\r\n const put=(x,y,c)=>{if(x<0||y<0||x>=size||y>=size)return;const o=(y*size+x)*4;px[o]=c[0];px[o+1]=c[1];px[o+2]=c[2];};\r\n const X=m=>Math.round(h+m*k),Y=m=>Math.round(h+m*k);\r\n const cWater=mix(.45),cPark=mix(.28),cRoad=mix(.55),cBld=mix(1);\r\n // area fill (water / park): triangle mask, then sparse dither so it never reads as a solid\r\n const fill=(lay,pick,cOf,dither)=>{\r\n  if(!lay?.idx?.length)return;\r\n  const mask=new Uint8Array(size*size);\r\n  for(let n=0;n<lay.start.length-1;n++){\r\n   if(!pick(n))continue;\r\n   for(let t=lay.start[n];t<lay.start[n+1];t+=3){\r\n    const a=lay.idx[t]*2,b=lay.idx[t+1]*2,c=lay.idx[t+2]*2,\r\n     ax=h+lay.pos[a]*k,ay=h+lay.pos[a+1]*k,bx=h+lay.pos[b]*k,by=h+lay.pos[b+1]*k,cx=h+lay.pos[c]*k,cy=h+lay.pos[c+1]*k;\r\n    const x0=Math.max(0,Math.floor(Math.min(ax,bx,cx))),x1=Math.min(size-1,Math.ceil(Math.max(ax,bx,cx))),\r\n     y0=Math.max(0,Math.floor(Math.min(ay,by,cy))),y1=Math.min(size-1,Math.ceil(Math.max(ay,by,cy)));\r\n    const d=(bx-ax)*(cy-ay)-(cx-ax)*(by-ay);if(!d)continue;\r\n    for(let y=y0;y<=y1;y++)for(let x=x0;x<=x1;x++){\r\n     const u=((x+.5-ax)*(cy-ay)-(cx-ax)*(y+.5-ay))/d,v=((bx-ax)*(y+.5-ay)-(x+.5-ax)*(by-ay))/d;\r\n     if(u>=0&&v>=0&&u+v<=1)mask[y*size+x]=1;}}}\r\n  for(let y=0;y<size;y++)for(let x=0;x<size;x++)if(mask[y*size+x]&&dither(x,y))put(x,y,cOf);\r\n };\r\n for(const m of models){if(!m||m.missing)continue;\r\n  fill(m.landuse,n=>m.landuse.cls[n]===3||m.landuse.cls[n]===4,cPark,(x,y)=>(x+y*2)%5===0);\r\n  fill(m.water,()=>true,cWater,(x,y)=>x%2===0&&y%2===0);}\r\n for(const m of models){if(!m||m.missing)continue;const r=m.roads;\r\n  for(let i=0;i<(r?.cls.length||0);i++){const c=r.cls[i];if(c===ROAD_SKIP||c===8||c>roadMax)continue;\r\n   let px0=null,py0=null;\r\n   for(let p=r.start[i];p<r.start[i+1];p++){const x=X(r.pts[p*2]),y=Y(r.pts[p*2+1]);\r\n    if(px0!==null){ // Bresenham, 1 px\r\n     let dx=Math.abs(x-px0),dy=-Math.abs(y-py0),sx=px0<x?1:-1,sy=py0<y?1:-1,e=dx+dy,cx=px0,cy=py0;\r\n     for(let s=0;s<4*size;s++){put(cx,cy,cRoad);if(cx===x&&cy===y)break;const e2=2*e;if(e2>=dy){e+=dy;cx+=sx;}if(e2<=dx){e+=dx;cy+=sy;}}}\r\n    px0=x;py0=y;}}}\r\n for(const m of models){if(!m||m.missing)continue;const b=m.buildings;\r\n  for(let i=0;i<(b?.height.length||0);i++){let x0=1e9,x1=-1e9,z0=1e9,z1=-1e9;\r\n   for(let t=b.start[i];t<b.start[i+1];t++){const v=b.idx[t]*3,x=b.pos[v],z=b.pos[v+2];if(x<x0)x0=x;if(x>x1)x1=x;if(z<z0)z0=z;if(z>z1)z1=z;}\r\n   if(x1>=x0)put(X((x0+x1)/2),Y((z0+z1)/2),cBld);}}\r\n return px;\r\n}\r\n\r\nexport function createMinimap(canvas,{getHome,tiles,ringCells,zooms=ZOOMS,fallback,overlay,doc=canvas.ownerDocument}={}){\r\n const S=canvas.width,g=canvas.getContext('2d'),cache=new Map(),pending=new Map(),failed=new Set();let zoom=0,dead=false;\r\n const blink=()=>Math.floor((globalThis.performance?.now?.()??Date.now())/400)%2===0;\r\n const tint=a=>`rgb(${mix(a).join()})`;\r\n function ready(i){\r\n  if(cache.has(i)||pending.has(i)||failed.has(i)||dead||!tiles||!getHome)return pending.get(i)||null;\r\n  const lv=zooms[i],p=(async()=>{\r\n   const home=await getHome();if(dead||!home)return;\r\n   const models=await tiles.load(ringCells(home.lat,home.lng,lv.z),{lat:home.lat,lng:home.lng});if(dead)return;\r\n   if(!models.some(m=>m&&!m.missing&&(m.roads?.cls.length||m.buildings?.height.length))){failed.add(i);return;} // no tiles here (offline / outside the bake): keep the base plot\r\n   const px=rasterize(models,{size:S,span:lv.span,roadMax:lv.z===14?ROAD_MAX_FAR:9}),cv=doc.createElement('canvas');\r\n   cv.width=cv.height=S;const cg=cv.getContext('2d');if(!cg)return;\r\n   const img=cg.createImageData(S,S);img.data.set(px);cg.putImageData(img,0,0);cache.set(i,cv);\r\n  })().catch(()=>{failed.add(i);}).finally(()=>{pending.delete(i);if(!dead)draw();});\r\n  pending.set(i,p);return p;\r\n }\r\n function draw(){\r\n  if(dead||!g)return;const cv=cache.get(zoom);\r\n  if(cv){g.drawImage(cv,0,0);g.fillStyle=blink()?tint(1):tint(0);g.fillRect(S/2-1,S/2-1,2,2);}\r\n  else{g.fillStyle=tint(0);g.fillRect(0,0,S,S);fallback?.(g,{tint,blink:blink()});ready(zoom);}\r\n  overlay?.(g,{tint,blink:blink(),map:!!cv,zoom});\r\n }\r\n function setZoom(i){zoom=Math.max(0,Math.min(zooms.length-1,i|0));canvas.dataset.zoom=String(zoom);draw();return zoom;}\r\n canvas.dataset.zoom='0';\r\n return {draw,setZoom,get zoom(){return zoom;},ready,dispose(){dead=true;cache.clear();pending.clear();}};\r\n}\r\n","modules/coach-world-rts/mining.mjs":"// Mining show: one visible crew member for every committed trip in the settled economy snapshot (base.rtsEconomy v2).\n// The trip state is the only source of truth: phase, route position, carried load and the deposit tile are read from it (projection); nothing\n// here invents a trip or credits anything. Between snapshots the phase is interpolated on the local clock (display only). Assignment changes,\n// stock running out and stop-gather never hide a load that is still on its way back. Manual orders, builder reservations and away crews drop the miner.\nimport {placeNodes,placeNode} from '../coach-world/resource-placement.mjs';\nimport {economyState} from '../coach-world/rts-economy.mjs';\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\nexport {placeNode};\nconst STRIKE_MS=1050,PHASE_ORDER=['outbound','working','returning','depositing'];\n\nexport function reservedBuilders(base){\n const out=new Set(),max=Number(base?.workers)||0;\n for(const b of Object.values(base?.buildings||{})){const j=b.job;if(j?.policy!=='occupied-worker')continue;const m=/^crew-(\\d+)$/.exec(j.builderId||'');if(m&&+m[1]>=1&&+m[1]<=max)out.add(+m[1]-1);}\n return out;\n}\nconst isWorker=u=>u.role==='worker'&&u.definition?.sheet!=='pets';\n\nconst ORE=['..aa....','.abbaa..','abbcbba.','abcccbba','.abbcba.','..abba..'],PAL={a:'#4d3b2a',b:'#8e7a5a'},DEAD={a:'#33322e',b:'#55544e',c:'#6a6a62'};\nconst VENT=['..d..d..','.dTTTTd.','dTgggTTd','dTgGGgTd','.dTgggd.','..dddd..'],VPAL={d:'#2b3b3a',T:'#4f6b66',g:'#35e0a0',G:'#d8fff0'},VDEAD={d:'#2b2b28',T:'#3f403c',g:'#555650',G:'#6a6a62'};\n\n\nexport function tripPosition(trip,w,dt=0){\n const c=trip.config,r=w.route,work=c.loadCycles*c.workCycleMs;\n const seq=[['outbound',r.outboundMs],['working',work],['returning',r.inboundMs],['depositing',c.depositMs]];\n const at=seq.findIndex(([p])=>p===w.phase);\n let t=seq.slice(0,at).reduce((s,[,d])=>s+d,0)+(seq[at][1]-w.remainingMs)+dt;\n for(const [phase,dur] of seq){if(t<dur)return {phase,into:t,dur};t-=dur;}\n return null;\n}\n// Position along a path of cells (path[0] is where the phase starts); zero-length paths stay on their cell.\nfunction placeAlong(path,into,dur){\n if(path.length<2||dur<=0)return path[path.length-1];\n const f=Math.min(1,into/dur)*(path.length-1),i=Math.min(path.length-2,Math.floor(f)),k=f-i;\n return {x:path[i].x+(path[i+1].x-path[i].x)*k,y:path[i].y+(path[i+1].y-path[i].y)*k};\n}\nconst positionOf=p=>p.phase==='outbound'?placeAlong([p.home,...p.route.outbound],p.into,p.dur)\n :p.phase==='returning'?placeAlong([p.node,...p.route.inbound],p.into,p.dur)\n :p.phase==='working'?{x:p.node.x,y:p.node.y}:{x:p.home.x,y:p.home.y};\n\nexport function createMining({rts,play=null,reduced=()=>false,clock=()=>Date.now()}={}){\n // One miner per committed trip. The trip's worker id decides the unit (crew-N -> visual N-1).\n const m={miners:[],place:null,places:[],placeKey:'',sfx:play,base:null,terrain:null,lastTr:null,reserved:new Set(),snap:null,snapAt:0,get node(){return m.place?.node||null;},\n  get miner(){return m.miners[0]?.u||null;},get phase(){return m.miners[0]?.phase||'idle';}};\n const ev=(r,type,on)=>rts.onEvent?.({type,unitId:r.u.id,on,kind:r.kind});\n function drop(r){\n  if(r.phase==='working')ev(r,'work',false);\n  if(r.carry)ev(r,'carry',false);\n  if(r.u.mover==='rts'){r.u.mover='codex';r.u.rtsPath=null;}\n  m.miners.splice(m.miners.indexOf(r),1);\n }\n // Trip snapshot -> per-worker position. Local clock only moves the snapshot forward between updates; a fresh snapshot resets it.\n // Rest is the station's authoritative flag (base.rest, set at the instant it flips): while resting nothing walks, works or deposits on screen.\n // Resuming starts from the snapshot's own remaining times, so rest time is never shown as progress.\n function projectTrips(base,nowMs){\n  const trip=base?.rtsEconomy?.version===2?base.rtsEconomy.tripState:null;\n  if(!trip)return new Map();\n  const resting=base.rest===true;\n  if(trip!==m.snap||resting!==m.resting){m.snap=trip;m.snapAt=nowMs;m.resting=resting;}\n  const dt=resting?0:Math.max(0,nowMs-m.snapAt),out=new Map();\n  for(const [id,w] of Object.entries(trip.workers)){\n   if(w.phase==='idle'||!w.route)continue;\n   const v=tripPosition(trip,w,dt),node=trip.nodes.find(n=>n.id===w.nodeId);\n   if(v&&node)out.set(id,{...v,route:w.route,node,home:trip.layout.home,carried:w.carried,at:trip.lastAt+dt});\n  }\n  return out;\n }\n // Enter every phase between the miner's last one and this one, so events fire once each and skipped phases still show their load.\n function enter(r,phase){\n  if(phase==='working'){ev(r,'work',true);r.strikeAt=-1;}\n  if(phase==='returning'){ev(r,'work',false);ev(r,'carry',true);r.carry=true;}\n  if(phase==='depositing'){if(r.carry)ev(r,'carry',false);r.carry=false;m.sfx?.('deposit');}\n }\n function strike(r){\n  const tr=m.lastTr,p=tr&&m.nodeScreen(tr,m.terrain,r.place);if(!p)return;\n  rts.fx?.spark(p.x,p.y,reduced()?1:3);\n }\n // Decides first, moves second. A snapshot is applied only if its trip clock moved forward. Inside one load progress never moves back\n // (an earlier snapshot holds the miner where it is, position included). A new load is recognised only by a fresher snapshot that\n // shows an outbound walk after this miner's load ended, or a different route; only then does the walk restart.\n function apply(r,p){\n  if(r.at!=null&&p.at<r.at)return; // the display clock (trip clock + local extrapolation) never runs backwards: an older snapshot moves nothing\n  const fromIdx=PHASE_ORDER.indexOf(r.phase),toIdx=PHASE_ORDER.indexOf(p.phase);\n  const nextLoad=r.phase!=null&&(p.route.hash!==r.routeHash||(p.phase==='outbound'&&fromIdx>0));\n  if(nextLoad){if(r.phase==='working')ev(r,'work',false);if(r.carry)ev(r,'carry',false);r.phase=null;r.carry=false;r.strikeAt=-1;}\n  const curIdx=PHASE_ORDER.indexOf(r.phase);\n  r.at=p.at;\n  if(r.phase!=null&&toIdx<curIdx)return; // same load, earlier snapshot phase: hold (clock already accepted above)\n  const pos=positionOf(p);r.u.x=pos.x;r.u.y=pos.y;r.u.rtsPath=null;r.place={node:p.node};r.routeHash=p.route.hash;\n  if(r.phase==null)enter(r,p.phase);\n  else if(toIdx>curIdx)for(let i=curIdx+1;i<=toIdx;i++)enter(r,PHASE_ORDER[i]);\n  r.phase=p.phase;\n  if(p.phase==='working'){const beat=Math.floor(p.into/STRIKE_MS);if(beat!==r.strikeAt){r.strikeAt=beat;m.sfx?.('mine-strike');strike(r);}}\n }\n // called once per frame by runtime.step; the trip snapshot is the source, n is unused (no per-tick loop)\n m.step=(crew,base,tr,n=1,terrain)=>{\n  m.lastTr=tr||m.lastTr;m.terrain=terrain||m.terrain;m.base=base;\n  const key=base?.buildings?.hq?JSON.stringify(Object.values(base.buildings).map(b=>[b.id,b.x,b.y,b.level,!!b.job])):'';\n  if(key!==m.placeKey){m.placeKey=key;const ps=placeNodes(base),p=placeNode(base);m.places=ps;m.place=p?ps.find(q=>q.node.x===p.node.x&&q.node.y===p.node.y)||p:null;}\n  m.reserved=reservedBuilders(base);\n  for(const u of crew)u.reservedBuilder=m.reserved.has(u.id); // canClaim() refuses these\n  const trips=projectTrips(base,clock());\n  const unitFor=wid=>crew.find(c=>c.id===Number(wid.slice(5))-1);\n  const free=u=>!!u&&isWorker(u)&&!u.away&&!u.hasOrder&&!u.rally&&!m.reserved.has(u.id);\n  for(const r of [...m.miners])if(!trips.has(r.workerId)||!crew.includes(r.u)||r.u.mover!=='rts'||!free(r.u))drop(r);\n  for(const [wid,p] of trips){\n   if(m.miners.some(r=>r.workerId===wid))continue;\n   const u=unitFor(wid);if(!free(u)||!rts.claim(u,base))continue;\n   m.miners.push({u,workerId:wid,phase:null,strikeAt:-1,carry:false,place:{node:p.node},kind:p.node.kind,faction:p.node.faction});\n  }\n  for(const r of m.miners){const p=trips.get(r.workerId);if(p)apply(r,p);}\n };\n m.dropAll=()=>{for(const r of [...m.miners])drop(r);};\n // release(): all miners; release(ids): just those unit ids (Set/array)\n m.release=ids=>{for(const r of [...m.miners])if(!ids||ids==='all'||ids.includes?.(r.u.id)||ids.has?.(r.u.id))drop(r);};\n // 2D fallback drawing (the 3D base draws its own meshes): every hall's mineral node + vent, depth-sorted with units by foot y; ground-height offset matches canvas-scene units (heightAt*2)\n const depleted=(f,k)=>{try{return economyState(m.base).nodes[f][k]<=0;}catch{return false;}};\n m.sortables=()=>m.places.flatMap(p=>[{depth:p.node.y+.75,draw:(ctx,tr,terrain)=>drawSprite(ctx,tr,terrain,p.node,ORE,{...PAL,c:FACTION_BY_ID[p.faction]?.color||'#f0c93c'},DEAD,depleted(p.faction,'minerals'))},\n  ...(p.gas?[{depth:p.gas.node.y+.75,draw:(ctx,tr,terrain)=>drawSprite(ctx,tr,terrain,p.gas.node,VENT,VPAL,VDEAD,depleted(p.faction,'gas'))}]:[])]);\n function drawSprite(ctx,tr,terrain,n,rows,pal,dead,off){\n  if(!n||!tr)return;const pl=off?dead:pal;\n  const h=terrain?.heightAt?terrain.heightAt(n.x,n.y)*2:0,p=Math.max(1,Math.round(tr.unit/10)),x=Math.round(tr.ox+(n.x+.5)*tr.unit-4*p),y=Math.round(tr.oy+(n.y+1)*tr.unit-h-6*p-2);\n  ctx.fillStyle='#0006';ctx.fillRect(x,y+6*p,8*p,p);\n  rows.forEach((row,j)=>{for(let i=0;i<8;i++){const c=pl[row[i]];if(c){ctx.fillStyle=c;ctx.fillRect(x+i*p,y+j*p,p,p);}}});\n }\n m.nodeScreen=(tr,terrain,place=m.place)=>{const n=place?.node;return n&&{x:tr.ox+(n.x+.5)*tr.unit,y:tr.oy+(n.y+.4)*tr.unit-(terrain?.heightAt?terrain.heightAt(n.x,n.y)*2:0)};};\n return m;\n}\n","modules/coach-world-rts/mom-categories.mjs":"import { BIOMES } from './biomes.mjs';\r\n\r\nexport const CATEGORIES = Object.freeze([\r\n  'house','apartment','retail','restaurant','office',\r\n  'industrial','civic','school','hospital','religious',\r\n  'parking','tower','other'\r\n]);\r\n\r\nexport function categoryOf(input) {\r\n  if (!input) return 'other';\r\n  const { ovClass = '', ovSubtype = '', osm = {} } = input;\r\n  const amenity = (osm.amenity || '').toLowerCase();\r\n  const building = (osm.building || '').toLowerCase();\r\n  const shop = (osm.shop || '').toString().trim();\r\n  const office = (osm.office || '').toString().trim();\r\n\r\n  if (['restaurant','cafe','fast_food','bar','pub'].includes(amenity))\r\n    return 'restaurant';\r\n  if (['school','university','college','kindergarten'].includes(amenity) || ovClass === 'education')\r\n    return 'school';\r\n  if (['hospital','clinic','doctors'].includes(amenity) || building === 'hospital' || ovClass === 'medical')\r\n    return 'hospital';\r\n  if (amenity === 'place_of_worship' ||\r\n      ['religious','church','mosque','temple','cathedral'].includes(building) ||\r\n      ovClass === 'religious')\r\n    return 'religious';\r\n  if (amenity === 'parking' ||\r\n      ['parking','garage','carport'].includes(building) ||\r\n      ovClass === 'parking')\r\n    return 'parking';\r\n  if (['townhall','police','fire_station','library','courthouse'].includes(amenity) ||\r\n      ['civic','government'].includes(building) ||\r\n      ['civic','government'].includes(ovClass))\r\n    return 'civic';\r\n  if (shop || ['retail','supermarket'].includes(building) || ['retail','shop'].includes(ovSubtype))\r\n    return 'retail';\r\n  if (office || building === 'office' || ovClass === 'office')\r\n    return 'office';\r\n  if (['industrial','warehouse','factory'].includes(building) || ovClass === 'industrial')\r\n    return 'industrial';\r\n  if (['skyscraper','tower'].includes(building) || ovSubtype === 'tower')\r\n    return 'tower';\r\n  if (building === 'apartments' || ovSubtype === 'apartments')\r\n    return 'apartment';\r\n  if (['house','residential','detached','terrace','bungalow',\r\n        'semidetached_house','semi_detached','hut'].includes(building) ||\r\n      ovClass === 'residential')\r\n    return 'house';\r\n  return 'other';\r\n}\r\n\r\nexport const PALETTE = Object.freeze({\r\n  wall: '#6b2fa0',\r\n  trim: '#d9a441',\r\n  glow: '#b98cff'\r\n});\r\n\r\nexport const RECIPES = Object.freeze({\r\n  house:      { roof: 'cap',    props: [],            glyph: null,        heightStep: 3, palette: PALETTE },\r\n  apartment:  { roof: 'flat',   props: ['antenna'],   glyph: null,        heightStep: 3, palette: PALETTE },\r\n  retail:     { roof: 'flat',   props: ['vent'],      glyph: 'cart',       heightStep: 3, palette: PALETTE },\r\n  restaurant: { roof: 'cap',    props: ['vent','tank'],'glyph': 'fork',    heightStep: 3, palette: PALETTE },\r\n  office:     { roof: 'flat',   props: ['antenna','dish'], glyph: 'desk',  heightStep: 3, palette: PALETTE },\r\n  industrial: { roof: 'flat',   props: ['tank','vent'],'glyph': 'gear',    heightStep: 3, palette: PALETTE },\r\n  civic:      { roof: 'dome',   props: [],            glyph: 'star',       heightStep: 3, palette: PALETTE },\r\n  school:     { roof: 'ring',   props: ['antenna'],   glyph: 'book',       heightStep: 3, palette: PALETTE },\r\n  hospital:   { roof: 'flat',   props: ['dish'],      glyph: 'cross',      heightStep: 3, palette: PALETTE },\r\n  religious:  { roof: 'spire',  props: [],            glyph: 'cross',      heightStep: 3, palette: PALETTE },\r\n  parking:    { roof: 'flat',   props: ['antenna','tank'], glyph: 'car',   heightStep: 3, palette: PALETTE },\r\n  tower:      { roof: 'spire',  props: ['antenna','dish','spire'],'glyph': 'tower',heightStep: 3, palette: PALETTE },\r\n  other:      { roof: 'cap',    props: ['antenna'],   glyph: null,        heightStep: 3, palette: PALETTE }\r\n});\r\n\r\nexport const RUINS = Object.freeze({\r\n  chest:          { ground: BIOMES['chest'].ground, ridge: BIOMES['chest'].ridge, shape: BIOMES['chest'].plant.shape, color: BIOMES['chest'].plant.color },\r\n  quads:          { ground: BIOMES['quads'].ground, ridge: BIOMES['quads'].ridge, shape: BIOMES['quads'].plant.shape, color: BIOMES['quads'].plant.color },\r\n  glutes:         { ground: BIOMES['glutes'].ground, ridge: BIOMES['glutes'].ridge, shape: BIOMES['glutes'].plant.shape, color: BIOMES['glutes'].plant.color },\r\n  'arms-shoulders':{ ground: BIOMES['arms-shoulders'].ground, ridge: BIOMES['arms-shoulders'].ridge, shape: BIOMES['arms-shoulders'].plant.shape, color: BIOMES['arms-shoulders'].plant.color },\r\n  yoga:           { ground: BIOMES['yoga'].ground, ridge: BIOMES['yoga'].ridge, shape: BIOMES['yoga'].plant.shape, color: BIOMES['yoga'].plant.color },\r\n  'martial-arts': { ground: BIOMES['martial-arts'].ground, ridge: BIOMES['martial-arts'].ridge, shape: BIOMES['martial-arts'].plant.shape, color: BIOMES['martial-arts'].plant.color },\r\n  cardio:         { ground: BIOMES['cardio'].ground, ridge: BIOMES['cardio'].ridge, shape: BIOMES['cardio'].plant.shape, color: BIOMES['cardio'].plant.color }\r\n});\r\n","modules/coach-world-rts/mom-kit.mjs":"// R58 WS-D: MOM Inc. city kit. One merged wall-mass geometry per cell (1 draw call, atlas map) + <=6 shared InstancedMesh props.\r\n// Every object carries userData.layer='mom' and is never raidable (pickRaidable). Categories/recipes come from mom-categories.mjs.\r\nimport { createPs1Material } from './ps1-material.mjs';\r\nimport { RECIPES } from './mom-categories.mjs';\r\n\r\nexport const MOM_PALETTE = Object.freeze({ wall: '#6b2fa0', trim: '#d9a441', glow: '#b98cff' });\r\nexport { CATEGORIES } from './mom-categories.mjs';\r\nexport const pickRaidable = objs => objs.filter(o => o.userData.layer !== 'mom' && o.userData.layer !== 'hub');\r\nexport const MAX_PROP_MESHES = 6;\r\n\r\n// 16x16 atlas cells in a 4x4 sheet (64x64 px): 0 solid, 1 floor lines, 2.. glyphs. 8x8 bitmaps drawn at 2x.\r\nconst GLYPHS = { fork: [0x24, 0x24, 0x24, 0x3C, 0x18, 0x18, 0x18, 0x18], cart: [0x00, 0x03, 0x7E, 0x42, 0x7E, 0x24, 0x24, 0x66], desk: [0x00, 0x7E, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x00], gear: [0x18, 0xDB, 0x7E, 0xE7, 0xE7, 0x7E, 0xDB, 0x18], cross: [0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18], book: [0x7E, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0x7E], star: [0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x7E, 0x66, 0x42], car: [0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x24, 0x24], tower: [0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x7E, 0x7E, 0xFF] };\r\nconst GLYPH_CELL = Object.fromEntries(Object.keys(GLYPHS).map((k, i) => [k, i + 2]));\r\nconst hex = c => [parseInt(c.slice(1, 3), 16), parseInt(c.slice(3, 5), 16), parseInt(c.slice(5, 7), 16)];\r\n\r\nexport function createMomAtlas(T) {\r\n  const px = new Uint8Array(64 * 64 * 4);\r\n  const put = (cx, cy, x, y, c) => { const o = ((cy * 16 + y) * 64 + cx * 16 + x) * 4; px[o] = c[0]; px[o + 1] = c[1]; px[o + 2] = c[2]; px[o + 3] = 255; };\r\n  const gold = hex(MOM_PALETTE.trim), dark = [34, 12, 58], rows = Object.values(GLYPHS);\r\n  for (let c = 0; c < 16; c++) for (let y = 0; y < 16; y++) for (let x = 0; x < 16; x++) {\r\n    const cx = c % 4, cy = c >> 2;\r\n    if (c === 1) put(cx, cy, x, y, (y % 4 >= 1 && y % 4 <= 2 && x % 5 >= 1 && x % 5 <= 3) ? [255, 255, 255] : [78, 48, 108]); // lit windows on dark lattice\r\n    else if (c >= 2 && c - 2 < rows.length) put(cx, cy, x, y, (rows[c - 2][(15 - y) >> 1] >> (7 - (x >> 1))) & 1 ? gold : dark);\r\n    else put(cx, cy, x, y, [255, 255, 255]);\r\n  }\r\n  const tex = new T.DataTexture(px, 64, 64, T.RGBAFormat);\r\n  tex.magFilter = tex.minFilter = T.NearestFilter; tex.generateMipmaps = false; tex.needsUpdate = true;\r\n  return tex;\r\n}\r\nconst cellUv = (c, u, v) => [(c % 4 + 0.03 + u * 0.94) / 4, ((c >> 2) + 0.03 + v * 0.94) / 4];\r\n\r\n// ---- geometry accumulator ----\r\nconst acc = () => ({ pos: [], nor: [], uv: [], col: [] });\r\nfunction addGeo(T, a, geo, x, y, z, color, cell = 0, ry = 0, sx = 1, sy = 1, sz = 1) {\r\n  const g = geo.index ? geo.toNonIndexed() : geo;\r\n  g.applyMatrix4(new T.Matrix4().compose(new T.Vector3(x, y, z), new T.Quaternion().setFromAxisAngle(new T.Vector3(0, 1, 0), ry), new T.Vector3(sx, sy, sz)));\r\n  const p = g.attributes.position, n = g.attributes.normal, u = g.attributes.uv, c = new T.Color(color);\r\n  for (let i = 0; i < p.count; i++) {\r\n    a.pos.push(p.getX(i), p.getY(i), p.getZ(i)); a.nor.push(n.getX(i), n.getY(i), n.getZ(i));\r\n    a.uv.push(...cellUv(cell, u.getX(i), u.getY(i))); a.col.push(c.r, c.g, c.b);\r\n  }\r\n  if (g !== geo) g.dispose(); geo.dispose();\r\n}\r\nfunction toGeometry(T, a) {\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute('position', new T.Float32BufferAttribute(a.pos, 3)); g.setAttribute('normal', new T.Float32BufferAttribute(a.nor, 3));\r\n  g.setAttribute('uv', new T.Float32BufferAttribute(a.uv, 2)); g.setAttribute('color', new T.Float32BufferAttribute(a.col, 3));\r\n  return g;\r\n}\r\nconst shade = (hexc, k) => '#' + hex(hexc).map(v => Math.max(0, Math.min(255, Math.round(v * k))).toString(16).padStart(2, '0')).join('');\r\nconst hash = s => { let h = 2166136261; for (const ch of String(s)) { h ^= ch.charCodeAt(0); h = Math.imul(h, 16777619); } return h >>> 0; };\r\n\r\n// ---- per-category massing: y up from 0, centred on x/z. Returns {y,w,d} = top of the main mass for the recipe roof crown. ----\r\nfunction mass(T, a, cat, w, d, h, pal, glyph) {\r\n  const B = (x, y, z, bw, bh, bd, col = pal.wall, cell = 1) => addGeo(T, a, new T.BoxGeometry(bw, bh, bd), x, y + bh / 2, z, col, cell);\r\n  const C = (x, y, z, r, ch, col = pal.wall, seg = 8, cell = 1) => addGeo(T, a, new T.CylinderGeometry(r, r, ch, seg), x, y + ch / 2, z, col, cell);\r\n  const K = (x, y, z, r, ch, col = pal.trim, seg = 6) => addGeo(T, a, new T.ConeGeometry(r, ch, seg), x, y + ch / 2, z, col, 0);\r\n  const band = (y, bw, bd, bh = 0.7) => B(0, y, 0, bw + 0.5, bh, bd + 0.5, pal.trim, 0);\r\n  const sign = (x, y, z, sw, sh) => { if (glyph && GLYPH_CELL[glyph]) addGeo(T, a, new T.PlaneGeometry(sw, sh), x, y, z, '#ffffff', GLYPH_CELL[glyph]); };\r\n  const r = Math.min(w, d) / 2;\r\n  switch (cat) {\r\n    case 'apartment': { const t = 3, th = h / t; for (let i = 0; i < t; i++) { const k = 1 - i * 0.2; B(0, i * th, 0, w * k, th, d * k); band(i * th + th - 0.7, w * k, d * k); } sign(0, h * 0.2, d / 2 + 0.05, w * 0.3, w * 0.3); return { y: h, w: w * 0.6, d: d * 0.6 }; }\r\n    case 'retail': { const bh = Math.min(h, 7); B(0, 0, 0, w, bh, d); B(0, bh, 0, w * 1.15, 0.9, d * 1.15, pal.trim, 0); B(w * 0.42, 0, d / 2 + 0.5, 1.2, bh * 2.4, 1.2, pal.glow, 0); sign(w * 0.42, bh * 2.6, d / 2 + 1.2, 5, 5); sign(0, bh * 0.6, d / 2 + 0.05, w * 0.5, bh * 0.5); return { y: bh + 0.9, w: w * 0.5, d: d * 0.5 }; }\r\n    case 'restaurant': { const bh = Math.min(h, 6); B(0, 0, 0, w, bh, d); addGeo(T, a, new T.SphereGeometry(r * 0.9, 10, 4, 0, Math.PI * 2, 0, Math.PI / 2), 0, bh, 0, pal.wall, 1, 0, 1.2, 0.5, 1.2); addGeo(T, a, new T.TorusGeometry(r * 1.05, 0.5, 4, 12).rotateX(Math.PI / 2), 0, bh + 0.2, 0, pal.trim, 0); sign(0, bh * 0.6, d / 2 + 0.05, w * 0.4, w * 0.4); return { y: bh + r * 0.5, w: 1, d: 1 }; }\r\n    case 'office': { B(0, 0, 0, w * 0.7, h, d * 0.7); for (let i = 1; i < 5; i++) B(0, h * i / 5, 0, w * 0.7 + 0.5, 0.5, d * 0.7 + 0.5, pal.trim, 0); B(0, h, 0, w * 0.5, 2, d * 0.5, pal.trim, 0); sign(0, h * 0.85, d * 0.35 + 0.05, w * 0.35, w * 0.35); return { y: h + 2, w: w * 0.3, d: d * 0.3 }; }\r\n    case 'industrial': { const bh = Math.min(h, 9); B(0, 0, -d * 0.1, w, bh, d * 0.7); for (let i = 0; i < 4; i++) addGeo(T, a, new T.ConeGeometry(w / 8, 2.4, 3), -w * 0.375 + i * w * 0.25, bh + 1.2, -d * 0.1, pal.trim, 0, 0, 1, 1, 2 * d / w); C(-w * 0.3, 0, d * 0.35, r * 0.28, bh * 1.8); C(w * 0.1, 0, d * 0.35, r * 0.28, bh * 1.8); C(w * 0.35, 0, d * 0.35, r * 0.2, bh * 2.4, pal.glow); sign(0, bh * 0.55, d * 0.25 + 0.05, w * 0.3, w * 0.3); return { y: 0, w: 0, d: 0 }; }\r\n    case 'civic': { const bh = Math.min(h, 10); B(0, 0, 0, w, bh * 0.6, d); for (let i = -2; i <= 2; i++) C(i * w * 0.19, 0, d / 2 + 0.8, 0.7, bh * 0.6, pal.trim, 6, 0); B(0, bh * 0.6, 0, w * 1.05, 1, d * 1.05, pal.trim, 0); C(0, bh * 0.6, 0, r * 0.5, bh * 0.4, pal.wall, 10); addGeo(T, a, new T.SphereGeometry(r * 0.55, 10, 5, 0, Math.PI * 2, 0, Math.PI / 2), 0, bh, 0, pal.trim, 0); sign(0, bh * 0.3, d / 2 + 0.06, w * 0.3, w * 0.3); return { y: 0, w: 0, d: 0 }; }\r\n    case 'school': { const bh = Math.min(h, 8); B(-w * 0.25, 0, 0, w * 0.5, bh * 0.7, d); B(w * 0.3, 0, 0, w * 0.35, bh * 0.7, d); B(w * 0.05, 0, 0, w * 0.15, bh * 1.8, d * 0.5, pal.trim, 1); sign(-w * 0.25, bh * 0.4, d / 2 + 0.05, w * 0.3, w * 0.3); K(w * 0.05, bh * 1.8, 0, d * 0.28, 3, pal.glow, 4); return { y: 0, w: 0, d: 0 }; }\r\n    case 'hospital': { const bh = Math.min(h, 12); B(0, 0, 0, w, bh * 0.5, d * 0.45); B(0, 0, 0, w * 0.45, bh, d); band(bh * 0.5, w, d * 0.45, 0.5); addGeo(T, a, new T.CylinderGeometry(r * 0.35, r * 0.35, 0.5, 12), 0, bh + 0.25, 0, pal.glow, 0); sign(0, bh * 0.7, d / 2 + 0.05, w * 0.3, w * 0.3); return { y: 0, w: 0, d: 0 }; }\r\n    case 'religious': { const bh = Math.min(h, 7); B(0, 0, 0, w * 0.8, bh, d); K(0, bh, 0, w * 0.55, bh * 0.5, pal.wall, 4); B(0, 0, -d * 0.35, w * 0.3, bh * 3.2, w * 0.3, pal.wall, 1); K(0, bh * 3.2, -d * 0.35, w * 0.28, bh * 1.8, pal.trim, 4); addGeo(T, a, new T.TorusGeometry(w * 0.12, 0.2, 3, 8), 0, bh * 3.0, -d * 0.35 + w * 0.15, pal.glow, 0); sign(0, bh * 0.5, d / 2 + 0.05, w * 0.35, w * 0.35); return { y: 0, w: 0, d: 0 }; }\r\n    case 'parking': { const t = 4, th = h / t; for (let i = 0; i < t; i++) B(0, i * th * 0.95 + th * 0.55, 0, w, 0.7, d, i % 2 ? pal.wall : pal.trim, 0); for (const [sx, sz] of [[-1, -1], [1, -1], [-1, 1], [1, 1]]) B(sx * (w / 2 - 0.5), 0, sz * (d / 2 - 0.5), 1, h * 0.9, 1, pal.wall, 0); B(0, 0, 0, w * 0.2, h * 0.9, d * 0.2); sign(0, h * 0.3, d / 2 + 0.05, w * 0.3, w * 0.3); return { y: h * 0.95, w: w * 0.3, d: d * 0.3 }; }\r\n    case 'tower': { const t = 4, th = h / t; for (let i = 0; i < t; i++) { const k = 0.8 - i * 0.14; B(0, i * th, 0, w * k, th, d * k); addGeo(T, a, new T.TorusGeometry(Math.max(w, d) * (k * 0.62 + 0.1), 0.5, 3, 10).rotateX(Math.PI / 2), 0, i * th + th, 0, pal.trim, 0); } K(0, h, 0, w * 0.1, h * 0.4, pal.glow, 4); sign(0, h * 0.18, d * 0.4 + 0.05, w * 0.3, w * 0.3); return { y: 0, w: 0, d: 0 }; }\r\n    case 'house': { B(0, 0, 0, w, h, d); K(0, h, 0, Math.max(w, d) * 0.75, h * 0.6, pal.trim, 4); return { y: 0, w: 0, d: 0 }; }\r\n    default: { B(0, 0, 0, w, h, d); addGeo(T, a, new T.ConeGeometry(Math.min(w, d) * 0.45, 1.6, 10), 0, h + 0.8, 0, pal.trim, 0); band(h * 0.5, w, d, 0.5); sign(0, h * 0.5, d / 2 + 0.05, w * 0.3, w * 0.3); return { y: 0, w: 0, d: 0 }; }\r\n  }\r\n}\r\n// recipe.roof crown, only where the massing leaves a clear top (top.y > 0)\r\nfunction roof(T, a, kind, top, pal) {\r\n  if (top.y <= 0 || !kind || kind === 'flat') return;\r\n  const s = Math.max(1, Math.min(top.w || 4, top.d || 4));\r\n  if (kind === 'cap') addGeo(T, a, new T.BoxGeometry(s * 1.05, 0.8, s * 1.05), 0, top.y + 0.4, 0, pal.trim, 0);\r\n  else if (kind === 'spire') addGeo(T, a, new T.ConeGeometry(s * 0.25, s * 1.4, 4), 0, top.y + s * 0.7, 0, pal.glow, 0);\r\n  else if (kind === 'ring') addGeo(T, a, new T.TorusGeometry(s * 0.6, 0.35, 3, 10).rotateX(Math.PI / 2), 0, top.y + 0.6, 0, pal.trim, 0);\r\n  else if (kind === 'dome') addGeo(T, a, new T.SphereGeometry(s * 0.4, 8, 4, 0, Math.PI * 2, 0, Math.PI / 2), 0, top.y, 0, pal.glow, 0);\r\n}\r\n\r\n// Standalone prototype geometry for one category (tests + dev page). Footprint w x d, height h, origin at ground centre.\r\nexport function protoGeometry(T, cat, { w = 24, d = 24, h = 30, recipes = RECIPES } = {}) {\r\n  const rc = recipes[cat] || recipes.other || {}, pal = { ...MOM_PALETTE, ...(rc.palette || {}) }, a = acc();\r\n  roof(T, a, rc.roof, mass(T, a, cat, w, d, h, pal, rc.glyph), pal);\r\n  return toGeometry(T, a);\r\n}\r\n\r\nconst PROP_GEO = {\r\n  antenna: T => new T.ConeGeometry(0.25, 5, 4).translate(0, 2.5, 0),\r\n  tank: T => new T.CylinderGeometry(1.1, 1.1, 2, 7).translate(0, 1, 0),\r\n  dish: T => new T.ConeGeometry(1.6, 0.9, 8).rotateX(Math.PI).translate(0, 1.2, 0),\r\n  stack: T => new T.CylinderGeometry(0.5, 0.8, 4, 6).translate(0, 2, 0),\r\n  ring: T => new T.TorusGeometry(1.8, 0.2, 3, 10).rotateX(Math.PI / 2).translate(0, 1, 0),\r\n  beacon: T => new T.OctahedronGeometry(0.9, 0).translate(0, 1.2, 0)\r\n};\r\nconst PROP_ALIAS = { vent: 'stack', spire: 'beacon' }; // mom-categories prop names -> shared prop meshes\r\nexport const PROP_NAMES = Object.keys(PROP_GEO);\r\nconst PROP_COL = { antenna: '#d9a441', tank: '#8a5cc0', dish: '#d9a441', stack: '#4a1f75', ring: '#b98cff', beacon: '#b98cff' };\r\n\r\n// cell: {buildings:[{id, x, z, w, d, h, cat, rot}]} in metres in cell space. 'house' is skipped: ruins-kit.mjs owns residential.\r\nexport function createMomKit(T, { cell, heightAt = () => 0, scale = 1, recipes = RECIPES, step = 3 } = {}) {\r\n  const group = new T.Group(); group.name = 'mom-kit';\r\n  const a = acc(), props = Object.fromEntries(PROP_NAMES.map(n => [n, []])), atlas = createMomAtlas(T);\r\n  let count = 0;\r\n  for (const b of cell.buildings || []) {\r\n    const cat = recipes[b.cat] ? b.cat : 'other';\r\n    if (cat === 'house') continue;\r\n    const rc = recipes[cat], pal = { ...MOM_PALETTE, ...(rc.palette || {}) }, st = rc.heightStep || step;\r\n    const w = Math.max(6, Math.min(60, b.w || 12)), d = Math.max(6, Math.min(60, b.d || 12)), h = Math.max(st, Math.round((b.h || 9) / st) * st);\r\n    const p = { ...pal, wall: shade(pal.wall, 0.9 + (hash(b.id ?? count) % 20) / 100) };\r\n    const sub = acc(), top = mass(T, sub, cat, w, d, h, p, rc.glyph); roof(T, sub, rc.roof, top, p);\r\n    const y = heightAt(b.x, b.z) || 0, c = Math.cos(b.rot || 0), s = Math.sin(b.rot || 0);\r\n    for (let i = 0; i < sub.pos.length; i += 3) {\r\n      const px = sub.pos[i], pz = sub.pos[i + 2], nx = sub.nor[i], nz = sub.nor[i + 2];\r\n      a.pos.push((b.x + px * c + pz * s) * scale, (y + sub.pos[i + 1]) * scale, (b.z - px * s + pz * c) * scale);\r\n      a.nor.push(nx * c + nz * s, sub.nor[i + 1], -nx * s + nz * c);\r\n    }\r\n    a.uv.push(...sub.uv); a.col.push(...sub.col);\r\n    (rc.props || []).map(n => PROP_ALIAS[n] || n).filter(n => PROP_GEO[n]).slice(0, 3).forEach((n, i) => { const ox = (i - 1) * Math.min(w, d) * 0.25; props[n].push([(b.x + ox * c) * scale, (y + (top.y || h)) * scale, (b.z - ox * s) * scale, b.rot || 0]); });\r\n    count++;\r\n  }\r\n  const mat = createPs1Material(T, { map: atlas, vertexColors: true }); mat.emissive = new T.Color('#2a1048');\r\n  const geos = [], mats = [mat];\r\n  if (a.pos.length) { const g = toGeometry(T, a); geos.push(g); const m = new T.Mesh(g, mat); m.name = 'mom-walls'; group.add(m); }\r\n  const q = new T.Matrix4(), e = new T.Euler();\r\n  for (const n of PROP_NAMES) {\r\n    const list = props[n]; if (!list.length) continue;\r\n    const g = PROP_GEO[n](T), pm = createPs1Material(T, { vertexColors: false }); pm.color = new T.Color(PROP_COL[n]); pm.emissive = new T.Color(PROP_COL[n]).multiplyScalar(0.25);\r\n    const im = new T.InstancedMesh(g, pm, list.length); im.name = 'mom-prop-' + n;\r\n    list.forEach((p, i) => { q.compose(new T.Vector3(p[0], p[1], p[2]), new T.Quaternion().setFromEuler(e.set(0, p[3], 0)), new T.Vector3(scale, scale, scale)); im.setMatrixAt(i, q); });\r\n    im.instanceMatrix.needsUpdate = true; geos.push(g); mats.push(pm); group.add(im);\r\n  }\r\n  group.traverse(o => { o.userData.layer = 'mom'; });\r\n  return { group, raidable: [], stats: () => ({ buildings: count, drawCalls: group.children.length, propMeshes: group.children.filter(c => c.isInstancedMesh).length }), dispose() { geos.forEach(g => g.dispose()); mats.forEach(m => m.dispose()); atlas.dispose(); } };\r\n}\r\n","modules/coach-world-rts/movement.mjs":"// W1 movement: one owner per unit (unit.mover 'codex'|'rts'), BFS + string-pull over occupancy U terrain.blocked.\r\nimport {findPath,occupancy} from '../coach-world/navigation.mjs';\r\nexport const SPEED=1.7,RADIUS=0.3,COLS=24;\r\nexport const combinedBlocked=(base,terrain)=>{const b=occupancy(base);for(const k of terrain?.blocked||[])b.add(k);return b;};\r\nexport const canClaim=(unit,base)=>unit.role==='worker'&&unit.mover!=='rts'&&!unit.away&&!base.rest&&!unit.hasOrder&&!unit.rally&&!unit.reservedBuilder;\r\nexport const claim=(unit,base)=>canClaim(unit,base)?(unit.mover='rts',true):false;\r\nexport function release(units,ids){for(const u of units)if(ids==='all'||ids.includes(u.id)||ids.has?.(u.id)){u.mover='codex';u.rtsPath=null;}}\r\n// distance from point to axis-aligned rect, and segment to rect\r\nconst pr=(px,py,x0,y0,x1,y1)=>Math.hypot(Math.max(x0-px,0,px-x1),Math.max(y0-py,0,py-y1));\r\nconst ps=(px,py,ax,ay,bx,by)=>{const dx=bx-ax,dy=by-ay,l=dx*dx+dy*dy,t=l?Math.max(0,Math.min(1,((px-ax)*dx+(py-ay)*dy)/l)):0;return Math.hypot(px-ax-t*dx,py-ay-t*dy);};\r\nfunction segRect(ax,ay,bx,by,x0,y0,x1,y1){ // exact min distance, 0 on intersection (Liang-Barsky)\r\n let t0=0,t1=1;const dx=bx-ax,dy=by-ay;for(const [p,q] of [[-dx,ax-x0],[dx,x1-ax],[-dy,ay-y0],[dy,y1-ay]]){if(p===0){if(q<0)return Math.min(pr(ax,ay,x0,y0,x1,y1),...[[x0,y0],[x1,y0],[x0,y1],[x1,y1]].map(c=>ps(c[0],c[1],ax,ay,bx,by)));continue;}const r=q/p;if(p<0){if(r>t1)return nearest();t0=Math.max(t0,r);}else{if(r<t0)return nearest();t1=Math.min(t1,r);}}\r\n return t0<=t1?0:nearest();\r\n function nearest(){return Math.min(pr(ax,ay,x0,y0,x1,y1),pr(bx,by,x0,y0,x1,y1),...[[x0,y0],[x1,y0],[x0,y1],[x1,y1]].map(c=>ps(c[0],c[1],ax,ay,bx,by)));}\r\n}\r\n// Points are tile coords as units stand on them (cell i centre = i+.5). Rejects any segment closer than RADIUS to a blocked/out-of-grid cell: covers supercover + corner cutting.\r\nexport function clear(blocked,a,b,rows=COLS,cols=COLS,r=RADIUS){\r\n const ax=a.x+.5,ay=a.y+.5,bx=b.x+.5,by=b.y+.5;\r\n for(let y=Math.max(0,Math.floor(Math.min(ay,by)-r)),y2=Math.min(rows-1,Math.floor(Math.max(ay,by)+r));y<=y2;y++)for(let x=Math.max(0,Math.floor(Math.min(ax,bx)-r)),x2=Math.min(cols-1,Math.floor(Math.max(ax,bx)+r));x<=x2;x++)\r\n  if(blocked.has(y*cols+x)&&segRect(ax,ay,bx,by,x,y,x+1,y+1)<r)return false;\r\n return true;\r\n}\r\nexport function stringPull(blocked,start,path,cols=COLS,rows=COLS){\r\n const out=[];let at=start,i=0;\r\n while(i<path.length){let j=path.length-1;while(j>i&&!clear(blocked,at,path[j],rows,cols))j--;out.push(path[j]);at=path[j];i=j+1;}\r\n return out;\r\n}\r\nexport function planPath(blocked,start,target,cols=COLS,rows=COLS){\r\n const s={x:Math.floor(start.x),y:Math.floor(start.y)},p=findPath(cols,rows,blocked,s,target);\r\n return p?stringPull(blocked,s,p,cols,rows):null;\r\n}\r\n// Advance an rts-owned unit along unit.rtsPath; returns tiles moved. Never touches a unit it does not own.\r\nexport function stepOwned(unit,dt){\r\n if(unit.mover!=='rts'||!unit.rtsPath?.length)return 0;\r\n let left=SPEED*dt,moved=0;\r\n while(left>0&&unit.rtsPath.length){const p=unit.rtsPath[0],dx=p.x-unit.x,dy=p.y-unit.y,d=Math.hypot(dx,dy);\r\n  if(d<=left){unit.x=p.x;unit.y=p.y;left-=d;moved+=d;unit.rtsPath.shift();}else{unit.x+=dx/d*left;unit.y+=dy/d*left;moved+=left;left=0;}}\r\n return moved;\r\n}\r\n// Air units (ships) fly straight over buildings and rocks: no BFS, one waypoint.\r\nexport function order(unit,blocked,target){if(unit.air){if(!target||!Number.isFinite(target.x)||!Number.isFinite(target.y))return false;unit.rtsPath=[{x:target.x,y:target.y}];return true;}if(unit.mover!=='rts')return false;const p=planPath(blocked,unit,target);if(!p)return false;unit.rtsPath=p;return true;}\r\n","modules/coach-world-rts/npc-achievement-rules.js":"var za=Object.defineProperty;var Ua=(e,a)=>()=>(e&&(a=e(e=0)),a);var ze=(e,a)=>{for(var t in a)za(e,t,{get:a[t],enumerable:!0})};function E(e=globalThis.localStorage){try{let a=JSON.parse(e?.getItem(xe)||\"null\");return He(a)?a:null}catch{return null}}function f(e,a=globalThis.localStorage){let t=Y??E(a);return t?.gameGeneration>1?`${e}/game/${t.gameGeneration}`:e}function Ja(e=globalThis.localStorage){if(!e)return;let a=Array.from({length:e.length},(t,r)=>e.key(r));for(let t of a)/^(?:myr5-roguelite-preview-v1\\/|myr5-world-run-outbox-v1\\/)/.test(t)&&e.removeItem(t)}function Qa(e,{storage:a=globalThis.localStorage}={}){if(!He(e))throw we();let t=Y??E(a);if(t&&e.gameGeneration<t.gameGeneration)throw we(t);let r={gameGeneration:e.gameGeneration,gameResetAt:e.gameResetAt};if(t&&t.gameGeneration===r.gameGeneration&&t.gameResetAt!==r.gameResetAt)throw we(t);if(Y=r,(!t||t.gameGeneration!==r.gameGeneration)&&(r.gameResetAt>0&&Ja(a),a?.setItem(xe,JSON.stringify(r)),t)){for(let s of Va)s(r);globalThis.dispatchEvent?.(new CustomEvent(\"myr5:game-generation-changed\",{detail:r}))}return r}function A(e=globalThis.myr5AuthenticatedAccount){let a=Y??E();return!a?.gameResetAt||!e?.user?.id||e.gameGeneration===a.gameGeneration}var xe,Y,Va,He,we,V=Ua(()=>{xe=\"myr5-game-generation-v1\",Y=null,Va=new Set,He=e=>Number.isSafeInteger(e?.gameGeneration)&&e.gameGeneration>=1&&Number.isSafeInteger(e?.gameResetAt)&&e.gameResetAt>=0,we=e=>Object.assign(Error(\"Game progress changed. Reopen the game.\"),{code:\"game_generation_mismatch\",status:409,...e});typeof globalThis.window==\"object\"&&typeof globalThis.addEventListener==\"function\"&&globalThis.addEventListener(\"storage\",e=>{if(!(e.key!==xe||!e.newValue))try{Qa(JSON.parse(e.newValue))}catch{}})});var Ue=Object.freeze({jogging:{name:\"Subway Burpees\",types:[\"cardio\",\"legs\"]},\"plank-flight\":{name:\"Jet Plank Joyride\",types:[\"core\",\"balance\"]},cover:{name:\"Crouching Crisis\",types:[\"legs\",\"boxing\"]},dance:{name:\"Dance, Dance, and Obey\",types:[\"cardio\",\"martial-arts\"]}}),v=[{id:\"chest\",name:\"Chest\",description:\"Push-up progressions\"},{id:\"legs\",name:\"Legs\",description:\"Squats and split squats\"},{id:\"hips\",name:\"Hips & glutes\",description:\"Hinges and bridges\"},{id:\"core\",name:\"Core\",description:\"Plank holds\"},{id:\"shoulders\",name:\"Shoulders\",description:\"Controlled arm movement\"},{id:\"balance\",name:\"Balance\",description:\"Single-leg yoga holds\"},{id:\"yoga\",name:\"Yoga\",description:\"Standing poses\"},{id:\"stances\",name:\"Martial stances\",description:\"Controlled stance holds\"},{id:\"boxing\",name:\"Boxing\",description:\"Slow shadowboxing\"},{id:\"cardio\",name:\"Cardio\",description:\"Marches and jacks\"}],G=[];function b(e,a,t,r,s={}){let c=s.kind??([\"plank\",\"sideplank\",\"balance\",\"yoga\",\"stance\"].includes(r)?\"hold\":r===\"boxing\"?\"pace\":r===\"march\"?\"steps\":\"reps\"),o=s.view??([\"pushup\",\"hinge\",\"bridge\",\"plank\",\"split\"].includes(r)?\"side\":\"front\"),n=c===\"hold\"?\"Pose hold\":c===\"pace\"?\"Active hand time\":c===\"steps\"?\"Knee lifts\":\"Repetitions\",d=s.limits??\"Counts the visible movement; does not grade technique.\",i=o===\"side\"?\"Camera beside you.\":o===\"angle\"?\"Camera at a slight angle.\":\"Face the camera.\";G.push({id:a,group:e,name:t,detector:r,kind:c,view:o,measurement:n,limits:d,difficulty:G.filter(l=>l.group===e).length+1,defaultGoal:c===\"hold\"||c===\"pace\"?15:5,evidence:\"rule-based; camera validation pending\",source:\"mediapipe\",...s,hint:`${i} ${s.cue??\"Keep the joints used for this movement visible.\"}`})}var $a=\"Counts elbow bend and return. Hand shape, support stability and chest depth are not verified.\";for(let[e,a,t]of[[\"knee-pushup\",\"Knee push-up\",{support:\"knees\",cue:\"Knees down; show shoulder, elbow, wrist and hip.\"}],[\"high-incline-pushup\",\"High incline push-up\",{incline:!0,cue:\"Hands on a stable high surface; show your full arm and torso.\"}],[\"low-incline-pushup\",\"Low incline push-up\",{incline:!0,cue:\"Hands on a stable lower surface; show your full arm and torso.\"}],[\"pushup\",\"Regular push-up\",{}],[\"wide-pushup\",\"Wide push-up\",{wide:!0}],[\"slow-pushup\",\"Slow push-up\",{minCycle:1.2}],[\"diamond-pushup\",\"Diamond push-up\",{narrow:!0}],[\"decline-pushup\",\"Feet-elevated push-up\",{decline:!0,cue:\"Feet on a stable low surface; show your full arm and torso.\"}]])b(\"chest\",e,a,\"pushup\",{cue:\"Show shoulder, elbow, wrist and hip. Start with your arm extended.\",limits:$a,...t});for(let[e,a,t]of[[\"shallow-squat\",\"Shallow squat\",{travel:.18}],[\"squat\",\"Bodyweight squat\",{}],[\"wide-squat\",\"Wide squat\",{wide:!0}],[\"pause-squat\",\"Pause squat\",{dwell:.8}],[\"slow-squat\",\"Slow squat\",{minCycle:1.2}],[\"split-left\",\"Split squat \\xB7 left\",{detector:\"split\",side:\"left\"}],[\"split-right\",\"Split squat \\xB7 right\",{detector:\"split\",side:\"right\"}],[\"wall-sit\",\"Wall sit\",{detector:\"yoga\",pose:\"wall-sit\",kind:\"hold\",view:\"side\",cue:\"Back on a wall, thighs level. Show shoulder, hip and knee.\",limits:\"Times the hip angle and thigh level. Wall contact and knee position are not tracked.\"}]])b(\"legs\",e,a,t.detector??\"squat\",{cue:t.side?\"Show the front leg from hip to knee. Start tall.\":\"Show shoulders and hips. Start standing tall.\",...t});for(let[e,a,t,r]of[[\"small-hinge\",\"Small hip hinge\",\"hinge\",{bend:25}],[\"hip-hinge\",\"Hip hinge\",\"hinge\",{}],[\"good-morning\",\"Bodyweight good morning\",\"hinge\",{arms:\"chest\"}],[\"glute-bridge\",\"Glute bridge\",\"bridge\",{}],[\"pause-bridge\",\"Pause glute bridge\",\"bridge\",{dwell:.8}]])b(\"hips\",e,a,t,{cue:t===\"bridge\"?\"Lie on your back, knees bent. Show shoulder, hip and knee. Start with hips down.\":\"Show shoulder, hip and knee. Start upright; hinge slowly.\",limits:\"Tracks hip movement. Back curvature and muscle engagement are not verified.\",...r});for(let[e,a,t]of[[\"knee-plank\",\"Knee plank\",{support:\"knees\"}],[\"high-plank\",\"High plank\",{}],[\"forearm-plank\",\"Forearm plank\",{forearm:!0}],[\"side-knee-left\",\"Side knee plank \\xB7 left\",{side:\"left\",support:\"knees\"}],[\"side-knee-right\",\"Side knee plank \\xB7 right\",{side:\"right\",support:\"knees\"}],[\"side-plank-left\",\"Side plank \\xB7 left\",{side:\"left\"}],[\"side-plank-right\",\"Side plank \\xB7 right\",{side:\"right\"}]])b(\"core\",e,a,t.side?\"sideplank\":\"plank\",{view:t.side?\"front\":\"side\",cue:t.side?\"Show both shoulders, hips and the supporting knee. Stack your shoulders.\":\"Show shoulder, elbow, wrist, hip and supporting knee.\",limits:\"Times the shoulder\\u2013hip\\u2013knee line. Feet, floor contact and loading are not tracked.\",...t});for(let[e,a,t,r]of[[\"front-raise\",\"Front arm raise\",\"raise\",{view:\"side\",plane:\"front\"}],[\"lateral-raise\",\"Lateral arm raise\",\"raise\",{}],[\"overhead-reach\",\"Overhead reach\",\"raise\",{overhead:!0}],[\"standing-press\",\"Standing arm press\",\"press\",{}],[\"slow-press\",\"Slow arm press\",\"press\",{minCycle:1.2}]])b(\"shoulders\",e,a,t,{cue:\"Keep both shoulders, elbows, wrists and hips visible. Use controlled, unloaded movement.\",...r});for(let[e,a,t]of[[\"knee-balance\",\"Knee-lift balance\",{kneeLift:!0}],[\"low-tree\",\"Low tree pose\",{low:!0}],[\"tree\",\"Tree pose\",{}],[\"overhead-tree\",\"Tree \\xB7 arms overhead\",{overhead:!0}]])b(\"balance\",e,a,\"balance\",{cue:\"Show both hips and knees. Either leg works; keep a support nearby if needed.\",limits:\"Times the visible leg shape. Balance, foot pressure and support use are not verified.\",...t});for(let[e,a,t]of[[\"mountain\",\"Mountain\",\"mountain\"],[\"salute\",\"Upward salute\",\"salute\"],[\"chair\",\"Chair pose\",\"chair\"],[\"warrior-one\",\"Warrior I\",\"warrior-one\"],[\"warrior\",\"Warrior II\",\"warrior\"],[\"goddess\",\"Goddess pose\",\"goddess\"]])b(\"yoga\",e,a,\"yoga\",{pose:t,view:[\"chair\",\"warrior-one\"].includes(t)?\"side\":\"front\",cue:\"Show arms, hips and knees. Hold only a comfortable position.\",limits:\"Times broad pose shape. Foot rotation and joint alignment are not graded.\"});for(let[e,a,t]of[[\"high-horse\",\"High horse stance\",{high:!0}],[\"horse\",\"Horse stance\",{}],[\"low-horse\",\"Lower horse stance\",{low:!0}],[\"front-stance-left\",\"Front stance \\xB7 left\",{side:\"left\"}],[\"front-stance-right\",\"Front stance \\xB7 right\",{side:\"right\"}]])b(\"stances\",e,a,\"stance\",{view:t.side?\"side\":\"front\",cue:\"Keep hips and knees visible. Move slowly into a comfortable stance.\",limits:\"Times stance shape only. This is not martial-arts technique instruction.\",...t});for(let[e,a,t]of[[\"jab-left\",\"Left straight practice\",{side:\"left\"}],[\"jab-right\",\"Right straight practice\",{side:\"right\"}],[\"boxing\",\"Alternating straights\",{}],[\"double-jab\",\"Double-jab practice\",{double:!0}]])b(\"boxing\",e,a,\"boxing\",{view:\"angle\",cue:\"Show shoulders, elbows, wrists and hips. Practice slow, controlled punches into empty space.\",limits:\"Tracks active hand time and relative pace. Punch type, power, accuracy and guard are not verified.\",...t});for(let[e,a,t,r]of[[\"march\",\"Easy march\",\"march\",{}],[\"high-march\",\"High-knee march\",\"march\",{lift:.4}],[\"jogging\",Ue.jogging.name,\"march\",{lift:.08,runner:!0,endless:!0,cue:\"The route moves automatically. Crouch deeply to duck, raise both arms to jump, and lean left or right to change lanes. Keep your upper body visible.\"}],[\"step-jack\",\"Step jack\",\"jack\",{step:!0}],[\"jumping-jack\",\"Jumping jack\",\"jack\",{}]])b(\"cardio\",e,a,t,{cue:t===\"jack\"?\"Show your upper body and knees. Start with arms down and knees close together.\":\"Show shoulders, hips and both knees. Start standing tall.\",limits:t===\"jack\"?\"Counts arm-and-knee opening cycles. Feet, airtime and landings are not tracked.\":\"Counts knee lifts. Footfalls and impact are not verified.\",...r});b(\"core\",\"plank-flight\",Ue[\"plank-flight\"].name,\"plank-flight\",{kind:\"hold\",view:\"front\",flight:!0,standalone:!0,endless:!0,cue:\"Phone faces you from a low stable stand. Keep both hands down, shoulders, arms, hips and knees visible. Rise/lower comfortably; lean left/right to fly.\",limits:\"Estimates face-on height and lean. Floor contact, arm loading and technique are not measured.\"});var m=Object.freeze(Object.fromEntries(G.map(e=>[e.id,Object.freeze(e)]))),he=Object.freeze(Object.fromEntries(v.map(e=>[e.id,Object.freeze(G.filter(a=>a.group===e.id&&!a.standalone))]))),Vt=G.length;var S=Object.freeze([\"roster/21-flyer--winged_humanoid_3d_model\",\"roster/18-quad-all--wooden_robot_3d_model\"]),qa=[[\"myr5\",\"original\",[\"MYR5 Jacked LaLAN\",\"Jack LaLanne\",\"jacked strength\",\"local area network\"],[\"MYR5 AT-Lift Prime\",\"Charles Atlas\",\"lift\",\"model designation\"]],[\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"arms\",[\"Arnoid Press\",\"Arnold Schwarzenegger\",\"Arnold press\",\"android\"],[\"ScotTorque Curl\",\"Larry Scott\",\"curl\",\"motor torque\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"arms\",[\"Ferri-Delt L0U\",\"Lou Ferrigno\",\"delt\",\"robot designation\"],[\"R0N Curlman\",\"Ronnie Coleman\",\"curl\",\"engine manifold\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model\",\"arms\",[\"Frank Delt-Zan3\",\"Frank Zane\",\"delt\",\"magnetic coil\"],[\"Cutlerator Curl\",\"Jay Cutler\",\"curl\",\"accelerator\"]],[\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"arms\",[\"KAI-nematic Raise\",\"Kai Greene\",\"raise\",\"kinematic mechanism\"],[\"Philament Heave Press\",\"Phil Heath\",\"press\",\"electrical filament\"]],[\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"arms\",[\"Wheel-er Servo-Shrug\",\"Flex Wheeler\",\"shrug\",\"servo wheel\"]],[\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"arms\",[\"Ree-volts Raise\",\"Steve Reeves\",\"raise\",\"voltage\"]],[\"08-shard-asym--robot_3d_model4\",\"arms\",[\"S3RG Oliv-Armature\",\"Sergio Oliva\",\"arm\",\"motor armature\"]],[\"17-shellcap-manyarm--3d_character_figurine\",\"arms\",[\"Delt-alinn Bailey\",\"Dana Linn Bailey\",\"delt\",\"Delta designation\"]],[\"06-ridge-triad--geometric_robot_3d_model1\",\"chest\",[\"Oliva-C Pec\",\"Sergio Oliva\",\"pec\",\"vacuum drive\"]],[\"08-shard-asym--geometric_robot_3d_model\",\"chest\",[\"Pec-n0ld Reactor\",\"Arnold Schwarzenegger\",\"pec\",\"reactor\"]],[\"16-spade-arch--pyramid_head_figure_3d_model\",\"chest\",[\"Franx Pec-Zane\",\"Frank Zane\",\"pec\",\"motor drive\"],[\"Y4TES Yoke\",\"Dorian Yates\",\"press\",\"powered yoke\"]],[\"04-crest-wedge--robot_creature_3d_model\",\"chest\",[\"L0U Ferri-pecctor\",\"Lou Ferrigno\",\"pec\",\"reactor\"],[\"Wheeler Wheel-Bench\",\"Flex Wheeler\",\"bench\",\"drive wheel\"]],[\"16-spade-arch--stylized_humanoid_3d_model\",\"chest\",[\"Cutlerator Press\",\"Jay Cutler\",\"press\",\"accelerator\"]],[\"04-crest-wedge--stylized_3d_character\",\"chest\",[\"Gr33ne Grid-Press\",\"Kai Greene\",\"press\",\"power grid designation\"]],[\"09-monolith-tanka--3d_robot_model\",\"chest\",[\"chris BenchSTEAD\",\"Chris Bumstead\",\"bench\",\"robot\"]],[\"09-monolith-tanka--white_horned_robot_3d_model\",\"chest\",[\"Cavalier-EX Chest\",\"Jeff Cavaliere\",\"chest\",\"exercise model EX\"]],[\"15-orbital-coili--robot_character_3d_model\",\"quads\",[\"t-OHM Platzma\",\"Tom Platz\",\"squat\",\"plasma\"]],[\"13-fan-split--stylized_3d_character2\",\"quads\",[\"Quad-Cutler LEGS THO\",\"Jay Cutler\",\"quad\",\"circuit\"],[\"BumSTEADy Squat\",\"Chris Bumstead\",\"squat\",\"steady-state control\"]],[\"22-curve--stylized_cartoon_figure_3d_model\",\"quads\",[\"Kai Gr33ne Lunge\",\"Kai Greene\",\"lunge\",\"gear designation\"]],[\"02-taper-tallstalk--3d_humanoid_figure\",\"quads\",[\"Hane-Yoke Lunge\",\"Lee Haney\",\"lunge\",\"powered yoke\"],[\"Stern-on Squat\",\"Erin Stern\",\"squat\",\"power-on switch\"]],[\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"quads\",[\"Oli-Volt Lunge\",\"Sergio Oliva\",\"lunge\",\"voltage\"],[\"Y4TES Yoke-Squat\",\"Dorian Yates\",\"squat\",\"robot designation\"]],[\"18-quad-all--wooden_four-legged_robot_3d_model\",\"quads\",[\"Priest-L33 Squat\",\"Lee Priest\",\"squat\",\"piston\"]],[\"18-quad-all--stylized_quadruped_3d_model\",\"quads\",[\"Quadzilla Platz\",\"Tom Platz\",\"quads\",\"kaiju creature\"],[\"SternShift Squat\",\"Erin Stern\",\"squat\",\"gear shift\"]],[\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"glutes\",[\"Con-Thrust-Era\",\"Bret Contreras\",\"hip thrust\",\"thruster\"]],[\"23-blob-texture-bodies--sand_creature_3d_model\",\"glutes\",[\"Tingularity Thrust\",\"Chloe Ting\",\"hip thrust\",\"singularity\"]],[\"03-pearl-orb-ring--stylized_3d_character1\",\"glutes\",[\"Gyrvanium Glute-Drive\",\"Caroline Girvan\",\"glute\",\"futuristic alloy\"],[\"Jill-iON Hinge-Flex\",\"Jillian Michaels\",\"hip hinge\",\"ion drive\"]],[\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"glutes\",[\"Trac-ION Hip-Thrust\",\"Tracy Anderson\",\"hip thrust\",\"ion drive\"],[\"Simmonics Bridge-Servo\",\"Whitney Simmons\",\"bridge\",\"electronics servo\"]],[\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"glutes\",[\"Robo-ertson Bridge\",\"Heather Robertson\",\"bridge\",\"robot\"],[\"Hearn-ess ExoHinge\",\"Katy Hearn\",\"hinge\",\"exosuit harness\"]],[\"03-pearl-orb-ring--circle_center\",\"glutes\",[\"Con-Terra Coil-Bridge\",\"Bret Contreras\",\"bridge\",\"planetary coil\"],[\"Cyb-Ho Holo-Bridge\",\"Cassey Ho\",\"bridge\",\"cyborg hologram\"]],[\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"glutes\",[\"Chlo-Turbo Thrust\",\"Chloe Ting\",\"thrust\",\"turbine\"]],[\"10-petal-wisp--fantasy_creature_3d_model4\",\"yoga\",[\"Adriene A-Sana Mk II\",\"Adriene Mishler\",\"asana\",\"model revision\"]],[\"19-genie-multi--fantasy_creature_3d_model\",\"yoga\",[\"Mat-Gregor Matrix\",\"Kino MacGregor\",\"mat\",\"simulation matrix\"]],[\"10-petal-wisp--ghost_character_3d_model\",\"yoga\",[\"Warri0r-ner Drive\",\"Dylan Werner\",\"warrior pose\",\"motor designation\"],[\"Kathryn Bend-1g\",\"Kathryn Budig\",\"bend\",\"robot\"]],[\"08-shard-asym--stylized_action_figure_3d_model\",\"yoga\",[\"Yee-lastic Flow\",\"Rodney Yee\",\"yoga flow\",\"elastic mechanics\"]],[\"22-curve--stylized_alien_3d_model\",\"yoga\",[\"Strom Flex Current\",\"Max Strom\",\"stretch\",\"electric current\"]],[\"10-petal-wisp--stylized_creature_3d_model\",\"yoga\",[\"Iyeng-Arc Inversion\",\"B. K. S. Iyengar\",\"inversion\",\"powered arc\"]],[\"19-genie-multi--stylized_octopus_3d_model\",\"yoga\",[\"Yee Gyro-Yogi\",\"Rodney Yee\",\"yoga\",\"gyroscope\"]],[\"01-seed-pearo--cute_alien_figure_3d_model\",\"yoga\",[\"Wern-Axis Flow\",\"Dylan Werner\",\"flow\",\"robotic axis\"],[\"Mat-Gregor Drive\",\"Kino MacGregor\",\"mat practice\",\"motor drive\"]],[\"01-seed-pearo--white_3d_character_model\",\"yoga\",[\"Budig-ital Balance\",\"Kathryn Budig\",\"balance\",\"digital\"],[\"Corn Kernel Cobra\",\"Seane Corn\",\"cobra pose\",\"computing kernel\"]],[\"09-monolith-tanka--boxy_humanoid_3d_model\",\"martial-arts\",[\"Bruce L3300\",\"Bruce Lee\",\"kick\",\"alphanumeric call sign\"],[\"GSP-Drive\",\"Georges St-Pierre\",\"spar\",\"motor drive\"]],[\"11-anvil-cask--clay-style_robot_3d_model\",\"martial-arts\",[\"Anderson Sil-VOLT\",\"Anderson Silva\",\"strike\",\"voltage\"],[\"r0nda Rous-Bot\",\"Ronda Rousey\",\"armbar\",\"robot\"]],[\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"martial-arts\",[\"C0N0R McG-Gear\",\"Conor McGregor\",\"kick\",\"gear transmission\"],[\"Nor-Rotor Punch\",\"Chuck Norris\",\"punch\",\"rotor\"]],[\"14-chisel-spire--cone_head_3d_model\",\"martial-arts\",[\"LiDAR Jett-Jab\",\"Jet Li\",\"jab\",\"laser ranging\"],[\"Jaa-Bot Jab\",\"Tony Jaa\",\"jab\",\"robot\"]],[\"08-shard-asym--fantasy_creature_3d_model2\",\"martial-arts\",[\"Waters0nic\",\"Michelle Waterson\",\"kick\",\"sonic drive\"],[\"Amanda Nu-NEURAL\",\"Amanda Nunes\",\"strike\",\"neural implant\"]],[\"09-monolith-tanka--mini_robot_3d_model\",\"martial-arts\",[\"Machi-Droid-a\",\"Lyoto Machida\",\"chop\",\"android\"],[\"Bas Rut-TORque\",\"Bas Rutten\",\"kick\",\"motor torque\"]],[\"09-monolith-tanka--robot_3d_model3\",\"martial-arts\",[\"Israel Ade-SYNC-ya\",\"Israel Adesanya\",\"strike\",\"synchronization\"],[\"Shev-Servo Sweep\",\"Valentina Shevchenko\",\"sweep\",\"servo\"]],[\"13-fan-split--stylized_toy_3d_model\",\"martial-arts\",[\"BlankSaber Kick\",\"Billy Blanks\",\"kick\",\"energy saber\"],[\"Wilsonic Kick\",\"Don Wilson\",\"kick\",\"sonic drive\"]],[\"09-monolith-tanka--robot_3d_model2\",\"martial-arts\",[\"Rous-Torque Throw\",\"Ronda Rousey\",\"throw\",\"motor torque\"],[\"Jackie Chain-Kick\",\"Jackie Chan\",\"kick\",\"chain drive\"]],[\"07-bulb-sphereling--humanoid_robot_3d_model\",\"cardio\",[\"B0LT Sprint-Drive\",\"Usain Bolt\",\"sprint\",\"electrical bolt drive\"]],[\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"cardio\",[\"Fon-Dynamo Footwork\",\"Jane Fonda\",\"footwork\",\"dynamo\"],[\"Simmonics Sweatstream\",\"Richard Simmons\",\"aerobic sweat\",\"electronics stream\"]],[\"06-ridge-triad--robot_3d_model1\",\"cardio\",[\"M0 Farah Fastlane\",\"Mo Farah\",\"fast run\",\"electric capacitance\"]],[\"03-pearl-orb-ring--robot_3d_model\",\"cardio\",[\"Feli-X Footdrive\",\"Allyson Felix\",\"footwork\",\"X-model drive\"],[\"Rad-Clutch Run\",\"Paula Radcliffe\",\"run\",\"motor clutch\"]],[\"20-lume--robotic_figure_3d_model\",\"cardio\",[\"Pre-Font-AI-ne Pace\",\"Steve Prefontaine\",\"pace\",\"AI running model\"],[\"Ohm-wens Run\",\"Jesse Owens\",\"run\",\"electrical resistance\"]],[\"07-bulb-sphereling--cute_robot_3d_model\",\"cardio\",[\"Billy Blanx Beam\",\"Billy Blanks\",\"boot camp\",\"waveform\"],[\"GebreShell Runloop\",\"Haile Gebrselassie\",\"run\",\"software shell loop\"]],[\"01-seed-pearo--blank_toy_figure_3d_model\",\"cardio\",[\"Phelpeller Pulse\",\"Michael Phelps\",\"cardio pulse\",\"propeller\"],[\"Fon-Droid Footwork\",\"Jane Fonda\",\"footwork\",\"android\"]],[\"18-quad-all--dragon_creature_3d_model\",\"meditation\",[\"H0F-Lung\",\"Wim Hof\",\"breathing\",\"oxygen-code lung\"]],[\"18-quad-all--fantasy_creature_3d_model1\",\"meditation\",[\"Max Ohm-Strom Breath\",\"Max Strom\",\"breathing\",\"electrical resistance\"],[\"Yee-uro Zen\",\"Rodney Yee\",\"mindful movement\",\"neural interface\"]],[\"18-quad-all--fantasy_creature_3d_model3\",\"meditation\",[\"Shiva Rea-spatory\",\"Shiva Rea\",\"respiration\",\"airlock\"]],[\"18-quad-all--four-legged_robot_3d_model\",\"meditation\",[\"Tara-5tile5-Standby\",\"Tara Stiles\",\"breathing\",\"computer standby\"]],[\"18-quad-all--quadruped_robot_3d_model1\",\"meditation\",[\"Werner Wind0wn\",\"Dylan Werner\",\"wind-down practice\",\"alphanumeric standby mode\"]],[\"18-quad-all--quadruped_robot_3d_model\",\"meditation\",[\"Yee Holo-Om\",\"Rodney Yee\",\"mindful movement\",\"hologram\"]],[\"18-quad-all--robotic_dog_3d_model\",\"meditation\",[\"Budig-ital Breath\",\"Kathryn Budig\",\"breathing\",\"digital\"]],[\"12-seedpod-snailslug--stylized_slug_3d_model\",\"meditation\",[\"H0F Hold-Circuit\",\"Wim Hof\",\"breath hold\",\"circuit and model code\"],[\"Ohm-Strom Still\",\"Max Strom\",\"stillness\",\"electrical resistance\"]],[\"12-seedpod-snailslug--stylized_worm_3d_model\",\"meditation\",[\"Pilat-OS Calm-Circuit\",\"Joseph Pilates\",\"mindful breathing\",\"operating system circuit\"]],[\"14-chisel-spire--low_poly_robot_3d_model\",\"meditation\",[\"Rea-Spire Relay\",\"Shiva Rea\",\"respiration\",\"air relay\"]],[\"01-seed-pearo--3d_character_model\",\"meditation\",[\"McKe-O2 Nose-Cone\",\"Patrick McKeown\",\"nasal breathing\",\"rocket nose cone\"],[\"Vertu-al Vent\",\"Shona Vertue\",\"ventilation\",\"virtual reality\"]],[\"23-blob-texture-bodies--blob_creature_3d_model\",\"meditation\",[\"Blob 1 \\xB7 H0F Hold-Drive\",\"Wim Hof\",\"breath hold\",\"air drive and model code\"]],[\"01-seed-pearo--blank_humanoid_figure_3d_model\",\"meditation\",[\"Corn Kernel Om\",\"Seane Corn\",\"meditation\",\"computing kernel\"],[\"Kino-Kinetic Breath\",\"Kino MacGregor\",\"breathing\",\"kinematics\"]]],$e=Object.freeze(Object.fromEntries(qa.map(([e,a,...t])=>[e===\"myr5\"?e:`roster/${e}`,Object.freeze({track:a,names:Object.freeze(t.map(([r,s,c,o])=>Object.freeze({name:r,person:s,workout:c,scifi:o})))})])));var Qt=Object.freeze(Object.fromEntries(Object.entries($e).map(([e,a])=>[e,Object.freeze(a.names.map(t=>t.name))])));function ja(e){let a=$e[e];if(!a)throw RangeError(`No coach name for ${e}.`);return a}var pe=(e,a=!1)=>{let t=ja(e).names;return t[a?1:0]?.name??t[0].name};var x=Object.freeze({sprintRounds:5,gentleActiveSeconds:300,paceActiveSeconds:300,meditationDays:Object.freeze([3,6,9,12])}),Ha=[{id:\"myr5\",sourceLabel:\"Original MYR5\"},{id:\"roster/01-seed-pearo--3d_character_model\",sourceLabel:\"Seed \\xB7 Pearo 1\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 2\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 5\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",sourceLabel:\"Taper \\xB7 Tallstalk 1\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",sourceLabel:\"Taper \\xB7 Tallstalk 2\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",sourceLabel:\"Taper \\xB7 Tallstalk 3\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 1\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--circle_center\",sourceLabel:\"Pearl \\xB7 Orb 2\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 3\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 4\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",sourceLabel:\"Pearl \\xB7 Orb 5\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 6\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",sourceLabel:\"Crest \\xB7 Wedge 1\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",sourceLabel:\"Crest \\xB7 Wedge 2\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",sourceLabel:\"Slope \\xB7 Bobble 1\",group:\"Slope \\xB7 Bobble\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 1\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 2\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 1\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 2\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",sourceLabel:\"Shard \\xB7 Asym 1\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--robot_3d_model4\",sourceLabel:\"Shard \\xB7 Asym 2\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",sourceLabel:\"Shard \\xB7 Asym 3\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",sourceLabel:\"Shard \\xB7 Asym 4\",group:\"Shard \\xB7 Asym\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",sourceLabel:\"Monolith \\xB7 Tanka 1\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 2\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 3\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",sourceLabel:\"Monolith \\xB7 Tanka 5\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 6\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 1\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 2\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",sourceLabel:\"Petal \\xB7 Wisp 3\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",sourceLabel:\"Anvil \\xB7 Cask 1\",group:\"Anvil \\xB7 Cask\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 1\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 2\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/13-fan-split--stylized_3d_character2\",sourceLabel:\"Fan \\xB7 Split 1\",group:\"Fan \\xB7 Split\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",sourceLabel:\"Fan \\xB7 Split 2\",group:\"Fan \\xB7 Split\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 1\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 2\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",sourceLabel:\"Orbital \\xB7 Coili 1\",group:\"Orbital \\xB7 Coili\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",sourceLabel:\"Spade \\xB7 Arch 1\",group:\"Spade \\xB7 Arch\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",sourceLabel:\"Spade \\xB7 Arch 2\",group:\"Spade \\xB7 Arch\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",sourceLabel:\"Shellcap \\xB7 Manyarm 1\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 2\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 3\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",sourceLabel:\"Four-legged 1\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",sourceLabel:\"Four-legged 2\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",sourceLabel:\"Four-legged 3\",group:\"Four-legged\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",sourceLabel:\"Four-legged 4\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",sourceLabel:\"Four-legged 5\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",sourceLabel:\"Four-legged 6\",group:\"Four-legged\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",sourceLabel:\"Four-legged 7\",group:\"Four-legged\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",sourceLabel:\"Four-legged 9\",group:\"Four-legged\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",sourceLabel:\"Genie \\xB7 Multi 1\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",sourceLabel:\"Genie \\xB7 Multi 2\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",sourceLabel:\"Genie \\xB7 Multi 3\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",sourceLabel:\"Genie \\xB7 Multi 4\",group:\"Genie \\xB7 Multi\"},{id:\"roster/20-lume--robotic_figure_3d_model\",sourceLabel:\"Lume 1\",group:\"Lume\"},{id:\"roster/22-curve--stylized_alien_3d_model\",sourceLabel:\"Curve 1\",group:\"Curve\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",sourceLabel:\"Curve 2\",group:\"Curve\"},{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",sourceLabel:\"Blob 1\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",sourceLabel:\"Blob 2\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",sourceLabel:\"Blob 3\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",sourceLabel:\"Blob 4\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",sourceLabel:\"Blob 5\",group:\"Blob\"}],C=Ha.map(e=>({...e,label:pe(e.id)})),Ka=[{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/20-lume--robotic_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"expert\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/08-shard-asym--robot_3d_model4\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",tracks:[\"chest\",\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",tracks:[\"chest\"],difficulty:\"expert\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",tracks:[\"glutes\"],difficulty:\"hard\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",tracks:[\"glutes\"],difficulty:\"expert\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",tracks:[\"quads\"],difficulty:\"easy\"},{id:\"roster/13-fan-split--stylized_3d_character2\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_alien_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",tracks:[\"yoga\"],difficulty:\"expert\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/01-seed-pearo--3d_character_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",tracks:[\"quads\"],difficulty:\"expert\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--circle_center\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",tracks:[\"chest\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"hard\"}],Wa={chest:[\"chest\"],quads:[\"legs\"],glutes:[\"hips\"],arms:[\"shoulders\"],yoga:[\"core\",\"balance\",\"yoga\"],\"martial-arts\":[\"stances\",\"boxing\"],cardio:[\"cardio\"],meditation:[\"meditation\"]},z=Object.freeze([\"chest\",\"quads\",\"glutes\",\"arms-shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),Xa=Object.freeze({arms:\"arms-shoulders\"}),Ya=e=>e===\"arms-shoulders\"?\"arms\":e,ge=e=>[...new Set((Array.isArray(e)?e:[]).map(a=>Xa[a]??a).filter(a=>z.includes(a)))],ye={chest:[\"crossbow\",\"cannon\"],legs:[\"greatsword\",\"hammer\"],hips:[\"scythe\",\"halberd\"],core:[\"mace\",\"tome\"],shoulders:[\"bow\",\"trident\"],balance:[\"axe\",\"sabre\"],yoga:[\"staff\",\"wand\"],stances:[\"dagger\",\"spear\"],boxing:[\"gauntlets\",\"rapier\"],cardio:[\"chakram\",\"flail\"]},_=Object.freeze([\"myr5\",\"roster/23-blob-texture-bodies--blob_creature_3d_model\"]);function ke(e){if(e===\"plank-flight\")return\"easy\";let a=m[e];if(!a)return\"easy\";let t=he[a.group].filter(r=>r.kind===a.kind);return[\"easy\",\"medium\",\"hard\",\"expert\"][Math.min(3,Math.floor(t.indexOf(a)*4/t.length))]}var g=Ka.filter(e=>!_.includes(e.id)).map(e=>({...e,name:pe(e.id),groups:e.tracks.flatMap(a=>Wa[a])})),W=Object.freeze(Object.fromEntries(z.map(e=>[e,g.find(a=>a.tracks[0]===Ya(e)&&a.difficulty===\"easy\").id]))),qe=e=>ge(e).map(a=>W[a]),X=[{id:\"supportive\",group:\"legs\",kind:\"hold\"},{id:\"direct\",group:\"legs\",kind:\"reps\"},{id:\"analytical\",group:\"chest\",kind:\"reps\"},{id:\"playful\",group:\"stances\",kind:\"hold\"},{id:\"calm\",group:\"yoga\",kind:\"hold\"},{id:\"mom\",group:\"hips\",kind:\"reps\"}];function je(e,a){return g.filter(t=>t.groups.includes(e)&&t.difficulty===a)}V();var U=Object.freeze([\"easy\",\"medium\",\"hard\",\"expert\"]),Xe=e=>Object.freeze(e.map(a=>Object.freeze(a.map(t=>t*10)))),sr=Xe([[4,8,16],[8,12,20],[12,16,24],[16,20,28]]),cr=Xe([[1,2,4],[2,3,5],[3,4,6],[4,5,7]]);var Ke=600;var Za=15;var et=Object.freeze({uncommon:1,rare:2,legendary:3,secret:3}),at=Object.freeze({uncommon:Object.freeze({color:90,\"64-bit\":7,texture:3}),rare:Object.freeze({color:80,\"64-bit\":15,texture:5}),legendary:Object.freeze({color:70,\"64-bit\":20,texture:10}),secret:Object.freeze({color:60,\"64-bit\":20,texture:20})}),tt=2,ve=500*10,R=Object.freeze({sessions:120,activeMinutes:30,expertXpPerMinute:280,meditationMultiplier:tt,trioBonus:ve}),rt=R.sessions*(R.activeMinutes*R.expertXpPerMinute*R.meditationMultiplier+R.trioBonus),or=R.expertXpPerMinute*Za/60;var Ye=250,_e=Ye-1,We=40,st=rt/10,y=Object.freeze(Array.from({length:Ye},(e,a)=>10*(We*a+Math.floor((st-We*_e)*a*(a-1)/(_e*(_e-1))))));function Ve(e,a){if(!Number.isFinite(e)||e<0)throw RangeError(`Invalid ${a}.`);return e}function Je(e,a){if(Ve(a,\"performance\"),e===\"hold\")return{weapon1:a>=60,weapon2:a>=180,coach:a>=300,golden:a>=Ke,special:a>=Ke};if(e===\"reps\")return{weapon1:a>=8,weapon2:a>=12,coach:a>=15,golden:!1,special:!1};throw RangeError(\"Unknown performance kind.\")}function Qe(e){Ve(e,\"XP\");let a=0;for(;a<y.length-1&&e>=y[a+1];)a++;let t=a===y.length-1;return{level:a+1,xp:e,floor:y[a],next:t?null:y[a+1],into:t?0:e-y[a],need:t?0:y[a+1]-y[a],max:t}}var Ze=\"myr5-performance-progress-v2\";function h(e=globalThis.localStorage,a=globalThis.myr5AuthenticatedAccount){let t=a?.user?.id;if(t)return`account:${t}${a.dataEpoch!=null?`:${a.dataEpoch}`:\"\"}`;if(!e)return\"guest:unavailable\";let r=\"myr5-local-guest-owner-v1\";try{let s=e.getItem(r);return s||(s=globalThis.crypto?.randomUUID?.()||`guest-${Date.now()}-${Math.random()}`,e.setItem(r,s)),`guest:${s}`}catch{return\"guest:unavailable\"}}var J=3,ea=()=>({version:2,accessVersion:J,xpScale:10,paths:[],sessions:{},days:{},coaches:[..._],goldenCoaches:[],weapons:{},ships:[],completions:{},totalXp:0,excludedCoaches:[]});function ct(e={}){let a=Object.hasOwn(e,\"storage\")?e.storage:globalThis.localStorage,t=Object.hasOwn(e,\"account\")?e.account:globalThis.myr5AuthenticatedAccount;return{storage:a,owner:e.owner??h(a,t)}}var k=e=>e&&typeof e==\"object\"&&!Array.isArray(e),$=e=>Number.isFinite(e)&&e>=0,Se=e=>typeof e==\"string\"&&/^\\d{4}-\\d{2}-\\d{2}$/.test(e)&&new Date(`${e}T12:00:00Z`).toISOString().slice(0,10)===e,ot=e=>Array.isArray(e)&&(e.length===0||e.length===2&&new Set(e).size===2&&e.every(a=>z.includes(a)));function nt(e){return e?.version===2&&(e.accessVersion===void 0||e.accessVersion===J)&&(e.xpScale===void 0||e.xpScale===10)&&(e.paths===void 0||ot(e.paths))&&(e.activities===void 0||k(e.activities)&&Object.values(e.activities).every(a=>k(a)&&[\"meditation\",\"food\"].includes(a.kind)&&Se(a.day)))&&k(e.sessions)&&k(e.days)&&k(e.weapons)&&k(e.completions)&&Array.isArray(e.coaches)&&e.coaches.every(a=>C.some(t=>t.id===a)||S.includes(a))&&new Set(e.coaches).size===e.coaches.length&&Array.isArray(e.goldenCoaches)&&e.goldenCoaches.every(a=>e.coaches.includes(a))&&Array.isArray(e.ships)&&e.ships.every(a=>X.some(t=>t.id===a))&&Object.values(e.weapons).every(a=>Number.isInteger(a)&&a>=0&&a<=20)&&Object.values(e.completions).every(a=>Number.isSafeInteger(a)&&a>=0)&&Object.entries(e.days).every(([a,t])=>Se(a)&&k(t)&&$(t.workoutXp)&&[\"workout\",\"meditation\",\"food\"].every(r=>typeof t[r]==\"boolean\"))&&Object.values(e.sessions).every(a=>k(a)&&Se(a.day)&&$(a.xp)&&$(a.value)&&$(a.continuous))}var aa={sprint:x.sprintRounds,gentle:x.gentleActiveSeconds,pace:x.paceActiveSeconds};function it(e){let a=new Set(_),t=(s,c)=>{for(let o of je(s,c))a.add(o.id)};for(let s of Object.values(e.sessions))if(s.kind===\"hold\"||s.kind===\"reps\")for(let[c,o]of s.kind===\"hold\"?Object.entries(s.perDifficultyContinuous??{[s.difficulty]:s.continuous}):[[s.difficulty,s.value]])$(o)&&Je(s.kind,o).coach&&t(s.group,c);else s.kind in aa&&s.value>=aa[s.kind]&&t(s.group,s.difficulty);let r=Object.values(e.days).filter(s=>s.meditation).length;return U.forEach((s,c)=>{r>=x.meditationDays[c]&&t(\"meditation\",s)}),a}function dt(e){if(e.xpScale!==10){for(let a of Object.values(e.days))a.workoutXp*=10;for(let a of Object.values(e.sessions))a.xp*=10;e.xpScale=10}e.excludedCoaches??=[];for(let a of S)e.excludedCoaches.includes(a)||e.excludedCoaches.push(a);if(e.paths??=[],e.accessVersion!==J){let a=new Set([...it(e),...qe(e.paths)]);e.coaches=C.map(t=>t.id).filter(t=>a.has(t)),e.accessVersion=J}e.coaches=e.coaches.filter(a=>!S.includes(a));for(let a of _)bt(e.coaches,a);return e.goldenCoaches=e.goldenCoaches.filter(a=>e.coaches.includes(a)),e}function w(e={}){try{let{storage:a,owner:t}=ct(e),r=JSON.parse(a?.getItem(t===\"server-derivation\"?`${Ze}/${t}`:f(`${Ze}/${t}`,a))||\"null\");return nt(r)?lt(dt(r)):ea()}catch{return ea()}}function lt(e){return e.totalXp=Object.values(e.days).reduce((a,t)=>a+t.workoutXp*(t.meditation?2:1)+(t.workout&&t.meditation&&t.food?ve:0),0)+(Number.isFinite(e.worldBonusXp)&&e.worldBonusXp>=0?e.worldBonusXp:0),e}var bt=(e,a)=>{e.includes(a)||e.push(a)};function Q(e){let a=Object.values(e?.days??{}),t=Object.keys(e?.sessions??{}).length>0||a.some(c=>c.workout),r=a.some(c=>c.meditation),s=a.some(c=>c.food);return{workout:t,meditation:r,food:s,done:t&&r&&s}}var Ce=(e={})=>{let a=w(e);return Q(a).done?C.map(t=>t.id).filter(t=>!S.includes(t)):a.coaches},ta=(e,a={})=>Ce(a).includes(e);var Z=Object.freeze([{id:\"supportive\",name:\"Bubble shuttle\",description:\"A rounded cockpit with four bulbous engine pods around a compact hull.\"},{id:\"direct\",name:\"Wing fighter\",description:\"A pointed cockpit between broad swept wings and long engine rails.\"},{id:\"analytical\",name:\"Ring-engine scout\",description:\"A narrow cockpit and small fins ahead of a large circular rear engine.\"},{id:\"playful\",name:\"Fork interceptor\",description:\"A dark central cockpit between long forked nose rails, with two side engine pods.\"},{id:\"calm\",name:\"Capsule pod\",description:\"A short rounded capsule with a forward cockpit and a thick rear housing.\"},{id:\"mom\",name:\"Cargo carrier\",description:\"A broad blocky hull with a raised centre tower and two large front engine housings.\"}].map(e=>Object.freeze({...e,assetPath:`assets/ships/${e.id}.glb`}))),Ee=e=>Z.find(a=>a.id===e)||Z[0];var ft=Object.freeze({chest:\"\\u{1F3CB}\\uFE0F\",quads:\"\\u{1F9B5}\",legs:\"\\u{1F9B5}\",glutes:\"\\u{1F351}\",hips:\"\\u{1F351}\",arms:\"\\u{1F4AA}\",\"arms-shoulders\":\"\\u{1F938}\",shoulders:\"\\u{1F938}\",core:\"\\u{1F3AF}\",balance:\"\\u2696\\uFE0F\",yoga:\"\\u{1F9D8}\",\"martial-arts\":\"\\u{1F94B}\",stances:\"\\u{1F94B}\",boxing:\"\\u{1F94A}\",cardio:\"\\u26A1\",meditation:\"\\u{1F319}\",original:\"\\u25C8\"});var ut=Object.fromEntries([...v.map(e=>[e.name,e.id]),[\"Quads\",\"quads\"],[\"Glutes\",\"glutes\"],[\"Arms & shoulders\",\"arms-shoulders\"],[\"Arms & Shoulders\",\"arms-shoulders\"],[\"Arms\",\"arms\"],[\"Martial arts\",\"martial-arts\"],[\"Martial Arts\",\"martial-arts\"],[\"Meditation\",\"meditation\"]]),mt=e=>e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\"),Sr=new RegExp(`(?<![\\\\w])(?<!Cool )(${Object.keys(ut).sort((e,a)=>a.length-e.length).map(mt).join(\"|\")})(?![\\\\w])`,\"g\");var Ae=e=>m[e]?.group||(e===\"jumping\"?\"cardio\":null);var Ar=Object.freeze({advance:\"That wasn't enough to impress my clipboard. Ready for the next level?\",max:\"Maximum difficulty? Fine. One more than your last round. Now we're negotiating.\",limit:\"Five rounds. Even I have to respect the paperwork. This exercise family is done for today.\",rest:\"Rest first. The next challenge can wait.\"});var Or=Object.freeze([{key:\"pushups\",mode:\"pushup\",goal:10,voice:!0},{key:\"squats\",mode:\"squat\",goal:10,voice:!0},{key:\"situps\",mode:\"high-plank\",goal:15,voice:!0},{key:\"meditation\",mode:null,goal:null,voice:!0},{key:\"food\",mode:null,goal:null,voice:!1}]),ra=Object.freeze({chest:\"chest\",legs:\"legs\",hips:\"hips\",shoulders:\"shoulders\",yoga:\"yoga\",stances:\"martial-arts\",boxing:\"martial-arts\",cardio:\"cardio\"}),ht=Object.freeze([\"chest\",\"legs\",\"hips\",\"shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),Fr=Object.freeze([...ht,\"meditation\",\"food\"]);var ee=5;var q=[{id:\"pal-01\",name:\"Acid Forge\",tagline:\"Lime bites oxblood and chrome\",colors:[\"#C6FF00\",\"#4A0E17\",\"#C0C7CE\"],unlockRule:\"aura-milestone\",unlockAtDay:5,finish:\"glitter\"},{id:\"pal-02\",name:\"Neon Afterburn Haze\",tagline:\"Orange aqua contrails with violet flare\",colors:[\"#FF6D00\",\"#00E0B0\",\"#C800FF\"],unlockRule:\"aura-milestone\",unlockAtDay:10,finish:\"brushed-metal\"},{id:\"pal-03\",name:\"Petrel Copper\",tagline:\"Petrol depths meet intense violet\",colors:[\"#0E5A61\",\"#B87333\",\"#6913E7\"],unlockRule:\"aura-milestone\",unlockAtDay:15,finish:\"polished-metal\"},{id:\"pal-04\",name:\"Vinaigrette Rust\",tagline:\"Oil pigment rust swirl\",colors:[\"#8A3B12\",\"#556B2F\",\"#E7A213\"],unlockRule:\"aura-milestone\",unlockAtDay:20,finish:\"marbled\"},{id:\"pal-05\",name:\"Neon Relic\",tagline:\"Hot magenta neon amber on bone\",colors:[\"#FF009F\",\"#FFB500\",\"#F2EFE6\"],unlockRule:\"aura-milestone\",unlockAtDay:25,finish:\"glitter\"},{id:\"pal-06\",name:\"Glacier Ember\",tagline:\"Ice teal over ember red\",colors:[\"#13E7D1\",\"#D9381E\",\"#2B2530\"],unlockRule:\"aura-milestone\",unlockAtDay:30,finish:\"pearl\"},{id:\"pal-07\",name:\"Chartreuse Storm\",tagline:\"Sludge green meets slate violet\",colors:[\"#B5C700\",\"#3D3654\",\"#E85D75\"],unlockRule:\"aura-milestone\",unlockAtDay:35,finish:\"matte\"},{id:\"pal-08\",name:\"Saffron Voltage\",tagline:\"Saffron arcs on ink blue\",colors:[\"#FFB000\",\"#101D42\",\"#00FAA6\"],unlockRule:\"aura-milestone\",unlockAtDay:40,finish:\"polished-metal\"},{id:\"pal-09\",name:\"Neon Venom Signal\",tagline:\"Poison lime warning under arterial red\",colors:[\"#A6FF00\",\"#FF2442\",\"#101828\"],unlockRule:\"aura-milestone\",unlockAtDay:45,finish:\"watercolor\"},{id:\"pal-10\",name:\"Mercury Moss\",tagline:\"Silver moss over saturated gold\",colors:[\"#AEB6BC\",\"#5C6B3C\",\"#E7A913\"],unlockRule:\"aura-milestone\",unlockAtDay:50,finish:\"brushed-metal\"},{id:\"pal-11\",name:\"Ultraviolet Tar\",tagline:\"Violet glow on tar black\",colors:[\"#7B2FF7\",\"#141414\",\"#FFC93C\"],unlockRule:\"aura-milestone\",unlockAtDay:55,finish:\"glaze\"},{id:\"pal-12\",name:\"Salmon Circuit\",tagline:\"Salmon pink on circuit green\",colors:[\"#FA2200\",\"#00A86B\",\"#2E2A5C\"],unlockRule:\"aura-milestone\",unlockAtDay:60,finish:\"pearl\"},{id:\"pal-13\",name:\"Muddy Pop\",tagline:\"Ochre mud sparks cerulean\",colors:[\"#8C6A2F\",\"#0081C8\",\"#E71362\"],unlockRule:\"battle-pass\",reward:\"strider-2:L1\",finish:\"matte\"},{id:\"pal-14\",name:\"Neon Vapor Crown\",tagline:\"Laser pink blue over dark purple\",colors:[\"#FF00AA\",\"#00D0FF\",\"#1B0033\"],unlockRule:\"battle-pass\",reward:\"strider-2:L3\",finish:\"watercolor\"},{id:\"pal-15\",name:\"Brick Vein\",tagline:\"Brick red veined in steel\",colors:[\"#A0342B\",\"#8C9BA5\",\"#D7E713\"],unlockRule:\"battle-pass\",reward:\"strider-2:L4\",finish:\"marbled\"},{id:\"pal-16\",name:\"Peacock Static\",tagline:\"Peacock green hums gold\",colors:[\"#046A5B\",\"#FAAF00\",\"#5B1E4B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L1\",finish:\"polished-metal\"},{id:\"pal-17\",name:\"Foghorn Flame\",tagline:\"Fog gray with fire-orange siren\",colors:[\"#B8C0C2\",\"#FA3F00\",\"#123C69\"],unlockRule:\"battle-pass\",reward:\"strider-3:L3\",finish:\"brushed-metal\"},{id:\"pal-18\",name:\"Toxic Honey\",tagline:\"Honey drips on toxic lime\",colors:[\"#D9A404\",\"#9BF00B\",\"#4A154B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L4\",finish:\"glaze\"},{id:\"pal-19\",name:\"Neon Crimson Mirage\",tagline:\"Scarlet toxic lime over deep jade\",colors:[\"#FF1744\",\"#76FF03\",\"#005E3C\"],unlockRule:\"battle-pass\",reward:\"strider-4:L1\",finish:\"matte\"},{id:\"pal-20\",name:\"Opal Drain\",tagline:\"Opal sheen on drainpipe green\",colors:[\"#13E7B9\",\"#3E5641\",\"#D1495B\"],unlockRule:\"battle-pass\",reward:\"strider-4:L3\",finish:\"pearl\"},{id:\"pal-21\",name:\"Rust Halo\",tagline:\"Rust ringed by mint flash\",colors:[\"#9C4221\",\"#12E898\",\"#2C2C54\"],unlockRule:\"battle-pass\",reward:\"strider-4:L4\",finish:\"brushed-metal\"},{id:\"pal-22\",name:\"Volt Quartz\",tagline:\"Electric yellow on rose quartz\",colors:[\"#F5E900\",\"#E71363\",\"#264653\"],unlockRule:\"battle-pass\",reward:\"strider-5:L1\",finish:\"glitter\"},{id:\"pal-23\",name:\"Neon Flare Garden\",tagline:\"Laser lime pink on teal steel\",colors:[\"#8CFF00\",\"#FF006E\",\"#00B8A9\"],unlockRule:\"battle-pass\",reward:\"strider-5:L3\",finish:\"matte\"},{id:\"pal-24\",name:\"Crimson Tidebreak\",tagline:\"Crimson breaks on seafoam\",colors:[\"#B91338\",\"#13E7B5\",\"#2A1E5C\"],unlockRule:\"battle-pass\",reward:\"strider-5:L4\",finish:\"watercolor\"},{id:\"pal-25\",name:\"Brass Mirage\",tagline:\"Brass shimmer dusky plum\",colors:[\"#C59B42\",\"#5A2A52\",\"#0D94ED\"],unlockRule:\"battle-pass\",reward:\"strider-6:L1\",finish:\"polished-metal\"},{id:\"pal-26\",name:\"Neon Infrared Bloom\",tagline:\"Red heat sliced by frozen cyan\",colors:[\"#FF0000\",\"#00F5FF\",\"#5B00B5\"],unlockRule:\"battle-pass\",reward:\"strider-6:L3\",finish:\"glaze\"},{id:\"pal-27\",name:\"Ice Magma\",tagline:\"Pigmented ice over magma orange\",colors:[\"#D6F0F0\",\"#F45B0F\",\"#3D2C8D\"],unlockRule:\"battle-pass\",reward:\"strider-6:L4\",finish:\"marbled\"},{id:\"pal-28\",name:\"Fern Radio\",tagline:\"Fern green static magenta\",colors:[\"#3F7A3A\",\"#E4007C\",\"#E7BE13\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L1\",finish:\"matte\"},{id:\"pal-29\",name:\"Porcelain Fuel\",tagline:\"Porcelain white fuel orange\",colors:[\"#F2F2F0\",\"#FF7A1A\",\"#14507A\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L3\",finish:\"glaze\"},{id:\"pal-30\",name:\"Neon Photon Alley\",tagline:\"Yellow signage aqua and violet glass\",colors:[\"#FFD300\",\"#00FFCC\",\"#7A00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L4\",finish:\"brushed-metal\"},{id:\"pal-31\",name:\"Neon Solar Flare\",tagline:\"Hot yellow ember in plum space\",colors:[\"#FFEA00\",\"#FF3D00\",\"#26142B\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L1\",finish:\"polished-metal\"},{id:\"pal-32\",name:\"Wet Cement Rose\",tagline:\"Cement gray wild rose\",colors:[\"#8D9096\",\"#D84A87\",\"#F0E130\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L3\",finish:\"matte\"},{id:\"pal-33\",name:\"Neon Laser Tropics\",tagline:\"Chartreuse beams with scorched orange shadows\",colors:[\"#C6FF00\",\"#FF4D00\",\"#001219\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L4\",finish:\"watercolor\"},{id:\"pal-34\",name:\"Copper Fog\",tagline:\"Copper glows through fog\",colors:[\"#B46A30\",\"#C4CBCE\",\"#B600F5\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L1\",finish:\"brushed-metal\"},{id:\"pal-35\",name:\"Laser Moss\",tagline:\"Laser cyan on moss\",colors:[\"#00E5FF\",\"#546B2F\",\"#FF4FA3\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L3\",finish:\"glitter\"},{id:\"pal-36\",name:\"Bone Ember\",tagline:\"Bone white ember orange\",colors:[\"#EDE8DC\",\"#E4572E\",\"#BA00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L4\",finish:\"matte\"},{id:\"pal-37\",name:\"Plum Static\",tagline:\"Plum noise with electric lime\",colors:[\"#4A1E4F\",\"#B8F000\",\"#33B5C7\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L1\",finish:\"glaze\"},{id:\"pal-38\",name:\"Neon Lime Vortex\",tagline:\"Acid green swirling through electric blue\",colors:[\"#BFFF00\",\"#00E5FF\",\"#12100F\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L3\",finish:\"pearl\"},{id:\"pal-39\",name:\"Clay Command\",tagline:\"Terracotta clay cobalt order\",colors:[\"#C06B55\",\"#2E4BC6\",\"#EDE6D6\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L4\",finish:\"marbled\"},{id:\"pal-40\",name:\"Static Cream\",tagline:\"Cream noise shock green\",colors:[\"#F4EDDE\",\"#39FF14\",\"#4A306D\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L1\",finish:\"glitter\"},{id:\"pal-41\",name:\"Midnight Papaya\",tagline:\"Midnight blue papaya burst\",colors:[\"#191970\",\"#FF9E42\",\"#13E796\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L3\",finish:\"polished-metal\"},{id:\"pal-42\",name:\"Dusty Laser\",tagline:\"Dusty mauve laser green\",colors:[\"#A27E8E\",\"#7CFC00\",\"#1B3B6F\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L4\",finish:\"matte\"},{id:\"pal-43\",name:\"Neon Atomic Sunset\",tagline:\"Amber fallout glowing beside ultraviolet pink\",colors:[\"#FF9100\",\"#FF00F5\",\"#062A2A\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L1\",finish:\"glaze\"},{id:\"pal-44\",name:\"Fern Flame\",tagline:\"Cool fern fierce flame\",colors:[\"#4F7942\",\"#FF3D2E\",\"#E75A13\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L3\",finish:\"brushed-metal\"},{id:\"pal-45\",name:\"Violet Quarry\",tagline:\"Quarry gray violet eruption\",colors:[\"#7D7F87\",\"#8B3DFF\",\"#DCE35B\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L4\",finish:\"matte\"},{id:\"pal-46\",name:\"Cadmium Deep\",tagline:\"Cadmium red deep lagoon\",colors:[\"#D32E12\",\"#046E8F\",\"#E79E13\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L1\",finish:\"polished-metal\"},{id:\"pal-47\",name:\"Pigment Riot\",tagline:\"Intense blue maroon and hot orange\",colors:[\"#139FE7\",\"#6E1423\",\"#FF7300\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L3\",finish:\"watercolor\"},{id:\"pal-48\",name:\"Neon Prism Riot\",tagline:\"Sea-glass cyan, magenta bite, lemon flash\",colors:[\"#00FFC6\",\"#FF00B8\",\"#F5FF00\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L4\",finish:\"pearl\"},{id:\"pal-49\",name:\"Neon Tropic Surge\",tagline:\"Aqua tide, citrus flare, violet thunder\",colors:[\"#00FF9D\",\"#FFB000\",\"#D000FF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L1\",finish:\"glitter\"},{id:\"pal-50\",name:\"Ash Honey\",tagline:\"Ash gray golden honey\",colors:[\"#9BA0A6\",\"#E0A100\",\"#2F6690\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L3\",finish:\"matte\"},{id:\"pal-51\",name:\"Cherry Motor\",tagline:\"Cherry motor oil with electric blue\",colors:[\"#B22234\",\"#3B2F2F\",\"#005CFF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L4\",finish:\"brushed-metal\"},{id:\"pal-52\",name:\"Glacier Punch\",tagline:\"Glacier pigmented hot punch\",colors:[\"#13A6E7\",\"#F52549\",\"#3D405B\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L1\",finish:\"glaze\"},{id:\"pal-53\",name:\"Tarnish Bloom\",tagline:\"Tarnished teal floral orange\",colors:[\"#3A7D7B\",\"#F4852B\",\"#E9D6EC\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L3\",finish:\"marbled\"},{id:\"pal-54\",name:\"Umber Signal\",tagline:\"Umber earth neon signal\",colors:[\"#635147\",\"#D0FF4B\",\"#7A28C0\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L4\",finish:\"matte\"},{id:\"pal-55\",name:\"Rose Quartz Coil\",tagline:\"Soft rose coil of teal\",colors:[\"#E7134B\",\"#0FA3A3\",\"#2B2118\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L1\",finish:\"pearl\"},{id:\"pal-56\",name:\"Sulfur Deck\",tagline:\"Sulfur yellow blue and hot orange\",colors:[\"#E3D200\",\"#274C77\",\"#FF4A00\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L3\",finish:\"polished-metal\"},{id:\"pal-57\",name:\"Neon Ember Static\",tagline:\"Orange yellow buzzing with royal voltage\",colors:[\"#FF5E00\",\"#FFE600\",\"#3A0CA3\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L4\",finish:\"watercolor\"},{id:\"pal-58\",name:\"Mango Ash\",tagline:\"Mango bright volcanic ash\",colors:[\"#FFA12B\",\"#59595E\",\"#64DDBB\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L1\",finish:\"matte\"},{id:\"pal-59\",name:\"Steel Petal\",tagline:\"Steel gray crushed petals\",colors:[\"#8A8F98\",\"#E71813\",\"#D8E26E\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L3\",finish:\"brushed-metal\"},{id:\"pal-60\",name:\"Ninja Cream\",tagline:\"Hidden navy cream coral\",colors:[\"#1E2A4A\",\"#F0EBE0\",\"#7EEC00\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L4\",finish:\"glaze\"},{id:\"pal-61\",name:\"Tangerine Static\",tagline:\"Tangerine buzz grape shadow\",colors:[\"#F28500\",\"#3D2159\",\"#202020\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L1\",finish:\"glitter\"},{id:\"pal-62\",name:\"Sage Pistol\",tagline:\"Muted sage pistol pink\",colors:[\"#9AB5A1\",\"#E45C98\",\"#33272A\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L3\",finish:\"matte\"},{id:\"pal-63\",name:\"Lagoon Battery\",tagline:\"Lagoon green charged yellow\",colors:[\"#1D8A7A\",\"#FAE000\",\"#76305B\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L4\",finish:\"polished-metal\"},{id:\"pal-64\",name:\"Wine Cooling\",tagline:\"Warm wine cooled by mint\",colors:[\"#701F3C\",\"#13E78C\",\"#E8D44D\"],unlockRule:\"battle-pass\",reward:\"warden-1:L1\",finish:\"pearl\"},{id:\"pal-65\",name:\"Charcoal Tulip\",tagline:\"Charcoal tulip flame combo\",colors:[\"#2E3033\",\"#FF4E50\",\"#CA00C7\"],unlockRule:\"battle-pass\",reward:\"warden-1:L3\",finish:\"matte\"},{id:\"pal-66\",name:\"Neon Acid Harbor\",tagline:\"Lime light cutting across magenta docks\",colors:[\"#39FF14\",\"#FF007F\",\"#001B2E\"],unlockRule:\"battle-pass\",reward:\"warden-1:L4\",finish:\"pearl\"},{id:\"pal-67\",name:\"Rustic Neon\",tagline:\"Barn red barn lime buzz\",colors:[\"#8A3324\",\"#C1FF33\",\"#2D4A6B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L1\",finish:\"marbled\"},{id:\"pal-68\",name:\"Alice Warning\",tagline:\"Alice blue caution orange\",colors:[\"#1399E7\",\"#FF7E1F\",\"#EEF700\"],unlockRule:\"battle-pass\",reward:\"blob-2:L3\",finish:\"watercolor\"},{id:\"pal-69\",name:\"Smog Candy\",tagline:\"Smog beige candy teal\",colors:[\"#E79813\",\"#00C2A8\",\"#D8395B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L4\",finish:\"matte\"},{id:\"pal-70\",name:\"Violet Furnace\",tagline:\"Furnace violet cooled sky\",colors:[\"#6A2D9E\",\"#F57C3B\",\"#302012\"],unlockRule:\"battle-pass\",reward:\"blob-3:L1\",finish:\"glaze\"},{id:\"pal-71\",name:\"Khaki Storm\",tagline:\"Khaki field storm magenta\",colors:[\"#A89B6E\",\"#E0218A\",\"#2A6F97\"],unlockRule:\"battle-pass\",reward:\"blob-3:L3\",finish:\"brushed-metal\"},{id:\"pal-72\",name:\"Neon Circuit Surge\",tagline:\"Turquoise sparks over midnight violet\",colors:[\"#00F5D4\",\"#F72585\",\"#0B0321\"],unlockRule:\"battle-pass\",reward:\"blob-3:L4\",finish:\"glitter\"},{id:\"pal-73\",name:\"Cedar Ultraviolet\",tagline:\"Cedar wood ultraviolet light\",colors:[\"#7A4532\",\"#8246FF\",\"#E9E4D8\"],unlockRule:\"battle-pass\",reward:\"blob-4:L1\",finish:\"matte\"},{id:\"pal-74\",name:\"Frosted Chili\",tagline:\"Frost blue chili red\",colors:[\"#13AAE7\",\"#C13A2F\",\"#4C5B2C\"],unlockRule:\"battle-pass\",reward:\"blob-4:L3\",finish:\"polished-metal\"},{id:\"pal-75\",name:\"Moss Rocket\",tagline:\"Moss green rocket pink\",colors:[\"#5B7042\",\"#FA0057\",\"#061F5B\"],unlockRule:\"battle-pass\",reward:\"blob-4:L4\",finish:\"matte\"},{id:\"pal-76\",name:\"Pewter Punch\",tagline:\"Pewter softened hot punch\",colors:[\"#99A0A8\",\"#FF3D7F\",\"#164D37\"],unlockRule:\"battle-pass\",reward:\"cap-2:L1\",finish:\"brushed-metal\"},{id:\"pal-77\",name:\"Neon Glacier Riot\",tagline:\"Cyan hot pink and radioactive lime\",colors:[\"#00F0FF\",\"#FF00E5\",\"#CCFF00\"],unlockRule:\"battle-pass\",reward:\"cap-2:L3\",finish:\"watercolor\"},{id:\"pal-78\",name:\"Cola Lightning\",tagline:\"Cola brown lightning yellow\",colors:[\"#3A2618\",\"#F7FF00\",\"#FA005E\"],unlockRule:\"battle-pass\",reward:\"cap-2:L4\",finish:\"glaze\"},{id:\"pal-79\",name:\"Periwinkle Fuel\",tagline:\"Soft periwinkle diesel orange\",colors:[\"#1325E7\",\"#E0641E\",\"#2F4730\"],unlockRule:\"battle-pass\",reward:\"cap-3:L1\",finish:\"matte\"},{id:\"pal-80\",name:\"Jade Alarm\",tagline:\"Jade calm siren red\",colors:[\"#57A78A\",\"#D91E36\",\"#E7AF13\"],unlockRule:\"battle-pass\",reward:\"cap-3:L3\",finish:\"pearl\"},{id:\"pal-81\",name:\"Neon Ultraviolet Pulse\",tagline:\"Violet beams with mint-charged current\",colors:[\"#8C00FF\",\"#00FFB2\",\"#160021\"],unlockRule:\"battle-pass\",reward:\"cap-3:L4\",finish:\"matte\"},{id:\"pal-82\",name:\"Neon Nightcaller\",tagline:\"Cyan violet flame in black-green fog\",colors:[\"#00FFFF\",\"#9400FF\",\"#131A00\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L1\",finish:\"marbled\"},{id:\"pal-83\",name:\"Ivory Shock\",tagline:\"Ivory calm violet shock\",colors:[\"#F0EEE4\",\"#7C1FD3\",\"#FFB200\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L3\",finish:\"glaze\"},{id:\"pal-84\",name:\"Neon Ruby Arc\",tagline:\"Red-hot signal over green afterglow\",colors:[\"#FF073A\",\"#00FF87\",\"#080F1B\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L4\",finish:\"glitter\"},{id:\"pal-85\",name:\"Maroon Cloud\",tagline:\"Maroon heavy cloud blue\",colors:[\"#6D1E2E\",\"#1381E7\",\"#E5D352\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L1\",finish:\"watercolor\"},{id:\"pal-86\",name:\"Chrome Berry\",tagline:\"Chrome band over berry\",colors:[\"#C3C9D1\",\"#8E2A5C\",\"#FFB347\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L3\",finish:\"polished-metal\"},{id:\"pal-87\",name:\"Lentil Neon\",tagline:\"Lentil tan electric aqua\",colors:[\"#B09A6E\",\"#00FFF0\",\"#5F1B63\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L4\",finish:\"matte\"},{id:\"pal-88\",name:\"Neon Cobalt Fuse\",tagline:\"Blue ignition wired to blazing yellow\",colors:[\"#004DFF\",\"#FFEE00\",\"#0D0221\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L1\",finish:\"glaze\"},{id:\"pal-89\",name:\"Spruce Bonfire\",tagline:\"Dark spruce bonfire orange\",colors:[\"#23493C\",\"#FF7A3D\",\"#0C1735\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L3\",finish:\"matte\"},{id:\"pal-90\",name:\"Lilac Foundry\",tagline:\"Lilac meets foundry steel\",colors:[\"#9813E7\",\"#7B8894\",\"#D4522A\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L4\",finish:\"brushed-metal\"},{id:\"pal-91\",name:\"Topaz Battle\",tagline:\"Topaz gold navy clash\",colors:[\"#F0B429\",\"#182952\",\"#FA007D\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L1\",finish:\"polished-metal\"},{id:\"pal-92\",name:\"Dusk Celery\",tagline:\"Dusky purple celery snap\",colors:[\"#574B6B\",\"#C8D86A\",\"#FA2100\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L3\",finish:\"matte\"},{id:\"pal-93\",name:\"Tide Copperfuse\",tagline:\"Tidal teal copperfuse glow\",colors:[\"#0F7B7B\",\"#C07840\",\"#C2F000\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L4\",finish:\"pearl\"},{id:\"pal-94\",name:\"Punch Driftwood\",tagline:\"Fruit punch on driftwood\",colors:[\"#E63362\",\"#6E5B4A\",\"#1648EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L1\",finish:\"watercolor\"},{id:\"pal-95\",name:\"Ink Lime\",tagline:\"Ink smudge streaked lime\",colors:[\"#23232A\",\"#A8E10C\",\"#7E10EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L3\",finish:\"glaze\"},{id:\"pal-96\",name:\"Porcelain Prism\",tagline:\"Porcelain violet over tidal blue\",colors:[\"#F5F1E8\",\"#BD05DA\",\"#005F73\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L4\",finish:\"marbled\"},{id:\"pal-97\",name:\"Wasabi Cloud\",tagline:\"Wasabi heat cloud gray\",colors:[\"#9FBF3B\",\"#8E94A0\",\"#E71363\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L1\",finish:\"matte\"},{id:\"pal-98\",name:\"Eggplant Flare\",tagline:\"Eggplant deep flare yellow\",colors:[\"#41284E\",\"#FFD23F\",\"#5BC0BE\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L3\",finish:\"glitter\"},{id:\"pal-99\",name:\"Stone Rose\",tagline:\"Stone gray antique rose\",colors:[\"#8F8A82\",\"#C46E8E\",\"#2F6DA3\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L4\",finish:\"brushed-metal\"},{id:\"pal-100\",name:\"Neon Quasar Pop\",tagline:\"Yellow green comet over indigo gravity\",colors:[\"#FFEF00\",\"#00FF3C\",\"#2D02B8\"],unlockRule:\"battle-pass\",reward:\"lume-1:L1\",finish:\"glaze\"},{id:\"pal-101\",name:\"Arctic Rust\",tagline:\"Arctic white rusted orange\",colors:[\"#E9F2F5\",\"#9E4A1D\",\"#3E3F8F\"],unlockRule:\"battle-pass\",reward:\"lume-1:L3\",finish:\"brushed-metal\"},{id:\"pal-102\",name:\"Magenta Selvage\",tagline:\"Magenta edge denim blue\",colors:[\"#D8117E\",\"#3B5B8C\",\"#E7D313\"],unlockRule:\"battle-pass\",reward:\"lume-1:L4\",finish:\"matte\"},{id:\"pal-103\",name:\"Haze Saffron\",tagline:\"Purple haze saffron spark\",colors:[\"#8467B8\",\"#F4A81D\",\"#1D4E50\"],unlockRule:\"battle-pass\",reward:\"food:L1\",finish:\"pearl\"},{id:\"pal-104\",name:\"Neon Cyber Reed\",tagline:\"Green circuitry humming beside lemon static\",colors:[\"#00FF66\",\"#FFEA00\",\"#4B0082\"],unlockRule:\"battle-pass\",reward:\"food:L2\",finish:\"glitter\"},{id:\"pal-105\",name:\"Neon Magma Bloom\",tagline:\"Molten orange meeting synthetic magenta\",colors:[\"#FF2E00\",\"#F000FF\",\"#0A0A0F\"],unlockRule:\"battle-pass\",reward:\"food:L3\",finish:\"polished-metal\"},{id:\"pal-106\",name:\"Dune Violet\",tagline:\"Warm dune electric violet\",colors:[\"#C9AE7E\",\"#7D2BF0\",\"#13E7E3\"],unlockRule:\"battle-pass\",reward:\"food:L4\",finish:\"marbled\"},{id:\"pal-107\",name:\"Ember Nightshade\",tagline:\"Violet shadows ember teal\",colors:[\"#523090\",\"#EF4E23\",\"#0C5D68\"],unlockRule:\"battle-pass\",reward:\"food:L5\",finish:\"watercolor\"},{id:\"opal-jelly\",name:\"Opal Jelly\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b3fff0\",\"#9767ff\",\"#fff1ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"bubble-glass\",name:\"Bubble Glass\",tagline:\"Shifting highlights and luminous color\",colors:[\"#58eaff\",\"#b8fff8\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"prism-crystal\",name:\"Prism Crystal\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b18aff\",\"#447bff\",\"#fff2ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"holo-foil\",name:\"Holographic Foil\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff54de\",\"#39e5ff\",\"#fff56b\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"glitter-resin\",name:\"Glitter Resin\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff319c\",\"#7025ff\",\"#fff6db\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"galaxy-geode\",name:\"Galaxy Geode\",tagline:\"Shifting highlights and luminous color\",colors:[\"#21134e\",\"#7b39e8\",\"#63ffff\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"caustic-slime\",name:\"Caustic Slime\",tagline:\"Organic horror surface\",colors:[\"#8aa52b\",\"#20300b\",\"#dcff57\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"blister-hide\",name:\"Blister Hide\",tagline:\"Organic horror surface\",colors:[\"#9e435f\",\"#2d1727\",\"#e7bb72\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"rotten-rind\",name:\"Rotten Rind\",tagline:\"Organic horror surface\",colors:[\"#626124\",\"#211916\",\"#a2b16b\"],unlockRule:\"battle-pass\",finish:\"matte\"},{id:\"parasite-nest\",name:\"Parasite Nest\",tagline:\"Organic horror surface\",colors:[\"#786541\",\"#151c14\",\"#ece2b0\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"exposed-sinew\",name:\"Exposed Sinew\",tagline:\"Organic horror surface\",colors:[\"#b72945\",\"#300b24\",\"#efb5a1\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"abyssal-maw\",name:\"Abyssal Maw\",tagline:\"Organic horror surface\",colors:[\"#522045\",\"#100d19\",\"#d9e6a7\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"circuit-alloy\",name:\"Circuit Alloy\",tagline:\"Machine surface\",colors:[\"#273f50\",\"#0a111c\",\"#4effdc\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"servo-armor\",name:\"Servo Armor\",tagline:\"Machine surface\",colors:[\"#617080\",\"#141b25\",\"#ff6739\"],unlockRule:\"battle-pass\",finish:\"brushed-metal\"},{id:\"chrome-rib\",name:\"Chrome Rib\",tagline:\"Machine surface\",colors:[\"#bfcde1\",\"#202f46\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"carbon-mech\",name:\"Carbon Mech\",tagline:\"Machine surface\",colors:[\"#323d46\",\"#080d15\",\"#95bac9\"],unlockRule:\"battle-pass\"},{id:\"hazard-panel\",name:\"Hazard Panel\",tagline:\"Machine surface\",colors:[\"#edb932\",\"#131a20\",\"#ffe9a2\"],unlockRule:\"battle-pass\"},{id:\"reactor-glass\",name:\"Reactor Glass\",tagline:\"Machine surface\",colors:[\"#48e8ff\",\"#063a55\",\"#d4ffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"}];var sa={schemaVersion:1,additive:!0,colorSpace:{basecolor:\"srgb\",tintMask:\"srgb\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"srgb\"},normalConvention:\"OpenGL +Y\",tinting:\"tint-mask multiplied by selected creature palette\",collections:[{id:\"creature-forged-realms\",packetId:\"coach-creature-forged-realms-v1\",displayName:\"Coach Creature Skins \\u2014 Forged Realms\",description:\"Additional fantasy\\u2013science-fiction creature skin unlocks for the Coach app. Separate from the original fitness-material battle-pass set.\",runtimeNotes:{normalConvention:\"OpenGL (+Y)\",tinting:\"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",recommendedColorSpace:{basecolor:\"sRGB\",tintMask:\"sRGB\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"sRGB\"}}},{id:\"creature-celestial-rift\",packetId:\"coach-creature-celestial-rift-v1\",displayName:\"Coach Creature Skins \\u2014 Celestial Rift\",description:\"A second non-overlapping set of fantasy\\u2013science-fiction creature skin unlocks for the Coach app. Additive to Forged Realms and the original fitness-material set.\",runtimeNotes:{normalConvention:\"OpenGL (+Y)\",tinting:\"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",recommendedColorSpace:{basecolor:\"sRGB\",tintMask:\"sRGB\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"sRGB\"}}}],skins:[{id:\"creature-starforged-plate\",displayName:\"Starforged Plate\",track:\"chest\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Metal009\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal009\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-starforged-plate/basecolor.webp\",bytes:8170,sha256:\"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"},normal:{file:\"textures/creature-starforged-plate/normal.webp\",bytes:878,sha256:\"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"},roughness:{file:\"textures/creature-starforged-plate/roughness.webp\",bytes:16326,sha256:\"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"},height:{file:\"textures/creature-starforged-plate/height.webp\",bytes:37602,sha256:\"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"},metalness:{file:\"textures/creature-starforged-plate/metalness.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-starforged-plate/tint-mask.webp\",bytes:47604,sha256:\"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"},preview:{file:\"textures/creature-starforged-plate/preview.webp\",bytes:9336,sha256:\"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"}},runtime:{unlock:{level:1,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-starforged-plate/basecolor\",bytes:8170,sha256:\"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"},normal:{entry:\"creature-starforged-plate/normal\",bytes:878,sha256:\"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"},roughness:{entry:\"creature-starforged-plate/roughness\",bytes:16326,sha256:\"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"},height:{entry:\"creature-starforged-plate/height\",bytes:37602,sha256:\"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"},metalness:{entry:\"creature-starforged-plate/metalness\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-starforged-plate/tintMask\",bytes:47604,sha256:\"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"},preview:{entry:\"creature-starforged-plate/preview\",bytes:9336,sha256:\"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"}}}},{id:\"creature-obsidian-carapace\",displayName:\"Obsidian Carapace\",track:\"chest\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Onyx013\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Onyx013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-obsidian-carapace/basecolor.webp\",bytes:50752,sha256:\"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"},normal:{file:\"textures/creature-obsidian-carapace/normal.webp\",bytes:596,sha256:\"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"},roughness:{file:\"textures/creature-obsidian-carapace/roughness.webp\",bytes:1382,sha256:\"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"},height:{file:\"textures/creature-obsidian-carapace/height.webp\",bytes:2988,sha256:\"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"},tintMask:{file:\"textures/creature-obsidian-carapace/tint-mask.webp\",bytes:65032,sha256:\"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"},preview:{file:\"textures/creature-obsidian-carapace/preview.webp\",bytes:58128,sha256:\"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"}},runtime:{unlock:{level:3,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-obsidian-carapace/basecolor\",bytes:50752,sha256:\"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"},normal:{entry:\"creature-obsidian-carapace/normal\",bytes:596,sha256:\"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"},roughness:{entry:\"creature-obsidian-carapace/roughness\",bytes:1382,sha256:\"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"},height:{entry:\"creature-obsidian-carapace/height\",bytes:2988,sha256:\"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"},tintMask:{entry:\"creature-obsidian-carapace/tintMask\",bytes:65032,sha256:\"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"},preview:{entry:\"creature-obsidian-carapace/preview\",bytes:58128,sha256:\"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"}}}},{id:\"creature-hive-armor\",displayName:\"Hive Armor\",track:\"chest\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"MetalPlates013\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/MetalPlates013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-hive-armor/basecolor.webp\",bytes:24764,sha256:\"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"},normal:{file:\"textures/creature-hive-armor/normal.webp\",bytes:14104,sha256:\"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"},roughness:{file:\"textures/creature-hive-armor/roughness.webp\",bytes:44122,sha256:\"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"},height:{file:\"textures/creature-hive-armor/height.webp\",bytes:9344,sha256:\"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"},ao:{file:\"textures/creature-hive-armor/ao.webp\",bytes:5394,sha256:\"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"},metalness:{file:\"textures/creature-hive-armor/metalness.webp\",bytes:38740,sha256:\"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"},tintMask:{file:\"textures/creature-hive-armor/tint-mask.webp\",bytes:61850,sha256:\"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"},preview:{file:\"textures/creature-hive-armor/preview.webp\",bytes:30780,sha256:\"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"}},runtime:{unlock:{level:5,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-hive-armor/basecolor\",bytes:24764,sha256:\"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"},normal:{entry:\"creature-hive-armor/normal\",bytes:14104,sha256:\"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"},roughness:{entry:\"creature-hive-armor/roughness\",bytes:44122,sha256:\"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"},height:{entry:\"creature-hive-armor/height\",bytes:9344,sha256:\"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"},ao:{entry:\"creature-hive-armor/ao\",bytes:5394,sha256:\"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"},metalness:{entry:\"creature-hive-armor/metalness\",bytes:38740,sha256:\"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"},tintMask:{entry:\"creature-hive-armor/tintMask\",bytes:61850,sha256:\"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"},preview:{entry:\"creature-hive-armor/preview\",bytes:30780,sha256:\"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"}}}},{id:\"creature-magma-scale\",displayName:\"Magma Scale\",track:\"quads\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Lava004\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Lava004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-magma-scale/basecolor.webp\",bytes:34804,sha256:\"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"},normal:{file:\"textures/creature-magma-scale/normal.webp\",bytes:55692,sha256:\"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"},roughness:{file:\"textures/creature-magma-scale/roughness.webp\",bytes:22680,sha256:\"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"},height:{file:\"textures/creature-magma-scale/height.webp\",bytes:12946,sha256:\"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"},emissive:{file:\"textures/creature-magma-scale/emissive.webp\",bytes:41142,sha256:\"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"},tintMask:{file:\"textures/creature-magma-scale/tint-mask.webp\",bytes:41404,sha256:\"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"},preview:{file:\"textures/creature-magma-scale/preview.webp\",bytes:38666,sha256:\"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"}},runtime:{unlock:{level:1,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-magma-scale/basecolor\",bytes:34804,sha256:\"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"},normal:{entry:\"creature-magma-scale/normal\",bytes:55692,sha256:\"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"},roughness:{entry:\"creature-magma-scale/roughness\",bytes:22680,sha256:\"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"},height:{entry:\"creature-magma-scale/height\",bytes:12946,sha256:\"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"},emissive:{entry:\"creature-magma-scale/emissive\",bytes:41142,sha256:\"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"},tintMask:{entry:\"creature-magma-scale/tintMask\",bytes:41404,sha256:\"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"},preview:{entry:\"creature-magma-scale/preview\",bytes:38666,sha256:\"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"}}}},{id:\"creature-frost-veins\",displayName:\"Frost Veins\",track:\"quads\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Ice003\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Ice003\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-frost-veins/basecolor.webp\",bytes:22338,sha256:\"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"},normal:{file:\"textures/creature-frost-veins/normal.webp\",bytes:4622,sha256:\"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"},roughness:{file:\"textures/creature-frost-veins/roughness.webp\",bytes:13946,sha256:\"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"},height:{file:\"textures/creature-frost-veins/height.webp\",bytes:12916,sha256:\"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"},tintMask:{file:\"textures/creature-frost-veins/tint-mask.webp\",bytes:26114,sha256:\"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"},preview:{file:\"textures/creature-frost-veins/preview.webp\",bytes:26196,sha256:\"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"}},runtime:{unlock:{level:3,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-frost-veins/basecolor\",bytes:22338,sha256:\"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"},normal:{entry:\"creature-frost-veins/normal\",bytes:4622,sha256:\"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"},roughness:{entry:\"creature-frost-veins/roughness\",bytes:13946,sha256:\"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"},height:{entry:\"creature-frost-veins/height\",bytes:12916,sha256:\"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"},tintMask:{entry:\"creature-frost-veins/tintMask\",bytes:26114,sha256:\"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"},preview:{entry:\"creature-frost-veins/preview\",bytes:26196,sha256:\"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"}}}},{id:\"creature-storm-carbon\",displayName:\"Storm Carbon\",track:\"quads\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"Fabric004\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Fabric004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-storm-carbon/basecolor.webp\",bytes:4186,sha256:\"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"},normal:{file:\"textures/creature-storm-carbon/normal.webp\",bytes:21570,sha256:\"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"},roughness:{file:\"textures/creature-storm-carbon/roughness.webp\",bytes:13466,sha256:\"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"},height:{file:\"textures/creature-storm-carbon/height.webp\",bytes:10398,sha256:\"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"},metalness:{file:\"textures/creature-storm-carbon/metalness.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-storm-carbon/tint-mask.webp\",bytes:11298,sha256:\"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"},preview:{file:\"textures/creature-storm-carbon/preview.webp\",bytes:4638,sha256:\"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"}},runtime:{unlock:{level:5,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-storm-carbon/basecolor\",bytes:4186,sha256:\"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"},normal:{entry:\"creature-storm-carbon/normal\",bytes:21570,sha256:\"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"},roughness:{entry:\"creature-storm-carbon/roughness\",bytes:13466,sha256:\"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"},height:{entry:\"creature-storm-carbon/height\",bytes:10398,sha256:\"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"},metalness:{entry:\"creature-storm-carbon/metalness\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-storm-carbon/tintMask\",bytes:11298,sha256:\"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"},preview:{entry:\"creature-storm-carbon/preview\",bytes:4638,sha256:\"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"}}}},{id:\"creature-ancient-bark\",displayName:\"Ancient Bark\",track:\"glutes\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Bark015\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Bark015\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ancient-bark/basecolor.webp\",bytes:76532,sha256:\"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"},normal:{file:\"textures/creature-ancient-bark/normal.webp\",bytes:81960,sha256:\"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"},roughness:{file:\"textures/creature-ancient-bark/roughness.webp\",bytes:30736,sha256:\"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"},height:{file:\"textures/creature-ancient-bark/height.webp\",bytes:12470,sha256:\"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"},ao:{file:\"textures/creature-ancient-bark/ao.webp\",bytes:62736,sha256:\"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"},tintMask:{file:\"textures/creature-ancient-bark/tint-mask.webp\",bytes:84688,sha256:\"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"},preview:{file:\"textures/creature-ancient-bark/preview.webp\",bytes:85306,sha256:\"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"}},runtime:{unlock:{level:1,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-ancient-bark/basecolor\",bytes:76532,sha256:\"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"},normal:{entry:\"creature-ancient-bark/normal\",bytes:81960,sha256:\"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"},roughness:{entry:\"creature-ancient-bark/roughness\",bytes:30736,sha256:\"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"},height:{entry:\"creature-ancient-bark/height\",bytes:12470,sha256:\"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"},ao:{entry:\"creature-ancient-bark/ao\",bytes:62736,sha256:\"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"},tintMask:{entry:\"creature-ancient-bark/tintMask\",bytes:84688,sha256:\"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"},preview:{entry:\"creature-ancient-bark/preview\",bytes:85306,sha256:\"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"}}}},{id:\"creature-mossback\",displayName:\"Mossback\",track:\"glutes\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bark013\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bark013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mossback/basecolor.webp\",bytes:62984,sha256:\"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"},normal:{file:\"textures/creature-mossback/normal.webp\",bytes:74686,sha256:\"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"},roughness:{file:\"textures/creature-mossback/roughness.webp\",bytes:42160,sha256:\"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"},height:{file:\"textures/creature-mossback/height.webp\",bytes:23126,sha256:\"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"},ao:{file:\"textures/creature-mossback/ao.webp\",bytes:44736,sha256:\"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"},tintMask:{file:\"textures/creature-mossback/tint-mask.webp\",bytes:71276,sha256:\"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"},preview:{file:\"textures/creature-mossback/preview.webp\",bytes:71140,sha256:\"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"}},runtime:{unlock:{level:3,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-mossback/basecolor\",bytes:62984,sha256:\"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"},normal:{entry:\"creature-mossback/normal\",bytes:74686,sha256:\"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"},roughness:{entry:\"creature-mossback/roughness\",bytes:42160,sha256:\"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"},height:{entry:\"creature-mossback/height\",bytes:23126,sha256:\"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"},ao:{entry:\"creature-mossback/ao\",bytes:44736,sha256:\"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"},tintMask:{entry:\"creature-mossback/tintMask\",bytes:71276,sha256:\"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"},preview:{entry:\"creature-mossback/preview\",bytes:71140,sha256:\"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"}}}},{id:\"creature-rootbound\",displayName:\"Rootbound Hide\",track:\"glutes\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Ground076\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Ground076\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-rootbound/basecolor.webp\",bytes:80318,sha256:\"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"},normal:{file:\"textures/creature-rootbound/normal.webp\",bytes:68262,sha256:\"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"},roughness:{file:\"textures/creature-rootbound/roughness.webp\",bytes:52578,sha256:\"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"},height:{file:\"textures/creature-rootbound/height.webp\",bytes:23352,sha256:\"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"},ao:{file:\"textures/creature-rootbound/ao.webp\",bytes:66244,sha256:\"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"},tintMask:{file:\"textures/creature-rootbound/tint-mask.webp\",bytes:88550,sha256:\"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"},preview:{file:\"textures/creature-rootbound/preview.webp\",bytes:86566,sha256:\"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"}},runtime:{unlock:{level:5,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-rootbound/basecolor\",bytes:80318,sha256:\"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"},normal:{entry:\"creature-rootbound/normal\",bytes:68262,sha256:\"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"},roughness:{entry:\"creature-rootbound/roughness\",bytes:52578,sha256:\"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"},height:{entry:\"creature-rootbound/height\",bytes:23352,sha256:\"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"},ao:{entry:\"creature-rootbound/ao\",bytes:66244,sha256:\"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"},tintMask:{entry:\"creature-rootbound/tintMask\",bytes:88550,sha256:\"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"},preview:{entry:\"creature-rootbound/preview\",bytes:86566,sha256:\"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"}}}},{id:\"creature-hammered-bronze\",displayName:\"Hammered Bronze Golem\",track:\"arms\",realm:\"hybrid\",rarity:\"rare\",sourceAssetId:\"Metal008\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal008\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-hammered-bronze/basecolor.webp\",bytes:21270,sha256:\"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"},normal:{file:\"textures/creature-hammered-bronze/normal.webp\",bytes:1860,sha256:\"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"},roughness:{file:\"textures/creature-hammered-bronze/roughness.webp\",bytes:22538,sha256:\"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"},height:{file:\"textures/creature-hammered-bronze/height.webp\",bytes:3670,sha256:\"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"},metalness:{file:\"textures/creature-hammered-bronze/metalness.webp\",bytes:3062,sha256:\"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"},tintMask:{file:\"textures/creature-hammered-bronze/tint-mask.webp\",bytes:69154,sha256:\"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"},preview:{file:\"textures/creature-hammered-bronze/preview.webp\",bytes:26964,sha256:\"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"}},runtime:{unlock:{level:1,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-hammered-bronze/basecolor\",bytes:21270,sha256:\"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"},normal:{entry:\"creature-hammered-bronze/normal\",bytes:1860,sha256:\"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"},roughness:{entry:\"creature-hammered-bronze/roughness\",bytes:22538,sha256:\"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"},height:{entry:\"creature-hammered-bronze/height\",bytes:3670,sha256:\"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"},metalness:{entry:\"creature-hammered-bronze/metalness\",bytes:3062,sha256:\"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"},tintMask:{entry:\"creature-hammered-bronze/tintMask\",bytes:69154,sha256:\"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"},preview:{entry:\"creature-hammered-bronze/preview\",bytes:26964,sha256:\"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"}}}},{id:\"creature-rope-sinew\",displayName:\"Rope Sinew\",track:\"arms\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rope001\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rope001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-rope-sinew/basecolor.webp\",bytes:29552,sha256:\"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"},normal:{file:\"textures/creature-rope-sinew/normal.webp\",bytes:58934,sha256:\"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"},roughness:{file:\"textures/creature-rope-sinew/roughness.webp\",bytes:12446,sha256:\"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"},height:{file:\"textures/creature-rope-sinew/height.webp\",bytes:8678,sha256:\"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"},metalness:{file:\"textures/creature-rope-sinew/metalness.webp\",bytes:530,sha256:\"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"},tintMask:{file:\"textures/creature-rope-sinew/tint-mask.webp\",bytes:45544,sha256:\"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"},preview:{file:\"textures/creature-rope-sinew/preview.webp\",bytes:35204,sha256:\"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"}},runtime:{unlock:{level:3,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-rope-sinew/basecolor\",bytes:29552,sha256:\"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"},normal:{entry:\"creature-rope-sinew/normal\",bytes:58934,sha256:\"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"},roughness:{entry:\"creature-rope-sinew/roughness\",bytes:12446,sha256:\"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"},height:{entry:\"creature-rope-sinew/height\",bytes:8678,sha256:\"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"},metalness:{entry:\"creature-rope-sinew/metalness\",bytes:530,sha256:\"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"},tintMask:{entry:\"creature-rope-sinew/tintMask\",bytes:45544,sha256:\"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"},preview:{entry:\"creature-rope-sinew/preview\",bytes:35204,sha256:\"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"}}}},{id:\"creature-runic-leather\",displayName:\"Runic Leather\",track:\"arms\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Leather032\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Leather032\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-runic-leather/basecolor.webp\",bytes:28382,sha256:\"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"},normal:{file:\"textures/creature-runic-leather/normal.webp\",bytes:7148,sha256:\"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"},roughness:{file:\"textures/creature-runic-leather/roughness.webp\",bytes:85118,sha256:\"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"},height:{file:\"textures/creature-runic-leather/height.webp\",bytes:90332,sha256:\"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"},tintMask:{file:\"textures/creature-runic-leather/tint-mask.webp\",bytes:89338,sha256:\"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"},preview:{file:\"textures/creature-runic-leather/preview.webp\",bytes:35058,sha256:\"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"}},runtime:{unlock:{level:5,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-runic-leather/basecolor\",bytes:28382,sha256:\"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"},normal:{entry:\"creature-runic-leather/normal\",bytes:7148,sha256:\"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"},roughness:{entry:\"creature-runic-leather/roughness\",bytes:85118,sha256:\"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"},height:{entry:\"creature-runic-leather/height\",bytes:90332,sha256:\"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"},tintMask:{entry:\"creature-runic-leather/tintMask\",bytes:89338,sha256:\"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"},preview:{entry:\"creature-runic-leather/preview\",bytes:35058,sha256:\"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"}}}},{id:\"creature-crystal-core\",displayName:\"Crystal Core\",track:\"yoga\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Onyx006\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Onyx006\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-crystal-core/basecolor.webp\",bytes:50726,sha256:\"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"},normal:{file:\"textures/creature-crystal-core/normal.webp\",bytes:572,sha256:\"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"},roughness:{file:\"textures/creature-crystal-core/roughness.webp\",bytes:15314,sha256:\"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"},height:{file:\"textures/creature-crystal-core/height.webp\",bytes:2740,sha256:\"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"},tintMask:{file:\"textures/creature-crystal-core/tint-mask.webp\",bytes:51868,sha256:\"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"},preview:{file:\"textures/creature-crystal-core/preview.webp\",bytes:56978,sha256:\"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"}},runtime:{unlock:{level:1,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-crystal-core/basecolor\",bytes:50726,sha256:\"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"},normal:{entry:\"creature-crystal-core/normal\",bytes:572,sha256:\"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"},roughness:{entry:\"creature-crystal-core/roughness\",bytes:15314,sha256:\"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"},height:{entry:\"creature-crystal-core/height\",bytes:2740,sha256:\"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"},tintMask:{entry:\"creature-crystal-core/tintMask\",bytes:51868,sha256:\"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"},preview:{entry:\"creature-crystal-core/preview\",bytes:56978,sha256:\"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"}}}},{id:\"creature-moon-marble\",displayName:\"Moon Marble\",track:\"yoga\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Marble016\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Marble016\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-moon-marble/basecolor.webp\",bytes:60346,sha256:\"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"},normal:{file:\"textures/creature-moon-marble/normal.webp\",bytes:558,sha256:\"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"},roughness:{file:\"textures/creature-moon-marble/roughness.webp\",bytes:3740,sha256:\"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"},height:{file:\"textures/creature-moon-marble/height.webp\",bytes:54702,sha256:\"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"},tintMask:{file:\"textures/creature-moon-marble/tint-mask.webp\",bytes:74936,sha256:\"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"},preview:{file:\"textures/creature-moon-marble/preview.webp\",bytes:68934,sha256:\"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"}},runtime:{unlock:{level:3,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-moon-marble/basecolor\",bytes:60346,sha256:\"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"},normal:{entry:\"creature-moon-marble/normal\",bytes:558,sha256:\"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"},roughness:{entry:\"creature-moon-marble/roughness\",bytes:3740,sha256:\"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"},height:{entry:\"creature-moon-marble/height\",bytes:54702,sha256:\"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"},tintMask:{entry:\"creature-moon-marble/tintMask\",bytes:74936,sha256:\"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"},preview:{entry:\"creature-moon-marble/preview\",bytes:68934,sha256:\"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"}}}},{id:\"creature-fae-petal\",displayName:\"Fae Petal\",track:\"yoga\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"FlowerSet001\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/FlowerSet001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-fae-petal/basecolor.webp\",bytes:17208,sha256:\"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"},normal:{file:\"textures/creature-fae-petal/normal.webp\",bytes:19898,sha256:\"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"},roughness:{file:\"textures/creature-fae-petal/roughness.webp\",bytes:6054,sha256:\"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"},height:{file:\"textures/creature-fae-petal/height.webp\",bytes:6176,sha256:\"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"},ao:{file:\"textures/creature-fae-petal/ao.webp\",bytes:8140,sha256:\"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"},opacity:{file:\"textures/creature-fae-petal/opacity.webp\",bytes:9784,sha256:\"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"},tintMask:{file:\"textures/creature-fae-petal/tint-mask.webp\",bytes:15026,sha256:\"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"},preview:{file:\"textures/creature-fae-petal/preview.webp\",bytes:19274,sha256:\"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"}},runtime:{unlock:{level:5,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-fae-petal/basecolor\",bytes:17208,sha256:\"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"},normal:{entry:\"creature-fae-petal/normal\",bytes:19898,sha256:\"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"},roughness:{entry:\"creature-fae-petal/roughness\",bytes:6054,sha256:\"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"},height:{entry:\"creature-fae-petal/height\",bytes:6176,sha256:\"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"},ao:{entry:\"creature-fae-petal/ao\",bytes:8140,sha256:\"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"},opacity:{entry:\"creature-fae-petal/opacity\",bytes:9784,sha256:\"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"},tintMask:{entry:\"creature-fae-petal/tintMask\",bytes:15026,sha256:\"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"},preview:{entry:\"creature-fae-petal/preview\",bytes:19274,sha256:\"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"}}}},{id:\"creature-dragon-scale\",displayName:\"Dragon Scale\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"RoofingTiles013A\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/RoofingTiles013A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-dragon-scale/basecolor.webp\",bytes:2668,sha256:\"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"},normal:{file:\"textures/creature-dragon-scale/normal.webp\",bytes:20688,sha256:\"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"},roughness:{file:\"textures/creature-dragon-scale/roughness.webp\",bytes:16142,sha256:\"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"},height:{file:\"textures/creature-dragon-scale/height.webp\",bytes:8400,sha256:\"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"},ao:{file:\"textures/creature-dragon-scale/ao.webp\",bytes:14048,sha256:\"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"},opacity:{file:\"textures/creature-dragon-scale/opacity.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-dragon-scale/tint-mask.webp\",bytes:33612,sha256:\"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"},preview:{file:\"textures/creature-dragon-scale/preview.webp\",bytes:3430,sha256:\"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"}},runtime:{unlock:{level:1,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-dragon-scale/basecolor\",bytes:2668,sha256:\"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"},normal:{entry:\"creature-dragon-scale/normal\",bytes:20688,sha256:\"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"},roughness:{entry:\"creature-dragon-scale/roughness\",bytes:16142,sha256:\"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"},height:{entry:\"creature-dragon-scale/height\",bytes:8400,sha256:\"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"},ao:{entry:\"creature-dragon-scale/ao\",bytes:14048,sha256:\"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"},opacity:{entry:\"creature-dragon-scale/opacity\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-dragon-scale/tintMask\",bytes:33612,sha256:\"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"},preview:{entry:\"creature-dragon-scale/preview\",bytes:3430,sha256:\"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"}}}},{id:\"creature-bamboo-hide\",displayName:\"Bamboo Hide\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bamboo001A\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bamboo001A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-bamboo-hide/basecolor.webp\",bytes:11654,sha256:\"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"},normal:{file:\"textures/creature-bamboo-hide/normal.webp\",bytes:20194,sha256:\"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"},roughness:{file:\"textures/creature-bamboo-hide/roughness.webp\",bytes:12634,sha256:\"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"},height:{file:\"textures/creature-bamboo-hide/height.webp\",bytes:7926,sha256:\"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"},ao:{file:\"textures/creature-bamboo-hide/ao.webp\",bytes:7788,sha256:\"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"},opacity:{file:\"textures/creature-bamboo-hide/opacity.webp\",bytes:2412,sha256:\"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"},tintMask:{file:\"textures/creature-bamboo-hide/tint-mask.webp\",bytes:37906,sha256:\"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"},preview:{file:\"textures/creature-bamboo-hide/preview.webp\",bytes:14130,sha256:\"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"}},runtime:{unlock:{level:3,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-bamboo-hide/basecolor\",bytes:11654,sha256:\"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"},normal:{entry:\"creature-bamboo-hide/normal\",bytes:20194,sha256:\"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"},roughness:{entry:\"creature-bamboo-hide/roughness\",bytes:12634,sha256:\"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"},height:{entry:\"creature-bamboo-hide/height\",bytes:7926,sha256:\"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"},ao:{entry:\"creature-bamboo-hide/ao\",bytes:7788,sha256:\"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"},opacity:{entry:\"creature-bamboo-hide/opacity\",bytes:2412,sha256:\"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"},tintMask:{entry:\"creature-bamboo-hide/tintMask\",bytes:37906,sha256:\"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"},preview:{entry:\"creature-bamboo-hide/preview\",bytes:14130,sha256:\"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"}}}},{id:\"creature-ivory-chitin\",displayName:\"Ivory Chitin\",track:\"martial-arts\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Marble021\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Marble021\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ivory-chitin/basecolor.webp\",bytes:15470,sha256:\"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"},normal:{file:\"textures/creature-ivory-chitin/normal.webp\",bytes:546,sha256:\"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"},roughness:{file:\"textures/creature-ivory-chitin/roughness.webp\",bytes:3860,sha256:\"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"},height:{file:\"textures/creature-ivory-chitin/height.webp\",bytes:50360,sha256:\"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"},tintMask:{file:\"textures/creature-ivory-chitin/tint-mask.webp\",bytes:97890,sha256:\"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"},preview:{file:\"textures/creature-ivory-chitin/preview.webp\",bytes:19758,sha256:\"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"}},runtime:{unlock:{level:5,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-ivory-chitin/basecolor\",bytes:15470,sha256:\"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"},normal:{entry:\"creature-ivory-chitin/normal\",bytes:546,sha256:\"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"},roughness:{entry:\"creature-ivory-chitin/roughness\",bytes:3860,sha256:\"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"},height:{entry:\"creature-ivory-chitin/height\",bytes:50360,sha256:\"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"},tintMask:{entry:\"creature-ivory-chitin/tintMask\",bytes:97890,sha256:\"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"},preview:{entry:\"creature-ivory-chitin/preview\",bytes:19758,sha256:\"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"}}}},{id:\"creature-circuit-hide\",displayName:\"Circuit Hide\",track:\"cardio\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Chip001\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Chip001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-circuit-hide/basecolor.webp\",bytes:40778,sha256:\"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"},normal:{file:\"textures/creature-circuit-hide/normal.webp\",bytes:27864,sha256:\"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"},roughness:{file:\"textures/creature-circuit-hide/roughness.webp\",bytes:26718,sha256:\"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"},height:{file:\"textures/creature-circuit-hide/height.webp\",bytes:23204,sha256:\"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"},metalness:{file:\"textures/creature-circuit-hide/metalness.webp\",bytes:24542,sha256:\"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"},tintMask:{file:\"textures/creature-circuit-hide/tint-mask.webp\",bytes:37986,sha256:\"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"},preview:{file:\"textures/creature-circuit-hide/preview.webp\",bytes:44810,sha256:\"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"}},runtime:{unlock:{level:1,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-circuit-hide/basecolor\",bytes:40778,sha256:\"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"},normal:{entry:\"creature-circuit-hide/normal\",bytes:27864,sha256:\"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"},roughness:{entry:\"creature-circuit-hide/roughness\",bytes:26718,sha256:\"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"},height:{entry:\"creature-circuit-hide/height\",bytes:23204,sha256:\"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"},metalness:{entry:\"creature-circuit-hide/metalness\",bytes:24542,sha256:\"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"},tintMask:{entry:\"creature-circuit-hide/tintMask\",bytes:37986,sha256:\"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"},preview:{entry:\"creature-circuit-hide/preview\",bytes:44810,sha256:\"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"}}}},{id:\"creature-reactor-rust\",displayName:\"Reactor Rust\",track:\"cardio\",realm:\"sci-fi\",rarity:\"epic\",sourceAssetId:\"Metal063\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Metal063\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-reactor-rust/basecolor.webp\",bytes:11264,sha256:\"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"},normal:{file:\"textures/creature-reactor-rust/normal.webp\",bytes:550,sha256:\"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"},roughness:{file:\"textures/creature-reactor-rust/roughness.webp\",bytes:29148,sha256:\"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"},height:{file:\"textures/creature-reactor-rust/height.webp\",bytes:37754,sha256:\"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"},metalness:{file:\"textures/creature-reactor-rust/metalness.webp\",bytes:43140,sha256:\"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"},tintMask:{file:\"textures/creature-reactor-rust/tint-mask.webp\",bytes:97766,sha256:\"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"},preview:{file:\"textures/creature-reactor-rust/preview.webp\",bytes:17150,sha256:\"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"}},runtime:{unlock:{level:3,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-reactor-rust/basecolor\",bytes:11264,sha256:\"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"},normal:{entry:\"creature-reactor-rust/normal\",bytes:550,sha256:\"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"},roughness:{entry:\"creature-reactor-rust/roughness\",bytes:29148,sha256:\"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"},height:{entry:\"creature-reactor-rust/height\",bytes:37754,sha256:\"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"},metalness:{entry:\"creature-reactor-rust/metalness\",bytes:43140,sha256:\"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"},tintMask:{entry:\"creature-reactor-rust/tintMask\",bytes:97766,sha256:\"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"},preview:{entry:\"creature-reactor-rust/preview\",bytes:17150,sha256:\"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"}}}},{id:\"creature-aero-mesh\",displayName:\"Aero Mesh\",track:\"cardio\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"MetalWalkway013\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/MetalWalkway013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-aero-mesh/basecolor.webp\",bytes:9048,sha256:\"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"},normal:{file:\"textures/creature-aero-mesh/normal.webp\",bytes:55928,sha256:\"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"},roughness:{file:\"textures/creature-aero-mesh/roughness.webp\",bytes:27652,sha256:\"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"},height:{file:\"textures/creature-aero-mesh/height.webp\",bytes:29354,sha256:\"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"},metalness:{file:\"textures/creature-aero-mesh/metalness.webp\",bytes:1340,sha256:\"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"},opacity:{file:\"textures/creature-aero-mesh/opacity.webp\",bytes:28608,sha256:\"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"},tintMask:{file:\"textures/creature-aero-mesh/tint-mask.webp\",bytes:23612,sha256:\"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"},preview:{file:\"textures/creature-aero-mesh/preview.webp\",bytes:11188,sha256:\"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"}},runtime:{unlock:{level:5,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-aero-mesh/basecolor\",bytes:9048,sha256:\"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"},normal:{entry:\"creature-aero-mesh/normal\",bytes:55928,sha256:\"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"},roughness:{entry:\"creature-aero-mesh/roughness\",bytes:27652,sha256:\"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"},height:{entry:\"creature-aero-mesh/height\",bytes:29354,sha256:\"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"},metalness:{entry:\"creature-aero-mesh/metalness\",bytes:1340,sha256:\"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"},opacity:{entry:\"creature-aero-mesh/opacity\",bytes:28608,sha256:\"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"},tintMask:{entry:\"creature-aero-mesh/tintMask\",bytes:23612,sha256:\"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"},preview:{entry:\"creature-aero-mesh/preview\",bytes:11188,sha256:\"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"}}}},{id:\"creature-zen-sand\",displayName:\"Zen Sand\",track:\"meditation\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Ground097\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Ground097\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-zen-sand/basecolor.webp\",bytes:2566,sha256:\"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"},normal:{file:\"textures/creature-zen-sand/normal.webp\",bytes:25542,sha256:\"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"},roughness:{file:\"textures/creature-zen-sand/roughness.webp\",bytes:9672,sha256:\"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"},height:{file:\"textures/creature-zen-sand/height.webp\",bytes:14040,sha256:\"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"},ao:{file:\"textures/creature-zen-sand/ao.webp\",bytes:2322,sha256:\"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"},tintMask:{file:\"textures/creature-zen-sand/tint-mask.webp\",bytes:27572,sha256:\"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"},preview:{file:\"textures/creature-zen-sand/preview.webp\",bytes:3032,sha256:\"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"}},runtime:{unlock:{level:1,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-zen-sand/basecolor\",bytes:2566,sha256:\"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"},normal:{entry:\"creature-zen-sand/normal\",bytes:25542,sha256:\"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"},roughness:{entry:\"creature-zen-sand/roughness\",bytes:9672,sha256:\"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"},height:{entry:\"creature-zen-sand/height\",bytes:14040,sha256:\"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"},ao:{entry:\"creature-zen-sand/ao\",bytes:2322,sha256:\"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"},tintMask:{entry:\"creature-zen-sand/tintMask\",bytes:27572,sha256:\"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"},preview:{entry:\"creature-zen-sand/preview\",bytes:3032,sha256:\"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"}}}},{id:\"creature-river-stone\",displayName:\"River Stone\",track:\"meditation\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rock020\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rock020\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-river-stone/basecolor.webp\",bytes:49168,sha256:\"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"},normal:{file:\"textures/creature-river-stone/normal.webp\",bytes:99336,sha256:\"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"},roughness:{file:\"textures/creature-river-stone/roughness.webp\",bytes:13096,sha256:\"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"},height:{file:\"textures/creature-river-stone/height.webp\",bytes:9202,sha256:\"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"},ao:{file:\"textures/creature-river-stone/ao.webp\",bytes:22322,sha256:\"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"},tintMask:{file:\"textures/creature-river-stone/tint-mask.webp\",bytes:76542,sha256:\"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"},preview:{file:\"textures/creature-river-stone/preview.webp\",bytes:57812,sha256:\"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"}},runtime:{unlock:{level:3,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-river-stone/basecolor\",bytes:49168,sha256:\"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"},normal:{entry:\"creature-river-stone/normal\",bytes:99336,sha256:\"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"},roughness:{entry:\"creature-river-stone/roughness\",bytes:13096,sha256:\"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"},height:{entry:\"creature-river-stone/height\",bytes:9202,sha256:\"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"},ao:{entry:\"creature-river-stone/ao\",bytes:22322,sha256:\"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"},tintMask:{entry:\"creature-river-stone/tintMask\",bytes:76542,sha256:\"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"},preview:{entry:\"creature-river-stone/preview\",bytes:57812,sha256:\"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"}}}},{id:\"creature-biolume-moss\",displayName:\"Biolume Moss\",track:\"meditation\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Moss002\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Moss002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-biolume-moss/basecolor.webp\",bytes:73648,sha256:\"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"},normal:{file:\"textures/creature-biolume-moss/normal.webp\",bytes:87824,sha256:\"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"},roughness:{file:\"textures/creature-biolume-moss/roughness.webp\",bytes:39816,sha256:\"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"},height:{file:\"textures/creature-biolume-moss/height.webp\",bytes:25618,sha256:\"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"},ao:{file:\"textures/creature-biolume-moss/ao.webp\",bytes:78346,sha256:\"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"},tintMask:{file:\"textures/creature-biolume-moss/tint-mask.webp\",bytes:99630,sha256:\"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"},preview:{file:\"textures/creature-biolume-moss/preview.webp\",bytes:77278,sha256:\"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"}},runtime:{unlock:{level:5,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-biolume-moss/basecolor\",bytes:73648,sha256:\"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"},normal:{entry:\"creature-biolume-moss/normal\",bytes:87824,sha256:\"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"},roughness:{entry:\"creature-biolume-moss/roughness\",bytes:39816,sha256:\"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"},height:{entry:\"creature-biolume-moss/height\",bytes:25618,sha256:\"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"},ao:{entry:\"creature-biolume-moss/ao\",bytes:78346,sha256:\"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"},tintMask:{entry:\"creature-biolume-moss/tintMask\",bytes:99630,sha256:\"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"},preview:{entry:\"creature-biolume-moss/preview\",bytes:77278,sha256:\"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"}}}},{id:\"creature-mirror-knight\",displayName:\"Mirror Knight\",track:\"chest\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Metal048A\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal048A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mirror-knight/basecolor.webp\",bytes:998,sha256:\"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"},normal:{file:\"textures/creature-mirror-knight/normal.webp\",bytes:594,sha256:\"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"},roughness:{file:\"textures/creature-mirror-knight/roughness.webp\",bytes:732,sha256:\"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"},height:{file:\"textures/creature-mirror-knight/height.webp\",bytes:526,sha256:\"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"},metalness:{file:\"textures/creature-mirror-knight/metalness.webp\",bytes:532,sha256:\"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"},tintMask:{file:\"textures/creature-mirror-knight/tint-mask.webp\",bytes:67594,sha256:\"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"},preview:{file:\"textures/creature-mirror-knight/preview.webp\",bytes:1298,sha256:\"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"}},runtime:{unlock:{level:1,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-mirror-knight/basecolor\",bytes:998,sha256:\"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"},normal:{entry:\"creature-mirror-knight/normal\",bytes:594,sha256:\"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"},roughness:{entry:\"creature-mirror-knight/roughness\",bytes:732,sha256:\"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"},height:{entry:\"creature-mirror-knight/height\",bytes:526,sha256:\"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"},metalness:{entry:\"creature-mirror-knight/metalness\",bytes:532,sha256:\"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"},tintMask:{entry:\"creature-mirror-knight/tintMask\",bytes:67594,sha256:\"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"},preview:{entry:\"creature-mirror-knight/preview\",bytes:1298,sha256:\"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"}}}},{id:\"creature-tarnished-crown\",displayName:\"Tarnished Crown\",track:\"chest\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Metal048C\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Metal048C\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-tarnished-crown/basecolor.webp\",bytes:10056,sha256:\"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"},normal:{file:\"textures/creature-tarnished-crown/normal.webp\",bytes:902,sha256:\"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"},roughness:{file:\"textures/creature-tarnished-crown/roughness.webp\",bytes:23264,sha256:\"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"},height:{file:\"textures/creature-tarnished-crown/height.webp\",bytes:6612,sha256:\"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"},metalness:{file:\"textures/creature-tarnished-crown/metalness.webp\",bytes:9582,sha256:\"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"},tintMask:{file:\"textures/creature-tarnished-crown/tint-mask.webp\",bytes:48438,sha256:\"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"},preview:{file:\"textures/creature-tarnished-crown/preview.webp\",bytes:11978,sha256:\"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"}},runtime:{unlock:{level:3,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-tarnished-crown/basecolor\",bytes:10056,sha256:\"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"},normal:{entry:\"creature-tarnished-crown/normal\",bytes:902,sha256:\"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"},roughness:{entry:\"creature-tarnished-crown/roughness\",bytes:23264,sha256:\"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"},height:{entry:\"creature-tarnished-crown/height\",bytes:6612,sha256:\"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"},metalness:{entry:\"creature-tarnished-crown/metalness\",bytes:9582,sha256:\"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"},tintMask:{entry:\"creature-tarnished-crown/tintMask\",bytes:48438,sha256:\"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"},preview:{entry:\"creature-tarnished-crown/preview\",bytes:11978,sha256:\"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"}}}},{id:\"creature-golden-scarab\",displayName:\"Golden Scarab\",track:\"chest\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Metal007\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Metal007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-golden-scarab/basecolor.webp\",bytes:18264,sha256:\"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"},normal:{file:\"textures/creature-golden-scarab/normal.webp\",bytes:1042,sha256:\"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"},roughness:{file:\"textures/creature-golden-scarab/roughness.webp\",bytes:15890,sha256:\"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"},height:{file:\"textures/creature-golden-scarab/height.webp\",bytes:1714,sha256:\"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"},metalness:{file:\"textures/creature-golden-scarab/metalness.webp\",bytes:1280,sha256:\"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"},tintMask:{file:\"textures/creature-golden-scarab/tint-mask.webp\",bytes:65652,sha256:\"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"},preview:{file:\"textures/creature-golden-scarab/preview.webp\",bytes:22470,sha256:\"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"}},runtime:{unlock:{level:5,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-golden-scarab/basecolor\",bytes:18264,sha256:\"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"},normal:{entry:\"creature-golden-scarab/normal\",bytes:1042,sha256:\"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"},roughness:{entry:\"creature-golden-scarab/roughness\",bytes:15890,sha256:\"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"},height:{entry:\"creature-golden-scarab/height\",bytes:1714,sha256:\"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"},metalness:{entry:\"creature-golden-scarab/metalness\",bytes:1280,sha256:\"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"},tintMask:{entry:\"creature-golden-scarab/tintMask\",bytes:65652,sha256:\"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"},preview:{entry:\"creature-golden-scarab/preview\",bytes:22470,sha256:\"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"}}}},{id:\"creature-emberflow\",displayName:\"Emberflow\",track:\"quads\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Lava001\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Lava001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-emberflow/basecolor.webp\",bytes:55502,sha256:\"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"},normal:{file:\"textures/creature-emberflow/normal.webp\",bytes:105598,sha256:\"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"},roughness:{file:\"textures/creature-emberflow/roughness.webp\",bytes:12882,sha256:\"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"},height:{file:\"textures/creature-emberflow/height.webp\",bytes:13998,sha256:\"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"},emissive:{file:\"textures/creature-emberflow/emissive.webp\",bytes:52850,sha256:\"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"},tintMask:{file:\"textures/creature-emberflow/tint-mask.webp\",bytes:51236,sha256:\"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"},preview:{file:\"textures/creature-emberflow/preview.webp\",bytes:57834,sha256:\"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"}},runtime:{unlock:{level:1,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-emberflow/basecolor\",bytes:55502,sha256:\"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"},normal:{entry:\"creature-emberflow/normal\",bytes:105598,sha256:\"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"},roughness:{entry:\"creature-emberflow/roughness\",bytes:12882,sha256:\"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"},height:{entry:\"creature-emberflow/height\",bytes:13998,sha256:\"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"},emissive:{entry:\"creature-emberflow/emissive\",bytes:52850,sha256:\"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"},tintMask:{entry:\"creature-emberflow/tintMask\",bytes:51236,sha256:\"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"},preview:{entry:\"creature-emberflow/preview\",bytes:57834,sha256:\"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"}}}},{id:\"creature-volcanic-heart\",displayName:\"Volcanic Heart\",track:\"quads\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Lava005\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Lava005\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-volcanic-heart/basecolor.webp\",bytes:52804,sha256:\"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"},normal:{file:\"textures/creature-volcanic-heart/normal.webp\",bytes:54736,sha256:\"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"},roughness:{file:\"textures/creature-volcanic-heart/roughness.webp\",bytes:20200,sha256:\"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"},height:{file:\"textures/creature-volcanic-heart/height.webp\",bytes:11990,sha256:\"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"},emissive:{file:\"textures/creature-volcanic-heart/emissive.webp\",bytes:61420,sha256:\"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"},tintMask:{file:\"textures/creature-volcanic-heart/tint-mask.webp\",bytes:53772,sha256:\"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"},preview:{file:\"textures/creature-volcanic-heart/preview.webp\",bytes:51986,sha256:\"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"}},runtime:{unlock:{level:3,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-volcanic-heart/basecolor\",bytes:52804,sha256:\"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"},normal:{entry:\"creature-volcanic-heart/normal\",bytes:54736,sha256:\"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"},roughness:{entry:\"creature-volcanic-heart/roughness\",bytes:20200,sha256:\"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"},height:{entry:\"creature-volcanic-heart/height\",bytes:11990,sha256:\"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"},emissive:{entry:\"creature-volcanic-heart/emissive\",bytes:61420,sha256:\"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"},tintMask:{entry:\"creature-volcanic-heart/tintMask\",bytes:53772,sha256:\"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"},preview:{entry:\"creature-volcanic-heart/preview\",bytes:51986,sha256:\"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"}}}},{id:\"creature-aurora-ice\",displayName:\"Aurora Ice\",track:\"quads\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Ice004\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Ice004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-aurora-ice/basecolor.webp\",bytes:28102,sha256:\"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"},normal:{file:\"textures/creature-aurora-ice/normal.webp\",bytes:9418,sha256:\"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"},roughness:{file:\"textures/creature-aurora-ice/roughness.webp\",bytes:16912,sha256:\"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"},height:{file:\"textures/creature-aurora-ice/height.webp\",bytes:24584,sha256:\"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"},tintMask:{file:\"textures/creature-aurora-ice/tint-mask.webp\",bytes:33120,sha256:\"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"},preview:{file:\"textures/creature-aurora-ice/preview.webp\",bytes:33622,sha256:\"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"}},runtime:{unlock:{level:5,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-aurora-ice/basecolor\",bytes:28102,sha256:\"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"},normal:{entry:\"creature-aurora-ice/normal\",bytes:9418,sha256:\"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"},roughness:{entry:\"creature-aurora-ice/roughness\",bytes:16912,sha256:\"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"},height:{entry:\"creature-aurora-ice/height\",bytes:24584,sha256:\"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"},tintMask:{entry:\"creature-aurora-ice/tintMask\",bytes:33120,sha256:\"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"},preview:{entry:\"creature-aurora-ice/preview\",bytes:33622,sha256:\"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"}}}},{id:\"creature-ironbark\",displayName:\"Ironbark\",track:\"glutes\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Bark014\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Bark014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ironbark/basecolor.webp\",bytes:60524,sha256:\"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"},normal:{file:\"textures/creature-ironbark/normal.webp\",bytes:39240,sha256:\"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"},roughness:{file:\"textures/creature-ironbark/roughness.webp\",bytes:61578,sha256:\"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"},height:{file:\"textures/creature-ironbark/height.webp\",bytes:11624,sha256:\"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"},ao:{file:\"textures/creature-ironbark/ao.webp\",bytes:46376,sha256:\"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"},tintMask:{file:\"textures/creature-ironbark/tint-mask.webp\",bytes:71852,sha256:\"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"},preview:{file:\"textures/creature-ironbark/preview.webp\",bytes:68160,sha256:\"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"}},runtime:{unlock:{level:1,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-ironbark/basecolor\",bytes:60524,sha256:\"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"},normal:{entry:\"creature-ironbark/normal\",bytes:39240,sha256:\"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"},roughness:{entry:\"creature-ironbark/roughness\",bytes:61578,sha256:\"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"},height:{entry:\"creature-ironbark/height\",bytes:11624,sha256:\"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"},ao:{entry:\"creature-ironbark/ao\",bytes:46376,sha256:\"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"},tintMask:{entry:\"creature-ironbark/tintMask\",bytes:71852,sha256:\"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"},preview:{entry:\"creature-ironbark/preview\",bytes:68160,sha256:\"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"}}}},{id:\"creature-oakscale\",displayName:\"Oakscale\",track:\"glutes\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bark012\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bark012\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-oakscale/basecolor.webp\",bytes:71646,sha256:\"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"},normal:{file:\"textures/creature-oakscale/normal.webp\",bytes:122322,sha256:\"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"},roughness:{file:\"textures/creature-oakscale/roughness.webp\",bytes:56118,sha256:\"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"},height:{file:\"textures/creature-oakscale/height.webp\",bytes:36178,sha256:\"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"},ao:{file:\"textures/creature-oakscale/ao.webp\",bytes:59772,sha256:\"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"},tintMask:{file:\"textures/creature-oakscale/tint-mask.webp\",bytes:89244,sha256:\"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"},preview:{file:\"textures/creature-oakscale/preview.webp\",bytes:80436,sha256:\"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"}},runtime:{unlock:{level:3,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-oakscale/basecolor\",bytes:71646,sha256:\"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"},normal:{entry:\"creature-oakscale/normal\",bytes:122322,sha256:\"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"},roughness:{entry:\"creature-oakscale/roughness\",bytes:56118,sha256:\"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"},height:{entry:\"creature-oakscale/height\",bytes:36178,sha256:\"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"},ao:{entry:\"creature-oakscale/ao\",bytes:59772,sha256:\"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"},tintMask:{entry:\"creature-oakscale/tintMask\",bytes:89244,sha256:\"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"},preview:{entry:\"creature-oakscale/preview\",bytes:80436,sha256:\"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"}}}},{id:\"creature-mosswood\",displayName:\"Mosswood\",track:\"glutes\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Bark008\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Bark008\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mosswood/basecolor.webp\",bytes:59368,sha256:\"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"},normal:{file:\"textures/creature-mosswood/normal.webp\",bytes:115452,sha256:\"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"},roughness:{file:\"textures/creature-mosswood/roughness.webp\",bytes:14720,sha256:\"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"},height:{file:\"textures/creature-mosswood/height.webp\",bytes:14968,sha256:\"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"},ao:{file:\"textures/creature-mosswood/ao.webp\",bytes:30290,sha256:\"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"},tintMask:{file:\"textures/creature-mosswood/tint-mask.webp\",bytes:71764,sha256:\"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"},preview:{file:\"textures/creature-mosswood/preview.webp\",bytes:68928,sha256:\"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"}},runtime:{unlock:{level:5,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-mosswood/basecolor\",bytes:59368,sha256:\"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"},normal:{entry:\"creature-mosswood/normal\",bytes:115452,sha256:\"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"},roughness:{entry:\"creature-mosswood/roughness\",bytes:14720,sha256:\"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"},height:{entry:\"creature-mosswood/height\",bytes:14968,sha256:\"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"},ao:{entry:\"creature-mosswood/ao\",bytes:30290,sha256:\"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"},tintMask:{entry:\"creature-mosswood/tintMask\",bytes:71764,sha256:\"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"},preview:{entry:\"creature-mosswood/preview\",bytes:68928,sha256:\"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"}}}},{id:\"creature-blood-onyx\",displayName:\"Blood Onyx\",track:\"arms\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Onyx010\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Onyx010\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-blood-onyx/basecolor.webp\",bytes:49258,sha256:\"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"},normal:{file:\"textures/creature-blood-onyx/normal.webp\",bytes:570,sha256:\"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"},roughness:{file:\"textures/creature-blood-onyx/roughness.webp\",bytes:3160,sha256:\"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"},height:{file:\"textures/creature-blood-onyx/height.webp\",bytes:2910,sha256:\"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"},tintMask:{file:\"textures/creature-blood-onyx/tint-mask.webp\",bytes:55394,sha256:\"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"},preview:{file:\"textures/creature-blood-onyx/preview.webp\",bytes:56594,sha256:\"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"}},runtime:{unlock:{level:1,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-blood-onyx/basecolor\",bytes:49258,sha256:\"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"},normal:{entry:\"creature-blood-onyx/normal\",bytes:570,sha256:\"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"},roughness:{entry:\"creature-blood-onyx/roughness\",bytes:3160,sha256:\"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"},height:{entry:\"creature-blood-onyx/height\",bytes:2910,sha256:\"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"},tintMask:{entry:\"creature-blood-onyx/tintMask\",bytes:55394,sha256:\"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"},preview:{entry:\"creature-blood-onyx/preview\",bytes:56594,sha256:\"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"}}}},{id:\"creature-honey-crystal\",displayName:\"Honey Crystal\",track:\"arms\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Onyx007\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Onyx007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-honey-crystal/basecolor.webp\",bytes:22816,sha256:\"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"},normal:{file:\"textures/creature-honey-crystal/normal.webp\",bytes:560,sha256:\"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"},roughness:{file:\"textures/creature-honey-crystal/roughness.webp\",bytes:3020,sha256:\"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"},height:{file:\"textures/creature-honey-crystal/height.webp\",bytes:2954,sha256:\"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"},tintMask:{file:\"textures/creature-honey-crystal/tint-mask.webp\",bytes:34036,sha256:\"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"},preview:{file:\"textures/creature-honey-crystal/preview.webp\",bytes:26820,sha256:\"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"}},runtime:{unlock:{level:3,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-honey-crystal/basecolor\",bytes:22816,sha256:\"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"},normal:{entry:\"creature-honey-crystal/normal\",bytes:560,sha256:\"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"},roughness:{entry:\"creature-honey-crystal/roughness\",bytes:3020,sha256:\"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"},height:{entry:\"creature-honey-crystal/height\",bytes:2954,sha256:\"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"},tintMask:{entry:\"creature-honey-crystal/tintMask\",bytes:34036,sha256:\"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"},preview:{entry:\"creature-honey-crystal/preview\",bytes:26820,sha256:\"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"}}}},{id:\"creature-void-mirror\",displayName:\"Void Mirror\",track:\"arms\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Onyx014\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Onyx014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-void-mirror/basecolor.webp\",bytes:53930,sha256:\"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"},normal:{file:\"textures/creature-void-mirror/normal.webp\",bytes:576,sha256:\"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"},roughness:{file:\"textures/creature-void-mirror/roughness.webp\",bytes:3126,sha256:\"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"},height:{file:\"textures/creature-void-mirror/height.webp\",bytes:3520,sha256:\"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"},tintMask:{file:\"textures/creature-void-mirror/tint-mask.webp\",bytes:62026,sha256:\"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"},preview:{file:\"textures/creature-void-mirror/preview.webp\",bytes:61294,sha256:\"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"}},runtime:{unlock:{level:5,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-void-mirror/basecolor\",bytes:53930,sha256:\"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"},normal:{entry:\"creature-void-mirror/normal\",bytes:576,sha256:\"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"},roughness:{entry:\"creature-void-mirror/roughness\",bytes:3126,sha256:\"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"},height:{entry:\"creature-void-mirror/height\",bytes:3520,sha256:\"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"},tintMask:{entry:\"creature-void-mirror/tintMask\",bytes:62026,sha256:\"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"},preview:{entry:\"creature-void-mirror/preview\",bytes:61294,sha256:\"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"}}}},{id:\"creature-snow-wraith\",displayName:\"Snow Wraith\",track:\"yoga\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Snow014\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Snow014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-snow-wraith/basecolor.webp\",bytes:6634,sha256:\"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"},normal:{file:\"textures/creature-snow-wraith/normal.webp\",bytes:25340,sha256:\"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"},roughness:{file:\"textures/creature-snow-wraith/roughness.webp\",bytes:11026,sha256:\"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"},height:{file:\"textures/creature-snow-wraith/height.webp\",bytes:11568,sha256:\"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"},ao:{file:\"textures/creature-snow-wraith/ao.webp\",bytes:4376,sha256:\"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"},tintMask:{file:\"textures/creature-snow-wraith/tint-mask.webp\",bytes:47502,sha256:\"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"},preview:{file:\"textures/creature-snow-wraith/preview.webp\",bytes:8238,sha256:\"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"}},runtime:{unlock:{level:1,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-snow-wraith/basecolor\",bytes:6634,sha256:\"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"},normal:{entry:\"creature-snow-wraith/normal\",bytes:25340,sha256:\"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"},roughness:{entry:\"creature-snow-wraith/roughness\",bytes:11026,sha256:\"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"},height:{entry:\"creature-snow-wraith/height\",bytes:11568,sha256:\"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"},ao:{entry:\"creature-snow-wraith/ao\",bytes:4376,sha256:\"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"},tintMask:{entry:\"creature-snow-wraith/tintMask\",bytes:47502,sha256:\"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"},preview:{entry:\"creature-snow-wraith/preview\",bytes:8238,sha256:\"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"}}}},{id:\"creature-moon-drift\",displayName:\"Moon Drift\",track:\"yoga\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Snow008A\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Snow008A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-moon-drift/basecolor.webp\",bytes:1988,sha256:\"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"},normal:{file:\"textures/creature-moon-drift/normal.webp\",bytes:19748,sha256:\"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"},roughness:{file:\"textures/creature-moon-drift/roughness.webp\",bytes:42334,sha256:\"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"},height:{file:\"textures/creature-moon-drift/height.webp\",bytes:10336,sha256:\"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"},ao:{file:\"textures/creature-moon-drift/ao.webp\",bytes:5248,sha256:\"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"},tintMask:{file:\"textures/creature-moon-drift/tint-mask.webp\",bytes:62850,sha256:\"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"},preview:{file:\"textures/creature-moon-drift/preview.webp\",bytes:2558,sha256:\"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"}},runtime:{unlock:{level:3,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-moon-drift/basecolor\",bytes:1988,sha256:\"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"},normal:{entry:\"creature-moon-drift/normal\",bytes:19748,sha256:\"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"},roughness:{entry:\"creature-moon-drift/roughness\",bytes:42334,sha256:\"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"},height:{entry:\"creature-moon-drift/height\",bytes:10336,sha256:\"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"},ao:{entry:\"creature-moon-drift/ao\",bytes:5248,sha256:\"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"},tintMask:{entry:\"creature-moon-drift/tintMask\",bytes:62850,sha256:\"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"},preview:{entry:\"creature-moon-drift/preview\",bytes:2558,sha256:\"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"}}}},{id:\"creature-pearl-spirit\",displayName:\"Pearl Spirit\",track:\"yoga\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Onyx015\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Onyx015\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-pearl-spirit/basecolor.webp\",bytes:29558,sha256:\"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"},normal:{file:\"textures/creature-pearl-spirit/normal.webp\",bytes:578,sha256:\"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"},roughness:{file:\"textures/creature-pearl-spirit/roughness.webp\",bytes:2712,sha256:\"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"},height:{file:\"textures/creature-pearl-spirit/height.webp\",bytes:2632,sha256:\"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"},tintMask:{file:\"textures/creature-pearl-spirit/tint-mask.webp\",bytes:70938,sha256:\"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"},preview:{file:\"textures/creature-pearl-spirit/preview.webp\",bytes:36178,sha256:\"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"}},runtime:{unlock:{level:5,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-pearl-spirit/basecolor\",bytes:29558,sha256:\"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"},normal:{entry:\"creature-pearl-spirit/normal\",bytes:578,sha256:\"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"},roughness:{entry:\"creature-pearl-spirit/roughness\",bytes:2712,sha256:\"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"},height:{entry:\"creature-pearl-spirit/height\",bytes:2632,sha256:\"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"},tintMask:{entry:\"creature-pearl-spirit/tintMask\",bytes:70938,sha256:\"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"},preview:{entry:\"creature-pearl-spirit/preview\",bytes:36178,sha256:\"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"}}}},{id:\"creature-red-desert-scale\",displayName:\"Red Desert Scale\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Rock029\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Rock029\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-red-desert-scale/basecolor.webp\",bytes:51964,sha256:\"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"},normal:{file:\"textures/creature-red-desert-scale/normal.webp\",bytes:120276,sha256:\"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"},roughness:{file:\"textures/creature-red-desert-scale/roughness.webp\",bytes:28424,sha256:\"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"},height:{file:\"textures/creature-red-desert-scale/height.webp\",bytes:8842,sha256:\"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"},ao:{file:\"textures/creature-red-desert-scale/ao.webp\",bytes:19082,sha256:\"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"},tintMask:{file:\"textures/creature-red-desert-scale/tint-mask.webp\",bytes:83596,sha256:\"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"},preview:{file:\"textures/creature-red-desert-scale/preview.webp\",bytes:57218,sha256:\"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"}},runtime:{unlock:{level:1,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-red-desert-scale/basecolor\",bytes:51964,sha256:\"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"},normal:{entry:\"creature-red-desert-scale/normal\",bytes:120276,sha256:\"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"},roughness:{entry:\"creature-red-desert-scale/roughness\",bytes:28424,sha256:\"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"},height:{entry:\"creature-red-desert-scale/height\",bytes:8842,sha256:\"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"},ao:{entry:\"creature-red-desert-scale/ao\",bytes:19082,sha256:\"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"},tintMask:{entry:\"creature-red-desert-scale/tintMask\",bytes:83596,sha256:\"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"},preview:{entry:\"creature-red-desert-scale/preview\",bytes:57218,sha256:\"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"}}}},{id:\"creature-cave-drake\",displayName:\"Cave Drake\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rock035\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rock035\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-cave-drake/basecolor.webp\",bytes:42954,sha256:\"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"},normal:{file:\"textures/creature-cave-drake/normal.webp\",bytes:126116,sha256:\"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"},roughness:{file:\"textures/creature-cave-drake/roughness.webp\",bytes:37152,sha256:\"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"},height:{file:\"textures/creature-cave-drake/height.webp\",bytes:10778,sha256:\"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"},ao:{file:\"textures/creature-cave-drake/ao.webp\",bytes:20824,sha256:\"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"},tintMask:{file:\"textures/creature-cave-drake/tint-mask.webp\",bytes:59714,sha256:\"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"},preview:{file:\"textures/creature-cave-drake/preview.webp\",bytes:52250,sha256:\"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"}},runtime:{unlock:{level:3,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-cave-drake/basecolor\",bytes:42954,sha256:\"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"},normal:{entry:\"creature-cave-drake/normal\",bytes:126116,sha256:\"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"},roughness:{entry:\"creature-cave-drake/roughness\",bytes:37152,sha256:\"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"},height:{entry:\"creature-cave-drake/height\",bytes:10778,sha256:\"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"},ao:{entry:\"creature-cave-drake/ao\",bytes:20824,sha256:\"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"},tintMask:{entry:\"creature-cave-drake/tintMask\",bytes:59714,sha256:\"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"},preview:{entry:\"creature-cave-drake/preview\",bytes:52250,sha256:\"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"}}}},{id:\"creature-sandstone-titan\",displayName:\"Sandstone Titan\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Rock061\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Rock061\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-sandstone-titan/basecolor.webp\",bytes:19630,sha256:\"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"},normal:{file:\"textures/creature-sandstone-titan/normal.webp\",bytes:59070,sha256:\"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"},roughness:{file:\"textures/creature-sandstone-titan/roughness.webp\",bytes:55850,sha256:\"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"},height:{file:\"textures/creature-sandstone-titan/height.webp\",bytes:8100,sha256:\"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"},ao:{file:\"textures/creature-sandstone-titan/ao.webp\",bytes:15348,sha256:\"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"},tintMask:{file:\"textures/creature-sandstone-titan/tint-mask.webp\",bytes:41274,sha256:\"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"},preview:{file:\"textures/creature-sandstone-titan/preview.webp\",bytes:24198,sha256:\"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"}},runtime:{unlock:{level:5,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-sandstone-titan/basecolor\",bytes:19630,sha256:\"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"},normal:{entry:\"creature-sandstone-titan/normal\",bytes:59070,sha256:\"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"},roughness:{entry:\"creature-sandstone-titan/roughness\",bytes:55850,sha256:\"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"},height:{entry:\"creature-sandstone-titan/height\",bytes:8100,sha256:\"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"},ao:{entry:\"creature-sandstone-titan/ao\",bytes:15348,sha256:\"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"},tintMask:{entry:\"creature-sandstone-titan/tintMask\",bytes:41274,sha256:\"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"},preview:{entry:\"creature-sandstone-titan/preview\",bytes:24198,sha256:\"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"}}}},{id:\"creature-neon-mainframe\",displayName:\"Neon Mainframe\",track:\"cardio\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Chip002\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Chip002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-neon-mainframe/basecolor.webp\",bytes:56744,sha256:\"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"},normal:{file:\"textures/creature-neon-mainframe/normal.webp\",bytes:37362,sha256:\"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"},roughness:{file:\"textures/creature-neon-mainframe/roughness.webp\",bytes:41338,sha256:\"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"},height:{file:\"textures/creature-neon-mainframe/height.webp\",bytes:33104,sha256:\"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"},metalness:{file:\"textures/creature-neon-mainframe/metalness.webp\",bytes:45012,sha256:\"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"},tintMask:{file:\"textures/creature-neon-mainframe/tint-mask.webp\",bytes:50140,sha256:\"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"},preview:{file:\"textures/creature-neon-mainframe/preview.webp\",bytes:61118,sha256:\"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"}},runtime:{unlock:{level:1,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-neon-mainframe/basecolor\",bytes:56744,sha256:\"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"},normal:{entry:\"creature-neon-mainframe/normal\",bytes:37362,sha256:\"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"},roughness:{entry:\"creature-neon-mainframe/roughness\",bytes:41338,sha256:\"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"},height:{entry:\"creature-neon-mainframe/height\",bytes:33104,sha256:\"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"},metalness:{entry:\"creature-neon-mainframe/metalness\",bytes:45012,sha256:\"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"},tintMask:{entry:\"creature-neon-mainframe/tintMask\",bytes:50140,sha256:\"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"},preview:{entry:\"creature-neon-mainframe/preview\",bytes:61118,sha256:\"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"}}}},{id:\"creature-reactor-grid\",displayName:\"Reactor Grid\",track:\"cardio\",realm:\"sci-fi\",rarity:\"epic\",sourceAssetId:\"Chip004\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Chip004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-reactor-grid/basecolor.webp\",bytes:68780,sha256:\"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"},normal:{file:\"textures/creature-reactor-grid/normal.webp\",bytes:30258,sha256:\"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"},roughness:{file:\"textures/creature-reactor-grid/roughness.webp\",bytes:28938,sha256:\"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"},height:{file:\"textures/creature-reactor-grid/height.webp\",bytes:18820,sha256:\"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"},metalness:{file:\"textures/creature-reactor-grid/metalness.webp\",bytes:8980,sha256:\"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"},tintMask:{file:\"textures/creature-reactor-grid/tint-mask.webp\",bytes:50226,sha256:\"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"},preview:{file:\"textures/creature-reactor-grid/preview.webp\",bytes:70556,sha256:\"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"}},runtime:{unlock:{level:3,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-reactor-grid/basecolor\",bytes:68780,sha256:\"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"},normal:{entry:\"creature-reactor-grid/normal\",bytes:30258,sha256:\"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"},roughness:{entry:\"creature-reactor-grid/roughness\",bytes:28938,sha256:\"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"},height:{entry:\"creature-reactor-grid/height\",bytes:18820,sha256:\"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"},metalness:{entry:\"creature-reactor-grid/metalness\",bytes:8980,sha256:\"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"},tintMask:{entry:\"creature-reactor-grid/tintMask\",bytes:50226,sha256:\"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"},preview:{entry:\"creature-reactor-grid/preview\",bytes:70556,sha256:\"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"}}}},{id:\"creature-quantum-core\",displayName:\"Quantum Core\",track:\"cardio\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"Chip007\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Chip007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-quantum-core/basecolor.webp\",bytes:51286,sha256:\"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"},normal:{file:\"textures/creature-quantum-core/normal.webp\",bytes:31990,sha256:\"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"},roughness:{file:\"textures/creature-quantum-core/roughness.webp\",bytes:31284,sha256:\"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"},height:{file:\"textures/creature-quantum-core/height.webp\",bytes:29580,sha256:\"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"},metalness:{file:\"textures/creature-quantum-core/metalness.webp\",bytes:38108,sha256:\"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"},tintMask:{file:\"textures/creature-quantum-core/tint-mask.webp\",bytes:41968,sha256:\"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"},preview:{file:\"textures/creature-quantum-core/preview.webp\",bytes:56764,sha256:\"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"}},runtime:{unlock:{level:5,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-quantum-core/basecolor\",bytes:51286,sha256:\"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"},normal:{entry:\"creature-quantum-core/normal\",bytes:31990,sha256:\"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"},roughness:{entry:\"creature-quantum-core/roughness\",bytes:31284,sha256:\"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"},height:{entry:\"creature-quantum-core/height\",bytes:29580,sha256:\"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"},metalness:{entry:\"creature-quantum-core/metalness\",bytes:38108,sha256:\"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"},tintMask:{entry:\"creature-quantum-core/tintMask\",bytes:41968,sha256:\"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"},preview:{entry:\"creature-quantum-core/preview\",bytes:56764,sha256:\"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"}}}},{id:\"creature-celestial-mosaic\",displayName:\"Celestial Mosaic\",track:\"meditation\",realm:\"hybrid\",rarity:\"rare\",sourceAssetId:\"Tiles070\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Tiles070\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-celestial-mosaic/basecolor.webp\",bytes:72806,sha256:\"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"},normal:{file:\"textures/creature-celestial-mosaic/normal.webp\",bytes:40154,sha256:\"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"},roughness:{file:\"textures/creature-celestial-mosaic/roughness.webp\",bytes:47808,sha256:\"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"},height:{file:\"textures/creature-celestial-mosaic/height.webp\",bytes:38486,sha256:\"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"},ao:{file:\"textures/creature-celestial-mosaic/ao.webp\",bytes:46104,sha256:\"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"},tintMask:{file:\"textures/creature-celestial-mosaic/tint-mask.webp\",bytes:63262,sha256:\"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"},preview:{file:\"textures/creature-celestial-mosaic/preview.webp\",bytes:76982,sha256:\"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"}},runtime:{unlock:{level:1,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-celestial-mosaic/basecolor\",bytes:72806,sha256:\"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"},normal:{entry:\"creature-celestial-mosaic/normal\",bytes:40154,sha256:\"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"},roughness:{entry:\"creature-celestial-mosaic/roughness\",bytes:47808,sha256:\"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"},height:{entry:\"creature-celestial-mosaic/height\",bytes:38486,sha256:\"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"},ao:{entry:\"creature-celestial-mosaic/ao\",bytes:46104,sha256:\"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"},tintMask:{entry:\"creature-celestial-mosaic/tintMask\",bytes:63262,sha256:\"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"},preview:{entry:\"creature-celestial-mosaic/preview\",bytes:76982,sha256:\"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"}}}},{id:\"creature-shellborn\",displayName:\"Shellborn\",track:\"meditation\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Ground060\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Ground060\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-shellborn/basecolor.webp\",bytes:9684,sha256:\"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"},normal:{file:\"textures/creature-shellborn/normal.webp\",bytes:23222,sha256:\"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"},roughness:{file:\"textures/creature-shellborn/roughness.webp\",bytes:8118,sha256:\"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"},height:{file:\"textures/creature-shellborn/height.webp\",bytes:17122,sha256:\"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"},ao:{file:\"textures/creature-shellborn/ao.webp\",bytes:9036,sha256:\"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"},tintMask:{file:\"textures/creature-shellborn/tint-mask.webp\",bytes:50694,sha256:\"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"},preview:{file:\"textures/creature-shellborn/preview.webp\",bytes:12672,sha256:\"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"}},runtime:{unlock:{level:3,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-shellborn/basecolor\",bytes:9684,sha256:\"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"},normal:{entry:\"creature-shellborn/normal\",bytes:23222,sha256:\"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"},roughness:{entry:\"creature-shellborn/roughness\",bytes:8118,sha256:\"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"},height:{entry:\"creature-shellborn/height\",bytes:17122,sha256:\"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"},ao:{entry:\"creature-shellborn/ao\",bytes:9036,sha256:\"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"},tintMask:{entry:\"creature-shellborn/tintMask\",bytes:50694,sha256:\"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"},preview:{entry:\"creature-shellborn/preview\",bytes:12672,sha256:\"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"}}}},{id:\"creature-cracked-starlight\",displayName:\"Cracked Starlight\",track:\"meditation\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Paint002\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Paint002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-cracked-starlight/basecolor.webp\",bytes:2308,sha256:\"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"},normal:{file:\"textures/creature-cracked-starlight/normal.webp\",bytes:36906,sha256:\"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"},roughness:{file:\"textures/creature-cracked-starlight/roughness.webp\",bytes:27138,sha256:\"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"},height:{file:\"textures/creature-cracked-starlight/height.webp\",bytes:21122,sha256:\"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"},ao:{file:\"textures/creature-cracked-starlight/ao.webp\",bytes:39506,sha256:\"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"},tintMask:{file:\"textures/creature-cracked-starlight/tint-mask.webp\",bytes:37962,sha256:\"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"},preview:{file:\"textures/creature-cracked-starlight/preview.webp\",bytes:2858,sha256:\"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"}},runtime:{unlock:{level:5,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-cracked-starlight/basecolor\",bytes:2308,sha256:\"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"},normal:{entry:\"creature-cracked-starlight/normal\",bytes:36906,sha256:\"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"},roughness:{entry:\"creature-cracked-starlight/roughness\",bytes:27138,sha256:\"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"},height:{entry:\"creature-cracked-starlight/height\",bytes:21122,sha256:\"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"},ao:{entry:\"creature-cracked-starlight/ao\",bytes:39506,sha256:\"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"},tintMask:{entry:\"creature-cracked-starlight/tintMask\",bytes:37962,sha256:\"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"},preview:{entry:\"creature-cracked-starlight/preview\",bytes:2858,sha256:\"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"}}}}]};var ca=1.1;var yt=[{id:\"nails\",label:\"Nails\",short:\"Nails\",description:\"Five claw and nail forms\"},{id:\"fingertips\",label:\"Finger tips\",short:\"Tips\",description:\"Distal pads and final phalanges\"},{id:\"fingers\",label:\"Fingers & knuckles\",short:\"Fingers\",description:\"Joined finger shafts, bases and knuckles\"},{id:\"palm\",label:\"Palm\",short:\"Palm\",description:\"The inner hand surface\"},{id:\"back_of_hand\",label:\"Back of hand\",short:\"Back\",description:\"The dorsal hand surface\"},{id:\"wrist\",label:\"Wrist\",short:\"Wrist\",description:\"Stump, cuff and transition\"}],ae=[{id:0,name:\"Mortal\",realm:\"Baseline \\xB7 Earth\",primary:\"#b9856f\",secondary:\"#714c42\",accent:\"#f0c0a3\",emissive:\"#000000\",roughness:.72,metalness:.02,detail:\"clean\"},{id:1,name:\"Verdant\",realm:\"Wildroot \\xB7 Dimension 12\",primary:\"#3f7448\",secondary:\"#1e3b2b\",accent:\"#b8dd6e\",emissive:\"#172d13\",roughness:.88,metalness:0,detail:\"vine\"},{id:2,name:\"Mycelial\",realm:\"Sporesea \\xB7 Dimension 09\",primary:\"#d5c7ae\",secondary:\"#776b82\",accent:\"#f59ec4\",emissive:\"#6d234a\",roughness:.92,metalness:0,detail:\"caps\"},{id:3,name:\"Chitin\",realm:\"Carapace \\xB7 Dimension 31\",primary:\"#4f263d\",secondary:\"#180f20\",accent:\"#d17663\",emissive:\"#240716\",roughness:.3,metalness:.28,detail:\"plates\"},{id:4,name:\"Reptilian\",realm:\"Scalehold \\xB7 Dimension 04\",primary:\"#5f7a3f\",secondary:\"#283621\",accent:\"#cfb85c\",emissive:\"#101c08\",roughness:.76,metalness:.04,detail:\"scales\"},{id:5,name:\"Abyssal\",realm:\"Deep Trench \\xB7 Dimension 88\",primary:\"#132a42\",secondary:\"#06111f\",accent:\"#4edfd3\",emissive:\"#0b756f\",roughness:.34,metalness:.12,detail:\"spines\"},{id:6,name:\"Coral\",realm:\"Living Reef \\xB7 Dimension 22\",primary:\"#e46867\",secondary:\"#70344c\",accent:\"#ffd27a\",emissive:\"#8b2438\",roughness:.83,metalness:0,detail:\"coral\"},{id:7,name:\"Skeletal\",realm:\"Ossuary \\xB7 Dimension 00\",primary:\"#d8d0af\",secondary:\"#80785f\",accent:\"#fff7d1\",emissive:\"#17160e\",roughness:.9,metalness:0,detail:\"bone\"},{id:8,name:\"Spectral\",realm:\"Veil \\xB7 Dimension 13\",primary:\"#8ed9cd\",secondary:\"#294c58\",accent:\"#d5fff8\",emissive:\"#248e86\",roughness:.18,metalness:.04,detail:\"mist\"},{id:9,name:\"Infernal\",realm:\"Cinder Court \\xB7 Dimension 66\",primary:\"#7b211e\",secondary:\"#260b0a\",accent:\"#ff8a2a\",emissive:\"#d33812\",roughness:.58,metalness:.1,detail:\"flame\"},{id:10,name:\"Celestial\",realm:\"High Orbit \\xB7 Dimension 07\",primary:\"#e6dca4\",secondary:\"#786da7\",accent:\"#fffbd7\",emissive:\"#8d74d6\",roughness:.24,metalness:.5,detail:\"halo\"},{id:11,name:\"Voidborn\",realm:\"Null Expanse \\xB7 Dimension \\u2205\",primary:\"#16101f\",secondary:\"#060409\",accent:\"#9f64ff\",emissive:\"#482285\",roughness:.16,metalness:.36,detail:\"void\"},{id:12,name:\"Eldritch\",realm:\"Watcher Fold \\xB7 Dimension 47\",primary:\"#6b426c\",secondary:\"#252038\",accent:\"#b6ef63\",emissive:\"#4e7628\",roughness:.64,metalness:.04,detail:\"eyes\"},{id:13,name:\"Stone Golem\",realm:\"Deep Strata \\xB7 Dimension 19\",primary:\"#6f7068\",secondary:\"#393b37\",accent:\"#b7a37d\",emissive:\"#171811\",roughness:.98,metalness:0,detail:\"rock\"},{id:14,name:\"Crystal\",realm:\"Prism Vault \\xB7 Dimension 52\",primary:\"#896bc2\",secondary:\"#302e66\",accent:\"#e0c8ff\",emissive:\"#5a3da8\",roughness:.12,metalness:.3,detail:\"crystal\"},{id:15,name:\"Magma\",realm:\"Mantle \\xB7 Dimension 03\",primary:\"#4a1713\",secondary:\"#140706\",accent:\"#ffb22f\",emissive:\"#e44712\",roughness:.78,metalness:.06,detail:\"magma\"},{id:16,name:\"Glacial\",realm:\"White Silence \\xB7 Dimension 71\",primary:\"#93c9df\",secondary:\"#315a76\",accent:\"#ebfdff\",emissive:\"#3c93bb\",roughness:.2,metalness:.14,detail:\"ice\"},{id:17,name:\"Stormcharged\",realm:\"Tempest Ring \\xB7 Dimension 28\",primary:\"#48536e\",secondary:\"#1a2236\",accent:\"#c3f7ff\",emissive:\"#3ebde0\",roughness:.4,metalness:.32,detail:\"storm\"},{id:18,name:\"Clockwork Robot\",realm:\"Brass Meridian \\xB7 Dimension 14\",primary:\"#9a6a37\",secondary:\"#33251d\",accent:\"#efd08c\",emissive:\"#5b2c12\",roughness:.32,metalness:.82,detail:\"gears\"},{id:19,name:\"Neon Synth\",realm:\"Aftergrid \\xB7 Dimension 20\",primary:\"#251d54\",secondary:\"#0b0921\",accent:\"#ff5bd7\",emissive:\"#b519ac\",roughness:.22,metalness:.55,detail:\"neon\"},{id:20,name:\"Fluffy\",realm:\"Cloudburrow \\xB7 Soft forms\",primary:\"#d6a987\",secondary:\"#bb8969\",accent:\"#ffe4c3\",emissive:\"#000000\",roughness:.88,metalness:0,detail:\"fur\"},{id:21,name:\"Jelly\",realm:\"Lime Lagoon \\xB7 Soft forms\",primary:\"#a7ec69\",secondary:\"#f1ffd4\",accent:\"#c9ff90\",emissive:\"#000000\",roughness:.13,metalness:0,detail:\"jelly\"},{id:22,name:\"Baby\",realm:\"Little One \\xB7 Soft forms\",primary:\"#efb398\",secondary:\"#d68e7c\",accent:\"#ffe2d5\",emissive:\"#000000\",roughness:.52,metalness:0,detail:\"baby\"}];var $r=ae.map(e=>({name:e.name,description:`A pure ${e.name.toLowerCase()} hand`,values:Object.fromEntries(yt.map(a=>[a.id,e.id]))})),qr=ae.filter(e=>![8,11].includes(e.id));var oa=Object.freeze([\"clay\",\"martial-arts-canvas-gi\",\"arms-rope\",\"yoga-cork\",\"glutes-peach\"]),Re=Object.freeze({\"legacy-0\":\"clay\",\"legacy-21\":\"opal-jelly\",\"bubble-glass\":\"glitter-resin\"}),na=Object.freeze([\"flat\",\"legacy-22\",...Object.keys(Re)]),ia=e=>Object.hasOwn(Re,e)?Re[e]:e;var re=Object.freeze([{id:\"strider\",name:\"Strider\",track:\"chest\",bosses:6},{id:\"ringer\",name:\"Ringer\",track:\"quads\",bosses:5},{id:\"manyarm\",name:\"Manyarm\",track:\"glutes\",bosses:5},{id:\"wedge\",name:\"Wedge\",track:\"arms-shoulders\",bosses:5},{id:\"warden\",name:\"Warden\",track:null,bosses:1},{id:\"blob\",name:\"Blob\",track:\"yoga\",bosses:4},{id:\"cap\",name:\"Cap\",track:\"martial-arts\",bosses:3},{id:\"stalk\",name:\"Stalk\",track:\"cardio\",bosses:4},{id:\"tanka\",name:\"Tanka\",track:\"meditation\",bosses:4},{id:\"lume\",name:\"Lume\",track:null,bosses:1}]),ua=5,se=Object.freeze(re.flatMap(e=>Array.from({length:e.bosses},(a,t)=>({id:`${e.id}-${t+1}`,name:e.bosses>1?`${e.name} ${t+1}`:e.name,row:e.id,track:e.track,index:t+1})))),F=Object.freeze({chest:{circuit:\"chest\",catalog:\"chest\",family:\"push\",name:\"Chest\",palette:\"pal-01\"},quads:{circuit:\"legs\",catalog:\"quads\",family:\"legs\",name:\"Quads\",palette:\"pal-02\"},glutes:{circuit:\"hips\",catalog:\"glutes\",family:\"legs\",name:\"Glutes\",palette:\"pal-03\"},\"arms-shoulders\":{circuit:\"shoulders\",catalog:\"arms\",family:\"push\",name:\"Arms & Shoulders\",palette:\"pal-04\"},yoga:{circuit:\"yoga\",catalog:\"yoga\",family:\"flow\",name:\"Yoga\",palette:\"pal-05\"},\"martial-arts\":{circuit:\"martial-arts\",catalog:\"martial-arts\",family:\"motion\",name:\"Martial Arts\",palette:\"pal-06\"},cardio:{circuit:\"cardio\",catalog:\"cardio\",family:\"motion\",name:\"Cardio\",palette:\"pal-07\"},meditation:{circuit:\"meditation\",catalog:\"meditation\",family:\"meditation\",name:\"Meditation\",palette:\"pal-08\"}}),Me=Object.freeze({push:\"pal-09\",legs:\"pal-10\",motion:\"pal-11\"}),kt=Object.freeze(sa.skins.map(e=>Object.freeze({kind:\"creature-skin\",id:e.id,name:e.displayName,line:`${e.realm} creature finish \\xB7 ${e.rarity}`,track:e.track,level:e.runtime.unlock.level,pack:e.runtime.pack,collection:e.collection}))),da=[\"supportive\",\"direct\",\"analytical\",\"playful\",\"calm\",\"mom\"],wt=Object.freeze(Object.values(F).flatMap(({catalog:e},a)=>[3,5].map((t,r)=>{let s=da[(a*2+r)%da.length];return Object.freeze({id:`ship-${s}`,ship:s,name:Ee(s).name,track:e,level:t})}))),la=[[\"chest-w1\",\"Glove on a Stick\",\"For winning arguments with the air.\"],[\"chest-w2\",\"Chrome Bar Mace\",\"It benches you.\"],[\"arms-w1\",\"Dumbbellchucks\",\"Twice the dumbbell, half the coordination.\"],[\"arms-w2\",\"Pull-Up Staff\",\"For reaching the top shelf of victory.\"],[\"quads-w1\",\"Squat-Rack Axe\",\"Leg day has a new ambassador.\"],[\"quads-w2\",\"Kettlebell Flail\",\"Swings hard, asks questions never.\"],[\"glutes-w1\",\"Band Slingshot\",\"Resistance is futile. Literally.\"],[\"glutes-w2\",\"Hip-Thrust Hammer\",\"Bridges: now with consequences.\"],[\"yoga-w1\",\"Mat Bo Staff\",\"Rolled with intention. Swung with more.\"],[\"yoga-w2\",\"Block Gauntlets\",\"Namaste out of the way.\"],[\"martial-arts-w1\",\"Black-Belt Whip\",\"It earned its own belt.\"],[\"martial-arts-w2\",\"Dummy Club\",\"The dummy saw it coming. Still lost.\"],[\"cardio-w1\",\"Jump-Rope Lasso\",\"For roping runaway heart rates.\"],[\"cardio-w2\",\"Shoe Boomerang\",\"It always comes back. Like soreness.\"],[\"meditation-w1\",\"Singing-Bowl Aegis\",\"Om. Also, clang.\"],[\"meditation-w2\",\"Incense Wand\",\"Smells like victory. Allegedly.\"]],ma=[[\"chest-plate-steel\",\"Plate Steel\",\"Now with 40% more clank.\"],[\"chest-rubber-grip\",\"Rubber Grip\",\"For hands that mean business.\"],[\"chest-chain-mail\",\"Chain Mail\",\"Medieval. But make it cardio.\"],[\"quads-track-rubber\",\"Track Rubber\",\"Smells faintly of starting blocks.\"],[\"quads-denim\",\"Denim\",\"For legs that never skip jeans either.\"],[\"quads-hex-tread\",\"Hex Tread\",\"Grip: gecko-grade.\"],[\"glutes-sweatshirt-fleece\",\"Sweatshirt Fleece\",\"Like a hug from laundry day.\"],[\"glutes-quilted\",\"Quilted\",\"Grandma approved. Gains approved harder.\"],[\"glutes-peach\",\"Speckled\",\"Ripe. Do not squeeze the coach.\"],[\"arms-hammered-bronze\",\"Hammered Bronze\",\"Shiny. Slightly dented. Like all of us.\"],[\"arms-rope\",\"Fine Stripe\",\"Climb every metaphor.\"],[\"arms-leather\",\"Snake Skin\",\"Broken in. Like you, after set three.\"],[\"yoga-cork\",\"Holey\",\"Floats. Like your downward dog.\"],[\"yoga-woven-mat\",\"Woven Mat\",\"Hand-woven by extremely calm spiders.\"],[\"yoga-petal\",\"Petal\",\"Delicate. Unlike your warrior two.\"],[\"martial-arts-canvas-gi\",\"Graph Paper\",\"It has seen things. Mostly laundry.\"],[\"martial-arts-bamboo\",\"Bamboo\",\"Bends. Does not break. Unlike resolutions.\"],[\"martial-arts-dragon-scale\",\"Dragon Scale\",\"The dragon is fine. It donated.\"],[\"cardio-mesh\",\"Cool Graph Paper\",\"Maximum airflow. Minimum excuses.\"],[\"cardio-terry-cloth\",\"Terry Cloth\",\"Absorbs sweat and bad decisions.\"],[\"cardio-pebble-path\",\"Pebble Path\",\"A tiny trail, wherever you go.\"],[\"meditation-sand-garden\",\"Wiggles\",\"Raked by a very patient rake.\"],[\"meditation-river-stone\",\"River Stone\",\"Smooth. Unbothered. Goals.\"],[\"meditation-moss\",\"Moss\",\"Grows on you. Literally, now.\"]],xt=Object.freeze({\"chest-rubber-grip\":[\"legacy-15\",\"Magma\",\"Runs hot. Cools never.\"],\"glutes-sweatshirt-fleece\":[\"legacy-13\",\"Stone Golem\",\"Built slow. Built to last.\"],\"arms-rope\":[\"legacy-14\",\"Crystal\",\"Clear eyes. Sharp facets.\"],\"arms-leather\":[\"legacy-8\",\"Spectral\",\"Here in spirit. Also in body.\"],\"yoga-cork\":[\"legacy-20\",\"Fluffy\",\"Maximum floof. Minimum mercy.\"],\"yoga-woven-mat\":[\"legacy-21\",\"Jelly\",\"Wobbles. Never falls.\"],\"cardio-terry-cloth\":[\"legacy-16\",\"Glacial\",\"Cool under pressure. Very cool.\"]}),_t={push:[\"Forged from lost remote controls.\",\"For the comfiest conqueror.\"],legs:[\"Always moving. Never in a hurry.\",\"Polished by a thousand commutes.\"],motion:[\"Nine more minutes. Forever.\",\"Shiny. Like your intentions at 6 AM.\"],flow:[\"Eight arms. Zero flexibility.\",\"Breathe in. The kraken breathes out.\"],meditation:[\"99+ unread. All of them urgent.\",\"Silent. At last.\"]},vt={push:\"It followed you home from the gym. Keep it.\",legs:\"It never skips leg day. It never skips anything.\",motion:\"Fast. Loud. Naps hard.\",flow:\"Bends in ways physics dislikes.\",meditation:\"It hums when you breathe. Do not ask how.\"},p=(e,[a,t,r])=>({kind:e,id:a,name:t,line:r}),O=(e,a)=>({kind:\"reward-pack\",id:`reward-pack:${e}:${a}`,tier:e,name:`${e[0].toUpperCase()+e.slice(1)} Pack`,line:\"Open for one random cosmetic.\"}),ha=Object.freeze([\"Mortal\",\"Verdant\",\"Mycelial\",\"Chitin\",\"Reptilian\",\"Abyssal\",\"Coral\",\"Skeletal\",\"Spectral\",\"Infernal\",\"Celestial\",\"Voidborn\",\"Eldritch\",\"Stone Golem\",\"Crystal\",\"Magma\",\"Glacial\",\"Stormcharged\",\"Clockwork Robot\",\"Neon Synth\",\"Fluffy\",\"Jelly\",\"Baby\"]),pa=()=>[...ma.map(e=>p(\"texture\",e)),...[[\"opal-jelly\",\"Opal Jelly\"],[\"bubble-glass\",\"Bubble Glass\"],[\"prism-crystal\",\"Prism Crystal\"],[\"holo-foil\",\"Holographic Foil\"],[\"glitter-resin\",\"Glitter Resin\"],[\"galaxy-geode\",\"Galaxy Geode\"],[\"caustic-slime\",\"Caustic Slime\"],[\"blister-hide\",\"Blister Hide\"],[\"rotten-rind\",\"Rotten Rind\"],[\"parasite-nest\",\"Parasite Nest\"],[\"exposed-sinew\",\"Exposed Sinew\"],[\"abyssal-maw\",\"Abyssal Maw\"],[\"circuit-alloy\",\"Circuit Alloy\"],[\"servo-armor\",\"Servo Armor\"],[\"chrome-rib\",\"Chrome Rib\"],[\"carbon-mech\",\"Carbon Mech\"],[\"hazard-panel\",\"Hazard Panel\"],[\"reactor-glass\",\"Reactor Glass\"]].map(([e,a])=>p(\"texture\",[e,a,\"\"])),...ha.map((e,a)=>p(\"texture\",[`legacy-${a}`,e,\"\"])),p(\"texture\",[\"coach-64-bit\",\"64-bit Pixel Finish\",\"A pixel finish for a coach with a boss skin.\"])].filter(e=>!oa.includes(e.id)&&!na.includes(e.id)),Qr=Object.freeze([\"#060409\",\"#ffffff\",\"#7f7d78\",\"#ff3b30\",\"#ff8a2a\",\"#ffd100\",\"#2bd97c\",\"#008c8c\",\"#2454d6\",\"#6a2bd9\",\"#f59ec4\",\"#7a5530\",\"#c4a77d\",\"#0b1a45\",\"#9fe2bf\"]),Zr=Object.freeze([\"Black\",\"White\",\"Grey\",\"Red\",\"Orange\",\"Yellow\",\"Green\",\"Teal\",\"Blue\",\"Purple\",\"Pink\",\"Brown\",\"Tan\",\"Navy\",\"Mint\"]),ga=()=>[...q.map(e=>({kind:\"palette\",id:e.id,name:e.name})),...[[\"default-slate\",\"Slate\",\"#8b8f9a\"],[\"default-clay\",\"Warm Clay\",\"#b7a68e\"],[\"default-ruby\",\"Ruby\",\"#a23b4a\"],[\"default-sapphire\",\"Sapphire\",\"#2d5aa0\"],[\"default-moss\",\"Moss\",\"#4c7a3f\"],[\"default-gold\",\"Gold\",\"#c9a13a\"],[\"default-charcoal\",\"Charcoal\",\"#333238\"],[\"default-blush\",\"Blush\",\"#d98fa0\"]].map(([e,a,t])=>({kind:\"color\",id:e,name:a,hex:t})),...ha.map((e,a)=>({kind:\"color\",id:`legacy-color-${a}`,name:`${e} (original)`,hex:a?ae[a].primary:\"#7946aa\"}))];function ba(e,a){for(let r=0;r<e.length;r++)e[r]=e[r].flatMap(s=>s.kind===\"palette\"?[O(\"uncommon\",`${a}:L${r+1}:${s.id}`)]:s.kind===\"texture\"?[O(\"legendary\",`${a}:L${r+1}:${s.id}`)]:s.kind===\"boss-skin\"?[{kind:\"boss-unlock\",id:a,name:s.name.replace(/ Skin$/,\" Beaten\"),line:\"Boss cleared at level 5.\"}]:[s]);e[3].push(O(\"rare\",`${a}:L4:64-bit`));let t=r=>r.some(s=>s.kind===\"reward-pack\");return t(e[1])||e[1].push(O(\"uncommon\",`${a}:L2:bonus`)),t(e[4])||e[4].push(O(\"legendary\",`${a}:L5:bonus`)),e}var fa=(e,a)=>e.find(t=>t[0]===a);function ce(e){let a=q.find(t=>t.id===e);return{kind:\"palette\",id:e,name:a.name,line:a.tagline}}var St=new Map(q.filter(e=>e.reward).map(e=>[e.reward,e.id])),ya=(e,a)=>Array.from({length:a},(t,r)=>St.get(`${e}:L${r+1}`)).map(t=>t?[ce(t)]:[]),Ct=e=>({kind:\"pet\",id:`${e}-pet`,name:`${e[0].toUpperCase()}${e.slice(1)} pet`,line:vt[e],family:e});function ka(e){let a=se.find(n=>n.id===e);if(!a)return null;let t=a.track&&F[a.track],r=_t[t?.family]||[null,null],s=ya(a.id,ua);if(s[1].push({kind:\"boss-texture\",id:`${a.id}-texture`,name:`${a.name} Texture`,line:r[0]}),s[4].push({kind:\"boss-skin\",id:`${a.id}-skin`,name:`${a.name} Skin`,line:r[1]}),!t||a.index!==1)return ba(s,a.id);let c=t.catalog,o=ma.filter(n=>n[0].startsWith(c+\"-\")).map(n=>xt[n[0]]??n);s[0].unshift(p(\"weapon\",fa(la,`${c}-w1`)),p(\"texture\",o[0])),s[1].unshift(ce(t.palette)),s[2].push(p(\"weapon\",fa(la,`${c}-w2`)),{kind:\"special\",id:`${c}-special`,name:`${t.name} Special`,line:\"Unlocked at level 3.\"},p(\"texture\",o[1])),s[3].push(Ct(t.family)),s[4].unshift({kind:\"aura\",id:`${c}-aura`,name:`${t.name} Aura`,line:\"Your first aura look.\"}),s[4].push(p(\"texture\",o[2]));for(let n of kt.filter(d=>d.track===c))s[n.level-1].push(n);for(let n of wt.filter(d=>d.track===c))s[n.level-1].push({kind:\"ship\",id:n.id,name:n.name,line:Ee(n.ship).description,ship:n.ship,pack:\"coach-ships-biomes\"});return ba(s,a.id)}var te=Object.freeze({id:\"food-bonus\",name:\"Second Helping\",line:\"Hits harder for one day. Chew responsibly.\",effect:Object.freeze({type:\"damage-multiplier\",days:1,value:ca})}),oe=q.filter(e=>e.reward?.startsWith(\"food:\")).length,wa=()=>ya(\"food\",oe).map((e,a)=>[...e.map(t=>O(\"uncommon\",`food:L${a+1}:${t.id}`)),{kind:\"bonus\",id:`${te.id}-${a+1}`,name:te.name,line:te.line,effect:te.effect}]);var cs=Object.freeze({chest:\"chest\",legs:\"quads\",hips:\"glutes\",shoulders:\"arms-shoulders\",core:\"yoga\",balance:\"yoga\",yoga:\"yoga\",stances:\"martial-arts\",boxing:\"martial-arts\",cardio:\"cardio\"});var xa=(e={})=>w(e).paths;var de={};ze(de,{cosmeticId:()=>u,currentCosmeticCoach:()=>H,grantUnlock:()=>j,grantUnlocks:()=>Ie,grantedIds:()=>Tt,isGranted:()=>Le,isGrantedToOriginalCoach:()=>Aa,migrateSavedCosmetics:()=>It,ownerGrantedIds:()=>Te});V();var Oe=\"myr5-unlock-pending-v1\",ne=Object.freeze([\"creature-skin\",\"ship\",\"reward-pack\"]),Et=new Set([...ne,\"texture\",\"color\",\"palette\",\"boss-skin\"]),At=({account:e=globalThis.myr5AuthenticatedAccount}={},a=!0)=>{try{let t=typeof e==\"string\"?e:e?.user?.id;if(typeof t==\"string\"&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(t))return`${Oe}/account/${t}${typeof e==\"object\"&&e?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(e.dataEpoch))}`:\"\"}`;let r=a?h(globalThis.localStorage,e):localStorage.getItem(\"myr5-local-guest-owner-v1\");return r?`${Oe}/guest/${encodeURIComponent(a?r:`guest:${r}`)}`:null}catch{return null}},_a=(e,a={})=>Et.has(e)||a.accountScoped===!0?At(a):Oe;var va=e=>{try{let a=JSON.parse(localStorage.getItem(e)||\"{}\");return a&&typeof a==\"object\"&&!Array.isArray(a)?a:{}}catch{return{}}},Sa=(e,a)=>Array.isArray(e[a])?e[a]:[];function Rt(e,a,t){if(!e)return;let r=va(e),s=Sa(r,a),c=t(s);if(c!==s)try{localStorage.setItem(e,JSON.stringify({...r,[a]:c}))}catch{}}function ie(e,a){let t=new Map;for(let{kind:r,id:s}of e){let c=_a(r,a);if(!c)continue;let o=t.get(c);o||(o={data:va(c),known:new Map},t.set(c,o)),o.known.has(r)||(o.data[r]=[...Sa(o.data,r)],o.known.set(r,new Set(o.data[r])));let n=o.known.get(r);n.has(s)||(n.add(s),o.data[r].push(s))}for(let[r,s]of t)try{localStorage.setItem(r,JSON.stringify(s.data))}catch{}}var L=(e,a,t)=>Rt(_a(e,t),e,r=>r.includes(a)?r:[...r,a]);var Mt=\"myr5-unlocks-v2\",Ot=()=>({texture:[],color:[],palette:[]}),u=(e,a)=>`coach:${encodeURIComponent(e)}:${a}`,T=(e,a)=>e===\"texture\"?ia(a):a,Ft=(e,a)=>{let t=/^(coach:[^:]*:)(.*)$/.exec(a);return t?t[1]+T(e,t[2]):a};function H(){try{return JSON.parse(localStorage.getItem(\"myr5-recipe-v1\")||\"{}\").body||\"myr5\"}catch{return\"myr5\"}}var Fe=()=>f(`${Mt}/${encodeURIComponent(h())}`);function I(){try{let e=JSON.parse(localStorage.getItem(Fe())||\"{}\");return Object.fromEntries([\"texture\",\"color\",\"palette\"].map(a=>[a,Array.isArray(e[a])?[...new Set(e[a].filter(t=>typeof t==\"string\").map(t=>Ft(a,t)))]:[]]))}catch{return Ot()}}function Ca(e){try{return localStorage.setItem(Fe(),JSON.stringify(e)),!0}catch{return!1}}var Ea=(e,a,t)=>I()[e]?.includes(u(t,T(e,a)))??!1;function Lt(e,a){let t=T(e,a);return(I()[e]||[]).some(r=>{let s=/^coach:([^:]+):(.*)$/.exec(r);if(!s||s[2]!==t)return!1;try{return decodeURIComponent(s[1])!==\"myr5\"}catch{return!1}})}var Aa=(e,a)=>Ea(e,a,\"myr5\")||Lt(e,a),Le=(e,a,t=H())=>t===\"myr5\"?Aa(e,a):Ea(e,a,t);function Ra(e){let a=globalThis.CustomEvent;if(!(typeof globalThis.dispatchEvent!=\"function\"||typeof a!=\"function\"||!e.length))try{globalThis.dispatchEvent(new a(\"myr5:cosmetics-unlocked\",{detail:{owner:h(),granted:e}}))}catch{}}function j(e,a,t=H()){if(!A()||![\"texture\",\"color\",\"palette\"].includes(e)||typeof a!=\"string\"||!a||typeof t!=\"string\"||!t)return!1;let r=I(),s=u(t,T(e,a));if(r[e].includes(s)||(r[e].push(s),!Ca(r)))return!1;let c=T(e,a),o=[{kind:e,id:c,coachId:t}];return L(e,s),t!==\"myr5\"&&L(e,u(\"myr5\",c)),Ra(o),!0}var Tt=(e,a=H())=>{let t=I()[e]||[],r=u(a,\"\"),s=t.filter(o=>o.startsWith(r)).map(o=>o.slice(r.length));if(a!==\"myr5\")return s;let c=t.flatMap(o=>{let n=/^coach:([^:]+):(.*)$/.exec(o);if(!n||!n[2])return[];try{return decodeURIComponent(n[1])===\"myr5\"?[]:[n[2]]}catch{return[]}});return[...new Set([...s,...c])]};function It(e){if((E()?.gameResetAt??0)>0)return;let a=e?.body;if(a)try{let t=`${Fe()}/saved-look/${encodeURIComponent(a)}`;if(localStorage.getItem(t))return;let r=JSON.parse(localStorage.getItem(\"myr5-unlocks-v1\")||\"{}\");for(let s of Object.values(e.materials||{}))if(s?.textureId&&r.texture?.includes(s.textureId)&&j(\"texture\",s.textureId,a),s?.colorId)for(let c of[\"color\",\"palette\"])r[c]?.includes(s.colorId)&&j(c,s.colorId,a);localStorage.setItem(t,\"1\")}catch{}}var Te=e=>{let a=I()[e]||[],t=a.flatMap(r=>{let s=/^coach:([^:]+):(.*)$/.exec(r);if(!s||!s[2])return[];try{return decodeURIComponent(s[1])===\"myr5\"?[]:[u(\"myr5\",s[2])]}catch{return[]}});return[...new Set([...a,...t])]};function Ie(e){if(!A())return 0;let a=I(),t=Object.fromEntries([\"texture\",\"color\",\"palette\"].map(c=>[c,new Set(a[c])])),r=[];for(let c of e){if(!t[c.kind]||typeof c.id!=\"string\"||!c.id||typeof c.coachId!=\"string\"||!c.coachId)continue;let o=T(c.kind,c.id),n=u(c.coachId,o);t[c.kind].has(n)||(t[c.kind].add(n),a[c.kind].push(n),r.push({kind:c.kind,id:o,coachId:c.coachId}))}if(!r.length||!Ca(a))return 0;let s=r.flatMap(({kind:c,id:o,coachId:n})=>[{kind:c,id:u(n,o)},...n===\"myr5\"?[]:[{kind:c,id:u(\"myr5\",o)}]]);return ie(s),Ra(r),r.length}var ue={};ze(ue,{ACCOUNT_SCOPED_LEDGER_KINDS:()=>ne,LEDGER_KEY:()=>P,LEDGER_KINDS:()=>D,grantUnlock:()=>fe,grantUnlocks:()=>Ne,grantedIds:()=>K,isGranted:()=>De});V();var P=\"myr5-battle-pass-ledger-v1\",D=Object.freeze([\"weapon\",\"pet\",\"boss-texture\",\"boss-skin\",\"boss-unlock\",\"special\",\"aura\",\"bonus\",\"creature-skin\",\"ship\",\"reward-pack\"]);function le(){try{let e=JSON.parse(localStorage.getItem(f(P))||\"{}\");return Object.fromEntries(D.map(a=>[a,Array.isArray(e[a])?e[a]:[]]))}catch{return Object.fromEntries(D.map(e=>[e,[]]))}}var Ma=new Set([...ne,\"boss-skin\"]),be=({account:e=globalThis.myr5AuthenticatedAccount}={},a=!0)=>{try{let t=typeof e==\"string\"?e:e?.user?.id;if(typeof t==\"string\"&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(t))return`${P}/account/${t}${typeof e==\"object\"&&e?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(e.dataEpoch))}`:\"\"}`;let r=a?h(globalThis.localStorage,e):localStorage.getItem(\"myr5-local-guest-owner-v1\");return r?`${P}/guest/${encodeURIComponent(a?r:`guest:${r}`)}`:null}catch{return null}};function Pe(e){try{let a=be(e,!1),t=a?f(a):null;if(!t)return{};let r=JSON.parse(localStorage.getItem(t)||\"{}\");return r&&typeof r==\"object\"&&!Array.isArray(r)?r:{}}catch{return{}}}var K=(e,a)=>{if(!D.includes(e))return[];let t=Pe(a)[e],r=Array.isArray(t)?t:[];return Ma.has(e)?r:r.length?[...new Set([...le()[e],...r])]:le()[e]},De=(e,a,t)=>K(e,t).includes(a),Oa=(e,a)=>Ma.has(e)||a?.accountScoped===!0;function fe(e,a,t){if(!A(t?.account)||!D.includes(e)||typeof a!=\"string\"||!a)return!1;if(Oa(e,t)){if(De(e,a,t))return!1;let s=be(t),c=s?f(s):null;if(!c)return!1;let o=Pe(t),n=Array.isArray(o[e])?o[e]:[];o[e]=[...n,a];try{localStorage.setItem(c,JSON.stringify(o))}catch{return!1}return L(e,a,{...t,accountScoped:!0}),!0}let r=le();if(r[e].includes(a))return!1;r[e].push(a);try{localStorage.setItem(f(P),JSON.stringify(r))}catch{return!1}return L(e,a),!0}function Ne(e,a,t){if(!A(t?.account)||!D.includes(e)||!Array.isArray(a))return 0;let r=Oa(e,t),s=r?be(t)?f(be(t)):null:f(P);if(!s)return 0;let c=r?Pe(t):le(),o=new Set(r?K(e,t):c[e]||[]),n=new Set(c[e]||[]),d=[];for(let i of a)typeof i==\"string\"&&i&&!o.has(i)&&(o.add(i),n.add(i),d.push(i));if(!d.length)return 0;c[e]=[...n];try{localStorage.setItem(s,JSON.stringify(c))}catch{return 0}return ie(d.map(i=>({kind:e,id:i})),r?{...t,accountScoped:!0}:t),d.length}var ks=Object.freeze({rare:2,legendary:3,odds:Object.freeze({secret:Object.freeze({color:75,\"64-bit\":0})})});var Pt={color:ga(),texture:pa()};var La=e=>u(e.coachId,e.id);var Dt=(e,a)=>a.flatMap(t=>e===\"64-bit\"?[{kind:\"boss-skin\",id:`${t}-skin`,name:\"64-bit Pixel Finish\",coachId:t}]:Pt[e].map(r=>({...r,coachId:t})));function Fa({coaches:e=Ce()}={}){let a=[...new Set(e)].filter(r=>ta(r)),t=Object.fromEntries([\"color\",\"palette\",\"texture\"].map(r=>[r,new Set(Te(r))]));return t[\"boss-skin\"]=new Set(K(\"boss-skin\")),[\"color\",\"64-bit\",\"texture\"].flatMap(r=>Dt(r,a).map(s=>({...s,category:r}))).filter(r=>!t[r.kind].has(La(r)))}function Ta(){let e=Fa(),a=e.filter(s=>s.kind!==\"boss-skin\"),t=e.filter(s=>s.kind===\"boss-skin\");Ie(a),Ne(\"boss-skin\",t.map(La));let r=Fa().length;return{granted:e.length-r,remaining:r}}var Nt=Object.fromEntries(Object.entries(F).map(([e,a])=>[a.circuit,e]));function Da(e=globalThis.myr5AuthenticatedAccount){let a=xa();return a.length||(a=(e?.onboarding?.data?.profile?.exercises||[]).map(t=>Nt[ra[Ae(t)]]).filter(Boolean)),new Set([\"meditation\",...a])}function Na(){let e=globalThis.coachProgress?.circuit?.tracks;if(e)return e;try{return JSON.parse(localStorage.getItem(\"myr5-workout-progress-v1\"))?.circuit?.tracks||{}}catch{return{}}}var Bt=(e,a=ee)=>Math.floor(Math.max(0,Number(e)||0)/a);function Gt({tracks:e=Na(),account:a,stepsPerLevel:t=ee}={}){let r=w({account:a}),s=Object.fromEntries(se.map(c=>[c.id,0]));for(let c of re){if(!c.track)continue;let o=g.filter(n=>n.tracks.includes(F[c.track].catalog));for(let n=0;n<c.bosses;n++){let d=o[Math.min(o.length-1,Math.floor(n*o.length/c.bosses))];d&&(s[`${c.id}-${n+1}`]=r.goldenCoaches.includes(d.id)?5:r.coaches.includes(d.id)?4:0)}}return s}var zt=new Set([\"texture\",\"color\",\"palette\"]),Ia=(e,a)=>(zt.has(e.kind)?de:ue).isGranted(e.kind,e.id,a);function Ut(e={}){let a=e.stepsPerLevel??ee,t=Da(e.account),r=Gt({...e,stepsPerLevel:a}),s=Math.max(0,Number((e.tracks??Na())?.food?.steps)||0),c=Math.min(oe,Bt(s,a)),o=i=>re.find(l=>l.track&&F[l.track].family===i&&t.has(l.track)&&r[`${l.id}-1`]>=4)?.id,n=se.map(i=>({...i,levels:r[i.id],rewards:ka(i.id).map((l,N)=>({level:N+1,beaten:r[i.id]>N,items:l.map(B=>{let Be=B.kind===\"pet\"&&Me[B.family]&&o(B.family),Ge=Be&&Be!==i.row?(()=>{let Ga=ce(Me[B.family]);return{kind:\"reward-pack\",id:`reward-pack:uncommon:${i.id}:L4:${Ga.id}`,tier:\"uncommon\",name:\"Uncommon Pack\",line:\"Open for one random cosmetic.\"}})():B;return{...Ge,granted:Ia(Ge,e)}})}))})),d={steps:s,levels:c,maxLevel:oe,rewards:wa().map((i,l)=>({level:l+1,beaten:c>l,items:i.map(N=>({...N,granted:Ia(N)}))}))};return{stepsPerLevel:a,available:[...t],progress:r,bosses:n,food:d}}function Pa(e={}){let a=Ut(e),t=[],r=Qe(w(e).totalXp);for(let s=2;s<=r.level;s++){let c=s%10===0?\"legendary\":s%5===0?\"rare\":\"uncommon\",o={kind:\"reward-pack\",id:`reward-pack:${c}:cosmetic-pass-v2:L${s}`,tier:c,name:`${c} pack`};fe(o.kind,o.id,e)&&t.push(o)}return r.max&&Ta(e),t.length&&typeof window<\"u\"&&window.dispatchEvent&&window.dispatchEvent(new CustomEvent(\"myr5:battle-pass\",{detail:{granted:t,state:a}})),{granted:t,state:a}}typeof window<\"u\"&&window.addEventListener&&(window.addEventListener(\"myr5:account-progress\",()=>Pa()),window.addEventListener(\"myr5:performance-progress\",()=>Pa()));var $t=Object.fromEntries([...v.map(e=>[e.id,e.name]),[\"meditation\",\"Meditation\"]]),Ba=C.map(e=>({...e,name:e.label,requirement:g.find(a=>a.id===e.id)||null})),qt=e=>e[0].toUpperCase()+e.slice(1),me=e=>$t[e]||e;function jt(e){let a=Ba.find(n=>n.id===e);if(!a)return null;let t=a.requirement;if(!t){let n=_.includes(e);return{starter:n,unlock:n?\"Available from the start\":\"Requirement pending placement.\",gold:\"Select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:[]}}if(t.groups.includes(\"meditation\"))return{starter:!1,unlock:`Complete meditation on ${x.meditationDays[U.indexOf(t.difficulty)]} separate days.`,gold:\"Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:[]};let r=Object.values(m).filter(n=>t.groups.includes(n.group)&&ke(n.id)===t.difficulty),s=Object.values(W).includes(e)?`Choose ${t.groups.map(me).join(\" / \")} as one of your two starting workout paths OR `:\"\",c=[...new Set(r.map(n=>n.kind))],o=[];return c.includes(\"hold\")&&o.push(\"hold for 5 uninterrupted minutes\"),c.includes(\"reps\")&&o.push(\"complete 15 reps in one working set\"),t.groups.includes(\"cardio\")?o.push(\"complete 5 sprint rounds or 5 active minutes of gentle cardio\"):c.some(n=>n===\"pace\"||n===\"steps\")&&o.push(\"complete 5 active minutes\"),{starter:!1,difficulty:t.difficulty,groups:t.groups,unlock:`${s}${qt(t.difficulty)} \\xB7 ${t.groups.map(me).join(\" / \")}: ${o.join(\" OR \")}.`,gold:c.includes(\"hold\")?\"Complete a 10-minute uninterrupted hold at this difficulty in one of these groups. Breaks and difficulty changes restart the uninterrupted attempt.\":\"Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:r.map(n=>n.name)}}function Ht(e=w()){return Ba.map(a=>({...a,state:e.coaches?.includes(a.id)||Q(e).done?\"done\":\"locked\",golden:e.goldenCoaches?.includes(a.id)===!0,requirements:jt(a.id)}))}function Kt(e){return{group:e,name:me(e),weapons:ye[e]||[],blocks:U.map((a,t)=>({difficulty:a,first:t*5+1,last:t*5+5})),hold:\"1 uninterrupted minute: first tier in the difficulty block. 3 minutes: second tier. 5 minutes: coach clear; repeating it adds one tier. 10 minutes: finish the five-tier block and earn gold.\",reps:\"8 reps: first tier in the difficulty block. 12 reps: second tier. 15 reps: coach clear; repeating it adds one tier. One working set per muscle group per day.\"}}var Wt=()=>X.map(e=>({...e,name:Z.find(a=>a.id===e.id)?.name||e.id,unlock:`Expert ${me(e.group)}: ${e.kind===\"hold\"?\"5 uninterrupted minutes\":\"15 reps in one working set\"}.`}));var Xt=[{id:\"strider\",track:\"chest\",color:\"#b48cff\",boxes:[[10.96,9.7,10.78,8.8],[28.4,8.05,7.89,10.45],[37.51,9.2,11.92,9.3],[51.62,8.05,11.48,10.45],[63.37,8.05,12.8,10.45],[78.18,7.9,15.43,10.6]]},{id:\"ringer\",track:\"quads\",color:\"#ff8a2a\",boxes:[[20.16,18.05,7.89,8.1],[31.11,17.95,10.69,8.4],[44.26,17.95,9.55,8.25],[57.06,17.95,9.03,8.3],[69.76,17.95,11.13,8.5]]},{id:\"manyarm\",track:\"glutes\",color:\"#7dff5a\",boxes:[[22.17,26.65,8.33,8.6],[34.18,26.45,9.73,8.8],[46.54,27.25,10.08,8],[57.49,27.4,7.54,7.85],[65.64,27.2,12.62,8.05]]},{id:\"wedge\",track:\"arms\",color:\"#39a8ff\",boxes:[[23.4,34.7,10.52,7.8],[35.06,34.7,8.15,7.45],[43.21,34.7,11.48,7.75],[55.04,34.7,9.82,7.75],[65.21,34.7,11.22,7.75]]},{id:\"warden\",track:null,color:\"#39a8ff\",boxes:[[45.57,41.95,8.24,5.7]]},{id:\"blob\",track:\"yoga\",color:\"#c65cff\",boxes:[[26.29,47.25,10.87,6.95],[38.56,47.1,9.55,7.2],[49.87,47.1,7.8,6.95],[60.74,47.3,9.55,7.45]]},{id:\"cap\",track:\"martial-arts\",color:\"#ff4fa0\",boxes:[[33.04,54.6,7.62,7.4],[40.93,54.55,11.13,7.45],[53.46,54.65,10.78,7.35]]},{id:\"stalk\",track:\"cardio\",color:\"#ff4a3d\",boxes:[[30.5,61.45,7.01,7.5],[38.21,61.45,8.15,7.25],[49.52,61.45,9.29,7.5],[58.55,61.45,8.59,7.5]]},{id:\"tanka\",track:\"meditation\",color:\"#ffd23a\",boxes:[[29.27,68.65,9.38,7.85],[39.09,68.65,9.64,7.8],[48.47,68.45,10.6,8],[59.95,68.5,8.33,8]]},{id:\"lume\",track:null,color:\"#ffc94a\",boxes:[[39.35,75.95,15.34,8.8]]}],$s=Xt.flatMap(e=>e.boxes.map((a,t)=>({id:`${e.id}-${t+1}`,row:e.id,color:e.color,box:a})));export{Ht as bossStates,Wt as shipRequirements,Kt as weaponRequirements};\n","modules/coach-world-rts/npc-achievement-rules.mjs":"// Compile only the shared rule exports for the directly served World modules.\r\nexport {bossStates,shipRequirements,weaponRequirements} from '../../achievements-board.mjs';\r\n","modules/coach-world-rts/npc-cosmetic-catalog.mjs":"// Generated from the real reward pools by scripts/build-npc-catalog.mjs.\nexport const NPC_COSMETICS={\"textures\":[{\"kind\":\"texture\",\"id\":\"chest-plate-steel\",\"name\":\"Plate Steel\",\"line\":\"Now with 40% more clank.\"},{\"kind\":\"texture\",\"id\":\"chest-rubber-grip\",\"name\":\"Rubber Grip\",\"line\":\"For hands that mean business.\"},{\"kind\":\"texture\",\"id\":\"chest-chain-mail\",\"name\":\"Chain Mail\",\"line\":\"Medieval. But make it cardio.\"},{\"kind\":\"texture\",\"id\":\"quads-track-rubber\",\"name\":\"Track Rubber\",\"line\":\"Smells faintly of starting blocks.\"},{\"kind\":\"texture\",\"id\":\"quads-denim\",\"name\":\"Denim\",\"line\":\"For legs that never skip jeans either.\"},{\"kind\":\"texture\",\"id\":\"quads-hex-tread\",\"name\":\"Hex Tread\",\"line\":\"Grip: gecko-grade.\"},{\"kind\":\"texture\",\"id\":\"glutes-sweatshirt-fleece\",\"name\":\"Sweatshirt Fleece\",\"line\":\"Like a hug from laundry day.\"},{\"kind\":\"texture\",\"id\":\"glutes-quilted\",\"name\":\"Quilted\",\"line\":\"Grandma approved. Gains approved harder.\"},{\"kind\":\"texture\",\"id\":\"arms-hammered-bronze\",\"name\":\"Hammered Bronze\",\"line\":\"Shiny. Slightly dented. Like all of us.\"},{\"kind\":\"texture\",\"id\":\"arms-leather\",\"name\":\"Snake Skin\",\"line\":\"Broken in. Like you, after set three.\"},{\"kind\":\"texture\",\"id\":\"yoga-woven-mat\",\"name\":\"Woven Mat\",\"line\":\"Hand-woven by extremely calm spiders.\"},{\"kind\":\"texture\",\"id\":\"yoga-petal\",\"name\":\"Petal\",\"line\":\"Delicate. Unlike your warrior two.\"},{\"kind\":\"texture\",\"id\":\"martial-arts-bamboo\",\"name\":\"Bamboo\",\"line\":\"Bends. Does not break. Unlike resolutions.\"},{\"kind\":\"texture\",\"id\":\"martial-arts-dragon-scale\",\"name\":\"Dragon Scale\",\"line\":\"The dragon is fine. It donated.\"},{\"kind\":\"texture\",\"id\":\"cardio-mesh\",\"name\":\"Cool Graph Paper\",\"line\":\"Maximum airflow. Minimum excuses.\"},{\"kind\":\"texture\",\"id\":\"cardio-terry-cloth\",\"name\":\"Terry Cloth\",\"line\":\"Absorbs sweat and bad decisions.\"},{\"kind\":\"texture\",\"id\":\"cardio-pebble-path\",\"name\":\"Pebble Path\",\"line\":\"A tiny trail, wherever you go.\"},{\"kind\":\"texture\",\"id\":\"meditation-sand-garden\",\"name\":\"Wiggles\",\"line\":\"Raked by a very patient rake.\"},{\"kind\":\"texture\",\"id\":\"meditation-river-stone\",\"name\":\"River Stone\",\"line\":\"Smooth. Unbothered. Goals.\"},{\"kind\":\"texture\",\"id\":\"meditation-moss\",\"name\":\"Moss\",\"line\":\"Grows on you. Literally, now.\"},{\"kind\":\"texture\",\"id\":\"opal-jelly\",\"name\":\"Opal Jelly\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"prism-crystal\",\"name\":\"Prism Crystal\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"holo-foil\",\"name\":\"Holographic Foil\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"glitter-resin\",\"name\":\"Glitter Resin\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"galaxy-geode\",\"name\":\"Galaxy Geode\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"caustic-slime\",\"name\":\"Caustic Slime\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"blister-hide\",\"name\":\"Blister Hide\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"rotten-rind\",\"name\":\"Rotten Rind\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"parasite-nest\",\"name\":\"Parasite Nest\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"exposed-sinew\",\"name\":\"Exposed Sinew\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"abyssal-maw\",\"name\":\"Abyssal Maw\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"circuit-alloy\",\"name\":\"Circuit Alloy\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"servo-armor\",\"name\":\"Servo Armor\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"chrome-rib\",\"name\":\"Chrome Rib\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"carbon-mech\",\"name\":\"Carbon Mech\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"hazard-panel\",\"name\":\"Hazard Panel\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"reactor-glass\",\"name\":\"Reactor Glass\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-1\",\"name\":\"Verdant\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-2\",\"name\":\"Mycelial\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-3\",\"name\":\"Chitin\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-4\",\"name\":\"Reptilian\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-5\",\"name\":\"Abyssal\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-6\",\"name\":\"Coral\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-7\",\"name\":\"Skeletal\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-8\",\"name\":\"Spectral\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-9\",\"name\":\"Infernal\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-10\",\"name\":\"Celestial\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-11\",\"name\":\"Voidborn\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-12\",\"name\":\"Eldritch\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-13\",\"name\":\"Stone Golem\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-14\",\"name\":\"Crystal\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-15\",\"name\":\"Magma\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-16\",\"name\":\"Glacial\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-17\",\"name\":\"Stormcharged\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-18\",\"name\":\"Clockwork Robot\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-19\",\"name\":\"Neon Synth\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"legacy-20\",\"name\":\"Fluffy\",\"line\":\"\"},{\"kind\":\"texture\",\"id\":\"coach-64-bit\",\"name\":\"64-bit Pixel Finish\",\"line\":\"A pixel finish for a coach with a boss skin.\"}],\"colours\":[{\"kind\":\"color\",\"id\":\"default-slate\",\"name\":\"Slate\",\"hex\":\"#8b8f9a\"},{\"kind\":\"color\",\"id\":\"default-clay\",\"name\":\"Warm Clay\",\"hex\":\"#b7a68e\"},{\"kind\":\"color\",\"id\":\"default-ruby\",\"name\":\"Ruby\",\"hex\":\"#a23b4a\"},{\"kind\":\"color\",\"id\":\"default-sapphire\",\"name\":\"Sapphire\",\"hex\":\"#2d5aa0\"},{\"kind\":\"color\",\"id\":\"default-moss\",\"name\":\"Moss\",\"hex\":\"#4c7a3f\"},{\"kind\":\"color\",\"id\":\"default-gold\",\"name\":\"Gold\",\"hex\":\"#c9a13a\"},{\"kind\":\"color\",\"id\":\"default-charcoal\",\"name\":\"Charcoal\",\"hex\":\"#333238\"},{\"kind\":\"color\",\"id\":\"default-blush\",\"name\":\"Blush\",\"hex\":\"#d98fa0\"},{\"kind\":\"color\",\"id\":\"legacy-color-0\",\"name\":\"Mortal (original)\",\"hex\":\"#7946aa\"},{\"kind\":\"color\",\"id\":\"legacy-color-1\",\"name\":\"Verdant (original)\",\"hex\":\"#3f7448\"},{\"kind\":\"color\",\"id\":\"legacy-color-2\",\"name\":\"Mycelial (original)\",\"hex\":\"#d5c7ae\"},{\"kind\":\"color\",\"id\":\"legacy-color-3\",\"name\":\"Chitin (original)\",\"hex\":\"#4f263d\"},{\"kind\":\"color\",\"id\":\"legacy-color-4\",\"name\":\"Reptilian (original)\",\"hex\":\"#5f7a3f\"},{\"kind\":\"color\",\"id\":\"legacy-color-5\",\"name\":\"Abyssal (original)\",\"hex\":\"#132a42\"},{\"kind\":\"color\",\"id\":\"legacy-color-6\",\"name\":\"Coral (original)\",\"hex\":\"#e46867\"},{\"kind\":\"color\",\"id\":\"legacy-color-7\",\"name\":\"Skeletal (original)\",\"hex\":\"#d8d0af\"},{\"kind\":\"color\",\"id\":\"legacy-color-8\",\"name\":\"Spectral (original)\",\"hex\":\"#8ed9cd\"},{\"kind\":\"color\",\"id\":\"legacy-color-9\",\"name\":\"Infernal (original)\",\"hex\":\"#7b211e\"},{\"kind\":\"color\",\"id\":\"legacy-color-10\",\"name\":\"Celestial (original)\",\"hex\":\"#e6dca4\"},{\"kind\":\"color\",\"id\":\"legacy-color-11\",\"name\":\"Voidborn (original)\",\"hex\":\"#16101f\"},{\"kind\":\"color\",\"id\":\"legacy-color-12\",\"name\":\"Eldritch (original)\",\"hex\":\"#6b426c\"},{\"kind\":\"color\",\"id\":\"legacy-color-13\",\"name\":\"Stone Golem (original)\",\"hex\":\"#6f7068\"},{\"kind\":\"color\",\"id\":\"legacy-color-14\",\"name\":\"Crystal (original)\",\"hex\":\"#896bc2\"},{\"kind\":\"color\",\"id\":\"legacy-color-15\",\"name\":\"Magma (original)\",\"hex\":\"#4a1713\"},{\"kind\":\"color\",\"id\":\"legacy-color-16\",\"name\":\"Glacial (original)\",\"hex\":\"#93c9df\"},{\"kind\":\"color\",\"id\":\"legacy-color-17\",\"name\":\"Stormcharged (original)\",\"hex\":\"#48536e\"},{\"kind\":\"color\",\"id\":\"legacy-color-18\",\"name\":\"Clockwork Robot (original)\",\"hex\":\"#9a6a37\"},{\"kind\":\"color\",\"id\":\"legacy-color-19\",\"name\":\"Neon Synth (original)\",\"hex\":\"#251d54\"},{\"kind\":\"color\",\"id\":\"legacy-color-20\",\"name\":\"Fluffy (original)\",\"hex\":\"#d6a987\"},{\"kind\":\"color\",\"id\":\"legacy-color-21\",\"name\":\"Jelly (original)\",\"hex\":\"#a7ec69\"},{\"kind\":\"color\",\"id\":\"legacy-color-22\",\"name\":\"Baby (original)\",\"hex\":\"#efb398\"}]};\n","modules/coach-world-rts/npc-cosmetic-grants.js":"var Q=(e,r)=>()=>(e&&(r=e(e=0)),r);function m(e=globalThis.localStorage){try{let r=JSON.parse(e?.getItem(y)||\"null\");return A(r)?r:null}catch{return null}}function S(e,r=globalThis.localStorage){let t=g??m(r);return t?.gameGeneration>1?`${e}/game/${t.gameGeneration}`:e}function re(e=globalThis.localStorage){if(!e)return;let r=Array.from({length:e.length},(t,o)=>e.key(o));for(let t of r)/^(?:myr5-roguelite-preview-v1\\/|myr5-world-run-outbox-v1\\/)/.test(t)&&e.removeItem(t)}function te(e,{storage:r=globalThis.localStorage}={}){if(!A(e))throw b();let t=g??m(r);if(t&&e.gameGeneration<t.gameGeneration)throw b(t);let o={gameGeneration:e.gameGeneration,gameResetAt:e.gameResetAt};if(t&&t.gameGeneration===o.gameGeneration&&t.gameResetAt!==o.gameResetAt)throw b(t);if(g=o,(!t||t.gameGeneration!==o.gameGeneration)&&(o.gameResetAt>0&&re(r),r?.setItem(y,JSON.stringify(o)),t)){for(let i of ee)i(o);globalThis.dispatchEvent?.(new CustomEvent(\"myr5:game-generation-changed\",{detail:o}))}return o}function k(e=globalThis.myr5AuthenticatedAccount){let r=g??m();return!r?.gameResetAt||!e?.user?.id||e.gameGeneration===r.gameGeneration}var y,g,ee,A,b,w=Q(()=>{y=\"myr5-game-generation-v1\",g=null,ee=new Set,A=e=>Number.isSafeInteger(e?.gameGeneration)&&e.gameGeneration>=1&&Number.isSafeInteger(e?.gameResetAt)&&e.gameResetAt>=0,b=e=>Object.assign(Error(\"Game progress changed. Reopen the game.\"),{code:\"game_generation_mismatch\",status:409,...e});typeof globalThis.window==\"object\"&&typeof globalThis.addEventListener==\"function\"&&globalThis.addEventListener(\"storage\",e=>{if(!(e.key!==y||!e.newValue))try{te(JSON.parse(e.newValue))}catch{}})});w();w();var R=Object.freeze({jogging:{name:\"Subway Burpees\",types:[\"cardio\",\"legs\"]},\"plank-flight\":{name:\"Jet Plank Joyride\",types:[\"core\",\"balance\"]},cover:{name:\"Crouching Crisis\",types:[\"legs\",\"boxing\"]},dance:{name:\"Dance, Dance, and Obey\",types:[\"cardio\",\"martial-arts\"]}}),oe=[{id:\"chest\",name:\"Chest\",description:\"Push-up progressions\"},{id:\"legs\",name:\"Legs\",description:\"Squats and split squats\"},{id:\"hips\",name:\"Hips & glutes\",description:\"Hinges and bridges\"},{id:\"core\",name:\"Core\",description:\"Plank holds\"},{id:\"shoulders\",name:\"Shoulders\",description:\"Controlled arm movement\"},{id:\"balance\",name:\"Balance\",description:\"Single-leg yoga holds\"},{id:\"yoga\",name:\"Yoga\",description:\"Standing poses\"},{id:\"stances\",name:\"Martial stances\",description:\"Controlled stance holds\"},{id:\"boxing\",name:\"Boxing\",description:\"Slow shadowboxing\"},{id:\"cardio\",name:\"Cardio\",description:\"Marches and jacks\"}],p=[];function l(e,r,t,o,i={}){let a=i.kind??([\"plank\",\"sideplank\",\"balance\",\"yoga\",\"stance\"].includes(o)?\"hold\":o===\"boxing\"?\"pace\":o===\"march\"?\"steps\":\"reps\"),n=i.view??([\"pushup\",\"hinge\",\"bridge\",\"plank\",\"split\"].includes(o)?\"side\":\"front\"),s=a===\"hold\"?\"Pose hold\":a===\"pace\"?\"Active hand time\":a===\"steps\"?\"Knee lifts\":\"Repetitions\",Y=i.limits??\"Counts the visible movement; does not grade technique.\",V=n===\"side\"?\"Camera beside you.\":n===\"angle\"?\"Camera at a slight angle.\":\"Face the camera.\";p.push({id:r,group:e,name:t,detector:o,kind:a,view:n,measurement:s,limits:Y,difficulty:p.filter(Z=>Z.group===e).length+1,defaultGoal:a===\"hold\"||a===\"pace\"?15:5,evidence:\"rule-based; camera validation pending\",source:\"mediapipe\",...i,hint:`${V} ${i.cue??\"Keep the joints used for this movement visible.\"}`})}var ae=\"Counts elbow bend and return. Hand shape, support stability and chest depth are not verified.\";for(let[e,r,t]of[[\"knee-pushup\",\"Knee push-up\",{support:\"knees\",cue:\"Knees down; show shoulder, elbow, wrist and hip.\"}],[\"high-incline-pushup\",\"High incline push-up\",{incline:!0,cue:\"Hands on a stable high surface; show your full arm and torso.\"}],[\"low-incline-pushup\",\"Low incline push-up\",{incline:!0,cue:\"Hands on a stable lower surface; show your full arm and torso.\"}],[\"pushup\",\"Regular push-up\",{}],[\"wide-pushup\",\"Wide push-up\",{wide:!0}],[\"slow-pushup\",\"Slow push-up\",{minCycle:1.2}],[\"diamond-pushup\",\"Diamond push-up\",{narrow:!0}],[\"decline-pushup\",\"Feet-elevated push-up\",{decline:!0,cue:\"Feet on a stable low surface; show your full arm and torso.\"}]])l(\"chest\",e,r,\"pushup\",{cue:\"Show shoulder, elbow, wrist and hip. Start with your arm extended.\",limits:ae,...t});for(let[e,r,t]of[[\"shallow-squat\",\"Shallow squat\",{travel:.18}],[\"squat\",\"Bodyweight squat\",{}],[\"wide-squat\",\"Wide squat\",{wide:!0}],[\"pause-squat\",\"Pause squat\",{dwell:.8}],[\"slow-squat\",\"Slow squat\",{minCycle:1.2}],[\"split-left\",\"Split squat \\xB7 left\",{detector:\"split\",side:\"left\"}],[\"split-right\",\"Split squat \\xB7 right\",{detector:\"split\",side:\"right\"}],[\"wall-sit\",\"Wall sit\",{detector:\"yoga\",pose:\"wall-sit\",kind:\"hold\",view:\"side\",cue:\"Back on a wall, thighs level. Show shoulder, hip and knee.\",limits:\"Times the hip angle and thigh level. Wall contact and knee position are not tracked.\"}]])l(\"legs\",e,r,t.detector??\"squat\",{cue:t.side?\"Show the front leg from hip to knee. Start tall.\":\"Show shoulders and hips. Start standing tall.\",...t});for(let[e,r,t,o]of[[\"small-hinge\",\"Small hip hinge\",\"hinge\",{bend:25}],[\"hip-hinge\",\"Hip hinge\",\"hinge\",{}],[\"good-morning\",\"Bodyweight good morning\",\"hinge\",{arms:\"chest\"}],[\"glute-bridge\",\"Glute bridge\",\"bridge\",{}],[\"pause-bridge\",\"Pause glute bridge\",\"bridge\",{dwell:.8}]])l(\"hips\",e,r,t,{cue:t===\"bridge\"?\"Lie on your back, knees bent. Show shoulder, hip and knee. Start with hips down.\":\"Show shoulder, hip and knee. Start upright; hinge slowly.\",limits:\"Tracks hip movement. Back curvature and muscle engagement are not verified.\",...o});for(let[e,r,t]of[[\"knee-plank\",\"Knee plank\",{support:\"knees\"}],[\"high-plank\",\"High plank\",{}],[\"forearm-plank\",\"Forearm plank\",{forearm:!0}],[\"side-knee-left\",\"Side knee plank \\xB7 left\",{side:\"left\",support:\"knees\"}],[\"side-knee-right\",\"Side knee plank \\xB7 right\",{side:\"right\",support:\"knees\"}],[\"side-plank-left\",\"Side plank \\xB7 left\",{side:\"left\"}],[\"side-plank-right\",\"Side plank \\xB7 right\",{side:\"right\"}]])l(\"core\",e,r,t.side?\"sideplank\":\"plank\",{view:t.side?\"front\":\"side\",cue:t.side?\"Show both shoulders, hips and the supporting knee. Stack your shoulders.\":\"Show shoulder, elbow, wrist, hip and supporting knee.\",limits:\"Times the shoulder\\u2013hip\\u2013knee line. Feet, floor contact and loading are not tracked.\",...t});for(let[e,r,t,o]of[[\"front-raise\",\"Front arm raise\",\"raise\",{view:\"side\",plane:\"front\"}],[\"lateral-raise\",\"Lateral arm raise\",\"raise\",{}],[\"overhead-reach\",\"Overhead reach\",\"raise\",{overhead:!0}],[\"standing-press\",\"Standing arm press\",\"press\",{}],[\"slow-press\",\"Slow arm press\",\"press\",{minCycle:1.2}]])l(\"shoulders\",e,r,t,{cue:\"Keep both shoulders, elbows, wrists and hips visible. Use controlled, unloaded movement.\",...o});for(let[e,r,t]of[[\"knee-balance\",\"Knee-lift balance\",{kneeLift:!0}],[\"low-tree\",\"Low tree pose\",{low:!0}],[\"tree\",\"Tree pose\",{}],[\"overhead-tree\",\"Tree \\xB7 arms overhead\",{overhead:!0}]])l(\"balance\",e,r,\"balance\",{cue:\"Show both hips and knees. Either leg works; keep a support nearby if needed.\",limits:\"Times the visible leg shape. Balance, foot pressure and support use are not verified.\",...t});for(let[e,r,t]of[[\"mountain\",\"Mountain\",\"mountain\"],[\"salute\",\"Upward salute\",\"salute\"],[\"chair\",\"Chair pose\",\"chair\"],[\"warrior-one\",\"Warrior I\",\"warrior-one\"],[\"warrior\",\"Warrior II\",\"warrior\"],[\"goddess\",\"Goddess pose\",\"goddess\"]])l(\"yoga\",e,r,\"yoga\",{pose:t,view:[\"chair\",\"warrior-one\"].includes(t)?\"side\":\"front\",cue:\"Show arms, hips and knees. Hold only a comfortable position.\",limits:\"Times broad pose shape. Foot rotation and joint alignment are not graded.\"});for(let[e,r,t]of[[\"high-horse\",\"High horse stance\",{high:!0}],[\"horse\",\"Horse stance\",{}],[\"low-horse\",\"Lower horse stance\",{low:!0}],[\"front-stance-left\",\"Front stance \\xB7 left\",{side:\"left\"}],[\"front-stance-right\",\"Front stance \\xB7 right\",{side:\"right\"}]])l(\"stances\",e,r,\"stance\",{view:t.side?\"side\":\"front\",cue:\"Keep hips and knees visible. Move slowly into a comfortable stance.\",limits:\"Times stance shape only. This is not martial-arts technique instruction.\",...t});for(let[e,r,t]of[[\"jab-left\",\"Left straight practice\",{side:\"left\"}],[\"jab-right\",\"Right straight practice\",{side:\"right\"}],[\"boxing\",\"Alternating straights\",{}],[\"double-jab\",\"Double-jab practice\",{double:!0}]])l(\"boxing\",e,r,\"boxing\",{view:\"angle\",cue:\"Show shoulders, elbows, wrists and hips. Practice slow, controlled punches into empty space.\",limits:\"Tracks active hand time and relative pace. Punch type, power, accuracy and guard are not verified.\",...t});for(let[e,r,t,o]of[[\"march\",\"Easy march\",\"march\",{}],[\"high-march\",\"High-knee march\",\"march\",{lift:.4}],[\"jogging\",R.jogging.name,\"march\",{lift:.08,runner:!0,endless:!0,cue:\"The route moves automatically. Crouch deeply to duck, raise both arms to jump, and lean left or right to change lanes. Keep your upper body visible.\"}],[\"step-jack\",\"Step jack\",\"jack\",{step:!0}],[\"jumping-jack\",\"Jumping jack\",\"jack\",{}]])l(\"cardio\",e,r,t,{cue:t===\"jack\"?\"Show your upper body and knees. Start with arms down and knees close together.\":\"Show shoulders, hips and both knees. Start standing tall.\",limits:t===\"jack\"?\"Counts arm-and-knee opening cycles. Feet, airtime and landings are not tracked.\":\"Counts knee lifts. Footfalls and impact are not verified.\",...o});l(\"core\",\"plank-flight\",R[\"plank-flight\"].name,\"plank-flight\",{kind:\"hold\",view:\"front\",flight:!0,standalone:!0,endless:!0,cue:\"Phone faces you from a low stable stand. Keep both hands down, shoulders, arms, hips and knees visible. Rise/lower comfortably; lean left/right to fly.\",limits:\"Estimates face-on height and lean. Floor contact, arm loading and technique are not measured.\"});var T=Object.freeze(Object.fromEntries(p.map(e=>[e.id,Object.freeze(e)]))),ie=Object.freeze(Object.fromEntries(oe.map(e=>[e.id,Object.freeze(p.filter(r=>r.group===e.id&&!r.standalone))]))),Pe=p.length;var I=Object.freeze([\"roster/21-flyer--winged_humanoid_3d_model\",\"roster/18-quad-all--wooden_robot_3d_model\"]),ne=[[\"myr5\",\"original\",[\"MYR5 Jacked LaLAN\",\"Jack LaLanne\",\"jacked strength\",\"local area network\"],[\"MYR5 AT-Lift Prime\",\"Charles Atlas\",\"lift\",\"model designation\"]],[\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"arms\",[\"Arnoid Press\",\"Arnold Schwarzenegger\",\"Arnold press\",\"android\"],[\"ScotTorque Curl\",\"Larry Scott\",\"curl\",\"motor torque\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"arms\",[\"Ferri-Delt L0U\",\"Lou Ferrigno\",\"delt\",\"robot designation\"],[\"R0N Curlman\",\"Ronnie Coleman\",\"curl\",\"engine manifold\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model\",\"arms\",[\"Frank Delt-Zan3\",\"Frank Zane\",\"delt\",\"magnetic coil\"],[\"Cutlerator Curl\",\"Jay Cutler\",\"curl\",\"accelerator\"]],[\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"arms\",[\"KAI-nematic Raise\",\"Kai Greene\",\"raise\",\"kinematic mechanism\"],[\"Philament Heave Press\",\"Phil Heath\",\"press\",\"electrical filament\"]],[\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"arms\",[\"Wheel-er Servo-Shrug\",\"Flex Wheeler\",\"shrug\",\"servo wheel\"]],[\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"arms\",[\"Ree-volts Raise\",\"Steve Reeves\",\"raise\",\"voltage\"]],[\"08-shard-asym--robot_3d_model4\",\"arms\",[\"S3RG Oliv-Armature\",\"Sergio Oliva\",\"arm\",\"motor armature\"]],[\"17-shellcap-manyarm--3d_character_figurine\",\"arms\",[\"Delt-alinn Bailey\",\"Dana Linn Bailey\",\"delt\",\"Delta designation\"]],[\"06-ridge-triad--geometric_robot_3d_model1\",\"chest\",[\"Oliva-C Pec\",\"Sergio Oliva\",\"pec\",\"vacuum drive\"]],[\"08-shard-asym--geometric_robot_3d_model\",\"chest\",[\"Pec-n0ld Reactor\",\"Arnold Schwarzenegger\",\"pec\",\"reactor\"]],[\"16-spade-arch--pyramid_head_figure_3d_model\",\"chest\",[\"Franx Pec-Zane\",\"Frank Zane\",\"pec\",\"motor drive\"],[\"Y4TES Yoke\",\"Dorian Yates\",\"press\",\"powered yoke\"]],[\"04-crest-wedge--robot_creature_3d_model\",\"chest\",[\"L0U Ferri-pecctor\",\"Lou Ferrigno\",\"pec\",\"reactor\"],[\"Wheeler Wheel-Bench\",\"Flex Wheeler\",\"bench\",\"drive wheel\"]],[\"16-spade-arch--stylized_humanoid_3d_model\",\"chest\",[\"Cutlerator Press\",\"Jay Cutler\",\"press\",\"accelerator\"]],[\"04-crest-wedge--stylized_3d_character\",\"chest\",[\"Gr33ne Grid-Press\",\"Kai Greene\",\"press\",\"power grid designation\"]],[\"09-monolith-tanka--3d_robot_model\",\"chest\",[\"chris BenchSTEAD\",\"Chris Bumstead\",\"bench\",\"robot\"]],[\"09-monolith-tanka--white_horned_robot_3d_model\",\"chest\",[\"Cavalier-EX Chest\",\"Jeff Cavaliere\",\"chest\",\"exercise model EX\"]],[\"15-orbital-coili--robot_character_3d_model\",\"quads\",[\"t-OHM Platzma\",\"Tom Platz\",\"squat\",\"plasma\"]],[\"13-fan-split--stylized_3d_character2\",\"quads\",[\"Quad-Cutler LEGS THO\",\"Jay Cutler\",\"quad\",\"circuit\"],[\"BumSTEADy Squat\",\"Chris Bumstead\",\"squat\",\"steady-state control\"]],[\"22-curve--stylized_cartoon_figure_3d_model\",\"quads\",[\"Kai Gr33ne Lunge\",\"Kai Greene\",\"lunge\",\"gear designation\"]],[\"02-taper-tallstalk--3d_humanoid_figure\",\"quads\",[\"Hane-Yoke Lunge\",\"Lee Haney\",\"lunge\",\"powered yoke\"],[\"Stern-on Squat\",\"Erin Stern\",\"squat\",\"power-on switch\"]],[\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"quads\",[\"Oli-Volt Lunge\",\"Sergio Oliva\",\"lunge\",\"voltage\"],[\"Y4TES Yoke-Squat\",\"Dorian Yates\",\"squat\",\"robot designation\"]],[\"18-quad-all--wooden_four-legged_robot_3d_model\",\"quads\",[\"Priest-L33 Squat\",\"Lee Priest\",\"squat\",\"piston\"]],[\"18-quad-all--stylized_quadruped_3d_model\",\"quads\",[\"Quadzilla Platz\",\"Tom Platz\",\"quads\",\"kaiju creature\"],[\"SternShift Squat\",\"Erin Stern\",\"squat\",\"gear shift\"]],[\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"glutes\",[\"Con-Thrust-Era\",\"Bret Contreras\",\"hip thrust\",\"thruster\"]],[\"23-blob-texture-bodies--sand_creature_3d_model\",\"glutes\",[\"Tingularity Thrust\",\"Chloe Ting\",\"hip thrust\",\"singularity\"]],[\"03-pearl-orb-ring--stylized_3d_character1\",\"glutes\",[\"Gyrvanium Glute-Drive\",\"Caroline Girvan\",\"glute\",\"futuristic alloy\"],[\"Jill-iON Hinge-Flex\",\"Jillian Michaels\",\"hip hinge\",\"ion drive\"]],[\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"glutes\",[\"Trac-ION Hip-Thrust\",\"Tracy Anderson\",\"hip thrust\",\"ion drive\"],[\"Simmonics Bridge-Servo\",\"Whitney Simmons\",\"bridge\",\"electronics servo\"]],[\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"glutes\",[\"Robo-ertson Bridge\",\"Heather Robertson\",\"bridge\",\"robot\"],[\"Hearn-ess ExoHinge\",\"Katy Hearn\",\"hinge\",\"exosuit harness\"]],[\"03-pearl-orb-ring--circle_center\",\"glutes\",[\"Con-Terra Coil-Bridge\",\"Bret Contreras\",\"bridge\",\"planetary coil\"],[\"Cyb-Ho Holo-Bridge\",\"Cassey Ho\",\"bridge\",\"cyborg hologram\"]],[\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"glutes\",[\"Chlo-Turbo Thrust\",\"Chloe Ting\",\"thrust\",\"turbine\"]],[\"10-petal-wisp--fantasy_creature_3d_model4\",\"yoga\",[\"Adriene A-Sana Mk II\",\"Adriene Mishler\",\"asana\",\"model revision\"]],[\"19-genie-multi--fantasy_creature_3d_model\",\"yoga\",[\"Mat-Gregor Matrix\",\"Kino MacGregor\",\"mat\",\"simulation matrix\"]],[\"10-petal-wisp--ghost_character_3d_model\",\"yoga\",[\"Warri0r-ner Drive\",\"Dylan Werner\",\"warrior pose\",\"motor designation\"],[\"Kathryn Bend-1g\",\"Kathryn Budig\",\"bend\",\"robot\"]],[\"08-shard-asym--stylized_action_figure_3d_model\",\"yoga\",[\"Yee-lastic Flow\",\"Rodney Yee\",\"yoga flow\",\"elastic mechanics\"]],[\"22-curve--stylized_alien_3d_model\",\"yoga\",[\"Strom Flex Current\",\"Max Strom\",\"stretch\",\"electric current\"]],[\"10-petal-wisp--stylized_creature_3d_model\",\"yoga\",[\"Iyeng-Arc Inversion\",\"B. K. S. Iyengar\",\"inversion\",\"powered arc\"]],[\"19-genie-multi--stylized_octopus_3d_model\",\"yoga\",[\"Yee Gyro-Yogi\",\"Rodney Yee\",\"yoga\",\"gyroscope\"]],[\"01-seed-pearo--cute_alien_figure_3d_model\",\"yoga\",[\"Wern-Axis Flow\",\"Dylan Werner\",\"flow\",\"robotic axis\"],[\"Mat-Gregor Drive\",\"Kino MacGregor\",\"mat practice\",\"motor drive\"]],[\"01-seed-pearo--white_3d_character_model\",\"yoga\",[\"Budig-ital Balance\",\"Kathryn Budig\",\"balance\",\"digital\"],[\"Corn Kernel Cobra\",\"Seane Corn\",\"cobra pose\",\"computing kernel\"]],[\"09-monolith-tanka--boxy_humanoid_3d_model\",\"martial-arts\",[\"Bruce L3300\",\"Bruce Lee\",\"kick\",\"alphanumeric call sign\"],[\"GSP-Drive\",\"Georges St-Pierre\",\"spar\",\"motor drive\"]],[\"11-anvil-cask--clay-style_robot_3d_model\",\"martial-arts\",[\"Anderson Sil-VOLT\",\"Anderson Silva\",\"strike\",\"voltage\"],[\"r0nda Rous-Bot\",\"Ronda Rousey\",\"armbar\",\"robot\"]],[\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"martial-arts\",[\"C0N0R McG-Gear\",\"Conor McGregor\",\"kick\",\"gear transmission\"],[\"Nor-Rotor Punch\",\"Chuck Norris\",\"punch\",\"rotor\"]],[\"14-chisel-spire--cone_head_3d_model\",\"martial-arts\",[\"LiDAR Jett-Jab\",\"Jet Li\",\"jab\",\"laser ranging\"],[\"Jaa-Bot Jab\",\"Tony Jaa\",\"jab\",\"robot\"]],[\"08-shard-asym--fantasy_creature_3d_model2\",\"martial-arts\",[\"Waters0nic\",\"Michelle Waterson\",\"kick\",\"sonic drive\"],[\"Amanda Nu-NEURAL\",\"Amanda Nunes\",\"strike\",\"neural implant\"]],[\"09-monolith-tanka--mini_robot_3d_model\",\"martial-arts\",[\"Machi-Droid-a\",\"Lyoto Machida\",\"chop\",\"android\"],[\"Bas Rut-TORque\",\"Bas Rutten\",\"kick\",\"motor torque\"]],[\"09-monolith-tanka--robot_3d_model3\",\"martial-arts\",[\"Israel Ade-SYNC-ya\",\"Israel Adesanya\",\"strike\",\"synchronization\"],[\"Shev-Servo Sweep\",\"Valentina Shevchenko\",\"sweep\",\"servo\"]],[\"13-fan-split--stylized_toy_3d_model\",\"martial-arts\",[\"BlankSaber Kick\",\"Billy Blanks\",\"kick\",\"energy saber\"],[\"Wilsonic Kick\",\"Don Wilson\",\"kick\",\"sonic drive\"]],[\"09-monolith-tanka--robot_3d_model2\",\"martial-arts\",[\"Rous-Torque Throw\",\"Ronda Rousey\",\"throw\",\"motor torque\"],[\"Jackie Chain-Kick\",\"Jackie Chan\",\"kick\",\"chain drive\"]],[\"07-bulb-sphereling--humanoid_robot_3d_model\",\"cardio\",[\"B0LT Sprint-Drive\",\"Usain Bolt\",\"sprint\",\"electrical bolt drive\"]],[\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"cardio\",[\"Fon-Dynamo Footwork\",\"Jane Fonda\",\"footwork\",\"dynamo\"],[\"Simmonics Sweatstream\",\"Richard Simmons\",\"aerobic sweat\",\"electronics stream\"]],[\"06-ridge-triad--robot_3d_model1\",\"cardio\",[\"M0 Farah Fastlane\",\"Mo Farah\",\"fast run\",\"electric capacitance\"]],[\"03-pearl-orb-ring--robot_3d_model\",\"cardio\",[\"Feli-X Footdrive\",\"Allyson Felix\",\"footwork\",\"X-model drive\"],[\"Rad-Clutch Run\",\"Paula Radcliffe\",\"run\",\"motor clutch\"]],[\"20-lume--robotic_figure_3d_model\",\"cardio\",[\"Pre-Font-AI-ne Pace\",\"Steve Prefontaine\",\"pace\",\"AI running model\"],[\"Ohm-wens Run\",\"Jesse Owens\",\"run\",\"electrical resistance\"]],[\"07-bulb-sphereling--cute_robot_3d_model\",\"cardio\",[\"Billy Blanx Beam\",\"Billy Blanks\",\"boot camp\",\"waveform\"],[\"GebreShell Runloop\",\"Haile Gebrselassie\",\"run\",\"software shell loop\"]],[\"01-seed-pearo--blank_toy_figure_3d_model\",\"cardio\",[\"Phelpeller Pulse\",\"Michael Phelps\",\"cardio pulse\",\"propeller\"],[\"Fon-Droid Footwork\",\"Jane Fonda\",\"footwork\",\"android\"]],[\"18-quad-all--dragon_creature_3d_model\",\"meditation\",[\"H0F-Lung\",\"Wim Hof\",\"breathing\",\"oxygen-code lung\"]],[\"18-quad-all--fantasy_creature_3d_model1\",\"meditation\",[\"Max Ohm-Strom Breath\",\"Max Strom\",\"breathing\",\"electrical resistance\"],[\"Yee-uro Zen\",\"Rodney Yee\",\"mindful movement\",\"neural interface\"]],[\"18-quad-all--fantasy_creature_3d_model3\",\"meditation\",[\"Shiva Rea-spatory\",\"Shiva Rea\",\"respiration\",\"airlock\"]],[\"18-quad-all--four-legged_robot_3d_model\",\"meditation\",[\"Tara-5tile5-Standby\",\"Tara Stiles\",\"breathing\",\"computer standby\"]],[\"18-quad-all--quadruped_robot_3d_model1\",\"meditation\",[\"Werner Wind0wn\",\"Dylan Werner\",\"wind-down practice\",\"alphanumeric standby mode\"]],[\"18-quad-all--quadruped_robot_3d_model\",\"meditation\",[\"Yee Holo-Om\",\"Rodney Yee\",\"mindful movement\",\"hologram\"]],[\"18-quad-all--robotic_dog_3d_model\",\"meditation\",[\"Budig-ital Breath\",\"Kathryn Budig\",\"breathing\",\"digital\"]],[\"12-seedpod-snailslug--stylized_slug_3d_model\",\"meditation\",[\"H0F Hold-Circuit\",\"Wim Hof\",\"breath hold\",\"circuit and model code\"],[\"Ohm-Strom Still\",\"Max Strom\",\"stillness\",\"electrical resistance\"]],[\"12-seedpod-snailslug--stylized_worm_3d_model\",\"meditation\",[\"Pilat-OS Calm-Circuit\",\"Joseph Pilates\",\"mindful breathing\",\"operating system circuit\"]],[\"14-chisel-spire--low_poly_robot_3d_model\",\"meditation\",[\"Rea-Spire Relay\",\"Shiva Rea\",\"respiration\",\"air relay\"]],[\"01-seed-pearo--3d_character_model\",\"meditation\",[\"McKe-O2 Nose-Cone\",\"Patrick McKeown\",\"nasal breathing\",\"rocket nose cone\"],[\"Vertu-al Vent\",\"Shona Vertue\",\"ventilation\",\"virtual reality\"]],[\"23-blob-texture-bodies--blob_creature_3d_model\",\"meditation\",[\"Blob 1 \\xB7 H0F Hold-Drive\",\"Wim Hof\",\"breath hold\",\"air drive and model code\"]],[\"01-seed-pearo--blank_humanoid_figure_3d_model\",\"meditation\",[\"Corn Kernel Om\",\"Seane Corn\",\"meditation\",\"computing kernel\"],[\"Kino-Kinetic Breath\",\"Kino MacGregor\",\"breathing\",\"kinematics\"]]],L=Object.freeze(Object.fromEntries(ne.map(([e,r,...t])=>[e===\"myr5\"?e:`roster/${e}`,Object.freeze({track:r,names:Object.freeze(t.map(([o,i,a,n])=>Object.freeze({name:o,person:i,workout:a,scifi:n})))})])));var Ne=Object.freeze(Object.fromEntries(Object.entries(L).map(([e,r])=>[e,Object.freeze(r.names.map(t=>t.name))])));function se(e){let r=L[e];if(!r)throw RangeError(`No coach name for ${e}.`);return r}var x=(e,r=!1)=>{let t=se(e).names;return t[r?1:0]?.name??t[0].name};var f=Object.freeze({sprintRounds:5,gentleActiveSeconds:300,paceActiveSeconds:300,meditationDays:Object.freeze([3,6,9,12])}),le=[{id:\"myr5\",sourceLabel:\"Original MYR5\"},{id:\"roster/01-seed-pearo--3d_character_model\",sourceLabel:\"Seed \\xB7 Pearo 1\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 2\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 5\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",sourceLabel:\"Taper \\xB7 Tallstalk 1\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",sourceLabel:\"Taper \\xB7 Tallstalk 2\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",sourceLabel:\"Taper \\xB7 Tallstalk 3\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 1\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--circle_center\",sourceLabel:\"Pearl \\xB7 Orb 2\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 3\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 4\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",sourceLabel:\"Pearl \\xB7 Orb 5\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 6\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",sourceLabel:\"Crest \\xB7 Wedge 1\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",sourceLabel:\"Crest \\xB7 Wedge 2\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",sourceLabel:\"Slope \\xB7 Bobble 1\",group:\"Slope \\xB7 Bobble\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 1\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 2\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 1\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 2\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",sourceLabel:\"Shard \\xB7 Asym 1\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--robot_3d_model4\",sourceLabel:\"Shard \\xB7 Asym 2\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",sourceLabel:\"Shard \\xB7 Asym 3\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",sourceLabel:\"Shard \\xB7 Asym 4\",group:\"Shard \\xB7 Asym\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",sourceLabel:\"Monolith \\xB7 Tanka 1\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 2\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 3\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",sourceLabel:\"Monolith \\xB7 Tanka 5\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 6\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 1\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 2\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",sourceLabel:\"Petal \\xB7 Wisp 3\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",sourceLabel:\"Anvil \\xB7 Cask 1\",group:\"Anvil \\xB7 Cask\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 1\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 2\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/13-fan-split--stylized_3d_character2\",sourceLabel:\"Fan \\xB7 Split 1\",group:\"Fan \\xB7 Split\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",sourceLabel:\"Fan \\xB7 Split 2\",group:\"Fan \\xB7 Split\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 1\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 2\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",sourceLabel:\"Orbital \\xB7 Coili 1\",group:\"Orbital \\xB7 Coili\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",sourceLabel:\"Spade \\xB7 Arch 1\",group:\"Spade \\xB7 Arch\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",sourceLabel:\"Spade \\xB7 Arch 2\",group:\"Spade \\xB7 Arch\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",sourceLabel:\"Shellcap \\xB7 Manyarm 1\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 2\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 3\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",sourceLabel:\"Four-legged 1\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",sourceLabel:\"Four-legged 2\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",sourceLabel:\"Four-legged 3\",group:\"Four-legged\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",sourceLabel:\"Four-legged 4\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",sourceLabel:\"Four-legged 5\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",sourceLabel:\"Four-legged 6\",group:\"Four-legged\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",sourceLabel:\"Four-legged 7\",group:\"Four-legged\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",sourceLabel:\"Four-legged 9\",group:\"Four-legged\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",sourceLabel:\"Genie \\xB7 Multi 1\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",sourceLabel:\"Genie \\xB7 Multi 2\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",sourceLabel:\"Genie \\xB7 Multi 3\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",sourceLabel:\"Genie \\xB7 Multi 4\",group:\"Genie \\xB7 Multi\"},{id:\"roster/20-lume--robotic_figure_3d_model\",sourceLabel:\"Lume 1\",group:\"Lume\"},{id:\"roster/22-curve--stylized_alien_3d_model\",sourceLabel:\"Curve 1\",group:\"Curve\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",sourceLabel:\"Curve 2\",group:\"Curve\"},{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",sourceLabel:\"Blob 1\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",sourceLabel:\"Blob 2\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",sourceLabel:\"Blob 3\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",sourceLabel:\"Blob 4\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",sourceLabel:\"Blob 5\",group:\"Blob\"}],de=le.map(e=>({...e,label:x(e.id)})),ce=[{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/20-lume--robotic_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"expert\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/08-shard-asym--robot_3d_model4\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",tracks:[\"chest\",\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",tracks:[\"chest\"],difficulty:\"expert\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",tracks:[\"glutes\"],difficulty:\"hard\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",tracks:[\"glutes\"],difficulty:\"expert\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",tracks:[\"quads\"],difficulty:\"easy\"},{id:\"roster/13-fan-split--stylized_3d_character2\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_alien_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",tracks:[\"yoga\"],difficulty:\"expert\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/01-seed-pearo--3d_character_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",tracks:[\"quads\"],difficulty:\"expert\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--circle_center\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",tracks:[\"chest\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"hard\"}],ue={chest:[\"chest\"],quads:[\"legs\"],glutes:[\"hips\"],arms:[\"shoulders\"],yoga:[\"core\",\"balance\",\"yoga\"],\"martial-arts\":[\"stances\",\"boxing\"],cardio:[\"cardio\"],meditation:[\"meditation\"]},P=Object.freeze([\"chest\",\"quads\",\"glutes\",\"arms-shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),ze=Object.freeze({arms:\"arms-shoulders\"}),me=e=>e===\"arms-shoulders\"?\"arms\":e;var M=Object.freeze([\"myr5\",\"roster/23-blob-texture-bodies--blob_creature_3d_model\"]);var N=ce.filter(e=>!M.includes(e.id)).map(e=>({...e,name:x(e.id),groups:e.tracks.flatMap(r=>ue[r])})),pe=Object.freeze(Object.fromEntries(P.map(e=>[e,N.find(r=>r.tracks[0]===me(e)&&r.difficulty===\"easy\").id])));var he=Object.freeze([\"easy\",\"medium\",\"hard\",\"expert\"]),D=e=>Object.freeze(e.map(r=>Object.freeze(r.map(t=>t*10)))),Be=D([[4,8,16],[8,12,20],[12,16,24],[16,20,28]]),Fe=D([[1,2,4],[2,3,5],[3,4,6],[4,5,7]]);var ge=15;var Ue=Object.freeze({uncommon:1,rare:2,legendary:3,secret:3}),He=Object.freeze({uncommon:Object.freeze({color:90,\"64-bit\":7,texture:3}),rare:Object.freeze({color:80,\"64-bit\":15,texture:5}),legendary:Object.freeze({color:70,\"64-bit\":20,texture:10}),secret:Object.freeze({color:60,\"64-bit\":20,texture:20})}),fe=2,G=500*10,d=Object.freeze({sessions:120,activeMinutes:30,expertXpPerMinute:280,meditationMultiplier:fe,trioBonus:G}),_e=d.sessions*(d.activeMinutes*d.expertXpPerMinute*d.meditationMultiplier+d.trioBonus),Ke=d.expertXpPerMinute*ge/60;var z=250,C=z-1,j=40,be=_e/10,Xe=Object.freeze(Array.from({length:z},(e,r)=>10*(j*r+Math.floor((be-j*C)*r*(r-1)/(C*(C-1))))));function h(e=globalThis.localStorage,r=globalThis.myr5AuthenticatedAccount){let t=r?.user?.id;if(t)return`account:${t}${r.dataEpoch!=null?`:${r.dataEpoch}`:\"\"}`;if(!e)return\"guest:unavailable\";let o=\"myr5-local-guest-owner-v1\";try{let i=e.getItem(o);return i||(i=globalThis.crypto?.randomUUID?.()||`guest-${Date.now()}-${Math.random()}`,e.setItem(o,i)),`guest:${i}`}catch{return\"guest:unavailable\"}}var mr={sprint:f.sprintRounds,gentle:f.gentleActiveSeconds,pace:f.paceActiveSeconds};var O=\"myr5-unlock-pending-v1\",ye=Object.freeze([\"creature-skin\",\"ship\",\"reward-pack\"]),Se=new Set([...ye,\"texture\",\"color\",\"palette\",\"boss-skin\"]),ke=({account:e=globalThis.myr5AuthenticatedAccount}={},r=!0)=>{try{let t=typeof e==\"string\"?e:e?.user?.id;if(typeof t==\"string\"&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(t))return`${O}/account/${t}${typeof e==\"object\"&&e?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(e.dataEpoch))}`:\"\"}`;let o=r?h(globalThis.localStorage,e):localStorage.getItem(\"myr5-local-guest-owner-v1\");return o?`${O}/guest/${encodeURIComponent(r?o:`guest:${o}`)}`:null}catch{return null}},q=(e,r={})=>Se.has(e)||r.accountScoped===!0?ke(r):O;var B=e=>{try{let r=JSON.parse(localStorage.getItem(e)||\"{}\");return r&&typeof r==\"object\"&&!Array.isArray(r)?r:{}}catch{return{}}},F=(e,r)=>Array.isArray(e[r])?e[r]:[];function we(e,r,t){if(!e)return;let o=B(e),i=F(o,r),a=t(i);if(a!==i)try{localStorage.setItem(e,JSON.stringify({...o,[r]:a}))}catch{}}function U(e,r){let t=new Map;for(let{kind:o,id:i}of e){let a=q(o,r);if(!a)continue;let n=t.get(a);n||(n={data:B(a),known:new Map},t.set(a,n)),n.known.has(o)||(n.data[o]=[...F(n.data,o)],n.known.set(o,new Set(n.data[o])));let s=n.known.get(o);s.has(i)||(s.add(i),n.data[o].push(i))}for(let[o,i]of t)try{localStorage.setItem(o,JSON.stringify(i.data))}catch{}}var E=(e,r,t)=>we(q(e,t),e,o=>o.includes(r)?o:[...o,r]);var fr=Object.freeze([\"clay\",\"martial-arts-canvas-gi\",\"arms-rope\",\"yoga-cork\",\"glutes-peach\"]),v=Object.freeze({\"legacy-0\":\"clay\",\"legacy-21\":\"opal-jelly\",\"bubble-glass\":\"glitter-resin\"}),_r=Object.freeze([\"flat\",\"legacy-22\",...Object.keys(v)]),H=e=>Object.hasOwn(v,e)?v[e]:e;var xe=\"myr5-unlocks-v2\",Ce=()=>({texture:[],color:[],palette:[]}),c=(e,r)=>`coach:${encodeURIComponent(e)}:${r}`,u=(e,r)=>e===\"texture\"?H(r):r,Oe=(e,r)=>{let t=/^(coach:[^:]*:)(.*)$/.exec(r);return t?t[1]+u(e,t[2]):r};function K(){try{return JSON.parse(localStorage.getItem(\"myr5-recipe-v1\")||\"{}\").body||\"myr5\"}catch{return\"myr5\"}}var X=()=>S(`${xe}/${encodeURIComponent(h())}`);function _(){try{let e=JSON.parse(localStorage.getItem(X())||\"{}\");return Object.fromEntries([\"texture\",\"color\",\"palette\"].map(r=>[r,Array.isArray(e[r])?[...new Set(e[r].filter(t=>typeof t==\"string\").map(t=>Oe(r,t)))]:[]]))}catch{return Ce()}}function $(e){try{return localStorage.setItem(X(),JSON.stringify(e)),!0}catch{return!1}}var W=(e,r,t)=>_()[e]?.includes(c(t,u(e,r)))??!1;function Ee(e,r){let t=u(e,r);return(_()[e]||[]).some(o=>{let i=/^coach:([^:]+):(.*)$/.exec(o);if(!i||i[2]!==t)return!1;try{return decodeURIComponent(i[1])!==\"myr5\"}catch{return!1}})}var ve=(e,r)=>W(e,r,\"myr5\")||Ee(e,r),Ae=(e,r,t=K())=>t===\"myr5\"?ve(e,r):W(e,r,t);function J(e){let r=globalThis.CustomEvent;if(!(typeof globalThis.dispatchEvent!=\"function\"||typeof r!=\"function\"||!e.length))try{globalThis.dispatchEvent(new r(\"myr5:cosmetics-unlocked\",{detail:{owner:h(),granted:e}}))}catch{}}function Re(e,r,t=K()){if(!k()||![\"texture\",\"color\",\"palette\"].includes(e)||typeof r!=\"string\"||!r||typeof t!=\"string\"||!t)return!1;let o=_(),i=c(t,u(e,r));if(o[e].includes(i)||(o[e].push(i),!$(o)))return!1;let a=u(e,r),n=[{kind:e,id:a,coachId:t}];return E(e,i),t!==\"myr5\"&&E(e,c(\"myr5\",a)),J(n),!0}function Te(e){if(!k())return 0;let r=_(),t=Object.fromEntries([\"texture\",\"color\",\"palette\"].map(a=>[a,new Set(r[a])])),o=[];for(let a of e){if(!t[a.kind]||typeof a.id!=\"string\"||!a.id||typeof a.coachId!=\"string\"||!a.coachId)continue;let n=u(a.kind,a.id),s=c(a.coachId,n);t[a.kind].has(s)||(t[a.kind].add(s),r[a.kind].push(s),o.push({kind:a.kind,id:n,coachId:a.coachId}))}if(!o.length||!$(r))return 0;let i=o.flatMap(({kind:a,id:n,coachId:s})=>[{kind:a,id:c(s,n)},...s===\"myr5\"?[]:[{kind:a,id:c(\"myr5\",n)}]]);return U(i),J(o),o.length}export{Re as grantUnlock,Te as grantUnlocks,Ae as isGranted};\n","modules/coach-world-rts/npc-cosmetic-grants.mjs":"// Compiled to npc-cosmetic-grants.js for the unbundled World runtime.\r\nexport {grantUnlock,grantUnlocks,isGranted} from '../../creature/source/creator/unlock-store.ts';\r\n","modules/coach-world-rts/npc-encounters.css":"/* NPC shop panel inside the AR walkthrough root (same pixel bezel as ar-npcs.css chip). */\r\n.cw-npc-shop{position:absolute;left:12px;right:12px;bottom:calc(16px + env(safe-area-inset-bottom,0px));z-index:6;max-height:62vh;overflow:auto;padding:10px 12px;\r\n background:#0b0f14f0;color:#fff;border:2px solid var(--npc-accent,#ffd23a);border-radius:12px;box-shadow:0 0 0 2px #000,inset 4px 0 0 var(--npc-tint,#a070c0);\r\n font:600 14px/1.25 \"Pixelify Sans\",m5x7,system-ui,sans-serif;touch-action:manipulation}\r\n.cw-npc-shop header{display:flex;align-items:center;gap:8px}.cw-npc-shop header b{font-size:17px}.cw-npc-shop header span{opacity:.75;font-size:12px;flex:1}\r\n.cw-npc-hint{margin:8px 0;padding:8px;border-left:3px solid var(--npc-accent,#ffd23a);background:#ffffff12;font-weight:500;font-size:13px}\r\n.cw-npc-wallet{margin:4px 0 8px;display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}.cw-npc-wallet b{color:var(--npc-accent,#ffd23a);font-size:18px}.cw-npc-wallet small{width:100%;opacity:.7;font-weight:500}\r\n.cw-npc-items{list-style:none;margin:0;padding:0;display:grid;gap:6px}.cw-npc-items li{display:flex;align-items:center;gap:8px;padding:6px 0;border-top:1px solid #ffffff22}\r\n.cw-npc-items li>div{flex:1;min-width:0;display:flex;flex-direction:column}.cw-npc-items li span{opacity:.75;font-size:12px;font-weight:500}.cw-npc-items em{font-style:normal;opacity:.8}\r\n.cw-npc-items li[data-owned=\"true\"]{opacity:.55}.cw-npc-items li[data-affordable=\"false\"][data-owned=\"false\"] button{opacity:.5}\r\n.cw-npc-items button{min-width:72px;min-height:40px}.cw-npc-note{min-height:1.2em;margin:6px 0 0;font-size:12px;opacity:.85}\r\n","modules/coach-world-rts/npc-encounters.mjs":"// World NPC encounter panel: one real achievement hint + a stable 4-item shop paid from the permanent wallet (npc-progression.mjs).\r\n// openNpcEncounter({npc,root,onClose,client,accountId,progress,grants,doc}) -> {element,refresh(),buy(itemId)->Promise<bool>,close() (dispose + onClose),dispose()}\r\n//  client = createWorldClient() (reads .snapshot.base, sends command({type:'npc-purchase',...})); the parent owns the server/guest route.\r\n//  Bought weapons/textures/colours are pushed into the REAL owned systems (unlock-ledger 'weapon', unlock-store 'texture'/'color'): idempotent.\r\nimport {bossStates,shipRequirements,weaponRequirements} from './npc-achievement-rules.js';\r\nimport {readPerformanceProgress,performanceOwner} from '../../performance-progress.mjs';\r\nimport {grantUnlock as ledgerGrant} from '../../unlock-ledger.mjs';\r\nimport {readProgression,npcInventory,CURRENCY} from './npc-progression.mjs';\r\n\r\n// Weapons: unlock-ledger 'weapon' (plain .mjs, safe to import raw). Textures/colours: parent-owned esbuild bundle npc-cosmetic-grants.js\r\n// re-exporting unlock-store.ts grantUnlock(kind,id). Lazy so a missing bundle (tests, dev before build) degrades to \"progression only\".\r\nconst storeP=()=>import('./npc-cosmetic-grants.js');\r\nconst grantCosmetic=async(kind,id)=>{const owner=performanceOwner(),epoch=globalThis.myr5AuthenticatedAccount?.dataEpoch,store=await storeP();if(performanceOwner()!==owner||globalThis.myr5AuthenticatedAccount?.dataEpoch!==epoch)throw new DOMException('Account changed before equipment sync.','AbortError');return store.grantUnlock(kind,id);};\r\nexport const REAL_GRANTS=Object.freeze({weapon:id=>ledgerGrant('weapon',id,{accountScoped:true}),texture:id=>grantCosmetic('texture',id),color:id=>grantCosmetic('color',id)});\r\nconst hash=s=>{let h=0x811c9dc5;for(const c of String(s))h=Math.imul(h^c.codePointAt(0),0x01000193)>>>0;return h;};\r\n\r\n// Every real, still-locked achievement the hint picker can point at. Nothing here grants anything.\r\nexport function achievementHints(progress=readPerformanceProgress()){\r\n const out=[];\r\n for(const b of bossStates(progress))if(b.state!=='done'&&b.requirements&&!b.requirements.starter)out.push({kind:'coach',id:b.id,name:b.name,text:b.requirements.unlock,done:false});\r\n for(const s of shipRequirements())out.push({kind:'ship',id:s.id,name:s.name,text:s.unlock,done:progress.ships?.includes(s.id)===true});\r\n for(const group of ['chest','legs','hips','core','shoulders','balance','yoga','stances','boxing','cardio']){const w=weaponRequirements(group);const tiers=w.weapons.map(t=>progress.weapons?.[t]??0);if(tiers.every(t=>t>=20))continue;out.push({kind:'weapon',id:group,name:`${w.name} weapons`,text:`${w.hold} ${w.reps}`,done:false});}\r\n return out.filter(h=>!h.done);\r\n}\r\n// Stable per NPC id over whatever is still locked; null once everything is earned.\r\nexport function npcHint(npcId,progress=readPerformanceProgress()){const list=achievementHints(progress),crowd=/^npc-([a-z0-9]+)-(\\d+)$/.exec(npcId);const index=crowd?hash(crowd[1])+Number(crowd[2]):hash(npcId);return list.length?list[index%list.length]:null;}\r\n\r\nfunction ensureCss(doc){const href=new URL('./npc-encounters.css',import.meta.url).href;if([...doc.querySelectorAll('link[data-cw-npc-shop]')].some(l=>l.href===href))return;const l=doc.createElement('link');l.rel='stylesheet';l.href=href;l.dataset.cwNpcShop='';doc.head.append(l);}\r\nconst esc=s=>String(s).replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\r\n\r\nexport function openNpcEncounter({npc,root,onClose,client,accountId=null,progress,grants=REAL_GRANTS,doc=root?.ownerDocument||globalThis.document}){\r\n if(!npc?.id||!root)throw new TypeError('openNpcEncounter needs npc.id and root');\r\n ensureCss(doc);\r\n const el=doc.createElement('section');el.className='cw-npc-shop';el.dataset.npcShop=npc.id;el.setAttribute('role','dialog');el.setAttribute('aria-label',`${npc.name||'NPC'} shop`);\r\n el.style.setProperty('--npc-tint',npc.look?.tint||'#a070c0');el.style.setProperty('--npc-accent',npc.look?.accent||'#ffd23a');\r\n let busy=false,note='',disposed=false;\r\n const owner=performanceOwner(),epoch=globalThis.myr5AuthenticatedAccount?.dataEpoch;\r\n const assertOwner=()=>{if(performanceOwner()!==owner||globalThis.myr5AuthenticatedAccount?.dataEpoch!==epoch)throw new DOMException('Account changed. Reopen this shop.','AbortError');};\r\n const hint=npcHint(npc.id,progress);\r\n const base=()=>client?.snapshot?.base;\r\n // Push everything owned into the real ledgers (both grant APIs are idempotent); resolves once the lazy store bundle has run.\r\n function syncOwned(p){assertOwner();return Promise.all([...p.weaponUpgrades.map(id=>grants.weapon?.(id)),...p.weaponTextures.map(id=>grants.texture?.(id)),...p.weaponColors.map(id=>grants.color?.(id))]);}\r\n function render(){\r\n  if(disposed)return;const p=readProgression(base()),items=npcInventory(npc.id,p);\r\n  el.innerHTML=`<header><b>${esc(npc.name||'NPC')}</b><span>${esc(npc.look?.name||'')}</span><button type=\"button\" class=\"cw-npc-x\" data-npc-close aria-label=\"Close\">✕</button></header>\r\n  <p class=\"cw-npc-hint\" data-hint-kind=\"${hint?esc(hint.kind):'none'}\">${hint?`<b>${esc(hint.name)}</b> ${esc(hint.text)}`:'You have earned every achievement I know of.'}</p>\r\n  <p class=\"cw-npc-wallet\" data-balance=\"${p.balance}\">${esc(CURRENCY.name)}: <b>${p.balance}</b><small>${esc(client?.authenticated===false?`Sign in to earn ${CURRENCY.name} from qualifying logged workouts. Guest catches stay on this device.`:CURRENCY.earnLine)}</small></p>\n  <ul class=\"cw-npc-items\">${items.map(i=>`<li data-item=\"${esc(i.id)}\" data-owned=\"${i.owned}\" data-affordable=\"${i.affordable}\"><div><b>${esc(i.name)}${i.kind==='stat'?` <em>Lv ${i.level}</em>`:''}</b><span>${esc(i.line)}</span></div>\r\n   <button type=\"button\" data-buy=\"${esc(i.id)}\" ${i.owned||!i.affordable||busy?'disabled':''}>${i.owned?(i.kind==='stat'?'Maxed':'Owned'):`${i.cost} ${esc(CURRENCY.symbol)}`}</button></li>`).join('')}</ul>\r\n  <p class=\"cw-npc-note\" role=\"status\">${esc(note)}</p>`;\r\n  el.querySelector('[data-npc-close]').onclick=close;\r\n  for(const b of el.querySelectorAll('[data-buy]'))b.onclick=()=>buy(b.dataset.buy);\r\n }\r\n async function buy(itemId){\r\n  if(busy||!client?.command||disposed)return false;\r\n  const it=npcInventory(npc.id,readProgression(base())).find(i=>i.id===itemId);if(!it||it.owned||!it.affordable)return false;\r\n  busy=true;note='…';render();\r\n  try{\r\n   // The server scopes receipts to this account/epoch. Retrying a lost response\r\n   // must keep the same intended level, including after this panel is reopened.\r\n   const receiptId=`npc-buy:${itemId}:${it.kind==='stat'?it.level+1:1}`;\r\n   const snap=await client.command({type:'npc-purchase',itemId,npcId:npc.id,receiptId});\r\n   assertOwner();const p=readProgression(snap?.base??base());await syncOwned(p);assertOwner();note=p.receipts&&Object.values(p.receipts).some(r=>r.itemId===itemId)?'Yours.':'';\r\n  }catch(e){note=e?.message||'Purchase failed.';busy=false;render();return false;}\r\n  busy=false;render();return true;\r\n }\r\n function close(){dispose();onClose?.();}\r\n function dispose(){if(disposed)return;disposed=true;el.remove();}\r\n void syncOwned(readProgression(base())).catch(e=>{if(!disposed){note=e?.message||'Equipment sync failed. Reopen this shop to retry.';render();}});\r\n render();root.append(el);\r\n return {element:el,refresh:render,buy,close,dispose};\r\n}\r\n","modules/coach-world-rts/npc-looks.mjs":"// AR NPC looks: palette + pattern swap on the MYR5 2D sprite atlas through its mask (no GLB, no extra GL context).\r\n// Tints and dress names come from arcade/cover/levels/*.mjs; see C:/myr5-work/polish-1018/COVER-LOOKS.md.\r\n// painters = the pattern painters from arcade/cover/textures.mjs (signature (g,s,r,col,mix)); pass them in.\r\nimport {mixHex} from '../coach-world/palettes.mjs';\r\n\r\nconst L=(id,name,src,tint,accent,pattern,dress)=>Object.freeze({id,name,src,tint,accent,pattern,dress});\r\nexport const LOOKS=Object.freeze([\r\n  L('crisis-purple','Purple Suit','crisis.mjs default tint','#a070c0','#ffd23a','tiles','suit'),\r\n  L('halo-grunt','Orange Vest','halo.mjs grunt','#e0a040','#ffd23a','hex','vest'),\r\n  L('halo-jackal','Violet Visor','halo.mjs jackal','#8a6ab0','#3050c0','hex','visor'),\r\n  L('halo-elite','Blue Helm','halo.mjs elite','#3050c0','#9fa9ba','hex','helmet'),\r\n  L('ps2-elite','Red Beret','ps2.mjs elite','#c03030','#23201c','brick','army'),\r\n  L('hive-goo','Hive Goo','hive.mjs security tint','#3a2f4a','#8a6ab0','plates','goo'),\r\n  L('judgment-chrome','Chrome Gun','judgment.mjs','#9fa9ba','#3050c0','plates','chrome'),\r\n  L('zombie-moss','Moss Goo','mansion.mjs zombie','#7a8a6a','#e0a040','planks','goo'),\r\n  L('kitchen-minion','Yellow Minion','kitchen.mjs minion','#ffd23a','#c03030','checker','antenna'),\r\n  L('crawler-grey','Grey Helm','hive.mjs crawler suit #555','#555555','#9fa9ba','concrete','helmet'),\r\n  L('spore-cap','Spore Cap','hive.mjs spore suit #666','#666666','#ffd23a','carpet','cap'),\r\n  L('ps2-olive-army','Olive Army','ps2 army + mansion tint (combo)','#7a8a6a','#c03030','grass','army'),\r\n  L('judgment-gun','Steel Gun','judgment chrome + gun (combo)','#9fa9ba','#a070c0','carpet','gun'),\r\n]);\r\n\r\nconst hash=s=>{let h=2166136261;for(const c of String(s))h=Math.imul(h^c.charCodeAt(0),16777619);return h>>>0;};\r\nconst rngFor=seed=>{let a=seed>>>0;return()=>{a=a+0x6D2B79F5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};};\r\nexport const lookFor=(seed,i)=>LOOKS[((seed+i*7)>>>0)%LOOKS.length];\r\n\r\nconst defaultCanvas=(w,h)=>Object.assign(document.createElement('canvas'),{width:w,height:h});\r\nconst cache=new WeakMap(),TILE=32;\r\n// Pattern painters from the Cover textures without touching that file: makeTexture paints P[pattern] onto a canvas; we keep the canvas and drop the (never uploaded) texture.\r\nexport const coverPainters=(T,makeTexture,patterns=[...new Set(LOOKS.map(l=>l.pattern))])=>Object.fromEntries(patterns.map(p=>[p,(g,s,r,col)=>{const t=makeTexture(T,p,col,s,(r()*1e9)|0);if(t?.image){g.drawImage(t.image,0,0);t.dispose?.();}}]));\r\n\r\n// Returns a canvas the size of the sheet's dye: dyed body, pattern at 12 % source-atop, 1-px accent rim behind.\r\n// Cached per (sheet, look.id). Returns null when the sheet cannot dye.\r\nexport function applyLook(sheet,look,{painters={},makeCanvas=defaultCanvas}={}){\r\n  let per=cache.get(sheet);if(!per){per=new Map();cache.set(sheet,per);}\r\n  if(per.has(look.id))return per.get(look.id);\r\n  const base=sheet.dye(look.tint);if(!base)return null;\r\n  const w=base.width,h=base.height,out=makeCanvas(w,h),g=out.getContext('2d');\r\n  g.imageSmoothingEnabled=false;\r\n  g.drawImage(base,0,0);\r\n  const paint=painters[look.pattern];\r\n  if(paint){\r\n    const S=128,pc=makeCanvas(S,S);paint(pc.getContext('2d'),S,rngFor(hash(look.id)),look.tint,mixHex);\r\n    g.globalAlpha=.12;g.globalCompositeOperation='source-atop';for(let y=0;y<h;y+=TILE)for(let x=0;x<w;x+=TILE)g.drawImage(pc,x,y,TILE,TILE); // tiled per ~cell, not stretched over the whole sheet (a stretched 128 px pattern was invisible inside one 28x40 frame)\r\n    g.globalAlpha=1;g.globalCompositeOperation='source-over';\r\n  }\r\n  const rim=makeCanvas(w,h),rg=rim.getContext('2d');\r\n  rg.drawImage(base,0,0);rg.globalCompositeOperation='source-in';rg.fillStyle=look.accent;rg.fillRect(0,0,w,h);\r\n  g.globalCompositeOperation='destination-over';\r\n  for(const [dx,dy] of [[-1,0],[1,0],[0,-1],[0,1]])g.drawImage(rim,dx,dy);\r\n  g.globalCompositeOperation='source-over';\r\n  per.set(look.id,out);\r\n  return out;\r\n}\r\n","modules/coach-world-rts/npc-progression.mjs":"// World NPC shop progression: pure, server-safe (no DOM, no localStorage, no roguelite imports).\r\n// State lives in base.npcProgression. Currency = progression.balance: ONE permanent home-base wallet kept across run end/fail/restart,\r\n// credited only by awardRunCurrency() (server validates the reward source) and debited only by applyNpcPurchase(). Never run coins.\r\n// Weapons/textures/colors bought here are ALSO pushed into the real owned systems by npc-encounters.mjs (ledger + unlock-store).\r\n// Permanent stats are bonus LEVEL counts the roguelite run owner feeds createRunStats({startingLevels}) on a NEW run only.\r\nimport {NPC_COSMETICS} from './npc-cosmetic-catalog.mjs';\nimport {RESOURCE_LABELS} from '../coach-world/resource-labels.mjs';\n\r\nexport const PROGRESSION_VERSION=1;\r\nexport const STATS=Object.freeze(['health','damage','luck']);\r\nexport const STAT_MAX=10;\r\n// Receipts are never evicted: eviction would let an old reward/purchase replay. ~100 bytes each; revisit only if saves balloon.\r\n// ponytail: provisional currency name + rates (numeric tuning, not Ian-specified); rename/retune here only.\r\nexport const CURRENCY=Object.freeze({name:RESOURCE_LABELS.permanentCurrency,symbol:'GC',maxAward:500,earnLine:'Earned from qualifying logged workouts; kept forever, even when a run ends or fails.'});\n\r\n// ponytail: effect numbers mirror roguelite boons.mjs BOON_TUNING; that worktree is read-only and not in production yet, so no import.\r\nexport const RELICS=Object.freeze({\r\n overshield:Object.freeze({id:'overshield',boonId:'overshield',name:'Lifting Belt',line:'Start every run with one overshield charge.',effects:Object.freeze({overshieldCharges:1})}),\n longerParry:Object.freeze({id:'longerParry',boonId:'longerParry',name:'Reaction Ball',line:'Parry windows last 1.5x longer in every run.',effects:Object.freeze({parryWindowFactor:1.5})}),\n bossPunch:Object.freeze({id:'bossPunch',boonId:'bossPunch',name:'Lifting Chalk',line:'Boss punches deal 1.25x in every run.',effects:Object.freeze({bossDamageFactor:1.25})}),\n flightClosing:Object.freeze({id:'flightClosing',boonId:'flightClosing',name:'Sprint Spikes',line:'Three catch windows in flight chases, not two.',effects:Object.freeze({chaseCatchWindows:3})}),\n});\r\nconst RELIC_DEFAULTS=Object.freeze({overshieldCharges:0,parryWindowFactor:1,bossDamageFactor:1,chaseCatchWindows:2});\r\n\r\n// unlock-ledger 'weapon' ids (battle-pass-rewards WEAPONS; cage.ts reads them).\r\nconst WEAPONS=[['chest-w1','Glove on a Stick'],['chest-w2','Chrome Bar Mace'],['arms-w1','Dumbbellchucks'],['arms-w2','Pull-Up Staff'],['quads-w1','Squat-Rack Axe'],['quads-w2','Kettlebell Flail'],\r\n ['glutes-w1','Band Slingshot'],['glutes-w2','Hip-Thrust Hammer'],['yoga-w1','Mat Bo Staff'],['yoga-w2','Block Gauntlets'],['martial-arts-w1','Black-Belt Whip'],['martial-arts-w2','Dummy Club'],\r\n ['cardio-w1','Jump-Rope Lasso'],['cardio-w2','Shoe Boomerang'],['meditation-w1','Singing-Bowl Aegis'],['meditation-w2','Incense Wand']];\r\nconst STAT_NAMES={health:['Iron Heart','+1 starting health level on every new run.'],damage:['Heavy Hands','+1 starting damage level on every new run.'],luck:['Lucky Tooth','+1 starting luck level on every new run (10 luck = 1 protection charge).']};\r\nexport const statCost=level=>40+level*25;\r\nconst OWNED_KEY={weapon:'weaponUpgrades',texture:'weaponTextures',color:'weaponColors'};\r\n\r\nexport const CATALOG=Object.freeze([\r\n ...STATS.map(s=>({id:`stat:${s}`,kind:'stat',stat:s,name:STAT_NAMES[s][0],line:STAT_NAMES[s][1],cost:statCost(0)})),\r\n ...Object.values(RELICS).map(r=>({id:`relic:${r.id}`,kind:'relic',relic:r.id,name:r.name,line:r.line,cost:120})),\r\n ...WEAPONS.map(([id,name])=>({id:`weapon:${id}`,kind:'weapon',unlockId:id,name,line:'Weapon upgrade for your coach cage.',cost:90})),\r\n ...NPC_COSMETICS.textures.map(t=>({id:`texture:${t.id}`,kind:'texture',unlockId:t.id,name:t.name,line:'Coach texture, yours for good.',cost:60})),\r\n ...NPC_COSMETICS.colours.map(c=>({id:`color:${c.id}`,kind:'color',unlockId:c.id,name:c.name,line:'Coach colour, yours for good.',cost:45})),\r\n].map(Object.freeze));\r\nconst BY_ID=new Map(CATALOG.map(i=>[i.id,i]));\r\nexport const itemById=id=>BY_ID.get(id)||null;\r\n\r\nexport function createProgression(){return {version:PROGRESSION_VERSION,revision:0,balance:0,currencyReceipts:{},stats:{health:0,damage:0,luck:0},relics:[],weaponUpgrades:[],weaponTextures:[],weaponColors:[],receipts:{}};}\r\nconst safeId=v=>typeof v==='string'&&v.length>0&&v.length<=128;\r\nconst safeKey=v=>typeof v==='string'&&v.length>0&&v.length<=300;\r\nexport const currencyReceiptKey=(runId,eventId)=>JSON.stringify([runId,eventId]); // collision-free even with ':' in ids\r\nconst idList=(v,ok)=>{if(v==null)return [];if(!Array.isArray(v)||v.some(x=>!safeId(x)||!ok(x))||new Set(v).size!==v.length)throw new TypeError('invalid progression list');return [...v];};\r\nexport function normalizeProgression(p){\r\n if(p==null)return createProgression();\r\n if(typeof p!=='object'||Array.isArray(p)||p.version!==PROGRESSION_VERSION)throw new TypeError('invalid npc progression');\r\n const out=createProgression();\r\n if(!Number.isSafeInteger(p.revision)||p.revision<0)throw new RangeError('invalid progression revision');out.revision=p.revision;\r\n const bal=p.balance??0;if(!Number.isSafeInteger(bal)||bal<0)throw new RangeError('invalid balance');out.balance=bal;\r\n const cr=p.currencyReceipts??{};if(typeof cr!=='object'||Array.isArray(cr))throw new TypeError('invalid currency receipts');\r\n for(const [k,v] of Object.entries(cr)){if(!safeKey(k)||!v||!Number.isSafeInteger(v.amount)||v.amount<=0||!Number.isSafeInteger(v.at))throw new TypeError('invalid currency receipt');out.currencyReceipts[k]={amount:v.amount,at:v.at};}\r\n for(const s of STATS){const v=p.stats?.[s]??0;if(!Number.isSafeInteger(v)||v<0||v>STAT_MAX)throw new RangeError(`invalid stat ${s}`);out.stats[s]=v;}\r\n out.relics=idList(p.relics,id=>Object.hasOwn(RELICS,id));\r\n out.weaponUpgrades=idList(p.weaponUpgrades,id=>BY_ID.has(`weapon:${id}`));\r\n out.weaponTextures=idList(p.weaponTextures,id=>BY_ID.has(`texture:${id}`));\r\n out.weaponColors=idList(p.weaponColors,id=>BY_ID.has(`color:${id}`));\r\n const r=p.receipts??{};if(typeof r!=='object'||Array.isArray(r))throw new TypeError('invalid receipts');\r\n for(const [k,v] of Object.entries(r)){if(!safeId(k)||!v||!safeId(v.itemId)||!Number.isSafeInteger(v.at))throw new TypeError('invalid receipt');out.receipts[k]={itemId:v.itemId,at:v.at};}\r\n return out;\r\n}\r\nexport const readProgression=base=>normalizeProgression(base?.npcProgression);\r\n\r\nconst hash=s=>{let h=0x811c9dc5;for(const c of String(s))h=Math.imul(h^c.codePointAt(0),0x01000193)>>>0;return h;};\r\nconst pick=(list,h,salt)=>list[(h+salt*0x9E3779B1>>>0)%list.length];\r\n// Stable 4-slot inventory per NPC id: one stat, one relic, one weapon, one texture or colour.\r\nexport function npcInventory(npcId,progression=createProgression()){\r\n const h=hash(npcId),p=normalizeProgression(progression),materials=p.balance,cos=CATALOG.filter(i=>i.kind==='texture'||i.kind==='color');\r\n const picks=[pick(CATALOG.filter(i=>i.kind==='stat'),h,1),pick(CATALOG.filter(i=>i.kind==='relic'),h,2),pick(CATALOG.filter(i=>i.kind==='weapon'),h,3),pick(cos,h,4)];\r\n return picks.map(i=>priceItem(i,p,materials));\r\n}\r\nexport function priceItem(item,p,materials=0){\r\n if(item.kind==='stat'){const level=p.stats[item.stat],owned=level>=STAT_MAX,cost=statCost(level);return {...item,level,cost,owned,affordable:!owned&&materials>=cost};}\r\n const owned=item.kind==='relic'?p.relics.includes(item.relic):p[OWNED_KEY[item.kind]].includes(item.unlockId);\r\n return {...item,owned,affordable:!owned&&materials>=item.cost};\r\n}\r\n\r\n// Pure purchase reducer for the server world route and the guest local base. Never throws on user input; returns reasons.\r\nexport function applyNpcPurchase(base,{itemId,npcId,receiptId,accountId}={},{now=Date.now()}={}){\r\n const result=(ok,reason,extra={})=>({base,result:{ok,reason,granted:false,cost:0,item:null,progression:readProgression(base),...extra}});\r\n if(!safeId(receiptId)||!safeId(npcId))return result(false,'bad_receipt');\r\n const p=readProgression(base),item=itemById(itemId);\r\n if(!item)return result(false,'unknown_item');\r\n const prior=p.receipts[receiptId];\r\n if(prior)return prior.itemId===itemId?result(true,'duplicate_receipt',{item}):result(false,'bad_receipt');\r\n if(!npcInventory(npcId,p).some(i=>i.id===itemId))return result(false,'npc_not_selling',{item});\r\n const funds=p.balance,priced=priceItem(item,p,funds);\r\n if(priced.owned)return result(false,item.kind==='stat'?'stat_maxed':'already_owned',{item:priced});\r\n if(!priced.affordable)return result(false,'insufficient_balance',{item:priced});\r\n const next=normalizeProgression(p);\r\n if(item.kind==='stat')next.stats[item.stat]++;else if(item.kind==='relic')next.relics.push(item.relic);else next[OWNED_KEY[item.kind]].push(item.unlockId);\r\n next.revision++;next.receipts[receiptId]={itemId,at:now};\r\n next.balance=funds-priced.cost;\r\n return {base:{...base,npcProgression:next},result:{ok:true,reason:null,granted:true,cost:priced.cost,item:priceItem(item,next,next.balance),progression:next,accountId:accountId??null}};\r\n}\r\n\r\n// Credit the permanent wallet from a TRUSTED reward source (parent server derives amount from the DB workout/run record; the public\r\n// client never picks the amount). Receipt = `${runId}:${eventId}`; replays return ok:true, awarded:false, no second credit.\r\nexport function awardRunCurrency(base,{runId,eventId,amount,accountId}={},{now=Date.now()}={}){\r\n const p=readProgression(base),result=(ok,reason,extra={})=>({base,result:{ok,reason,awarded:false,amount:0,progression:p,...extra}});\r\n if(!safeId(runId)||!safeId(eventId))return result(false,'bad_receipt');\r\n if(!Number.isSafeInteger(amount)||amount<=0||amount>CURRENCY.maxAward)return result(false,'bad_amount');\r\n const receiptId=currencyReceiptKey(runId,eventId),prior=p.currencyReceipts[receiptId];\r\n if(prior)return prior.amount===amount?result(true,'duplicate_receipt'):result(false,'bad_receipt');\r\n const next=normalizeProgression(p);if(!Number.isSafeInteger(next.balance+amount))return result(false,'bad_amount');\r\n next.balance+=amount;next.revision++;next.currencyReceipts[receiptId]={amount,at:now};\r\n return {base:{...base,npcProgression:next},result:{ok:true,reason:null,awarded:true,amount,progression:next,accountId:accountId??null}};\r\n}\r\n\r\n// Exact run-owner contract. Permanent stats are bonus LEVEL counts, not raw HP/damage.\r\nexport function getRunStartBenefits(progression,{accountId}={}){\r\n const p=normalizeProgression(progression);\r\n return {version:1,accountId:accountId??null,revision:p.revision,statBonuses:{...p.stats},relics:p.relics.map(id=>({id,boonId:RELICS[id].boonId,effects:{...RELICS[id].effects}})),\r\n  weaponUpgrades:[...p.weaponUpgrades],weaponTextures:[...p.weaponTextures],weaponColors:[...p.weaponColors]};\r\n}\r\nexport function relicEffects(relicIds=[]){\r\n const out={...RELIC_DEFAULTS};\r\n for(const id of relicIds){const r=Object.hasOwn(RELICS,id)?RELICS[id]:null;if(!r)throw new RangeError(`unknown relic: ${id}`);Object.assign(out,r.effects);}\r\n return out;\r\n}\r\n// One stable receipt per account+relic: grantBoon() dedupes inside a run inventory; a resumed run restores its inventory and must not call this again.\r\nexport const startBoonReceipts=benefits=>benefits.relics.map(r=>({boonId:r.boonId,receiptId:`npc-start:${benefits.accountId??'guest'}:${r.id}`}));\r\n","modules/coach-world-rts/pin-drop.mjs":"// Pin drop (pegman): the Camera button opens the one-map world zoomed out; place a pin (tap or drag), DROP -> the raid drop pod falls on that\r\n// world point (drop-pod-scene playDrop) -> on landing the AR walkthrough starts with the pin as the viewpoint origin (camera passthrough + true-north\r\n// device orientation; drag-look only if orientation is denied). Exit -> onExit (the caller remounts the base).\r\n// Privacy: the pin is a local lat/lng only. Nothing here touches the network; only the player's own base is ever drawn (friends stay fogged).\r\nimport {toLocal} from '../coach-world/world-tiles.mjs';\r\nimport {pickHome,coarseFix,STRIP,createWorldLayer} from './world-layer.mjs';\r\nimport {createPoseSource} from './pose-source.mjs';\r\nimport {createCompass} from './geo-follow.mjs';\r\nimport {playDrop} from './drop-pod-scene.mjs';\r\nimport {SCALE} from './base-meshes.mjs';\r\nimport {validHome} from './ar-anchor.mjs';\r\nimport {openPortalAbovePod,portalStyle,DOLLY_MS} from './portal-pod.mjs';\r\n\r\nconst RAD=Math.PI/180,R=6378137;\r\n// ---- pure maths: pin lat/lng <-> base3d world (metres about the world origin, scaled, plus the layer centre) ----\r\nexport const pinToWorld=(pin,origin,center={x:0,z:0},scale=SCALE)=>{const m=toLocal(origin,pin.lat,pin.lng);return {x:center.x+m.x*scale,z:center.z+m.z*scale};};\r\nexport const worldToPin=(w,origin,center={x:0,z:0},scale=SCALE)=>({lat:origin.lat-(w.z-center.z)/scale/(R*RAD),lng:origin.lng+(w.x-center.x)/scale/(R*RAD*Math.cos(origin.lat*RAD))});\r\n// default pin: coarse fix (only if location was already granted) > saved home > Strip\r\nexport const defaultPin=(base,fix)=>Number.isFinite(fix?.lat)&&Number.isFinite(fix?.lng)?{lat:fix.lat,lng:fix.lng}:validHome(base?.home)?{lat:base.home.latitude,lng:base.home.longitude}:{...STRIP};\r\n// playDrop's tile maths is (tile+.5)*scale from origin {0,0}\r\nexport const worldToTile=(w,scale=SCALE)=>({x:w.x/scale-.5,y:w.z/scale-.5});\r\n\r\nconst EDGES={map:{open:'pin'},pin:{drop:'drop',cancel:'map'},drop:{landed:'pod',cancel:'map'},pod:{exit:'map'}};\r\n// ?settle=ms (default 350, 0..3000): wait after base3d's WebGL is released before the walkthrough claims a new one (iOS context-loss knob)\r\nexport const settleMs=search=>{const q=new URLSearchParams(search||''),v=Number(q.get('settle'));return q.has('settle')&&Number.isFinite(v)?Math.max(0,Math.min(3000,v)):350;};\r\nexport const READY_MAX_MS=8000,REVEAL_MS=500,LAND_GAP_M=3;\r\n// \"Land beside\" an AR NPC: npc = metres east (+x) / south (+z) of the AR pin; the new pin stops LAND_GAP_M short of it on the line back to the pin.\r\nexport function pinBeside(pin,npc,gap=LAND_GAP_M){const d=Math.hypot(npc.x,npc.z),k=d>gap?(d-gap)/d:0;return worldToPin({x:npc.x*k,z:npc.z*k},pin,{x:0,z:0},1);}\r\n// z16 slippy tile of a pin: the AR NPC spawn seed (ar-npcs hashSeed {x,y,z}), so the same spot always shows the same crowd\r\nexport const pinCell=(p,z=16)=>{const n=2**z,la=p.lat*Math.PI/180;return {x:Math.floor((p.lng+180)/360*n),y:Math.floor((1-Math.log(Math.tan(la)+1/Math.cos(la))/Math.PI)/2*n),z};};\r\nexport function createPinMachine(){let s='map';return {get state(){return s;},send(e){const n=EDGES[s][e];if(n)s=n;return n!=null;}};}\r\n\r\nexport function mountPinDrop({wrap,host,getBase,signal,onExit=()=>{},initialPin=null,target=null,boarding=true,deps={}}){\r\n const doc=wrap.ownerDocument??document,win=doc.defaultView??globalThis;\r\n const D={pickHome,coarseFix,createPose:()=>createPoseSource({win}),createCompass:()=>createCompass(win),playDrop,loadWalk:()=>import('./walkthrough.mjs'),\r\n  getUserMedia:c=>navigator.mediaDevices?.getUserMedia?.(c),openPortal:openPortalAbovePod,portalStyle,now:()=>performance.now(),\r\n  sleep:ms=>new Promise(r=>setTimeout(r,ms)),reduced:()=>{try{return !!win.matchMedia?.('(prefers-reduced-motion: reduce)').matches;}catch{return false;}},settle:settleMs(win.location?.search),...deps};\r\n const m=createPinMachine(),off=[];\r\n let disposed=false,h=null,rr=null,origin=null,pin=null,marker=null,drop=null,wt=null,ui=null,canvas=null,walkP=null,portal=null,cover=null,fov0=null,beside=null;\r\n const alive=()=>!disposed&&!signal?.aborted;\r\n const say=t=>{if(ui)ui.chip.textContent=t;};\r\n\r\n function mkUi(){\r\n  const root=doc.createElement('div');root.dataset.pinUi='';root.style.cssText='position:absolute;left:8px;right:8px;bottom:calc(10px + env(safe-area-inset-bottom));z-index:5;display:flex;gap:8px;align-items:center;justify-content:center;pointer-events:none;flex-wrap:wrap';\r\n  const chip=doc.createElement('span');chip.style.cssText='flex:1 0 100%;text-align:center;color:#fff;text-shadow:0 1px 3px #000;font:600 13px system-ui';chip.textContent='Tap or drag the pin where you want to land';\r\n  const mk=(t,k)=>{const b=doc.createElement('button');b.type='button';b.textContent=t;b.dataset[k]='';b.style.cssText='pointer-events:auto;min-height:44px;padding:6px 18px;border:2px solid #fff;border-radius:10px;background:#0b0f14e6;color:#fff;font:700 15px system-ui';return b;};\r\n  const dropB=mk('DROP','pinDrop'),cancelB=mk('Cancel','pinCancel');dropB.style.background='#c42a52';\r\n  root.append(chip,dropB,cancelB);wrap.append(root);return {root,chip,dropB,cancelB};\r\n }\r\n function place(p){\r\n  pin=p;if(!marker||!rr)return;const w=pinToWorld(pin,origin,rr.center);marker.position.set(w.x,0,w.z);\r\n }\r\n function mkMarker(T){\r\n  const g=new T.Group(),mat=new T.MeshBasicMaterial({color:0xff3d6e,depthTest:false,transparent:true});\r\n  const cone=new T.Mesh(new T.ConeGeometry(.5,2,10),mat),head=new T.Mesh(new T.SphereGeometry(.7,10,8),mat);\r\n  cone.rotation.x=Math.PI;cone.position.y=1;head.position.y=2.6;g.add(cone,head);g.traverse(o=>{o.renderOrder=999;});g.name='pin-marker';return g;\r\n }\r\n // screen -> ground (y=0) through base3d's own camera\r\n function groundAt(e){\r\n  const T=rr.T,r=canvas.getBoundingClientRect(),ray=new T.Raycaster();\r\n  ray.setFromCamera({x:(e.clientX-r.left)/r.width*2-1,y:1-(e.clientY-r.top)/r.height*2},rr.camera);\r\n  const v=ray.ray.intersectPlane(new T.Plane(new T.Vector3(0,1,0),0),new T.Vector3());return v?{x:v.x,z:v.z}:null;\r\n }\r\n const nearMarker=e=>{const r=canvas.getBoundingClientRect(),v=new rr.T.Vector3(marker.position.x,2,marker.position.z).project(rr.camera);\r\n  return Math.hypot(r.left+(v.x*.5+.5)*r.width-e.clientX,r.top+(.5-v.y*.5)*r.height-e.clientY)<48;};\r\n\r\n async function open(){\r\n  if(!m.send('open'))return false;\r\n  h=wrap.__base3d;rr=h?await h.ready:null;if(!alive())return false;\r\n  if(!rr){m.send('cancel');onExit();return false;}\r\n  const base=getBase(),[home,fix]=await Promise.all([D.pickHome(base),Promise.resolve(D.coarseFix()).catch(()=>null)]);if(!alive())return false;\r\n  origin={lat:home.lat,lng:home.lng};pin=initialPin&&Number.isFinite(initialPin.lat)&&Number.isFinite(initialPin.lng)?{lat:initialPin.lat,lng:initialPin.lng}:defaultPin(base,fix);\r\n  marker=mkMarker(rr.T);rr.scene.add(marker);place(pin); // scene, not world: base3d dissolves world children at far zoom\r\n  off.push(h.onFrame(()=>{marker.scale.setScalar(Math.max(.5,rr.camera.position.distanceTo(marker.position)/28));}));\r\n  h.recenter();h.setZoom(.03);\r\n  canvas=wrap.querySelector('canvas[data-base3d]');ui=mkUi();\r\n  ui.dropB.onclick=startDrop;ui.cancelB.onclick=cancel;\r\n  if(canvas){\r\n   const ac=new AbortController(),cap={signal:ac.signal,capture:true};off.push(()=>ac.abort());\r\n   let dragging=false,down=null;\r\n   canvas.addEventListener('pointerdown',e=>{if(m.state!=='pin')return;down={x:e.clientX,y:e.clientY,t:performance.now()};if(nearMarker(e)){dragging=true;e.stopImmediatePropagation();canvas.setPointerCapture?.(e.pointerId);}},cap);\r\n   canvas.addEventListener('pointermove',e=>{if(!dragging)return;e.stopImmediatePropagation();const w=groundAt(e);if(w)place(worldToPin(w,origin,rr.center));},cap);\r\n   const up=e=>{\r\n    if(m.state!=='pin'){dragging=false;return;}\r\n    if(dragging){dragging=false;e.stopImmediatePropagation();return;}\r\n    if(down&&e.type==='pointerup'&&Math.hypot(e.clientX-down.x,e.clientY-down.y)<8&&performance.now()-down.t<400){const w=groundAt(e);if(w){place(worldToPin(w,origin,rr.center));down=null;e.stopImmediatePropagation();canvas.dispatchEvent?.(new (globalThis.PointerEvent||Event)('pointercancel',{pointerId:e.pointerId}));return;}} // pin tap must not also open the HUD build tray: swallow the up, cancel the pointer so base3d clears it\r\n    down=null;};\r\n   canvas.addEventListener('pointerup',up,cap);canvas.addEventListener('pointercancel',up,cap);\r\n  }\r\n  return true;\r\n }\r\n\r\n // ---- DROP: every permission request happens synchronously in this tap, before any await ----\r\n function startDrop(ev){\r\n  if(!m.send('drop'))return;\r\n  const pose=D.createPose(),permP=pose.requestPermission();\r\n  const compass=D.createCompass(),compP=Promise.resolve(compass.request?.()).catch(()=>false);\r\n  let gumP;try{gumP=Promise.resolve(D.getUserMedia({audio:false,video:{facingMode:{ideal:'environment'}}})).catch(()=>null);}catch{gumP=Promise.resolve(null);}\r\n  pose.requestPermission=()=>permP;compass.request=()=>compP; // walkthrough asks again: hand it the answers instead of re-prompting\r\n  const pinned={...pin},w=pinToWorld(pinned,origin,rr.center),s=SCALE;\r\n  walkP=Promise.resolve(D.loadWalk()); // preload while the pod falls\r\n  ui.dropB.disabled=true;marker.visible=false;say('Pod inbound…');\r\n  h.cameraOverride=cam=>{cam.position.set(w.x-7*s,14*s,w.z+20*s);cam.lookAt(w.x,4*s,w.z);};\r\n  drop=D.playDrop(rr.T,rr.scene,{tile:worldToTile(w,s),scale:s,origin:{x:0,y:0},onLanded:d=>{if(alive()&&m.send('landed'))portalIn(ev,{pose,compass,gumP,pinned},d,w);}});\r\n }\r\n // ---- landed: a phone-shaped portal opens above the pod, the camera turns to it and dollies in until it fills the screen ----\r\n function portalIn(ev,ctx,d,w){\r\n  const s=SCALE,style=D.portalStyle(),top=Number.isFinite(d?.top)?d.top:2.1*s;if(ui)ui.root.style.display='none'; // clean frame for the portal: no pin chip/buttons\r\n  if(D.reduced()){showCover(style,true);void enterAr(ev,ctx);return;} // reduced motion: instant swap, no quad\r\n  const cam=rr.camera,from={pos:cam.position.clone(),target:new rr.T.Vector3(w.x,4*s,w.z),fov:cam.fov};fov0=cam.fov;\r\n  portal=D.openPortal(rr.T,rr.scene,{podPos:{x:d?.position?.[0]??w.x,y:top,z:d?.position?.[2]??w.z},scale:s,camera:cam,style});\r\n  const t0=D.now();let filled=false;\r\n  h.cameraOverride=c=>{\r\n   if(!portal)return;const e=D.now()-t0,k=Math.min(1,e/DOLLY_MS);portal.update(e);portal.dollyPose(c,k,from);\r\n   if(!filled&&(portal.fillFraction(c)>=.98||k>=1)){filled=true;showCover(style,false);void enterAr(ev,ctx);}\r\n  };\r\n }\r\n function showCover(style,still){\r\n  if(cover||!host)return;\r\n  if(doc.head&&!doc.querySelector?.('link[data-cw-portal-pod]')){const l=doc.createElement('link');l.rel='stylesheet';l.dataset.cwPortalPod='';l.href=new URL('./portal-pod.css',import.meta.url).href;doc.head.append(l);}\r\n  const c=cover=doc.createElement('div');c.className='cw-portal-cover';c.dataset.portalCover='';c.setAttribute?.('aria-hidden','true');c.setAttribute?.('popover','manual');\r\n  const st=c.style;st.setProperty?.('--pp-core',style.core);st.setProperty?.('--pp-strip',style.strip);style.colors.forEach((v,i)=>st.setProperty?.('--pp-c'+i,v));\r\n  if(still)c.dataset.still='';host.append(c);try{c.showPopover?.();}catch{} // top layer: above the World modal dialog\r\n }\r\n function removeCover(){const c=cover;cover=null;try{c?.hidePopover?.();}catch{}c?.remove();}\r\n // walkthrough root ready -> phone-shaped clip-path opens over the cover, then the cover goes and the phone frame stays\r\n async function reveal(style){\r\n  const t0=D.now(),q='[data-rts-walk-root][data-ready]';\r\n  while(alive()&&cover&&!host.querySelector?.(q)&&D.now()-t0<READY_MAX_MS)await D.sleep(50);\r\n  if(!alive()||!cover)return;\r\n  const root=host.querySelector?.('[data-rts-walk-root]');\r\n  if(root){root.classList?.add('cw-peer');root.style.setProperty?.('--cw-peer-metal',style.metal);root.style.setProperty?.('--cw-peer-strip',style.strip);}\r\n  const anim=!D.reduced()&&root?.animate?.([{clipPath:'inset(50% 50% round 28px)'},{clipPath:'inset(0% 0% round 28px)'}],{duration:REVEAL_MS,easing:'cubic-bezier(.2,.7,.2,1)'});\r\n  if(anim){try{cover.hidePopover?.();}catch{}cover.removeAttribute?.('popover');await anim.finished.catch(()=>{});} // out of the top layer: the root (z above the cover) now draws over it\r\n  removeCover();\r\n }\r\n function restoreFov(){if(fov0!=null&&rr?.camera){rr.camera.fov=fov0;rr.camera.updateProjectionMatrix?.();}fov0=null;}\r\n async function enterAr(ev,{pose,compass,gumP,pinned}){\r\n  try{\r\n   const mod=await walkP;if(!alive()||m.state!=='pod')return;\r\n   const style=D.portalStyle();\r\n   // release base3d's WebGL first, then settle, then the walkthrough claims its own context (never two at once)\r\n   h.cameraOverride=null;restoreFov();portal?.dispose();portal=null;drop?.dispose();drop=null;h.dispose();\r\n   await D.sleep(D.settle);if(!alive()||m.state!=='pod')return;\r\n   // The base sits at the world origin; a one-shot \"GPS\" fix at the pin makes the anchor offset = pin - origin (never the phone's real position).\r\n   const arBase=()=>({...getBase(),home:{latitude:origin.lat,longitude:origin.lng,consent:true}});\r\n   const geo={watchPosition(cb){cb({coords:{latitude:pinned.lat,longitude:pinned.lng,accuracy:5}});return 1;},clearWatch(){}};\r\n   wt=mod.createWalkthrough({host,getBase:arBase,signal,deps:{pose,compass,geo,getUserMedia:()=>gumP,worldLayer:D.worldLayer??createWorldLayer,forceAnchor:true,liftoff:true,client:D.worldClient,onWorldChange:D.onWorldChange,pinCell:pinCell(pinned),target,onLandBeside:landBeside,onBoard:boarding?()=>{const point={...pinned};teardown();if(alive())onExit({flight:{pin:point}});}:undefined}});\r\n   wt.start(ev,{onExit:finish});\r\n   if(cover)void reveal(style);\r\n  }catch(e){console.warn('coach-world pin-drop AR',e);finish();}\r\n }\r\n // Lane I seam: an AR NPC's \"Land beside\" -> lift off; the caller reopens pin mode with initialPin = landBeside.pin and shows the toast.\r\n // No auto-DROP: DROP must stay a user tap (its permission prompts are synchronous in that gesture).\r\n function landBeside(npc){\r\n  if(m.state!=='pod'||!npc)return;const p=pinBeside(pin,npc),name=npc.name||'them';\r\n  beside={pin:p,name,toast:`Tap DROP to land beside ${name}`,target:{id:npc.id,name,rare:npc.rare,level:npc.level,look:npc.look,source:npc.source,heightM:npc.heightM,tier:npc.tier,cell:npc.cell}};\r\n  const ex=host.querySelector?.('[data-rts-walk-root] [data-exit]');if(ex?.click)ex.click();else finish();\r\n }\r\n function finish(){if(m.state==='pod')m.send('exit');teardown();const b=beside;beside=null;if(alive())onExit(b?{landBeside:b}:undefined);}\r\n function cancel(){m.send('cancel');teardown();onExit();}\r\n function teardown(){\r\n  for(const f of off.splice(0))try{f();}catch{}\r\n  if(h)h.cameraOverride=null;restoreFov();portal?.dispose();portal=null;removeCover();drop?.dispose();drop=null;marker?.removeFromParent();marker?.traverse(o=>{o.geometry?.dispose();o.material?.dispose?.();});marker=null;\r\n  ui?.root.remove();ui=null;const w=wt;wt=null;try{w?.dispose();}catch{}\r\n }\r\n function dispose(){if(disposed)return;disposed=true;signal?.removeEventListener('abort',dispose);teardown();}\r\n signal?.addEventListener('abort',dispose,{once:true});\r\n return {open,dispose,get state(){return m.state;},get pin(){return pin;},setPin:place,drop:startDrop,cancel};\r\n}\r\n","modules/coach-world-rts/pixel-outline.mjs":"// Depth-edge outline pass for the pixel look. One scene render into an UnsignedByte colour target + DepthTexture, then ONE fullscreen quad\r\n// that darkens pixels on a depth discontinuity. No normal pass, no extra WebGLRenderer (the host passes its own renderer).\r\n//\r\n// The depth-edge indicator is adapted from three.js examples/jsm/postprocessing/RenderPixelatedPass.js:\r\n// Copyright 2010-2024 three.js authors, MIT License (https://github.com/mrdoob/three.js/blob/dev/LICENSE).\r\n// Changes: depth is linearised to view-space units first so the threshold is in metres, normals are dropped, the quad is a plain ShaderMaterial.\r\nimport {createLutBuilder,LUT_GLSL} from './retro-palette.mjs';\r\nexport const OUTLINE_STRENGTH=.4;\r\n\r\nexport const OUTLINE_VERT='varying vec2 vUv;void main(){vUv=uv;gl_Position=vec4(position.xy,0.,1.);}';\r\nexport const OUTLINE_FRAG=`\r\n${LUT_GLSL}\r\nuniform sampler2D tDiffuse;uniform sampler2D tDepth;uniform vec4 resolution;uniform float cameraNear;uniform float cameraFar;uniform float strength;uniform float threshold;\r\nvarying vec2 vUv;\r\nfloat viewZ(float d){return (cameraNear*cameraFar)/((cameraFar-cameraNear)*d-cameraFar);}\r\nfloat getDepth(int x,int y){float d=texture2D(tDepth,vUv+vec2(float(x),float(y))*resolution.zw).r;return -viewZ(d);}\r\nfloat depthEdgeIndicator(float depth){\r\n float diff=0.;\r\n diff+=clamp(getDepth(1,0)-depth,0.,1e4);\r\n diff+=clamp(getDepth(-1,0)-depth,0.,1e4);\r\n diff+=clamp(getDepth(0,1)-depth,0.,1e4);\r\n diff+=clamp(getDepth(0,-1)-depth,0.,1e4);\r\n return smoothstep(threshold*depth,threshold*depth*2.,diff);\r\n}\r\nvoid main(){\r\n vec4 texel=texture2D(tDiffuse,vUv);float depth=getDepth(0,0);\r\n float dei=depthEdgeIndicator(depth);\r\n vec3 c=texel.rgb*(1.-strength*dei);if(uRetro>.5)c=cwRetro(c); // V9 ?retro=1: our 256-colour palette, through the strip LUT\r\n gl_FragColor=vec4(c,texel.a);\r\n}`;\r\n\r\n// -> {render(scene,camera), resize(w,h), dispose(), enabled, target}. When enabled=false (or a host sets it at degrade >= 2) render() is a plain renderer.render.\r\n// retro: build our palette LUT a slice per render (a few ms) and switch it on when complete (V9, ?retro=1).\r\nexport function createOutline(T,renderer,{width=320,height=180,strength=OUTLINE_STRENGTH,threshold=.04,retro=false}={}){\r\n const depthTexture=new T.DepthTexture(width,height);depthTexture.type=T.UnsignedIntType;depthTexture.minFilter=depthTexture.magFilter=T.NearestFilter;\r\n const target=new T.WebGLRenderTarget(width,height,{minFilter:T.NearestFilter,magFilter:T.NearestFilter,format:T.RGBAFormat,type:T.UnsignedByteType,depthTexture,depthBuffer:true,generateMipmaps:false});\r\n // The target must hold exactly the bytes the screen would get, because raw ShaderMaterials (alien sky, critters, ruins, weather, portal) write display\r\n // values while lit/PS1 materials encode sRGB in-shader only for the screen. three encodes into a target only when it is flagged XR\r\n // (outputColorSpace = target.texture.colorSpace); RGBA8 storage keeps sampling from decoding, so the quad copies bytes through.\r\n // ponytail: borrows three's isXRRenderTarget path (r17x: affects output colour space + tone mapping only); revisit on a three upgrade.\r\n target.isXRRenderTarget=true;target.texture.colorSpace=T.SRGBColorSpace;target.texture.internalFormat='RGBA8';\r\n const material=new T.ShaderMaterial({vertexShader:OUTLINE_VERT,fragmentShader:OUTLINE_FRAG,depthTest:false,depthWrite:false,uniforms:{\r\n  tDiffuse:{value:target.texture},tDepth:{value:depthTexture},resolution:{value:new T.Vector4(width,height,1/width,1/height)},\r\n  cameraNear:{value:.1},cameraFar:{value:100},strength:{value:strength},threshold:{value:threshold},tLut:{value:null},uLutSize:{value:32},uRetro:{value:0}}});\r\n const lut=retro?createLutBuilder():null,lutTex=lut?Object.assign(new T.DataTexture(lut.data,lut.size*lut.size,lut.size,T.RGBAFormat),{magFilter:T.NearestFilter,minFilter:T.NearestFilter,generateMipmaps:false}):null;\r\n if(lutTex){material.uniforms.tLut.value=lutTex;material.uniforms.uLutSize.value=lut.size;}\r\n const geometry=new T.PlaneGeometry(2,2),quad=new T.Mesh(geometry,material),scene=new T.Scene(),cam=new T.OrthographicCamera(-1,1,1,-1,0,1);\r\n quad.frustumCulled=false;scene.add(quad);\r\n const o={enabled:true,target,material,\r\n  get retro(){return material.uniforms.uRetro.value>.5;},\r\n  render(sceneIn,camera){\r\n   if(lut&&!lut.done&&lut.step()){lutTex.needsUpdate=true;material.uniforms.uRetro.value=1;}\r\n   if(!o.enabled||!renderer.setRenderTarget){renderer.render(sceneIn,camera);return;}\r\n   material.uniforms.cameraNear.value=camera.near;material.uniforms.cameraFar.value=camera.far;\r\n   const prev=renderer.getRenderTarget?.()??null,info=renderer.info,auto=info?.autoReset; // one info window for both passes, so stats() count the scene, not just the quad\r\n   if(info){info.autoReset=false;info.reset?.();}\r\n   renderer.setRenderTarget(target);renderer.render(sceneIn,camera);\r\n   renderer.setRenderTarget(prev);renderer.render(scene,cam);\r\n   if(info)info.autoReset=auto;\r\n  },\r\n  resize(w,h){w=Math.max(1,Math.floor(w));h=Math.max(1,Math.floor(h));target.setSize(w,h);material.uniforms.resolution.value.set(w,h,1/w,1/h);},\r\n  dispose(){target.dispose();depthTexture.dispose();lutTex?.dispose();material.dispose();geometry.dispose();scene.clear();}};\r\n return o;\r\n}\r\n","modules/coach-world-rts/portal-pod.css":"/* Lane B (R81 polish, D8/D2): CSS-only wormhole cover between the base3d portal dolly and the AR walkthrough, and the phone frame\r\n   around the AR view (\"peer through a portal\"). No GL here: the cover hides the GPU hand-off (base3d released, settle, walkthrough claims). */\r\n.cw-portal-cover{position:fixed;inset:0;width:100%;height:100%;max-width:none;max-height:none;margin:0;padding:0;border:0;z-index:2147482999;overflow:hidden;pointer-events:none;color:transparent;\r\n background:#000 radial-gradient(circle at 50% 50%,#fff 0 2%,var(--pp-core,#b026ff) 9%,var(--pp-c3,#6200ea) 22%,color-mix(in srgb,var(--pp-c2,#aa00ff) 80%,#000) 40%,color-mix(in srgb,var(--pp-c1,#d500f9) 55%,#000) 66%,color-mix(in srgb,var(--pp-c0,#e040fb) 30%,#000) 100%)}\r\n.cw-portal-cover::before,.cw-portal-cover::after{content:'';position:absolute;left:50%;top:50%;width:160vmax;height:160vmax;margin:-80vmax 0 0 -80vmax;border-radius:50%;\r\n border:3vmax solid var(--pp-strip,#b026ff);box-shadow:0 0 4vmax var(--pp-strip,#b026ff),inset 0 0 4vmax var(--pp-strip,#b026ff);opacity:0;transform:scale(0);animation:cw-pp-ring .9s cubic-bezier(.55,0,1,.45) infinite}\r\n.cw-portal-cover::after{animation-delay:.45s;border-color:var(--pp-core,#fff)}\r\n@keyframes cw-pp-ring{0%{transform:scale(0);opacity:0}20%{opacity:.9}100%{transform:scale(1);opacity:.2}}\r\n[data-rts-walk-root].cw-peer::after{content:'';position:absolute;inset:0;border:6px solid var(--cw-peer-metal,#4d3d4f);border-radius:28px;pointer-events:none;z-index:50;\r\n box-shadow:0 0 0 48px #000,inset 0 0 14px var(--cw-peer-strip,#b026ff)}\r\n.cw-portal-cover[data-still]::before,.cw-portal-cover[data-still]::after{animation:none;opacity:.5;transform:scale(.6)}\r\n@media (prefers-reduced-motion:reduce){.cw-portal-cover::before,.cw-portal-cover::after{animation:none;opacity:.5;transform:scale(.6)}}\r\n","modules/coach-world-rts/portal-pod.mjs":"// Lane B (R81 polish, D8): the pod-landing portal. A phone-shaped (9:19.5) rounded-rect portal opens above the landed drop pod INSIDE the\r\n// base3d scene (one GL context: no second renderer, never playWormhole/portal.mjs). Its face is a small wormhole tunnel ShaderMaterial in the\r\n// user's own portal colours (board palette + light strip + frame metal). pin-drop drives the camera with dollyPose() until the portal fills\r\n// the screen, then a CSS cover (portalStyle colours) takes over while base3d is released and the AR walkthrough starts.\r\n// openPortalAbovePod(T,scene,{podPos,scale,camera,style}) -> {group,center,normal,update(ms),fillFraction(camera),dollyPose(camera,k,from),dispose}\r\nimport {readLook,readBoard,stripSeq} from '../portal/portal-look.mjs';\r\nimport {paletteFor} from '../portal/portal-tunnel-palettes.mjs';\r\n\r\nexport const PHONE=9/19.5,OPEN_MS=450,DOLLY_MS=2000,LOOK_K=.3,FOV_LOOK=35,FOV_END=50,OVERSCAN=1.12;\r\nexport const paletteKey=id=>'myr5.wormholePalette.'+id; // same key as portal.mjs:90 (not imported: portal.mjs mounts the second GL context)\r\nconst ease=k=>k<=0?0:k>=1?1:k*k*(3-2*k);\r\nconst lerp=(a,b,k)=>a+(b-a)*k;\r\n\r\n// The selected wormhole's colours. Quilt has no material palette: portal.mjs rings it in the strip colour, so do the same.\r\nexport function portalStyle(){\r\n const look=readLook(),board=readBoard();let pid=null;try{pid=localStorage.getItem(paletteKey(board));}catch{}\r\n const pal=board==='quilt'?null:paletteFor(board,pid),colors=pal?.colors?.slice(0,4)||stripSeq(look.strip);\r\n while(colors.length<4)colors.push(colors[colors.length-1]);\r\n return {board,colors,core:pal?.core||look.strip,strip:look.strip,metal:look.portal};\r\n}\r\n\r\nexport function roundedRect(T,w,h,r,seg=3){\r\n const s=new T.Shape(),x=-w/2,y=-h/2;\r\n s.moveTo(x+r,y);s.lineTo(x+w-r,y);s.quadraticCurveTo(x+w,y,x+w,y+r);s.lineTo(x+w,y+h-r);s.quadraticCurveTo(x+w,y+h,x+w-r,y+h);\r\n s.lineTo(x+r,y+h);s.quadraticCurveTo(x,y+h,x,y+h-r);s.lineTo(x,y+r);s.quadraticCurveTo(x,y,x+r,y);\r\n return new T.ShapeGeometry(s,seg);\r\n}\r\n\r\nexport const TUNNEL_FRAG=`uniform float uTime,uOpen,uR;uniform vec2 uHalf;uniform vec3 uColors[4],uCore,uStrip;varying vec2 vP;\r\nfloat sdRound(vec2 p,vec2 b,float r){vec2 q=abs(p)-b+r;return length(max(q,0.))+min(max(q.x,q.y),0.)-r;}\r\nvoid main(){\r\n vec2 p=vP/uHalf.y;float r=length(p),a=atan(p.y,p.x),z=.32/(r+.035),run=z-uTime*1.4;\r\n float band=mod(floor(run),4.);vec3 c=band<.5?uColors[0]:band<1.5?uColors[1]:band<2.5?uColors[2]:uColors[3];\r\n float sp=.5+.5*sin(a*3.+z*2.2-uTime*3.);c*=.5+.6*sp;c=mix(c,c*1.7,smoothstep(.8,1.,fract(run)));\r\n c=mix(c,uCore*1.35+.15,smoothstep(.3,0.,r));c*=smoothstep(4.5,1.5,z*.4)*.6+.4;\r\n float d=sdRound(vP,uHalf,uR);c=mix(c,uStrip*1.4,smoothstep(-.14*uHalf.x,0.,d)); // light-strip glow inside the frame\r\n gl_FragColor=vec4(c*uOpen,1.);\r\n}`;\r\nconst TUNNEL_VERT='varying vec2 vP;void main(){vP=position.xy;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}';\r\n\r\nexport function openPortalAbovePod(T,scene,{podPos,scale=1,camera,style=portalStyle()}={}){\r\n const H=2.6*scale,W=H*PHONE,R=W*.14,rim=.07*scale;\r\n const center=new T.Vector3(podPos.x,podPos.y+2.2*scale,podPos.z),group=new T.Group();group.name='rts-portal-pod';group.position.copy(center);\r\n group.lookAt(camera.position); // +Z faces the camera that watched the pod land\r\n const normal=camera.position.clone().sub(center).normalize();\r\n const col=h=>new T.Color(h);\r\n const u={uTime:{value:0},uOpen:{value:0},uR:{value:R},uHalf:{value:new T.Vector2(W/2,H/2)},uColors:{value:style.colors.map(col)},uCore:{value:col(style.core)},uStrip:{value:col(style.strip)}};\r\n const face=new T.Mesh(roundedRect(T,W,H,R),new T.ShaderMaterial({uniforms:u,vertexShader:TUNNEL_VERT,fragmentShader:TUNNEL_FRAG,fog:false,side:T.DoubleSide}));\r\n const frame=new T.Mesh(roundedRect(T,W+2*rim,H+2*rim,R+rim),new T.MeshBasicMaterial({color:col(style.metal),fog:false,side:T.DoubleSide}));\r\n frame.position.z=-.02*scale;face.renderOrder=frame.renderOrder=998;group.add(frame,face);group.scale.set(.35,.02,1);scene.add(group);\r\n // NDC corners of the full rect (rounded corners are hidden by OVERSCAN at the end of the dolly)\r\n const corners=[[-1,-1],[1,-1],[1,1],[-1,1]].map(([x,y])=>new T.Vector3(x*W/2,y*H/2,0)),v=new T.Vector3();\r\n let disposed=false;\r\n return {group,center,normal,width:W,height:H,\r\n  update(ms){const k=ease(ms/OPEN_MS);group.scale.set(lerp(.35,1,k),lerp(.02,1,k),1);u.uOpen.value=.25+.75*k;u.uTime.value=ms/1000;},\r\n  // fraction of the screen inside the portal's projected rect (0..1)\r\n  fillFraction(cam){\r\n   cam.updateMatrixWorld?.();group.updateMatrixWorld(true);let x0=1,x1=-1,y0=1,y1=-1;\r\n   for(const c of corners){v.copy(c).applyMatrix4(group.matrixWorld).project(cam);if(v.z>1||v.z<-1)return 0;x0=Math.min(x0,v.x);x1=Math.max(x1,v.x);y0=Math.min(y0,v.y);y1=Math.max(y1,v.y);}\r\n   const w=Math.max(0,Math.min(1,x1)-Math.max(-1,x0)),h=Math.max(0,Math.min(1,y1)-Math.max(-1,y0));return w*h/4;\r\n  },\r\n  // distance along the normal at which the portal covers the whole frustum (both axes) at fov FOV_END\r\n  fillDistance(aspect){const t=Math.tan(FOV_END*Math.PI/360);return Math.min(H/2/t,W/2/(t*aspect))/OVERSCAN;},\r\n  // k 0..1: first LOOK_K turns the camera onto the portal (fov -> 35), the rest dollies down the normal into it (fov 35 -> 50)\r\n  dollyPose(cam,k,from){\r\n   if(k<LOOK_K){const a=ease(k/LOOK_K);cam.position.copy(from.pos);cam.fov=lerp(from.fov,FOV_LOOK,a);cam.lookAt(v.copy(from.target).lerp(center,a));}\r\n   else{const a=ease((k-LOOK_K)/(1-LOOK_K)),end=center.clone().addScaledVector(normal,this.fillDistance(cam.aspect||1));\r\n    cam.position.lerpVectors(from.pos,end,a);cam.fov=lerp(FOV_LOOK,FOV_END,a);cam.lookAt(center);}\r\n   cam.updateProjectionMatrix?.();cam.updateMatrixWorld?.();\r\n  },\r\n  dispose(){if(disposed)return;disposed=true;group.removeFromParent();for(const m of [face,frame]){m.geometry.dispose();m.material.dispose();}},\r\n };\r\n}\r\n","modules/coach-world-rts/pose-source.mjs":"// W8a pose source: 3DoF orientation (iOS + Android), manual drag-look fallback. Pose = {position,quaternion,confidence,source}.\r\n// Quaternions are plain [x,y,z,w] arrays so the maths needs no three.js. Position is fixed (rotation-only until W8b PDR).\r\nconst RAD=Math.PI/180,H=Math.SQRT1_2;\r\nexport const qmul=(a,b)=>[a[3]*b[0]+a[0]*b[3]+a[1]*b[2]-a[2]*b[1],a[3]*b[1]-a[0]*b[2]+a[1]*b[3]+a[2]*b[0],a[3]*b[2]+a[0]*b[1]-a[1]*b[0]+a[2]*b[3],a[3]*b[3]-a[0]*b[0]-a[1]*b[1]-a[2]*b[2]];\r\nconst axis=(x,y,z,a)=>{const s=Math.sin(a/2);return [x*s,y*s,z*s,Math.cos(a/2)];};\r\nexport const rotate=(q,[x,y,z])=>{const t=qmul(qmul(q,[x,y,z,0]),[-q[0],-q[1],-q[2],q[3]]);return [t[0],t[1],t[2]];};\r\n// W3C DeviceOrientation (degrees) -> camera quaternion, the classic three DeviceOrientationControls maths (euler YXZ, -90deg about X, screen angle about Z).\r\nexport function orientationQuaternion(alpha,beta,gamma,screenDeg=0){\r\n let q=qmul(qmul(axis(0,1,0,alpha*RAD),axis(1,0,0,beta*RAD)),axis(0,0,1,-gamma*RAD));\r\n q=qmul(q,[-H,0,0,H]);return qmul(q,axis(0,0,1,-screenDeg*RAD));\r\n}\r\nexport function slerp(a,b,t){\r\n let d=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];const s=d<0?-1:1;d*=s;\r\n if(d>.9995){const r=a.map((v,i)=>v+(s*b[i]-v)*t),n=Math.hypot(...r);return r.map(v=>v/n);}\r\n const th=Math.acos(d),k=Math.sin(th),wa=Math.sin((1-t)*th)/k,wb=Math.sin(t*th)/k*s;return a.map((v,i)=>v*wa+b[i]*wb);\r\n}\r\n// Heading of the view direction about +Y: 0 facing -Z, positive turning left (counter-clockwise from above).\r\nexport const yawOf=q=>{const [fx,,fz]=rotate(q,[0,0,-1]);return Math.atan2(-fx,-fz);};\r\n\r\nexport function createPoseSource({win=globalThis,now=()=>performance.now(),eyeHeight=1.6,sensorTimeoutMs=1500,smoothing=.3,screenAngle=()=>win.screen?.orientation?.angle??win.orientation??0}={}){\r\n let mode='idle',target=null,smooth=null,offset=0,yaw=0,pitch=0,absolute=false,since=0,disposed=false;\r\n const listeners=[];\r\n const onEvent=abs=>e=>{\r\n  if(disposed||e.alpha==null||e.beta==null)return;\r\n  if(abs)absolute=true;else if(absolute)return; // compass-referenced events win once seen\r\n  target=orientationQuaternion(e.alpha,e.beta,e.gamma??0,screenAngle());mode='sensor';\r\n };\r\n return {\r\n  get mode(){return mode;},\r\n  get absolute(){return absolute;}, // true once a compass-referenced (north-aligned) event was seen\r\n  // MUST be called synchronously inside the user gesture, before any await. Both prompts are fired up front (iOS 13+).\r\n  requestPermission(){\r\n   try{\r\n    const asks=[win.DeviceOrientationEvent,win.DeviceMotionEvent].map(E=>typeof E?.requestPermission==='function'?E.requestPermission():'granted');\r\n    return Promise.all(asks.map(p=>Promise.resolve(p).catch(()=>'denied'))).then(r=>r.every(v=>v==='granted')?'granted':'denied');\r\n   }catch{return Promise.resolve('denied');}\r\n  },\r\n  // granted -> listen to sensors (falls back to drag if no valid event arrives); anything else -> drag-look.\r\n  begin(permission){\r\n   since=now();if(permission!=='granted'){mode='drag';return;}\r\n   mode='waiting';\r\n   for(const [name,abs] of [['deviceorientationabsolute',true],['deviceorientation',false]]){const h=onEvent(abs);win.addEventListener(name,h);listeners.push([name,h]);}\r\n  },\r\n  drag(dx,dy){if(mode!=='drag')return;yaw-=dx*.005;pitch=Math.max(-1.4,Math.min(1.4,pitch-dy*.005));},\r\n  // \"Face your base\": the current heading becomes the front. Placement only, not a safety reset.\r\n  recenter(){if(mode==='sensor'&&smooth)offset=-yawOf(smooth);else{yaw=0;pitch=0;offset=0;}},\r\n  pose(){\r\n   if(mode==='waiting'&&now()-since>sensorTimeoutMs)mode='drag';\r\n   let q,confidence=1;\r\n   if(mode==='sensor'){smooth=smooth?slerp(smooth,target,smoothing):target;q=qmul(axis(0,1,0,offset),smooth);}\r\n   else{q=qmul(axis(0,1,0,yaw),axis(1,0,0,pitch));confidence=mode==='drag'?.5:0;}\r\n   return {position:[0,eyeHeight,0],quaternion:q,confidence,source:mode==='sensor'?'imu3dof':'drag'};\r\n  },\r\n  dispose(){disposed=true;for(const [n,h] of listeners)win.removeEventListener(n,h);listeners.length=0;},\r\n };\r\n}\r\n","modules/coach-world-rts/provenance.json":"[\r\n {\r\n  \"path\": \"audio/sfx/mechanical-click-1.mp3\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"https://opengameart.org/content/7-mechanical-clicks-and-buzzes (mechanical) / audio/sfx/credits.html\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"CC0-1.0\",\r\n  \"shipped\": true,\r\n  \"notes\": \"select-ack cue (reused, existing app asset; synth fallback 'mechanical')\"\r\n },\r\n {\r\n  \"path\": \"audio/sfx/mechanical-click-2.mp3\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"https://opengameart.org/content/7-mechanical-clicks-and-buzzes (mechanical) / audio/sfx/credits.html\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"CC0-1.0\",\r\n  \"shipped\": true,\r\n  \"notes\": \"select-ack cue (reused, existing app asset)\"\r\n },\r\n {\r\n  \"path\": \"audio/sfx/cogs-ratchet-1.mp3\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"https://opengameart.org/content/7-mechanical-clicks-and-buzzes (mechanical) / audio/sfx/credits.html\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"CC0-1.0\",\r\n  \"shipped\": true,\r\n  \"notes\": \"mine-strike cue (reused, existing app asset; synth fallback 'cogs')\"\r\n },\r\n {\r\n  \"path\": \"audio/sfx/cogs-ratchet-2.mp3\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"https://opengameart.org/content/7-mechanical-clicks-and-buzzes (mechanical) / audio/sfx/credits.html\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"CC0-1.0\",\r\n  \"shipped\": true,\r\n  \"notes\": \"mine-strike cue (reused, existing app asset)\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/sfx.mjs#deposit\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"original synth\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"deposit ping: original WebAudio synthesis, no sample\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/pixelify-sans.woff2\",\r\n  \"kind\": \"font\",\r\n  \"source\": \"https://github.com/google/fonts/tree/main/ofl/pixelifysans (eifetx/Pixelify-Sans)\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"OFL-1.1\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W5 HUD titles; variable wght 400-700 converted ttf->woff2 with fontTools; licence ui/pixelify-sans-OFL.txt\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/m5x7.woff2\",\r\n  \"kind\": \"font\",\r\n  \"source\": \"https://managore.itch.io/m5x7 (Daniel Linssen); file from github yourlin/Werewolf-Unity mirror\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"CC0-1.0\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W5 HUD numbers and tooltips; licence verified on itch page 2026-10-16, ui/m5x7-LICENSE.txt\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/bezel-occupied.png\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, generated by tools/rts-hud-art.py\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"96x96 9-slice bezel, slice 24. Terran-style riveted metal (occupied-worker factions). No Blizzard or Kenney pixels\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/bezel-warp.png\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, generated by tools/rts-hud-art.py\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"96x96 9-slice bezel, slice 24. Protoss-style gold trim (seeded-warp factions). No Blizzard or Kenney pixels\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/bezel-seed.png\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, generated by tools/rts-hud-art.py\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"96x96 9-slice bezel, slice 24. Zerg-style organic rib (consumed-seed factions). No Blizzard or Kenney pixels\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ui/icons/*.png\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, hand-pixeled in tools/rts-hud-art.py (15 icons 16x16: up x move wave fuel breath zz group flag train ship crest home door list)\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W5 command card icons\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/mining.mjs#ore-node\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, 8x6 pixel ore cluster drawn in code\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W3 resource node near HQ\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/ps1-material.mjs\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"original, written from the A7 description (vertex snap, affine uv, 5-5-5 Bayer dither); BucketOSoftware/playshader-one (MPL-2.0) was read for ideas only and NOT copied\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W8a PS1 look\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/base-meshes.mjs#textures\",\r\n  \"kind\": \"image\",\r\n  \"source\": \"original, procedural 64x64 greyscale panel cell per family generated in code\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W8a near-field building texels\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/sfx.mjs#build-start\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"original synth\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"build-start cue: original WebAudio synthesis (low double tick), no sample\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/sfx.mjs#build-complete\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"original synth\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"build-complete cue: original WebAudio synthesis (2-note fifth C5-G5), no sample\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/sfx.mjs#level-up\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"original synth\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"level-up cue: original WebAudio synthesis (3-note arpeggio C5-E5-G5), no sample\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/construction.mjs\",\r\n  \"kind\": \"pixel-art\",\r\n  \"source\": \"original, drawn in code (stakes, scaffold, resonator, seed mound, creep dots); building bodies reuse the app's own pixel-art.mjs sprites\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"W3 construction sequences; no imported art\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/sfx.mjs#unit-ready\",\r\n  \"kind\": \"audio\",\r\n  \"source\": \"original synth\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"unit-ready cue: original WebAudio synthesis (2-note rise E5-A5), no sample\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world/vendor/martini/martini.mjs\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"https://github.com/mapbox/martini (npm @mapbox/martini 0.2.0, index.js unmodified)\",\r\n  \"version\": \"0.2.0\",\r\n  \"licence\": \"ISC\",\r\n  \"shipped\": true,\r\n  \"notes\": \"R58 WS-C RTIN terrain mesher; LICENSE alongside\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world/vendor/world-tiles-vendor.mjs#pmtiles\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"https://www.npmjs.com/package/pmtiles (protomaps/PMTiles)\",\r\n  \"version\": \"4.5.0\",\r\n  \"licence\": \"BSD-3-Clause\",\r\n  \"shipped\": true,\r\n  \"notes\": \"bundled with esbuild into world-tiles-vendor.mjs; licence modules/coach-world/vendor/pmtiles-LICENSE.txt\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world/vendor/world-tiles-vendor.mjs#vector-tile\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"https://www.npmjs.com/package/@mapbox/vector-tile\",\r\n  \"version\": \"3.0.0\",\r\n  \"licence\": \"BSD-3-Clause\",\r\n  \"shipped\": true,\r\n  \"notes\": \"bundled; modules/coach-world/vendor/vector-tile-LICENSE.txt; pulls @mapbox/point-geometry (ISC, point-geometry-LICENSE.txt)\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world/vendor/world-tiles-vendor.mjs#pbf\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"https://www.npmjs.com/package/pbf\",\r\n  \"version\": \"5.1.2\",\r\n  \"licence\": \"BSD-3-Clause\",\r\n  \"shipped\": true,\r\n  \"notes\": \"bundled (PbfReader only); modules/coach-world/vendor/pbf-LICENSE.txt\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world/vendor/world-tiles-vendor.mjs#earcut\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"https://www.npmjs.com/package/earcut\",\r\n  \"version\": \"3.2.4\",\r\n  \"licence\": \"ISC\",\r\n  \"shipped\": true,\r\n  \"notes\": \"bundled; modules/coach-world/vendor/earcut-LICENSE.txt\"\r\n },\r\n {\r\n  \"path\": \"tests/fixtures/vegas-fremont-z16.pmtiles + world-static/\",\r\n  \"kind\": \"data\",\r\n  \"source\": \"Overpass API extract of OpenStreetMap, Fremont St Las Vegas (tests/fixtures/make-world-fixture.mjs)\",\r\n  \"version\": \"2026-10-17\",\r\n  \"licence\": \"ODbL-1.0 (c) OpenStreetMap contributors\",\r\n  \"shipped\": true,\r\n  \"notes\": \"test fixture only, 24 z16 tiles; not served to users\"\r\n },\r\n {\r\n  \"path\": \"server/world-weather.mjs#nws\",\r\n  \"kind\": \"data\",\r\n  \"source\": \"https://www.weather.gov/documentation/services-web-api (api.weather.gov, fetched server-side by our Worker; User-Agent 'MYR5 (myr5.mominc.online)')\",\r\n  \"version\": \"v1\",\r\n  \"licence\": \"US government work, public domain (no key, free for any use)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"Credit line: 'Weather: U.S. National Weather Service' (ATTRIBUTION in weather.mjs). Client only calls our /api/weather with lat/lng rounded to 0.1 deg (rounded again server-side); users' IPs never reach NWS. Open-Meteo is OFF by default (free tier is non-commercial); enable only via deps.config.provider='open-meteo' if Ian approves non-commercial use.\"\r\n },\r\n {\r\n  \"path\": \"modules/coach-world-rts/weather-fx.mjs\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"original\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"R58 alien weather FX, procedural, no assets\"\r\n },\r\n {\r\n  \"path\": \"server/world-weather.mjs\",\r\n  \"kind\": \"code\",\r\n  \"source\": \"original\",\r\n  \"version\": \"n/a\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"R58 GET /api/weather route\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/supportive.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/supportive.glb (project hangar ship, ship-catalog id 'supportive') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/direct.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/direct.glb (project hangar ship, ship-catalog id 'direct') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/analytical.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/analytical.glb (project hangar ship, ship-catalog id 'analytical') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/playful.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/playful.glb (project hangar ship, ship-catalog id 'playful') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/calm.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/calm.glb (project hangar ship, ship-catalog id 'calm') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n },\r\n {\r\n  \"path\": \"pod/worlds/traffic/mom.glb\",\r\n  \"kind\": \"model\",\r\n  \"source\": \"derived from plan/assets-inbox/ships/mom.glb (project hangar ship, ship-catalog id 'mom') by tools/traffic-ships/build.mjs\",\r\n  \"version\": \"R59\",\r\n  \"licence\": \"original (project-owned)\",\r\n  \"shipped\": true,\r\n  \"notes\": \"freeway traffic hull: vertex-cluster decimated to ~2.3k tris, texture brightness baked to vertex colour, nose +z; ~28 KB\"\r\n }\r\n]\r\n","modules/coach-world-rts/ps1-material.mjs":"import {GLSL_COMPRESS} from './ar-scale.mjs';\r\n// Our own PS1-look material (written from the A7 description; no third-party shader code).\r\n// MeshLambertMaterial + onBeforeCompile keeps three's vertex colours and fog. Effects:\r\n//  vertex snap to the internal pixel grid, affine UVs (uv*w in the vertex stage, /w in the fragment stage cancels perspective),\r\n//  5-5-5 colour (31 levels) or 3-level posterise, 4x4 Bayer ordered dither.\r\n// Low internal resolution + nearest upscale is done by the renderer size (fitPixelRenderer scale 3) and CSS pixelated.\r\nexport function createPs1Material(T,{map=null,vertexColors=true,snap=true,affine=true,posterize=false,resolution=[320,180],alphaTest=0}={}){\r\n const m=new T.MeshLambertMaterial({map,vertexColors,flatShading:false,alphaTest});\r\n const uniforms={uPs1Res:{value:new T.Vector2(resolution[0],resolution[1])},uPs1Levels:{value:posterize?2:31},uPs1Fade:{value:1}};\r\n m.userData.ps1={uniforms,setResolution:(w,h)=>uniforms.uPs1Res.value.set(w,h),setFade:v=>{uniforms.uPs1Fade.value=v;}}; // 1 = opaque; <1 dissolves by screen-space alpha hash (R58 base fade)\r\n m.customProgramCacheKey=()=>`ps1${snap?'s':''}${affine&&map?'a':''}${posterize?'p':''}${alphaTest?'c':''}`;\r\n m.onBeforeCompile=shader=>{\r\n  Object.assign(shader.uniforms,uniforms);\r\n  const useAffine=affine&&!!map;\r\n  shader.vertexShader=shader.vertexShader\r\n   .replace('#include <common>',`#include <common>\\nuniform vec2 uPs1Res;\\n${useAffine?'varying vec2 vPs1Uv;\\nvarying float vPs1W;':''}`)\r\n   .replace('#include <project_vertex>',`#include <project_vertex>\r\n${snap?'{vec2 g=uPs1Res*0.5;gl_Position.xy=floor(gl_Position.xy/gl_Position.w*g+0.5)/g*gl_Position.w;}':''}\r\n${useAffine?'vPs1Uv=vMapUv*gl_Position.w;vPs1W=gl_Position.w;':''}`);\r\n  shader.fragmentShader=shader.fragmentShader\r\n   .replace('#include <common>',`#include <common>\\nfloat cwBayer(vec2 p){vec2 q=mod(floor(p),4.);float a=mod(q.x,2.),b=mod(q.y,2.),c=floor(q.x/2.),d=floor(q.y/2.);return (4.*(2.*a+3.*b-4.*a*b)+(2.*c+3.*d-4.*c*d)+.5)/16.;}\\nuniform float uPs1Levels;\\nuniform float uPs1Fade;\\n${useAffine?'varying vec2 vPs1Uv;\\nvarying float vPs1W;':''}\r\nconst float ps1Bayer[16]=float[16](0.,8.,2.,10.,12.,4.,14.,6.,3.,11.,1.,9.,15.,7.,13.,5.);`);\r\n  if(useAffine)shader.fragmentShader=shader.fragmentShader.replace('#include <map_fragment>','diffuseColor*=texture2D(map,vPs1Uv/vPs1W);');\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <alphatest_fragment>','#include <alphatest_fragment>\\nif(uPs1Fade<1.&&cwBayer(gl_FragCoord.xy)>=uPs1Fade)discard;').replace('#include <dithering_fragment>',`#include <dithering_fragment>\r\n{int ix=int(mod(gl_FragCoord.x,4.)),iy=int(mod(gl_FragCoord.y,4.));float b=(ps1Bayer[iy*4+ix]+.5)/16.;\r\ngl_FragColor.rgb=floor(gl_FragColor.rgb*uPs1Levels+b)/uPs1Levels;}`);\r\n };\r\n return m;\r\n}\r\n\r\n// R58: give any non-shader material the same screen-space alpha-hash fade (terrain3d lambert, sprites, points). One recompile on first call.\r\nexport function addFade(m){\r\n const have=m.userData.ps1?.setFade?m.userData.ps1:m.userData.cwFade;if(have)return have;\r\n const u={value:1},prev=m.onBeforeCompile;\r\n m.onBeforeCompile=(sh,r)=>{prev?.call(m,sh,r);sh.uniforms.uPs1Fade=u;\r\n  sh.fragmentShader='uniform float uPs1Fade;\\nfloat cwBayer(vec2 p){vec2 q=mod(floor(p),4.);float a=mod(q.x,2.),b=mod(q.y,2.),c=floor(q.x/2.),d=floor(q.y/2.);return (4.*(2.*a+3.*b-4.*a*b)+(2.*c+3.*d-4.*c*d)+.5)/16.;}\\n'+sh.fragmentShader.replace('#include <alphatest_fragment>','#include <alphatest_fragment>\\nif(uPs1Fade<1.&&cwBayer(gl_FragCoord.xy)>=uPs1Fade)discard;');};\r\n m.needsUpdate=true;return m.userData.cwFade={setFade:v=>{u.value=v;}};\r\n}\r\n// R81 lane C: AR distance compression (ar-scale.mjs compress) in the vertex stage, about the camera, bearings kept. Meshes/points/lines (after project_vertex),\r\n// sprites (centre moved, size kept) and plain ShaderMaterials (`gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);`). u = shared {uCwD0,uCwDH}.\r\n// Returns false when the shader has no known hook (caller hides it). Objects must not be CPU-culled on their true positions (caller sets frustumCulled=false).\r\nconst CW_MESH='#include <project_vertex>',CW_SPRITE='vec4 mvPosition = modelViewMatrix[ 3 ];',CW_RAW='gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);';\r\nexport function addCompress(m,u){\r\n if(m.userData.cwCompress)return true;\r\n const raw=m.isShaderMaterial,src=raw?m.vertexShader:'';if(raw&&!src.includes(CW_RAW))return false;\r\n const prev=m.onBeforeCompile,key=m.customProgramCacheKey;\r\n m.onBeforeCompile=(sh,r)=>{prev?.call(m,sh,r);Object.assign(sh.uniforms,u);let v=sh.vertexShader;\r\n  if(v.includes(CW_RAW))v=v.replace(CW_RAW,'gl_Position=projectionMatrix*viewMatrix*vec4(cwCompressW((modelMatrix*vec4(position,1.)).xyz),1.);');\r\n  else if(v.includes(CW_SPRITE))v=v.replace(CW_SPRITE,CW_SPRITE+'\\n{vec3 cwC=(modelMatrix*vec4(0.,0.,0.,1.)).xyz;mvPosition.xyz+=(viewMatrix*vec4(cwCompressW(cwC)-cwC,0.)).xyz;}');\r\n  else v=v.replace(CW_MESH,CW_MESH+'\\n{vec4 cwW=vec4(transformed,1.);\\n#ifdef USE_BATCHING\\ncwW=batchingMatrix*cwW;\\n#endif\\n#ifdef USE_INSTANCING\\ncwW=instanceMatrix*cwW;\\n#endif\\ncwW=modelMatrix*cwW;cwW.xyz=cwCompressW(cwW.xyz);mvPosition=viewMatrix*cwW;gl_Position=projectionMatrix*mvPosition;}');\r\n  sh.vertexShader=GLSL_COMPRESS+v;};\r\n m.customProgramCacheKey=()=>(key?key.call(m):'')+'|cwc'; // ps1 materials share a fixed key; compressed ones must not reuse an uncompressed program\r\n m.needsUpdate=true;m.userData.cwCompress=true;return true;\r\n}\r\n","modules/coach-world-rts/raid-appearance.mjs":"import {COACH_UNITS,SECTION_COMPANIONS,BASIC_WORKER} from '../coach-world/unit-catalog.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {sizeFor} from './runtime.mjs';\r\nimport {selectedHull,liveShipTier} from './ship-roster.mjs';\r\nexport function raidAppearance(role,base){\r\n const faction=FACTION_BY_ID[base?.faction]?base.faction:'chest';\r\n if(role==='a'){const hull=selectedHull(base);return {role:'ship',worker:true,air:true,faction,scale:1.1,shipTier:liveShipTier(base),shipId:hull.id,shipVariant:hull.variant};} // the side's actual selected hull\r\n let d;\r\n if(role==='s'||role==='h')d=SECTION_COMPANIONS[faction].find(x=>x.role===(role==='s'?'scout':'carrier'));\r\n else if(role==='g'||role==='b'){\r\n  const options=COACH_UNITS.filter(x=>x.section===faction&&x.role===(role==='b'?'boss':'grunt'));\r\n  const earned=new Set(base?.coachAccess||[]);d=options.find(x=>earned.has(x.sourceCoachId))||options[0];\r\n }\r\n d??=BASIC_WORKER;\r\n return {role:d.role,worker:true,faction,source:d.sourceCoachId,sheet:d.rtsSheet||null,scale:sizeFor(d)};\r\n}\r\nexport function raidClip(action,now){\r\n if(!action)return null;const elapsed=Math.max(0,now-action.at),clip=action.kind==='death'?'death':action.kind;\r\n const duration={attack:420,hit:210,death:966}[clip];\r\n return duration&&elapsed<duration?{clip,clipElapsed:elapsed}:null;\r\n}\r\n","modules/coach-world-rts/raid-arena.mjs":"import {baseSystems,defenseSystems} from '../coach-world/base-systems.mjs';\r\n// R55 raid: pure arena <-> base-tile mapping and sim input builder. No three, no DOM, no network (tested in Node).\r\n// The sim arena is 20x12 tiles (battle-sim W,H in 1/256). Attackers spawn around arena (2.5, 6) and defenders around (17.5, 6). The arena is laid onto the 24x24 base so\r\n// the attacker centroid sits on the drop tile, the +x axis points at the base centre (snapped to a grid axis, a pure rotation so no mirroring), and one arena tile = K base tiles.\r\n// Tile coordinates here are CENTRE coordinates: tile i spans [i, i+1), its centre is i+.5 (same convention as the crew billboards).\r\nimport {simulate,W,H,MAX_UNITS,MAX_TOWERS,RULES_VERSIONS} from './battle-sim.mjs';\r\nimport {NPC_ARMIES} from './battle-view.mjs';\r\nimport {BUILDINGS,BUILDING_BY_ID,MAX_LEVEL,hangarShipTier} from '../coach-world/catalog.mjs';\nimport {levelStats} from './unit-levels.mjs';\r\nimport {unitRoster} from '../coach-world/expedition-catalog.mjs';\r\nimport {unitInstances} from '../coach-world/production.mjs';\r\n\r\nexport const ARENA_TILES=[W/256,H/256],ANCHOR=[2.5,6],K=.6,CENTRE=[12,12],GRID=24;\r\nexport const DEFENSE_ZONE=[2304,3584,512,1792]; // arena 1/256 tile: x 9-14, y 2-7 tiles = the mid-arena band where garrison and attackers actually meet (measured over 51 drops: hits at x~2400-2700, y~500-900)\r\nconst NPC_GARRISON={easy:'gg',normal:'ggh',hard:'ggggs'}; // R3-C7: per-tier, tuned against the real HQ1 crew 'sgggg' (was one 'ggggs' for all: that crew lost easy 0/200)\r\nconst DEFAULT_PADS=[0,1,2];\r\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\r\n\r\n// Orientation: arena +x points from the drop tile toward the base centre along the dominant grid axis; +y is that axis rotated 90 degrees.\r\nexport function orientation(drop,centre=CENTRE){\r\n const dx=centre[0]-(drop.x+.5),dy=centre[1]-(drop.y+.5);\r\n const ex=Math.abs(dx)>=Math.abs(dy)?[dx<0?-1:1,0]:[0,dy<0?-1:1];\r\n return {ex,ey:[-ex[1],ex[0]]};\r\n}\r\n\r\n// dropTile {x,y} integers -> {drop, ex, ey, k, pads, toWorld(ax,ay), toArena(tx,ty), towersArena(tiles)}\r\nexport function arenaFromTile(drop,{k=K,centre=CENTRE}={}){\r\n const {ex,ey}=orientation(drop,centre),cx=drop.x+.5,cy=drop.y+.5;\r\n const toWorld=(ax,ay)=>({x:cx+k*((ax-ANCHOR[0])*ex[0]+(ay-ANCHOR[1])*ey[0]),y:cy+k*((ax-ANCHOR[0])*ex[1]+(ay-ANCHOR[1])*ey[1])});\r\n const toArena=(tx,ty)=>{const dx=(tx-cx)/k,dy=(ty-cy)/k;return {x:ANCHOR[0]+dx*ex[0]+dy*ex[1],y:ANCHOR[1]+dx*ey[0]+dy*ey[1]};};\r\n // tile centres -> sim tower placement (integers in 1/256, clamped inside the arena; a tower far past the arena edge stands at the edge)\r\n const towersArena=pts=>pts.map(p=>{const a=toArena(p.x,p.y);return [clamp(Math.round(a.x*256),0,W),clamp(Math.round(a.y*256),0,H)];});\r\n // v6 infrastructure (press, mortar, beacon, trap, barriers, generator): the 24x24 base is wider than the arena window, so raw clamping parked sites in far corners\r\n // (a press on the attackers' own pad, a beacon nowhere near the fight). Keep each site's depth ordering but land it inside the garrison pocket.\r\n const defenseArena=pts=>pts.map(p=>{const a=toArena(p.x,p.y);return [clamp(Math.round(a.x*256),DEFENSE_ZONE[0],DEFENSE_ZONE[1]),clamp(Math.round(a.y*256),DEFENSE_ZONE[2],DEFENSE_ZONE[3])];});\r\n return {drop:{...drop},ex,ey,k,pads:DEFAULT_PADS.slice(),toWorld,toArena,towersArena,defenseArena};\r\n}\r\n\r\nconst built=(base,id)=>{const b=base?.buildings?.[id];return b&&(b.level>0||b.job)?b:null;};\r\n// Drop tile must be on the map and not on a standing building (tower, wall, HQ...). Returns {ok,reason?}.\r\nexport function validDrop(base,tile){\r\n if(!tile||!Number.isInteger(tile.x)||!Number.isInteger(tile.y)||tile.x<0||tile.y<0||tile.x>=GRID||tile.y>=GRID)return {ok:false,reason:'Off the map'};\r\n for(const d of BUILDINGS){const b=built(base,d.id);if(!b)continue;const x=b.x??d.x,y=b.y??d.y;\r\n  if(tile.x>=x&&tile.x<x+d.w&&tile.y>=y&&tile.y<y+d.h)return {ok:false,reason:'A building is in the way'};}\r\n return {ok:true};\r\n}\r\n\r\n// Standing defense towers of a base as tile-centre points, stable order, at most MAX_TOWERS.\r\nexport function towerPoints(base){\r\n return BUILDINGS.filter(d=>d.kind==='tower'&&built(base,d.id)&&built(base,d.id).level>0&&base.rtsTech?.health?.[d.id]!==0).slice(0,MAX_TOWERS)\r\n  .map(d=>{const b=base.buildings[d.id];return {x:(b.x??d.x)+d.w/2,y:(b.y??d.y)+d.h/2};});\r\n}\r\n\r\n// Defender garrison for a rendered base: NPC tier uses NPC_ARMIES, a friend gets a small militia scaled by their highest building level.\r\nexport function defenderUnits(base,tier=null){\r\n const nt=towerPoints(base).length,room=MAX_UNITS-nt;\r\n if(tier&&NPC_ARMIES[tier]){\n  // Week zero preserves the tuned tier templates exactly; each earned growth level\n  // adds one deterministic garrison role, bounded by the simulator's unit slots.\n  const builtLevels=Object.values(base?.buildings??{}).filter(b=>b?.level>0).map(b=>b.level);\n  const level=Math.max(1,Math.min(MAX_LEVEL,builtLevels.length?Math.max(...builtLevels):1));\n  const additions='gshbg'.slice(0,Math.max(0,level-1));\n  return (NPC_GARRISON[tier]+additions).slice(0,Math.max(1,room));\n } // difficulty = tower count (1/2/3), tuned so the starter crew sgggg wins easy 100% / normal ~43% / hard 0% at level 1\n const hq=Math.max(1,Number.isInteger(base?.buildings?.hq?.level)?base.buildings.hq.level:1); // keyed to HQ level, so the garrison grows with the base core\r\n const militia='g'.repeat(2+hq)+(hq>=3?'s':'')+(hq>=5?'b':'');\r\n const crew=rosterFromUnits(unitInstances(base||{}).filter(u=>!u.away),'');\r\n return (crew?[...militia+crew].sort((p,q)=>RANK(p)-RANK(q)).join(''):militia).slice(0,Math.max(1,room));\r\n}\r\n\r\n// Player's selected troops -> sim roster string (workers fight as grunts, scouts scout, carriers haul, anything else is a boss once). Empty -> the practice default.\r\nconst ROLE_CHAR={worker:'g',grunt:'g',boss:'b',scout:'s',hauler:'h',carrier:'h',ship:'a'}; // preserve explicit gameplay roles; ships use the v5 air role\r\nconst RANK=c=>'bsh'.indexOf(c)<0?3:'bsh'.indexOf(c);\r\nexport function rosterFromUnits(units,fallback='gggsbh'){\r\n let boss=false,u=[];\r\n for(const x of units||[]){let c=ROLE_CHAR[x?.role];if(!c){c=boss?'g':'b';boss=true;}u.push(c);}\r\n u=u.map((c,i)=>[c,i]).sort((p,q)=>RANK(p[0])-RANK(q[0])||p[1]-q[1]).slice(0,MAX_UNITS).map(p=>p[0]).join(''); // strongest first: boss > scout > hauler > workers fill the rest\r\n if(!u||/^h+$/.test(u))u=fallback;\r\n return u;\r\n}\r\n\r\n// Everything the sim needs for a drop: {input, arena}. The attacker is side 0 ('a'), the garrison and towers are side 1.\r\n// Hangar ships fly with the raid: `ships` = attacker's hangarShipTier (ships3d shipTier). Air already picked in `attackers` counts toward it; ships replace the\r\n// last ground slots when the roster is full, but at least one ground unit stays.\r\nexport function withShips(attackers,ships=0){\r\n let u=attackers||'';const have=[...u].filter(c=>c==='a').length,add=Math.max(0,Math.min(ships,MAX_UNITS-1)-have);\r\n for(let i=0;i<add;i++){if(u.length<MAX_UNITS)u+='a';else{const k=u.replace(/a+$/,'').length-1;if(k<1)break;u=u.slice(0,k)+u.slice(k+1)+'a';}}\r\n return u;\r\n}\r\n// R1-C6: the attacker's best hall level feeds sim upgrades; an unbuilt hangar (level 0) sends no ships. Does not alter hangarShipTier (3D visuals).\r\nexport const attackerUpgrades=base=>{const {w,a}=levelStats(Math.max(0,...BUILDINGS.filter(d=>d.kind==='hall').map(d=>base?.buildings?.[d.id]?.level|0)));const tech=baseSystems(base);return {w:Math.min(5,w+tech.weaponUpgrade),a:Math.min(5,a+tech.armorUpgrade)};};\r\nexport const raidShips=base=>(base?.buildings?.hangar?.level|0)>0?hangarShipTier(base.buildings.hangar.level):0;\r\nexport function buildRaid({drop,base,tier=null,attackers,ships=0,seed=1,upgrades={w:0,a:0}}){\r\n attackers=withShips(attackers,ships);\r\n const arena=arenaFromTile(drop),pts=towerPoints(base),nt=pts.length,units=defenderUnits(base,tier);\r\n const tech=baseSystems(base),def={w:Math.min(5,tech.weaponUpgrade),a:Math.min(5,tech.armorUpgrade),u:units+'t'.repeat(nt)};if(nt)def.towers=arena.towersArena(pts);\r\n const defenses=defenseSystems(base,arena);\r\n // Empty legacy targets need no infrastructure payload; retain their v5 input contract.\r\n const input={rulesVersion:defenses.length?RULES_VERSIONS.at(-1):5,seed,rosters:[{w:upgrades.w,a:upgrades.a,u:attackers},def],pads:[arena.pads,arena.pads],...(defenses.length?{systems:[[],defenses]}:{})};\r\n return {input,arena};\r\n}\r\nexport const simulateRaid=r=>simulate(r.input);\r\n\r\n// Deterministic seed from the tile + a target label so the same drop replays the same.\r\nexport function seedFor(tile,label=''){let h=2166136261;for(const c of `${tile.x},${tile.y},${label}`)h=Math.imul(h^c.charCodeAt(0),16777619)>>>0;return (h%2147483646)+1;}\r\nexport {BUILDING_BY_ID};\r\n","modules/coach-world-rts/raid-targets.mjs":"import { BUILDINGS, MAX_LEVEL } from '../coach-world/catalog.mjs';\nimport { newBase } from '../coach-world/state.mjs';\n\nconst LABEL={easy:'Scrap outpost',normal:'Raider camp',hard:'Warlord fortress'};\nconst TOWERS={easy:1,normal:2,hard:3};\nconst FACTION={easy:'chest',normal:'quads',hard:'martial-arts'};\nconst WEEK_MS=7*24*60*60*1000;\n// Current pacing policy, exposed so tuning is explicit and testable.\nexport const RAID_GROWTH_POLICY=Object.freeze({weekMs:WEEK_MS,maxLevel:MAX_LEVEL});\n\nfunction growthFor(mappedAt,now){\n const validNow=Number.isSafeInteger(now)&&now>=0;\n const validMapped=Number.isSafeInteger(mappedAt)&&mappedAt>=0;\n const completedWeeks=validNow&&validMapped&&mappedAt<=now?Math.floor((now-mappedAt)/RAID_GROWTH_POLICY.weekMs):0;\n const level=Math.min(RAID_GROWTH_POLICY.maxLevel,1+completedWeeks);\n const nextGrowthAt=validMapped&&level<RAID_GROWTH_POLICY.maxLevel?mappedAt+(completedWeeks+1)*RAID_GROWTH_POLICY.weekMs:null;\n return {level,growth:{mappedAt:validMapped?mappedAt:null,completedWeeks,nextGrowthAt}};\n}\n\nexport function npcTargets({mappedAt=null,now=0}={}){\n const {level,growth}=growthFor(mappedAt,now);\n return Object.keys(LABEL).map(tier=>{\n  const base=newBase(`raid-npc-${tier}`,0);base.faction=FACTION[tier];\n  BUILDINGS.filter(d=>d.kind==='tower').slice(0,TOWERS[tier]).forEach(d=>{base.buildings[d.id].level=1;});\n  if(level>1)for(const d of BUILDINGS){const building=base.buildings[d.id];if(building?.level>0)building.level=Math.min(level,MAX_LEVEL);}\n  return {id:`npc-${tier}`,label:`${LABEL[tier]} (${tier}) level ${level}/${MAX_LEVEL}`,tier,base,level,maxLevel:MAX_LEVEL,growth:{...growth}};\n });\n}\n","modules/coach-world-rts/raid.mjs":"// R55 remote raid. Pick a target base (friends + same-sector rivals from the authorized appearance DTO, else NPC bases) -> see it in base3d -> tap a tile -> confirm ->\r\n// the Commander pod drops (drop-pod-scene) -> pod-top camera -> troops fight on battle-sim around the drop tile with elements + towers -> VICTORY/DEFEAT.\r\n// Client-only practice (P1): no fetch beyond the friend list read, no storage, no reward. Never reads or shows a friend's home coordinates.\r\nimport {simulate,ABILITIES,ABILITY_RADIUS,ABILITY_COOLDOWN,ABILITY_LEAD,abilityState,W,H} from './battle-sim.mjs';\r\nimport {fireCannon} from './cannon-fx.mjs';\r\nimport {createPlayback,bannerFor,rosterMap} from './battle-view.mjs';\r\nimport {buildRaid,validDrop,rosterFromUnits,withShips,seedFor,attackerUpgrades,raidShips,arenaFromTile} from './raid-arena.mjs';\r\nimport {BUILDINGS,FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {UNIT_BY_ID} from '../coach-world/expedition-catalog.mjs';\r\nimport {newBase,normalizeBase} from '../coach-world/state.mjs';\r\nimport {SCALE,spawnTile} from './base-meshes.mjs';\r\nimport {towerPoints} from './raid-arena.mjs';\r\nimport {raidAppearance,raidClip} from './raid-appearance.mjs';\r\nimport {npcTargets} from './raid-targets.mjs';\r\nimport {createPoseSource} from './pose-source.mjs';\r\nexport {npcTargets} from './raid-targets.mjs';\r\n\r\n// ---------- flow state machine (pure) ----------\r\n// idle -open-> picking -pick-> scouting -tile-> confirming -confirm-> dropping -landed-> fighting -end-> result -again-> picking; close from anywhere -> idle.\r\nconst TABLE={\r\n idle:{open:'picking'},\r\n picking:{pick:'scouting',close:'idle'},\r\n scouting:{tile:'confirming',back:'picking',close:'idle'},\r\n confirming:{tile:'confirming',confirm:'dropping',back:'scouting',close:'idle'},\r\n dropping:{landed:'fighting',close:'idle'},\r\n fighting:{end:'result',close:'idle'},\r\n result:{again:'picking',close:'idle'},\r\n};\r\nexport const initialState=()=>({name:'idle',target:null,tile:null,result:null,banner:null});\r\nexport function step(s,ev,data={}){\r\n const to=TABLE[s.name]?.[ev];if(!to)return s; // illegal event: state unchanged\r\n const n={...s,name:to};\r\n if(ev==='pick'){n.target=data.target;n.tile=null;}\r\n if(ev==='tile')n.tile=data.tile;\r\n if(ev==='back'&&to==='scouting')n.tile=null;\r\n if(ev==='back'&&to==='picking')n.target=null;\r\n if(ev==='end'){n.result=data.result;n.banner=data.banner;}\r\n if(to==='picking'||to==='idle'){n.target=null;n.tile=null;if(ev!=='end'){n.result=null;n.banner=null;}}\r\n return n;\r\n}\r\n\r\n// ---------- targets ----------\r\n// Friend appearance DTO {publicId, base:{faction,buildings,...}, home?} -> render target. The DTO carries no seed and `home` is dropped on purpose.\r\nexport function friendTarget(f,i=0,kind='Friend'){\r\n try{\r\n  const dto=f?.base;if(!dto||typeof f.publicId!=='string')return null;\r\n  const b=newBase('raid-friend-'+f.publicId,0);b.faction=dto.faction;\r\n  for(const d of BUILDINGS){const x=dto.buildings?.[d.id];if(x&&Number.isInteger(x.level)&&Number.isInteger(x.x)&&Number.isInteger(x.y))Object.assign(b.buildings[d.id],{level:x.level,x:x.x,y:x.y,job:null});}\r\n  for(const [id,n] of Object.entries(dto.units||{}))if(Object.hasOwn(UNIT_BY_ID,id)&&UNIT_BY_ID[id].trainable&&Number.isSafeInteger(n)&&n>0&&n<=200)b.units[id]=n; // home units only (server DTO); defenderUnits turns them into the garrison\r\n  if(dto.rtsTech)b.rtsTech=structuredClone(dto.rtsTech);const base=normalizeBase(b);\r\n  return {id:'friend-'+f.publicId,rival:kind==='Rival',label:`${kind} ${i+1} / ${FACTION_BY_ID[base.faction]?.name||base.faction}`,tier:null,base};\r\n }catch{return null;}\r\n}\r\n// Difficulty pips = the garrison's towers (1-3). Thumbnail = 3 px per tile top-down map in faction colours (cheap 2D, no extra WebGL context).\r\nexport const pipsFor=base=>Math.max(1,Math.min(3,towerPoints(base).length));\r\nexport function raidBattleSource({raid,target,tile,attackers,base}){\r\n if(raid)return {input:structuredClone(raid.simInput),arena:arenaFromTile(raid.dropTile)};\r\n return buildRaid({drop:tile,base:target.base,tier:target.tier,attackers,ships:0,upgrades:attackerUpgrades(base),seed:seedFor(tile,target.id)});\r\n}\r\nfunction thumb(base){\r\n const c=document.createElement('canvas');c.width=c.height=72;const g=c.getContext('2d');g.fillStyle='#10161a';g.fillRect(0,0,72,72);\r\n for(const d of BUILDINGS){const b=base.buildings?.[d.id];if(!b||!(b.level>0))continue;g.fillStyle=d.kind==='tower'?'#7fe8ff':d.id==='hq'?'#ffffff':(FACTION_BY_ID[d.track]?.color||'#9aa6a2');g.fillRect((b.x??d.x)*3,(b.y??d.y)*3,d.w*3,d.h*3);}\r\n return c;\r\n}\r\nfunction card(t){\r\n const s=document.createElement('span'),n=pipsFor(t.base),p=document.createElement('span');p.className='cw-raid-pips';p.setAttribute('aria-label',`Difficulty ${n} of 3`);p.innerHTML='&#9632;'.repeat(n)+'<i>'+'&#9632;'.repeat(3-n)+'</i>';\r\n const sm=document.createElement('small');sm.textContent=(FACTION_BY_ID[t.base.faction]?.name||t.base.faction)+(t.tier?'':t.rival?' / rival':' / friend');\r\n if(t.growth){const age=document.createElement('small');age.textContent=t.growth.mappedAt===null?'Map your home to start weekly growth.':t.level>=t.maxLevel?'Current building level cap reached.':`Grows weekly; next level ${new Date(t.growth.nextGrowthAt).toLocaleDateString()}`;s.append(document.createTextNode(t.label),sm,age,p);}else s.append(document.createTextNode(t.label),sm,p);return s;\r\n}\r\nexport async function loadTargets(client,npcs=npcTargets()){\r\n let friends=[];\r\n try{const m=await client?.map?.();friends=[...(m?.friends||[]).slice(0,8).map(f=>friendTarget(f)),...(m?.rivals||[]).slice(0,8).map((f,i)=>friendTarget(f,i,'Rival'))].filter(Boolean);}catch{/* offline / preview: NPC bases only */}\r\n return [...friends,...npcs];\r\n}\r\n\r\n// ---------- controller ----------\r\nconst BEZEL=new URL('./ui/bezel-occupied.png',import.meta.url).href;\r\n// HUD pixel style: Pixelify headings, m5x7 numbers, square 2px-bordered buttons, family bezel under the sheet, faction colour accents (--cw-faction).\r\nconst CSS=`.cw-raid{--px:'CW Pixelify',ui-monospace,monospace;--num:'CW m5x7',ui-monospace,monospace;--cw-faction:#7fd0ff;position:absolute;inset:0;z-index:50;display:flex;flex-direction:column;background:#0b1014;color:#dfe8ee;font:16px/1.15 var(--num)}\r\n.cw-raid-top{display:flex;justify-content:space-between;align-items:stretch;background:#0d1217e6;border-bottom:2px solid var(--cw-faction);min-height:40px}.cw-raid-top b{font:400 16px var(--px);color:var(--cw-faction);padding:10px 10px;letter-spacing:1px}\r\n.cw-raid-stage{position:relative;flex:1;min-height:0;overflow:hidden;image-rendering:pixelated}\r\n.cw-raid-sheet{border:18px solid transparent;border-image:url(${BEZEL}) 24 fill/18px/0 stretch;image-rendering:pixelated;display:flex;flex-direction:column;gap:8px;max-height:52%;overflow:auto;margin-top:-6px;padding:0 2px}\r\n.cw-raid button{min-width:48px;min-height:48px;padding:0 12px;border-radius:0;background:#1b222a;border:2px solid #4a5662;color:#dfe8ee;font:16px/1 var(--num);image-rendering:pixelated}\r\n.cw-raid-top button{min-height:40px;background:#14181d}.cw-raid button.go{background:#3a3010;border-color:#f0c64a;color:#ffe28a;font-family:var(--px)}.cw-raid button:disabled{opacity:.45}\r\n.cw-raid-banner{font:400 32px var(--px);text-align:center;padding:6px;letter-spacing:3px;text-shadow:0 2px 0 #000}.cw-raid-banner.win{color:#7dff9a}.cw-raid-banner.lose{color:#ff7a6a}\r\n.cw-raid-row{display:flex;gap:8px;flex-wrap:wrap}.cw-raid-row button{flex:1}.cw-raid-list{display:flex;flex-direction:column;gap:6px}\r\n.cw-raid .cw-ab{position:relative;flex:0 0 56px;min-width:56px;border-radius:50%!important;border:0!important;background:conic-gradient(var(--cw-faction) var(--ab,360deg),#2a323a 0)!important;color:#fff;font:14px var(--num)}.cw-raid .cw-ab::before{content:'';position:absolute;inset:4px;border-radius:50%;background:#14181d;z-index:0}.cw-raid .cw-ab span{position:relative;z-index:1}.cw-raid .cw-ab.armed{outline:3px solid #ffe28a;animation:cw-ab 0.6s infinite alternate}@keyframes cw-ab{to{outline-offset:3px}}@media (prefers-reduced-motion:reduce){.cw-raid .cw-ab.armed{animation:none}}\r\n.cw-raid-hint{font:16px var(--num);color:#b8c4cc}\r\n.cw-raid-card{display:flex;align-items:center;gap:10px;text-align:left;border-left:6px solid var(--c)!important;padding:4px 8px!important}.cw-raid-card canvas{width:72px;height:72px;flex:0 0 72px;image-rendering:pixelated;border:2px solid #4a5662;background:#0b0f12}\r\n.cw-raid-card span{display:flex;flex-direction:column;gap:4px;font:400 14px var(--px)}.cw-raid-card small{font:16px var(--num);color:#b8c4cc}.cw-raid-pips{letter-spacing:3px;color:#f0c64a}.cw-raid-pips i{color:#4a5662;font-style:normal}`;\r\n\r\n// startRaid(panel,{client,getBase,sceneMode,signal,deps}) -> {dispose,state(),api}\r\n// deps (tests/dev): loadThree-less overrides {mountBase3d,mountFx3d,dropScene,poseSource,targets,onResult}.\r\nexport function startRaid(panel,{client,getBase,sceneMode,signal,deps={}}={}){\r\n const doc=panel.ownerDocument||document;\r\n {const href=new URL('./hud.css',import.meta.url).href;if(![...doc.querySelectorAll('link[data-cw-rts-hud]')].some(l=>l.href===href)){const l=doc.createElement('link');l.rel='stylesheet';l.href=href;l.dataset.cwRtsHud='';doc.head.append(l);}} // pixel fonts (@font-face)\r\n if(!doc.getElementById('cw-raid-css')){const st=doc.createElement('style');st.id='cw-raid-css';st.textContent=CSS;doc.head.append(st);}\r\n const root=doc.createElement('div');root.className='cw-raid';root.dataset.raid='';\r\n const top=doc.createElement('div');top.className='cw-raid-top';const title=doc.createElement('b');title.textContent='RAID';\r\n const closeBtn=btn('Close','x');top.append(title,closeBtn);\r\n const stage=doc.createElement('div');stage.className='cw-raid-stage';const sheet=doc.createElement('div');sheet.className='cw-raid-sheet';\r\n root.append(top,stage,sheet);if(getComputedStyle(panel).position==='static')panel.style.position='relative';panel.append(root);\r\n function btn(text,aria,cls){const b=doc.createElement('button');b.type='button';b.textContent=text;if(aria)b.setAttribute('aria-label',text||aria);if(cls)b.className=cls;return b;}\r\n\r\n let S=step(initialState(),'open'),disposed=false,run=null,raidStatus={checked:false,enabled:false,ticketAvailable:false,active:null,error:null,targets:null}; // run = per-target 3D session\r\n const dots=[]; // live troop dots for the crew billboards\r\n const set=(ev,data)=>{const n=step(S,ev,data);if(n===S)return false;const was=S.name;S=n;root.dataset.raidState=S.name;if(was!==S.name)render();return true;};\r\n const api={state:()=>S,pick:t=>set('pick',{target:t})&&enterScout(t),tap:t=>tapTile(t),confirm:()=>confirmDrop(),back:()=>{if(S.name==='scouting')leaveRun();else run?.marker?.removeFromParent(),run&&(run.marker=null);set('back');},close:()=>requestClose(),dots,get run(){return run;}};\r\n root.__raid=api;panel.__raid=api;root.dataset.raidState=S.name;\r\n\r\n // --- picker ---\r\n let targets=null;\r\n const rewardedTarget=t=>!!client?.authenticated&&raidStatus.enabled&&raidStatus.ticketAvailable&&!raidStatus.active&&!!t?.id?.startsWith('npc-');\r\n async function refreshRaidStatus(){\r\n  if(!client?.authenticated||!client?.raidCurrent){raidStatus={checked:true,enabled:false,ticketAvailable:false,active:null,error:null};return raidStatus;}\r\n  try{const dto=await client.raidCurrent();raidStatus={checked:true,enabled:true,ticketAvailable:dto.ticketAvailable===true,active:dto.active||null,error:null,targets:Array.isArray(dto.targets)?dto.targets:null};targets=null;}\r\n  catch(error){if(error?.status===404||error?.code==='world_route_not_found')raidStatus={checked:true,enabled:false,ticketAvailable:false,active:null,error:null};else raidStatus={checked:true,enabled:true,ticketAvailable:false,active:null,error};}\r\n  if(!disposed){if(S.name==='picking')renderPicker();else if(S.name==='scouting')renderScout();}return raidStatus;\r\n }\r\n function modeNote(){\r\n  const note=doc.createElement('div');note.className='cw-raid-hint';note.dataset.raidMode=raidStatus.active?'rewarded':client?.authenticated&&!raidStatus.checked?'checking':client?.authenticated&&raidStatus.enabled&&raidStatus.ticketAvailable?'rewarded':'practice';\r\n  if(client?.authenticated&&!raidStatus.checked){note.textContent='Checking server raid ticket…';return note;}\r\n  note.textContent=raidStatus.active?'A rewarded raid is saved. Resume it or abandon it before starting another.':raidStatus.error?'Could not check raid ticket. Retry before starting an NPC raid.':client?.authenticated&&raidStatus.enabled&&raidStatus.ticketAvailable?'Rewarded NPC raid · 1 earned ticket':'Practice raid — no rewards';return note;\r\n }\r\n function renderPicker(){\r\n  sheet.replaceChildren();stage.replaceChildren();title.textContent='RAID / pick a target';\r\n  sheet.append(modeNote());\r\n  if(raidStatus.error){const retry=btn('Retry raid status');retry.dataset.raidRetry='';retry.onclick=()=>void refreshRaidStatus();sheet.append(retry);}\r\n  if(raidStatus.active){const resume=btn('Resume saved raid');resume.dataset.raidResume='';resume.onclick=()=>void resumeSavedRaid(raidStatus.active);const abandon=btn('Abandon saved raid');abandon.dataset.raidAbandon='';abandon.onclick=()=>void abandonSavedRaid();sheet.append(resume,abandon);}\r\n  const list=doc.createElement('div');list.className='cw-raid-list';sheet.append(list);\r\n  if(client?.authenticated&&!raidStatus.checked&&!deps.targets){list.textContent='Checking NPC base levels...';return;}\r\n  const show=ts=>{list.replaceChildren();for(const t of ts){const b=btn('',t.label);b.className='cw-raid-card';b.dataset.target=t.id;b.disabled=!!raidStatus.active&&t.id.startsWith('npc-');b.style.setProperty('--c',FACTION_BY_ID[t.base.faction]?.color||'#9aa6a2');\r\n   b.append(thumb(t.base),card(t));b.onclick=()=>api.pick(t);list.append(b);}};\r\n  if(targets)show(targets);else{list.textContent='Scanning for bases...';(deps.targets?Promise.resolve(deps.targets()):loadTargets(client,raidStatus.targets??(raidStatus.error?[]:npcTargets()))).then(ts=>{targets=ts;if(!disposed&&S.name==='picking')show(ts);});}\r\n }\r\n async function abandonSavedRaid(){\r\n  if(!raidStatus.active||!client?.raidAbandon)return;const b=sheet.querySelector('[data-raid-abandon]');if(b)b.disabled=true;\r\n  try{const result=await client.raidAbandon(raidStatus.active.id);if(result.raid?.status==='active')throw Error('Raid is still active. Retry abandon.');raidStatus.active=null;await refreshRaidStatus();}\r\n  catch(error){raidStatus.error=error;if(!disposed&&S.name==='picking')renderPicker();}\r\n }\r\n async function resumeSavedRaid(raid){\r\n  const target=raid.target??npcTargets().find(t=>t.id===raid.targetId);if(!target||!raid.simInput||!raid.dropTile){raidStatus.error=Error('Saved raid snapshot is incomplete. Retry status or abandon it.');renderPicker();return;}\r\n  const pose=deps.poseSource||createPoseSource(),permission=pose.requestPermission();permission.then(p=>pose.begin(p));\r\n  if(!set('pick',{target}))return;await enterScout(target);if(!run||disposed)return;\r\n  run.rewardedRaid=raid;run.pose=pose;run.deploy={attackers:raid.simInput.rosters?.[0]?.u||'',selectedUnitIDs:raid.selectedUnitIDs||[],troops:raid.selectedUnitIDs?.length||0,ships:0};\r\n  if(!tapTile(raid.dropTile)||!set('confirm'))return;await playPinnedDrop(raid,pose);\r\n }\r\n\r\n // --- scouting / drop ---\r\n async function enterScout(target){\r\n  title.textContent='RAID / '+target.label;stage.replaceChildren();\r\n  const rts={getUnitDots:()=>dots}; // the crew billboards draw the battle units\r\n  const mod=deps.mountBase3d?{mountBase3d:deps.mountBase3d}:await import('./base3d.mjs');if(disposed||S.name!=='scouting')return;\r\n  const fxmod=deps.mountFx3d?{mountFx3d:deps.mountFx3d}:await import('./fx3d.mjs').catch(()=>null);if(disposed||S.name!=='scouting')return;\r\n  const tapper={tapTile:t=>tapTile(t),selectTiles(){},selectedId:()=>null,getSelectedUnits:()=>[]};\r\n  root.style.setProperty('--cw-faction',FACTION_BY_ID[target.base.faction]?.color||'#7fd0ff');\r\n  const h=mod.mountBase3d(stage,{getBase:()=>target.base,sceneMode:tapper,rts,signal,crewScale:2}); // troops x2: tiny from the pod cam otherwise\r\n  run={target,handle:h,stopFx:fxmod?.mountFx3d?.(h,{cap:10}) /* few simultaneous effects: a wall of particles hides the fight */,marker:null,drop:null,toggle:null,pb:null,off:null,pose:null,rewardedRaid:null,startBody:null};\r\n  renderScout();\r\n  h.ready.then(r=>{if(!r||run?.handle!==h)return;run.r=r;for(const o of r.world.children)if(o.geometry?.type==='IcosahedronGeometry')o.visible=false;}); // the target's ore node is not part of a raid\r\n }\r\n function leaveRun(keepNothing=true){\r\n  if(!run)return;try{run.off?.();run.toggle?.dispose();run.drop?.dispose();run.pose?.dispose?.();run.stopFx?.();run.handle.dispose();}catch(e){console.warn('raid dispose',e);}\r\n  run=null;dots.length=0;stage.replaceChildren();\r\n }\r\n function renderScout(){\r\n  sheet.replaceChildren();const msg=doc.createElement('div');msg.dataset.raidHint='';msg.className='cw-raid-hint';sheet.append(msg);\r\n  const mode=modeNote();mode.dataset.raidMode=rewardedTarget(run.target)?'rewarded':'practice';mode.textContent=raidStatus.error&&client?.authenticated&&run.target.id.startsWith('npc-')?'Could not check ticket. Retry raid status before confirming.':raidStatus.active&&run.target.id.startsWith('npc-')?'A saved rewarded raid exists. Resume or abandon it first.':rewardedTarget(run.target)?'Rewarded NPC raid · ticket consumed only after server confirms start':'Practice raid — no rewards';sheet.append(mode);\r\n  if(raidStatus.error&&client?.authenticated&&run.target.id.startsWith('npc-')){const retryStatus=btn('Retry raid status');retryStatus.dataset.raidRetry='';retryStatus.onclick=()=>void refreshRaidStatus();sheet.append(retryStatus);}\r\n  const plan=deployPlan(),deploy=doc.createElement('div');deploy.className='cw-raid-hint';deploy.dataset.raidDeploy='';\r\n  deploy.textContent=plan.attackers?`Deploying ${plan.troops} troop${plan.troops===1?'':'s'} + ${plan.ships} ship${plan.ships===1?'':'s'}.`:'No troops selected.';\r\n  sheet.append(deploy);\r\n  if(!plan.attackers&&!run.rewardedRaid){const choose=btn('Choose troops');choose.dataset.raidChooseTroops='';choose.onclick=()=>{dispose();sceneMode?.selectTroops?.();};sheet.append(choose);}\r\n  const row=doc.createElement('div');row.className='cw-raid-row';const back=btn('Back'),go=btn('DROP POD HERE',null,'go');go.dataset.raidDrop='';go.disabled=true;\r\n  back.onclick=()=>api.back();go.onclick=()=>confirmDrop();row.append(back,go);sheet.append(row);\r\n  msg.textContent=!plan.attackers?'Select troops before raiding.':S.tile?`Drop at ${S.tile.x},${S.tile.y}. Tap another tile to move it.`:'Tap the tile where the Commander pod lands. Troops deploy around it.';\r\n  const canReward=rewardedTarget(run.target),selectedIds=selectedUnitIDs();go.disabled=!S.tile||(!plan.attackers&&!canReward)||client?.authenticated&&run.target.id.startsWith('npc-')&&!raidStatus.checked||!!raidStatus.error&&client?.authenticated&&run.target.id.startsWith('npc-')||!!raidStatus.active&&run.target.id.startsWith('npc-')&&!run.rewardedRaid||canReward&&!selectedIds.length;\r\n }\r\n function tapTile(t){\r\n  if(run?.aiming&&S.name==='fighting')return castAt(t);\r\n  if(!run||!(S.name==='scouting'||S.name==='confirming'))return false;\r\n  const tile={x:Math.floor(t.x),y:Math.floor(t.y)},v=validDrop(run.target.base,tile);\r\n  if(!v.ok){const m=sheet.querySelector('[data-raid-hint]');if(m)m.textContent=v.reason+'. Pick open ground.';return false;}\r\n  set('tile',{tile});moveMarker(tile);renderScout();return true;\r\n }\r\n function moveMarker(tile){\r\n  const r=run?.r;if(!r)return;const {T}=r;\r\n  if(!run.marker){const g=new T.Group(),ring=new T.Mesh(new T.RingGeometry(.7,1,24),new T.MeshBasicMaterial({color:0xff5a3c,side:T.DoubleSide,depthTest:false,fog:false,transparent:true}));\r\n   ring.rotation.x=-Math.PI/2;ring.renderOrder=5;const beam=new T.Mesh(new T.CylinderGeometry(.08,.08,6,6),new T.MeshBasicMaterial({color:0xff9a7a,transparent:true,opacity:.6,depthTest:false,fog:false}));beam.position.y=3;beam.renderOrder=5;g.add(ring,beam);run.marker=g;run.handle.addToWorld(g);}\r\n  const sp=spawnTile(); // base3d world: metres from the spawn tile, +z = tile +y\r\n  run.marker.position.set((tile.x+.5-sp.x)*SCALE,.15,(tile.y+.5-sp.y)*SCALE);run.marker.scale.setScalar(SCALE*.9);\r\n }\r\n\r\n function selectedUnitIDs(){\r\n  const units=fightCrew(sceneMode?.getSelectedUnits?.()||[],getBase?.());return [...new Set(units.map(u=>u?.instanceId).filter(id=>typeof id==='string'&&id.length))];\r\n }\r\n function requestPoseInTap(){const pose=deps.poseSource||createPoseSource();run.pose=pose;pose.requestPermission().then(p=>pose.begin(p));return pose;}\r\n async function confirmDrop(){\r\n  if(!run||!S.tile)return false;\r\n  const plan=deployPlan(),rewarded=rewardedTarget(run.target),selected=selectedUnitIDs();\r\n  if(client?.authenticated&&run.target.id.startsWith('npc-')&&(!raidStatus.checked||raidStatus.error)){const m=sheet.querySelector('[data-raid-hint]');if(m)m.textContent='Could not check ticket. Retry raid status before confirming.';return false;}\r\n  if(raidStatus.active&&run.target.id.startsWith('npc-')&&!run.rewardedRaid){const m=sheet.querySelector('[data-raid-hint]');if(m)m.textContent='A saved rewarded raid exists. Resume or abandon it first.';return false;}\r\n  if(rewarded&&!selected.length){const m=sheet.querySelector('[data-raid-hint]');if(m)m.textContent='Select an available worker or companion before starting a rewarded raid.';return false;}\r\n  if(!rewarded&&!plan.attackers){renderScout();return false;}\r\n  // Sensor permission runs in this tap, before the network await.\r\n  const pose=run.pose||requestPoseInTap();let pinned=null;\r\n  if(rewarded){\r\n   run.startBody??={id:crypto.randomUUID(),targetId:run.target.id,selectedUnitIDs:selected,dropTile:{...S.tile}};\r\n   run.starting=true;closeBtn.disabled=true;\r\n   try{const started=await client.raidStart(run.startBody);pinned=started.raid;if(pinned?.target)Object.assign(run.target,pinned.target);run.rewardedRaid=pinned;raidStatus.active=pinned;raidStatus.ticketAvailable=started.ticketAvailable===true;if(!pinned?.simInput||pinned.simInput.rulesVersion!==6||pinned.targetId!==run.target.id||pinned.dropTile?.x!==S.tile.x||pinned.dropTile?.y!==S.tile.y)throw Error('Server raid snapshot is incomplete. Reload the active raid before continuing.');}\r\n   catch(error){const m=sheet.querySelector('[data-raid-hint]');if(m)m.textContent=`Raid start was not confirmed: ${error.message||error}. Retry this drop; no fight started.`;if(error?.status>=400&&error.status<500)run.startBody=null;return false;}\r\n   finally{if(run){run.starting=false;closeBtn.disabled=false;}}\r\n   if(run.closeRequested)return false;\r\n   run.deploy={selectedUnitIDs:[...(pinned.selectedUnitIDs||[])],attackers:pinned.simInput.rosters?.[0]?.u||'',troops:pinned.selectedUnitIDs?.length||0,ships:[...(pinned.simInput.rosters?.[0]?.u||'')].filter(c=>c==='a').length};\r\n  }else run.deploy=plan;\r\n  if(!set('confirm'))return false;\r\n  return playPinnedDrop(pinned,pose);\r\n }\r\n async function playPinnedDrop(raid,pose=run?.pose){\r\n  if(!run||disposed)return false;const {handle}=run,tile=raid?.dropTile||S.tile;run.pose=pose||run.pose;\r\n  title.textContent='RAID / drop in progress';renderFight();run.marker?.removeFromParent();\r\n  const scene=deps.dropScene||await import('./drop-pod-scene.mjs');if(!run||disposed)return false;\r\n  const rr=run.r||await handle.ready;if(!rr||!run)return false;run.r=rr;\r\n  run.drop=scene.playDrop(rr.T,rr.world,{tile,scale:SCALE,onLanded:()=>landed(scene,rr)});return true;\r\n }\r\n // R2-C2: the wander crew pads to 7 workers; a raid only fields the real ones\r\n function fightCrew(units,b){let w=Number(b?.workers)||0;return units.filter(u=>u?.role!=='worker'||w-->0);}\r\n // The player's own deploy: selected crew (workers capped as in fightCrew) -> roster with NO practice fallback, plus real hangar ships.\r\n // Haulers never fight: a hauler-only selection is an empty roster (rosterFromUnits fallback ''). Ships alone can still deploy.\r\n function deployPlan(){\r\n  const units=fightCrew(sceneMode?.getSelectedUnits?.()||[],getBase?.());\r\n  const roster=rosterFromUnits(units,'');\r\n  const attackers=withShips(roster,raidShips(getBase?.()));\r\n  return {units:units.length,troops:roster.length,ships:[...attackers].filter(c=>c==='a').length,attackers};\r\n }\r\n function renderSaving(error){\r\n  if(!run)return;title.textContent='RAID / saving result';sheet.replaceChildren();const msg=doc.createElement('div');msg.className='cw-raid-hint';msg.dataset.raidSaving='';msg.textContent=error?`Saving could not be confirmed: ${error.message||error}. Retry the same raid.`:'Saving server result…';sheet.append(msg);\r\n  const row=doc.createElement('div');row.className='cw-raid-row';const retry=btn('Retry save');retry.dataset.raidSaveRetry='';retry.onclick=()=>void settleRewarded(run.pendingEnd.result,run.pendingEnd.banner);const close=btn('Close');close.onclick=()=>void requestClose();row.append(retry,close);sheet.append(row);\r\n }\r\n async function settleRewarded(result,banner){\r\n  if(!run?.rewardedRaid||run.settling)return false;run.settling=true;\r\n  run.pendingEnd??={result,banner};run.completeAbilityInputs??=(run.input?.abilities||[]).map(a=>({tick:a.tick,x:a.x,y:a.y}));renderSaving(null);\r\n  try{\r\n   const saved=await client.raidComplete(run.rewardedRaid.id,run.completeAbilityInputs),raid=saved.raid;\r\n   if(!raid||raid.id!==run.rewardedRaid.id||!['won','lost'].includes(raid.status)||!raid.result)throw Error('Server has not returned a terminal raid result yet.');\r\n   run.serverRaid=raid;run.entitlementRefreshPending=saved.entitlementRefreshPending===true;run.rewardedRaid=raid;run.settling=false;\r\n   if(S.name==='fighting')set('end',{result:raid.result,banner:raid.status==='won'?'VICTORY':'DEFEAT'});return true;\r\n  }catch(error){run.settling=false;renderSaving(error);return false;}\r\n }\r\n function landed(scene,rr){\r\n  if(!run||S.name!=='dropping')return;set('landed');\r\n  const {handle,target}=run,tile=S.tile,attackers=run.deploy.attackers;\r\n  const {input,arena}=raidBattleSource({raid:run.rewardedRaid,target,tile,attackers,base:getBase?.()}); // rewarded battles replay only the server-pinned v6 snapshot\r\n  const sim=simulate(input);run.input=input;run.arena=arena;\r\n  const unitsNow=()=>run?.pb?.units()||[];\r\n  const worldOf=u=>{const w=arena.toWorld(u.x,u.y);return {x:w.x-.5,y:w.y-.5};};\r\n  const actions=new Map(),facilityFired=new Map();\r\n  const facilityFx=a=>{if(!a.source||!run)return;const nowMs=performance.now(),last=facilityFired.get(a.source.id)??-Infinity;if(nowMs-last<400)return;const def=BUILDINGS.find(d=>d.id===a.source.id),site=target.base.buildings?.[a.source.id],unit=unitsNow().find(u=>u.id===a.b);if(!def||!site||!unit)return;facilityFired.set(a.source.id,nowMs);const from={x:site.x+def.w/2-.5,y:site.y+def.h/2-.5},element={press:'chest',mortar:'quads',trap:'glutes',beacon:'cardio'}[a.source.kind];if(element)handle.fx3d?.fire(from,worldOf(unit),element);};\r\n  const pb=createPlayback(sim,{roster:rosterMap(input.rosters),speed:1,windup:{g:168,s:168,b:168,h:168},onAction:a=>{if(a.kind==='attack'||a.kind==='death')actions.set(a.u,a);else if(a.kind==='hit'||a.kind==='zap')actions.set(a.b,{...a,kind:'hit'});facilityFx(a);},onHit:a=>{\r\n    const us=unitsNow(),A=us.find(u=>u.id===a.u),B=us.find(u=>u.id===a.b);if(!A||!B)return;\r\n    const nowMs=performance.now();if(nowMs-(run.lastFx||0)<140)return;run.lastFx=nowMs; // thin the particles: one effect per 140 ms\r\n    handle.fx3d?.fire(worldOf(A),worldOf(B),A.role==='t'?'tower':pb.factionOf(A.id));},\r\n   onEnd:(res,banner)=>{\r\n    if(run?.rewardedRaid){void settleRewarded(res,banner);return;}\r\n    deps.onResult?.({input:run?.input||input,result:res,banner,target:target.id});if(S.name==='fighting')set('end',{result:res,banner});}});\r\n  run.pb=pb;\r\n  // aim the pod cam at the first strike (where the fight starts); the garrison side if nobody ever hits\r\n  const first=sim.events.find(e=>e.k==='hit'),vt=first&&sim.trace[Math.min(first.t,sim.trace.length-1)],vw=first?arena.toWorld(vt[first.b*3]/256,vt[first.b*3+1]/256):null;\r\n  const aim=vw?[vw.x-(tile.x+.5),vw.y-(tile.y+.5)]:arena.ex;\r\n  run.off=handle.onFrame(t=>{pb.step(t);updateAb();dots.length=0;\r\n   for(const u of pb.units()){if(u.role==='t')continue;const anim=raidClip(actions.get(u.id),pb.playMs+pb.offset);if(u.dead&&anim?.clip!=='death')continue;const w=worldOf(u),look=raidAppearance(u.role,u.side?target.base:getBase?.());dots.push({id:'raid-'+u.id,x:w.x,y:w.y,...look,...anim,level:1,color:iced(u)?'#bfeaff':u.side?'#e0603a':'#3aa0e0'});}\r\n   if(!run.faced&&run.pose&&run.pose.mode==='drag'){run.faced=true; // turn the head toward the garrison once: drag-look yaw is world-space, base3d's world is rotated\r\n    const e=new rr.T.Vector3(aim[0],0,aim[1]).applyQuaternion(rr.world.quaternion),psi=Math.atan2(-e.x,-e.z);run.pose.drag(-psi/.005,70);}});\r\n  // pod cam: eye 4*SCALE up (podCam's height is 2.5*scale, so scale*1.6), pulled back 2 m, looking ~20 deg down, near plane out past tower geometry\r\n  run.toggle=(scene.createCamToggle)(handle,rr.camera,run.pose,{el:stage.querySelector('canvas'),scale:SCALE*1.6});run.toggle.set('pod');\r\n  const inner=handle.cameraOverride;rr.camera.near=.8;rr.camera.updateProjectionMatrix();\r\n  if(inner)handle.cameraOverride=c=>{inner(c);c.translateZ(1.3*SCALE);c.updateMatrixWorld?.();};\r\n  renderFight();\r\n }\r\n\r\n // --- fight / result ---\r\n function renderFight(){\r\n  title.textContent=S.name==='dropping'?'RAID / pod inbound':'RAID / battle';sheet.replaceChildren();\r\n  if(S.name==='result'){const rewarded=!!run?.serverRaid,note=doc.createElement('div');note.className='cw-raid-hint';note.dataset.raidMode=rewarded?'rewarded':'practice';note.textContent=rewarded?(run.serverRaid.status==='won'?`Server confirmed · cosmetic pack ${run.serverRaid.packId}`:'Server confirmed · no reward pack'):'Practice raid — no rewards';const b=doc.createElement('div');b.className='cw-raid-banner '+(S.banner==='VICTORY'?'win':S.banner==='DEFEAT'?'lose':'');b.dataset.raidBanner='';b.textContent=S.banner;const row=doc.createElement('div');row.className='cw-raid-row';\r\n   const again=btn('Raid again');again.onclick=async()=>{if(rewarded)await refreshRaidStatus();leaveRun();set('again');};const done=btn('Done');done.onclick=()=>void requestClose();row.append(again,done);sheet.append(note,b,row);if(run?.entitlementRefreshPending){const pending=doc.createElement('div');pending.className='cw-raid-hint';pending.textContent='Reward saved. Account entitlements will refresh when you reconnect.';sheet.append(pending);}return;}\r\n  const row=doc.createElement('div');row.className='cw-raid-row';\r\n  const view=btn('Pod / Top view');view.onclick=()=>run?.toggle?.toggle();\r\n  const spd=btn('Speed 1x');spd.dataset.raidSpeed='';spd.onclick=()=>{const pb=run?.pb;if(!pb)return;const s=pb.speed===1?4:1;pb.setSpeed(s);spd.textContent=`Speed ${s}x`;};\r\n  const recall=btn('Recall');recall.onclick=()=>void requestClose();\r\n  const ab=btn('','Fire cannon','cw-ab');ab.dataset.raidCannon='';ab.innerHTML='<span></span>';ab.onclick=()=>arm();if(run)run.abBtn=ab;\r\n  row.append(ab,view,spd,recall);sheet.append(row);updateAb();\r\n }\r\n // --- v4 cooldown cannon: tap the button, then tap the ground. The cast is an input event {tick,faction,x,y}; the sim is re-run and playback swaps to it.\r\n // units inside a live yoga frost zone look icy\r\n const iced=u=>{const tk=run.pb.tick;return (run.input.abilities||[]).some(a=>a.faction==='yoga'&&tk>=a.tick&&tk<a.tick+ABILITIES.yoga.dur&&u.side!==(a.side??0)&&(u.x*256-a.x)**2+(u.y*256-a.y)**2<=ABILITY_RADIUS**2);};\r\n const myFaction=()=>run?.rewardedRaid?.playerFaction||FACTION_BY_ID[getBase?.()?.faction]?.id||'chest';\r\n function abState(){const pb=run?.pb;return pb&&run.input?abilityState(run.input.abilities,0,pb.tick+ABILITY_LEAD):null;}\r\n function updateAb(){\r\n  const b=run?.abBtn,st=abState();if(!b||!st)return;\r\n  const live=S.name==='fighting'&&!run.pb.over,sp=b.firstChild;\r\n  b.style.setProperty('--ab',(st.cooldown?(1-st.cooldown/ABILITY_COOLDOWN)*360:360)+'deg');\r\n  const txt=st.charges<=0?'0':st.cooldown?Math.ceil(st.cooldown/10)+'s':'x'+st.charges;if(sp.textContent!==txt)sp.textContent=txt;\r\n  b.disabled=!live||!st.ready;b.classList.toggle('armed',!!run.aiming);\r\n  b.title=ABILITIES[myFaction()].name;\r\n }\r\n function arm(){\r\n  if(!run||S.name!=='fighting')return false;const st=abState();if(!st?.ready){run.aiming=false;updateAb();return false;}\r\n  run.aiming=!run.aiming;const m=sheet.querySelector('[data-raid-hint]');if(m)m.remove();\r\n  if(run.aiming){const h=doc.createElement('div');h.className='cw-raid-hint';h.dataset.raidHint='';h.textContent=ABILITIES[myFaction()].name+': tap the ground to fire.';sheet.prepend(h);}\r\n  updateAb();return true;\r\n }\r\n // t: base-grid tile {x,y} from base3d. Returns true when fired.\r\n function castAt(t){\r\n  const st=abState();if(!run||!st?.ready){return false;}\r\n  const pb=run.pb,arena=run.arena,a=arena.toArena(t.x+.5,t.y+.5),clamp=(v,hi)=>Math.min(hi,Math.max(0,Math.round(v)));\r\n  const ev={tick:pb.tick+ABILITY_LEAD,faction:myFaction(),x:clamp(a.x*256,W),y:clamp(a.y*256,H)};\r\n  const input={...run.input,abilities:[...(run.input.abilities||[]),ev]};\r\n  let sim;try{sim=simulate(input);}catch(e){console.warn('cannon rejected',e);return false;}\r\n  run.input=input;pb.swap(sim);run.aiming=false;sheet.querySelector('[data-raid-hint]')?.remove();\r\n  const rr=run.r,to=arena.toWorld(ev.x/256,ev.y/256),drop=S.tile;\r\n  if(rr)fireCannon(rr.T,rr.world,{from:{x:drop.x+.5,y:drop.y+.5},to,faction:ev.faction,radius:ABILITY_RADIUS/256*arena.k,flightMs:ABILITY_LEAD*100/pb.speed,onFrame:run.handle.onFrame});\r\n  updateAb();return true;\r\n }\r\n api.cannon={arm,cast:castAt,state:abState};\r\n function render(){\r\n  if(S.name==='picking'){leaveRun();renderPicker();}\r\n  else if(S.name==='scouting'&&run)renderScout();\r\n  else if(S.name==='dropping'||S.name==='fighting'||S.name==='result')renderFight();\r\n }\r\n function dispose(){\r\n  if(disposed)return;disposed=true;set('close');leaveRun();root.remove();if(panel.__raid===api)delete panel.__raid;signal?.removeEventListener('abort',dispose);\r\n }\r\n function renderClosePending(error){\r\n  if(!run&&!raidStatus.active)return;title.textContent='RAID / saving close';sheet.replaceChildren();const msg=doc.createElement('div');msg.className='cw-raid-hint';msg.textContent=`Abandon could not be confirmed: ${error?.message||error}. The saved raid remains pinned.`;sheet.append(msg);\r\n  const retry=btn('Retry close');retry.dataset.raidAbandonRetry='';retry.onclick=()=>void requestClose();const stay=btn('Return to raid');stay.onclick=()=>{if(!run)renderPicker();else if(S.name==='fighting'||S.name==='dropping')renderFight();else renderScout();};sheet.append(retry,stay);\r\n }\r\n async function requestClose(){\r\n  if(run?.startBody&&!run.rewardedRaid){run.closeRequested=true;try{const started=await client.raidStart(run.startBody);if(!started.raid?.id||started.raid.status!=='active')throw Error('Server did not return the pinned active raid.');run.rewardedRaid=started.raid;raidStatus.active=started.raid;}\r\n   catch(error){if(error?.status>=400&&error.status<500){run.startBody=null;dispose();return true;}renderClosePending(error);return false;}}\r\n  const active=run?.rewardedRaid?.status==='active'?run.rewardedRaid:raidStatus.active;\r\n  if(active&&client?.raidAbandon){\r\n   try{\r\n    const result=await client.raidAbandon(active.id);if(result.raid?.status==='active')throw Error('Server still reports an active raid.');\r\n    if(result.raid?.status==='won'||result.raid?.status==='lost'){\r\n     if(run?.completeAbilityInputs){const completed=await client.raidComplete(active.id,run.completeAbilityInputs);run.serverRaid=completed.raid;run.entitlementRefreshPending=completed.entitlementRefreshPending===true;}\r\n    }\r\n    if(run)run.rewardedRaid=result.raid;raidStatus.active=null;dispose();return true;\r\n   }catch(error){renderClosePending(error);return false;}\r\n  }\r\n  dispose();return true;\r\n }\r\n closeBtn.onclick=()=>void requestClose();signal?.addEventListener('abort',dispose,{once:true});\r\n render();void refreshRaidStatus();\r\n return {dispose,state:()=>S,api};\r\n}\r\n","modules/coach-world-rts/resource-nodes.mjs":"// WS-3: one cosmetic resource node per built hall (materials still come only from workouts), shaped + coloured per faction.\r\nimport {occupancy} from '../coach-world/navigation.mjs';\r\nimport {FACTIONS,FACTION_BY_ID as FB} from '../coach-world/catalog.mjs';\r\nimport {placeNodes} from '../coach-world/resource-placement.mjs';\nexport {placeNodes};\r\nimport {economyState} from '../coach-world/rts-economy.mjs';\r\n\r\n// 3D: tile (x,y) -> world ((x+.5-origin.x)*scale, ., (y+.5-origin.y)*scale), same as base3d pickTile\r\n// biomes = the biomes.mjs namespace ({NODE_SHAPES,shapeGeometry}), injected by base3d (optional-sibling import) so mining.mjs never depends on it\r\n// Per place: a faction-shaped mineral formation (`<faction>:minerals`) and a dark vent with a teal gas plume (`<faction>:gas`), so the two deposits read apart.\r\n// Exhausted deposits (rtsEconomy) go grey/inert. pick(ray) takes a ray in this group's space and returns {faction,kind} | null.\r\nconst GAS=0x3df0b0,DEAD=0x4a4a46;\r\nexport function createNodes3d(T,{biomes,getBase,scale=1.6,origin={x:12,y:12},heightAt=()=>0,ground=null}={}){ // ground(x,y) = ready-made height in metres (terrain3d relief); else heightAt tiles * .35 * scale\r\n const {NODE_SHAPES,shapeGeometry}=biomes,group=new T.Group(),meshes=new Map();\r\n let geos=[],mats=[],key='',baseKey='',places=[];\r\n const at=(o,n)=>o.position.set((n.x+.5-origin.x)*scale,(ground?ground(n.x,n.y):heightAt(n.x,n.y)*.35*scale)+.3*scale,(n.y+.5-origin.y)*scale);\r\n const clear=()=>{for(const o of meshes.values())group.remove(o);meshes.clear();for(const g of geos)g.dispose();for(const m of mats)m.dispose();geos=[];mats=[];};\r\n const sync=()=>{\r\n  const b=getBase()||{},bk=`${b.faction}|`+Object.values(b.buildings||{}).map(x=>`${x.id}:${x.x}:${x.y}:${x.level}`).join();\r\n  if(bk===baseKey)return;baseKey=bk;\r\n  places=placeNodes(b);const k=JSON.stringify(places.map(p=>[p.faction,p.node.x,p.node.y,p.gas?.node.x,p.gas?.node.y]));\r\n  if(k===key)return;key=k;clear();\r\n  for(const p of places){\r\n   const s=NODE_SHAPES[p.faction];if(!s)continue;\r\n   const g=shapeGeometry(T,s.shape,.45*scale),color=new T.Color(FB[p.faction].color),m=new T.MeshStandardMaterial({color,emissive:color,emissiveIntensity:.3,flatShading:true,roughness:.6});\r\n   geos.push(g);mats.push(m);\r\n   const o=new T.Mesh(g,m);at(o,p.node);o.userData={faction:p.faction,kind:'minerals',color};group.add(o);meshes.set(`${p.faction}:minerals`,o);\r\n   if(!p.gas)continue;\r\n   const rg=new T.CylinderGeometry(.42*scale,.55*scale,.28*scale,7),pg=new T.ConeGeometry(.3*scale,1.1*scale,6,1,true),rm=new T.MeshStandardMaterial({color:0x2b3b3a,flatShading:true,roughness:.9}),pm=new T.MeshStandardMaterial({color:GAS,emissive:GAS,emissiveIntensity:.8,transparent:true,opacity:.65,flatShading:true,depthWrite:false,side:T.DoubleSide});\r\n   geos.push(rg,pg);mats.push(rm,pm);\r\n   const vent=new T.Group(),rock=new T.Mesh(rg,rm),plume=new T.Mesh(pg,pm);plume.position.y=.65*scale;vent.add(rock,plume);at(vent,p.gas.node);vent.userData={faction:p.faction,kind:'gas',plume};group.add(vent);meshes.set(`${p.faction}:gas`,vent);\r\n  }\r\n };\r\n sync();\r\n return {group,meshes,get places(){return places;},\r\n  update(t=0){sync();let i=0,e=null;try{e=economyState(getBase()).nodes;}catch{}\r\n   for(const o of meshes.values()){const u=o.userData,x=!!e&&e[u.faction][u.kind]<=0;u.dead=x;i++;\r\n    if(u.kind==='gas'){u.plume.visible=!x;u.plume.scale.y=1+.25*Math.sin(t*3+i);u.plume.material.opacity=.5+.2*Math.sin(t*2+i);continue;}\r\n    o.material.color.set(x?DEAD:u.color);o.material.emissive.set(x?0:u.color);o.material.emissiveIntensity=x?0:.3+.2*Math.sin(t*2+i);o.rotation.y=x?0:t*.3+i;}},\r\n  pick(ray){let best=null,bd=1e9;for(const o of meshes.values()){const hit=ray.intersectSphere(new T.Sphere(o.position,.9*scale),new T.Vector3());if(hit){const d=hit.distanceTo(ray.origin);if(d<bd){bd=d;best={faction:o.userData.faction,kind:o.userData.kind};}}}return best;},\r\n  dispose(){clear();group.removeFromParent();}};\r\n}\r\n","modules/coach-world-rts/retro-palette.mjs":"// V9 retro palette (?retro=1): OUR 256-colour palette, gathered from our own painted art (V7 ground atlas, V8 herd sheet, every faction\r\n// colour in biomes.mjs, V3's planets) plus a grey ramp and a hue ring, cut to 256 by farthest-point selection in OKLab. Never Daggerfall's\r\n// ART_PAL. A 32^3 lookup (laid out as a 1024x32 strip, x = b*32 + r, y = g) maps every display colour to its nearest palette entry in OKLab,\r\n// lightness weighed below hue as DFO's retroPass does; built a slice at a time (DFO RETRO_LUT_BUDGET_MS) so it never stalls a frame.\r\nimport {GROUND_PALETTES} from './ground-atlas.mjs';\r\nimport {paintHerdAtlas} from './herd-atlas.mjs';\r\nimport {BIOMES} from './biomes.mjs';\r\nimport {PLANETS} from './sky-alien.mjs';\r\nexport const PALETTE_SIZE=256,LUT_SIZE=32,LUT_BUDGET_MS=3;\r\nconst lin=c=>c<=.04045?c/12.92:((c+.055)/1.055)**2.4;\r\nexport function oklab([r,g,b]){r=lin(r/255);g=lin(g/255);b=lin(b/255);\r\n const l=Math.cbrt(.4122214708*r+.5363325363*g+.0514459929*b),m=Math.cbrt(.2119034982*r+.6806995451*g+.1073969566*b),s=Math.cbrt(.0883024619*r+.2817188376*g+.6299787005*b);\r\n return [.2104542553*l+.793617785*m-.0040720468*s,1.9779984951*l-2.428592205*m+.4505937099*s,.0259040371*l+.7827717662*m-.8086757660*s];}\r\nconst dist=(p,q)=>(p[0]-q[0])**2+2*((p[1]-q[1])**2+(p[2]-q[2])**2); // hue weighed above lightness\r\nconst hexRgb=h=>[parseInt(h.slice(1,3),16),parseInt(h.slice(3,5),16),parseInt(h.slice(5,7),16)];\r\nfunction hsv(h,s,v){const f=(n,k=(n+h*6)%6)=>v-v*s*Math.max(0,Math.min(k,4-k,1));return [f(5),f(3),f(1)].map(x=>Math.round(x*255));}\r\nexport function gatherColours(){const out=new Map(),add=c=>{const k=c.join(',');if(!out.has(k))out.set(k,c);};\r\n for(const pal of Object.values(GROUND_PALETTES))pal.forEach(h=>add(hexRgb(h)));\r\n const h=paintHerdAtlas().data;for(let i=0;i<h.length;i+=4)if(h[i+3])add([h[i],h[i+1],h[i+2]]);\r\n (function walk(o){if(typeof o==='string'&&/^#[0-9a-f]{6}$/i.test(o))add(hexRgb(o));else if(o&&typeof o==='object')for(const v of Object.values(o))walk(v);})(BIOMES);\r\n for(const p of PLANETS)p.pal.forEach(walk=>add(hexRgb(walk)));\r\n for(let i=0;i<16;i++){const v=Math.round(i*255/15);add([v,v,v]);}\r\n for(let k=0;k<12;k++)for(const [s,v] of [[.85,.95],[.75,.7],[.6,.45],[.45,.25]])add(hsv(k/12,s,v));\r\n return [...out.values()];}\r\n// farthest-point cut: start from black and white, then repeatedly take the colour farthest (OKLab) from everything kept\r\nexport function buildPalette(n=PALETTE_SIZE){const all=gatherColours(),lab=all.map(oklab),keep=[];if(all.length<=n)return all;\r\n const near=new Float64Array(all.length).fill(Infinity),take=i=>{keep.push(i);for(let k=0;k<all.length;k++)near[k]=Math.min(near[k],dist(lab[k],lab[i]));};\r\n const lum=all.map(c=>c[0]+c[1]+c[2]);take(lum.indexOf(Math.min(...lum)));take(lum.indexOf(Math.max(...lum)));\r\n while(keep.length<n){let best=0;for(let k=1;k<all.length;k++)if(near[k]>near[best])best=k;take(best);}\r\n return keep.map(i=>all[i]);}\r\n// incremental LUT: step(ms) fills cells until the budget is spent; done once every cell has its nearest palette colour\r\nexport function createLutBuilder(palette=buildPalette(),size=LUT_SIZE,now=()=>performance.now()){\r\n const data=new Uint8Array(size*size*size*4),lab=palette.map(oklab),N=size**3;let cell=0;\r\n return {data,size,palette,get done(){return cell>=N;},get progress(){return cell/N;},\r\n  step(ms=LUT_BUDGET_MS){const t0=now();while(cell<N){for(let k=0;k<256&&cell<N;k++,cell++){const r=cell%size,g=(cell/size|0)%size,b=cell/(size*size)|0,q=oklab([r,g,b].map(v=>v*255/(size-1)));\r\n    let bi=0,bd=Infinity;for(let p=0;p<lab.length;p++){const d=dist(q,lab[p]);if(d<bd){bd=d;bi=p;}}const o=(g*size*size+b*size+r)*4,c=palette[bi];data[o]=c[0];data[o+1]=c[1];data[o+2]=c[2];data[o+3]=255;}\r\n   if(now()-t0>=ms)break;}return cell>=N;}};}\r\n// GLSL: map a display colour through the strip LUT (nearest; uLutSize = 32)\r\nexport const LUT_GLSL=`uniform sampler2D tLut;uniform float uLutSize,uRetro;\r\nvec3 cwRetro(vec3 c){vec3 i=floor(clamp(c,0.,1.)*(uLutSize-1.)+.5);return texture2D(tLut,vec2((i.b*uLutSize+i.r+.5)/(uLutSize*uLutSize),(i.g+.5)/uLutSize)).rgb;}`;\r\n","modules/coach-world-rts/roster-terminal.mjs":"import {UNIT_CATALOG,UNIT_SECTIONS,isWorldUnitAvailable} from '../coach-world/unit-catalog.mjs';\r\nimport {drawUnitPortrait} from './unit-portrait.mjs';\r\nimport {BUILDING_BY_ID,FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nconst CLIPS=['idle','walk','work','attack','hit','death'],DIRS=['N','NE','E','SE','S','SW','W','NW'];\r\nconst title=s=>String(s||'Neutral').replaceAll('-',' ').replace(/\\b\\w/g,c=>c.toUpperCase());\r\nconst earnedIds=b=>b?.coachAccess||b?.earnedCoachIds||b?.earnedCoaches||[];\r\nconst status=(u,b)=>u.unlockPolicy==='baseline'?'Baseline':u.unlockPolicy==='world-baseline-role'?'World role':isWorldUnitAvailable(u,earnedIds(b))?'Earned':'Locked';\r\nconst el=(tag,cls,text)=>{const e=document.createElement(tag);if(cls)e.className=cls;if(text)e.textContent=text;return e;};\r\nexport function mountRosterTerminal(host,{getBase=()=>null,loadPortrait=drawUnitPortrait}={}){\r\n const doc=host.ownerDocument||document,root=el('section','rts-roster'),header=el('header'),count=el('span','roster-count');\r\n const search=el('input'),filter=el('select');search.type='search';search.placeholder='Search crew, roles, families';search.setAttribute('aria-label','Search roster');filter.setAttribute('aria-label','Filter faction');\r\n for(const [id,label] of [['all','All civilizations'],...UNIT_SECTIONS.map(id=>[id,FACTION_BY_ID[id]?.name||title(id)]),['neutral','Neutral']]){const o=el('option','',label);o.value=id;filter.append(o);}\r\n const content=el('div','roster-content'),list=el('div','roster-list'),detail=el('section','roster-detail'),canvas=el('canvas'),scratch=el('canvas'),meta=el('div','roster-meta'),controls=el('div','roster-controls'),statusLine=el('p','roster-source');\r\n canvas.width=canvas.height=scratch.width=scratch.height=192;canvas.setAttribute('aria-label','Selected unit animation');list.setAttribute('aria-label','Units');\r\n header.append(el('h3','','CREW ARCHIVE'),count);const filters=el('div','roster-filters');filters.append(search,filter);content.append(list,detail);detail.append(canvas,meta,statusLine,controls);root.append(header,filters,content);host.append(root);\r\n let base=getBase(),selected=UNIT_CATALOG[0],action='idle',direction='S',raf=0,disposed=false,epoch=0,started=0,sig='';\r\n const reduced=()=>doc.defaultView?.matchMedia?.('(prefers-reduced-motion: reduce)').matches||false;\r\n function buttons(){for(const bt of controls.querySelectorAll('button'))bt.setAttribute('aria-pressed',String(bt.dataset.direction?bt.dataset.direction===direction:bt.dataset.action===action));}\r\n function renderList(){\r\n  const term=search.value.trim().toLowerCase(),f=filter.value;list.replaceChildren();\r\n  const rows=UNIT_CATALOG.filter(u=>(f==='all'||(u.section||'neutral')===f)&&(!term||[u.label,u.role,u.family,u.section].join(' ').toLowerCase().includes(term)));count.textContent=`${rows.length} / ${UNIT_CATALOG.length}`;\r\n  for(const u of rows){const bt=el('button','roster-row');bt.type='button';bt.dataset.rosterId=u.id;bt.setAttribute('aria-pressed',String(u===selected));bt.append(el('b','',u.label),el('span','',`${title(u.role)} · ${status(u,base)}`));bt.onclick=()=>choose(u);list.append(bt);}\r\n  if(!rows.length)list.append(el('p','','No units match this filter.'));\r\n }\r\n async function redraw(ts,token){\r\n  if(disposed||token!==epoch)return;\r\n  if(!root.getClientRects().length||doc.hidden){raf=requestAnimationFrame(t=>redraw(t,token));return;}\r\n  const result=await loadPortrait(scratch.getContext('2d'),selected,{base,time:Math.max(0,ts-started),action,direction,animate:!reduced(),scale:3});\r\n  if(disposed||token!==epoch)return;\r\n  const c=canvas.getContext('2d');c.clearRect(0,0,192,192);c.imageSmoothingEnabled=false;\r\n  if(result)c.drawImage(scratch,0,0);else{c.fillStyle='#ffb887';c.textAlign='center';c.fillText('Animation unavailable',96,96);}\r\n  if(!reduced())raf=requestAnimationFrame(t=>redraw(t,token));\r\n }\r\n function restart(){cancelAnimationFrame(raf);const token=++epoch;started=performance.now();buttons();void redraw(started,token);}\r\n function choose(u){\r\n  selected=u;action='idle';direction='S';\r\n  const path=(u.productionPath||[]).map(id=>BUILDING_BY_ID[id]?.label||({production:'Companion production',expedition:'Launch post'}[id]||title(id)));\r\n  meta.replaceChildren(el('strong','',u.label),el('p','',`${title(u.role)} · ${FACTION_BY_ID[u.section]?.name||'Neutral'} · ${u.difficulty||'standard'}`),el('p','',`Route: ${path.join(' → ')||'Command habitat'}`));\r\n  statusLine.textContent=`${status(u,base)} · ${u.family||'Crew'} · 8 facings`;\r\n  controls.replaceChildren();\r\n  for(const d of DIRS){const bt=el('button','',d);bt.type='button';bt.dataset.direction=d;bt.setAttribute('aria-label',`Facing ${d}`);bt.onclick=()=>{direction=d;restart();};controls.append(bt);}\r\n  for(const a of CLIPS){const bt=el('button','',title(a));bt.type='button';bt.dataset.action=a;bt.setAttribute('aria-label',`${a} clip`);bt.onclick=()=>{action=a;restart();};controls.append(bt);}\r\n  renderList();restart();\r\n }\r\n search.oninput=renderList;filter.onchange=renderList;choose(selected);\r\n return {update(next=getBase()){const key=JSON.stringify(earnedIds(next));base=next;if(key===sig)return;sig=key;renderList();statusLine.textContent=`${status(selected,base)} · ${selected.family||'Crew'} · 8 facings`;},dispose(){disposed=true;epoch++;cancelAnimationFrame(raf);root.remove();}};\r\n}\r\n","modules/coach-world-rts/ruins-kit.mjs":"// R58 WS-D: residential cells. Heavy ground fog, 6-12 dilapidated broken biome blocks (one merged mesh), and 2-3 giant biome formations\r\n// (NODE_SHAPES x10) poking out of the fog as the biome marker. Ruins are layer 'ruins' (the player's own neighbourhood, not MOM infrastructure).\r\nimport { BIOMES, NODE_SHAPES, shapeGeometry } from './biomes.mjs';\r\nimport { createPs1Material } from './ps1-material.mjs';\r\n\r\nexport const FOG_HEIGHT = 14; // metres of fog; blocks (<= 8 m) vanish in it, formations (~35 m) tower out of it\r\nconst rng = seed => { let s = (seed >>> 0) || 1; return () => { s = (s + 0x6D2B79F5) >>> 0; let t = s; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; };\r\nconst mix = (T, a, b, k) => new T.Color(a).lerp(new T.Color(b), k);\r\n\r\nfunction fogMaterial(T, color, depth, half) {\r\n  return new T.ShaderMaterial({\r\n    transparent: true, depthWrite: false, side: T.DoubleSide,\r\n    fog: true, uniforms: T.UniformsUtils.merge([T.UniformsLib.fog, { uTime: { value: 0 }, uColor: { value: new T.Color(color) }, uH: { value: depth }, uHalf: { value: new T.Vector2(half[0], half[1]) } }]), // V1: the shared fog law (fog-law.mjs) via three's fog chunks\r\n    // V1: mvPosition rebuilt from gl_Position so the fog distance follows any AR compress rewrite of the gl_Position line\r\n    vertexShader: 'varying vec3 vP;varying vec2 vL;\\n#include <fog_pars_vertex>\\nvoid main(){vL=position.xz;vP=position;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);vec4 mvPosition=inverse(projectionMatrix)*gl_Position;\\n#include <fog_vertex>\\n}',\r\n    // height fade (dense at the ground, thin at the top) x soft footprint edge x slow drifting bands\r\n    fragmentShader: '#include <fog_pars_fragment>\\nuniform float uTime,uH;uniform vec2 uHalf;uniform vec3 uColor;varying vec3 vP;varying vec2 vL;void main(){float h=clamp(vP.y/uH,0.,1.);float e=1.-smoothstep(.55,1.,max(abs(vL.x)/uHalf.x,abs(vL.y)/uHalf.y));float n=.75+.25*sin(vL.x*.13+uTime*.4)*sin(vL.y*.11-uTime*.3);gl_FragColor=vec4(uColor,.34*pow(1.-h,.6)*n*e);\\n#include <fog_fragment>\\n}'\r\n  });\r\n}\r\n\r\n// one geometry of stacked horizontal slabs (side faces of a box fade to nothing at the footprint edge, so a box would look empty)\r\nfunction fogSlabs(T, w, d, n = 8) {\r\n  const p = [], i = [];\r\n  for (let k = 0; k < n; k++) { const y = (k / (n - 1)) * FOG_HEIGHT, o = k * 4; p.push(-w / 2, y, -d / 2, w / 2, y, -d / 2, w / 2, y, d / 2, -w / 2, y, d / 2); i.push(o, o + 1, o + 2, o, o + 2, o + 3); }\r\n  const g = new T.BufferGeometry(); g.setAttribute('position', new T.Float32BufferAttribute(p, 3)); g.setIndex(i); return g;\r\n}\r\n\r\n// cell: {x, z, w, d, seed}. faction: biome id. heightAt(x,z) in cell metres. Returns {group, update(t), dispose, stats()}.\r\nexport function createRuinsKit(T, { cell, faction = 'chest', heightAt = () => 0, scale = 1 } = {}) {\r\n  const bio = BIOMES[faction] || BIOMES.chest, shape = NODE_SHAPES[faction] || NODE_SHAPES.chest, rnd = rng(cell.seed ?? 1);\r\n  const group = new T.Group(); group.name = 'ruins-kit'; group.userData.layer = 'ruins';\r\n  const cw = cell.w || 80, cd = cell.d || 80, y0 = heightAt(cell.x, cell.z) || 0, disposables = [];\r\n\r\n  // broken blocks: box shells with a missing slab, tilted and sunk, plus a rubble pile each. Merged by vertex colour into one mesh.\r\n  const pos = [], nor = [], col = [], n = 6 + Math.floor(rnd() * 7), tmp = new T.Matrix4(), q = new T.Quaternion(), e = new T.Euler();\r\n  const push = (geo, x, y, z, rx, ry, rz, s, c) => {\r\n    const g = geo.index ? geo.toNonIndexed() : geo;\r\n    g.applyMatrix4(tmp.compose(new T.Vector3(x, y, z), q.setFromEuler(e.set(rx, ry, rz)), new T.Vector3(...s)));\r\n    const p = g.attributes.position, nn = g.attributes.normal;\r\n    for (let i = 0; i < p.count; i++) { pos.push(p.getX(i), p.getY(i), p.getZ(i)); nor.push(nn.getX(i), nn.getY(i), nn.getZ(i)); col.push(c.r, c.g, c.b); }\r\n    g.dispose(); if (g !== geo) geo.dispose();\r\n  };\r\n  const wallCol = mix(T, bio.ground, '#2a2030', 0.55), rock = new T.Color(bio.rock.color);\r\n  for (let i = 0; i < n; i++) {\r\n    const x = (rnd() - 0.5) * cw * 0.85, z = (rnd() - 0.5) * cd * 0.85, w = 4 + rnd() * 6, d = 4 + rnd() * 6, h = 3 + rnd() * 5, c = wallCol.clone().multiplyScalar(0.8 + rnd() * 0.4);\r\n    const gy = (heightAt(cell.x + x, cell.z + z) || 0) - y0 - 0.6, ry = rnd() * Math.PI, tilt = (rnd() - 0.5) * 0.22;\r\n    push(new T.BoxGeometry(1, 1, 1), x, gy + h * 0.5, z, tilt, ry, tilt * 0.6, [w, h, d], c);\r\n    push(new T.BoxGeometry(1, 1, 1), x + w * 0.2, gy + h * 0.45, z - d * 0.1, tilt, ry + 0.4, 0, [w * 0.4, h * 0.9, d * 0.5], c.clone().multiplyScalar(0.7)); // staggered second wall\r\n    push(new T.ConeGeometry(1, 1, 5), x - w * 0.3, gy + h + 0.3, z + d * 0.2, 0.1, ry, 0.35, [w * 0.5, 1.8, d * 0.5], rock); // snapped roof stub\r\n    push(new T.IcosahedronGeometry(1, 0), x + w * 0.6, gy + 0.4, z + d * 0.6, 0, ry, 0, [w * 0.35, 0.7, d * 0.3], rock); // rubble\r\n  }\r\n  const bg = new T.BufferGeometry();\r\n  bg.setAttribute('position', new T.Float32BufferAttribute(pos, 3)); bg.setAttribute('normal', new T.Float32BufferAttribute(nor, 3)); bg.setAttribute('color', new T.Float32BufferAttribute(col, 3));\r\n  const bm = createPs1Material(T, { vertexColors: true }); bm.emissive = new T.Color(bio.ground).multiplyScalar(0.12);\r\n  const blocks = new T.Mesh(bg, bm); blocks.name = 'ruins-blocks'; group.add(blocks);\r\n  disposables.push(bg, bm);\r\n\r\n  // giant biome formations: NODE_SHAPES shape x10, tilted, lit with the biome plant colour so they read through the fog\r\n  const fcount = 2 + Math.floor(rnd() * 2), fm = createPs1Material(T, { vertexColors: false }), forms = [];\r\n  fm.color = new T.Color(bio.plant.color); fm.emissive = new T.Color(bio.plant.color).multiplyScalar(0.22);\r\n  disposables.push(fm);\r\n  for (let i = 0; i < fcount; i++) {\r\n    const g = shapeGeometry(T, i === fcount - 1 && fcount === 3 ? bio.rock.shape : shape.shape, 10 * scale), m = new T.Mesh(g, fm);\r\n    const a = (i / fcount) * Math.PI * 2 + rnd(), r = (0.15 + rnd() * 0.25) * Math.min(cw, cd);\r\n    m.position.set(Math.cos(a) * r * scale, (FOG_HEIGHT * 0.6 + 6 * rnd()) * scale, Math.sin(a) * r * scale); m.scale.setScalar(2.8);\r\n    m.rotation.set((rnd() - 0.5) * 0.3, rnd() * 6.28, (rnd() - 0.5) * 0.3); m.name = 'ruins-formation'; forms.push(m); group.add(m); disposables.push(g);\r\n  }\r\n\r\n  // fog volume last (transparent, depthWrite off), so blocks and formations show through it\r\n  const fogGeo = fogSlabs(T, cw, cd), fog = fogMaterial(T, mix(T, bio.ground, '#e0d4f0', 0.75), FOG_HEIGHT, [cw / 2, cd / 2]);\r\n  const fogMesh = new T.Mesh(fogGeo, fog); fogMesh.name = 'ruins-fog'; fogMesh.renderOrder = 5; group.add(fogMesh); disposables.push(fogGeo, fog);\r\n\r\n  // ground-relative blocks/formations use y relative to the cell centre; scale and place the whole group\r\n  group.scale.setScalar(1); blocks.scale.setScalar(scale); fogMesh.scale.setScalar(scale);\r\n  group.position.set(cell.x * scale, y0 * scale, cell.z * scale);\r\n  group.traverse(o => { o.userData.layer = 'ruins'; });\r\n  return {\r\n    group, update(t) { fog.uniforms.uTime.value = t; },\r\n    stats: () => ({ blocks: n, formations: fcount, drawCalls: group.children.length }),\r\n    dispose() { disposables.forEach(d => d.dispose()); }\r\n  };\r\n}\r\n","modules/coach-world-rts/run-loadout-panel.mjs":"import {CLASS_IDS,classGear} from '../roguelite/class-combat.mjs';\nimport {DEFAULT_RUN_LOADOUT,validateRunLoadout} from '../roguelite/run-loadout.mjs';\nimport {RELICS} from './npc-progression.mjs';\nimport {listedOwnedWeapons,resolveEquippedAvatarWeapon,RUN_WEAPON_MAPPINGS,OWNED_WEAPON_MAPPINGS} from '../roguelite/weapon-style-catalog.mjs';\n\nexport const WORLD_RUN_ENTRY='/roguelite-run.html?worldRun=1';\nconst DEFENSE={knight:'Shield guard; sidestep red attacks.',monk:'Dodge, chain punches and charge your aura.',dual:'Cross guard, directional parries and opposite-hand counters.',mage:'Raise hands for your finite shield; trace and confirm spells.'};\nconst STYLE={knight:'Shield and lance',monk:'Fist style',dual:'Paired blades',mage:'Elemental focus'};\nconst gear=CLASS_IDS.flatMap(styleId=>[0,1,2].map(tier=>({...classGear(styleId,tier),styleId})));\nconst knownWeaponIds=new Set([...RUN_WEAPON_MAPPINGS,...OWNED_WEAPON_MAPPINGS].map(w=>w.id));\nconst ids=value=>Array.isArray(value)?value.filter(id=>typeof id==='string'):[];\nexport function runLoadoutChoices(base,serverPerformance=null){\n const p=base?.npcProgression,weaponIds=ids(p?.weaponUpgrades),relicIds=ids(p?.relics);\n const galaWeaponTiers=serverPerformance?.weapons??{},authority={weaponIds:weaponIds.filter(id=>knownWeaponIds.has(id)),galaWeaponTiers};\n // Gala choices appear only after the server supplies ownership. Saved appearance is intent only.\n const weapons=listedOwnedWeapons(authority).filter(w=>serverPerformance||!w.weaponId.startsWith('gala:')).map(w=>{\n  let compatible=true;try{validateRunLoadout({weaponId:w.weaponId,styleId:w.styleId,relicIds:[]},{weaponIds,relicIds,galaWeaponTiers});}catch{compatible=false;}\n  return {...w,id:w.weaponId,tier:gear.find(g=>g.id===w.weaponId)?.tier??null,compatible};\n });\n return {weaponIds,relicIds,galaWeaponTiers,weapons,\n  unmapped:weaponIds.filter(id=>!knownWeaponIds.has(id)).map(id=>({id,name:id})),\n  relics:relicIds.filter(id=>RELICS[id]).map(id=>RELICS[id])};\n}\nconst mk=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n;};\nconst accountKey=a=>a?.user?.id&&Number.isSafeInteger(a.dataEpoch)?`${a.user.id}/${a.dataEpoch}/${a.gameGeneration??''}`:null;\n\n// Borrow the host's current account/base. No equipment, aura or ownership writes.\nexport function mountRunLoadoutPanel({getBase,getAccount=()=>globalThis.myr5AuthenticatedAccount,\n createRunClient,navigate=url=>location.assign(url),onStatus=()=>{},mountHeartSelection}={}){\n const dialog=mk('dialog','cw-run-loadout');dialog.setAttribute('aria-label','Run loadout');\n const heading=mk('h2','','Run loadout'),intro=mk('p','','Your equipped run weapon determines your fighting style.');\n const weapons=mk('div','cw-run-weapons');weapons.setAttribute('role','group');weapons.setAttribute('aria-label','Run weapon');\n const summary=mk('p','cw-run-summary'),armor=mk('p','','Armor never repairs during a run. After it breaks, the next unblocked hit ends the run. Less armor gives faster recovery and more response time.');\n const character=mk('div','cw-run-character'),characterCanvas=mk('canvas'),heartIcon=mk('span','cw-run-insert-heart','♥');\n characterCanvas.width=64;characterCanvas.height=96;characterCanvas.setAttribute('aria-label','Equipped run character');heartIcon.setAttribute('aria-hidden','true');character.hidden=true;heartIcon.hidden=true;character.append(characterCanvas,heartIcon);\n const heartHeading=mk('h3','','Coach Heart · one passive'),heartMount=mk('section','cw-run-heart');heartMount.dataset.runHeartMount='';heartMount.setAttribute('aria-label','Equipped Coach Heart');\n heartMount.append(mk('p','','Rescued coaches grant collectible hearts. Equip one heart passive when heart selection is available.'));\n const heartSelect=mk('select');heartSelect.setAttribute('aria-label','Coach Heart');heartSelect.dataset.runHeart='';\n const heartDescription=mk('p','cw-run-heart-description');\n if(!mountHeartSelection)heartMount.replaceChildren(heartSelect,heartDescription);\n const heartScope=mk('p','','Chest wearables stack with your heart during the run. MOM Inc. confiscates them on capture.');\n const relicHeading=mk('h3','','Legacy account relics · optional'),relics=mk('div','cw-run-relics'),unmapped=mk('p','cw-run-unmapped');\n const status=mk('p','cw-run-status');status.setAttribute('role','status');status.setAttribute('aria-live','polite');\n const actions=mk('div','cw-run-actions'),go=mk('button','','Start run'),close=mk('button','','Close');go.type=close.type='button';go.dataset.runConfirm='';close.dataset.runClose='';\n actions.append(go,close);dialog.append(heading,intro,character,weapons,summary,armor,heartHeading,heartMount,heartScope,relicHeading,relics,unmapped,status,actions);document.body.append(dialog);\n // Parent owns the forthcoming authoritative heart DTO/API. Mount only; no invented ledger or payload field.\n const heartPanel=mountHeartSelection?.(heartMount,{getBase,getAccount});\n let client=null,scope=null,selection=structuredClone(DEFAULT_RUN_LOADOUT),savedRun=null,serverPerformance=null,heartInventory=null,heartId=null,active=false,busy=false,ready=false,disposed=false,serial=0,watch=0,signature='',returnFocus=null;\n heartSelect.onchange=()=>{heartId=heartSelect.value||null;signature='';update();};\n const sameAccount=()=>!disposed&&!!scope&&scope===accountKey(getAccount());\n const message=text=>{status.textContent=text;};\n function resetClient(){client?.dispose();client=null;}\n function invalidate(){serial++;ready=false;busy=false;go.disabled=true;weapons.replaceChildren();relics.replaceChildren();summary.textContent='';unmapped.textContent='';heartPanel?.update?.({readOnly:true,invalidated:true});message('Account changed. Close and reopen loadout.');resetClient();onStatus(false);}\n function update(){\n  if(!dialog.open)return;\n  if(!sameAccount()){invalidate();return;}\n  heartPanel?.update?.({readOnly:active||busy||!ready});\n  const choices=runLoadoutChoices(getBase(),serverPerformance),nextSignature=JSON.stringify([choices,heartInventory,heartId,active,busy,ready]);\n  if(signature===nextSignature)return;signature=nextSignature;\n  try{selection=validateRunLoadout(selection,choices);}catch{selection=structuredClone(DEFAULT_RUN_LOADOUT);message('Owned equipment changed. Review your selection before starting.');}\n  weapons.hidden=unmapped.hidden=active;\n  relics.hidden=relicHeading.hidden=true; // New rescue runs use one Heart; legacy relics remain pinned only on resume.\n  selection.relicIds=[];\n  const ownedHearts=heartInventory?.version===1&&Array.isArray(heartInventory.owned)?heartInventory.owned:[];\n  if(heartId&&!ownedHearts.some(h=>h.heartId===heartId)){heartId=null;message('Heart ownership changed. Review your loadout.');}\n  heartSelect.replaceChildren(new Option('No Heart equipped',''),...ownedHearts.map(h=>new Option(h.name,h.heartId)));\n  heartSelect.value=heartId||'';heartSelect.disabled=active||busy||!ready;\n  const selectedHeart=ownedHearts.find(h=>h.heartId===heartId);\n  heartIcon.hidden=!selectedHeart;\n  const avatar=globalThis.GalaAvatar,weapon=choices.weapons.find(w=>w.id===(active?savedRun?.loadout?.weaponId:selection.weaponId));\n  character.hidden=!avatar?.draw||!weapon;\n  if(!character.hidden){try{const raw=getAccount()?.profile?.['mominc-avatar-v1']??globalThis.localStorage?.getItem('mominc-avatar-v1'),intent=typeof raw==='string'?JSON.parse(raw):raw;\n   const look=avatar.normalize(intent??structuredClone(avatar.defaultLook));look.weapon={...weapon.avatarWeapon};avatar.draw(characterCanvas,look,{base:true});}catch{character.hidden=true;}}\n  heartDescription.textContent=active?'Your saved Heart stays equipped for this run.':selectedHeart?\n   `${selectedHeart.name} · ${selectedHeart.damageMultiplier>1?`${Math.round((selectedHeart.damageMultiplier-1)*100)}% more damage`:selectedHeart.speedMultiplier<1?`${Math.round((1-selectedHeart.speedMultiplier)*100)}% faster recovery`:`${selectedHeart.initialArmorBonus} extra armor reserve`}. Equip changes apply to this new run.`:\n   ownedHearts.length?'Choose one rescued coach’s Heart for its passive power.':'Rescue coaches to collect their Hearts. You can start without one.';\n  weapons.replaceChildren();\n  for(const w of choices.weapons){const name=w.id.startsWith('gala:')?`${w.name} · tier ${w.avatarWeapon.tier+1}`:w.name;const b=mk('button','',w.compatible?name:`${name} · run support pending`);b.type='button';b.dataset.runWeapon=w.id;b.setAttribute('aria-pressed',String(w.id===selection.weaponId));b.disabled=active||busy||!ready||!w.compatible;\n   b.onclick=()=>{selection={...selection,weaponId:w.id,styleId:w.styleId};signature='';update();};weapons.append(b);}\n  const selectedRelics=choices.relics.filter(r=>selection.relicIds.includes(r.id));\n  summary.textContent=active?`Saved run · ${gear.find(w=>w.id===savedRun?.loadout?.weaponId)?.name||'saved weapon'} · ${STYLE[savedRun?.loadout?.styleId]||'saved style'}. Relics and armor stay as saved.`:`${STYLE[selection.styleId]} · ${DEFENSE[selection.styleId]}${selectedRelics.length?` Selected relics: ${selectedRelics.map(r=>r.name).join(', ')}.`:''}`;\n  relics.replaceChildren();\n  for(const r of choices.relics){const label=mk('label'),input=mk('input');input.type='checkbox';input.dataset.runRelic=r.id;input.checked=selection.relicIds.includes(r.id);input.disabled=active||busy||!ready||selection.relicIds.length>=3&&!input.checked;\n   const line=r.id==='overshield'?'One protection charge before armor breaks; no extra life in last stand.':r.line.replace('every run','this run');\n   label.append(input,mk('span','',`${r.name}: ${line}`));input.onchange=()=>{selection.relicIds=input.checked?[...selection.relicIds,r.id]:selection.relicIds.filter(id=>id!==r.id);signature='';update();};relics.append(label);}\n  if(!choices.relics.length)relics.append(mk('p','','No legacy account relics selected. Chest wearables are collected during a run.'));\n  unmapped.textContent=choices.unmapped.length?`Owned account gear awaiting run mapping: ${choices.unmapped.map(w=>w.name).join(', ')}. Choose a supported run weapon above.`:'';\n  go.textContent=busy?'Checking…':active?'Resume run':'Start run';go.disabled=busy||!ready;\n }\n async function check(){\n  const token=++serial;busy=true;ready=false;signature='';update();message('Checking your saved run…');\n  try{\n   if(!client){const created=createRunClient?await createRunClient({getAccount}):(await import('../roguelite/world-run-client.mjs')).createWorldRunClient({getAccount});\n    if(token!==serial||!sameAccount()){created.dispose();return;}client=created;}\n   const value=await client.current();if(token!==serial||!sameAccount())return;\n   active=value?.envelope?.runStatus==='active';savedRun=value?.envelope?.runState;heartInventory=value?.hearts??value?.envelope?.hearts??null;\n   heartId=heartInventory?.equippedHeartId??null;serverPerformance=value?.serverPerformance??value?.envelope?.serverPerformance??null;\n   if(!active&&serverPerformance){\n    try{const raw=getAccount()?.profile?.['mominc-avatar-v1']??globalThis.localStorage?.getItem('mominc-avatar-v1');\n     const intent=typeof raw==='string'?JSON.parse(raw):raw;\n     if(intent?.weapon){const choices=runLoadoutChoices(getBase(),serverPerformance),equipped=resolveEquippedAvatarWeapon(intent.weapon,{weaponIds:choices.weaponIds.filter(id=>knownWeaponIds.has(id)),galaWeaponTiers:choices.galaWeaponTiers});selection={weaponId:equipped.weaponId,styleId:equipped.styleId,relicIds:[]};}\n    }catch{} // Unowned or obsolete appearance falls back to the validated starter.\n   }\n   ready=true;onStatus(active);\n   message(active?'Resume keeps the saved weapon, relics, armor and progress unchanged.':'Confirm equipment to start a fresh authenticated run.');\n  }catch(e){if(token===serial&&sameAccount())message(e?.message||'Could not check the saved run. Close and retry.');}\n  finally{if(token===serial&&sameAccount()){busy=false;signature='';update();}}\n }\n async function confirm(){\n  if(busy||!ready)return;if(!sameAccount()){invalidate();return;}\n  if(active){navigate(WORLD_RUN_ENTRY);return;}\n  // Revalidate against the latest borrowed ownership immediately before POST.\n  let loadout;try{loadout=validateRunLoadout(selection,runLoadoutChoices(getBase(),serverPerformance));}catch(e){message(e.message);signature='';update();return;}\n  const token=++serial;busy=true;signature='';update();message('Starting run…');\n  try{await client.begin(true,loadout,{heartId});if(token!==serial||!sameAccount()||!dialog.open)return;navigate(WORLD_RUN_ENTRY);}\n  catch(e){if(token===serial&&sameAccount()){\n   // A lost start response may still have created the run. Never blindly POST again.\n   ready=false;message(e?.message||'Start failed.');await check();\n  }}finally{if(token===serial&&sameAccount()){busy=false;signature='';update();}}\n }\n go.onclick=()=>void confirm();close.onclick=()=>dialog.close();\n function onClose(){if(dialog.open)return;serial++;clearInterval(watch);resetClient();busy=false;ready=false;returnFocus?.focus?.({preventScroll:true});}\n dialog.addEventListener('close',onClose);\n return {\n  async open(){if(disposed)return;if(dialog.open){dialog.focus();return;}\n   serial++;clearInterval(watch);resetClient();returnFocus=document.activeElement;scope=accountKey(getAccount());active=false;busy=false;ready=false;savedRun=null;serverPerformance=null;heartInventory=null;heartId=null;selection=structuredClone(DEFAULT_RUN_LOADOUT);signature='';\n   dialog.showModal();if(!scope){invalidate();message('Sign in to start or resume an account run.');return;}\n   watch=setInterval(update,250);await check();\n  },\n  update,\n  dispose(){if(disposed)return;disposed=true;serial++;clearInterval(watch);resetClient();heartPanel?.dispose?.();dialog.removeEventListener('close',onClose);if(dialog.open)dialog.close();dialog.remove();},\n };\n}\n","modules/coach-world-rts/runtime.mjs":"// W1 animation runtime. createRts({ownerId}) -> {step,owns,release,drawUnit,drawBuilding,drawOverlays,onEvent,dispose} + moveTo/claim/setSelected/getUnitDots.\r\nimport {FACTION_BY_ID,FACTIONS} from '../coach-world/catalog.mjs';\r\nimport {UNIT_BY_ID as WORLD_UNITS} from '../coach-world/unit-catalog.mjs';\r\nimport {TICK,headingOf,sectorOf,turnToward,facingFor,normalizeAtlas,walkFrame,clipFrame} from './clips.mjs';\r\nimport {createFx,shadow,ring,healthBar} from './fx.mjs';\r\nimport {claim,release as releaseUnits,order,combinedBlocked,stepOwned} from './movement.mjs';\r\nimport {createMining} from './mining.mjs';\r\nimport {createConstruction} from './construction.mjs';\r\nimport {createPoseMixer} from './coach-motion.mjs';\r\nimport {unitLevel,lookFor,FACTION_AURA} from './unit-levels.mjs';\r\nimport {auraFramesSync,drawAura2d} from './aura-sprites.mjs';\r\nimport {createTrail2d} from './unit-trail.mjs';\r\nexport {TICK};\r\nexport const MAX_TICKS=5,BUDGET_BYTES=24*2**20;\r\n\r\nexport function createClock({tick=TICK,maxTicks=MAX_TICKS}={}){\r\n let last=null,acc=0;const c={ticks:0,alpha:0,\r\n  advance(now,hidden=false){if(hidden){last=null;return 0;}if(last==null){last=now;return 0;}acc+=Math.max(0,now-last);last=now;let n=Math.floor(acc/tick);if(n>maxTicks){n=maxTicks;acc=0;}else acc-=n*tick;c.ticks+=n;c.alpha=acc/tick;return n;}};\r\n return c;\r\n}\r\n\r\nconst mk=(w,h)=>typeof OffscreenCanvas!=='undefined'?new OffscreenCanvas(w,h):Object.assign(document.createElement('canvas'),{width:w,height:h});\r\nconst img=src=>new Promise((ok,no)=>{const i=new Image();i.onload=()=>ok(i);i.onerror=no;i.src=src;});\r\nconst rgb=hex=>[1,3,5].map(i=>parseInt(hex.slice(i,i+2),16));\r\n// default loader: pilot atlas next to this file. Rejects (-> fallback) when fetch/Image are missing or files are absent.\r\nexport async function loadSheet(url=new URL('./atlas/myr5.json',import.meta.url)){\r\n if(typeof url==='string'&&!/[\\/.]/.test(url))url=new URL(`./atlas/${url}.json`,import.meta.url); // loadSheet('bodies-chest')\r\n const atlas=normalizeAtlas(await (await fetch(url)).json());\r\n const opt=n=>n?img(new URL(n,url).href).catch(()=>null):null;\r\n const [gray,mask,carry,fx]=await Promise.all([img(new URL(atlas.image||`${atlas.unit}.png`,url).href),img(new URL(atlas.mask||`${atlas.unit}-mask.png`,url).href),opt(atlas.carry?.sheet),opt(atlas.fx?.spark?.sheet)]);\r\n return makeSheet(atlas,gray,mask,BUDGET_BYTES,{carry,fx});\r\n}\r\n// Lazy per-faction body sheets (WS-K): atlas/bodies-<section>.json, one load per name, null when missing (callers fall back to the myr5 sheet).\r\nconst factionSheets=new Map();\r\nexport const sheetNameFor=def=>def?.rtsSheet||null; // catalog rtsSheet: 'bodies-<section>' | 'bodies-neutral' | 'bodies-pets'; none = the myr5 sheet\r\nexport function factionSheet(name,loader=loadSheet){if(!name)return null;let e=factionSheets.get(name);if(!e){e={sheet:null,p:Promise.resolve().then(()=>loader(name)).then(s=>{e.sheet=s;return s;}).catch(()=>null)};factionSheets.set(name,e);}return e.sheet;}\r\n// 4-step team ramp from a faction colour (same shape as the baked red/blue/green ramps): dark, mid, base, highlight.\r\n// Roster size by tier (Ian): small starter grunts ~0.8x up to boss 1.6x; workers 1x, four-legged companions/wild 0.8x.\r\nconst GRUNT_SIZE={starter:.8,easy:.9,medium:1,hard:1.15};\r\nexport const sizeFor=d=>!d?1:d.role==='boss'?1.6:d.role==='grunt'?GRUNT_SIZE[d.difficulty]??1:d.role==='scout'||d.role==='carrier'||d.sheet==='pets'?.8:1;\r\nexport const rampFor=hex=>{const c=rgb(hex);return [c.map(v=>Math.round(v*.27)),c.map(v=>Math.round(v*.62)),c,c.map(v=>Math.round(v+(255-v)*.55))];};\r\n// Team region (mask, bilinear upsample, >127): 4-step ramp picked by gray bucket (teamEdges). Elsewhere gray*baseTint, highlights go white.\r\n// Atlases without teamEdges keep the old gray*tint dye. Dyes cached per colour; ledger = decoded bytes.\r\nexport function makeSheet(atlas,gray,mask,budget=BUDGET_BYTES,{carry=null,fx=null}={}){\r\n const w=gray.width,h=gray.height,dyes=new Map();let bytes=w*h*4+mask.width*mask.height*4+(carry?carry.width*carry.height*4:0)+(fx?fx.width*fx.height*4:0),g,m;\r\n return {atlas,carry,fx,get bytes(){return bytes;},\r\n  dye(hex){\r\n   if(dyes.has(hex))return dyes.get(hex);\r\n   if(bytes+w*h*4>budget){dyes.set(hex,null);return null;}\r\n   if(!g){const a=mk(w,h).getContext('2d');a.drawImage(gray,0,0);g=a.getImageData(0,0,w,h).data;const b=mk(w,h).getContext('2d');b.imageSmoothingEnabled=true;b.drawImage(mask,0,0,w,h);m=b.getImageData(0,0,w,h).data;}\r\n   const out=mk(w,h),o=out.getContext('2d'),d=o.createImageData(w,h),t=rgb(hex),B=atlas.baseTint,E=atlas.teamEdges,R=E&&rampFor(hex);\r\n   for(let i=0;i<w*h;i++){const p=i*4,l=g[p];d.data[p+3]=g[p+3];\r\n    if(E&&m[p]>127){const r=R[(l>E[0])+(l>E[1])+(l>E[2])];d.data[p]=r[0];d.data[p+1]=r[1];d.data[p+2]=r[2];continue;}\r\n    if(E){const hi=Math.min(1,Math.max(0,(l-190)/50));for(let c=0;c<3;c++)d.data[p+c]=Math.min(255,l*(B[c]+(255-B[c])*hi)/160);continue;}\r\n    const k=m[p]/255;for(let c=0;c<3;c++)d.data[p+c]=Math.min(255,l/160*(t[c]*k+B[c]*(1-k)));}\r\n   o.putImageData(d,0,0);bytes+=w*h*4;dyes.set(hex,out);return out;\r\n  }};\r\n}\r\n\r\n// Units scale with the world like buildings (SC): the cell is drawn at a nearest-neighbour factor chosen so the worker body\r\n// is about 0.55 tile tall. Factor in device px per atlas px: 0.5 (half), 1, 2, 3...; sc = css px per atlas px.\r\nexport const BODY_TILES=0.55,BODY_FRAC=0.7; // visible body is ~70% of the 40 px cell\r\nexport function unitFactor(unitPx,dpr,cellH){const r=BODY_TILES/BODY_FRAC*unitPx*dpr/cellH;return r<.75?.5:Math.max(1,Math.round(r));}\r\nconst unitScale=(ctx,tr,A)=>{const dpr=ctx.getTransform?.()?.a||2;return tr?.unit?unitFactor(tr.unit,dpr,A.cell[1])/dpr:20/A.cell[1];};\r\nlet current=null;\r\n// Wild hunt: caught four-legged coaches stand by the HQ door as crew (first 24, fixed slots). Same dot shape as crew; role 'wild' + tier for the tier plate.\r\nexport const WILD_DOT_CAP=24;\r\nexport function wildDots(base){\r\n const hq=base?.buildings?.hq,list=Array.isArray(base?.wild)?base.wild.slice(0,WILD_DOT_CAP):[];if(!hq||!list.length)return [];\r\n return list.flatMap((w,i)=>{const u=WORLD_UNITS[w.unitId];if(!u)return [];return [{id:'wild:'+w.id,x:hq.x-.5+i%6,y:hq.y+4+1+Math.floor(i/6),level:1,faction:u.section,color:FACTION_BY_ID[u.section]?.color||'#fff',worker:true,scale:.8,role:'wild',tier:w.tier,unitId:w.unitId,source:u.sourceCoachId,sheet:sheetNameFor(u)}];});\r\n}\r\nexport const getUnitDots=()=>current?current.getUnitDots():[];\r\nexport const getPose=u=>current?.poseFor(u)||null;\r\n\r\nexport function createRts({ownerId=()=>null,sheet=undefined,onSpark,loader=loadSheet}={}){\r\n const clock=createClock(),states=new Map(),selected=new Set(),fx=createFx({onSpark,reduced:()=>typeof matchMedia!=='undefined'&&matchMedia('(prefers-reduced-motion: reduce)').matches});\r\n const api={},mining=createMining({rts:api,reduced:()=>typeof matchMedia!=='undefined'&&matchMedia('(prefers-reduced-motion: reduce)').matches});\r\n const construction=createConstruction({rts:api,ownerId,reduced:()=>typeof matchMedia!=='undefined'&&matchMedia('(prefers-reduced-motion: reduce)').matches});\r\n import('./sfx.mjs').then(m=>{mining.sfx=m.play;construction.sfx=m.play;}).catch(()=>{});\r\n let sh=sheet||null,crewNow=[],baseNow=null,last=null,blockedKey='',blocked=null,disposed=false;\r\n if(sheet===undefined)Promise.resolve().then(()=>loader()).then(s=>{if(!disposed)sh=s;}).catch(()=>{sh=null;});\r\n const isWorker=u=>u.role==='worker'&&u.definition?.sheet!=='pets';\r\n const isBody=u=>isWorker(u)||['grunt','boss','scout','carrier'].includes(u.role||u.definition?.role); // everything the atlas draws: workers + earned coaches + companions\r\n const get=u=>{let s=states.get(u.id);if(!s){s={x:u.x,y:u.y,dist:0,sector:4,want:4,mode:null,shot:null,since:clock.ticks,moving:false,carry:false,strike:-1,mixer:createPoseMixer(),trail:null};states.set(u.id,s);}return s;};\r\n Object.assign(api,{\r\n  clock,fx,mining,construction,isWorking:id=>states.get(id)?.mode==='work',\r\n  step(t,crew,base,tr,serverNow,terrain){\r\n   const dt=last==null?0:Math.min(.1,(t-last)/1000);last=t;crewNow=crew;baseNow=base||null;\r\n   const n=clock.advance(t,typeof document!=='undefined'&&document.hidden);\r\n   for(let k=0;k<n;k++){fx.step();for(const s of states.values())s.sector=turnToward(s.sector,s.want);}\r\n   for(const u of crew){\r\n    if(u.mover==='rts'&&(u.away||base?.rest||u.rally||u.hasOrder))u.mover='codex';\r\n    if(u.mover==='rts')stepOwned(u,dt);\r\n    const s=get(u),dx=u.x-s.x,dy=u.y-s.y,d=Math.hypot(dx,dy);s.x=u.x;s.y=u.y;s.moving=d>1e-4;\r\n    if(s.moving){s.dist+=d;if(isBody(u)){s.want=sectorOf(headingOf(dx,dy));if(s.mode==='work')s.mode=null;}}\r\n   }\r\n   construction.step(base||{},serverNow,n);\r\n   mining.step(crew,base||{},tr,n,terrain);\r\n  },\r\n  // Coach motion for any unit: pets (four-legged) squat, everything else waddles. Walk phase follows distance walked so it matches the atlas stride.\r\n  poseFor(u){\r\n   const s=get(u),sec=clock.ticks*TICK/1000,moving=s.moving;\r\n   const name=moving?(u.definition?.sheet==='pets'?'squat':'walk'):'idle';\r\n   return s.mixer.sample(name,moving?s.dist/(sh?.atlas?.stride||.5)*.9:sec,sec);\r\n  },\r\n  owns:u=>u.mover==='rts',\r\n  claim:(u,base)=>claim(u,base||{}),\r\n  release(ids,reason){mining.release(ids);releaseUnits(crewNow,ids,reason);},\r\n  moveTo(u,target,base,terrain){const key=terrain?.signature||'';if(!blocked||key!==blockedKey){blocked=combinedBlocked(base,terrain);blockedKey=key;}return order(u,blocked,target);},\r\n  setSelected(ids){selected.clear();for(const i of ids)selected.add(i);},\r\n  // events: {type:'work'|'carry'|'build'|'ko'|'levelup'|'attack'|'hit', unitId, on}\r\n  onEvent(e){const s=states.get(e.unitId);if(!s)return;\r\n   if(e.type==='carry')s.carry=e.on!==false;\r\n   else if(e.type==='work'||e.type==='build'){s.mode=e.on===false?null:'work';s.since=clock.ticks;s.strike=-1;}\r\n   else if(['ko','levelup','attack','hit'].includes(e.type)){if(s.shot?.clip==='death')return;s.shot={clip:e.type==='ko'?'death':e.type==='levelup'?'cheer':e.type};s.since=clock.ticks;}},\r\n  drawUnit(ctx,unit,point,tr){\r\n   if(!sh||!isBody(unit))return false;\r\n   const f=FACTION_BY_ID[unit.definition.section]||FACTIONS[0],fs=factionSheet(sheetNameFor(unit.definition)),ud=fs?.atlas?.units?.[unit.definition.sourceCoachId];\r\n   if(!ud&&!isWorker(unit))return false; // roster sheet not loaded yet / missing: canvas-scene draws its own art\r\n   const S=ud?fs:sh,CL=ud?ud.clips:sh.atlas.clips,sprite=S.dye(f.color);if(!sprite)return false;\r\n   const A=S.atlas,s=get(unit),since=clock.ticks-s.since;\r\n   let name=s.moving?'walk':s.mode==='work'?'work':'idle';\r\n   if(s.shot){const c=CL[s.shot.clip];if(c&&(s.shot.clip==='death'||since<sum(c)))name=s.shot.clip;else s.shot=null;} // Attack/Death/LevelUp stubs: clip only if the atlas has one\r\n   const clip=CL[name]||CL.idle,{stored,mirror}=facingFor(s.sector,A.dirs);\r\n   let fr=0;\r\n   if(name==='walk')fr=walkFrame(s.dist,clip.frames,A.stride);\r\n   else if(clip.frames>1){fr=clipFrame(clip,since,name==='work'||name==='idle');}\r\n   const [rx,ry,rw,rh]=clip.rects[stored][fr],sc=unitScale(ctx,tr,A)*sizeFor(unit.definition),bob=name==='idle'&&A.idle.bobPx?Math.round(Math.sin(clock.ticks*TICK/A.idle.bobMs*Math.PI*2)*.5+.5)*A.idle.bobPx*sc:0;\r\n   shadow(ctx,point.x,point.y+1,A.cell[0]*sc*.8);ring(ctx,point.x,point.y+1,A.cell[0]*sc,f.color,selected.has(unit.id));\r\n   // level looks (unit-levels): 2 accent ring, 3 aura under the sprite, 4 neon trail, 5 second aura. The atlas frames already bake the waddle, so no runtime bob/roll here.\r\n   const look=lookFor(unitLevel(unit,baseNow)),mid=point.y-A.cell[1]*sc*.4;\r\n   if(look.tint){ctx.save();ctx.strokeStyle='#fff0a6';ctx.globalAlpha=.55;ctx.beginPath();ctx.ellipse(point.x,point.y+1,A.cell[0]*sc*.5,A.cell[0]*sc*.22,0,0,Math.PI*2);ctx.stroke();ctx.restore();}\r\n   if(look.trail){const tr2=s.trail||(s.trail=createTrail2d());if(s.moving)tr2.push(point.x,point.y);else if(tr2.pts.length)tr2.pts.shift();tr2.draw(ctx,f.color);}\r\n   if(look.aura){const sz=A.cell[0]*sc*2.2,tk=clock.ticks;drawAura2d(ctx,auraFramesSync(FACTION_AURA[unit.definition.section]),point.x,mid,sz,tk);if(look.second)drawAura2d(ctx,auraFramesSync(look.second),point.x,mid,sz*1.2,tk*.7);}\r\n   ctx.save();ctx.imageSmoothingEnabled=false;const dp=ctx.getTransform?.()?.a||1;ctx.translate(Math.round(point.x*dp)/dp,Math.round(point.y*dp)/dp);if(mirror)ctx.scale(-1,1);\r\n   const ox=-A.anchor[0]*sc,oy=-A.anchor[1]*sc-bob,cw=rw*sc,chh=rh*sc;\r\n   // carry chunk: one cell per walk frame x stored facing; behind the body except back views (NE/N)\r\n   const cy=s.carry&&S.carry&&A.carry?.layer?{x:(name==='walk'?fr:0)*A.cell[0],y:stored*A.cell[1],above:A.carry.layer[stored]!=='below'}:null;\r\n   if(cy&&!cy.above)ctx.drawImage(S.carry,cy.x,cy.y,A.cell[0],A.cell[1],ox,oy,cw,chh);\r\n   ctx.drawImage(sprite,rx,ry,rw,rh,ox,oy,cw,chh);\r\n   if(cy?.above)ctx.drawImage(S.carry,cy.x,cy.y,A.cell[0],A.cell[1],ox,oy,cw,chh);\r\n   // strike spark sprite at the baked per-facing point; plays from startMs of each work cycle\r\n   const sp=A.fx?.spark;\r\n   if(name==='work'&&sp&&S.fx){const el=((since%sum(clip))*TICK)-sp.startMs;let acc=0,i=-1;if(el>=0)for(let k=0;k<sp.ms.length;k++){acc+=sp.ms[k];if(el<acc){i=k;break;}}\r\n    if(i>=0){const [px,py]=sp.strike[stored],[r0,r1,r2,r3]=sp.rects[i];ctx.drawImage(S.fx,r0,r1,r2,r3,(px-A.anchor[0]-sp.center[0])*sc,(py-A.anchor[1]-sp.center[1])*sc,r2*sc,r3*sc);}}\r\n   ctx.restore();\r\n   if(unit.hp!=null&&unit.maxHp&&unit.hp<unit.maxHp)healthBar(ctx,point.x,point.y-A.anchor[1]*sc-3,A.cell[0]*sc*.5,unit.hp/unit.maxHp);\r\n   return true;\r\n  },\r\n  drawBuilding:(ctx,b,f,x,y,size)=>construction.drawBuilding(ctx,b,f,x,y,size),\r\n  sortables:()=>mining.sortables(),\r\n  drawSortable(ctx,s,tr,terrain){s.draw(ctx,tr,terrain);},\r\n  drawOverlays(ctx){fx.draw(ctx);},\r\n  getUnitDots:()=>crewNow.map(u=>{const s=get(u),fs=factionSheet(sheetNameFor(u.definition)),clips=fs?.atlas?.units?.[u.definition?.sourceCoachId]?.clips||sh?.atlas?.clips,c=clips?.[s.shot?.clip],elapsed=(clock.ticks-s.since)*TICK,shot=c&&(s.shot.clip==='death'||elapsed<sum(c)*TICK)?{clip:s.shot.clip,clipElapsed:elapsed}:{};return {id:u.id,selectionId:u.id,instanceId:u.definition?.instanceKey||u.id,x:u.x,y:u.y,level:unitLevel(u,baseNow),faction:u.definition?.section,color:FACTION_BY_ID[u.definition?.section]?.color||'#fff',worker:isBody(u),scale:sizeFor(u.definition),source:u.definition?.sourceCoachId,sheet:sheetNameFor(u.definition),...shot};}).concat(wildDots(baseNow)),\r\n  dispose(){disposed=true;mining.release();construction.dispose();states.clear();selected.clear();fx.parts.length=0;sh=null;if(current===api)current=null;},\r\n });\r\n const sum=c=>c.ticks.reduce((a,b)=>a+b,0);\r\n current=api;return api;\r\n}\r\n","modules/coach-world-rts/selection-panel.css":".cw-rts .cw-rts-selpanel{\r\n    position:fixed;\r\n    right:0;\r\n    width:120px;\r\n    bottom:calc(var(--cw-console-h,0px) + 8px);\r\n    max-height:40vh;\r\n    overflow-y:auto;\r\n    overscroll-behavior:contain;\r\n    z-index:3;\r\n    background:#0d1217e6;\r\n    border-left:2px solid var(--cw-faction,#7fd4ff);\r\n    color:#dfe8ee;\r\n    display:flex;\r\n    flex-direction:column;\r\n    gap:4px;\r\n    padding:4px;\r\n    pointer-events:auto;\r\n    touch-action:manipulation\r\n}\r\n.cw-rts .cw-rts-selpanel[hidden]{display:none}\r\n.cw-rts .cw-rts-selrow{\r\n    display:grid;\r\n    grid-template-columns:32px 1fr auto;\r\n    align-items:center;\r\n    gap:4px;\r\n    min-height:40px;\r\n    padding:2px 4px;\r\n    background:#1b222a;\r\n    border:1px solid #4a5662;\r\n    font:12px/1 var(--num)\r\n}\r\n.cw-rts .cw-rts-selrow.is-active{border-color:var(--cw-faction,#7fd4ff)}\r\n.cw-rts .cw-rts-selportrait{\r\n    width:32px;height:32px;\r\n    image-rendering:pixelated;\r\n    background:#0b0f12;\r\n    border:1px solid var(--cw-faction,#7fd4ff)\r\n}\r\n.cw-rts .cw-rts-selcount{\r\n    font:14px var(--num);\r\n    color:#dfe8ee;\r\n    min-width:20px;\r\n    text-align:right\r\n}\r\n.cw-rts .cw-rts-hpchips{display:flex;gap:2px;flex-wrap:wrap}\r\n.cw-rts .cw-rts-hp{\r\n    width:8px;height:8px;\r\n    box-shadow:0 0 0 1px #000\r\n}\r\n.cw-rts .cw-rts-hp.is-green{background:#4fd16a}\r\n.cw-rts .cw-rts-hp.is-yellow{background:#e8c547}\r\n.cw-rts .cw-rts-hp.is-red{background:#e0524a}\r\n.cw-rts .cw-rts-selx{\r\n    width:24px;height:24px;\r\n    background:#2a1a1a;\r\n    border:1px solid #6b3b38;\r\n    color:#ffb4a8;\r\n    font:14px/1 var(--num);\r\n    padding:0;\r\n    touch-action:manipulation;\r\n    cursor:pointer\r\n}\r\n.cw-rts .cw-rts-selx:focus-visible{\r\n    outline:2px solid #fff;\r\n    outline-offset:-3px\r\n}\r\n.cw-rts .cw-rts-single{\r\n    display:grid;\r\n    grid-template-columns:1fr;\r\n    gap:6px;\r\n    padding:6px\r\n}\r\n.cw-rts .cw-rts-bigportrait{\r\n    width:64px;height:64px;\r\n    image-rendering:pixelated;\r\n    background:#0b0f12;\r\n    border:2px solid var(--cw-faction,#7fd4ff)\r\n}\r\n.cw-rts .cw-rts-stats{\r\n    display:grid;\r\n    grid-template-columns:minmax(0,1fr);\r\n    gap:1px;\r\n    font:12px var(--num);\r\n    color:#b8c4cc\r\n}\r\n/* V1-C2 centre box: between minimap and command grid; takes the portrait slot while one unit is selected */\r\n.cw-rts .cw-rts-centre{\r\n    flex:1 1 0;\r\n    min-width:0;\r\n    max-width:340px;\r\n    position:relative;\r\n    align-self:stretch;\r\n    box-sizing:border-box;\r\n    display:flex;\r\n    flex-direction:column;\r\n    justify-content:center;\r\n    padding:2px;\r\n    background:#0d1217e6;\r\n    border:1px solid var(--cw-faction,#7fd4ff);\r\n    color:#dfe8ee\r\n}\r\n.cw-rts .cw-rts-centre[hidden]{display:none}\r\n.cw-rts .cw-rts-centre .cw-rts-abilities{position:absolute;top:2px;right:2px}\r\n.cw-rts .cw-rts-centre .cw-rts-ability{width:20px;height:20px}\r\n.cw-rts .cw-rts-centre .cw-rts-selcmds .cw-rts-cmd span{display:none}\r\n.cw-rts .cw-rts-centre .cw-rts-selcmds .cw-rts-cmd{min-width:0;width:auto}\r\n.cw-rts .cw-rts-centre .cw-rts-selcmds .cw-rts-cmd img{width:16px;height:16px}\r\n.cw-rts .cw-rts-inner:has(.cw-rts-centre:not([hidden])) .cw-rts-portrait{display:none}\r\n.cw-rts .cw-rts-centre .cw-rts-single{padding:0;gap:2px;justify-items:center}\r\n.cw-rts .cw-rts-centre .cw-rts-hpbar{height:6px;width:100%}\r\n.cw-rts .cw-rts-centre .cw-rts-stats{justify-items:stretch;width:100%;gap:0;font:12px/1.1 var(--num)}\r\n.cw-rts .cw-rts-centre .cw-rts-stats>*{overflow:visible;text-overflow:clip;white-space:normal;overflow-wrap:anywhere;min-width:0}\r\n.cw-rts .cw-rts-name{font:bold 13px/1.1 var(--num);color:#fff}\r\n.cw-rts .cw-rts-kills{color:#b8c4cc}\r\n.cw-rts .cw-rts-hpnum,.cw-rts .cw-rts-shnum{color:#4fd16a}\r\n.cw-rts .cw-rts-shnum{color:#6ec6ff}\r\n.cw-rts .cw-rts-hpbar{\r\n    height:8px;\r\n    background:#2a1a1a;\r\n    position:relative;\r\n    overflow:hidden\r\n}\r\n.cw-rts .cw-rts-hpfill{\r\n    position:absolute;\r\n    left:0;top:0;bottom:0;\r\n    background:#4fd16a\r\n}\r\n.cw-rts .cw-rts-shield{\r\n    position:absolute;\r\n    left:0;top:0;bottom:0;\r\n    background:#6ec6ff;\r\n    opacity:.8\r\n}\r\n.cw-rts .cw-rts-abilities{display:flex;gap:4px}\r\n.cw-rts .cw-rts-ability{\r\n    width:28px;height:28px;\r\n    border:1px solid #4a5662;\r\n    background:#1b222a;\r\n    image-rendering:pixelated\r\n}\r\n.cw-rts .cw-rts-ability[aria-disabled=true]{opacity:.4}\r\n.cw-rts .cw-rts-selcmds{\r\n    display:grid;\r\n    grid-template-columns:repeat(3,1fr);\r\n    gap:4px\r\n}\r\n.cw-rts .cw-rts-selcmds .cw-rts-cmd{min-height:44px}\r\n.cw-rts .cw-rts-selcmds .cw-rts-cmd[aria-pressed=true]{\r\n    background:var(--cw-faction,#7fd4ff);\r\n    color:#000\r\n}\r\n.cw-rts .is-compact .cw-rts-selpanel{width:96px}\r\n.cw-rts .is-compact .cw-rts-selrow{\r\n    grid-template-columns:24px 1fr auto;\r\n    min-height:32px\r\n}\r\n.cw-rts .is-compact .cw-rts-selportrait{width:24px;height:24px}\r\n.cw-rts .cw-rts-selpanel:not([hidden]){animation:cw-rts-selin .18s steps(3)}\r\n@keyframes cw-rts-selin{\r\n    from{transform:translateX(100%)}\r\n    to{transform:none}\r\n}\r\n@media (prefers-reduced-motion:reduce){\r\n    .cw-rts .cw-rts-selpanel:not([hidden]){animation:none}\r\n}\r\n.cw-rts .cw-rts-selpanel{box-sizing:border-box;overflow-x:hidden}\r\n.cw-rts .cw-rts-single{min-width:0;padding:2px}\r\n.cw-rts .cw-rts-selcmds{grid-template-columns:repeat(3,minmax(0,1fr));gap:2px}\r\n.cw-rts .cw-rts-selcmds .cw-rts-cmd{min-width:0;padding:0;font-size:10px}\r\n/* Next unlocks (unlocks-panel.mjs) in the base Sheet: pixel HUD, same palette as the selection panel. */\r\n.cw-rts .cw-rts-unlocks{box-sizing:border-box;margin:8px;padding:8px;background:#0d1217e6;border:2px solid var(--cw-faction,#7fd4ff);color:#dfe8ee;font:12px var(--num)}\r\n.cw-rts .cw-rts-unlocks-title{margin:0 0 6px;font:14px var(--num);letter-spacing:.08em;color:var(--cw-faction,#7fd4ff)}\r\n.cw-rts .cw-rts-unlocks-head{display:grid;grid-template-columns:auto 1fr;gap:4px 8px;align-items:center;margin-bottom:8px}\r\n.cw-rts .cw-rts-unlocks-lv{font:16px var(--num);color:#fff}\r\n.cw-rts .cw-rts-unlocks-xp{justify-self:end;color:#b8c4cc}\r\n.cw-rts .cw-rts-xpbar{grid-column:1/-1;height:8px;background:#2a1a1a;position:relative;overflow:hidden;box-shadow:0 0 0 1px #000}\r\n.cw-rts .cw-rts-xpfill{position:absolute;left:0;top:0;bottom:0;background:var(--cw-faction,#7fd4ff)}\r\n.cw-rts .cw-rts-unlock-list{list-style:none;margin:0;padding:0;display:grid;gap:4px}\r\n.cw-rts .cw-rts-unlock{display:grid;grid-template-columns:1fr auto;gap:2px 6px;padding:4px 6px;background:#1b222a;border:1px solid #4a5662;min-height:40px;box-sizing:border-box}\r\n.cw-rts .cw-rts-unlock.is-unlocked{border-color:#4fd16a}\r\n.cw-rts .cw-rts-unlock.is-next{border-color:var(--cw-faction,#7fd4ff);box-shadow:0 0 0 1px #000 inset}\r\n.cw-rts .cw-rts-unlock.is-locked{opacity:.6}\r\n.cw-rts .cw-rts-unlock-name{color:#fff}\r\n.cw-rts .cw-rts-unlock-lvl{justify-self:end;color:#b8c4cc}\r\n.cw-rts .cw-rts-unlock-state{grid-column:1/-1;font-size:10px;color:#4fd16a}\r\n.cw-rts .cw-rts-unlock.is-locked .cw-rts-unlock-state{color:#e8c547}\r\n.cw-rts .cw-rts-selstat{display:block;font:9px var(--num);color:#8fa0ab;white-space:normal;overflow-wrap:anywhere}\r\n/* R81 air troops: Move/Hold/Stop row + one-line hint under it */\r\n.cw-rts .cw-rts-selhint{display:block;min-height:12px;margin-top:2px;font-size:10px;line-height:12px;color:#9fe6b8}\r\n.cw-rts .cw-rts-selrow.is-tiles{grid-template-columns:1fr auto}\r\n.cw-rts .cw-rts-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:3px}\r\n.cw-rts .cw-rts-tile{display:block;height:44px;background:#0b0f12;border:1px solid #4fd16a;position:relative}\r\n.cw-rts .cw-rts-tile::after{content:\"\";position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(to bottom,rgba(79,209,106,.09) 0 1px,transparent 1px 3px)}\r\n.cw-rts .cw-rts-tile.is-yellow{border-color:#e8c547}\r\n.cw-rts .cw-rts-tile.is-red{border-color:#e0524a}\r\n.cw-rts .cw-rts-tile-art{display:block;width:100%;height:100%;image-rendering:pixelated;box-shadow:inset 0 0 0 2px rgba(79,209,106,.55)}\r\n.cw-rts .cw-rts-tilecount{display:block;font-size:11px;font-family:var(--num);color:#b8c4cc}\r\n/* V2-C2 multi-select: tile grid lives in the console centre box (4/row portrait, 6/row landscape); no side list. Box capped so the console stays in the bottom 40%. */\r\n.cw-rts .cw-rts-centre{max-height:140px;overflow-y:auto}\r\n.cw-rts .cw-rts-centre .cw-rts-selrow{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px;min-height:0;padding:2px}\r\n.cw-rts .cw-rts-centre .cw-rts-tiles{grid-template-columns:repeat(4,minmax(0,1fr));gap:2px}\r\n.cw-rts .cw-rts-centre .cw-rts-tile{height:auto;aspect-ratio:1}\r\n.cw-rts .cw-rts-centre .cw-rts-tilecount{font-size:10px}\r\n@media (orientation:landscape){.cw-rts .cw-rts-centre .cw-rts-tiles{grid-template-columns:repeat(6,minmax(0,1fr))}}\r\n.cw-rts .cw-rts-centre>*{flex-shrink:0}\r\n.cw-rts .cw-rts-centre{justify-content:safe center}\r\n","modules/coach-world-rts/selection-panel.mjs":"// R53 selection panel (StarCraft selection HUD). mountSelectionPanel(host,{getSelected,deselect,focus,getLevel,drawPortrait,commands})\r\n//   -> {update(force),dispose()}. getSelected()=sceneMode.getSelectedUnits() (units with instanceId, role, faction/section, label); deselect(ids[])=sceneMode.deselect.\r\n//   Rows = unit type (portrait, count, HP chips, x = deselect that type). Tap a row narrows to that type; a single unit gets the big card.\r\n//   HP is cosmetic-full until units carry hp (same honesty as the HUD portrait). Kills likewise default 0. Styles: selection-panel.css (class names from the local draft).\r\nimport {ROLES,ABILITIES} from './battle-sim.mjs';\r\nimport {levelStats} from './unit-levels.mjs';\r\nimport {elementFor} from './elements.mjs';\r\nconst ICON=n=>new URL(`./ui/icons/${n}.png`,import.meta.url).href;\r\nconst ROLE_KEY={grunt:'g',scout:'s',hauler:'h',carrier:'h',boss:'b',tower:'t',ship:'a'};\r\n// R81 air troops (ships3d getSelected): units with air:true carry their own commands {move,hold,stop}; move returns a hint string.\r\nconst AIR_CMDS=[['move','Move','move'],['hold','Hold','ship'],['stop','Stop','x']];\r\n// Mirrors battle-sim's private WPN_STEP/ARM_STEP (tenths per upgrade level). selection-panel.test.mjs checks parity against simulate().\r\nconst WPN_STEP=10,ARM_STEP=10;\r\n// V2-C6 centre portrait: loops the unit's own atlas clip at ~8 fps (drawUnitPortrait animate). One loop at a time; it stops when the card is replaced, the panel is disposed or the canvas leaves the DOM; rAF itself pauses while the tab is hidden. prefers-reduced-motion keeps the static portrait.\r\nconst reducedMotion=()=>!!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\r\nlet portraitAnim=null;\r\nconst stopPortraitAnim=()=>{if(portraitAnim){cancelAnimationFrame(portraitAnim.raf);portraitAnim=null;}};\r\nconst animatePortrait=(c,u,draw)=>{stopPortraitAnim();if(reducedMotion()||u.portrait)return;const st={raf:0,last:-1e9},t0=performance.now();portraitAnim=st;\r\n const tick=now=>{if(portraitAnim!==st)return;if(!c.isConnected){portraitAnim=null;return;}if(now-st.last>=126){st.last=now;try{draw(c.getContext('2d'),u,{animate:true,time:now-t0});}catch{/* sprite gone */}}st.raf=requestAnimationFrame(tick);};\r\n st.raf=requestAnimationFrame(tick);};\r\nconst mk=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n;};\r\nconst typeOf=u=>u.role||'worker';\r\nconst nice=t=>t[0].toUpperCase()+t.slice(1);\r\nconst hpFrac=u=>u.maxHp>0?Math.max(0,Math.min(1,(u.hp??u.maxHp)/u.maxHp)):1;\r\nconst hpName=f=>f>.66?'green':f>.33?'yellow':'red';\r\nconst fmt=n=>String(Math.round(n*100)/100);\r\n// Sim-true stats for a unit at a level: role table x levelStats(level). Display units: HP/ATK/ARM as the sim shows them (tenths / 10), RNG and SPD in tiles (256 per tile; SPD per second at 10 Hz).\r\n// levelStats().speedPct is not read by battle-sim, so SPD is the sim's base speed. Null for roles the sim does not know (workers).\r\nexport function troopStats(u,level){\r\n const key=ROLE_KEY[typeOf(u)],d=key?ROLES[key]:null;if(!d)return null;\r\n const lvl=Math.min(6,Math.max(1,Math.floor(level)||1)),s=levelStats(lvl),faction=u.faction||u.section;\r\n return {role:d.name,level:lvl,hp:d.hp/10,atk:d.atk?(d.atk+s.w*WPN_STEP)/10:0,arm:(d.ar+s.a*ARM_STEP)/10,rng:d.rng/256,spd:d.spd*10/256,\r\n  element:elementFor(faction)?.id??d.el??null,cannon:ABILITIES[faction]?.name??null};\r\n}\r\n\r\n// cardHost (optional): the single-unit card renders there (hud centre box) instead of over the scene.\r\nexport function mountSelectionPanel(host,{getSelected=()=>[],deselect=()=>{},focus=()=>{},getLevel=()=>1,drawPortrait=()=>{},cardHost=null}={}){\r\n const href=new URL('./selection-panel.css',import.meta.url).href;\r\n if(![...document.querySelectorAll('link[data-cw-sel]')].some(l=>l.href===href)){const l=mk('link');l.rel='stylesheet';l.href=href;l.dataset.cwSel='';document.head.append(l);}\r\n const root=mk('div','cw-rts-selpanel');root.dataset.selPanel='';root.setAttribute('role','group');root.setAttribute('aria-label','Selected units');root.hidden=true;host.append(root);\r\n let narrow=null,sig='',disposed=false;\r\n const portrait=(u,cls)=>{const c=mk('canvas',cls);c.width=c.height=64;try{(u.portrait||(g=>drawPortrait(g,u)))(c.getContext('2d'));}catch{/* no sprite: empty slot */}return c;};\r\n // V4-C4 green wireframe tile: the sprite's luminance drawn in green (alpha kept), cached per unit + sprite key. Tiles never animate, so the cache holds one tinted canvas per unit; capped at 96 entries.\r\nconst tileCache=new Map();\r\nconst tileArt=u=>{const c=mk('canvas','cw-rts-tile-art');c.width=c.height=64;const key=`${u.instanceId??''}|${typeOf(u)}|${u.faction||u.section||''}|${u.unitId||u.id||''}`;\r\n let entry=tileCache.get(key);\r\n if(!entry){const src=mk('canvas');src.width=src.height=64;const g=src.getContext('2d');entry={canvas:src,ready:false,promise:null};\r\n  const finish=()=>{try{const d=g.getImageData(0,0,64,64),p=d.data;for(let i=0;i<p.length;i+=4){const y=p[i]*.3+p[i+1]*.59+p[i+2]*.11,L=Math.min(255,Math.max(y*.3,(y-90)*2.2));p[i]=0;p[i+1]=L;p[i+2]=L*.3;}g.putImageData(d,0,0);}catch{/* no sprite: empty slot */}entry.ready=true;};\r\n  try{const result=(u.portrait||(q=>drawPortrait(q,u)))(g);if(result?.then)entry.promise=Promise.resolve(result).then(finish,finish);else finish();}catch{finish();}\r\n  if(tileCache.size>=96)tileCache.delete(tileCache.keys().next().value);tileCache.set(key,entry);}\r\n const paint=()=>{if(!disposed&&c.isConnected)c.getContext('2d').drawImage(entry.canvas,0,0);};\r\n if(entry.ready)c.getContext('2d').drawImage(entry.canvas,0,0);else entry.promise.then(paint);\r\n return c;};\r\nconst chips=us=>{const w=mk('span','cw-rts-hpchips');for(const u of us.slice(0,4))w.append(mk('i','cw-rts-hp is-'+hpName(hpFrac(u))));return w;};\r\n\r\n function airCmds(us){ // Move / Hold / Stop for a group of ships; the button of their current order is pressed\r\n  const c=mk('div','cw-rts-selcmds');c.dataset.airCmds='';const hint=mk('small','cw-rts-selhint');hint.dataset.selHint='';\r\n  for(const [id,label,icon] of AIR_CMDS){const fn=us[0].commands?.[id];if(!fn)continue;const b=mk('button','cw-rts-cmd');b.type='button';b.dataset.selCmd=id;b.setAttribute('aria-label',label+(us.length>1?` ${us.length} ships`:''));\r\n   b.setAttribute('aria-pressed',String(us.every(u=>u.order===id)));const i=mk('img');i.src=ICON(icon);i.alt='';i.width=i.height=16;b.append(i,mk('span','',label));\r\n   b.onclick=()=>{let r;try{r=fn();}catch{/* ship gone */}hint.textContent=typeof r==='string'?r:'';if(id!=='move'){sig='';update();}else c.querySelectorAll('[data-sel-cmd]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));};c.append(b);}\r\n  const w=mk('div','');w.append(c,hint);return w;\r\n }\r\n function card(u){\r\n  const lvl=getLevel(u),f=hpFrac(u),c=mk('div','cw-rts-single'),ts=troopStats(u,lvl)||{};c.dataset.selCard='';\r\n  const big=portrait(u,'cw-rts-bigportrait');c.append(big);animatePortrait(big,u,drawPortrait);\r\n  // Centre box (V1-C2): real hp/maxHp (role table when the unit carries no max), kills when tracked, shield only when tracked.\r\n  const max=u.maxHp??ts.hp,cur=u.hp??max,num=v=>v!=null?fmt(v):'--';\r\n  const st=mk('div','cw-rts-stats');st.append(mk('b','cw-rts-name',u.label||nice(typeOf(u))),mk('span','cw-rts-kills',`Kills: ${u.kills??0}`),\r\n   mk('span','cw-rts-hpnum',`HP ${num(cur)}/${num(max)}`),mk('span','cw-rts-shnum',u.shield!=null?`SH ${num(u.shield)}/${num(u.maxShield??u.shield)}`:'SH --/--'));\r\n  const hp=mk('div','cw-rts-hpbar');hp.setAttribute('role','img');hp.setAttribute('aria-label',`Health ${Math.round(f*100)}%`);const fill=mk('i','cw-rts-hpfill');fill.style.width=Math.round(f*100)+'%';hp.append(fill);\r\n  if((u.faction||u.section)==='yoga'){const sh=mk('i','cw-rts-shield');sh.style.width='40%';sh.title='Shield';hp.append(sh);}\r\n  const ab=mk('div','cw-rts-abilities'),on=levelStats(lvl).ability1,a1=mk('img','cw-rts-ability');a1.src=ICON('crest');a1.alt='Ability';a1.setAttribute('aria-disabled',String(!on));a1.title=on?'Ability unlocked':'Unlocks at Lv 3';ab.append(a1);\r\n  c.append(st,hp,ab);\r\n  return c; // unit commands (Move/Stop/Rally) live in the command card (hud ACT); air ships keep their Move/Hold/Stop row\r\n }\r\n\r\n function update(force){\r\n  if(disposed)return;let all=[];try{all=getSelected()||[];}catch{all=[];}\r\n  const types=[...new Set(all.map(typeOf))];if(narrow&&!types.includes(narrow))narrow=null;\r\n  const shown=narrow?all.filter(u=>typeOf(u)===narrow):all;\r\n  const s=all.map(u=>`${u.instanceId}:${Math.round(hpFrac(u)*10)}:${u.order||''}`).join()+'|'+(narrow||'')+'|'+(shown.length===1?getLevel(shown[0]):'');\r\n  if(s===sig&&!force)return;sig=s;stopPortraitAnim();root.replaceChildren();root.hidden=!all.length;if(cardHost){cardHost.replaceChildren();cardHost.hidden=true;}if(!all.length)return;\r\n  if(all.length>1||narrow){\r\n for(const t of types){const us=all.filter(u=>typeOf(u)===t),r=mk('div','cw-rts-selrow');r.dataset.selType=t;r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-pressed',String(t===narrow));r.setAttribute('aria-label',`${nice(t)} x${us.length}${t===narrow?', narrowed':''}`);if(t===narrow)r.classList.add('is-active');\r\n  if(all.length>1) r.classList.add('is-tiles');\r\n  const pick=()=>{narrow=t===narrow?null:t;sig='';try{focus(us.map(u=>u.instanceId));}catch{/* focus is optional */}update();};\r\n  r.onclick=pick;r.onkeydown=e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();pick();}};\r\n  const mid=mk('span',''); \r\n  if(all.length>1){\r\n    const tilesDiv=mk('div','cw-rts-tiles');\r\n    for(const u of us){const tile=mk('span',`cw-rts-tile is-${hpName(hpFrac(u))}`);tile.append(tileArt(u));tilesDiv.append(tile);}\r\n    mid.append(tilesDiv, mk('small','cw-rts-tilecount',`${us.length} selected`));\r\n  }else{\r\n    mid.append(mk('b','cw-rts-selcount','x'+us.length));\r\n  }\r\n  const ts=troopStats(us[0],getLevel(us[0]));if(ts)mid.append(mk('small','cw-rts-selstat',`HP ${fmt(ts.hp)} ATK ${fmt(ts.atk)} RNG ${fmt(ts.rng)} SPD ${fmt(ts.spd)} Lv${ts.level}`));\r\n  const x=mk('button','cw-rts-selx','×');x.type='button';x.setAttribute('aria-label',`Deselect ${nice(t)}`);x.onclick=e=>{e.stopPropagation();deselect(us.map(u=>u.instanceId));sig='';update();};\r\n  if(all.length>1){r.append(mid,x);}else{r.append(portrait(us[0],'cw-rts-selportrait'),mid,x);}\r\n  (cardHost||root).append(r);\r\n }\r\n}\r\n  if(shown.length===1){const c=card(shown[0]);if(cardHost){cardHost.append(c);cardHost.hidden=false;}else root.append(c);}\r\n  if(shown.length&&shown.every(u=>u.air))root.append(airCmds(shown));\r\n  if(cardHost)cardHost.hidden=!cardHost.childElementCount;\r\n  root.hidden=!root.childElementCount;\r\n }\r\n update();\r\n return {update,dispose(){disposed=true;stopPortraitAnim();root.remove();}};\r\n}\r\n","modules/coach-world-rts/sfx.mjs":"// Coach World RTS cues, played through the app's shared PhysicalSound (mute/volume pref, iOS gesture unlock, tab-hidden guard).\r\n// select-ack/mine-strike reuse existing cues; deposit is a tiny original WebAudio ping. Positional pan is not wired (x,y accepted for later).\r\nimport {physicalSound} from '../../audio/physical-sound.mjs';\r\n\r\nconst MIN_GAP_MS={'select-ack':60,'mine-strike':250,deposit:120,'build-start':200,'build-complete':300,'level-up':300,'unit-ready':300};\r\n// original WebAudio notes (Hz, offset s): build-start low double tick, build-complete 2-note fifth, level-up 3-note arpeggio\r\nconst NOTES={'build-start':[[196,0],[262,.06]],'build-complete':[[523,0],[784,.11]],'level-up':[[523,0],[659,.09],[784,.18]],'unit-ready':[[659,0],[880,.07]],'tranq-zap':[[880,0],[440,.05],[220,.1]],snore:[[196,0],[165,.25]],'beam-up':[[300,0],[450,.12],[600,.24],[900,.36]],spooked:[[880,0],[1100,.06]],poof:[[600,0],[300,.07],[150,.14]],caught:[[523,0],[659,.1],[784,.2],[1047,.3]],wobble:[[330,0],[300,.06]]}; // mine-strike max 4/s\r\nconst REUSE={'select-ack':'mechanical','mine-strike':'cogs'};\r\n\r\nexport function createSfx({sound=physicalSound,now=()=>performance.now()}={}){\r\n const last=new Map();let trip=0,tripAt=-Infinity;\r\n sound.mount?.(); // idempotent; binds pointerdown/keydown unlock\r\n function play(cue,_pos){\r\n  try{\r\n   const gap=MIN_GAP_MS[cue];if(gap===undefined||sound.muted||!sound.volume)return false;\r\n   const t=now();if(t-(last.get(cue)??-Infinity)<gap)return false;last.set(cue,t);\r\n   if(REUSE[cue]){sound.play(REUSE[cue]);return true;}\r\n   if(NOTES[cue]){sound.unlock?.();sound.voice?.(cue,.3,x=>{for(const [f,d] of NOTES[cue])sound.tone(x,f,.16,.09,'triangle',f,d);});return true;}\r\n   // deposit: +1 semitone per consecutive trip (within 8s), up to 5\r\n   trip=t-tripAt<8000?Math.min(trip+1,5):0;tripAt=t;const f=1568*2**(trip/12);\r\n   sound.unlock?.();\r\n   sound.voice?.('deposit',.3,x=>{sound.tone(x,f,.16,.09,'sine',f);sound.tone(x,f*1.5,.2,.05,'sine',f*1.5,.07);});\r\n   return true;\r\n  }catch{return false;}\r\n }\r\n return {play};\r\n}\r\nexport const {play}=createSfx();\r\n","modules/coach-world-rts/ship-path.mjs":"export function figure8(t, {cx = 0, cz = 0, rx = 8, rz = 5, y = 6} = {}) {\r\n    const a = t * 2 * Math.PI / 20;\r\n    const x = cx + rx * Math.sin(a);\r\n    const z = cz + rz * Math.sin(2 * a) / 2;\r\n    const dx = rx * Math.cos(a);\r\n    const dz = rz * Math.cos(2 * a);\r\n    const yaw = Math.atan2(dx, dz);\r\n    let roll = 0.35 * Math.sin(2 * a);\r\n    if (roll > 0.35) roll = 0.35;\r\n    else if (roll < -0.35) roll = -0.35;\r\n    return {x, y, z, yaw, roll};\r\n}\r\n\r\nexport function createRibbon(T, {points = 24, color = 0xffffff, width = 0.25} = {}) {\r\n    const geometry = new T.BufferGeometry();\r\n    const material = new T.MeshBasicMaterial({\r\n        color,\r\n        transparent: true,\r\n        opacity: 1,\r\n        blending: T.AdditiveBlending,\r\n        depthWrite: false,\r\n        side: T.DoubleSide,\r\n        vertexColors: true\r\n    });\r\n    const mesh = new T.Mesh(geometry, material);\r\n    const buffer = new Array(points).fill(null);\r\n    let start = 0;\r\n    let count = 0;\r\n\r\n    function push(pos) {\r\n        const idx = (start + count) % points;\r\n        buffer[idx] = {x: pos.x, y: pos.y, z: pos.z};\r\n        if (count < points) {\r\n            count++;\r\n        } else {\r\n            start = (start + 1) % points;\r\n        }\r\n    }\r\n\r\n    function update() {\r\n        if (count < 2) return;\r\n        const segs = count - 1;\r\n        const vertCount = segs * 6;\r\n        const posArray = new Float32Array(vertCount * 3);\r\n        const alphaArray = new Float32Array(vertCount);\r\n        const colArray = new Float32Array(vertCount * 3);\r\n        const base = new T.Color(material.color);\r\n        let vi = 0, ai = 0;\r\n\r\n        for (let i = 0; i < segs; i++) {\r\n            const idx0 = (start + i) % points;\r\n            const idx1 = (start + i + 1) % points;\r\n            const p0 = buffer[idx0];\r\n            const p1 = buffer[idx1];\r\n\r\n            let prev, next;\r\n            if (i === 0) {\r\n                prev = p0;\r\n                next = buffer[(start + i + 2) % points] || p1;\r\n            } else if (i === segs - 1) {\r\n                prev = buffer[(start + i - 1) % points];\r\n                next = p1;\r\n            } else {\r\n                prev = buffer[(start + i - 1) % points];\r\n                next = buffer[(start + i + 2) % points];\r\n            }\r\n            const dirX = next.x - prev.x;\r\n            const dirZ = next.z - prev.z;\r\n            let len = Math.hypot(dirX, dirZ);\r\n            if (len === 0) len = 1;\r\n            const perpX = -dirZ / len;\r\n            const perpZ = dirX / len;\r\n            const halfW = width / 2;\r\n\r\n            const left0 = {x: p0.x + perpX * halfW, y: p0.y, z: p0.z + perpZ * halfW};\r\n            const right0 = {x: p0.x - perpX * halfW, y: p0.y, z: p0.z - perpZ * halfW};\r\n            const left1 = {x: p1.x + perpX * halfW, y: p1.y, z: p1.z + perpZ * halfW};\r\n            const right1 = {x: p1.x - perpX * halfW, y: p1.y, z: p1.z - perpZ * halfW};\r\n\r\n            const alpha0 = i / (points - 1);\r\n            const alpha1 = (i + 1) / (points - 1);\r\n\r\n            // Triangle 1\r\n            posArray[vi++] = left0.x; posArray[vi++] = left0.y; posArray[vi++] = left0.z;\r\n            alphaArray[ai++] = alpha0;\r\n            posArray[vi++] = right0.x; posArray[vi++] = right0.y; posArray[vi++] = right0.z;\r\n            alphaArray[ai++] = alpha0;\r\n            posArray[vi++] = left1.x; posArray[vi++] = left1.y; posArray[vi++] = left1.z;\r\n            alphaArray[ai++] = alpha1;\r\n\r\n            // Triangle 2\r\n            posArray[vi++] = right0.x; posArray[vi++] = right0.y; posArray[vi++] = right0.z;\r\n            alphaArray[ai++] = alpha0;\r\n            posArray[vi++] = left1.x; posArray[vi++] = left1.y; posArray[vi++] = left1.z;\r\n            alphaArray[ai++] = alpha1;\r\n            posArray[vi++] = right1.x; posArray[vi++] = right1.y; posArray[vi++] = right1.z;\r\n            alphaArray[ai++] = alpha1;\r\n        }\r\n\r\n        geometry.setAttribute('position', new T.BufferAttribute(posArray, 3));\r\n        for (let k = 0; k < vertCount; k++) {\r\n            colArray[k * 3] = base.r * alphaArray[k]; colArray[k * 3 + 1] = base.g * alphaArray[k]; colArray[k * 3 + 2] = base.b * alphaArray[k];\r\n        }\r\n        geometry.setAttribute('color', new T.BufferAttribute(colArray, 3));\r\n        geometry.setAttribute('alpha', new T.BufferAttribute(alphaArray, 1));\r\n        geometry.attributes.position.needsUpdate = true;\r\n        geometry.attributes.alpha.needsUpdate = true;\r\n    }\r\n\r\n    function dispose() {\r\n        geometry.dispose();\r\n        material.dispose();\r\n    }\r\n\r\n    return {push, update, mesh, dispose};\r\n}\r\n","modules/coach-world-rts/ship-roster-terminal.mjs":"import {SHIP_ROSTER,SHIP_TIERS,SHIP_VARIANTS,SHIP_DIRECTIONS,SHIP_STATES,SHIP_CLIP_MS,SHIP_VARIANT_LABELS,SHIP_STATE_LABELS,paintShip,shipAvailability,selectedHull,liveShipTier} from './ship-roster.mjs';\r\n\r\nconst el=(d,tag,cls='',text='')=>{const n=d.createElement(tag);if(cls)n.className=cls;if(text)n.textContent=text;return n;};\r\n// Only touch the DOM when a value really changes: the HUD watches this subtree with a MutationObserver.\r\nconst setText=(n,v)=>{if(n.textContent!==v)n.textContent=v;};\r\nconst setAttr=(n,k,v)=>{if(n.getAttribute(k)!==v)n.setAttribute(k,v);};\r\nconst CSS='.ship-roster-terminal{font:14px system-ui;color:#e7f5ff;background:#0b1723;border:1px solid #31506a;border-radius:14px;padding:12px;max-width:720px}.ship-roster-terminal *{box-sizing:border-box}.ship-roster-terminal header{display:flex;justify-content:space-between;align-items:center;gap:8px}.ship-roster-terminal h3{font-size:15px;letter-spacing:.12em;margin:0}.ship-roster-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin:10px 0}.ship-roster-terminal button,.ship-roster-terminal select{min-height:44px;min-width:44px;width:100%;color:inherit;background:#13283a;border:1px solid #42627b;border-radius:8px;padding:8px;font:inherit}.ship-roster-terminal button[aria-pressed=true]{border-color:#74d9ff;box-shadow:inset 0 0 0 1px #74d9ff}.ship-roster-terminal button[data-locked]{color:#8aa3b5}.ship-roster-stage{display:grid;grid-template-columns:minmax(140px,220px) 1fr;gap:12px;align-items:center}.ship-roster-stage canvas{width:100%;height:auto;image-rendering:pixelated;border-radius:10px}.ship-roster-controls{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-top:8px}.ship-roster-controls label{display:grid;gap:2px;font-size:12px;color:#b4c8d5}.ship-roster-note{color:#b4c8d5;font-size:12px}@media(max-width:480px){.ship-roster-stage{grid-template-columns:112px 1fr}.ship-roster-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}';\r\nconst status=(base,id)=>shipAvailability(base,id)==='Locked'?'UNCHARTED':selectedHull(base).id===id?'IN FLIGHT':'DOCKED';\r\n\r\nexport function mountShipRosterTerminal(host,{getBase=()=>null}={}){\r\n const d=host.ownerDocument||document,root=el(d,'section','ship-roster-terminal'),style=el(d,'style','',CSS);\r\n const head=el(d,'header'),title=el(d,'h3','','HANGAR REGISTRY'),ownership=el(d,'span','ship-roster-note'),list=el(d,'div','ship-roster-grid'),stage=el(d,'div','ship-roster-stage'),canvas=el(d,'canvas'),info=el(d,'div'),meta=el(d,'p'),note=el(d,'p','ship-roster-note','Uncharted hulls are earned in training. The hangar launches whichever docked hull you commission.'),controls=el(d,'div','ship-roster-controls');\r\n canvas.width=canvas.height=192;canvas.setAttribute('role','img');head.append(title,ownership);stage.append(canvas,info);info.append(meta,note,controls);root.append(style,head,list,stage);host.append(root);\r\n let base=getBase();const hull=selectedHull(base);\r\n const view={id:hull.id,tier:liveShipTier(base),variant:SHIP_VARIANTS[hull.variant],direction:'SE',state:'idle'};\r\n let raf=0,disposed=false,started=performance.now();\r\n const reduced=()=>d.defaultView?.matchMedia?.('(prefers-reduced-motion: reduce)').matches||false;\r\n const buttons=SHIP_ROSTER.map(ship=>{const b=el(d,'button');b.type='button';b.dataset.id=ship.id;b.onclick=()=>{if(view.id===ship.id)return;view.id=ship.id;sync();restart();};list.append(b);return b;});\r\n const selects={};\r\n for(const [label,key,values,names] of [['Tier','tier',SHIP_TIERS,v=>`Class ${v}`],['Marking','variant',SHIP_VARIANTS,v=>SHIP_VARIANT_LABELS[v]],['Facing','direction',SHIP_DIRECTIONS,v=>v],['State','state',SHIP_STATES,v=>SHIP_STATE_LABELS[v]]]){\r\n  const wrap=el(d,'label','',label),sel=el(d,'select');sel.setAttribute('aria-label',label);\r\n  for(const value of values){const o=el(d,'option','',names(value));o.value=String(value);sel.append(o);}\r\n  sel.value=String(view[key]);sel.onchange=()=>{view[key]=key==='tier'?Number(sel.value):sel.value;sync();restart();};wrap.append(sel);controls.append(wrap);selects[key]=sel;\r\n }\r\n function sync(){ // idempotent: an unchanged base/view writes nothing\r\n  SHIP_ROSTER.forEach((ship,i)=>{const b=buttons[i],st=status(base,ship.id);setText(b,`${ship.label} · ${st}`);setAttr(b,'aria-pressed',String(ship.id===view.id));if(st==='UNCHARTED'){if(!b.hasAttribute('data-locked'))b.setAttribute('data-locked','');}else if(b.hasAttribute('data-locked'))b.removeAttribute('data-locked');});\r\n  const ship=SHIP_ROSTER.find(s=>s.id===view.id);setText(ownership,status(base,view.id));setText(meta,`${ship.label} · ${ship.frame}. ${ship.role}`);\r\n  setAttr(canvas,'aria-label',`${ship.label} ${ship.frame}, class ${view.tier} ${SHIP_VARIANT_LABELS[view.variant]} livery, ${SHIP_STATE_LABELS[view.state].toLowerCase()}`);\r\n  for(const [k,sel] of Object.entries(selects))if(sel.value!==String(view[k]))sel.value=String(view[k]);\r\n }\r\n function draw(t){\r\n  if(disposed)return;\r\n  if(root.getClientRects().length&&!d.hidden){const clip=SHIP_CLIP_MS[view.state],el2=t-started,time=clip?el2%(clip+500):el2; // one-shots replay with a short pause\r\n   paintShip({canvas,...view,time,background:true,labels:true});}\r\n  if(!reduced())raf=requestAnimationFrame(draw);\r\n }\r\n function restart(){cancelAnimationFrame(raf);started=performance.now();draw(started);}\r\n sync();restart();\r\n return {update(next=getBase()){base=next;sync();},dispose(){disposed=true;cancelAnimationFrame(raf);root.remove();}};\r\n}\r\n","modules/coach-world-rts/ship-roster.mjs":"import {SHIP_IDS,hangarShipTier} from '../coach-world/catalog.mjs';\r\n\r\nexport {SHIP_IDS};\r\nexport const SHIP_TIERS=Object.freeze([1,2,3,4]);\r\nexport const SHIP_VARIANTS=Object.freeze(['standard','reinforced','scout','veteran']); // index = base.ships[id].variant (0..3)\r\nexport const SHIP_DIRECTIONS=Object.freeze(['N','NE','E','SE','S','SW','W','NW']); // = clips.mjs SECTORS; N = bow away from the viewer\r\nexport const SHIP_STATES=Object.freeze(['idle','fly','attack','hit','death']);\r\nexport const SHIP_RES=48; // native sprite raster (crew billboard canvas size)\r\nexport const SHIP_CLIP_MS=Object.freeze({attack:420,hit:210,death:966}); // same one-shot lengths as raid-appearance raidClip\r\nconst FRAME_MS=60,LOOP_MS=120,LOOP_FRAMES=4;\r\n\r\nconst DATA={\r\n supportive:['Supportive','Escort frigate','#62d8ff','#e9fbff','Shields the strike line and keeps the wing flying.'],\r\n direct:['Direct','Lance corvette','#ff765f','#ffd1a1','Spear-point interceptor. First in, twin lances hot.'],\r\n analytical:['Analytical','Survey cruiser','#ad91ff','#e2d8ff','Long-range sensor array. Spots ambushes before they spring.'],\r\n playful:['Playful','Skirmisher','#ffd34e','#fff4c2','Swept-wing dogfighter. Too quick to pin down.'],\r\n calm:['Calm','Bulwark','#76e19b','#d9ffe4','Armored escort that soaks fire for the fleet.'],\r\n mom:['Mom','Matriarch','#ff83bd','#fff0f7','Fleet flagship. Launches the wing and brings it home.'],\r\n};\r\nexport const SHIP_VARIANT_LABELS=Object.freeze({standard:'Line',reinforced:'Armored',scout:'Pathfinder',veteran:'Veteran'});\r\nexport const SHIP_STATE_LABELS=Object.freeze({idle:'Station keeping',fly:'Cruise',attack:'Weapons hot',hit:'Shield breach',death:'Hull loss'});\r\nexport const SHIP_ROSTER=Object.freeze(SHIP_IDS.map((id,index)=>{const [label,frame,color,plate,role]=DATA[id];return Object.freeze({id,label,frame,color,plate,role,index,air:true,tiers:SHIP_TIERS,variants:SHIP_VARIANTS,states:SHIP_STATES,directions:SHIP_DIRECTIONS});}));\r\nexport const shipRecord=id=>SHIP_ROSTER.find(s=>s.id===id)||SHIP_ROSTER[0];\r\nconst owns=(base,id)=>id==='supportive'||Object.hasOwn(base?.ships??{},id);\r\n// base.ships = {[id]:{tier,variant}} (coach-world state.mjs); supportive is the starter hull every station has.\r\nexport function shipAvailability(base,id){return owns(base,id)?'Owned':'Locked';}\r\n// The hull the station actually flies: base.selectedShip when owned, else the starter.\r\nexport function selectedHull(base){const id=SHIP_IDS.includes(base?.selectedShip)&&owns(base,base.selectedShip)?base.selectedShip:'supportive',v=Number(base?.ships?.[id]?.variant)|0;return {id,variant:Math.max(0,Math.min(3,v))};}\r\nexport const liveShipTier=base=>hangarShipTier(base?.buildings?.hangar?.level??1);\r\n// One-shot states play once and hold their last frame; idle/fly loop.\r\nexport function shipFrame(state,time=0){const t=Math.max(0,Number(time)||0),ms=SHIP_CLIP_MS[state];return ms?Math.min(Math.floor(ms/FRAME_MS),Math.floor(t/FRAME_MS)):Math.floor(t/LOOP_MS)%LOOP_FRAMES;}\r\nexport const shipFrameCount=state=>SHIP_CLIP_MS[state]?Math.floor(SHIP_CLIP_MS[state]/FRAME_MS)+1:LOOP_FRAMES;\r\n\r\n// ---- native pixel hulls. Local frame: bow toward -y, 1 unit = 1 px at tier scale 1; mirrored shapes test |x|. Kept inside r<=18 so tier 4 (x1.1) never clips.\r\nconst E=(cx,cy,rx,ry)=>(x,y)=>{const a=(Math.abs(x)-cx)/rx,b=(y-cy)/ry;return a*a+b*b<=1;};\r\nconst E1=(cx,cy,rx,ry)=>(x,y)=>{const a=(x-cx)/rx,b=(y-cy)/ry;return a*a+b*b<=1;}; // unmirrored\r\nconst R=(x0,y0,x1,y1)=>(x,y)=>{const a=Math.abs(x);return a>=x0&&a<=x1&&y>=y0&&y<=y1;};\r\nconst P=pts=>(x,y)=>{const a=Math.abs(x);let inside=false;for(let i=0,j=pts.length-1;i<pts.length;j=i++){const [xi,yi]=pts[i],[xj,yj]=pts[j];if((yi>y)!==(yj>y)&&a<(xj-xi)*(y-yi)/(yj-yi)+xi)inside=!inside;}return inside;};\r\nconst RING=(cy,r0,r1,minY)=>(x,y)=>{const d=Math.hypot(x,y-cy);return d>=r0&&d<=r1&&y>=minY;};\r\n// materials\r\nconst HULL=1,PLATE=2,TRIM=3,DARK=4,GLOW=5,NOZ=6;\r\n// Each hull: shapes [material,test] painted in order, engines [x,y,r] (mirrored), guns [x,y] (mirrored).\r\nconst HULLS={\r\n supportive:L=>({shapes:[\r\n  [HULL,E(0,0,4.5,13)],[HULL,R(3,-1,7,5)],[HULL,E(8,3,3,7)],[PLATE,E(0,-6,2.4,6)],[TRIM,E(8,-2,1.6,2.4)],\r\n  ...(L>=2?[[PLATE,E(8,5,1.4,3)]]:[]),...(L>=3?[[HULL,RING(2,11.5,13,-3)],[TRIM,RING(2,11.5,12.2,-3)]]:[]),...(L>=4?[[HULL,E(13,10,2.2,4)],[GLOW,E(13,7.5,1,1.2)]]:[]),\r\n  [GLOW,E(0,-8,1.5,2.5)],[DARK,R(0,3,3,4)],[NOZ,E(2,12.5,1.5,1.4)],[NOZ,E(8,9.5,1.4,1)]],\r\n  engines:[[2,12.5,1.5],[8,9.5,1.4],...(L>=4?[[13,13.5,1.2]]:[])],guns:[[8,-6],...(L>=3?[[0,-13]]:[])]}),\r\n direct:L=>({shapes:[\r\n  [HULL,P([[0,-18],[2.5,-8],[3,12],[0,14]])],[HULL,P([[2.5,0],[8+L,9],[8+L,12],[3,9]])],[TRIM,P([[2.5,0],[8+L,9],[7+L,10],[2.5,2]])],\r\n  ...(L>=2?[[HULL,R(4,-14,5.2,3)],[GLOW,R(4,-15,5.2,-13)]]:[]),...(L>=3?[[HULL,P([[2.5,-9],[6.5,-5],[2.5,-5]])]]:[]),...(L>=4?[[NOZ,R(7+L,5,8+L,12)],[HULL,P([[3,8],[6,14],[3,13]])]]:[]),\r\n  [PLATE,R(0,-6,1,8)],[GLOW,E(0,-9,1.2,2.6)],[DARK,R(0,4,2,5)],[NOZ,E(0,13,2,1.5)]],\r\n  engines:[[0,13,2],...(L>=4?[[4.5,13.5,1.2]]:[])],guns:[[0,-18],...(L>=2?[[4.6,-15]]:[])]}),\r\n analytical:L=>({shapes:[\r\n  [HULL,P([[0,-10],[5,-6],[5,8],[0,12]])],[PLATE,E(0,-12,4+L*.5,3)],[GLOW,E(0,-12,1.6,1.1)],[DARK,R(0,-1,5,0)],\r\n  ...(L>=2?[[HULL,R(5,-2.5,9+L,-.6)],[GLOW,E(9+L,-1.5,1.5,1.5)]]:[]),...(L>=3?[[HULL,R(5,5,7+L,6.6)],[TRIM,E(7+L,5.8,1.3,1.3)]]:[]),...(L>=4?[[HULL,RING(4,9,10.4,8)]]:[]),\r\n  [TRIM,R(0,3,4,4)],[GLOW,E(0,-4,1.3,2)],[NOZ,E(2.5,12,1.5,1.5)]],\r\n  engines:[[2.5,12,1.5]],guns:[[0,-15],...(L>=2?[[9+L,-3]]:[])]}),\r\n playful:L=>({shapes:[\r\n  [HULL,E(0,0,5,7.5)],[HULL,P([[3,1],[10+L*.6,-7],[12+L*.6,-4],[4,6]])],[HULL,P([[1.5,5],[6,12],[4,12.5],[1,8]])],[TRIM,P([[10+L*.6,-7],[12+L*.6,-4],[11+L*.6,-3.5],[9.5+L*.6,-6]])],\r\n  ...(L>=2?[[TRIM,E(11+L*.6,-6,1.4,2.6)]]:[]),...(L>=3?[[HULL,R(2,-11,3.2,-5)],[GLOW,R(2,-12,3.2,-10.5)]]:[]),...(L>=4?[[HULL,P([[4,6],[9,9],[8,11],[3.5,8]])]]:[]),\r\n  [GLOW,E(0,-2,2.6,3.2)],[PLATE,E(0,-3.5,1,1.2)],[NOZ,E(0,8,2,1.5)]],\r\n  engines:[[0,8.5,2],[5,12.5,1.1]],guns:[[11+L*.6,-9],...(L>=3?[[2.6,-12]]:[])]}),\r\n calm:L=>({shapes:[\r\n  [HULL,P([[0,-12],[5,-12],[8,-8],[8,10],[6,13],[0,13]])],...(L>=2?[[PLATE,R(8,-6,10,8)]]:[]),...(L>=3?[[PLATE,R(10,-3,11.6,6)],[DARK,R(8,1,11.6,2)]]:[]),\r\n  [DARK,R(0,-9,6,-8)],[DARK,R(0,0,7,1)],[PLATE,R(0,4,3,10)],[GLOW,R(0,5,2,6.5)],...(L>=4?[[TRIM,E(4,-4,2.1,2.1)],[DARK,E(4,-4,.8,.8)]]:[]),\r\n  [NOZ,E(2.5,13,1.5,1.5)],[NOZ,E(6,12.2,1.3,1.3)]],\r\n  engines:[[2.5,13,1.5],[6,12.2,1.3]],guns:[[4,-12],...(L>=4?[[4,-6.5]]:[])]}),\r\n mom:L=>({shapes:[\r\n  [HULL,P([[0,-16],[4,-10],[13,7],[11.5,11],[5,8],[0,12]])],[PLATE,R(0,-14,1.8,10)],[DARK,R(5.5,2,8.5,4.5)],\r\n  ...(L>=2?[[HULL,E(12.5,3,2,5)],[TRIM,E(12.5,0,1,1.5)]]:[]),...(L>=3?[[GLOW,E(0,-6,2,3)]]:[[GLOW,E(0,-6,1.3,2.2)]]),...(L>=4?[[HULL,P([[2.5,-11],[5,-16],[6,-9.5]])],[TRIM,P([[4.2,-14],[5,-16],[5.4,-13]])]]:[]),\r\n  [TRIM,P([[4,-10],[13,7],[12,7.5],[3.6,-8.5]])],[NOZ,E(3,11,1.5,1.5)],[NOZ,E(10,10.5,1.2,1.2)]],\r\n  engines:[[3,11,1.5],[10,10.5,1.2]],guns:[[5,-15],[12.5,-2]]}),\r\n};\r\n// variant: silhouette scale (reinforced wider, scout sleeker) + decals painted only on hull pixels.\r\nconst VARIANT_FORM=[[1,1],[1.12,1],[.86,1.06],[1,1]];\r\nconst VARIANT_STRIPE=['#e7f5ff','#c8d2dc','#8bfff1','#ffd75e'];\r\nconst hex=s=>{const n=parseInt(s.slice(1),16);return [n>>16&255,n>>8&255,n&255];};\r\nconst mix=(a,b,k)=>a.map((v,i)=>Math.round(v+(b[i]-v)*k));\r\nconst WHITE=[255,255,255],BLACK=[0,0,0];\r\nconst hash=(a,b,c=0)=>{let h=Math.imul(a|0,374761393)^Math.imul(b|0,668265263)^Math.imul(c|0,2147483647);h=Math.imul(h^h>>>13,1274126177);return ((h^h>>>16)>>>0)/4294967296;};\r\n\r\nconst cache=new Map(),CACHE_MAX=600; // ponytail: FIFO pixel cache; key = every paint input, so live billboards reuse frames\r\n// Pure RGBA raster (no DOM): {width,height,data:Uint8ClampedArray}. Background always transparent.\r\nexport function shipPixels({id='supportive',tier=1,variant='standard',direction='S',state='idle',time=0,trim=null}={}){\r\n const rec=shipRecord(id),L=Math.max(1,Math.min(4,Number(tier)||1)),vi=typeof variant==='number'?Math.max(0,Math.min(3,variant|0)):Math.max(0,SHIP_VARIANTS.indexOf(variant)),s=SHIP_STATES.includes(state)?state:'idle',dir=Math.max(0,SHIP_DIRECTIONS.indexOf(direction)),f=shipFrame(s,time);\r\n const key=`${rec.id}|${L}|${vi}|${dir}|${s}|${f}|${trim||''}`;const hit=cache.get(key);if(hit)return hit;\r\n const N=SHIP_RES,C=N/2,data=new Uint8ClampedArray(N*N*4),mat=new Uint8Array(N*N),lxs=new Float32Array(N*N),lys=new Float32Array(N*N);\r\n const def=HULLS[rec.id](L),k=.7+.1*L,[sx,sy]=VARIANT_FORM[vi],a=dir*Math.PI/4,ca=Math.cos(a),sa=Math.sin(a);\r\n const death=s==='death'?f/(shipFrameCount('death')-1):0,hitF=s==='hit'?f:-1;\r\n const ox=hitF>=0?[1,-1,1,0][hitF]||0:0,oy=s==='idle'?[0,0,1,1][f]:0,spread=1+death*.7;\r\n const stripe=vi===1?y=>y>=-2&&y<=-1||y>=1&&y<=2:vi===2?(y,x)=>Math.abs(Math.abs(x)*.8+y-1)<.7:vi===3?(y,x)=>Math.abs(Math.abs(x)*.6+y+3)<.6||Math.abs(Math.abs(x)*.6+y+1)<.6:y=>y>=-1.5&&y<=-.5;\r\n for(let py=0;py<N;py++)for(let px=0;px<N;px++){\r\n  const wx=px+.5-C-ox,wy=py+.5-C-oy;if(wx*wx+wy*wy>22*22)continue; // hard radius: the outer ring of the raster is always transparent\r\n  let lx=(wx*ca+wy*sa)/k/sx/spread,ly=(-wx*sa+wy*ca)/k/sy/spread,m=0;\r\n  for(const [mm,test] of def.shapes)if(test(lx,ly))m=mm;\r\n  if(m&&death>0&&hash(Math.floor(lx/3.5),Math.floor(ly/3.5),7)<death*1.25-.1)m=0; // hull breaks up into chunks\r\n  if(m===HULL&&stripe(ly,lx))m=7;\r\n  if(m===HULL&&vi===3&&hash(Math.floor(lx),Math.floor(ly),3)>.93)m=DARK; // veteran scorch\r\n  if(m===HULL&&s!=='death'&&ly>=5.4&&ly<=6.6)for(let j=0;j<L;j++)if(Math.abs(lx-(j-(L-1)/2)*2.2)<.6)m=8; // tier pips\r\n  const i=py*N+px;mat[i]=m;lxs[i]=lx;lys[i]=ly;\r\n }\r\n const base=hex(rec.color),plate=hex(rec.plate),trimC=trim?hex(trim):mix(base,WHITE,.25),pal=[null,base,plate,trimC,mix(base,BLACK,.62),hex('#e9fbff'),hex('#2b3440'),hex(VARIANT_STRIPE[vi]),hex('#fff2b0')],outline=mix(base,BLACK,.84);\r\n const at=(x,y)=>x<0||y<0||x>=N||y>=N?0:mat[y*N+x];\r\n const put=(i,c,al=255)=>{data[i*4]=c[0];data[i*4+1]=c[1];data[i*4+2]=c[2];data[i*4+3]=al;};\r\n for(let y=0;y<N;y++)for(let x=0;x<N;x++){\r\n  const i=y*N+x,m=mat[i];if(!m)continue;\r\n  if(!at(x-1,y)||!at(x+1,y)||!at(x,y-1)||!at(x,y+1)){put(i,outline);continue;}\r\n  let c=pal[m];\r\n  if(m!==GLOW&&m!==8){ // light from screen top-left, panel seams in hull space (they rotate with the ship)\r\n   if(!at(x-1,y-1)||!at(x-2,y-2))c=mix(c,WHITE,.38);else if(!at(x+1,y+1)||!at(x+2,y+2))c=mix(c,BLACK,.4);\r\n   else if(at(x+1,y+1)!==m)c=mix(c,BLACK,.22);\r\n   if(m===HULL&&((Math.floor(lys[i]+40)%4===0)||hash(Math.floor(lxs[i]/3),Math.floor(lys[i]/3))>.86))c=mix(c,BLACK,.14);\r\n  }\r\n  if(death>0)c=mix(c,hex('#ff9f43'),Math.min(1,death*1.6));\r\n  if(hitF===0||hitF===1&&hash(x,y,f)>.5)c=mix(c,WHITE,.85);\r\n  put(i,c);\r\n }\r\n // ---- state effects (hull space, so they turn with the facing)\r\n const eff=(fn)=>{for(let py=0;py<N;py++)for(let px=0;px<N;px++){const wx=px+.5-C-ox,wy=py+.5-C-oy;if(wx*wx+wy*wy>22*22)continue;const i=py*N+px,lx=(wx*ca+wy*sa)/k,ly=(-wx*sa+wy*ca)/k,r=fn(lx,ly,wx,wy,i);if(r)put(i,r[0],r[1]??255);}};\r\n if(s!=='death'||death<.15){\r\n  const len=(s==='fly'?4.2:s==='attack'?3.2:2)+[0,.8,.3,1.1][f%4],flick=s==='hit'&&f%2;\r\n  if(!flick)eff((lx,ly,wx,wy,i)=>{if(mat[i])return null;const ax=Math.abs(lx/sx);for(const [ex,ey,er] of def.engines){const d=ly/sy-(ey+er*.6);if(d<0||d>len)continue;const w=er*(1-d/len)+.15;if(Math.abs(ax-ex)<w){const q=d/len;return [q<.25?hex('#fffbe8'):q<.6?hex('#ffd35c'):hex('#ff8a3d'),q<.6?255:200];}}return null;});\r\n }\r\n if(s==='attack'){const ph=f%4;eff((lx,ly,wx,wy,i)=>{const ax=Math.abs(lx/sx);for(const [gx,gy] of def.guns){const dy=gy-ly/sy;if(ph<2&&Math.hypot(ax-gx,dy+.4)<1.9-ph*.6)return [hex(ph?'#ff9f43':'#fff8c2')];const bolt=dy-(2+ph*4);if(Math.abs(ax-gx)<.55&&bolt>=0&&bolt<3)return [hex(bolt<1?'#ffffff':'#fff274')];}return null;});}\r\n if(hitF>=0){eff((lx,ly,wx,wy,i)=>{const r=Math.hypot(wx,wy),edge=19*k;if(r>edge-1.2&&r<edge&&wx>-4&&hitF<3)return [hex('#9fe8ff'),150-hitF*40];if(mat[i]&&hitF>=1&&hash(Math.floor(lx*.7),Math.floor(ly*.7),f)>.9)return [hex(hash(lx|0,ly|0,f+9)>.5?'#fff2b0':'#ff756b')];return null;});}\r\n if(death>0){const rad=3+death*17;eff((lx,ly,wx,wy,i)=>{const r=Math.hypot(wx,wy),n=hash(Math.floor(wx/2),Math.floor(wy/2),f),edge=rad*(.8+n*.3);if(r>edge)return null;const q=r/edge,al=Math.round(255*Math.max(0,1-Math.max(0,death-.55)/.45));if(al<8)return null;if(mat[i]&&q<.6)return null;return [q<.3&&death<.6?hex('#fffbe8'):q<.6?hex('#ffb347'):q<.85?hex('#ff5a3c'):hex('#5a2a2a'),Math.round(al*(q<.85?1:.6))];});}\r\n const out={width:N,height:N,data};if(cache.size>=CACHE_MAX)cache.delete(cache.keys().next().value);cache.set(key,out);return out;\r\n}\r\n\r\nlet scratch=null;\r\nconst makeCanvas=size=>globalThis.OffscreenCanvas?new OffscreenCanvas(size,size):globalThis.document?Object.assign(document.createElement('canvas'),{width:size,height:size}):null;\r\n// Canvas painter. Live use (default): transparent, centred, no labels. The archive passes {background:true,labels:true}.\r\n// canvas: draw into an existing canvas (crew billboards / ships3d textures) instead of allocating one.\r\nexport function paintShip({size=SHIP_RES,canvas=null,background=false,labels=false,...o}={}){\r\n canvas??=makeCanvas(size);\r\n if(!canvas){const noop=()=>{};const ctx={imageSmoothingEnabled:false,fillStyle:'',font:'',clearRect:noop,fillRect:noop,fillText:noop,drawImage:noop,putImageData:noop};return {width:size,height:size,getContext:()=>ctx};}\r\n const c=canvas.getContext('2d');if(!c)return canvas;\r\n const W=canvas.width,H=canvas.height,img=shipPixels(o),px=W/32;c.clearRect(0,0,W,H);\r\n if(background){c.fillStyle='#0d2031';c.fillRect(0,0,W,H);c.fillStyle='#16344a';for(let g=0;g<W;g+=W/8){c.fillRect(g,0,1,H);c.fillRect(0,g,W,1);}}\r\n const id=c.createImageData(SHIP_RES,SHIP_RES);id.data.set(img.data);\r\n if(W===SHIP_RES&&H===SHIP_RES&&!background)c.putImageData(id,0,0);\r\n else{scratch??=makeCanvas(SHIP_RES);scratch.getContext('2d').putImageData(id,0,0);c.imageSmoothingEnabled=false;c.drawImage(scratch,0,0,SHIP_RES,SHIP_RES,0,0,W,H);}\r\n if(labels){const L=Math.max(1,Math.min(4,Number(o.tier)||1)),v=typeof o.variant==='number'?SHIP_VARIANTS[o.variant]||'standard':SHIP_VARIANTS.includes(o.variant)?o.variant:'standard';c.fillStyle='#081721cc';c.fillRect(2*px,27*px,14*px,3.4*px);c.fillStyle='#e7f5ff';c.font=`${2.4*px}px monospace`;c.fillText(`T${L} ${SHIP_VARIANT_LABELS[v].toUpperCase()}`,3*px,29.6*px);}\r\n return canvas;\r\n}\r\n","modules/coach-world-rts/ships3d.mjs":"// R52 L4 + R81 lane A: 3D ships over the base that are air TROOPS: select, Move (fly straight, banking), Hold (figure-8 patrol), Stop (hover in place).\r\n// createShips3d(T,{getBase,scale,deps})->{group,update(t),dispose,ready,ships,getUnits,select,selected,order,deselect,pick}. t in ms.\r\n// Hook: base3d `handle.addToWorld(ships.group)`, `handle.onFrame(t=>ships.update(t))`; centre with `ships.group.position.set(handle.center.x,0,handle.center.z)` (mountShips3d).\r\n// order(ids,target) takes target {x,z} in the PARENT (base3d world group) metres, or the strings 'hold' / 'stop'. Bigger lead ship for a higher hangar tier (+ escorts).\r\nimport {figure8,createRibbon} from './ship-path.mjs';\r\nimport {FACTIONS,hangarShipTier} from '../coach-world/catalog.mjs';\r\nimport {HANGAR_SHIP} from '../coach-world/unit-catalog.mjs';\r\nimport {paintShip,selectedHull,shipFrame,SHIP_DIRECTIONS,SHIP_RES} from './ship-roster.mjs';\r\n\r\nexport const LOAD_TIMEOUT_MS=7000,STARTER_GLB='/pod/worlds/starter/supportive.glb',TRAIL_STEP_MS=50;\r\nexport const FLY=4,ARRIVE=.08,BANK=.35; // FLY: cruise in `scale` units per second; ARRIVE: stop distance (scale units)\r\nexport const shipTier=base=>hangarShipTier(base?.buildings?.hangar?.level??1);\r\nconst factionColor=base=>parseInt((FACTIONS.find(f=>f.id===base?.faction)??FACTIONS[0]).color.slice(1),16);\r\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\r\nconst wrapPi=a=>Math.atan2(Math.sin(a),Math.cos(a));\r\n\r\nasync function defaultGlb(url){\r\n const [{GLTFLoader},{MeshoptDecoder}]=await Promise.all([import('/vendor/three/GLTFLoader.js'),import('/vendor/three/meshopt_decoder.module.js')]);\r\n return (await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url)).scene;\r\n}\r\n// Only the supportive starter hull has shipped GLB geometry; every other selected hull flies as its native painted sprite (null = no GLB).\r\nasync function defaultUrl(hull){\r\n if(hull?.id&&hull.id!=='supportive')return null;\r\n try{const b=await (await import('../ships/verified-ship-assets.mjs')).localVerifiedBridge();if(b?.ownedShipIds().includes('supportive'))return b.getShipUrl('supportive');}catch{}\r\n return STARTER_GLB;\r\n}\r\nconst trimOf=base=>(FACTIONS.find(f=>f.id===base?.faction)??FACTIONS[0]).color;\r\n// Native painted hull as a camera-facing sprite. Each sprite owns a 48px texture and repaints only when its\r\n// facing (ship yaw vs camera, picked in onBeforeRender) or animation frame changes. userData.fork() = an independent copy.\r\nexport function paintedShipSprite(T,{hull,tier,trim}){\r\n const canvas=document.createElement('canvas');canvas.width=canvas.height=SHIP_RES;\r\n const tex=new T.CanvasTexture(canvas);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.colorSpace=T.SRGBColorSpace;\r\n const sprite=new T.Sprite(new T.SpriteMaterial({map:tex,transparent:true,alphaTest:.05,toneMapped:false}));sprite.name='rts-ship-sprite';\r\n let key='';const P=new T.Vector3();\r\n const paint=direction=>{const u=sprite.userData,state=u.state||'idle',k=`${direction}|${state}|${shipFrame(state,u.time)}`;if(k===key)return;key=k;paintShip({canvas,id:hull.id,variant:hull.variant,tier,trim,direction,state,time:u.time});tex.needsUpdate=true;};\r\n sprite.onBeforeRender=(r,scene,camera)=>{ // bow direction relative to the viewer -> one of 8 facings (N = flying away from camera)\r\n  sprite.getWorldPosition(P);const lx=P.x-camera.position.x,lz=P.z-camera.position.z,ll=Math.hypot(lx,lz)||1,yaw=sprite.userData.yaw||0,hx=Math.sin(yaw),hz=Math.cos(yaw);\r\n  const ang=Math.atan2(hx*(-lz/ll)+hz*(lx/ll),(hx*lx+hz*lz)/ll);paint(SHIP_DIRECTIONS[((Math.round(ang/(Math.PI/4))%8)+8)%8]);};\r\n paint('S');\r\n sprite.userData.fork=()=>paintedShipSprite(T,{hull,tier,trim});\r\n sprite.userData.dispose=()=>{tex.dispose();sprite.material.dispose();};\r\n return sprite;\r\n}\r\nfunction defaultSprite(T,base,tier,hull=selectedHull(base)){return Promise.resolve(paintedShipSprite(T,{hull,tier,trim:trimOf(base)}));}\r\n// Selection-panel portrait: the selected hull, painted natively.\r\nfunction drawShipPortrait(g,hull,tier,trim){g.imageSmoothingEnabled=false;g.clearRect(0,0,64,64);const art=paintShip({...hull,tier,trim,direction:'SE'});if(art?.width)try{g.drawImage(art,0,0,64,64);}catch{}}\r\n\r\nexport function createShips3d(T,{getBase,scale=1,deps={}}){\r\n const D={loadGlb:defaultGlb,resolveUrl:defaultUrl,makeSprite:defaultSprite,timeoutMs:LOAD_TIMEOUT_MS,...deps};\r\n const group=new T.Group(),ships=[];group.name='rts-ships';let tier=0,color=0,hullKey='',hull=selectedHull(null),gen=0,disposed=false,lastPush=-1e9,lastT=null;\r\n const sel=new Set();\r\n // one instanced shadow blob for all ships (1 draw call) + one selection ring per ship (hidden unless selected)\r\n const shadowGeo=new T.CircleGeometry(.5,12),shadowMat=new T.MeshBasicMaterial({color:0,transparent:true,opacity:.35,depthWrite:false,fog:false});\r\n const ringGeo=new T.RingGeometry(.5,.6,20),ringMat=new T.MeshBasicMaterial({color:0x57e08a,side:T.DoubleSide,transparent:true,depthTest:false,fog:false});\r\n let shadows=null;const M=new T.Matrix4(),Q=new T.Quaternion().setFromAxisAngle(new T.Vector3(1,0,0),-Math.PI/2),V=new T.Vector3(),S3=new T.Vector3();\r\n const fit=(obj,size)=>{const box=new T.Box3().setFromObject(obj),v=box.getSize(new T.Vector3()),m=Math.max(v.x,v.y,v.z)||1;obj.scale.multiplyScalar(size/m);};\r\n function clear(){for(const s of ships.splice(0)){s.ribbon.dispose();if(s.sprite)s.body.userData.dispose?.();group.remove(s.pivot,s.ribbon.mesh,s.ring);}if(shadows){group.remove(shadows);shadows.dispose?.();shadows=null;}}\r\n const holdPoint=(s,t)=>figure8(t/1000+s.phase,{cx:s.hc.x,cz:s.hc.z,rx:3.2*scale,rz:2.2*scale,y:s.alt}); // R81: a tight patrol that stays on screen over the base\r\n async function build(){\r\n  const my=++gen,base=getBase(),n=shipTier(base);tier=n;color=factionColor(base);hull=selectedHull(base);hullKey=`${hull.id}:${hull.variant}`;clear();\r\n  let model;\r\n  try{\r\n   const load=Promise.resolve(D.resolveUrl(hull)).then(url=>url?D.loadGlb(url):null);load.catch(()=>{}); // a late rejection after the timeout must not go unhandled\r\n   let timer;model=await Promise.race([load,new Promise((_,rej)=>{timer=setTimeout(()=>rej(new Error('ship load timeout')),D.timeoutMs);})]).finally(()=>clearTimeout(timer));\r\n  }catch{model=null;}\r\n  if(disposed||my!==gen)return;\r\n  const sprite=model&&n<2?null:await D.makeSprite(T,base,n,hull);if(disposed||my!==gen)return; // R81 perf: escorts are pixel sprites (a GLB ship is ~6-9k tris; 4 on screen broke the 100k budget)\r\n  shadows=new T.InstancedMesh(shadowGeo,shadowMat,n);shadows.name='rts-ship-shadows';shadows.frustumCulled=false;shadows.renderOrder=1;group.add(shadows);\r\n  let usedSprite=0;\r\n  for(let i=0;i<n;i++){ // lead ship grows with tier; escorts smaller, spread around the loop. R81: troop-sized (was n*1.2..1.8 scale: bigger than the HQ)\r\n   const glb=model&&!i,size=(i?.6:1)*(.5+.15*n)*scale*(glb?1:1.4),pivot=new T.Group(),body=glb?model.clone(true):sprite.userData?.fork?(usedSprite++?sprite.userData.fork():sprite):sprite.clone();\r\n   if(glb)fit(body,size);else body.scale.setScalar(size);pivot.add(body);const ribbon=createRibbon(T,{points:24,color,width:size*.12});ribbon.mesh.frustumCulled=false;\r\n   const ring=new T.Mesh(ringGeo,ringMat);ring.rotation.x=-Math.PI/2;ring.renderOrder=3;ring.scale.setScalar(size);ring.visible=false;ring.name='rts-ship-ring';\r\n   const s={id:'ship-'+i,air:true,pivot,body,ribbon,ring,size,phase:i*20/n,sprite:!glb,alt:(1+((i*20/n)%2)*.2)*scale,order:'hold',target:null,hc:{x:0,z:0},yaw:0,roll:0,placed:false};\r\n   const p=holdPoint(s,lastT??0);pivot.position.set(p.x,p.y,p.z);\r\n   group.add(pivot,ribbon.mesh,ring);ships.push(s);\r\n  }\r\n  for(const id of [...sel])if(!ships.some(s=>s.id===id))sel.delete(id);\r\n }\r\n const ready=build();\r\n function update(t=0){\r\n  const b=getBase();if(b&&ships.length){const h=selectedHull(b);if(shipTier(b)!==tier||factionColor(b)!==color||`${h.id}:${h.variant}`!==hullKey)build();}\r\n  const dt=lastT==null?0:clamp((t-lastT)/1000,0,.25);lastT=t;\r\n  const push=t-lastPush>=TRAIL_STEP_MS;if(push)lastPush=t;\r\n  ships.forEach((s,i)=>{\r\n   const P=s.pivot.position;let want;\r\n   if(s.order==='hold')want=holdPoint(s,t);\r\n   else if(s.order==='move')want={x:s.target.x,y:s.alt,z:s.target.z};\r\n   else want={x:P.x,y:s.alt+Math.sin(t/600+s.phase)*.08*scale,z:P.z}; // stop: hover with a slow bob\r\n   if(!s.placed){P.set(want.x,want.y,want.z);s.placed=true;if(want.yaw!=null)s.yaw=want.yaw;}\r\n   const dx=want.x-P.x,dz=want.z-P.z,d=Math.hypot(dx,dz),stepMax=FLY*scale*dt*(s.order==='hold'?1.6:1);\r\n   if(d>1e-6){const k=Math.min(1,stepMax/d);P.x+=dx*k;P.z+=dz*k;}\r\n   P.y+=(want.y-P.y)*Math.min(1,dt*3);\r\n   // heading: the path's own yaw on a figure-8 once on it, else the flight direction; bank = turn rate\r\n   const head=s.order==='hold'&&d<.5*scale?want.yaw:d>1e-3&&s.order!=='stop'?Math.atan2(dx,dz):s.yaw,dy=wrapPi(head-s.yaw);\r\n   s.yaw=wrapPi(s.yaw+dy*Math.min(1,dt*4));s.roll+=(clamp(dy*1.2,-BANK,BANK)-s.roll)*Math.min(1,dt*4);\r\n   s.pivot.rotation.set(0,s.yaw,0);s.body.rotation.z=s.sprite?0:s.roll;\r\n   if(s.sprite){const u=s.body.userData;u.yaw=s.yaw;u.time=t;u.state=s.order==='move'?'fly':'idle';}\r\n   if(s.order==='move'&&d<ARRIVE*scale){s.order='stop';s.target=null;} // arrived: hover there (SC air \"stop\")\r\n   if(shadows){M.compose(V.set(P.x,.05,P.z),Q,S3.setScalar(s.size*.8));shadows.setMatrixAt(i,M);}\r\n   s.ring.visible=sel.has(s.id);if(s.ring.visible)s.ring.position.set(P.x,.09,P.z);\r\n   if(push){s.ribbon.push(P);s.ribbon.update();}\r\n  });\r\n  if(shadows)shadows.instanceMatrix.needsUpdate=true;\r\n }\r\n const ids=x=>x==null?ships.map(s=>s.id):Array.isArray(x)?x:[x];\r\n // target in parent space -> group-local; several ships fan out around the point so they do not stack\r\n function order(which,target){\r\n  const list=ships.filter(s=>ids(which).includes(s.id));if(!list.length)return false;\r\n  list.forEach((s,k)=>{\r\n   if(target==='hold'){s.order='hold';s.hc={x:s.pivot.position.x,z:s.pivot.position.z};}\r\n   else if(target==='stop'){s.order='stop';s.target=null;}\r\n   else if(target&&Number.isFinite(target.x)&&Number.isFinite(target.z)){\r\n    const a=k*2.4,r=k?1.2*scale*Math.sqrt(k):0;s.order='move';s.target={x:target.x-group.position.x+r*Math.cos(a),z:target.z-group.position.z+r*Math.sin(a)};}\r\n  });\r\n  return true;\r\n }\r\n const unitOf=s=>{const b=getBase(),f=b?.faction||'chest';return {instanceId:s.id,id:s.id,air:true,role:HANGAR_SHIP.role,label:HANGAR_SHIP.label,faction:f,section:f,order:s.order,\r\n  x:s.pivot.position.x+group.position.x,z:s.pivot.position.z+group.position.z,alt:s.pivot.position.y,target:s.target&&{x:s.target.x+group.position.x,z:s.target.z+group.position.z},\r\n  hull:hull.id,variant:hull.variant,portrait:g=>drawShipPortrait(g,hull,tier,trimOf(b)),\r\n  commands:{move:()=>'Tap the ground to fly there',hold:()=>{order([...sel],'hold');},stop:()=>{order([...sel],'stop');}}};};\r\n return {group,update,ready,ships,order,\r\n  getUnits:()=>ships.map(unitOf),\r\n  getSelected:()=>ships.filter(s=>sel.has(s.id)).map(unitOf),\r\n  selected:()=>[...sel],\r\n  select(list){sel.clear();for(const id of ids(list??[]))if(ships.some(s=>s.id===id))sel.add(id);for(const s of ships)s.ring.visible=sel.has(s.id);},\r\n  deselect(list){for(const id of list||[])sel.delete(id);for(const s of ships)s.ring.visible=sel.has(s.id);},\r\n  // nearest ship to a screen point: project(Vector3 world, sizeMetres) -> {x,y,r?} css px or null; radius px (min hit radius). Returns ship id or null.\r\n  pick(project,p,radius=26){let best=null,bd=Infinity;for(const s of [...ships].sort((a,b)=>sel.has(b.id)-sel.has(a.id))){if(best&&sel.has(best)&&!sel.has(s.id))break; // a selected ship under the finger wins (tap it again to deselect)\r\n   const q=project(s.pivot.getWorldPosition(new T.Vector3()),s.size);if(!q)continue;const d=Math.hypot(q.x-p.x,q.y-p.y),r=Math.max(radius,q.r||0);if(d<r&&d<bd){bd=d;best=s.id;}}return best;},\r\n  dispose(){disposed=true;clear();shadowGeo.dispose();shadowMat.dispose();ringGeo.dispose();ringMat.dispose();group.removeFromParent();}};\r\n}\r\n\r\n// base3d wiring in one call: const stop=mountShips3d(T,handle,{getBase,scale}); stop.ships = the createShips3d handle. handle = mountBase3d result.\r\nexport function mountShips3d(T,handle,{getBase,scale,deps}){\r\n const s=createShips3d(T,{getBase,scale,deps});s.group.position.set(handle.center.x,0,handle.center.z);\r\n handle.addToWorld(s.group);const off=handle.onFrame(t=>s.update(t));\r\n return Object.assign(()=>{off();s.dispose();},{ships:s});\r\n}\r\n","modules/coach-world-rts/sky-alien.mjs":"// Lane F: alien sky. Stars + 4 painted planets (2 ringed, one instanced draw) + a MOM Inc. mothership billboard.\r\n// Pure pixel art painted in code into DataTextures (no canvas, no asset files, works in node tests). One GL context:\r\n// everything lives in the host scene. Host positions `group` at the camera each frame and calls update(t, pitchDeg).\r\n// Draw calls: stars 1 + planets 1 + atmosphere/carrier 1 + MOM fleet 1 = 4. Atmosphere: 448 tris, no new texture.\r\n// V3 (Daggerfall look): 360 deg sky. Planets and a 3-depth MOM fleet all round the dome (elevation 5..80), the real local sun\r\n// (elevation drives the palette: DFO SKY_KEYS blended with the faction atmosphere) and two moons with a lit-sphere terminator.\r\n// From DFO Online src/render/enhancedSky.js @ e3054735 (MIT, coach-world/vendor/dfo-LICENSE.txt): SKY_KEYS, paletteAt (verbatim\r\n// interpolation), the moon() terminator, horizon glow and sun disc/halo formulas, and the \"moon on the sun's arc, behind it\r\n// by phase\" placement. Ours: faction blend, real sun/moon clock, fleet, ridges.\r\nimport { BIOMES } from './biomes.mjs';\r\nconst clamp01=x=>Math.min(1,Math.max(0,x)),smooth=(a,b,x)=>{const t=clamp01((x-a)/(b-a));return t*t*(3-2*t);};\r\nconst rgb=h=>{const n=parseInt(h.slice(1),16);return [n>>16&255,n>>8&255,n&255];};\r\nconst BAYER=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];\r\nconst DEG=Math.PI/180;\r\nexport const PLANET_PX=256,SHIP_W=128,SHIP_H=48,STAR_COUNT=400,SKY_RADIUS=80,FLEET_W=96,FLEET_H=16;\r\n\r\n// Tiny raster: RGBA bytes, y is top-down for the painter but stored flipped so uv v=1 is the image top.\r\nexport function makeRaster(w,h){\r\n const data=new Uint8Array(w*h*4);\r\n const put=(x,y,col,s=1)=>{if(!col)return;const c=typeof col==='string'?rgb(col):col;\r\n  for(let j=0;j<s;j++)for(let i=0;i<s;i++){const X=x*s+i,Y=y*s+j;if(X<0||Y<0||X>=w||Y>=h)continue;const o=((h-1-Y)*w+X)*4;data[o]=c[0];data[o+1]=c[1];data[o+2]=c[2];data[o+3]=255;}};\r\n return {w,h,data,put};\r\n}\r\nconst hash2=(x,y,s)=>{let h=(x*374761393+y*668265263+s*2147483647)|0;h=Math.imul(h^(h>>>13),1274126177);return ((h^(h>>>16))>>>0)/4294967296;};\r\nconst vnoise=(x,y,s)=>{const xi=Math.floor(x),yi=Math.floor(y),fx=x-xi,fy=y-yi,u=fx*fx*(3-2*fx),v=fy*fy*(3-2*fy);\r\n const a=hash2(xi,yi,s),b=hash2(xi+1,yi,s),c=hash2(xi,yi+1,s),d=hash2(xi+1,yi+1,s);return a+(b-a)*u+(c-a)*v+(a-b-c+d)*u*v;};\r\n\r\n// --- planets: 4 cells of 128x128 in the 256 atlas, painted on a 64x64 logical grid at 2x so pixels stay chunky.\r\nexport const PLANETS=[\r\n {name:'ember-giant',pal:['#2a0f1c','#6b2230','#c4503a','#ee9a4a','#ffe08a'],bands:9,warp:.5,ring:{tilt:-.38,pal:['#4a2a3a','#a8604a','#f0c080']},R:17,seed:3},\r\n {name:'glacier-ringed',pal:['#0c1a3a','#1d4a7a','#3a8fb8','#8fe0e0','#f0ffff'],bands:6,warp:.25,ring:{tilt:.3,pal:['#2a3a66','#6a8fd0','#d8f0ff']},R:16,seed:7},\r\n {name:'dust-moon',pal:['#1b1428','#3d3050','#6c5a80','#a28fb0','#dccfe8'],bands:0,warp:0,craters:9,R:22,seed:11},\r\n {name:'toxic-swirl',pal:['#0e2a1c','#1f5a2a','#58a838','#b8e050','#f4ff9a'],bands:5,warp:1.6,R:24,seed:19}\r\n];\r\nfunction paintPlanet(r,ox,oy,p){ // ox,oy = cell origin in logical (64-grid) units of the 128 cell\r\n const S=2,C=32,L=(()=>{const v=[-.62,-.5,.6],n=Math.hypot(...v);return v.map(x=>x/n);})(),ring=p.ring,pal=p.pal.map(rgb);\r\n const rp=ring&&ring.pal.map(rgb),ct=Math.cos(ring?ring.tilt:0),st=Math.sin(ring?ring.tilt:0),A=30,B=7.5;\r\n const craters=[];for(let i=0;i<(p.craters||0);i++){craters.push([hash2(i,1,p.seed)*1.4-.7,hash2(i,2,p.seed)*1.4-.7,.08+hash2(i,3,p.seed)*.16]);}\r\n for(let y=0;y<64;y++)for(let x=0;x<64;x++){\r\n  const dx=x+.5-C,dy=y+.5-C,rr=Math.hypot(dx,dy);let col=null,inDisc=rr<=p.R;\r\n  if(inDisc){\r\n   const nx=dx/p.R,ny=dy/p.R,nz=Math.sqrt(Math.max(0,1-nx*nx-ny*ny));\r\n   let lit=Math.max(0,nx*L[0]+ny*L[1]+nz*L[2]);\r\n   if(p.bands){const w=p.warp*(vnoise(nx*3+p.seed,ny*3,p.seed)-.5)*2;lit+=Math.sin((ny+w*.4)*p.bands)*.13+ (vnoise(nx*6,ny*14,p.seed)-.5)*.1;}\r\n   for(const [cx,cy,cr] of craters){const d=Math.hypot(nx-cx,ny-cy);if(d<cr)lit-=.22*(1-d/cr)+(d>cr*.7?-.08:0);}\r\n   const th=(BAYER[(y&3)*4+(x&3)]+.5)/16-.5;let k=Math.floor(lit*4.2+th*.9+.15);k=Math.max(0,Math.min(4,k));\r\n   if(nz<.2)k=Math.min(k,1);col=pal[k]; // limb darkening = dark rim\r\n  }\r\n  if(ring){ // rotated ellipse; upper half (ry<0) is the far side, hidden behind the disc\r\n   const rx=dx*ct+dy*st,ry=-dx*st+dy*ct,e=Math.hypot(rx/A,ry/B);\r\n   if(e>=.62&&e<=1&&!(e>.82&&e<.87)&&!(ry<0&&inDisc)){const band=e<.76?0:e<.9?1:2;let c=rp[band];if(rx*L[0]+ry*L[1]>0&&((x+y)&1))c=rp[Math.max(0,band-1)];col=c;}\r\n  }\r\n  if(col)r.put(x+ox,y+oy,col,S);\r\n }\r\n}\r\nexport function paintPlanetAtlas(){\r\n const r=makeRaster(PLANET_PX,PLANET_PX);\r\n PLANETS.forEach((p,i)=>paintPlanet(r,(i&1)*64,(i>>1)*64,p));\r\n return r;\r\n}\r\n\r\n// --- mothership 128x48: dark saucer carrier, glass bridge dome, window rows, rim lights, blocky MOM INC mark.\r\nconst FONT={M:['10001','11011','10101','10001','10001'],O:['01110','10001','10001','10001','01110'],I:['111','010','010','010','111'],N:['10001','11001','10101','10011','10001'],C:['01111','10000','10000','10000','01111']};\r\nexport function paintMothership(){\r\n const r=makeRaster(SHIP_W,SHIP_H),cx=64,cy=29;\r\n const pal=['#14162a','#222747','#363e6a','#556092','#7a86c0'];\r\n for(let y=0;y<SHIP_H;y++)for(let x=0;x<SHIP_W;x++){\r\n  const dx=(x+.5-cx)/62,dyU=(y+.5-cy)/9,dyL=(y+.5-cy)/13;\r\n  const body=y<cy?dx*dx+dyU*dyU<=1:dx*dx+dyL*dyL<=1;\r\n  const dome=(()=>{const a=(x+.5-cx)/17,b=(y+.5-(cy-5))/13;return y<cy-3&&a*a+b*b<=1;})();\r\n  const under=y>=cy+4&&y<cy+13&&Math.abs(x+.5-cx)<(26-(y-cy-4)*2.2); // keel\r\n  let c=null;\r\n  if(body){const v=(y-(cy-9))/22,th=(BAYER[(y&3)*4+(x&3)]+.5)/16-.5;let k=Math.floor((1-v)*3.4+th*.8);c=pal[Math.max(0,Math.min(4,k))];}\r\n  if(dome){const t=(y-(cy-18))/14,th=(BAYER[(y&3)*4+(x&3)]+.5)/16-.5;c=t+th*.4<.45?'#8ff0ff':t+th*.4<.8?'#3aa0c8':'#1c5a88';}\r\n  if(under&&!body)c=y>cy+9?'#0e1020':'#1a1e38';\r\n  if(c)r.put(x,y,c);\r\n }\r\n for(let x=6;x<122;x+=2){ // window row along the hull belt\r\n  const t=(x+.5-cx)/62;if(Math.abs(t)<.93){r.put(x,cy-1,((x>>1)%5===0)?'#1a1e38':'#ffe27a');if(((x>>1)%3)===0)r.put(x,cy+2,'#fff2b0');}\r\n }\r\n for(let x=4;x<124;x+=6){const t=(x-cx)/62,yy=cy+Math.round(Math.sqrt(Math.max(0,1-t*t))*12*.55);r.put(x,yy,(x/6|0)%2?'#ff3a8c':'#ffd23a');} // rim lights\r\n let px=cx-14;for(const ch of 'MOM INC'){if(ch===' '){px+=3;continue;}const g=FONT[ch];g.forEach((row,j)=>{for(let i=0;i<row.length;i++)if(row[i]==='1')r.put(px+i,cy+4+j-3,'#ffb42a');});px+=g[0].length+1;}\r\n for(let i=-1;i<=1;i++)r.put(cx+i*16,cy+13,'#7affc8'); // tractor emitters\r\n for(let x=2;x<8;x++)r.put(x,cy-1,x<5?'#ff7a3a':'#ffd23a'); // thruster glow, left tip\r\n return r;\r\n}\r\n\r\n// --- MOM fleet atlas 96x16: three 32x16 silhouettes (saucer, cruiser, scout), same hull palette as the carrier.\r\nexport const FLEET_KINDS=['saucer','cruiser','scout'];\r\nexport function paintFleet(){\r\n const r=makeRaster(FLEET_W,FLEET_H),hull=['#14162a','#222747','#363e6a','#556092'],dith=(x,y)=>(BAYER[(y&3)*4+(x&3)]+.5)/16-.5;\r\n const shade=(x,y,v)=>hull[Math.max(0,Math.min(3,Math.floor(v*3.6+dith(x,y)*.8)))];\r\n for(let y=0;y<16;y++)for(let x=0;x<32;x++){\r\n  // saucer (cell 0): flat ellipse, glass dome, window belt\r\n  {const dx=(x+.5-16)/15,dy=(y+.5-9)/(y<9?3:4.5),dome=((x+.5-16)/5)**2+((y+.5-6.5)/3.5)**2<=1&&y<8;let c=null;\r\n   if(dx*dx+dy*dy<=1)c=shade(x,y,1-(y-6)/8);if(dome)c=y<6?'#8ff0ff':'#3aa0c8';if(y===9&&Math.abs(dx)<.85&&x%3===0)c='#ffe27a';if(c)r.put(x,y,c);}\r\n  // cruiser (cell 1): long wedge hull, bridge tower, MOM orange stripe, engine glow on the left\r\n  {const ox=32,t=(x+.5)/32,top=7-3.5*t,bot=10+1.5*(1-t),tower=x>=20&&x<25&&y>=3&&y<7;let c=null;\r\n   if((y>=top&&y<=bot&&x>=2)||tower)c=shade(x,y,1-(y-3)/9);if(y===9&&x>3&&x<29)c='#ffb42a';if(x<3&&y>=7&&y<=10)c=x<2?'#ff7a3a':'#ffd23a';if(tower&&y===4&&x%2)c='#ffe27a';if(c)r.put(x+ox,y,c);}\r\n  // scout (cell 2): delta wing pointing right, cyan canopy\r\n  {const ox=64,u=x+.5-6,half=u<0?0:Math.max(0,5.5-u*.24),cy=8.5,canopy=((x+.5-17)/3.5)**2+((y+.5-7.5)/1.6)**2<=1;let c=null;\r\n   if(u>=0&&u<=22&&Math.abs(y+.5-cy)<=half)c=shade(x,y,1-(y-3)/10);if(canopy)c='#8ff0ff';if(x===6&&Math.abs(y+.5-cy)<3)c='#ff3a8c';if(c)r.put(x+ox,y,c);}\r\n }\r\n return r;\r\n}\r\n\r\n// --- DFO enhancedSky palette (verbatim table + interpolation), blended per faction in factionSky().\r\nexport const SKY_KEYS = Object.freeze([\r\n  { elev: -90, zenith: '#04060d', horizon: '#090c16', sun: '#000000', glow: '#000000', glowAmount: 0.0, stars: 1.0 },\r\n  { elev: -18, zenith: '#060914', horizon: '#0f131f', sun: '#000000', glow: '#1a1420', glowAmount: 0.0, stars: 1.0 },\r\n  { elev: -9,  zenith: '#0c1330', horizon: '#2b2238', sun: '#000000', glow: '#6a3a3a', glowAmount: 0.30, stars: 0.85 },\r\n  { elev: -4,  zenith: '#1a2a5a', horizon: '#4e4258', sun: '#000000', glow: '#d6693a', glowAmount: 0.75, stars: 0.45 },\r\n  { elev: -2,  zenith: '#233674', horizon: '#8e6062', sun: '#e06a2c', glow: '#f07c3c', glowAmount: 0.92, stars: 0.24 },\r\n  { elev: 0,   zenith: '#2c4d8e', horizon: '#c4a49a', sun: '#ffa64e', glow: '#ff9a4c', glowAmount: 1.0, stars: 0.10 },\r\n  { elev: 2,   zenith: '#335ca4', horizon: '#e0b49c', sun: '#ffb96a', glow: '#ffac60', glowAmount: 0.84, stars: 0.03 },\r\n  { elev: 4,   zenith: '#3a68b4', horizon: '#d9c6b4', sun: '#ffd39a', glow: '#ffbd7a', glowAmount: 0.6, stars: 0.0 },\r\n  { elev: 7,   zenith: '#3c70c0', horizon: '#d2cfc8', sun: '#ffe0b8', glow: '#ffcf9c', glowAmount: 0.42, stars: 0.0 },\r\n  { elev: 12,  zenith: '#3f77cc', horizon: '#c9dcf0', sun: '#ffefd2', glow: '#ffe1b8', glowAmount: 0.28, stars: 0.0 },\r\n  { elev: 30,  zenith: '#336bc6', horizon: '#b6d0ec', sun: '#fff7e6', glow: '#fff0d8', glowAmount: 0.14, stars: 0.0 },\r\n  { elev: 90,  zenith: '#2a5ab8', horizon: '#a8c8ea', sun: '#ffffff', glow: '#ffffff', glowAmount: 0.08, stars: 0.0 },\r\n]);\r\nconst hex = (h) => [parseInt(h.slice(1, 3), 16) / 255, parseInt(h.slice(3, 5), 16) / 255, parseInt(h.slice(5, 7), 16) / 255];\r\nconst mix3 = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];\r\nconst rowOf = (k) => ({ zenith: hex(k.zenith), horizon: hex(k.horizon), sun: hex(k.sun), glow: hex(k.glow), glowAmount: k.glowAmount, stars: k.stars });\r\nexport function paletteAt(elevDeg) {\r\n  const keys = SKY_KEYS;\r\n  if (elevDeg <= keys[0].elev) return rowOf(keys[0]);\r\n  for (let i = 1; i < keys.length; i++) {\r\n    if (elevDeg <= keys[i].elev) {\r\n      const a = keys[i - 1], b = keys[i];\r\n      const t = (elevDeg - a.elev) / (b.elev - a.elev);\r\n      return {\r\n        zenith: mix3(hex(a.zenith), hex(b.zenith), t),\r\n        horizon: mix3(hex(a.horizon), hex(b.horizon), t),\r\n        sun: mix3(hex(a.sun), hex(b.sun), t),\r\n        glow: mix3(hex(a.glow), hex(b.glow), t),\r\n        glowAmount: a.glowAmount + (b.glowAmount - a.glowAmount) * t,\r\n        stars: a.stars + (b.stars - a.stars) * t,\r\n      };\r\n    }\r\n  }\r\n  return rowOf(keys[keys.length - 1]);\r\n}\r\n// Ours: the alien tint. Part DFO's real sky, part the faction atmosphere, so night keeps the faction's dark look and day lifts it.\r\nexport const FACTION_MIX=.55;\r\nexport function factionSky(atmosphere,elevDeg){\r\n const p=paletteAt(elevDeg),f=FACTION_MIX,A=Object.fromEntries(['horizon','haze','zenith','dust'].map(k=>[k,hex(atmosphere[k])]));\r\n const horizon=mix3(p.horizon,A.horizon,f),zenith=mix3(p.zenith,A.zenith,f),haze=mix3(mix3(p.horizon,p.zenith,.45),A.haze,f);\r\n return {...p,horizon,zenith,haze,dust:A.dust};\r\n}\r\n\r\n// --- the real sky clock. Sun: low-precision solar position (about 1 deg), az from north, clockwise (east = +x, north = -z).\r\nconst J2000=Date.UTC(2000,0,1,12),SYNODIC=29.530588853,NEW_MOON=Date.UTC(2000,0,6,18,14);\r\nexport function sunPosition(ms,lat,lng){\r\n const d=(ms-J2000)/864e5,g=(357.529+.98560028*d)*DEG,L=(280.459+.98564736*d+1.915*Math.sin(g)+.02*Math.sin(2*g))*DEG,e=(23.439-3.6e-7*d)*DEG;\r\n const ra=Math.atan2(Math.cos(e)*Math.sin(L),Math.cos(L)),dec=Math.asin(Math.sin(e)*Math.sin(L));\r\n const H=((280.46061837+360.98564736629*d+lng)*DEG)-ra,la=lat*DEG;\r\n const elev=Math.asin(Math.sin(la)*Math.sin(dec)+Math.cos(la)*Math.cos(dec)*Math.cos(H))/DEG;\r\n const az=Math.atan2(-Math.sin(H),Math.tan(dec)*Math.cos(la)-Math.sin(la)*Math.cos(H))/DEG;\r\n return {elev,az};\r\n}\r\n// Moon A: the real phase (0 new, .5 full). Moon B: MOM's little moon, an 11.3-day cycle riding 9 deg higher. DFO's law: a moon sits\r\n// on the sun's arc, behind it by its phase, so it stands where the sun stood `phase` of a day earlier.\r\nexport const MOONS=[{radius:.040,color:'#d39a86',period:SYNODIC,lift:0},{radius:.021,color:'#e9e6dc',period:11.3,lift:9}];\r\nexport function skyClock(ms,lat,lng){\r\n const sun=sunPosition(ms,lat,lng),days=(ms-NEW_MOON)/864e5;\r\n const moons=MOONS.map(m=>{const phase=((days/m.period)%1+1)%1,p=sunPosition(ms-phase*864e5,lat,lng);return {...m,phase,elev:p.elev+m.lift,az:p.az};});\r\n return {sun,moons};\r\n}\r\nexport const dirOf=(azDeg,elDeg)=>{const a=azDeg*DEG,e=elDeg*DEG;return [Math.sin(a)*Math.cos(e),Math.sin(e),-Math.cos(a)*Math.cos(e)];};\r\n\r\nconst POINT_V=`attribute vec3 color;attribute float tw;uniform float uTime,uSize,uFade;varying vec3 vC,vRay;varying float vA;\r\nuniform float uStars;\r\nvoid main(){vRay=position;vC=color;vA=uStars*uFade*(.7+.3*sin(uTime*(.6+tw*2.)+tw*40.));gl_PointSize=uSize;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`;\r\n// Project the existing carrier texture onto a camera-centred shell. Sharing its ray test with\r\n// stars keeps distant stars behind the opaque hull while atmosphere and carrier share one draw.\r\nconst CARRIER_GLSL=`uniform sampler2D uShipMap;uniform vec3 uShipCentre,uShipRight,uShipUp;\r\nvec4 carrier(vec3 ray){\r\n float facing=dot(ray,normalize(uShipCentre));if(facing<=.01)return vec4(0.);\r\n vec3 hit=ray*(length(uShipCentre)/facing)-uShipCentre;\r\n vec2 uv=vec2(dot(hit,uShipRight)/62.,dot(hit,uShipUp)/23.25)+.5;\r\n if(min(uv.x,uv.y)<0.||max(uv.x,uv.y)>1.)return vec4(0.);\r\n return texture2D(uShipMap,uv);\r\n}`;\r\nconst POINT_F=`${CARRIER_GLSL} varying vec3 vC,vRay;varying float vA;void main(){if(carrier(normalize(vRay)).a>.5)discard;gl_FragColor=vec4(vC,vA*smoothstep(-.015,.18,normalize(vRay).y));}`;\r\nconst QUAD_F=`uniform sampler2D uMap;uniform float uFade;varying vec2 vUv;void main(){vec4 c=texture2D(uMap,vUv);if(c.a<.5)discard;gl_FragColor=vec4(c.rgb,uFade);}`;\r\nconst PLANET_V=`attribute vec2 uvOff;varying vec2 vUv;varying vec3 vRay;void main(){vUv=uv*.5+uvOff;vec4 p=instanceMatrix*vec4(position,1.);vRay=p.xyz;gl_Position=projectionMatrix*modelViewMatrix*p;}`;\r\nconst PLANET_F=`${CARRIER_GLSL} uniform sampler2D uMap;uniform float uFade;varying vec2 vUv;varying vec3 vRay;void main(){vec4 c=texture2D(uMap,vUv);if(c.a<.5||carrier(normalize(vRay)).a>.5)discard;gl_FragColor=vec4(c.rgb,uFade);}`;\r\nconst SHIP_V=`varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`;\r\nexport const SHADERS={POINT_V,POINT_F,QUAD_F,PLANET_V,SHIP_V};\r\n\r\n// Static, direction-based atmosphere. Broad haze, two distant ridge silhouettes, a tilted dust\r\n// belt and a broken orbital arc separate horizon/mid-sky/deep space without animated noise.\r\nconst ATMOS_V=`varying vec3 vDir;void main(){vDir=position;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`;\r\nconst MOON_GLSL=`uniform vec3 uSunDir,uSunCol,uGlowCol,uMoonACol,uMoonBCol;uniform vec4 uMoonA,uMoonB;uniform float uSunVis,uGlowAmt,uMoonAVis,uMoonBVis;\r\nvec3 moon(vec3 dir,vec4 m,vec3 col,float vis){\r\n if(vis<=0.)return vec3(0.);vec3 md=m.xyz;float r=m.w,cosA=dot(dir,md);if(cosA<cos(r*1.6))return vec3(0.);\r\n vec3 up=abs(md.y)<.99?vec3(0.,1.,0.):vec3(1.,0.,0.),ex=normalize(cross(up,md)),ey=cross(md,ex);\r\n vec2 uv=vec2(dot(dir,ex),dot(dir,ey))/r;float rr=dot(uv,uv),disc=1.-smoothstep(.88,1.06,rr);\r\n vec3 n=normalize(ex*uv.x+ey*uv.y-md*sqrt(max(0.,1.-min(rr,1.))));\r\n float lit=smoothstep(-.06,.16,dot(n,uSunDir)),limb=.85+.15*(1.-rr);\r\n return col*(.05+.95*lit)*limb*disc*vis;\r\n}`;\r\nconst ATMOS_F=`${CARRIER_GLSL} ${MOON_GLSL} uniform vec3 uHorizon,uHaze,uZenith,uDust,uWeatherTint;uniform float uSeed,uFade,uRidge;varying vec3 vDir;\r\nvoid main(){\r\n vec3 d=normalize(vDir);float h=d.y,az=atan(d.x,-d.z);\r\n vec3 c=mix(uHorizon,uHaze,smoothstep(-.04,.24,h));c=mix(c,uZenith,smoothstep(.16,.78,h));\r\n float ridge=.025+.022*sin(az*7.+uSeed)+.012*sin(az*19.-uSeed);\r\n float farRidge=(1.-smoothstep(ridge,ridge+.008,h))*uRidge;\r\n float nearRidge=(1.-smoothstep(ridge*.52-.025,ridge*.52-.017,h))*uRidge;\r\n c=mix(c,uHaze*.72,farRidge*.6);c=mix(c,uZenith,nearRidge*.75);\r\n float belt=abs(d.y*.8+d.x*.45-d.z*.18-.42);\r\n float dust=(1.-smoothstep(.025,.17,belt))*smoothstep(.18,.45,h);\r\n float cloud=.55+.25*sin(az*4.+h*13.+uSeed)+.2*sin(az*11.-h*21.);\r\n c=mix(c,uDust,dust*cloud*.18);\r\n float arc=abs(d.y*.92-d.x*.24-.62);\r\n float broken=smoothstep(-.2,.35,sin(az*3.+uSeed));\r\n c+=uDust*(1.-smoothstep(.0015,.005,arc))*broken*.11;\r\n vec2 dh=normalize(d.xz+vec2(1e-5,0.)),sh=normalize(uSunDir.xz+vec2(1e-5,0.));\r\n c+=uGlowCol*pow(max(dot(dh,sh),0.),2.5)*exp(-abs(h)*9.)*uGlowAmt*(1.-nearRidge);\r\n c+=moon(d,uMoonA,uMoonACol,uMoonAVis)+moon(d,uMoonB,uMoonBCol,uMoonBVis);\r\n float cs=dot(d,uSunDir);c+=uSunCol*(smoothstep(cos(.032),cos(.0192),cs)+pow(max(cs,0.),40.)*.55+pow(max(cs,0.),8.)*.1)*uSunVis*(1.-nearRidge);\r\n c=floor(clamp(c,0.,1.)*64.+.5)/64.;c*=uWeatherTint;\r\n vec4 hull=carrier(d);if(hull.a>.5)c=hull.rgb;\r\n gl_FragColor=vec4(c,uFade);\r\n}`;\r\n\r\n// Fleet: one instanced impostor draw. aHaze lifts far hulls into the sky's haze; aDrift turns each slot about the zenith.\r\nconst FLEET_V=`attribute float aCell,aHaze,aDrift;uniform float uTime;varying vec2 vUv;varying vec3 vRay;varying float vHaze;\r\nvoid main(){vUv=vec2((uv.x+aCell)/3.,uv.y);vHaze=aHaze;vec4 p=instanceMatrix*vec4(position,1.);float a=uTime*aDrift,c=cos(a),s=sin(a);p.xz=mat2(c,-s,s,c)*p.xz;vRay=p.xyz;gl_Position=projectionMatrix*modelViewMatrix*p;}`;\r\nconst FLEET_F=`${CARRIER_GLSL} uniform sampler2D uMap;uniform vec3 uHaze,uWeatherTint;uniform float uFade;varying vec2 vUv;varying vec3 vRay;varying float vHaze;\r\nvoid main(){vec4 c=texture2D(uMap,vUv);if(c.a<.5||carrier(normalize(vRay)).a>.5)discard;gl_FragColor=vec4(mix(c.rgb,uHaze,vHaze)*uWeatherTint,uFade);}`;\r\n// Three depth layers: [count, size, haze, elevation min, max, drift rad/s]. Near hulls sit low and move, far ones are high, small and hazed.\r\nexport const FLEET_LAYERS=[[6,15,0,6,30,.004],[8,9,.38,12,52,.0025],[10,5,.66,22,80,.0012]];\r\nexport const FLEET_COUNT=FLEET_LAYERS.reduce((n,l)=>n+l[0],0);\r\n\r\n// Placement on the dome: [azimuthDeg, elevationDeg, quadSize]. Clustered so a portrait phone (narrow ~30 deg wide view)\r\n// looking up around azimuth 0 sees the ringed pair plus the carrier.\r\n// V3: four more round the rest of the azimuth (atlas cells reused), so every heading has a sky object.\r\nconst SLOTS=[[-7,56,32],[12,69,25],[-24,42,17],[27,36,21],[96,24,15],[160,52,20],[-150,33,17],[-82,66,13]];\r\nconst SHIP={az:4,el:30,w:62};\r\n\r\nfunction seeded(seed){let s=(seed|0)||1;return ()=>{s^=s<<13;s^=s>>>17;s^=s<<5;return ((s>>>0)%1e6)/1e6;};}\r\nfunction texOf(T,r){const t=new T.DataTexture(r.data,r.w,r.h,T.RGBAFormat);t.magFilter=t.minFilter=T.NearestFilter;t.generateMipmaps=false;t.needsUpdate=true;return t;}\r\n\r\n// occlude: the host draws world geometry beyond the dome radius (city flight scales the group out); every sky draw then\r\n// depth-tests so buildings stay in front. ridges:false drops the painted horizon ridges (the host has real terrain); below the\r\n// horizon the dome is then exactly colors.horizon, so a host fog set to it meets the sky without a seam. lat/lng/now feed the real sun and moons (default Las Vegas, the wall clock).\r\nexport function createAlienSky(T,{seed=1,pointSize=1,faction='chest',reducedMotion=false,occlude=false,lat=36.17,lng=-115.14,now=()=>Date.now(),ridges=true}={}){\r\n const rnd=seeded(seed*7919+13),group=new T.Group(),uFade={value:1},uTime={value:0},disposables=[];\r\n group.name='alien-sky';\r\n const atmosphere=BIOMES[faction]?.atmosphere||BIOMES.chest.atmosphere;\r\n const stex=texOf(T,paintMothership()),carrierUniforms={uShipMap:{value:stex},uShipCentre:{value:new T.Vector3()},uShipRight:{value:new T.Vector3()},uShipUp:{value:new T.Vector3()}};\r\n const ag=new T.SphereGeometry(SKY_RADIUS*1.1,32,8),am=new T.ShaderMaterial({\r\n  vertexShader:ATMOS_V,fragmentShader:ATMOS_F,side:T.BackSide,transparent:true,depthTest:true,depthWrite:false,\r\n  uniforms:{...carrierUniforms,uSunDir:{value:new T.Vector3(0,1,0)},uSunCol:{value:new T.Color()},uGlowCol:{value:new T.Color()},uGlowAmt:{value:0},uSunVis:{value:0},\r\n   uMoonA:{value:new T.Vector4(0,1,0,MOONS[0].radius)},uMoonB:{value:new T.Vector4(0,1,0,MOONS[1].radius)},uMoonACol:{value:new T.Color().fromArray(hex(MOONS[0].color))},uMoonBCol:{value:new T.Color().fromArray(hex(MOONS[1].color))},uMoonAVis:{value:0},uMoonBVis:{value:0},\r\n   uRidge:{value:ridges?1:0},uWeatherTint:{value:new T.Color(1,1,1)},uHorizon:{value:new T.Color(atmosphere.horizon).convertLinearToSRGB()},uHaze:{value:new T.Color(atmosphere.haze).convertLinearToSRGB()},uZenith:{value:new T.Color(atmosphere.zenith).convertLinearToSRGB()},uDust:{value:new T.Color(atmosphere.dust).convertLinearToSRGB()},uSeed:{value:hash2(3,7,seed)*6.28},uFade}\r\n });\r\n const air=new T.Mesh(ag,am);air.name='alien-atmosphere';air.renderOrder=-.8;air.frustumCulled=false;group.add(air);\r\n // Existing weather owns the rts-sky material tint, including reduced-motion lightning rules.\r\n // Read that tint at render time so the new opaque atmosphere preserves the host weather.\r\n let hostSky;\r\n air.onBeforeRender=(_renderer,scene)=>{hostSky ||= scene.getObjectByName('rts-sky');const tint=hostSky?.material?.color;if(tint)am.uniforms.uWeatherTint.value.copy(tint);};\r\n // stars\r\n const pos=new Float32Array(STAR_COUNT*3),col=new Float32Array(STAR_COUNT*3),tw=new Float32Array(STAR_COUNT),tints=[[1,1,1],[.7,.85,1],[1,.92,.65],[1,.6,.8]];\r\n for(let i=0;i<STAR_COUNT;i++){const az=rnd()*Math.PI*2,el=Math.asin(rnd()*1.05-.05),R=SKY_RADIUS*1.06,c=Math.cos(el);\r\n  pos.set([R*c*Math.sin(az),R*Math.sin(el),-R*c*Math.cos(az)],i*3);col.set(tints[rnd()<.7?0:1+(rnd()*3|0)],i*3);tw[i]=rnd();}\r\n const sg=new T.BufferGeometry();sg.setAttribute('position',new T.BufferAttribute(pos,3));sg.setAttribute('color',new T.BufferAttribute(col,3));sg.setAttribute('tw',new T.BufferAttribute(tw,1));\r\n const uStars={value:1},sm=new T.ShaderMaterial({vertexShader:POINT_V,fragmentShader:POINT_F,transparent:true,depthTest:false,depthWrite:false,uniforms:{...carrierUniforms,uTime,uStars,uSize:{value:pointSize},uFade}});\r\n const stars=new T.Points(sg,sm);stars.renderOrder=-.6;stars.frustumCulled=false;group.add(stars);\r\n // planets\r\n const atlas=paintPlanetAtlas(),ptex=texOf(T,atlas),pg=new T.PlaneGeometry(1,1),pmat=new T.ShaderMaterial({vertexShader:PLANET_V,fragmentShader:PLANET_F,transparent:true,depthTest:false,depthWrite:false,side:T.DoubleSide,uniforms:{...carrierUniforms,uMap:{value:ptex},uFade}});\r\n const planets=new T.InstancedMesh(pg,pmat,SLOTS.length),uvOff=new Float32Array(SLOTS.length*2),dummy=new T.Object3D();\r\n SLOTS.forEach(([az,el,sz],i)=>{const a=az*DEG,e=el*DEG;dummy.position.set(Math.sin(a)*Math.cos(e),Math.sin(e),-Math.cos(a)*Math.cos(e)).multiplyScalar(SKY_RADIUS);dummy.lookAt(0,0,0);dummy.scale.setScalar(sz);dummy.updateMatrix();planets.setMatrixAt(i,dummy.matrix);\r\n  uvOff[i*2]=(i&1)*.5;uvOff[i*2+1]=.5-((i&3)>>1)*.5;});\r\n pg.setAttribute('uvOff',new T.InstancedBufferAttribute(uvOff,2));\r\n planets.renderOrder=-.5;planets.frustumCulled=false;group.add(planets);\r\n // mothership\r\n const shipMat=new T.ShaderMaterial({vertexShader:SHIP_V,fragmentShader:QUAD_F,transparent:true,depthTest:false,depthWrite:false,side:T.DoubleSide,uniforms:{uMap:{value:stex},uFade}});\r\n const ship=new T.Mesh(new T.PlaneGeometry(SHIP.w,SHIP.w*SHIP_H/SHIP_W),shipMat);ship.renderOrder=-.45;ship.frustumCulled=false;ship.visible=false;group.add(ship); // projection anchor; pixels are painted by the atmosphere draw\r\n const placeShip=t=>{const a=(SHIP.az+.05*t)*DEG,e=SHIP.el*DEG;ship.position.set(Math.sin(a)*Math.cos(e),Math.sin(e),-Math.cos(a)*Math.cos(e)).multiplyScalar(SKY_RADIUS*.98);ship.lookAt(0,0,0);\r\n  carrierUniforms.uShipCentre.value.copy(ship.position);\r\n  carrierUniforms.uShipRight.value.set(1,0,0).applyQuaternion(ship.quaternion);\r\n  carrierUniforms.uShipUp.value.set(0,1,0).applyQuaternion(ship.quaternion);\r\n };\r\n placeShip(0);\r\n // MOM fleet: deterministic slots per seed, at least 9 deg apart, mirrored at random so they do not all fly one way\r\n const ftex=texOf(T,paintFleet()),fg=new T.PlaneGeometry(1,.5),U=am.uniforms,fmat=new T.ShaderMaterial({vertexShader:FLEET_V,fragmentShader:FLEET_F,transparent:true,depthTest:false,depthWrite:false,side:T.DoubleSide,\r\n  uniforms:{...carrierUniforms,uMap:{value:ftex},uHaze:U.uHaze,uWeatherTint:U.uWeatherTint,uTime,uFade}});\r\n const fleet=new T.InstancedMesh(fg,fmat,FLEET_COUNT),aCell=new Float32Array(FLEET_COUNT),aHaze=new Float32Array(FLEET_COUNT),aDrift=new Float32Array(FLEET_COUNT),placed=[];\r\n let n=0;for(const [count,size,haze,lo,hi,drift] of FLEET_LAYERS)for(let k=0;k<count;k++,n++){\r\n  let v;for(let tries=0;tries<40;tries++){v=dirOf(rnd()*360-180,lo+(hi-lo)*rnd());if(placed.every(q=>q[0]*v[0]+q[1]*v[1]+q[2]*v[2]<Math.cos(9*DEG)))break;}\r\n  placed.push(v);dummy.position.set(...v).multiplyScalar(SKY_RADIUS*.96);dummy.lookAt(0,0,0);dummy.scale.set(size*(rnd()<.5?-1:1),size,size);dummy.updateMatrix();fleet.setMatrixAt(n,dummy.matrix);\r\n  aCell[n]=n%3;aHaze[n]=haze;aDrift[n]=drift*(rnd()<.5?-1:1);\r\n }\r\n fg.setAttribute('aCell',new T.InstancedBufferAttribute(aCell,1));fg.setAttribute('aHaze',new T.InstancedBufferAttribute(aHaze,1));fg.setAttribute('aDrift',new T.InstancedBufferAttribute(aDrift,1));\r\n fleet.renderOrder=-.48;fleet.frustumCulled=false;fleet.name='mom-fleet';group.add(fleet);\r\n if(occlude)for(const m of [am,sm,pmat,fmat])m.depthTest=true;\r\n disposables.push(ag,am,sg,sm,pg,pmat,ptex,ship.geometry,shipMat,stex,fg,fmat,ftex);\r\n // the sky clock: real sun elevation -> faction palette, sun disc/glow, two moons. Re-read every 10 s of wall clock (the sun moves ~0.04 deg).\r\n let colors=null,clockAt=-Infinity;\r\n function tick(){const ms=now();if(colors&&Math.abs(ms-clockAt)<1e4)return;clockAt=ms;\r\n  const {sun,moons}=skyClock(ms,lat,lng),p=colors=factionSky(atmosphere,sun.elev),day=smooth(-6,10,sun.elev);colors.sunElev=sun.elev;\r\n  U.uHorizon.value.fromArray(p.horizon);U.uHaze.value.fromArray(p.haze);U.uZenith.value.fromArray(p.zenith);\r\n  U.uSunDir.value.fromArray(dirOf(sun.az,sun.elev));U.uSunCol.value.fromArray(p.sun);U.uGlowCol.value.fromArray(p.glow);U.uGlowAmt.value=p.glowAmount;U.uSunVis.value=smooth(-3,1,sun.elev);\r\n  moons.forEach((m,i)=>{(i?U.uMoonB:U.uMoonA).value.set(...dirOf(m.az,m.elev),m.radius);(i?U.uMoonBVis:U.uMoonAVis).value=smooth(-2,3,m.elev)*(1-.6*day);});\r\n  uStars.value=Math.max(.15,p.stars);\r\n }\r\n // ponytail: pitchDeg kept for API compatibility; sky is always on, no tilt fade (phone tilt no longer hides it)\r\n function update(t=0,pitchDeg=90){const time=reducedMotion?0:t;uFade.value=1;uTime.value=time;group.visible=true;placeShip(time);tick();}\r\n update(0,90);\r\n return {group,stars,planets,ship,fleet,update,get colors(){return colors;},dispose(){for(const d of disposables)d.dispose?.();planets.dispose?.();fleet.dispose?.();group.removeFromParent();},\r\n  stats:()=>({calls:4,tris:ag.index.count/3+SLOTS.length*2+FLEET_COUNT*2,textureBytes:PLANET_PX*PLANET_PX*4+SHIP_W*SHIP_H*4+FLEET_W*FLEET_H*4})};\r\n}\r\n","modules/coach-world-rts/sky-critters.mjs":"// Lane F: sky critters. 96 alien bugs / butterflies / birds as ONE InstancedMesh (1 draw call, 192 tris).\r\n// Lissajous flight + wind drift + billboarding + flipbook frame all happen in the vertex/fragment shader, so the CPU\r\n// only writes the `uTime` uniform per frame. Atlas 64x32 painted in code: 4 species (rows) x 4 flap frames (16x8 cells).\r\n// Species 0 bug is additive glow (premultiplied output, alpha 0); species 1-3 are alpha-tested opaque pixels.\r\nimport {makeRaster} from './sky-alien.mjs';\r\nexport const CRITTER_COUNT=96,ATLAS_W=64,ATLAS_H=32,CELL_W=16,CELL_H=8;\r\nconst SEED=n=>{let s=(n|0)||1;return ()=>{s^=s<<13;s^=s>>>17;s^=s<<5;return ((s>>>0)%1e6)/1e6;};};\r\n\r\nfunction line(r,x0,y0,x1,y1,col,ox,oy){let dx=Math.abs(x1-x0),dy=-Math.abs(y1-y0),sx=x0<x1?1:-1,sy=y0<y1?1:-1,e=dx+dy;\r\n for(;;){r.put(ox+x0,oy+y0,col);if(x0===x1&&y0===y1)break;const e2=2*e;if(e2>=dy){e+=dy;x0+=sx;}if(e2<=dx){e+=dx;y0+=sy;}}}\r\n\r\nexport function paintCritterAtlas(){\r\n const r=makeRaster(ATLAS_W,ATLAS_H);\r\n for(let f=0;f<4;f++){\r\n  const ox=f*CELL_W,P=(x,y,c)=>r.put(ox+x,y,c),Q=(x,y,c)=>r.put(ox+x,8+y,c),B=(x,y,c)=>r.put(ox+x,16+y,c),M=(x,y,c)=>r.put(ox+x,24+y,c);\r\n  // row 0: glow bug (bright cyan-lime; additive). wings buzz up/down\r\n  const wy=[1,2,4,2][f];\r\n  P(7,4,'#d8fff0');P(8,4,'#7dffb0');P(7,3,'#7dffb0');P(8,3,'#3affd0');P(9,4,'#3affd0');\r\n  for(const s of [-1,1]){P(7.5+s*2|0,wy+1,'#a8ffe8');P(7.5+s*3|0,wy+1,'#3affd0');}\r\n  P(6,wy+1,'#a8ffe8');P(5,wy+1,'#3affd0');P(10,wy+1,'#a8ffe8');P(11,wy+1,'#3affd0');\r\n  // row 1: butterfly, wing span closes/opens per frame\r\n  const span=[6,4,2,4][f];\r\n  for(let y=0;y<8;y++)for(let i=1;i<=span;i++){const reach=span*(1-Math.abs(y-3)/4.2);if(i<=reach){const edge=i>reach-1.2,c=edge?'#ffb13b':(y<4?'#ff4fd8':'#c030a8');Q(7-i+1,y,c);Q(8+i-1,y,c);}}\r\n  Q(7,2,'#2a1a3a');Q(8,2,'#2a1a3a');Q(7,3,'#2a1a3a');Q(8,3,'#2a1a3a');Q(7,4,'#2a1a3a');Q(8,4,'#2a1a3a');Q(7,5,'#2a1a3a');Q(8,5,'#2a1a3a');\r\n  Q(6,1,'#2a1a3a');Q(9,1,'#2a1a3a');\r\n  // row 2: alien long-neck bird, faces right, one big wing sweeping\r\n  const tipY=[0,2,5,7][f],tipX=[4,3,3,4][f];\r\n  for(let x=4;x<=11;x++)for(let y=3;y<=5;y++){const inside=!((x<5||x>10)&&y!==4);if(inside)B(x,y,y===5?'#7ad0c0':'#1f6f78');}\r\n  line(r,7,3,tipX,tipY,'#2e9aa0',ox,16);line(r,8,3,tipX+1,tipY,'#1f6f78',ox,16);line(r,6,3,tipX-1,tipY,'#1f6f78',ox,16);\r\n  B(11,3,'#1f6f78');B(12,2,'#1f6f78');B(12,1,'#2e9aa0');B(13,1,'#ff4fd8');B(14,1,'#ffd23a');B(12,0,'#ff4fd8');B(11,0,'#ff4fd8');B(3,4,'#1f6f78');B(2,4,'#ff4fd8');\r\n  // row 3: manta glider (orange/red) with twin trailing ribbons, wing tips flex\r\n  const fl=[-2,-1,1,2][f];\r\n  for(let x=2;x<=13;x++){const w=Math.round((1-Math.abs(x-7.5)/6)*3);for(let k=0;k<w;k++)M(x,Math.max(0,Math.min(7,4+fl*(Math.abs(x-7.5)/6)*2|0)-k+(k>w-2?1:0)),k===0?'#ffd23a':'#e0603a');}\r\n  for(let x=5;x<=10;x++)M(x,4,'#a02a3a');M(7,3,'#fff2b0');M(8,3,'#fff2b0');M(14,4,'#ff4fd8');M(1,5,'#ff4fd8');M(0,6,'#ff4fd8');M(0,5,'#7a1f4a');\r\n }\r\n return r;\r\n}\r\n\r\nconst V=`attribute vec3 aCentre,aAmp,aFreq;attribute float aPhase,aSpecies;uniform float uTime,uScale;\r\nvarying vec2 vUv;varying float vSp;\r\nvoid main(){\r\n float sp=aSpecies;vSp=sp;float t=uTime;\r\n float wind=sp<.5?0.:sp<1.5?.35:1.3; // m/s along +x/+z diagonal\r\n vec3 p=aCentre;p.xz+=vec2(wind,wind*.6)*t;p.xz=mod(p.xz+40.,80.)-40.;\r\n vec3 off=aAmp*vec3(sin(aFreq.x*t+aPhase),.5*sin(aFreq.y*t+aPhase*1.7),cos(aFreq.z*t+aPhase*.6));\r\n vec3 vel=aAmp*aFreq*vec3(cos(aFreq.x*t+aPhase),0.,-sin(aFreq.z*t+aPhase*.6))+vec3(wind,0.,wind*.6);\r\n p+=off;float edge=smoothstep(0.,6.,38.-max(abs(p.x),abs(p.z)));\r\n vec2 sz=sp<.5?vec2(.8,.4):sp<1.5?vec2(1.4,.7):sp<2.5?vec2(3.,1.5):vec2(4.,2.);\r\n vec4 mv=modelViewMatrix*vec4(p*uScale,1.);\r\n float face=dot(vel,vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]))<0.?-1.:1.;\r\n vec2 q=position.xy;mv.xy+=q*sz*uScale*edge;\r\n float fps=sp<.5?10.:sp<1.5?6.:5.;float frame=floor(mod(t*fps+aPhase*4.,4.));\r\n vec2 u=vec2(q.x*face+.5,q.y+.5);vUv=vec2((frame+u.x)/4.,1.-(sp+1.-u.y)/4.);\r\n gl_Position=projectionMatrix*mv;}`;\r\nconst F=`uniform sampler2D uMap;varying vec2 vUv;varying float vSp;\r\nvoid main(){vec4 c=texture2D(uMap,vUv);if(c.a<.5)discard;gl_FragColor=vSp<.5?vec4(c.rgb,0.):vec4(c.rgb,1.);}`;\r\nexport const CRITTER_SHADERS={V,F};\r\n\r\nexport function createSkyCritters(T,{seed=1,biomeWeights={bug:40,butterfly:30,bird:26},scale=1}={}){\r\n const rnd=SEED(seed*104729+7),N=CRITTER_COUNT,w=biomeWeights,tot=(w.bug||0)+(w.butterfly||0)+(w.bird||0)||1;\r\n const nBug=Math.round(N*(w.bug||0)/tot),nBut=Math.round(N*(w.butterfly||0)/tot),nBird=N-nBug-nBut;\r\n const centre=new Float32Array(N*3),amp=new Float32Array(N*3),freq=new Float32Array(N*3),phase=new Float32Array(N),species=new Float32Array(N);\r\n for(let i=0;i<N;i++){\r\n  const sp=i<nBug?0:i<nBug+nBut?1:2+(i&1),a=rnd()*Math.PI*2,rad=6+34*Math.sqrt(rnd()),R=(lo,hi)=>lo+(hi-lo)*rnd();\r\n  const h=sp===0?R(1.5,5):sp===1?R(1.5,8):R(6,15);\r\n  centre.set([Math.sin(a)*rad,h,Math.cos(a)*rad],i*3);\r\n  amp.set(sp===0?[R(.4,.9),R(.3,.6),R(.4,.9)]:sp===1?[R(1.5,3.5),R(.6,1.4),R(1.5,3.5)]:[R(7,12),R(1.2,2.5),R(7,12)],i*3);\r\n  freq.set(sp===0?[R(3,5),R(4,6),R(3,5)]:sp===1?[R(.6,1.2),R(1.5,2.5),R(.6,1.2)]:[R(.12,.28),R(.4,.8),R(.12,.28)],i*3);\r\n  phase[i]=rnd()*6.283;species[i]=sp;\r\n }\r\n const g=new T.PlaneGeometry(1,1),at=(a,n)=>new T.InstancedBufferAttribute(a,n);\r\n g.setAttribute('aCentre',at(centre,3));g.setAttribute('aAmp',at(amp,3));g.setAttribute('aFreq',at(freq,3));g.setAttribute('aPhase',at(phase,1));g.setAttribute('aSpecies',at(species,1));\r\n const r=paintCritterAtlas(),tex=new T.DataTexture(r.data,r.w,r.h,T.RGBAFormat);tex.magFilter=tex.minFilter=T.NearestFilter;tex.generateMipmaps=false;tex.needsUpdate=true;\r\n const uTime={value:0},m=new T.ShaderMaterial({vertexShader:V,fragmentShader:F,transparent:true,depthWrite:true,side:T.DoubleSide,\r\n  blending:T.CustomBlending,blendEquation:T.AddEquation,blendSrc:T.OneFactor,blendDst:T.OneMinusSrcAlphaFactor,uniforms:{uMap:{value:tex},uTime,uScale:{value:scale}}});\r\n const mesh=new T.InstancedMesh(g,m,N);mesh.name='sky-critters';mesh.frustumCulled=false;\r\n return {mesh,count:N,counts:{bug:nBug,butterfly:nBut,bird:nBird},update(t=0){uTime.value=t;},dispose(){g.dispose();m.dispose();tex.dispose();mesh.dispose?.();mesh.removeFromParent();},stats:()=>({calls:1,tris:N*2,textureBytes:ATLAS_W*ATLAS_H*4})};\r\n}\r\n","modules/coach-world-rts/sprite-shade.mjs":"// V8 sprite shading (Daggerfall look): DFO's billboards go through the same fog and light as the geometry; ours did not (unlit sprites\r\n// glowed at night and ignored distance). One shared time-of-day tint, from the real sun (V3's sky clock: device clock, Las Vegas default,\r\n// never a location request), multiplied in just before the fog chunk, so every patched sprite also fogs through the V1 law.\r\n// shadeSprite(material) patches a built-in material (SpriteMaterial / Lambert) in place; custom shaders read SPRITE_TINT and nightAmount().\r\nimport {skyClock} from './sky-alien.mjs';\r\nconst clamp01=x=>Math.min(1,Math.max(0,x)),smooth=(a,b,x)=>{const t=clamp01((x-a)/(b-a));return t*t*(3-2*t);};\r\nexport const spriteTint={r:1,g:1,b:1},SPRITE_TINT={value:spriteTint}; // one value object shared by every program (a plain {r,g,b} is a vec3 to three)\r\nconst NIGHT=[.46,.53,.8],DUSK=[1,.8,.64];\r\nexport function tintForSun(elev){const day=smooth(-8,8,elev),dusk=(1-smooth(2,14,Math.abs(elev)))*.65,c=NIGHT.map((n,i)=>n+(1-n)*day);\r\n return c.map((v,i)=>v+(DUSK[i]-v)*dusk);}\r\nlet night=0,at=-Infinity;\r\nexport const nightAmount=()=>night;\r\n// Re-read the sun every 10 s of wall clock (it moves ~0.04 deg); returns the tint.\r\nexport function updateSpriteTint(ms=Date.now(),lat=36.17,lng=-115.14){\r\n if(Math.abs(ms-at)<1e4)return spriteTint;at=ms;const e=skyClock(ms,lat,lng).sun.elev,[r,g,b]=tintForSun(e);\r\n spriteTint.r=r;spriteTint.g=g;spriteTint.b=b;night=1-smooth(-8,6,e);return spriteTint;\r\n}\r\nexport function shadeSprite(material){\r\n if(material.userData.cwSpriteShade)return material;material.userData.cwSpriteShade=true;material.fog=true;\r\n const prev=material.onBeforeCompile,key=material.customProgramCacheKey;\r\n material.onBeforeCompile=(shader,r)=>{prev?.call(material,shader,r);shader.uniforms.cwSpriteTint=SPRITE_TINT;\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <common>','#include <common>\\nuniform vec3 cwSpriteTint;').replace('#include <fog_fragment>','gl_FragColor.rgb*=cwSpriteTint;\\n#include <fog_fragment>');};\r\n material.customProgramCacheKey=()=>`${key.call(material)}-st`;\r\n return material;\r\n}\r\n","modules/coach-world-rts/tech-tree.mjs":"// The terminal reads the same quotes and catalog as the saved command reducer.\r\nimport {BUILDINGS,BUILDING_BY_ID,FACTION_BY_ID,MAX_LEVEL} from '../coach-world/catalog.mjs';\r\nimport {constructionQuote} from '../coach-world/state.mjs';\r\nimport {buildingFunction} from '../coach-world/base-systems.mjs';\r\n\r\nexport const BUILDING_FUNCTIONS=Object.freeze({\r\n hq:['Command nexus','Enter your character War Room; coordinate the settlement and unlock higher building levels.'],\r\n hall:['Coach garrison','Earned coaches guard this faction. Level 3 opens its senior coach slot.'],\r\n care:['Nutrition Kitchen','Spend a Meal pack earned from logged food to restore station Glycogen.'],\r\n observatory:['Breathwork relay','Spend a Breathwork token earned from meditation to restore station Composure.'],\r\n habitat:['Crew quarters','Rest or resume the settlement; support levels reduce command upgrade penalties.'],\r\n hangar:['Flight operations','Choose an earned hull, command air troops, and supply expedition transport.'],\r\n production:['Companion fabrication','Train a pathfinder; level 3 opens the pack companion. One training job per facility.'],\r\n expedition:['Mission control','Choose this faction’s crew, prepare a journey, launch, and recall it.'],\r\n tower:['Air defense','Fixed ranged defense protects the garrison in raids.'],\r\n press:['Close defense','An armored close-range defense emplacement.'],\r\n mortar:['Siege battery','A long-range ground defense emplacement.'],\r\n beacon:['Support relay','A defensive support relay.'],\r\n generator:['Power core','Settlement power infrastructure.'],\r\n wall:['Armor barrier','Blocks occupied ground; place it without sealing building entrances.'],\r\n fence:['Light barrier','A compact ground barrier; keep routes to every entrance open.'],\r\n trap:['Ground snare','A compact ground defense site.'],\r\n silo:['Protein storage','Settlement Protein infrastructure.'],\n yard:['Drill ground','Settlement training infrastructure.'],\r\n landing:['Arrival control','The first free build. Opens the first hall and the arrival route.'],\r\n});\r\nexport function techNode(base,def,track=def.track??base?.faction){\r\n const site=base?.buildings?.[def.id],level=site?.level??0;\r\n let quote;try{quote=constructionQuote(base,def.id,track);}catch{quote={allowed:false,reason:'Station data unavailable',prerequisites:[]};}\r\n const status=site?.job?(level>0?'UPGRADING':'CONSTRUCTING'):level>=MAX_LEVEL?'MAXIMUM':level>0?'ONLINE':quote.allowed?'READY':'LOCKED';\r\n const [role,description]=BUILDING_FUNCTIONS[def.kind]||['Station facility','Select this facility to inspect its commands.'];\r\n return {id:def.id,label:def.label,kind:def.kind,track:def.track,role,description,level,status,quote,upgradeStatus:level>0&&level<MAX_LEVEL&&!site?.job?(quote.allowed?'UPGRADE READY':'UPGRADE LOCKED'):null,system:buildingFunction(base,def),\r\n  requirements:(quote.prerequisites||[]).map(p=>({...p,label:BUILDING_BY_ID[p.buildingId]?.label||p.buildingId})),\r\n  unlocks:BUILDINGS.filter(d=>(d.prerequisites||[]).some(p=>p.buildingId===def.id)).map(d=>({id:d.id,label:d.label,level:d.prerequisites.find(p=>p.buildingId===def.id).level})),\r\n  material:FACTION_BY_ID[quote.track||track]?.label||'matching'};\r\n}\r\nexport function techNodes(base){return BUILDINGS.map(d=>techNode(base,d));}\r\nexport function systemReadout(profile){\r\n if(!profile?.active)return ['OFFLINE / complete construction to activate'];\r\n const e=profile.effects,out=[],n=v=>Math.round(v*100)/100;\r\n if(e.damage)out.push(`${n(e.damage/10)} damage${e.period?` / ${n(e.period/10)} sec`:' / trigger'}`);\r\n if(e.range)out.push(`${n(e.range/256)} tile range${e.groundOnly?' / ground targets':''}`);\r\n if(e.splashRadius)out.push(`${n(e.splashRadius/256)} tile splash radius`);\r\n if(e.heal)out.push(`${n(e.heal/10)} HP restored / ${n(e.period/10)} sec`);\r\n if(e.slowPct)out.push(`${e.slowPct}% slow / ${n(e.slowTicks/10)} sec`);\r\n if(e.garrisonHp)out.push(`+${n(e.garrisonHp/10)} HP to each garrison troop`);\r\n if(e.garrisonArmor)out.push(`+${n(e.garrisonArmor/10)} garrison armor`);\r\n if(e.defenseDamagePct)out.push(`+${e.defenseDamagePct}% defensive battery damage`);\r\n if(e.launchFuelSaving)out.push(`${e.launchFuelSaving} less expedition Glycogen`);\r\n if(e.materialDiscountPct)out.push(`${e.materialDiscountPct}% construction, training and expedition Protein discount`);\n if(e.trainingSpeedPct)out.push(`${e.trainingSpeedPct}% shorter companion training`);\r\n if(e.rationFuelBonus)out.push(`${22+e.rationFuelBonus} Glycogen per Meal pack`);\r\n if(e.meditationCalmBonus)out.push(`${22+e.meditationCalmBonus} Composure per Breathwork token`);\r\n if(e.journeySpeedPct>2)out.push(`${e.journeySpeedPct-2}% shorter outbound journeys`);\r\n if(e.shipSupply)out.push(`+${e.shipSupply} expedition Training capacity`);\r\n if(e.shipTier)out.push(`Air troop tier ${e.shipTier}`);\r\n if(e.coachTier)out.push(`Coach hall level ${e.coachTier}${e.coachTier>=3?' / senior coach slot open':''}`);\r\n if(e.techLevel)out.push(`Command technology level ${e.techLevel}`);\r\n if(e.outboundBaseMs)out.push(`${n(e.outboundBaseMs/60000)} min base outbound journey`);\r\n if(e.unlockLevel)out.push(`Production tier ${e.unlockLevel}${e.unlockLevel>=3?' / pack companion unlocked':''}`);\r\n if(e.packAccess)out.push('Arrival route and first hall access online');\r\n if(e.baseHp)out.push(`${n((e.baseHp+(e.towerHpBonus||0))/10)} HP / ${n((e.baseDamage+(e.towerDamageBonus||0))/10)} base attack / ${n(e.attackRange/256)} tile range / anti-air`);\r\n return out.length?out:['Infrastructure online'];\r\n}\r\n","modules/coach-world-rts/terminal.css":"/* Engineering terminal: physical bezel, segmented controls, readable phone text. */\r\n.cw-rts .cw-rts-bezel{background:linear-gradient(135deg,#45525d 0,#18252f 3%,#0b141b 45%,#293b46 98%);box-shadow:inset 0 1px #98a8b255,inset 0 -3px #04090d,0 -5px 18px #0008}\r\n.cw-rts .cw-rts-inner{background:linear-gradient(180deg,#07121bea,#101c25);border:1px solid #61758155;box-shadow:inset 0 2px 9px #000b}\r\n.cw-rts .cw-rts-cmd{position:relative;background:linear-gradient(150deg,#2e414e,#17242e 48%,#0d1922);border-color:#6d808a #16252f #091019 #536772;box-shadow:inset 0 0 0 1px #051019;color:#d9f5e8;text-shadow:0 1px #000}\r\n.cw-rts .cw-rts-cmd::after{content:'';position:absolute;left:4px;top:4px;width:3px;height:3px;background:#7be7b3;box-shadow:0 0 4px #72ffb766;pointer-events:none}\r\n.cw-rts .cw-rts-cmd[aria-disabled=true]{color:#90a19e;background:linear-gradient(145deg,#253139,#111c24)}\r\n.cw-rts .cw-rts-cmd[aria-disabled=true]::after{background:#d69b52;box-shadow:none}\r\n.cw-rts .cw-rts-cmd[aria-pressed=true],.cw-rts .cw-rts-cmd:active{background:#233e3e;border-color:#7df8bb;box-shadow:inset 0 0 12px #67e2b733}\r\n.cw-rts .cw-rts-top{background:linear-gradient(180deg,#17252ff2,#071119e8);border-bottom:1px solid #627b8555}\r\n.cw-rts .cw-rts-vital{border-left:2px solid #6da68c;padding-left:7px}\r\n.cw-rts .cw-rts-num{color:#a5ffcf;text-shadow:0 0 8px #64f3b244}\r\n.cw-rts .cw-rts-sheet{background:radial-gradient(ellipse at 100% 0,#20383b55,transparent 50%),#071219;color:#cce0dc}\r\n.cw-rts .cw-rts-sheet-head{background:linear-gradient(180deg,#2c404c,#0c1923);border-bottom-color:#72998a}\r\n.cw-rts .cw-rts-sheet-body .cw-card{background:#0c1b24;border:1px solid #38505c;box-shadow:inset 3px 0 #4d8a74;margin-top:12px;scroll-margin-top:62px}\r\n.cw-rts .cw-rts-sheet-body button,.cw-rts .cw-rts-sheet-body select{border-radius:2px;border:1px solid #547168;background:#152e32;color:#d2f5e4;min-height:44px}\r\n.cw-rts .cw-rts-tray{background:linear-gradient(180deg,#20333bee,#08141af5);border:1px solid #62877d;box-shadow:0 -8px 28px #0008}\r\n.cw-rts .cw-rts-tray-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 9px;border-bottom:1px solid #59776955;font:10px/1.3 ui-monospace,monospace;letter-spacing:.04em;color:#a6c8bb}\r\n.cw-rts .cw-rts-tray-head button{flex:none;min-height:44px;padding:0 9px;background:#142d30;color:#b9ffdb;border:1px solid #729c8b;font:10px ui-monospace,monospace;letter-spacing:.05em}\r\n.cw-rts .cw-rts-tray-tab{min-height:44px;background:#07151a88;border-right:1px solid #435e5744;color:#c2dcd0}\r\n.cw-rts .cw-rts-tray-tab[aria-selected=true]{background:linear-gradient(180deg,#3c716155,#132924);color:#c9ffe0;border-color:#a3e4be}\r\n.cw-rts .cw-rts-tray-tile{position:relative;width:104px;height:158px;gap:4px;justify-content:flex-start;background:linear-gradient(145deg,#29423e,#10212b);border:1px solid #618176;box-shadow:inset 0 0 0 2px #0a161e}\r\n.cw-rts .cw-rts-tray-tile canvas{width:44px;height:44px;flex:none}\r\n.cw-rts .cw-rts-tray-name{font:11px/1.15 ui-monospace,monospace;min-height:26px;text-align:center;color:#d4e9df}\r\n.cw-rts .cw-rts-tray-tile b{font:11px ui-monospace,monospace;color:#b1ffd5}\r\n.cw-rts .cw-rts-tray-state{font:9px ui-monospace,monospace;letter-spacing:.06em;color:#90b8a8}\r\n.cw-rts .cw-rts-tray-wallet{font:9px/1.2 ui-monospace,monospace;text-align:center;color:#c9d4a7}\r\n.cw-rts .cw-rts-tray-tile.is-locked{filter:none;border-color:#5e655c;background:linear-gradient(145deg,#292e30,#121e26)}\r\n.cw-rts .cw-rts-tray-tile.is-locked .cw-rts-tray-state{color:#e3b478}\r\n.cw-rts .cw-tech-terminal{font:12px/1.5 ui-monospace,monospace;letter-spacing:.02em;color:#c2e0d2;scroll-margin-top:60px}\r\n.cw-rts .cw-tech-head{display:flex;flex-wrap:wrap;gap:5px 16px;justify-content:space-between;padding:10px 0;border-bottom:1px solid #587b6c;color:#88b5a2;font-size:10px}\r\n.cw-rts .cw-tech-head b{color:#c2ffdb;letter-spacing:.16em}\r\n.cw-rts .cw-tech-dossier{margin-top:10px;padding:16px;background:linear-gradient(135deg,#19342e55,#0a1822);border:1px solid #506e62;box-shadow:inset 3px 0 #84c5a0}\r\n.cw-rts .cw-tech-title{display:grid;gap:4px}\r\n.cw-rts .cw-tech-title>span{font-size:10px;letter-spacing:.12em;color:#94bbac}\r\n.cw-rts .cw-tech-title strong{font-size:17px;color:#e0f9ec}\r\n.cw-rts .cw-tech-state{font-size:10px;letter-spacing:.16em;color:#b0f6cc}\r\n.cw-rts .cw-tech-description{font-size:12px;line-height:1.6}\r\n.cw-rts .cw-tech-cost{padding:8px;background:#020b10;border:1px solid #385348;color:#f5da9e;font-variant-numeric:tabular-nums}\r\n.cw-rts .cw-tech-needs{list-style:none;padding:0;display:grid;gap:6px;color:#e9c38e}\r\n.cw-rts .cw-tech-needs .is-met{color:#91d8b1}\r\n.cw-rts .cw-tech-systems{padding:10px 10px 10px 26px;background:#06150e;border:1px solid #3b6149;color:#a7ecc0;font-size:11px;line-height:1.8}\r\n.cw-rts .cw-tech-blocked{color:#f1ae8a;border-left:2px solid #ca825b;padding-left:9px}\r\n.cw-rts .cw-tech-next{font-size:10px;color:#a9c6bb}\r\n.cw-rts .cw-tech-operate{width:100%;font:11px ui-monospace,monospace;letter-spacing:.06em}\r\n.cw-rts .cw-tech-tree{margin:12px 0;border:1px solid #425d54;padding:12px;background:#0a1820}\r\n.cw-rts .cw-tech-tree summary{cursor:pointer;min-height:44px;display:flex;align-items:center;color:#d1f7df;font-size:12px}\r\n.cw-rts .cw-tech-tree summary::before{content:'▸';margin-right:8px;color:#8ff1bc}\r\n.cw-rts .cw-tech-tree[open] summary::before{content:'▾'}\r\n.cw-rts .cw-tech-tree select{width:100%;margin-bottom:10px;padding:6px;font:12px ui-monospace,monospace}\r\n.cw-rts .cw-tech-rows{display:grid;gap:6px;max-height:420px;overflow:auto;overscroll-behavior:contain}\r\n.cw-rts .cw-tech-node{display:grid;gap:4px;padding:12px;text-align:left;width:100%;border-left:3px solid #4e7363!important}\r\n.cw-rts .cw-tech-node b{font-size:12px}.cw-rts .cw-tech-node span{font-size:10px;color:#b6dba2}.cw-rts .cw-tech-node small{font-size:10px;color:#a4bcb2}\r\n.cw-rts .cw-tech-node.is-selected{background:#24463c!important;border-color:#a2f6c3!important}\r\n.cw-rts .cw-tech-archive{padding:12px;margin:12px 0;background:#0a1921;border:1px solid #436354}\r\n.cw-rts .cw-tech-archive>summary{min-height:44px;display:flex;align-items:center;color:#beefcf;font:11px ui-monospace,monospace;cursor:pointer;letter-spacing:.07em}\r\n.cw-rts .rts-roster{font:12px/1.5 ui-monospace,monospace;min-width:0;color:#cce4d7}\r\n.cw-rts .rts-roster header{display:flex;align-items:center;justify-content:space-between;gap:8px}.cw-rts .rts-roster h3{font-size:12px}\r\n.cw-rts .roster-filters{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}.cw-rts .roster-filters>*{flex:1 1 130px;min-width:0;width:100%;font:12px ui-monospace,monospace;padding:8px}\r\n.cw-rts .roster-filters input{color:#cbeedb;background:#031018;border:1px solid #537563;min-height:44px}\r\n.cw-rts .roster-content{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}\r\n.cw-rts .roster-list{display:grid;gap:5px;max-height:225px;overflow:auto;overscroll-behavior:contain}\r\n.cw-rts .roster-row{display:grid;gap:3px;text-align:left;padding:10px;width:100%;min-width:0}.cw-rts .roster-row b{font-size:11px}.cw-rts .roster-row span{font-size:10px;color:#aac9b9}\r\n.cw-rts .roster-row[aria-pressed=true]{border-color:#b9f7c8;background:#294d3d}\r\n.cw-rts .roster-detail{display:grid;justify-items:center;gap:8px;min-width:0;text-align:center}\r\n.cw-rts .roster-detail canvas{width:160px;height:160px;image-rendering:pixelated;background:radial-gradient(#2b443e,#061019);border:1px solid #597769}\r\n.cw-rts .roster-meta p,.cw-rts .roster-source{font-size:11px;line-height:1.5;margin:4px 0;color:#a6c6b7}\r\n.cw-rts .roster-controls{display:flex;justify-content:center;flex-wrap:wrap;gap:5px}.cw-rts .roster-controls button{min-width:44px;padding:6px;font:11px ui-monospace,monospace}.cw-rts .roster-controls button[aria-pressed=true]{background:#365d46;border-color:#9ce3b5}\r\n@media(min-width:700px){.cw-rts .roster-content{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.cw-rts .roster-list{max-height:440px}}\r\n@media(max-height:500px){.cw-rts .cw-rts-tray-tile{height:142px}.cw-rts .cw-rts-tray-tile canvas{width:28px;height:28px}.cw-rts .cw-rts-tray-head{padding:2px 8px}}\r\n","modules/coach-world-rts/terminal.mjs":"import {BUILDINGS,FACTIONS,BUILDING_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {techNode,techNodes,systemReadout} from './tech-tree.mjs';\r\nimport {resourceDisplayText as resourceText} from '../coach-world/resource-labels.mjs';\r\nconst node=(tag,cls,text)=>{const e=document.createElement(tag);e.className=cls||'';if(text!=null)e.textContent=text;return e;};\r\nconst time=ms=>ms?`${Math.ceil(ms/60000)} MIN`:'INSTANT';\r\nexport function mountTechTerminal(host,{getBase,getSelected,select,operate}={}){\r\n const root=node('section','cw-tech-terminal');root.dataset.techTerminal='';\r\n const head=node('div','cw-tech-head');head.append(node('span','','MYR5 // ENGINEERING'),node('b','','TECH NETWORK'));\r\n const dossier=node('div','cw-tech-dossier'),tree=node('details','cw-tech-tree'),summary=node('summary','','Building network / prerequisites');\r\n const filter=node('select');filter.setAttribute('aria-label','Filter building network');\r\n for(const [id,label] of [['all','All civilizations'],['shared','Shared infrastructure'],...FACTIONS.map(f=>[f.id,f.name])]){const o=node('option','',label);o.value=id;filter.append(o);}\r\n const rows=node('div','cw-tech-rows');tree.append(summary,filter,rows);root.append(head,dossier,tree);host.prepend(root);\r\n let sig='',disposed=false;\r\n function update(force=false){\r\n  if(disposed)return;const b=getBase?.(),id=getSelected?.()||'hq';if(!b)return;\r\n  const key=JSON.stringify([id,filter.value,b.faction,b.materials,b.fuel,b.rest,b.buildings,b.training,b.units,b.expedition?.status,b.rations,b.calmTokens]);\r\n  if(!force&&key===sig)return;sig=key;\r\n  const d=BUILDING_BY_ID[id]||BUILDINGS[0],n=techNode(b,d),q=n.quote;\r\n  dossier.replaceChildren();dossier.dataset.techSelected=n.id;\r\n  const label=node('div','cw-tech-title');label.append(node('span','',n.role.toUpperCase()),node('strong','',`${n.label} / L${n.level}`),node('b','cw-tech-state',n.status+(n.upgradeStatus?' / '+n.upgradeStatus:'')));\r\n  const cost=node('p','cw-tech-cost',n.status==='MAXIMUM'?'ALL TIERS INSTALLED':`${q.cost??'--'} ${n.material.toUpperCase()} Protein · ${q.fuelCost??5} Glycogen · ${time(q.durationMs)}`);\r\n  const needs=node('ul','cw-tech-needs');for(const r of n.requirements){const li=node('li',r.met?'is-met':'',`${r.met?'✓':'→'} ${r.label} / L${r.level}`);needs.append(li);}\r\n  if(q.daysRequired)needs.append(node('li',q.qualifyingDays>=q.daysRequired?'is-met':'',`${q.qualifyingDays}/${q.daysRequired} qualifying workout days`));\r\n  const next=node('p','cw-tech-next',n.unlocks.length?'FEEDS → '+n.unlocks.map(x=>`${x.label} (L${x.level})`).join(' · '):'Connects to the command network');\r\n  const go=node('button','cw-tech-operate','OPEN RESEARCH NETWORK');go.type='button';go.dataset.techOperate=id;go.onclick=()=>operate?.(id);\r\n  dossier.append(label,node('p','cw-tech-description',n.description),cost,needs);\r\n  const systems=node('ul','cw-tech-systems');systems.setAttribute('aria-label','Facility effects');for(const text of systemReadout(n.system))systems.append(node('li','',text));dossier.append(systems);\r\n  if(!q.allowed&&q.reason&&n.status!=='MAXIMUM')dossier.append(node('p','cw-tech-blocked',(n.level>0?'Next upgrade: ':'')+resourceText(q.reason)));\r\n  dossier.append(next,go);\r\n  rows.replaceChildren();const list=techNodes(b).filter(n=>filter.value==='all'||filter.value==='shared'&&!n.track||n.track===filter.value);\r\n  summary.textContent=`Building network / ${list.length} facilities`;\r\n  for(const n of list){const bt=node('button','cw-tech-node'+(n.id===id?' is-selected':''));bt.type='button';bt.dataset.techId=n.id;bt.setAttribute('aria-pressed',String(n.id===id));\r\n   bt.append(node('b','',n.label),node('span','',`L${n.level} · ${n.status}`),node('small','',n.requirements.map(r=>`${r.met?'✓':'→'} ${r.label} L${r.level}`).join(' / ')||'COMMAND ROOT'));\r\n   bt.onclick=()=>{select?.(n.id);update(true);};rows.append(bt);}\r\n }\r\n filter.onchange=()=>update(true);update();\r\n return {root,update,openTree(){tree.open=true;root.scrollIntoView({block:'start'});},dispose(){disposed=true;root.remove();}};\r\n}\r\n","modules/coach-world-rts/terrain3d.mjs":"import { BIOMES, shapeGeometry, biomeOf, noise2, fbm, densityField, SUBS, GROUND_DETAIL } from \"./biomes.mjs\";\r\nimport { BIOME_TINTS } from \"../coach-world/palettes.mjs\";\r\nimport { createPs1Material } from \"./ps1-material.mjs\";\r\nimport { createTerrain } from \"../coach-world/terrain.mjs\";\r\nimport { occupancy } from \"../coach-world/navigation.mjs\";\r\nimport { placeNode } from \"./mining.mjs\";\r\n\r\nexport const MAX_INSTANCES = 600, GLB_TIMEOUT_MS = 7000;\r\nconst clamp = (v, a, b) => Math.max(a, Math.min(b, v));\r\n// GLB upgrades: plant shape -> decimated board model (first mesh only). Any failure keeps the procedural shapeGeometry.\r\nconst GLB_FOR = { stalk: \"/pod/worlds/boards/flower.glb\", reed: \"/pod/worlds/boards/grass.glb\" };\r\nasync function defaultGlb(url) {\r\n  const [{ GLTFLoader }, { MeshoptDecoder }] = await Promise.all([import(\"/vendor/three/GLTFLoader.js\"), import(\"/vendor/three/meshopt_decoder.module.js\")]);\r\n  return (await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url)).scene;\r\n}\r\n\r\n// R58: vertex-clustering decimation. The grass GLB is 16000 triangles and every instance draws every merged shape (7.4M tris at 455 plants),\r\n// so cap GLB plants at GLB_TRIS. Grid snap + average per cell, drop collapsed triangles; the grid is refined until it still fits the budget.\r\nexport const GLB_TRIS = 24;\r\nexport function decimate(T, g, maxTris = GLB_TRIS) {\r\n  const src = g.index ? g.toNonIndexed() : g, P = src.attributes.position, n = P.count;\r\n  if (n / 3 <= maxTris) return src;\r\n  src.computeBoundingBox();\r\n  const bb = src.boundingBox, size = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z, 1e-6);\r\n  let best = null;\r\n  for (let res = 3; res <= 64; res++) {\r\n    const cell = size / res, ids = new Map(), sum = [], cnt = [], map = new Uint32Array(n);\r\n    for (let i = 0; i < n; i++) {\r\n      const x = P.getX(i), y = P.getY(i), z = P.getZ(i), k = Math.floor((x - bb.min.x) / cell) + \",\" + Math.floor((y - bb.min.y) / cell) + \",\" + Math.floor((z - bb.min.z) / cell);\r\n      let id = ids.get(k);\r\n      if (id === undefined) { id = sum.length / 3; ids.set(k, id); sum.push(0, 0, 0); cnt.push(0); }\r\n      sum[id * 3] += x; sum[id * 3 + 1] += y; sum[id * 3 + 2] += z; cnt[id]++; map[i] = id;\r\n    }\r\n    const tris = [], seen = new Set();\r\n    for (let i = 0; i < n; i += 3) {\r\n      const a = map[i], b = map[i + 1], c = map[i + 2];\r\n      if (a === b || b === c || a === c) continue;\r\n      const key = [a, b, c].sort((u, v) => u - v).join(); if (seen.has(key)) continue; seen.add(key);\r\n      tris.push(a, b, c);\r\n    }\r\n    if (tris.length / 3 > maxTris) break;\r\n    best = { tris, sum, cnt };\r\n  }\r\n  if (!best) return src; // ponytail: never under the coarsest grid; falls back to full geometry only for degenerate input\r\n  const pos = new Float32Array(best.tris.length * 3);\r\n  best.tris.forEach((id, i) => { pos[i * 3] = best.sum[id * 3] / best.cnt[id]; pos[i * 3 + 1] = best.sum[id * 3 + 1] / best.cnt[id]; pos[i * 3 + 2] = best.sum[id * 3 + 2] / best.cnt[id]; });\r\n  const out = new T.BufferGeometry();\r\n  out.setAttribute(\"position\", new T.BufferAttribute(pos, 3));\r\n  out.computeVertexNormals();\r\n  src.dispose();\r\n  return out;\r\n}\r\n\r\n// Merge shape geometries into one non-indexed geometry; every vertex carries `sid` (index into the list) so ONE InstancedMesh can show many shapes.\r\nexport function mergeShapes(T, geoms) {\r\n  let n = 0;\r\n  const flat = geoms.map((g) => { const f = g.index ? g.toNonIndexed() : g; n += f.attributes.position.count; return f; });\r\n  const pos = new Float32Array(n * 3), nor = new Float32Array(n * 3), sid = new Float32Array(n);\r\n  let o = 0;\r\n  flat.forEach((f, i) => {\r\n    const c = f.attributes.position.count;\r\n    pos.set(f.attributes.position.array, o * 3);\r\n    if (f.attributes.normal) nor.set(f.attributes.normal.array, o * 3);\r\n    sid.fill(i, o, o + c);\r\n    o += c;\r\n  });\r\n  const m = new T.BufferGeometry();\r\n  m.setAttribute(\"position\", new T.BufferAttribute(pos, 3));\r\n  m.setAttribute(\"normal\", new T.BufferAttribute(nor, 3));\r\n  m.setAttribute(\"sid\", new T.BufferAttribute(sid, 1));\r\n  flat.forEach((f, i) => { if (f !== geoms[i]) f.dispose(); });\r\n  geoms.forEach((g) => g.dispose());\r\n  return m;\r\n}\r\n\r\n// PS1 material + per-instance shape pick: vertices whose `sid` differs from the instance's `iShape` are clipped away.\r\nfunction propMaterial(T, resolution) {\r\n  const m = createPs1Material(T, { vertexColors: false, resolution }), prev = m.onBeforeCompile;\r\n  m.onBeforeCompile = (shader, r) => {\r\n    prev(shader, r);\r\n    shader.vertexShader = shader.vertexShader\r\n      .replace(\"#include <common>\", \"#include <common>\\nattribute float sid;\\nattribute float iShape;\")\r\n      .replace(\"#include <project_vertex>\", \"#include <project_vertex>\\nif (abs(sid - iShape) > 0.5) gl_Position = vec4(2.0, 2.0, 2.0, 1.0);\");\r\n  };\r\n  m.customProgramCacheKey = () => \"ps1-terrain-props\";\r\n  return m;\r\n}\r\n\r\nconst grounded = (g) => { g.computeBoundingBox(); return g.translate(0, -g.boundingBox.min.y, 0); }; // shape sits on y=0\r\n\r\n// V1-C7: one 256px detail tile, generated once and cached (null without a DOM, e.g. node tests).\r\n// Channels (raw, NoColorSpace): R = plate (0 dirt, .6..1 plate, .3 seam), G = creep (0 none, .55 body, 1 rim), B = dirt speckle. Alpha stays 255 so premultiplied canvas storage keeps RGB.\r\nconst DETAIL_TILE = 256;\r\nlet detailTile;\r\nfunction groundDetailTexture(T) {\r\n  if (detailTile) return detailTile;\r\n  try {\r\n    const cv = document.createElement(\"canvas\"); cv.width = cv.height = DETAIL_TILE;\r\n    const ctx = cv.getContext(\"2d\"), img = ctx.createImageData(DETAIL_TILE, DETAIL_TILE), d = img.data;\r\n    const h2 = (x, y) => { let n = (Math.imul(x, 374761393) + Math.imul(y, 668265263)) | 0; n = Math.imul(n ^ (n >>> 13), 1274126177); return ((n ^ (n >>> 16)) >>> 0) / 4294967296; };\r\n    const vnoise = (x, y, cells) => { // seamless value noise: lattice wraps at `cells`, so the tile repeats cleanly\r\n      const gx = x * cells, gy = y * cells, x0 = Math.floor(gx), y0 = Math.floor(gy), fx = gx - x0, fy = gy - y0;\r\n      const u = fx * fx * (3 - 2 * fx), v = fy * fy * (3 - 2 * fy), m = (i) => ((i % cells) + cells) % cells;\r\n      const a = h2(m(x0), m(y0)), b = h2(m(x0 + 1), m(y0)), c = h2(m(x0), m(y0 + 1)), e = h2(m(x0 + 1), m(y0 + 1));\r\n      return (a + (b - a) * u) * (1 - v) + (c + (e - c) * u) * v;\r\n    };\r\n    for (let py = 0; py < DETAIL_TILE; py++) for (let px = 0; px < DETAIL_TILE; px++) {\r\n      const x = px / DETAIL_TILE, y = py / DETAIL_TILE, i = (py * DETAIL_TILE + px) * 4;\r\n      const seam = px % 64 < 2 || py % 64 < 2; // 4x4 plates per tile, 2px seams\r\n      const plate = seam ? 0.3 : 0.6 + 0.4 * h2((px >> 6) + 4 * (py >> 6), 77);\r\n      const n = vnoise(x, y, 6) * 0.7 + vnoise(x, y, 12) * 0.3; // creep blobs, rim where n just crosses the threshold\r\n      const creep = n > 0.66 ? 0.55 : n > 0.6 ? 1 : 0;\r\n      d[i] = plate * 255; d[i + 1] = creep * 255; d[i + 2] = (0.5 * vnoise(x, y, 16) + 0.5 * h2(px * 7 + 1, py * 13 + 5)) * 255; d[i + 3] = 255;\r\n    }\r\n    ctx.putImageData(img, 0, 0);\r\n    const tex = new T.CanvasTexture(cv);\r\n    tex.wrapS = tex.wrapT = T.RepeatWrapping; tex.needsUpdate = true;\r\n    detailTile = tex;\r\n  } catch { /* no canvas: plain ground */ }\r\n  return detailTile || null;\r\n}\r\n\r\nexport function createTerrain3d(T, base, { scale = 1.6, heightAt, groundCanvas, cells, occupied, avoid, loadGlb, resolution = [320, 180], timeoutMs = GLB_TIMEOUT_MS, fadeColor = 0x25333c } = {}) {\r\n  const seedNum =\r\n    typeof base.seed === \"string\"\r\n      ? [...base.seed].reduce((s, c) => s + c.charCodeAt(0), 0)\r\n      : Number(base.seed);\r\n  const hash = (n) =>\r\n    Math.abs(Math.sin(n * 12.9898) * 43758.5453 % 1);\r\n  const vn = noise2(seedNum), vsub = noise2(seedNum + 101), vbump = noise2(seedNum + 303), density = densityField(seedNum);\r\n  const noise = (x, z) => vn(x, z); // R81: seeded value noise (was hash(sin))\r\n  const subAt = (tx, tz) => Math.min(2, Math.floor(vsub(tx / 6, tz / 6) * 3)); // sub-flavour 0 lush / 1 barren / 2 rocky\r\n  const bidOf = (cell) => (BIOMES[cell?.faction] ? cell.faction : base.faction);\r\n  const ter = (!cells || !occupied) ? createTerrain(base) : null;\r\n  cells = cells || ter.cells;\r\n  occupied = occupied || occupancy(base);\r\n  const roads = ter?.roads || new Set();\r\n  if (!avoid) { try { const nd = placeNode(base)?.node; avoid = nd ? [nd] : []; } catch { avoid = []; } }\r\n  const flatCells = new Set();\r\n  for (const k of occupied) flatCells.add(k);\r\n  for (const k of roads) flatCells.add(k);\r\n  for (const c of cells) if (c.kind !== \"soil\") flatCells.add(c.y * 24 + c.x);\r\n  heightAt = heightAt || ((x, y) => cells[clamp(Math.floor(y), 0, 23) * 24 + clamp(Math.floor(x), 0, 23)]?.height || 0);\r\n  const cellAt = (x, y) => cells[clamp(Math.floor(y), 0, 23) * 24 + clamp(Math.floor(x), 0, 23)];\r\n\r\n  // Relief: bilinear of the 4 nearest cell centres; each cell = height * .35 * (.5 + biome relief) (so relief .5 = the old look, relief 1 + height 2 = 1.05 tile units).\r\n  // Cells under buildings/roads are height 0, so the ground is flat there.\r\n  const cellY = (cx, cy) => {\r\n    const x = clamp(cx, 0, 23), y = clamp(cy, 0, 23), br = biomeOf(cellAt(x, y) || {}, base);\r\n    return heightAt(x, y) * 0.35 * (0.5 + (br?.relief ?? 0.5));\r\n  };\r\n  const smooth = (a, b, x) => { const t = clamp((x - a) / (b - a), 0, 1); return t * t * (3 - 2 * t); };\r\n  const freeAt = (x, y) => (flatCells.has(clamp(y, 0, 23) * 24 + clamp(x, 0, 23)) ? 0 : 1);\r\n  const reliefAt = (tx, tz) => {\r\n    const u = clamp(tx - 0.5, 0, 22.999), v = clamp(tz - 0.5, 0, 22.999), x0 = Math.floor(u), y0 = Math.floor(v), fx = u - x0, fy = v - y0;\r\n    const a = cellY(x0, y0), b = cellY(x0 + 1, y0), c = cellY(x0, y0 + 1), d = cellY(x0 + 1, y0 + 1);\r\n    let y = (a * (1 - fx) + b * fx) * (1 - fy) + (c * (1 - fx) + d * fx) * fy;\r\n    const bm = biomeOf(cellAt(tx, tz) || {}, base), ridge = bm?.ridge, k = Math.min(1, y / 0.35); // ridge styles fade to 0 on flat ground\r\n    // free = 1 on open soil, 0 on/next to buildings and roads (4-corner bilinear, steep smoothstep so the ground under a building stays exactly flat)\r\n    const m = (a, b) => freeAt(a, b), fr = (m(x0, y0) * (1 - fx) + m(x0 + 1, y0) * fx) * (1 - fy) + (m(x0, y0 + 1) * (1 - fx) + m(x0 + 1, y0 + 1) * fx) * fy;\r\n    const free = Math.max(0, Math.min(1, (fr - 0.8) / 0.2));\r\n    if (ridge === \"plate\") y = Math.round(y / 0.3) * 0.3;\r\n    else if (ridge === \"dune\") y += 0.07 * k * Math.sin((tx + tz) * 1.3);\r\n    else if (ridge === \"scorch\" || ridge === \"slab\") y += (noise(tx * 3, tz * 3) - 0.5) * 0.12 * k;\r\n    else if (ridge === \"drum\") y = y * 0.4 + 0.5 * smooth(0.1, 0.35, y); // mesa: steep step up to a flat top\r\n    else if (ridge === \"mound\") { // Gaussian bumps, one per 6-tile lattice cell\r\n      const gx = Math.floor(tx / 6), gz = Math.floor(tz / 6);\r\n      for (let i = -1; i <= 1; i++) for (let j = -1; j <= 1; j++) {\r\n        const cx = (gx + i + 0.2 + 0.6 * vbump(gx + i + 0.5, gz + j + 0.5)) * 6, cz = (gz + j + 0.2 + 0.6 * vbump(gx + i + 9.5, gz + j + 9.5)) * 6;\r\n        y += free * 0.3 * Math.exp(-((tx - cx) ** 2 + (tz - cz) ** 2) / 3);\r\n      }\r\n    } else if (ridge === \"ring\") { // crater rim around two centres: abs(r - r0)\r\n      for (const [cx, cz] of [[7, 8], [17, 16]]) y += free * 0.32 * Math.exp(-(((Math.hypot(tx - cx, tz - cz) - 3.2) / 0.7) ** 2));\r\n    }\r\n    y += free * (bm?.amp ?? 0.5) * 0.3 * fbm(vn, tx, tz, bm?.roughness ?? 0.5); // fBm relief; amp is in biome units, 0.3 tile max\r\n    return Math.max(0, Math.min(1.1, y));\r\n  };\r\n\r\n  const group = new T.Group();\r\n\r\n  // Inner ground: relief + a light per-biome tint in vertex colours on top of the pixel-art ground texture\r\n  const gGeom = new T.PlaneGeometry(24 * scale, 24 * scale, 48, 48);\r\n  gGeom.rotateX(-Math.PI / 2);\r\n  const posArr = gGeom.attributes.position.array, gcol = new Float32Array(posArr.length), tmp = new T.Color(), bgc = {};\r\n  for (let i = 0; i < posArr.length; i += 3) {\r\n    const vx = posArr[i], vz = posArr[i + 2];\r\n    const tx = vx / scale + 12, tz = vz / scale + 12;\r\n    const y = reliefAt(tx, tz);\r\n    posArr[i + 1] = y * scale;\r\n    const bg = BIOME_TINTS[bidOf(cellAt(tx, tz))]?.[SUBS[subAt(tx, tz)]]?.ground || biomeOf(cellAt(tx, tz) || {}, base)?.ground;\r\n    tmp.setRGB(1, 1, 1);\r\n    if (bg) tmp.lerp((bgc[bg] ||= new T.Color(bg)), 0.28);\r\n    tmp.multiplyScalar(0.88 + Math.min(0.2, y * 0.25)); // higher ground a touch lighter\r\n    gcol[i] = tmp.r; gcol[i + 1] = tmp.g; gcol[i + 2] = tmp.b;\r\n  }\r\n  gGeom.setAttribute(\"color\", new T.BufferAttribute(gcol, 3));\r\n  gGeom.computeVertexNormals();\r\n  const gMat =\r\n    groundCanvas\r\n      ? (() => {\r\n          const tex = new T.CanvasTexture(groundCanvas);\r\n          tex.magFilter = tex.minFilter = T.NearestFilter;\r\n          tex.colorSpace = T.SRGBColorSpace;\r\n          tex.needsUpdate = true;\r\n          return new T.MeshLambertMaterial({ map: tex, vertexColors: true });\r\n        })()\r\n      : new T.MeshLambertMaterial({ color: 0x3a5a3a, vertexColors: true });\r\n  // R58: the visible base is a soft circle, not the 24-tile square. Ground + skirt blend to the fog colour by radial distance\r\n  // from the base centre (smoothstep .5..1 of the half-width), so no square corners show at any zoom.\r\n  const fadeCol = new T.Color(fadeColor); // R58: with the world on this is the faction ground colour so the base blends into the habitat disc\r\n  const radialFade = (mat) => {\r\n    mat.onBeforeCompile = (sh) => {\r\n      sh.uniforms.uFadeCol = { value: fadeCol }; sh.uniforms.uFadeR = { value: 12 * scale };\r\n      sh.vertexShader = sh.vertexShader.replace(\"#include <common>\", \"#include <common>\\nvarying vec2 vRad;\").replace(\"#include <begin_vertex>\", \"#include <begin_vertex>\\nvRad = position.xz;\");\r\n      sh.fragmentShader = sh.fragmentShader.replace(\"#include <common>\", \"#include <common>\\nvarying vec2 vRad; uniform vec3 uFadeCol; uniform float uFadeR;\")\r\n        .replace(\"#include <opaque_fragment>\", \"outgoingLight = mix(outgoingLight, uFadeCol, smoothstep(.5, 1., length(vRad) / uFadeR));\\n#include <opaque_fragment>\");\r\n    };\r\n    mat.customProgramCacheKey = () => \"r58-radial-fade\";\r\n    return mat;\r\n  };\r\n  radialFade(gMat);\r\n  // V1-C7: tiled detail overlay (dirt speckle everywhere, metal plates inside the base radius, creep on the outer ring). Same texture on portrait and landscape.\r\n  const detail = groundDetailTexture(T);\r\n  if (detail) {\r\n    const fadeCompile = gMat.onBeforeCompile;\r\n    gMat.onBeforeCompile = (sh, r) => {\r\n      fadeCompile(sh, r);\r\n      Object.assign(sh.uniforms, { uTile: { value: detail }, uTileW: { value: 4 * scale }, uBaseR: { value: 6 * scale }, uPlate: { value: new T.Color(GROUND_DETAIL.plate) }, uCreep: { value: new T.Color(GROUND_DETAIL.creep) }, uCreepRim: { value: new T.Color(GROUND_DETAIL.creepRim) } });\r\n      sh.fragmentShader = sh.fragmentShader\r\n        .replace(\"varying vec2 vRad;\", \"varying vec2 vRad;\\nuniform sampler2D uTile; uniform float uTileW, uBaseR; uniform vec3 uPlate, uCreep, uCreepRim;\")\r\n        .replace(\"#include <color_fragment>\", `#include <color_fragment>\r\n  vec4 tl = texture2D(uTile, vRad / uTileW);\r\n  float dBase = length(vRad) / uBaseR;\r\n  float inBase = 1.0 - smoothstep(0.85, 1.0, dBase), outer = smoothstep(0.7, 0.95, dBase);\r\n  vec3 col = diffuseColor.rgb * (0.82 + 0.3 * tl.b);\r\n  float seamA = smoothstep(0.05, 0.15, tl.r) * (1.0 - smoothstep(0.25, 0.4, tl.r));\r\n  vec3 plate = uPlate * (0.8 + 0.25 * tl.r) * (1.0 - 0.85 * seamA);\r\n  col = mix(col, plate, smoothstep(0.05, 0.15, tl.r) * inBase * 0.9);\r\n  vec3 creep = mix(uCreep, uCreepRim, smoothstep(0.8, 0.95, tl.g));\r\n  diffuseColor.rgb = mix(col, creep, smoothstep(0.3, 0.45, tl.g) * outer * 0.9);`);\r\n    };\r\n    gMat.customProgramCacheKey = () => \"rts-ground-detail\"; // own program: the skirt shares the radial-fade key\r\n  }\r\n  group.add(new T.Mesh(gGeom, gMat));\r\n\r\n  // Outer skirt: a ring (the inner map square is cut out so it never overlaps the ground) whose vertex colours run from the\r\n  // sampled ground-edge colour to the fog colour, so no border is visible. Colours are 0..1 linear (T.Color converts from sRGB).\r\n  const fogC = new T.Color(fadeColor);\r\n  const edge = edgeColor(groundCanvas) || [61, 90, 64];\r\n  const innerC = new T.Color().setRGB(edge[0] / 255, edge[1] / 255, edge[2] / 255, T.SRGBColorSpace);\r\n  const SEG = 30, size = 72 * scale, half = 12 * scale;\r\n  let sGeom = new T.PlaneGeometry(size, size, SEG, SEG);\r\n  sGeom.rotateX(-Math.PI / 2);\r\n  const sposArr = sGeom.attributes.position.array, colors = [], c = new T.Color();\r\n  for (let i = 0; i < sposArr.length; i += 3) {\r\n    const vx = sposArr[i], vz = sposArr[i + 2];\r\n    const out = Math.max(0, Math.max(Math.abs(vx), Math.abs(vz)) - half) / (36 * scale - half), rad = Math.hypot(vx, vz) / half; // 0 at the map edge .. 1 at the skirt edge\r\n    const n = noise(vx / scale, vz / scale);\r\n    sposArr[i + 1] = -0.05 * scale + out * (-0.15 * scale + 0.8 * scale * n);\r\n    c.copy(innerC).lerp(fogC, Math.min(1, Math.max(out * 1.3, (rad - 0.5) * 2)));\r\n    colors.push(c.r, c.g, c.b);\r\n  }\r\n  sGeom.setAttribute(\"color\", new T.BufferAttribute(new Float32Array(colors), 3));\r\n  const idx = sGeom.index.array, keep = [], inside = (k) => Math.abs(sposArr[k * 3]) < half - 1e-3 && Math.abs(sposArr[k * 3 + 2]) < half - 1e-3;\r\n  for (let i = 0; i < idx.length; i += 3) if (!(inside(idx[i]) && inside(idx[i + 1]) && inside(idx[i + 2]))) keep.push(idx[i], idx[i + 1], idx[i + 2]);\r\n  sGeom.setIndex(keep);\r\n  const sMat = radialFade(new T.MeshLambertMaterial({ vertexColors: true }));\r\n  group.add(new T.Mesh(sGeom, sMat));\r\n\r\n  // Vegetation + rock growth. Biome tables scatter plants and rocks over free soil (not occupied / road / water / next to a resource node).\r\n  // Two InstancedMeshes (plants, rocks), each one merged geometry of every shape with a per-instance shape id; <= MAX_INSTANCES in total.\r\n  const free = {};\r\n  for (const cell of cells) {\r\n    const k = cell.y * 24 + cell.x;\r\n    if (cell.kind !== \"soil\" || occupied.has(k) || roads.has(k)) continue;\r\n    if (avoid.some((p) => Math.abs(p.x - cell.x) <= 1 && Math.abs(p.y - cell.y) <= 1)) continue;\r\n    (free[cell.faction] ||= []).push(cell);\r\n  }\r\n  const RIM = 0, fids = Object.keys(BIOMES);\r\n  const plantShapes = [...new Set(fids.flatMap((f) => BIOMES[f].plants))], rockShapes = [...new Set(fids.flatMap((f) => [...BIOMES[f].rocks, BIOMES[f].landmark.shape]))];\r\n  const want = fids.reduce((s, f) => s + BIOMES[f].plant.count + BIOMES[f].rock.count, 0), cap = Math.min(1, (MAX_INSTANCES - RIM - fids.length) / Math.max(1, want));\r\n  const items = { plant: [], rock: [] };\r\n  let rs = (seedNum * 2654435761) >>> 0; // mulberry32\r\n  const rnd = () => { rs = (rs + 0x6d2b79f5) >>> 0; let t = Math.imul(rs ^ (rs >>> 15), 1 | rs); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };\r\n  // Poisson-ish: no two props closer than 0.6 world units (grid of that cell size). Sub-flavour weights: plants love lush, rocks love rocky.\r\n  const MIN_D = 0.25 / scale, SUBW = { plant: [1, 0.6, 0.4], rock: [0.6, 1, 1] }, grid = new Map(), gk = (x, z) => Math.floor(x / MIN_D) + \",\" + Math.floor(z / MIN_D);\r\n  const spaced = (tx, tz) => {\r\n    const gx = Math.floor(tx / MIN_D), gz = Math.floor(tz / MIN_D);\r\n    for (let i = -1; i <= 1; i++) for (let j = -1; j <= 1; j++) for (const p of grid.get((gx + i) + \",\" + (gz + j)) || []) if (Math.hypot(p[0] - tx, p[1] - tz) < MIN_D) return false;\r\n    return true;\r\n  };\r\n  const claim = (tx, tz) => { const k = gk(tx, tz); (grid.get(k) || grid.set(k, []).get(k)).push([tx, tz]); };\r\n  for (const f of fids) {\r\n    const b = BIOMES[f], list = free[f] || [];\r\n    if (!list.length) continue;\r\n    const tints = BIOME_TINTS[f];\r\n    const spawn = (kind, shapes, n, s0, s1, tint) => {\r\n      for (let i = 0; i < Math.round(n * cap); i++) {\r\n        for (let tries = 0; tries < 120; tries++) { // clumps: accept by density field x sub-flavour weight; re-roll otherwise (keeps clearings empty)\r\n          const cell = list[Math.floor(rnd() * list.length)], tx = cell.x + 0.1 + rnd() * 0.8, tz = cell.y + 0.1 + rnd() * 0.8;\r\n          if (Math.hypot(tx - 12, tz - 12) > 9.5) continue; // R58: keep props inside the soft circle\r\n          const d = density(tx, tz), sub = subAt(tx, tz);\r\n          if (rnd() >= Math.min(1, d * 3.5) * SUBW[kind][sub] || !spaced(tx, tz)) continue;\r\n          claim(tx, tz);\r\n          const shape = shapes[Math.min(shapes.length - 1, Math.floor(((rnd() + sub * 0.5) / 1.5) * shapes.length))];\r\n          items[kind].push({ shape, tx, tz, d, sc: (s0 + rnd() * (s1 - s0)) * scale, color: tint(sub), y: reliefAt(tx, tz) * scale });\r\n          break;\r\n        }\r\n      }\r\n    };\r\n    const pg = new T.Color(b.plant.color || b.ground), rg = new T.Color(b.rock.color || b.ground), pc = new T.Color(), rc = new T.Color();\r\n    spawn(\"plant\", b.plants, b.plant.count, b.plant.scale[0], b.plant.scale[1], (sub) => pc.set(tints[SUBS[sub]].plant).clone().offsetHSL((rnd() - 0.5) * 0.06, 0.1, (rnd() - 0.5) * 0.1));\r\n    spawn(\"rock\", b.rocks, b.rock.count, 0.25, 0.7, (sub) => rc.set(tints[SUBS[sub]].rock).clone().multiplyScalar(0.8 + rnd() * 0.6));\r\n    for (let tries = 0; tries < 40; tries++) { // one landmark per biome, on open soil with a clump of props around it when possible\r\n      const cell = list[Math.floor(rnd() * list.length)], tx = cell.x + 0.5, tz = cell.y + 0.5, L = b.landmark;\r\n      if (Math.hypot(tx - 12, tz - 12) > 9 || (tries < 30 && density(tx, tz) < 0.4) || !spaced(tx, tz)) continue;\r\n      claim(tx, tz);\r\n      items.rock.push({ shape: L.shape, tx, tz, d: 0.5, sc: (L.scale[0] + rnd() * (L.scale[1] - L.scale[0])) * scale, color: rg.clone().multiplyScalar(1.1), y: reliefAt(tx, tz) * scale, lm: true });\r\n      break;\r\n    }\r\n  }\r\n  const rim = biomeOf({}, base);\r\n  if (rim) for (let i = 0; i < RIM; i++) { // boulders out on the skirt\r\n    const ang = 2 * Math.PI * hash(seedNum + i), r = 14 + 16 * hash(seedNum + i + 1);\r\n    items.rock.push({ shape: rim.rock.shape, tx: 12 + r * Math.cos(ang), tz: 12 + r * Math.sin(ang), sc: (0.6 + hash(seedNum + i + 3) * 0.8) * scale, color: new T.Color(0x5b6d78), y: -0.05 * scale });\r\n  }\r\n\r\n  const dummy = new T.Object3D(), solid = propMaterial(T, resolution), meshes = [];\r\n  const build = (kind, names) => {\r\n    const list = items[kind];\r\n    if (!list.length) return null;\r\n    const geo = mergeShapes(T, names.map((s) => grounded(shapeGeometry(T, s, 1)))), mesh = new T.InstancedMesh(geo, solid, list.length), shape = new Float32Array(list.length);\r\n    list.forEach((it, i) => {\r\n      dummy.position.set((it.tx - 12) * scale, it.y, (it.tz - 12) * scale);\r\n      dummy.rotation.set(0, hash(seedNum + i * 3.7) * Math.PI * 2, 0);\r\n      dummy.scale.set(it.sc, it.sc * (kind === \"plant\" ? 0.7 + it.d * 0.8 + hash(seedNum + i) * 0.6 : 0.6 + hash(seedNum + i) * 0.5), it.sc); // plants: taller in dense clumps\r\n      if (kind === \"rock\") dummy.position.y -= it.sc * 0.2; // rocks sit sunk into the soil\r\n      dummy.updateMatrix();\r\n      mesh.setMatrixAt(i, dummy.matrix);\r\n      mesh.setColorAt(i, it.color);\r\n      shape[i] = Math.max(0, names.indexOf(it.shape));\r\n    });\r\n    geo.setAttribute(\"iShape\", new T.InstancedBufferAttribute(shape, 1));\r\n    mesh.instanceMatrix.needsUpdate = true;\r\n    if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;\r\n    mesh.frustumCulled = false; // one shape's bounding sphere would cull the whole field\r\n    mesh.name = \"terrain-\" + kind;\r\n    group.add(mesh); meshes.push(mesh);\r\n    return mesh;\r\n  };\r\n  // R81 integration: plants shuffled (seeded) so a lower instance count thins every biome evenly; base3d sets the count by zoom (propLod).\r\n  for (let i = items.plant.length - 1; i > 0; i--) { const j = Math.floor(rnd() * (i + 1)); [items.plant[i], items.plant[j]] = [items.plant[j], items.plant[i]]; }\r\n  const plantMesh = build(\"plant\", plantShapes);\r\n  build(\"rock\", rockShapes);\r\n\r\n  // Optional GLB upgrade of stalk/reed plants (flower.glb / grass.glb); failure or timeout keeps the procedural shapes.\r\n  let disposed = false;\r\n  const glb = loadGlb || (typeof window !== \"undefined\" ? defaultGlb : null);\r\n  if (glb && plantMesh) {\r\n    Promise.all(Object.entries(GLB_FOR).filter(([s]) => plantShapes.includes(s)).map(async ([s, url]) => {\r\n      let timer;\r\n      try {\r\n        const root = await Promise.race([Promise.resolve().then(() => glb(url)), new Promise((_, rej) => { timer = setTimeout(() => rej(new Error(\"glb timeout\")), timeoutMs); })]);\r\n        let m = null;\r\n        root?.traverse?.((o) => { if (!m && o.isMesh) m = o; });\r\n        if (!m) return null;\r\n        const g = decimate(T, m.geometry.clone());\r\n        g.deleteAttribute(\"uv\"); g.deleteAttribute(\"color\");\r\n        g.computeBoundingBox();\r\n        const bb = g.boundingBox, k = 1 / Math.max(bb.max.y - bb.min.y, bb.max.x - bb.min.x, bb.max.z - bb.min.z, 1e-3);\r\n        g.translate(-(bb.max.x + bb.min.x) / 2, -bb.min.y, -(bb.max.z + bb.min.z) / 2);\r\n        g.scale(k, k, k);\r\n        return [s, g];\r\n      } catch { return null; } finally { clearTimeout(timer); }\r\n    })).then((got) => {\r\n      const swap = new Map(got.filter(Boolean));\r\n      if (disposed || !swap.size) return;\r\n      const old = plantMesh.geometry, merged = mergeShapes(T, plantShapes.map((s) => swap.get(s) || grounded(shapeGeometry(T, s, 1))));\r\n      merged.setAttribute(\"iShape\", old.attributes.iShape);\r\n      plantMesh.geometry = merged; old.dispose();\r\n    });\r\n  }\r\n  const stats = () => ({ drawCalls: meshes.length, instances: meshes.reduce((s, m) => s + m.count, 0) });\r\n\r\n  function dispose() {\r\n    disposed = true;\r\n    if (group.parent) group.parent.remove(group);\r\n    group.traverse((c) => {\r\n      if (c.geometry) c.geometry.dispose();\r\n      if (c.material) {\r\n        if (Array.isArray(c.material))\r\n          c.material.forEach((m) => m.dispose());\r\n        else c.material.dispose();\r\n      }\r\n      if (c.isInstancedMesh) c.dispose();\r\n    });\r\n  }\r\n\r\n  const propLod = (k) => { if (plantMesh) plantMesh.count = Math.max(1, Math.round(items.plant.length * Math.min(1, Math.max(0, k)))); };\r\n  return { group, dispose, stats, propLod, relief: (tx, tz) => reliefAt(tx, tz) * scale }; // relief(tileX, tileY) -> world y of the ground\r\n}\r\n\r\n// Mean colour of the outermost ring of the ground canvas (null without a readable 2D canvas, e.g. in node tests).\r\nfunction edgeColor(cv) {\r\n  try {\r\n    const w = cv.width, h = cv.height, ctx = cv.getContext(\"2d\"), acc = [0, 0, 0];\r\n    let n = 0;\r\n    for (const [x, y, ww, hh] of [[0, 0, w, 4], [0, h - 4, w, 4], [0, 0, 4, h], [w - 4, 0, 4, h]]) {\r\n      const d = ctx.getImageData(x, y, ww, hh).data;\r\n      for (let i = 0; i < d.length; i += 4) { acc[0] += d[i]; acc[1] += d[i + 1]; acc[2] += d[i + 2]; n++; }\r\n    }\r\n    return n ? acc.map((v) => v / n) : null;\r\n  } catch { return null; }\r\n}\r\n","modules/coach-world-rts/tower-art.mjs":"export const TOWER_ART = Object.freeze({\r\n  tower: {\r\n    motif: 'Ice spire turrets',\r\n    shape: 'spire',\r\n    material: 'frosted crystal on a riveted stone plinth',\r\n    rule: 'Tall narrow column with a faceted ice head; a single cold accent light on top.'\r\n  },\r\n  wall: {\r\n    motif: 'Segmented barrier blocks',\r\n    shape: 'block',\r\n    material: 'stacked stone slabs with iron bands',\r\n    rule: 'Low wide block, one bevelled cap, two rivets per face.'\r\n  },\r\n  generator: {\r\n    motif: 'Spinning core dynamos',\r\n    shape: 'core',\r\n    material: 'brushed alloy with a glowing ring',\r\n    rule: 'Squat drum with a horizontal ring around the middle and a round vent cap.'\r\n  },\r\n  press: {\r\n    motif: 'Piston press cannons',\r\n    shape: 'press',\r\n    material: 'squat steel block with a horizontal piston',\r\n    rule: 'Squat block, one horizontal piston barrel, a round plate disc on the rear.'\r\n  },\r\n  mortar: {\r\n    motif: 'Lobbed mortar pods',\r\n    shape: 'mortar',\r\n    material: 'bowl on a tripod with a loose shell',\r\n    rule: 'Hemispherical bowl on three legs, one loose ball on top, accent on the ball.'\r\n  },\r\n  beacon: {\r\n    motif: 'Splash beacon towers',\r\n    shape: 'beacon',\r\n    material: 'slim stem with glowing rings',\r\n    rule: 'Slim stem, two stacked rings, a glowing orb on top.'\r\n  },\r\n  trap: {\r\n    motif: 'Spring trap plates',\r\n    shape: 'trap',\r\n    material: 'flat plate with four pegs',\r\n    rule: 'Flat wide plate with four short pegs; hidden until it fires.'\r\n  },\r\n  fence: {\r\n    motif: 'Sagging rope barricades',\r\n    shape: 'fence',\r\n    material: 'two posts and taut rope',\r\n    rule: 'Two posts with a sagging rope box between them, low and cheap.'\r\n  },\r\n  silo: {\r\n    motif: 'Stacked material silos',\r\n    shape: 'silo',\r\n    material: 'riveted cylinder with a dome cap',\r\n    rule: 'Tall cylinder, domed cap, seven thin coloured bands as material stacks.'\r\n  },\r\n  landing: {\r\n    motif: 'Drop pod landing pad',\r\n    shape: 'landing',\r\n    material: 'dark alloy disc with a glowing guide ring',\r\n    rule: 'Wide low disc, one accent ring, four slim lamp posts, a bright centre mark; nothing tall so pods can land.'\r\n  },\r\n  yard: {\r\n    motif: 'Army camp racks',\r\n    shape: 'yard',\r\n    material: 'flat slab with corner posts and rack bars',\r\n    rule: 'Flat slab, four corner posts, two rack bars across the top.'\r\n  }\r\n});\r\n\r\nexport function partsForDefense(T, kind, level, links = []) { // links: [[dx,dz]] unit directions to adjacent built walls\r\n  level = Math.max(0, Math.min(5, level | 0));\r\n  let w, d, height;\r\n  switch (kind) {\r\n    case 'tower':\r\n      w = d = 3.2;\r\n      height = 2.4 + 0.5 * level;\r\n      break;\r\n    case 'wall':\r\n      w = d = 1.6;\r\n      height = 0.9 + 0.1 * level;\r\n      break;\r\n    case 'generator':\r\n      w = d = 3.2;\r\n      height = 1.6 + 0.2 * level;\r\n      break;\r\n    case 'press':\r\n      w = d = 3.2;\r\n      height = 1.8 + 0.4 * level;\r\n      break;\r\n    case 'mortar':\r\n      w = d = 3.2;\r\n      height = 1.6 + 0.4 * level;\r\n      break;\r\n    case 'beacon':\r\n      w = d = 3.2;\r\n      height = 3.0 + 0.5 * level;\r\n      break;\r\n    case 'trap':\r\n      w = d = 1.6;\r\n      height = 0.15; // fixed\r\n      break;\r\n    case 'fence':\r\n      w = d = 1.6;\r\n      height = 1.0 + 0.1 * level;\r\n      break;\r\n    case 'silo':\r\n      w = d = 3.2;\r\n      height = 2.2 + 0.3 * level;\r\n      break;\r\n    case 'landing':\r\n      w = d = 4.2;\r\n      height = 0.25;\r\n      break;\r\n    case 'yard':\r\n      w = d = 3.2;\r\n      height = 0.3 + 0.1 * level;\r\n      break;\r\n    default:\r\n      return [];\r\n  }\r\n  if (level <= 0) return [{ g: new T.BoxGeometry(w * 0.9, height, d * 0.9), y: height / 2 }];\r\n\r\n  const P = [];\r\n  const r = Math.min(w, d) * 0.45;\r\n\r\n  if (kind === 'tower') {\r\n    // Plinth, narrow shaft, crystal head, forward barrel\r\n    P.push({ g: new T.BoxGeometry(1.6, 0.35, 1.6), y: 0.175 });\r\n    P.push({ g: new T.CylinderGeometry(0.45, 0.6, height, 8), y: height / 2 });\r\n    P.push({ g: new T.ConeGeometry(0.5, 0.9, 5, 1), y: height + 0.45, accent: 1 });\r\n    P.push({ g: new T.CylinderGeometry(0.1, 0.1, 1.1, 6), y: height - 0.2, z: 0.7, rx: Math.PI / 2 });\r\n    if (level >= 2) {\r\n      // Accent ring\r\n      P.push({\r\n        g: new T.TorusGeometry(0.62, 0.1, 6, 12),\r\n        y: height * 0.6,\r\n        rx: Math.PI / 2\r\n      });\r\n    }\r\n    if (level >= 4) {\r\n      // Side spikes\r\n      for (const s of [-1, 1]) {\r\n        P.push({\r\n          g: new T.OctahedronGeometry(0.22, 0),\r\n          x: s * 0.7,\r\n          y: height + 0.3,\r\n          accent: 1\r\n        });\r\n      }\r\n    }\r\n    if (level >= 5) {\r\n      // Top orb\r\n      P.push({\r\n        g: new T.OctahedronGeometry(0.35, 0),\r\n        y: height + 1.2,\r\n        accent: 1\r\n      });\r\n    }\r\n  } else if (kind === 'wall') {\r\n    // Base wall\r\n    P.push({ g: new T.BoxGeometry(w * 0.9, height, d * 0.9), y: height / 2 });\r\n    for (const [lx, lz] of links) P.push({ g: new T.BoxGeometry(lx ? w * 0.5 : d * 0.9, height * 0.85, lz ? d * 0.5 : w * 0.9), x: lx * w * 0.72, z: lz * d * 0.72, y: height * 0.425 });\r\n    if (level >= 3) {\r\n      // Top cap\r\n      P.push({\r\n        g: new T.BoxGeometry(0.3 * w, 0.1 * height, 0.3 * d),\r\n        y: height + 0.05 * height,\r\n        accent: 1\r\n      });\r\n    }\r\n    if (level >= 5) {\r\n      // Crenellation\r\n      for (const s of [-1, 0, 1]) {\r\n        P.push({\r\n          g: new T.BoxGeometry(0.2 * w, 0.1 * height, 0.2 * d),\r\n          x: s * w / 4,\r\n          y: height + 0.05 * height,\r\n          accent: 1\r\n        });\r\n      }\r\n    }\r\n  } else if (kind === 'generator') {\r\n    const radius = r;\r\n    // Squat drum with a round vent cap\r\n    P.push({ g: new T.CylinderGeometry(radius * 0.85, radius * 0.95, height, 12), y: height / 2 });\r\n    P.push({ g: new T.CylinderGeometry(radius * 0.4, radius * 0.5, 0.25, 8), y: height + 0.12, accent: 1 });\r\n    if (level >= 1) {\r\n      // Ring\r\n      P.push({\r\n        g: new T.TorusGeometry(radius * 0.95, 0.12 * radius, 6, 12),\r\n        y: height * 0.5,\r\n        rx: Math.PI / 2,\r\n        accent: 1\r\n      });\r\n    }\r\n    if (level >= 4) {\r\n      // Vent\r\n      P.push({\r\n        g: new T.CylinderGeometry(0.05 * radius, 0.05 * radius, height * 0.3, 3, 1),\r\n        y: height + 0.15 * height,\r\n        accent: 1\r\n      });\r\n    }\r\n  } else if (kind === 'press') {\r\n    // Squat box base\r\n    P.push({ g: new T.BoxGeometry(w * 0.9, height * 0.6, d * 0.9), y: height * 0.3 });\r\n    // Horizontal cylinder barrel\r\n    P.push({\r\n      g: new T.CylinderGeometry(0.18, 0.18, w * 0.7, 8),\r\n      y: height * 0.7,\r\n      z: d * 0.25,\r\n      rx: Math.PI / 2,\r\n      accent: 1\r\n    });\r\n    // Round plate disc\r\n    if (level >= 2) {\r\n      P.push({\r\n        g: new T.CylinderGeometry(w * 0.4, w * 0.4, 0.1, 8),\r\n        y: height * 0.9,\r\n        accent: 1\r\n      });\r\n    }\r\n  } else if (kind === 'mortar') {\r\n    // Bowl\r\n    P.push({\r\n      g: new T.SphereGeometry(w * 0.4, 10, 6, 0, Math.PI * 2, 0, Math.PI / 2),\r\n      y: height * 0.5\r\n    });\r\n    // Three legs\r\n    const legGeom = new T.CylinderGeometry(0.05, 0.05, height * 0.5, 4, 1);\r\n    P.push({ g: legGeom, x: -w * 0.3, z: w * 0.3 });\r\n    P.push({ g: legGeom, x: w * 0.3, z: w * 0.3 });\r\n    P.push({ g: legGeom, x: 0, z: -w * 0.3 });\r\n    // Ball\r\n    P.push({\r\n      g: new T.SphereGeometry(w * 0.15, 8, 6),\r\n      y: height * 0.9,\r\n      accent: 1\r\n    });\r\n  } else if (kind === 'beacon') {\r\n    // Stem\r\n    P.push({\r\n      g: new T.CylinderGeometry(w * 0.12, w * 0.15, height, 6, 1),\r\n      y: height / 2\r\n    });\r\n    // Two rings\r\n    const ringGeom = new T.TorusGeometry(w * 0.35, 0.08, 4, 8);\r\n    P.push({\r\n      g: ringGeom,\r\n      y: height * 0.6,\r\n      rx: Math.PI / 2,\r\n      accent: 1\r\n    });\r\n    P.push({\r\n      g: ringGeom,\r\n      y: height * 0.8,\r\n      rx: Math.PI / 2,\r\n      accent: 1\r\n    });\r\n    // Orb\r\n    P.push({\r\n      g: new T.OctahedronGeometry(0.3, 0),\r\n      y: height + 0.3,\r\n      accent: 1\r\n    });\r\n  } else if (kind === 'trap') {\r\n    // Plate\r\n    P.push({\r\n      g: new T.BoxGeometry(w * (.65 + level * .05), height, d * (.65 + level * .05)),\r\n      y: height / 2\r\n    });\r\n    if (level >= 1) {\r\n      const pegGeom = new T.CylinderGeometry(0.04, 0.04, 0.25, 4, 1);\r\n      for (const sx of [-1, 1]) {\r\n        for (const sz of [-1, 1]) {\r\n          P.push({\r\n            g: pegGeom,\r\n            x: sx * w * 0.3,\r\n            z: sz * w * 0.3,\r\n            y: height + 0.12,\r\n            accent: 1\r\n          });\r\n        }\r\n      }\r\n    }\r\n  } else if (kind === 'fence') {\r\n    // Two posts\r\n    const postGeom = new T.CylinderGeometry(0.06, 0.06, height * 1.5, 4, 1);\r\n    P.push({ g: postGeom, x: -w * 0.4, y: height * 0.75 });\r\n    P.push({ g: postGeom, x: w * 0.4, y: height * 0.75 });\r\n    // Rope box\r\n    if (level >= 2) {\r\n      P.push({\r\n        g: new T.BoxGeometry(w * 0.8, 0.05, 0.05),\r\n        y: height * 0.8,\r\n        accent: 1\r\n      });\r\n    }\r\n  } else if (kind === 'silo') {\r\n    // Cylinder\r\n    P.push({\r\n      g: new T.CylinderGeometry(w * 0.4, w * 0.4, height, 12, 1),\r\n      y: height / 2\r\n    });\r\n    // Dome\r\n    P.push({\r\n      g: new T.SphereGeometry(w * 0.4, 6, 3, 0, Math.PI * 2, 0, Math.PI / 2),\r\n      y: height\r\n    });\r\n    if (level >= 2) {\r\n      const bandGeom = new T.CylinderGeometry(w * 0.42, w * 0.42, 0.06, 6, 1);\r\n      for (let i = 1; i <= 7; i++) {\r\n        P.push({\r\n          g: bandGeom,\r\n          y: height * (i + 0.5) / 8,\r\n          accent: 1\r\n        });\r\n      }\r\n    }\r\n  } else if (kind === 'landing') {\r\n    const R = w * (0.40 + level * 0.018);\r\n    P.push({ g: new T.CylinderGeometry(R, R * 1.06, height, 12, 1), y: height / 2 });\r\n    P.push({ g: new T.TorusGeometry(R * 0.82, 0.07, 4, 12), y: height + 0.03, rx: Math.PI / 2, accent: 1 });\r\n    P.push({ g: new T.CylinderGeometry(R * 0.22, R * 0.22, 0.06, 6, 1), y: height + 0.03, accent: 1 });\r\n    const lamp = new T.CylinderGeometry(0.05, 0.05, 0.9, 3, 1);\r\n    for (let i = 0; i < 4; i++) {\r\n      const a = Math.PI / 4 + i * Math.PI / 2;\r\n      P.push({ g: lamp, x: Math.cos(a) * R * 0.95, z: Math.sin(a) * R * 0.95, y: height + 0.45 });\r\n      P.push({ g: new T.OctahedronGeometry(0.1, 0), x: Math.cos(a) * R * 0.95, z: Math.sin(a) * R * 0.95, y: height + 0.95, accent: 1 });\r\n    }\r\n  } else if (kind === 'yard') {\r\n    // Slab\r\n    P.push({\r\n      g: new T.BoxGeometry(w * 0.9, height, d * 0.9),\r\n      y: height / 2\r\n    });\r\n    // Corner posts\r\n    const postGeom = new T.CylinderGeometry(0.06, 0.06, 0.8, 4, 1);\r\n    for (const sx of [-1, 1]) {\r\n      for (const sz of [-1, 1]) {\r\n        P.push({\r\n          g: postGeom,\r\n          x: sx * w * 0.4,\r\n          z: sz * d * 0.4,\r\n          y: height + 0.4\r\n        });\r\n      }\r\n    }\r\n    // Rack bars\r\n    if (level >= 2) {\r\n      P.push({\r\n        g: new T.BoxGeometry(w * 0.8, 0.06, 0.06),\r\n        y: height + 0.8,\r\n        accent: 1\r\n      });\r\n    }\r\n  }\r\n\r\n  // Each tier installs a functional module, including fixed-height pads and traps.\r\n  // Twelve triangles per stage; low profiles remain low so landing/construction reads.\r\n  const modules={tower:'ammo feed',wall:'armor buttress',generator:'cooling bank',press:'hydraulic ram',mortar:'shell rack',beacon:'relay receiver',trap:'spring jaw',fence:'tension brace',silo:'transfer pump',landing:'guidance console',yard:'equipment locker'};\r\n  const low=['trap','landing','yard','wall','fence'].includes(kind);\r\n  for(let i=0;i<level-1;i++){\r\n    const side=i%2?-1:1,back=i<2?-1:1;\r\n    P.push({g:new T.BoxGeometry(w*.19,low?.2+i*.12:height*(.22+i*.04),d*.22),x:side*w*.32,z:back*d*.31,y:low?height+.1+i*.06:height*(.11+i*.02),accent:i===3?1:0,machine:modules[kind]});\r\n  }\r\n  // Normalize placed parts before constraining independent sites to their footprint.\r\n  // Connected walls deliberately retain their joining arms.\r\n  const bounds=new T.Box3();\r\n  for(const p of P){p.g=p.g.clone();if(p.sy)p.g.scale(1,p.sy,1);if(p.rx)p.g.rotateX(p.rx);p.g.translate(p.x||0,p.y||0,p.z||0);delete p.x;delete p.y;delete p.z;delete p.rx;delete p.sy;p.g.computeBoundingBox();bounds.union(p.g.boundingBox);}\r\n  if(!links.length){const sx=Math.min(1,w*.49/Math.max(Math.abs(bounds.min.x),Math.abs(bounds.max.x))),sz=Math.min(1,d*.49/Math.max(Math.abs(bounds.min.z),Math.abs(bounds.max.z)));for(const p of P)p.g.scale(sx,1,sz);}\r\n  return P;\r\n}\r\n","modules/coach-world-rts/unit-levels.mjs":"// modules/coach-world-rts/unit-levels.mjs\r\nconst sectionOf = u => u.section ?? u.definition?.section;\r\nexport const isBoss = u => !!(u.boss || u.role === 'boss' || u.definition?.role === 'boss');\r\n\r\nexport function unitLevel(unit, base) {\r\n  let level = base?.buildings?.[`hall-${sectionOf(unit)}`]?.level ?? 1;\r\n  if (isBoss(unit)) level += 1;\r\n  return Math.min(6, Math.max(1, level));\r\n}\r\n\r\nexport function levelStats(level) {\r\n  const l = Math.min(6, Math.max(1, level));\r\n  return { w: Math.floor(l / 2), a: Math.floor(l / 2), speedPct: 4 * l, ability1: l >= 3 };\r\n}\r\n\r\nexport const FACTION_AURA = Object.freeze({\r\n  chest: 'bluefire',\r\n  quads: 'sunburn',\r\n  glutes: 'magicbubbles',\r\n  'arms-shoulders': 'casting',\r\n  yoga: 'protectioncircle',\r\n  'martial-arts': 'firespin',\r\n  cardio: 'vortex'\r\n});\r\n\r\nexport const LEVEL_LOOK = Object.freeze({\r\n  2: { tint: true },\r\n  3: { aura: true, trail: false },\r\n  4: { aura: true, trail: true },\r\n  5: { aura: true, trail: true, doubleAura: true, secondAura: 'nebula' }\r\n});\r\n\r\n// What a level looks like (levels above 5 keep the level-5 look).\r\nexport function lookFor(level) {\r\n  const l = Math.min(5, level);\r\n  return { tint: l >= 2, aura: l >= 3, trail: l >= 4, second: l >= 5 ? LEVEL_LOOK[5].secondAura : null };\r\n}\r\n","modules/coach-world-rts/unit-portrait.mjs":"import {loadSheet} from './runtime.mjs';\r\nimport {TICK} from './clips.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\n\r\nlet cachedName=null,cachedSheet=null;\r\nconst sheetName=u=>u?.rtsSheet||(u?.role==='worker'?'myr5':null);\r\nconst getSheet=async name=>{if(cachedName===name&&cachedSheet)return cachedSheet;cachedName=name;cachedSheet=loadSheet(name).catch(()=>null);return cachedSheet;};\r\nconst sourceId=u=>u?.role==='worker'?null:u?.sourceCoachId;\r\n\r\n/** Draw one catalog unit from its actual RTS atlas. Returns false when its source is unavailable. */\r\nexport async function drawUnitPortrait(ctx,u,{base=null,time=0,action='idle',direction='S',animate=false,scale=2}={}){\r\n const name=sheetName(u);if(!name)return false;\r\n const sheet=await getSheet(name);if(!sheet)return false;\r\n const atlas=sheet.atlas,unit=sourceId(u)?atlas.units?.[sourceId(u)]:null;\r\n if(sourceId(u)&&!unit)return false;\r\n const clips=unit?.clips||atlas.clips;\r\n // Animated portrait: the atlas idle clip is a single frame (its authored motion is a 1px bob), so loop walk instead.\r\n const act=animate&&clips[action]?.frames<=1&&clips.walk?'walk':action,clip=clips[act];if(!clip)return false;\r\n const dir=atlas.dirs[direction];if(!dir)return false;\r\n const [stored,mirrored]=dir;\r\n const total=clip.ticks.reduce((a,b)=>a+b,0);let frame=0,tick=0;\r\n if(animate&&clip.frames>1){let elapsed=Math.floor(Math.max(0,time)/TICK);elapsed=act==='death'?Math.min(elapsed,total-1):elapsed%total;for(;frame<clip.frames-1&&elapsed>=clip.ticks[frame];frame++)elapsed-=clip.ticks[frame];tick=frame;}\r\n const [x,y,w,h]=clip.rects[stored][frame];\r\n const sprite=sheet.dye(FACTION_BY_ID[u.section||u.faction||base?.faction]?.color||'#69d7d1');if(!sprite)return false;\r\n const size=scale,dx=-w*size/2,dy=-h*size/2;\r\n ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);ctx.save();ctx.imageSmoothingEnabled=false;ctx.translate(Math.round(ctx.canvas.width/2),Math.round(ctx.canvas.height/2));if(mirrored)ctx.scale(-1,1);ctx.drawImage(sprite,x,y,w,h,dx,dy,w*size,h*size);ctx.restore();\r\n return {source:name==='myr5'?'worker':sourceId(u),action,direction,frame:tick};\r\n}\r\n\r\nexport function clearUnitPortraitCache(){cachedName=null;cachedSheet=null;}\r\n","modules/coach-world-rts/unit-trail.mjs":"// 8-point neon trails. 2D: createTrail2d().push(x,y) per moved step, draw(ctx,color). 3D: createTrail3d(T,{color}) wraps ship-path createRibbon.\r\nimport { createRibbon } from './ship-path.mjs';\r\n\r\nexport const TRAIL_POINTS = 8;\r\n\r\nexport function createTrail2d({ points = TRAIL_POINTS, minStep = 1.5 } = {}) {\r\n  const pts = [];\r\n  return {\r\n    pts,\r\n    push(x, y) {\r\n      const l = pts.at(-1);\r\n      if (l && Math.hypot(x - l.x, y - l.y) < minStep) return;\r\n      pts.push({ x, y });\r\n      if (pts.length > points) pts.shift();\r\n    },\r\n    // oldest point fades out; neon = wide dim stroke under a thin bright one\r\n    draw(ctx, color) {\r\n      if (pts.length < 2) return;\r\n      ctx.save();\r\n      ctx.globalCompositeOperation = 'lighter'; ctx.lineCap = 'round'; ctx.strokeStyle = color;\r\n      for (let i = 1; i < pts.length; i++) {\r\n        const a = i / (pts.length - 1);\r\n        for (const [w, k] of [[4, .3], [2, 1]]) {\r\n          ctx.globalAlpha = a * k; ctx.lineWidth = w;\r\n          ctx.beginPath(); ctx.moveTo(pts[i - 1].x, pts[i - 1].y); ctx.lineTo(pts[i].x, pts[i].y); ctx.stroke();\r\n        }\r\n      }\r\n      ctx.restore();\r\n    },\r\n    clear() { pts.length = 0; }\r\n  };\r\n}\r\n\r\n// World-space ribbon (metres); push a {x,y,z} each frame the unit moved.\r\nexport function createTrail3d(T, { color = 0xffffff, width = .5, points = TRAIL_POINTS } = {}) {\r\n  const r = createRibbon(T, { points, color, width });\r\n  r.mesh.frustumCulled = false; r.mesh.renderOrder = 4;\r\n  let last = null;\r\n  return {\r\n    mesh: r.mesh,\r\n    push(p, minStep = .25) {\r\n      if (last && Math.hypot(p.x - last.x, p.z - last.z) < minStep) return false;\r\n      last = { x: p.x, y: p.y, z: p.z }; r.push(last); r.update(); return true;\r\n    },\r\n    dispose() { r.dispose(); r.mesh.removeFromParent(); }\r\n  };\r\n}\r\n","modules/coach-world-rts/unlocks-panel.mjs":"// Next unlocks (Sheet). mountUnlocksPanel(host,{getBase}) -> {update(),dispose()}. Rows = coach-world/levels.mjs UNLOCKS + faction-switch milestones (state.mjs faction rule: free at 5/10/15/20).\r\n// Re-renders on a 500 ms timer and stops once the root has been detached (hud dispose removes the sheet).\r\nimport {UNLOCKS,LEVEL_XP} from '../coach-world/levels.mjs';\r\nimport {accountLevel} from '../coach-world/state.mjs';\r\nconst LABEL={home:'Landing Zone',flag:'Flag',gym:'Gym base',work:'Work base','npc-raids':'NPC raids'};\r\nconst FACTION_STEPS=Object.freeze([5,10,15,20].map(level=>({id:`faction-${level}`,label:'Faction switch',level})));\r\n\r\n// Pure: level, XP progress and the rows with unlocked state. Level/XP mirror accountLevel (xp = building levels, landing excluded).\r\nexport function unlockRows(base){\r\n const level=base?accountLevel(base):1;\r\n const xp=base?Object.values(base.buildings||{}).reduce((s,x)=>s+(x.id==='landing'?0:x.level||0),0):0;\r\n const lo=LEVEL_XP[level-1]??0,hi=LEVEL_XP[level];\r\n const pct=hi==null?1:Math.max(0,Math.min(1,(xp-lo)/(hi-lo)));\r\n const rows=[...UNLOCKS.map(u=>({id:u.id,label:LABEL[u.id]||u.id,level:u.level})),...FACTION_STEPS]\r\n  .sort((a,b)=>a.level-b.level).map(r=>({...r,unlocked:level>=r.level}));\r\n return {level,xp,next:hi??null,pct,rows};\r\n}\r\n\r\nconst mk=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n;};\r\n\r\nexport function mountUnlocksPanel(host,{getBase=()=>null}={}){\r\n const root=mk('section','cw-rts-unlocks');root.dataset.unlocks='';root.setAttribute('aria-label','Next unlocks');host.append(root);\r\n let sig='',seen=false,timer=0;\r\n function render(){\r\n  if(root.isConnected)seen=true;else if(seen){clearInterval(timer);return;}\r\n  const r=unlockRows(getBase());const s=JSON.stringify(r);if(s===sig)return;sig=s;\r\n  const head=mk('div','cw-rts-unlocks-head'),xp=mk('div','cw-rts-xpbar');xp.setAttribute('role','img');xp.setAttribute('aria-label',`Level ${r.level}, ${Math.round(r.pct*100)}% to next`);\r\n  const fill=mk('i','cw-rts-xpfill');fill.style.width=Math.round(r.pct*100)+'%';xp.append(fill);\r\n  head.append(mk('b','cw-rts-unlocks-lv',`LV ${r.level}`),mk('span','cw-rts-unlocks-xp',r.next==null?'MAX':`${r.xp} / ${r.next} XP`),xp);\r\n  const list=mk('ul','cw-rts-unlock-list');const nextLocked=r.rows.find(x=>!x.unlocked);\r\n  for(const x of r.rows){\r\n   const li=mk('li',`cw-rts-unlock ${x.unlocked?'is-unlocked':'is-locked'}${x===nextLocked?' is-next':''}`);li.dataset.unlock=x.id;li.dataset.state=x.unlocked?'unlocked':'locked';\r\n   li.append(mk('span','cw-rts-unlock-name',x.label),mk('span','cw-rts-unlock-lvl',`Lv ${x.level}`),mk('span','cw-rts-unlock-state',x.unlocked?'UNLOCKED':`LOCKED: reach Lv ${x.level}`));\r\n   list.append(li);\r\n  }\r\n  root.replaceChildren(mk('h4','cw-rts-unlocks-title','NEXT UNLOCKS'),head,list);\r\n }\r\n timer=setInterval(render,500);render();\r\n return {update(){sig='';render();},dispose(){clearInterval(timer);root.remove();}};\r\n}\r\n","modules/coach-world-rts/walk-hud.mjs":"// W8a pixel HUD for the walkthrough: hud.css look (Pixelify/m5x7 fonts, family bezel) without touching hud.css, plus a heading tick strip.\r\nconst URLS={font:new URL('./ui/pixelify-sans.woff2',import.meta.url).href,num:new URL('./ui/m5x7.woff2',import.meta.url).href,\r\n 'occupied-worker':new URL('./ui/bezel-occupied.png',import.meta.url).href,'seeded-warp':new URL('./ui/bezel-warp.png',import.meta.url).href,'consumed-seed':new URL('./ui/bezel-seed.png',import.meta.url).href};\r\nconst CSS=(bezel,color)=>`\r\n@font-face{font-family:'CW Pixelify';src:url(${URLS.font}) format('woff2');font-weight:400 700;font-display:swap}\r\n@font-face{font-family:'CW m5x7';src:url(${URLS.num}) format('woff2');font-display:swap}\r\n[data-rts-walk-root]{--f:${color};font:16px 'CW m5x7',ui-monospace,monospace}\r\n[data-rts-walk-root] button{font:400 16px 'CW Pixelify',ui-monospace,monospace;color:#dfe8ee;background:none;min-width:96px;min-height:48px;border:14px solid transparent;border-image:url(${bezel}) 24 fill/14px stretch;image-rendering:pixelated;padding:0 4px;cursor:pointer}\r\n[data-rts-walk-root] button:active{filter:brightness(1.4)}\r\n[data-rts-walk-root] [data-compass]{position:absolute;left:50%;top:6px;transform:translateX(-50%);width:min(94vw,420px);height:auto;image-rendering:pixelated;pointer-events:none}\r\n[data-rts-walk-root] [data-photo]{position:absolute;right:10px;bottom:calc(78px + env(safe-area-inset-bottom,0px));z-index:3}\r\n[data-rts-walk-root] [data-chip]{top:calc(6px + min(94vw,420px)*.11 + 4px)!important;font:16px 'CW m5x7',ui-monospace,monospace;color:#dfe8ee;text-shadow:0 1px 0 #000,1px 0 0 #000}`;\r\n// capture(): host-supplied photo handler (walkthrough wires captureWorldPhoto + showPhotoPreview); no button without it.\r\nexport function createWalkHud(root,{doc=root.ownerDocument,policy='occupied-worker',color='#88c6ae',capture}={}){\r\n const style=doc.createElement('style');style.textContent=CSS(URLS[policy]||URLS['occupied-worker'],color);root.prepend(style);\r\n const cv=doc.createElement('canvas');cv.width=188;cv.height=20;cv.dataset.compass='';root.append(cv);const g=cv.getContext?.('2d');let lastDeg=null;\r\n let photo=null;\r\n if(typeof capture==='function'){photo=doc.createElement('button');photo.type='button';photo.dataset.photo='';photo.textContent='Photo';photo.setAttribute?.('aria-label','Take photo');photo.onclick=()=>{try{const r=capture();r?.catch?.(()=>{});}catch{/* capture failures never break the walk */}};root.append(photo);}\r\n const SPAN=120,PX=cv.width/SPAN;\r\n return {\r\n  // yaw: radians, positive = turned left of the HQ direction. Bearing 0 = \"HQ\", increasing clockwise.\r\n  dispose(){photo?.remove();},\r\n  update(yaw){\r\n   if(!g)return;const deg=((-yaw*180/Math.PI)%360+360)%360;if(lastDeg!==null&&Math.abs(deg-lastDeg)<.4)return;lastDeg=deg;\r\n   g.clearRect(0,0,188,20);g.fillStyle='#0d1217e6';g.fillRect(0,0,188,16);g.fillStyle=color;g.fillRect(0,16,188,1);\r\n   g.font=\"16px 'CW m5x7',monospace\";g.textAlign='center';g.textBaseline='top';\r\n   for(let t=Math.floor((deg-SPAN/2)/15)*15;t<=deg+SPAN/2;t+=15){\r\n    const x=Math.round(94+(t-deg)*PX),b=((t%360)+360)%360,major=b%45===0;g.fillStyle=major?'#dfe8ee':'#8a99a6';g.fillRect(x,major?9:12,1,major?7:4);\r\n    if(major){g.fillStyle=b===0?color:'#dfe8ee';g.fillText(b===0?'HQ':String(b),x,0);}\r\n   }\r\n   g.fillStyle='#ffcf82';g.fillRect(93,15,3,2);g.fillRect(94,17,1,2);\r\n  },\r\n };\r\n}\r\n","modules/coach-world-rts/walkthrough.mjs":"// W8a 360 look-around of your own base (seam S3). createWalkthrough({host,getBase,signal}) -> {start(event,{release}),dispose()}.\r\n// start() runs permission prompts synchronously inside the tap, then awaits release() (the camera view's WebGL release) before claiming the GPU.\r\n// Rungs: sensors (iOS + Android 3DoF) -> drag-look when denied/absent; rear camera passthrough when granted, else full VR sky.\r\nimport {claimGraphics,releaseGraphics} from '../graphics-lifecycle.mjs';\r\nimport {createPixelBillboards,disposePixelObject,fitPixelRenderer} from '../coach-world/sprite-billboards.mjs';\r\nimport {createTerrain,drawTerrain} from '../coach-world/terrain.mjs';\r\nimport {createPoseSource} from './pose-source.mjs';\r\nimport {createBaseMeshes,layoutBase,spawnTile,worldYaw,SCALE} from './base-meshes.mjs';\r\nimport {createAnchor,validHome,chipText,AHEAD_M} from './ar-anchor.mjs';\r\nimport {createCompass} from './geo-follow.mjs';\r\nimport {createCrewBillboards} from './crew-billboards.mjs';\r\nimport {createWalkHud} from './walk-hud.mjs';\r\nimport {yawOf} from './pose-source.mjs';\r\nimport {placeNode} from './mining.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {createPs1Material} from './ps1-material.mjs';\r\nimport {mountWeatherFx} from './weather-fx.mjs';\r\nimport {readArScale,passthroughFov,crewHeightFor} from './ar-scale.mjs';\r\nimport {captureWorldPhoto,showPhotoPreview} from '../coach-world/photo.mjs';\r\nimport {watchWeather,overrideFrom} from './weather.mjs';\r\nimport {createAlienSky} from './sky-alien.mjs';\r\nimport {createSkyCritters} from './sky-critters.mjs';\r\nimport {createOutline} from './pixel-outline.mjs';\r\nimport {createNpcClientGetter} from './ar-catch.mjs';\r\n\r\nexport const PS1_SCALE=2,FRAME_MS=33,FAR_PLANE=200,AR_FAR=400,BEACON_MIN_M=30; // AR_FAR: the compressed world ends at 120 m (ar-scale DH)\r\nconst defaults={\r\n loadRts:()=>import('./runtime.mjs'),\r\n loadScene:()=>import('../coach-world/canvas-scene.mjs'),\r\n loadThree:()=>import('/vendor/three/three.module.js'),\r\n loadWorldClient:()=>import('../coach-world/client.mjs'),\r\n createRenderer:(T,canvas)=>new T.WebGLRenderer({canvas,alpha:true,antialias:false}),\r\n fit:fitPixelRenderer,\r\n getUserMedia:c=>navigator.mediaDevices?.getUserMedia?.(c),\r\n groundCanvas:base=>{const c=document.createElement('canvas');c.width=c.height=768;drawTerrain(c.getContext('2d'),createTerrain(base),{unit:32,ox:0,oy:0});return c;},\r\n};\r\n// No-camera (virtual sky) mode only; passthrough uses the real camera FOV (ar-scale passthroughFov). Portrait phones need a taller fov to keep a ~75 deg horizontal view (capped so the PS1 look does not melt).\r\nexport const fovFor=aspect=>aspect>=1?70:Math.min(100,2*Math.atan(Math.tan(37.5*Math.PI/180)/aspect)*180/Math.PI);\r\nconst stopStream=s=>s?.getTracks?.().forEach(t=>t.stop());\r\n\r\nfunction skyDome(T){\r\n const g=new T.SphereGeometry(90,12,8),p=g.attributes.position,c=[],top=new T.Color(0x1d2c55),mid=new T.Color(0x7d6fa8),low=new T.Color(0x25333c),k=new T.Color();\r\n for(let i=0;i<p.count;i++){const y=p.getY(i)/90;k.copy(y>0?mid.clone().lerp(top,Math.min(1,y*1.6)):low.clone().lerp(mid,Math.max(0,1+y*4)));c.push(k.r,k.g,k.b);}\r\n g.setAttribute('color',new T.Float32BufferAttribute(c,3));\r\n const m=new T.Mesh(g,new T.MeshBasicMaterial({vertexColors:true,side:T.BackSide,fog:false,depthWrite:false}));m.renderOrder=-1;m.name='rts-sky';return m;\r\n}\r\n\r\nexport function createWalkthrough({host,getBase,signal,deps={}}){\r\n const D={...defaults,createCompass,...deps},doc=host.ownerDocument??document;\r\n let disposed=false,gen=0,res=null;\r\n const dead=g=>disposed||signal?.aborted||g!==gen;\r\n\r\n function cleanup(r){\r\n  if(!r||r.done)return;r.done=true;\r\n  cancelAnimationFrame(r.raf);r.listen?.abort();r.wxAbort?.abort();r.observer?.disconnect();r.pose?.dispose();r.anchor?.dispose();r.compass?.dispose();\r\n  stopStream(r.stream);if(r.video)r.video.srcObject=null;\r\n  if(r.scene)disposePixelObject(r.scene);\r\n  r.boarding?.dispose();r.npcs?.dispose();try{r.npcClient?.dispose();}catch{}r.alien?.dispose();r.critters?.dispose();r.outline?.dispose();r.weather?.dispose();r.worldLayer?.dispose();r.meshes?.dispose();r.layer?.dispose();r.crew?.dispose();r.sim?.dispose();\r\n  const rd=r.renderer;r.renderer=null;\r\n  if(rd)void releaseGraphics(r.canvas,rd.getContext?.(),()=>{rd.dispose();rd.forceContextLoss?.();});\r\n  r.claim?.();r.root?.remove();\r\n  try{r.onExit?.();}catch{} // pin-drop: lets the caller leave the AR pod\r\n }\r\n function stop(){gen++;const r=res;res=null;cleanup(r);}\r\n\r\n function ui(){\r\n  const root=doc.createElement('div');root.dataset.rtsWalkRoot='';\r\n  root.style.cssText='position:fixed;inset:0;z-index:2147483000;background:#0b0f14;overflow:hidden;touch-action:none;color:#fff';\r\n  root.innerHTML='<video muted playsinline style=\"position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:none\"></video><canvas aria-label=\"360 look around your base\" style=\"position:absolute\"></canvas><div data-chip style=\"position:absolute;left:8px;right:8px;text-align:center\">Starting…</div><div style=\"position:absolute;left:0;right:0;bottom:calc(10px + env(safe-area-inset-bottom,0px));display:flex;gap:8px;justify-content:center\"><button data-recenter>Face base</button><button data-exit>Exit</button></div>';\r\n  host.append(root);return root;\r\n }\r\n\r\n function start(event,{release,onExit}={}){\r\n  if(disposed||res||signal?.aborted)return;\r\n  const myGen=++gen,r=res={done:false,listen:new AbortController(),onExit};\r\n  // --- synchronous, inside the gesture: permission prompts first ---\r\n  const pose=r.pose=D.pose??createPoseSource({win:doc.defaultView??globalThis}),perm=pose.requestPermission();\r\n  const home=getBase().home,arHome=validHome(home);\r\n  if(arHome){r.compass=D.compass??D.createCompass(doc.defaultView??globalThis);void Promise.resolve(r.compass.request?.()).catch(()=>{});}\r\n  let camP;try{camP=Promise.resolve(D.getUserMedia({audio:false,video:{facingMode:{ideal:'environment'}}})).catch(()=>null);}catch{camP=Promise.resolve(null);}\r\n  camP=camP.then(s=>{if(!s)return null;if(dead(myGen)){stopStream(s);return null;}r.stream=s;return s;});\r\n  const root=r.root=ui(),hud=createWalkHud(root,{doc,policy:FACTION_BY_ID[getBase().faction]?.policy,color:FACTION_BY_ID[getBase().faction]?.color,capture:async()=>{if(r.done)return;r.snap?.();const blob=await captureWorldPhoto({video:r.video,overlay:r.canvas,alive:()=>!r.done});showPhotoPreview(blob,root,{signal:r.listen.signal});}}),chip=root.querySelector('[data-chip]'),canvas=r.canvas=root.querySelector('canvas'),video=r.video=root.querySelector('video');\r\n  root.querySelector('[data-exit]').onclick=D.liftoff?()=>{r.lift??=performance.now();}:stop;root.querySelector('[data-recenter]').onclick=()=>pose.recenter();\r\n  void (async()=>{\r\n   try{\r\n    await release?.();if(dead(myGen))return;\r\n    r.claim=claimGraphics('coach-world');await r.claim.ready;if(dead(myGen))return;\r\n    const [T,permission,stream]=await Promise.all([D.loadThree(),perm,camP]);if(dead(myGen))return;\r\n    pose.begin(permission);\r\n    const base=getBase(),renderer=r.renderer=D.createRenderer(T,canvas),scene=r.scene=new T.Scene(),camera=new T.PerspectiveCamera(70,1,.1,FAR_PLANE);\r\n    scene.fog=new T.FogExp2(0x25333c,.02);\r\n    // R81: depth-edge outline (lane D) + alien sky that fades in as you look up (lane F, pitch 5..15 deg)\r\n    const outline=r.outline=createOutline(T,renderer),seed=[...String(base.seed??1)].reduce((h,c)=>Math.imul(h,31)+c.charCodeAt(0)|0,7),alien=r.alien=createAlienSky(T,{seed,faction:base.faction,reducedMotion:globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches});scene.add(alien.group);\r\n    const sun=new T.DirectionalLight(0xfff0d8,2.2);sun.position.set(-4,8,3);scene.add(new T.AmbientLight(0xffffff,1.1),sun);\r\n    const world=new T.Group();world.rotation.y=worldYaw();const sp=spawnTile();\r\n    const layer=r.layer=createPixelBillboards(T,D.billboardCanvas?{getCanvas:D.billboardCanvas}:undefined),meshes=r.meshes=createBaseMeshes(T,base,{layer,resolution:[320,180]});\r\n    const gtex=new T.CanvasTexture(D.groundCanvas(base));gtex.magFilter=gtex.minFilter=T.NearestFilter;gtex.generateMipmaps=false;gtex.colorSpace=T.SRGBColorSpace;\r\n    const ground=new T.Mesh(new T.PlaneGeometry(24*SCALE,24*SCALE,12,12),createPs1Material(T,{map:gtex,vertexColors:false,resolution:[320,180]}));\r\n    ground.rotation.x=-Math.PI/2;ground.position.set((12-sp.x)*SCALE,0,(12-sp.y)*SCALE);\r\n    const sky=skyDome(T);\r\n    world.add(ground,meshes.group,layer.group);const arRoot=new T.Group();arRoot.add(world);scene.add(sky,arRoot);\r\n    // life: ore node + the runtime's workers (same state the 2D map draws)\r\n    let nd=null;try{nd=placeNode(base)?.node;}catch{}\r\n    if(nd){const ore=new T.Mesh(new T.IcosahedronGeometry(.45*SCALE,0),new T.MeshLambertMaterial({color:0xf0c93c,flatShading:true}));ore.position.set((nd.x+.5-sp.x)*SCALE,.35*SCALE,(nd.y+.5-sp.y)*SCALE);world.add(ore);}\r\n    let crew=null,rts=null,arS=0; // arS: base scale while in AR (0 = virtual sky, crew keeps its base-view sizes)\r\n    const heightFor=u=>arS?crewHeightFor(arS)(u):(u.scale||1);\r\n    Promise.resolve().then(()=>D.loadRts()).then(async m=>{if(!m||dead(myGen))return;const sheet=await m.loadSheet().catch(()=>null);if(dead(myGen))return;rts={m,sheet};\r\n     // The 2D base scene is gone while the camera tab is open, so this view owns a hidden one: the single stepper of crew + runtime (mining, walking) that the billboards read.\r\n     if(m.createRts){const sc=await D.loadScene();if(dead(myGen))return;const cv=doc.createElement('canvas');cv.setAttribute('aria-hidden','true');cv.style.cssText='position:fixed;left:-9999px;top:0;width:96px;height:96px;pointer-events:none';root.append(cv);r.sim=sc.mountBaseScene(cv,{getBase,rts:m.createRts({ownerId:D.ownerId})});}\r\n     crew=r.crew=createCrewBillboards(T,{units:D.units??m.getUnitDots,poseFor:m.getPose,getSheet:()=>sheet,getFactionSheet:n=>m.factionSheet?.(n),getBase,scale:SCALE,origin:sp,heightFor});world.add(crew.group);}).catch(()=>{});\r\n    const passthrough=!!stream;\r\n    let anchor=null;\r\n    if(passthrough){\r\n     video.srcObject=stream;video.style.display='block';sky.visible=ground.visible=false;try{await video.play();}catch{}if(dead(myGen))return;\r\n     // AR: the base is ~3x life size (HQ ~9-10 m tall) with the HQ centre at arRoot's origin, which sits on your home (or 8 m ahead).\r\n     const hq=layoutBase(base).find(b=>b.id==='hq');\r\n     arS=readArScale();world.scale.setScalar(arS);world.position.set(hq.x,0,hq.z).applyAxisAngle(new T.Vector3(0,1,0),worldYaw()).multiplyScalar(-arS);\r\n     scene.fog.density=.008;arRoot.position.set(0,0,-(AHEAD_M+hq.w*arS/2)); // nearest HQ wall 8 m ahead\r\n     if(arHome){anchor=r.anchor=createAnchor({home,geo:D.geo,compass:r.compass,poseYaw:()=>yawOf(pose.pose().quaternion),absolute:()=>pose.absolute}).start();root.querySelector('[data-recenter]').style.display='none';}\r\n    }\r\n    if(!passthrough&&D.forceAnchor&&arHome){anchor=r.anchor=createAnchor({home,geo:D.geo,compass:r.compass,poseYaw:()=>yawOf(pose.pose().quaternion),absolute:()=>pose.absolute}).start();root.querySelector('[data-recenter]').style.display='none';arS=readArScale();world.scale.setScalar(arS);const hq=layoutBase(base).find(b=>b.id==='hq');world.position.set(hq.x,0,hq.z).applyAxisAngle(new T.Vector3(0,1,0),worldYaw()).multiplyScalar(-arS);}\r\n    // pin-drop: the generated world (terrain, roads, MOM buildings, traffic...) in true metres around the same origin as the base, plus a beacon so a far base never just vanishes\r\n    let wl=null,eyeY=0,beacon=null;const up=new T.Vector3(0,1,0),tmp=new T.Vector3();\r\n    if(D.worldLayer&&anchor){\r\n     wl=r.worldLayer=D.worldLayer(T,{center:{x:0,z:0},scale:1,resolution:[320,180],getBase,compress:true});arRoot.add(wl.group);wl.setFade?.(1);ground.visible=false;camera.far=AR_FAR;camera.updateProjectionMatrix();scene.fog.density=.0014;\r\n     beacon=new T.Mesh(new T.CylinderGeometry(1.5,1.5,260,8,1,true),new T.MeshBasicMaterial({color:0xff5a3a,fog:false,transparent:true,opacity:.55,depthWrite:false,side:T.DoubleSide}));beacon.position.y=130;beacon.name='base-beacon';arRoot.add(beacon);r.beacon=beacon;\r\n     // lane I city NPCs around the PIN (arRoot's origin is the base; npcRoot follows the pin every frame). Land beside hands pin-drop the NPC's own spot (metres E/S of the pin); pin-drop keeps the 3 m gap.\r\n     if(D.pinCell){const npcRoot=r.npcRoot=new T.Group();npcRoot.name='ar-npc-root';arRoot.add(npcRoot);\r\n      // Talk/catch need real saved state: this view owns one world client (own signal, disposed with the view), loaded on the first talk/catch, never before.\r\n      // Prefer the host's own client (D.client, e.g. the World view's) so purchases/catches land in the snapshot it already renders; else own a lazy one (disposed with this view).\r\n      // Every command result also goes out through D.onWorldChange so the host refreshes before it draws again. Loaded on the first talk/catch, never before.\r\n      const npcClient=r.npcClient=createNpcClientGetter({shared:D.client,create:async()=>(await D.loadWorldClient()).createWorldClient({signal:r.listen.signal}),onWorldChange:D.onWorldChange,alive:()=>!dead(myGen)}),getClient=npcClient.get;\r\n      import('./ar-npcs.mjs').then(m=>{if(dead(myGen))return;r.npcs=m.attachArNpcs(T,{scene,camera,canvas,world:npcRoot,scale:1,pinCell:D.pinCell,focus:D.target,getClient,isCaught:e=>getBase().wild?.some(w=>w.id===e.id)===true,getSheet:()=>rts?.sheet,getFactionSheet:n=>rts?.m.factionSheet?.(n),\r\n       onLandBeside:p=>{const n=r.npcs?.npcs.find(q=>q.id===p.id);D.onLandBeside?.(n?{x:n.x,z:n.z,id:n.id,name:n.name,rare:n.rare,level:n.level,look:n.look,source:n.source,heightM:n.heightM,tier:n.tier,cell:n.cell}:p);}});}).catch(e=>console.warn('coach-world ar npcs',e));}\r\n    }\r\n    // weather: micro mode (effects stay out in the fog beyond the habitat, never over the base). The group is scaled with the base (arS in AR), so the base-sized inner radius clears it.\r\n    {const wxRoot=new T.Group(),inAr=!!anchor,hq0=layoutBase(base).find(b=>b.id==='hq'),ll=home&&arHome?{lat:home.latitude,lng:home.longitude}:null;\r\n     let state=overrideFrom()||{kind:'clear',intensity:0};\r\n     if(!overrideFrom()&&ll){r.wxAbort=new AbortController();watchWeather({getLatLng:async()=>ll,onState:v=>{state=v;},signal:r.wxAbort.signal});}\r\n     scene.add(wxRoot);wxRoot.scale.setScalar(inAr?arS:1);\r\n     if(!inAr)wxRoot.position.copy(ground.position).applyAxisAngle(new T.Vector3(0,1,0),worldYaw());\r\n     const fx=(D.mountWeather||mountWeatherFx)(T,{world:wxRoot,camera,getZoom:()=>2.6,getState:()=>state,center:{x:0,z:0},viewH:180});\r\n     r.weather={fx,sync:()=>{if(inAr){wxRoot.position.copy(arRoot.position);wxRoot.rotation.y=arRoot.rotation.y;}},dispose(){fx.dispose?.();wxRoot.removeFromParent();}};}\r\n    const crit=r.critters=createSkyCritters(T,{seed,scale:arS||1});scene.add(crit.mesh);const fwd=new T.Vector3();\r\n    const label=()=>passthrough?(anchor?chipText(anchor.status,anchor.distance()):chipText('none'))+(pose.mode==='drag'?' · drag to look':''):'Virtual sky'+(pose.mode==='drag'?' · drag to look':' · turn your body to look');\r\n    chip.textContent=label();\r\n    if(typeof D.onBoard==='function')import('../coach-flight/boarding.mjs').then(m=>{if(dead(myGen))return;r.boarding=m.attachBoarding(T,{scene,parking:world,camera,canvas,root,getBase,onBoard:()=>{if(dead(myGen))return;r.onExit=null;stop();D.onBoard();}});}).catch(e=>console.warn('ship boarding',e));\r\n    const resize=()=>{if(r.renderer&&!r.done){const s=D.fit(renderer,camera,root,PS1_SCALE);camera.fov=passthrough?passthroughFov(s.width/s.height):fovFor(s.width/s.height);camera.updateProjectionMatrix();ground.material.userData.ps1.setResolution(s.width,s.height);meshes.setResolution(s.width,s.height);outline.resize(s.width,s.height);}};\r\n    r.observer=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;r.observer?.observe(root);resize();\r\n    const sig={signal:r.listen.signal};let drag=null;\r\n    canvas.addEventListener('pointerdown',e=>{drag=[e.clientX,e.clientY];canvas.setPointerCapture?.(e.pointerId);},sig);\r\n    canvas.addEventListener('pointermove',e=>{if(!drag)return;pose.drag(e.clientX-drag[0],e.clientY-drag[1]);drag=[e.clientX,e.clientY];},sig);\r\n    for(const n of ['pointerup','pointercancel'])canvas.addEventListener(n,()=>{drag=null;},sig);\r\n    let last=-1e9,shown=pose.mode;\r\n    const frame=t=>{\r\n     if(r.done)return;r.raf=requestAnimationFrame(frame);if(doc.hidden||t-last<FRAME_MS)return;last=t;\r\n     const p=pose.pose();crew?.update();hud.update(yawOf(p.quaternion));camera.position.set(...p.position);camera.quaternion.set(...p.quaternion);\r\n     if(anchor){anchor.update();const o=anchor.offset();if(o){arRoot.position.set(-o.x,0,-o.z);arRoot.rotation.y=anchor.yaw();\r\n      if(wl){wl.update?.(t,0,camera);tmp.set(o.x,0,o.z).applyAxisAngle(up,-anchor.yaw());eyeY=wl.heightAt?.(tmp.x,tmp.z)||0;camera.position.y+=eyeY;r.npcRoot?.position.set(tmp.x,eyeY,tmp.z);if(beacon)beacon.visible=Math.hypot(o.x,o.z)>BEACON_MIN_M;} // pose.position already provides human eye height; NPC feet and camera share the same terrain ground\r\n     }}\r\n     if(r.lift!=null){const k=Math.min(1,(t-r.lift)/1200);camera.position.y+=140*k*k;root.style.opacity=String(1-Math.max(0,(k-.6)/.4));if(k>=1){stop();return;}}\r\n     const text=label();if(pose.mode!==shown||anchor&&chip.textContent!==text){shown=pose.mode;chip.textContent=text;}\r\n     r.weather?.sync();r.weather?.fx.update(FRAME_MS/1000);\r\n     fwd.set(0,0,-1).applyQuaternion(camera.quaternion);alien.group.position.copy(camera.position);alien.update(t/1000,Math.asin(Math.max(-1,Math.min(1,fwd.y)))*180/Math.PI);\r\n     crit.mesh.position.set(camera.position.x,camera.position.y-1.6,camera.position.z);crit.update(t/1000);r.npcs?.update(t);\r\n     r.boarding?.update();outline.render(scene,camera);\r\n    };\r\n    r.snap=()=>{if(r.renderer)outline.render(scene,camera);}; // photo: the WebGL buffer is only readable right after a render\r\n    root.__ar={scene,camera,renderer,pose,outline,alien,get npcs(){return r.npcs;},stats:()=>({calls:renderer.info.render.calls,tris:renderer.info.render.triangles})}; // debug/test handle (like wrap.__base3d)\r\n    root.dataset.ready='1';frame(0);\r\n   }catch(e){\r\n    console.warn('coach-world rts walkthrough',e);\r\n    if(res===r)root.querySelector('[data-chip]').textContent='360 view unavailable on this device.';\r\n   }\r\n  })();\r\n }\r\n\r\n const onAbort=()=>dispose();\r\n signal?.addEventListener('abort',onAbort,{once:true});\r\n function dispose(){if(disposed)return;disposed=true;signal?.removeEventListener('abort',onAbort);stop();}\r\n return {start,dispose,stop};\r\n}\r\n","modules/coach-world-rts/weather-fx.mjs":"// R58 alien weather FX. mountWeatherFx(T,{world,camera,getZoom,getState,center?,reducedMotion?,viewH?,rand?}) -> {update(dt seconds),dispose,stats,tint}.\r\n// <=3 draw calls: 1 LineSegments (rain streaks + forked bolts), 1 Points (splashes / fire / ion snow / fog wisps, additive PS1 squares), 1 InstancedMesh (twister debris cones).\r\n// LOD: macro (zoomed out) spreads effects over the whole city; micro (zoomed into the base) keeps everything OUTSIDE the base radius, in/behind the fog ring.\r\n// Mount in base3d with ONE line: see attachWeather() at the bottom.\r\nimport { watchWeather, overrideFrom } from './weather.mjs';\r\nimport { createDustMotes } from './dust-motes.mjs';\r\nimport { createWindStreaks } from './wind-streaks.mjs';\r\nimport { createInsects } from './insects.mjs';\r\nimport { nightAmount } from './sprite-shade.mjs';\r\n\r\nexport const SCALE = 1.6, BASE_R = 12 * SCALE, MICRO_INNER = BASE_R + 4, MICRO_OUTER = 60, CITY_R = 520, ZOOM_RANGE = [.07, 2.6];\r\nconst clamp = (v, a, b) => Math.min(b, Math.max(a, v));\r\nconst smooth = (a, b, v) => { const t = clamp((v - a) / (b - a), 0, 1); return t * t * (3 - 2 * t); };\r\nconst lerp = (a, b, t) => a + (b - a) * t;\r\n\r\n// z01 = log position in the zoom range (same as base3d zoomFx). inner: nothing may be inside it (the base, until it dissolves at macro). outer: farthest effect.\r\nexport function lodRadii(zoom) {\r\n  const z = clamp(+zoom || 1, ZOOM_RANGE[0], ZOOM_RANGE[1]), z01 = (Math.log(z) - Math.log(ZOOM_RANGE[0])) / (Math.log(ZOOM_RANGE[1]) - Math.log(ZOOM_RANGE[0]));\r\n  return { z01, inner: MICRO_INNER * smooth(.3, .55, z01), outer: lerp(CITY_R, MICRO_OUTER, smooth(.2, .7, z01)) };\r\n}\r\n// Visual size multiplier so effects read from the macro camera (distance ~ 1/zoom), softened.\r\nexport const fxScale = zoom => Math.pow(1 / clamp(+zoom || 1, ZOOM_RANGE[0], 1), .95);\r\n// Is a point (relative to the base centre) legal for an effect of radius `pad` at this zoom?\r\nexport const allowedAt = (dx, dz, zoom, pad = 0) => { const { inner, outer } = lodRadii(zoom), d = Math.hypot(dx, dz); return d - pad >= inner && d + pad <= outer; };\r\n// Uniform-area point in the legal annulus. u1,u2 in [0,1).\r\nexport function samplePoint(u1, u2, zoom, pad = 0) {\r\n  const { inner, outer } = lodRadii(zoom), a = inner + pad, b = Math.max(a + 1, outer - pad), r = Math.sqrt(lerp(a * a, b * b, u1)), t = u2 * Math.PI * 2;\r\n  return [Math.cos(t) * r, Math.sin(t) * r];\r\n}\r\n// Push a roaming thing back into the legal annulus (hard guarantee for twisters). Returns the corrected [dx,dz].\r\nexport function confine(dx, dz, zoom, pad = 0) {\r\n  if (!dx && !dz) dx = 1e-6;\r\n  const { inner, outer } = lodRadii(zoom), lo = inner + pad, hi = Math.max(lo + 1, outer - pad), d = Math.hypot(dx, dz) || 1e-6, k = clamp(d, lo, hi) / d;\r\n  return [dx * k, dz * k];\r\n}\r\n\r\n// V5 atmosphere. Weather fog: DFU/DFO FOG_SETTINGS exp2 rows per metre (rainy .003, snowy .005; dust/ash and acid fog held lighter so the\r\n// base stays readable), mapped by a local gpt-oss-20b card and checked. The RTS view looks through more air than DFO's walker: BASE_FOG_K.\r\nexport const WEATHER_FOG = Object.freeze({ 'clear': 0, 'acid-rain': 0.003, 'red-storm': 0.003, 'twisters': 0.01, 'firelands': 0.01, 'acid-fog': 0.02, 'ion-snow': 0.005 });\r\nexport const BASE_FOG_K = .5, WIND_STREAK = [4, 12], WIND_DIR = [.8, -.6]; // streaks start/full in m/s; Vegas prevailing wind blows from the SW\r\n// per scene unit (1 m = SCALE units), capped by the view: the heaviest row (.02) leaves ~30 % at the fog far, so a zoomed-out map\r\n// never drowns (DFO's per-metre rows are for a walker; the RTS camera looks through hundreds of metres of air).\r\nexport const fogDensityFor = (kind, k, far = Infinity) => Math.min((WEATHER_FOG[kind] || 0) * BASE_FOG_K / SCALE, (WEATHER_FOG[kind] || 0) / .02 * 1.1 / far) * clamp(k, 0, 1);\r\nexport const streakStrength = (kind, k, wind) => Math.max(smooth(WIND_STREAK[0], WIND_STREAK[1], +wind || 0), kind === 'twisters' ? clamp(k, 0, 1) : 0, kind === 'firelands' ? .5 * clamp(k, 0, 1) : 0);\r\nconst TINT = { 'clear': [1, 1, 1], 'acid-rain': [.72, 1, .62], 'red-storm': [.78, .36, .4], 'twisters': [.82, .74, .95], 'firelands': [1.35, .8, .55], 'acid-fog': [.7, 1.12, .55], 'ion-snow': [.85, 1.05, 1.25] };\r\nexport const tintFor = (kind, k) => (TINT[kind] || TINT.clear).map(c => lerp(1, c, clamp(k, 0, 1)));\r\n\r\nconst BOLTS = 4, BOLT_QUADS = 22, RAIN_N = 5000, LINE_SEGS = RAIN_N, MAIN_SEGS = 13;\r\nconst FIRE_SLOTS = 14, FIRE_PTS = 14, SCORCH = 6, SPL_SLOTS = 48, SPL_PTS = 3, SNOW_N = 520, WISP_N = 48;\r\nconst O_FIRE = 0, O_SPL = O_FIRE + FIRE_SLOTS * FIRE_PTS, O_SNOW = O_SPL + SPL_SLOTS * SPL_PTS, O_WISP = O_SNOW + SNOW_N, PTS = O_WISP + WISP_N;\r\nconst TW_MAX = 5, DEBRIS = 110;\r\n\r\nconst VERT = `attribute vec4 aColor; attribute vec2 aSize; uniform float uK; varying vec4 vC; varying float vSoft;\r\nvoid main(){ vC=aColor; vSoft=aSize.y; vec4 mv=modelViewMatrix*vec4(position,1.0); gl_Position=projectionMatrix*mv; gl_PointSize=aSize.x<=0.0?0.0:clamp(aSize.x*uK/max(0.1,-mv.z),1.5,96.0); }`;\r\nconst FRAG = `varying vec4 vC; varying float vSoft;\r\nvoid main(){ float a=vC.a; if(vSoft>0.5){ float d=length(gl_PointCoord-.5)*2.0; a*=clamp(1.0-d,0.0,1.0); } if(a<=0.01) discard; gl_FragColor=vec4(vC.rgb,a); }`;\r\n\r\nexport function mountWeatherFx(T, { world, camera, getZoom = () => 1, getState = () => null, center = null, reducedMotion, viewH = 300, rand = Math.random } = {}) {\r\n  const reduced = reducedMotion ?? !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;\r\n  const ctr = () => { const c = typeof center === 'function' ? center() : center; return c ? [c.x || 0, c.z || 0] : [0, 0]; };\r\n  const root = new T.Group(); root.name = 'rts-weather'; world.add(root);\r\n  // V5: dust motes (always, 1 draw) + desert wind streaks (1 draw while windy) live outside root so clear weather keeps them.\r\n  const air = new T.Group(); air.name = 'rts-air'; world.add(air);\r\n  const motes = createDustMotes(T, { count: 500, box: 40, size: 3, color: [1, .88, .62], seed: 7, opacity: .8 }), streaks = createWindStreaks(T, { count: 110, box: 120, height: 6, length: 11, color: [1, .95, .8], seed: 9, opacity: .9 });\r\n  const insects = createInsects(T, { box: 50, size: 2.5, seed: 13 }); // V8: 4 insect families, 1 draw, fireflies by night\r\n  air.add(motes.object, streaks.object, insects.object); streaks.setWind(WIND_DIR[0], WIND_DIR[1], 0);\r\n  const airC = new T.Vector3(), airCam = new T.Vector3();\r\n  const camV = new T.Vector3(), camLocal = () => { if (!camera) return null; camV.copy(camera.position); world.updateWorldMatrix?.(true, false); return world.worldToLocal(camV); };\r\n\r\n  // ---- lines: bolts first, then rain\r\n  const lp = new Float32Array(LINE_SEGS * 6), lc = new Float32Array(LINE_SEGS * 6);\r\n  const lg = new T.BufferGeometry(); lg.setAttribute('position', new T.BufferAttribute(lp, 3).setUsage(T.DynamicDrawUsage)); lg.setAttribute('color', new T.BufferAttribute(lc, 3).setUsage(T.DynamicDrawUsage));\r\n  const lines = new T.LineSegments(lg, new T.LineBasicMaterial({ vertexColors: true, transparent: true, blending: T.AdditiveBlending, depthWrite: false })); lines.frustumCulled = false; lines.renderOrder = 5; lines.visible = false;\r\n  // bolts: camera-facing quad ribbons (halo + core per segment), one Mesh, additive\r\n  const BQ = BOLTS * BOLT_QUADS * 2, bp = new Float32Array(BQ * 12), bcol = new Float32Array(BQ * 12), bidx = new Uint32Array(BQ * 6);\r\n  for (let q = 0; q < BQ; q++) bidx.set([q * 4, q * 4 + 1, q * 4 + 2, q * 4 + 2, q * 4 + 1, q * 4 + 3], q * 6);\r\n  const bg = new T.BufferGeometry(); bg.setAttribute('position', new T.BufferAttribute(bp, 3).setUsage(T.DynamicDrawUsage)); bg.setAttribute('color', new T.BufferAttribute(bcol, 3).setUsage(T.DynamicDrawUsage)); bg.setIndex(new T.BufferAttribute(bidx, 1));\r\n  const boltMesh = new T.Mesh(bg, new T.MeshBasicMaterial({ vertexColors: true, transparent: true, blending: T.AdditiveBlending, depthWrite: false, side: T.DoubleSide, fog: false })); boltMesh.frustumCulled = false; boltMesh.renderOrder = 7; boltMesh.visible = false;\r\n  const rainX = new Float32Array(RAIN_N).fill(NaN), rainZ = new Float32Array(RAIN_N), rainY = new Float32Array(RAIN_N), rainV = new Float32Array(RAIN_N), rainB = new Float32Array(RAIN_N);\r\n  for (let i = 0; i < RAIN_N; i++) { rainV[i] = .75 + rand() * .5; rainB[i] = .5 + rand() * .5; }\r\n  let lastRain = 0;\r\n\r\n  // ---- points (one buffer; pools parked with size 0 when their kind is off)\r\n  const pp = new Float32Array(PTS * 3), pc = new Float32Array(PTS * 4), ps = new Float32Array(PTS * 2);\r\n  const pg = new T.BufferGeometry(); pg.setAttribute('position', new T.BufferAttribute(pp, 3).setUsage(T.DynamicDrawUsage)); pg.setAttribute('aColor', new T.BufferAttribute(pc, 4).setUsage(T.DynamicDrawUsage)); pg.setAttribute('aSize', new T.BufferAttribute(ps, 2).setUsage(T.DynamicDrawUsage));\r\n  const pmat = new T.ShaderMaterial({ vertexShader: VERT, fragmentShader: FRAG, uniforms: { uK: { value: 1 } }, transparent: true, depthWrite: false }) // normal alpha blending: no additive white-out, dark scorch/dust possible;\r\n  const points = new T.Points(pg, pmat); points.frustumCulled = false; points.renderOrder = 6; points.visible = false;\r\n  const setP = (i, x, y, z, r, g, b, a, size, soft = 0) => { pp[i * 3] = x; pp[i * 3 + 1] = y; pp[i * 3 + 2] = z; pc[i * 4] = r; pc[i * 4 + 1] = g; pc[i * 4 + 2] = b; pc[i * 4 + 3] = a; ps[i * 2] = size; ps[i * 2 + 1] = soft; };\r\n  const park = (o, n) => { for (let i = o; i < o + n; i++) { ps[i * 2] = 0; pc[i * 4 + 3] = 0; } };\r\n  park(0, PTS);\r\n\r\n  // ---- twister debris\r\n  const igeo = new T.ConeGeometry(.5, 1.5, 4), imat = new T.MeshBasicMaterial({ color: 0xffffff, fog: false });\r\n  const inst = new T.InstancedMesh(igeo, imat, TW_MAX * DEBRIS); inst.frustumCulled = false; inst.visible = false; inst.count = 0; inst.instanceMatrix.setUsage(T.DynamicDrawUsage);\r\n  const dcol = [0x6a2f8f, 0xb050e8, 0x2a1840, 0xd9a0ff, 0x8a5a3a, 0xff7a30], tmpC = new T.Color();\r\n  const dh = [], da = [], dr = [];\r\n  for (let i = 0; i < TW_MAX * DEBRIS; i++) { dh.push(((i % DEBRIS) + rand()) / DEBRIS); da.push(rand() * 6.283); dr.push(rand()); tmpC.set(dcol[(rand() * dcol.length) | 0]); inst.setColorAt(i, tmpC); }\r\n  inst.instanceColor.needsUpdate = true;\r\n  root.add(lines, points, inst, boltMesh);\r\n  const dummy = new T.Object3D(), twisters = [];\r\n\r\n  // ---- sky / fog tint (restored on dispose)\r\n  const scene = world.parent, sky = scene?.getObjectByName?.('rts-sky') || null, skyMat = sky?.material || null, skyBase = skyMat?.color?.clone?.() || null;\r\n  const fogBase = scene?.fog?.color?.clone?.() || null, bgBase = scene?.background?.isColor ? scene.background.clone() : null;\r\n  const tintOut = [1, 1, 1];\r\n  function applyTint(rgb, flash) {\r\n    tintOut[0] = rgb[0] + flash; tintOut[1] = rgb[1] + flash * .15; tintOut[2] = rgb[2] + flash * .2;\r\n    if (skyMat && skyBase) skyMat.color.setRGB(skyBase.r * tintOut[0], skyBase.g * tintOut[1], skyBase.b * tintOut[2]);\r\n    if (fogBase) scene.fog.color.setRGB(fogBase.r * tintOut[0], fogBase.g * tintOut[1], fogBase.b * tintOut[2]);\r\n    if (bgBase) scene.background.setRGB(bgBase.r * tintOut[0], bgBase.g * tintOut[1], bgBase.b * tintOut[2]);\r\n  }\r\n\r\n  // ---- state\r\n  const cur = { kind: 'clear', k: 0 }; let time = 0, boltAt = 1, dead = false, lastKind = 'clear';\r\n  const bolts = Array.from({ length: BOLTS }, () => ({ age: 99, life: 0, segs: [] }));\r\n  const fire = Array.from({ length: FIRE_SLOTS }, () => ({ ph: 3, t: rand() * 3, x: 0, z: 0, r: 3, burn: 5 }));\r\n  const spl = Array.from({ length: SPL_SLOTS }, () => ({ age: 9, x: 0, z: 0 }));\r\n  const snow = { init: false, x: new Float32Array(SNOW_N).fill(NaN), z: new Float32Array(SNOW_N), y: new Float32Array(SNOW_N), s: new Float32Array(SNOW_N) };\r\n  const wisp = { init: false, x: new Float32Array(WISP_N).fill(NaN), z: new Float32Array(WISP_N), y: new Float32Array(WISP_N), s: new Float32Array(WISP_N) };\r\n\r\n  // micro view on a phone is a narrow slice: aim spawns of the few big effects (twisters, fire, bolts) at the far/near side of the camera axis so they are seen in the fog.\r\n  const aim = (zoom, pad, near = false, far = false) => {\r\n    const [cx, cz] = ctr(), cp = camLocal();\r\n    let [dx, dz] = samplePoint(rand() ** 2, rand(), zoom, pad);\r\n    if (!cp) return [dx, dz];\r\n    if (lodRadii(zoom).z01 < .5) return [dx * (near ? .3 : .55), dz * (near ? .3 : .55)]; // macro: keep the show over the part of the city on screen\r\n    const r = Math.hypot(dx, dz), a = Math.atan2(cp.z - cz, cp.x - cx) + (far || rand() < .7 ? Math.PI : 0) + (rand() - .5) * .7;\r\n    return [Math.cos(a) * r, Math.sin(a) * r];\r\n  };\r\n  function makeBolt(b, zoom, sz, near = false) {\r\n    const [x0, z0] = aim(zoom, 2 * sz, near, true), H = 60 * Math.pow(sz, .8), segs = [], nodes = [], N = 13;\r\n    let x = x0, z = z0;\r\n    for (let i = 0; i <= N; i++) { nodes.push([x, H * (1 - i / N), z]); x += (rand() - .5) * H * .16; z += (rand() - .5) * H * .16; }\r\n    for (let i = 0; i < N; i++) segs.push(nodes[i], nodes[i + 1]);\r\n    for (let f = 0; f < 2; f++) { // forks\r\n      let [fx, fy, fz] = nodes[2 + ((rand() * (N - 5)) | 0)]; const ang = rand() * 6.283;\r\n      for (let i = 0; i < 4; i++) { const nx = fx + Math.cos(ang) * H * .07 + (rand() - .5) * H * .05, nz = fz + Math.sin(ang) * H * .07 + (rand() - .5) * H * .05, ny = Math.max(0, fy - H * .09); segs.push([fx, fy, fz], [nx, ny, nz]); fx = nx; fy = ny; fz = nz; }\r\n    }\r\n    b.segs = segs; b.age = 0; b.life = reduced ? .9 : .42;\r\n  }\r\n  // hit, drop, restrike, decay. Reduced motion: one slow steady fade (no strobing; the sky flash is also off).\r\n  const envelope = (age, life) => reduced ? Math.sin(clamp(age / life, 0, 1) * Math.PI) * .6 : age < .06 ? 1 : age < .1 ? .12 : age < .2 ? 1.25 : Math.max(0, 1 - (age - .2) / (life - .2));\r\n\r\n  function spawnSplash(x, z) { const s = spl.find(q => q.age >= .42); if (s) { s.x = x; s.z = z; s.age = 0; } }\r\n\r\n  // Bolt ribbons. Returns the peak envelope (drives the sky flash). Core ~6 px, halo ~20 px, forks thinner; lands with a red ground flash (splash pool points).\r\n  function writeBolts(cx, cz, sz, dt, storm) {\r\n    let peak = 0; const cp = camLocal(), tanH = Math.tan(((camera?.fov ?? 60) * Math.PI) / 360), P = new T.Vector3(), D = new T.Vector3(), V = new T.Vector3(), N = new T.Vector3();\r\n    for (let bi = 0; bi < BOLTS; bi++) {\r\n      const b = bolts[bi]; let e = 0;\r\n      if (b.age < b.life) { b.age += dt; e = Math.max(0, envelope(b.age, b.life)); peak = Math.max(peak, e); }\r\n      const n = e > .01 ? Math.min(b.segs.length / 2, BOLT_QUADS) : 0, gi = O_SPL + bi;\r\n      for (let sI = 0; sI < BOLT_QUADS; sI++) for (let pass = 0; pass < 2; pass++) {\r\n        const q = ((bi * BOLT_QUADS + sI) * 2 + pass) * 12;\r\n        if (sI >= n) { for (let t = 0; t < 12; t++) { bp[q + t] = 0; bcol[q + t] = 0; } continue; }\r\n        const a = b.segs[sI * 2], c = b.segs[sI * 2 + 1], fork = sI >= MAIN_SEGS, ax = cx + a[0], az = cz + a[2], cxx = cx + c[0], czz = cz + c[2];\r\n        D.set(cxx - ax, c[1] - a[1], czz - az); P.set((ax + cxx) / 2, (a[1] + c[1]) / 2, (az + czz) / 2);\r\n        V.set(cp ? cp.x - P.x : 0, cp ? cp.y - P.y : 1, cp ? cp.z - P.z : 0); const dist = Math.max(1, V.length());\r\n        N.crossVectors(D, V); if (N.lengthSq() < 1e-9) N.set(1, 0, 0); N.normalize();\r\n        const px = (2 * dist * tanH) / viewH, w = (pass ? 6 : 20) * px * (fork ? .55 : 1) * .5, m = e * (pass ? 1 : .45);\r\n        const v = [[ax - N.x * w, a[1] - N.y * w, az - N.z * w], [ax + N.x * w, a[1] + N.y * w, az + N.z * w], [cxx - N.x * w, c[1] - N.y * w, czz - N.z * w], [cxx + N.x * w, c[1] + N.y * w, czz + N.z * w]];\r\n        const col = pass ? [1, .55, .6] : [1, .06, .1];\r\n        for (let t = 0; t < 4; t++) { bp.set(v[t], q + t * 3); bcol[q + t * 3] = col[0] * m; bcol[q + t * 3 + 1] = col[1] * m; bcol[q + t * 3 + 2] = col[2] * m; }\r\n      }\r\n      if (storm && e > .01) { const l = b.segs[MAIN_SEGS * 2 - 1]; setP(gi, cx + l[0], .3 * sz, cz + l[2], 1, .08, .1, clamp(e * .55, 0, .8), 22 * sz, 1); } else if (ps[gi * 2] > 0) ps[gi * 2] = 0;\r\n    }\r\n    bg.attributes.position.needsUpdate = true; bg.attributes.color.needsUpdate = true; boltMesh.visible = peak > 0;\r\n    return peak;\r\n  }\r\n\r\n  function writeLines(kind, k, cx, cz, zoom, sz, dt) {\r\n    const rainOn = kind === 'acid-rain' || kind === 'red-storm', cnt = rainOn ? Math.round(RAIN_N * clamp(k, 0, 1) * (kind === 'red-storm' ? .55 : 1)) : 0;\r\n    const H = 42 * sz, fall = 55 * sz, L = 4 * sz, slant = (kind === 'red-storm' ? .35 : .12) * L, col = kind === 'red-storm' ? [1, .1, .18] : [.35, 1.3, .12];\r\n    for (let i = 0; i < cnt; i++) {\r\n      const fresh = Number.isNaN(rainX[i]);\r\n      if (fresh || rainY[i] < 0 || !allowedAt(rainX[i], rainZ[i], zoom)) {\r\n        if (!fresh && rainY[i] < 0 && kind === 'acid-rain' && allowedAt(rainX[i], rainZ[i], zoom) && rand() < .05) spawnSplash(rainX[i], rainZ[i]);\r\n        const [dx, dz] = samplePoint(rand(), rand(), zoom); rainX[i] = dx; rainZ[i] = dz; rainY[i] = fresh ? rand() : 1;\r\n      }\r\n      rainY[i] -= (fall * rainV[i] * dt) / H;\r\n      const v = i * 2, y = Math.max(0, rainY[i]) * H, x = cx + rainX[i], z = cz + rainZ[i], bb = rainB[i] * (.5 + .5 * k);\r\n      lp[v * 3] = x; lp[v * 3 + 1] = y; lp[v * 3 + 2] = z; lp[v * 3 + 3] = x + slant; lp[v * 3 + 4] = y + L; lp[v * 3 + 5] = z;\r\n      for (let q = 0; q < 2; q++) { const f = bb * (q ? .3 : 1); lc[(v + q) * 3] = col[0] * f; lc[(v + q) * 3 + 1] = col[1] * f; lc[(v + q) * 3 + 2] = col[2] * f; }\r\n    }\r\n    for (let i = cnt; i < lastRain; i++) { const v = i * 2; for (let q = 0; q < 6; q++) lc[v * 3 + q] = 0; }\r\n    if (!rainOn) rainX.fill(NaN);\r\n    lastRain = cnt;\r\n    lg.attributes.position.needsUpdate = true; lg.attributes.color.needsUpdate = true;\r\n    lines.visible = cnt > 0;\r\n  }\r\n\r\n  function writeSplash(cx, cz, sz, dt) {\r\n    for (let i = 0; i < SPL_SLOTS; i++) {\r\n      const s = spl[i], o = O_SPL + i * SPL_PTS;\r\n      if (s.age >= .42) { for (let j = 0; j < SPL_PTS; j++) ps[(o + j) * 2] = 0; continue; }\r\n      s.age += dt; const u = clamp(s.age / .42, 0, 1), x = cx + s.x, z = cz + s.z, fade = 1 - u;\r\n      setP(o, x, .15 * sz, z, .35, 1, .2, .9 * fade, (.5 + 1.6 * u) * sz, 1);\r\n      setP(o + 1, x + .5 * sz * u, (2.6 * u - 2.4 * u * u) * sz * 1.4, z + .2 * sz * u, .6, 1, .35, fade, .32 * sz);\r\n      setP(o + 2, x - .4 * sz * u, (2.1 * u - 2.4 * u * u) * sz * 1.4, z - .3 * sz * u, .6, 1, .35, fade, .28 * sz);\r\n    }\r\n  }\r\n\r\n  function writeFire(cx, cz, zoom, sz, k, on, dt) {\r\n    const want = on ? Math.max(1, Math.round(FIRE_SLOTS * k)) : 0, m = reduced ? .5 : 1;\r\n    for (let i = 0; i < FIRE_SLOTS; i++) {\r\n      const f = fire[i], o = O_FIRE + i * FIRE_PTS; f.t += dt * m;\r\n      if (f.ph === 3) { // idle until a new patch ignites\r\n        if (i < want && f.t > rand() * 2.5 + .3) { const r = (6 + rand() * 5) * sz, [dx, dz] = aim(zoom, r); Object.assign(f, { ph: 0, t: 0, x: dx, z: dz, r, burn: 3.5 + rand() * 6 }); }\r\n        else { park(o, FIRE_PTS); continue; }\r\n      } else if (f.ph === 0 && f.t > 1.3) { f.ph = 1; f.t = 0; }\r\n      else if (f.ph === 1 && (f.t > f.burn || i >= want)) { f.ph = 2; f.t = 0; }\r\n      else if (f.ph === 2 && f.t > 2.6) { f.ph = 3; f.t = 0; park(o, FIRE_PTS); continue; }\r\n      const grow = f.ph === 0 ? f.t / 1.3 : f.ph === 1 ? 1 : 1 - f.t / 2.6, dying = f.ph === 2, X = cx + f.x, Z = cz + f.z;\r\n      for (let j = 0; j < SCORCH; j++) { // dark scorch ring on the ground (stays a little after the flames die)\r\n        const a = j / SCORCH * 6.283 + i, sc = Math.min(1, (f.ph === 0 ? f.t / 1.3 : 1));\r\n        setP(o + j, X + Math.cos(a) * f.r * .85 * sc, .1 * sz, Z + Math.sin(a) * f.r * .85 * sc, .04, .02, .02, (dying ? .75 * grow + .25 : .7) * .8, f.r * .8, 1);\r\n      }\r\n      for (let j = 0; j < FIRE_PTS - SCORCH; j++) {\r\n        const q = SCORCH + j, a = j * 2.4 + i, rr = f.r * (j === 0 ? 0 : .3 + .5 * (((j * 37) % 7) / 7)) * grow, fl = (Math.sin(time * (7 + j * 1.7) * m + i * 3 + j) + 1) / 2;\r\n        const x = X + Math.cos(a) * rr, z = Z + Math.sin(a) * rr, rise = (dying ? .1 : .25 + fl * .6) * f.r * grow;\r\n        if (dying) setP(o + q, x, rise * .3, z, .3 + .15 * fl, .06, .03, .5 * grow, f.r * .4, 1);\r\n        else if (j % 4 === 0) setP(o + q, x, rise * 1.3, z, 1, .7, .15, .75, f.r * (.28 + .1 * fl) * grow, 1); // hot tip\r\n        else setP(o + q, x, rise, z, .95, .22 + .12 * fl, .03, .95, f.r * (.55 + .15 * fl) * (.4 + .6 * grow), 1); // orange/red core\r\n      }\r\n    }\r\n  }\r\n\r\n  function writeSnow(cx, cz, zoom, sz, k, on, dt) {\r\n    const cnt = on ? Math.round(SNOW_N * k) : 0, H = 34 * sz;\r\n    for (let i = 0; i < SNOW_N; i++) {\r\n      if (i >= cnt) { ps[(O_SNOW + i) * 2] = 0; continue; }\r\n      if (Number.isNaN(snow.x[i]) || snow.y[i] < 0 || !allowedAt(snow.x[i], snow.z[i], zoom)) { const fresh = Number.isNaN(snow.x[i]), [dx, dz] = samplePoint(rand(), rand(), zoom); snow.x[i] = dx; snow.z[i] = dz; snow.y[i] = fresh ? rand() : 1; snow.s[i] = .5 + rand(); }\r\n      snow.y[i] -= (3.2 * sz * snow.s[i] * dt) / H;\r\n      const tw = .55 + .45 * Math.sin(time * 5 + i), sway = Math.sin(time * .8 + i) * 1.2 * sz;\r\n      setP(O_SNOW + i, cx + snow.x[i] + sway, Math.max(0, snow.y[i]) * H, cz + snow.z[i], .75, .95 + .05 * tw, 1, .6 + .35 * tw, (.45 + .3 * snow.s[i]) * sz);\r\n    }\r\n    if (!on) snow.x.fill(NaN); snow.init = on;\r\n  }\r\n\r\n  function writeWisps(cx, cz, zoom, sz, k, on, dt, glow = false) {\r\n    const cnt = on ? Math.round(WISP_N * (.5 + .5 * k)) : 0;\r\n    for (let i = 0; i < WISP_N; i++) {\r\n      if (i >= cnt) { ps[(O_WISP + i) * 2] = 0; continue; }\r\n      if (Number.isNaN(wisp.x[i]) || !allowedAt(wisp.x[i], wisp.z[i], zoom)) { const [dx, dz] = samplePoint(rand(), rand(), zoom); wisp.x[i] = dx; wisp.z[i] = dz; wisp.y[i] = (1 + rand() * 5) * sz; wisp.s[i] = .6 + rand() * .8; }\r\n      wisp.x[i] += Math.sin(time * .15 + i) * 1.4 * sz * dt; wisp.z[i] += Math.cos(time * .12 + i * 2) * 1.4 * sz * dt;\r\n      setP(O_WISP + i, cx + wisp.x[i], wisp.y[i], cz + wisp.z[i], .25, .8, .18, (glow ? .3 + .2 * k : .3 + .2 * k) * (.6 + .4 * Math.sin(time * .5 + i)), (glow ? 40 : 26) * sz * wisp.s[i], 1);\r\n    }\r\n    if (!on) wisp.x.fill(NaN); wisp.init = on;\r\n  }\r\n\r\n  function writeTwisters(cx, cz, zoom, sz, k, on, dt) {\r\n    const want = on ? 2 + Math.round(3 * k) : 0, m = reduced ? .5 : 1, Hh = 45 * Math.pow(sz, .7), R = .2 * Hh, macro = lodRadii(zoom).z01 < .5, camP = camLocal();\r\n    while (twisters.length < want) {\r\n      const i = twisters.length; let [dx, dz] = aim(zoom, R * 1.7);\r\n      if (macro) { const a = (i / want) * 6.283 + rand() * .6, r = lodRadii(zoom).outer * (.25 + rand() * .3); dx = Math.cos(a) * r; dz = Math.sin(a) * r; } // spread, each clearly separate\r\n      twisters.push({ x: dx, z: dz, h: rand() * 6.283, sp: (7 + rand() * 7) * Math.sqrt(sz), turn: (rand() - .5) * .8, spin: 2.2 + rand() * 1.5, born: 0 });\r\n    }\r\n    if (twisters.length > want) twisters.length = want;\r\n    for (const t of twisters) {\r\n      t.born = Math.min(1, t.born + dt / 2.5); t.h += (Math.sin(time * .35 + t.spin * 7) * .6 + t.turn * .3) * dt * m; t.x += Math.cos(t.h) * t.sp * dt * m; t.z += Math.sin(t.h) * t.sp * dt * m;\r\n      const [ax, az] = confine(t.x, t.z, zoom, R * 1.7); // hard guarantee: never over the base, never past the city/fog edge\r\n      if (ax !== t.x || az !== t.z) { t.h = Math.atan2(az - t.z, ax - t.x); t.x = ax; t.z = az; }\r\n    }\r\n    const DUST = 36; if (on || twisters.length) park(O_FIRE, TW_MAX * DUST);\r\n    for (let ti = 0; ti < twisters.length; ti++) {\r\n      const t = twisters[ti], gx = cx + t.x, gz = cz + t.z, sway = h => Math.sin(h * 3 + time * 1.3 * m + ti) * R * .3 * h;\r\n      const prof = h => R * (.1 + .9 * Math.pow(h, 1.3)) * t.born; // narrow at the ground, wide at the top\r\n      for (let d = 0; d < DUST; d++) { // dust column: dark soft blobs along the funnel, bigger dark skirt at the base\r\n        const h = d / (DUST - 1), rr = prof(h), a = d * 2.4 + time * t.spin * m, skirt = d < 6;\r\n        setP(O_FIRE + ti * DUST + d, gx + Math.cos(a) * rr * .5 + sway(h), (skirt ? 1.5 : h * Hh * t.born), gz + Math.sin(a) * rr * .5, skirt ? .2 : .3, skirt ? .12 : .14, skirt ? .1 : .42, skirt ? .55 : .35, skirt ? R * (1.6 - d * .1) * t.born : rr * 1.7 + R * .15, 1);\r\n      }\r\n      for (let d = 0; d < DEBRIS; d++) { // small debris spiralling up the column\r\n        const idx = ti * DEBRIS + d, h = dh[idx], rr = prof(h) * (1 + dr[idx] * .5), ang = da[idx] + h * 7 + time * t.spin * m * (1.6 - h * .8) * 3;\r\n        dummy.position.set(gx + Math.cos(ang) * rr + sway(h), h * Hh * t.born + .3, gz + Math.sin(ang) * rr);\r\n        dummy.rotation.set(time * 3 * m + dr[idx] * 6, ang, time * 2.2 * m + dr[idx] * 3);\r\n        const want0 = Hh * .014 * (.6 + dr[idx] * .9), cd = camP ? Math.hypot(camP.x - dummy.position.x, camP.y - dummy.position.y, camP.z - dummy.position.z) : 1e9;\r\n        dummy.scale.setScalar(Math.max(.001, Math.min(want0, cd * .012) * t.born)); dummy.updateMatrix(); inst.setMatrixAt(idx, dummy.matrix);\r\n      }\r\n    }\r\n    inst.count = twisters.length * DEBRIS; inst.visible = inst.count > 0; inst.instanceMatrix.needsUpdate = true;\r\n  }\r\n\r\n  function update(dt) {\r\n    if (dead) return; dt = clamp(+dt || 0, 0, .1); time += dt;\r\n    const target = getState?.() || { kind: 'clear', intensity: 0 }, zoom = getZoom?.() ?? 1, [cx, cz] = ctr(), sz = fxScale(zoom);\r\n    if (target.kind !== cur.kind) { cur.k -= dt / 1.1; if (cur.k <= 0) { cur.k = 0; cur.kind = target.kind; twisters.length = 0; park(0, PTS); } }\r\n    else cur.k += (clamp(target.intensity, 0, 1) - cur.k) * Math.min(1, dt * 1.4);\r\n    const kind = cur.kind, k = cur.k, storm = kind === 'red-storm';\r\n    pmat.uniforms.uK.value = viewH / (2 * Math.tan(((camera?.fov ?? 60) * Math.PI) / 360));\r\n    if (storm && k > .05) { boltAt -= dt; if (boltAt <= 0) { const b = bolts.find(x => x.age >= x.life); if (b) makeBolt(b, zoom, sz); boltAt = (reduced ? 2.2 : .7) + (rand() * 2.4) / (.4 + k); } }\r\n    writeLines(kind, k, cx, cz, zoom, sz, dt);\r\n    const peak = writeBolts(cx, cz, sz, dt, storm);\r\n    const spOn = kind === 'acid-rain', glowOn = spOn, fireOn = kind === 'firelands', snowOn = kind === 'ion-snow', fogOn = kind === 'acid-fog';\r\n    if (spOn) writeSplash(cx, cz, sz, dt); else if (lastKind === 'acid-rain') park(O_SPL, SPL_SLOTS * SPL_PTS);\r\n    if (fireOn) writeFire(cx, cz, zoom, sz, k, fireOn, dt);\r\n    if (snowOn || snow.init) writeSnow(cx, cz, zoom, sz, k, snowOn, dt);\r\n    if (fogOn || glowOn || wisp.init) writeWisps(cx, cz, zoom, sz, k, fogOn || glowOn, dt, glowOn);\r\n    writeTwisters(cx, cz, zoom, sz, k, kind === 'twisters', dt);\r\n    lastKind = kind;\r\n    pg.attributes.position.needsUpdate = pg.attributes.aColor.needsUpdate = pg.attributes.aSize.needsUpdate = true;\r\n    points.visible = spOn || fireOn || snowOn || fogOn || twisters.length > 0 || (storm && peak > 0);\r\n    applyTint(tintFor(kind, k), reduced || !storm ? 0 : peak * .5); // no lightning flash under reduced motion\r\n    root.visible = kind !== 'clear' || k > 0;\r\n    // V5 atmosphere: weather fog density into the scene's fog law, wind into streaks and motes; both scale with the zoom like the rest\r\n    const fo = scene?.userData?.cwFog; if (fo) fo.weather = fogDensityFor(kind, k, scene.fog?.far);\r\n    const ws = reduced ? 0 : streakStrength(kind, k, target.raw?.wind), at = reduced ? 0 : time;\r\n    motes.object.scale.setScalar(sz); streaks.object.scale.setScalar(sz);\r\n    airC.set(cx / sz, 14, cz / sz); motes.update(at, airC); airC.y = 0; streaks.update(at, airC); insects.object.scale.setScalar(sz); insects.update(at, airC); insects.setNight(nightAmount()); insects.setWind(WIND_DIR[0] * (.4 + 2 * ws), WIND_DIR[1] * (.4 + 2 * ws));\r\n    motes.setWind(WIND_DIR[0] * (.4 + 2 * ws), WIND_DIR[1] * (.4 + 2 * ws)); streaks.setWind(WIND_DIR[0], WIND_DIR[1], ws);\r\n    if (camera) { world.localToWorld(airCam.set(cx, 0, cz)); motes.object.material.uniforms.uSize.value = .35 * camera.position.distanceTo(airCam) / sz; }\r\n  }\r\n\r\n  function dispose() {\r\n    if (dead) return; dead = true;\r\n    if (skyMat && skyBase) skyMat.color.copy(skyBase);\r\n    if (fogBase) scene.fog.color.copy(fogBase);\r\n    if (bgBase) scene.background.copy(bgBase);\r\n    const fo = scene?.userData?.cwFog; if (fo) fo.weather = 0;\r\n    air.removeFromParent(); motes.dispose(); streaks.dispose(); insects.dispose();\r\n    root.removeFromParent(); lg.dispose(); lines.material.dispose(); bg.dispose(); boltMesh.material.dispose(); pg.dispose(); pmat.dispose(); igeo.dispose(); imat.dispose(); inst.dispose?.();\r\n  }\r\n  const stats = () => ({ kind: cur.kind, k: cur.k, drawCalls: (lines.visible ? 1 : 0) + (points.visible ? 1 : 0) + (inst.visible ? 1 : 0) + (boltMesh.visible ? 1 : 0), twisters: twisters.length, reduced, atmosCalls: 2 + streaks.stats().calls, fogDensity: fogDensityFor(cur.kind, cur.k, scene?.fog?.far), lod: lodRadii(getZoom?.() ?? 1), twisterPos: twisters.map(t => [t.x, t.z]) });\r\n  const strike = near => { const b = bolts.find(x => x.age >= x.life); if (b) makeBolt(b, getZoom?.() ?? 1, fxScale(getZoom?.() ?? 1), !!near); };\r\n  return { update, dispose, stats, strike, tint: tintOut, root };\r\n}\r\n\r\n// ONE-LINE base3d hook (after `const api={...}` is built, before `return api`):\r\n//   import('./weather-fx.mjs').then(m=>m.attachWeather(api)).catch(()=>{});\r\n// Waits for api.ready, mounts the FX in the base world, follows api.view.zoom, keeps real weather fresh (?weather=<kind> overrides), and detaches on api.dispose.\r\nexport function attachWeather(api, { getLatLng, state, fxOpts = {} } = {}) {\r\n  const ac = new AbortController(); let cur = overrideFrom() || { kind: 'clear', intensity: 0 }, fx = null, off = null, stopW = null, done = false;\r\n  const ll = getLatLng || (async () => {\r\n    if (globalThis.__cwLatLng) return globalThis.__cwLatLng;\r\n    try { const p = await navigator.permissions?.query({ name: 'geolocation' }); if (p?.state !== 'granted') return null; } catch { return null; }\r\n    return new Promise(res => navigator.geolocation.getCurrentPosition(q => res({ lat: q.coords.latitude, lng: q.coords.longitude }), () => res(null), { maximumAge: 6e5, timeout: 8e3 }));\r\n  });\r\n  if (!overrideFrom()) stopW = watchWeather({ getLatLng: ll, onState: s => { cur = s; }, signal: ac.signal });\r\n  api.ready?.then(r => {\r\n    if (done || !r) return;\r\n    fx = mountWeatherFx(r.T, { world: r.world, camera: r.camera, center: () => api.center, getZoom: () => api.view?.zoom ?? 1, getState: () => (state ? state() : cur), ...fxOpts });\r\n    off = api.onFrame((t, dtMs) => fx.update((dtMs || 33) / 1000));\r\n  });\r\n  const stop = () => { if (done) return; done = true; ac.abort(); stopW?.(); off?.(); fx?.dispose(); };\r\n  const d0 = api.dispose; if (d0) api.dispose = () => { stop(); d0(); };\r\n  return { stop, get fx() { return fx; }, get state() { return cur; } };\r\n}\r\n","modules/coach-world-rts/weather.mjs":"// R58 real weather -> ALIEN weather. getWeather({lat,lng}) -> {kind,intensity,raw,source}; alienFor(raw) is the pure table.\r\n// Source: Open-Meteo current conditions (no key). Only lat/lng rounded to 0.1 deg (~11 km) are ever sent. Cached 30 min in localStorage.\r\n// Live feed = our Worker route GET /api/weather (server/world-weather.mjs): U.S. National Weather Service data fetched server-side, so the user's IP never\r\n// reaches a third party and only 0.1 deg coordinates are sent. Non-US/failure -> last known / clear. Open-Meteo (free tier is non-commercial) is OFF unless\r\n// deps.config.provider === 'open-meteo'.\r\nexport const KINDS = Object.freeze(['clear', 'acid-rain', 'red-storm', 'twisters', 'firelands', 'acid-fog', 'ion-snow']);\r\nexport const OPEN_METEO = { base: 'https://api.open-meteo.com/v1/forecast', apikey: '' }, WEATHER_API = '/api/weather', ATTRIBUTION = 'Weather: U.S. National Weather Service';\r\nexport const CACHE_KEY = 'cw-weather-v1', CACHE_MS = 30 * 60 * 1000, REFRESH_MS = 30 * 60 * 1000;\r\nexport const WIND_TWISTER = 9, HOT_C = 32;\r\n\r\nconst clamp = (v, a, b) => Math.min(b, Math.max(a, v));\r\nconst RAIN = { 51: .25, 53: .35, 55: .45, 56: .4, 57: .5, 61: .5, 63: .7, 65: .9, 66: .6, 67: .9, 80: .5, 81: .75, 82: 1 };\r\nconst SNOW = { 71: .4, 73: .65, 75: .9, 77: .35, 85: .6, 86: .9 };\r\nconst FOG = { 45: .6, 48: .8 };\r\nconst STORM = { 95: .75, 96: .9, 99: 1 };\r\n\r\n// Coarse location for any network call: 0.1 deg. Never send raw fixes.\r\nexport const roundCoord = v => Math.round(+v * 10) / 10 + 0; // +0 turns -0 into 0\r\n\r\n// raw = {code (WMO), wind (m/s), temp (degC)}. Priority: thunder > wind > heat > snow > rain > fog > clear.\r\nexport function alienFor(raw = {}) {\r\n  const code = Number.isFinite(+raw.code) ? +raw.code : 0, wind = Number.isFinite(+raw.wind) ? +raw.wind : 0, temp = Number.isFinite(+raw.temp) ? +raw.temp : 15;\r\n  const out = (kind, intensity) => ({ kind, intensity: Math.round(clamp(intensity, 0, 1) * 100) / 100 });\r\n  if (STORM[code]) return out('red-storm', STORM[code]);\r\n  if (wind >= WIND_TWISTER) return out('twisters', .3 + (wind - WIND_TWISTER) / 12 * .7);\r\n  if (temp >= HOT_C) return out('firelands', .35 + (temp - HOT_C) / 10 * .65);\r\n  if (SNOW[code]) return out('ion-snow', SNOW[code]);\r\n  if (RAIN[code]) return out('acid-rain', RAIN[code]);\r\n  if (FOG[code]) return out('acid-fog', FOG[code]);\r\n  return out('clear', 0);\r\n}\r\n\r\n// ?weather=<kind>[&wx=0..1] test override (also accepts a bare kind name). Returns a state or null.\r\nexport function overrideFrom(search = globalThis.location?.search || '') {\r\n  const q = new URLSearchParams(search), k = q.get('weather');\r\n  if (!k || !KINDS.includes(k)) return null;\r\n  const i = q.has('wx') ? +q.get('wx') : .85;\r\n  return { kind: k, intensity: k === 'clear' ? 0 : clamp(Number.isFinite(i) ? i : .85, 0, 1), raw: null, source: 'override' };\r\n}\r\n\r\nconst store = () => { try { return globalThis.localStorage || null; } catch { return null; } };\r\nexport function readCache(now = Date.now(), ls = store()) {\r\n  try { const c = JSON.parse(ls?.getItem(CACHE_KEY) || 'null'); return c && c.state && KINDS.includes(c.state.kind) && Number.isFinite(c.at) ? c : null; } catch { return null; }\r\n}\r\nexport function writeCache(state, key, now = Date.now(), ls = store()) { try { ls?.setItem(CACHE_KEY, JSON.stringify({ at: now, key, state })); } catch { /* quota/private mode */ } }\r\n\r\nconst q2 = ({ lat, lng }) => ({ latitude: String(roundCoord(lat)), longitude: String(roundCoord(lng)) });\r\nexport function buildUrl({ lat, lng }, { base = WEATHER_API } = {}) { const c = q2({ lat, lng }); return `${base}?${new URLSearchParams({ lat: c.latitude, lng: c.longitude })}`; }\r\nexport function buildOpenMeteoUrl(c, { base = OPEN_METEO.base, apikey = OPEN_METEO.apikey } = {}) {\r\n  const p = new URLSearchParams({ ...q2(c), current: 'temperature_2m,weather_code,wind_speed_10m', wind_speed_unit: 'ms', timezone: 'GMT' });\r\n  if (apikey) p.set('apikey', apikey);\r\n  return `${base}?${p}`;\r\n}\r\n\r\n// deps (tests): fetch, now, ls, config. Fresh cache (<30 min, same coarse cell) -> no network. Offline/failed -> last known (any age) or clear.\r\nexport async function getWeather({ lat, lng } = {}, deps = {}) {\r\n  const ov = overrideFrom(deps.search);\r\n  if (ov) return ov;\r\n  const now = (deps.now || Date.now)(), ls = deps.ls === undefined ? store() : deps.ls, fetchFn = deps.fetch || globalThis.fetch?.bind(globalThis);\r\n  const cache = readCache(now, ls), clear = { kind: 'clear', intensity: 0, raw: null, source: 'default' };\r\n  if (!Number.isFinite(+lat) || !Number.isFinite(+lng)) return cache ? { ...cache.state, source: 'cache' } : clear;\r\n  const key = `${roundCoord(lat)},${roundCoord(lng)}`;\r\n  if (cache && cache.key === key && now - cache.at < CACHE_MS && now >= cache.at) return { ...cache.state, source: 'cache' };\r\n  try {\r\n    const om = deps.config?.provider === 'open-meteo';\r\n    const res = await fetchFn(om ? buildOpenMeteoUrl({ lat, lng }, deps.config) : buildUrl({ lat, lng }, deps.config), { cache: 'no-store', credentials: om ? 'omit' : 'same-origin', referrerPolicy: 'no-referrer' });\r\n    if (!res.ok) throw new Error('weather http ' + res.status);\r\n    const body = await res.json(), c = om ? body.current : body;\r\n    if (!c) throw new Error('weather payload');\r\n    const raw = om ? { code: c.weather_code, wind: c.wind_speed_10m, temp: c.temperature_2m } : { code: c.code, wind: c.wind, temp: c.temp }, state = { ...alienFor(raw), raw, source: om ? 'open-meteo' : 'nws' };\r\n    writeCache(state, key, now, ls);\r\n    return state;\r\n  } catch {\r\n    return cache ? { ...cache.state, source: 'stale' } : clear;\r\n  }\r\n}\r\n\r\n// Keeps the state fresh: calls onState now and every 30 min. Returns stop(). getLatLng may return null (no location yet -> clear/last known).\r\nexport function watchWeather({ getLatLng, onState, signal } = {}, deps = {}) {\r\n  let stopped = false, timer = 0;\r\n  const tick = async () => {\r\n    if (stopped) return;\r\n    let p = null; try { p = await getLatLng?.(); } catch { /* no location */ }\r\n    const s = await getWeather(p || {}, deps);\r\n    if (!stopped) onState?.(s);\r\n    if (!stopped) timer = setTimeout(tick, REFRESH_MS);\r\n  };\r\n  const stop = () => { stopped = true; clearTimeout(timer); };\r\n  signal?.addEventListener('abort', stop, { once: true });\r\n  tick();\r\n  return stop;\r\n}\r\n","modules/coach-world-rts/wild-aim-scene.mjs":"// AIM scene for the wild hunt: transparent three.js layer over the rear-camera video. Four-legged coach billboard at a fixed 8-14 m stand-off with a rarity plate/aura,\r\n// the raid laser cannon (cannon-fx fireCannon, mode 'sleep'), then sway -> sleep (Zzz) -> the PLAYER'S OWN ship (ships3d, hangar tier + faction colour) descends with a tractor beam and lifts it.\r\n// createAimScene(T,deps) -> {scene,camera,update(t,pose),fire(t,from?),startCatch(t),skip(t),flee(stage,k),stagger(t),aimAngle(pose),dispose,...}. No renderer in here.\r\nimport {fireCannon} from './cannon-fx.mjs';\r\nimport {createShips3d} from './ships3d.mjs';\r\nimport {dropProgress} from './drop-pod-scene.mjs';\r\nimport {createCatch,PHASES} from './wild-hunt-core.mjs';\r\nimport {tierColor,tierIndex} from './wild-contract.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {createWildForest,hashSeed} from './wild-forest.mjs';\r\n\r\n// R81: COACH_H = the four-legged body itself = 2x the player (ar-scale coachHeightM rare, 3.4 m); COACH_W by the 48x36 cell aspect.\r\n// The pets frames hold a ~28 px tall body with its feet on row 33 of 36, so the padded sprite quad is sized from that, feet on the ground.\r\nexport const STAND_OFF_M=8.5,SHIP_HOVER=6,SHIP_SCALE=1.6,COACH_H=3.4,COACH_W=COACH_H*48/36,PET_BODY_PX=28,PET_FEET_PX=33;\r\n// a 90 m square of 'park' land around the player: the alien forest grows here exactly as in the world layer\r\nexport const PARK_PATCH=(r=34)=>({pos:new Float32Array([-r,-r,r,-r,r,r,-r,r]),idx:new Uint32Array([0,1,2,0,2,3]),cls:new Uint8Array([3]),start:new Uint32Array([0,6])});\r\nconst CHECK_FACTION='chest';\r\n\r\n// Coach canvas: one frame of the `pets` sheet (48x36), faction-dyed through the mask, tier-coloured rim glow. Pure canvas ops, injectable.\r\nexport function makeCoachCanvas(doc,{img,mask,frame=[0,0,48,36],rim='#fff',dye=null,rimBlur=12}={}){\r\n const [fx,fy,fw,fh]=frame,pad=14,c=Object.assign(doc.createElement('canvas'),{width:fw+pad*2,height:fh+pad*2}),g=c.getContext('2d');\r\n g.imageSmoothingEnabled=false;\r\n const body=Object.assign(doc.createElement('canvas'),{width:fw,height:fh}),b=body.getContext('2d');b.imageSmoothingEnabled=false;\r\n if(img)b.drawImage(img,fx,fy,fw,fh,0,0,fw,fh);else{b.fillStyle='#8a93a0';b.fillRect(8,10,32,16);}\r\n if(mask&&dye){const m=Object.assign(doc.createElement('canvas'),{width:fw,height:fh}),k=m.getContext('2d');k.drawImage(mask,fx,fy,fw,fh,0,0,fw,fh);k.globalCompositeOperation='source-in';k.fillStyle=dye;k.fillRect(0,0,fw,fh);b.globalAlpha=.6;b.globalCompositeOperation='source-atop';b.drawImage(m,0,0);b.globalAlpha=1;b.globalCompositeOperation='source-over';}\r\n g.shadowColor=rim;g.shadowBlur=rimBlur;g.drawImage(body,pad,pad);g.shadowBlur=0;g.drawImage(body,pad,pad);\r\n return c;\r\n}\r\nconst loadImg=(doc,src)=>new Promise(res=>{const i=new doc.defaultView.Image();i.onload=()=>res(i);i.onerror=()=>res(null);i.src=src;});\r\nexport const loadPets=async doc=>({img:await loadImg(doc,'/pod/gala-coaches/pets.png'),mask:await loadImg(doc,'/pod/gala-coaches/pets-mask.png')});\r\n\r\nfunction glyphCanvas(doc,text,color,size=64){\r\n const c=Object.assign(doc.createElement('canvas'),{width:size,height:size}),g=c.getContext('2d');g.font=`bold ${size*.8}px system-ui,sans-serif`;g.textAlign='center';g.textBaseline='middle';\r\n g.lineWidth=size*.12;g.strokeStyle='#0d0d14';g.strokeText(text,size/2,size/2+2);g.fillStyle=color;g.fillText(text,size/2,size/2+2);return c;\r\n}\r\n\r\nexport function createAimScene(T,{doc=globalThis.document,rules,getBase,tier='white',faction=CHECK_FACTION,facing=0,frame=[0,0,48,36],pets={},reduced=false,now=()=>performance.now(),makeCanvas=null,ships=null,standOff=STAND_OFF_M,hooks={},noForest=false}){\r\n const rarity=rules.rarity,tIdx=tierIndex(rarity,tier),tname=rarity.TIERS[tIdx],col=ms=>tierColor(rarity,tname,ms);\r\n const mk=makeCanvas||((w,h)=>Object.assign(doc.createElement('canvas'),{width:w,height:h}));\r\n const own=[],track=o=>(own.push(o),o);\r\n const scene=new T.Scene(),camera=new T.PerspectiveCamera(70,1,.1,300),root=new T.Group();root.name='wild-aim';scene.add(root);\r\n const sun=new T.DirectionalLight(0xffffff,2);sun.position.set(-3,8,4);scene.add(new T.AmbientLight(0xffffff,1.2),sun); // one light (A15 6)\r\n const fc=FACTION_BY_ID[faction]?.color||'#ffffff';\r\n\r\n // --- small sprite pools: sparks / dust (14), Zzz (3), \"!\" (1), flash ---\r\n const dotCv=mk(32,32),dg=dotCv.getContext('2d'),grad=dg.createRadialGradient?.(16,16,0,16,16,16);if(grad){grad.addColorStop(0,'rgba(255,255,255,1)');grad.addColorStop(.4,'rgba(255,255,255,.5)');grad.addColorStop(1,'rgba(255,255,255,0)');dg.fillStyle=grad;dg.fillRect(0,0,32,32);}\r\n const dotTex=track(new T.CanvasTexture(dotCv));\r\n // --- alien forest around the target (same recipe as the world layer) + floor for when there is no camera passthrough ---\r\n const forest=noForest?null:createWildForest(T,{cell:{x:7,y:7,z:99},landuse:PARK_PATCH(),heightAt:()=>0,scale:1,seed:tIdx*17+3,resolution:[320,180],reduced});\r\n if(forest){forest.group.name='aim-forest';root.add(forest.group);}\r\n const ground=new T.Mesh(track(new T.CircleGeometry(90,24)),track(new T.MeshBasicMaterial({color:0x1d3b2a,toneMapped:false,fog:false})));ground.rotation.x=-Math.PI/2;ground.position.y=-.02;root.add(ground);\r\n\r\n // --- coach billboard ---\r\n const coachTex=track(new T.CanvasTexture(pets.canvas||(pets.make?pets.make(fc,col(0)):makeCoachCanvas(doc,{img:pets.img,mask:pets.mask,frame,rim:col(0),dye:fc}))));\r\n coachTex.magFilter=coachTex.minFilter=T.NearestFilter;coachTex.generateMipmaps=false;\r\n const coachMat=track(new T.SpriteMaterial({map:coachTex,transparent:true,alphaTest:.05,toneMapped:false,fog:false})),coach=new T.Sprite(coachMat);\r\n const cw=coachTex.image?.width||frame[2],ch=coachTex.image?.height||frame[3],mpp=COACH_H/PET_BODY_PX,QW=cw*mpp,QH=ch*mpp;\r\n coach.center.set(.5,Math.max(0,(ch-(ch-frame[3])/2-PET_FEET_PX)/ch));coach.renderOrder=3;coach.position.z=-standOff;coach.scale.set(QW,QH,1);coach.name='wild-coach';root.add(coach);\r\n const aura=track(new T.Sprite(track(new T.SpriteMaterial({map:dotTex,color:new T.Color(col(0)),transparent:true,blending:T.AdditiveBlending,depthWrite:false,opacity:tIdx?.2:0,depthTest:false,toneMapped:false}))));aura.scale.set(COACH_W*1.9,COACH_W*1.5,1);aura.position.y=1;aura.renderOrder=1;root.add(aura);\r\n // rarity plate on the ground: shape by tier (segment count), pips = 1..6 small discs\r\n const SEG={circle:40,triangle:3,square:4,pentagon:5,star:10,diamond:4};\r\n const plateGeo=track(new T.RingGeometry(1.9,2.6,SEG[rarity.SHAPES[tname]]||40));\r\n const plate=new T.Mesh(plateGeo,track(new T.MeshBasicMaterial({color:new T.Color(col(0)),transparent:true,opacity:.9,side:T.DoubleSide,depthWrite:false,toneMapped:false})));\r\n plate.rotation.x=-Math.PI/2;plate.position.y=.05;root.add(plate);\r\n const pipGeo=track(new T.CircleGeometry(.15,10)),pipMat=track(new T.MeshBasicMaterial({color:new T.Color(col(0)),toneMapped:false,side:T.DoubleSide}));\r\n for(let i=0;i<(rarity.PIPS[tname]||1);i++){const p=new T.Mesh(pipGeo,pipMat);p.rotation.x=-Math.PI/2;p.position.set((i-((rarity.PIPS[tname]||1)-1)/2)*.28,.06,1.85);root.add(p);}\r\n let pillar=null;if(tIdx>=4){pillar=new T.Mesh(track(new T.CylinderGeometry(.5,.5,40,12,1,true)),track(new T.MeshBasicMaterial({color:new T.Color(col(0)),transparent:true,opacity:.12,blending:T.AdditiveBlending,depthWrite:false,side:T.DoubleSide,toneMapped:false})));pillar.position.y=20;root.add(pillar);}\r\n\r\n const dots=Array.from({length:14},()=>{const s=new T.Sprite(track(new T.SpriteMaterial({map:dotTex,color:0xffffff,transparent:true,blending:T.AdditiveBlending,depthWrite:false,opacity:0,toneMapped:false})));s.visible=false;s.userData={t0:-1e9,vx:0,vy:0,vz:0,life:600,size:.2};root.add(s);return s;});\r\n let dotHead=0;\r\n const emit=(x,y,z,vx,vy,vz,color,life=600,size=.2,additive=true)=>{const s=dots[dotHead++%dots.length];s.position.set(x,y,z);s.material.color.set(color);s.material.blending=additive?T.AdditiveBlending:T.NormalBlending;Object.assign(s.userData,{t0:now(),vx,vy,vz,life,size});s.visible=true;};\r\n const zzzTex=track(new T.CanvasTexture(glyphCanvas(doc||{createElement:()=>mk(64,64)},'Z','#cfe6ff')));\r\n const zzz=Array.from({length:3},()=>{const s=new T.Sprite(track(new T.SpriteMaterial({map:zzzTex,transparent:true,depthWrite:false,opacity:0,toneMapped:false})));s.visible=false;s.userData={t0:-1e9};root.add(s);return s;});\r\n let zHead=0,lastZ=-1e9;\r\n const bangTex=track(new T.CanvasTexture(glyphCanvas(doc||{createElement:()=>mk(64,64)},'!','#ff4a3a'))),bang=new T.Sprite(track(new T.SpriteMaterial({map:bangTex,transparent:true,depthWrite:false,toneMapped:false})));bang.visible=false;root.add(bang);\r\n\r\n // --- placement: coach straight ahead of the view yaw at entry ---\r\n const place={x:0,z:-standOff,yaw:0};\r\n function setPlacement(yaw=0){place.yaw=yaw;place.x=-Math.sin(yaw)*standOff;place.z=-Math.cos(yaw)*standOff;\r\n  root.position.set(0,0,0);for(const o of [coach,plate,aura])o.position.x=place.x;coach.position.z=aura.position.z=plate.position.z=place.z;\r\n  root.children.filter(c=>c.geometry===pipGeo).forEach((p,i,a)=>p.position.set(place.x+(i-(a.length-1)/2)*.45,.06,place.z+3.1));if(pillar)pillar.position.set(place.x,20,place.z);clearCorridor();}\r\n // trees close to you or standing between you and the target are scaled to nothing so the coach is never hidden (the rest of the forest stays)\r\n const trees=forest?forest.group.children.filter(c=>c.isInstancedMesh&&c.name==='forest-trees'):[],orig=new Map();\r\n function clearCorridor(){\r\n  for(const im of trees){if(!orig.has(im))orig.set(im,im.instanceMatrix.array.slice());const a=im.instanceMatrix.array;a.set(orig.get(im));\r\n   const ex=place.x*1.25,ez=place.z*1.25,l2=ex*ex+ez*ez;\r\n   for(let i=0;i<im.count;i++){const o=i*16,x=a[o+12],z=a[o+14],t=l2?Math.max(0,Math.min(1,(x*ex+z*ez)/l2)):0,d=Math.hypot(x-ex*t,z-ez*t);\r\n    if(d<4.2||Math.hypot(x,z)<5.5)for(const k of [0,1,2,4,5,6,8,9,10])a[o+k]=0;}\r\n   im.instanceMatrix.needsUpdate=true;}\r\n }\r\n setPlacement(0);\r\n const coachHome=()=>({x:place.x,z:place.z});\r\n\r\n // --- ship (the player's own) + beam, driven manually: only the lead pivot is used, update() is never called (that is the figure-8) ---\r\n const fleet=ships||createShips3d(T,{getBase,scale:SHIP_SCALE});fleet.group.visible=false;root.add(fleet.group);\r\n const beam=new T.Mesh(track(new T.CylinderGeometry(.5,2.6,SHIP_HOVER,16,1,true)),track(new T.MeshBasicMaterial({color:new T.Color(col(0)),transparent:true,opacity:0,blending:T.AdditiveBlending,depthWrite:false,side:T.DoubleSide,toneMapped:false})));beam.visible=false;root.add(beam);\r\n const lead=()=>fleet.ships?.[0];\r\n const tint=new T.Color(fc);\r\n void Promise.resolve(fleet.ready).then(()=>{ // real ships3d GLB in faction colour (emissive wash keeps the hull readable)\r\n  for(const sh of fleet.ships||[])sh.pivot.traverse(o=>{if(!o.isMesh||!o.material)return;const ms=(Array.isArray(o.material)?o.material:[o.material]).map(m=>{const c=m.clone();c.color?.lerp(tint,.45);if(c.emissive){c.emissive.copy(tint).multiplyScalar(.35);}own.push(c);return c;});o.material=Array.isArray(o.material)?ms:ms[0];});\r\n  S.shipSprite=!!fleet.ships?.[0]?.sprite;}).catch(()=>{});\r\n const shipTo=(x,y,z,tilt=0)=>{const s=lead();if(!s)return;s.pivot.position.set(x,y,z);s.pivot.rotation.set(0,0,tilt);s.ribbon.mesh.visible=false;fleet.ships.slice(1).forEach(o=>{o.pivot.visible=false;o.ribbon.mesh.visible=false;});};\r\n\r\n // --- state ---\r\n const S={cannon:null,stagger:-1e9,fleeStage:null,fleeK:0,fleeFrom:null,gone:false,flash:-1e9,catching:null,lastPhase:null,phaseHook:hooks.onPhase||(()=>{})};\r\n let disposed=false;\r\n\r\n function fire(t=now(),from=null){\r\n  S.cannon?.dispose();const a=from||{x:camera.position.x-Math.sin(place.yaw)*.8,y:camera.position.z-Math.cos(place.yaw)*.8};\r\n  S.cannon=fireCannon(T,root,{from:{x:a.x,y:a.y},to:{x:place.x,y:place.z},faction,mode:'sleep',radius:1.2,scale:1,origin:{x:0,y:0},flightMs:260,reduced,now});\r\n  return S.cannon;\r\n }\r\n const stagger=(t=now())=>{S.stagger=t;for(let i=0;i<6;i++)emit(place.x,1,place.z,(Math.random()-.5)*3,1+Math.random()*2,(Math.random()-.5)*3,col(0),500,.25);};\r\n function startCatch(t=now(),opts={}){S.catching=createCatch({tierIdx:tIdx,reduced});S.catching.start(t);S.zStart=-1;S.lastPhase=null;return S.catching;}\r\n const skip=t=>S.catching?.skip(t)??false;\r\n const canSkip=t=>S.catching?.canSkip(t)??false;\r\n function flee(stage,k=0){S.fleeStage=stage;S.fleeK=k;}\r\n\r\n // angle between the camera forward and the coach torso (deg): reticle quality\r\n const fwd=new T.Vector3(),to=new T.Vector3();\r\n function aimAngle(){camera.getWorldDirection(fwd);to.set(place.x-camera.position.x,1-camera.position.y,place.z-camera.position.z).normalize();return Math.acos(Math.max(-1,Math.min(1,fwd.dot(to))))*180/Math.PI;}\r\n\r\n function update(t,pose){\r\n  if(disposed)return;\r\n  if(pose){camera.position.set(...pose.position);camera.quaternion.set(...pose.quaternion);}\r\n  camera.updateMatrixWorld(true);forest?.update(t/1000,0,camera);\r\n  const ms=t,c=col(ms);\r\n  if(tIdx===5){aura.material.color.set(c);plate.material.color.set(c);pipMat.color.set(c);beam.material.color.set(c);pillar?.material.color.set(c);} // iridescent cycles the holo hues\r\n  S.cannon&&!S.cannon.update(t)&&(S.cannon=null);\r\n  for(const s of dots){if(!s.visible)continue;const e=t-s.userData.t0;if(e>s.userData.life){s.visible=false;continue;}const k=e/s.userData.life,sec=e/1000;s.position.x+=s.userData.vx*.016;s.position.y+=s.userData.vy*.016;s.position.z+=s.userData.vz*.016;s.material.opacity=1-k;s.scale.setScalar(s.userData.size*(1+k));}\r\n  for(const z of zzz){if(!z.visible)continue;const e=t-z.userData.t0;if(e>1400){z.visible=false;continue;}z.material.opacity=1-e/1400;z.position.set(place.x+.5+Math.sin(e*.004)*.15,1.2+e*.0006,place.z);z.scale.setScalar(.5+e*.0003);}\r\n  // base pose\r\n  coach.position.z=place.z;\r\n  let rot=0,ox=0,oy=0,sc=1,vis=true,alpha=1,spinX=1;\r\n  const stag=t-S.stagger;if(stag<300){ox=-Math.sin(stag/300*Math.PI)*.2;}\r\n  if(S.fleeStage){\r\n   const fs=S.fleeStage,k=S.fleeK;bang.visible=fs==='alert'||fs==='warning';\r\n   if(bang.visible){bang.position.set(place.x,COACH_H+.7,place.z);bang.scale.setScalar(fs==='alert'?.5+.9*Math.sin(Math.min(1,k)*Math.PI*.5)*(1+.25*(1-k)):.9);}\r\n   if(fs==='warning'){ox=Math.sin(t*.06)*.08;aura.material.color.set('#ff4a3a');}\r\n   if(fs==='flee'){const run=k*14;ox=-Math.sin(place.yaw)*run;oy=0;const zz=-Math.cos(place.yaw)*run;coach.position.z=place.z+zz;plate.position.z=place.z; if(t-(S.lastDust||0)>180){S.lastDust=t;emit(place.x+ox,.2,place.z+zz,0,.6,0,'#9a8a73',500,.5,false);}}\r\n   if(fs==='vanish'||fs==='gone'){const run=14,zz=-Math.cos(place.yaw)*run;coach.position.z=place.z+zz;ox=-Math.sin(place.yaw)*run;sc=Math.max(0,1-S.fleeK);if(fs==='vanish'&&S.fleeK<.15){for(let i=0;i<3;i++)emit(place.x+ox,1,place.z+zz,(Math.random()-.5)*4,2,(Math.random()-.5)*4,col(0),500,.3);}vis=fs==='vanish';plate.material.opacity=.9*(1-S.fleeK);}\r\n  }\r\n  // catch sequence\r\n  if(S.catching){\r\n   const st=S.catching.update(t),p=st.phase,k=st.k;if(p!==S.lastPhase){S.lastPhase=p;S.phaseHook(p,st);}\r\n   const cx=place.x,cz=place.z,top=SHIP_HOVER,start={x:cx-5*SHIP_SCALE,y:top+26,z:cz-8*SHIP_SCALE};\r\n   const idx=PHASES.indexOf(p);\r\n   if(idx>=PHASES.indexOf('sway')&&p!=='done'&&p!=='tranq'){aura.material.opacity=(tIdx?.2:0)*(p==='sway'?1-.7*k:.3);}\r\n   if(p==='sway'){rot=Math.sin(k*Math.PI*4)*.14;}\r\n   if(p==='sleep'){const fall=reduced?1:Math.min(1,Math.max(0,(k*1.2-.1)/.5));rot=-1.4*(1-Math.pow(1-fall,2));\r\n    if(!reduced&&t-lastZ>350){lastZ=t;const z=zzz[zHead++%3];z.userData.t0=t;z.visible=true;}\r\n    if(k>.65){plate.material.color.set('#3dff6a');}}\r\n   const asleep=idx>=PHASES.indexOf('sleep');if(asleep)rot=reduced?-1.4:-1.4;\r\n   if(!reduced){\r\n    const s0=lead();fleet.group.visible=s0&&(p==='descend'||p==='beam'||p==='liftoff');\r\n    if(p==='descend'){const e=1-dropProgress(k);shipTo(cx+(start.x-cx)*e,top+(start.y-top)*e,cz+(start.z-cz)*e,0);if(k>.97&&S.landFlash!==true){S.landFlash=true;for(let i=0;i<8;i++)emit(cx,top-1,cz,(Math.random()-.5)*5,.4,(Math.random()-.5)*5,col(0),600,.3);}}\r\n    if(p==='beam'){shipTo(cx,top+Math.sin(t*.004)*.08,cz,0);beam.visible=true;beam.position.set(cx,top/2,cz);beam.material.opacity=.45+.15*Math.sin(t*.02);beam.scale.set(1,1,1);\r\n     oy=k*(top-1.2);sc=1-.4*k;spinX=Math.max(.05,Math.abs(Math.cos(k*14)));\r\n     if(t-(S.lastSpark||0)>70){S.lastSpark=t;const a=t*.01;emit(cx+Math.cos(a)*.9,.3+oy*0.6,cz+Math.sin(a)*.9,0,2.2,0,col(0),500,.18);}}\r\n    if(p==='liftoff'){beam.visible=false;coach.visible=false;plate.material.opacity=.9*(1-k);const e=dropProgress(k);shipTo(cx+(start.x-cx)*e,top+(start.y-top)*e,cz+(start.z-cz)*e,.26);S.liftoffHidden=true;aura.material.opacity=0;}\r\n   }else{\r\n    if(p==='liftoff'){alpha=1-k;plate.material.opacity=.9*(1-k);}\r\n   }\r\n   if(p==='toast'||p==='done'){fleet.group.visible=false;beam.visible=false;coach.visible=false;}\r\n   S.state=st;\r\n  }\r\n  const ph=S.state?.phase;\r\n  coachMat.rotation=rot;coachMat.opacity=alpha;\r\n  coach.scale.set(QW*sc*spinX,QH*sc,1);\r\n  coach.visible=vis&&!(S.catching&&(ph==='toast'||ph==='done'||(ph==='liftoff'&&!reduced)));\r\n  coach.position.set(place.x+ox,oy,coach.position.z);\r\n  aura.position.set(coach.position.x,1+oy,coach.position.z);\r\n }\r\n function dispose(){\r\n  if(disposed)return;disposed=true;S.cannon?.dispose();forest?.dispose();fleet.dispose?.();\r\n  root.traverse(o=>{o.geometry?.dispose?.();});for(const o of own)o.dispose?.();\r\n  root.removeFromParent();\r\n }\r\n return {scene,camera,root,coach,plate,beam,fleet,forest,ground,setPassthrough:on=>{ground.visible=!on;},get shipIsSprite(){return !!S.shipSprite;},update,fire,stagger,startCatch,skip,canSkip,flee,aimAngle,setPlacement,dispose,\r\n  catchState:()=>S.state||null,place,get cannon(){return S.cannon;},tierName:tname,color:col};\r\n}\r\n","modules/coach-world-rts/wild-contract.mjs":"// Wild-coach hunt: shared constants, flag, rules loader (real wild-rarity/wild-spook when present, else stubs per PLAN-WILD), server API client.\r\n// Everything here is pure or takes its deps, so node tests cover it. Feature is OFF unless ?wild=1 (or localStorage cw-wild='1'); ?wild=0 always wins.\r\nexport const wildFlag=(win=globalThis)=>{\r\n try{const q=new URLSearchParams(win.location?.search||'').get('wild');if(q==='0')return false;if(q==='1')return true;return win.localStorage?.getItem('cw-wild')==='1';}catch{return false;}\r\n};\r\nexport const wildLabFlag=(win=globalThis)=>{try{return new URLSearchParams(win.location?.search||'').get('wildlab')==='1';}catch{return false;}};\r\nexport const wildStubFlag=(win=globalThis)=>{try{return new URLSearchParams(win.location?.search||'').get('wildstub')==='1';}catch{return false;}};\r\n\r\n// A14 2.2 range bands (outer to inner). `max` is d_eff metres.\r\nexport const BANDS=Object.freeze([{id:'aware',max:400},{id:'tracking',max:150},{id:'alert',max:60},{id:'stalk',max:25},{id:'range',max:15}]);\r\nexport const SAFETY=Object.freeze({roadM:10,softMps:2.5,lockMps:4,lockSamples:2,lockMs:5*60000,aimStillMps:.4,aimStillMs:1000,aimPitchDeg:25,aimLowerDeg:12,aimLowerMs:1000,aimExitMps:.8,aimExitMs:2000,gpsPauseAcc:40,inRangeAcc:15,closeEnoughD:30,closeEnoughStillMs:2000,inRangeHoldMs:1500,hyst:1.15,dwellMs:4000,quietFrom:22,quietTo:5,startCardMs:2000,strideM:.75,pauseWaitMs:90000});\r\n\r\n// ---- rules: adapter over the landed tables (wild-rarity.mjs, wild-spook.mjs). Tier id for the top tier is 'iridescent'. ----\r\nimport * as R from './wild-rarity.mjs';\r\nimport {stepSpook,createSpook as newSpookState} from './wild-spook.mjs';\r\nconst hex=n=>'#'+n.toString(16).padStart(6,'0');\r\nexport const RARITY=Object.freeze({\r\n TIERS:R.TIERS,PALETTE:R.PALETTE,PIPS:R.PIPS,SHAPES:R.SHAPE,SEDATION:R.SEDATION_PER_HIT,DARTS:R.DARTS,GAIN_MULT:R.SPOOK_MULT,\r\n ODDS:Object.fromEntries(R.TIERS.map(t=>[t,[R.CATCH_ODDS[t],Math.min(1,R.CATCH_ODDS[t]+.07)]])), // [normal, clean-approach]\r\n HOLO:R.HOLO_HUES.map(hex)\r\n});\r\nexport const tierName=(r,t)=>typeof t==='number'?r.TIERS[Math.max(0,Math.min(r.TIERS.length-1,t))]:(r.TIERS.includes(t)?t:'white');\r\nexport const tierIndex=(r,t)=>Math.max(0,r.TIERS.indexOf(tierName(r,t)));\r\n// iridescent cycles the holo hues; every other tier is a fixed hex\r\nexport const tierColor=(r,t,ms=0)=>{const n=tierName(r,t),c=r.PALETTE[n];return c?c:r.HOLO[Math.floor(ms/180)%r.HOLO.length];};\r\nconst ARC_DEG={front:30,side:90,rear:150};\r\n// Hunt-side spook: createSpook(tier,{napping}) -> {state,step(sample,dtMs)->{value,event:null|'fidget'|'warn'|'flee'}}. sample.stomp is a count; peakG is only passed on a real stomp.\r\nexport function createSpook(tier='white',{napping=false}={}){\r\n const h={state:newSpookState(),value:0,tier:tierName(RARITY,tier)};\r\n h.step=(s,dtMs=100)=>{\r\n  if(s.paused)return {value:h.value,event:null};\r\n  const r=stepSpook(h.state,{dtMs,distanceM:s.dEff??0,accuracyM:s.acc??5,speedMps:s.speed||0,cadenceHz:s.cadenceHz||0,peakG:s.stomp?2:0,gyroRms:s.shakeRms||0,approachDeg:ARC_DEG[s.bearingArc]??90,tier:h.tier,napping,roadDistM:s.roadDist});\r\n  h.state=r.state;h.value=r.meter;\r\n  return {value:r.meter,event:r.event==='bolt'?'warn':r.event};\r\n };\r\n return h;\r\n}\r\nexport const loadRules=async()=>({rarity:RARITY,createSpook});\r\n\r\n// ---- server API (WS-B contract). Real: GET /api/world/wild/nearby, POST envelope type wild-start/wild-shot/wild-end. Stub: local simulation, only when ?wildstub=1 ----\r\nexport const distBearing=(lat1,lng1,lat2,lng2)=>{const k=Math.PI/180,dy=(lat2-lat1)*111320,dx=(lng2-lng1)*111320*Math.cos(lat1*k);return {d:Math.hypot(dx,dy),bearing:(Math.atan2(dx,dy)/k+360)%360};};\r\nexport function createWildApi(client,{fetchImpl=globalThis.fetch?.bind(globalThis),stub=false,rarity=RARITY,rand=Math.random,now=()=>Date.now(),getContext=null}={}){\r\n const fakes=new Map();\r\n // Server wire (server/world-wild.mjs): every wild-* command needs the live walk session ({sessionId,nonce}) and tzOffset; wild-shot also sends the fixes since the last shot as `track`.\r\n // getContext() -> {sessionId,nonce,track?}. The envelope returns a snapshot; the wild payload is its `.result`.\r\n const post=async action=>{\r\n  const ctx=getContext?.()||{},full={...action,tzOffset:new Date().getTimezoneOffset(),sessionId:ctx.sessionId,nonce:ctx.nonce,...(action.type==='wild-shot'&&ctx.track?{track:ctx.track}:{})};\r\n  const un=r=>r&&r.result?r.result:r;\r\n  if(typeof client?.wildCommand==='function')return un(await client.wildCommand(full));\r\n  if(typeof client?.command==='function')return un(await client.command(full));\r\n  throw Object.assign(Error('wild api not wired'),{code:'wild_unwired'});\r\n };\r\n return {\r\n  stub,\r\n  async nearby({latitude,longitude}){\r\n   if(stub)return {spawns:[...fakes.values()]};\r\n   if(typeof client?.wildNearby==='function')return client.wildNearby({latitude,longitude});\r\n   if(!fetchImpl)return {spawns:[]};\r\n   const res=await fetchImpl(`/api/world/wild/nearby?latitude=${encodeURIComponent(latitude)}&longitude=${encodeURIComponent(longitude)}`,{credentials:'include'});\r\n   if(res.status===404)return {spawns:[],off:true};\r\n   if(!res.ok)throw Object.assign(Error('wild nearby '+res.status),{status:res.status});\r\n   return res.json();\r\n  },\r\n  seed(spawns){for(const s of spawns)fakes.set(s.spawnId,s);}, // stub only\r\n  async start(spawnId){\r\n   if(!stub)return post({type:'wild-start',spawnId});\r\n   const s=fakes.get(spawnId);if(!s)throw Object.assign(Error('gone'),{code:'wild_dead'});\r\n   return {encounterId:'stub-'+spawnId,spawn:{...s,exact:true},expiresAt:now()+360000,sedation:0,darts:tierName(rarity,s.tier)==='iridescent'?rarity.DARTS.iridescent:rarity.DARTS.default};\r\n  },\r\n  async shot(encounterId,{quality,aimRms,tier,sedation=0,dartsLeft=5}){\r\n   if(!stub)return post({type:'wild-shot',encounterId,quality,aimRms});\r\n   const name=tierName(rarity,tier),base=rarity.SEDATION[name]??50,add=quality==='perfect'?base*1.5:quality==='hit'?base:0,total=Math.min(100,sedation+add);\r\n   if(quality==='miss')return {outcome:dartsLeft<=1?'fled':'continue',sedation:total,spookBump:15,dartsLeft:dartsLeft-1};\r\n   if(total<100)return {outcome:dartsLeft<=1?'fled':'continue',sedation:total,dartsLeft:dartsLeft-1};\r\n   const o=rarity.ODDS[name]||[.5,.5];\r\n   return rand()<o[0]?{outcome:'caught',sedation:100,catch:{id:encounterId,tier:name}}:{outcome:'fled',sedation:100};\r\n  },\r\n  async end(encounterId,reason='left'){if(stub)return {ok:true};try{return await post({type:'wild-end',encounterId,reason});}catch{return {ok:false};}},\r\n };\r\n}\r\n\r\n// Permissions MUST be requested synchronously inside the user tap (iOS). Returns promises; the camera stream is only a permission probe (stopped at once, AIM re-acquires it), so walking never burns battery on a live camera.\r\nexport function requestSensors(win=globalThis,nav=win.navigator){\r\n const ask=E=>{try{return typeof E?.requestPermission==='function'?Promise.resolve(E.requestPermission()).catch(()=>'denied'):Promise.resolve('granted');}catch{return Promise.resolve('denied');}};\r\n const motion=ask(win.DeviceMotionEvent),orient=ask(win.DeviceOrientationEvent);\r\n let cam;try{cam=Promise.resolve(nav?.mediaDevices?.getUserMedia?.({audio:false,video:{facingMode:{ideal:'environment'}}})).then(s=>{s?.getTracks?.().forEach(t=>t.stop());return !!s;}).catch(()=>false);}catch{cam=Promise.resolve(false);}\r\n return {motion,orient,cam,sensors:Promise.all([motion,orient]).then(r=>r.every(v=>v==='granted'))};\r\n}\r\n","modules/coach-world-rts/wild-flee.mjs":"// Wild coach spooked: alert (\"!\") -> warning (1 s recover chance) -> flee (dash + dust) -> vanish -> \"got away\". A15 4 timings, pure.\r\n// createFlee({now}) -> {start(t), update(t,{speed,dist})->{stage,k,recovered,done,dash}, stage}. Recover only in the warning window: stop (<0.5 m/s) AND back off (>8 m).\r\nexport const FLEE_MS=Object.freeze({alert:400,warning:1000,flee:2800,vanish:3400});\r\nexport const RECOVER=Object.freeze({speed:.5,dist:8});\r\nexport const STAGES=Object.freeze(['alert','warning','flee','vanish','gone']);\r\nexport const REDUCED_MS=Object.freeze({alert:0,warning:0,flee:0,vanish:500});\r\n\r\nexport function createFlee({reduced=false}={}){\r\n const T=reduced?REDUCED_MS:FLEE_MS;let t0=null,stage=null,recovered=false,done=false;\r\n const stageAt=el=>el<T.alert?'alert':el<T.warning?'warning':el<T.flee?'flee':el<T.vanish?'vanish':'gone';\r\n return {\r\n  get stage(){return stage;},get recovered(){return recovered;},get done(){return done;},\r\n  start(t){t0=t;stage=reduced?'vanish':'alert';recovered=false;done=false;},\r\n  update(t,{speed=9,dist=0}={}){\r\n   if(t0==null||done)return {stage,k:1,recovered,done,dash:0};\r\n   const el=t-t0,s=stageAt(el);\r\n   if(s==='warning'&&!reduced&&speed<RECOVER.speed&&dist>RECOVER.dist){recovered=true;done=true;stage=null;return {stage:null,k:0,recovered:true,done:true,dash:0};}\r\n   stage=s;const lo=s==='alert'?0:T[STAGES[STAGES.indexOf(s)-1]],hi=T[s]??T.vanish,k=s==='gone'?1:Math.min(1,Math.max(0,(el-lo)/(hi-lo||1)));\r\n   if(s==='gone')done=true;\r\n   return {stage:s,k,recovered:false,done,dash:s==='flee'?k:s==='vanish'||s==='gone'?1:0}; // dash 0..1 = fraction of the run covered\r\n  },\r\n };\r\n}\r\nconst DIRS=['north','north-east','east','south-east','south','south-west','west','north-west'];\r\nexport const compassWord=deg=>DIRS[Math.round((((deg%360)+360)%360)/45)%8];\r\nexport const gotAwayText=(bearingDeg,metres)=>`It got away ${compassWord(bearingDeg)}, ${Math.round(metres/10)*10} m`;\r\n","modules/coach-world-rts/wild-forest.mjs":"// WS-K alien forest: parks / natural land are neutral and always look like this (same recipe in every faction city).\r\n// createWildForest(T,{cell,landuse,heightAt,scale,seed,resolution,reduced}) -> {group,update(t,d,cam),step(yaw,player,nowMs),stats,dispose}.\r\n//  landuse = decoded cell landuse {pos(x,z pairs),idx,cls,start}; only classes park/grass grow forest. Frame: metres about the home origin (like every world-layer part).\r\n//  5 draw calls per cell: 2 InstancedMesh tree archetypes (spiral-trunk tree w/ pulsing pods, giant mushroom; up to 420), 1 static floor+vines mesh (linking vines, tentacles, hanging strands, glowing moss, fungus, grass, rocks),\r\n//  1 InstancedMesh of <=60 critter billboards + 1 InstancedMesh of dust/leaf puffs. Critters flee the view direction (yaw cone +-35 deg), hide behind the nearest tree/rock, peek back out 3-6 s after you look away;\r\n//  entering a park puts some in front of you and the ring around you stays stocked just outside your view, so turning always reveals critters that then bolt.\r\nimport {shadeSprite} from './sprite-shade.mjs';\r\nimport {createPs1Material} from './ps1-material.mjs';\r\n\r\nexport const MAX_TREES=420,MAX_ROCKS=24,MAX_VINES=200,MAX_CRITTERS=60,TREE_AREA=22,CONE=35*Math.PI/180,RANGE=18,HIDE_MS=400,SHY_RANGE=14,MIN_AREA=300;\r\n// LOD (perf): full detail only within NEAR_M of the camera target while the map camera is closer than MACRO_M (metres); ground cover only within COVER_M;\r\n// everything else is ONE instanced canopy-blob mesh (<= CANOPY_MAX blobs x 20 tris = 2k tris per cell). Total forest tris visible <= FOREST_CAP at any zoom.\r\nexport const NEAR_M=150,COVER_M=60,MACRO_M=220,FOREST_CAP=60000,CANOPY_MAX=111,BLOB_TRIS=18,COVER_BIN=64;\r\nconst PARK=['park','grass'],TAU=Math.PI*2;\r\nconst LAND=['residential','commercial','industrial','park','grass','other']; // = world-tiles.worker LAND_CLASSES\r\nconst rng=seed=>{let s=seed>>>0||1;return()=>{s=(s+0x6D2B79F5)>>>0;let t=s;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};};\r\nexport const hashSeed=(cell,seed=0)=>{let h=2166136261^seed;for(const ch of `${cell?.z}/${cell?.x}/${cell?.y}`){h^=ch.charCodeAt(0);h=Math.imul(h,16777619);}return h>>>0;};\r\n\r\n// ---------- pure critter logic (view yaw convention = pose-source yawOf: 0 faces -Z, positive turns left) ----------\r\nexport const wrapPi=a=>{a%=TAU;return a>Math.PI?a-TAU:a<-Math.PI?a+TAU:a;};\r\nexport const bearingOf=(player,x,z)=>Math.atan2(-(x-player.x),-(z-player.z));\r\nexport const inCone=(player,x,z,viewYaw,cone=CONE,range=RANGE)=>{const dx=x-player.x,dz=z-player.z;return dx*dx+dz*dz<=range*range&&Math.abs(wrapPi(bearingOf(player,x,z)-viewYaw))<=cone;};\r\n// where a critter at (x,z) runs to: the far side of the nearest obstacle (from the player), else straight away from the player\r\nexport function pickHide(c,player,obstacles,maxDist=SHY_RANGE){\r\n let best=null,bd=maxDist*maxDist,pd=Math.hypot(c.x-player.x,c.z-player.z);\r\n for(const o of obstacles){const d=(o.x-c.x)**2+(o.z-c.z)**2;if(d>=bd||Math.hypot(o.x-player.x,o.z-player.z)<pd-1)continue;best=o;bd=d;} // must sit between-or-beyond: never run towards you\r\n if(best){const dx=best.x-player.x,dz=best.z-player.z,l=Math.hypot(dx,dz)||1,k=best.r+.5;return {x:best.x+dx/l*k,z:best.z+dz/l*k,obstacle:best};}\r\n const dx=c.x-player.x,dz=c.z-player.z,l=Math.hypot(dx,dz)||1;return {x:c.x+dx/l*8,z:c.z+dz/l*8,obstacle:null};\r\n}\r\nexport function critterAt(c,now,out={x:0,z:0,k:1}){const k=Math.min(1,Math.max(0,(now-c.t0)/c.dur)),e=k*k*(3-2*k);out.x=c.fx+(c.tx-c.fx)*e;out.z=c.fz+(c.tz-c.fz)*e;out.k=k;return out;}\r\nconst moveTo=(c,x,z,now,dur)=>{const p=critterAt(c,now,P);c.fx=p.x;c.fz=p.z;c.tx=x;c.tz=z;c.t0=now;c.dur=dur;};\r\nconst P={x:0,z:0,k:1};\r\n// one decision pass for one critter (10 Hz): out+seen -> hide; hidden+unseen for `wake` -> home\r\nexport function decide(c,player,viewYaw,obstacles,now,rand=Math.random){\r\n const p=critterAt(c,now,P),x=p.x,z=p.z;\r\n if(c.state===0){\r\n  if(inCone(player,x,z,viewYaw)){c.x=x;c.z=z;const h=pickHide(c,player,obstacles);moveTo(c,h.x,h.z,now,HIDE_MS);c.state=1;c.wake=now+HIDE_MS+3000+rand()*3000;return 1;}\r\n }else if(inCone(player,c.tx,c.tz,viewYaw)){c.wake=Math.max(c.wake,now+3000+rand()*3000);} // still being looked at: keep hiding\r\n else if(now>=c.wake&&p.k>=1){moveTo(c,c.hx,c.hz,now,HIDE_MS*1.5);c.state=0;return 2;}\r\n return 0;\r\n}\r\n\r\n// ---------- geometry (non-indexed soups; flat shading = PS1) ----------\r\nclass Soup{constructor(){this.p=[];this.c=[];}\r\n tri(a,b,d,col){this.p.push(a[0],a[1],a[2],b[0],b[1],b[2],d[0],d[1],d[2]);for(let i=0;i<3;i++)this.c.push(col[0],col[1],col[2]);}\r\n quad(a,b,c,d,col){this.tri(a,b,c,col);this.tri(a,c,d,col);}\r\n geo(T){const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(this.p,3));g.setAttribute('color',new T.Float32BufferAttribute(this.c,3));g.computeVertexNormals();g.computeBoundingSphere();return g;}\r\n}\r\nconst mix=(a,b,t)=>[a[0]+(b[0]-a[0])*t,a[1]+(b[1]-a[1])*t,a[2]+(b[2]-a[2])*t];\r\nconst PAL={low:[.08,.05,.14],trunk:[[.3,.12,.5],[.1,.4,.45],[.55,.15,.3]],canopy:[[1,.25,.75],[.2,.95,.9],[.7,1,.2],[.6,.35,1]],pod:[[1,1,.35],[.45,1,.7],[1,.55,.95]],vine:[[.2,.9,.5],[.75,.3,1],[.1,.8,.95]],rock:[[.3,.28,.4],[.22,.32,.36]],moss:[[.1,.95,.8],[.9,.2,.9],[.55,1,.15],[.2,.55,1]],grass:[[.35,1,.4],[.2,.85,.9],[.8,.95,.3]],cap:[[1,.3,.6],[.3,.8,1],[1,.75,.2],[.65,.35,1]]};\r\nconst pick=(r,a)=>a[Math.floor(r()*a.length)];\r\nconst ICO=(T)=>new T.IcosahedronGeometry(1,0).attributes.position.array;\r\nfunction blob(soup,ico,cx,cy,cz,rx,ry,rz,col,r){\r\n for(let i=0;i<ico.length;i+=9){const v=[];for(let k=0;k<3;k++){const j=i+k*3,w=1+(r()-.5)*.35;v.push([cx+ico[j]*rx*w,cy+ico[j+1]*ry*w,cz+ico[j+2]*rz*w]);}\r\n  soup.tri(v[0],v[1],v[2],mix(col,[1,1,1],.12*(v[0][1]-cy)/ry+.05));}\r\n}\r\n// stacked rings: radius profile, twist rad/m, wobbly centre. returns top centre + ring list\r\nfunction stem(soup,x,y,z,h,rad,prof,twist,bend,ph,tc,sides,r){\r\n const segs=prof.length,ring=i=>{const t=i/(segs-1),cx=x+Math.sin(t*3.2+ph)*bend*t*h*.35,cz=z+Math.cos(t*2.4+ph)*bend*t*h*.35,rr=rad*prof[i],ys=y+t*h,pts=[];\r\n  for(let s=0;s<sides;s++){const a=s/sides*TAU+twist*t*h;pts.push([cx+Math.cos(a)*rr,ys,cz+Math.sin(a)*rr]);}return {pts,c:[cx,ys,cz],t};};\r\n let prev=ring(0);\r\n for(let i=1;i<segs;i++){const cur=ring(i),col=mix(PAL.low,tc,.35+.65*cur.t);for(let s=0;s<sides;s++){const n=(s+1)%sides;soup.quad(prev.pts[s],prev.pts[n],cur.pts[n],cur.pts[s],i%2?col:mix(col,[1,1,1],.12));}prev=cur;}\r\n return prev.c;\r\n}\r\n// archetype 1: spiral trunk (strong corkscrew), lumpy canopy, pulsing pods hanging underneath. unit height ~6.\r\nfunction spiralTree(T,r){\r\n const soup=new Soup(),ico=ICO(T),tc=pick(r,PAL.trunk),top=stem(soup,0,0,0,6,.45,[1.3,.9,.7,.55,.5,.3,.12],2.6,1.1,r()*TAU,tc,5,r),cc=pick(r,PAL.canopy),pods=[];\r\n for(let b=0;b<3;b++){const rr=1.2+r()*1.3,ox=(r()-.5)*2.6,oz=(r()-.5)*2.6,oy=(r()-.2)*1.3;blob(soup,ico,top[0]+ox,top[1]+oy,top[2]+oz,rr,rr*(.5+r()*.5),rr,mix(cc,pick(r,PAL.canopy),r()*.45),r);pods.push([top[0]+ox,top[1]+oy-rr*.4,top[2]+oz,rr]);}\r\n const pc=pick(r,PAL.pod);for(let p=0;p<5;p++){const b=pods[p%3],a=r()*TAU,d=b[3]*.7;blob(soup,ico,b[0]+Math.cos(a)*d,b[1]-.4-r()*1,b[2]+Math.sin(a)*d,.26,.36,.26,pc,r);}\r\n soup.canopy=cc;soup.trunk=tc;return soup;\r\n}\r\n// archetype 2: giant mushroom: curved stem, big dome cap with glowing spots and gill underside. unit height ~4.5.\r\nfunction mushroom(T,r){\r\n const soup=new Soup(),top=stem(soup,0,0,0,3.6,.5,[1.2,.8,.6,.55,.6],1.2,.8,r()*TAU,[.85,.8,.9],6,r),capc=pick(r,PAL.cap),R=2.5,rings=3,sl=9;\r\n const pt=(i,k)=>{const el=i/rings*Math.PI/2,a=k/sl*TAU;return [top[0]+Math.cos(a)*Math.cos(el)*R,top[1]+Math.sin(el)*R*.55,top[2]+Math.sin(a)*Math.cos(el)*R];};\r\n for(let i=0;i<rings;i++)for(let k=0;k<sl;k++){const n=(k+1)%sl,c=mix(capc,[1,1,1],i*.12+((k+i)%2?.06:0));\r\n  if(i===rings-1)soup.tri(pt(i,k),pt(i,n),[top[0],top[1]+R*.55,top[2]],c);else soup.quad(pt(i,k),pt(i,n),pt(i+1,n),pt(i+1,k),c);\r\n  if(i===0)soup.tri(pt(0,n),pt(0,k),[top[0],top[1]-.1,top[2]],[.25,.12,.3]);}\r\n const sc=[1,1,.6];for(let p=0;p<5;p++){const a=r()*TAU,el=.4+r()*.8;blob(soup,ICO(T),top[0]+Math.cos(a)*Math.cos(el)*R*.9,top[1]+Math.sin(el)*R*.55+.1,top[2]+Math.sin(a)*Math.cos(el)*R*.9,.28,.2,.28,sc,r);}\r\n soup.canopy=capc;soup.trunk=[.85,.8,.9];return soup;\r\n}\r\nfunction rock(soup,ico,x,y,z,r){const s=.6+r()*1.2;blob(soup,ico,x,y+s*.3,z,s,s*.7,s*.9,pick(r,PAL.rock),r);return {x,z,r:s*.9};}\r\nfunction ribbon(soup,a,b,sag,w,col,segs=7){\r\n const dx=b[0]-a[0],dz=b[2]-a[2],l=Math.hypot(dx,dz)||1,nx=-dz/l*w,nz=dx/l*w;let prev=null;\r\n for(let i=0;i<=segs;i++){const t=i/segs,x=a[0]+dx*t,z=a[2]+dz*t,y=a[1]+(b[1]-a[1])*t-Math.sin(t*Math.PI)*sag,cur=[[x-nx,y,z-nz],[x+nx,y,z+nz]];\r\n  if(prev)soup.quad(prev[0],prev[1],cur[1],cur[0],mix(col,[1,1,1],t*.2));prev=cur;}\r\n}\r\n// tentacle: curling ribbon that rises from the ground and spirals\r\nfunction tentacle(soup,x,y,z,h,col,r){\r\n const segs=9,w0=.22,ph=r()*TAU,tw=(r()<.5?-1:1)*(.7+r()*.8),rad=.5+r()*.6;let prev=null;\r\n for(let i=0;i<=segs;i++){const t=i/segs,a=ph+tw*t*TAU*.6,cx=x+Math.cos(a)*rad*t,cz=z+Math.sin(a)*rad*t,cy=y+h*Math.sin(t*Math.PI*.55)*(1-.25*t),w=w0*(1-t*.8),nx=-Math.sin(a)*w,nz=Math.cos(a)*w,cur=[[cx-nx,cy,cz-nz],[cx+nx,cy,cz+nz]];\r\n  if(prev)soup.quad(prev[0],prev[1],cur[1],cur[0],mix(col,[1,1,1],t*.5));prev=cur;}\r\n}\r\n\r\n// mid-range tree (~28 tris): tapered 4-sided trunk + one canopy blob, same colours as the full archetype\r\nfunction liteTree(T,def,r){\r\n const soup=new Soup(),h=def.h;stem(soup,0,0,0,h*.85,def.rad>.7?.5:.45,[1.2,.5],0,0,0,def.soup.trunk,4,r);\r\n blob(soup,ICO(T),0,h*.85,0,h>5?2.2:2.5,h>5?1.6:1.4,h>5?2.2:2.5,def.soup.canopy,r);return soup;\r\n}\r\n// canopy blob (18 tris): dark 6-sided trunk/shadow skirt flaring from the ground to the dome rim, lumpy squashed dome above. Unit radius, ~1.5 tall, base at y=0.\r\nfunction domeSoup(r){\r\n const soup=new Soup(),N=6,ring=(y,rad,j)=>{const p=[];for(let i=0;i<N;i++){const a=i/N*TAU,k=rad*(1+(r()-.5)*j);p.push([Math.cos(a)*k,y,Math.sin(a)*k]);}return p;};\r\n const r0=ring(0,.28,0),r1=ring(.85,1,.3),top=[0,1.5,0],dark=[.22,.16,.3],mid=[.82,.82,.82],hi=[1.05,1.05,1.05];\r\n for(let i=0;i<N;i++){const n=(i+1)%N;soup.quad(r0[i],r0[n],r1[n],r1[i],i%2?dark:mix(dark,mid,.25));soup.tri(r1[i],r1[n],top,i%2?hi:mid);}\r\n return soup;\r\n}\r\nconst rectDist=(x,z,x0,z0,x1,z1)=>Math.hypot(Math.max(x0-x,0,x-x1),Math.max(z0-z,0,z-z1));\r\n// One pass over every forest of the world layer. All start as canopy; the nearest cells within NEAR_M get promoted to full detail while the map camera is near\r\n// (macro=false), each given what is left of FOREST_CAP. Canopies of the farthest cells are dropped if even they would not fit.\r\nexport function planForestLod(forests,tx,tz,macro,cap=FOREST_CAP){\r\n const mode=new Map();let total=0;\r\n for(const f of forests){f.dist=f.bbox?rectDist(tx,tz,...f.bbox):Infinity;total+=f.stats.canopyTris;mode.set(f,'canopy');}\r\n for(const f of [...forests].sort((a,b)=>b.dist-a.dist)){if(total<=cap)break;total-=f.stats.canopyTris;mode.set(f,'off');}\r\n if(!macro)for(const f of forests.filter(f=>f.dist<=NEAR_M&&mode.get(f)==='canopy').sort((a,b)=>a.dist-b.dist)){\r\n  const room=cap-(total-f.stats.canopyTris),used=f.setLod('full',tx,tz,room);if(used>room)break; // the fixed part (floor, cover, critters) alone does not fit: stay canopy\r\n  total+=used-f.stats.canopyTris;mode.set(f,'full');}\r\n for(const [f,m] of mode)if(m!=='full')f.setLod(m);\r\n return total;\r\n}\r\n\r\n// ---------- polygon sampling ----------\r\nfunction parkTris(landuse){\r\n const tris=[],{pos,idx,cls,start}=landuse||{};if(!cls||!cls.length)return {tris,area:0};\r\n for(let i=0;i<cls.length;i++){if(!PARK.includes(LAND[cls[i]]))continue;\r\n  for(let k=start[i];k+2<start[i+1];k+=3){const a=idx[k]*2,b=idx[k+1]*2,c=idx[k+2]*2,ar=Math.abs((pos[b]-pos[a])*(pos[c+1]-pos[a+1])-(pos[c]-pos[a])*(pos[b+1]-pos[a+1]))/2;\r\n   if(ar>.5)tris.push({ax:pos[a],az:pos[a+1],bx:pos[b],bz:pos[b+1],cx:pos[c],cz:pos[c+1],ar});}}\r\n let area=0;for(const t of tris){area+=t.ar;t.cum=area;}\r\n return {tris,area};\r\n}\r\nfunction sample(tp,r,out){\r\n const w=r()*tp.area;let lo=0,hi=tp.tris.length-1;while(lo<hi){const m=(lo+hi)>>1;if(tp.tris[m].cum<w)lo=m+1;else hi=m;}\r\n const t=tp.tris[lo];let u=r(),v=r();if(u+v>1){u=1-u;v=1-v;}\r\n out.x=t.ax+(t.bx-t.ax)*u+(t.cx-t.ax)*v;out.z=t.az+(t.bz-t.az)*u+(t.cz-t.az)*v;return out;\r\n}\r\n\r\nfunction critterSprite(T){\r\n if(typeof document==='undefined')return null;\r\n try{const c=document.createElement('canvas');c.width=c.height=32;const g=c.getContext('2d');if(!g)return null;\r\n  const body=(col,o)=>{g.fillStyle=col;g.beginPath();g.ellipse(16,19,10+o,8+o,0,0,TAU);g.fill();g.fillRect(8-o,24,3+2*o,7+o);g.fillRect(21-o,24,3+2*o,7+o);g.fillRect(14-o,25,3+2*o,6+o);\r\n   g.beginPath();g.moveTo(8-o,14);g.lineTo(5-o,2-o);g.lineTo(14,11);g.fill();g.beginPath();g.moveTo(24+o,14);g.lineTo(27+o,2-o);g.lineTo(18,11);g.fill();};\r\n  body('#1a0a2a',1.5);body('#fff',0);g.fillStyle='#1a0a2a';g.fillRect(10,15,4,5);g.fillRect(18,15,4,5);g.fillStyle='#fff';g.fillRect(11,16,2,2);g.fillRect(19,16,2,2);\r\n  const t=new T.CanvasTexture(c);t.magFilter=t.minFilter=T.NearestFilter;t.generateMipmaps=false;t.colorSpace=T.SRGBColorSpace;return t;}catch{return null;}\r\n}\r\n\r\nexport function createWildForest(T,{cell=null,landuse=null,heightAt=()=>0,scale=1,seed=0,resolution=[320,180],reduced=false}={}){\r\n const group=new T.Group();group.name='wild-forest';group.scale.setScalar(scale);group.userData.layer='forest';\r\n const r=rng(hashSeed(cell,seed)),tp=parkTris(landuse),obstacles=[],parts=[],pt={x:0,z:0};\r\n const stats={trees:0,rocks:0,vines:0,cover:0,critters:0,draws:0,tris:0,canopyTris:0,vis:0,lod:'canopy'};\r\n let critters=null,puffMesh=null,crit=[],sprite=null,dead=false,setLod=()=>0,bbox=null;const mats=[];\r\n const puffs=[],NP=48;let puffHead=0;\r\n const M=new T.Matrix4(),Q=new T.Quaternion(),V=new T.Vector3(),S=new T.Vector3(),UP=new T.Vector3(0,1,0),cp={x:0,z:0,k:1},pl={x:0,z:0},PC=new T.Color();\r\n if(tp.area>=MIN_AREA){\r\n  const ico=ICO(T),statics=new Soup(),mk=(soup)=>{const g=soup.geo(T);parts.push(g);return g;};\r\n  const treeMat=createPs1Material(T,{vertexColors:true,resolution});treeMat.side=T.DoubleSide;mats.push(treeMat);parts.push(treeMat);\r\n  const nT=Math.min(MAX_TREES,Math.max(6,Math.floor(tp.area/TREE_AREA))),nS=Math.ceil(nT*.6),defs=[{soup:spiralTree(T,rng(hashSeed(cell,seed+1))),n:nS,h:6,s:[.7,1.7],rad:.6},{soup:mushroom(T,rng(hashSeed(cell,seed+2))),n:nT-nS,h:4.5,s:[.6,1.5],rad:.9}];\r\n  const E=new T.Euler(),TINT=[[1,1,1],[1,.8,1],[.8,1,1],[1,1,.7],[.8,.9,1.25]],trees=[],recs=[],fullM=[],liteM=[],ft=[],lt=[];\r\n  const liteDefs=defs.map((d,k)=>liteTree(T,d,rng(hashSeed(cell,seed+3+k)))); // own rng streams: the full archetypes and placement are untouched\r\n  defs.forEach((d,k)=>{const g=mk(d.soup),lg=mk(liteDefs[k]),cap=Math.max(1,d.n),im=new T.InstancedMesh(g,treeMat,cap),lm=new T.InstancedMesh(lg,treeMat,cap);\r\n   ft[k]=d.soup.p.length/9;lt[k]=liteDefs[k].p.length/9;\r\n   for(const m of [im,lm]){m.frustumCulled=false;m.name='forest-trees';m.userData.layer='forest';m.setColorAt(0,PC.setRGB(1,1,1));m.count=0;group.add(m);parts.push(m);}\r\n   lm.name='forest-trees-lite';fullM.push(im);liteM.push(lm);\r\n   for(let i=0;i<d.n;i++){sample(tp,r,pt);const s=d.s[0]+r()*(d.s[1]-d.s[0]),y=heightAt(pt.x,pt.z)+.06;\r\n    E.set((r()-.5)*.25,r()*TAU,(r()-.5)*.25);Q.setFromEuler(E);V.set(pt.x,y,pt.z);S.set(s,s*(.8+r()*.5),s);M.compose(V,Q,S);\r\n    const t=TINT[Math.floor(r()*TINT.length)];recs.push({k,x:pt.x,z:pt.z,m:M.toArray(),c:t,d2:0});\r\n    const o={x:pt.x,z:pt.z,r:d.rad*s,top:[pt.x,y+d.h*s*.9,pt.z],h:d.h*s};trees.push(o);obstacles.push(o);}\r\n   stats.tris+=d.n*ft[k];});\r\n  // canopy: every k-th tree (even spread) becomes a tree-sized blob (r 3-6 m) standing ON the terrain; <= CANOPY_MAX blobs x BLOB_TRIS tris = 2k tris. Colour = that tree's tinted canopy colour (+-8%).\r\n  const cg=rng(hashSeed(cell,seed+9)),step=Math.max(1,Math.ceil(recs.length/CANOPY_MAX)),blobs=[];\r\n  for(let i=0;i<recs.length&&blobs.length<CANOPY_MAX;i+=step){const rc=recs[i],o=trees[i],cc=defs[rc.k].soup.canopy,rx=Math.min(6,Math.max(3,2.2+o.h*.45)),v=.92+cg()*.16;\r\n   blobs.push({x:rc.x,z:rc.z,m:M.compose(V.set(rc.x,heightAt(rc.x,rc.z)+.06,rc.z),Q.setFromAxisAngle(UP,cg()*TAU),S.set(rx,rx,rx)).toArray(),c:[cc[0]*rc.c[0]*v,cc[1]*rc.c[1]*v,cc[2]*rc.c[2]*v]});}\r\n  const canopy=new T.InstancedMesh(mk(domeSoup(rng(hashSeed(cell,seed+8)))),treeMat,Math.max(1,blobs.length));canopy.frustumCulled=false;canopy.name='forest-canopy';canopy.userData.layer='forest';canopy.setColorAt(0,PC.setRGB(1,1,1));canopy.count=0;group.add(canopy);parts.push(canopy);\r\n  stats.canopyTris=blobs.length*BLOB_TRIS;\r\n  {let x0=1e9,z0=1e9,x1=-1e9,z1=-1e9;for(const t of tp.tris)for(const [x,z] of [[t.ax,t.az],[t.bx,t.bz],[t.cx,t.cz]]){x0=Math.min(x0,x);x1=Math.max(x1,x);z0=Math.min(z0,z);z1=Math.max(z1,z);}bbox=[x0,z0,x1,z1];}\r\n  const nR=Math.min(MAX_ROCKS,Math.floor(nT/6)+2);\r\n  for(let i=0;i<nR;i++){sample(tp,r,pt);obstacles.push(rock(statics,ico,pt.x,heightAt(pt.x,pt.z)+.06,pt.z,r));}\r\n  // vines link treetops <12 m apart; tentacles curl up from the floor; strands hang from canopies\r\n  let nv=0;const VP=Math.floor(MAX_VINES*.6);\r\n  for(let i=0;i<trees.length&&nv<VP;i++)for(let j=i+1;j<trees.length&&nv<VP;j++){const a=trees[i],b=trees[j],d=Math.hypot(a.x-b.x,a.z-b.z);if(d>=12||d<2||r()<.55)continue;\r\n   ribbon(statics,[a.x,a.top[1]-a.h*.25,a.z],[b.x,b.top[1]-b.h*.25,b.z],.3*d,.12,pick(r,PAL.vine));nv++;}\r\n  for(;nv<MAX_VINES*.8;){sample(tp,r,pt);tentacle(statics,pt.x,heightAt(pt.x,pt.z)+.06,pt.z,1.5+r()*3,pick(r,PAL.vine),r);nv++;}\r\n  for(let i=0;nv<MAX_VINES&&i<trees.length;i++){const a=trees[i],ang=r()*TAU;ribbon(statics,[a.top[0],a.top[1]-.3,a.top[2]],[a.top[0]+Math.cos(ang)*.8,a.top[1]-2.5-r()*3,a.top[2]+Math.sin(ang)*.8],.1,.1,pick(r,PAL.vine),5);nv++;}\r\n  const cbins=new Map(),cv=(x,z)=>{const i=Math.floor(x/COVER_BIN),j=Math.floor(z/COVER_BIN),k=i+','+j;let b=cbins.get(k);if(!b)cbins.set(k,b={soup:new Soup(),i,j});return b.soup;};\r\n  // ground cover: glowing moss discs, fungus, tall grass blades\r\n  const nM=Math.min(350,Math.floor(tp.area/40)),nF=Math.min(160,Math.floor(tp.area/70)),nG=Math.min(1800,Math.floor(tp.area/10));\r\n  for(let i=0;i<nM;i++){sample(tp,r,pt);const rad=1+r()*2.4,col=pick(r,PAL.moss),ctr=[pt.x,heightAt(pt.x,pt.z)+.17,pt.z],ph=r()*TAU,ring=a=>{const x=pt.x+Math.cos(a)*rad,z=pt.z+Math.sin(a)*rad;return [x,heightAt(x,z)+.1,z];};\r\n   for(let k=0;k<6;k++)cv(pt.x,pt.z).tri(ctr,ring(ph+k/6*TAU),ring(ph+(k+1)/6*TAU),mix(col,[.05,.1,.15],.35*(k%2)));}\r\n  for(let i=0;i<nF;i++){sample(tp,r,pt);const y=heightAt(pt.x,pt.z)+.06,s=.5+r()*.9,col=pick(r,PAL.cap),q=(a,rr,h)=>[pt.x+Math.cos(a)*rr*s,y+h*s,pt.z+Math.sin(a)*rr*s];\r\n   for(let k=0;k<6;k++){const a=k/6*TAU,b=(k+1)/6*TAU,c=cv(pt.x,pt.z);c.tri(q(a,.55,.7),q(b,.55,.7),q(0,0,1.15),mix(col,[1,1,1],k%2*.2));c.tri(q(a,.1,0),q(b,.1,0),q(a,.1,.7),[.9,.9,.95]);}}\r\n  for(let i=0;i<nG;i++){sample(tp,r,pt);const y=heightAt(pt.x,pt.z)+.06,h=.7+r()*1.6,a=r()*TAU,lean=(r()-.5)*.8,col=pick(r,PAL.grass),w=.09;\r\n   cv(pt.x,pt.z).tri([pt.x-Math.cos(a)*w,y,pt.z-Math.sin(a)*w],[pt.x+Math.cos(a)*w,y,pt.z+Math.sin(a)*w],[pt.x+Math.sin(a)*lean,y+h,pt.z-Math.cos(a)*lean],mix(col,[1,1,1],.25));}\r\n  const sg=mk(statics),sm=createPs1Material(T,{vertexColors:true,resolution});sm.side=T.DoubleSide;mats.push(sm);parts.push(sm);\r\n  const sMesh=new T.Mesh(sg,sm);sMesh.name='forest-floor-vines';sMesh.matrixAutoUpdate=false;sMesh.frustumCulled=false;sMesh.userData.layer='forest';group.add(sMesh);\r\n  const cover=[...cbins.values()].map(b=>{const m=new T.Mesh(mk(b.soup),sm);m.name='forest-cover';m.matrixAutoUpdate=false;m.frustumCulled=false;m.userData.layer='forest';group.add(m);\r\n   return {m,tris:b.soup.p.length/9,r:[b.i*COVER_BIN,b.j*COVER_BIN,(b.i+1)*COVER_BIN,(b.j+1)*COVER_BIN]};});\r\n  Object.assign(stats,{trees:trees.length,rocks:nR,vines:nv,cover:nM+nF+nG,tris:stats.tris+statics.p.length/9+cover.reduce((n,c)=>n+c.tris,0)});\r\n  // critters: one InstancedMesh of billboards; puffs: one InstancedMesh of dust / leaf blobs\r\n  const n=MAX_CRITTERS,pg=new T.PlaneGeometry(1,1).translate(0,.5,0);sprite=critterSprite(T);\r\n  const cm=shadeSprite(createPs1Material(T,{map:sprite,vertexColors:false,alphaTest:.5,resolution})); // V8 time-of-day tintcm.side=T.DoubleSide;cm.emissive=new T.Color(0x666666);\r\n  critters=new T.InstancedMesh(pg,cm,n);critters.name='forest-critters';critters.frustumCulled=false;critters.userData.layer='forest';\r\n  for(let i=0;i<n;i++){sample(tp,r,pt);const s=1.8+r()*1.2;crit.push({hx:pt.x,hz:pt.z,x:pt.x,z:pt.z,fx:pt.x,fz:pt.z,tx:pt.x,tz:pt.z,t0:0,dur:1,state:0,wake:0,s,hop:r()*TAU});\r\n   PC.setHSL(r(),1,.55);critters.setColorAt(i,PC);}\r\n  critters.instanceColor.needsUpdate=true;group.add(critters);parts.push(pg,cm,critters);if(sprite)parts.push(sprite);\r\n  const png=new T.IcosahedronGeometry(1,0),pm=createPs1Material(T,{vertexColors:false,resolution});pm.emissive=new T.Color(0x555555);\r\n  puffMesh=new T.InstancedMesh(png,pm,NP);puffMesh.frustumCulled=false;puffMesh.name='forest-puffs';puffMesh.userData.layer='forest';\r\n  S.set(0,0,0);M.compose(V.set(0,-999,0),Q.identity(),S);for(let i=0;i<NP;i++){puffMesh.setMatrixAt(i,M);puffMesh.setColorAt(i,PC.setRGB(.9,.8,.6));puffs.push({x:0,y:0,z:0,vx:0,vy:0,vz:0,born:-1e9,life:1,live:false});}\r\n  group.add(puffMesh);parts.push(png,pm,puffMesh);\r\n  stats.critters=n;\r\n  // LOD switch. mode 'full' = trees within NEAR_M (nearest-first full detail, rest lite, trimmed to `budget` tris), floor/vines, cover bins within COVER_M, critters, canopy blobs beyond NEAR_M;\r\n  // 'canopy' = blobs only; 'off' = nothing. Returns the triangles it draws. ponytail: trees beyond NEAR_M inside a blob whose centre is inside NEAR_M are not drawn (150 m is past the fog).\r\n  const NR2=NEAR_M*NEAR_M,sT=statics.p.length/9,wr=(im,i,rc)=>{im.instanceMatrix.array.set(rc.m,i*16);im.instanceColor.array.set(rc.c,i*3);},flush=im=>{im.instanceMatrix.needsUpdate=true;im.instanceColor.needsUpdate=true;};\r\n  let lodKey='';\r\n  setLod=(mode,tx=0,tz=0,budget=Infinity)=>{\r\n   const key=mode==='full'?`f${Math.round(tx/5)},${Math.round(tz/5)},${Math.round(Math.min(budget,1e6)/500)}`:mode;\r\n   if(key===lodKey)return stats.vis;lodKey=key;\r\n   const full=mode==='full';let used=0;\r\n   sMesh.visible=critters.visible=puffMesh.visible=full;if(full)used+=sT+2*n+20*NP;\r\n   for(const c of cover){const on=full&&rectDist(tx,tz,...c.r)<=COVER_M;c.m.visible=on;if(on)used+=c.tris;}\r\n   let nb=0;if(mode!=='off')for(const b of blobs)if(!full||(b.x-tx)**2+(b.z-tz)**2>=NR2)wr(canopy,nb++,b);\r\n   canopy.count=nb;canopy.visible=nb>0;if(nb)flush(canopy);used+=nb*BLOB_TRIS;\r\n   const nF=defs.map(()=>0),nL=defs.map(()=>0);\r\n   if(full){const inR=[];for(const rc of recs){rc.d2=(rc.x-tx)**2+(rc.z-tz)**2;if(rc.d2<=NR2)inR.push(rc);}\r\n    inR.sort((a,b)=>a.d2-b.d2);const room=budget-used;let c=0;for(const rc of inR)c+=lt[rc.k];\r\n    while(c>room&&inR.length)c-=lt[inR.pop().k];\r\n    let nf=0;for(const rc of inR){const x=ft[rc.k]-lt[rc.k];if(c+x>room)break;c+=x;nf++;}\r\n    inR.forEach((rc,i)=>wr(i<nf?fullM[rc.k]:liteM[rc.k],(i<nf?nF:nL)[rc.k]++,rc));used+=c;}\r\n   for(const [ms,cnt] of [[fullM,nF],[liteM,nL]])ms.forEach((im,k)=>{im.count=cnt[k];im.visible=cnt[k]>0;if(cnt[k])flush(im);});\r\n   let dc=0;group.traverse(o=>{if(o.isMesh&&o.visible)dc++;});\r\n   stats.vis=used;stats.lod=mode;stats.draws=dc;return used;\r\n  };\r\n  for(const m of mats)m.emissive.setRGB(.09,.035,.12); // until the first step() pulses it\r\n  setLod('canopy');\r\n }\r\n let lastDecide=-1e9,inside=false;\r\n const DUST=[.9,.8,.6],LEAF=[.5,.95,.3];\r\n function emit(x,z,now){ // 4 puffs: dust + leaves kicked up when a critter bolts\r\n  for(let k=0;k<4;k++){const i=puffHead++%NP,p=puffs[i],a=Math.random()*TAU,sp=1+Math.random()*2.2,col=k%2?LEAF:DUST;\r\n   p.x=x;p.y=heightAt(x,z)+.3;p.z=z;p.vx=Math.cos(a)*sp;p.vz=Math.sin(a)*sp;p.vy=1.5+Math.random()*2;p.born=now;p.life=600+Math.random()*300;p.live=true;puffMesh.setColorAt(i,PC.setRGB(col[0],col[1],col[2]));}\r\n  puffMesh.instanceColor.needsUpdate=true;\r\n }\r\n function place(now,yaw){\r\n  if(!critters)return;Q.setFromAxisAngle(UP,yaw);\r\n  for(let i=0;i<crit.length;i++){const c=crit[i];critterAt(c,now,cp);\r\n   const run=cp.k<1?Math.abs(Math.sin(now*.03+c.hop))*.3*c.s:0,bob=1+.07*Math.sin(now*.007+c.hop); // scampering hop / idle breathing\r\n   V.set(cp.x,heightAt(cp.x,cp.z)+.06+run,cp.z);S.set(c.s,c.s*bob,1);M.compose(V,Q,S);critters.setMatrixAt(i,M);}\r\n  critters.instanceMatrix.needsUpdate=true;\r\n  Q.identity();for(let i=0;i<NP;i++){const p=puffs[i];if(!p.live)continue;const k=(now-p.born)/p.life;\r\n   if(k>=1||k<0){p.live=false;S.set(0,0,0);V.set(0,-999,0);M.compose(V,Q,S);puffMesh.setMatrixAt(i,M);continue;}\r\n   const t=(now-p.born)/1000,sz=(.25+.5*k)*Math.pow(1-k,.6);V.set(p.x+p.vx*t,p.y+p.vy*t-3*t*t,p.z+p.vz*t);S.setScalar(sz);M.compose(V,Q,S);puffMesh.setMatrixAt(i,M);}\r\n  puffMesh.instanceMatrix.needsUpdate=true;\r\n }\r\n const relocate=(c,o,px,pz)=>{const d=Math.hypot(px-o.x,pz-o.z)||1,x=o.x+(px-o.x)/d*(o.r+1.4),z=o.z+(pz-o.z)/d*(o.r+1.4);c.hx=c.x=c.fx=c.tx=x;c.hz=c.z=c.fz=c.tz=z;c.t0=0;c.dur=1;c.state=0;};\r\n function step(yaw,player,now){\r\n  if(dead||!critters)return;\r\n  const pulse=reduced?.7:.55+.45*Math.sin(now*.003);for(const m of mats)m.emissive.setRGB(.16*pulse,.06*pulse,.22*pulse); // bioluminescent breathing\r\n  if(!reduced&&now-lastDecide>=100){lastDecide=now;\r\n   for(const c of crit)if(decide(c,player,yaw,obstacles,now)===1)emit(c.fx,c.fz,now);\r\n   let near=false;for(const o of obstacles)if((o.x-player.x)**2+(o.z-player.z)**2<625){near=true;break;}\r\n   if(!near)inside=false;\r\n   else{const L=obstacles.length,k0=Math.abs(now|0)%L,front=[],side=[];\r\n    for(let k=0;k<L;k++){const o=obstacles[(k0+k)%L],d2=(o.x-player.x)**2+(o.z-player.z)**2;if(d2<36||d2>324)continue;(inCone(player,o.x,o.z,yaw,CONE-.1,18)?front:side).push(o);if(front.length>=12&&side.length>=12)break;}\r\n    const ringPt=(ang,d)=>({x:player.x-Math.sin(ang)*d,z:player.z-Math.cos(ang)*d,r:-1.4}); // open-ground spot when no trunk is handy (you are inside the forest, so it is forest)\r\n    while(front.length<8)front.push(ringPt(yaw+(Math.random()-.5)*.9,6+Math.random()*8));\r\n    while(side.length<6)side.push(ringPt(yaw+(Math.random()<.5?-1:1)*(1.1+Math.random()*2),6+Math.random()*10));\r\n    if(!inside){inside=true;let n=0;for(const c of crit){if(n>=8||!front.length)break;if(c.state!==0||(c.x-player.x)**2+(c.z-player.z)**2<324)continue;relocate(c,front[n%front.length],player.x,player.z);n++;}} // entering a park: some are always in front of you\r\n    let near18=0;for(const c of crit)if(c.state===0&&(c.x-player.x)**2+(c.z-player.z)**2<324)near18++;\r\n    if(near18<30&&side.length){let n=0;for(const c of crit){if(n>=5)break;if(c.state!==0||(c.x-player.x)**2+(c.z-player.z)**2<2025)continue;relocate(c,side[(n++*7+k0)%side.length],player.x,player.z);}} // keep the ring around you stocked, just outside your view\r\n   }\r\n  }\r\n  place(now,yaw);\r\n }\r\n // map view: the point on the ground the camera looks at (the \"player\"); AR (d=0): the camera itself. -> {x,z,yaw} in group-local metres (shared object)\r\n const A={x:0,z:0,yaw:0};\r\n function aim(cam,d=0){\r\n   if(dead||!cam||!critters)return null;\r\n   cam.updateWorldMatrix(true,false);group.updateWorldMatrix(true,false);\r\n   V.setFromMatrixPosition(cam.matrixWorld);group.worldToLocal(V);const ox=V.x,oy=V.y,oz=V.z;\r\n   cam.getWorldPosition(V);S.set(0,0,-1).transformDirection(cam.matrixWorld);V.add(S);group.worldToLocal(V);\r\n   let fx=V.x-ox,fy=V.y-oy,fz=V.z-oz,px=ox,pz=oz;\r\n   if(Math.hypot(fx,fz)<.25*Math.hypot(fx,fy,fz)){ // looking (almost) straight down: screen-up is \"ahead\"\r\n    S.set(0,1,0).transformDirection(cam.matrixWorld);cam.getWorldPosition(V);V.add(S);group.worldToLocal(V);fx=V.x-ox;fz=V.z-oz;}\r\n   if(d>0&&fy<-.05){const k=-oy/fy;px=ox+fx*k;pz=oz+fz*k;}\r\n   A.x=px;A.z=pz;A.yaw=Math.atan2(-fx,-fz);return A;\r\n }\r\n return {group,stats,obstacles,critters:crit,step,\r\n  // t = seconds (world layer / walkthrough frame clock); d = map camera distance (0 in AR); cam = three camera\r\n  update(t,d=0,cam=null){\r\n   if(stats.lod!=='full')return;const a=aim(cam,d);if(!a)return;\r\n   pl.x=a.x;pl.z=a.z;step(a.yaw,pl,t*1000);\r\n  },\r\n  aim,bbox,setLod:(...a)=>setLod(...a),\r\n  dispose(){dead=true;group.removeFromParent();for(const p of parts)p.dispose?.();parts.length=0;crit.length=0;obstacles.length=0;},\r\n };\r\n}\r\n","modules/coach-world-rts/wild-hunt-core.mjs":"// Wild hunt logic with no DOM and no three.js: range bands, IN RANGE, safety gates, WALK<->AIM gate, motion analysis, catch timeline. Node-testable.\r\nimport {BANDS,SAFETY} from './wild-contract.mjs';\r\n\r\nexport const effD=(d,acc)=>Math.max(0,d-.5*(acc||0));\r\nexport const bandIndexFor=dEff=>{let i=-1;for(let k=0;k<BANDS.length;k++)if(dEff<=BANDS[k].max)i=k;return i;};\r\n\r\n// Inward moves need 4 s dwell per band (anti GPS-jump); outward only at +15% distance (anti flicker). First sample is taken as-is.\r\nexport function createBandTracker({dwellMs=SAFETY.dwellMs,hyst=SAFETY.hyst}={}){\r\n let cur=null,since=0;\r\n return {\r\n  get band(){return cur==null||cur<0?null:BANDS[cur].id;},\r\n  step(t,dEff){\r\n   const target=bandIndexFor(dEff);\r\n   if(cur==null){cur=target;since=t;return this.band;}\r\n   if(target>cur){if(t-since>=dwellMs){cur+=1;since=t;}}\r\n   else if(target<cur){if(cur<0||dEff>BANDS[cur].max*hyst){cur=target;since=t;}}\r\n   return this.band;\r\n  },\r\n };\r\n}\r\n\r\n// IN RANGE: strict = d_eff<=15 and accuracy<=15 held 1.5 s. Under trees (acc>15): d_eff<=30 and still 2 s (\"close enough\", spook then comes from motion only).\r\n// acc>40 pauses the hunt (no penalty); after 90 s it just waits (kept, never flees).\r\nexport function createRangeTracker(){\r\n let since=null,mode=null,held=false,pausedAt=null;\r\n return {\r\n  step(t,{dEff,acc,speed}){\r\n   const paused=acc>SAFETY.gpsPauseAcc;if(paused){pausedAt??=t;since=null;held=false;return {inRange:false,paused:true,closeEnough:false,pausedMs:t-pausedAt,waiting:t-pausedAt>=SAFETY.pauseWaitMs};}\r\n   pausedAt=null;\r\n   const strict=acc<=SAFETY.inRangeAcc,lim=strict?15:SAFETY.closeEnoughD,need=strict?SAFETY.inRangeHoldMs:SAFETY.closeEnoughStillMs;\r\n   const ok=held?dEff<=lim*SAFETY.hyst&&(strict||speed<SAFETY.aimExitMps):dEff<=lim&&(strict||speed<SAFETY.aimStillMps);\r\n   if(!ok){since=null;held=false;mode=null;}\r\n   else{if(since==null||mode!==strict){since=t;mode=strict;}if(t-since>=need)held=true;}\r\n   return {inRange:held,paused:false,closeEnough:held&&!strict,pausedMs:0,waiting:false};\r\n  },\r\n };\r\n}\r\n\r\n// Road freeze + speed caps. roadDist in metres to the nearest drivable road (Infinity when unknown: spawns are >=25 m from roads by construction).\r\n// >2.5 m/s soft warning; >4 m/s on 2 consecutive samples (one GPS spike is not a sprint) = 5 min lockout, `released` fires once so the caller drops the token.\r\nexport function createSafety(){\r\n let lockUntil=0,hot=0;\r\n return {\r\n  step(t,{speed=0,roadDist=Infinity,hour=12}={}){\r\n   let released=false;\r\n   hot=speed>SAFETY.lockMps?hot+1:0;\r\n   if(hot>=SAFETY.lockSamples&&t>=lockUntil){lockUntil=t+SAFETY.lockMs;released=true;}\r\n   const locked=t<lockUntil,quiet=hour>=SAFETY.quietFrom||hour<SAFETY.quietTo;\r\n   return {freeze:roadDist<SAFETY.roadM,warn:speed>SAFETY.softMps,locked,lockLeftMs:Math.max(0,lockUntil-t),quiet,released,text:locked?'Slow down. The hunt is paused for 5 minutes.':roadDist<SAFETY.roadM?'Step away from the road':speed>SAFETY.softMps?'Slow down. Do not run.':quiet?'Hunts rest at night.':''};\r\n  },\r\n };\r\n}\r\nconst NOT_DRIVABLE=new Set(['footway','path','pedestrian','steps','cycleway','track','rail','bridleway']);\r\nexport const isDrivable=cls=>!NOT_DRIVABLE.has(cls);\r\nconst segDist=(px,pz,ax,az,bx,bz)=>{const dx=bx-ax,dz=bz-az,l=dx*dx+dz*dz,t=l?Math.max(0,Math.min(1,((px-ax)*dx+(pz-az)*dz)/l)):0;return Math.hypot(px-(ax+dx*t),pz-(az+dz*t));};\r\n// roads: [{class, pts:[[x,z]..]}] in metres about the same origin as (x,z) (the world-roads input format)\r\nexport function roadDistance(roads,x,z){let best=Infinity;for(const r of roads||[]){if(!isDrivable(r.class))continue;const p=r.pts;for(let i=1;i<p.length;i++)best=Math.min(best,segDist(x,z,p[i-1][0],p[i-1][1],p[i][0],p[i][1]));}return best;}\r\n\r\n// WALK <-> AIM. AIM opens only still (<0.4 m/s for 1 s) AND phone raised (>25 deg) AND allowed (stalk or closer). It exits on moving >0.8 m/s for 2 s (eyes-up card),\r\n// on lowering the phone for 1 s, or when no longer allowed.\r\nexport function createAimGate(){\r\n let mode='WALK',a=null,b=null;\r\n return {\r\n  get mode(){return mode;},\r\n  step(t,{speed=0,pitchDeg=0,allowed=true}={}){\r\n   let reason=null,eyesUp=false;\r\n   if(mode==='WALK'){\r\n    const ok=allowed&&speed<SAFETY.aimStillMps&&pitchDeg>SAFETY.aimPitchDeg;\r\n    if(ok){a??=t;if(t-a>=SAFETY.aimStillMs){mode='AIM';a=b=null;reason='open';}}else a=null;\r\n   }else{\r\n    if(!allowed){mode='WALK';reason='range';a=b=null;}\r\n    else{\r\n     speed>SAFETY.aimExitMps?(a??=t):(a=null);\r\n     pitchDeg<SAFETY.aimLowerDeg?(b??=t):(b=null);\r\n     if(a!=null&&t-a>=SAFETY.aimExitMs){mode='WALK';reason='moving';eyesUp=true;a=b=null;}\r\n     else if(b!=null&&t-b>=SAFETY.aimLowerMs){mode='WALK';reason='lowered';a=b=null;}\r\n    }\r\n   }\r\n   return {mode,reason,eyesUp};\r\n  },\r\n };\r\n}\r\n\r\n// Devicemotion -> cadence (steps/s), heavy-step count, peak g, gyro RMS (rad/s). Thresholds are the S1/S2 spike knobs: override with TUNE or localStorage cw-wildlab-tune.\r\nexport const TUNE={stepThresh:1.5,stompThresh:6,minStepMs:280,gyroWindowMs:500,cadenceWindowMs:3000,peakWindowMs:1000};\r\nexport function createMotion({tune={}}={}){\r\n const K={...TUNE,...tune};let g=[0,0,9.81],steps=[],lastStep=-1e9,up=false,peakMag=0,stomps=0,peaks=[],gyro=[];\r\n return {\r\n  K,\r\n  // acc: accelerationIncludingGravity {x,y,z} m/s^2; rot: rotationRate deg/s {alpha,beta,gamma}\r\n  push(t,acc,rot){\r\n   if(acc&&Number.isFinite(acc.x)){\r\n    g=g.map((v,i)=>v+(([acc.x,acc.y,acc.z][i]??v)-v)*.1);\r\n    const l=[acc.x-g[0],acc.y-g[1],acc.z-g[2]],m=Math.hypot(...l);peaks.push([t,m]);\r\n    if(m>K.stepThresh&&!up&&t-lastStep>=K.minStepMs){up=true;lastStep=t;steps.push(t);peakMag=m;}\r\n    else if(up){peakMag=Math.max(peakMag,m);if(m<K.stepThresh*.6){up=false;if(peakMag>K.stompThresh)stomps++;peakMag=0;}}\r\n    while(peaks.length&&t-peaks[0][0]>K.peakWindowMs)peaks.shift();\r\n    while(steps.length&&t-steps[0]>K.cadenceWindowMs)steps.shift();\r\n   }\r\n   if(rot&&Number.isFinite(rot.alpha)){const r=Math.PI/180;gyro.push([t,(rot.alpha*r)**2+(rot.beta*r)**2+(rot.gamma*r)**2]);while(gyro.length&&t-gyro[0][0]>K.gyroWindowMs)gyro.shift();}\r\n  },\r\n  cadenceHz(t){const n=steps.filter(s=>t-s<=K.cadenceWindowMs).length;return n/(K.cadenceWindowMs/1000);},\r\n  peakG(){return peaks.reduce((m,p)=>Math.max(m,p[1]),0)/9.81;},\r\n  gyroRms(){return gyro.length?Math.sqrt(gyro.reduce((s,p)=>s+p[1],0)/gyro.length):0;},\r\n  takeStomps(){const n=stomps;stomps=0;return n;},\r\n  speedEstimate(t){return this.cadenceHz(t)*SAFETY.strideM;},\r\n };\r\n}\r\n\r\n// Coach facing scans 90 deg every 20-40 s (deterministic per seed). Arc of the player relative to that facing: front 120, rear 120, side the rest.\r\nexport const angleDelta=(from,to)=>{const d=(((to-from)%360)+540)%360-180;return d;};\r\nexport const facingAt=(baseDeg,seed,tSec)=>{const period=20+(Math.abs(seed|0)%21);return (baseDeg+90*Math.floor(tSec/period))%360;};\r\nexport const bearingArc=(facingDeg,bearingToPlayerDeg)=>{const r=Math.abs(angleDelta(facingDeg,bearingToPlayerDeg));return r<60?'front':r>120?'rear':'side';};\r\n\r\n// Catch beat sheet (A15 3): tranq 0, sway 300, sleep 1300, descend 2500, beam 3700, lift-off 4400, toast 5000, +150 ms hover per tier (cap +1000 so <=6.5 s). Skippable after 1.5 s.\r\nexport const PHASES=Object.freeze(['aim','tranq','sway','sleep','descend','beam','liftoff','toast','done']);\r\nexport const SKIP_AFTER_MS=1500;\r\nexport function catchTimeline(tierIdx=0,reduced=false){\r\n if(reduced)return [['tranq',0,150],['sleep',150,900],['liftoff',900,1500],['toast',1500,2000]];\r\n const e=Math.min(Math.max(0,tierIdx)*150,1000);\r\n return [['tranq',0,300],['sway',300,1300],['sleep',1300,2500],['descend',2500,3700],['beam',3700,4400+e],['liftoff',4400+e,5000+e],['toast',5000+e,5500+e]];\r\n}\r\nexport function createCatch({tierIdx=0,reduced=false}={}){\r\n const tl=catchTimeline(tierIdx,reduced),total=tl[tl.length-1][2],toastAt=tl[tl.length-1][1];let t0=null,skipped=false;\r\n return {\r\n  timeline:tl,total,\r\n  get started(){return t0!=null;},\r\n  start(t){t0=t;skipped=false;},\r\n  canSkip(t){return t0!=null&&!skipped&&t-t0>=SKIP_AFTER_MS&&t-t0<toastAt;},\r\n  skip(t){if(!this.canSkip(t))return false;t0=t-toastAt;skipped=true;return true;},\r\n  update(t){\r\n   if(t0==null)return {phase:'aim',k:0,done:false,el:0};\r\n   const el=t-t0;if(el>=total)return {phase:'done',k:1,done:true,el};\r\n   const seg=tl.findLast(s=>el>=s[1]);return {phase:seg[0],k:(el-seg[1])/(seg[2]-seg[1]),done:false,el,skipped};\r\n  },\r\n };\r\n}\r\n\r\n// Reticle quality (A14 3.1): gyro RMS over the charge window + angle off the coach torso. Perfect <0.15 rad/s & <12 deg, Hit <0.5 & <25, else Miss.\r\nexport const shotQuality=(rms,angleDeg)=>rms<.15&&angleDeg<12?'perfect':rms<.5&&angleDeg<25?'hit':'miss';\r\nexport const CHARGE_MS=800;\r\n","modules/coach-world-rts/wild-hunt-view.mjs":"// Wild-coach hunt view. WALK = HUD over the Map tab (band chip, arrow, metres, spook eye, noise ring, safety cards).\r\n// AIM = rear-camera passthrough + three.js scene (wild-aim-scene.mjs), only when standing still with the phone raised; moving exits. Server decides catch/flee; this file animates.\r\n// mountWildHunt(host,{api,rules,perms,subscribe,encounter,target,getBase,signal,onEnd,win,doc,deps}) -> {dispose,tick,state,...}. Feature flag lives in the caller (wild-radar).\r\nimport {distBearing,SAFETY,tierName,tierIndex,tierColor} from './wild-contract.mjs';\r\nimport {effD,createBandTracker,createRangeTracker,createSafety,createAimGate,createMotion,roadDistance,facingAt,bearingArc,shotQuality,CHARGE_MS} from './wild-hunt-core.mjs';\r\nimport {createFlee,gotAwayText} from './wild-flee.mjs';\r\nimport {createTrack,compassHeading,angleDelta} from './geo-follow.mjs';\r\nimport {createPoseSource,yawOf} from './pose-source.mjs';\r\nimport {injectWildStyle,px} from './wild-ui.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\n\r\nexport const TICK_MS=100,FRAME_MS=33,PS1=2;\r\nconst CUES={'tranq':'tranq-zap','sleep':'snore','beam':'beam-up','toast':'caught','alert':'spooked','vanish':'poof'};\r\nexport const unitFor=(spawn,id='')=>{const i=Number.isInteger(spawn?.unitIndex)?spawn.unitIndex:[...String(spawn?.unitId||id)].reduce((h,c)=>(h*31+c.charCodeAt(0))>>>0,7)%FOUR_LEGGED_SOURCES.length;return FOUR_LEGGED_SOURCES[i%FOUR_LEGGED_SOURCES.length];};\r\nconst el=(doc,tag,css,text)=>{const e=doc.createElement(tag);if(css)e.style.cssText=css;if(text!=null)e.textContent=text;return e;};\r\nconst _CARD='position:absolute;left:50%;transform:translateX(-50%);padding:10px 16px;border-radius:12px;background:#0d0d14e6;color:#fff;font:600 16px system-ui,sans-serif;text-align:center;border:2px solid #ffffff44;max-width:88vw';\r\n\r\nexport function mountWildHunt(host,o){\r\n const win=o.win||globalThis,doc=o.doc||win.document,ab=new AbortController(),sig=ab.signal;\r\n const D={\r\n  now:()=>win.performance.now(),hour:()=>new Date().getHours(),reduced:()=>!!win.matchMedia?.('(prefers-reduced-motion: reduce)').matches,\r\n  loadThree:()=>import('/vendor/three/three.module.js'),loadScene:()=>import('./wild-aim-scene.mjs'),\r\n  createRenderer:(T,canvas)=>new T.WebGLRenderer({canvas,alpha:true,antialias:false}),\r\n  getUserMedia:c=>win.navigator?.mediaDevices?.getUserMedia?.(c),roads:()=>[],project:null,\r\n  setInt:(f,ms)=>win.setInterval(f,ms),clearInt:h=>win.clearInterval(h),raf:f=>win.requestAnimationFrame(f),caf:h=>win.cancelAnimationFrame(h),\r\n  play:cue=>import('./sfx.mjs').then(m=>m.play(cue)).catch(()=>{}),haptic:ms=>{try{win.navigator?.vibrate?.(ms);}catch{}},\r\n  ...(o.deps||{})\r\n };\r\n const enc=o.encounter||{},sp=enc.spawn||{},tier=tierName(o.rules.rarity,sp.tier??o.target?.tier),tIdx=tierIndex(o.rules.rarity,tier);\r\n const dead=()=>sig.aborted||o.signal?.aborted;\r\n const coachPos={lat:sp.lat,lng:sp.lng},seed=[...String(enc.encounterId||o.target?.spawnId||'')].reduce((h,c)=>(h*31+c.charCodeAt(0))>>>0,1);\r\n const faction=sp.faction||o.target?.faction||o.getBase?.()?.faction||'chest';\r\n const track=createTrack(),motion=createMotion(),bands=createBandTracker(),range=createRangeTracker(),safety=createSafety(),gate=createAimGate();\r\n let spook=o.rules.createSpook(tier,{napping:!!sp.napping}),flee=null,fix=null,origin=null,heading=null,beta=null,phase='walk',sed=enc.sedation||0,darts=enc.darts??(tier==='iridescent'?6:5),busy=false,aim=null,ended=false,eyes=0,lastBand=null,noQuiet=false,start=D.now(),lastTick=start,charge=null,lastWarn=0,pendingStream=false;\r\n const S={band:null,spook:0,inRange:false,d:null,mode:'WALK',safety:null};\r\n\r\n // ---- DOM ----\r\n const k=injectWildStyle(doc,faction),root=px(doc,'div','wh');root.style.cssText='position:fixed;inset:0;z-index:2147482000;pointer-events:none';root.dataset.wildHunt='';\r\n const top=px(doc,'div','wh-card row'),arrow=px(doc,'div','wh-arrow'),line=px(doc,'div',''),band=px(doc,'div','wh-sub'),metres=px(doc,'div','wh-big'),eye=px(doc,'div','wh-eye');eye.append(doc.createElement('i'));eye.style.background='conic-gradient(#3dff6a 0deg,#222 0deg)';\r\n top.style.top='calc(env(safe-area-inset-top,0px) + 6px)';line.append(metres,band);top.append(arrow,line,eye);\r\n const banner=px(doc,'div','wh-card wh-big'),toast=px(doc,'div','wh-card'),leave=px(doc,'button','wh-btn','Leave'),aimBtn=px(doc,'button','wh-btn go','Aim');\r\n banner.style.cssText='top:46%;display:none';toast.style.cssText='bottom:calc(env(safe-area-inset-bottom,0px) + 96px);display:none';\r\n leave.style.cssText='position:absolute;left:8px;bottom:calc(env(safe-area-inset-bottom,0px) + 12px)';aimBtn.style.cssText='position:absolute;right:8px;bottom:calc(env(safe-area-inset-bottom,0px) + 12px);display:none';\r\n root.append(top,banner,toast,leave,aimBtn);host.append(root);\r\n const say=(box,text,ms)=>{box.textContent=text;box.style.display=text?'block':'none';if(ms){const h=D.setInt(()=>{D.clearInt(h);if(box.textContent===text){box.style.display='none';}},ms);}};\r\n const sfx=cue=>{try{D.play(CUES[cue]||cue);}catch{}};\r\n say(banner,\"Look up. Don't run. Stay off roads.\",SAFETY.startCardMs);\r\n\r\n // ---- sensors (permissions were requested synchronously in the tap by the caller) ----\r\n Promise.resolve(o.perms?.sensors).then(ok=>{\r\n  if(dead())return;\r\n  if(!ok){say(toast,'Motion access off. Sneaking is judged by GPS only.',4000);aimBtn.style.display='block';return;}\r\n  win.addEventListener('devicemotion',e=>{const a=e.accelerationIncludingGravity,r=e.rotationRate;motion.push(D.now(),a&&{x:a.x,y:a.y,z:a.z},r&&{alpha:r.alpha,beta:r.beta,gamma:r.gamma});},{signal:sig});\r\n  const ori=e=>{const h=compassHeading(e,win.screen?.orientation?.angle||0);if(h!=null)heading=h;if(Number.isFinite(e.beta))beta=e.beta;};\r\n  for(const n of ['deviceorientationabsolute','deviceorientation'])win.addEventListener(n,ori,{signal:sig});\r\n });\r\n const trail=[];let sentAt=0; // fixes since the last shot: the server replays spook from this (wild-shot `track`)\r\n const onFix=f=>{if(Number.isFinite(f?.latitude)){const at=Number.isFinite(f.sampledAt)?f.sampledAt:Date.now();fix={...f,sampledAt:at};track.push(fix);if(!trail.length||at>trail[trail.length-1].sampledAt){trail.push({latitude:f.latitude,longitude:f.longitude,accuracy:f.accuracy??50,sampledAt:at});if(trail.length>200)trail.shift();}}},unsub=o.subscribe?.(onFix);if(o.lastFix)onFix(o.lastFix); // a still phone gets no new fix: seed with the radar's last one\r\n const takeTrack=()=>{const pts=trail.filter(p=>p.sampledAt>sentAt).slice(-60);if(pts.length)sentAt=pts[pts.length-1].sampledAt;return pts;};\r\n\r\n const local=(lat,lng)=>{if(D.project)return D.project(lat,lng);origin??={lat,lng};const k=Math.PI/180;return {x:(lng-origin.lng)*111320*Math.cos(origin.lat*k),z:-(lat-origin.lat)*111320};};\r\n\r\n // ---- the 10 Hz loop ----\r\n function tick(t=D.now()){\r\n  if(ended||dead())return S;\r\n  const dt=Math.min(1000,t-lastTick);lastTick=t;\r\n  if(D.hour()>=SAFETY.quietFrom||D.hour()<SAFETY.quietTo){if(!noQuiet){noQuiet=true;say(banner,'Hunts rest at night.');finish('quiet');}return S;}\r\n  if(!fix){metres.textContent='…';band.textContent='Waiting for GPS';return S;}\r\n  const {d,bearing}=distBearing(fix.latitude,fix.longitude,coachPos.lat,coachPos.lng),acc=fix.accuracy||0,dEff=effD(d,acc);\r\n  const gps=Number.isFinite(fix.speed)?fix.speed:track.speed,mSpeed=motion.speedEstimate(t),haveMotion=motion.cadenceHz(t)>0||beta!=null;\r\n  const loose=acc>SAFETY.inRangeAcc,speed=loose&&haveMotion?mSpeed:Math.max(gps||0,haveMotion?mSpeed:0); // under trees GPS speed lies: motion sensors only\r\n  const pos=local(fix.latitude,fix.longitude),roadDist=roadDistance(D.roads(),pos.x,pos.z);\r\n  const saf=safety.step(t,{speed:Math.max(gps||0,mSpeed),roadDist,hour:D.hour()});\r\n  const b=bands.step(t,dEff),rg=range.step(t,{dEff,acc,speed:gps||0});\r\n  const cur=rg.inRange?'range':b;\r\n  const facing=facingAt(sp.facing||0,seed,(t-start)/1000),arc=bearingArc(facing,(bearing+180)%360);\r\n  const paused=rg.paused||saf.locked||saf.warn||!!flee;\r\n  const sample={band:cur,speed,dEff,acc,stomp:motion.takeStomps(),shakeRms:motion.gyroRms(),bearingArc:arc,roadDist,cadenceHz:motion.cadenceHz(t),paused};\r\n  const sres=spook.step(sample,dt||TICK_MS);\r\n  S.band=cur;S.spook=sres.value;S.inRange=rg.inRange;S.d=d;S.mode=gate.mode;S.safety=saf;S.rangeState=rg;\r\n  if(saf.released){say(banner,saf.text);api_end('locked');finish('locked');return S;}\r\n  // HUD\r\n  const rel=heading==null?bearing:angleDelta(heading,bearing);arrow.style.transform=`rotate(${rel}deg)`;\r\n  metres.textContent=`${Math.round(d/10)*10} m`;band.textContent=rg.paused?'GPS weak, step into the open':(rg.inRange?'IN RANGE':cur||'far')+(saf.text?` · ${saf.text}`:'');\r\n  const v=Math.round(sres.value*3.6);eye.style.background=`conic-gradient(${sres.value>=85?'#ff4a3a':sres.value>=60?'#ffb21c':'#3dff6a'} ${v}deg,#222 ${v}deg)`;\r\n  if(cur!==lastBand){lastBand=cur;if(cur==='range')D.haptic(60);}\r\n  if(rg.inRange&&gate.mode==='WALK')say(banner,'IN RANGE · raise your phone and stand still');\r\n  else if(!rg.inRange&&banner.textContent.startsWith('IN RANGE'))say(banner,'');\r\n  if(saf.freeze||saf.warn){if(t-lastWarn>1500){lastWarn=t;say(toast,saf.text,1500);}}\r\n  if(sres.event==='fidget')sfx('wobble');\r\n  if(sres.event==='warn'){D.haptic([30,30,30]);root.style.boxShadow='inset 0 0 40px #ff4a3a88';}else if(sres.value<80)root.style.boxShadow='none';\r\n  if(sres.event==='flee'&&!flee)startFlee(t,bearing,d);\r\n  // flee stages\r\n  if(flee){const f=flee.update(t,{speed,dist:d});aim?.scene.flee(f.stage,f.k);\r\n   if(f.recovered){flee=null;aim?.scene.flee(null,0);spook=o.rules.createSpook(tier,{napping:!!sp.napping});say(toast,'It settled. Careful.',1500);}\r\n   else if(f.done){say(banner,gotAwayText(bearing,d+14));api_end('fled');finish('fled');return S;}}\r\n  // AIM gate\r\n  const pitch=beta??(o.rules.assumePitch??60),allowed=(cur==='stalk'||cur==='range')&&!saf.freeze&&!saf.warn&&!saf.locked&&!rg.paused&&!flee&&phase!=='catch';\r\n  const g=gate.step(t,{speed:gps||0,pitchDeg:pitch,allowed});\r\n  if(g.reason==='open')openAim();\r\n  if(g.mode==='WALK'&&aim&&phase==='aim'){closeAim();if(g.eyesUp)say(banner,'Eyes up. Stand still to aim.',2500);}\r\n  aimBtn.style.display=allowed&&!aim?'block':'none';\r\n  return S;\r\n }\r\n const api_end=reason=>{try{Promise.resolve(o.api.end(enc.encounterId,reason)).catch(()=>{});}catch{}};\r\n function startFlee(t,bearing,d){flee=createFlee({reduced:D.reduced()});flee.start(t);sfx('alert');D.haptic([40,40,80]);if(!aim)say(banner,'!');}\r\n\r\n // ---- AIM ----\r\n async function openAim(){\r\n  if(aim||ended||phase==='catch')return;phase='aim';\r\n  const a=aim={closed:false,video:null,stream:null};\r\n  try{\r\n   const [T,sceneMod]=await Promise.all([D.loadThree(),D.loadScene()]);if(a.closed||dead())return;\r\n   const aroot=a.root=px(doc,'div','wh');aroot.style.cssText='position:fixed;inset:0;z-index:2147482500;background:linear-gradient(#1b3a4a,#2d5a3a 55%,#16301f);touch-action:none;overflow:hidden';\r\n   const video=a.video=el(doc,'video','position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:none');video.muted=true;video.playsInline=true;\r\n   const canvas=a.canvas=el(doc,'canvas','position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated');\r\n   const ret=el(doc,'div','position:absolute;left:50%;top:50%;width:64px;height:64px;margin:-32px 0 0 -32px;border:3px solid #ffffffcc;border-radius:50%;box-shadow:0 0 0 2px #0d0d14'),ring=el(doc,'div','position:absolute;left:50%;top:50%;width:84px;height:84px;margin:-42px 0 0 -42px;border-radius:50%;background:conic-gradient(#fff 0deg,#0000 0deg);-webkit-mask:radial-gradient(circle,#0000 56%,#000 58%);mask:radial-gradient(circle,#0000 56%,#000 58%)'),\r\n    info=px(doc,'div','wh-card'),ban=px(doc,'div','wh-card wh-big'),pad=el(doc,'div','position:absolute;inset:0'),exit=px(doc,'button','wh-btn','Back'),tt=px(doc,'div','wh-card');\r\n   info.style.top='calc(env(safe-area-inset-top,0px) + 6px)';ban.style.cssText='top:110px;display:none';tt.style.cssText='bottom:calc(env(safe-area-inset-bottom,0px) + 90px);display:none';exit.style.cssText='position:absolute;left:8px;bottom:calc(env(safe-area-inset-bottom,0px) + 12px)';\r\n   aroot.append(video,canvas,pad,ret,ring,info,ban,tt,exit);host.append(aroot);ret.style.pointerEvents=ring.style.pointerEvents='none';a.ui={ret,ring,info,ban,tt}; // the reticle sits over screen centre: it must not eat the hold-to-fire touch\r\n   \r\n   (async()=>{try{const s=await D.getUserMedia({audio:false,video:{facingMode:{ideal:'environment'}}});if(!s)return;if(a.closed){s.getTracks().forEach(x=>x.stop());return;}a.stream=s;video.srcObject=s;video.style.display='block';a.scene?.setPassthrough(true);await video.play();}catch{}})();\r\n   const pose=a.pose=D.pose||createPoseSource({win});Promise.resolve(o.perms?.sensors).then(ok=>pose.begin(ok?'granted':'denied'));\r\n   const renderer=a.renderer=D.createRenderer(T,canvas),base=o.getBase?.(),pets=await (sceneMod.loadPets?.(doc)||{}).catch(()=>({}));if(a.closed||dead()){renderer.dispose?.();return;}\r\n   const unit=unitFor(sp,enc.encounterId),dye=FACTION_BY_ID[faction]?.color||'#fff';\r\n   const scene=a.scene=sceneMod.createAimScene(T,{doc,rules:o.rules,getBase:()=>o.getBase?.()||{},tier,faction,facing:sp.facing||0,frame:unit.frame,pets:{...pets},reduced:D.reduced(),now:D.now,hooks:{onPhase:(p)=>sfx(p)}});scene.setPassthrough(!!a.stream);\r\n   const fit=()=>{const w=win.innerWidth,h=win.innerHeight;renderer.setSize(Math.round(w/PS1),Math.round(h/PS1),false);scene.camera.aspect=w/h;scene.camera.fov=w/h<1?Math.min(100,2*Math.atan(Math.tan(37.5*Math.PI/180)/(w/h))*180/Math.PI):70;scene.camera.updateProjectionMatrix();};\r\n   fit();win.addEventListener('resize',fit,{signal:sig});\r\n   const p0=pose.pose();scene.setPlacement(yawOf(p0.quaternion));\r\n   exit.onclick=()=>{gate.step(0,{});closeAim();};\r\n   // hold to charge, release to fire\r\n   pad.addEventListener('pointerdown',e=>{if(busy||phase==='catch'){if(phase==='catch'&&scene.canSkip(D.now()))scene.skip(D.now());return;}if(!S.inRange){say(ban,'Get closer');return;}charge={t:D.now()};D.haptic(10);});\r\n   const release=()=>{if(!charge)return;const held=D.now()-charge.t;charge=null;ring.style.background='conic-gradient(#fff 0deg,#0000 0deg)';if(held>=CHARGE_MS)shoot();else say(ban,'Hold to charge');};\r\n   pad.addEventListener('pointerup',release);pad.addEventListener('pointercancel',()=>{charge=null;});\r\n   let last=-1e9;\r\n   const frame=t=>{\r\n    if(a.closed)return;a.raf=D.raf(frame);if(doc.hidden||t-last<FRAME_MS)return;last=t;\r\n    const ps=pose.pose();scene.update(t,ps);\r\n    if(charge){const k=Math.min(1,(t-charge.t)/CHARGE_MS);ring.style.background=`conic-gradient(${k>=1?'#3dff6a':'#fff'} ${k*360}deg,#0000 ${k*360}deg)`;}\r\n    info.textContent=`${tierName(o.rules.rarity,tier)} · sedation ${Math.round(sed)}% · darts ${darts}`;\r\n    const cs=scene.catchState();if(cs){if(scene.canSkip(t)&&!a.skipHint){a.skipHint=true;say(tt,'Tap to skip');}if(cs.phase==='toast'&&!a.toasted){a.toasted=true;say(tt,'Added to your base');}\r\n     if(cs.done&&!a.finished){a.finished=true;D.haptic(40);win.dispatchEvent?.(new win.CustomEvent('myr5:wild-caught',{detail:{encounterId:enc.encounterId,tier}}));finish('caught');return;}}\r\n    renderer.render(scene.scene,scene.camera);\r\n   };\r\n   a.raf=D.raf(frame);S.aimReady=true;\r\n  }catch(e){console.warn('wild aim',e);closeAim();}\r\n }\r\n function closeAim(){\r\n  const a=aim;if(!a)return;a.closed=true;aim=null;if(a.raf)D.caf(a.raf);\r\n  a.scene?.dispose();a.stream?.getTracks().forEach(t=>t.stop());if(a.video)a.video.srcObject=null;a.pose?.dispose();\r\n  const r=a.renderer;if(r){try{r.dispose();r.forceContextLoss?.();}catch{}}\r\n  a.root?.remove();phase='walk';\r\n }\r\n async function shoot(){\r\n  if(busy||!aim?.scene||!S.inRange)return;busy=true;const a=aim,t=D.now(),rms=motion.gyroRms(),ang=a.scene.aimAngle(),q=shotQuality(rms,ang);\r\n  a.scene.fire(t);sfx('tranq');D.haptic(25);\r\n  try{\r\n   const res=await o.api.shot(enc.encounterId,{quality:q,aimRms:rms,tier,sedation:sed,dartsLeft:darts});\r\n   if(ended||a.closed)return;\r\n   sed=res.sedation??sed;darts=res.dartsLeft??darts-1;\r\n   if(res.outcome==='caught'){phase='catch';a.scene.startCatch(D.now());}\r\n   else if(res.outcome==='fled'){startFlee(D.now(),0,S.d||0);}\r\n   else{a.scene.stagger(D.now());say(a.ui.ban,q==='miss'?'Missed!':q==='perfect'?'Perfect!':'Hit');if(spook.state&&Number.isFinite(spook.state.meter))spook.state={...spook.state,meter:Math.min(99,spook.state.meter+(q==='miss'?15:10))};}\r\n  }catch(e){say(a.ui.ban,'Shot failed, try again');}\r\n  finally{busy=false;}\r\n }\r\n aimBtn.onclick=()=>{gate.step(0,{});beta??=60;openAim();};\r\n leave.onclick=()=>{api_end('left');finish('left');};\r\n\r\n function finish(why){\r\n  if(ended)return;ended=true;S.end=why;if(why!=='caught')closeAim(); // HUD banners (\"It got away\") live under the AIM layer\r\n  const h=D.setInt(()=>{D.clearInt(h);dispose();},why==='caught'||why==='left'?0:2600);\r\n }\r\n function dispose(){\r\n  if(S.disposed)return;S.disposed=true;ended=true;ab.abort();D.clearInt(loop);closeAim();try{unsub?.();}catch{}root.remove();\r\n  try{o.onEnd?.(S.end||'left');}catch{}\r\n }\r\n const loop=D.setInt(()=>tick(),TICK_MS);\r\n return {tick,dispose,takeTrack,state:S,root,motion,get aim(){return aim;},openAim,shoot,get spook(){return spook;},_set:(k,v)=>{if(k==='beta')beta=v;if(k==='heading')heading=v;}};\r\n}\r\n","modules/coach-world-rts/wild-lab.mjs":"// Hidden phone tuning overlay (?wildlab=1): live cadence Hz, peak g, gyro RMS, GPS accuracy. S1 = walk/stomp/jog marks, S2 = aim hold marks, saved to localStorage cw-wildlab-log (Copy -> clipboard).\r\nimport {requestSensors,wildLabFlag} from './wild-contract.mjs';\r\nimport {createMotion} from './wild-hunt-core.mjs';\r\nexport const LOG_KEY='cw-wildlab-log',LOG_MAX=300;\r\nexport const snapshot=(motion,t,fix,spike=null)=>({t:Math.round(t),spike,cadenceHz:+motion.cadenceHz(t).toFixed(2),peakG:+motion.peakG().toFixed(2),gyroRms:+motion.gyroRms().toFixed(3),acc:fix?.accuracy??null,speed:fix?.speed??null});\r\nexport function mountWildLab(host,{win=globalThis,subscribe,flag=wildLabFlag,motion=createMotion()}={}){\r\n const doc=win.document;if(!flag(win)||!doc)return undefined;\r\n const ab=new AbortController(),box=doc.createElement('div');box.dataset.wildLab='';\r\n box.style.cssText='position:fixed;right:6px;top:120px;z-index:2147483200;font:12px ui-monospace,monospace;color:#9fffb0;background:#000c;padding:6px;border-radius:8px;max-width:60vw';\r\n const out=doc.createElement('pre');out.style.margin='0';const mk=(t,f)=>{const b=doc.createElement('button');b.textContent=t;b.style.cssText='min-height:40px;margin:2px;font:12px system-ui';b.onclick=f;return b;};\r\n let fix=null,on=false,timer=0;const log=[];const now=()=>win.performance.now();\r\n const mark=s=>{log.push(snapshot(motion,now(),fix,s));if(log.length>LOG_MAX)log.shift();try{win.localStorage.setItem(LOG_KEY,JSON.stringify(log));}catch{}};\r\n const start=mk('LAB',()=>{if(on)return;on=true;const p=requestSensors(win); // tap = gesture\r\n  p.sensors.then(ok=>{if(!ok){out.textContent='motion denied';return;}win.addEventListener('devicemotion',e=>{const a=e.accelerationIncludingGravity,r=e.rotationRate;motion.push(now(),a&&{x:a.x,y:a.y,z:a.z},r&&{alpha:r.alpha,beta:r.beta,gamma:r.gamma});},{signal:ab.signal});\r\n   timer=win.setInterval(()=>{const s=snapshot(motion,now(),fix);out.textContent=`cadence ${s.cadenceHz} Hz\\npeak ${s.peakG} g\\ngyro ${s.gyroRms} rad/s\\nGPS acc ${s.acc??'-'} m\\nmarks ${log.length}`;},250);});});\r\n box.append(out,start,mk('S1',()=>mark('S1')),mk('S2',()=>mark('S2')),mk('Copy',()=>win.navigator?.clipboard?.writeText(JSON.stringify(log))));host.append(box);\r\n const unsub=subscribe?.(f=>{fix=f;});\r\n return ()=>{ab.abort();win.clearInterval(timer);try{unsub?.();}catch{}box.remove();};\r\n}\r\n","modules/coach-world-rts/wild-radar.mjs":"// Wild-coach radar INSIDE the one-map base3d world view: createWildRadar = a world-layer part (400 m ring + sweep, tier pins over the real park forest, 20 s poll, HUD-style Hunt button).\r\n// Off unless ?wild=1. Beyond 150 m the server only sends cell centres + near/far; this module never invents a precise position (pinPos only reads exact fields when present).\r\nimport {wildFlag,wildStubFlag,loadRules,createWildApi,requestSensors,tierName,tierColor,distBearing,BANDS} from './wild-contract.mjs';\r\nimport {injectWildStyle,px} from './wild-ui.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\n\r\nexport const SRC_PINS='cw-wild-pins',SRC_RING='cw-wild-ring',LAYERS=['cw-wild-ring-line','cw-wild-pins','cw-wild-pins-label'];\r\nexport const POLL_MS=20000,RING_M=400,SEEN_KEY='cw-wild-seen',WEAK_ACC=40,MOVE_REPOLL_M=150;\r\n\r\nexport const roundTen=m=>Math.round(m/10)*10;\r\nexport const distLabel=m=>`${roundTen(m)} m`;\r\nexport const bandWord=d=>d>RING_M?'hidden':d>BANDS[1].max?(d>250?'far':'near'):d>BANDS[2].max?'tracking':d>BANDS[3].max?'alert':d>BANDS[4].max?'stalk':'in range';\r\n// exact position only exists after wild-start released it; otherwise the cell centre (already fuzzed server-side)\r\nexport const pinPos=s=>s.exact&&Number.isFinite(s.lat)?{lat:s.lat,lng:s.lng,exact:true}:s.cellCenter?{lat:s.cellCenter.lat,lng:s.cellCenter.lng,exact:false}:Number.isFinite(s.lat)?{lat:s.lat,lng:s.lng,exact:!!s.exact}:null;\r\n\r\nexport function shapePoints(shape,r=1){\r\n const poly=(n,rot=-Math.PI/2)=>Array.from({length:n},(_,i)=>[Math.cos(rot+i*2*Math.PI/n)*r,Math.sin(rot+i*2*Math.PI/n)*r]);\r\n if(shape==='triangle')return poly(3);if(shape==='square')return poly(4,Math.PI/4);if(shape==='diamond')return poly(4);if(shape==='pentagon')return poly(5);\r\n if(shape==='star')return Array.from({length:12},(_,i)=>{const a=-Math.PI/2+i*Math.PI/6,k=i%2?.5:1;return [Math.cos(a)*r*k,Math.sin(a)*r*k];});\r\n return poly(24);\r\n}\r\nexport const loadSeen=(store=globalThis.localStorage)=>{try{return new Set(JSON.parse(store?.getItem(SEEN_KEY)||'[]'));}catch{return new Set();}};\r\nexport const saveSeen=(set,store=globalThis.localStorage)=>{try{store?.setItem(SEEN_KEY,JSON.stringify([...set].slice(-200)));}catch{}};\r\n\r\n// What a pin looks like. silhouette = grey body with only the tier glow until the spawnId has been seen (A14 2.2 Aware).\r\nexport function pinSpec(spawn,{seen=new Set(),rarity,factionOf=()=>null,ms=0}={}){\r\n const name=tierName(rarity,spawn.tier),sil=!seen.has(spawn.spawnId),fac=!sil?factionOf(spawn):null;\r\n return {key:`${name}|${sil?'x':fac||'-'}`,tier:name,shape:rarity.SHAPES[name],pips:rarity.PIPS[name],fill:sil?'#8a93a0':tierColor(rarity,name,ms),glow:tierColor(rarity,name,ms),silhouette:sil,glyph:fac,underStroke:'#0d0d14'};\r\n}\r\nexport function drawPin(doc,spec,{size=48,factionColor=()=>'#fff'}={}){\r\n const c=Object.assign(doc.createElement('canvas'),{width:size,height:size}),g=c.getContext('2d'),m=size/2,r=size*.28;\r\n g.lineJoin='round';\r\n const path=(rr)=>{g.beginPath();shapePoints(spec.shape,rr).forEach(([x,y],i)=>i?g.lineTo(m+x,m-2+y):g.moveTo(m+x,m-2+y));g.closePath();};\r\n g.shadowColor=spec.glow;g.shadowBlur=spec.silhouette?8:5;path(r);g.fillStyle=spec.fill;g.fill();g.shadowBlur=0;\r\n g.lineWidth=4;g.strokeStyle=spec.underStroke;g.stroke();path(r);g.fillStyle=spec.fill;g.fill();\r\n if(spec.glyph){g.beginPath();g.arc(m,m-2,r*.38,0,Math.PI*2);g.fillStyle=factionColor(spec.glyph);g.fill();g.lineWidth=2;g.strokeStyle=spec.underStroke;g.stroke();}\r\n for(let i=0;i<spec.pips;i++){const x=m+(i-(spec.pips-1)/2)*5;g.beginPath();g.arc(x,size-5,2,0,Math.PI*2);g.fillStyle=spec.fill;g.fill();g.lineWidth=1;g.strokeStyle=spec.underStroke;g.stroke();}\r\n return c;\r\n}\r\n\r\n// Poller: every POLL_MS and when the player moved MOVE_REPOLL_M since the last poll; pauses while hidden. All timing injectable.\r\nexport function createPoller({poll,isHidden=()=>globalThis.document?.hidden,setTimer=(f,ms)=>setInterval(f,ms),clearTimer=clearInterval,now=()=>Date.now()}){\r\n let h=null,last=null,busy=false,stopped=false,lastAt=-Infinity;\r\n const run=async(fix)=>{if(stopped||busy||isHidden()||!fix)return false;busy=true;try{await poll(fix);last=fix;lastAt=now();}finally{busy=false;}return true;};\r\n return {\r\n  start(getFix){h=setTimer(()=>{run(getFix());},POLL_MS);return run(getFix());},\r\n  moved(fix){if(!fix)return false;if(!last)return run(fix);const {d}=distBearing(last.latitude,last.longitude,fix.latitude,fix.longitude);return d>=MOVE_REPOLL_M&&now()-lastAt>3000?run(fix):false;},\r\n  stop(){stopped=true;if(h!=null)clearTimer(h);h=null;},\r\n };\r\n}\r\n\r\nconst factionOf=s=>s.faction||null;\r\nconst MLAT=111320;\r\nexport const toLocal=(origin,lat,lng)=>({x:(lng-origin.lng)*MLAT*Math.cos(origin.lat*Math.PI/180),z:-(lat-origin.lat)*MLAT}); // world-layer frame: +x east, +z south\r\nexport const PIN_PX=.05; // sprite height as a fraction of the screen (constant size at any zoom)\r\n\r\nexport function createWildRadar(T,{origin,heightAt=()=>0,scale=1,getBase=()=>null,deps={}}={}){\r\n const win=deps.win||globalThis,doc=deps.doc||win.document,group=new T.Group();group.name='wild-radar';group.scale.setScalar(scale);\r\n const ab=new AbortController(),own=[],track=o=>(own.push(o),o);\r\n let rules=null,api=null,fix=null,spawns=[],hunt=null,weak=false,off=false,dead=false,dirty=true,stubSeeded=false,lastSig=-1,poller=null,unsub=null,lab=null;\r\n const seen=loadSeen(win.localStorage),texCache=new Map(),sprites=new Map();\r\n // ring (400 m) + rotating sweep, flat over the ground at the player\r\n const flat={transparent:true,side:T.DoubleSide,depthWrite:false,depthTest:false,blending:T.AdditiveBlending,toneMapped:false,fog:false};\r\n const ringMat=track(new T.MeshBasicMaterial({color:0x7ff6ff,opacity:.55,...flat})),sweepMat=track(new T.MeshBasicMaterial({color:0x7ff6ff,opacity:.08,...flat}));\r\n const ring=new T.Mesh(track(new T.RingGeometry(RING_M-2.5,RING_M+2.5,96)),ringMat),sweep=new T.Mesh(track(new T.CircleGeometry(RING_M,24,0,.6)),sweepMat);\r\n for(const m of [ring,sweep]){m.rotation.x=-Math.PI/2;m.renderOrder=40;m.frustumCulled=false;group.add(m);}\r\n // HUD chip + Hunt button (pixel style)\r\n const host=deps.host||doc.getElementById('coachWorldPanel')||doc.body,box=px(doc,'div','wh');box.dataset.wildRadar='';box.style.cssText='position:fixed;left:8px;bottom:calc(env(safe-area-inset-bottom,0px) + 70px);z-index:2147481000;display:none;gap:6px;align-items:center;pointer-events:none';\r\n const txt=px(doc,'span','wh-sub'),btn=px(doc,'button','wh-btn go','Hunt');btn.hidden=true;txt.style.cssText='font-size:22px;color:#dfe8ee;text-shadow:0 1px 0 #000,1px 0 0 #000';\r\n box.append(txt,btn);host.append(box);injectWildStyle(doc,getBase()?.faction);\r\n\r\n const iconFor=spec=>{let t=texCache.get(spec.key);if(t)return t;const c=drawPin(doc,spec,{size:64,factionColor:id=>FACTION_BY_ID?.[id]?.color||'#fff'});t=track(new T.CanvasTexture(c));t.magFilter=t.minFilter=T.NearestFilter;t.generateMipmaps=false;t.colorSpace=T.SRGBColorSpace;texCache.set(spec.key,t);return t;};\r\n function syncPins(ms){\r\n  const live=new Set();let best=null;\r\n  for(const s of spawns){const p=pinPos(s);if(!p||!fix)continue;const {d,bearing}=distBearing(fix.latitude,fix.longitude,p.lat,p.lng);if(d>RING_M*1.4)continue;\r\n   live.add(s.spawnId);const dEff=Math.max(0,d-.5*(fix.accuracy||0));if(dEff<=BANDS[1].max&&p.exact)seen.add(s.spawnId);\r\n   const spec=pinSpec(s,{seen,rarity:rules.rarity,factionOf,ms}),L=toLocal(origin,p.lat,p.lng),y=heightAt(L.x,L.z);\r\n   let e=sprites.get(s.spawnId);\r\n   if(!e){const sp=new T.Sprite(track(new T.SpriteMaterial({map:iconFor(spec),transparent:true,depthTest:false,depthWrite:false,sizeAttenuation:false,toneMapped:false,fog:false})));sp.renderOrder=50;sp.center.set(.5,0);\r\n    const pole=new T.Mesh(track(new T.CylinderGeometry(.5,.5,40,6,1,true)),track(new T.MeshBasicMaterial({color:new T.Color(spec.glow),opacity:.35,transparent:true,blending:T.AdditiveBlending,depthWrite:false,toneMapped:false,fog:false})));pole.renderOrder=45;\r\n    group.add(sp,pole);e={sp,pole,key:spec.key};sprites.set(s.spawnId,e);}\r\n   if(e.key!==spec.key){e.sp.material.map=iconFor(spec);e.sp.material.needsUpdate=true;e.key=spec.key;}\r\n   e.sp.scale.set(PIN_PX,PIN_PX,1);e.sp.position.set(L.x,y+42,L.z);e.pole.position.set(L.x,y+20,L.z);e.pole.material.color.set(spec.glow);\r\n   if(!best||d<best.d)best={s,d,dEff,bearing,exact:p.exact};}\r\n  for(const [id,e] of sprites)if(!live.has(id)){group.remove(e.sp,e.pole);e.sp.material.dispose();e.pole.geometry.dispose();e.pole.material.dispose();sprites.delete(id);}\r\n  weak=!!fix&&(fix.accuracy||0)>WEAK_ACC;\r\n  btn.hidden=!(best&&best.dEff<=BANDS[1].max&&!weak&&!hunt);\r\n  txt.textContent=weak?'GPS weak - step into the open':off?'Hunting is warming up':best?`${bandWord(best.d)} ${distLabel(best.d)}`:'';\r\n  box.dataset.target=best?best.s.spawnId:'';saveSeen(seen,win.localStorage);\r\n  if(best)win.dispatchEvent?.(new win.CustomEvent('myr5:wild-target',{detail:{spawnId:best.s.spawnId,band:bandWord(best.d)}}));\r\n  return best;\r\n }\r\n function placeRing(){if(!fix)return;const L=toLocal(origin,fix.latitude,fix.longitude),y=heightAt(L.x,L.z)+1.5;ring.position.set(L.x,y,L.z);sweep.position.set(L.x,y,L.z);}\r\n const watch=fn=>{const g=win.navigator?.geolocation;if(!g)return()=>{};const id=g.watchPosition(p=>fn({latitude:p.coords.latitude,longitude:p.coords.longitude,accuracy:p.coords.accuracy,speed:p.coords.speed,heading:p.coords.heading,sampledAt:p.timestamp}),()=>{},{enableHighAccuracy:true,maximumAge:2000});return()=>g.clearWatch(id);};\r\n const subscribe=deps.subscribe||watch;\r\n const boot=(async()=>{\r\n  rules=await (deps.rules||loadRules());if(dead)return;\r\n  import('./wild-lab.mjs').then(m=>{if(!dead)lab=m.mountWildLab(host,{win,subscribe});},()=>{});\r\n  api=deps.api||createWildApi(deps.client,{stub:(deps.stub??wildStubFlag)(win),rarity:rules.rarity,getContext:()=>({...(win.__cwWalkSession||{}),track:hunt?.takeTrack?.()})}) // live walk session published by map-view + our own fixes;\r\n  poller=createPoller({poll:async f=>{let r;try{r=await api.nearby({latitude:f.latitude,longitude:f.longitude});}catch{r={off:true};}off=!!r.off;spawns=r.spawns||[];dirty=true;},isHidden:()=>doc.hidden});\r\n  unsub=subscribe(f=>{\r\n   if(!Number.isFinite(f?.latitude))return;fix=f;placeRing();dirty=true;\r\n   if(api.stub&&!stubSeeded){stubSeeded=true;{const q=new URLSearchParams(win.location?.search||''),[e,n]=(q.get('wildoff')||'0,122').split(',').map(Number); // dev stub: where the fake coach stands (metres east,north of you)\r\n    api.seed(deps.fakeSpawns||[{spawnId:'stub-1',tier:q.get('wildtier')||'gold',lat:f.latitude+n/MLAT,lng:f.longitude+e/(MLAT*Math.cos(f.latitude*Math.PI/180)),facing:200,faction:'chest',exact:true}]);}}\r\n   poller.moved(f);\r\n  });\r\n  poller.start(()=>fix);\r\n })().catch(e=>console.warn('coach-world wild radar',e));\r\n btn.onclick=()=>{ // tap = the gesture: permission prompts fire here, synchronously, before any await\r\n  const perms=requestSensors(win),target=spawns.find(s=>s.spawnId===box.dataset.target);if(!target)return;\r\n  if(!api.stub&&!win.__cwWalkSession?.sessionId){txt.textContent='Start a sector walk first';return;} // the server judges the hunt from the live walk session\r\n  btn.disabled=true;\r\n  Promise.resolve().then(()=>(deps.loadHunt||(()=>import('./wild-hunt-view.mjs')))()).then(async m=>{\r\n   const started=await api.start(target.spawnId);if(dead)return;\r\n   hunt=m.mountWildHunt(deps.huntHost||host,{api,rules,perms,subscribe,encounter:started,target,getBase,lastFix:fix,signal:ab.signal,win,doc,deps:deps.huntDeps,onEnd:()=>{hunt=null;btn.disabled=false;dirty=true;}});\r\n  }).catch(e=>{btn.disabled=false;txt.textContent=e?.code==='wild_dead'?'It left.':'Cannot start the hunt right now.';});\r\n };\r\n const part={\r\n  group,ready:boot,sprites,get best(){return box.dataset.target;},\r\n  setFade(){}, // overlay part: ignores the base dissolve; visibility comes from the layer group\r\n  update(t,d,cam){ // t seconds (world layer clock)\r\n   if(dead)return;if(cam)part.cam=cam;const on=group.visible!==false&&group.parent?.visible!==false;box.style.display=on&&(txt.textContent||!btn.hidden)?'flex':'none';\r\n   sweep.rotation.z=-t*.9;\r\n   if(rules&&(dirty||Math.floor(t)!==lastSig)){lastSig=Math.floor(t);dirty=false;syncPins(t*1000);}\r\n  },\r\n  dispose(){\r\n   dead=true;ab.abort();lab?.();poller?.stop();try{unsub?.();}catch{}hunt?.dispose?.();box.remove();\r\n   for(const e of sprites.values())e.sp.material.dispose();for(const o of own)o.dispose?.();group.removeFromParent();\r\n  },\r\n };\r\n if(win.location?.search?.includes('wilddebug'))win.__wr=part;\r\n return part;\r\n}\r\n","modules/coach-world-rts/wild-rarity.mjs":"export const TIERS = Object.freeze(['white', 'green', 'blue', 'orange', 'gold', 'iridescent']);\r\n\r\nexport const TIER_WEIGHTS = Object.freeze({\r\n  white: 0.58, green: 0.25, blue: 0.11,\r\n  orange: 0.045, gold: 0.014, iridescent: 0.001\r\n});\r\n\r\nexport const LAND_TILT = Object.freeze({\r\n  park: Object.freeze({}),\r\n  wood: Object.freeze({ blue: 1.5, green: 1.2 }),\r\n  desert: Object.freeze({ orange: 1.5, white: 0.8 }),\r\n  wetland: Object.freeze({ blue: 1.3, iridescent: 1.5 })\r\n});\r\n\r\nexport const CATCH_ODDS = Object.freeze({\r\n  white: 0.95, green: 0.85, blue: 0.70,\r\n  orange: 0.52, gold: 0.35, iridescent: 0.20\r\n});\r\n\r\nexport const SPOOK_MULT = Object.freeze({\r\n  white: 0.8, green: 1.0, blue: 1.15,\r\n  orange: 1.35, gold: 1.6, iridescent: 2.0\r\n});\r\n\r\nexport const SEDATION_PER_HIT = Object.freeze({\r\n  white: 60, green: 50, blue: 42,\r\n  orange: 34, gold: 28, iridescent: 22\r\n});\r\n\r\nexport const DARTS = Object.freeze({ default: 5, iridescent: 6 });\r\n\r\nexport const DAILY_CAPS = Object.freeze({\r\n  encounters: 40, blue: 10, orange: 5,\r\n  gold: 3, iridescent: 1\r\n});\r\n\r\nexport const PIPS = Object.freeze({\r\n  white: 1, green: 2, blue: 3,\r\n  orange: 4, gold: 5, iridescent: 6\r\n});\r\n\r\nexport const SHAPE = Object.freeze({\r\n  white: 'circle', green: 'triangle', blue: 'square',\r\n  orange: 'pentagon', gold: 'star', iridescent: 'diamond'\r\n});\r\n\r\nexport const PLATE = Object.freeze({\r\n  white: 'ring', green: 'ring-notched', blue: 'square-diamond',\r\n  orange: 'chevron', gold: 'star6', iridescent: 'star6'\r\n});\r\n\r\nexport const PALETTE = Object.freeze({\r\n  white: '#ffffff', green: '#3dff6a', blue: '#2f8bff',\r\n  orange: '#ff8a1c', gold: '#ffd21f', iridescent: null\r\n});\r\n\r\nexport const HOLO_HUES = Object.freeze([0x3ff5ff, 0xb388ff, 0xff4bd8, 0xffd36a]);\r\n\r\nexport const AURA = Object.freeze({\r\n  white: null, green: 'magicbubbles', blue: 'protectioncircle',\r\n  orange: 'firespin', gold: 'sunburn', iridescent: 'nebula'\r\n});\r\n\r\nexport const PLATE_STROKE_DARK = '#0d0d14';\r\n\r\nfunction fnv1aHash(str) {\r\n  let h = 2166136261;\r\n  for (let i = 0; i < str.length; ++i) {\r\n    h = Math.imul(h ^ str.charCodeAt(i), 16777619);\r\n  }\r\n  return h >>> 0;\r\n}\r\n\r\nexport function weightsFor(landType, goldenHour = false) {\r\n  const base = TIER_WEIGHTS;\r\n  const tilt = LAND_TILT[landType] || {};\r\n  const w = {};\r\n  let sum = 0;\r\n  for (const tier of TIERS) {\r\n    let weight = base[tier];\r\n    weight *= tilt[tier] ?? 1;\r\n    if (landType === 'park' && tier === 'iridescent') weight = 0;\r\n    if (goldenHour && tier === 'gold') weight *= 2;\r\n    w[tier] = weight;\r\n    sum += weight;\r\n  }\r\n  for (const tier of TIERS) {\r\n    w[tier] /= sum;\r\n  }\r\n  return Object.freeze(w);\r\n}\r\n\r\nexport function rollTier(seed, landType, goldenHour = false) {\r\n  const hash = fnv1aHash(String(seed));\r\n  const u01 = hash / 4294967296;\r\n  const weights = weightsFor(landType, goldenHour);\r\n  let cum = 0;\r\n  for (const tier of TIERS) {\r\n    cum += weights[tier];\r\n    if (u01 < cum) return tier;\r\n  }\r\n  return 'white';\r\n}\r\n","modules/coach-world-rts/wild-spook.mjs":"import { SPOOK_MULT } from './wild-rarity.mjs';\r\n\r\nexport const BANDS_M = Object.freeze({\r\n  aware: 400,\r\n  tracking: 150,\r\n  alert: 60,\r\n  stalk: 25,\r\n  inRange: 15,\r\n});\r\nexport const HYSTERESIS = 1.15;\r\nexport const MIN_DWELL_MS = 4000;\r\nexport const WIGGLE_MS = 3000;\r\nexport const TREE_ACCURACY_M = 40; // above this the hunt pauses (no penalty)\r\nexport const TREE_DISTANCE_M = 30;\r\nexport const NAP_GAIN = 0.4;\r\nexport const NAP_DECAY = 0.5;\r\nexport const ROAD_FREEZE_M = 10;\r\nexport const STRICT_ACCURACY_M = 15;\r\nexport const STRICT_HOLD_MS = 1500;\r\nexport const TREE_HOLD_MS = 2000;\r\nexport const STILL_SPEED_MPS = 0.4;\r\nexport const ALERT_GAIN = Object.freeze({ speedFree: 1.4, perMps: 3.0 });\r\nexport const STALK_GAIN = Object.freeze({ speedFree: 0.8, perMps: 7.0 });\r\nexport const INRANGE_GAIN = Object.freeze({ speedFree: 0.5, perMps: 10.0 });\r\nexport const STOMP_PEAK_G = 1.8;\r\nexport const STOMP_GAIN = 6;\r\nexport const SHAKE_RMS = 2.0;\r\nexport const SHAKE_PER_S = 2;\r\nexport const NEAR_STOMP_M = 40;\r\nexport const DECAY_STILL_PER_S = 4;\r\nexport const DECAY_MOVING_PER_S = 1.5;\r\nexport const STILL_DECAY_SPEED = 0.5;\r\nexport const STILL_CADENCE_HZ = 0.8;\r\nexport const DIRECTION_MULT = Object.freeze({ rear: 0.5, side: 1.0, front: 1.5 });\r\nexport const FIDGET_AT = 60;\r\nexport const BOLT_AT = 85;\r\nexport const FLEE_AT = 100;\r\n\r\nexport function createSpook() {\r\n  return { meter: 0, band: 'hidden', bandSinceMs: 0, holdMs: 0, fled: false, clockMs: 0 };\r\n}\r\n\r\nfunction clamp(v, lo, hi) {\r\n  return v < lo ? lo : v > hi ? hi : v;\r\n}\r\nfunction round2(v) { return Math.round(v * 100) / 100; }\r\n\r\nexport function stepSpook(state, inputs) {\r\n  const dtMs = clamp(inputs.dtMs ?? 0, 0, 1000);\r\n  const nowClock = state.clockMs + dtMs;\r\n\r\n  // Road freeze: within 10 m of a drivable road the meter and the approach stand still (no gain, no decay, no band change)\r\n  if ((inputs.roadDistM ?? Infinity) < ROAD_FREEZE_M) {\r\n    return { state: { ...state, clockMs: nowClock, holdMs: 0 }, meter: state.meter, band: state.band, fled: state.fled, event: null, roadFrozen: true };\r\n  }\r\n\r\n  // Raw band from distance\r\n  let rawBand;\r\n  if (inputs.distanceM > 400) rawBand = 'hidden';\r\n  else if (inputs.distanceM > 150) rawBand = 'aware';\r\n  else if (inputs.distanceM > 60) rawBand = 'tracking';\r\n  else if (inputs.distanceM > 25) rawBand = 'alert';\r\n  else if (inputs.distanceM > 15) rawBand = 'stalk';\r\n  else rawBand = 'stalk';\r\n\r\n  // Hysteresis band walk\r\n  let nextBand = rawBand;\r\n  const currOuter = BANDS_M[state.band] ?? 0;\r\n  const rawOuter = BANDS_M[rawBand] ?? 0;\r\n\r\n  if (state.band !== rawBand) {\r\n    if (inputs.distanceM > currOuter * HYSTERESIS) {\r\n      // moving outward allowed only after hysteresis threshold\r\n      nextBand = rawBand;\r\n    } else if (nowClock - state.bandSinceMs < MIN_DWELL_MS) {\r\n      // moving inward but still waiting dwell\r\n      nextBand = state.band;\r\n    }\r\n  }\r\n\r\n  const newState = { ...state, clockMs: nowClock };\r\n  if (nextBand !== state.band) newState.bandSinceMs = nowClock;\r\n\r\n  // In‑range hold logic\r\n  let strictCandidate =\r\n    inputs.distanceM <= 15 &&\r\n    inputs.accuracyM <= STRICT_ACCURACY_M;\r\n  // tree fallback ONLY when GPS is too poor for the strict rule (accuracy > 15 m)\r\n  let treeCandidate =\r\n    inputs.distanceM <= TREE_DISTANCE_M &&\r\n    inputs.accuracyM > STRICT_ACCURACY_M &&\r\n    inputs.accuracyM <= TREE_ACCURACY_M &&\r\n    (inputs.speedMps ?? 0) < STILL_SPEED_MPS;\r\n\r\n  let holdMs = state.holdMs;\r\n  if (strictCandidate || treeCandidate) {\r\n    holdMs += dtMs;\r\n  } else {\r\n    holdMs = 0;\r\n  }\r\n\r\n  if (strictCandidate && holdMs >= STRICT_HOLD_MS) nextBand = 'inRange';\r\n  else if (treeCandidate && holdMs >= TREE_HOLD_MS) nextBand = 'inRange';\r\n  else if (state.band === 'inRange' && !(strictCandidate || treeCandidate))\r\n    nextBand = 'stalk';\r\n\r\n  newState.holdMs = holdMs;\r\n  newState.band = nextBand;\r\n\r\n  // Gain calculation\r\n  let gain = 0;\r\n  const tierMult = SPOOK_MULT[inputs.tier] ?? 1;\r\n  if (['alert', 'stalk', 'inRange'].includes(nextBand)) {\r\n    let cfg;\r\n    if (nextBand === 'alert') cfg = ALERT_GAIN;\r\n    else if (nextBand === 'stalk') cfg = STALK_GAIN;\r\n    else cfg = INRANGE_GAIN;\r\n\r\n    const speedTerm =\r\n      Math.max(0, (inputs.speedMps ?? 0) - cfg.speedFree) * cfg.perMps;\r\n    let dirMult;\r\n    if (inputs.approachDeg <= 60) dirMult = DIRECTION_MULT.front;\r\n    else if (inputs.approachDeg >= 120) dirMult = DIRECTION_MULT.rear;\r\n    else dirMult = DIRECTION_MULT.side;\r\n    dirMult = clamp(dirMult, 0.5, 1.5);\r\n\r\n    gain =\r\n      speedTerm *\r\n      tierMult *\r\n      (inputs.napping ? NAP_GAIN : 1) *\r\n      dirMult *\r\n      (nowClock - newState.bandSinceMs < WIGGLE_MS ? 0.5 : 1);\r\n  }\r\n\r\n  // Stomp and shake\r\n  const stomp = inputs.peakG >= STOMP_PEAK_G && inputs.distanceM < NEAR_STOMP_M\r\n    ? STOMP_GAIN\r\n    : 0;\r\n  const shake =\r\n    inputs.gyroRms > SHAKE_RMS && inputs.distanceM < NEAR_STOMP_M\r\n      ? SHAKE_PER_S * (dtMs / 1000)\r\n      : 0;\r\n\r\n  // Decay\r\n  const decayRate =\r\n    (inputs.speedMps ?? 0) < STILL_DECAY_SPEED &&\r\n    (inputs.cadenceHz ?? 0) < STILL_CADENCE_HZ\r\n      ? DECAY_STILL_PER_S\r\n      : DECAY_MOVING_PER_S;\r\n  const dtSec = dtMs / 1000;\r\n  const decay = decayRate * (inputs.napping ? NAP_DECAY : 1);\r\n\r\n  // Meter update\r\n  let newMeter =\r\n    clamp(\r\n      state.meter + gain * dtSec - decay * dtSec + (inputs.napping ? stomp * NAP_GAIN : stomp) + shake,\r\n      0,\r\n      100\r\n    );\r\n  newMeter = round2(newMeter);\r\n\r\n  // Flee logic\r\n  const fled = state.fled || newMeter >= 100;\r\n  if (fled) newMeter = 100;\r\n\r\n  newState.meter = newMeter;\r\n  newState.fled = fled;\r\n\r\n  // Event detection\r\n  let event = null;\r\n  const thresholds = [\r\n    { val: FIDGET_AT, name: 'fidget' },\r\n    { val: BOLT_AT, name: 'bolt' },\r\n    { val: FLEE_AT, name: 'flee' },\r\n  ];\r\n  for (const t of thresholds) {\r\n    if (state.meter < t.val && newMeter >= t.val) event = t.name;\r\n  }\r\n\r\n  return { state: newState, meter: newMeter, band: nextBand, fled, event, roadFrozen: false };\r\n}\r\n","modules/coach-world-rts/wild-ui.mjs":"// Wild hunt UI in the RTS HUD pixel style (same fonts + faction bezel as walk-hud / hud.css). One <style> per document; classes wh-card / wh-btn / wh-eye.\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nconst U=n=>new URL('./ui/'+n,import.meta.url).href;\r\nconst BEZEL={'occupied-worker':'bezel-occupied.png','seeded-warp':'bezel-warp.png','consumed-seed':'bezel-seed.png'};\r\nexport const styleFor=(faction)=>{const f=FACTION_BY_ID[faction]||FACTION_BY_ID.chest||{};return {color:f.color||'#88c6ae',bezel:U(BEZEL[f.policy]||BEZEL['occupied-worker'])};};\r\nexport const WILD_CSS=({color,bezel})=>`\r\n@font-face{font-family:'CW Pixelify';src:url(${U('pixelify-sans.woff2')}) format('woff2');font-weight:400 700;font-display:swap}\r\n@font-face{font-family:'CW m5x7';src:url(${U('m5x7.woff2')}) format('woff2');font-display:swap}\r\n.wh{--f:${color};font:20px 'CW m5x7',ui-monospace,monospace;color:#dfe8ee;image-rendering:pixelated;text-shadow:0 1px 0 #000}\r\n.wh-card{position:absolute;left:50%;transform:translateX(-50%);box-sizing:border-box;max-width:min(92vw,420px);border:14px solid transparent;border-image:url(${bezel}) 24 fill/14px stretch;padding:2px 6px;text-align:center;font:20px 'CW m5x7',ui-monospace,monospace;color:#dfe8ee;pointer-events:none}\r\n.wh-card.row{display:flex;gap:10px;align-items:center;text-align:left}\r\n.wh-big{font:400 28px 'CW m5x7',ui-monospace,monospace}\r\n.wh-sub{font:16px 'CW m5x7',ui-monospace,monospace;color:var(--f);opacity:.95}\r\n.wh-btn{pointer-events:auto;font:400 18px 'CW Pixelify',ui-monospace,monospace;color:#dfe8ee;background:none;min-width:96px;min-height:52px;border:14px solid transparent;border-image:url(${bezel}) 24 fill/14px stretch;image-rendering:pixelated;padding:0 4px;cursor:pointer;text-shadow:0 1px 0 #000}\r\n.wh-btn:active{filter:brightness(1.4)}\r\n.wh-btn.go{color:#ffd21f}\r\n.wh-eye{width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;image-rendering:pixelated;border:3px solid #0d0d14;box-shadow:0 0 0 2px var(--f)}\r\n.wh-eye i{width:22px;height:12px;background:#fff;border:3px solid #0d0d14;border-radius:50%;position:relative;display:block}\r\n.wh-eye i:after{content:'';position:absolute;left:5px;top:0;width:6px;height:6px;background:#0d0d14}\r\n.wh-arrow{width:0;height:0;border-left:11px solid transparent;border-right:11px solid transparent;border-bottom:26px solid var(--f);filter:drop-shadow(0 2px 0 #000);transition:transform .2s}\r\n`;\r\nexport function injectWildStyle(doc,faction){\r\n const k=styleFor(faction);let s=doc.getElementById?.('wh-style');\r\n if(!s){s=doc.createElement('style');s.id='wh-style';(doc.head||doc.documentElement).append(s);}\r\n s.textContent=WILD_CSS(k);return k;\r\n}\r\nexport const px=(doc,tag,cls,text)=>{const e=doc.createElement(tag);e.className=cls;if(text!=null)e.textContent=text;return e;};\r\n","modules/coach-world-rts/wind-streaks.mjs":"// V5 desert wind streaks (Daggerfall look). Local gpt-oss-20b card (D:/outputs/daggerfall-1019/v5/card.mjs E), validated (contract,\r\n// headless WebGL compile + render, 1 draw, hidden at strength 0) and reviewed (mod period fix). Structure from DFO Online windWisps.js (MIT).\r\n// createWindStreaks(T,{count,box,height,length,color,seed,opacity}) -> {object,update(t,centre),setWind(x,z,strength),dispose(),stats()}\r\n// ES module – no imports, only the exported function\r\nexport function createWindStreaks(T, opts = {}) {\r\n  // Default options\r\n  const {\r\n    count   = 60,\r\n    box     = 120,\r\n    height  = 6,\r\n    length  = 7,\r\n    color   = [0.95, 0.85, 0.65],\r\n    seed    = 1,\r\n    opacity = 0.5\r\n  } = opts;\r\n\r\n  // Simple deterministic PRNG (xorshift32)\r\n  function prng(seed) {\r\n    let x = seed;\r\n    return () => {\r\n      x ^= x << 13;\r\n      x ^= x >> 17;\r\n      x ^= x << 5;\r\n      return (x >>> 0) / 4294967296; // [0,1)\r\n    };\r\n  }\r\n  const rand = prng(seed);\r\n\r\n  // Geometry: two vertices per streak\r\n  const positions = new Float32Array(count * 2 * 3);\r\n  const aEnd      = new Float32Array(count * 2);   // 0 for tail, 1 for head\r\n  const aSeed     = new Float32Array(count * 2);   // same seed per streak\r\n\r\n  let pIdx = 0;\r\n  for (let i = 0; i < count; ++i) {\r\n    const s = rand();          // seed for this streak\r\n    const x = rand() * box;\r\n    const z = rand() * box;\r\n    const y = 0.3 + rand() * (height - 0.3);\r\n\r\n    // Tail vertex\r\n    positions[pIdx++] = x; positions[pIdx++] = y; positions[pIdx++] = z;\r\n    aEnd[i*2]   = 0.0;\r\n    aSeed[i*2]  = s;\r\n\r\n    // Head vertex\r\n    positions[pIdx++] = x; positions[pIdx++] = y; positions[pIdx++] = z;\r\n    aEnd[i*2+1] = 1.0;\r\n    aSeed[i*2+1] = s;\r\n  }\r\n\r\n  const geometry = new T.BufferGeometry();\r\n  geometry.setAttribute('position', new T.Float32BufferAttribute(positions, 3));\r\n  geometry.setAttribute('aEnd',   new T.Float32BufferAttribute(aEnd, 1));\r\n  geometry.setAttribute('aSeed',  new T.Float32BufferAttribute(aSeed, 1));\r\n\r\n  // Uniforms\r\n  const uniforms = {\r\n    uTime:     { value: 0 },\r\n    uCentre:   { value: new T.Vector3() },\r\n    uWind:     { value: new T.Vector2(0, 0) },\r\n    uSpeed:    { value: 14.0 },\r\n    uLength:   { value: length },\r\n    uColor:    { value: new T.Color(...color) },\r\n    uOpacity:  { value: opacity },\r\n    uStrength: { value: 0 },\r\n    box:       { value: box }\r\n  };\r\n\r\n  // Vertex shader\r\n  const vs = `\r\n    attribute float aEnd;\r\n    attribute float aSeed;\r\n\r\n    uniform vec3 uCentre;\r\n    uniform vec2 uWind;\r\n    uniform float uSpeed;\r\n    uniform float uLength;\r\n    uniform float uTime;\r\n    uniform float box;\r\n\r\n    varying float vLife;\r\n    varying float vTail;\r\n    varying float edgeFade;\r\n\r\n    void main() {\r\n      // Direction of wind\r\n      vec3 dir = normalize(vec3(uWind.x, 0.0, uWind.y) + vec3(1e-5));\r\n\r\n      // Base position (modulo box on XZ)\r\n      vec3 p = mod(position.xyz + dir * uSpeed * uTime * (0.7 + 0.6 * aSeed) - uCentre + vec3(box*0.5, 0.0, box*0.5), vec3(box, 1.0, box)) // review fix: y period 1 not 0 (mod by 0 is undefined; y is overwritten below)\r\n               + uCentre - vec3(box*0.5, 0.0, box*0.5);\r\n\r\n      // Y stays at centre.y + original y\r\n      p.y = uCentre.y + position.y;\r\n\r\n      // Add length offset for head/tail\r\n      p += dir * uLength * aEnd;\r\n\r\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);\r\n\r\n      // Life cycle\r\n      vLife = sin(fract(uTime*0.25 + aSeed*5.0)*3.141592653589793);\r\n      vTail = aEnd;\r\n\r\n      // Edge fade (distance from centre in XZ)\r\n      float dx = abs(p.x - uCentre.x);\r\n      float dz = abs(p.z - uCentre.z);\r\n      float maxDist = max(dx, dz);\r\n      edgeFade = 1.0 - smoothstep(0.35*box, 0.5*box, maxDist);\r\n    }\r\n  `;\r\n\r\n  // Fragment shader\r\n  const fs = `\r\n    uniform vec3 uColor;\r\n    uniform float uOpacity;\r\n    uniform float uStrength;\r\n\r\n    varying float vLife;\r\n    varying float vTail;\r\n    varying float edgeFade;\r\n\r\n    void main() {\r\n      float strength = mix(0.15, 1.0, vTail);\r\n      gl_FragColor = vec4(uColor, uOpacity * uStrength * vLife * edgeFade * strength);\r\n    }\r\n  `;\r\n\r\n  const material = new T.ShaderMaterial({\r\n    uniforms,\r\n    vertexShader: vs,\r\n    fragmentShader: fs,\r\n    transparent: true,\r\n    depthWrite: false,\r\n    fog: false\r\n  });\r\n\r\n  const lines = new T.LineSegments(geometry, material);\r\n  lines.frustumCulled = false;\r\n  lines.name = 'wind-streaks';\r\n\r\n  // Helper to set wind direction and strength\r\n  function setWind(x, z, strength) {\r\n    uniforms.uWind.value.set(x, z);\r\n    uniforms.uStrength.value = strength;\r\n    lines.visible = strength > 0.01;\r\n  }\r\n\r\n  return {\r\n    object: lines,\r\n    update(timeSeconds, centreVector3) {\r\n      uniforms.uTime.value = timeSeconds;\r\n      uniforms.uCentre.value.copy(centreVector3);\r\n    },\r\n    setWind,\r\n    dispose() {\r\n      geometry.dispose();\r\n      material.dispose();\r\n    },\r\n    stats() {\r\n      return { calls: lines.visible ? 1 : 0, count };\r\n    }\r\n  };\r\n}\r\n","modules/coach-world-rts/world-layer.mjs":"// R58 world layer glue: real Vegas around the base. Mounted by base3d.mjs (addWorldLayer / optional import).\r\n// createWorldLayer(T,{center,scale,resolution,getBase}) -> {group,setFade,update,dispose,ready}. Sync return; content streams in cell by cell.\r\n// Frame: metres about the home origin (+x east, +z south, +y up); the root sits at the base centre and the terrain/roads groups carry `scale`.\r\n// Home = base.home (on-device only) > coarse GPS fix when permission is already granted > Las Vegas Strip. Outside the bake bounds -> Strip.\r\nimport {createWorldTiles,ringCells,toLocal,WORLD_CELL_Z} from '../coach-world/world-tiles.mjs';\r\nimport {CATS,ROAD_CLASSES} from '../coach-world/world-tiles.worker.mjs';\r\nimport {createWorldTerrain} from './world-terrain.mjs';\r\nimport {createWorldRoads} from './world-roads.mjs';\r\nimport * as Traffic from './world-traffic.mjs'; // namespace: the traffic lane is still changing its exports\r\nimport {createMomKit} from './mom-kit.mjs';\r\nimport {createRuinsKit} from './ruins-kit.mjs';\r\nimport {createHubs,loadHubs,localHubs} from './hubs-kit.mjs';\r\nimport {addFade,addCompress,createPs1Material} from './ps1-material.mjs';\r\nimport {heightCap,compress as remap,D0,DH} from './ar-scale.mjs';\r\nimport {BIOMES,shapeGeometry} from './biomes.mjs';\r\nimport {createWildForest,planForestLod,MACRO_M} from './wild-forest.mjs';\r\nimport {wildFlag} from './wild-contract.mjs';\r\nimport {createFogBanks} from './fog-banks.mjs';\r\nimport {createFarRing,ringTiles} from './far-ring.mjs';\r\nimport {createGroundDetail} from './ground-tiles.mjs';\r\nimport {createHerds} from './herds.mjs';\r\nimport {createChangeLedger} from './change-ledger.mjs';\r\n\r\nexport const STRIP={lat:36.1147,lng:-115.1728};\r\nexport const BOUNDS=[-115.45,35.95,-114.95,36.35]; // manifest.json bounds: west,south,east,north\r\n// Personal habitat (Ian): no roads/buildings/ruins inside R_RING (120 m, so no road fragments under the biome ground); faction-biome relief, ground, plants and rocks fill it, fading out 70->120 m into the city.\r\n// Tall MOM buildings near home are capped (30 m at 120 m -> 60 m at 250 m) so nothing looms over the base at mid zoom; the full skyline stands farther out.\r\nexport const AR_HUBS=4;\r\n// R81 AR budget (<= 125k tris): past D0 the compressed city squeezes into the last metres before the 120 m horizon, so AR keeps fewer plants/rocks and the nearest 250 buildings\r\nexport const AR_HABITAT={plants:300,rocks:120},AR_BUILDINGS=250,AR_ROAD_M=700; // roads past 700 m real are < 2 m wide slivers on the horizon\r\nexport const BEACON_DIST=30*1.6/.3,R_CLEAR=24,R_RING=120,SKY_CAP=[30,60,120,250],MAX_BUILDINGS=500,MAX_RUINS=6,MAX_SHIPS=10,GRID=65,BASE_RECT={x0:-30,z0:-30,x1:30,z1:30,feather:40};\r\n// V1 load veil: pipeline stages (tiles, DEM, terrain, roads, traffic, hubs, forest, buildings+ruins) and the ease rate (1/s)\r\nexport const VEIL_STAGES=8,VEIL_RATE=2.5;\r\n// V2 Fog of Change (base view only): every ring cell loads under a bank that lifts nearest-first when its data lands; a missing tile keeps\r\n// its bank; a cell or your base that changed since your last visit is banked until you scout it (view focus inside it, camera within\r\n// SCOUT_M, for SCOUT_S seconds); the base bank lifts by itself (its new data is right there). fogChange/clearFog take zone flips, drops, scouts.\r\n// V6 far ring: base view z10 (124 m/px) x 32 px ~ 4 km, past the fog at full zoom-out; flight z9 (248 m/px) x 100 px ~ 25 km, out to the\r\n// ridges round the valley (Frenchman, Sunrise, Black Mountain), hazed by the 14-24 km flight rim. DEM tiles retry on DFO's streamer backoff (2..32 s) without ever blocking `ready`; the ring rebuilds when a late tile lands.\r\nexport const RING={base:{z:10,radiusPx:32},flight:{z:9,radiusPx:100}},RING_RETRY_S=[2,4,8,16,32];\r\nexport const SCOUT_M=700,SCOUT_S=1.5,BASE_BANK_R=60,LIFT_STAGGER=.25,FOG_BANK_COLOR=[.58,.63,.68];\r\nexport const cellSignature=m=>m.missing?'missing':`${m.buildings?.height?.length??0}:${m.roads?.cls?.length??0}:${m.landuse?.cls?.length??0}`; // decoded cells are columnar\r\nexport const baseSignature=b=>Object.values(b?.buildings||{}).map(x=>`${x.id}:${x.x}:${x.y}:${x.level}`).sort().join('|');\r\nconst fogStore=()=>{try{return globalThis.localStorage;}catch{return undefined;}};\r\nconst RAD=Math.PI/180,R=6378137,M_EQ=40075016.686,DEM_Z=14;\r\nconst inBounds=(lat,lng)=>lng>=BOUNDS[0]&&lng<=BOUNDS[2]&&lat>=BOUNDS[1]&&lat<=BOUNDS[3];\r\n\r\n// base.home / GPS / Strip. getFix() -> Promise<{lat,lng}|null>.\r\nexport async function pickHome(base,getFix=coarseFix){\r\n const h=base?.home;\r\n if(h&&Number.isFinite(h.latitude)&&Number.isFinite(h.longitude)&&inBounds(h.latitude,h.longitude))return {lat:h.latitude,lng:h.longitude,from:'home'};\r\n try{const f=await getFix();if(f&&inBounds(f.lat,f.lng))return {...f,from:'gps'};}catch{}\r\n return {...STRIP,from:'strip'};\r\n}\r\n// Never prompts: only reads a fix when location permission is already granted (a prompt on the Base tab would be a surprise).\r\nexport async function coarseFix(){\r\n const nav=globalThis.navigator;if(!nav?.geolocation)return null;\r\n try{const p=await nav.permissions?.query({name:'geolocation'});if(p?.state!=='granted')return null;}catch{return null;}\r\n return new Promise(res=>nav.geolocation.getCurrentPosition(p=>res({lat:p.coords.latitude,lng:p.coords.longitude}),()=>res(null),{enableHighAccuracy:false,timeout:3000,maximumAge:600000}));\r\n}\r\n\r\n// cell frame in metres about origin (same maths as the tile worker's frame())\r\nexport function cellFrame(cell,o){\r\n const n=2**cell.z,lng=cell.x/n*360-180,lat=Math.atan(Math.sinh(Math.PI*(1-2*cell.y/n)))/RAD,p=toLocal(o,lat,lng);\r\n return {x0:p.x,z0:p.z,s:M_EQ*Math.cos(o.lat*RAD)/n};\r\n}\r\n// local metres -> terrarium z14 pixel coords (world pixels)\r\nexport function demPixel(o,x,z){\r\n const lat=o.lat-z/(R*RAD),lng=o.lng+x/(R*RAD*Math.cos(o.lat*RAD)),n=2**DEM_Z*256,s=Math.sin(lat*RAD);\r\n return {px:(lng+180)/360*n,py:(0.5-Math.log((1+s)/(1-s))/(4*Math.PI))*n};\r\n}\r\n// bilinear sample of decoded DEM tiles. tiles: Map \"tx,ty\" -> Float32Array(256*256) metres\r\nexport function demAt(tiles,o,x,z){\r\n // V6: a sample in a tile that is not loaded is NaN, not 0 m (0 m dropped the outer cell edges ~700 m: the \"cube walls\" under the world).\r\n // The bilinear renormalises over the samples it has; no data at all = NaN (callers keep the base level).\r\n const {px,py}=demPixel(o,x,z),at=(ix,iy)=>{const t=tiles.get(Math.floor(ix/256)+','+Math.floor(iy/256));return t?t[(((iy%256)+256)%256)*256+(((ix%256)+256)%256)]:NaN;};\r\n const fx=px-.5,fy=py-.5,x0=Math.floor(fx),y0=Math.floor(fy),tx=fx-x0,ty=fy-y0;let sum=0,w=0;\r\n for(const [ix,iy,k] of [[x0,y0,(1-tx)*(1-ty)],[x0+1,y0,tx*(1-ty)],[x0,y0+1,(1-tx)*ty],[x0+1,y0+1,tx*ty]]){const v=at(ix,iy);if(Number.isFinite(v)){sum+=v*k;w+=k;}}\r\n return w>1e-9?sum/w:NaN;\r\n}\r\nexport const terrariumToMetres=(rgba,out=new Float32Array(rgba.length/4))=>{for(let i=0;i<out.length;i++)out[i]=rgba[i*4]*256+rgba[i*4+1]+rgba[i*4+2]/256-32768;return out;};\r\n\r\n// clip road polylines to outside the habitat circle: densify to <= 6 m, drop points inside, split into runs\r\nexport function clipRoads(roads,r=R_RING,rMax=Infinity){\r\n const out=[];\r\n for(const road of roads){const pts=[];\r\n  for(let i=0;i<road.pts.length-1;i++){const [ax,az]=road.pts[i],[bx,bz]=road.pts[i+1],n=Math.max(1,Math.ceil(Math.hypot(bx-ax,bz-az)/6));for(let k=0;k<n;k++)pts.push([ax+(bx-ax)*k/n,az+(bz-az)*k/n]);}\r\n  pts.push(road.pts[road.pts.length-1]);\r\n  let run=[];const flush=()=>{if(run.length>1)out.push({...road,pts:run});run=[];};\r\n  for(const p of pts){const d=Math.hypot(p[0],p[1]);if(d<r||d>rMax)flush();else run.push(p);}\r\n  flush();}\r\n return out;\r\n}\r\nexport const capHeight=(b)=>{const d=Math.hypot(b.x,b.z);return d>=SKY_CAP[3]?b.h:Math.min(b.h,SKY_CAP[0]+(d-SKY_CAP[2])/(SKY_CAP[3]-SKY_CAP[2])*(SKY_CAP[1]-SKY_CAP[0]));};\r\n// R81 AR (compress on): heights capped by the rendered distance instead (ar-scale heightCap); capHeight stays for the base view.\r\nexport const capFor=compress=>compress?b=>Math.min(b.h,heightCap(Math.hypot(b.x,b.z))):capHeight;\r\nexport const outsideHabitat=(b,r=R_RING)=>Math.hypot(b.x,b.z)-Math.max(b.w||0,b.d||0)/2>=r;\r\n// decoded cell roads -> world-roads input [{class,pts:[[x,z]..],bridge,tunnel}]; rail dropped, unknown -> service\r\nexport function roadsOf(m){\r\n const r=m.roads,out=[];\r\n for(let i=0;i<r.cls.length;i++){const name=ROAD_CLASSES[r.cls[i]];if(name==='rail')continue;const pts=[];\r\n  for(let k=r.start[i];k<r.start[i+1];k++)pts.push([r.pts[k*2],r.pts[k*2+1]]);\r\n  if(pts.length>1)out.push({class:name==='other'?'service':name,pts,bridge:!!r.bridge[i],tunnel:!!r.tunnel[i]});}\r\n return out;\r\n}\r\n// decoded cell buildings -> mom-kit input [{id,x,z,w,d,h,cat,rot}] (axis-aligned bbox of each prism)\r\nexport function buildingsOf(m,cellId=''){\r\n const b=m.buildings,out=[];\r\n for(let i=0;i<b.height.length;i++){let x0=1e9,x1=-1e9,z0=1e9,z1=-1e9;\r\n  for(let k=b.start[i];k<b.start[i+1];k++){const v=b.idx[k]*3,x=b.pos[v],z=b.pos[v+2];if(x<x0)x0=x;if(x>x1)x1=x;if(z<z0)z0=z;if(z>z1)z1=z;}\r\n  if(x1<x0)continue;out.push({id:cellId+':'+i,x:(x0+x1)/2,z:(z0+z1)/2,w:x1-x0,d:z1-z0,h:b.height[i],cat:CATS[b.cat[i]]||'other',rot:0});}\r\n return out;\r\n}\r\n\r\nasync function decodePng(bytes){\r\n const bmp=await createImageBitmap(new Blob([bytes],{type:'image/png'})),c=typeof OffscreenCanvas!=='undefined'?new OffscreenCanvas(256,256):Object.assign(document.createElement('canvas'),{width:256,height:256}),g=c.getContext('2d',{willReadFrequently:true});\r\n g.drawImage(bmp,0,0);return terrariumToMetres(g.getImageData(0,0,256,256).data);\r\n}\r\nconst tick=()=>new Promise(r=>setTimeout(r,0));\r\n\r\n// faction biome ground disc (alpha fades 70 -> 120 m, so it blends into the city) + scattered biome plants/rocks. y follows the terrain.\r\nexport function createHabitat(T,{faction='chest',heightAt=()=>0,scale=1,resolution=[320,180],seed=5,plants=800,rocks=200}={}){\r\n const bio=BIOMES[faction]||BIOMES.chest,group=new T.Group();group.name='habitat';group.scale.setScalar(scale);\r\n const sm=(a,b,x)=>{const t=Math.min(1,Math.max(0,(x-a)/(b-a)));return t*t*(3-2*t);},rel=(x,z)=>{const r=Math.hypot(x,z);return 2.6*sm(20,50,r)*(1-sm(85,120,r))*(.5+.5*Math.sin(x*.045+Math.cos(z*.06)*2)*Math.cos(z*.05+Math.sin(x*.03)*1.7));},\r\n  ring=new T.RingGeometry(.01,R_RING,96,20),P=ring.attributes.position,col=new Float32Array(P.count*4),g=new T.Color(bio.ground);\r\n for(let i=0;i<P.count;i++){const x=P.getX(i),z=-P.getY(i),r=Math.hypot(x,z),t=Math.min(1,Math.max(0,(r-70)/(R_RING-70)));\r\n  P.setXYZ(i,x,heightAt(x,z)+.06+rel(x,z),z);const k=.85+.15*Math.sin(x*.21)*Math.cos(z*.17);col.set([g.r*k,g.g*k,g.b*k,1-t*t*(3-2*t)],i*4);}\r\n ring.setAttribute('color',new T.BufferAttribute(col,4));ring.computeVertexNormals();\r\n const gm=createPs1Material(T,{vertexColors:true,resolution});gm.transparent=true;gm.depthWrite=false;gm.side=T.DoubleSide;gm.polygonOffset=true;gm.polygonOffsetFactor=-2;gm.polygonOffsetUnits=-2;\r\n const mesh=new T.Mesh(ring,gm);mesh.renderOrder=1;group.add(mesh);\r\n let s=seed;const rnd=()=>(s=(s*1664525+1013904223)>>>0)/4294967296,parts=[ring,gm];\r\n const scatter=(kind,def,n,lo,hi)=>{\r\n  const geo=shapeGeometry(T,def.shape,1),m=createPs1Material(T,{vertexColors:false,resolution}),im=new T.InstancedMesh(geo,m,n),M=new T.Matrix4(),c=new T.Color(def.color||bio.ground);let k=0;\r\n  for(let tries=0;k<n&&tries<n*8;tries++){const a=rnd()*Math.PI*2,r=14+Math.sqrt(rnd())*(R_RING-14-6),keep=1-Math.min(1,Math.max(0,(r-70)/(R_RING-70)));if(rnd()>keep)continue;\r\n   const x=Math.cos(a)*r,z=Math.sin(a)*r,sc=lo+rnd()*(hi-lo);M.compose(new T.Vector3(x,heightAt(x,z)+.06+rel(x,z),z),new T.Quaternion().setFromAxisAngle(new T.Vector3(0,1,0),rnd()*6.28),new T.Vector3(sc,sc*(kind==='plant'?1+rnd()*.8:.6+rnd()*.5),sc));\r\n   im.setMatrixAt(k,M);im.setColorAt(k,c.clone().offsetHSL((rnd()-.5)*.06,.05,(rnd()-.5)*.1));k++;}\r\n  im.count=k;im.instanceMatrix.needsUpdate=true;if(im.instanceColor)im.instanceColor.needsUpdate=true;im.frustumCulled=false;group.add(im);parts.push(geo,m);};\r\n scatter(\"plant\",bio.plant,plants,1.6,4.2);scatter(\"rock\",bio.rock,rocks,1,3);\r\n // Home beacon: a tall faction-coloured spire with a glowing crown, so the base is findable from macro zoom. Constant on-screen size: height follows the camera distance; hidden below zoom ~.3.\r\n const bc=new T.Color(bio.plant.color),bm=createPs1Material(T,{vertexColors:false,resolution}),bg=new T.ConeGeometry(1,1,6),cg=new T.OctahedronGeometry(1,0),beacon=new T.Group(),spire=new T.Mesh(bg,bm),crown=new T.Mesh(cg,bm);\r\n bm.color=bc.clone();bm.emissive=bc.clone().multiplyScalar(.9);bg.translate(0,.5,0);spire.scale.set(2.2,1,2.2);beacon.add(spire,crown);beacon.visible=false;beacon.name='home-beacon';group.add(beacon);parts.push(bg,cg,bm);\r\n return {group,beacon,update(t,d=0){const on=d>BEACON_DIST;beacon.visible=on;if(!on)return;const h=Math.min(140,d/scale*.11);spire.scale.set(h*.09,h,h*.09);crown.scale.setScalar(h*.1);crown.position.y=h;},dispose(){group.removeFromParent();parts.forEach(p=>p.dispose());}};\r\n}\r\n\r\n// compress: AR only (walkthrough). Every world material gets the radial distance remap (ar-scale compress, uniforms uCwD0/uCwDH, live via setCompress).\r\nexport function createWorldLayer(T,{center={x:0,z:0},scale=1.6,resolution=[320,180],getBase=()=>null,deps={},compress=false,flight=false}={}){\r\n const root=new T.Group();root.name='world-layer';root.position.set(center.x,-.3,center.z);\r\n const parts=[],forests=[],mats=new Set();let fade=0,dirty=false,dead=false,tiles=null,terrain=null,owed=VEIL_STAGES,veil=1,veilAt=0;\r\n const landed=()=>{owed=Math.max(0,owed-1);}; // V1 load veil: one stage of the pipeline landed (fog recedes a step)\r\n const add=(p)=>{if(dead){p.dispose?.();return;}parts.push(p);root.add(p.group);dirty=true;};\r\n// ponytail: custom ShaderMaterials (ruins fog, traffic glow) switch at fade .35 instead of dissolving\r\n const cu={uCwD0:{value:D0},uCwDH:{value:DH}},culls=[],bx=new T.Box3(),cw=new T.Vector3(),cm=new T.Matrix4();\r\n // Compressed objects draw nearer than their CPU bounds. Each frame every one gets a proxy bounding sphere around its remapped footprint (three's frustum\r\n // test reads object.boundingSphere first): world AABB -> ground radius rxz at distance L -> rendered radial span f(L-rxz)..f(L+rxz) and the bearing\r\n // spread, plus the true vertical half-height. The remap is a radial contraction, so the drawn object stays inside. Sprites are just never culled.\r\n const recull=cam=>{const d0=cu.uCwD0.value,dh=cu.uCwDH.value,cx=cam.position.x,cz=cam.position.z;\r\n  for(const o of culls){const u=o.userData;let p=o.boundingSphere;\r\n   if(!u.cwBox||!p||p!==u.cwProxy||!p.center.equals(u.cwAt)||p.radius!==u.cwR){ // first time, or the owner recomputed its bounds\r\n    if(o.isInstancedMesh){o.computeBoundingBox();u.cwBox=o.boundingBox.clone();}else{if(!o.geometry.boundingBox)o.geometry.computeBoundingBox();u.cwBox=o.geometry.boundingBox.clone();}\r\n    p=o.boundingSphere=u.cwProxy=new T.Sphere();u.cwAt=new T.Vector3();}\r\n   if(u.cwBox.isEmpty()){p.center.set(0,0,0);p.radius=0;}else{\r\n    bx.copy(u.cwBox).applyMatrix4(o.matrixWorld);bx.getCenter(cw);\r\n    const rx=(bx.max.x-bx.min.x)/2,rz=(bx.max.z-bx.min.z)/2,hy=(bx.max.y-bx.min.y)/2,rxz=Math.hypot(rx,rz),vx=cw.x-cx,vz=cw.z-cz,L=Math.hypot(vx,vz);\r\n    let R;\r\n    if(L<=rxz){R=Math.hypot(remap(L+rxz,d0,dh),hy);cw.x=cx;cw.z=cz;} // footprint around the viewer: everything within f(L+rxz)\r\n    else{const lo=remap(L-rxz,d0,dh),hi=remap(L+rxz,d0,dh),m=(lo+hi)/2,b=hi*rxz/L,a=Math.max(hi-m,m-lo*Math.sqrt(1-(rxz/L)**2));R=Math.hypot(a,b,hy);cw.x=cx+vx/L*m;cw.z=cz+vz/L*m;}\r\n    const sc=o.matrixWorld.getMaxScaleOnAxis()||1;p.center.copy(cw).applyMatrix4(cm.copy(o.matrixWorld).invert());p.radius=R/sc;}\r\n   u.cwAt.copy(p.center);u.cwR=p.radius;}};\r\n const apply=()=>{dirty=false;root.traverse(o=>{const m=o.material;for(const x of Array.isArray(m)?m:m?[m]:[]){if(!x.isShaderMaterial){addFade(x).setFade(fade);mats.add(x);}else x.visible=fade>.35;\r\n   if(compress){if(!addCompress(x,cu))x.visible=false;else if(!o.userData.cwCull){o.userData.cwCull=1;if(o.isSprite)o.frustumCulled=false;else if(o.geometry)culls.push(o);}}}});}; // unknown shaders hide\r\n // V2 Fog of Change (banks are never pickable: three's raycaster ignores visible and the cards sit at identity instance matrices): banks live in metres (scaled like every other part); kinds: load | missing | change | base | api\r\n const fogOn=!flight&&!compress,banks=fogOn?createFogBanks(T,{max:64,color:FOG_BANK_COLOR,seed:11,fadeNear:2500,fadeFar:9000}):null,fog=new Map(),scoutAt=new Map();\r\n const ledger=fogOn?(deps.ledger||createChangeLedger({storage:fogStore(),owner:String(getBase()?.seed??'guest')})):null;let fogT=0;\r\n const fogSync=()=>banks?.setBanks([...fog.entries()].map(([id,b])=>({id,...b})));\r\n const fogPut=(id,b)=>{if(!banks)return;fog.set(id,b);fogSync();},fogDrop=id=>{if(fog.delete(id))fogSync();};\r\n if(banks){const g=new T.Group();g.name='fog-of-change';g.scale.setScalar(scale);banks.object.raycast=()=>{};g.add(banks.object);add({group:g,update:t=>banks.update(t),dispose:()=>banks.dispose()});\r\n  const sig=baseSignature(getBase()),seen=ledger.check('base',sig);if(seen==='changed')fogPut('base',{x:0,y:4,z:0,r:BASE_BANK_R,kind:'base',liftIn:2.5,sig,ledgerId:'base'});else ledger.see('base',sig);}\r\n let ground=null; // V7 ground detail (tiles, flora, tufts)\r\n let ringResolve,ring=null;const ringReady=compress?Promise.resolve(null):new Promise(r=>{ringResolve=r;});\r\n async function loadRing(origin,h0,hole){ // V6: never throws, never blocks; resolves ringReady with the first ring built (or null)\r\n  const spec=flight?RING.flight:RING.base,want=ringTiles(origin,spec.z,spec.radiusPx),got=new Map(),build=()=>{if(dead||!got.size)return;\r\n   const r=createFarRing(T,{material:terrain.material,dem:got,origin,z:spec.z,radiusPx:spec.radiusPx,hole,h0,exaggeration:terrain.exaggeration,under:terrain.heightAtM});\r\n   r.group.scale.setScalar(scale);if(ring){parts.splice(parts.indexOf(ring),1);ring.dispose();}ring=r;add(r);ringResolve?.(r);};\r\n  const fetchTile=async([tx,ty])=>{try{got.set(tx+','+ty,await (deps.decodeDem||decodePng)(new Uint8Array(await tiles.loadDem(tx,ty,spec.z))));return true;}catch{return false;}};\r\n  const failed=(await Promise.all(want.map(async t=>(await fetchTile(t))?null:t))).filter(Boolean);if(dead)return ringResolve?.(null);build();if(!got.size&&!failed.length)ringResolve?.(null);\r\n  for(const t of failed)(async()=>{for(const s of RING_RETRY_S){await new Promise(r=>setTimeout(r,(deps.ringRetryScale??1000)*s));if(dead)return;if(await fetchTile(t)){build();return;}}if(!ring)ringResolve?.(null);})();\r\n }\r\n const ready=(async()=>{\r\n  const home=await pickHome(getBase(),deps.getFix);if(dead)return null;const origin={lat:home.lat,lng:home.lng},cells=ringCells(origin.lat,origin.lng,WORLD_CELL_Z);\r\n  const cellFrames=cells.map(c=>cellFrame(c,origin));if(banks)cellFrames.forEach((f,i)=>fogPut('load:'+cells[i].x+','+cells[i].y,{x:f.x0+f.s/2,y:0,z:f.z0+f.s/2,r:f.s/2,kind:'load'}));\r\n  tiles=(deps.createTiles||createWorldTiles)({origin});\r\n  const decoded=await tiles.load(cells,origin);if(dead)return null;if(flight&&decoded.some(m=>m.missing))throw new Error('Flight map ring is incomplete.');landed();await tick();\r\n  // DEM: every z14 tile the ring touches\r\n  const frames=cells.map(c=>cellFrame(c,origin)),need=new Set();\r\n  frames.forEach(f=>{for(const [x,z] of [[f.x0,f.z0],[f.x0+f.s,f.z0],[f.x0,f.z0+f.s],[f.x0+f.s,f.z0+f.s]]){const {px,py}=demPixel(origin,x,z);need.add(Math.floor(px/256)+','+Math.floor(py/256));}});\r\n  const dem=new Map();\r\n  await Promise.all([...need].map(async k=>{const [tx,ty]=k.split(',').map(Number);try{dem.set(k,await (deps.decodeDem||decodePng)(new Uint8Array(await tiles.loadDem(tx,ty,DEM_Z))));}catch{}}));\r\n  if(dead)return null;if(flight&&dem.size!==need.size)throw new Error('Flight elevation coverage is incomplete.');landed();\r\n  const h0=dem.size?(demAt(dem,origin,0,0)||0):0,tcells=frames.map((f,i)=>{const step=f.s/(GRID-1),heights=new Float32Array(GRID*GRID);\r\n   for(let gz=0;gz<GRID;gz++)for(let gx=0;gx<GRID;gx++){const e=dem.size?demAt(dem,origin,f.x0+gx*step,f.z0+gz*step)-h0:0;heights[gz*GRID+gx]=Number.isFinite(e)?e:0;}\r\n   return {id:cells[i].x+','+cells[i].y,minX:f.x0,minZ:f.z0,size:f.s,grid:GRID,heights};});\r\n  terrain=createWorldTerrain(T,{cells:tcells,scale,exaggeration:1.5,flatten:[BASE_RECT],resolution,nearRadius:120,farError:4});\r\n  // V7: patch the ground material BEFORE the terrain is first drawn, so the big program compiles once (a recompile is seconds in SwiftShader)\r\n  if(!compress)try{ground=createGroundDetail(T,{terrain,cells:tcells,decoded,seed:(origin.lat*1e4^origin.lng*1e4)|0,resolution});}catch(e){console.warn('coach-world ground detail',e);}\r\n  add(terrain);if(ground)add(ground);\r\n  // V8: herds graze the open ground of the base view (flight is too high to see them, AR keeps its budget); they flee the view focus\r\n  if(ground&&!flight)try{const h=createHerds(T,{grid:ground.grid,heightAt:terrain.heightAtM,seed:(origin.lat*1e5^origin.lng*1e5)|0,scale});\r\n   if(h)add({group:h.group,herds:h,update:(ts,d,cam)=>h.update(ts,{x:ground.focus.x,z:ground.focus.y},cam,d/scale),dispose:()=>h.dispose()});}catch(e){console.warn('coach-world herds',e);}\r\n  landed();await tick();\r\n  if(!compress)void loadRing(origin,h0,{x0:Math.min(...tcells.map(c=>c.minX)),z0:Math.min(...tcells.map(c=>c.minZ)),x1:Math.max(...tcells.map(c=>c.minX+c.size)),z1:Math.max(...tcells.map(c=>c.minZ+c.size))});\r\n  const faction=getBase()?.faction||'chest';if(!flight)add(createHabitat(T,{faction,heightAt:terrain.heightAtM,scale,resolution,...(compress?AR_HABITAT:{})}));\r\n  const roads=clipRoads(decoded.flatMap(m=>m.missing?[]:roadsOf(m)).filter(r=>r.class!=='footway'&&r.class!=='path'),flight?0:R_RING,compress?AR_ROAD_M:Infinity);\r\n  const rd=createWorldRoads(T,{roads,heightAt:terrain.heightAtM,scale,cellOf:terrain.cellIdAt,noise:terrain.noise,resolution});add(rd);landed();await tick();\r\n  try{const models=Traffic.loadTrafficModels?await Traffic.loadTrafficModels(T):undefined,hull=!models&&Traffic.loadTrafficShip?await Traffic.loadTrafficShip(T):undefined;if(!dead)add(Traffic.createTraffic(T,{roads,heightAt:terrain.heightAtM,scale,models,hull,resolution,count:MAX_SHIPS}));}catch(e){console.warn('coach-world traffic',e);}landed();\r\n  // Gym Hubs: monolith towers visible across the whole city (data = one small nameless hubs.json)\r\n  try{let hubs=localHubs(await (deps.loadHubs||loadHubs)(),origin);if(compress)hubs=hubs.sort((a,b)=>Math.hypot(a.x,a.z)-Math.hypot(b.x,b.z)).slice(0,AR_HUBS); // AR: every hub lands on the 120 m horizon, so only the nearest few (6 draws each)\r\n  if(flight)hubs=hubs.sort((a,b)=>Math.hypot(a.x,a.z)-Math.hypot(b.x,b.z)).slice(0,3);\r\n  if(!dead&&hubs.length)add(compress?createHubs(T,{hubs,scale,resolution,height:heightCap(1e9)}):createHubs(T,{hubs,scale,resolution,labels:!flight}));}catch(e){console.warn('coach-world hubs',e);}landed();\r\n  // WS-K: parks + natural land are neutral alien forest in every faction city (one forest per decoded cell, 2 draw calls each)\r\n  try{decoded.forEach((m,i)=>{if(dead||m.missing||!m.landuse?.cls?.length)return;const f=createWildForest(T,{cell:cells[i],landuse:m.landuse,heightAt:terrain.heightAtM,scale,resolution});if(f.stats.trees){add(f);forests.push(f);}else f.dispose();});}catch(e){console.warn('coach-world forest',e);}landed();\r\n  // wild coaches: radar ring + pins inside this one-map world view (flag ?wild=1, off by default)\r\n  try{if(!flight&&wildFlag()){const m=await import('./wild-radar.mjs');if(!dead)add(m.createWildRadar(T,{origin,heightAt:terrain.heightAtM,scale,getBase,deps:deps.wild}));}}catch(e){console.warn('coach-world wild radar',e);}\r\n  // buildings nearest the base first, capped; houses -> ruins (one kit per cell), the rest -> MOM kit\r\n  const all=decoded.flatMap((m,i)=>m.missing?[]:buildingsOf(m,String(i))).filter(b=>outsideHabitat(b,flight?0:R_RING)).map(b=>({...b,h:flight?b.h:capFor(compress)(b)})).sort((a,b)=>Math.hypot(a.x,a.z)-Math.hypot(b.x,b.z)).slice(0,flight?220:compress?AR_BUILDINGS:MAX_BUILDINGS);\r\n  const houses=all.filter(b=>b.cat==='house'),rest=all.filter(b=>b.cat!=='house');\r\n  for(let i=0;i<rest.length&&!dead;i+=300){add(createMomKit(T,{cell:{buildings:rest.slice(i,i+300)},heightAt:terrain.heightAtM,scale}));await tick();}\r\n  // residential areas: one fogged ruins kit per 80 m block of houses (densest first, max MAX_RUINS), so homes near the pin are fogged in AR and the zoomed world alike\r\n  if(houses.length>=3&&!dead){const blk=new Map();for(const b of houses){const k=Math.floor(b.x/80)+','+Math.floor(b.z/80),e=blk.get(k)||{n:0,sx:0,sz:0};e.n++;e.sx+=b.x;e.sz+=b.z;blk.set(k,e);}\r\n   [...blk.values()].filter(e=>e.n>=3).sort((a,b)=>b.n-a.n).slice(0,MAX_RUINS).forEach((e,i)=>add(createRuinsKit(T,{cell:{x:e.sx/e.n,z:e.sz/e.n,w:80,d:80,seed:e.n*31+i},faction,heightAt:terrain.heightAtM,scale})));}\r\n  let clearance=450;if(flight){for(const c of tcells)for(const h of c.heights)clearance=Math.max(clearance,h*1.5+70);for(const m of decoded)if(!m.missing)for(const b of buildingsOf(m,'clearance'))clearance=Math.max(clearance,terrain.heightAtM(b.x,b.z)+b.h+70);}\r\n  const exclusions=[];if(flight){const near=(x,z)=>Math.hypot(x,z)<900,box=(x0,z0,x1,z1)=>{if(near((x0+x1)/2,(z0+z1)/2))exclusions.push({x0,x1,z0,z1});};for(const road of roads)for(let i=0;i<road.pts.length-1;i+=10){const pts=road.pts.slice(i,Math.min(i+11,road.pts.length)),xs=pts.map(p=>p[0]),zs=pts.map(p=>p[1]);box(Math.min(...xs)-10,Math.min(...zs)-10,Math.max(...xs)+10,Math.max(...zs)+10);}for(const m of decoded){if(m.missing)continue;for(const b of buildingsOf(m,'ecology'))box(b.x-b.w/2-3,b.z-b.d/2-3,b.x+b.w/2+3,b.z+b.d/2+3);const w=m.water;if(w)for(let i=0;i<w.start.length-1;i++){let x0=Infinity,z0=Infinity,x1=-Infinity,z1=-Infinity;for(let j=w.start[i];j<w.start[i+1];j++){const k=w.idx[j]*2,x=w.pos[k],z=w.pos[k+1];x0=Math.min(x0,x);x1=Math.max(x1,x);z0=Math.min(z0,z);z1=Math.max(z1,z);}if(Number.isFinite(x0))box(x0,z0,x1,z1);}}}\r\n  if(banks&&!dead){ // data landed: lift the load banks nearest-first; missing tiles keep theirs; changed cells wait for a scout\r\n   cells.map((c,i)=>({c,i,f:cellFrames[i]})).sort((a,b)=>Math.hypot(a.f.x0+a.f.s/2,a.f.z0+a.f.s/2)-Math.hypot(b.f.x0+b.f.s/2,b.f.z0+b.f.s/2)).forEach(({c,i,f},k)=>{\r\n    const id=c.x+','+c.y,m=decoded[i],at={x:f.x0+f.s/2,y:terrain.heightAtM(f.x0+f.s/2,f.z0+f.s/2),z:f.z0+f.s/2,r:f.s/2};\r\n    if(m.missing){fog.delete('load:'+id);fogPut('missing:'+id,{...at,kind:'missing'});return;}\r\n    const sig=cellSignature(m),st=ledger.check('cell:'+id,sig);\r\n    if(st==='changed'){fog.delete('load:'+id);fogPut('change:'+id,{...at,kind:'change',sig,ledgerId:'cell:'+id});}\r\n    else{ledger.see('cell:'+id,sig);fog.get('load:'+id).lift=fogT+.6+k*LIFT_STAGGER;}\r\n   });\r\n  }\r\n  owed=0;return {origin,home:home.from,clearance,exclusions};\r\n })().catch(e=>{console.warn('coach-world world layer',e);return null;}).finally(()=>{owed=0;if(!terrain)ringResolve?.(null);}); // failed or dead: nothing more is coming, lift the veil\r\n const fogTick=t=>{fogT=t/1000;let gone=false;for(const [id,b] of fog){if(b.liftIn!=null){b.lift=fogT+b.liftIn;delete b.liftIn;}\r\n  if(b.lift!=null&&fogT>=b.lift){if(b.ledgerId)ledger.see(b.ledgerId,b.sig);fog.delete(id);gone=true;}}if(gone)fogSync();};\r\n return {\r\n  group:root,ready,parts,ringReady,heightAt:(x,z)=>terrain?terrain.heightAt(x,z):0,\r\n  // V2 Fog of Change API (metres about the home origin). kind 'change' banks clear on scout; others on clearFog or their own lift.\r\n  fogChange(id,{x,z,r=120,kind='api'}={}){fogPut('api:'+id,{x,y:terrain?terrain.heightAtM(x,z):0,z,r,kind});},\r\n  clearFog(id){fogDrop(id);fogDrop('api:'+id);},\r\n  scout(x,z,d){ground?.setFocus(x,z);if(!banks)return;const now=fogT;for(const [id,b] of fog){if(b.kind!=='change'&&b.kind!=='api')continue;const inside=d<=SCOUT_M&&Math.hypot(x-b.x,z-b.z)<=b.r;\r\n   if(!inside){scoutAt.delete(id);continue;}if(!scoutAt.has(id))scoutAt.set(id,now);else if(now-scoutAt.get(id)>=SCOUT_S){scoutAt.delete(id);if(b.ledgerId)ledger.see(b.ledgerId,b.sig);fogDrop(id);}}},\r\n  fogState:()=>({banks:[...fog.entries()].map(([id,b])=>({id,kind:b.kind})),draw:banks?.stats()}),\r\n  get veil(){return veil;}, // V1: 1 = nothing landed yet (fog pulled in), 0 = done; eased in update()\r\n  setFade(v){if(v===fade&&!dirty)return;fade=v;apply();},\r\n  setCompress(d0=D0,dh=DH){cu.uCwD0.value=d0;cu.uCwDH.value=dh;}, // AR tuning; d0=1e9 = off (materials still carry the chunk)\r\n  update(t,d,cam){if(banks)fogTick(t);if(ground&&flight&&cam)ground.setFocus((cam.position.x-root.position.x)/scale,(cam.position.z-root.position.z)/scale);if(dirty)apply();{const dt=veilAt?Math.min(.25,Math.max(0,(t-veilAt)/1000)):0;veilAt=t;veil+=(owed/VEIL_STAGES-veil)*Math.min(1,dt*VEIL_RATE);}if(compress&&cam)recull(cam);\r\n   if(forests.length&&cam){const a=forests[0].aim(cam,d);if(a)planForestLod(forests,a.x,a.z,d/scale>MACRO_M);} // forest LOD: before the forests' own update (critter step runs only for the full-detail ones)\r\n   for(const p of parts)p.update?.(t/1000,d,cam);},\r\n  dispose(){dead=true;ringResolve?.(null);for(const p of parts)p.dispose?.();parts.length=0;forests.length=0;culls.length=0;tiles?.dispose?.();root.removeFromParent();},\r\n };\r\n}\r\n","modules/coach-world-rts/world-roads.mjs":"// R58 WS-C: road ribbons draped on the rendered terrain. ONE mesh (one draw call) per terrain cell, one shared PS1 strip material.\r\n// createWorldRoads(T,{roads,heightAt,scale,cellOf,noise,resolution})\r\n//   roads = [{pts:[[x,z]...] metres, class, bridge?, tunnel?}]; heightAt(x,z) in METRES (terrain.heightAtM); cellOf(x,z)->id (terrain.cellIdAt, world units)\r\n//   -> {group,update(tSeconds),stats(),dispose}. Group is scaled by `scale` like the terrain.\r\n// Bridge = flat deck at the higher endpoint's ground height; tunnel = not drawn (the ground covers it).\r\nimport { createPs1Material } from \"./ps1-material.mjs\";\r\nimport { addGroundNoise, makeNoise } from \"./world-terrain.mjs\";\r\n\r\nexport const WIDTH = { motorway: 14, trunk: 12, motorway_link: 8, primary: 10, secondary: 8.5, tertiary: 7, residential: 7, service: 4, path: 1.5, footway: 1.5 };\r\nexport const PULL = 0.004, STEP = 10, LIFT = 0.05, V_METRES = 16, SCROLL_MPS = 6;\r\nconst TINT = { footway: [0.8, 0.7, 0.5], path: [0.8, 0.7, 0.5] };\r\nconst widthOf = (c) => WIDTH[c] ?? 7;\r\nexport const isFreeway = (c) => c === \"motorway\" || c === \"trunk\" || c === \"motorway_link\";\r\n\r\n// 64x256 strip: u across the road, v along it (V_METRES per repeat). MOM Inc. look: purple asphalt, gold edge lines + centre dashes, neon\r\n// magenta/cyan lane dividers. Bright pixels are the \"emissive\" lane lines.\r\nexport function stripTexture(T) {\r\n  const W = 64, H = 256, d = new Uint8Array(W * H * 4);\r\n  for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {\r\n    let r = 52, g = 26, b = 84; // purple asphalt\r\n    const n = (x * 7 + y * 13) % 5; r += n; g += n; b += n * 2; // PS1 grit\r\n    const dash = y < 112;\r\n    if (x < 3 || x >= 61) { r = 255; g = 205; b = 60; } // gold edge lines\r\n    else if ((x >= 30 && x <= 33) && dash) { r = 255; g = 215; b = 70; } // gold centre dashes\r\n    else if ((x === 17 || x === 46) && y % 128 < 48) { if (x === 17) { r = 255; g = 60; b = 220; } else { r = 60; g = 240; b = 255; } } // neon dividers\r\n    const i = (y * W + x) * 4; d[i] = r; d[i + 1] = g; d[i + 2] = b; d[i + 3] = 255;\r\n  }\r\n  const t = new T.DataTexture(d, W, H, T.RGBAFormat);\r\n  t.wrapS = T.ClampToEdgeWrapping; t.wrapT = T.RepeatWrapping; t.magFilter = t.minFilter = T.NearestFilter; t.generateMipmaps = false; t.needsUpdate = true;\r\n  return t;\r\n}\r\n\r\nfunction roadMaterial(T, resolution, noise) {\r\n  const map = stripTexture(T), m = createPs1Material(T, { map, vertexColors: true, resolution }), prev = m.onBeforeCompile;\r\n  m.polygonOffset = true; m.polygonOffsetFactor = m.polygonOffsetUnits = -2;\r\n  const time = { uRoadT: { value: 0 } };\r\n  m.userData.time = time.uRoadT;\r\n  m.onBeforeCompile = (shader, r) => {\r\n    prev(shader, r);\r\n    Object.assign(shader.uniforms, time);\r\n    shader.vertexShader = shader.vertexShader.replace(\"#include <common>\", \"#include <common>\\nuniform float uRoadT;\")\r\n      .replace(\"#include <uv_vertex>\", \"#include <uv_vertex>\\n#ifdef USE_MAP\\nvMapUv.y-=uRoadT;\\n#endif\");\r\n    // bright strip pixels glow (lane lines), scaled by the vertex tint\r\n    shader.fragmentShader = shader.fragmentShader.replace(\"#include <emissivemap_fragment>\", `#include <emissivemap_fragment>\r\n{float lum=max(diffuseColor.r,max(diffuseColor.g,diffuseColor.b));totalEmissiveRadiance+=diffuseColor.rgb*step(0.7,lum)*0.9;}`);\r\n  };\r\n  const key = m.customProgramCacheKey; m.customProgramCacheKey = () => `${key.call(m)}-road`;\r\n  addGroundNoise(m, noise);\r\n  m.userData.map = map;\r\n  return m;\r\n}\r\n\r\n// y function for one road: flat deck for bridges, draped (+LIFT) otherwise.\r\nexport function roadY(road, hm) {\r\n  if (road.bridge) { const p = road.pts, deck = Math.max(hm(p[0][0], p[0][1]), hm(p[p.length - 1][0], p[p.length - 1][1])) + LIFT; return () => deck; }\r\n  return (x, z) => hm(x, z) + LIFT;\r\n}\r\n\r\n// centreline resampled so no segment exceeds `step`; with `f` (ground height) each piece is bisected (<=3 levels, to ~1.25 m) until the\r\n// midpoint sag is <= TOL, so a ribbon stays within TOL of the drawn mesh even on cliffs. Returns [{x,z,s}] (s = distance along).\r\nexport const TOL = 0.1;\r\nexport function resample(pts, step = STEP, f = null, hw = 0) {\r\n  const out = []; let s = 0;\r\n  const sag = (ax, az, bx, bz) => { // worst deviation of the quad surface (bilinear of the 4 corners) from the ground, sampled 3 along x 5 across\r\n    const dx = bx - ax, dz = bz - az, l = Math.hypot(dx, dz) || 1, nx = -dz / l, nz = dx / l; let w = 0;\r\n    const row = (t, o) => f(ax + dx * t + nx * o, az + dz * t + nz * o);\r\n    for (const o of hw ? [-1, -0.5, 0, 0.5, 1] : [0]) for (const t of [0.25, 0.5, 0.75]) w = Math.max(w, Math.abs(row(t, o * hw) - (row(0, o * hw) * (1 - t) + row(1, o * hw) * t)));\r\n    return w;\r\n  };\r\n  const piece = (ax, az, bx, bz, s0, len, depth) => {\r\n    if (f && depth > 0 && sag(ax, az, bx, bz) > TOL) { const mx = (ax + bx) / 2, mz = (az + bz) / 2; piece(ax, az, mx, mz, s0, len / 2, depth - 1); piece(mx, mz, bx, bz, s0 + len / 2, len / 2, depth - 1); return; }\r\n    out.push({ x: ax, z: az, s: s0 });\r\n  };\r\n  for (let i = 0; i < pts.length - 1; i++) {\r\n    const [ax, az] = pts[i], [bx, bz] = pts[i + 1], len = Math.hypot(bx - ax, bz - az), n = Math.max(1, Math.ceil(len / step));\r\n    for (let k = 0; k < n; k++) {\r\n      const x0 = ax + (bx - ax) * k / n, z0 = az + (bz - az) * k / n, x1 = ax + (bx - ax) * (k + 1) / n, z1 = az + (bz - az) * (k + 1) / n;\r\n      piece(x0, z0, x1, z1, s + len * k / n, len / n, 3);\r\n    }\r\n    s += len;\r\n  }\r\n  const l = pts[pts.length - 1]; out.push({ x: l[0], z: l[1], s });\r\n  return out;\r\n}\r\n\r\nexport function createWorldRoads(T, { roads, heightAt, scale = 1, cellOf = () => \"all\", noise = makeNoise(T), resolution = [320, 180] } = {}) {\r\n  const group = new T.Group(); group.name = \"world-roads\"; group.scale.setScalar(scale);\r\n  const material = roadMaterial(T, resolution, noise), cellsAcc = new Map();\r\n  const acc = (id) => { let a = cellsAcc.get(id); if (!a) cellsAcc.set(id, a = { pos: [], col: [], uv: [], idx: [] }); return a; };\r\n  const uvPlain = 11 / 64; // footway / junction slice of the strip\r\n  const nodes = new Map(); // junction detection: quantised endpoints -> {x,z,hw,n,y}\r\n  const key = (x, z) => `${Math.round(x * 2)},${Math.round(z * 2)}`;\r\n  let ribbons = 0;\r\n\r\n  for (const road of roads) {\r\n    if (road.tunnel || !road.pts || road.pts.length < 2) continue;\r\n    const hw = widthOf(road.class) / 2, yAt = roadY(road, heightAt), P = resample(road.pts, STEP, road.bridge ? null : yAt, hw), tint = TINT[road.class] || [1, 1, 1];\r\n    const narrow = hw < 1;\r\n    // per-point left/right offsets (miter, clamped)\r\n    const C = hw * 2 < 3 ? 1 : hw * 2 < 8 ? 2 : 4, rows = []; // columns across the width so cross-slope is followed too\r\n    for (let i = 0; i < P.length; i++) {\r\n      const a = P[Math.max(0, i - 1)], b = P[Math.min(P.length - 1, i + 1)];\r\n      let tx = b.x - a.x, tz = b.z - a.z; const tl = Math.hypot(tx, tz) || 1; tx /= tl; tz /= tl;\r\n      const nx = -tz, nz = tx; let m = 1;\r\n      if (i > 0 && i < P.length - 1) { // miter against the incoming segment\r\n        const p = P[i - 1]; let sx = P[i].x - p.x, sz = P[i].z - p.z; const sl = Math.hypot(sx, sz) || 1; sx /= sl; sz /= sl;\r\n        m = Math.min(2, 1 / Math.max(0.5, nx * -sz + nz * sx));\r\n      }\r\n      rows.push(Array.from({ length: C + 1 }, (_, c) => { const o = (-1 + 2 * c / C) * hw * m; return [P[i].x + nx * o, P[i].z + nz * o]; }));\r\n    }\r\n    ribbons++;\r\n    for (let i = 0; i < P.length - 1; i++) {\r\n      const mx = (P[i].x + P[i + 1].x) / 2, mz = (P[i].z + P[i + 1].z) / 2, a = acc(cellOf(mx * scale, mz * scale) ?? \"off\");\r\n      const v0 = P[i].s / V_METRES, v1 = P[i + 1].s / V_METRES, ua = narrow ? uvPlain : 0.02, ub = narrow ? uvPlain + 0.02 : 0.98;\r\n      for (let c = 0; c < C; c++) {\r\n        const base = a.pos.length / 3, u0 = ua + (ub - ua) * c / C, u1 = ua + (ub - ua) * (c + 1) / C;\r\n        const q = [[rows[i][c], u0, v0], [rows[i][c + 1], u1, v0], [rows[i + 1][c], u0, v1], [rows[i + 1][c + 1], u1, v1]];\r\n        for (const [p, u, v] of q) { a.pos.push(p[0], yAt(p[0], p[1]), p[1]); a.col.push(tint[0], tint[1], tint[2]); a.uv.push(u, v); }\r\n        const e1x = a.pos[3 * (base + 1)] - a.pos[3 * base], e1z = a.pos[3 * (base + 1) + 2] - a.pos[3 * base + 2], e2x = a.pos[3 * (base + 2)] - a.pos[3 * base], e2z = a.pos[3 * (base + 2) + 2] - a.pos[3 * base + 2];\r\n        if (e1z * e2x - e1x * e2z >= 0) a.idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2); else a.idx.push(base, base + 2, base + 1, base + 1, base + 2, base + 3);\r\n      }\r\n    }\r\n    // junction bookkeeping: degree 1 per endpoint touch, 2 per interior touch; 3+ = junction\r\n    road.pts.forEach(([x, z], i) => {\r\n      const k = key(x, z), n = nodes.get(k) || { x, z, hw: 0, n: 0, y: yAt(x, z) };\r\n      n.hw = Math.max(n.hw, hw); n.n += i === 0 || i === road.pts.length - 1 ? 1 : 2; nodes.set(k, n);\r\n    });\r\n  }\r\n  // junction fan discs where 3+ roads meet (also cover mitre gaps)\r\n  let junctions = 0;\r\n  for (const n of nodes.values()) {\r\n    if (n.n < 3) continue;\r\n    junctions++;\r\n    const a = acc(cellOf(n.x * scale, n.z * scale) ?? \"off\"), base = a.pos.length / 3, S = 12, r = n.hw * 1.05;\r\n    a.pos.push(n.x, n.y, n.z); a.col.push(1, 1, 1); a.uv.push(uvPlain, 0);\r\n    for (let k = 0; k < S; k++) { const t = (k / S) * Math.PI * 2, x = n.x + Math.cos(t) * r, z = n.z + Math.sin(t) * r; a.pos.push(x, heightAt(x, z) + LIFT + 0.01, z); a.col.push(1, 1, 1); a.uv.push(uvPlain, 0); }\r\n    for (let k = 0; k < S; k++) a.idx.push(base, base + 1 + (k + 1) % S, base + 1 + k); // counter-clockwise seen from +y\r\n  }\r\n  let tris = 0;\r\n  for (const [id, a] of cellsAcc) {\r\n    const g = new T.BufferGeometry();\r\n    g.setAttribute(\"position\", new T.Float32BufferAttribute(a.pos, 3));\r\n    g.setAttribute(\"color\", new T.Float32BufferAttribute(a.col, 3));\r\n    g.setAttribute(\"uv\", new T.Float32BufferAttribute(a.uv, 2));\r\n    g.setIndex(a.idx); g.computeVertexNormals(); g.computeBoundingSphere();\r\n    const mesh = new T.Mesh(g, material); mesh.name = `roads-${id}`; group.add(mesh); tris += a.idx.length / 3;\r\n  }\r\n  return {\r\n    group,\r\n    update(tSeconds) { material.userData.time.value = (tSeconds * SCROLL_MPS / V_METRES) % 1; },\r\n    stats: () => ({ drawCalls: group.children.length, triangles: tris, ribbons, junctions }),\r\n    dispose() { for (const m of group.children) m.geometry.dispose(); material.userData.map.dispose(); material.dispose(); group.clear(); },\r\n  };\r\n}\r\n","modules/coach-world-rts/world-terrain.mjs":"// R58 WS-C: real-world terrain. One martini (RTIN) mesh per cell from a DEM grid, PS1 material, skirts, micro-noise near the camera.\r\n// Contract (WS-B world-tiles feeds this): cell = {id, minX, minZ, size, grid, heights}  (metres about the origin, +x east, +z south;\r\n//   grid = 2^n+1, heights = Float32Array(grid*grid) row-major, row index runs +z; neighbouring cells share their edge samples).\r\n// createWorldTerrain(T,{cells,scale,exaggeration,flatten,focus,nearError,farError,nearRadius,resolution})\r\n//   -> {group,heightAt(x,z),cellIdAt(x,z),setCamera(x,z),noise,stats(),dispose}\r\n// heightAt/setCamera use WORLD units (metres*scale); the group is scaled by `scale`, so everything inside stays in metres.\r\n// heightAt samples the rendered triangle (RTIN descent), not the DEM, so roads draped on it sit on what is drawn.\r\nimport Martini from \"../coach-world/vendor/martini/martini.mjs\";\r\nimport { createPs1Material } from \"./ps1-material.mjs\";\r\n\r\nexport const NEAR_ERROR = 0.25, FAR_ERROR = 2, NEAR_RADIUS = 350, NOISE_RADIUS = 25, NOISE_AMP = 0.08, SKIRT = 4;\r\nconst martiniFor = new Map();\r\nconst getMartini = (n) => { if (!martiniFor.has(n)) martiniFor.set(n, new Martini(n)); return martiniFor.get(n); };\r\n\r\n// Shared by roads so they ride the same displacement. Needs uniform vec3 uNear (local metres) in the vertex shader.\r\nexport function makeNoise(T) { return { uNear: { value: new T.Vector3(1e9, 0, 1e9) }, uAmp: { value: NOISE_AMP } }; }\r\nconst NOISE_COMMON = `uniform vec3 uNear;\\nuniform float uAmp;\\nfloat gnoise(vec2 p){return sin(p.x*1.7+sin(p.y*2.3))*sin(p.y*1.9+sin(p.x*1.3));}`;\r\nconst NOISE_BEGIN = `#include <begin_vertex>\\n{float d=distance(transformed.xz,uNear.xz);transformed.y+=uAmp*gnoise(transformed.xz)*(1.0-smoothstep(${(NOISE_RADIUS / 2).toFixed(1)},${NOISE_RADIUS.toFixed(1)},d));}`;\r\nexport function addGroundNoise(material, noise) {\r\n  const prev = material.onBeforeCompile, key = material.customProgramCacheKey;\r\n  material.onBeforeCompile = (shader, r) => {\r\n    prev?.(shader, r);\r\n    Object.assign(shader.uniforms, noise);\r\n    shader.vertexShader = shader.vertexShader.replace(\"#include <common>\", `#include <common>\\n${NOISE_COMMON}`).replace(\"#include <begin_vertex>\", NOISE_BEGIN);\r\n  };\r\n  material.customProgramCacheKey = () => `${key.call(material)}-gn`;\r\n  return material;\r\n}\r\n\r\n// Desert ramp by absolute height (m), darkened by slope. PS1 posterise does the rest.\r\nconst RAMP = [[0, 0.62, 0.50, 0.32], [25, 0.55, 0.44, 0.28], [60, 0.46, 0.37, 0.27], [110, 0.42, 0.38, 0.35]];\r\nexport const groundRamp = (h, out) => ramp(h, out); // V6: the far ring colours by the same height ramp\r\nfunction ramp(h, out) {\r\n  let i = 0;\r\n  while (i < RAMP.length - 2 && h > RAMP[i + 1][0]) i++;\r\n  const a = RAMP[i], b = RAMP[i + 1], t = Math.max(0, Math.min(1, (h - a[0]) / (b[0] - a[0])));\r\n  out[0] = a[1] + (b[1] - a[1]) * t; out[1] = a[2] + (b[2] - a[2]) * t; out[2] = a[3] + (b[3] - a[3]) * t;\r\n}\r\n\r\n// Flatten rects [{x0,z0,x1,z1,feather=4}] into a DEM copy (already exaggerated); returns the level used.\r\nfunction flattenInto(h, cell, rects) {\r\n  const N = cell.grid, step = cell.size / (N - 1);\r\n  for (const r of rects || []) {\r\n    const f = r.feather ?? 4, gx0 = Math.max(0, Math.floor((r.x0 - f - cell.minX) / step)), gx1 = Math.min(N - 1, Math.ceil((r.x1 + f - cell.minX) / step));\r\n    const gz0 = Math.max(0, Math.floor((r.z0 - f - cell.minZ) / step)), gz1 = Math.min(N - 1, Math.ceil((r.z1 + f - cell.minZ) / step));\r\n    if (gx0 > gx1 || gz0 > gz1) continue;\r\n    const level = r.level ?? r.centerLevel;\r\n    if (level == null) continue;\r\n    for (let gz = gz0; gz <= gz1; gz++) for (let gx = gx0; gx <= gx1; gx++) {\r\n      const x = cell.minX + gx * step, z = cell.minZ + gz * step;\r\n      const dx = Math.max(r.x0 - x, 0, x - r.x1), dz = Math.max(r.z0 - z, 0, z - r.z1), d = Math.max(0, Math.hypot(dx, dz) - step); // core grown one DEM step so the rect interior interpolates flat\r\n      if (d >= f) continue;\r\n      const k = d <= 0 ? 1 : 1 - d / f, w = k * k * (3 - 2 * k), i = gz * N + gx;\r\n      h[i] += (level - h[i]) * w;\r\n    }\r\n  }\r\n}\r\n\r\nexport function createWorldTerrain(T, { cells, scale = 1, exaggeration = 1.5, flatten = [], focus = { x: 0, z: 0 }, nearError = NEAR_ERROR, farError = FAR_ERROR, nearRadius = NEAR_RADIUS, resolution = [320, 180] } = {}) {\r\n  const exag = Math.max(1, Math.min(2, exaggeration));\r\n  const group = new T.Group(); group.name = \"world-terrain\"; group.scale.setScalar(scale);\r\n  const noise = makeNoise(T);\r\n  const material = addGroundNoise(createPs1Material(T, { vertexColors: true, resolution }), noise);\r\n  material.side = T.DoubleSide; // skirts\r\n  // flatten levels: mean exaggerated DEM under each rect, taken from the first cell that contains the rect centre, so all cells agree\r\n  const rects = flatten.map((r) => {\r\n    if (r.level != null) return r;\r\n    const cx = (r.x0 + r.x1) / 2, cz = (r.z0 + r.z1) / 2, c = cells.find((q) => cx >= q.minX && cx <= q.minX + q.size && cz >= q.minZ && cz <= q.minZ + q.size);\r\n    if (!c) return { ...r, centerLevel: null };\r\n    const N = c.grid, st = c.size / (N - 1);\r\n    let sum = 0, n = 0;\r\n    for (let z = r.z0; z <= r.z1; z += st) for (let x = r.x0; x <= r.x1; x += st) {\r\n      const gx = Math.max(0, Math.min(N - 1, Math.round((x - c.minX) / st))), gz = Math.max(0, Math.min(N - 1, Math.round((z - c.minZ) / st)));\r\n      sum += c.heights[gz * N + gx] * exag; n++;\r\n    }\r\n    const level = n ? sum / n : null;\r\n    let drop = 0; // widen the feather with the cut/fill depth so the base never sits on a cliff (slope <= ~1:2.5)\r\n    if (level != null) for (let t = 0; t < 1; t += 0.05) for (const [x, z] of [[r.x0 + (r.x1 - r.x0) * t, r.z0], [r.x0 + (r.x1 - r.x0) * t, r.z1], [r.x0, r.z0 + (r.z1 - r.z0) * t], [r.x1, r.z0 + (r.z1 - r.z0) * t]]) {\r\n      const gx = Math.max(0, Math.min(N - 1, Math.round((x - c.minX) / st))), gz = Math.max(0, Math.min(N - 1, Math.round((z - c.minZ) / st)));\r\n      drop = Math.max(drop, Math.abs(c.heights[gz * N + gx] * exag - level));\r\n    }\r\n    return { ...r, centerLevel: level, feather: Math.max(r.feather ?? 4, drop * 2.5) };\r\n  });\r\n\r\n  const built = []; // {cell, tile, maxError, mesh, geometry}\r\n  let tris = 0, verts = 0;\r\n  for (const cell of cells) {\r\n    const N = cell.grid, step = cell.size / (N - 1), h = new Float32Array(N * N);\r\n    for (let i = 0; i < h.length; i++) h[i] = cell.heights[i] * exag;\r\n    flattenInto(h, cell, rects);\r\n    const martini = getMartini(N), tile = martini.createTile(h);\r\n    const dist = Math.hypot(cell.minX + cell.size / 2 - focus.x, cell.minZ + cell.size / 2 - focus.z);\r\n    const maxError = dist < nearRadius ? nearError : farError;\r\n    const { vertices, triangles } = tile.getMesh(maxError);\r\n    const nv = vertices.length / 2, edge = [[], [], [], []]; // gx=0, gx=max, gz=0, gz=max\r\n    const pos = [], col = [], tmp = [0, 0, 0], at = (gx, gz) => h[Math.max(0, Math.min(N - 1, gz)) * N + Math.max(0, Math.min(N - 1, gx))];\r\n    for (let i = 0; i < nv; i++) {\r\n      const gx = vertices[2 * i], gz = vertices[2 * i + 1], y = h[gz * N + gx];\r\n      pos.push(cell.minX + gx * step, y, cell.minZ + gz * step);\r\n      const sx = (at(gx + 1, gz) - at(gx - 1, gz)) / (2 * step), sz = (at(gx, gz + 1) - at(gx, gz - 1)) / (2 * step), shade = 1 / Math.sqrt(1 + sx * sx + sz * sz);\r\n      ramp(y, tmp); const k = 0.55 + 0.45 * shade;\r\n      col.push(tmp[0] * k, tmp[1] * k, tmp[2] * k);\r\n      if (gx === 0) edge[0].push(i); if (gx === N - 1) edge[1].push(i); if (gz === 0) edge[2].push(i); if (gz === N - 1) edge[3].push(i);\r\n    }\r\n    // wind every triangle so its normal faces +y\r\n    const idx = [];\r\n    for (let i = 0; i < triangles.length; i += 3) {\r\n      const a = triangles[i], b = triangles[i + 1], c = triangles[i + 2];\r\n      const e1x = pos[3 * b] - pos[3 * a], e1z = pos[3 * b + 2] - pos[3 * a + 2], e2x = pos[3 * c] - pos[3 * a], e2z = pos[3 * c + 2] - pos[3 * a + 2];\r\n      if (e1z * e2x - e1x * e2z >= 0) idx.push(a, b, c); else idx.push(a, c, b);\r\n    }\r\n    // skirts: duplicate each boundary vertex dropped by SKIRT and stitch (hides LOD cracks between cells)\r\n    edge.forEach((list, e) => {\r\n      list.sort((p, q) => (e < 2 ? pos[3 * p + 2] - pos[3 * q + 2] : pos[3 * p] - pos[3 * q]));\r\n      const base = pos.length / 3;\r\n      for (const v of list) { pos.push(pos[3 * v], pos[3 * v + 1] - SKIRT, pos[3 * v + 2]); col.push(col[3 * v] * 0.6, col[3 * v + 1] * 0.6, col[3 * v + 2] * 0.6); }\r\n      for (let j = 0; j + 1 < list.length; j++) { const a = list[j], b = list[j + 1], c = base + j, d = base + j + 1; idx.push(a, b, c, b, d, c); }\r\n    });\r\n    const g = new T.BufferGeometry();\r\n    g.setAttribute(\"position\", new T.Float32BufferAttribute(pos, 3));\r\n    g.setAttribute(\"color\", new T.Float32BufferAttribute(col, 3));\r\n    g.setIndex(idx); g.computeVertexNormals(); g.computeBoundingSphere();\r\n    const mesh = new T.Mesh(g, material); mesh.name = `terrain-${cell.id}`; mesh.frustumCulled = true;\r\n    group.add(mesh);\r\n    built.push({ cell, tile, maxError, mesh, errors: tile.errors, N, step });\r\n    tris += idx.length / 3; verts += pos.length / 3;\r\n  }\r\n\r\n  const find = (x, z) => built.find((b) => x >= b.cell.minX && x <= b.cell.minX + b.cell.size && z >= b.cell.minZ && z <= b.cell.minZ + b.cell.size);\r\n  // RTIN point location: descend the implicit triangle tree exactly as getMesh does, then interpolate the leaf plane.\r\n  function heightIn(b, x, z) {\r\n    const { N, step, errors, maxError, tile } = b, h = tile.terrain, max = N - 1;\r\n    const u = Math.max(0, Math.min(max, (x - b.cell.minX) / step)), v = Math.max(0, Math.min(max, (z - b.cell.minZ) / step));\r\n    const inside = (ax, ay, bx, by, cx, cy) => {\r\n      const d1 = (u - bx) * (ay - by) - (ax - bx) * (v - by), d2 = (u - cx) * (by - cy) - (bx - cx) * (v - cy), d3 = (u - ax) * (cy - ay) - (cx - ax) * (v - ay);\r\n      return !((d1 < -1e-9 || d2 < -1e-9 || d3 < -1e-9) && (d1 > 1e-9 || d2 > 1e-9 || d3 > 1e-9));\r\n    };\r\n    const hz = (px, py) => h[py * N + px];\r\n    function walk(ax, ay, bx, by, cx, cy) {\r\n      const mx = (ax + bx) >> 1, my = (ay + by) >> 1;\r\n      if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * N + mx] > maxError) {\r\n        const l = [cx, cy, ax, ay, mx, my], r = [bx, by, cx, cy, mx, my];\r\n        if (inside(...l)) return walk(...l);\r\n        if (inside(...r)) return walk(...r);\r\n        return walk(...l);\r\n      }\r\n      // barycentric on the leaf triangle\r\n      const det = (by - cy) * (ax - cx) + (cx - bx) * (ay - cy);\r\n      const wa = ((by - cy) * (u - cx) + (cx - bx) * (v - cy)) / det, wb = ((cy - ay) * (u - cx) + (ax - cx) * (v - cy)) / det;\r\n      return wa * hz(ax, ay) + wb * hz(bx, by) + (1 - wa - wb) * hz(cx, cy);\r\n    }\r\n    const t1 = [0, 0, max, max, max, 0], t2 = [max, max, 0, 0, 0, max];\r\n    return inside(...t1) ? walk(...t1) : walk(...t2);\r\n  }\r\n  const heightM = (x, z) => { const b = find(x, z); return b ? heightIn(b, x, z) : 0; };\r\n  return {\r\n    group, noise, exaggeration: exag, material, // V6: the far ring shares the ground material (same PS1 look, fade and fog)\r\n    heightAt: (x, z) => heightM(x / scale, z / scale) * scale,\r\n    heightAtM: heightM, // metres in/out, used by roads/traffic so they stay in the scaled group's space\r\n    cellIdAt: (x, z) => find(x, z)?.cell.id ?? null,\r\n    setCamera(x, z) { noise.uNear.value.set(x / scale, 0, z / scale); },\r\n    stats: () => ({ drawCalls: built.length, triangles: tris, vertices: verts, cells: built.length, maxErrors: built.map((b) => b.maxError) }),\r\n    dispose() { for (const b of built) b.mesh.geometry.dispose(); material.dispose(); group.clear(); },\r\n  };\r\n}\r\n","modules/coach-world-rts/world-traffic.mjs":"// R58/R59 WS-C: MOM Inc. freeway traffic. Every ship model we have (6 hangar ships, decimated to ~2k tris under pod/worlds/traffic/) flies as freeway\r\n// traffic in four liveries: purple/gold MOM police (flashing light bar), yellow taxis, freight haulers (cargo box, biggest), purple-gold civilians.\r\n// Ships hover HOVER (3 m ~ 10 ft) over the road with an additive under-glow, an additive fading trail and a blob shadow.\r\n// Draw calls: one InstancedMesh per model (<= MODELS) + glow/trail + shadow = <= 8, cap MAX_SHIPS. No per-ship CPU: poses come from a path texture,\r\n// per-instance (row, phase, speed, lane, kind, scale) attributes and a single time uniform. Heading convention = ship-path.mjs figure8 (forward +z).\r\n// LAZY: nothing here fetches until loadTrafficModels() is called - call it only once the world layer is visible (~200 KB for all six).\r\n// bakeHull(T,scene,{nose})  big GLB scene -> decimated hull (nose +z, HULL_SIZE m, brightness in vertex colour). Used by tools/traffic-ships/build.mjs.\r\n// loadTrafficModels(T,{loadGlb,baseUrl,ids})->[{id,hull}]  (wedge fallback). createTraffic(T,{roads,heightAt,count,scale,models})->{group,meshes,update,stats,dispose}\r\nimport { createPs1Material } from \"./ps1-material.mjs\";\r\nimport { isFreeway, roadY, WIDTH } from \"./world-roads.mjs\";\r\n\r\nexport const MAX_SHIPS = 40, PATH_SAMPLES = 128, HOVER = 3, HULL_SIZE = 4.5, TRAIL_SECONDS = 0.55, HULL_GAIN = 1.5;\r\nexport const TRI_MIN = 1500, TRI_MAX = 2500;\r\nexport const KIND = { police: 0, taxi: 1, freight: 2, civilian: 3 };\r\n// Native forward axis of each source GLB in xz (checked on top-down renders, see tools/traffic-ships/build.mjs). Baked into the traffic GLBs: all are nose +z.\r\nexport const MODELS = [\r\n  { id: \"supportive\", nose: [1, 0] }, { id: \"direct\", nose: [1, 0] }, { id: \"analytical\", nose: [1, 0] },\r\n  { id: \"playful\", nose: [0, -1] }, { id: \"calm\", nose: [0, -1] }, { id: \"mom\", nose: [-1, 1] },\r\n];\r\nexport const MODEL_BASE = \"/pod/worlds/traffic/\";\r\nconst LIVERY = [[0.5, 0.18, 0.95], [1.0, 0.82, 0.08], [0.5, 0.56, 0.66], [0.6, 0.32, 0.95], [1.0, 0.6, 0.16]]; // police, taxi, freight, civilian violet, civilian amber-gold\r\nconst livery = (kind, i) => (kind === 3 ? LIVERY[3 + (i % 2)] : LIVERY[kind]);\r\nconst GLOW = [[0.85, 0.5, 1.0], [1.0, 0.85, 0.3], [1.0, 0.6, 0.25], [0.8, 0.55, 1.0]];\r\nconst MIX = [1, 3, 0, 2, 3, 1, 3, 2, 0, 3, 1, 2, 3, 1]; // civilian/taxi heavy, police + freight rarer\r\nconst SCALE = [[0.95, 1.3], [0.7, 1.05], [1.4, 1.8], [0.75, 1.35]]; // per-kind instance scale range (freight biggest)\r\n\r\nasync function defaultGlb(url) {\r\n  const [{ GLTFLoader }, { MeshoptDecoder }] = await Promise.all([import(\"/vendor/three/GLTFLoader.js\"), import(\"/vendor/three/meshopt_decoder.module.js\")]);\r\n  return (await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url)).scene;\r\n}\r\n\r\n// ---- hull from the real GLB -------------------------------------------------------------------------------------------------------------\r\nfunction textureLum(map) { // returns (u,v)=>luminance 0..1 or null (browser only)\r\n  try {\r\n    const img = map?.image; if (!img || typeof document === \"undefined\") return null;\r\n    const w = img.width, h = img.height, c = document.createElement(\"canvas\"); c.width = w; c.height = h;\r\n    const x = c.getContext(\"2d\", { willReadFrequently: true }); x.drawImage(img, 0, 0);\r\n    const d = x.getImageData(0, 0, w, h).data;\r\n    return (u, v) => { const i = (Math.min(h - 1, Math.max(0, Math.floor(v * h))) * w + Math.min(w - 1, Math.max(0, Math.floor(u * w)))) * 4; return (0.3 * d[i] + 0.59 * d[i + 1] + 0.11 * d[i + 2]) / 255; };\r\n  } catch { return null; }\r\n}\r\n\r\n// Vertex-cluster decimation: snap vertices to a grid, merge, drop degenerate triangles. Cheap, PS1-appropriate.\r\nfunction cluster(pos, uvlum, idx, cell) {\r\n  const map = new Map(), outPos = [], outLum = [], cnt = [], remap = new Uint32Array(pos.length / 3);\r\n  for (let i = 0; i < pos.length / 3; i++) {\r\n    const k = `${Math.round(pos[3 * i] / cell)},${Math.round(pos[3 * i + 1] / cell)},${Math.round(pos[3 * i + 2] / cell)}`;\r\n    let j = map.get(k);\r\n    if (j === undefined) { j = outPos.length / 3; map.set(k, j); outPos.push(0, 0, 0); outLum.push(0); cnt.push(0); }\r\n    outPos[3 * j] += pos[3 * i]; outPos[3 * j + 1] += pos[3 * i + 1]; outPos[3 * j + 2] += pos[3 * i + 2]; outLum[j] += uvlum[i]; cnt[j]++; remap[i] = j;\r\n  }\r\n  for (let j = 0; j < cnt.length; j++) { outPos[3 * j] /= cnt[j]; outPos[3 * j + 1] /= cnt[j]; outPos[3 * j + 2] /= cnt[j]; outLum[j] /= cnt[j]; }\r\n  const tris = [];\r\n  for (let i = 0; i < idx.length; i += 3) { const a = remap[idx[i]], b = remap[idx[i + 1]], c = remap[idx[i + 2]]; if (a !== b && b !== c && a !== c) tris.push(a, b, c); }\r\n  return { pos: outPos, lum: outLum, tris };\r\n}\r\n\r\nexport function wedgeHull(T) {\r\n  const nose = [0, 0.4, 2.2], l = [-2, 0.4, -1.6], r = [2, 0.4, -1.6], top = [0, 1.5, -1], bot = [0, 0, -1.2];\r\n  const tri = [nose, l, top, nose, top, r, nose, bot, l, nose, r, bot, l, bot, top, r, top, bot];\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute(\"position\", new T.Float32BufferAttribute(tri.flat(), 3));\r\n  g.setAttribute(\"color\", new T.Float32BufferAttribute(tri.flatMap(() => [0.6, 0.6, 0.6]), 3));\r\n  g.computeVertexNormals();\r\n  return g;\r\n}\r\n\r\nexport function bakeHull(T, scene, { nose = [1, 0], size = HULL_SIZE } = {}) {\r\n  scene.updateMatrixWorld(true);\r\n  const pos = [], lum = [], idx = [];\r\n  scene.traverse((o) => {\r\n    if (!o.isMesh) return;\r\n    const g = o.geometry, p = g.attributes.position, uv = g.attributes.uv, f = textureLum(o.material?.map), base = pos.length / 3, v = new T.Vector3();\r\n    for (let i = 0; i < p.count; i++) { v.fromBufferAttribute(p, i).applyMatrix4(o.matrixWorld); pos.push(v.x, v.y, v.z); lum.push(f && uv ? f(uv.getX(i), uv.getY(i)) : 0.7); }\r\n    const ix = g.index; for (let i = 0; i < (ix ? ix.count : p.count); i++) idx.push(base + (ix ? ix.getX(i) : i));\r\n  });\r\n  const nl = Math.hypot(nose[0], nose[1]), nx = nose[0] / nl, nz = nose[1] / nl; // nose -> +z: z' = p.n, x' = x*nz - z*nx\r\n  for (let i = 0; i < pos.length; i += 3) { const x = pos[i], z = pos[i + 2]; pos[i] = x * nz - z * nx; pos[i + 2] = x * nx + z * nz; }\r\n  const bb = new T.Box3(), t = new T.Vector3(); for (let i = 0; i < pos.length; i += 3) bb.expandByPoint(t.set(pos[i], pos[i + 1], pos[i + 2]));\r\n  const sz = bb.getSize(new T.Vector3()), k = size / Math.max(sz.x, sz.y, sz.z), cx = (bb.min.x + bb.max.x) / 2, cz = (bb.min.z + bb.max.z) / 2;\r\n  for (let i = 0; i < pos.length; i += 3) { pos[i] = (pos[i] - cx) * k; pos[i + 1] = (pos[i + 1] - bb.min.y) * k; pos[i + 2] = (pos[i + 2] - cz) * k; }\r\n  let cell = size / 40, c = cluster(pos, lum, idx, cell);\r\n  for (let n = 0; n < 24 && (c.tris.length / 3 > TRI_MAX || c.tris.length / 3 < TRI_MIN); n++) { cell *= c.tris.length / 3 > TRI_MAX ? 1.1 : 1 / 1.1; c = cluster(pos, lum, idx, cell); }\r\n  let mean = 0; c.lum.forEach((l) => (mean += l)); mean = mean / c.lum.length || 1;\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute(\"position\", new T.Float32BufferAttribute(c.pos, 3));\r\n  g.setAttribute(\"color\", new T.Float32BufferAttribute(c.lum.flatMap((l) => { const q = Math.min(1, l / mean / HULL_GAIN); return [q, q, q]; }), 3)); // x HULL_GAIN at build time\r\n  g.setIndex(c.tris);\r\n  return g;\r\n}\r\n\r\n// Lazy: fetch the six small traffic GLBs (already nose +z, decimated, brightness baked). Any failure on a model skips it; none loaded -> wedge.\r\nexport async function loadTrafficModels(T, { loadGlb = defaultGlb, baseUrl = MODEL_BASE, ids = MODELS.map((m) => m.id), timeoutMs = 7000 } = {}) {\r\n  const out = [];\r\n  await Promise.all(ids.map(async (id) => {\r\n    try {\r\n      const scene = await Promise.race([loadGlb(`${baseUrl}${id}.glb`), new Promise((_, rej) => setTimeout(() => rej(new Error(\"timeout\")), timeoutMs))]);\r\n      let hull = null; scene.traverse((o) => { if (o.isMesh && !hull) hull = o.geometry; });\r\n      if (hull?.attributes?.color) { hull.userData.source = \"glb\"; out.push({ id, hull }); }\r\n    } catch { /* skip this model */ }\r\n  }));\r\n  out.sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id));\r\n  if (!out.length) { const hull = wedgeHull(T); hull.userData.source = \"wedge\"; out.push({ id: \"wedge\", hull }); }\r\n  return out;\r\n}\r\n\r\n// hull + livery parts into ONE non-indexed geometry. sid: 0 = all kinds, 1+kind = that kind only. part: 0 hull, 1 light bar, 2 cargo, 3 taxi sign.\r\nexport function buildFleet(T, hull) {\r\n  hull.computeBoundingBox();\r\n  const bb = hull.boundingBox, w = bb.max.x - bb.min.x, len = bb.max.z - bb.min.z, P0 = hull.attributes.position;\r\n  let top = bb.min.y; // roof height over the centre line (bars/boxes sit on the hull, not on a far wing tip)\r\n  for (let i = 0; i < P0.count; i++) if (Math.abs(P0.getX(i)) < 0.18 * w && Math.abs(P0.getZ(i) - (bb.min.z + bb.max.z) / 2) < 0.3 * len) top = Math.max(top, P0.getY(i));\r\n  const zc = (bb.min.z + bb.max.z) / 2, cw = Math.min(2.2, 0.45 * w), cl = Math.min(2.6, 0.55 * len);\r\n  let cTop = bb.min.y; // highest hull point under the cargo footprint so the box rests on the hull\r\n  for (let i = 0; i < P0.count; i++) if (Math.abs(P0.getX(i)) < cw / 2 && Math.abs(P0.getZ(i) - (zc - 0.12 * len)) < cl / 2) cTop = Math.max(cTop, P0.getY(i));\r\n  const pos = [], col = [], nor = [], sid = [], part = [];\r\n  const add = (g, s, p, color, gain = 1) => {\r\n    const f = g.index ? g.toNonIndexed() : g; f.computeVertexNormals();\r\n    const P = f.attributes.position, N = f.attributes.normal, C = f.attributes.color;\r\n    for (let i = 0; i < P.count; i++) {\r\n      pos.push(P.getX(i), P.getY(i), P.getZ(i)); nor.push(N.getX(i), N.getY(i), N.getZ(i));\r\n      if (color) col.push(...color); else col.push(C.getX(i) * gain, C.getY(i) * gain, C.getZ(i) * gain);\r\n      sid.push(s); part.push(p);\r\n    }\r\n  };\r\n  const box = (x, y, z, sx, sy, sz) => new T.BoxGeometry(sx, sy, sz).translate(x, y, z);\r\n  const bw = Math.min(2.2, 0.4 * w);\r\n  add(hull, 0, 0, null, HULL_GAIN);\r\n  add(box(0, top, zc + 0.1 * len, bw, 0.22, 0.4), 1 + KIND.police, 1, [1, 1, 1]);                 // police light bar\r\n  add(box(0, top - 0.1, zc + 0.1 * len, bw * 1.15, 0.08, 0.55), 1 + KIND.police, 0, [0.9, 0.7, 0.2]); // gold trim under the bar\r\n  add(box(0, top + 0.1, zc + 0.1 * len, 0.7, 0.25, 0.3), 1 + KIND.taxi, 3, [1, 1, 1]);          // taxi roof sign\r\n  add(box(0, cTop + 0.45, zc - 0.12 * len, cw, 1.1, cl), 1 + KIND.freight, 2, [1.7, 1.0, 0.45]);   // cargo box (orange-brown on gunmetal)\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute(\"position\", new T.Float32BufferAttribute(pos, 3));\r\n  g.setAttribute(\"normal\", new T.Float32BufferAttribute(nor, 3));\r\n  g.setAttribute(\"color\", new T.Float32BufferAttribute(col, 3));\r\n  g.setAttribute(\"sid\", new T.Float32BufferAttribute(sid, 1));\r\n  g.setAttribute(\"part\", new T.Float32BufferAttribute(part, 1));\r\n  return g;\r\n}\r\n\r\n// ---- shaders ----------------------------------------------------------------------------------------------------------------------------\r\nconst POSE = `uniform sampler2D uPath;uniform float uTime;uniform float uHover;attribute vec4 aShip;attribute float aKind;attribute float aScale;\r\nvec3 pathAt(float row,float s){float f=clamp(s,0.0,1.0)*${PATH_SAMPLES - 1}.0;int i0=int(floor(f));int i1=min(i0+1,${PATH_SAMPLES - 1});\r\n return mix(texelFetch(uPath,ivec2(i0,int(row)),0).xyz,texelFetch(uPath,ivec2(i1,int(row)),0).xyz,fract(f));}\r\nfloat shipS(){return fract(aShip.y+uTime*aShip.z);}\r\nvec3 dirAt(float s){vec3 t=pathAt(aShip.x,s+0.006)-pathAt(aShip.x,s-0.006);if(aShip.z<0.0)t=-t;return t;}\r\nvec3 rightOf(vec3 t){vec2 f=normalize(t.xz+vec2(1e-5));return vec3(f.y,0.0,-f.x);}\r\nfloat kScale(){return aScale;}\r\nfloat laneOff(){return aShip.w;}\r\nfloat bobAt(){return 0.15*sin(uTime*2.0+aShip.y*30.0);}`;\r\n\r\nfunction shipMaterial(T, resolution, u) {\r\n  const m = createPs1Material(T, { vertexColors: true, resolution }), prev = m.onBeforeCompile;\r\n  m.userData.u = u;\r\n  m.onBeforeCompile = (shader, r) => {\r\n    prev(shader, r); Object.assign(shader.uniforms, u);\r\n    shader.vertexShader = shader.vertexShader.replace(\"#include <common>\", `#include <common>\\n${POSE}\\nattribute float sid;attribute float part;varying vec3 vGlowC;\r\nmat3 shipFrame;vec3 shipPos;\r\nvoid shipPose(){float s=shipS();vec3 t=dirAt(s);vec3 right=rightOf(t);vec2 f=normalize(t.xz+vec2(1e-5));vec3 fw=vec3(f.x,t.y*0.05,f.y);\r\n float bank=0.2*sin(uTime*1.3+aShip.y*40.0);vec3 up=normalize(vec3(right.x*bank,1.0,right.z*bank));\r\n shipFrame=mat3(right,up,normalize(fw));shipPos=pathAt(aShip.x,s)+right*laneOff()+vec3(0.0,bobAt(),0.0);}`)\r\n      .replace(\"#include <beginnormal_vertex>\", \"#include <beginnormal_vertex>\\nshipPose();objectNormal=shipFrame*objectNormal;\")\r\n      .replace(\"#include <begin_vertex>\", `#include <begin_vertex>\r\nvGlowC=vec3(0.0);\r\nif(part>0.5&&part<1.5){bool L=position.x>0.0;bool ph=sin(uTime*9.0+aShip.y*40.0)>0.0;float on=(L==ph)?1.0:0.0;vGlowC=(L?vec3(1.0,0.78,0.2):vec3(0.75,0.35,1.0))*(0.1+on*1.7);}\r\nelse if(part>2.5)vGlowC=vec3(1.0,0.95,0.6)*0.9;\r\ntransformed=shipFrame*(transformed*kScale())+shipPos;`)\r\n      .replace(\"#include <project_vertex>\", \"#include <project_vertex>\\nif(sid>0.5&&abs(sid-1.0-aKind)>0.5)gl_Position=vec4(2.0,2.0,2.0,1.0);\");\r\n    shader.fragmentShader = shader.fragmentShader.replace(\"#include <common>\", \"#include <common>\\nvarying vec3 vGlowC;\")\r\n      .replace(\"#include <emissivemap_fragment>\", \"#include <emissivemap_fragment>\\ntotalEmissiveRadiance+=vGlowC;\");\r\n  };\r\n  const key = m.customProgramCacheKey; m.customProgramCacheKey = () => `${key.call(m)}-fleet`;\r\n  return m;\r\n}\r\n\r\n// decal material: deco attr dk = (mode 0 pool / 1 trail, tk 0..1 along trail, side -1..1)\r\nfunction decoMaterial(T, u, { shadow }) {\r\n  const m = new T.MeshBasicMaterial({ transparent: true, depthWrite: false, blending: shadow ? T.NormalBlending : T.AdditiveBlending, side: T.DoubleSide });\r\n  m.polygonOffset = true; m.polygonOffsetFactor = m.polygonOffsetUnits = -3;\r\n  m.userData.u = u;\r\n  m.onBeforeCompile = (shader) => {\r\n    Object.assign(shader.uniforms, u);\r\n    shader.vertexShader = shader.vertexShader.replace(\"#include <common>\", `#include <common>\\n${POSE}\\nattribute vec3 dk;varying vec2 vUv2;varying float vMode;varying float vH;`)\r\n      .replace(\"#include <begin_vertex>\", `#include <begin_vertex>\r\nfloat s=shipS();float ks=kScale();vec3 pc=pathAt(aShip.x,s);vec3 t=dirAt(s);vec3 right=rightOf(t);vec2 f=normalize(t.xz+vec2(1e-5));vec3 fw=vec3(f.x,0.0,f.y);\r\nvMode=dk.x;vH=(pc.y+bobAt()-(pc.y-uHover))/uHover;\r\nif(dk.x<0.5){float S=${shadow ? \"2.6\" : \"3.4\"}*ks;vec3 gp=vec3(pc.x,pc.y-uHover+0.12,pc.z)+right*laneOff();transformed=gp+right*position.x*S+fw*position.z*S;vUv2=position.xz;}\r\nelse{float sb=s-aShip.z*dk.y*${TRAIL_SECONDS.toFixed(2)};vec3 pb=pathAt(aShip.x,sb);vec3 tb=dirAt(clamp(sb,0.0,1.0));vec3 rb=rightOf(tb);\r\n transformed=pb+rb*(laneOff()+dk.z*0.75*ks)+vec3(0.0,0.9*ks+bobAt(),0.0);vUv2=vec2(dk.z,dk.y);}`);\r\n    shader.fragmentShader = shader.fragmentShader.replace(\"#include <common>\", \"#include <common>\\nvarying vec2 vUv2;varying float vMode;varying float vH;\")\r\n      .replace(\"#include <color_fragment>\", `#include <color_fragment>\r\n{float a;if(vMode<0.5){float r=length(vUv2);a=pow(clamp(1.0-r,0.0,1.0),${shadow ? \"1.2\" : \"1.6\"});${shadow ? \"a*=0.55*(1.0-clamp(vH*0.45,0.0,0.7));\" : \"a*=0.9;\"}}\r\n else{a=pow(1.0-vUv2.y,1.6)*(1.0-abs(vUv2.x))*0.75;${shadow ? \"a=0.0;\" : \"\"}}\r\n diffuseColor.a*=a;${shadow ? \"diffuseColor.rgb=vec3(0.0);\" : \"\"}}`);\r\n  };\r\n  m.customProgramCacheKey = () => (shadow ? \"traffic-shadow\" : \"traffic-glow\");\r\n  return m;\r\n}\r\n\r\nfunction decoGeometry(T, { trail }) {\r\n  const pos = [], dk = [], idx = [], N = 8;\r\n  if (!trail) { pos.push(-1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, 1); for (let i = 0; i < 4; i++) dk.push(0, 0, 0); idx.push(0, 2, 1, 1, 2, 3); }\r\n  else {\r\n    // pool quad (mode 0) first, then the trail strip (mode 1)\r\n    pos.push(-1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, 1); for (let i = 0; i < 4; i++) dk.push(0, 0, 0); idx.push(0, 2, 1, 1, 2, 3);\r\n    for (let k = 0; k <= N; k++) for (const side of [-1, 1]) { pos.push(0, 0, 0); dk.push(1, k / N, side); }\r\n    for (let k = 0; k < N; k++) { const a = 4 + 2 * k; idx.push(a, a + 1, a + 2, a + 1, a + 3, a + 2); }\r\n  }\r\n  const g = new T.BufferGeometry();\r\n  g.setAttribute(\"position\", new T.Float32BufferAttribute(pos, 3)); g.setAttribute(\"dk\", new T.Float32BufferAttribute(dk, 3)); g.setIndex(idx);\r\n  return g;\r\n}\r\n\r\n// ---- traffic ----------------------------------------------------------------------------------------------------------------------------\r\nexport function createTraffic(T, { roads, heightAt, count = MAX_SHIPS, scale = 1, models, hull, hover = HOVER, resolution = [320, 180], seed = 7 } = {}) {\r\n  const hulls = models?.length ? models : [{ id: \"wedge\", hull: hull || wedgeHull(T) }];\r\n  const paths = [];\r\n  for (const r of roads || []) {\r\n    if (!isFreeway(r.class) || r.tunnel || !r.pts || r.pts.length < 2) continue;\r\n    const y = roadY(r, heightAt), c = new T.CatmullRomCurve3(r.pts.map(([x, z]) => new T.Vector3(x, 0, z)), false, \"centripetal\");\r\n    const len = c.getLength(), pts = c.getSpacedPoints(PATH_SAMPLES - 1).map((p) => new T.Vector3(p.x, y(p.x, p.z) + hover, p.z));\r\n    paths.push({ pts, len, half: (WIDTH[r.class] ?? 14) / 2 });\r\n  }\r\n  const group = new T.Group(); group.name = \"world-traffic\"; group.scale.setScalar(scale);\r\n  const n = paths.length ? Math.min(MAX_SHIPS, Math.max(0, count)) : 0, rows = Math.max(1, paths.length), M = hulls.length;\r\n  const tex = new T.DataTexture(new Float32Array(PATH_SAMPLES * rows * 4), PATH_SAMPLES, rows, T.RGBAFormat, T.FloatType);\r\n  paths.forEach((p, row) => p.pts.forEach((v, i) => tex.image.data.set([v.x, v.y, v.z, 1], (row * PATH_SAMPLES + i) * 4)));\r\n  tex.magFilter = tex.minFilter = T.NearestFilter; tex.generateMipmaps = false; tex.needsUpdate = true;\r\n  const u = { uPath: { value: tex }, uTime: { value: 0 }, uHover: { value: hover } };\r\n  let rnd = seed; const rand = () => (rnd = (rnd * 16807) % 2147483647) / 2147483647;\r\n  const totalLen = paths.reduce((s, p) => s + p.len, 0);\r\n  // per ship: path row, phase, speed, lane, kind, scale, model; models are spread so every kind meets every model\r\n  const ships = [];\r\n  for (let i = 0; i < n; i++) {\r\n    let pick = (i + 0.5) / n * totalLen, row = 0; while (row < paths.length - 1 && pick > paths[row].len) pick -= paths[row++].len;\r\n    const kind = MIX[i % MIX.length], model = (i + Math.floor(i / MIX.length)) % M, dir = i % 2 ? -1 : 1, sc = SCALE[kind][0] + rand() * (SCALE[kind][1] - SCALE[kind][0]);\r\n    const mps = (kind === 2 ? 11 : 15) + rand() * 12;\r\n    ships.push({ row, phase: rand(), speed: dir * mps / paths[row].len, lane: paths[row].half * (0.2 + 0.55 * rand()), kind, scale: sc, model, i });\r\n  }\r\n  const fleets = hulls.map((h) => buildFleet(T, h.hull)), color = new T.Color(), meshes = [], geoms = [...fleets];\r\n  const fill = (list) => ({ aShip: new T.InstancedBufferAttribute(Float32Array.from(list.flatMap((s) => [s.row, s.phase, s.speed, s.lane])), 4), aKind: new T.InstancedBufferAttribute(Float32Array.from(list.map((s) => s.kind)), 1), aScale: new T.InstancedBufferAttribute(Float32Array.from(list.map((s) => s.scale)), 1) });\r\n  const shipMat = shipMaterial(T, resolution, u), hullMeshes = [];\r\n  fleets.forEach((fg, m) => {\r\n    const list = ships.filter((s) => s.model === m); if (!list.length) return;\r\n    const a = fill(list); for (const k in a) fg.setAttribute(k, a[k]);\r\n    const mesh = new T.InstancedMesh(fg, shipMat, list.length); mesh.name = `traffic-${hulls[m].id}`;\r\n    list.forEach((s, j) => mesh.setColorAt(j, color.setRGB(...livery(s.kind, s.i))));\r\n    mesh.renderOrder = 3; hullMeshes.push(mesh);\r\n  });\r\n  const glowG = decoGeometry(T, { trail: true }), shadowG = decoGeometry(T, { trail: false }), all = fill(ships);\r\n  for (const g of [glowG, shadowG]) for (const k in all) g.setAttribute(k, all[k]);\r\n  geoms.push(glowG, shadowG);\r\n  const glow = new T.InstancedMesh(glowG, decoMaterial(T, u, { shadow: false }), n), shadow = new T.InstancedMesh(shadowG, decoMaterial(T, u, { shadow: true }), n);\r\n  ships.forEach((s, i) => { glow.setColorAt(i, color.setRGB(...GLOW[s.kind])); shadow.setColorAt(i, color.setRGB(0, 0, 0)); });\r\n  shadow.renderOrder = 1; glow.renderOrder = 2;\r\n  meshes.push(shadow, glow, ...hullMeshes);\r\n  for (const m of meshes) { m.frustumCulled = false; m.instanceMatrix.needsUpdate = true; if (m.instanceColor) m.instanceColor.needsUpdate = true; group.add(m); }\r\n  return {\r\n    group, meshes, mesh: hullMeshes[0], hullMeshes, ships, hullSource: hulls[0].hull?.userData?.source ?? \"wedge\",\r\n    update(tSeconds) { u.uTime.value = tSeconds; },\r\n    stats: () => ({ drawCalls: n ? meshes.length : 0, ships: n, paths: paths.length, models: hullMeshes.length, hullTriangles: fleets.map((f) => f.attributes.position.count / 3) }),\r\n    time: u.uTime,\r\n    dispose() { for (const g of geoms) g.dispose(); shipMat.dispose(); for (const m of [glow, shadow]) m.material.dispose(); tex.dispose(); group.clear(); },\r\n  };\r\n}\r\n","modules/coach-world/art/manifest.json":"{\r\n  \"version\": 1,\r\n  \"size\": 64,\r\n  \"kinds\": [\r\n    \"hq\",\r\n    \"hall\",\r\n    \"building\",\r\n    \"ship\",\r\n    \"resource\",\r\n    \"shop\",\r\n    \"node\",\r\n    \"rubble\",\r\n    \"crew\",\r\n    \"production\",\r\n    \"expedition\",\r\n    \"foundation\",\r\n    \"drop\"\r\n  ],\r\n  \"variants\": 5,\r\n  \"factions\": {\r\n    \"chest\": {\r\n      \"motif\": \"Faceted wedge crowns\",\r\n      \"shape\": \"wedge\",\r\n      \"material\": \"beveled ceramic armor\",\r\n      \"rule\": \"Crest and diamond planes over broad shoulders and a narrow waist.\",\r\n      \"png\": \"chest.png\",\r\n      \"source\": \"chest.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"quads\": {\r\n      \"motif\": \"Stacked oval pylons\",\r\n      \"shape\": \"pylon\",\r\n      \"material\": \"segmented load-bearing stone\",\r\n      \"rule\": \"Egg-shaped drums taper upward from paired broad foundations.\",\r\n      \"png\": \"quads.png\",\r\n      \"source\": \"quads.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"glutes\": {\r\n      \"motif\": \"Pearl pod vaults\",\r\n      \"shape\": \"pod\",\r\n      \"material\": \"rounded living pearl\",\r\n      \"rule\": \"Stacked positive-volume spheres, bubble joints and a heavy lower core.\",\r\n      \"png\": \"glutes.png\",\r\n      \"source\": \"glutes.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"arms-shoulders\": {\r\n      \"motif\": \"Branching canopy foundries\",\r\n      \"shape\": \"canopy\",\r\n      \"material\": \"ribbed shell and mechanical branches\",\r\n      \"rule\": \"One shoulder canopy carries paired radial arms.\",\r\n      \"png\": \"arms-shoulders.png\",\r\n      \"source\": \"arms-shoulders.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"yoga\": {\r\n      \"motif\": \"Suspended curl sanctuaries\",\r\n      \"shape\": \"curl\",\r\n      \"material\": \"flowing translucent ceramic\",\r\n      \"rule\": \"Unplanted S stems and hooked ribbon tips float above their foundations.\",\r\n      \"png\": \"yoga.png\",\r\n      \"source\": \"yoga.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"martial-arts\": {\r\n      \"motif\": \"Folded plate bastions\",\r\n      \"shape\": \"fan\",\r\n      \"material\": \"creased armored plates\",\r\n      \"rule\": \"Blunt monoliths unfold into ribbed fan helmets on compact braced feet.\",\r\n      \"png\": \"martial-arts.png\",\r\n      \"source\": \"martial-arts.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    },\r\n    \"cardio\": {\r\n      \"motif\": \"Open-core relay towers\",\r\n      \"shape\": \"ring\",\r\n      \"material\": \"light skeletal alloy\",\r\n      \"rule\": \"Negative-space rings, suspended discs and lean open struts.\",\r\n      \"png\": \"cardio.png\",\r\n      \"source\": \"cardio.piskel\",\r\n      \"frames\": [\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            0,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            64,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            128,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            192,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            256,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            320,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            384,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            448,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            512,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            576,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            640,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            704,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 0,\r\n          \"frame\": [\r\n            768,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            832,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            896,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            960,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1024,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1088,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1152,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1216,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1280,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1344,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1408,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1472,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1536,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 1,\r\n          \"frame\": [\r\n            1600,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1664,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1728,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1792,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1856,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1920,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            1984,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2048,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2112,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2176,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2240,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2304,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2368,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 2,\r\n          \"frame\": [\r\n            2432,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2496,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2560,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2624,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2688,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2752,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2816,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2880,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            2944,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3008,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3072,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3136,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3200,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 3,\r\n          \"frame\": [\r\n            3264,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hq\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3328,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"hall\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3392,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"building\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3456,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"ship\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3520,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"resource\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3584,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"shop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3648,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"node\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3712,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"rubble\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3776,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"crew\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3840,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"production\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3904,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"expedition\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            3968,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"foundation\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4032,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        },\r\n        {\r\n          \"kind\": \"drop\",\r\n          \"variant\": 4,\r\n          \"frame\": [\r\n            4096,\r\n            0,\r\n            64,\r\n            64\r\n          ]\r\n        }\r\n      ]\r\n    }\r\n  }\r\n}\r\n","modules/coach-world/authorized-friends.mjs":"// Ephemeral permission-scoped rendering input. No storage or public-map cache.\r\nimport {distanceMeters,validCoordinate} from './geo.mjs';\r\nimport {localOffset} from './public-geography.mjs';\r\nimport {FACTION_BY_ID} from './catalog.mjs';\r\nexport function authorizedFriendLandmarks(friends,anchor){\r\n if(!anchor||!validCoordinate(anchor.latitude,anchor.longitude))return [];\r\n const result=[];for(const friend of Array.isArray(friends)?friends:[]){const h=friend?.home;if(!h||h.consent!==true||!validCoordinate(h.latitude,h.longitude)||typeof friend.publicId!=='string'||!friend.base||!Object.hasOwn(FACTION_BY_ID,friend.base.faction)||distanceMeters(anchor,h)>2000)continue;const levels=Object.values(friend.base.buildings||{}).map(b=>b?.level).filter(l=>Number.isInteger(l)&&l>=1&&l<=10);result.push({...localOffset(anchor,h.longitude,h.latitude),faction:friend.base.faction,level:Math.max(1,...levels)});if(result.length===8)break;}return result;\r\n}\r\n","modules/coach-world/base-systems.mjs":"// Pure derived rules. No saved fields, clock, rewards or client authority.\r\nimport {BUILDINGS,BUILDING_BY_ID,MAX_LEVEL,hangarShipTier} from './catalog.mjs';\r\nimport {researchEffects,buildingHealth} from './rts-tech.mjs';\r\nexport const SYSTEMS_VERSION=1;\r\nexport const DEFENSE_SYSTEM_KINDS=['tower','press','mortar','beacon','trap','wall','fence','generator'];\r\nexport function buildingFunction(base,definition){\r\n const d=typeof definition==='string'?BUILDING_BY_ID[definition]:BUILDING_BY_ID[definition?.id];\r\n if(!d)return null;\r\n const b=base?.buildings?.[d.id],level=Number.isInteger(b?.level)?Math.max(0,Math.min(MAX_LEVEL,b.level)):0;\r\n // Existing completed levels stay online during upgrades; scaffolding has no effect.\r\n // A building beaten to 0 HP in a server drill is offline until repaired (rts-tech.mjs); research add-ons boost their host.\r\n const tech=researchEffects(base),active=level>0&&!tech.offlineBuildingIds.includes(d.id),effects={};\r\n if(active)switch(d.kind){\r\n case 'tower':Object.assign(effects,{baseDamage:80,baseHp:3000,attackRange:512,attackPeriod:10,antiAir:true,towerDamageBonus:12*(level-1),towerHpBonus:250*(level-1)});break;\r\n case 'press':Object.assign(effects,{damage:45*level,period:16,range:900,armorPiercing:true});break;\r\n case 'mortar':Object.assign(effects,{damage:25*level,period:35,range:2304,minRange:512,splashRadius:512,groundOnly:true});break;\r\n case 'beacon':Object.assign(effects,{heal:12*level,period:20,range:1280});break;\r\n case 'trap':Object.assign(effects,{damage:300*level,range:768,slowPct:40,slowTicks:15+5*level,charges:1,groundOnly:true});break;\r\n case 'wall':effects.garrisonHp=25*level;break;\r\n case 'fence':{effects.garrisonArmor=3*level;effects.garrisonHp=120*level;}break;\r\n case 'generator':effects.defenseDamagePct=5*level;effects.launchFuelSaving=level;break;\r\n case 'silo':effects.materialDiscountPct=2*level;break;\r\n case 'yard':effects.trainingSpeedPct=5*level;break;\r\n case 'care':effects.rationFuelBonus=3*level;break;\r\n case 'observatory':effects.meditationCalmBonus=3*level;effects.journeySpeedPct=2*level;break;\r\n case 'habitat':effects.shipSupply=level;break;\r\n case 'production':effects.trainingSpeedPct=4*(level-1)+(tech.facilityTrainingSpeedPct[d.id]||0);effects.unlockLevel=level+(tech.advancedCompanionFacilities.includes(d.id)?1:0);break;\r\n case 'expedition':effects.outboundBaseMs=(6-level)*60000;break;\r\n case 'hangar':effects.shipTier=hangarShipTier(level);break;\r\n case 'hq':effects.techLevel=level;effects.movementSpeedPct=10*(level-1);break; // provisional tuning: +10% trip movement per completed HQ level above 1 (capped in baseSystems)\r\n case 'hall':effects.coachTier=level;break;\r\n case 'landing':effects.packAccess=true;break;\r\n }\r\n if(active&&['care','observatory','habitat','hangar'].includes(d.kind))effects.hqSupportLevels=level;\r\n return {id:d.id,kind:d.kind,level,active,status:!active?(level>0?'offline':b?.job?'building':'unbuilt'):b?.job?'upgrading':'operational',rest:base?.rest===true,health:buildingHealth(base,d.id),effects};\r\n}\r\nexport function baseSystems(base){\r\n const profiles=BUILDINGS.map(d=>buildingFunction(base,d)),tech=researchEffects(base);\r\n const sum=k=>profiles.reduce((n,p)=>n+(p.effects[k]||0),0);\r\n return {version:SYSTEMS_VERSION,profiles,materialDiscountPct:Math.min(20,sum('materialDiscountPct')),trainingSpeedPct:Math.min(25,profiles.filter(p=>p.kind==='yard').reduce((n,p)=>n+(p.effects.trainingSpeedPct||0),0)+tech.trainingSpeedPct),rationFuelBonus:sum('rationFuelBonus'),meditationCalmBonus:sum('meditationCalmBonus'),journeySpeedPct:sum('journeySpeedPct')+tech.journeySpeedPct,movementSpeedPct:Math.min(40,sum('movementSpeedPct')),shipSupply:Math.min(5,sum('shipSupply')),launchFuelSaving:sum('launchFuelSaving'),weaponUpgrade:tech.weaponUpgrade,armorUpgrade:tech.armorUpgrade};\r\n}\r\nexport const discountedCost=(cost,pct)=>cost===0?0:Math.max(1,Math.ceil(cost*(100-pct)/100));\r\nexport function trainingQuote(base,unit){\r\n const s=baseSystems(base),p=buildingFunction(base,unit?.buildingId),speed=Math.min(50,s.trainingSpeedPct+(p?.effects.trainingSpeedPct||0));\r\n return {materialCost:discountedCost(unit.materialCost,s.materialDiscountPct),trainingMs:Math.max(30000,Math.ceil(unit.trainingMs*(100-speed)/100))};\r\n}\r\n// Infrastructure has its own bounded channel, never competes with the eight troop slots.\r\nexport function defenseSystems(base,arena){\r\n return BUILDINGS.flatMap(d=>{const p=buildingFunction(base,d);if(!p.active||!DEFENSE_SYSTEM_KINDS.includes(d.kind))return [];\r\n const b=base.buildings[d.id],map=d.kind!=='tower'&&arena.defenseArena||arena.towersArena,[[x,y]]=map([{x:(b.x??d.x)+d.w/2,y:(b.y??d.y)+d.h/2}]);\r\n return [{id:d.id,kind:d.kind,level:p.level,x,y}];});\r\n}\r\n","modules/coach-world/blueprint-entitlements.mjs":"﻿import { BUILDINGS, BUILDING_BY_ID } from './catalog.mjs';\n\nconst SAFE_ID = /^[A-Za-z0-9:_-]{1,128}$/;\nconst own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);\nconst isRecord = value => value !== null && typeof value === 'object' && !Array.isArray(value)\n  && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);\nconst isId = value => typeof value === 'string' && SAFE_ID.test(value);\nconst fail = message => { throw new TypeError(message); };\n\n// Catalog metadata names real buildings only; membership here never grants eligibility.\nexport const BLUEPRINT_CATALOG = Object.freeze(BUILDINGS.map(building => Object.freeze({\n  id: `blueprint:${building.id}`,\n  buildingId: building.id,\n  label: building.label,\n})));\nexport const BLUEPRINT_BY_ID = Object.freeze(Object.fromEntries(BLUEPRINT_CATALOG.map(item => [item.id, item])));\nconst BUILDING_IDS = new Set(Object.keys(BUILDING_BY_ID));\nconst BLUEPRINT_IDS = new Set(BLUEPRINT_CATALOG.map(item => item.id));\n\nfunction scope(accountId, dataEpoch) {\n  if (typeof accountId !== 'string' || accountId.length < 1 || accountId.length > 160 || !/^[A-Za-z0-9:_-]+$/.test(accountId)) fail('accountId must be a nonempty bounded id');\n  if (!Number.isSafeInteger(dataEpoch) || dataEpoch < 1) fail('dataEpoch must be a positive safe integer');\n}\n\nfunction assertExactKeys(value, keys, label) {\n  if (!isRecord(value) || Object.keys(value).length !== keys.length || keys.some(key => !own(value, key))) fail(`invalid ${label}`);\n}\n\nfunction validateState(state, accountId, dataEpoch) {\n  scope(accountId, dataEpoch);\n  assertExactKeys(state, ['version', 'accountId', 'dataEpoch', 'owned', 'receipts'], 'blueprint entitlement state');\n  if (state.version !== 1 || state.accountId !== accountId || state.dataEpoch !== dataEpoch\n      || !Array.isArray(state.owned) || !Array.isArray(state.receipts)) fail('blueprint entitlement scope mismatch');\n  const owned = new Set();\n  for (const id of state.owned) {\n    if (typeof id !== 'string' || !BLUEPRINT_IDS.has(id) || owned.has(id)) fail('invalid owned blueprint');\n    owned.add(id);\n  }\n  const receipts = new Map();\n  for (const receipt of state.receipts) {\n    assertExactKeys(receipt, ['key', 'runId', 'visitId', 'chestId', 'buildingId', 'at'], 'blueprint receipt');\n    if (![receipt.runId, receipt.visitId, receipt.chestId].every(isId)\n        || !BUILDING_IDS.has(receipt.buildingId)\n        || !Number.isSafeInteger(receipt.at) || receipt.at < 0) fail('invalid blueprint receipt fields');\n    const key = JSON.stringify([receipt.runId, receipt.visitId, receipt.chestId]);\n    if (receipt.key !== key || receipts.has(key)) fail('invalid or duplicate blueprint receipt key');\n    receipts.set(key, receipt.buildingId);\n  }\n  const receiptOwned = new Set([...receipts.values()].map(id => `blueprint:${id}`));\n  if (receiptOwned.size !== owned.size || [...owned].some(id => !receiptOwned.has(id))) fail('blueprint ledger is inconsistent');\n  return receipts;\n}\n\nexport function createBlueprintEntitlements({ accountId, dataEpoch } = {}) {\n  scope(accountId, dataEpoch);\n  return { version: 1, accountId, dataEpoch, owned: [], receipts: [] };\n}\n\nexport function restoreBlueprintEntitlements(saved, { accountId, dataEpoch } = {}) {\n  validateState(saved, accountId, dataEpoch);\n  return structuredClone(saved);\n}\n\n/** Pure entitlement ledger only. Future authenticated server callers own eligibility and persistence. */\nexport function grantBlueprintEntitlement(state, { accountId, dataEpoch, runId, visitId, chestId, buildingId, now } = {}) {\n  let priorReceipts;\n  try {\n    priorReceipts = validateState(state, accountId, dataEpoch);\n  } catch (error) {\n    return { ok: false, reason: 'invalid-state', state, granted: false, receipt: null, error: error.message };\n  }\n  if (![runId, visitId, chestId].every(isId)) return { ok: false, reason: 'invalid-id', state, granted: false, receipt: null };\n  if (!BUILDING_IDS.has(buildingId)) return { ok: false, reason: 'unknown-building', state, granted: false, receipt: null };\n  if (!Number.isSafeInteger(now) || now < 0) return { ok: false, reason: 'invalid-time', state, granted: false, receipt: null };\n\n  const key = JSON.stringify([runId, visitId, chestId]);\n  const priorBuilding = priorReceipts.get(key);\n  if (priorBuilding !== undefined) {\n    if (priorBuilding !== buildingId) return { ok: false, reason: 'receipt-conflict', state, granted: false, receipt: null };\n    const receipt = state.receipts.find(item => item.key === key);\n    return { ok: true, reason: 'duplicate', state, granted: false, receipt };\n  }\n\n  const next = structuredClone(state);\n  const receipt = { key, runId, visitId, chestId, buildingId, at: now };\n  next.receipts.push(receipt);\n  const blueprintId = `blueprint:${buildingId}`;\n  const granted = !next.owned.includes(blueprintId);\n  if (granted) next.owned.push(blueprintId);\n  return { ok: true, reason: granted ? 'granted' : 'already-owned', state: next, granted, receipt };\n}\r\n\r\n","modules/coach-world/blueprint-policy.mjs":"import {BUILDINGS,BUILDING_BY_ID} from './catalog.mjs';\n\n// New-account policy. Saves without this marker keep their existing access.\n// Core construction remains broad at level one; materials and tech prerequisites still apply.\nexport const BLUEPRINT_POLICY_VERSION=1;\nconst CORE_KINDS=new Set(['hq','landing','hall','care','observatory','habitat','hangar','production','trap','fence']);\nexport const BLUEPRINT_DROP_BUILDING_IDS=Object.freeze(BUILDINGS.filter(b=>!CORE_KINDS.has(b.kind)).map(b=>b.id));\nexport function buildingBlueprintStatus(base,buildingId){\n if(!Object.hasOwn(BUILDING_BY_ID,buildingId))throw new TypeError('Unknown blueprint building');\n const owned=base.blueprintEntitlements?.owned?.includes(`blueprint:${buildingId}`)===true;\n const required=base.blueprintPolicyVersion===BLUEPRINT_POLICY_VERSION&&BLUEPRINT_DROP_BUILDING_IDS.includes(buildingId)\n  &&(base.buildings?.[buildingId]?.level??0)===0;\n return {id:`blueprint:${buildingId}`,owned,required,available:!required||owned};\n}\n","modules/coach-world/camp-rescue-view.mjs":"import {projectCoachPedestals} from '../coach-world-rts/coach-pedestals.mjs';\nimport {canonicalCoachDefinition} from './coach-manifest.mjs';\nimport {captureGameGeneration,subscribeGameGeneration} from './game-generation-client.mjs';\n\n// Presentation only: never write this borrowed base to the world command/save path.\nexport function projectCampRescues(base,hearts){\n const owned=new Map();\n if(hearts?.version===1&&Array.isArray(hearts.owned))for(const heart of hearts.owned){\n  if(canonicalCoachDefinition(heart?.coachId)&&heart.heartId===`heart:${heart.coachId}`\n   &&typeof heart.name==='string'&&[heart.speedMultiplier,heart.damageMultiplier,heart.initialArmorBonus].every(Number.isFinite))owned.set(heart.coachId,heart);\n }\n const presentationBase={...base,coachAccess:[...new Set([...(base?.coachAccess||[]),...owned.keys()])]};\n return {base:presentationBase,coaches:projectCoachPedestals(presentationBase).filter(row=>row.earned&&owned.has(row.coachId)).map(row=>({...row,heart:owned.get(row.coachId)}))};\n}\n\nconst accountKey=a=>JSON.stringify([a?.user?.id,a?.dataEpoch,a?.gameGeneration]);\nexport function mountCampRescueView(host,{getBase,getAccount=()=>globalThis.myr5AuthenticatedAccount,openHeart=()=>{},signal,\n createRunClient=async options=>(await import('../roguelite/world-run-client.mjs')).createWorldRunClient(options),onInvalidate=()=>{}}){\n const doc=host.ownerDocument,scope=accountKey(getAccount());let disposed=false,client=null,hearts=null,generation=null,serial=0;\n const make=(tag,text)=>{const n=doc.createElement(tag);if(text)n.textContent=text;return n;};\n const section=make('section'),title=make('h3','Rescued coaches'),message=make('p','Checking your saved rescues…'),list=make('div'),button=make('button','Insert a Coach Heart');\n section.className='cw-card';section.dataset.campRescues='';message.className='cw-note';list.setAttribute('aria-label','Rescued coaches at camp');\n Object.assign(list.style,{display:'flex',flexWrap:'wrap',gap:'8px'});button.type='button';button.onclick=()=>{if(live())openHeart();};\n section.append(title,message,list,button);host.append(section);\n const live=()=>!disposed&&!signal?.aborted&&accountKey(getAccount())===scope&&(generation===null||captureGameGeneration()===generation);\n function invalidate(){if(disposed)return;hearts=null;list.replaceChildren();button.disabled=true;message.textContent='Account or game progress changed. Reopen camp.';dispose();onInvalidate();}\n function check(){if(!live())invalidate();}\n function render(){\n  list.replaceChildren();const rows=projectCampRescues(getBase(),hearts).coaches;\n  message.textContent=rows.length?`${rows.length} rescued coaches are home. Their statues stand on the camp pedestals.`:'Rescue a coach to bring them home and unlock their statue and Heart.';\n  for(const row of rows){\n   const card=make('figure'),canvas=make('canvas'),label=make('figcaption',row.canonicalLabel);card.dataset.campCoach=row.coachId;card.style.margin='0';card.style.width='96px';\n   canvas.width=canvas.height=64;canvas.setAttribute('role','img');canvas.setAttribute('aria-label',row.canonicalLabel);canvas.style.imageRendering='pixelated';canvas.style.width=canvas.style.height='64px';\n   label.style.fontSize='12px';card.append(canvas,label);list.append(card);\n   if(row.visual){const image=doc.createElement('img');image.onload=()=>{if(!live()||!canvas.isConnected)return;const [x,y,w,h]=row.visual.frame;\n    if(x+w>image.naturalWidth||y+h>image.naturalHeight)return;const context=canvas.getContext('2d'),k=Math.min(64/w,64/h);context.imageSmoothingEnabled=false;context.drawImage(image,x,y,w,h,(64-w*k)/2,(64-h*k)/2,w*k,h*k);};image.src=row.visual.atlasPath;}\n  }\n }\n async function refresh(){\n  const token=++serial;if(!getAccount()?.user?.id){message.textContent='Sign in to see your saved rescued coaches.';button.disabled=true;return;}\n  try{client??=await createRunClient({getAccount});if(!live()||token!==serial){client?.dispose();return;}\n   const value=await client.current();if(!live()||token!==serial)return;\n   const account=getAccount();generation=captureGameGeneration();\n   if(value.targetAccountId!==account.user.id||value.dataEpoch!==account.dataEpoch||value.gameGeneration!==generation)throw Error('Saved rescue scope changed. Reopen camp.');\n   hearts=value.hearts??value.envelope?.hearts??null;render();\n  }catch(error){if(!live()||token!==serial)return;hearts=null;list.replaceChildren();message.textContent=error.message||'Saved rescues unavailable. Reopen camp to retry.';}\n }\n const off=subscribeGameGeneration(()=>invalidate()),watch=setInterval(check,1000);\n globalThis.addEventListener?.('myr5:account-ready',check);globalThis.addEventListener?.('myr5:auth-transition',check);signal?.addEventListener('abort',dispose,{once:true});\n function dispose(){if(disposed)return;disposed=true;serial++;hearts=null;client?.dispose();off();clearInterval(watch);signal?.removeEventListener('abort',dispose);globalThis.removeEventListener?.('myr5:account-ready',check);globalThis.removeEventListener?.('myr5:auth-transition',check);section.remove();}\n const ready=refresh();return {ready,refresh,getBase:()=>live()?projectCampRescues(getBase(),hearts).base:{...getBase(),coachAccess:[]},dispose};\n}\n","modules/coach-world/canvas-scene.mjs":"import {createDragMove,movable} from '../coach-world-rts/drag-move.mjs';\r\nimport {BUILDINGS,FACTIONS,FACTION_BY_ID,MAX_LEVEL} from './catalog.mjs';\r\nimport {createWanderers,stepWanderers,occupancy,findPath} from './navigation.mjs';\r\nimport {BASIC_WORKER,SECTION_COMPANIONS,UNIT_BY_ID} from './unit-catalog.mjs';\r\nimport {unitRoster} from './expedition-catalog.mjs';\r\nimport {unitInstances} from './production.mjs';\r\nimport {getSpriteCanvas} from './pixel-art.mjs';\r\nimport {createTerrain,drawTerrain} from './terrain.mjs';\r\nimport {createTouchGesture,unitsInRectangle,rallyUnits,stepRalliedUnits} from './touchselection.mjs';\r\nimport {createOrderController,ORDER_TYPES} from './orders.mjs';\r\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\r\nexport function drawBuilding(ctx,b,f,x,y,size,{selected=false}={}){\r\n const level=Number.isInteger(b.level)?b.level:1,w=size*(b.w||3),h=size*(b.h||3),pixels=w<48?32:64*Math.max(1,Math.round(w/64)),left=Math.round(x+w/2-pixels/2),top=Math.round(y+h-pixels),kind=level===0?'foundation':['hq','hall','production','expedition','care','observatory','habitat','hangar'].includes(b.kind)?b.kind:'building';\r\n ctx.imageSmoothingEnabled=false;ctx.drawImage(getSpriteCanvas(f.id,kind,Math.max(0,Math.min(4,level-1))),left,top,pixels,pixels);\r\n if(b.job){ctx.fillStyle='#eab46f';for(let i=0;i<5;i++)ctx.fillRect(left+3+i*(pixels-6)/5,top+pixels-4,3,2);}\r\n if(selected){ctx.strokeStyle='#fff4b8';ctx.lineWidth=1;ctx.strokeRect(left-2,top-2,pixels+4,pixels+4);}\r\n ctx.font='bold 8px monospace';const label=level===0?'SITE':`${level}/${MAX_LEVEL}`,width=ctx.measureText(label).width+6;ctx.fillStyle='#09171ee8';ctx.fillRect(Math.round(x+w/2-width/2),Math.round(y+h-3),width,11);ctx.fillStyle=level===0?'#b7c8c5':f.accent;ctx.textAlign='center';ctx.fillText(label,Math.round(x+w/2),Math.round(y+h+5));ctx.textAlign='start';\r\n}\r\nexport function constructionCountdown(dueAt,now=Date.now()){if(!Number.isFinite(dueAt)||!Number.isFinite(now))return '';const seconds=Math.max(0,Math.ceil((dueAt-now)/1000));if(!seconds)return 'Ready to finish';return `${Math.floor(seconds/60)}m ${String(seconds%60).padStart(2,'0')}s`;}\r\nconst productionUnits=base=>unitInstances(base).map(instance=>{const def=UNIT_BY_ID[instance.visualId];return def?{...def,section:instance.faction,role:instance.role==='hauler'?'carrier':instance.role,instanceKey:instance.instanceId,productionUnitId:instance.unitId,away:instance.away}:null;}).filter(Boolean);\r\nexport function sceneUnitDefinitions(input={}, {rts=false,fight=false}={}){const base=input.buildings?input:{coachAccess:input.coaches||[],buildings:Object.fromEntries(FACTIONS.map(f=>[`hall-${f.id}`,{level:5}]))},basics=rts?Array.from({length:Math.min(320,Math.max(0,Number(input.workers)||0))},(_,i)=>({...BASIC_WORKER,section:FACTIONS[i%FACTIONS.length].id,instanceKey:`worker#${i+1}`})):FACTIONS.map(f=>({...BASIC_WORKER,section:f.id})),companions=fight?[]:rts?productionUnits(base):FACTIONS.flatMap(f=>SECTION_COMPANIONS[f.id]||[]),coaches=fight?[]:unitRoster(base).filter(u=>u.owned&&['grunt','boss'].includes(u.role)).map(u=>UNIT_BY_ID[u.visualId]).filter(Boolean);return [...basics,...companions,...coaches];}\r\nexport function isSceneUnitAway(unit,base){const m=base.expedition;if(!m||!['launched','returning'].includes(m.status))return false;if(unit.role==='worker')return unit.section===m.factionId&&m.unitIds.includes('myr5-worker')&&(!unit.instanceKey||unit.instanceKey==='worker#1');if(unit.productionUnitId)return unit.instanceKey?.endsWith('#1')&&m.unitIds.includes(unit.productionUnitId);const id=unit.role==='scout'?`companion-${unit.section}-scout`:unit.role==='carrier'?`companion-${unit.section}-hauler`:['boss','grunt'].includes(unit.role)?`${unit.role}-${unit.section}`:null;return !!id&&m.unitIds.includes(id)&&(!unit.instanceKey||unit.instanceKey.endsWith('#1'));}\r\nconst crewSignature=base=>JSON.stringify([base?.workers,base?.units,base?.coachAccess,Object.entries(base?.buildings||{}).filter(([id])=>id.startsWith('hall-')).map(([id,b])=>[id,b.level])]);\r\nexport function mountBaseScene(canvas,{getBase,onSelect=()=>{},onEnterWarRoom=()=>{},onSelectionChange=()=>{},onRally=()=>{},getServerTime=()=>Date.now(),rts=null,command=null}){\r\n const ctx=canvas.getContext('2d'),pointers=new Map(),abort=new AbortController(),definitions=sceneUnitDefinitions(getBase()||{},{rts:!!rts}),crew=createWanderers(definitions.length),selectedUnits=new Set(),atlases=new Map(),images=[];\r\n let raf=0,disposed=false,last=0,scale=1,pan={x:0,y:0},pinch=0,selected='hq',transition=null,entryFinished=false,terrain=null,terrainKey='',initialized=false,selectionArmed=false,crewKey=crewSignature(getBase()),fitKey='',fitScale=1;const stableIds=new Map();let nextStableId=1000;\r\n const safe=rts?(name,...args)=>{try{return rts[name]?.(...args);}catch(e){console.warn('coach-world rts',name,e);return undefined;}}:()=>undefined;\r\n const selectedInfo=unit=>({...unit.definition,instanceId:unit.definition.instanceKey||unit.id,selectionId:unit.id});\r\n const notify=()=>onSelectionChange({count:selectedUnits.size,ids:[...selectedUnits],units:crew.filter(u=>!u.away&&selectedUnits.has(u.id)).map(selectedInfo),selectionMode:selectionArmed});\r\n const orders=createOrderController({getUnits:()=>crew,getSelected:()=>[...selectedUnits],getBase,getTerrain:()=>terrain,selectGroup:ids=>{selectedUnits.clear();for(const id of ids)selectedUnits.add(id);selectionArmed=false;notify();},notify});let orderMode=null,patrolStart=null;\r\n const dm=command?createDragMove({getBase,command}):null;let dmOn=false,dmGhost=null; // R53 drag-move (2D): long-press a built idle building, ghost rect, release on green sends `move`\r\n const buildingTileAt=p=>{const tr=transform(),x=(p.x-tr.ox)/tr.unit,y=(p.y-tr.oy)/tr.unit,base=getBase();return {x,y,id:base&&[...BUILDINGS].reverse().find(d=>{const b={...d,...base.buildings[d.id]};return movable(base,d.id)&&x>=b.x&&x<=b.x+b.w&&y>=b.y-1&&y<=b.y+b.h;})?.id};};\r\n const endMove=drop=>{dmOn=false;dmGhost=null;delete canvas.dataset.troopGesture;if(drop&&dm.active)dm.drop();else dm?.cancel();};\r\n const gesture=createTouchGesture({onHold:()=>{try{navigator.vibrate?.(12);}catch{}const st=gesture.state,hit=dm&&st?buildingTileAt(st.start):null;if(hit?.id&&dm.begin(hit.id,hit)){dmOn=true;canvas.dataset.troopGesture='move';dmGhost=dm.hover(hit);return;}canvas.dataset.troopGesture='select';}});\r\n for(const path of new Set(definitions.map(u=>u.atlasPath))){const image=new Image();images.push(image);image.onload=()=>{if(!disposed)atlases.set(path,image);};image.src=path;}\r\n const box=()=>canvas.getBoundingClientRect();\r\n function transform(){const r=box(),unit=Math.min(r.width/26,r.height/25)*scale,normal={unit,ox:r.width/2-12*unit+pan.x,oy:r.height/2-11*unit+pan.y};if(!transition)return normal;const progress=clamp((performance.now()-transition.started)/450,0,1),ease=1-(1-progress)**3,targetUnit=transition.from.unit*3,hq=getBase()?.buildings?.hq||{x:10,y:9},target={unit:targetUnit,ox:r.width/2-(hq.x+2)*targetUnit,oy:r.height/2-(hq.y+2)*targetUnit};return Object.fromEntries(Object.keys(normal).map(k=>[k,transition.from[k]+(target[k]-transition.from[k])*ease]));}\r\n const localPoint=e=>{const r=box();return {x:e.clientX-r.left,y:e.clientY-r.top};};\r\n function terrainFor(base){const key=`${base.seed}:${base.faction}:`+Object.values(base.buildings).map(b=>`${b.id}:${b.x}:${b.y}:${b.level}:${!!b.job}`).join('|');if(key!==terrainKey){terrain=createTerrain(base);terrainKey=key;safe('release','all','terrain');for(const unit of crew){unit.path=[];unit.index=0;unit.rally=false;unit.hasOrder=false;}}return terrain;}\r\n function nearestFree(base,point){const blocked=occupancy(base);for(const key of terrain.blocked)blocked.add(key);let best=null,distance=Infinity;for(let y=0;y<24;y++)for(let x=0;x<24;x++)if(!blocked.has(y*24+x)){const d=Math.hypot(x-point.x,y-point.y);if(d<distance){distance=d;best={x,y};}}return best||{x:0,y:0};}\r\n function rebuildCrew(base){crewKey=crewSignature(base);safe('release','all','crew-rebuild');definitions.splice(0,definitions.length,...sceneUnitDefinitions(base,{rts:true}));crew.splice(0,crew.length,...createWanderers(definitions.length));selectedUnits.clear();initialized=false;terrainKey='';notify();}\r\n function initialize(base){if(initialized)return;initialized=true;for(let i=0;i<crew.length;i++){const definition=definitions[i],hall=base.buildings[`hall-${definition.section}`],def=BUILDINGS.find(d=>d.id===`hall-${definition.section}`),point=nearestFree(base,{x:(hall?.x||2)+(def?.w||3)/2,y:(hall?.y||2)+(def?.h||3)+1});let id=crew[i].id;if(rts){if(definition.role==='worker')id=Math.max(0,Number(/#(\\d+)$/.exec(definition.instanceKey||'')?.[1]||1)-1);else{const key=definition.instanceKey||`${definition.id||definition.role}:${definition.section}:${i}`;if(!stableIds.has(key))stableIds.set(key,nextStableId++);id=stableIds.get(key);}}Object.assign(crew[i],point,{id,definition,sectionAnchor:point,role:definition.role,rally:definition.role!=='worker',hasOrder:false});}}\r\n function enterWarRoom(){if(disposed||transition||entryFinished||!getBase())return false;gesture.cancel();selected='hq';if(matchMedia('(prefers-reduced-motion: reduce)').matches){entryFinished=true;onEnterWarRoom();return true;}transition={started:performance.now(),from:transform()};canvas.dataset.enteringWarRoom='true';canvas.setAttribute('aria-busy','true');return true;}\r\n function stopHarvestFor(ids){if(!command)return;const base=getBase(),workers=crew.filter(u=>ids.has(u.id)&&u.role==='worker'&&base?.rtsEconomy?.assignments?.[`crew-${u.id+1}`]).map(u=>`crew-${u.id+1}`);if(!workers.length)return;void(async()=>{for(const workerId of workers){if(disposed)return;const result=await command({type:'stop-gather',workerId});if(result?.ok===false)break;}})().catch(e=>console.warn('coach-world worker recall',e));}\r\n function rally(target){const base=getBase();if(!base||base.rest||!terrain||!selectedUnits.size)return 0;safe('release',[...selectedUnits],'rally');const count=rallyUnits(crew,selectedUnits,target,base,terrain.blocked);for(const unit of crew)if(selectedUnits.has(unit.id)&&unit.rally)unit.hasOrder=true;if(count)stopHarvestFor(selectedUnits);selectionArmed=false;notify();onRally({count,target});return count;}\r\n function patrol(base,t){for(const unit of crew){if(unit.away||unit.role==='worker'||unit.hasOrder||base.rest||safe('owns',unit))continue;if(unit.path?.[unit.index])continue;if(unit.patrolWait==null){unit.patrolWait=t+1100+unit.id*37;continue;}if(t<unit.patrolWait)continue;unit.patrolWait=null;const a=unit.sectionAnchor,offset=(unit.cycle++%5)-2,target=nearestFree(base,{x:a.x+offset,y:a.y+(unit.cycle%3)-1});rallyUnits([unit],new Set([unit.id]),target,base,terrain.blocked);}}\r\n function screenUnit(unit,tr){return {id:unit.id,x:tr.ox+(unit.x+.5)*tr.unit,y:tr.oy+(unit.y+.5)*tr.unit-terrain.heightAt(unit.x,unit.y)*2};}\r\n function drawUnit(unit,tr,t,base){const point=screenUnit(unit,tr),definition=unit.definition,atlas=atlases.get(definition.atlasPath),pet=definition.sheet==='pets',height=pet?(definition.role==='carrier'?16:13):definition.role==='boss'?34:definition.role==='worker'?20:24,width=Math.round(height*definition.frame[2]/definition.frame[3]),bob=base.rest?0:Math.sin(unit.phase)*1.2,f=FACTION_BY_ID[definition.section]||FACTIONS[0];if(safe('drawUnit',ctx,unit,point,tr))return;\r\n  ctx.fillStyle='#08121688';ctx.fillRect(Math.round(point.x-width/2),Math.round(point.y),width,3);ctx.strokeStyle=selectedUnits.has(unit.id)?'#fff0a6':f.color;ctx.lineWidth=selectedUnits.has(unit.id)?2:1;ctx.beginPath();ctx.ellipse(point.x,point.y+1,Math.max(5,width*.42),3,0,0,Math.PI*2);ctx.stroke();if(atlas){const pose=safe('poseFor',unit);ctx.save();ctx.translate(Math.round(point.x),Math.round(point.y-height+bob+(pose?height:0)));if(pose){ctx.translate(0,-pose.bodyY*height);ctx.rotate(pose.bodyZ);ctx.scale(1,pose.scaleY);ctx.translate(0,-height);}if(unit.facing==='left')ctx.scale(-1,1);ctx.drawImage(atlas,...definition.frame,-Math.round(width/2),0,width,height);ctx.restore();}else ctx.drawImage(getSpriteCanvas(f.id,'crew'),Math.round(point.x-height/2),Math.round(point.y-height),height,height);\r\n  if(unit.state==='building'){ctx.fillStyle='#ffd88b';ctx.fillRect(point.x+width/2,point.y-height+3,3,3);}if(base.rest){ctx.fillStyle='#c7d7bb';ctx.font='9px monospace';ctx.fillText('z',point.x+4,point.y-height);}if(unit.hasOrder&&unit.rallyTarget){ctx.fillStyle='#e9d58c';ctx.fillRect(tr.ox+(unit.rallyTarget.x+.5)*tr.unit-1,tr.oy+(unit.rallyTarget.y+.5)*tr.unit-1,3,3);}\r\n }\r\n function draw(t){if(disposed)return;raf=requestAnimationFrame(draw);if(t-last<33||document.hidden)return;const dt=Math.min(.1,(t-last)/1000);last=t;const r=box();if(!r.width||!r.height)return;if(rts&&fitKey!==r.width+'x'+r.height&&scale===fitScale){fitKey=r.width+'x'+r.height;fitScale=scale=Math.max(r.width/24,r.height/22)/Math.min(r.width/26,r.height/25);}const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;if(reduced)t=0;const dpr=Math.min(devicePixelRatio||1,2);if(canvas.width!==Math.round(r.width*dpr)||canvas.height!==Math.round(r.height*dpr)){canvas.width=Math.round(r.width*dpr);canvas.height=Math.round(r.height*dpr);}ctx.setTransform(dpr,0,0,dpr,0,0);ctx.imageSmoothingEnabled=false;ctx.fillStyle='#0d1924';ctx.fillRect(0,0,r.width,r.height);const base=getBase();if(!base)return;if(rts&&crewSignature(base)!==crewKey)rebuildCrew(base);terrainFor(base);initialize(base);let selectionChanged=false;for(const unit of crew){unit.away=isSceneUnitAway(unit.definition,base);if(unit.away&&selectedUnits.delete(unit.id))selectionChanged=true;}if(selectionChanged)notify();const tr=transform(),{unit:u,ox,oy}=tr;drawTerrain(ctx,terrain,tr);\r\n  const blocked=occupancy(base);for(const key of terrain.blocked)blocked.add(key);for(const unit of crew)if(blocked.has(Math.floor(unit.y)*24+Math.floor(unit.x))){Object.assign(unit,nearestFree(base,unit));unit.path=[];unit.index=0;unit.hasOrder=false;unit.rally=false;}\r\n  for(const unit of crew)if(unit.role==='worker'&&Object.values(base.buildings).some(b=>b.job?.policy==='occupied-worker'&&b.job.builderId===`crew-${unit.id+1}`)&&unit.hasOrder){unit.hasOrder=false;unit.rally=false;unit.path=[];unit.version=null;}const builders=crew.filter(unit=>!unit.away&&unit.role==='worker'&&!unit.hasOrder&&!safe('owns',unit));stepWanderers(builders,base,reduced?0:dt,t,{extraBlocked:terrain.blocked});patrol(base,t);stepRalliedUnits(crew.filter(u=>!u.away&&!safe('owns',u)),reduced?0:dt,base.rest);\r\n  orders.step();safe('step',t,crew,base,tr,getServerTime(),terrain);\r\n  const rows=[...BUILDINGS.map(def=>({type:'building',value:{...def,...base.buildings[def.id]},depth:(base.buildings[def.id]?.y??def.y)+def.h})),...crew.filter(unit=>!unit.away).map(unit=>({type:'unit',value:unit,depth:unit.y+.5})),...(safe('sortables')||[]).map(value=>({type:'rts',value,depth:value.depth}))].sort((a,b)=>a.depth-b.depth);\r\n  for(const row of rows){if(row.type==='rts'){safe('drawSortable',ctx,row.value,tr,terrain);continue;}if(row.type==='unit'){drawUnit(row.value,tr,t,base);continue;}const b=row.value,f=FACTION_BY_ID[b.track]||FACTION_BY_ID[base.faction]||FACTIONS[0];{const by=oy+b.y*u-terrain.heightAt(b.x,b.y)*2;if(!safe('drawBuilding',ctx,b,f,ox+b.x*u,by,u))drawBuilding(ctx,b,f,ox+b.x*u,by,u,{selected:b.id===selected});}if(b.job){const text=constructionCountdown(b.job.dueAt,getServerTime());ctx.font='bold 10px monospace';ctx.textAlign='center';const x=ox+(b.x+b.w/2)*u,y=oy+(b.y+b.h)*u+13,width=ctx.measureText(text).width+10;ctx.fillStyle='#101923ee';ctx.fillRect(x-width/2,y-11,width,15);ctx.fillStyle='#ffe3a0';ctx.fillText(text,x,y);ctx.textAlign='start';}}\r\n  safe('drawOverlays',ctx,tr);\r\n  if(dmGhost){ctx.fillStyle=dmGhost.ok?'#57e08a99':dmGhost.reason?'#ff4a4a99':'#f0d89099';ctx.fillRect(ox+dmGhost.x*u,oy+dmGhost.y*u,dmGhost.w*u,dmGhost.h*u);ctx.strokeStyle='#fff';ctx.lineWidth=1;ctx.strokeRect(ox+dmGhost.x*u+.5,oy+dmGhost.y*u+.5,dmGhost.w*u-1,dmGhost.h*u-1);}\r\n  const f=FACTION_BY_ID[base.faction]||FACTIONS[0];for(const [x,y] of [[1.4,4],[21.8,7],[2,18],[21,20]])ctx.drawImage(getSpriteCanvas(f.id,'resource'),Math.round(ox+x*u-16),Math.round(oy+y*u-25),32,32);const hull=base.ships?.[base.selectedShip]||{tier:0},tier=Math.min(4,hull.tier||0),pixels=32*(tier>=3?2:1),a=t/6500;ctx.drawImage(getSpriteCanvas(f.id,'ship',tier),Math.round(ox+(12+Math.cos(a)*8)*u-pixels/2),Math.round(oy+(9+Math.sin(a)*5)*u-pixels/2),pixels,pixels);\r\n  const g=gesture.state;if(g?.mode==='select'){ctx.fillStyle='#d5dfa61a';ctx.strokeStyle='#fff0a6';ctx.lineWidth=1;const x=Math.min(g.start.x,g.point.x),y=Math.min(g.start.y,g.point.y),w=Math.max(12,Math.abs(g.start.x-g.point.x)),h=Math.max(12,Math.abs(g.start.y-g.point.y));ctx.fillRect(x,y,w,h);ctx.strokeRect(Math.round(x),Math.round(y),Math.round(w),Math.round(h));}\r\n  if(transition&&performance.now()-transition.started>=450){entryFinished=true;transition=null;delete canvas.dataset.enteringWarRoom;canvas.removeAttribute('aria-busy');onEnterWarRoom();}\r\n }\r\n canvas.addEventListener('contextmenu',e=>e.preventDefault(),{signal:abort.signal});canvas.tabIndex=0;\r\n canvas.addEventListener('touchend',e=>{if(e.cancelable)e.preventDefault();},{signal:abort.signal,passive:false}); // taps run on pointerup: no compat click onto the build tray the tap opens\r\n canvas.addEventListener('pointerdown',e=>{if(transition||entryFinished)return;try{canvas.setPointerCapture?.(e.pointerId);}catch{/* pointer already gone: still handle the touch */}const p=localPoint(e);pointers.set(e.pointerId,p);if(pointers.size===1)gesture.down(p,{select:selectionArmed});else{if(dmOn)endMove(false);gesture.pinch();const points=[...pointers.values()];pinch=Math.hypot(points[0].x-points[1].x,points[0].y-points[1].y);}},{signal:abort.signal});\r\n canvas.addEventListener('pointermove',e=>{if(!pointers.has(e.pointerId))return;const point=localPoint(e);pointers.set(e.pointerId,point);if(pointers.size>=2){if(dmOn)endMove(false);gesture.pinch();const points=[...pointers.values()],distance=Math.hypot(points[0].x-points[1].x,points[0].y-points[1].y);if(pinch)scale=clamp(scale*distance/pinch,.65,2.6);pinch=distance;return;}const move=gesture.move(point);if(dmOn){dmGhost=dm.hover(buildingTileAt(point));return;}if(move?.mode==='pan'){pan.x=clamp(pan.x+move.dx,-450,450);pan.y=clamp(pan.y+move.dy,-450,450);}},{signal:abort.signal});\r\n // S12: tile-space input for the 3D base (base3d.mjs); the 2D pointer handlers below call the same tapTile.\r\n let emptyTap=null;\r\n // Only built (or building) structures take a tap: unbuilt sites cover most of the 24x24 map at their catalog plots, so they count as empty ground (the build tray opens for that spot).\r\n function issueOrder(type,options={}){if(!ORDER_TYPES.has(type))return {count:0,unsupported:true};if(type==='patrol'&&!options.target){if(!patrolStart){patrolStart=options.waypoint||null;return {count:0,waitingForSecondTarget:true};}options={...options,target:patrolStart,waypoint:options.waypoint};patrolStart=null;}const result=orders.apply(type,options);if(result.count)stopHarvestFor(new Set(options.ids??selectedUnits));selectionArmed=false;return result;}\r\n function tapTile({x,y}){const base=getBase(),building=[...BUILDINGS].reverse().find(d=>{const s=base?.buildings?.[d.id];if(!s||!(s.level>0||s.job))return false;const b={...d,...s};return x>=b.x&&x<=b.x+b.w&&y>=b.y-1&&y<=b.y+b.h;});if(orderMode&&!building){const target={x:Math.floor(x),y:Math.floor(y)};if(orderMode==='patrol'&&!patrolStart){patrolStart=target;return;}if(orderMode==='patrol'){issueOrder('patrol',{target:patrolStart,waypoint:target});patrolStart=null;}else issueOrder(orderMode,{target});orderMode=null;orders.setMode(null);canvas.dataset.orderMode='';return;}if(building){selected=building.id;onSelect(building.id);if(building.id==='hq')enterWarRoom();}else if(selectedUnits.size){rally({x:Math.floor(x),y:Math.floor(y)});selectedUnits.clear();selectionArmed=false;notify();}else emptyTap?.({x,y});} // one tap = one rally order, then the troops let go so the next ground tap builds again\r\n function selectTiles(a,b,pad=.5){selectedUnits.clear();const x0=Math.min(a.x,b.x)-pad,x1=Math.max(a.x,b.x)+pad,y0=Math.min(a.y,b.y)-pad,y1=Math.max(a.y,b.y)+pad;for(const unit of crew)if(!unit.away&&unit.x+.5>=x0&&unit.x+.5<=x1&&unit.y+.5>=y0&&unit.y+.5<=y1)selectedUnits.add(unit.id);selectionArmed=false;notify();}\r\n function deselect(ids){let n=0;for(const id of ids||[]){const unit=crew.find(u=>u.id===id||u.definition.instanceKey===id);if(unit&&selectedUnits.delete(unit.id))n++;}if(n)notify();}\r\n const end=e=>{const count=pointers.size;pointers.delete(e.pointerId);if(e.type==='pointercancel'){if(dmOn)endMove(false);gesture.cancel();pinch=0;delete canvas.dataset.troopGesture;return;}if(count!==1){gesture.pinch();pinch=0;return;}if(dmOn){gesture.up();endMove(true);return;}const g=gesture.up();delete canvas.dataset.troopGesture;if(!g)return;if(g.mode==='select'){selectedUnits.clear();const tr=transform();for(const id of unitsInRectangle(crew.filter(unit=>!unit.away).map(unit=>screenUnit(unit,tr)),g.start,g.point,g.moved?5:18))selectedUnits.add(id);selectionArmed=false;notify();return;}if(g.mode!=='pending'||g.moved)return;const tr=transform(),point=localPoint(e);tapTile({x:(point.x-tr.ox)/tr.unit,y:(point.y-tr.oy)/tr.unit});};canvas.addEventListener('pointerup',end,{signal:abort.signal});canvas.addEventListener('pointercancel',end,{signal:abort.signal});\r\n const keydown=e=>{if(orders.keydown?.(e)){orderMode=orders.getState().mode;patrolStart=null;if(orderMode)selectionArmed=false;if(orderMode==='stop'||orderMode==='holdposition'){issueOrder(orderMode);orderMode=null;orders.setMode(null);}canvas.dataset.orderMode=orderMode||'';return;}if(e.key==='Escape'&&orderMode){orderMode=null;patrolStart=null;orders.setMode(null);canvas.dataset.orderMode='';e.preventDefault();e.stopPropagation();}else if(e.key==='Escape'&&selectedUnits.size){e.preventDefault();e.stopPropagation();selectedUnits.clear();selectionArmed=false;notify();}else if(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(e.key)){e.preventDefault();pan.x+=e.key==='ArrowLeft'?20:e.key==='ArrowRight'?-20:0;pan.y+=e.key==='ArrowUp'?20:e.key==='ArrowDown'?-20:0;}};\r\n canvas.addEventListener('keydown',keydown,{signal:abort.signal});(typeof window==='undefined'?globalThis:window).addEventListener?.('keydown',keydown,{signal:abort.signal});\r\n raf=requestAnimationFrame(draw);return {enterWarRoom,tapTile,setEmptyTap(fn){emptyTap=fn;},selectTiles,deselect,select(id){selected=id;},selectedId(){return selected;},selectTroops(){if(!initialized){const base=getBase();if(base){terrainFor(base);initialize(base);}}selectedUnits.clear();for(const unit of crew)if(!isSceneUnitAway(unit.definition,getBase()))selectedUnits.add(unit.id);selectionArmed=true;notify();},clearSelection(){selectedUnits.clear();selectionArmed=false;gesture.cancel();notify();},rallyAtHQ(){const hq=getBase()?.buildings?.hq;if(hq)return rally({x:hq.x+2,y:hq.y+4});return 0;},issueOrder,setOrderMode(type){orderMode=orders.setMode(type);patrolStart=null;if(orderMode)selectionArmed=false;canvas.dataset.orderMode=orderMode||'';return orderMode;},assignControlGroup:orders.assign,recallControlGroup(group){const ids=orders.recall(group);if(!ids)return false;selectedUnits.clear();for(const id of ids)selectedUnits.add(id);selectionArmed=false;notify();return true;},getOrderState:orders.getState,setResourceHandler:orders.setResourceHandler,resourceSelect:orders.resourceSelect,getSelectedUnits(){return crew.filter(unit=>!unit.away&&selectedUnits.has(unit.id)).map(selectedInfo);},recenter(){scale=1;pan={x:0,y:0};},dispose(){disposed=true;orders.dispose();safe('dispose');transition=null;gesture.dispose();delete canvas.dataset.enteringWarRoom;delete canvas.dataset.troopGesture;delete canvas.dataset.orderMode;canvas.removeAttribute('aria-busy');for(const image of images)image.onload=null;atlases.clear();abort.abort();cancelAnimationFrame(raf);pointers.clear();}};\r\n}\r\n","modules/coach-world/catalog.mjs":"// Original MYR5 civilizations. Existing workout IDs remain canonical.\r\nexport const MAX_LEVEL=5;\r\nexport const hangarShipTier=level=>Math.max(1,Math.min(4,Math.ceil(level*4/MAX_LEVEL)));\r\nexport const SKYLINE_LOD_METERS=914.4;\r\nexport const RULES=Object.freeze({version:2,materialPerDay:10,maxTracksPerDay:3,dayGates:[1,3,6,10],buildMinuteMs:300000,launchCapacity:10,walkDwellMs:90000,walkSamples:6,walkDailyCap:12,walkHourlyCap:4,captureWindowMs:1800000,conversionMs:21600000,healingMs:86400000,graceMs:21600000});\r\nexport const FACTIONS=Object.freeze([\r\n {id:'chest',label:'Chest',name:'Pressforge',policy:'occupied-worker',color:'#ed7855',dark:'#643c46',accent:'#ffe2aa',groups:['chest'],description:'Crested wedge armor and diamond crowns echo the chest coaches angular heads and broad tapered shoulders.'},\r\n {id:'quads',label:'Quads',name:'Stride Citadel',policy:'occupied-worker',color:'#d8b85e',dark:'#4d4e59',accent:'#fff0ae',groups:['legs'],description:'Stacked oval drums and tapered pylons stand on broad paired foundations, echoing the quads coaches.'},\r\n {id:'glutes',label:'Glutes',name:'Thrustroot',policy:'consumed-seed',color:'#c579b6',dark:'#53365f',accent:'#a1e5bc',groups:['hips'],description:'Stacked pearl pods, bubble joints and heavy rounded vaults inherit the glutes coaches spherical bodies.'},\r\n {id:'arms-shoulders',label:'Arms & shoulders',name:'Orbit Arsenal',policy:'occupied-worker',color:'#6b9ee8',dark:'#293f6a',accent:'#ffcf82',groups:['shoulders'],description:'Wide shoulder canopies carry paired branching arms, echoing the arms and shoulders coaches.'},\r\n {id:'yoga',label:'Yoga & balance',name:'Stillwave',policy:'seeded-warp',color:'#88c6ae',dark:'#394f68',accent:'#f3e4b9',groups:['core','balance','yoga'],description:'Floating S-stems, hooked crowns and curling ribbons inherit the yoga coaches suspended wisp bodies.'},\r\n {id:'martial-arts',label:'Martial arts',name:'Moltguard',policy:'consumed-seed',color:'#d96564',dark:'#463b4b',accent:'#c0e2b1',groups:['stances','boxing'],description:'Compact braced bastions unfold from blunt monolith plates into ribbed fan helmets.'},\r\n {id:'cardio',label:'Cardio',name:'Pulseway',policy:'seeded-warp',color:'#5fcdd1',dark:'#294d62',accent:'#eb99df',groups:['cardio'],description:'Open-core rings, suspended saucers and thin skeletal struts echo the cardio coaches airy silhouettes.'},\r\n].map(f=>Object.freeze({...f,groups:Object.freeze(f.groups)})));\r\nexport const FACTION_BY_ID=Object.freeze(Object.fromEntries(FACTIONS.map(f=>[f.id,f])));\r\nexport const GROUP_TO_FACTION=Object.freeze(Object.fromEntries(FACTIONS.flatMap(f=>f.groups.map(g=>[g,f.id]))));\r\nexport const BUILDINGS=Object.freeze([\r\n {id:'hq',kind:'hq',track:null,x:10,y:9,w:4,h:4,label:'Command habitat'},\r\n ...FACTIONS.map((f,i)=>({id:`hall-${f.id}`,kind:'hall',track:f.id,x:[3,10,17,3,17,5,15][i],y:[3,2,3,10,10,17,17][i],w:3,h:3,label:f.name})),\r\n {id:'care',kind:'care',track:null,x:3,y:6,w:2,h:2,label:'Nutrition Kitchen'},\n {id:'observatory',kind:'observatory',track:null,x:18,y:6,w:2,h:2,label:'Breath observatory'},\r\n {id:'habitat',kind:'habitat',track:null,x:9,y:15,w:2,h:2,label:'Crew habitat'},\r\n {id:'hangar',kind:'hangar',track:null,x:12,y:15,w:3,h:2,label:'Ship hangar'},\r\n ...FACTIONS.flatMap((f,i)=>[\r\n  {id:`production-${f.id}`,kind:'production',track:f.id,x:[1,4,7,16,19,22,0][i],y:i===6?22:0,w:2,h:2,label:`${f.name} production`,startsUnbuilt:true,prerequisites:[{buildingId:'hq',level:1},{buildingId:`hall-${f.id}`,level:1}]},\r\n  {id:`expedition-${f.id}`,kind:'expedition',track:f.id,x:[3,6,9,12,15,18,21][i],y:22,w:2,h:2,label:`${f.name} launch post`,startsUnbuilt:true,prerequisites:[{buildingId:'hq',level:2},{buildingId:`production-${f.id}`,level:1}]},\r\n ]),\r\n // R7 defense (additive, v2 saves gain these as unbuilt sites): 3 towers 30, 6 wall pieces 10, 1 generator 40; all need HQ level 2.\r\n ...[[7,9],[15,9],[11,6]].map(([x,y],i)=>({id:`tower-${i+1}`,kind:'tower',track:null,x,y,w:2,h:2,label:`Defense tower ${i+1}`,startsUnbuilt:true,buildCost:30,prerequisites:[{buildingId:'hq',level:2}]})),\r\n ...[[8,8],[15,8],[8,13],[9,13],[14,13],[15,13]].map(([x,y],i)=>({id:`wall-${i+1}`,kind:'wall',track:null,x,y,w:1,h:1,label:`Wall ${i+1}`,startsUnbuilt:true,buildCost:10,prerequisites:[{buildingId:'hq',level:2}]})),\r\n {id:'generator',kind:'generator',track:null,x:20,y:14,w:2,h:2,label:'Power generator',startsUnbuilt:true,buildCost:40,prerequisites:[{buildingId:'hq',level:2}]},\r\n // R57 defense catalog (additive, all unbuilt): CoC/SC archetypes as original workout-themed kinds. Plots are disjoint from each other and from every other default.\r\n {id:'press-1',kind:'press',track:null,x:5,y:13,w:2,h:2,label:'Plate press 1',startsUnbuilt:true,buildCost:35,prerequisites:[{buildingId:'hq',level:2}]},\r\n {id:'press-2',kind:'press',track:null,x:14,y:6,w:2,h:2,label:'Plate press 2',startsUnbuilt:true,buildCost:35,prerequisites:[{buildingId:'hq',level:2}]},\r\n {id:'mortar-1',kind:'mortar',track:null,x:21,y:10,w:2,h:2,label:'Kettlebell lobber 1',startsUnbuilt:true,buildCost:45,prerequisites:[{buildingId:'hq',level:2}]},\r\n {id:'mortar-2',kind:'mortar',track:null,x:1,y:15,w:2,h:2,label:'Kettlebell lobber 2',startsUnbuilt:true,buildCost:45,prerequisites:[{buildingId:'hq',level:2}]},\r\n {id:'beacon-1',kind:'beacon',track:null,x:21,y:17,w:2,h:2,label:'Breath beacon',startsUnbuilt:true,buildCost:50,prerequisites:[{buildingId:'hq',level:2}]},\r\n ...[[6,9],[12,20],[17,21],[2,20]].map(([x,y],i)=>({id:`trap-${i+1}`,kind:'trap',track:null,x,y,w:1,h:1,label:`Band snare ${i+1}`,startsUnbuilt:true,buildCost:8,prerequisites:[{buildingId:'hq',level:1}]})),\r\n ...[[9,11],[9,12],[14,11],[14,12],[16,13],[17,14],[4,15],[6,15]].map(([x,y],i)=>({id:`fence-${i+1}`,kind:'fence',track:null,x,y,w:1,h:1,label:`Rope barricade ${i+1}`,startsUnbuilt:true,buildCost:5,prerequisites:[{buildingId:'hq',level:1}]})),\r\n ...[[22,14],[22,12]].map(([x,y],i)=>({id:`silo-${i+1}`,kind:'silo',track:null,x,y,w:2,h:2,label:`Protein silo ${i+1}`,startsUnbuilt:true,buildCost:25,prerequisites:[{buildingId:'hq',level:2}]})),\n {id:'yard',kind:'yard',track:null,x:19,y:19,w:3,h:3,label:'Drill yard',startsUnbuilt:true,buildCost:20,prerequisites:[{buildingId:'hq',level:2}]},\r\n // R74: the Landing Zone is the SECOND building (HQ -> Landing Zone -> first hall). Free + instant; packs need it built (pack-gate.mjs).\r\n {id:'landing',kind:'landing',track:null,x:10,y:13,w:3,h:2,label:'Landing Zone',startsUnbuilt:true,buildCost:0,prerequisites:[{buildingId:'hq',level:1}]},\r\n].map(Object.freeze));\r\n// Defense kinds the battle/raid/art code reads (R57). Consumers opt in per kind; catalog order is unchanged.\r\nexport const DEFENSE_KINDS=Object.freeze(['tower','wall','generator','press','mortar','beacon','trap','fence']);\r\nexport const BUILDING_BY_ID=Object.freeze(Object.fromEntries(BUILDINGS.map(b=>[b.id,b])));\r\nexport const SHIP_IDS=Object.freeze(['supportive','direct','analytical','playful','calm','mom']);\r\nexport const NPC_ITEMS=Object.freeze([{id:'repair-flag',label:'Repair pennant',cost:10},{id:'garden-lamp',label:'Biome lantern',cost:15},{id:'landing-ring',label:'Landing ring',cost:25}]);\r\n","modules/coach-world/client.mjs":"import {bootstrapGameGeneration,captureGameGeneration,gameGenerationHeaders,acceptGameGeneration,subscribeGameGeneration,gameCacheKey} from './game-generation-client.mjs';\r\nimport {authFetch} from '../../auth-client.mjs';\r\nimport {authTransitions} from '../../auth-transition.mjs';\r\nimport {performanceOwner,readPerformanceProgress,mergeVerifiedPerformance} from '../../performance-progress.mjs';\r\nimport {grantUnlock} from '../../unlock-ledger.mjs';\r\nimport {mirrorPackGate} from './pack-gate.mjs';\r\nimport {freshBase,advanceBase,applyBaseAction,creditPersonalProgress,assertBase,normalizeBase} from './state.mjs';\r\n\r\nexport function createWorldClient({signal,onInvalidate=()=>{},fetchImpl=authFetch,transitionApi=authTransitions(),getAccount=()=>globalThis.myr5AuthenticatedAccount,getOwner=()=>performanceOwner()}={}){\r\n const owner=getOwner(),account=getAccount();\r\n const user=account?.user?.id,epoch=account?.dataEpoch,key=gameCacheKey(`myr5-coach-world-v1/${encodeURIComponent(owner)}`);\r\n let snapshot=null,disposed=false,queue=Promise.resolve(),gameGeneration=null,generationBootstrap=null;const requestAbort=new AbortController();const transitions=transitionApi,ticket=transitions.capture();\n const offGeneration=subscribeGameGeneration(next=>{if(gameGeneration!==null&&next.gameGeneration!==gameGeneration){disposed=true;snapshot=null;requestAbort.abort();onInvalidate();}});\r\n const grantExpeditionPacks=b=>{for(const h of b?.expeditionHistory||[])if(h.pack)grantUnlock('reward-pack',h.pack,{account});}; // idempotent: pack id is per expedition\r\n const current=()=>{if(disposed||signal?.aborted||getOwner()!==owner||!transitions.isCurrent(ticket))throw new DOMException('World closed or account changed.','AbortError');};\r\n const invalidate=()=>{if(getOwner()!==owner){disposed=true;snapshot=null;onInvalidate();}};\r\n globalThis.addEventListener('myr5:account-ready',invalidate);globalThis.addEventListener('myr5:auth-transition',invalidate);\r\n const unsubscribe=transitions.subscribe(()=>{disposed=true;snapshot=null;onInvalidate();});\r\n const save=base=>{current();try{localStorage.setItem(key,JSON.stringify(base));}catch{throw Error('Base could not be saved on this device. Free storage and retry.');}return base;};\r\n async function request(path,body){\r\n  current();if(!user)throw Error('Sign in to use shared sectors, shops and nearby battles.');\n  if(epoch==null)throw Error('Your account is still loading. Close World and retry.');\n  if(gameGeneration===null){generationBootstrap??=bootstrapGameGeneration({fetchImpl,signal:requestAbort.signal});await generationBootstrap;current();gameGeneration=captureGameGeneration();}\n  const headers={'X-Target-Account':user,'X-Expected-Data-Epoch':String(epoch),...gameGenerationHeaders(gameGeneration)};\r\n  if(body){headers['Content-Type']='application/json';headers['Idempotency-Key']=body.id;}\r\n  const response=await fetchImpl(path,{signal:AbortSignal.any([requestAbort.signal,...(signal?[signal]:[])]),headers,method:body?'POST':'GET',body:body?JSON.stringify(body):undefined});current();\r\n  let data;try{data=await response.json();}catch{throw Error('World returned an unreadable response. Retry shortly.');}current();\r\n  if(response.ok||data.code==='game_generation_mismatch')acceptGameGeneration(data,gameGeneration);\r\n  if(!response.ok)throw Object.assign(Error(response.status===401?'Sign in again to use shared World features.':data.message||data.error||`World request failed (${response.status}).`),{status:response.status,code:data.code||data.error});\r\n  if(data.targetAccountId&&data.targetAccountId!==user||data.dataEpoch!=null&&Number(data.dataEpoch)!==Number(epoch))throw Error('Account changed. Reopen World.');\r\n  return data;\r\n }\r\n async function execute(action){\r\n  async function post(body){try{return await request('/api/world',body);}catch(e){current();if(e instanceof TypeError||e.name==='TimeoutError')return request('/api/world',body);throw e;}}\r\n  const make=()=>({...action,id:crypto.randomUUID(),revision:snapshot.revision});\r\n  try{snapshot=await post(make());}catch(e){if(!['world_revision_conflict','world_state_conflict'].includes(e.code))throw e;snapshot=await request('/api/world');snapshot=await post(make());}\r\n  if(action.type==='tick')snapshot=await request('/api/world');snapshot={...snapshot,base:normalizeBase(snapshot.base)};return snapshot;\r\n }\r\n async function raidHttp(path,body){\r\n  current();if(!user)throw Error('Sign in to start a rewarded NPC raid. Practice raids have no rewards.');\r\n  try{return await request(path,body);}catch(e){current();if(e instanceof TypeError||e.name==='TimeoutError')return request(path,body);throw e;}\r\n }\r\n function raidCommand(work){const run=queue.then(async()=>{current();return work();});queue=run.catch(()=>{});return run;}\r\n const raidCurrent=()=>raidCommand(()=>request('/api/world/raids/current'));\r\n const raidStart=({id=crypto.randomUUID(),targetId,selectedUnitIDs,dropTile})=>{\r\n  const body={id,targetId,selectedUnitIDs:[...selectedUnitIDs],dropTile:{...dropTile}};\r\n  return raidCommand(async()=>{const result=await raidHttp('/api/world/raids/start',body);current();return result;});\r\n };\r\n const raidComplete=(id,abilityInputs=[])=>{\r\n  const body={id,abilityInputs:abilityInputs.map(a=>({tick:a.tick,x:a.x,y:a.y}))};\r\n  return raidCommand(async()=>{\r\n   const result=await raidHttp('/api/world/raids/complete',body);current();let entitlementRefreshPending=false;\r\n   if(result.raid?.status==='won'&&typeof result.raid.packId==='string'&&/^reward-pack:(?:uncommon|rare|legendary):raid-[A-Za-z0-9_-]+$/.test(result.raid.packId)){\r\n    grantUnlock('reward-pack',result.raid.packId,{account});\r\n    try{const verified=await request('/api/account');current();if(verified?.progress?.performance)mergeVerifiedPerformance(verified);globalThis.dispatchEvent(new CustomEvent('myr5:account-entitlements-updated',{detail:{ownedPacks:verified?.entitlements?.ownedPacks||[]}}));}\r\n    catch(error){current();entitlementRefreshPending=true;}\r\n    globalThis.dispatchEvent(new CustomEvent('myr5:battle-pass',{detail:{granted:[{kind:'reward-pack',id:result.raid.packId}]}}));\r\n   }\r\n   return {...result,entitlementRefreshPending};\r\n  });\r\n };\r\n const raidAbandon=id=>raidCommand(()=>raidHttp('/api/world/raids/complete',{id,action:'abandon'}));\r\n return {owner,authenticated:!!user,get snapshot(){return snapshot;},\r\n  async load(){current();if(user){await bootstrapGameGeneration({fetchImpl,signal});current();gameGeneration=captureGameGeneration();snapshot=await request('/api/world');await execute({type:'reconcile'});await execute({type:'npc-claim-rewards'});}else{\r\n   let base;try{base=normalizeBase(JSON.parse(localStorage.getItem(key)));assertBase(base);}catch{base=freshBase(owner);}\r\n   base=advanceBase(base);base=creditPersonalProgress(base,readPerformanceProgress({owner}));save(base);snapshot={base,revision:0,publicId:null,serverTime:Date.now()};\r\n  }current();mirrorPackGate(owner,snapshot.base);grantExpeditionPacks(snapshot.base);return snapshot;},\r\n  command(action){const run=queue.then(async()=>{current();if(!snapshot)throw Error('Your base is still loading.');\r\n   if(user){await execute(action);}\r\n   else {if(action.type==='ar-catch'){const {arCatchEntry}=await import('../coach-world-rts/ar-catch.mjs');current();const now=Date.now(),{entry}=arCatchEntry(action,now),base=applyBaseAction(advanceBase(snapshot.base),{type:'wild-catch',entry},{now,wildServer:true});save(base);snapshot={...snapshot,base,result:{caught:true,entry}};}\r\n    else if(action.type==='rts-defense-drill'){const {defenseDrill}=await import('./rts-defense.mjs');current();const now=Date.now(),base=defenseDrill(advanceBase(snapshot.base,now),action,{now});save(base);snapshot={...snapshot,base};}\r\n    else if(action.type==='npc-purchase'){const {applyNpcPurchase}=await import('../coach-world-rts/npc-progression.mjs');current();const next=applyNpcPurchase(advanceBase(snapshot.base),{...action,accountId:owner},{now:Date.now()});if(!next.result.ok)throw Error(next.result.reason);save(next.base);snapshot={...snapshot,base:next.base,result:next.result};}\r\n    else {if(!['gather','stop-gather','rts-research','rts-addon','rts-repair','rts-cancel','cancel-production','build','upgrade','cancel','move','care','rest','faction','select-ship','train-unit','cancel-training','prepare-expedition','launch-expedition','return-expedition','cancel-expedition','start-over','outpost'].includes(action.type))throw Error('Sign in to use shared world features.');const base=applyBaseAction(advanceBase(snapshot.base),action,{now:Date.now()});save(base);snapshot={...snapshot,base};}}\r\n   current();mirrorPackGate(owner,snapshot.base);grantExpeditionPacks(snapshot.base);return snapshot;\r\n  });queue=run.catch(()=>{});return run;},\r\n  async runBenefits(){current();if(user)return request('/api/world/run-benefits');if(!snapshot)throw Error('Your base is still loading.');const {getRunStartBenefits,readProgression}=await import('../coach-world-rts/npc-progression.mjs');current();return {targetAccountId:owner,dataEpoch:null,revision:snapshot.revision,balance:readProgression(snapshot.base).balance,benefits:getRunStartBenefits(readProgression(snapshot.base),{accountId:owner})};},\r\n  raidCurrent,raidStart,raidComplete,raidAbandon,\r\n  async map({latitude,longitude,radius=5000}={}){if(user){const query=Number.isFinite(latitude)&&Number.isFinite(longitude)?`?latitude=${encodeURIComponent(latitude)}&longitude=${encodeURIComponent(longitude)}&radius=${Math.max(100,Math.min(5000,radius))}`:'';return request('/api/world/map'+query);}current();const {WORLD_SITES,WORLD_SECTORS}=await import('./sites.mjs');current();return {serverTime:Date.now(),preview:true,sites:WORLD_SITES.map(s=>({...s})),sectors:WORLD_SECTORS.map(s=>({id:s.id,siteId:s.siteId,initialPatchId:s.initialPatchId,controller:s.initialFaction,phase:'stable',conversionProgress:1,polygonGeoJSON:{type:'Polygon',coordinates:[[...s.polygon,s.polygon[0]]]}})),friends:[],geometryEpoch:1};},encounter:id=>request(`/api/world/encounters/${encodeURIComponent(id)}`),\r\n  zones({latitude,longitude}){return request(`/api/world/zones?latitude=${encodeURIComponent(latitude)}&longitude=${encodeURIComponent(longitude)}`);},\r\n  async drops(){const data=await request('/api/world/drops');current();for(const claim of data.claims||[])for(const id of claim.packs||[])grantUnlock('reward-pack',id,{account});return data;},\r\n  dropsCommand(action){const run=queue.then(async()=>{current();const body={...action,id:crypto.randomUUID()};let response;try{response=await request('/api/world/drops',body);}catch(e){if(e instanceof TypeError||e.name==='TimeoutError')response=await request('/api/world/drops',body);else throw e;}current();if(response.result?.claimed){for(const id of response.result.claim.packs)grantUnlock('reward-pack',id,{account});try{const verified=await request('/api/account');current();mergeVerifiedPerformance(verified);}catch{current();response.progressRefreshPending=true;}globalThis.dispatchEvent(new CustomEvent('myr5:battle-pass',{detail:{granted:response.result.claim.packs.map(id=>({kind:'reward-pack',id}))}}));}globalThis.dispatchEvent(new CustomEvent('myr5:world-drops-updated'));return response;});queue=run.catch(()=>{});return run;},\r\n  dispose(){offGeneration();requestAbort.abort();disposed=true;snapshot=null;unsubscribe();globalThis.removeEventListener('myr5:account-ready',invalidate);globalThis.removeEventListener('myr5:auth-transition',invalidate);}\r\n };\r\n}\r\n","modules/coach-world/coach-manifest-data.mjs":"// GENERATED at author time from the reviewed coach-manifest-v2.json (sha256 792d5ad5595d21b9448887282202dbbb60c106cbc6bb16f645921d3cc97bbbe9); do not hand-edit.\n// Pure literal data: no imports, no runtime sorting or assignment. Consumed only by coach-manifest.mjs.\nexport const SOURCE_SHA256='792d5ad5595d21b9448887282202dbbb60c106cbc6bb16f645921d3cc97bbbe9';\nexport const COACH_ROW_LITERAL=[\n {\n  \"coachId\": \"myr5\",\n  \"canonicalLabel\": \"MYR5 Jacked LaLAN\",\n  \"slotId\": \"P-N-00\",\n  \"sourceVisualId\": \"worker:myr5\",\n  \"sourceVisualKind\": \"basic-worker\",\n  \"sourceCoachId\": \"myr5\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"worker\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": true,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/01-seed-pearo--3d_character_model\",\n  \"canonicalLabel\": \"McKe-O2 Nose-Cone\",\n  \"slotId\": \"P-N-02\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--3d_character_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/01-seed-pearo--3d_character_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"canonicalLabel\": \"Wern-Axis Flow\",\n  \"slotId\": \"P-N-04\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"canonicalLabel\": \"Phelpeller Pulse\",\n  \"slotId\": \"P-N-06\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"canonicalLabel\": \"Hane-Yoke Lunge\",\n  \"slotId\": \"P-N-08\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"canonicalLabel\": \"Arnoid Press\",\n  \"slotId\": \"P-N-10\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"canonicalLabel\": \"Oli-Volt Lunge\",\n  \"slotId\": \"P-N-12\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"canonicalLabel\": \"Robo-ertson Bridge\",\n  \"slotId\": \"P-N-14\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"canonicalLabel\": \"Con-Terra Coil-Bridge\",\n  \"slotId\": \"P-N-16\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--circle_center\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"canonicalLabel\": \"Fon-Dynamo Footwork\",\n  \"slotId\": \"P-N-18\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"canonicalLabel\": \"Feli-X Footdrive\",\n  \"slotId\": \"P-N-20\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"canonicalLabel\": \"Gyrvanium Glute-Drive\",\n  \"slotId\": \"P-N-22\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"canonicalLabel\": \"Trac-ION Hip-Thrust\",\n  \"slotId\": \"P-N-24\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"canonicalLabel\": \"L0U Ferri-pecctor\",\n  \"slotId\": \"P-N-26\",\n  \"sourceVisualId\": \"coach:roster/04-crest-wedge--robot_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/04-crest-wedge--stylized_3d_character\",\n  \"canonicalLabel\": \"Gr33ne Grid-Press\",\n  \"slotId\": \"P-N-28\",\n  \"sourceVisualId\": \"coach:roster/04-crest-wedge--stylized_3d_character\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/04-crest-wedge--stylized_3d_character\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"canonicalLabel\": \"C0N0R McG-Gear\",\n  \"slotId\": \"P-N-30\",\n  \"sourceVisualId\": \"coach:roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"canonicalLabel\": \"Oliva-C Pec\",\n  \"slotId\": \"P-N-31\",\n  \"sourceVisualId\": \"coach:roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/06-ridge-triad--robot_3d_model1\",\n  \"canonicalLabel\": \"M0 Farah Fastlane\",\n  \"slotId\": \"P-E-00\",\n  \"sourceVisualId\": \"coach:roster/06-ridge-triad--robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/06-ridge-triad--robot_3d_model1\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"canonicalLabel\": \"Billy Blanx Beam\",\n  \"slotId\": \"P-E-02\",\n  \"sourceVisualId\": \"coach:roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"canonicalLabel\": \"B0LT Sprint-Drive\",\n  \"slotId\": \"P-E-04\",\n  \"sourceVisualId\": \"coach:roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/08-shard-asym--geometric_robot_3d_model\",\n  \"canonicalLabel\": \"Pec-n0ld Reactor\",\n  \"slotId\": \"P-E-06\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--geometric_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/08-shard-asym--geometric_robot_3d_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/08-shard-asym--robot_3d_model4\",\n  \"canonicalLabel\": \"S3RG Oliv-Armature\",\n  \"slotId\": \"P-E-08\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--robot_3d_model4\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/08-shard-asym--robot_3d_model4\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"canonicalLabel\": \"Waters0nic\",\n  \"slotId\": \"P-E-10\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"canonicalLabel\": \"Yee-lastic Flow\",\n  \"slotId\": \"P-E-12\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"canonicalLabel\": \"chris BenchSTEAD\",\n  \"slotId\": \"P-E-14\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--3d_robot_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"canonicalLabel\": \"Bruce L3300\",\n  \"slotId\": \"P-E-16\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"canonicalLabel\": \"Machi-Droid-a\",\n  \"slotId\": \"P-E-18\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/09-monolith-tanka--robot_3d_model3\",\n  \"canonicalLabel\": \"Israel Ade-SYNC-ya\",\n  \"slotId\": \"P-E-20\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--robot_3d_model3\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/09-monolith-tanka--robot_3d_model3\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"canonicalLabel\": \"Cavalier-EX Chest\",\n  \"slotId\": \"P-E-22\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"canonicalLabel\": \"Iyeng-Arc Inversion\",\n  \"slotId\": \"P-E-24\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/10-petal-wisp--ghost_character_3d_model\",\n  \"canonicalLabel\": \"Warri0r-ner Drive\",\n  \"slotId\": \"P-E-26\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--ghost_character_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/10-petal-wisp--ghost_character_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"canonicalLabel\": \"Adriene A-Sana Mk II\",\n  \"slotId\": \"P-E-28\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"canonicalLabel\": \"Anderson Sil-VOLT\",\n  \"slotId\": \"P-E-30\",\n  \"sourceVisualId\": \"coach:roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"canonicalLabel\": \"H0F Hold-Circuit\",\n  \"slotId\": \"P-S-00\",\n  \"sourceVisualId\": \"coach:roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"canonicalLabel\": \"Pilat-OS Calm-Circuit\",\n  \"slotId\": \"P-S-02\",\n  \"sourceVisualId\": \"coach:roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/13-fan-split--stylized_3d_character2\",\n  \"canonicalLabel\": \"Quad-Cutler LEGS THO\",\n  \"slotId\": \"P-S-04\",\n  \"sourceVisualId\": \"coach:roster/13-fan-split--stylized_3d_character2\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/13-fan-split--stylized_3d_character2\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/13-fan-split--stylized_toy_3d_model\",\n  \"canonicalLabel\": \"BlankSaber Kick\",\n  \"slotId\": \"P-S-06\",\n  \"sourceVisualId\": \"coach:roster/13-fan-split--stylized_toy_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/13-fan-split--stylized_toy_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"canonicalLabel\": \"LiDAR Jett-Jab\",\n  \"slotId\": \"P-S-08\",\n  \"sourceVisualId\": \"coach:roster/14-chisel-spire--cone_head_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"martial-arts\"\n },\n {\n  \"coachId\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"canonicalLabel\": \"Rea-Spire Relay\",\n  \"slotId\": \"P-S-10\",\n  \"sourceVisualId\": \"coach:roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"canonicalLabel\": \"t-OHM Platzma\",\n  \"slotId\": \"P-S-12\",\n  \"sourceVisualId\": \"coach:roster/15-orbital-coili--robot_character_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"canonicalLabel\": \"Franx Pec-Zane\",\n  \"slotId\": \"P-S-14\",\n  \"sourceVisualId\": \"coach:roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"canonicalLabel\": \"Cutlerator Press\",\n  \"slotId\": \"P-S-16\",\n  \"sourceVisualId\": \"coach:roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"chest\"\n },\n {\n  \"coachId\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"canonicalLabel\": \"Delt-alinn Bailey\",\n  \"slotId\": \"P-S-18\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"canonicalLabel\": \"KAI-nematic Raise\",\n  \"slotId\": \"P-S-20\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"canonicalLabel\": \"Wheel-er Servo-Shrug\",\n  \"slotId\": \"P-S-22\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"canonicalLabel\": \"H0F-Lung\",\n  \"slotId\": \"P-S-24\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=0,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"canonicalLabel\": \"Max Ohm-Strom Breath\",\n  \"slotId\": \"P-S-26\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=48,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"canonicalLabel\": \"Shiva Rea-spatory\",\n  \"slotId\": \"P-S-28\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=96,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"canonicalLabel\": \"Tara-5tile5-Standby\",\n  \"slotId\": \"P-S-30\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=144,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"canonicalLabel\": \"Werner Wind0wn\",\n  \"slotId\": \"P-W-00\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=192,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"canonicalLabel\": \"Yee Holo-Om\",\n  \"slotId\": \"P-W-02\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=0,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"canonicalLabel\": \"Budig-ital Breath\",\n  \"slotId\": \"P-W-04\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=48,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"canonicalLabel\": \"Priest-L33 Squat\",\n  \"slotId\": \"P-W-06\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=96,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"sourceCoachId\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"scout\",\n  \"simBase\": \"s\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": \"quad\",\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"canonicalLabel\": \"Mat-Gregor Matrix\",\n  \"slotId\": \"P-W-08\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"canonicalLabel\": \"Ferri-Delt L0U\",\n  \"slotId\": \"P-W-10\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"canonicalLabel\": \"Frank Delt-Zan3\",\n  \"slotId\": \"P-W-12\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"canonicalLabel\": \"Yee Gyro-Yogi\",\n  \"slotId\": \"P-W-14\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/20-lume--robotic_figure_3d_model\",\n  \"canonicalLabel\": \"Pre-Font-AI-ne Pace\",\n  \"slotId\": \"P-W-16\",\n  \"sourceVisualId\": \"coach:roster/20-lume--robotic_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/20-lume--robotic_figure_3d_model\",\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"commander\",\n  \"simBase\": \"b\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"cardio\"\n },\n {\n  \"coachId\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"canonicalLabel\": \"Strom Flex Current\",\n  \"slotId\": \"P-W-18\",\n  \"sourceVisualId\": \"coach:roster/22-curve--stylized_alien_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"yoga\"\n },\n {\n  \"coachId\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"canonicalLabel\": \"Kai Gr33ne Lunge\",\n  \"slotId\": \"P-W-20\",\n  \"sourceVisualId\": \"coach:roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"quads\"\n },\n {\n  \"coachId\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"canonicalLabel\": \"Blob 1 · H0F Hold-Drive\",\n  \"slotId\": \"P-W-22\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": null\n },\n {\n  \"coachId\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"canonicalLabel\": \"Chlo-Turbo Thrust\",\n  \"slotId\": \"P-W-24\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"canonicalLabel\": \"Con-Thrust-Era\",\n  \"slotId\": \"P-W-26\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"skirmisher\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"nimble\",\n  \"simWpnOverride\": \"rapid\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n },\n {\n  \"coachId\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"canonicalLabel\": \"Ree-volts Raise\",\n  \"slotId\": \"P-W-28\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"frontline\",\n  \"simBase\": \"g\",\n  \"simTagOverride\": \"plated\",\n  \"simWpnOverride\": \"burst\",\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"arms-shoulders\"\n },\n {\n  \"coachId\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"canonicalLabel\": \"Tingularity Thrust\",\n  \"slotId\": \"P-W-30\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"support\",\n  \"simBase\": \"h\",\n  \"simTagOverride\": null,\n  \"simWpnOverride\": null,\n  \"quadrupedPolicyFlag\": null,\n  \"roleCatalogVersion\": 1,\n  \"roleStatus\": \"proposed-policy-v1\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  },\n  \"abilityFaction\": \"glutes\"\n }\n];\nexport const PEDESTAL_SLOT_LITERAL=[\n {\n  \"slotId\": \"P-N-00\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 0,\n  \"assignedCoachId\": \"myr5\",\n  \"coordinate\": {\n   \"xTile\": -2,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -22.4,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-01\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 1,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -1.096774193548,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -20.95483871,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-02\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 2,\n  \"assignedCoachId\": \"roster/01-seed-pearo--3d_character_model\",\n  \"coordinate\": {\n   \"xTile\": -0.193548387097,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -19.509677419,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-03\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 3,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 0.709677419355,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -18.064516129,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-04\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 4,\n  \"assignedCoachId\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 1.612903225806,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -16.619354839,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-05\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 5,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 2.516129032258,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -15.174193548,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-06\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 6,\n  \"assignedCoachId\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 3.41935483871,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -13.729032258,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-07\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 7,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 4.322580645161,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -12.283870968,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-08\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 8,\n  \"assignedCoachId\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"coordinate\": {\n   \"xTile\": 5.225806451613,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -10.838709677,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-09\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 9,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 6.129032258065,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -9.393548387,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-10\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 10,\n  \"assignedCoachId\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": 7.032258064516,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -7.948387097,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-11\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 11,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 7.935483870968,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -6.503225806,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-12\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 12,\n  \"assignedCoachId\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 8.838709677419,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -5.058064516,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-13\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 13,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 9.741935483871,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -3.612903226,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-14\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 14,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 10.645161290323,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -2.167741935,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-15\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 15,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 11.548387096774,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": -0.722580645,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-16\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 16,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"coordinate\": {\n   \"xTile\": 12.451612903226,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 0.722580645,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-17\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 17,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 13.354838709677,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 2.167741935,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-18\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 18,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 14.258064516129,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 3.612903226,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-19\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 19,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 15.161290322581,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 5.058064516,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-20\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 20,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 16.064516129032,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 6.503225806,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-21\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 21,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 16.967741935484,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 7.948387097,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-22\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 22,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"coordinate\": {\n   \"xTile\": 17.870967741935,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 9.393548387,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-23\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 23,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 18.774193548387,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 10.838709677,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-24\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 24,\n  \"assignedCoachId\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 19.677419354839,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 12.283870968,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-25\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 25,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 20.58064516129,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 13.729032258,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-26\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 26,\n  \"assignedCoachId\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 21.483870967742,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 15.174193548,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-27\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 27,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 22.387096774194,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 16.619354839,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-28\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 28,\n  \"assignedCoachId\": \"roster/04-crest-wedge--stylized_3d_character\",\n  \"coordinate\": {\n   \"xTile\": 23.290322580645,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 18.064516129,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-29\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 29,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 24.193548387097,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 19.509677419,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-30\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 30,\n  \"assignedCoachId\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 25.096774193548,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 20.95483871,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-N-31\",\n  \"rail\": \"N\",\n  \"railOrdinal\": 31,\n  \"assignedCoachId\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": 26,\n   \"yTile\": -4,\n   \"xMetersFromMapCenter\": 22.4,\n   \"zMetersFromMapCenter\": -25.6\n  }\n },\n {\n  \"slotId\": \"P-E-00\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 0,\n  \"assignedCoachId\": \"roster/06-ridge-triad--robot_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": -2,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -22.4\n  }\n },\n {\n  \"slotId\": \"P-E-01\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 1,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": -1.096774193548,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -20.95483871\n  }\n },\n {\n  \"slotId\": \"P-E-02\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 2,\n  \"assignedCoachId\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": -0.193548387097,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -19.509677419\n  }\n },\n {\n  \"slotId\": \"P-E-03\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 3,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 0.709677419355,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -18.064516129\n  }\n },\n {\n  \"slotId\": \"P-E-04\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 4,\n  \"assignedCoachId\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 1.612903225806,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -16.619354839\n  }\n },\n {\n  \"slotId\": \"P-E-05\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 5,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 2.516129032258,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -15.174193548\n  }\n },\n {\n  \"slotId\": \"P-E-06\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 6,\n  \"assignedCoachId\": \"roster/08-shard-asym--geometric_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 3.41935483871,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -13.729032258\n  }\n },\n {\n  \"slotId\": \"P-E-07\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 7,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 4.322580645161,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -12.283870968\n  }\n },\n {\n  \"slotId\": \"P-E-08\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 8,\n  \"assignedCoachId\": \"roster/08-shard-asym--robot_3d_model4\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 5.225806451613,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -10.838709677\n  }\n },\n {\n  \"slotId\": \"P-E-09\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 9,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 6.129032258065,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -9.393548387\n  }\n },\n {\n  \"slotId\": \"P-E-10\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 10,\n  \"assignedCoachId\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 7.032258064516,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -7.948387097\n  }\n },\n {\n  \"slotId\": \"P-E-11\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 11,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 7.935483870968,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -6.503225806\n  }\n },\n {\n  \"slotId\": \"P-E-12\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 12,\n  \"assignedCoachId\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 8.838709677419,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -5.058064516\n  }\n },\n {\n  \"slotId\": \"P-E-13\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 13,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 9.741935483871,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -3.612903226\n  }\n },\n {\n  \"slotId\": \"P-E-14\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 14,\n  \"assignedCoachId\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 10.645161290323,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -2.167741935\n  }\n },\n {\n  \"slotId\": \"P-E-15\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 15,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 11.548387096774,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": -0.722580645\n  }\n },\n {\n  \"slotId\": \"P-E-16\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 16,\n  \"assignedCoachId\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 12.451612903226,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 0.722580645\n  }\n },\n {\n  \"slotId\": \"P-E-17\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 17,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 13.354838709677,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 2.167741935\n  }\n },\n {\n  \"slotId\": \"P-E-18\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 18,\n  \"assignedCoachId\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 14.258064516129,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 3.612903226\n  }\n },\n {\n  \"slotId\": \"P-E-19\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 19,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 15.161290322581,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 5.058064516\n  }\n },\n {\n  \"slotId\": \"P-E-20\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 20,\n  \"assignedCoachId\": \"roster/09-monolith-tanka--robot_3d_model3\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 16.064516129032,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 6.503225806\n  }\n },\n {\n  \"slotId\": \"P-E-21\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 21,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 16.967741935484,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 7.948387097\n  }\n },\n {\n  \"slotId\": \"P-E-22\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 22,\n  \"assignedCoachId\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 17.870967741935,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 9.393548387\n  }\n },\n {\n  \"slotId\": \"P-E-23\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 23,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 18.774193548387,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 10.838709677\n  }\n },\n {\n  \"slotId\": \"P-E-24\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 24,\n  \"assignedCoachId\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 19.677419354839,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 12.283870968\n  }\n },\n {\n  \"slotId\": \"P-E-25\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 25,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 20.58064516129,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 13.729032258\n  }\n },\n {\n  \"slotId\": \"P-E-26\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 26,\n  \"assignedCoachId\": \"roster/10-petal-wisp--ghost_character_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 21.483870967742,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 15.174193548\n  }\n },\n {\n  \"slotId\": \"P-E-27\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 27,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 22.387096774194,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 16.619354839\n  }\n },\n {\n  \"slotId\": \"P-E-28\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 28,\n  \"assignedCoachId\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 23.290322580645,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 18.064516129\n  }\n },\n {\n  \"slotId\": \"P-E-29\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 29,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 24.193548387097,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 19.509677419\n  }\n },\n {\n  \"slotId\": \"P-E-30\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 30,\n  \"assignedCoachId\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 25.096774193548,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 20.95483871\n  }\n },\n {\n  \"slotId\": \"P-E-31\",\n  \"rail\": \"E\",\n  \"railOrdinal\": 31,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 28,\n   \"yTile\": 26,\n   \"xMetersFromMapCenter\": 25.6,\n   \"zMetersFromMapCenter\": 22.4\n  }\n },\n {\n  \"slotId\": \"P-S-00\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 0,\n  \"assignedCoachId\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -2,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -22.4,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-01\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 1,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -1.096774193548,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -20.95483871,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-02\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 2,\n  \"assignedCoachId\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -0.193548387097,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -19.509677419,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-03\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 3,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 0.709677419355,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -18.064516129,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-04\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 4,\n  \"assignedCoachId\": \"roster/13-fan-split--stylized_3d_character2\",\n  \"coordinate\": {\n   \"xTile\": 1.612903225806,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -16.619354839,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-05\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 5,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 2.516129032258,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -15.174193548,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-06\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 6,\n  \"assignedCoachId\": \"roster/13-fan-split--stylized_toy_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 3.41935483871,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -13.729032258,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-07\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 7,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 4.322580645161,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -12.283870968,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-08\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 8,\n  \"assignedCoachId\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 5.225806451613,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -10.838709677,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-09\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 9,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 6.129032258065,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -9.393548387,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-10\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 10,\n  \"assignedCoachId\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 7.032258064516,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -7.948387097,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-11\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 11,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 7.935483870968,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -6.503225806,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-12\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 12,\n  \"assignedCoachId\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 8.838709677419,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -5.058064516,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-13\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 13,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 9.741935483871,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -3.612903226,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-14\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 14,\n  \"assignedCoachId\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 10.645161290323,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -2.167741935,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-15\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 15,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 11.548387096774,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": -0.722580645,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-16\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 16,\n  \"assignedCoachId\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 12.451612903226,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 0.722580645,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-17\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 17,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 13.354838709677,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 2.167741935,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-18\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 18,\n  \"assignedCoachId\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"coordinate\": {\n   \"xTile\": 14.258064516129,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 3.612903226,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-19\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 19,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 15.161290322581,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 5.058064516,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-20\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 20,\n  \"assignedCoachId\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 16.064516129032,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 6.503225806,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-21\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 21,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 16.967741935484,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 7.948387097,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-22\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 22,\n  \"assignedCoachId\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 17.870967741935,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 9.393548387,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-23\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 23,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 18.774193548387,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 10.838709677,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-24\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 24,\n  \"assignedCoachId\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 19.677419354839,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 12.283870968,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-25\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 25,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 20.58064516129,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 13.729032258,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-26\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 26,\n  \"assignedCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": 21.483870967742,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 15.174193548,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-27\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 27,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 22.387096774194,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 16.619354839,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-28\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 28,\n  \"assignedCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"coordinate\": {\n   \"xTile\": 23.290322580645,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 18.064516129,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-29\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 29,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 24.193548387097,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 19.509677419,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-30\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 30,\n  \"assignedCoachId\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": 25.096774193548,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 20.95483871,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-S-31\",\n  \"rail\": \"S\",\n  \"railOrdinal\": 31,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": 26,\n   \"yTile\": 28,\n   \"xMetersFromMapCenter\": 22.4,\n   \"zMetersFromMapCenter\": 25.6\n  }\n },\n {\n  \"slotId\": \"P-W-00\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 0,\n  \"assignedCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": -2,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -22.4\n  }\n },\n {\n  \"slotId\": \"P-W-01\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 1,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": -1.096774193548,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -20.95483871\n  }\n },\n {\n  \"slotId\": \"P-W-02\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 2,\n  \"assignedCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": -0.193548387097,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -19.509677419\n  }\n },\n {\n  \"slotId\": \"P-W-03\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 3,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 0.709677419355,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -18.064516129\n  }\n },\n {\n  \"slotId\": \"P-W-04\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 4,\n  \"assignedCoachId\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 1.612903225806,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -16.619354839\n  }\n },\n {\n  \"slotId\": \"P-W-05\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 5,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 2.516129032258,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -15.174193548\n  }\n },\n {\n  \"slotId\": \"P-W-06\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 6,\n  \"assignedCoachId\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 3.41935483871,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -13.729032258\n  }\n },\n {\n  \"slotId\": \"P-W-07\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 7,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 4.322580645161,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -12.283870968\n  }\n },\n {\n  \"slotId\": \"P-W-08\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 8,\n  \"assignedCoachId\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 5.225806451613,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -10.838709677\n  }\n },\n {\n  \"slotId\": \"P-W-09\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 9,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 6.129032258065,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -9.393548387\n  }\n },\n {\n  \"slotId\": \"P-W-10\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 10,\n  \"assignedCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 7.032258064516,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -7.948387097\n  }\n },\n {\n  \"slotId\": \"P-W-11\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 11,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 7.935483870968,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -6.503225806\n  }\n },\n {\n  \"slotId\": \"P-W-12\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 12,\n  \"assignedCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 8.838709677419,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -5.058064516\n  }\n },\n {\n  \"slotId\": \"P-W-13\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 13,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 9.741935483871,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -3.612903226\n  }\n },\n {\n  \"slotId\": \"P-W-14\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 14,\n  \"assignedCoachId\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 10.645161290323,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -2.167741935\n  }\n },\n {\n  \"slotId\": \"P-W-15\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 15,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 11.548387096774,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": -0.722580645\n  }\n },\n {\n  \"slotId\": \"P-W-16\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 16,\n  \"assignedCoachId\": \"roster/20-lume--robotic_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 12.451612903226,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 0.722580645\n  }\n },\n {\n  \"slotId\": \"P-W-17\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 17,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 13.354838709677,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 2.167741935\n  }\n },\n {\n  \"slotId\": \"P-W-18\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 18,\n  \"assignedCoachId\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 14.258064516129,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 3.612903226\n  }\n },\n {\n  \"slotId\": \"P-W-19\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 19,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 15.161290322581,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 5.058064516\n  }\n },\n {\n  \"slotId\": \"P-W-20\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 20,\n  \"assignedCoachId\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 16.064516129032,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 6.503225806\n  }\n },\n {\n  \"slotId\": \"P-W-21\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 21,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 16.967741935484,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 7.948387097\n  }\n },\n {\n  \"slotId\": \"P-W-22\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 22,\n  \"assignedCoachId\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 17.870967741935,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 9.393548387\n  }\n },\n {\n  \"slotId\": \"P-W-23\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 23,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 18.774193548387,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 10.838709677\n  }\n },\n {\n  \"slotId\": \"P-W-24\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 24,\n  \"assignedCoachId\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 19.677419354839,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 12.283870968\n  }\n },\n {\n  \"slotId\": \"P-W-25\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 25,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 20.58064516129,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 13.729032258\n  }\n },\n {\n  \"slotId\": \"P-W-26\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 26,\n  \"assignedCoachId\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 21.483870967742,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 15.174193548\n  }\n },\n {\n  \"slotId\": \"P-W-27\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 27,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 22.387096774194,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 16.619354839\n  }\n },\n {\n  \"slotId\": \"P-W-28\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 28,\n  \"assignedCoachId\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 23.290322580645,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 18.064516129\n  }\n },\n {\n  \"slotId\": \"P-W-29\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 29,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 24.193548387097,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 19.509677419\n  }\n },\n {\n  \"slotId\": \"P-W-30\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 30,\n  \"assignedCoachId\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 25.096774193548,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 20.95483871\n  }\n },\n {\n  \"slotId\": \"P-W-31\",\n  \"rail\": \"W\",\n  \"railOrdinal\": 31,\n  \"assignedCoachId\": null,\n  \"coordinate\": {\n   \"xTile\": -4,\n   \"yTile\": 26,\n   \"xMetersFromMapCenter\": -25.6,\n   \"zMetersFromMapCenter\": 22.4\n  }\n }\n];\n","modules/coach-world/coach-manifest.mjs":"// Pure catalog adapter for the reviewed coach manifest v2: 65 canonical coaches and 128 pedestal slots.\n// Data is the author-time literal in coach-manifest-data.mjs. Everything is frozen (deep); lookups are Map-backed and\n// return null for unknown IDs. No grant, unlock or availability claims live here; consumers keep their own rules.\n// Not yet used by roster, simulator, production, scene or shipCapacity code (those consumers are unchanged).\nimport {SOURCE_SHA256,COACH_ROW_LITERAL,PEDESTAL_SLOT_LITERAL} from './coach-manifest-data.mjs';\n\nexport const COACH_MANIFEST_VERSION='coach-manifest-v2';\nexport const COACH_MANIFEST_SOURCE_SHA256=SOURCE_SHA256;\n\nconst deepFreeze=v=>{if(v&&typeof v==='object'&&!Object.isFrozen(v)){Object.freeze(v);for(const k of Object.keys(v))deepFreeze(v[k]);}return v;};\n\n// canonicalCoachId is an explicit alias of coachId (the canonical identity). No field is derived from sourceVisualId or\n// sourceCoachId, and no prefix is ever stripped or guessed.\nconst coachRow=r=>({\n coachId:r.coachId,\n canonicalCoachId:r.coachId,\n canonicalLabel:r.canonicalLabel,\n slotId:r.slotId,\n sourceVisualId:r.sourceVisualId,\n sourceVisualKind:r.sourceVisualKind,\n sourceCoachId:r.sourceCoachId,\n section:structuredClone(r.section),\n element:structuredClone(r.element),\n raidRole:r.raidRole,\n simBase:r.simBase,\n simTagOverride:r.simTagOverride,\n simWpnOverride:r.simWpnOverride,\n quadrupedPolicyFlag:r.quadrupedPolicyFlag,\n roleCatalogVersion:r.roleCatalogVersion,\n roleStatus:r.roleStatus,\n capabilities:{...r.capabilities},\n abilityFaction:r.abilityFaction,\n});\n\nconst slotRecord=s=>({\n slotId:s.slotId,\n rail:s.rail,\n railOrdinal:s.railOrdinal,\n assignedCoachId:s.assignedCoachId,\n coordinate:{...s.coordinate},\n});\n\nexport const CANONICAL_COACHES=deepFreeze(COACH_ROW_LITERAL.map(coachRow));\nexport const PEDESTAL_SLOTS=deepFreeze(PEDESTAL_SLOT_LITERAL.map(slotRecord));\n\nconst COACH_BY_ID=new Map(CANONICAL_COACHES.map(row=>[row.coachId,row]));\nconst SLOT_BY_ID=new Map(PEDESTAL_SLOTS.map(slot=>[slot.slotId,slot]));\n\n// Exact canonical ID only. Visual IDs, labels, prefixes and non-strings are unknown and return null.\nexport function canonicalCoachDefinition(id){return typeof id==='string'&&COACH_BY_ID.has(id)?COACH_BY_ID.get(id):null;}\n\n// Exact slot ID only. Reserved slots (assignedCoachId null) are known and return their record.\nexport function pedestalSlotDefinition(slotId){return typeof slotId==='string'&&SLOT_BY_ID.has(slotId)?SLOT_BY_ID.get(slotId):null;}\n","modules/coach-world/entry.mjs":"// One visible door; the shared router owns opening/back/close behavior.\r\nexport function mountWorldEntry(){\r\n let disposed=false;const add=()=>{const settings=document.getElementById('settings');if(settings&&!settings.querySelector('[data-world-entry]')){const a=document.createElement('a');a.href='#world';a.dataset.worldEntry='';a.textContent='Open your War Room base';a.className='button';a.addEventListener('click',()=>settings.close());settings.append(a);}\r\n  const header=document.querySelector('body > .ship-header');if(header&&!document.querySelector('.ship-header [data-world-entry]')){const a=document.createElement('a');a.href='#world';a.dataset.worldEntry='';a.textContent='BASE';a.setAttribute('aria-label','Open War Room base');a.style.cssText='color:#d9efbc;font:700 11px monospace;letter-spacing:.12em;text-decoration:none;padding:12px 9px;border:1px solid #77937f66;border-radius:8px;margin-left:auto;margin-right:10px;background:#132a2cdd;';const ref=document.getElementById('openSettings');(ref?.parentNode||header).insertBefore(a,ref?.parentNode?ref:null);}\r\n };const observe=new MutationObserver(()=>{if(!disposed)add();});observe.observe(document.body,{childList:true,subtree:true});add();return ()=>{disposed=true;observe.disconnect();};\r\n}\r\nif(typeof document!=='undefined'){if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',()=>mountWorldEntry(),{once:true});else mountWorldEntry();}\r\n","modules/coach-world/expedition-catalog.mjs":"import {busyHarvestWorkers} from './rts-economy.mjs';\r\nimport {baseSystems,discountedCost} from './base-systems.mjs';\r\nimport {researchEffects,buildingHealth} from './rts-tech.mjs';\r\nimport {FACTIONS,SHIP_IDS} from './catalog.mjs';\r\nimport {COACH_REQUIREMENTS,STARTER_COACH_IDS} from '../../performance-catalog.mjs';\r\nexport const UNIT_CATALOG=Object.freeze([\r\n {id:'myr5-worker',label:'MYR5 builder',role:'worker',faction:null,buildingId:'hq',requiredLevel:1,supply:1,trainable:false,description:'Builds, repairs and carries expedition supplies. Never consumed.'},\r\n ...FACTIONS.flatMap(f=>[\r\n  {id:`grunt-${f.id}`,label:`${f.label} coach grunt`,role:'grunt',faction:f.id,buildingId:`hall-${f.id}`,requiredLevel:1,supply:1,trainable:false,description:'An available coach guards the group and escorts supplies.'},\r\n  {id:`boss-${f.id}`,label:`${f.label} coach boss`,role:'boss',faction:f.id,buildingId:`hall-${f.id}`,requiredLevel:3,supply:3,trainable:false,description:'The senior coach leads the expedition; permanent coach progress is protected.'},\r\n  {id:`companion-${f.id}-scout`,label:`${f.name} pathfinder`,role:'scout',faction:f.id,buildingId:`production-${f.id}`,requiredLevel:1,supply:1,trainable:true,materialCost:8,trainingMs:120000,description:'A four-legged companion scouts virtual terrain and guides the group.'},\r\n  {id:`companion-${f.id}-hauler`,label:`${f.name} pack companion`,role:'hauler',faction:f.id,buildingId:`production-${f.id}`,requiredLevel:3,supply:2,trainable:true,materialCost:15,trainingMs:240000,description:'A four-legged companion carries supplies and recovery gear.'},\r\n ]),\r\n].map(Object.freeze));\r\nexport const UNIT_BY_ID=Object.freeze(Object.fromEntries(UNIT_CATALOG.map(v=>[v.id,v])));\r\nexport const SHIP_ROLES=Object.freeze(Object.fromEntries(SHIP_IDS.map(id=>[id,Object.freeze({id,role:id==='supportive'?'supply-scout':'carrier',description:id==='supportive'?'Smallest starter supply shuttle.':'Larger carrier; capacity grows with earned hull and hangar tiers.'})])));\r\nexport function shipCapacity(base,shipId,unitIds=[]){\r\n if(!Object.hasOwn(base.ships??{},shipId))return null;\r\n const hauler=unitIds.some(id=>UNIT_BY_ID[id]?.role==='hauler');\r\n return (shipId==='supportive'?3:3+2*(base.ships[shipId].tier||1))+(hauler?2:0)+baseSystems(base).shipSupply;\r\n}\r\nexport function unitRoster(base){const reserved=new Set(['launched','returning'].includes(base.expedition?.status)?base.expedition.unitIds:[]),earned=new Set(base.coachAccess??STARTER_COACH_IDS);return UNIT_CATALOG.map(unit=>{const buildingLevel=base.buildings?.[unit.buildingId]?.level??0,coach=['grunt','boss'].includes(unit.role)?COACH_REQUIREMENTS.filter(c=>c.tracks.includes(unit.faction==='arms-shoulders'?'arms':unit.faction)&&earned.has(c.id)&&(unit.role==='boss'?c.difficulty==='expert':c.difficulty!=='expert')).sort((a,b)=>['easy','medium','hard','expert'].indexOf(a.difficulty)-['easy','medium','hard','expert'].indexOf(b.difficulty)||a.id.localeCompare(b.id))[0]:null,owned=unit.trainable?(base.units?.[unit.id]??0)>0:buildingLevel>=unit.requiredLevel&&(unit.role==='worker'||!!coach),sourceCoachId=unit.role==='worker'?'myr5':coach?.id??null,visualId=unit.role==='worker'?'worker:myr5':unit.trainable?`companion:${unit.faction}:${unit.role==='hauler'?'carrier':'scout'}`:sourceCoachId?`coach:${sourceCoachId}`:null;return {...unit,owned,available:owned&&!reserved.has(unit.id),reserved:reserved.has(unit.id),buildingLevel,unlocked:buildingLevel>=(unit.role==='hauler'&&researchEffects(base).advancedCompanionFacilities.includes(unit.buildingId)?2:unit.requiredLevel),training:base.training?.[unit.id]??null,count:unit.trainable?Math.max(0,base.units?.[unit.id]??0):owned?1:0,queued:Object.values(base.productionQueues??{}).flat().filter(j=>j.unitId===unit.id).reduce((n,j)=>n+j.count,0),sourceCoachId,visualId};});}\r\nexport function expeditionQuote(base,plan){\r\n const fail=reason=>({allowed:false,reason});if(base.rest)return fail('Leave rest mode before launching an expedition.');const systems=baseSystems(base);if(!plan||!FACTIONS.some(f=>f.id===plan.factionId))return fail('Choose an expedition civilization.');\r\n const facility=base.buildings?.[`expedition-${plan.factionId}`];if(!facility||facility.level<1||facility.job||buildingHealth(base,`expedition-${plan.factionId}`).offline||base.rtsTech?.repairs?.[`expedition-${plan.factionId}`])return fail('Build an idle launch post for this civilization first.');\r\n if(!Object.hasOwn(base.ships??{},plan.shipId))return fail('Choose an earned ship.');if(!Array.isArray(plan.unitIds)||!plan.unitIds.length||plan.unitIds.length>8||new Set(plan.unitIds).size!==plan.unitIds.length)return fail('Choose one to eight distinct available units.');\r\n if(plan.unitIds.includes('myr5-worker')&&busyHarvestWorkers({...base,expedition:null}).has('crew-1'))return fail('This builder is busy with construction or repair.');\r\n const roster=unitRoster({...base,expedition:null}),units=plan.unitIds.map(id=>roster.find(u=>u.id===id));if(units.some(u=>!u||!u.owned||u.faction&&u.faction!==plan.factionId))return fail('Selected units must be earned and belong to this civilization.');\r\n // Roles come from the owned roster, never from client-supplied quote fields.\r\n // Each benefit applies once per role; companions are permanent, not consumables.\r\n const roles=new Set(units.map(u=>u.role)),supply=units.reduce((n,u)=>n+u.supply,0),\r\n capacity=shipCapacity(base,plan.shipId,plan.unitIds),\r\n materialCost=discountedCost(Math.max(1,10+supply*2-(roles.has('worker')?2:0)),systems.materialDiscountPct),\r\n fuelCost=Math.max(1,10+supply*2-(roles.has('grunt')?2:0)-systems.launchFuelSaving),\r\n durationMs=Math.max(30000,Math.ceil((6-facility.level)*60000*(roles.has('scout')?.75:1)*(roles.has('boss')?.85:1)*(102-systems.journeySpeedPct)/100)),\r\n returnDurationMs=roles.has('hauler')?45000:60000,\r\n roleEffects=[\r\n  ...(systems.shipSupply>1?[`Habitat: +${systems.shipSupply-1} ship supply above standard.`]:[]),\r\n  ...(systems.materialDiscountPct?[`Silos: ${systems.materialDiscountPct}% material saving (rounded up).`]:[]),\r\n  ...(systems.launchFuelSaving?[`Generator: ${systems.launchFuelSaving} less launch fuel.`]:[]),\r\n  ...(systems.journeySpeedPct>2?[`Observatory: ${systems.journeySpeedPct-2}% shorter outbound journey.`]:[]),\r\n  ...(roles.has('worker')?['Builder: 2 fewer launch materials.']:[]),\r\n  ...(roles.has('grunt')?['Escort: 2 less launch fuel.']:[]),\r\n  ...(roles.has('scout')?['Scout: 25% shorter outbound journey.']:[]),\r\n  ...(roles.has('boss')?['Leader: 15% shorter outbound journey.']:[]),\r\n  ...(roles.has('hauler')?['Pack companion: +2 ship capacity and 25% faster return.']:[]),\r\n ];\r\n const result={factionId:plan.factionId,shipId:plan.shipId,unitIds:[...plan.unitIds],supply,capacity,materialCost,fuelCost,durationMs,returnDurationMs,roleEffects};\r\n if(supply>capacity)return {...result,...fail(`This ship carries ${capacity} supply; selected units use ${supply}.`)};\r\n if((base.materials?.[plan.factionId]??0)<materialCost)return {...result,...fail(`Need ${materialCost} matching materials to launch.`)};if(base.fuel<fuelCost)return {...result,...fail(`Need ${fuelCost} station fuel to launch.`)};\r\n return {...result,allowed:true,reason:null};\r\n}\r\n","modules/coach-world/game-generation-client.mjs":"// Game lifetime is independent of auth/dataEpoch and fitness/offline storage.\r\nconst META='myr5-game-generation-v1';\r\nlet current=null,inflight=null;\r\nconst listeners=new Set();\r\nconst valid=m=>Number.isSafeInteger(m?.gameGeneration)&&m.gameGeneration>=1&&Number.isSafeInteger(m?.gameResetAt)&&m.gameResetAt>=0;\r\nconst error=m=>Object.assign(Error('Game progress changed. Reopen the game.'),{code:'game_generation_mismatch',status:409,...m});\r\nexport function storedGameGeneration(storage=globalThis.localStorage){try{const m=JSON.parse(storage?.getItem(META)||'null');return valid(m)?m:null;}catch{return null;}}\r\nexport function gameCacheKey(key,storage=globalThis.localStorage){const m=current??storedGameGeneration(storage);return m?.gameGeneration>1?`${key}/game/${m.gameGeneration}`:key;}\r\nexport function gameRewardAfterCutoff(record,storage=globalThis.localStorage){const m=current??storedGameGeneration(storage);return !m?.gameResetAt||(Number.isSafeInteger(record?.startedAt)&&Number.isSafeInteger(record?.completedAt)&&record.startedAt>m.gameResetAt&&record.completedAt>=record.startedAt);}\r\nexport function captureGameGeneration(){if(!current)throw Object.assign(Error('Game metadata is still loading.'),{code:'game_generation_required'});return current.gameGeneration;}\r\nexport const gameGenerationHeaders=g=>{if(!Number.isSafeInteger(g)||g<1)throw error();return {'X-MYR5-Game-Generation':String(g)};};\r\nexport const subscribeGameGeneration=listener=>{listeners.add(listener);return()=>listeners.delete(listener);};\r\n// Only game previews and game event outboxes are removed. IndexedDB/workout queues,\r\n// auth, recipes, addresses, purchase receipts and account epochs are untouched.\r\nexport function invalidateEarnedGameCaches(storage=globalThis.localStorage){\r\n if(!storage)return;\r\n const keys=Array.from({length:storage.length},(_,i)=>storage.key(i));\r\n for(const key of keys)if(/^(?:myr5-roguelite-preview-v1\\/|myr5-world-run-outbox-v1\\/)/.test(key))storage.removeItem(key);\r\n}\r\nfunction publish(m,{storage=globalThis.localStorage}={}){\r\n if(!valid(m))throw error();\r\n const old=current??storedGameGeneration(storage);\r\n if(old&&m.gameGeneration<old.gameGeneration)throw error(old);\r\n const next={gameGeneration:m.gameGeneration,gameResetAt:m.gameResetAt};\r\n if(old&&old.gameGeneration===next.gameGeneration&&old.gameResetAt!==next.gameResetAt)throw error(old);\r\n current=next;\r\n if(!old||old.gameGeneration!==next.gameGeneration){\r\n  if(next.gameResetAt>0)invalidateEarnedGameCaches(storage);\r\n  storage?.setItem(META,JSON.stringify(next));\r\n  if(old){for(const fn of listeners)fn(next);globalThis.dispatchEvent?.(new CustomEvent('myr5:game-generation-changed',{detail:next}));}\r\n }\r\n return next;\r\n}\r\nexport async function bootstrapGameGeneration({fetchImpl,signal,storage=globalThis.localStorage,force=false}={}){\r\n if(!fetchImpl)fetchImpl=(await import('../../auth-client.mjs')).authFetch;\r\n // Always obtain authenticated metadata for a new client, rather than trusting local storage.\r\n if(inflight&&!force)return inflight;\r\n const pending=(async()=>{const response=await fetchImpl('/api/game-generation',{signal});const data=await response.json();if(!response.ok)throw Object.assign(Error(data.error||'Game metadata unavailable.'),{status:response.status});return publish(data,{storage});})();\r\n inflight=pending;try{return await pending;}finally{if(inflight===pending)inflight=null;}\r\n}\r\nexport function acceptGameGeneration(dto,expected,{storage=globalThis.localStorage}={}){\r\n if(!valid(dto))throw error(current);\r\n if(current&&dto.gameGeneration<current.gameGeneration)throw error(current);\r\n const changed=expected!=null&&dto.gameGeneration!==expected;\r\n publish(dto,{storage});if(changed)throw error(dto);return dto;\r\n}\r\nexport function isCurrentGameAccount(account=globalThis.myr5AuthenticatedAccount){const m=current??storedGameGeneration();return !m?.gameResetAt||!account?.user?.id||account.gameGeneration===m.gameGeneration;}\r\nexport const isGameRequest=path=>/^\\/api\\/(?:account(?:\\?|$)|world(?:\\/|\\?|$)|war-room(?:\\/|\\?|$))/.test(path);\r\n// Cross-tab reset invalidates in-memory writers as well as persisted game events.\r\nif(typeof globalThis.window==='object'&&typeof globalThis.addEventListener==='function')globalThis.addEventListener('storage',event=>{\r\n if(event.key!==META||!event.newValue)return;try{publish(JSON.parse(event.newValue));}catch{}\r\n});\r\n","modules/coach-world/geo.mjs":"// H3 indexes conquest sectors; spherical nearest-anchor influence is a separate layer.\r\nimport {latLngToCell,cellToBoundary,cellToLatLng,gridDisk,polygonToCells} from './vendor/h3.mjs';\r\nexport {latLngToCell,cellToBoundary,cellToLatLng,gridDisk,polygonToCells};\r\nexport const EARTH_METERS=6371008.8;\r\nexport function validCoordinate(lat,lng){return typeof lat==='number'&&typeof lng==='number'&&Number.isFinite(lat)&&Number.isFinite(lng)&&Math.abs(lat)<=90&&Math.abs(lng)<=180;}\r\nexport function distanceMeters(a,b,c,d){let lat1,lng1,lat2,lng2;if(typeof a==='number'){[lat1,lng1,lat2,lng2]=[a,b,c,d];}else{lat1=a.latitude??a.lat;lng1=a.longitude??a.lng;lat2=b.latitude??b.lat;lng2=b.longitude??b.lng;}if(!validCoordinate(lat1,lng1)||!validCoordinate(lat2,lng2))return Infinity;const rad=Math.PI/180,p=lat1*rad,q=lat2*rad,dl=(lng2-lng1)*rad;const h=Math.sin((q-p)/2)**2+Math.cos(p)*Math.cos(q)*Math.sin(dl/2)**2;return 2*EARTH_METERS*Math.atan2(Math.sqrt(Math.min(1,h)),Math.sqrt(Math.max(0,1-h)));}\r\nexport function unitVector(lat,lng){if(!validCoordinate(lat,lng))throw RangeError('Invalid geographic coordinate.');const p=lat*Math.PI/180,l=lng*Math.PI/180;return [Math.cos(p)*Math.cos(l),Math.cos(p)*Math.sin(l),Math.sin(p)];}\r\nexport function nearestAnchor(point,anchors){const u=unitVector(point.latitude??point.lat,point.longitude??point.lng);let best=null,score=-Infinity;for(const a of anchors){const v=unitVector(a.latitude??a.lat,a.longitude??a.lng),s=u[0]*v[0]+u[1]*v[1]+u[2]*v[2];if(s>score+1e-14||(Math.abs(s-score)<=1e-14&&String(a.id)<String(best?.id))){best=a;score=s;}}return best;}\r\nfunction inRing(x,y,ring){let inside=false;for(let i=0,j=ring.length-1;i<ring.length;j=i++){const [xi,yi]=ring[i],[xj,yj]=ring[j];if((yi>y)!==(yj>y)&&x<(xj-xi)*(y-yi)/(yj-yi)+xi)inside=!inside;}return inside;}\r\nexport function containsPoint(geometry,latitude,longitude){if(!validCoordinate(latitude,longitude)||!geometry)return false;const polys=geometry.type==='MultiPolygon'?geometry.coordinates:geometry.type==='Polygon'?[geometry.coordinates]:[];return polys.some(p=>inRing(longitude,latitude,p[0])&&!p.slice(1).some(r=>inRing(longitude,latitude,r)));}\r\nexport const sectorAt=(lat,lng)=>validCoordinate(lat,lng)?latLngToCell(lat,lng,10):null;\r\nexport function neighbors(id){try{return gridDisk(id,1).filter(v=>v!==id);}catch{return [];}}\r\nexport function conservativeNearby(a,b,max=100){if(!a||!b||!Number.isFinite(a.accuracy)||!Number.isFinite(b.accuracy)||a.accuracy<0||b.accuracy<0)return false;return distanceMeters(a,b)+a.accuracy+b.accuracy<=max;}\r\n","modules/coach-world/harvest-adapter.mjs":"// Harvest adapter: the one owner of base.rtsEconomy (version 2). It drives the pure trip engine (harvest-trips.mjs) from the station clock.\n// Canonical: base.rtsEconomy.tripState, whose bankOperatingMs is the only operating-energy bank.\n// Projection: base.rtsEconomy.projection = { gas, credited }. Credited is the cumulative deposit already paid into base.materials / gas, so each load credits once.\n// Gather assignments are standing orders: an idle assigned worker dispatches its next full load whenever the bank covers the whole round trip.\n// Trusted writes: awardAcceptedRunEnergy (server-only, receipt-idempotent). No public action adds energy.\nimport {BUILDING_BY_ID,FACTIONS,FACTION_BY_ID} from './catalog.mjs';\nimport {occupancy} from './navigation.mjs';\nimport {placeNodes} from './resource-placement.mjs';\nimport {HARVEST_RULES} from './harvest-rules.mjs';\nimport {DEFAULT_TRIP_CONFIG,advanceHarvest,assignTrip,createHarvestState,grantRunEnergy,loadHarvestState,quoteTrip,setLayout,setRest} from './harvest-trips.mjs';\nimport {baseSystems} from './base-systems.mjs';\n\nconst GRID=24,MAX_MS=Number.MAX_SAFE_INTEGER,KINDS=Object.freeze(['minerals','gas']),WORKER=/^crew-([1-8])$/;\nconst FULL={minerals:HARVEST_RULES.mineralStock,gas:HARVEST_RULES.gasStock};\nconst int=(v,min=0,max=MAX_MS)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\nconst obj=v=>v!==null&&typeof v==='object'&&!Array.isArray(v);\nconst fail=message=>{throw RangeError(message);};\nconst copy=v=>JSON.parse(JSON.stringify(v));\nconst sum=(a,b)=>{const r=a+b;if(!Number.isSafeInteger(r))fail('Resource balance is too large.');return r;};\nconst nodeIdOf=(faction,kind)=>`${faction}-${kind}`;\nconst active=w=>w.phase!=='idle';\nconst idleWorker=()=>({phase:'idle',remainingMs:0,nodeId:null,route:null,carried:null});\nconst crewIds=base=>Array.from({length:base.workers??0},(_,i)=>`crew-${i+1}`);\nconst busyAt=(spans,id,t)=>(spans.get(id)??[]).some(([s,e])=>t>=s&&t<e);\n\n// Busy reservations from jobs and expeditions. Travelling workers are reserved separately (busyHarvestWorkers).\nexport function busySpans(base){\n const m=new Map(),add=(w,s,e)=>{if(!w)return;if(!m.has(w))m.set(w,[]);m.get(w).push([int(s)?s:-Infinity,int(e)?e:Infinity]);};\n for(const b of Object.values(base.buildings??{}))if(b.job?.builderId)add(b.job.builderId,b.job.startedAt,b.job.dueAt);\n for(const r of Object.values(base.rtsTech?.repairs??{}))add(r.workerId,r.startedAt,r.dueAt);\n const x=base.expedition;if(['launched','returning'].includes(x?.status)&&x.unitIds?.includes('myr5-worker'))add('crew-1',x.launchedAt,x.status==='returning'?x.returnDueAt:x.dueAt+x.returnDurationMs);\n return m;\n}\nconst tripOf=base=>base.rtsEconomy?.version===2?base.rtsEconomy.tripState:null;\n// Builders in use: job/repair/expedition reservations, plus every worker on a committed trip (a new build can never take a travelling worker).\nexport function busyHarvestWorkers(base){\n const s=new Set(busySpans(base).keys()),t=tripOf(base);\n if(t)for(const [id,w] of Object.entries(t.workers))if(active(w))s.add(id);\n return s;\n}\n// Any committed trip holds the layout: buildings may not be placed or moved until every load has returned.\nexport function harvestLayoutLocked(base){const t=tripOf(base);return !!t&&Object.values(t.workers).some(active);}\n\n// True when a committed load walks any cell of the rectangle: a new footprint there would strand that load.\nexport function harvestRouteBlocked(base,x,y,w,h){\n const t=tripOf(base);if(!t)return false;\n const inside=p=>p.x>=x&&p.x<x+w&&p.y>=y&&p.y<y+h;\n return Object.values(t.workers).some(wk=>wk.route&&[...wk.route.outbound,...wk.route.inbound].some(inside));\n}\n\n// Grid cells the trip engine walks: occupied footprints are blocked; home is a free cell beside the HQ.\nexport function harvestLayoutOf(base,revision){\n const hq=base.buildings.hq,def=BUILDING_BY_ID.hq,blocked=occupancy(base),door=[];\n for(let x=hq.x;x<hq.x+def.w;x++)door.push({x,y:hq.y+def.h},{x,y:hq.y-1});\n for(let y=hq.y;y<hq.y+def.h;y++)door.push({x:hq.x+def.w,y},{x:hq.x-1,y});\n const free=p=>p.x>=0&&p.y>=0&&p.x<GRID&&p.y<GRID&&!blocked.has(p.y*GRID+p.x);\n const home=door.find(free);if(!home)fail('No free entrance beside the HQ.');\n const cells=[...blocked].sort((a,b)=>a-b).map(k=>({x:k%GRID,y:Math.floor(k/GRID)}));\n return {revision,cols:GRID,rows:GRID,home:{x:home.x,y:home.y},blocked:cells};\n}\n\n// Deposit sources: the real placed mineral formation and gas vent per faction (resource-nodes placeNodes). A faction without a placement\n// keeps its stock at the HQ door, where its gate (hall built) refuses every trip.\nexport function harvestNodesOf(base,home){\n const placed=new Map();\n for(const p of placeNodes(base)){placed.set(nodeIdOf(p.faction,'minerals'),p.node);placed.set(nodeIdOf(p.faction,'gas'),p.gas?.node??p.node);}\n return FACTIONS.flatMap(f=>KINDS.map(kind=>{const at=placed.get(nodeIdOf(f.id,kind))??home;return {id:nodeIdOf(f.id,kind),kind,faction:f.id,x:at.x,y:at.y,stock:FULL[kind]};}));\n}\n\nfunction freshTrip(base,now,stockOf=(faction,kind)=>FULL[kind]){\n const layout=harvestLayoutOf(base,1),nodes=harvestNodesOf(base,layout.home).map(n=>({...n,stock:stockOf(n.faction,n.kind)}));\n return createHarvestState({now,config:DEFAULT_TRIP_CONFIG,layout,nodes,workerIds:crewIds(base),bankOperatingMs:0});\n}\n\nfunction checkAssignments(a,base){\n if(!obj(a))fail('Invalid worker assignment.');\n const out={};\n for(const id of Object.keys(a).sort()){\n  const m=WORKER.exec(id),v=a[id];\n  if(!m||Number(m[1])>(base.workers??0)||!obj(v)||!KINDS.includes(v.kind)||!Object.hasOwn(FACTION_BY_ID,v.faction))fail('Invalid worker assignment.');\n  out[id]={kind:v.kind,faction:v.faction};\n }\n return out;\n}\nfunction checkProjection(p){\n if(!obj(p)||!int(p.gas)||!obj(p.credited))fail('Invalid harvesting projection.');\n const credited={};\n for(const [f,b] of Object.entries(p.credited)){if(!Object.hasOwn(FACTION_BY_ID,f)||!obj(b)||!int(b.minerals)||!int(b.gas))fail('Invalid harvesting projection.');credited[f]={minerals:b.minerals,gas:b.gas};}\n return {gas:p.gas,credited};\n}\n\n// Legacy version 1 (passive per-cycle income): keep finite stock, gas, materials (already in base.materials) and assignments.\n// Bank starts at zero, the old clock carry is discarded, and no income is accrued for the old period.\nfunction migrate(base,e){\n if(!int(e.lastAt)||!int(e.gas)||!obj(e.assignments)||!obj(e.nodes))fail('Invalid harvesting ledger.');\n for(const f of Object.keys(e.nodes))if(!Object.hasOwn(FACTION_BY_ID,f))fail('Unknown resource deposit.');\n const stockOf=(faction,kind)=>{const n=obj(e.nodes[faction])?e.nodes[faction][kind]:undefined;if(n===undefined)return FULL[kind];if(!int(n,0,FULL[kind]))fail('Invalid resource deposit.');return n;};\n return {version:2,tripState:freshTrip(base,e.lastAt,stockOf),assignments:checkAssignments(e.assignments,base),projection:{gas:e.gas,credited:{}}};\n}\n\n// Validates whatever is stored (or creates the fresh version 2 record). Never advances the clock.\nfunction ensureEconomy(base,now=0){\n const e=base.rtsEconomy;\n if(e==null)return {version:2,tripState:freshTrip(base,now),assignments:{},projection:{gas:0,credited:{}}};\n if(!obj(e))fail('Invalid harvesting state.');\n if(e.version===1)return migrate(base,e);\n if(e.version!==2)fail('Invalid harvesting state.');\n return {version:2,tripState:loadHarvestState(e.tripState),assignments:checkAssignments(e.assignments,base),projection:checkProjection(e.projection)};\n}\n\n// Idle crew for any station workers the engine does not know yet (the crew count only grows).\nfunction addCrew(st,base){\n const missing=crewIds(base).filter(id=>!Object.hasOwn(st.workers,id));\n if(!missing.length)return st;\n const workers={...st.workers};for(const id of missing)workers[id]=idleWorker();\n return {...st,workers};\n}\n\n// Positions follow the placed visuals. They move only while no trip is committed, and only through setLayout (revision +1) when blocked cells change.\nfunction syncLayout(st,out){\n const next=harvestLayoutOf(out,st.layout.revision);\n let s=st;\n if(JSON.stringify([next.home,next.blocked])!==JSON.stringify([st.layout.home,st.layout.blocked]))s=setLayout(s,{...next,revision:st.layout.revision+1},s.lastAt);\n const at=new Map(harvestNodesOf(out,s.layout.home).map(n=>[n.id,n]));\n return {...s,nodes:s.nodes.map(n=>({...n,x:at.get(n.id).x,y:at.get(n.id).y}))};\n}\n\n// A gather needs its deposit to be reachable on the station, open (hall, generator) and not exhausted. Checked before any dispatch.\nfunction gateOpen(out,st,a){\n if(a.faction!==out.faction&&!(out.buildings[`hall-${a.faction}`]?.level>0))return false;\n if(a.kind==='gas'&&(!(out.buildings.generator?.level>0)||out.rtsTech?.health?.generator===0))return false;\n return st.nodes.some(n=>n.id===nodeIdOf(a.faction,a.kind)&&n.stock>0);\n}\n\n// Standing orders: each idle, unreserved, assigned worker starts one full load if the bank covers its round trip.\nfunction dispatch(st,out){\n if(st.rest)return st;\n const spans=busySpans(out);\n for(const id of Object.keys(out.rtsEconomy.assignments).sort()){\n  const a=out.rtsEconomy.assignments[id],w=st.workers[id];\n  if(!w||active(w)||busyAt(spans,id,st.lastAt)||!gateOpen(out,st,a))continue;\n  try{st=assignTrip(st,{workerId:id,nodeId:nodeIdOf(a.faction,a.kind),now:st.lastAt,available:true});}\n  catch(error){if(!(error instanceof RangeError))throw error;} // bank short or no route: the worker waits idle\n }\n return st;\n}\n\n// Next instant something changes: a load step ends, or a job/repair/expedition reservation ends. The clock never steps through idle time.\nfunction nextBoundary(st,out,now){\n let t=now;\n for(const w of Object.values(st.workers))if(active(w))t=Math.min(t,st.lastAt+w.remainingMs);\n for(const spans of busySpans(out).values())for(const [,e] of spans)if(Number.isFinite(e)&&e>st.lastAt&&e<t)t=e;\n // Every construction due time is a boundary: a completed HQ level changes the pace (speedAt), even for seed-policy jobs with no builder.\n for(const b of Object.values(out.buildings??{}))if(b.job&&Number.isSafeInteger(b.job.dueAt)&&b.job.dueAt>st.lastAt&&b.job.dueAt<t)t=b.job.dueAt;\n return t;\n}\n\n// Movement pace from the completed HQ level at instant t. A job counts only once due. Offline HQ (0 HP) gives no bonus (baseSystems).\n// Pace is applied only at an idle boundary, so routes already walking keep the pace they were dispatched with.\nexport function harvestPaceAt(base,t){\n const hq=base.buildings?.hq,level=hq?.job&&hq.job.dueAt<=t?hq.job.targetLevel:hq?.level;\n const view={...base,buildings:{...base.buildings,hq:{...hq,level,job:null}}};\n const pct=baseSystems(view).movementSpeedPct;\n return Math.max(1,Math.round(DEFAULT_TRIP_CONFIG.moveMsPerCell*100/(100+pct)));\n}\n\n// Credits each new deposit exactly once: minerals into base.materials, gas into the projection.\nfunction creditDeposits(out,st){\n const p=out.rtsEconomy.projection;\n for(const [faction,bucket] of Object.entries(st.deposited)){\n  const c=p.credited[faction]??{minerals:0,gas:0},dm=bucket.minerals-c.minerals,dg=bucket.gas-c.gas;\n  if(dm)out.materials[faction]=sum(out.materials[faction]??0,dm);\n  if(dg)p.gas=sum(p.gas,dg);\n  p.credited[faction]={minerals:bucket.minerals,gas:bucket.gas};\n }\n}\n\n// Settles the station from its stored clock to `now`. Rest is read at the settled instant: every action advances before it changes the flag.\nfunction settleTo(out,now){\n const e=out.rtsEconomy;\n let st=setRest(e.tripState,!!out.rest,e.tripState.lastAt);\n if(!Object.values(st.workers).some(active))st=syncLayout(st,out);\n st=addCrew(st,out);\n for(;;){\n  // Idle boundary: pick up the station's current pace before anything new is dispatched. Walking routes keep the pace they were given.\n  if(!Object.values(st.workers).some(active))st={...st,config:{...st.config,moveMsPerCell:harvestPaceAt(out,st.lastAt)}};\n  st=dispatch(st,out);\n  if(st.lastAt>=now)break;\n  st=advanceHarvest(st,nextBoundary(st,out,now)).state;\n }\n e.tripState=st;\n creditDeposits(out,st);\n return out;\n}\n\nexport function advanceRtsEconomy(base,now){\n if(!int(now))fail('Invalid harvesting clock.');\n const out={...base,materials:{...base.materials},rtsEconomy:ensureEconomy(base,now)};\n if(now<out.rtsEconomy.tripState.lastAt)fail('Harvesting clock moved backwards.');\n return settleTo(out,now);\n}\n\n// Read-only quote. Travelling workers may take a new assignment: it applies when their current load returns.\nfunction gatherQuote(out,workerId,kind,faction){\n const bad=reason=>({allowed:false,reason}),st=out.rtsEconomy.tripState;\n if(out.rest)return bad('Resume the station before gathering.');\n if(busyAt(busySpans(out),workerId,st.lastAt))return bad('This builder is busy.');\n if(faction!==out.faction&&!(out.buildings[`hall-${faction}`]?.level>0))return bad('Build this civilization’s hall to reach its deposit.');\n if(kind==='gas'&&(!(out.buildings.generator?.level>0)||out.rtsTech?.health?.generator===0))return bad('Build a power generator to refine gas.');\n const node=st.nodes.find(n=>n.id===nodeIdOf(faction,kind));\n if(!node)return bad('Unknown resource deposit.');\n if(node.stock<=0)return bad('This deposit is exhausted.');\n const w=st.workers[workerId];\n if(w&&!active(w)){\n  const q=quoteTrip(st,{workerId,nodeId:node.id,now:st.lastAt});\n  if(!q.allowed)return bad(/Insufficient operating time/.test(q.reason)?(st.bankOperatingMs===0?'Complete a workout run to recharge workers.':'Not enough operating energy for a full round trip yet.'):q.reason);\n }\n return {allowed:true,reason:null,workerId,kind,faction,cycleMs:HARVEST_RULES.cycleMs,perCycle:kind==='gas'?HARVEST_RULES.gasPerCycle:HARVEST_RULES.mineralsPerCycle};\n}\n\nexport function harvestQuote(base,workerId,kind,faction=base.faction){\n return gatherQuote({...base,rtsEconomy:ensureEconomy(base)},workerId,kind,faction);\n}\n\n// Player commands: gather (standing order, refused when it cannot start) and stop-gather (the load in flight still returns).\nexport function applyRtsEconomyAction(base,action,{now}={}){\n if(!int(now))fail('Invalid harvesting clock.');\n const out=advanceRtsEconomy(base,now),e=out.rtsEconomy;\n if(action.type==='stop-gather'){\n  if(!WORKER.test(action.workerId)||!Object.hasOwn(e.assignments,action.workerId))fail('This worker is not gathering.');\n  delete e.assignments[action.workerId];\n  return out;\n }\n if(action.type!=='gather')fail('Unknown harvesting command.');\n const {workerId,kind}=action,faction=action.faction??base.faction;\n if(!WORKER.test(workerId)||Number(workerId.slice(5))>(base.workers??0))fail('Choose an available builder.');\n if(!KINDS.includes(kind)||!Object.hasOwn(FACTION_BY_ID,faction))fail('Choose minerals or gas at a known deposit.');\n const q=gatherQuote(out,workerId,kind,faction);\n if(!q.allowed)fail(q.reason);\n e.assignments={...e.assignments,[workerId]:{kind,faction}};\n return settleTo(out,now);\n}\n\nexport function economyState(base){\n const e=ensureEconomy(base),st=e.tripState,nodes={};\n for(const f of FACTIONS)nodes[f.id]={minerals:st.nodes.find(n=>n.id===nodeIdOf(f.id,'minerals'))?.stock??0,gas:st.nodes.find(n=>n.id===nodeIdOf(f.id,'gas'))?.stock??0};\n return {version:1,lastAt:st.lastAt,gas:e.projection.gas,assignments:copy(e.assignments),nodes};\n}\nexport const normalizeRtsEconomy=base=>({...base,rtsEconomy:ensureEconomy(base)});\n\n// Trusted, server-only: a host's accepted run grants operating energy once per receipt. Duplicate receipts and over-cap grants throw.\nexport function awardAcceptedRunEnergy(base,{receiptId,ms,now}={}){\n if(!int(now))fail('Invalid harvesting clock.');\n const out=advanceRtsEconomy(base,now);\n out.rtsEconomy.tripState=grantRunEnergy(out.rtsEconomy.tripState,{receipt:receiptId,ms,now});\n return settleTo(out,now);\n}\n\n// Read-only snapshot of the stored bank (as of the last settled clock). Clients and hosts never write it.\nexport function harvestEnergySnapshot(base){\n return {bankOperatingMs:base.rtsEconomy?.version===2?base.rtsEconomy.tripState.bankOperatingMs:0,version:1};\n}\n\n// Read-only HUD readout of the stored snapshot: the operating bank and each crew member's trip phase and load.\nexport function harvestReadout(base){\n const st=ensureEconomy(base).tripState,workers={};\n for(const [id,w] of Object.entries(st.workers))workers[id]={phase:w.phase,remainingMs:w.remainingMs,nodeId:w.nodeId,carried:w.carried?{kind:w.carried.kind,amount:w.carried.amount}:null};\n return {bankOperatingMs:st.bankOperatingMs,workers};\n}\n","modules/coach-world/harvest-rules.mjs":"// Harvesting constants, extracted unchanged from rts-economy.mjs so the pure trip engine can import them without a cycle.\n// rts-economy.mjs re-exports HARVEST_RULES for every existing reader.\nexport const HARVEST_RULES=Object.freeze({version:1,cycleMs:10000,mineralsPerCycle:5,gasPerCycle:4,mineralStock:1500,gasStock:750,maxCatchupMs:600000});\n","modules/coach-world/harvest-trips.mjs":"// RAID1021 M2 harvest trips: pure server-side accepted-state engine. Wired through harvest-adapter.mjs (the one owner of base.rtsEconomy).\n// Owns the shared operating clock, worker trip phases, finite node stock and deposited balances.\n// Pure: no DOM, no ambient time, no randomness, no network, no async crypto. All times are integer ms.\n// One trip = one load: outbound -> working (debits node stock once) -> returning -> depositing (credits once).\nimport {findPath} from './navigation.mjs';\nimport {HARVEST_RULES} from './harvest-rules.mjs';\n\nexport const TRIP_VERSION=1;\nexport const PHASES=Object.freeze(['idle','outbound','working','returning','depositing']);\nconst ACTIVE=new Set(PHASES.slice(1));\nconst KINDS=Object.freeze(['minerals','gas']);\nconst WORKER_RE=/^crew-[1-8]$/,ID_RE=/^[a-z0-9][a-z0-9-]{0,39}$/,RECEIPT_RE=/^[A-Za-z0-9:_.-]{1,80}$/;\nconst MAX_SIDE=256,MAX_NODES=64,MAX_MS=Number.MAX_SAFE_INTEGER;\n\n// PROPOSED tuning (ours, not Ian-selected). Work cycle and per-cycle yield reuse HARVEST_RULES; move speed, load size, deposit time and bank cap are named proposals.\nexport const DEFAULT_TRIP_CONFIG=Object.freeze({\n moveMsPerCell:400,loadCycles:3,depositMs:3000,workCycleMs:HARVEST_RULES.cycleMs,bankCapMs:600000,\n perCycle:Object.freeze({minerals:HARVEST_RULES.mineralsPerCycle,gas:HARVEST_RULES.gasPerCycle}),\n});\n\nconst fail=message=>{throw new RangeError(message);};\nconst isInt=(v,min,max)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\nconst isObj=v=>v!==null&&typeof v==='object'&&!Array.isArray(v);\nconst copy=v=>JSON.parse(JSON.stringify(v));\nconst keyOf=(cols,p)=>p.y*cols+p.x;\nconst blockedSet=L=>new Set(L.blocked.map(p=>keyOf(L.cols,p)));\nconst idleWorker=()=>({phase:'idle',remainingMs:0,nodeId:null,route:null,carried:null});\n// Plain maps keyed by player/data strings: only own properties count. Inherited keys ('constructor', '__proto__') never resolve, and writes define an own data property (a plain assignment to '__proto__' would hit the inherited setter).\nconst getOwn=(o,k)=>Object.hasOwn(o,k)?o[k]:undefined;\nconst setOwn=(o,k,v)=>Object.defineProperty(o,k,{value:v,enumerable:true,writable:true,configurable:true});\n// Checked integer sum: fails closed instead of silently losing precision past MAX_SAFE_INTEGER.\nconst sumSafe=values=>{let total=0;for(const v of values){total+=v;if(!Number.isSafeInteger(total))fail('Integer overflow in harvest totals.');}return total;};\nconst workMs=c=>c.loadCycles*c.workCycleMs;\nconst loadUnits=(c,kind)=>c.loadCycles*c.perCycle[kind];\nconst activeIds=s=>Object.keys(s.workers).sort().filter(id=>ACTIVE.has(s.workers[id].phase));\n\n// Deterministic 32-bit FNV-1a. Detects accidental route/layout drift only; not a security hash.\nexport function plainHash(text){let h=0x811c9dc5;for(let i=0;i<text.length;i++)h=Math.imul(h^text.charCodeAt(i),0x01000193)>>>0;return h.toString(16).padStart(8,'0');}\n\nfunction validateConfig(c){\n if(!isObj(c)||!isObj(c.perCycle))fail('Invalid trip config.');\n if(!isInt(c.moveMsPerCell,1,60000)||!isInt(c.loadCycles,1,50)||!isInt(c.depositMs,1,600000)||!isInt(c.workCycleMs,1,600000)||!isInt(c.bankCapMs,1,MAX_MS)||!isInt(c.perCycle.minerals,1,1e6)||!isInt(c.perCycle.gas,1,1e6))fail('Invalid trip config.');\n return {moveMsPerCell:c.moveMsPerCell,loadCycles:c.loadCycles,depositMs:c.depositMs,workCycleMs:c.workCycleMs,bankCapMs:c.bankCapMs,perCycle:{minerals:c.perCycle.minerals,gas:c.perCycle.gas}};\n}\n\nfunction validateLayout(layout){\n if(!isObj(layout)||!isInt(layout.revision,0,MAX_MS)||!isInt(layout.cols,1,MAX_SIDE)||!isInt(layout.rows,1,MAX_SIDE))fail('Invalid layout.');\n const {cols,rows}=layout,inside=p=>isObj(p)&&isInt(p.x,0,cols-1)&&isInt(p.y,0,rows-1);\n if(!inside(layout.home))fail('Home cell is outside the grid.');\n if(!Array.isArray(layout.blocked))fail('Invalid blocked cells.');\n const seen=new Set(),blocked=[];\n for(const p of layout.blocked){if(!inside(p))fail('Blocked cell is outside the grid.');const k=keyOf(cols,p);if(seen.has(k))fail('Duplicate blocked cell.');seen.add(k);blocked.push({x:p.x,y:p.y});}\n if(seen.has(keyOf(cols,layout.home)))fail('Home cell is blocked.');\n blocked.sort((a,b)=>a.y-b.y||a.x-b.x);\n return {revision:layout.revision,cols,rows,home:{x:layout.home.x,y:layout.home.y},blocked};\n}\n\nfunction validateNodes(nodes,L){\n if(!Array.isArray(nodes)||nodes.length>MAX_NODES)fail('Invalid resource nodes.');\n const ids=new Set(),out=[];\n for(const n of nodes){\n  if(!isObj(n)||!ID_RE.test(n.id)||ids.has(n.id)||!KINDS.includes(n.kind)||!ID_RE.test(n.faction)||!isInt(n.x,0,L.cols-1)||!isInt(n.y,0,L.rows-1)||!isInt(n.stock,0,MAX_MS))fail('Invalid resource node.');\n  ids.add(n.id);out.push({id:n.id,kind:n.kind,faction:n.faction,x:n.x,y:n.y,stock:n.stock});\n }\n return out.sort((a,b)=>a.id<b.id?-1:a.id>b.id?1:0);\n}\n\nconst routeHash=(L,r)=>plainHash(JSON.stringify([L.revision,L.cols,L.rows,L.home,L.blocked,r.nodeId,r.moveMsPerCell,r.outbound,r.inbound]));\nfunction finishRoute(L,r){\n const route={nodeId:r.nodeId,layoutRevision:L.revision,moveMsPerCell:r.moveMsPerCell,outbound:r.outbound,inbound:r.inbound,outboundMs:r.outbound.length*r.moveMsPerCell,inboundMs:r.inbound.length*r.moveMsPerCell};\n route.hash=routeHash(L,route);\n return route;\n}\n\n// Total active time still owed by one worker's committed trip (outbound + work + return + deposit from its current phase).\nfunction remainingTripMs(c,w){\n const r=w.route;\n switch(w.phase){\n  case 'outbound':return w.remainingMs+workMs(c)+r.inboundMs+c.depositMs;\n  case 'working':return w.remainingMs+r.inboundMs+c.depositMs;\n  case 'returning':return w.remainingMs+c.depositMs;\n  case 'depositing':return w.remainingMs;\n  default:return 0;\n }\n}\nconst maxActiveRemaining=s=>activeIds(s).reduce((m,id)=>Math.max(m,remainingTripMs(s.config,s.workers[id])),0);\n\n// Zero-length phases and phase ends at the same instant settle here, workers in ID order, repeated until nothing is due.\nfunction settle(s){\n let changed=true;\n while(changed){\n  changed=false;\n  for(const id of activeIds(s)){\n   const w=s.workers[id];\n   if(w.remainingMs!==0)continue;\n   changed=true;\n   if(w.phase==='outbound'){w.phase='working';w.remainingMs=workMs(s.config);}\n   else if(w.phase==='working'){\n    const node=s.nodes.find(n=>n.id===w.nodeId),amount=Math.min(node.stock,loadUnits(s.config,node.kind));\n    node.stock-=amount;\n    w.carried={kind:node.kind,faction:node.faction,amount,nodeId:node.id};\n    w.phase='returning';w.remainingMs=w.route.inboundMs;\n   }\n   else if(w.phase==='returning'){w.phase='depositing';w.remainingMs=s.config.depositMs;}\n   else if(w.phase==='depositing'){\n    const c=w.carried;\n    if(!Object.hasOwn(s.deposited,c.faction))setOwn(s.deposited,c.faction,{minerals:0,gas:0});\n    const bucket=getOwn(s.deposited,c.faction);\n    bucket[c.kind]+=c.amount;\n    Object.assign(w,idleWorker());\n   }\n  }\n }\n}\n\n// Advances in place from s.lastAt to now. Burns the shared clock once per ms while ANY trip is active; rest pauses everything.\n// maxEvents bounds burn steps; returns false (lastAt < now) when the cap is hit, and the next call resumes from the same cursor.\nfunction advanceInPlace(s,now,maxEvents){\n if(!isInt(now,0,MAX_MS)||now<s.lastAt)fail('Harvesting clock moved backwards.');\n let events=0;\n for(;;){\n  settle(s);\n  if(s.lastAt===now)return true;\n  const act=activeIds(s);\n  if(s.rest||!act.length){s.lastAt=now;return true;}\n  if(events>=maxEvents)return false;\n  const next=Math.min(...act.map(id=>s.workers[id].remainingMs)),dt=Math.min(now-s.lastAt,next);\n  s.bankOperatingMs-=dt;\n  if(s.bankOperatingMs<0)fail('Operating time bank underflow.');\n  for(const id of act)s.workers[id].remainingMs-=dt;\n  s.lastAt+=dt;events+=1;\n }\n}\n\nexport function createHarvestState({now,config,layout,nodes,workerIds,bankOperatingMs=0}){\n if(!isInt(now,0,MAX_MS))fail('Invalid harvesting clock.');\n const cfg=validateConfig(config),L=validateLayout(layout),N=validateNodes(nodes,L);\n if(!isInt(bankOperatingMs,0,cfg.bankCapMs))fail('Invalid operating time bank.');\n if(!Array.isArray(workerIds)||!workerIds.length||workerIds.length>8||new Set(workerIds).size!==workerIds.length||workerIds.some(id=>!WORKER_RE.test(id)))fail('Invalid workers.');\n const workers={};for(const id of [...workerIds].sort())workers[id]=idleWorker();\n const originStock=N.reduce((sum,n)=>sum+n.stock,0);\n if(!isInt(originStock,0,MAX_MS))fail('Resource stock overflow.');\n return {version:TRIP_VERSION,lastAt:now,rest:false,bankOperatingMs,config:cfg,layout:L,nodes:N,workers,deposited:{},grants:{},originStock};\n}\n\n// Read-only conservation view. conserved = node stock + carried + deposited equals the origin stock.\nexport function ledgerTotals(s){\n const nodeStock=sumSafe(s.nodes.map(n=>n.stock)),carried=sumSafe(Object.values(s.workers).map(w=>w.carried?.amount??0));\n const deposited=sumSafe(Object.values(s.deposited).flatMap(b=>[b.minerals,b.gas])),grantedMs=sumSafe(Object.values(s.grants));\n return {bankOperatingMs:s.bankOperatingMs,grantedMs,nodeStock,carried,deposited,originStock:s.originStock,conserved:nodeStock+carried+deposited===s.originStock};\n}\n\nfunction planTrip(s,{workerId,nodeId,available=true}){\n const no=reason=>({allowed:false,reason});\n const w=getOwn(s.workers,workerId);\n if(s.rest)return no('Resume the station before sending a harvest trip.');\n if(!w)return no('Choose an available builder.');\n if(w.phase!=='idle')return no('This builder is already on a harvest trip.');\n if(available!==true)return no('This builder is busy with another job.');\n const node=s.nodes.find(n=>n.id===nodeId);\n if(!node)return no('Unknown resource deposit.');\n if(node.stock<=0)return no('This deposit is exhausted.');\n const L=s.layout,blocked=blockedSet(L),here={x:node.x,y:node.y};\n const out=findPath(L.cols,L.rows,blocked,L.home,here);\n if(!out)return no('No legal route from home to this deposit.');\n const back=findPath(L.cols,L.rows,blocked,here,L.home);\n if(!back)return no('No legal route back home from this deposit.');\n const route=finishRoute(L,{nodeId,moveMsPerCell:s.config.moveMsPerCell,outbound:out,inbound:back});\n const reserveMs=route.outboundMs+workMs(s.config)+route.inboundMs+s.config.depositMs;\n const need=Math.max(maxActiveRemaining(s),reserveMs);\n if(s.bankOperatingMs<need)return no(`Insufficient operating time: this round trip reserves ${reserveMs} ms; bank has ${s.bankOperatingMs} ms.`);\n return {allowed:true,reason:null,workerId,nodeId,kind:node.kind,faction:node.faction,loadUnits:loadUnits(s.config,node.kind),reserveMs,route};\n}\n\n// Read-only: advances a copy to now, then reports whether the trip is legal and what it reserves.\nexport function quoteTrip(state,req){\n const s=copy(state);advanceInPlace(s,req.now,Infinity);\n return planTrip(s,req);\n}\n\n// Dispatch: refuses (throws RangeError with the reason) unless the full round trip is reserved in the bank. Returns a new state.\nexport function assignTrip(state,req){\n const s=copy(state);advanceInPlace(s,req.now,Infinity);\n const q=planTrip(s,req);\n if(!q.allowed)fail(q.reason);\n Object.assign(s.workers[req.workerId],{phase:'outbound',remainingMs:q.route.outboundMs,nodeId:req.nodeId,route:q.route});\n settle(s);\n return s;\n}\n\n// {state,done}. done=false means maxEvents was hit: call again with the same now to resume. Same-instant ties are settled before the cap returns.\nexport function advanceHarvest(state,now,{maxEvents=Infinity}={}){\n const s=copy(state),done=advanceInPlace(s,now,maxEvents);\n return {state:s,done};\n}\n\n// Rest is a state flag set at an explicit boundary: advance-then-toggle. Rest pauses trips and burns nothing.\nexport function setRest(state,rest,now){\n if(typeof rest!=='boolean')fail('Invalid rest flag.');\n const s=copy(state);advanceInPlace(s,now,Infinity);\n s.rest=rest;\n return s;\n}\n\n// Layout changes are refused while any committed route is not yet idle; the caller must advance to return first.\nexport function setLayout(state,layout,now){\n const s=copy(state);advanceInPlace(s,now,Infinity);\n if(activeIds(s).length)fail('Layout is locked while a committed route is active.');\n const L=validateLayout(layout);\n if(L.revision<=s.layout.revision)fail('Layout revision must increase.');\n validateNodes(s.nodes,L);\n s.layout=L;\n return s;\n}\n\n// Trusted-caller grant only (never exported to clients). Receipt is single-use; bank cannot exceed its cap.\nexport function grantRunEnergy(state,{receipt,ms,now}){\n const s=copy(state);advanceInPlace(s,now,Infinity);\n if(typeof receipt!=='string'||!RECEIPT_RE.test(receipt))fail('Invalid grant receipt.');\n if(Object.hasOwn(s.grants,receipt))fail('Receipt already granted.');\n if(!isInt(ms,1,MAX_MS))fail('Invalid run energy grant.');\n if(s.bankOperatingMs+ms>s.config.bankCapMs)fail('Operating time bank would exceed its cap.');\n sumSafe([...Object.values(s.grants),ms]);\n s.bankOperatingMs+=ms;setOwn(s.grants,receipt,ms);\n return s;\n}\n\nexport function serializeHarvestState(state){return JSON.stringify(state);}\n\n// Validates everything, rebuilds canonical shapes, verifies route hashes and bindings, and checks conservation and bank coverage.\nexport function loadHarvestState(input){\n const raw=typeof input==='string'?JSON.parse(input):copy(input);\n if(!isObj(raw)||raw.version!==TRIP_VERSION)fail('Invalid harvest trip state.');\n if(!isInt(raw.lastAt,0,MAX_MS)||typeof raw.rest!=='boolean')fail('Invalid harvest clock.');\n const config=validateConfig(raw.config),layout=validateLayout(raw.layout),nodes=validateNodes(raw.nodes,layout),blocked=blockedSet(layout);\n if(!isInt(raw.bankOperatingMs,0,config.bankCapMs)||!isInt(raw.originStock,0,MAX_MS))fail('Invalid operating time bank.');\n if(!isObj(raw.workers))fail('Invalid workers.');\n const ids=Object.keys(raw.workers).sort();\n if(!ids.length||ids.length>8||ids.some(id=>!WORKER_RE.test(id)))fail('Invalid workers.');\n const workers={};\n for(const id of ids){\n  const w=raw.workers[id];\n  if(!isObj(w)||!PHASES.includes(w.phase))fail('Invalid worker phase.');\n  if(w.phase==='idle'){\n   if(w.remainingMs!==0||w.nodeId!==null||w.route!==null||w.carried!==null)fail('Idle worker carries trip state.');\n   workers[id]=idleWorker();continue;\n  }\n  const node=nodes.find(n=>n.id===w.nodeId),r=w.route;\n  if(!node||!isObj(r)||r.nodeId!==node.id||!Array.isArray(r.outbound)||!Array.isArray(r.inbound))fail('Route node mismatch.');\n  const route=finishRoute(layout,{nodeId:node.id,moveMsPerCell:r.moveMsPerCell,outbound:r.outbound,inbound:r.inbound});\n  if(route.hash!==r.hash||r.layoutRevision!==layout.revision||r.moveMsPerCell!==config.moveMsPerCell)fail('Route binding is stale.');\n  const walk=(start,cells)=>cells.every((p,i)=>{const prev=i?cells[i-1]:start;return isInt(p.x,0,layout.cols-1)&&isInt(p.y,0,layout.rows-1)&&!blocked.has(keyOf(layout.cols,p))&&Math.abs(p.x-prev.x)+Math.abs(p.y-prev.y)===1;});\n  const endOf=(start,cells)=>cells.length?cells[cells.length-1]:start;\n  if(!walk(layout.home,route.outbound)||!walk({x:node.x,y:node.y},route.inbound))fail('Route crosses blocked cells.');\n  const at=endOf(layout.home,route.outbound),back=endOf({x:node.x,y:node.y},route.inbound);\n  if(at.x!==node.x||at.y!==node.y||back.x!==layout.home.x||back.y!==layout.home.y)fail('Route does not join home and deposit.');\n  const full={outbound:route.outboundMs,working:workMs(config),returning:route.inboundMs,depositing:config.depositMs}[w.phase];\n  if(!isInt(w.remainingMs,1,full))fail('Invalid phase remaining time.');\n  let carried=null;\n  if(w.phase==='returning'||w.phase==='depositing'){\n   const c=w.carried;\n   if(!isObj(c)||c.kind!==node.kind||c.faction!==node.faction||c.nodeId!==node.id||!isInt(c.amount,0,loadUnits(config,node.kind)))fail('Invalid carried load.');\n   carried={kind:c.kind,faction:c.faction,amount:c.amount,nodeId:c.nodeId};\n  }else if(w.carried!==null)fail('Carried load before work completes.');\n  workers[id]={phase:w.phase,remainingMs:w.remainingMs,nodeId:node.id,route,carried};\n }\n const deposited={};\n if(!isObj(raw.deposited))fail('Invalid deposits.');\n for(const [faction,b] of Object.entries(raw.deposited)){\n  if(!ID_RE.test(faction)||!isObj(b)||!isInt(b.minerals,0,MAX_MS)||!isInt(b.gas,0,MAX_MS))fail('Invalid deposits.');\n  setOwn(deposited,faction,{minerals:b.minerals,gas:b.gas});\n }\n const grants={};\n if(!isObj(raw.grants))fail('Invalid grants.');\n for(const [receipt,ms] of Object.entries(raw.grants)){if(!RECEIPT_RE.test(receipt)||!isInt(ms,1,config.bankCapMs))fail('Invalid grants.');setOwn(grants,receipt,ms);}\n const s={version:TRIP_VERSION,lastAt:raw.lastAt,rest:raw.rest,bankOperatingMs:raw.bankOperatingMs,config,layout,nodes,workers,deposited,grants,originStock:raw.originStock};\n if(!ledgerTotals(s).conserved)fail('Node, carry and deposit totals do not match origin stock.');\n if(s.bankOperatingMs<maxActiveRemaining(s))fail('Operating time cannot cover committed trips.');\n return s;\n}\n","modules/coach-world/hub-window.mjs":"﻿import {authFetch} from '../../auth-client.mjs';\r\nimport {authTransitions} from '../../auth-transition.mjs';\r\nimport {performanceOwner} from '../../performance-progress.mjs';\r\nimport {assertBase,normalizeBase,newBase} from './state.mjs';\r\nimport {BUILDINGS,FACTION_BY_ID} from './catalog.mjs';\r\nimport {createTerrain,drawTerrain} from './terrain.mjs';\r\nimport {getSpriteCanvas} from './pixel-art.mjs';\r\n\r\n// Selected identity is independent of the environment/flight biome. A missing\r\n// saved base must never silently claim the default preview's Chest clan.\r\nexport function hubClanIdentity(base){return base&&Object.hasOwn(FACTION_BY_ID,base.faction)?base.faction:null;}\r\n\r\nexport function guestHubBase({storage,owner,account}={}){if(account?.user?.id||typeof owner!=='string'||!owner.startsWith('guest:')||owner==='guest:unavailable')return null;try{return assertBase(normalizeBase(JSON.parse(storage.getItem(`myr5-coach-world-v1/${encodeURIComponent(owner)}`))));}catch{return null;}}\r\nexport function createHubBaseController({apply,request,transitions,getAccount,readGuest}){\r\n let disposed=false,generation=0,pending=null;const reset=()=>{generation++;pending?.abort();pending=null;if(!disposed)apply(null);},unsubscribe=transitions.subscribe(reset);\r\n return {reset,async sync(){reset();if(disposed)return;const run=generation,account=getAccount();if(!account?.user?.id){try{apply(readGuest());}catch{/* Keep the neutral window if device storage is blocked. */}return;}const owner=account.user.id,epoch=account.dataEpoch;if(!Number.isSafeInteger(epoch)||epoch<1)return;let ticket;try{ticket=transitions.capture();}catch{return;}const requestController=new AbortController();pending=requestController;const abort=()=>requestController.abort();ticket.signal.addEventListener('abort',abort,{once:true});\r\n  try{const response=await request('/api/world',{method:'GET',headers:{'X-Target-Account':owner,'X-Expected-Data-Epoch':String(epoch)},signal:requestController.signal,cache:'no-store'});if(disposed||run!==generation||!transitions.isCurrent(ticket)||!response.ok)return;const data=await response.json(),current=getAccount();if(disposed||run!==generation||!transitions.isCurrent(ticket)||current?.user?.id!==owner||current?.dataEpoch!==epoch||data.targetAccountId!==owner||data.dataEpoch!==epoch)return;const base=assertBase(data.base);apply(base,{faction:FACTION_BY_ID[data.environment?.faction]?data.environment.faction:base.faction,phase:data.environment?.phase||'stable'});\r\n  }catch{/* A private base stays hidden whenever account authority is unavailable. */}finally{ticket.signal.removeEventListener('abort',abort);if(pending===requestController)pending=null;}\r\n },dispose(){reset();disposed=true;unsubscribe?.();}};\r\n}\r\n\r\n// A static 2D window in the existing character port: no GPU or animation loop.\r\nexport function mountHubBaseWindow(host){\r\n const canvas=document.createElement('canvas'),image=document.createElement('img');image.className='home-base-window';image.alt='';image.setAttribute('aria-hidden','true');canvas.width=640;canvas.height=360;host.prepend(image);const ctx=canvas.getContext('2d');let disposed=false;\r\n function paint(base,environment={}){if(disposed)return;const saved=!!base,clan=hubClanIdentity(base);image.dataset.baseFaction=clan||'neutral';image.dataset.savedBase=String(saved);host.dataset.baseFaction=clan||'neutral';host.dataset.savedBase=String(saved);globalThis.dispatchEvent(new CustomEvent('myr5:hub-base',{detail:{faction:clan,saved}}));base??=newBase('hub-window-preview');const faction=FACTION_BY_ID[environment.faction||base.faction]||FACTION_BY_ID.chest;ctx.imageSmoothingEnabled=false;ctx.fillStyle='#14212c';ctx.fillRect(0,0,640,360);ctx.fillStyle=clan?faction.dark:'#344650';ctx.fillRect(0,38,640,170);ctx.fillStyle='#182933';for(let i=0;i<12;i++)ctx.fillRect(i*59-18,78+(i%3)*12,78,130);const terrain=createTerrain(base);ctx.save();ctx.translate(32,42);ctx.scale(1,.5);drawTerrain(ctx,terrain,{unit:24,ox:0,oy:0});ctx.restore();\r\n  for(const def of [...BUILDINGS].sort((a,b)=>(base.buildings[a.id].y+a.h)-(base.buildings[b.id].y+b.h))){const b=base.buildings[def.id],level=b.level,kind=level===0?'foundation':['hq','hall','production','expedition','care','observatory','habitat','hangar'].includes(def.kind)?def.kind:'building',f=def.track||base.faction,size=def.kind==='hq'?80:48,x=32+(b.x+def.w/2)*24,y=42+(b.y+def.h)*12;ctx.drawImage(getSpriteCanvas(f,kind,Math.max(0,Math.min(4,level-1))),Math.round(x-size/2),Math.round(y-size),size,size);}\r\n  ctx.fillStyle='#101d2666';ctx.fillRect(0,0,640,360);const ship=base.ships?.[base.selectedShip]||{tier:0};ctx.drawImage(getSpriteCanvas(faction.id,'ship',Math.min(4,ship.tier||0)),500,35,48,48);image.src=canvas.toDataURL('image/png');\r\n }\r\n const controller=createHubBaseController({apply:paint,request:authFetch,transitions:authTransitions(),getAccount:()=>globalThis.myr5AuthenticatedAccount,readGuest:()=>{let storage;try{storage=localStorage;}catch{}return guestHubBase({storage,owner:performanceOwner(storage??null),account:globalThis.myr5AuthenticatedAccount});}}),abort=new AbortController();paint(null);\r\n const sync=()=>void controller.sync();for(const event of ['myr5:account-ready','myr5:login-ready','myr5:account-progress','myr5:world-updated','myr5:world-saved','pageshow'])globalThis.addEventListener(event,sync,{signal:abort.signal});globalThis.addEventListener('storage',event=>{if(event.key?.startsWith('myr5-coach-world-v1/'))sync();},{signal:abort.signal});document.addEventListener('close',event=>{if(event.target?.id==='coachWorldPanel')sync();},{capture:true,signal:abort.signal});sync();\r\n // Existing World identity readout updates after its successful same-tab save.\r\n // Treat it only as an invalidation signal, then re-read through owner guards.\r\n const saves=new MutationObserver(records=>{if(records.some(record=>(record.target.nodeType===1?record.target:record.target.parentElement)?.closest?.('#coachWorldPanel [data-identity]')))sync();});saves.observe(document.body,{subtree:true,childList:true,characterData:true});\r\n return {dispose(){if(disposed)return;disposed=true;saves.disconnect();controller.dispose();abort.abort();image.remove();delete host.dataset.baseFaction;delete host.dataset.savedBase;canvas.width=canvas.height=0;}};\r\n}\r\n","modules/coach-world/levels.mjs":"export const LEVEL_XP = Object.freeze([0, 3, 7, 12, 18, 25, 33, 42, 52, 63, 75, 88, 102, 117, 133, 150, 168, 187, 207, 228]);\r\n\r\nexport const UNLOCKS = Object.freeze([\r\n  Object.freeze({ level: 1, id: 'home' }),\r\n  Object.freeze({ level: 2, id: 'flag' }),\r\n  Object.freeze({ level: 3, id: 'gym' }),\r\n  Object.freeze({ level: 5, id: 'work' }),\r\n  Object.freeze({ level: 10, id: 'npc-raids' })\r\n]);\r\n","modules/coach-world/location-drop-rules.mjs":"import {EXERCISES,GROUP_EXERCISES} from '../../exercise-library.mjs';\r\nimport {GROUP_TO_FACTION} from './catalog.mjs';\r\nexport const DROP_KINDS=Object.freeze(['home','work','gym']);\r\nexport const DROP_RULES=Object.freeze({cooldownMs:86400000,challengeMs:120000,dwellMs:10000,radiusMeters:60,maxAccuracy:25,rounds:10,gymPacks:3});\r\n// Bonus XP only: never manufacture workout sessions, coach milestones or battle charge.\r\nexport function gymCircuitReward(performance={}){\r\n const selected=new Set(performance.paths||[]),best=new Map();\r\n for(const session of Object.values(performance.sessions||{})){\r\n  const exercise=EXERCISES[session.mode],faction=GROUP_TO_FACTION[exercise?.group];if(!exercise||!faction||selected.size&&!selected.has(faction)||!Number.isFinite(session.xp)||session.xp<=0)continue;\r\n  const level=(GROUP_EXERCISES[exercise.group]||[]).findIndex(e=>e.id===exercise.id)+1,prior=best.get(exercise.group);\r\n  if(!prior||level>prior.level||level===prior.level&&session.xp>prior.xpPerRound)best.set(exercise.group,{mode:exercise.id,name:exercise.name,group:exercise.group,faction,level,xpPerRound:session.xp});\r\n }\r\n const exercises=[...best.values()].sort((a,b)=>a.group.localeCompare(b.group)),xp=Math.round(exercises.reduce((sum,e)=>sum+e.xpPerRound*DROP_RULES.rounds,0)*1000)/1000;\r\n return {rounds:DROP_RULES.rounds,exercises,xp,secretPacks:DROP_RULES.gymPacks,eligible:exercises.length>0,basis:'highest-completed-mode-per-active-workout-family'};\r\n}\r\nexport function dropDistance(a,b){const r=Math.PI/180,p=(b.latitude-a.latitude)*r,q=(b.longitude-a.longitude)*r;return 12742000*Math.asin(Math.min(1,Math.sqrt(Math.sin(p/2)**2+Math.cos(a.latitude*r)*Math.cos(b.latitude*r)*Math.sin(q/2)**2)));}\r\n","modules/coach-world/location-drops-view.mjs":"import {DROP_KINDS,DROP_RULES} from './location-drop-rules.mjs';\r\nconst labels={home:'Home',work:'Work',gym:'Gym'},human=e=>({drop_move_closer:'Move closer to your saved drop spot.',drop_cache_recharging:'This cache is recharging. Check its timer.',drop_complete_a_phone_workout_first:'Complete a phone workout to establish your gym circuit reward.',drop_location_change_cooldown:'This location was just saved. You can move it after one hour.',drop_fresh_accurate_gps_required:'Wait for a fresh GPS fix with accuracy of 25 m or better.',drop_challenge_expired:'That pickup expired. Start a fresh pickup.',drop_state_changed:'The cache changed. Refresh and try again.'}[e.code]||e.message);\r\nexport function mountLocationDrops(host,{client,signal}={}){\r\n const abort=new AbortController();let disposed=false,data=null,busy=false,active=null,poll=0,timer=0,generation=0;\r\n const alive=()=>!disposed&&!signal?.aborted,stop=()=>{generation++;clearInterval(poll);poll=0;active=null;};\r\n host.classList.add('cw-card');host.innerHTML='<span class=\"cw-eyebrow\">PRIVATE SUPPLY CACHES</span><h3>A reason to get there.</h3><p class=\"cw-note\">Home, optional work, and gym drops recharge 24 hours after collection. Your saved spots are visible only to you.</p><label class=\"cw-check\"><input type=\"checkbox\" data-drop-consent> This is an accessible spot I may use, and I want to save it privately.</label><div data-drop-cards></div><p data-drop-status class=\"cw-note\" role=\"status\"></p><button data-stop-drop hidden>Cancel pickup</button>';\r\n const status=host.querySelector('[data-drop-status]'),cards=host.querySelector('[data-drop-cards]'),consent=host.querySelector('[data-drop-consent]'),cancel=host.querySelector('[data-stop-drop]');\r\n const getFix=()=>new Promise((resolve,reject)=>{if(!navigator.geolocation)return reject(Error('GPS is unavailable in this browser.'));navigator.geolocation.getCurrentPosition(p=>resolve({latitude:p.coords.latitude,longitude:p.coords.longitude,accuracy:p.coords.accuracy,sampledAt:p.timestamp}),()=>reject(Error('Allow location access to use this private supply cache.')),{enableHighAccuracy:true,maximumAge:0,timeout:15000});});\r\n function countdown(){if(!alive()||!data)return;const now=Date.now()+(data.serverTime-data.receivedAt);for(const n of host.querySelectorAll('[data-drop-available]')){const seconds=Math.max(0,Math.ceil((Number(n.dataset.dropAvailable)-now)/1000));n.textContent=seconds?`Recharging / ${Math.floor(seconds/3600)}h ${Math.floor(seconds%3600/60)}m`:'Ready to collect nearby';}}\r\n function render(){if(!alive())return;cards.replaceChildren();for(const kind of DROP_KINDS){const location=data?.locations?.find(l=>l.kind===kind),card=document.createElement('article');card.className='cw-drop-card';const title=document.createElement('h4');title.textContent=`${labels[kind]} drop${kind==='work'?' / optional':''}`;card.append(title);const reward=document.createElement('p');reward.className='cw-note';reward.textContent=kind==='gym'?data?.gymReward?.eligible?`${data.gymReward.xp.toLocaleString()} bonus XP + 3 secret packs. Ten rounds of each highest completed exercise in your active workout families.`:'3 secret packs + full circuit bonus XP once you have a completed phone workout.':'1 uncommon cosmetic pack.';card.append(reward);\r\n   if(kind==='gym'&&data?.gymReward?.eligible){const basis=document.createElement('small');basis.textContent=data.gymReward.exercises.map(e=>`${e.name} / level ${e.level}`).join(' · ');card.append(basis);}\r\n   const row=document.createElement('div');row.className='cw-actions';const save=document.createElement('button');save.textContent=location?'Move drop to this spot':'Save this spot';save.disabled=!client.authenticated;save.onclick=()=>saveLocation(kind);row.append(save);\r\n   if(location){const collect=document.createElement('button');collect.textContent='Collect nearby';collect.className='cw-primary';collect.onclick=()=>start(kind);row.append(collect);const remove=document.createElement('button');remove.textContent='Remove';remove.onclick=()=>run(async()=>{stop();await client.dropsCommand({type:'remove-location',kind});await refresh();status.textContent=`${labels[kind]} spot removed.`;});row.append(remove);const recharge=document.createElement('p');recharge.className='cw-note';recharge.dataset.dropAvailable=String(location.availableAt);card.append(recharge);}card.append(row);cards.append(card);\r\n  }countdown();}\r\n async function refresh(){const next=await client.drops();if(alive()){data={...next,receivedAt:Date.now()};render();}}\r\n async function run(fn){if(busy||!alive())return;busy=true;try{await fn();}catch(e){stop();if(alive())status.textContent=human(e);}finally{busy=false;if(alive())cancel.hidden=!active;}}\r\n const saveLocation=kind=>run(async()=>{if(!consent.checked)throw Error('Confirm this is an accessible spot you want to save privately.');stop();status.textContent='Waiting for an accurate location fix...';const fix=await getFix();if(!alive())return;await client.dropsCommand({type:'set-location',kind,confirmPrivateLocation:true,confirmAccessible:true,...fix});await refresh();if(alive())status.textContent=`${labels[kind]} drop saved privately. Its marker is on your Sectors map.`;});\r\n const start=kind=>run(async()=>{stop();status.textContent='Finding your saved cache...';const fix=await getFix();if(!alive())return;const response=await client.dropsCommand({type:'start-claim',kind,...fix});if(!alive())return;const ticket=++generation;active={kind,...response.result.challenge,seq:0};cancel.hidden=false;status.textContent='Stay near the cache while GPS confirms your pickup / about 15 seconds.';\r\n  poll=setInterval(()=>{if(!alive()||ticket!==generation||!active||busy)return;void run(async()=>{if(Date.now()>active.expiresAt){stop();throw Error('Pickup expired. Start again.');}const fix=await getFix();if(!alive()||ticket!==generation)return;const response=await client.dropsCommand({type:'sample-claim',kind,challengeId:active.id,nonce:active.nonce,seq:++active.seq,...fix});if(!alive()||ticket!==generation)return;if(response.result.claimed){const claim=response.result.claim;stop();await refresh();status.textContent=`Collected ${claim.packs.length} ${kind==='gym'?'secret':'uncommon'} pack${claim.packs.length===1?'':'s'}${claim.xp?` and ${claim.xp.toLocaleString()} bonus XP`:''}. Open your packs from the pack button.`;}else status.textContent=`GPS pickup in progress / ${Math.floor(response.result.dwellMs/1000)} seconds confirmed.`;});},3000);\r\n });\r\n cancel.onclick=()=>{stop();cancel.hidden=true;status.textContent='Pickup cancelled. The cache is still there.';};timer=setInterval(countdown,1000);\r\n if(client.authenticated)void refresh().catch(e=>{if(alive())status.textContent=human(e);});else{render();consent.disabled=true;status.textContent='Sign in to save private cache locations and collect rewards.';}\r\n const dispose=()=>{if(disposed)return;disposed=true;stop();clearInterval(timer);abort.abort();};signal?.addEventListener('abort',dispose,{once:true});document.addEventListener('visibilitychange',()=>{if(document.hidden){stop();cancel.hidden=true;if(alive())status.textContent='Pickup paused while the app was hidden.';}},{signal:abort.signal});return {dispose};\r\n}\r\n","modules/coach-world/map-view.mjs":"import {FACTION_BY_ID,NPC_ITEMS} from './catalog.mjs';\r\nimport {rememberPublicGeography,updatePublicTerritory,publicThemeAt} from './public-geography.mjs';\r\nimport {drawBuilding} from './canvas-scene.mjs';\r\nimport {getSpriteCanvas} from './pixel-art.mjs';\r\nimport {buildWorldNodes} from './world-nodes.mjs';\r\nimport {mountPrivateDropPins} from './private-drop-pins.mjs';\r\nimport {claimGraphics,releaseGraphics} from '../graphics-lifecycle.mjs';\r\nimport {rtsFlag} from '../coach-world-rts/flag.mjs';\r\n\r\n// RTS seam S4 (flag on only). Each feature module loads and mounts independently: a missing or failing one affects only itself.\r\n// A cleanup is registered only while `signal` is live; if abort wins the race the cleanup is called at once instead.\r\nexport const RTS_MAP_FEATURES=Object.freeze([['pixel-map-style.mjs','attachPixelStyle'],['geo-follow.mjs','attachFollow'],['npc-view.mjs','attachNpcs'],['hubs-map.mjs','attachHubs']]);\r\nexport function attachRtsMap(map,{signal,subscribe,isSiteOpen},cleanups,load=path=>{const spec='../coach-world-rts/'+path;return import(spec);}){ // non-literal: esbuild must not glob-bundle the whole RTS folder\r\n const opts={attachPixelStyle:{signal},attachFollow:{subscribe,signal},attachNpcs:{signal,isSiteOpen},attachHubs:{signal}};\r\n for(const [path,fn] of RTS_MAP_FEATURES)Promise.resolve().then(()=>load(path)).then(async m=>{\r\n  if(signal.aborted)return;\r\n  let cleanup;try{cleanup=await m[fn](map,opts[fn]);}catch(e){console.warn(path,e);return;}\r\n  if(typeof cleanup!=='function')return;\r\n  if(signal.aborted){try{cleanup();}catch(e){console.warn(path,e);}return;}\r\n  cleanups.push(cleanup);\r\n },()=>{});\r\n}\r\nlet mapLibrary;\r\nfunction loadMapLibrary(){\r\n if(globalThis.maplibregl)return Promise.resolve(globalThis.maplibregl);\r\n if(!mapLibrary)mapLibrary=new Promise((resolve,reject)=>{const css=document.createElement('link');css.rel='stylesheet';css.href='/modules/coach-world/vendor/maplibre-gl.css';document.head.append(css);const script=document.createElement('script');script.src='/modules/coach-world/vendor/maplibre-gl.js';script.onload=()=>globalThis.maplibregl?resolve(globalThis.maplibregl):reject(Error('Map renderer unavailable.'));script.onerror=()=>reject(Error('Map renderer could not load.'));document.head.append(script);}).catch(e=>{mapLibrary=null;throw e;});return mapLibrary;\r\n}\r\nconst text=(tag,value,className)=>{const e=document.createElement(tag);e.textContent=value;if(className)e.className=className;return e;};\r\n// wild hunt reads the live walk session from here ({sessionId,nonce}); cleared when the walk ends\r\nconst publishWalk=w=>{globalThis.__cwWalkSession=w&&(w.id||w.sessionId)&&w.nonce?{sessionId:w.id||w.sessionId,nonce:w.nonce}:null;return w;};\r\nexport function mountSectors(host,{client,signal,onSnapshot=()=>{}}){\r\n let disposed=false,watch=null,walk=null,seq=0,sending=false,generation=0,geographicMap=null,mapClaim=null,lastFix=null,latestData=null,viewportTimer=0,privateDropPins=null,mapRts=null;const abort=new AbortController(),viewAbort=new AbortController(),rtsCleanups=[],fixListeners=new Set();\r\n host.innerHTML='<div class=\"cw-sector-intro\"><span class=\"cw-eyebrow\">THE FRONTIER</span><h3>Earn your ground.</h3><p>Small territory patches. A few public hubs. Contested ground stays scarred while control spreads, then heals after sustained peace.</p></div><label class=\"cw-check\"><input type=\"checkbox\" data-lawful-walk> I am walking somewhere I am allowed to be; virtual territory is not permission to enter property.</label><div class=\"cw-actions\"><button data-walk>Begin a sector walk</button><button data-refresh>Refresh sectors</button><button data-skyline>Prepare mapped skyline here</button></div><p data-walk-status role=\"status\">GPS is off. Walking requires your explicit permission.</p><div data-map></div><div data-sites></div><section class=\"cw-card\"><h3>Connect at a public hub</h3><p data-public></p><div class=\"cw-actions\"><button data-friend>Create friend code</button><input data-friend-code placeholder=\"Friend invite code\" aria-label=\"Friend invite code\"><button data-accept-friend>Accept friend</button></div><p data-invite></p><div class=\"cw-actions\"><input data-target placeholder=\"Player public ID\" aria-label=\"Player public ID\"><select data-site aria-label=\"Battle meetup hub\"></select><button data-battle>Invite to battle</button></div><label class=\"cw-check\"><input type=\"checkbox\" data-public-path> I am on an ordinary open public path and choose my current spot for this battle.</label><div class=\"cw-actions\"><input data-encounter placeholder=\"Encounter ID\" aria-label=\"Encounter ID\"><button data-accept-battle>Accept battle</button><button data-cancel-battle>Cancel battle</button><button data-check-battle>Check battle</button></div><p class=\"cw-note\">Both players must be nearby at the same eligible public hub. Battles reveal a temporary defense view, never a home address. Friendship does not automatically share home location.</p></section><p data-error role=\"status\"></p>';\r\n const q=s=>host.querySelector(s),status=q('[data-walk-status]'),error=q('[data-error]');q('[data-public]').textContent=client.snapshot?.publicId?`Your public ID: ${client.snapshot.publicId}`:'Sign in to visit shops, share friend codes and battle nearby.';\r\n const alive=()=>!disposed&&!signal?.aborted;\r\n const report=e=>{if(alive())error.textContent=e.name==='AbortError'?'':e.message;};\r\n const cmd=async action=>{error.textContent='';const s=await client.command(action);if(alive()){onSnapshot(s);return s;}throw new DOMException('Closed','AbortError');};\r\n async function refresh(){const g=++generation;q('[data-refresh]').disabled=true;try{const center=geographicMap?.getCenter(),data=await client.map(center?{latitude:center.lat,longitude:center.lng,radius:5000}:{});if(!alive()||g!==generation)return;render(data);}catch(e){report(e);}finally{if(alive()&&g===generation)q('[data-refresh]').disabled=false;}}\r\n function stopRtsMap(){mapRts?.abort();mapRts=null;for(const cleanup of rtsCleanups.splice(0)){try{cleanup();}catch(e){console.warn('coach-world rts cleanup',e);}}}\r\n function startRtsMap(map){stopRtsMap();const own=new AbortController();mapRts=own;if(viewAbort.signal.aborted)own.abort();else viewAbort.signal.addEventListener('abort',()=>own.abort(),{once:true});attachRtsMap(map,{signal:own.signal,subscribe:fn=>{fixListeners.add(fn);return()=>fixListeners.delete(fn);},isSiteOpen:()=>null},rtsCleanups);}\r\n function releaseMap(){stopRtsMap();privateDropPins?.dispose();privateDropPins=null;const map=geographicMap,claim=mapClaim;geographicMap=null;mapClaim=null;if(!map){claim?.();return;}const canvas=map.getCanvas(),gl=canvas.getContext('webgl2')||canvas.getContext('webgl');void releaseGraphics(canvas,gl,()=>{map.remove();gl?.getExtension('WEBGL_lose_context')?.loseContext();}).then(()=>claim?.());}\r\n function syncNodeSprites(map,data){\r\n  const nodes=buildWorldNodes(data.sectors||[],data.sites||[]),features=[];\r\n  for(const n of nodes){const kind=n.type==='public-hub'?'shop':n.type==='public-node'?'node':'resource',key=`cw-${n.faction}-${kind}`;if(!map.hasImage?.(key)){const source=getSpriteCanvas(n.faction,kind),image=source.getContext('2d').getImageData(0,0,64,64);map.addImage(key,image,{pixelRatio:2});}features.push({type:'Feature',id:n.id,geometry:{type:'Point',coordinates:[n.lng,n.lat]},properties:{sprite:key,label:n.label,type:n.type}});}\r\n  const value={type:'FeatureCollection',features},source=map.getSource('cw-world-node-sprites');if(source)source.setData(value);else{map.addSource('cw-world-node-sprites',{type:'geojson',data:value});map.addLayer({id:'cw-world-node-sprites',type:'symbol',source:'cw-world-node-sprites',minzoom:12,layout:{'icon-image':['get','sprite'],'icon-anchor':'bottom','icon-allow-overlap':false,'icon-padding':4,'icon-pitch-alignment':'viewport','icon-rotation-alignment':'viewport'}});}\r\n }\r\n async function mountGeographicMap(node,data,g){let ownClaim;try{const lib=await loadMapLibrary();if(!alive()||generation!==g||!node.isConnected)return;ownClaim=claimGraphics('coach-world-map');await ownClaim.ready;if(!alive()||generation!==g||!node.isConnected){ownClaim();return;}mapClaim=ownClaim;const sites=data.sites||[],first=sites.find(s=>Number.isFinite(s.longitude??s.lng)&&Number.isFinite(s.latitude??s.lat));const lng=data.query?.longitude??first?.longitude??first?.lng??-115.1398,lat=data.query?.latitude??first?.latitude??first?.lat??36.1699;\r\n  geographicMap=new lib.Map({container:node,style:'https://tiles.openfreemap.org/styles/liberty',center:[lng,lat],zoom:11,pitch:35,attributionControl:true});geographicMap.addControl(new lib.NavigationControl({showCompass:false}),'top-right');geographicMap.on('moveend',()=>{if(!alive())return;clearTimeout(viewportTimer);viewportTimer=setTimeout(()=>{if(alive()&&geographicMap)void refresh();},450);});\r\n  geographicMap.on('error',()=>{if(alive())node.setAttribute('aria-label','Map tiles may be unavailable. Public sites and sectors remain listed below.');});\r\n  const activeMap=geographicMap;activeMap.on('load',()=>{if(!alive()||geographicMap!==activeMap)return;data=latestData||data;const map=activeMap,style=map.getStyle(),buildingLayer=style.layers?.find(l=>l['source-layer']==='building');if(buildingLayer){try{map.addLayer({id:'cw-alien-building-masses',type:'fill',source:buildingLayer.source,'source-layer':'building',minzoom:13,paint:{'fill-color':'#526b69','fill-opacity':.5}});}catch{}}\r\n   syncNodeSprites(map,data);privateDropPins=mountPrivateDropPins(map,client,{signal:abort.signal});\r\n   const features=[];for(const s of sites){const x=s.longitude??s.lng,y=s.latitude??s.lat;if(Number.isFinite(x)&&Number.isFinite(y))features.push({type:'Feature',geometry:{type:'Point',coordinates:[x,y]},properties:{label:s.label||s.name||s.id}});}map.addSource('cw-public-hubs',{type:'geojson',data:{type:'FeatureCollection',features}});map.addLayer({id:'cw-public-hubs',type:'circle',source:'cw-public-hubs',paint:{'circle-radius':8,'circle-color':'#d5d9a5','circle-stroke-color':'#142c32','circle-stroke-width':3}});\r\n   const patches=(data.sectors||[]).map(s=>({...s,geometry:s.geometry||s.polygonGeoJSON||(Array.isArray(s.polygon)?{type:'Polygon',coordinates:[[...s.polygon,s.polygon[0]]]}:null)})).filter(s=>s.geometry?.type==='Polygon').map(s=>({type:'Feature',geometry:s.geometry,properties:{color:FACTION_BY_ID[s.controller||s.faction]?.color||'#8a9795',phase:s.phase||'neutral'}}));{map.addSource('cw-public-territory',{type:'geojson',data:{type:'FeatureCollection',features:patches}});map.addLayer({id:'cw-public-territory',type:'fill',source:'cw-public-territory',paint:{'fill-color':['get','color'],'fill-opacity':.24}});map.addLayer({id:'cw-public-territory-borders',type:'line',source:'cw-public-territory',paint:{'line-color':['get','color'],'line-width':2}});}\r\n   const friends=(data.friends||[]).filter(f=>f.home?.consent===true&&Number.isFinite(f.home.latitude)&&Number.isFinite(f.home.longitude)).map(f=>({type:'Feature',geometry:{type:'Point',coordinates:[f.home.longitude,f.home.latitude]},properties:{label:f.publicId}}));{map.addSource('cw-shared-friend-landmarks',{type:'geojson',data:{type:'FeatureCollection',features:friends}});map.addLayer({id:'cw-shared-friend-landmarks',type:'circle',source:'cw-shared-friend-landmarks',paint:{'circle-radius':15,'circle-color':'#e8e7b0','circle-stroke-width':5,'circle-stroke-color':'#5c8978'}});map.addLayer({id:'cw-shared-friend-names',type:'symbol',source:'cw-shared-friend-landmarks',layout:{'text-field':['get','label'],'text-offset':[0,-2],'text-size':12},paint:{'text-color':'#f5efd4','text-halo-color':'#152631','text-halo-width':2}});}\r\n   if(rtsFlag())startRtsMap(map);\r\n  });\r\n }catch{releaseMap();ownClaim?.();if(alive()&&node.isConnected)node.replaceWith(text('p','Geographic tiles are unavailable. Use the public hub list and official access links below.','cw-note'));}}\r\n function render(data){latestData=data;updatePublicTerritory(data);if(data.preview)error.textContent='Read-only world preview. Sign in for live territory control.';const sites=q('[data-sites]'),map=q('[data-map]'),select=q('[data-site]'),retained=geographicMap?map.querySelector('.cw-map'):null;if(!retained)releaseMap();sites.replaceChildren();map.replaceChildren();select.replaceChildren();const sectors=data.sectors||[];\r\n  const geographic=retained||document.createElement('div');geographic.className='cw-map';geographic.setAttribute('aria-label','Existing geographic map with anonymous building masses and public meetup hubs');map.append(geographic,text('p','Map: OpenFreeMap / OpenStreetMap contributors. Building masses are existing map geometry, not player homes. Ground fog never grants access to private bases.','cw-note'));if(!retained)void mountGeographicMap(geographic,data,generation);else{const friends=(data.friends||[]).filter(f=>f.home?.consent===true&&Number.isFinite(f.home.latitude)&&Number.isFinite(f.home.longitude)).map(f=>({type:'Feature',geometry:{type:'Point',coordinates:[f.home.longitude,f.home.latitude]},properties:{label:f.publicId}}));geographicMap.getSource('cw-shared-friend-landmarks')?.setData({type:'FeatureCollection',features:friends});const patches=sectors.map(s=>({...s,geometry:s.geometry||s.polygonGeoJSON||(Array.isArray(s.polygon)?{type:'Polygon',coordinates:[[...s.polygon,s.polygon[0]]]}:null)})).filter(s=>s.geometry?.type==='Polygon').map(s=>({type:'Feature',geometry:s.geometry,properties:{color:FACTION_BY_ID[s.controller||s.faction]?.color||'#8a9795',phase:s.phase||'neutral'}}));geographicMap.getSource('cw-public-territory')?.setData({type:'FeatureCollection',features:patches});syncNodeSprites(geographicMap,data);geographicMap.resize();}\r\n  const nodes=buildWorldNodes(sectors,data.sites||[]);map.append(text('p',`${nodes.filter(n=>n.type==='resource').length} resource formations / ${nodes.filter(n=>n.type==='public-node').length} public relays. Scenery uses public territory data; resource harvesting is not enabled.`,'cw-note'));\r\n  const patchCount=new Set(sectors.map(s=>s.initialPatchId).filter(Boolean)).size;map.append(text('h3',patchCount?`${patchCount} biome patches / ${sectors.length} territory cells`:`${sectors.length} territory cells`),text('p','Homes stay under fog. Territory shown here comes from public sector anchors, never houses.','cw-note'));\r\n  const tiles=document.createElement('div');tiles.className='cw-sector-grid';\r\n  for(const s of sectors.slice(0,84)){const f=FACTION_BY_ID[s.controller||s.faction||s.controllerFaction],phase=s.phase||s.state||'neutral',tile=document.createElement('article');tile.className=`cw-sector-tile ${['warzone','contested','converting','healing'].includes(phase)?'is-scarred':''}`;tile.style.setProperty('--sector-color',f?.color||'#768793');tile.append(text('strong',s.label||f?.name||'Unclaimed frontier'),text('span',`Sector ${String(s.id).slice(-9,-3).toUpperCase()}`),text('b',phase.toUpperCase()));const progress=Number(s.conversionProgress??s.progress);if(Number.isFinite(progress))tile.append(text('small',`Conversion ${Math.round(progress<=1?progress*100:progress)}%`));tiles.append(tile);}\r\n  map.append(tiles);if(data.query?.truncated||data.truncated)map.append(text('p','This public map response is bounded. Pan or zoom and refresh to inspect another area.','cw-note'));if(!sectors.length)map.append(text('p','No public sectors are available here yet. No private home geometry is shown.'));\r\n  for(const site of data.sites||[]){const card=document.createElement('article');card.className='cw-card';const territory=publicThemeAt(data.sectors||[],site.latitude,site.longitude),f=FACTION_BY_ID[territory.faction]||FACTION_BY_ID[site.faction||site.biome];card.dataset.phase=territory.phase;card.append(text('span',site.kind==='battle'?'BATTLE SECTOR':'PUBLIC HUB','cw-eyebrow'),text('h3',site.label||site.name||site.id),text('p',`${f?.name||'Public alien outpost'} · ${site.hours||'Check official access hours before visiting.'}`));const source=typeof site.source==='string'?site.source:site.source?.officialPage||site.url;if(source){const a=document.createElement('a');a.href=source;a.target='_blank';a.rel='noopener noreferrer';a.textContent='Official place / access information';card.append(a);}if(site.accessNote)card.append(text('p',site.accessNote,'cw-note'));const row=document.createElement('div');row.className='cw-actions';const visit=document.createElement('button');visit.textContent='Visit this hub';visit.onclick=()=>cmd({type:'visit',siteId:site.id}).then(s=>{if(alive())error.textContent=s.result?.message||'Visit recorded.';}).catch(report);row.append(visit);card.append(row);\r\n   if(f){const art=document.createElement('canvas');art.width=140;art.height=90;art.className='cw-hub-art';art.setAttribute('aria-label',`${f.name} public outpost`);drawBuilding(art.getContext('2d'),{level:3,w:3,h:2,job:territory.phase==='stable'?null:{}},f,45,45,16);if(territory.phase!=='stable'){const c=art.getContext('2d');c.fillStyle='#111a21';for(let i=0;i<8;i++)c.fillRect(25+i*11,73+(i%2)*5,8,5);}card.prepend(art);card.append(text('p',`${data.preview?'Seeded initial theme':'Territory theme'}: ${f.name} / ${territory.phase}`,'cw-note'));}\r\n   card.append(text('p','NPC cosmetic shop · purchases require a fresh visit at this hub. Player-made marketplace comes later.','cw-note'));\r\n   visit.disabled=!client.authenticated;\r\n   for(const item of site.items||site.shop?.items||NPC_ITEMS){const button=document.createElement('button');button.textContent=`${item.label||item.name||item.id} · ${item.cost??item.price??'see price'}`;button.disabled=!client.authenticated;button.onclick=()=>cmd({type:'purchase',siteId:site.id,itemId:item.id}).then(()=>{if(alive())error.textContent='Purchase saved.';}).catch(report);row.append(button);}\r\n   sites.append(card);const option=document.createElement('option');option.value=site.id;option.textContent=site.label||site.name||site.id;select.append(option);\r\n  }\r\n  if(sectors.length>84)map.append(text('p',`Showing 84 of ${sectors.length} public cells in the list. Geographic territory includes all available cells.`,'cw-note'));\r\n  for(const friend of data.friends||[]){const id=friend.publicId||friend.public_id;if(!id)continue;const card=document.createElement('section');card.className='cw-card';card.append(text('h3',`Connected: ${id}`),text('p','Home coordinates remain private unless separately shared.','cw-note'));const revoke=document.createElement('button');revoke.textContent='Revoke connection';revoke.onclick=()=>cmd({type:'friend-revoke',publicId:id}).then(()=>refresh()).catch(report);const share=document.createElement('button');share.textContent='Share my exact home with this friend';share.onclick=()=>cmd({type:'friend-location',publicId:id,shareLocation:true}).then(()=>{if(alive())error.textContent='Your exact home is now shared with this friend.';}).catch(report);const stopShare=document.createElement('button');stopShare.textContent='Stop sharing my home';stopShare.onclick=()=>cmd({type:'friend-location',publicId:id,shareLocation:false}).then(()=>{if(alive())error.textContent='Your exact home is private from this friend.';}).catch(report);const row=document.createElement('div');row.className='cw-actions';row.append(share,stopShare,revoke);card.append(row);sites.append(card);}\r\n }\r\n function stopWatch(){if(watch!=null)navigator.geolocation?.clearWatch(watch);watch=null;}\r\n function startWatch(){\r\n  watch=navigator.geolocation.watchPosition(async p=>{if(!alive()||!walk||sending)return;const active=walk;lastFix={latitude:p.coords.latitude,longitude:p.coords.longitude,accuracy:p.coords.accuracy,sampledAt:p.timestamp};for(const fn of fixListeners)try{fn(lastFix);}catch(e){console.warn('coach-world rts fix',e);}status.textContent=`GPS accuracy ±${Math.round(p.coords.accuracy)} m · walking session active`;sending=true;try{const s=await cmd({type:'walk-sample',sessionId:active.id||active.sessionId,nonce:active.nonce,seq:++seq,latitude:p.coords.latitude,longitude:p.coords.longitude,accuracy:p.coords.accuracy,sampledAt:p.timestamp});if(alive()&&walk===active)status.textContent=s.result?.message||(s.result?.credited?'Influence accepted. Expansion remains war-torn while the control window resolves.':s.result?.status==='tracking'?`Tracking public sector / ${Math.floor((s.result.dwellMs||0)/1000)} s dwell / ${Math.round(s.result.movement||0)} m accepted movement`:`GPS checked: ${s.result?.status||'waiting'}. Only eligible open public ground earns influence.`);}catch(e){report(e);stopWatch();if(alive())status.textContent='Walk paused after a rejected sample. End this walk and start again.';}finally{sending=false;}},e=>{status.textContent=e.code===1?'GPS permission denied. No location samples sent.':'GPS fix unavailable. Move to open public ground or end the walk.';stopWatch();},{enableHighAccuracy:true,maximumAge:0,timeout:20000});\r\n }\r\n async function stopWalk(){const active=walk;publishWalk(walk=null);stopWatch();q('[data-walk]').textContent='Begin a sector walk';status.textContent='GPS is off. Walk ended.';if(active)try{await cmd({type:'walk-stop',sessionId:active.id||active.sessionId,nonce:active.nonce});}catch(e){report(e);}}\r\n q('[data-walk]').onclick=async()=>{if(walk)return stopWalk();if(!q('[data-lawful-walk]').checked){status.textContent='Confirm that you are walking somewhere you are allowed to be.';return;}if(!navigator.geolocation){status.textContent='Location is unavailable on this browser.';return;}q('[data-walk]').disabled=true;try{const snapshot=await cmd({type:'walk-start',confirmLawfulWalking:true});if(!alive())return;walk=snapshot.result?.session||snapshot.result?.walk||snapshot.walkSession||snapshot.result;publishWalk(walk);if(!(walk?.id||walk?.sessionId)||!walk?.nonce)throw Error('Walk session was not returned. Retry.');seq=0;status.textContent='Waiting for GPS permission and an accurate fix…';q('[data-walk]').textContent='End sector walk';\r\n   startWatch();\r\n  }catch(e){report(e);publishWalk(walk=null);stopWatch();}finally{if(alive())q('[data-walk]').disabled=false;}};\r\n q('[data-refresh]').onclick=refresh;\r\n q('[data-skyline]').onclick=()=>{if(!geographicMap){error.textContent='Wait for the geographic map to load.';return;}const map=geographicMap;error.textContent='Loading actual building footprints around the map center?';const capture=()=>{if(!alive()||geographicMap!==map)return;try{const data=rememberPublicGeography(map,latestData||{});error.textContent=`${data.buildings.length} existing mapped buildings prepared. OpenStreetMap attribution retained.`;}catch(e){report(e);}};if(map.getZoom()<15){map.once('idle',capture);map.easeTo({zoom:15,duration:500});}else if(map.isStyleLoaded())capture();else map.once('idle',capture);};\r\n q('[data-friend]').onclick=()=>cmd({type:'friend-invite'}).then(s=>{if(alive())q('[data-invite]').textContent=`Friend invite: ${s.result?.code||s.result?.invite?.code||'Created. Refresh to check status.'}`;}).catch(report);\r\n q('[data-accept-friend]').onclick=()=>cmd({type:'friend-accept',code:q('[data-friend-code]').value.trim()}).then(()=>{if(alive())q('[data-invite]').textContent='Friend connection saved.';}).catch(report);\r\n q('[data-battle]').onclick=()=>{if(!q('[data-public-path]').checked){error.textContent='Confirm your current spot is an ordinary open public path.';return;}if(!lastFix||Date.now()-lastFix.sampledAt>15000){error.textContent='Begin a sector walk and wait for a fresh GPS fix before inviting.';return;}cmd({type:'encounter-invite',targetPublicId:q('[data-target]').value.trim(),siteId:q('[data-site]').value,meetingAnchor:{latitude:lastFix.latitude,longitude:lastFix.longitude},confirmPublicPath:true}).then(s=>{if(alive()){const id=s.result?.encounterId||s.result?.encounter?.id||s.result?.id;q('[data-encounter]').value=id||'';error.textContent=id?`Battle invite ${id}`:'Invite sent.';}}).catch(report);};\r\n for(const action of ['accept','cancel'])q(`[data-${action}-battle]`).onclick=()=>cmd({type:`encounter-${action}`,encounterId:q('[data-encounter]').value.trim()}).then(s=>{if(alive())error.textContent=s.result?.status||`Battle ${action} saved.`;}).catch(report);\r\n q('[data-check-battle]').onclick=()=>client.encounter(q('[data-encounter]').value.trim()).then(s=>{if(alive())error.textContent=`Encounter: ${s.status||s.encounter?.status||'checked'}`;}).catch(report);\r\n const refreshTimer=setInterval(()=>{if(alive()&&client.authenticated&&!document.hidden)void refresh();},15000);\r\n if(!client.authenticated){for(const el of host.querySelectorAll('button,input,select'))el.disabled=true;q('[data-refresh]').disabled=false;q('[data-skyline]').disabled=false;error.textContent='Read-only world preview. Sign in for live territory control.';}void refresh();\r\n if(rtsFlag()){let resume=false;document.addEventListener('visibilitychange',()=>{if(!alive())return;if(document.hidden){if(watch!=null){stopWatch();resume=true;}}else if(resume){resume=false;if(walk&&watch==null)startWatch();}},{signal:viewAbort.signal});}\r\n function dispose(){if(disposed)return;disposed=true;viewAbort.abort();generation++;clearInterval(refreshTimer);clearTimeout(viewportTimer);stopWatch();publishWalk(walk=null);releaseMap();abort.abort();}signal?.addEventListener('abort',dispose,{once:true});globalThis.addEventListener('pagehide',dispose,{signal:abort.signal});return {dispose};\r\n}\r\n","modules/coach-world/navigation.mjs":"/* Grid BFS and waypoint walking adapted from StarNet worldmodel.js:2102–2127\r\n * and world.js:1861–1896 at 7ee93ceac14c6bcb500ab263e92186e3a2d8b5d7.\r\n * Copyright (c) 2026 Andrew Sims. MIT; full notice in STARNET-LICENSE.txt.\r\n * MYR5 changes: standalone ES module, bounded open grid, own building footprints,\r\n * deterministic decorative crew states. No StarNet artwork or service runtime.\r\n */\r\nimport {BUILDING_BY_ID} from './catalog.mjs';\r\nexport function findPath(cols,rows,blocked,start,target){\r\n const valid=p=>Number.isInteger(p.x)&&Number.isInteger(p.y)&&p.x>=0&&p.y>=0&&p.x<cols&&p.y<rows&&!blocked.has(p.y*cols+p.x);\r\n if(!valid(start)||!valid(target))return null;if(start.x===target.x&&start.y===target.y)return [];\r\n const from=start.y*cols+start.x,to=target.y*cols+target.x,prev=new Int32Array(cols*rows).fill(-1),queue=[from];prev[from]=from;\r\n for(let head=0;head<queue.length;head++){const at=queue[head];if(at===to)break;const x=at%cols,y=Math.floor(at/cols);for(const [nx,ny] of [[x+1,y],[x-1,y],[x,y+1],[x,y-1]]){if(!valid({x:nx,y:ny}))continue;const next=ny*cols+nx;if(prev[next]!==-1)continue;prev[next]=at;queue.push(next);}}\r\n if(prev[to]===-1)return null;const path=[];for(let at=to;at!==from;at=prev[at])path.push({x:at%cols,y:Math.floor(at/cols)});return path.reverse();\r\n}\r\nexport function occupancy(base){const blocked=new Set();for(const b of Object.values(base.buildings)){const d=BUILDING_BY_ID[b.id];if(!d||((b.level??1)<=0&&!b.job))continue;for(let y=b.y;y<b.y+d.h;y++)for(let x=b.x;x<b.x+d.w;x++)blocked.add(y*24+x);}return blocked;}\r\nfunction entrance(b,blocked,from){const d=BUILDING_BY_ID[b.id],points=[];for(let x=b.x;x<b.x+d.w;x++){points.push({x,y:b.y-1},{x,y:b.y+d.h});}for(let y=b.y;y<b.y+d.h;y++){points.push({x:b.x-1,y},{x:b.x+d.w,y});}return points.filter(p=>p.x>=0&&p.x<24&&p.y>=0&&p.y<24&&!blocked.has(p.y*24+p.x)).sort((a,c)=>Math.hypot(a.x-from.x,a.y-from.y)-Math.hypot(c.x-from.x,c.y-from.y))[0];}\r\nexport function createWanderers(count=9){return Array.from({length:Math.min(320,count)},(_,i)=>({id:i,x:1+i%3,y:1+Math.floor(i/3),path:[],index:0,phase:i,state:'idle',target:null,cycle:0,waitUntil:0}));}\r\nexport function stepWanderers(crew,base,dt,now,{extraBlocked=new Set()}={}){\r\n const blocked=occupancy(base);for(const key of extraBlocked)blocked.add(key);const free=[];for(let y=0;y<24;y++)for(let x=0;x<24;x++)if(!blocked.has(y*24+x))free.push({x,y});const version=Object.values(base.buildings).map(b=>`${b.id}:${b.x}:${b.y}:${b.level}:${b.job?.builderId||''}:${b.job?.policy||''}`).join('|')+':'+[...extraBlocked].join(',');\r\n for(const w of crew){w.phase+=Math.max(0,Math.min(.1,dt))*7;if(base.rest){w.state='rest';continue;}if(blocked.has(Math.floor(w.y)*24+Math.floor(w.x))){const p=free[Math.min(w.id,free.length-1)];Object.assign(w,{x:p.x,y:p.y,path:[],index:0});}\r\n  const job=Object.values(base.buildings).find(b=>b.job?.policy==='occupied-worker'&&b.job.builderId===`crew-${w.id+1}`),target=job?entrance(job,blocked,w):null;\r\n  if(w.version!==version||(target&&w.target!==job.id)){w.path=[];w.index=0;w.version=version;w.target=job?.id??null;}\r\n  if(!w.path.length&&now>=w.waitUntil){const destination=target||free[(w.id*67+w.cycle++*47)%free.length],p=findPath(24,24,blocked,{x:Math.floor(w.x),y:Math.floor(w.y)},destination);w.path=p||[];w.index=0;if(!w.path.length){w.state=job?'building':'idle';w.waitUntil=now+(job?500:900+w.id*110);}}\r\n  const p=w.path[w.index];if(!p)continue;const dx=p.x-w.x,dy=p.y-w.y,d=Math.hypot(dx,dy),step=Math.min(.1,Math.max(0,dt))*1.7;if(d<=step){w.x=p.x;w.y=p.y;w.index++;if(w.index>=w.path.length){w.path=[];w.index=0;w.state=job?'building':'idle';w.waitUntil=now+700;}}else{w.x+=dx/d*step;w.y+=dy/d*step;w.state='walking';w.facing=dx<0?'left':'right';}\r\n }return crew;\r\n}\r\n","modules/coach-world/orders.mjs":"import {rallyUnits} from './touchselection.mjs';\r\n\r\nexport const ORDER_TYPES = new Set(['move','stop','holdposition','patrol']);\r\n\r\nexport function createOrderController({getUnits,getSelected,getBase,getTerrain,selectGroup=()=>{},notify=()=>{},onResourceSelect=()=>{}}){\r\n const groups=new Map();let mode=null,patrolIndex=new Map(),resourceHandler=onResourceSelect;\r\n function setMode(type){mode=ORDER_TYPES.has(type)?type:null;return mode;}\r\n const available=()=>getUnits().filter(u=>!u.away);\r\n function apply(type,{target=null,waypoint=null,ids=getSelected()}={}){\r\n  if(!ORDER_TYPES.has(type))return {count:0,unsupported:true};\r\n  const base=getBase(),units=available().filter(u=>ids.includes(u.id));if(!units.length||!base)return {count:0};\r\n  if(!['stop','holdposition'].includes(type)&&(!validPoint(target)||(type==='patrol'&&waypoint!=null&&!validPoint(waypoint))))return {count:0};\r\n  const terrain=getTerrain(),blocked=terrain?.blocked||new Set();let count=0;\r\n  for(const u of units){u.path=[];u.index=0;u.rally=false;u.rallyTarget=null;u.patrolWait=null;u.order=type;u.hasOrder=true;u.mover='codex';u.rtsPath=null;\r\n   if(type==='stop'||type==='holdposition'){u.state=type==='stop'?'stopped':'holding';patrolIndex.delete(u.id);count++;continue;}\r\n   const dest=target;\r\n   const ok=rallyUnits([u],new Set([u.id]),dest,base,blocked)>0;\r\n   if(ok){u.hasOrder=true;u.order=type;u.state=type==='patrol'?'patrolling':'moving';if(type==='patrol'){u.patrolPoints=[{x:Math.floor(target?.x??dest.x),y:Math.floor(target?.y??dest.y)},{x:Math.floor(waypoint?.x??dest.x),y:Math.floor(waypoint?.y??dest.y)}];patrolIndex.set(u.id,1);}count++;}\r\n  }\r\n  notify();return {count,combat:false};\r\n }\r\n function step(){const base=getBase(),terrain=getTerrain();if(!base||base.rest)return;for(const u of available())if(u.order==='patrol'&&u.patrolPoints?.length&&!u.path?.length){const next=u.patrolPoints[patrolIndex.get(u.id)||0];if(!next)continue;const moved=rallyUnits([u],new Set([u.id]),next,base,terrain?.blocked||new Set());if(moved)patrolIndex.set(u.id,1-(patrolIndex.get(u.id)||0));}}\r\n function assign(group,ids=getSelected()){const key=Number(group);if(!Number.isInteger(key)||key<1||key>9)return false;const valid=new Set(available().map(u=>u.id));groups.set(key,[...new Set(ids)].filter(id=>valid.has(id)));return true;}\r\n function recall(group){const ids=groups.get(Number(group));if(!ids)return false;const valid=new Set(available().map(u=>u.id));const keep=ids.filter(id=>valid.has(id));groups.set(Number(group),keep);return keep;}\r\n function keydown(e){if(e.altKey||e.metaKey||e.repeat||ignored(e.target))return false;\r\n  if(!e.ctrlKey&&!e.shiftKey){const type=({m:'move',p:'patrol',h:'holdposition',s:'stop'})[String(e.key).toLowerCase()];if(type){setMode(type);e.preventDefault();e.stopPropagation();return true;}}\r\n  if(!/^[1-9]$/.test(e.key))return false;if(e.ctrlKey){if(e.shiftKey)return false;e.preventDefault();e.stopPropagation();return assign(e.key);}if(e.shiftKey)return false;const ids=recall(e.key);if(!ids)return false;e.preventDefault();e.stopPropagation();selectGroup(ids);return true;}\r\n const getGroups=()=>Object.fromEntries([...groups].map(([k,v])=>[k,[...v]]));\r\n return {apply,step,keydown,assign,recall,getGroups,setMode,getState(){return {mode,groups:getGroups()};},setResourceHandler(fn){resourceHandler=typeof fn==='function'?fn:()=>{};},resourceSelect(value){resourceHandler?.(value);},dispose(){groups.clear();patrolIndex.clear();mode=null;resourceHandler=()=>{};}};\r\n}\r\nfunction ignored(target){\r\n if(target?.closest?.('input,textarea,select,button,[contenteditable=\"true\"],[contenteditable=\"\"]'))return true;\r\n const modal=target?.closest?.('dialog[open],[role=\"dialog\"],[aria-modal=\"true\"]');\r\n return !!modal&&modal.id!=='coachWorldPanel';\r\n}\r\nfunction validPoint(point){return Number.isFinite(point?.x)&&Number.isFinite(point?.y);}\r\n","modules/coach-world/pack-gate.mjs":"import {gameCacheKey} from './game-generation-client.mjs';\r\n// R74: cosmetic packs need a built Landing Zone (the drop pod lands on it). Pure helper + a tiny per-owner localStorage mirror,\r\n// because the pack code (reward-packs.mjs, another lane) reads synchronously while the base lives in World state / on the server.\r\nexport const canReceivePacks=base=>(base?.buildings?.landing?.level??0)>=1;\r\nconst flagKey=owner=>`myr5-landing-built/${encodeURIComponent(owner)}`;\r\nexport function mirrorPackGate(owner,base,storage=globalThis.localStorage){try{storage.setItem(gameCacheKey(flagKey(owner),storage),canReceivePacks(base)?'1':'0');}catch{/* private mode: flag absent = ungated */}}\r\n// Sync read for the pack lane. No flag yet (World never seen for this owner) = UNGATED so existing players never lose packs;\r\n// once the base is known, '0' (HQ-only / fresh / start-over, no Landing Zone) gates and '1' opens.\r\nexport function packGateOpen(owner,storage=globalThis.localStorage){try{return storage.getItem(gameCacheKey(flagKey(owner),storage))!=='0';}catch{return true;}}\r\n","modules/coach-world/palettes.mjs":"// Palette ramps for coach-world sprites and biome tints for the RTS base.\r\n// Indices 0-3 of every FACTION_RAMPS entry equal the legacy spritePalette() output, so existing sprites keep reading.\r\nimport {FACTIONS} from './catalog.mjs';\r\n\r\nconst hex=c=>[1,3,5].map(i=>parseInt(c.slice(i,i+2),16));\r\nexport const mixHex=(a,b,t)=>'#'+hex(a).map((n,i)=>Math.round(n+(hex(b)[i]-n)*t).toString(16).padStart(2,'0')).join('');\r\n\r\n// 8 team shades dark -> color -> accent.\r\nconst shade=(f,t)=>t<.5?mixHex(f.dark,f.color,t*2):mixHex(f.color,f.accent,(t-.5)*2);\r\nconst teamRamp=f=>Object.freeze(['#00000000','#101c29','#243342',f.dark,\r\n  ...[0,1,2,3,4,5,6,7].map(k=>shade(f,(k+1)/8)),\r\n  mixHex(f.accent,'#ffffff',.5),'#111b25','#75cbc1','#b5eee0']);\r\nconst NEUTRAL=Object.freeze(['#00000000','#141b20','#293136','#3d464b',\r\n  '#576269','#68737a','#748087','#81969b','#9ca6ab','#b3c1c4','#c4cccf','#e3e7e5',\r\n  '#f4f6f0','#11191e','#81969b','#b3c1c4']);\r\n\r\nexport const FACTION_RAMPS=Object.freeze({\r\n  ...Object.fromEntries(FACTIONS.map(f=>[f.id,teamRamp(f)])),\r\n  neutral:NEUTRAL,\r\n});\r\n\r\n// Biome ground + prop tints. lush = the biomes.mjs colours; barren and rocky are derived from them.\r\nconst BASE={\r\n  chest:{ground:'#9a5a38',plant:'#ff8a4d',rock:'#6b3a2a'},\r\n  quads:{ground:'#8a8440',plant:'#ffd24a',rock:'#6a6a52'},\r\n  glutes:{ground:'#8a4a7a',plant:'#ff7fd0',rock:'#6a3a66'},\r\n  'arms-shoulders':{ground:'#4a6a9a',plant:'#6fb2ff',rock:'#3a4a6a'},\r\n  yoga:{ground:'#3a8a78',plant:'#7dffcf',rock:'#4a6a62'},\r\n  'martial-arts':{ground:'#7a3a3a',plant:'#ff4a4a',rock:'#3a2a30'},\r\n  cardio:{ground:'#3a7f8a',plant:'#5ff0ff',rock:'#4a6a78'},\r\n};\r\nconst sub=(b)=>Object.freeze({\r\n  lush:Object.freeze({...b}),\r\n  barren:Object.freeze({ground:mixHex(b.ground,'#d8c8a0',.35),plant:mixHex(b.plant,'#d8c8a0',.6),rock:mixHex(b.rock,'#d8c8a0',.35)}),\r\n  rocky:Object.freeze({ground:mixHex(b.ground,'#202020',.3),plant:mixHex(b.plant,'#202020',.2),rock:mixHex(b.rock,'#ffffff',.12)}),\r\n});\r\nexport const BIOME_TINTS=Object.freeze(Object.fromEntries(Object.entries(BASE).map(([id,b])=>[id,sub(b)])));\r\n","modules/coach-world/photo.mjs":"export function coverCrop(sw,sh,dw,dh){if(![sw,sh,dw,dh].every(v=>Number.isFinite(v)&&v>0))throw Error('Camera frame is not ready.');const s=Math.max(dw/sw,dh/sh),w=dw/s,h=dh/s;return [(sw-w)/2,(sh-h)/2,w,h];}\r\nexport async function captureWorldPhoto({video,overlay,alive=()=>true}){\r\n if(!alive())throw new DOMException('Camera closed.','AbortError');const canvas=document.createElement('canvas');canvas.width=overlay.width;canvas.height=overlay.height;const c=canvas.getContext('2d');\r\n if(video?.srcObject){if(video.readyState<2)throw Error('Wait for the camera image before taking a photo.');c.drawImage(video,...coverCrop(video.videoWidth,video.videoHeight,canvas.width,canvas.height),0,0,canvas.width,canvas.height);}else{c.fillStyle='#111f32';c.fillRect(0,0,canvas.width,canvas.height);}\r\n c.drawImage(overlay,0,0);const blob=await new Promise((resolve,reject)=>{try{canvas.toBlob(b=>b?resolve(b):reject(Error('Photo export failed.')),'image/png');}catch(e){reject(e);}});if(!alive())throw new DOMException('Camera closed.','AbortError');return blob;\r\n}\r\nexport function showPhotoPreview(blob,host,{signal}={}){\r\n const url=URL.createObjectURL(blob),card=document.createElement('section');card.className='cw-photo';const image=document.createElement('img');image.src=url;image.alt='Your captured Coach World scene';const close=document.createElement('button');close.textContent='Close photo';const download=document.createElement('a');download.href=url;download.download='myr5-world.png';download.textContent='Save PNG';download.className='cw-button';const share=document.createElement('button');share.textContent='Share';const status=document.createElement('p');status.setAttribute('role','status');\r\n const file=new File([blob],'myr5-world.png',{type:'image/png'});share.hidden=!navigator.canShare?.({files:[file]});share.onclick=async()=>{try{await navigator.share({files:[file]});}catch(e){if(e.name!=='AbortError')status.textContent='Sharing unavailable. Use Save PNG.';}};\r\n let released=false;const dispose=()=>{if(released)return;released=true;URL.revokeObjectURL(url);card.remove();};close.onclick=dispose;signal?.addEventListener('abort',dispose,{once:true});card.append(image,download,share,close,status);host.append(card);return dispose;\r\n}\r\n","modules/coach-world/pixel-art.mjs":"// Original MYR5 pixel architecture. Integer raster cells, no vector antialiasing.\r\n// Editable PNG/Piskel exports: scripts/export-world-sprites.mjs.\r\nimport {FACTION_BY_ID} from './catalog.mjs';\r\nimport {SPRITE_OVERRIDES} from './pixel-overrides.mjs';\r\nimport {FACTION_RAMPS} from './palettes.mjs';\r\nexport const SPRITE_SIZE=64;\r\n// R81 pixel density: bigger footprints get more pixels, same 64-unit design space drawn at an integer-ish scale (hq 128, 3-tile kinds 96, rest 64).\r\nexport const SPRITE_SIZES=Object.freeze({hq:128,hall:96,hangar:96,landing:96,production:96,expedition:96});\r\nexport const spriteSizeFor=kind=>SPRITE_SIZES[kind]||SPRITE_SIZE;\r\nexport const ART_KINDS=Object.freeze(['hq','hall','building','ship','resource','shop','node','rubble','crew','production','expedition','foundation','drop','landing','care','observatory','habitat','hangar']);\r\nexport const ART_DIRECTION=Object.freeze({\r\n chest:{motif:'Faceted wedge crowns',shape:'wedge',material:'beveled ceramic armor',rule:'Crest and diamond planes over broad shoulders and a narrow waist.'},\r\n quads:{motif:'Stacked oval pylons',shape:'pylon',material:'segmented load-bearing stone',rule:'Egg-shaped drums taper upward from paired broad foundations.'},\r\n glutes:{motif:'Pearl pod vaults',shape:'pod',material:'rounded living pearl',rule:'Stacked positive-volume spheres, bubble joints and a heavy lower core.'},\r\n 'arms-shoulders':{motif:'Branching canopy foundries',shape:'canopy',material:'ribbed shell and mechanical branches',rule:'One shoulder canopy carries paired radial arms.'},\r\n yoga:{motif:'Suspended curl sanctuaries',shape:'curl',material:'flowing translucent ceramic',rule:'Unplanted S stems and hooked ribbon tips float above their foundations.'},\r\n 'martial-arts':{motif:'Folded plate bastions',shape:'fan',material:'creased armored plates',rule:'Blunt monoliths unfold into ribbed fan helmets on compact braced feet.'},\r\n cardio:{motif:'Open-core relay towers',shape:'ring',material:'light skeletal alloy',rule:'Negative-space rings, suspended discs and lean open struts.'}\r\n});\r\nconst rasters=new Map(),canvases=new Map();\r\nconst hex=c=>[parseInt(c.slice(1,3),16),parseInt(c.slice(3,5),16),parseInt(c.slice(5,7),16)];\r\nconst mix=(a,b,t)=>'#'+hex(a).map((n,i)=>Math.round(n+(hex(b)[i]-n)*t).toString(16).padStart(2,'0')).join('');\r\nexport function spritePalette(faction){const R=FACTION_RAMPS[faction]||FACTION_RAMPS.chest,X=faction==='neutral'?[R[4],R[8],R[10],R[12]]:[R[5],R[9],R[10],R[12]];return [...legacyPalette(faction),...X];}\r\nfunction legacyPalette(faction){if(faction==='neutral')return ['#00000000','#141b20','#293136','#3d464b','#576269','#748087','#9ca6ab','#c4cccf','#e3e7e5','#11191e','#81969b','#b3c1c4'];const f=FACTION_BY_ID[faction]||FACTION_BY_ID.chest;return ['#00000000','#101c29','#243342',f.dark,mix(f.dark,f.color,.48),f.color,mix(f.color,f.accent,.42),f.accent,'#eef0ce','#111b25','#75cbc1','#b5eee0'];}\r\nfunction raster(S=1){\r\n const W=Math.round(64*S),cells=new Uint8Array(W*W),Q=v=>Math.round(v*S),px=(x,y,c)=>{if(x>=0&&x<W&&y>=0&&y<W)cells[y*W+x]=c;};\r\n const rect=(x,y,w,h,c)=>{const x0=Q(x),y0=Q(y),x1=Q(x+w),y1=Q(y+h);for(let j=y0;j<y1;j++)for(let i=x0;i<x1;i++)px(i,j,c);};\r\n const p=(x,y,c)=>rect(x,y,1,1,c);\r\n const line=(x0,y0,x1,y1,c,width=1)=>{x0=Q(x0);y0=Q(y0);x1=Q(x1);y1=Q(y1);const n=Math.max(1,Math.round(width*S)),dx=Math.abs(x1-x0),sx=x0<x1?1:-1,dy=-Math.abs(y1-y0),sy=y0<y1?1:-1;let e=dx+dy;for(;;){for(let j=0;j<n;j++)for(let i=0;i<n;i++)px(x0+i,y0+j,c);if(x0===x1&&y0===y1)break;const e2=2*e;if(e2>=dy){e+=dy;x0+=sx;}if(e2<=dx){e+=dx;y0+=sy;}}};\r\n const poly=(pts,c)=>{pts=pts.map(([x,y])=>[x*S,y*S]);const min=Math.max(0,Math.floor(Math.min(...pts.map(v=>v[1])))),max=Math.min(W-1,Math.ceil(Math.max(...pts.map(v=>v[1]))));for(let y=min;y<=max;y++)for(let x=0;x<W;x++){let inside=false;for(let i=0,j=pts.length-1;i<pts.length;j=i++){const a=pts[i],b=pts[j];if((a[1]>y)!==(b[1]>y)&&x<(b[0]-a[0])*(y-a[1])/(b[1]-a[1])+a[0])inside=!inside;}if(inside)px(x,y,c);}};\r\n const ellipse=(cx,cy,rx,ry,c)=>{cx*=S;cy*=S;rx*=S;ry*=S;for(let y=Math.floor(cy-ry);y<=cy+ry;y++)for(let x=Math.floor(cx-rx);x<=cx+rx;x++)if(((x-cx)/rx)**2+((y-cy)/ry)**2<=1)px(x,y,c);};\r\n const bevel=(pts,c=5)=>{poly(pts,1);const cx=pts.reduce((n,a)=>n+a[0],0)/pts.length,cy=pts.reduce((n,a)=>n+a[1],0)/pts.length;poly(pts.map(([x,y])=>[x+(cx-x)*.12,y+(cy-y)*.12]),c);for(let i=0;i<pts.length-1;i++)if(pts[i+1][0]>=pts[i][0])line(pts[i][0]+1,pts[i][1]+1,pts[i+1][0]-1,pts[i+1][1]+1,6);};\r\n const orb=(cx,cy,rx,ry)=>{ellipse(cx,cy,rx+1,ry+1,1);ellipse(cx,cy,rx,ry,3);ellipse(cx-1,cy-2,rx-1,ry-2,5);ellipse(cx-2,cy-3,Math.max(1,rx-3),Math.max(1,ry-4),6);rect(cx-rx+3,cy-ry+3,3,2,7);};\r\n return {cells,W,S,p,rect,line,poly,ellipse,bevel,orb};\r\n}\r\n// Extra pixels for scaled sprites only: body panel seams, top-edge highlight, under-edge shade and window glints, all at native resolution (indices from the 16-entry ramp).\r\nfunction detail(cells,W,S){\r\n const src=Uint8Array.from(cells),at=(x,y)=>x<0||y<0||x>=W||y>=W?0:src[y*W+x],seam=Math.round(6*S),col=Math.round(11*S);\r\n for(let y=0;y<W;y++)for(let x=0;x<W;x++){const c=src[y*W+x];if(!c)continue;\r\n  if(c===5||c===4){ // team body: brick/panel seams, then edge light and shade\r\n   if(y%seam===seam-1&&(x+(Math.floor(y/seam)%2)*(col>>1))%col>1)cells[y*W+x]=c===5?4:3;\r\n   else if(!at(x,y-1))cells[y*W+x]=c===5?6:5;\r\n   else if(!at(x,y+1)||(!at(x-1,y+1)&&c===4))cells[y*W+x]=3;\r\n   else if(c===5&&(x*7+y*3)%17===0)cells[y*W+x]=12; // sparse dither grain\r\n  }else if(c===3&&at(x,y-1)===5)cells[y*W+x]=13;\r\n  else if(c===10&&at(x,y-1)===10&&at(x,y+1)!==10)cells[y*W+x]=14; // window glint\r\n  else if(c===7&&!at(x,y-1))cells[y*W+x]=15;\r\n }\r\n}\r\nfunction emblem(r,shape,cx,cy,size=8){const {line,ellipse,rect,poly}=r;\r\n if(shape==='wedge')poly([[cx,cy-size],[cx+size,cy],[cx,cy+size*.6],[cx-size,cy]],7);\r\n if(shape==='pylon'){ellipse(cx,cy,size*.65,size,7);line(cx-size*.65,cy,cx+size*.65,cy,4);}\r\n if(shape==='pod'){ellipse(cx,cy,size,size,7);ellipse(cx,cy,size*.55,size*.55,5);}\r\n if(shape==='canopy'){poly([[cx-size,cy],[cx-size*.6,cy-size*.6],[cx,cy-size],[cx+size*.6,cy-size*.6],[cx+size,cy]],7);for(const side of [-1,1])line(cx,cy,cx+side*size,cy+size*.6,6,2);}\r\n if(shape==='curl'){line(cx-size*.4,cy+size,cx+size*.4,cy,7,2);line(cx+size*.4,cy,cx-size*.4,cy-size*.5,7,2);line(cx-size*.4,cy-size*.5,cx,cy-size,7,2);}\r\n if(shape==='fan'){for(let i=-2;i<=2;i++)line(cx,cy+size*.5,cx+i*size*.4,cy-size+Math.abs(i),7);}\r\n if(shape==='ring'){ellipse(cx,cy,size,size*.7,7);ellipse(cx,cy,size-2,size*.7-2,3);}\r\n}\r\n// care: tall chimney + steam + striped awning; observatory: tilted tube + dish; habitat: row of linked pods; hangar: open gantry arch, landing disc and a parked ship.\r\nfunction kindTell(r,kind,shape){const {rect,line,poly,ellipse,bevel,orb}=r;\r\n if(kind==='care'){rect(45,6,7,30,1);rect(46,7,5,29,3);rect(46,7,2,29,5);rect(43,4,11,4,1);rect(44,5,9,2,6);for(const [x,y,rx] of [[48,2,4],[52,-2,3]])ellipse(x,Math.max(2,y+2),rx,3,8);\r\n  poly([[6,44],[30,38],[34,43],[8,50]],1);for(let i=0;i<5;i++)poly([[8+i*5,45-i*1.2],[12+i*5,44.2-i*1.2],[12+i*5,48-i*1.2],[8+i*5,49-i*1.2]],i%2?8:7);rect(9,50,2,8,1);rect(31,45,2,8,1);}\r\n if(kind==='observatory'){ellipse(32,50,17,5,1);rect(15,38,34,13,1);rect(16,39,32,10,3);rect(16,39,32,3,6);bevel([[20,38],[44,38],[40,28],[24,28]],5);\r\n  line(26,32,43,10,1,6);line(26,32,43,10,5,4);line(27,31,42,11,7,1);ellipse(46,10,8,4,1);ellipse(46,10,7,3,10);ellipse(46,9,4,1,11);rect(30,43,4,8,1);rect(31,44,2,6,10);}\r\n if(kind==='habitat'){line(12,50,52,50,1,5);line(12,50,52,50,3,3);for(const [x,y,rx,ry] of [[12,43,8,9],[32,40,10,12],[52,43,8,9]]){orb(x,y,rx,ry);rect(x-1,y+ry-5,3,5,1);rect(x,y+ry-4,1,3,10);}rect(30,26,4,6,3);rect(31,22,2,5,7);}\r\n if(kind==='hangar'){rect(4,34,56,24,1);rect(5,35,54,22,3);rect(5,35,54,3,6);poly([[16,58],[16,44],[22,38],[42,38],[48,44],[48,58]],1);poly([[18,58],[18,45],[23,40],[41,40],[46,45],[46,58]],10);rect(24,48,16,4,1);line(18,58,46,58,1,2);\r\n  ellipse(32,32,22,5,1);ellipse(32,31,20,4,3);ellipse(32,31,16,3,6);poly([[32,18],[40,31],[32,29],[24,31]],5);line(32,18,32,29,7);for(const x of [6,57]){rect(x-1,18,3,17,1);rect(x,19,1,14,10);}rect(12,28,3,6,7);rect(49,28,3,6,7);}\r\n}\r\n// Five authored architecture stages share the near mesh's functional progression.\r\n// A left/top light and dark right wall make machinery legible at mobile size.\r\nconst TIER_BUILDINGS=new Set(['hq','hall','care','observatory','habitat','hangar','production','expedition']);\r\nfunction tierBuilding(r,kind,shape,v){\r\n const {rect,line,poly,ellipse,orb}=r;\r\n const roof=(x,y,s)=>{\r\n  if(shape==='wedge'){r.bevel([[x-s,y+3],[x-s*.55,y-s*.65],[x+s*.4,y-s*.65],[x+s,y+2],[x,y+6]],5);line(x,y-s*.5,x,y+4,7);}\r\n  else if(shape==='pylon'){orb(x,y,s*.65,s*.8);line(x-s*.55,y,x+s*.55,y,3,2);}\r\n  else if(shape==='pod'){orb(x-s*.5,y+1,s*.6,s*.55);orb(x+s*.5,y+1,s*.6,s*.55);orb(x,y-s*.45,s*.65,s*.65);}\r\n  else if(shape==='canopy'){poly([[x-s,y],[x-s*.65,y-s*.5],[x,y-s*.8],[x+s*.65,y-s*.5],[x+s,y],[x+s*.6,y+3],[x-s*.6,y+3]],1);poly([[x-s+2,y],[x,y-s*.6],[x+s-2,y],[x,y+2]],6);line(x-s*.7,y+4,x-s*.4,y+8,5,2);line(x+s*.7,y+4,x+s*.4,y+8,3,2);}\r\n  else if(shape==='curl'){line(x-s*.5,y+5,x+s*.4,y-1,1,5);line(x+s*.4,y-1,x-s*.3,y-s*.6,1,5);line(x-s*.5,y+4,x+s*.4,y-1,6,2);line(x+s*.4,y-1,x-s*.3,y-s*.6,7,2);}\r\n  else if(shape==='fan'){poly([[x-s,y],[x-s*.7,y-s*.6],[x,y-s],[x+s*.7,y-s*.6],[x+s,y],[x,y+5]],1);for(let i=-2;i<=2;i++)line(x,y+3,x+i*s*.38,y-s+Math.abs(i)*2,i<0?6:4,2);}\r\n  else {ellipse(x,y,s,s*.7,1);ellipse(x,y-1,s-1,s*.7-1,6);ellipse(x,y-1,s*.55,s*.35,0);line(x-s*.6,y+4,x-s*.4,y+8,5,2);line(x+s*.6,y+4,x+s*.4,y+8,3,2);}\r\n };\r\n const room=(x,y,w,h,d=5)=>{\r\n  poly([[x,y],[x+w,y],[x+w+d,y-d],[x+d,y-d]],1);\r\n  poly([[x+1,y-1],[x+w-1,y-1],[x+w+d-2,y-d+1],[x+d,y-d+1]],6);\r\n  rect(x,y,w,h,1);rect(x+1,y+1,w-2,h-2,4);line(x+1,y+1,x+w-2,y+1,6);\r\n  poly([[x+w,y],[x+w+d,y-d],[x+w+d,y+h-d],[x+w,y+h]],2);\r\n  line(x+w,y+1,x+w,y+h-1,3);\r\n  for(let i=0;i<Math.floor((w-3)/6);i++){rect(x+3+i*6,y+h-6,3,3,1);rect(x+3+i*6,y+h-6,2,1,10);}\r\n };\r\n // Shared ground plane never changes size: upgrades use the plot's spare space.\r\n ellipse(32,56,28,6,1);poly([[5,50],[31,42],[59,50],[33,60]],3);line(6,50,31,43,6);\r\n const lift=v*2;\r\n if(kind==='hq'){\r\n  room(17,35-lift,27,19+lift,8);room(7,44,12,10,5);room(43,43,12,10,4);\r\n  roof(31,28-lift,13);rect(27,42,8,13,1);rect(29,43,3,10,10);\r\n }else if(kind==='hall'){\r\n  for(let i=0;i<2+(v>=3?1:0);i++)room(12+i*5,43-i*10-lift,36-i*10,12,6);\r\n  roof(33,19-lift,9);\r\n }else if(kind==='care'){\r\n  room(9,36-lift,41,18+lift,6);rect(43,12-lift,7,27,1);rect(44,13-lift,2,25,6);rect(42,10-lift,10,4,3);line(43,10-lift,50,10-lift,7);\r\n  poly([[8,41],[32,37],[36,43],[10,49]],1);for(let i=0;i<5;i++)poly([[10+i*5,42-i],[14+i*5,41-i],[15+i*5,46-i],[11+i*5,47-i]],i%2?8:5);\r\n  ellipse(46,6-lift/3,4,2,8);\r\n }else if(kind==='observatory'){\r\n  room(15,40-lift,31,14+lift,6);ellipse(29,33-lift,17,6,1);ellipse(29,32-lift,15,4,6);\r\n  line(29,33-lift,45,16-lift,1,7);line(29,32-lift,45,15-lift,5,4);line(30,31-lift,45,15-lift,7);\r\n  ellipse(46,14-lift,9,4,1);ellipse(46,13-lift,7,2,10);\r\n }else if(kind==='habitat'){\r\n  line(10,49,52,49,1,5);line(10,48,52,48,5,2);\r\n  for(const [x,y] of [[14,41],[33,37-lift],[51,42]]){orb(x,y,8,10);rect(x-2,y+3,5,7,1);rect(x-1,y+4,2,4,10);}\r\n }else if(kind==='hangar'){\r\n  room(7,31-lift,45,23+lift,5);rect(18,38-lift,27,18+lift,1);rect(20,39-lift,23,15+lift,2);\r\n  for(const x of [14,48]){rect(x,29-lift,4,27+lift,1);rect(x+1,30-lift,2,25+lift,6);}rect(14,27-lift,38,5,1);line(15,28-lift,50,28-lift,7);\r\n  ellipse(32,51,12,3,10);ellipse(32,51,9,1,3);\r\n  if(v)poly([[31,40],[39,49],[31,47],[24,49]],7);\r\n }else if(kind==='production'){\r\n  room(9,32-lift,43,20+lift,6);rect(22,38-lift,16,17+lift,1);rect(24,39-lift,12,13+lift,2);\r\n  poly([[23,50],[36,50],[47,58],[30,58]],1);for(let i=0;i<4;i++)line(26+i*3,52,33+i*3,57,6);\r\n  line(9,37,9,17-lift,3,2);poly([[11,18-lift],[22,21-lift],[11,25-lift]],7);\r\n }else if(kind==='expedition'){\r\n  room(9,46,40,9,6);line(17,45,43,18-lift,1,6);line(17,44,43,17-lift,5,4);line(18,43,43,17-lift,7);\r\n  line(39,46,30,31-lift/2,3,3);orb(44,15-lift,6,6);\r\n }\r\n // Modules occupy distinct left/right/back regions rather than universal roof jewellery.\r\n const sites={hq:[[4,40],[41,27],[5,25],[44,13]],hall:[[4,44],[44,34],[4,26],[45,17]],care:[[3,48],[43,46],[4,26],[30,17]],observatory:[[5,48],[44,44],[3,27],[43,30]],habitat:[[3,49],[42,25],[4,28],[22,18]],hangar:[[3,44],[44,47],[3,24],[44,14]],production:[[3,46],[43,43],[3,24],[43,16]],expedition:[[3,48],[41,44],[4,30],[44,25]]};\r\n for(let i=0;i<v;i++){\r\n  const [x,y]=sites[kind][i];\r\n  // Braced service conduits attach elevated receivers/cranes to the building.\r\n  if(i>=2){line(x+5,y+6,x+5,48,1,3);line(x+5,y+6,x+5,47,3);line(x+5,y+9,x<32?19:45,43,3,2);}\r\n  room(x,y,10,9+i,3);\r\n  if(kind==='care'||kind==='production'){for(let j=0;j<3;j++)line(x+2+j*3,y+2,x+2+j*3,y+6,2);}\r\n  else if(kind==='habitat'){orb(x+5,y+1,5,4);}\r\n  else {rect(x+2,y+3,6,3,1);line(x+2,y+3,x+6,y+3,10);}\r\n  if(kind==='care'&&(i===0||i===3)){ellipse(x+5,y,5,3,1);ellipse(x+4,y-1,4,2,6);}\r\n  if(kind==='observatory'&&i>=2){ellipse(x+5,y-2,7,3,1);ellipse(x+5,y-3,5,2,10);line(x+5,y-3,x+8,y-7,7);}\r\n  if(kind==='hangar'&&i===2){line(x+4,y-3,x+17,y-3,1,3);line(x+5,y-3,x+16,y-3,6);line(x+16,y-2,x+16,y+5,7);}\r\n  if(kind==='production'&&i===2){rect(x+2,y-2,5,9,1);rect(x+3,y-1,3,7,6);line(x+4,y+2,x+13,y+2,7,2);}\r\n  if(kind==='expedition'&&i===1){line(x+2,y+5,x+11,y-6,1,4);line(x+3,y+4,x+11,y-5,6,2);}\r\n }\r\n // Compact repeated faction crest, visible in every kind and at every tier.\r\n emblem(r,shape,33,kind==='habitat'?34-lift:kind==='hall'?20-lift:kind==='expedition'?48:kind==='hangar'?25-lift:29-lift,4);\r\n}\r\nexport function rasterSprite(factionId,kind='hq',variant=0){\r\n const id=factionId==='neutral'&&kind==='resource'?'neutral':Object.hasOwn(ART_DIRECTION,factionId)?factionId:'chest';kind=ART_KINDS.includes(kind)?kind:'building';variant=Math.max(0,Math.min(4,Math.floor(Number(variant)||0)));const key=`${id}/${kind}/${variant}`;if(rasters.has(key))return rasters.get(key);\r\n if(SPRITE_OVERRIDES[key]){const edited=SPRITE_OVERRIDES[key],N=spriteSizeFor(kind),E=Math.round(Math.sqrt(edited.pixels.length))||64,px=N===E?Uint8Array.from(edited.pixels):Uint8Array.from({length:N*N},(_,i)=>edited.pixels[Math.floor(Math.floor(i/N)*E/N)*E+Math.floor((i%N)*E/N)]),result=Object.freeze({width:N,height:N,pixels:px,palette:Object.freeze(edited.palette),faction:id,kind,variant});rasters.set(key,result);return result;}\r\n const S=spriteSizeFor(kind)/64,r=raster(S),{rect,line,poly,ellipse,bevel,orb}=r,shape=id==='neutral'?'mineral':ART_DIRECTION[id].shape;\r\n // Every sprite has the same light from upper-left, dark one-cell contour and no half pixels.\r\n if(kind==='landing'){\r\n  ellipse(32,52,28,9,1);ellipse(32,50,26,8,3);ellipse(32,49,21,6,4);ellipse(32,49,17,4,10);ellipse(32,49,13,3,3);for(const x of [8,24,40,56]){rect(x-1,40,3,9,5);rect(x-1,38,3,3,10);}line(24,49,40,49,7);line(32,46,32,52,7);\r\n }else if(kind==='drop'){\r\n  ellipse(32,55,21,5,1);bevel([[13,29],[31,23],[51,30],[51,49],[32,56],[13,48]],4);poly([[32,26],[49,31],[49,47],[32,53]],3);bevel([[12,27],[31,20],[53,27],[53,33],[32,40],[12,33]],5);line(31,22,31,38,7,3);line(32,40,32,53,7,2);rect(25,36,12,8,1);rect(27,37,8,5,10);line(27,13,32,17,7,2);line(32,17,37,12,7,2);rect(30,8,3,3,8);\r\n }else if(kind==='foundation'){\r\n  poly([[32,37],[59,45],[59,51],[32,60],[5,51],[5,45]],1);poly([[32,39],[55,46],[32,54],[9,46]],3);line(10,47,31,40,6);line(32,55,54,47,4);for(const [x,y] of [[11,45],[32,39],[52,45],[32,52]]){rect(x-1,y-5,3,7,5);rect(x,y-7,2,2,7);}emblem(r,shape,32,46,5);\r\n }else if(kind==='rubble'){\r\n  ellipse(32,54,25,6,1);for(let i=0;i<9;i++){const x=11+(i*17)%43,y=43+(i*7)%12;bevel([[x,y-5],[x+6,y-2],[x+4,y+4],[x-4,y+2]],i%2?3:4);}line(19,48,28,41,2,2);rect(28,40,4,7,9);emblem(r,shape,45,43,5);\r\n }else if(kind==='resource'){\r\n  // ground shadow\r\n  ellipse(32,56,23,5,1);\r\n  ellipse(32,54,20,4,3);\r\n\r\n  // main silhouette\r\n  switch(shape){\r\n    case 'wedge':\r\n      r.bevel([[10,50],[15,30],[25,45]],5);\r\n      r.poly([[20,40],[28,20],[35,38]],4);\r\n      r.poly([[30,48],[38,28],[44,46]],4);\r\n      r.line(10,50,15,30,7,1);\r\n      r.line(20,40,28,20,7,1);\r\n      r.line(30,48,38,28,7,1);\r\n      break;\r\n    case 'pylon':\r\n      r.ellipse(32,42,12,8,4);\r\n      r.ellipse(32,52,10,6,4);\r\n      r.rect(24,50,16,4,3);\r\n      r.line(24,54,40,54,3,1);\r\n      r.line(24,58,40,58,3,1);\r\n      break;\r\n    case 'pod':\r\n      r.orb(28,48,5,5);\r\n      r.orb(36,52,6,6);\r\n      r.orb(32,56,4,4);\r\n      r.line(32,56,32,54,10,1);\r\n      break;\r\n    case 'canopy':\r\n      r.ellipse(32,40,12,8,6);\r\n      r.rect(30,48,4,2,3);\r\n      r.orb(28,52,3,3);\r\n      r.orb(36,52,3,3);\r\n      break;\r\n    case 'curl':\r\n      r.line(20,60,25,45,2,1);\r\n      r.line(27,58,32,43,2,1);\r\n      r.line(34,56,39,41,2,1);\r\n      r.line(25,45,26,44,7,1);\r\n      r.line(32,43,33,42,7,1);\r\n      r.line(39,41,40,40,7,1);\r\n      break;\r\n    case 'fan':\r\n      r.poly([[20,60],[24,48],[28,60]],4);\r\n      r.poly([[30,58],[34,46],[38,58]],4);\r\n      r.poly([[40,56],[44,44],[48,56]],4);\r\n      r.poly([[50,54],[54,42],[58,54]],4);\r\n      r.poly([[60,52],[64,40],[68,52]],4);\r\n      break;\r\n    case 'ring':\r\n      r.ellipse(32,48,20,2,1);\r\n      r.ellipse(32,48,15,2,6);\r\n      r.rect(30,50,4,2,3);\r\n      break;\r\n    default:\r\n      // mineral (neutral)\r\n      r.bevel([[10,55],[18,45],[26,55]],5);\r\n      r.bevel([[22,60],[30,50],[38,60]],5);\r\n      r.bevel([[34,65],[42,55],[50,65]],5);\r\n  }\r\n\r\n  // variant details\r\n  if(variant >= 1){\r\n    r.rect(28,58,2,2,7);\r\n    r.rect(36,58,2,2,7);\r\n  }\r\n  if(variant >= 2){\r\n    r.line(20,60,60,60,3,1);\r\n  }\r\n  if(variant >= 3){\r\n    r.line(32,40,32,30,11,1);\r\n  }\r\n  if(variant >= 4){\r\n    r.line(24,58,26,56,10,1);\r\n    r.line(38,58,36,56,10,1);\r\n  }\r\n}else if(kind==='ship'){\r\n  ellipse(32,53,19+variant*2,3,2);\r\n  if(shape==='wedge'){bevel([[32,7],[42,25],[58,40],[52,46],[38,39],[32,47],[26,39],[12,46],[6,40],[22,25]],4);poly([[32,10],[38,32],[32,40],[26,32]],6);line(32,12,32,29,7);}\r\n  if(shape==='pylon'){for(const x of [19,45]){orb(x,36,8,12);line(x-6,37,x+6,37,3,2);}orb(32,24,13,18);line(20,22,44,22,3,2);line(20,29,44,29,3,2);ellipse(32,16,17,3,7);ellipse(32,16,10,1,4);}\r\n  if(shape==='pod'){orb(18,37,12,12);orb(46,37,12,12);orb(32,28,15,19);orb(32,16,9,9);orb(32,39,8,8);rect(29,35,5,5,10);}\r\n  if(shape==='canopy'){bevel([[5,26],[13,15],[27,10],[40,12],[53,18],[59,28],[44,35],[20,35]],5);for(const side of [-1,1])for(let i=0;i<3;i++){line(32+side*9,28+i*3,32+side*(20+i*2),38+i*4,3,4);line(32+side*9,28+i*3,32+side*(20+i*2),38+i*4,6);}bevel([[25,31],[39,31],[43,44],[32,49],[21,44]],4);line(12,27,52,28,7);}\r\n  if(shape==='curl'){for(const side of [-1,1]){const pts=[[32,45],[32+side*10,37],[32+side*18,28],[32+side*16,17],[32+side*8,13],[32+side*5,18]];for(let i=1;i<pts.length;i++)line(...pts[i-1],...pts[i],1,7);for(let i=1;i<pts.length;i++)line(...pts[i-1],...pts[i],5,5);for(let i=1;i<pts.length;i++)line(...pts[i-1],...pts[i],7);}orb(32,30,6,11);}\r\n  if(shape==='fan'){bevel([[7,28],[12,16],[22,10],[32,8],[44,12],[54,20],[58,30],[32,45]],5);for(const [x,y] of [[12,19],[21,13],[31,11],[42,15],[51,22],[54,29]]){line(32,41,x,y,3,2);line(33,40,x+1,y,6);}bevel([[21,42],[32,34],[43,42],[39,49],[25,49]],3);}\r\n  if(shape==='ring'){ellipse(32,28,25,17,1);ellipse(32,27,23,15,5);ellipse(32,27,16,10,0);line(12,20,22,15,7,2);bevel([[32,8],[38,17],[36,24],[28,24],[26,17]],6);rect(29,33,6,13,4);line(15,36,24,46,6,2);line(48,36,40,46,4,2);}\r\n  rect(21,44,4,5,10);rect(39,44,4,5,10);rect(22,49,2,3,11);rect(40,49,2,3,11);\r\n }else if(kind==='crew'){\r\n  ellipse(32,57,12,3,1);\r\n  if(shape==='curl'){line(31,49,36,40,1,6);line(31,49,36,40,5,4);line(36,40,29,28,6,4);line(30,28,35,19,6,3);line(35,19,31,14,7,2);line(29,34,20,41,5,2);line(35,33,43,39,5,2);orb(32,27,5,7);}\r\n  else{if(shape==='pod'){orb(32,39,11,12);orb(32,22,10,10);orb(20,43,5,6);orb(44,43,5,6);}else{bevel([[25,33],[39,33],[40,45],[34,48],[25,45]],5);line(24,36,19,47,4,4);line(39,36,45,47,4,4);}\r\n   if(shape==='wedge')bevel([[24,19],[34,10],[42,19],[33,29],[25,26]],6);\r\n   if(shape==='pylon'){orb(32,23,8,11);line(21,22,43,22,7);line(26,36,38,36,3,2);}\r\n   if(shape==='canopy'){bevel([[18,23],[22,16],[32,12],[40,15],[47,24],[40,29],[22,28]],5);for(const side of [-1,1]){line(32+side*7,35,32+side*16,42,5,3);line(32+side*7,40,32+side*14,49,6,3);}}\r\n   if(shape==='fan'){bevel([[22,19],[27,13],[34,11],[42,17],[44,24],[32,30]],5);for(let i=0;i<5;i++)line(32,28,24+i*4,16,7);}\r\n   if(shape==='ring'){ellipse(32,38,10,12,6);ellipse(32,38,7,9,0);bevel([[20,21],[32,16],[44,21],[32,27]],5);}\r\n   rect(25,47,5,10,4);rect(35,47,5,10,4);rect(24,55,7,3,6);rect(34,55,7,3,6);}\r\n }else if(TIER_BUILDINGS.has(kind)){\r\n  tierBuilding(r,kind,shape,variant);\r\n }else {\r\n  // Three-quarter ground plinth anchors every tier to a shared world scale.\r\n  poly([[32,42],[61,49],[59,56],[32,63],[3,55],[3,49]],1);poly([[32,43],[58,49],[32,57],[6,49]],4);poly([[6,50],[32,57],[32,60],[6,53]],3);poly([[33,57],[58,50],[58,54],[33,61]],2);line(10,49,30,44,6);line(33,58,53,52,5);\r\n  if(variant>=1){for(const x of [6,58]){line(x,49,x,29-variant*4,3,3);emblem(r,shape,x,28-variant*4,4);rect(x-2,47,5,6,4);}}\r\n  if(variant>=2){for(const x of [12,51]){bevel([[x-5,37],[x,29],[x+5,37],[x+4,49],[x-4,51]],4);emblem(r,shape,x,37,3);}}\r\n  if(variant>=3){line(32,8,32,1,7,2);for(const x of [3,60]){rect(x-1,18,3,7,3);rect(x,18,1,4,10);}line(5,26,12,31,6);line(59,26,51,31,6);}\r\n  if(variant>=4){for(const x of [6,57]){orb(x,35,5,8);emblem(r,shape,x,25,4);line(x,26,x,36,7);}line(5,43,13,45,10,2);line(51,45,59,43,10,2);}\r\n  if(shape==='wedge'){\r\n   bevel([[11,31],[31,21],[52,29],[51,48],[31,56],[11,48]],4);poly([[31,23],[51,30],[50,46],[31,53]],3);bevel([[8,29],[27,10],[39,9],[56,28],[32,38]],5);poly([[29,12],[34,12],[36,32],[31,35]],7);poly([[36,12],[52,27],[38,32]],4);line(13,30,28,36,6);bevel([[16,34],[25,38],[25,48],[16,45]],5);bevel([[38,38],[46,34],[46,44],[38,48]],3);rect(28,39,7,12,1);line(29,40,29,49,10);line(34,40,34,49,5);\r\n  }else if(shape==='pylon'){\r\n   for(const x of [17,45]){bevel([[x-7,43],[x+5,40],[x+8,51],[x-4,55]],4);orb(x,32,8,16);line(x-6,32,x+6,34,3);line(x-6,37,x+6,39,3);}\r\n   orb(31,26,13,18);ellipse(31,20,14,5,1);ellipse(31,19,13,4,6);line(18,28,43,29,3,2);line(20,36,42,37,3,2);rect(27,39,8,13,1);rect(29,40,3,10,10);orb(31,12,6,9);\r\n  }else if(shape==='pod'){\r\n   orb(17,43,10,11);orb(47,43,10,11);orb(32,37,18,18);orb(32,18,13,12);orb(31,14,9,8);ellipse(32,40,9,10,1);ellipse(32,39,7,8,3);ellipse(31,37,5,6,10);ellipse(29,35,2,2,11);line(18,47,23,52,6,2);line(43,49,47,46,5,2);orb(13,51,5,5);orb(51,51,5,5);\r\n  }else if(shape==='canopy'){\r\n   for(const side of [-1,1])for(let i=0;i<3;i++){const x=32+side*(15+i*3),y=28+i*8;line(32+side*6,25+i*7,x,y,1,5);line(32+side*6,25+i*7,x,y,4,3);line(x,y,x+side*3,y+6,6,3);rect(x+side*3-2,y+5,6,4,3);rect(x+side*3-1,y+5,3,2,7);}\r\n   bevel([[26,24],[38,24],[42,49],[33,55],[23,49]],5);bevel([[4,24],[13,12],[28,6],[39,7],[53,16],[59,25],[49,31],[16,31]],5);poly([[32,8],[52,17],[56,25],[32,26]],4);line(9,25,53,26,7);for(let i=0;i<6;i++)line(15+i*6,27,16+i*6,30,3);rect(28,37,8,13,1);rect(30,38,3,10,10);\r\n  }else if(shape==='curl'){\r\n   ellipse(32,50,18,5,3);ellipse(32,47,16,3,6);ellipse(32,47,11,1,1);\r\n   const paths=[[[31,44],[40,35],[35,27],[25,23],[24,16],[33,11],[39,13]],[[23,41],[15,34],[16,27],[21,25]],[[42,40],[50,33],[49,25],[45,23]]];\r\n   for(const pts of paths){for(let i=1;i<pts.length;i++)line(...pts[i-1],...pts[i],1,6);for(let i=1;i<pts.length;i++)line(...pts[i-1],...pts[i],5,4);for(let i=1;i<pts.length;i++)line(pts[i-1][0],pts[i-1][1],pts[i][0],pts[i][1],7);}\r\n   orb(32,28,7,7);rect(30,27,4,3,11);for(const [x,y] of [[22,13],[44,18],[16,43]]){rect(x,y,2,2,7);}\r\n  }else if(shape==='fan'){\r\n   bevel([[13,38],[26,32],[38,32],[51,38],[47,52],[32,57],[17,52]],4);bevel([[7,25],[13,14],[22,7],[32,5],[43,9],[53,17],[58,27],[33,39]],5);poly([[33,7],[43,10],[54,25],[33,35]],4);for(const [x,y] of [[13,17],[21,10],[30,7],[40,10],[49,17],[54,25]]){line(32,35,x,y,3);line(33,34,x+1,y,6);}rect(24,38,17,6,3);rect(27,39,4,2,7);rect(34,39,4,2,7);bevel([[21,45],[28,45],[26,55],[16,54]],5);bevel([[38,45],[45,45],[48,53],[38,56]],3);rect(29,46,7,10,1);line(30,47,30,54,10);\r\n  }else if(shape==='ring'){\r\n   for(const x of [18,45]){line(x,49,x+(x<32?7:-7),28,1,5);line(x,49,x+(x<32?7:-7),28,5,3);bevel([[x-5,49],[x+5,48],[x+7,53],[x-4,56]],4);}\r\n   ellipse(32,30,17,17,1);ellipse(32,29,15,16,5);ellipse(32,29,10,11,0);line(21,18,26,14,7,2);line(17,29,17,34,6,2);bevel([[8,13],[30,5],[53,12],[55,17],[33,24],[9,19]],5);poly([[33,7],[52,13],[52,17],[33,21]],3);line(12,14,29,8,7);rect(29,43,7,8,3);rect(31,44,2,6,10);\r\n  }\r\n  // Common authored service details; tier bits change infrastructure, never faction identity.\r\n  for(let i=0;i<variant+1;i++){rect(8+i*5,53+i%2,3,2,7);rect(48-i*4,53,2,2,10);}\r\n  if(kind==='shop'||kind==='node'){bevel([[14,38],[32,34],[50,38],[48,44],[16,44]],4);for(let i=0;i<7;i++)rect(16+i*4,39,2,4,i%2?6:3);rect(24,45,16,7,1);rect(26,46,12,3,10);}\r\n  if(kind==='hall')emblem(r,shape,32,29,6);\r\n  if(kind==='building'){rect(7,44,7,7,3);rect(8,45,5,2,6);line(8,49,12,49,1);}\r\n  if(kind==='care'||kind==='observatory'||kind==='habitat'||kind==='hangar')kindTell(r,kind,shape);\r\n  if(kind==='production'){for(const x of [8,53]){bevel([[x-5,38],[x+5,36],[x+7,50],[x,54],[x-6,50]],4);rect(x-3,41,6,7,1);rect(x-2,42,4,3,10);emblem(r,shape,x,33,5);}bevel([[18,47],[32,43],[46,48],[43,57],[21,57]],3);for(let i=0;i<4;i++)line(23+i*5,49,23+i*5,54,6);}\r\n  if(kind==='expedition'){bevel([[6,46],[31,39],[59,46],[56,55],[31,63],[7,55]],3);ellipse(32,49,19,6,7);ellipse(32,49,16,4,3);line(14,49,49,49,7);line(31,44,31,54,7);for(const x of [7,56]){rect(x,24,2,25,4);poly([[x+1,24],[x+(x<32?9:-8),27],[x+1,31]],7);}emblem(r,shape,32,49,4);}\r\n }\r\n if(S>1)detail(r.cells,r.W,S);\r\n const result=Object.freeze({width:r.W,height:r.W,pixels:r.cells,palette:Object.freeze(spritePalette(id)),faction:id,kind,variant});rasters.set(key,result);return result;\r\n}\r\nexport function getSpriteCanvas(factionId,kind='hq',variant=0){const sprite=rasterSprite(factionId,kind,variant),key=`${sprite.faction}/${sprite.kind}/${sprite.variant}`;if(canvases.has(key))return canvases.get(key);const canvas=document.createElement('canvas');const N=sprite.width||64;canvas.width=N;canvas.height=N;const ctx=canvas.getContext('2d'),image=ctx.createImageData(N,N),colors=sprite.palette.map((c,i)=>i?hex(c):[0,0,0]);for(let i=0;i<N*N;i++){const color=sprite.pixels[i],offset=i*4;image.data[offset]=colors[color][0];image.data[offset+1]=colors[color][1];image.data[offset+2]=colors[color][2];image.data[offset+3]=color?255:0;}ctx.putImageData(image,0,0);canvases.set(key,canvas);return canvas;}\r\n","modules/coach-world/pixel-overrides.mjs":"// Pixel-editor imports live here. Run scripts/import-world-sprite.py after editing an exported PNG.\r\nexport const SPRITE_OVERRIDES=Object.freeze({});\r\n","modules/coach-world/private-drop-pins.mjs":"import {getSpriteCanvas} from './pixel-art.mjs';\r\n// Owner-only transient overlay. Never pass these features into public geography.\r\nexport function mountPrivateDropPins(map,client,{signal}={}){\r\n if(!client.authenticated)return {dispose(){}};let disposed=false,generation=0;const source='cw-private-drop-pins',layer=source,alive=()=>!disposed&&!signal?.aborted;\r\n const clear=()=>{try{map.getSource(source)?.setData({type:'FeatureCollection',features:[]});}catch{}};\r\n async function refresh(){clear();const ticket=++generation;try{const data=await client.drops();if(!alive()||ticket!==generation)return;const features=data.locations.map(location=>({type:'Feature',geometry:{type:'Point',coordinates:[location.longitude,location.latitude]},properties:{kind:location.kind}})),value={type:'FeatureCollection',features};if(map.getSource(source))map.getSource(source).setData(value);else{const canvas=getSpriteCanvas(client.snapshot?.base?.faction,'drop');map.addImage('cw-private-drop-image',canvas.getContext('2d').getImageData(0,0,64,64),{pixelRatio:2});map.addSource(source,{type:'geojson',data:value});map.addLayer({id:layer,type:'symbol',source,layout:{'icon-image':'cw-private-drop-image','icon-anchor':'bottom','icon-allow-overlap':true,'icon-pitch-alignment':'viewport','icon-rotation-alignment':'viewport'}});}}catch{clear();}}\r\n const dispose=()=>{if(disposed)return;disposed=true;generation++;globalThis.removeEventListener('myr5:world-drops-updated',refresh);document.removeEventListener('visibilitychange',visibility);clear();try{if(map.getLayer?.(layer))map.removeLayer(layer);if(map.getSource(source))map.removeSource(source);if(map.hasImage?.('cw-private-drop-image'))map.removeImage('cw-private-drop-image');}catch{}};\r\n const visibility=()=>{if(document.hidden){generation++;clear();}else if(alive())void refresh();};globalThis.addEventListener('myr5:world-drops-updated',refresh);document.addEventListener('visibilitychange',visibility);signal?.addEventListener('abort',dispose,{once:true});void refresh();return {dispose};\r\n}\r\n","modules/coach-world/production.mjs":"// Real StarCraft-style production: 5 queue slots per production building + supply (HQ and Crew habitat provide it).\r\n// Pure; no clock, no wallet writes outside the explicit mutators (which take an already-cloned base + `now`).\r\n// Saved (additive): base.productionQueues {[buildingId]:[item]}, base.productionSerial (last queue number).\r\n//   item = {queueId:'q-N', buildingId, unitId, count:1 (always one unit per slot), faction, startedAt, dueAt, durationMs, cost}  (cost = materials paid for that one unit)\r\n//   Timers are sequential: items[i].startedAt === items[i-1].dueAt, dueAt === startedAt + durationMs; head starts at enqueue time if the queue was empty.\r\n// Derived, never trusted from saves: base.training {[unitId]:{unitId,faction,startedAt,dueAt,cost,queueId,count}} = HEAD job per building (old UI compat).\r\n// base.units {[unitId]:count} now holds real instance counts (was 0/1). Renderers: unitInstances(base) -> one entry per owned instance.\r\nimport {MAX_LEVEL} from './catalog.mjs';\r\nimport {trainingQuote} from './base-systems.mjs';\r\nimport {researchEffects,buildingHealth} from './rts-tech.mjs';\r\nimport {UNIT_BY_ID,unitRoster} from './expedition-catalog.mjs';\r\nexport const PRODUCTION_RULES=Object.freeze({slots:5,maxCount:5, /* maxCount = max records one train-unit action may add; one record = one unit, so a queue never holds more than 5 units */ refundPct:90,hqSupportPerLevel:10,habitatSupportPerLevel:8,supplyCap:200,unitCountCap:200});\r\nconst own=(v,k)=>Object.hasOwn(v,k),integer=(v,min=0,max=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(v)&&v>=min&&v<=max,object=v=>v&&typeof v==='object'&&!Array.isArray(v),fail=m=>{throw RangeError(m);};\r\nconst level=(base,id)=>{const l=base?.rtsTech?.health?.[id]===0?0:base?.buildings?.[id]?.level;return Number.isInteger(l)?Math.max(0,Math.min(MAX_LEVEL,l)):0;};\r\nconst trainable=id=>typeof id==='string'&&own(UNIT_BY_ID,id)&&UNIT_BY_ID[id].trainable;\r\nconst unitSupply=id=>UNIT_BY_ID[id]?.supply??1;\r\nfunction legacyQueues(training,serial){\r\n const queues={},jobs=Object.entries(object(training)?training:{}).map(([id,j])=>{if(!trainable(id)||!object(j)||j.unitId!==id||j.faction!==UNIT_BY_ID[id].faction||!integer(j.startedAt)||!integer(j.dueAt,j.startedAt)||!integer(j.cost))fail('Invalid training job.');return {id,j};}).sort((a,b)=>a.j.dueAt-b.j.dueAt||a.id.localeCompare(b.id));\r\n for(const {id,j} of jobs){const u=UNIT_BY_ID[id],q=queues[u.buildingId]??=[];if(q.length>=PRODUCTION_RULES.slots)fail('Invalid training job.');const startedAt=q.length?q.at(-1).dueAt:j.startedAt,durationMs=j.dueAt-j.startedAt;q.push({queueId:`q-${++serial}`,buildingId:u.buildingId,unitId:id,count:1,faction:u.faction,startedAt,dueAt:startedAt+durationMs,durationMs,cost:j.cost});}\r\n return {queues,serial};\r\n}\r\n// Read side: works on normalized bases and on old snapshots that only carry `training` (derived view, no migration).\r\nexport const queuesOf=base=>object(base?.productionQueues)?base.productionQueues:legacyQueues(base?.training,base?.productionSerial??0).queues;\r\nexport function validateProduction(b){\r\n if(!object(b.productionQueues)||!integer(b.productionSerial))fail('Invalid production queue.');\r\n const ids=new Set();\r\n for(const [bid,items] of Object.entries(b.productionQueues)){\r\n  if(!own(b.buildings,bid)||!/^production-/.test(bid)||!Array.isArray(items)||items.length>PRODUCTION_RULES.slots)fail('Invalid production queue.');\r\n  items.forEach((j,i)=>{const u=trainable(j?.unitId)?UNIT_BY_ID[j.unitId]:null;\r\n   if(!u||!object(j)||u.buildingId!==bid||j.buildingId!==bid||j.faction!==u.faction||j.count!==1||!integer(j.startedAt)||!integer(j.durationMs)||j.dueAt!==j.startedAt+j.durationMs||!integer(j.cost)||typeof j.queueId!=='string'||!/^q-[1-9][0-9]*$/.test(j.queueId)||Number(j.queueId.slice(2))>b.productionSerial||ids.has(j.queueId)||(i>0&&j.startedAt!==items[i-1].dueAt))fail('Invalid training job.');\r\n   ids.add(j.queueId);});\r\n }\r\n}\r\n// Head job per building -> the old one-job-per-unit `training` map.\r\nexport function projectTraining(b){\r\n b.training=Object.fromEntries(Object.values(b.productionQueues).filter(q=>q.length).map(([h])=>[h.unitId,{unitId:h.unitId,faction:h.faction,startedAt:h.startedAt,dueAt:h.dueAt,cost:h.cost,queueId:h.queueId,count:h.count}]));\r\n}\r\n// Call on every normalize: migrates legacy head jobs exactly once (only while productionQueues is absent), then validates and re-projects.\r\nexport function ensureProduction(b){\r\n if(!own(b,'productionQueues')){const m=legacyQueues(b.training,Number.isSafeInteger(b.productionSerial)?b.productionSerial:0);b.productionQueues=m.queues;b.productionSerial=m.serial;}\r\n b.productionSerial??=0;\r\n if(object(b.productionQueues))for(const k of Object.keys(b.productionQueues))if(Array.isArray(b.productionQueues[k])&&!b.productionQueues[k].length)delete b.productionQueues[k];\r\n validateProduction(b);projectTraining(b);return b;\r\n}\r\nexport function supplyStatus(base){\r\n const hq=level(base,'hq')*PRODUCTION_RULES.hqSupportPerLevel,habitat=level(base,'habitat')*PRODUCTION_RULES.habitatSupportPerLevel,cap=Math.min(PRODUCTION_RULES.supplyCap,hq+habitat),\r\n workers=Number.isInteger(base?.workers)?base.workers:0,\r\n units=Object.entries(base?.units??{}).reduce((n,[id,c])=>n+(Number.isInteger(c)?c:0)*unitSupply(id),0),\r\n coaches=unitRoster(base).filter(u=>!u.trainable&&u.role!=='worker'&&u.owned).reduce((n,u)=>n+u.supply,0),\r\n queued=Object.values(queuesOf(base)).flat().reduce((n,j)=>n+j.count*unitSupply(j.unitId),0),used=workers+units+coaches,committed=used+queued;\r\n // `over` (committed > cap) never removes anything; it only blocks new training until supply is built.\r\n return {cap,provided:{hq,habitat},workers,coaches,units,used,queued,reserved:queued,committed,free:Math.max(0,cap-committed),over:committed>cap};\r\n}\r\nexport function productionQueue(base,buildingId){\r\n const items=(queuesOf(base)[buildingId]??[]).map((j,position)=>({...j,position,head:position===0,label:UNIT_BY_ID[j.unitId]?.label??j.unitId,supply:j.count*unitSupply(j.unitId)})),slots=PRODUCTION_RULES.slots;\r\n return {buildingId,slots,items,head:items[0]??null,used:items.length,free:slots-items.length,full:items.length>=slots,endsAt:items.length?items.at(-1).dueAt:null,level:level(base,buildingId),busy:!!base?.buildings?.[buildingId]?.job};\r\n}\r\nexport function trainingAvailability(base,unitId,count=1){\r\n const u=trainable(unitId)?UNIT_BY_ID[unitId]:null;\r\n if(!u)return {allowed:false,reason:'This unit is unlocked by its coach hall.',unitId:typeof unitId==='string'?unitId:null,count:null};\r\n const tq=trainingQuote(base,u),supply=supplyStatus(base),q=productionQueue(base,u.buildingId),bl=level(base,u.buildingId),have=base.materials?.[u.faction]??0,\r\n maxCount=Math.max(0,Math.min(q.free,Math.floor(supply.free/u.supply),Math.floor(have/tq.materialCost))),\r\n r={unitId:u.id,count:integer(count,1,PRODUCTION_RULES.slots)?count:null,buildingId:u.buildingId,faction:u.faction,requiredLevel:u.requiredLevel,buildingLevel:bl,supplyEach:u.supply,unitMaterialCost:tq.materialCost,unitTrainingMs:tq.trainingMs,slotsFree:q.free,maxCount,supply:{cap:supply.cap,used:supply.used,reserved:supply.reserved,free:supply.free},allowed:false,reason:null};\r\n if(r.count===null)return {...r,reason:`Choose 1 to ${PRODUCTION_RULES.slots} units.`};\r\n const n=r.count;Object.assign(r,{materialCost:tq.materialCost*n,trainingMs:tq.trainingMs*n,supplyCost:u.supply*n});\r\n const no=reason=>({...r,reason});\r\n if(base.rest)return no('Leave rest mode before training.');\r\n const requiredLevel=u.role==='hauler'&&researchEffects(base).advancedCompanionFacilities.includes(u.buildingId)?2:u.requiredLevel;\r\n r.requiredLevel=requiredLevel;\r\n if(buildingHealth(base,u.buildingId).offline)return no('Repair this production facility before training.');\r\n if(bl<requiredLevel||base.buildings?.[u.buildingId]?.job||base.rtsTech?.jobs?.[u.buildingId]||base.rtsTech?.repairs?.[u.buildingId])return no(`Need an idle ${u.buildingId.replace(/^production-/,'')} production level ${requiredLevel}.`);\r\n if(q.full||n>q.free)return no(q.full?`Production queue is full (${PRODUCTION_RULES.slots} slots).`:`Only ${q.free} queue slot${q.free===1?'':'s'} free.`);\r\n if(r.supplyCost>supply.free)return no(`Need ${r.supplyCost} supply; ${supply.free} free (cap ${supply.cap}). Build more habitat or upgrade the HQ.`);\r\n if(have<r.materialCost)return no('Need more matching materials to train this companion.');\r\n return {...r,allowed:true};\r\n}\r\n// --- Mutators: take a cloned, advanced base ---\r\n// `count` = number of single-unit records appended (each takes one of the 5 slots); all-or-nothing.\r\nexport function enqueueProduction(b,{unitId,count=1},now){\r\n const a=trainingAvailability(b,unitId,count);if(!a.allowed)fail(a.reason);\r\n const q=b.productionQueues[a.buildingId]??=[];b.materials[a.faction]-=a.materialCost;\r\n for(let i=0;i<a.count;i++){const startedAt=q.length?q.at(-1).dueAt:now;q.push({queueId:`q-${++b.productionSerial}`,buildingId:a.buildingId,unitId:a.unitId,count:1,faction:a.faction,startedAt,dueAt:startedAt+a.unitTrainingMs,durationMs:a.unitTrainingMs,cost:a.unitMaterialCost});}\r\n projectTraining(b);\r\n}\r\n// Exact item by queueId; legacy {unitId} cancels that unit's first (head-most) queued item. 90% refund; later jobs reflow without overlap.\r\nexport function cancelProduction(b,{queueId,unitId},now){\r\n if(queueId!=null&&typeof queueId!=='string')fail('Choose a production item.');\r\n for(const q of Object.values(b.productionQueues)){\r\n  const i=q.findIndex(j=>queueId!=null?j.queueId===queueId:j.unitId===unitId);if(i<0)continue;\r\n  const [j]=q.splice(i,1);b.materials[j.faction]+=Math.floor(j.cost*PRODUCTION_RULES.refundPct/100);\r\n  let t=i===0?now:q[i-1].dueAt;for(let k=i;k<q.length;k++){q[k].startedAt=t;t=q[k].dueAt=t+q[k].durationMs;}\r\n  ensureProduction(b);return j;\r\n }\r\n fail('No companion training to cancel.');\r\n}\r\n// Completes every finished head exactly once, however many are overdue; next head already starts at the previous dueAt.\r\nexport function advanceProduction(b,now){\r\n for(const q of Object.values(b.productionQueues))while(q.length&&q[0].dueAt<=now){const j=q.shift();b.units[j.unitId]=Math.min(PRODUCTION_RULES.unitCountCap,(b.units[j.unitId]??0)+j.count);}\r\n for(const k of Object.keys(b.productionQueues))if(!b.productionQueues[k].length)delete b.productionQueues[k];\r\n projectTraining(b);\r\n}\r\n// Renderer contract: one entry per owned trained instance, stable ids `${unitId}#${1..count}`. Instance #1 of an expedition unit is the one away.\r\nexport function unitInstances(base,{cap=PRODUCTION_RULES.unitCountCap}={}){\r\n const away=new Set(['launched','returning'].includes(base?.expedition?.status)?base.expedition.unitIds:[]),out=[];\r\n for(const r of unitRoster(base)){if(!r.trainable)continue;const count=Math.min(PRODUCTION_RULES.unitCountCap,base.units?.[r.id]??0);for(let i=1;i<=count&&out.length<cap;i++)out.push({instanceId:`${r.id}#${i}`,unitId:r.id,index:i,count,role:r.role,faction:r.faction,visualId:r.visualId,away:away.has(r.id)&&i===1});}\r\n return out;\r\n}\r\n","modules/coach-world/public-geography.mjs":"// Public OpenStreetMap geometry only. Never put account/base/presence data here.\r\nimport {distanceMeters,containsPoint,cellToLatLng} from './geo.mjs';\r\nimport {buildWorldNodes} from './world-nodes.mjs';\r\nlet cached=null,sequence=0;\r\nexport function publicThemeAt(sectors,latitude,longitude){\r\n let best=null,nearest=Infinity;for(const s of sectors){const geometry=s.geometry||s.polygonGeoJSON||(s.polygon?{type:'Polygon',coordinates:[s.polygon]}:null);let center;if(geometry){const ring=geometry.coordinates?.[0];if(ring?.length)center={latitude:ring.reduce((n,p)=>n+p[1],0)/ring.length,longitude:ring.reduce((n,p)=>n+p[0],0)/ring.length};if(containsPoint(geometry,latitude,longitude)){best=s;break;}}if(!center){try{const p=cellToLatLng(s.id);center={latitude:p[0],longitude:p[1]};}catch{continue;}}const d=distanceMeters({latitude,longitude},center);if(d<nearest){nearest=d;best=s;}}\r\n return {faction:best?.controller||best?.initialFaction||best?.faction||null,phase:best?.phase||'stable',revision:best?.revision||0,conversionProgress:best?.conversionProgress??1,sectorId:best?.id||null};\r\n}\r\nconst signature=sectors=>sectors.map(s=>`${s.id}:${s.controller||s.initialFaction||s.faction}:${s.phase}:${s.revision||0}:${s.conversionProgress??1}`).join('|');\r\nexport function updatePublicTerritory({sectors=[],sites=[],preview=false}={}){if(!cached)return;const next=signature(sectors),nodes=buildWorldNodes(sectors,sites),nodeSignature=JSON.stringify(nodes);if(cached.territorySignature===next&&cached.preview===preview&&cached.nodeSignature===nodeSignature)return;for(const b of cached.buildings)Object.assign(b,publicThemeAt(sectors,b.latitude,b.longitude));cached={...cached,nodes,nodeSignature,preview,territorySignature:next,captureId:++sequence};}\r\nexport function rememberPublicGeography(map,{sectors=[],sites=[],preview=false}={}){\r\n if(!map?.isStyleLoaded())throw Error('Map tiles are still loading. Wait and retry.');const center=map.getCenter(),anchor={latitude:center.lat,longitude:center.lng},layers=map.getStyle().layers.filter(l=>l['source-layer']==='building').map(l=>l.id);if(!layers.length)throw Error('This map has no building footprints.');\r\n const seen=new Set(),buildings=[];for(const feature of map.queryRenderedFeatures({layers})){const polygons=feature.geometry.type==='Polygon'?[feature.geometry.coordinates]:feature.geometry.type==='MultiPolygon'?feature.geometry.coordinates:[];for(const polygon of polygons){const ring=polygon[0];if(!ring||ring.length<4)continue;const bounded=ring.slice(0,120).filter(p=>p.length>=2&&p.slice(0,2).every(Number.isFinite));if(bounded.length<4)continue;const key=bounded.map(p=>p.map(v=>v.toFixed(6)).join(',')).join(';');if(seen.has(key))continue;seen.add(key);const longitude=bounded.reduce((n,p)=>n+p[0],0)/bounded.length,latitude=bounded.reduce((n,p)=>n+p[1],0)/bounded.length,distance=distanceMeters(anchor,{latitude,longitude});if(distance>2000)continue;const raw=Number(feature.properties?.render_height??feature.properties?.height),height=Number.isFinite(raw)?Math.min(180,Math.max(3,raw)):8;buildings.push({ring:bounded.map(p=>p.slice(0,2)),latitude,longitude,height,distance});if(buildings.length>=3000)break;}if(buildings.length>=3000)break;}\r\n buildings.sort((a,b)=>a.distance-b.distance);buildings.splice(160);for(const b of buildings)Object.assign(b,publicThemeAt(sectors,b.latitude,b.longitude));\r\n if(!buildings.length)throw Error('No mapped building footprints are visible. Zoom into nearby city streets and retry.');const nodes=buildWorldNodes(sectors,sites);cached={anchor,buildings,nodes,nodeSignature:JSON.stringify(nodes),captureId:++sequence,territorySignature:signature(sectors),preview,capturedAt:Date.now(),attribution:'OpenFreeMap / © OpenStreetMap contributors',source:'https://tiles.openfreemap.org/styles/liberty'};return cached;\r\n}\r\nexport function readPublicGeography(){return cached&&Date.now()-cached.capturedAt<3600000?cached:null;}\r\nexport function localOffset(anchor,longitude,latitude){return {x:(longitude-anchor.longitude)*111320*Math.cos(anchor.latitude*Math.PI/180),z:-(latitude-anchor.latitude)*110540};}\r\nexport function cityMasses(geography,lod=914.4){const near=[],far=new Map();for(const b of geography.buildings){const points=b.ring.map(p=>localOffset(geography.anchor,p[0],p[1]));if(b.distance<lod){near.push({...b,points});continue;}const x=points.reduce((n,p)=>n+p.x,0)/points.length,z=points.reduce((n,p)=>n+p.z,0)/points.length,key=`${Math.floor(x/100)}:${Math.floor(z/100)}:${b.faction}:${b.phase}`,mass=far.get(key)||{x:Math.floor(x/100)*100+50,z:Math.floor(z/100)*100+50,height:0,faction:b.faction,phase:b.phase,conversionProgress:b.conversionProgress};mass.height=Math.max(mass.height,b.height);far.set(key,mass);}return {near,far:[...far.values()]};}\r\n","modules/coach-world/resource-labels.mjs":"// Display vocabulary only. Existing save keys, receipts, costs and effects stay canonical.\nexport const RESOURCE_LABELS=Object.freeze({\n minerals:'Protein',gas:'Creatine',fuel:'Glycogen',calm:'Composure',\n charge:'Readiness',energy:'Stamina',supply:'Training capacity',\n rations:'Meal packs',calmTokens:'Breathwork tokens',\n permanentCurrency:'Gym Credits',runCurrency:'Run Tokens',\n});\nexport const harvestResourceLabel=kind=>RESOURCE_LABELS[kind]??kind;\n\n// Translate legacy quote/error copy at the display boundary, never stored identifiers.\nconst LEGACY_WORDS=Object.freeze({mineral:'Protein',minerals:'Protein',material:'Protein',materials:'Protein',mat:'Protein',\n gas:'Creatine',fuel:'Glycogen',supply:'Training capacity',ration:'Meal pack',rations:'Meal packs',\n calm:'Composure','calm token':'Breathwork token','calm tokens':'Breathwork tokens',charge:'Readiness','launch charge':'Readiness',energy:'Stamina'});\nexport const resourceDisplayText=value=>String(value??'').replace(/\\b(calm tokens?|launch charge|minerals?|materials?|gas|fuel|supply|rations?|calm|charge|energy|mat)\\b/gi,word=>LEGACY_WORDS[word.toLowerCase()]);\n","modules/coach-world/resource-placement.mjs":"// Pure resource placement: where the station's mineral formations, vents and deposit tiles sit. Shared by the visual layer and the harvest adapter.\n// Dependencies: catalog and navigation only (no DOM, no economy), so the adapter can import it without a cycle. Positions are unchanged.\nimport {occupancy} from './navigation.mjs';\nimport {FACTIONS} from './catalog.mjs';\n\n// node + spots + deposit point near the HQ: first candidate whose tile and >=2 of W/E/S spots are free\r\nexport function placeNode(base){\r\n const hq=base?.buildings?.hq||{x:10,y:9},w=hq.w||4,h=hq.h||4,blocked=occupancy(base||{buildings:{}}),free=(x,y)=>x>=0&&y>=0&&x<24&&y<24&&!blocked.has(y*24+x);\r\n const deposit={x:hq.x+1,y:hq.y+h};\r\n for(const [dx,dy] of [[1,3],[-4,2],[w+2,2],[3,3],[-3,4],[w+3,-1],[1,5]]){\r\n  const n={x:hq.x+dx,y:hq.y+h-1+dy};\r\n  const spots=[{x:n.x-1,y:n.y},{x:n.x+1,y:n.y},{x:n.x,y:n.y+1}].filter(s=>free(s.x,s.y));\r\n  if(free(n.x,n.y)&&spots.length>=2&&free(deposit.x,deposit.y))return {node:n,spots,deposit};\r\n }\r\n return null;\r\n}\r\n\n// ring 2-3 tiles off the hall edge: E, W, S, N, then the four corners (hall is w x h at x,y)\r\nconst ringOffsets=(x,y,w,h)=>[[w+2,1],[-3,1],[1,h+2],[1,-3],[w+2,h+1],[-3,h+1],[w+2,-2]].map(([dx,dy])=>({x:x+dx,y:y+dy}));\r\nconst GAS_OFFSETS=[[-3,0],[3,0],[0,3],[0,-3],[-3,3],[3,-3],[-4,1],[4,-1],[-3,-3],[3,3]];\r\nexport function placeNodes(base){\r\n const out=[],bf=base?.faction||FACTIONS[0].id,hq=placeNode(base);\r\n if(hq)out.push({faction:bf,...hq});\r\n const blocked=new Set(occupancy(base||{buildings:{}}));\r\n for(const p of out)for(const t of [p.node,...p.spots,p.deposit])blocked.add(t.y*24+t.x);\r\n const free=(x,y)=>x>=0&&y>=0&&x<24&&y<24&&!blocked.has(y*24+x);\r\n for(const f of FACTIONS){\r\n  const b=base?.buildings?.[`hall-${f.id}`];\r\n  if(!b||(b.level??0)<1||f.id===bf)continue;\r\n  const w=b.w||3,h=b.h||3,edge=[];\r\n  for(let i=0;i<w;i++)edge.push({x:b.x+i,y:b.y-1},{x:b.x+i,y:b.y+h});\r\n  for(let i=0;i<h;i++)edge.push({x:b.x-1,y:b.y+i},{x:b.x+w,y:b.y+i});\r\n  for(const n of ringOffsets(b.x,b.y,w,h)){\r\n   const spots=[{x:n.x-1,y:n.y},{x:n.x+1,y:n.y},{x:n.x,y:n.y+1}].filter(s=>free(s.x,s.y));\r\n   if(!free(n.x,n.y)||spots.length<2)continue;\r\n   const deposit=edge.filter(e=>free(e.x,e.y)).sort((a,c)=>Math.hypot(a.x-n.x,a.y-n.y)-Math.hypot(c.x-n.x,c.y-n.y))[0]; // nearest free hall entrance tile\r\n   if(!deposit)break;\r\n   out.push({faction:f.id,node:n,spots,deposit});\r\n   for(const t of [n,...spots])blocked.add(t.y*24+t.x);\r\n   break;\r\n  }\r\n }\r\n // gas vent per place: another free tile 3-4 tiles off the mineral node, same hall deposit tile; a vent with no room is skipped (gas miners then share the mineral node)\r\n for(const p of out){\r\n  for(const [dx,dy] of GAS_OFFSETS){\r\n   const n={x:p.node.x+dx,y:p.node.y+dy},spots=[{x:n.x-1,y:n.y},{x:n.x+1,y:n.y},{x:n.x,y:n.y+1}].filter(s=>free(s.x,s.y));\r\n   if(!free(n.x,n.y)||spots.length<2)continue;\r\n   p.gas={node:n,spots,deposit:p.deposit};for(const t of [n,...spots])blocked.add(t.y*24+t.x);break;\r\n  }\r\n }\r\n return out;\r\n}\n","modules/coach-world/rts-defense-state.mjs":"import {BUILDING_BY_ID} from './catalog.mjs';\r\nexport const DEFENSE_VERSION=1;\r\nconst own=(v,k)=>Object.hasOwn(v,k),obj=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst int=(v,min=0,max=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\r\nconst fail=message=>{throw RangeError(message);};\r\nexport function defenseState(base){\r\n const saved=base?.rtsDefense;if(saved==null)return {version:DEFENSE_VERSION,serial:0,lastAt:0,lastResult:null};\r\n if(!obj(saved)||saved.version!==DEFENSE_VERSION||!int(saved.serial)||!int(saved.lastAt))fail('Invalid drill state.');\r\n const r=saved.lastResult;if(r!=null){\r\n  if(!obj(r)||!['a','b','draw'].includes(r.winner)||!int(r.ticks,0,750)||!int(r.a,0,1000)||!int(r.b,0,1000)||!['easy','normal','hard'].includes(r.difficulty)||!int(r.seed,1)||!int(r.serial,1)||!obj(r.damage))fail('Invalid drill result.');\r\n  for(const [id,hp] of Object.entries(r.damage))if(!own(BUILDING_BY_ID,id)||!int(hp))fail('Invalid drill damage.');\r\n }\r\n return {version:DEFENSE_VERSION,serial:saved.serial,lastAt:saved.lastAt,lastResult:r?{winner:r.winner,ticks:r.ticks,a:r.a,b:r.b,difficulty:r.difficulty,seed:r.seed,serial:r.serial,damage:{...r.damage}}:null};\r\n}\r\nexport const normalizeRtsDefense=base=>({...base,rtsDefense:defenseState(base)});\r\n","modules/coach-world/rts-defense.mjs":"// Server-simulated NPC defense drill: the only canonical origin of building damage (rts-tech.mjs health/repair). Pure; no DOM, no state.mjs import.\r\n// The drill is an abstract exercise: the whole garrison's remaining strength maps to modest even structure damage. No XP, packs, coins, unlocks or unit changes.\r\nimport {BUILDINGS,BUILDING_BY_ID} from './catalog.mjs';\r\nimport {defenseSystems} from './base-systems.mjs';\r\nimport {researchEffects,applyRtsBuildingDamage,normalizeRtsTech,buildingHpMax} from './rts-tech.mjs';\r\nimport {simulate,W,H,MAX_UNITS,MAX_TOWERS,RULES_VERSIONS} from '../coach-world-rts/battle-sim.mjs';\r\nimport {DEFENSE_VERSION,defenseState} from './rts-defense-state.mjs';\r\nexport {DEFENSE_VERSION,normalizeRtsDefense} from './rts-defense-state.mjs';\r\nexport const DRILL_RULES=Object.freeze({fuelCost:5,cooldownMs:60000,maxDamagePerMille:250});\r\n// Same shapes as the practice raid NPC armies; copied so this module never loads the playback graph.\r\nexport const DRILL_ARMIES=Object.freeze({easy:{w:0,a:0,u:'gggs'},normal:{w:0,a:0,u:'gggsbh'},hard:{w:1,a:1,u:'ggssbbhh'}});\r\nconst own=(v,k)=>Object.hasOwn(v,k),obj=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst int=(v,min=0,max=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\r\nconst fail=message=>{throw RangeError(message);};\r\nconst ZONE=[2304,3584,512,1792];\r\nexport function drillSeed(seed,serial){let h=2166136261;for(const c of `${seed}:${serial}`)h=Math.imul(h^c.charCodeAt(0),16777619)>>>0;return (h%2147483646)+1;}\r\n// Base tiles (0..24) land in the mid-arena garrison pocket; towers keep their left-right order.\r\nconst arena={towersArena:pts=>pts.map(p=>[Math.round(ZONE[0]+p.x/24*(ZONE[1]-ZONE[0])),Math.round(ZONE[2]+p.y/24*(ZONE[3]-ZONE[2]))]),defenseArena:pts=>pts.map(p=>[Math.round(ZONE[0]+p.x/24*(ZONE[1]-ZONE[0])),Math.round(ZONE[2]+p.y/24*(ZONE[3]-ZONE[2]))])};\r\nconst RANK=c=>'bsh'.indexOf(c)<0?3:'bsh'.indexOf(c);\r\n// Militia keyed to HQ level + the owned trainable companions (persisted {id:count}; units away on an expedition excluded) + one tower unit per standing tower.\r\nexport function drillGarrison(base){\r\n const hq=Math.max(1,base.buildings?.hq?.level|0),away=new Set(['launched','returning'].includes(base.expedition?.status)?base.expedition.unitIds:[]);\r\n const towers=BUILDINGS.filter(d=>d.kind==='tower'&&base.buildings?.[d.id]?.level>0&&base.rtsTech?.health?.[d.id]!==0).slice(0,MAX_TOWERS);\r\n let crew='';for(const [id,n] of Object.entries(base.units??{}))if(n>0)crew+=(id.endsWith('-hauler')?'h':'s').repeat(Math.min(MAX_UNITS,Math.max(0,n-(away.has(id)?1:0))));\r\n const ground=[...'g'.repeat(2+hq)+(hq>=3?'s':'')+(hq>=5?'b':'')+crew].sort((p,q)=>RANK(p)-RANK(q)).join('').slice(0,Math.max(1,MAX_UNITS-towers.length));\r\n return {u:ground+'t'.repeat(towers.length),towers:arena.towersArena(towers.map(d=>{const b=base.buildings[d.id];return {x:b.x+d.w/2,y:b.y+d.h/2};}))};\r\n}\r\nexport function drillInput(base,difficulty,seed){\r\n const npc=DRILL_ARMIES[difficulty],tech=researchEffects(base),g=drillGarrison(base),systems=defenseSystems(base,arena);\r\n const def={w:tech.weaponUpgrade,a:tech.armorUpgrade,u:g.u};if(g.towers.length)def.towers=g.towers;\r\n return {rulesVersion:RULES_VERSIONS.at(-1),seed,rosters:[{w:npc.w,a:npc.a,u:npc.u},def],pads:[[0,1,2],[0,1,2]],systems:[[],systems]};\r\n}\r\n// Lost garrison strength (per mille) -> each standing building loses up to 25% of its max HP. Zero-HP buildings stay at zero (already offline).\r\nexport function drillDamage(base,result){\r\n const lost=1000-result.b,out={};if(lost<=0)return out;\r\n for(const d of BUILDINGS){const b=base.buildings?.[d.id];if(!(b?.level>0))continue;const hp=Math.floor(buildingHpMax(d.id,b.level)*lost*DRILL_RULES.maxDamagePerMille/1000000);if(hp>0)out[d.id]=hp;}\r\n return out;\r\n}\r\nexport function drillQuote(base,now){\r\n const s=defenseState(base),t=normalizeRtsTech(base).rtsTech,bad=reason=>({allowed:false,reason}),remaining=s.serial?Math.max(0,s.lastAt+DRILL_RULES.cooldownMs-now):0;\r\n if(base.rest)return bad('Leave rest mode before a drill.');if((base.buildings?.hq?.level|0)<1||t.health.hq===0)return bad('Repair the command habitat first.');\r\n if(Object.values(base.buildings).some(x=>x.job)||Object.keys(t.repairs).length||Object.keys(t.jobs).length||Object.values(base.productionQueues??{}).some(q=>q.length))return bad('Finish construction, production, research and repairs before a drill.');\r\n if(remaining>0)return bad(`Drill ready in ${Math.ceil(remaining/1000)} s.`);if(!int(base.fuel)||base.fuel<DRILL_RULES.fuelCost)return bad(`Need ${DRILL_RULES.fuelCost} fuel.`);\r\n return {allowed:true,reason:null,fuelCost:DRILL_RULES.fuelCost,cooldownMs:DRILL_RULES.cooldownMs,warning:'Structures take real damage; repair afterward.'};\r\n}\r\n// action: {type:'rts-defense-drill', difficulty:'easy'|'normal'|'hard'}. Seed, HP, result and damage are server-derived; extra action fields are ignored.\r\nexport function defenseDrill(base,action,{now}={}){\r\n if(!int(now))fail('Invalid drill clock.');if(!obj(action)||action.type!=='rts-defense-drill')fail('Unknown drill command.');\r\n const difficulty=own(DRILL_ARMIES,action.difficulty)?action.difficulty:'easy',q=drillQuote(base,now);if(!q.allowed)fail(q.reason);\r\n const s=defenseState(base),serial=s.serial+1,seed=drillSeed(base.seed,serial),sim=simulate(drillInput(base,difficulty,seed)),r=sim.result,damage=drillDamage(base,r);\r\n const out=applyRtsBuildingDamage(base,damage);out.fuel=base.fuel-DRILL_RULES.fuelCost;\r\n out.rtsDefense={version:DEFENSE_VERSION,serial,lastAt:now,lastResult:{winner:r.winner,ticks:r.ticks,a:r.a,b:r.b,difficulty,seed,serial,damage}};\r\n return out;\r\n}\r\n","modules/coach-world/rts-economy.mjs":"// Finite home-base deposits. Server state/clock owns income; animation never credits money.\n// Façade: the accepted state and every rule live in harvest-adapter.mjs (base.rtsEconomy v2). These names stay so existing readers\n// (mining, facility controls, HUD, resource nodes, expedition gate, station state) keep working unchanged.\nexport {HARVEST_RULES} from './harvest-rules.mjs';\nexport {\n economyState,\n normalizeRtsEconomy,\n advanceRtsEconomy,\n applyRtsEconomyAction,\n busySpans,\n busyHarvestWorkers,\n harvestLayoutLocked,\n harvestRouteBlocked,\n harvestQuote,\n awardAcceptedRunEnergy,\n harvestEnergySnapshot,\n harvestReadout,\n} from './harvest-adapter.mjs';\n","modules/coach-world/rts-tech.mjs":"// Pure timed research, production add-ons, building health and repair. Server clock/costs canonical; no client HP, cost or effect input.\r\n// Additive save field base.rtsTech (version 1). Imports catalog only (base-systems.mjs imports this file for derived effects: no cycle).\r\nimport {BUILDING_BY_ID,FACTION_BY_ID,MAX_LEVEL} from './catalog.mjs';\r\nexport const TECH_VERSION=1,TECH_MAX_LEVEL=3;\r\nconst own=(v,k)=>Object.hasOwn(v,k),obj=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst int=(v,min=0,max=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\r\nconst fail=message=>{throw RangeError(message);};\r\nconst isHall=id=>BUILDING_BY_ID[id]?.kind==='hall',isProduction=id=>BUILDING_BY_ID[id]?.kind==='production';\r\n// Research: level n costs TIERS[n-1]; level n also needs HQ level >= n and the hosting facility level >= n.\r\nconst TIERS=Object.freeze([{mineral:20,gas:0,durationMs:120000},{mineral:40,gas:20,durationMs:240000},{mineral:60,gas:40,durationMs:360000}]);\r\nexport const researchCatalog=Object.freeze({\r\n techs:Object.freeze([\r\n  {id:'weapons',label:'Weapons research',hosts:id=>id==='yard'||isHall(id),hostLabel:'Drill yard or any coach hall',effect:'+1 attack upgrade per level for every troop in raids and drills'},\r\n  {id:'armor',label:'Armor research',hosts:id=>id==='yard'||isHall(id),hostLabel:'Drill yard or any coach hall',effect:'+1 armor upgrade per level for every troop in raids and drills'},\r\n  {id:'logistics',label:'Logistics research',hosts:id=>id==='hq',hostLabel:'Command habitat',effect:'5% shorter companion training and 2% shorter outbound journeys per level'},\r\n ].map(Object.freeze)),\r\n tiers:TIERS,\r\n addons:Object.freeze([{id:'tech-lab',label:'Tech lab add-on',hosts:isProduction,hostLabel:'Any production facility',requiredLevel:2,mineral:30,gas:10,durationMs:180000,effect:'Pack companion unlocks at production level 2 and this facility trains 10% faster'}].map(Object.freeze)),\r\n repair:Object.freeze({mineralPerFullHp:20,msPerFullHp:120000,minMs:15000}),\r\n});\r\nconst TECH_BY_ID=Object.fromEntries(researchCatalog.techs.map(t=>[t.id,t])),ADDON_BY_ID=Object.fromEntries(researchCatalog.addons.map(a=>[a.id,a]));\r\n// Canonical HP ceiling per building level; unbuilt sites have none.\r\nexport const buildingHpMax=(buildingId,level)=>{const d=BUILDING_BY_ID[buildingId];return d&&int(level,1)?level*500+(d.kind==='hq'?1000:0):0;};\r\nconst level=(base,id)=>{const l=base?.buildings?.[id]?.level;return int(l,0,MAX_LEVEL)?l:0;};\r\nconst gasOf=base=>{const g=base?.rtsEconomy?.gas;return int(g)?g:0;};\r\nconst empty=()=>({version:TECH_VERSION,levels:{weapons:0,armor:0,logistics:0},jobs:{},addons:{},repairs:{},health:{}});\r\nfunction techState(base){\r\n const saved=base?.rtsTech;if(saved==null)return empty();\r\n if(!obj(saved)||saved.version!==TECH_VERSION)fail('Invalid research state.');\r\n const t=empty();\r\n if(!obj(saved.levels))fail('Invalid research levels.');for(const k of Object.keys(saved.levels)){if(!own(TECH_BY_ID,k)||!int(saved.levels[k],0,TECH_MAX_LEVEL))fail('Invalid research level.');t.levels[k]=saved.levels[k];}\r\n for(const k of ['jobs','addons','repairs','health'])if(saved[k]!=null&&!obj(saved[k]))fail('Invalid research ledger.');\r\n for(const [id,j] of Object.entries(saved.jobs??{})){\r\n  if(!own(BUILDING_BY_ID,id)||!obj(j)||!['research','addon'].includes(j.kind)||!int(j.startedAt)||!int(j.dueAt,j.startedAt)||!int(j.mineralCost)||!int(j.gasCost)||!own(FACTION_BY_ID,j.track))fail('Invalid research job.');\r\n  if(j.kind==='research'){if(!own(TECH_BY_ID,j.id)||!TECH_BY_ID[j.id].hosts(id)||!int(j.targetLevel,1,TECH_MAX_LEVEL)||j.targetLevel!==t.levels[j.id]+1)fail('Invalid research job.');t.jobs[id]={kind:'research',id:j.id,targetLevel:j.targetLevel,startedAt:j.startedAt,dueAt:j.dueAt,mineralCost:j.mineralCost,gasCost:j.gasCost,track:j.track};}\r\n  else{if(!own(ADDON_BY_ID,j.id)||!ADDON_BY_ID[j.id].hosts(id)||own(saved.addons??{},id))fail('Invalid add-on job.');t.jobs[id]={kind:'addon',id:j.id,startedAt:j.startedAt,dueAt:j.dueAt,mineralCost:j.mineralCost,gasCost:j.gasCost,track:j.track};}\r\n }\r\n if(Object.values(t.jobs).filter(j=>j.kind==='research').some((j,i,a)=>a.findIndex(o=>o.id===j.id)!==i))fail('Duplicate research job.');\r\n for(const [id,a] of Object.entries(saved.addons??{})){if(!own(BUILDING_BY_ID,id)||!own(ADDON_BY_ID,a)||!ADDON_BY_ID[a].hosts(id))fail('Invalid add-on.');t.addons[id]=a;}\r\n for(const [id,hp] of Object.entries(saved.health??{})){if(!own(BUILDING_BY_ID,id)||!int(hp,0,buildingHpMax(id,level(base,id))))fail('Invalid building health.');if(hp<buildingHpMax(id,level(base,id)))t.health[id]=hp;}\r\n for(const [id,r] of Object.entries(saved.repairs??{})){if(!own(BUILDING_BY_ID,id)||!own(t.health,id)||!obj(r)||!int(r.startedAt)||!int(r.dueAt,r.startedAt)||!int(r.cost)||!own(FACTION_BY_ID,r.track)||!/^crew-[1-8]$/.test(r.workerId))fail('Invalid repair job.');t.repairs[id]={startedAt:r.startedAt,dueAt:r.dueAt,cost:r.cost,track:r.track,workerId:r.workerId};}\r\n if(new Set(Object.values(t.repairs).map(r=>r.workerId)).size!==Object.keys(t.repairs).length)fail('A crew cannot repair two buildings.');\r\n return t;\r\n}\r\nexport const normalizeRtsTech=base=>({...base,rtsTech:techState(base)});\r\n// HUD health readout. Full health is never stored; absent = max.\r\nexport function buildingHealth(base,buildingId){\r\n const t=techState(base),max=buildingHpMax(buildingId,level(base,buildingId)),hp=own(t.health,buildingId)?t.health[buildingId]:max;\r\n return {hp,max,pct:max?Math.floor(hp*100/max):0,damaged:hp<max,offline:max>0&&hp===0,repair:t.repairs[buildingId]??null};\r\n}\r\n// Derived bonuses consumed by base-systems.mjs (baseSystems/buildingFunction) and the raid/drill roster builders.\r\nexport function researchEffects(base){\r\n const t=techState(base),l=t.levels;\r\n return {weaponUpgrade:l.weapons,armorUpgrade:l.armor,trainingSpeedPct:5*l.logistics,journeySpeedPct:2*l.logistics,\r\n  facilityTrainingSpeedPct:Object.fromEntries(Object.keys(t.addons).map(id=>[id,10])),advancedCompanionFacilities:Object.keys(t.addons),\r\n  offlineBuildingIds:Object.keys(t.health).filter(id=>t.health[id]===0)};\r\n}\r\nconst busyWorkers=(base,t)=>{const ids=new Set(Object.values(base.buildings??{}).map(b=>b.job?.builderId).filter(Boolean));for(const r of Object.values(t.repairs))ids.add(r.workerId);if(['launched','returning'].includes(base.expedition?.status)&&base.expedition.unitIds?.includes('myr5-worker'))ids.add('crew-1');return ids;};\r\nconst facilityBusy=(base,t,id)=>!!((own(t.health,id)&&t.health[id]===0)||base.buildings?.[id]?.job||t.jobs[id]||t.repairs[id]||Object.values(base.training??{}).some(j=>BUILDING_BY_ID[id]?.kind==='production'&&j.faction===BUILDING_BY_ID[id].track));\r\nconst wallet=(base,track,mineral,gas,q)=>(base.materials?.[track]??0)<mineral?{...q,allowed:false,reason:`Need ${mineral} ${FACTION_BY_ID[track].label} material.`}:gasOf(base)<gas?{...q,allowed:false,reason:`Need ${gas} gas.`}:q;\r\n// kind: 'research' {techId}, 'addon' {addonId}, 'repair'. Never throws; returns {allowed,reason,...terms}.\r\nexport const researchQuote=(base,researchId,buildingId,track)=>quoteFor(base,buildingId,{techId:researchId,track});\r\nfunction quoteFor(base,facilityId,request={}){\r\n const t=techState(base),d=own(BUILDING_BY_ID,facilityId)?BUILDING_BY_ID[facilityId]:null,bad=(reason,q={})=>({...q,allowed:false,reason}),kind=request.kind??'research';\r\n if(!d)return bad('Choose a facility.');const lvl=level(base,facilityId),track=d.track??(own(FACTION_BY_ID,request.track)?request.track:base.faction);if(!own(FACTION_BY_ID,track))return bad('Choose a workout material.');\r\n if(kind==='repair'){\r\n  const h=buildingHealth(base,facilityId);if(!h.max)return bad('Build this structure first.');if(!h.damaged)return bad('This building is intact.');\r\n  const frac=(h.max-h.hp)/h.max,cost=Math.max(1,Math.ceil(researchCatalog.repair.mineralPerFullHp*frac)),durationMs=Math.max(researchCatalog.repair.minMs,Math.ceil(researchCatalog.repair.msPerFullHp*frac)),busy=busyWorkers(base,t),workerId=Array.from({length:int(base.workers,1,8)?base.workers:1},(_,i)=>`crew-${i+1}`).find(id=>!busy.has(id))??null,q={kind,track,cost,durationMs,hp:h.hp,max:h.max,workerId,allowed:true,reason:null};\r\n  if(base.rest)return bad('Leave rest mode before repairs.',q);if(t.repairs[facilityId])return bad('Repairs are under way.',q);if(base.buildings[facilityId].job||t.jobs[facilityId])return bad('This building is busy.',q);if(!workerId)return bad('All builder crews are busy.',q);return wallet(base,track,cost,0,q);\r\n }\r\n if(kind==='addon'){\r\n  const a=own(ADDON_BY_ID,request.addonId)?ADDON_BY_ID[request.addonId]:null;if(!a||!a.hosts(facilityId))return bad('This add-on does not fit here.');\r\n  const q={kind,id:a.id,track,cost:a.mineral,gasCost:a.gas,durationMs:a.durationMs,requiredLevel:a.requiredLevel,allowed:true,reason:null};\r\n  if(t.addons[facilityId])return bad('This facility already has its add-on.',q);if(base.rest)return bad('Leave rest mode before building add-ons.',q);if(lvl<a.requiredLevel)return bad(`Need ${d.label} level ${a.requiredLevel}.`,q);if(own(t.health,facilityId)&&t.health[facilityId]===0)return bad('Repair this facility first.',q);if(facilityBusy(base,t,facilityId))return bad('This facility is busy.',q);return wallet(base,track,a.mineral,a.gas,q);\r\n }\r\n const tech=own(TECH_BY_ID,request.techId)?TECH_BY_ID[request.techId]:null;if(!tech)return bad('Choose a research topic.');if(!tech.hosts(facilityId))return bad(`Research ${tech.label} at the ${tech.hostLabel}.`);\r\n const next=t.levels[tech.id]+1,tier=TIERS[Math.min(TECH_MAX_LEVEL,next)-1],q={kind:'research',id:tech.id,targetLevel:Math.min(TECH_MAX_LEVEL,next),track,cost:tier.mineral,gasCost:tier.gas,durationMs:tier.durationMs,prerequisites:[{buildingId:'hq',level:next,met:level(base,'hq')>=next},{buildingId:facilityId,level:next,met:lvl>=next}],allowed:true,reason:null};\r\n if(next>TECH_MAX_LEVEL)return bad('Maximum research level reached.',q);if(lvl<1)return bad('Build this facility first.',q);if(base.rest)return bad('Leave rest mode before research.',q);\r\n if(own(t.health,facilityId)&&t.health[facilityId]===0)return bad('Repair this facility first.',q);if(Object.values(t.jobs).some(j=>j.kind==='research'&&j.id===tech.id))return bad(`${tech.label} is already under way.`,q);if(facilityBusy(base,t,facilityId))return bad('This facility is busy.',q);\r\n const missing=q.prerequisites.find(p=>!p.met);if(missing)return bad(`Need ${BUILDING_BY_ID[missing.buildingId].label} level ${missing.level}.`,q);return wallet(base,track,tier.mineral,tier.gas,q);\r\n}\r\n// Per-building panel for the HUD: every option with its quote, the active job and health.\r\nexport function researchPanel(base,facilityId,now){\r\n const t=techState(base);\r\n return {facilityId,level:level(base,facilityId),job:t.jobs[facilityId]??null,addon:t.addons[facilityId]??null,health:buildingHealth(base,facilityId),levels:{...t.levels},\r\n  research:researchCatalog.techs.filter(x=>x.hosts(facilityId)).map(x=>({...x,hosts:undefined,level:t.levels[x.id],quote:quoteFor(base,facilityId,{techId:x.id})})),\r\n  addons:researchCatalog.addons.filter(a=>a.hosts(facilityId)).map(a=>({...a,hosts:undefined,owned:t.addons[facilityId]===a.id,quote:quoteFor(base,facilityId,{kind:'addon',addonId:a.id})})),\r\n  repair:quoteFor(base,facilityId,{kind:'repair'}),remainingMs:Math.max(0,(t.jobs[facilityId]?.dueAt??t.repairs[facilityId]?.dueAt??now??0)-(now??0))};\r\n}\r\nconst spend=(out,track,mineral,gas)=>{out.materials={...out.materials,[track]:out.materials[track]-mineral};if(gas)out.rtsEconomy={...out.rtsEconomy,gas:gasOf(out)-gas};};\r\n// Exactly-once completion: a finished job is deleted as its effect lands, so replaying the same clock is a no-op.\r\nexport function advanceRtsTech(base,now){\r\n if(!int(now))fail('Invalid research clock.');const out=normalizeRtsTech(base),t=out.rtsTech;\r\n for(const [id,j] of Object.entries(t.jobs))if(j.dueAt<=now){if(j.kind==='research')t.levels[j.id]=j.targetLevel;else t.addons[id]=j.id;delete t.jobs[id];}\r\n for(const [id,r] of Object.entries(t.repairs))if(r.dueAt<=now){delete t.health[id];delete t.repairs[id];}\r\n return out;\r\n}\r\n// Actions: rts-research {facilityId,techId,track?} | rts-addon {facilityId,addonId} | rts-repair {buildingId} | rts-cancel {facilityId} (90% refund of mineral and gas).\r\nexport function applyRtsTechAction(base,action,{now}={}){\r\n if(!obj(action)||typeof action.type!=='string')fail('Choose a research command.');\r\n const out=advanceRtsTech(base,now),t=out.rtsTech,id=action.facilityId??action.buildingId;\r\n switch(action.type){\r\n case 'rts-research':case 'rts-addon':{const q=quoteFor(out,id,action.type==='rts-addon'?{kind:'addon',addonId:action.addonId}:{techId:action.techId,track:action.track});if(!q.allowed)fail(q.reason);\r\n  spend(out,q.track,q.cost,q.gasCost);t.jobs[id]={kind:q.kind,id:q.id,...(q.kind==='research'?{targetLevel:q.targetLevel}:{}),startedAt:now,dueAt:now+q.durationMs,mineralCost:q.cost,gasCost:q.gasCost,track:q.track};break;}\r\n case 'rts-repair':{const q=quoteFor(out,id,{kind:'repair'});if(!q.allowed)fail(q.reason);spend(out,q.track,q.cost,0);t.repairs[id]={startedAt:now,dueAt:now+q.durationMs,cost:q.cost,track:q.track,workerId:q.workerId};break;}\r\n case 'rts-cancel':{const j=own(t.jobs,id)?t.jobs[id]:null,r=own(t.repairs,id)?t.repairs[id]:null;if(!j&&!r)fail('Nothing to cancel here.');\r\n  if(j){spend(out,j.track,-Math.floor(j.mineralCost*.9),-Math.floor(j.gasCost*.9));delete t.jobs[id];}else{spend(out,r.track,-Math.floor(r.cost*.9),0);delete t.repairs[id];}break;}\r\n default:fail('Unknown research command.');}\r\n return normalizeRtsTech(out);\r\n}\r\n// INTERNAL (server drill settlement only; never a dispatchable action). damageByBuilding: {[buildingId]: whole HP}. Unbuilt sites and unknown IDs are ignored; HP floors at 0.\r\nexport function applyRtsBuildingDamage(base,damageByBuilding){\r\n if(!obj(damageByBuilding))fail('Invalid drill damage.');const out=normalizeRtsTech(base),t=out.rtsTech;\r\n for(const [id,amount] of Object.entries(damageByBuilding)){if(!own(BUILDING_BY_ID,id)||!int(amount))fail('Invalid drill damage.');const max=buildingHpMax(id,level(out,id));if(!max||!amount)continue;const hp=Math.max(0,(own(t.health,id)?t.health[id]:max)-amount);if(hp<max)t.health[id]=hp;}\r\n return out;\r\n}\r\n// UI contract aliases (frozen 21 Oct): array catalog + per-kind quotes with the root's preferred argument order.\r\nexport const RESEARCH_CATALOG=Object.freeze(researchCatalog.techs.map(t=>Object.freeze({id:t.id,label:t.label,hostLabel:t.hostLabel,effect:t.effect,maxLevel:TECH_MAX_LEVEL,tiers:TIERS})));\r\nexport const ADDON_CATALOG=researchCatalog.addons;\r\nexport const hostsResearch=(researchId,buildingId)=>!!TECH_BY_ID[researchId]?.hosts(buildingId);\r\nexport const addonQuote=(base,buildingId)=>quoteFor(base,buildingId,{kind:'addon',addonId:'tech-lab'});\r\nexport const repairQuote=(base,buildingId)=>quoteFor(base,buildingId,{kind:'repair'});\r\nexport const researchLevels=base=>({...techState(base).levels});\r\nexport const facilityJob=(base,buildingId)=>{const t=techState(base);return t.jobs[buildingId]??(t.repairs[buildingId]?{kind:'repair',...t.repairs[buildingId]}:null);};\r\n","modules/coach-world/run-grant-rules.mjs":"﻿const TERMINAL_RUN_STATUSES = new Set(['completed', 'defeated', 'abandoned']);\n\nfunction requireRecord(name, value) {\n  if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n    throw new TypeError(`${name} must be an object`);\n  }\n}\n\nfunction requireSafeNonnegativeInteger(name, value) {\n  if (!Number.isSafeInteger(value) || value < 0) {\n    throw new RangeError(`${name} must be a nonnegative safe integer`);\n  }\n}\n\nfunction requireNonemptyString(name, value) {\n  if (typeof value !== 'string' || value.length === 0) {\n    throw new TypeError(`${name} must be a nonempty string`);\n  }\n}\n\nfunction readEnergyPolicy(policy) {\n  requireRecord('policy', policy);\n  for (const key of ['energyPerUnitMs', 'energyPerRunCapMs', 'energyBankCapMs']) {\n    requireSafeNonnegativeInteger(`policy.${key}`, policy[key]);\n  }\n  return policy;\n}\n\n/**\n * Return an energy grant in milliseconds for one already accepted qualifying unit.\n * This is a pure grant calculation, not an energy clock or receipt validator.\n * The caller must journal the accepted unit and grant atomically to prevent replay.\n */\nexport function energyGrantMs({ runGrantedMs, bankAvailableMs } = {}, policy) {\n  requireSafeNonnegativeInteger('runGrantedMs', runGrantedMs);\n  requireSafeNonnegativeInteger('bankAvailableMs', bankAvailableMs);\n  const { energyPerUnitMs, energyPerRunCapMs, energyBankCapMs } = readEnergyPolicy(policy);\n  const remainingRun = Math.max(0, energyPerRunCapMs - runGrantedMs);\n  const remainingBank = Math.max(0, energyBankCapMs - bankAvailableMs);\n  return Math.min(energyPerUnitMs, remainingRun, remainingBank);\n}\n\n/**\n * Pure terminal-run ticket predicate. `hasAvailableTicket` means a ticket already\n * exists for the account (capacity is occupied), not free capacity. Inputs are\n * normalized server-side state, not browser claims. Repeating this call with the same arguments repeats the\n * same answer; callers must journal one decision per run and consume capacity\n * atomically. A full capacity discards this run's eligibility; it is not deferred.\n */\nexport function ticketDecision({ run, lastRaidStartSeq, hasAvailableTicket } = {}, policy) {\n  requireRecord('run', run);\n  requireRecord('policy', policy);\n  requireSafeNonnegativeInteger('policy.ticketMinUnits', policy.ticketMinUnits);\n  requireSafeNonnegativeInteger('policy.ticketCapacity', policy.ticketCapacity);\n  if (policy.ticketCapacity > 1) throw new RangeError('policy.ticketCapacity must be 0 or 1');\n  requireNonemptyString('run.runId', run.runId);\n  requireNonemptyString('run.status', run.status);\n  requireSafeNonnegativeInteger('run.startedSeq', run.startedSeq);\n  requireSafeNonnegativeInteger('run.acceptedQualifyingUnits', run.acceptedQualifyingUnits);\n  if (lastRaidStartSeq !== null) requireSafeNonnegativeInteger('lastRaidStartSeq', lastRaidStartSeq);\n  if (typeof hasAvailableTicket !== 'boolean') throw new TypeError('hasAvailableTicket must be boolean');\n\n  const base = {\n    runId: run.runId,\n    qualifyingUnits: run.acceptedQualifyingUnits,\n    ticketCapacity: policy.ticketCapacity,\n  };\n  const deny = reason => ({ ...base, eligible: false, reason });\n  if (!TERMINAL_RUN_STATUSES.has(run.status)) return deny('not_terminal');\n  if (run.acceptedQualifyingUnits < policy.ticketMinUnits) return deny('below_threshold');\n  if (lastRaidStartSeq !== null && run.startedSeq <= lastRaidStartSeq) return deny('run_started_before_last_raid');\n  if (policy.ticketCapacity === 0 || hasAvailableTicket) return deny('capacity_full');\n  return { ...base, eligible: true, reason: 'eligible' };\n}\n\nfunction validatePromotionContext(context) {\n  requireRecord('context', context);\n  for (const key of ['ownerId', 'runId', 'levelId', 'scopeId']) requireNonemptyString(`context.${key}`, context[key]);\n  requireSafeNonnegativeInteger('context.epoch', context.epoch);\n}\n\nfunction validateAcceptedReceipt(receipt, index) {\n  const name = `acceptedReceipts[${index}]`;\n  requireRecord(name, receipt);\n  for (const key of ['ownerId', 'runId', 'levelId', 'scopeId']) requireNonemptyString(`${name}.${key}`, receipt[key]);\n  requireSafeNonnegativeInteger(`${name}.epoch`, receipt.epoch);\n  requireNonemptyString(`${name}.kind`, receipt.kind);\n}\n\nfunction receiptMatchesContext(receipt, context) {\n  return receipt.ownerId === context.ownerId\n    && receipt.epoch === context.epoch\n    && receipt.runId === context.runId\n    && receipt.levelId === context.levelId\n    && receipt.scopeId === context.scopeId;\n}\n\n/**\n * Promote eligible candidates from normalized, ALREADY SERVER-ACCEPTED receipt\n * records loaded by a trusted caller. This function cannot authenticate, accept,\n * persist, deduplicate, or make browser-provided data authoritative.\n *\n * Receipt shapes consumed:\n * - { kind:'required_visit', visitId, completed:true, ...scope fields }\n * - { kind:'mainboss', phase:2, outcome:'defeated', finisherComplete:true, ...,\n *     canonicalCoachId }\n * - { kind:'miniboss', outcome:'defeated', finisherComplete:true, ...,\n *     canonicalCoachId }\n * - { kind:'level_clear', outcome:'cleared', ...scope fields }\n *\n * Every receipt must match owner/epoch/run/level/scope. Promotion requires a\n * nonempty complete requiredVisitIds set, the same run's phase-2 main finisher,\n * and its accepted level-clear receipt. Returned coach IDs come only from\n * canonicalCoachId fields on matching finisher receipts; they are deduplicated\n * and sorted. No element, difficulty, catalog, or fallback coach is inferred.\n */\nexport function promoteFromAcceptedReceipts({\n  context,\n  requiredVisitIds,\n  acceptedReceipts,\n} = {}) {\n  validatePromotionContext(context);\n  if (!Array.isArray(requiredVisitIds)) throw new TypeError('requiredVisitIds must be an array');\n  if (requiredVisitIds.length === 0) {\n    return promotionResult(context, false, 'required_path_empty', [], []);\n  }\n  const required = new Set();\n  for (const [index, id] of requiredVisitIds.entries()) {\n    requireNonemptyString(`requiredVisitIds[${index}]`, id);\n    if (required.has(id)) return promotionResult(context, false, 'required_path_invalid', [], [...required].sort());\n    required.add(id);\n  }\n  if (!Array.isArray(acceptedReceipts)) throw new TypeError('acceptedReceipts must be an array');\n  acceptedReceipts.forEach(validateAcceptedReceipt);\n  const matching = acceptedReceipts.filter(receipt => receiptMatchesContext(receipt, context));\n  const completedVisits = new Set(matching\n    .filter(receipt => receipt.kind === 'required_visit' && receipt.completed === true)\n    .map(receipt => receipt.visitId)\n    .filter(id => typeof id === 'string' && id.length > 0));\n  const missingVisitIds = [...required].filter(id => !completedVisits.has(id)).sort();\n  const hasMainPhase2Finisher = matching.some(receipt => receipt.kind === 'mainboss'\n    && receipt.phase === 2\n    && receipt.outcome === 'defeated'\n    && receipt.finisherComplete === true);\n  const hasAcceptedLevelClear = matching.some(receipt => receipt.kind === 'level_clear'\n    && receipt.outcome === 'cleared');\n  if (missingVisitIds.length) return promotionResult(context, false, 'required_path_incomplete', [], missingVisitIds);\n  if (!hasMainPhase2Finisher) return promotionResult(context, false, 'main_phase2_finisher_missing', [], []);\n  if (!hasAcceptedLevelClear) return promotionResult(context, false, 'level_clear_missing', [], []);\n\n  const coachIds = [...new Set(matching\n    .filter(receipt => (receipt.kind === 'mainboss' || receipt.kind === 'miniboss')\n      && receipt.outcome === 'defeated'\n      && receipt.finisherComplete === true\n      && (receipt.kind !== 'mainboss' || receipt.phase === 2)\n      && typeof receipt.canonicalCoachId === 'string'\n      && receipt.canonicalCoachId.length > 0)\n    .map(receipt => receipt.canonicalCoachId))].sort();\n  if (!coachIds.length) return promotionResult(context, false, 'no_eligible_finisher', [], []);\n  return promotionResult(context, true, 'promotable', coachIds, []);\n}\n\nfunction promotionResult(context, eligible, reason, coachIds, missingVisitIds) {\n  return {\n    ownerId: context.ownerId,\n    epoch: context.epoch,\n    runId: context.runId,\n    levelId: context.levelId,\n    scopeId: context.scopeId,\n    eligible,\n    reason,\n    coachIds: [...coachIds],\n    missingVisitIds: [...missingVisitIds],\n  };\n}\n","modules/coach-world/sites-data.mjs":"// Official Clark County recreation GIS; source URLs retained per site.\r\nexport const SITE_DATA=[{\"id\":\"vegas-chest-1\",\"name\":\"Lorenzi Park\",\"track\":\"chest\",\"faction\":\"chest\",\"latitude\":36.1792022,\"longitude\":-115.1860033,\"referenceCoordinate\":{\"latitude\":36.1792022,\"longitude\":-115.1860033,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.183435,36.181077],[-115.187862,36.181157],[-115.18788,36.181143],[-115.18789,36.181119],[-115.187923,36.177448],[-115.185554,36.177415],[-115.184234,36.177397],[-115.184228,36.178605],[-115.184198,36.179049],[-115.183977,36.179392],[-115.183702,36.179635],[-115.183342,36.179958],[-115.18325,36.18004],[-115.183226,36.180115],[-115.183234,36.180211],[-115.183301,36.180422],[-115.183366,36.180589],[-115.183425,36.180768],[-115.183456,36.180946],[-115.183435,36.181077]]],[[[-115.183242,36.180197],[-115.183245,36.180038],[-115.183323,36.179968],[-115.183438,36.179865],[-115.183637,36.179687],[-115.183826,36.179518],[-115.184,36.179356],[-115.184206,36.179035],[-115.184215,36.178901],[-115.184233,36.178625],[-115.184242,36.177383],[-115.181345,36.177392],[-115.181344,36.181027],[-115.183443,36.181063],[-115.183464,36.180932],[-115.183433,36.180754],[-115.183374,36.180575],[-115.183309,36.180408],[-115.183242,36.180197]]]]},\"hours\":\"07:00-23:00\",\"openingHours\":[7,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/5\",\"gisObjectIdField\":\"OBJECTID_1\",\"gisObjectIds\":[73,74],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/5/query?objectIds=73,74&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-5.json\",\"rawFeatureIds\":[73,74],\"rawPropertiesObjectIds\":[73,74],\"rawStatusCodes\":[1,1],\"officialPage\":\"https://www.lasvegasnevada.gov/Residents/Parks-Facilities/Lorenzi-Park\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-quads-1\",\"name\":\"Centennial Hills Park\",\"track\":\"quads\",\"faction\":\"quads\",\"latitude\":36.2854174,\"longitude\":-115.2627944,\"referenceCoordinate\":{\"latitude\":36.2854174,\"longitude\":-115.2627944,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.261461,36.287552],[-115.261492,36.287672],[-115.261603,36.287677],[-115.261849,36.287752],[-115.262442,36.287759],[-115.262646,36.28777],[-115.262818,36.28778],[-115.262942,36.28769],[-115.263132,36.287602],[-115.263178,36.28758],[-115.263356,36.287528],[-115.264966,36.287562],[-115.26504,36.284325],[-115.264855,36.284304],[-115.264646,36.284274],[-115.264285,36.284176],[-115.263966,36.28398],[-115.263792,36.283861],[-115.263707,36.283768],[-115.263605,36.283653],[-115.263534,36.28356],[-115.263427,36.283443],[-115.263231,36.283292],[-115.263129,36.283231],[-115.262945,36.283135],[-115.262747,36.283036],[-115.262533,36.282941],[-115.262433,36.282889],[-115.262316,36.282808],[-115.262208,36.28271],[-115.262068,36.282542],[-115.261965,36.2824],[-115.261867,36.282234],[-115.261796,36.282135],[-115.261696,36.281985],[-115.26151,36.2818],[-115.261289,36.281945],[-115.260806,36.282512],[-115.260684,36.282998],[-115.260642,36.283387],[-115.260537,36.28651],[-115.261403,36.286547],[-115.261397,36.286911],[-115.261307,36.287179],[-115.26092,36.28724],[-115.260784,36.287215],[-115.26071,36.287175],[-115.260618,36.287095],[-115.260581,36.287089],[-115.26055,36.287154],[-115.260543,36.287224],[-115.26053,36.287414],[-115.261227,36.287416],[-115.261382,36.287461],[-115.261461,36.287552]]],[[[-115.261502,36.288076],[-115.26157,36.288231],[-115.261546,36.289575],[-115.261452,36.28976],[-115.261366,36.289865],[-115.261285,36.289969],[-115.261143,36.290044],[-115.261118,36.290074],[-115.261142,36.290109],[-115.261278,36.290199],[-115.261302,36.290314],[-115.261431,36.29054],[-115.262613,36.290634],[-115.262638,36.289518],[-115.262633,36.28779],[-115.261837,36.287777],[-115.261621,36.287707],[-115.261516,36.287702],[-115.261502,36.288076]]]]},\"hours\":\"07:00-23:00\",\"openingHours\":[7,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/5\",\"gisObjectIdField\":\"OBJECTID_1\",\"gisObjectIds\":[65],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/5/query?objectIds=65&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-5.json\",\"rawFeatureIds\":[65],\"rawPropertiesObjectIds\":[65],\"rawStatusCodes\":[1],\"officialPage\":\"https://www.lasvegasnevada.gov/Residents/Parks-Facilities/Centennial-Hills-Park\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-glutes-2\",\"name\":\"Craig Ranch Regional Park\",\"track\":\"glutes\",\"faction\":\"glutes\",\"latitude\":36.2432799,\"longitude\":-115.1491227,\"referenceCoordinate\":{\"latitude\":36.2432799,\"longitude\":-115.1491227,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.146822,36.246436],[-115.146877,36.246466],[-115.146913,36.246485],[-115.146949,36.246503],[-115.146986,36.24652],[-115.147079,36.246564],[-115.147119,36.246583],[-115.147158,36.246603],[-115.147197,36.246623],[-115.147235,36.246644],[-115.147273,36.246666],[-115.14731,36.246688],[-115.147347,36.24671],[-115.147383,36.246733],[-115.147419,36.246757],[-115.147455,36.246781],[-115.147489,36.246806],[-115.147513,36.246822],[-115.147522,36.246828],[-115.147541,36.246839],[-115.147561,36.24685],[-115.14757,36.246856],[-115.147591,36.246866],[-115.147611,36.246875],[-115.147632,36.246884],[-115.147643,36.246889],[-115.147664,36.246897],[-115.147675,36.246901],[-115.147697,36.246908],[-115.147708,36.246911],[-115.14773,36.246917],[-115.147742,36.24692],[-115.147764,36.246926],[-115.147776,36.246928],[-115.147799,36.246932],[-115.147822,36.246936],[-115.147846,36.246939],[-115.147869,36.246941],[-115.147881,36.246942],[-115.147904,36.246943],[-115.147928,36.246944],[-115.14794,36.246944],[-115.152821,36.246941],[-115.153103,36.246941],[-115.154531,36.246921],[-115.154488,36.243158],[-115.154448,36.239561],[-115.153623,36.239559],[-115.153606,36.239567],[-115.153606,36.239559],[-115.15302,36.239558],[-115.148844,36.239606],[-115.148904,36.242955],[-115.145882,36.243039],[-115.143766,36.243098],[-115.14376,36.243185],[-115.143757,36.243235],[-115.143755,36.243325],[-115.143756,36.243375],[-115.143757,36.243426],[-115.143768,36.243683],[-115.143802,36.243682],[-115.143811,36.243892],[-115.143864,36.243973],[-115.143969,36.243924],[-115.143953,36.24367],[-115.14403,36.24368],[-115.144057,36.243689],[-115.144081,36.243698],[-115.144105,36.243706],[-115.144129,36.243714],[-115.144153,36.243721],[-115.144179,36.243728],[-115.144206,36.243735],[-115.144233,36.243741],[-115.14431,36.243755],[-115.144348,36.243762],[-115.144366,36.243765],[-115.144386,36.243767],[-115.144399,36.243768],[-115.14443,36.243769],[-115.144448,36.243769],[-115.144482,36.24377],[-115.14454,36.243768],[-115.144576,36.244615],[-115.144661,36.244554],[-115.14497,36.244564],[-115.145063,36.244706],[-115.145892,36.245467],[-115.146888,36.24636],[-115.146822,36.246436]]]]},\"hours\":\"06:00-23:00; source says seasonal changes possible\",\"openingHours\":[6,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/10\",\"gisObjectIdField\":\"OBJECTID_1\",\"gisObjectIds\":[11],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/10/query?objectIds=11&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-10.json\",\"rawFeatureIds\":[11],\"rawPropertiesObjectIds\":[12],\"rawStatusCodes\":[1],\"officialPage\":\"https://www.cityofnorthlasvegas.com/home/showpublisheddocument/5643/638169045960900000\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-arms-shoulders-1\",\"name\":\"Desert Breeze Park\",\"track\":\"arms-shoulders\",\"faction\":\"arms-shoulders\",\"latitude\":36.1258471,\"longitude\":-115.2759792,\"referenceCoordinate\":{\"latitude\":36.1258471,\"longitude\":-115.2759792,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.274399,36.129463],[-115.278954,36.129564],[-115.278968,36.12591],[-115.278981,36.122239],[-115.270005,36.122076],[-115.269992,36.123989],[-115.269981,36.125717],[-115.272992,36.125785],[-115.272955,36.127448],[-115.274316,36.127452],[-115.274399,36.129463]]]]},\"hours\":\"06:00-23:00 general county rule; posted exceptions/closures override\",\"openingHours\":[6,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6\",\"gisObjectIdField\":\"OBJECTID_12\",\"gisObjectIds\":[109],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6/query?objectIds=109&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-6.json\",\"rawFeatureIds\":[109],\"rawPropertiesObjectIds\":[109],\"rawStatusCodes\":[1],\"officialPage\":\"https://parkslocator.clarkcountynv.gov/Search/ParkDetail?parkId=3\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-chest-2\",\"name\":\"Sunset Park\",\"track\":\"chest\",\"faction\":\"chest\",\"latitude\":36.065258,\"longitude\":-115.1122018,\"referenceCoordinate\":{\"latitude\":36.065258,\"longitude\":-115.1122018,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.106059,36.069979],[-115.10606,36.070319],[-115.10606,36.070662],[-115.10606,36.070684],[-115.106061,36.071433],[-115.106984,36.071429],[-115.107753,36.071426],[-115.108616,36.071422],[-115.108601,36.070898],[-115.108568,36.069747],[-115.109143,36.069749],[-115.10969,36.06975],[-115.110182,36.069754],[-115.1108,36.069758],[-115.11082,36.070402],[-115.11084,36.071042],[-115.110852,36.07142],[-115.118298,36.071448],[-115.118334,36.071444],[-115.118367,36.071435],[-115.11841,36.071416],[-115.118447,36.071388],[-115.118467,36.071365],[-115.118484,36.071338],[-115.118493,36.07131],[-115.118497,36.071273],[-115.118327,36.064392],[-115.118327,36.064392],[-115.118217,36.059831],[-115.113819,36.0579],[-115.1139,36.060643],[-115.109398,36.060669],[-115.109419,36.061613],[-115.109517,36.064359],[-115.106053,36.064358],[-115.106058,36.066124],[-115.106058,36.067886],[-115.106058,36.067972],[-115.106061,36.069639],[-115.106059,36.069979]]],[[[-115.113806,36.057058],[-115.113789,36.057588],[-115.118196,36.059519],[-115.118196,36.059516],[-115.118133,36.057077],[-115.118122,36.057044],[-115.1181,36.057022],[-115.118066,36.057008],[-115.118002,36.057007],[-115.113806,36.057058]]]]},\"hours\":\"06:00-23:00 general county rule; posted exceptions/closures override\",\"openingHours\":[6,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6\",\"gisObjectIdField\":\"OBJECTID_12\",\"gisObjectIds\":[62],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6/query?objectIds=62&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-6.json\",\"rawFeatureIds\":[62],\"rawPropertiesObjectIds\":[62],\"rawStatusCodes\":[1],\"officialPage\":\"https://www.clarkcountynv.gov/government/departments/parks___recreation/services/area_reservations/sunset-park-webpage\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-quads-2\",\"name\":\"Cornerstone Park\",\"track\":\"quads\",\"faction\":\"quads\",\"latitude\":36.0361899,\"longitude\":-115.0533469,\"referenceCoordinate\":{\"latitude\":36.0361899,\"longitude\":-115.0533469,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.050198,36.038553],[-115.050232,36.038554],[-115.050233,36.038588],[-115.050237,36.038623],[-115.050245,36.038656],[-115.050255,36.038691],[-115.05027,36.038725],[-115.050286,36.038756],[-115.050305,36.038787],[-115.050327,36.038816],[-115.050353,36.038845],[-115.05038,36.038871],[-115.05041,36.038896],[-115.050442,36.038919],[-115.050477,36.03894],[-115.050512,36.038959],[-115.050549,36.038975],[-115.050588,36.038989],[-115.050996,36.039125],[-115.051019,36.039134],[-115.051041,36.039143],[-115.051062,36.039154],[-115.051083,36.039166],[-115.051102,36.03918],[-115.051121,36.039194],[-115.051138,36.039209],[-115.051152,36.039224],[-115.051185,36.03926],[-115.051233,36.038064],[-115.052812,36.038043],[-115.055603,36.038006],[-115.055604,36.037999],[-115.055605,36.037947],[-115.055611,36.037856],[-115.055613,36.037854],[-115.055617,36.037821],[-115.055622,36.037772],[-115.055638,36.037696],[-115.055638,36.037692],[-115.055674,36.037543],[-115.055699,36.037439],[-115.055718,36.037389],[-115.055772,36.037234],[-115.055837,36.037083],[-115.055867,36.037011],[-115.05591,36.036936],[-115.055921,36.036912],[-115.055983,36.0368],[-115.055991,36.036787],[-115.056034,36.036718],[-115.056085,36.036645],[-115.056141,36.036563],[-115.056184,36.036511],[-115.056217,36.03647],[-115.056294,36.036377],[-115.056305,36.036369],[-115.056376,36.036295],[-115.056396,36.036274],[-115.05642,36.03625],[-115.056538,36.03613],[-115.056643,36.036007],[-115.056643,36.036005],[-115.056713,36.035918],[-115.05674,36.03588],[-115.056767,36.035845],[-115.056829,36.03575],[-115.056901,36.035631],[-115.056909,36.035616],[-115.056947,36.035552],[-115.056971,36.035505],[-115.05698,36.035489],[-115.056985,36.03548],[-115.057009,36.035431],[-115.057047,36.035338],[-115.057091,36.035223],[-115.057101,36.035197],[-115.057134,36.035096],[-115.057145,36.035051],[-115.057183,36.034919],[-115.057183,36.034901],[-115.057205,36.034811],[-115.057219,36.034709],[-115.057233,36.034555],[-115.057239,36.034493],[-115.057122,36.034494],[-115.056943,36.034488],[-115.056764,36.034475],[-115.056585,36.034455],[-115.056409,36.034428],[-115.056234,36.034395],[-115.055779,36.0344],[-115.055785,36.03429],[-115.055785,36.034275],[-115.055658,36.034231],[-115.055538,36.034186],[-115.05542,36.034138],[-115.055305,36.034086],[-115.055247,36.034059],[-115.055135,36.034002],[-115.055025,36.033942],[-115.054918,36.033879],[-115.054814,36.033813],[-115.054548,36.033639],[-115.054467,36.033588],[-115.054418,36.033558],[-115.054318,36.0335],[-115.054266,36.033473],[-115.054163,36.03342],[-115.05411,36.033394],[-115.054003,36.033346],[-115.053949,36.03332],[-115.053894,36.033298],[-115.053783,36.033255],[-115.053671,36.033216],[-115.053556,36.033179],[-115.05344,36.033146],[-115.053323,36.033117],[-115.053205,36.03309],[-115.053085,36.033067],[-115.052965,36.033048],[-115.052844,36.033032],[-115.052722,36.033019],[-115.0526,36.03301],[-115.052477,36.033004],[-115.052354,36.033002],[-115.052232,36.033003],[-115.052109,36.033008],[-115.051987,36.033016],[-115.051865,36.033027],[-115.051743,36.033042],[-115.051623,36.033061],[-115.051503,36.033083],[-115.051443,36.033095],[-115.051325,36.033122],[-115.051208,36.033152],[-115.051093,36.033186],[-115.050979,36.033223],[-115.050866,36.033263],[-115.050755,36.033306],[-115.050647,36.033352],[-115.05054,36.033402],[-115.050436,36.033454],[-115.050333,36.033509],[-115.050232,36.033569],[-115.050226,36.034462],[-115.050201,36.038077],[-115.050198,36.038553]]]]},\"hours\":\"06:00-22:00\",\"openingHours\":[6,22],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/7\",\"gisObjectIdField\":\"OBJECTID_1\",\"gisObjectIds\":[50],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/7/query?objectIds=50&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-7.json\",\"rawFeatureIds\":[50],\"rawPropertiesObjectIds\":[138],\"rawStatusCodes\":[1],\"officialPage\":\"https://www.cityofhenderson.com/Home/Components/FacilityDirectory/FacilityDirectory/218/752?npage=1\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1},{\"id\":\"vegas-glutes-1\",\"name\":\"Mountains Edge Park\",\"track\":\"glutes\",\"faction\":\"glutes\",\"latitude\":36.004247,\"longitude\":-115.2700208,\"referenceCoordinate\":{\"latitude\":36.004247,\"longitude\":-115.2700208,\"method\":\"Shapely representative_point of union of same-name cached PKSTATUS=1 GIS polygons in EPSG:4326\",\"purpose\":\"Discovery reference ONLY; not a navigation target, meeting anchor or battle presence anchor\"},\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-115.270719,36.002378],[-115.270735,36.005036],[-115.270735,36.005047],[-115.272281,36.005016],[-115.272338,36.005015],[-115.273238,36.005006],[-115.273728,36.004995],[-115.273728,36.004992],[-115.273728,36.004991],[-115.273728,36.00499],[-115.273656,36.002351],[-115.273643,36.002351],[-115.273629,36.002351],[-115.273608,36.002351],[-115.272893,36.002358],[-115.272717,36.002359],[-115.272559,36.002361],[-115.270719,36.002378]]],[[[-115.26631,36.002431],[-115.266326,36.003504],[-115.266344,36.005195],[-115.268655,36.005117],[-115.268673,36.005117],[-115.270735,36.005047],[-115.270735,36.005036],[-115.270719,36.002378],[-115.26631,36.002431]]]]},\"hours\":\"06:00-23:00 general county rule; posted exceptions/closures override\",\"openingHours\":[6,23],\"timezone\":\"America/Los_Angeles\",\"enabled\":true,\"discoverable\":true,\"source\":{\"gisLayer\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6\",\"gisObjectIdField\":\"OBJECTID_12\",\"gisObjectIds\":[156,170],\"coordinateQuery\":\"https://maps.clarkcountynv.gov/arcgis/rest/services/OpenData/Recreation/MapServer/6/query?objectIds=156,170&outFields=*&outSR=4326&f=geojson\",\"rawSnapshot\":\"vegas-gis-layer-6.json\",\"rawFeatureIds\":[156,170],\"rawPropertiesObjectIds\":[169,183],\"rawStatusCodes\":[1,1],\"officialPage\":\"https://parkslocator.clarkcountynv.gov/Search/ParkDetail?parkId=104\",\"retrieved\":\"2026-10-16\"},\"referenceOnly\":true,\"meetingAnchor\":null,\"accessNote\":\"Choose an ordinary open public path; park reference is not a navigation or meeting point. Avoid water, roads, play equipment and restricted areas.\",\"inventoryVersion\":1}];\r\n","modules/coach-world/sites.mjs":"import {SITE_DATA} from './sites-data.mjs';\r\nimport {FACTIONS} from './catalog.mjs';\r\nimport {polygonToCells,cellToBoundary,cellToLatLng,latLngToCell,containsPoint,distanceMeters,neighbors} from './geo.mjs';\r\nexport const WORLD_SITES=Object.freeze(SITE_DATA.map(s=>Object.freeze({...s})));\r\nexport function isSiteOpen(site,now=Date.now()){if(!site?.enabled||!Number.isFinite(now))return false;const h=Number(new Intl.DateTimeFormat('en-US',{hour:'numeric',hourCycle:'h23',timeZone:site.timezone||'America/Los_Angeles'}).format(new Date(now)));const [a,b]=site.openingHours||[0,24];return a<=b?h>=a&&h<b:h>=a||h<b;}\r\n// Existing public-park polygons provide access candidates, not surveyed walking routes.\r\n// Six starting patches per hub are distinct from the seven maintained meetup/shop hubs.\r\nconst sectors=[],known=new Set();\r\nWORLD_SITES.forEach((site,siteIndex)=>{\r\n const cells=new Set();for(const poly of site.geometry.coordinates){try{for(const id of polygonToCells(poly,10,true))cells.add(id);}catch{}}\r\n const rows=[...cells].map(id=>{const [latitude,longitude]=cellToLatLng(id);return {id,latitude,longitude};}).filter(c=>containsPoint(site.geometry,c.latitude,c.longitude));\r\n rows.sort((a,b)=>distanceMeters(a,site)-distanceMeters(b,site)||a.id.localeCompare(b.id));\r\n const seeds=[];if(rows.length)seeds.push(rows[0]);while(seeds.length<Math.min(6,rows.length)){const next=rows.filter(c=>!seeds.includes(c)).sort((a,b)=>Math.min(...seeds.map(s=>distanceMeters(b,s)))-Math.min(...seeds.map(s=>distanceMeters(a,s)))||a.id.localeCompare(b.id))[0];seeds.push(next);}\r\n for(const row of rows){if(known.has(row.id))continue;known.add(row.id);let i=0,best=Infinity;seeds.forEach((s,j)=>{const d=distanceMeters(s,row);if(d<best){best=d;i=j;}});const faction=FACTIONS[(siteIndex*6+i)%7].id;\r\n  sectors.push(Object.freeze({...row,siteId:site.id,faction,initialFaction:faction,initialPatchId:`${site.id}-patch-${i}`,neighbors:neighbors(row.id),polygon:cellToBoundary(row.id,true),outpostFactions:seeds.some(s=>s.id===row.id)?FACTIONS.map(f=>f.id):[],enabled:true}));\r\n }\r\n});\r\nexport const WORLD_SECTORS=Object.freeze(sectors);\r\nexport const SECTOR_BY_ID=Object.freeze(Object.fromEntries(WORLD_SECTORS.map(s=>[s.id,s])));\r\nexport function locatePublicSector(latitude,longitude,accuracy=0,now=Date.now()){\r\n if(typeof accuracy!=='number'||!Number.isFinite(accuracy)||accuracy<0||accuracy>100)return {status:'ambiguous',sectorId:null,siteId:null};\r\n let cell;try{cell=latLngToCell(latitude,longitude,10);}catch{return {status:'outside',sectorId:null,siteId:null};}\r\n const sector=SECTOR_BY_ID[cell];if(!sector)return {status:'outside',sectorId:null,siteId:null};\r\n const site=WORLD_SITES.find(s=>s.id===sector.siteId);if(!site||!containsPoint(site.geometry,latitude,longitude))return {status:'outside',sectorId:cell,siteId:sector.siteId};\r\n if(!isSiteOpen(site,now))return {status:'closed',sectorId:cell,siteId:site.id};\r\n // Do not demand a GPS uncertainty disk fit a narrow sidewalk. Poor fixes stay ambiguous.\r\n let boundaryAmbiguous=false;if(accuracy>0){const latRadius=accuracy/111320,lngRadius=latRadius/Math.max(.05,Math.cos(latitude*Math.PI/180));for(let i=0;i<8;i++){const a=i*Math.PI/4;if(!containsPoint(site.geometry,latitude+Math.sin(a)*latRadius,longitude+Math.cos(a)*lngRadius)){boundaryAmbiguous=true;break;}}}\r\n return {status:accuracy>25||boundaryAmbiguous?'ambiguous':'inside',sectorId:cell,siteId:site.id,sector,site};\r\n}\r\nexport const COVERAGE=Object.freeze({metro:'Las Vegas valley',publicHubCount:WORLD_SITES.length,initialPatchCount:new Set(WORLD_SECTORS.map(s=>s.initialPatchId)).size,sectorCount:WORLD_SECTORS.length,physicalAnchorsVerified:0,referenceOnly:true});\r\n","modules/coach-world/sprite-billboards.mjs":"import {getSpriteCanvas} from './pixel-art.mjs';\r\n\r\nexport function createPixelBillboards(T,{getCanvas=getSpriteCanvas}={}){\r\n const group=new T.Group(),textures=new Map(),materials=new Set();let disposed=false;group.name='pixel-billboard-layer';\r\n return {group,add({faction='chest',kind='hq',variant=0,x=0,y=0,z=0,height=4,opacity=1,tint=0xffffff}={}){\r\n  if(disposed)throw Error('Pixel layer is disposed.');variant=Math.max(0,Math.min(4,Math.floor(variant)||0));const key=`${faction}:${kind}:${variant}`;let texture=textures.get(key);\r\n  if(!texture){const canvas=getCanvas(faction,kind,variant);texture=new T.CanvasTexture(canvas);texture.magFilter=texture.minFilter=T.NearestFilter;texture.generateMipmaps=false;texture.colorSpace=T.SRGBColorSpace;textures.set(key,texture);}\r\n  const material=new T.SpriteMaterial({map:texture,color:tint,transparent:true,opacity,alphaTest:.06,depthWrite:true,toneMapped:false});materials.add(material);const sprite=new T.Sprite(material),canvas=texture.image;sprite.center.set(.5,0);sprite.position.set(x,y,z);sprite.scale.set(height*(canvas.width/canvas.height),height,1);sprite.name=`pixel-${kind}`;group.add(sprite);return sprite;\r\n },dispose(){if(disposed)return;disposed=true;group.removeFromParent();materials.forEach(m=>m.dispose());textures.forEach(t=>t.dispose());materials.clear();textures.clear();group.clear();}};\r\n}\r\n\r\n// THREE.Sprite instances share library-owned geometry. Dispose our materials and\r\n// textures, while leaving that geometry available to the other active layers.\r\nexport function disposePixelObject(root){if(!root)return;root.removeFromParent?.();const materials=new Set(),textures=new Set(),geometry=new Set();root.traverse(o=>{if(o.geometry&&!o.isSprite)geometry.add(o.geometry);for(const m of Array.isArray(o.material)?o.material:o.material?[o.material]:[]){materials.add(m);if(m.map)textures.add(m.map);}});textures.forEach(t=>t.dispose());materials.forEach(m=>m.dispose());geometry.forEach(g=>g.dispose());root.clear?.();}\r\n\r\nexport function pixelViewport(width,height,scale=2){scale=Math.max(1,scale);const rasterWidth=Math.max(1,Math.floor(width/scale)),rasterHeight=Math.max(1,Math.floor(height/scale));return {width:rasterWidth,height:rasterHeight,scale,displayWidth:Math.round(rasterWidth*scale),displayHeight:Math.round(rasterHeight*scale)};}\r\nexport function fitPixelRenderer(renderer,camera,container,scale=2){const r=container.getBoundingClientRect(),size=pixelViewport(Math.max(1,r.width),Math.max(1,r.height),scale);renderer.setPixelRatio(1);renderer.setSize(size.width,size.height,false);const canvas=renderer.domElement;Object.assign(canvas.style,{width:`${size.displayWidth}px`,height:`${size.displayHeight}px`,left:'50%',top:'50%',right:'auto',bottom:'auto',transform:'translate(-50%,-50%)',imageRendering:'pixelated'});camera.aspect=size.width/size.height;camera.updateProjectionMatrix();return size;}\r\n","modules/coach-world/state.mjs":"import {normalizeRtsDefense} from './rts-defense-state.mjs';\r\nimport {baseSystems,discountedCost,trainingQuote} from './base-systems.mjs';\r\nimport {FACTIONS,FACTION_BY_ID,GROUP_TO_FACTION,BUILDINGS,BUILDING_BY_ID,MAX_LEVEL,RULES,SHIP_IDS,hangarShipTier} from './catalog.mjs';\r\nimport {COACHES,STARTER_COACH_IDS} from '../../performance-catalog.mjs';\r\nimport {LEVEL_XP,UNLOCKS} from './levels.mjs';\r\nimport {UNIT_BY_ID,UNIT_CATALOG,expeditionQuote,unitRoster} from './expedition-catalog.mjs';\r\nimport {PRODUCTION_RULES,advanceProduction,cancelProduction,enqueueProduction,ensureProduction} from './production.mjs';\r\nimport {normalizeRtsEconomy,advanceRtsEconomy,applyRtsEconomyAction,busyHarvestWorkers,harvestLayoutLocked,harvestRouteBlocked} from './rts-economy.mjs';\r\nimport {normalizeRtsTech,advanceRtsTech,applyRtsTechAction} from './rts-tech.mjs';\r\nimport {UNIT_BY_ID as WORLD_UNIT_BY_ID} from './unit-catalog.mjs';\r\nimport {restoreBlueprintEntitlements} from './blueprint-entitlements.mjs';\r\nimport {BLUEPRINT_POLICY_VERSION,buildingBlueprintStatus} from './blueprint-policy.mjs';\r\nconst own=(v,k)=>Object.hasOwn(v,k),bad=new Set(['__proto__','constructor','prototype']);\r\nconst integer=(v,min=0,max=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(v)&&v>=min&&v<=max;\r\nconst object=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst dayOf=now=>new Date(now).toISOString().slice(0,10);\r\nconst validDay=s=>typeof s==='string'&&/^\\d{4}-\\d{2}-\\d{2}$/.test(s)&&Number.isFinite(Date.parse(s+'T12:00:00Z'))&&new Date(s+'T12:00:00Z').toISOString().slice(0,10)===s;\r\nfunction safe(v,depth=0){if(depth>15)throw RangeError('Station data is too deeply nested.');if(typeof v==='number'&&!Number.isFinite(v))throw RangeError('Invalid station number.');if(v&&typeof v==='object')for(const k of Object.keys(v)){if(bad.has(k))throw RangeError('Unsafe station key.');safe(v[k],depth+1);}}\r\nconst clone=v=>{safe(v);return structuredClone(v);};\r\nconst fail=message=>{throw RangeError(message);};\r\nexport function freshBase(seed='guest',now=Date.now()){const b=newBase(seed,now);for(const x of Object.values(b.buildings))if(x.id!=='hq')x.level=0;return b;}\r\nexport function newBase(seed='guest',now=Date.now()){\r\n if(typeof seed!=='string'||seed.length>160)fail('Invalid station owner.');\r\n const materials=Object.fromEntries(FACTIONS.map(f=>[f.id,20]));materials.chest=150;\r\n const result={version:2,seed,faction:'chest',buildings:Object.fromEntries(BUILDINGS.map(b=>[b.id,{id:b.id,track:b.track,level:b.startsUnbuilt?0:1,x:b.x,y:b.y,job:null}])),materials,buildingStarterGrant:2,buildingStarterGrantFaction:'chest',buildingStarterFactionChosen:false,fuel:60,calm:60,launchCharge:0,rest:false,ships:{supportive:{tier:0,variant:0}},selectedShip:'supportive',rewardDays:{},sourceKeys:{},careDays:{},factionChangedAt:0,home:null,raidMappedAt:null,publicSiteId:null,outposts:{},wild:[],factionMilestones:[],inventory:{},rations:0,calmTokens:0,seedWorkers:3,workers:4,coachAccess:[...STARTER_COACH_IDS],units:{},training:{},productionQueues:{},productionSerial:0,expedition:null,expeditionSerial:0,expeditionHistory:[]};return normalizeRtsDefense(normalizeRtsTech(normalizeRtsEconomy(result)));\n}\r\n// R58 WS-G: account level from total building levels (levels.mjs LEVEL_XP). Outposts (gym/work bases) unlock at UNLOCKS levels.\r\nexport const OUTPOST_SLOTS=['gym','work'];\r\nexport const accountLevel=b=>{const xp=Object.values(b.buildings).reduce((s,x)=>s+(x.id==='landing'?0:x.level),0);let l=1;while(l<LEVEL_XP.length&&xp>=LEVEL_XP[l])l++;return l;};\r\nconst validPlace=p=>object(p)&&Number.isFinite(p.latitude)&&Math.abs(p.latitude)<=90&&Number.isFinite(p.longitude)&&Math.abs(p.longitude)<=180&&p.consent===true;\r\nconst validOutposts=o=>object(o)&&Object.entries(o).every(([slot,p])=>OUTPOST_SLOTS.includes(slot)&&object(p)&&p.slot===slot&&own(FACTION_BY_ID,p.faction)&&validPlace(p.place)&&object(p.buildings)&&Object.keys(p.buildings).length===BUILDINGS.length&&BUILDINGS.every(d=>{const x=p.buildings[d.id];return object(x)&&x.id===d.id&&x.track===d.track&&integer(x.level,0,d.id==='hq'?2:MAX_LEVEL)&&integer(x.x,0,24-d.w)&&integer(x.y,0,24-d.h)&&x.job===null;})&&Object.values(p.buildings).filter(x=>x.level>0).length<=8&&layoutValid(p.buildings));\r\n// Wild hunt ledger (additive; old saves load with []). Entries are written only by the server-side 'wild-catch' reducer case.\r\nexport const WILD_TIERS=Object.freeze(['white','green','blue','orange','gold','iridescent']);\r\nexport const WILD_LEDGER_CAP=200;\r\nconst validWildEntry=w=>object(w)&&typeof w.id==='string'&&/^[A-Za-z0-9:_-]{1,100}$/.test(w.id)&&own(WORLD_UNIT_BY_ID,w.unitId)&&WORLD_UNIT_BY_ID[w.unitId].family==='Four-legged'&&WILD_TIERS.includes(w.tier)&&typeof w.seed==='string'&&w.seed.length<=100&&integer(w.caughtAt)&&typeof w.zoneId==='string'&&w.zoneId.length<=40;\r\nconst validWild=a=>Array.isArray(a)&&a.length<=WILD_LEDGER_CAP&&a.every(validWildEntry)&&new Set(a.map(w=>w.id)).size===a.length;\r\n// Other players never receive outpost places.\r\nexport const publicOutposts=o=>Object.fromEntries(Object.entries(object(o)?o:{}).map(([k,{place,...rest}])=>[k,rest]));\r\nfunction overlap(a,b){const x=BUILDING_BY_ID[a.id],y=BUILDING_BY_ID[b.id];return a.x<b.x+y.w&&a.x+x.w>b.x&&a.y<b.y+y.h&&a.y+x.h>b.y;}\r\nfunction layoutValid(buildings){const rows=Object.values(buildings).filter(b=>b.level>0||b.job);for(let i=0;i<rows.length;i++){const b=rows[i],d=BUILDING_BY_ID[b.id];if(!d||!integer(b.x,0,24-d.w)||!integer(b.y,0,24-d.h))return false;for(let j=0;j<i;j++)if(overlap(b,rows[j]))return false;}return true;}\r\nfunction assertV2(b){\r\n Object.assign(b,normalizeRtsDefense(normalizeRtsTech(normalizeRtsEconomy(b))));\r\n if(b.blueprintEntitlements!==undefined)restoreBlueprintEntitlements(b.blueprintEntitlements,{accountId:b.seed,dataEpoch:b.blueprintEntitlements?.dataEpoch});\r\n if(b.blueprintPolicyVersion!==undefined&&b.blueprintPolicyVersion!==BLUEPRINT_POLICY_VERSION)fail('Unknown building blueprint policy.');\r\n safe(b);if(!object(b)||b.version!==2||typeof b.seed!=='string'||b.seed.length>160||!own(FACTION_BY_ID,b.faction)||!object(b.buildings)||Object.keys(b.buildings).length!==BUILDINGS.length||!object(b.materials))fail('Invalid saved station.');\r\n for(const f of FACTIONS)if(!integer(b.materials[f.id]))fail('Invalid station material.');\r\n for(const d of BUILDINGS){const x=b.buildings[d.id];if(!object(x)||x.id!==d.id||x.track!==d.track||!integer(x.level,d.id==='hq'?1:0,MAX_LEVEL)||!integer(x.x,0,24-d.w)||!integer(x.y,0,24-d.h))fail('Invalid station building.');const j=x.job;if(j!=null&&(!object(j)||!integer(j.targetLevel,1,MAX_LEVEL)||j.targetLevel!==x.level+1||!integer(j.startedAt)||!integer(j.dueAt,j.startedAt)||!integer(j.cost)||!own(FACTION_BY_ID,j.track)||!['occupied-worker','seeded-warp','consumed-seed'].includes(j.policy)))fail('Invalid construction job.');}\r\n if(!layoutValid(b.buildings)||!integer(b.fuel,0,100)||!integer(b.calm,0,100)||!integer(b.launchCharge,0,10)||typeof b.rest!=='boolean'||!integer(b.rations)||!integer(b.calmTokens)||!integer(b.seedWorkers,0,3)||!integer(b.workers,1,8)||!integer(b.factionChangedAt))fail('Invalid station state.');\r\n for(const k of ['rewardDays','sourceKeys','careDays','inventory','ships'])if(!object(b[k]))fail('Invalid station ledger.');\r\n if(!own(b.ships,'supportive')||!own(b.ships,b.selectedShip))fail('Invalid selected ship.');\r\n for(const [id,s] of Object.entries(b.ships))if(!SHIP_IDS.includes(id)||!object(s)||!integer(s.tier,0,4)||!integer(s.variant,0,3)||(id==='supportive'&&s.tier!==0))fail('Invalid station ship.');\r\n for(const [day,tracks] of Object.entries(b.rewardDays))if(!validDay(day)||!Array.isArray(tracks)||tracks.length>3||new Set(tracks).size!==tracks.length||tracks.some(t=>!own(FACTION_BY_ID,t)))fail('Invalid reward day.');\r\n if(b.raidMappedAt!==undefined&&b.raidMappedAt!==null&&!integer(b.raidMappedAt))fail('Invalid raid mapping time.');\n if(b.home!==null&&(!object(b.home)||!Number.isFinite(b.home.latitude)||Math.abs(b.home.latitude)>90||!Number.isFinite(b.home.longitude)||Math.abs(b.home.longitude)>180||b.home.consent!==true))fail('Invalid private home.');\n if(!Array.isArray(b.coachAccess)||new Set(b.coachAccess).size!==b.coachAccess.length||b.coachAccess.some(id=>!COACHES.some(c=>c.id===id)))fail('Invalid earned coach access.');\r\n if(!object(b.units)||!object(b.training)||!integer(b.expeditionSerial)||!Array.isArray(b.expeditionHistory)||b.expeditionHistory.length>12)fail('Invalid expedition ledger.');\r\n for(const [id,count]of Object.entries(b.units))if(!own(UNIT_BY_ID,id)||!UNIT_BY_ID[id].trainable||!integer(count,0,PRODUCTION_RULES.unitCountCap))fail('Invalid trained unit.');\r\n if(b.expedition!==null){const e=b.expedition;if(!object(e)||typeof e.id!=='string'||!/^expedition-[1-9][0-9]*$/.test(e.id)||!['prepared','launched','returning','returned'].includes(e.status)||!own(FACTION_BY_ID,e.factionId)||!own(b.ships,e.shipId)||!Array.isArray(e.unitIds)||e.unitIds.length<1||e.unitIds.length>8||new Set(e.unitIds).size!==e.unitIds.length||e.unitIds.some(id=>!own(UNIT_BY_ID,id))||typeof e.shared!=='boolean'||!integer(e.preparedAt)||!integer(e.materialCost)||!integer(e.fuelCost)||!integer(e.durationMs,1)||!integer(e.returnDurationMs,1))fail('Invalid expedition plan.');if(e.shared&&(typeof e.siteId!=='string'||!/^[A-Za-z0-9_-]{1,100}$/.test(e.siteId)))fail('Choose a public mission hub.');if(e.status!=='prepared'&&(!integer(e.launchedAt)||!integer(e.dueAt,e.launchedAt)))fail('Invalid expedition journey.');if(['returning','returned'].includes(e.status)&&!integer(e.returnDueAt,e.launchedAt))fail('Invalid expedition return.');}\r\n if(b.publicSiteId!==null&&(typeof b.publicSiteId!=='string'||!/^[a-zA-Z0-9_-]{1,100}$/.test(b.publicSiteId)))fail('Invalid public hub.');if(!validOutposts(b.outposts))fail('Invalid outpost.');if(!validWild(b.wild))fail('Invalid wild ledger.');if(!Array.isArray(b.factionMilestones)||b.factionMilestones.some(m=>!integer(m,5,LEVEL_XP.length)||m%5)||new Set(b.factionMilestones).size!==b.factionMilestones.length)fail('Invalid faction milestones.');return b;\r\n}\r\n// Adds missing unbuilt catalog sites at their default plot, else the first free spot (v1 migration and additive v2 catalog growth).\r\nfunction addUnbuilt(b,defs){const occupied=()=>Object.values(b.buildings);for(const d of defs){if(own(b.buildings,d.id))continue;let position={x:d.x,y:d.y};const clear=p=>!occupied().some(x=>{const def=BUILDING_BY_ID[x.id];return p.x<x.x+def.w&&p.x+d.w>x.x&&p.y<x.y+def.h&&p.y+d.h>x.y;});if(!clear(position)){let found=false;for(let y=0;y<=24-d.h&&!found;y++)for(let x=0;x<=24-d.w;x++)if(clear({x,y})){position={x,y};found=true;break;}}b.buildings[d.id]={id:d.id,track:d.track,level:0,...position,job:null};}}\r\n// R74: saves from before the Landing Zone keep their packs: any base with more than the HQ built gains a built Landing Zone; outposts gain an unbuilt one.\r\nfunction ensureLanding(b){\r\n const d=BUILDING_BY_ID.landing;\r\n if(object(b.buildings)&&!own(b.buildings,'landing')){const used=Object.values(b.buildings).some(x=>object(x)&&x.id!=='hq'&&(x.level>0||x.job));addUnbuilt(b,[d]);if(used)b.buildings.landing.level=1;}\r\n if(object(b.outposts))for(const p of Object.values(b.outposts))if(object(p)&&object(p.buildings)&&!own(p.buildings,'landing'))p.buildings.landing={id:'landing',track:null,level:0,x:d.x,y:d.y,job:null};\r\n}\r\nexport function normalizeBase(base){\r\n safe(base);if(!object(base)||![1,2].includes(base.version))fail('Invalid saved station.');const b=clone(base);if(b.version===2){b.coachAccess??=[...STARTER_COACH_IDS];b.outposts??={};b.wild??=[];b.factionMilestones??=[];if(object(b.buildings))ensureLanding(b);if(object(b.buildings))addUnbuilt(b,BUILDINGS.filter(v=>v.startsUnbuilt&&!own(b.buildings,v.id)&&!['production','expedition'].includes(v.kind)));if(object(b.buildings)&&accountLevel(b)<=1){if(b.buildingStarterGrant===true)b.materials[b.faction]+=70;else if(b.buildingStarterGrant!==2)b.materials[b.faction]+=130;}b.buildingStarterGrant=2;b.buildingStarterGrantFaction=own(FACTION_BY_ID,b.buildingStarterGrantFaction)?b.buildingStarterGrantFaction:b.faction;b.buildingStarterFactionChosen=b.buildingStarterFactionChosen===true;if(object(b.units)&&object(b.buildings))ensureProduction(b);return assertV2(b);}\r\n const original=BUILDINGS.filter(d=>!d.startsUnbuilt);if(!object(b.buildings)||Object.keys(b.buildings).length!==original.length||!object(b.materials))fail('Invalid legacy station.');b.legacyTiers={};b.migration={from:1,to:2,refundedJobs:[]};\r\n for(const d of original){const x=b.buildings[d.id];if(!object(x)||x.id!==d.id||x.track!==d.track||!integer(x.level,1,10))fail('Invalid legacy building.');const oldLevel=x.level;b.legacyTiers[d.id]=oldLevel;x.level=Math.ceil(oldLevel/2);if(x.job){const j=x.job;if(!integer(j.targetLevel,2,10)||j.targetLevel!==oldLevel+1||!integer(j.cost)||!own(FACTION_BY_ID,j.track)||!integer(j.startedAt)||!integer(j.dueAt,j.startedAt)||!['occupied-worker','seeded-warp','consumed-seed'].includes(j.policy))fail('Invalid legacy construction.');const target=Math.ceil(j.targetLevel/2);if(target<=x.level||x.level>=MAX_LEVEL){b.materials[j.track]+=j.cost;b.fuel=Math.min(100,b.fuel+5);if(j.policy==='consumed-seed')b.seedWorkers=Math.min(3,b.seedWorkers+1);x.job=null;b.migration.refundedJobs.push(d.id);}else j.targetLevel=target;}}\r\n ensureLanding(b);addUnbuilt(b,BUILDINGS.filter(v=>v.startsUnbuilt));\r\n b.version=2;b.coachAccess=[...STARTER_COACH_IDS];b.outposts={};b.wild=[];b.factionMilestones=[];b.units={};b.training={};b.productionQueues={};b.productionSerial=0;b.expedition=null;b.expeditionSerial=0;b.expeditionHistory=[];return assertV2(b);\r\n}\r\nexport function assertBase(base){return normalizeBase(base);}\r\nexport function advanceBase(base,now=Date.now()){\r\n if(!integer(now))fail('Invalid station clock.');const b=advanceRtsEconomy(normalizeBase(base),now);\r\n Object.assign(b,advanceRtsTech(b,now));\r\n for(const x of Object.values(b.buildings)){const j=x.job;if(j&&j.dueAt<=now){x.level=j.targetLevel;x.job=null;if(j.policy==='consumed-seed')b.seedWorkers=Math.min(3,b.seedWorkers+1);}}\r\n advanceProduction(b,now);\r\n const e=b.expedition;if(e?.status==='launched'&&e.dueAt<=now){e.status='returning';e.returnDueAt=e.dueAt+e.returnDurationMs;}if(e?.status==='returning'&&e.returnDueAt<=now){e.status='returned';e.returnedAt=e.returnDueAt;if(!b.expeditionHistory.some(v=>v.id===e.id))b.expeditionHistory.push({id:e.id,factionId:e.factionId,shipId:e.shipId,unitIds:[...e.unitIds],shared:e.shared,status:'returned',returnedAt:e.returnedAt,pack:`reward-pack:legendary:${e.id}:${e.launchedAt}`});b.expeditionHistory=b.expeditionHistory.slice(-12);}\r\n return assertV2(b);\r\n}\r\nexport function qualifyingDays(base,track){return Object.values(base.rewardDays).filter(v=>Array.isArray(v)&&v.includes(track)).length;}\r\nfunction reachableEntrances(buildings){const blocked=new Set();for(const b of Object.values(buildings).filter(b=>b.level>0||b.job)){const d=BUILDING_BY_ID[b.id];for(let y=b.y;y<b.y+d.h;y++)for(let x=b.x;x<b.x+d.w;x++)blocked.add(y*24+x);}const q=[0],seen=new Set(q);for(let i=0;i<q.length;i++){const n=q[i],x=n%24,y=Math.floor(n/24);for(const [a,c] of [[x-1,y],[x+1,y],[x,y-1],[x,y+1]]){const k=c*24+a;if(a>=0&&a<24&&c>=0&&c<24&&!blocked.has(k)&&!seen.has(k)){seen.add(k);q.push(k);}}}return Object.values(buildings).filter(b=>b.level>0||b.job).every(b=>{const d=BUILDING_BY_ID[b.id];for(let x=b.x;x<b.x+d.w;x++)if(seen.has((b.y-1)*24+x)||seen.has((b.y+d.h)*24+x))return true;for(let y=b.y;y<b.y+d.h;y++)if(seen.has(y*24+b.x-1)||seen.has(y*24+b.x+d.w))return true;return false;});}\r\nexport function constructionQuote(base,buildingId,requestedTrack){\r\n const b=normalizeBase(base),d=own(BUILDING_BY_ID,buildingId)?BUILDING_BY_ID[buildingId]:null,x=own(b.buildings,buildingId)?b.buildings[buildingId]:null;if(!d||!x)return {allowed:false,reason:'Choose a building.'};const track=d.track??requestedTrack??b.faction;if(!own(FACTION_BY_ID,track))return {allowed:false,reason:'Choose a workout material.'};const next=x.level+1,build=x.level===0,daysRequired=0,days=qualifyingDays(b,track),support=['care','observatory','habitat','hangar'].reduce((n,id)=>n+b.buildings[id].level,0)/4,softCapMultiplier=d.id==='hq'?1+Math.max(0,next-1-Math.floor(support))*.25:1;\r\n let prerequisites=build?(d.prerequisites??(['care','observatory','habitat','hangar'].includes(d.kind)?[{buildingId:'hq',level:1}]:[])):d.id==='hq'?[]:[{buildingId:'hq',level:Math.min(MAX_LEVEL,Math.max(1,next-(d.kind==='expedition'?-1:1)))}];if(build&&d.kind==='hall')prerequisites.push({buildingId:'landing',level:1});if(!build&&d.kind==='production')prerequisites.push({buildingId:`hall-${d.track}`,level:Math.min(3,next)});if(!build&&d.kind==='expedition')prerequisites.push({buildingId:`production-${d.track}`,level:Math.min(3,next)});prerequisites=prerequisites.map(p=>({...p,met:(b.buildings[p.buildingId]?.level??0)>=p.level}));\r\n const firstHall=build&&(d.kind==='landing'||d.kind==='hall'&&Object.values(b.buildings).every(v=>v.id==='hq'||v.id==='landing'||(v.level===0&&!v.job))),cost=firstHall?0:discountedCost(Math.ceil((build?(d.buildCost??(d.kind==='expedition'?40:30)):20*next)*softCapMultiplier),baseSystems(b).materialDiscountPct),durationMs=firstHall?0:Math.ceil(RULES.buildMinuteMs*(build?1:next-1)*softCapMultiplier),q={targetLevel:next,cost,fuelCost:5,durationMs,daysRequired:firstHall?0:daysRequired,qualifyingDays:days,prerequisites,softCapMultiplier,track,blueprint:buildingBlueprintStatus(b,d.id),allowed:true,reason:null};\r\n const fail=reason=>({...q,allowed:false,reason});if(!q.blueprint.available)return fail(`Recover the ${d.label} blueprint from a treasure chest.`);if(b.rest)return fail('Leave rest mode before construction.');if(x.job||b.rtsTech.jobs[d.id]||b.rtsTech.repairs[d.id]||(b.productionQueues[d.id]?.length??0)>0)return fail('This building is busy.');if(next>MAX_LEVEL)return fail('Maximum level reached.');const missing=prerequisites.find(p=>!p.met);if(missing)return fail(`Need ${BUILDING_BY_ID[missing.buildingId].label} level ${missing.level}.`);if(days<q.daysRequired)return fail(`Earn ${daysRequired} qualifying ${FACTION_BY_ID[track].label} days first.`);if(b.materials[track]<cost)return fail(`Need ${cost} ${FACTION_BY_ID[track].label} material.`);if(b.fuel<5)return fail('Fuel the station before construction.');if(FACTION_BY_ID[track].policy==='occupied-worker'&&busyHarvestWorkers(b).size>=b.workers)return fail('All builder crews are busy.');if(b.rtsTech.health[d.id]===0)return fail('Repair this structure before upgrading.');return q;\r\n}\r\nexport function bestTrack(base,buildingId){\r\n const b=normalizeBase(base),d=own(BUILDING_BY_ID,buildingId)?BUILDING_BY_ID[buildingId]:null;if(d?.track)return d.track;\r\n const ok=FACTIONS.filter(f=>constructionQuote(b,buildingId,f.id).allowed).map(f=>f.id);\r\n return ok.length?ok.sort((a,c)=>b.materials[c]-b.materials[a]||(a===b.faction)-(c===b.faction))[0]:b.faction;\r\n}\r\nexport function applyBaseAction(base,action,{now=Date.now(),sharedMissionAuthorized=false,wildServer=false}={}){\r\n if(!object(action)||typeof action.type!=='string')fail('Choose a station action.');safe(action);const b=advanceBase(base,now),x=own(b.buildings,action.buildingId)?b.buildings[action.buildingId]:null,d=own(BUILDING_BY_ID,action.buildingId)?BUILDING_BY_ID[action.buildingId]:null;\r\n switch(action.type){\r\n case 'gather':case 'stop-gather':Object.assign(b,applyRtsEconomyAction(b,action,{now}));break;\r\n case 'rts-research':case 'rts-addon':case 'rts-repair':case 'rts-cancel':Object.assign(b,applyRtsTechAction(b,action,{now}));break;\r\n case 'build':case 'upgrade':{if(!x)fail('Choose a building.');if(action.type==='build'&&harvestRouteBlocked(b,x.x,x.y,BUILDING_BY_ID[action.buildingId].w,BUILDING_BY_ID[action.buildingId].h))fail('A harvest run is walking that plot. Wait for it to return.');if(action.type==='build'&&x.level!==0)fail('This building is already built.');if(action.type==='upgrade'&&x.level===0)fail('Build this structure first.');const quote=constructionQuote(b,d.id,action.track);if(!quote.allowed)fail(quote.reason);const {track,cost,targetLevel:next,durationMs,softCapMultiplier}=quote,policy=FACTION_BY_ID[track].policy,reserved=busyHarvestWorkers(b),used=reserved.size;if(policy==='occupied-worker'&&used>=b.workers)fail('All builder crews are busy.');if(policy==='consumed-seed'&&b.seedWorkers<1)fail('A root worker is regrowing.');b.materials[track]-=cost;b.fuel-=5;if(cost>0)b.buildingStarterFactionChosen=true;if(policy==='consumed-seed')b.seedWorkers--;x.job={targetLevel:next,startedAt:now,dueAt:now+durationMs,cost,track,policy,softCapMultiplier,builderId:policy==='occupied-worker'?Array.from({length:b.workers},(_,i)=>`crew-${i+1}`).find(id=>!reserved.has(id)):null};if(durationMs<=0){x.level=next;x.job=null;if(policy==='consumed-seed')b.seedWorkers=Math.min(3,b.seedWorkers+1);} // free first hall is instant, no tick needed\r\n if(!layoutValid(b.buildings)||!reachableEntrances(b.buildings))fail('Choose a clear plot with a reachable entrance.');break;}\r\n case 'cancel':{if(!x?.job)fail('No construction to cancel.');const j=x.job;b.materials[j.track]+=Math.floor(j.cost*.9);if(j.policy==='consumed-seed')b.seedWorkers=Math.min(3,b.seedWorkers+1);x.job=null;break;}\r\n case 'move':{if(harvestLayoutLocked(b))fail('Wait for harvest runs to return before moving buildings.');if(!x||x.job)fail('Choose an idle building.');if(!integer(action.x,0,23)||!integer(action.y,0,23))fail('Choose a valid plot.');x.x=action.x;x.y=action.y;if(!layoutValid(b.buildings)||!reachableEntrances(b.buildings))fail('Leave every building a reachable entrance.');break;}\r\n case 'train-unit':enqueueProduction(b,{unitId:action.unitId,count:action.count??1},now);break; // production.mjs trainingAvailability = the one rule set (tech level, idle, 5 slots, supply, materials)\r\n case 'cancel-production':case 'cancel-training':cancelProduction(b,{queueId:action.queueId,unitId:action.unitId},now);break; // exact item by queueId; legacy {unitId} = head-most item of that unit\r\n case 'prepare-expedition':{if(['launched','returning'].includes(b.expedition?.status))fail('Wait for the current expedition to return.');const plan={factionId:action.factionId??b.faction,shipId:action.shipId??b.selectedShip,unitIds:action.unitIds,shared:action.shared===true,siteId:action.shared===true?action.siteId:null};if(plan.shared&&(typeof plan.siteId!=='string'||!/^[A-Za-z0-9_-]{1,100}$/.test(plan.siteId)))fail('Choose a public mission hub.');const q=expeditionQuote(b,plan);if(!q.allowed)fail(q.reason);b.expeditionSerial++;b.expedition={...plan,...q,id:`expedition-${b.expeditionSerial}`,status:'prepared',preparedAt:now,rewardAuthority:'server-only'};delete b.expedition.allowed;delete b.expedition.reason;break;}\r\n case 'launch-expedition':{const e=b.expedition;if(e?.status!=='prepared')fail('Prepare an expedition first.');if(e.unitIds.includes('myr5-worker')&&busyHarvestWorkers(b).has('crew-1'))fail('The builder is busy or on a harvest run. Wait for it to return.');if(e.shared&&!sharedMissionAuthorized)fail('Shared missions require fresh presence at the selected public hub.');const q=expeditionQuote(b,e);if(!q.allowed)fail(q.reason);b.materials[e.factionId]-=q.materialCost;b.fuel-=q.fuelCost;Object.assign(e,q,{status:'launched',launchedAt:now,dueAt:now+q.durationMs});delete e.allowed;delete e.reason;break;}\r\n case 'return-expedition':{const e=b.expedition;if(e?.status==='returned')break;if(e?.status!=='launched')fail('This expedition is already returning or has not launched.');e.status='returning';e.returnDueAt=now+e.returnDurationMs;break;}\r\n case 'cancel-expedition':if(b.expedition?.status!=='prepared')fail('Only an unlaunched plan can be cancelled.');b.expedition=null;break;\r\n case 'care':{const day=dayOf(now),flags=b.careDays[day]??={};if(action.kind==='greet'){if(flags.greet)fail('Your crew has been greeted today.');flags.greet=true;b.calm=Math.min(100,b.calm+10);}else if(action.kind==='fuel'){if(b.rations<1)fail('Log food to earn a station ration.');if(b.fuel>=100)fail('Fuel is full.');b.rations--;b.fuel=Math.min(100,b.fuel+22+baseSystems(b).rationFuelBonus);}else if(action.kind==='meditate'){if(b.calmTokens<1)fail('Complete meditation to earn calm.');if(b.calm>=100)fail('Calm is full.');b.calmTokens--;b.calm=Math.min(100,b.calm+22+baseSystems(b).meditationCalmBonus);}else fail('Unknown care action.');break;}\r\n case 'rest':if(typeof action.value!=='boolean')fail('Choose rest on or off.');b.rest=action.value;break;\r\n case 'faction':if(!own(FACTION_BY_ID,action.factionId))fail('Choose a workout faction.');if(action.factionId===b.faction)break;const milestone=Math.floor(accountLevel(b)/5)*5,free=milestone>=5&&!b.factionMilestones.includes(milestone);if(free)b.factionMilestones.push(milestone);else if(b.factionChangedAt&&now-b.factionChangedAt<7*86400000)fail('Faction changes unlock after seven days.');if(!b.buildingStarterFactionChosen&&b.buildingStarterGrant===2&&b.buildingStarterGrantFaction===b.faction&&b.materials[b.faction]>=150){b.materials[b.faction]-=130;b.materials[action.factionId]+=130;b.buildingStarterGrantFaction=action.factionId;b.buildingStarterFactionChosen=true;}b.faction=action.factionId;b.factionChangedAt=now;break;\r\n case 'home':if(action.consent!==true||typeof action.latitude!=='number'||typeof action.longitude!=='number'||!Number.isFinite(action.latitude)||!Number.isFinite(action.longitude)||Math.abs(action.latitude)>90||Math.abs(action.longitude)>180)fail('Confirm a valid private home location.');{const firstHome=b.home===null;b.home={latitude:action.latitude,longitude:action.longitude,consent:true};if(firstHome&&b.raidMappedAt==null)b.raidMappedAt=now;}break;\n case 'public-site':if(typeof action.siteId!=='string'||!/^[a-zA-Z0-9_-]{1,100}$/.test(action.siteId))fail('Choose a public hub.');b.publicSiteId=action.siteId;break;\r\n case 'outpost':{if(!OUTPOST_SLOTS.includes(action.slot))fail('Choose a base to build.');const need=UNLOCKS.find(u=>u.id===action.slot).level;if(accountLevel(b)<need)fail(`Reach level ${need} to unlock this base.`);if(b.outposts[action.slot])fail('This base already exists.');if(!own(FACTION_BY_ID,action.faction))fail('Choose a workout faction.');if(!validPlace(action.place))fail('Confirm a valid base location.');b.outposts[action.slot]={slot:action.slot,faction:action.faction,place:{latitude:action.place.latitude,longitude:action.place.longitude,consent:true},buildings:freshBase(b.seed,now).buildings};break;}\r\n case 'select-ship':if(!own(b.ships,action.shipId))fail('Earn this ship first.');b.selectedShip=action.shipId;break;\r\n case 'wild-catch':{if(wildServer!==true)fail('Unknown station action.');const e=action.entry;if(!validWildEntry(e))fail('Invalid wild catch.');if(b.wild.some(w=>w.id===e.id))break;if(b.wild.length>=WILD_LEDGER_CAP)fail('The wild roster is full.');b.wild.push({id:e.id,unitId:e.unitId,tier:e.tier,seed:e.seed,caughtAt:e.caughtAt,zoneId:e.zoneId});break;}\r\n case 'start-over':{if(action.confirm!=='START OVER')fail('Confirm Start over first.');const f=freshBase(b.seed,now);b.buildings=f.buildings;b.materials=f.materials;if(b.faction!=='chest'){b.materials.chest-=130;b.materials[b.faction]+=130;}b.buildingStarterGrant=2;b.buildingStarterGrantFaction=b.faction;b.buildingStarterFactionChosen=false;b.units={};b.training={};b.productionQueues={};delete b.rtsEconomy;delete b.rtsTech;delete b.rtsDefense;b.expedition=null;b.outposts={};b.wild=[];b.factionMilestones=[];b.launchCharge=0;b.seedWorkers=f.seedWorkers;break;}\r\n default:fail('Unknown station action.');}\r\n return assertBase(b);\r\n}\r\nexport function creditPersonalProgress(base,performance,{now=Date.now()}={}){\r\n const b=advanceBase(base,now);if(!object(performance))return b;if(Array.isArray(performance.coaches))b.coachAccess=[...new Set([...b.coachAccess,...performance.coaches.filter(id=>COACHES.some(c=>c.id===id))])];\r\n const sessions=object(performance.sessions)?Object.entries(performance.sessions):[];sessions.sort((a,c)=>String(a[1]?.day).localeCompare(String(c[1]?.day))||a[0].localeCompare(c[0]));\r\n for(const [id,s] of sessions){if(bad.has(id)||id.length>250||!object(s)||!validDay(s.day)||s.day>dayOf(now))continue;const key='workout:'+id,track=GROUP_TO_FACTION[s.group];if(own(b.sourceKeys,key)||!track||!(typeof s.xp==='number'&&s.xp>0))continue;const value=typeof s.value==='number'?s.value:0,active=typeof s.activeSeconds==='number'?s.activeSeconds:0,continuous=typeof s.continuous==='number'?s.continuous:0;const qualified=s.kind==='reps'?value>=5:s.kind==='hold'?continuous>=60||active>=60:value>=60||active>=60;if(!qualified)continue;const tracks=b.rewardDays[s.day]??=[];if(!tracks.includes(track)&&tracks.length<3){tracks.push(track);b.materials[track]+=10;}b.sourceKeys[key]=true;}\r\n const creditCare=(kind,day)=>{if(!validDay(day)||day>dayOf(now))return;const flags=b.careDays[day]??={};if(kind==='food'&&!flags.food){flags.food=true;b.rations++;}if(kind==='meditation'&&!flags.meditation){flags.meditation=true;b.calmTokens++;}};\r\n if(object(performance.activities))for(const a of Object.values(performance.activities))if(object(a))creditCare(a.kind,a.day);\r\n if(object(performance.days))for(const [day,a] of Object.entries(performance.days))if(object(a)){if(a.food)creditCare('food',day);if(a.meditation)creditCare('meditation',day);}\r\n let hash=0;for(const c of b.seed)hash=(Math.imul(hash,31)+c.charCodeAt(0))>>>0;for(const id of Array.isArray(performance.ships)?performance.ships:[])if(SHIP_IDS.includes(id)&&id!=='supportive'){const tier=hangarShipTier(b.buildings.hangar.level);if(own(b.ships,id))b.ships[id].tier=Math.max(b.ships[id].tier,tier);else b.ships[id]={tier,variant:(hash+SHIP_IDS.indexOf(id))%4};}\r\n // Guest-personal reconciliation NEVER grants expedition charge.\r\n return assertBase(b);\r\n}\r\n","modules/coach-world/terrain.mjs":"import {BUILDINGS,FACTIONS,FACTION_BY_ID} from './catalog.mjs';\r\nimport {occupancy,findPath} from './navigation.mjs';\r\nimport {BIOMES} from '../coach-world-rts/biomes.mjs';\r\nexport const TERRAIN_SIZE=24,TILE_PIXELS=32;\r\nconst composites=new WeakMap(),cache=new Map(),clamp=(v,a,b)=>Math.max(a,Math.min(b,v));\r\nfunction hash(x,y,seed=0){let n=Math.imul(x+seed,374761393)^Math.imul(y+17,668265263);n=Math.imul(n^(n>>>13),1274126177);return (n^(n>>>16))>>>0;}\r\nfunction seedOf(text){let n=0;for(const c of String(text))n=(Math.imul(n,31)+c.charCodeAt(0))>>>0;return n;}\r\nfunction tint(a,b,weight){const channels=[1,3,5].map(i=>Math.round(parseInt(a.slice(i,i+2),16)*(1-weight)+parseInt(b.slice(i,i+2),16)*weight));return '#'+channels.map(n=>n.toString(16).padStart(2,'0')).join('');}\r\n// Periodic domain-warped beds: broad plates, thin sediment lips and branching fault seams.\r\n// The variant warp fades at tile edges, so four variants meet without a bright tile border.\r\nfunction geology(x,y,variant,surface){\r\n const u=x/31,v=y/31,tau=Math.PI*2,edge=Math.sin(Math.PI*u)*Math.sin(Math.PI*v);\r\n const warp=Math.sin(tau*u)*Math.cos(tau*v)*surface.warp+edge*Math.sin(variant*1.7)*2;\r\n const bed=y*surface.beds/32+Math.sin(tau*u+surface.angle)*.55+warp*.15;\r\n const phase=bed-Math.floor(bed),plate=Math.sin(tau*u)*Math.sin(tau*v+surface.angle)+edge*Math.cos(variant*2.1)*.35;\r\n const fault=Math.abs(Math.sin(tau*u+Math.sin(tau*v)*.45+surface.angle));\r\n const branch=Math.abs(Math.sin(tau*(u+v)+surface.angle+warp*.12));\r\n if(edge>.12&&((fault<.065&&v>.2&&v<.85)||(branch<.055&&fault<.6)))return 7;\r\n if(edge>.25&&fault<.14&&phase>.55&&phase<.83)return 11;\r\n if(phase<.075)return 10;\r\n return plate>.22?8:plate<-.22?9:1;\r\n}\r\nexport function terrainRaster(faction='chest',kind='soil',variant=0){\r\n const f=FACTION_BY_ID[faction]||FACTIONS[0],index=FACTIONS.indexOf(f),pixels=new Uint8Array(32*32),soil=tint('#202b2d',f.dark,.42),palette=[tint(soil,'#858782',.09),soil,tint(soil,f.color,.18),tint(soil,f.color,.23),'#162e3a','#203c48','#35515a',tint(soil,'#0c181e',.25)];\r\n const surface=BIOMES[f.id].surface;\r\n palette.push(tint(soil,surface.mineral,.12),tint(soil,'#10191f',.18),tint(soil,surface.mineral,.22),tint(soil,surface.mineral,.34));\r\n for(let y=0;y<32;y++)for(let x=0;x<32;x++){const n=hash(x,y,variant+index*19);let color=kind==='soil'?geology(x,y,variant,surface):1;\r\n  if(kind==='water'){color=4;if((x+Math.floor(y/4)*5+variant*3)%23<8&&y%7<2)color=5;if((x+y*3+variant)%43===0)color=6;}\r\n  else if(kind==='path'||kind==='bridge'){color=kind==='bridge'?2:0;if(x%16===0||y%16===0)color=7;if((x+y+variant)%29===0)color=1;if(kind==='bridge'&&(x<3||x>28))color=3;}\r\n  else{if(n%83===0)color=0;const cluster=(x-(8+variant*4))**2+(y-(10+variant*3))**2<48;if(cluster&&index===0&&((x+y)%15===0||(x-y+32)%23===0))color=2;if(cluster&&index===1&&(((x-16)**2/2+(y-16)**2)%89<8))color=2;if(cluster&&index===2&&((x*3+y+variant)%23<2||((x-8)**2+(y-23)**2)%101<5))color=2;if(cluster&&index===3&&((y%12===0&&x%16<9)||(x%16===8&&y%12<8)))color=2;if(cluster&&index===4&&Math.abs(Math.hypot(x-16,y-16)%9)<.8)color=2;if(cluster&&index===5&&((x+Math.abs(y-16))%18<2))color=2;if(cluster&&index===6&&((x%16<2)||(y%16<2&&x%16<9)))color=2;if(cluster&&n%83===0)color=3;}\r\n  pixels[y*32+x]=color;\r\n }return {width:32,height:32,pixels,palette};\r\n}\r\nexport function terrainTileCanvas(faction,kind,variant){const key=`${faction}/${kind}/${variant}`;if(cache.has(key))return cache.get(key);const raster=terrainRaster(faction,kind,variant),canvas=document.createElement('canvas');canvas.width=canvas.height=32;const ctx=canvas.getContext('2d'),image=ctx.createImageData(32,32),colors=raster.palette.map(c=>[1,3,5].map(i=>parseInt(c.slice(i,i+2),16)));for(let i=0;i<1024;i++){image.data.set([...colors[raster.pixels[i]],255],i*4);}ctx.putImageData(image,0,0);cache.set(key,canvas);return canvas;}\r\nexport function createTerrain(base){\r\n const definitions=BUILDINGS.map(d=>({...d,...base.buildings?.[d.id]})),halls=definitions.filter(d=>d.kind==='hall'),seed=seedOf(base.seed),cells=[],blocked=new Set(),occupied=occupancy(base),roads=new Set();\r\n for(let y=0;y<24;y++)for(let x=0;x<24;x++){let nearest=halls[0],distance=Infinity;for(const hall of halls){const d=(x-hall.x-hall.w/2)**2+(y-hall.y-hall.h/2)**2;if(d<distance){distance=d;nearest=hall;}}const faction=nearest?.track||base.faction||'chest',water=(x<3&&y>17)||(x>20&&y<5),height=water?0:hash(Math.floor(x/4),Math.floor(y/4),seed)%3;cells.push({x,y,faction,kind:water?'water':'soil',height,variant:hash(x,y,seed)%4});}\r\n const hq=definitions.find(d=>d.id==='hq')||{x:10,y:9,w:4,h:4},origin={x:clamp(Math.floor(hq.x+hq.w/2),0,23),y:clamp(hq.y+hq.h,0,23)};\r\n for(const d of definitions){const entrances=[];for(let x=d.x;x<d.x+d.w;x++)entrances.push({x,y:d.y-1},{x,y:d.y+d.h});for(let y=d.y;y<d.y+d.h;y++)entrances.push({x:d.x-1,y},{x:d.x+d.w,y});const target=entrances.filter(p=>p.x>=0&&p.y>=0&&p.x<24&&p.y<24&&!occupied.has(p.y*24+p.x)).sort((a,b)=>Math.hypot(a.x-origin.x,a.y-origin.y)-Math.hypot(b.x-origin.x,b.y-origin.y))[0];if(target){const path=findPath(24,24,occupied,origin,target);for(const p of path||[])roads.add(p.y*24+p.x);}}\r\n for(const d of definitions)for(let y=Math.max(0,d.y-1);y<Math.min(24,d.y+d.h+1);y++)for(let x=Math.max(0,d.x-1);x<Math.min(24,d.x+d.w+1);x++){const cell=cells[y*24+x];cell.height=0;if(cell.kind==='water')cell.kind='soil';if(d.track)cell.faction=d.track;}\r\n for(const cell of cells){if(roads.has(cell.y*24+cell.x)){cell.kind=cell.kind==='water'?'bridge':'path';cell.height=0;}if(cell.kind==='water')blocked.add(cell.y*24+cell.x);}\r\n return {cells,blocked,roads,signature:`${base.seed}:${base.faction}:`+definitions.map(d=>`${d.id}:${d.x}:${d.y}:${d.level}`).join('|'),heightAt(x,y){return cells[clamp(Math.floor(y),0,23)*24+clamp(Math.floor(x),0,23)]?.height||0;}};\r\n}\r\n// Transitional sediment stays inside soil; road decks and water silhouettes remain untouched.\r\nfunction paintLandform(ctx,cell,x,y,unit,soil){\r\n if(cell.kind!=='soil')return;\r\n const mineral=(BIOMES[cell.faction]||BIOMES.chest).surface.mineral;\r\n // Shared world coordinates carry broad sediment shelves across the four cached tile variants.\r\n // Only the tint crosses faction boundaries; the authoritative terrain grid stays unchanged.\r\n ctx.globalAlpha=.18;\r\n for(let p=0;p<32;p+=8)for(let q=0;q<32;q+=8){\r\n  const wx=cell.x*32+p,wz=cell.y*32+q;\r\n  const bed=Math.sin(wz*.048+Math.sin(wx*.021)*1.6),basin=Math.sin(wx*.013+wz*.009);\r\n  if(Math.abs(bed)<.6)continue;\r\n  ctx.fillStyle=bed>0?tint(soil,mineral,.3+basin*.08):'#111a20';\r\n  ctx.fillRect(x+p*unit/32,y+q*unit/32,unit/4,unit/4);\r\n }\r\n ctx.globalAlpha=1;\r\n}\r\nfunction paintTransitions(ctx,terrain,cell,x,y,unit,soils){\r\n if(cell.kind!=='soil')return;\r\n const soil=soils[cell.faction]||soils.chest;\r\n for(const [dx,dy] of [[-1,0],[1,0],[0,-1],[0,1]]){\r\n  const nx=cell.x+dx,ny=cell.y+dy;if(nx<0||ny<0||nx>=24||ny>=24)continue;\r\n  const other=terrain.cells[ny*24+nx];if(!other)continue;\r\n  const shore=other.kind==='water',route=other.kind==='path'||other.kind==='bridge';\r\n  if(!shore&&!route&&other.faction===cell.faction)continue;\r\n  const target=shore?'#35515a':route?'#858782':(soils[other.faction]||soils.chest);\r\n  for(let p=0;p<32;p+=4){\r\n   const width=1+hash(p,cell.x+cell.y*24,dx+dy*7)%3;\r\n   for(let d=0;d<width;d++){\r\n    ctx.fillStyle=tint(soil,target,(shore?.38:route?.16:.65)*(1-d/(width+1)));\r\n    const px=dx<0?d:dx>0?31-d:p,py=dy<0?d:dy>0?31-d:p;\r\n    ctx.fillRect(x+px*unit/32,y+py*unit/32,dx?unit/32:Math.ceil(unit/16),dy?unit/32:Math.ceil(unit/16));\r\n   }\r\n  }\r\n }\r\n}\r\nexport function terrainComposite(terrain,unit=32){\r\n const previous=composites.get(terrain);if(previous?.unit===unit)return previous.canvas;\r\n const canvas=document.createElement('canvas');canvas.width=Math.ceil(24*unit);canvas.height=Math.ceil(24*unit)+8;\r\n const ctx=canvas.getContext('2d');ctx.imageSmoothingEnabled=false;\r\n const soils=Object.fromEntries(FACTIONS.map(f=>[f.id,tint('#202b2d',f.dark,.42)]));\r\n for(const cell of terrain.cells){\r\n  const x=Math.round(cell.x*unit),y=Math.round(cell.y*unit-cell.height*2)+4,size=Math.ceil(unit);\r\n  if(cell.height){ctx.fillStyle='#122025';ctx.fillRect(x,y+size,size,cell.height*2+1);}\r\n  ctx.drawImage(terrainTileCanvas(cell.faction,cell.kind,cell.variant),x,y,size,size);\r\n  paintLandform(ctx,cell,x,y,unit,soils[cell.faction]||soils.chest);\r\n  paintTransitions(ctx,terrain,cell,x,y,unit,soils);\r\n }\r\n composites.set(terrain,{unit,canvas});return canvas;\r\n}\r\nexport function drawTerrain(ctx,terrain,{unit,ox,oy}){ctx.imageSmoothingEnabled=false;ctx.drawImage(terrainComposite(terrain,unit),Math.round(ox),Math.round(oy)-4);}\r\n","modules/coach-world/touchselection.mjs":"import {findPath,occupancy} from './navigation.mjs';\r\nexport function unitsInRectangle(units,a,b,pad=8){const left=Math.min(a.x,b.x)-pad,right=Math.max(a.x,b.x)+pad,top=Math.min(a.y,b.y)-pad,bottom=Math.max(a.y,b.y)+pad;return units.filter(u=>u.x>=left&&u.x<=right&&u.y>=top&&u.y<=bottom).map(u=>u.id);}\r\nexport function createTouchGesture({holdMs=430,threshold=7,setTimer=setTimeout,clearTimer=clearTimeout,onHold=()=>{}}={}){\r\n let state=null,timer=null;const stop=()=>{if(timer!=null)clearTimer(timer);timer=null;};\r\n return {get state(){return state;},down(point,{select=false}={}){stop();state={start:{...point},point:{...point},mode:select?'select':'pending',moved:false};if(!select)timer=setTimer(()=>{if(state?.mode==='pending'){state.mode='select';onHold();}},holdMs);return state;},move(point){if(!state)return null;const previous=state.point;state.point={...point};if(Math.hypot(point.x-state.start.x,point.y-state.start.y)>threshold){state.moved=true;if(state.mode==='pending'){state.mode='pan';stop();}}return {mode:state.mode,dx:point.x-previous.x,dy:point.y-previous.y};},pinch(){stop();if(state)state.mode='pinch';},up(){stop();const result=state;state=null;return result;},cancel(){stop();state=null;},dispose(){stop();state=null;}};\r\n}\r\nexport function rallyUnits(units,ids,target,base,extraBlocked=new Set()){\r\n const blocked=occupancy(base);for(const key of extraBlocked)blocked.add(key);const candidates=[];for(let radius=0;radius<5;radius++)for(let y=target.y-radius;y<=target.y+radius;y++)for(let x=target.x-radius;x<=target.x+radius;x++)if(x>=0&&x<24&&y>=0&&y<24&&!blocked.has(y*24+x)&&!candidates.some(p=>p.x===x&&p.y===y))candidates.push({x,y});let assigned=0;\r\n for(const unit of units){if(unit.away||!ids.has(unit.id)||unit.state==='building'||Object.values(base.buildings).some(b=>b.job?.policy==='occupied-worker'&&b.job.builderId===`crew-${unit.id+1}`))continue;const origin={x:Math.floor(unit.x),y:Math.floor(unit.y)};for(let i=0;i<candidates.length;i++){const path=findPath(24,24,blocked,origin,candidates[i]);if(path){unit.path=path;unit.index=0;unit.rallyTarget=candidates[i];unit.state=path.length?'rally':'holding';unit.rally=true;candidates.splice(i,1);assigned++;break;}}}return assigned;\r\n}\r\nexport function stepRalliedUnits(units,dt,rest=false){for(const unit of units){if(!unit.rally||rest)continue;const p=unit.path?.[unit.index||0];if(!p){unit.state='holding';continue;}const dx=p.x-unit.x,dy=p.y-unit.y,d=Math.hypot(dx,dy),step=Math.min(.1,Math.max(0,dt))*1.7;if(d<=step){unit.x=p.x;unit.y=p.y;unit.index=(unit.index||0)+1;}else{unit.x+=dx/d*step;unit.y+=dy/d*step;unit.state='rally';unit.facing=dx<0?'left':'right';}unit.phase=(unit.phase||0)+step*7;}}\r\n","modules/coach-world/unit-catalog.mjs":"// Verified actual atlas crops. Eight active quadruped appearances supply fourteen\r\n// World role definitions through explicit appearance reuse + faction dye. This\r\n// catalog grants no coach/cosmetic ownership and does not enforce production gates.\r\nconst freeze=value=>{if(value&&typeof value==='object'){for(const v of Object.values(value))freeze(v);Object.freeze(value);}return value;};\r\nexport const UNIT_SECTIONS=freeze(['chest','quads','glutes','arms-shoulders','yoga','martial-arts','cardio']);\r\nexport const FOUR_LEGGED_SOURCES=freeze([{\"sourceCoachId\":\"roster/18-quad-all--dragon_creature_3d_model\",\"sourceLabel\":\"H0F-Lung\",\"sheet\":\"pets\",\"frame\":[0,0,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--fantasy_creature_3d_model1\",\"sourceLabel\":\"Max Ohm-Strom Breath\",\"sheet\":\"pets\",\"frame\":[48,0,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--fantasy_creature_3d_model3\",\"sourceLabel\":\"Shiva Rea-spatory\",\"sheet\":\"pets\",\"frame\":[96,0,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--four-legged_robot_3d_model\",\"sourceLabel\":\"Tara-5tile5-Standby\",\"sheet\":\"pets\",\"frame\":[144,0,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--quadruped_robot_3d_model1\",\"sourceLabel\":\"Werner Wind0wn\",\"sheet\":\"pets\",\"frame\":[192,0,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--quadruped_robot_3d_model\",\"sourceLabel\":\"Yee Holo-Om\",\"sheet\":\"pets\",\"frame\":[0,36,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--robotic_dog_3d_model\",\"sourceLabel\":\"Budig-ital Breath\",\"sheet\":\"pets\",\"frame\":[48,36,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"},{\"sourceCoachId\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"sourceLabel\":\"Priest-L33 Squat\",\"sheet\":\"pets\",\"frame\":[96,36,48,36],\"atlasPath\":\"/pod/gala-coaches/pets.png\",\"family\":\"Four-legged\"}]);\r\nexport const BASIC_WORKER=freeze({id:'worker:myr5',label:'MYR5 basic worker',role:'worker',section:null,variant:0,unlockPolicy:'baseline',productionPath:['hq'],...{\"sourceCoachId\":\"myr5\",\"sourceLabel\":\"MYR5 Jacked LaLAN\",\"sheet\":\"bodies\",\"frame\":[0,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"MYR5\"}});\r\nconst roleLabels={chest:['Crest scout','Plate carrier'],quads:['Trail scout','Load carrier'],glutes:['Pod scout','Pearl carrier'],'arms-shoulders':['Relay scout','Joint carrier'],yoga:['Wisp scout','Calm carrier'],'martial-arts':['Border scout','Shield carrier'],cardio:['Pace scout','Cell carrier']};\r\nexport const SECTION_COMPANIONS=freeze(Object.fromEntries(UNIT_SECTIONS.map((section,index)=>[section,['scout','carrier'].map((role,variant)=>{const source=FOUR_LEGGED_SOURCES[(index*2+variant)%FOUR_LEGGED_SOURCES.length];return {...source,id:'companion:'+section+':'+role,label:roleLabels[section][variant],role,section,variant,dyeFaction:section,appearanceReused:true,rtsSheet:'bodies-pets',unlockPolicy:'world-baseline-role',productionPath:['hall-'+section,'production','expedition']};})])));\r\nexport const COACH_UNITS=freeze([{\"sourceCoachId\":\"roster/01-seed-pearo--3d_character_model\",\"sourceLabel\":\"McKe-O2 Nose-Cone\",\"sheet\":\"bodies\",\"frame\":[64,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Seed · Pearo\",\"section\":null,\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\",\"sourceLabel\":\"Wern-Axis Flow\",\"sheet\":\"bodies\",\"frame\":[128,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Seed · Pearo\",\"section\":\"yoga\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\",\"sourceLabel\":\"Phelpeller Pulse\",\"sheet\":\"bodies\",\"frame\":[192,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Seed · Pearo\",\"section\":\"cardio\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/02-taper-tallstalk--3d_humanoid_figure\",\"sourceLabel\":\"Hane-Yoke Lunge\",\"sheet\":\"bodies\",\"frame\":[256,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Taper · Tallstalk\",\"section\":\"quads\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"sourceLabel\":\"Arnoid Press\",\"sheet\":\"bodies\",\"frame\":[320,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Taper · Tallstalk\",\"section\":\"arms-shoulders\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"sourceLabel\":\"Oli-Volt Lunge\",\"sheet\":\"bodies\",\"frame\":[384,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Taper · Tallstalk\",\"section\":\"quads\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"sourceLabel\":\"Robo-ertson Bridge\",\"sheet\":\"bodies\",\"frame\":[448,0,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"glutes\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--circle_center\",\"sourceLabel\":\"Con-Terra Coil-Bridge\",\"sheet\":\"bodies\",\"frame\":[0,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"glutes\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"sourceLabel\":\"Fon-Dynamo Footwork\",\"sheet\":\"bodies\",\"frame\":[64,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"cardio\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--robot_3d_model\",\"sourceLabel\":\"Feli-X Footdrive\",\"sheet\":\"bodies\",\"frame\":[128,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"cardio\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"sourceLabel\":\"Gyrvanium Glute-Drive\",\"sheet\":\"bodies\",\"frame\":[192,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"glutes\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"sourceLabel\":\"Trac-ION Hip-Thrust\",\"sheet\":\"bodies\",\"frame\":[256,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Pearl · Orb\",\"section\":\"glutes\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/04-crest-wedge--robot_creature_3d_model\",\"sourceLabel\":\"L0U Ferri-pecctor\",\"sheet\":\"bodies\",\"frame\":[320,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Crest · Wedge\",\"section\":\"chest\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/04-crest-wedge--stylized_3d_character\",\"sourceLabel\":\"Gr33ne Grid-Press\",\"sheet\":\"bodies\",\"frame\":[384,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Crest · Wedge\",\"section\":\"chest\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"sourceLabel\":\"C0N0R McG-Gear\",\"sheet\":\"bodies\",\"frame\":[448,96,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Slope · Bobble\",\"section\":\"martial-arts\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"sourceLabel\":\"Oliva-C Pec\",\"sheet\":\"bodies\",\"frame\":[0,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Ridge · Triad\",\"section\":\"chest\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/06-ridge-triad--robot_3d_model1\",\"sourceLabel\":\"M0 Farah Fastlane\",\"sheet\":\"bodies\",\"frame\":[64,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Ridge · Triad\",\"section\":\"cardio\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/07-bulb-sphereling--cute_robot_3d_model\",\"sourceLabel\":\"Billy Blanx Beam\",\"sheet\":\"bodies\",\"frame\":[128,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Bulb · Sphereling\",\"section\":\"cardio\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"sourceLabel\":\"B0LT Sprint-Drive\",\"sheet\":\"bodies\",\"frame\":[192,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Bulb · Sphereling\",\"section\":\"cardio\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/08-shard-asym--geometric_robot_3d_model\",\"sourceLabel\":\"Pec-n0ld Reactor\",\"sheet\":\"bodies\",\"frame\":[256,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shard · Asym\",\"section\":\"chest\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/08-shard-asym--robot_3d_model4\",\"sourceLabel\":\"S3RG Oliv-Armature\",\"sheet\":\"bodies\",\"frame\":[320,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shard · Asym\",\"section\":\"arms-shoulders\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"sourceLabel\":\"Waters0nic\",\"sheet\":\"bodies\",\"frame\":[384,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shard · Asym\",\"section\":\"martial-arts\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"sourceLabel\":\"Yee-lastic Flow\",\"sheet\":\"bodies\",\"frame\":[448,192,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shard · Asym\",\"section\":\"yoga\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/09-monolith-tanka--3d_robot_model\",\"sourceLabel\":\"chris BenchSTEAD\",\"sheet\":\"bodies\",\"frame\":[0,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Monolith · Tanka\",\"section\":\"chest\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"sourceLabel\":\"Bruce L3300\",\"sheet\":\"bodies\",\"frame\":[64,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Monolith · Tanka\",\"section\":\"martial-arts\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/09-monolith-tanka--mini_robot_3d_model\",\"sourceLabel\":\"Machi-Droid-a\",\"sheet\":\"bodies\",\"frame\":[128,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Monolith · Tanka\",\"section\":\"martial-arts\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/09-monolith-tanka--robot_3d_model3\",\"sourceLabel\":\"Israel Ade-SYNC-ya\",\"sheet\":\"bodies\",\"frame\":[192,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Monolith · Tanka\",\"section\":\"martial-arts\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\",\"sourceLabel\":\"Cavalier-EX Chest\",\"sheet\":\"bodies\",\"frame\":[256,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Monolith · Tanka\",\"section\":\"chest\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/10-petal-wisp--stylized_creature_3d_model\",\"sourceLabel\":\"Iyeng-Arc Inversion\",\"sheet\":\"bodies\",\"frame\":[320,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Petal · Wisp\",\"section\":\"yoga\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/10-petal-wisp--ghost_character_3d_model\",\"sourceLabel\":\"Warri0r-ner Drive\",\"sheet\":\"bodies\",\"frame\":[384,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Petal · Wisp\",\"section\":\"yoga\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"sourceLabel\":\"Adriene A-Sana Mk II\",\"sheet\":\"bodies\",\"frame\":[448,288,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Petal · Wisp\",\"section\":\"yoga\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"sourceLabel\":\"Anderson Sil-VOLT\",\"sheet\":\"bodies\",\"frame\":[0,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Anvil · Cask\",\"section\":\"martial-arts\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"sourceLabel\":\"H0F Hold-Circuit\",\"sheet\":\"bodies\",\"frame\":[64,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Seedpod · Snailslug\",\"section\":null,\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"sourceLabel\":\"Pilat-OS Calm-Circuit\",\"sheet\":\"bodies\",\"frame\":[128,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Seedpod · Snailslug\",\"section\":null,\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/13-fan-split--stylized_3d_character2\",\"sourceLabel\":\"Quad-Cutler LEGS THO\",\"sheet\":\"bodies\",\"frame\":[192,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Fan · Split\",\"section\":\"quads\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/13-fan-split--stylized_toy_3d_model\",\"sourceLabel\":\"BlankSaber Kick\",\"sheet\":\"bodies\",\"frame\":[256,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Fan · Split\",\"section\":\"martial-arts\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/14-chisel-spire--cone_head_3d_model\",\"sourceLabel\":\"LiDAR Jett-Jab\",\"sheet\":\"bodies\",\"frame\":[320,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Chisel · Spire\",\"section\":\"martial-arts\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"sourceLabel\":\"Rea-Spire Relay\",\"sheet\":\"bodies\",\"frame\":[384,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Chisel · Spire\",\"section\":null,\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/15-orbital-coili--robot_character_3d_model\",\"sourceLabel\":\"t-OHM Platzma\",\"sheet\":\"bodies\",\"frame\":[448,384,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Orbital · Coili\",\"section\":\"quads\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"sourceLabel\":\"Franx Pec-Zane\",\"sheet\":\"bodies\",\"frame\":[0,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Spade · Arch\",\"section\":\"chest\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"sourceLabel\":\"Cutlerator Press\",\"sheet\":\"bodies\",\"frame\":[64,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Spade · Arch\",\"section\":\"chest\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/17-shellcap-manyarm--3d_character_figurine\",\"sourceLabel\":\"Delt-alinn Bailey\",\"sheet\":\"bodies\",\"frame\":[128,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shellcap · Manyarm\",\"section\":\"arms-shoulders\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"sourceLabel\":\"KAI-nematic Raise\",\"sheet\":\"bodies\",\"frame\":[192,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shellcap · Manyarm\",\"section\":\"arms-shoulders\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"sourceLabel\":\"Wheel-er Servo-Shrug\",\"sheet\":\"bodies\",\"frame\":[256,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Shellcap · Manyarm\",\"section\":\"arms-shoulders\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/19-genie-multi--fantasy_creature_3d_model\",\"sourceLabel\":\"Mat-Gregor Matrix\",\"sheet\":\"bodies\",\"frame\":[320,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Genie · Multi\",\"section\":\"yoga\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"sourceLabel\":\"Ferri-Delt L0U\",\"sheet\":\"bodies\",\"frame\":[384,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Genie · Multi\",\"section\":\"arms-shoulders\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"sourceLabel\":\"Frank Delt-Zan3\",\"sheet\":\"bodies\",\"frame\":[448,480,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Genie · Multi\",\"section\":\"arms-shoulders\",\"difficulty\":\"medium\"},{\"sourceCoachId\":\"roster/19-genie-multi--stylized_octopus_3d_model\",\"sourceLabel\":\"Yee Gyro-Yogi\",\"sheet\":\"bodies\",\"frame\":[0,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Genie · Multi\",\"section\":\"yoga\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/20-lume--robotic_figure_3d_model\",\"sourceLabel\":\"Pre-Font-AI-ne Pace\",\"sheet\":\"bodies\",\"frame\":[64,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Lume\",\"section\":\"cardio\",\"difficulty\":\"expert\"},{\"sourceCoachId\":\"roster/22-curve--stylized_alien_3d_model\",\"sourceLabel\":\"Strom Flex Current\",\"sheet\":\"bodies\",\"frame\":[192,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Curve\",\"section\":\"yoga\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"sourceLabel\":\"Kai Gr33ne Lunge\",\"sheet\":\"bodies\",\"frame\":[256,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Curve\",\"section\":\"quads\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/23-blob-texture-bodies--blob_creature_3d_model\",\"sourceLabel\":\"Blob 1 · H0F Hold-Drive\",\"sheet\":\"bodies\",\"frame\":[320,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Blob\",\"section\":null,\"difficulty\":\"starter\"},{\"sourceCoachId\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"sourceLabel\":\"Chlo-Turbo Thrust\",\"sheet\":\"bodies\",\"frame\":[384,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Blob\",\"section\":\"glutes\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"sourceLabel\":\"Con-Thrust-Era\",\"sheet\":\"bodies\",\"frame\":[448,576,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Blob\",\"section\":\"glutes\",\"difficulty\":\"easy\"},{\"sourceCoachId\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"sourceLabel\":\"Ree-volts Raise\",\"sheet\":\"bodies\",\"frame\":[0,672,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Blob\",\"section\":\"arms-shoulders\",\"difficulty\":\"hard\"},{\"sourceCoachId\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"sourceLabel\":\"Tingularity Thrust\",\"sheet\":\"bodies\",\"frame\":[64,672,64,96],\"atlasPath\":\"/pod/gala-coaches/bodies.png\",\"family\":\"Blob\",\"section\":\"glutes\",\"difficulty\":\"medium\"}].map(source=>({...source,id:'coach:'+source.sourceCoachId,label:source.sourceLabel,role:source.difficulty==='expert'?'boss':'grunt',variant:0,rtsSheet:'bodies-'+(source.section||'neutral'),unlockPolicy:'earned-coach',productionPath:source.section?['hall-'+source.section,'production','expedition']:['production','expedition']})));\r\n// R81: hangar ships are air troops (count = hangarShipTier, no training queue). Kept out of UNIT_CATALOG: they are not crew and never walk.\r\nexport const HANGAR_SHIP=freeze({id:'ship:hangar',label:'Hangar ship',role:'ship',air:true,section:null,simRole:'a',unlockPolicy:'hangar-level',productionPath:['hangar']});\r\nexport const UNIT_CATALOG=freeze([BASIC_WORKER,...UNIT_SECTIONS.flatMap(section=>SECTION_COMPANIONS[section]),...COACH_UNITS]);\r\nexport const UNIT_BY_ID=freeze(Object.fromEntries(UNIT_CATALOG.map(unit=>[unit.id,unit])));\r\nexport function unitDefinition(id){return typeof id==='string'&&Object.hasOwn(UNIT_BY_ID,id)?UNIT_BY_ID[id]:null;}\r\nexport function initialWorldUnits(section='chest'){const chosen=UNIT_SECTIONS.includes(section)?section:'chest';return [BASIC_WORKER,...SECTION_COMPANIONS[chosen]];}\r\nexport function unitsForSection(section){return UNIT_SECTIONS.includes(section)?UNIT_CATALOG.filter(unit=>unit.section===section||unit.id===BASIC_WORKER.id):[];}\r\nexport function isWorldUnitAvailable(unitOrId,earnedCoachIds=[]){const unit=typeof unitOrId==='string'?unitDefinition(unitOrId):unitDefinition(unitOrId?.id);if(!unit)return false;return unit.unlockPolicy!=='earned-coach'||new Set(earnedCoachIds).has(unit.sourceCoachId);}\r\n","modules/coach-world/vendor/dfo-grass-pixel-art.mjs":"// Copied verbatim from DFO Online src/render/grassPixelArt.js @ e3054735 (MIT, (c) 2026 Lattymoy; see coach-world/vendor/dfo-LICENSE.txt).\r\n// V7 (Daggerfall look): the near grass tufts' sprite sheet. Pure functions, no imports.\r\n// @ts-check\r\n// ═══════════════════════════════════════════════════════════════════\r\n// GRASS-PX (2026-09-21, Mac: \"with the grass model, is there a way we\r\n// can turn the grass into a pixel art design ... Let's see how detailed\r\n// you can be\").\r\n//\r\n// THE TUFT IS A SPRITE, AS EVERY OTHER LIVING THING IN THIS WORLD IS.\r\n// Daggerfall draws its trees, its people and its monsters as hand-set\r\n// pixel flats, and the lab's grass - a million smooth, tapered,\r\n// gradient-lit blades - is the one thing outdoors that does not look\r\n// drawn by the same hand. The pixel style keeps the whole of the lab's\r\n// field (the placer, the packed lanes, the cells, the host-paid fade,\r\n// the wind, the time of day) and changes only what a blade LOOKS like:\r\n// the quad carries a tuft sprite instead of a tapered blade, the sprite\r\n// is four flat tones instead of a gradient, the distance fade is an\r\n// ordered dither instead of a transparency, and the lit colour is\r\n// snapped to a short ramp so the day's light reads as bands, the way a\r\n// paletted screen banded it. The SWAY is the lab's, smooth, in both\r\n// styles (GRASS-PX3: it was stepped at first, and Mac missed the way\r\n// the grass flowed with the wind - the one thing in the field that\r\n// should never look drawn frame by frame).\r\n//\r\n// THE SHEET IS BUILT HERE, AT BOOT, FROM A SEED - not loaded, not drawn\r\n// by hand in a file. Eight tufts, each eight texels wide and sixteen\r\n// tall (GRASS-PX4; sixteen by thirty-two until 2026-09-22, and that\r\n// size still builds through `buildTuftSheet({ w: 16, h: 32 })`), each\r\n// three to five blades laid as one-texel stalks\r\n// that curve with their own lean the way the lab's stalks bend (the\r\n// sideways travel goes as height squared). Every texel is either a\r\n// blade or nothing: the alpha is 0 or 255 and never between, because a\r\n// soft edge is the one thing a pixel sprite must not have. The texel\r\n// carries three more things the fragment stage reads: WHICH TONE it is\r\n// (root, mid, tip, or the one tip highlight that catches the sun's rim),\r\n// HOW FAR UP ITS OWN BLADE it sits (so the lab's root-to-tip lighting\r\n// law runs along the drawn stalk rather than up the quad), and WHICH\r\n// BLADE of the tuft it belongs to (so the blades of one tuft are not\r\n// all the same shade).\r\n//\r\n// THE MIP CHAIN IS BUILT HERE TOO, AND IT IS NOT AN AVERAGE. A sprite\r\n// that is a quarter blade texels and three-quarters air averages to an\r\n// alpha under a half at the first mip level, and an alpha test then\r\n// throws the whole tuft away: the field would vanish a few cells out.\r\n// Each level takes the MAX alpha of its block and the tone of the texel\r\n// that carried it, so a tuft far off is a solid pixel of the right\r\n// colour rather than nothing. The GPU picks the level; the chain is\r\n// what makes the pick safe.\r\n//\r\n// Everything below is pure and deterministic: the pins compare two\r\n// builds byte for byte, and a sheet that changed by a texel is a pin\r\n// that failed.\r\n\r\n/** how many tufts the sheet holds, side by side */\r\nexport const PX_VARIANTS = 8;\r\n/** one tuft's texel size: eight across, sixteen up - the quad is half\r\n *  its drawn height wide (labGrass.js GRASSPX_VS_EDITS), so a texel is\r\n *  square on every blade, buried or not.\r\n *\r\n *  GRASS-PX4 (2026-09-22, Mac: \"have grass have larger pixels\"): it\r\n *  was sixteen by thirty-two. A texel of that sheet on a blade half a\r\n *  metre tall is a centimetre and a half - three screen pixels at a\r\n *  few metres, under one past ten - while every character in this\r\n *  world is pixelized to CHAR_PIXEL (9) blocks and the Morrowind arm\r\n *  to MW_ARM_PIXEL (3). The tuft was drawn by a finer hand than the\r\n *  people standing in it. Halving the sheet doubles the texel on the\r\n *  same quad; nothing about the field's placement, count, sway or\r\n *  light moves. EVERY LAW BELOW IS WRITTEN OFF THESE TWO NUMBERS, not\r\n *  off literals that happened to equal them at 16x32: the edge\r\n *  margin, the shortest blade, the lean, the highlight's threshold -\r\n *  so the sheet is one function of its size, and the pins hold the\r\n *  laws at any size rather than the numbers at one. */\r\nexport const PX_TUFT_W = 8;\r\nexport const PX_TUFT_H = 16;\r\n/** THE LAWS AS FUNCTIONS OF THE TUFT'S SIZE (GRASS-PX4). Each was a\r\n *  literal at 16x32; the fraction is what it always meant. AUDIT\r\n *  GRASS-PX4 F1/F4: ONE function each, read by the constants below AND\r\n *  by layTuft/paintTuft - the first cut wrote the fraction twice, and a\r\n *  sheet built from a second copy is pinned by neither.\r\n *   - a blade's root stands at least `tuftMarginFor(w)` texels in from\r\n *     either edge column, so no tuft touches its neighbour (3 of 16)\r\n *   - the shortest blade is `bladeMinFor(h)`: the height law is\r\n *     floor(h * (0.45 + rnd * 0.55)), so its floor is floor(0.45 h) -\r\n *     7 of 16, 14 of 32. AUDIT GRASS-PX4 F2: the constant used to say\r\n *     \"a quarter of the tuft\" and clamp the height to it, and the clamp\r\n *     never fired at either size; the constant is the law's own floor\r\n *     now and the pins hold that a blade reaches it and none goes under\r\n *   - the tip's sideways travel is two to six sixteenths of the width\r\n *     (2..6 texels of 16)\r\n *   - the highlight lands only on a blade tall enough to clear the\r\n *     sward: `highlightMinFor(h)`, five eighths of the tuft (20 of 32) */\r\nexport const tuftMarginFor = (w) => Math.max(1, Math.round(w * 3 / 16));\r\nexport const bladeMinFor = (h) => Math.floor(h * 0.45);\r\nexport const highlightMinFor = (h) => Math.round(h * 20 / 32);\r\nexport const PX_TUFT_MARGIN = tuftMarginFor(PX_TUFT_W);\r\nexport const PX_BLADE_MIN = bladeMinFor(PX_TUFT_H);\r\nexport const PX_HIGHLIGHT_MIN = highlightMinFor(PX_TUFT_H);\r\n/** the tones a texel can be, in the R channel: 0 nothing, 1 root,\r\n *  2 mid, 3 tip, 4 the tip highlight. Stored as tone * 64 (255 for 4)\r\n *  and read back as floor(R * 4 + 0.5). */\r\nexport const PX_TONES = 4;\r\n/** the lit colour is snapped to this many luminance steps */\r\nexport const PX_RAMP_STEPS = 8;\r\n/** the patch tint is snapped to this many bands */\r\nexport const PX_TINT_BANDS = 4;\r\n/** the sheet's seed - the sheet is the same on every machine, every boot */\r\nexport const PX_SEED = 0x9e3779b1;\r\n/** GRASS AUDIT 1: a tuft stands in for THIS many of the lab's blades.\r\n *  The placer emits one instance per lab blade; a tuft is three to five\r\n *  blades on a quad half its height wide, so drawing one per blade was\r\n *  several times the field's visual density. The host submits half of\r\n *  each cell's blades in the pixel style (the placer's order is random,\r\n *  so the first half is a uniform half). A third was tried and read\r\n *  sparse at the feet, where a tuft is one-texel stalks over open\r\n *  ground; half is the lab's sward, at half the fill. */\r\nexport const PX_BLADES_PER_TUFT = 2;\r\n\r\n/** mulberry32: a small, fast, seedable generator with no state outside\r\n *  its closure, so a sheet is a function of its seed and nothing else */\r\nexport function mulberry32(seed) {\r\n  let a = seed >>> 0;\r\n  return () => {\r\n    a = (a + 0x6D2B79F5) >>> 0;\r\n    let t = a;\r\n    t = Math.imul(t ^ (t >>> 15), t | 1);\r\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\r\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\r\n  };\r\n}\r\n\r\n/** the byte a tone is stored as: 0, 64, 128, 192, 255 */\r\nexport const toneByte = (tone) => tone >= PX_TONES ? 255 : tone * 64;\r\n\r\n/**\r\n * One tuft's blades, as the placer's own laws would lay them if they\r\n * were texels. `w` x `h` texels, row 0 the base. Returns the blades so\r\n * the pins can hold the laws, and paints them into `out` at column\r\n * offset `x0` of a sheet `stride` texels wide.\r\n *\r\n * @param {() => number} rnd\r\n * @param {number} w\r\n * @param {number} h\r\n */\r\nexport function layTuft(rnd, w = PX_TUFT_W, h = PX_TUFT_H) {\r\n  const blades = [];\r\n  const n = 3 + Math.floor(rnd() * 3);            // three to five blades\r\n  // GRASS-PX4: the laws at THIS size - the margin and the lean are the\r\n  // sheet's fractions (the functions beside the constants), scaled to\r\n  // whatever w and h a caller hands in, so a tuft laid for a pin at\r\n  // 16x32 and the shipped 8x16 are the same tuft at two scales.\r\n  const margin = tuftMarginFor(w);\r\n  for (let b = 0; b < n; b++) {\r\n    const x0 = margin + Math.floor(rnd() * (w - 2 * margin));   // never on the edge columns, so no tuft touches its neighbour\r\n    const height = Math.floor(h * (0.45 + rnd() * 0.55));   // bladeMinFor(h)..h: 7..16 of 16\r\n    let lean = (rnd() * 2 - 1) * (2 + rnd() * 4) * (w / 16);   // sideways travel at the tip, either way, in texels of THIS width\r\n    lean = Math.max(1 - x0, Math.min(w - 2 - x0, lean));   // ...but the tip stays inside the tuft: a blade cut off by the sheet's edge is a flat line, not a blade\r\n    const wide = rnd() < 0.5;                     // half the blades carry a two-texel base\r\n    const head = height >= h * 0.8 && rnd() < 0.4;   // a tall blade may carry a seed head\r\n    // AUDIT GRASS-PX4 F3: the head is two texels wide and sits to the tip's RIGHT, so a headed tip one column\r\n    // short of the edge painted the edge column - on 6% of tufts at 8x16 (3% at 16x32), none of them the shipped\r\n    // seeds', which is why \"no tuft on its edge column\" passed as a pin and was not a law. The head stays inside\r\n    // the tuft the way the tip does.\r\n    if (head) lean = Math.min(w - 3 - x0, lean);\r\n    blades.push({ x0, height, lean, wide, head, ordinal: n > 1 ? b / (n - 1) : 0 });\r\n  }\r\n  return blades;\r\n}\r\n\r\n/**\r\n * The tone of a texel `f` of the way up its blade: the root third is\r\n * root, the middle is mid, the top quarter is tip, and the very last\r\n * texel of a blade tall enough to stand clear of the sward is the\r\n * highlight the sun's rim lands on.\r\n */\r\nexport function toneAt(f, isTip, height, highlightMin = PX_HIGHLIGHT_MIN) {\r\n  if (isTip && height >= highlightMin) return 4;\r\n  return f < 0.35 ? 1 : f < 0.75 ? 2 : 3;\r\n}\r\n/** GRASS AUDIT 1: the highlight is the top TWO texels of a tall blade,\r\n *  not one - one was overpainted by a later blade or the blade's own\r\n *  seed head half the time, and twelve highlight texels in a sheet of\r\n *  771 is a rim nobody sees. `r` is the texel's row, `top` the blade's.\r\n *  GRASS-PX4: \"tall\" is five eighths of the tuft, whatever the tuft's\r\n *  height - `highlightMin` is PX_HIGHLIGHT_MIN for the shipped sheet\r\n *  and the same fraction of any other size paintTuft is handed. */\r\nexport const isHighlightRow = (r, top, height, highlightMin = PX_HIGHLIGHT_MIN) => height >= highlightMin && r >= top - 1;\r\n\r\n/**\r\n * Paint one tuft into a sheet. Later blades paint over earlier ones,\r\n * as a painter would lay them.\r\n *\r\n * @param {Uint8Array} out RGBA texels, row 0 first\r\n * @param {number} stride the sheet's width in texels\r\n * @param {number} ox the tuft's first column\r\n * @param {ReturnType<typeof layTuft>} blades\r\n */\r\nexport function paintTuft(out, stride, ox, blades, w = PX_TUFT_W, h = PX_TUFT_H) {\r\n  const highlightMin = highlightMinFor(h);   // GRASS-PX4: the sheet's fraction, at this height - the ONE function (AUDIT GRASS-PX4 F1)\r\n  const put = (x, y, tone, f, ordinal) => {\r\n    if (x < 0 || x >= w || y < 0 || y >= h) return;\r\n    const i = ((y * stride) + ox + x) * 4;\r\n    out[i] = toneByte(tone);\r\n    out[i + 1] = Math.round(f * 255);\r\n    out[i + 2] = Math.round(ordinal * 255);\r\n    out[i + 3] = 255;\r\n  };\r\n  for (const bl of blades) {\r\n    const top = bl.height - 1;\r\n    if (bl.head) {\r\n      // a seed head: a 2x2 cap of mid tone straddling the tip, the way a\r\n      // grass flower sits heavier than the stalk that holds it. GRASS\r\n      // AUDIT 1: painted BEFORE the stalk, so the stalk's own highlight\r\n      // texel lands on top of it and not under it.\r\n      const xt = bl.x0 + Math.round(bl.lean);\r\n      for (const [dx, dy] of [[0, -1], [1, -1], [0, 0], [1, 0]]) put(xt + dx, top + dy, 2, 1, bl.ordinal);\r\n    }\r\n    for (let r = 0; r <= top; r++) {\r\n      const f = top > 0 ? r / top : 1;\r\n      // the stalk's curve is the lab's: the tip travels, the root does\r\n      // not, and the travel goes as height squared\r\n      const x = bl.x0 + Math.round(bl.lean * f * f);\r\n      const tone = isHighlightRow(r, top, bl.height, highlightMin) ? 4 : toneAt(f, r === top, bl.height, highlightMin);\r\n      put(x, r, tone, f, bl.ordinal);\r\n      if (bl.wide && f < 0.2) put(x + (bl.lean >= 0 ? -1 : 1), r, 1, f, bl.ordinal);   // the base's second texel sits on the side the blade leans AWAY from, and only the bottom fifth carries it - wider and the bases read as blocks\r\n    }\r\n  }\r\n}\r\n\r\n/**\r\n * The whole sheet: `variants` tufts side by side, each from its own\r\n * stream of the seed. Returns the level-0 texels and the sheet's size.\r\n */\r\nexport function buildTuftSheet({ variants = PX_VARIANTS, w = PX_TUFT_W, h = PX_TUFT_H, seed = PX_SEED } = {}) {\r\n  const width = variants * w, height = h;\r\n  const data = new Uint8Array(width * height * 4);\r\n  for (let v = 0; v < variants; v++) {\r\n    const rnd = mulberry32((seed ^ Math.imul(v + 1, 0x85ebca6b)) >>> 0);\r\n    paintTuft(data, width, v * w, layTuft(rnd, w, h), w, h);\r\n  }\r\n  return { width, height, data, variants };\r\n}\r\n\r\n/**\r\n * One mip level down, PRESERVING COVERAGE. GRASS AUDIT 1: the first\r\n * chain took the MAX alpha of every block, which kept a far tuft from\r\n * vanishing and also made it a solid rectangle by the third level (19%\r\n * of the sheet covered at level 0, 88% at level 3, 100% from level 4)\r\n * of the ROOT tone (the tie-break took the lowest row) - a far field\r\n * that was a wall of dark blocks three blade-widths wide. An average\r\n * goes the other way and the tuft is gone by level 1.\r\n *\r\n * The right answer is the alpha-test mip law (Castano): a level keeps\r\n * the FRACTION of covered texels the base level had, so a tuft at any\r\n * distance is as dense as it was up close - the blocks with the most\r\n * covered texels are kept, the rest are air, and the alpha stays 0 or\r\n * 255. Each kept block takes the tone of its HIGHEST covered texel (by\r\n * height along the blade, the tip winning over the highlight on a\r\n * tie), because what the eye sees of a distant blade is its tip, which\r\n * is GR4's law for the smooth blade too. Two floors: every tuft keeps\r\n * at least one texel while it is still a texel wide, and once a texel\r\n * spans more than one tuft (the last three levels, under two pixels on\r\n * screen) the level is fully covered (the per-tuft floor does that on\r\n * its own), because a mark that is there is better than a mark that\r\n * flickers.\r\n *\r\n * Sizes halve and floor at one, which is the chain WebGL wants.\r\n */\r\nexport function downsampleCoverage(level, { targetFrac, variants = PX_VARIANTS }) {\r\n  const { width, height, data } = level;\r\n  const w2 = Math.max(1, width >> 1), h2 = Math.max(1, height >> 1);\r\n  const out = new Uint8Array(w2 * h2 * 4);\r\n  const blocks = [];\r\n  for (let y = 0; y < h2; y++) {\r\n    for (let x = 0; x < w2; x++) {\r\n      let count = 0, bi = -1, bg = -1, bt = 9;\r\n      for (let dy = 0; dy < 2; dy++) {\r\n        for (let dx = 0; dx < 2; dx++) {\r\n          const sx = Math.min(width - 1, x * 2 + dx), sy = Math.min(height - 1, y * 2 + dy);\r\n          const i = (sy * width + sx) * 4;\r\n          if (!data[i + 3]) continue;\r\n          count++;\r\n          const g = data[i + 1], t = data[i];\r\n          if (g > bg || (g === bg && t < bt)) { bg = g; bt = t; bi = i; }\r\n        }\r\n      }\r\n      const o = (y * w2 + x) * 4;\r\n      if (bi >= 0) { out[o] = data[bi]; out[o + 1] = data[bi + 1]; out[o + 2] = data[bi + 2]; }\r\n      blocks.push({ x, y, count, g: bg, o });\r\n    }\r\n  }\r\n  const tuftAt = w2 / variants;   // this level's texels per tuft, across\r\n  const covered = blocks.filter((b) => b.count > 0);\r\n  covered.sort((a, b) => b.count - a.count || b.g - a.g || b.y - a.y || a.x - b.x);\r\n  const keep = Math.min(covered.length, Math.max(1, Math.round(targetFrac * w2 * h2)));\r\n  for (let k = 0; k < keep; k++) out[covered[k].o + 3] = 255;\r\n  // the per-tuft floor: a tuft still a texel wide keeps at least one\r\n  // texel - and once a texel spans more than one tuft, every tuft's\r\n  // claim on it fills the whole level, which is the \"always grass\" law\r\n  // for the last three levels stated once rather than twice\r\n  for (let v = 0; v < variants; v++) {\r\n    const x0 = Math.floor(v * tuftAt), x1 = Math.floor((v + 1) * tuftAt);\r\n    const mine = covered.filter((b) => b.x >= x0 && b.x < x1);\r\n    if (mine.length && !mine.some((b) => out[b.o + 3])) out[mine[0].o + 3] = 255;\r\n  }\r\n  return { width: w2, height: h2, data: out };\r\n}\r\n\r\n/** the covered fraction of a level */\r\nexport function coverageOf(level) {\r\n  let n = 0;\r\n  for (let i = 3; i < level.data.length; i += 4) if (level.data[i]) n++;\r\n  return n / (level.width * level.height);\r\n}\r\n\r\n/** the full chain, level 0 first, down to 1x1 - a chain that stops\r\n *  short is an INCOMPLETE texture, which samples black */\r\nexport function buildTuftMips(sheet = buildTuftSheet()) {\r\n  /** @type {{ width: number, height: number, data: Uint8Array, variants?: number }[]} */\r\n  const levels = [sheet];\r\n  const targetFrac = coverageOf(sheet);\r\n  while (levels[levels.length - 1].width > 1 || levels[levels.length - 1].height > 1) {\r\n    levels.push(downsampleCoverage(levels[levels.length - 1], { targetFrac, variants: sheet.variants ?? PX_VARIANTS }));\r\n  }\r\n  return levels;\r\n}\r\n\r\n/** the style the pref names: anything that is not the word `smooth` is\r\n *  a sprite style - the row's default (the meadow, MEADOW1; grassMeadow.js\r\n *  meadowGrass says which) or this one - the same fallback the settings\r\n *  pane draws for a value that is no tier (enhancedMenu.js, BLOOD AUDIT 5) */\r\nexport const pixelGrass = (style) => style !== 'smooth';\r\n","modules/coach-world/vendor/h3.mjs":"// @ts-nocheck\r\n\r\nvar libh3 = function (libh3) {\r\n  libh3 = libh3 || {};\r\n  var Module = typeof libh3 !== \"undefined\" ? libh3 : {};\r\n  var moduleOverrides = {};\r\n  var key;\r\n  for (key in Module) {\r\n    if (Module.hasOwnProperty(key)) {\r\n      moduleOverrides[key] = Module[key];\r\n    }\r\n  }\r\n  var arguments_ = [];\r\n  var thisProgram = \"./this.program\";\r\n  var ENVIRONMENT_IS_WEB = false;\r\n  var ENVIRONMENT_IS_WORKER = false;\r\n  var ENVIRONMENT_IS_NODE = false;\r\n  var ENVIRONMENT_HAS_NODE = false;\r\n  var ENVIRONMENT_IS_SHELL = false;\r\n  ENVIRONMENT_IS_WEB = typeof window === \"object\";\r\n  ENVIRONMENT_IS_WORKER = typeof importScripts === \"function\";\r\n  // MYR5: this self-contained asm.js build embeds its memory image. Avoid the\r\n  // obsolete CommonJS __dirname/require branch in ESM and Workers nodejs_compat.\r\n  ENVIRONMENT_HAS_NODE = false;\r\n  ENVIRONMENT_IS_NODE = ENVIRONMENT_HAS_NODE && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER;\r\n  ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;\r\n  var scriptDirectory = \"\";\r\n  function locateFile(path) {\r\n    if (Module[\"locateFile\"]) {\r\n      return Module[\"locateFile\"](path, scriptDirectory);\r\n    }\r\n    return scriptDirectory + path;\r\n  }\r\n  var read_, readAsync, readBinary;\r\n  // MYR5: removed unreachable Node filesystem loader; see vendor/provenance.json.\r\n  if (ENVIRONMENT_IS_SHELL) {\r\n    if (typeof read != \"undefined\") {\r\n      read_ = function shell_read(f) {\r\n        var data = tryParseAsDataURI(f);\r\n        if (data) {\r\n          return intArrayToString(data);\r\n        }\r\n        return read(f);\r\n      };\r\n    }\r\n    readBinary = function readBinary(f) {\r\n      var data;\r\n      data = tryParseAsDataURI(f);\r\n      if (data) {\r\n        return data;\r\n      }\r\n      if (typeof readbuffer === \"function\") {\r\n        return new Uint8Array(readbuffer(f));\r\n      }\r\n      data = read(f, \"binary\");\r\n      assert(typeof data === \"object\");\r\n      return data;\r\n    };\r\n    if (typeof scriptArgs != \"undefined\") {\r\n      arguments_ = scriptArgs;\r\n    } else if (typeof arguments != \"undefined\") {\r\n      arguments_ = arguments;\r\n    }\r\n    if (typeof print !== \"undefined\") {\r\n      if (typeof console === \"undefined\") { console = {}; }\r\n      console.log = print;\r\n      console.warn = console.error = typeof printErr !== \"undefined\" ? printErr : print;\r\n    }\r\n  } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {\r\n    if (ENVIRONMENT_IS_WORKER) {\r\n      scriptDirectory = self.location.href;\r\n    } else if (typeof document !== \"undefined\" && document.currentScript) {\r\n      scriptDirectory = document.currentScript.src;\r\n    }\r\n    if (scriptDirectory.indexOf(\"blob:\") !== 0) {\r\n      scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf(\"/\") + 1);\r\n    } else {\r\n      scriptDirectory = \"\";\r\n    }\r\n    read_ = function shell_read(url) {\r\n      try {\r\n        var xhr = new XMLHttpRequest();\r\n        xhr.open(\"GET\", url, false);\r\n        xhr.send(null);\r\n        return xhr.responseText;\r\n      } catch (err) {\r\n        var data = tryParseAsDataURI(url);\r\n        if (data) {\r\n          return intArrayToString(data);\r\n        }\r\n        throw err;\r\n      }\r\n    };\r\n    if (ENVIRONMENT_IS_WORKER) {\r\n      readBinary = function readBinary(url) {\r\n        try {\r\n          var xhr = new XMLHttpRequest();\r\n          xhr.open(\"GET\", url, false);\r\n          xhr.responseType = \"arraybuffer\";\r\n          xhr.send(null);\r\n          return new Uint8Array(xhr.response);\r\n        } catch (err) {\r\n          var data = tryParseAsDataURI(url);\r\n          if (data) {\r\n            return data;\r\n          }\r\n          throw err;\r\n        }\r\n      };\r\n    }\r\n    readAsync = function readAsync(url, onload, onerror) {\r\n      var xhr = new XMLHttpRequest();\r\n      xhr.open(\"GET\", url, true);\r\n      xhr.responseType = \"arraybuffer\";\r\n      xhr.onload = function xhr_onload() {\r\n        if (xhr.status == 200 || xhr.status == 0 && xhr.response) {\r\n          onload(xhr.response);\r\n          return;\r\n        }\r\n        var data = tryParseAsDataURI(url);\r\n        if (data) {\r\n          onload(data.buffer);\r\n          return;\r\n        }\r\n        onerror();\r\n      };\r\n      xhr.onerror = onerror;\r\n      xhr.send(null);\r\n    };\r\n  }\r\n  var out = Module[\"print\"] || console.log.bind(console);\r\n  var err = Module[\"printErr\"] || console.warn.bind(console);\r\n  for (key in moduleOverrides) {\r\n    if (moduleOverrides.hasOwnProperty(key)) {\r\n      Module[key] = moduleOverrides[key];\r\n    }\r\n  }\r\n  moduleOverrides = null;\r\n  if (Module[\"arguments\"]) { arguments_ = Module[\"arguments\"]; }\r\n  if (Module[\"thisProgram\"]) { thisProgram = Module[\"thisProgram\"]; }\r\n  var tempRet0 = 0;\r\n  var setTempRet0 = function (value) {\r\n    tempRet0 = value;\r\n  };\r\n  var getTempRet0 = function () {\r\n    return tempRet0;\r\n  };\r\n  var GLOBAL_BASE = 8;\r\n  function setValue(ptr, value, type, noSafe) {\r\n    type = type || \"i8\";\r\n    if (type.charAt(type.length - 1) === \"*\") { type = \"i32\"; }\r\n    switch (type) {\r\n      case \"i1\":\r\n        HEAP8[ptr >> 0] = value;\r\n        break;\r\n      case \"i8\":\r\n        HEAP8[ptr >> 0] = value;\r\n        break;\r\n      case \"i16\":\r\n        HEAP16[ptr >> 1] = value;\r\n        break;\r\n      case \"i32\":\r\n        HEAP32[ptr >> 2] = value;\r\n        break;\r\n      case \"i64\":\r\n        tempI64 = [value >>> 0, (tempDouble = value, +Math_abs(tempDouble) >= +1 ? tempDouble > +0 ? (Math_min(+Math_floor(tempDouble / +4294967296), +4294967295) | 0) >>> 0 : ~~+Math_ceil((tempDouble - +(~~tempDouble >>> 0)) / +4294967296) >>> 0 : 0)], HEAP32[ptr >> 2] = tempI64[0], HEAP32[ptr + 4 >> 2] = tempI64[1];\r\n        break;\r\n      case \"float\":\r\n        HEAPF32[ptr >> 2] = value;\r\n        break;\r\n      case \"double\":\r\n        HEAPF64[ptr >> 3] = value;\r\n        break;\r\n      default:\r\n        abort(\"invalid type for setValue: \" + type);\r\n    }\r\n  }\r\n  function getValue(ptr, type, noSafe) {\r\n    type = type || \"i8\";\r\n    if (type.charAt(type.length - 1) === \"*\") { type = \"i32\"; }\r\n    switch (type) {\r\n      case \"i1\":\r\n        return HEAP8[ptr >> 0];\r\n      case \"i8\":\r\n        return HEAP8[ptr >> 0];\r\n      case \"i16\":\r\n        return HEAP16[ptr >> 1];\r\n      case \"i32\":\r\n        return HEAP32[ptr >> 2];\r\n      case \"i64\":\r\n        return HEAP32[ptr >> 2];\r\n      case \"float\":\r\n        return HEAPF32[ptr >> 2];\r\n      case \"double\":\r\n        return HEAPF64[ptr >> 3];\r\n      default:\r\n        abort(\"invalid type for getValue: \" + type);\r\n    }\r\n    return null;\r\n  }\r\n  var ABORT = false;\r\n  function assert(condition, text) {\r\n    if (!condition) {\r\n      abort(\"Assertion failed: \" + text);\r\n    }\r\n  }\r\n  function getCFunc(ident) {\r\n    var func = Module[\"_\" + ident];\r\n    assert(func, \"Cannot call unknown function \" + ident + \", make sure it is exported\");\r\n    return func;\r\n  }\r\n  function ccall(ident, returnType, argTypes, args, opts) {\r\n    var toC = {\r\n      \"string\": function (str) {\r\n        var ret = 0;\r\n        if (str !== null && str !== undefined && str !== 0) {\r\n          var len = (str.length << 2) + 1;\r\n          ret = stackAlloc(len);\r\n          stringToUTF8(str, ret, len);\r\n        }\r\n        return ret;\r\n      },\r\n      \"array\": function (arr) {\r\n        var ret = stackAlloc(arr.length);\r\n        writeArrayToMemory(arr, ret);\r\n        return ret;\r\n      }\r\n    };\r\n    function convertReturnValue(ret) {\r\n      if (returnType === \"string\") { return UTF8ToString(ret); }\r\n      if (returnType === \"boolean\") { return Boolean(ret); }\r\n      return ret;\r\n    }\r\n    var func = getCFunc(ident);\r\n    var cArgs = [];\r\n    var stack = 0;\r\n    if (args) {\r\n      for (var i = 0; i < args.length; i++) {\r\n        var converter = toC[argTypes[i]];\r\n        if (converter) {\r\n          if (stack === 0) { stack = stackSave(); }\r\n          cArgs[i] = converter(args[i]);\r\n        } else {\r\n          cArgs[i] = args[i];\r\n        }\r\n      }\r\n    }\r\n    var ret = func.apply(null, cArgs);\r\n    ret = convertReturnValue(ret);\r\n    if (stack !== 0) { stackRestore(stack); }\r\n    return ret;\r\n  }\r\n  function cwrap(ident, returnType, argTypes, opts) {\r\n    argTypes = argTypes || [];\r\n    var numericArgs = argTypes.every(function (type) {\r\n      return type === \"number\";\r\n    });\r\n    var numericRet = returnType !== \"string\";\r\n    if (numericRet && numericArgs && !opts) {\r\n      return getCFunc(ident);\r\n    }\r\n    return function () {\r\n      return ccall(ident, returnType, argTypes, arguments, opts);\r\n    };\r\n  }\r\n  var UTF8Decoder = typeof TextDecoder !== \"undefined\" ? new TextDecoder(\"utf8\") : undefined;\r\n  function UTF8ArrayToString(u8Array, idx, maxBytesToRead) {\r\n    var endIdx = idx + maxBytesToRead;\r\n    var endPtr = idx;\r\n    while (u8Array[endPtr] && !(endPtr >= endIdx)) { ++endPtr; }\r\n    if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) {\r\n      return UTF8Decoder.decode(u8Array.subarray(idx, endPtr));\r\n    } else {\r\n      var str = \"\";\r\n      while (idx < endPtr) {\r\n        var u0 = u8Array[idx++];\r\n        if (!(u0 & 128)) {\r\n          str += String.fromCharCode(u0);\r\n          continue;\r\n        }\r\n        var u1 = u8Array[idx++] & 63;\r\n        if ((u0 & 224) == 192) {\r\n          str += String.fromCharCode((u0 & 31) << 6 | u1);\r\n          continue;\r\n        }\r\n        var u2 = u8Array[idx++] & 63;\r\n        if ((u0 & 240) == 224) {\r\n          u0 = (u0 & 15) << 12 | u1 << 6 | u2;\r\n        } else {\r\n          u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | u8Array[idx++] & 63;\r\n        }\r\n        if (u0 < 65536) {\r\n          str += String.fromCharCode(u0);\r\n        } else {\r\n          var ch = u0 - 65536;\r\n          str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);\r\n        }\r\n      }\r\n    }\r\n    return str;\r\n  }\r\n  function UTF8ToString(ptr, maxBytesToRead) {\r\n    return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : \"\";\r\n  }\r\n  function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) {\r\n    if (!(maxBytesToWrite > 0)) { return 0; }\r\n    var startIdx = outIdx;\r\n    var endIdx = outIdx + maxBytesToWrite - 1;\r\n    for (var i = 0; i < str.length; ++i) {\r\n      var u = str.charCodeAt(i);\r\n      if (u >= 55296 && u <= 57343) {\r\n        var u1 = str.charCodeAt(++i);\r\n        u = 65536 + ((u & 1023) << 10) | u1 & 1023;\r\n      }\r\n      if (u <= 127) {\r\n        if (outIdx >= endIdx) { break; }\r\n        outU8Array[outIdx++] = u;\r\n      } else if (u <= 2047) {\r\n        if (outIdx + 1 >= endIdx) { break; }\r\n        outU8Array[outIdx++] = 192 | u >> 6;\r\n        outU8Array[outIdx++] = 128 | u & 63;\r\n      } else if (u <= 65535) {\r\n        if (outIdx + 2 >= endIdx) { break; }\r\n        outU8Array[outIdx++] = 224 | u >> 12;\r\n        outU8Array[outIdx++] = 128 | u >> 6 & 63;\r\n        outU8Array[outIdx++] = 128 | u & 63;\r\n      } else {\r\n        if (outIdx + 3 >= endIdx) { break; }\r\n        outU8Array[outIdx++] = 240 | u >> 18;\r\n        outU8Array[outIdx++] = 128 | u >> 12 & 63;\r\n        outU8Array[outIdx++] = 128 | u >> 6 & 63;\r\n        outU8Array[outIdx++] = 128 | u & 63;\r\n      }\r\n    }\r\n    outU8Array[outIdx] = 0;\r\n    return outIdx - startIdx;\r\n  }\r\n  function stringToUTF8(str, outPtr, maxBytesToWrite) {\r\n    return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);\r\n  }\r\n  var UTF16Decoder = typeof TextDecoder !== \"undefined\" ? new TextDecoder(\"utf-16le\") : undefined;\r\n  function writeArrayToMemory(array, buffer) {\r\n    HEAP8.set(array, buffer);\r\n  }\r\n  function alignUp(x, multiple) {\r\n    if (x % multiple > 0) {\r\n      x += multiple - x % multiple;\r\n    }\r\n    return x;\r\n  }\r\n  var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;\r\n  function updateGlobalBufferAndViews(buf) {\r\n    buffer = buf;\r\n    Module[\"HEAP8\"] = HEAP8 = new Int8Array(buf);\r\n    Module[\"HEAP16\"] = HEAP16 = new Int16Array(buf);\r\n    Module[\"HEAP32\"] = HEAP32 = new Int32Array(buf);\r\n    Module[\"HEAPU8\"] = HEAPU8 = new Uint8Array(buf);\r\n    Module[\"HEAPU16\"] = HEAPU16 = new Uint16Array(buf);\r\n    Module[\"HEAPU32\"] = HEAPU32 = new Uint32Array(buf);\r\n    Module[\"HEAPF32\"] = HEAPF32 = new Float32Array(buf);\r\n    Module[\"HEAPF64\"] = HEAPF64 = new Float64Array(buf);\r\n  }\r\n  var DYNAMIC_BASE = 5271520,\r\n    DYNAMICTOP_PTR = 28608;\r\n  var INITIAL_TOTAL_MEMORY = Module[\"TOTAL_MEMORY\"] || 33554432;\r\n  if (Module[\"buffer\"]) {\r\n    buffer = Module[\"buffer\"];\r\n  } else {\r\n    buffer = new ArrayBuffer(INITIAL_TOTAL_MEMORY);\r\n  }\r\n  INITIAL_TOTAL_MEMORY = buffer.byteLength;\r\n  updateGlobalBufferAndViews(buffer);\r\n  HEAP32[DYNAMICTOP_PTR >> 2] = DYNAMIC_BASE;\r\n  function callRuntimeCallbacks(callbacks) {\r\n    while (callbacks.length > 0) {\r\n      var callback = callbacks.shift();\r\n      if (typeof callback == \"function\") {\r\n        callback();\r\n        continue;\r\n      }\r\n      var func = callback.func;\r\n      if (typeof func === \"number\") {\r\n        if (callback.arg === undefined) {\r\n          Module[\"dynCall_v\"](func);\r\n        } else {\r\n          Module[\"dynCall_vi\"](func, callback.arg);\r\n        }\r\n      } else {\r\n        func(callback.arg === undefined ? null : callback.arg);\r\n      }\r\n    }\r\n  }\r\n  var __ATPRERUN__ = [];\r\n  var __ATINIT__ = [];\r\n  var __ATMAIN__ = [];\r\n  var __ATPOSTRUN__ = [];\r\n  function preRun() {\r\n    if (Module[\"preRun\"]) {\r\n      if (typeof Module[\"preRun\"] == \"function\") { Module[\"preRun\"] = [Module[\"preRun\"]]; }\r\n      while (Module[\"preRun\"].length) {\r\n        addOnPreRun(Module[\"preRun\"].shift());\r\n      }\r\n    }\r\n    callRuntimeCallbacks(__ATPRERUN__);\r\n  }\r\n  function initRuntime() {\r\n    callRuntimeCallbacks(__ATINIT__);\r\n  }\r\n  function preMain() {\r\n    callRuntimeCallbacks(__ATMAIN__);\r\n  }\r\n  function postRun() {\r\n    if (Module[\"postRun\"]) {\r\n      if (typeof Module[\"postRun\"] == \"function\") { Module[\"postRun\"] = [Module[\"postRun\"]]; }\r\n      while (Module[\"postRun\"].length) {\r\n        addOnPostRun(Module[\"postRun\"].shift());\r\n      }\r\n    }\r\n    callRuntimeCallbacks(__ATPOSTRUN__);\r\n  }\r\n  function addOnPreRun(cb) {\r\n    __ATPRERUN__.unshift(cb);\r\n  }\r\n  function addOnPostRun(cb) {\r\n    __ATPOSTRUN__.unshift(cb);\r\n  }\r\n  var Math_abs = Math.abs;\r\n  var Math_ceil = Math.ceil;\r\n  var Math_floor = Math.floor;\r\n  var Math_min = Math.min;\r\n  var runDependencies = 0;\r\n  var runDependencyWatcher = null;\r\n  var dependenciesFulfilled = null;\r\n  function addRunDependency(id) {\r\n    runDependencies++;\r\n    if (Module[\"monitorRunDependencies\"]) {\r\n      Module[\"monitorRunDependencies\"](runDependencies);\r\n    }\r\n  }\r\n  function removeRunDependency(id) {\r\n    runDependencies--;\r\n    if (Module[\"monitorRunDependencies\"]) {\r\n      Module[\"monitorRunDependencies\"](runDependencies);\r\n    }\r\n    if (runDependencies == 0) {\r\n      if (runDependencyWatcher !== null) {\r\n        clearInterval(runDependencyWatcher);\r\n        runDependencyWatcher = null;\r\n      }\r\n      if (dependenciesFulfilled) {\r\n        var callback = dependenciesFulfilled;\r\n        dependenciesFulfilled = null;\r\n        callback();\r\n      }\r\n    }\r\n  }\r\n  Module[\"preloadedImages\"] = {};\r\n  Module[\"preloadedAudios\"] = {};\r\n  var memoryInitializer = null;\r\n  var dataURIPrefix = \"data:application/octet-stream;base64,\";\r\n  function isDataURI(filename) {\r\n    return String.prototype.startsWith ? filename.startsWith(dataURIPrefix) : filename.indexOf(dataURIPrefix) === 0;\r\n  }\r\n  var tempDouble;\r\n  var tempI64;\r\n  memoryInitializer = \"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAQAAAAIAAAADAAAABAAAAAUAAAAGAAAAAQAAAAQAAAADAAAABgAAAAUAAAACAAAAAAAAAAIAAAADAAAAAQAAAAQAAAAGAAAAAAAAAAUAAAADAAAABgAAAAQAAAAFAAAAAAAAAAEAAAACAAAABAAAAAUAAAAGAAAAAAAAAAIAAAADAAAAAQAAAAUAAAACAAAAAAAAAAEAAAADAAAABgAAAAQAAAAGAAAAAAAAAAUAAAACAAAAAQAAAAQAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAIAAAADAAAAAAAAAAAAAAACAAAAAAAAAAEAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAYAAAAAAAAABQAAAAAAAAAAAAAABAAAAAUAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAYAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAAAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAAAAAAAQAAAAMAAAAEAAAABQAAAAYAAAAAAAAAAQAAAAIAAAAEAAAABQAAAAYAAAAAAAAAAQAAAAIAAAADAAAABQAAAAYAAAAAAAAAAQAAAAIAAAADAAAABAAAAAYAAAAAAAAAAQAAAAIAAAADAAAABAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAYAAAAAAAAAAwAAAAIAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAFAAAABAAAAAAAAAABAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAYAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAAAgAAAAQAAAADAAAACAAAAAEAAAAHAAAABgAAAAkAAAAAAAAAAwAAAAIAAAACAAAABgAAAAoAAAALAAAAAAAAAAEAAAAFAAAAAwAAAA0AAAABAAAABwAAAAQAAAAMAAAAAAAAAAQAAAB/AAAADwAAAAgAAAADAAAAAAAAAAwAAAAFAAAAAgAAABIAAAAKAAAACAAAAAAAAAAQAAAABgAAAA4AAAALAAAAEQAAAAEAAAAJAAAAAgAAAAcAAAAVAAAACQAAABMAAAADAAAADQAAAAEAAAAIAAAABQAAABYAAAAQAAAABAAAAAAAAAAPAAAACQAAABMAAAAOAAAAFAAAAAEAAAAHAAAABgAAAAoAAAALAAAAGAAAABcAAAAFAAAAAgAAABIAAAALAAAAEQAAABcAAAAZAAAAAgAAAAYAAAAKAAAADAAAABwAAAANAAAAGgAAAAQAAAAPAAAAAwAAAA0AAAAaAAAAFQAAAB0AAAADAAAADAAAAAcAAAAOAAAAfwAAABEAAAAbAAAACQAAABQAAAAGAAAADwAAABYAAAAcAAAAHwAAAAQAAAAIAAAADAAAABAAAAASAAAAIQAAAB4AAAAIAAAABQAAABYAAAARAAAACwAAAA4AAAAGAAAAIwAAABkAAAAbAAAAEgAAABgAAAAeAAAAIAAAAAUAAAAKAAAAEAAAABMAAAAiAAAAFAAAACQAAAAHAAAAFQAAAAkAAAAUAAAADgAAABMAAAAJAAAAKAAAABsAAAAkAAAAFQAAACYAAAATAAAAIgAAAA0AAAAdAAAABwAAABYAAAAQAAAAKQAAACEAAAAPAAAACAAAAB8AAAAXAAAAGAAAAAsAAAAKAAAAJwAAACUAAAAZAAAAGAAAAH8AAAAgAAAAJQAAAAoAAAAXAAAAEgAAABkAAAAXAAAAEQAAAAsAAAAtAAAAJwAAACMAAAAaAAAAKgAAAB0AAAArAAAADAAAABwAAAANAAAAGwAAACgAAAAjAAAALgAAAA4AAAAUAAAAEQAAABwAAAAfAAAAKgAAACwAAAAMAAAADwAAABoAAAAdAAAAKwAAACYAAAAvAAAADQAAABoAAAAVAAAAHgAAACAAAAAwAAAAMgAAABAAAAASAAAAIQAAAB8AAAApAAAALAAAADUAAAAPAAAAFgAAABwAAAAgAAAAHgAAABgAAAASAAAANAAAADIAAAAlAAAAIQAAAB4AAAAxAAAAMAAAABYAAAAQAAAAKQAAACIAAAATAAAAJgAAABUAAAA2AAAAJAAAADMAAAAjAAAALgAAAC0AAAA4AAAAEQAAABsAAAAZAAAAJAAAABQAAAAiAAAAEwAAADcAAAAoAAAANgAAACUAAAAnAAAANAAAADkAAAAYAAAAFwAAACAAAAAmAAAAfwAAACIAAAAzAAAAHQAAAC8AAAAVAAAAJwAAACUAAAAZAAAAFwAAADsAAAA5AAAALQAAACgAAAAbAAAAJAAAABQAAAA8AAAALgAAADcAAAApAAAAMQAAADUAAAA9AAAAFgAAACEAAAAfAAAAKgAAADoAAAArAAAAPgAAABwAAAAsAAAAGgAAACsAAAA+AAAALwAAAEAAAAAaAAAAKgAAAB0AAAAsAAAANQAAADoAAABBAAAAHAAAAB8AAAAqAAAALQAAACcAAAAjAAAAGQAAAD8AAAA7AAAAOAAAAC4AAAA8AAAAOAAAAEQAAAAbAAAAKAAAACMAAAAvAAAAJgAAACsAAAAdAAAARQAAADMAAABAAAAAMAAAADEAAAAeAAAAIQAAAEMAAABCAAAAMgAAADEAAAB/AAAAPQAAAEIAAAAhAAAAMAAAACkAAAAyAAAAMAAAACAAAAAeAAAARgAAAEMAAAA0AAAAMwAAAEUAAAA2AAAARwAAACYAAAAvAAAAIgAAADQAAAA5AAAARgAAAEoAAAAgAAAAJQAAADIAAAA1AAAAPQAAAEEAAABLAAAAHwAAACkAAAAsAAAANgAAAEcAAAA3AAAASQAAACIAAAAzAAAAJAAAADcAAAAoAAAANgAAACQAAABIAAAAPAAAAEkAAAA4AAAARAAAAD8AAABNAAAAIwAAAC4AAAAtAAAAOQAAADsAAABKAAAATgAAACUAAAAnAAAANAAAADoAAAB/AAAAPgAAAEwAAAAsAAAAQQAAACoAAAA7AAAAPwAAAE4AAABPAAAAJwAAAC0AAAA5AAAAPAAAAEgAAABEAAAAUAAAACgAAAA3AAAALgAAAD0AAAA1AAAAMQAAACkAAABRAAAASwAAAEIAAAA+AAAAKwAAADoAAAAqAAAAUgAAAEAAAABMAAAAPwAAAH8AAAA4AAAALQAAAE8AAAA7AAAATQAAAEAAAAAvAAAAPgAAACsAAABUAAAARQAAAFIAAABBAAAAOgAAADUAAAAsAAAAVgAAAEwAAABLAAAAQgAAAEMAAABRAAAAVQAAADEAAAAwAAAAPQAAAEMAAABCAAAAMgAAADAAAABXAAAAVQAAAEYAAABEAAAAOAAAADwAAAAuAAAAWgAAAE0AAABQAAAARQAAADMAAABAAAAALwAAAFkAAABHAAAAVAAAAEYAAABDAAAANAAAADIAAABTAAAAVwAAAEoAAABHAAAAWQAAAEkAAABbAAAAMwAAAEUAAAA2AAAASAAAAH8AAABJAAAANwAAAFAAAAA8AAAAWAAAAEkAAABbAAAASAAAAFgAAAA2AAAARwAAADcAAABKAAAATgAAAFMAAABcAAAANAAAADkAAABGAAAASwAAAEEAAAA9AAAANQAAAF4AAABWAAAAUQAAAEwAAABWAAAAUgAAAGAAAAA6AAAAQQAAAD4AAABNAAAAPwAAAEQAAAA4AAAAXQAAAE8AAABaAAAATgAAAEoAAAA7AAAAOQAAAF8AAABcAAAATwAAAE8AAABOAAAAPwAAADsAAABdAAAAXwAAAE0AAABQAAAARAAAAEgAAAA8AAAAYwAAAFoAAABYAAAAUQAAAFUAAABeAAAAZQAAAD0AAABCAAAASwAAAFIAAABgAAAAVAAAAGIAAAA+AAAATAAAAEAAAABTAAAAfwAAAEoAAABGAAAAZAAAAFcAAABcAAAAVAAAAEUAAABSAAAAQAAAAGEAAABZAAAAYgAAAFUAAABXAAAAZQAAAGYAAABCAAAAQwAAAFEAAABWAAAATAAAAEsAAABBAAAAaAAAAGAAAABeAAAAVwAAAFMAAABmAAAAZAAAAEMAAABGAAAAVQAAAFgAAABIAAAAWwAAAEkAAABjAAAAUAAAAGkAAABZAAAAYQAAAFsAAABnAAAARQAAAFQAAABHAAAAWgAAAE0AAABQAAAARAAAAGoAAABdAAAAYwAAAFsAAABJAAAAWQAAAEcAAABpAAAAWAAAAGcAAABcAAAAUwAAAE4AAABKAAAAbAAAAGQAAABfAAAAXQAAAE8AAABaAAAATQAAAG0AAABfAAAAagAAAF4AAABWAAAAUQAAAEsAAABrAAAAaAAAAGUAAABfAAAAXAAAAE8AAABOAAAAbQAAAGwAAABdAAAAYAAAAGgAAABiAAAAbgAAAEwAAABWAAAAUgAAAGEAAAB/AAAAYgAAAFQAAABnAAAAWQAAAG8AAABiAAAAbgAAAGEAAABvAAAAUgAAAGAAAABUAAAAYwAAAFAAAABpAAAAWAAAAGoAAABaAAAAcQAAAGQAAABmAAAAUwAAAFcAAABsAAAAcgAAAFwAAABlAAAAZgAAAGsAAABwAAAAUQAAAFUAAABeAAAAZgAAAGUAAABXAAAAVQAAAHIAAABwAAAAZAAAAGcAAABbAAAAYQAAAFkAAAB0AAAAaQAAAG8AAABoAAAAawAAAG4AAABzAAAAVgAAAF4AAABgAAAAaQAAAFgAAABnAAAAWwAAAHEAAABjAAAAdAAAAGoAAABdAAAAYwAAAFoAAAB1AAAAbQAAAHEAAABrAAAAfwAAAGUAAABeAAAAcwAAAGgAAABwAAAAbAAAAGQAAABfAAAAXAAAAHYAAAByAAAAbQAAAG0AAABsAAAAXQAAAF8AAAB1AAAAdgAAAGoAAABuAAAAYgAAAGgAAABgAAAAdwAAAG8AAABzAAAAbwAAAGEAAABuAAAAYgAAAHQAAABnAAAAdwAAAHAAAABrAAAAZgAAAGUAAAB4AAAAcwAAAHIAAABxAAAAYwAAAHQAAABpAAAAdQAAAGoAAAB5AAAAcgAAAHAAAABkAAAAZgAAAHYAAAB4AAAAbAAAAHMAAABuAAAAawAAAGgAAAB4AAAAdwAAAHAAAAB0AAAAZwAAAHcAAABvAAAAcQAAAGkAAAB5AAAAdQAAAH8AAABtAAAAdgAAAHEAAAB5AAAAagAAAHYAAAB4AAAAbAAAAHIAAAB1AAAAeQAAAG0AAAB3AAAAbwAAAHMAAABuAAAAeQAAAHQAAAB4AAAAeAAAAHMAAAByAAAAcAAAAHkAAAB3AAAAdgAAAHkAAAB0AAAAeAAAAHcAAAB1AAAAcQAAAHYAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAABAAAABQAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAACAAAABQAAAAEAAAAAAAAA/////wEAAAAAAAAAAwAAAAQAAAACAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAMAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAFAAAAAQAAAAAAAAAAAAAAAQAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAAAAABAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAADAAAABQAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAP////8DAAAAAAAAAAUAAAACAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAEAAAABQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAFAAAABQAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAAAAAD/////AwAAAAAAAAAFAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAQAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAAAAAP////8DAAAAAAAAAAUAAAACAAAAAAAAAAAAAAADAAAAAAAAAAAAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAUAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAAABQAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAAAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAAAAAAAAAAA/////wMAAAAAAAAABQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAA/////wMAAAAAAAAABQAAAAIAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAAAAAD/////AwAAAAAAAAAFAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAADAAAAAAAAAAMAAAADAAAAAwAAAAAAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAAAAD/////AwAAAAAAAAAFAAAAAgAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAAAAAAAAAAADAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAAAAAAAAAAAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAQAAAAAAAAABAAAAAAAAAAAAAAABAAAAAwAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAP////8DAAAAAAAAAAUAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAUAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAA/////wMAAAAAAAAABQAAAAIAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAAFAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAQAAAAAAAAABAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAQAAAAAAAAABAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAUAAAABAAAAAAAAAP////8DAAAAAAAAAAUAAAACAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAABAAAAAUAAAABAAAAAAAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAADAAAAAQAAAAAAAAABAAAAAAAAAAUAAAAAAAAAAAAAAAUAAAAFAAAAAAAAAAAAAAD/////AQAAAAAAAAADAAAABAAAAAIAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAUAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAEAAAD//////////wEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAACAAAAAAAAAAAAAAABAAAAAgAAAAYAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAKAAAAAgAAAAAAAAAAAAAAAQAAAAEAAAAFAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAACAAAAAAAAAAAAAAABAAAAAwAAAAcAAAAGAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAABwAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAADgAAAAIAAAAAAAAAAAAAAAEAAAAAAAAACQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAMAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAIAAAAAAAAAAAAAAAEAAAAEAAAACAAAAAoAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAgAAAAAAAAAAAAAAAQAAAAsAAAAPAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAOAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAIAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAgAAAAAAAAAAAAAAAQAAAAwAAAAQAAAADAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAADwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAACAAAAAAAAAAAAAAABAAAACgAAABMAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAOAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAACQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAIAAAAAAAAAAAAAAAEAAAANAAAAEQAAAA0AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAATAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAACAAAAAAAAAAAAAAABAAAADgAAABIAAAAPAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAADwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAASAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAABIAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAATAAAAAgAAAAAAAAAAAAAAAQAAAP//////////EwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAASAAAAAAAAABgAAAAAAAAAIQAAAAAAAAAeAAAAAAAAACAAAAADAAAAMQAAAAEAAAAwAAAAAwAAADIAAAADAAAACAAAAAAAAAAFAAAABQAAAAoAAAAFAAAAFgAAAAAAAAAQAAAAAAAAABIAAAAAAAAAKQAAAAEAAAAhAAAAAAAAAB4AAAAAAAAABAAAAAAAAAAAAAAABQAAAAIAAAAFAAAADwAAAAEAAAAIAAAAAAAAAAUAAAAFAAAAHwAAAAEAAAAWAAAAAAAAABAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAA4AAAAAAAAACgAAAAAAAAALAAAAAAAAABEAAAADAAAAGAAAAAEAAAAXAAAAAwAAABkAAAADAAAAAAAAAAAAAAABAAAABQAAAAkAAAAFAAAABQAAAAAAAAACAAAAAAAAAAYAAAAAAAAAEgAAAAEAAAAKAAAAAAAAAAsAAAAAAAAABAAAAAEAAAADAAAABQAAAAcAAAAFAAAACAAAAAEAAAAAAAAAAAAAAAEAAAAFAAAAEAAAAAEAAAAFAAAAAAAAAAIAAAAAAAAABwAAAAAAAAAVAAAAAAAAACYAAAAAAAAACQAAAAAAAAATAAAAAAAAACIAAAADAAAADgAAAAEAAAAUAAAAAwAAACQAAAADAAAAAwAAAAAAAAANAAAABQAAAB0AAAAFAAAAAQAAAAAAAAAHAAAAAAAAABUAAAAAAAAABgAAAAEAAAAJAAAAAAAAABMAAAAAAAAABAAAAAIAAAAMAAAABQAAABoAAAAFAAAAAAAAAAEAAAADAAAAAAAAAA0AAAAFAAAAAgAAAAEAAAABAAAAAAAAAAcAAAAAAAAAGgAAAAAAAAAqAAAAAAAAADoAAAAAAAAAHQAAAAAAAAArAAAAAAAAAD4AAAADAAAAJgAAAAEAAAAvAAAAAwAAAEAAAAADAAAADAAAAAAAAAAcAAAABQAAACwAAAAFAAAADQAAAAAAAAAaAAAAAAAAACoAAAAAAAAAFQAAAAEAAAAdAAAAAAAAACsAAAAAAAAABAAAAAMAAAAPAAAABQAAAB8AAAAFAAAAAwAAAAEAAAAMAAAAAAAAABwAAAAFAAAABwAAAAEAAAANAAAAAAAAABoAAAAAAAAAHwAAAAAAAAApAAAAAAAAADEAAAAAAAAALAAAAAAAAAA1AAAAAAAAAD0AAAADAAAAOgAAAAEAAABBAAAAAwAAAEsAAAADAAAADwAAAAAAAAAWAAAABQAAACEAAAAFAAAAHAAAAAAAAAAfAAAAAAAAACkAAAAAAAAAKgAAAAEAAAAsAAAAAAAAADUAAAAAAAAABAAAAAQAAAAIAAAABQAAABAAAAAFAAAADAAAAAEAAAAPAAAAAAAAABYAAAAFAAAAGgAAAAEAAAAcAAAAAAAAAB8AAAAAAAAAMgAAAAAAAAAwAAAAAAAAADEAAAADAAAAIAAAAAAAAAAeAAAAAwAAACEAAAADAAAAGAAAAAMAAAASAAAAAwAAABAAAAADAAAARgAAAAAAAABDAAAAAAAAAEIAAAADAAAANAAAAAMAAAAyAAAAAAAAADAAAAAAAAAAJQAAAAMAAAAgAAAAAAAAAB4AAAADAAAAUwAAAAAAAABXAAAAAwAAAFUAAAADAAAASgAAAAMAAABGAAAAAAAAAEMAAAAAAAAAOQAAAAEAAAA0AAAAAwAAADIAAAAAAAAAGQAAAAAAAAAXAAAAAAAAABgAAAADAAAAEQAAAAAAAAALAAAAAwAAAAoAAAADAAAADgAAAAMAAAAGAAAAAwAAAAIAAAADAAAALQAAAAAAAAAnAAAAAAAAACUAAAADAAAAIwAAAAMAAAAZAAAAAAAAABcAAAAAAAAAGwAAAAMAAAARAAAAAAAAAAsAAAADAAAAPwAAAAAAAAA7AAAAAwAAADkAAAADAAAAOAAAAAMAAAAtAAAAAAAAACcAAAAAAAAALgAAAAMAAAAjAAAAAwAAABkAAAAAAAAAJAAAAAAAAAAUAAAAAAAAAA4AAAADAAAAIgAAAAAAAAATAAAAAwAAAAkAAAADAAAAJgAAAAMAAAAVAAAAAwAAAAcAAAADAAAANwAAAAAAAAAoAAAAAAAAABsAAAADAAAANgAAAAMAAAAkAAAAAAAAABQAAAAAAAAAMwAAAAMAAAAiAAAAAAAAABMAAAADAAAASAAAAAAAAAA8AAAAAwAAAC4AAAADAAAASQAAAAMAAAA3AAAAAAAAACgAAAAAAAAARwAAAAMAAAA2AAAAAwAAACQAAAAAAAAAQAAAAAAAAAAvAAAAAAAAACYAAAADAAAAPgAAAAAAAAArAAAAAwAAAB0AAAADAAAAOgAAAAMAAAAqAAAAAwAAABoAAAADAAAAVAAAAAAAAABFAAAAAAAAADMAAAADAAAAUgAAAAMAAABAAAAAAAAAAC8AAAAAAAAATAAAAAMAAAA+AAAAAAAAACsAAAADAAAAYQAAAAAAAABZAAAAAwAAAEcAAAADAAAAYgAAAAMAAABUAAAAAAAAAEUAAAAAAAAAYAAAAAMAAABSAAAAAwAAAEAAAAAAAAAASwAAAAAAAABBAAAAAAAAADoAAAADAAAAPQAAAAAAAAA1AAAAAwAAACwAAAADAAAAMQAAAAMAAAApAAAAAwAAAB8AAAADAAAAXgAAAAAAAABWAAAAAAAAAEwAAAADAAAAUQAAAAMAAABLAAAAAAAAAEEAAAAAAAAAQgAAAAMAAAA9AAAAAAAAADUAAAADAAAAawAAAAAAAABoAAAAAwAAAGAAAAADAAAAZQAAAAMAAABeAAAAAAAAAFYAAAAAAAAAVQAAAAMAAABRAAAAAwAAAEsAAAAAAAAAOQAAAAAAAAA7AAAAAAAAAD8AAAADAAAASgAAAAAAAABOAAAAAwAAAE8AAAADAAAAUwAAAAMAAABcAAAAAwAAAF8AAAADAAAAJQAAAAAAAAAnAAAAAwAAAC0AAAADAAAANAAAAAAAAAA5AAAAAAAAADsAAAAAAAAARgAAAAMAAABKAAAAAAAAAE4AAAADAAAAGAAAAAAAAAAXAAAAAwAAABkAAAADAAAAIAAAAAMAAAAlAAAAAAAAACcAAAADAAAAMgAAAAMAAAA0AAAAAAAAADkAAAAAAAAALgAAAAAAAAA8AAAAAAAAAEgAAAADAAAAOAAAAAAAAABEAAAAAwAAAFAAAAADAAAAPwAAAAMAAABNAAAAAwAAAFoAAAADAAAAGwAAAAAAAAAoAAAAAwAAADcAAAADAAAAIwAAAAAAAAAuAAAAAAAAADwAAAAAAAAALQAAAAMAAAA4AAAAAAAAAEQAAAADAAAADgAAAAAAAAAUAAAAAwAAACQAAAADAAAAEQAAAAMAAAAbAAAAAAAAACgAAAADAAAAGQAAAAMAAAAjAAAAAAAAAC4AAAAAAAAARwAAAAAAAABZAAAAAAAAAGEAAAADAAAASQAAAAAAAABbAAAAAwAAAGcAAAADAAAASAAAAAMAAABYAAAAAwAAAGkAAAADAAAAMwAAAAAAAABFAAAAAwAAAFQAAAADAAAANgAAAAAAAABHAAAAAAAAAFkAAAAAAAAANwAAAAMAAABJAAAAAAAAAFsAAAADAAAAJgAAAAAAAAAvAAAAAwAAAEAAAAADAAAAIgAAAAMAAAAzAAAAAAAAAEUAAAADAAAAJAAAAAMAAAA2AAAAAAAAAEcAAAAAAAAAYAAAAAAAAABoAAAAAAAAAGsAAAADAAAAYgAAAAAAAABuAAAAAwAAAHMAAAADAAAAYQAAAAMAAABvAAAAAwAAAHcAAAADAAAATAAAAAAAAABWAAAAAwAAAF4AAAADAAAAUgAAAAAAAABgAAAAAAAAAGgAAAAAAAAAVAAAAAMAAABiAAAAAAAAAG4AAAADAAAAOgAAAAAAAABBAAAAAwAAAEsAAAADAAAAPgAAAAMAAABMAAAAAAAAAFYAAAADAAAAQAAAAAMAAABSAAAAAAAAAGAAAAAAAAAAVQAAAAAAAABXAAAAAAAAAFMAAAADAAAAZQAAAAAAAABmAAAAAwAAAGQAAAADAAAAawAAAAMAAABwAAAAAwAAAHIAAAADAAAAQgAAAAAAAABDAAAAAwAAAEYAAAADAAAAUQAAAAAAAABVAAAAAAAAAFcAAAAAAAAAXgAAAAMAAABlAAAAAAAAAGYAAAADAAAAMQAAAAAAAAAwAAAAAwAAADIAAAADAAAAPQAAAAMAAABCAAAAAAAAAEMAAAADAAAASwAAAAMAAABRAAAAAAAAAFUAAAAAAAAAXwAAAAAAAABcAAAAAAAAAFMAAAAAAAAATwAAAAAAAABOAAAAAAAAAEoAAAADAAAAPwAAAAEAAAA7AAAAAwAAADkAAAADAAAAbQAAAAAAAABsAAAAAAAAAGQAAAAFAAAAXQAAAAEAAABfAAAAAAAAAFwAAAAAAAAATQAAAAEAAABPAAAAAAAAAE4AAAAAAAAAdQAAAAQAAAB2AAAABQAAAHIAAAAFAAAAagAAAAEAAABtAAAAAAAAAGwAAAAAAAAAWgAAAAEAAABdAAAAAQAAAF8AAAAAAAAAWgAAAAAAAABNAAAAAAAAAD8AAAAAAAAAUAAAAAAAAABEAAAAAAAAADgAAAADAAAASAAAAAEAAAA8AAAAAwAAAC4AAAADAAAAagAAAAAAAABdAAAAAAAAAE8AAAAFAAAAYwAAAAEAAABaAAAAAAAAAE0AAAAAAAAAWAAAAAEAAABQAAAAAAAAAEQAAAAAAAAAdQAAAAMAAABtAAAABQAAAF8AAAAFAAAAcQAAAAEAAABqAAAAAAAAAF0AAAAAAAAAaQAAAAEAAABjAAAAAQAAAFoAAAAAAAAAaQAAAAAAAABYAAAAAAAAAEgAAAAAAAAAZwAAAAAAAABbAAAAAAAAAEkAAAADAAAAYQAAAAEAAABZAAAAAwAAAEcAAAADAAAAcQAAAAAAAABjAAAAAAAAAFAAAAAFAAAAdAAAAAEAAABpAAAAAAAAAFgAAAAAAAAAbwAAAAEAAABnAAAAAAAAAFsAAAAAAAAAdQAAAAIAAABqAAAABQAAAFoAAAAFAAAAeQAAAAEAAABxAAAAAAAAAGMAAAAAAAAAdwAAAAEAAAB0AAAAAQAAAGkAAAAAAAAAdwAAAAAAAABvAAAAAAAAAGEAAAAAAAAAcwAAAAAAAABuAAAAAAAAAGIAAAADAAAAawAAAAEAAABoAAAAAwAAAGAAAAADAAAAeQAAAAAAAAB0AAAAAAAAAGcAAAAFAAAAeAAAAAEAAAB3AAAAAAAAAG8AAAAAAAAAcAAAAAEAAABzAAAAAAAAAG4AAAAAAAAAdQAAAAEAAABxAAAABQAAAGkAAAAFAAAAdgAAAAEAAAB5AAAAAAAAAHQAAAAAAAAAcgAAAAEAAAB4AAAAAQAAAHcAAAAAAAAAcgAAAAAAAABwAAAAAAAAAGsAAAAAAAAAZAAAAAAAAABmAAAAAAAAAGUAAAADAAAAUwAAAAEAAABXAAAAAwAAAFUAAAADAAAAdgAAAAAAAAB4AAAAAAAAAHMAAAAFAAAAbAAAAAEAAAByAAAAAAAAAHAAAAAAAAAAXAAAAAEAAABkAAAAAAAAAGYAAAAAAAAAdQAAAAAAAAB5AAAABQAAAHcAAAAFAAAAbQAAAAEAAAB2AAAAAAAAAHgAAAAAAAAAXwAAAAEAAABsAAAAAQAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAGAAAAAgAAAAUAAAABAAAABAAAAAAAAAAAAAAABQAAAAMAAAABAAAABgAAAAQAAAACAAAAAAAAAH6iBfbytuk/Gq6akm/58z/Xrm0Liez0P5doSdOpSwRAWs602ULg8D/dT7Rcbo/1v1N1RQHFNOM/g9Snx7HW3L8HWsP8Q3jfP6VwOLosutk/9rjk1YQcxj+gnmKMsNn6P/HDeuPFY+M/YHwDjqKhB0Ci19/fCVrbP4UxKkDWOP6/pvljWa09tL9wi7wrQXjnv/Z6yLImkM2/3yTlOzY14D+m+WNZrT20PzwKVQnrQwNA9nrIsiaQzT/g40rFrRQFwPa45NWEHMa/kbslHEZq97/xw3rjxWPjv4cLC2SMBci/otff3wla27+rKF5oIAv0P1N1RQHFNOO/iDJPGyWHBUAHWsP8Q3jfvwQf/by16gXAfqIF9vK26b8XrO0Vh0r+v9eubQuJ7PS/BxLrA0ZZ479azrTZQuDwv1MK1EuItPw/yscgV9Z6FkAwHBR2WjQMQJNRzXsQ5vY/GlUHVJYKF0DONuFv2lMNQNCGZ28QJfk/0WUwoIL36D8ggDOMQuATQNqMOeAy/wZAWFYOYM+M2z/LWC4uH3oSQDE+LyTsMgRAkJzhRGWFGEDd4soovCQQQKqk0DJMEP8/rGmNdwOLBUAW2X/9xCbjP4hu3dcqJhNAzuYItRvdB0CgzW3zJW/sPxotm/Y2TxRAQAk9XmdDDEC1Kx9MKgT3P1M+NctcghZAFVqcLlb0C0Bgzd3sB2b2P77mZDPUWhZAFROHJpUGCEDAfma5CxXtPz1DWq/zYxRAmhYY5824F0DOuQKWSbAOQNCMqrvu3fs/L6DR22K2wT9nAAxPBU8RQGiN6mW43AFAZhu25b633D8c1YgmzowSQNM25BRKWARArGS08/lNxD+LFssHwmMRQLC5aNcxBgJABL9HT0WRF0CjCmJmOGEOQHsuaVzMP/s/TWJCaGGwBUCeu1PAPLzjP9nqN9DZOBNAKE4JcydbCkCGtbd1qjPzP8dgm9U8jhVAtPeKTkVwDkCeCLss5l37P401XMPLmBdAFd29VMVQDUBg0yA55h75Pz6odcYLCRdApBM4rBrkAkDyAVWgQxbRP4XDMnK20hFAymLlF7EmzD8GUgo9XBHlP3lbK7T9COc/k+OhPthhy7+YGEpnrOvCPzBFhLs15u4/epbqB6H4uz9IuuLF5svev6lzLKY31es/CaQ0envF5z8ZY0xlUADXv7zaz7HYEuI/CfbK1sn16T8uAQfWwxLWPzKn/YuFN94/5KdbC1AFu793fyCSnlfvPzK2y4doAMY/NRg5t1/X6b/shq4QJaHDP5yNIAKPOeI/vpn7BSE30r/X4YQrO6nrv78Ziv/Thto/DqJ1Y6+y5z9l51NaxFrlv8QlA65HOLS/86dxiEc96z+Hj0+LFjneP6LzBZ8LTc2/DaJ1Y6+y579l51NaxFrlP8QlA65HOLQ/8qdxiEc967+Jj0+LFjnev6LzBZ8LTc0/1qdbC1AFuz93fyCSnlfvvzK2y4doAMa/NRg5t1/X6T/vhq4QJaHDv5yNIAKPOeK/wJn7BSE30j/W4YQrO6nrP78Ziv/Thtq/CaQ0envF578XY0xlUADXP7zaz7HYEuK/CvbK1sn16b8rAQfWwxLWvzKn/YuFN96/zWLlF7EmzL8GUgo9XBHlv3lbK7T9COe/kOOhPthhyz+cGEpnrOvCvzBFhLs15u6/c5bqB6H4u79IuuLF5sveP6lzLKY31eu/AQAAAP////8HAAAA/////zEAAAD/////VwEAAP////9hCQAA/////6dBAAD/////kcsBAP/////3kAwA/////8H2VwAAAAAAAAAAAAAAAAACAAAA/////w4AAAD/////YgAAAP////+uAgAA/////8ISAAD/////ToMAAP////8ilwMA/////+4hGQD/////gu2vAAAAAAAAAAAAAAAAAAAAAAACAAAA//////////8BAAAAAwAAAP//////////////////////////////////////////////////////////////////////////AQAAAAAAAAACAAAA////////////////AwAAAP//////////////////////////////////////////////////////////////////////////AQAAAAAAAAACAAAA////////////////AwAAAP//////////////////////////////////////////////////////////////////////////AQAAAAAAAAACAAAA////////////////AwAAAP//////////////////////////////////////////////////////////AgAAAP//////////AQAAAAAAAAD/////////////////////AwAAAP////////////////////////////////////////////////////8DAAAA/////////////////////wAAAAD/////////////////////AQAAAP///////////////wIAAAD///////////////////////////////8DAAAA/////////////////////wAAAAD///////////////8CAAAAAQAAAP////////////////////////////////////////////////////8DAAAA/////////////////////wAAAAD///////////////8CAAAAAQAAAP////////////////////////////////////////////////////8DAAAA/////////////////////wAAAAD///////////////8CAAAAAQAAAP////////////////////////////////////////////////////8DAAAA/////////////////////wAAAAD///////////////8CAAAAAQAAAP////////////////////////////////////////////////////8BAAAAAgAAAP///////////////wAAAAD/////////////////////AwAAAP////////////////////////////////////////////////////8BAAAAAgAAAP///////////////wAAAAD/////////////////////AwAAAP////////////////////////////////////////////////////8BAAAAAgAAAP///////////////wAAAAD/////////////////////AwAAAP////////////////////////////////////////////////////8BAAAAAgAAAP///////////////wAAAAD/////////////////////AwAAAP///////////////////////////////wIAAAD///////////////8BAAAA/////////////////////wAAAAD/////////////////////AwAAAP////////////////////////////////////////////////////8DAAAA/////////////////////wAAAAABAAAA//////////8CAAAA//////////////////////////////////////////////////////////8DAAAA////////////////AgAAAAAAAAABAAAA//////////////////////////////////////////////////////////////////////////8DAAAA////////////////AgAAAAAAAAABAAAA//////////////////////////////////////////////////////////////////////////8DAAAA////////////////AgAAAAAAAAABAAAA//////////////////////////////////////////////////////////////////////////8DAAAAAQAAAP//////////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAACAAAAAAAAAAIAAAABAAAAAQAAAAIAAAACAAAAAAAAAAUAAAAFAAAAAAAAAAIAAAACAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAEAAAACAAAAAgAAAAIAAAAAAAAABQAAAAYAAAAAAAAAAgAAAAIAAAADAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAAAAAACAAAAAQAAAAMAAAACAAAAAgAAAAAAAAAFAAAABwAAAAAAAAACAAAAAgAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAIAAAABAAAABAAAAAIAAAACAAAAAAAAAAUAAAAIAAAAAAAAAAIAAAACAAAAAwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAIAAAAAAAAAAgAAAAEAAAAAAAAAAgAAAAIAAAAAAAAABQAAAAkAAAAAAAAAAgAAAAIAAAADAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAgAAAAIAAAAAAAAAAwAAAA4AAAACAAAAAAAAAAIAAAADAAAAAAAAAAAAAAACAAAAAgAAAAMAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAACAAAAAgAAAAAAAAADAAAACgAAAAIAAAAAAAAAAgAAAAMAAAABAAAAAAAAAAIAAAACAAAAAwAAAAcAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAIAAAACAAAAAAAAAAMAAAALAAAAAgAAAAAAAAACAAAAAwAAAAIAAAAAAAAAAgAAAAIAAAADAAAACAAAAAAAAAAAAAAAAAAAAAAAAAANAAAAAgAAAAIAAAAAAAAAAwAAAAwAAAACAAAAAAAAAAIAAAADAAAAAwAAAAAAAAACAAAAAgAAAAMAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAACAAAAAgAAAAAAAAADAAAADQAAAAIAAAAAAAAAAgAAAAMAAAAEAAAAAAAAAAIAAAACAAAAAwAAAAoAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAIAAAACAAAAAAAAAAMAAAAGAAAAAgAAAAAAAAACAAAAAwAAAA8AAAAAAAAAAgAAAAIAAAADAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAgAAAAIAAAAAAAAAAwAAAAcAAAACAAAAAAAAAAIAAAADAAAAEAAAAAAAAAACAAAAAgAAAAMAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAACAAAAAgAAAAAAAAADAAAACAAAAAIAAAAAAAAAAgAAAAMAAAARAAAAAAAAAAIAAAACAAAAAwAAAA0AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAIAAAACAAAAAAAAAAMAAAAJAAAAAgAAAAAAAAACAAAAAwAAABIAAAAAAAAAAgAAAAIAAAADAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAgAAAAIAAAAAAAAAAwAAAAUAAAACAAAAAAAAAAIAAAADAAAAEwAAAAAAAAACAAAAAgAAAAMAAAAPAAAAAAAAAAAAAAAAAAAAAAAAABAAAAACAAAAAAAAAAIAAAABAAAAEwAAAAIAAAACAAAAAAAAAAUAAAAKAAAAAAAAAAIAAAACAAAAAwAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAIAAAAAAAAAAgAAAAEAAAAPAAAAAgAAAAIAAAAAAAAABQAAAAsAAAAAAAAAAgAAAAIAAAADAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAASAAAAAgAAAAAAAAACAAAAAQAAABAAAAACAAAAAgAAAAAAAAAFAAAADAAAAAAAAAACAAAAAgAAAAMAAAASAAAAAAAAAAAAAAAAAAAAAAAAABMAAAACAAAAAAAAAAIAAAABAAAAEQAAAAIAAAACAAAAAAAAAAUAAAANAAAAAAAAAAIAAAACAAAAAwAAABMAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAIAAAAAAAAAAgAAAAEAAAASAAAAAgAAAAIAAAAAAAAABQAAAA4AAAAAAAAAAgAAAAIAAAADAAAAAgAAAAEAAAAAAAAAAQAAAAIAAAAAAAAAAAAAAAIAAAABAAAAAAAAAAEAAAACAAAAAQAAAAAAAAACAAAAAAAAAAUAAAAEAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAFAAAABAAAAAAAAAABAAAABQAAAAQAAAAAAAAABQAAAAAAAAACAAAAAQAAAAAAAAABAAAAAgAAAAAAAAAAAAAAAgAAAAEAAAAAAAAAAQAAAAIAAAABAAAAAAAAAAIAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAUAAAAEAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAFAAAABAAAAAAAAAABAAAABQAAAAQAAAAAAAAABQAAAAUAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAABAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAQAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAABAAAAAAAAAAAAAQAAAAAAAAAAAAA6B6FaUp9QQTPXMuL4myJBraiDfBwx9UBYJseitzTIQOL5if9jqZtAnXX+Z+ycb0C3pucbhRBCQG8wJBYqpRRAlWbDCzCY5z/eFWBUEve6P/+qo4Q50Y4/D9YM3iCcYT8fcA2QJSA0P4ADxu0qAAc/BNcGolVJ2j5d9FACqwquPh9z7MthtI9CSUSYJke/YUJQ/64OyjU0Qpi0+HCmFQdCm3GfIVdh2kHsJ11kAyauQYC3UDFJOoFBSJsFV1OwU0FK5fcxX4AmQWhy/zZIt/lACqaCPsBjzUDbdUNIScugQMYQlVJ4MXNANiuq8GTvRUDxTXnulxEZQFZ8QX5kpuw/qmG/JwYFlEAluh3Q6DB+QKn4vyNq0GZAKOXekas+UUB8xabXXhI6QG63C2pLtSNAdDBtyNfLDUDyOcu67ID2P0rCMvRXAeE/Ki2TSVyzyT9Dk+8Sz2uzP5J+w5ARWp0/NQAoOiMuhj9YnP+RyMJwPxgW7TvQVFk/KgsLYF0kQz9g5dAC6IwzQcgHPVvDex1B1XjppodHBkHJq3OMM9fwQNvcmJ7wddlAInGPpQs/w0BRobq5EBmtQJZ2ai7n+ZVAtv2G5E+bgECG+gIfKBlpQK5f8jdI91JAL39sL/WpPEB8rGxhDqklQK6yUf43XhBAxL9y/tK8+D86XyZpgrHiPwAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////AAAAAP////8AAAAAAAAAAAAAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAP////8FAAAABQAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////////////////////////////wAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////AAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAEAAAAAAAAABQAAAAEAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAAAQABAAABAQAAAAAAAQAAAAEAAAABAAEAAAAAAAAAAAAAAAAAAAAAquJYWJZl+D9jaeZNtj/zPwwdI9KqaeO/qGefXwdHdz+q4lhYlmX4P+OrlPMN3PI/DB0j0qpp47+7SQLV4VIEQKriWFiWZfg/r2kma3tz8T82eQmLqNIGwMRIWXMqSvo/fcCszPux9j+jara6ozTwP6hnn18HR3c/MSoKLequ8r+SabgA2nj0P7jBLbDOHO8/1Ym/ICfH4T+6lxjvlFXHv73m373LRPU/0vXyDVxo7T+ToKRHJXMAQF/33578aPE/pAyy64tD9T8+U/hCvyruPwxv8Y7YYwLAuXYr8NAiCEB4+LDK0Sn0P1Qeuy4j+eo/OMx50n7K7L+TrGB/nyf8v5ehC2fbYPM/aXMKexiT6z8mFRIMjg/zP7yUVwGGBNw/E6opHERf8z/z0wR2g9DqPw4pBpcOhvu/NbA29uWAA8DMaTExyXzyP02biiQ+Ruk/S8jz2/FKBEB1pzZnpbb9P7pQU4wLfPI//7ZcQXeG6D9CqEQvAYoIwDB2VB6sSgRAVyv8H5We8T+EHWF8XNPmPzB2wT8Nrrg/SEi+cX+w4L8of+GtdSDxP1sjk5AdouU/6ZjOVru13r8K0obqI6bxvwVbdNXyhfA/w5GG024n5z+rwmtMzP8BwLw9pSX49QXABe/2uQxP8D+b6wCzCvXkP7uGT87fK+Q/pz/JWw4coj+qoBf2J0nwP/yE3PUo0+I/vFJeHcaC+D96luSIqvntP/bf8sHUYu8/gZNN41mL4z9bhOqVOF4FwO6lmAh1hQhAbCVxbdhk7z+1C8NdDcfiPwG36x/0OQBAx0WJ76c2+D9nlSHXANfuP2HlfZ3gqOE/EwnVlVPg9r96+oHzEH//v5bXzdT1Auw/DM3GwLsA4D9p/8uoKcr+v+U9x5DQVAPAehjSdghb7D9sc1IetODgP8MVwwB1pu6/azPk6OGe978W8t/TUc3rP+0QMvYfP+A/RsG/QpSE8D+l3uwScxzgPwQaifgujuw/k1Vti1I43z8MAwLnSh0GQH5nYnwwZgJAiGUzWC5s6j8WyyI/BbLgPw4iUapGeQJAB3W+imnp/j9BLWR4ssrpP2t+gG5Pstk/cpBsfm6DCMCOpU9dOZsFQEv8nFypHeo/ehJ6i+6S2D9jqlGEmarLv7STC5TRiOa/bC+x8WZD6D9H3yUkWpDZP8gZvmCMuQLAreY19/eRBsCoPOc8UzzpP6KI/QV+y9g/t/MoboyWzT+Hv5q3Zu3Mvy2xROCT4uY/9gQitMMg1T9abAqhWMDkv1oLTavoUfG/PMUJP9CD5j+fHRX3t6fSPz7W2gk6bvs/WRnuHwqN9D8YFturGCTmP1EZczv0b9I/5t4exabB5D/1ESLh5fTEP9X2z6SYweQ/6lv3I2zT0D9zkRGNUNMAQKoSvc4EIfs/Xggt8wQI5T+mJHHg/w/SP4lhT/9t8vQ/DrZ/DbwH7D+XlhbYZrjkP34LIpFt6c4/lwfp8fLX9L+j96CTTf76v3WdNhEv9uM/d8c3o4lV0D/vFdCHVcsFwAHeDq0F1QhApbYqcZiN5D9KoilqByXLPwX0/diA0vq/0fo0GxnxAMBbaTkvlCzjP/RrFrWXrMs/UYTrky7jA0DB9f4FiZYAQEGAk/3QzeE/r/TeqE8t0D/OqjlsnPbvvz8RKU8JOfW/smSEbK/O4T8MzuyPm3DDP/rFtctq9gZAfb1EVEaSA0Dts5dVInnhP18SFMc79MM/7y34cw6LAMDFrRJsZO0DwC2KLvLSYuA/hx5wcUHewz+49SnK/4ruPyeS0PX9a+E/ZxaaLvvZ3z8WPu5T2QS8Pygo4RIvMqa/BJ0Kqsd0279cKW4ay8jdP3b05bmZ364/10/qtdxk2r+Bcz6CDMvpv54qOw+Amdw/qLV71pW7sT/YKc80nIPUP8OfIaBJ77G/LyTuD1un2z+diYu8efWzP1wU7ACkfwjAZroyPL1yBkAmv3lKJJbbPysKSE4W+p0/dIgqY79TA8ATLTOQ3tsGwJ2zweD/Xdg/XO/jXeFUaL8VW2qLFKfov1cA9Aa6XfK/tIa7YGgI2T+f3hu/sxqPv2nXdPpf3Pc/jkw8Jbda8j+tT/z8tGPVP1yBHpJd35k/KYvYOy1s8j/yz+kCQjPrP9+agH7x59g/PZfJ9aBhpr/rDKzvYBb+PwtkiaGCt/c/vb1mVr+f1T/JIHwHc8Govw7aeF6+9vG/Xv7kD6fp979isYioQYHVP7AIQZuSFrG/3z1AdUTnAUDN3XY9O7f9P0AdQ9ljYNQ/dJANJPTOrb8kLECUiiPlP4yF7UgmStA/9xGmXxCG1T9qZzix4W2zv2SGJRJVrPe/Fh9a2M/B/b8IexzFCoPSP9y1QFD2bLe/Q86cWLJe/b+mOOfYm78BwOTjkPAGE9E/8aPCUKu/ub9pPZyLCiUGwBA7Mev/BQlALOmrlRi+0j+AMJ/dKULBv7iLtL6a6QRAEMDV/yajAUDa62dE3crJP1P70RgBUbq/38hVnR6esT/s1tG10Z/Ov/zLwalHPss/dTS9NKTXx78nMcRzCIEHQAabxDsAmQRA0tyLK3gSyT+Aui7nOhDGv5Gs58z3WgHATN3forJuBMCAui7nOhDGP9Lciyt4Esm/WAJyHQ4c7z8UP5HFIs3iP3U0vTSk18c//MvBqUc+y7+cvv8HLg/Kvy1I/mHsI+K/U/vRGAFRuj/a62dE3crJv8p+WV8KlQjAuQ/nOP43B0CAMJ/dKULBPyzpq5UYvtK/ZoU+VoLh4L9etLlRUfvtv/GjwlCrv7k/5OOQ8AYT0b9DfT9FhufXPwUX8hJp+4u/3LVAUPZstz8IexzFCoPSv9+L609E5fQ/q9Fz7X2J7T9qZzix4W2zP/cRpl8QhtW/vtNilqGX+j8MOy7QJoL0P3SQDST0zq0/QB1D2WNg1L8IIjSvGNkDwGB8Jou2GAfAsAhBm5IWsT9isYioQYHVvyS9D3zb6uy/gnwRa7uM9L/JIHwHc8GoP729Zla/n9W/CsAHJZwmAEDEW6OYT1r6Pz2XyfWgYaY/35qAfvHn2L83Tdy4lS30vxf2/gZ0jPq/XIEekl3fmb+tT/z8tGPVvybPr2zJ1/+/K7mJ0ypVAsCf3hu/sxqPPwCGu2BoCNm/5oITrpZn+r+UDUyDP+n/v1zv413hVGg/nbPB4P9d2L9MlmkxNvgCQMtZlKE85v8/KwpIThb6nb8mv3lKJJbbv8+SZsTvOOc/pQCIIOYw0j+diYu8efWzvy8k7g9bp9u/kxYDa+pKtD9XlYvA8HnVv6i1e9aVu7G/nio7D4CZ3L/WR6rNh5EGwCkgQweBkghAdvTluZnfrr9cKW4ay8jdvxbjhr1f1QVAR5C0MzivAkAWPu5T2QS8v2cWmi772d+/cKj4lzLJCEBx2QJfYrMFQIcecHFB3sO/LYou8tJi4L+jr7lhO38BwIcI0Nb7xgTAXxIUxzv0w7/ts5dVInnhv0T+l8DZLfE/MP3FoFvS5D8MzuyPm3DDv7JkhGyvzuG/tzhzRIRc0b9Ovv3/0z7mv6/03qhPLdC/m4CT/dDN4b9dwjU5VCQBQBBJX1ntCv0/9GsWtZesy79baTkvlCzjv1mjYgEz++S/oW6KnOQW8b9KoilqByXLv6W2KnGYjeS/SmaKz3Vx9z+BZB5yxGHwP3fHN6OJVdC/dZ02ES/2478PuaBjLrXaP4/JU81pPaO/fgsikW3pzr+XlhbYZrjkv4tSn7YDbP0/f2LnFKlF9z+mJHHg/w/Sv14ILfMECOW/mfg4qYhR/b+OP+RQDCACwOpb9yNs09C/1fbPpJjB5L9pN2WOVZ3wv3hHy9nxIve/URlzO/Rv0r8YFturGCTmv1d1/KKR8QPA8gsy9qzSB8CfHRX3t6fSvzzFCT/Qg+a/EYStnrzV9r/2QJqI7Lb9v/YEIrTDINW/LbFE4JPi5r/7kQEs5fEDQHunnf4GeQBAooj9BX7L2L+oPOc8Uzzpv+ydYY2SSAfAL4HK6CRTB0BH3yUkWpDZv2wvsfFmQ+i/Ik0Yzruh6T8fM3LoGoDUP3oSeovukti/S/ycXKkd6r9rEv+7UWcHQCRIQe/GfwNAa36Abk+y2b9BLWR4ssrpv9KT87qa0bM/FTyktw823L8WyyI/BbLgv4hlM1gubOq/DizMp9Ki6r8b5ckdjVrzv5NVbYtSON+/BBqJ+C6O7L/dUBFqgyXYv00Wh18r7+q/7RAy9h8/4L8W8t/TUc3rv4RM5DKx3wDAfvWIj94aBcBsc1IetODgv3oY0nYIW+y/oGcTFF54AUDkJqS/FKX6PwzNxsC7AOC/ltfN1PUC7L+5Wrz/zHnzP6688w2rNOc/YeV9neCo4b9nlSHXANfuvw9RsxKjY/s/1V8GteXE8j+1C8NdDcfiv2wlcW3YZO+/IOywaA7Q8b9bFP+4Tg36v4GTTeNZi+O/9t/ywdRi77+tRc3yFR7eP2bkcHXJkLO//ITc9SjT4r+qoBf2J0nwv2YHKoswwfm/iQcLspCjAcCb6wCzCvXkvwXv9rkMT/C/YkuwYAMXBMApCNUai9kIwMORhtNuJ+e/BVt01fKF8L+ZqWEfvIjsP6h693QZYNk/WyOTkB2i5b8of+GtdSDxvwpaaulDSwVADMQAX+lOAECEHWF8XNPmv1cr/B+VnvG/XyFG6opcCMD/mtR32/UEQP+2XEF3hui/ulBTjAt88r/imfCfRP+yP9zbvtc8XeO/TZuKJD5G6b/MaTExyXzyvxiTQeElXOO/rbJRQVGN9L/z0wR2g9DqvxOqKRxEX/O/FDGCEei99j9x8zV4VYTmP2lzCnsYk+u/l6ELZ9tg878pRXacaDT/v3k6GZRqoQXAVB67LiP56r94+LDK0Sn0vwO6pZ9b7wFAvK0nKVcc9j8+U/hCvyruv6QMsuuLQ/W/FPhKFYv46j8MyxaDTOW/v9L18g1caO2/vebfvctE9b/7GD8ZrF3xv3gx1AR9bQDAuMEtsM4c77+SabgA2nj0v5xKFIwxsATArKNSBaKsB0Cjara6ozTwv33ArMz7sfa/dF2U0FcWCcDxL357DJX/P69pJmt7c/G/quJYWJZl+L/YntVJlnrSP4sRLzXM+fe/46uU8w3c8r+q4lhYlmX4v85lu5+QRwRAsI0H/WU8479jaeZNtj/zv6riWFiWZfi/sI0H/WU847/OZbufkEcEQHAoPUBrnss/9exKzDtFtT88wM8kax+gP9OqeKeAYog/MW0ItiZvcj+ph+smvt5bP2lCaV5dEUU/StaUmQDaLz+kK9y22BMYP0O3whZuMwI/IIbgZGWE6z7UkjYaEM3UPuezxwa9cr8+LybxRMnFpz6E1N8DbPiRPsYjySMvK3s+//////8fAAj//////zMQCP////9/MiAI/////28yMAj/////YzJACP///z9iMlAI////N2IyYAj///8zYjJwCP//vzNiMoAI//+rM2IykAj/f6szYjKgCP8PqzNiMrAI/wOrM2IywAi/A6szYjLQCJ8DqzNiMuAImQOrM2Iy8Aj//////z8PCP//////Kx8I/////38pLwj/////Pyk/CP////85KU8I////PzgpXwj///8POClvCP///w44KX8I//8fDjgpjwj//w8OOCmfCP9/DQ44Ka8I/w8NDjgpvwj/DQ0OOCnPCP8MDQ44Kd8IxwwNDjgp7wjEDA0OOCn/CAcAAAAHAAAAAQAAAAIAAAAEAAAAAwAAAAAAAAAAAAAABwAAAAMAAAABAAAAAgAAAAUAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAAAAAACAAAAAQAAAAMAAAAOAAAABgAAAAsAAAACAAAABwAAAAEAAAAYAAAABQAAAAoAAAABAAAABgAAAAAAAAAmAAAABwAAAAwAAAADAAAACAAAAAIAAAAxAAAACQAAAA4AAAAAAAAABQAAAAQAAAA6AAAACAAAAA0AAAAEAAAACQAAAAMAAAA/AAAACwAAAAYAAAAPAAAACgAAABAAAABIAAAADAAAAAcAAAAQAAAACwAAABEAAABTAAAACgAAAAUAAAATAAAADgAAAA8AAABhAAAADQAAAAgAAAARAAAADAAAABIAAABrAAAADgAAAAkAAAASAAAADQAAABMAAAB1AAAADwAAABMAAAARAAAAEgAAABAAAAAGAAAAAgAAAAMAAAAFAAAABAAAAAAAAAAAAAAAAAAAAAYAAAACAAAAAwAAAAEAAAAFAAAABAAAAAAAAAAAAAAABwAAAAUAAAADAAAABAAAAAEAAAAAAAAAAgAAAAAAAAACAAAAAwAAAAEAAAAFAAAABAAAAAYAAAAAAAAAAAAAABgtRFT7Ifk/GC1EVPsh+b8YLURU+yEJQBgtRFT7IQnAYWxnb3MuYwBoM05laWdoYm9yUm90YXRpb25zAGNvb3JkaWprLmMAX3VwQXA3Q2hlY2tlZABfdXBBcDdyQ2hlY2tlZABkaXJlY3RlZEVkZ2UuYwBkaXJlY3RlZEVkZ2VUb0JvdW5kYXJ5AGFkamFjZW50RmFjZURpclt0bXBGaWprLmZhY2VdW2ZpamsuZmFjZV0gPT0gS0kAZmFjZWlqay5jAF9mYWNlSWprUGVudFRvQ2VsbEJvdW5kYXJ5AGFkamFjZW50RmFjZURpcltjZW50ZXJJSksuZmFjZV1bZmFjZTJdID09IEtJAF9mYWNlSWprVG9DZWxsQm91bmRhcnkAaDNJbmRleC5jAGNvbXBhY3RDZWxscwBsYXRMbmdUb0NlbGwAY2VsbFRvQ2hpbGRQb3MAdmFsaWRhdGVDaGlsZFBvcwBsYXRMbmcuYwBjZWxsQXJlYVJhZHMyAHBvbHlnb24tPm5leHQgPT0gTlVMTABsaW5rZWRHZW8uYwBhZGROZXdMaW5rZWRQb2x5Z29uAG5leHQgIT0gTlVMTABsb29wICE9IE5VTEwAYWRkTmV3TGlua2VkTG9vcABwb2x5Z29uLT5maXJzdCA9PSBOVUxMAGFkZExpbmtlZExvb3AAY29vcmQgIT0gTlVMTABhZGRMaW5rZWRDb29yZABsb29wLT5maXJzdCA9PSBOVUxMAGlubmVyTG9vcHMgIT0gTlVMTABub3JtYWxpemVNdWx0aVBvbHlnb24AYmJveGVzICE9IE5VTEwAY2FuZGlkYXRlcyAhPSBOVUxMAGZpbmRQb2x5Z29uRm9ySG9sZQBjYW5kaWRhdGVCQm94ZXMgIT0gTlVMTAByZXZEaXIgIT0gSU5WQUxJRF9ESUdJVABsb2NhbGlqLmMAY2VsbFRvTG9jYWxJamsAYmFzZUNlbGwgIT0gb3JpZ2luQmFzZUNlbGwAIShvcmlnaW5PblBlbnQgJiYgaW5kZXhPblBlbnQpAGJhc2VDZWxsID09IG9yaWdpbkJhc2VDZWxsAGJhc2VDZWxsICE9IElOVkFMSURfQkFTRV9DRUxMAGxvY2FsSWprVG9DZWxsACFfaXNCYXNlQ2VsbFBlbnRhZ29uKGJhc2VDZWxsKQBiYXNlQ2VsbFJvdGF0aW9ucyA+PSAwAGdyaWRQYXRoQ2VsbHMAcG9seWZpbGwuYwBpdGVyU3RlcFBvbHlnb25Db21wYWN0ADAAdmVydGV4LmMAY2VsbFRvVmVydGV4AGdyYXBoLT5idWNrZXRzICE9IE5VTEwAdmVydGV4R3JhcGguYwBpbml0VmVydGV4R3JhcGgAbm9kZSAhPSBOVUxMAGFkZFZlcnRleE5vZGU=\";\r\n  var tempDoublePtr = 28624;\r\n  function demangle(func) {\r\n    return func;\r\n  }\r\n  function demangleAll(text) {\r\n    var regex = /\\b__Z[\\w\\d_]+/g;\r\n    return text.replace(regex, function (x) {\r\n      var y = demangle(x);\r\n      return x === y ? x : y + \" [\" + x + \"]\";\r\n    });\r\n  }\r\n  function jsStackTrace() {\r\n    var err = new Error();\r\n    if (!err.stack) {\r\n      try {\r\n        throw new Error(0);\r\n      } catch (e) {\r\n        err = e;\r\n      }\r\n      if (!err.stack) {\r\n        return \"(no stack trace available)\";\r\n      }\r\n    }\r\n    return err.stack.toString();\r\n  }\r\n  function stackTrace() {\r\n    var js = jsStackTrace();\r\n    if (Module[\"extraStackTrace\"]) { js += \"\\n\" + Module[\"extraStackTrace\"](); }\r\n    return demangleAll(js);\r\n  }\r\n  function ___assert_fail(condition, filename, line, func) {\r\n    abort(\"Assertion failed: \" + UTF8ToString(condition) + \", at: \" + [filename ? UTF8ToString(filename) : \"unknown filename\", line, func ? UTF8ToString(func) : \"unknown function\"]);\r\n  }\r\n  function _emscripten_get_heap_size() {\r\n    return HEAP8.length;\r\n  }\r\n  function _emscripten_memcpy_big(dest, src, num) {\r\n    HEAPU8.set(HEAPU8.subarray(src, src + num), dest);\r\n  }\r\n  function ___setErrNo(value) {\r\n    if (Module[\"___errno_location\"]) { HEAP32[Module[\"___errno_location\"]() >> 2] = value; }\r\n    return value;\r\n  }\r\n  function abortOnCannotGrowMemory(requestedSize) {\r\n    abort(\"OOM\");\r\n  }\r\n  function emscripten_realloc_buffer(size) {\r\n    try {\r\n      var newBuffer = new ArrayBuffer(size);\r\n      if (newBuffer.byteLength != size) { return; }\r\n      new Int8Array(newBuffer).set(HEAP8);\r\n      _emscripten_replace_memory(newBuffer);\r\n      updateGlobalBufferAndViews(newBuffer);\r\n      return 1;\r\n    } catch (e) {}\r\n  }\r\n  function _emscripten_resize_heap(requestedSize) {\r\n    var oldSize = _emscripten_get_heap_size();\r\n    var PAGE_MULTIPLE = 16777216;\r\n    var LIMIT = 2147483648 - PAGE_MULTIPLE;\r\n    if (requestedSize > LIMIT) {\r\n      return false;\r\n    }\r\n    var MIN_TOTAL_MEMORY = 16777216;\r\n    var newSize = Math.max(oldSize, MIN_TOTAL_MEMORY);\r\n    while (newSize < requestedSize) {\r\n      if (newSize <= 536870912) {\r\n        newSize = alignUp(2 * newSize, PAGE_MULTIPLE);\r\n      } else {\r\n        newSize = Math.min(alignUp((3 * newSize + 2147483648) / 4, PAGE_MULTIPLE), LIMIT);\r\n      }\r\n    }\r\n    var replacement = emscripten_realloc_buffer(newSize);\r\n    if (!replacement) {\r\n      return false;\r\n    }\r\n    return true;\r\n  }\r\n  function intArrayToString(array) {\r\n    var ret = [];\r\n    for (var i = 0; i < array.length; i++) {\r\n      var chr = array[i];\r\n      if (chr > 255) {\r\n        chr &= 255;\r\n      }\r\n      ret.push(String.fromCharCode(chr));\r\n    }\r\n    return ret.join(\"\");\r\n  }\r\n  var decodeBase64 = typeof atob === \"function\" ? atob : function (input) {\r\n    var keyStr = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";\r\n    var output = \"\";\r\n    var chr1, chr2, chr3;\r\n    var enc1, enc2, enc3, enc4;\r\n    var i = 0;\r\n    input = input.replace(/[^A-Za-z0-9\\+\\/\\=]/g, \"\");\r\n    do {\r\n      enc1 = keyStr.indexOf(input.charAt(i++));\r\n      enc2 = keyStr.indexOf(input.charAt(i++));\r\n      enc3 = keyStr.indexOf(input.charAt(i++));\r\n      enc4 = keyStr.indexOf(input.charAt(i++));\r\n      chr1 = enc1 << 2 | enc2 >> 4;\r\n      chr2 = (enc2 & 15) << 4 | enc3 >> 2;\r\n      chr3 = (enc3 & 3) << 6 | enc4;\r\n      output = output + String.fromCharCode(chr1);\r\n      if (enc3 !== 64) {\r\n        output = output + String.fromCharCode(chr2);\r\n      }\r\n      if (enc4 !== 64) {\r\n        output = output + String.fromCharCode(chr3);\r\n      }\r\n    } while (i < input.length);\r\n    return output;\r\n  };\r\n  function intArrayFromBase64(s) {\r\n    if (typeof ENVIRONMENT_IS_NODE === \"boolean\" && ENVIRONMENT_IS_NODE) {\r\n      var buf;\r\n      try {\r\n        buf = Buffer.from(s, \"base64\");\r\n      } catch (_) {\r\n        buf = new Buffer(s, \"base64\");\r\n      }\r\n      return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\r\n    }\r\n    try {\r\n      var decoded = decodeBase64(s);\r\n      var bytes = new Uint8Array(decoded.length);\r\n      for (var i = 0; i < decoded.length; ++i) {\r\n        bytes[i] = decoded.charCodeAt(i);\r\n      }\r\n      return bytes;\r\n    } catch (_) {\r\n      throw new Error(\"Converting base64 string to bytes failed.\");\r\n    }\r\n  }\r\n  function tryParseAsDataURI(filename) {\r\n    if (!isDataURI(filename)) {\r\n      return;\r\n    }\r\n    return intArrayFromBase64(filename.slice(dataURIPrefix.length));\r\n  }\r\n  var asmGlobalArg = {\r\n    \"Math\": Math,\r\n    \"Int8Array\": Int8Array,\r\n    \"Int32Array\": Int32Array,\r\n    \"Uint8Array\": Uint8Array,\r\n    \"Float32Array\": Float32Array,\r\n    \"Float64Array\": Float64Array\r\n  };\r\n  var asmLibraryArg = {\r\n    \"a\": abort,\r\n    \"b\": setTempRet0,\r\n    \"c\": getTempRet0,\r\n    \"d\": ___assert_fail,\r\n    \"e\": ___setErrNo,\r\n    \"f\": _emscripten_get_heap_size,\r\n    \"g\": _emscripten_memcpy_big,\r\n    \"h\": _emscripten_resize_heap,\r\n    \"i\": abortOnCannotGrowMemory,\r\n    \"j\": demangle,\r\n    \"k\": demangleAll,\r\n    \"l\": emscripten_realloc_buffer,\r\n    \"m\": jsStackTrace,\r\n    \"n\": stackTrace,\r\n    \"o\": tempDoublePtr,\r\n    \"p\": DYNAMICTOP_PTR\r\n  }; // EMSCRIPTEN_START_ASM\r\n  var asm = (/** @suppress {uselessCode} */function (global, env, buffer) {\r\n    \"almost asm\";\r\n\r\n    var a = new global.Int8Array(buffer),\r\n      b = new global.Int32Array(buffer),\r\n      c = new global.Uint8Array(buffer),\r\n      d = new global.Float32Array(buffer),\r\n      e = new global.Float64Array(buffer),\r\n      f = env.o | 0,\r\n      g = env.p | 0,\r\n      p = global.Math.floor,\r\n      q = global.Math.abs,\r\n      r = global.Math.sqrt,\r\n      s = global.Math.pow,\r\n      t = global.Math.cos,\r\n      u = global.Math.sin,\r\n      v = global.Math.tan,\r\n      w = global.Math.acos,\r\n      x = global.Math.asin,\r\n      y = global.Math.atan,\r\n      z = global.Math.atan2,\r\n      A = global.Math.ceil,\r\n      B = global.Math.imul,\r\n      C = global.Math.min,\r\n      D = global.Math.max,\r\n      E = global.Math.clz32,\r\n      G = env.b,\r\n      H = env.c,\r\n      I = env.d,\r\n      J = env.e,\r\n      K = env.f,\r\n      L = env.g,\r\n      M = env.h,\r\n      N = env.i,\r\n      T = 28640;\r\n    function W(newBuffer) {\r\n      a = new Int8Array(newBuffer);\r\n      c = new Uint8Array(newBuffer);\r\n      b = new Int32Array(newBuffer);\r\n      d = new Float32Array(newBuffer);\r\n      e = new Float64Array(newBuffer);\r\n      buffer = newBuffer;\r\n      return true;\r\n    }\r\n    // EMSCRIPTEN_START_FUNCS\r\n    function X(a) {\r\n      a = a | 0;\r\n      var b = 0;\r\n      b = T;\r\n      T = T + a | 0;\r\n      T = T + 15 & -16;\r\n      return b | 0;\r\n    }\r\n    function Y() {\r\n      return T | 0;\r\n    }\r\n    function Z(a) {\r\n      a = a | 0;\r\n      T = a;\r\n    }\r\n    function _(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      T = a;\r\n    }\r\n    function $(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0;\r\n      if ((a | 0) < 0) {\r\n        c = 2;\r\n        return c | 0;\r\n      }\r\n      if ((a | 0) > 13780509) {\r\n        c = uc(15, c) | 0;\r\n        return c | 0;\r\n      } else {\r\n        d = ((a | 0) < 0) << 31 >> 31;\r\n        f = Md(a | 0, d | 0, 3, 0) | 0;\r\n        e = H() | 0;\r\n        d = Gd(a | 0, d | 0, 1, 0) | 0;\r\n        d = Md(f | 0, e | 0, d | 0, H() | 0) | 0;\r\n        d = Gd(d | 0, H() | 0, 1, 0) | 0;\r\n        a = H() | 0;\r\n        b[c >> 2] = d;\r\n        b[c + 4 >> 2] = a;\r\n        c = 0;\r\n        return c | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function aa(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      return ba(a, b, c, d, 0) | 0;\r\n    }\r\n    function ba(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      j = T;\r\n      T = T + 16 | 0;\r\n      h = j;\r\n      if (!(ca(a, c, d, e, f) | 0)) {\r\n        e = 0;\r\n        T = j;\r\n        return e | 0;\r\n      }\r\n      do { if ((d | 0) >= 0) {\r\n        if ((d | 0) > 13780509) {\r\n          g = uc(15, h) | 0;\r\n          if (g | 0) { break; }\r\n          i = h;\r\n          h = b[i >> 2] | 0;\r\n          i = b[i + 4 >> 2] | 0;\r\n        } else {\r\n          g = ((d | 0) < 0) << 31 >> 31;\r\n          k = Md(d | 0, g | 0, 3, 0) | 0;\r\n          i = H() | 0;\r\n          g = Gd(d | 0, g | 0, 1, 0) | 0;\r\n          g = Md(k | 0, i | 0, g | 0, H() | 0) | 0;\r\n          g = Gd(g | 0, H() | 0, 1, 0) | 0;\r\n          i = H() | 0;\r\n          b[h >> 2] = g;\r\n          b[h + 4 >> 2] = i;\r\n          h = g;\r\n        }\r\n        Xd(e | 0, 0, h << 3 | 0) | 0;\r\n        if (f | 0) {\r\n          Xd(f | 0, 0, h << 2 | 0) | 0;\r\n          g = da(a, c, d, e, f, h, i, 0) | 0;\r\n          break;\r\n        }\r\n        g = Fd(h, 4) | 0;\r\n        if (!g) { g = 13; }else {\r\n          k = da(a, c, d, e, g, h, i, 0) | 0;\r\n          Ed(g);\r\n          g = k;\r\n        }\r\n      } else { g = 2; } } while (0);\r\n      k = g;\r\n      T = j;\r\n      return k | 0;\r\n    }\r\n    function ca(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0;\r\n      q = T;\r\n      T = T + 16 | 0;\r\n      o = q;\r\n      p = q + 8 | 0;\r\n      n = o;\r\n      b[n >> 2] = a;\r\n      b[n + 4 >> 2] = c;\r\n      if ((d | 0) < 0) {\r\n        p = 2;\r\n        T = q;\r\n        return p | 0;\r\n      }\r\n      g = e;\r\n      b[g >> 2] = a;\r\n      b[g + 4 >> 2] = c;\r\n      g = (f | 0) != 0;\r\n      if (g) { b[f >> 2] = 0; }\r\n      if (Hb(a, c) | 0) {\r\n        p = 9;\r\n        T = q;\r\n        return p | 0;\r\n      }\r\n      b[p >> 2] = 0;\r\n      a: do { if ((d | 0) >= 1) {\r\n        if (g) {\r\n          l = 1;\r\n          k = 0;\r\n          m = 0;\r\n          n = 1;\r\n          g = a;\r\n          while (1) {\r\n            if (!(k | m)) {\r\n              g = ea(g, c, 4, p, o) | 0;\r\n              if (g | 0) { break a; }\r\n              c = o;\r\n              g = b[c >> 2] | 0;\r\n              c = b[c + 4 >> 2] | 0;\r\n              if (Hb(g, c) | 0) {\r\n                g = 9;\r\n                break a;\r\n              }\r\n            }\r\n            g = ea(g, c, b[26800 + (m << 2) >> 2] | 0, p, o) | 0;\r\n            if (g | 0) { break a; }\r\n            c = o;\r\n            g = b[c >> 2] | 0;\r\n            c = b[c + 4 >> 2] | 0;\r\n            a = e + (l << 3) | 0;\r\n            b[a >> 2] = g;\r\n            b[a + 4 >> 2] = c;\r\n            b[f + (l << 2) >> 2] = n;\r\n            a = k + 1 | 0;\r\n            h = (a | 0) == (n | 0);\r\n            i = m + 1 | 0;\r\n            j = (i | 0) == 6;\r\n            if (Hb(g, c) | 0) {\r\n              g = 9;\r\n              break a;\r\n            }\r\n            n = n + (j & h & 1) | 0;\r\n            if ((n | 0) > (d | 0)) {\r\n              g = 0;\r\n              break;\r\n            } else {\r\n              l = l + 1 | 0;\r\n              k = h ? 0 : a;\r\n              m = h ? j ? 0 : i : m;\r\n            }\r\n          }\r\n        } else {\r\n          l = 1;\r\n          k = 0;\r\n          m = 0;\r\n          n = 1;\r\n          g = a;\r\n          while (1) {\r\n            if (!(k | m)) {\r\n              g = ea(g, c, 4, p, o) | 0;\r\n              if (g | 0) { break a; }\r\n              c = o;\r\n              g = b[c >> 2] | 0;\r\n              c = b[c + 4 >> 2] | 0;\r\n              if (Hb(g, c) | 0) {\r\n                g = 9;\r\n                break a;\r\n              }\r\n            }\r\n            g = ea(g, c, b[26800 + (m << 2) >> 2] | 0, p, o) | 0;\r\n            if (g | 0) { break a; }\r\n            c = o;\r\n            g = b[c >> 2] | 0;\r\n            c = b[c + 4 >> 2] | 0;\r\n            a = e + (l << 3) | 0;\r\n            b[a >> 2] = g;\r\n            b[a + 4 >> 2] = c;\r\n            a = k + 1 | 0;\r\n            h = (a | 0) == (n | 0);\r\n            i = m + 1 | 0;\r\n            j = (i | 0) == 6;\r\n            if (Hb(g, c) | 0) {\r\n              g = 9;\r\n              break a;\r\n            }\r\n            n = n + (j & h & 1) | 0;\r\n            if ((n | 0) > (d | 0)) {\r\n              g = 0;\r\n              break;\r\n            } else {\r\n              l = l + 1 | 0;\r\n              k = h ? 0 : a;\r\n              m = h ? j ? 0 : i : m;\r\n            }\r\n          }\r\n        }\r\n      } else { g = 0; } } while (0);\r\n      p = g;\r\n      T = q;\r\n      return p | 0;\r\n    }\r\n    function da(a, c, d, e, f, g, h, i) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      h = h | 0;\r\n      i = i | 0;\r\n      var j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0;\r\n      q = T;\r\n      T = T + 16 | 0;\r\n      o = q + 8 | 0;\r\n      p = q;\r\n      j = Od(a | 0, c | 0, g | 0, h | 0) | 0;\r\n      l = H() | 0;\r\n      m = e + (j << 3) | 0;\r\n      r = m;\r\n      s = b[r >> 2] | 0;\r\n      r = b[r + 4 >> 2] | 0;\r\n      k = (s | 0) == (a | 0) & (r | 0) == (c | 0);\r\n      if (!((s | 0) == 0 & (r | 0) == 0 | k)) { do {\r\n        j = Gd(j | 0, l | 0, 1, 0) | 0;\r\n        j = Nd(j | 0, H() | 0, g | 0, h | 0) | 0;\r\n        l = H() | 0;\r\n        m = e + (j << 3) | 0;\r\n        s = m;\r\n        r = b[s >> 2] | 0;\r\n        s = b[s + 4 >> 2] | 0;\r\n        k = (r | 0) == (a | 0) & (s | 0) == (c | 0);\r\n      } while (!((r | 0) == 0 & (s | 0) == 0 | k)); }\r\n      j = f + (j << 2) | 0;\r\n      if (k ? (b[j >> 2] | 0) <= (i | 0) : 0) {\r\n        s = 0;\r\n        T = q;\r\n        return s | 0;\r\n      }\r\n      s = m;\r\n      b[s >> 2] = a;\r\n      b[s + 4 >> 2] = c;\r\n      b[j >> 2] = i;\r\n      if ((i | 0) >= (d | 0)) {\r\n        s = 0;\r\n        T = q;\r\n        return s | 0;\r\n      }\r\n      k = i + 1 | 0;\r\n      b[o >> 2] = 0;\r\n      j = ea(a, c, 2, o, p) | 0;\r\n      switch (j | 0) {\r\n        case 9:\r\n          {\r\n            n = 9;\r\n            break;\r\n          }\r\n        case 0:\r\n          {\r\n            j = p;\r\n            j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n            if (!j) { n = 9; }\r\n            break;\r\n          }\r\n        default:\r\n\r\n      }\r\n      a: do { if ((n | 0) == 9) {\r\n        b[o >> 2] = 0;\r\n        j = ea(a, c, 3, o, p) | 0;\r\n        switch (j | 0) {\r\n          case 9:\r\n            break;\r\n          case 0:\r\n            {\r\n              j = p;\r\n              j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n              if (j | 0) { break a; }\r\n              break;\r\n            }\r\n          default:\r\n            break a;\r\n        }\r\n        b[o >> 2] = 0;\r\n        j = ea(a, c, 1, o, p) | 0;\r\n        switch (j | 0) {\r\n          case 9:\r\n            break;\r\n          case 0:\r\n            {\r\n              j = p;\r\n              j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n              if (j | 0) { break a; }\r\n              break;\r\n            }\r\n          default:\r\n            break a;\r\n        }\r\n        b[o >> 2] = 0;\r\n        j = ea(a, c, 5, o, p) | 0;\r\n        switch (j | 0) {\r\n          case 9:\r\n            break;\r\n          case 0:\r\n            {\r\n              j = p;\r\n              j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n              if (j | 0) { break a; }\r\n              break;\r\n            }\r\n          default:\r\n            break a;\r\n        }\r\n        b[o >> 2] = 0;\r\n        j = ea(a, c, 4, o, p) | 0;\r\n        switch (j | 0) {\r\n          case 9:\r\n            break;\r\n          case 0:\r\n            {\r\n              j = p;\r\n              j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n              if (j | 0) { break a; }\r\n              break;\r\n            }\r\n          default:\r\n            break a;\r\n        }\r\n        b[o >> 2] = 0;\r\n        j = ea(a, c, 6, o, p) | 0;\r\n        switch (j | 0) {\r\n          case 9:\r\n            break;\r\n          case 0:\r\n            {\r\n              j = p;\r\n              j = da(b[j >> 2] | 0, b[j + 4 >> 2] | 0, d, e, f, g, h, k) | 0;\r\n              if (j | 0) { break a; }\r\n              break;\r\n            }\r\n          default:\r\n            break a;\r\n        }\r\n        s = 0;\r\n        T = q;\r\n        return s | 0;\r\n      } } while (0);\r\n      s = j;\r\n      T = q;\r\n      return s | 0;\r\n    }\r\n    function ea(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0;\r\n      if (d >>> 0 > 6) {\r\n        f = 1;\r\n        return f | 0;\r\n      }\r\n      m = (b[e >> 2] | 0) % 6 | 0;\r\n      b[e >> 2] = m;\r\n      if ((m | 0) > 0) {\r\n        g = 0;\r\n        do {\r\n          d = $a(d) | 0;\r\n          g = g + 1 | 0;\r\n        } while ((g | 0) < (b[e >> 2] | 0));\r\n      }\r\n      m = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      l = m & 127;\r\n      if (l >>> 0 > 121) {\r\n        f = 5;\r\n        return f | 0;\r\n      }\r\n      j = Pb(a, c) | 0;\r\n      g = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      g = g & 15;\r\n      a: do { if (!g) { k = 8; }else {\r\n        while (1) {\r\n          h = (15 - g | 0) * 3 | 0;\r\n          i = Qd(a | 0, c | 0, h | 0) | 0;\r\n          H() | 0;\r\n          i = i & 7;\r\n          if ((i | 0) == 7) {\r\n            c = 5;\r\n            break;\r\n          }\r\n          p = (Vb(g) | 0) == 0;\r\n          g = g + -1 | 0;\r\n          n = Rd(7, 0, h | 0) | 0;\r\n          c = c & ~(H() | 0);\r\n          o = Rd(b[(p ? 432 : 16) + (i * 28 | 0) + (d << 2) >> 2] | 0, 0, h | 0) | 0;\r\n          h = H() | 0;\r\n          d = b[(p ? 640 : 224) + (i * 28 | 0) + (d << 2) >> 2] | 0;\r\n          a = o | a & ~n;\r\n          c = h | c;\r\n          if (!d) {\r\n            d = 0;\r\n            break a;\r\n          }\r\n          if (!g) {\r\n            k = 8;\r\n            break a;\r\n          }\r\n        }\r\n        return c | 0;\r\n      } } while (0);\r\n      if ((k | 0) == 8) {\r\n        p = b[848 + (l * 28 | 0) + (d << 2) >> 2] | 0;\r\n        o = Rd(p | 0, 0, 45) | 0;\r\n        a = o | a;\r\n        c = H() | 0 | c & -1040385;\r\n        d = b[4272 + (l * 28 | 0) + (d << 2) >> 2] | 0;\r\n        if ((p & 127 | 0) == 127) {\r\n          p = Rd(b[848 + (l * 28 | 0) + 20 >> 2] | 0, 0, 45) | 0;\r\n          c = H() | 0 | c & -1040385;\r\n          d = b[4272 + (l * 28 | 0) + 20 >> 2] | 0;\r\n          a = Rb(p | a, c) | 0;\r\n          c = H() | 0;\r\n          b[e >> 2] = (b[e >> 2] | 0) + 1;\r\n        }\r\n      }\r\n      i = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      i = i & 127;\r\n      b: do { if (!(oa(i) | 0)) {\r\n        if ((d | 0) > 0) {\r\n          g = 0;\r\n          do {\r\n            a = Rb(a, c) | 0;\r\n            c = H() | 0;\r\n            g = g + 1 | 0;\r\n          } while ((g | 0) != (d | 0));\r\n        }\r\n      } else {\r\n        c: do { if ((Pb(a, c) | 0) == 1) {\r\n          if ((l | 0) != (i | 0)) { if (ua(i, b[7696 + (l * 28 | 0) >> 2] | 0) | 0) {\r\n            a = Tb(a, c) | 0;\r\n            h = 1;\r\n            c = H() | 0;\r\n            break;\r\n          } else { I(27795, 26864, 533, 26872); } }\r\n          switch (j | 0) {\r\n            case 3:\r\n              {\r\n                a = Rb(a, c) | 0;\r\n                c = H() | 0;\r\n                b[e >> 2] = (b[e >> 2] | 0) + 1;\r\n                h = 0;\r\n                break c;\r\n              }\r\n            case 5:\r\n              {\r\n                a = Tb(a, c) | 0;\r\n                c = H() | 0;\r\n                b[e >> 2] = (b[e >> 2] | 0) + 5;\r\n                h = 0;\r\n                break c;\r\n              }\r\n            case 0:\r\n              {\r\n                p = 9;\r\n                return p | 0;\r\n              }\r\n            default:\r\n              {\r\n                p = 1;\r\n                return p | 0;\r\n              }\r\n          }\r\n        } else { h = 0; } } while (0);\r\n        if ((d | 0) > 0) {\r\n          g = 0;\r\n          do {\r\n            a = Qb(a, c) | 0;\r\n            c = H() | 0;\r\n            g = g + 1 | 0;\r\n          } while ((g | 0) != (d | 0));\r\n        }\r\n        if ((l | 0) != (i | 0)) {\r\n          if (!(pa(i) | 0)) {\r\n            if ((h | 0) != 0 | (Pb(a, c) | 0) != 5) { break; }\r\n            b[e >> 2] = (b[e >> 2] | 0) + 1;\r\n            break;\r\n          }\r\n          switch (m & 127) {\r\n            case 8:\r\n            case 118:\r\n              break b;\r\n            default:\r\n\r\n          }\r\n          if ((Pb(a, c) | 0) != 3) { b[e >> 2] = (b[e >> 2] | 0) + 1; }\r\n        }\r\n      } } while (0);\r\n      b[e >> 2] = ((b[e >> 2] | 0) + d | 0) % 6 | 0;\r\n      p = f;\r\n      b[p >> 2] = a;\r\n      b[p + 4 >> 2] = c;\r\n      p = 0;\r\n      return p | 0;\r\n    }\r\n    function fa(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      if (!(ga(a, b, c, d) | 0)) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      Xd(d | 0, 0, c * 48 | 0) | 0;\r\n      d = ha(a, b, c, d) | 0;\r\n      return d | 0;\r\n    }\r\n    function ga(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0;\r\n      p = T;\r\n      T = T + 16 | 0;\r\n      n = p;\r\n      o = p + 8 | 0;\r\n      m = n;\r\n      b[m >> 2] = a;\r\n      b[m + 4 >> 2] = c;\r\n      if ((d | 0) < 0) {\r\n        o = 2;\r\n        T = p;\r\n        return o | 0;\r\n      }\r\n      if (!d) {\r\n        o = e;\r\n        b[o >> 2] = a;\r\n        b[o + 4 >> 2] = c;\r\n        o = 0;\r\n        T = p;\r\n        return o | 0;\r\n      }\r\n      b[o >> 2] = 0;\r\n      a: do { if (!(Hb(a, c) | 0)) {\r\n        f = 0;\r\n        m = a;\r\n        do {\r\n          a = ea(m, c, 4, o, n) | 0;\r\n          if (a | 0) { break a; }\r\n          c = n;\r\n          m = b[c >> 2] | 0;\r\n          c = b[c + 4 >> 2] | 0;\r\n          f = f + 1 | 0;\r\n          if (Hb(m, c) | 0) {\r\n            a = 9;\r\n            break a;\r\n          }\r\n        } while ((f | 0) < (d | 0));\r\n        l = e;\r\n        b[l >> 2] = m;\r\n        b[l + 4 >> 2] = c;\r\n        l = d + -1 | 0;\r\n        k = 0;\r\n        a = 1;\r\n        do {\r\n          f = 26800 + (k << 2) | 0;\r\n          if ((k | 0) == 5) {\r\n            h = b[f >> 2] | 0;\r\n            g = 0;\r\n            f = a;\r\n            while (1) {\r\n              a = n;\r\n              a = ea(b[a >> 2] | 0, b[a + 4 >> 2] | 0, h, o, n) | 0;\r\n              if (a | 0) { break a; }\r\n              if ((g | 0) != (l | 0)) {\r\n                j = n;\r\n                i = b[j >> 2] | 0;\r\n                j = b[j + 4 >> 2] | 0;\r\n                a = e + (f << 3) | 0;\r\n                b[a >> 2] = i;\r\n                b[a + 4 >> 2] = j;\r\n                if (!(Hb(i, j) | 0)) { a = f + 1 | 0; }else {\r\n                  a = 9;\r\n                  break a;\r\n                }\r\n              } else { a = f; }\r\n              g = g + 1 | 0;\r\n              if ((g | 0) >= (d | 0)) { break; }else { f = a; }\r\n            }\r\n          } else {\r\n            h = n;\r\n            j = b[f >> 2] | 0;\r\n            i = 0;\r\n            f = a;\r\n            g = b[h >> 2] | 0;\r\n            h = b[h + 4 >> 2] | 0;\r\n            while (1) {\r\n              a = ea(g, h, j, o, n) | 0;\r\n              if (a | 0) { break a; }\r\n              h = n;\r\n              g = b[h >> 2] | 0;\r\n              h = b[h + 4 >> 2] | 0;\r\n              a = e + (f << 3) | 0;\r\n              b[a >> 2] = g;\r\n              b[a + 4 >> 2] = h;\r\n              a = f + 1 | 0;\r\n              if (Hb(g, h) | 0) {\r\n                a = 9;\r\n                break a;\r\n              }\r\n              i = i + 1 | 0;\r\n              if ((i | 0) >= (d | 0)) { break; }else { f = a; }\r\n            }\r\n          }\r\n          k = k + 1 | 0;\r\n        } while (k >>> 0 < 6);\r\n        a = n;\r\n        a = ((m | 0) == (b[a >> 2] | 0) ? (c | 0) == (b[a + 4 >> 2] | 0) : 0) ? 0 : 9;\r\n      } else { a = 9; } } while (0);\r\n      o = a;\r\n      T = p;\r\n      return o | 0;\r\n    }\r\n    function ha(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0;\r\n      m = T;\r\n      T = T + 16 | 0;\r\n      h = m;\r\n      if (!d) {\r\n        b[e >> 2] = a;\r\n        b[e + 4 >> 2] = c;\r\n        e = 0;\r\n        T = m;\r\n        return e | 0;\r\n      }\r\n      do { if ((d | 0) >= 0) {\r\n        if ((d | 0) > 13780509) {\r\n          f = uc(15, h) | 0;\r\n          if (f | 0) { break; }\r\n          g = h;\r\n          f = b[g >> 2] | 0;\r\n          g = b[g + 4 >> 2] | 0;\r\n        } else {\r\n          f = ((d | 0) < 0) << 31 >> 31;\r\n          l = Md(d | 0, f | 0, 3, 0) | 0;\r\n          g = H() | 0;\r\n          f = Gd(d | 0, f | 0, 1, 0) | 0;\r\n          f = Md(l | 0, g | 0, f | 0, H() | 0) | 0;\r\n          f = Gd(f | 0, H() | 0, 1, 0) | 0;\r\n          g = H() | 0;\r\n          l = h;\r\n          b[l >> 2] = f;\r\n          b[l + 4 >> 2] = g;\r\n        }\r\n        k = Fd(f, 8) | 0;\r\n        if (!k) { f = 13; }else {\r\n          l = Fd(f, 4) | 0;\r\n          if (!l) {\r\n            Ed(k);\r\n            f = 13;\r\n            break;\r\n          }\r\n          f = da(a, c, d, k, l, f, g, 0) | 0;\r\n          if (f | 0) {\r\n            Ed(k);\r\n            Ed(l);\r\n            break;\r\n          }\r\n          c = b[h >> 2] | 0;\r\n          h = b[h + 4 >> 2] | 0;\r\n          if ((h | 0) > 0 | (h | 0) == 0 & c >>> 0 > 0) {\r\n            f = 0;\r\n            i = 0;\r\n            j = 0;\r\n            do {\r\n              a = k + (i << 3) | 0;\r\n              g = b[a >> 2] | 0;\r\n              a = b[a + 4 >> 2] | 0;\r\n              if (!((g | 0) == 0 & (a | 0) == 0) ? (b[l + (i << 2) >> 2] | 0) == (d | 0) : 0) {\r\n                n = e + (f << 3) | 0;\r\n                b[n >> 2] = g;\r\n                b[n + 4 >> 2] = a;\r\n                f = f + 1 | 0;\r\n              }\r\n              i = Gd(i | 0, j | 0, 1, 0) | 0;\r\n              j = H() | 0;\r\n            } while ((j | 0) < (h | 0) | (j | 0) == (h | 0) & i >>> 0 < c >>> 0);\r\n          }\r\n          Ed(k);\r\n          Ed(l);\r\n          f = 0;\r\n        }\r\n      } else { f = 2; } } while (0);\r\n      n = f;\r\n      T = m;\r\n      return n | 0;\r\n    }\r\n    function ia(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      i = T;\r\n      T = T + 16 | 0;\r\n      g = i;\r\n      h = i + 8 | 0;\r\n      f = (Hb(a, c) | 0) == 0;\r\n      f = f ? 1 : 2;\r\n      while (1) {\r\n        b[h >> 2] = 0;\r\n        k = (ea(a, c, f, h, g) | 0) == 0;\r\n        j = g;\r\n        if (k & ((b[j >> 2] | 0) == (d | 0) ? (b[j + 4 >> 2] | 0) == (e | 0) : 0)) {\r\n          a = 4;\r\n          break;\r\n        }\r\n        f = f + 1 | 0;\r\n        if (f >>> 0 >= 7) {\r\n          f = 7;\r\n          a = 4;\r\n          break;\r\n        }\r\n      }\r\n      if ((a | 0) == 4) {\r\n        T = i;\r\n        return f | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function ja(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      i = T;\r\n      T = T + 48 | 0;\r\n      f = i + 16 | 0;\r\n      g = i + 8 | 0;\r\n      h = i;\r\n      d = Xc(d) | 0;\r\n      if (d | 0) {\r\n        h = d;\r\n        T = i;\r\n        return h | 0;\r\n      }\r\n      k = a;\r\n      j = b[k + 4 >> 2] | 0;\r\n      d = g;\r\n      b[d >> 2] = b[k >> 2];\r\n      b[d + 4 >> 2] = j;\r\n      Wc(g, f);\r\n      d = Ha(f, c, h) | 0;\r\n      if (!d) {\r\n        c = b[g >> 2] | 0;\r\n        g = b[a + 8 >> 2] | 0;\r\n        if ((g | 0) > 0) {\r\n          f = b[a + 12 >> 2] | 0;\r\n          d = 0;\r\n          do {\r\n            c = (b[f + (d << 3) >> 2] | 0) + c | 0;\r\n            d = d + 1 | 0;\r\n          } while ((d | 0) < (g | 0));\r\n        }\r\n        d = h;\r\n        f = b[d >> 2] | 0;\r\n        d = b[d + 4 >> 2] | 0;\r\n        g = ((c | 0) < 0) << 31 >> 31;\r\n        if ((d | 0) < (g | 0) | (d | 0) == (g | 0) & f >>> 0 < c >>> 0) {\r\n          d = h;\r\n          b[d >> 2] = c;\r\n          b[d + 4 >> 2] = g;\r\n          d = g;\r\n        } else { c = f; }\r\n        j = Gd(c | 0, d | 0, 12, 0) | 0;\r\n        k = H() | 0;\r\n        d = h;\r\n        b[d >> 2] = j;\r\n        b[d + 4 >> 2] = k;\r\n        d = e;\r\n        b[d >> 2] = j;\r\n        b[d + 4 >> 2] = k;\r\n        d = 0;\r\n      }\r\n      k = d;\r\n      T = i;\r\n      return k | 0;\r\n    }\r\n    function ka(a, c, d, f, g, h, i) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      h = h | 0;\r\n      i = i | 0;\r\n      var j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0,\r\n        D = 0,\r\n        E = 0,\r\n        F = 0,\r\n        G = 0,\r\n        I = 0,\r\n        J = 0.0,\r\n        K = 0.0,\r\n        L = 0.0,\r\n        M = 0.0;\r\n      I = T;\r\n      T = T + 64 | 0;\r\n      D = I + 48 | 0;\r\n      E = I + 32 | 0;\r\n      F = I + 24 | 0;\r\n      x = I + 8 | 0;\r\n      y = I;\r\n      k = b[a >> 2] | 0;\r\n      if ((k | 0) <= 0) {\r\n        G = 0;\r\n        T = I;\r\n        return G | 0;\r\n      }\r\n      z = a + 4 | 0;\r\n      A = D + 8 | 0;\r\n      B = E + 8 | 0;\r\n      C = x + 8 | 0;\r\n      j = 0;\r\n      v = 0;\r\n      while (1) {\r\n        l = b[z >> 2] | 0;\r\n        u = l + (v << 4) | 0;\r\n        b[D >> 2] = b[u >> 2];\r\n        b[D + 4 >> 2] = b[u + 4 >> 2];\r\n        b[D + 8 >> 2] = b[u + 8 >> 2];\r\n        b[D + 12 >> 2] = b[u + 12 >> 2];\r\n        if ((v | 0) == (k + -1 | 0)) {\r\n          b[E >> 2] = b[l >> 2];\r\n          b[E + 4 >> 2] = b[l + 4 >> 2];\r\n          b[E + 8 >> 2] = b[l + 8 >> 2];\r\n          b[E + 12 >> 2] = b[l + 12 >> 2];\r\n        } else {\r\n          u = l + (v + 1 << 4) | 0;\r\n          b[E >> 2] = b[u >> 2];\r\n          b[E + 4 >> 2] = b[u + 4 >> 2];\r\n          b[E + 8 >> 2] = b[u + 8 >> 2];\r\n          b[E + 12 >> 2] = b[u + 12 >> 2];\r\n        }\r\n        k = Ia(D, E, f, F) | 0;\r\n        a: do { if (!k) {\r\n          k = F;\r\n          l = b[k >> 2] | 0;\r\n          k = b[k + 4 >> 2] | 0;\r\n          if ((k | 0) > 0 | (k | 0) == 0 & l >>> 0 > 0) {\r\n            t = 0;\r\n            u = 0;\r\n            b: while (1) {\r\n              K = 1.0 / (+(l >>> 0) + 4294967296.0 * +(k | 0));\r\n              M = +e[D >> 3];\r\n              k = Hd(l | 0, k | 0, t | 0, u | 0) | 0;\r\n              L = +(k >>> 0) + 4294967296.0 * +(H() | 0);\r\n              J = +(t >>> 0) + 4294967296.0 * +(u | 0);\r\n              e[x >> 3] = K * (M * L) + K * (+e[E >> 3] * J);\r\n              e[C >> 3] = K * (+e[A >> 3] * L) + K * (+e[B >> 3] * J);\r\n              k = Wb(x, f, y) | 0;\r\n              if (k | 0) {\r\n                j = k;\r\n                break;\r\n              }\r\n              s = y;\r\n              r = b[s >> 2] | 0;\r\n              s = b[s + 4 >> 2] | 0;\r\n              o = Od(r | 0, s | 0, c | 0, d | 0) | 0;\r\n              m = H() | 0;\r\n              k = i + (o << 3) | 0;\r\n              n = k;\r\n              l = b[n >> 2] | 0;\r\n              n = b[n + 4 >> 2] | 0;\r\n              c: do { if ((l | 0) == 0 & (n | 0) == 0) {\r\n                w = k;\r\n                G = 16;\r\n              } else {\r\n                p = 0;\r\n                q = 0;\r\n                while (1) {\r\n                  if ((p | 0) > (d | 0) | (p | 0) == (d | 0) & q >>> 0 > c >>> 0) {\r\n                    j = 1;\r\n                    break b;\r\n                  }\r\n                  if ((l | 0) == (r | 0) & (n | 0) == (s | 0)) { break c; }\r\n                  k = Gd(o | 0, m | 0, 1, 0) | 0;\r\n                  o = Nd(k | 0, H() | 0, c | 0, d | 0) | 0;\r\n                  m = H() | 0;\r\n                  q = Gd(q | 0, p | 0, 1, 0) | 0;\r\n                  p = H() | 0;\r\n                  k = i + (o << 3) | 0;\r\n                  n = k;\r\n                  l = b[n >> 2] | 0;\r\n                  n = b[n + 4 >> 2] | 0;\r\n                  if ((l | 0) == 0 & (n | 0) == 0) {\r\n                    w = k;\r\n                    G = 16;\r\n                    break;\r\n                  }\r\n                }\r\n              } } while (0);\r\n              if ((G | 0) == 16 ? (G = 0, !((r | 0) == 0 & (s | 0) == 0)) : 0) {\r\n                q = w;\r\n                b[q >> 2] = r;\r\n                b[q + 4 >> 2] = s;\r\n                q = h + (b[g >> 2] << 3) | 0;\r\n                b[q >> 2] = r;\r\n                b[q + 4 >> 2] = s;\r\n                q = g;\r\n                q = Gd(b[q >> 2] | 0, b[q + 4 >> 2] | 0, 1, 0) | 0;\r\n                r = H() | 0;\r\n                s = g;\r\n                b[s >> 2] = q;\r\n                b[s + 4 >> 2] = r;\r\n              }\r\n              t = Gd(t | 0, u | 0, 1, 0) | 0;\r\n              u = H() | 0;\r\n              k = F;\r\n              l = b[k >> 2] | 0;\r\n              k = b[k + 4 >> 2] | 0;\r\n              if (!((k | 0) > (u | 0) | (k | 0) == (u | 0) & l >>> 0 > t >>> 0)) {\r\n                l = 1;\r\n                break a;\r\n              }\r\n            }\r\n            l = 0;\r\n          } else { l = 1; }\r\n        } else {\r\n          l = 0;\r\n          j = k;\r\n        } } while (0);\r\n        v = v + 1 | 0;\r\n        if (!l) {\r\n          G = 21;\r\n          break;\r\n        }\r\n        k = b[a >> 2] | 0;\r\n        if ((v | 0) >= (k | 0)) {\r\n          j = 0;\r\n          G = 21;\r\n          break;\r\n        }\r\n      }\r\n      if ((G | 0) == 21) {\r\n        T = I;\r\n        return j | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function la(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0,\r\n        D = 0,\r\n        E = 0,\r\n        F = 0,\r\n        G = 0,\r\n        I = 0,\r\n        J = 0,\r\n        K = 0;\r\n      K = T;\r\n      T = T + 112 | 0;\r\n      F = K + 80 | 0;\r\n      j = K + 72 | 0;\r\n      G = K;\r\n      I = K + 56 | 0;\r\n      f = Xc(d) | 0;\r\n      if (f | 0) {\r\n        J = f;\r\n        T = K;\r\n        return J | 0;\r\n      }\r\n      k = a + 8 | 0;\r\n      J = Dd((b[k >> 2] << 5) + 32 | 0) | 0;\r\n      if (!J) {\r\n        J = 13;\r\n        T = K;\r\n        return J | 0;\r\n      }\r\n      Yc(a, J);\r\n      f = Xc(d) | 0;\r\n      if (!f) {\r\n        D = a;\r\n        E = b[D + 4 >> 2] | 0;\r\n        f = j;\r\n        b[f >> 2] = b[D >> 2];\r\n        b[f + 4 >> 2] = E;\r\n        Wc(j, F);\r\n        f = Ha(F, c, G) | 0;\r\n        if (!f) {\r\n          f = b[j >> 2] | 0;\r\n          g = b[k >> 2] | 0;\r\n          if ((g | 0) > 0) {\r\n            h = b[a + 12 >> 2] | 0;\r\n            d = 0;\r\n            do {\r\n              f = (b[h + (d << 3) >> 2] | 0) + f | 0;\r\n              d = d + 1 | 0;\r\n            } while ((d | 0) != (g | 0));\r\n            d = f;\r\n          } else { d = f; }\r\n          f = G;\r\n          g = b[f >> 2] | 0;\r\n          f = b[f + 4 >> 2] | 0;\r\n          h = ((d | 0) < 0) << 31 >> 31;\r\n          if ((f | 0) < (h | 0) | (f | 0) == (h | 0) & g >>> 0 < d >>> 0) {\r\n            f = G;\r\n            b[f >> 2] = d;\r\n            b[f + 4 >> 2] = h;\r\n            f = h;\r\n          } else { d = g; }\r\n          D = Gd(d | 0, f | 0, 12, 0) | 0;\r\n          E = H() | 0;\r\n          f = G;\r\n          b[f >> 2] = D;\r\n          b[f + 4 >> 2] = E;\r\n          f = 0;\r\n        } else {\r\n          D = 0;\r\n          E = 0;\r\n        }\r\n        if (!f) {\r\n          d = Fd(D, 8) | 0;\r\n          if (!d) {\r\n            Ed(J);\r\n            J = 13;\r\n            T = K;\r\n            return J | 0;\r\n          }\r\n          i = Fd(D, 8) | 0;\r\n          if (!i) {\r\n            Ed(J);\r\n            Ed(d);\r\n            J = 13;\r\n            T = K;\r\n            return J | 0;\r\n          }\r\n          B = F;\r\n          b[B >> 2] = 0;\r\n          b[B + 4 >> 2] = 0;\r\n          B = a;\r\n          C = b[B + 4 >> 2] | 0;\r\n          f = j;\r\n          b[f >> 2] = b[B >> 2];\r\n          b[f + 4 >> 2] = C;\r\n          f = ka(j, D, E, c, F, d, i) | 0;\r\n          a: do { if (!f) {\r\n            b: do { if ((b[k >> 2] | 0) > 0) {\r\n              h = a + 12 | 0;\r\n              g = 0;\r\n              while (1) {\r\n                f = ka((b[h >> 2] | 0) + (g << 3) | 0, D, E, c, F, d, i) | 0;\r\n                g = g + 1 | 0;\r\n                if (f | 0) { break; }\r\n                if ((g | 0) >= (b[k >> 2] | 0)) { break b; }\r\n              }\r\n              Ed(d);\r\n              Ed(i);\r\n              Ed(J);\r\n              break a;\r\n            } } while (0);\r\n            if ((E | 0) > 0 | (E | 0) == 0 & D >>> 0 > 0) { Xd(i | 0, 0, D << 3 | 0) | 0; }\r\n            C = F;\r\n            B = b[C + 4 >> 2] | 0;\r\n            c: do { if ((B | 0) > 0 | (B | 0) == 0 & (b[C >> 2] | 0) >>> 0 > 0) {\r\n              y = d;\r\n              z = i;\r\n              A = d;\r\n              B = i;\r\n              C = d;\r\n              f = d;\r\n              v = d;\r\n              w = i;\r\n              x = i;\r\n              d = i;\r\n              d: while (1) {\r\n                r = 0;\r\n                s = 0;\r\n                t = 0;\r\n                u = 0;\r\n                g = 0;\r\n                h = 0;\r\n                while (1) {\r\n                  i = G;\r\n                  j = i + 56 | 0;\r\n                  do {\r\n                    b[i >> 2] = 0;\r\n                    i = i + 4 | 0;\r\n                  } while ((i | 0) < (j | 0));\r\n                  c = y + (r << 3) | 0;\r\n                  k = b[c >> 2] | 0;\r\n                  c = b[c + 4 >> 2] | 0;\r\n                  if (ca(k, c, 1, G, 0) | 0) {\r\n                    i = G;\r\n                    j = i + 56 | 0;\r\n                    do {\r\n                      b[i >> 2] = 0;\r\n                      i = i + 4 | 0;\r\n                    } while ((i | 0) < (j | 0));\r\n                    i = Fd(7, 4) | 0;\r\n                    if (i | 0) {\r\n                      da(k, c, 1, G, i, 7, 0, 0) | 0;\r\n                      Ed(i);\r\n                    }\r\n                  }\r\n                  q = 0;\r\n                  while (1) {\r\n                    p = G + (q << 3) | 0;\r\n                    o = b[p >> 2] | 0;\r\n                    p = b[p + 4 >> 2] | 0;\r\n                    e: do { if ((o | 0) == 0 & (p | 0) == 0) {\r\n                      i = g;\r\n                      j = h;\r\n                    } else {\r\n                      l = Od(o | 0, p | 0, D | 0, E | 0) | 0;\r\n                      k = H() | 0;\r\n                      i = e + (l << 3) | 0;\r\n                      c = i;\r\n                      j = b[c >> 2] | 0;\r\n                      c = b[c + 4 >> 2] | 0;\r\n                      if (!((j | 0) == 0 & (c | 0) == 0)) {\r\n                        m = 0;\r\n                        n = 0;\r\n                        do {\r\n                          if ((m | 0) > (E | 0) | (m | 0) == (E | 0) & n >>> 0 > D >>> 0) { break d; }\r\n                          if ((j | 0) == (o | 0) & (c | 0) == (p | 0)) {\r\n                            i = g;\r\n                            j = h;\r\n                            break e;\r\n                          }\r\n                          i = Gd(l | 0, k | 0, 1, 0) | 0;\r\n                          l = Nd(i | 0, H() | 0, D | 0, E | 0) | 0;\r\n                          k = H() | 0;\r\n                          n = Gd(n | 0, m | 0, 1, 0) | 0;\r\n                          m = H() | 0;\r\n                          i = e + (l << 3) | 0;\r\n                          c = i;\r\n                          j = b[c >> 2] | 0;\r\n                          c = b[c + 4 >> 2] | 0;\r\n                        } while (!((j | 0) == 0 & (c | 0) == 0));\r\n                      }\r\n                      if ((o | 0) == 0 & (p | 0) == 0) {\r\n                        i = g;\r\n                        j = h;\r\n                        break;\r\n                      }\r\n                      Zb(o, p, I) | 0;\r\n                      if (Zc(a, J, I) | 0) {\r\n                        n = Gd(g | 0, h | 0, 1, 0) | 0;\r\n                        h = H() | 0;\r\n                        m = i;\r\n                        b[m >> 2] = o;\r\n                        b[m + 4 >> 2] = p;\r\n                        g = z + (g << 3) | 0;\r\n                        b[g >> 2] = o;\r\n                        b[g + 4 >> 2] = p;\r\n                        g = n;\r\n                      }\r\n                      i = g;\r\n                      j = h;\r\n                    } } while (0);\r\n                    q = q + 1 | 0;\r\n                    if (q >>> 0 >= 7) { break; }else {\r\n                      g = i;\r\n                      h = j;\r\n                    }\r\n                  }\r\n                  r = Gd(r | 0, s | 0, 1, 0) | 0;\r\n                  s = H() | 0;\r\n                  t = Gd(t | 0, u | 0, 1, 0) | 0;\r\n                  u = H() | 0;\r\n                  h = F;\r\n                  g = b[h >> 2] | 0;\r\n                  h = b[h + 4 >> 2] | 0;\r\n                  if (!((u | 0) < (h | 0) | (u | 0) == (h | 0) & t >>> 0 < g >>> 0)) { break; }else {\r\n                    g = i;\r\n                    h = j;\r\n                  }\r\n                }\r\n                if ((h | 0) > 0 | (h | 0) == 0 & g >>> 0 > 0) {\r\n                  g = 0;\r\n                  h = 0;\r\n                  do {\r\n                    u = y + (g << 3) | 0;\r\n                    b[u >> 2] = 0;\r\n                    b[u + 4 >> 2] = 0;\r\n                    g = Gd(g | 0, h | 0, 1, 0) | 0;\r\n                    h = H() | 0;\r\n                    u = F;\r\n                    t = b[u + 4 >> 2] | 0;\r\n                  } while ((h | 0) < (t | 0) | ((h | 0) == (t | 0) ? g >>> 0 < (b[u >> 2] | 0) >>> 0 : 0));\r\n                }\r\n                u = F;\r\n                b[u >> 2] = i;\r\n                b[u + 4 >> 2] = j;\r\n                if ((j | 0) > 0 | (j | 0) == 0 & i >>> 0 > 0) {\r\n                  q = d;\r\n                  r = x;\r\n                  s = C;\r\n                  t = w;\r\n                  u = z;\r\n                  d = v;\r\n                  x = f;\r\n                  w = A;\r\n                  v = q;\r\n                  f = r;\r\n                  C = B;\r\n                  B = s;\r\n                  A = t;\r\n                  z = y;\r\n                  y = u;\r\n                } else { break c; }\r\n              }\r\n              Ed(A);\r\n              Ed(B);\r\n              Ed(J);\r\n              f = 1;\r\n              break a;\r\n            } else { f = i; } } while (0);\r\n            Ed(J);\r\n            Ed(d);\r\n            Ed(f);\r\n            f = 0;\r\n          } else {\r\n            Ed(d);\r\n            Ed(i);\r\n            Ed(J);\r\n          } } while (0);\r\n          J = f;\r\n          T = K;\r\n          return J | 0;\r\n        }\r\n      }\r\n      Ed(J);\r\n      J = f;\r\n      T = K;\r\n      return J | 0;\r\n    }\r\n    function ma(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0;\r\n      l = T;\r\n      T = T + 176 | 0;\r\n      j = l;\r\n      if ((c | 0) < 1) {\r\n        ud(d, 0, 0);\r\n        k = 0;\r\n        T = l;\r\n        return k | 0;\r\n      }\r\n      i = a;\r\n      i = Qd(b[i >> 2] | 0, b[i + 4 >> 2] | 0, 52) | 0;\r\n      H() | 0;\r\n      ud(d, (c | 0) > 6 ? c : 6, i & 15);\r\n      i = 0;\r\n      while (1) {\r\n        e = a + (i << 3) | 0;\r\n        e = _b(b[e >> 2] | 0, b[e + 4 >> 2] | 0, j) | 0;\r\n        if (e | 0) { break; }\r\n        e = b[j >> 2] | 0;\r\n        if ((e | 0) > 0) {\r\n          h = 0;\r\n          do {\r\n            g = j + 8 + (h << 4) | 0;\r\n            h = h + 1 | 0;\r\n            e = j + 8 + (((h | 0) % (e | 0) | 0) << 4) | 0;\r\n            f = zd(d, e, g) | 0;\r\n            if (!f) { yd(d, g, e) | 0; }else { xd(d, f) | 0; }\r\n            e = b[j >> 2] | 0;\r\n          } while ((h | 0) < (e | 0));\r\n        }\r\n        i = i + 1 | 0;\r\n        if ((i | 0) >= (c | 0)) {\r\n          e = 0;\r\n          k = 13;\r\n          break;\r\n        }\r\n      }\r\n      if ((k | 0) == 13) {\r\n        T = l;\r\n        return e | 0;\r\n      }\r\n      vd(d);\r\n      k = e;\r\n      T = l;\r\n      return k | 0;\r\n    }\r\n    function na(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      g = T;\r\n      T = T + 32 | 0;\r\n      e = g;\r\n      f = g + 16 | 0;\r\n      a = ma(a, c, f) | 0;\r\n      if (a | 0) {\r\n        d = a;\r\n        T = g;\r\n        return d | 0;\r\n      }\r\n      b[d >> 2] = 0;\r\n      b[d + 4 >> 2] = 0;\r\n      b[d + 8 >> 2] = 0;\r\n      a = wd(f) | 0;\r\n      if (a | 0) { do {\r\n        c = Cc(d) | 0;\r\n        do {\r\n          Dc(c, a) | 0;\r\n          h = a + 16 | 0;\r\n          b[e >> 2] = b[h >> 2];\r\n          b[e + 4 >> 2] = b[h + 4 >> 2];\r\n          b[e + 8 >> 2] = b[h + 8 >> 2];\r\n          b[e + 12 >> 2] = b[h + 12 >> 2];\r\n          xd(f, a) | 0;\r\n          a = Ad(f, e) | 0;\r\n        } while ((a | 0) != 0);\r\n        a = wd(f) | 0;\r\n      } while ((a | 0) != 0); }\r\n      vd(f);\r\n      a = Fc(d) | 0;\r\n      if (!a) {\r\n        h = 0;\r\n        T = g;\r\n        return h | 0;\r\n      }\r\n      Ec(d);\r\n      h = a;\r\n      T = g;\r\n      return h | 0;\r\n    }\r\n    function oa(a) {\r\n      a = a | 0;\r\n      if (a >>> 0 > 121) {\r\n        a = 0;\r\n        return a | 0;\r\n      }\r\n      a = b[7696 + (a * 28 | 0) + 16 >> 2] | 0;\r\n      return a | 0;\r\n    }\r\n    function pa(a) {\r\n      a = a | 0;\r\n      return (a | 0) == 4 | (a | 0) == 117 | 0;\r\n    }\r\n    function qa(a) {\r\n      a = a | 0;\r\n      return b[11120 + ((b[a >> 2] | 0) * 216 | 0) + ((b[a + 4 >> 2] | 0) * 72 | 0) + ((b[a + 8 >> 2] | 0) * 24 | 0) + (b[a + 12 >> 2] << 3) >> 2] | 0;\r\n    }\r\n    function ra(a) {\r\n      a = a | 0;\r\n      return b[11120 + ((b[a >> 2] | 0) * 216 | 0) + ((b[a + 4 >> 2] | 0) * 72 | 0) + ((b[a + 8 >> 2] | 0) * 24 | 0) + (b[a + 12 >> 2] << 3) + 4 >> 2] | 0;\r\n    }\r\n    function sa(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      a = 7696 + (a * 28 | 0) | 0;\r\n      b[c >> 2] = b[a >> 2];\r\n      b[c + 4 >> 2] = b[a + 4 >> 2];\r\n      b[c + 8 >> 2] = b[a + 8 >> 2];\r\n      b[c + 12 >> 2] = b[a + 12 >> 2];\r\n      return;\r\n    }\r\n    function ta(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0;\r\n      if (c >>> 0 > 20) {\r\n        c = -1;\r\n        return c | 0;\r\n      }\r\n      do { if ((b[11120 + (c * 216 | 0) >> 2] | 0) != (a | 0)) {\r\n        if ((b[11120 + (c * 216 | 0) + 8 >> 2] | 0) != (a | 0)) {\r\n          if ((b[11120 + (c * 216 | 0) + 16 >> 2] | 0) != (a | 0)) {\r\n            if ((b[11120 + (c * 216 | 0) + 24 >> 2] | 0) != (a | 0)) {\r\n              if ((b[11120 + (c * 216 | 0) + 32 >> 2] | 0) != (a | 0)) {\r\n                if ((b[11120 + (c * 216 | 0) + 40 >> 2] | 0) != (a | 0)) {\r\n                  if ((b[11120 + (c * 216 | 0) + 48 >> 2] | 0) != (a | 0)) {\r\n                    if ((b[11120 + (c * 216 | 0) + 56 >> 2] | 0) != (a | 0)) {\r\n                      if ((b[11120 + (c * 216 | 0) + 64 >> 2] | 0) != (a | 0)) {\r\n                        if ((b[11120 + (c * 216 | 0) + 72 >> 2] | 0) != (a | 0)) {\r\n                          if ((b[11120 + (c * 216 | 0) + 80 >> 2] | 0) != (a | 0)) {\r\n                            if ((b[11120 + (c * 216 | 0) + 88 >> 2] | 0) != (a | 0)) {\r\n                              if ((b[11120 + (c * 216 | 0) + 96 >> 2] | 0) != (a | 0)) {\r\n                                if ((b[11120 + (c * 216 | 0) + 104 >> 2] | 0) != (a | 0)) {\r\n                                  if ((b[11120 + (c * 216 | 0) + 112 >> 2] | 0) != (a | 0)) {\r\n                                    if ((b[11120 + (c * 216 | 0) + 120 >> 2] | 0) != (a | 0)) {\r\n                                      if ((b[11120 + (c * 216 | 0) + 128 >> 2] | 0) != (a | 0)) {\r\n                                        if ((b[11120 + (c * 216 | 0) + 136 >> 2] | 0) == (a | 0)) {\r\n                                          a = 2;\r\n                                          d = 1;\r\n                                          e = 2;\r\n                                        } else {\r\n                                          if ((b[11120 + (c * 216 | 0) + 144 >> 2] | 0) == (a | 0)) {\r\n                                            a = 0;\r\n                                            d = 2;\r\n                                            e = 0;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 152 >> 2] | 0) == (a | 0)) {\r\n                                            a = 0;\r\n                                            d = 2;\r\n                                            e = 1;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 160 >> 2] | 0) == (a | 0)) {\r\n                                            a = 0;\r\n                                            d = 2;\r\n                                            e = 2;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 168 >> 2] | 0) == (a | 0)) {\r\n                                            a = 1;\r\n                                            d = 2;\r\n                                            e = 0;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 176 >> 2] | 0) == (a | 0)) {\r\n                                            a = 1;\r\n                                            d = 2;\r\n                                            e = 1;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 184 >> 2] | 0) == (a | 0)) {\r\n                                            a = 1;\r\n                                            d = 2;\r\n                                            e = 2;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 192 >> 2] | 0) == (a | 0)) {\r\n                                            a = 2;\r\n                                            d = 2;\r\n                                            e = 0;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 200 >> 2] | 0) == (a | 0)) {\r\n                                            a = 2;\r\n                                            d = 2;\r\n                                            e = 1;\r\n                                            break;\r\n                                          }\r\n                                          if ((b[11120 + (c * 216 | 0) + 208 >> 2] | 0) == (a | 0)) {\r\n                                            a = 2;\r\n                                            d = 2;\r\n                                            e = 2;\r\n                                            break;\r\n                                          } else { a = -1; }\r\n                                          return a | 0;\r\n                                        }\r\n                                      } else {\r\n                                        a = 2;\r\n                                        d = 1;\r\n                                        e = 1;\r\n                                      }\r\n                                    } else {\r\n                                      a = 2;\r\n                                      d = 1;\r\n                                      e = 0;\r\n                                    }\r\n                                  } else {\r\n                                    a = 1;\r\n                                    d = 1;\r\n                                    e = 2;\r\n                                  }\r\n                                } else {\r\n                                  a = 1;\r\n                                  d = 1;\r\n                                  e = 1;\r\n                                }\r\n                              } else {\r\n                                a = 1;\r\n                                d = 1;\r\n                                e = 0;\r\n                              }\r\n                            } else {\r\n                              a = 0;\r\n                              d = 1;\r\n                              e = 2;\r\n                            }\r\n                          } else {\r\n                            a = 0;\r\n                            d = 1;\r\n                            e = 1;\r\n                          }\r\n                        } else {\r\n                          a = 0;\r\n                          d = 1;\r\n                          e = 0;\r\n                        }\r\n                      } else {\r\n                        a = 2;\r\n                        d = 0;\r\n                        e = 2;\r\n                      }\r\n                    } else {\r\n                      a = 2;\r\n                      d = 0;\r\n                      e = 1;\r\n                    }\r\n                  } else {\r\n                    a = 2;\r\n                    d = 0;\r\n                    e = 0;\r\n                  }\r\n                } else {\r\n                  a = 1;\r\n                  d = 0;\r\n                  e = 2;\r\n                }\r\n              } else {\r\n                a = 1;\r\n                d = 0;\r\n                e = 1;\r\n              }\r\n            } else {\r\n              a = 1;\r\n              d = 0;\r\n              e = 0;\r\n            }\r\n          } else {\r\n            a = 0;\r\n            d = 0;\r\n            e = 2;\r\n          }\r\n        } else {\r\n          a = 0;\r\n          d = 0;\r\n          e = 1;\r\n        }\r\n      } else {\r\n        a = 0;\r\n        d = 0;\r\n        e = 0;\r\n      } } while (0);\r\n      c = b[11120 + (c * 216 | 0) + (d * 72 | 0) + (a * 24 | 0) + (e << 3) + 4 >> 2] | 0;\r\n      return c | 0;\r\n    }\r\n    function ua(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      if ((b[7696 + (a * 28 | 0) + 20 >> 2] | 0) == (c | 0)) {\r\n        c = 1;\r\n        return c | 0;\r\n      }\r\n      c = (b[7696 + (a * 28 | 0) + 24 >> 2] | 0) == (c | 0);\r\n      return c | 0;\r\n    }\r\n    function va(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      return b[848 + (a * 28 | 0) + (c << 2) >> 2] | 0;\r\n    }\r\n    function wa(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      if ((b[848 + (a * 28 | 0) >> 2] | 0) == (c | 0)) {\r\n        c = 0;\r\n        return c | 0;\r\n      }\r\n      if ((b[848 + (a * 28 | 0) + 4 >> 2] | 0) == (c | 0)) {\r\n        c = 1;\r\n        return c | 0;\r\n      }\r\n      if ((b[848 + (a * 28 | 0) + 8 >> 2] | 0) == (c | 0)) {\r\n        c = 2;\r\n        return c | 0;\r\n      }\r\n      if ((b[848 + (a * 28 | 0) + 12 >> 2] | 0) == (c | 0)) {\r\n        c = 3;\r\n        return c | 0;\r\n      }\r\n      if ((b[848 + (a * 28 | 0) + 16 >> 2] | 0) == (c | 0)) {\r\n        c = 4;\r\n        return c | 0;\r\n      }\r\n      if ((b[848 + (a * 28 | 0) + 20 >> 2] | 0) == (c | 0)) {\r\n        c = 5;\r\n        return c | 0;\r\n      } else { return ((b[848 + (a * 28 | 0) + 24 >> 2] | 0) == (c | 0) ? 6 : 7) | 0; }\r\n      return 0;\r\n    }\r\n    function xa() {\r\n      return 122;\r\n    }\r\n    function ya(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      c = 0;\r\n      do {\r\n        Rd(c | 0, 0, 45) | 0;\r\n        e = H() | 0 | 134225919;\r\n        d = a + (c << 3) | 0;\r\n        b[d >> 2] = -1;\r\n        b[d + 4 >> 2] = e;\r\n        c = c + 1 | 0;\r\n      } while ((c | 0) != 122);\r\n      return 0;\r\n    }\r\n    function za(a) {\r\n      a = a | 0;\r\n      var b = 0.0,\r\n        c = 0.0,\r\n        d = 0.0;\r\n      d = +e[a + 16 >> 3];\r\n      c = +e[a + 24 >> 3];\r\n      b = d - c;\r\n      return +(d < c ? b + 6.283185307179586 : b);\r\n    }\r\n    function Aa(a) {\r\n      a = a | 0;\r\n      return +e[a + 16 >> 3] < +e[a + 24 >> 3] | 0;\r\n    }\r\n    function Ba(a) {\r\n      a = a | 0;\r\n      return +(+e[a >> 3] - +e[a + 8 >> 3]);\r\n    }\r\n    function Ca(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0;\r\n      c = +e[b >> 3];\r\n      if (!(c >= +e[a + 8 >> 3])) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      if (!(c <= +e[a >> 3])) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      d = +e[a + 16 >> 3];\r\n      c = +e[a + 24 >> 3];\r\n      f = +e[b + 8 >> 3];\r\n      b = f >= c;\r\n      a = f <= d & 1;\r\n      if (d < c) {\r\n        if (b) { a = 1; }\r\n      } else if (!b) { a = 0; }\r\n      b = (a | 0) != 0;\r\n      return b | 0;\r\n    }\r\n    function Da(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        f = 0,\r\n        g = 0.0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0;\r\n      if (+e[a >> 3] < +e[b + 8 >> 3]) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      if (+e[a + 8 >> 3] > +e[b >> 3]) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      g = +e[a + 16 >> 3];\r\n      c = a + 24 | 0;\r\n      l = +e[c >> 3];\r\n      h = g < l;\r\n      d = b + 16 | 0;\r\n      k = +e[d >> 3];\r\n      f = b + 24 | 0;\r\n      j = +e[f >> 3];\r\n      i = k < j;\r\n      b = l - k < j - g;\r\n      a = h ? i | b ? 1 : 2 : 0;\r\n      b = i ? h ? 1 : b ? 2 : 1 : 0;\r\n      g = +kc(g, a);\r\n      if (g < +kc(+e[f >> 3], b)) {\r\n        i = 0;\r\n        return i | 0;\r\n      }\r\n      l = +kc(+e[c >> 3], a);\r\n      if (l > +kc(+e[d >> 3], b)) {\r\n        i = 0;\r\n        return i | 0;\r\n      }\r\n      i = 1;\r\n      return i | 0;\r\n    }\r\n    function Ea(a, c, d, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0;\r\n      h = +e[a + 16 >> 3];\r\n      k = +e[a + 24 >> 3];\r\n      a = h < k;\r\n      j = +e[c + 16 >> 3];\r\n      i = +e[c + 24 >> 3];\r\n      g = j < i;\r\n      c = k - j < i - h;\r\n      b[d >> 2] = a ? g | c ? 1 : 2 : 0;\r\n      b[f >> 2] = g ? a ? 1 : c ? 2 : 1 : 0;\r\n      return;\r\n    }\r\n    function Fa(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        f = 0,\r\n        g = 0.0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0;\r\n      if (+e[a >> 3] < +e[b >> 3]) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      if (+e[a + 8 >> 3] > +e[b + 8 >> 3]) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      d = a + 16 | 0;\r\n      j = +e[d >> 3];\r\n      g = +e[a + 24 >> 3];\r\n      h = j < g;\r\n      c = b + 16 | 0;\r\n      l = +e[c >> 3];\r\n      f = b + 24 | 0;\r\n      k = +e[f >> 3];\r\n      i = l < k;\r\n      b = g - l < k - j;\r\n      a = h ? i | b ? 1 : 2 : 0;\r\n      b = i ? h ? 1 : b ? 2 : 1 : 0;\r\n      g = +kc(g, a);\r\n      if (!(g <= +kc(+e[f >> 3], b))) {\r\n        i = 0;\r\n        return i | 0;\r\n      }\r\n      l = +kc(+e[d >> 3], a);\r\n      i = l >= +kc(+e[c >> 3], b);\r\n      return i | 0;\r\n    }\r\n    function Ga(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0;\r\n      g = T;\r\n      T = T + 176 | 0;\r\n      f = g;\r\n      b[f >> 2] = 4;\r\n      j = +e[c >> 3];\r\n      e[f + 8 >> 3] = j;\r\n      h = +e[c + 16 >> 3];\r\n      e[f + 16 >> 3] = h;\r\n      e[f + 24 >> 3] = j;\r\n      j = +e[c + 24 >> 3];\r\n      e[f + 32 >> 3] = j;\r\n      i = +e[c + 8 >> 3];\r\n      e[f + 40 >> 3] = i;\r\n      e[f + 48 >> 3] = j;\r\n      e[f + 56 >> 3] = i;\r\n      e[f + 64 >> 3] = h;\r\n      c = f + 72 | 0;\r\n      d = c + 96 | 0;\r\n      do {\r\n        b[c >> 2] = 0;\r\n        c = c + 4 | 0;\r\n      } while ((c | 0) < (d | 0));\r\n      Wd(a | 0, f | 0, 168) | 0;\r\n      T = g;\r\n      return;\r\n    }\r\n    function Ha(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0.0,\r\n        v = 0.0;\r\n      t = T;\r\n      T = T + 288 | 0;\r\n      n = t + 264 | 0;\r\n      o = t + 96 | 0;\r\n      m = t;\r\n      k = m;\r\n      l = k + 96 | 0;\r\n      do {\r\n        b[k >> 2] = 0;\r\n        k = k + 4 | 0;\r\n      } while ((k | 0) < (l | 0));\r\n      c = cc(c, m) | 0;\r\n      if (c | 0) {\r\n        s = c;\r\n        T = t;\r\n        return s | 0;\r\n      }\r\n      l = m;\r\n      m = b[l >> 2] | 0;\r\n      l = b[l + 4 >> 2] | 0;\r\n      Zb(m, l, n) | 0;\r\n      _b(m, l, o) | 0;\r\n      j = +mc(n, o + 8 | 0);\r\n      e[n >> 3] = +e[a >> 3];\r\n      l = n + 8 | 0;\r\n      e[l >> 3] = +e[a + 16 >> 3];\r\n      e[o >> 3] = +e[a + 8 >> 3];\r\n      m = o + 8 | 0;\r\n      e[m >> 3] = +e[a + 24 >> 3];\r\n      h = +mc(n, o);\r\n      v = +e[l >> 3] - +e[m >> 3];\r\n      i = +q(+v);\r\n      u = +e[n >> 3] - +e[o >> 3];\r\n      g = +q(+u);\r\n      if (!(v == 0.0 | u == 0.0) ? (v = +Td(+i, +g), v = +A(+(h * h / +Ud(+(v / +Ud(+i, +g)), 3.0) / (j * (j * 2.59807621135) * .8))), e[f >> 3] = v, r = ~~v >>> 0, s = +q(v) >= 1.0 ? v > 0.0 ? ~~+C(+p(v / 4294967296.0), 4294967295.0) >>> 0 : ~~+A((v - +(~~v >>> 0)) / 4294967296.0) >>> 0 : 0, !((b[f + 4 >> 2] & 2146435072 | 0) == 2146435072)) : 0) {\r\n        o = (r | 0) == 0 & (s | 0) == 0;\r\n        c = d;\r\n        b[c >> 2] = o ? 1 : r;\r\n        b[c + 4 >> 2] = o ? 0 : s;\r\n        c = 0;\r\n      } else { c = 1; }\r\n      s = c;\r\n      T = t;\r\n      return s | 0;\r\n    }\r\n    function Ia(a, c, d, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0.0;\r\n      m = T;\r\n      T = T + 288 | 0;\r\n      j = m + 264 | 0;\r\n      k = m + 96 | 0;\r\n      l = m;\r\n      h = l;\r\n      i = h + 96 | 0;\r\n      do {\r\n        b[h >> 2] = 0;\r\n        h = h + 4 | 0;\r\n      } while ((h | 0) < (i | 0));\r\n      d = cc(d, l) | 0;\r\n      if (d | 0) {\r\n        g = d;\r\n        T = m;\r\n        return g | 0;\r\n      }\r\n      d = l;\r\n      h = b[d >> 2] | 0;\r\n      d = b[d + 4 >> 2] | 0;\r\n      Zb(h, d, j) | 0;\r\n      _b(h, d, k) | 0;\r\n      n = +mc(j, k + 8 | 0);\r\n      n = +A(+(+mc(a, c) / (n * 2.0)));\r\n      e[f >> 3] = n;\r\n      d = ~~n >>> 0;\r\n      h = +q(n) >= 1.0 ? n > 0.0 ? ~~+C(+p(n / 4294967296.0), 4294967295.0) >>> 0 : ~~+A((n - +(~~n >>> 0)) / 4294967296.0) >>> 0 : 0;\r\n      if ((b[f + 4 >> 2] & 2146435072 | 0) == 2146435072) {\r\n        g = 1;\r\n        T = m;\r\n        return g | 0;\r\n      }\r\n      l = (d | 0) == 0 & (h | 0) == 0;\r\n      b[g >> 2] = l ? 1 : d;\r\n      b[g + 4 >> 2] = l ? 0 : h;\r\n      g = 0;\r\n      T = m;\r\n      return g | 0;\r\n    }\r\n    function Ja(a, b) {\r\n      a = a | 0;\r\n      b = +b;\r\n      var c = 0,\r\n        d = 0.0,\r\n        f = 0.0,\r\n        g = 0,\r\n        h = 0.0,\r\n        i = 0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0;\r\n      g = a + 16 | 0;\r\n      h = +e[g >> 3];\r\n      c = a + 24 | 0;\r\n      f = +e[c >> 3];\r\n      d = h - f;\r\n      d = h < f ? d + 6.283185307179586 : d;\r\n      k = +e[a >> 3];\r\n      i = a + 8 | 0;\r\n      j = +e[i >> 3];\r\n      l = k - j;\r\n      d = (d * b - d) * .5;\r\n      b = (l * b - l) * .5;\r\n      k = k + b;\r\n      e[a >> 3] = k > 1.5707963267948966 ? 1.5707963267948966 : k;\r\n      b = j - b;\r\n      e[i >> 3] = b < -1.5707963267948966 ? -1.5707963267948966 : b;\r\n      b = h + d;\r\n      b = b > 3.141592653589793 ? b + -6.283185307179586 : b;\r\n      e[g >> 3] = b < -3.141592653589793 ? b + 6.283185307179586 : b;\r\n      b = f - d;\r\n      b = b > 3.141592653589793 ? b + -6.283185307179586 : b;\r\n      e[c >> 3] = b < -3.141592653589793 ? b + 6.283185307179586 : b;\r\n      return;\r\n    }\r\n    function Ka(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      b[a >> 2] = c;\r\n      b[a + 4 >> 2] = d;\r\n      b[a + 8 >> 2] = e;\r\n      return;\r\n    }\r\n    function La(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0.0;\r\n      n = c + 8 | 0;\r\n      b[n >> 2] = 0;\r\n      k = +e[a >> 3];\r\n      i = +q(+k);\r\n      l = +e[a + 8 >> 3];\r\n      j = +q(+l) * 1.1547005383792515;\r\n      i = i + j * .5;\r\n      d = ~~i;\r\n      a = ~~j;\r\n      i = i - +(d | 0);\r\n      j = j - +(a | 0);\r\n      do { if (i < .5) {\r\n        if (i < .3333333333333333) {\r\n          b[c >> 2] = d;\r\n          if (j < (i + 1.0) * .5) {\r\n            b[c + 4 >> 2] = a;\r\n            break;\r\n          } else {\r\n            a = a + 1 | 0;\r\n            b[c + 4 >> 2] = a;\r\n            break;\r\n          }\r\n        } else {\r\n          o = 1.0 - i;\r\n          a = (!(j < o) & 1) + a | 0;\r\n          b[c + 4 >> 2] = a;\r\n          if (o <= j & j < i * 2.0) {\r\n            d = d + 1 | 0;\r\n            b[c >> 2] = d;\r\n            break;\r\n          } else {\r\n            b[c >> 2] = d;\r\n            break;\r\n          }\r\n        }\r\n      } else {\r\n        if (!(i < .6666666666666666)) {\r\n          d = d + 1 | 0;\r\n          b[c >> 2] = d;\r\n          if (j < i * .5) {\r\n            b[c + 4 >> 2] = a;\r\n            break;\r\n          } else {\r\n            a = a + 1 | 0;\r\n            b[c + 4 >> 2] = a;\r\n            break;\r\n          }\r\n        }\r\n        if (j < 1.0 - i) {\r\n          b[c + 4 >> 2] = a;\r\n          if (i * 2.0 + -1.0 < j) {\r\n            b[c >> 2] = d;\r\n            break;\r\n          }\r\n        } else {\r\n          a = a + 1 | 0;\r\n          b[c + 4 >> 2] = a;\r\n        }\r\n        d = d + 1 | 0;\r\n        b[c >> 2] = d;\r\n      } } while (0);\r\n      do { if (k < 0.0) { if (!(a & 1)) {\r\n        m = (a | 0) / 2 | 0;\r\n        m = Hd(d | 0, ((d | 0) < 0) << 31 >> 31 | 0, m | 0, ((m | 0) < 0) << 31 >> 31 | 0) | 0;\r\n        d = ~~(+(d | 0) - (+(m >>> 0) + 4294967296.0 * +(H() | 0)) * 2.0);\r\n        b[c >> 2] = d;\r\n        break;\r\n      } else {\r\n        m = (a + 1 | 0) / 2 | 0;\r\n        m = Hd(d | 0, ((d | 0) < 0) << 31 >> 31 | 0, m | 0, ((m | 0) < 0) << 31 >> 31 | 0) | 0;\r\n        d = ~~(+(d | 0) - ((+(m >>> 0) + 4294967296.0 * +(H() | 0)) * 2.0 + 1.0));\r\n        b[c >> 2] = d;\r\n        break;\r\n      } } } while (0);\r\n      m = c + 4 | 0;\r\n      if (l < 0.0) {\r\n        d = d - ((a << 1 | 1 | 0) / 2 | 0) | 0;\r\n        b[c >> 2] = d;\r\n        a = 0 - a | 0;\r\n        b[m >> 2] = a;\r\n      }\r\n      f = a - d | 0;\r\n      if ((d | 0) < 0) {\r\n        g = 0 - d | 0;\r\n        b[m >> 2] = f;\r\n        b[n >> 2] = g;\r\n        b[c >> 2] = 0;\r\n        a = f;\r\n        d = 0;\r\n      } else { g = 0; }\r\n      if ((a | 0) < 0) {\r\n        d = d - a | 0;\r\n        b[c >> 2] = d;\r\n        g = g - a | 0;\r\n        b[n >> 2] = g;\r\n        b[m >> 2] = 0;\r\n        a = 0;\r\n      }\r\n      h = d - g | 0;\r\n      f = a - g | 0;\r\n      if ((g | 0) < 0) {\r\n        b[c >> 2] = h;\r\n        b[m >> 2] = f;\r\n        b[n >> 2] = 0;\r\n        a = f;\r\n        d = h;\r\n        g = 0;\r\n      }\r\n      f = (a | 0) < (d | 0) ? a : d;\r\n      f = (g | 0) < (f | 0) ? g : f;\r\n      if ((f | 0) <= 0) { return; }\r\n      b[c >> 2] = d - f;\r\n      b[m >> 2] = a - f;\r\n      b[n >> 2] = g - f;\r\n      return;\r\n    }\r\n    function Ma(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      c = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      d = b[h >> 2] | 0;\r\n      if ((c | 0) < 0) {\r\n        d = d - c | 0;\r\n        b[h >> 2] = d;\r\n        g = a + 8 | 0;\r\n        b[g >> 2] = (b[g >> 2] | 0) - c;\r\n        b[a >> 2] = 0;\r\n        c = 0;\r\n      }\r\n      if ((d | 0) < 0) {\r\n        c = c - d | 0;\r\n        b[a >> 2] = c;\r\n        g = a + 8 | 0;\r\n        f = (b[g >> 2] | 0) - d | 0;\r\n        b[g >> 2] = f;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      } else {\r\n        f = a + 8 | 0;\r\n        g = f;\r\n        f = b[f >> 2] | 0;\r\n      }\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[a >> 2] = c;\r\n        d = d - f | 0;\r\n        b[h >> 2] = d;\r\n        b[g >> 2] = 0;\r\n        f = 0;\r\n      }\r\n      e = (d | 0) < (c | 0) ? d : c;\r\n      e = (f | 0) < (e | 0) ? f : e;\r\n      if ((e | 0) <= 0) { return; }\r\n      b[a >> 2] = c - e;\r\n      b[h >> 2] = d - e;\r\n      b[g >> 2] = f - e;\r\n      return;\r\n    }\r\n    function Na(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0.0,\r\n        f = 0;\r\n      f = b[a + 8 >> 2] | 0;\r\n      d = +((b[a + 4 >> 2] | 0) - f | 0);\r\n      e[c >> 3] = +((b[a >> 2] | 0) - f | 0) - d * .5;\r\n      e[c + 8 >> 3] = d * .8660254037844386;\r\n      return;\r\n    }\r\n    function Oa(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      b[d >> 2] = (b[c >> 2] | 0) + (b[a >> 2] | 0);\r\n      b[d + 4 >> 2] = (b[c + 4 >> 2] | 0) + (b[a + 4 >> 2] | 0);\r\n      b[d + 8 >> 2] = (b[c + 8 >> 2] | 0) + (b[a + 8 >> 2] | 0);\r\n      return;\r\n    }\r\n    function Pa(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      b[d >> 2] = (b[a >> 2] | 0) - (b[c >> 2] | 0);\r\n      b[d + 4 >> 2] = (b[a + 4 >> 2] | 0) - (b[c + 4 >> 2] | 0);\r\n      b[d + 8 >> 2] = (b[a + 8 >> 2] | 0) - (b[c + 8 >> 2] | 0);\r\n      return;\r\n    }\r\n    function Qa(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0;\r\n      d = B(b[a >> 2] | 0, c) | 0;\r\n      b[a >> 2] = d;\r\n      d = a + 4 | 0;\r\n      e = B(b[d >> 2] | 0, c) | 0;\r\n      b[d >> 2] = e;\r\n      a = a + 8 | 0;\r\n      c = B(b[a >> 2] | 0, c) | 0;\r\n      b[a >> 2] = c;\r\n      return;\r\n    }\r\n    function Ra(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = b[a >> 2] | 0;\r\n      i = (h | 0) < 0;\r\n      e = (b[a + 4 >> 2] | 0) - (i ? h : 0) | 0;\r\n      g = (e | 0) < 0;\r\n      f = (g ? 0 - e | 0 : 0) + ((b[a + 8 >> 2] | 0) - (i ? h : 0)) | 0;\r\n      d = (f | 0) < 0;\r\n      a = d ? 0 : f;\r\n      c = (g ? 0 : e) - (d ? f : 0) | 0;\r\n      f = (i ? 0 : h) - (g ? e : 0) - (d ? f : 0) | 0;\r\n      d = (c | 0) < (f | 0) ? c : f;\r\n      d = (a | 0) < (d | 0) ? a : d;\r\n      e = (d | 0) > 0;\r\n      a = a - (e ? d : 0) | 0;\r\n      c = c - (e ? d : 0) | 0;\r\n      a: do { switch (f - (e ? d : 0) | 0) {\r\n        case 0:\r\n          switch (c | 0) {\r\n            case 0:\r\n              {\r\n                i = (a | 0) == 0 ? 0 : (a | 0) == 1 ? 1 : 7;\r\n                return i | 0;\r\n              }\r\n            case 1:\r\n              {\r\n                i = (a | 0) == 0 ? 2 : (a | 0) == 1 ? 3 : 7;\r\n                return i | 0;\r\n              }\r\n            default:\r\n              break a;\r\n          }\r\n        case 1:\r\n          switch (c | 0) {\r\n            case 0:\r\n              {\r\n                i = (a | 0) == 0 ? 4 : (a | 0) == 1 ? 5 : 7;\r\n                return i | 0;\r\n              }\r\n            case 1:\r\n              {\r\n                if (!a) { a = 6; }else { break a; }\r\n                return a | 0;\r\n              }\r\n            default:\r\n              break a;\r\n          }\r\n        default:\r\n\r\n      } } while (0);\r\n      i = 7;\r\n      return i | 0;\r\n    }\r\n    function Sa(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      j = a + 8 | 0;\r\n      h = b[j >> 2] | 0;\r\n      i = (b[a >> 2] | 0) - h | 0;\r\n      k = a + 4 | 0;\r\n      h = (b[k >> 2] | 0) - h | 0;\r\n      if (i >>> 0 > 715827881 | h >>> 0 > 715827881) {\r\n        e = (i | 0) > 0;\r\n        f = 2147483647 - i | 0;\r\n        g = -2147483648 - i | 0;\r\n        if (e ? (f | 0) < (i | 0) : (g | 0) > (i | 0)) {\r\n          k = 1;\r\n          return k | 0;\r\n        }\r\n        d = i << 1;\r\n        if (e ? (2147483647 - d | 0) < (i | 0) : (-2147483648 - d | 0) > (i | 0)) {\r\n          k = 1;\r\n          return k | 0;\r\n        }\r\n        if ((h | 0) > 0 ? (2147483647 - h | 0) < (h | 0) : (-2147483648 - h | 0) > (h | 0)) {\r\n          k = 1;\r\n          return k | 0;\r\n        }\r\n        c = i * 3 | 0;\r\n        d = h << 1;\r\n        if ((e ? (f | 0) < (d | 0) : (g | 0) > (d | 0)) ? 1 : (i | 0) > -1 ? (c | -2147483648 | 0) >= (h | 0) : (c ^ -2147483648 | 0) < (h | 0)) {\r\n          k = 1;\r\n          return k | 0;\r\n        }\r\n      } else {\r\n        d = h << 1;\r\n        c = i * 3 | 0;\r\n      }\r\n      e = Cd(+(c - h | 0) * .14285714285714285) | 0;\r\n      b[a >> 2] = e;\r\n      f = Cd(+(d + i | 0) * .14285714285714285) | 0;\r\n      b[k >> 2] = f;\r\n      b[j >> 2] = 0;\r\n      d = (f | 0) < (e | 0);\r\n      c = d ? e : f;\r\n      d = d ? f : e;\r\n      if ((d | 0) < 0) {\r\n        if ((d | 0) == -2147483648 ? 1 : (c | 0) > 0 ? (2147483647 - c | 0) < (d | 0) : (-2147483648 - c | 0) > (d | 0)) { I(27795, 26892, 354, 26903); }\r\n        if ((c | 0) > -1 ? (c | -2147483648 | 0) >= (d | 0) : (c ^ -2147483648 | 0) < (d | 0)) { I(27795, 26892, 354, 26903); }\r\n      }\r\n      c = f - e | 0;\r\n      if ((e | 0) < 0) {\r\n        d = 0 - e | 0;\r\n        b[k >> 2] = c;\r\n        b[j >> 2] = d;\r\n        b[a >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        c = f;\r\n        d = 0;\r\n      }\r\n      if ((c | 0) < 0) {\r\n        e = e - c | 0;\r\n        b[a >> 2] = e;\r\n        d = d - c | 0;\r\n        b[j >> 2] = d;\r\n        b[k >> 2] = 0;\r\n        c = 0;\r\n      }\r\n      g = e - d | 0;\r\n      f = c - d | 0;\r\n      if ((d | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[k >> 2] = f;\r\n        b[j >> 2] = 0;\r\n        c = f;\r\n        f = g;\r\n        d = 0;\r\n      } else { f = e; }\r\n      e = (c | 0) < (f | 0) ? c : f;\r\n      e = (d | 0) < (e | 0) ? d : e;\r\n      if ((e | 0) <= 0) {\r\n        k = 0;\r\n        return k | 0;\r\n      }\r\n      b[a >> 2] = f - e;\r\n      b[k >> 2] = c - e;\r\n      b[j >> 2] = d - e;\r\n      k = 0;\r\n      return k | 0;\r\n    }\r\n    function Ta(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      h = a + 8 | 0;\r\n      f = b[h >> 2] | 0;\r\n      g = (b[a >> 2] | 0) - f | 0;\r\n      i = a + 4 | 0;\r\n      f = (b[i >> 2] | 0) - f | 0;\r\n      if (g >>> 0 > 715827881 | f >>> 0 > 715827881) {\r\n        d = (g | 0) > 0;\r\n        if (d ? (2147483647 - g | 0) < (g | 0) : (-2147483648 - g | 0) > (g | 0)) {\r\n          i = 1;\r\n          return i | 0;\r\n        }\r\n        c = g << 1;\r\n        e = (f | 0) > 0;\r\n        if (e ? (2147483647 - f | 0) < (f | 0) : (-2147483648 - f | 0) > (f | 0)) {\r\n          i = 1;\r\n          return i | 0;\r\n        }\r\n        j = f << 1;\r\n        if (e ? (2147483647 - j | 0) < (f | 0) : (-2147483648 - j | 0) > (f | 0)) {\r\n          j = 1;\r\n          return j | 0;\r\n        }\r\n        if (d ? (2147483647 - c | 0) < (f | 0) : (-2147483648 - c | 0) > (f | 0)) {\r\n          j = 1;\r\n          return j | 0;\r\n        }\r\n        d = f * 3 | 0;\r\n        if ((f | 0) > -1 ? (d | -2147483648 | 0) >= (g | 0) : (d ^ -2147483648 | 0) < (g | 0)) {\r\n          j = 1;\r\n          return j | 0;\r\n        }\r\n      } else {\r\n        d = f * 3 | 0;\r\n        c = g << 1;\r\n      }\r\n      e = Cd(+(c + f | 0) * .14285714285714285) | 0;\r\n      b[a >> 2] = e;\r\n      f = Cd(+(d - g | 0) * .14285714285714285) | 0;\r\n      b[i >> 2] = f;\r\n      b[h >> 2] = 0;\r\n      d = (f | 0) < (e | 0);\r\n      c = d ? e : f;\r\n      d = d ? f : e;\r\n      if ((d | 0) < 0) {\r\n        if ((d | 0) == -2147483648 ? 1 : (c | 0) > 0 ? (2147483647 - c | 0) < (d | 0) : (-2147483648 - c | 0) > (d | 0)) { I(27795, 26892, 402, 26917); }\r\n        if ((c | 0) > -1 ? (c | -2147483648 | 0) >= (d | 0) : (c ^ -2147483648 | 0) < (d | 0)) { I(27795, 26892, 402, 26917); }\r\n      }\r\n      c = f - e | 0;\r\n      if ((e | 0) < 0) {\r\n        d = 0 - e | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = d;\r\n        b[a >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        c = f;\r\n        d = 0;\r\n      }\r\n      if ((c | 0) < 0) {\r\n        e = e - c | 0;\r\n        b[a >> 2] = e;\r\n        d = d - c | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = 0;\r\n        c = 0;\r\n      }\r\n      g = e - d | 0;\r\n      f = c - d | 0;\r\n      if ((d | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[i >> 2] = f;\r\n        b[h >> 2] = 0;\r\n        c = f;\r\n        f = g;\r\n        d = 0;\r\n      } else { f = e; }\r\n      e = (c | 0) < (f | 0) ? c : f;\r\n      e = (d | 0) < (e | 0) ? d : e;\r\n      if ((e | 0) <= 0) {\r\n        j = 0;\r\n        return j | 0;\r\n      }\r\n      b[a >> 2] = f - e;\r\n      b[i >> 2] = c - e;\r\n      b[h >> 2] = d - e;\r\n      j = 0;\r\n      return j | 0;\r\n    }\r\n    function Ua(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = a + 8 | 0;\r\n      d = b[h >> 2] | 0;\r\n      c = (b[a >> 2] | 0) - d | 0;\r\n      i = a + 4 | 0;\r\n      d = (b[i >> 2] | 0) - d | 0;\r\n      e = Cd(+((c * 3 | 0) - d | 0) * .14285714285714285) | 0;\r\n      b[a >> 2] = e;\r\n      c = Cd(+((d << 1) + c | 0) * .14285714285714285) | 0;\r\n      b[i >> 2] = c;\r\n      b[h >> 2] = 0;\r\n      d = c - e | 0;\r\n      if ((e | 0) < 0) {\r\n        g = 0 - e | 0;\r\n        b[i >> 2] = d;\r\n        b[h >> 2] = g;\r\n        b[a >> 2] = 0;\r\n        c = d;\r\n        e = 0;\r\n        d = g;\r\n      } else { d = 0; }\r\n      if ((c | 0) < 0) {\r\n        e = e - c | 0;\r\n        b[a >> 2] = e;\r\n        d = d - c | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = 0;\r\n        c = 0;\r\n      }\r\n      g = e - d | 0;\r\n      f = c - d | 0;\r\n      if ((d | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[i >> 2] = f;\r\n        b[h >> 2] = 0;\r\n        c = f;\r\n        f = g;\r\n        d = 0;\r\n      } else { f = e; }\r\n      e = (c | 0) < (f | 0) ? c : f;\r\n      e = (d | 0) < (e | 0) ? d : e;\r\n      if ((e | 0) <= 0) { return; }\r\n      b[a >> 2] = f - e;\r\n      b[i >> 2] = c - e;\r\n      b[h >> 2] = d - e;\r\n      return;\r\n    }\r\n    function Va(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = a + 8 | 0;\r\n      d = b[h >> 2] | 0;\r\n      c = (b[a >> 2] | 0) - d | 0;\r\n      i = a + 4 | 0;\r\n      d = (b[i >> 2] | 0) - d | 0;\r\n      e = Cd(+((c << 1) + d | 0) * .14285714285714285) | 0;\r\n      b[a >> 2] = e;\r\n      c = Cd(+((d * 3 | 0) - c | 0) * .14285714285714285) | 0;\r\n      b[i >> 2] = c;\r\n      b[h >> 2] = 0;\r\n      d = c - e | 0;\r\n      if ((e | 0) < 0) {\r\n        g = 0 - e | 0;\r\n        b[i >> 2] = d;\r\n        b[h >> 2] = g;\r\n        b[a >> 2] = 0;\r\n        c = d;\r\n        e = 0;\r\n        d = g;\r\n      } else { d = 0; }\r\n      if ((c | 0) < 0) {\r\n        e = e - c | 0;\r\n        b[a >> 2] = e;\r\n        d = d - c | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = 0;\r\n        c = 0;\r\n      }\r\n      g = e - d | 0;\r\n      f = c - d | 0;\r\n      if ((d | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[i >> 2] = f;\r\n        b[h >> 2] = 0;\r\n        c = f;\r\n        f = g;\r\n        d = 0;\r\n      } else { f = e; }\r\n      e = (c | 0) < (f | 0) ? c : f;\r\n      e = (d | 0) < (e | 0) ? d : e;\r\n      if ((e | 0) <= 0) { return; }\r\n      b[a >> 2] = f - e;\r\n      b[i >> 2] = c - e;\r\n      b[h >> 2] = d - e;\r\n      return;\r\n    }\r\n    function Wa(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      c = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      d = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      e = b[i >> 2] | 0;\r\n      f = d + (c * 3 | 0) | 0;\r\n      b[a >> 2] = f;\r\n      d = e + (d * 3 | 0) | 0;\r\n      b[h >> 2] = d;\r\n      c = (e * 3 | 0) + c | 0;\r\n      b[i >> 2] = c;\r\n      e = d - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = e;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        d = e;\r\n        e = 0;\r\n      } else { e = f; }\r\n      if ((d | 0) < 0) {\r\n        e = e - d | 0;\r\n        b[a >> 2] = e;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = e - c | 0;\r\n      f = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = f;\r\n        b[i >> 2] = 0;\r\n        e = g;\r\n        c = 0;\r\n      } else { f = d; }\r\n      d = (f | 0) < (e | 0) ? f : e;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = e - d;\r\n      b[h >> 2] = f - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function Xa(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      f = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      c = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      d = b[i >> 2] | 0;\r\n      e = (c * 3 | 0) + f | 0;\r\n      f = d + (f * 3 | 0) | 0;\r\n      b[a >> 2] = f;\r\n      b[h >> 2] = e;\r\n      c = (d * 3 | 0) + c | 0;\r\n      b[i >> 2] = c;\r\n      d = e - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        f = 0;\r\n      } else { d = e; }\r\n      if ((d | 0) < 0) {\r\n        f = f - d | 0;\r\n        b[a >> 2] = f;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = f - c | 0;\r\n      e = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = e;\r\n        b[i >> 2] = 0;\r\n        f = g;\r\n        c = 0;\r\n      } else { e = d; }\r\n      d = (e | 0) < (f | 0) ? e : f;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = f - d;\r\n      b[h >> 2] = e - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function Ya(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      if ((c + -1 | 0) >>> 0 >= 6) { return; }\r\n      f = (b[15440 + (c * 12 | 0) >> 2] | 0) + (b[a >> 2] | 0) | 0;\r\n      b[a >> 2] = f;\r\n      i = a + 4 | 0;\r\n      e = (b[15440 + (c * 12 | 0) + 4 >> 2] | 0) + (b[i >> 2] | 0) | 0;\r\n      b[i >> 2] = e;\r\n      h = a + 8 | 0;\r\n      c = (b[15440 + (c * 12 | 0) + 8 >> 2] | 0) + (b[h >> 2] | 0) | 0;\r\n      b[h >> 2] = c;\r\n      d = e - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[i >> 2] = d;\r\n        b[h >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        d = e;\r\n        e = f;\r\n      }\r\n      if ((d | 0) < 0) {\r\n        e = e - d | 0;\r\n        b[a >> 2] = e;\r\n        c = c - d | 0;\r\n        b[h >> 2] = c;\r\n        b[i >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = e - c | 0;\r\n      f = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[i >> 2] = f;\r\n        b[h >> 2] = 0;\r\n        e = g;\r\n        c = 0;\r\n      } else { f = d; }\r\n      d = (f | 0) < (e | 0) ? f : e;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = e - d;\r\n      b[i >> 2] = f - d;\r\n      b[h >> 2] = c - d;\r\n      return;\r\n    }\r\n    function Za(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      f = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      c = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      d = b[i >> 2] | 0;\r\n      e = c + f | 0;\r\n      f = d + f | 0;\r\n      b[a >> 2] = f;\r\n      b[h >> 2] = e;\r\n      c = d + c | 0;\r\n      b[i >> 2] = c;\r\n      d = e - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        d = e;\r\n        e = f;\r\n      }\r\n      if ((d | 0) < 0) {\r\n        e = e - d | 0;\r\n        b[a >> 2] = e;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = e - c | 0;\r\n      f = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = f;\r\n        b[i >> 2] = 0;\r\n        e = g;\r\n        c = 0;\r\n      } else { f = d; }\r\n      d = (f | 0) < (e | 0) ? f : e;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = e - d;\r\n      b[h >> 2] = f - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function _a(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      c = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      e = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      d = b[i >> 2] | 0;\r\n      f = e + c | 0;\r\n      b[a >> 2] = f;\r\n      e = d + e | 0;\r\n      b[h >> 2] = e;\r\n      c = d + c | 0;\r\n      b[i >> 2] = c;\r\n      d = e - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        d = e;\r\n        e = f;\r\n      }\r\n      if ((d | 0) < 0) {\r\n        e = e - d | 0;\r\n        b[a >> 2] = e;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = e - c | 0;\r\n      f = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = f;\r\n        b[i >> 2] = 0;\r\n        e = g;\r\n        c = 0;\r\n      } else { f = d; }\r\n      d = (f | 0) < (e | 0) ? f : e;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = e - d;\r\n      b[h >> 2] = f - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function $a(a) {\r\n      a = a | 0;\r\n      switch (a | 0) {\r\n        case 1:\r\n          {\r\n            a = 5;\r\n            break;\r\n          }\r\n        case 5:\r\n          {\r\n            a = 4;\r\n            break;\r\n          }\r\n        case 4:\r\n          {\r\n            a = 6;\r\n            break;\r\n          }\r\n        case 6:\r\n          {\r\n            a = 2;\r\n            break;\r\n          }\r\n        case 2:\r\n          {\r\n            a = 3;\r\n            break;\r\n          }\r\n        case 3:\r\n          {\r\n            a = 1;\r\n            break;\r\n          }\r\n        default:\r\n\r\n      }\r\n      return a | 0;\r\n    }\r\n    function ab(a) {\r\n      a = a | 0;\r\n      switch (a | 0) {\r\n        case 1:\r\n          {\r\n            a = 3;\r\n            break;\r\n          }\r\n        case 3:\r\n          {\r\n            a = 2;\r\n            break;\r\n          }\r\n        case 2:\r\n          {\r\n            a = 6;\r\n            break;\r\n          }\r\n        case 6:\r\n          {\r\n            a = 4;\r\n            break;\r\n          }\r\n        case 4:\r\n          {\r\n            a = 5;\r\n            break;\r\n          }\r\n        case 5:\r\n          {\r\n            a = 1;\r\n            break;\r\n          }\r\n        default:\r\n\r\n      }\r\n      return a | 0;\r\n    }\r\n    function bb(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      c = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      d = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      e = b[i >> 2] | 0;\r\n      f = d + (c << 1) | 0;\r\n      b[a >> 2] = f;\r\n      d = e + (d << 1) | 0;\r\n      b[h >> 2] = d;\r\n      c = (e << 1) + c | 0;\r\n      b[i >> 2] = c;\r\n      e = d - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = e;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        d = e;\r\n        e = 0;\r\n      } else { e = f; }\r\n      if ((d | 0) < 0) {\r\n        e = e - d | 0;\r\n        b[a >> 2] = e;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = e - c | 0;\r\n      f = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = f;\r\n        b[i >> 2] = 0;\r\n        e = g;\r\n        c = 0;\r\n      } else { f = d; }\r\n      d = (f | 0) < (e | 0) ? f : e;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = e - d;\r\n      b[h >> 2] = f - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function cb(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      f = b[a >> 2] | 0;\r\n      h = a + 4 | 0;\r\n      c = b[h >> 2] | 0;\r\n      i = a + 8 | 0;\r\n      d = b[i >> 2] | 0;\r\n      e = (c << 1) + f | 0;\r\n      f = d + (f << 1) | 0;\r\n      b[a >> 2] = f;\r\n      b[h >> 2] = e;\r\n      c = (d << 1) + c | 0;\r\n      b[i >> 2] = c;\r\n      d = e - f | 0;\r\n      if ((f | 0) < 0) {\r\n        c = c - f | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = c;\r\n        b[a >> 2] = 0;\r\n        f = 0;\r\n      } else { d = e; }\r\n      if ((d | 0) < 0) {\r\n        f = f - d | 0;\r\n        b[a >> 2] = f;\r\n        c = c - d | 0;\r\n        b[i >> 2] = c;\r\n        b[h >> 2] = 0;\r\n        d = 0;\r\n      }\r\n      g = f - c | 0;\r\n      e = d - c | 0;\r\n      if ((c | 0) < 0) {\r\n        b[a >> 2] = g;\r\n        b[h >> 2] = e;\r\n        b[i >> 2] = 0;\r\n        f = g;\r\n        c = 0;\r\n      } else { e = d; }\r\n      d = (e | 0) < (f | 0) ? e : f;\r\n      d = (c | 0) < (d | 0) ? c : d;\r\n      if ((d | 0) <= 0) { return; }\r\n      b[a >> 2] = f - d;\r\n      b[h >> 2] = e - d;\r\n      b[i >> 2] = c - d;\r\n      return;\r\n    }\r\n    function db(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = (b[a >> 2] | 0) - (b[c >> 2] | 0) | 0;\r\n      i = (h | 0) < 0;\r\n      e = (b[a + 4 >> 2] | 0) - (b[c + 4 >> 2] | 0) - (i ? h : 0) | 0;\r\n      g = (e | 0) < 0;\r\n      f = (i ? 0 - h | 0 : 0) + (b[a + 8 >> 2] | 0) - (b[c + 8 >> 2] | 0) + (g ? 0 - e | 0 : 0) | 0;\r\n      a = (f | 0) < 0;\r\n      c = a ? 0 : f;\r\n      d = (g ? 0 : e) - (a ? f : 0) | 0;\r\n      f = (i ? 0 : h) - (g ? e : 0) - (a ? f : 0) | 0;\r\n      a = (d | 0) < (f | 0) ? d : f;\r\n      a = (c | 0) < (a | 0) ? c : a;\r\n      e = (a | 0) > 0;\r\n      c = c - (e ? a : 0) | 0;\r\n      d = d - (e ? a : 0) | 0;\r\n      a = f - (e ? a : 0) | 0;\r\n      a = (a | 0) > -1 ? a : 0 - a | 0;\r\n      d = (d | 0) > -1 ? d : 0 - d | 0;\r\n      c = (c | 0) > -1 ? c : 0 - c | 0;\r\n      c = (d | 0) > (c | 0) ? d : c;\r\n      return ((a | 0) > (c | 0) ? a : c) | 0;\r\n    }\r\n    function eb(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0;\r\n      d = b[a + 8 >> 2] | 0;\r\n      b[c >> 2] = (b[a >> 2] | 0) - d;\r\n      b[c + 4 >> 2] = (b[a + 4 >> 2] | 0) - d;\r\n      return;\r\n    }\r\n    function fb(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      e = b[a >> 2] | 0;\r\n      b[c >> 2] = e;\r\n      f = b[a + 4 >> 2] | 0;\r\n      h = c + 4 | 0;\r\n      b[h >> 2] = f;\r\n      i = c + 8 | 0;\r\n      b[i >> 2] = 0;\r\n      d = (f | 0) < (e | 0);\r\n      a = d ? e : f;\r\n      d = d ? f : e;\r\n      if ((d | 0) < 0) {\r\n        if ((d | 0) == -2147483648 ? 1 : (a | 0) > 0 ? (2147483647 - a | 0) < (d | 0) : (-2147483648 - a | 0) > (d | 0)) {\r\n          c = 1;\r\n          return c | 0;\r\n        }\r\n        if ((a | 0) > -1 ? (a | -2147483648 | 0) >= (d | 0) : (a ^ -2147483648 | 0) < (d | 0)) {\r\n          c = 1;\r\n          return c | 0;\r\n        }\r\n      }\r\n      a = f - e | 0;\r\n      if ((e | 0) < 0) {\r\n        d = 0 - e | 0;\r\n        b[h >> 2] = a;\r\n        b[i >> 2] = d;\r\n        b[c >> 2] = 0;\r\n        e = 0;\r\n      } else {\r\n        a = f;\r\n        d = 0;\r\n      }\r\n      if ((a | 0) < 0) {\r\n        e = e - a | 0;\r\n        b[c >> 2] = e;\r\n        d = d - a | 0;\r\n        b[i >> 2] = d;\r\n        b[h >> 2] = 0;\r\n        a = 0;\r\n      }\r\n      g = e - d | 0;\r\n      f = a - d | 0;\r\n      if ((d | 0) < 0) {\r\n        b[c >> 2] = g;\r\n        b[h >> 2] = f;\r\n        b[i >> 2] = 0;\r\n        a = f;\r\n        f = g;\r\n        d = 0;\r\n      } else { f = e; }\r\n      e = (a | 0) < (f | 0) ? a : f;\r\n      e = (d | 0) < (e | 0) ? d : e;\r\n      if ((e | 0) <= 0) {\r\n        c = 0;\r\n        return c | 0;\r\n      }\r\n      b[c >> 2] = f - e;\r\n      b[h >> 2] = a - e;\r\n      b[i >> 2] = d - e;\r\n      c = 0;\r\n      return c | 0;\r\n    }\r\n    function gb(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0;\r\n      c = a + 8 | 0;\r\n      f = b[c >> 2] | 0;\r\n      d = f - (b[a >> 2] | 0) | 0;\r\n      b[a >> 2] = d;\r\n      e = a + 4 | 0;\r\n      a = (b[e >> 2] | 0) - f | 0;\r\n      b[e >> 2] = a;\r\n      b[c >> 2] = 0 - (a + d);\r\n      return;\r\n    }\r\n    function hb(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      d = b[a >> 2] | 0;\r\n      c = 0 - d | 0;\r\n      b[a >> 2] = c;\r\n      h = a + 8 | 0;\r\n      b[h >> 2] = 0;\r\n      i = a + 4 | 0;\r\n      e = b[i >> 2] | 0;\r\n      f = e + d | 0;\r\n      if ((d | 0) > 0) {\r\n        b[i >> 2] = f;\r\n        b[h >> 2] = d;\r\n        b[a >> 2] = 0;\r\n        c = 0;\r\n        e = f;\r\n      } else { d = 0; }\r\n      if ((e | 0) < 0) {\r\n        g = c - e | 0;\r\n        b[a >> 2] = g;\r\n        d = d - e | 0;\r\n        b[h >> 2] = d;\r\n        b[i >> 2] = 0;\r\n        f = g - d | 0;\r\n        c = 0 - d | 0;\r\n        if ((d | 0) < 0) {\r\n          b[a >> 2] = f;\r\n          b[i >> 2] = c;\r\n          b[h >> 2] = 0;\r\n          e = c;\r\n          d = 0;\r\n        } else {\r\n          e = 0;\r\n          f = g;\r\n        }\r\n      } else { f = c; }\r\n      c = (e | 0) < (f | 0) ? e : f;\r\n      c = (d | 0) < (c | 0) ? d : c;\r\n      if ((c | 0) <= 0) { return; }\r\n      b[a >> 2] = f - c;\r\n      b[i >> 2] = e - c;\r\n      b[h >> 2] = d - c;\r\n      return;\r\n    }\r\n    function ib(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0;\r\n      m = T;\r\n      T = T + 64 | 0;\r\n      l = m;\r\n      i = m + 56 | 0;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 134217728 & (0 == 0 & (e & 2013265920 | 0) == 134217728))) {\r\n        f = 5;\r\n        T = m;\r\n        return f | 0;\r\n      }\r\n      if ((a | 0) == (d | 0) & (c | 0) == (e | 0)) {\r\n        b[f >> 2] = 0;\r\n        f = 0;\r\n        T = m;\r\n        return f | 0;\r\n      }\r\n      h = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      h = h & 15;\r\n      k = Qd(d | 0, e | 0, 52) | 0;\r\n      H() | 0;\r\n      if ((h | 0) != (k & 15 | 0)) {\r\n        f = 12;\r\n        T = m;\r\n        return f | 0;\r\n      }\r\n      g = h + -1 | 0;\r\n      if (h >>> 0 > 1) {\r\n        Fb(a, c, g, l) | 0;\r\n        Fb(d, e, g, i) | 0;\r\n        k = l;\r\n        j = b[k >> 2] | 0;\r\n        k = b[k + 4 >> 2] | 0;\r\n        a: do { if ((j | 0) == (b[i >> 2] | 0) ? (k | 0) == (b[i + 4 >> 2] | 0) : 0) {\r\n          h = (h ^ 15) * 3 | 0;\r\n          g = Qd(a | 0, c | 0, h | 0) | 0;\r\n          H() | 0;\r\n          g = g & 7;\r\n          h = Qd(d | 0, e | 0, h | 0) | 0;\r\n          H() | 0;\r\n          h = h & 7;\r\n          do { if (!((g | 0) == 0 | (h | 0) == 0)) {\r\n            if ((g | 0) == 7) { g = 5; }else {\r\n              if ((g | 0) == 1 | (h | 0) == 1 ? Hb(j, k) | 0 : 0) {\r\n                g = 5;\r\n                break;\r\n              }\r\n              if ((b[15536 + (g << 2) >> 2] | 0) != (h | 0) ? (b[15568 + (g << 2) >> 2] | 0) != (h | 0) : 0) { break a; }\r\n              b[f >> 2] = 1;\r\n              g = 0;\r\n            }\r\n          } else {\r\n            b[f >> 2] = 1;\r\n            g = 0;\r\n          } } while (0);\r\n          f = g;\r\n          T = m;\r\n          return f | 0;\r\n        } } while (0);\r\n      }\r\n      g = l;\r\n      h = g + 56 | 0;\r\n      do {\r\n        b[g >> 2] = 0;\r\n        g = g + 4 | 0;\r\n      } while ((g | 0) < (h | 0));\r\n      aa(a, c, 1, l) | 0;\r\n      c = l;\r\n      if (((((!((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0) ? (c = l + 8 | 0, !((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0)) : 0) ? (c = l + 16 | 0, !((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0)) : 0) ? (c = l + 24 | 0, !((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0)) : 0) ? (c = l + 32 | 0, !((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0)) : 0) ? (c = l + 40 | 0, !((b[c >> 2] | 0) == (d | 0) ? (b[c + 4 >> 2] | 0) == (e | 0) : 0)) : 0) {\r\n        g = l + 48 | 0;\r\n        g = ((b[g >> 2] | 0) == (d | 0) ? (b[g + 4 >> 2] | 0) == (e | 0) : 0) & 1;\r\n      } else { g = 1; }\r\n      b[f >> 2] = g;\r\n      f = 0;\r\n      T = m;\r\n      return f | 0;\r\n    }\r\n    function jb(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      d = ia(a, c, d, e) | 0;\r\n      if ((d | 0) == 7) {\r\n        f = 11;\r\n        return f | 0;\r\n      }\r\n      e = Rd(d | 0, 0, 56) | 0;\r\n      c = c & -2130706433 | (H() | 0) | 268435456;\r\n      b[f >> 2] = a | e;\r\n      b[f + 4 >> 2] = c;\r\n      f = 0;\r\n      return f | 0;\r\n    }\r\n    function kb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 268435456)) {\r\n        d = 6;\r\n        return d | 0;\r\n      }\r\n      b[d >> 2] = a;\r\n      b[d + 4 >> 2] = c & -2130706433 | 134217728;\r\n      d = 0;\r\n      return d | 0;\r\n    }\r\n    function lb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      e = f;\r\n      b[e >> 2] = 0;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 268435456)) {\r\n        e = 6;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      g = Qd(a | 0, c | 0, 56) | 0;\r\n      H() | 0;\r\n      e = ea(a, c & -2130706433 | 134217728, g & 7, e, d) | 0;\r\n      T = f;\r\n      return e | 0;\r\n    }\r\n    function mb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0;\r\n      c = Qd(a | 0, b | 0, 56) | 0;\r\n      H() | 0;\r\n      switch (c & 7) {\r\n        case 0:\r\n        case 7:\r\n          {\r\n            c = 0;\r\n            return c | 0;\r\n          }\r\n        default:\r\n\r\n      }\r\n      c = b & -2130706433 | 134217728;\r\n      if (!(0 == 0 & (b & 2013265920 | 0) == 268435456)) {\r\n        c = 0;\r\n        return c | 0;\r\n      }\r\n      if (0 == 0 & (b & 117440512 | 0) == 16777216 & (Hb(a, c) | 0) != 0) {\r\n        c = 0;\r\n        return c | 0;\r\n      }\r\n      c = Db(a, c) | 0;\r\n      return c | 0;\r\n    }\r\n    function nb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      e = f;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 268435456)) {\r\n        e = 6;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      g = c & -2130706433 | 134217728;\r\n      h = d;\r\n      b[h >> 2] = a;\r\n      b[h + 4 >> 2] = g;\r\n      b[e >> 2] = 0;\r\n      c = Qd(a | 0, c | 0, 56) | 0;\r\n      H() | 0;\r\n      e = ea(a, g, c & 7, e, d + 8 | 0) | 0;\r\n      T = f;\r\n      return e | 0;\r\n    }\r\n    function ob(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0;\r\n      f = (Hb(a, c) | 0) == 0;\r\n      c = c & -2130706433;\r\n      e = d;\r\n      b[e >> 2] = f ? a : 0;\r\n      b[e + 4 >> 2] = f ? c | 285212672 : 0;\r\n      e = d + 8 | 0;\r\n      b[e >> 2] = a;\r\n      b[e + 4 >> 2] = c | 301989888;\r\n      e = d + 16 | 0;\r\n      b[e >> 2] = a;\r\n      b[e + 4 >> 2] = c | 318767104;\r\n      e = d + 24 | 0;\r\n      b[e >> 2] = a;\r\n      b[e + 4 >> 2] = c | 335544320;\r\n      e = d + 32 | 0;\r\n      b[e >> 2] = a;\r\n      b[e + 4 >> 2] = c | 352321536;\r\n      d = d + 40 | 0;\r\n      b[d >> 2] = a;\r\n      b[d + 4 >> 2] = c | 369098752;\r\n      return 0;\r\n    }\r\n    function pb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      h = T;\r\n      T = T + 16 | 0;\r\n      f = h;\r\n      g = c & -2130706433 | 134217728;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 268435456)) {\r\n        g = 6;\r\n        T = h;\r\n        return g | 0;\r\n      }\r\n      e = Qd(a | 0, c | 0, 56) | 0;\r\n      H() | 0;\r\n      e = od(a, g, e & 7) | 0;\r\n      if ((e | 0) == -1) {\r\n        b[d >> 2] = 0;\r\n        g = 6;\r\n        T = h;\r\n        return g | 0;\r\n      }\r\n      if (Yb(a, g, f) | 0) { I(27795, 26932, 282, 26947); }\r\n      c = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      c = c & 15;\r\n      if (!(Hb(a, g) | 0)) { zb(f, c, e, 2, d); }else { vb(f, c, e, 2, d); }\r\n      g = 0;\r\n      T = h;\r\n      return g | 0;\r\n    }\r\n    function qb(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0;\r\n      d = T;\r\n      T = T + 16 | 0;\r\n      e = d;\r\n      rb(a, b, c, e);\r\n      La(e, c + 4 | 0);\r\n      T = d;\r\n      return;\r\n    }\r\n    function rb(a, c, d, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      var g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0,\r\n        k = 0;\r\n      j = T;\r\n      T = T + 16 | 0;\r\n      k = j;\r\n      sb(a, d, k);\r\n      h = +w(+(1.0 - +e[k >> 3] * .5));\r\n      if (h < 1.0e-16) {\r\n        b[f >> 2] = 0;\r\n        b[f + 4 >> 2] = 0;\r\n        b[f + 8 >> 2] = 0;\r\n        b[f + 12 >> 2] = 0;\r\n        T = j;\r\n        return;\r\n      }\r\n      k = b[d >> 2] | 0;\r\n      g = +e[15920 + (k * 24 | 0) >> 3];\r\n      g = +ic(g - +ic(+oc(15600 + (k << 4) | 0, a)));\r\n      if (!(Vb(c) | 0)) { i = g; }else { i = +ic(g + -.3334731722518321); }\r\n      g = +v(+h) * 2.618033988749896;\r\n      if ((c | 0) > 0) {\r\n        a = 0;\r\n        do {\r\n          g = g * 2.6457513110645907;\r\n          a = a + 1 | 0;\r\n        } while ((a | 0) != (c | 0));\r\n      }\r\n      h = +t(+i) * g;\r\n      e[f >> 3] = h;\r\n      i = +u(+i) * g;\r\n      e[f + 8 >> 3] = i;\r\n      T = j;\r\n      return;\r\n    }\r\n    function sb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0,\r\n        h = 0;\r\n      h = T;\r\n      T = T + 32 | 0;\r\n      g = h;\r\n      nd(a, g);\r\n      b[c >> 2] = 0;\r\n      e[d >> 3] = 5.0;\r\n      f = +md(16400, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 0;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16424, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 1;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16448, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 2;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16472, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 3;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16496, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 4;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16520, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 5;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16544, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 6;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16568, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 7;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16592, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 8;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16616, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 9;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16640, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 10;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16664, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 11;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16688, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 12;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16712, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 13;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16736, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 14;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16760, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 15;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16784, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 16;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16808, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 17;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16832, g);\r\n      if (f < +e[d >> 3]) {\r\n        b[c >> 2] = 18;\r\n        e[d >> 3] = f;\r\n      }\r\n      f = +md(16856, g);\r\n      if (!(f < +e[d >> 3])) {\r\n        T = h;\r\n        return;\r\n      }\r\n      b[c >> 2] = 19;\r\n      e[d >> 3] = f;\r\n      T = h;\r\n      return;\r\n    }\r\n    function tb(a, c, d, f, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0;\r\n      h = +jd(a);\r\n      if (h < 1.0e-16) {\r\n        c = 15600 + (c << 4) | 0;\r\n        b[g >> 2] = b[c >> 2];\r\n        b[g + 4 >> 2] = b[c + 4 >> 2];\r\n        b[g + 8 >> 2] = b[c + 8 >> 2];\r\n        b[g + 12 >> 2] = b[c + 12 >> 2];\r\n        return;\r\n      }\r\n      i = +z(+ +e[a + 8 >> 3], + +e[a >> 3]);\r\n      if ((d | 0) > 0) {\r\n        a = 0;\r\n        do {\r\n          h = h * .37796447300922725;\r\n          a = a + 1 | 0;\r\n        } while ((a | 0) != (d | 0));\r\n      }\r\n      j = h * .3333333333333333;\r\n      if (!f) {\r\n        h = +y(+(h * .381966011250105));\r\n        if (Vb(d) | 0) { i = +ic(i + .3334731722518321); }\r\n      } else {\r\n        d = (Vb(d) | 0) == 0;\r\n        h = +y(+((d ? j : j * .37796447300922725) * .381966011250105));\r\n      }\r\n      pc(15600 + (c << 4) | 0, +ic(+e[15920 + (c * 24 | 0) >> 3] - i), h, g);\r\n      return;\r\n    }\r\n    function ub(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0;\r\n      e = T;\r\n      T = T + 16 | 0;\r\n      f = e;\r\n      Na(a + 4 | 0, f);\r\n      tb(f, b[a >> 2] | 0, c, 0, d);\r\n      T = e;\r\n      return;\r\n    }\r\n    function vb(a, c, d, f, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0,\r\n        D = 0,\r\n        E = 0,\r\n        F = 0,\r\n        G = 0,\r\n        H = 0.0,\r\n        J = 0.0;\r\n      G = T;\r\n      T = T + 272 | 0;\r\n      h = G + 256 | 0;\r\n      u = G + 240 | 0;\r\n      D = G;\r\n      E = G + 224 | 0;\r\n      F = G + 208 | 0;\r\n      v = G + 176 | 0;\r\n      w = G + 160 | 0;\r\n      x = G + 192 | 0;\r\n      y = G + 144 | 0;\r\n      z = G + 128 | 0;\r\n      A = G + 112 | 0;\r\n      B = G + 96 | 0;\r\n      C = G + 80 | 0;\r\n      b[h >> 2] = c;\r\n      b[u >> 2] = b[a >> 2];\r\n      b[u + 4 >> 2] = b[a + 4 >> 2];\r\n      b[u + 8 >> 2] = b[a + 8 >> 2];\r\n      b[u + 12 >> 2] = b[a + 12 >> 2];\r\n      wb(u, h, D);\r\n      b[g >> 2] = 0;\r\n      u = f + d + ((f | 0) == 5 & 1) | 0;\r\n      if ((u | 0) <= (d | 0)) {\r\n        T = G;\r\n        return;\r\n      }\r\n      k = b[h >> 2] | 0;\r\n      l = E + 4 | 0;\r\n      m = v + 4 | 0;\r\n      n = d + 5 | 0;\r\n      o = 16880 + (k << 2) | 0;\r\n      p = 16960 + (k << 2) | 0;\r\n      q = z + 8 | 0;\r\n      r = A + 8 | 0;\r\n      s = B + 8 | 0;\r\n      t = F + 4 | 0;\r\n      j = d;\r\n      a: while (1) {\r\n        i = D + (((j | 0) % 5 | 0) << 4) | 0;\r\n        b[F >> 2] = b[i >> 2];\r\n        b[F + 4 >> 2] = b[i + 4 >> 2];\r\n        b[F + 8 >> 2] = b[i + 8 >> 2];\r\n        b[F + 12 >> 2] = b[i + 12 >> 2];\r\n        do {} while ((xb(F, k, 0, 1) | 0) == 2);\r\n        if ((j | 0) > (d | 0) & (Vb(c) | 0) != 0) {\r\n          b[v >> 2] = b[F >> 2];\r\n          b[v + 4 >> 2] = b[F + 4 >> 2];\r\n          b[v + 8 >> 2] = b[F + 8 >> 2];\r\n          b[v + 12 >> 2] = b[F + 12 >> 2];\r\n          Na(l, w);\r\n          f = b[v >> 2] | 0;\r\n          h = b[17040 + (f * 80 | 0) + (b[E >> 2] << 2) >> 2] | 0;\r\n          b[v >> 2] = b[18640 + (f * 80 | 0) + (h * 20 | 0) >> 2];\r\n          i = b[18640 + (f * 80 | 0) + (h * 20 | 0) + 16 >> 2] | 0;\r\n          if ((i | 0) > 0) {\r\n            a = 0;\r\n            do {\r\n              Za(m);\r\n              a = a + 1 | 0;\r\n            } while ((a | 0) < (i | 0));\r\n          }\r\n          i = 18640 + (f * 80 | 0) + (h * 20 | 0) + 4 | 0;\r\n          b[x >> 2] = b[i >> 2];\r\n          b[x + 4 >> 2] = b[i + 4 >> 2];\r\n          b[x + 8 >> 2] = b[i + 8 >> 2];\r\n          Qa(x, (b[o >> 2] | 0) * 3 | 0);\r\n          Oa(m, x, m);\r\n          Ma(m);\r\n          Na(m, y);\r\n          H = +(b[p >> 2] | 0);\r\n          e[z >> 3] = H * 3.0;\r\n          e[q >> 3] = 0.0;\r\n          J = H * -1.5;\r\n          e[A >> 3] = J;\r\n          e[r >> 3] = H * 2.598076211353316;\r\n          e[B >> 3] = J;\r\n          e[s >> 3] = H * -2.598076211353316;\r\n          switch (b[17040 + ((b[v >> 2] | 0) * 80 | 0) + (b[F >> 2] << 2) >> 2] | 0) {\r\n            case 1:\r\n              {\r\n                a = A;\r\n                f = z;\r\n                break;\r\n              }\r\n            case 3:\r\n              {\r\n                a = B;\r\n                f = A;\r\n                break;\r\n              }\r\n            case 2:\r\n              {\r\n                a = z;\r\n                f = B;\r\n                break;\r\n              }\r\n            default:\r\n              {\r\n                a = 12;\r\n                break a;\r\n              }\r\n          }\r\n          kd(w, y, f, a, C);\r\n          tb(C, b[v >> 2] | 0, k, 1, g + 8 + (b[g >> 2] << 4) | 0);\r\n          b[g >> 2] = (b[g >> 2] | 0) + 1;\r\n        }\r\n        if ((j | 0) < (n | 0)) {\r\n          Na(t, v);\r\n          tb(v, b[F >> 2] | 0, k, 1, g + 8 + (b[g >> 2] << 4) | 0);\r\n          b[g >> 2] = (b[g >> 2] | 0) + 1;\r\n        }\r\n        b[E >> 2] = b[F >> 2];\r\n        b[E + 4 >> 2] = b[F + 4 >> 2];\r\n        b[E + 8 >> 2] = b[F + 8 >> 2];\r\n        b[E + 12 >> 2] = b[F + 12 >> 2];\r\n        j = j + 1 | 0;\r\n        if ((j | 0) >= (u | 0)) {\r\n          a = 3;\r\n          break;\r\n        }\r\n      }\r\n      if ((a | 0) == 3) {\r\n        T = G;\r\n        return;\r\n      } else if ((a | 0) == 12) { I(26970, 27017, 572, 27027); }\r\n    }\r\n    function wb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 128 | 0;\r\n      e = j + 64 | 0;\r\n      f = j;\r\n      g = e;\r\n      h = 20240;\r\n      i = g + 60 | 0;\r\n      do {\r\n        b[g >> 2] = b[h >> 2];\r\n        g = g + 4 | 0;\r\n        h = h + 4 | 0;\r\n      } while ((g | 0) < (i | 0));\r\n      g = f;\r\n      h = 20304;\r\n      i = g + 60 | 0;\r\n      do {\r\n        b[g >> 2] = b[h >> 2];\r\n        g = g + 4 | 0;\r\n        h = h + 4 | 0;\r\n      } while ((g | 0) < (i | 0));\r\n      i = (Vb(b[c >> 2] | 0) | 0) == 0;\r\n      e = i ? e : f;\r\n      f = a + 4 | 0;\r\n      bb(f);\r\n      cb(f);\r\n      if (Vb(b[c >> 2] | 0) | 0) {\r\n        Xa(f);\r\n        b[c >> 2] = (b[c >> 2] | 0) + 1;\r\n      }\r\n      b[d >> 2] = b[a >> 2];\r\n      c = d + 4 | 0;\r\n      Oa(f, e, c);\r\n      Ma(c);\r\n      b[d + 16 >> 2] = b[a >> 2];\r\n      c = d + 20 | 0;\r\n      Oa(f, e + 12 | 0, c);\r\n      Ma(c);\r\n      b[d + 32 >> 2] = b[a >> 2];\r\n      c = d + 36 | 0;\r\n      Oa(f, e + 24 | 0, c);\r\n      Ma(c);\r\n      b[d + 48 >> 2] = b[a >> 2];\r\n      c = d + 52 | 0;\r\n      Oa(f, e + 36 | 0, c);\r\n      Ma(c);\r\n      b[d + 64 >> 2] = b[a >> 2];\r\n      d = d + 68 | 0;\r\n      Oa(f, e + 48 | 0, d);\r\n      Ma(d);\r\n      T = j;\r\n      return;\r\n    }\r\n    function xb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0;\r\n      p = T;\r\n      T = T + 32 | 0;\r\n      n = p + 12 | 0;\r\n      i = p;\r\n      o = a + 4 | 0;\r\n      m = b[16960 + (c << 2) >> 2] | 0;\r\n      l = (e | 0) != 0;\r\n      m = l ? m * 3 | 0 : m;\r\n      f = b[o >> 2] | 0;\r\n      k = a + 8 | 0;\r\n      h = b[k >> 2] | 0;\r\n      if (l) {\r\n        g = a + 12 | 0;\r\n        e = b[g >> 2] | 0;\r\n        f = h + f + e | 0;\r\n        if ((f | 0) == (m | 0)) {\r\n          o = 1;\r\n          T = p;\r\n          return o | 0;\r\n        } else { j = g; }\r\n      } else {\r\n        j = a + 12 | 0;\r\n        e = b[j >> 2] | 0;\r\n        f = h + f + e | 0;\r\n      }\r\n      if ((f | 0) <= (m | 0)) {\r\n        o = 0;\r\n        T = p;\r\n        return o | 0;\r\n      }\r\n      do { if ((e | 0) > 0) {\r\n        e = b[a >> 2] | 0;\r\n        if ((h | 0) > 0) {\r\n          g = 18640 + (e * 80 | 0) + 60 | 0;\r\n          e = a;\r\n          break;\r\n        }\r\n        e = 18640 + (e * 80 | 0) + 40 | 0;\r\n        if (!d) {\r\n          g = e;\r\n          e = a;\r\n        } else {\r\n          Ka(n, m, 0, 0);\r\n          Pa(o, n, i);\r\n          _a(i);\r\n          Oa(i, n, o);\r\n          g = e;\r\n          e = a;\r\n        }\r\n      } else {\r\n        g = 18640 + ((b[a >> 2] | 0) * 80 | 0) + 20 | 0;\r\n        e = a;\r\n      } } while (0);\r\n      b[e >> 2] = b[g >> 2];\r\n      f = g + 16 | 0;\r\n      if ((b[f >> 2] | 0) > 0) {\r\n        e = 0;\r\n        do {\r\n          Za(o);\r\n          e = e + 1 | 0;\r\n        } while ((e | 0) < (b[f >> 2] | 0));\r\n      }\r\n      a = g + 4 | 0;\r\n      b[n >> 2] = b[a >> 2];\r\n      b[n + 4 >> 2] = b[a + 4 >> 2];\r\n      b[n + 8 >> 2] = b[a + 8 >> 2];\r\n      c = b[16880 + (c << 2) >> 2] | 0;\r\n      Qa(n, l ? c * 3 | 0 : c);\r\n      Oa(o, n, o);\r\n      Ma(o);\r\n      if (l) { e = ((b[k >> 2] | 0) + (b[o >> 2] | 0) + (b[j >> 2] | 0) | 0) == (m | 0) ? 1 : 2; }else { e = 2; }\r\n      o = e;\r\n      T = p;\r\n      return o | 0;\r\n    }\r\n    function yb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0;\r\n      do { c = xb(a, b, 0, 1) | 0; } while ((c | 0) == 2);\r\n      return c | 0;\r\n    }\r\n    function zb(a, c, d, f, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0.0,\r\n        D = 0.0;\r\n      B = T;\r\n      T = T + 240 | 0;\r\n      h = B + 224 | 0;\r\n      x = B + 208 | 0;\r\n      y = B;\r\n      z = B + 192 | 0;\r\n      A = B + 176 | 0;\r\n      s = B + 160 | 0;\r\n      t = B + 144 | 0;\r\n      u = B + 128 | 0;\r\n      v = B + 112 | 0;\r\n      w = B + 96 | 0;\r\n      b[h >> 2] = c;\r\n      b[x >> 2] = b[a >> 2];\r\n      b[x + 4 >> 2] = b[a + 4 >> 2];\r\n      b[x + 8 >> 2] = b[a + 8 >> 2];\r\n      b[x + 12 >> 2] = b[a + 12 >> 2];\r\n      Ab(x, h, y);\r\n      b[g >> 2] = 0;\r\n      r = f + d + ((f | 0) == 6 & 1) | 0;\r\n      if ((r | 0) <= (d | 0)) {\r\n        T = B;\r\n        return;\r\n      }\r\n      k = b[h >> 2] | 0;\r\n      l = d + 6 | 0;\r\n      m = 16960 + (k << 2) | 0;\r\n      n = t + 8 | 0;\r\n      o = u + 8 | 0;\r\n      p = v + 8 | 0;\r\n      q = z + 4 | 0;\r\n      i = 0;\r\n      j = d;\r\n      f = -1;\r\n      a: while (1) {\r\n        h = (j | 0) % 6 | 0;\r\n        a = y + (h << 4) | 0;\r\n        b[z >> 2] = b[a >> 2];\r\n        b[z + 4 >> 2] = b[a + 4 >> 2];\r\n        b[z + 8 >> 2] = b[a + 8 >> 2];\r\n        b[z + 12 >> 2] = b[a + 12 >> 2];\r\n        a = i;\r\n        i = xb(z, k, 0, 1) | 0;\r\n        if ((j | 0) > (d | 0) & (Vb(c) | 0) != 0 ? (a | 0) != 1 ? (b[z >> 2] | 0) != (f | 0) : 0 : 0) {\r\n          Na(y + (((h + 5 | 0) % 6 | 0) << 4) + 4 | 0, A);\r\n          Na(y + (h << 4) + 4 | 0, s);\r\n          C = +(b[m >> 2] | 0);\r\n          e[t >> 3] = C * 3.0;\r\n          e[n >> 3] = 0.0;\r\n          D = C * -1.5;\r\n          e[u >> 3] = D;\r\n          e[o >> 3] = C * 2.598076211353316;\r\n          e[v >> 3] = D;\r\n          e[p >> 3] = C * -2.598076211353316;\r\n          h = b[x >> 2] | 0;\r\n          switch (b[17040 + (h * 80 | 0) + (((f | 0) == (h | 0) ? b[z >> 2] | 0 : f) << 2) >> 2] | 0) {\r\n            case 1:\r\n              {\r\n                a = u;\r\n                f = t;\r\n                break;\r\n              }\r\n            case 3:\r\n              {\r\n                a = v;\r\n                f = u;\r\n                break;\r\n              }\r\n            case 2:\r\n              {\r\n                a = t;\r\n                f = v;\r\n                break;\r\n              }\r\n            default:\r\n              {\r\n                a = 8;\r\n                break a;\r\n              }\r\n          }\r\n          kd(A, s, f, a, w);\r\n          if (!(ld(A, w) | 0) ? !(ld(s, w) | 0) : 0) {\r\n            tb(w, b[x >> 2] | 0, k, 1, g + 8 + (b[g >> 2] << 4) | 0);\r\n            b[g >> 2] = (b[g >> 2] | 0) + 1;\r\n          }\r\n        }\r\n        if ((j | 0) < (l | 0)) {\r\n          Na(q, A);\r\n          tb(A, b[z >> 2] | 0, k, 1, g + 8 + (b[g >> 2] << 4) | 0);\r\n          b[g >> 2] = (b[g >> 2] | 0) + 1;\r\n        }\r\n        j = j + 1 | 0;\r\n        if ((j | 0) >= (r | 0)) {\r\n          a = 3;\r\n          break;\r\n        } else { f = b[z >> 2] | 0; }\r\n      }\r\n      if ((a | 0) == 3) {\r\n        T = B;\r\n        return;\r\n      } else if ((a | 0) == 8) { I(27054, 27017, 737, 27099); }\r\n    }\r\n    function Ab(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 160 | 0;\r\n      e = j + 80 | 0;\r\n      f = j;\r\n      g = e;\r\n      h = 20368;\r\n      i = g + 72 | 0;\r\n      do {\r\n        b[g >> 2] = b[h >> 2];\r\n        g = g + 4 | 0;\r\n        h = h + 4 | 0;\r\n      } while ((g | 0) < (i | 0));\r\n      g = f;\r\n      h = 20448;\r\n      i = g + 72 | 0;\r\n      do {\r\n        b[g >> 2] = b[h >> 2];\r\n        g = g + 4 | 0;\r\n        h = h + 4 | 0;\r\n      } while ((g | 0) < (i | 0));\r\n      i = (Vb(b[c >> 2] | 0) | 0) == 0;\r\n      e = i ? e : f;\r\n      f = a + 4 | 0;\r\n      bb(f);\r\n      cb(f);\r\n      if (Vb(b[c >> 2] | 0) | 0) {\r\n        Xa(f);\r\n        b[c >> 2] = (b[c >> 2] | 0) + 1;\r\n      }\r\n      b[d >> 2] = b[a >> 2];\r\n      c = d + 4 | 0;\r\n      Oa(f, e, c);\r\n      Ma(c);\r\n      b[d + 16 >> 2] = b[a >> 2];\r\n      c = d + 20 | 0;\r\n      Oa(f, e + 12 | 0, c);\r\n      Ma(c);\r\n      b[d + 32 >> 2] = b[a >> 2];\r\n      c = d + 36 | 0;\r\n      Oa(f, e + 24 | 0, c);\r\n      Ma(c);\r\n      b[d + 48 >> 2] = b[a >> 2];\r\n      c = d + 52 | 0;\r\n      Oa(f, e + 36 | 0, c);\r\n      Ma(c);\r\n      b[d + 64 >> 2] = b[a >> 2];\r\n      c = d + 68 | 0;\r\n      Oa(f, e + 48 | 0, c);\r\n      Ma(c);\r\n      b[d + 80 >> 2] = b[a >> 2];\r\n      d = d + 84 | 0;\r\n      Oa(f, e + 60 | 0, d);\r\n      Ma(d);\r\n      T = j;\r\n      return;\r\n    }\r\n    function Bb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      b = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      return b & 15 | 0;\r\n    }\r\n    function Cb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      b = Qd(a | 0, b | 0, 45) | 0;\r\n      H() | 0;\r\n      return b & 127 | 0;\r\n    }\r\n    function Db(b, c) {\r\n      b = b | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      if (!(0 == 0 & (c & -16777216 | 0) == 134217728)) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      e = Qd(b | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      d = Qd(b | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      d = d & 127;\r\n      if (d >>> 0 > 121) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      h = (e ^ 15) * 3 | 0;\r\n      f = Qd(b | 0, c | 0, h | 0) | 0;\r\n      h = Rd(f | 0, H() | 0, h | 0) | 0;\r\n      f = H() | 0;\r\n      g = Hd(-1227133514, -1171, h | 0, f | 0) | 0;\r\n      if (!((h & 613566756 & g | 0) == 0 & (f & 4681 & (H() | 0) | 0) == 0)) {\r\n        h = 0;\r\n        return h | 0;\r\n      }\r\n      h = (e * 3 | 0) + 19 | 0;\r\n      g = Rd(~b | 0, ~c | 0, h | 0) | 0;\r\n      h = Qd(g | 0, H() | 0, h | 0) | 0;\r\n      if (!((e | 0) == 15 | (h | 0) == 0 & (H() | 0) == 0)) {\r\n        h = 0;\r\n        return h | 0;\r\n      }\r\n      if (!(a[20528 + d >> 0] | 0)) {\r\n        h = 1;\r\n        return h | 0;\r\n      }\r\n      c = c & 8191;\r\n      if ((b | 0) == 0 & (c | 0) == 0) {\r\n        h = 1;\r\n        return h | 0;\r\n      } else {\r\n        h = Sd(b | 0, c | 0, 0) | 0;\r\n        H() | 0;\r\n        return ((63 - h | 0) % 3 | 0 | 0) != 0 | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function Eb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      f = Rd(c | 0, 0, 52) | 0;\r\n      g = H() | 0;\r\n      d = Rd(d | 0, 0, 45) | 0;\r\n      d = g | (H() | 0) | 134225919;\r\n      if ((c | 0) < 1) {\r\n        g = -1;\r\n        e = d;\r\n        c = a;\r\n        b[c >> 2] = g;\r\n        a = a + 4 | 0;\r\n        b[a >> 2] = e;\r\n        return;\r\n      }\r\n      g = 1;\r\n      f = -1;\r\n      while (1) {\r\n        h = (15 - g | 0) * 3 | 0;\r\n        i = Rd(7, 0, h | 0) | 0;\r\n        d = d & ~(H() | 0);\r\n        h = Rd(e | 0, 0, h | 0) | 0;\r\n        f = f & ~i | h;\r\n        d = d | (H() | 0);\r\n        if ((g | 0) == (c | 0)) { break; }else { g = g + 1 | 0; }\r\n      }\r\n      i = a;\r\n      h = i;\r\n      b[h >> 2] = f;\r\n      i = i + 4 | 0;\r\n      b[i >> 2] = d;\r\n      return;\r\n    }\r\n    function Fb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0;\r\n      g = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      g = g & 15;\r\n      if (d >>> 0 > 15) {\r\n        e = 4;\r\n        return e | 0;\r\n      }\r\n      if ((g | 0) < (d | 0)) {\r\n        e = 12;\r\n        return e | 0;\r\n      }\r\n      if ((g | 0) == (d | 0)) {\r\n        b[e >> 2] = a;\r\n        b[e + 4 >> 2] = c;\r\n        e = 0;\r\n        return e | 0;\r\n      }\r\n      f = Rd(d | 0, 0, 52) | 0;\r\n      f = f | a;\r\n      a = H() | 0 | c & -15728641;\r\n      if ((g | 0) > (d | 0)) { do {\r\n        c = Rd(7, 0, (14 - d | 0) * 3 | 0) | 0;\r\n        d = d + 1 | 0;\r\n        f = c | f;\r\n        a = H() | 0 | a;\r\n      } while ((d | 0) < (g | 0)); }\r\n      b[e >> 2] = f;\r\n      b[e + 4 >> 2] = a;\r\n      e = 0;\r\n      return e | 0;\r\n    }\r\n    function Gb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      g = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      g = g & 15;\r\n      if (!((d | 0) < 16 & (g | 0) <= (d | 0))) {\r\n        e = 4;\r\n        return e | 0;\r\n      }\r\n      f = d - g | 0;\r\n      d = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      a: do { if (!(oa(d & 127) | 0)) {\r\n        d = Oc(7, 0, f, ((f | 0) < 0) << 31 >> 31) | 0;\r\n        f = H() | 0;\r\n      } else {\r\n        b: do { if (g | 0) {\r\n          d = 1;\r\n          while (1) {\r\n            h = Rd(7, 0, (15 - d | 0) * 3 | 0) | 0;\r\n            if (!((h & a | 0) == 0 & ((H() | 0) & c | 0) == 0)) { break; }\r\n            if (d >>> 0 < g >>> 0) { d = d + 1 | 0; }else { break b; }\r\n          }\r\n          d = Oc(7, 0, f, ((f | 0) < 0) << 31 >> 31) | 0;\r\n          f = H() | 0;\r\n          break a;\r\n        } } while (0);\r\n        d = Oc(7, 0, f, ((f | 0) < 0) << 31 >> 31) | 0;\r\n        d = Md(d | 0, H() | 0, 5, 0) | 0;\r\n        d = Gd(d | 0, H() | 0, -5, -1) | 0;\r\n        d = Kd(d | 0, H() | 0, 6, 0) | 0;\r\n        d = Gd(d | 0, H() | 0, 1, 0) | 0;\r\n        f = H() | 0;\r\n      } } while (0);\r\n      h = e;\r\n      b[h >> 2] = d;\r\n      b[h + 4 >> 2] = f;\r\n      h = 0;\r\n      return h | 0;\r\n    }\r\n    function Hb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      e = Qd(a | 0, b | 0, 45) | 0;\r\n      H() | 0;\r\n      if (!(oa(e & 127) | 0)) {\r\n        e = 0;\r\n        return e | 0;\r\n      }\r\n      e = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      a: do { if (!e) { c = 0; }else {\r\n        d = 1;\r\n        while (1) {\r\n          c = Qd(a | 0, b | 0, (15 - d | 0) * 3 | 0) | 0;\r\n          H() | 0;\r\n          c = c & 7;\r\n          if (c | 0) { break a; }\r\n          if (d >>> 0 < e >>> 0) { d = d + 1 | 0; }else {\r\n            c = 0;\r\n            break;\r\n          }\r\n        }\r\n      } } while (0);\r\n      e = (c | 0) == 0 & 1;\r\n      return e | 0;\r\n    }\r\n    function Ib(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = T;\r\n      T = T + 16 | 0;\r\n      g = h;\r\n      fc(g, a, c, d);\r\n      c = g;\r\n      a = b[c >> 2] | 0;\r\n      c = b[c + 4 >> 2] | 0;\r\n      if ((a | 0) == 0 & (c | 0) == 0) {\r\n        T = h;\r\n        return 0;\r\n      }\r\n      f = 0;\r\n      d = 0;\r\n      do {\r\n        i = e + (f << 3) | 0;\r\n        b[i >> 2] = a;\r\n        b[i + 4 >> 2] = c;\r\n        f = Gd(f | 0, d | 0, 1, 0) | 0;\r\n        d = H() | 0;\r\n        hc(g);\r\n        i = g;\r\n        a = b[i >> 2] | 0;\r\n        c = b[i + 4 >> 2] | 0;\r\n      } while (!((a | 0) == 0 & (c | 0) == 0));\r\n      T = h;\r\n      return 0;\r\n    }\r\n    function Jb(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      if ((d | 0) < (c | 0)) {\r\n        c = b;\r\n        d = a;\r\n        G(c | 0);\r\n        return d | 0;\r\n      }\r\n      c = Rd(-1, -1, ((d - c | 0) * 3 | 0) + 3 | 0) | 0;\r\n      d = Rd(~c | 0, ~(H() | 0) | 0, (15 - d | 0) * 3 | 0) | 0;\r\n      c = ~(H() | 0) & b;\r\n      d = ~d & a;\r\n      G(c | 0);\r\n      return d | 0;\r\n    }\r\n    function Kb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0;\r\n      f = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      f = f & 15;\r\n      if (!((d | 0) < 16 & (f | 0) <= (d | 0))) {\r\n        e = 4;\r\n        return e | 0;\r\n      }\r\n      if ((f | 0) < (d | 0)) {\r\n        f = Rd(-1, -1, ((d + -1 - f | 0) * 3 | 0) + 3 | 0) | 0;\r\n        f = Rd(~f | 0, ~(H() | 0) | 0, (15 - d | 0) * 3 | 0) | 0;\r\n        c = ~(H() | 0) & c;\r\n        a = ~f & a;\r\n      }\r\n      f = Rd(d | 0, 0, 52) | 0;\r\n      d = c & -15728641 | (H() | 0);\r\n      b[e >> 2] = a | f;\r\n      b[e + 4 >> 2] = d;\r\n      e = 0;\r\n      return e | 0;\r\n    }\r\n    function Lb(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0,\r\n        D = 0,\r\n        E = 0;\r\n      if ((d | 0) == 0 & (e | 0) == 0) {\r\n        E = 0;\r\n        return E | 0;\r\n      }\r\n      f = a;\r\n      g = b[f >> 2] | 0;\r\n      f = b[f + 4 >> 2] | 0;\r\n      if (0 == 0 & (f & 15728640 | 0) == 0) {\r\n        if (!((e | 0) > 0 | (e | 0) == 0 & d >>> 0 > 0)) {\r\n          E = 0;\r\n          return E | 0;\r\n        }\r\n        E = c;\r\n        b[E >> 2] = g;\r\n        b[E + 4 >> 2] = f;\r\n        if ((d | 0) == 1 & (e | 0) == 0) {\r\n          E = 0;\r\n          return E | 0;\r\n        }\r\n        f = 1;\r\n        g = 0;\r\n        do {\r\n          C = a + (f << 3) | 0;\r\n          D = b[C + 4 >> 2] | 0;\r\n          E = c + (f << 3) | 0;\r\n          b[E >> 2] = b[C >> 2];\r\n          b[E + 4 >> 2] = D;\r\n          f = Gd(f | 0, g | 0, 1, 0) | 0;\r\n          g = H() | 0;\r\n        } while ((g | 0) < (e | 0) | (g | 0) == (e | 0) & f >>> 0 < d >>> 0);\r\n        f = 0;\r\n        return f | 0;\r\n      }\r\n      B = d << 3;\r\n      D = Dd(B) | 0;\r\n      if (!D) {\r\n        E = 13;\r\n        return E | 0;\r\n      }\r\n      Wd(D | 0, a | 0, B | 0) | 0;\r\n      C = Fd(d, 8) | 0;\r\n      if (!C) {\r\n        Ed(D);\r\n        E = 13;\r\n        return E | 0;\r\n      }\r\n      a: while (1) {\r\n        f = D;\r\n        k = b[f >> 2] | 0;\r\n        f = b[f + 4 >> 2] | 0;\r\n        z = Qd(k | 0, f | 0, 52) | 0;\r\n        H() | 0;\r\n        z = z & 15;\r\n        A = z + -1 | 0;\r\n        y = (z | 0) != 0;\r\n        x = (e | 0) > 0 | (e | 0) == 0 & d >>> 0 > 0;\r\n        b: do { if (y & x) {\r\n          t = Rd(A | 0, 0, 52) | 0;\r\n          u = H() | 0;\r\n          if (A >>> 0 > 15) {\r\n            if (!((k | 0) == 0 & (f | 0) == 0)) {\r\n              E = 16;\r\n              break a;\r\n            }\r\n            g = 0;\r\n            a = 0;\r\n            while (1) {\r\n              g = Gd(g | 0, a | 0, 1, 0) | 0;\r\n              a = H() | 0;\r\n              if (!((a | 0) < (e | 0) | (a | 0) == (e | 0) & g >>> 0 < d >>> 0)) { break b; }\r\n              h = D + (g << 3) | 0;\r\n              w = b[h >> 2] | 0;\r\n              h = b[h + 4 >> 2] | 0;\r\n              if (!((w | 0) == 0 & (h | 0) == 0)) {\r\n                f = h;\r\n                E = 16;\r\n                break a;\r\n              }\r\n            }\r\n          }\r\n          i = k;\r\n          a = f;\r\n          g = 0;\r\n          h = 0;\r\n          while (1) {\r\n            if (!((i | 0) == 0 & (a | 0) == 0)) {\r\n              if (!(0 == 0 & (a & 117440512 | 0) == 0)) {\r\n                E = 21;\r\n                break a;\r\n              }\r\n              l = Qd(i | 0, a | 0, 52) | 0;\r\n              H() | 0;\r\n              l = l & 15;\r\n              if ((l | 0) < (A | 0)) {\r\n                f = 12;\r\n                E = 27;\r\n                break a;\r\n              }\r\n              if ((l | 0) != (A | 0)) {\r\n                i = i | t;\r\n                a = a & -15728641 | u;\r\n                if (l >>> 0 >= z >>> 0) {\r\n                  j = A;\r\n                  do {\r\n                    w = Rd(7, 0, (14 - j | 0) * 3 | 0) | 0;\r\n                    j = j + 1 | 0;\r\n                    i = w | i;\r\n                    a = H() | 0 | a;\r\n                  } while (j >>> 0 < l >>> 0);\r\n                }\r\n              }\r\n              n = Od(i | 0, a | 0, d | 0, e | 0) | 0;\r\n              o = H() | 0;\r\n              j = C + (n << 3) | 0;\r\n              l = j;\r\n              m = b[l >> 2] | 0;\r\n              l = b[l + 4 >> 2] | 0;\r\n              if (!((m | 0) == 0 & (l | 0) == 0)) {\r\n                r = 0;\r\n                s = 0;\r\n                do {\r\n                  if ((r | 0) > (e | 0) | (r | 0) == (e | 0) & s >>> 0 > d >>> 0) {\r\n                    E = 31;\r\n                    break a;\r\n                  }\r\n                  if ((m | 0) == (i | 0) & (l & -117440513 | 0) == (a | 0)) {\r\n                    p = Qd(m | 0, l | 0, 56) | 0;\r\n                    H() | 0;\r\n                    p = p & 7;\r\n                    q = p + 1 | 0;\r\n                    w = Qd(m | 0, l | 0, 45) | 0;\r\n                    H() | 0;\r\n                    c: do { if (!(oa(w & 127) | 0)) { l = 7; }else {\r\n                      m = Qd(m | 0, l | 0, 52) | 0;\r\n                      H() | 0;\r\n                      m = m & 15;\r\n                      if (!m) {\r\n                        l = 6;\r\n                        break;\r\n                      }\r\n                      l = 1;\r\n                      while (1) {\r\n                        w = Rd(7, 0, (15 - l | 0) * 3 | 0) | 0;\r\n                        if (!((w & i | 0) == 0 & ((H() | 0) & a | 0) == 0)) {\r\n                          l = 7;\r\n                          break c;\r\n                        }\r\n                        if (l >>> 0 < m >>> 0) { l = l + 1 | 0; }else {\r\n                          l = 6;\r\n                          break;\r\n                        }\r\n                      }\r\n                    } } while (0);\r\n                    if ((p + 2 | 0) >>> 0 > l >>> 0) {\r\n                      E = 41;\r\n                      break a;\r\n                    }\r\n                    w = Rd(q | 0, 0, 56) | 0;\r\n                    a = H() | 0 | a & -117440513;\r\n                    v = j;\r\n                    b[v >> 2] = 0;\r\n                    b[v + 4 >> 2] = 0;\r\n                    i = w | i;\r\n                  } else {\r\n                    n = Gd(n | 0, o | 0, 1, 0) | 0;\r\n                    n = Nd(n | 0, H() | 0, d | 0, e | 0) | 0;\r\n                    o = H() | 0;\r\n                  }\r\n                  s = Gd(s | 0, r | 0, 1, 0) | 0;\r\n                  r = H() | 0;\r\n                  j = C + (n << 3) | 0;\r\n                  l = j;\r\n                  m = b[l >> 2] | 0;\r\n                  l = b[l + 4 >> 2] | 0;\r\n                } while (!((m | 0) == 0 & (l | 0) == 0));\r\n              }\r\n              w = j;\r\n              b[w >> 2] = i;\r\n              b[w + 4 >> 2] = a;\r\n            }\r\n            g = Gd(g | 0, h | 0, 1, 0) | 0;\r\n            h = H() | 0;\r\n            if (!((h | 0) < (e | 0) | (h | 0) == (e | 0) & g >>> 0 < d >>> 0)) { break b; }\r\n            a = D + (g << 3) | 0;\r\n            i = b[a >> 2] | 0;\r\n            a = b[a + 4 >> 2] | 0;\r\n          }\r\n        } } while (0);\r\n        w = Gd(d | 0, e | 0, 5, 0) | 0;\r\n        v = H() | 0;\r\n        if (v >>> 0 < 0 | (v | 0) == 0 & w >>> 0 < 11) {\r\n          E = 85;\r\n          break;\r\n        }\r\n        w = Kd(d | 0, e | 0, 6, 0) | 0;\r\n        H() | 0;\r\n        w = Fd(w, 8) | 0;\r\n        if (!w) {\r\n          E = 48;\r\n          break;\r\n        }\r\n        do { if (x) {\r\n          q = 0;\r\n          a = 0;\r\n          p = 0;\r\n          r = 0;\r\n          while (1) {\r\n            l = C + (q << 3) | 0;\r\n            h = l;\r\n            g = b[h >> 2] | 0;\r\n            h = b[h + 4 >> 2] | 0;\r\n            if (!((g | 0) == 0 & (h | 0) == 0)) {\r\n              m = Qd(g | 0, h | 0, 56) | 0;\r\n              H() | 0;\r\n              m = m & 7;\r\n              i = m + 1 | 0;\r\n              n = h & -117440513;\r\n              v = Qd(g | 0, h | 0, 45) | 0;\r\n              H() | 0;\r\n              d: do { if (oa(v & 127) | 0) {\r\n                o = Qd(g | 0, h | 0, 52) | 0;\r\n                H() | 0;\r\n                o = o & 15;\r\n                if (o | 0) {\r\n                  j = 1;\r\n                  while (1) {\r\n                    v = Rd(7, 0, (15 - j | 0) * 3 | 0) | 0;\r\n                    if (!((g & v | 0) == 0 & (n & (H() | 0) | 0) == 0)) { break d; }\r\n                    if (j >>> 0 < o >>> 0) { j = j + 1 | 0; }else { break; }\r\n                  }\r\n                }\r\n                h = Rd(i | 0, 0, 56) | 0;\r\n                g = h | g;\r\n                h = H() | 0 | n;\r\n                i = l;\r\n                b[i >> 2] = g;\r\n                b[i + 4 >> 2] = h;\r\n                i = m + 2 | 0;\r\n              } } while (0);\r\n              if ((i | 0) == 7) {\r\n                v = w + (a << 3) | 0;\r\n                b[v >> 2] = g;\r\n                b[v + 4 >> 2] = h & -117440513;\r\n                a = Gd(a | 0, p | 0, 1, 0) | 0;\r\n                v = H() | 0;\r\n              } else { v = p; }\r\n            } else { v = p; }\r\n            q = Gd(q | 0, r | 0, 1, 0) | 0;\r\n            r = H() | 0;\r\n            if (!((r | 0) < (e | 0) | (r | 0) == (e | 0) & q >>> 0 < d >>> 0)) { break; }else { p = v; }\r\n          }\r\n          if (x) {\r\n            s = A >>> 0 > 15;\r\n            t = Rd(A | 0, 0, 52) | 0;\r\n            u = H() | 0;\r\n            if (!y) {\r\n              g = 0;\r\n              j = 0;\r\n              i = 0;\r\n              h = 0;\r\n              while (1) {\r\n                if (!((k | 0) == 0 & (f | 0) == 0)) {\r\n                  A = c + (g << 3) | 0;\r\n                  b[A >> 2] = k;\r\n                  b[A + 4 >> 2] = f;\r\n                  g = Gd(g | 0, j | 0, 1, 0) | 0;\r\n                  j = H() | 0;\r\n                }\r\n                i = Gd(i | 0, h | 0, 1, 0) | 0;\r\n                h = H() | 0;\r\n                if (!((h | 0) < (e | 0) | (h | 0) == (e | 0) & i >>> 0 < d >>> 0)) { break; }\r\n                f = D + (i << 3) | 0;\r\n                k = b[f >> 2] | 0;\r\n                f = b[f + 4 >> 2] | 0;\r\n              }\r\n              f = v;\r\n              break;\r\n            }\r\n            g = 0;\r\n            j = 0;\r\n            h = 0;\r\n            i = 0;\r\n            while (1) {\r\n              do { if (!((k | 0) == 0 & (f | 0) == 0)) {\r\n                o = Qd(k | 0, f | 0, 52) | 0;\r\n                H() | 0;\r\n                o = o & 15;\r\n                if (s | (o | 0) < (A | 0)) {\r\n                  E = 80;\r\n                  break a;\r\n                }\r\n                if ((o | 0) != (A | 0)) {\r\n                  l = k | t;\r\n                  m = f & -15728641 | u;\r\n                  if (o >>> 0 >= z >>> 0) {\r\n                    n = A;\r\n                    do {\r\n                      y = Rd(7, 0, (14 - n | 0) * 3 | 0) | 0;\r\n                      n = n + 1 | 0;\r\n                      l = y | l;\r\n                      m = H() | 0 | m;\r\n                    } while (n >>> 0 < o >>> 0);\r\n                  }\r\n                } else {\r\n                  l = k;\r\n                  m = f;\r\n                }\r\n                p = Od(l | 0, m | 0, d | 0, e | 0) | 0;\r\n                n = 0;\r\n                o = 0;\r\n                r = H() | 0;\r\n                do {\r\n                  if ((n | 0) > (e | 0) | (n | 0) == (e | 0) & o >>> 0 > d >>> 0) {\r\n                    E = 81;\r\n                    break a;\r\n                  }\r\n                  y = C + (p << 3) | 0;\r\n                  q = b[y + 4 >> 2] | 0;\r\n                  if ((q & -117440513 | 0) == (m | 0) ? (b[y >> 2] | 0) == (l | 0) : 0) {\r\n                    E = 65;\r\n                    break;\r\n                  }\r\n                  y = Gd(p | 0, r | 0, 1, 0) | 0;\r\n                  p = Nd(y | 0, H() | 0, d | 0, e | 0) | 0;\r\n                  r = H() | 0;\r\n                  o = Gd(o | 0, n | 0, 1, 0) | 0;\r\n                  n = H() | 0;\r\n                  y = C + (p << 3) | 0;\r\n                } while (!((b[y >> 2] | 0) == (l | 0) ? (b[y + 4 >> 2] | 0) == (m | 0) : 0));\r\n                if ((E | 0) == 65 ? (E = 0, 0 == 0 & (q & 117440512 | 0) == 100663296) : 0) { break; }\r\n                y = c + (g << 3) | 0;\r\n                b[y >> 2] = k;\r\n                b[y + 4 >> 2] = f;\r\n                g = Gd(g | 0, j | 0, 1, 0) | 0;\r\n                j = H() | 0;\r\n              } } while (0);\r\n              h = Gd(h | 0, i | 0, 1, 0) | 0;\r\n              i = H() | 0;\r\n              if (!((i | 0) < (e | 0) | (i | 0) == (e | 0) & h >>> 0 < d >>> 0)) { break; }\r\n              f = D + (h << 3) | 0;\r\n              k = b[f >> 2] | 0;\r\n              f = b[f + 4 >> 2] | 0;\r\n            }\r\n            f = v;\r\n          } else {\r\n            g = 0;\r\n            f = v;\r\n          }\r\n        } else {\r\n          g = 0;\r\n          a = 0;\r\n          f = 0;\r\n        } } while (0);\r\n        Xd(C | 0, 0, B | 0) | 0;\r\n        Wd(D | 0, w | 0, a << 3 | 0) | 0;\r\n        Ed(w);\r\n        if ((a | 0) == 0 & (f | 0) == 0) {\r\n          E = 89;\r\n          break;\r\n        } else {\r\n          c = c + (g << 3) | 0;\r\n          e = f;\r\n          d = a;\r\n        }\r\n      }\r\n      if ((E | 0) == 16) {\r\n        if (0 == 0 & (f & 117440512 | 0) == 0) {\r\n          f = 4;\r\n          E = 27;\r\n        } else { E = 21; }\r\n      } else if ((E | 0) == 31) { I(27795, 27122, 529, 27132); }else if ((E | 0) == 41) {\r\n        Ed(D);\r\n        Ed(C);\r\n        E = 10;\r\n        return E | 0;\r\n      } else if ((E | 0) == 48) {\r\n        Ed(D);\r\n        Ed(C);\r\n        E = 13;\r\n        return E | 0;\r\n      } else if ((E | 0) == 80) { I(27795, 27122, 620, 27132); }else if ((E | 0) == 81) { I(27795, 27122, 632, 27132); }else if ((E | 0) == 85) {\r\n        Wd(c | 0, D | 0, d << 3 | 0) | 0;\r\n        E = 89;\r\n      }\r\n      if ((E | 0) == 21) {\r\n        Ed(D);\r\n        Ed(C);\r\n        E = 5;\r\n        return E | 0;\r\n      } else if ((E | 0) == 27) {\r\n        Ed(D);\r\n        Ed(C);\r\n        E = f;\r\n        return E | 0;\r\n      } else if ((E | 0) == 89) {\r\n        Ed(D);\r\n        Ed(C);\r\n        E = 0;\r\n        return E | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function Mb(a, c, d, e, f, g, h) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      h = h | 0;\r\n      var i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0;\r\n      q = T;\r\n      T = T + 16 | 0;\r\n      p = q;\r\n      if (!((d | 0) > 0 | (d | 0) == 0 & c >>> 0 > 0)) {\r\n        p = 0;\r\n        T = q;\r\n        return p | 0;\r\n      }\r\n      if ((h | 0) >= 16) {\r\n        p = 12;\r\n        T = q;\r\n        return p | 0;\r\n      }\r\n      n = 0;\r\n      o = 0;\r\n      m = 0;\r\n      i = 0;\r\n      a: while (1) {\r\n        k = a + (n << 3) | 0;\r\n        j = b[k >> 2] | 0;\r\n        k = b[k + 4 >> 2] | 0;\r\n        l = Qd(j | 0, k | 0, 52) | 0;\r\n        H() | 0;\r\n        if ((l & 15 | 0) > (h | 0)) {\r\n          i = 12;\r\n          j = 11;\r\n          break;\r\n        }\r\n        fc(p, j, k, h);\r\n        l = p;\r\n        k = b[l >> 2] | 0;\r\n        l = b[l + 4 >> 2] | 0;\r\n        if ((k | 0) == 0 & (l | 0) == 0) { j = m; }else {\r\n          j = m;\r\n          do {\r\n            if (!((i | 0) < (g | 0) | (i | 0) == (g | 0) & j >>> 0 < f >>> 0)) {\r\n              j = 10;\r\n              break a;\r\n            }\r\n            m = e + (j << 3) | 0;\r\n            b[m >> 2] = k;\r\n            b[m + 4 >> 2] = l;\r\n            j = Gd(j | 0, i | 0, 1, 0) | 0;\r\n            i = H() | 0;\r\n            hc(p);\r\n            m = p;\r\n            k = b[m >> 2] | 0;\r\n            l = b[m + 4 >> 2] | 0;\r\n          } while (!((k | 0) == 0 & (l | 0) == 0));\r\n        }\r\n        n = Gd(n | 0, o | 0, 1, 0) | 0;\r\n        o = H() | 0;\r\n        if (!((o | 0) < (d | 0) | (o | 0) == (d | 0) & n >>> 0 < c >>> 0)) {\r\n          i = 0;\r\n          j = 11;\r\n          break;\r\n        } else { m = j; }\r\n      }\r\n      if ((j | 0) == 10) {\r\n        p = 14;\r\n        T = q;\r\n        return p | 0;\r\n      } else if ((j | 0) == 11) {\r\n        T = q;\r\n        return i | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function Nb(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0;\r\n      n = T;\r\n      T = T + 16 | 0;\r\n      m = n;\r\n      a: do { if ((d | 0) > 0 | (d | 0) == 0 & c >>> 0 > 0) {\r\n        k = 0;\r\n        h = 0;\r\n        g = 0;\r\n        l = 0;\r\n        while (1) {\r\n          j = a + (k << 3) | 0;\r\n          i = b[j >> 2] | 0;\r\n          j = b[j + 4 >> 2] | 0;\r\n          if (!((i | 0) == 0 & (j | 0) == 0)) {\r\n            j = (Gb(i, j, e, m) | 0) == 0;\r\n            i = m;\r\n            h = Gd(b[i >> 2] | 0, b[i + 4 >> 2] | 0, h | 0, g | 0) | 0;\r\n            g = H() | 0;\r\n            if (!j) {\r\n              g = 12;\r\n              break;\r\n            }\r\n          }\r\n          k = Gd(k | 0, l | 0, 1, 0) | 0;\r\n          l = H() | 0;\r\n          if (!((l | 0) < (d | 0) | (l | 0) == (d | 0) & k >>> 0 < c >>> 0)) { break a; }\r\n        }\r\n        T = n;\r\n        return g | 0;\r\n      } else {\r\n        h = 0;\r\n        g = 0;\r\n      } } while (0);\r\n      b[f >> 2] = h;\r\n      b[f + 4 >> 2] = g;\r\n      f = 0;\r\n      T = n;\r\n      return f | 0;\r\n    }\r\n    function Ob(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      b = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      return b & 1 | 0;\r\n    }\r\n    function Pb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      e = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      if (!e) {\r\n        e = 0;\r\n        return e | 0;\r\n      }\r\n      d = 1;\r\n      while (1) {\r\n        c = Qd(a | 0, b | 0, (15 - d | 0) * 3 | 0) | 0;\r\n        H() | 0;\r\n        c = c & 7;\r\n        if (c | 0) {\r\n          d = 5;\r\n          break;\r\n        }\r\n        if (d >>> 0 < e >>> 0) { d = d + 1 | 0; }else {\r\n          c = 0;\r\n          d = 5;\r\n          break;\r\n        }\r\n      }\r\n      if ((d | 0) == 5) { return c | 0; }\r\n      return 0;\r\n    }\r\n    function Qb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      i = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      i = i & 15;\r\n      if (!i) {\r\n        h = b;\r\n        i = a;\r\n        G(h | 0);\r\n        return i | 0;\r\n      }\r\n      h = 1;\r\n      c = 0;\r\n      while (1) {\r\n        f = (15 - h | 0) * 3 | 0;\r\n        d = Rd(7, 0, f | 0) | 0;\r\n        e = H() | 0;\r\n        g = Qd(a | 0, b | 0, f | 0) | 0;\r\n        H() | 0;\r\n        f = Rd($a(g & 7) | 0, 0, f | 0) | 0;\r\n        g = H() | 0;\r\n        a = f | a & ~d;\r\n        b = g | b & ~e;\r\n        a: do { if (!c) { if (!((f & d | 0) == 0 & (g & e | 0) == 0)) {\r\n          d = Qd(a | 0, b | 0, 52) | 0;\r\n          H() | 0;\r\n          d = d & 15;\r\n          if (!d) { c = 1; }else {\r\n            c = 1;\r\n            b: while (1) {\r\n              g = Qd(a | 0, b | 0, (15 - c | 0) * 3 | 0) | 0;\r\n              H() | 0;\r\n              switch (g & 7) {\r\n                case 1:\r\n                  break b;\r\n                case 0:\r\n                  break;\r\n                default:\r\n                  {\r\n                    c = 1;\r\n                    break a;\r\n                  }\r\n              }\r\n              if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else {\r\n                c = 1;\r\n                break a;\r\n              }\r\n            }\r\n            c = 1;\r\n            while (1) {\r\n              g = (15 - c | 0) * 3 | 0;\r\n              e = Qd(a | 0, b | 0, g | 0) | 0;\r\n              H() | 0;\r\n              f = Rd(7, 0, g | 0) | 0;\r\n              b = b & ~(H() | 0);\r\n              g = Rd($a(e & 7) | 0, 0, g | 0) | 0;\r\n              a = a & ~f | g;\r\n              b = b | (H() | 0);\r\n              if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else {\r\n                c = 1;\r\n                break;\r\n              }\r\n            }\r\n          }\r\n        } else { c = 0; } } } while (0);\r\n        if (h >>> 0 < i >>> 0) { h = h + 1 | 0; }else { break; }\r\n      }\r\n      G(b | 0);\r\n      return a | 0;\r\n    }\r\n    function Rb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      d = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      d = d & 15;\r\n      if (!d) {\r\n        c = b;\r\n        d = a;\r\n        G(c | 0);\r\n        return d | 0;\r\n      }\r\n      c = 1;\r\n      while (1) {\r\n        f = (15 - c | 0) * 3 | 0;\r\n        g = Qd(a | 0, b | 0, f | 0) | 0;\r\n        H() | 0;\r\n        e = Rd(7, 0, f | 0) | 0;\r\n        b = b & ~(H() | 0);\r\n        f = Rd($a(g & 7) | 0, 0, f | 0) | 0;\r\n        a = f | a & ~e;\r\n        b = H() | 0 | b;\r\n        if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else { break; }\r\n      }\r\n      G(b | 0);\r\n      return a | 0;\r\n    }\r\n    function Sb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      i = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      i = i & 15;\r\n      if (!i) {\r\n        h = b;\r\n        i = a;\r\n        G(h | 0);\r\n        return i | 0;\r\n      }\r\n      h = 1;\r\n      c = 0;\r\n      while (1) {\r\n        f = (15 - h | 0) * 3 | 0;\r\n        d = Rd(7, 0, f | 0) | 0;\r\n        e = H() | 0;\r\n        g = Qd(a | 0, b | 0, f | 0) | 0;\r\n        H() | 0;\r\n        f = Rd(ab(g & 7) | 0, 0, f | 0) | 0;\r\n        g = H() | 0;\r\n        a = f | a & ~d;\r\n        b = g | b & ~e;\r\n        a: do { if (!c) { if (!((f & d | 0) == 0 & (g & e | 0) == 0)) {\r\n          d = Qd(a | 0, b | 0, 52) | 0;\r\n          H() | 0;\r\n          d = d & 15;\r\n          if (!d) { c = 1; }else {\r\n            c = 1;\r\n            b: while (1) {\r\n              g = Qd(a | 0, b | 0, (15 - c | 0) * 3 | 0) | 0;\r\n              H() | 0;\r\n              switch (g & 7) {\r\n                case 1:\r\n                  break b;\r\n                case 0:\r\n                  break;\r\n                default:\r\n                  {\r\n                    c = 1;\r\n                    break a;\r\n                  }\r\n              }\r\n              if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else {\r\n                c = 1;\r\n                break a;\r\n              }\r\n            }\r\n            c = 1;\r\n            while (1) {\r\n              e = (15 - c | 0) * 3 | 0;\r\n              f = Rd(7, 0, e | 0) | 0;\r\n              g = b & ~(H() | 0);\r\n              b = Qd(a | 0, b | 0, e | 0) | 0;\r\n              H() | 0;\r\n              b = Rd(ab(b & 7) | 0, 0, e | 0) | 0;\r\n              a = a & ~f | b;\r\n              b = g | (H() | 0);\r\n              if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else {\r\n                c = 1;\r\n                break;\r\n              }\r\n            }\r\n          }\r\n        } else { c = 0; } } } while (0);\r\n        if (h >>> 0 < i >>> 0) { h = h + 1 | 0; }else { break; }\r\n      }\r\n      G(b | 0);\r\n      return a | 0;\r\n    }\r\n    function Tb(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      d = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      d = d & 15;\r\n      if (!d) {\r\n        c = b;\r\n        d = a;\r\n        G(c | 0);\r\n        return d | 0;\r\n      }\r\n      c = 1;\r\n      while (1) {\r\n        g = (15 - c | 0) * 3 | 0;\r\n        f = Rd(7, 0, g | 0) | 0;\r\n        e = b & ~(H() | 0);\r\n        b = Qd(a | 0, b | 0, g | 0) | 0;\r\n        H() | 0;\r\n        b = Rd(ab(b & 7) | 0, 0, g | 0) | 0;\r\n        a = b | a & ~f;\r\n        b = H() | 0 | e;\r\n        if (c >>> 0 < d >>> 0) { c = c + 1 | 0; }else { break; }\r\n      }\r\n      G(b | 0);\r\n      return a | 0;\r\n    }\r\n    function Ub(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0;\r\n      j = T;\r\n      T = T + 64 | 0;\r\n      i = j + 40 | 0;\r\n      e = j + 24 | 0;\r\n      f = j + 12 | 0;\r\n      g = j;\r\n      Rd(c | 0, 0, 52) | 0;\r\n      d = H() | 0 | 134225919;\r\n      if (!c) {\r\n        if ((b[a + 4 >> 2] | 0) > 2) {\r\n          h = 0;\r\n          i = 0;\r\n          G(h | 0);\r\n          T = j;\r\n          return i | 0;\r\n        }\r\n        if ((b[a + 8 >> 2] | 0) > 2) {\r\n          h = 0;\r\n          i = 0;\r\n          G(h | 0);\r\n          T = j;\r\n          return i | 0;\r\n        }\r\n        if ((b[a + 12 >> 2] | 0) > 2) {\r\n          h = 0;\r\n          i = 0;\r\n          G(h | 0);\r\n          T = j;\r\n          return i | 0;\r\n        }\r\n        Rd(qa(a) | 0, 0, 45) | 0;\r\n        h = H() | 0 | d;\r\n        i = -1;\r\n        G(h | 0);\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      b[i >> 2] = b[a >> 2];\r\n      b[i + 4 >> 2] = b[a + 4 >> 2];\r\n      b[i + 8 >> 2] = b[a + 8 >> 2];\r\n      b[i + 12 >> 2] = b[a + 12 >> 2];\r\n      h = i + 4 | 0;\r\n      if ((c | 0) > 0) {\r\n        a = -1;\r\n        while (1) {\r\n          b[e >> 2] = b[h >> 2];\r\n          b[e + 4 >> 2] = b[h + 4 >> 2];\r\n          b[e + 8 >> 2] = b[h + 8 >> 2];\r\n          if (!(c & 1)) {\r\n            Va(h);\r\n            b[f >> 2] = b[h >> 2];\r\n            b[f + 4 >> 2] = b[h + 4 >> 2];\r\n            b[f + 8 >> 2] = b[h + 8 >> 2];\r\n            Xa(f);\r\n          } else {\r\n            Ua(h);\r\n            b[f >> 2] = b[h >> 2];\r\n            b[f + 4 >> 2] = b[h + 4 >> 2];\r\n            b[f + 8 >> 2] = b[h + 8 >> 2];\r\n            Wa(f);\r\n          }\r\n          Pa(e, f, g);\r\n          Ma(g);\r\n          l = (15 - c | 0) * 3 | 0;\r\n          k = Rd(7, 0, l | 0) | 0;\r\n          d = d & ~(H() | 0);\r\n          l = Rd(Ra(g) | 0, 0, l | 0) | 0;\r\n          a = l | a & ~k;\r\n          d = H() | 0 | d;\r\n          if ((c | 0) > 1) { c = c + -1 | 0; }else { break; }\r\n        }\r\n      } else { a = -1; }\r\n      a: do { if (((b[h >> 2] | 0) <= 2 ? (b[i + 8 >> 2] | 0) <= 2 : 0) ? (b[i + 12 >> 2] | 0) <= 2 : 0) {\r\n        e = qa(i) | 0;\r\n        c = Rd(e | 0, 0, 45) | 0;\r\n        c = c | a;\r\n        a = H() | 0 | d & -1040385;\r\n        g = ra(i) | 0;\r\n        if (!(oa(e) | 0)) {\r\n          if ((g | 0) <= 0) { break; }\r\n          f = 0;\r\n          while (1) {\r\n            e = Qd(c | 0, a | 0, 52) | 0;\r\n            H() | 0;\r\n            e = e & 15;\r\n            if (e) {\r\n              d = 1;\r\n              while (1) {\r\n                l = (15 - d | 0) * 3 | 0;\r\n                i = Qd(c | 0, a | 0, l | 0) | 0;\r\n                H() | 0;\r\n                k = Rd(7, 0, l | 0) | 0;\r\n                a = a & ~(H() | 0);\r\n                l = Rd($a(i & 7) | 0, 0, l | 0) | 0;\r\n                c = c & ~k | l;\r\n                a = a | (H() | 0);\r\n                if (d >>> 0 < e >>> 0) { d = d + 1 | 0; }else { break; }\r\n              }\r\n            }\r\n            f = f + 1 | 0;\r\n            if ((f | 0) == (g | 0)) { break a; }\r\n          }\r\n        }\r\n        f = Qd(c | 0, a | 0, 52) | 0;\r\n        H() | 0;\r\n        f = f & 15;\r\n        b: do { if (f) {\r\n          d = 1;\r\n          c: while (1) {\r\n            l = Qd(c | 0, a | 0, (15 - d | 0) * 3 | 0) | 0;\r\n            H() | 0;\r\n            switch (l & 7) {\r\n              case 1:\r\n                break c;\r\n              case 0:\r\n                break;\r\n              default:\r\n                break b;\r\n            }\r\n            if (d >>> 0 < f >>> 0) { d = d + 1 | 0; }else { break b; }\r\n          }\r\n          if (ua(e, b[i >> 2] | 0) | 0) {\r\n            d = 1;\r\n            while (1) {\r\n              i = (15 - d | 0) * 3 | 0;\r\n              k = Rd(7, 0, i | 0) | 0;\r\n              l = a & ~(H() | 0);\r\n              a = Qd(c | 0, a | 0, i | 0) | 0;\r\n              H() | 0;\r\n              a = Rd(ab(a & 7) | 0, 0, i | 0) | 0;\r\n              c = c & ~k | a;\r\n              a = l | (H() | 0);\r\n              if (d >>> 0 < f >>> 0) { d = d + 1 | 0; }else { break; }\r\n            }\r\n          } else {\r\n            d = 1;\r\n            while (1) {\r\n              l = (15 - d | 0) * 3 | 0;\r\n              i = Qd(c | 0, a | 0, l | 0) | 0;\r\n              H() | 0;\r\n              k = Rd(7, 0, l | 0) | 0;\r\n              a = a & ~(H() | 0);\r\n              l = Rd($a(i & 7) | 0, 0, l | 0) | 0;\r\n              c = c & ~k | l;\r\n              a = a | (H() | 0);\r\n              if (d >>> 0 < f >>> 0) { d = d + 1 | 0; }else { break; }\r\n            }\r\n          }\r\n        } } while (0);\r\n        if ((g | 0) > 0) {\r\n          d = 0;\r\n          do {\r\n            c = Qb(c, a) | 0;\r\n            a = H() | 0;\r\n            d = d + 1 | 0;\r\n          } while ((d | 0) != (g | 0));\r\n        }\r\n      } else {\r\n        c = 0;\r\n        a = 0;\r\n      } } while (0);\r\n      k = a;\r\n      l = c;\r\n      G(k | 0);\r\n      T = j;\r\n      return l | 0;\r\n    }\r\n    function Vb(a) {\r\n      a = a | 0;\r\n      return (a | 0) % 2 | 0 | 0;\r\n    }\r\n    function Wb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      e = f;\r\n      if (c >>> 0 > 15) {\r\n        e = 4;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      if ((b[a + 4 >> 2] & 2146435072 | 0) == 2146435072) {\r\n        e = 3;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      if ((b[a + 8 + 4 >> 2] & 2146435072 | 0) == 2146435072) {\r\n        e = 3;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      qb(a, c, e);\r\n      c = Ub(e, c) | 0;\r\n      e = H() | 0;\r\n      b[d >> 2] = c;\r\n      b[d + 4 >> 2] = e;\r\n      if ((c | 0) == 0 & (e | 0) == 0) { I(27795, 27122, 959, 27145); }\r\n      e = 0;\r\n      T = f;\r\n      return e | 0;\r\n    }\r\n    function Xb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      f = d + 4 | 0;\r\n      g = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      g = g & 15;\r\n      h = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      e = (g | 0) == 0;\r\n      if (!(oa(h & 127) | 0)) {\r\n        if (e) {\r\n          h = 0;\r\n          return h | 0;\r\n        }\r\n        if ((b[f >> 2] | 0) == 0 ? (b[d + 8 >> 2] | 0) == 0 : 0) { e = (b[d + 12 >> 2] | 0) != 0 & 1; }else { e = 1; }\r\n      } else if (e) {\r\n        h = 1;\r\n        return h | 0;\r\n      } else { e = 1; }\r\n      d = 1;\r\n      while (1) {\r\n        if (!(d & 1)) { Xa(f); }else { Wa(f); }\r\n        h = Qd(a | 0, c | 0, (15 - d | 0) * 3 | 0) | 0;\r\n        H() | 0;\r\n        Ya(f, h & 7);\r\n        if (d >>> 0 < g >>> 0) { d = d + 1 | 0; }else { break; }\r\n      }\r\n      return e | 0;\r\n    }\r\n    function Yb(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0;\r\n      l = T;\r\n      T = T + 16 | 0;\r\n      j = l;\r\n      k = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      k = k & 127;\r\n      if (k >>> 0 > 121) {\r\n        b[d >> 2] = 0;\r\n        b[d + 4 >> 2] = 0;\r\n        b[d + 8 >> 2] = 0;\r\n        b[d + 12 >> 2] = 0;\r\n        k = 5;\r\n        T = l;\r\n        return k | 0;\r\n      }\r\n      a: do { if ((oa(k) | 0) != 0 ? (g = Qd(a | 0, c | 0, 52) | 0, H() | 0, g = g & 15, (g | 0) != 0) : 0) {\r\n        e = 1;\r\n        b: while (1) {\r\n          i = Qd(a | 0, c | 0, (15 - e | 0) * 3 | 0) | 0;\r\n          H() | 0;\r\n          switch (i & 7) {\r\n            case 5:\r\n              break b;\r\n            case 0:\r\n              break;\r\n            default:\r\n              {\r\n                e = c;\r\n                break a;\r\n              }\r\n          }\r\n          if (e >>> 0 < g >>> 0) { e = e + 1 | 0; }else {\r\n            e = c;\r\n            break a;\r\n          }\r\n        }\r\n        f = 1;\r\n        e = c;\r\n        while (1) {\r\n          c = (15 - f | 0) * 3 | 0;\r\n          h = Rd(7, 0, c | 0) | 0;\r\n          i = e & ~(H() | 0);\r\n          e = Qd(a | 0, e | 0, c | 0) | 0;\r\n          H() | 0;\r\n          e = Rd(ab(e & 7) | 0, 0, c | 0) | 0;\r\n          a = a & ~h | e;\r\n          e = i | (H() | 0);\r\n          if (f >>> 0 < g >>> 0) { f = f + 1 | 0; }else { break; }\r\n        }\r\n      } else { e = c; } } while (0);\r\n      i = 7696 + (k * 28 | 0) | 0;\r\n      b[d >> 2] = b[i >> 2];\r\n      b[d + 4 >> 2] = b[i + 4 >> 2];\r\n      b[d + 8 >> 2] = b[i + 8 >> 2];\r\n      b[d + 12 >> 2] = b[i + 12 >> 2];\r\n      if (!(Xb(a, e, d) | 0)) {\r\n        k = 0;\r\n        T = l;\r\n        return k | 0;\r\n      }\r\n      h = d + 4 | 0;\r\n      b[j >> 2] = b[h >> 2];\r\n      b[j + 4 >> 2] = b[h + 4 >> 2];\r\n      b[j + 8 >> 2] = b[h + 8 >> 2];\r\n      g = Qd(a | 0, e | 0, 52) | 0;\r\n      H() | 0;\r\n      i = g & 15;\r\n      if (!(g & 1)) { g = i; }else {\r\n        Xa(h);\r\n        g = i + 1 | 0;\r\n      }\r\n      if (!(oa(k) | 0)) { e = 0; }else {\r\n        c: do { if (!i) { e = 0; }else {\r\n          c = 1;\r\n          while (1) {\r\n            f = Qd(a | 0, e | 0, (15 - c | 0) * 3 | 0) | 0;\r\n            H() | 0;\r\n            f = f & 7;\r\n            if (f | 0) {\r\n              e = f;\r\n              break c;\r\n            }\r\n            if (c >>> 0 < i >>> 0) { c = c + 1 | 0; }else {\r\n              e = 0;\r\n              break;\r\n            }\r\n          }\r\n        } } while (0);\r\n        e = (e | 0) == 4 & 1;\r\n      }\r\n      if (!(xb(d, g, e, 0) | 0)) {\r\n        if ((g | 0) != (i | 0)) {\r\n          b[h >> 2] = b[j >> 2];\r\n          b[h + 4 >> 2] = b[j + 4 >> 2];\r\n          b[h + 8 >> 2] = b[j + 8 >> 2];\r\n        }\r\n      } else {\r\n        if (oa(k) | 0) { do {} while ((xb(d, g, 0, 0) | 0) != 0); }\r\n        if ((g | 0) != (i | 0)) { Va(h); }\r\n      }\r\n      k = 0;\r\n      T = l;\r\n      return k | 0;\r\n    }\r\n    function Zb(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      d = f;\r\n      e = Yb(a, b, d) | 0;\r\n      if (e | 0) {\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      e = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      ub(d, e & 15, c);\r\n      e = 0;\r\n      T = f;\r\n      return e | 0;\r\n    }\r\n    function _b(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      g = T;\r\n      T = T + 16 | 0;\r\n      f = g;\r\n      d = Yb(a, b, f) | 0;\r\n      if (d | 0) {\r\n        f = d;\r\n        T = g;\r\n        return f | 0;\r\n      }\r\n      d = Qd(a | 0, b | 0, 45) | 0;\r\n      H() | 0;\r\n      d = (oa(d & 127) | 0) == 0;\r\n      e = Qd(a | 0, b | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      a: do { if (!d) {\r\n        if (e | 0) {\r\n          d = 1;\r\n          while (1) {\r\n            h = Rd(7, 0, (15 - d | 0) * 3 | 0) | 0;\r\n            if (!((h & a | 0) == 0 & ((H() | 0) & b | 0) == 0)) { break a; }\r\n            if (d >>> 0 < e >>> 0) { d = d + 1 | 0; }else { break; }\r\n          }\r\n        }\r\n        vb(f, e, 0, 5, c);\r\n        h = 0;\r\n        T = g;\r\n        return h | 0;\r\n      } } while (0);\r\n      zb(f, e, 0, 6, c);\r\n      h = 0;\r\n      T = g;\r\n      return h | 0;\r\n    }\r\n    function $b(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      f = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      if (!(oa(f & 127) | 0)) {\r\n        f = 2;\r\n        b[d >> 2] = f;\r\n        return 0;\r\n      }\r\n      f = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      f = f & 15;\r\n      if (!f) {\r\n        f = 5;\r\n        b[d >> 2] = f;\r\n        return 0;\r\n      }\r\n      e = 1;\r\n      while (1) {\r\n        g = Rd(7, 0, (15 - e | 0) * 3 | 0) | 0;\r\n        if (!((g & a | 0) == 0 & ((H() | 0) & c | 0) == 0)) {\r\n          e = 2;\r\n          a = 6;\r\n          break;\r\n        }\r\n        if (e >>> 0 < f >>> 0) { e = e + 1 | 0; }else {\r\n          e = 5;\r\n          a = 6;\r\n          break;\r\n        }\r\n      }\r\n      if ((a | 0) == 6) {\r\n        b[d >> 2] = e;\r\n        return 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function ac(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0;\r\n      m = T;\r\n      T = T + 128 | 0;\r\n      k = m + 112 | 0;\r\n      g = m + 96 | 0;\r\n      l = m;\r\n      f = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      i = f & 15;\r\n      b[k >> 2] = i;\r\n      h = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      h = h & 127;\r\n      a: do { if (oa(h) | 0) {\r\n        if (i | 0) {\r\n          e = 1;\r\n          while (1) {\r\n            j = Rd(7, 0, (15 - e | 0) * 3 | 0) | 0;\r\n            if (!((j & a | 0) == 0 & ((H() | 0) & c | 0) == 0)) {\r\n              f = 0;\r\n              break a;\r\n            }\r\n            if (e >>> 0 < i >>> 0) { e = e + 1 | 0; }else { break; }\r\n          }\r\n        }\r\n        if (!(f & 1)) {\r\n          j = Rd(i + 1 | 0, 0, 52) | 0;\r\n          l = H() | 0 | c & -15728641;\r\n          k = Rd(7, 0, (14 - i | 0) * 3 | 0) | 0;\r\n          l = ac((j | a) & ~k, l & ~(H() | 0), d) | 0;\r\n          T = m;\r\n          return l | 0;\r\n        } else { f = 1; }\r\n      } else { f = 0; } } while (0);\r\n      e = Yb(a, c, g) | 0;\r\n      if (!e) {\r\n        if (f) {\r\n          wb(g, k, l);\r\n          j = 5;\r\n        } else {\r\n          Ab(g, k, l);\r\n          j = 6;\r\n        }\r\n        b: do { if (oa(h) | 0) {\r\n          if (!i) { a = 5; }else {\r\n            e = 1;\r\n            while (1) {\r\n              h = Rd(7, 0, (15 - e | 0) * 3 | 0) | 0;\r\n              if (!((h & a | 0) == 0 & ((H() | 0) & c | 0) == 0)) {\r\n                a = 2;\r\n                break b;\r\n              }\r\n              if (e >>> 0 < i >>> 0) { e = e + 1 | 0; }else {\r\n                a = 5;\r\n                break;\r\n              }\r\n            }\r\n          }\r\n        } else { a = 2; } } while (0);\r\n        Xd(d | 0, -1, a << 2 | 0) | 0;\r\n        c: do { if (f) {\r\n          g = 0;\r\n          while (1) {\r\n            h = l + (g << 4) | 0;\r\n            yb(h, b[k >> 2] | 0) | 0;\r\n            h = b[h >> 2] | 0;\r\n            i = b[d >> 2] | 0;\r\n            if ((i | 0) == -1 | (i | 0) == (h | 0)) { e = d; }else {\r\n              f = 0;\r\n              do {\r\n                f = f + 1 | 0;\r\n                if (f >>> 0 >= a >>> 0) {\r\n                  e = 1;\r\n                  break c;\r\n                }\r\n                e = d + (f << 2) | 0;\r\n                i = b[e >> 2] | 0;\r\n              } while (!((i | 0) == -1 | (i | 0) == (h | 0)));\r\n            }\r\n            b[e >> 2] = h;\r\n            g = g + 1 | 0;\r\n            if (g >>> 0 >= j >>> 0) {\r\n              e = 0;\r\n              break;\r\n            }\r\n          }\r\n        } else {\r\n          g = 0;\r\n          while (1) {\r\n            h = l + (g << 4) | 0;\r\n            xb(h, b[k >> 2] | 0, 0, 1) | 0;\r\n            h = b[h >> 2] | 0;\r\n            i = b[d >> 2] | 0;\r\n            if ((i | 0) == -1 | (i | 0) == (h | 0)) { e = d; }else {\r\n              f = 0;\r\n              do {\r\n                f = f + 1 | 0;\r\n                if (f >>> 0 >= a >>> 0) {\r\n                  e = 1;\r\n                  break c;\r\n                }\r\n                e = d + (f << 2) | 0;\r\n                i = b[e >> 2] | 0;\r\n              } while (!((i | 0) == -1 | (i | 0) == (h | 0)));\r\n            }\r\n            b[e >> 2] = h;\r\n            g = g + 1 | 0;\r\n            if (g >>> 0 >= j >>> 0) {\r\n              e = 0;\r\n              break;\r\n            }\r\n          }\r\n        } } while (0);\r\n      }\r\n      l = e;\r\n      T = m;\r\n      return l | 0;\r\n    }\r\n    function bc() {\r\n      return 12;\r\n    }\r\n    function cc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      if (a >>> 0 > 15) {\r\n        i = 4;\r\n        return i | 0;\r\n      }\r\n      Rd(a | 0, 0, 52) | 0;\r\n      i = H() | 0 | 134225919;\r\n      if (!a) {\r\n        d = 0;\r\n        e = 0;\r\n        do {\r\n          if (oa(e) | 0) {\r\n            Rd(e | 0, 0, 45) | 0;\r\n            h = i | (H() | 0);\r\n            a = c + (d << 3) | 0;\r\n            b[a >> 2] = -1;\r\n            b[a + 4 >> 2] = h;\r\n            d = d + 1 | 0;\r\n          }\r\n          e = e + 1 | 0;\r\n        } while ((e | 0) != 122);\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      d = 0;\r\n      h = 0;\r\n      do {\r\n        if (oa(h) | 0) {\r\n          Rd(h | 0, 0, 45) | 0;\r\n          e = 1;\r\n          f = -1;\r\n          g = i | (H() | 0);\r\n          while (1) {\r\n            j = Rd(7, 0, (15 - e | 0) * 3 | 0) | 0;\r\n            f = f & ~j;\r\n            g = g & ~(H() | 0);\r\n            if ((e | 0) == (a | 0)) { break; }else { e = e + 1 | 0; }\r\n          }\r\n          j = c + (d << 3) | 0;\r\n          b[j >> 2] = f;\r\n          b[j + 4 >> 2] = g;\r\n          d = d + 1 | 0;\r\n        }\r\n        h = h + 1 | 0;\r\n      } while ((h | 0) != 122);\r\n      d = 0;\r\n      return d | 0;\r\n    }\r\n    function dc(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0;\r\n      t = T;\r\n      T = T + 16 | 0;\r\n      r = t;\r\n      s = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      s = s & 15;\r\n      if (d >>> 0 > 15) {\r\n        s = 4;\r\n        T = t;\r\n        return s | 0;\r\n      }\r\n      if ((s | 0) < (d | 0)) {\r\n        s = 12;\r\n        T = t;\r\n        return s | 0;\r\n      }\r\n      if ((s | 0) != (d | 0)) {\r\n        g = Rd(d | 0, 0, 52) | 0;\r\n        g = g | a;\r\n        i = H() | 0 | c & -15728641;\r\n        if ((s | 0) > (d | 0)) {\r\n          j = d;\r\n          do {\r\n            q = Rd(7, 0, (14 - j | 0) * 3 | 0) | 0;\r\n            j = j + 1 | 0;\r\n            g = q | g;\r\n            i = H() | 0 | i;\r\n          } while ((j | 0) < (s | 0));\r\n          q = g;\r\n        } else { q = g; }\r\n      } else {\r\n        q = a;\r\n        i = c;\r\n      }\r\n      p = Qd(q | 0, i | 0, 45) | 0;\r\n      H() | 0;\r\n      a: do { if (oa(p & 127) | 0) {\r\n        j = Qd(q | 0, i | 0, 52) | 0;\r\n        H() | 0;\r\n        j = j & 15;\r\n        if (j | 0) {\r\n          g = 1;\r\n          while (1) {\r\n            p = Rd(7, 0, (15 - g | 0) * 3 | 0) | 0;\r\n            if (!((p & q | 0) == 0 & ((H() | 0) & i | 0) == 0)) {\r\n              k = 33;\r\n              break a;\r\n            }\r\n            if (g >>> 0 < j >>> 0) { g = g + 1 | 0; }else { break; }\r\n          }\r\n        }\r\n        p = e;\r\n        b[p >> 2] = 0;\r\n        b[p + 4 >> 2] = 0;\r\n        if ((s | 0) > (d | 0)) {\r\n          p = c & -15728641;\r\n          o = s;\r\n          while (1) {\r\n            n = o;\r\n            o = o + -1 | 0;\r\n            if (o >>> 0 > 15 | (s | 0) < (o | 0)) {\r\n              k = 19;\r\n              break;\r\n            }\r\n            if ((s | 0) != (o | 0)) {\r\n              g = Rd(o | 0, 0, 52) | 0;\r\n              g = g | a;\r\n              j = H() | 0 | p;\r\n              if ((s | 0) < (n | 0)) { m = g; }else {\r\n                k = o;\r\n                do {\r\n                  m = Rd(7, 0, (14 - k | 0) * 3 | 0) | 0;\r\n                  k = k + 1 | 0;\r\n                  g = m | g;\r\n                  j = H() | 0 | j;\r\n                } while ((k | 0) < (s | 0));\r\n                m = g;\r\n              }\r\n            } else {\r\n              m = a;\r\n              j = c;\r\n            }\r\n            l = Qd(m | 0, j | 0, 45) | 0;\r\n            H() | 0;\r\n            if (!(oa(l & 127) | 0)) { g = 0; }else {\r\n              l = Qd(m | 0, j | 0, 52) | 0;\r\n              H() | 0;\r\n              l = l & 15;\r\n              b: do { if (!l) { g = 0; }else {\r\n                k = 1;\r\n                while (1) {\r\n                  g = Qd(m | 0, j | 0, (15 - k | 0) * 3 | 0) | 0;\r\n                  H() | 0;\r\n                  g = g & 7;\r\n                  if (g | 0) { break b; }\r\n                  if (k >>> 0 < l >>> 0) { k = k + 1 | 0; }else {\r\n                    g = 0;\r\n                    break;\r\n                  }\r\n                }\r\n              } } while (0);\r\n              g = (g | 0) == 0 & 1;\r\n            }\r\n            j = Qd(a | 0, c | 0, (15 - n | 0) * 3 | 0) | 0;\r\n            H() | 0;\r\n            j = j & 7;\r\n            if ((j | 0) == 7) {\r\n              f = 5;\r\n              k = 42;\r\n              break;\r\n            }\r\n            g = (g | 0) != 0;\r\n            if ((j | 0) == 1 & g) {\r\n              f = 5;\r\n              k = 42;\r\n              break;\r\n            }\r\n            m = j + (((j | 0) != 0 & g) << 31 >> 31) | 0;\r\n            if (m | 0) {\r\n              k = s - n | 0;\r\n              k = Oc(7, 0, k, ((k | 0) < 0) << 31 >> 31) | 0;\r\n              l = H() | 0;\r\n              if (g) {\r\n                g = Md(k | 0, l | 0, 5, 0) | 0;\r\n                g = Gd(g | 0, H() | 0, -5, -1) | 0;\r\n                g = Kd(g | 0, H() | 0, 6, 0) | 0;\r\n                g = Gd(g | 0, H() | 0, 1, 0) | 0;\r\n                j = H() | 0;\r\n              } else {\r\n                g = k;\r\n                j = l;\r\n              }\r\n              n = m + -1 | 0;\r\n              n = Md(k | 0, l | 0, n | 0, ((n | 0) < 0) << 31 >> 31 | 0) | 0;\r\n              n = Gd(g | 0, j | 0, n | 0, H() | 0) | 0;\r\n              m = H() | 0;\r\n              l = e;\r\n              l = Gd(n | 0, m | 0, b[l >> 2] | 0, b[l + 4 >> 2] | 0) | 0;\r\n              m = H() | 0;\r\n              n = e;\r\n              b[n >> 2] = l;\r\n              b[n + 4 >> 2] = m;\r\n            }\r\n            if ((o | 0) <= (d | 0)) {\r\n              k = 37;\r\n              break;\r\n            }\r\n          }\r\n          if ((k | 0) == 19) { I(27795, 27122, 1276, 27158); }else if ((k | 0) == 37) {\r\n            h = e;\r\n            f = b[h + 4 >> 2] | 0;\r\n            h = b[h >> 2] | 0;\r\n            break;\r\n          } else if ((k | 0) == 42) {\r\n            T = t;\r\n            return f | 0;\r\n          }\r\n        } else {\r\n          f = 0;\r\n          h = 0;\r\n        }\r\n      } else { k = 33; } } while (0);\r\n      c: do { if ((k | 0) == 33) {\r\n        p = e;\r\n        b[p >> 2] = 0;\r\n        b[p + 4 >> 2] = 0;\r\n        if ((s | 0) > (d | 0)) {\r\n          g = s;\r\n          while (1) {\r\n            f = Qd(a | 0, c | 0, (15 - g | 0) * 3 | 0) | 0;\r\n            H() | 0;\r\n            f = f & 7;\r\n            if ((f | 0) == 7) {\r\n              f = 5;\r\n              break;\r\n            }\r\n            h = s - g | 0;\r\n            h = Oc(7, 0, h, ((h | 0) < 0) << 31 >> 31) | 0;\r\n            f = Md(h | 0, H() | 0, f | 0, 0) | 0;\r\n            h = H() | 0;\r\n            p = e;\r\n            h = Gd(b[p >> 2] | 0, b[p + 4 >> 2] | 0, f | 0, h | 0) | 0;\r\n            f = H() | 0;\r\n            p = e;\r\n            b[p >> 2] = h;\r\n            b[p + 4 >> 2] = f;\r\n            g = g + -1 | 0;\r\n            if ((g | 0) <= (d | 0)) { break c; }\r\n          }\r\n          T = t;\r\n          return f | 0;\r\n        } else {\r\n          f = 0;\r\n          h = 0;\r\n        }\r\n      } } while (0);\r\n      if (Gb(q, i, s, r) | 0) { I(27795, 27122, 1236, 27173); }\r\n      s = r;\r\n      r = b[s + 4 >> 2] | 0;\r\n      if (((f | 0) > -1 | (f | 0) == -1 & h >>> 0 > 4294967295) & ((r | 0) > (f | 0) | ((r | 0) == (f | 0) ? (b[s >> 2] | 0) >>> 0 > h >>> 0 : 0))) {\r\n        s = 0;\r\n        T = t;\r\n        return s | 0;\r\n      } else { I(27795, 27122, 1316, 27158); }\r\n      return 0;\r\n    }\r\n    function ec(a, c, d, e, f, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0;\r\n      m = T;\r\n      T = T + 16 | 0;\r\n      h = m;\r\n      if (f >>> 0 > 15) {\r\n        g = 4;\r\n        T = m;\r\n        return g | 0;\r\n      }\r\n      i = Qd(d | 0, e | 0, 52) | 0;\r\n      H() | 0;\r\n      i = i & 15;\r\n      if ((i | 0) > (f | 0)) {\r\n        g = 12;\r\n        T = m;\r\n        return g | 0;\r\n      }\r\n      if (Gb(d, e, f, h) | 0) { I(27795, 27122, 1236, 27173); }\r\n      l = h;\r\n      k = b[l + 4 >> 2] | 0;\r\n      if (!(((c | 0) > -1 | (c | 0) == -1 & a >>> 0 > 4294967295) & ((k | 0) > (c | 0) | ((k | 0) == (c | 0) ? (b[l >> 2] | 0) >>> 0 > a >>> 0 : 0)))) {\r\n        g = 2;\r\n        T = m;\r\n        return g | 0;\r\n      }\r\n      l = f - i | 0;\r\n      f = Rd(f | 0, 0, 52) | 0;\r\n      j = H() | 0 | e & -15728641;\r\n      k = g;\r\n      b[k >> 2] = f | d;\r\n      b[k + 4 >> 2] = j;\r\n      k = Qd(d | 0, e | 0, 45) | 0;\r\n      H() | 0;\r\n      a: do { if (oa(k & 127) | 0) {\r\n        if (i | 0) {\r\n          h = 1;\r\n          while (1) {\r\n            k = Rd(7, 0, (15 - h | 0) * 3 | 0) | 0;\r\n            if (!((k & d | 0) == 0 & ((H() | 0) & e | 0) == 0)) { break a; }\r\n            if (h >>> 0 < i >>> 0) { h = h + 1 | 0; }else { break; }\r\n          }\r\n        }\r\n        if ((l | 0) < 1) {\r\n          g = 0;\r\n          T = m;\r\n          return g | 0;\r\n        }\r\n        k = i ^ 15;\r\n        e = -1;\r\n        j = 1;\r\n        h = 1;\r\n        while (1) {\r\n          i = l - j | 0;\r\n          i = Oc(7, 0, i, ((i | 0) < 0) << 31 >> 31) | 0;\r\n          d = H() | 0;\r\n          do { if (h) {\r\n            h = Md(i | 0, d | 0, 5, 0) | 0;\r\n            h = Gd(h | 0, H() | 0, -5, -1) | 0;\r\n            h = Kd(h | 0, H() | 0, 6, 0) | 0;\r\n            f = H() | 0;\r\n            if ((c | 0) > (f | 0) | (c | 0) == (f | 0) & a >>> 0 > h >>> 0) {\r\n              c = Gd(a | 0, c | 0, -1, -1) | 0;\r\n              c = Hd(c | 0, H() | 0, h | 0, f | 0) | 0;\r\n              h = H() | 0;\r\n              n = g;\r\n              p = b[n >> 2] | 0;\r\n              n = b[n + 4 >> 2] | 0;\r\n              q = (k + e | 0) * 3 | 0;\r\n              o = Rd(7, 0, q | 0) | 0;\r\n              n = n & ~(H() | 0);\r\n              e = Kd(c | 0, h | 0, i | 0, d | 0) | 0;\r\n              a = H() | 0;\r\n              f = Gd(e | 0, a | 0, 2, 0) | 0;\r\n              q = Rd(f | 0, H() | 0, q | 0) | 0;\r\n              n = H() | 0 | n;\r\n              f = g;\r\n              b[f >> 2] = q | p & ~o;\r\n              b[f + 4 >> 2] = n;\r\n              a = Md(e | 0, a | 0, i | 0, d | 0) | 0;\r\n              a = Hd(c | 0, h | 0, a | 0, H() | 0) | 0;\r\n              h = 0;\r\n              c = H() | 0;\r\n              break;\r\n            } else {\r\n              q = g;\r\n              o = b[q >> 2] | 0;\r\n              q = b[q + 4 >> 2] | 0;\r\n              p = Rd(7, 0, (k + e | 0) * 3 | 0) | 0;\r\n              q = q & ~(H() | 0);\r\n              h = g;\r\n              b[h >> 2] = o & ~p;\r\n              b[h + 4 >> 2] = q;\r\n              h = 1;\r\n              break;\r\n            }\r\n          } else {\r\n            o = g;\r\n            f = b[o >> 2] | 0;\r\n            o = b[o + 4 >> 2] | 0;\r\n            e = (k + e | 0) * 3 | 0;\r\n            n = Rd(7, 0, e | 0) | 0;\r\n            o = o & ~(H() | 0);\r\n            q = Kd(a | 0, c | 0, i | 0, d | 0) | 0;\r\n            h = H() | 0;\r\n            e = Rd(q | 0, h | 0, e | 0) | 0;\r\n            o = H() | 0 | o;\r\n            p = g;\r\n            b[p >> 2] = e | f & ~n;\r\n            b[p + 4 >> 2] = o;\r\n            h = Md(q | 0, h | 0, i | 0, d | 0) | 0;\r\n            a = Hd(a | 0, c | 0, h | 0, H() | 0) | 0;\r\n            h = 0;\r\n            c = H() | 0;\r\n          } } while (0);\r\n          if ((l | 0) > (j | 0)) {\r\n            e = ~j;\r\n            j = j + 1 | 0;\r\n          } else {\r\n            c = 0;\r\n            break;\r\n          }\r\n        }\r\n        T = m;\r\n        return c | 0;\r\n      } } while (0);\r\n      if ((l | 0) < 1) {\r\n        q = 0;\r\n        T = m;\r\n        return q | 0;\r\n      }\r\n      f = i ^ 15;\r\n      h = 1;\r\n      while (1) {\r\n        p = l - h | 0;\r\n        p = Oc(7, 0, p, ((p | 0) < 0) << 31 >> 31) | 0;\r\n        q = H() | 0;\r\n        j = g;\r\n        d = b[j >> 2] | 0;\r\n        j = b[j + 4 >> 2] | 0;\r\n        i = (f - h | 0) * 3 | 0;\r\n        e = Rd(7, 0, i | 0) | 0;\r\n        j = j & ~(H() | 0);\r\n        n = Kd(a | 0, c | 0, p | 0, q | 0) | 0;\r\n        o = H() | 0;\r\n        i = Rd(n | 0, o | 0, i | 0) | 0;\r\n        j = H() | 0 | j;\r\n        k = g;\r\n        b[k >> 2] = i | d & ~e;\r\n        b[k + 4 >> 2] = j;\r\n        q = Md(n | 0, o | 0, p | 0, q | 0) | 0;\r\n        a = Hd(a | 0, c | 0, q | 0, H() | 0) | 0;\r\n        c = H() | 0;\r\n        if ((l | 0) <= (h | 0)) {\r\n          c = 0;\r\n          break;\r\n        } else { h = h + 1 | 0; }\r\n      }\r\n      T = m;\r\n      return c | 0;\r\n    }\r\n    function fc(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      f = Qd(c | 0, d | 0, 52) | 0;\r\n      H() | 0;\r\n      f = f & 15;\r\n      if ((c | 0) == 0 & (d | 0) == 0 | ((e | 0) > 15 | (f | 0) > (e | 0))) {\r\n        g = -1;\r\n        c = -1;\r\n        d = 0;\r\n        f = 0;\r\n      } else {\r\n        c = Jb(c, d, f + 1 | 0, e) | 0;\r\n        h = (H() | 0) & -15728641;\r\n        d = Rd(e | 0, 0, 52) | 0;\r\n        d = c | d;\r\n        h = h | (H() | 0);\r\n        c = (Hb(d, h) | 0) == 0;\r\n        g = f;\r\n        c = c ? -1 : e;\r\n        f = h;\r\n      }\r\n      h = a;\r\n      b[h >> 2] = d;\r\n      b[h + 4 >> 2] = f;\r\n      b[a + 8 >> 2] = g;\r\n      b[a + 12 >> 2] = c;\r\n      return;\r\n    }\r\n    function gc(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0;\r\n      f = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      f = f & 15;\r\n      g = e + 8 | 0;\r\n      b[g >> 2] = f;\r\n      if ((a | 0) == 0 & (c | 0) == 0 | ((d | 0) > 15 | (f | 0) > (d | 0))) {\r\n        d = e;\r\n        b[d >> 2] = 0;\r\n        b[d + 4 >> 2] = 0;\r\n        b[g >> 2] = -1;\r\n        b[e + 12 >> 2] = -1;\r\n        return;\r\n      }\r\n      a = Jb(a, c, f + 1 | 0, d) | 0;\r\n      g = (H() | 0) & -15728641;\r\n      f = Rd(d | 0, 0, 52) | 0;\r\n      f = a | f;\r\n      g = g | (H() | 0);\r\n      a = e;\r\n      b[a >> 2] = f;\r\n      b[a + 4 >> 2] = g;\r\n      a = e + 12 | 0;\r\n      if (!(Hb(f, g) | 0)) {\r\n        b[a >> 2] = -1;\r\n        return;\r\n      } else {\r\n        b[a >> 2] = d;\r\n        return;\r\n      }\r\n    }\r\n    function hc(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      d = a;\r\n      c = b[d >> 2] | 0;\r\n      d = b[d + 4 >> 2] | 0;\r\n      if ((c | 0) == 0 & (d | 0) == 0) { return; }\r\n      e = Qd(c | 0, d | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      i = Rd(1, 0, (e ^ 15) * 3 | 0) | 0;\r\n      c = Gd(i | 0, H() | 0, c | 0, d | 0) | 0;\r\n      d = H() | 0;\r\n      i = a;\r\n      b[i >> 2] = c;\r\n      b[i + 4 >> 2] = d;\r\n      i = a + 8 | 0;\r\n      h = b[i >> 2] | 0;\r\n      if ((e | 0) < (h | 0)) { return; }\r\n      j = a + 12 | 0;\r\n      g = e;\r\n      while (1) {\r\n        if ((g | 0) == (h | 0)) {\r\n          e = 5;\r\n          break;\r\n        }\r\n        k = (g | 0) == (b[j >> 2] | 0);\r\n        f = (15 - g | 0) * 3 | 0;\r\n        e = Qd(c | 0, d | 0, f | 0) | 0;\r\n        H() | 0;\r\n        e = e & 7;\r\n        if (k & ((e | 0) == 1 & 0 == 0)) {\r\n          e = 7;\r\n          break;\r\n        }\r\n        if (!((e | 0) == 7 & 0 == 0)) {\r\n          e = 10;\r\n          break;\r\n        }\r\n        k = Rd(1, 0, f | 0) | 0;\r\n        c = Gd(c | 0, d | 0, k | 0, H() | 0) | 0;\r\n        d = H() | 0;\r\n        k = a;\r\n        b[k >> 2] = c;\r\n        b[k + 4 >> 2] = d;\r\n        if ((g | 0) > (h | 0)) { g = g + -1 | 0; }else {\r\n          e = 10;\r\n          break;\r\n        }\r\n      }\r\n      if ((e | 0) == 5) {\r\n        k = a;\r\n        b[k >> 2] = 0;\r\n        b[k + 4 >> 2] = 0;\r\n        b[i >> 2] = -1;\r\n        b[j >> 2] = -1;\r\n        return;\r\n      } else if ((e | 0) == 7) {\r\n        h = Rd(1, 0, f | 0) | 0;\r\n        h = Gd(c | 0, d | 0, h | 0, H() | 0) | 0;\r\n        i = H() | 0;\r\n        k = a;\r\n        b[k >> 2] = h;\r\n        b[k + 4 >> 2] = i;\r\n        b[j >> 2] = g + -1;\r\n        return;\r\n      } else if ((e | 0) == 10) { return; }\r\n    }\r\n    function ic(a) {\r\n      a = +a;\r\n      var b = 0.0;\r\n      b = a < 0.0 ? a + 6.283185307179586 : a;\r\n      return +(!(a >= 6.283185307179586) ? b : b + -6.283185307179586);\r\n    }\r\n    function jc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (!(+q(+(+e[a >> 3] - +e[b >> 3])) < 1.7453292519943298e-11)) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      b = +q(+(+e[a + 8 >> 3] - +e[b + 8 >> 3])) < 1.7453292519943298e-11;\r\n      return b | 0;\r\n    }\r\n    function kc(a, b) {\r\n      a = +a;\r\n      b = b | 0;\r\n      switch (b | 0) {\r\n        case 1:\r\n          {\r\n            a = a < 0.0 ? a + 6.283185307179586 : a;\r\n            break;\r\n          }\r\n        case 2:\r\n          {\r\n            a = a > 0.0 ? a + -6.283185307179586 : a;\r\n            break;\r\n          }\r\n        default:\r\n\r\n      }\r\n      return +a;\r\n    }\r\n    function lc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0;\r\n      f = +e[b >> 3];\r\n      d = +e[a >> 3];\r\n      g = +u(+((f - d) * .5));\r\n      c = +u(+((+e[b + 8 >> 3] - +e[a + 8 >> 3]) * .5));\r\n      c = g * g + c * (+t(+f) * +t(+d) * c);\r\n      return +(+z(+ +r(+c), + +r(+(1.0 - c))) * 2.0);\r\n    }\r\n    function mc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0;\r\n      f = +e[b >> 3];\r\n      d = +e[a >> 3];\r\n      g = +u(+((f - d) * .5));\r\n      c = +u(+((+e[b + 8 >> 3] - +e[a + 8 >> 3]) * .5));\r\n      c = g * g + c * (+t(+f) * +t(+d) * c);\r\n      return +(+z(+ +r(+c), + +r(+(1.0 - c))) * 2.0 * 6371.007180918475);\r\n    }\r\n    function nc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0;\r\n      f = +e[b >> 3];\r\n      d = +e[a >> 3];\r\n      g = +u(+((f - d) * .5));\r\n      c = +u(+((+e[b + 8 >> 3] - +e[a + 8 >> 3]) * .5));\r\n      c = g * g + c * (+t(+f) * +t(+d) * c);\r\n      return +(+z(+ +r(+c), + +r(+(1.0 - c))) * 2.0 * 6371.007180918475 * 1.0e3);\r\n    }\r\n    function oc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0;\r\n      g = +e[b >> 3];\r\n      d = +t(+g);\r\n      f = +e[b + 8 >> 3] - +e[a + 8 >> 3];\r\n      h = d * +u(+f);\r\n      c = +e[a >> 3];\r\n      return + +z(+h, +(+u(+g) * +t(+c) - +t(+f) * (d * +u(+c))));\r\n    }\r\n    function pc(a, c, d, f) {\r\n      a = a | 0;\r\n      c = +c;\r\n      d = +d;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0;\r\n      if (d < 1.0e-16) {\r\n        b[f >> 2] = b[a >> 2];\r\n        b[f + 4 >> 2] = b[a + 4 >> 2];\r\n        b[f + 8 >> 2] = b[a + 8 >> 2];\r\n        b[f + 12 >> 2] = b[a + 12 >> 2];\r\n        return;\r\n      }\r\n      h = c < 0.0 ? c + 6.283185307179586 : c;\r\n      h = !(c >= 6.283185307179586) ? h : h + -6.283185307179586;\r\n      do { if (h < 1.0e-16) {\r\n        c = +e[a >> 3] + d;\r\n        e[f >> 3] = c;\r\n        g = f;\r\n      } else {\r\n        g = +q(+(h + -3.141592653589793)) < 1.0e-16;\r\n        c = +e[a >> 3];\r\n        if (g) {\r\n          c = c - d;\r\n          e[f >> 3] = c;\r\n          g = f;\r\n          break;\r\n        }\r\n        i = +t(+d);\r\n        d = +u(+d);\r\n        c = i * +u(+c) + +t(+h) * (d * +t(+c));\r\n        c = c > 1.0 ? 1.0 : c;\r\n        c = +x(+(c < -1.0 ? -1.0 : c));\r\n        e[f >> 3] = c;\r\n        if (+q(+(c + -1.5707963267948966)) < 1.0e-16) {\r\n          e[f >> 3] = 1.5707963267948966;\r\n          e[f + 8 >> 3] = 0.0;\r\n          return;\r\n        }\r\n        if (+q(+(c + 1.5707963267948966)) < 1.0e-16) {\r\n          e[f >> 3] = -1.5707963267948966;\r\n          e[f + 8 >> 3] = 0.0;\r\n          return;\r\n        }\r\n        j = 1.0 / +t(+c);\r\n        h = d * +u(+h) * j;\r\n        d = +e[a >> 3];\r\n        c = j * ((i - +u(+c) * +u(+d)) / +t(+d));\r\n        i = h > 1.0 ? 1.0 : h;\r\n        c = c > 1.0 ? 1.0 : c;\r\n        c = +e[a + 8 >> 3] + +z(+(i < -1.0 ? -1.0 : i), +(c < -1.0 ? -1.0 : c));\r\n        if (c > 3.141592653589793) { do { c = c + -6.283185307179586; } while (c > 3.141592653589793); }\r\n        if (c < -3.141592653589793) { do { c = c + 6.283185307179586; } while (c < -3.141592653589793); }\r\n        e[f + 8 >> 3] = c;\r\n        return;\r\n      } } while (0);\r\n      if (+q(+(c + -1.5707963267948966)) < 1.0e-16) {\r\n        e[g >> 3] = 1.5707963267948966;\r\n        e[f + 8 >> 3] = 0.0;\r\n        return;\r\n      }\r\n      if (+q(+(c + 1.5707963267948966)) < 1.0e-16) {\r\n        e[g >> 3] = -1.5707963267948966;\r\n        e[f + 8 >> 3] = 0.0;\r\n        return;\r\n      }\r\n      c = +e[a + 8 >> 3];\r\n      if (c > 3.141592653589793) { do { c = c + -6.283185307179586; } while (c > 3.141592653589793); }\r\n      if (c < -3.141592653589793) { do { c = c + 6.283185307179586; } while (c < -3.141592653589793); }\r\n      e[f + 8 >> 3] = c;\r\n      return;\r\n    }\r\n    function qc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (a >>> 0 > 15) {\r\n        b = 4;\r\n        return b | 0;\r\n      }\r\n      e[b >> 3] = +e[20656 + (a << 3) >> 3];\r\n      b = 0;\r\n      return b | 0;\r\n    }\r\n    function rc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (a >>> 0 > 15) {\r\n        b = 4;\r\n        return b | 0;\r\n      }\r\n      e[b >> 3] = +e[20784 + (a << 3) >> 3];\r\n      b = 0;\r\n      return b | 0;\r\n    }\r\n    function sc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (a >>> 0 > 15) {\r\n        b = 4;\r\n        return b | 0;\r\n      }\r\n      e[b >> 3] = +e[20912 + (a << 3) >> 3];\r\n      b = 0;\r\n      return b | 0;\r\n    }\r\n    function tc(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (a >>> 0 > 15) {\r\n        b = 4;\r\n        return b | 0;\r\n      }\r\n      e[b >> 3] = +e[21040 + (a << 3) >> 3];\r\n      b = 0;\r\n      return b | 0;\r\n    }\r\n    function uc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0;\r\n      if (a >>> 0 > 15) {\r\n        c = 4;\r\n        return c | 0;\r\n      }\r\n      d = Oc(7, 0, a, ((a | 0) < 0) << 31 >> 31) | 0;\r\n      d = Md(d | 0, H() | 0, 120, 0) | 0;\r\n      a = H() | 0;\r\n      b[c >> 2] = d | 2;\r\n      b[c + 4 >> 2] = a;\r\n      c = 0;\r\n      return c | 0;\r\n    }\r\n    function vc(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      var d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0,\r\n        n = 0.0;\r\n      n = +e[b >> 3];\r\n      l = +e[a >> 3];\r\n      j = +u(+((n - l) * .5));\r\n      g = +e[b + 8 >> 3];\r\n      k = +e[a + 8 >> 3];\r\n      h = +u(+((g - k) * .5));\r\n      i = +t(+l);\r\n      m = +t(+n);\r\n      h = j * j + h * (m * i * h);\r\n      h = +z(+ +r(+h), + +r(+(1.0 - h))) * 2.0;\r\n      j = +e[c >> 3];\r\n      n = +u(+((j - n) * .5));\r\n      d = +e[c + 8 >> 3];\r\n      g = +u(+((d - g) * .5));\r\n      f = +t(+j);\r\n      g = n * n + g * (m * f * g);\r\n      g = +z(+ +r(+g), + +r(+(1.0 - g))) * 2.0;\r\n      j = +u(+((l - j) * .5));\r\n      d = +u(+((k - d) * .5));\r\n      d = j * j + d * (i * f * d);\r\n      d = +z(+ +r(+d), + +r(+(1.0 - d))) * 2.0;\r\n      f = (h + g + d) * .5;\r\n      return +(+y(+ +r(+(+v(+(f * .5)) * +v(+((f - h) * .5)) * +v(+((f - g) * .5)) * +v(+((f - d) * .5))))) * 4.0);\r\n    }\r\n    function wc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 192 | 0;\r\n      h = j + 168 | 0;\r\n      i = j;\r\n      g = Zb(a, c, h) | 0;\r\n      if (g | 0) {\r\n        d = g;\r\n        T = j;\r\n        return d | 0;\r\n      }\r\n      if (_b(a, c, i) | 0) { I(27795, 27190, 415, 27199); }\r\n      c = b[i >> 2] | 0;\r\n      if ((c | 0) > 0) {\r\n        f = +vc(i + 8 | 0, i + 8 + (((c | 0) != 1 & 1) << 4) | 0, h) + 0.0;\r\n        if ((c | 0) != 1) {\r\n          a = 1;\r\n          do {\r\n            g = a;\r\n            a = a + 1 | 0;\r\n            f = f + +vc(i + 8 + (g << 4) | 0, i + 8 + (((a | 0) % (c | 0) | 0) << 4) | 0, h);\r\n          } while ((a | 0) < (c | 0));\r\n        }\r\n      } else { f = 0.0; }\r\n      e[d >> 3] = f;\r\n      d = 0;\r\n      T = j;\r\n      return d | 0;\r\n    }\r\n    function xc(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      a = wc(a, b, c) | 0;\r\n      if (a | 0) { return a | 0; }\r\n      e[c >> 3] = +e[c >> 3] * 6371.007180918475 * 6371.007180918475;\r\n      return a | 0;\r\n    }\r\n    function yc(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      a = wc(a, b, c) | 0;\r\n      if (a | 0) { return a | 0; }\r\n      e[c >> 3] = +e[c >> 3] * 6371.007180918475 * 6371.007180918475 * 1.0e3 * 1.0e3;\r\n      return a | 0;\r\n    }\r\n    function zc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0;\r\n      j = T;\r\n      T = T + 176 | 0;\r\n      i = j;\r\n      a = pb(a, c, i) | 0;\r\n      if (a | 0) {\r\n        i = a;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      e[d >> 3] = 0.0;\r\n      a = b[i >> 2] | 0;\r\n      if ((a | 0) <= 1) {\r\n        i = 0;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      c = a + -1 | 0;\r\n      a = 0;\r\n      f = +e[i + 8 >> 3];\r\n      g = +e[i + 16 >> 3];\r\n      h = 0.0;\r\n      do {\r\n        a = a + 1 | 0;\r\n        l = f;\r\n        f = +e[i + 8 + (a << 4) >> 3];\r\n        m = +u(+((f - l) * .5));\r\n        k = g;\r\n        g = +e[i + 8 + (a << 4) + 8 >> 3];\r\n        k = +u(+((g - k) * .5));\r\n        k = m * m + k * (+t(+f) * +t(+l) * k);\r\n        h = h + +z(+ +r(+k), + +r(+(1.0 - k))) * 2.0;\r\n      } while ((a | 0) < (c | 0));\r\n      e[d >> 3] = h;\r\n      i = 0;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Ac(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0;\r\n      j = T;\r\n      T = T + 176 | 0;\r\n      i = j;\r\n      a = pb(a, c, i) | 0;\r\n      if (a | 0) {\r\n        i = a;\r\n        h = +e[d >> 3];\r\n        h = h * 6371.007180918475;\r\n        e[d >> 3] = h;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      e[d >> 3] = 0.0;\r\n      a = b[i >> 2] | 0;\r\n      if ((a | 0) <= 1) {\r\n        i = 0;\r\n        h = 0.0;\r\n        h = h * 6371.007180918475;\r\n        e[d >> 3] = h;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      c = a + -1 | 0;\r\n      a = 0;\r\n      f = +e[i + 8 >> 3];\r\n      g = +e[i + 16 >> 3];\r\n      h = 0.0;\r\n      do {\r\n        a = a + 1 | 0;\r\n        l = f;\r\n        f = +e[i + 8 + (a << 4) >> 3];\r\n        m = +u(+((f - l) * .5));\r\n        k = g;\r\n        g = +e[i + 8 + (a << 4) + 8 >> 3];\r\n        k = +u(+((g - k) * .5));\r\n        k = m * m + k * (+t(+l) * +t(+f) * k);\r\n        h = h + +z(+ +r(+k), + +r(+(1.0 - k))) * 2.0;\r\n      } while ((a | 0) != (c | 0));\r\n      e[d >> 3] = h;\r\n      i = 0;\r\n      m = h;\r\n      m = m * 6371.007180918475;\r\n      e[d >> 3] = m;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Bc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0;\r\n      j = T;\r\n      T = T + 176 | 0;\r\n      i = j;\r\n      a = pb(a, c, i) | 0;\r\n      if (a | 0) {\r\n        i = a;\r\n        h = +e[d >> 3];\r\n        h = h * 6371.007180918475;\r\n        h = h * 1.0e3;\r\n        e[d >> 3] = h;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      e[d >> 3] = 0.0;\r\n      a = b[i >> 2] | 0;\r\n      if ((a | 0) <= 1) {\r\n        i = 0;\r\n        h = 0.0;\r\n        h = h * 6371.007180918475;\r\n        h = h * 1.0e3;\r\n        e[d >> 3] = h;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      c = a + -1 | 0;\r\n      a = 0;\r\n      f = +e[i + 8 >> 3];\r\n      g = +e[i + 16 >> 3];\r\n      h = 0.0;\r\n      do {\r\n        a = a + 1 | 0;\r\n        l = f;\r\n        f = +e[i + 8 + (a << 4) >> 3];\r\n        m = +u(+((f - l) * .5));\r\n        k = g;\r\n        g = +e[i + 8 + (a << 4) + 8 >> 3];\r\n        k = +u(+((g - k) * .5));\r\n        k = m * m + k * (+t(+l) * +t(+f) * k);\r\n        h = h + +z(+ +r(+k), + +r(+(1.0 - k))) * 2.0;\r\n      } while ((a | 0) != (c | 0));\r\n      e[d >> 3] = h;\r\n      i = 0;\r\n      m = h;\r\n      m = m * 6371.007180918475;\r\n      m = m * 1.0e3;\r\n      e[d >> 3] = m;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Cc(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      c = Fd(1, 12) | 0;\r\n      if (!c) { I(27280, 27235, 49, 27293); }\r\n      d = a + 4 | 0;\r\n      e = b[d >> 2] | 0;\r\n      if (e | 0) {\r\n        e = e + 8 | 0;\r\n        b[e >> 2] = c;\r\n        b[d >> 2] = c;\r\n        return c | 0;\r\n      }\r\n      if (b[a >> 2] | 0) { I(27310, 27235, 61, 27333); }\r\n      e = a;\r\n      b[e >> 2] = c;\r\n      b[d >> 2] = c;\r\n      return c | 0;\r\n    }\r\n    function Dc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0;\r\n      e = Dd(24) | 0;\r\n      if (!e) { I(27347, 27235, 78, 27361); }\r\n      b[e >> 2] = b[c >> 2];\r\n      b[e + 4 >> 2] = b[c + 4 >> 2];\r\n      b[e + 8 >> 2] = b[c + 8 >> 2];\r\n      b[e + 12 >> 2] = b[c + 12 >> 2];\r\n      b[e + 16 >> 2] = 0;\r\n      c = a + 4 | 0;\r\n      d = b[c >> 2] | 0;\r\n      if (d | 0) {\r\n        b[d + 16 >> 2] = e;\r\n        b[c >> 2] = e;\r\n        return e | 0;\r\n      }\r\n      if (b[a >> 2] | 0) { I(27376, 27235, 82, 27361); }\r\n      b[a >> 2] = e;\r\n      b[c >> 2] = e;\r\n      return e | 0;\r\n    }\r\n    function Ec(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0;\r\n      if (!a) { return; }\r\n      e = 1;\r\n      while (1) {\r\n        c = b[a >> 2] | 0;\r\n        if (c | 0) { do {\r\n          d = b[c >> 2] | 0;\r\n          if (d | 0) { do {\r\n            f = d;\r\n            d = b[d + 16 >> 2] | 0;\r\n            Ed(f);\r\n          } while ((d | 0) != 0); }\r\n          f = c;\r\n          c = b[c + 8 >> 2] | 0;\r\n          Ed(f);\r\n        } while ((c | 0) != 0); }\r\n        c = a;\r\n        a = b[a + 8 >> 2] | 0;\r\n        if (!e) { Ed(c); }\r\n        if (!a) { break; }else { e = 0; }\r\n      }\r\n      return;\r\n    }\r\n    function Fc(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0.0,\r\n        i = 0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        r = 0,\r\n        s = 0.0,\r\n        t = 0.0,\r\n        u = 0.0,\r\n        v = 0.0,\r\n        w = 0.0,\r\n        x = 0.0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0,\r\n        B = 0,\r\n        C = 0,\r\n        D = 0,\r\n        E = 0,\r\n        F = 0,\r\n        G = 0,\r\n        H = 0,\r\n        J = 0,\r\n        K = 0;\r\n      g = a + 8 | 0;\r\n      if (b[g >> 2] | 0) {\r\n        K = 1;\r\n        return K | 0;\r\n      }\r\n      f = b[a >> 2] | 0;\r\n      if (!f) {\r\n        K = 0;\r\n        return K | 0;\r\n      }\r\n      c = f;\r\n      d = 0;\r\n      do {\r\n        d = d + 1 | 0;\r\n        c = b[c + 8 >> 2] | 0;\r\n      } while ((c | 0) != 0);\r\n      if (d >>> 0 < 2) {\r\n        K = 0;\r\n        return K | 0;\r\n      }\r\n      H = Dd(d << 2) | 0;\r\n      if (!H) { I(27396, 27235, 317, 27415); }\r\n      G = Dd(d << 5) | 0;\r\n      if (!G) { I(27437, 27235, 321, 27415); }\r\n      b[a >> 2] = 0;\r\n      z = a + 4 | 0;\r\n      b[z >> 2] = 0;\r\n      b[g >> 2] = 0;\r\n      d = 0;\r\n      F = 0;\r\n      y = 0;\r\n      n = 0;\r\n      a: while (1) {\r\n        m = b[f >> 2] | 0;\r\n        if (m) {\r\n          h = 0.0;\r\n          i = m;\r\n          do {\r\n            k = +e[i + 8 >> 3];\r\n            c = i;\r\n            i = b[i + 16 >> 2] | 0;\r\n            l = (i | 0) == 0;\r\n            g = l ? m : i;\r\n            j = +e[g + 8 >> 3];\r\n            if (+q(+(k - j)) > 3.141592653589793) {\r\n              K = 14;\r\n              break;\r\n            }\r\n            h = h + (j - k) * (+e[c >> 3] + +e[g >> 3]);\r\n          } while (!l);\r\n          if ((K | 0) == 14) {\r\n            K = 0;\r\n            h = 0.0;\r\n            c = m;\r\n            do {\r\n              x = +e[c + 8 >> 3];\r\n              E = c + 16 | 0;\r\n              D = b[E >> 2] | 0;\r\n              D = (D | 0) == 0 ? m : D;\r\n              w = +e[D + 8 >> 3];\r\n              h = h + (+e[c >> 3] + +e[D >> 3]) * ((w < 0.0 ? w + 6.283185307179586 : w) - (x < 0.0 ? x + 6.283185307179586 : x));\r\n              c = b[((c | 0) == 0 ? f : E) >> 2] | 0;\r\n            } while ((c | 0) != 0);\r\n          }\r\n          if (h > 0.0) {\r\n            b[H + (F << 2) >> 2] = f;\r\n            F = F + 1 | 0;\r\n            g = y;\r\n            c = n;\r\n          } else { K = 19; }\r\n        } else { K = 19; }\r\n        if ((K | 0) == 19) {\r\n          K = 0;\r\n          do { if (!d) {\r\n            if (!n) {\r\n              if (!(b[a >> 2] | 0)) {\r\n                g = z;\r\n                i = a;\r\n                c = f;\r\n                d = a;\r\n                break;\r\n              } else {\r\n                K = 27;\r\n                break a;\r\n              }\r\n            } else {\r\n              g = z;\r\n              i = n + 8 | 0;\r\n              c = f;\r\n              d = a;\r\n              break;\r\n            }\r\n          } else {\r\n            c = d + 8 | 0;\r\n            if (b[c >> 2] | 0) {\r\n              K = 21;\r\n              break a;\r\n            }\r\n            d = Fd(1, 12) | 0;\r\n            if (!d) {\r\n              K = 23;\r\n              break a;\r\n            }\r\n            b[c >> 2] = d;\r\n            g = d + 4 | 0;\r\n            i = d;\r\n            c = n;\r\n          } } while (0);\r\n          b[i >> 2] = f;\r\n          b[g >> 2] = f;\r\n          i = G + (y << 5) | 0;\r\n          l = b[f >> 2] | 0;\r\n          if (l) {\r\n            m = G + (y << 5) + 8 | 0;\r\n            e[m >> 3] = 1797693134862315708145274.0e284;\r\n            n = G + (y << 5) + 24 | 0;\r\n            e[n >> 3] = 1797693134862315708145274.0e284;\r\n            e[i >> 3] = -1797693134862315708145274.0e284;\r\n            o = G + (y << 5) + 16 | 0;\r\n            e[o >> 3] = -1797693134862315708145274.0e284;\r\n            u = 1797693134862315708145274.0e284;\r\n            v = -1797693134862315708145274.0e284;\r\n            g = 0;\r\n            p = l;\r\n            k = 1797693134862315708145274.0e284;\r\n            s = 1797693134862315708145274.0e284;\r\n            t = -1797693134862315708145274.0e284;\r\n            j = -1797693134862315708145274.0e284;\r\n            while (1) {\r\n              h = +e[p >> 3];\r\n              x = +e[p + 8 >> 3];\r\n              p = b[p + 16 >> 2] | 0;\r\n              r = (p | 0) == 0;\r\n              w = +e[(r ? l : p) + 8 >> 3];\r\n              if (h < k) {\r\n                e[m >> 3] = h;\r\n                k = h;\r\n              }\r\n              if (x < s) {\r\n                e[n >> 3] = x;\r\n                s = x;\r\n              }\r\n              if (h > t) { e[i >> 3] = h; }else { h = t; }\r\n              if (x > j) {\r\n                e[o >> 3] = x;\r\n                j = x;\r\n              }\r\n              u = x > 0.0 & x < u ? x : u;\r\n              v = x < 0.0 & x > v ? x : v;\r\n              g = g | +q(+(x - w)) > 3.141592653589793;\r\n              if (r) { break; }else { t = h; }\r\n            }\r\n            if (g) {\r\n              e[o >> 3] = v;\r\n              e[n >> 3] = u;\r\n            }\r\n          } else {\r\n            b[i >> 2] = 0;\r\n            b[i + 4 >> 2] = 0;\r\n            b[i + 8 >> 2] = 0;\r\n            b[i + 12 >> 2] = 0;\r\n            b[i + 16 >> 2] = 0;\r\n            b[i + 20 >> 2] = 0;\r\n            b[i + 24 >> 2] = 0;\r\n            b[i + 28 >> 2] = 0;\r\n          }\r\n          g = y + 1 | 0;\r\n        }\r\n        E = f + 8 | 0;\r\n        f = b[E >> 2] | 0;\r\n        b[E >> 2] = 0;\r\n        if (!f) {\r\n          K = 45;\r\n          break;\r\n        } else {\r\n          y = g;\r\n          n = c;\r\n        }\r\n      }\r\n      if ((K | 0) == 21) { I(27213, 27235, 35, 27247); }else if ((K | 0) == 23) { I(27267, 27235, 37, 27247); }else if ((K | 0) == 27) { I(27310, 27235, 61, 27333); }else if ((K | 0) == 45) {\r\n        b: do { if ((F | 0) > 0) {\r\n          E = (g | 0) == 0;\r\n          C = g << 2;\r\n          D = (a | 0) == 0;\r\n          B = 0;\r\n          c = 0;\r\n          while (1) {\r\n            A = b[H + (B << 2) >> 2] | 0;\r\n            if (!E) {\r\n              y = Dd(C) | 0;\r\n              if (!y) {\r\n                K = 50;\r\n                break;\r\n              }\r\n              z = Dd(C) | 0;\r\n              if (!z) {\r\n                K = 52;\r\n                break;\r\n              }\r\n              c: do { if (!D) {\r\n                g = 0;\r\n                d = 0;\r\n                i = a;\r\n                while (1) {\r\n                  f = G + (g << 5) | 0;\r\n                  if (Gc(b[i >> 2] | 0, f, b[A >> 2] | 0) | 0) {\r\n                    b[y + (d << 2) >> 2] = i;\r\n                    b[z + (d << 2) >> 2] = f;\r\n                    r = d + 1 | 0;\r\n                  } else { r = d; }\r\n                  i = b[i + 8 >> 2] | 0;\r\n                  if (!i) { break; }else {\r\n                    g = g + 1 | 0;\r\n                    d = r;\r\n                  }\r\n                }\r\n                if ((r | 0) > 0) {\r\n                  f = b[y >> 2] | 0;\r\n                  if ((r | 0) == 1) { d = f; }else {\r\n                    o = 0;\r\n                    p = -1;\r\n                    d = f;\r\n                    n = f;\r\n                    while (1) {\r\n                      l = b[n >> 2] | 0;\r\n                      f = 0;\r\n                      i = 0;\r\n                      while (1) {\r\n                        g = b[b[y + (i << 2) >> 2] >> 2] | 0;\r\n                        if ((g | 0) == (l | 0)) { m = f; }else { m = f + ((Gc(g, b[z + (i << 2) >> 2] | 0, b[l >> 2] | 0) | 0) & 1) | 0; }\r\n                        i = i + 1 | 0;\r\n                        if ((i | 0) == (r | 0)) { break; }else { f = m; }\r\n                      }\r\n                      g = (m | 0) > (p | 0);\r\n                      d = g ? n : d;\r\n                      f = o + 1 | 0;\r\n                      if ((f | 0) == (r | 0)) { break c; }\r\n                      o = f;\r\n                      p = g ? m : p;\r\n                      n = b[y + (f << 2) >> 2] | 0;\r\n                    }\r\n                  }\r\n                } else { d = 0; }\r\n              } else { d = 0; } } while (0);\r\n              Ed(y);\r\n              Ed(z);\r\n              if (d) {\r\n                g = d + 4 | 0;\r\n                f = b[g >> 2] | 0;\r\n                if (!f) {\r\n                  if (b[d >> 2] | 0) {\r\n                    K = 70;\r\n                    break;\r\n                  }\r\n                } else { d = f + 8 | 0; }\r\n                b[d >> 2] = A;\r\n                b[g >> 2] = A;\r\n              } else { K = 73; }\r\n            } else { K = 73; }\r\n            if ((K | 0) == 73) {\r\n              K = 0;\r\n              c = b[A >> 2] | 0;\r\n              if (c | 0) { do {\r\n                z = c;\r\n                c = b[c + 16 >> 2] | 0;\r\n                Ed(z);\r\n              } while ((c | 0) != 0); }\r\n              Ed(A);\r\n              c = 1;\r\n            }\r\n            B = B + 1 | 0;\r\n            if ((B | 0) >= (F | 0)) {\r\n              J = c;\r\n              break b;\r\n            }\r\n          }\r\n          if ((K | 0) == 50) { I(27452, 27235, 249, 27471); }else if ((K | 0) == 52) { I(27490, 27235, 252, 27471); }else if ((K | 0) == 70) { I(27310, 27235, 61, 27333); }\r\n        } else { J = 0; } } while (0);\r\n        Ed(H);\r\n        Ed(G);\r\n        K = J;\r\n        return K | 0;\r\n      }\r\n      return 0;\r\n    }\r\n    function Gc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0;\r\n      if (!(Ca(c, d) | 0)) {\r\n        a = 0;\r\n        return a | 0;\r\n      }\r\n      c = Aa(c) | 0;\r\n      f = +e[d >> 3];\r\n      g = +e[d + 8 >> 3];\r\n      g = c & g < 0.0 ? g + 6.283185307179586 : g;\r\n      a = b[a >> 2] | 0;\r\n      if (!a) {\r\n        a = 0;\r\n        return a | 0;\r\n      }\r\n      if (c) {\r\n        c = 0;\r\n        l = g;\r\n        d = a;\r\n        a: while (1) {\r\n          while (1) {\r\n            i = +e[d >> 3];\r\n            g = +e[d + 8 >> 3];\r\n            d = d + 16 | 0;\r\n            m = b[d >> 2] | 0;\r\n            m = (m | 0) == 0 ? a : m;\r\n            h = +e[m >> 3];\r\n            j = +e[m + 8 >> 3];\r\n            if (i > h) {\r\n              k = i;\r\n              i = j;\r\n            } else {\r\n              k = h;\r\n              h = i;\r\n              i = g;\r\n              g = j;\r\n            }\r\n            f = f == h | f == k ? f + 2.220446049250313e-16 : f;\r\n            if (!(f < h | f > k)) { break; }\r\n            d = b[d >> 2] | 0;\r\n            if (!d) {\r\n              d = 22;\r\n              break a;\r\n            }\r\n          }\r\n          j = i < 0.0 ? i + 6.283185307179586 : i;\r\n          i = g < 0.0 ? g + 6.283185307179586 : g;\r\n          l = j == l | i == l ? l + -2.220446049250313e-16 : l;\r\n          k = j + (i - j) * ((f - h) / (k - h));\r\n          if ((k < 0.0 ? k + 6.283185307179586 : k) > l) { c = c ^ 1; }\r\n          d = b[d >> 2] | 0;\r\n          if (!d) {\r\n            d = 22;\r\n            break;\r\n          }\r\n        }\r\n        if ((d | 0) == 22) { return c | 0; }\r\n      } else {\r\n        c = 0;\r\n        l = g;\r\n        d = a;\r\n        b: while (1) {\r\n          while (1) {\r\n            i = +e[d >> 3];\r\n            g = +e[d + 8 >> 3];\r\n            d = d + 16 | 0;\r\n            m = b[d >> 2] | 0;\r\n            m = (m | 0) == 0 ? a : m;\r\n            h = +e[m >> 3];\r\n            j = +e[m + 8 >> 3];\r\n            if (i > h) {\r\n              k = i;\r\n              i = j;\r\n            } else {\r\n              k = h;\r\n              h = i;\r\n              i = g;\r\n              g = j;\r\n            }\r\n            f = f == h | f == k ? f + 2.220446049250313e-16 : f;\r\n            if (!(f < h | f > k)) { break; }\r\n            d = b[d >> 2] | 0;\r\n            if (!d) {\r\n              d = 22;\r\n              break b;\r\n            }\r\n          }\r\n          l = i == l | g == l ? l + -2.220446049250313e-16 : l;\r\n          if (i + (g - i) * ((f - h) / (k - h)) > l) { c = c ^ 1; }\r\n          d = b[d >> 2] | 0;\r\n          if (!d) {\r\n            d = 22;\r\n            break;\r\n          }\r\n        }\r\n        if ((d | 0) == 22) { return c | 0; }\r\n      }\r\n      return 0;\r\n    }\r\n    function Hc(c, d, e, f, g) {\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0;\r\n      u = T;\r\n      T = T + 32 | 0;\r\n      t = u + 16 | 0;\r\n      s = u;\r\n      h = Qd(c | 0, d | 0, 52) | 0;\r\n      H() | 0;\r\n      h = h & 15;\r\n      p = Qd(e | 0, f | 0, 52) | 0;\r\n      H() | 0;\r\n      if ((h | 0) != (p & 15 | 0)) {\r\n        t = 12;\r\n        T = u;\r\n        return t | 0;\r\n      }\r\n      l = Qd(c | 0, d | 0, 45) | 0;\r\n      H() | 0;\r\n      l = l & 127;\r\n      m = Qd(e | 0, f | 0, 45) | 0;\r\n      H() | 0;\r\n      m = m & 127;\r\n      if (l >>> 0 > 121 | m >>> 0 > 121) {\r\n        t = 5;\r\n        T = u;\r\n        return t | 0;\r\n      }\r\n      p = (l | 0) != (m | 0);\r\n      if (p) {\r\n        j = wa(l, m) | 0;\r\n        if ((j | 0) == 7) {\r\n          t = 1;\r\n          T = u;\r\n          return t | 0;\r\n        }\r\n        k = wa(m, l) | 0;\r\n        if ((k | 0) == 7) { I(27514, 27538, 161, 27548); }else {\r\n          q = j;\r\n          i = k;\r\n        }\r\n      } else {\r\n        q = 0;\r\n        i = 0;\r\n      }\r\n      n = oa(l) | 0;\r\n      o = oa(m) | 0;\r\n      b[t >> 2] = 0;\r\n      b[t + 4 >> 2] = 0;\r\n      b[t + 8 >> 2] = 0;\r\n      b[t + 12 >> 2] = 0;\r\n      do { if (!q) {\r\n        Xb(e, f, t) | 0;\r\n        if ((n | 0) != 0 & (o | 0) != 0) {\r\n          if ((m | 0) != (l | 0)) { I(27621, 27538, 261, 27548); }\r\n          i = Pb(c, d) | 0;\r\n          h = Pb(e, f) | 0;\r\n          if (!((i | 0) == 7 | (h | 0) == 7)) {\r\n            if (!(a[22e3 + (i * 7 | 0) + h >> 0] | 0)) {\r\n              i = b[21168 + (i * 28 | 0) + (h << 2) >> 2] | 0;\r\n              if ((i | 0) > 0) {\r\n                j = t + 4 | 0;\r\n                h = 0;\r\n                do {\r\n                  _a(j);\r\n                  h = h + 1 | 0;\r\n                } while ((h | 0) != (i | 0));\r\n                r = 51;\r\n              } else { r = 51; }\r\n            } else { h = 1; }\r\n          } else { h = 5; }\r\n        } else { r = 51; }\r\n      } else {\r\n        m = b[4272 + (l * 28 | 0) + (q << 2) >> 2] | 0;\r\n        j = (m | 0) > 0;\r\n        if (!o) {\r\n          if (j) {\r\n            l = 0;\r\n            k = e;\r\n            j = f;\r\n            do {\r\n              k = Tb(k, j) | 0;\r\n              j = H() | 0;\r\n              i = ab(i) | 0;\r\n              l = l + 1 | 0;\r\n            } while ((l | 0) != (m | 0));\r\n            m = i;\r\n            l = k;\r\n            k = j;\r\n          } else {\r\n            m = i;\r\n            l = e;\r\n            k = f;\r\n          }\r\n        } else if (j) {\r\n          l = 0;\r\n          k = e;\r\n          j = f;\r\n          do {\r\n            k = Sb(k, j) | 0;\r\n            j = H() | 0;\r\n            i = ab(i) | 0;\r\n            if ((i | 0) == 1) { i = ab(1) | 0; }\r\n            l = l + 1 | 0;\r\n          } while ((l | 0) != (m | 0));\r\n          m = i;\r\n          l = k;\r\n          k = j;\r\n        } else {\r\n          m = i;\r\n          l = e;\r\n          k = f;\r\n        }\r\n        Xb(l, k, t) | 0;\r\n        if (!p) { I(27563, 27538, 191, 27548); }\r\n        j = (n | 0) != 0;\r\n        i = (o | 0) != 0;\r\n        if (j & i) { I(27590, 27538, 192, 27548); }\r\n        if (!j) {\r\n          if (i) {\r\n            i = Pb(l, k) | 0;\r\n            if ((i | 0) == 7) {\r\n              h = 5;\r\n              break;\r\n            }\r\n            if (a[22e3 + (i * 7 | 0) + m >> 0] | 0) {\r\n              h = 1;\r\n              break;\r\n            }\r\n            l = 0;\r\n            k = b[21168 + (m * 28 | 0) + (i << 2) >> 2] | 0;\r\n          } else {\r\n            l = 0;\r\n            k = 0;\r\n          }\r\n        } else {\r\n          i = Pb(c, d) | 0;\r\n          if ((i | 0) == 7) {\r\n            h = 5;\r\n            break;\r\n          }\r\n          if (a[22e3 + (i * 7 | 0) + q >> 0] | 0) {\r\n            h = 1;\r\n            break;\r\n          }\r\n          k = b[21168 + (i * 28 | 0) + (q << 2) >> 2] | 0;\r\n          l = k;\r\n        }\r\n        if ((l | k | 0) < 0) { h = 5; }else {\r\n          if ((k | 0) > 0) {\r\n            j = t + 4 | 0;\r\n            i = 0;\r\n            do {\r\n              _a(j);\r\n              i = i + 1 | 0;\r\n            } while ((i | 0) != (k | 0));\r\n          }\r\n          b[s >> 2] = 0;\r\n          b[s + 4 >> 2] = 0;\r\n          b[s + 8 >> 2] = 0;\r\n          Ya(s, q);\r\n          if (h | 0) { while (1) {\r\n            if (!(Vb(h) | 0)) { Xa(s); }else { Wa(s); }\r\n            if ((h | 0) > 1) { h = h + -1 | 0; }else { break; }\r\n          } }\r\n          if ((l | 0) > 0) {\r\n            h = 0;\r\n            do {\r\n              _a(s);\r\n              h = h + 1 | 0;\r\n            } while ((h | 0) != (l | 0));\r\n          }\r\n          r = t + 4 | 0;\r\n          Oa(r, s, r);\r\n          Ma(r);\r\n          r = 51;\r\n        }\r\n      } } while (0);\r\n      if ((r | 0) == 51) {\r\n        h = t + 4 | 0;\r\n        b[g >> 2] = b[h >> 2];\r\n        b[g + 4 >> 2] = b[h + 4 >> 2];\r\n        b[g + 8 >> 2] = b[h + 8 >> 2];\r\n        h = 0;\r\n      }\r\n      t = h;\r\n      T = u;\r\n      return t | 0;\r\n    }\r\n    function Ic(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0;\r\n      q = T;\r\n      T = T + 48 | 0;\r\n      k = q + 36 | 0;\r\n      h = q + 24 | 0;\r\n      i = q + 12 | 0;\r\n      j = q;\r\n      f = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      f = f & 15;\r\n      n = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      n = n & 127;\r\n      if (n >>> 0 > 121) {\r\n        e = 5;\r\n        T = q;\r\n        return e | 0;\r\n      }\r\n      l = oa(n) | 0;\r\n      Rd(f | 0, 0, 52) | 0;\r\n      r = H() | 0 | 134225919;\r\n      g = e;\r\n      b[g >> 2] = -1;\r\n      b[g + 4 >> 2] = r;\r\n      if (!f) {\r\n        f = Ra(d) | 0;\r\n        if ((f | 0) == 7) {\r\n          r = 1;\r\n          T = q;\r\n          return r | 0;\r\n        }\r\n        f = va(n, f) | 0;\r\n        if ((f | 0) == 127) {\r\n          r = 1;\r\n          T = q;\r\n          return r | 0;\r\n        }\r\n        o = Rd(f | 0, 0, 45) | 0;\r\n        p = H() | 0;\r\n        n = e;\r\n        p = b[n + 4 >> 2] & -1040385 | p;\r\n        r = e;\r\n        b[r >> 2] = b[n >> 2] | o;\r\n        b[r + 4 >> 2] = p;\r\n        r = 0;\r\n        T = q;\r\n        return r | 0;\r\n      }\r\n      b[k >> 2] = b[d >> 2];\r\n      b[k + 4 >> 2] = b[d + 4 >> 2];\r\n      b[k + 8 >> 2] = b[d + 8 >> 2];\r\n      d = f;\r\n      while (1) {\r\n        g = d;\r\n        d = d + -1 | 0;\r\n        b[h >> 2] = b[k >> 2];\r\n        b[h + 4 >> 2] = b[k + 4 >> 2];\r\n        b[h + 8 >> 2] = b[k + 8 >> 2];\r\n        if (!(Vb(g) | 0)) {\r\n          f = Ta(k) | 0;\r\n          if (f | 0) {\r\n            d = 13;\r\n            break;\r\n          }\r\n          b[i >> 2] = b[k >> 2];\r\n          b[i + 4 >> 2] = b[k + 4 >> 2];\r\n          b[i + 8 >> 2] = b[k + 8 >> 2];\r\n          Xa(i);\r\n        } else {\r\n          f = Sa(k) | 0;\r\n          if (f | 0) {\r\n            d = 13;\r\n            break;\r\n          }\r\n          b[i >> 2] = b[k >> 2];\r\n          b[i + 4 >> 2] = b[k + 4 >> 2];\r\n          b[i + 8 >> 2] = b[k + 8 >> 2];\r\n          Wa(i);\r\n        }\r\n        Pa(h, i, j);\r\n        Ma(j);\r\n        f = e;\r\n        t = b[f >> 2] | 0;\r\n        f = b[f + 4 >> 2] | 0;\r\n        u = (15 - g | 0) * 3 | 0;\r\n        s = Rd(7, 0, u | 0) | 0;\r\n        f = f & ~(H() | 0);\r\n        u = Rd(Ra(j) | 0, 0, u | 0) | 0;\r\n        f = H() | 0 | f;\r\n        r = e;\r\n        b[r >> 2] = u | t & ~s;\r\n        b[r + 4 >> 2] = f;\r\n        if ((g | 0) <= 1) {\r\n          d = 14;\r\n          break;\r\n        }\r\n      }\r\n      a: do { if ((d | 0) != 13) { if ((d | 0) == 14) { if (((b[k >> 2] | 0) <= 1 ? (b[k + 4 >> 2] | 0) <= 1 : 0) ? (b[k + 8 >> 2] | 0) <= 1 : 0) {\r\n        d = Ra(k) | 0;\r\n        f = va(n, d) | 0;\r\n        if ((f | 0) == 127) { j = 0; }else { j = oa(f) | 0; }\r\n        b: do { if (!d) {\r\n          if ((l | 0) != 0 & (j | 0) != 0) {\r\n            d = Pb(a, c) | 0;\r\n            g = e;\r\n            g = Pb(b[g >> 2] | 0, b[g + 4 >> 2] | 0) | 0;\r\n            if ((d | 0) == 7 | (g | 0) == 7) {\r\n              f = 5;\r\n              break a;\r\n            }\r\n            g = b[21376 + (d * 28 | 0) + (g << 2) >> 2] | 0;\r\n            if ((g | 0) < 0) {\r\n              f = 5;\r\n              break a;\r\n            }\r\n            if (!g) { d = 59; }else {\r\n              i = e;\r\n              d = 0;\r\n              h = b[i >> 2] | 0;\r\n              i = b[i + 4 >> 2] | 0;\r\n              do {\r\n                h = Rb(h, i) | 0;\r\n                i = H() | 0;\r\n                u = e;\r\n                b[u >> 2] = h;\r\n                b[u + 4 >> 2] = i;\r\n                d = d + 1 | 0;\r\n              } while ((d | 0) < (g | 0));\r\n              d = 58;\r\n            }\r\n          } else { d = 58; }\r\n        } else {\r\n          if (l) {\r\n            f = Pb(a, c) | 0;\r\n            if ((f | 0) == 7) {\r\n              f = 5;\r\n              break a;\r\n            }\r\n            g = b[21376 + (f * 28 | 0) + (d << 2) >> 2] | 0;\r\n            if ((g | 0) > 0) {\r\n              f = d;\r\n              d = 0;\r\n              do {\r\n                f = $a(f) | 0;\r\n                d = d + 1 | 0;\r\n              } while ((d | 0) != (g | 0));\r\n            } else { f = d; }\r\n            if ((f | 0) == 1) {\r\n              f = 9;\r\n              break a;\r\n            }\r\n            d = va(n, f) | 0;\r\n            if ((d | 0) == 127) { I(27648, 27538, 411, 27678); }\r\n            if (!(oa(d) | 0)) {\r\n              p = d;\r\n              o = g;\r\n              m = f;\r\n            } else { I(27693, 27538, 412, 27678); }\r\n          } else {\r\n            p = f;\r\n            o = 0;\r\n            m = d;\r\n          }\r\n          i = b[4272 + (n * 28 | 0) + (m << 2) >> 2] | 0;\r\n          if ((i | 0) <= -1) { I(27724, 27538, 419, 27678); }\r\n          if (!j) {\r\n            if ((o | 0) < 0) {\r\n              f = 5;\r\n              break a;\r\n            }\r\n            if (o | 0) {\r\n              g = e;\r\n              f = 0;\r\n              d = b[g >> 2] | 0;\r\n              g = b[g + 4 >> 2] | 0;\r\n              do {\r\n                d = Rb(d, g) | 0;\r\n                g = H() | 0;\r\n                u = e;\r\n                b[u >> 2] = d;\r\n                b[u + 4 >> 2] = g;\r\n                f = f + 1 | 0;\r\n              } while ((f | 0) < (o | 0));\r\n            }\r\n            if ((i | 0) <= 0) {\r\n              f = p;\r\n              d = 58;\r\n              break;\r\n            }\r\n            g = e;\r\n            f = 0;\r\n            d = b[g >> 2] | 0;\r\n            g = b[g + 4 >> 2] | 0;\r\n            while (1) {\r\n              d = Rb(d, g) | 0;\r\n              g = H() | 0;\r\n              u = e;\r\n              b[u >> 2] = d;\r\n              b[u + 4 >> 2] = g;\r\n              f = f + 1 | 0;\r\n              if ((f | 0) == (i | 0)) {\r\n                f = p;\r\n                d = 58;\r\n                break b;\r\n              }\r\n            }\r\n          }\r\n          h = wa(p, n) | 0;\r\n          if ((h | 0) == 7) { I(27514, 27538, 428, 27678); }\r\n          f = e;\r\n          d = b[f >> 2] | 0;\r\n          f = b[f + 4 >> 2] | 0;\r\n          if ((i | 0) > 0) {\r\n            g = 0;\r\n            do {\r\n              d = Rb(d, f) | 0;\r\n              f = H() | 0;\r\n              u = e;\r\n              b[u >> 2] = d;\r\n              b[u + 4 >> 2] = f;\r\n              g = g + 1 | 0;\r\n            } while ((g | 0) != (i | 0));\r\n          }\r\n          f = Pb(d, f) | 0;\r\n          if ((f | 0) == 7) { I(27795, 27538, 440, 27678); }\r\n          d = pa(p) | 0;\r\n          d = b[(d ? 21792 : 21584) + (h * 28 | 0) + (f << 2) >> 2] | 0;\r\n          if ((d | 0) < 0) { I(27795, 27538, 454, 27678); }\r\n          if (!d) {\r\n            f = p;\r\n            d = 58;\r\n          } else {\r\n            h = e;\r\n            f = 0;\r\n            g = b[h >> 2] | 0;\r\n            h = b[h + 4 >> 2] | 0;\r\n            do {\r\n              g = Qb(g, h) | 0;\r\n              h = H() | 0;\r\n              u = e;\r\n              b[u >> 2] = g;\r\n              b[u + 4 >> 2] = h;\r\n              f = f + 1 | 0;\r\n            } while ((f | 0) < (d | 0));\r\n            f = p;\r\n            d = 58;\r\n          }\r\n        } } while (0);\r\n        if ((d | 0) == 58) { if (j) { d = 59; } }\r\n        if ((d | 0) == 59) {\r\n          u = e;\r\n          if ((Pb(b[u >> 2] | 0, b[u + 4 >> 2] | 0) | 0) == 1) {\r\n            f = 9;\r\n            break;\r\n          }\r\n        }\r\n        u = e;\r\n        s = b[u >> 2] | 0;\r\n        u = b[u + 4 >> 2] & -1040385;\r\n        t = Rd(f | 0, 0, 45) | 0;\r\n        u = u | (H() | 0);\r\n        f = e;\r\n        b[f >> 2] = s | t;\r\n        b[f + 4 >> 2] = u;\r\n        f = 0;\r\n      } else { f = 1; } } } } while (0);\r\n      u = f;\r\n      T = q;\r\n      return u | 0;\r\n    }\r\n    function Jc(a, b, c, d, e, f) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0;\r\n      h = T;\r\n      T = T + 16 | 0;\r\n      g = h;\r\n      if (!e) {\r\n        a = Hc(a, b, c, d, g) | 0;\r\n        if (!a) {\r\n          eb(g, f);\r\n          a = 0;\r\n        }\r\n      } else { a = 15; }\r\n      T = h;\r\n      return a | 0;\r\n    }\r\n    function Kc(a, b, c, d, e) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0;\r\n      g = T;\r\n      T = T + 16 | 0;\r\n      f = g;\r\n      if (!d) {\r\n        c = fb(c, f) | 0;\r\n        if (!c) { c = Ic(a, b, f, e) | 0; }\r\n      } else { c = 15; }\r\n      T = g;\r\n      return c | 0;\r\n    }\r\n    function Lc(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 32 | 0;\r\n      h = j + 12 | 0;\r\n      i = j;\r\n      g = Hc(a, c, a, c, h) | 0;\r\n      if (g | 0) {\r\n        i = g;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      a = Hc(a, c, d, e, i) | 0;\r\n      if (a | 0) {\r\n        i = a;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      h = db(h, i) | 0;\r\n      i = f;\r\n      b[i >> 2] = h;\r\n      b[i + 4 >> 2] = ((h | 0) < 0) << 31 >> 31;\r\n      i = 0;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Mc(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 32 | 0;\r\n      h = j + 12 | 0;\r\n      i = j;\r\n      g = Hc(a, c, a, c, h) | 0;\r\n      if (!g) {\r\n        g = Hc(a, c, d, e, i) | 0;\r\n        if (!g) {\r\n          e = db(h, i) | 0;\r\n          e = Gd(e | 0, ((e | 0) < 0) << 31 >> 31 | 0, 1, 0) | 0;\r\n          h = H() | 0;\r\n          i = f;\r\n          b[i >> 2] = e;\r\n          b[i + 4 >> 2] = h;\r\n          i = 0;\r\n          T = j;\r\n          return i | 0;\r\n        }\r\n      }\r\n      i = g;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Nc(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0,\r\n        l = 0.0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        r = 0.0,\r\n        s = 0,\r\n        t = 0.0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0.0;\r\n      z = T;\r\n      T = T + 48 | 0;\r\n      x = z + 24 | 0;\r\n      h = z + 12 | 0;\r\n      y = z;\r\n      g = Hc(a, c, a, c, x) | 0;\r\n      if (!g) {\r\n        g = Hc(a, c, d, e, h) | 0;\r\n        if (!g) {\r\n          v = db(x, h) | 0;\r\n          w = ((v | 0) < 0) << 31 >> 31;\r\n          b[x >> 2] = 0;\r\n          b[x + 4 >> 2] = 0;\r\n          b[x + 8 >> 2] = 0;\r\n          b[h >> 2] = 0;\r\n          b[h + 4 >> 2] = 0;\r\n          b[h + 8 >> 2] = 0;\r\n          if (Hc(a, c, a, c, x) | 0) { I(27795, 27538, 692, 27747); }\r\n          if (Hc(a, c, d, e, h) | 0) { I(27795, 27538, 697, 27747); }\r\n          gb(x);\r\n          gb(h);\r\n          l = (v | 0) == 0 ? 0.0 : 1.0 / +(v | 0);\r\n          d = b[x >> 2] | 0;\r\n          r = l * +((b[h >> 2] | 0) - d | 0);\r\n          s = x + 4 | 0;\r\n          e = b[s >> 2] | 0;\r\n          t = l * +((b[h + 4 >> 2] | 0) - e | 0);\r\n          u = x + 8 | 0;\r\n          g = b[u >> 2] | 0;\r\n          l = l * +((b[h + 8 >> 2] | 0) - g | 0);\r\n          b[y >> 2] = d;\r\n          m = y + 4 | 0;\r\n          b[m >> 2] = e;\r\n          n = y + 8 | 0;\r\n          b[n >> 2] = g;\r\n          a: do { if ((v | 0) < 0) { g = 0; }else {\r\n            o = 0;\r\n            p = 0;\r\n            while (1) {\r\n              j = +(p >>> 0) + 4294967296.0 * +(o | 0);\r\n              A = r * j + +(d | 0);\r\n              i = t * j + +(e | 0);\r\n              j = l * j + +(g | 0);\r\n              d = ~~+Vd(+A);\r\n              h = ~~+Vd(+i);\r\n              g = ~~+Vd(+j);\r\n              A = +q(+(+(d | 0) - A));\r\n              i = +q(+(+(h | 0) - i));\r\n              j = +q(+(+(g | 0) - j));\r\n              do { if (!(A > i & A > j)) {\r\n                k = 0 - d | 0;\r\n                if (i > j) {\r\n                  e = k - g | 0;\r\n                  break;\r\n                } else {\r\n                  e = h;\r\n                  g = k - h | 0;\r\n                  break;\r\n                }\r\n              } else {\r\n                d = 0 - (h + g) | 0;\r\n                e = h;\r\n              } } while (0);\r\n              b[y >> 2] = d;\r\n              b[m >> 2] = e;\r\n              b[n >> 2] = g;\r\n              hb(y);\r\n              g = Ic(a, c, y, f + (p << 3) | 0) | 0;\r\n              if (g | 0) { break a; }\r\n              if (!((o | 0) < (w | 0) | (o | 0) == (w | 0) & p >>> 0 < v >>> 0)) {\r\n                g = 0;\r\n                break a;\r\n              }\r\n              d = Gd(p | 0, o | 0, 1, 0) | 0;\r\n              e = H() | 0;\r\n              o = e;\r\n              p = d;\r\n              d = b[x >> 2] | 0;\r\n              e = b[s >> 2] | 0;\r\n              g = b[u >> 2] | 0;\r\n            }\r\n          } } while (0);\r\n          y = g;\r\n          T = z;\r\n          return y | 0;\r\n        }\r\n      }\r\n      y = g;\r\n      T = z;\r\n      return y | 0;\r\n    }\r\n    function Oc(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      if ((c | 0) == 0 & (d | 0) == 0) {\r\n        e = 0;\r\n        f = 1;\r\n        G(e | 0);\r\n        return f | 0;\r\n      }\r\n      f = a;\r\n      e = b;\r\n      a = 1;\r\n      b = 0;\r\n      do {\r\n        g = (c & 1 | 0) == 0 & 0 == 0;\r\n        a = Md((g ? 1 : f) | 0, (g ? 0 : e) | 0, a | 0, b | 0) | 0;\r\n        b = H() | 0;\r\n        c = Pd(c | 0, d | 0, 1) | 0;\r\n        d = H() | 0;\r\n        f = Md(f | 0, e | 0, f | 0, e | 0) | 0;\r\n        e = H() | 0;\r\n      } while (!((c | 0) == 0 & (d | 0) == 0));\r\n      G(b | 0);\r\n      return a | 0;\r\n    }\r\n    function Pc(a, c, d, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0;\r\n      j = T;\r\n      T = T + 16 | 0;\r\n      h = j;\r\n      i = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      i = i & 15;\r\n      do { if (!i) {\r\n        g = Qd(a | 0, c | 0, 45) | 0;\r\n        H() | 0;\r\n        g = g & 127;\r\n        if (g >>> 0 > 121) {\r\n          i = 5;\r\n          T = j;\r\n          return i | 0;\r\n        } else {\r\n          h = 22064 + (g << 5) | 0;\r\n          b[d >> 2] = b[h >> 2];\r\n          b[d + 4 >> 2] = b[h + 4 >> 2];\r\n          b[d + 8 >> 2] = b[h + 8 >> 2];\r\n          b[d + 12 >> 2] = b[h + 12 >> 2];\r\n          b[d + 16 >> 2] = b[h + 16 >> 2];\r\n          b[d + 20 >> 2] = b[h + 20 >> 2];\r\n          b[d + 24 >> 2] = b[h + 24 >> 2];\r\n          b[d + 28 >> 2] = b[h + 28 >> 2];\r\n          break;\r\n        }\r\n      } else {\r\n        g = Zb(a, c, h) | 0;\r\n        if (!g) {\r\n          l = +e[h >> 3];\r\n          k = 1.0 / +t(+l);\r\n          m = +e[25968 + (i << 3) >> 3];\r\n          e[d >> 3] = l + m;\r\n          e[d + 8 >> 3] = l - m;\r\n          l = +e[h + 8 >> 3];\r\n          k = m * k;\r\n          e[d + 16 >> 3] = k + l;\r\n          e[d + 24 >> 3] = l - k;\r\n          break;\r\n        }\r\n        i = g;\r\n        T = j;\r\n        return i | 0;\r\n      } } while (0);\r\n      Ja(d, f ? 1.4 : 1.1);\r\n      f = 26096 + (i << 3) | 0;\r\n      if ((b[f >> 2] | 0) == (a | 0) ? (b[f + 4 >> 2] | 0) == (c | 0) : 0) { e[d >> 3] = 1.5707963267948966; }\r\n      i = 26224 + (i << 3) | 0;\r\n      if ((b[i >> 2] | 0) == (a | 0) ? (b[i + 4 >> 2] | 0) == (c | 0) : 0) { e[d + 8 >> 3] = -1.5707963267948966; }\r\n      if (!(+e[d >> 3] == 1.5707963267948966) ? !(+e[d + 8 >> 3] == -1.5707963267948966) : 0) {\r\n        i = 0;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      e[d + 16 >> 3] = 3.141592653589793;\r\n      e[d + 24 >> 3] = -3.141592653589793;\r\n      i = 0;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function Qc(c, d, e, f) {\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0;\r\n      l = T;\r\n      T = T + 48 | 0;\r\n      i = l + 32 | 0;\r\n      h = l + 40 | 0;\r\n      j = l;\r\n      Eb(i, 0, 0, 0);\r\n      k = b[i >> 2] | 0;\r\n      i = b[i + 4 >> 2] | 0;\r\n      do { if (e >>> 0 <= 15) {\r\n        g = Xc(f) | 0;\r\n        if (g | 0) {\r\n          f = j;\r\n          b[f >> 2] = 0;\r\n          b[f + 4 >> 2] = 0;\r\n          b[j + 8 >> 2] = g;\r\n          b[j + 12 >> 2] = -1;\r\n          f = j + 16 | 0;\r\n          k = j + 29 | 0;\r\n          b[f >> 2] = 0;\r\n          b[f + 4 >> 2] = 0;\r\n          b[f + 8 >> 2] = 0;\r\n          a[f + 12 >> 0] = 0;\r\n          a[k >> 0] = a[h >> 0] | 0;\r\n          a[k + 1 >> 0] = a[h + 1 >> 0] | 0;\r\n          a[k + 2 >> 0] = a[h + 2 >> 0] | 0;\r\n          break;\r\n        }\r\n        g = Fd((b[d + 8 >> 2] | 0) + 1 | 0, 32) | 0;\r\n        if (!g) {\r\n          f = j;\r\n          b[f >> 2] = 0;\r\n          b[f + 4 >> 2] = 0;\r\n          b[j + 8 >> 2] = 13;\r\n          b[j + 12 >> 2] = -1;\r\n          f = j + 16 | 0;\r\n          k = j + 29 | 0;\r\n          b[f >> 2] = 0;\r\n          b[f + 4 >> 2] = 0;\r\n          b[f + 8 >> 2] = 0;\r\n          a[f + 12 >> 0] = 0;\r\n          a[k >> 0] = a[h >> 0] | 0;\r\n          a[k + 1 >> 0] = a[h + 1 >> 0] | 0;\r\n          a[k + 2 >> 0] = a[h + 2 >> 0] | 0;\r\n          break;\r\n        } else {\r\n          Yc(d, g);\r\n          m = j;\r\n          b[m >> 2] = k;\r\n          b[m + 4 >> 2] = i;\r\n          b[j + 8 >> 2] = 0;\r\n          b[j + 12 >> 2] = e;\r\n          b[j + 16 >> 2] = f;\r\n          b[j + 20 >> 2] = d;\r\n          b[j + 24 >> 2] = g;\r\n          a[j + 28 >> 0] = 0;\r\n          k = j + 29 | 0;\r\n          a[k >> 0] = a[h >> 0] | 0;\r\n          a[k + 1 >> 0] = a[h + 1 >> 0] | 0;\r\n          a[k + 2 >> 0] = a[h + 2 >> 0] | 0;\r\n          break;\r\n        }\r\n      } else {\r\n        k = j;\r\n        b[k >> 2] = 0;\r\n        b[k + 4 >> 2] = 0;\r\n        b[j + 8 >> 2] = 4;\r\n        b[j + 12 >> 2] = -1;\r\n        k = j + 16 | 0;\r\n        m = j + 29 | 0;\r\n        b[k >> 2] = 0;\r\n        b[k + 4 >> 2] = 0;\r\n        b[k + 8 >> 2] = 0;\r\n        a[k + 12 >> 0] = 0;\r\n        a[m >> 0] = a[h >> 0] | 0;\r\n        a[m + 1 >> 0] = a[h + 1 >> 0] | 0;\r\n        a[m + 2 >> 0] = a[h + 2 >> 0] | 0;\r\n      } } while (0);\r\n      Rc(j);\r\n      b[c >> 2] = b[j >> 2];\r\n      b[c + 4 >> 2] = b[j + 4 >> 2];\r\n      b[c + 8 >> 2] = b[j + 8 >> 2];\r\n      b[c + 12 >> 2] = b[j + 12 >> 2];\r\n      b[c + 16 >> 2] = b[j + 16 >> 2];\r\n      b[c + 20 >> 2] = b[j + 20 >> 2];\r\n      b[c + 24 >> 2] = b[j + 24 >> 2];\r\n      b[c + 28 >> 2] = b[j + 28 >> 2];\r\n      T = l;\r\n      return;\r\n    }\r\n    function Rc(c) {\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0;\r\n      w = T;\r\n      T = T + 336 | 0;\r\n      p = w + 168 | 0;\r\n      q = w;\r\n      f = c;\r\n      e = b[f >> 2] | 0;\r\n      f = b[f + 4 >> 2] | 0;\r\n      if ((e | 0) == 0 & (f | 0) == 0) {\r\n        T = w;\r\n        return;\r\n      }\r\n      d = c + 28 | 0;\r\n      if (!(a[d >> 0] | 0)) { a[d >> 0] = 1; }else {\r\n        e = Sc(e, f) | 0;\r\n        f = H() | 0;\r\n      }\r\n      v = c + 20 | 0;\r\n      if (!(b[b[v >> 2] >> 2] | 0)) {\r\n        d = c + 24 | 0;\r\n        e = b[d >> 2] | 0;\r\n        if (e | 0) { Ed(e); }\r\n        u = c;\r\n        b[u >> 2] = 0;\r\n        b[u + 4 >> 2] = 0;\r\n        b[c + 8 >> 2] = 0;\r\n        b[v >> 2] = 0;\r\n        b[c + 12 >> 2] = -1;\r\n        b[c + 16 >> 2] = 0;\r\n        b[d >> 2] = 0;\r\n        T = w;\r\n        return;\r\n      }\r\n      u = c + 16 | 0;\r\n      d = b[u >> 2] | 0;\r\n      g = d & 15;\r\n      a: do { if (!((e | 0) == 0 & (f | 0) == 0)) {\r\n        r = c + 12 | 0;\r\n        n = (g | 0) == 3;\r\n        m = d & 255;\r\n        k = (g | 1 | 0) == 3;\r\n        o = c + 24 | 0;\r\n        l = (g + -1 | 0) >>> 0 < 3;\r\n        i = (g | 2 | 0) == 3;\r\n        j = q + 8 | 0;\r\n        b: while (1) {\r\n          h = Qd(e | 0, f | 0, 52) | 0;\r\n          H() | 0;\r\n          h = h & 15;\r\n          if ((h | 0) == (b[r >> 2] | 0)) {\r\n            switch (m & 15) {\r\n              case 0:\r\n              case 2:\r\n              case 3:\r\n                {\r\n                  g = Zb(e, f, p) | 0;\r\n                  if (g | 0) {\r\n                    s = 15;\r\n                    break b;\r\n                  }\r\n                  if (Zc(b[v >> 2] | 0, b[o >> 2] | 0, p) | 0) {\r\n                    s = 19;\r\n                    break b;\r\n                  }\r\n                  break;\r\n                }\r\n              default:\r\n\r\n            }\r\n            if (k ? (g = b[(b[v >> 2] | 0) + 4 >> 2] | 0, b[p >> 2] = b[g >> 2], b[p + 4 >> 2] = b[g + 4 >> 2], b[p + 8 >> 2] = b[g + 8 >> 2], b[p + 12 >> 2] = b[g + 12 >> 2], Ca(26832, p) | 0) : 0) {\r\n              if (Wb(b[(b[v >> 2] | 0) + 4 >> 2] | 0, h, q) | 0) {\r\n                s = 25;\r\n                break;\r\n              }\r\n              g = q;\r\n              if ((b[g >> 2] | 0) == (e | 0) ? (b[g + 4 >> 2] | 0) == (f | 0) : 0) {\r\n                s = 29;\r\n                break;\r\n              }\r\n            }\r\n            if (l) {\r\n              g = _b(e, f, p) | 0;\r\n              if (g | 0) {\r\n                s = 32;\r\n                break;\r\n              }\r\n              if (Pc(e, f, q, 0) | 0) {\r\n                s = 36;\r\n                break;\r\n              }\r\n              if (i ? _c(b[v >> 2] | 0, b[o >> 2] | 0, p, q) | 0 : 0) {\r\n                s = 42;\r\n                break;\r\n              }\r\n              if (k ? ad(b[v >> 2] | 0, b[o >> 2] | 0, p, q) | 0 : 0) {\r\n                s = 42;\r\n                break;\r\n              }\r\n            }\r\n            if (n) {\r\n              d = Pc(e, f, p, 1) | 0;\r\n              g = b[o >> 2] | 0;\r\n              if (d | 0) {\r\n                s = 45;\r\n                break;\r\n              }\r\n              if (Da(g, p) | 0) {\r\n                Ga(q, p);\r\n                if (Fa(p, b[o >> 2] | 0) | 0) {\r\n                  s = 53;\r\n                  break;\r\n                }\r\n                if (Zc(b[v >> 2] | 0, b[o >> 2] | 0, j) | 0) {\r\n                  s = 53;\r\n                  break;\r\n                }\r\n                if (ad(b[v >> 2] | 0, b[o >> 2] | 0, q, p) | 0) {\r\n                  s = 53;\r\n                  break;\r\n                }\r\n              }\r\n            }\r\n          }\r\n          do { if ((h | 0) < (b[r >> 2] | 0)) {\r\n            d = Pc(e, f, p, 1) | 0;\r\n            g = b[o >> 2] | 0;\r\n            if (d | 0) {\r\n              s = 58;\r\n              break b;\r\n            }\r\n            if (!(Da(g, p) | 0)) {\r\n              s = 73;\r\n              break;\r\n            }\r\n            if (Fa(b[o >> 2] | 0, p) | 0 ? (Ga(q, p), _c(b[v >> 2] | 0, b[o >> 2] | 0, q, p) | 0) : 0) {\r\n              s = 65;\r\n              break b;\r\n            }\r\n            e = Kb(e, f, h + 1 | 0, q) | 0;\r\n            if (e | 0) {\r\n              s = 67;\r\n              break b;\r\n            }\r\n            f = q;\r\n            e = b[f >> 2] | 0;\r\n            f = b[f + 4 >> 2] | 0;\r\n          } else { s = 73; } } while (0);\r\n          if ((s | 0) == 73) {\r\n            s = 0;\r\n            e = Sc(e, f) | 0;\r\n            f = H() | 0;\r\n          }\r\n          if ((e | 0) == 0 & (f | 0) == 0) {\r\n            t = o;\r\n            break a;\r\n          }\r\n        }\r\n        switch (s | 0) {\r\n          case 15:\r\n            {\r\n              d = b[o >> 2] | 0;\r\n              if (d | 0) { Ed(d); }\r\n              s = c;\r\n              b[s >> 2] = 0;\r\n              b[s + 4 >> 2] = 0;\r\n              b[v >> 2] = 0;\r\n              b[r >> 2] = -1;\r\n              b[u >> 2] = 0;\r\n              b[o >> 2] = 0;\r\n              b[c + 8 >> 2] = g;\r\n              s = 20;\r\n              break;\r\n            }\r\n          case 19:\r\n            {\r\n              b[c >> 2] = e;\r\n              b[c + 4 >> 2] = f;\r\n              s = 20;\r\n              break;\r\n            }\r\n          case 25:\r\n            {\r\n              I(27795, 27761, 470, 27772);\r\n              break;\r\n            }\r\n          case 29:\r\n            {\r\n              b[c >> 2] = e;\r\n              b[c + 4 >> 2] = f;\r\n              T = w;\r\n              return;\r\n            }\r\n          case 32:\r\n            {\r\n              d = b[o >> 2] | 0;\r\n              if (d | 0) { Ed(d); }\r\n              t = c;\r\n              b[t >> 2] = 0;\r\n              b[t + 4 >> 2] = 0;\r\n              b[v >> 2] = 0;\r\n              b[r >> 2] = -1;\r\n              b[u >> 2] = 0;\r\n              b[o >> 2] = 0;\r\n              b[c + 8 >> 2] = g;\r\n              T = w;\r\n              return;\r\n            }\r\n          case 36:\r\n            {\r\n              I(27795, 27761, 493, 27772);\r\n              break;\r\n            }\r\n          case 42:\r\n            {\r\n              b[c >> 2] = e;\r\n              b[c + 4 >> 2] = f;\r\n              T = w;\r\n              return;\r\n            }\r\n          case 45:\r\n            {\r\n              if (g | 0) { Ed(g); }\r\n              s = c;\r\n              b[s >> 2] = 0;\r\n              b[s + 4 >> 2] = 0;\r\n              b[v >> 2] = 0;\r\n              b[r >> 2] = -1;\r\n              b[u >> 2] = 0;\r\n              b[o >> 2] = 0;\r\n              b[c + 8 >> 2] = d;\r\n              s = 55;\r\n              break;\r\n            }\r\n          case 53:\r\n            {\r\n              b[c >> 2] = e;\r\n              b[c + 4 >> 2] = f;\r\n              s = 55;\r\n              break;\r\n            }\r\n          case 58:\r\n            {\r\n              if (g | 0) { Ed(g); }\r\n              s = c;\r\n              b[s >> 2] = 0;\r\n              b[s + 4 >> 2] = 0;\r\n              b[v >> 2] = 0;\r\n              b[r >> 2] = -1;\r\n              b[u >> 2] = 0;\r\n              b[o >> 2] = 0;\r\n              b[c + 8 >> 2] = d;\r\n              s = 71;\r\n              break;\r\n            }\r\n          case 65:\r\n            {\r\n              b[c >> 2] = e;\r\n              b[c + 4 >> 2] = f;\r\n              s = 71;\r\n              break;\r\n            }\r\n          case 67:\r\n            {\r\n              d = b[o >> 2] | 0;\r\n              if (d | 0) { Ed(d); }\r\n              t = c;\r\n              b[t >> 2] = 0;\r\n              b[t + 4 >> 2] = 0;\r\n              b[v >> 2] = 0;\r\n              b[r >> 2] = -1;\r\n              b[u >> 2] = 0;\r\n              b[o >> 2] = 0;\r\n              b[c + 8 >> 2] = e;\r\n              T = w;\r\n              return;\r\n            }\r\n        }\r\n        if ((s | 0) == 20) {\r\n          T = w;\r\n          return;\r\n        } else if ((s | 0) == 55) {\r\n          T = w;\r\n          return;\r\n        } else if ((s | 0) == 71) {\r\n          T = w;\r\n          return;\r\n        }\r\n      } else { t = c + 24 | 0; } } while (0);\r\n      d = b[t >> 2] | 0;\r\n      if (d | 0) { Ed(d); }\r\n      s = c;\r\n      b[s >> 2] = 0;\r\n      b[s + 4 >> 2] = 0;\r\n      b[c + 8 >> 2] = 0;\r\n      b[v >> 2] = 0;\r\n      b[c + 12 >> 2] = -1;\r\n      b[u >> 2] = 0;\r\n      b[t >> 2] = 0;\r\n      T = w;\r\n      return;\r\n    }\r\n    function Sc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0;\r\n      m = T;\r\n      T = T + 16 | 0;\r\n      l = m;\r\n      e = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      e = e & 15;\r\n      d = Qd(a | 0, c | 0, 45) | 0;\r\n      H() | 0;\r\n      do { if (e) {\r\n        while (1) {\r\n          d = Rd(e + 4095 | 0, 0, 52) | 0;\r\n          f = H() | 0 | c & -15728641;\r\n          g = (15 - e | 0) * 3 | 0;\r\n          h = Rd(7, 0, g | 0) | 0;\r\n          i = H() | 0;\r\n          d = d | a | h;\r\n          f = f | i;\r\n          j = Qd(a | 0, c | 0, g | 0) | 0;\r\n          H() | 0;\r\n          j = j & 7;\r\n          e = e + -1 | 0;\r\n          if (j >>> 0 < 6) { break; }\r\n          if (!e) {\r\n            k = 4;\r\n            break;\r\n          } else {\r\n            c = f;\r\n            a = d;\r\n          }\r\n        }\r\n        if ((k | 0) == 4) {\r\n          d = Qd(d | 0, f | 0, 45) | 0;\r\n          H() | 0;\r\n          break;\r\n        }\r\n        l = (j | 0) == 0 & (Hb(d, f) | 0) != 0;\r\n        l = Rd((l ? 2 : 1) + j | 0, 0, g | 0) | 0;\r\n        k = H() | 0 | c & ~i;\r\n        l = l | a & ~h;\r\n        G(k | 0);\r\n        T = m;\r\n        return l | 0;\r\n      } } while (0);\r\n      d = d & 127;\r\n      if (d >>> 0 > 120) {\r\n        k = 0;\r\n        l = 0;\r\n        G(k | 0);\r\n        T = m;\r\n        return l | 0;\r\n      }\r\n      Eb(l, 0, d + 1 | 0, 0);\r\n      k = b[l + 4 >> 2] | 0;\r\n      l = b[l >> 2] | 0;\r\n      G(k | 0);\r\n      T = m;\r\n      return l | 0;\r\n    }\r\n    function Tc(a, c, d, e, f, g) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0;\r\n      r = T;\r\n      T = T + 160 | 0;\r\n      m = r + 80 | 0;\r\n      i = r + 64 | 0;\r\n      n = r + 112 | 0;\r\n      q = r;\r\n      Qc(m, a, c, d);\r\n      k = m;\r\n      fc(i, b[k >> 2] | 0, b[k + 4 >> 2] | 0, c);\r\n      k = i;\r\n      j = b[k >> 2] | 0;\r\n      k = b[k + 4 >> 2] | 0;\r\n      h = b[m + 8 >> 2] | 0;\r\n      o = n + 4 | 0;\r\n      b[o >> 2] = b[m >> 2];\r\n      b[o + 4 >> 2] = b[m + 4 >> 2];\r\n      b[o + 8 >> 2] = b[m + 8 >> 2];\r\n      b[o + 12 >> 2] = b[m + 12 >> 2];\r\n      b[o + 16 >> 2] = b[m + 16 >> 2];\r\n      b[o + 20 >> 2] = b[m + 20 >> 2];\r\n      b[o + 24 >> 2] = b[m + 24 >> 2];\r\n      b[o + 28 >> 2] = b[m + 28 >> 2];\r\n      o = q;\r\n      b[o >> 2] = j;\r\n      b[o + 4 >> 2] = k;\r\n      o = q + 8 | 0;\r\n      b[o >> 2] = h;\r\n      a = q + 12 | 0;\r\n      c = n;\r\n      d = a + 36 | 0;\r\n      do {\r\n        b[a >> 2] = b[c >> 2];\r\n        a = a + 4 | 0;\r\n        c = c + 4 | 0;\r\n      } while ((a | 0) < (d | 0));\r\n      n = q + 48 | 0;\r\n      b[n >> 2] = b[i >> 2];\r\n      b[n + 4 >> 2] = b[i + 4 >> 2];\r\n      b[n + 8 >> 2] = b[i + 8 >> 2];\r\n      b[n + 12 >> 2] = b[i + 12 >> 2];\r\n      if ((j | 0) == 0 & (k | 0) == 0) {\r\n        q = h;\r\n        T = r;\r\n        return q | 0;\r\n      }\r\n      d = q + 16 | 0;\r\n      l = q + 24 | 0;\r\n      m = q + 28 | 0;\r\n      h = 0;\r\n      i = 0;\r\n      c = j;\r\n      a = k;\r\n      do {\r\n        if (!((h | 0) < (f | 0) | (h | 0) == (f | 0) & i >>> 0 < e >>> 0)) {\r\n          p = 4;\r\n          break;\r\n        }\r\n        k = i;\r\n        i = Gd(i | 0, h | 0, 1, 0) | 0;\r\n        h = H() | 0;\r\n        k = g + (k << 3) | 0;\r\n        b[k >> 2] = c;\r\n        b[k + 4 >> 2] = a;\r\n        hc(n);\r\n        a = n;\r\n        c = b[a >> 2] | 0;\r\n        a = b[a + 4 >> 2] | 0;\r\n        if ((c | 0) == 0 & (a | 0) == 0) {\r\n          Rc(d);\r\n          c = d;\r\n          a = b[c >> 2] | 0;\r\n          c = b[c + 4 >> 2] | 0;\r\n          if ((a | 0) == 0 & (c | 0) == 0) {\r\n            p = 10;\r\n            break;\r\n          }\r\n          gc(a, c, b[m >> 2] | 0, n);\r\n          a = n;\r\n          c = b[a >> 2] | 0;\r\n          a = b[a + 4 >> 2] | 0;\r\n        }\r\n        k = q;\r\n        b[k >> 2] = c;\r\n        b[k + 4 >> 2] = a;\r\n      } while (!((c | 0) == 0 & (a | 0) == 0));\r\n      if ((p | 0) == 4) {\r\n        a = q + 40 | 0;\r\n        c = b[a >> 2] | 0;\r\n        if (c | 0) { Ed(c); }\r\n        p = q + 16 | 0;\r\n        b[p >> 2] = 0;\r\n        b[p + 4 >> 2] = 0;\r\n        b[l >> 2] = 0;\r\n        b[q + 36 >> 2] = 0;\r\n        b[m >> 2] = -1;\r\n        b[q + 32 >> 2] = 0;\r\n        b[a >> 2] = 0;\r\n        gc(0, 0, 0, n);\r\n        b[q >> 2] = 0;\r\n        b[q + 4 >> 2] = 0;\r\n        b[o >> 2] = 0;\r\n        q = 14;\r\n        T = r;\r\n        return q | 0;\r\n      } else if ((p | 0) == 10) {\r\n        b[q >> 2] = 0;\r\n        b[q + 4 >> 2] = 0;\r\n        b[o >> 2] = b[l >> 2];\r\n      }\r\n      q = b[o >> 2] | 0;\r\n      T = r;\r\n      return q | 0;\r\n    }\r\n    function Uc(c, d, f, g) {\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      g = g | 0;\r\n      var h = 0,\r\n        i = 0.0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0.0,\r\n        r = 0;\r\n      o = T;\r\n      T = T + 48 | 0;\r\n      l = o + 32 | 0;\r\n      k = o + 40 | 0;\r\n      m = o;\r\n      if (!(b[c >> 2] | 0)) {\r\n        n = g;\r\n        b[n >> 2] = 0;\r\n        b[n + 4 >> 2] = 0;\r\n        n = 0;\r\n        T = o;\r\n        return n | 0;\r\n      }\r\n      Eb(l, 0, 0, 0);\r\n      j = l;\r\n      h = b[j >> 2] | 0;\r\n      j = b[j + 4 >> 2] | 0;\r\n      do { if (d >>> 0 > 15) {\r\n        n = m;\r\n        b[n >> 2] = 0;\r\n        b[n + 4 >> 2] = 0;\r\n        b[m + 8 >> 2] = 4;\r\n        b[m + 12 >> 2] = -1;\r\n        n = m + 16 | 0;\r\n        f = m + 29 | 0;\r\n        b[n >> 2] = 0;\r\n        b[n + 4 >> 2] = 0;\r\n        b[n + 8 >> 2] = 0;\r\n        a[n + 12 >> 0] = 0;\r\n        a[f >> 0] = a[k >> 0] | 0;\r\n        a[f + 1 >> 0] = a[k + 1 >> 0] | 0;\r\n        a[f + 2 >> 0] = a[k + 2 >> 0] | 0;\r\n        f = 4;\r\n        n = 9;\r\n      } else {\r\n        f = Xc(f) | 0;\r\n        if (f | 0) {\r\n          l = m;\r\n          b[l >> 2] = 0;\r\n          b[l + 4 >> 2] = 0;\r\n          b[m + 8 >> 2] = f;\r\n          b[m + 12 >> 2] = -1;\r\n          l = m + 16 | 0;\r\n          n = m + 29 | 0;\r\n          b[l >> 2] = 0;\r\n          b[l + 4 >> 2] = 0;\r\n          b[l + 8 >> 2] = 0;\r\n          a[l + 12 >> 0] = 0;\r\n          a[n >> 0] = a[k >> 0] | 0;\r\n          a[n + 1 >> 0] = a[k + 1 >> 0] | 0;\r\n          a[n + 2 >> 0] = a[k + 2 >> 0] | 0;\r\n          n = 9;\r\n          break;\r\n        }\r\n        f = Fd((b[c + 8 >> 2] | 0) + 1 | 0, 32) | 0;\r\n        if (!f) {\r\n          n = m;\r\n          b[n >> 2] = 0;\r\n          b[n + 4 >> 2] = 0;\r\n          b[m + 8 >> 2] = 13;\r\n          b[m + 12 >> 2] = -1;\r\n          n = m + 16 | 0;\r\n          f = m + 29 | 0;\r\n          b[n >> 2] = 0;\r\n          b[n + 4 >> 2] = 0;\r\n          b[n + 8 >> 2] = 0;\r\n          a[n + 12 >> 0] = 0;\r\n          a[f >> 0] = a[k >> 0] | 0;\r\n          a[f + 1 >> 0] = a[k + 1 >> 0] | 0;\r\n          a[f + 2 >> 0] = a[k + 2 >> 0] | 0;\r\n          f = 13;\r\n          n = 9;\r\n          break;\r\n        }\r\n        Yc(c, f);\r\n        r = m;\r\n        b[r >> 2] = h;\r\n        b[r + 4 >> 2] = j;\r\n        j = m + 8 | 0;\r\n        b[j >> 2] = 0;\r\n        b[m + 12 >> 2] = d;\r\n        b[m + 20 >> 2] = c;\r\n        b[m + 24 >> 2] = f;\r\n        a[m + 28 >> 0] = 0;\r\n        h = m + 29 | 0;\r\n        a[h >> 0] = a[k >> 0] | 0;\r\n        a[h + 1 >> 0] = a[k + 1 >> 0] | 0;\r\n        a[h + 2 >> 0] = a[k + 2 >> 0] | 0;\r\n        b[m + 16 >> 2] = 3;\r\n        p = +Ba(f);\r\n        p = p * +za(f);\r\n        i = +q(+ +e[f >> 3]);\r\n        i = p / +t(+ +Ud(+i, + +q(+ +e[f + 8 >> 3]))) * 6371.007180918475 * 6371.007180918475;\r\n        h = m + 12 | 0;\r\n        f = b[h >> 2] | 0;\r\n        a: do { if ((f | 0) > 0) { do {\r\n          qc(f + -1 | 0, l) | 0;\r\n          if (!(i / +e[l >> 3] > 10.0)) { break a; }\r\n          r = b[h >> 2] | 0;\r\n          f = r + -1 | 0;\r\n          b[h >> 2] = f;\r\n        } while ((r | 0) > 1); } } while (0);\r\n        Rc(m);\r\n        h = g;\r\n        b[h >> 2] = 0;\r\n        b[h + 4 >> 2] = 0;\r\n        h = m;\r\n        f = b[h >> 2] | 0;\r\n        h = b[h + 4 >> 2] | 0;\r\n        if (!((f | 0) == 0 & (h | 0) == 0)) { do {\r\n          Gb(f, h, d, l) | 0;\r\n          k = l;\r\n          c = g;\r\n          k = Gd(b[c >> 2] | 0, b[c + 4 >> 2] | 0, b[k >> 2] | 0, b[k + 4 >> 2] | 0) | 0;\r\n          c = H() | 0;\r\n          r = g;\r\n          b[r >> 2] = k;\r\n          b[r + 4 >> 2] = c;\r\n          Rc(m);\r\n          r = m;\r\n          f = b[r >> 2] | 0;\r\n          h = b[r + 4 >> 2] | 0;\r\n        } while (!((f | 0) == 0 & (h | 0) == 0)); }\r\n        f = b[j >> 2] | 0;\r\n      } } while (0);\r\n      r = f;\r\n      T = o;\r\n      return r | 0;\r\n    }\r\n    function Vc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0,\r\n        m = 0.0,\r\n        n = 0,\r\n        o = 0;\r\n      if (!(Ca(c, d) | 0)) {\r\n        o = 0;\r\n        return o | 0;\r\n      }\r\n      c = Aa(c) | 0;\r\n      f = +e[d >> 3];\r\n      g = +e[d + 8 >> 3];\r\n      g = c & g < 0.0 ? g + 6.283185307179586 : g;\r\n      o = b[a >> 2] | 0;\r\n      if ((o | 0) <= 0) {\r\n        o = 0;\r\n        return o | 0;\r\n      }\r\n      n = b[a + 4 >> 2] | 0;\r\n      if (c) {\r\n        c = 0;\r\n        m = g;\r\n        d = -1;\r\n        a = 0;\r\n        a: while (1) {\r\n          l = a;\r\n          while (1) {\r\n            i = +e[n + (l << 4) >> 3];\r\n            g = +e[n + (l << 4) + 8 >> 3];\r\n            a = (d + 2 | 0) % (o | 0) | 0;\r\n            h = +e[n + (a << 4) >> 3];\r\n            j = +e[n + (a << 4) + 8 >> 3];\r\n            if (i > h) {\r\n              k = i;\r\n              i = j;\r\n            } else {\r\n              k = h;\r\n              h = i;\r\n              i = g;\r\n              g = j;\r\n            }\r\n            f = f == h | f == k ? f + 2.220446049250313e-16 : f;\r\n            if (!(f < h | f > k)) { break; }\r\n            d = l + 1 | 0;\r\n            if ((d | 0) >= (o | 0)) {\r\n              d = 22;\r\n              break a;\r\n            } else {\r\n              a = l;\r\n              l = d;\r\n              d = a;\r\n            }\r\n          }\r\n          j = i < 0.0 ? i + 6.283185307179586 : i;\r\n          i = g < 0.0 ? g + 6.283185307179586 : g;\r\n          m = j == m | i == m ? m + -2.220446049250313e-16 : m;\r\n          k = j + (i - j) * ((f - h) / (k - h));\r\n          if ((k < 0.0 ? k + 6.283185307179586 : k) > m) { c = c ^ 1; }\r\n          a = l + 1 | 0;\r\n          if ((a | 0) >= (o | 0)) {\r\n            d = 22;\r\n            break;\r\n          } else { d = l; }\r\n        }\r\n        if ((d | 0) == 22) { return c | 0; }\r\n      } else {\r\n        c = 0;\r\n        m = g;\r\n        d = -1;\r\n        a = 0;\r\n        b: while (1) {\r\n          l = a;\r\n          while (1) {\r\n            i = +e[n + (l << 4) >> 3];\r\n            g = +e[n + (l << 4) + 8 >> 3];\r\n            a = (d + 2 | 0) % (o | 0) | 0;\r\n            h = +e[n + (a << 4) >> 3];\r\n            j = +e[n + (a << 4) + 8 >> 3];\r\n            if (i > h) {\r\n              k = i;\r\n              i = j;\r\n            } else {\r\n              k = h;\r\n              h = i;\r\n              i = g;\r\n              g = j;\r\n            }\r\n            f = f == h | f == k ? f + 2.220446049250313e-16 : f;\r\n            if (!(f < h | f > k)) { break; }\r\n            d = l + 1 | 0;\r\n            if ((d | 0) >= (o | 0)) {\r\n              d = 22;\r\n              break b;\r\n            } else {\r\n              a = l;\r\n              l = d;\r\n              d = a;\r\n            }\r\n          }\r\n          m = i == m | g == m ? m + -2.220446049250313e-16 : m;\r\n          if (i + (g - i) * ((f - h) / (k - h)) > m) { c = c ^ 1; }\r\n          a = l + 1 | 0;\r\n          if ((a | 0) >= (o | 0)) {\r\n            d = 22;\r\n            break;\r\n          } else { d = l; }\r\n        }\r\n        if ((d | 0) == 22) { return c | 0; }\r\n      }\r\n      return 0;\r\n    }\r\n    function Wc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0.0,\r\n        f = 0.0,\r\n        g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0;\r\n      r = b[a >> 2] | 0;\r\n      if (!r) {\r\n        b[c >> 2] = 0;\r\n        b[c + 4 >> 2] = 0;\r\n        b[c + 8 >> 2] = 0;\r\n        b[c + 12 >> 2] = 0;\r\n        b[c + 16 >> 2] = 0;\r\n        b[c + 20 >> 2] = 0;\r\n        b[c + 24 >> 2] = 0;\r\n        b[c + 28 >> 2] = 0;\r\n        return;\r\n      }\r\n      s = c + 8 | 0;\r\n      e[s >> 3] = 1797693134862315708145274.0e284;\r\n      t = c + 24 | 0;\r\n      e[t >> 3] = 1797693134862315708145274.0e284;\r\n      e[c >> 3] = -1797693134862315708145274.0e284;\r\n      u = c + 16 | 0;\r\n      e[u >> 3] = -1797693134862315708145274.0e284;\r\n      if ((r | 0) <= 0) { return; }\r\n      o = b[a + 4 >> 2] | 0;\r\n      l = 1797693134862315708145274.0e284;\r\n      m = -1797693134862315708145274.0e284;\r\n      n = 0;\r\n      a = -1;\r\n      h = 1797693134862315708145274.0e284;\r\n      i = 1797693134862315708145274.0e284;\r\n      k = -1797693134862315708145274.0e284;\r\n      f = -1797693134862315708145274.0e284;\r\n      p = 0;\r\n      while (1) {\r\n        d = +e[o + (p << 4) >> 3];\r\n        j = +e[o + (p << 4) + 8 >> 3];\r\n        a = a + 2 | 0;\r\n        g = +e[o + (((a | 0) == (r | 0) ? 0 : a) << 4) + 8 >> 3];\r\n        if (d < h) {\r\n          e[s >> 3] = d;\r\n          h = d;\r\n        }\r\n        if (j < i) {\r\n          e[t >> 3] = j;\r\n          i = j;\r\n        }\r\n        if (d > k) { e[c >> 3] = d; }else { d = k; }\r\n        if (j > f) {\r\n          e[u >> 3] = j;\r\n          f = j;\r\n        }\r\n        l = j > 0.0 & j < l ? j : l;\r\n        m = j < 0.0 & j > m ? j : m;\r\n        n = n | +q(+(j - g)) > 3.141592653589793;\r\n        a = p + 1 | 0;\r\n        if ((a | 0) == (r | 0)) { break; }else {\r\n          v = p;\r\n          k = d;\r\n          p = a;\r\n          a = v;\r\n        }\r\n      }\r\n      if (!n) { return; }\r\n      e[u >> 3] = m;\r\n      e[t >> 3] = l;\r\n      return;\r\n    }\r\n    function Xc(a) {\r\n      a = a | 0;\r\n      return (a >>> 0 < 4 ? 0 : 15) | 0;\r\n    }\r\n    function Yc(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0,\r\n        n = 0.0,\r\n        o = 0.0,\r\n        p = 0.0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0,\r\n        x = 0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0;\r\n      r = b[a >> 2] | 0;\r\n      if (r) {\r\n        s = c + 8 | 0;\r\n        e[s >> 3] = 1797693134862315708145274.0e284;\r\n        t = c + 24 | 0;\r\n        e[t >> 3] = 1797693134862315708145274.0e284;\r\n        e[c >> 3] = -1797693134862315708145274.0e284;\r\n        u = c + 16 | 0;\r\n        e[u >> 3] = -1797693134862315708145274.0e284;\r\n        if ((r | 0) > 0) {\r\n          g = b[a + 4 >> 2] | 0;\r\n          o = 1797693134862315708145274.0e284;\r\n          p = -1797693134862315708145274.0e284;\r\n          f = 0;\r\n          d = -1;\r\n          k = 1797693134862315708145274.0e284;\r\n          l = 1797693134862315708145274.0e284;\r\n          n = -1797693134862315708145274.0e284;\r\n          i = -1797693134862315708145274.0e284;\r\n          v = 0;\r\n          while (1) {\r\n            h = +e[g + (v << 4) >> 3];\r\n            m = +e[g + (v << 4) + 8 >> 3];\r\n            z = d + 2 | 0;\r\n            j = +e[g + (((z | 0) == (r | 0) ? 0 : z) << 4) + 8 >> 3];\r\n            if (h < k) {\r\n              e[s >> 3] = h;\r\n              k = h;\r\n            }\r\n            if (m < l) {\r\n              e[t >> 3] = m;\r\n              l = m;\r\n            }\r\n            if (h > n) { e[c >> 3] = h; }else { h = n; }\r\n            if (m > i) {\r\n              e[u >> 3] = m;\r\n              i = m;\r\n            }\r\n            o = m > 0.0 & m < o ? m : o;\r\n            p = m < 0.0 & m > p ? m : p;\r\n            f = f | +q(+(m - j)) > 3.141592653589793;\r\n            d = v + 1 | 0;\r\n            if ((d | 0) == (r | 0)) { break; }else {\r\n              z = v;\r\n              n = h;\r\n              v = d;\r\n              d = z;\r\n            }\r\n          }\r\n          if (f) {\r\n            e[u >> 3] = p;\r\n            e[t >> 3] = o;\r\n          }\r\n        }\r\n      } else {\r\n        b[c >> 2] = 0;\r\n        b[c + 4 >> 2] = 0;\r\n        b[c + 8 >> 2] = 0;\r\n        b[c + 12 >> 2] = 0;\r\n        b[c + 16 >> 2] = 0;\r\n        b[c + 20 >> 2] = 0;\r\n        b[c + 24 >> 2] = 0;\r\n        b[c + 28 >> 2] = 0;\r\n      }\r\n      z = a + 8 | 0;\r\n      d = b[z >> 2] | 0;\r\n      if ((d | 0) <= 0) { return; }\r\n      y = a + 12 | 0;\r\n      x = 0;\r\n      do {\r\n        g = b[y >> 2] | 0;\r\n        f = x;\r\n        x = x + 1 | 0;\r\n        t = c + (x << 5) | 0;\r\n        u = b[g + (f << 3) >> 2] | 0;\r\n        if (u) {\r\n          v = c + (x << 5) + 8 | 0;\r\n          e[v >> 3] = 1797693134862315708145274.0e284;\r\n          a = c + (x << 5) + 24 | 0;\r\n          e[a >> 3] = 1797693134862315708145274.0e284;\r\n          e[t >> 3] = -1797693134862315708145274.0e284;\r\n          w = c + (x << 5) + 16 | 0;\r\n          e[w >> 3] = -1797693134862315708145274.0e284;\r\n          if ((u | 0) > 0) {\r\n            r = b[g + (f << 3) + 4 >> 2] | 0;\r\n            o = 1797693134862315708145274.0e284;\r\n            p = -1797693134862315708145274.0e284;\r\n            g = 0;\r\n            f = -1;\r\n            s = 0;\r\n            k = 1797693134862315708145274.0e284;\r\n            l = 1797693134862315708145274.0e284;\r\n            m = -1797693134862315708145274.0e284;\r\n            i = -1797693134862315708145274.0e284;\r\n            while (1) {\r\n              h = +e[r + (s << 4) >> 3];\r\n              n = +e[r + (s << 4) + 8 >> 3];\r\n              f = f + 2 | 0;\r\n              j = +e[r + (((f | 0) == (u | 0) ? 0 : f) << 4) + 8 >> 3];\r\n              if (h < k) {\r\n                e[v >> 3] = h;\r\n                k = h;\r\n              }\r\n              if (n < l) {\r\n                e[a >> 3] = n;\r\n                l = n;\r\n              }\r\n              if (h > m) { e[t >> 3] = h; }else { h = m; }\r\n              if (n > i) {\r\n                e[w >> 3] = n;\r\n                i = n;\r\n              }\r\n              o = n > 0.0 & n < o ? n : o;\r\n              p = n < 0.0 & n > p ? n : p;\r\n              g = g | +q(+(n - j)) > 3.141592653589793;\r\n              f = s + 1 | 0;\r\n              if ((f | 0) == (u | 0)) { break; }else {\r\n                A = s;\r\n                s = f;\r\n                m = h;\r\n                f = A;\r\n              }\r\n            }\r\n            if (g) {\r\n              e[w >> 3] = p;\r\n              e[a >> 3] = o;\r\n            }\r\n          }\r\n        } else {\r\n          b[t >> 2] = 0;\r\n          b[t + 4 >> 2] = 0;\r\n          b[t + 8 >> 2] = 0;\r\n          b[t + 12 >> 2] = 0;\r\n          b[t + 16 >> 2] = 0;\r\n          b[t + 20 >> 2] = 0;\r\n          b[t + 24 >> 2] = 0;\r\n          b[t + 28 >> 2] = 0;\r\n          d = b[z >> 2] | 0;\r\n        }\r\n      } while ((x | 0) < (d | 0));\r\n      return;\r\n    }\r\n    function Zc(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      if (!(Vc(a, c, d) | 0)) {\r\n        f = 0;\r\n        return f | 0;\r\n      }\r\n      f = a + 8 | 0;\r\n      if ((b[f >> 2] | 0) <= 0) {\r\n        f = 1;\r\n        return f | 0;\r\n      }\r\n      e = a + 12 | 0;\r\n      a = 0;\r\n      while (1) {\r\n        g = a;\r\n        a = a + 1 | 0;\r\n        if (Vc((b[e >> 2] | 0) + (g << 3) | 0, c + (a << 5) | 0, d) | 0) {\r\n          a = 0;\r\n          e = 6;\r\n          break;\r\n        }\r\n        if ((a | 0) >= (b[f >> 2] | 0)) {\r\n          a = 1;\r\n          e = 6;\r\n          break;\r\n        }\r\n      }\r\n      if ((e | 0) == 6) { return a | 0; }\r\n      return 0;\r\n    }\r\n    function _c(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0;\r\n      k = T;\r\n      T = T + 16 | 0;\r\n      i = k;\r\n      h = d + 8 | 0;\r\n      if (!(Vc(a, c, h) | 0)) {\r\n        j = 0;\r\n        T = k;\r\n        return j | 0;\r\n      }\r\n      j = a + 8 | 0;\r\n      a: do { if ((b[j >> 2] | 0) > 0) {\r\n        g = a + 12 | 0;\r\n        f = 0;\r\n        while (1) {\r\n          l = f;\r\n          f = f + 1 | 0;\r\n          if (Vc((b[g >> 2] | 0) + (l << 3) | 0, c + (f << 5) | 0, h) | 0) {\r\n            f = 0;\r\n            break;\r\n          }\r\n          if ((f | 0) >= (b[j >> 2] | 0)) { break a; }\r\n        }\r\n        T = k;\r\n        return f | 0;\r\n      } } while (0);\r\n      if ($c(a, c, d, e) | 0) {\r\n        l = 0;\r\n        T = k;\r\n        return l | 0;\r\n      }\r\n      b[i >> 2] = b[d >> 2];\r\n      b[i + 4 >> 2] = h;\r\n      f = b[j >> 2] | 0;\r\n      b: do { if ((f | 0) > 0) {\r\n        a = a + 12 | 0;\r\n        h = 0;\r\n        g = f;\r\n        while (1) {\r\n          f = b[a >> 2] | 0;\r\n          if ((b[f + (h << 3) >> 2] | 0) > 0) {\r\n            if (Vc(i, e, b[f + (h << 3) + 4 >> 2] | 0) | 0) {\r\n              f = 0;\r\n              break b;\r\n            }\r\n            f = h + 1 | 0;\r\n            if ($c((b[a >> 2] | 0) + (h << 3) | 0, c + (f << 5) | 0, d, e) | 0) {\r\n              f = 0;\r\n              break b;\r\n            }\r\n            g = b[j >> 2] | 0;\r\n          } else { f = h + 1 | 0; }\r\n          if ((f | 0) < (g | 0)) { h = f; }else {\r\n            f = 1;\r\n            break;\r\n          }\r\n        }\r\n      } else { f = 1; } } while (0);\r\n      l = f;\r\n      T = k;\r\n      return l | 0;\r\n    }\r\n    function $c(a, c, d, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0,\r\n        n = 0.0,\r\n        o = 0.0,\r\n        p = 0.0,\r\n        q = 0.0,\r\n        r = 0.0,\r\n        s = 0.0,\r\n        t = 0.0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0.0,\r\n        x = 0.0,\r\n        y = 0,\r\n        z = 0,\r\n        A = 0;\r\n      y = T;\r\n      T = T + 176 | 0;\r\n      u = y + 172 | 0;\r\n      g = y + 168 | 0;\r\n      v = y;\r\n      if (!(Da(c, f) | 0)) {\r\n        a = 0;\r\n        T = y;\r\n        return a | 0;\r\n      }\r\n      Ea(c, f, u, g);\r\n      Wd(v | 0, d | 0, 168) | 0;\r\n      if ((b[d >> 2] | 0) > 0) {\r\n        c = 0;\r\n        do {\r\n          z = v + 8 + (c << 4) + 8 | 0;\r\n          t = +kc(+e[z >> 3], b[g >> 2] | 0);\r\n          e[z >> 3] = t;\r\n          c = c + 1 | 0;\r\n        } while ((c | 0) < (b[d >> 2] | 0));\r\n      }\r\n      r = +e[f >> 3];\r\n      s = +e[f + 8 >> 3];\r\n      t = +kc(+e[f + 16 >> 3], b[g >> 2] | 0);\r\n      p = +kc(+e[f + 24 >> 3], b[g >> 2] | 0);\r\n      a: do { if ((b[a >> 2] | 0) > 0) {\r\n        f = a + 4 | 0;\r\n        g = b[v >> 2] | 0;\r\n        if ((g | 0) <= 0) {\r\n          c = 0;\r\n          while (1) {\r\n            c = c + 1 | 0;\r\n            if ((c | 0) >= (b[a >> 2] | 0)) {\r\n              c = 0;\r\n              break a;\r\n            }\r\n          }\r\n        }\r\n        d = 0;\r\n        while (1) {\r\n          c = b[f >> 2] | 0;\r\n          o = +e[c + (d << 4) >> 3];\r\n          q = +kc(+e[c + (d << 4) + 8 >> 3], b[u >> 2] | 0);\r\n          c = b[f >> 2] | 0;\r\n          d = d + 1 | 0;\r\n          z = (d | 0) % (b[a >> 2] | 0) | 0;\r\n          h = +e[c + (z << 4) >> 3];\r\n          i = +kc(+e[c + (z << 4) + 8 >> 3], b[u >> 2] | 0);\r\n          if (((!(o >= r) | !(h >= r) ? !(o <= s) | !(h <= s) : 0) ? !(q <= p) | !(i <= p) : 0) ? !(q >= t) | !(i >= t) : 0) {\r\n            n = h - o;\r\n            l = i - q;\r\n            c = 0;\r\n            do {\r\n              A = c;\r\n              c = c + 1 | 0;\r\n              z = (c | 0) == (g | 0) ? 0 : c;\r\n              h = +e[v + 8 + (A << 4) + 8 >> 3];\r\n              i = +e[v + 8 + (z << 4) + 8 >> 3] - h;\r\n              j = +e[v + 8 + (A << 4) >> 3];\r\n              k = +e[v + 8 + (z << 4) >> 3] - j;\r\n              m = n * i - l * k;\r\n              if ((m != 0.0 ? (w = q - h, x = o - j, k = (w * k - i * x) / m, !(k < 0.0 | k > 1.0)) : 0) ? (m = (n * w - l * x) / m, m >= 0.0 & m <= 1.0) : 0) {\r\n                c = 1;\r\n                break a;\r\n              }\r\n            } while ((c | 0) < (g | 0));\r\n          }\r\n          if ((d | 0) >= (b[a >> 2] | 0)) {\r\n            c = 0;\r\n            break;\r\n          }\r\n        }\r\n      } else { c = 0; } } while (0);\r\n      A = c;\r\n      T = y;\r\n      return A | 0;\r\n    }\r\n    function ad(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      if ($c(a, c, d, e) | 0) {\r\n        g = 1;\r\n        return g | 0;\r\n      }\r\n      g = a + 8 | 0;\r\n      if ((b[g >> 2] | 0) <= 0) {\r\n        g = 0;\r\n        return g | 0;\r\n      }\r\n      f = a + 12 | 0;\r\n      a = 0;\r\n      while (1) {\r\n        h = a;\r\n        a = a + 1 | 0;\r\n        if ($c((b[f >> 2] | 0) + (h << 3) | 0, c + (a << 5) | 0, d, e) | 0) {\r\n          a = 1;\r\n          f = 6;\r\n          break;\r\n        }\r\n        if ((a | 0) >= (b[g >> 2] | 0)) {\r\n          a = 0;\r\n          f = 6;\r\n          break;\r\n        }\r\n      }\r\n      if ((f | 0) == 6) { return a | 0; }\r\n      return 0;\r\n    }\r\n    function bd() {\r\n      return 8;\r\n    }\r\n    function cd() {\r\n      return 16;\r\n    }\r\n    function dd() {\r\n      return 168;\r\n    }\r\n    function ed() {\r\n      return 8;\r\n    }\r\n    function fd() {\r\n      return 16;\r\n    }\r\n    function gd() {\r\n      return 12;\r\n    }\r\n    function hd() {\r\n      return 8;\r\n    }\r\n    function id(a) {\r\n      a = a | 0;\r\n      return +(+((b[a >> 2] | 0) >>> 0) + 4294967296.0 * +(b[a + 4 >> 2] | 0));\r\n    }\r\n    function jd(a) {\r\n      a = a | 0;\r\n      var b = 0.0,\r\n        c = 0.0;\r\n      c = +e[a >> 3];\r\n      b = +e[a + 8 >> 3];\r\n      return + +r(+(c * c + b * b));\r\n    }\r\n    function kd(a, b, c, d, f) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      f = f | 0;\r\n      var g = 0.0,\r\n        h = 0.0,\r\n        i = 0.0,\r\n        j = 0.0,\r\n        k = 0.0,\r\n        l = 0.0,\r\n        m = 0.0,\r\n        n = 0.0;\r\n      k = +e[a >> 3];\r\n      j = +e[b >> 3] - k;\r\n      i = +e[a + 8 >> 3];\r\n      h = +e[b + 8 >> 3] - i;\r\n      m = +e[c >> 3];\r\n      g = +e[d >> 3] - m;\r\n      n = +e[c + 8 >> 3];\r\n      l = +e[d + 8 >> 3] - n;\r\n      g = (g * (i - n) - (k - m) * l) / (j * l - h * g);\r\n      e[f >> 3] = k + j * g;\r\n      e[f + 8 >> 3] = i + h * g;\r\n      return;\r\n    }\r\n    function ld(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      if (!(+q(+(+e[a >> 3] - +e[b >> 3])) < 1.1920928955078125e-07)) {\r\n        b = 0;\r\n        return b | 0;\r\n      }\r\n      b = +q(+(+e[a + 8 >> 3] - +e[b + 8 >> 3])) < 1.1920928955078125e-07;\r\n      return b | 0;\r\n    }\r\n    function md(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0;\r\n      f = +e[a >> 3] - +e[b >> 3];\r\n      d = +e[a + 8 >> 3] - +e[b + 8 >> 3];\r\n      c = +e[a + 16 >> 3] - +e[b + 16 >> 3];\r\n      return +(f * f + d * d + c * c);\r\n    }\r\n    function nd(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0.0,\r\n        d = 0.0,\r\n        f = 0.0;\r\n      c = +e[a >> 3];\r\n      d = +t(+c);\r\n      c = +u(+c);\r\n      e[b + 16 >> 3] = c;\r\n      c = +e[a + 8 >> 3];\r\n      f = d * +t(+c);\r\n      e[b >> 3] = f;\r\n      c = d * +u(+c);\r\n      e[b + 8 >> 3] = c;\r\n      return;\r\n    }\r\n    function od(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      g = T;\r\n      T = T + 16 | 0;\r\n      f = g;\r\n      e = Hb(a, c) | 0;\r\n      if ((d + -1 | 0) >>> 0 > 5) {\r\n        f = -1;\r\n        T = g;\r\n        return f | 0;\r\n      }\r\n      e = (e | 0) != 0;\r\n      if ((d | 0) == 1 & e) {\r\n        f = -1;\r\n        T = g;\r\n        return f | 0;\r\n      }\r\n      do { if (!(pd(a, c, f) | 0)) {\r\n        if (e) {\r\n          e = ((b[26352 + (d << 2) >> 2] | 0) + 5 - (b[f >> 2] | 0) | 0) % 5 | 0;\r\n          break;\r\n        } else {\r\n          e = ((b[26384 + (d << 2) >> 2] | 0) + 6 - (b[f >> 2] | 0) | 0) % 6 | 0;\r\n          break;\r\n        }\r\n      } else { e = -1; } } while (0);\r\n      f = e;\r\n      T = g;\r\n      return f | 0;\r\n    }\r\n    function pd(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      k = T;\r\n      T = T + 32 | 0;\r\n      h = k + 16 | 0;\r\n      i = k;\r\n      e = Yb(a, c, h) | 0;\r\n      if (e | 0) {\r\n        d = e;\r\n        T = k;\r\n        return d | 0;\r\n      }\r\n      g = Cb(a, c) | 0;\r\n      j = Pb(a, c) | 0;\r\n      sa(g, i);\r\n      e = ta(g, b[h >> 2] | 0) | 0;\r\n      a: do { if (oa(g) | 0) {\r\n        do { switch (g | 0) {\r\n          case 4:\r\n            {\r\n              a = 0;\r\n              break;\r\n            }\r\n          case 14:\r\n            {\r\n              a = 1;\r\n              break;\r\n            }\r\n          case 24:\r\n            {\r\n              a = 2;\r\n              break;\r\n            }\r\n          case 38:\r\n            {\r\n              a = 3;\r\n              break;\r\n            }\r\n          case 49:\r\n            {\r\n              a = 4;\r\n              break;\r\n            }\r\n          case 58:\r\n            {\r\n              a = 5;\r\n              break;\r\n            }\r\n          case 63:\r\n            {\r\n              a = 6;\r\n              break;\r\n            }\r\n          case 72:\r\n            {\r\n              a = 7;\r\n              break;\r\n            }\r\n          case 83:\r\n            {\r\n              a = 8;\r\n              break;\r\n            }\r\n          case 97:\r\n            {\r\n              a = 9;\r\n              break;\r\n            }\r\n          case 107:\r\n            {\r\n              a = 10;\r\n              break;\r\n            }\r\n          case 117:\r\n            {\r\n              a = 11;\r\n              break;\r\n            }\r\n          default:\r\n            {\r\n              e = 1;\r\n              break a;\r\n            }\r\n        } } while (0);\r\n        f = b[26416 + (a * 24 | 0) + 8 >> 2] | 0;\r\n        c = b[26416 + (a * 24 | 0) + 16 >> 2] | 0;\r\n        a = b[h >> 2] | 0;\r\n        if ((a | 0) != (b[i >> 2] | 0)) {\r\n          i = pa(g) | 0;\r\n          a = b[h >> 2] | 0;\r\n          if (i | (a | 0) == (c | 0)) { e = (e + 1 | 0) % 6 | 0; }\r\n        }\r\n        if ((j | 0) == 3 & (a | 0) == (c | 0)) {\r\n          e = (e + 5 | 0) % 6 | 0;\r\n          f = 22;\r\n          break;\r\n        }\r\n        if ((j | 0) == 5 & (a | 0) == (f | 0)) {\r\n          e = (e + 1 | 0) % 6 | 0;\r\n          f = 22;\r\n        } else { f = 22; }\r\n      } else { f = 22; } } while (0);\r\n      if ((f | 0) == 22) {\r\n        b[d >> 2] = e;\r\n        e = 0;\r\n      }\r\n      d = e;\r\n      T = k;\r\n      return d | 0;\r\n    }\r\n    function qd(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0;\r\n      u = T;\r\n      T = T + 32 | 0;\r\n      t = u + 24 | 0;\r\n      r = u + 20 | 0;\r\n      p = u + 8 | 0;\r\n      o = u + 16 | 0;\r\n      n = u;\r\n      j = (Hb(a, c) | 0) == 0;\r\n      j = j ? 6 : 5;\r\n      l = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      l = l & 15;\r\n      if (j >>> 0 <= d >>> 0) {\r\n        e = 2;\r\n        T = u;\r\n        return e | 0;\r\n      }\r\n      m = (l | 0) == 0;\r\n      if (!m ? (q = Rd(7, 0, (l ^ 15) * 3 | 0) | 0, (q & a | 0) == 0 & ((H() | 0) & c | 0) == 0) : 0) { f = d; }else { g = 4; }\r\n      a: do { if ((g | 0) == 4) {\r\n        f = (Hb(a, c) | 0) != 0;\r\n        if (((f ? 4 : 5) | 0) < (d | 0)) {\r\n          e = 1;\r\n          T = u;\r\n          return e | 0;\r\n        }\r\n        if (pd(a, c, t) | 0) {\r\n          e = 1;\r\n          T = u;\r\n          return e | 0;\r\n        }\r\n        g = (b[t >> 2] | 0) + d | 0;\r\n        if (f) { f = 26704 + (((g | 0) % 5 | 0) << 2) | 0; }else { f = 26736 + (((g | 0) % 6 | 0) << 2) | 0; }\r\n        q = b[f >> 2] | 0;\r\n        if ((q | 0) == 7) {\r\n          e = 1;\r\n          T = u;\r\n          return e | 0;\r\n        }\r\n        b[r >> 2] = 0;\r\n        f = ea(a, c, q, r, p) | 0;\r\n        do { if (!f) {\r\n          i = p;\r\n          k = b[i >> 2] | 0;\r\n          i = b[i + 4 >> 2] | 0;\r\n          h = i >>> 0 < c >>> 0 | (i | 0) == (c | 0) & k >>> 0 < a >>> 0;\r\n          g = h ? k : a;\r\n          h = h ? i : c;\r\n          if (!m ? (m = Rd(7, 0, (l ^ 15) * 3 | 0) | 0, (k & m | 0) == 0 & (i & (H() | 0) | 0) == 0) : 0) { f = d; }else {\r\n            i = (d + -1 + j | 0) % (j | 0) | 0;\r\n            f = Hb(a, c) | 0;\r\n            if ((i | 0) < 0) { I(27795, 27797, 246, 27806); }\r\n            j = (f | 0) != 0;\r\n            if (((j ? 4 : 5) | 0) < (i | 0)) { I(27795, 27797, 246, 27806); }\r\n            if (pd(a, c, t) | 0) { I(27795, 27797, 246, 27806); }\r\n            f = (b[t >> 2] | 0) + i | 0;\r\n            if (j) { f = 26704 + (((f | 0) % 5 | 0) << 2) | 0; }else { f = 26736 + (((f | 0) % 6 | 0) << 2) | 0; }\r\n            i = b[f >> 2] | 0;\r\n            if ((i | 0) == 7) { I(27795, 27797, 246, 27806); }\r\n            b[o >> 2] = 0;\r\n            f = ea(a, c, i, o, n) | 0;\r\n            if (f | 0) { break; }\r\n            k = n;\r\n            j = b[k >> 2] | 0;\r\n            k = b[k + 4 >> 2] | 0;\r\n            do { if (k >>> 0 < h >>> 0 | (k | 0) == (h | 0) & j >>> 0 < g >>> 0) {\r\n              if (!(Hb(j, k) | 0)) { g = b[26800 + ((((b[o >> 2] | 0) + (b[26768 + (i << 2) >> 2] | 0) | 0) % 6 | 0) << 2) >> 2] | 0; }else { g = ia(j, k, a, c) | 0; }\r\n              f = Hb(j, k) | 0;\r\n              if ((g + -1 | 0) >>> 0 > 5) {\r\n                f = -1;\r\n                g = j;\r\n                h = k;\r\n                break;\r\n              }\r\n              f = (f | 0) != 0;\r\n              if ((g | 0) == 1 & f) {\r\n                f = -1;\r\n                g = j;\r\n                h = k;\r\n                break;\r\n              }\r\n              do { if (!(pd(j, k, t) | 0)) {\r\n                if (f) {\r\n                  f = ((b[26352 + (g << 2) >> 2] | 0) + 5 - (b[t >> 2] | 0) | 0) % 5 | 0;\r\n                  break;\r\n                } else {\r\n                  f = ((b[26384 + (g << 2) >> 2] | 0) + 6 - (b[t >> 2] | 0) | 0) % 6 | 0;\r\n                  break;\r\n                }\r\n              } else { f = -1; } } while (0);\r\n              g = j;\r\n              h = k;\r\n            } else { f = d; } } while (0);\r\n            i = p;\r\n            k = b[i >> 2] | 0;\r\n            i = b[i + 4 >> 2] | 0;\r\n          }\r\n          if ((g | 0) == (k | 0) & (h | 0) == (i | 0)) {\r\n            j = (Hb(k, i) | 0) != 0;\r\n            if (j) { a = ia(k, i, a, c) | 0; }else { a = b[26800 + ((((b[r >> 2] | 0) + (b[26768 + (q << 2) >> 2] | 0) | 0) % 6 | 0) << 2) >> 2] | 0; }\r\n            f = Hb(k, i) | 0;\r\n            if ((a + -1 | 0) >>> 0 <= 5 ? (s = (f | 0) != 0, !((a | 0) == 1 & s)) : 0) {\r\n              do { if (!(pd(k, i, t) | 0)) {\r\n                if (s) {\r\n                  f = ((b[26352 + (a << 2) >> 2] | 0) + 5 - (b[t >> 2] | 0) | 0) % 5 | 0;\r\n                  break;\r\n                } else {\r\n                  f = ((b[26384 + (a << 2) >> 2] | 0) + 6 - (b[t >> 2] | 0) | 0) % 6 | 0;\r\n                  break;\r\n                }\r\n              } else { f = -1; } } while (0);\r\n            } else { f = -1; }\r\n            f = f + 1 | 0;\r\n            f = (f | 0) == 6 | j & (f | 0) == 5 ? 0 : f;\r\n          }\r\n          c = h;\r\n          a = g;\r\n          break a;\r\n        } } while (0);\r\n        e = f;\r\n        T = u;\r\n        return e | 0;\r\n      } } while (0);\r\n      s = Rd(f | 0, 0, 56) | 0;\r\n      t = H() | 0 | c & -2130706433 | 536870912;\r\n      b[e >> 2] = s | a;\r\n      b[e + 4 >> 2] = t;\r\n      e = 0;\r\n      T = u;\r\n      return e | 0;\r\n    }\r\n    function rd(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      g = (Hb(a, c) | 0) == 0;\r\n      e = qd(a, c, 0, d) | 0;\r\n      f = (e | 0) == 0;\r\n      if (g) {\r\n        if (!f) {\r\n          g = e;\r\n          return g | 0;\r\n        }\r\n        e = qd(a, c, 1, d + 8 | 0) | 0;\r\n        if (e | 0) {\r\n          g = e;\r\n          return g | 0;\r\n        }\r\n        e = qd(a, c, 2, d + 16 | 0) | 0;\r\n        if (e | 0) {\r\n          g = e;\r\n          return g | 0;\r\n        }\r\n        e = qd(a, c, 3, d + 24 | 0) | 0;\r\n        if (e | 0) {\r\n          g = e;\r\n          return g | 0;\r\n        }\r\n        e = qd(a, c, 4, d + 32 | 0) | 0;\r\n        if (!e) { return qd(a, c, 5, d + 40 | 0) | 0; }else {\r\n          g = e;\r\n          return g | 0;\r\n        }\r\n      }\r\n      if (!f) {\r\n        g = e;\r\n        return g | 0;\r\n      }\r\n      e = qd(a, c, 1, d + 8 | 0) | 0;\r\n      if (e | 0) {\r\n        g = e;\r\n        return g | 0;\r\n      }\r\n      e = qd(a, c, 2, d + 16 | 0) | 0;\r\n      if (e | 0) {\r\n        g = e;\r\n        return g | 0;\r\n      }\r\n      e = qd(a, c, 3, d + 24 | 0) | 0;\r\n      if (e | 0) {\r\n        g = e;\r\n        return g | 0;\r\n      }\r\n      e = qd(a, c, 4, d + 32 | 0) | 0;\r\n      if (e | 0) {\r\n        g = e;\r\n        return g | 0;\r\n      }\r\n      g = d + 40 | 0;\r\n      b[g >> 2] = 0;\r\n      b[g + 4 >> 2] = 0;\r\n      g = 0;\r\n      return g | 0;\r\n    }\r\n    function sd(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = T;\r\n      T = T + 192 | 0;\r\n      f = j;\r\n      g = j + 168 | 0;\r\n      h = Qd(a | 0, c | 0, 56) | 0;\r\n      H() | 0;\r\n      h = h & 7;\r\n      i = c & -2130706433 | 134217728;\r\n      e = Yb(a, i, g) | 0;\r\n      if (e | 0) {\r\n        i = e;\r\n        T = j;\r\n        return i | 0;\r\n      }\r\n      c = Qd(a | 0, c | 0, 52) | 0;\r\n      H() | 0;\r\n      c = c & 15;\r\n      if (!(Hb(a, i) | 0)) { zb(g, c, h, 1, f); }else { vb(g, c, h, 1, f); }\r\n      i = f + 8 | 0;\r\n      b[d >> 2] = b[i >> 2];\r\n      b[d + 4 >> 2] = b[i + 4 >> 2];\r\n      b[d + 8 >> 2] = b[i + 8 >> 2];\r\n      b[d + 12 >> 2] = b[i + 12 >> 2];\r\n      i = 0;\r\n      T = j;\r\n      return i | 0;\r\n    }\r\n    function td(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      d = f;\r\n      if (!(0 == 0 & (c & 2013265920 | 0) == 536870912)) {\r\n        e = 0;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      e = c & -2130706433 | 134217728;\r\n      if (!(Db(a, e) | 0)) {\r\n        e = 0;\r\n        T = f;\r\n        return e | 0;\r\n      }\r\n      g = Qd(a | 0, c | 0, 56) | 0;\r\n      H() | 0;\r\n      g = (qd(a, e, g & 7, d) | 0) == 0;\r\n      e = d;\r\n      e = g & ((b[e >> 2] | 0) == (a | 0) ? (b[e + 4 >> 2] | 0) == (c | 0) : 0) & 1;\r\n      T = f;\r\n      return e | 0;\r\n    }\r\n    function ud(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0;\r\n      if ((c | 0) > 0) {\r\n        e = Fd(c, 4) | 0;\r\n        b[a >> 2] = e;\r\n        if (!e) { I(27819, 27842, 40, 27856); }\r\n      } else { b[a >> 2] = 0; }\r\n      b[a + 4 >> 2] = c;\r\n      b[a + 8 >> 2] = 0;\r\n      b[a + 12 >> 2] = d;\r\n      return;\r\n    }\r\n    function vd(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      g = a + 4 | 0;\r\n      h = a + 12 | 0;\r\n      i = a + 8 | 0;\r\n      a: while (1) {\r\n        d = b[g >> 2] | 0;\r\n        c = 0;\r\n        while (1) {\r\n          if ((c | 0) >= (d | 0)) { break a; }\r\n          f = b[a >> 2] | 0;\r\n          j = b[f + (c << 2) >> 2] | 0;\r\n          if (!j) { c = c + 1 | 0; }else { break; }\r\n        }\r\n        c = f + (~~(+q(+(+s(10.0, + +(15 - (b[h >> 2] | 0) | 0)) * (+e[j >> 3] + +e[j + 8 >> 3]))) % +(d | 0)) >>> 0 << 2) | 0;\r\n        d = b[c >> 2] | 0;\r\n        b: do { if (d | 0) {\r\n          f = j + 32 | 0;\r\n          if ((d | 0) == (j | 0)) { b[c >> 2] = b[f >> 2]; }else {\r\n            d = d + 32 | 0;\r\n            c = b[d >> 2] | 0;\r\n            if (!c) { break; }\r\n            while (1) {\r\n              if ((c | 0) == (j | 0)) { break; }\r\n              d = c + 32 | 0;\r\n              c = b[d >> 2] | 0;\r\n              if (!c) { break b; }\r\n            }\r\n            b[d >> 2] = b[f >> 2];\r\n          }\r\n          Ed(j);\r\n          b[i >> 2] = (b[i >> 2] | 0) + -1;\r\n        } } while (0);\r\n      }\r\n      Ed(b[a >> 2] | 0);\r\n      return;\r\n    }\r\n    function wd(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      e = b[a + 4 >> 2] | 0;\r\n      d = 0;\r\n      while (1) {\r\n        if ((d | 0) >= (e | 0)) {\r\n          c = 0;\r\n          d = 4;\r\n          break;\r\n        }\r\n        c = b[(b[a >> 2] | 0) + (d << 2) >> 2] | 0;\r\n        if (!c) { d = d + 1 | 0; }else {\r\n          d = 4;\r\n          break;\r\n        }\r\n      }\r\n      if ((d | 0) == 4) { return c | 0; }\r\n      return 0;\r\n    }\r\n    function xd(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      d = ~~(+q(+(+s(10.0, + +(15 - (b[a + 12 >> 2] | 0) | 0)) * (+e[c >> 3] + +e[c + 8 >> 3]))) % +(b[a + 4 >> 2] | 0)) >>> 0;\r\n      d = (b[a >> 2] | 0) + (d << 2) | 0;\r\n      f = b[d >> 2] | 0;\r\n      if (!f) {\r\n        h = 1;\r\n        return h | 0;\r\n      }\r\n      h = c + 32 | 0;\r\n      do { if ((f | 0) != (c | 0)) {\r\n        d = b[f + 32 >> 2] | 0;\r\n        if (!d) {\r\n          h = 1;\r\n          return h | 0;\r\n        }\r\n        g = d;\r\n        while (1) {\r\n          if ((g | 0) == (c | 0)) {\r\n            g = 8;\r\n            break;\r\n          }\r\n          d = b[g + 32 >> 2] | 0;\r\n          if (!d) {\r\n            d = 1;\r\n            g = 10;\r\n            break;\r\n          } else {\r\n            f = g;\r\n            g = d;\r\n          }\r\n        }\r\n        if ((g | 0) == 8) {\r\n          b[f + 32 >> 2] = b[h >> 2];\r\n          break;\r\n        } else if ((g | 0) == 10) { return d | 0; }\r\n      } else { b[d >> 2] = b[h >> 2]; } } while (0);\r\n      Ed(c);\r\n      h = a + 8 | 0;\r\n      b[h >> 2] = (b[h >> 2] | 0) + -1;\r\n      h = 0;\r\n      return h | 0;\r\n    }\r\n    function yd(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = Dd(40) | 0;\r\n      if (!h) { I(27872, 27842, 98, 27885); }\r\n      b[h >> 2] = b[c >> 2];\r\n      b[h + 4 >> 2] = b[c + 4 >> 2];\r\n      b[h + 8 >> 2] = b[c + 8 >> 2];\r\n      b[h + 12 >> 2] = b[c + 12 >> 2];\r\n      g = h + 16 | 0;\r\n      b[g >> 2] = b[d >> 2];\r\n      b[g + 4 >> 2] = b[d + 4 >> 2];\r\n      b[g + 8 >> 2] = b[d + 8 >> 2];\r\n      b[g + 12 >> 2] = b[d + 12 >> 2];\r\n      b[h + 32 >> 2] = 0;\r\n      g = ~~(+q(+(+s(10.0, + +(15 - (b[a + 12 >> 2] | 0) | 0)) * (+e[c >> 3] + +e[c + 8 >> 3]))) % +(b[a + 4 >> 2] | 0)) >>> 0;\r\n      g = (b[a >> 2] | 0) + (g << 2) | 0;\r\n      f = b[g >> 2] | 0;\r\n      do { if (!f) { b[g >> 2] = h; }else {\r\n        while (1) {\r\n          if (jc(f, c) | 0 ? jc(f + 16 | 0, d) | 0 : 0) { break; }\r\n          g = b[f + 32 >> 2] | 0;\r\n          f = (g | 0) == 0 ? f : g;\r\n          if (!(b[f + 32 >> 2] | 0)) {\r\n            i = 10;\r\n            break;\r\n          }\r\n        }\r\n        if ((i | 0) == 10) {\r\n          b[f + 32 >> 2] = h;\r\n          break;\r\n        }\r\n        Ed(h);\r\n        i = f;\r\n        return i | 0;\r\n      } } while (0);\r\n      i = a + 8 | 0;\r\n      b[i >> 2] = (b[i >> 2] | 0) + 1;\r\n      i = h;\r\n      return i | 0;\r\n    }\r\n    function zd(a, c, d) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var f = 0,\r\n        g = 0;\r\n      g = ~~(+q(+(+s(10.0, + +(15 - (b[a + 12 >> 2] | 0) | 0)) * (+e[c >> 3] + +e[c + 8 >> 3]))) % +(b[a + 4 >> 2] | 0)) >>> 0;\r\n      g = b[(b[a >> 2] | 0) + (g << 2) >> 2] | 0;\r\n      if (!g) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      if (!d) {\r\n        a = g;\r\n        while (1) {\r\n          if (jc(a, c) | 0) {\r\n            f = 10;\r\n            break;\r\n          }\r\n          a = b[a + 32 >> 2] | 0;\r\n          if (!a) {\r\n            a = 0;\r\n            f = 10;\r\n            break;\r\n          }\r\n        }\r\n        if ((f | 0) == 10) { return a | 0; }\r\n      }\r\n      a = g;\r\n      while (1) {\r\n        if (jc(a, c) | 0 ? jc(a + 16 | 0, d) | 0 : 0) {\r\n          f = 10;\r\n          break;\r\n        }\r\n        a = b[a + 32 >> 2] | 0;\r\n        if (!a) {\r\n          a = 0;\r\n          f = 10;\r\n          break;\r\n        }\r\n      }\r\n      if ((f | 0) == 10) { return a | 0; }\r\n      return 0;\r\n    }\r\n    function Ad(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0;\r\n      d = ~~(+q(+(+s(10.0, + +(15 - (b[a + 12 >> 2] | 0) | 0)) * (+e[c >> 3] + +e[c + 8 >> 3]))) % +(b[a + 4 >> 2] | 0)) >>> 0;\r\n      a = b[(b[a >> 2] | 0) + (d << 2) >> 2] | 0;\r\n      if (!a) {\r\n        d = 0;\r\n        return d | 0;\r\n      }\r\n      while (1) {\r\n        if (jc(a, c) | 0) {\r\n          c = 5;\r\n          break;\r\n        }\r\n        a = b[a + 32 >> 2] | 0;\r\n        if (!a) {\r\n          a = 0;\r\n          c = 5;\r\n          break;\r\n        }\r\n      }\r\n      if ((c | 0) == 5) { return a | 0; }\r\n      return 0;\r\n    }\r\n    function Bd() {\r\n      return 27904;\r\n    }\r\n    function Cd(a) {\r\n      a = +a;\r\n      return ~~+Yd(+a) | 0;\r\n    }\r\n    function Dd(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0,\r\n        q = 0,\r\n        r = 0,\r\n        s = 0,\r\n        t = 0,\r\n        u = 0,\r\n        v = 0,\r\n        w = 0;\r\n      w = T;\r\n      T = T + 16 | 0;\r\n      n = w;\r\n      do { if (a >>> 0 < 245) {\r\n        k = a >>> 0 < 11 ? 16 : a + 11 & -8;\r\n        a = k >>> 3;\r\n        m = b[6977] | 0;\r\n        d = m >>> a;\r\n        if (d & 3 | 0) {\r\n          c = (d & 1 ^ 1) + a | 0;\r\n          a = 27948 + (c << 1 << 2) | 0;\r\n          d = a + 8 | 0;\r\n          e = b[d >> 2] | 0;\r\n          f = e + 8 | 0;\r\n          g = b[f >> 2] | 0;\r\n          if ((g | 0) == (a | 0)) { b[6977] = m & ~(1 << c); }else {\r\n            b[g + 12 >> 2] = a;\r\n            b[d >> 2] = g;\r\n          }\r\n          v = c << 3;\r\n          b[e + 4 >> 2] = v | 3;\r\n          v = e + v + 4 | 0;\r\n          b[v >> 2] = b[v >> 2] | 1;\r\n          v = f;\r\n          T = w;\r\n          return v | 0;\r\n        }\r\n        l = b[6979] | 0;\r\n        if (k >>> 0 > l >>> 0) {\r\n          if (d | 0) {\r\n            c = 2 << a;\r\n            c = d << a & (c | 0 - c);\r\n            c = (c & 0 - c) + -1 | 0;\r\n            i = c >>> 12 & 16;\r\n            c = c >>> i;\r\n            d = c >>> 5 & 8;\r\n            c = c >>> d;\r\n            g = c >>> 2 & 4;\r\n            c = c >>> g;\r\n            a = c >>> 1 & 2;\r\n            c = c >>> a;\r\n            e = c >>> 1 & 1;\r\n            e = (d | i | g | a | e) + (c >>> e) | 0;\r\n            c = 27948 + (e << 1 << 2) | 0;\r\n            a = c + 8 | 0;\r\n            g = b[a >> 2] | 0;\r\n            i = g + 8 | 0;\r\n            d = b[i >> 2] | 0;\r\n            if ((d | 0) == (c | 0)) {\r\n              a = m & ~(1 << e);\r\n              b[6977] = a;\r\n            } else {\r\n              b[d + 12 >> 2] = c;\r\n              b[a >> 2] = d;\r\n              a = m;\r\n            }\r\n            v = e << 3;\r\n            h = v - k | 0;\r\n            b[g + 4 >> 2] = k | 3;\r\n            f = g + k | 0;\r\n            b[f + 4 >> 2] = h | 1;\r\n            b[g + v >> 2] = h;\r\n            if (l | 0) {\r\n              e = b[6982] | 0;\r\n              c = l >>> 3;\r\n              d = 27948 + (c << 1 << 2) | 0;\r\n              c = 1 << c;\r\n              if (!(a & c)) {\r\n                b[6977] = a | c;\r\n                c = d;\r\n                a = d + 8 | 0;\r\n              } else {\r\n                a = d + 8 | 0;\r\n                c = b[a >> 2] | 0;\r\n              }\r\n              b[a >> 2] = e;\r\n              b[c + 12 >> 2] = e;\r\n              b[e + 8 >> 2] = c;\r\n              b[e + 12 >> 2] = d;\r\n            }\r\n            b[6979] = h;\r\n            b[6982] = f;\r\n            v = i;\r\n            T = w;\r\n            return v | 0;\r\n          }\r\n          g = b[6978] | 0;\r\n          if (g) {\r\n            d = (g & 0 - g) + -1 | 0;\r\n            f = d >>> 12 & 16;\r\n            d = d >>> f;\r\n            e = d >>> 5 & 8;\r\n            d = d >>> e;\r\n            h = d >>> 2 & 4;\r\n            d = d >>> h;\r\n            i = d >>> 1 & 2;\r\n            d = d >>> i;\r\n            j = d >>> 1 & 1;\r\n            j = b[28212 + ((e | f | h | i | j) + (d >>> j) << 2) >> 2] | 0;\r\n            d = j;\r\n            i = j;\r\n            j = (b[j + 4 >> 2] & -8) - k | 0;\r\n            while (1) {\r\n              a = b[d + 16 >> 2] | 0;\r\n              if (!a) {\r\n                a = b[d + 20 >> 2] | 0;\r\n                if (!a) { break; }\r\n              }\r\n              h = (b[a + 4 >> 2] & -8) - k | 0;\r\n              f = h >>> 0 < j >>> 0;\r\n              d = a;\r\n              i = f ? a : i;\r\n              j = f ? h : j;\r\n            }\r\n            h = i + k | 0;\r\n            if (h >>> 0 > i >>> 0) {\r\n              f = b[i + 24 >> 2] | 0;\r\n              c = b[i + 12 >> 2] | 0;\r\n              do { if ((c | 0) == (i | 0)) {\r\n                a = i + 20 | 0;\r\n                c = b[a >> 2] | 0;\r\n                if (!c) {\r\n                  a = i + 16 | 0;\r\n                  c = b[a >> 2] | 0;\r\n                  if (!c) {\r\n                    d = 0;\r\n                    break;\r\n                  }\r\n                }\r\n                while (1) {\r\n                  e = c + 20 | 0;\r\n                  d = b[e >> 2] | 0;\r\n                  if (!d) {\r\n                    e = c + 16 | 0;\r\n                    d = b[e >> 2] | 0;\r\n                    if (!d) { break; }else {\r\n                      c = d;\r\n                      a = e;\r\n                    }\r\n                  } else {\r\n                    c = d;\r\n                    a = e;\r\n                  }\r\n                }\r\n                b[a >> 2] = 0;\r\n                d = c;\r\n              } else {\r\n                d = b[i + 8 >> 2] | 0;\r\n                b[d + 12 >> 2] = c;\r\n                b[c + 8 >> 2] = d;\r\n                d = c;\r\n              } } while (0);\r\n              do { if (f | 0) {\r\n                c = b[i + 28 >> 2] | 0;\r\n                a = 28212 + (c << 2) | 0;\r\n                if ((i | 0) == (b[a >> 2] | 0)) {\r\n                  b[a >> 2] = d;\r\n                  if (!d) {\r\n                    b[6978] = g & ~(1 << c);\r\n                    break;\r\n                  }\r\n                } else {\r\n                  v = f + 16 | 0;\r\n                  b[((b[v >> 2] | 0) == (i | 0) ? v : f + 20 | 0) >> 2] = d;\r\n                  if (!d) { break; }\r\n                }\r\n                b[d + 24 >> 2] = f;\r\n                c = b[i + 16 >> 2] | 0;\r\n                if (c | 0) {\r\n                  b[d + 16 >> 2] = c;\r\n                  b[c + 24 >> 2] = d;\r\n                }\r\n                c = b[i + 20 >> 2] | 0;\r\n                if (c | 0) {\r\n                  b[d + 20 >> 2] = c;\r\n                  b[c + 24 >> 2] = d;\r\n                }\r\n              } } while (0);\r\n              if (j >>> 0 < 16) {\r\n                v = j + k | 0;\r\n                b[i + 4 >> 2] = v | 3;\r\n                v = i + v + 4 | 0;\r\n                b[v >> 2] = b[v >> 2] | 1;\r\n              } else {\r\n                b[i + 4 >> 2] = k | 3;\r\n                b[h + 4 >> 2] = j | 1;\r\n                b[h + j >> 2] = j;\r\n                if (l | 0) {\r\n                  e = b[6982] | 0;\r\n                  c = l >>> 3;\r\n                  d = 27948 + (c << 1 << 2) | 0;\r\n                  c = 1 << c;\r\n                  if (!(c & m)) {\r\n                    b[6977] = c | m;\r\n                    c = d;\r\n                    a = d + 8 | 0;\r\n                  } else {\r\n                    a = d + 8 | 0;\r\n                    c = b[a >> 2] | 0;\r\n                  }\r\n                  b[a >> 2] = e;\r\n                  b[c + 12 >> 2] = e;\r\n                  b[e + 8 >> 2] = c;\r\n                  b[e + 12 >> 2] = d;\r\n                }\r\n                b[6979] = j;\r\n                b[6982] = h;\r\n              }\r\n              v = i + 8 | 0;\r\n              T = w;\r\n              return v | 0;\r\n            } else { m = k; }\r\n          } else { m = k; }\r\n        } else { m = k; }\r\n      } else if (a >>> 0 <= 4294967231) {\r\n        a = a + 11 | 0;\r\n        k = a & -8;\r\n        e = b[6978] | 0;\r\n        if (e) {\r\n          f = 0 - k | 0;\r\n          a = a >>> 8;\r\n          if (a) {\r\n            if (k >>> 0 > 16777215) { j = 31; }else {\r\n              m = (a + 1048320 | 0) >>> 16 & 8;\r\n              q = a << m;\r\n              i = (q + 520192 | 0) >>> 16 & 4;\r\n              q = q << i;\r\n              j = (q + 245760 | 0) >>> 16 & 2;\r\n              j = 14 - (i | m | j) + (q << j >>> 15) | 0;\r\n              j = k >>> (j + 7 | 0) & 1 | j << 1;\r\n            }\r\n          } else { j = 0; }\r\n          d = b[28212 + (j << 2) >> 2] | 0;\r\n          a: do { if (!d) {\r\n            d = 0;\r\n            a = 0;\r\n            q = 61;\r\n          } else {\r\n            a = 0;\r\n            i = k << ((j | 0) == 31 ? 0 : 25 - (j >>> 1) | 0);\r\n            g = 0;\r\n            while (1) {\r\n              h = (b[d + 4 >> 2] & -8) - k | 0;\r\n              if (h >>> 0 < f >>> 0) { if (!h) {\r\n                a = d;\r\n                f = 0;\r\n                q = 65;\r\n                break a;\r\n              } else {\r\n                a = d;\r\n                f = h;\r\n              } }\r\n              q = b[d + 20 >> 2] | 0;\r\n              d = b[d + 16 + (i >>> 31 << 2) >> 2] | 0;\r\n              g = (q | 0) == 0 | (q | 0) == (d | 0) ? g : q;\r\n              if (!d) {\r\n                d = g;\r\n                q = 61;\r\n                break;\r\n              } else { i = i << 1; }\r\n            }\r\n          } } while (0);\r\n          if ((q | 0) == 61) {\r\n            if ((d | 0) == 0 & (a | 0) == 0) {\r\n              a = 2 << j;\r\n              a = (a | 0 - a) & e;\r\n              if (!a) {\r\n                m = k;\r\n                break;\r\n              }\r\n              m = (a & 0 - a) + -1 | 0;\r\n              h = m >>> 12 & 16;\r\n              m = m >>> h;\r\n              g = m >>> 5 & 8;\r\n              m = m >>> g;\r\n              i = m >>> 2 & 4;\r\n              m = m >>> i;\r\n              j = m >>> 1 & 2;\r\n              m = m >>> j;\r\n              d = m >>> 1 & 1;\r\n              a = 0;\r\n              d = b[28212 + ((g | h | i | j | d) + (m >>> d) << 2) >> 2] | 0;\r\n            }\r\n            if (!d) {\r\n              i = a;\r\n              h = f;\r\n            } else { q = 65; }\r\n          }\r\n          if ((q | 0) == 65) {\r\n            g = d;\r\n            while (1) {\r\n              m = (b[g + 4 >> 2] & -8) - k | 0;\r\n              d = m >>> 0 < f >>> 0;\r\n              f = d ? m : f;\r\n              a = d ? g : a;\r\n              d = b[g + 16 >> 2] | 0;\r\n              if (!d) { d = b[g + 20 >> 2] | 0; }\r\n              if (!d) {\r\n                i = a;\r\n                h = f;\r\n                break;\r\n              } else { g = d; }\r\n            }\r\n          }\r\n          if (((i | 0) != 0 ? h >>> 0 < ((b[6979] | 0) - k | 0) >>> 0 : 0) ? (l = i + k | 0, l >>> 0 > i >>> 0) : 0) {\r\n            g = b[i + 24 >> 2] | 0;\r\n            c = b[i + 12 >> 2] | 0;\r\n            do { if ((c | 0) == (i | 0)) {\r\n              a = i + 20 | 0;\r\n              c = b[a >> 2] | 0;\r\n              if (!c) {\r\n                a = i + 16 | 0;\r\n                c = b[a >> 2] | 0;\r\n                if (!c) {\r\n                  c = 0;\r\n                  break;\r\n                }\r\n              }\r\n              while (1) {\r\n                f = c + 20 | 0;\r\n                d = b[f >> 2] | 0;\r\n                if (!d) {\r\n                  f = c + 16 | 0;\r\n                  d = b[f >> 2] | 0;\r\n                  if (!d) { break; }else {\r\n                    c = d;\r\n                    a = f;\r\n                  }\r\n                } else {\r\n                  c = d;\r\n                  a = f;\r\n                }\r\n              }\r\n              b[a >> 2] = 0;\r\n            } else {\r\n              v = b[i + 8 >> 2] | 0;\r\n              b[v + 12 >> 2] = c;\r\n              b[c + 8 >> 2] = v;\r\n            } } while (0);\r\n            do { if (g) {\r\n              a = b[i + 28 >> 2] | 0;\r\n              d = 28212 + (a << 2) | 0;\r\n              if ((i | 0) == (b[d >> 2] | 0)) {\r\n                b[d >> 2] = c;\r\n                if (!c) {\r\n                  e = e & ~(1 << a);\r\n                  b[6978] = e;\r\n                  break;\r\n                }\r\n              } else {\r\n                v = g + 16 | 0;\r\n                b[((b[v >> 2] | 0) == (i | 0) ? v : g + 20 | 0) >> 2] = c;\r\n                if (!c) { break; }\r\n              }\r\n              b[c + 24 >> 2] = g;\r\n              a = b[i + 16 >> 2] | 0;\r\n              if (a | 0) {\r\n                b[c + 16 >> 2] = a;\r\n                b[a + 24 >> 2] = c;\r\n              }\r\n              a = b[i + 20 >> 2] | 0;\r\n              if (a) {\r\n                b[c + 20 >> 2] = a;\r\n                b[a + 24 >> 2] = c;\r\n              }\r\n            } } while (0);\r\n            b: do { if (h >>> 0 < 16) {\r\n              v = h + k | 0;\r\n              b[i + 4 >> 2] = v | 3;\r\n              v = i + v + 4 | 0;\r\n              b[v >> 2] = b[v >> 2] | 1;\r\n            } else {\r\n              b[i + 4 >> 2] = k | 3;\r\n              b[l + 4 >> 2] = h | 1;\r\n              b[l + h >> 2] = h;\r\n              c = h >>> 3;\r\n              if (h >>> 0 < 256) {\r\n                d = 27948 + (c << 1 << 2) | 0;\r\n                a = b[6977] | 0;\r\n                c = 1 << c;\r\n                if (!(a & c)) {\r\n                  b[6977] = a | c;\r\n                  c = d;\r\n                  a = d + 8 | 0;\r\n                } else {\r\n                  a = d + 8 | 0;\r\n                  c = b[a >> 2] | 0;\r\n                }\r\n                b[a >> 2] = l;\r\n                b[c + 12 >> 2] = l;\r\n                b[l + 8 >> 2] = c;\r\n                b[l + 12 >> 2] = d;\r\n                break;\r\n              }\r\n              c = h >>> 8;\r\n              if (c) {\r\n                if (h >>> 0 > 16777215) { d = 31; }else {\r\n                  u = (c + 1048320 | 0) >>> 16 & 8;\r\n                  v = c << u;\r\n                  t = (v + 520192 | 0) >>> 16 & 4;\r\n                  v = v << t;\r\n                  d = (v + 245760 | 0) >>> 16 & 2;\r\n                  d = 14 - (t | u | d) + (v << d >>> 15) | 0;\r\n                  d = h >>> (d + 7 | 0) & 1 | d << 1;\r\n                }\r\n              } else { d = 0; }\r\n              c = 28212 + (d << 2) | 0;\r\n              b[l + 28 >> 2] = d;\r\n              a = l + 16 | 0;\r\n              b[a + 4 >> 2] = 0;\r\n              b[a >> 2] = 0;\r\n              a = 1 << d;\r\n              if (!(e & a)) {\r\n                b[6978] = e | a;\r\n                b[c >> 2] = l;\r\n                b[l + 24 >> 2] = c;\r\n                b[l + 12 >> 2] = l;\r\n                b[l + 8 >> 2] = l;\r\n                break;\r\n              }\r\n              c = b[c >> 2] | 0;\r\n              c: do { if ((b[c + 4 >> 2] & -8 | 0) != (h | 0)) {\r\n                e = h << ((d | 0) == 31 ? 0 : 25 - (d >>> 1) | 0);\r\n                while (1) {\r\n                  d = c + 16 + (e >>> 31 << 2) | 0;\r\n                  a = b[d >> 2] | 0;\r\n                  if (!a) { break; }\r\n                  if ((b[a + 4 >> 2] & -8 | 0) == (h | 0)) {\r\n                    c = a;\r\n                    break c;\r\n                  } else {\r\n                    e = e << 1;\r\n                    c = a;\r\n                  }\r\n                }\r\n                b[d >> 2] = l;\r\n                b[l + 24 >> 2] = c;\r\n                b[l + 12 >> 2] = l;\r\n                b[l + 8 >> 2] = l;\r\n                break b;\r\n              } } while (0);\r\n              u = c + 8 | 0;\r\n              v = b[u >> 2] | 0;\r\n              b[v + 12 >> 2] = l;\r\n              b[u >> 2] = l;\r\n              b[l + 8 >> 2] = v;\r\n              b[l + 12 >> 2] = c;\r\n              b[l + 24 >> 2] = 0;\r\n            } } while (0);\r\n            v = i + 8 | 0;\r\n            T = w;\r\n            return v | 0;\r\n          } else { m = k; }\r\n        } else { m = k; }\r\n      } else { m = -1; } } while (0);\r\n      d = b[6979] | 0;\r\n      if (d >>> 0 >= m >>> 0) {\r\n        c = d - m | 0;\r\n        a = b[6982] | 0;\r\n        if (c >>> 0 > 15) {\r\n          v = a + m | 0;\r\n          b[6982] = v;\r\n          b[6979] = c;\r\n          b[v + 4 >> 2] = c | 1;\r\n          b[a + d >> 2] = c;\r\n          b[a + 4 >> 2] = m | 3;\r\n        } else {\r\n          b[6979] = 0;\r\n          b[6982] = 0;\r\n          b[a + 4 >> 2] = d | 3;\r\n          v = a + d + 4 | 0;\r\n          b[v >> 2] = b[v >> 2] | 1;\r\n        }\r\n        v = a + 8 | 0;\r\n        T = w;\r\n        return v | 0;\r\n      }\r\n      h = b[6980] | 0;\r\n      if (h >>> 0 > m >>> 0) {\r\n        t = h - m | 0;\r\n        b[6980] = t;\r\n        v = b[6983] | 0;\r\n        u = v + m | 0;\r\n        b[6983] = u;\r\n        b[u + 4 >> 2] = t | 1;\r\n        b[v + 4 >> 2] = m | 3;\r\n        v = v + 8 | 0;\r\n        T = w;\r\n        return v | 0;\r\n      }\r\n      if (!(b[7095] | 0)) {\r\n        b[7097] = 4096;\r\n        b[7096] = 4096;\r\n        b[7098] = -1;\r\n        b[7099] = -1;\r\n        b[7100] = 0;\r\n        b[7088] = 0;\r\n        b[7095] = n & -16 ^ 1431655768;\r\n        a = 4096;\r\n      } else { a = b[7097] | 0; }\r\n      i = m + 48 | 0;\r\n      j = m + 47 | 0;\r\n      g = a + j | 0;\r\n      f = 0 - a | 0;\r\n      k = g & f;\r\n      if (k >>> 0 <= m >>> 0) {\r\n        v = 0;\r\n        T = w;\r\n        return v | 0;\r\n      }\r\n      a = b[7087] | 0;\r\n      if (a | 0 ? (l = b[7085] | 0, n = l + k | 0, n >>> 0 <= l >>> 0 | n >>> 0 > a >>> 0) : 0) {\r\n        v = 0;\r\n        T = w;\r\n        return v | 0;\r\n      }\r\n      d: do { if (!(b[7088] & 4)) {\r\n        d = b[6983] | 0;\r\n        e: do { if (d) {\r\n          e = 28356;\r\n          while (1) {\r\n            n = b[e >> 2] | 0;\r\n            if (n >>> 0 <= d >>> 0 ? (n + (b[e + 4 >> 2] | 0) | 0) >>> 0 > d >>> 0 : 0) { break; }\r\n            a = b[e + 8 >> 2] | 0;\r\n            if (!a) {\r\n              q = 128;\r\n              break e;\r\n            } else { e = a; }\r\n          }\r\n          c = g - h & f;\r\n          if (c >>> 0 < 2147483647) {\r\n            a = Zd(c | 0) | 0;\r\n            if ((a | 0) == ((b[e >> 2] | 0) + (b[e + 4 >> 2] | 0) | 0)) {\r\n              if ((a | 0) != (-1 | 0)) {\r\n                h = c;\r\n                g = a;\r\n                q = 145;\r\n                break d;\r\n              }\r\n            } else {\r\n              e = a;\r\n              q = 136;\r\n            }\r\n          } else { c = 0; }\r\n        } else { q = 128; } } while (0);\r\n        do { if ((q | 0) == 128) {\r\n          d = Zd(0) | 0;\r\n          if ((d | 0) != (-1 | 0) ? (c = d, o = b[7096] | 0, p = o + -1 | 0, c = ((p & c | 0) == 0 ? 0 : (p + c & 0 - o) - c | 0) + k | 0, o = b[7085] | 0, p = c + o | 0, c >>> 0 > m >>> 0 & c >>> 0 < 2147483647) : 0) {\r\n            n = b[7087] | 0;\r\n            if (n | 0 ? p >>> 0 <= o >>> 0 | p >>> 0 > n >>> 0 : 0) {\r\n              c = 0;\r\n              break;\r\n            }\r\n            a = Zd(c | 0) | 0;\r\n            if ((a | 0) == (d | 0)) {\r\n              h = c;\r\n              g = d;\r\n              q = 145;\r\n              break d;\r\n            } else {\r\n              e = a;\r\n              q = 136;\r\n            }\r\n          } else { c = 0; }\r\n        } } while (0);\r\n        do { if ((q | 0) == 136) {\r\n          d = 0 - c | 0;\r\n          if (!(i >>> 0 > c >>> 0 & (c >>> 0 < 2147483647 & (e | 0) != (-1 | 0)))) { if ((e | 0) == (-1 | 0)) {\r\n            c = 0;\r\n            break;\r\n          } else {\r\n            h = c;\r\n            g = e;\r\n            q = 145;\r\n            break d;\r\n          } }\r\n          a = b[7097] | 0;\r\n          a = j - c + a & 0 - a;\r\n          if (a >>> 0 >= 2147483647) {\r\n            h = c;\r\n            g = e;\r\n            q = 145;\r\n            break d;\r\n          }\r\n          if ((Zd(a | 0) | 0) == (-1 | 0)) {\r\n            Zd(d | 0) | 0;\r\n            c = 0;\r\n            break;\r\n          } else {\r\n            h = a + c | 0;\r\n            g = e;\r\n            q = 145;\r\n            break d;\r\n          }\r\n        } } while (0);\r\n        b[7088] = b[7088] | 4;\r\n        q = 143;\r\n      } else {\r\n        c = 0;\r\n        q = 143;\r\n      } } while (0);\r\n      if (((q | 0) == 143 ? k >>> 0 < 2147483647 : 0) ? (t = Zd(k | 0) | 0, p = Zd(0) | 0, r = p - t | 0, s = r >>> 0 > (m + 40 | 0) >>> 0, !((t | 0) == (-1 | 0) | s ^ 1 | t >>> 0 < p >>> 0 & ((t | 0) != (-1 | 0) & (p | 0) != (-1 | 0)) ^ 1)) : 0) {\r\n        h = s ? r : c;\r\n        g = t;\r\n        q = 145;\r\n      }\r\n      if ((q | 0) == 145) {\r\n        c = (b[7085] | 0) + h | 0;\r\n        b[7085] = c;\r\n        if (c >>> 0 > (b[7086] | 0) >>> 0) { b[7086] = c; }\r\n        j = b[6983] | 0;\r\n        f: do { if (j) {\r\n          c = 28356;\r\n          while (1) {\r\n            a = b[c >> 2] | 0;\r\n            d = b[c + 4 >> 2] | 0;\r\n            if ((g | 0) == (a + d | 0)) {\r\n              q = 154;\r\n              break;\r\n            }\r\n            e = b[c + 8 >> 2] | 0;\r\n            if (!e) { break; }else { c = e; }\r\n          }\r\n          if (((q | 0) == 154 ? (u = c + 4 | 0, (b[c + 12 >> 2] & 8 | 0) == 0) : 0) ? g >>> 0 > j >>> 0 & a >>> 0 <= j >>> 0 : 0) {\r\n            b[u >> 2] = d + h;\r\n            v = (b[6980] | 0) + h | 0;\r\n            t = j + 8 | 0;\r\n            t = (t & 7 | 0) == 0 ? 0 : 0 - t & 7;\r\n            u = j + t | 0;\r\n            t = v - t | 0;\r\n            b[6983] = u;\r\n            b[6980] = t;\r\n            b[u + 4 >> 2] = t | 1;\r\n            b[j + v + 4 >> 2] = 40;\r\n            b[6984] = b[7099];\r\n            break;\r\n          }\r\n          if (g >>> 0 < (b[6981] | 0) >>> 0) { b[6981] = g; }\r\n          d = g + h | 0;\r\n          c = 28356;\r\n          while (1) {\r\n            if ((b[c >> 2] | 0) == (d | 0)) {\r\n              q = 162;\r\n              break;\r\n            }\r\n            a = b[c + 8 >> 2] | 0;\r\n            if (!a) { break; }else { c = a; }\r\n          }\r\n          if ((q | 0) == 162 ? (b[c + 12 >> 2] & 8 | 0) == 0 : 0) {\r\n            b[c >> 2] = g;\r\n            l = c + 4 | 0;\r\n            b[l >> 2] = (b[l >> 2] | 0) + h;\r\n            l = g + 8 | 0;\r\n            l = g + ((l & 7 | 0) == 0 ? 0 : 0 - l & 7) | 0;\r\n            c = d + 8 | 0;\r\n            c = d + ((c & 7 | 0) == 0 ? 0 : 0 - c & 7) | 0;\r\n            k = l + m | 0;\r\n            i = c - l - m | 0;\r\n            b[l + 4 >> 2] = m | 3;\r\n            g: do { if ((j | 0) == (c | 0)) {\r\n              v = (b[6980] | 0) + i | 0;\r\n              b[6980] = v;\r\n              b[6983] = k;\r\n              b[k + 4 >> 2] = v | 1;\r\n            } else {\r\n              if ((b[6982] | 0) == (c | 0)) {\r\n                v = (b[6979] | 0) + i | 0;\r\n                b[6979] = v;\r\n                b[6982] = k;\r\n                b[k + 4 >> 2] = v | 1;\r\n                b[k + v >> 2] = v;\r\n                break;\r\n              }\r\n              a = b[c + 4 >> 2] | 0;\r\n              if ((a & 3 | 0) == 1) {\r\n                h = a & -8;\r\n                e = a >>> 3;\r\n                h: do { if (a >>> 0 < 256) {\r\n                  a = b[c + 8 >> 2] | 0;\r\n                  d = b[c + 12 >> 2] | 0;\r\n                  if ((d | 0) == (a | 0)) {\r\n                    b[6977] = b[6977] & ~(1 << e);\r\n                    break;\r\n                  } else {\r\n                    b[a + 12 >> 2] = d;\r\n                    b[d + 8 >> 2] = a;\r\n                    break;\r\n                  }\r\n                } else {\r\n                  g = b[c + 24 >> 2] | 0;\r\n                  a = b[c + 12 >> 2] | 0;\r\n                  do { if ((a | 0) == (c | 0)) {\r\n                    d = c + 16 | 0;\r\n                    e = d + 4 | 0;\r\n                    a = b[e >> 2] | 0;\r\n                    if (!a) {\r\n                      a = b[d >> 2] | 0;\r\n                      if (!a) {\r\n                        a = 0;\r\n                        break;\r\n                      }\r\n                    } else { d = e; }\r\n                    while (1) {\r\n                      f = a + 20 | 0;\r\n                      e = b[f >> 2] | 0;\r\n                      if (!e) {\r\n                        f = a + 16 | 0;\r\n                        e = b[f >> 2] | 0;\r\n                        if (!e) { break; }else {\r\n                          a = e;\r\n                          d = f;\r\n                        }\r\n                      } else {\r\n                        a = e;\r\n                        d = f;\r\n                      }\r\n                    }\r\n                    b[d >> 2] = 0;\r\n                  } else {\r\n                    v = b[c + 8 >> 2] | 0;\r\n                    b[v + 12 >> 2] = a;\r\n                    b[a + 8 >> 2] = v;\r\n                  } } while (0);\r\n                  if (!g) { break; }\r\n                  d = b[c + 28 >> 2] | 0;\r\n                  e = 28212 + (d << 2) | 0;\r\n                  do { if ((b[e >> 2] | 0) != (c | 0)) {\r\n                    v = g + 16 | 0;\r\n                    b[((b[v >> 2] | 0) == (c | 0) ? v : g + 20 | 0) >> 2] = a;\r\n                    if (!a) { break h; }\r\n                  } else {\r\n                    b[e >> 2] = a;\r\n                    if (a | 0) { break; }\r\n                    b[6978] = b[6978] & ~(1 << d);\r\n                    break h;\r\n                  } } while (0);\r\n                  b[a + 24 >> 2] = g;\r\n                  d = c + 16 | 0;\r\n                  e = b[d >> 2] | 0;\r\n                  if (e | 0) {\r\n                    b[a + 16 >> 2] = e;\r\n                    b[e + 24 >> 2] = a;\r\n                  }\r\n                  d = b[d + 4 >> 2] | 0;\r\n                  if (!d) { break; }\r\n                  b[a + 20 >> 2] = d;\r\n                  b[d + 24 >> 2] = a;\r\n                } } while (0);\r\n                c = c + h | 0;\r\n                f = h + i | 0;\r\n              } else { f = i; }\r\n              c = c + 4 | 0;\r\n              b[c >> 2] = b[c >> 2] & -2;\r\n              b[k + 4 >> 2] = f | 1;\r\n              b[k + f >> 2] = f;\r\n              c = f >>> 3;\r\n              if (f >>> 0 < 256) {\r\n                d = 27948 + (c << 1 << 2) | 0;\r\n                a = b[6977] | 0;\r\n                c = 1 << c;\r\n                if (!(a & c)) {\r\n                  b[6977] = a | c;\r\n                  c = d;\r\n                  a = d + 8 | 0;\r\n                } else {\r\n                  a = d + 8 | 0;\r\n                  c = b[a >> 2] | 0;\r\n                }\r\n                b[a >> 2] = k;\r\n                b[c + 12 >> 2] = k;\r\n                b[k + 8 >> 2] = c;\r\n                b[k + 12 >> 2] = d;\r\n                break;\r\n              }\r\n              c = f >>> 8;\r\n              do { if (!c) { e = 0; }else {\r\n                if (f >>> 0 > 16777215) {\r\n                  e = 31;\r\n                  break;\r\n                }\r\n                u = (c + 1048320 | 0) >>> 16 & 8;\r\n                v = c << u;\r\n                t = (v + 520192 | 0) >>> 16 & 4;\r\n                v = v << t;\r\n                e = (v + 245760 | 0) >>> 16 & 2;\r\n                e = 14 - (t | u | e) + (v << e >>> 15) | 0;\r\n                e = f >>> (e + 7 | 0) & 1 | e << 1;\r\n              } } while (0);\r\n              c = 28212 + (e << 2) | 0;\r\n              b[k + 28 >> 2] = e;\r\n              a = k + 16 | 0;\r\n              b[a + 4 >> 2] = 0;\r\n              b[a >> 2] = 0;\r\n              a = b[6978] | 0;\r\n              d = 1 << e;\r\n              if (!(a & d)) {\r\n                b[6978] = a | d;\r\n                b[c >> 2] = k;\r\n                b[k + 24 >> 2] = c;\r\n                b[k + 12 >> 2] = k;\r\n                b[k + 8 >> 2] = k;\r\n                break;\r\n              }\r\n              c = b[c >> 2] | 0;\r\n              i: do { if ((b[c + 4 >> 2] & -8 | 0) != (f | 0)) {\r\n                e = f << ((e | 0) == 31 ? 0 : 25 - (e >>> 1) | 0);\r\n                while (1) {\r\n                  d = c + 16 + (e >>> 31 << 2) | 0;\r\n                  a = b[d >> 2] | 0;\r\n                  if (!a) { break; }\r\n                  if ((b[a + 4 >> 2] & -8 | 0) == (f | 0)) {\r\n                    c = a;\r\n                    break i;\r\n                  } else {\r\n                    e = e << 1;\r\n                    c = a;\r\n                  }\r\n                }\r\n                b[d >> 2] = k;\r\n                b[k + 24 >> 2] = c;\r\n                b[k + 12 >> 2] = k;\r\n                b[k + 8 >> 2] = k;\r\n                break g;\r\n              } } while (0);\r\n              u = c + 8 | 0;\r\n              v = b[u >> 2] | 0;\r\n              b[v + 12 >> 2] = k;\r\n              b[u >> 2] = k;\r\n              b[k + 8 >> 2] = v;\r\n              b[k + 12 >> 2] = c;\r\n              b[k + 24 >> 2] = 0;\r\n            } } while (0);\r\n            v = l + 8 | 0;\r\n            T = w;\r\n            return v | 0;\r\n          }\r\n          c = 28356;\r\n          while (1) {\r\n            a = b[c >> 2] | 0;\r\n            if (a >>> 0 <= j >>> 0 ? (v = a + (b[c + 4 >> 2] | 0) | 0, v >>> 0 > j >>> 0) : 0) { break; }\r\n            c = b[c + 8 >> 2] | 0;\r\n          }\r\n          f = v + -47 | 0;\r\n          a = f + 8 | 0;\r\n          a = f + ((a & 7 | 0) == 0 ? 0 : 0 - a & 7) | 0;\r\n          f = j + 16 | 0;\r\n          a = a >>> 0 < f >>> 0 ? j : a;\r\n          c = a + 8 | 0;\r\n          d = h + -40 | 0;\r\n          t = g + 8 | 0;\r\n          t = (t & 7 | 0) == 0 ? 0 : 0 - t & 7;\r\n          u = g + t | 0;\r\n          t = d - t | 0;\r\n          b[6983] = u;\r\n          b[6980] = t;\r\n          b[u + 4 >> 2] = t | 1;\r\n          b[g + d + 4 >> 2] = 40;\r\n          b[6984] = b[7099];\r\n          d = a + 4 | 0;\r\n          b[d >> 2] = 27;\r\n          b[c >> 2] = b[7089];\r\n          b[c + 4 >> 2] = b[7090];\r\n          b[c + 8 >> 2] = b[7091];\r\n          b[c + 12 >> 2] = b[7092];\r\n          b[7089] = g;\r\n          b[7090] = h;\r\n          b[7092] = 0;\r\n          b[7091] = c;\r\n          c = a + 24 | 0;\r\n          do {\r\n            u = c;\r\n            c = c + 4 | 0;\r\n            b[c >> 2] = 7;\r\n          } while ((u + 8 | 0) >>> 0 < v >>> 0);\r\n          if ((a | 0) != (j | 0)) {\r\n            g = a - j | 0;\r\n            b[d >> 2] = b[d >> 2] & -2;\r\n            b[j + 4 >> 2] = g | 1;\r\n            b[a >> 2] = g;\r\n            c = g >>> 3;\r\n            if (g >>> 0 < 256) {\r\n              d = 27948 + (c << 1 << 2) | 0;\r\n              a = b[6977] | 0;\r\n              c = 1 << c;\r\n              if (!(a & c)) {\r\n                b[6977] = a | c;\r\n                c = d;\r\n                a = d + 8 | 0;\r\n              } else {\r\n                a = d + 8 | 0;\r\n                c = b[a >> 2] | 0;\r\n              }\r\n              b[a >> 2] = j;\r\n              b[c + 12 >> 2] = j;\r\n              b[j + 8 >> 2] = c;\r\n              b[j + 12 >> 2] = d;\r\n              break;\r\n            }\r\n            c = g >>> 8;\r\n            if (c) {\r\n              if (g >>> 0 > 16777215) { e = 31; }else {\r\n                u = (c + 1048320 | 0) >>> 16 & 8;\r\n                v = c << u;\r\n                t = (v + 520192 | 0) >>> 16 & 4;\r\n                v = v << t;\r\n                e = (v + 245760 | 0) >>> 16 & 2;\r\n                e = 14 - (t | u | e) + (v << e >>> 15) | 0;\r\n                e = g >>> (e + 7 | 0) & 1 | e << 1;\r\n              }\r\n            } else { e = 0; }\r\n            d = 28212 + (e << 2) | 0;\r\n            b[j + 28 >> 2] = e;\r\n            b[j + 20 >> 2] = 0;\r\n            b[f >> 2] = 0;\r\n            c = b[6978] | 0;\r\n            a = 1 << e;\r\n            if (!(c & a)) {\r\n              b[6978] = c | a;\r\n              b[d >> 2] = j;\r\n              b[j + 24 >> 2] = d;\r\n              b[j + 12 >> 2] = j;\r\n              b[j + 8 >> 2] = j;\r\n              break;\r\n            }\r\n            c = b[d >> 2] | 0;\r\n            j: do { if ((b[c + 4 >> 2] & -8 | 0) != (g | 0)) {\r\n              e = g << ((e | 0) == 31 ? 0 : 25 - (e >>> 1) | 0);\r\n              while (1) {\r\n                d = c + 16 + (e >>> 31 << 2) | 0;\r\n                a = b[d >> 2] | 0;\r\n                if (!a) { break; }\r\n                if ((b[a + 4 >> 2] & -8 | 0) == (g | 0)) {\r\n                  c = a;\r\n                  break j;\r\n                } else {\r\n                  e = e << 1;\r\n                  c = a;\r\n                }\r\n              }\r\n              b[d >> 2] = j;\r\n              b[j + 24 >> 2] = c;\r\n              b[j + 12 >> 2] = j;\r\n              b[j + 8 >> 2] = j;\r\n              break f;\r\n            } } while (0);\r\n            u = c + 8 | 0;\r\n            v = b[u >> 2] | 0;\r\n            b[v + 12 >> 2] = j;\r\n            b[u >> 2] = j;\r\n            b[j + 8 >> 2] = v;\r\n            b[j + 12 >> 2] = c;\r\n            b[j + 24 >> 2] = 0;\r\n          }\r\n        } else {\r\n          v = b[6981] | 0;\r\n          if ((v | 0) == 0 | g >>> 0 < v >>> 0) { b[6981] = g; }\r\n          b[7089] = g;\r\n          b[7090] = h;\r\n          b[7092] = 0;\r\n          b[6986] = b[7095];\r\n          b[6985] = -1;\r\n          b[6990] = 27948;\r\n          b[6989] = 27948;\r\n          b[6992] = 27956;\r\n          b[6991] = 27956;\r\n          b[6994] = 27964;\r\n          b[6993] = 27964;\r\n          b[6996] = 27972;\r\n          b[6995] = 27972;\r\n          b[6998] = 27980;\r\n          b[6997] = 27980;\r\n          b[7e3] = 27988;\r\n          b[6999] = 27988;\r\n          b[7002] = 27996;\r\n          b[7001] = 27996;\r\n          b[7004] = 28004;\r\n          b[7003] = 28004;\r\n          b[7006] = 28012;\r\n          b[7005] = 28012;\r\n          b[7008] = 28020;\r\n          b[7007] = 28020;\r\n          b[7010] = 28028;\r\n          b[7009] = 28028;\r\n          b[7012] = 28036;\r\n          b[7011] = 28036;\r\n          b[7014] = 28044;\r\n          b[7013] = 28044;\r\n          b[7016] = 28052;\r\n          b[7015] = 28052;\r\n          b[7018] = 28060;\r\n          b[7017] = 28060;\r\n          b[7020] = 28068;\r\n          b[7019] = 28068;\r\n          b[7022] = 28076;\r\n          b[7021] = 28076;\r\n          b[7024] = 28084;\r\n          b[7023] = 28084;\r\n          b[7026] = 28092;\r\n          b[7025] = 28092;\r\n          b[7028] = 28100;\r\n          b[7027] = 28100;\r\n          b[7030] = 28108;\r\n          b[7029] = 28108;\r\n          b[7032] = 28116;\r\n          b[7031] = 28116;\r\n          b[7034] = 28124;\r\n          b[7033] = 28124;\r\n          b[7036] = 28132;\r\n          b[7035] = 28132;\r\n          b[7038] = 28140;\r\n          b[7037] = 28140;\r\n          b[7040] = 28148;\r\n          b[7039] = 28148;\r\n          b[7042] = 28156;\r\n          b[7041] = 28156;\r\n          b[7044] = 28164;\r\n          b[7043] = 28164;\r\n          b[7046] = 28172;\r\n          b[7045] = 28172;\r\n          b[7048] = 28180;\r\n          b[7047] = 28180;\r\n          b[7050] = 28188;\r\n          b[7049] = 28188;\r\n          b[7052] = 28196;\r\n          b[7051] = 28196;\r\n          v = h + -40 | 0;\r\n          t = g + 8 | 0;\r\n          t = (t & 7 | 0) == 0 ? 0 : 0 - t & 7;\r\n          u = g + t | 0;\r\n          t = v - t | 0;\r\n          b[6983] = u;\r\n          b[6980] = t;\r\n          b[u + 4 >> 2] = t | 1;\r\n          b[g + v + 4 >> 2] = 40;\r\n          b[6984] = b[7099];\r\n        } } while (0);\r\n        c = b[6980] | 0;\r\n        if (c >>> 0 > m >>> 0) {\r\n          t = c - m | 0;\r\n          b[6980] = t;\r\n          v = b[6983] | 0;\r\n          u = v + m | 0;\r\n          b[6983] = u;\r\n          b[u + 4 >> 2] = t | 1;\r\n          b[v + 4 >> 2] = m | 3;\r\n          v = v + 8 | 0;\r\n          T = w;\r\n          return v | 0;\r\n        }\r\n      }\r\n      v = Bd() | 0;\r\n      b[v >> 2] = 12;\r\n      v = 0;\r\n      T = w;\r\n      return v | 0;\r\n    }\r\n    function Ed(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      if (!a) { return; }\r\n      d = a + -8 | 0;\r\n      f = b[6981] | 0;\r\n      a = b[a + -4 >> 2] | 0;\r\n      c = a & -8;\r\n      j = d + c | 0;\r\n      do { if (!(a & 1)) {\r\n        e = b[d >> 2] | 0;\r\n        if (!(a & 3)) { return; }\r\n        h = d + (0 - e) | 0;\r\n        g = e + c | 0;\r\n        if (h >>> 0 < f >>> 0) { return; }\r\n        if ((b[6982] | 0) == (h | 0)) {\r\n          a = j + 4 | 0;\r\n          c = b[a >> 2] | 0;\r\n          if ((c & 3 | 0) != 3) {\r\n            i = h;\r\n            c = g;\r\n            break;\r\n          }\r\n          b[6979] = g;\r\n          b[a >> 2] = c & -2;\r\n          b[h + 4 >> 2] = g | 1;\r\n          b[h + g >> 2] = g;\r\n          return;\r\n        }\r\n        d = e >>> 3;\r\n        if (e >>> 0 < 256) {\r\n          a = b[h + 8 >> 2] | 0;\r\n          c = b[h + 12 >> 2] | 0;\r\n          if ((c | 0) == (a | 0)) {\r\n            b[6977] = b[6977] & ~(1 << d);\r\n            i = h;\r\n            c = g;\r\n            break;\r\n          } else {\r\n            b[a + 12 >> 2] = c;\r\n            b[c + 8 >> 2] = a;\r\n            i = h;\r\n            c = g;\r\n            break;\r\n          }\r\n        }\r\n        f = b[h + 24 >> 2] | 0;\r\n        a = b[h + 12 >> 2] | 0;\r\n        do { if ((a | 0) == (h | 0)) {\r\n          c = h + 16 | 0;\r\n          d = c + 4 | 0;\r\n          a = b[d >> 2] | 0;\r\n          if (!a) {\r\n            a = b[c >> 2] | 0;\r\n            if (!a) {\r\n              a = 0;\r\n              break;\r\n            }\r\n          } else { c = d; }\r\n          while (1) {\r\n            e = a + 20 | 0;\r\n            d = b[e >> 2] | 0;\r\n            if (!d) {\r\n              e = a + 16 | 0;\r\n              d = b[e >> 2] | 0;\r\n              if (!d) { break; }else {\r\n                a = d;\r\n                c = e;\r\n              }\r\n            } else {\r\n              a = d;\r\n              c = e;\r\n            }\r\n          }\r\n          b[c >> 2] = 0;\r\n        } else {\r\n          i = b[h + 8 >> 2] | 0;\r\n          b[i + 12 >> 2] = a;\r\n          b[a + 8 >> 2] = i;\r\n        } } while (0);\r\n        if (f) {\r\n          c = b[h + 28 >> 2] | 0;\r\n          d = 28212 + (c << 2) | 0;\r\n          if ((b[d >> 2] | 0) == (h | 0)) {\r\n            b[d >> 2] = a;\r\n            if (!a) {\r\n              b[6978] = b[6978] & ~(1 << c);\r\n              i = h;\r\n              c = g;\r\n              break;\r\n            }\r\n          } else {\r\n            i = f + 16 | 0;\r\n            b[((b[i >> 2] | 0) == (h | 0) ? i : f + 20 | 0) >> 2] = a;\r\n            if (!a) {\r\n              i = h;\r\n              c = g;\r\n              break;\r\n            }\r\n          }\r\n          b[a + 24 >> 2] = f;\r\n          c = h + 16 | 0;\r\n          d = b[c >> 2] | 0;\r\n          if (d | 0) {\r\n            b[a + 16 >> 2] = d;\r\n            b[d + 24 >> 2] = a;\r\n          }\r\n          c = b[c + 4 >> 2] | 0;\r\n          if (c) {\r\n            b[a + 20 >> 2] = c;\r\n            b[c + 24 >> 2] = a;\r\n            i = h;\r\n            c = g;\r\n          } else {\r\n            i = h;\r\n            c = g;\r\n          }\r\n        } else {\r\n          i = h;\r\n          c = g;\r\n        }\r\n      } else {\r\n        i = d;\r\n        h = d;\r\n      } } while (0);\r\n      if (h >>> 0 >= j >>> 0) { return; }\r\n      a = j + 4 | 0;\r\n      e = b[a >> 2] | 0;\r\n      if (!(e & 1)) { return; }\r\n      if (!(e & 2)) {\r\n        if ((b[6983] | 0) == (j | 0)) {\r\n          j = (b[6980] | 0) + c | 0;\r\n          b[6980] = j;\r\n          b[6983] = i;\r\n          b[i + 4 >> 2] = j | 1;\r\n          if ((i | 0) != (b[6982] | 0)) { return; }\r\n          b[6982] = 0;\r\n          b[6979] = 0;\r\n          return;\r\n        }\r\n        if ((b[6982] | 0) == (j | 0)) {\r\n          j = (b[6979] | 0) + c | 0;\r\n          b[6979] = j;\r\n          b[6982] = h;\r\n          b[i + 4 >> 2] = j | 1;\r\n          b[h + j >> 2] = j;\r\n          return;\r\n        }\r\n        f = (e & -8) + c | 0;\r\n        d = e >>> 3;\r\n        do { if (e >>> 0 < 256) {\r\n          c = b[j + 8 >> 2] | 0;\r\n          a = b[j + 12 >> 2] | 0;\r\n          if ((a | 0) == (c | 0)) {\r\n            b[6977] = b[6977] & ~(1 << d);\r\n            break;\r\n          } else {\r\n            b[c + 12 >> 2] = a;\r\n            b[a + 8 >> 2] = c;\r\n            break;\r\n          }\r\n        } else {\r\n          g = b[j + 24 >> 2] | 0;\r\n          a = b[j + 12 >> 2] | 0;\r\n          do { if ((a | 0) == (j | 0)) {\r\n            c = j + 16 | 0;\r\n            d = c + 4 | 0;\r\n            a = b[d >> 2] | 0;\r\n            if (!a) {\r\n              a = b[c >> 2] | 0;\r\n              if (!a) {\r\n                d = 0;\r\n                break;\r\n              }\r\n            } else { c = d; }\r\n            while (1) {\r\n              e = a + 20 | 0;\r\n              d = b[e >> 2] | 0;\r\n              if (!d) {\r\n                e = a + 16 | 0;\r\n                d = b[e >> 2] | 0;\r\n                if (!d) { break; }else {\r\n                  a = d;\r\n                  c = e;\r\n                }\r\n              } else {\r\n                a = d;\r\n                c = e;\r\n              }\r\n            }\r\n            b[c >> 2] = 0;\r\n            d = a;\r\n          } else {\r\n            d = b[j + 8 >> 2] | 0;\r\n            b[d + 12 >> 2] = a;\r\n            b[a + 8 >> 2] = d;\r\n            d = a;\r\n          } } while (0);\r\n          if (g | 0) {\r\n            a = b[j + 28 >> 2] | 0;\r\n            c = 28212 + (a << 2) | 0;\r\n            if ((b[c >> 2] | 0) == (j | 0)) {\r\n              b[c >> 2] = d;\r\n              if (!d) {\r\n                b[6978] = b[6978] & ~(1 << a);\r\n                break;\r\n              }\r\n            } else {\r\n              e = g + 16 | 0;\r\n              b[((b[e >> 2] | 0) == (j | 0) ? e : g + 20 | 0) >> 2] = d;\r\n              if (!d) { break; }\r\n            }\r\n            b[d + 24 >> 2] = g;\r\n            a = j + 16 | 0;\r\n            c = b[a >> 2] | 0;\r\n            if (c | 0) {\r\n              b[d + 16 >> 2] = c;\r\n              b[c + 24 >> 2] = d;\r\n            }\r\n            a = b[a + 4 >> 2] | 0;\r\n            if (a | 0) {\r\n              b[d + 20 >> 2] = a;\r\n              b[a + 24 >> 2] = d;\r\n            }\r\n          }\r\n        } } while (0);\r\n        b[i + 4 >> 2] = f | 1;\r\n        b[h + f >> 2] = f;\r\n        if ((i | 0) == (b[6982] | 0)) {\r\n          b[6979] = f;\r\n          return;\r\n        }\r\n      } else {\r\n        b[a >> 2] = e & -2;\r\n        b[i + 4 >> 2] = c | 1;\r\n        b[h + c >> 2] = c;\r\n        f = c;\r\n      }\r\n      a = f >>> 3;\r\n      if (f >>> 0 < 256) {\r\n        d = 27948 + (a << 1 << 2) | 0;\r\n        c = b[6977] | 0;\r\n        a = 1 << a;\r\n        if (!(c & a)) {\r\n          b[6977] = c | a;\r\n          a = d;\r\n          c = d + 8 | 0;\r\n        } else {\r\n          c = d + 8 | 0;\r\n          a = b[c >> 2] | 0;\r\n        }\r\n        b[c >> 2] = i;\r\n        b[a + 12 >> 2] = i;\r\n        b[i + 8 >> 2] = a;\r\n        b[i + 12 >> 2] = d;\r\n        return;\r\n      }\r\n      a = f >>> 8;\r\n      if (a) {\r\n        if (f >>> 0 > 16777215) { e = 31; }else {\r\n          h = (a + 1048320 | 0) >>> 16 & 8;\r\n          j = a << h;\r\n          g = (j + 520192 | 0) >>> 16 & 4;\r\n          j = j << g;\r\n          e = (j + 245760 | 0) >>> 16 & 2;\r\n          e = 14 - (g | h | e) + (j << e >>> 15) | 0;\r\n          e = f >>> (e + 7 | 0) & 1 | e << 1;\r\n        }\r\n      } else { e = 0; }\r\n      a = 28212 + (e << 2) | 0;\r\n      b[i + 28 >> 2] = e;\r\n      b[i + 20 >> 2] = 0;\r\n      b[i + 16 >> 2] = 0;\r\n      c = b[6978] | 0;\r\n      d = 1 << e;\r\n      a: do { if (!(c & d)) {\r\n        b[6978] = c | d;\r\n        b[a >> 2] = i;\r\n        b[i + 24 >> 2] = a;\r\n        b[i + 12 >> 2] = i;\r\n        b[i + 8 >> 2] = i;\r\n      } else {\r\n        a = b[a >> 2] | 0;\r\n        b: do { if ((b[a + 4 >> 2] & -8 | 0) != (f | 0)) {\r\n          e = f << ((e | 0) == 31 ? 0 : 25 - (e >>> 1) | 0);\r\n          while (1) {\r\n            d = a + 16 + (e >>> 31 << 2) | 0;\r\n            c = b[d >> 2] | 0;\r\n            if (!c) { break; }\r\n            if ((b[c + 4 >> 2] & -8 | 0) == (f | 0)) {\r\n              a = c;\r\n              break b;\r\n            } else {\r\n              e = e << 1;\r\n              a = c;\r\n            }\r\n          }\r\n          b[d >> 2] = i;\r\n          b[i + 24 >> 2] = a;\r\n          b[i + 12 >> 2] = i;\r\n          b[i + 8 >> 2] = i;\r\n          break a;\r\n        } } while (0);\r\n        h = a + 8 | 0;\r\n        j = b[h >> 2] | 0;\r\n        b[j + 12 >> 2] = i;\r\n        b[h >> 2] = i;\r\n        b[i + 8 >> 2] = j;\r\n        b[i + 12 >> 2] = a;\r\n        b[i + 24 >> 2] = 0;\r\n      } } while (0);\r\n      j = (b[6985] | 0) + -1 | 0;\r\n      b[6985] = j;\r\n      if (j | 0) { return; }\r\n      a = 28364;\r\n      while (1) {\r\n        a = b[a >> 2] | 0;\r\n        if (!a) { break; }else { a = a + 8 | 0; }\r\n      }\r\n      b[6985] = -1;\r\n      return;\r\n    }\r\n    function Fd(a, c) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      var d = 0;\r\n      if (a) {\r\n        d = B(c, a) | 0;\r\n        if ((c | a) >>> 0 > 65535) { d = ((d >>> 0) / (a >>> 0) | 0 | 0) == (c | 0) ? d : -1; }\r\n      } else { d = 0; }\r\n      a = Dd(d) | 0;\r\n      if (!a) { return a | 0; }\r\n      if (!(b[a + -4 >> 2] & 3)) { return a | 0; }\r\n      Xd(a | 0, 0, d | 0) | 0;\r\n      return a | 0;\r\n    }\r\n    function Gd(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      c = a + c >>> 0;\r\n      return (G(b + d + (c >>> 0 < a >>> 0 | 0) >>> 0 | 0), c | 0) | 0;\r\n    }\r\n    function Hd(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      d = b - d - (c >>> 0 > a >>> 0 | 0) >>> 0;\r\n      return (G(d | 0), a - c >>> 0 | 0) | 0;\r\n    }\r\n    function Id(a) {\r\n      a = a | 0;\r\n      return (a ? 31 - (E(a ^ a - 1) | 0) | 0 : 32) | 0;\r\n    }\r\n    function Jd(a, c, d, e, f) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      f = f | 0;\r\n      var g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0,\r\n        l = 0,\r\n        m = 0,\r\n        n = 0,\r\n        o = 0,\r\n        p = 0;\r\n      l = a;\r\n      j = c;\r\n      k = j;\r\n      h = d;\r\n      n = e;\r\n      i = n;\r\n      if (!k) {\r\n        g = (f | 0) != 0;\r\n        if (!i) {\r\n          if (g) {\r\n            b[f >> 2] = (l >>> 0) % (h >>> 0);\r\n            b[f + 4 >> 2] = 0;\r\n          }\r\n          n = 0;\r\n          f = (l >>> 0) / (h >>> 0) >>> 0;\r\n          return (G(n | 0), f) | 0;\r\n        } else {\r\n          if (!g) {\r\n            n = 0;\r\n            f = 0;\r\n            return (G(n | 0), f) | 0;\r\n          }\r\n          b[f >> 2] = a | 0;\r\n          b[f + 4 >> 2] = c & 0;\r\n          n = 0;\r\n          f = 0;\r\n          return (G(n | 0), f) | 0;\r\n        }\r\n      }\r\n      g = (i | 0) == 0;\r\n      do { if (h) {\r\n        if (!g) {\r\n          g = (E(i | 0) | 0) - (E(k | 0) | 0) | 0;\r\n          if (g >>> 0 <= 31) {\r\n            m = g + 1 | 0;\r\n            i = 31 - g | 0;\r\n            c = g - 31 >> 31;\r\n            h = m;\r\n            a = l >>> (m >>> 0) & c | k << i;\r\n            c = k >>> (m >>> 0) & c;\r\n            g = 0;\r\n            i = l << i;\r\n            break;\r\n          }\r\n          if (!f) {\r\n            n = 0;\r\n            f = 0;\r\n            return (G(n | 0), f) | 0;\r\n          }\r\n          b[f >> 2] = a | 0;\r\n          b[f + 4 >> 2] = j | c & 0;\r\n          n = 0;\r\n          f = 0;\r\n          return (G(n | 0), f) | 0;\r\n        }\r\n        g = h - 1 | 0;\r\n        if (g & h | 0) {\r\n          i = (E(h | 0) | 0) + 33 - (E(k | 0) | 0) | 0;\r\n          p = 64 - i | 0;\r\n          m = 32 - i | 0;\r\n          j = m >> 31;\r\n          o = i - 32 | 0;\r\n          c = o >> 31;\r\n          h = i;\r\n          a = m - 1 >> 31 & k >>> (o >>> 0) | (k << m | l >>> (i >>> 0)) & c;\r\n          c = c & k >>> (i >>> 0);\r\n          g = l << p & j;\r\n          i = (k << p | l >>> (o >>> 0)) & j | l << m & i - 33 >> 31;\r\n          break;\r\n        }\r\n        if (f | 0) {\r\n          b[f >> 2] = g & l;\r\n          b[f + 4 >> 2] = 0;\r\n        }\r\n        if ((h | 0) == 1) {\r\n          o = j | c & 0;\r\n          p = a | 0 | 0;\r\n          return (G(o | 0), p) | 0;\r\n        } else {\r\n          p = Id(h | 0) | 0;\r\n          o = k >>> (p >>> 0) | 0;\r\n          p = k << 32 - p | l >>> (p >>> 0) | 0;\r\n          return (G(o | 0), p) | 0;\r\n        }\r\n      } else {\r\n        if (g) {\r\n          if (f | 0) {\r\n            b[f >> 2] = (k >>> 0) % (h >>> 0);\r\n            b[f + 4 >> 2] = 0;\r\n          }\r\n          o = 0;\r\n          p = (k >>> 0) / (h >>> 0) >>> 0;\r\n          return (G(o | 0), p) | 0;\r\n        }\r\n        if (!l) {\r\n          if (f | 0) {\r\n            b[f >> 2] = 0;\r\n            b[f + 4 >> 2] = (k >>> 0) % (i >>> 0);\r\n          }\r\n          o = 0;\r\n          p = (k >>> 0) / (i >>> 0) >>> 0;\r\n          return (G(o | 0), p) | 0;\r\n        }\r\n        g = i - 1 | 0;\r\n        if (!(g & i)) {\r\n          if (f | 0) {\r\n            b[f >> 2] = a | 0;\r\n            b[f + 4 >> 2] = g & k | c & 0;\r\n          }\r\n          o = 0;\r\n          p = k >>> ((Id(i | 0) | 0) >>> 0);\r\n          return (G(o | 0), p) | 0;\r\n        }\r\n        g = (E(i | 0) | 0) - (E(k | 0) | 0) | 0;\r\n        if (g >>> 0 <= 30) {\r\n          c = g + 1 | 0;\r\n          i = 31 - g | 0;\r\n          h = c;\r\n          a = k << i | l >>> (c >>> 0);\r\n          c = k >>> (c >>> 0);\r\n          g = 0;\r\n          i = l << i;\r\n          break;\r\n        }\r\n        if (!f) {\r\n          o = 0;\r\n          p = 0;\r\n          return (G(o | 0), p) | 0;\r\n        }\r\n        b[f >> 2] = a | 0;\r\n        b[f + 4 >> 2] = j | c & 0;\r\n        o = 0;\r\n        p = 0;\r\n        return (G(o | 0), p) | 0;\r\n      } } while (0);\r\n      if (!h) {\r\n        k = i;\r\n        j = 0;\r\n        i = 0;\r\n      } else {\r\n        m = d | 0 | 0;\r\n        l = n | e & 0;\r\n        k = Gd(m | 0, l | 0, -1, -1) | 0;\r\n        d = H() | 0;\r\n        j = i;\r\n        i = 0;\r\n        do {\r\n          e = j;\r\n          j = g >>> 31 | j << 1;\r\n          g = i | g << 1;\r\n          e = a << 1 | e >>> 31 | 0;\r\n          n = a >>> 31 | c << 1 | 0;\r\n          Hd(k | 0, d | 0, e | 0, n | 0) | 0;\r\n          p = H() | 0;\r\n          o = p >> 31 | ((p | 0) < 0 ? -1 : 0) << 1;\r\n          i = o & 1;\r\n          a = Hd(e | 0, n | 0, o & m | 0, (((p | 0) < 0 ? -1 : 0) >> 31 | ((p | 0) < 0 ? -1 : 0) << 1) & l | 0) | 0;\r\n          c = H() | 0;\r\n          h = h - 1 | 0;\r\n        } while ((h | 0) != 0);\r\n        k = j;\r\n        j = 0;\r\n      }\r\n      h = 0;\r\n      if (f | 0) {\r\n        b[f >> 2] = a;\r\n        b[f + 4 >> 2] = c;\r\n      }\r\n      o = (g | 0) >>> 31 | (k | h) << 1 | (h << 1 | g >>> 31) & 0 | j;\r\n      p = (g << 1 | 0 >>> 31) & -2 | i;\r\n      return (G(o | 0), p) | 0;\r\n    }\r\n    function Kd(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0;\r\n      j = b >> 31 | ((b | 0) < 0 ? -1 : 0) << 1;\r\n      i = ((b | 0) < 0 ? -1 : 0) >> 31 | ((b | 0) < 0 ? -1 : 0) << 1;\r\n      f = d >> 31 | ((d | 0) < 0 ? -1 : 0) << 1;\r\n      e = ((d | 0) < 0 ? -1 : 0) >> 31 | ((d | 0) < 0 ? -1 : 0) << 1;\r\n      h = Hd(j ^ a | 0, i ^ b | 0, j | 0, i | 0) | 0;\r\n      g = H() | 0;\r\n      a = f ^ j;\r\n      b = e ^ i;\r\n      return Hd((Jd(h, g, Hd(f ^ c | 0, e ^ d | 0, f | 0, e | 0) | 0, H() | 0, 0) | 0) ^ a | 0, (H() | 0) ^ b | 0, a | 0, b | 0) | 0;\r\n    }\r\n    function Ld(a, b) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0,\r\n        f = 0;\r\n      f = a & 65535;\r\n      e = b & 65535;\r\n      c = B(e, f) | 0;\r\n      d = a >>> 16;\r\n      a = (c >>> 16) + (B(e, d) | 0) | 0;\r\n      e = b >>> 16;\r\n      b = B(e, f) | 0;\r\n      return (G((a >>> 16) + (B(e, d) | 0) + (((a & 65535) + b | 0) >>> 16) | 0), a + b << 16 | c & 65535 | 0) | 0;\r\n    }\r\n    function Md(a, b, c, d) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      var e = 0,\r\n        f = 0;\r\n      e = a;\r\n      f = c;\r\n      c = Ld(e, f) | 0;\r\n      a = H() | 0;\r\n      return (G((B(b, f) | 0) + (B(d, e) | 0) + a | a & 0 | 0), c | 0 | 0) | 0;\r\n    }\r\n    function Nd(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0,\r\n        j = 0,\r\n        k = 0;\r\n      f = T;\r\n      T = T + 16 | 0;\r\n      i = f | 0;\r\n      h = c >> 31 | ((c | 0) < 0 ? -1 : 0) << 1;\r\n      g = ((c | 0) < 0 ? -1 : 0) >> 31 | ((c | 0) < 0 ? -1 : 0) << 1;\r\n      k = e >> 31 | ((e | 0) < 0 ? -1 : 0) << 1;\r\n      j = ((e | 0) < 0 ? -1 : 0) >> 31 | ((e | 0) < 0 ? -1 : 0) << 1;\r\n      a = Hd(h ^ a | 0, g ^ c | 0, h | 0, g | 0) | 0;\r\n      c = H() | 0;\r\n      Jd(a, c, Hd(k ^ d | 0, j ^ e | 0, k | 0, j | 0) | 0, H() | 0, i) | 0;\r\n      e = Hd(b[i >> 2] ^ h | 0, b[i + 4 >> 2] ^ g | 0, h | 0, g | 0) | 0;\r\n      d = H() | 0;\r\n      T = f;\r\n      return (G(d | 0), e) | 0;\r\n    }\r\n    function Od(a, c, d, e) {\r\n      a = a | 0;\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0;\r\n      g = T;\r\n      T = T + 16 | 0;\r\n      f = g | 0;\r\n      Jd(a, c, d, e, f) | 0;\r\n      T = g;\r\n      return (G(b[f + 4 >> 2] | 0), b[f >> 2] | 0) | 0;\r\n    }\r\n    function Pd(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      if ((c | 0) < 32) {\r\n        G(b >> c | 0);\r\n        return a >>> c | (b & (1 << c) - 1) << 32 - c;\r\n      }\r\n      G(((b | 0) < 0 ? -1 : 0) | 0);\r\n      return b >> c - 32 | 0;\r\n    }\r\n    function Qd(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      if ((c | 0) < 32) {\r\n        G(b >>> c | 0);\r\n        return a >>> c | (b & (1 << c) - 1) << 32 - c;\r\n      }\r\n      G(0);\r\n      return b >>> c - 32 | 0;\r\n    }\r\n    function Rd(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      if ((c | 0) < 32) {\r\n        G(b << c | (a & (1 << c) - 1 << 32 - c) >>> 32 - c | 0);\r\n        return a << c;\r\n      }\r\n      G(a << c - 32 | 0);\r\n      return 0;\r\n    }\r\n    function Sd(a, b, c) {\r\n      a = a | 0;\r\n      b = b | 0;\r\n      c = c | 0;\r\n      b = E(b) | 0;\r\n      if ((b | 0) == 32) { b = b + (E(a) | 0) | 0; }\r\n      G(0);\r\n      return b | 0;\r\n    }\r\n    function Td(a, b) {\r\n      a = +a;\r\n      b = +b;\r\n      if (a != a) { return +b; }\r\n      if (b != b) { return +a; }\r\n      return +D(+a, +b);\r\n    }\r\n    function Ud(a, b) {\r\n      a = +a;\r\n      b = +b;\r\n      if (a != a) { return +b; }\r\n      if (b != b) { return +a; }\r\n      return +C(+a, +b);\r\n    }\r\n    function Vd(a) {\r\n      a = +a;\r\n      return a >= 0.0 ? +p(a + .5) : +A(a - .5);\r\n    }\r\n    function Wd(c, d, e) {\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0;\r\n      if ((e | 0) >= 8192) {\r\n        L(c | 0, d | 0, e | 0) | 0;\r\n        return c | 0;\r\n      }\r\n      h = c | 0;\r\n      g = c + e | 0;\r\n      if ((c & 3) == (d & 3)) {\r\n        while (c & 3) {\r\n          if (!e) { return h | 0; }\r\n          a[c >> 0] = a[d >> 0] | 0;\r\n          c = c + 1 | 0;\r\n          d = d + 1 | 0;\r\n          e = e - 1 | 0;\r\n        }\r\n        e = g & -4 | 0;\r\n        f = e - 64 | 0;\r\n        while ((c | 0) <= (f | 0)) {\r\n          b[c >> 2] = b[d >> 2];\r\n          b[c + 4 >> 2] = b[d + 4 >> 2];\r\n          b[c + 8 >> 2] = b[d + 8 >> 2];\r\n          b[c + 12 >> 2] = b[d + 12 >> 2];\r\n          b[c + 16 >> 2] = b[d + 16 >> 2];\r\n          b[c + 20 >> 2] = b[d + 20 >> 2];\r\n          b[c + 24 >> 2] = b[d + 24 >> 2];\r\n          b[c + 28 >> 2] = b[d + 28 >> 2];\r\n          b[c + 32 >> 2] = b[d + 32 >> 2];\r\n          b[c + 36 >> 2] = b[d + 36 >> 2];\r\n          b[c + 40 >> 2] = b[d + 40 >> 2];\r\n          b[c + 44 >> 2] = b[d + 44 >> 2];\r\n          b[c + 48 >> 2] = b[d + 48 >> 2];\r\n          b[c + 52 >> 2] = b[d + 52 >> 2];\r\n          b[c + 56 >> 2] = b[d + 56 >> 2];\r\n          b[c + 60 >> 2] = b[d + 60 >> 2];\r\n          c = c + 64 | 0;\r\n          d = d + 64 | 0;\r\n        }\r\n        while ((c | 0) < (e | 0)) {\r\n          b[c >> 2] = b[d >> 2];\r\n          c = c + 4 | 0;\r\n          d = d + 4 | 0;\r\n        }\r\n      } else {\r\n        e = g - 4 | 0;\r\n        while ((c | 0) < (e | 0)) {\r\n          a[c >> 0] = a[d >> 0] | 0;\r\n          a[c + 1 >> 0] = a[d + 1 >> 0] | 0;\r\n          a[c + 2 >> 0] = a[d + 2 >> 0] | 0;\r\n          a[c + 3 >> 0] = a[d + 3 >> 0] | 0;\r\n          c = c + 4 | 0;\r\n          d = d + 4 | 0;\r\n        }\r\n      }\r\n      while ((c | 0) < (g | 0)) {\r\n        a[c >> 0] = a[d >> 0] | 0;\r\n        c = c + 1 | 0;\r\n        d = d + 1 | 0;\r\n      }\r\n      return h | 0;\r\n    }\r\n    function Xd(c, d, e) {\r\n      c = c | 0;\r\n      d = d | 0;\r\n      e = e | 0;\r\n      var f = 0,\r\n        g = 0,\r\n        h = 0,\r\n        i = 0;\r\n      h = c + e | 0;\r\n      d = d & 255;\r\n      if ((e | 0) >= 67) {\r\n        while (c & 3) {\r\n          a[c >> 0] = d;\r\n          c = c + 1 | 0;\r\n        }\r\n        f = h & -4 | 0;\r\n        i = d | d << 8 | d << 16 | d << 24;\r\n        g = f - 64 | 0;\r\n        while ((c | 0) <= (g | 0)) {\r\n          b[c >> 2] = i;\r\n          b[c + 4 >> 2] = i;\r\n          b[c + 8 >> 2] = i;\r\n          b[c + 12 >> 2] = i;\r\n          b[c + 16 >> 2] = i;\r\n          b[c + 20 >> 2] = i;\r\n          b[c + 24 >> 2] = i;\r\n          b[c + 28 >> 2] = i;\r\n          b[c + 32 >> 2] = i;\r\n          b[c + 36 >> 2] = i;\r\n          b[c + 40 >> 2] = i;\r\n          b[c + 44 >> 2] = i;\r\n          b[c + 48 >> 2] = i;\r\n          b[c + 52 >> 2] = i;\r\n          b[c + 56 >> 2] = i;\r\n          b[c + 60 >> 2] = i;\r\n          c = c + 64 | 0;\r\n        }\r\n        while ((c | 0) < (f | 0)) {\r\n          b[c >> 2] = i;\r\n          c = c + 4 | 0;\r\n        }\r\n      }\r\n      while ((c | 0) < (h | 0)) {\r\n        a[c >> 0] = d;\r\n        c = c + 1 | 0;\r\n      }\r\n      return h - e | 0;\r\n    }\r\n    function Yd(a) {\r\n      a = +a;\r\n      return a >= 0.0 ? +p(a + .5) : +A(a - .5);\r\n    }\r\n    function Zd(a) {\r\n      a = a | 0;\r\n      var c = 0,\r\n        d = 0,\r\n        e = 0;\r\n      e = K() | 0;\r\n      d = b[g >> 2] | 0;\r\n      c = d + a | 0;\r\n      if ((a | 0) > 0 & (c | 0) < (d | 0) | (c | 0) < 0) {\r\n        N(c | 0) | 0;\r\n        J(12);\r\n        return -1;\r\n      }\r\n      if ((c | 0) > (e | 0)) { if (!(M(c | 0) | 0)) {\r\n        J(12);\r\n        return -1;\r\n      } }\r\n      b[g >> 2] = c;\r\n      return d | 0;\r\n    }\r\n\r\n    // EMSCRIPTEN_END_FUNCS\r\n    return {\r\n      ___divdi3: Kd,\r\n      ___muldi3: Md,\r\n      ___remdi3: Nd,\r\n      ___uremdi3: Od,\r\n      _areNeighborCells: ib,\r\n      _bitshift64Ashr: Pd,\r\n      _bitshift64Lshr: Qd,\r\n      _bitshift64Shl: Rd,\r\n      _calloc: Fd,\r\n      _cellAreaKm2: xc,\r\n      _cellAreaM2: yc,\r\n      _cellAreaRads2: wc,\r\n      _cellToBoundary: _b,\r\n      _cellToCenterChild: Kb,\r\n      _cellToChildPos: dc,\r\n      _cellToChildren: Ib,\r\n      _cellToChildrenSize: Gb,\r\n      _cellToLatLng: Zb,\r\n      _cellToLocalIj: Jc,\r\n      _cellToParent: Fb,\r\n      _cellToVertex: qd,\r\n      _cellToVertexes: rd,\r\n      _cellsToDirectedEdge: jb,\r\n      _cellsToLinkedMultiPolygon: na,\r\n      _childPosToCell: ec,\r\n      _compactCells: Lb,\r\n      _destroyLinkedMultiPolygon: Ec,\r\n      _directedEdgeToBoundary: pb,\r\n      _directedEdgeToCells: nb,\r\n      _edgeLengthKm: Ac,\r\n      _edgeLengthM: Bc,\r\n      _edgeLengthRads: zc,\r\n      _emscripten_replace_memory: W,\r\n      _free: Ed,\r\n      _getBaseCellNumber: Cb,\r\n      _getDirectedEdgeDestination: lb,\r\n      _getDirectedEdgeOrigin: kb,\r\n      _getHexagonAreaAvgKm2: qc,\r\n      _getHexagonAreaAvgM2: rc,\r\n      _getHexagonEdgeLengthAvgKm: sc,\r\n      _getHexagonEdgeLengthAvgM: tc,\r\n      _getIcosahedronFaces: ac,\r\n      _getNumCells: uc,\r\n      _getPentagons: cc,\r\n      _getRes0Cells: ya,\r\n      _getResolution: Bb,\r\n      _greatCircleDistanceKm: mc,\r\n      _greatCircleDistanceM: nc,\r\n      _greatCircleDistanceRads: lc,\r\n      _gridDisk: aa,\r\n      _gridDiskDistances: ba,\r\n      _gridDistance: Lc,\r\n      _gridPathCells: Nc,\r\n      _gridPathCellsSize: Mc,\r\n      _gridRing: fa,\r\n      _gridRingUnsafe: ga,\r\n      _i64Add: Gd,\r\n      _i64Subtract: Hd,\r\n      _isPentagon: Hb,\r\n      _isResClassIII: Ob,\r\n      _isValidCell: Db,\r\n      _isValidDirectedEdge: mb,\r\n      _isValidVertex: td,\r\n      _latLngToCell: Wb,\r\n      _llvm_ctlz_i64: Sd,\r\n      _llvm_maxnum_f64: Td,\r\n      _llvm_minnum_f64: Ud,\r\n      _llvm_round_f64: Vd,\r\n      _localIjToCell: Kc,\r\n      _malloc: Dd,\r\n      _maxFaceCount: $b,\r\n      _maxGridDiskSize: $,\r\n      _maxPolygonToCellsSize: ja,\r\n      _maxPolygonToCellsSizeExperimental: Uc,\r\n      _memcpy: Wd,\r\n      _memset: Xd,\r\n      _originToDirectedEdges: ob,\r\n      _pentagonCount: bc,\r\n      _polygonToCells: la,\r\n      _polygonToCellsExperimental: Tc,\r\n      _readInt64AsDoubleFromPointer: id,\r\n      _res0CellCount: xa,\r\n      _round: Yd,\r\n      _sbrk: Zd,\r\n      _sizeOfCellBoundary: dd,\r\n      _sizeOfCoordIJ: hd,\r\n      _sizeOfGeoLoop: ed,\r\n      _sizeOfGeoPolygon: fd,\r\n      _sizeOfH3Index: bd,\r\n      _sizeOfLatLng: cd,\r\n      _sizeOfLinkedGeoPolygon: gd,\r\n      _uncompactCells: Mb,\r\n      _uncompactCellsSize: Nb,\r\n      _vertexToLatLng: sd,\r\n      establishStackSpace: _,\r\n      stackAlloc: X,\r\n      stackRestore: Z,\r\n      stackSave: Y\r\n    };\r\n  }\r\n\r\n  // EMSCRIPTEN_END_ASM\r\n  )(asmGlobalArg, asmLibraryArg, buffer);\r\n  var ___divdi3 = Module[\"___divdi3\"] = asm[\"___divdi3\"];\r\n  var ___muldi3 = Module[\"___muldi3\"] = asm[\"___muldi3\"];\r\n  var ___remdi3 = Module[\"___remdi3\"] = asm[\"___remdi3\"];\r\n  var ___uremdi3 = Module[\"___uremdi3\"] = asm[\"___uremdi3\"];\r\n  var _areNeighborCells = Module[\"_areNeighborCells\"] = asm[\"_areNeighborCells\"];\r\n  var _bitshift64Ashr = Module[\"_bitshift64Ashr\"] = asm[\"_bitshift64Ashr\"];\r\n  var _bitshift64Lshr = Module[\"_bitshift64Lshr\"] = asm[\"_bitshift64Lshr\"];\r\n  var _bitshift64Shl = Module[\"_bitshift64Shl\"] = asm[\"_bitshift64Shl\"];\r\n  var _calloc = Module[\"_calloc\"] = asm[\"_calloc\"];\r\n  var _cellAreaKm2 = Module[\"_cellAreaKm2\"] = asm[\"_cellAreaKm2\"];\r\n  var _cellAreaM2 = Module[\"_cellAreaM2\"] = asm[\"_cellAreaM2\"];\r\n  var _cellAreaRads2 = Module[\"_cellAreaRads2\"] = asm[\"_cellAreaRads2\"];\r\n  var _cellToBoundary = Module[\"_cellToBoundary\"] = asm[\"_cellToBoundary\"];\r\n  var _cellToCenterChild = Module[\"_cellToCenterChild\"] = asm[\"_cellToCenterChild\"];\r\n  var _cellToChildPos = Module[\"_cellToChildPos\"] = asm[\"_cellToChildPos\"];\r\n  var _cellToChildren = Module[\"_cellToChildren\"] = asm[\"_cellToChildren\"];\r\n  var _cellToChildrenSize = Module[\"_cellToChildrenSize\"] = asm[\"_cellToChildrenSize\"];\r\n  var _cellToLatLng = Module[\"_cellToLatLng\"] = asm[\"_cellToLatLng\"];\r\n  var _cellToLocalIj = Module[\"_cellToLocalIj\"] = asm[\"_cellToLocalIj\"];\r\n  var _cellToParent = Module[\"_cellToParent\"] = asm[\"_cellToParent\"];\r\n  var _cellToVertex = Module[\"_cellToVertex\"] = asm[\"_cellToVertex\"];\r\n  var _cellToVertexes = Module[\"_cellToVertexes\"] = asm[\"_cellToVertexes\"];\r\n  var _cellsToDirectedEdge = Module[\"_cellsToDirectedEdge\"] = asm[\"_cellsToDirectedEdge\"];\r\n  var _cellsToLinkedMultiPolygon = Module[\"_cellsToLinkedMultiPolygon\"] = asm[\"_cellsToLinkedMultiPolygon\"];\r\n  var _childPosToCell = Module[\"_childPosToCell\"] = asm[\"_childPosToCell\"];\r\n  var _compactCells = Module[\"_compactCells\"] = asm[\"_compactCells\"];\r\n  var _destroyLinkedMultiPolygon = Module[\"_destroyLinkedMultiPolygon\"] = asm[\"_destroyLinkedMultiPolygon\"];\r\n  var _directedEdgeToBoundary = Module[\"_directedEdgeToBoundary\"] = asm[\"_directedEdgeToBoundary\"];\r\n  var _directedEdgeToCells = Module[\"_directedEdgeToCells\"] = asm[\"_directedEdgeToCells\"];\r\n  var _edgeLengthKm = Module[\"_edgeLengthKm\"] = asm[\"_edgeLengthKm\"];\r\n  var _edgeLengthM = Module[\"_edgeLengthM\"] = asm[\"_edgeLengthM\"];\r\n  var _edgeLengthRads = Module[\"_edgeLengthRads\"] = asm[\"_edgeLengthRads\"];\r\n  var _emscripten_replace_memory = Module[\"_emscripten_replace_memory\"] = asm[\"_emscripten_replace_memory\"];\r\n  var _free = Module[\"_free\"] = asm[\"_free\"];\r\n  var _getBaseCellNumber = Module[\"_getBaseCellNumber\"] = asm[\"_getBaseCellNumber\"];\r\n  var _getDirectedEdgeDestination = Module[\"_getDirectedEdgeDestination\"] = asm[\"_getDirectedEdgeDestination\"];\r\n  var _getDirectedEdgeOrigin = Module[\"_getDirectedEdgeOrigin\"] = asm[\"_getDirectedEdgeOrigin\"];\r\n  var _getHexagonAreaAvgKm2 = Module[\"_getHexagonAreaAvgKm2\"] = asm[\"_getHexagonAreaAvgKm2\"];\r\n  var _getHexagonAreaAvgM2 = Module[\"_getHexagonAreaAvgM2\"] = asm[\"_getHexagonAreaAvgM2\"];\r\n  var _getHexagonEdgeLengthAvgKm = Module[\"_getHexagonEdgeLengthAvgKm\"] = asm[\"_getHexagonEdgeLengthAvgKm\"];\r\n  var _getHexagonEdgeLengthAvgM = Module[\"_getHexagonEdgeLengthAvgM\"] = asm[\"_getHexagonEdgeLengthAvgM\"];\r\n  var _getIcosahedronFaces = Module[\"_getIcosahedronFaces\"] = asm[\"_getIcosahedronFaces\"];\r\n  var _getNumCells = Module[\"_getNumCells\"] = asm[\"_getNumCells\"];\r\n  var _getPentagons = Module[\"_getPentagons\"] = asm[\"_getPentagons\"];\r\n  var _getRes0Cells = Module[\"_getRes0Cells\"] = asm[\"_getRes0Cells\"];\r\n  var _getResolution = Module[\"_getResolution\"] = asm[\"_getResolution\"];\r\n  var _greatCircleDistanceKm = Module[\"_greatCircleDistanceKm\"] = asm[\"_greatCircleDistanceKm\"];\r\n  var _greatCircleDistanceM = Module[\"_greatCircleDistanceM\"] = asm[\"_greatCircleDistanceM\"];\r\n  var _greatCircleDistanceRads = Module[\"_greatCircleDistanceRads\"] = asm[\"_greatCircleDistanceRads\"];\r\n  var _gridDisk = Module[\"_gridDisk\"] = asm[\"_gridDisk\"];\r\n  var _gridDiskDistances = Module[\"_gridDiskDistances\"] = asm[\"_gridDiskDistances\"];\r\n  var _gridDistance = Module[\"_gridDistance\"] = asm[\"_gridDistance\"];\r\n  var _gridPathCells = Module[\"_gridPathCells\"] = asm[\"_gridPathCells\"];\r\n  var _gridPathCellsSize = Module[\"_gridPathCellsSize\"] = asm[\"_gridPathCellsSize\"];\r\n  var _gridRing = Module[\"_gridRing\"] = asm[\"_gridRing\"];\r\n  var _gridRingUnsafe = Module[\"_gridRingUnsafe\"] = asm[\"_gridRingUnsafe\"];\r\n  var _i64Add = Module[\"_i64Add\"] = asm[\"_i64Add\"];\r\n  var _i64Subtract = Module[\"_i64Subtract\"] = asm[\"_i64Subtract\"];\r\n  var _isPentagon = Module[\"_isPentagon\"] = asm[\"_isPentagon\"];\r\n  var _isResClassIII = Module[\"_isResClassIII\"] = asm[\"_isResClassIII\"];\r\n  var _isValidCell = Module[\"_isValidCell\"] = asm[\"_isValidCell\"];\r\n  var _isValidDirectedEdge = Module[\"_isValidDirectedEdge\"] = asm[\"_isValidDirectedEdge\"];\r\n  var _isValidVertex = Module[\"_isValidVertex\"] = asm[\"_isValidVertex\"];\r\n  var _latLngToCell = Module[\"_latLngToCell\"] = asm[\"_latLngToCell\"];\r\n  var _llvm_ctlz_i64 = Module[\"_llvm_ctlz_i64\"] = asm[\"_llvm_ctlz_i64\"];\r\n  var _llvm_maxnum_f64 = Module[\"_llvm_maxnum_f64\"] = asm[\"_llvm_maxnum_f64\"];\r\n  var _llvm_minnum_f64 = Module[\"_llvm_minnum_f64\"] = asm[\"_llvm_minnum_f64\"];\r\n  var _llvm_round_f64 = Module[\"_llvm_round_f64\"] = asm[\"_llvm_round_f64\"];\r\n  var _localIjToCell = Module[\"_localIjToCell\"] = asm[\"_localIjToCell\"];\r\n  var _malloc = Module[\"_malloc\"] = asm[\"_malloc\"];\r\n  var _maxFaceCount = Module[\"_maxFaceCount\"] = asm[\"_maxFaceCount\"];\r\n  var _maxGridDiskSize = Module[\"_maxGridDiskSize\"] = asm[\"_maxGridDiskSize\"];\r\n  var _maxPolygonToCellsSize = Module[\"_maxPolygonToCellsSize\"] = asm[\"_maxPolygonToCellsSize\"];\r\n  var _maxPolygonToCellsSizeExperimental = Module[\"_maxPolygonToCellsSizeExperimental\"] = asm[\"_maxPolygonToCellsSizeExperimental\"];\r\n  var _memcpy = Module[\"_memcpy\"] = asm[\"_memcpy\"];\r\n  var _memset = Module[\"_memset\"] = asm[\"_memset\"];\r\n  var _originToDirectedEdges = Module[\"_originToDirectedEdges\"] = asm[\"_originToDirectedEdges\"];\r\n  var _pentagonCount = Module[\"_pentagonCount\"] = asm[\"_pentagonCount\"];\r\n  var _polygonToCells = Module[\"_polygonToCells\"] = asm[\"_polygonToCells\"];\r\n  var _polygonToCellsExperimental = Module[\"_polygonToCellsExperimental\"] = asm[\"_polygonToCellsExperimental\"];\r\n  var _readInt64AsDoubleFromPointer = Module[\"_readInt64AsDoubleFromPointer\"] = asm[\"_readInt64AsDoubleFromPointer\"];\r\n  var _res0CellCount = Module[\"_res0CellCount\"] = asm[\"_res0CellCount\"];\r\n  var _round = Module[\"_round\"] = asm[\"_round\"];\r\n  var _sbrk = Module[\"_sbrk\"] = asm[\"_sbrk\"];\r\n  var _sizeOfCellBoundary = Module[\"_sizeOfCellBoundary\"] = asm[\"_sizeOfCellBoundary\"];\r\n  var _sizeOfCoordIJ = Module[\"_sizeOfCoordIJ\"] = asm[\"_sizeOfCoordIJ\"];\r\n  var _sizeOfGeoLoop = Module[\"_sizeOfGeoLoop\"] = asm[\"_sizeOfGeoLoop\"];\r\n  var _sizeOfGeoPolygon = Module[\"_sizeOfGeoPolygon\"] = asm[\"_sizeOfGeoPolygon\"];\r\n  var _sizeOfH3Index = Module[\"_sizeOfH3Index\"] = asm[\"_sizeOfH3Index\"];\r\n  var _sizeOfLatLng = Module[\"_sizeOfLatLng\"] = asm[\"_sizeOfLatLng\"];\r\n  var _sizeOfLinkedGeoPolygon = Module[\"_sizeOfLinkedGeoPolygon\"] = asm[\"_sizeOfLinkedGeoPolygon\"];\r\n  var _uncompactCells = Module[\"_uncompactCells\"] = asm[\"_uncompactCells\"];\r\n  var _uncompactCellsSize = Module[\"_uncompactCellsSize\"] = asm[\"_uncompactCellsSize\"];\r\n  var _vertexToLatLng = Module[\"_vertexToLatLng\"] = asm[\"_vertexToLatLng\"];\r\n  var establishStackSpace = Module[\"establishStackSpace\"] = asm[\"establishStackSpace\"];\r\n  var stackAlloc = Module[\"stackAlloc\"] = asm[\"stackAlloc\"];\r\n  var stackRestore = Module[\"stackRestore\"] = asm[\"stackRestore\"];\r\n  var stackSave = Module[\"stackSave\"] = asm[\"stackSave\"];\r\n  Module[\"asm\"] = asm;\r\n  Module[\"cwrap\"] = cwrap;\r\n  Module[\"setValue\"] = setValue;\r\n  Module[\"getValue\"] = getValue;\r\n  if (memoryInitializer) {\r\n    if (!isDataURI(memoryInitializer)) {\r\n      memoryInitializer = locateFile(memoryInitializer);\r\n    }\r\n    if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {\r\n      var data = readBinary(memoryInitializer);\r\n      HEAPU8.set(data, GLOBAL_BASE);\r\n    } else {\r\n      addRunDependency(\"memory initializer\");\r\n      var applyMemoryInitializer = function (data) {\r\n        if (data.byteLength) { data = new Uint8Array(data); }\r\n        HEAPU8.set(data, GLOBAL_BASE);\r\n        if (Module[\"memoryInitializerRequest\"]) { delete Module[\"memoryInitializerRequest\"].response; }\r\n        removeRunDependency(\"memory initializer\");\r\n      };\r\n      var doBrowserLoad = function () {\r\n        readAsync(memoryInitializer, applyMemoryInitializer, function () {\r\n          throw \"could not load memory initializer \" + memoryInitializer;\r\n        });\r\n      };\r\n      var memoryInitializerBytes = tryParseAsDataURI(memoryInitializer);\r\n      if (memoryInitializerBytes) {\r\n        applyMemoryInitializer(memoryInitializerBytes.buffer);\r\n      } else if (Module[\"memoryInitializerRequest\"]) {\r\n        var useRequest = function () {\r\n          var request = Module[\"memoryInitializerRequest\"];\r\n          var response = request.response;\r\n          if (request.status !== 200 && request.status !== 0) {\r\n            var data = tryParseAsDataURI(Module[\"memoryInitializerRequestURL\"]);\r\n            if (data) {\r\n              response = data.buffer;\r\n            } else {\r\n              console.warn(\"a problem seems to have happened with Module.memoryInitializerRequest, status: \" + request.status + \", retrying \" + memoryInitializer);\r\n              doBrowserLoad();\r\n              return;\r\n            }\r\n          }\r\n          applyMemoryInitializer(response);\r\n        };\r\n        if (Module[\"memoryInitializerRequest\"].response) {\r\n          setTimeout(useRequest, 0);\r\n        } else {\r\n          Module[\"memoryInitializerRequest\"].addEventListener(\"load\", useRequest);\r\n        }\r\n      } else {\r\n        doBrowserLoad();\r\n      }\r\n    }\r\n  }\r\n  var calledRun;\r\n  dependenciesFulfilled = function runCaller() {\r\n    if (!calledRun) { run(); }\r\n    if (!calledRun) { dependenciesFulfilled = runCaller; }\r\n  };\r\n  function run(args) {\r\n    args = args || arguments_;\r\n    if (runDependencies > 0) {\r\n      return;\r\n    }\r\n    preRun();\r\n    if (runDependencies > 0) { return; }\r\n    function doRun() {\r\n      if (calledRun) { return; }\r\n      calledRun = true;\r\n      if (ABORT) { return; }\r\n      initRuntime();\r\n      preMain();\r\n      if (Module[\"onRuntimeInitialized\"]) { Module[\"onRuntimeInitialized\"](); }\r\n      postRun();\r\n    }\r\n    if (Module[\"setStatus\"]) {\r\n      Module[\"setStatus\"](\"Running...\");\r\n      setTimeout(function () {\r\n        setTimeout(function () {\r\n          Module[\"setStatus\"](\"\");\r\n        }, 1);\r\n        doRun();\r\n      }, 1);\r\n    } else {\r\n      doRun();\r\n    }\r\n  }\r\n  Module[\"run\"] = run;\r\n  function abort(what) {\r\n    if (Module[\"onAbort\"]) {\r\n      Module[\"onAbort\"](what);\r\n    }\r\n    what += \"\";\r\n    out(what);\r\n    err(what);\r\n    ABORT = true;\r\n    throw \"abort(\" + what + \"). Build with -s ASSERTIONS=1 for more info.\";\r\n  }\r\n  Module[\"abort\"] = abort;\r\n  if (Module[\"preInit\"]) {\r\n    if (typeof Module[\"preInit\"] == \"function\") { Module[\"preInit\"] = [Module[\"preInit\"]]; }\r\n    while (Module[\"preInit\"].length > 0) {\r\n      Module[\"preInit\"].pop()();\r\n    }\r\n  }\r\n  run();\r\n  return libh3;\r\n}(typeof libh3 === 'object' ? libh3 : {});\r\n\r\n/*\r\n * Copyright 2018-2019, 2022 Uber Technologies, Inc.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *         http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n// Define the C bindings for the h3 library\r\n\r\n// Add some aliases to make the function definitions more intelligible\r\nvar NUMBER = 'number';\r\nvar H3_ERROR = NUMBER;\r\nvar BOOLEAN = NUMBER;\r\nvar H3_LOWER = NUMBER;\r\nvar H3_UPPER = NUMBER;\r\nvar RESOLUTION = NUMBER;\r\nvar POINTER = NUMBER;\r\n\r\n// Define the bindings to functions in the C lib. Functions are defined as\r\n// [name, return type, [arg types]]. You must run `npm run build-emscripten`\r\n// before new functions added here will be available.\r\n/** @type {([string, string] | [string, string | null, string[]])[]} */\r\nvar BINDINGS = [\r\n// The size functions are inserted via build/sizes.h\r\n['sizeOfH3Index', NUMBER], ['sizeOfLatLng', NUMBER], ['sizeOfCellBoundary', NUMBER], ['sizeOfGeoLoop', NUMBER], ['sizeOfGeoPolygon', NUMBER], ['sizeOfLinkedGeoPolygon', NUMBER], ['sizeOfCoordIJ', NUMBER], ['readInt64AsDoubleFromPointer', NUMBER],\r\n// The remaining functions are defined in the core lib in h3Api.h\r\n['isValidCell', BOOLEAN, [H3_LOWER, H3_UPPER]], ['latLngToCell', H3_ERROR, [NUMBER, NUMBER, RESOLUTION, POINTER]], ['cellToLatLng', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['cellToBoundary', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['maxGridDiskSize', H3_ERROR, [NUMBER, POINTER]], ['gridDisk', H3_ERROR, [H3_LOWER, H3_UPPER, NUMBER, POINTER]], ['gridDiskDistances', H3_ERROR, [H3_LOWER, H3_UPPER, NUMBER, POINTER, POINTER]], ['gridRing', H3_ERROR, [H3_LOWER, H3_UPPER, NUMBER, POINTER]], ['gridRingUnsafe', H3_ERROR, [H3_LOWER, H3_UPPER, NUMBER, POINTER]], ['maxPolygonToCellsSize', H3_ERROR, [POINTER, RESOLUTION, NUMBER, POINTER]], ['polygonToCells', H3_ERROR, [POINTER, RESOLUTION, NUMBER, POINTER]], ['maxPolygonToCellsSizeExperimental', H3_ERROR, [POINTER, RESOLUTION, NUMBER, POINTER]], ['polygonToCellsExperimental', H3_ERROR, [POINTER, RESOLUTION, NUMBER, NUMBER, NUMBER, POINTER]], ['cellsToLinkedMultiPolygon', H3_ERROR, [POINTER, NUMBER, POINTER]], ['destroyLinkedMultiPolygon', null, [POINTER]], ['compactCells', H3_ERROR, [POINTER, POINTER, NUMBER, NUMBER]], ['uncompactCells', H3_ERROR, [POINTER, NUMBER, NUMBER, POINTER, NUMBER, RESOLUTION]], ['uncompactCellsSize', H3_ERROR, [POINTER, NUMBER, NUMBER, RESOLUTION, POINTER]], ['isPentagon', BOOLEAN, [H3_LOWER, H3_UPPER]], ['isResClassIII', BOOLEAN, [H3_LOWER, H3_UPPER]], ['getBaseCellNumber', NUMBER, [H3_LOWER, H3_UPPER]], ['getResolution', NUMBER, [H3_LOWER, H3_UPPER]], ['maxFaceCount', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['getIcosahedronFaces', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['cellToParent', H3_ERROR, [H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['cellToChildren', H3_ERROR, [H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['cellToCenterChild', H3_ERROR, [H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['cellToChildrenSize', H3_ERROR, [H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['cellToChildPos', H3_ERROR, [H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['childPosToCell', H3_ERROR, [NUMBER, NUMBER, H3_LOWER, H3_UPPER, RESOLUTION, POINTER]], ['areNeighborCells', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, POINTER]], ['cellsToDirectedEdge', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, POINTER]], ['getDirectedEdgeOrigin', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['getDirectedEdgeDestination', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['isValidDirectedEdge', BOOLEAN, [H3_LOWER, H3_UPPER]], ['directedEdgeToCells', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['originToDirectedEdges', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['directedEdgeToBoundary', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['gridDistance', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, POINTER]], ['gridPathCells', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, POINTER]], ['gridPathCellsSize', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, POINTER]], ['cellToLocalIj', H3_ERROR, [H3_LOWER, H3_UPPER, H3_LOWER, H3_UPPER, NUMBER, POINTER]], ['localIjToCell', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER, NUMBER, POINTER]], ['getHexagonAreaAvgM2', H3_ERROR, [RESOLUTION, POINTER]], ['getHexagonAreaAvgKm2', H3_ERROR, [RESOLUTION, POINTER]], ['getHexagonEdgeLengthAvgM', H3_ERROR, [RESOLUTION, POINTER]], ['getHexagonEdgeLengthAvgKm', H3_ERROR, [RESOLUTION, POINTER]], ['greatCircleDistanceM', NUMBER, [POINTER, POINTER]], ['greatCircleDistanceKm', NUMBER, [POINTER, POINTER]], ['greatCircleDistanceRads', NUMBER, [POINTER, POINTER]], ['cellAreaM2', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['cellAreaKm2', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['cellAreaRads2', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['edgeLengthM', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['edgeLengthKm', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['edgeLengthRads', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['getNumCells', H3_ERROR, [RESOLUTION, POINTER]], ['getRes0Cells', H3_ERROR, [POINTER]], ['res0CellCount', NUMBER], ['getPentagons', H3_ERROR, [NUMBER, POINTER]], ['pentagonCount', NUMBER], ['cellToVertex', H3_ERROR, [H3_LOWER, H3_UPPER, NUMBER, POINTER]], ['cellToVertexes', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['vertexToLatLng', H3_ERROR, [H3_LOWER, H3_UPPER, POINTER]], ['isValidVertex', BOOLEAN, [H3_LOWER, H3_UPPER]]];\r\n\r\n/*\r\n * Copyright 2018-2019, 2022 Uber Technologies, Inc.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *         http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n// Error codes from the code library, aliased here for legibility\r\nvar E_SUCCESS = 0;\r\nvar E_FAILED = 1;\r\nvar E_DOMAIN = 2;\r\nvar E_LATLNG_DOMAIN = 3;\r\nvar E_RES_DOMAIN = 4;\r\nvar E_CELL_INVALID = 5;\r\nvar E_DIR_EDGE_INVALID = 6;\r\nvar E_UNDIR_EDGE_INVALID = 7;\r\nvar E_VERTEX_INVALID = 8;\r\nvar E_PENTAGON = 9;\r\nvar E_DUPLICATE_INPUT = 10;\r\nvar E_NOT_NEIGHBORS = 11;\r\nvar E_RES_MISMATCH = 12;\r\nvar E_MEMORY_ALLOC = 13;\r\nvar E_MEMORY_BOUNDS = 14;\r\nvar E_OPTION_INVALID = 15;\r\n\r\n/**\r\n * Error messages corresponding to the core library error codes. See\r\n * https://h3geo.org/docs/library/errors#table-of-error-codes\r\n * @private\r\n */\r\nvar H3_ERROR_MSGS = {};\r\nH3_ERROR_MSGS[E_SUCCESS] = 'Success';\r\nH3_ERROR_MSGS[E_FAILED] = 'The operation failed but a more specific error is not available';\r\nH3_ERROR_MSGS[E_DOMAIN] = 'Argument was outside of acceptable range';\r\nH3_ERROR_MSGS[E_LATLNG_DOMAIN] = 'Latitude or longitude arguments were outside of acceptable range';\r\nH3_ERROR_MSGS[E_RES_DOMAIN] = 'Resolution argument was outside of acceptable range';\r\nH3_ERROR_MSGS[E_CELL_INVALID] = 'Cell argument was not valid';\r\nH3_ERROR_MSGS[E_DIR_EDGE_INVALID] = 'Directed edge argument was not valid';\r\nH3_ERROR_MSGS[E_UNDIR_EDGE_INVALID] = 'Undirected edge argument was not valid';\r\nH3_ERROR_MSGS[E_VERTEX_INVALID] = 'Vertex argument was not valid';\r\nH3_ERROR_MSGS[E_PENTAGON] = 'Pentagon distortion was encountered';\r\nH3_ERROR_MSGS[E_DUPLICATE_INPUT] = 'Duplicate input';\r\nH3_ERROR_MSGS[E_NOT_NEIGHBORS] = 'Cell arguments were not neighbors';\r\nH3_ERROR_MSGS[E_RES_MISMATCH] = 'Cell arguments had incompatible resolutions';\r\nH3_ERROR_MSGS[E_MEMORY_ALLOC] = 'Memory allocation failed';\r\nH3_ERROR_MSGS[E_MEMORY_BOUNDS] = 'Bounds of provided memory were insufficient';\r\nH3_ERROR_MSGS[E_OPTION_INVALID] = 'Mode or flags argument was not valid';\r\n\r\n// Error codes for JS errors thrown in the bindings\r\nvar E_UNKNOWN_UNIT = 1000;\r\nvar E_ARRAY_LENGTH = 1001;\r\nvar E_NULL_INDEX = 1002;\r\n\r\n/**\r\n * Error messages for errors thrown in the binding code. These don't strictly\r\n * need error codes, but it's simpler to treat all of the errors consistently\r\n * @private\r\n */\r\nvar JS_ERROR_MESSAGES = {};\r\nJS_ERROR_MESSAGES[E_UNKNOWN_UNIT] = 'Unknown unit';\r\nJS_ERROR_MESSAGES[E_ARRAY_LENGTH] = 'Array length out of bounds';\r\nJS_ERROR_MESSAGES[E_NULL_INDEX] = 'Got unexpected null value for H3 index';\r\nvar UNKNOWN_ERROR_MSG = 'Unknown error';\r\n\r\n/**\r\n * Create an error with an attached code\r\n * @private\r\n * @param {Record<number, string>} messages  Map of code-to-messages to use\r\n * @param {number} errCode                   Numeric error code\r\n * @param {{value: unknown} | {}} [meta]     Metadata with value to associate with the error\r\n */\r\nfunction createError(messages, errCode, meta) {\r\n  // The error value may be \"undefined\", so check if the argument was provided\r\n  var hasValue = meta && 'value' in meta;\r\n  // Throw a custom error type with the code attached\r\n  var err = new Error(((messages[errCode] || UNKNOWN_ERROR_MSG) + \" (code: \" + errCode + (hasValue ? (\", value: \" + (meta.value)) : '') + \")\"));\r\n  // @ts-expect-error - TS doesn't like extending Error\r\n  err.code = errCode;\r\n  return err;\r\n}\r\n\r\n/**\r\n * Custom error for H3Error codes\r\n * @private\r\n * @param {number} errCode     Error code from the H3 library\r\n * @param {unknown} [value]    Value to associate with the error, if any\r\n * @returns {Error}\r\n */\r\nfunction H3LibraryError(errCode, value) {\r\n  // The error value may be \"undefined\", so check if the argument was provided\r\n  var meta = arguments.length === 2 ? {\r\n    value: value\r\n  } : {};\r\n  return createError(H3_ERROR_MSGS, errCode, meta);\r\n}\r\n\r\n/**\r\n * Custom errors thrown from the JS bindings.\r\n * @private\r\n * @param {number} errCode     Error code from the H3 library\r\n * @param {unknown} [value]    Value to associate with the error, if any\r\n * @returns {Error}\r\n */\r\nfunction JSBindingError(errCode, value) {\r\n  // The error value may be \"undefined\", so check if the argument was provided\r\n  var meta = arguments.length === 2 ? {\r\n    value: value\r\n  } : {};\r\n  return createError(JS_ERROR_MESSAGES, errCode, meta);\r\n}\r\n\r\n/**\r\n * Throw a JavaScript error if the C library return code is an error\r\n * @private\r\n * @param {number} errCode     Error code from the H3 library\r\n * @throws {Error} Error if err is not E_SUCCESS (0)\r\n */\r\nfunction throwIfError(errCode) {\r\n  if (errCode !== 0) {\r\n    throw H3LibraryError(errCode);\r\n  }\r\n}\r\n\r\n/*\r\n * Copyright 2018-2019, 2022 Uber Technologies, Inc.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *         http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n/**\r\n * Map of C-defined functions\r\n * @type {any}\r\n * @private\r\n */\r\nvar H3 = {};\r\n\r\n// Create the bound functions themselves\r\nBINDINGS.forEach(function bind(def) {\r\n  H3[def[0]] = libh3.cwrap.apply(libh3, def);\r\n});\r\n\r\n// Alias the hexidecimal base for legibility\r\nvar BASE_16 = 16;\r\n\r\n// Alias unused bits for legibility\r\nvar UNUSED_UPPER_32_BITS = 0;\r\n\r\n// ----------------------------------------------------------------------------\r\n// Byte size imports\r\n\r\nvar SZ_INT = 4;\r\nvar SZ_PTR = 4;\r\nvar SZ_DBL = 8;\r\nvar SZ_INT64 = 8;\r\nvar SZ_H3INDEX = H3.sizeOfH3Index();\r\nvar SZ_LATLNG = H3.sizeOfLatLng();\r\nvar SZ_CELLBOUNDARY = H3.sizeOfCellBoundary();\r\nvar SZ_GEOPOLYGON = H3.sizeOfGeoPolygon();\r\nvar SZ_GEOLOOP = H3.sizeOfGeoLoop();\r\nvar SZ_LINKED_GEOPOLYGON = H3.sizeOfLinkedGeoPolygon();\r\nvar SZ_COORDIJ = H3.sizeOfCoordIJ();\r\n\r\n// ----------------------------------------------------------------------------\r\n// Custom types\r\n\r\n/**\r\n * 64-bit hexidecimal string representation of an H3 index\r\n * @static\r\n * @typedef {string} H3Index\r\n */\r\n\r\n/**\r\n * 64-bit hexidecimal string representation of an H3 index,\r\n * or two 32-bit integers in little endian order in an array.\r\n * @static\r\n * @typedef {string | number[]} H3IndexInput\r\n */\r\n\r\n/**\r\n * Coordinates as an `{i, j}` pair\r\n * @static\r\n * @typedef CoordIJ\r\n * @property {number} i\r\n * @property {number} j\r\n */\r\n\r\n/**\r\n * Custom JS Error instance with an attached error code. Error codes come from the\r\n * core H3 library and can be found [in the H3 docs](https://h3geo.org/docs/library/errors#table-of-error-codes).\r\n * @static\r\n * @typedef H3Error\r\n * @property {string} message\r\n * @property {number} code\r\n */\r\n\r\n/**\r\n * Pair of lat,lng coordinates (or lng,lat if GeoJSON output is specified)\r\n * @static\r\n * @typedef {number[]} CoordPair\r\n */\r\n\r\n/**\r\n * Pair of lower,upper 32-bit ints representing a 64-bit value\r\n * @static\r\n * @typedef {number[]} SplitLong\r\n */\r\n\r\n// ----------------------------------------------------------------------------\r\n// Unit constants\r\n\r\n/**\r\n * Length/Area units\r\n * @static\r\n * @property {string} m\r\n * @property {string} m2\r\n * @property {string} km\r\n * @property {string} km2\r\n * @property {string} rads\r\n * @property {string} rads2\r\n */\r\nvar UNITS = {\r\n  m: 'm',\r\n  m2: 'm2',\r\n  km: 'km',\r\n  km2: 'km2',\r\n  rads: 'rads',\r\n  rads2: 'rads2'\r\n};\r\n\r\n// ----------------------------------------------------------------------------\r\n// Flags\r\n\r\n/**\r\n * Mode flags for polygonToCells\r\n * @static\r\n * @property {string} containmentCenter\r\n * @property {string} containmentFull\r\n * @property {string} containmentOverlapping\r\n * @property {string} containmentOverlappingBbox\r\n */\r\nvar POLYGON_TO_CELLS_FLAGS = {\r\n  containmentCenter: 'containmentCenter',\r\n  containmentFull: 'containmentFull',\r\n  containmentOverlapping: 'containmentOverlapping',\r\n  containmentOverlappingBbox: 'containmentOverlappingBbox'\r\n};\r\n\r\n// ----------------------------------------------------------------------------\r\n// Utilities and helpers\r\n\r\n/**\r\n * @private\r\n * @param {string} flags Value from POLYGON_TO_CELLS_FLAGS\r\n * @returns {number} Flag value\r\n * @throws {H3Error} If invalid\r\n */\r\nfunction polygonToCellsFlagsToNumber(flags) {\r\n  switch (flags) {\r\n    case POLYGON_TO_CELLS_FLAGS.containmentCenter:\r\n      return 0;\r\n    case POLYGON_TO_CELLS_FLAGS.containmentFull:\r\n      return 1;\r\n    case POLYGON_TO_CELLS_FLAGS.containmentOverlapping:\r\n      return 2;\r\n    case POLYGON_TO_CELLS_FLAGS.containmentOverlappingBbox:\r\n      return 3;\r\n    default:\r\n      throw JSBindingError(E_OPTION_INVALID, flags);\r\n  }\r\n}\r\n\r\n/**\r\n * Validate a resolution, throwing an error if invalid\r\n * @private\r\n * @param  {unknown} res Value to validate\r\n * @return {number}      Valid res\r\n * @throws {H3Error}     If invalid\r\n */\r\nfunction validateRes(res) {\r\n  if (typeof res !== 'number' || res < 0 || res > 15 || Math.floor(res) !== res) {\r\n    throw H3LibraryError(E_RES_DOMAIN, res);\r\n  }\r\n  return res;\r\n}\r\n\r\n/**\r\n * Assert H3 index output, throwing an error if null\r\n * @private\r\n * @param {H3Index | null} h3Index    Index to validate\r\n * @return {H3Index}\r\n * @throws {H3Error}     If invalid\r\n */\r\nfunction validateH3Index(h3Index) {\r\n  if (!h3Index) { throw JSBindingError(E_NULL_INDEX); }\r\n  return h3Index;\r\n}\r\nvar MAX_JS_ARRAY_LENGTH = Math.pow(2, 32) - 1;\r\n\r\n/**\r\n * Validate an array length. JS will throw its own error if you try\r\n * to create an array larger than 2^32 - 1, but validating beforehand\r\n * allows us to exit early before we try to process large amounts\r\n * of data that won't even fit in an output array\r\n * @private\r\n * @param  {number} length  Length to validate\r\n * @return {number}         Valid array length\r\n * @throws {H3Error}        If invalid\r\n */\r\nfunction validateArrayLength(length) {\r\n  if (length > MAX_JS_ARRAY_LENGTH) {\r\n    throw JSBindingError(E_ARRAY_LENGTH, length);\r\n  }\r\n  return length;\r\n}\r\nvar INVALID_HEXIDECIMAL_CHAR = /[^0-9a-fA-F]/;\r\n\r\n/**\r\n * Convert an H3 index (64-bit hexidecimal string) into a \"split long\" - a pair of 32-bit ints\r\n * @param  {H3IndexInput} h3Index  H3 index to check\r\n * @return {SplitLong}             A two-element array with 32 lower bits and 32 upper bits\r\n */\r\nfunction h3IndexToSplitLong(h3Index) {\r\n  if (Array.isArray(h3Index) && h3Index.length === 2 && Number.isInteger(h3Index[0]) && Number.isInteger(h3Index[1])) {\r\n    return h3Index;\r\n  }\r\n  if (typeof h3Index !== 'string' || INVALID_HEXIDECIMAL_CHAR.test(h3Index)) {\r\n    return [0, 0];\r\n  }\r\n  var upper = parseInt(h3Index.substring(0, h3Index.length - 8), BASE_16);\r\n  var lower = parseInt(h3Index.substring(h3Index.length - 8), BASE_16);\r\n  return [lower, upper];\r\n}\r\n\r\n/**\r\n * Convert a 32-bit int to a hexdecimal string\r\n * @private\r\n * @param  {number} num  Integer to convert\r\n * @return {H3Index}     Hexidecimal string\r\n */\r\nfunction hexFrom32Bit(num) {\r\n  if (num >= 0) {\r\n    return num.toString(BASE_16);\r\n  }\r\n\r\n  // Handle negative numbers\r\n  num = num & 0x7fffffff;\r\n  var tempStr = zeroPad(8, num.toString(BASE_16));\r\n  var topNum = (parseInt(tempStr[0], BASE_16) + 8).toString(BASE_16);\r\n  tempStr = topNum + tempStr.substring(1);\r\n  return tempStr;\r\n}\r\n\r\n/**\r\n * Get a H3 index string from a split long (pair of 32-bit ints)\r\n * @param  {number} lower Lower 32 bits\r\n * @param  {number} upper Upper 32 bits\r\n * @return {H3Index}       H3 index\r\n */\r\nfunction splitLongToH3Index(lower, upper) {\r\n  return hexFrom32Bit(upper) + zeroPad(8, hexFrom32Bit(lower));\r\n}\r\n\r\n/**\r\n * Zero-pad a string to a given length\r\n * @private\r\n * @param  {number} fullLen Target length\r\n * @param  {string} numStr  String to zero-pad\r\n * @return {string}         Zero-padded string\r\n */\r\nfunction zeroPad(fullLen, numStr) {\r\n  var numZeroes = fullLen - numStr.length;\r\n  var outStr = '';\r\n  for (var i = 0; i < numZeroes; i++) {\r\n    outStr += '0';\r\n  }\r\n  outStr = outStr + numStr;\r\n  return outStr;\r\n}\r\n\r\n// One more than the max size of an unsigned 32-bit int.\r\n// Dividing by this number is equivalent to num >>> 32\r\nvar UPPER_BIT_DIVISOR = Math.pow(2, 32);\r\n\r\n/**\r\n * Convert a JS double-precision floating point number to a split long\r\n * @private\r\n * @param  {number} num  Number to convert\r\n * @return {SplitLong}     A two-element array with 32 lower bits and 32 upper bits\r\n */\r\nfunction numberToSplitLong(num) {\r\n  if (typeof num !== 'number') {\r\n    return [0, 0];\r\n  }\r\n  return [num | 0, num / UPPER_BIT_DIVISOR | 0];\r\n}\r\n\r\n/**\r\n * Populate a C-appropriate GeoLoop struct from a polygon array\r\n * @private\r\n * @param  {number[][]} polygonArray  Polygon, as an array of coordinate pairs\r\n * @param  {number}  geoLoop          C pointer to a GeoLoop struct\r\n * @param  {boolean} isGeoJson        Whether coordinates are in [lng, lat] order per GeoJSON spec\r\n * @return {number}                   C pointer to populated GeoLoop struct\r\n */\r\nfunction polygonArrayToGeoLoop(polygonArray, geoLoop, isGeoJson) {\r\n  var numVerts = polygonArray.length;\r\n  var geoCoordArray = libh3._calloc(numVerts, SZ_LATLNG);\r\n  // Support [lng, lat] pairs if GeoJSON is specified\r\n  var latIndex = isGeoJson ? 1 : 0;\r\n  var lngIndex = isGeoJson ? 0 : 1;\r\n  for (var i = 0; i < numVerts * 2; i += 2) {\r\n    libh3.HEAPF64.set([polygonArray[i / 2][latIndex], polygonArray[i / 2][lngIndex]].map(degsToRads), geoCoordArray / SZ_DBL + i);\r\n  }\r\n  libh3.HEAPU32.set([numVerts, geoCoordArray], geoLoop / SZ_INT);\r\n  return geoLoop;\r\n}\r\n\r\n/**\r\n * Create a C-appropriate GeoPolygon struct from an array of polygons\r\n * @private\r\n * @param  {number[][][]} coordinates Array of polygons, each an array of coordinate pairs\r\n * @param  {boolean} isGeoJson        Whether coordinates are in [lng, lat] order per GeoJSON spec\r\n * @return {number}                   C pointer to populated GeoPolygon struct\r\n */\r\nfunction coordinatesToGeoPolygon(coordinates, isGeoJson) {\r\n  // Any loops beyond the first loop are holes\r\n  var numHoles = coordinates.length - 1;\r\n  var geoPolygon = libh3._calloc(SZ_GEOPOLYGON);\r\n  // Byte positions within the struct\r\n  var geoLoopOffset = 0;\r\n  var numHolesOffset = geoLoopOffset + SZ_GEOLOOP;\r\n  var holesOffset = numHolesOffset + SZ_INT;\r\n  // geoLoop is first part of struct\r\n  polygonArrayToGeoLoop(coordinates[0], geoPolygon + geoLoopOffset, isGeoJson);\r\n  var holes;\r\n  if (numHoles > 0) {\r\n    holes = libh3._calloc(numHoles, SZ_GEOLOOP);\r\n    for (var i = 0; i < numHoles; i++) {\r\n      polygonArrayToGeoLoop(coordinates[i + 1], holes + SZ_GEOLOOP * i, isGeoJson);\r\n    }\r\n  }\r\n  libh3.setValue(geoPolygon + numHolesOffset, numHoles, 'i32');\r\n  libh3.setValue(geoPolygon + holesOffset, holes, 'i32');\r\n  return geoPolygon;\r\n}\r\n\r\n/**\r\n * Free memory allocated for a GeoPolygon struct. It is an error to access the struct\r\n * after passing it to this method.\r\n * @private\r\n * @param {number} geoPolygon     C pointer to GeoPolygon struct\r\n * @return {void}\r\n */\r\nfunction destroyGeoPolygon(geoPolygon) {\r\n  // Byte positions within the struct\r\n  var geoLoopOffset = 0;\r\n  var numHolesOffset = geoLoopOffset + SZ_GEOLOOP;\r\n  var holesOffset = numHolesOffset + SZ_INT;\r\n  // Offset of the geoLoop vertex array pointer within the GeoLoop struct\r\n  var geoLoopArrayOffset = SZ_INT;\r\n  // Free the outer vertex array\r\n  libh3._free(libh3.getValue(geoPolygon + geoLoopOffset + geoLoopArrayOffset, 'i8*'));\r\n  // Free the vertex array for the holes, if any\r\n  var numHoles = libh3.getValue(geoPolygon + numHolesOffset, 'i32');\r\n  if (numHoles > 0) {\r\n    var holes = libh3.getValue(geoPolygon + holesOffset, 'i32');\r\n    for (var i = 0; i < numHoles; i++) {\r\n      libh3._free(libh3.getValue(holes + SZ_GEOLOOP * i + geoLoopArrayOffset, 'i8*'));\r\n    }\r\n    libh3._free(holes);\r\n  }\r\n  libh3._free(geoPolygon);\r\n}\r\n\r\n/**\r\n * Read an H3 index from a pointer to C memory.\r\n * @private\r\n * @param  {number} cAddress  Pointer to allocated C memory\r\n * @param {number} offset     Offset, in number of H3 indexes, in case we're\r\n *                            reading an array\r\n * @return {H3Index | null}   H3 index, or null if index was invalid\r\n */\r\nfunction readH3IndexFromPointer(cAddress, offset) {\r\n  if ( offset === void 0 ) offset = 0;\r\n\r\n  var lower = libh3.getValue(cAddress + SZ_H3INDEX * offset, 'i32');\r\n  var upper = libh3.getValue(cAddress + SZ_H3INDEX * offset + SZ_INT, 'i32');\r\n  // The lower bits are allowed to be 0s, but if the upper bits are 0\r\n  // this represents an invalid H3 index\r\n  return upper ? splitLongToH3Index(lower, upper) : null;\r\n}\r\n\r\n/**\r\n * Read a boolean (32 bit) from a pointer to C memory.\r\n * @private\r\n * @param  {number} cAddress  Pointer to allocated C memory\r\n * @param {number} offset     Offset, in number of booleans, in case we're\r\n *                            reading an array\r\n * @return {Boolean} Boolean value\r\n */\r\nfunction readBooleanFromPointer(cAddress, offset) {\r\n  if ( offset === void 0 ) offset = 0;\r\n\r\n  var val = libh3.getValue(cAddress + SZ_INT * offset, 'i32');\r\n  return Boolean(val);\r\n}\r\n\r\n/**\r\n * Read a double from a pointer to C memory.\r\n * @private\r\n * @param  {number} cAddress  Pointer to allocated C memory\r\n * @param {number} offset     Offset, in number of doubles, in case we're\r\n *                            reading an array\r\n * @return {number} Double value\r\n */\r\nfunction readDoubleFromPointer(cAddress, offset) {\r\n  if ( offset === void 0 ) offset = 0;\r\n\r\n  return libh3.getValue(cAddress + SZ_DBL * offset, 'double');\r\n}\r\n\r\n/**\r\n * Read a 64-bit int from a pointer to C memory into a JS 64-bit float.\r\n * Note that this may lose precision if larger than MAX_SAFE_INTEGER\r\n * @private\r\n * @param  {number} cAddress  Pointer to allocated C memory\r\n * @return {number} Double value\r\n */\r\nfunction readInt64AsDoubleFromPointer(cAddress) {\r\n  return H3.readInt64AsDoubleFromPointer(cAddress);\r\n}\r\n\r\n/**\r\n * Store an H3 index in C memory. Primarily used as an efficient way to\r\n * write sets of hexagons.\r\n * @private\r\n * @param  {H3IndexInput} h3Index  H3 index to store\r\n * @param  {number} cAddress  Pointer to allocated C memory\r\n * @param {number} offset     Offset, in number of H3 indexes from beginning\r\n *                            of the current array\r\n */\r\nfunction storeH3Index(h3Index, cAddress, offset) {\r\n  // HEAPU32 is a typed array projection on the index space\r\n  // as unsigned 32-bit integers. This means the index needs\r\n  // to be divided by SZ_INT (4) to access correctly. Also,\r\n  // the H3 index is 64 bits, so we skip by twos as we're writing\r\n  // to 32-bit integers in the proper order.\r\n  libh3.HEAPU32.set(h3IndexToSplitLong(h3Index), cAddress / SZ_INT + 2 * offset);\r\n}\r\n\r\n/**\r\n * Read an array of 64-bit H3 indexes from C and convert to a JS array of\r\n * H3 index strings\r\n * @private\r\n * @param  {number} cAddress    Pointer to C ouput array\r\n * @param  {number} maxCount    Max number of hexagons in array. Hexagons with\r\n *                              the value 0 will be skipped, so this isn't\r\n *                              necessarily the length of the output array.\r\n * @return {H3Index[]}          Array of H3 indexes\r\n */\r\nfunction readArrayOfH3Indexes(cAddress, maxCount) {\r\n  var out = [];\r\n  for (var i = 0; i < maxCount; i++) {\r\n    var h3Index = readH3IndexFromPointer(cAddress, i);\r\n    if (h3Index !== null) {\r\n      out.push(h3Index);\r\n    }\r\n  }\r\n  return out;\r\n}\r\n\r\n/**\r\n * Store an array of H3 index strings as a C array of 64-bit integers.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C input array\r\n * @param  {H3IndexInput[]} hexagons H3 indexes to pass to the C lib\r\n */\r\nfunction storeArrayOfH3Indexes(cAddress, hexagons) {\r\n  // Assuming the cAddress points to an already appropriately\r\n  // allocated space\r\n  var count = hexagons.length;\r\n  for (var i = 0; i < count; i++) {\r\n    storeH3Index(hexagons[i], cAddress, i);\r\n  }\r\n}\r\n\r\n/**\r\n * Populate a C-appropriate LatLng struct from a [lat, lng] array\r\n * @private\r\n * @param {number} lat     Coordinate latitude\r\n * @param {number} lng     Coordinate longitude\r\n * @return {number}        C pointer to populated LatLng struct\r\n */\r\nfunction storeLatLng(lat, lng) {\r\n  var geoCoord = libh3._calloc(1, SZ_LATLNG);\r\n  libh3.HEAPF64.set([lat, lng].map(degsToRads), geoCoord / SZ_DBL);\r\n  return geoCoord;\r\n}\r\n\r\n/**\r\n * Read a single lat or lng value\r\n * @private\r\n * @param  {number} cAddress Pointer to C value\r\n * @return {number}\r\n */\r\nfunction readSingleCoord(cAddress) {\r\n  return radsToDegs(libh3.getValue(cAddress, 'double'));\r\n}\r\n\r\n/**\r\n * Read a LatLng from C and return a [lat, lng] pair.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C struct\r\n * @return {CoordPair}          [lat, lng] pair\r\n */\r\nfunction readLatLng(cAddress) {\r\n  return [readSingleCoord(cAddress), readSingleCoord(cAddress + SZ_DBL)];\r\n}\r\n\r\n/**\r\n * Read a LatLng from C and return a GeoJSON-style [lng, lat] pair.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C struct\r\n * @return {CoordPair}          [lng, lat] pair\r\n */\r\nfunction readLatLngGeoJson(cAddress) {\r\n  return [readSingleCoord(cAddress + SZ_DBL), readSingleCoord(cAddress)];\r\n}\r\n\r\n/**\r\n * Read the CellBoundary structure into a list of geo coordinate pairs\r\n * @private\r\n * @param {number}  cellBoundary       C pointer to CellBoundary struct\r\n * @param {boolean} [geoJsonCoords]    Whether to provide GeoJSON coordinate order: [lng, lat]\r\n * @param {boolean} [closedLoop]       Whether to close the loop\r\n * @return {CoordPair[]}               Array of geo coordinate pairs\r\n */\r\nfunction readCellBoundary(cellBoundary, geoJsonCoords, closedLoop) {\r\n  var numVerts = libh3.getValue(cellBoundary, 'i32');\r\n  // Note that though numVerts is an int, the coordinate doubles have to be\r\n  // aligned to 8 bytes, hence the 8-byte offset here\r\n  var vertsPos = cellBoundary + SZ_DBL;\r\n  var out = [];\r\n  // Support [lng, lat] pairs if GeoJSON is specified\r\n  var readCoord = geoJsonCoords ? readLatLngGeoJson : readLatLng;\r\n  for (var i = 0; i < numVerts * 2; i += 2) {\r\n    out.push(readCoord(vertsPos + SZ_DBL * i));\r\n  }\r\n  if (closedLoop) {\r\n    // Close loop if GeoJSON is specified\r\n    out.push(out[0]);\r\n  }\r\n  return out;\r\n}\r\n\r\n/**\r\n * Read the LinkedGeoPolygon structure into a nested array of MultiPolygon coordinates\r\n * @private\r\n * @param {number}  polygon           C pointer to LinkedGeoPolygon struct\r\n * @param {boolean} [formatAsGeoJson] Whether to provide GeoJSON output: [lng, lat], closed loops\r\n * @return {CoordPair[][][]}          MultiPolygon-style output.\r\n */\r\nfunction readMultiPolygon(polygon, formatAsGeoJson) {\r\n  var output = [];\r\n  var readCoord = formatAsGeoJson ? readLatLngGeoJson : readLatLng;\r\n  var loops;\r\n  var loop;\r\n  var coords;\r\n  var coord;\r\n  // Loop through the linked structure, building the output\r\n  while (polygon) {\r\n    output.push(loops = []);\r\n    // Follow ->first pointer\r\n    loop = libh3.getValue(polygon, 'i8*');\r\n    while (loop) {\r\n      loops.push(coords = []);\r\n      // Follow ->first pointer\r\n      coord = libh3.getValue(loop, 'i8*');\r\n      while (coord) {\r\n        coords.push(readCoord(coord));\r\n        // Follow ->next pointer\r\n        coord = libh3.getValue(coord + SZ_DBL * 2, 'i8*');\r\n      }\r\n      if (formatAsGeoJson) {\r\n        // Close loop if GeoJSON is requested\r\n        coords.push(coords[0]);\r\n      }\r\n      // Follow ->next pointer\r\n      loop = libh3.getValue(loop + SZ_PTR * 2, 'i8*');\r\n    }\r\n    // Follow ->next pointer\r\n    polygon = libh3.getValue(polygon + SZ_PTR * 2, 'i8*');\r\n  }\r\n  return output;\r\n}\r\n\r\n/**\r\n * Read a CoordIJ from C and return an {i, j} pair.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C struct\r\n * @return {CoordIJ}            {i, j} pair\r\n */\r\nfunction readCoordIJ(cAddress) {\r\n  return {\r\n    i: libh3.getValue(cAddress, 'i32'),\r\n    j: libh3.getValue(cAddress + SZ_INT, 'i32')\r\n  };\r\n}\r\n\r\n/**\r\n * Store an {i, j} pair to a C CoordIJ struct.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C memory\r\n * @param {CoordIJ} ij          {i,j} pair to store\r\n * @return {void}\r\n */\r\nfunction storeCoordIJ(cAddress, ref) {\r\n  var i = ref.i;\r\n  var j = ref.j;\r\n\r\n  libh3.setValue(cAddress, i, 'i32');\r\n  libh3.setValue(cAddress + SZ_INT, j, 'i32');\r\n}\r\n\r\n/**\r\n * Read an array of positive integers array from C. Negative\r\n * values are considered invalid and ignored in output.\r\n * @private\r\n * @param  {number} cAddress    Pointer to C array\r\n * @param  {number} count       Length of C array\r\n * @return {number[]}           Javascript integer array\r\n */\r\nfunction readArrayOfPositiveIntegers(cAddress, count) {\r\n  var out = [];\r\n  for (var i = 0; i < count; i++) {\r\n    var int = libh3.getValue(cAddress + SZ_INT * i, 'i32');\r\n    if (int >= 0) {\r\n      out.push(int);\r\n    }\r\n  }\r\n  return out;\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public API functions: Core\r\n\r\n/**\r\n * Whether a given string represents a valid H3 index\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to check\r\n * @return {boolean}          Whether the index is valid\r\n */\r\nfunction isValidCell(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return Boolean(H3.isValidCell(lower, upper));\r\n}\r\n\r\n/**\r\n * Whether the given H3 index is a pentagon\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to check\r\n * @return {boolean}          isPentagon\r\n */\r\nfunction isPentagon(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return Boolean(H3.isPentagon(lower, upper));\r\n}\r\n\r\n/**\r\n * Whether the given H3 index is in a Class III resolution (rotated versus\r\n * the icosahedron and subject to shape distortion adding extra points on\r\n * icosahedron edges, making them not true hexagons).\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to check\r\n * @return {boolean}          isResClassIII\r\n */\r\nfunction isResClassIII(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return Boolean(H3.isResClassIII(lower, upper));\r\n}\r\n\r\n/**\r\n * Get the number of the base cell for a given H3 index\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get the base cell for\r\n * @return {number}           Index of the base cell (0-121)\r\n */\r\nfunction getBaseCellNumber(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return H3.getBaseCellNumber(lower, upper);\r\n}\r\n\r\n/**\r\n * Get the indices of all icosahedron faces intersected by a given H3 index\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get faces for\r\n * @return {number[]}              Indices (0-19) of all intersected faces\r\n * @throws {H3Error}               If input is invalid\r\n */\r\nfunction getIcosahedronFaces(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var countPtr = libh3._malloc(SZ_INT);\r\n  try {\r\n    throwIfError(H3.maxFaceCount(lower, upper, countPtr));\r\n    var count = libh3.getValue(countPtr, 'i32');\r\n    var faces = libh3._malloc(SZ_INT * count);\r\n    try {\r\n      throwIfError(H3.getIcosahedronFaces(lower, upper, faces));\r\n      return readArrayOfPositiveIntegers(faces, count);\r\n    } finally {\r\n      libh3._free(faces);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Returns the resolution of an H3 index\r\n * @static\r\n * @param  {H3IndexInput} h3Index H3 index to get resolution\r\n * @return {number}          The number (0-15) resolution, or -1 if invalid\r\n */\r\nfunction getResolution(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  if (!H3.isValidCell(lower, upper)) {\r\n    // Compatability with stated API\r\n    return -1;\r\n  }\r\n  return H3.getResolution(lower, upper);\r\n}\r\n\r\n/**\r\n * Get the hexagon containing a lat,lon point\r\n * @static\r\n * @param  {number} lat Latitude of point\r\n * @param  {number} lng Longtitude of point\r\n * @param  {number} res Resolution of hexagons to return\r\n * @return {H3Index}    H3 index\r\n * @throws {H3Error}    If input is invalid\r\n */\r\nfunction latLngToCell(lat, lng, res) {\r\n  var latLng = libh3._malloc(SZ_LATLNG);\r\n  // Slightly more efficient way to set the memory\r\n  libh3.HEAPF64.set([lat, lng].map(degsToRads), latLng / SZ_DBL);\r\n  // Read value as a split long\r\n  var h3Index = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.latLngToCell(latLng, res, h3Index));\r\n    return validateH3Index(readH3IndexFromPointer(h3Index));\r\n  } finally {\r\n    libh3._free(h3Index);\r\n    libh3._free(latLng);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the lat,lon center of a given hexagon\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index\r\n * @return {CoordPair}             Point as a [lat, lng] pair\r\n * @throws {H3Error}               If input is invalid\r\n */\r\nfunction cellToLatLng(h3Index) {\r\n  var latLng = libh3._malloc(SZ_LATLNG);\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  try {\r\n    throwIfError(H3.cellToLatLng(lower, upper, latLng));\r\n    return readLatLng(latLng);\r\n  } finally {\r\n    libh3._free(latLng);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the vertices of a given hexagon (or pentagon), as an array of [lat, lng]\r\n * points. For pentagons and hexagons on the edge of an icosahedron face, this\r\n * function may return up to 10 vertices.\r\n * @static\r\n * @param  {H3IndexInput} h3Index          H3 index\r\n * @param {boolean} [formatAsGeoJson] Whether to provide GeoJSON output: [lng, lat], closed loops\r\n * @return {CoordPair[]}              Array of [lat, lng] pairs\r\n * @throws {H3Error}                  If input is invalid\r\n */\r\nfunction cellToBoundary(h3Index, formatAsGeoJson) {\r\n  var cellBoundary = libh3._malloc(SZ_CELLBOUNDARY);\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  try {\r\n    throwIfError(H3.cellToBoundary(lower, upper, cellBoundary));\r\n    return readCellBoundary(cellBoundary, formatAsGeoJson, formatAsGeoJson);\r\n  } finally {\r\n    libh3._free(cellBoundary);\r\n  }\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public API functions: Algorithms\r\n\r\n/**\r\n * Get the parent of the given hexagon at a particular resolution\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get parent for\r\n * @param  {number} res       Resolution of hexagon to return\r\n * @return {H3Index}          H3 index of parent, or null for invalid input\r\n * @throws {H3Error}          If input is invalid\r\n */\r\nfunction cellToParent(h3Index, res) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var parent = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellToParent(lower, upper, res, parent));\r\n    return validateH3Index(readH3IndexFromPointer(parent));\r\n  } finally {\r\n    libh3._free(parent);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the children/descendents of the given hexagon at a particular resolution\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get children for\r\n * @param  {number} res       Resolution of hexagons to return\r\n * @return {H3Index[]}        H3 indexes of children, or empty array for invalid input\r\n * @throws {H3Error}          If resolution is invalid or output is too large for JS\r\n */\r\nfunction cellToChildren(h3Index, res) {\r\n  // Bad input in this case can potentially result in high computation volume\r\n  // using the current C algorithm. Validate and return an empty array on failure.\r\n  if (!isValidCell(h3Index)) {\r\n    return [];\r\n  }\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var count = validateArrayLength(cellToChildrenSize(h3Index, res));\r\n  var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellToChildren(lower, upper, res, hexagons));\r\n    return readArrayOfH3Indexes(hexagons, count);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the number of children for a cell at a given resolution\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get child count for\r\n * @param  {number} res            Child resolution\r\n * @return {number}                Number of children at res for the given cell\r\n * @throws {H3Error}               If cell or parentRes are invalid\r\n */\r\nfunction cellToChildrenSize(h3Index, res) {\r\n  if (!isValidCell(h3Index)) {\r\n    throw H3LibraryError(E_CELL_INVALID);\r\n  }\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.cellToChildrenSize(lower, upper, res, countPtr));\r\n    return readInt64AsDoubleFromPointer(countPtr);\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the center child of the given hexagon at a particular resolution\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get center child for\r\n * @param  {number} res       Resolution of cell to return\r\n * @return {H3Index}          H3 index of child, or null for invalid input\r\n * @throws {H3Error}          If resolution is invalid\r\n */\r\nfunction cellToCenterChild(h3Index, res) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var centerChild = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellToCenterChild(lower, upper, res, centerChild));\r\n    return validateH3Index(readH3IndexFromPointer(centerChild));\r\n  } finally {\r\n    libh3._free(centerChild);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the position of the cell within an ordered list of all children of the\r\n * cell's parent at the specified resolution.\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index to get child pos for\r\n * @param  {number} parentRes      Resolution of reference parent\r\n * @return {number}                Position of child within parent at parentRes\r\n * @throws {H3Error}               If cell or parentRes are invalid\r\n */\r\nfunction cellToChildPos(h3Index, parentRes) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var childPos = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.cellToChildPos(lower, upper, parentRes, childPos));\r\n    return readInt64AsDoubleFromPointer(childPos);\r\n  } finally {\r\n    libh3._free(childPos);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the child cell at a given position within an ordered list of all children\r\n * at the specified resolution\r\n * @static\r\n * @param  {number} childPos       Position of the child cell to get\r\n * @param  {H3IndexInput} h3Index  H3 index of the parent cell\r\n * @param  {number} childRes       Resolution of child cell to return\r\n * @return {H3Index}          H3 index of child\r\n * @throws {H3Error}          If input is invalid\r\n */\r\nfunction childPosToCell(childPos, h3Index, childRes) {\r\n  var ref = numberToSplitLong(childPos);\r\n  var cpLower = ref[0];\r\n  var cpUpper = ref[1];\r\n  var ref$1 = h3IndexToSplitLong(h3Index);\r\n  var lower = ref$1[0];\r\n  var upper = ref$1[1];\r\n  var child = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.childPosToCell(cpLower, cpUpper, lower, upper, childRes, child));\r\n    return validateH3Index(readH3IndexFromPointer(child));\r\n  } finally {\r\n    libh3._free(child);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons in a k-ring around a given center. The order of the hexagons is undefined.\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index of center hexagon\r\n * @param  {number} ringSize  Radius of k-ring\r\n * @return {H3Index[]}        H3 indexes for all hexagons in ring\r\n * @throws {H3Error}          If input is invalid or output is too large for JS\r\n */\r\nfunction gridDisk(h3Index, ringSize) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.maxGridDiskSize(ringSize, countPtr));\r\n    var count = validateArrayLength(readInt64AsDoubleFromPointer(countPtr));\r\n    var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n    try {\r\n      throwIfError(H3.gridDisk(lower, upper, ringSize, hexagons));\r\n      return readArrayOfH3Indexes(hexagons, count);\r\n    } finally {\r\n      libh3._free(hexagons);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons in a k-ring around a given center, in an array of arrays\r\n * ordered by distance from the origin. The order of the hexagons within each ring is undefined.\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index of center hexagon\r\n * @param  {number} ringSize  Radius of k-ring\r\n * @return {H3Index[][]}      Array of arrays with H3 indexes for all hexagons each ring\r\n * @throws {H3Error}          If input is invalid or output is too large for JS\r\n */\r\nfunction gridDiskDistances(h3Index, ringSize) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.maxGridDiskSize(ringSize, countPtr));\r\n    var count = validateArrayLength(readInt64AsDoubleFromPointer(countPtr));\r\n    var kRings = libh3._calloc(count, SZ_H3INDEX);\r\n    var distances = libh3._calloc(count, SZ_INT);\r\n    try {\r\n      throwIfError(H3.gridDiskDistances(lower, upper, ringSize, kRings, distances));\r\n      /**\r\n       * An array of empty arrays to hold the output\r\n       * @type {string[][]}\r\n       * @private\r\n       */\r\n      var out = [];\r\n      for (var i = 0; i < ringSize + 1; i++) {\r\n        out.push([]);\r\n      }\r\n      // Read the array of hexagons, putting them into the appropriate rings\r\n      for (var i$1 = 0; i$1 < count; i$1++) {\r\n        var cell = readH3IndexFromPointer(kRings, i$1);\r\n        var index = libh3.getValue(distances + SZ_INT * i$1, 'i32');\r\n        // eslint-disable-next-line max-depth\r\n        if (cell !== null) {\r\n          out[index].push(cell);\r\n        }\r\n      }\r\n      return out;\r\n    } finally {\r\n      libh3._free(kRings);\r\n      libh3._free(distances);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons in a hollow hexagonal ring centered at origin with sides of a given length.\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index of center hexagon\r\n * @param  {number} ringSize  Radius of ring\r\n * @return {H3Index[]}        H3 indexes for all hexagons in ring\r\n * @throws {Error}            If the algorithm could not calculate the ring\r\n * @throws {H3Error}          If input is invalid\r\n */\r\nfunction gridRing(h3Index, ringSize) {\r\n  var maxCount = ringSize === 0 ? 1 : 6 * ringSize;\r\n  var hexagons = libh3._calloc(maxCount, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.gridRing.apply(H3, h3IndexToSplitLong(h3Index).concat( [ringSize], [hexagons] )));\r\n    return readArrayOfH3Indexes(hexagons, maxCount);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons in a hollow hexagonal ring centered at origin with sides of a given length.\r\n * Unlike gridDisk, this function will throw an error if there is a pentagon anywhere in the ring.\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index of center hexagon\r\n * @param  {number} ringSize  Radius of ring\r\n * @return {H3Index[]}        H3 indexes for all hexagons in ring\r\n * @throws {Error}            If the algorithm could not calculate the ring\r\n * @throws {H3Error}          If input is invalid\r\n */\r\nfunction gridRingUnsafe(h3Index, ringSize) {\r\n  var maxCount = ringSize === 0 ? 1 : 6 * ringSize;\r\n  var hexagons = libh3._calloc(maxCount, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.gridRingUnsafe.apply(H3, h3IndexToSplitLong(h3Index).concat( [ringSize], [hexagons] )));\r\n    return readArrayOfH3Indexes(hexagons, maxCount);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons with centers contained in a given polygon. The polygon\r\n * is specified with GeoJson semantics as an array of loops. Each loop is\r\n * an array of [lat, lng] pairs (or [lng, lat] if isGeoJson is specified).\r\n * The first loop is the perimeter of the polygon, and subsequent loops are\r\n * expected to be holes.\r\n * @static\r\n * @param  {number[][] | number[][][]} coordinates\r\n *                                  Array of loops, or a single loop\r\n * @param  {number} res             Resolution of hexagons to return\r\n * @param  {boolean} [isGeoJson]    Whether to expect GeoJson-style [lng, lat]\r\n *                                  pairs instead of [lat, lng]\r\n * @return {H3Index[]}              H3 indexes for all hexagons in polygon\r\n * @throws {H3Error}                If input is invalid or output is too large for JS\r\n */\r\nfunction polygonToCells(coordinates, res, isGeoJson) {\r\n  validateRes(res);\r\n  isGeoJson = Boolean(isGeoJson);\r\n  // Guard against empty input\r\n  if (coordinates.length === 0 || coordinates[0].length === 0) {\r\n    return [];\r\n  }\r\n  // Wrap to expected format if a single loop is provided\r\n  var polygon = typeof coordinates[0][0] === 'number' ? [coordinates] : coordinates;\r\n  var geoPolygon = coordinatesToGeoPolygon(\r\n  // @ts-expect-error - There's no way to convince TS that polygon is now number[][][]\r\n  polygon, isGeoJson);\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.maxPolygonToCellsSize(geoPolygon, res, 0, countPtr));\r\n    var count = validateArrayLength(readInt64AsDoubleFromPointer(countPtr));\r\n    var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n    try {\r\n      throwIfError(H3.polygonToCells(geoPolygon, res, 0, hexagons));\r\n      return readArrayOfH3Indexes(hexagons, count);\r\n    } finally {\r\n      libh3._free(hexagons);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n    destroyGeoPolygon(geoPolygon);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all hexagons with centers contained in a given polygon. The polygon\r\n * is specified with GeoJson semantics as an array of loops. Each loop is\r\n * an array of [lat, lng] pairs (or [lng, lat] if isGeoJson is specified).\r\n * The first loop is the perimeter of the polygon, and subsequent loops are\r\n * expected to be holes.\r\n * @static\r\n * @param  {number[][] | number[][][]} coordinates\r\n *                                  Array of loops, or a single loop\r\n * @param  {number} res             Resolution of hexagons to return\r\n * @param  {string} flags           Value from POLYGON_TO_CELLS_FLAGS\r\n * @param  {boolean} [isGeoJson]    Whether to expect GeoJson-style [lng, lat]\r\n *                                  pairs instead of [lat, lng]\r\n * @return {H3Index[]}              H3 indexes for all hexagons in polygon\r\n * @throws {H3Error}                If input is invalid or output is too large for JS\r\n */\r\nfunction polygonToCellsExperimental(coordinates, res, flags, isGeoJson) {\r\n  validateRes(res);\r\n  isGeoJson = Boolean(isGeoJson);\r\n  var flagsInt = polygonToCellsFlagsToNumber(flags);\r\n  // Guard against empty input\r\n  if (coordinates.length === 0 || coordinates[0].length === 0) {\r\n    return [];\r\n  }\r\n  // Wrap to expected format if a single loop is provided\r\n  var polygon = typeof coordinates[0][0] === 'number' ? [coordinates] : coordinates;\r\n  var geoPolygon = coordinatesToGeoPolygon(\r\n  // @ts-expect-error - There's no way to convince TS that polygon is now number[][][]\r\n  polygon, isGeoJson);\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.maxPolygonToCellsSizeExperimental(geoPolygon, res, flagsInt, countPtr));\r\n    var count = validateArrayLength(readInt64AsDoubleFromPointer(countPtr));\r\n    var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n    try {\r\n      throwIfError(H3.polygonToCellsExperimental(geoPolygon, res, flagsInt, count, UNUSED_UPPER_32_BITS, hexagons));\r\n      return readArrayOfH3Indexes(hexagons, count);\r\n    } finally {\r\n      libh3._free(hexagons);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n    destroyGeoPolygon(geoPolygon);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the outlines of a set of H3 hexagons, returned in GeoJSON MultiPolygon\r\n * format (an array of polygons, each with an array of loops, each an array of\r\n * coordinates). Coordinates are returned as [lat, lng] pairs unless GeoJSON\r\n * is requested.\r\n *\r\n * It is the responsibility of the caller to ensure that all hexagons in the\r\n * set have the same resolution and that the set contains no duplicates. Behavior\r\n * is undefined if duplicates or multiple resolutions are present, and the\r\n * algorithm may produce unexpected or invalid polygons.\r\n *\r\n * @static\r\n * @param {H3IndexInput[]} h3Indexes  H3 indexes to get outlines for\r\n * @param {boolean} [formatAsGeoJson] Whether to provide GeoJSON output: [lng, lat], closed loops\r\n * @return {CoordPair[][][]}          MultiPolygon-style output.\r\n * @throws {H3Error}                  If input is invalid\r\n */\r\nfunction cellsToMultiPolygon(h3Indexes, formatAsGeoJson) {\r\n  // Early exit on empty input\r\n  if (!h3Indexes || !h3Indexes.length) {\r\n    return [];\r\n  }\r\n  // Set up input set\r\n  var indexCount = h3Indexes.length;\r\n  var set = libh3._calloc(indexCount, SZ_H3INDEX);\r\n  storeArrayOfH3Indexes(set, h3Indexes);\r\n  // Allocate memory for output linked polygon\r\n  var polygon = libh3._calloc(SZ_LINKED_GEOPOLYGON);\r\n  try {\r\n    throwIfError(H3.cellsToLinkedMultiPolygon(set, indexCount, polygon));\r\n    return readMultiPolygon(polygon, formatAsGeoJson);\r\n  } finally {\r\n    // Clean up\r\n    H3.destroyLinkedMultiPolygon(polygon);\r\n    libh3._free(polygon);\r\n    libh3._free(set);\r\n  }\r\n}\r\n\r\n/**\r\n * Compact a set of hexagons of the same resolution into a set of hexagons across\r\n * multiple levels that represents the same area.\r\n * @static\r\n * @param  {H3IndexInput[]} h3Set H3 indexes to compact\r\n * @return {H3Index[]}       Compacted H3 indexes\r\n * @throws {H3Error}         If the input is invalid (e.g. duplicate hexagons)\r\n */\r\nfunction compactCells(h3Set) {\r\n  if (!h3Set || !h3Set.length) {\r\n    return [];\r\n  }\r\n  // Set up input set\r\n  var count = h3Set.length;\r\n  var set = libh3._calloc(count, SZ_H3INDEX);\r\n  storeArrayOfH3Indexes(set, h3Set);\r\n  // Allocate memory for compacted hexagons, worst-case is no compaction\r\n  var compactedSet = libh3._calloc(count, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.compactCells(set, compactedSet, count, UNUSED_UPPER_32_BITS));\r\n    return readArrayOfH3Indexes(compactedSet, count);\r\n  } finally {\r\n    libh3._free(set);\r\n    libh3._free(compactedSet);\r\n  }\r\n}\r\n\r\n/**\r\n * Uncompact a compacted set of hexagons to hexagons of the same resolution\r\n * @static\r\n * @param  {H3IndexInput[]} compactedSet H3 indexes to uncompact\r\n * @param  {number}    res          The resolution to uncompact to\r\n * @return {H3Index[]}              The uncompacted H3 indexes\r\n * @throws {H3Error}                If the input is invalid (e.g. invalid resolution)\r\n */\r\nfunction uncompactCells(compactedSet, res) {\r\n  validateRes(res);\r\n  if (!compactedSet || !compactedSet.length) {\r\n    return [];\r\n  }\r\n  // Set up input set\r\n  var count = compactedSet.length;\r\n  var set = libh3._calloc(count, SZ_H3INDEX);\r\n  storeArrayOfH3Indexes(set, compactedSet);\r\n  // Estimate how many hexagons we need (always overestimates if in error)\r\n  var uncompactCellSizePtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.uncompactCellsSize(set, count, UNUSED_UPPER_32_BITS, res, uncompactCellSizePtr));\r\n    var uncompactCellSize = validateArrayLength(readInt64AsDoubleFromPointer(uncompactCellSizePtr));\r\n    // Allocate memory for uncompacted hexagons\r\n    var uncompactedSet = libh3._calloc(uncompactCellSize, SZ_H3INDEX);\r\n    try {\r\n      throwIfError(H3.uncompactCells(set, count, UNUSED_UPPER_32_BITS, uncompactedSet, uncompactCellSize, UNUSED_UPPER_32_BITS, res));\r\n      return readArrayOfH3Indexes(uncompactedSet, uncompactCellSize);\r\n    } finally {\r\n      libh3._free(set);\r\n      libh3._free(uncompactedSet);\r\n    }\r\n  } finally {\r\n    libh3._free(uncompactCellSizePtr);\r\n  }\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public API functions: Directed edges\r\n\r\n/**\r\n * Whether two H3 indexes are neighbors (share an edge)\r\n * @static\r\n * @param  {H3IndexInput} origin      Origin hexagon index\r\n * @param  {H3IndexInput} destination Destination hexagon index\r\n * @return {boolean}             Whether the hexagons share an edge\r\n * @throws {H3Error}             If the input is invalid\r\n */\r\nfunction areNeighborCells(origin, destination) {\r\n  var ref = h3IndexToSplitLong(origin);\r\n  var oLower = ref[0];\r\n  var oUpper = ref[1];\r\n  var ref$1 = h3IndexToSplitLong(destination);\r\n  var dLower = ref$1[0];\r\n  var dUpper = ref$1[1];\r\n  var out = libh3._malloc(SZ_INT);\r\n  try {\r\n    throwIfError(H3.areNeighborCells(oLower, oUpper, dLower, dUpper, out));\r\n    return readBooleanFromPointer(out);\r\n  } finally {\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n/**\r\n * Get an H3 index representing a unidirectional edge for a given origin and destination\r\n * @static\r\n * @param  {H3IndexInput} origin      Origin hexagon index\r\n * @param  {H3IndexInput} destination Destination hexagon index\r\n * @return {H3Index}             H3 index of the edge, or null if no edge is shared\r\n * @throws {H3Error}             If the input is invalid\r\n */\r\nfunction cellsToDirectedEdge(origin, destination) {\r\n  var ref = h3IndexToSplitLong(origin);\r\n  var oLower = ref[0];\r\n  var oUpper = ref[1];\r\n  var ref$1 = h3IndexToSplitLong(destination);\r\n  var dLower = ref$1[0];\r\n  var dUpper = ref$1[1];\r\n  var h3Index = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellsToDirectedEdge(oLower, oUpper, dLower, dUpper, h3Index));\r\n    return validateH3Index(readH3IndexFromPointer(h3Index));\r\n  } finally {\r\n    libh3._free(h3Index);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the origin hexagon from an H3 index representing a unidirectional edge\r\n * @static\r\n * @param  {H3IndexInput} edgeIndex H3 index of the edge\r\n * @return {H3Index}           H3 index of the edge origin\r\n * @throws {H3Error}           If the input is invalid\r\n */\r\nfunction getDirectedEdgeOrigin(edgeIndex) {\r\n  var ref = h3IndexToSplitLong(edgeIndex);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var h3Index = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.getDirectedEdgeOrigin(lower, upper, h3Index));\r\n    return validateH3Index(readH3IndexFromPointer(h3Index));\r\n  } finally {\r\n    libh3._free(h3Index);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the destination hexagon from an H3 index representing a unidirectional edge\r\n * @static\r\n * @param  {H3IndexInput} edgeIndex H3 index of the edge\r\n * @return {H3Index}           H3 index of the edge destination\r\n * @throws {H3Error}           If the input is invalid\r\n */\r\nfunction getDirectedEdgeDestination(edgeIndex) {\r\n  var ref = h3IndexToSplitLong(edgeIndex);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var h3Index = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.getDirectedEdgeDestination(lower, upper, h3Index));\r\n    return validateH3Index(readH3IndexFromPointer(h3Index));\r\n  } finally {\r\n    libh3._free(h3Index);\r\n  }\r\n}\r\n\r\n/**\r\n * Whether the input is a valid unidirectional edge\r\n * @static\r\n * @param  {H3IndexInput} edgeIndex H3 index of the edge\r\n * @return {boolean}           Whether the index is valid\r\n */\r\nfunction isValidDirectedEdge(edgeIndex) {\r\n  var ref = h3IndexToSplitLong(edgeIndex);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return Boolean(H3.isValidDirectedEdge(lower, upper));\r\n}\r\n\r\n/**\r\n * Get the [origin, destination] pair represented by a unidirectional edge\r\n * @static\r\n * @param  {H3IndexInput} edgeIndex H3 index of the edge\r\n * @return {H3Index[]}         [origin, destination] pair as H3 indexes\r\n * @throws {H3Error}           If the input is invalid\r\n */\r\nfunction directedEdgeToCells(edgeIndex) {\r\n  var ref = h3IndexToSplitLong(edgeIndex);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var count = 2;\r\n  var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.directedEdgeToCells(lower, upper, hexagons));\r\n    return readArrayOfH3Indexes(hexagons, count);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all of the unidirectional edges with the given H3 index as the origin (i.e. an edge to\r\n * every neighbor)\r\n * @static\r\n * @param  {H3IndexInput} h3Index   H3 index of the origin hexagon\r\n * @return {H3Index[]}         List of unidirectional edges\r\n * @throws {H3Error}           If the input is invalid\r\n */\r\nfunction originToDirectedEdges(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var count = 6;\r\n  var edges = libh3._calloc(count, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.originToDirectedEdges(lower, upper, edges));\r\n    return readArrayOfH3Indexes(edges, count);\r\n  } finally {\r\n    libh3._free(edges);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the vertices of a given edge as an array of [lat, lng] points. Note that for edges that\r\n * cross the edge of an icosahedron face, this may return 3 coordinates.\r\n * @static\r\n * @param  {H3IndexInput} edgeIndex        H3 index of the edge\r\n * @param {boolean} [formatAsGeoJson] Whether to provide GeoJSON output: [lng, lat]\r\n * @return {CoordPair[]}              Array of geo coordinate pairs\r\n * @throws {H3Error}                  If the input is invalid\r\n */\r\nfunction directedEdgeToBoundary(edgeIndex, formatAsGeoJson) {\r\n  var cellBoundary = libh3._malloc(SZ_CELLBOUNDARY);\r\n  var ref = h3IndexToSplitLong(edgeIndex);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  try {\r\n    throwIfError(H3.directedEdgeToBoundary(lower, upper, cellBoundary));\r\n    return readCellBoundary(cellBoundary, formatAsGeoJson);\r\n  } finally {\r\n    libh3._free(cellBoundary);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the grid distance between two hex indexes. This function may fail\r\n * to find the distance between two indexes if they are very far apart or\r\n * on opposite sides of a pentagon.\r\n * @static\r\n * @param  {H3IndexInput} origin      Origin hexagon index\r\n * @param  {H3IndexInput} destination Destination hexagon index\r\n * @return {number}          Distance between hexagons\r\n * @throws {H3Error}         If input is invalid or the distance could not be calculated\r\n */\r\nfunction gridDistance(origin, destination) {\r\n  var ref = h3IndexToSplitLong(origin);\r\n  var oLower = ref[0];\r\n  var oUpper = ref[1];\r\n  var ref$1 = h3IndexToSplitLong(destination);\r\n  var dLower = ref$1[0];\r\n  var dUpper = ref$1[1];\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.gridDistance(oLower, oUpper, dLower, dUpper, countPtr));\r\n    return readInt64AsDoubleFromPointer(countPtr);\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Given two H3 indexes, return the line of indexes between them (inclusive).\r\n *\r\n * This function may fail to find the line between two indexes, for\r\n * example if they are very far apart. It may also fail when finding\r\n * distances for indexes on opposite sides of a pentagon.\r\n *\r\n * Notes:\r\n *\r\n *  - The specific output of this function should not be considered stable\r\n *    across library versions. The only guarantees the library provides are\r\n *    that the line length will be `h3Distance(start, end) + 1` and that\r\n *    every index in the line will be a neighbor of the preceding index.\r\n *  - Lines are drawn in grid space, and may not correspond exactly to either\r\n *    Cartesian lines or great arcs.\r\n *\r\n * @static\r\n * @param  {H3IndexInput} origin      Origin hexagon index\r\n * @param  {H3IndexInput} destination Destination hexagon index\r\n * @return {H3Index[]}           H3 indexes connecting origin and destination\r\n * @throws {H3Error}             If input is invalid or the line cannot be calculated\r\n */\r\nfunction gridPathCells(origin, destination) {\r\n  var ref = h3IndexToSplitLong(origin);\r\n  var oLower = ref[0];\r\n  var oUpper = ref[1];\r\n  var ref$1 = h3IndexToSplitLong(destination);\r\n  var dLower = ref$1[0];\r\n  var dUpper = ref$1[1];\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    throwIfError(H3.gridPathCellsSize(oLower, oUpper, dLower, dUpper, countPtr));\r\n    var count = validateArrayLength(readInt64AsDoubleFromPointer(countPtr));\r\n    var hexagons = libh3._calloc(count, SZ_H3INDEX);\r\n    try {\r\n      H3.gridPathCells(oLower, oUpper, dLower, dUpper, hexagons);\r\n      return readArrayOfH3Indexes(hexagons, count);\r\n    } finally {\r\n      libh3._free(hexagons);\r\n    }\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\nvar LOCAL_IJ_DEFAULT_MODE = 0;\r\n\r\n/**\r\n * Produces IJ coordinates for an H3 index anchored by an origin.\r\n *\r\n * - The coordinate space used by this function may have deleted\r\n * regions or warping due to pentagonal distortion.\r\n * - Coordinates are only comparable if they come from the same\r\n * origin index.\r\n * - Failure may occur if the index is too far away from the origin\r\n * or if the index is on the other side of a pentagon.\r\n * - This function is experimental, and its output is not guaranteed\r\n * to be compatible across different versions of H3.\r\n * @static\r\n * @param  {H3IndexInput} origin      Origin H3 index\r\n * @param  {H3IndexInput} destination H3 index for which to find relative coordinates\r\n * @return {CoordIJ}             Coordinates as an `{i, j}` pair\r\n * @throws {H3Error}             If the IJ coordinates cannot be calculated\r\n */\r\nfunction cellToLocalIj(origin, destination) {\r\n  var ij = libh3._malloc(SZ_COORDIJ);\r\n  try {\r\n    throwIfError(H3.cellToLocalIj.apply(H3, h3IndexToSplitLong(origin).concat( h3IndexToSplitLong(destination), [LOCAL_IJ_DEFAULT_MODE], [ij] )));\r\n    return readCoordIJ(ij);\r\n  } finally {\r\n    libh3._free(ij);\r\n  }\r\n}\r\n\r\n/**\r\n * Produces an H3 index for IJ coordinates anchored by an origin.\r\n *\r\n * - The coordinate space used by this function may have deleted\r\n * regions or warping due to pentagonal distortion.\r\n * - Coordinates are only comparable if they come from the same\r\n * origin index.\r\n * - Failure may occur if the index is too far away from the origin\r\n * or if the index is on the other side of a pentagon.\r\n * - This function is experimental, and its output is not guaranteed\r\n * to be compatible across different versions of H3.\r\n * @static\r\n * @param  {H3IndexInput} origin     Origin H3 index\r\n * @param  {CoordIJ} coords     Coordinates as an `{i, j}` pair\r\n * @return {H3Index}            H3 index at the relative coordinates\r\n * @throws {H3Error}            If the H3 index cannot be calculated\r\n */\r\nfunction localIjToCell(origin, coords) {\r\n  // Validate input coords\r\n  if (!coords || typeof coords.i !== 'number' || typeof coords.j !== 'number') {\r\n    throw new Error('Coordinates must be provided as an {i, j} object');\r\n  }\r\n  // Allocate memory for the CoordIJ struct and an H3 index to hold the return value\r\n  var ij = libh3._malloc(SZ_COORDIJ);\r\n  var out = libh3._malloc(SZ_H3INDEX);\r\n  storeCoordIJ(ij, coords);\r\n  try {\r\n    throwIfError(H3.localIjToCell.apply(H3, h3IndexToSplitLong(origin).concat( [ij], [LOCAL_IJ_DEFAULT_MODE], [out] )));\r\n    return validateH3Index(readH3IndexFromPointer(out));\r\n  } finally {\r\n    libh3._free(ij);\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public API functions: Distance/area utilities\r\n\r\n/**\r\n * Great circle distance between two geo points. This is not specific to H3,\r\n * but is implemented in the library and provided here as a convenience.\r\n * @static\r\n * @param  {number[]} latLng1 Origin coordinate as [lat, lng]\r\n * @param  {number[]} latLng2 Destination coordinate as [lat, lng]\r\n * @param  {string}   unit    Distance unit (either UNITS.m, UNITS.km, or UNITS.rads)\r\n * @return {number}           Great circle distance\r\n * @throws {H3Error}          If the unit is invalid\r\n */\r\nfunction greatCircleDistance(latLng1, latLng2, unit) {\r\n  var coord1 = storeLatLng(latLng1[0], latLng1[1]);\r\n  var coord2 = storeLatLng(latLng2[0], latLng2[1]);\r\n  var result;\r\n  switch (unit) {\r\n    case UNITS.m:\r\n      result = H3.greatCircleDistanceM(coord1, coord2);\r\n      break;\r\n    case UNITS.km:\r\n      result = H3.greatCircleDistanceKm(coord1, coord2);\r\n      break;\r\n    case UNITS.rads:\r\n      result = H3.greatCircleDistanceRads(coord1, coord2);\r\n      break;\r\n    default:\r\n      result = null;\r\n  }\r\n  libh3._free(coord1);\r\n  libh3._free(coord2);\r\n  if (result === null) {\r\n    throw JSBindingError(E_UNKNOWN_UNIT, unit);\r\n  }\r\n  return result;\r\n}\r\n\r\n/**\r\n * Exact area of a given cell\r\n * @static\r\n * @param  {H3IndexInput} h3Index  H3 index of the hexagon to measure\r\n * @param  {string}  unit     Distance unit (either UNITS.m2, UNITS.km2, or UNITS.rads2)\r\n * @return {number}           Cell area\r\n * @throws {H3Error}          If the input is invalid\r\n */\r\nfunction cellArea(h3Index, unit) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var out = libh3._malloc(SZ_DBL);\r\n  try {\r\n    switch (unit) {\r\n      case UNITS.m2:\r\n        throwIfError(H3.cellAreaM2(lower, upper, out));\r\n        break;\r\n      case UNITS.km2:\r\n        throwIfError(H3.cellAreaKm2(lower, upper, out));\r\n        break;\r\n      case UNITS.rads2:\r\n        throwIfError(H3.cellAreaRads2(lower, upper, out));\r\n        break;\r\n      default:\r\n        throw JSBindingError(E_UNKNOWN_UNIT, unit);\r\n    }\r\n    return readDoubleFromPointer(out);\r\n  } finally {\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n/**\r\n * Calculate length of a given unidirectional edge\r\n * @static\r\n * @param  {H3IndexInput} edge     H3 index of the edge to measure\r\n * @param  {string}  unit     Distance unit (either UNITS.m, UNITS.km, or UNITS.rads)\r\n * @return {number}           Cell area\r\n * @throws {H3Error}          If the input is invalid\r\n */\r\nfunction edgeLength(edge, unit) {\r\n  var ref = h3IndexToSplitLong(edge);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var out = libh3._malloc(SZ_DBL);\r\n  try {\r\n    switch (unit) {\r\n      case UNITS.m:\r\n        throwIfError(H3.edgeLengthM(lower, upper, out));\r\n        break;\r\n      case UNITS.km:\r\n        throwIfError(H3.edgeLengthKm(lower, upper, out));\r\n        break;\r\n      case UNITS.rads:\r\n        throwIfError(H3.edgeLengthRads(lower, upper, out));\r\n        break;\r\n      default:\r\n        throw JSBindingError(E_UNKNOWN_UNIT, unit);\r\n    }\r\n    return readDoubleFromPointer(out);\r\n  } finally {\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n/**\r\n * Average hexagon area at a given resolution\r\n * @static\r\n * @param  {number} res  Hexagon resolution\r\n * @param  {string} unit Area unit (either UNITS.m2, UNITS.km2, or UNITS.rads2)\r\n * @return {number}      Average area\r\n * @throws {H3Error}     If the input is invalid\r\n */\r\nfunction getHexagonAreaAvg(res, unit) {\r\n  validateRes(res);\r\n  var out = libh3._malloc(SZ_DBL);\r\n  try {\r\n    switch (unit) {\r\n      case UNITS.m2:\r\n        throwIfError(H3.getHexagonAreaAvgM2(res, out));\r\n        break;\r\n      case UNITS.km2:\r\n        throwIfError(H3.getHexagonAreaAvgKm2(res, out));\r\n        break;\r\n      default:\r\n        throw JSBindingError(E_UNKNOWN_UNIT, unit);\r\n    }\r\n    return readDoubleFromPointer(out);\r\n  } finally {\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n/**\r\n * Average hexagon edge length at a given resolution\r\n * @static\r\n * @param  {number} res  Hexagon resolution\r\n * @param  {string} unit Distance unit (either UNITS.m, UNITS.km, or UNITS.rads)\r\n * @return {number}      Average edge length\r\n * @throws {H3Error}     If the input is invalid\r\n */\r\nfunction getHexagonEdgeLengthAvg(res, unit) {\r\n  validateRes(res);\r\n  var out = libh3._malloc(SZ_DBL);\r\n  try {\r\n    switch (unit) {\r\n      case UNITS.m:\r\n        throwIfError(H3.getHexagonEdgeLengthAvgM(res, out));\r\n        break;\r\n      case UNITS.km:\r\n        throwIfError(H3.getHexagonEdgeLengthAvgKm(res, out));\r\n        break;\r\n      default:\r\n        throw JSBindingError(E_UNKNOWN_UNIT, unit);\r\n    }\r\n    return readDoubleFromPointer(out);\r\n  } finally {\r\n    libh3._free(out);\r\n  }\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public API functions: Vertex mode\r\n\r\n/**\r\n * Find the index for a vertex of a cell.\r\n * @static\r\n * @param {H3IndexInput} h3Index     Cell to find the vertex for\r\n * @param {number} vertexNum         Number (index) of the vertex to calculate\r\n * @return {H3Index}     Vertex index\r\n * @throws {H3Error}     If the input is invalid\r\n */\r\nfunction cellToVertex(h3Index, vertexNum) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var vertexIndex = libh3._malloc(SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellToVertex(lower, upper, vertexNum, vertexIndex));\r\n    return validateH3Index(readH3IndexFromPointer(vertexIndex));\r\n  } finally {\r\n    libh3._free(vertexIndex);\r\n  }\r\n}\r\n\r\n/**\r\n * Find the indexes for all vertexes of a cell.\r\n * @static\r\n * @param {H3IndexInput} h3Index     Cell to find all vertexes for\r\n * @return {H3Index[]}   All vertex indexes of this cell\r\n * @throws {H3Error}     If the input is invalid\r\n */\r\nfunction cellToVertexes(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  var maxNumVertexes = 6;\r\n  var vertexIndexes = libh3._calloc(maxNumVertexes, SZ_H3INDEX);\r\n  try {\r\n    throwIfError(H3.cellToVertexes(lower, upper, vertexIndexes));\r\n    return readArrayOfH3Indexes(vertexIndexes, maxNumVertexes);\r\n  } finally {\r\n    libh3._free(vertexIndexes);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the lat, lng of a given vertex\r\n * @static\r\n * @param {H3IndexInput} h3Index A vertex index\r\n * @returns {CoordPair}          Latitude, longitude coordinates of the vertex\r\n * @throws {H3Error}             If the input is invalid\r\n */\r\nfunction vertexToLatLng(h3Index) {\r\n  var latlng = libh3._malloc(SZ_LATLNG);\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  try {\r\n    throwIfError(H3.vertexToLatLng(lower, upper, latlng));\r\n    return readLatLng(latlng);\r\n  } finally {\r\n    libh3._free(latlng);\r\n  }\r\n}\r\n\r\n/**\r\n * Returns true if the input is a valid vertex index.\r\n * @static\r\n * @param {H3IndexInput} h3Index An index to test for being a vertex index\r\n * @returns {boolean} True if the index represents a vertex\r\n */\r\nfunction isValidVertex(h3Index) {\r\n  var ref = h3IndexToSplitLong(h3Index);\r\n  var lower = ref[0];\r\n  var upper = ref[1];\r\n  return Boolean(H3.isValidVertex(lower, upper));\r\n}\r\n\r\n// ----------------------------------------------------------------------------\r\n// Public informational utilities\r\n\r\n/**\r\n * The total count of hexagons in the world at a given resolution. Note that above\r\n * resolution 8 the exact count cannot be represented in a JavaScript 32-bit number,\r\n * so consumers should use caution when applying further operations to the output.\r\n * @static\r\n * @param  {number} res  Hexagon resolution\r\n * @return {number}      Count\r\n * @throws {H3Error}     If the resolution is invalid\r\n */\r\nfunction getNumCells(res) {\r\n  validateRes(res);\r\n  var countPtr = libh3._malloc(SZ_INT64);\r\n  try {\r\n    // Get number as a long value\r\n    throwIfError(H3.getNumCells(res, countPtr));\r\n    return readInt64AsDoubleFromPointer(countPtr);\r\n  } finally {\r\n    libh3._free(countPtr);\r\n  }\r\n}\r\n\r\n/**\r\n * Get all H3 indexes at resolution 0. As every index at every resolution > 0 is\r\n * the descendant of a res 0 index, this can be used with h3ToChildren to iterate\r\n * over H3 indexes at any resolution.\r\n * @static\r\n * @return {H3Index[]}  All H3 indexes at res 0\r\n */\r\nfunction getRes0Cells() {\r\n  var count = H3.res0CellCount();\r\n  var hexagons = libh3._malloc(SZ_H3INDEX * count);\r\n  try {\r\n    throwIfError(H3.getRes0Cells(hexagons));\r\n    return readArrayOfH3Indexes(hexagons, count);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Get the twelve pentagon indexes at a given resolution.\r\n * @static\r\n * @param  {number} res  Hexagon resolution\r\n * @return {H3Index[]}   All H3 pentagon indexes at res\r\n * @throws {H3Error}     If the resolution is invalid\r\n */\r\nfunction getPentagons(res) {\r\n  validateRes(res);\r\n  var count = H3.pentagonCount();\r\n  var hexagons = libh3._malloc(SZ_H3INDEX * count);\r\n  try {\r\n    throwIfError(H3.getPentagons(res, hexagons));\r\n    return readArrayOfH3Indexes(hexagons, count);\r\n  } finally {\r\n    libh3._free(hexagons);\r\n  }\r\n}\r\n\r\n/**\r\n * Convert degrees to radians\r\n * @static\r\n * @param  {number} deg Value in degrees\r\n * @return {number}     Value in radians\r\n */\r\nfunction degsToRads(deg) {\r\n  return deg * Math.PI / 180;\r\n}\r\n\r\n/**\r\n * Convert radians to degrees\r\n * @static\r\n * @param  {number} rad Value in radians\r\n * @return {number}     Value in degrees\r\n */\r\nfunction radsToDegs(rad) {\r\n  return rad * 180 / Math.PI;\r\n}\r\n\r\nexport { UNITS, POLYGON_TO_CELLS_FLAGS, h3IndexToSplitLong, splitLongToH3Index, isValidCell, isPentagon, isResClassIII, getBaseCellNumber, getIcosahedronFaces, getResolution, latLngToCell, cellToLatLng, cellToBoundary, cellToParent, cellToChildren, cellToChildrenSize, cellToCenterChild, cellToChildPos, childPosToCell, gridDisk, gridDiskDistances, gridRing, gridRingUnsafe, polygonToCells, polygonToCellsExperimental, cellsToMultiPolygon, compactCells, uncompactCells, areNeighborCells, cellsToDirectedEdge, getDirectedEdgeOrigin, getDirectedEdgeDestination, isValidDirectedEdge, directedEdgeToCells, originToDirectedEdges, directedEdgeToBoundary, gridDistance, gridPathCells, cellToLocalIj, localIjToCell, greatCircleDistance, cellArea, edgeLength, getHexagonAreaAvg, getHexagonEdgeLengthAvg, cellToVertex, cellToVertexes, vertexToLatLng, isValidVertex, getNumCells, getRes0Cells, getPentagons, degsToRads, radsToDegs };\r\n//# sourceMappingURL=h3-js.es.js.map\r\n","modules/coach-world/vendor/maplibre-gl.css":".maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0 0 0/0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px rgba(0,0,0,.1)}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:rgb(0 0 0/5%)}}.maplibregl-ctrl button:not(:disabled):active{background-color:rgb(0 0 0/5%)}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E\")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E\")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E\")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E\")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E\")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E\")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E\")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E\")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E\")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E\")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E\")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E\")}}@keyframes maplibregl-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E\");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E\")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E\")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:hsla(0,0%,100%,.5);margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:hsla(0,0%,100%,.5);background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E\");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:rgb(0 0 0/5%)}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E\")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E\")}}.maplibregl-ctrl-attrib a{color:rgba(0,0,0,.75);text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{background-color:hsla(0,0%,100%,.75);border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-close-button:hover{background-color:rgb(0 0 0/5%)}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.1);padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:\"\";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px rgba(0,0,0,.35);box-sizing:border-box;content:\"\";height:19px;left:-2px;position:absolute;top:-2px;width:19px}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:rgba(0,0,0,.4);color:#fff;display:flex;font-size:1.4em;inset:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}","modules/coach-world/vendor/maplibre-gl.js":"/**\r\n * MapLibre GL JS\r\n * @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v5.6.0/LICENSE.txt\r\n */\r\n(function (global, factory) {\r\ntypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\r\ntypeof define === 'function' && define.amd ? define(factory) :\r\n(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.maplibregl = factory());\r\n})(this, (function () { 'use strict';\r\n\r\n/* eslint-disable */\r\n\r\nvar maplibregl = {};\r\nvar modules = {};\r\nfunction define(moduleName, _dependencies, moduleFactory) {\r\n    modules[moduleName] = moduleFactory;\r\n\r\n    // to get the list of modules see generated dist/maplibre-gl-dev.js file (look for `define(` calls)\r\n    if (moduleName !== 'index') {\r\n        return;\r\n    }\r\n\r\n    // we assume that when an index module is initializing then other modules are loaded already\r\n    var workerBundleString = 'var sharedModule = {}; (' + modules.shared + ')(sharedModule); (' + modules.worker + ')(sharedModule);'\r\n\r\n    var sharedModule = {};\r\n    // the order of arguments of a module factory depends on rollup (it decides who is whose dependency)\r\n    // to check the correct order, see dist/maplibre-gl-dev.js file (look for `define(` calls)\r\n    // we assume that for our 3 chunks it will generate 3 modules and their order is predefined like the following\r\n    modules.shared(sharedModule);\r\n    modules.index(maplibregl, sharedModule);\r\n\r\n    if (typeof window !== 'undefined') {\r\n        maplibregl.setWorkerUrl(window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' })));\r\n    }\r\n\r\n    return maplibregl;\r\n};\r\n\r\n\r\n\r\ndefine(\"shared\",[\"exports\"],(function(t){\"use strict\";function e(t,e,r,n){return new(r||(r=Promise))((function(i,s){function a(t){try{l(n.next(t));}catch(t){s(t);}}function o(t){try{l(n.throw(t));}catch(t){s(t);}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e);}))).then(a,o);}l((n=n.apply(t,e||[])).next());}))}function r(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,\"default\")?t.default:t}var n,i;function s(){if(i)return n;function t(t,e){this.x=t,this.y=e;}return i=1,n=t,t.prototype={clone:function(){return new t(this.x,this.y)},add:function(t){return this.clone()._add(t)},sub:function(t){return this.clone()._sub(t)},multByPoint:function(t){return this.clone()._multByPoint(t)},divByPoint:function(t){return this.clone()._divByPoint(t)},mult:function(t){return this.clone()._mult(t)},div:function(t){return this.clone()._div(t)},rotate:function(t){return this.clone()._rotate(t)},rotateAround:function(t,e){return this.clone()._rotateAround(t,e)},matMult:function(t){return this.clone()._matMult(t)},unit:function(){return this.clone()._unit()},perp:function(){return this.clone()._perp()},round:function(){return this.clone()._round()},mag:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals:function(t){return this.x===t.x&&this.y===t.y},dist:function(t){return Math.sqrt(this.distSqr(t))},distSqr:function(t){var e=t.x-this.x,r=t.y-this.y;return e*e+r*r},angle:function(){return Math.atan2(this.y,this.x)},angleTo:function(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith:function(t){return this.angleWithSep(t.x,t.y)},angleWithSep:function(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult:function(t){var e=t[2]*this.x+t[3]*this.y;return this.x=t[0]*this.x+t[1]*this.y,this.y=e,this},_add:function(t){return this.x+=t.x,this.y+=t.y,this},_sub:function(t){return this.x-=t.x,this.y-=t.y,this},_mult:function(t){return this.x*=t,this.y*=t,this},_div:function(t){return this.x/=t,this.y/=t,this},_multByPoint:function(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint:function(t){return this.x/=t.x,this.y/=t.y,this},_unit:function(){return this._div(this.mag()),this},_perp:function(){var t=this.y;return this.y=this.x,this.x=-t,this},_rotate:function(t){var e=Math.cos(t),r=Math.sin(t),n=r*this.x+e*this.y;return this.x=e*this.x-r*this.y,this.y=n,this},_rotateAround:function(t,e){var r=Math.cos(t),n=Math.sin(t),i=e.y+n*(this.x-e.x)+r*(this.y-e.y);return this.x=e.x+r*(this.x-e.x)-n*(this.y-e.y),this.y=i,this},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}},t.convert=function(e){return e instanceof t?e:Array.isArray(e)?new t(e[0],e[1]):e},n}\"function\"==typeof SuppressedError&&SuppressedError;var a,o,l=r(s()),u=function(){if(o)return a;function t(t,e,r,n){this.cx=3*t,this.bx=3*(r-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(n-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=r,this.p2y=n;}return o=1,a=t,t.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var r=t,n=0;n<8;n++){var i=this.sampleCurveX(r)-t;if(Math.abs(i)<e)return r;var s=this.sampleCurveDerivativeX(r);if(Math.abs(s)<1e-6)break;r-=i/s;}var a=0,o=1;for(r=t,n=0;n<20&&(i=this.sampleCurveX(r),!(Math.abs(i-t)<e));n++)t>i?a=r:o=r,r=.5*(o-a)+a;return r},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},a}(),c=r(u);let h,p;function f(){return null==h&&(h=\"undefined\"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext(\"2d\")&&\"function\"==typeof createImageBitmap),h}function d(){if(null==p&&(p=!1,f())){const t=5,e=new OffscreenCanvas(t,t).getContext(\"2d\",{willReadFrequently:!0});if(e){for(let r=0;r<t*t;r++){const n=4*r;e.fillStyle=`rgb(${n},${n+1},${n+2})`,e.fillRect(r%t,Math.floor(r/t),1,1);}const r=e.getImageData(0,0,t,t).data;for(let e=0;e<t*t*4;e++)if(e%4!=3&&r[e]!==e){p=!0;break}}}return p||!1}var y=1e-6,m=\"undefined\"!=typeof Float32Array?Float32Array:Array;function g(){var t=new m(9);return m!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0),t[0]=1,t[4]=1,t[8]=1,t}function x(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function v(){var t=new m(3);return m!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function b(t){return Math.hypot(t[0],t[1],t[2])}function w(t,e,r){var n=new m(3);return n[0]=t,n[1]=e,n[2]=r,n}function _(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t}function S(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t}function A(t,e,r){var n=e[0],i=e[1],s=e[2],a=r[0],o=r[1],l=r[2];return t[0]=i*l-s*o,t[1]=s*a-n*l,t[2]=n*o-i*a,t}Math.hypot||(Math.hypot=function(){for(var t=0,e=arguments.length;e--;)t+=arguments[e]*arguments[e];return Math.sqrt(t)});var k,M=b;function I(t,e,r){var n=e[0],i=e[1],s=e[2],a=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*s+r[12]*a,t[1]=r[1]*n+r[5]*i+r[9]*s+r[13]*a,t[2]=r[2]*n+r[6]*i+r[10]*s+r[14]*a,t[3]=r[3]*n+r[7]*i+r[11]*s+r[15]*a,t}function z(){var t=new m(4);return m!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function P(t,e,r,n){var i=.5*Math.PI/180;e*=i,r*=i,n*=i;var s=Math.sin(e),a=Math.cos(e),o=Math.sin(r),l=Math.cos(r),u=Math.sin(n),c=Math.cos(n);return t[0]=s*l*c-a*o*u,t[1]=a*o*c+s*l*u,t[2]=a*l*u-s*o*c,t[3]=a*l*c+s*o*u,t}function C(){var t=new m(2);return m!=Float32Array&&(t[0]=0,t[1]=0),t}function B(t,e){var r=new m(2);return r[0]=t,r[1]=e,r}v(),k=new m(4),m!=Float32Array&&(k[0]=0,k[1]=0,k[2]=0,k[3]=0),v(),w(1,0,0),w(0,1,0),z(),z(),g(),C();const E=8192;function T(t,e,r){return e*(E/(t.tileSize*Math.pow(2,r-t.tileID.overscaledZ)))}function V(t,e){return (t%e+e)%e}function F(t,e,r){return t*(1-r)+e*r}function L(t){if(t<=0)return 0;if(t>=1)return 1;const e=t*t,r=e*t;return 4*(t<.5?r:3*(t-e)+r-.75)}function $(t,e,r,n){const i=new c(t,e,r,n);return t=>i.solve(t)}const D=$(.25,.1,.25,1);function O(t,e,r){return Math.min(r,Math.max(e,t))}function R(t,e,r){const n=r-e,i=((t-e)%n+n)%n+e;return i===e?r:i}function j(t,...e){for(const r of e)for(const e in r)t[e]=r[e];return t}let N=1;function U(t,e,r){const n={};for(const r in t)n[r]=e.call(this,t[r],r,t);return n}function q(t,e,r){const n={};for(const r in t)e.call(this,t[r],r,t)&&(n[r]=t[r]);return n}function G(t){return Array.isArray(t)?t.map(G):\"object\"==typeof t&&t?U(t,G):t}const X={};function Z(t){X[t]||(\"undefined\"!=typeof console&&console.warn(t),X[t]=!0);}function Y(t,e,r){return (r.y-t.y)*(e.x-t.x)>(e.y-t.y)*(r.x-t.x)}function H(t){return \"undefined\"!=typeof WorkerGlobalScope&&void 0!==t&&t instanceof WorkerGlobalScope}let K=null;function J(t){return \"undefined\"!=typeof ImageBitmap&&t instanceof ImageBitmap}const W=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=\";function Q(t,r,n,i,s){return e(this,void 0,void 0,(function*(){if(\"undefined\"==typeof VideoFrame)throw new Error(\"VideoFrame not supported\");const e=new VideoFrame(t,{timestamp:0});try{const a=null==e?void 0:e.format;if(!a||!a.startsWith(\"BGR\")&&!a.startsWith(\"RGB\"))throw new Error(`Unrecognized format ${a}`);const o=a.startsWith(\"BGR\"),l=new Uint8ClampedArray(i*s*4);if(yield e.copyTo(l,function(t,e,r,n,i){const s=4*Math.max(1,0),a=(Math.max(0,r)-r)*n*4+s,o=4*n,l=Math.max(0,e),u=Math.max(0,r);return {rect:{x:l,y:u,width:Math.min(t.width,e+n)-l,height:Math.min(t.height,r+i)-u},layout:[{offset:a,stride:o}]}}(t,r,n,i,s)),o)for(let t=0;t<l.length;t+=4){const e=l[t];l[t]=l[t+2],l[t+2]=e;}return l}finally{e.close();}}))}let tt,et;function rt(t,e,r,n){return t.addEventListener(e,r,n),{unsubscribe:()=>{t.removeEventListener(e,r,n);}}}function nt(t){return t*Math.PI/180}function it(t){return t/Math.PI*180}const st={touchstart:!0,touchmove:!0,touchmoveWindow:!0,touchend:!0,touchcancel:!0},at={dblclick:!0,click:!0,mouseover:!0,mouseout:!0,mousedown:!0,mousemove:!0,mousemoveWindow:!0,mouseup:!0,mouseupWindow:!0,contextmenu:!0,wheel:!0},ot=\"AbortError\";function lt(){return new Error(ot)}const ut={MAX_PARALLEL_IMAGE_REQUESTS:16,MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME:8,MAX_TILE_CACHE_ZOOM_LEVELS:5,REGISTERED_PROTOCOLS:{},WORKER_URL:\"\"};function ct(t){return ut.REGISTERED_PROTOCOLS[t.substring(0,t.indexOf(\"://\"))]}const ht=\"global-dispatcher\";class pt extends Error{constructor(t,e,r,n){super(`AJAXError: ${e} (${t}): ${r}`),this.status=t,this.statusText=e,this.url=r,this.body=n;}}const ft=()=>H(self)?self.worker&&self.worker.referrer:(\"blob:\"===window.location.protocol?window.parent:window).location.href,dt=function(t,r){if(/:\\/\\//.test(t.url)&&!/^https?:|^file:/.test(t.url)){const e=ct(t.url);if(e)return e(t,r);if(H(self)&&self.worker&&self.worker.actor)return self.worker.actor.sendAsync({type:\"GR\",data:t,targetMapId:ht},r)}if(!(/^file:/.test(n=t.url)||/^file:/.test(ft())&&!/^\\w+:/.test(n))){if(fetch&&Request&&AbortController&&Object.prototype.hasOwnProperty.call(Request.prototype,\"signal\"))return function(t,r){return e(this,void 0,void 0,(function*(){const e=new Request(t.url,{method:t.method||\"GET\",body:t.body,credentials:t.credentials,headers:t.headers,cache:t.cache,referrer:ft(),signal:r.signal});let n,i;\"json\"!==t.type||e.headers.has(\"Accept\")||e.headers.set(\"Accept\",\"application/json\");try{n=yield fetch(e);}catch(e){throw new pt(0,e.message,t.url,new Blob)}if(!n.ok){const e=yield n.blob();throw new pt(n.status,n.statusText,t.url,e)}i=\"arrayBuffer\"===t.type||\"image\"===t.type?n.arrayBuffer():\"json\"===t.type?n.json():n.text();const s=yield i;if(r.signal.aborted)throw lt();return {data:s,cacheControl:n.headers.get(\"Cache-Control\"),expires:n.headers.get(\"Expires\")}}))}(t,r);if(H(self)&&self.worker&&self.worker.actor)return self.worker.actor.sendAsync({type:\"GR\",data:t,mustQueue:!0,targetMapId:ht},r)}var n;return function(t,e){return new Promise(((r,n)=>{var i;const s=new XMLHttpRequest;s.open(t.method||\"GET\",t.url,!0),\"arrayBuffer\"!==t.type&&\"image\"!==t.type||(s.responseType=\"arraybuffer\");for(const e in t.headers)s.setRequestHeader(e,t.headers[e]);\"json\"===t.type&&(s.responseType=\"text\",(null===(i=t.headers)||void 0===i?void 0:i.Accept)||s.setRequestHeader(\"Accept\",\"application/json\")),s.withCredentials=\"include\"===t.credentials,s.onerror=()=>{n(new Error(s.statusText));},s.onload=()=>{if(!e.signal.aborted)if((s.status>=200&&s.status<300||0===s.status)&&null!==s.response){let e=s.response;if(\"json\"===t.type)try{e=JSON.parse(s.response);}catch(t){return void n(t)}r({data:e,cacheControl:s.getResponseHeader(\"Cache-Control\"),expires:s.getResponseHeader(\"Expires\")});}else {const e=new Blob([s.response],{type:s.getResponseHeader(\"Content-Type\")});n(new pt(s.status,s.statusText,t.url,e));}},e.signal.addEventListener(\"abort\",(()=>{s.abort(),n(lt());})),s.send(t.body);}))}(t,r)};function yt(t){if(!t||t.indexOf(\"://\")<=0||0===t.indexOf(\"data:image/\")||0===t.indexOf(\"blob:\"))return !0;const e=new URL(t),r=window.location;return e.protocol===r.protocol&&e.host===r.host}function mt(t,e,r){r[t]&&-1!==r[t].indexOf(e)||(r[t]=r[t]||[],r[t].push(e));}function gt(t,e,r){if(r&&r[t]){const n=r[t].indexOf(e);-1!==n&&r[t].splice(n,1);}}class xt{constructor(t,e={}){j(this,e),this.type=t;}}class vt extends xt{constructor(t,e={}){super(\"error\",j({error:t},e));}}class bt{on(t,e){return this._listeners=this._listeners||{},mt(t,e,this._listeners),{unsubscribe:()=>{this.off(t,e);}}}off(t,e){return gt(t,e,this._listeners),gt(t,e,this._oneTimeListeners),this}once(t,e){return e?(this._oneTimeListeners=this._oneTimeListeners||{},mt(t,e,this._oneTimeListeners),this):new Promise((e=>this.once(t,e)))}fire(t,e){\"string\"==typeof t&&(t=new xt(t,e||{}));const r=t.type;if(this.listens(r)){t.target=this;const e=this._listeners&&this._listeners[r]?this._listeners[r].slice():[];for(const r of e)r.call(this,t);const n=this._oneTimeListeners&&this._oneTimeListeners[r]?this._oneTimeListeners[r].slice():[];for(const e of n)gt(r,e,this._oneTimeListeners),e.call(this,t);const i=this._eventedParent;i&&(j(t,\"function\"==typeof this._eventedParentData?this._eventedParentData():this._eventedParentData),i.fire(t));}else t instanceof vt&&console.error(t.error);return this}listens(t){return this._listeners&&this._listeners[t]&&this._listeners[t].length>0||this._oneTimeListeners&&this._oneTimeListeners[t]&&this._oneTimeListeners[t].length>0||this._eventedParent&&this._eventedParent.listens(t)}setEventedParent(t,e){return this._eventedParent=t,this._eventedParentData=e,this}}var wt={$version:8,$root:{version:{required:!0,type:\"enum\",values:[8]},name:{type:\"string\"},metadata:{type:\"*\"},center:{type:\"array\",value:\"number\"},centerAltitude:{type:\"number\"},zoom:{type:\"number\"},bearing:{type:\"number\",default:0,period:360,units:\"degrees\"},pitch:{type:\"number\",default:0,units:\"degrees\"},roll:{type:\"number\",default:0,units:\"degrees\"},state:{type:\"state\",default:{}},light:{type:\"light\"},sky:{type:\"sky\"},projection:{type:\"projection\"},terrain:{type:\"terrain\"},sources:{required:!0,type:\"sources\"},sprite:{type:\"sprite\"},glyphs:{type:\"string\"},transition:{type:\"transition\"},layers:{required:!0,type:\"array\",value:\"layer\"}},sources:{\"*\":{type:\"source\"}},source:[\"source_vector\",\"source_raster\",\"source_raster_dem\",\"source_geojson\",\"source_video\",\"source_image\"],source_vector:{type:{required:!0,type:\"enum\",values:{vector:{}}},url:{type:\"string\"},tiles:{type:\"array\",value:\"string\"},bounds:{type:\"array\",value:\"number\",length:4,default:[-180,-85.051129,180,85.051129]},scheme:{type:\"enum\",values:{xyz:{},tms:{}},default:\"xyz\"},minzoom:{type:\"number\",default:0},maxzoom:{type:\"number\",default:22},attribution:{type:\"string\"},promoteId:{type:\"promoteId\"},volatile:{type:\"boolean\",default:!1},\"*\":{type:\"*\"}},source_raster:{type:{required:!0,type:\"enum\",values:{raster:{}}},url:{type:\"string\"},tiles:{type:\"array\",value:\"string\"},bounds:{type:\"array\",value:\"number\",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:\"number\",default:0},maxzoom:{type:\"number\",default:22},tileSize:{type:\"number\",default:512,units:\"pixels\"},scheme:{type:\"enum\",values:{xyz:{},tms:{}},default:\"xyz\"},attribution:{type:\"string\"},volatile:{type:\"boolean\",default:!1},\"*\":{type:\"*\"}},source_raster_dem:{type:{required:!0,type:\"enum\",values:{\"raster-dem\":{}}},url:{type:\"string\"},tiles:{type:\"array\",value:\"string\"},bounds:{type:\"array\",value:\"number\",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:\"number\",default:0},maxzoom:{type:\"number\",default:22},tileSize:{type:\"number\",default:512,units:\"pixels\"},attribution:{type:\"string\"},encoding:{type:\"enum\",values:{terrarium:{},mapbox:{},custom:{}},default:\"mapbox\"},redFactor:{type:\"number\",default:1},blueFactor:{type:\"number\",default:1},greenFactor:{type:\"number\",default:1},baseShift:{type:\"number\",default:0},volatile:{type:\"boolean\",default:!1},\"*\":{type:\"*\"}},source_geojson:{type:{required:!0,type:\"enum\",values:{geojson:{}}},data:{required:!0,type:\"*\"},maxzoom:{type:\"number\",default:18},attribution:{type:\"string\"},buffer:{type:\"number\",default:128,maximum:512,minimum:0},filter:{type:\"*\"},tolerance:{type:\"number\",default:.375},cluster:{type:\"boolean\",default:!1},clusterRadius:{type:\"number\",default:50,minimum:0},clusterMaxZoom:{type:\"number\"},clusterMinPoints:{type:\"number\"},clusterProperties:{type:\"*\"},lineMetrics:{type:\"boolean\",default:!1},generateId:{type:\"boolean\",default:!1},promoteId:{type:\"promoteId\"}},source_video:{type:{required:!0,type:\"enum\",values:{video:{}}},urls:{required:!0,type:\"array\",value:\"string\"},coordinates:{required:!0,type:\"array\",length:4,value:{type:\"array\",length:2,value:\"number\"}}},source_image:{type:{required:!0,type:\"enum\",values:{image:{}}},url:{required:!0,type:\"string\"},coordinates:{required:!0,type:\"array\",length:4,value:{type:\"array\",length:2,value:\"number\"}}},layer:{id:{type:\"string\",required:!0},type:{type:\"enum\",values:{fill:{},line:{},symbol:{},circle:{},heatmap:{},\"fill-extrusion\":{},raster:{},hillshade:{},\"color-relief\":{},background:{}},required:!0},metadata:{type:\"*\"},source:{type:\"string\"},\"source-layer\":{type:\"string\"},minzoom:{type:\"number\",minimum:0,maximum:24},maxzoom:{type:\"number\",minimum:0,maximum:24},filter:{type:\"filter\"},layout:{type:\"layout\"},paint:{type:\"paint\"}},layout:[\"layout_fill\",\"layout_line\",\"layout_circle\",\"layout_heatmap\",\"layout_fill-extrusion\",\"layout_symbol\",\"layout_raster\",\"layout_hillshade\",\"layout_color-relief\",\"layout_background\"],layout_background:{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_fill:{\"fill-sort-key\":{type:\"number\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_circle:{\"circle-sort-key\":{type:\"number\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_heatmap:{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},\"layout_fill-extrusion\":{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_line:{\"line-cap\":{type:\"enum\",values:{butt:{},round:{},square:{}},default:\"butt\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"line-join\":{type:\"enum\",values:{bevel:{},round:{},miter:{}},default:\"miter\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"line-miter-limit\":{type:\"number\",default:2,requires:[{\"line-join\":\"miter\"}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"line-round-limit\":{type:\"number\",default:1.05,requires:[{\"line-join\":\"round\"}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"line-sort-key\":{type:\"number\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_symbol:{\"symbol-placement\":{type:\"enum\",values:{point:{},line:{},\"line-center\":{}},default:\"point\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"symbol-spacing\":{type:\"number\",default:250,minimum:1,units:\"pixels\",requires:[{\"symbol-placement\":\"line\"}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"symbol-avoid-edges\":{type:\"boolean\",default:!1,expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"symbol-sort-key\":{type:\"number\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"symbol-z-order\":{type:\"enum\",values:{auto:{},\"viewport-y\":{},source:{}},default:\"auto\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-allow-overlap\":{type:\"boolean\",default:!1,requires:[\"icon-image\",{\"!\":\"icon-overlap\"}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-overlap\":{type:\"enum\",values:{never:{},always:{},cooperative:{}},requires:[\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-ignore-placement\":{type:\"boolean\",default:!1,requires:[\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-optional\":{type:\"boolean\",default:!1,requires:[\"icon-image\",\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-rotation-alignment\":{type:\"enum\",values:{map:{},viewport:{},auto:{}},default:\"auto\",requires:[\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-size\":{type:\"number\",default:1,minimum:0,units:\"factor of the original icon size\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-text-fit\":{type:\"enum\",values:{none:{},width:{},height:{},both:{}},default:\"none\",requires:[\"icon-image\",\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-text-fit-padding\":{type:\"array\",value:\"number\",length:4,default:[0,0,0,0],units:\"pixels\",requires:[\"icon-image\",\"text-field\",{\"icon-text-fit\":[\"both\",\"width\",\"height\"]}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-image\":{type:\"resolvedImage\",tokens:!0,expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-rotate\":{type:\"number\",default:0,period:360,units:\"degrees\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-padding\":{type:\"padding\",default:[2],units:\"pixels\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-keep-upright\":{type:\"boolean\",default:!1,requires:[\"icon-image\",{\"icon-rotation-alignment\":\"map\"},{\"symbol-placement\":[\"line\",\"line-center\"]}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-offset\":{type:\"array\",value:\"number\",length:2,default:[0,0],requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-anchor\":{type:\"enum\",values:{center:{},left:{},right:{},top:{},bottom:{},\"top-left\":{},\"top-right\":{},\"bottom-left\":{},\"bottom-right\":{}},default:\"center\",requires:[\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"icon-pitch-alignment\":{type:\"enum\",values:{map:{},viewport:{},auto:{}},default:\"auto\",requires:[\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-pitch-alignment\":{type:\"enum\",values:{map:{},viewport:{},auto:{}},default:\"auto\",requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-rotation-alignment\":{type:\"enum\",values:{map:{},viewport:{},\"viewport-glyph\":{},auto:{}},default:\"auto\",requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-field\":{type:\"formatted\",default:\"\",tokens:!0,expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-font\":{type:\"array\",value:\"string\",default:[\"Open Sans Regular\",\"Arial Unicode MS Regular\"],requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-size\":{type:\"number\",default:16,minimum:0,units:\"pixels\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-max-width\":{type:\"number\",default:10,minimum:0,units:\"ems\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-line-height\":{type:\"number\",default:1.2,units:\"ems\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-letter-spacing\":{type:\"number\",default:0,units:\"ems\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-justify\":{type:\"enum\",values:{auto:{},left:{},center:{},right:{}},default:\"center\",requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-radial-offset\":{type:\"number\",units:\"ems\",default:0,requires:[\"text-field\"],\"property-type\":\"data-driven\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]}},\"text-variable-anchor\":{type:\"array\",value:\"enum\",values:{center:{},left:{},right:{},top:{},bottom:{},\"top-left\":{},\"top-right\":{},\"bottom-left\":{},\"bottom-right\":{}},requires:[\"text-field\",{\"symbol-placement\":[\"point\"]}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-variable-anchor-offset\":{type:\"variableAnchorOffsetCollection\",requires:[\"text-field\",{\"symbol-placement\":[\"point\"]}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-anchor\":{type:\"enum\",values:{center:{},left:{},right:{},top:{},bottom:{},\"top-left\":{},\"top-right\":{},\"bottom-left\":{},\"bottom-right\":{}},default:\"center\",requires:[\"text-field\",{\"!\":\"text-variable-anchor\"}],expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-max-angle\":{type:\"number\",default:45,units:\"degrees\",requires:[\"text-field\",{\"symbol-placement\":[\"line\",\"line-center\"]}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-writing-mode\":{type:\"array\",value:\"enum\",values:{horizontal:{},vertical:{}},requires:[\"text-field\",{\"symbol-placement\":[\"point\"]}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-rotate\":{type:\"number\",default:0,period:360,units:\"degrees\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-padding\":{type:\"number\",default:2,minimum:0,units:\"pixels\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-keep-upright\":{type:\"boolean\",default:!0,requires:[\"text-field\",{\"text-rotation-alignment\":\"map\"},{\"symbol-placement\":[\"line\",\"line-center\"]}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-transform\":{type:\"enum\",values:{none:{},uppercase:{},lowercase:{}},default:\"none\",requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-offset\":{type:\"array\",value:\"number\",units:\"ems\",length:2,default:[0,0],requires:[\"text-field\",{\"!\":\"text-radial-offset\"}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"data-driven\"},\"text-allow-overlap\":{type:\"boolean\",default:!1,requires:[\"text-field\",{\"!\":\"text-overlap\"}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-overlap\":{type:\"enum\",values:{never:{},always:{},cooperative:{}},requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-ignore-placement\":{type:\"boolean\",default:!1,requires:[\"text-field\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-optional\":{type:\"boolean\",default:!1,requires:[\"text-field\",\"icon-image\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_raster:{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},layout_hillshade:{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},\"layout_color-relief\":{visibility:{type:\"enum\",values:{visible:{},none:{}},default:\"visible\",\"property-type\":\"constant\"}},filter:{type:\"array\",value:\"*\"},filter_operator:{type:\"enum\",values:{\"==\":{},\"!=\":{},\">\":{},\">=\":{},\"<\":{},\"<=\":{},in:{},\"!in\":{},all:{},any:{},none:{},has:{},\"!has\":{}}},geometry_type:{type:\"enum\",values:{Point:{},LineString:{},Polygon:{}}},function:{expression:{type:\"expression\"},stops:{type:\"array\",value:\"function_stop\"},base:{type:\"number\",default:1,minimum:0},property:{type:\"string\",default:\"$zoom\"},type:{type:\"enum\",values:{identity:{},exponential:{},interval:{},categorical:{}},default:\"exponential\"},colorSpace:{type:\"enum\",values:{rgb:{},lab:{},hcl:{}},default:\"rgb\"},default:{type:\"*\",required:!1}},function_stop:{type:\"array\",minimum:0,maximum:24,value:[\"number\",\"color\"],length:2},expression:{type:\"array\",value:\"*\",minimum:1},light:{anchor:{type:\"enum\",default:\"viewport\",values:{map:{},viewport:{}},\"property-type\":\"data-constant\",transition:!1,expression:{interpolated:!1,parameters:[\"zoom\"]}},position:{type:\"array\",default:[1.15,210,30],length:3,value:\"number\",\"property-type\":\"data-constant\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]}},color:{type:\"color\",\"property-type\":\"data-constant\",default:\"#ffffff\",expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},intensity:{type:\"number\",\"property-type\":\"data-constant\",default:.5,minimum:0,maximum:1,expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0}},sky:{\"sky-color\":{type:\"color\",\"property-type\":\"data-constant\",default:\"#88C6FC\",expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"horizon-color\":{type:\"color\",\"property-type\":\"data-constant\",default:\"#ffffff\",expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"fog-color\":{type:\"color\",\"property-type\":\"data-constant\",default:\"#ffffff\",expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"fog-ground-blend\":{type:\"number\",\"property-type\":\"data-constant\",default:.5,minimum:0,maximum:1,expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"horizon-fog-blend\":{type:\"number\",\"property-type\":\"data-constant\",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"sky-horizon-blend\":{type:\"number\",\"property-type\":\"data-constant\",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0},\"atmosphere-blend\":{type:\"number\",\"property-type\":\"data-constant\",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:[\"zoom\"]},transition:!0}},terrain:{source:{type:\"string\",required:!0},exaggeration:{type:\"number\",minimum:0,default:1}},projection:{type:{type:\"projectionDefinition\",default:\"mercator\",\"property-type\":\"data-constant\",transition:!1,expression:{interpolated:!0,parameters:[\"zoom\"]}}},paint:[\"paint_fill\",\"paint_line\",\"paint_circle\",\"paint_heatmap\",\"paint_fill-extrusion\",\"paint_symbol\",\"paint_raster\",\"paint_hillshade\",\"paint_color-relief\",\"paint_background\"],paint_fill:{\"fill-antialias\":{type:\"boolean\",default:!0,expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-color\":{type:\"color\",default:\"#000000\",transition:!0,requires:[{\"!\":\"fill-pattern\"}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-outline-color\":{type:\"color\",transition:!0,requires:[{\"!\":\"fill-pattern\"},{\"fill-antialias\":!0}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"fill-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-pattern\":{type:\"resolvedImage\",transition:!0,expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"cross-faded-data-driven\"}},\"paint_fill-extrusion\":{\"fill-extrusion-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-extrusion-color\":{type:\"color\",default:\"#000000\",transition:!0,requires:[{\"!\":\"fill-extrusion-pattern\"}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-extrusion-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-extrusion-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"fill-extrusion-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"fill-extrusion-pattern\":{type:\"resolvedImage\",transition:!0,expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"cross-faded-data-driven\"},\"fill-extrusion-height\":{type:\"number\",default:0,minimum:0,units:\"meters\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-extrusion-base\":{type:\"number\",default:0,minimum:0,units:\"meters\",transition:!0,requires:[\"fill-extrusion-height\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"fill-extrusion-vertical-gradient\":{type:\"boolean\",default:!0,transition:!1,expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},paint_line:{\"line-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-color\":{type:\"color\",default:\"#000000\",transition:!0,requires:[{\"!\":\"line-pattern\"}],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"line-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"line-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"line-width\":{type:\"number\",default:1,minimum:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-gap-width\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-offset\":{type:\"number\",default:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-blur\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"line-dasharray\":{type:\"array\",value:\"number\",minimum:0,transition:!0,units:\"line widths\",requires:[{\"!\":\"line-pattern\"}],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"cross-faded\"},\"line-pattern\":{type:\"resolvedImage\",transition:!0,expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]},\"property-type\":\"cross-faded-data-driven\"},\"line-gradient\":{type:\"color\",transition:!1,requires:[{\"!\":\"line-dasharray\"},{\"!\":\"line-pattern\"},{source:\"geojson\",has:{lineMetrics:!0}}],expression:{interpolated:!0,parameters:[\"line-progress\"]},\"property-type\":\"color-ramp\"}},paint_circle:{\"circle-radius\":{type:\"number\",default:5,minimum:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-color\":{type:\"color\",default:\"#000000\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-blur\":{type:\"number\",default:0,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"circle-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"circle-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"circle-pitch-scale\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"circle-pitch-alignment\":{type:\"enum\",values:{map:{},viewport:{}},default:\"viewport\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"circle-stroke-width\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-stroke-color\":{type:\"color\",default:\"#000000\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"circle-stroke-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"}},paint_heatmap:{\"heatmap-radius\":{type:\"number\",default:30,minimum:1,transition:!0,units:\"pixels\",expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"heatmap-weight\":{type:\"number\",default:1,minimum:0,transition:!1,expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"heatmap-intensity\":{type:\"number\",default:1,minimum:0,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"heatmap-color\":{type:\"color\",default:[\"interpolate\",[\"linear\"],[\"heatmap-density\"],0,\"rgba(0, 0, 255, 0)\",.1,\"royalblue\",.3,\"cyan\",.5,\"lime\",.7,\"yellow\",1,\"red\"],transition:!1,expression:{interpolated:!0,parameters:[\"heatmap-density\"]},\"property-type\":\"color-ramp\"},\"heatmap-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},paint_symbol:{\"icon-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"icon-color\":{type:\"color\",default:\"#000000\",transition:!0,requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"icon-halo-color\":{type:\"color\",default:\"rgba(0, 0, 0, 0)\",transition:!0,requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"icon-halo-width\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"icon-halo-blur\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"icon-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",requires:[\"icon-image\"],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"icon-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"icon-image\",\"icon-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"text-color\":{type:\"color\",default:\"#000000\",transition:!0,overridable:!0,requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"text-halo-color\":{type:\"color\",default:\"rgba(0, 0, 0, 0)\",transition:!0,requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"text-halo-width\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"text-halo-blur\":{type:\"number\",default:0,minimum:0,transition:!0,units:\"pixels\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\",\"feature\",\"feature-state\"]},\"property-type\":\"data-driven\"},\"text-translate\":{type:\"array\",value:\"number\",length:2,default:[0,0],transition:!0,units:\"pixels\",requires:[\"text-field\"],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"text-translate-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"map\",requires:[\"text-field\",\"text-translate\"],expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},paint_raster:{\"raster-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-hue-rotate\":{type:\"number\",default:0,period:360,transition:!0,units:\"degrees\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-brightness-min\":{type:\"number\",default:0,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-brightness-max\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-saturation\":{type:\"number\",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-contrast\":{type:\"number\",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-resampling\":{type:\"enum\",values:{linear:{},nearest:{}},default:\"linear\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"raster-fade-duration\":{type:\"number\",default:300,minimum:0,transition:!1,units:\"milliseconds\",expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},paint_hillshade:{\"hillshade-illumination-direction\":{type:\"numberArray\",default:335,minimum:0,maximum:359,transition:!1,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-illumination-altitude\":{type:\"numberArray\",default:45,minimum:0,maximum:90,transition:!1,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-illumination-anchor\":{type:\"enum\",values:{map:{},viewport:{}},default:\"viewport\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-exaggeration\":{type:\"number\",default:.5,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-shadow-color\":{type:\"colorArray\",default:\"#000000\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-highlight-color\":{type:\"colorArray\",default:\"#FFFFFF\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-accent-color\":{type:\"color\",default:\"#000000\",transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"hillshade-method\":{type:\"enum\",values:{standard:{},basic:{},combined:{},igor:{},multidirectional:{}},default:\"standard\",expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},\"paint_color-relief\":{\"color-relief-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"color-relief-color\":{type:\"color\",transition:!1,expression:{interpolated:!0,parameters:[\"elevation\"]},\"property-type\":\"color-ramp\"}},paint_background:{\"background-color\":{type:\"color\",default:\"#000000\",transition:!0,requires:[{\"!\":\"background-pattern\"}],expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"},\"background-pattern\":{type:\"resolvedImage\",transition:!0,expression:{interpolated:!1,parameters:[\"zoom\"]},\"property-type\":\"cross-faded\"},\"background-opacity\":{type:\"number\",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:[\"zoom\"]},\"property-type\":\"data-constant\"}},transition:{duration:{type:\"number\",default:300,minimum:0,units:\"milliseconds\"},delay:{type:\"number\",default:0,minimum:0,units:\"milliseconds\"}},\"property-type\":{\"data-driven\":{type:\"property-type\"},\"cross-faded\":{type:\"property-type\"},\"cross-faded-data-driven\":{type:\"property-type\"},\"color-ramp\":{type:\"property-type\"},\"data-constant\":{type:\"property-type\"},constant:{type:\"property-type\"}},promoteId:{\"*\":{type:\"string\"}}};const _t=[\"type\",\"source\",\"source-layer\",\"minzoom\",\"maxzoom\",\"filter\",\"layout\"];function St(t,e){const r={};for(const e in t)\"ref\"!==e&&(r[e]=t[e]);return _t.forEach((t=>{t in e&&(r[t]=e[t]);})),r}function At(t,e){if(Array.isArray(t)){if(!Array.isArray(e)||t.length!==e.length)return !1;for(let r=0;r<t.length;r++)if(!At(t[r],e[r]))return !1;return !0}if(\"object\"==typeof t&&null!==t&&null!==e){if(\"object\"!=typeof e)return !1;if(Object.keys(t).length!==Object.keys(e).length)return !1;for(const r in t)if(!At(t[r],e[r]))return !1;return !0}return t===e}function kt(t,e){t.push(e);}function Mt(t,e,r){kt(r,{command:\"addSource\",args:[t,e[t]]});}function It(t,e,r){kt(e,{command:\"removeSource\",args:[t]}),r[t]=!0;}function zt(t,e,r,n){It(t,r,n),Mt(t,e,r);}function Pt(t,e,r){let n;for(n in t[r])if(Object.prototype.hasOwnProperty.call(t[r],n)&&\"data\"!==n&&!At(t[r][n],e[r][n]))return !1;for(n in e[r])if(Object.prototype.hasOwnProperty.call(e[r],n)&&\"data\"!==n&&!At(t[r][n],e[r][n]))return !1;return !0}function Ct(t,e,r,n,i,s){t=t||{},e=e||{};for(const a in t)Object.prototype.hasOwnProperty.call(t,a)&&(At(t[a],e[a])||r.push({command:s,args:[n,a,e[a],i]}));for(const a in e)Object.prototype.hasOwnProperty.call(e,a)&&!Object.prototype.hasOwnProperty.call(t,a)&&(At(t[a],e[a])||r.push({command:s,args:[n,a,e[a],i]}));}function Bt(t){return t.id}function Et(t,e){return t[e.id]=e,t}class Tt{constructor(t,e,r,n){this.message=(t?`${t}: `:\"\")+r,n&&(this.identifier=n),null!=e&&e.__line__&&(this.line=e.__line__);}}function Vt(t,...e){for(const r of e)for(const e in r)t[e]=r[e];return t}class Ft extends Error{constructor(t,e){super(e),this.message=e,this.key=t;}}class Lt{constructor(t,e=[]){this.parent=t,this.bindings={};for(const[t,r]of e)this.bindings[t]=r;}concat(t){return new Lt(this,t)}get(t){if(this.bindings[t])return this.bindings[t];if(this.parent)return this.parent.get(t);throw new Error(`${t} not found in scope.`)}has(t){return !!this.bindings[t]||!!this.parent&&this.parent.has(t)}}const $t={kind:\"null\"},Dt={kind:\"number\"},Ot={kind:\"string\"},Rt={kind:\"boolean\"},jt={kind:\"color\"},Nt={kind:\"projectionDefinition\"},Ut={kind:\"object\"},qt={kind:\"value\"},Gt={kind:\"collator\"},Xt={kind:\"formatted\"},Zt={kind:\"padding\"},Yt={kind:\"colorArray\"},Ht={kind:\"numberArray\"},Kt={kind:\"resolvedImage\"},Jt={kind:\"variableAnchorOffsetCollection\"};function Wt(t,e){return {kind:\"array\",itemType:t,N:e}}function Qt(t){if(\"array\"===t.kind){const e=Qt(t.itemType);return \"number\"==typeof t.N?`array<${e}, ${t.N}>`:\"value\"===t.itemType.kind?\"array\":`array<${e}>`}return t.kind}const te=[$t,Dt,Ot,Rt,jt,Nt,Xt,Ut,Wt(qt),Zt,Ht,Yt,Kt,Jt];function ee(t,e){if(\"error\"===e.kind)return null;if(\"array\"===t.kind){if(\"array\"===e.kind&&(0===e.N&&\"value\"===e.itemType.kind||!ee(t.itemType,e.itemType))&&(\"number\"!=typeof t.N||t.N===e.N))return null}else {if(t.kind===e.kind)return null;if(\"value\"===t.kind)for(const t of te)if(!ee(t,e))return null}return `Expected ${Qt(t)} but found ${Qt(e)} instead.`}function re(t,e){return e.some((e=>e.kind===t.kind))}function ne(t,e){return e.some((e=>\"null\"===e?null===t:\"array\"===e?Array.isArray(t):\"object\"===e?t&&!Array.isArray(t)&&\"object\"==typeof t:e===typeof t))}function ie(t,e){return \"array\"===t.kind&&\"array\"===e.kind?t.itemType.kind===e.itemType.kind&&\"number\"==typeof t.N:t.kind===e.kind}const se=.96422,ae=.82521,oe=4/29,le=6/29,ue=3*le*le,ce=le*le*le,he=Math.PI/180,pe=180/Math.PI;function fe(t){return (t%=360)<0&&(t+=360),t}function de([t,e,r,n]){let i,s;const a=me((.2225045*(t=ye(t))+.7168786*(e=ye(e))+.0606169*(r=ye(r)))/1);t===e&&e===r?i=s=a:(i=me((.4360747*t+.3850649*e+.1430804*r)/se),s=me((.0139322*t+.0971045*e+.7141733*r)/ae));const o=116*a-16;return [o<0?0:o,500*(i-a),200*(a-s),n]}function ye(t){return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function me(t){return t>ce?Math.pow(t,1/3):t/ue+oe}function ge([t,e,r,n]){let i=(t+16)/116,s=isNaN(e)?i:i+e/500,a=isNaN(r)?i:i-r/200;return i=1*ve(i),s=se*ve(s),a=ae*ve(a),[xe(3.1338561*s-1.6168667*i-.4906146*a),xe(-.9787684*s+1.9161415*i+.033454*a),xe(.0719453*s-.2289914*i+1.4052427*a),n]}function xe(t){return (t=t<=.00304?12.92*t:1.055*Math.pow(t,1/2.4)-.055)<0?0:t>1?1:t}function ve(t){return t>le?t*t*t:ue*(t-oe)}const be=Object.hasOwn||function(t,e){return Object.prototype.hasOwnProperty.call(t,e)};function we(t,e){return be(t,e)?t[e]:void 0}function _e(t){return parseInt(t.padEnd(2,t),16)/255}function Se(t,e){return Ae(e?t/100:t,0,1)}function Ae(t,e,r){return Math.min(Math.max(e,t),r)}function ke(t){return !t.some(Number.isNaN)}const Me={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};function Ie(t,e,r){return t+r*(e-t)}function ze(t,e,r){return t.map(((t,n)=>Ie(t,e[n],r)))}class Pe{constructor(t,e,r,n=1,i=!0){this.r=t,this.g=e,this.b=r,this.a=n,i||(this.r*=n,this.g*=n,this.b*=n,n||this.overwriteGetter(\"rgb\",[t,e,r,n]));}static parse(t){if(t instanceof Pe)return t;if(\"string\"!=typeof t)return;const e=function(t){if(\"transparent\"===(t=t.toLowerCase().trim()))return [0,0,0,0];const e=we(Me,t);if(e){const[t,r,n]=e;return [t/255,r/255,n/255,1]}if(t.startsWith(\"#\")&&/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(t)){const e=t.length<6?1:2;let r=1;return [_e(t.slice(r,r+=e)),_e(t.slice(r,r+=e)),_e(t.slice(r,r+=e)),_e(t.slice(r,r+e)||\"ff\")]}if(t.startsWith(\"rgb\")){const e=t.match(/^rgba?\\(\\s*([\\de.+-]+)(%)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)(%)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)(%)?(?:\\s*([,\\/])\\s*([\\de.+-]+)(%)?)?\\s*\\)$/);if(e){const[t,r,n,i,s,a,o,l,u,c,h,p]=e,f=[i||\" \",o||\" \",c].join(\"\");if(\"  \"===f||\"  /\"===f||\",,\"===f||\",,,\"===f){const t=[n,a,u].join(\"\"),e=\"%%%\"===t?100:\"\"===t?255:0;if(e){const t=[Ae(+r/e,0,1),Ae(+s/e,0,1),Ae(+l/e,0,1),h?Se(+h,p):1];if(ke(t))return t}}return}}const r=t.match(/^hsla?\\(\\s*([\\de.+-]+)(?:deg)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)%(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)%(?:\\s*([,\\/])\\s*([\\de.+-]+)(%)?)?\\s*\\)$/);if(r){const[t,e,n,i,s,a,o,l,u]=r,c=[n||\" \",s||\" \",o].join(\"\");if(\"  \"===c||\"  /\"===c||\",,\"===c||\",,,\"===c){const t=[+e,Ae(+i,0,100),Ae(+a,0,100),l?Se(+l,u):1];if(ke(t))return function([t,e,r,n]){function i(n){const i=(n+t/30)%12,s=e*Math.min(r,1-r);return r-s*Math.max(-1,Math.min(i-3,9-i,1))}return t=fe(t),e/=100,r/=100,[i(0),i(8),i(4),n]}(t)}}}(t);return e?new Pe(...e,!1):void 0}get rgb(){const{r:t,g:e,b:r,a:n}=this,i=n||1/0;return this.overwriteGetter(\"rgb\",[t/i,e/i,r/i,n])}get hcl(){return this.overwriteGetter(\"hcl\",function(t){const[e,r,n,i]=de(t),s=Math.sqrt(r*r+n*n);return [Math.round(1e4*s)?fe(Math.atan2(n,r)*pe):NaN,s,e,i]}(this.rgb))}get lab(){return this.overwriteGetter(\"lab\",de(this.rgb))}overwriteGetter(t,e){return Object.defineProperty(this,t,{value:e}),e}toString(){const[t,e,r,n]=this.rgb;return `rgba(${[t,e,r].map((t=>Math.round(255*t))).join(\",\")},${n})`}static interpolate(t,e,r,n=\"rgb\"){switch(n){case \"rgb\":{const[n,i,s,a]=ze(t.rgb,e.rgb,r);return new Pe(n,i,s,a,!1)}case \"hcl\":{const[n,i,s,a]=t.hcl,[o,l,u,c]=e.hcl;let h,p;if(isNaN(n)||isNaN(o))isNaN(n)?isNaN(o)?h=NaN:(h=o,1!==s&&0!==s||(p=l)):(h=n,1!==u&&0!==u||(p=i));else {let t=o-n;o>n&&t>180?t-=360:o<n&&n-o>180&&(t+=360),h=n+r*t;}const[f,d,y,m]=function([t,e,r,n]){return t=isNaN(t)?0:t*he,ge([r,Math.cos(t)*e,Math.sin(t)*e,n])}([h,null!=p?p:Ie(i,l,r),Ie(s,u,r),Ie(a,c,r)]);return new Pe(f,d,y,m,!1)}case \"lab\":{const[n,i,s,a]=ge(ze(t.lab,e.lab,r));return new Pe(n,i,s,a,!1)}}}}Pe.black=new Pe(0,0,0,1),Pe.white=new Pe(1,1,1,1),Pe.transparent=new Pe(0,0,0,0),Pe.red=new Pe(1,0,0,1);class Ce{constructor(t,e,r){this.sensitivity=t?e?\"variant\":\"case\":e?\"accent\":\"base\",this.locale=r,this.collator=new Intl.Collator(this.locale?this.locale:[],{sensitivity:this.sensitivity,usage:\"search\"});}compare(t,e){return this.collator.compare(t,e)}resolvedLocale(){return new Intl.Collator(this.locale?this.locale:[]).resolvedOptions().locale}}const Be=[\"bottom\",\"center\",\"top\"];class Ee{constructor(t,e,r,n,i,s){this.text=t,this.image=e,this.scale=r,this.fontStack=n,this.textColor=i,this.verticalAlign=s;}}class Te{constructor(t){this.sections=t;}static fromString(t){return new Te([new Ee(t,null,null,null,null,null)])}isEmpty(){return 0===this.sections.length||!this.sections.some((t=>0!==t.text.length||t.image&&0!==t.image.name.length))}static factory(t){return t instanceof Te?t:Te.fromString(t)}toString(){return 0===this.sections.length?\"\":this.sections.map((t=>t.text)).join(\"\")}}class Ve{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Ve)return t;if(\"number\"==typeof t)return new Ve([t,t,t,t]);if(Array.isArray(t)&&!(t.length<1||t.length>4)){for(const e of t)if(\"number\"!=typeof e)return;switch(t.length){case 1:t=[t[0],t[0],t[0],t[0]];break;case 2:t=[t[0],t[1],t[0],t[1]];break;case 3:t=[t[0],t[1],t[2],t[1]];}return new Ve(t)}}toString(){return JSON.stringify(this.values)}static interpolate(t,e,r){return new Ve(ze(t.values,e.values,r))}}class Fe{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Fe)return t;if(\"number\"==typeof t)return new Fe([t]);if(Array.isArray(t)){for(const e of t)if(\"number\"!=typeof e)return;return new Fe(t)}}toString(){return JSON.stringify(this.values)}static interpolate(t,e,r){return new Fe(ze(t.values,e.values,r))}}class Le{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Le)return t;if(\"string\"==typeof t){const e=Pe.parse(t);if(!e)return;return new Le([e])}if(!Array.isArray(t))return;const e=[];for(const r of t){if(\"string\"!=typeof r)return;const t=Pe.parse(r);if(!t)return;e.push(t);}return new Le(e)}toString(){return JSON.stringify(this.values)}static interpolate(t,e,r,n=\"rgb\"){const i=[];if(t.values.length!=e.values.length)throw new Error(`colorArray: Arrays have mismatched length (${t.values.length} vs. ${e.values.length}), cannot interpolate.`);for(let s=0;s<t.values.length;s++)i.push(Pe.interpolate(t.values[s],e.values[s],r,n));return new Le(i)}}class $e extends Error{constructor(t){super(t),this.name=\"RuntimeError\";}toJSON(){return this.message}}const De=new Set([\"center\",\"left\",\"right\",\"top\",\"bottom\",\"top-left\",\"top-right\",\"bottom-left\",\"bottom-right\"]);class Oe{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Oe)return t;if(Array.isArray(t)&&!(t.length<1)&&t.length%2==0){for(let e=0;e<t.length;e+=2){const r=t[e],n=t[e+1];if(\"string\"!=typeof r||!De.has(r))return;if(!Array.isArray(n)||2!==n.length||\"number\"!=typeof n[0]||\"number\"!=typeof n[1])return}return new Oe(t)}}toString(){return JSON.stringify(this.values)}static interpolate(t,e,r){const n=t.values,i=e.values;if(n.length!==i.length)throw new $e(`Cannot interpolate values of different length. from: ${t.toString()}, to: ${e.toString()}`);const s=[];for(let t=0;t<n.length;t+=2){if(n[t]!==i[t])throw new $e(`Cannot interpolate values containing mismatched anchors. from[${t}]: ${n[t]}, to[${t}]: ${i[t]}`);s.push(n[t]);const[e,a]=n[t+1],[o,l]=i[t+1];s.push([Ie(e,o,r),Ie(a,l,r)]);}return new Oe(s)}}class Re{constructor(t){this.name=t.name,this.available=t.available;}toString(){return this.name}static fromString(t){return t?new Re({name:t,available:!1}):null}}class je{constructor(t,e,r){this.from=t,this.to=e,this.transition=r;}static interpolate(t,e,r){return new je(t,e,r)}static parse(t){return t instanceof je?t:Array.isArray(t)&&3===t.length&&\"string\"==typeof t[0]&&\"string\"==typeof t[1]&&\"number\"==typeof t[2]?new je(t[0],t[1],t[2]):\"object\"==typeof t&&\"string\"==typeof t.from&&\"string\"==typeof t.to&&\"number\"==typeof t.transition?new je(t.from,t.to,t.transition):\"string\"==typeof t?new je(t,t,1):void 0}}function Ne(t,e,r,n){return \"number\"==typeof t&&t>=0&&t<=255&&\"number\"==typeof e&&e>=0&&e<=255&&\"number\"==typeof r&&r>=0&&r<=255?void 0===n||\"number\"==typeof n&&n>=0&&n<=1?null:`Invalid rgba value [${[t,e,r,n].join(\", \")}]: 'a' must be between 0 and 1.`:`Invalid rgba value [${(\"number\"==typeof n?[t,e,r,n]:[t,e,r]).join(\", \")}]: 'r', 'g', and 'b' must be between 0 and 255.`}function Ue(t){if(null===t||\"string\"==typeof t||\"boolean\"==typeof t||\"number\"==typeof t||t instanceof je||t instanceof Pe||t instanceof Ce||t instanceof Te||t instanceof Ve||t instanceof Fe||t instanceof Le||t instanceof Oe||t instanceof Re)return !0;if(Array.isArray(t)){for(const e of t)if(!Ue(e))return !1;return !0}if(\"object\"==typeof t){for(const e in t)if(!Ue(t[e]))return !1;return !0}return !1}function qe(t){if(null===t)return $t;if(\"string\"==typeof t)return Ot;if(\"boolean\"==typeof t)return Rt;if(\"number\"==typeof t)return Dt;if(t instanceof Pe)return jt;if(t instanceof je)return Nt;if(t instanceof Ce)return Gt;if(t instanceof Te)return Xt;if(t instanceof Ve)return Zt;if(t instanceof Fe)return Ht;if(t instanceof Le)return Yt;if(t instanceof Oe)return Jt;if(t instanceof Re)return Kt;if(Array.isArray(t)){const e=t.length;let r;for(const e of t){const t=qe(e);if(r){if(r===t)continue;r=qt;break}r=t;}return Wt(r||qt,e)}return Ut}function Ge(t){const e=typeof t;return null===t?\"\":\"string\"===e||\"number\"===e||\"boolean\"===e?String(t):t instanceof Pe||t instanceof je||t instanceof Te||t instanceof Ve||t instanceof Fe||t instanceof Le||t instanceof Oe||t instanceof Re?t.toString():JSON.stringify(t)}class Xe{constructor(t,e){this.type=t,this.value=e;}static parse(t,e){if(2!==t.length)return e.error(`'literal' expression requires exactly one argument, but found ${t.length-1} instead.`);if(!Ue(t[1]))return e.error(\"invalid value\");const r=t[1];let n=qe(r);const i=e.expectedType;return \"array\"!==n.kind||0!==n.N||!i||\"array\"!==i.kind||\"number\"==typeof i.N&&0!==i.N||(n=i),new Xe(n,r)}evaluate(){return this.value}eachChild(){}outputDefined(){return !0}}const Ze={string:Ot,number:Dt,boolean:Rt,object:Ut};class Ye{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error(\"Expected at least one argument.\");let r,n=1;const i=t[0];if(\"array\"===i){let i,s;if(t.length>2){const r=t[1];if(\"string\"!=typeof r||!(r in Ze)||\"object\"===r)return e.error('The item type argument of \"array\" must be one of string, number, boolean',1);i=Ze[r],n++;}else i=qt;if(t.length>3){if(null!==t[2]&&(\"number\"!=typeof t[2]||t[2]<0||t[2]!==Math.floor(t[2])))return e.error('The length argument to \"array\" must be a positive integer literal',2);s=t[2],n++;}r=Wt(i,s);}else {if(!Ze[i])throw new Error(`Types doesn't contain name = ${i}`);r=Ze[i];}const s=[];for(;n<t.length;n++){const r=e.parse(t[n],n,qt);if(!r)return null;s.push(r);}return new Ye(r,s)}evaluate(t){for(let e=0;e<this.args.length;e++){const r=this.args[e].evaluate(t);if(!ee(this.type,qe(r)))return r;if(e===this.args.length-1)throw new $e(`Expected value to be of type ${Qt(this.type)}, but found ${Qt(qe(r))} instead.`)}throw new Error}eachChild(t){this.args.forEach(t);}outputDefined(){return this.args.every((t=>t.outputDefined()))}}const He={\"to-boolean\":Rt,\"to-color\":jt,\"to-number\":Dt,\"to-string\":Ot};class Ke{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error(\"Expected at least one argument.\");const r=t[0];if(!He[r])throw new Error(`Can't parse ${r} as it is not part of the known types`);if((\"to-boolean\"===r||\"to-string\"===r)&&2!==t.length)return e.error(\"Expected one argument.\");const n=He[r],i=[];for(let r=1;r<t.length;r++){const n=e.parse(t[r],r,qt);if(!n)return null;i.push(n);}return new Ke(n,i)}evaluate(t){switch(this.type.kind){case \"boolean\":return Boolean(this.args[0].evaluate(t));case \"color\":{let e,r;for(const n of this.args){if(e=n.evaluate(t),r=null,e instanceof Pe)return e;if(\"string\"==typeof e){const r=t.parseColor(e);if(r)return r}else if(Array.isArray(e)&&(r=e.length<3||e.length>4?`Invalid rgba value ${JSON.stringify(e)}: expected an array containing either three or four numeric values.`:Ne(e[0],e[1],e[2],e[3]),!r))return new Pe(e[0]/255,e[1]/255,e[2]/255,e[3])}throw new $e(r||`Could not parse color from value '${\"string\"==typeof e?e:JSON.stringify(e)}'`)}case \"padding\":{let e;for(const r of this.args){e=r.evaluate(t);const n=Ve.parse(e);if(n)return n}throw new $e(`Could not parse padding from value '${\"string\"==typeof e?e:JSON.stringify(e)}'`)}case \"numberArray\":{let e;for(const r of this.args){e=r.evaluate(t);const n=Fe.parse(e);if(n)return n}throw new $e(`Could not parse numberArray from value '${\"string\"==typeof e?e:JSON.stringify(e)}'`)}case \"colorArray\":{let e;for(const r of this.args){e=r.evaluate(t);const n=Le.parse(e);if(n)return n}throw new $e(`Could not parse colorArray from value '${\"string\"==typeof e?e:JSON.stringify(e)}'`)}case \"variableAnchorOffsetCollection\":{let e;for(const r of this.args){e=r.evaluate(t);const n=Oe.parse(e);if(n)return n}throw new $e(`Could not parse variableAnchorOffsetCollection from value '${\"string\"==typeof e?e:JSON.stringify(e)}'`)}case \"number\":{let e=null;for(const r of this.args){if(e=r.evaluate(t),null===e)return 0;const n=Number(e);if(!isNaN(n))return n}throw new $e(`Could not convert ${JSON.stringify(e)} to number.`)}case \"formatted\":return Te.fromString(Ge(this.args[0].evaluate(t)));case \"resolvedImage\":return Re.fromString(Ge(this.args[0].evaluate(t)));case \"projectionDefinition\":return this.args[0].evaluate(t);default:return Ge(this.args[0].evaluate(t))}}eachChild(t){this.args.forEach(t);}outputDefined(){return this.args.every((t=>t.outputDefined()))}}const Je=[\"Unknown\",\"Point\",\"LineString\",\"Polygon\"];class We{constructor(){this.globals=null,this.feature=null,this.featureState=null,this.formattedSection=null,this._parseColorCache=new Map,this.availableImages=null,this.canonical=null;}id(){return this.feature&&\"id\"in this.feature?this.feature.id:null}geometryType(){return this.feature?\"number\"==typeof this.feature.type?Je[this.feature.type]:this.feature.type:null}geometry(){return this.feature&&\"geometry\"in this.feature?this.feature.geometry:null}canonicalID(){return this.canonical}properties(){return this.feature&&this.feature.properties||{}}parseColor(t){let e=this._parseColorCache.get(t);return e||(e=Pe.parse(t),this._parseColorCache.set(t,e)),e}}class Qe{constructor(t,e,r=[],n,i=new Lt,s=[]){this.registry=t,this.path=r,this.key=r.map((t=>`[${t}]`)).join(\"\"),this.scope=i,this.errors=s,this.expectedType=n,this._isConstant=e;}parse(t,e,r,n,i={}){return e?this.concat(e,r,n)._parse(t,i):this._parse(t,i)}_parse(t,e){function r(t,e,r){return \"assert\"===r?new Ye(e,[t]):\"coerce\"===r?new Ke(e,[t]):t}if(null!==t&&\"string\"!=typeof t&&\"boolean\"!=typeof t&&\"number\"!=typeof t||(t=[\"literal\",t]),Array.isArray(t)){if(0===t.length)return this.error('Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].');const n=t[0];if(\"string\"!=typeof n)return this.error(`Expression name must be a string, but found ${typeof n} instead. If you wanted a literal array, use [\"literal\", [...]].`,0),null;const i=this.registry[n];if(i){let n=i.parse(t,this);if(!n)return null;if(this.expectedType){const t=this.expectedType,i=n.type;if(\"string\"!==t.kind&&\"number\"!==t.kind&&\"boolean\"!==t.kind&&\"object\"!==t.kind&&\"array\"!==t.kind||\"value\"!==i.kind){if(\"projectionDefinition\"===t.kind&&[\"string\",\"array\"].includes(i.kind)||[\"color\",\"formatted\",\"resolvedImage\"].includes(t.kind)&&[\"value\",\"string\"].includes(i.kind)||[\"padding\",\"numberArray\"].includes(t.kind)&&[\"value\",\"number\",\"array\"].includes(i.kind)||\"colorArray\"===t.kind&&[\"value\",\"string\",\"array\"].includes(i.kind)||\"variableAnchorOffsetCollection\"===t.kind&&[\"value\",\"array\"].includes(i.kind))n=r(n,t,e.typeAnnotation||\"coerce\");else if(this.checkSubtype(t,i))return null}else n=r(n,t,e.typeAnnotation||\"assert\");}if(!(n instanceof Xe)&&\"resolvedImage\"!==n.type.kind&&this._isConstant(n)){const t=new We;try{n=new Xe(n.type,n.evaluate(t));}catch(t){return this.error(t.message),null}}return n}return this.error(`Unknown expression \"${n}\". If you wanted a literal array, use [\"literal\", [...]].`,0)}return this.error(void 0===t?\"'undefined' value invalid. Use null instead.\":\"object\"==typeof t?'Bare objects invalid. Use [\"literal\", {...}] instead.':`Expected an array, but found ${typeof t} instead.`)}concat(t,e,r){const n=\"number\"==typeof t?this.path.concat(t):this.path,i=r?this.scope.concat(r):this.scope;return new Qe(this.registry,this._isConstant,n,e||null,i,this.errors)}error(t,...e){const r=`${this.key}${e.map((t=>`[${t}]`)).join(\"\")}`;this.errors.push(new Ft(r,t));}checkSubtype(t,e){const r=ee(t,e);return r&&this.error(r),r}}class tr{constructor(t,e){this.type=e.type,this.bindings=[].concat(t),this.result=e;}evaluate(t){return this.result.evaluate(t)}eachChild(t){for(const e of this.bindings)t(e[1]);t(this.result);}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found ${t.length-1} instead.`);const r=[];for(let n=1;n<t.length-1;n+=2){const i=t[n];if(\"string\"!=typeof i)return e.error(`Expected string, but found ${typeof i} instead.`,n);if(/[^a-zA-Z0-9_]/.test(i))return e.error(\"Variable names must contain only alphanumeric characters or '_'.\",n);const s=e.parse(t[n+1],n+1);if(!s)return null;r.push([i,s]);}const n=e.parse(t[t.length-1],t.length-1,e.expectedType,r);return n?new tr(r,n):null}outputDefined(){return this.result.outputDefined()}}class er{constructor(t,e){this.type=e.type,this.name=t,this.boundExpression=e;}static parse(t,e){if(2!==t.length||\"string\"!=typeof t[1])return e.error(\"'var' expression requires exactly one string literal argument.\");const r=t[1];return e.scope.has(r)?new er(r,e.scope.get(r)):e.error(`Unknown variable \"${r}\". Make sure \"${r}\" has been bound in an enclosing \"let\" expression before using it.`,1)}evaluate(t){return this.boundExpression.evaluate(t)}eachChild(){}outputDefined(){return !1}}class rr{constructor(t,e,r){this.type=t,this.index=e,this.input=r;}static parse(t,e){if(3!==t.length)return e.error(`Expected 2 arguments, but found ${t.length-1} instead.`);const r=e.parse(t[1],1,Dt),n=e.parse(t[2],2,Wt(e.expectedType||qt));return r&&n?new rr(n.type.itemType,r,n):null}evaluate(t){const e=this.index.evaluate(t),r=this.input.evaluate(t);if(e<0)throw new $e(`Array index out of bounds: ${e} < 0.`);if(e>=r.length)throw new $e(`Array index out of bounds: ${e} > ${r.length-1}.`);if(e!==Math.floor(e))throw new $e(`Array index must be an integer, but found ${e} instead.`);return r[e]}eachChild(t){t(this.index),t(this.input);}outputDefined(){return !1}}class nr{constructor(t,e){this.type=Rt,this.needle=t,this.haystack=e;}static parse(t,e){if(3!==t.length)return e.error(`Expected 2 arguments, but found ${t.length-1} instead.`);const r=e.parse(t[1],1,qt),n=e.parse(t[2],2,qt);return r&&n?re(r.type,[Rt,Ot,Dt,$t,qt])?new nr(r,n):e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(r.type)} instead`):null}evaluate(t){const e=this.needle.evaluate(t),r=this.haystack.evaluate(t);if(!r)return !1;if(!ne(e,[\"boolean\",\"string\",\"number\",\"null\"]))throw new $e(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(qe(e))} instead.`);if(!ne(r,[\"string\",\"array\"]))throw new $e(`Expected second argument to be of type array or string, but found ${Qt(qe(r))} instead.`);return r.indexOf(e)>=0}eachChild(t){t(this.needle),t(this.haystack);}outputDefined(){return !0}}class ir{constructor(t,e,r){this.type=Dt,this.needle=t,this.haystack=e,this.fromIndex=r;}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 3 or 4 arguments, but found ${t.length-1} instead.`);const r=e.parse(t[1],1,qt),n=e.parse(t[2],2,qt);if(!r||!n)return null;if(!re(r.type,[Rt,Ot,Dt,$t,qt]))return e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(r.type)} instead`);if(4===t.length){const i=e.parse(t[3],3,Dt);return i?new ir(r,n,i):null}return new ir(r,n)}evaluate(t){const e=this.needle.evaluate(t),r=this.haystack.evaluate(t);if(!ne(e,[\"boolean\",\"string\",\"number\",\"null\"]))throw new $e(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(qe(e))} instead.`);let n;if(this.fromIndex&&(n=this.fromIndex.evaluate(t)),ne(r,[\"string\"])){const t=r.indexOf(e,n);return -1===t?-1:[...r.slice(0,t)].length}if(ne(r,[\"array\"]))return r.indexOf(e,n);throw new $e(`Expected second argument to be of type array or string, but found ${Qt(qe(r))} instead.`)}eachChild(t){t(this.needle),t(this.haystack),this.fromIndex&&t(this.fromIndex);}outputDefined(){return !1}}class sr{constructor(t,e,r,n,i,s){this.inputType=t,this.type=e,this.input=r,this.cases=n,this.outputs=i,this.otherwise=s;}static parse(t,e){if(t.length<5)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if(t.length%2!=1)return e.error(\"Expected an even number of arguments.\");let r,n;e.expectedType&&\"value\"!==e.expectedType.kind&&(n=e.expectedType);const i={},s=[];for(let a=2;a<t.length-1;a+=2){let o=t[a];const l=t[a+1];Array.isArray(o)||(o=[o]);const u=e.concat(a);if(0===o.length)return u.error(\"Expected at least one branch label.\");for(const t of o){if(\"number\"!=typeof t&&\"string\"!=typeof t)return u.error(\"Branch labels must be numbers or strings.\");if(\"number\"==typeof t&&Math.abs(t)>Number.MAX_SAFE_INTEGER)return u.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);if(\"number\"==typeof t&&Math.floor(t)!==t)return u.error(\"Numeric branch labels must be integer values.\");if(r){if(u.checkSubtype(r,qe(t)))return null}else r=qe(t);if(void 0!==i[String(t)])return u.error(\"Branch labels must be unique.\");i[String(t)]=s.length;}const c=e.parse(l,a,n);if(!c)return null;n=n||c.type,s.push(c);}const a=e.parse(t[1],1,qt);if(!a)return null;const o=e.parse(t[t.length-1],t.length-1,n);return o?\"value\"!==a.type.kind&&e.concat(1).checkSubtype(r,a.type)?null:new sr(r,n,a,i,s,o):null}evaluate(t){const e=this.input.evaluate(t);return (qe(e)===this.inputType&&this.outputs[this.cases[e]]||this.otherwise).evaluate(t)}eachChild(t){t(this.input),this.outputs.forEach(t),t(this.otherwise);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))&&this.otherwise.outputDefined()}}class ar{constructor(t,e,r){this.type=t,this.branches=e,this.otherwise=r;}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found only ${t.length-1}.`);if(t.length%2!=0)return e.error(\"Expected an odd number of arguments.\");let r;e.expectedType&&\"value\"!==e.expectedType.kind&&(r=e.expectedType);const n=[];for(let i=1;i<t.length-1;i+=2){const s=e.parse(t[i],i,Rt);if(!s)return null;const a=e.parse(t[i+1],i+1,r);if(!a)return null;n.push([s,a]),r=r||a.type;}const i=e.parse(t[t.length-1],t.length-1,r);if(!i)return null;if(!r)throw new Error(\"Can't infer output type\");return new ar(r,n,i)}evaluate(t){for(const[e,r]of this.branches)if(e.evaluate(t))return r.evaluate(t);return this.otherwise.evaluate(t)}eachChild(t){for(const[e,r]of this.branches)t(e),t(r);t(this.otherwise);}outputDefined(){return this.branches.every((([t,e])=>e.outputDefined()))&&this.otherwise.outputDefined()}}class or{constructor(t,e,r,n){this.type=t,this.input=e,this.beginIndex=r,this.endIndex=n;}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 3 or 4 arguments, but found ${t.length-1} instead.`);const r=e.parse(t[1],1,qt),n=e.parse(t[2],2,Dt);if(!r||!n)return null;if(!re(r.type,[Wt(qt),Ot,qt]))return e.error(`Expected first argument to be of type array or string, but found ${Qt(r.type)} instead`);if(4===t.length){const i=e.parse(t[3],3,Dt);return i?new or(r.type,r,n,i):null}return new or(r.type,r,n)}evaluate(t){const e=this.input.evaluate(t),r=this.beginIndex.evaluate(t);let n;if(this.endIndex&&(n=this.endIndex.evaluate(t)),ne(e,[\"string\"]))return [...e].slice(r,n).join(\"\");if(ne(e,[\"array\"]))return e.slice(r,n);throw new $e(`Expected first argument to be of type array or string, but found ${Qt(qe(e))} instead.`)}eachChild(t){t(this.input),t(this.beginIndex),this.endIndex&&t(this.endIndex);}outputDefined(){return !1}}function lr(t,e){const r=t.length-1;let n,i,s=0,a=r,o=0;for(;s<=a;)if(o=Math.floor((s+a)/2),n=t[o],i=t[o+1],n<=e){if(o===r||e<i)return o;s=o+1;}else {if(!(n>e))throw new $e(\"Input is not a number.\");a=o-1;}return 0}class ur{constructor(t,e,r){this.type=t,this.input=e,this.labels=[],this.outputs=[];for(const[t,e]of r)this.labels.push(t),this.outputs.push(e);}static parse(t,e){if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error(\"Expected an even number of arguments.\");const r=e.parse(t[1],1,Dt);if(!r)return null;const n=[];let i=null;e.expectedType&&\"value\"!==e.expectedType.kind&&(i=e.expectedType);for(let r=1;r<t.length;r+=2){const s=1===r?-1/0:t[r],a=t[r+1],o=r,l=r+1;if(\"number\"!=typeof s)return e.error('Input/output pairs for \"step\" expressions must be defined using literal numeric values (not computed expressions) for the input values.',o);if(n.length&&n[n.length-1][0]>=s)return e.error('Input/output pairs for \"step\" expressions must be arranged with input values in strictly ascending order.',o);const u=e.parse(a,l,i);if(!u)return null;i=i||u.type,n.push([s,u]);}return new ur(i,r,n)}evaluate(t){const e=this.labels,r=this.outputs;if(1===e.length)return r[0].evaluate(t);const n=this.input.evaluate(t);if(n<=e[0])return r[0].evaluate(t);const i=e.length;return n>=e[i-1]?r[i-1].evaluate(t):r[lr(e,n)].evaluate(t)}eachChild(t){t(this.input);for(const e of this.outputs)t(e);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function cr(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,\"default\")?t.default:t}var hr,pr,fr=function(){if(pr)return hr;function t(t,e,r,n){this.cx=3*t,this.bx=3*(r-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(n-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=r,this.p2y=n;}return pr=1,hr=t,t.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var r=t,n=0;n<8;n++){var i=this.sampleCurveX(r)-t;if(Math.abs(i)<e)return r;var s=this.sampleCurveDerivativeX(r);if(Math.abs(s)<1e-6)break;r-=i/s;}var a=0,o=1;for(r=t,n=0;n<20&&(i=this.sampleCurveX(r),!(Math.abs(i-t)<e));n++)t>i?a=r:o=r,r=.5*(o-a)+a;return r},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},hr}(),dr=cr(fr);class yr{constructor(t,e,r,n,i){this.type=t,this.operator=e,this.interpolation=r,this.input=n,this.labels=[],this.outputs=[];for(const[t,e]of i)this.labels.push(t),this.outputs.push(e);}static interpolationFactor(t,e,r,n){let i=0;if(\"exponential\"===t.name)i=mr(e,t.base,r,n);else if(\"linear\"===t.name)i=mr(e,1,r,n);else if(\"cubic-bezier\"===t.name){const s=t.controlPoints;i=new dr(s[0],s[1],s[2],s[3]).solve(mr(e,1,r,n));}return i}static parse(t,e){let[r,n,i,...s]=t;if(!Array.isArray(n)||0===n.length)return e.error(\"Expected an interpolation type expression.\",1);if(\"linear\"===n[0])n={name:\"linear\"};else if(\"exponential\"===n[0]){const t=n[1];if(\"number\"!=typeof t)return e.error(\"Exponential interpolation requires a numeric base.\",1,1);n={name:\"exponential\",base:t};}else {if(\"cubic-bezier\"!==n[0])return e.error(`Unknown interpolation type ${String(n[0])}`,1,0);{const t=n.slice(1);if(4!==t.length||t.some((t=>\"number\"!=typeof t||t<0||t>1)))return e.error(\"Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.\",1);n={name:\"cubic-bezier\",controlPoints:t};}}if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error(\"Expected an even number of arguments.\");if(i=e.parse(i,2,Dt),!i)return null;const a=[];let o=null;\"interpolate-hcl\"!==r&&\"interpolate-lab\"!==r||e.expectedType==Yt?e.expectedType&&\"value\"!==e.expectedType.kind&&(o=e.expectedType):o=jt;for(let t=0;t<s.length;t+=2){const r=s[t],n=s[t+1],i=t+3,l=t+4;if(\"number\"!=typeof r)return e.error('Input/output pairs for \"interpolate\" expressions must be defined using literal numeric values (not computed expressions) for the input values.',i);if(a.length&&a[a.length-1][0]>=r)return e.error('Input/output pairs for \"interpolate\" expressions must be arranged with input values in strictly ascending order.',i);const u=e.parse(n,l,o);if(!u)return null;o=o||u.type,a.push([r,u]);}return ie(o,Dt)||ie(o,Nt)||ie(o,jt)||ie(o,Zt)||ie(o,Ht)||ie(o,Yt)||ie(o,Jt)||ie(o,Wt(Dt))?new yr(o,r,n,i,a):e.error(`Type ${Qt(o)} is not interpolatable.`)}evaluate(t){const e=this.labels,r=this.outputs;if(1===e.length)return r[0].evaluate(t);const n=this.input.evaluate(t);if(n<=e[0])return r[0].evaluate(t);const i=e.length;if(n>=e[i-1])return r[i-1].evaluate(t);const s=lr(e,n),a=yr.interpolationFactor(this.interpolation,n,e[s],e[s+1]),o=r[s].evaluate(t),l=r[s+1].evaluate(t);switch(this.operator){case \"interpolate\":switch(this.type.kind){case \"number\":return Ie(o,l,a);case \"color\":return Pe.interpolate(o,l,a);case \"padding\":return Ve.interpolate(o,l,a);case \"colorArray\":return Le.interpolate(o,l,a);case \"numberArray\":return Fe.interpolate(o,l,a);case \"variableAnchorOffsetCollection\":return Oe.interpolate(o,l,a);case \"array\":return ze(o,l,a);case \"projectionDefinition\":return je.interpolate(o,l,a)}case \"interpolate-hcl\":switch(this.type.kind){case \"color\":return Pe.interpolate(o,l,a,\"hcl\");case \"colorArray\":return Le.interpolate(o,l,a,\"hcl\")}case \"interpolate-lab\":switch(this.type.kind){case \"color\":return Pe.interpolate(o,l,a,\"lab\");case \"colorArray\":return Le.interpolate(o,l,a,\"lab\")}}}eachChild(t){t(this.input);for(const e of this.outputs)t(e);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function mr(t,e,r,n){const i=n-r,s=t-r;return 0===i?0:1===e?s/i:(Math.pow(e,s)-1)/(Math.pow(e,i)-1)}const gr={color:Pe.interpolate,number:Ie,padding:Ve.interpolate,numberArray:Fe.interpolate,colorArray:Le.interpolate,variableAnchorOffsetCollection:Oe.interpolate,array:ze};class xr{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error(\"Expected at least one argument.\");let r=null;const n=e.expectedType;n&&\"value\"!==n.kind&&(r=n);const i=[];for(const n of t.slice(1)){const t=e.parse(n,1+i.length,r,void 0,{typeAnnotation:\"omit\"});if(!t)return null;r=r||t.type,i.push(t);}if(!r)throw new Error(\"No output type\");const s=n&&i.some((t=>ee(n,t.type)));return new xr(s?qt:r,i)}evaluate(t){let e,r=null,n=0;for(const i of this.args)if(n++,r=i.evaluate(t),r&&r instanceof Re&&!r.available&&(e||(e=r.name),r=null,n===this.args.length&&(r=e)),null!==r)break;return r}eachChild(t){this.args.forEach(t);}outputDefined(){return this.args.every((t=>t.outputDefined()))}}function vr(t,e){return \"==\"===t||\"!=\"===t?\"boolean\"===e.kind||\"string\"===e.kind||\"number\"===e.kind||\"null\"===e.kind||\"value\"===e.kind:\"string\"===e.kind||\"number\"===e.kind||\"value\"===e.kind}function br(t,e,r,n){return 0===n.compare(e,r)}function wr(t,e,r){const n=\"==\"!==t&&\"!=\"!==t;return class i{constructor(t,e,r){this.type=Rt,this.lhs=t,this.rhs=e,this.collator=r,this.hasUntypedArgument=\"value\"===t.type.kind||\"value\"===e.type.kind;}static parse(t,e){if(3!==t.length&&4!==t.length)return e.error(\"Expected two or three arguments.\");const r=t[0];let s=e.parse(t[1],1,qt);if(!s)return null;if(!vr(r,s.type))return e.concat(1).error(`\"${r}\" comparisons are not supported for type '${Qt(s.type)}'.`);let a=e.parse(t[2],2,qt);if(!a)return null;if(!vr(r,a.type))return e.concat(2).error(`\"${r}\" comparisons are not supported for type '${Qt(a.type)}'.`);if(s.type.kind!==a.type.kind&&\"value\"!==s.type.kind&&\"value\"!==a.type.kind)return e.error(`Cannot compare types '${Qt(s.type)}' and '${Qt(a.type)}'.`);n&&(\"value\"===s.type.kind&&\"value\"!==a.type.kind?s=new Ye(a.type,[s]):\"value\"!==s.type.kind&&\"value\"===a.type.kind&&(a=new Ye(s.type,[a])));let o=null;if(4===t.length){if(\"string\"!==s.type.kind&&\"string\"!==a.type.kind&&\"value\"!==s.type.kind&&\"value\"!==a.type.kind)return e.error(\"Cannot use collator to compare non-string types.\");if(o=e.parse(t[3],3,Gt),!o)return null}return new i(s,a,o)}evaluate(i){const s=this.lhs.evaluate(i),a=this.rhs.evaluate(i);if(n&&this.hasUntypedArgument){const e=qe(s),r=qe(a);if(e.kind!==r.kind||\"string\"!==e.kind&&\"number\"!==e.kind)throw new $e(`Expected arguments for \"${t}\" to be (string, string) or (number, number), but found (${e.kind}, ${r.kind}) instead.`)}if(this.collator&&!n&&this.hasUntypedArgument){const t=qe(s),r=qe(a);if(\"string\"!==t.kind||\"string\"!==r.kind)return e(i,s,a)}return this.collator?r(i,s,a,this.collator.evaluate(i)):e(i,s,a)}eachChild(t){t(this.lhs),t(this.rhs),this.collator&&t(this.collator);}outputDefined(){return !0}}}const _r=wr(\"==\",(function(t,e,r){return e===r}),br),Sr=wr(\"!=\",(function(t,e,r){return e!==r}),(function(t,e,r,n){return !br(0,e,r,n)})),Ar=wr(\"<\",(function(t,e,r){return e<r}),(function(t,e,r,n){return n.compare(e,r)<0})),kr=wr(\">\",(function(t,e,r){return e>r}),(function(t,e,r,n){return n.compare(e,r)>0})),Mr=wr(\"<=\",(function(t,e,r){return e<=r}),(function(t,e,r,n){return n.compare(e,r)<=0})),Ir=wr(\">=\",(function(t,e,r){return e>=r}),(function(t,e,r,n){return n.compare(e,r)>=0}));class zr{constructor(t,e,r){this.type=Gt,this.locale=r,this.caseSensitive=t,this.diacriticSensitive=e;}static parse(t,e){if(2!==t.length)return e.error(\"Expected one argument.\");const r=t[1];if(\"object\"!=typeof r||Array.isArray(r))return e.error(\"Collator options argument must be an object.\");const n=e.parse(void 0!==r[\"case-sensitive\"]&&r[\"case-sensitive\"],1,Rt);if(!n)return null;const i=e.parse(void 0!==r[\"diacritic-sensitive\"]&&r[\"diacritic-sensitive\"],1,Rt);if(!i)return null;let s=null;return r.locale&&(s=e.parse(r.locale,1,Ot),!s)?null:new zr(n,i,s)}evaluate(t){return new Ce(this.caseSensitive.evaluate(t),this.diacriticSensitive.evaluate(t),this.locale?this.locale.evaluate(t):null)}eachChild(t){t(this.caseSensitive),t(this.diacriticSensitive),this.locale&&t(this.locale);}outputDefined(){return !1}}class Pr{constructor(t,e,r,n,i){this.type=Ot,this.number=t,this.locale=e,this.currency=r,this.minFractionDigits=n,this.maxFractionDigits=i;}static parse(t,e){if(3!==t.length)return e.error(\"Expected two arguments.\");const r=e.parse(t[1],1,Dt);if(!r)return null;const n=t[2];if(\"object\"!=typeof n||Array.isArray(n))return e.error(\"NumberFormat options argument must be an object.\");let i=null;if(n.locale&&(i=e.parse(n.locale,1,Ot),!i))return null;let s=null;if(n.currency&&(s=e.parse(n.currency,1,Ot),!s))return null;let a=null;if(n[\"min-fraction-digits\"]&&(a=e.parse(n[\"min-fraction-digits\"],1,Dt),!a))return null;let o=null;return n[\"max-fraction-digits\"]&&(o=e.parse(n[\"max-fraction-digits\"],1,Dt),!o)?null:new Pr(r,i,s,a,o)}evaluate(t){return new Intl.NumberFormat(this.locale?this.locale.evaluate(t):[],{style:this.currency?\"currency\":\"decimal\",currency:this.currency?this.currency.evaluate(t):void 0,minimumFractionDigits:this.minFractionDigits?this.minFractionDigits.evaluate(t):void 0,maximumFractionDigits:this.maxFractionDigits?this.maxFractionDigits.evaluate(t):void 0}).format(this.number.evaluate(t))}eachChild(t){t(this.number),this.locale&&t(this.locale),this.currency&&t(this.currency),this.minFractionDigits&&t(this.minFractionDigits),this.maxFractionDigits&&t(this.maxFractionDigits);}outputDefined(){return !1}}class Cr{constructor(t){this.type=Xt,this.sections=t;}static parse(t,e){if(t.length<2)return e.error(\"Expected at least one argument.\");const r=t[1];if(!Array.isArray(r)&&\"object\"==typeof r)return e.error(\"First argument must be an image or text section.\");const n=[];let i=!1;for(let r=1;r<=t.length-1;++r){const s=t[r];if(i&&\"object\"==typeof s&&!Array.isArray(s)){i=!1;let t=null;if(s[\"font-scale\"]&&(t=e.parse(s[\"font-scale\"],1,Dt),!t))return null;let r=null;if(s[\"text-font\"]&&(r=e.parse(s[\"text-font\"],1,Wt(Ot)),!r))return null;let a=null;if(s[\"text-color\"]&&(a=e.parse(s[\"text-color\"],1,jt),!a))return null;let o=null;if(s[\"vertical-align\"]){if(\"string\"==typeof s[\"vertical-align\"]&&!Be.includes(s[\"vertical-align\"]))return e.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${s[\"vertical-align\"]}' instead.`);if(o=e.parse(s[\"vertical-align\"],1,Ot),!o)return null}const l=n[n.length-1];l.scale=t,l.font=r,l.textColor=a,l.verticalAlign=o;}else {const s=e.parse(t[r],1,qt);if(!s)return null;const a=s.type.kind;if(\"string\"!==a&&\"value\"!==a&&\"null\"!==a&&\"resolvedImage\"!==a)return e.error(\"Formatted text type must be 'string', 'value', 'image' or 'null'.\");i=!0,n.push({content:s,scale:null,font:null,textColor:null,verticalAlign:null});}}return new Cr(n)}evaluate(t){return new Te(this.sections.map((e=>{const r=e.content.evaluate(t);return qe(r)===Kt?new Ee(\"\",r,null,null,null,e.verticalAlign?e.verticalAlign.evaluate(t):null):new Ee(Ge(r),null,e.scale?e.scale.evaluate(t):null,e.font?e.font.evaluate(t).join(\",\"):null,e.textColor?e.textColor.evaluate(t):null,e.verticalAlign?e.verticalAlign.evaluate(t):null)})))}eachChild(t){for(const e of this.sections)t(e.content),e.scale&&t(e.scale),e.font&&t(e.font),e.textColor&&t(e.textColor),e.verticalAlign&&t(e.verticalAlign);}outputDefined(){return !1}}class Br{constructor(t){this.type=Kt,this.input=t;}static parse(t,e){if(2!==t.length)return e.error(\"Expected two arguments.\");const r=e.parse(t[1],1,Ot);return r?new Br(r):e.error(\"No image name provided.\")}evaluate(t){const e=this.input.evaluate(t),r=Re.fromString(e);return r&&t.availableImages&&(r.available=t.availableImages.indexOf(e)>-1),r}eachChild(t){t(this.input);}outputDefined(){return !1}}class Er{constructor(t){this.type=Dt,this.input=t;}static parse(t,e){if(2!==t.length)return e.error(`Expected 1 argument, but found ${t.length-1} instead.`);const r=e.parse(t[1],1);return r?\"array\"!==r.type.kind&&\"string\"!==r.type.kind&&\"value\"!==r.type.kind?e.error(`Expected argument of type string or array, but found ${Qt(r.type)} instead.`):new Er(r):null}evaluate(t){const e=this.input.evaluate(t);if(\"string\"==typeof e)return [...e].length;if(Array.isArray(e))return e.length;throw new $e(`Expected value to be of type string or array, but found ${Qt(qe(e))} instead.`)}eachChild(t){t(this.input);}outputDefined(){return !1}}const Tr=8192;function Vr(t,e){const r=(180+t[0])/360,n=(180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t[1]*Math.PI/360)))/360,i=Math.pow(2,e.z);return [Math.round(r*i*Tr),Math.round(n*i*Tr)]}function Fr(t,e){const r=Math.pow(2,e.z);return [(i=(t[0]/Tr+e.x)/r,360*i-180),(n=(t[1]/Tr+e.y)/r,360/Math.PI*Math.atan(Math.exp((180-360*n)*Math.PI/180))-90)];var n,i;}function Lr(t,e){t[0]=Math.min(t[0],e[0]),t[1]=Math.min(t[1],e[1]),t[2]=Math.max(t[2],e[0]),t[3]=Math.max(t[3],e[1]);}function $r(t,e){return !(t[0]<=e[0]||t[2]>=e[2]||t[1]<=e[1]||t[3]>=e[3])}function Dr(t,e,r){const n=t[0]-e[0],i=t[1]-e[1],s=t[0]-r[0],a=t[1]-r[1];return n*a-s*i==0&&n*s<=0&&i*a<=0}function Or(t,e,r,n){return 0!=(i=[n[0]-r[0],n[1]-r[1]])[0]*(s=[e[0]-t[0],e[1]-t[1]])[1]-i[1]*s[0]&&!(!Gr(t,e,r,n)||!Gr(r,n,t,e));var i,s;}function Rr(t,e,r){for(const n of r)for(let r=0;r<n.length-1;++r)if(Or(t,e,n[r],n[r+1]))return !0;return !1}function jr(t,e,r=!1){let n=!1;for(const o of e)for(let e=0;e<o.length-1;e++){if(Dr(t,o[e],o[e+1]))return r;(s=o[e])[1]>(i=t)[1]!=(a=o[e+1])[1]>i[1]&&i[0]<(a[0]-s[0])*(i[1]-s[1])/(a[1]-s[1])+s[0]&&(n=!n);}var i,s,a;return n}function Nr(t,e){for(const r of e)if(jr(t,r))return !0;return !1}function Ur(t,e){for(const r of t)if(!jr(r,e))return !1;for(let r=0;r<t.length-1;++r)if(Rr(t[r],t[r+1],e))return !1;return !0}function qr(t,e){for(const r of e)if(Ur(t,r))return !0;return !1}function Gr(t,e,r,n){const i=n[0]-r[0],s=n[1]-r[1],a=(t[0]-r[0])*s-i*(t[1]-r[1]),o=(e[0]-r[0])*s-i*(e[1]-r[1]);return a>0&&o<0||a<0&&o>0}function Xr(t,e,r){const n=[];for(let i=0;i<t.length;i++){const s=[];for(let n=0;n<t[i].length;n++){const a=Vr(t[i][n],r);Lr(e,a),s.push(a);}n.push(s);}return n}function Zr(t,e,r){const n=[];for(let i=0;i<t.length;i++){const s=Xr(t[i],e,r);n.push(s);}return n}function Yr(t,e,r,n){if(t[0]<r[0]||t[0]>r[2]){const e=.5*n;let i=t[0]-r[0]>e?-n:r[0]-t[0]>e?n:0;0===i&&(i=t[0]-r[2]>e?-n:r[2]-t[0]>e?n:0),t[0]+=i;}Lr(e,t);}function Hr(t,e,r,n){const i=Math.pow(2,n.z)*Tr,s=[n.x*Tr,n.y*Tr],a=[];for(const n of t)for(const t of n){const n=[t.x+s[0],t.y+s[1]];Yr(n,e,r,i),a.push(n);}return a}function Kr(t,e,r,n){const i=Math.pow(2,n.z)*Tr,s=[n.x*Tr,n.y*Tr],a=[];for(const r of t){const t=[];for(const n of r){const r=[n.x+s[0],n.y+s[1]];Lr(e,r),t.push(r);}a.push(t);}if(e[2]-e[0]<=i/2){(o=e)[0]=o[1]=1/0,o[2]=o[3]=-1/0;for(const t of a)for(const n of t)Yr(n,e,r,i);}var o;return a}class Jr{constructor(t,e){this.type=Rt,this.geojson=t,this.geometries=e;}static parse(t,e){if(2!==t.length)return e.error(`'within' expression requires exactly one argument, but found ${t.length-1} instead.`);if(Ue(t[1])){const e=t[1];if(\"FeatureCollection\"===e.type){const t=[];for(const r of e.features){const{type:e,coordinates:n}=r.geometry;\"Polygon\"===e&&t.push(n),\"MultiPolygon\"===e&&t.push(...n);}if(t.length)return new Jr(e,{type:\"MultiPolygon\",coordinates:t})}else if(\"Feature\"===e.type){const t=e.geometry.type;if(\"Polygon\"===t||\"MultiPolygon\"===t)return new Jr(e,e.geometry)}else if(\"Polygon\"===e.type||\"MultiPolygon\"===e.type)return new Jr(e,e)}return e.error(\"'within' expression requires valid geojson object that contains polygon geometry type.\")}evaluate(t){if(null!=t.geometry()&&null!=t.canonicalID()){if(\"Point\"===t.geometryType())return function(t,e){const r=[1/0,1/0,-1/0,-1/0],n=[1/0,1/0,-1/0,-1/0],i=t.canonicalID();if(\"Polygon\"===e.type){const s=Xr(e.coordinates,n,i),a=Hr(t.geometry(),r,n,i);if(!$r(r,n))return !1;for(const t of a)if(!jr(t,s))return !1}if(\"MultiPolygon\"===e.type){const s=Zr(e.coordinates,n,i),a=Hr(t.geometry(),r,n,i);if(!$r(r,n))return !1;for(const t of a)if(!Nr(t,s))return !1}return !0}(t,this.geometries);if(\"LineString\"===t.geometryType())return function(t,e){const r=[1/0,1/0,-1/0,-1/0],n=[1/0,1/0,-1/0,-1/0],i=t.canonicalID();if(\"Polygon\"===e.type){const s=Xr(e.coordinates,n,i),a=Kr(t.geometry(),r,n,i);if(!$r(r,n))return !1;for(const t of a)if(!Ur(t,s))return !1}if(\"MultiPolygon\"===e.type){const s=Zr(e.coordinates,n,i),a=Kr(t.geometry(),r,n,i);if(!$r(r,n))return !1;for(const t of a)if(!qr(t,s))return !1}return !0}(t,this.geometries)}return !1}eachChild(){}outputDefined(){return !0}}let Wr=class{constructor(t=[],e=(t,e)=>t<e?-1:t>e?1:0){if(this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t);}push(t){this.data.push(t),this._up(this.length++);}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return --this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:r}=this,n=e[t];for(;t>0;){const i=t-1>>1,s=e[i];if(r(n,s)>=0)break;e[t]=s,t=i;}e[t]=n;}_down(t){const{data:e,compare:r}=this,n=this.length>>1,i=e[t];for(;t<n;){let n=1+(t<<1);const s=n+1;if(s<this.length&&r(e[s],e[n])<0&&(n=s),r(e[n],i)>=0)break;e[t]=e[n],t=n;}e[t]=i;}};function Qr(t,e,r=0,n=t.length-1,i=en){for(;n>r;){if(n-r>600){const s=n-r+1,a=e-r+1,o=Math.log(s),l=.5*Math.exp(2*o/3),u=.5*Math.sqrt(o*l*(s-l)/s)*(a-s/2<0?-1:1);Qr(t,e,Math.max(r,Math.floor(e-a*l/s+u)),Math.min(n,Math.floor(e+(s-a)*l/s+u)),i);}const s=t[e];let a=r,o=n;for(tn(t,r,e),i(t[n],s)>0&&tn(t,r,n);a<o;){for(tn(t,a,o),a++,o--;i(t[a],s)<0;)a++;for(;i(t[o],s)>0;)o--;}0===i(t[r],s)?tn(t,r,o):(o++,tn(t,o,n)),o<=e&&(r=o+1),e<=o&&(n=o-1);}}function tn(t,e,r){const n=t[e];t[e]=t[r],t[r]=n;}function en(t,e){return t<e?-1:t>e?1:0}function rn(t,e){if(t.length<=1)return [t];const r=[];let n,i;for(const e of t){const t=sn(e);0!==t&&(e.area=Math.abs(t),void 0===i&&(i=t<0),i===t<0?(n&&r.push(n),n=[e]):n.push(e));}if(n&&r.push(n),e>1)for(let t=0;t<r.length;t++)r[t].length<=e||(Qr(r[t],e,1,r[t].length-1,nn),r[t]=r[t].slice(0,e));return r}function nn(t,e){return e.area-t.area}function sn(t){let e=0;for(let r,n,i=0,s=t.length,a=s-1;i<s;a=i++)r=t[i],n=t[a],e+=(n.x-r.x)*(r.y+n.y);return e}const an=1/298.257223563,on=an*(2-an),ln=Math.PI/180;class un{constructor(t){const e=6378.137*ln*1e3,r=Math.cos(t*ln),n=1/(1-on*(1-r*r)),i=Math.sqrt(n);this.kx=e*i*r,this.ky=e*i*n*(1-on);}distance(t,e){const r=this.wrap(t[0]-e[0])*this.kx,n=(t[1]-e[1])*this.ky;return Math.sqrt(r*r+n*n)}pointOnLine(t,e){let r,n,i,s,a=1/0;for(let o=0;o<t.length-1;o++){let l=t[o][0],u=t[o][1],c=this.wrap(t[o+1][0]-l)*this.kx,h=(t[o+1][1]-u)*this.ky,p=0;0===c&&0===h||(p=(this.wrap(e[0]-l)*this.kx*c+(e[1]-u)*this.ky*h)/(c*c+h*h),p>1?(l=t[o+1][0],u=t[o+1][1]):p>0&&(l+=c/this.kx*p,u+=h/this.ky*p)),c=this.wrap(e[0]-l)*this.kx,h=(e[1]-u)*this.ky;const f=c*c+h*h;f<a&&(a=f,r=l,n=u,i=o,s=p);}return {point:[r,n],index:i,t:Math.max(0,Math.min(1,s))}}wrap(t){for(;t<-180;)t+=360;for(;t>180;)t-=360;return t}}function cn(t,e){return e[0]-t[0]}function hn(t){return t[1]-t[0]+1}function pn(t,e){return t[1]>=t[0]&&t[1]<e}function fn(t,e){if(t[0]>t[1])return [null,null];const r=hn(t);if(e){if(2===r)return [t,null];const e=Math.floor(r/2);return [[t[0],t[0]+e],[t[0]+e,t[1]]]}if(1===r)return [t,null];const n=Math.floor(r/2)-1;return [[t[0],t[0]+n],[t[0]+n+1,t[1]]]}function dn(t,e){if(!pn(e,t.length))return [1/0,1/0,-1/0,-1/0];const r=[1/0,1/0,-1/0,-1/0];for(let n=e[0];n<=e[1];++n)Lr(r,t[n]);return r}function yn(t){const e=[1/0,1/0,-1/0,-1/0];for(const r of t)for(const t of r)Lr(e,t);return e}function mn(t){return t[0]!==-1/0&&t[1]!==-1/0&&t[2]!==1/0&&t[3]!==1/0}function gn(t,e,r){if(!mn(t)||!mn(e))return NaN;let n=0,i=0;return t[2]<e[0]&&(n=e[0]-t[2]),t[0]>e[2]&&(n=t[0]-e[2]),t[1]>e[3]&&(i=t[1]-e[3]),t[3]<e[1]&&(i=e[1]-t[3]),r.distance([0,0],[n,i])}function xn(t,e,r){const n=r.pointOnLine(e,t);return r.distance(t,n.point)}function vn(t,e,r,n,i){const s=Math.min(xn(t,[r,n],i),xn(e,[r,n],i)),a=Math.min(xn(r,[t,e],i),xn(n,[t,e],i));return Math.min(s,a)}function bn(t,e,r,n,i){if(!pn(e,t.length)||!pn(n,r.length))return 1/0;let s=1/0;for(let a=e[0];a<e[1];++a){const e=t[a],o=t[a+1];for(let t=n[0];t<n[1];++t){const n=r[t],a=r[t+1];if(Or(e,o,n,a))return 0;s=Math.min(s,vn(e,o,n,a,i));}}return s}function wn(t,e,r,n,i){if(!pn(e,t.length)||!pn(n,r.length))return NaN;let s=1/0;for(let a=e[0];a<=e[1];++a)for(let e=n[0];e<=n[1];++e)if(s=Math.min(s,i.distance(t[a],r[e])),0===s)return s;return s}function _n(t,e,r){if(jr(t,e,!0))return 0;let n=1/0;for(const i of e){const e=i[0],s=i[i.length-1];if(e!==s&&(n=Math.min(n,xn(t,[s,e],r)),0===n))return n;const a=r.pointOnLine(i,t);if(n=Math.min(n,r.distance(t,a.point)),0===n)return n}return n}function Sn(t,e,r,n){if(!pn(e,t.length))return NaN;for(let n=e[0];n<=e[1];++n)if(jr(t[n],r,!0))return 0;let i=1/0;for(let s=e[0];s<e[1];++s){const e=t[s],a=t[s+1];for(const t of r)for(let r=0,s=t.length,o=s-1;r<s;o=r++){const s=t[o],l=t[r];if(Or(e,a,s,l))return 0;i=Math.min(i,vn(e,a,s,l,n));}}return i}function An(t,e){for(const r of t)for(const t of r)if(jr(t,e,!0))return !0;return !1}function kn(t,e,r,n=1/0){const i=yn(t),s=yn(e);if(n!==1/0&&gn(i,s,r)>=n)return n;if($r(i,s)){if(An(t,e))return 0}else if(An(e,t))return 0;let a=1/0;for(const n of t)for(let t=0,i=n.length,s=i-1;t<i;s=t++){const i=n[s],o=n[t];for(const t of e)for(let e=0,n=t.length,s=n-1;e<n;s=e++){const n=t[s],l=t[e];if(Or(i,o,n,l))return 0;a=Math.min(a,vn(i,o,n,l,r));}}return a}function Mn(t,e,r,n,i,s){if(!s)return;const a=gn(dn(n,s),i,r);a<e&&t.push([a,s,[0,0]]);}function In(t,e,r,n,i,s,a){if(!s||!a)return;const o=gn(dn(n,s),dn(i,a),r);o<e&&t.push([o,s,a]);}function zn(t,e,r,n,i=1/0){let s=Math.min(n.distance(t[0],r[0][0]),i);if(0===s)return s;const a=new Wr([[0,[0,t.length-1],[0,0]]],cn),o=yn(r);for(;a.length>0;){const i=a.pop();if(i[0]>=s)continue;const l=i[1],u=e?50:100;if(hn(l)<=u){if(!pn(l,t.length))return NaN;if(e){const e=Sn(t,l,r,n);if(isNaN(e)||0===e)return e;s=Math.min(s,e);}else for(let e=l[0];e<=l[1];++e){const i=_n(t[e],r,n);if(s=Math.min(s,i),0===s)return 0}}else {const r=fn(l,e);Mn(a,s,n,t,o,r[0]),Mn(a,s,n,t,o,r[1]);}}return s}function Pn(t,e,r,n,i,s=1/0){let a=Math.min(s,i.distance(t[0],r[0]));if(0===a)return a;const o=new Wr([[0,[0,t.length-1],[0,r.length-1]]],cn);for(;o.length>0;){const s=o.pop();if(s[0]>=a)continue;const l=s[1],u=s[2],c=e?50:100,h=n?50:100;if(hn(l)<=c&&hn(u)<=h){if(!pn(l,t.length)&&pn(u,r.length))return NaN;let s;if(e&&n)s=bn(t,l,r,u,i),a=Math.min(a,s);else if(e&&!n){const e=t.slice(l[0],l[1]+1);for(let t=u[0];t<=u[1];++t)if(s=xn(r[t],e,i),a=Math.min(a,s),0===a)return a}else if(!e&&n){const e=r.slice(u[0],u[1]+1);for(let r=l[0];r<=l[1];++r)if(s=xn(t[r],e,i),a=Math.min(a,s),0===a)return a}else s=wn(t,l,r,u,i),a=Math.min(a,s);}else {const s=fn(l,e),c=fn(u,n);In(o,a,i,t,r,s[0],c[0]),In(o,a,i,t,r,s[0],c[1]),In(o,a,i,t,r,s[1],c[0]),In(o,a,i,t,r,s[1],c[1]);}}return a}function Cn(t){return \"MultiPolygon\"===t.type?t.coordinates.map((t=>({type:\"Polygon\",coordinates:t}))):\"MultiLineString\"===t.type?t.coordinates.map((t=>({type:\"LineString\",coordinates:t}))):\"MultiPoint\"===t.type?t.coordinates.map((t=>({type:\"Point\",coordinates:t}))):[t]}class Bn{constructor(t,e){this.type=Dt,this.geojson=t,this.geometries=e;}static parse(t,e){if(2!==t.length)return e.error(`'distance' expression requires exactly one argument, but found ${t.length-1} instead.`);if(Ue(t[1])){const e=t[1];if(\"FeatureCollection\"===e.type)return new Bn(e,e.features.map((t=>Cn(t.geometry))).flat());if(\"Feature\"===e.type)return new Bn(e,Cn(e.geometry));if(\"type\"in e&&\"coordinates\"in e)return new Bn(e,Cn(e))}return e.error(\"'distance' expression requires valid geojson object that contains polygon geometry type.\")}evaluate(t){if(null!=t.geometry()&&null!=t.canonicalID()){if(\"Point\"===t.geometryType())return function(t,e){const r=t.geometry(),n=r.flat().map((e=>Fr([e.x,e.y],t.canonical)));if(0===r.length)return NaN;const i=new un(n[0][1]);let s=1/0;for(const t of e){switch(t.type){case \"Point\":s=Math.min(s,Pn(n,!1,[t.coordinates],!1,i,s));break;case \"LineString\":s=Math.min(s,Pn(n,!1,t.coordinates,!0,i,s));break;case \"Polygon\":s=Math.min(s,zn(n,!1,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries);if(\"LineString\"===t.geometryType())return function(t,e){const r=t.geometry(),n=r.flat().map((e=>Fr([e.x,e.y],t.canonical)));if(0===r.length)return NaN;const i=new un(n[0][1]);let s=1/0;for(const t of e){switch(t.type){case \"Point\":s=Math.min(s,Pn(n,!0,[t.coordinates],!1,i,s));break;case \"LineString\":s=Math.min(s,Pn(n,!0,t.coordinates,!0,i,s));break;case \"Polygon\":s=Math.min(s,zn(n,!0,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries);if(\"Polygon\"===t.geometryType())return function(t,e){const r=t.geometry();if(0===r.length||0===r[0].length)return NaN;const n=rn(r,0).map((e=>e.map((e=>e.map((e=>Fr([e.x,e.y],t.canonical))))))),i=new un(n[0][0][0][1]);let s=1/0;for(const t of e)for(const e of n){switch(t.type){case \"Point\":s=Math.min(s,zn([t.coordinates],!1,e,i,s));break;case \"LineString\":s=Math.min(s,zn(t.coordinates,!0,e,i,s));break;case \"Polygon\":s=Math.min(s,kn(e,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries)}return NaN}eachChild(){}outputDefined(){return !0}}class En{constructor(t){this.type=qt,this.key=t;}static parse(t,e){if(2!==t.length)return e.error(`Expected 1 argument, but found ${t.length-1} instead.`);const r=t[1];return null==r?e.error(\"Global state property must be defined.\"):\"string\"!=typeof r?e.error(`Global state property must be string, but found ${typeof t[1]} instead.`):new En(r)}evaluate(t){var e;const r=null===(e=t.globals)||void 0===e?void 0:e.globalState;return r&&0!==Object.keys(r).length?we(r,this.key):null}eachChild(){}outputDefined(){return !1}}const Tn={\"==\":_r,\"!=\":Sr,\">\":kr,\"<\":Ar,\">=\":Ir,\"<=\":Mr,array:Ye,at:rr,boolean:Ye,case:ar,coalesce:xr,collator:zr,format:Cr,image:Br,in:nr,\"index-of\":ir,interpolate:yr,\"interpolate-hcl\":yr,\"interpolate-lab\":yr,length:Er,let:tr,literal:Xe,match:sr,number:Ye,\"number-format\":Pr,object:Ye,slice:or,step:ur,string:Ye,\"to-boolean\":Ke,\"to-color\":Ke,\"to-number\":Ke,\"to-string\":Ke,var:er,within:Jr,distance:Bn,\"global-state\":En};class Vn{constructor(t,e,r,n){this.name=t,this.type=e,this._evaluate=r,this.args=n;}evaluate(t){return this._evaluate(t,this.args)}eachChild(t){this.args.forEach(t);}outputDefined(){return !1}static parse(t,e){const r=t[0],n=Vn.definitions[r];if(!n)return e.error(`Unknown expression \"${r}\". If you wanted a literal array, use [\"literal\", [...]].`,0);const i=Array.isArray(n)?n[0]:n.type,s=Array.isArray(n)?[[n[1],n[2]]]:n.overloads,a=s.filter((([e])=>!Array.isArray(e)||e.length===t.length-1));let o=null;for(const[n,s]of a){o=new Qe(e.registry,On,e.path,null,e.scope);const a=[];let l=!1;for(let e=1;e<t.length;e++){const r=t[e],i=Array.isArray(n)?n[e-1]:n.type,s=o.parse(r,1+a.length,i);if(!s){l=!0;break}a.push(s);}if(!l)if(Array.isArray(n)&&n.length!==a.length)o.error(`Expected ${n.length} arguments, but found ${a.length} instead.`);else {for(let t=0;t<a.length;t++){const e=Array.isArray(n)?n[t]:n.type,r=a[t];o.concat(t+1).checkSubtype(e,r.type);}if(0===o.errors.length)return new Vn(r,i,s,a)}}if(1===a.length)e.errors.push(...o.errors);else {const r=(a.length?a:s).map((([t])=>{return e=t,Array.isArray(e)?`(${e.map(Qt).join(\", \")})`:`(${Qt(e.type)}...)`;var e;})).join(\" | \"),n=[];for(let r=1;r<t.length;r++){const i=e.parse(t[r],1+n.length);if(!i)return null;n.push(Qt(i.type));}e.error(`Expected arguments of type ${r}, but found (${n.join(\", \")}) instead.`);}return null}static register(t,e){Vn.definitions=e;for(const r in e)t[r]=Vn;}}function Fn(t,[e,r,n,i]){e=e.evaluate(t),r=r.evaluate(t),n=n.evaluate(t);const s=i?i.evaluate(t):1,a=Ne(e,r,n,s);if(a)throw new $e(a);return new Pe(e/255,r/255,n/255,s,!1)}function Ln(t,e){return t in e}function $n(t,e){const r=e[t];return void 0===r?null:r}function Dn(t){return {type:t}}function On(t){if(t instanceof er)return On(t.boundExpression);if(t instanceof Vn&&\"error\"===t.name)return !1;if(t instanceof zr)return !1;if(t instanceof Jr)return !1;if(t instanceof Bn)return !1;if(t instanceof En)return !1;const e=t instanceof Ke||t instanceof Ye;let r=!0;return t.eachChild((t=>{r=e?r&&On(t):r&&t instanceof Xe;})),!!r&&Rn(t)&&Nn(t,[\"zoom\",\"heatmap-density\",\"elevation\",\"line-progress\",\"accumulated\",\"is-supported-script\"])}function Rn(t){if(t instanceof Vn){if(\"get\"===t.name&&1===t.args.length)return !1;if(\"feature-state\"===t.name)return !1;if(\"has\"===t.name&&1===t.args.length)return !1;if(\"properties\"===t.name||\"geometry-type\"===t.name||\"id\"===t.name)return !1;if(/^filter-/.test(t.name))return !1}if(t instanceof Jr)return !1;if(t instanceof Bn)return !1;let e=!0;return t.eachChild((t=>{e&&!Rn(t)&&(e=!1);})),e}function jn(t){if(t instanceof Vn&&\"feature-state\"===t.name)return !1;let e=!0;return t.eachChild((t=>{e&&!jn(t)&&(e=!1);})),e}function Nn(t,e){if(t instanceof Vn&&e.indexOf(t.name)>=0)return !1;let r=!0;return t.eachChild((t=>{r&&!Nn(t,e)&&(r=!1);})),r}function Un(t){return {result:\"success\",value:t}}function qn(t){return {result:\"error\",value:t}}function Gn(t){return \"data-driven\"===t[\"property-type\"]||\"cross-faded-data-driven\"===t[\"property-type\"]}function Xn(t){return !!t.expression&&t.expression.parameters.indexOf(\"zoom\")>-1}function Zn(t){return !!t.expression&&t.expression.interpolated}function Yn(t){return t instanceof Number?\"number\":t instanceof String?\"string\":t instanceof Boolean?\"boolean\":Array.isArray(t)?\"array\":null===t?\"null\":typeof t}function Hn(t){return \"object\"==typeof t&&null!==t&&!Array.isArray(t)&&qe(t)===Ut}function Kn(t){return t}function Jn(t,e){const r=t.stops&&\"object\"==typeof t.stops[0][0],n=r||!(r||void 0!==t.property),i=t.type||(Zn(e)?\"exponential\":\"interval\"),s=function(t){switch(t.type){case \"color\":return Pe.parse;case \"padding\":return Ve.parse;case \"numberArray\":return Fe.parse;case \"colorArray\":return Le.parse;default:return null}}(e);if(s&&((t=Vt({},t)).stops&&(t.stops=t.stops.map((t=>[t[0],s(t[1])]))),t.default=s(t.default?t.default:e.default)),t.colorSpace&&\"rgb\"!==(a=t.colorSpace)&&\"hcl\"!==a&&\"lab\"!==a)throw new Error(`Unknown color space: \"${t.colorSpace}\"`);var a;const o=function(t){switch(t){case \"exponential\":return ei;case \"interval\":return ti;case \"categorical\":return Qn;case \"identity\":return ri;default:throw new Error(`Unknown function type \"${t}\"`)}}(i);let l,u;if(\"categorical\"===i){l=Object.create(null);for(const e of t.stops)l[e[0]]=e[1];u=typeof t.stops[0][0];}if(r){const r={},n=[];for(let e=0;e<t.stops.length;e++){const i=t.stops[e],s=i[0].zoom;void 0===r[s]&&(r[s]={zoom:s,type:t.type,property:t.property,default:t.default,stops:[]},n.push(s)),r[s].stops.push([i[0].value,i[1]]);}const i=[];for(const t of n)i.push([r[t].zoom,Jn(r[t],e)]);const s={name:\"linear\"};return {kind:\"composite\",interpolationType:s,interpolationFactor:yr.interpolationFactor.bind(void 0,s),zoomStops:i.map((t=>t[0])),evaluate:({zoom:r},n)=>ei({stops:i,base:t.base},e,r).evaluate(r,n)}}if(n){const r=\"exponential\"===i?{name:\"exponential\",base:void 0!==t.base?t.base:1}:null;return {kind:\"camera\",interpolationType:r,interpolationFactor:yr.interpolationFactor.bind(void 0,r),zoomStops:t.stops.map((t=>t[0])),evaluate:({zoom:r})=>o(t,e,r,l,u)}}return {kind:\"source\",evaluate(r,n){const i=n&&n.properties?n.properties[t.property]:void 0;return void 0===i?Wn(t.default,e.default):o(t,e,i,l,u)}}}function Wn(t,e,r){return void 0!==t?t:void 0!==e?e:void 0!==r?r:void 0}function Qn(t,e,r,n,i){return Wn(typeof r===i?n[r]:void 0,t.default,e.default)}function ti(t,e,r){if(\"number\"!==Yn(r))return Wn(t.default,e.default);const n=t.stops.length;if(1===n)return t.stops[0][1];if(r<=t.stops[0][0])return t.stops[0][1];if(r>=t.stops[n-1][0])return t.stops[n-1][1];const i=lr(t.stops.map((t=>t[0])),r);return t.stops[i][1]}function ei(t,e,r){const n=void 0!==t.base?t.base:1;if(\"number\"!==Yn(r))return Wn(t.default,e.default);const i=t.stops.length;if(1===i)return t.stops[0][1];if(r<=t.stops[0][0])return t.stops[0][1];if(r>=t.stops[i-1][0])return t.stops[i-1][1];const s=lr(t.stops.map((t=>t[0])),r),a=function(t,e,r,n){const i=n-r,s=t-r;return 0===i?0:1===e?s/i:(Math.pow(e,s)-1)/(Math.pow(e,i)-1)}(r,n,t.stops[s][0],t.stops[s+1][0]),o=t.stops[s][1],l=t.stops[s+1][1],u=gr[e.type]||Kn;return \"function\"==typeof o.evaluate?{evaluate(...e){const r=o.evaluate.apply(void 0,e),n=l.evaluate.apply(void 0,e);if(void 0!==r&&void 0!==n)return u(r,n,a,t.colorSpace)}}:u(o,l,a,t.colorSpace)}function ri(t,e,r){switch(e.type){case \"color\":r=Pe.parse(r);break;case \"formatted\":r=Te.fromString(r.toString());break;case \"resolvedImage\":r=Re.fromString(r.toString());break;case \"padding\":r=Ve.parse(r);break;case \"colorArray\":r=Le.parse(r);break;case \"numberArray\":r=Fe.parse(r);break;default:Yn(r)===e.type||\"enum\"===e.type&&e.values[r]||(r=void 0);}return Wn(r,t.default,e.default)}Vn.register(Tn,{error:[{kind:\"error\"},[Ot],(t,[e])=>{throw new $e(e.evaluate(t))}],typeof:[Ot,[qt],(t,[e])=>Qt(qe(e.evaluate(t)))],\"to-rgba\":[Wt(Dt,4),[jt],(t,[e])=>{const[r,n,i,s]=e.evaluate(t).rgb;return [255*r,255*n,255*i,s]}],rgb:[jt,[Dt,Dt,Dt],Fn],rgba:[jt,[Dt,Dt,Dt,Dt],Fn],has:{type:Rt,overloads:[[[Ot],(t,[e])=>Ln(e.evaluate(t),t.properties())],[[Ot,Ut],(t,[e,r])=>Ln(e.evaluate(t),r.evaluate(t))]]},get:{type:qt,overloads:[[[Ot],(t,[e])=>$n(e.evaluate(t),t.properties())],[[Ot,Ut],(t,[e,r])=>$n(e.evaluate(t),r.evaluate(t))]]},\"feature-state\":[qt,[Ot],(t,[e])=>$n(e.evaluate(t),t.featureState||{})],properties:[Ut,[],t=>t.properties()],\"geometry-type\":[Ot,[],t=>t.geometryType()],id:[qt,[],t=>t.id()],zoom:[Dt,[],t=>t.globals.zoom],\"heatmap-density\":[Dt,[],t=>t.globals.heatmapDensity||0],elevation:[Dt,[],t=>t.globals.elevation||0],\"line-progress\":[Dt,[],t=>t.globals.lineProgress||0],accumulated:[qt,[],t=>void 0===t.globals.accumulated?null:t.globals.accumulated],\"+\":[Dt,Dn(Dt),(t,e)=>{let r=0;for(const n of e)r+=n.evaluate(t);return r}],\"*\":[Dt,Dn(Dt),(t,e)=>{let r=1;for(const n of e)r*=n.evaluate(t);return r}],\"-\":{type:Dt,overloads:[[[Dt,Dt],(t,[e,r])=>e.evaluate(t)-r.evaluate(t)],[[Dt],(t,[e])=>-e.evaluate(t)]]},\"/\":[Dt,[Dt,Dt],(t,[e,r])=>e.evaluate(t)/r.evaluate(t)],\"%\":[Dt,[Dt,Dt],(t,[e,r])=>e.evaluate(t)%r.evaluate(t)],ln2:[Dt,[],()=>Math.LN2],pi:[Dt,[],()=>Math.PI],e:[Dt,[],()=>Math.E],\"^\":[Dt,[Dt,Dt],(t,[e,r])=>Math.pow(e.evaluate(t),r.evaluate(t))],sqrt:[Dt,[Dt],(t,[e])=>Math.sqrt(e.evaluate(t))],log10:[Dt,[Dt],(t,[e])=>Math.log(e.evaluate(t))/Math.LN10],ln:[Dt,[Dt],(t,[e])=>Math.log(e.evaluate(t))],log2:[Dt,[Dt],(t,[e])=>Math.log(e.evaluate(t))/Math.LN2],sin:[Dt,[Dt],(t,[e])=>Math.sin(e.evaluate(t))],cos:[Dt,[Dt],(t,[e])=>Math.cos(e.evaluate(t))],tan:[Dt,[Dt],(t,[e])=>Math.tan(e.evaluate(t))],asin:[Dt,[Dt],(t,[e])=>Math.asin(e.evaluate(t))],acos:[Dt,[Dt],(t,[e])=>Math.acos(e.evaluate(t))],atan:[Dt,[Dt],(t,[e])=>Math.atan(e.evaluate(t))],min:[Dt,Dn(Dt),(t,e)=>Math.min(...e.map((e=>e.evaluate(t))))],max:[Dt,Dn(Dt),(t,e)=>Math.max(...e.map((e=>e.evaluate(t))))],abs:[Dt,[Dt],(t,[e])=>Math.abs(e.evaluate(t))],round:[Dt,[Dt],(t,[e])=>{const r=e.evaluate(t);return r<0?-Math.round(-r):Math.round(r)}],floor:[Dt,[Dt],(t,[e])=>Math.floor(e.evaluate(t))],ceil:[Dt,[Dt],(t,[e])=>Math.ceil(e.evaluate(t))],\"filter-==\":[Rt,[Ot,qt],(t,[e,r])=>t.properties()[e.value]===r.value],\"filter-id-==\":[Rt,[qt],(t,[e])=>t.id()===e.value],\"filter-type-==\":[Rt,[Ot],(t,[e])=>t.geometryType()===e.value],\"filter-<\":[Rt,[Ot,qt],(t,[e,r])=>{const n=t.properties()[e.value],i=r.value;return typeof n==typeof i&&n<i}],\"filter-id-<\":[Rt,[qt],(t,[e])=>{const r=t.id(),n=e.value;return typeof r==typeof n&&r<n}],\"filter->\":[Rt,[Ot,qt],(t,[e,r])=>{const n=t.properties()[e.value],i=r.value;return typeof n==typeof i&&n>i}],\"filter-id->\":[Rt,[qt],(t,[e])=>{const r=t.id(),n=e.value;return typeof r==typeof n&&r>n}],\"filter-<=\":[Rt,[Ot,qt],(t,[e,r])=>{const n=t.properties()[e.value],i=r.value;return typeof n==typeof i&&n<=i}],\"filter-id-<=\":[Rt,[qt],(t,[e])=>{const r=t.id(),n=e.value;return typeof r==typeof n&&r<=n}],\"filter->=\":[Rt,[Ot,qt],(t,[e,r])=>{const n=t.properties()[e.value],i=r.value;return typeof n==typeof i&&n>=i}],\"filter-id->=\":[Rt,[qt],(t,[e])=>{const r=t.id(),n=e.value;return typeof r==typeof n&&r>=n}],\"filter-has\":[Rt,[qt],(t,[e])=>e.value in t.properties()],\"filter-has-id\":[Rt,[],t=>null!==t.id()&&void 0!==t.id()],\"filter-type-in\":[Rt,[Wt(Ot)],(t,[e])=>e.value.indexOf(t.geometryType())>=0],\"filter-id-in\":[Rt,[Wt(qt)],(t,[e])=>e.value.indexOf(t.id())>=0],\"filter-in-small\":[Rt,[Ot,Wt(qt)],(t,[e,r])=>r.value.indexOf(t.properties()[e.value])>=0],\"filter-in-large\":[Rt,[Ot,Wt(qt)],(t,[e,r])=>function(t,e,r,n){for(;r<=n;){const i=r+n>>1;if(e[i]===t)return !0;e[i]>t?n=i-1:r=i+1;}return !1}(t.properties()[e.value],r.value,0,r.value.length-1)],all:{type:Rt,overloads:[[[Rt,Rt],(t,[e,r])=>e.evaluate(t)&&r.evaluate(t)],[Dn(Rt),(t,e)=>{for(const r of e)if(!r.evaluate(t))return !1;return !0}]]},any:{type:Rt,overloads:[[[Rt,Rt],(t,[e,r])=>e.evaluate(t)||r.evaluate(t)],[Dn(Rt),(t,e)=>{for(const r of e)if(r.evaluate(t))return !0;return !1}]]},\"!\":[Rt,[Rt],(t,[e])=>!e.evaluate(t)],\"is-supported-script\":[Rt,[Ot],(t,[e])=>{const r=t.globals&&t.globals.isSupportedScript;return !r||r(e.evaluate(t))}],upcase:[Ot,[Ot],(t,[e])=>e.evaluate(t).toUpperCase()],downcase:[Ot,[Ot],(t,[e])=>e.evaluate(t).toLowerCase()],concat:[Ot,Dn(qt),(t,e)=>e.map((e=>Ge(e.evaluate(t)))).join(\"\")],\"resolved-locale\":[Ot,[Gt],(t,[e])=>e.evaluate(t).resolvedLocale()]});class ni{constructor(t,e){this.expression=t,this._warningHistory={},this._evaluator=new We,this._defaultValue=e?function(t){if(\"color\"===t.type&&Hn(t.default))return new Pe(0,0,0,0);switch(t.type){case \"color\":return Pe.parse(t.default)||null;case \"padding\":return Ve.parse(t.default)||null;case \"numberArray\":return Fe.parse(t.default)||null;case \"colorArray\":return Le.parse(t.default)||null;case \"variableAnchorOffsetCollection\":return Oe.parse(t.default)||null;case \"projectionDefinition\":return je.parse(t.default)||null;default:return void 0===t.default?null:t.default}}(e):null,this._enumValues=e&&\"enum\"===e.type?e.values:null;}evaluateWithoutErrorHandling(t,e,r,n,i,s){return this._evaluator.globals=t,this._evaluator.feature=e,this._evaluator.featureState=r,this._evaluator.canonical=n,this._evaluator.availableImages=i||null,this._evaluator.formattedSection=s,this.expression.evaluate(this._evaluator)}evaluate(t,e,r,n,i,s){this._evaluator.globals=t,this._evaluator.feature=e||null,this._evaluator.featureState=r||null,this._evaluator.canonical=n,this._evaluator.availableImages=i||null,this._evaluator.formattedSection=s||null;try{const t=this.expression.evaluate(this._evaluator);if(null==t||\"number\"==typeof t&&t!=t)return this._defaultValue;if(this._enumValues&&!(t in this._enumValues))throw new $e(`Expected value to be one of ${Object.keys(this._enumValues).map((t=>JSON.stringify(t))).join(\", \")}, but found ${JSON.stringify(t)} instead.`);return t}catch(t){return this._warningHistory[t.message]||(this._warningHistory[t.message]=!0,\"undefined\"!=typeof console&&console.warn(t.message)),this._defaultValue}}}function ii(t){return Array.isArray(t)&&t.length>0&&\"string\"==typeof t[0]&&t[0]in Tn}function si(t,e){const r=new Qe(Tn,On,[],e?function(t){const e={color:jt,string:Ot,number:Dt,enum:Ot,boolean:Rt,formatted:Xt,padding:Zt,numberArray:Ht,colorArray:Yt,projectionDefinition:Nt,resolvedImage:Kt,variableAnchorOffsetCollection:Jt};return \"array\"===t.type?Wt(e[t.value]||qt,t.length):e[t.type]}(e):void 0),n=r.parse(t,void 0,void 0,void 0,e&&\"string\"===e.type?{typeAnnotation:\"coerce\"}:void 0);return n?Un(new ni(n,e)):qn(r.errors)}class ai{constructor(t,e){this.kind=t,this._styleExpression=e,this.isStateDependent=\"constant\"!==t&&!jn(e.expression),this.globalStateRefs=hi(e.expression);}evaluateWithoutErrorHandling(t,e,r,n,i,s){return this._styleExpression.evaluateWithoutErrorHandling(t,e,r,n,i,s)}evaluate(t,e,r,n,i,s){return this._styleExpression.evaluate(t,e,r,n,i,s)}}class oi{constructor(t,e,r,n){this.kind=t,this.zoomStops=r,this._styleExpression=e,this.isStateDependent=\"camera\"!==t&&!jn(e.expression),this.globalStateRefs=hi(e.expression),this.interpolationType=n;}evaluateWithoutErrorHandling(t,e,r,n,i,s){return this._styleExpression.evaluateWithoutErrorHandling(t,e,r,n,i,s)}evaluate(t,e,r,n,i,s){return this._styleExpression.evaluate(t,e,r,n,i,s)}interpolationFactor(t,e,r){return this.interpolationType?yr.interpolationFactor(this.interpolationType,t,e,r):0}}function li(t,e){const r=si(t,e);if(\"error\"===r.result)return r;const n=r.value.expression,i=Rn(n);if(!i&&!Gn(e))return qn([new Ft(\"\",\"data expressions not supported\")]);const s=Nn(n,[\"zoom\"]);if(!s&&!Xn(e))return qn([new Ft(\"\",\"zoom expressions not supported\")]);const a=ci(n);return a||s?a instanceof Ft?qn([a]):a instanceof yr&&!Zn(e)?qn([new Ft(\"\",'\"interpolate\" expressions cannot be used with this property')]):Un(a?new oi(i?\"camera\":\"composite\",r.value,a.labels,a instanceof yr?a.interpolation:void 0):new ai(i?\"constant\":\"source\",r.value)):qn([new Ft(\"\",'\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression.')])}class ui{constructor(t,e){this._parameters=t,this._specification=e,Vt(this,Jn(this._parameters,this._specification));}static deserialize(t){return new ui(t._parameters,t._specification)}static serialize(t){return {_parameters:t._parameters,_specification:t._specification}}}function ci(t){let e=null;if(t instanceof tr)e=ci(t.result);else if(t instanceof xr){for(const r of t.args)if(e=ci(r),e)break}else (t instanceof ur||t instanceof yr)&&t.input instanceof Vn&&\"zoom\"===t.input.name&&(e=t);return e instanceof Ft||t.eachChild((t=>{const r=ci(t);r instanceof Ft?e=r:!e&&r?e=new Ft(\"\",'\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression.'):e&&r&&e!==r&&(e=new Ft(\"\",'Only one zoom-based \"step\" or \"interpolate\" subexpression may be used in an expression.'));})),e}function hi(t,e=new Set){return t instanceof En&&e.add(t.key),t.eachChild((t=>{hi(t,e);})),e}function pi(t){if(!0===t||!1===t)return !0;if(!Array.isArray(t)||0===t.length)return !1;switch(t[0]){case \"has\":return t.length>=2&&\"$id\"!==t[1]&&\"$type\"!==t[1];case \"in\":return t.length>=3&&(\"string\"!=typeof t[1]||Array.isArray(t[2]));case \"!in\":case \"!has\":case \"none\":return !1;case \"==\":case \"!=\":case \">\":case \">=\":case \"<\":case \"<=\":return 3!==t.length||Array.isArray(t[1])||Array.isArray(t[2]);case \"any\":case \"all\":for(const e of t.slice(1))if(!pi(e)&&\"boolean\"!=typeof e)return !1;return !0;default:return !0}}const fi={type:\"boolean\",default:!1,transition:!1,\"property-type\":\"data-driven\",expression:{interpolated:!1,parameters:[\"zoom\",\"feature\"]}};function di(t){if(null==t)return {filter:()=>!0,needGeometry:!1,getGlobalStateRefs:()=>new Set};pi(t)||(t=gi(t));const e=si(t,fi);if(\"error\"===e.result)throw new Error(e.value.map((t=>`${t.key}: ${t.message}`)).join(\", \"));return {filter:(t,r,n)=>e.value.evaluate(t,r,{},n),needGeometry:mi(t),getGlobalStateRefs:()=>hi(e.value.expression)}}function yi(t,e){return t<e?-1:t>e?1:0}function mi(t){if(!Array.isArray(t))return !1;if(\"within\"===t[0]||\"distance\"===t[0])return !0;for(let e=1;e<t.length;e++)if(mi(t[e]))return !0;return !1}function gi(t){if(!t)return !0;const e=t[0];return t.length<=1?\"any\"!==e:\"==\"===e?xi(t[1],t[2],\"==\"):\"!=\"===e?wi(xi(t[1],t[2],\"==\")):\"<\"===e||\">\"===e||\"<=\"===e||\">=\"===e?xi(t[1],t[2],e):\"any\"===e?(r=t.slice(1),[\"any\"].concat(r.map(gi))):\"all\"===e?[\"all\"].concat(t.slice(1).map(gi)):\"none\"===e?[\"all\"].concat(t.slice(1).map(gi).map(wi)):\"in\"===e?vi(t[1],t.slice(2)):\"!in\"===e?wi(vi(t[1],t.slice(2))):\"has\"===e?bi(t[1]):\"!has\"!==e||wi(bi(t[1]));var r;}function xi(t,e,r){switch(t){case \"$type\":return [`filter-type-${r}`,e];case \"$id\":return [`filter-id-${r}`,e];default:return [`filter-${r}`,t,e]}}function vi(t,e){if(0===e.length)return !1;switch(t){case \"$type\":return [\"filter-type-in\",[\"literal\",e]];case \"$id\":return [\"filter-id-in\",[\"literal\",e]];default:return e.length>200&&!e.some((t=>typeof t!=typeof e[0]))?[\"filter-in-large\",t,[\"literal\",e.sort(yi)]]:[\"filter-in-small\",t,[\"literal\",e]]}}function bi(t){switch(t){case \"$type\":return !0;case \"$id\":return [\"filter-has-id\"];default:return [\"filter-has\",t]}}function wi(t){return [\"!\",t]}function _i(t){const e=typeof t;if(\"number\"===e||\"boolean\"===e||\"string\"===e||null==t)return JSON.stringify(t);if(Array.isArray(t)){let e=\"[\";for(const r of t)e+=`${_i(r)},`;return `${e}]`}const r=Object.keys(t).sort();let n=\"{\";for(let e=0;e<r.length;e++)n+=`${JSON.stringify(r[e])}:${_i(t[r[e]])},`;return `${n}}`}function Si(t){let e=\"\";for(const r of _t)e+=`/${_i(t[r])}`;return e}function Ai(t){const e=t.value;return e?[new Tt(t.key,e,\"constants have been deprecated as of v8\")]:[]}function ki(t){return t instanceof Number||t instanceof String||t instanceof Boolean?t.valueOf():t}function Mi(t){if(Array.isArray(t))return t.map(Mi);if(t instanceof Object&&!(t instanceof Number||t instanceof String||t instanceof Boolean)){const e={};for(const r in t)e[r]=Mi(t[r]);return e}return ki(t)}function Ii(t){const e=t.key,r=t.value,n=t.valueSpec||{},i=t.objectElementValidators||{},s=t.style,a=t.styleSpec,o=t.validateSpec;let l=[];const u=Yn(r);if(\"object\"!==u)return [new Tt(e,r,`object expected, ${u} found`)];for(const t in r){const u=t.split(\".\")[0],c=we(n,u)||n[\"*\"];let h;if(we(i,u))h=i[u];else if(we(n,u))h=o;else if(i[\"*\"])h=i[\"*\"];else {if(!n[\"*\"]){l.push(new Tt(e,r[t],`unknown property \"${t}\"`));continue}h=o;}l=l.concat(h({key:(e?`${e}.`:e)+t,value:r[t],valueSpec:c,style:s,styleSpec:a,object:r,objectKey:t,validateSpec:o},r));}for(const t in n)i[t]||n[t].required&&void 0===n[t].default&&void 0===r[t]&&l.push(new Tt(e,r,`missing required property \"${t}\"`));return l}function zi(t){const e=t.value,r=t.valueSpec,n=t.style,i=t.styleSpec,s=t.key,a=t.arrayElementValidator||t.validateSpec;if(\"array\"!==Yn(e))return [new Tt(s,e,`array expected, ${Yn(e)} found`)];if(r.length&&e.length!==r.length)return [new Tt(s,e,`array length ${r.length} expected, length ${e.length} found`)];if(r[\"min-length\"]&&e.length<r[\"min-length\"])return [new Tt(s,e,`array length at least ${r[\"min-length\"]} expected, length ${e.length} found`)];let o={type:r.value,values:r.values};i.$version<7&&(o.function=r.function),\"object\"===Yn(r.value)&&(o=r.value);let l=[];for(let r=0;r<e.length;r++)l=l.concat(a({array:e,arrayIndex:r,value:e[r],valueSpec:o,validateSpec:t.validateSpec,style:n,styleSpec:i,key:`${s}[${r}]`}));return l}function Pi(t){const e=t.key,r=t.value,n=t.valueSpec;let i=Yn(r);return \"number\"===i&&r!=r&&(i=\"NaN\"),\"number\"!==i?[new Tt(e,r,`number expected, ${i} found`)]:\"minimum\"in n&&r<n.minimum?[new Tt(e,r,`${r} is less than the minimum value ${n.minimum}`)]:\"maximum\"in n&&r>n.maximum?[new Tt(e,r,`${r} is greater than the maximum value ${n.maximum}`)]:[]}function Ci(t){const e=t.valueSpec,r=ki(t.value.type);let n,i,s,a={};const o=\"categorical\"!==r&&void 0===t.value.property,l=!o,u=\"array\"===Yn(t.value.stops)&&\"array\"===Yn(t.value.stops[0])&&\"object\"===Yn(t.value.stops[0][0]),c=Ii({key:t.key,value:t.value,valueSpec:t.styleSpec.function,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{stops:function(t){if(\"identity\"===r)return [new Tt(t.key,t.value,'identity function may not have a \"stops\" property')];let e=[];const n=t.value;return e=e.concat(zi({key:t.key,value:n,valueSpec:t.valueSpec,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,arrayElementValidator:h})),\"array\"===Yn(n)&&0===n.length&&e.push(new Tt(t.key,n,\"array must have at least one stop\")),e},default:function(t){return t.validateSpec({key:t.key,value:t.value,valueSpec:e,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec})}}});return \"identity\"===r&&o&&c.push(new Tt(t.key,t.value,'missing required property \"property\"')),\"identity\"===r||t.value.stops||c.push(new Tt(t.key,t.value,'missing required property \"stops\"')),\"exponential\"===r&&t.valueSpec.expression&&!Zn(t.valueSpec)&&c.push(new Tt(t.key,t.value,\"exponential functions not supported\")),t.styleSpec.$version>=8&&(l&&!Gn(t.valueSpec)?c.push(new Tt(t.key,t.value,\"property functions not supported\")):o&&!Xn(t.valueSpec)&&c.push(new Tt(t.key,t.value,\"zoom functions not supported\"))),\"categorical\"!==r&&!u||void 0!==t.value.property||c.push(new Tt(t.key,t.value,'\"property\" property is required')),c;function h(t){let r=[];const n=t.value,o=t.key;if(\"array\"!==Yn(n))return [new Tt(o,n,`array expected, ${Yn(n)} found`)];if(2!==n.length)return [new Tt(o,n,`array length 2 expected, length ${n.length} found`)];if(u){if(\"object\"!==Yn(n[0]))return [new Tt(o,n,`object expected, ${Yn(n[0])} found`)];if(void 0===n[0].zoom)return [new Tt(o,n,\"object stop key must have zoom\")];if(void 0===n[0].value)return [new Tt(o,n,\"object stop key must have value\")];if(s&&s>ki(n[0].zoom))return [new Tt(o,n[0].zoom,\"stop zoom values must appear in ascending order\")];ki(n[0].zoom)!==s&&(s=ki(n[0].zoom),i=void 0,a={}),r=r.concat(Ii({key:`${o}[0]`,value:n[0],valueSpec:{zoom:{}},validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{zoom:Pi,value:p}}));}else r=r.concat(p({key:`${o}[0]`,value:n[0],validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec},n));return ii(Mi(n[1]))?r.concat([new Tt(`${o}[1]`,n[1],\"expressions are not allowed in function stops.\")]):r.concat(t.validateSpec({key:`${o}[1]`,value:n[1],valueSpec:e,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec}))}function p(t,s){const o=Yn(t.value),l=ki(t.value),u=null!==t.value?t.value:s;if(n){if(o!==n)return [new Tt(t.key,u,`${o} stop domain type must match previous stop domain type ${n}`)]}else n=o;if(\"number\"!==o&&\"string\"!==o&&\"boolean\"!==o)return [new Tt(t.key,u,\"stop domain value must be a number, string, or boolean\")];if(\"number\"!==o&&\"categorical\"!==r){let n=`number expected, ${o} found`;return Gn(e)&&void 0===r&&(n+='\\nIf you intended to use a categorical function, specify `\"type\": \"categorical\"`.'),[new Tt(t.key,u,n)]}return \"categorical\"!==r||\"number\"!==o||isFinite(l)&&Math.floor(l)===l?\"categorical\"!==r&&\"number\"===o&&void 0!==i&&l<i?[new Tt(t.key,u,\"stop domain values must appear in ascending order\")]:(i=l,\"categorical\"===r&&l in a?[new Tt(t.key,u,\"stop domain values must be unique\")]:(a[l]=!0,[])):[new Tt(t.key,u,`integer expected, found ${l}`)]}}function Bi(t){const e=(\"property\"===t.expressionContext?li:si)(Mi(t.value),t.valueSpec);if(\"error\"===e.result)return e.value.map((e=>new Tt(`${t.key}${e.key}`,t.value,e.message)));const r=e.value.expression||e.value._styleExpression.expression;if(\"property\"===t.expressionContext&&\"text-font\"===t.propertyKey&&!r.outputDefined())return [new Tt(t.key,t.value,`Invalid data expression for \"${t.propertyKey}\". Output values must be contained as literals within the expression.`)];if(\"property\"===t.expressionContext&&\"layout\"===t.propertyType&&!jn(r))return [new Tt(t.key,t.value,'\"feature-state\" data expressions are not supported with layout properties.')];if(\"filter\"===t.expressionContext&&!jn(r))return [new Tt(t.key,t.value,'\"feature-state\" data expressions are not supported with filters.')];if(t.expressionContext&&0===t.expressionContext.indexOf(\"cluster\")){if(!Nn(r,[\"zoom\",\"feature-state\"]))return [new Tt(t.key,t.value,'\"zoom\" and \"feature-state\" expressions are not supported with cluster properties.')];if(\"cluster-initial\"===t.expressionContext&&!Rn(r))return [new Tt(t.key,t.value,\"Feature data expressions are not supported with initial expression part of cluster properties.\")]}return []}function Ei(t){const e=t.key,r=t.value,n=Yn(r);return \"string\"!==n?[new Tt(e,r,`color expected, ${n} found`)]:Pe.parse(String(r))?[]:[new Tt(e,r,`color expected, \"${r}\" found`)]}function Ti(t){const e=t.key,r=t.value,n=t.valueSpec,i=[];return Array.isArray(n.values)?-1===n.values.indexOf(ki(r))&&i.push(new Tt(e,r,`expected one of [${n.values.join(\", \")}], ${JSON.stringify(r)} found`)):-1===Object.keys(n.values).indexOf(ki(r))&&i.push(new Tt(e,r,`expected one of [${Object.keys(n.values).join(\", \")}], ${JSON.stringify(r)} found`)),i}function Vi(t){return pi(Mi(t.value))?Bi(Vt({},t,{expressionContext:\"filter\",valueSpec:{value:\"boolean\"}})):Fi(t)}function Fi(t){const e=t.value,r=t.key;if(\"array\"!==Yn(e))return [new Tt(r,e,`array expected, ${Yn(e)} found`)];const n=t.styleSpec;let i,s=[];if(e.length<1)return [new Tt(r,e,\"filter array must have at least 1 element\")];switch(s=s.concat(Ti({key:`${r}[0]`,value:e[0],valueSpec:n.filter_operator,style:t.style,styleSpec:t.styleSpec})),ki(e[0])){case \"<\":case \"<=\":case \">\":case \">=\":e.length>=2&&\"$type\"===ki(e[1])&&s.push(new Tt(r,e,`\"$type\" cannot be use with operator \"${e[0]}\"`));case \"==\":case \"!=\":3!==e.length&&s.push(new Tt(r,e,`filter array for operator \"${e[0]}\" must have 3 elements`));case \"in\":case \"!in\":e.length>=2&&(i=Yn(e[1]),\"string\"!==i&&s.push(new Tt(`${r}[1]`,e[1],`string expected, ${i} found`)));for(let a=2;a<e.length;a++)i=Yn(e[a]),\"$type\"===ki(e[1])?s=s.concat(Ti({key:`${r}[${a}]`,value:e[a],valueSpec:n.geometry_type,style:t.style,styleSpec:t.styleSpec})):\"string\"!==i&&\"number\"!==i&&\"boolean\"!==i&&s.push(new Tt(`${r}[${a}]`,e[a],`string, number, or boolean expected, ${i} found`));break;case \"any\":case \"all\":case \"none\":for(let n=1;n<e.length;n++)s=s.concat(Fi({key:`${r}[${n}]`,value:e[n],style:t.style,styleSpec:t.styleSpec}));break;case \"has\":case \"!has\":i=Yn(e[1]),2!==e.length?s.push(new Tt(r,e,`filter array for \"${e[0]}\" operator must have 2 elements`)):\"string\"!==i&&s.push(new Tt(`${r}[1]`,e[1],`string expected, ${i} found`));}return s}function Li(t,e){const r=t.key,n=t.validateSpec,i=t.style,s=t.styleSpec,a=t.value,o=t.objectKey,l=s[`${e}_${t.layerType}`];if(!l)return [];const u=o.match(/^(.*)-transition$/);if(\"paint\"===e&&u&&l[u[1]]&&l[u[1]].transition)return n({key:r,value:a,valueSpec:s.transition,style:i,styleSpec:s});const c=t.valueSpec||l[o];if(!c)return [new Tt(r,a,`unknown property \"${o}\"`)];let h;if(\"string\"===Yn(a)&&Gn(c)&&!c.tokens&&(h=/^{([^}]+)}$/.exec(a)))return [new Tt(r,a,`\"${o}\" does not support interpolation syntax\\nUse an identity property function instead: \\`{ \"type\": \"identity\", \"property\": ${JSON.stringify(h[1])} }\\`.`)];const p=[];return \"symbol\"===t.layerType&&(\"text-field\"===o&&i&&!i.glyphs&&p.push(new Tt(r,a,'use of \"text-field\" requires a style \"glyphs\" property')),\"text-font\"===o&&Hn(Mi(a))&&\"identity\"===ki(a.type)&&p.push(new Tt(r,a,'\"text-font\" does not support identity functions'))),p.concat(n({key:t.key,value:a,valueSpec:c,style:i,styleSpec:s,expressionContext:\"property\",propertyType:e,propertyKey:o}))}function $i(t){return Li(t,\"paint\")}function Di(t){return Li(t,\"layout\")}function Oi(t){let e=[];const r=t.value,n=t.key,i=t.style,s=t.styleSpec;if(\"object\"!==Yn(r))return [new Tt(n,r,`object expected, ${Yn(r)} found`)];r.type||r.ref||e.push(new Tt(n,r,'either \"type\" or \"ref\" is required'));let a=ki(r.type);const o=ki(r.ref);if(r.id){const s=ki(r.id);for(let a=0;a<t.arrayIndex;a++){const t=i.layers[a];ki(t.id)===s&&e.push(new Tt(n,r.id,`duplicate layer id \"${r.id}\", previously used at line ${t.id.__line__}`));}}if(\"ref\"in r){let t;[\"type\",\"source\",\"source-layer\",\"filter\",\"layout\"].forEach((t=>{t in r&&e.push(new Tt(n,r[t],`\"${t}\" is prohibited for ref layers`));})),i.layers.forEach((e=>{ki(e.id)===o&&(t=e);})),t?t.ref?e.push(new Tt(n,r.ref,\"ref cannot reference another ref layer\")):a=ki(t.type):e.push(new Tt(n,r.ref,`ref layer \"${o}\" not found`));}else if(\"background\"!==a)if(r.source){const t=i.sources&&i.sources[r.source],s=t&&ki(t.type);t?\"vector\"===s&&\"raster\"===a?e.push(new Tt(n,r.source,`layer \"${r.id}\" requires a raster source`)):\"raster-dem\"!==s&&\"hillshade\"===a||\"raster-dem\"!==s&&\"color-relief\"===a?e.push(new Tt(n,r.source,`layer \"${r.id}\" requires a raster-dem source`)):\"raster\"===s&&\"raster\"!==a?e.push(new Tt(n,r.source,`layer \"${r.id}\" requires a vector source`)):\"vector\"!==s||r[\"source-layer\"]?\"raster-dem\"===s&&\"hillshade\"!==a&&\"color-relief\"!==a?e.push(new Tt(n,r.source,\"raster-dem source can only be used with layer type 'hillshade' or 'color-relief'.\")):\"line\"!==a||!r.paint||!r.paint[\"line-gradient\"]||\"geojson\"===s&&t.lineMetrics||e.push(new Tt(n,r,`layer \"${r.id}\" specifies a line-gradient, which requires a GeoJSON source with \\`lineMetrics\\` enabled.`)):e.push(new Tt(n,r,`layer \"${r.id}\" must specify a \"source-layer\"`)):e.push(new Tt(n,r.source,`source \"${r.source}\" not found`));}else e.push(new Tt(n,r,'missing required property \"source\"'));return e=e.concat(Ii({key:n,value:r,valueSpec:s.layer,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{\"*\":()=>[],type:()=>t.validateSpec({key:`${n}.type`,value:r.type,valueSpec:s.layer.type,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,object:r,objectKey:\"type\"}),filter:Vi,layout:t=>Ii({layer:r,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{\"*\":t=>Di(Vt({layerType:a},t))}}),paint:t=>Ii({layer:r,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{\"*\":t=>$i(Vt({layerType:a},t))}})}})),e}function Ri(t){const e=t.value,r=t.key,n=Yn(e);return \"string\"!==n?[new Tt(r,e,`string expected, ${n} found`)]:[]}const ji={promoteId:function({key:t,value:e}){if(\"string\"===Yn(e))return Ri({key:t,value:e});{const r=[];for(const n in e)r.push(...Ri({key:`${t}.${n}`,value:e[n]}));return r}}};function Ni(t){const e=t.value,r=t.key,n=t.styleSpec,i=t.style,s=t.validateSpec;if(!e.type)return [new Tt(r,e,'\"type\" is required')];const a=ki(e.type);let o;switch(a){case \"vector\":case \"raster\":return o=Ii({key:r,value:e,valueSpec:n[`source_${a.replace(\"-\",\"_\")}`],style:t.style,styleSpec:n,objectElementValidators:ji,validateSpec:s}),o;case \"raster-dem\":return o=function(t){var e;const r=null!==(e=t.sourceName)&&void 0!==e?e:\"\",n=t.value,i=t.styleSpec,s=i.source_raster_dem,a=t.style;let o=[];const l=Yn(n);if(void 0===n)return o;if(\"object\"!==l)return o.push(new Tt(\"source_raster_dem\",n,`object expected, ${l} found`)),o;const u=\"custom\"===ki(n.encoding),c=[\"redFactor\",\"greenFactor\",\"blueFactor\",\"baseShift\"],h=t.value.encoding?`\"${t.value.encoding}\"`:\"Default\";for(const e in n)!u&&c.includes(e)?o.push(new Tt(e,n[e],`In \"${r}\": \"${e}\" is only valid when \"encoding\" is set to \"custom\". ${h} encoding found`)):s[e]?o=o.concat(t.validateSpec({key:e,value:n[e],valueSpec:s[e],validateSpec:t.validateSpec,style:a,styleSpec:i})):o.push(new Tt(e,n[e],`unknown property \"${e}\"`));return o}({sourceName:r,value:e,style:t.style,styleSpec:n,validateSpec:s}),o;case \"geojson\":if(o=Ii({key:r,value:e,valueSpec:n.source_geojson,style:i,styleSpec:n,validateSpec:s,objectElementValidators:ji}),e.cluster)for(const t in e.clusterProperties){const[n,i]=e.clusterProperties[t],s=\"string\"==typeof n?[n,[\"accumulated\"],[\"get\",t]]:n;o.push(...Bi({key:`${r}.${t}.map`,value:i,expressionContext:\"cluster-map\"})),o.push(...Bi({key:`${r}.${t}.reduce`,value:s,expressionContext:\"cluster-reduce\"}));}return o;case \"video\":return Ii({key:r,value:e,valueSpec:n.source_video,style:i,validateSpec:s,styleSpec:n});case \"image\":return Ii({key:r,value:e,valueSpec:n.source_image,style:i,validateSpec:s,styleSpec:n});case \"canvas\":return [new Tt(r,null,\"Please use runtime APIs to add canvas sources, rather than including them in stylesheets.\",\"source.canvas\")];default:return Ti({key:`${r}.type`,value:e.type,valueSpec:{values:[\"vector\",\"raster\",\"raster-dem\",\"geojson\",\"video\",\"image\"]}})}}function Ui(t){const e=t.value,r=t.styleSpec,n=r.light,i=t.style;let s=[];const a=Yn(e);if(void 0===e)return s;if(\"object\"!==a)return s=s.concat([new Tt(\"light\",e,`object expected, ${a} found`)]),s;for(const a in e){const o=a.match(/^(.*)-transition$/);s=s.concat(o&&n[o[1]]&&n[o[1]].transition?t.validateSpec({key:a,value:e[a],valueSpec:r.transition,validateSpec:t.validateSpec,style:i,styleSpec:r}):n[a]?t.validateSpec({key:a,value:e[a],valueSpec:n[a],validateSpec:t.validateSpec,style:i,styleSpec:r}):[new Tt(a,e[a],`unknown property \"${a}\"`)]);}return s}function qi(t){const e=t.value,r=t.styleSpec,n=r.sky,i=t.style,s=Yn(e);if(void 0===e)return [];if(\"object\"!==s)return [new Tt(\"sky\",e,`object expected, ${s} found`)];let a=[];for(const s in e)a=a.concat(n[s]?t.validateSpec({key:s,value:e[s],valueSpec:n[s],style:i,styleSpec:r}):[new Tt(s,e[s],`unknown property \"${s}\"`)]);return a}function Gi(t){const e=t.value,r=t.styleSpec,n=r.terrain,i=t.style;let s=[];const a=Yn(e);if(void 0===e)return s;if(\"object\"!==a)return s=s.concat([new Tt(\"terrain\",e,`object expected, ${a} found`)]),s;for(const a in e)s=s.concat(n[a]?t.validateSpec({key:a,value:e[a],valueSpec:n[a],validateSpec:t.validateSpec,style:i,styleSpec:r}):[new Tt(a,e[a],`unknown property \"${a}\"`)]);return s}function Xi(t){let e=[];const r=t.value,n=t.key;if(Array.isArray(r)){const i=[],s=[];for(const a in r)r[a].id&&i.includes(r[a].id)&&e.push(new Tt(n,r,`all the sprites' ids must be unique, but ${r[a].id} is duplicated`)),i.push(r[a].id),r[a].url&&s.includes(r[a].url)&&e.push(new Tt(n,r,`all the sprites' URLs must be unique, but ${r[a].url} is duplicated`)),s.push(r[a].url),e=e.concat(Ii({key:`${n}[${a}]`,value:r[a],valueSpec:{id:{type:\"string\",required:!0},url:{type:\"string\",required:!0}},validateSpec:t.validateSpec}));return e}return Ri({key:n,value:r})}function Zi(t){return e=t.value,Boolean(e)&&e.constructor===Object?[]:[new Tt(t.key,t.value,`object expected, ${Yn(t.value)} found`)];var e;}const Yi={\"*\":()=>[],array:zi,boolean:function(t){const e=t.value,r=t.key,n=Yn(e);return \"boolean\"!==n?[new Tt(r,e,`boolean expected, ${n} found`)]:[]},number:Pi,color:Ei,constants:Ai,enum:Ti,filter:Vi,function:Ci,layer:Oi,object:Ii,source:Ni,light:Ui,sky:qi,terrain:Gi,projection:function(t){const e=t.value,r=t.styleSpec,n=r.projection,i=t.style,s=Yn(e);if(void 0===e)return [];if(\"object\"!==s)return [new Tt(\"projection\",e,`object expected, ${s} found`)];let a=[];for(const s in e)a=a.concat(n[s]?t.validateSpec({key:s,value:e[s],valueSpec:n[s],style:i,styleSpec:r}):[new Tt(s,e[s],`unknown property \"${s}\"`)]);return a},projectionDefinition:function(t){const e=t.key;let r=t.value;r=r instanceof String?r.valueOf():r;const n=Yn(r);return \"array\"!==n||function(t){return Array.isArray(t)&&3===t.length&&\"string\"==typeof t[0]&&\"string\"==typeof t[1]&&\"number\"==typeof t[2]}(r)||function(t){return !![\"interpolate\",\"step\",\"literal\"].includes(t[0])}(r)?[\"array\",\"string\"].includes(n)?[]:[new Tt(e,r,`projection expected, invalid type \"${n}\" found`)]:[new Tt(e,r,`projection expected, invalid array ${JSON.stringify(r)} found`)]},string:Ri,formatted:function(t){return 0===Ri(t).length?[]:Bi(t)},resolvedImage:function(t){return 0===Ri(t).length?[]:Bi(t)},padding:function(t){const e=t.key,r=t.value;if(\"array\"===Yn(r)){if(r.length<1||r.length>4)return [new Tt(e,r,`padding requires 1 to 4 values; ${r.length} values found`)];const n={type:\"number\"};let i=[];for(let s=0;s<r.length;s++)i=i.concat(t.validateSpec({key:`${e}[${s}]`,value:r[s],validateSpec:t.validateSpec,valueSpec:n}));return i}return Pi({key:e,value:r,valueSpec:{}})},numberArray:function(t){const e=t.key,r=t.value;if(\"array\"===Yn(r)){const n={type:\"number\"};if(r.length<1)return [new Tt(e,r,\"array length at least 1 expected, length 0 found\")];let i=[];for(let s=0;s<r.length;s++)i=i.concat(t.validateSpec({key:`${e}[${s}]`,value:r[s],validateSpec:t.validateSpec,valueSpec:n}));return i}return Pi({key:e,value:r,valueSpec:{}})},colorArray:function(t){const e=t.key,r=t.value;if(\"array\"===Yn(r)){if(r.length<1)return [new Tt(e,r,\"array length at least 1 expected, length 0 found\")];let t=[];for(let n=0;n<r.length;n++)t=t.concat(Ei({key:`${e}[${n}]`,value:r[n]}));return t}return Ei({key:e,value:r})},variableAnchorOffsetCollection:function(t){const e=t.key,r=t.value,n=Yn(r),i=t.styleSpec;if(\"array\"!==n||r.length<1||r.length%2!=0)return [new Tt(e,r,\"variableAnchorOffsetCollection requires a non-empty array of even length\")];let s=[];for(let n=0;n<r.length;n+=2)s=s.concat(Ti({key:`${e}[${n}]`,value:r[n],valueSpec:i.layout_symbol[\"text-anchor\"]})),s=s.concat(zi({key:`${e}[${n+1}]`,value:r[n+1],valueSpec:{length:2,value:\"number\"},validateSpec:t.validateSpec,style:t.style,styleSpec:i}));return s},sprite:Xi,state:Zi};function Hi(t){const e=t.value,r=t.valueSpec,n=t.styleSpec;return t.validateSpec=Hi,r.expression&&Hn(ki(e))?Ci(t):r.expression&&ii(Mi(e))?Bi(t):r.type&&Yi[r.type]?Yi[r.type](t):Ii(Vt({},t,{valueSpec:r.type?n[r.type]:r}))}function Ki(t){const e=t.value,r=t.key,n=Ri(t);return n.length||(-1===e.indexOf(\"{fontstack}\")&&n.push(new Tt(r,e,'\"glyphs\" url must include a \"{fontstack}\" token')),-1===e.indexOf(\"{range}\")&&n.push(new Tt(r,e,'\"glyphs\" url must include a \"{range}\" token'))),n}function Ji(t,e=wt){let r=[];return r=r.concat(Hi({key:\"\",value:t,valueSpec:e.$root,styleSpec:e,style:t,validateSpec:Hi,objectElementValidators:{glyphs:Ki,\"*\":()=>[]}})),t.constants&&(r=r.concat(Ai({key:\"constants\",value:t.constants}))),Qi(r)}function Wi(t){return function(e){return t({...e,validateSpec:Hi})}}function Qi(t){return [].concat(t).sort(((t,e)=>t.line-e.line))}function ts(t){return function(...e){return Qi(t.apply(this,e))}}Ji.source=ts(Wi(Ni)),Ji.sprite=ts(Wi(Xi)),Ji.glyphs=ts(Wi(Ki)),Ji.light=ts(Wi(Ui)),Ji.sky=ts(Wi(qi)),Ji.terrain=ts(Wi(Gi)),Ji.state=ts(Wi(Zi)),Ji.layer=ts(Wi(Oi)),Ji.filter=ts(Wi(Vi)),Ji.paintProperty=ts(Wi($i)),Ji.layoutProperty=ts(Wi(Di));const es=Ji,rs=es.light,ns=es.sky,is=es.paintProperty,ss=es.layoutProperty;function as(t,e){let r=!1;if(e&&e.length)for(const n of e)t.fire(new vt(new Error(n.message))),r=!0;return r}class os{constructor(t,e,r){const n=this.cells=[];if(t instanceof ArrayBuffer){this.arrayBuffer=t;const i=new Int32Array(this.arrayBuffer);t=i[0],this.d=(e=i[1])+2*(r=i[2]);for(let t=0;t<this.d*this.d;t++){const e=i[3+t],r=i[3+t+1];n.push(e===r?null:i.subarray(e,r));}const s=i[3+n.length+1];this.keys=i.subarray(i[3+n.length],s),this.bboxes=i.subarray(s),this.insert=this._insertReadonly;}else {this.d=e+2*r;for(let t=0;t<this.d*this.d;t++)n.push([]);this.keys=[],this.bboxes=[];}this.n=e,this.extent=t,this.padding=r,this.scale=e/t,this.uid=0;const i=r/e*t;this.min=-i,this.max=t+i;}insert(t,e,r,n,i){this._forEachCell(e,r,n,i,this._insertCell,this.uid++,void 0,void 0),this.keys.push(t),this.bboxes.push(e),this.bboxes.push(r),this.bboxes.push(n),this.bboxes.push(i);}_insertReadonly(){throw new Error(\"Cannot insert into a GridIndex created from an ArrayBuffer.\")}_insertCell(t,e,r,n,i,s){this.cells[i].push(s);}query(t,e,r,n,i){const s=this.min,a=this.max;if(t<=s&&e<=s&&a<=r&&a<=n&&!i)return Array.prototype.slice.call(this.keys);{const s=[];return this._forEachCell(t,e,r,n,this._queryCell,s,{},i),s}}_queryCell(t,e,r,n,i,s,a,o){const l=this.cells[i];if(null!==l){const i=this.keys,u=this.bboxes;for(let c=0;c<l.length;c++){const h=l[c];if(void 0===a[h]){const l=4*h;(o?o(u[l+0],u[l+1],u[l+2],u[l+3]):t<=u[l+2]&&e<=u[l+3]&&r>=u[l+0]&&n>=u[l+1])?(a[h]=!0,s.push(i[h])):a[h]=!1;}}}}_forEachCell(t,e,r,n,i,s,a,o){const l=this._convertToCellCoord(t),u=this._convertToCellCoord(e),c=this._convertToCellCoord(r),h=this._convertToCellCoord(n);for(let p=l;p<=c;p++)for(let l=u;l<=h;l++){const u=this.d*l+p;if((!o||o(this._convertFromCellCoord(p),this._convertFromCellCoord(l),this._convertFromCellCoord(p+1),this._convertFromCellCoord(l+1)))&&i.call(this,t,e,r,n,u,s,a,o))return}}_convertFromCellCoord(t){return (t-this.padding)/this.scale}_convertToCellCoord(t){return Math.max(0,Math.min(this.d-1,Math.floor(t*this.scale)+this.padding))}toArrayBuffer(){if(this.arrayBuffer)return this.arrayBuffer;const t=this.cells,e=3+this.cells.length+1+1;let r=0;for(let t=0;t<this.cells.length;t++)r+=this.cells[t].length;const n=new Int32Array(e+r+this.keys.length+this.bboxes.length);n[0]=this.extent,n[1]=this.n,n[2]=this.padding;let i=e;for(let e=0;e<t.length;e++){const r=t[e];n[3+e]=i,n.set(r,i),i+=r.length;}return n[3+t.length]=i,n.set(this.keys,i),i+=this.keys.length,n[3+t.length+1]=i,n.set(this.bboxes,i),i+=this.bboxes.length,n.buffer}static serialize(t,e){const r=t.toArrayBuffer();return e&&e.push(r),{buffer:r}}static deserialize(t){return new os(t.buffer)}}const ls={};function us(t,e,r={}){if(ls[t])throw new Error(`${t} is already registered.`);Object.defineProperty(e,\"_classRegistryKey\",{value:t,writeable:!1}),ls[t]={klass:e,omit:r.omit||[],shallow:r.shallow||[]};}us(\"Object\",Object),us(\"Set\",Set),us(\"TransferableGridIndex\",os),us(\"Color\",Pe),us(\"Error\",Error),us(\"AJAXError\",pt),us(\"ResolvedImage\",Re),us(\"StylePropertyFunction\",ui),us(\"StyleExpression\",ni,{omit:[\"_evaluator\"]}),us(\"ZoomDependentExpression\",oi),us(\"ZoomConstantExpression\",ai),us(\"CompoundExpression\",Vn,{omit:[\"_evaluate\"]});for(const t in Tn)Tn[t]._classRegistryKey||us(`Expression_${t}`,Tn[t]);function cs(t){return t&&\"undefined\"!=typeof ArrayBuffer&&(t instanceof ArrayBuffer||t.constructor&&\"ArrayBuffer\"===t.constructor.name)}function hs(t){return t.$name||t.constructor._classRegistryKey}function ps(t){return !function(t){if(null===t||\"object\"!=typeof t)return !1;const e=hs(t);return !(!e||\"Object\"===e)}(t)&&(null==t||\"boolean\"==typeof t||\"number\"==typeof t||\"string\"==typeof t||t instanceof Boolean||t instanceof Number||t instanceof String||t instanceof Date||t instanceof RegExp||t instanceof Blob||t instanceof Error||cs(t)||J(t)||ArrayBuffer.isView(t)||t instanceof ImageData)}function fs(t,e){if(ps(t))return (cs(t)||J(t))&&e&&e.push(t),ArrayBuffer.isView(t)&&e&&e.push(t.buffer),t instanceof ImageData&&e&&e.push(t.data.buffer),t;if(Array.isArray(t)){const r=[];for(const n of t)r.push(fs(n,e));return r}if(\"object\"!=typeof t)throw new Error(\"can't serialize object of type \"+typeof t);const r=hs(t);if(!r)throw new Error(`can't serialize object of unregistered class ${t.constructor.name}`);if(!ls[r])throw new Error(`${r} is not registered.`);const{klass:n}=ls[r],i=n.serialize?n.serialize(t,e):{};if(n.serialize){if(e&&i===e[e.length-1])throw new Error(\"statically serialized object won't survive transfer of $name property\")}else {for(const n in t){if(!t.hasOwnProperty(n))continue;if(ls[r].omit.indexOf(n)>=0)continue;const s=t[n];i[n]=ls[r].shallow.indexOf(n)>=0?s:fs(s,e);}t instanceof Error&&(i.message=t.message);}if(i.$name)throw new Error(\"$name property is reserved for worker serialization logic.\");return \"Object\"!==r&&(i.$name=r),i}function ds(t){if(ps(t))return t;if(Array.isArray(t))return t.map(ds);if(\"object\"!=typeof t)throw new Error(\"can't deserialize object of type \"+typeof t);const e=hs(t)||\"Object\";if(!ls[e])throw new Error(`can't deserialize unregistered class ${e}`);const{klass:r}=ls[e];if(!r)throw new Error(`can't deserialize unregistered class ${e}`);if(r.deserialize)return r.deserialize(t);const n=Object.create(r.prototype);for(const r of Object.keys(t)){if(\"$name\"===r)continue;const i=t[r];n[r]=ls[e].shallow.indexOf(r)>=0?i:ds(i);}return n}class ys{constructor(){this.first=!0;}update(t,e){const r=Math.floor(t);return this.first?(this.first=!1,this.lastIntegerZoom=r,this.lastIntegerZoomTime=0,this.lastZoom=t,this.lastFloorZoom=r,!0):(this.lastFloorZoom>r?(this.lastIntegerZoom=r+1,this.lastIntegerZoomTime=e):this.lastFloorZoom<r&&(this.lastIntegerZoom=r,this.lastIntegerZoomTime=e),t!==this.lastZoom&&(this.lastZoom=t,this.lastFloorZoom=r,!0))}}const ms={\"Latin-1 Supplement\":t=>t>=128&&t<=255,\"Hangul Jamo\":t=>t>=4352&&t<=4607,Khmer:t=>t>=6016&&t<=6143,\"General Punctuation\":t=>t>=8192&&t<=8303,\"Letterlike Symbols\":t=>t>=8448&&t<=8527,\"Number Forms\":t=>t>=8528&&t<=8591,\"Miscellaneous Technical\":t=>t>=8960&&t<=9215,\"Control Pictures\":t=>t>=9216&&t<=9279,\"Optical Character Recognition\":t=>t>=9280&&t<=9311,\"Enclosed Alphanumerics\":t=>t>=9312&&t<=9471,\"Geometric Shapes\":t=>t>=9632&&t<=9727,\"Miscellaneous Symbols\":t=>t>=9728&&t<=9983,\"Miscellaneous Symbols and Arrows\":t=>t>=11008&&t<=11263,\"Ideographic Description Characters\":t=>t>=12272&&t<=12287,\"CJK Symbols and Punctuation\":t=>t>=12288&&t<=12351,Hiragana:t=>t>=12352&&t<=12447,Katakana:t=>t>=12448&&t<=12543,Kanbun:t=>t>=12688&&t<=12703,\"CJK Strokes\":t=>t>=12736&&t<=12783,\"Enclosed CJK Letters and Months\":t=>t>=12800&&t<=13055,\"CJK Compatibility\":t=>t>=13056&&t<=13311,\"Yijing Hexagram Symbols\":t=>t>=19904&&t<=19967,\"CJK Unified Ideographs\":t=>t>=19968&&t<=40959,\"Hangul Syllables\":t=>t>=44032&&t<=55215,\"Private Use Area\":t=>t>=57344&&t<=63743,\"Vertical Forms\":t=>t>=65040&&t<=65055,\"CJK Compatibility Forms\":t=>t>=65072&&t<=65103,\"Small Form Variants\":t=>t>=65104&&t<=65135,\"Halfwidth and Fullwidth Forms\":t=>t>=65280&&t<=65519};function gs(t){for(const e of t)if(Ss(e.charCodeAt(0)))return !0;return !1}function xs(t){for(const e of t)if(!ws(e.charCodeAt(0)))return !1;return !0}function vs(t){const e=t.map((t=>{try{return new RegExp(`\\\\p{sc=${t}}`,\"u\").source}catch(t){return null}})).filter((t=>t));return new RegExp(e.join(\"|\"),\"u\")}const bs=vs([\"Arab\",\"Dupl\",\"Mong\",\"Ougr\",\"Syrc\"]);function ws(t){return !bs.test(String.fromCodePoint(t))}const _s=vs([\"Bopo\",\"Hani\",\"Hira\",\"Kana\",\"Kits\",\"Nshu\",\"Tang\",\"Yiii\"]);function Ss(t){return !(746!==t&&747!==t&&(t<4352||!(ms[\"CJK Compatibility Forms\"](t)&&!(t>=65097&&t<=65103)||ms[\"CJK Compatibility\"](t)||ms[\"CJK Strokes\"](t)||!(!ms[\"CJK Symbols and Punctuation\"](t)||t>=12296&&t<=12305||t>=12308&&t<=12319||12336===t)||ms[\"Enclosed CJK Letters and Months\"](t)||ms[\"Ideographic Description Characters\"](t)||ms.Kanbun(t)||ms.Katakana(t)&&12540!==t||!(!ms[\"Halfwidth and Fullwidth Forms\"](t)||65288===t||65289===t||65293===t||t>=65306&&t<=65310||65339===t||65341===t||65343===t||t>=65371&&t<=65503||65507===t||t>=65512&&t<=65519)||!(!ms[\"Small Form Variants\"](t)||t>=65112&&t<=65118||t>=65123&&t<=65126)||ms[\"Vertical Forms\"](t)||ms[\"Yijing Hexagram Symbols\"](t)||/\\p{sc=Cans}/u.test(String.fromCodePoint(t))||/\\p{sc=Hang}/u.test(String.fromCodePoint(t))||_s.test(String.fromCodePoint(t)))))}function As(t){return !(Ss(t)||function(t){return !!(ms[\"Latin-1 Supplement\"](t)&&(167===t||169===t||174===t||177===t||188===t||189===t||190===t||215===t||247===t)||ms[\"General Punctuation\"](t)&&(8214===t||8224===t||8225===t||8240===t||8241===t||8251===t||8252===t||8258===t||8263===t||8264===t||8265===t||8273===t)||ms[\"Letterlike Symbols\"](t)||ms[\"Number Forms\"](t)||ms[\"Miscellaneous Technical\"](t)&&(t>=8960&&t<=8967||t>=8972&&t<=8991||t>=8996&&t<=9e3||9003===t||t>=9085&&t<=9114||t>=9150&&t<=9165||9167===t||t>=9169&&t<=9179||t>=9186&&t<=9215)||ms[\"Control Pictures\"](t)&&9251!==t||ms[\"Optical Character Recognition\"](t)||ms[\"Enclosed Alphanumerics\"](t)||ms[\"Geometric Shapes\"](t)||ms[\"Miscellaneous Symbols\"](t)&&!(t>=9754&&t<=9759)||ms[\"Miscellaneous Symbols and Arrows\"](t)&&(t>=11026&&t<=11055||t>=11088&&t<=11097||t>=11192&&t<=11243)||ms[\"CJK Symbols and Punctuation\"](t)||ms.Katakana(t)||ms[\"Private Use Area\"](t)||ms[\"CJK Compatibility Forms\"](t)||ms[\"Small Form Variants\"](t)||ms[\"Halfwidth and Fullwidth Forms\"](t)||8734===t||8756===t||8757===t||t>=9984&&t<=10087||t>=10102&&t<=10131||65532===t||65533===t)}(t))}const ks=vs([\"Adlm\",\"Arab\",\"Armi\",\"Avst\",\"Chrs\",\"Cprt\",\"Egyp\",\"Elym\",\"Gara\",\"Hatr\",\"Hebr\",\"Hung\",\"Khar\",\"Lydi\",\"Mand\",\"Mani\",\"Mend\",\"Merc\",\"Mero\",\"Narb\",\"Nbat\",\"Nkoo\",\"Orkh\",\"Palm\",\"Phli\",\"Phlp\",\"Phnx\",\"Prti\",\"Rohg\",\"Samr\",\"Sarb\",\"Sogo\",\"Syrc\",\"Thaa\",\"Todr\",\"Yezi\"]);function Ms(t){return ks.test(String.fromCodePoint(t))}function Is(t,e){return !(!e&&Ms(t)||t>=2304&&t<=3583||t>=3840&&t<=4255||ms.Khmer(t))}function zs(t){for(const e of t)if(Ms(e.charCodeAt(0)))return !0;return !1}const Ps=new class{constructor(){this.TIMEOUT=5e3,this.applyArabicShaping=null,this.processBidirectionalText=null,this.processStyledBidirectionalText=null,this.pluginStatus=\"unavailable\",this.pluginURL=null,this.loadScriptResolve=()=>{};}setState(t){this.pluginStatus=t.pluginStatus,this.pluginURL=t.pluginURL;}getState(){return {pluginStatus:this.pluginStatus,pluginURL:this.pluginURL}}setMethods(t){if(Ps.isParsed())throw new Error(\"RTL text plugin already registered.\");this.applyArabicShaping=t.applyArabicShaping,this.processBidirectionalText=t.processBidirectionalText,this.processStyledBidirectionalText=t.processStyledBidirectionalText,this.loadScriptResolve();}isParsed(){return null!=this.applyArabicShaping&&null!=this.processBidirectionalText&&null!=this.processStyledBidirectionalText}getRTLTextPluginStatus(){return this.pluginStatus}syncState(t,r){return e(this,void 0,void 0,(function*(){if(this.isParsed())return this.getState();if(\"loading\"!==t.pluginStatus)return this.setState(t),t;const e=t.pluginURL,n=new Promise((t=>{this.loadScriptResolve=t;}));r(e);const i=new Promise((t=>setTimeout((()=>t()),this.TIMEOUT)));if(yield Promise.race([n,i]),this.isParsed()){const t={pluginStatus:\"loaded\",pluginURL:e};return this.setState(t),t}throw this.setState({pluginStatus:\"error\",pluginURL:\"\"}),new Error(`RTL Text Plugin failed to import scripts from ${e}`)}))}};class Cs{constructor(t,e){this.zoom=t,e?(this.now=e.now||0,this.fadeDuration=e.fadeDuration||0,this.zoomHistory=e.zoomHistory||new ys,this.transition=e.transition||{},this.globalState=e.globalState||{}):(this.now=0,this.fadeDuration=0,this.zoomHistory=new ys,this.transition={},this.globalState={});}isSupportedScript(t){return function(t,e){for(const r of t)if(!Is(r.charCodeAt(0),e))return !1;return !0}(t,\"loaded\"===Ps.getRTLTextPluginStatus())}crossFadingFactor(){return 0===this.fadeDuration?1:Math.min((this.now-this.zoomHistory.lastIntegerZoomTime)/this.fadeDuration,1)}getCrossfadeParameters(){const t=this.zoom,e=t-Math.floor(t),r=this.crossFadingFactor();return t>this.zoomHistory.lastIntegerZoom?{fromScale:2,toScale:1,t:e+(1-e)*r}:{fromScale:.5,toScale:1,t:1-(1-r)*e}}}class Bs{constructor(t,e){this.property=t,this.value=e,this.expression=function(t,e){if(Hn(t))return new ui(t,e);if(ii(t)){const r=li(t,e);if(\"error\"===r.result)throw new Error(r.value.map((t=>`${t.key}: ${t.message}`)).join(\", \"));return r.value}{let r=t;return \"color\"===e.type&&\"string\"==typeof t?r=Pe.parse(t):\"padding\"!==e.type||\"number\"!=typeof t&&!Array.isArray(t)?\"numberArray\"!==e.type||\"number\"!=typeof t&&!Array.isArray(t)?\"colorArray\"!==e.type||\"string\"!=typeof t&&!Array.isArray(t)?\"variableAnchorOffsetCollection\"===e.type&&Array.isArray(t)?r=Oe.parse(t):\"projectionDefinition\"===e.type&&\"string\"==typeof t&&(r=je.parse(t)):r=Le.parse(t):r=Fe.parse(t):r=Ve.parse(t),{globalStateRefs:new Set,kind:\"constant\",evaluate:()=>r}}}(void 0===e?t.specification.default:e,t.specification);}isDataDriven(){return \"source\"===this.expression.kind||\"composite\"===this.expression.kind}getGlobalStateRefs(){return this.expression.globalStateRefs||new Set}possiblyEvaluate(t,e,r){return this.property.possiblyEvaluate(this,t,e,r)}}class Es{constructor(t){this.property=t,this.value=new Bs(t,void 0);}transitioned(t,e){return new Vs(this.property,this.value,e,j({},t.transition,this.transition),t.now)}untransitioned(){return new Vs(this.property,this.value,null,{},0)}}class Ts{constructor(t){this._properties=t,this._values=Object.create(t.defaultTransitionablePropertyValues);}getValue(t){return G(this._values[t].value.value)}setValue(t,e){Object.prototype.hasOwnProperty.call(this._values,t)||(this._values[t]=new Es(this._values[t].property)),this._values[t].value=new Bs(this._values[t].property,null===e?void 0:G(e));}getTransition(t){return G(this._values[t].transition)}setTransition(t,e){Object.prototype.hasOwnProperty.call(this._values,t)||(this._values[t]=new Es(this._values[t].property)),this._values[t].transition=G(e)||void 0;}serialize(){const t={};for(const e of Object.keys(this._values)){const r=this.getValue(e);void 0!==r&&(t[e]=r);const n=this.getTransition(e);void 0!==n&&(t[`${e}-transition`]=n);}return t}transitioned(t,e){const r=new Fs(this._properties);for(const n of Object.keys(this._values))r._values[n]=this._values[n].transitioned(t,e._values[n]);return r}untransitioned(){const t=new Fs(this._properties);for(const e of Object.keys(this._values))t._values[e]=this._values[e].untransitioned();return t}}class Vs{constructor(t,e,r,n,i){this.property=t,this.value=e,this.begin=i+n.delay||0,this.end=this.begin+n.duration||0,t.specification.transition&&(n.delay||n.duration)&&(this.prior=r);}possiblyEvaluate(t,e,r){const n=t.now||0,i=this.value.possiblyEvaluate(t,e,r),s=this.prior;if(s){if(n>this.end)return this.prior=null,i;if(this.value.isDataDriven())return this.prior=null,i;if(n<this.begin)return s.possiblyEvaluate(t,e,r);{const a=(n-this.begin)/(this.end-this.begin);return this.property.interpolate(s.possiblyEvaluate(t,e,r),i,L(a))}}return i}}class Fs{constructor(t){this._properties=t,this._values=Object.create(t.defaultTransitioningPropertyValues);}possiblyEvaluate(t,e,r){const n=new Ds(this._properties);for(const i of Object.keys(this._values))n._values[i]=this._values[i].possiblyEvaluate(t,e,r);return n}hasTransition(){for(const t of Object.keys(this._values))if(this._values[t].prior)return !0;return !1}}class Ls{constructor(t){this._properties=t,this._values=Object.create(t.defaultPropertyValues);}hasValue(t){return void 0!==this._values[t].value}getValue(t){return G(this._values[t].value)}setValue(t,e){this._values[t]=new Bs(this._values[t].property,null===e?void 0:G(e));}serialize(){const t={};for(const e of Object.keys(this._values)){const r=this.getValue(e);void 0!==r&&(t[e]=r);}return t}possiblyEvaluate(t,e,r){const n=new Ds(this._properties);for(const i of Object.keys(this._values))n._values[i]=this._values[i].possiblyEvaluate(t,e,r);return n}}class $s{constructor(t,e,r){this.property=t,this.value=e,this.parameters=r;}isConstant(){return \"constant\"===this.value.kind}constantOr(t){return \"constant\"===this.value.kind?this.value.value:t}evaluate(t,e,r,n){return this.property.evaluate(this.value,this.parameters,t,e,r,n)}}class Ds{constructor(t){this._properties=t,this._values=Object.create(t.defaultPossiblyEvaluatedValues);}get(t){return this._values[t]}}class Os{constructor(t){this.specification=t;}possiblyEvaluate(t,e){if(t.isDataDriven())throw new Error(\"Value should not be data driven\");return t.expression.evaluate(e)}interpolate(t,e,r){const n=gr[this.specification.type];return n?n(t,e,r):t}}class Rs{constructor(t,e){this.specification=t,this.overrides=e;}possiblyEvaluate(t,e,r,n){return new $s(this,\"constant\"===t.expression.kind||\"camera\"===t.expression.kind?{kind:\"constant\",value:t.expression.evaluate(e,null,{},r,n)}:t.expression,e)}interpolate(t,e,r){if(\"constant\"!==t.value.kind||\"constant\"!==e.value.kind)return t;if(void 0===t.value.value||void 0===e.value.value)return new $s(this,{kind:\"constant\",value:void 0},t.parameters);const n=gr[this.specification.type];if(n){const i=n(t.value.value,e.value.value,r);return new $s(this,{kind:\"constant\",value:i},t.parameters)}return t}evaluate(t,e,r,n,i,s){return \"constant\"===t.kind?t.value:t.evaluate(e,r,n,i,s)}}class js extends Rs{possiblyEvaluate(t,e,r,n){if(void 0===t.value)return new $s(this,{kind:\"constant\",value:void 0},e);if(\"constant\"===t.expression.kind){const i=t.expression.evaluate(e,null,{},r,n),s=\"resolvedImage\"===t.property.specification.type&&\"string\"!=typeof i?i.name:i,a=this._calculate(s,s,s,e);return new $s(this,{kind:\"constant\",value:a},e)}if(\"camera\"===t.expression.kind){const r=this._calculate(t.expression.evaluate({zoom:e.zoom-1}),t.expression.evaluate({zoom:e.zoom}),t.expression.evaluate({zoom:e.zoom+1}),e);return new $s(this,{kind:\"constant\",value:r},e)}return new $s(this,t.expression,e)}evaluate(t,e,r,n,i,s){if(\"source\"===t.kind){const a=t.evaluate(e,r,n,i,s);return this._calculate(a,a,a,e)}return \"composite\"===t.kind?this._calculate(t.evaluate({zoom:Math.floor(e.zoom)-1},r,n),t.evaluate({zoom:Math.floor(e.zoom)},r,n),t.evaluate({zoom:Math.floor(e.zoom)+1},r,n),e):t.value}_calculate(t,e,r,n){return n.zoom>n.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:r,to:e}}interpolate(t){return t}}class Ns{constructor(t){this.specification=t;}possiblyEvaluate(t,e,r,n){if(void 0!==t.value){if(\"constant\"===t.expression.kind){const i=t.expression.evaluate(e,null,{},r,n);return this._calculate(i,i,i,e)}return this._calculate(t.expression.evaluate(new Cs(Math.floor(e.zoom-1),e)),t.expression.evaluate(new Cs(Math.floor(e.zoom),e)),t.expression.evaluate(new Cs(Math.floor(e.zoom+1),e)),e)}}_calculate(t,e,r,n){return n.zoom>n.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:r,to:e}}interpolate(t){return t}}class Us{constructor(t){this.specification=t;}possiblyEvaluate(t,e,r,n){return !!t.expression.evaluate(e,null,{},r,n)}interpolate(){return !1}}class qs{constructor(t){this.properties=t,this.defaultPropertyValues={},this.defaultTransitionablePropertyValues={},this.defaultTransitioningPropertyValues={},this.defaultPossiblyEvaluatedValues={},this.overridableProperties=[];for(const e in t){const r=t[e];r.specification.overridable&&this.overridableProperties.push(e);const n=this.defaultPropertyValues[e]=new Bs(r,void 0),i=this.defaultTransitionablePropertyValues[e]=new Es(r);this.defaultTransitioningPropertyValues[e]=i.untransitioned(),this.defaultPossiblyEvaluatedValues[e]=n.possiblyEvaluate({});}}}us(\"DataDrivenProperty\",Rs),us(\"DataConstantProperty\",Os),us(\"CrossFadedDataDrivenProperty\",js),us(\"CrossFadedProperty\",Ns),us(\"ColorRampProperty\",Us);const Gs=\"-transition\";class Xs extends bt{constructor(t,e){if(super(),this.id=t.id,this.type=t.type,this._featureFilter={filter:()=>!0,needGeometry:!1,getGlobalStateRefs:()=>new Set},\"custom\"!==t.type&&(this.metadata=t.metadata,this.minzoom=t.minzoom,this.maxzoom=t.maxzoom,\"background\"!==t.type&&(this.source=t.source,this.sourceLayer=t[\"source-layer\"],this.filter=t.filter,this._featureFilter=di(t.filter)),e.layout&&(this._unevaluatedLayout=new Ls(e.layout)),e.paint)){this._transitionablePaint=new Ts(e.paint);for(const e in t.paint)this.setPaintProperty(e,t.paint[e],{validate:!1});for(const e in t.layout)this.setLayoutProperty(e,t.layout[e],{validate:!1});this._transitioningPaint=this._transitionablePaint.untransitioned(),this.paint=new Ds(e.paint);}}setFilter(t){this.filter=t,this._featureFilter=di(t);}getCrossfadeParameters(){return this._crossfadeParameters}getLayoutProperty(t){return \"visibility\"===t?this.visibility:this._unevaluatedLayout.getValue(t)}getLayoutAffectingGlobalStateRefs(){const t=new Set;if(this._unevaluatedLayout)for(const e in this._unevaluatedLayout._values){const r=this._unevaluatedLayout._values[e];for(const e of r.getGlobalStateRefs())t.add(e);}for(const e of this._featureFilter.getGlobalStateRefs())t.add(e);return t}setLayoutProperty(t,e,r={}){null!=e&&this._validate(ss,`layers.${this.id}.layout.${t}`,t,e,r)||(\"visibility\"!==t?this._unevaluatedLayout.setValue(t,e):this.visibility=e);}getPaintProperty(t){return t.endsWith(Gs)?this._transitionablePaint.getTransition(t.slice(0,-11)):this._transitionablePaint.getValue(t)}setPaintProperty(t,e,r={}){if(null!=e&&this._validate(is,`layers.${this.id}.paint.${t}`,t,e,r))return !1;if(t.endsWith(Gs))return this._transitionablePaint.setTransition(t.slice(0,-11),e||void 0),!1;{const r=this._transitionablePaint._values[t],n=\"cross-faded-data-driven\"===r.property.specification[\"property-type\"],i=r.value.isDataDriven(),s=r.value;this._transitionablePaint.setValue(t,e),this._handleSpecialPaintPropertyUpdate(t);const a=this._transitionablePaint._values[t].value;return a.isDataDriven()||i||n||this._handleOverridablePaintPropertyUpdate(t,s,a)}}_handleSpecialPaintPropertyUpdate(t){}_handleOverridablePaintPropertyUpdate(t,e,r){return !1}isHidden(t){return !!(this.minzoom&&t<this.minzoom)||!!(this.maxzoom&&t>=this.maxzoom)||\"none\"===this.visibility}updateTransitions(t){this._transitioningPaint=this._transitionablePaint.transitioned(t,this._transitioningPaint);}hasTransition(){return this._transitioningPaint.hasTransition()}recalculate(t,e){t.getCrossfadeParameters&&(this._crossfadeParameters=t.getCrossfadeParameters()),this._unevaluatedLayout&&(this.layout=this._unevaluatedLayout.possiblyEvaluate(t,void 0,e)),this.paint=this._transitioningPaint.possiblyEvaluate(t,void 0,e);}serialize(){const t={id:this.id,type:this.type,source:this.source,\"source-layer\":this.sourceLayer,metadata:this.metadata,minzoom:this.minzoom,maxzoom:this.maxzoom,filter:this.filter,layout:this._unevaluatedLayout&&this._unevaluatedLayout.serialize(),paint:this._transitionablePaint&&this._transitionablePaint.serialize()};return this.visibility&&(t.layout=t.layout||{},t.layout.visibility=this.visibility),q(t,((t,e)=>!(void 0===t||\"layout\"===e&&!Object.keys(t).length||\"paint\"===e&&!Object.keys(t).length)))}_validate(t,e,r,n,i={}){return (!i||!1!==i.validate)&&as(this,t.call(es,{key:e,layerType:this.type,objectKey:r,value:n,styleSpec:wt,style:{glyphs:!0,sprite:!0}}))}is3D(){return !1}isTileClipped(){return !1}hasOffscreenPass(){return !1}resize(){}isStateDependent(){for(const t in this.paint._values){const e=this.paint.get(t);if(e instanceof $s&&Gn(e.property.specification)&&(\"source\"===e.value.kind||\"composite\"===e.value.kind)&&e.value.isStateDependent)return !0}return !1}}const Zs={Int8:Int8Array,Uint8:Uint8Array,Int16:Int16Array,Uint16:Uint16Array,Int32:Int32Array,Uint32:Uint32Array,Float32:Float32Array};class Ys{constructor(t,e){this._structArray=t,this._pos1=e*this.size,this._pos2=this._pos1/2,this._pos4=this._pos1/4,this._pos8=this._pos1/8;}}class Hs{constructor(){this.isTransferred=!1,this.capacity=-1,this.resize(0);}static serialize(t,e){return t._trim(),e&&(t.isTransferred=!0,e.push(t.arrayBuffer)),{length:t.length,arrayBuffer:t.arrayBuffer}}static deserialize(t){const e=Object.create(this.prototype);return e.arrayBuffer=t.arrayBuffer,e.length=t.length,e.capacity=t.arrayBuffer.byteLength/e.bytesPerElement,e._refreshViews(),e}_trim(){this.length!==this.capacity&&(this.capacity=this.length,this.arrayBuffer=this.arrayBuffer.slice(0,this.length*this.bytesPerElement),this._refreshViews());}clear(){this.length=0;}resize(t){this.reserve(t),this.length=t;}reserve(t){if(t>this.capacity){this.capacity=Math.max(t,Math.floor(5*this.capacity),128),this.arrayBuffer=new ArrayBuffer(this.capacity*this.bytesPerElement);const e=this.uint8;this._refreshViews(),e&&this.uint8.set(e);}}_refreshViews(){throw new Error(\"_refreshViews() must be implemented by each concrete StructArray layout\")}}function Ks(t,e=1){let r=0,n=0;return {members:t.map((t=>{const i=Zs[t.type].BYTES_PER_ELEMENT,s=r=Js(r,Math.max(e,i)),a=t.components||1;return n=Math.max(n,i),r+=i*a,{name:t.name,type:t.type,components:a,offset:s}})),size:Js(r,Math.max(n,e)),alignment:e}}function Js(t,e){return Math.ceil(t/e)*e}class Ws extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e){const r=this.length;return this.resize(r+1),this.emplace(r,t,e)}emplace(t,e,r){const n=2*t;return this.int16[n+0]=e,this.int16[n+1]=r,t}}Ws.prototype.bytesPerElement=4,us(\"StructArrayLayout2i4\",Ws);class Qs extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,r)}emplace(t,e,r,n){const i=3*t;return this.int16[i+0]=e,this.int16[i+1]=r,this.int16[i+2]=n,t}}Qs.prototype.bytesPerElement=6,us(\"StructArrayLayout3i6\",Qs);class ta extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n){const i=this.length;return this.resize(i+1),this.emplace(i,t,e,r,n)}emplace(t,e,r,n,i){const s=4*t;return this.int16[s+0]=e,this.int16[s+1]=r,this.int16[s+2]=n,this.int16[s+3]=i,t}}ta.prototype.bytesPerElement=8,us(\"StructArrayLayout4i8\",ta);class ea extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s){const a=this.length;return this.resize(a+1),this.emplace(a,t,e,r,n,i,s)}emplace(t,e,r,n,i,s,a){const o=6*t;return this.int16[o+0]=e,this.int16[o+1]=r,this.int16[o+2]=n,this.int16[o+3]=i,this.int16[o+4]=s,this.int16[o+5]=a,t}}ea.prototype.bytesPerElement=12,us(\"StructArrayLayout2i4i12\",ea);class ra extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s){const a=this.length;return this.resize(a+1),this.emplace(a,t,e,r,n,i,s)}emplace(t,e,r,n,i,s,a){const o=4*t,l=8*t;return this.int16[o+0]=e,this.int16[o+1]=r,this.uint8[l+4]=n,this.uint8[l+5]=i,this.uint8[l+6]=s,this.uint8[l+7]=a,t}}ra.prototype.bytesPerElement=8,us(\"StructArrayLayout2i4ub8\",ra);class na extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e){const r=this.length;return this.resize(r+1),this.emplace(r,t,e)}emplace(t,e,r){const n=2*t;return this.float32[n+0]=e,this.float32[n+1]=r,t}}na.prototype.bytesPerElement=8,us(\"StructArrayLayout2f8\",na);class ia extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s,a,o,l,u){const c=this.length;return this.resize(c+1),this.emplace(c,t,e,r,n,i,s,a,o,l,u)}emplace(t,e,r,n,i,s,a,o,l,u,c){const h=10*t;return this.uint16[h+0]=e,this.uint16[h+1]=r,this.uint16[h+2]=n,this.uint16[h+3]=i,this.uint16[h+4]=s,this.uint16[h+5]=a,this.uint16[h+6]=o,this.uint16[h+7]=l,this.uint16[h+8]=u,this.uint16[h+9]=c,t}}ia.prototype.bytesPerElement=20,us(\"StructArrayLayout10ui20\",ia);class sa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s,a,o,l,u,c,h){const p=this.length;return this.resize(p+1),this.emplace(p,t,e,r,n,i,s,a,o,l,u,c,h)}emplace(t,e,r,n,i,s,a,o,l,u,c,h,p){const f=12*t;return this.int16[f+0]=e,this.int16[f+1]=r,this.int16[f+2]=n,this.int16[f+3]=i,this.uint16[f+4]=s,this.uint16[f+5]=a,this.uint16[f+6]=o,this.uint16[f+7]=l,this.int16[f+8]=u,this.int16[f+9]=c,this.int16[f+10]=h,this.int16[f+11]=p,t}}sa.prototype.bytesPerElement=24,us(\"StructArrayLayout4i4ui4i24\",sa);class aa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,r)}emplace(t,e,r,n){const i=3*t;return this.float32[i+0]=e,this.float32[i+1]=r,this.float32[i+2]=n,t}}aa.prototype.bytesPerElement=12,us(\"StructArrayLayout3f12\",aa);class oa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint32[1*t+0]=e,t}}oa.prototype.bytesPerElement=4,us(\"StructArrayLayout1ul4\",oa);class la extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s,a,o,l){const u=this.length;return this.resize(u+1),this.emplace(u,t,e,r,n,i,s,a,o,l)}emplace(t,e,r,n,i,s,a,o,l,u){const c=10*t,h=5*t;return this.int16[c+0]=e,this.int16[c+1]=r,this.int16[c+2]=n,this.int16[c+3]=i,this.int16[c+4]=s,this.int16[c+5]=a,this.uint32[h+3]=o,this.uint16[c+8]=l,this.uint16[c+9]=u,t}}la.prototype.bytesPerElement=20,us(\"StructArrayLayout6i1ul2ui20\",la);class ua extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s){const a=this.length;return this.resize(a+1),this.emplace(a,t,e,r,n,i,s)}emplace(t,e,r,n,i,s,a){const o=6*t;return this.int16[o+0]=e,this.int16[o+1]=r,this.int16[o+2]=n,this.int16[o+3]=i,this.int16[o+4]=s,this.int16[o+5]=a,t}}ua.prototype.bytesPerElement=12,us(\"StructArrayLayout2i2i2i12\",ua);class ca extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i){const s=this.length;return this.resize(s+1),this.emplace(s,t,e,r,n,i)}emplace(t,e,r,n,i,s){const a=4*t,o=8*t;return this.float32[a+0]=e,this.float32[a+1]=r,this.float32[a+2]=n,this.int16[o+6]=i,this.int16[o+7]=s,t}}ca.prototype.bytesPerElement=16,us(\"StructArrayLayout2f1f2i16\",ca);class ha extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s){const a=this.length;return this.resize(a+1),this.emplace(a,t,e,r,n,i,s)}emplace(t,e,r,n,i,s,a){const o=16*t,l=4*t,u=8*t;return this.uint8[o+0]=e,this.uint8[o+1]=r,this.float32[l+1]=n,this.float32[l+2]=i,this.int16[u+6]=s,this.int16[u+7]=a,t}}ha.prototype.bytesPerElement=16,us(\"StructArrayLayout2ub2f2i16\",ha);class pa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,r)}emplace(t,e,r,n){const i=3*t;return this.uint16[i+0]=e,this.uint16[i+1]=r,this.uint16[i+2]=n,t}}pa.prototype.bytesPerElement=6,us(\"StructArrayLayout3ui6\",pa);class fa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m){const g=this.length;return this.resize(g+1),this.emplace(g,t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m)}emplace(t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g){const x=24*t,v=12*t,b=48*t;return this.int16[x+0]=e,this.int16[x+1]=r,this.uint16[x+2]=n,this.uint16[x+3]=i,this.uint32[v+2]=s,this.uint32[v+3]=a,this.uint32[v+4]=o,this.uint16[x+10]=l,this.uint16[x+11]=u,this.uint16[x+12]=c,this.float32[v+7]=h,this.float32[v+8]=p,this.uint8[b+36]=f,this.uint8[b+37]=d,this.uint8[b+38]=y,this.uint32[v+10]=m,this.int16[x+22]=g,t}}fa.prototype.bytesPerElement=48,us(\"StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48\",fa);class da extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,k,M,I){const z=this.length;return this.resize(z+1),this.emplace(z,t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,k,M,I)}emplace(t,e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,k,M,I,z){const P=32*t,C=16*t;return this.int16[P+0]=e,this.int16[P+1]=r,this.int16[P+2]=n,this.int16[P+3]=i,this.int16[P+4]=s,this.int16[P+5]=a,this.int16[P+6]=o,this.int16[P+7]=l,this.uint16[P+8]=u,this.uint16[P+9]=c,this.uint16[P+10]=h,this.uint16[P+11]=p,this.uint16[P+12]=f,this.uint16[P+13]=d,this.uint16[P+14]=y,this.uint16[P+15]=m,this.uint16[P+16]=g,this.uint16[P+17]=x,this.uint16[P+18]=v,this.uint16[P+19]=b,this.uint16[P+20]=w,this.uint16[P+21]=_,this.uint16[P+22]=S,this.uint32[C+12]=A,this.float32[C+13]=k,this.float32[C+14]=M,this.uint16[P+30]=I,this.uint16[P+31]=z,t}}da.prototype.bytesPerElement=64,us(\"StructArrayLayout8i15ui1ul2f2ui64\",da);class ya extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.float32[1*t+0]=e,t}}ya.prototype.bytesPerElement=4,us(\"StructArrayLayout1f4\",ya);class ma extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,r)}emplace(t,e,r,n){const i=3*t;return this.uint16[6*t+0]=e,this.float32[i+1]=r,this.float32[i+2]=n,t}}ma.prototype.bytesPerElement=12,us(\"StructArrayLayout1ui2f12\",ma);class ga extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,r)}emplace(t,e,r,n){const i=4*t;return this.uint32[2*t+0]=e,this.uint16[i+2]=r,this.uint16[i+3]=n,t}}ga.prototype.bytesPerElement=8,us(\"StructArrayLayout1ul2ui8\",ga);class xa extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e){const r=this.length;return this.resize(r+1),this.emplace(r,t,e)}emplace(t,e,r){const n=2*t;return this.uint16[n+0]=e,this.uint16[n+1]=r,t}}xa.prototype.bytesPerElement=4,us(\"StructArrayLayout2ui4\",xa);class va extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint16[1*t+0]=e,t}}va.prototype.bytesPerElement=2,us(\"StructArrayLayout1ui2\",va);class ba extends Hs{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,r,n){const i=this.length;return this.resize(i+1),this.emplace(i,t,e,r,n)}emplace(t,e,r,n,i){const s=4*t;return this.float32[s+0]=e,this.float32[s+1]=r,this.float32[s+2]=n,this.float32[s+3]=i,t}}ba.prototype.bytesPerElement=16,us(\"StructArrayLayout4f16\",ba);class wa extends Ys{get anchorPointX(){return this._structArray.int16[this._pos2+0]}get anchorPointY(){return this._structArray.int16[this._pos2+1]}get x1(){return this._structArray.int16[this._pos2+2]}get y1(){return this._structArray.int16[this._pos2+3]}get x2(){return this._structArray.int16[this._pos2+4]}get y2(){return this._structArray.int16[this._pos2+5]}get featureIndex(){return this._structArray.uint32[this._pos4+3]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+8]}get bucketIndex(){return this._structArray.uint16[this._pos2+9]}get anchorPoint(){return new l(this.anchorPointX,this.anchorPointY)}}wa.prototype.size=20;class _a extends la{get(t){return new wa(this,t)}}us(\"CollisionBoxArray\",_a);class Sa extends Ys{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get glyphStartIndex(){return this._structArray.uint16[this._pos2+2]}get numGlyphs(){return this._structArray.uint16[this._pos2+3]}get vertexStartIndex(){return this._structArray.uint32[this._pos4+2]}get lineStartIndex(){return this._structArray.uint32[this._pos4+3]}get lineLength(){return this._structArray.uint32[this._pos4+4]}get segment(){return this._structArray.uint16[this._pos2+10]}get lowerSize(){return this._structArray.uint16[this._pos2+11]}get upperSize(){return this._structArray.uint16[this._pos2+12]}get lineOffsetX(){return this._structArray.float32[this._pos4+7]}get lineOffsetY(){return this._structArray.float32[this._pos4+8]}get writingMode(){return this._structArray.uint8[this._pos1+36]}get placedOrientation(){return this._structArray.uint8[this._pos1+37]}set placedOrientation(t){this._structArray.uint8[this._pos1+37]=t;}get hidden(){return this._structArray.uint8[this._pos1+38]}set hidden(t){this._structArray.uint8[this._pos1+38]=t;}get crossTileID(){return this._structArray.uint32[this._pos4+10]}set crossTileID(t){this._structArray.uint32[this._pos4+10]=t;}get associatedIconIndex(){return this._structArray.int16[this._pos2+22]}}Sa.prototype.size=48;class Aa extends fa{get(t){return new Sa(this,t)}}us(\"PlacedSymbolArray\",Aa);class ka extends Ys{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get rightJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+2]}get centerJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+3]}get leftJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+4]}get verticalPlacedTextSymbolIndex(){return this._structArray.int16[this._pos2+5]}get placedIconSymbolIndex(){return this._structArray.int16[this._pos2+6]}get verticalPlacedIconSymbolIndex(){return this._structArray.int16[this._pos2+7]}get key(){return this._structArray.uint16[this._pos2+8]}get textBoxStartIndex(){return this._structArray.uint16[this._pos2+9]}get textBoxEndIndex(){return this._structArray.uint16[this._pos2+10]}get verticalTextBoxStartIndex(){return this._structArray.uint16[this._pos2+11]}get verticalTextBoxEndIndex(){return this._structArray.uint16[this._pos2+12]}get iconBoxStartIndex(){return this._structArray.uint16[this._pos2+13]}get iconBoxEndIndex(){return this._structArray.uint16[this._pos2+14]}get verticalIconBoxStartIndex(){return this._structArray.uint16[this._pos2+15]}get verticalIconBoxEndIndex(){return this._structArray.uint16[this._pos2+16]}get featureIndex(){return this._structArray.uint16[this._pos2+17]}get numHorizontalGlyphVertices(){return this._structArray.uint16[this._pos2+18]}get numVerticalGlyphVertices(){return this._structArray.uint16[this._pos2+19]}get numIconVertices(){return this._structArray.uint16[this._pos2+20]}get numVerticalIconVertices(){return this._structArray.uint16[this._pos2+21]}get useRuntimeCollisionCircles(){return this._structArray.uint16[this._pos2+22]}get crossTileID(){return this._structArray.uint32[this._pos4+12]}set crossTileID(t){this._structArray.uint32[this._pos4+12]=t;}get textBoxScale(){return this._structArray.float32[this._pos4+13]}get collisionCircleDiameter(){return this._structArray.float32[this._pos4+14]}get textAnchorOffsetStartIndex(){return this._structArray.uint16[this._pos2+30]}get textAnchorOffsetEndIndex(){return this._structArray.uint16[this._pos2+31]}}ka.prototype.size=64;class Ma extends da{get(t){return new ka(this,t)}}us(\"SymbolInstanceArray\",Ma);class Ia extends ya{getoffsetX(t){return this.float32[1*t+0]}}us(\"GlyphOffsetArray\",Ia);class za extends Qs{getx(t){return this.int16[3*t+0]}gety(t){return this.int16[3*t+1]}gettileUnitDistanceFromAnchor(t){return this.int16[3*t+2]}}us(\"SymbolLineVertexArray\",za);class Pa extends Ys{get textAnchor(){return this._structArray.uint16[this._pos2+0]}get textOffset0(){return this._structArray.float32[this._pos4+1]}get textOffset1(){return this._structArray.float32[this._pos4+2]}}Pa.prototype.size=12;class Ca extends ma{get(t){return new Pa(this,t)}}us(\"TextAnchorOffsetArray\",Ca);class Ba extends Ys{get featureIndex(){return this._structArray.uint32[this._pos4+0]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+2]}get bucketIndex(){return this._structArray.uint16[this._pos2+3]}}Ba.prototype.size=8;class Ea extends ga{get(t){return new Ba(this,t)}}us(\"FeatureIndexArray\",Ea);class Ta extends Ws{}class Va extends Ws{}class Fa extends Ws{}class La extends ea{}class $a extends ra{}class Da extends na{}class Oa extends ia{}class Ra extends sa{}class ja extends aa{}class Na extends oa{}class Ua extends ua{}class qa extends ha{}class Ga extends pa{}class Xa extends xa{}const Za=Ks([{name:\"a_pos\",components:2,type:\"Int16\"}],4),{members:Ya}=Za;class Ha{constructor(t=[]){this._forceNewSegmentOnNextPrepare=!1,this.segments=t;}prepareSegment(t,e,r,n){const i=this.segments[this.segments.length-1];return t>Ha.MAX_VERTEX_ARRAY_LENGTH&&Z(`Max vertices per segment is ${Ha.MAX_VERTEX_ARRAY_LENGTH}: bucket requested ${t}. Consider using the \\`fillLargeMeshArrays\\` function if you require meshes with more than ${Ha.MAX_VERTEX_ARRAY_LENGTH} vertices.`),this._forceNewSegmentOnNextPrepare||!i||i.vertexLength+t>Ha.MAX_VERTEX_ARRAY_LENGTH||i.sortKey!==n?this.createNewSegment(e,r,n):i}createNewSegment(t,e,r){const n={vertexOffset:t.length,primitiveOffset:e.length,vertexLength:0,primitiveLength:0,vaos:{}};return void 0!==r&&(n.sortKey=r),this._forceNewSegmentOnNextPrepare=!1,this.segments.push(n),n}getOrCreateLatestSegment(t,e,r){return this.prepareSegment(0,t,e,r)}forceNewSegmentOnNextPrepare(){this._forceNewSegmentOnNextPrepare=!0;}get(){return this.segments}destroy(){for(const t of this.segments)for(const e in t.vaos)t.vaos[e].destroy();}static simpleSegment(t,e,r,n){return new Ha([{vertexOffset:t,primitiveOffset:e,vertexLength:r,primitiveLength:n,vaos:{},sortKey:0}])}}function Ka(t,e){return 256*(t=O(Math.floor(t),0,255))+O(Math.floor(e),0,255)}Ha.MAX_VERTEX_ARRAY_LENGTH=Math.pow(2,16)-1,us(\"SegmentVector\",Ha);const Ja=Ks([{name:\"a_pattern_from\",components:4,type:\"Uint16\"},{name:\"a_pattern_to\",components:4,type:\"Uint16\"},{name:\"a_pixel_ratio_from\",components:1,type:\"Uint16\"},{name:\"a_pixel_ratio_to\",components:1,type:\"Uint16\"}]);var Wa,Qa,to,eo={exports:{}},ro={exports:{}},no={exports:{}},io=function(){if(to)return eo.exports;to=1;var t=(Wa||(Wa=1,ro.exports=function(t,e){var r,n,i,s,a,o,l,u;for(n=t.length-(r=3&t.length),i=e,a=3432918353,o=461845907,u=0;u<n;)l=255&t.charCodeAt(u)|(255&t.charCodeAt(++u))<<8|(255&t.charCodeAt(++u))<<16|(255&t.charCodeAt(++u))<<24,++u,i=27492+(65535&(s=5*(65535&(i=(i^=l=(65535&(l=(l=(65535&l)*a+(((l>>>16)*a&65535)<<16)&4294967295)<<15|l>>>17))*o+(((l>>>16)*o&65535)<<16)&4294967295)<<13|i>>>19))+((5*(i>>>16)&65535)<<16)&4294967295))+((58964+(s>>>16)&65535)<<16);switch(l=0,r){case 3:l^=(255&t.charCodeAt(u+2))<<16;case 2:l^=(255&t.charCodeAt(u+1))<<8;case 1:i^=l=(65535&(l=(l=(65535&(l^=255&t.charCodeAt(u)))*a+(((l>>>16)*a&65535)<<16)&4294967295)<<15|l>>>17))*o+(((l>>>16)*o&65535)<<16)&4294967295;}return i^=t.length,i=2246822507*(65535&(i^=i>>>16))+((2246822507*(i>>>16)&65535)<<16)&4294967295,i=3266489909*(65535&(i^=i>>>13))+((3266489909*(i>>>16)&65535)<<16)&4294967295,(i^=i>>>16)>>>0}),ro.exports),e=(Qa||(Qa=1,no.exports=function(t,e){for(var r,n=t.length,i=e^n,s=0;n>=4;)r=1540483477*(65535&(r=255&t.charCodeAt(s)|(255&t.charCodeAt(++s))<<8|(255&t.charCodeAt(++s))<<16|(255&t.charCodeAt(++s))<<24))+((1540483477*(r>>>16)&65535)<<16),i=1540483477*(65535&i)+((1540483477*(i>>>16)&65535)<<16)^(r=1540483477*(65535&(r^=r>>>24))+((1540483477*(r>>>16)&65535)<<16)),n-=4,++s;switch(n){case 3:i^=(255&t.charCodeAt(s+2))<<16;case 2:i^=(255&t.charCodeAt(s+1))<<8;case 1:i=1540483477*(65535&(i^=255&t.charCodeAt(s)))+((1540483477*(i>>>16)&65535)<<16);}return i=1540483477*(65535&(i^=i>>>13))+((1540483477*(i>>>16)&65535)<<16),(i^=i>>>15)>>>0}),no.exports);return eo.exports=t,eo.exports.murmur3=t,eo.exports.murmur2=e,eo.exports}(),so=r(io);class ao{constructor(){this.ids=[],this.positions=[],this.indexed=!1;}add(t,e,r,n){this.ids.push(oo(t)),this.positions.push(e,r,n);}getPositions(t){if(!this.indexed)throw new Error(\"Trying to get index, but feature positions are not indexed\");const e=oo(t);let r=0,n=this.ids.length-1;for(;r<n;){const t=r+n>>1;this.ids[t]>=e?n=t:r=t+1;}const i=[];for(;this.ids[r]===e;)i.push({index:this.positions[3*r],start:this.positions[3*r+1],end:this.positions[3*r+2]}),r++;return i}static serialize(t,e){const r=new Float64Array(t.ids),n=new Uint32Array(t.positions);return lo(r,n,0,r.length-1),e&&e.push(r.buffer,n.buffer),{ids:r,positions:n}}static deserialize(t){const e=new ao;return e.ids=t.ids,e.positions=t.positions,e.indexed=!0,e}}function oo(t){const e=+t;return !isNaN(e)&&e<=Number.MAX_SAFE_INTEGER?e:so(String(t))}function lo(t,e,r,n){for(;r<n;){const i=t[r+n>>1];let s=r-1,a=n+1;for(;;){do{s++;}while(t[s]<i);do{a--;}while(t[a]>i);if(s>=a)break;uo(t,s,a),uo(e,3*s,3*a),uo(e,3*s+1,3*a+1),uo(e,3*s+2,3*a+2);}a-r<n-a?(lo(t,e,r,a),r=a+1):(lo(t,e,a+1,n),n=a);}}function uo(t,e,r){const n=t[e];t[e]=t[r],t[r]=n;}us(\"FeaturePositionMap\",ao);class co{constructor(t,e){this.gl=t.gl,this.location=e;}}class ho extends co{constructor(t,e){super(t,e),this.current=0;}set(t){this.current!==t&&(this.current=t,this.gl.uniform1f(this.location,t));}}class po extends co{constructor(t,e){super(t,e),this.current=[0,0,0,0];}set(t){t[0]===this.current[0]&&t[1]===this.current[1]&&t[2]===this.current[2]&&t[3]===this.current[3]||(this.current=t,this.gl.uniform4f(this.location,t[0],t[1],t[2],t[3]));}}class fo extends co{constructor(t,e){super(t,e),this.current=Pe.transparent;}set(t){t.r===this.current.r&&t.g===this.current.g&&t.b===this.current.b&&t.a===this.current.a||(this.current=t,this.gl.uniform4f(this.location,t.r,t.g,t.b,t.a));}}const yo=new Float32Array(16);function mo(t){return [Ka(255*t.r,255*t.g),Ka(255*t.b,255*t.a)]}class go{constructor(t,e,r){this.value=t,this.uniformNames=e.map((t=>`u_${t}`)),this.type=r;}setUniform(t,e,r){t.set(r.constantOr(this.value));}getBinding(t,e,r){return \"color\"===this.type?new fo(t,e):new ho(t,e)}}class xo{constructor(t,e){this.uniformNames=e.map((t=>`u_${t}`)),this.patternFrom=null,this.patternTo=null,this.pixelRatioFrom=1,this.pixelRatioTo=1;}setConstantPatternPositions(t,e){this.pixelRatioFrom=e.pixelRatio,this.pixelRatioTo=t.pixelRatio,this.patternFrom=e.tlbr,this.patternTo=t.tlbr;}setUniform(t,e,r,n){const i=\"u_pattern_to\"===n?this.patternTo:\"u_pattern_from\"===n?this.patternFrom:\"u_pixel_ratio_to\"===n?this.pixelRatioTo:\"u_pixel_ratio_from\"===n?this.pixelRatioFrom:null;i&&t.set(i);}getBinding(t,e,r){return \"u_pattern\"===r.substr(0,9)?new po(t,e):new ho(t,e)}}class vo{constructor(t,e,r,n){this.expression=t,this.type=r,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:\"Float32\",components:\"color\"===r?2:1,offset:0}))),this.paintVertexArray=new n;}populatePaintArray(t,e,r,n,i){const s=this.paintVertexArray.length,a=this.expression.evaluate(new Cs(0),e,{},n,[],i);this.paintVertexArray.resize(t),this._setPaintValue(s,t,a);}updatePaintArray(t,e,r,n){const i=this.expression.evaluate({zoom:0},r,n);this._setPaintValue(t,e,i);}_setPaintValue(t,e,r){if(\"color\"===this.type){const n=mo(r);for(let r=t;r<e;r++)this.paintVertexArray.emplace(r,n[0],n[1]);}else {for(let n=t;n<e;n++)this.paintVertexArray.emplace(n,r);this.maxValue=Math.max(this.maxValue,Math.abs(r));}}upload(t){this.paintVertexArray&&this.paintVertexArray.arrayBuffer&&(this.paintVertexBuffer&&this.paintVertexBuffer.buffer?this.paintVertexBuffer.updateData(this.paintVertexArray):this.paintVertexBuffer=t.createVertexBuffer(this.paintVertexArray,this.paintVertexAttributes,this.expression.isStateDependent));}destroy(){this.paintVertexBuffer&&this.paintVertexBuffer.destroy();}}class bo{constructor(t,e,r,n,i,s){this.expression=t,this.uniformNames=e.map((t=>`u_${t}_t`)),this.type=r,this.useIntegerZoom=n,this.zoom=i,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:\"Float32\",components:\"color\"===r?4:2,offset:0}))),this.paintVertexArray=new s;}populatePaintArray(t,e,r,n,i){const s=this.expression.evaluate(new Cs(this.zoom),e,{},n,[],i),a=this.expression.evaluate(new Cs(this.zoom+1),e,{},n,[],i),o=this.paintVertexArray.length;this.paintVertexArray.resize(t),this._setPaintValue(o,t,s,a);}updatePaintArray(t,e,r,n){const i=this.expression.evaluate({zoom:this.zoom},r,n),s=this.expression.evaluate({zoom:this.zoom+1},r,n);this._setPaintValue(t,e,i,s);}_setPaintValue(t,e,r,n){if(\"color\"===this.type){const i=mo(r),s=mo(n);for(let r=t;r<e;r++)this.paintVertexArray.emplace(r,i[0],i[1],s[0],s[1]);}else {for(let i=t;i<e;i++)this.paintVertexArray.emplace(i,r,n);this.maxValue=Math.max(this.maxValue,Math.abs(r),Math.abs(n));}}upload(t){this.paintVertexArray&&this.paintVertexArray.arrayBuffer&&(this.paintVertexBuffer&&this.paintVertexBuffer.buffer?this.paintVertexBuffer.updateData(this.paintVertexArray):this.paintVertexBuffer=t.createVertexBuffer(this.paintVertexArray,this.paintVertexAttributes,this.expression.isStateDependent));}destroy(){this.paintVertexBuffer&&this.paintVertexBuffer.destroy();}setUniform(t,e){const r=this.useIntegerZoom?Math.floor(e.zoom):e.zoom,n=O(this.expression.interpolationFactor(r,this.zoom,this.zoom+1),0,1);t.set(n);}getBinding(t,e,r){return new ho(t,e)}}class wo{constructor(t,e,r,n,i,s){this.expression=t,this.type=e,this.useIntegerZoom=r,this.zoom=n,this.layerId=s,this.zoomInPaintVertexArray=new i,this.zoomOutPaintVertexArray=new i;}populatePaintArray(t,e,r){const n=this.zoomInPaintVertexArray.length;this.zoomInPaintVertexArray.resize(t),this.zoomOutPaintVertexArray.resize(t),this._setPaintValues(n,t,e.patterns&&e.patterns[this.layerId],r);}updatePaintArray(t,e,r,n,i){this._setPaintValues(t,e,r.patterns&&r.patterns[this.layerId],i);}_setPaintValues(t,e,r,n){if(!n||!r)return;const{min:i,mid:s,max:a}=r,o=n[i],l=n[s],u=n[a];if(o&&l&&u)for(let r=t;r<e;r++)this.zoomInPaintVertexArray.emplace(r,l.tl[0],l.tl[1],l.br[0],l.br[1],o.tl[0],o.tl[1],o.br[0],o.br[1],l.pixelRatio,o.pixelRatio),this.zoomOutPaintVertexArray.emplace(r,l.tl[0],l.tl[1],l.br[0],l.br[1],u.tl[0],u.tl[1],u.br[0],u.br[1],l.pixelRatio,u.pixelRatio);}upload(t){this.zoomInPaintVertexArray&&this.zoomInPaintVertexArray.arrayBuffer&&this.zoomOutPaintVertexArray&&this.zoomOutPaintVertexArray.arrayBuffer&&(this.zoomInPaintVertexBuffer=t.createVertexBuffer(this.zoomInPaintVertexArray,Ja.members,this.expression.isStateDependent),this.zoomOutPaintVertexBuffer=t.createVertexBuffer(this.zoomOutPaintVertexArray,Ja.members,this.expression.isStateDependent));}destroy(){this.zoomOutPaintVertexBuffer&&this.zoomOutPaintVertexBuffer.destroy(),this.zoomInPaintVertexBuffer&&this.zoomInPaintVertexBuffer.destroy();}}class _o{constructor(t,e,r){this.binders={},this._buffers=[];const n=[];for(const i in t.paint._values){if(!r(i))continue;const s=t.paint.get(i);if(!(s instanceof $s&&Gn(s.property.specification)))continue;const a=Ao(i,t.type),o=s.value,l=s.property.specification.type,u=s.property.useIntegerZoom,c=s.property.specification[\"property-type\"],h=\"cross-faded\"===c||\"cross-faded-data-driven\"===c;if(\"constant\"===o.kind)this.binders[i]=h?new xo(o.value,a):new go(o.value,a,l),n.push(`/u_${i}`);else if(\"source\"===o.kind||h){const r=ko(i,l,\"source\");this.binders[i]=h?new wo(o,l,u,e,r,t.id):new vo(o,a,l,r),n.push(`/a_${i}`);}else {const t=ko(i,l,\"composite\");this.binders[i]=new bo(o,a,l,u,e,t),n.push(`/z_${i}`);}}this.cacheKey=n.sort().join(\"\");}getMaxValue(t){const e=this.binders[t];return e instanceof vo||e instanceof bo?e.maxValue:0}populatePaintArrays(t,e,r,n,i){for(const s in this.binders){const a=this.binders[s];(a instanceof vo||a instanceof bo||a instanceof wo)&&a.populatePaintArray(t,e,r,n,i);}}setConstantPatternPositions(t,e){for(const r in this.binders){const n=this.binders[r];n instanceof xo&&n.setConstantPatternPositions(t,e);}}updatePaintArrays(t,e,r,n,i){let s=!1;for(const a in t){const o=e.getPositions(a);for(const e of o){const o=r.feature(e.index);for(const r in this.binders){const l=this.binders[r];if((l instanceof vo||l instanceof bo||l instanceof wo)&&!0===l.expression.isStateDependent){const u=n.paint.get(r);l.expression=u.value,l.updatePaintArray(e.start,e.end,o,t[a],i),s=!0;}}}}return s}defines(){const t=[];for(const e in this.binders){const r=this.binders[e];(r instanceof go||r instanceof xo)&&t.push(...r.uniformNames.map((t=>`#define HAS_UNIFORM_${t}`)));}return t}getBinderAttributes(){const t=[];for(const e in this.binders){const r=this.binders[e];if(r instanceof vo||r instanceof bo)for(let e=0;e<r.paintVertexAttributes.length;e++)t.push(r.paintVertexAttributes[e].name);else if(r instanceof wo)for(let e=0;e<Ja.members.length;e++)t.push(Ja.members[e].name);}return t}getBinderUniforms(){const t=[];for(const e in this.binders){const r=this.binders[e];if(r instanceof go||r instanceof xo||r instanceof bo)for(const e of r.uniformNames)t.push(e);}return t}getPaintVertexBuffers(){return this._buffers}getUniforms(t,e){const r=[];for(const n in this.binders){const i=this.binders[n];if(i instanceof go||i instanceof xo||i instanceof bo)for(const s of i.uniformNames)if(e[s]){const a=i.getBinding(t,e[s],s);r.push({name:s,property:n,binding:a});}}return r}setUniforms(t,e,r,n){for(const{name:t,property:i,binding:s}of e)this.binders[i].setUniform(s,n,r.get(i),t);}updatePaintBuffers(t){this._buffers=[];for(const e in this.binders){const r=this.binders[e];if(t&&r instanceof wo){const e=2===t.fromScale?r.zoomInPaintVertexBuffer:r.zoomOutPaintVertexBuffer;e&&this._buffers.push(e);}else (r instanceof vo||r instanceof bo)&&r.paintVertexBuffer&&this._buffers.push(r.paintVertexBuffer);}}upload(t){for(const e in this.binders){const r=this.binders[e];(r instanceof vo||r instanceof bo||r instanceof wo)&&r.upload(t);}this.updatePaintBuffers();}destroy(){for(const t in this.binders){const e=this.binders[t];(e instanceof vo||e instanceof bo||e instanceof wo)&&e.destroy();}}}class So{constructor(t,e,r=()=>!0){this.programConfigurations={};for(const n of t)this.programConfigurations[n.id]=new _o(n,e,r);this.needsUpload=!1,this._featureMap=new ao,this._bufferOffset=0;}populatePaintArrays(t,e,r,n,i,s){for(const r in this.programConfigurations)this.programConfigurations[r].populatePaintArrays(t,e,n,i,s);void 0!==e.id&&this._featureMap.add(e.id,r,this._bufferOffset,t),this._bufferOffset=t,this.needsUpload=!0;}updatePaintArrays(t,e,r,n){for(const i of r)this.needsUpload=this.programConfigurations[i.id].updatePaintArrays(t,this._featureMap,e,i,n)||this.needsUpload;}get(t){return this.programConfigurations[t]}upload(t){if(this.needsUpload){for(const e in this.programConfigurations)this.programConfigurations[e].upload(t);this.needsUpload=!1;}}destroy(){for(const t in this.programConfigurations)this.programConfigurations[t].destroy();}}function Ao(t,e){return {\"text-opacity\":[\"opacity\"],\"icon-opacity\":[\"opacity\"],\"text-color\":[\"fill_color\"],\"icon-color\":[\"fill_color\"],\"text-halo-color\":[\"halo_color\"],\"icon-halo-color\":[\"halo_color\"],\"text-halo-blur\":[\"halo_blur\"],\"icon-halo-blur\":[\"halo_blur\"],\"text-halo-width\":[\"halo_width\"],\"icon-halo-width\":[\"halo_width\"],\"line-gap-width\":[\"gapwidth\"],\"line-pattern\":[\"pattern_to\",\"pattern_from\",\"pixel_ratio_to\",\"pixel_ratio_from\"],\"fill-pattern\":[\"pattern_to\",\"pattern_from\",\"pixel_ratio_to\",\"pixel_ratio_from\"],\"fill-extrusion-pattern\":[\"pattern_to\",\"pattern_from\",\"pixel_ratio_to\",\"pixel_ratio_from\"]}[t]||[t.replace(`${e}-`,\"\").replace(/-/g,\"_\")]}function ko(t,e,r){const n={color:{source:na,composite:ba},number:{source:ya,composite:na}},i=function(t){return {\"line-pattern\":{source:Oa,composite:Oa},\"fill-pattern\":{source:Oa,composite:Oa},\"fill-extrusion-pattern\":{source:Oa,composite:Oa}}[t]}(t);return i&&i[r]||n[e][r]}us(\"ConstantBinder\",go),us(\"CrossFadedConstantBinder\",xo),us(\"SourceExpressionBinder\",vo),us(\"CrossFadedCompositeBinder\",wo),us(\"CompositeExpressionBinder\",bo),us(\"ProgramConfiguration\",_o,{omit:[\"_buffers\"]}),us(\"ProgramConfigurationSet\",So);const Mo=Math.pow(2,14)-1,Io=-Mo-1;function zo(t){const e=E/t.extent,r=t.loadGeometry();for(let t=0;t<r.length;t++){const n=r[t];for(let t=0;t<n.length;t++){const r=n[t],i=Math.round(r.x*e),s=Math.round(r.y*e);r.x=O(i,Io,Mo),r.y=O(s,Io,Mo),(i<r.x||i>r.x+1||s<r.y||s>r.y+1)&&Z(\"Geometry exceeds allowed extent, reduce your vector tile buffer size\");}}return r}function Po(t,e){return {type:t.type,id:t.id,properties:t.properties,geometry:e?zo(t):[]}}const Co=-32768;function Bo(t,e,r,n,i){t.emplaceBack(Co+8*e+n,Co+8*r+i);}class Eo{constructor(t){this.zoom=t.zoom,this.globalState=t.globalState,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.layoutVertexArray=new Va,this.indexArray=new Ga,this.segments=new Ha,this.programConfigurations=new So(t.layers,t.zoom),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,r){const n=this.layers[0],i=[];let s=null,a=!1,o=\"heatmap\"===n.type;if(\"circle\"===n.type){const t=n;s=t.layout.get(\"circle-sort-key\"),a=!s.isConstant(),o=o||\"map\"===t.paint.get(\"circle-pitch-alignment\");}const l=o?e.subdivisionGranularity.circle:1;for(const{feature:e,id:n,index:o,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,u=Po(e,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom,{globalState:this.globalState}),u,r))continue;const c=a?s.evaluate(u,{},r):void 0,h={id:n,properties:e.properties,type:e.type,sourceLayerIndex:l,index:o,geometry:t?u.geometry:zo(e),patterns:{},sortKey:c};i.push(h);}a&&i.sort(((t,e)=>t.sortKey-e.sortKey));for(const n of i){const{geometry:i,index:s,sourceLayerIndex:a}=n,o=t[s].feature;this.addFeature(n,i,s,r,l),e.featureIndex.insert(o,i,s,a,this.index);}}update(t,e,r){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,r);}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,Ya),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy());}addFeature(t,e,r,n,i=1){let s;switch(i){case 1:s=[0,7];break;case 3:s=[0,2,5,7];break;case 5:s=[0,1,3,4,6,7];break;case 7:s=[0,1,2,3,4,5,6,7];break;default:throw new Error(`Invalid circle bucket granularity: ${i}; valid values are 1, 3, 5, 7.`)}const a=s.length;for(const r of e)for(const e of r){const r=e.x,n=e.y;if(r<0||r>=E||n<0||n>=E)continue;const i=this.segments.prepareSegment(a*a,this.layoutVertexArray,this.indexArray,t.sortKey),o=i.vertexLength;for(let t=0;t<a;t++)for(let e=0;e<a;e++)Bo(this.layoutVertexArray,r,n,s[e],s[t]);for(let t=0;t<a-1;t++)for(let e=0;e<a-1;e++){const r=o+t*a+e,n=o+(t+1)*a+e;this.indexArray.emplaceBack(r,n+1,r+1),this.indexArray.emplaceBack(r,n,n+1);}i.vertexLength+=a*a,i.primitiveLength+=(a-1)*(a-1)*2;}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,r,{},n);}}function To(t,e){for(let r=0;r<t.length;r++)if(No(e,t[r]))return !0;for(let r=0;r<e.length;r++)if(No(t,e[r]))return !0;return !!$o(t,e)}function Vo(t,e,r){return !!No(t,e)||!!Oo(e,t,r)}function Fo(t,e){if(1===t.length)return jo(e,t[0]);for(let r=0;r<e.length;r++){const n=e[r];for(let e=0;e<n.length;e++)if(No(t,n[e]))return !0}for(let r=0;r<t.length;r++)if(jo(e,t[r]))return !0;for(let r=0;r<e.length;r++)if($o(t,e[r]))return !0;return !1}function Lo(t,e,r){if(t.length>1){if($o(t,e))return !0;for(let n=0;n<e.length;n++)if(Oo(e[n],t,r))return !0}for(let n=0;n<t.length;n++)if(Oo(t[n],e,r))return !0;return !1}function $o(t,e){if(0===t.length||0===e.length)return !1;for(let r=0;r<t.length-1;r++){const n=t[r],i=t[r+1];for(let t=0;t<e.length-1;t++)if(Do(n,i,e[t],e[t+1]))return !0}return !1}function Do(t,e,r,n){return Y(t,r,n)!==Y(e,r,n)&&Y(t,e,r)!==Y(t,e,n)}function Oo(t,e,r){const n=r*r;if(1===e.length)return t.distSqr(e[0])<n;for(let r=1;r<e.length;r++)if(Ro(t,e[r-1],e[r])<n)return !0;return !1}function Ro(t,e,r){const n=e.distSqr(r);if(0===n)return t.distSqr(e);const i=((t.x-e.x)*(r.x-e.x)+(t.y-e.y)*(r.y-e.y))/n;return t.distSqr(i<0?e:i>1?r:r.sub(e)._mult(i)._add(e))}function jo(t,e){let r,n,i,s=!1;for(let a=0;a<t.length;a++){r=t[a];for(let t=0,a=r.length-1;t<r.length;a=t++)n=r[t],i=r[a],n.y>e.y!=i.y>e.y&&e.x<(i.x-n.x)*(e.y-n.y)/(i.y-n.y)+n.x&&(s=!s);}return s}function No(t,e){let r=!1;for(let n=0,i=t.length-1;n<t.length;i=n++){const s=t[n],a=t[i];s.y>e.y!=a.y>e.y&&e.x<(a.x-s.x)*(e.y-s.y)/(a.y-s.y)+s.x&&(r=!r);}return r}function Uo(t,e,r){const n=r[0],i=r[2];if(t.x<n.x&&e.x<n.x||t.x>i.x&&e.x>i.x||t.y<n.y&&e.y<n.y||t.y>i.y&&e.y>i.y)return !1;const s=Y(t,e,r[0]);return s!==Y(t,e,r[1])||s!==Y(t,e,r[2])||s!==Y(t,e,r[3])}function qo(t,e,r){const n=e.paint.get(t).value;return \"constant\"===n.kind?n.value:r.programConfigurations.get(e.id).getMaxValue(t)}function Go(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1])}function Xo(t,e,r,n,i){if(!e[0]&&!e[1])return t;const s=l.convert(e)._mult(i);\"viewport\"===r&&s._rotate(-n);const a=[];for(let e=0;e<t.length;e++)a.push(t[e].sub(s));return a}let Zo,Yo;us(\"CircleBucket\",Eo,{omit:[\"layers\"]});var Ho={get paint(){return Yo=Yo||new qs({\"circle-radius\":new Rs(wt.paint_circle[\"circle-radius\"]),\"circle-color\":new Rs(wt.paint_circle[\"circle-color\"]),\"circle-blur\":new Rs(wt.paint_circle[\"circle-blur\"]),\"circle-opacity\":new Rs(wt.paint_circle[\"circle-opacity\"]),\"circle-translate\":new Os(wt.paint_circle[\"circle-translate\"]),\"circle-translate-anchor\":new Os(wt.paint_circle[\"circle-translate-anchor\"]),\"circle-pitch-scale\":new Os(wt.paint_circle[\"circle-pitch-scale\"]),\"circle-pitch-alignment\":new Os(wt.paint_circle[\"circle-pitch-alignment\"]),\"circle-stroke-width\":new Rs(wt.paint_circle[\"circle-stroke-width\"]),\"circle-stroke-color\":new Rs(wt.paint_circle[\"circle-stroke-color\"]),\"circle-stroke-opacity\":new Rs(wt.paint_circle[\"circle-stroke-opacity\"])})},get layout(){return Zo=Zo||new qs({\"circle-sort-key\":new Rs(wt.layout_circle[\"circle-sort-key\"])})}};class Ko extends Xs{constructor(t){super(t,Ho);}createBucket(t){return new Eo(t)}queryRadius(t){const e=t;return qo(\"circle-radius\",this,e)+qo(\"circle-stroke-width\",this,e)+Go(this.paint.get(\"circle-translate\"))}queryIntersectsFeature({queryGeometry:t,feature:e,featureState:r,geometry:n,transform:i,pixelsToTileUnits:s,unwrappedTileID:a,getElevation:o}){const l=Xo(t,this.paint.get(\"circle-translate\"),this.paint.get(\"circle-translate-anchor\"),-i.bearingInRadians,s),u=this.paint.get(\"circle-radius\").evaluate(e,r)+this.paint.get(\"circle-stroke-width\").evaluate(e,r),c=\"map\"===this.paint.get(\"circle-pitch-alignment\"),h=c?l:function(t,e,r,n){return t.map((t=>Jo(t,e,r,n)))}(l,i,a,o),p=c?u*s:u;for(const t of n)for(const e of t){const t=c?e:Jo(e,i,a,o);let r=p;const n=i.projectTileCoordinates(e.x,e.y,a,o).signedDistanceFromCamera;if(\"viewport\"===this.paint.get(\"circle-pitch-scale\")&&\"map\"===this.paint.get(\"circle-pitch-alignment\")?r*=n/i.cameraToCenterDistance:\"map\"===this.paint.get(\"circle-pitch-scale\")&&\"viewport\"===this.paint.get(\"circle-pitch-alignment\")&&(r*=i.cameraToCenterDistance/n),Vo(h,t,r))return !0}return !1}}function Jo(t,e,r,n){const i=e.projectTileCoordinates(t.x,t.y,r,n).point;return new l((.5*i.x+.5)*e.width,(.5*-i.y+.5)*e.height)}class Wo extends Eo{}let Qo;us(\"HeatmapBucket\",Wo,{omit:[\"layers\"]});var tl={get paint(){return Qo=Qo||new qs({\"heatmap-radius\":new Rs(wt.paint_heatmap[\"heatmap-radius\"]),\"heatmap-weight\":new Rs(wt.paint_heatmap[\"heatmap-weight\"]),\"heatmap-intensity\":new Os(wt.paint_heatmap[\"heatmap-intensity\"]),\"heatmap-color\":new Us(wt.paint_heatmap[\"heatmap-color\"]),\"heatmap-opacity\":new Os(wt.paint_heatmap[\"heatmap-opacity\"])})}};function el(t,{width:e,height:r},n,i){if(i){if(i instanceof Uint8ClampedArray)i=new Uint8Array(i.buffer);else if(i.length!==e*r*n)throw new RangeError(`mismatched image size. expected: ${i.length} but got: ${e*r*n}`)}else i=new Uint8Array(e*r*n);return t.width=e,t.height=r,t.data=i,t}function rl(t,{width:e,height:r},n){if(e===t.width&&r===t.height)return;const i=el({},{width:e,height:r},n);nl(t,i,{x:0,y:0},{x:0,y:0},{width:Math.min(t.width,e),height:Math.min(t.height,r)},n),t.width=e,t.height=r,t.data=i.data;}function nl(t,e,r,n,i,s){if(0===i.width||0===i.height)return e;if(i.width>t.width||i.height>t.height||r.x>t.width-i.width||r.y>t.height-i.height)throw new RangeError(\"out of range source coordinates for image copy\");if(i.width>e.width||i.height>e.height||n.x>e.width-i.width||n.y>e.height-i.height)throw new RangeError(\"out of range destination coordinates for image copy\");const a=t.data,o=e.data;if(a===o)throw new Error(\"srcData equals dstData, so image is already copied\");for(let l=0;l<i.height;l++){const u=((r.y+l)*t.width+r.x)*s,c=((n.y+l)*e.width+n.x)*s;for(let t=0;t<i.width*s;t++)o[c+t]=a[u+t];}return e}class il{constructor(t,e){el(this,t,1,e);}resize(t){rl(this,t,1);}clone(){return new il({width:this.width,height:this.height},new Uint8Array(this.data))}static copy(t,e,r,n,i){nl(t,e,r,n,i,1);}}class sl{constructor(t,e){el(this,t,4,e);}resize(t){rl(this,t,4);}replace(t,e){e?this.data.set(t):this.data=t instanceof Uint8ClampedArray?new Uint8Array(t.buffer):t;}clone(){return new sl({width:this.width,height:this.height},new Uint8Array(this.data))}static copy(t,e,r,n,i){nl(t,e,r,n,i,4);}setPixel(t,e,r){const n=4*(t*this.width+e);this.data[n+0]=Math.round(255*r.r/r.a),this.data[n+1]=Math.round(255*r.g/r.a),this.data[n+2]=Math.round(255*r.b/r.a),this.data[n+3]=Math.round(255*r.a);}}function al(t){const e={},r=t.resolution||256,n=t.clips?t.clips.length:1,i=t.image||new sl({width:r,height:n});if(Math.log(r)/Math.LN2%1!=0)throw new Error(`width is not a power of 2 - ${r}`);const s=(n,s,a)=>{e[t.evaluationKey]=a;const o=t.expression.evaluate(e);i.setPixel(n/4/r,s/4,o);};if(t.clips)for(let e=0,i=0;e<n;++e,i+=4*r)for(let n=0,a=0;n<r;n++,a+=4){const o=n/(r-1),{start:l,end:u}=t.clips[e];s(i,a,l*(1-o)+u*o);}else for(let t=0,e=0;t<r;t++,e+=4)s(0,e,t/(r-1));return i}us(\"AlphaImage\",il),us(\"RGBAImage\",sl);const ol=\"big-fb\";class ll extends Xs{createBucket(t){return new Wo(t)}constructor(t){super(t,tl),this.heatmapFbos=new Map,this._updateColorRamp();}_handleSpecialPaintPropertyUpdate(t){\"heatmap-color\"===t&&this._updateColorRamp();}_updateColorRamp(){this.colorRamp=al({expression:this._transitionablePaint._values[\"heatmap-color\"].value.expression,evaluationKey:\"heatmapDensity\",image:this.colorRamp}),this.colorRampTexture=null;}resize(){this.heatmapFbos.has(ol)&&this.heatmapFbos.delete(ol);}queryRadius(){return 0}queryIntersectsFeature(){return !1}hasOffscreenPass(){return 0!==this.paint.get(\"heatmap-opacity\")&&\"none\"!==this.visibility}}let ul;var cl={get paint(){return ul=ul||new qs({\"hillshade-illumination-direction\":new Os(wt.paint_hillshade[\"hillshade-illumination-direction\"]),\"hillshade-illumination-altitude\":new Os(wt.paint_hillshade[\"hillshade-illumination-altitude\"]),\"hillshade-illumination-anchor\":new Os(wt.paint_hillshade[\"hillshade-illumination-anchor\"]),\"hillshade-exaggeration\":new Os(wt.paint_hillshade[\"hillshade-exaggeration\"]),\"hillshade-shadow-color\":new Os(wt.paint_hillshade[\"hillshade-shadow-color\"]),\"hillshade-highlight-color\":new Os(wt.paint_hillshade[\"hillshade-highlight-color\"]),\"hillshade-accent-color\":new Os(wt.paint_hillshade[\"hillshade-accent-color\"]),\"hillshade-method\":new Os(wt.paint_hillshade[\"hillshade-method\"])})}};class hl extends Xs{constructor(t){super(t,cl),this.recalculate({zoom:0,zoomHistory:{}},void 0);}getIlluminationProperties(){let t=this.paint.get(\"hillshade-illumination-direction\").values,e=this.paint.get(\"hillshade-illumination-altitude\").values,r=this.paint.get(\"hillshade-highlight-color\").values,n=this.paint.get(\"hillshade-shadow-color\").values;const i=Math.max(t.length,e.length,r.length,n.length);t=t.concat(Array(i-t.length).fill(t.at(-1))),e=e.concat(Array(i-e.length).fill(e.at(-1))),r=r.concat(Array(i-r.length).fill(r.at(-1))),n=n.concat(Array(i-n.length).fill(n.at(-1)));const s=e.map(nt);return {directionRadians:t.map(nt),altitudeRadians:s,shadowColor:n,highlightColor:r}}hasOffscreenPass(){return 0!==this.paint.get(\"hillshade-exaggeration\")&&\"none\"!==this.visibility}}let pl;var fl={get paint(){return pl=pl||new qs({\"color-relief-opacity\":new Os(wt[\"paint_color-relief\"][\"color-relief-opacity\"]),\"color-relief-color\":new Us(wt[\"paint_color-relief\"][\"color-relief-color\"])})}};class dl{constructor(t,e,r,n){this.context=t,this.format=r,this.texture=t.gl.createTexture(),this.update(e,n);}update(t,e,r){const{width:n,height:i}=t,s=!(this.size&&this.size[0]===n&&this.size[1]===i||r),{context:a}=this,{gl:o}=a;if(this.useMipmap=Boolean(e&&e.useMipmap),o.bindTexture(o.TEXTURE_2D,this.texture),a.pixelStoreUnpackFlipY.set(!1),a.pixelStoreUnpack.set(1),a.pixelStoreUnpackPremultiplyAlpha.set(this.format===o.RGBA&&(!e||!1!==e.premultiply)),s)this.size=[n,i],t instanceof HTMLImageElement||t instanceof HTMLCanvasElement||t instanceof HTMLVideoElement||t instanceof ImageData||J(t)?o.texImage2D(o.TEXTURE_2D,0,this.format,this.format,o.UNSIGNED_BYTE,t):o.texImage2D(o.TEXTURE_2D,0,this.format,n,i,0,this.format,o.UNSIGNED_BYTE,t.data);else {const{x:e,y:s}=r||{x:0,y:0};t instanceof HTMLImageElement||t instanceof HTMLCanvasElement||t instanceof HTMLVideoElement||t instanceof ImageData||J(t)?o.texSubImage2D(o.TEXTURE_2D,0,e,s,o.RGBA,o.UNSIGNED_BYTE,t):o.texSubImage2D(o.TEXTURE_2D,0,e,s,n,i,o.RGBA,o.UNSIGNED_BYTE,t.data);}this.useMipmap&&this.isSizePowerOfTwo()&&o.generateMipmap(o.TEXTURE_2D),a.pixelStoreUnpackFlipY.setDefault(),a.pixelStoreUnpack.setDefault(),a.pixelStoreUnpackPremultiplyAlpha.setDefault();}bind(t,e,r){const{context:n}=this,{gl:i}=n;i.bindTexture(i.TEXTURE_2D,this.texture),r!==i.LINEAR_MIPMAP_NEAREST||this.isSizePowerOfTwo()||(r=i.LINEAR),t!==this.filter&&(i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,t),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,r||t),this.filter=t),e!==this.wrap&&(i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,e),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,e),this.wrap=e);}isSizePowerOfTwo(){return this.size[0]===this.size[1]&&Math.log(this.size[0])/Math.LN2%1==0}destroy(){const{gl:t}=this.context;t.deleteTexture(this.texture),this.texture=null;}}class yl{constructor(t,e,r,n=1,i=1,s=1,a=0){if(this.uid=t,e.height!==e.width)throw new RangeError(\"DEM tiles must be square\");if(r&&![\"mapbox\",\"terrarium\",\"custom\"].includes(r))return void Z(`\"${r}\" is not a valid encoding type. Valid types include \"mapbox\", \"terrarium\" and \"custom\".`);this.stride=e.height;const o=this.dim=e.height-2;switch(this.data=new Uint32Array(e.data.buffer),r){case \"terrarium\":this.redFactor=256,this.greenFactor=1,this.blueFactor=1/256,this.baseShift=32768;break;case \"custom\":this.redFactor=n,this.greenFactor=i,this.blueFactor=s,this.baseShift=a;break;default:this.redFactor=6553.6,this.greenFactor=25.6,this.blueFactor=.1,this.baseShift=1e4;}for(let t=0;t<o;t++)this.data[this._idx(-1,t)]=this.data[this._idx(0,t)],this.data[this._idx(o,t)]=this.data[this._idx(o-1,t)],this.data[this._idx(t,-1)]=this.data[this._idx(t,0)],this.data[this._idx(t,o)]=this.data[this._idx(t,o-1)];this.data[this._idx(-1,-1)]=this.data[this._idx(0,0)],this.data[this._idx(o,-1)]=this.data[this._idx(o-1,0)],this.data[this._idx(-1,o)]=this.data[this._idx(0,o-1)],this.data[this._idx(o,o)]=this.data[this._idx(o-1,o-1)],this.min=Number.MAX_SAFE_INTEGER,this.max=Number.MIN_SAFE_INTEGER;for(let t=0;t<o;t++)for(let e=0;e<o;e++){const r=this.get(t,e);r>this.max&&(this.max=r),r<this.min&&(this.min=r);}}get(t,e){const r=new Uint8Array(this.data.buffer),n=4*this._idx(t,e);return this.unpack(r[n],r[n+1],r[n+2])}getUnpackVector(){return [this.redFactor,this.greenFactor,this.blueFactor,this.baseShift]}_idx(t,e){if(t<-1||t>=this.dim+1||e<-1||e>=this.dim+1)throw new RangeError(\"out of range source coordinates for DEM data\");return (e+1)*this.stride+(t+1)}unpack(t,e,r){return t*this.redFactor+e*this.greenFactor+r*this.blueFactor-this.baseShift}pack(t){return ml(t,this.getUnpackVector())}getPixels(){return new sl({width:this.stride,height:this.stride},new Uint8Array(this.data.buffer))}backfillBorder(t,e,r){if(this.dim!==t.dim)throw new Error(\"dem dimension mismatch\");let n=e*this.dim,i=e*this.dim+this.dim,s=r*this.dim,a=r*this.dim+this.dim;switch(e){case -1:n=i-1;break;case 1:i=n+1;}switch(r){case -1:s=a-1;break;case 1:a=s+1;}const o=-e*this.dim,l=-r*this.dim;for(let e=s;e<a;e++)for(let r=n;r<i;r++)this.data[this._idx(r,e)]=t.data[this._idx(r+o,e+l)];}}function ml(t,e){const r=e[0],n=e[1],i=e[2],s=e[3],a=Math.min(r,n,i),o=Math.round((t+s)/a);return {r:Math.floor(o*a/r)%256,g:Math.floor(o*a/n)%256,b:Math.floor(o*a/i)%256}}us(\"DEMData\",yl);class gl extends Xs{constructor(t){super(t,fl);}_createColorRamp(t){const e={elevationStops:[],colorStops:[]},r=this._transitionablePaint._values[\"color-relief-color\"].value.expression;if(r instanceof ai&&r._styleExpression.expression instanceof yr){this.colorRampExpression=r;const t=r._styleExpression.expression;e.elevationStops=t.labels,e.colorStops=[];for(const r of e.elevationStops)e.colorStops.push(t.evaluate({globals:{elevation:r}}));}if(e.elevationStops.length<1&&(e.elevationStops=[0],e.colorStops=[Pe.transparent]),e.elevationStops.length<2&&(e.elevationStops.push(e.elevationStops[0]+1),e.colorStops.push(e.colorStops[0])),e.elevationStops.length<=t)return e;const n={elevationStops:[],colorStops:[]},i=(e.elevationStops.length-1)/(t-1);for(let t=0;t<e.elevationStops.length-.5;t+=i)n.elevationStops.push(e.elevationStops[Math.round(t)]),n.colorStops.push(e.colorStops[Math.round(t)]);return Z(`Too many colors in specification of ${this.id} color-relief layer, may not render properly.`),n}_colorRampChanged(){return this.colorRampExpression!=this._transitionablePaint._values[\"color-relief-color\"].value.expression}getColorRampTextures(t,e,r){if(this.colorRampTextures&&!this._colorRampChanged())return this.colorRampTextures;const n=this._createColorRamp(e),i=new sl({width:n.colorStops.length,height:1}),s=new sl({width:n.colorStops.length,height:1});for(let t=0;t<n.elevationStops.length;t++){const e=ml(n.elevationStops[t],r);s.setPixel(0,t,new Pe(e.r/255,e.g/255,e.b/255,1)),i.setPixel(0,t,n.colorStops[t]);}return this.colorRampTextures={elevationTexture:new dl(t,s,t.gl.RGBA),colorTexture:new dl(t,i,t.gl.RGBA)},this.colorRampTextures}hasOffscreenPass(){return \"none\"!==this.visibility&&!!this.colorRampTextures}}const xl=Ks([{name:\"a_pos\",components:2,type:\"Int16\"}],4),{members:vl}=xl;function bl(t,e,r){const n=r.patternDependencies;let i=!1;for(const r of e){const e=r.paint.get(`${t}-pattern`);e.isConstant()||(i=!0);const s=e.constantOr(null);s&&(i=!0,n[s.to]=!0,n[s.from]=!0);}return i}function wl(t,e,r,n,i){const s=i.patternDependencies;for(const a of e){const e=a.paint.get(`${t}-pattern`).value;if(\"constant\"!==e.kind){let t=e.evaluate({zoom:n-1},r,{},i.availableImages),o=e.evaluate({zoom:n},r,{},i.availableImages),l=e.evaluate({zoom:n+1},r,{},i.availableImages);t=t&&t.name?t.name:t,o=o&&o.name?o.name:o,l=l&&l.name?l.name:l,s[t]=!0,s[o]=!0,s[l]=!0,r.patterns[a.id]={min:t,mid:o,max:l};}}return r}function _l(t,e,r,n,i){let s;if(i===function(t,e,r,n){let i=0;for(let s=e,a=r-n;s<r;s+=n)i+=(t[a]-t[s])*(t[s+1]+t[a+1]),a=s;return i}(t,e,r,n)>0)for(let i=e;i<r;i+=n)s=ql(i/n|0,t[i],t[i+1],s);else for(let i=r-n;i>=e;i-=n)s=ql(i/n|0,t[i],t[i+1],s);return s&&Dl(s,s.next)&&(Gl(s),s=s.next),s}function Sl(t,e){if(!t)return t;e||(e=t);let r,n=t;do{if(r=!1,n.steiner||!Dl(n,n.next)&&0!==$l(n.prev,n,n.next))n=n.next;else {if(Gl(n),n=e=n.prev,n===n.next)break;r=!0;}}while(r||n!==e);return e}function Al(t,e,r,n,i,s,a){if(!t)return;!a&&s&&function(t,e,r,n){let i=t;do{0===i.z&&(i.z=El(i.x,i.y,e,r,n)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next;}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){let e,r=1;do{let n,i=t;t=null;let s=null;for(e=0;i;){e++;let a=i,o=0;for(let t=0;t<r&&(o++,a=a.nextZ,a);t++);let l=r;for(;o>0||l>0&&a;)0!==o&&(0===l||!a||i.z<=a.z)?(n=i,i=i.nextZ,o--):(n=a,a=a.nextZ,l--),s?s.nextZ=n:t=n,n.prevZ=s,s=n;i=a;}s.nextZ=null,r*=2;}while(e>1)}(i);}(t,n,i,s);let o=t;for(;t.prev!==t.next;){const l=t.prev,u=t.next;if(s?Ml(t,n,i,s):kl(t))e.push(l.i,t.i,u.i),Gl(t),t=u.next,o=u.next;else if((t=u)===o){a?1===a?Al(t=Il(Sl(t),e),e,r,n,i,s,2):2===a&&zl(t,e,r,n,i,s):Al(Sl(t),e,r,n,i,s,1);break}}}function kl(t){const e=t.prev,r=t,n=t.next;if($l(e,r,n)>=0)return !1;const i=e.x,s=r.x,a=n.x,o=e.y,l=r.y,u=n.y,c=Math.min(i,s,a),h=Math.min(o,l,u),p=Math.max(i,s,a),f=Math.max(o,l,u);let d=n.next;for(;d!==e;){if(d.x>=c&&d.x<=p&&d.y>=h&&d.y<=f&&Fl(i,o,s,l,a,u,d.x,d.y)&&$l(d.prev,d,d.next)>=0)return !1;d=d.next;}return !0}function Ml(t,e,r,n){const i=t.prev,s=t,a=t.next;if($l(i,s,a)>=0)return !1;const o=i.x,l=s.x,u=a.x,c=i.y,h=s.y,p=a.y,f=Math.min(o,l,u),d=Math.min(c,h,p),y=Math.max(o,l,u),m=Math.max(c,h,p),g=El(f,d,e,r,n),x=El(y,m,e,r,n);let v=t.prevZ,b=t.nextZ;for(;v&&v.z>=g&&b&&b.z<=x;){if(v.x>=f&&v.x<=y&&v.y>=d&&v.y<=m&&v!==i&&v!==a&&Fl(o,c,l,h,u,p,v.x,v.y)&&$l(v.prev,v,v.next)>=0)return !1;if(v=v.prevZ,b.x>=f&&b.x<=y&&b.y>=d&&b.y<=m&&b!==i&&b!==a&&Fl(o,c,l,h,u,p,b.x,b.y)&&$l(b.prev,b,b.next)>=0)return !1;b=b.nextZ;}for(;v&&v.z>=g;){if(v.x>=f&&v.x<=y&&v.y>=d&&v.y<=m&&v!==i&&v!==a&&Fl(o,c,l,h,u,p,v.x,v.y)&&$l(v.prev,v,v.next)>=0)return !1;v=v.prevZ;}for(;b&&b.z<=x;){if(b.x>=f&&b.x<=y&&b.y>=d&&b.y<=m&&b!==i&&b!==a&&Fl(o,c,l,h,u,p,b.x,b.y)&&$l(b.prev,b,b.next)>=0)return !1;b=b.nextZ;}return !0}function Il(t,e){let r=t;do{const n=r.prev,i=r.next.next;!Dl(n,i)&&Ol(n,r,r.next,i)&&Nl(n,i)&&Nl(i,n)&&(e.push(n.i,r.i,i.i),Gl(r),Gl(r.next),r=t=i),r=r.next;}while(r!==t);return Sl(r)}function zl(t,e,r,n,i,s){let a=t;do{let t=a.next.next;for(;t!==a.prev;){if(a.i!==t.i&&Ll(a,t)){let o=Ul(a,t);return a=Sl(a,a.next),o=Sl(o,o.next),Al(a,e,r,n,i,s,0),void Al(o,e,r,n,i,s,0)}t=t.next;}a=a.next;}while(a!==t)}function Pl(t,e){let r=t.x-e.x;return 0===r&&(r=t.y-e.y,0===r)&&(r=(t.next.y-t.y)/(t.next.x-t.x)-(e.next.y-e.y)/(e.next.x-e.x)),r}function Cl(t,e){const r=function(t,e){let r=e;const n=t.x,i=t.y;let s,a=-1/0;if(Dl(t,r))return r;do{if(Dl(t,r.next))return r.next;if(i<=r.y&&i>=r.next.y&&r.next.y!==r.y){const t=r.x+(i-r.y)*(r.next.x-r.x)/(r.next.y-r.y);if(t<=n&&t>a&&(a=t,s=r.x<r.next.x?r:r.next,t===n))return s}r=r.next;}while(r!==e);if(!s)return null;const o=s,l=s.x,u=s.y;let c=1/0;r=s;do{if(n>=r.x&&r.x>=l&&n!==r.x&&Vl(i<u?n:a,i,l,u,i<u?a:n,i,r.x,r.y)){const e=Math.abs(i-r.y)/(n-r.x);Nl(r,t)&&(e<c||e===c&&(r.x>s.x||r.x===s.x&&Bl(s,r)))&&(s=r,c=e);}r=r.next;}while(r!==o);return s}(t,e);if(!r)return e;const n=Ul(r,t);return Sl(n,n.next),Sl(r,r.next)}function Bl(t,e){return $l(t.prev,t,e.prev)<0&&$l(e.next,t,t.next)<0}function El(t,e,r,n,i){return (t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-r)*i|0)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-n)*i|0)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function Tl(t){let e=t,r=t;do{(e.x<r.x||e.x===r.x&&e.y<r.y)&&(r=e),e=e.next;}while(e!==t);return r}function Vl(t,e,r,n,i,s,a,o){return (i-a)*(e-o)>=(t-a)*(s-o)&&(t-a)*(n-o)>=(r-a)*(e-o)&&(r-a)*(s-o)>=(i-a)*(n-o)}function Fl(t,e,r,n,i,s,a,o){return !(t===a&&e===o)&&Vl(t,e,r,n,i,s,a,o)}function Ll(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){let r=t;do{if(r.i!==t.i&&r.next.i!==t.i&&r.i!==e.i&&r.next.i!==e.i&&Ol(r,r.next,t,e))return !0;r=r.next;}while(r!==t);return !1}(t,e)&&(Nl(t,e)&&Nl(e,t)&&function(t,e){let r=t,n=!1;const i=(t.x+e.x)/2,s=(t.y+e.y)/2;do{r.y>s!=r.next.y>s&&r.next.y!==r.y&&i<(r.next.x-r.x)*(s-r.y)/(r.next.y-r.y)+r.x&&(n=!n),r=r.next;}while(r!==t);return n}(t,e)&&($l(t.prev,t,e.prev)||$l(t,e.prev,e))||Dl(t,e)&&$l(t.prev,t,t.next)>0&&$l(e.prev,e,e.next)>0)}function $l(t,e,r){return (e.y-t.y)*(r.x-e.x)-(e.x-t.x)*(r.y-e.y)}function Dl(t,e){return t.x===e.x&&t.y===e.y}function Ol(t,e,r,n){const i=jl($l(t,e,r)),s=jl($l(t,e,n)),a=jl($l(r,n,t)),o=jl($l(r,n,e));return i!==s&&a!==o||!(0!==i||!Rl(t,r,e))||!(0!==s||!Rl(t,n,e))||!(0!==a||!Rl(r,t,n))||!(0!==o||!Rl(r,e,n))}function Rl(t,e,r){return e.x<=Math.max(t.x,r.x)&&e.x>=Math.min(t.x,r.x)&&e.y<=Math.max(t.y,r.y)&&e.y>=Math.min(t.y,r.y)}function jl(t){return t>0?1:t<0?-1:0}function Nl(t,e){return $l(t.prev,t,t.next)<0?$l(t,e,t.next)>=0&&$l(t,t.prev,e)>=0:$l(t,e,t.prev)<0||$l(t,t.next,e)<0}function Ul(t,e){const r=Xl(t.i,t.x,t.y),n=Xl(e.i,e.x,e.y),i=t.next,s=e.prev;return t.next=e,e.prev=t,r.next=i,i.prev=r,n.next=r,r.prev=n,s.next=n,n.prev=s,n}function ql(t,e,r,n){const i=Xl(t,e,r);return n?(i.next=n.next,i.prev=n,n.next.prev=i,n.next=i):(i.prev=i,i.next=i),i}function Gl(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ);}function Xl(t,e,r){return {i:t,x:e,y:r,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}class Zl{constructor(t,e){if(e>t)throw new Error(\"Min granularity must not be greater than base granularity.\");this._baseZoomGranularity=t,this._minGranularity=e;}getGranularityForZoomLevel(t){return Math.max(Math.floor(this._baseZoomGranularity/(1<<t)),this._minGranularity,1)}}class Yl{constructor(t){this.fill=t.fill,this.line=t.line,this.tile=t.tile,this.stencil=t.stencil,this.circle=t.circle;}}Yl.noSubdivision=new Yl({fill:new Zl(0,0),line:new Zl(0,0),tile:new Zl(0,0),stencil:new Zl(0,0),circle:1}),us(\"SubdivisionGranularityExpression\",Zl),us(\"SubdivisionGranularitySetting\",Yl);const Hl=-32768,Kl=32767;class Jl{constructor(t,e){this._vertexBuffer=[],this._vertexDictionary=new Map,this._used=!1,this._granularity=t,this._granularityCellSize=E/t,this._canonical=e;}_getKey(t,e){return (t+=32768)<<16|e+32768}_vertexToIndex(t,e){if(t<-32768||e<-32768||t>32767||e>32767)throw new Error(\"Vertex coordinates are out of signed 16 bit integer range.\");const r=0|Math.round(t),n=0|Math.round(e),i=this._getKey(r,n);if(this._vertexDictionary.has(i))return this._vertexDictionary.get(i);const s=this._vertexBuffer.length/2;return this._vertexDictionary.set(i,s),this._vertexBuffer.push(r,n),s}_subdivideTrianglesScanline(t){if(this._granularity<2)return function(t,e){const r=[];for(let n=0;n<e.length;n+=3){const i=e[n],s=e[n+1],a=e[n+2],o=t[2*i],l=t[2*i+1];(t[2*s]-o)*(t[2*a+1]-l)-(t[2*s+1]-l)*(t[2*a]-o)>0?(r.push(i),r.push(a),r.push(s)):(r.push(i),r.push(s),r.push(a));}return r}(this._vertexBuffer,t);const e=[],r=t.length;for(let n=0;n<r;n+=3){const r=[t[n+0],t[n+1],t[n+2]],i=[this._vertexBuffer[2*t[n+0]+0],this._vertexBuffer[2*t[n+0]+1],this._vertexBuffer[2*t[n+1]+0],this._vertexBuffer[2*t[n+1]+1],this._vertexBuffer[2*t[n+2]+0],this._vertexBuffer[2*t[n+2]+1]];let s=1/0,a=1/0,o=-1/0,l=-1/0;for(let t=0;t<3;t++){const e=i[2*t],r=i[2*t+1];s=Math.min(s,e),o=Math.max(o,e),a=Math.min(a,r),l=Math.max(l,r);}if(s===o||a===l)continue;const u=Math.floor(s/this._granularityCellSize),c=Math.ceil(o/this._granularityCellSize),h=Math.floor(a/this._granularityCellSize),p=Math.ceil(l/this._granularityCellSize);if(u!==c||h!==p)for(let t=h;t<p;t++){const n=this._scanlineGenerateVertexRingForCellRow(t,i,r);tu(this._vertexBuffer,n,e);}else e.push(...r);}return e}_scanlineGenerateVertexRingForCellRow(t,e,r){const n=t*this._granularityCellSize,i=n+this._granularityCellSize,s=[];for(let t=0;t<3;t++){const a=e[2*t],o=e[2*t+1],l=e[2*(t+1)%6],u=e[(2*(t+1)+1)%6],c=e[2*(t+2)%6],h=e[(2*(t+2)+1)%6],p=l-a,f=u-o,d=0===p,y=0===f,m=(n-o)/f,g=(i-o)/f,x=Math.min(m,g),v=Math.max(m,g);if(!y&&(x>=1||v<=0)||y&&(o<n||o>i)){u>=n&&u<=i&&s.push(r[(t+1)%3]);continue}!y&&x>0&&s.push(this._vertexToIndex(a+p*x,o+f*x));const b=a+p*Math.max(x,0),w=a+p*Math.min(v,1);d||this._generateIntraEdgeVertices(s,a,o,l,u,b,w),!y&&v<1&&s.push(this._vertexToIndex(a+p*v,o+f*v)),(y||u>=n&&u<=i)&&s.push(r[(t+1)%3]),!y&&(u<=n||u>=i)&&this._generateInterEdgeVertices(s,a,o,l,u,c,h,w,n,i);}return s}_generateIntraEdgeVertices(t,e,r,n,i,s,a){const o=n-e,l=i-r,u=0===l,c=u?Math.min(e,n):Math.min(s,a),h=u?Math.max(e,n):Math.max(s,a),p=Math.floor(c/this._granularityCellSize)+1,f=Math.ceil(h/this._granularityCellSize)-1;if(u?e<n:s<a)for(let n=p;n<=f;n++){const i=n*this._granularityCellSize;t.push(this._vertexToIndex(i,r+l*(i-e)/o));}else for(let n=f;n>=p;n--){const i=n*this._granularityCellSize;t.push(this._vertexToIndex(i,r+l*(i-e)/o));}}_generateInterEdgeVertices(t,e,r,n,i,s,a,o,l,u){const c=i-r,h=s-n,p=a-i,f=(l-i)/p,d=(u-i)/p,y=Math.min(f,d),m=Math.max(f,d),g=n+h*y;let x=Math.floor(Math.min(g,o)/this._granularityCellSize)+1,v=Math.ceil(Math.max(g,o)/this._granularityCellSize)-1,b=o<g;const w=0===p;if(w&&(a===l||a===u))return;if(w||y>=1||m<=0){const t=r-a,n=s+(e-s)*Math.min((l-a)/t,(u-a)/t);x=Math.floor(Math.min(n,o)/this._granularityCellSize)+1,v=Math.ceil(Math.max(n,o)/this._granularityCellSize)-1,b=o<n;}const _=c>0?u:l;if(b)for(let e=x;e<=v;e++)t.push(this._vertexToIndex(e*this._granularityCellSize,_));else for(let e=v;e>=x;e--)t.push(this._vertexToIndex(e*this._granularityCellSize,_));}_generateOutline(t){const e=[];for(const r of t){const t=Ql(r,this._granularity,!0),n=this._pointArrayToIndices(t),i=[];for(let t=1;t<n.length;t++)i.push(n[t-1]),i.push(n[t]);e.push(i);}return e}_handlePoles(t){let e=!1,r=!1;this._canonical&&(0===this._canonical.y&&(e=!0),this._canonical.y===(1<<this._canonical.z)-1&&(r=!0)),(e||r)&&this._fillPoles(t,e,r);}_ensureNoPoleVertices(){const t=this._vertexBuffer;for(let e=0;e<t.length;e+=2){const r=t[e+1];r===Hl&&(t[e+1]=-32767),r===Kl&&(t[e+1]=32766);}}_generatePoleQuad(t,e,r,n,i,s){n>i!=(s===Hl)?(t.push(e),t.push(r),t.push(this._vertexToIndex(n,s)),t.push(r),t.push(this._vertexToIndex(i,s)),t.push(this._vertexToIndex(n,s))):(t.push(r),t.push(e),t.push(this._vertexToIndex(n,s)),t.push(this._vertexToIndex(i,s)),t.push(r),t.push(this._vertexToIndex(n,s)));}_fillPoles(t,e,r){const n=this._vertexBuffer,i=E,s=t.length;for(let a=2;a<s;a+=3){const s=t[a-2],o=t[a-1],l=t[a],u=n[2*s],c=n[2*s+1],h=n[2*o],p=n[2*o+1],f=n[2*l],d=n[2*l+1];e&&(0===c&&0===p&&this._generatePoleQuad(t,s,o,u,h,Hl),0===p&&0===d&&this._generatePoleQuad(t,o,l,h,f,Hl),0===d&&0===c&&this._generatePoleQuad(t,l,s,f,u,Hl)),r&&(c===i&&p===i&&this._generatePoleQuad(t,s,o,u,h,Kl),p===i&&d===i&&this._generatePoleQuad(t,o,l,h,f,Kl),d===i&&c===i&&this._generatePoleQuad(t,l,s,f,u,Kl));}}_initializeVertices(t){for(let e=0;e<t.length;e+=2)this._vertexToIndex(t[e],t[e+1]);}subdividePolygonInternal(t,e){if(this._used)throw new Error(\"Subdivision: multiple use not allowed.\");this._used=!0;const{flattened:r,holeIndices:n}=function(t){const e=[],r=[];for(const n of t)if(0!==n.length){n!==t[0]&&e.push(r.length/2);for(let t=0;t<n.length;t++)r.push(n[t].x),r.push(n[t].y);}return {flattened:r,holeIndices:e}}(t);let i;this._initializeVertices(r);try{const t=function(t,e,r=2){const n=e&&e.length,i=n?e[0]*r:t.length;let s=_l(t,0,i,r,!0);const a=[];if(!s||s.next===s.prev)return a;let o,l,u;if(n&&(s=function(t,e,r,n){const i=[];for(let r=0,s=e.length;r<s;r++){const a=_l(t,e[r]*n,r<s-1?e[r+1]*n:t.length,n,!1);a===a.next&&(a.steiner=!0),i.push(Tl(a));}i.sort(Pl);for(let t=0;t<i.length;t++)r=Cl(i[t],r);return r}(t,e,s,r)),t.length>80*r){o=1/0,l=1/0;let e=-1/0,n=-1/0;for(let s=r;s<i;s+=r){const r=t[s],i=t[s+1];r<o&&(o=r),i<l&&(l=i),r>e&&(e=r),i>n&&(n=i);}u=Math.max(e-o,n-l),u=0!==u?32767/u:0;}return Al(s,a,r,o,l,u,0),a}(r,n),e=this._convertIndices(r,t);i=this._subdivideTrianglesScanline(e);}catch(t){console.error(t);}let s=[];return e&&(s=this._generateOutline(t)),this._ensureNoPoleVertices(),this._handlePoles(i),{verticesFlattened:this._vertexBuffer,indicesTriangles:i,indicesLineList:s}}_convertIndices(t,e){const r=[];for(let n=0;n<e.length;n++)r.push(this._vertexToIndex(t[2*e[n]],t[2*e[n]+1]));return r}_pointArrayToIndices(t){const e=[];for(let r=0;r<t.length;r++){const n=t[r];e.push(this._vertexToIndex(n.x,n.y));}return e}}function Wl(t,e,r,n=!0){return new Jl(r,e).subdividePolygonInternal(t,n)}function Ql(t,e,r=!1){if(!t||t.length<1)return [];if(t.length<2)return [];const n=t[0],i=t[t.length-1],s=r&&(n.x!==i.x||n.y!==i.y);if(e<2)return s?[...t,t[0]]:[...t];const a=Math.floor(E/e),o=[];o.push(new l(t[0].x,t[0].y));const u=t.length,c=s?u:u-1;for(let e=0;e<c;e++){const r=t[e],n=e<u-1?t[e+1]:t[0],i=r.x,s=r.y,c=n.x,h=n.y,p=i!==c,f=s!==h;if(!p&&!f)continue;const d=c-i,y=h-s,m=Math.abs(d),g=Math.abs(y);let x=i,v=s;for(;;){const t=d>0?(Math.floor(x/a)+1)*a:(Math.ceil(x/a)-1)*a,e=y>0?(Math.floor(v/a)+1)*a:(Math.ceil(v/a)-1)*a,r=Math.abs(x-t),n=Math.abs(v-e),i=Math.abs(x-c),s=Math.abs(v-h),u=p?r/m:Number.POSITIVE_INFINITY,b=f?n/g:Number.POSITIVE_INFINITY;if((i<=r||!p)&&(s<=n||!f))break;if(u<b&&p||!f){x=t,v+=y*u;const e=new l(x,Math.round(v));o[o.length-1].x===e.x&&o[o.length-1].y===e.y||o.push(e);}else {x+=d*b,v=e;const t=new l(Math.round(x),v);o[o.length-1].x===t.x&&o[o.length-1].y===t.y||o.push(t);}}const b=new l(c,h);o[o.length-1].x===b.x&&o[o.length-1].y===b.y||o.push(b);}return o}function tu(t,e,r){if(0===e.length)throw new Error(\"Subdivision vertex ring is empty.\");let n=0,i=t[2*e[0]];for(let r=1;r<e.length;r++){const s=t[2*e[r]];s<i&&(i=s,n=r);}const s=e.length;let a=n,o=(a+1)%s;for(;;){const n=a-1>=0?a-1:s-1,i=(o+1)%s,l=t[2*e[n]],u=t[2*e[i]],c=t[2*e[a]],h=t[2*e[a]+1],p=t[2*e[o]+1];let f=!1;if(l<u)f=!0;else if(l>u)f=!1;else {const r=p-h,s=-(t[2*e[o]]-c),a=h<p?1:-1;((l-c)*r+(t[2*e[n]+1]-h)*s)*a>((u-c)*r+(t[2*e[i]+1]-h)*s)*a&&(f=!0);}if(f){const t=e[n],i=e[a],l=e[o];t!==i&&t!==l&&i!==l&&r.push(l,i,t),a--,a<0&&(a=s-1);}else {const t=e[i],n=e[a],l=e[o];t!==n&&t!==l&&n!==l&&r.push(l,n,t),o++,o>=s&&(o=0);}if(n===i)break}}function eu(t,e,r,n,i,s,a,o,l){const u=i.length/2,c=a&&o&&l;if(u<Ha.MAX_VERTEX_ARRAY_LENGTH){const h=e.prepareSegment(u,r,n),p=h.vertexLength;for(let t=0;t<s.length;t+=3)n.emplaceBack(p+s[t],p+s[t+1],p+s[t+2]);let f,d;h.vertexLength+=u,h.primitiveLength+=s.length/3,c&&(d=a.prepareSegment(u,r,o),f=d.vertexLength,d.vertexLength+=u);for(let e=0;e<i.length;e+=2)t(i[e],i[e+1]);if(c)for(let t=0;t<l.length;t++){const e=l[t];for(let t=1;t<e.length;t+=2)o.emplaceBack(f+e[t-1],f+e[t]);d.primitiveLength+=e.length/2;}}else !function(t,e,r,n,i,s){const a=[];for(let t=0;t<n.length/2;t++)a.push(-1);const o={count:0};let l=0,u=t.getOrCreateLatestSegment(e,r),c=u.vertexLength;for(let h=2;h<i.length;h+=3){const p=i[h-2],f=i[h-1],d=i[h];let y=a[p]<l,m=a[f]<l,g=a[d]<l;u.vertexLength+((y?1:0)+(m?1:0)+(g?1:0))>Ha.MAX_VERTEX_ARRAY_LENGTH&&(u=t.createNewSegment(e,r),l=o.count,y=!0,m=!0,g=!0,c=0);const x=ru(a,n,s,o,p,y,u),v=ru(a,n,s,o,f,m,u),b=ru(a,n,s,o,d,g,u);r.emplaceBack(c+x-l,c+v-l,c+b-l),u.primitiveLength++;}}(e,r,n,i,s,t),c&&function(t,e,r,n,i,s){const a=[];for(let t=0;t<n.length/2;t++)a.push(-1);const o={count:0};let l=0,u=t.getOrCreateLatestSegment(e,r),c=u.vertexLength;for(let h=0;h<i.length;h++){const p=i[h];for(let f=1;f<i[h].length;f+=2){const i=p[f-1],h=p[f];let d=a[i]<l,y=a[h]<l;u.vertexLength+((d?1:0)+(y?1:0))>Ha.MAX_VERTEX_ARRAY_LENGTH&&(u=t.createNewSegment(e,r),l=o.count,d=!0,y=!0,c=0);const m=ru(a,n,s,o,i,d,u),g=ru(a,n,s,o,h,y,u);r.emplaceBack(c+m-l,c+g-l),u.primitiveLength++;}}}(a,r,o,i,l,t),e.forceNewSegmentOnNextPrepare(),null==a||a.forceNewSegmentOnNextPrepare();}function ru(t,e,r,n,i,s,a){if(s){const s=n.count;return r(e[2*i],e[2*i+1]),t[i]=n.count,n.count++,a.vertexLength++,s}return t[i]}class nu{constructor(t){this.zoom=t.zoom,this.globalState=t.globalState,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.patternFeatures=[],this.layoutVertexArray=new Fa,this.indexArray=new Ga,this.indexArray2=new Xa,this.programConfigurations=new So(t.layers,t.zoom),this.segments=new Ha,this.segments2=new Ha,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,r){this.hasPattern=bl(\"fill\",this.layers,e);const n=this.layers[0].layout.get(\"fill-sort-key\"),i=!n.isConstant(),s=[];for(const{feature:a,id:o,index:l,sourceLayerIndex:u}of t){const t=this.layers[0]._featureFilter.needGeometry,c=Po(a,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom,{globalState:this.globalState}),c,r))continue;const h=i?n.evaluate(c,{},r,e.availableImages):void 0,p={id:o,properties:a.properties,type:a.type,sourceLayerIndex:u,index:l,geometry:t?c.geometry:zo(a),patterns:{},sortKey:h};s.push(p);}i&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const n of s){const{geometry:i,index:s,sourceLayerIndex:a}=n;if(this.hasPattern){const t=wl(\"fill\",this.layers,n,this.zoom,e);this.patternFeatures.push(t);}else this.addFeature(n,i,s,r,{},e.subdivisionGranularity);e.featureIndex.insert(t[s].feature,i,s,a,this.index);}}update(t,e,r){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,r);}addFeatures(t,e,r){for(const n of this.patternFeatures)this.addFeature(n,n.geometry,n.index,e,r,t.subdivisionGranularity);}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,vl),this.indexBuffer=t.createIndexBuffer(this.indexArray),this.indexBuffer2=t.createIndexBuffer(this.indexArray2)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.indexBuffer2.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.segments2.destroy());}addFeature(t,e,r,n,i,s){for(const t of rn(e,500)){const e=Wl(t,n,s.fill.getGranularityForZoomLevel(n.z)),r=this.layoutVertexArray;eu(((t,e)=>{r.emplaceBack(t,e);}),this.segments,this.layoutVertexArray,this.indexArray,e.verticesFlattened,e.indicesTriangles,this.segments2,this.indexArray2,e.indicesLineList);}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,r,i,n);}}let iu,su;us(\"FillBucket\",nu,{omit:[\"layers\",\"patternFeatures\"]});var au={get paint(){return su=su||new qs({\"fill-antialias\":new Os(wt.paint_fill[\"fill-antialias\"]),\"fill-opacity\":new Rs(wt.paint_fill[\"fill-opacity\"]),\"fill-color\":new Rs(wt.paint_fill[\"fill-color\"]),\"fill-outline-color\":new Rs(wt.paint_fill[\"fill-outline-color\"]),\"fill-translate\":new Os(wt.paint_fill[\"fill-translate\"]),\"fill-translate-anchor\":new Os(wt.paint_fill[\"fill-translate-anchor\"]),\"fill-pattern\":new js(wt.paint_fill[\"fill-pattern\"])})},get layout(){return iu=iu||new qs({\"fill-sort-key\":new Rs(wt.layout_fill[\"fill-sort-key\"])})}};class ou extends Xs{constructor(t){super(t,au);}recalculate(t,e){super.recalculate(t,e);const r=this.paint._values[\"fill-outline-color\"];\"constant\"===r.value.kind&&void 0===r.value.value&&(this.paint._values[\"fill-outline-color\"]=this.paint._values[\"fill-color\"]);}createBucket(t){return new nu(t)}queryRadius(){return Go(this.paint.get(\"fill-translate\"))}queryIntersectsFeature({queryGeometry:t,geometry:e,transform:r,pixelsToTileUnits:n}){return Fo(Xo(t,this.paint.get(\"fill-translate\"),this.paint.get(\"fill-translate-anchor\"),-r.bearingInRadians,n),e)}isTileClipped(){return !0}}const lu=Ks([{name:\"a_pos\",components:2,type:\"Int16\"},{name:\"a_normal_ed\",components:4,type:\"Int16\"}],4),uu=Ks([{name:\"a_centroid\",components:2,type:\"Int16\"}],4),{members:cu}=lu;var hu,pu,fu,du,yu,mu,gu,xu={};function vu(){if(pu)return hu;pu=1;var t=s();function e(t,e,n,i,s){this.properties={},this.extent=n,this.type=0,this._pbf=t,this._geometry=-1,this._keys=i,this._values=s,t.readFields(r,this,e);}function r(t,e,r){1==t?e.id=r.readVarint():2==t?function(t,e){for(var r=t.readVarint()+t.pos;t.pos<r;){var n=e._keys[t.readVarint()],i=e._values[t.readVarint()];e.properties[n]=i;}}(r,e):3==t?e.type=r.readVarint():4==t&&(e._geometry=r.pos);}function n(t){for(var e,r,n=0,i=0,s=t.length,a=s-1;i<s;a=i++)n+=((r=t[a]).x-(e=t[i]).x)*(e.y+r.y);return n}return hu=e,e.types=[\"Unknown\",\"Point\",\"LineString\",\"Polygon\"],e.prototype.loadGeometry=function(){var e=this._pbf;e.pos=this._geometry;for(var r,n=e.readVarint()+e.pos,i=1,s=0,a=0,o=0,l=[];e.pos<n;){if(s<=0){var u=e.readVarint();i=7&u,s=u>>3;}if(s--,1===i||2===i)a+=e.readSVarint(),o+=e.readSVarint(),1===i&&(r&&l.push(r),r=[]),r.push(new t(a,o));else {if(7!==i)throw new Error(\"unknown command \"+i);r&&r.push(r[0].clone());}}return r&&l.push(r),l},e.prototype.bbox=function(){var t=this._pbf;t.pos=this._geometry;for(var e=t.readVarint()+t.pos,r=1,n=0,i=0,s=0,a=1/0,o=-1/0,l=1/0,u=-1/0;t.pos<e;){if(n<=0){var c=t.readVarint();r=7&c,n=c>>3;}if(n--,1===r||2===r)(i+=t.readSVarint())<a&&(a=i),i>o&&(o=i),(s+=t.readSVarint())<l&&(l=s),s>u&&(u=s);else if(7!==r)throw new Error(\"unknown command \"+r)}return [a,l,o,u]},e.prototype.toGeoJSON=function(t,r,i){var s,a,o=this.extent*Math.pow(2,i),l=this.extent*t,u=this.extent*r,c=this.loadGeometry(),h=e.types[this.type];function p(t){for(var e=0;e<t.length;e++){var r=t[e];t[e]=[360*(r.x+l)/o-180,360/Math.PI*Math.atan(Math.exp((180-360*(r.y+u)/o)*Math.PI/180))-90];}}switch(this.type){case 1:var f=[];for(s=0;s<c.length;s++)f[s]=c[s][0];p(c=f);break;case 2:for(s=0;s<c.length;s++)p(c[s]);break;case 3:for(c=function(t){var e=t.length;if(e<=1)return [t];for(var r,i,s=[],a=0;a<e;a++){var o=n(t[a]);0!==o&&(void 0===i&&(i=o<0),i===o<0?(r&&s.push(r),r=[t[a]]):r.push(t[a]));}return r&&s.push(r),s}(c),s=0;s<c.length;s++)for(a=0;a<c[s].length;a++)p(c[s][a]);}1===c.length?c=c[0]:h=\"Multi\"+h;var d={type:\"Feature\",geometry:{type:h,coordinates:c},properties:this.properties};return \"id\"in this&&(d.id=this.id),d},hu}function bu(){if(du)return fu;du=1;var t=vu();function e(t,e){this.version=1,this.name=null,this.extent=4096,this.length=0,this._pbf=t,this._keys=[],this._values=[],this._features=[],t.readFields(r,this,e),this.length=this._features.length;}function r(t,e,r){15===t?e.version=r.readVarint():1===t?e.name=r.readString():5===t?e.extent=r.readVarint():2===t?e._features.push(r.pos):3===t?e._keys.push(r.readString()):4===t&&e._values.push(function(t){for(var e=null,r=t.readVarint()+t.pos;t.pos<r;){var n=t.readVarint()>>3;e=1===n?t.readString():2===n?t.readFloat():3===n?t.readDouble():4===n?t.readVarint64():5===n?t.readVarint():6===n?t.readSVarint():7===n?t.readBoolean():null;}return e}(r));}return fu=e,e.prototype.feature=function(e){if(e<0||e>=this._features.length)throw new Error(\"feature index out of bounds\");this._pbf.pos=this._features[e];var r=this._pbf.readVarint()+this._pbf.pos;return new t(this._pbf,r,this.extent,this._keys,this._values)},fu}function wu(){return gu||(gu=1,xu.VectorTile=function(){if(mu)return yu;mu=1;var t=bu();function e(e,r,n){if(3===e){var i=new t(n,n.readVarint()+n.pos);i.length&&(r[i.name]=i);}}return yu=function(t,r){this.layers=t.readFields(e,{},r);},yu}(),xu.VectorTileFeature=vu(),xu.VectorTileLayer=bu()),xu}var _u=r(wu());const Su=_u.VectorTileFeature.types,Au=Math.pow(2,13);function ku(t,e,r,n,i,s,a,o){t.emplaceBack(e,r,2*Math.floor(n*Au)+a,i*Au*2,s*Au*2,Math.round(o));}class Mu{constructor(t){this.zoom=t.zoom,this.globalState=t.globalState,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.layoutVertexArray=new La,this.centroidVertexArray=new Ta,this.indexArray=new Ga,this.programConfigurations=new So(t.layers,t.zoom),this.segments=new Ha,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,r){this.features=[],this.hasPattern=bl(\"fill-extrusion\",this.layers,e);for(const{feature:n,id:i,index:s,sourceLayerIndex:a}of t){const t=this.layers[0]._featureFilter.needGeometry,o=Po(n,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom,{globalState:this.globalState}),o,r))continue;const l={id:i,sourceLayerIndex:a,index:s,geometry:t?o.geometry:zo(n),properties:n.properties,type:n.type,patterns:{}};this.hasPattern?this.features.push(wl(\"fill-extrusion\",this.layers,l,this.zoom,e)):this.addFeature(l,l.geometry,s,r,{},e.subdivisionGranularity),e.featureIndex.insert(n,l.geometry,s,a,this.index,!0);}}addFeatures(t,e,r){for(const n of this.features){const{geometry:i}=n;this.addFeature(n,i,n.index,e,r,t.subdivisionGranularity);}}update(t,e,r){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,r);}isEmpty(){return 0===this.layoutVertexArray.length&&0===this.centroidVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,cu),this.centroidVertexBuffer=t.createVertexBuffer(this.centroidVertexArray,uu.members,!0),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.centroidVertexBuffer.destroy());}addFeature(t,e,r,n,i,s){for(const r of rn(e,500)){const e={x:0,y:0,sampleCount:0},i=this.layoutVertexArray.length;this.processPolygon(e,n,t,r,s);const a=this.layoutVertexArray.length-i,o=Math.floor(e.x/e.sampleCount),l=Math.floor(e.y/e.sampleCount);for(let t=0;t<a;t++)this.centroidVertexArray.emplaceBack(o,l);}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,r,i,n);}processPolygon(t,e,r,n,i){if(n.length<1)return;if(Pu(n[0]))return;for(const e of n)0!==e.length&&Iu(t,e);const s={segment:this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray)},a=i.fill.getGranularityForZoomLevel(e.z),o=\"Polygon\"===Su[r.type];for(const t of n){if(0===t.length)continue;if(Pu(t))continue;const e=Ql(t,a,o);this._generateSideFaces(e,s);}if(!o)return;const l=Wl(n,e,a,!1),u=this.layoutVertexArray;eu(((t,e)=>{ku(u,t,e,0,0,1,1,0);}),this.segments,this.layoutVertexArray,this.indexArray,l.verticesFlattened,l.indicesTriangles);}_generateSideFaces(t,e){let r=0;for(let n=1;n<t.length;n++){const i=t[n],s=t[n-1];if(zu(i,s))continue;e.segment.vertexLength+4>Ha.MAX_VERTEX_ARRAY_LENGTH&&(e.segment=this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray));const a=i.sub(s)._perp()._unit(),o=s.dist(i);r+o>32768&&(r=0),ku(this.layoutVertexArray,i.x,i.y,a.x,a.y,0,0,r),ku(this.layoutVertexArray,i.x,i.y,a.x,a.y,0,1,r),r+=o,ku(this.layoutVertexArray,s.x,s.y,a.x,a.y,0,0,r),ku(this.layoutVertexArray,s.x,s.y,a.x,a.y,0,1,r);const l=e.segment.vertexLength;this.indexArray.emplaceBack(l,l+2,l+1),this.indexArray.emplaceBack(l+1,l+2,l+3),e.segment.vertexLength+=4,e.segment.primitiveLength+=2;}}}function Iu(t,e){for(let r=0;r<e.length;r++){const n=e[r];r===e.length-1&&e[0].x===n.x&&e[0].y===n.y||(t.x+=n.x,t.y+=n.y,t.sampleCount++);}}function zu(t,e){return t.x===e.x&&(t.x<0||t.x>E)||t.y===e.y&&(t.y<0||t.y>E)}function Pu(t){return t.every((t=>t.x<0))||t.every((t=>t.x>E))||t.every((t=>t.y<0))||t.every((t=>t.y>E))}let Cu;us(\"FillExtrusionBucket\",Mu,{omit:[\"layers\",\"features\"]});var Bu={get paint(){return Cu=Cu||new qs({\"fill-extrusion-opacity\":new Os(wt[\"paint_fill-extrusion\"][\"fill-extrusion-opacity\"]),\"fill-extrusion-color\":new Rs(wt[\"paint_fill-extrusion\"][\"fill-extrusion-color\"]),\"fill-extrusion-translate\":new Os(wt[\"paint_fill-extrusion\"][\"fill-extrusion-translate\"]),\"fill-extrusion-translate-anchor\":new Os(wt[\"paint_fill-extrusion\"][\"fill-extrusion-translate-anchor\"]),\"fill-extrusion-pattern\":new js(wt[\"paint_fill-extrusion\"][\"fill-extrusion-pattern\"]),\"fill-extrusion-height\":new Rs(wt[\"paint_fill-extrusion\"][\"fill-extrusion-height\"]),\"fill-extrusion-base\":new Rs(wt[\"paint_fill-extrusion\"][\"fill-extrusion-base\"]),\"fill-extrusion-vertical-gradient\":new Os(wt[\"paint_fill-extrusion\"][\"fill-extrusion-vertical-gradient\"])})}};class Eu extends Xs{constructor(t){super(t,Bu);}createBucket(t){return new Mu(t)}queryRadius(){return Go(this.paint.get(\"fill-extrusion-translate\"))}is3D(){return !0}queryIntersectsFeature({queryGeometry:t,feature:e,featureState:r,geometry:n,transform:i,pixelsToTileUnits:s,pixelPosMatrix:a}){const o=Xo(t,this.paint.get(\"fill-extrusion-translate\"),this.paint.get(\"fill-extrusion-translate-anchor\"),-i.bearingInRadians,s),u=this.paint.get(\"fill-extrusion-height\").evaluate(e,r),c=this.paint.get(\"fill-extrusion-base\").evaluate(e,r),h=function(t,e){const r=[];for(const n of t){const t=[n.x,n.y,0,1];I(t,t,e),r.push(new l(t[0]/t[3],t[1]/t[3]));}return r}(o,a),p=function(t,e,r,n){const i=[],s=[],a=n[8]*e,o=n[9]*e,u=n[10]*e,c=n[11]*e,h=n[8]*r,p=n[9]*r,f=n[10]*r,d=n[11]*r;for(const e of t){const t=[],r=[];for(const i of e){const e=i.x,s=i.y,y=n[0]*e+n[4]*s+n[12],m=n[1]*e+n[5]*s+n[13],g=n[2]*e+n[6]*s+n[14],x=n[3]*e+n[7]*s+n[15],v=g+u,b=x+c,w=y+h,_=m+p,S=g+f,A=x+d,k=new l((y+a)/b,(m+o)/b);k.z=v/b,t.push(k);const M=new l(w/A,_/A);M.z=S/A,r.push(M);}i.push(t),s.push(r);}return [i,s]}(n,c,u,a);return function(t,e,r){let n=1/0;Fo(r,e)&&(n=Vu(r,e[0]));for(let i=0;i<e.length;i++){const s=e[i],a=t[i];for(let t=0;t<s.length-1;t++){const e=s[t],i=[e,s[t+1],a[t+1],a[t],e];To(r,i)&&(n=Math.min(n,Vu(r,i)));}}return n!==1/0&&n}(p[0],p[1],h)}}function Tu(t,e){return t.x*e.x+t.y*e.y}function Vu(t,e){if(1===t.length){let r=0;const n=e[r++];let i;for(;!i||n.equals(i);)if(i=e[r++],!i)return 1/0;for(;r<e.length;r++){const s=e[r],a=t[0],o=i.sub(n),l=s.sub(n),u=a.sub(n),c=Tu(o,o),h=Tu(o,l),p=Tu(l,l),f=Tu(u,o),d=Tu(u,l),y=c*p-h*h,m=(p*f-h*d)/y,g=(c*d-h*f)/y,x=n.z*(1-m-g)+i.z*m+s.z*g;if(isFinite(x))return x}return 1/0}{let t=1/0;for(const r of e)t=Math.min(t,r.z);return t}}const Fu=Ks([{name:\"a_pos_normal\",components:2,type:\"Int16\"},{name:\"a_data\",components:4,type:\"Uint8\"}],4),{members:Lu}=Fu,$u=Ks([{name:\"a_uv_x\",components:1,type:\"Float32\"},{name:\"a_split_index\",components:1,type:\"Float32\"}]),{members:Du}=$u,Ou=_u.VectorTileFeature.types,Ru=Math.cos(Math.PI/180*37.5),ju=Math.pow(2,14)/.5;class Nu{constructor(t){this.zoom=t.zoom,this.globalState=t.globalState,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.patternFeatures=[],this.lineClipsArray=[],this.gradients={},this.layers.forEach((t=>{this.gradients[t.id]={};})),this.layoutVertexArray=new $a,this.layoutVertexArray2=new Da,this.indexArray=new Ga,this.programConfigurations=new So(t.layers,t.zoom),this.segments=new Ha,this.maxLineLength=0,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,r){this.hasPattern=bl(\"line\",this.layers,e);const n=this.layers[0].layout.get(\"line-sort-key\"),i=!n.isConstant(),s=[];for(const{feature:e,id:a,index:o,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,u=Po(e,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom,{globalState:this.globalState}),u,r))continue;const c=i?n.evaluate(u,{},r):void 0,h={id:a,properties:e.properties,type:e.type,sourceLayerIndex:l,index:o,geometry:t?u.geometry:zo(e),patterns:{},sortKey:c};s.push(h);}i&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const n of s){const{geometry:i,index:s,sourceLayerIndex:a}=n;if(this.hasPattern){const t=wl(\"line\",this.layers,n,this.zoom,e);this.patternFeatures.push(t);}else this.addFeature(n,i,s,r,{},e.subdivisionGranularity);e.featureIndex.insert(t[s].feature,i,s,a,this.index);}}update(t,e,r){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,r);}addFeatures(t,e,r){for(const n of this.patternFeatures)this.addFeature(n,n.geometry,n.index,e,r,t.subdivisionGranularity);}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(0!==this.layoutVertexArray2.length&&(this.layoutVertexBuffer2=t.createVertexBuffer(this.layoutVertexArray2,Du)),this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,Lu),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy());}lineFeatureClips(t){if(t.properties&&Object.prototype.hasOwnProperty.call(t.properties,\"mapbox_clip_start\")&&Object.prototype.hasOwnProperty.call(t.properties,\"mapbox_clip_end\"))return {start:+t.properties.mapbox_clip_start,end:+t.properties.mapbox_clip_end}}addFeature(t,e,r,n,i,s){const a=this.layers[0].layout,o=a.get(\"line-join\").evaluate(t,{}),l=a.get(\"line-cap\"),u=a.get(\"line-miter-limit\"),c=a.get(\"line-round-limit\");this.lineClips=this.lineFeatureClips(t);for(const r of e)this.addLine(r,t,o,l,u,c,n,s);this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,r,i,n);}addLine(t,e,r,n,i,s,a,o){if(this.distance=0,this.scaledDistance=0,this.totalDistance=0,t=Ql(t,a?o.line.getGranularityForZoomLevel(a.z):1),this.lineClips){this.lineClipsArray.push(this.lineClips);for(let e=0;e<t.length-1;e++)this.totalDistance+=t[e].dist(t[e+1]);this.updateScaledDistance(),this.maxLineLength=Math.max(this.maxLineLength,this.totalDistance);}const l=\"Polygon\"===Ou[e.type];let u=t.length;for(;u>=2&&t[u-1].equals(t[u-2]);)u--;let c=0;for(;c<u-1&&t[c].equals(t[c+1]);)c++;if(u<(l?3:2))return;\"bevel\"===r&&(i=1.05);const h=this.overscaling<=16?122880/(512*this.overscaling):0,p=this.segments.prepareSegment(10*u,this.layoutVertexArray,this.indexArray);let f,d,y,m,g;this.e1=this.e2=-1,l&&(f=t[u-2],g=t[c].sub(f)._unit()._perp());for(let e=c;e<u;e++){if(y=e===u-1?l?t[c+1]:void 0:t[e+1],y&&t[e].equals(y))continue;g&&(m=g),f&&(d=f),f=t[e],g=y?y.sub(f)._unit()._perp():m,m=m||g;let a=m.add(g);0===a.x&&0===a.y||a._unit();const o=m.x*g.x+m.y*g.y,x=a.x*g.x+a.y*g.y,v=0!==x?1/x:1/0,b=2*Math.sqrt(2-2*x),w=x<Ru&&d&&y,_=m.x*g.y-m.y*g.x>0;if(w&&e>c){const t=f.dist(d);if(t>2*h){const e=f.sub(f.sub(d)._mult(h/t)._round());this.updateDistance(d,e),this.addCurrentVertex(e,m,0,0,p),d=e;}}const S=d&&y;let A=S?r:l?\"butt\":n;if(S&&\"round\"===A&&(v<s?A=\"miter\":v<=2&&(A=\"fakeround\")),\"miter\"===A&&v>i&&(A=\"bevel\"),\"bevel\"===A&&(v>2&&(A=\"flipbevel\"),v<i&&(A=\"miter\")),d&&this.updateDistance(d,f),\"miter\"===A)a._mult(v),this.addCurrentVertex(f,a,0,0,p);else if(\"flipbevel\"===A){if(v>100)a=g.mult(-1);else {const t=v*m.add(g).mag()/m.sub(g).mag();a._perp()._mult(t*(_?-1:1));}this.addCurrentVertex(f,a,0,0,p),this.addCurrentVertex(f,a.mult(-1),0,0,p);}else if(\"bevel\"===A||\"fakeround\"===A){const t=-Math.sqrt(v*v-1),e=_?t:0,r=_?0:t;if(d&&this.addCurrentVertex(f,m,e,r,p),\"fakeround\"===A){const t=Math.round(180*b/Math.PI/20);for(let e=1;e<t;e++){let r=e/t;if(.5!==r){const t=r-.5;r+=r*t*(r-1)*((1.0904+o*(o*(3.55645-1.43519*o)-3.2452))*t*t+(.848013+o*(.215638*o-1.06021)));}const n=g.sub(m)._mult(r)._add(m)._unit()._mult(_?-1:1);this.addHalfVertex(f,n.x,n.y,!1,_,0,p);}}y&&this.addCurrentVertex(f,g,-e,-r,p);}else if(\"butt\"===A)this.addCurrentVertex(f,a,0,0,p);else if(\"square\"===A){const t=d?1:-1;this.addCurrentVertex(f,a,t,t,p);}else \"round\"===A&&(d&&(this.addCurrentVertex(f,m,0,0,p),this.addCurrentVertex(f,m,1,1,p,!0)),y&&(this.addCurrentVertex(f,g,-1,-1,p,!0),this.addCurrentVertex(f,g,0,0,p)));if(w&&e<u-1){const t=f.dist(y);if(t>2*h){const e=f.add(y.sub(f)._mult(h/t)._round());this.updateDistance(f,e),this.addCurrentVertex(e,g,0,0,p),f=e;}}}}addCurrentVertex(t,e,r,n,i,s=!1){const a=e.y*n-e.x,o=-e.y-e.x*n;this.addHalfVertex(t,e.x+e.y*r,e.y-e.x*r,s,!1,r,i),this.addHalfVertex(t,a,o,s,!0,-n,i),this.distance>ju/2&&0===this.totalDistance&&(this.distance=0,this.updateScaledDistance(),this.addCurrentVertex(t,e,r,n,i,s));}addHalfVertex({x:t,y:e},r,n,i,s,a,o){const l=.5*(this.lineClips?this.scaledDistance*(ju-1):this.scaledDistance);this.layoutVertexArray.emplaceBack((t<<1)+(i?1:0),(e<<1)+(s?1:0),Math.round(63*r)+128,Math.round(63*n)+128,1+(0===a?0:a<0?-1:1)|(63&l)<<2,l>>6),this.lineClips&&this.layoutVertexArray2.emplaceBack((this.scaledDistance-this.lineClips.start)/(this.lineClips.end-this.lineClips.start),this.lineClipsArray.length);const u=o.vertexLength++;this.e1>=0&&this.e2>=0&&(this.indexArray.emplaceBack(this.e1,u,this.e2),o.primitiveLength++),s?this.e2=u:this.e1=u;}updateScaledDistance(){this.scaledDistance=this.lineClips?this.lineClips.start+(this.lineClips.end-this.lineClips.start)*this.distance/this.totalDistance:this.distance;}updateDistance(t,e){this.distance+=t.dist(e),this.updateScaledDistance();}}let Uu,qu;us(\"LineBucket\",Nu,{omit:[\"layers\",\"patternFeatures\"]});var Gu={get paint(){return qu=qu||new qs({\"line-opacity\":new Rs(wt.paint_line[\"line-opacity\"]),\"line-color\":new Rs(wt.paint_line[\"line-color\"]),\"line-translate\":new Os(wt.paint_line[\"line-translate\"]),\"line-translate-anchor\":new Os(wt.paint_line[\"line-translate-anchor\"]),\"line-width\":new Rs(wt.paint_line[\"line-width\"]),\"line-gap-width\":new Rs(wt.paint_line[\"line-gap-width\"]),\"line-offset\":new Rs(wt.paint_line[\"line-offset\"]),\"line-blur\":new Rs(wt.paint_line[\"line-blur\"]),\"line-dasharray\":new Ns(wt.paint_line[\"line-dasharray\"]),\"line-pattern\":new js(wt.paint_line[\"line-pattern\"]),\"line-gradient\":new Us(wt.paint_line[\"line-gradient\"])})},get layout(){return Uu=Uu||new qs({\"line-cap\":new Os(wt.layout_line[\"line-cap\"]),\"line-join\":new Rs(wt.layout_line[\"line-join\"]),\"line-miter-limit\":new Os(wt.layout_line[\"line-miter-limit\"]),\"line-round-limit\":new Os(wt.layout_line[\"line-round-limit\"]),\"line-sort-key\":new Rs(wt.layout_line[\"line-sort-key\"])})}};class Xu extends Rs{possiblyEvaluate(t,e){return e=new Cs(Math.floor(e.zoom),{now:e.now,fadeDuration:e.fadeDuration,zoomHistory:e.zoomHistory,transition:e.transition}),super.possiblyEvaluate(t,e)}evaluate(t,e,r,n){return e=j({},e,{zoom:Math.floor(e.zoom)}),super.evaluate(t,e,r,n)}}let Zu;class Yu extends Xs{constructor(t){super(t,Gu),this.gradientVersion=0,Zu||(Zu=new Xu(Gu.paint.properties[\"line-width\"].specification),Zu.useIntegerZoom=!0);}_handleSpecialPaintPropertyUpdate(t){if(\"line-gradient\"===t){const t=this.gradientExpression();this.stepInterpolant=!!function(t){return void 0!==t._styleExpression}(t)&&t._styleExpression.expression instanceof ur,this.gradientVersion=(this.gradientVersion+1)%Number.MAX_SAFE_INTEGER;}}gradientExpression(){return this._transitionablePaint._values[\"line-gradient\"].value.expression}recalculate(t,e){super.recalculate(t,e),this.paint._values[\"line-floorwidth\"]=Zu.possiblyEvaluate(this._transitioningPaint._values[\"line-width\"].value,t);}createBucket(t){return new Nu(t)}queryRadius(t){const e=t,r=Hu(qo(\"line-width\",this,e),qo(\"line-gap-width\",this,e)),n=qo(\"line-offset\",this,e);return r/2+Math.abs(n)+Go(this.paint.get(\"line-translate\"))}queryIntersectsFeature({queryGeometry:t,feature:e,featureState:r,geometry:n,transform:i,pixelsToTileUnits:s}){const a=Xo(t,this.paint.get(\"line-translate\"),this.paint.get(\"line-translate-anchor\"),-i.bearingInRadians,s),o=s/2*Hu(this.paint.get(\"line-width\").evaluate(e,r),this.paint.get(\"line-gap-width\").evaluate(e,r)),u=this.paint.get(\"line-offset\").evaluate(e,r);return u&&(n=function(t,e){const r=[];for(let n=0;n<t.length;n++){const i=t[n],s=[];for(let t=0;t<i.length;t++){const r=i[t-1],n=i[t],a=i[t+1],o=0===t?new l(0,0):n.sub(r)._unit()._perp(),u=t===i.length-1?new l(0,0):a.sub(n)._unit()._perp(),c=o._add(u)._unit(),h=c.x*u.x+c.y*u.y;0!==h&&c._mult(1/h),s.push(c._mult(e)._add(n));}r.push(s);}return r}(n,u*s)),function(t,e,r){for(let n=0;n<e.length;n++){const i=e[n];if(t.length>=3)for(let e=0;e<i.length;e++)if(No(t,i[e]))return !0;if(Lo(t,i,r))return !0}return !1}(a,n,o)}isTileClipped(){return !0}}function Hu(t,e){return e>0?e+2*t:t}const Ku=Ks([{name:\"a_pos_offset\",components:4,type:\"Int16\"},{name:\"a_data\",components:4,type:\"Uint16\"},{name:\"a_pixeloffset\",components:4,type:\"Int16\"}],4),Ju=Ks([{name:\"a_projected_pos\",components:3,type:\"Float32\"}],4);Ks([{name:\"a_fade_opacity\",components:1,type:\"Uint32\"}],4);const Wu=Ks([{name:\"a_placed\",components:2,type:\"Uint8\"},{name:\"a_shift\",components:2,type:\"Float32\"},{name:\"a_box_real\",components:2,type:\"Int16\"}]);Ks([{type:\"Int16\",name:\"anchorPointX\"},{type:\"Int16\",name:\"anchorPointY\"},{type:\"Int16\",name:\"x1\"},{type:\"Int16\",name:\"y1\"},{type:\"Int16\",name:\"x2\"},{type:\"Int16\",name:\"y2\"},{type:\"Uint32\",name:\"featureIndex\"},{type:\"Uint16\",name:\"sourceLayerIndex\"},{type:\"Uint16\",name:\"bucketIndex\"}]);const Qu=Ks([{name:\"a_pos\",components:2,type:\"Int16\"},{name:\"a_anchor_pos\",components:2,type:\"Int16\"},{name:\"a_extrude\",components:2,type:\"Int16\"}],4),tc=Ks([{name:\"a_pos\",components:2,type:\"Float32\"},{name:\"a_radius\",components:1,type:\"Float32\"},{name:\"a_flags\",components:2,type:\"Int16\"}],4);function ec(t,e,r){return t.sections.forEach((t=>{t.text=function(t,e,r){const n=e.layout.get(\"text-transform\").evaluate(r,{});return \"uppercase\"===n?t=t.toLocaleUpperCase():\"lowercase\"===n&&(t=t.toLocaleLowerCase()),Ps.applyArabicShaping&&(t=Ps.applyArabicShaping(t)),t}(t.text,e,r);})),t}Ks([{name:\"triangle\",components:3,type:\"Uint16\"}]),Ks([{type:\"Int16\",name:\"anchorX\"},{type:\"Int16\",name:\"anchorY\"},{type:\"Uint16\",name:\"glyphStartIndex\"},{type:\"Uint16\",name:\"numGlyphs\"},{type:\"Uint32\",name:\"vertexStartIndex\"},{type:\"Uint32\",name:\"lineStartIndex\"},{type:\"Uint32\",name:\"lineLength\"},{type:\"Uint16\",name:\"segment\"},{type:\"Uint16\",name:\"lowerSize\"},{type:\"Uint16\",name:\"upperSize\"},{type:\"Float32\",name:\"lineOffsetX\"},{type:\"Float32\",name:\"lineOffsetY\"},{type:\"Uint8\",name:\"writingMode\"},{type:\"Uint8\",name:\"placedOrientation\"},{type:\"Uint8\",name:\"hidden\"},{type:\"Uint32\",name:\"crossTileID\"},{type:\"Int16\",name:\"associatedIconIndex\"}]),Ks([{type:\"Int16\",name:\"anchorX\"},{type:\"Int16\",name:\"anchorY\"},{type:\"Int16\",name:\"rightJustifiedTextSymbolIndex\"},{type:\"Int16\",name:\"centerJustifiedTextSymbolIndex\"},{type:\"Int16\",name:\"leftJustifiedTextSymbolIndex\"},{type:\"Int16\",name:\"verticalPlacedTextSymbolIndex\"},{type:\"Int16\",name:\"placedIconSymbolIndex\"},{type:\"Int16\",name:\"verticalPlacedIconSymbolIndex\"},{type:\"Uint16\",name:\"key\"},{type:\"Uint16\",name:\"textBoxStartIndex\"},{type:\"Uint16\",name:\"textBoxEndIndex\"},{type:\"Uint16\",name:\"verticalTextBoxStartIndex\"},{type:\"Uint16\",name:\"verticalTextBoxEndIndex\"},{type:\"Uint16\",name:\"iconBoxStartIndex\"},{type:\"Uint16\",name:\"iconBoxEndIndex\"},{type:\"Uint16\",name:\"verticalIconBoxStartIndex\"},{type:\"Uint16\",name:\"verticalIconBoxEndIndex\"},{type:\"Uint16\",name:\"featureIndex\"},{type:\"Uint16\",name:\"numHorizontalGlyphVertices\"},{type:\"Uint16\",name:\"numVerticalGlyphVertices\"},{type:\"Uint16\",name:\"numIconVertices\"},{type:\"Uint16\",name:\"numVerticalIconVertices\"},{type:\"Uint16\",name:\"useRuntimeCollisionCircles\"},{type:\"Uint32\",name:\"crossTileID\"},{type:\"Float32\",name:\"textBoxScale\"},{type:\"Float32\",name:\"collisionCircleDiameter\"},{type:\"Uint16\",name:\"textAnchorOffsetStartIndex\"},{type:\"Uint16\",name:\"textAnchorOffsetEndIndex\"}]),Ks([{type:\"Float32\",name:\"offsetX\"}]),Ks([{type:\"Int16\",name:\"x\"},{type:\"Int16\",name:\"y\"},{type:\"Int16\",name:\"tileUnitDistanceFromAnchor\"}]),Ks([{type:\"Uint16\",name:\"textAnchor\"},{type:\"Float32\",components:2,name:\"textOffset\"}]);const rc={\"!\":\"︕\",\"#\":\"＃\",$:\"＄\",\"%\":\"％\",\"&\":\"＆\",\"(\":\"︵\",\")\":\"︶\",\"*\":\"＊\",\"+\":\"＋\",\",\":\"︐\",\"-\":\"︲\",\".\":\"・\",\"/\":\"／\",\":\":\"︓\",\";\":\"︔\",\"<\":\"︿\",\"=\":\"＝\",\">\":\"﹀\",\"?\":\"︖\",\"@\":\"＠\",\"[\":\"﹇\",\"\\\\\":\"＼\",\"]\":\"﹈\",\"^\":\"＾\",_:\"︳\",\"`\":\"｀\",\"{\":\"︷\",\"|\":\"―\",\"}\":\"︸\",\"~\":\"～\",\"¢\":\"￠\",\"£\":\"￡\",\"¥\":\"￥\",\"¦\":\"￤\",\"¬\":\"￢\",\"¯\":\"￣\",\"–\":\"︲\",\"—\":\"︱\",\"‘\":\"﹃\",\"’\":\"﹄\",\"“\":\"﹁\",\"”\":\"﹂\",\"…\":\"︙\",\"‧\":\"・\",\"₩\":\"￦\",\"、\":\"︑\",\"。\":\"︒\",\"〈\":\"︿\",\"〉\":\"﹀\",\"《\":\"︽\",\"》\":\"︾\",\"「\":\"﹁\",\"」\":\"﹂\",\"『\":\"﹃\",\"』\":\"﹄\",\"【\":\"︻\",\"】\":\"︼\",\"〔\":\"︹\",\"〕\":\"︺\",\"〖\":\"︗\",\"〗\":\"︘\",\"！\":\"︕\",\"（\":\"︵\",\"）\":\"︶\",\"，\":\"︐\",\"－\":\"︲\",\"．\":\"・\",\"：\":\"︓\",\"；\":\"︔\",\"＜\":\"︿\",\"＞\":\"﹀\",\"？\":\"︖\",\"［\":\"﹇\",\"］\":\"﹈\",\"＿\":\"︳\",\"｛\":\"︷\",\"｜\":\"―\",\"｝\":\"︸\",\"｟\":\"︵\",\"｠\":\"︶\",\"｡\":\"︒\",\"｢\":\"﹁\",\"｣\":\"﹂\"};var nc,ic,sc,ac=24,oc={};function lc(){return nc||(nc=1,oc.read=function(t,e,r,n,i){var s,a,o=8*i-n-1,l=(1<<o)-1,u=l>>1,c=-7,h=r?i-1:0,p=r?-1:1,f=t[e+h];for(h+=p,s=f&(1<<-c)-1,f>>=-c,c+=o;c>0;s=256*s+t[e+h],h+=p,c-=8);for(a=s&(1<<-c)-1,s>>=-c,c+=n;c>0;a=256*a+t[e+h],h+=p,c-=8);if(0===s)s=1-u;else {if(s===l)return a?NaN:1/0*(f?-1:1);a+=Math.pow(2,n),s-=u;}return (f?-1:1)*a*Math.pow(2,s-n)},oc.write=function(t,e,r,n,i,s){var a,o,l,u=8*s-i-1,c=(1<<u)-1,h=c>>1,p=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,f=n?0:s-1,d=n?1:-1,y=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(o=isNaN(e)?1:0,a=c):(a=Math.floor(Math.log(e)/Math.LN2),e*(l=Math.pow(2,-a))<1&&(a--,l*=2),(e+=a+h>=1?p/l:p*Math.pow(2,1-h))*l>=2&&(a++,l/=2),a+h>=c?(o=0,a=c):a+h>=1?(o=(e*l-1)*Math.pow(2,i),a+=h):(o=e*Math.pow(2,h-1)*Math.pow(2,i),a=0));i>=8;t[r+f]=255&o,f+=d,o/=256,i-=8);for(a=a<<i|o,u+=i;u>0;t[r+f]=255&a,f+=d,a/=256,u-=8);t[r+f-d]|=128*y;}),oc}function uc(){if(sc)return ic;sc=1,ic=e;var t=lc();function e(t){this.buf=ArrayBuffer.isView&&ArrayBuffer.isView(t)?t:new Uint8Array(t||0),this.pos=0,this.type=0,this.length=this.buf.length;}e.Varint=0,e.Fixed64=1,e.Bytes=2,e.Fixed32=5;var r=4294967296,n=1/r,i=\"undefined\"==typeof TextDecoder?null:new TextDecoder(\"utf-8\");function s(t){return t.type===e.Bytes?t.readVarint()+t.pos:t.pos+1}function a(t,e,r){return r?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function o(t,e,r){var n=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.floor(Math.log(e)/(7*Math.LN2));r.realloc(n);for(var i=r.pos-1;i>=t;i--)r.buf[i+n]=r.buf[i];}function l(t,e){for(var r=0;r<t.length;r++)e.writeVarint(t[r]);}function u(t,e){for(var r=0;r<t.length;r++)e.writeSVarint(t[r]);}function c(t,e){for(var r=0;r<t.length;r++)e.writeFloat(t[r]);}function h(t,e){for(var r=0;r<t.length;r++)e.writeDouble(t[r]);}function p(t,e){for(var r=0;r<t.length;r++)e.writeBoolean(t[r]);}function f(t,e){for(var r=0;r<t.length;r++)e.writeFixed32(t[r]);}function d(t,e){for(var r=0;r<t.length;r++)e.writeSFixed32(t[r]);}function y(t,e){for(var r=0;r<t.length;r++)e.writeFixed64(t[r]);}function m(t,e){for(var r=0;r<t.length;r++)e.writeSFixed64(t[r]);}function g(t,e){return (t[e]|t[e+1]<<8|t[e+2]<<16)+16777216*t[e+3]}function x(t,e,r){t[r]=e,t[r+1]=e>>>8,t[r+2]=e>>>16,t[r+3]=e>>>24;}function v(t,e){return (t[e]|t[e+1]<<8|t[e+2]<<16)+(t[e+3]<<24)}return e.prototype={destroy:function(){this.buf=null;},readFields:function(t,e,r){for(r=r||this.length;this.pos<r;){var n=this.readVarint(),i=n>>3,s=this.pos;this.type=7&n,t(i,e,this),this.pos===s&&this.skip(n);}return e},readMessage:function(t,e){return this.readFields(t,e,this.readVarint()+this.pos)},readFixed32:function(){var t=g(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=v(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=g(this.buf,this.pos)+g(this.buf,this.pos+4)*r;return this.pos+=8,t},readSFixed64:function(){var t=g(this.buf,this.pos)+v(this.buf,this.pos+4)*r;return this.pos+=8,t},readFloat:function(){var e=t.read(this.buf,this.pos,!0,23,4);return this.pos+=4,e},readDouble:function(){var e=t.read(this.buf,this.pos,!0,52,8);return this.pos+=8,e},readVarint:function(t){var e,r,n=this.buf;return e=127&(r=n[this.pos++]),r<128?e:(e|=(127&(r=n[this.pos++]))<<7,r<128?e:(e|=(127&(r=n[this.pos++]))<<14,r<128?e:(e|=(127&(r=n[this.pos++]))<<21,r<128?e:function(t,e,r){var n,i,s=r.buf;if(n=(112&(i=s[r.pos++]))>>4,i<128)return a(t,n,e);if(n|=(127&(i=s[r.pos++]))<<3,i<128)return a(t,n,e);if(n|=(127&(i=s[r.pos++]))<<10,i<128)return a(t,n,e);if(n|=(127&(i=s[r.pos++]))<<17,i<128)return a(t,n,e);if(n|=(127&(i=s[r.pos++]))<<24,i<128)return a(t,n,e);if(n|=(1&(i=s[r.pos++]))<<31,i<128)return a(t,n,e);throw new Error(\"Expected varint not more than 10 bytes\")}(e|=(15&(r=n[this.pos]))<<28,t,this))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2==1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,e=this.pos;return this.pos=t,t-e>=12&&i?function(t,e,r){return i.decode(t.subarray(e,r))}(this.buf,e,t):function(t,e,r){for(var n=\"\",i=e;i<r;){var s,a,o,l=t[i],u=null,c=l>239?4:l>223?3:l>191?2:1;if(i+c>r)break;1===c?l<128&&(u=l):2===c?128==(192&(s=t[i+1]))&&(u=(31&l)<<6|63&s)<=127&&(u=null):3===c?(a=t[i+2],128==(192&(s=t[i+1]))&&128==(192&a)&&((u=(15&l)<<12|(63&s)<<6|63&a)<=2047||u>=55296&&u<=57343)&&(u=null)):4===c&&(a=t[i+2],o=t[i+3],128==(192&(s=t[i+1]))&&128==(192&a)&&128==(192&o)&&((u=(15&l)<<18|(63&s)<<12|(63&a)<<6|63&o)<=65535||u>=1114112)&&(u=null)),null===u?(u=65533,c=1):u>65535&&(u-=65536,n+=String.fromCharCode(u>>>10&1023|55296),u=56320|1023&u),n+=String.fromCharCode(u),i+=c;}return n}(this.buf,e,t)},readBytes:function(){var t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e},readPackedVarint:function(t,r){if(this.type!==e.Bytes)return t.push(this.readVarint(r));var n=s(this);for(t=t||[];this.pos<n;)t.push(this.readVarint(r));return t},readPackedSVarint:function(t){if(this.type!==e.Bytes)return t.push(this.readSVarint());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readSVarint());return t},readPackedBoolean:function(t){if(this.type!==e.Bytes)return t.push(this.readBoolean());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readBoolean());return t},readPackedFloat:function(t){if(this.type!==e.Bytes)return t.push(this.readFloat());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readFloat());return t},readPackedDouble:function(t){if(this.type!==e.Bytes)return t.push(this.readDouble());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readDouble());return t},readPackedFixed32:function(t){if(this.type!==e.Bytes)return t.push(this.readFixed32());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readFixed32());return t},readPackedSFixed32:function(t){if(this.type!==e.Bytes)return t.push(this.readSFixed32());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readSFixed32());return t},readPackedFixed64:function(t){if(this.type!==e.Bytes)return t.push(this.readFixed64());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readFixed64());return t},readPackedSFixed64:function(t){if(this.type!==e.Bytes)return t.push(this.readSFixed64());var r=s(this);for(t=t||[];this.pos<r;)t.push(this.readSFixed64());return t},skip:function(t){var r=7&t;if(r===e.Varint)for(;this.buf[this.pos++]>127;);else if(r===e.Bytes)this.pos=this.readVarint()+this.pos;else if(r===e.Fixed32)this.pos+=4;else {if(r!==e.Fixed64)throw new Error(\"Unimplemented type: \"+r);this.pos+=8;}},writeTag:function(t,e){this.writeVarint(t<<3|e);},realloc:function(t){for(var e=this.length||16;e<this.pos+t;)e*=2;if(e!==this.length){var r=new Uint8Array(e);r.set(this.buf),this.buf=r,this.length=e;}},finish:function(){return this.length=this.pos,this.pos=0,this.buf.subarray(0,this.length)},writeFixed32:function(t){this.realloc(4),x(this.buf,t,this.pos),this.pos+=4;},writeSFixed32:function(t){this.realloc(4),x(this.buf,t,this.pos),this.pos+=4;},writeFixed64:function(t){this.realloc(8),x(this.buf,-1&t,this.pos),x(this.buf,Math.floor(t*n),this.pos+4),this.pos+=8;},writeSFixed64:function(t){this.realloc(8),x(this.buf,-1&t,this.pos),x(this.buf,Math.floor(t*n),this.pos+4),this.pos+=8;},writeVarint:function(t){(t=+t||0)>268435455||t<0?function(t,e){var r,n;if(t>=0?(r=t%4294967296|0,n=t/4294967296|0):(n=~(-t/4294967296),4294967295^(r=~(-t%4294967296))?r=r+1|0:(r=0,n=n+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error(\"Given varint doesn't fit into 10 bytes\");e.realloc(10),function(t,e,r){r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,r.buf[r.pos]=127&(t>>>=7);}(r,0,e),function(t,e){var r=(7&t)<<4;e.buf[e.pos++]|=r|((t>>>=3)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t)))));}(n,e);}(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127))));},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t);},writeBoolean:function(t){this.writeVarint(Boolean(t));},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var e=this.pos;this.pos=function(t,e,r){for(var n,i,s=0;s<e.length;s++){if((n=e.charCodeAt(s))>55295&&n<57344){if(!i){n>56319||s+1===e.length?(t[r++]=239,t[r++]=191,t[r++]=189):i=n;continue}if(n<56320){t[r++]=239,t[r++]=191,t[r++]=189,i=n;continue}n=i-55296<<10|n-56320|65536,i=null;}else i&&(t[r++]=239,t[r++]=191,t[r++]=189,i=null);n<128?t[r++]=n:(n<2048?t[r++]=n>>6|192:(n<65536?t[r++]=n>>12|224:(t[r++]=n>>18|240,t[r++]=n>>12&63|128),t[r++]=n>>6&63|128),t[r++]=63&n|128);}return r}(this.buf,t,this.pos);var r=this.pos-e;r>=128&&o(e,r,this),this.pos=e-1,this.writeVarint(r),this.pos+=r;},writeFloat:function(e){this.realloc(4),t.write(this.buf,e,this.pos,!0,23,4),this.pos+=4;},writeDouble:function(e){this.realloc(8),t.write(this.buf,e,this.pos,!0,52,8),this.pos+=8;},writeBytes:function(t){var e=t.length;this.writeVarint(e),this.realloc(e);for(var r=0;r<e;r++)this.buf[this.pos++]=t[r];},writeRawMessage:function(t,e){this.pos++;var r=this.pos;t(e,this);var n=this.pos-r;n>=128&&o(r,n,this),this.pos=r-1,this.writeVarint(n),this.pos+=n;},writeMessage:function(t,r,n){this.writeTag(t,e.Bytes),this.writeRawMessage(r,n);},writePackedVarint:function(t,e){e.length&&this.writeMessage(t,l,e);},writePackedSVarint:function(t,e){e.length&&this.writeMessage(t,u,e);},writePackedBoolean:function(t,e){e.length&&this.writeMessage(t,p,e);},writePackedFloat:function(t,e){e.length&&this.writeMessage(t,c,e);},writePackedDouble:function(t,e){e.length&&this.writeMessage(t,h,e);},writePackedFixed32:function(t,e){e.length&&this.writeMessage(t,f,e);},writePackedSFixed32:function(t,e){e.length&&this.writeMessage(t,d,e);},writePackedFixed64:function(t,e){e.length&&this.writeMessage(t,y,e);},writePackedSFixed64:function(t,e){e.length&&this.writeMessage(t,m,e);},writeBytesField:function(t,r){this.writeTag(t,e.Bytes),this.writeBytes(r);},writeFixed32Field:function(t,r){this.writeTag(t,e.Fixed32),this.writeFixed32(r);},writeSFixed32Field:function(t,r){this.writeTag(t,e.Fixed32),this.writeSFixed32(r);},writeFixed64Field:function(t,r){this.writeTag(t,e.Fixed64),this.writeFixed64(r);},writeSFixed64Field:function(t,r){this.writeTag(t,e.Fixed64),this.writeSFixed64(r);},writeVarintField:function(t,r){this.writeTag(t,e.Varint),this.writeVarint(r);},writeSVarintField:function(t,r){this.writeTag(t,e.Varint),this.writeSVarint(r);},writeStringField:function(t,r){this.writeTag(t,e.Bytes),this.writeString(r);},writeFloatField:function(t,r){this.writeTag(t,e.Fixed32),this.writeFloat(r);},writeDoubleField:function(t,r){this.writeTag(t,e.Fixed64),this.writeDouble(r);},writeBooleanField:function(t,e){this.writeVarintField(t,Boolean(e));}},ic}var cc,hc=r(uc());function pc(t,e,r){1===t&&r.readMessage(fc,e);}function fc(t,e,r){if(3===t){const{id:t,bitmap:n,width:i,height:s,left:a,top:o,advance:l}=r.readMessage(dc,{});e.push({id:t,bitmap:new il({width:i+6,height:s+6},n),metrics:{width:i,height:s,left:a,top:o,advance:l}});}}function dc(t,e,r){1===t?e.id=r.readVarint():2===t?e.bitmap=r.readBytes():3===t?e.width=r.readVarint():4===t?e.height=r.readVarint():5===t?e.left=r.readSVarint():6===t?e.top=r.readSVarint():7===t&&(e.advance=r.readVarint());}function yc(t){let e=0,r=0;for(const n of t)e+=n.w*n.h,r=Math.max(r,n.w);t.sort(((t,e)=>e.h-t.h));const n=[{x:0,y:0,w:Math.max(Math.ceil(Math.sqrt(e/.95)),r),h:1/0}];let i=0,s=0;for(const e of t)for(let t=n.length-1;t>=0;t--){const r=n[t];if(!(e.w>r.w||e.h>r.h)){if(e.x=r.x,e.y=r.y,s=Math.max(s,e.y+e.h),i=Math.max(i,e.x+e.w),e.w===r.w&&e.h===r.h){const e=n.pop();t<n.length&&(n[t]=e);}else e.h===r.h?(r.x+=e.w,r.w-=e.w):e.w===r.w?(r.y+=e.h,r.h-=e.h):(n.push({x:r.x+e.w,y:r.y,w:r.w-e.w,h:e.h}),r.y+=e.h,r.h-=e.h);break}}return {w:i,h:s,fill:e/(i*s)||0}}class mc{constructor(t,{pixelRatio:e,version:r,stretchX:n,stretchY:i,content:s,textFitWidth:a,textFitHeight:o}){this.paddedRect=t,this.pixelRatio=e,this.stretchX=n,this.stretchY=i,this.content=s,this.version=r,this.textFitWidth=a,this.textFitHeight=o;}get tl(){return [this.paddedRect.x+1,this.paddedRect.y+1]}get br(){return [this.paddedRect.x+this.paddedRect.w-1,this.paddedRect.y+this.paddedRect.h-1]}get tlbr(){return this.tl.concat(this.br)}get displaySize(){return [(this.paddedRect.w-2)/this.pixelRatio,(this.paddedRect.h-2)/this.pixelRatio]}}class gc{constructor(t,e){const r={},n={};this.haveRenderCallbacks=[];const i=[];this.addImages(t,r,i),this.addImages(e,n,i);const{w:s,h:a}=yc(i),o=new sl({width:s||1,height:a||1});for(const e in t){const n=t[e],i=r[e].paddedRect;sl.copy(n.data,o,{x:0,y:0},{x:i.x+1,y:i.y+1},n.data);}for(const t in e){const r=e[t],i=n[t].paddedRect,s=i.x+1,a=i.y+1,l=r.data.width,u=r.data.height;sl.copy(r.data,o,{x:0,y:0},{x:s,y:a},r.data),sl.copy(r.data,o,{x:0,y:u-1},{x:s,y:a-1},{width:l,height:1}),sl.copy(r.data,o,{x:0,y:0},{x:s,y:a+u},{width:l,height:1}),sl.copy(r.data,o,{x:l-1,y:0},{x:s-1,y:a},{width:1,height:u}),sl.copy(r.data,o,{x:0,y:0},{x:s+l,y:a},{width:1,height:u});}this.image=o,this.iconPositions=r,this.patternPositions=n;}addImages(t,e,r){for(const n in t){const i=t[n],s={x:0,y:0,w:i.data.width+2,h:i.data.height+2};r.push(s),e[n]=new mc(s,i),i.hasRenderCallback&&this.haveRenderCallbacks.push(n);}}patchUpdatedImages(t,e){t.dispatchRenderCallbacks(this.haveRenderCallbacks);for(const r in t.updatedImages)this.patchUpdatedImage(this.iconPositions[r],t.getImage(r),e),this.patchUpdatedImage(this.patternPositions[r],t.getImage(r),e);}patchUpdatedImage(t,e,r){if(!t||!e)return;if(t.version===e.version)return;t.version=e.version;const[n,i]=t.tl;r.update(e.data,void 0,{x:n,y:i});}}us(\"ImagePosition\",mc),us(\"ImageAtlas\",gc),t.an=void 0,(cc=t.an||(t.an={}))[cc.none=0]=\"none\",cc[cc.horizontal=1]=\"horizontal\",cc[cc.vertical=2]=\"vertical\",cc[cc.horizontalOnly=3]=\"horizontalOnly\";class xc{constructor(){this.scale=1,this.fontStack=\"\",this.imageName=null,this.verticalAlign=\"bottom\";}static forText(t,e,r){const n=new xc;return n.scale=t||1,n.fontStack=e,n.verticalAlign=r||\"bottom\",n}static forImage(t,e){const r=new xc;return r.imageName=t,r.verticalAlign=e||\"bottom\",r}}class vc{constructor(){this.text=\"\",this.sectionIndex=[],this.sections=[],this.imageSectionID=null;}static fromFeature(t,e){const r=new vc;for(let n=0;n<t.sections.length;n++){const i=t.sections[n];i.image?r.addImageSection(i):r.addTextSection(i,e);}return r}length(){return this.text.length}getSection(t){return this.sections[this.sectionIndex[t]]}getSectionIndex(t){return this.sectionIndex[t]}getCharCode(t){return this.text.charCodeAt(t)}verticalizePunctuation(){this.text=function(t){let e=\"\";for(let r=0;r<t.length;r++){const n=t.charCodeAt(r+1)||null,i=t.charCodeAt(r-1)||null;e+=n&&As(n)&&!rc[t[r+1]]||i&&As(i)&&!rc[t[r-1]]||!rc[t[r]]?t[r]:rc[t[r]];}return e}(this.text);}trim(){let t=0;for(let e=0;e<this.text.length&&wc[this.text.charCodeAt(e)];e++)t++;let e=this.text.length;for(let r=this.text.length-1;r>=0&&r>=t&&wc[this.text.charCodeAt(r)];r--)e--;this.text=this.text.substring(t,e),this.sectionIndex=this.sectionIndex.slice(t,e);}substring(t,e){const r=new vc;return r.text=this.text.substring(t,e),r.sectionIndex=this.sectionIndex.slice(t,e),r.sections=this.sections,r}toString(){return this.text}getMaxScale(){return this.sectionIndex.reduce(((t,e)=>Math.max(t,this.sections[e].scale)),0)}getMaxImageSize(t){let e=0,r=0;for(let n=0;n<this.length();n++){const i=this.getSection(n);if(i.imageName){const n=t[i.imageName];if(!n)continue;const s=n.displaySize;e=Math.max(e,s[0]),r=Math.max(r,s[1]);}}return {maxImageWidth:e,maxImageHeight:r}}addTextSection(t,e){this.text+=t.text,this.sections.push(xc.forText(t.scale,t.fontStack||e,t.verticalAlign));const r=this.sections.length-1;for(let e=0;e<t.text.length;++e)this.sectionIndex.push(r);}addImageSection(t){const e=t.image?t.image.name:\"\";if(0===e.length)return void Z(\"Can't add FormattedSection with an empty image.\");const r=this.getNextImageSectionCharCode();r?(this.text+=String.fromCharCode(r),this.sections.push(xc.forImage(e,t.verticalAlign)),this.sectionIndex.push(this.sections.length-1)):Z(\"Reached maximum number of images 6401\");}getNextImageSectionCharCode(){return this.imageSectionID?this.imageSectionID>=63743?null:++this.imageSectionID:(this.imageSectionID=57344,this.imageSectionID)}}function bc(e,r,n,i,s,a,o,l,u,c,h,p,f,d,y){const m=vc.fromFeature(e,s);let g;p===t.an.vertical&&m.verticalizePunctuation();const{processBidirectionalText:x,processStyledBidirectionalText:v}=Ps;if(x&&1===m.sections.length){g=[];const t=x(m.toString(),Pc(m,c,a,r,i,d));for(const e of t){const t=new vc;t.text=e,t.sections=m.sections;for(let r=0;r<e.length;r++)t.sectionIndex.push(0);g.push(t);}}else if(v){g=[];const t=v(m.text,m.sectionIndex,Pc(m,c,a,r,i,d));for(const e of t){const t=new vc;t.text=e[0],t.sectionIndex=e[1],t.sections=m.sections,g.push(t);}}else g=function(t,e){const r=[],n=t.text;let i=0;for(const n of e)r.push(t.substring(i,n)),i=n;return i<n.length&&r.push(t.substring(i,n.length)),r}(m,Pc(m,c,a,r,i,d));const b=[],w={positionedLines:b,text:m.toString(),top:h[1],bottom:h[1],left:h[0],right:h[0],writingMode:p,iconsInText:!1,verticalizable:!1};return function(t,e,r,n,i,s,a,o,l,u,c,h){let p=0,f=0,d=0,y=0;const m=\"right\"===o?1:\"left\"===o?0:.5,g=ac/h;let x=0;for(const a of i){a.trim();const i=a.getMaxScale(),o={positionedGlyphs:[],lineOffset:0};t.positionedLines[x]=o;const h=o.positionedGlyphs;let v=0;if(!a.length()){f+=s,++x;continue}const b=Bc(n,a,g);for(let s=0;s<a.length();s++){const o=a.getSection(s),d=a.getSectionIndex(s),y=a.getCharCode(s),m=Tc(l,c,y);let x;if(o.imageName){if(t.iconsInText=!0,o.scale=o.scale*g,x=Fc(o,m,i,b,n),!x)continue;v=Math.max(v,x.imageOffset);}else if(x=Vc(o,y,m,b,e,r),!x)continue;const{rect:w,metrics:_,baselineOffset:S}=x;h.push({glyph:y,imageName:o.imageName,x:p,y:f+S+-17,vertical:m,scale:o.scale,fontStack:o.fontStack,sectionIndex:d,metrics:_,rect:w}),m?(t.verticalizable=!0,p+=(o.imageName?_.advance:ac)*o.scale+u):p+=_.advance*o.scale+u;}0!==h.length&&(d=Math.max(p-u,d),Lc(h,0,h.length-1,m)),p=0,o.lineOffset=Math.max(v,(i-1)*ac);const w=s*i+v;f+=w,y=Math.max(w,y),++x;}const{horizontalAlign:v,verticalAlign:b}=Cc(a);((function(t,e,r,n,i,s,a,o,l){const u=(e-r)*i;let c=0;c=s!==a?-o*n- -17:-n*l*a+.5*a;for(const e of t)for(const t of e.positionedGlyphs)t.x+=u,t.y+=c;}))(t.positionedLines,m,v,b,d,y,s,f,i.length),t.top+=-b*f,t.bottom=t.top+f,t.left+=-v*d,t.right=t.left+d;}(w,r,n,i,g,o,l,u,p,c,f,y),!function(t){for(const e of t)if(0!==e.positionedGlyphs.length)return !1;return !0}(b)&&w}const wc={9:!0,10:!0,11:!0,12:!0,13:!0,32:!0},_c={10:!0,32:!0,38:!0,41:!0,43:!0,45:!0,47:!0,173:!0,183:!0,8203:!0,8208:!0,8211:!0,8231:!0},Sc={40:!0};function Ac(t,e,r,n,i,s){if(e.imageName){const t=n[e.imageName];return t?t.displaySize[0]*e.scale*ac/s+i:0}{const n=r[e.fontStack],s=n&&n[t];return s?s.metrics.advance*e.scale+i:0}}function kc(t,e,r,n){const i=Math.pow(t-e,2);return n?t<e?i/2:2*i:i+Math.abs(r)*r}function Mc(t,e,r){let n=0;return 10===t&&(n-=1e4),r&&(n+=150),40!==t&&65288!==t||(n+=50),41!==e&&65289!==e||(n+=50),n}function Ic(t,e,r,n,i,s){let a=null,o=kc(e,r,i,s);for(const t of n){const n=kc(e-t.x,r,i,s)+t.badness;n<=o&&(a=t,o=n);}return {index:t,x:e,priorBreak:a,badness:o}}function zc(t){return t?zc(t.priorBreak).concat(t.index):[]}function Pc(t,e,r,n,i,s){if(!t)return [];const a=[],o=function(t,e,r,n,i,s){let a=0;for(let r=0;r<t.length();r++){const o=t.getSection(r);a+=Ac(t.getCharCode(r),o,n,i,e,s);}return a/Math.max(1,Math.ceil(a/r))}(t,e,r,n,i,s),l=t.text.indexOf(\"​\")>=0;let u=0;for(let r=0;r<t.length();r++){const h=t.getSection(r),p=t.getCharCode(r);if(wc[p]||(u+=Ac(p,h,n,i,e,s)),r<t.length()-1){const e=!((c=p)<11904)&&(!!ms[\"CJK Compatibility Forms\"](c)||!!ms[\"CJK Compatibility\"](c)||!!ms[\"CJK Strokes\"](c)||!!ms[\"CJK Symbols and Punctuation\"](c)||!!ms[\"Enclosed CJK Letters and Months\"](c)||!!ms[\"Halfwidth and Fullwidth Forms\"](c)||!!ms[\"Ideographic Description Characters\"](c)||!!ms[\"Vertical Forms\"](c)||_s.test(String.fromCodePoint(c)));(_c[p]||e||h.imageName||r!==t.length()-2&&Sc[t.getCharCode(r+1)])&&a.push(Ic(r+1,u,o,a,Mc(p,t.getCharCode(r+1),e&&l),!1));}}var c;return zc(Ic(t.length(),u,o,a,0,!0))}function Cc(t){let e=.5,r=.5;switch(t){case \"right\":case \"top-right\":case \"bottom-right\":e=1;break;case \"left\":case \"top-left\":case \"bottom-left\":e=0;}switch(t){case \"bottom\":case \"bottom-right\":case \"bottom-left\":r=1;break;case \"top\":case \"top-right\":case \"top-left\":r=0;}return {horizontalAlign:e,verticalAlign:r}}function Bc(t,e,r){const n=e.getMaxScale()*ac,{maxImageWidth:i,maxImageHeight:s}=e.getMaxImageSize(t),a=Math.max(n,s*r);return {verticalLineContentWidth:Math.max(n,i*r),horizontalLineContentHeight:a}}function Ec(t){switch(t){case \"top\":return 0;case \"center\":return .5;default:return 1}}function Tc(e,r,n){return !(e===t.an.horizontal||!r&&!Ss(n)||r&&(wc[n]||(i=n,/\\p{sc=Arab}/u.test(String.fromCodePoint(i)))));var i;}function Vc(t,e,r,n,i,s){const a=s[t.fontStack],o=function(t,e,r,n){if(t&&t.rect)return t;const i=e[r.fontStack],s=i&&i[n];return s?{rect:null,metrics:s.metrics}:null}(a&&a[e],i,t,e);if(null===o)return null;let l;if(r)l=n.verticalLineContentWidth-t.scale*ac;else {const e=Ec(t.verticalAlign);l=(n.horizontalLineContentHeight-t.scale*ac)*e;}return {rect:o.rect,metrics:o.metrics,baselineOffset:l}}function Fc(t,e,r,n,i){const s=i[t.imageName];if(!s)return null;const a=s.paddedRect,o=s.displaySize,l={width:o[0],height:o[1],left:1,top:-3,advance:e?o[1]:o[0]};let u;if(e)u=n.verticalLineContentWidth-o[1]*t.scale;else {const e=Ec(t.verticalAlign);u=(n.horizontalLineContentHeight-o[1]*t.scale)*e;}return {rect:a,metrics:l,baselineOffset:u,imageOffset:(e?o[0]:o[1])*t.scale-ac*r}}function Lc(t,e,r,n){if(0===n)return;const i=t[r],s=(t[r].x+i.metrics.advance*i.scale)*n;for(let n=e;n<=r;n++)t[n].x-=s;}function $c(t,e,r){const{horizontalAlign:n,verticalAlign:i}=Cc(r),s=e[0]-t.displaySize[0]*n,a=e[1]-t.displaySize[1]*i;return {image:t,top:a,bottom:a+t.displaySize[1],left:s,right:s+t.displaySize[0]}}function Dc(t){var e,r;let n=t.left,i=t.top,s=t.right-n,a=t.bottom-i;const o=null!==(e=t.image.textFitWidth)&&void 0!==e?e:\"stretchOrShrink\",l=null!==(r=t.image.textFitHeight)&&void 0!==r?r:\"stretchOrShrink\",u=(t.image.content[2]-t.image.content[0])/(t.image.content[3]-t.image.content[1]);if(\"proportional\"===l){if(\"stretchOnly\"===o&&s/a<u||\"proportional\"===o){const t=Math.ceil(a*u);n*=t/s,s=t;}}else if(\"proportional\"===o&&\"stretchOnly\"===l&&0!==u&&s/a>u){const t=Math.ceil(s/u);i*=t/a,a=t;}return {x1:n,y1:i,x2:n+s,y2:i+a}}function Oc(t,e,r,n,i,s){const a=t.image;let o;if(a.content){const t=a.content,e=a.pixelRatio||1;o=[t[0]/e,t[1]/e,a.displaySize[0]-t[2]/e,a.displaySize[1]-t[3]/e];}const l=e.left*s,u=e.right*s;let c,h,p,f;\"width\"===r||\"both\"===r?(f=i[0]+l-n[3],h=i[0]+u+n[1]):(f=i[0]+(l+u-a.displaySize[0])/2,h=f+a.displaySize[0]);const d=e.top*s,y=e.bottom*s;return \"height\"===r||\"both\"===r?(c=i[1]+d-n[0],p=i[1]+y+n[2]):(c=i[1]+(d+y-a.displaySize[1])/2,p=c+a.displaySize[1]),{image:a,top:c,right:h,bottom:p,left:f,collisionPadding:o}}const Rc=128,jc=32640;function Nc(t,e){const{expression:r}=e;if(\"constant\"===r.kind)return {kind:\"constant\",layoutSize:r.evaluate(new Cs(t+1))};if(\"source\"===r.kind)return {kind:\"source\"};{const{zoomStops:e,interpolationType:n}=r;let i=0;for(;i<e.length&&e[i]<=t;)i++;i=Math.max(0,i-1);let s=i;for(;s<e.length&&e[s]<t+1;)s++;s=Math.min(e.length-1,s);const a=e[i],o=e[s];return \"composite\"===r.kind?{kind:\"composite\",minZoom:a,maxZoom:o,interpolationType:n}:{kind:\"camera\",minZoom:a,maxZoom:o,minSize:r.evaluate(new Cs(a)),maxSize:r.evaluate(new Cs(o)),interpolationType:n}}}function Uc(t,e,r){let n=\"never\";const i=t.get(e);return i?n=i:t.get(r)&&(n=\"always\"),n}const qc=_u.VectorTileFeature.types,Gc=[{name:\"a_fade_opacity\",components:1,type:\"Uint8\",offset:0}];function Xc(t,e,r,n,i,s,a,o,l,u,c,h,p){const f=o?Math.min(jc,Math.round(o[0])):0,d=o?Math.min(jc,Math.round(o[1])):0;t.emplaceBack(e,r,Math.round(32*n),Math.round(32*i),s,a,(f<<1)+(l?1:0),d,16*u,16*c,256*h,256*p);}function Zc(t,e,r){t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r);}function Yc(t){for(const e of t.sections)if(zs(e.text))return !0;return !1}class Hc{constructor(t){this.layoutVertexArray=new Ra,this.indexArray=new Ga,this.programConfigurations=t,this.segments=new Ha,this.dynamicLayoutVertexArray=new ja,this.opacityVertexArray=new Na,this.hasVisibleVertices=!1,this.placedSymbolArray=new Aa;}isEmpty(){return 0===this.layoutVertexArray.length&&0===this.indexArray.length&&0===this.dynamicLayoutVertexArray.length&&0===this.opacityVertexArray.length}upload(t,e,r,n){this.isEmpty()||(r&&(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,Ku.members),this.indexBuffer=t.createIndexBuffer(this.indexArray,e),this.dynamicLayoutVertexBuffer=t.createVertexBuffer(this.dynamicLayoutVertexArray,Ju.members,!0),this.opacityVertexBuffer=t.createVertexBuffer(this.opacityVertexArray,Gc,!0),this.opacityVertexBuffer.itemSize=1),(r||n)&&this.programConfigurations.upload(t));}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.dynamicLayoutVertexBuffer.destroy(),this.opacityVertexBuffer.destroy());}}us(\"SymbolBuffers\",Hc);class Kc{constructor(t,e,r){this.layoutVertexArray=new t,this.layoutAttributes=e,this.indexArray=new r,this.segments=new Ha,this.collisionVertexArray=new qa;}upload(t){this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,this.layoutAttributes),this.indexBuffer=t.createIndexBuffer(this.indexArray),this.collisionVertexBuffer=t.createVertexBuffer(this.collisionVertexArray,Wu.members,!0);}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.segments.destroy(),this.collisionVertexBuffer.destroy());}}us(\"CollisionBuffers\",Kc);class Jc{constructor(e){this.collisionBoxArray=e.collisionBoxArray,this.zoom=e.zoom,this.globalState=e.globalState,this.overscaling=e.overscaling,this.layers=e.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=e.index,this.pixelRatio=e.pixelRatio,this.sourceLayerIndex=e.sourceLayerIndex,this.hasPattern=!1,this.hasRTLText=!1,this.sortKeyRanges=[],this.collisionCircleArray=[];const r=this.layers[0]._unevaluatedLayout._values;this.textSizeData=Nc(this.zoom,r[\"text-size\"]),this.iconSizeData=Nc(this.zoom,r[\"icon-size\"]);const n=this.layers[0].layout,i=n.get(\"symbol-sort-key\"),s=n.get(\"symbol-z-order\");this.canOverlap=\"never\"!==Uc(n,\"text-overlap\",\"text-allow-overlap\")||\"never\"!==Uc(n,\"icon-overlap\",\"icon-allow-overlap\")||n.get(\"text-ignore-placement\")||n.get(\"icon-ignore-placement\"),this.sortFeaturesByKey=\"viewport-y\"!==s&&!i.isConstant(),this.sortFeaturesByY=(\"viewport-y\"===s||\"auto\"===s&&!this.sortFeaturesByKey)&&this.canOverlap,\"point\"===n.get(\"symbol-placement\")&&(this.writingModes=n.get(\"text-writing-mode\").map((e=>t.an[e]))),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id)),this.sourceID=e.sourceID;}createArrays(){this.text=new Hc(new So(this.layers,this.zoom,(t=>/^text/.test(t)))),this.icon=new Hc(new So(this.layers,this.zoom,(t=>/^icon/.test(t)))),this.glyphOffsetArray=new Ia,this.lineVertexArray=new za,this.symbolInstances=new Ma,this.textAnchorOffsets=new Ca;}calculateGlyphDependencies(t,e,r,n,i){for(let s=0;s<t.length;s++)if(e[t.charCodeAt(s)]=!0,(r||n)&&i){const r=rc[t.charAt(s)];r&&(e[r.charCodeAt(0)]=!0);}}populate(e,r,n){const i=this.layers[0],s=i.layout,a=s.get(\"text-font\"),o=s.get(\"text-field\"),l=s.get(\"icon-image\"),u=(\"constant\"!==o.value.kind||o.value.value instanceof Te&&!o.value.value.isEmpty()||o.value.value.toString().length>0)&&(\"constant\"!==a.value.kind||a.value.value.length>0),c=\"constant\"!==l.value.kind||!!l.value.value||Object.keys(l.parameters).length>0,h=s.get(\"symbol-sort-key\");if(this.features=[],!u&&!c)return;const p=r.iconDependencies,f=r.glyphDependencies,d=r.availableImages,y=new Cs(this.zoom,{globalState:this.globalState});for(const{feature:r,id:o,index:l,sourceLayerIndex:m}of e){const e=i._featureFilter.needGeometry,g=Po(r,e);if(!i._featureFilter.filter(y,g,n))continue;let x,v;if(e||(g.geometry=zo(r)),u){const t=i.getValueAndResolveTokens(\"text-field\",g,n,d),e=Te.factory(t),r=this.hasRTLText=this.hasRTLText||Yc(e);(!r||\"unavailable\"===Ps.getRTLTextPluginStatus()||r&&Ps.isParsed())&&(x=ec(e,i,g));}if(c){const t=i.getValueAndResolveTokens(\"icon-image\",g,n,d);v=t instanceof Re?t:Re.fromString(t);}if(!x&&!v)continue;const b=this.sortFeaturesByKey?h.evaluate(g,{},n):void 0;if(this.features.push({id:o,text:x,icon:v,index:l,sourceLayerIndex:m,geometry:g.geometry,properties:r.properties,type:qc[r.type],sortKey:b}),v&&(p[v.name]=!0),x){const e=a.evaluate(g,{},n).join(\",\"),r=\"viewport\"!==s.get(\"text-rotation-alignment\")&&\"point\"!==s.get(\"symbol-placement\");this.allowVerticalPlacement=this.writingModes&&this.writingModes.indexOf(t.an.vertical)>=0;for(const t of x.sections)if(t.image)p[t.image.name]=!0;else {const n=gs(x.toString()),i=t.fontStack||e,s=f[i]=f[i]||{};this.calculateGlyphDependencies(t.text,s,r,this.allowVerticalPlacement,n);}}}\"line\"===s.get(\"symbol-placement\")&&(this.features=function(t){const e={},r={},n=[];let i=0;function s(e){n.push(t[e]),i++;}function a(t,e,i){const s=r[t];return delete r[t],r[e]=s,n[s].geometry[0].pop(),n[s].geometry[0]=n[s].geometry[0].concat(i[0]),s}function o(t,r,i){const s=e[r];return delete e[r],e[t]=s,n[s].geometry[0].shift(),n[s].geometry[0]=i[0].concat(n[s].geometry[0]),s}function l(t,e,r){const n=r?e[0][e[0].length-1]:e[0][0];return `${t}:${n.x}:${n.y}`}for(let u=0;u<t.length;u++){const c=t[u],h=c.geometry,p=c.text?c.text.toString():null;if(!p){s(u);continue}const f=l(p,h),d=l(p,h,!0);if(f in r&&d in e&&r[f]!==e[d]){const t=o(f,d,h),i=a(f,d,n[t].geometry);delete e[f],delete r[d],r[l(p,n[i].geometry,!0)]=i,n[t].geometry=null;}else f in r?a(f,d,h):d in e?o(f,d,h):(s(u),e[f]=i-1,r[d]=i-1);}return n.filter((t=>t.geometry))}(this.features)),this.sortFeaturesByKey&&this.features.sort(((t,e)=>t.sortKey-e.sortKey));}update(t,e,r){this.stateDependentLayers.length&&(this.text.programConfigurations.updatePaintArrays(t,e,this.layers,r),this.icon.programConfigurations.updatePaintArrays(t,e,this.layers,r));}isEmpty(){return 0===this.symbolInstances.length&&!this.hasRTLText}uploadPending(){return !this.uploaded||this.text.programConfigurations.needsUpload||this.icon.programConfigurations.needsUpload}upload(t){!this.uploaded&&this.hasDebugData()&&(this.textCollisionBox.upload(t),this.iconCollisionBox.upload(t)),this.text.upload(t,this.sortFeaturesByY,!this.uploaded,this.text.programConfigurations.needsUpload),this.icon.upload(t,this.sortFeaturesByY,!this.uploaded,this.icon.programConfigurations.needsUpload),this.uploaded=!0;}destroyDebugData(){this.textCollisionBox.destroy(),this.iconCollisionBox.destroy();}destroy(){this.text.destroy(),this.icon.destroy(),this.hasDebugData()&&this.destroyDebugData();}addToLineVertexArray(t,e){const r=this.lineVertexArray.length;if(void 0!==t.segment){let r=t.dist(e[t.segment+1]),n=t.dist(e[t.segment]);const i={};for(let n=t.segment+1;n<e.length;n++)i[n]={x:e[n].x,y:e[n].y,tileUnitDistanceFromAnchor:r},n<e.length-1&&(r+=e[n+1].dist(e[n]));for(let r=t.segment||0;r>=0;r--)i[r]={x:e[r].x,y:e[r].y,tileUnitDistanceFromAnchor:n},r>0&&(n+=e[r-1].dist(e[r]));for(let t=0;t<e.length;t++){const e=i[t];this.lineVertexArray.emplaceBack(e.x,e.y,e.tileUnitDistanceFromAnchor);}}return {lineStartIndex:r,lineLength:this.lineVertexArray.length-r}}addSymbols(e,r,n,i,s,a,o,l,u,c,h,p){const f=e.indexArray,d=e.layoutVertexArray,y=e.segments.prepareSegment(4*r.length,d,f,this.canOverlap?a.sortKey:void 0),m=this.glyphOffsetArray.length,g=y.vertexLength,x=this.allowVerticalPlacement&&o===t.an.vertical?Math.PI/2:0,v=a.text&&a.text.sections;for(let t=0;t<r.length;t++){const{tl:i,tr:s,bl:o,br:u,tex:c,pixelOffsetTL:h,pixelOffsetBR:m,minFontScaleX:g,minFontScaleY:b,glyphOffset:w,isSDF:_,sectionIndex:S}=r[t],A=y.vertexLength,k=w[1];Xc(d,l.x,l.y,i.x,k+i.y,c.x,c.y,n,_,h.x,h.y,g,b),Xc(d,l.x,l.y,s.x,k+s.y,c.x+c.w,c.y,n,_,m.x,h.y,g,b),Xc(d,l.x,l.y,o.x,k+o.y,c.x,c.y+c.h,n,_,h.x,m.y,g,b),Xc(d,l.x,l.y,u.x,k+u.y,c.x+c.w,c.y+c.h,n,_,m.x,m.y,g,b),Zc(e.dynamicLayoutVertexArray,l,x),f.emplaceBack(A,A+2,A+1),f.emplaceBack(A+1,A+2,A+3),y.vertexLength+=4,y.primitiveLength+=2,this.glyphOffsetArray.emplaceBack(w[0]),t!==r.length-1&&S===r[t+1].sectionIndex||e.programConfigurations.populatePaintArrays(d.length,a,a.index,{},p,v&&v[S]);}e.placedSymbolArray.emplaceBack(l.x,l.y,m,this.glyphOffsetArray.length-m,g,u,c,l.segment,n?n[0]:0,n?n[1]:0,i[0],i[1],o,0,!1,0,h);}_addCollisionDebugVertex(t,e,r,n,i,s){return e.emplaceBack(0,0),t.emplaceBack(r.x,r.y,n,i,Math.round(s.x),Math.round(s.y))}addCollisionDebugVertices(t,e,r,n,i,s,a){const o=i.segments.prepareSegment(4,i.layoutVertexArray,i.indexArray),u=o.vertexLength,c=i.layoutVertexArray,h=i.collisionVertexArray,p=a.anchorX,f=a.anchorY;this._addCollisionDebugVertex(c,h,s,p,f,new l(t,e)),this._addCollisionDebugVertex(c,h,s,p,f,new l(r,e)),this._addCollisionDebugVertex(c,h,s,p,f,new l(r,n)),this._addCollisionDebugVertex(c,h,s,p,f,new l(t,n)),o.vertexLength+=4;const d=i.indexArray;d.emplaceBack(u,u+1),d.emplaceBack(u+1,u+2),d.emplaceBack(u+2,u+3),d.emplaceBack(u+3,u),o.primitiveLength+=4;}addDebugCollisionBoxes(t,e,r,n){for(let i=t;i<e;i++){const t=this.collisionBoxArray.get(i);this.addCollisionDebugVertices(t.x1,t.y1,t.x2,t.y2,n?this.textCollisionBox:this.iconCollisionBox,t.anchorPoint,r);}}generateCollisionDebugBuffers(){this.hasDebugData()&&this.destroyDebugData(),this.textCollisionBox=new Kc(Ua,Qu.members,Xa),this.iconCollisionBox=new Kc(Ua,Qu.members,Xa);for(let t=0;t<this.symbolInstances.length;t++){const e=this.symbolInstances.get(t);this.addDebugCollisionBoxes(e.textBoxStartIndex,e.textBoxEndIndex,e,!0),this.addDebugCollisionBoxes(e.verticalTextBoxStartIndex,e.verticalTextBoxEndIndex,e,!0),this.addDebugCollisionBoxes(e.iconBoxStartIndex,e.iconBoxEndIndex,e,!1),this.addDebugCollisionBoxes(e.verticalIconBoxStartIndex,e.verticalIconBoxEndIndex,e,!1);}}_deserializeCollisionBoxesForSymbol(t,e,r,n,i,s,a,o,l){const u={};for(let n=e;n<r;n++){const e=t.get(n);u.textBox={x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2,anchorPointX:e.anchorPointX,anchorPointY:e.anchorPointY},u.textFeatureIndex=e.featureIndex;break}for(let e=n;e<i;e++){const r=t.get(e);u.verticalTextBox={x1:r.x1,y1:r.y1,x2:r.x2,y2:r.y2,anchorPointX:r.anchorPointX,anchorPointY:r.anchorPointY},u.verticalTextFeatureIndex=r.featureIndex;break}for(let e=s;e<a;e++){const r=t.get(e);u.iconBox={x1:r.x1,y1:r.y1,x2:r.x2,y2:r.y2,anchorPointX:r.anchorPointX,anchorPointY:r.anchorPointY},u.iconFeatureIndex=r.featureIndex;break}for(let e=o;e<l;e++){const r=t.get(e);u.verticalIconBox={x1:r.x1,y1:r.y1,x2:r.x2,y2:r.y2,anchorPointX:r.anchorPointX,anchorPointY:r.anchorPointY},u.verticalIconFeatureIndex=r.featureIndex;break}return u}deserializeCollisionBoxes(t){this.collisionArrays=[];for(let e=0;e<this.symbolInstances.length;e++){const r=this.symbolInstances.get(e);this.collisionArrays.push(this._deserializeCollisionBoxesForSymbol(t,r.textBoxStartIndex,r.textBoxEndIndex,r.verticalTextBoxStartIndex,r.verticalTextBoxEndIndex,r.iconBoxStartIndex,r.iconBoxEndIndex,r.verticalIconBoxStartIndex,r.verticalIconBoxEndIndex));}}hasTextData(){return this.text.segments.get().length>0}hasIconData(){return this.icon.segments.get().length>0}hasDebugData(){return this.textCollisionBox&&this.iconCollisionBox}hasTextCollisionBoxData(){return this.hasDebugData()&&this.textCollisionBox.segments.get().length>0}hasIconCollisionBoxData(){return this.hasDebugData()&&this.iconCollisionBox.segments.get().length>0}addIndicesForPlacedSymbol(t,e){const r=t.placedSymbolArray.get(e),n=r.vertexStartIndex+4*r.numGlyphs;for(let e=r.vertexStartIndex;e<n;e+=4)t.indexArray.emplaceBack(e,e+2,e+1),t.indexArray.emplaceBack(e+1,e+2,e+3);}getSortedSymbolIndexes(t){if(this.sortedAngle===t&&void 0!==this.symbolInstanceIndexes)return this.symbolInstanceIndexes;const e=Math.sin(t),r=Math.cos(t),n=[],i=[],s=[];for(let t=0;t<this.symbolInstances.length;++t){s.push(t);const a=this.symbolInstances.get(t);n.push(0|Math.round(e*a.anchorX+r*a.anchorY)),i.push(a.featureIndex);}return s.sort(((t,e)=>n[t]-n[e]||i[e]-i[t])),s}addToSortKeyRanges(t,e){const r=this.sortKeyRanges[this.sortKeyRanges.length-1];r&&r.sortKey===e?r.symbolInstanceEnd=t+1:this.sortKeyRanges.push({sortKey:e,symbolInstanceStart:t,symbolInstanceEnd:t+1});}sortFeatures(t){if(this.sortFeaturesByY&&this.sortedAngle!==t&&!(this.text.segments.get().length>1||this.icon.segments.get().length>1)){this.symbolInstanceIndexes=this.getSortedSymbolIndexes(t),this.sortedAngle=t,this.text.indexArray.clear(),this.icon.indexArray.clear(),this.featureSortOrder=[];for(const t of this.symbolInstanceIndexes){const e=this.symbolInstances.get(t);this.featureSortOrder.push(e.featureIndex),[e.rightJustifiedTextSymbolIndex,e.centerJustifiedTextSymbolIndex,e.leftJustifiedTextSymbolIndex].forEach(((t,e,r)=>{t>=0&&r.indexOf(t)===e&&this.addIndicesForPlacedSymbol(this.text,t);})),e.verticalPlacedTextSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.text,e.verticalPlacedTextSymbolIndex),e.placedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.placedIconSymbolIndex),e.verticalPlacedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.verticalPlacedIconSymbolIndex);}this.text.indexBuffer&&this.text.indexBuffer.updateData(this.text.indexArray),this.icon.indexBuffer&&this.icon.indexBuffer.updateData(this.icon.indexArray);}}}let Wc,Qc;us(\"SymbolBucket\",Jc,{omit:[\"layers\",\"collisionBoxArray\",\"features\",\"compareText\"]}),Jc.MAX_GLYPHS=65535,Jc.addDynamicAttributes=Zc;var th={get paint(){return Qc=Qc||new qs({\"icon-opacity\":new Rs(wt.paint_symbol[\"icon-opacity\"]),\"icon-color\":new Rs(wt.paint_symbol[\"icon-color\"]),\"icon-halo-color\":new Rs(wt.paint_symbol[\"icon-halo-color\"]),\"icon-halo-width\":new Rs(wt.paint_symbol[\"icon-halo-width\"]),\"icon-halo-blur\":new Rs(wt.paint_symbol[\"icon-halo-blur\"]),\"icon-translate\":new Os(wt.paint_symbol[\"icon-translate\"]),\"icon-translate-anchor\":new Os(wt.paint_symbol[\"icon-translate-anchor\"]),\"text-opacity\":new Rs(wt.paint_symbol[\"text-opacity\"]),\"text-color\":new Rs(wt.paint_symbol[\"text-color\"],{runtimeType:jt,getOverride:t=>t.textColor,hasOverride:t=>!!t.textColor}),\"text-halo-color\":new Rs(wt.paint_symbol[\"text-halo-color\"]),\"text-halo-width\":new Rs(wt.paint_symbol[\"text-halo-width\"]),\"text-halo-blur\":new Rs(wt.paint_symbol[\"text-halo-blur\"]),\"text-translate\":new Os(wt.paint_symbol[\"text-translate\"]),\"text-translate-anchor\":new Os(wt.paint_symbol[\"text-translate-anchor\"])})},get layout(){return Wc=Wc||new qs({\"symbol-placement\":new Os(wt.layout_symbol[\"symbol-placement\"]),\"symbol-spacing\":new Os(wt.layout_symbol[\"symbol-spacing\"]),\"symbol-avoid-edges\":new Os(wt.layout_symbol[\"symbol-avoid-edges\"]),\"symbol-sort-key\":new Rs(wt.layout_symbol[\"symbol-sort-key\"]),\"symbol-z-order\":new Os(wt.layout_symbol[\"symbol-z-order\"]),\"icon-allow-overlap\":new Os(wt.layout_symbol[\"icon-allow-overlap\"]),\"icon-overlap\":new Os(wt.layout_symbol[\"icon-overlap\"]),\"icon-ignore-placement\":new Os(wt.layout_symbol[\"icon-ignore-placement\"]),\"icon-optional\":new Os(wt.layout_symbol[\"icon-optional\"]),\"icon-rotation-alignment\":new Os(wt.layout_symbol[\"icon-rotation-alignment\"]),\"icon-size\":new Rs(wt.layout_symbol[\"icon-size\"]),\"icon-text-fit\":new Os(wt.layout_symbol[\"icon-text-fit\"]),\"icon-text-fit-padding\":new Os(wt.layout_symbol[\"icon-text-fit-padding\"]),\"icon-image\":new Rs(wt.layout_symbol[\"icon-image\"]),\"icon-rotate\":new Rs(wt.layout_symbol[\"icon-rotate\"]),\"icon-padding\":new Rs(wt.layout_symbol[\"icon-padding\"]),\"icon-keep-upright\":new Os(wt.layout_symbol[\"icon-keep-upright\"]),\"icon-offset\":new Rs(wt.layout_symbol[\"icon-offset\"]),\"icon-anchor\":new Rs(wt.layout_symbol[\"icon-anchor\"]),\"icon-pitch-alignment\":new Os(wt.layout_symbol[\"icon-pitch-alignment\"]),\"text-pitch-alignment\":new Os(wt.layout_symbol[\"text-pitch-alignment\"]),\"text-rotation-alignment\":new Os(wt.layout_symbol[\"text-rotation-alignment\"]),\"text-field\":new Rs(wt.layout_symbol[\"text-field\"]),\"text-font\":new Rs(wt.layout_symbol[\"text-font\"]),\"text-size\":new Rs(wt.layout_symbol[\"text-size\"]),\"text-max-width\":new Rs(wt.layout_symbol[\"text-max-width\"]),\"text-line-height\":new Os(wt.layout_symbol[\"text-line-height\"]),\"text-letter-spacing\":new Rs(wt.layout_symbol[\"text-letter-spacing\"]),\"text-justify\":new Rs(wt.layout_symbol[\"text-justify\"]),\"text-radial-offset\":new Rs(wt.layout_symbol[\"text-radial-offset\"]),\"text-variable-anchor\":new Os(wt.layout_symbol[\"text-variable-anchor\"]),\"text-variable-anchor-offset\":new Rs(wt.layout_symbol[\"text-variable-anchor-offset\"]),\"text-anchor\":new Rs(wt.layout_symbol[\"text-anchor\"]),\"text-max-angle\":new Os(wt.layout_symbol[\"text-max-angle\"]),\"text-writing-mode\":new Os(wt.layout_symbol[\"text-writing-mode\"]),\"text-rotate\":new Rs(wt.layout_symbol[\"text-rotate\"]),\"text-padding\":new Os(wt.layout_symbol[\"text-padding\"]),\"text-keep-upright\":new Os(wt.layout_symbol[\"text-keep-upright\"]),\"text-transform\":new Rs(wt.layout_symbol[\"text-transform\"]),\"text-offset\":new Rs(wt.layout_symbol[\"text-offset\"]),\"text-allow-overlap\":new Os(wt.layout_symbol[\"text-allow-overlap\"]),\"text-overlap\":new Os(wt.layout_symbol[\"text-overlap\"]),\"text-ignore-placement\":new Os(wt.layout_symbol[\"text-ignore-placement\"]),\"text-optional\":new Os(wt.layout_symbol[\"text-optional\"])})}};class eh{constructor(t){if(void 0===t.property.overrides)throw new Error(\"overrides must be provided to instantiate FormatSectionOverride class\");this.type=t.property.overrides?t.property.overrides.runtimeType:$t,this.defaultValue=t;}evaluate(t){if(t.formattedSection){const e=this.defaultValue.property.overrides;if(e&&e.hasOverride(t.formattedSection))return e.getOverride(t.formattedSection)}return t.feature&&t.featureState?this.defaultValue.evaluate(t.feature,t.featureState):this.defaultValue.property.specification.default}eachChild(t){this.defaultValue.isConstant()||t(this.defaultValue.value._styleExpression.expression);}outputDefined(){return !1}serialize(){return null}}us(\"FormatSectionOverride\",eh,{omit:[\"defaultValue\"]});class rh extends Xs{constructor(t){super(t,th);}recalculate(t,e){if(super.recalculate(t,e),\"auto\"===this.layout.get(\"icon-rotation-alignment\")&&(this.layout._values[\"icon-rotation-alignment\"]=\"point\"!==this.layout.get(\"symbol-placement\")?\"map\":\"viewport\"),\"auto\"===this.layout.get(\"text-rotation-alignment\")&&(this.layout._values[\"text-rotation-alignment\"]=\"point\"!==this.layout.get(\"symbol-placement\")?\"map\":\"viewport\"),\"auto\"===this.layout.get(\"text-pitch-alignment\")&&(this.layout._values[\"text-pitch-alignment\"]=\"map\"===this.layout.get(\"text-rotation-alignment\")?\"map\":\"viewport\"),\"auto\"===this.layout.get(\"icon-pitch-alignment\")&&(this.layout._values[\"icon-pitch-alignment\"]=this.layout.get(\"icon-rotation-alignment\")),\"point\"===this.layout.get(\"symbol-placement\")){const t=this.layout.get(\"text-writing-mode\");if(t){const e=[];for(const r of t)e.indexOf(r)<0&&e.push(r);this.layout._values[\"text-writing-mode\"]=e;}else this.layout._values[\"text-writing-mode\"]=[\"horizontal\"];}this._setPaintOverrides();}getValueAndResolveTokens(t,e,r,n){const i=this.layout.get(t).evaluate(e,{},r,n),s=this._unevaluatedLayout._values[t];return s.isDataDriven()||ii(s.value)||!i?i:function(t,e){return e.replace(/{([^{}]+)}/g,((e,r)=>t&&r in t?String(t[r]):\"\"))}(e.properties,i)}createBucket(t){return new Jc(t)}queryRadius(){return 0}queryIntersectsFeature(){throw new Error(\"Should take a different path in FeatureIndex\")}_setPaintOverrides(){for(const t of th.paint.overridableProperties){if(!rh.hasPaintOverride(this.layout,t))continue;const e=this.paint.get(t),r=new eh(e),n=new ni(r,e.property.specification);let i=null;i=\"constant\"===e.value.kind||\"source\"===e.value.kind?new ai(\"source\",n):new oi(\"composite\",n,e.value.zoomStops),this.paint._values[t]=new $s(e.property,i,e.parameters);}}_handleOverridablePaintPropertyUpdate(t,e,r){return !(!this.layout||e.isDataDriven()||r.isDataDriven())&&rh.hasPaintOverride(this.layout,t)}static hasPaintOverride(t,e){const r=t.get(\"text-field\"),n=th.paint.properties[e];let i=!1;const s=t=>{for(const e of t)if(n.overrides&&n.overrides.hasOverride(e))return void(i=!0)};if(\"constant\"===r.value.kind&&r.value.value instanceof Te)s(r.value.value.sections);else if(\"source\"===r.value.kind){const t=e=>{i||(e instanceof Xe&&qe(e.value)===Xt?s(e.value.sections):e instanceof Cr?s(e.sections):e.eachChild(t));},e=r.value;e._styleExpression&&t(e._styleExpression.expression);}return i}}let nh;var ih={get paint(){return nh=nh||new qs({\"background-color\":new Os(wt.paint_background[\"background-color\"]),\"background-pattern\":new Ns(wt.paint_background[\"background-pattern\"]),\"background-opacity\":new Os(wt.paint_background[\"background-opacity\"])})}};class sh extends Xs{constructor(t){super(t,ih);}}let ah;var oh={get paint(){return ah=ah||new qs({\"raster-opacity\":new Os(wt.paint_raster[\"raster-opacity\"]),\"raster-hue-rotate\":new Os(wt.paint_raster[\"raster-hue-rotate\"]),\"raster-brightness-min\":new Os(wt.paint_raster[\"raster-brightness-min\"]),\"raster-brightness-max\":new Os(wt.paint_raster[\"raster-brightness-max\"]),\"raster-saturation\":new Os(wt.paint_raster[\"raster-saturation\"]),\"raster-contrast\":new Os(wt.paint_raster[\"raster-contrast\"]),\"raster-resampling\":new Os(wt.paint_raster[\"raster-resampling\"]),\"raster-fade-duration\":new Os(wt.paint_raster[\"raster-fade-duration\"])})}};class lh extends Xs{constructor(t){super(t,oh);}}class uh extends Xs{constructor(t){super(t,{}),this.onAdd=t=>{this.implementation.onAdd&&this.implementation.onAdd(t,t.painter.context.gl);},this.onRemove=t=>{this.implementation.onRemove&&this.implementation.onRemove(t,t.painter.context.gl);},this.implementation=t;}is3D(){return \"3d\"===this.implementation.renderingMode}hasOffscreenPass(){return void 0!==this.implementation.prerender}recalculate(){}updateTransitions(){}hasTransition(){return !1}serialize(){throw new Error(\"Custom layers cannot be serialized\")}}class ch{constructor(t){this._methodToThrottle=t,this._triggered=!1,\"undefined\"!=typeof MessageChannel&&(this._channel=new MessageChannel,this._channel.port2.onmessage=()=>{this._triggered=!1,this._methodToThrottle();});}trigger(){this._triggered||(this._triggered=!0,this._channel?this._channel.port1.postMessage(!0):setTimeout((()=>{this._triggered=!1,this._methodToThrottle();}),0));}remove(){delete this._channel,this._methodToThrottle=()=>{};}}const hh={once:!0},ph=6371008.8;class fh{constructor(t,e){if(isNaN(t)||isNaN(e))throw new Error(`Invalid LngLat object: (${t}, ${e})`);if(this.lng=+t,this.lat=+e,this.lat>90||this.lat<-90)throw new Error(\"Invalid LngLat latitude value: must be between -90 and 90\")}wrap(){return new fh(R(this.lng,-180,180),this.lat)}toArray(){return [this.lng,this.lat]}toString(){return `LngLat(${this.lng}, ${this.lat})`}distanceTo(t){const e=Math.PI/180,r=this.lat*e,n=t.lat*e,i=Math.sin(r)*Math.sin(n)+Math.cos(r)*Math.cos(n)*Math.cos((t.lng-this.lng)*e);return ph*Math.acos(Math.min(i,1))}static convert(t){if(t instanceof fh)return t;if(Array.isArray(t)&&(2===t.length||3===t.length))return new fh(Number(t[0]),Number(t[1]));if(!Array.isArray(t)&&\"object\"==typeof t&&null!==t)return new fh(Number(\"lng\"in t?t.lng:t.lon),Number(t.lat));throw new Error(\"`LngLatLike` argument must be specified as a LngLat instance, an object {lng: <lng>, lat: <lat>}, an object {lon: <lng>, lat: <lat>}, or an array of [<lng>, <lat>]\")}}const dh=2*Math.PI*ph;function yh(t){return dh*Math.cos(t*Math.PI/180)}function mh(t){return (180+t)/360}function gh(t){return (180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t*Math.PI/360)))/360}function xh(t,e){return t/yh(e)}function vh(t){return 360/Math.PI*Math.atan(Math.exp((180-360*t)*Math.PI/180))-90}function bh(t,e){return t*yh(vh(e))}class wh{constructor(t,e,r=0){this.x=+t,this.y=+e,this.z=+r;}static fromLngLat(t,e=0){const r=fh.convert(t);return new wh(mh(r.lng),gh(r.lat),xh(e,r.lat))}toLngLat(){return new fh(360*this.x-180,vh(this.y))}toAltitude(){return bh(this.z,this.y)}meterInMercatorCoordinateUnits(){return 1/dh*(t=vh(this.y),1/Math.cos(t*Math.PI/180));var t;}}function _h(t,e,r){var n=2*Math.PI*6378137/256/Math.pow(2,r);return [t*n-2*Math.PI*6378137/2,e*n-2*Math.PI*6378137/2]}class Sh{constructor(t,e,r){if(!function(t,e,r){return !(t<0||t>25||r<0||r>=Math.pow(2,t)||e<0||e>=Math.pow(2,t))}(t,e,r))throw new Error(`x=${e}, y=${r}, z=${t} outside of bounds. 0<=x<${Math.pow(2,t)}, 0<=y<${Math.pow(2,t)} 0<=z<=25 `);this.z=t,this.x=e,this.y=r,this.key=Mh(0,t,t,e,r);}equals(t){return this.z===t.z&&this.x===t.x&&this.y===t.y}url(t,e,r){const n=(s=this.y,a=this.z,o=_h(256*(i=this.x),256*(s=Math.pow(2,a)-s-1),a),l=_h(256*(i+1),256*(s+1),a),o[0]+\",\"+o[1]+\",\"+l[0]+\",\"+l[1]);var i,s,a,o,l;const u=function(t,e,r){let n,i=\"\";for(let s=t;s>0;s--)n=1<<s-1,i+=(e&n?1:0)+(r&n?2:0);return i}(this.z,this.x,this.y);return t[(this.x+this.y)%t.length].replace(/{prefix}/g,(this.x%16).toString(16)+(this.y%16).toString(16)).replace(/{z}/g,String(this.z)).replace(/{x}/g,String(this.x)).replace(/{y}/g,String(\"tms\"===r?Math.pow(2,this.z)-this.y-1:this.y)).replace(/{ratio}/g,e>1?\"@2x\":\"\").replace(/{quadkey}/g,u).replace(/{bbox-epsg-3857}/g,n)}isChildOf(t){const e=this.z-t.z;return e>0&&t.x===this.x>>e&&t.y===this.y>>e}getTilePoint(t){const e=Math.pow(2,this.z);return new l((t.x*e-this.x)*E,(t.y*e-this.y)*E)}toString(){return `${this.z}/${this.x}/${this.y}`}}class Ah{constructor(t,e){this.wrap=t,this.canonical=e,this.key=Mh(t,e.z,e.z,e.x,e.y);}}class kh{constructor(t,e,r,n,i){if(this.terrainRttPosMatrix32f=null,t<r)throw new Error(`overscaledZ should be >= z; overscaledZ = ${t}; z = ${r}`);this.overscaledZ=t,this.wrap=e,this.canonical=new Sh(r,+n,+i),this.key=Mh(e,t,r,n,i);}clone(){return new kh(this.overscaledZ,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)}equals(t){return this.overscaledZ===t.overscaledZ&&this.wrap===t.wrap&&this.canonical.equals(t.canonical)}scaledTo(t){if(t>this.overscaledZ)throw new Error(`targetZ > this.overscaledZ; targetZ = ${t}; overscaledZ = ${this.overscaledZ}`);const e=this.canonical.z-t;return t>this.canonical.z?new kh(t,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y):new kh(t,this.wrap,t,this.canonical.x>>e,this.canonical.y>>e)}calculateScaledKey(t,e){if(t>this.overscaledZ)throw new Error(`targetZ > this.overscaledZ; targetZ = ${t}; overscaledZ = ${this.overscaledZ}`);const r=this.canonical.z-t;return t>this.canonical.z?Mh(this.wrap*+e,t,this.canonical.z,this.canonical.x,this.canonical.y):Mh(this.wrap*+e,t,t,this.canonical.x>>r,this.canonical.y>>r)}isChildOf(t){if(t.wrap!==this.wrap)return !1;const e=this.canonical.z-t.canonical.z;return 0===t.overscaledZ||t.overscaledZ<this.overscaledZ&&t.canonical.x===this.canonical.x>>e&&t.canonical.y===this.canonical.y>>e}children(t){if(this.overscaledZ>=t)return [new kh(this.overscaledZ+1,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)];const e=this.canonical.z+1,r=2*this.canonical.x,n=2*this.canonical.y;return [new kh(e,this.wrap,e,r,n),new kh(e,this.wrap,e,r+1,n),new kh(e,this.wrap,e,r,n+1),new kh(e,this.wrap,e,r+1,n+1)]}isLessThan(t){return this.wrap<t.wrap||!(this.wrap>t.wrap)&&(this.overscaledZ<t.overscaledZ||!(this.overscaledZ>t.overscaledZ)&&(this.canonical.x<t.canonical.x||!(this.canonical.x>t.canonical.x)&&this.canonical.y<t.canonical.y))}wrapped(){return new kh(this.overscaledZ,0,this.canonical.z,this.canonical.x,this.canonical.y)}unwrapTo(t){return new kh(this.overscaledZ,t,this.canonical.z,this.canonical.x,this.canonical.y)}overscaleFactor(){return Math.pow(2,this.overscaledZ-this.canonical.z)}toUnwrapped(){return new Ah(this.wrap,this.canonical)}toString(){return `${this.overscaledZ}/${this.canonical.x}/${this.canonical.y}`}getTilePoint(t){return this.canonical.getTilePoint(new wh(t.x-this.wrap,t.y))}}function Mh(t,e,r,n,i){(t*=2)<0&&(t=-1*t-1);const s=1<<r;return (s*s*t+s*i+n).toString(36)+r.toString(36)+e.toString(36)}us(\"CanonicalTileID\",Sh),us(\"OverscaledTileID\",kh,{omit:[\"terrainRttPosMatrix32f\"]});class Ih{constructor(){this.minX=1/0,this.maxX=-1/0,this.minY=1/0,this.maxY=-1/0;}extend(t){return this.minX=Math.min(this.minX,t.x),this.minY=Math.min(this.minY,t.y),this.maxX=Math.max(this.maxX,t.x),this.maxY=Math.max(this.maxY,t.y),this}expandBy(t){return this.minX-=t,this.minY-=t,this.maxX+=t,this.maxY+=t,(this.minX>this.maxX||this.minY>this.maxY)&&(this.minX=1/0,this.maxX=-1/0,this.minY=1/0,this.maxY=-1/0),this}shrinkBy(t){return this.expandBy(-t)}map(t){const e=new Ih;return e.extend(t(new l(this.minX,this.minY))),e.extend(t(new l(this.maxX,this.minY))),e.extend(t(new l(this.minX,this.maxY))),e.extend(t(new l(this.maxX,this.maxY))),e}static fromPoints(t){const e=new Ih;for(const r of t)e.extend(r);return e}contains(t){return t.x>=this.minX&&t.x<=this.maxX&&t.y>=this.minY&&t.y<=this.maxY}empty(){return this.minX>this.maxX}width(){return this.maxX-this.minX}height(){return this.maxY-this.minY}covers(t){return !this.empty()&&!t.empty()&&t.minX>=this.minX&&t.maxX<=this.maxX&&t.minY>=this.minY&&t.maxY<=this.maxY}intersects(t){return !this.empty()&&!t.empty()&&t.minX<=this.maxX&&t.maxX>=this.minX&&t.minY<=this.maxY&&t.maxY>=this.minY}}class zh{constructor(t){this._stringToNumber={},this._numberToString=[];for(let e=0;e<t.length;e++){const r=t[e];this._stringToNumber[r]=e,this._numberToString[e]=r;}}encode(t){return this._stringToNumber[t]}decode(t){if(t>=this._numberToString.length)throw new Error(`Out of bounds. Index requested n=${t} can't be >= this._numberToString.length ${this._numberToString.length}`);return this._numberToString[t]}}class Ph{constructor(t,e,r,n,i){this.type=\"Feature\",this._vectorTileFeature=t,t._z=e,t._x=r,t._y=n,this.properties=t.properties,this.id=i;}get geometry(){return void 0===this._geometry&&(this._geometry=this._vectorTileFeature.toGeoJSON(this._vectorTileFeature._x,this._vectorTileFeature._y,this._vectorTileFeature._z).geometry),this._geometry}set geometry(t){this._geometry=t;}toJSON(){const t={geometry:this.geometry};for(const e in this)\"_geometry\"!==e&&\"_vectorTileFeature\"!==e&&(t[e]=this[e]);return t}}class Ch{constructor(t,e){this.tileID=t,this.x=t.canonical.x,this.y=t.canonical.y,this.z=t.canonical.z,this.grid=new os(E,16,0),this.grid3D=new os(E,16,0),this.featureIndexArray=new Ea,this.promoteId=e;}insert(t,e,r,n,i,s){const a=this.featureIndexArray.length;this.featureIndexArray.emplaceBack(r,n,i);const o=s?this.grid3D:this.grid;for(let t=0;t<e.length;t++){const r=e[t],n=[1/0,1/0,-1/0,-1/0];for(let t=0;t<r.length;t++){const e=r[t];n[0]=Math.min(n[0],e.x),n[1]=Math.min(n[1],e.y),n[2]=Math.max(n[2],e.x),n[3]=Math.max(n[3],e.y);}n[0]<E&&n[1]<E&&n[2]>=0&&n[3]>=0&&o.insert(a,n[0],n[1],n[2],n[3]);}}loadVTLayers(){return this.vtLayers||(this.vtLayers=new _u.VectorTile(new hc(this.rawTileData)).layers,this.sourceLayerCoder=new zh(this.vtLayers?Object.keys(this.vtLayers).sort():[\"_geojsonTileLayer\"])),this.vtLayers}query(t,e,r,n){this.loadVTLayers();const i=t.params,s=E/t.tileSize/t.scale,a=di(i.filter),o=t.queryGeometry,u=t.queryPadding*s,c=Ih.fromPoints(o),h=this.grid.query(c.minX-u,c.minY-u,c.maxX+u,c.maxY+u),p=Ih.fromPoints(t.cameraQueryGeometry).expandBy(u),f=this.grid3D.query(p.minX,p.minY,p.maxX,p.maxY,((e,r,n,i)=>function(t,e,r,n,i){for(const s of t)if(e<=s.x&&r<=s.y&&n>=s.x&&i>=s.y)return !0;const s=[new l(e,r),new l(e,i),new l(n,i),new l(n,r)];if(t.length>2)for(const e of s)if(No(t,e))return !0;for(let e=0;e<t.length-1;e++)if(Uo(t[e],t[e+1],s))return !0;return !1}(t.cameraQueryGeometry,e-u,r-u,n+u,i+u)));for(const t of f)h.push(t);h.sort(Eh);const d={};let y;for(let l=0;l<h.length;l++){const u=h[l];if(u===y)continue;y=u;const c=this.featureIndexArray.get(u);let p=null;this.loadMatchingFeature(d,c.bucketIndex,c.sourceLayerIndex,c.featureIndex,a,i.layers,i.availableImages,e,r,n,((e,r,n)=>(p||(p=zo(e)),r.queryIntersectsFeature({queryGeometry:o,feature:e,featureState:n,geometry:p,zoom:this.z,transform:t.transform,pixelsToTileUnits:s,pixelPosMatrix:t.pixelPosMatrix,unwrappedTileID:this.tileID.toUnwrapped(),getElevation:t.getElevation}))));}return d}loadMatchingFeature(t,e,r,n,i,s,a,o,l,u,c){const h=this.bucketLayerIDs[e];if(s&&!h.some((t=>s.has(t))))return;const p=this.sourceLayerCoder.decode(r),f=this.vtLayers[p].feature(n);if(i.needGeometry){const t=Po(f,!0);if(!i.filter(new Cs(this.tileID.overscaledZ),t,this.tileID.canonical))return}else if(!i.filter(new Cs(this.tileID.overscaledZ),f))return;const d=this.getId(f,p);for(let e=0;e<h.length;e++){const r=h[e];if(s&&!s.has(r))continue;const i=o[r];if(!i)continue;let p={};d&&u&&(p=u.getState(i.sourceLayer||\"_geojsonTileLayer\",d));const y=j({},l[r]);y.paint=Bh(y.paint,i.paint,f,p,a),y.layout=Bh(y.layout,i.layout,f,p,a);const m=!c||c(f,i,p);if(!m)continue;const g=new Ph(f,this.z,this.x,this.y,d);g.layer=y;let x=t[r];void 0===x&&(x=t[r]=[]),x.push({featureIndex:n,feature:g,intersectionZ:m});}}lookupSymbolFeatures(t,e,r,n,i,s,a,o){const l={};this.loadVTLayers();const u=di(i);for(const i of t)this.loadMatchingFeature(l,r,n,i,u,s,a,o,e);return l}hasLayer(t){for(const e of this.bucketLayerIDs)for(const r of e)if(t===r)return !0;return !1}getId(t,e){var r;let n=t.id;return this.promoteId&&(n=t.properties[\"string\"==typeof this.promoteId?this.promoteId:this.promoteId[e]],\"boolean\"==typeof n&&(n=Number(n)),void 0===n&&(null===(r=t.properties)||void 0===r?void 0:r.cluster)&&this.promoteId&&(n=Number(t.properties.cluster_id))),n}}function Bh(t,e,r,n,i){return U(t,((t,s)=>{const a=e instanceof Ds?e.get(s):null;return a&&a.evaluate?a.evaluate(r,n,i):a}))}function Eh(t,e){return e-t}function Th(t,e,r,n,i){const s=[];for(let a=0;a<t.length;a++){const o=t[a];let u;for(let t=0;t<o.length-1;t++){let a=o[t],c=o[t+1];a.x<e&&c.x<e||(a.x<e?a=new l(e,a.y+(e-a.x)/(c.x-a.x)*(c.y-a.y))._round():c.x<e&&(c=new l(e,a.y+(e-a.x)/(c.x-a.x)*(c.y-a.y))._round()),a.y<r&&c.y<r||(a.y<r?a=new l(a.x+(r-a.y)/(c.y-a.y)*(c.x-a.x),r)._round():c.y<r&&(c=new l(a.x+(r-a.y)/(c.y-a.y)*(c.x-a.x),r)._round()),a.x>=n&&c.x>=n||(a.x>=n?a=new l(n,a.y+(n-a.x)/(c.x-a.x)*(c.y-a.y))._round():c.x>=n&&(c=new l(n,a.y+(n-a.x)/(c.x-a.x)*(c.y-a.y))._round()),a.y>=i&&c.y>=i||(a.y>=i?a=new l(a.x+(i-a.y)/(c.y-a.y)*(c.x-a.x),i)._round():c.y>=i&&(c=new l(a.x+(i-a.y)/(c.y-a.y)*(c.x-a.x),i)._round()),u&&a.equals(u[u.length-1])||(u=[a],s.push(u)),u.push(c)))));}}return s}us(\"FeatureIndex\",Ch,{omit:[\"rawTileData\",\"sourceLayerCoder\"]});class Vh extends l{constructor(t,e,r,n){super(t,e),this.angle=r,void 0!==n&&(this.segment=n);}clone(){return new Vh(this.x,this.y,this.angle,this.segment)}}function Fh(t,e,r,n,i){if(void 0===e.segment||0===r)return !0;let s=e,a=e.segment+1,o=0;for(;o>-r/2;){if(a--,a<0)return !1;o-=t[a].dist(s),s=t[a];}o+=t[a].dist(t[a+1]),a++;const l=[];let u=0;for(;o<r/2;){const e=t[a],r=t[a+1];if(!r)return !1;let s=t[a-1].angleTo(e)-e.angleTo(r);for(s=Math.abs((s+3*Math.PI)%(2*Math.PI)-Math.PI),l.push({distance:o,angleDelta:s}),u+=s;o-l[0].distance>n;)u-=l.shift().angleDelta;if(u>i)return !1;a++,o+=e.dist(r);}return !0}function Lh(t){let e=0;for(let r=0;r<t.length-1;r++)e+=t[r].dist(t[r+1]);return e}function $h(t,e,r){return t?.6*e*r:0}function Dh(t,e){return Math.max(t?t.right-t.left:0,e?e.right-e.left:0)}function Oh(t,e,r,n,i,s){const a=$h(r,i,s),o=Dh(r,n)*s;let l=0;const u=Lh(t)/2;for(let r=0;r<t.length-1;r++){const n=t[r],i=t[r+1],s=n.dist(i);if(l+s>u){const c=(u-l)/s,h=gr.number(n.x,i.x,c),p=gr.number(n.y,i.y,c),f=new Vh(h,p,i.angleTo(n),r);return f._round(),!a||Fh(t,f,o,a,e)?f:void 0}l+=s;}}function Rh(t,e,r,n,i,s,a,o,l){const u=$h(n,s,a),c=Dh(n,i),h=c*a,p=0===t[0].x||t[0].x===l||0===t[0].y||t[0].y===l;return e-h<e/4&&(e=h+e/4),jh(t,p?e/2*o%e:(c/2+2*s)*a*o%e,e,u,r,h,p,!1,l)}function jh(t,e,r,n,i,s,a,o,l){const u=s/2,c=Lh(t);let h=0,p=e-r,f=[];for(let e=0;e<t.length-1;e++){const a=t[e],o=t[e+1],d=a.dist(o),y=o.angleTo(a);for(;p+r<h+d;){p+=r;const m=(p-h)/d,g=gr.number(a.x,o.x,m),x=gr.number(a.y,o.y,m);if(g>=0&&g<l&&x>=0&&x<l&&p-u>=0&&p+u<=c){const r=new Vh(g,x,y,e);r._round(),n&&!Fh(t,r,s,n,i)||f.push(r);}}h+=d;}return o||f.length||a||(f=jh(t,h/2,r,n,i,s,a,!0,l)),f}function Nh(t,e,r,n){const i=[],s=t.image,a=s.pixelRatio,o=s.paddedRect.w-2,u=s.paddedRect.h-2;let c={x1:t.left,y1:t.top,x2:t.right,y2:t.bottom};const h=s.stretchX||[[0,o]],p=s.stretchY||[[0,u]],f=(t,e)=>t+e[1]-e[0],d=h.reduce(f,0),y=p.reduce(f,0),m=o-d,g=u-y;let x=0,v=d,b=0,w=y,_=0,S=m,A=0,k=g;if(s.content&&n){const e=s.content,r=e[2]-e[0],n=e[3]-e[1];(s.textFitWidth||s.textFitHeight)&&(c=Dc(t)),x=Uh(h,0,e[0]),b=Uh(p,0,e[1]),v=Uh(h,e[0],e[2]),w=Uh(p,e[1],e[3]),_=e[0]-x,A=e[1]-b,S=r-v,k=n-w;}const M=c.x1,I=c.y1,z=c.x2-M,P=c.y2-I,C=(t,n,i,o)=>{const u=Gh(t.stretch-x,v,z,M),c=Xh(t.fixed-_,S,t.stretch,d),h=Gh(n.stretch-b,w,P,I),p=Xh(n.fixed-A,k,n.stretch,y),f=Gh(i.stretch-x,v,z,M),m=Xh(i.fixed-_,S,i.stretch,d),g=Gh(o.stretch-b,w,P,I),C=Xh(o.fixed-A,k,o.stretch,y),B=new l(u,h),E=new l(f,h),T=new l(f,g),V=new l(u,g),F=new l(c/a,p/a),L=new l(m/a,C/a),$=e*Math.PI/180;if($){const t=Math.sin($),e=Math.cos($),r=[e,-t,t,e];B._matMult(r),E._matMult(r),V._matMult(r),T._matMult(r);}const D=t.stretch+t.fixed,O=n.stretch+n.fixed;return {tl:B,tr:E,bl:V,br:T,tex:{x:s.paddedRect.x+1+D,y:s.paddedRect.y+1+O,w:i.stretch+i.fixed-D,h:o.stretch+o.fixed-O},writingMode:void 0,glyphOffset:[0,0],sectionIndex:0,pixelOffsetTL:F,pixelOffsetBR:L,minFontScaleX:S/a/z,minFontScaleY:k/a/P,isSDF:r}};if(n&&(s.stretchX||s.stretchY)){const t=qh(h,m,d),e=qh(p,g,y);for(let r=0;r<t.length-1;r++){const n=t[r],s=t[r+1];for(let t=0;t<e.length-1;t++)i.push(C(n,e[t],s,e[t+1]));}}else i.push(C({fixed:0,stretch:-1},{fixed:0,stretch:-1},{fixed:0,stretch:o+1},{fixed:0,stretch:u+1}));return i}function Uh(t,e,r){let n=0;for(const i of t)n+=Math.max(e,Math.min(r,i[1]))-Math.max(e,Math.min(r,i[0]));return n}function qh(t,e,r){const n=[{fixed:-1,stretch:0}];for(const[e,r]of t){const t=n[n.length-1];n.push({fixed:e-t.stretch,stretch:t.stretch}),n.push({fixed:e-t.stretch,stretch:t.stretch+(r-e)});}return n.push({fixed:e+1,stretch:r}),n}function Gh(t,e,r,n){return t/e*r+n}function Xh(t,e,r,n){return t-e*r/n}us(\"Anchor\",Vh);class Zh{constructor(t,e,r,n,i,s,a,o,u,c){var h;if(this.boxStartIndex=t.length,u){let t=s.top,e=s.bottom;const r=s.collisionPadding;r&&(t-=r[1],e+=r[3]);let n=e-t;n>0&&(n=Math.max(10,n),this.circleDiameter=n);}else {const u=(null===(h=s.image)||void 0===h?void 0:h.content)&&(s.image.textFitWidth||s.image.textFitHeight)?Dc(s):{x1:s.left,y1:s.top,x2:s.right,y2:s.bottom};u.y1=u.y1*a-o[0],u.y2=u.y2*a+o[2],u.x1=u.x1*a-o[3],u.x2=u.x2*a+o[1];const p=s.collisionPadding;if(p&&(u.x1-=p[0]*a,u.y1-=p[1]*a,u.x2+=p[2]*a,u.y2+=p[3]*a),c){const t=new l(u.x1,u.y1),e=new l(u.x2,u.y1),r=new l(u.x1,u.y2),n=new l(u.x2,u.y2),i=c*Math.PI/180;t._rotate(i),e._rotate(i),r._rotate(i),n._rotate(i),u.x1=Math.min(t.x,e.x,r.x,n.x),u.x2=Math.max(t.x,e.x,r.x,n.x),u.y1=Math.min(t.y,e.y,r.y,n.y),u.y2=Math.max(t.y,e.y,r.y,n.y);}t.emplaceBack(e.x,e.y,u.x1,u.y1,u.x2,u.y2,r,n,i);}this.boxEndIndex=t.length;}}class Yh{constructor(t=[],e=(t,e)=>t<e?-1:t>e?1:0){if(this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t);}push(t){this.data.push(t),this._up(this.length++);}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return --this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:r}=this,n=e[t];for(;t>0;){const i=t-1>>1,s=e[i];if(r(n,s)>=0)break;e[t]=s,t=i;}e[t]=n;}_down(t){const{data:e,compare:r}=this,n=this.length>>1,i=e[t];for(;t<n;){let n=1+(t<<1);const s=n+1;if(s<this.length&&r(e[s],e[n])<0&&(n=s),r(e[n],i)>=0)break;e[t]=e[n],t=n;}e[t]=i;}}function Hh(t,e=1,r=!1){const n=Ih.fromPoints(t[0]),i=Math.min(n.width(),n.height());let s=i/2;const a=new Yh([],Kh),{minX:o,minY:u,maxX:c,maxY:h}=n;if(0===i)return new l(o,u);for(let e=o;e<c;e+=i)for(let r=u;r<h;r+=i)a.push(new Jh(e+s,r+s,s,t));let p=function(t){let e=0,r=0,n=0;const i=t[0];for(let t=0,s=i.length,a=s-1;t<s;a=t++){const s=i[t],o=i[a],l=s.x*o.y-o.x*s.y;r+=(s.x+o.x)*l,n+=(s.y+o.y)*l,e+=3*l;}return new Jh(r/e,n/e,0,t)}(t),f=a.length;for(;a.length;){const n=a.pop();(n.d>p.d||!p.d)&&(p=n,r&&console.log(\"found best %d after %d probes\",Math.round(1e4*n.d)/1e4,f)),n.max-p.d<=e||(s=n.h/2,a.push(new Jh(n.p.x-s,n.p.y-s,s,t)),a.push(new Jh(n.p.x+s,n.p.y-s,s,t)),a.push(new Jh(n.p.x-s,n.p.y+s,s,t)),a.push(new Jh(n.p.x+s,n.p.y+s,s,t)),f+=4);}return r&&(console.log(`num probes: ${f}`),console.log(`best distance: ${p.d}`)),p.p}function Kh(t,e){return e.max-t.max}function Jh(t,e,r,n){this.p=new l(t,e),this.h=r,this.d=function(t,e){let r=!1,n=1/0;for(let i=0;i<e.length;i++){const s=e[i];for(let e=0,i=s.length,a=i-1;e<i;a=e++){const i=s[e],o=s[a];i.y>t.y!=o.y>t.y&&t.x<(o.x-i.x)*(t.y-i.y)/(o.y-i.y)+i.x&&(r=!r),n=Math.min(n,Ro(t,i,o));}}return (r?1:-1)*Math.sqrt(n)}(this.p,n),this.max=this.d+this.h*Math.SQRT2;}var Wh;t.aD=void 0,(Wh=t.aD||(t.aD={}))[Wh.center=1]=\"center\",Wh[Wh.left=2]=\"left\",Wh[Wh.right=3]=\"right\",Wh[Wh.top=4]=\"top\",Wh[Wh.bottom=5]=\"bottom\",Wh[Wh[\"top-left\"]=6]=\"top-left\",Wh[Wh[\"top-right\"]=7]=\"top-right\",Wh[Wh[\"bottom-left\"]=8]=\"bottom-left\",Wh[Wh[\"bottom-right\"]=9]=\"bottom-right\";const Qh=Number.POSITIVE_INFINITY;function tp(t,e){return e[1]!==Qh?function(t,e,r){let n=0,i=0;switch(e=Math.abs(e),r=Math.abs(r),t){case \"top-right\":case \"top-left\":case \"top\":i=r-7;break;case \"bottom-right\":case \"bottom-left\":case \"bottom\":i=7-r;}switch(t){case \"top-right\":case \"bottom-right\":case \"right\":n=-e;break;case \"top-left\":case \"bottom-left\":case \"left\":n=e;}return [n,i]}(t,e[0],e[1]):function(t,e){let r=0,n=0;e<0&&(e=0);const i=e/Math.SQRT2;switch(t){case \"top-right\":case \"top-left\":n=i-7;break;case \"bottom-right\":case \"bottom-left\":n=7-i;break;case \"bottom\":n=7-e;break;case \"top\":n=e-7;}switch(t){case \"top-right\":case \"bottom-right\":r=-i;break;case \"top-left\":case \"bottom-left\":r=i;break;case \"left\":r=e;break;case \"right\":r=-e;}return [r,n]}(t,e[0])}function ep(t,e,r){var n;const i=t.layout,s=null===(n=i.get(\"text-variable-anchor-offset\"))||void 0===n?void 0:n.evaluate(e,{},r);if(s){const t=s.values,e=[];for(let r=0;r<t.length;r+=2){const n=e[r]=t[r],i=t[r+1].map((t=>t*ac));n.startsWith(\"top\")?i[1]-=7:n.startsWith(\"bottom\")&&(i[1]+=7),e[r+1]=i;}return new Oe(e)}const a=i.get(\"text-variable-anchor\");if(a){let n;n=void 0!==t._unevaluatedLayout.getValue(\"text-radial-offset\")?[i.get(\"text-radial-offset\").evaluate(e,{},r)*ac,Qh]:i.get(\"text-offset\").evaluate(e,{},r).map((t=>t*ac));const s=[];for(const t of a)s.push(t,tp(t,n));return new Oe(s)}return null}function rp(t){switch(t){case \"right\":case \"top-right\":case \"bottom-right\":return \"right\";case \"left\":case \"top-left\":case \"bottom-left\":return \"left\"}return \"center\"}function np(e,r,n,i,s,a,o,l,u,c,h,p){let f=a.textMaxSize.evaluate(r,{});void 0===f&&(f=o);const d=e.layers[0].layout,y=d.get(\"icon-offset\").evaluate(r,{},h),m=sp(n.horizontal),g=o/24,x=e.tilePixelRatio*g,v=e.tilePixelRatio*f/24,b=e.tilePixelRatio*l,w=e.tilePixelRatio*d.get(\"symbol-spacing\"),_=d.get(\"text-padding\")*e.tilePixelRatio,S=function(t,e,r,n=1){const i=t.get(\"icon-padding\").evaluate(e,{},r),s=i&&i.values;return [s[0]*n,s[1]*n,s[2]*n,s[3]*n]}(d,r,h,e.tilePixelRatio),A=d.get(\"text-max-angle\")/180*Math.PI,k=\"viewport\"!==d.get(\"text-rotation-alignment\")&&\"point\"!==d.get(\"symbol-placement\"),M=\"map\"===d.get(\"icon-rotation-alignment\")&&\"point\"!==d.get(\"symbol-placement\"),I=d.get(\"symbol-placement\"),z=w/2,P=d.get(\"icon-text-fit\");let C;i&&\"none\"!==P&&(e.allowVerticalPlacement&&n.vertical&&(C=Oc(i,n.vertical,P,d.get(\"icon-text-fit-padding\"),y,g)),m&&(i=Oc(i,m,P,d.get(\"icon-text-fit-padding\"),y,g)));const B=h?p.line.getGranularityForZoomLevel(h.z):1,T=(l,p)=>{p.x<0||p.x>=E||p.y<0||p.y>=E||function(e,r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,k){const M=e.addToLineVertexArray(r,n);let I,z,P,C,B=0,E=0,T=0,V=0,F=-1,L=-1;const $={};let D=so(\"\");if(e.allowVerticalPlacement&&i.vertical){const t=l.layout.get(\"text-rotate\").evaluate(w,{},A)+90;P=new Zh(u,r,c,h,p,i.vertical,f,d,y,t),o&&(C=new Zh(u,r,c,h,p,o,g,x,y,t));}if(s){const n=l.layout.get(\"icon-rotate\").evaluate(w,{}),i=\"none\"!==l.layout.get(\"icon-text-fit\"),a=Nh(s,n,S,i),f=o?Nh(o,n,S,i):void 0;z=new Zh(u,r,c,h,p,s,g,x,!1,n),B=4*a.length;const d=e.iconSizeData;let y=null;\"source\"===d.kind?(y=[Rc*l.layout.get(\"icon-size\").evaluate(w,{})],y[0]>jc&&Z(`${e.layerIds[0]}: Value for \"icon-size\" is >= 255. Reduce your \"icon-size\".`)):\"composite\"===d.kind&&(y=[Rc*_.compositeIconSizes[0].evaluate(w,{},A),Rc*_.compositeIconSizes[1].evaluate(w,{},A)],(y[0]>jc||y[1]>jc)&&Z(`${e.layerIds[0]}: Value for \"icon-size\" is >= 255. Reduce your \"icon-size\".`)),e.addSymbols(e.icon,a,y,b,v,w,t.an.none,r,M.lineStartIndex,M.lineLength,-1,A),F=e.icon.placedSymbolArray.length-1,f&&(E=4*f.length,e.addSymbols(e.icon,f,y,b,v,w,t.an.vertical,r,M.lineStartIndex,M.lineLength,-1,A),L=e.icon.placedSymbolArray.length-1);}const O=Object.keys(i.horizontal);for(const n of O){const s=i.horizontal[n];if(!I){D=so(s.text);const t=l.layout.get(\"text-rotate\").evaluate(w,{},A);I=new Zh(u,r,c,h,p,s,f,d,y,t);}const o=1===s.positionedLines.length;if(T+=ip(e,r,s,a,l,y,w,m,M,i.vertical?t.an.horizontal:t.an.horizontalOnly,o?O:[n],$,F,_,A),o)break}i.vertical&&(V+=ip(e,r,i.vertical,a,l,y,w,m,M,t.an.vertical,[\"vertical\"],$,L,_,A));const R=I?I.boxStartIndex:e.collisionBoxArray.length,j=I?I.boxEndIndex:e.collisionBoxArray.length,N=P?P.boxStartIndex:e.collisionBoxArray.length,U=P?P.boxEndIndex:e.collisionBoxArray.length,q=z?z.boxStartIndex:e.collisionBoxArray.length,G=z?z.boxEndIndex:e.collisionBoxArray.length,X=C?C.boxStartIndex:e.collisionBoxArray.length,Y=C?C.boxEndIndex:e.collisionBoxArray.length;let H=-1;const K=(t,e)=>t&&t.circleDiameter?Math.max(t.circleDiameter,e):e;H=K(I,H),H=K(P,H),H=K(z,H),H=K(C,H);const J=H>-1?1:0;J&&(H*=k/ac),e.glyphOffsetArray.length>=Jc.MAX_GLYPHS&&Z(\"Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907\"),void 0!==w.sortKey&&e.addToSortKeyRanges(e.symbolInstances.length,w.sortKey);const W=ep(l,w,A),[Q,tt]=function(e,r){const n=e.length,i=null==r?void 0:r.values;if((null==i?void 0:i.length)>0)for(let r=0;r<i.length;r+=2){const n=i[r+1];e.emplaceBack(t.aD[i[r]],n[0],n[1]);}return [n,e.length]}(e.textAnchorOffsets,W);e.symbolInstances.emplaceBack(r.x,r.y,$.right>=0?$.right:-1,$.center>=0?$.center:-1,$.left>=0?$.left:-1,$.vertical||-1,F,L,D,R,j,N,U,q,G,X,Y,c,T,V,B,E,J,0,f,H,Q,tt);}(e,p,l,n,i,s,C,e.layers[0],e.collisionBoxArray,r.index,r.sourceLayerIndex,e.index,x,[_,_,_,_],k,u,b,S,M,y,r,a,c,h,o);};if(\"line\"===I)for(const t of Th(r.geometry,0,0,E,E)){const r=Ql(t,B),s=Rh(r,w,A,n.vertical||m,i,24,v,e.overscaling,E);for(const t of s)m&&ap(e,m.text,z,t)||T(r,t);}else if(\"line-center\"===I){for(const t of r.geometry)if(t.length>1){const e=Ql(t,B),r=Oh(e,A,n.vertical||m,i,24,v);r&&T(e,r);}}else if(\"Polygon\"===r.type)for(const t of rn(r.geometry,0)){const e=Hh(t,16);T(Ql(t[0],B,!0),new Vh(e.x,e.y,0));}else if(\"LineString\"===r.type)for(const t of r.geometry){const e=Ql(t,B);T(e,new Vh(e[0].x,e[0].y,0));}else if(\"Point\"===r.type)for(const t of r.geometry)for(const e of t)T([e],new Vh(e.x,e.y,0));}function ip(t,e,r,n,i,s,a,o,u,c,h,p,f,d,y){const m=function(t,e,r,n,i,s,a,o){const u=n.layout.get(\"text-rotate\").evaluate(s,{})*Math.PI/180,c=[];for(const t of e.positionedLines)for(const n of t.positionedGlyphs){if(!n.rect)continue;const s=n.rect||{};let h=4,p=!0,f=1,d=0;const y=(i||o)&&n.vertical,m=n.metrics.advance*n.scale/2;if(o&&e.verticalizable&&(d=t.lineOffset/2-(n.imageName?-(ac-n.metrics.width*n.scale)/2:(n.scale-1)*ac)),n.imageName){const t=a[n.imageName];p=t.sdf,f=t.pixelRatio,h=1/f;}const g=i?[n.x+m,n.y]:[0,0];let x=i?[0,0]:[n.x+m+r[0],n.y+r[1]-d],v=[0,0];y&&(v=x,x=[0,0]);const b=n.metrics.isDoubleResolution?2:1,w=(n.metrics.left-h)*n.scale-m+x[0],_=(-n.metrics.top-h)*n.scale+x[1],S=w+s.w/b*n.scale/f,A=_+s.h/b*n.scale/f,k=new l(w,_),M=new l(S,_),I=new l(w,A),z=new l(S,A);if(y){const t=new l(-m,m- -17),e=-Math.PI/2,r=12-m,i=new l(22-r,-(n.imageName?r:0)),s=new l(...v);k._rotateAround(e,t)._add(i)._add(s),M._rotateAround(e,t)._add(i)._add(s),I._rotateAround(e,t)._add(i)._add(s),z._rotateAround(e,t)._add(i)._add(s);}if(u){const t=Math.sin(u),e=Math.cos(u),r=[e,-t,t,e];k._matMult(r),M._matMult(r),I._matMult(r),z._matMult(r);}const P=new l(0,0),C=new l(0,0);c.push({tl:k,tr:M,bl:I,br:z,tex:s,writingMode:e.writingMode,glyphOffset:g,sectionIndex:n.sectionIndex,isSDF:p,pixelOffsetTL:P,pixelOffsetBR:C,minFontScaleX:0,minFontScaleY:0});}return c}(0,r,o,i,s,a,n,t.allowVerticalPlacement),g=t.textSizeData;let x=null;\"source\"===g.kind?(x=[Rc*i.layout.get(\"text-size\").evaluate(a,{})],x[0]>jc&&Z(`${t.layerIds[0]}: Value for \"text-size\" is >= 255. Reduce your \"text-size\".`)):\"composite\"===g.kind&&(x=[Rc*d.compositeTextSizes[0].evaluate(a,{},y),Rc*d.compositeTextSizes[1].evaluate(a,{},y)],(x[0]>jc||x[1]>jc)&&Z(`${t.layerIds[0]}: Value for \"text-size\" is >= 255. Reduce your \"text-size\".`)),t.addSymbols(t.text,m,x,o,s,a,c,e,u.lineStartIndex,u.lineLength,f,y);for(const e of h)p[e]=t.text.placedSymbolArray.length-1;return 4*m.length}function sp(t){for(const e in t)return t[e];return null}function ap(t,e,r,n){const i=t.compareText;if(e in i){const t=i[e];for(let e=t.length-1;e>=0;e--)if(n.dist(t[e])<r)return !0}else i[e]=[];return i[e].push(n),!1}const op=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class lp{static from(t){if(!(t instanceof ArrayBuffer))throw new Error(\"Data must be an instance of ArrayBuffer.\");const[e,r]=new Uint8Array(t,0,2);if(219!==e)throw new Error(\"Data does not appear to be in a KDBush format.\");const n=r>>4;if(1!==n)throw new Error(`Got v${n} data when expected v1.`);const i=op[15&r];if(!i)throw new Error(\"Unrecognized array type.\");const[s]=new Uint16Array(t,2,1),[a]=new Uint32Array(t,4,1);return new lp(a,s,i,t)}constructor(t,e=64,r=Float64Array,n){if(isNaN(t)||t<0)throw new Error(`Unpexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+e,2),65535),this.ArrayType=r,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const i=op.indexOf(this.ArrayType),s=2*t*this.ArrayType.BYTES_PER_ELEMENT,a=t*this.IndexArrayType.BYTES_PER_ELEMENT,o=(8-a%8)%8;if(i<0)throw new Error(`Unexpected typed array class: ${r}.`);n&&n instanceof ArrayBuffer?(this.data=n,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+a+o,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+s+a+o),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+a+o,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+i]),new Uint16Array(this.data,2,1)[0]=e,new Uint32Array(this.data,4,1)[0]=t);}add(t,e){const r=this._pos>>1;return this.ids[r]=r,this.coords[this._pos++]=t,this.coords[this._pos++]=e,r}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return up(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}range(t,e,r,n){if(!this._finished)throw new Error(\"Data not yet indexed - call index.finish().\");const{ids:i,coords:s,nodeSize:a}=this,o=[0,i.length-1,0],l=[];for(;o.length;){const u=o.pop()||0,c=o.pop()||0,h=o.pop()||0;if(c-h<=a){for(let a=h;a<=c;a++){const o=s[2*a],u=s[2*a+1];o>=t&&o<=r&&u>=e&&u<=n&&l.push(i[a]);}continue}const p=h+c>>1,f=s[2*p],d=s[2*p+1];f>=t&&f<=r&&d>=e&&d<=n&&l.push(i[p]),(0===u?t<=f:e<=d)&&(o.push(h),o.push(p-1),o.push(1-u)),(0===u?r>=f:n>=d)&&(o.push(p+1),o.push(c),o.push(1-u));}return l}within(t,e,r){if(!this._finished)throw new Error(\"Data not yet indexed - call index.finish().\");const{ids:n,coords:i,nodeSize:s}=this,a=[0,n.length-1,0],o=[],l=r*r;for(;a.length;){const u=a.pop()||0,c=a.pop()||0,h=a.pop()||0;if(c-h<=s){for(let r=h;r<=c;r++)fp(i[2*r],i[2*r+1],t,e)<=l&&o.push(n[r]);continue}const p=h+c>>1,f=i[2*p],d=i[2*p+1];fp(f,d,t,e)<=l&&o.push(n[p]),(0===u?t-r<=f:e-r<=d)&&(a.push(h),a.push(p-1),a.push(1-u)),(0===u?t+r>=f:e+r>=d)&&(a.push(p+1),a.push(c),a.push(1-u));}return o}}function up(t,e,r,n,i,s){if(i-n<=r)return;const a=n+i>>1;cp(t,e,a,n,i,s),up(t,e,r,n,a-1,1-s),up(t,e,r,a+1,i,1-s);}function cp(t,e,r,n,i,s){for(;i>n;){if(i-n>600){const a=i-n+1,o=r-n+1,l=Math.log(a),u=.5*Math.exp(2*l/3),c=.5*Math.sqrt(l*u*(a-u)/a)*(o-a/2<0?-1:1);cp(t,e,r,Math.max(n,Math.floor(r-o*u/a+c)),Math.min(i,Math.floor(r+(a-o)*u/a+c)),s);}const a=e[2*r+s];let o=n,l=i;for(hp(t,e,n,r),e[2*i+s]>a&&hp(t,e,n,i);o<l;){for(hp(t,e,o,l),o++,l--;e[2*o+s]<a;)o++;for(;e[2*l+s]>a;)l--;}e[2*n+s]===a?hp(t,e,n,l):(l++,hp(t,e,l,i)),l<=r&&(n=l+1),r<=l&&(i=l-1);}}function hp(t,e,r,n){pp(t,r,n),pp(e,2*r,2*n),pp(e,2*r+1,2*n+1);}function pp(t,e,r){const n=t[e];t[e]=t[r],t[r]=n;}function fp(t,e,r,n){const i=t-r,s=e-n;return i*i+s*s}var dp;t.cw=void 0,(dp=t.cw||(t.cw={})).create=\"create\",dp.load=\"load\",dp.fullLoad=\"fullLoad\";let yp=null,mp=[];const gp=1e3/60,xp=\"loadTime\",vp=\"fullLoadTime\",bp={mark(t){performance.mark(t);},frame(t){const e=t;null!=yp&&mp.push(e-yp),yp=e;},clearMetrics(){yp=null,mp=[],performance.clearMeasures(xp),performance.clearMeasures(vp);for(const e in t.cw)performance.clearMarks(t.cw[e]);},getPerformanceMetrics(){performance.measure(xp,t.cw.create,t.cw.load),performance.measure(vp,t.cw.create,t.cw.fullLoad);const e=performance.getEntriesByName(xp)[0].duration,r=performance.getEntriesByName(vp)[0].duration,n=mp.length,i=1/(mp.reduce(((t,e)=>t+e),0)/n/1e3),s=mp.filter((t=>t>gp)).reduce(((t,e)=>t+(e-gp)/gp),0);return {loadTime:e,fullLoadTime:r,fps:i,percentDroppedFrames:s/(n+s)*100,totalFrames:n}}};t.$=E,t.A=m,t.B=function([t,e,r]){return e+=90,e*=Math.PI/180,r*=Math.PI/180,{x:t*Math.cos(e)*Math.sin(r),y:t*Math.sin(e)*Math.sin(r),z:t*Math.cos(r)}},t.C=gr,t.D=Os,t.E=bt,t.F=Cs,t.G=ns,t.H=function(t){if(null==K){const e=t.navigator?t.navigator.userAgent:null;K=!!t.safari||!(!e||!(/\\b(iPad|iPhone|iPod)\\b/.test(e)||e.match(\"Safari\")&&!e.match(\"Chrome\")));}return K},t.I=mc,t.J=class{constructor(t,e){this.target=t,this.mapId=e,this.resolveRejects={},this.tasks={},this.taskQueue=[],this.abortControllers={},this.messageHandlers={},this.invoker=new ch((()=>this.process())),this.subscription=rt(this.target,\"message\",(t=>this.receive(t)),!1),this.globalScope=H(self)?t:window;}registerMessageHandler(t,e){this.messageHandlers[t]=e;}sendAsync(t,e){return new Promise(((r,n)=>{const i=Math.round(1e18*Math.random()).toString(36).substring(0,10),s=e?rt(e.signal,\"abort\",(()=>{null==s||s.unsubscribe(),delete this.resolveRejects[i];const e={id:i,type:\"<cancel>\",origin:location.origin,targetMapId:t.targetMapId,sourceMapId:this.mapId};this.target.postMessage(e);}),hh):null;this.resolveRejects[i]={resolve:t=>{null==s||s.unsubscribe(),r(t);},reject:t=>{null==s||s.unsubscribe(),n(t);}};const a=[],o=Object.assign(Object.assign({},t),{id:i,sourceMapId:this.mapId,origin:location.origin,data:fs(t.data,a)});this.target.postMessage(o,{transfer:a});}))}receive(t){const e=t.data,r=e.id;if(!(\"file://\"!==e.origin&&\"file://\"!==location.origin&&\"resource://android\"!==e.origin&&\"resource://android\"!==location.origin&&e.origin!==location.origin||e.targetMapId&&this.mapId!==e.targetMapId)){if(\"<cancel>\"===e.type){delete this.tasks[r];const t=this.abortControllers[r];return delete this.abortControllers[r],void(t&&t.abort())}if(H(self)||e.mustQueue)return this.tasks[r]=e,this.taskQueue.push(r),void this.invoker.trigger();this.processTask(r,e);}}process(){if(0===this.taskQueue.length)return;const t=this.taskQueue.shift(),e=this.tasks[t];delete this.tasks[t],this.taskQueue.length>0&&this.invoker.trigger(),e&&this.processTask(t,e);}processTask(t,r){return e(this,void 0,void 0,(function*(){if(\"<response>\"===r.type){const e=this.resolveRejects[t];if(delete this.resolveRejects[t],!e)return;return void(r.error?e.reject(ds(r.error)):e.resolve(ds(r.data)))}if(!this.messageHandlers[r.type])return void this.completeTask(t,new Error(`Could not find a registered handler for ${r.type}, map ID: ${this.mapId}, available handlers: ${Object.keys(this.messageHandlers).join(\", \")}`));const e=ds(r.data),n=new AbortController;this.abortControllers[t]=n;try{const i=yield this.messageHandlers[r.type](r.sourceMapId,e,n);this.completeTask(t,null,i);}catch(e){this.completeTask(t,e);}}))}completeTask(t,e,r){const n=[];delete this.abortControllers[t];const i={id:t,type:\"<response>\",sourceMapId:this.mapId,origin:location.origin,error:e?fs(e):null,data:fs(r,n)};this.target.postMessage(i,{transfer:n});}remove(){this.invoker.remove(),this.subscription.unsubscribe();}},t.K=ht,t.L=function(){var t=new m(16);return m!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},t.M=function(t,e,r){var n,i,s,a,o,l,u,c,h,p,f,d,y=r[0],m=r[1],g=r[2];return e===t?(t[12]=e[0]*y+e[4]*m+e[8]*g+e[12],t[13]=e[1]*y+e[5]*m+e[9]*g+e[13],t[14]=e[2]*y+e[6]*m+e[10]*g+e[14],t[15]=e[3]*y+e[7]*m+e[11]*g+e[15]):(i=e[1],s=e[2],a=e[3],o=e[4],l=e[5],u=e[6],c=e[7],h=e[8],p=e[9],f=e[10],d=e[11],t[0]=n=e[0],t[1]=i,t[2]=s,t[3]=a,t[4]=o,t[5]=l,t[6]=u,t[7]=c,t[8]=h,t[9]=p,t[10]=f,t[11]=d,t[12]=n*y+o*m+h*g+e[12],t[13]=i*y+l*m+p*g+e[13],t[14]=s*y+u*m+f*g+e[14],t[15]=a*y+c*m+d*g+e[15]),t},t.N=function(t,e,r){var n=r[0],i=r[1],s=r[2];return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t[3]=e[3]*n,t[4]=e[4]*i,t[5]=e[5]*i,t[6]=e[6]*i,t[7]=e[7]*i,t[8]=e[8]*s,t[9]=e[9]*s,t[10]=e[10]*s,t[11]=e[11]*s,t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},t.O=function(t,e,r){var n=e[0],i=e[1],s=e[2],a=e[3],o=e[4],l=e[5],u=e[6],c=e[7],h=e[8],p=e[9],f=e[10],d=e[11],y=e[12],m=e[13],g=e[14],x=e[15],v=r[0],b=r[1],w=r[2],_=r[3];return t[0]=v*n+b*o+w*h+_*y,t[1]=v*i+b*l+w*p+_*m,t[2]=v*s+b*u+w*f+_*g,t[3]=v*a+b*c+w*d+_*x,t[4]=(v=r[4])*n+(b=r[5])*o+(w=r[6])*h+(_=r[7])*y,t[5]=v*i+b*l+w*p+_*m,t[6]=v*s+b*u+w*f+_*g,t[7]=v*a+b*c+w*d+_*x,t[8]=(v=r[8])*n+(b=r[9])*o+(w=r[10])*h+(_=r[11])*y,t[9]=v*i+b*l+w*p+_*m,t[10]=v*s+b*u+w*f+_*g,t[11]=v*a+b*c+w*d+_*x,t[12]=(v=r[12])*n+(b=r[13])*o+(w=r[14])*h+(_=r[15])*y,t[13]=v*i+b*l+w*p+_*m,t[14]=v*s+b*u+w*f+_*g,t[15]=v*a+b*c+w*d+_*x,t},t.P=l,t.Q=function(t,e){const r={};for(let n=0;n<e.length;n++){const i=e[n];i in t&&(r[i]=t[i]);}return r},t.R=sl,t.S=fh,t.T=dl,t.U=gh,t.V=mh,t.W=f,t.X=d,t.Y=Q,t.Z=kh,t._=e,t.a=ut,t.a$=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t},t.a0=wh,t.a1=Ih,t.a2=25,t.a3=Sh,t.a4=t=>{const e=window.document.createElement(\"video\");return e.muted=!0,new Promise((r=>{e.onloadstart=()=>{r(e);};for(const r of t){const t=window.document.createElement(\"source\");yt(r)||(e.crossOrigin=\"Anonymous\"),t.src=r,e.appendChild(t);}}))},t.a5=Tt,t.a6=function(){return N++},t.a7=_a,t.a8=Jc,t.a9=di,t.aA=ac,t.aB=T,t.aC=function(t,e,r,n,i=!1){if(!r[0]&&!r[1])return [0,0];const s=i?\"map\"===n?-t.bearingInRadians:0:\"viewport\"===n?t.bearingInRadians:0;if(s){const t=Math.sin(s),e=Math.cos(s);r=[r[0]*e-r[1]*t,r[0]*t+r[1]*e];}return [i?r[0]:T(e,r[0],t.zoom),i?r[1]:T(e,r[1],t.zoom)]},t.aE=Uc,t.aF=rp,t.aG=Cc,t.aH=lp,t.aI=Ks,t.aJ=Yl,t.aK=Ta,t.aL=Ha,t.aM=Ga,t.aN=R,t.aO=it,t.aP=bh,t.aQ=S,t.aR=_,t.aS=function(t){var e=new m(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e},t.aT=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t},t.aU=function(t,e){var r=e[0],n=e[1],i=e[2],s=r*r+n*n+i*i;return s>0&&(s=1/Math.sqrt(s)),t[0]=e[0]*s,t[1]=e[1]*s,t[2]=e[2]*s,t},t.aV=A,t.aW=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},t.aX=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t[3]=e[3]*r[3],t},t.aY=b,t.aZ=function(t,e,r){const n=e[0]*r[0]+e[1]*r[1]+e[2]*r[2];return 0===n?null:(-(t[0]*r[0]+t[1]*r[1]+t[2]*r[2])-r[3])/n},t.a_=M,t.aa=Po,t.ab=Ph,t.ac=function(t){const e={};if(t.replace(/(?:^|(?:\\s*\\,\\s*))([^\\x00-\\x20\\(\\)<>@\\,;\\:\\\\\"\\/\\[\\]\\?\\=\\{\\}\\x7F]+)(?:\\=(?:([^\\x00-\\x20\\(\\)<>@\\,;\\:\\\\\"\\/\\[\\]\\?\\=\\{\\}\\x7F]+)|(?:\\\"((?:[^\"\\\\]|\\\\.)*)\\\")))?/g,((t,r,n,i)=>{const s=n||i;return e[r]=!s||s.toLowerCase(),\"\"})),e[\"max-age\"]){const t=parseInt(e[\"max-age\"],10);isNaN(t)?delete e[\"max-age\"]:e[\"max-age\"]=t;}return e},t.ad=nt,t.ae=function(t){return Math.pow(2,t)},t.af=x,t.ag=O,t.ah=85.051129,t.ai=xh,t.aj=function(t){return Math.log(t)/Math.LN2},t.ak=function(t){var e=t[0],r=t[1];return e*e+r*r},t.al=function(t,e){const r=[];for(const n in t)n in e||r.push(n);return r},t.am=function(t,e){let r=0,n=0;if(\"constant\"===t.kind)n=t.layoutSize;else if(\"source\"!==t.kind){const{interpolationType:i,minZoom:s,maxZoom:a}=t,o=i?O(yr.interpolationFactor(i,e,s,a),0,1):0;\"camera\"===t.kind?n=gr.number(t.minSize,t.maxSize,o):r=o;}return {uSizeT:r,uSize:n}},t.ao=function(t,{uSize:e,uSizeT:r},{lowerSize:n,upperSize:i}){return \"source\"===t.kind?n/Rc:\"composite\"===t.kind?gr.number(n/Rc,i/Rc,r):e},t.ap=function(t,e){var r=e[0],n=e[1],i=e[2],s=e[3],a=e[4],o=e[5],l=e[6],u=e[7],c=e[8],h=e[9],p=e[10],f=e[11],d=e[12],y=e[13],m=e[14],g=e[15],x=r*o-n*a,v=r*l-i*a,b=r*u-s*a,w=n*l-i*o,_=n*u-s*o,S=i*u-s*l,A=c*y-h*d,k=c*m-p*d,M=c*g-f*d,I=h*m-p*y,z=h*g-f*y,P=p*g-f*m,C=x*P-v*z+b*I+w*M-_*k+S*A;return C?(t[0]=(o*P-l*z+u*I)*(C=1/C),t[1]=(i*z-n*P-s*I)*C,t[2]=(y*S-m*_+g*w)*C,t[3]=(p*_-h*S-f*w)*C,t[4]=(l*M-a*P-u*k)*C,t[5]=(r*P-i*M+s*k)*C,t[6]=(m*b-d*S-g*v)*C,t[7]=(c*S-p*b+f*v)*C,t[8]=(a*z-o*M+u*A)*C,t[9]=(n*M-r*z-s*A)*C,t[10]=(d*_-y*b+g*x)*C,t[11]=(h*b-c*_-f*x)*C,t[12]=(o*k-a*I-l*A)*C,t[13]=(r*I-n*k+i*A)*C,t[14]=(y*v-d*w-m*x)*C,t[15]=(c*w-h*v+p*x)*C,t):null},t.aq=C,t.ar=function(t){return Math.hypot(t[0],t[1])},t.as=function(t){return t[0]=0,t[1]=0,t},t.at=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t},t.au=Zc,t.av=I,t.aw=function(t,e,r,n){const i=e.y-t.y,s=e.x-t.x,a=n.y-r.y,o=n.x-r.x,u=a*s-o*i;if(0===u)return null;const c=(o*(t.y-r.y)-a*(t.x-r.x))/u;return new l(t.x+c*s,t.y+c*i)},t.ax=Th,t.ay=To,t.az=function(t){let e=1/0,r=1/0,n=-1/0,i=-1/0;for(const s of t)e=Math.min(e,s.x),r=Math.min(r,s.y),n=Math.max(n,s.x),i=Math.max(i,s.y);return [e,r,n,i]},t.b=J,t.b$=tc,t.b0=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]},t.b1=Ah,t.b2=Mh,t.b3=function(t,e,r,n,i){var s,a=1/Math.tan(e/2);return t[0]=a/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=i&&i!==1/0?(t[10]=(i+n)*(s=1/(n-i)),t[14]=2*i*n*s):(t[10]=-1,t[14]=-2*n),t},t.b4=function(t){var e=new m(16);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},t.b5=function(t,e,r){var n=Math.sin(r),i=Math.cos(r),s=e[0],a=e[1],o=e[2],l=e[3],u=e[4],c=e[5],h=e[6],p=e[7];return e!==t&&(t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[0]=s*i+u*n,t[1]=a*i+c*n,t[2]=o*i+h*n,t[3]=l*i+p*n,t[4]=u*i-s*n,t[5]=c*i-a*n,t[6]=h*i-o*n,t[7]=p*i-l*n,t},t.b6=function(t,e,r){var n=Math.sin(r),i=Math.cos(r),s=e[4],a=e[5],o=e[6],l=e[7],u=e[8],c=e[9],h=e[10],p=e[11];return e!==t&&(t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[4]=s*i+u*n,t[5]=a*i+c*n,t[6]=o*i+h*n,t[7]=l*i+p*n,t[8]=u*i-s*n,t[9]=c*i-a*n,t[10]=h*i-o*n,t[11]=p*i-l*n,t},t.b7=function(){const t=new Float32Array(16);return x(t),t},t.b8=function(){const t=new Float64Array(16);return x(t),t},t.b9=function(){return new Float64Array(16)},t.bA=function(t){return t[0]=0,t[1]=0,t[2]=0,t},t.bB=function(t,e,r,n){const i=Math.sqrt(t*t+e*e),s=Math.sqrt(r*r+n*n);t/=i,e/=i,r/=s,n/=s;const a=Math.acos(t*r+e*n);return -e*r+t*n>0?a:-a},t.bC=function(t,e){const r=V(t,2*Math.PI),n=V(e,2*Math.PI);return Math.min(Math.abs(r-n),Math.abs(r-n+2*Math.PI),Math.abs(r-n-2*Math.PI))},t.bD=function(){const t={},e=wt.$version;for(const r in wt.$root){const n=wt.$root[r];if(n.required){let i=null;i=\"version\"===r?e:\"array\"===n.type?[]:{},null!=i&&(t[r]=i);}}return t},t.bE=ys,t.bF=ft,t.bG=function t(e,r){if(Array.isArray(e)){if(!Array.isArray(r)||e.length!==r.length)return !1;for(let n=0;n<e.length;n++)if(!t(e[n],r[n]))return !1;return !0}if(\"object\"==typeof e&&null!==e&&null!==r){if(\"object\"!=typeof r)return !1;if(Object.keys(e).length!==Object.keys(r).length)return !1;for(const n in e)if(!t(e[n],r[n]))return !1;return !0}return e===r},t.bH=function(t){t=t.slice();const e=Object.create(null);for(let r=0;r<t.length;r++)e[t[r].id]=t[r];for(let r=0;r<t.length;r++)\"ref\"in t[r]&&(t[r]=St(t[r],e[t[r].ref]));return t},t.bI=function(t){if(\"custom\"===t.type)return new uh(t);switch(t.type){case \"background\":return new sh(t);case \"circle\":return new Ko(t);case \"color-relief\":return new gl(t);case \"fill\":return new ou(t);case \"fill-extrusion\":return new Eu(t);case \"heatmap\":return new ll(t);case \"hillshade\":return new hl(t);case \"line\":return new Yu(t);case \"raster\":return new lh(t);case \"symbol\":return new rh(t)}},t.bJ=G,t.bK=function(t,e){if(!t)return [{command:\"setStyle\",args:[e]}];let r=[];try{if(!At(t.version,e.version))return [{command:\"setStyle\",args:[e]}];At(t.center,e.center)||r.push({command:\"setCenter\",args:[e.center]}),At(t.state,e.state)||r.push({command:\"setGlobalState\",args:[e.state]}),At(t.centerAltitude,e.centerAltitude)||r.push({command:\"setCenterAltitude\",args:[e.centerAltitude]}),At(t.zoom,e.zoom)||r.push({command:\"setZoom\",args:[e.zoom]}),At(t.bearing,e.bearing)||r.push({command:\"setBearing\",args:[e.bearing]}),At(t.pitch,e.pitch)||r.push({command:\"setPitch\",args:[e.pitch]}),At(t.roll,e.roll)||r.push({command:\"setRoll\",args:[e.roll]}),At(t.sprite,e.sprite)||r.push({command:\"setSprite\",args:[e.sprite]}),At(t.glyphs,e.glyphs)||r.push({command:\"setGlyphs\",args:[e.glyphs]}),At(t.transition,e.transition)||r.push({command:\"setTransition\",args:[e.transition]}),At(t.light,e.light)||r.push({command:\"setLight\",args:[e.light]}),At(t.terrain,e.terrain)||r.push({command:\"setTerrain\",args:[e.terrain]}),At(t.sky,e.sky)||r.push({command:\"setSky\",args:[e.sky]}),At(t.projection,e.projection)||r.push({command:\"setProjection\",args:[e.projection]});const n={},i=[];!function(t,e,r,n){let i;for(i in e=e||{},t=t||{})Object.prototype.hasOwnProperty.call(t,i)&&(Object.prototype.hasOwnProperty.call(e,i)||It(i,r,n));for(i in e)Object.prototype.hasOwnProperty.call(e,i)&&(Object.prototype.hasOwnProperty.call(t,i)?At(t[i],e[i])||(\"geojson\"===t[i].type&&\"geojson\"===e[i].type&&Pt(t,e,i)?kt(r,{command:\"setGeoJSONSourceData\",args:[i,e[i].data]}):zt(i,e,r,n)):Mt(i,e,r));}(t.sources,e.sources,i,n);const s=[];t.layers&&t.layers.forEach((t=>{\"source\"in t&&n[t.source]?r.push({command:\"removeLayer\",args:[t.id]}):s.push(t);})),r=r.concat(i),function(t,e,r){e=e||[];const n=(t=t||[]).map(Bt),i=e.map(Bt),s=t.reduce(Et,{}),a=e.reduce(Et,{}),o=n.slice(),l=Object.create(null);let u,c,h,p,f;for(let t=0,e=0;t<n.length;t++)u=n[t],Object.prototype.hasOwnProperty.call(a,u)?e++:(kt(r,{command:\"removeLayer\",args:[u]}),o.splice(o.indexOf(u,e),1));for(let t=0,e=0;t<i.length;t++)u=i[i.length-1-t],o[o.length-1-t]!==u&&(Object.prototype.hasOwnProperty.call(s,u)?(kt(r,{command:\"removeLayer\",args:[u]}),o.splice(o.lastIndexOf(u,o.length-e),1)):e++,p=o[o.length-t],kt(r,{command:\"addLayer\",args:[a[u],p]}),o.splice(o.length-t,0,u),l[u]=!0);for(let t=0;t<i.length;t++)if(u=i[t],c=s[u],h=a[u],!l[u]&&!At(c,h))if(At(c.source,h.source)&&At(c[\"source-layer\"],h[\"source-layer\"])&&At(c.type,h.type)){for(f in Ct(c.layout,h.layout,r,u,null,\"setLayoutProperty\"),Ct(c.paint,h.paint,r,u,null,\"setPaintProperty\"),At(c.filter,h.filter)||kt(r,{command:\"setFilter\",args:[u,h.filter]}),At(c.minzoom,h.minzoom)&&At(c.maxzoom,h.maxzoom)||kt(r,{command:\"setLayerZoomRange\",args:[u,h.minzoom,h.maxzoom]}),c)Object.prototype.hasOwnProperty.call(c,f)&&\"layout\"!==f&&\"paint\"!==f&&\"filter\"!==f&&\"metadata\"!==f&&\"minzoom\"!==f&&\"maxzoom\"!==f&&(0===f.indexOf(\"paint.\")?Ct(c[f],h[f],r,u,f.slice(6),\"setPaintProperty\"):At(c[f],h[f])||kt(r,{command:\"setLayerProperty\",args:[u,f,h[f]]}));for(f in h)Object.prototype.hasOwnProperty.call(h,f)&&!Object.prototype.hasOwnProperty.call(c,f)&&\"layout\"!==f&&\"paint\"!==f&&\"filter\"!==f&&\"metadata\"!==f&&\"minzoom\"!==f&&\"maxzoom\"!==f&&(0===f.indexOf(\"paint.\")?Ct(c[f],h[f],r,u,f.slice(6),\"setPaintProperty\"):At(c[f],h[f])||kt(r,{command:\"setLayerProperty\",args:[u,f,h[f]]}));}else kt(r,{command:\"removeLayer\",args:[u]}),p=o[o.lastIndexOf(u)+1],kt(r,{command:\"addLayer\",args:[h,p]});}(s,e.layers,r);}catch(t){console.warn(\"Unable to compute style diff:\",t),r=[{command:\"setStyle\",args:[e]}];}return r},t.bL=function(t){const e=[],r=t.id;return void 0===r&&e.push({message:`layers.${r}: missing required property \"id\"`}),void 0===t.render&&e.push({message:`layers.${r}: missing required method \"render\"`}),t.renderingMode&&\"2d\"!==t.renderingMode&&\"3d\"!==t.renderingMode&&e.push({message:`layers.${r}: property \"renderingMode\" must be either \"2d\" or \"3d\"`}),e},t.bM=U,t.bN=q,t.bO=class extends co{constructor(t,e){super(t,e),this.current=0;}set(t){this.current!==t&&(this.current=t,this.gl.uniform1i(this.location,t));}},t.bP=fo,t.bQ=class extends co{constructor(t,e){super(t,e),this.current=yo;}set(t){if(t[12]!==this.current[12]||t[0]!==this.current[0])return this.current=t,void this.gl.uniformMatrix4fv(this.location,!1,t);for(let e=1;e<16;e++)if(t[e]!==this.current[e]){this.current=t,this.gl.uniformMatrix4fv(this.location,!1,t);break}}},t.bR=po,t.bS=class extends co{constructor(t,e){super(t,e),this.current=[0,0,0];}set(t){t[0]===this.current[0]&&t[1]===this.current[1]&&t[2]===this.current[2]||(this.current=t,this.gl.uniform3f(this.location,t[0],t[1],t[2]));}},t.bT=class extends co{constructor(t,e){super(t,e),this.current=[0,0];}set(t){t[0]===this.current[0]&&t[1]===this.current[1]||(this.current=t,this.gl.uniform2f(this.location,t[0],t[1]));}},t.bU=g,t.bV=function(t,e){var r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=r,t[2]=0,t[3]=-r,t[4]=n,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},t.bW=function(t,e,r){var n=e[0],i=e[1],s=e[2];return t[0]=n*r[0]+i*r[3]+s*r[6],t[1]=n*r[1]+i*r[4]+s*r[7],t[2]=n*r[2]+i*r[5]+s*r[8],t},t.bX=function(t,e,r,n,i,s,a){var o=1/(e-r),l=1/(n-i),u=1/(s-a);return t[0]=-2*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*l,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*u,t[11]=0,t[12]=(e+r)*o,t[13]=(i+n)*l,t[14]=(a+s)*u,t[15]=1,t},t.bY=class extends co{constructor(t,e){super(t,e),this.current=new Array;}set(t){if(t!=this.current){this.current=t;const e=new Float32Array(4*t.length);for(let r=0;r<t.length;r++)e[4*r]=t[r].r,e[4*r+1]=t[r].g,e[4*r+2]=t[r].b,e[4*r+3]=t[r].a;this.gl.uniform4fv(this.location,e);}}},t.bZ=class extends co{constructor(t,e){super(t,e),this.current=new Array;}set(t){if(t!=this.current){this.current=t;const e=new Float32Array(t);this.gl.uniform1fv(this.location,e);}}},t.b_=class extends ca{},t.ba=function(t,e,r){const n=new Float64Array(4);return P(n,t,e-90,r),n},t.bb=function(t,e,r,n){var i,s,a,o,l,u=e[0],c=e[1],h=e[2],p=e[3],f=r[0],d=r[1],m=r[2],g=r[3];return (s=u*f+c*d+h*m+p*g)<0&&(s=-s,f=-f,d=-d,m=-m,g=-g),1-s>y?(i=Math.acos(s),a=Math.sin(i),o=Math.sin((1-n)*i)/a,l=Math.sin(n*i)/a):(o=1-n,l=n),t[0]=o*u+l*f,t[1]=o*c+l*d,t[2]=o*h+l*m,t[3]=o*p+l*g,t},t.bc=function(t){const e=new Float64Array(9);var r,n,i,s,a,o,l,u,c,h,p,f,d,y,m,g,x,v;h=(i=(n=t)[0])*(l=i+i),p=(s=n[1])*l,d=(a=n[2])*l,y=a*(u=s+s),g=(o=n[3])*l,x=o*u,v=o*(c=a+a),(r=e)[0]=1-(f=s*u)-(m=a*c),r[3]=p-v,r[6]=d+x,r[1]=p+v,r[4]=1-h-m,r[7]=y-g,r[2]=d-x,r[5]=y+g,r[8]=1-h-f;const b=it(-Math.asin(O(e[2],-1,1)));let w,_;return Math.hypot(e[5],e[8])<.001?(w=0,_=-it(Math.atan2(e[3],e[4]))):(w=it(0===e[5]&&0===e[8]?0:Math.atan2(e[5],e[8])),_=it(0===e[1]&&0===e[0]?0:Math.atan2(e[1],e[0]))),{roll:w,pitch:b+90,bearing:_}},t.bd=function(t,e){return t.roll==e.roll&&t.pitch==e.pitch&&t.bearing==e.bearing},t.be=Pe,t.bf=ho,t.bg=Hl,t.bh=Kl,t.bi=Zl,t.bj=F,t.bk=L,t.bl=je,t.bm=function(t,e,r,n,i){return F(n,i,O((t-e)/(r-e),0,1))},t.bn=V,t.bo=function(){return new Float64Array(3)},t.bp=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t},t.bq=P,t.br=function(t,e,r){var n=r[0],i=r[1],s=r[2],a=e[0],o=e[1],l=e[2],u=i*l-s*o,c=s*a-n*l,h=n*o-i*a,p=i*h-s*c,f=s*u-n*h,d=n*c-i*u,y=2*r[3];return c*=y,h*=y,f*=2,d*=2,t[0]=a+(u*=y)+(p*=2),t[1]=o+c+f,t[2]=l+h+d,t},t.bs=function(t,e,r){const n=(i=[t[0],t[1],t[2],e[0],e[1],e[2],r[0],r[1],r[2]])[0]*((c=i[8])*(a=i[4])-(o=i[5])*(u=i[7]))+i[1]*(-c*(s=i[3])+o*(l=i[6]))+i[2]*(u*s-a*l);var i,s,a,o,l,u,c;if(0===n)return null;const h=A([],[e[0],e[1],e[2]],[r[0],r[1],r[2]]),p=A([],[r[0],r[1],r[2]],[t[0],t[1],t[2]]),f=A([],[t[0],t[1],t[2]],[e[0],e[1],e[2]]),d=S([],h,-t[3]);return _(d,d,S([],p,-e[3])),_(d,d,S([],f,-r[3])),S(d,d,1/n),d},t.bt=ph,t.bu=function(){return new Float64Array(4)},t.bv=function(t,e,r,n){var i=[],s=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],s[0]=i[0]*Math.cos(n)-i[1]*Math.sin(n),s[1]=i[0]*Math.sin(n)+i[1]*Math.cos(n),s[2]=i[2],t[0]=s[0]+r[0],t[1]=s[1]+r[1],t[2]=s[2]+r[2],t},t.bw=function(t,e,r,n){var i=[],s=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],s[0]=i[0],s[1]=i[1]*Math.cos(n)-i[2]*Math.sin(n),s[2]=i[1]*Math.sin(n)+i[2]*Math.cos(n),t[0]=s[0]+r[0],t[1]=s[1]+r[1],t[2]=s[2]+r[2],t},t.bx=function(t,e,r,n){var i=[],s=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],s[0]=i[2]*Math.sin(n)+i[0]*Math.cos(n),s[1]=i[1],s[2]=i[2]*Math.cos(n)-i[0]*Math.sin(n),t[0]=s[0]+r[0],t[1]=s[1]+r[1],t[2]=s[2]+r[2],t},t.by=function(t,e,r){var n=Math.sin(r),i=Math.cos(r),s=e[0],a=e[1],o=e[2],l=e[3],u=e[8],c=e[9],h=e[10],p=e[11];return e!==t&&(t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[0]=s*i-u*n,t[1]=a*i-c*n,t[2]=o*i-h*n,t[3]=l*i-p*n,t[8]=s*n+u*i,t[9]=a*n+c*i,t[10]=o*n+h*i,t[11]=l*n+p*i,t},t.bz=function(t,e){const r=V(t,360),n=V(e,360),i=n-r,s=n>r?i-360:i+360;return Math.abs(i)<Math.abs(s)?i:s},t.c=lt,t.c0=class extends pa{},t.c1=ol,t.c2=function(t){return t<=1?1:Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},t.c3=al,t.c4=function(t,e,r){var n=e[0],i=e[1],s=e[2],a=r[3]*n+r[7]*i+r[11]*s+r[15];return t[0]=(r[0]*n+r[4]*i+r[8]*s+r[12])/(a=a||1),t[1]=(r[1]*n+r[5]*i+r[9]*s+r[13])/a,t[2]=(r[2]*n+r[6]*i+r[10]*s+r[14])/a,t},t.c5=class extends ta{},t.c6=class extends va{},t.c7=function(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]&&t[6]===e[6]&&t[7]===e[7]&&t[8]===e[8]&&t[9]===e[9]&&t[10]===e[10]&&t[11]===e[11]&&t[12]===e[12]&&t[13]===e[13]&&t[14]===e[14]&&t[15]===e[15]},t.c8=function(t,e){var r=t[0],n=t[1],i=t[2],s=t[3],a=t[4],o=t[5],l=t[6],u=t[7],c=t[8],h=t[9],p=t[10],f=t[11],d=t[12],m=t[13],g=t[14],x=t[15],v=e[0],b=e[1],w=e[2],_=e[3],S=e[4],A=e[5],k=e[6],M=e[7],I=e[8],z=e[9],P=e[10],C=e[11],B=e[12],E=e[13],T=e[14],V=e[15];return Math.abs(r-v)<=y*Math.max(1,Math.abs(r),Math.abs(v))&&Math.abs(n-b)<=y*Math.max(1,Math.abs(n),Math.abs(b))&&Math.abs(i-w)<=y*Math.max(1,Math.abs(i),Math.abs(w))&&Math.abs(s-_)<=y*Math.max(1,Math.abs(s),Math.abs(_))&&Math.abs(a-S)<=y*Math.max(1,Math.abs(a),Math.abs(S))&&Math.abs(o-A)<=y*Math.max(1,Math.abs(o),Math.abs(A))&&Math.abs(l-k)<=y*Math.max(1,Math.abs(l),Math.abs(k))&&Math.abs(u-M)<=y*Math.max(1,Math.abs(u),Math.abs(M))&&Math.abs(c-I)<=y*Math.max(1,Math.abs(c),Math.abs(I))&&Math.abs(h-z)<=y*Math.max(1,Math.abs(h),Math.abs(z))&&Math.abs(p-P)<=y*Math.max(1,Math.abs(p),Math.abs(P))&&Math.abs(f-C)<=y*Math.max(1,Math.abs(f),Math.abs(C))&&Math.abs(d-B)<=y*Math.max(1,Math.abs(d),Math.abs(B))&&Math.abs(m-E)<=y*Math.max(1,Math.abs(m),Math.abs(E))&&Math.abs(g-T)<=y*Math.max(1,Math.abs(g),Math.abs(T))&&Math.abs(x-V)<=y*Math.max(1,Math.abs(x),Math.abs(V))},t.c9=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},t.cA=function(t){delete ut.REGISTERED_PROTOCOLS[t];},t.cB=function(t,e){const r={};for(let n=0;n<t.length;n++){const i=e&&e[t[n].id]||Si(t[n]);e&&(e[t[n].id]=i);let s=r[i];s||(s=r[i]=[]),s.push(t[n]);}const n=[];for(const t in r)n.push(r[t]);return n},t.cC=us,t.cD=zh,t.cE=Ch,t.cF=gc,t.cG=function(e){e.bucket.createArrays(),e.bucket.tilePixelRatio=E/(512*e.bucket.overscaling),e.bucket.compareText={},e.bucket.iconsNeedLinear=!1;const r=e.bucket.layers[0],n=r.layout,i=r._unevaluatedLayout._values,s={layoutIconSize:i[\"icon-size\"].possiblyEvaluate(new Cs(e.bucket.zoom+1),e.canonical),layoutTextSize:i[\"text-size\"].possiblyEvaluate(new Cs(e.bucket.zoom+1),e.canonical),textMaxSize:i[\"text-size\"].possiblyEvaluate(new Cs(18))};if(\"composite\"===e.bucket.textSizeData.kind){const{minZoom:t,maxZoom:r}=e.bucket.textSizeData;s.compositeTextSizes=[i[\"text-size\"].possiblyEvaluate(new Cs(t),e.canonical),i[\"text-size\"].possiblyEvaluate(new Cs(r),e.canonical)];}if(\"composite\"===e.bucket.iconSizeData.kind){const{minZoom:t,maxZoom:r}=e.bucket.iconSizeData;s.compositeIconSizes=[i[\"icon-size\"].possiblyEvaluate(new Cs(t),e.canonical),i[\"icon-size\"].possiblyEvaluate(new Cs(r),e.canonical)];}const a=n.get(\"text-line-height\")*ac,o=\"viewport\"!==n.get(\"text-rotation-alignment\")&&\"point\"!==n.get(\"symbol-placement\"),l=n.get(\"text-keep-upright\"),u=n.get(\"text-size\");for(const i of e.bucket.features){const c=n.get(\"text-font\").evaluate(i,{},e.canonical).join(\",\"),h=u.evaluate(i,{},e.canonical),p=s.layoutTextSize.evaluate(i,{},e.canonical),f=s.layoutIconSize.evaluate(i,{},e.canonical),d={horizontal:{},vertical:void 0},y=i.text;let m,g=[0,0];if(y){const s=y.toString(),u=n.get(\"text-letter-spacing\").evaluate(i,{},e.canonical)*ac,f=xs(s)?u:0,m=n.get(\"text-anchor\").evaluate(i,{},e.canonical),x=ep(r,i,e.canonical);if(!x){const t=n.get(\"text-radial-offset\").evaluate(i,{},e.canonical);g=t?tp(m,[t*ac,Qh]):n.get(\"text-offset\").evaluate(i,{},e.canonical).map((t=>t*ac));}let v=o?\"center\":n.get(\"text-justify\").evaluate(i,{},e.canonical);const b=\"point\"===n.get(\"symbol-placement\")?n.get(\"text-max-width\").evaluate(i,{},e.canonical)*ac:1/0,w=()=>{e.bucket.allowVerticalPlacement&&gs(s)&&(d.vertical=bc(y,e.glyphMap,e.glyphPositions,e.imagePositions,c,b,a,m,\"left\",f,g,t.an.vertical,!0,p,h));};if(!o&&x){const r=new Set;if(\"auto\"===v)for(let t=0;t<x.values.length;t+=2)r.add(rp(x.values[t]));else r.add(v);let n=!1;for(const i of r)if(!d.horizontal[i])if(n)d.horizontal[i]=d.horizontal[0];else {const r=bc(y,e.glyphMap,e.glyphPositions,e.imagePositions,c,b,a,\"center\",i,f,g,t.an.horizontal,!1,p,h);r&&(d.horizontal[i]=r,n=1===r.positionedLines.length);}w();}else {\"auto\"===v&&(v=rp(m));const r=bc(y,e.glyphMap,e.glyphPositions,e.imagePositions,c,b,a,m,v,f,g,t.an.horizontal,!1,p,h);r&&(d.horizontal[v]=r),w(),gs(s)&&o&&l&&(d.vertical=bc(y,e.glyphMap,e.glyphPositions,e.imagePositions,c,b,a,m,v,f,g,t.an.vertical,!1,p,h));}}let x=!1;if(i.icon&&i.icon.name){const t=e.imageMap[i.icon.name];t&&(m=$c(e.imagePositions[i.icon.name],n.get(\"icon-offset\").evaluate(i,{},e.canonical),n.get(\"icon-anchor\").evaluate(i,{},e.canonical)),x=!!t.sdf,void 0===e.bucket.sdfIcons?e.bucket.sdfIcons=x:e.bucket.sdfIcons!==x&&Z(\"Style sheet warning: Cannot mix SDF and non-SDF icons in one buffer\"),(t.pixelRatio!==e.bucket.pixelRatio||0!==n.get(\"icon-rotate\").constantOr(1))&&(e.bucket.iconsNeedLinear=!0));}const v=sp(d.horizontal)||d.vertical;e.bucket.iconsInText=!!v&&v.iconsInText,(v||m)&&np(e.bucket,i,d,m,e.imageMap,s,p,f,g,x,e.canonical,e.subdivisionGranularity);}e.showCollisionBoxes&&e.bucket.generateCollisionDebugBuffers();},t.cH=Nu,t.cI=nu,t.cJ=Mu,t.cK=_u,t.cL=hc,t.cM=class{constructor(t){this._marks={start:[t.url,\"start\"].join(\"#\"),end:[t.url,\"end\"].join(\"#\"),measure:t.url.toString()},performance.mark(this._marks.start);}finish(){performance.mark(this._marks.end);let t=performance.getEntriesByName(this._marks.measure);return 0===t.length&&(performance.measure(this._marks.measure,this._marks.start,this._marks.end),t=performance.getEntriesByName(this._marks.measure),performance.clearMarks(this._marks.start),performance.clearMarks(this._marks.end),performance.clearMeasures(this._marks.measure)),t}},t.cN=function(t,r,n,i,s){return e(this,void 0,void 0,(function*(){if(d())try{return yield Q(t,r,n,i,s)}catch(t){}return function(t,e,r,n,i){const s=t.width,a=t.height;tt&&et||(tt=new OffscreenCanvas(s,a),et=tt.getContext(\"2d\",{willReadFrequently:!0})),tt.width=s,tt.height=a,et.drawImage(t,0,0,s,a);const o=et.getImageData(e,r,n,i);return et.clearRect(0,0,s,a),o.data}(t,r,n,i,s)}))},t.cO=yl,t.cP=r,t.cQ=s,t.cR=wu,t.cS=uc,t.cT=si,t.cU=Ps,t.ca=t=>\"symbol\"===t.type,t.cb=t=>\"circle\"===t.type,t.cc=t=>\"heatmap\"===t.type,t.cd=t=>\"line\"===t.type,t.ce=t=>\"fill\"===t.type,t.cf=t=>\"fill-extrusion\"===t.type,t.cg=t=>\"hillshade\"===t.type,t.ch=t=>\"color-relief\"===t.type,t.ci=t=>\"raster\"===t.type,t.cj=t=>\"background\"===t.type,t.ck=t=>\"custom\"===t.type,t.cl=$,t.cm=function(t,e,r){const n=B(e.x-r.x,e.y-r.y),i=B(t.x-r.x,t.y-r.y);var s,a;return it(Math.atan2(n[0]*i[1]-n[1]*i[0],(s=n)[0]*(a=i)[0]+s[1]*a[1]))},t.cn=D,t.co=function(t,e){return at[e]&&(t instanceof MouseEvent||t instanceof WheelEvent)},t.cp=function(t,e){return st[e]&&\"touches\"in t},t.cq=function(t){return st[t]||at[t]},t.cr=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[4]*i+r[12],t[1]=r[1]*n+r[5]*i+r[13],t},t.cs=function(t,e){const{x:r,y:n}=wh.fromLngLat(e);return !(t<0||t>25||n<0||n>=1||r<0||r>=1)},t.ct=function(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=e[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=e[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},t.cu=class extends Qs{},t.cv=bp,t.cx=function(t){return t.message===ot},t.cy=pt,t.cz=function(t,e){ut.REGISTERED_PROTOCOLS[t]=e;},t.d=yt,t.e=j,t.f=t=>e(void 0,void 0,void 0,(function*(){if(0===t.byteLength)return createImageBitmap(new ImageData(1,1));const e=new Blob([new Uint8Array(t)],{type:\"image/png\"});try{return createImageBitmap(e)}catch(t){throw new Error(`Could not load image because of ${t.message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`)}})),t.g=ct,t.h=t=>new Promise(((e,r)=>{const n=new Image;n.onload=()=>{e(n),URL.revokeObjectURL(n.src),n.onload=null,window.requestAnimationFrame((()=>{n.src=W;}));},n.onerror=()=>r(new Error(\"Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.\"));const i=new Blob([new Uint8Array(t)],{type:\"image/png\"});n.src=t.byteLength?URL.createObjectURL(i):W;})),t.i=H,t.j=(t,e)=>dt(j(t,{type:\"json\"}),e),t.k=vt,t.l=xt,t.m=dt,t.n=(t,e)=>dt(j(t,{type:\"arrayBuffer\"}),e),t.o=function(t){return new hc(t).readFields(pc,[])},t.p=yc,t.q=il,t.r=qs,t.s=rt,t.t=Ts,t.u=ms,t.v=wt,t.w=Z,t.x=rs,t.y=as,t.z=es;}));\r\n\r\ndefine(\"worker\",[\"./shared\"],(function(e){\"use strict\";class t{constructor(e){this.keyCache={},e&&this.replace(e);}replace(e){this._layerConfigs={},this._layers={},this.update(e,[]);}update(t,o){for(const o of t){this._layerConfigs[o.id]=o;const t=this._layers[o.id]=e.bI(o);t._featureFilter=e.a9(t.filter),this.keyCache[o.id]&&delete this.keyCache[o.id];}for(const e of o)delete this.keyCache[e],delete this._layerConfigs[e],delete this._layers[e];this.familiesBySource={};const i=e.cB(Object.values(this._layerConfigs),this.keyCache);for(const e of i){const t=e.map((e=>this._layers[e.id])),o=t[0];if(\"none\"===o.visibility)continue;const i=o.source||\"\";let r=this.familiesBySource[i];r||(r=this.familiesBySource[i]={});const s=o.sourceLayer||\"_geojsonTileLayer\";let n=r[s];n||(n=r[s]=[]),n.push(t);}}}class o{constructor(t){const o={},i=[];for(const e in t){const r=t[e],s=o[e]={};for(const e in r){const t=r[+e];if(!t||0===t.bitmap.width||0===t.bitmap.height)continue;const o={x:0,y:0,w:t.bitmap.width+2,h:t.bitmap.height+2};i.push(o),s[e]={rect:o,metrics:t.metrics};}}const{w:r,h:s}=e.p(i),n=new e.q({width:r||1,height:s||1});for(const i in t){const r=t[i];for(const t in r){const s=r[+t];if(!s||0===s.bitmap.width||0===s.bitmap.height)continue;const a=o[i][t].rect;e.q.copy(s.bitmap,n,{x:0,y:0},{x:a.x+1,y:a.y+1},s.bitmap);}}this.image=n,this.positions=o;}}e.cC(\"GlyphAtlas\",o);class i{constructor(t){this.tileID=new e.Z(t.tileID.overscaledZ,t.tileID.wrap,t.tileID.canonical.z,t.tileID.canonical.x,t.tileID.canonical.y),this.uid=t.uid,this.zoom=t.zoom,this.pixelRatio=t.pixelRatio,this.tileSize=t.tileSize,this.source=t.source,this.overscaling=this.tileID.overscaleFactor(),this.showCollisionBoxes=t.showCollisionBoxes,this.collectResourceTiming=!!t.collectResourceTiming,this.returnDependencies=!!t.returnDependencies,this.promoteId=t.promoteId,this.inFlightDependencies=[],this.globalState=t.globalState;}parse(t,i,s,n,a){return e._(this,void 0,void 0,(function*(){this.status=\"parsing\",this.data=t,this.collisionBoxArray=new e.a7;const l=new e.cD(Object.keys(t.layers).sort()),c=new e.cE(this.tileID,this.promoteId);c.bucketLayerIDs=[];const u={},h={featureIndex:c,iconDependencies:{},patternDependencies:{},glyphDependencies:{},availableImages:s,subdivisionGranularity:a},d=i.familiesBySource[this.source];for(const o in d){const i=t.layers[o];if(!i)continue;1===i.version&&e.w(`Vector tile source \"${this.source}\" layer \"${o}\" does not use vector tile spec v2 and therefore may have some rendering errors.`);const n=l.encode(o),a=[];for(let e=0;e<i.length;e++){const t=i.feature(e),r=c.getId(t,o);a.push({feature:t,id:r,index:e,sourceLayerIndex:n});}for(const t of d[o]){const o=t[0];o.source!==this.source&&e.w(`layer.source = ${o.source} does not equal this.source = ${this.source}`),o.minzoom&&this.zoom<Math.floor(o.minzoom)||o.maxzoom&&this.zoom>=o.maxzoom||\"none\"!==o.visibility&&(r(t,this.zoom,s),(u[o.id]=o.createBucket({index:c.bucketLayerIDs.length,layers:t,zoom:this.zoom,pixelRatio:this.pixelRatio,overscaling:this.overscaling,collisionBoxArray:this.collisionBoxArray,sourceLayerIndex:n,sourceID:this.source,globalState:this.globalState})).populate(a,h,this.tileID.canonical),c.bucketLayerIDs.push(t.map((e=>e.id))));}}const f=e.bM(h.glyphDependencies,(e=>Object.keys(e).map(Number)));this.inFlightDependencies.forEach((e=>null==e?void 0:e.abort())),this.inFlightDependencies=[];let g=Promise.resolve({});if(Object.keys(f).length){const e=new AbortController;this.inFlightDependencies.push(e),g=n.sendAsync({type:\"GG\",data:{stacks:f,source:this.source,tileID:this.tileID,type:\"glyphs\"}},e);}const p=Object.keys(h.iconDependencies);let m=Promise.resolve({});if(p.length){const e=new AbortController;this.inFlightDependencies.push(e),m=n.sendAsync({type:\"GI\",data:{icons:p,source:this.source,tileID:this.tileID,type:\"icons\"}},e);}const y=Object.keys(h.patternDependencies);let v=Promise.resolve({});if(y.length){const e=new AbortController;this.inFlightDependencies.push(e),v=n.sendAsync({type:\"GI\",data:{icons:y,source:this.source,tileID:this.tileID,type:\"patterns\"}},e);}const[w,x,b]=yield Promise.all([g,m,v]),S=new o(w),_=new e.cF(x,b);for(const t in u){const o=u[t];o instanceof e.a8?(r(o.layers,this.zoom,s),e.cG({bucket:o,glyphMap:w,glyphPositions:S.positions,imageMap:x,imagePositions:_.iconPositions,showCollisionBoxes:this.showCollisionBoxes,canonical:this.tileID.canonical,subdivisionGranularity:h.subdivisionGranularity})):o.hasPattern&&(o instanceof e.cH||o instanceof e.cI||o instanceof e.cJ)&&(r(o.layers,this.zoom,s),o.addFeatures(h,this.tileID.canonical,_.patternPositions));}return this.status=\"done\",{buckets:Object.values(u).filter((e=>!e.isEmpty())),featureIndex:c,collisionBoxArray:this.collisionBoxArray,glyphAtlasImage:S.image,imageAtlas:_,glyphMap:this.returnDependencies?w:null,iconMap:this.returnDependencies?x:null,glyphPositions:this.returnDependencies?S.positions:null}}))}}function r(t,o,i){const r=new e.F(o);for(const e of t)e.recalculate(r,i);}class s{constructor(e,t,o){this.actor=e,this.layerIndex=t,this.availableImages=o,this.fetching={},this.loading={},this.loaded={};}loadVectorTile(t,o){return e._(this,void 0,void 0,(function*(){const i=yield e.n(t.request,o);try{return {vectorTile:new e.cK.VectorTile(new e.cL(i.data)),rawData:i.data,cacheControl:i.cacheControl,expires:i.expires}}catch(e){const o=new Uint8Array(i.data);let r=`Unable to parse the tile at ${t.request.url}, `;throw r+=31===o[0]&&139===o[1]?\"please make sure the data is not gzipped and that you have configured the relevant header in the server\":`got error: ${e.message}`,new Error(r)}}))}loadTile(t){return e._(this,void 0,void 0,(function*(){const o=t.uid,r=!!(t&&t.request&&t.request.collectResourceTiming)&&new e.cM(t.request),s=new i(t);this.loading[o]=s;const n=new AbortController;s.abort=n;try{const i=yield this.loadVectorTile(t,n);if(delete this.loading[o],!i)return null;const a=i.rawData,l={};i.expires&&(l.expires=i.expires),i.cacheControl&&(l.cacheControl=i.cacheControl);const c={};if(r){const e=r.finish();e&&(c.resourceTiming=JSON.parse(JSON.stringify(e)));}s.vectorTile=i.vectorTile;const u=s.parse(i.vectorTile,this.layerIndex,this.availableImages,this.actor,t.subdivisionGranularity);this.loaded[o]=s,this.fetching[o]={rawTileData:a,cacheControl:l,resourceTiming:c};try{const t=yield u;return e.e({rawTileData:a.slice(0)},t,l,c)}finally{delete this.fetching[o];}}catch(e){throw delete this.loading[o],s.status=\"done\",this.loaded[o]=s,e}}))}reloadTile(t){return e._(this,void 0,void 0,(function*(){const o=t.uid;if(!this.loaded||!this.loaded[o])throw new Error(\"Should not be trying to reload a tile that was never loaded or has been removed\");const i=this.loaded[o];if(i.showCollisionBoxes=t.showCollisionBoxes,i.globalState=t.globalState,\"parsing\"===i.status){const r=yield i.parse(i.vectorTile,this.layerIndex,this.availableImages,this.actor,t.subdivisionGranularity);let s;if(this.fetching[o]){const{rawTileData:t,cacheControl:i,resourceTiming:n}=this.fetching[o];delete this.fetching[o],s=e.e({rawTileData:t.slice(0)},r,i,n);}else s=r;return s}if(\"done\"===i.status&&i.vectorTile)return i.parse(i.vectorTile,this.layerIndex,this.availableImages,this.actor,t.subdivisionGranularity)}))}abortTile(t){return e._(this,void 0,void 0,(function*(){const e=this.loading,o=t.uid;e&&e[o]&&e[o].abort&&(e[o].abort.abort(),delete e[o]);}))}removeTile(t){return e._(this,void 0,void 0,(function*(){this.loaded&&this.loaded[t.uid]&&delete this.loaded[t.uid];}))}}class n{constructor(){this.loaded={};}loadTile(t){return e._(this,void 0,void 0,(function*(){const{uid:o,encoding:i,rawImageData:r,redFactor:s,greenFactor:n,blueFactor:a,baseShift:l}=t,c=r.width+2,u=r.height+2,h=e.b(r)?new e.R({width:c,height:u},yield e.cN(r,-1,-1,c,u)):r,d=new e.cO(o,h,i,s,n,a,l);return this.loaded=this.loaded||{},this.loaded[o]=d,d}))}removeTile(e){const t=this.loaded,o=e.uid;t&&t[o]&&delete t[o];}}var a,l,c=function(){if(l)return a;function e(e,o){if(0!==e.length){t(e[0],o);for(var i=1;i<e.length;i++)t(e[i],!o);}}function t(e,t){for(var o=0,i=0,r=0,s=e.length,n=s-1;r<s;n=r++){var a=(e[r][0]-e[n][0])*(e[n][1]+e[r][1]),l=o+a;i+=Math.abs(o)>=Math.abs(a)?o-l+a:a-l+o,o=l;}o+i>=0!=!!t&&e.reverse();}return l=1,a=function t(o,i){var r,s=o&&o.type;if(\"FeatureCollection\"===s)for(r=0;r<o.features.length;r++)t(o.features[r],i);else if(\"GeometryCollection\"===s)for(r=0;r<o.geometries.length;r++)t(o.geometries[r],i);else if(\"Feature\"===s)t(o.geometry,i);else if(\"Polygon\"===s)e(o.coordinates,i);else if(\"MultiPolygon\"===s)for(r=0;r<o.coordinates.length;r++)e(o.coordinates[r],i);return o}}(),u=e.cP(c);const h=e.cK.VectorTileFeature.prototype.toGeoJSON;class d{constructor(t){this._feature=t,this.extent=e.$,this.type=t.type,this.properties=t.tags,\"id\"in t&&!isNaN(t.id)&&(this.id=parseInt(t.id,10));}loadGeometry(){if(1===this._feature.type){const t=[];for(const o of this._feature.geometry)t.push([new e.P(o[0],o[1])]);return t}{const t=[];for(const o of this._feature.geometry){const i=[];for(const t of o)i.push(new e.P(t[0],t[1]));t.push(i);}return t}}toGeoJSON(e,t,o){return h.call(this,e,t,o)}}class f{constructor(t){this.layers={_geojsonTileLayer:this},this.name=\"_geojsonTileLayer\",this.extent=e.$,this.length=t.length,this._features=t;}feature(e){return new d(this._features[e])}}var g,p,m,y={exports:{}},v=function(){if(m)return y.exports;m=1;var t=e.cS(),o=function(){if(p)return g;p=1;var t=e.cQ(),o=e.cR().VectorTileFeature;function i(e,t){this.options=t||{},this.features=e,this.length=e.length;}function r(e,t){this.id=\"number\"==typeof e.id?e.id:void 0,this.type=e.type,this.rawGeometry=1===e.type?[e.geometry]:e.geometry,this.properties=e.tags,this.extent=t||4096;}return g=i,i.prototype.feature=function(e){return new r(this.features[e],this.options.extent)},r.prototype.loadGeometry=function(){var e=this.rawGeometry;this.geometry=[];for(var o=0;o<e.length;o++){for(var i=e[o],r=[],s=0;s<i.length;s++)r.push(new t(i[s][0],i[s][1]));this.geometry.push(r);}return this.geometry},r.prototype.bbox=function(){this.geometry||this.loadGeometry();for(var e=this.geometry,t=1/0,o=-1/0,i=1/0,r=-1/0,s=0;s<e.length;s++)for(var n=e[s],a=0;a<n.length;a++){var l=n[a];t=Math.min(t,l.x),o=Math.max(o,l.x),i=Math.min(i,l.y),r=Math.max(r,l.y);}return [t,i,o,r]},r.prototype.toGeoJSON=o.prototype.toGeoJSON,g}();function i(e){var o=new t;return function(e,t){for(var o in e.layers)t.writeMessage(3,r,e.layers[o]);}(e,o),o.finish()}function r(e,t){var o;t.writeVarintField(15,e.version||1),t.writeStringField(1,e.name||\"\"),t.writeVarintField(5,e.extent||4096);var i={keys:[],values:[],keycache:{},valuecache:{}};for(o=0;o<e.length;o++)i.feature=e.feature(o),t.writeMessage(2,s,i);var r=i.keys;for(o=0;o<r.length;o++)t.writeStringField(3,r[o]);var n=i.values;for(o=0;o<n.length;o++)t.writeMessage(4,u,n[o]);}function s(e,t){var o=e.feature;void 0!==o.id&&t.writeVarintField(1,o.id),t.writeMessage(2,n,e),t.writeVarintField(3,o.type),t.writeMessage(4,c,o);}function n(e,t){var o=e.feature,i=e.keys,r=e.values,s=e.keycache,n=e.valuecache;for(var a in o.properties){var l=o.properties[a],c=s[a];if(null!==l){void 0===c&&(i.push(a),s[a]=c=i.length-1),t.writeVarint(c);var u=typeof l;\"string\"!==u&&\"boolean\"!==u&&\"number\"!==u&&(l=JSON.stringify(l));var h=u+\":\"+l,d=n[h];void 0===d&&(r.push(l),n[h]=d=r.length-1),t.writeVarint(d);}}}function a(e,t){return (t<<3)+(7&e)}function l(e){return e<<1^e>>31}function c(e,t){for(var o=e.loadGeometry(),i=e.type,r=0,s=0,n=o.length,c=0;c<n;c++){var u=o[c],h=1;1===i&&(h=u.length),t.writeVarint(a(1,h));for(var d=3===i?u.length-1:u.length,f=0;f<d;f++){1===f&&1!==i&&t.writeVarint(a(2,d-1));var g=u[f].x-r,p=u[f].y-s;t.writeVarint(l(g)),t.writeVarint(l(p)),r+=g,s+=p;}3===i&&t.writeVarint(a(7,1));}}function u(e,t){var o=typeof e;\"string\"===o?t.writeStringField(1,e):\"boolean\"===o?t.writeBooleanField(7,e):\"number\"===o&&(e%1!=0?t.writeDoubleField(3,e):e<0?t.writeSVarintField(6,e):t.writeVarintField(5,e));}return y.exports=i,y.exports.fromVectorTileJs=i,y.exports.fromGeojsonVt=function(e,t){t=t||{};var r={};for(var s in e)r[s]=new o(e[s].features,t),r[s].name=s,r[s].version=t.version,r[s].extent=t.extent;return i({layers:r})},y.exports.GeoJSONWrapper=o,y.exports}(),w=e.cP(v);const x={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:e=>e},b=Math.fround||(S=new Float32Array(1),e=>(S[0]=+e,S[0]));var S;class _{constructor(e){this.options=Object.assign(Object.create(x),e),this.trees=new Array(this.options.maxZoom+1),this.stride=this.options.reduce?7:6,this.clusterProps=[];}load(e){const{log:t,minZoom:o,maxZoom:i}=this.options;t&&console.time(\"total time\");const r=`prepare ${e.length} points`;t&&console.time(r),this.points=e;const s=[];for(let t=0;t<e.length;t++){const o=e[t];if(!o.geometry)continue;const[i,r]=o.geometry.coordinates,n=b(P(i)),a=b(k(r));s.push(n,a,1/0,t,-1,1),this.options.reduce&&s.push(0);}let n=this.trees[i+1]=this._createTree(s);t&&console.timeEnd(r);for(let e=i;e>=o;e--){const o=+Date.now();n=this.trees[e]=this._createTree(this._cluster(n,e)),t&&console.log(\"z%d: %d clusters in %dms\",e,n.numItems,+Date.now()-o);}return t&&console.timeEnd(\"total time\"),this}getClusters(e,t){let o=((e[0]+180)%360+360)%360-180;const i=Math.max(-90,Math.min(90,e[1]));let r=180===e[2]?180:((e[2]+180)%360+360)%360-180;const s=Math.max(-90,Math.min(90,e[3]));if(e[2]-e[0]>=360)o=-180,r=180;else if(o>r){const e=this.getClusters([o,i,180,s],t),n=this.getClusters([-180,i,r,s],t);return e.concat(n)}const n=this.trees[this._limitZoom(t)],a=n.range(P(o),k(s),P(r),k(i)),l=n.data,c=[];for(const e of a){const t=this.stride*e;c.push(l[t+5]>1?M(l,t,this.clusterProps):this.points[l[t+3]]);}return c}getChildren(e){const t=this._getOriginId(e),o=this._getOriginZoom(e),i=\"No cluster with the specified id.\",r=this.trees[o];if(!r)throw new Error(i);const s=r.data;if(t*this.stride>=s.length)throw new Error(i);const n=this.options.radius/(this.options.extent*Math.pow(2,o-1)),a=r.within(s[t*this.stride],s[t*this.stride+1],n),l=[];for(const t of a){const o=t*this.stride;s[o+4]===e&&l.push(s[o+5]>1?M(s,o,this.clusterProps):this.points[s[o+3]]);}if(0===l.length)throw new Error(i);return l}getLeaves(e,t,o){const i=[];return this._appendLeaves(i,e,t=t||10,o=o||0,0),i}getTile(e,t,o){const i=this.trees[this._limitZoom(e)],r=Math.pow(2,e),{extent:s,radius:n}=this.options,a=n/s,l=(o-a)/r,c=(o+1+a)/r,u={features:[]};return this._addTileFeatures(i.range((t-a)/r,l,(t+1+a)/r,c),i.data,t,o,r,u),0===t&&this._addTileFeatures(i.range(1-a/r,l,1,c),i.data,r,o,r,u),t===r-1&&this._addTileFeatures(i.range(0,l,a/r,c),i.data,-1,o,r,u),u.features.length?u:null}getClusterExpansionZoom(e){let t=this._getOriginZoom(e)-1;for(;t<=this.options.maxZoom;){const o=this.getChildren(e);if(t++,1!==o.length)break;e=o[0].properties.cluster_id;}return t}_appendLeaves(e,t,o,i,r){const s=this.getChildren(t);for(const t of s){const s=t.properties;if(s&&s.cluster?r+s.point_count<=i?r+=s.point_count:r=this._appendLeaves(e,s.cluster_id,o,i,r):r<i?r++:e.push(t),e.length===o)break}return r}_createTree(t){const o=new e.aH(t.length/this.stride|0,this.options.nodeSize,Float32Array);for(let e=0;e<t.length;e+=this.stride)o.add(t[e],t[e+1]);return o.finish(),o.data=t,o}_addTileFeatures(e,t,o,i,r,s){for(const n of e){const e=n*this.stride,a=t[e+5]>1;let l,c,u;if(a)l=I(t,e,this.clusterProps),c=t[e],u=t[e+1];else {const o=this.points[t[e+3]];l=o.properties;const[i,r]=o.geometry.coordinates;c=P(i),u=k(r);}const h={type:1,geometry:[[Math.round(this.options.extent*(c*r-o)),Math.round(this.options.extent*(u*r-i))]],tags:l};let d;d=a||this.options.generateId?t[e+3]:this.points[t[e+3]].id,void 0!==d&&(h.id=d),s.features.push(h);}}_limitZoom(e){return Math.max(this.options.minZoom,Math.min(Math.floor(+e),this.options.maxZoom+1))}_cluster(e,t){const{radius:o,extent:i,reduce:r,minPoints:s}=this.options,n=o/(i*Math.pow(2,t)),a=e.data,l=[],c=this.stride;for(let o=0;o<a.length;o+=c){if(a[o+2]<=t)continue;a[o+2]=t;const i=a[o],u=a[o+1],h=e.within(a[o],a[o+1],n),d=a[o+5];let f=d;for(const e of h){const o=e*c;a[o+2]>t&&(f+=a[o+5]);}if(f>d&&f>=s){let e,s=i*d,n=u*d,g=-1;const p=(o/c<<5)+(t+1)+this.points.length;for(const i of h){const l=i*c;if(a[l+2]<=t)continue;a[l+2]=t;const u=a[l+5];s+=a[l]*u,n+=a[l+1]*u,a[l+4]=p,r&&(e||(e=this._map(a,o,!0),g=this.clusterProps.length,this.clusterProps.push(e)),r(e,this._map(a,l)));}a[o+4]=p,l.push(s/f,n/f,1/0,p,-1,f),r&&l.push(g);}else {for(let e=0;e<c;e++)l.push(a[o+e]);if(f>1)for(const e of h){const o=e*c;if(!(a[o+2]<=t)){a[o+2]=t;for(let e=0;e<c;e++)l.push(a[o+e]);}}}}return l}_getOriginId(e){return e-this.points.length>>5}_getOriginZoom(e){return (e-this.points.length)%32}_map(e,t,o){if(e[t+5]>1){const i=this.clusterProps[e[t+6]];return o?Object.assign({},i):i}const i=this.points[e[t+3]].properties,r=this.options.map(i);return o&&r===i?Object.assign({},r):r}}function M(e,t,o){return {type:\"Feature\",id:e[t+3],properties:I(e,t,o),geometry:{type:\"Point\",coordinates:[(i=e[t],360*(i-.5)),T(e[t+1])]}};var i;}function I(e,t,o){const i=e[t+5],r=i>=1e4?`${Math.round(i/1e3)}k`:i>=1e3?Math.round(i/100)/10+\"k\":i,s=e[t+6],n=-1===s?{}:Object.assign({},o[s]);return Object.assign(n,{cluster:!0,cluster_id:e[t+3],point_count:i,point_count_abbreviated:r})}function P(e){return e/360+.5}function k(e){const t=Math.sin(e*Math.PI/180),o=.5-.25*Math.log((1+t)/(1-t))/Math.PI;return o<0?0:o>1?1:o}function T(e){const t=(180-360*e)*Math.PI/180;return 360*Math.atan(Math.exp(t))/Math.PI-90}function D(e,t,o,i){let r=i;const s=t+(o-t>>1);let n,a=o-t;const l=e[t],c=e[t+1],u=e[o],h=e[o+1];for(let i=t+3;i<o;i+=3){const t=C(e[i],e[i+1],l,c,u,h);if(t>r)n=i,r=t;else if(t===r){const e=Math.abs(i-s);e<a&&(n=i,a=e);}}r>i&&(n-t>3&&D(e,t,n,i),e[n+2]=r,o-n>3&&D(e,n,o,i));}function C(e,t,o,i,r,s){let n=r-o,a=s-i;if(0!==n||0!==a){const l=((e-o)*n+(t-i)*a)/(n*n+a*a);l>1?(o=r,i=s):l>0&&(o+=n*l,i+=a*l);}return n=e-o,a=t-i,n*n+a*a}function O(e,t,o,i){const r={id:null==e?null:e,type:t,geometry:o,tags:i,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0};if(\"Point\"===t||\"MultiPoint\"===t||\"LineString\"===t)L(r,o);else if(\"Polygon\"===t)L(r,o[0]);else if(\"MultiLineString\"===t)for(const e of o)L(r,e);else if(\"MultiPolygon\"===t)for(const e of o)L(r,e[0]);return r}function L(e,t){for(let o=0;o<t.length;o+=3)e.minX=Math.min(e.minX,t[o]),e.minY=Math.min(e.minY,t[o+1]),e.maxX=Math.max(e.maxX,t[o]),e.maxY=Math.max(e.maxY,t[o+1]);}function F(e,t,o,i){if(!t.geometry)return;const r=t.geometry.coordinates;if(r&&0===r.length)return;const s=t.geometry.type,n=Math.pow(o.tolerance/((1<<o.maxZoom)*o.extent),2);let a=[],l=t.id;if(o.promoteId?l=t.properties[o.promoteId]:o.generateId&&(l=i||0),\"Point\"===s)G(r,a);else if(\"MultiPoint\"===s)for(const e of r)G(e,a);else if(\"LineString\"===s)z(r,a,n,!1);else if(\"MultiLineString\"===s){if(o.lineMetrics){for(const o of r)a=[],z(o,a,n,!1),e.push(O(l,\"LineString\",a,t.properties));return}A(r,a,n,!1);}else if(\"Polygon\"===s)A(r,a,n,!0);else {if(\"MultiPolygon\"!==s){if(\"GeometryCollection\"===s){for(const r of t.geometry.geometries)F(e,{id:l,geometry:r,properties:t.properties},o,i);return}throw new Error(\"Input data is not a valid GeoJSON object.\")}for(const e of r){const t=[];A(e,t,n,!0),a.push(t);}}e.push(O(l,s,a,t.properties));}function G(e,t){t.push(j(e[0]),E(e[1]),0);}function z(e,t,o,i){let r,s,n=0;for(let o=0;o<e.length;o++){const a=j(e[o][0]),l=E(e[o][1]);t.push(a,l,0),o>0&&(n+=i?(r*l-a*s)/2:Math.sqrt(Math.pow(a-r,2)+Math.pow(l-s,2))),r=a,s=l;}const a=t.length-3;t[2]=1,D(t,0,a,o),t[a+2]=1,t.size=Math.abs(n),t.start=0,t.end=t.size;}function A(e,t,o,i){for(let r=0;r<e.length;r++){const s=[];z(e[r],s,o,i),t.push(s);}}function j(e){return e/360+.5}function E(e){const t=Math.sin(e*Math.PI/180),o=.5-.25*Math.log((1+t)/(1-t))/Math.PI;return o<0?0:o>1?1:o}function Z(e,t,o,i,r,s,n,a){if(i/=t,s>=(o/=t)&&n<i)return e;if(n<o||s>=i)return null;const l=[];for(const t of e){const e=t.geometry;let s=t.type;const n=0===r?t.minX:t.minY,c=0===r?t.maxX:t.maxY;if(n>=o&&c<i){l.push(t);continue}if(c<o||n>=i)continue;let u=[];if(\"Point\"===s||\"MultiPoint\"===s)N(e,u,o,i,r);else if(\"LineString\"===s)J(e,u,o,i,r,!1,a.lineMetrics);else if(\"MultiLineString\"===s)W(e,u,o,i,r,!1);else if(\"Polygon\"===s)W(e,u,o,i,r,!0);else if(\"MultiPolygon\"===s)for(const t of e){const e=[];W(t,e,o,i,r,!0),e.length&&u.push(e);}if(u.length){if(a.lineMetrics&&\"LineString\"===s){for(const e of u)l.push(O(t.id,s,e,t.tags));continue}\"LineString\"!==s&&\"MultiLineString\"!==s||(1===u.length?(s=\"LineString\",u=u[0]):s=\"MultiLineString\"),\"Point\"!==s&&\"MultiPoint\"!==s||(s=3===u.length?\"Point\":\"MultiPoint\"),l.push(O(t.id,s,u,t.tags));}}return l.length?l:null}function N(e,t,o,i,r){for(let s=0;s<e.length;s+=3){const n=e[s+r];n>=o&&n<=i&&Y(t,e[s],e[s+1],e[s+2]);}}function J(e,t,o,i,r,s,n){let a=R(e);const l=0===r?V:q;let c,u,h=e.start;for(let d=0;d<e.length-3;d+=3){const f=e[d],g=e[d+1],p=e[d+2],m=e[d+3],y=e[d+4],v=0===r?f:g,w=0===r?m:y;let x=!1;n&&(c=Math.sqrt(Math.pow(f-m,2)+Math.pow(g-y,2))),v<o?w>o&&(u=l(a,f,g,m,y,o),n&&(a.start=h+c*u)):v>i?w<i&&(u=l(a,f,g,m,y,i),n&&(a.start=h+c*u)):Y(a,f,g,p),w<o&&v>=o&&(u=l(a,f,g,m,y,o),x=!0),w>i&&v<=i&&(u=l(a,f,g,m,y,i),x=!0),!s&&x&&(n&&(a.end=h+c*u),t.push(a),a=R(e)),n&&(h+=c);}let d=e.length-3;const f=e[d],g=e[d+1],p=0===r?f:g;p>=o&&p<=i&&Y(a,f,g,e[d+2]),d=a.length-3,s&&d>=3&&(a[d]!==a[0]||a[d+1]!==a[1])&&Y(a,a[0],a[1],a[2]),a.length&&t.push(a);}function R(e){const t=[];return t.size=e.size,t.start=e.start,t.end=e.end,t}function W(e,t,o,i,r,s){for(const n of e)J(n,t,o,i,r,s,!1);}function Y(e,t,o,i){e.push(t,o,i);}function V(e,t,o,i,r,s){const n=(s-t)/(i-t);return Y(e,s,o+(r-o)*n,1),n}function q(e,t,o,i,r,s){const n=(s-o)/(r-o);return Y(e,t+(i-t)*n,s,1),n}function H(e,t){const o=[];for(let i=0;i<e.length;i++){const r=e[i],s=r.type;let n;if(\"Point\"===s||\"MultiPoint\"===s||\"LineString\"===s)n=X(r.geometry,t);else if(\"MultiLineString\"===s||\"Polygon\"===s){n=[];for(const e of r.geometry)n.push(X(e,t));}else if(\"MultiPolygon\"===s){n=[];for(const e of r.geometry){const o=[];for(const i of e)o.push(X(i,t));n.push(o);}}o.push(O(r.id,s,n,r.tags));}return o}function X(e,t){const o=[];o.size=e.size,void 0!==e.start&&(o.start=e.start,o.end=e.end);for(let i=0;i<e.length;i+=3)o.push(e[i]+t,e[i+1],e[i+2]);return o}function B(e,t){if(e.transformed)return e;const o=1<<e.z,i=e.x,r=e.y;for(const s of e.features){const e=s.geometry,n=s.type;if(s.geometry=[],1===n)for(let n=0;n<e.length;n+=2)s.geometry.push($(e[n],e[n+1],t,o,i,r));else for(let n=0;n<e.length;n++){const a=[];for(let s=0;s<e[n].length;s+=2)a.push($(e[n][s],e[n][s+1],t,o,i,r));s.geometry.push(a);}}return e.transformed=!0,e}function $(e,t,o,i,r,s){return [Math.round(o*(e*i-r)),Math.round(o*(t*i-s))]}function U(e,t,o,i,r){const s=t===r.maxZoom?0:r.tolerance/((1<<t)*r.extent),n={features:[],numPoints:0,numSimplified:0,numFeatures:e.length,source:null,x:o,y:i,z:t,transformed:!1,minX:2,minY:1,maxX:-1,maxY:0};for(const t of e)K(n,t,s,r);return n}function K(e,t,o,i){const r=t.geometry,s=t.type,n=[];if(e.minX=Math.min(e.minX,t.minX),e.minY=Math.min(e.minY,t.minY),e.maxX=Math.max(e.maxX,t.maxX),e.maxY=Math.max(e.maxY,t.maxY),\"Point\"===s||\"MultiPoint\"===s)for(let t=0;t<r.length;t+=3)n.push(r[t],r[t+1]),e.numPoints++,e.numSimplified++;else if(\"LineString\"===s)Q(n,r,e,o,!1,!1);else if(\"MultiLineString\"===s||\"Polygon\"===s)for(let t=0;t<r.length;t++)Q(n,r[t],e,o,\"Polygon\"===s,0===t);else if(\"MultiPolygon\"===s)for(let t=0;t<r.length;t++){const i=r[t];for(let t=0;t<i.length;t++)Q(n,i[t],e,o,!0,0===t);}if(n.length){let o=t.tags||null;if(\"LineString\"===s&&i.lineMetrics){o={};for(const e in t.tags)o[e]=t.tags[e];o.mapbox_clip_start=r.start/r.size,o.mapbox_clip_end=r.end/r.size;}const a={geometry:n,type:\"Polygon\"===s||\"MultiPolygon\"===s?3:\"LineString\"===s||\"MultiLineString\"===s?2:1,tags:o};null!==t.id&&(a.id=t.id),e.features.push(a);}}function Q(e,t,o,i,r,s){const n=i*i;if(i>0&&t.size<(r?n:i))return void(o.numPoints+=t.length/3);const a=[];for(let e=0;e<t.length;e+=3)(0===i||t[e+2]>n)&&(o.numSimplified++,a.push(t[e],t[e+1])),o.numPoints++;r&&function(e,t){let o=0;for(let t=0,i=e.length,r=i-2;t<i;r=t,t+=2)o+=(e[t]-e[r])*(e[t+1]+e[r+1]);if(o>0===t)for(let t=0,o=e.length;t<o/2;t+=2){const i=e[t],r=e[t+1];e[t]=e[o-2-t],e[t+1]=e[o-1-t],e[o-2-t]=i,e[o-1-t]=r;}}(a,s),e.push(a);}const ee={maxZoom:14,indexMaxZoom:5,indexMaxPoints:1e5,tolerance:3,extent:4096,buffer:64,lineMetrics:!1,promoteId:null,generateId:!1,debug:0};class te{constructor(e,t){const o=(t=this.options=function(e,t){for(const o in t)e[o]=t[o];return e}(Object.create(ee),t)).debug;if(o&&console.time(\"preprocess data\"),t.maxZoom<0||t.maxZoom>24)throw new Error(\"maxZoom should be in the 0-24 range\");if(t.promoteId&&t.generateId)throw new Error(\"promoteId and generateId cannot be used together.\");let i=function(e,t){const o=[];if(\"FeatureCollection\"===e.type)for(let i=0;i<e.features.length;i++)F(o,e.features[i],t,i);else F(o,\"Feature\"===e.type?e:{geometry:e},t);return o}(e,t);this.tiles={},this.tileCoords=[],o&&(console.timeEnd(\"preprocess data\"),console.log(\"index: maxZoom: %d, maxPoints: %d\",t.indexMaxZoom,t.indexMaxPoints),console.time(\"generate tiles\"),this.stats={},this.total=0),i=function(e,t){const o=t.buffer/t.extent;let i=e;const r=Z(e,1,-1-o,o,0,-1,2,t),s=Z(e,1,1-o,2+o,0,-1,2,t);return (r||s)&&(i=Z(e,1,-o,1+o,0,-1,2,t)||[],r&&(i=H(r,1).concat(i)),s&&(i=i.concat(H(s,-1)))),i}(i,t),i.length&&this.splitTile(i,0,0,0),o&&(i.length&&console.log(\"features: %d, points: %d\",this.tiles[0].numFeatures,this.tiles[0].numPoints),console.timeEnd(\"generate tiles\"),console.log(\"tiles generated:\",this.total,JSON.stringify(this.stats)));}splitTile(e,t,o,i,r,s,n){const a=[e,t,o,i],l=this.options,c=l.debug;for(;a.length;){i=a.pop(),o=a.pop(),t=a.pop(),e=a.pop();const u=1<<t,h=oe(t,o,i);let d=this.tiles[h];if(!d&&(c>1&&console.time(\"creation\"),d=this.tiles[h]=U(e,t,o,i,l),this.tileCoords.push({z:t,x:o,y:i}),c)){c>1&&(console.log(\"tile z%d-%d-%d (features: %d, points: %d, simplified: %d)\",t,o,i,d.numFeatures,d.numPoints,d.numSimplified),console.timeEnd(\"creation\"));const e=`z${t}`;this.stats[e]=(this.stats[e]||0)+1,this.total++;}if(d.source=e,null==r){if(t===l.indexMaxZoom||d.numPoints<=l.indexMaxPoints)continue}else {if(t===l.maxZoom||t===r)continue;if(null!=r){const e=r-t;if(o!==s>>e||i!==n>>e)continue}}if(d.source=null,0===e.length)continue;c>1&&console.time(\"clipping\");const f=.5*l.buffer/l.extent,g=.5-f,p=.5+f,m=1+f;let y=null,v=null,w=null,x=null,b=Z(e,u,o-f,o+p,0,d.minX,d.maxX,l),S=Z(e,u,o+g,o+m,0,d.minX,d.maxX,l);e=null,b&&(y=Z(b,u,i-f,i+p,1,d.minY,d.maxY,l),v=Z(b,u,i+g,i+m,1,d.minY,d.maxY,l),b=null),S&&(w=Z(S,u,i-f,i+p,1,d.minY,d.maxY,l),x=Z(S,u,i+g,i+m,1,d.minY,d.maxY,l),S=null),c>1&&console.timeEnd(\"clipping\"),a.push(y||[],t+1,2*o,2*i),a.push(v||[],t+1,2*o,2*i+1),a.push(w||[],t+1,2*o+1,2*i),a.push(x||[],t+1,2*o+1,2*i+1);}}getTile(e,t,o){e=+e,t=+t,o=+o;const i=this.options,{extent:r,debug:s}=i;if(e<0||e>24)return null;const n=1<<e,a=oe(e,t=t+n&n-1,o);if(this.tiles[a])return B(this.tiles[a],r);s>1&&console.log(\"drilling down to z%d-%d-%d\",e,t,o);let l,c=e,u=t,h=o;for(;!l&&c>0;)c--,u>>=1,h>>=1,l=this.tiles[oe(c,u,h)];return l&&l.source?(s>1&&(console.log(\"found parent tile z%d-%d-%d\",c,u,h),console.time(\"drilling down\")),this.splitTile(l.source,c,u,h,e,t,o),s>1&&console.timeEnd(\"drilling down\"),this.tiles[a]?B(this.tiles[a],r):null):null}}function oe(e,t,o){return 32*((1<<e)*o+t)+e}function ie(e,t){return t?e.properties[t]:e.id}function re(e,t){if(null==e)return !0;if(\"Feature\"===e.type)return null!=ie(e,t);if(\"FeatureCollection\"===e.type){const o=new Set;for(const i of e.features){const e=ie(i,t);if(null==e)return !1;if(o.has(e))return !1;o.add(e);}return !0}return !1}function se(e,t){const o=new Map;if(null==e);else if(\"Feature\"===e.type)o.set(ie(e,t),e);else for(const i of e.features)o.set(ie(i,t),i);return o}class ne extends s{constructor(){super(...arguments),this._dataUpdateable=new Map;}loadVectorTile(t,o){return e._(this,void 0,void 0,(function*(){const e=t.tileID.canonical;if(!this._geoJSONIndex)throw new Error(\"Unable to parse the data into a cluster or geojson\");const o=this._geoJSONIndex.getTile(e.z,e.x,e.y);if(!o)return null;const i=new f(o.features);let r=w(i);return 0===r.byteOffset&&r.byteLength===r.buffer.byteLength||(r=new Uint8Array(r)),{vectorTile:i,rawData:r.buffer}}))}loadData(t){return e._(this,void 0,void 0,(function*(){var o;null===(o=this._pendingRequest)||void 0===o||o.abort();const i=!!(t&&t.request&&t.request.collectResourceTiming)&&new e.cM(t.request);this._pendingRequest=new AbortController;try{this._pendingData=this.loadAndProcessGeoJSON(t,this._pendingRequest),this._geoJSONIndex=t.cluster?new _(function({superclusterOptions:t,clusterProperties:o}){if(!o||!t)return t;const i={},r={},s={accumulated:null,zoom:0},n={properties:null},a=Object.keys(o);for(const t of a){const[s,n]=o[t],a=e.cT(n),l=e.cT(\"string\"==typeof s?[s,[\"accumulated\"],[\"get\",t]]:s);i[t]=a.value,r[t]=l.value;}return t.map=e=>{n.properties=e;const t={};for(const e of a)t[e]=i[e].evaluate(s,n);return t},t.reduce=(e,t)=>{n.properties=t;for(const t of a)s.accumulated=e[t],e[t]=r[t].evaluate(s,n);},t}(t)).load((yield this._pendingData).features):(r=yield this._pendingData,new te(r,t.geojsonVtOptions)),this.loaded={};const o={};if(i){const e=i.finish();e&&(o.resourceTiming={},o.resourceTiming[t.source]=JSON.parse(JSON.stringify(e)));}return o}catch(t){if(delete this._pendingRequest,e.cx(t))return {abandoned:!0};throw t}var r;}))}getData(){return e._(this,void 0,void 0,(function*(){return this._pendingData}))}reloadTile(e){const t=this.loaded;return t&&t[e.uid]?super.reloadTile(e):this.loadTile(e)}loadAndProcessGeoJSON(t,o){return e._(this,void 0,void 0,(function*(){let i=yield this.loadGeoJSON(t,o);if(delete this._pendingRequest,\"object\"!=typeof i)throw new Error(`Input data given to '${t.source}' is not a valid GeoJSON object.`);if(u(i,!0),t.filter){const o=e.cT(t.filter,{type:\"boolean\",\"property-type\":\"data-driven\",overridable:!1,transition:!1});if(\"error\"===o.result)throw new Error(o.value.map((e=>`${e.key}: ${e.message}`)).join(\", \"));const r=i.features.filter((e=>o.value.evaluate({zoom:0},e)));i={type:\"FeatureCollection\",features:r};}return i}))}loadGeoJSON(t,o){return e._(this,void 0,void 0,(function*(){const{promoteId:i}=t;if(t.request){const r=yield e.j(t.request,o);return this._dataUpdateable=re(r.data,i)?se(r.data,i):void 0,r.data}if(\"string\"==typeof t.data)try{const e=JSON.parse(t.data);return this._dataUpdateable=re(e,i)?se(e,i):void 0,e}catch(e){throw new Error(`Input data given to '${t.source}' is not a valid GeoJSON object.`)}if(!t.dataDiff)throw new Error(`Input data given to '${t.source}' is not a valid GeoJSON object.`);if(!this._dataUpdateable)throw new Error(`Cannot update existing geojson data in ${t.source}`);return function(e,t,o){var i,r,s,n;if(t.removeAll&&e.clear(),t.remove)for(const o of t.remove)e.delete(o);if(t.add)for(const i of t.add){const t=ie(i,o);null!=t&&e.set(t,i);}if(t.update)for(const o of t.update){let t=e.get(o.id);if(null==t)continue;const a=!o.removeAllProperties&&((null===(i=o.removeProperties)||void 0===i?void 0:i.length)>0||(null===(r=o.addOrUpdateProperties)||void 0===r?void 0:r.length)>0);if((o.newGeometry||o.removeAllProperties||a)&&(t=Object.assign({},t),e.set(o.id,t),a&&(t.properties=Object.assign({},t.properties))),o.newGeometry&&(t.geometry=o.newGeometry),o.removeAllProperties)t.properties={};else if((null===(s=o.removeProperties)||void 0===s?void 0:s.length)>0)for(const e of o.removeProperties)Object.prototype.hasOwnProperty.call(t.properties,e)&&delete t.properties[e];if((null===(n=o.addOrUpdateProperties)||void 0===n?void 0:n.length)>0)for(const{key:e,value:i}of o.addOrUpdateProperties)t.properties[e]=i;}}(this._dataUpdateable,t.dataDiff,i),{type:\"FeatureCollection\",features:Array.from(this._dataUpdateable.values())}}))}removeSource(t){return e._(this,void 0,void 0,(function*(){this._pendingRequest&&this._pendingRequest.abort();}))}getClusterExpansionZoom(e){return this._geoJSONIndex.getClusterExpansionZoom(e.clusterId)}getClusterChildren(e){return this._geoJSONIndex.getChildren(e.clusterId)}getClusterLeaves(e){return this._geoJSONIndex.getLeaves(e.clusterId,e.limit,e.offset)}}class ae{constructor(t){this.self=t,this.actor=new e.J(t),this.layerIndexes={},this.availableImages={},this.workerSources={},this.demWorkerSources={},this.externalWorkerSourceTypes={},this.self.registerWorkerSource=(e,t)=>{if(this.externalWorkerSourceTypes[e])throw new Error(`Worker source with name \"${e}\" already registered.`);this.externalWorkerSourceTypes[e]=t;},this.self.addProtocol=e.cz,this.self.removeProtocol=e.cA,this.self.registerRTLTextPlugin=t=>{e.cU.setMethods(t);},this.actor.registerMessageHandler(\"LDT\",((e,t)=>this._getDEMWorkerSource(e,t.source).loadTile(t))),this.actor.registerMessageHandler(\"RDT\",((t,o)=>e._(this,void 0,void 0,(function*(){this._getDEMWorkerSource(t,o.source).removeTile(o);})))),this.actor.registerMessageHandler(\"GCEZ\",((t,o)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,o.type,o.source).getClusterExpansionZoom(o)})))),this.actor.registerMessageHandler(\"GCC\",((t,o)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,o.type,o.source).getClusterChildren(o)})))),this.actor.registerMessageHandler(\"GCL\",((t,o)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,o.type,o.source).getClusterLeaves(o)})))),this.actor.registerMessageHandler(\"LD\",((e,t)=>this._getWorkerSource(e,t.type,t.source).loadData(t))),this.actor.registerMessageHandler(\"GD\",((e,t)=>this._getWorkerSource(e,t.type,t.source).getData())),this.actor.registerMessageHandler(\"LT\",((e,t)=>this._getWorkerSource(e,t.type,t.source).loadTile(t))),this.actor.registerMessageHandler(\"RT\",((e,t)=>this._getWorkerSource(e,t.type,t.source).reloadTile(t))),this.actor.registerMessageHandler(\"AT\",((e,t)=>this._getWorkerSource(e,t.type,t.source).abortTile(t))),this.actor.registerMessageHandler(\"RMT\",((e,t)=>this._getWorkerSource(e,t.type,t.source).removeTile(t))),this.actor.registerMessageHandler(\"RS\",((t,o)=>e._(this,void 0,void 0,(function*(){if(!this.workerSources[t]||!this.workerSources[t][o.type]||!this.workerSources[t][o.type][o.source])return;const e=this.workerSources[t][o.type][o.source];delete this.workerSources[t][o.type][o.source],void 0!==e.removeSource&&e.removeSource(o);})))),this.actor.registerMessageHandler(\"RM\",(t=>e._(this,void 0,void 0,(function*(){delete this.layerIndexes[t],delete this.availableImages[t],delete this.workerSources[t],delete this.demWorkerSources[t];})))),this.actor.registerMessageHandler(\"SR\",((t,o)=>e._(this,void 0,void 0,(function*(){this.referrer=o;})))),this.actor.registerMessageHandler(\"SRPS\",((e,t)=>this._syncRTLPluginState(e,t))),this.actor.registerMessageHandler(\"IS\",((t,o)=>e._(this,void 0,void 0,(function*(){this.self.importScripts(o);})))),this.actor.registerMessageHandler(\"SI\",((e,t)=>this._setImages(e,t))),this.actor.registerMessageHandler(\"UL\",((t,o)=>e._(this,void 0,void 0,(function*(){this._getLayerIndex(t).update(o.layers,o.removedIds);})))),this.actor.registerMessageHandler(\"SL\",((t,o)=>e._(this,void 0,void 0,(function*(){this._getLayerIndex(t).replace(o);}))));}_setImages(t,o){return e._(this,void 0,void 0,(function*(){this.availableImages[t]=o;for(const e in this.workerSources[t]){const i=this.workerSources[t][e];for(const e in i)i[e].availableImages=o;}}))}_syncRTLPluginState(t,o){return e._(this,void 0,void 0,(function*(){return yield e.cU.syncState(o,this.self.importScripts)}))}_getAvailableImages(e){let t=this.availableImages[e];return t||(t=[]),t}_getLayerIndex(e){let o=this.layerIndexes[e];return o||(o=this.layerIndexes[e]=new t),o}_getWorkerSource(e,t,o){if(this.workerSources[e]||(this.workerSources[e]={}),this.workerSources[e][t]||(this.workerSources[e][t]={}),!this.workerSources[e][t][o]){const i={sendAsync:(t,o)=>(t.targetMapId=e,this.actor.sendAsync(t,o))};switch(t){case \"vector\":this.workerSources[e][t][o]=new s(i,this._getLayerIndex(e),this._getAvailableImages(e));break;case \"geojson\":this.workerSources[e][t][o]=new ne(i,this._getLayerIndex(e),this._getAvailableImages(e));break;default:this.workerSources[e][t][o]=new this.externalWorkerSourceTypes[t](i,this._getLayerIndex(e),this._getAvailableImages(e));}}return this.workerSources[e][t][o]}_getDEMWorkerSource(e,t){return this.demWorkerSources[e]||(this.demWorkerSources[e]={}),this.demWorkerSources[e][t]||(this.demWorkerSources[e][t]=new n),this.demWorkerSources[e][t]}}return e.i(self)&&(self.worker=new ae(self)),ae}));\r\n\r\ndefine(\"index\",[\"exports\",\"./shared\"],(function(e,t){\"use strict\";var i=\"5.6.0\";function o(){var e=new t.A(4);return t.A!=Float32Array&&(e[1]=0,e[2]=0),e[0]=1,e[3]=1,e}let r,a;const s={now:\"undefined\"!=typeof performance&&performance&&performance.now?performance.now.bind(performance):Date.now.bind(Date),frame(e,i,o){const r=requestAnimationFrame((e=>{a(),i(e);})),{unsubscribe:a}=t.s(e.signal,\"abort\",(()=>{a(),cancelAnimationFrame(r),o(t.c());}),!1);},frameAsync(e){return new Promise(((t,i)=>{this.frame(e,t,i);}))},getImageData(e,t=0){return this.getImageCanvasContext(e).getImageData(-t,-t,e.width+2*t,e.height+2*t)},getImageCanvasContext(e){const t=window.document.createElement(\"canvas\"),i=t.getContext(\"2d\",{willReadFrequently:!0});if(!i)throw new Error(\"failed to create canvas 2d context\");return t.width=e.width,t.height=e.height,i.drawImage(e,0,0,e.width,e.height),i},resolveURL:e=>(r||(r=document.createElement(\"a\")),r.href=e,r.href),hardwareConcurrency:\"undefined\"!=typeof navigator&&navigator.hardwareConcurrency||4,get prefersReducedMotion(){return !!matchMedia&&(null==a&&(a=matchMedia(\"(prefers-reduced-motion: reduce)\")),a.matches)}};class n{static testProp(e){if(!n.docStyle)return e[0];for(let t=0;t<e.length;t++)if(e[t]in n.docStyle)return e[t];return e[0]}static create(e,t,i){const o=window.document.createElement(e);return void 0!==t&&(o.className=t),i&&i.appendChild(o),o}static createNS(e,t){return window.document.createElementNS(e,t)}static disableDrag(){n.docStyle&&n.selectProp&&(n.userSelect=n.docStyle[n.selectProp],n.docStyle[n.selectProp]=\"none\");}static enableDrag(){n.docStyle&&n.selectProp&&(n.docStyle[n.selectProp]=n.userSelect);}static setTransform(e,t){e.style[n.transformProp]=t;}static addEventListener(e,t,i,o={}){e.addEventListener(t,i,\"passive\"in o?o:o.capture);}static removeEventListener(e,t,i,o={}){e.removeEventListener(t,i,\"passive\"in o?o:o.capture);}static suppressClickInternal(e){e.preventDefault(),e.stopPropagation(),window.removeEventListener(\"click\",n.suppressClickInternal,!0);}static suppressClick(){window.addEventListener(\"click\",n.suppressClickInternal,!0),window.setTimeout((()=>{window.removeEventListener(\"click\",n.suppressClickInternal,!0);}),0);}static getScale(e){const t=e.getBoundingClientRect();return {x:t.width/e.offsetWidth||1,y:t.height/e.offsetHeight||1,boundingClientRect:t}}static getPoint(e,i,o){const r=i.boundingClientRect;return new t.P((o.clientX-r.left)/i.x-e.clientLeft,(o.clientY-r.top)/i.y-e.clientTop)}static mousePos(e,t){const i=n.getScale(e);return n.getPoint(e,i,t)}static touchPos(e,t){const i=[],o=n.getScale(e);for(let r=0;r<t.length;r++)i.push(n.getPoint(e,o,t[r]));return i}static mouseButton(e){return e.button}static remove(e){e.parentNode&&e.parentNode.removeChild(e);}static sanitize(e){const t=(new DOMParser).parseFromString(e,\"text/html\").body||document.createElement(\"body\"),i=t.querySelectorAll(\"script\");for(const e of i)e.remove();return n.clean(t),t.innerHTML}static isPossiblyDangerous(e,t){const i=t.replace(/\\s+/g,\"\").toLowerCase();return !(![\"src\",\"href\",\"xlink:href\"].includes(e)||!i.includes(\"javascript:\")&&!i.includes(\"data:\"))||!!e.startsWith(\"on\")||void 0}static clean(e){const t=e.children;for(const e of t)n.removeAttributes(e),n.clean(e);}static removeAttributes(e){for(const{name:t,value:i}of e.attributes)n.isPossiblyDangerous(t,i)&&e.removeAttribute(t);}}n.docStyle=\"undefined\"!=typeof window&&window.document&&window.document.documentElement.style,n.selectProp=n.testProp([\"userSelect\",\"MozUserSelect\",\"WebkitUserSelect\",\"msUserSelect\"]),n.transformProp=n.testProp([\"transform\",\"WebkitTransform\"]);const l={supported:!1,testSupport:function(e){!u&&h&&(d?_(e):c=e);}};let c,h,u=!1,d=!1;function _(e){const t=e.createTexture();e.bindTexture(e.TEXTURE_2D,t);try{if(e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,h),e.isContextLost())return;l.supported=!0;}catch(e){}e.deleteTexture(t),u=!0;}var p;\"undefined\"!=typeof document&&(h=document.createElement(\"img\"),h.onload=()=>{c&&_(c),c=null,d=!0;},h.onerror=()=>{u=!0,c=null;},h.src=\"data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=\"),function(e){let i,o,r,a;e.resetRequestQueue=()=>{i=[],o=0,r=0,a={};},e.addThrottleControl=e=>{const t=r++;return a[t]=e,t},e.removeThrottleControl=e=>{delete a[e],n();},e.getImage=(e,o,r=!0)=>new Promise(((a,s)=>{l.supported&&(e.headers||(e.headers={}),e.headers.accept=\"image/webp,*/*\"),t.e(e,{type:\"image\"}),i.push({abortController:o,requestParameters:e,supportImageRefresh:r,state:\"queued\",onError:e=>{s(e);},onSuccess:e=>{a(e);}}),n();}));const s=e=>t._(this,void 0,void 0,(function*(){e.state=\"running\";const{requestParameters:i,supportImageRefresh:r,onError:a,onSuccess:s,abortController:l}=e,h=!1===r&&!t.i(self)&&!t.g(i.url)&&(!i.headers||Object.keys(i.headers).reduce(((e,t)=>e&&\"accept\"===t),!0));o++;const u=h?c(i,l):t.m(i,l);try{const i=yield u;delete e.abortController,e.state=\"completed\",i.data instanceof HTMLImageElement||t.b(i.data)?s(i):i.data&&s({data:yield(d=i.data,\"function\"==typeof createImageBitmap?t.f(d):t.h(d)),cacheControl:i.cacheControl,expires:i.expires});}catch(t){delete e.abortController,a(t);}finally{o--,n();}var d;})),n=()=>{const e=(()=>{for(const e of Object.keys(a))if(a[e]())return !0;return !1})()?t.a.MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME:t.a.MAX_PARALLEL_IMAGE_REQUESTS;for(let t=o;t<e&&i.length>0;t++){const e=i.shift();e.abortController.signal.aborted?t--:s(e);}},c=(e,i)=>new Promise(((o,r)=>{const a=new Image,s=e.url,n=e.credentials;n&&\"include\"===n?a.crossOrigin=\"use-credentials\":(n&&\"same-origin\"===n||!t.d(s))&&(a.crossOrigin=\"anonymous\"),i.signal.addEventListener(\"abort\",(()=>{a.src=\"\",r(t.c());})),a.fetchPriority=\"high\",a.onload=()=>{a.onerror=a.onload=null,o({data:a});},a.onerror=()=>{a.onerror=a.onload=null,i.signal.aborted||r(new Error(\"Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.\"));},a.src=s;}));}(p||(p={})),p.resetRequestQueue();class m{constructor(e){this._transformRequestFn=e;}transformRequest(e,t){return this._transformRequestFn&&this._transformRequestFn(e,t)||{url:e}}setTransformRequest(e){this._transformRequestFn=e;}}function f(e){const t=[];if(\"string\"==typeof e)t.push({id:\"default\",url:e});else if(e&&e.length>0){const i=[];for(const{id:o,url:r}of e){const e=`${o}${r}`;-1===i.indexOf(e)&&(i.push(e),t.push({id:o,url:r}));}}return t}function g(e,t,i){try{const o=new URL(e);return o.pathname+=`${t}${i}`,o.toString()}catch(t){throw new Error(`Invalid sprite URL \"${e}\", must be absolute. Modify style specification directly or use TransformStyleFunction to correct the issue dynamically`)}}function v(e){const{userImage:t}=e;return !!(t&&t.render&&t.render())&&(e.data.replace(new Uint8Array(t.data.buffer)),!0)}class b extends t.E{constructor(){super(),this.images={},this.updatedImages={},this.callbackDispatchedThisFrame={},this.loaded=!1,this.requestors=[],this.patterns={},this.atlasImage=new t.R({width:1,height:1}),this.dirty=!0;}isLoaded(){return this.loaded}setLoaded(e){if(this.loaded!==e&&(this.loaded=e,e)){for(const{ids:e,promiseResolve:t}of this.requestors)t(this._getImagesForIds(e));this.requestors=[];}}getImage(e){const i=this.images[e];if(i&&!i.data&&i.spriteData){const e=i.spriteData;i.data=new t.R({width:e.width,height:e.height},e.context.getImageData(e.x,e.y,e.width,e.height).data),i.spriteData=null;}return i}addImage(e,t){if(this.images[e])throw new Error(`Image id ${e} already exist, use updateImage instead`);this._validate(e,t)&&(this.images[e]=t);}_validate(e,i){let o=!0;const r=i.data||i.spriteData;return this._validateStretch(i.stretchX,r&&r.width)||(this.fire(new t.k(new Error(`Image \"${e}\" has invalid \"stretchX\" value`))),o=!1),this._validateStretch(i.stretchY,r&&r.height)||(this.fire(new t.k(new Error(`Image \"${e}\" has invalid \"stretchY\" value`))),o=!1),this._validateContent(i.content,i)||(this.fire(new t.k(new Error(`Image \"${e}\" has invalid \"content\" value`))),o=!1),o}_validateStretch(e,t){if(!e)return !0;let i=0;for(const o of e){if(o[0]<i||o[1]<o[0]||t<o[1])return !1;i=o[1];}return !0}_validateContent(e,t){if(!e)return !0;if(4!==e.length)return !1;const i=t.spriteData,o=i&&i.width||t.data.width,r=i&&i.height||t.data.height;return !(e[0]<0||o<e[0]||e[1]<0||r<e[1]||e[2]<0||o<e[2]||e[3]<0||r<e[3]||e[2]<e[0]||e[3]<e[1])}updateImage(e,t,i=!0){const o=this.getImage(e);if(i&&(o.data.width!==t.data.width||o.data.height!==t.data.height))throw new Error(`size mismatch between old image (${o.data.width}x${o.data.height}) and new image (${t.data.width}x${t.data.height}).`);t.version=o.version+1,this.images[e]=t,this.updatedImages[e]=!0;}removeImage(e){const t=this.images[e];delete this.images[e],delete this.patterns[e],t.userImage&&t.userImage.onRemove&&t.userImage.onRemove();}listImages(){return Object.keys(this.images)}getImages(e){return new Promise(((t,i)=>{let o=!0;if(!this.isLoaded())for(const t of e)this.images[t]||(o=!1);this.isLoaded()||o?t(this._getImagesForIds(e)):this.requestors.push({ids:e,promiseResolve:t});}))}_getImagesForIds(e){const i={};for(const o of e){let e=this.getImage(o);e||(this.fire(new t.l(\"styleimagemissing\",{id:o})),e=this.getImage(o)),e?i[o]={data:e.data.clone(),pixelRatio:e.pixelRatio,sdf:e.sdf,version:e.version,stretchX:e.stretchX,stretchY:e.stretchY,content:e.content,textFitWidth:e.textFitWidth,textFitHeight:e.textFitHeight,hasRenderCallback:Boolean(e.userImage&&e.userImage.render)}:t.w(`Image \"${o}\" could not be loaded. Please make sure you have added the image with map.addImage() or a \"sprite\" property in your style. You can provide missing images by listening for the \"styleimagemissing\" map event.`);}return i}getPixelSize(){const{width:e,height:t}=this.atlasImage;return {width:e,height:t}}getPattern(e){const i=this.patterns[e],o=this.getImage(e);if(!o)return null;if(i&&i.position.version===o.version)return i.position;if(i)i.position.version=o.version;else {const i={w:o.data.width+2,h:o.data.height+2,x:0,y:0},r=new t.I(i,o);this.patterns[e]={bin:i,position:r};}return this._updatePatternAtlas(),this.patterns[e].position}bind(e){const i=e.gl;this.atlasTexture?this.dirty&&(this.atlasTexture.update(this.atlasImage),this.dirty=!1):this.atlasTexture=new t.T(e,this.atlasImage,i.RGBA),this.atlasTexture.bind(i.LINEAR,i.CLAMP_TO_EDGE);}_updatePatternAtlas(){const e=[];for(const t in this.patterns)e.push(this.patterns[t].bin);const{w:i,h:o}=t.p(e),r=this.atlasImage;r.resize({width:i||1,height:o||1});for(const e in this.patterns){const{bin:i}=this.patterns[e],o=i.x+1,a=i.y+1,s=this.getImage(e).data,n=s.width,l=s.height;t.R.copy(s,r,{x:0,y:0},{x:o,y:a},{width:n,height:l}),t.R.copy(s,r,{x:0,y:l-1},{x:o,y:a-1},{width:n,height:1}),t.R.copy(s,r,{x:0,y:0},{x:o,y:a+l},{width:n,height:1}),t.R.copy(s,r,{x:n-1,y:0},{x:o-1,y:a},{width:1,height:l}),t.R.copy(s,r,{x:0,y:0},{x:o+n,y:a},{width:1,height:l});}this.dirty=!0;}beginFrame(){this.callbackDispatchedThisFrame={};}dispatchRenderCallbacks(e){for(const i of e){if(this.callbackDispatchedThisFrame[i])continue;this.callbackDispatchedThisFrame[i]=!0;const e=this.getImage(i);e||t.w(`Image with ID: \"${i}\" was not found`),v(e)&&this.updateImage(i,e);}}}const x=1e20;function y(e,t,i,o,r,a,s,n,l){for(let c=t;c<t+o;c++)w(e,i*a+c,a,r,s,n,l);for(let c=i;c<i+r;c++)w(e,c*a+t,1,o,s,n,l);}function w(e,t,i,o,r,a,s){a[0]=0,s[0]=-1e20,s[1]=x,r[0]=e[t];for(let n=1,l=0,c=0;n<o;n++){r[n]=e[t+n*i];const o=n*n;do{const e=a[l];c=(r[n]-r[e]+o-e*e)/(n-e)/2;}while(c<=s[l]&&--l>-1);l++,a[l]=n,s[l]=c,s[l+1]=x;}for(let n=0,l=0;n<o;n++){for(;s[l+1]<n;)l++;const o=a[l],c=n-o;e[t+n*i]=r[o]+c*c;}}class T{constructor(e,t){this.requestManager=e,this.localIdeographFontFamily=t,this.entries={};}setURL(e){this.url=e;}getGlyphs(e){return t._(this,void 0,void 0,(function*(){const t=[];for(const i in e)for(const o of e[i])t.push(this._getAndCacheGlyphsPromise(i,o));const i=yield Promise.all(t),o={};for(const{stack:e,id:t,glyph:r}of i)o[e]||(o[e]={}),o[e][t]=r&&{id:r.id,bitmap:r.bitmap.clone(),metrics:r.metrics};return o}))}_getAndCacheGlyphsPromise(e,i){return t._(this,void 0,void 0,(function*(){let t=this.entries[e];t||(t=this.entries[e]={glyphs:{},requests:{},ranges:{}});let o=t.glyphs[i];if(void 0!==o)return {stack:e,id:i,glyph:o};if(o=this._tinySDF(t,e,i),o)return t.glyphs[i]=o,{stack:e,id:i,glyph:o};const r=Math.floor(i/256);if(256*r>65535)throw new Error(\"glyphs > 65535 not supported\");if(t.ranges[r])return {stack:e,id:i,glyph:o};if(!this.url)throw new Error(\"glyphsUrl is not set\");if(!t.requests[r]){const i=T.loadGlyphRange(e,r,this.url,this.requestManager);t.requests[r]=i;}const a=yield t.requests[r];for(const e in a)this._doesCharSupportLocalGlyph(+e)||(t.glyphs[+e]=a[+e]);return t.ranges[r]=!0,{stack:e,id:i,glyph:a[i]||null}}))}_doesCharSupportLocalGlyph(e){return !!this.localIdeographFontFamily&&(/\\p{Ideo}|\\p{sc=Hang}|\\p{sc=Hira}|\\p{sc=Kana}/u.test(String.fromCodePoint(e))||t.u[\"CJK Unified Ideographs\"](e)||t.u[\"Hangul Syllables\"](e)||t.u.Hiragana(e)||t.u.Katakana(e)||t.u[\"CJK Symbols and Punctuation\"](e)||t.u[\"Halfwidth and Fullwidth Forms\"](e))}_tinySDF(e,i,o){const r=this.localIdeographFontFamily;if(!r)return;if(!this._doesCharSupportLocalGlyph(o))return;let a=e.tinySDF;if(!a){let t=\"400\";/bold/i.test(i)?t=\"900\":/medium/i.test(i)?t=\"500\":/light/i.test(i)&&(t=\"200\"),a=e.tinySDF=new T.TinySDF({fontSize:48,buffer:6,radius:16,cutoff:.25,fontFamily:r,fontWeight:t});}const s=a.draw(String.fromCharCode(o));return {id:o,bitmap:new t.q({width:s.width||60,height:s.height||60},s.data),metrics:{width:s.glyphWidth/2||24,height:s.glyphHeight/2||24,left:s.glyphLeft/2+.5||0,top:s.glyphTop/2-27.5||-8,advance:s.glyphAdvance/2||24,isDoubleResolution:!0}}}}T.loadGlyphRange=function(e,i,o,r){return t._(this,void 0,void 0,(function*(){const a=256*i,s=a+255,n=r.transformRequest(o.replace(\"{fontstack}\",e).replace(\"{range}\",`${a}-${s}`),\"Glyphs\"),l=yield t.n(n,new AbortController);if(!l||!l.data)throw new Error(`Could not load glyph range. range: ${i}, ${a}-${s}`);const c={};for(const e of t.o(l.data))c[e.id]=e;return c}))},T.TinySDF=class{constructor({fontSize:e=24,buffer:t=3,radius:i=8,cutoff:o=.25,fontFamily:r=\"sans-serif\",fontWeight:a=\"normal\",fontStyle:s=\"normal\"}={}){this.buffer=t,this.cutoff=o,this.radius=i;const n=this.size=e+4*t,l=this._createCanvas(n),c=this.ctx=l.getContext(\"2d\",{willReadFrequently:!0});c.font=`${s} ${a} ${e}px ${r}`,c.textBaseline=\"alphabetic\",c.textAlign=\"left\",c.fillStyle=\"black\",this.gridOuter=new Float64Array(n*n),this.gridInner=new Float64Array(n*n),this.f=new Float64Array(n),this.z=new Float64Array(n+1),this.v=new Uint16Array(n);}_createCanvas(e){const t=document.createElement(\"canvas\");return t.width=t.height=e,t}draw(e){const{width:t,actualBoundingBoxAscent:i,actualBoundingBoxDescent:o,actualBoundingBoxLeft:r,actualBoundingBoxRight:a}=this.ctx.measureText(e),s=Math.ceil(i),n=Math.max(0,Math.min(this.size-this.buffer,Math.ceil(a-r))),l=Math.min(this.size-this.buffer,s+Math.ceil(o)),c=n+2*this.buffer,h=l+2*this.buffer,u=Math.max(c*h,0),d=new Uint8ClampedArray(u),_={data:d,width:c,height:h,glyphWidth:n,glyphHeight:l,glyphTop:s,glyphLeft:0,glyphAdvance:t};if(0===n||0===l)return _;const{ctx:p,buffer:m,gridInner:f,gridOuter:g}=this;p.clearRect(m,m,n,l),p.fillText(e,m,m+s);const v=p.getImageData(m,m,n,l);g.fill(x,0,u),f.fill(0,0,u);for(let e=0;e<l;e++)for(let t=0;t<n;t++){const i=v.data[4*(e*n+t)+3]/255;if(0===i)continue;const o=(e+m)*c+t+m;if(1===i)g[o]=0,f[o]=x;else {const e=.5-i;g[o]=e>0?e*e:0,f[o]=e<0?e*e:0;}}y(g,0,0,c,h,c,this.f,this.v,this.z),y(f,m,m,n,l,c,this.f,this.v,this.z);for(let e=0;e<u;e++){const t=Math.sqrt(g[e])-Math.sqrt(f[e]);d[e]=Math.round(255-255*(t/this.radius+this.cutoff));}return _}};class P{constructor(){this.specification=t.v.light.position;}possiblyEvaluate(e,i){return t.B(e.expression.evaluate(i))}interpolate(e,i,o){return {x:t.C.number(e.x,i.x,o),y:t.C.number(e.y,i.y,o),z:t.C.number(e.z,i.z,o)}}}let C;class I extends t.E{constructor(e){super(),C=C||new t.r({anchor:new t.D(t.v.light.anchor),position:new P,color:new t.D(t.v.light.color),intensity:new t.D(t.v.light.intensity)}),this._transitionable=new t.t(C),this.setLight(e),this._transitioning=this._transitionable.untransitioned();}getLight(){return this._transitionable.serialize()}setLight(e,i={}){if(!this._validate(t.x,e,i))for(const t in e){const i=e[t];t.endsWith(\"-transition\")?this._transitionable.setTransition(t.slice(0,-11),i):this._transitionable.setValue(t,i);}}updateTransitions(e){this._transitioning=this._transitionable.transitioned(e,this._transitioning);}hasTransition(){return this._transitioning.hasTransition()}recalculate(e){this.properties=this._transitioning.possiblyEvaluate(e);}_validate(e,i,o){return (!o||!1!==o.validate)&&t.y(this,e.call(t.z,{value:i,style:{glyphs:!0,sprite:!0},styleSpec:t.v}))}}const M=new t.r({\"sky-color\":new t.D(t.v.sky[\"sky-color\"]),\"horizon-color\":new t.D(t.v.sky[\"horizon-color\"]),\"fog-color\":new t.D(t.v.sky[\"fog-color\"]),\"fog-ground-blend\":new t.D(t.v.sky[\"fog-ground-blend\"]),\"horizon-fog-blend\":new t.D(t.v.sky[\"horizon-fog-blend\"]),\"sky-horizon-blend\":new t.D(t.v.sky[\"sky-horizon-blend\"]),\"atmosphere-blend\":new t.D(t.v.sky[\"atmosphere-blend\"])});class S extends t.E{constructor(e){super(),this._transitionable=new t.t(M),this.setSky(e),this._transitioning=this._transitionable.untransitioned(),this.recalculate(new t.F(0));}setSky(e,i={}){if(!this._validate(t.G,e,i)){e||(e={\"sky-color\":\"transparent\",\"horizon-color\":\"transparent\",\"fog-color\":\"transparent\",\"fog-ground-blend\":1,\"atmosphere-blend\":0});for(const t in e){const i=e[t];t.endsWith(\"-transition\")?this._transitionable.setTransition(t.slice(0,-11),i):this._transitionable.setValue(t,i);}}}getSky(){return this._transitionable.serialize()}updateTransitions(e){this._transitioning=this._transitionable.transitioned(e,this._transitioning);}hasTransition(){return this._transitioning.hasTransition()}recalculate(e){this.properties=this._transitioning.possiblyEvaluate(e);}_validate(e,i,o={}){return !1!==(null==o?void 0:o.validate)&&t.y(this,e.call(t.z,t.e({value:i,style:{glyphs:!0,sprite:!0},styleSpec:t.v})))}calculateFogBlendOpacity(e){return e<60?0:e<70?(e-60)/10:1}}class E{constructor(e,t){this.width=e,this.height=t,this.nextRow=0,this.data=new Uint8Array(this.width*this.height),this.dashEntry={};}getDash(e,t){const i=e.join(\",\")+String(t);return this.dashEntry[i]||(this.dashEntry[i]=this.addDash(e,t)),this.dashEntry[i]}getDashRanges(e,t,i){const o=[];let r=e.length%2==1?-e[e.length-1]*i:0,a=e[0]*i,s=!0;o.push({left:r,right:a,isDash:s,zeroLength:0===e[0]});let n=e[0];for(let t=1;t<e.length;t++){s=!s;const l=e[t];r=n*i,n+=l,a=n*i,o.push({left:r,right:a,isDash:s,zeroLength:0===l});}return o}addRoundDash(e,t,i){const o=t/2;for(let t=-i;t<=i;t++){const r=this.width*(this.nextRow+i+t);let a=0,s=e[a];for(let n=0;n<this.width;n++){n/s.right>1&&(s=e[++a]);const l=Math.abs(n-s.left),c=Math.abs(n-s.right),h=Math.min(l,c);let u;const d=t/i*(o+1);if(s.isDash){const e=o-Math.abs(d);u=Math.sqrt(h*h+e*e);}else u=o-Math.sqrt(h*h+d*d);this.data[r+n]=Math.max(0,Math.min(255,u+128));}}}addRegularDash(e){for(let t=e.length-1;t>=0;--t){const i=e[t],o=e[t+1];i.zeroLength?e.splice(t,1):o&&o.isDash===i.isDash&&(o.left=i.left,e.splice(t,1));}const t=e[0],i=e[e.length-1];t.isDash===i.isDash&&(t.left=i.left-this.width,i.right=t.right+this.width);const o=this.width*this.nextRow;let r=0,a=e[r];for(let t=0;t<this.width;t++){t/a.right>1&&(a=e[++r]);const i=Math.abs(t-a.left),s=Math.abs(t-a.right),n=Math.min(i,s);this.data[o+t]=Math.max(0,Math.min(255,(a.isDash?n:-n)+128));}}addDash(e,i){const o=i?7:0,r=2*o+1;if(this.nextRow+r>this.height)return t.w(\"LineAtlas out of space\"),null;let a=0;for(let t=0;t<e.length;t++)a+=e[t];if(0!==a){const t=this.width/a,r=this.getDashRanges(e,this.width,t);i?this.addRoundDash(r,t,o):this.addRegularDash(r);}const s={y:(this.nextRow+o+.5)/this.height,height:2*o/this.height,width:a};return this.nextRow+=r,this.dirty=!0,s}bind(e){const t=e.gl;this.texture?(t.bindTexture(t.TEXTURE_2D,this.texture),this.dirty&&(this.dirty=!1,t.texSubImage2D(t.TEXTURE_2D,0,0,0,this.width,this.height,t.ALPHA,t.UNSIGNED_BYTE,this.data))):(this.texture=t.createTexture(),t.bindTexture(t.TEXTURE_2D,this.texture),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.REPEAT),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.REPEAT),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR),t.texImage2D(t.TEXTURE_2D,0,t.ALPHA,this.width,this.height,0,t.ALPHA,t.UNSIGNED_BYTE,this.data));}}const R=\"maplibre_preloaded_worker_pool\";class z{constructor(){this.active={};}acquire(e){if(!this.workers)for(this.workers=[];this.workers.length<z.workerCount;)this.workers.push(new Worker(t.a.WORKER_URL));return this.active[e]=!0,this.workers.slice()}release(e){delete this.active[e],0===this.numActive()&&(this.workers.forEach((e=>{e.terminate();})),this.workers=null);}isPreloaded(){return !!this.active[R]}numActive(){return Object.keys(this.active).length}}const D=Math.floor(s.hardwareConcurrency/2);let A,L;function k(){return A||(A=new z),A}z.workerCount=t.H(globalThis)?Math.max(Math.min(D,3),1):1;class F{constructor(e,i){this.workerPool=e,this.actors=[],this.currentActor=0,this.id=i;const o=this.workerPool.acquire(i);for(let e=0;e<o.length;e++){const r=new t.J(o[e],i);r.name=`Worker ${e}`,this.actors.push(r);}if(!this.actors.length)throw new Error(\"No actors found\")}broadcast(e,t){const i=[];for(const o of this.actors)i.push(o.sendAsync({type:e,data:t}));return Promise.all(i)}getActor(){return this.currentActor=(this.currentActor+1)%this.actors.length,this.actors[this.currentActor]}remove(e=!0){this.actors.forEach((e=>{e.remove();})),this.actors=[],e&&this.workerPool.release(this.id);}registerMessageHandler(e,t){for(const i of this.actors)i.registerMessageHandler(e,t);}}function B(){return L||(L=new F(k(),t.K),L.registerMessageHandler(\"GR\",((e,i,o)=>t.m(i,o)))),L}function O(e,i){const o=t.L();return t.M(o,o,[1,1,0]),t.N(o,o,[.5*e.width,.5*e.height,1]),e.calculatePosMatrix?t.O(o,o,e.calculatePosMatrix(i.toUnwrapped())):o}function j(e,t,i,o,r,a,s){var n;const l=function(e,t,i){if(e)for(const o of e){const e=t[o];if(e&&e.source===i&&\"fill-extrusion\"===e.type)return !0}else for(const e in t){const o=t[e];if(o.source===i&&\"fill-extrusion\"===o.type)return !0}return !1}(null!==(n=null==r?void 0:r.layers)&&void 0!==n?n:null,t,e.id),c=a.maxPitchScaleFactor(),h=e.tilesIn(o,c,l);h.sort(N);const u=[];for(const o of h)u.push({wrappedTileID:o.tileID.wrapped().key,queryResults:o.tile.queryRenderedFeatures(t,i,e._state,o.queryGeometry,o.cameraQueryGeometry,o.scale,r,a,c,O(e.transform,o.tileID),s?(e,t)=>s(o.tileID,e,t):void 0)});return function(e,t){for(const i in e)for(const o of e[i])Z(o,t);return e}(function(e){const t={},i={};for(const o of e){const e=o.queryResults,r=o.wrappedTileID,a=i[r]=i[r]||{};for(const i in e){const o=e[i],r=a[i]=a[i]||{},s=t[i]=t[i]||[];for(const e of o)r[e.featureIndex]||(r[e.featureIndex]=!0,s.push(e));}}return t}(u),e)}function N(e,t){const i=e.tileID,o=t.tileID;return i.overscaledZ-o.overscaledZ||i.canonical.y-o.canonical.y||i.wrap-o.wrap||i.canonical.x-o.canonical.x}function Z(e,t){const i=e.feature,o=t.getFeatureState(i.layer[\"source-layer\"],i.id);i.source=i.layer.source,i.layer[\"source-layer\"]&&(i.sourceLayer=i.layer[\"source-layer\"]),i.state=o;}function U(e,i,o){return t._(this,void 0,void 0,(function*(){let r=e;if(e.url?r=(yield t.j(i.transformRequest(e.url,\"Source\"),o)).data:yield s.frameAsync(o),!r)return null;const a=t.Q(t.e(r,e),[\"tiles\",\"minzoom\",\"maxzoom\",\"attribution\",\"bounds\",\"scheme\",\"tileSize\",\"encoding\"]);return \"vector_layers\"in r&&r.vector_layers&&(a.vectorLayerIds=r.vector_layers.map((e=>e.id))),a}))}class G{constructor(e,t){e&&(t?this.setSouthWest(e).setNorthEast(t):Array.isArray(e)&&(4===e.length?this.setSouthWest([e[0],e[1]]).setNorthEast([e[2],e[3]]):this.setSouthWest(e[0]).setNorthEast(e[1])));}setNorthEast(e){return this._ne=e instanceof t.S?new t.S(e.lng,e.lat):t.S.convert(e),this}setSouthWest(e){return this._sw=e instanceof t.S?new t.S(e.lng,e.lat):t.S.convert(e),this}extend(e){const i=this._sw,o=this._ne;let r,a;if(e instanceof t.S)r=e,a=e;else {if(!(e instanceof G))return Array.isArray(e)?4===e.length||e.every(Array.isArray)?this.extend(G.convert(e)):this.extend(t.S.convert(e)):e&&(\"lng\"in e||\"lon\"in e)&&\"lat\"in e?this.extend(t.S.convert(e)):this;if(r=e._sw,a=e._ne,!r||!a)return this}return i||o?(i.lng=Math.min(r.lng,i.lng),i.lat=Math.min(r.lat,i.lat),o.lng=Math.max(a.lng,o.lng),o.lat=Math.max(a.lat,o.lat)):(this._sw=new t.S(r.lng,r.lat),this._ne=new t.S(a.lng,a.lat)),this}getCenter(){return new t.S((this._sw.lng+this._ne.lng)/2,(this._sw.lat+this._ne.lat)/2)}getSouthWest(){return this._sw}getNorthEast(){return this._ne}getNorthWest(){return new t.S(this.getWest(),this.getNorth())}getSouthEast(){return new t.S(this.getEast(),this.getSouth())}getWest(){return this._sw.lng}getSouth(){return this._sw.lat}getEast(){return this._ne.lng}getNorth(){return this._ne.lat}toArray(){return [this._sw.toArray(),this._ne.toArray()]}toString(){return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`}isEmpty(){return !(this._sw&&this._ne)}contains(e){const{lng:i,lat:o}=t.S.convert(e);let r=this._sw.lng<=i&&i<=this._ne.lng;return this._sw.lng>this._ne.lng&&(r=this._sw.lng>=i&&i>=this._ne.lng),this._sw.lat<=o&&o<=this._ne.lat&&r}static convert(e){return e instanceof G?e:e?new G(e):e}static fromLngLat(e,i=0){const o=360*i/40075017,r=o/Math.cos(Math.PI/180*e.lat);return new G(new t.S(e.lng-r,e.lat-o),new t.S(e.lng+r,e.lat+o))}adjustAntiMeridian(){const e=new t.S(this._sw.lng,this._sw.lat),i=new t.S(this._ne.lng,this._ne.lat);return new G(e,e.lng>i.lng?new t.S(i.lng+360,i.lat):i)}}class V{constructor(e,t,i){this.bounds=G.convert(this.validateBounds(e)),this.minzoom=t||0,this.maxzoom=i||24;}validateBounds(e){return Array.isArray(e)&&4===e.length?[Math.max(-180,e[0]),Math.max(-90,e[1]),Math.min(180,e[2]),Math.min(90,e[3])]:[-180,-90,180,90]}contains(e){const i=Math.pow(2,e.z),o=Math.floor(t.V(this.bounds.getWest())*i),r=Math.floor(t.U(this.bounds.getNorth())*i),a=Math.ceil(t.V(this.bounds.getEast())*i),s=Math.ceil(t.U(this.bounds.getSouth())*i);return e.x>=o&&e.x<a&&e.y>=r&&e.y<s}}class $ extends t.E{constructor(e,i,o,r){if(super(),this.id=e,this.dispatcher=o,this.type=\"vector\",this.minzoom=0,this.maxzoom=22,this.scheme=\"xyz\",this.tileSize=512,this.reparseOverscaled=!0,this.isTileClipped=!0,this._loaded=!1,t.e(this,t.Q(i,[\"url\",\"scheme\",\"tileSize\",\"promoteId\"])),this._options=t.e({type:\"vector\"},i),this._collectResourceTiming=i.collectResourceTiming,512!==this.tileSize)throw new Error(\"vector tile sources must have a tileSize of 512\");this.setEventedParent(r);}load(){return t._(this,void 0,void 0,(function*(){this._loaded=!1,this.fire(new t.l(\"dataloading\",{dataType:\"source\"})),this._tileJSONRequest=new AbortController;try{const e=yield U(this._options,this.map._requestManager,this._tileJSONRequest);this._tileJSONRequest=null,this._loaded=!0,this.map.style.sourceCaches[this.id].clearTiles(),e&&(t.e(this,e),e.bounds&&(this.tileBounds=new V(e.bounds,this.minzoom,this.maxzoom)),this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"metadata\"})),this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"content\"})));}catch(e){this._tileJSONRequest=null,this.fire(new t.k(e));}}))}loaded(){return this._loaded}hasTile(e){return !this.tileBounds||this.tileBounds.contains(e.canonical)}onAdd(e){this.map=e,this.load();}setSourceProperty(e){this._tileJSONRequest&&this._tileJSONRequest.abort(),e(),this.load();}setTiles(e){return this.setSourceProperty((()=>{this._options.tiles=e;})),this}setUrl(e){return this.setSourceProperty((()=>{this.url=e,this._options.url=e;})),this}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null);}serialize(){return t.e({},this._options)}loadTile(e){return t._(this,void 0,void 0,(function*(){const t=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme),i={request:this.map._requestManager.transformRequest(t,\"Tile\"),uid:e.uid,tileID:e.tileID,zoom:e.tileID.overscaledZ,tileSize:this.tileSize*e.tileID.overscaleFactor(),type:this.type,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId,subdivisionGranularity:this.map.style.projection.subdivisionGranularity,globalState:this.map.getGlobalState()};i.request.collectResourceTiming=this._collectResourceTiming;let o=\"RT\";if(e.actor&&\"expired\"!==e.state){if(\"loading\"===e.state)return new Promise(((t,i)=>{e.reloadPromise={resolve:t,reject:i};}))}else e.actor=this.dispatcher.getActor(),o=\"LT\";e.abortController=new AbortController;try{const t=yield e.actor.sendAsync({type:o,data:i},e.abortController);if(delete e.abortController,e.aborted)return;this._afterTileLoadWorkerResponse(e,t);}catch(t){if(delete e.abortController,e.aborted)return;if(t&&404!==t.status)throw t;this._afterTileLoadWorkerResponse(e,null);}}))}_afterTileLoadWorkerResponse(e,t){if(t&&t.resourceTiming&&(e.resourceTiming=t.resourceTiming),t&&this.map._refreshExpiredTiles&&e.setExpiryData(t),e.loadVectorData(t,this.map.painter),e.reloadPromise){const t=e.reloadPromise;e.reloadPromise=null,this.loadTile(e).then(t.resolve).catch(t.reject);}}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController),e.actor&&(yield e.actor.sendAsync({type:\"AT\",data:{uid:e.uid,type:this.type,source:this.id}}));}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.unloadVectorData(),e.actor&&(yield e.actor.sendAsync({type:\"RMT\",data:{uid:e.uid,type:this.type,source:this.id}}));}))}hasTransition(){return !1}}class q extends t.E{constructor(e,i,o,r){super(),this.id=e,this.dispatcher=o,this.setEventedParent(r),this.type=\"raster\",this.minzoom=0,this.maxzoom=22,this.roundZoom=!0,this.scheme=\"xyz\",this.tileSize=512,this._loaded=!1,this._options=t.e({type:\"raster\"},i),t.e(this,t.Q(i,[\"url\",\"scheme\",\"tileSize\"]));}load(){return t._(this,arguments,void 0,(function*(e=!1){this._loaded=!1,this.fire(new t.l(\"dataloading\",{dataType:\"source\"})),this._tileJSONRequest=new AbortController;try{const i=yield U(this._options,this.map._requestManager,this._tileJSONRequest);this._tileJSONRequest=null,this._loaded=!0,i&&(t.e(this,i),i.bounds&&(this.tileBounds=new V(i.bounds,this.minzoom,this.maxzoom)),this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"metadata\"})),this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"content\",sourceDataChanged:e})));}catch(e){this._tileJSONRequest=null,this.fire(new t.k(e));}}))}loaded(){return this._loaded}onAdd(e){this.map=e,this.load();}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null);}setSourceProperty(e){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null),e(),this.load(!0);}setTiles(e){return this.setSourceProperty((()=>{this._options.tiles=e;})),this}setUrl(e){return this.setSourceProperty((()=>{this.url=e,this._options.url=e;})),this}serialize(){return t.e({},this._options)}hasTile(e){return !this.tileBounds||this.tileBounds.contains(e.canonical)}loadTile(e){return t._(this,void 0,void 0,(function*(){const i=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme);e.abortController=new AbortController;try{const o=yield p.getImage(this.map._requestManager.transformRequest(i,\"Tile\"),e.abortController,this.map._refreshExpiredTiles);if(delete e.abortController,e.aborted)return void(e.state=\"unloaded\");if(o&&o.data){this.map._refreshExpiredTiles&&(o.cacheControl||o.expires)&&e.setExpiryData({cacheControl:o.cacheControl,expires:o.expires});const i=this.map.painter.context,r=i.gl,a=o.data;e.texture=this.map.painter.getTileTexture(a.width),e.texture?e.texture.update(a,{useMipmap:!0}):(e.texture=new t.T(i,a,r.RGBA,{useMipmap:!0}),e.texture.bind(r.LINEAR,r.CLAMP_TO_EDGE,r.LINEAR_MIPMAP_NEAREST)),e.state=\"loaded\";}}catch(t){if(delete e.abortController,e.aborted)e.state=\"unloaded\";else if(t)throw e.state=\"errored\",t}}))}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController);}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.texture&&this.map.painter.saveTileTexture(e.texture);}))}hasTransition(){return !1}}class W extends q{constructor(e,i,o,r){super(e,i,o,r),this.type=\"raster-dem\",this.maxzoom=22,this._options=t.e({type:\"raster-dem\"},i),this.encoding=i.encoding||\"mapbox\",this.redFactor=i.redFactor,this.greenFactor=i.greenFactor,this.blueFactor=i.blueFactor,this.baseShift=i.baseShift;}loadTile(e){return t._(this,void 0,void 0,(function*(){const i=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme),o=this.map._requestManager.transformRequest(i,\"Tile\");e.neighboringTiles=this._getNeighboringTiles(e.tileID),e.abortController=new AbortController;try{const i=yield p.getImage(o,e.abortController,this.map._refreshExpiredTiles);if(delete e.abortController,e.aborted)return void(e.state=\"unloaded\");if(i&&i.data){const o=i.data;this.map._refreshExpiredTiles&&(i.cacheControl||i.expires)&&e.setExpiryData({cacheControl:i.cacheControl,expires:i.expires});const r=t.b(o)&&t.W()?o:yield this.readImageNow(o),a={type:this.type,uid:e.uid,source:this.id,rawImageData:r,encoding:this.encoding,redFactor:this.redFactor,greenFactor:this.greenFactor,blueFactor:this.blueFactor,baseShift:this.baseShift};if(!e.actor||\"expired\"===e.state){e.actor=this.dispatcher.getActor();const t=yield e.actor.sendAsync({type:\"LDT\",data:a});e.dem=t,e.needsHillshadePrepare=!0,e.needsTerrainPrepare=!0,e.state=\"loaded\";}}}catch(t){if(delete e.abortController,e.aborted)e.state=\"unloaded\";else if(t)throw e.state=\"errored\",t}}))}readImageNow(e){return t._(this,void 0,void 0,(function*(){if(\"undefined\"!=typeof VideoFrame&&t.X()){const i=e.width+2,o=e.height+2;try{return new t.R({width:i,height:o},yield t.Y(e,-1,-1,i,o))}catch(e){}}return s.getImageData(e,1)}))}_getNeighboringTiles(e){const i=e.canonical,o=Math.pow(2,i.z),r=(i.x-1+o)%o,a=0===i.x?e.wrap-1:e.wrap,s=(i.x+1+o)%o,n=i.x+1===o?e.wrap+1:e.wrap,l={};return l[new t.Z(e.overscaledZ,a,i.z,r,i.y).key]={backfilled:!1},l[new t.Z(e.overscaledZ,n,i.z,s,i.y).key]={backfilled:!1},i.y>0&&(l[new t.Z(e.overscaledZ,a,i.z,r,i.y-1).key]={backfilled:!1},l[new t.Z(e.overscaledZ,e.wrap,i.z,i.x,i.y-1).key]={backfilled:!1},l[new t.Z(e.overscaledZ,n,i.z,s,i.y-1).key]={backfilled:!1}),i.y+1<o&&(l[new t.Z(e.overscaledZ,a,i.z,r,i.y+1).key]={backfilled:!1},l[new t.Z(e.overscaledZ,e.wrap,i.z,i.x,i.y+1).key]={backfilled:!1},l[new t.Z(e.overscaledZ,n,i.z,s,i.y+1).key]={backfilled:!1}),l}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.demTexture&&this.map.painter.saveTileTexture(e.demTexture),e.fbo&&(e.fbo.destroy(),delete e.fbo),e.dem&&delete e.dem,delete e.neighboringTiles,e.state=\"unloaded\",e.actor&&(yield e.actor.sendAsync({type:\"RDT\",data:{type:this.type,uid:e.uid,source:this.id}}));}))}}class H extends t.E{constructor(e,i,o,r){super(),this.id=e,this.type=\"geojson\",this.minzoom=0,this.maxzoom=18,this.tileSize=512,this.isTileClipped=!0,this.reparseOverscaled=!0,this._removed=!1,this._pendingLoads=0,this.actor=o.getActor(),this.setEventedParent(r),this._data=i.data,this._options=t.e({},i),this._collectResourceTiming=i.collectResourceTiming,void 0!==i.maxzoom&&(this.maxzoom=i.maxzoom),i.type&&(this.type=i.type),i.attribution&&(this.attribution=i.attribution),this.promoteId=i.promoteId,void 0!==i.clusterMaxZoom&&this.maxzoom<=i.clusterMaxZoom&&t.w(`The maxzoom value \"${this.maxzoom}\" is expected to be greater than the clusterMaxZoom value \"${i.clusterMaxZoom}\".`),this.workerOptions=t.e({source:this.id,cluster:i.cluster||!1,geojsonVtOptions:{buffer:this._pixelsToTileUnits(void 0!==i.buffer?i.buffer:128),tolerance:this._pixelsToTileUnits(void 0!==i.tolerance?i.tolerance:.375),extent:t.$,maxZoom:this.maxzoom,lineMetrics:i.lineMetrics||!1,generateId:i.generateId||!1},superclusterOptions:{maxZoom:this._getClusterMaxZoom(i.clusterMaxZoom),minPoints:Math.max(2,i.clusterMinPoints||2),extent:t.$,radius:this._pixelsToTileUnits(i.clusterRadius||50),log:!1,generateId:i.generateId||!1},clusterProperties:i.clusterProperties,filter:i.filter},i.workerOptions),\"string\"==typeof this.promoteId&&(this.workerOptions.promoteId=this.promoteId);}_pixelsToTileUnits(e){return e*(t.$/this.tileSize)}_getClusterMaxZoom(e){const i=e?Math.round(e):this.maxzoom-1;return Number.isInteger(e)||void 0===e||t.w(`Integer expected for option 'clusterMaxZoom': provided value \"${e}\" rounded to \"${i}\"`),i}load(){return t._(this,void 0,void 0,(function*(){yield this._updateWorkerData();}))}onAdd(e){this.map=e,this.load();}setData(e){return this._data=e,this._updateWorkerData(),this}updateData(e){return this._updateWorkerData(e),this}getData(){return t._(this,void 0,void 0,(function*(){const e=t.e({type:this.type},this.workerOptions);return this.actor.sendAsync({type:\"GD\",data:e})}))}getCoordinatesFromGeometry(e){return \"GeometryCollection\"===e.type?e.geometries.map((e=>e.coordinates)).flat(1/0):e.coordinates.flat(1/0)}getBounds(){return t._(this,void 0,void 0,(function*(){const e=new G,t=yield this.getData();let i;switch(t.type){case \"FeatureCollection\":i=t.features.map((e=>this.getCoordinatesFromGeometry(e.geometry))).flat(1/0);break;case \"Feature\":i=this.getCoordinatesFromGeometry(t.geometry);break;default:i=this.getCoordinatesFromGeometry(t);}if(0==i.length)return e;for(let t=0;t<i.length-1;t+=2)e.extend([i[t],i[t+1]]);return e}))}setClusterOptions(e){return this.workerOptions.cluster=e.cluster,e&&(void 0!==e.clusterRadius&&(this.workerOptions.superclusterOptions.radius=this._pixelsToTileUnits(e.clusterRadius)),void 0!==e.clusterMaxZoom&&(this.workerOptions.superclusterOptions.maxZoom=this._getClusterMaxZoom(e.clusterMaxZoom))),this._updateWorkerData(),this}getClusterExpansionZoom(e){return this.actor.sendAsync({type:\"GCEZ\",data:{type:this.type,clusterId:e,source:this.id}})}getClusterChildren(e){return this.actor.sendAsync({type:\"GCC\",data:{type:this.type,clusterId:e,source:this.id}})}getClusterLeaves(e,t,i){return this.actor.sendAsync({type:\"GCL\",data:{type:this.type,source:this.id,clusterId:e,limit:t,offset:i}})}_updateWorkerData(e){return t._(this,void 0,void 0,(function*(){const i=t.e({type:this.type},this.workerOptions);e?i.dataDiff=e:\"string\"==typeof this._data?(i.request=this.map._requestManager.transformRequest(s.resolveURL(this._data),\"Source\"),i.request.collectResourceTiming=this._collectResourceTiming):i.data=JSON.stringify(this._data),this._pendingLoads++,this.fire(new t.l(\"dataloading\",{dataType:\"source\"}));try{const e=yield this.actor.sendAsync({type:\"LD\",data:i});if(this._pendingLoads--,this._removed||e.abandoned)return void this.fire(new t.l(\"dataabort\",{dataType:\"source\"}));let o=null;e.resourceTiming&&e.resourceTiming[this.id]&&(o=e.resourceTiming[this.id].slice(0));const r={dataType:\"source\"};this._collectResourceTiming&&o&&o.length>0&&t.e(r,{resourceTiming:o}),this.fire(new t.l(\"data\",Object.assign(Object.assign({},r),{sourceDataType:\"metadata\"}))),this.fire(new t.l(\"data\",Object.assign(Object.assign({},r),{sourceDataType:\"content\"})));}catch(e){if(this._pendingLoads--,this._removed)return void this.fire(new t.l(\"dataabort\",{dataType:\"source\"}));this.fire(new t.k(e));}}))}loaded(){return 0===this._pendingLoads}loadTile(e){return t._(this,void 0,void 0,(function*(){const t=e.actor?\"RT\":\"LT\";e.actor=this.actor;const i={type:this.type,uid:e.uid,tileID:e.tileID,zoom:e.tileID.overscaledZ,maxZoom:this.maxzoom,tileSize:this.tileSize,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId,subdivisionGranularity:this.map.style.projection.subdivisionGranularity,globalState:this.map.getGlobalState()};e.abortController=new AbortController;const o=yield this.actor.sendAsync({type:t,data:i},e.abortController);delete e.abortController,e.unloadVectorData(),e.aborted||e.loadVectorData(o,this.map.painter,\"RT\"===t);}))}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController),e.aborted=!0;}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.unloadVectorData(),yield this.actor.sendAsync({type:\"RMT\",data:{uid:e.uid,type:this.type,source:this.id}});}))}onRemove(){this._removed=!0,this.actor.sendAsync({type:\"RS\",data:{type:this.type,source:this.id}});}serialize(){return t.e({},this._options,{type:this.type,data:this._data})}hasTransition(){return !1}}class X extends t.E{constructor(e,t,i,o){super(),this.flippedWindingOrder=!1,this.id=e,this.dispatcher=i,this.coordinates=t.coordinates,this.type=\"image\",this.minzoom=0,this.maxzoom=22,this.tileSize=512,this.tiles={},this._loaded=!1,this.setEventedParent(o),this.options=t;}load(e){return t._(this,void 0,void 0,(function*(){this._loaded=!1,this.fire(new t.l(\"dataloading\",{dataType:\"source\"})),this.url=this.options.url,this._request=new AbortController;try{const t=yield p.getImage(this.map._requestManager.transformRequest(this.url,\"Image\"),this._request);this._request=null,this._loaded=!0,t&&t.data&&(this.image=t.data,e&&(this.coordinates=e),this._finishLoading());}catch(e){this._request=null,this._loaded=!0,this.fire(new t.k(e));}}))}loaded(){return this._loaded}updateImage(e){return e.url?(this._request&&(this._request.abort(),this._request=null),this.options.url=e.url,this.load(e.coordinates).finally((()=>{this.texture=null;})),this):this}_finishLoading(){this.map&&(this.setCoordinates(this.coordinates),this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"metadata\"})));}onAdd(e){this.map=e,this.load();}onRemove(){this._request&&(this._request.abort(),this._request=null);}setCoordinates(e){this.coordinates=e;const i=e.map(t.a0.fromLngLat);var o;return this.tileID=function(e){const i=t.a1.fromPoints(e),o=i.width(),r=i.height(),a=Math.max(o,r),s=Math.max(0,Math.floor(-Math.log(a)/Math.LN2)),n=Math.pow(2,s);return new t.a3(s,Math.floor((i.minX+i.maxX)/2*n),Math.floor((i.minY+i.maxY)/2*n))}(i),this.terrainTileRanges=this._getOverlappingTileRanges(i),this.minzoom=this.maxzoom=this.tileID.z,this.tileCoords=i.map((e=>this.tileID.getTilePoint(e)._round())),this.flippedWindingOrder=((o=this.tileCoords)[1].x-o[0].x)*(o[2].y-o[0].y)-(o[1].y-o[0].y)*(o[2].x-o[0].x)<0,this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"content\"})),this}prepare(){if(0===Object.keys(this.tiles).length||!this.image)return;const e=this.map.painter.context,i=e.gl;this.texture||(this.texture=new t.T(e,this.image,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));let o=!1;for(const e in this.tiles){const t=this.tiles[e];\"loaded\"!==t.state&&(t.state=\"loaded\",t.texture=this.texture,o=!0);}o&&this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"idle\",sourceId:this.id}));}loadTile(e){return t._(this,void 0,void 0,(function*(){this.tileID&&this.tileID.equals(e.tileID.canonical)?(this.tiles[String(e.tileID.wrap)]=e,e.buckets={}):e.state=\"errored\";}))}serialize(){return {type:\"image\",url:this.options.url,coordinates:this.coordinates}}hasTransition(){return !1}_getOverlappingTileRanges(e){const{minX:i,minY:o,maxX:r,maxY:a}=t.a1.fromPoints(e),s={};for(let e=0;e<=t.a2;e++){const t=Math.pow(2,e),n=Math.floor(i*t),l=Math.floor(o*t),c=Math.floor(r*t),h=Math.floor(a*t);s[e]={minTileX:n,minTileY:l,maxTileX:c,maxTileY:h};}return s}}class K extends X{constructor(e,t,i,o){super(e,t,i,o),this.roundZoom=!0,this.type=\"video\",this.options=t;}load(){return t._(this,void 0,void 0,(function*(){this._loaded=!1;const e=this.options;this.urls=[];for(const t of e.urls)this.urls.push(this.map._requestManager.transformRequest(t,\"Source\").url);try{const e=yield t.a4(this.urls);if(this._loaded=!0,!e)return;this.video=e,this.video.loop=!0,this.video.addEventListener(\"playing\",(()=>{this.map.triggerRepaint();})),this.map&&this.video.play(),this._finishLoading();}catch(e){this.fire(new t.k(e));}}))}pause(){this.video&&this.video.pause();}play(){this.video&&this.video.play();}seek(e){if(this.video){const i=this.video.seekable;e<i.start(0)||e>i.end(0)?this.fire(new t.k(new t.a5(`sources.${this.id}`,null,`Playback for this video can be set only between the ${i.start(0)} and ${i.end(0)}-second mark.`))):this.video.currentTime=e;}}getVideo(){return this.video}onAdd(e){this.map||(this.map=e,this.load(),this.video&&(this.video.play(),this.setCoordinates(this.coordinates)));}prepare(){if(0===Object.keys(this.tiles).length||this.video.readyState<2)return;const e=this.map.painter.context,i=e.gl;this.texture?this.video.paused||(this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE),i.texSubImage2D(i.TEXTURE_2D,0,0,0,i.RGBA,i.UNSIGNED_BYTE,this.video)):(this.texture=new t.T(e,this.video,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));let o=!1;for(const e in this.tiles){const t=this.tiles[e];\"loaded\"!==t.state&&(t.state=\"loaded\",t.texture=this.texture,o=!0);}o&&this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"idle\",sourceId:this.id}));}serialize(){return {type:\"video\",urls:this.urls,coordinates:this.coordinates}}hasTransition(){return this.video&&!this.video.paused}}class Y extends X{constructor(e,i,o,r){super(e,i,o,r),i.coordinates?Array.isArray(i.coordinates)&&4===i.coordinates.length&&!i.coordinates.some((e=>!Array.isArray(e)||2!==e.length||e.some((e=>\"number\"!=typeof e))))||this.fire(new t.k(new t.a5(`sources.${e}`,null,'\"coordinates\" property must be an array of 4 longitude/latitude array pairs'))):this.fire(new t.k(new t.a5(`sources.${e}`,null,'missing required property \"coordinates\"'))),i.animate&&\"boolean\"!=typeof i.animate&&this.fire(new t.k(new t.a5(`sources.${e}`,null,'optional \"animate\" property must be a boolean value'))),i.canvas?\"string\"==typeof i.canvas||i.canvas instanceof HTMLCanvasElement||this.fire(new t.k(new t.a5(`sources.${e}`,null,'\"canvas\" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance'))):this.fire(new t.k(new t.a5(`sources.${e}`,null,'missing required property \"canvas\"'))),this.options=i,this.animate=void 0===i.animate||i.animate;}load(){return t._(this,void 0,void 0,(function*(){this._loaded=!0,this.canvas||(this.canvas=this.options.canvas instanceof HTMLCanvasElement?this.options.canvas:document.getElementById(this.options.canvas)),this.width=this.canvas.width,this.height=this.canvas.height,this._hasInvalidDimensions()?this.fire(new t.k(new Error(\"Canvas dimensions cannot be less than or equal to zero.\"))):(this.play=function(){this._playing=!0,this.map.triggerRepaint();},this.pause=function(){this._playing&&(this.prepare(),this._playing=!1);},this._finishLoading());}))}getCanvas(){return this.canvas}onAdd(e){this.map=e,this.load(),this.canvas&&this.animate&&this.play();}onRemove(){this.pause();}prepare(){let e=!1;if(this.canvas.width!==this.width&&(this.width=this.canvas.width,e=!0),this.canvas.height!==this.height&&(this.height=this.canvas.height,e=!0),this._hasInvalidDimensions())return;if(0===Object.keys(this.tiles).length)return;const i=this.map.painter.context,o=i.gl;this.texture?(e||this._playing)&&this.texture.update(this.canvas,{premultiply:!0}):this.texture=new t.T(i,this.canvas,o.RGBA,{premultiply:!0});let r=!1;for(const e in this.tiles){const t=this.tiles[e];\"loaded\"!==t.state&&(t.state=\"loaded\",t.texture=this.texture,r=!0);}r&&this.fire(new t.l(\"data\",{dataType:\"source\",sourceDataType:\"idle\",sourceId:this.id}));}serialize(){return {type:\"canvas\",coordinates:this.coordinates}}hasTransition(){return this._playing}_hasInvalidDimensions(){for(const e of [this.canvas.width,this.canvas.height])if(isNaN(e)||e<=0)return !0;return !1}}const Q={},J=e=>{switch(e){case \"geojson\":return H;case \"image\":return X;case \"raster\":return q;case \"raster-dem\":return W;case \"vector\":return $;case \"video\":return K;case \"canvas\":return Y}return Q[e]},ee=\"RTLPluginLoaded\";class te extends t.E{constructor(){super(...arguments),this.status=\"unavailable\",this.url=null,this.dispatcher=B();}_syncState(e){return this.status=e,this.dispatcher.broadcast(\"SRPS\",{pluginStatus:e,pluginURL:this.url}).catch((e=>{throw this.status=\"error\",e}))}getRTLTextPluginStatus(){return this.status}clearRTLTextPlugin(){this.status=\"unavailable\",this.url=null;}setRTLTextPlugin(e){return t._(this,arguments,void 0,(function*(e,t=!1){if(this.url)throw new Error(\"setRTLTextPlugin cannot be called multiple times.\");if(this.url=s.resolveURL(e),!this.url)throw new Error(`requested url ${e} is invalid`);if(\"unavailable\"===this.status){if(!t)return this._requestImport();this.status=\"deferred\",this._syncState(this.status);}else if(\"requested\"===this.status)return this._requestImport()}))}_requestImport(){return t._(this,void 0,void 0,(function*(){yield this._syncState(\"loading\"),this.status=\"loaded\",this.fire(new t.l(ee));}))}lazyLoad(){\"unavailable\"===this.status?this.status=\"requested\":\"deferred\"===this.status&&this._requestImport();}}let ie=null;function oe(){return ie||(ie=new te),ie}class re{constructor(e,i){this.timeAdded=0,this.fadeEndTime=0,this.tileID=e,this.uid=t.a6(),this.uses=0,this.tileSize=i,this.buckets={},this.expirationTime=null,this.queryPadding=0,this.hasSymbolBuckets=!1,this.hasRTLText=!1,this.dependencies={},this.rtt=[],this.rttCoords={},this.expiredRequestCount=0,this.state=\"loading\";}registerFadeDuration(e){const t=e+this.timeAdded;t<this.fadeEndTime||(this.fadeEndTime=t);}wasRequested(){return \"errored\"===this.state||\"loaded\"===this.state||\"reloading\"===this.state}clearTextures(e){this.demTexture&&e.saveTileTexture(this.demTexture),this.demTexture=null;}loadVectorData(e,i,o){if(this.hasData()&&this.unloadVectorData(),this.state=\"loaded\",e){e.featureIndex&&(this.latestFeatureIndex=e.featureIndex,e.rawTileData?(this.latestRawTileData=e.rawTileData,this.latestFeatureIndex.rawTileData=e.rawTileData):this.latestRawTileData&&(this.latestFeatureIndex.rawTileData=this.latestRawTileData)),this.collisionBoxArray=e.collisionBoxArray,this.buckets=function(e,t){const i={};if(!t)return i;for(const o of e){const e=o.layerIds.map((e=>t.getLayer(e))).filter(Boolean);if(0!==e.length){o.layers=e,o.stateDependentLayerIds&&(o.stateDependentLayers=o.stateDependentLayerIds.map((t=>e.filter((e=>e.id===t))[0])));for(const t of e)i[t.id]=o;}}return i}(e.buckets,null==i?void 0:i.style),this.hasSymbolBuckets=!1;for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.a8){if(this.hasSymbolBuckets=!0,!o)break;i.justReloaded=!0;}}if(this.hasRTLText=!1,this.hasSymbolBuckets)for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.a8&&i.hasRTLText){this.hasRTLText=!0,oe().lazyLoad();break}}this.queryPadding=0;for(const e in this.buckets){const t=this.buckets[e];this.queryPadding=Math.max(this.queryPadding,i.style.getLayer(e).queryRadius(t));}e.imageAtlas&&(this.imageAtlas=e.imageAtlas),e.glyphAtlasImage&&(this.glyphAtlasImage=e.glyphAtlasImage);}else this.collisionBoxArray=new t.a7;}unloadVectorData(){for(const e in this.buckets)this.buckets[e].destroy();this.buckets={},this.imageAtlasTexture&&this.imageAtlasTexture.destroy(),this.imageAtlas&&(this.imageAtlas=null),this.glyphAtlasTexture&&this.glyphAtlasTexture.destroy(),this.latestFeatureIndex=null,this.state=\"unloaded\";}getBucket(e){return this.buckets[e.id]}upload(e){for(const t in this.buckets){const i=this.buckets[t];i.uploadPending()&&i.upload(e);}const i=e.gl;this.imageAtlas&&!this.imageAtlas.uploaded&&(this.imageAtlasTexture=new t.T(e,this.imageAtlas.image,i.RGBA),this.imageAtlas.uploaded=!0),this.glyphAtlasImage&&(this.glyphAtlasTexture=new t.T(e,this.glyphAtlasImage,i.ALPHA),this.glyphAtlasImage=null);}prepare(e){this.imageAtlas&&this.imageAtlas.patchUpdatedImages(e,this.imageAtlasTexture);}queryRenderedFeatures(e,t,i,o,r,a,s,n,l,c,h){return this.latestFeatureIndex&&this.latestFeatureIndex.rawTileData?this.latestFeatureIndex.query({queryGeometry:o,cameraQueryGeometry:r,scale:a,tileSize:this.tileSize,pixelPosMatrix:c,transform:n,params:s,queryPadding:this.queryPadding*l,getElevation:h},e,t,i):{}}querySourceFeatures(e,i){const o=this.latestFeatureIndex;if(!o||!o.rawTileData)return;const r=o.loadVTLayers(),a=i&&i.sourceLayer?i.sourceLayer:\"\",s=r._geojsonTileLayer||r[a];if(!s)return;const n=t.a9(i&&i.filter),{z:l,x:c,y:h}=this.tileID.canonical,u={z:l,x:c,y:h};for(let i=0;i<s.length;i++){const r=s.feature(i);if(n.needGeometry){const e=t.aa(r,!0);if(!n.filter(new t.F(this.tileID.overscaledZ),e,this.tileID.canonical))continue}else if(!n.filter(new t.F(this.tileID.overscaledZ),r))continue;const d=o.getId(r,a),_=new t.ab(r,l,c,h,d);_.tile=u,e.push(_);}}hasData(){return \"loaded\"===this.state||\"reloading\"===this.state||\"expired\"===this.state}patternsLoaded(){return this.imageAtlas&&!!Object.keys(this.imageAtlas.patternPositions).length}setExpiryData(e){const i=this.expirationTime;if(e.cacheControl){const i=t.ac(e.cacheControl);i[\"max-age\"]&&(this.expirationTime=Date.now()+1e3*i[\"max-age\"]);}else e.expires&&(this.expirationTime=new Date(e.expires).getTime());if(this.expirationTime){const e=Date.now();let t=!1;if(this.expirationTime>e)t=!1;else if(i)if(this.expirationTime<i)t=!0;else {const o=this.expirationTime-i;o?this.expirationTime=e+Math.max(o,3e4):t=!0;}else t=!0;t?(this.expiredRequestCount++,this.state=\"expired\"):this.expiredRequestCount=0;}}getExpiryTimeout(){if(this.expirationTime)return this.expiredRequestCount?1e3*(1<<Math.min(this.expiredRequestCount-1,31)):Math.min(this.expirationTime-(new Date).getTime(),Math.pow(2,31)-1)}setFeatureState(e,t){if(!this.latestFeatureIndex||!this.latestFeatureIndex.rawTileData||0===Object.keys(e).length)return;const i=this.latestFeatureIndex.loadVTLayers();for(const o in this.buckets){if(!t.style.hasLayer(o))continue;const r=this.buckets[o],a=r.layers[0].sourceLayer||\"_geojsonTileLayer\",s=i[a],n=e[a];if(!s||!n||0===Object.keys(n).length)continue;r.update(n,s,this.imageAtlas&&this.imageAtlas.patternPositions||{});const l=t&&t.style&&t.style.getLayer(o);l&&(this.queryPadding=Math.max(this.queryPadding,l.queryRadius(r)));}}holdingForFade(){return void 0!==this.symbolFadeHoldUntil}symbolFadeFinished(){return !this.symbolFadeHoldUntil||this.symbolFadeHoldUntil<s.now()}clearFadeHold(){this.symbolFadeHoldUntil=void 0;}setHoldDuration(e){this.symbolFadeHoldUntil=s.now()+e;}setDependencies(e,t){const i={};for(const e of t)i[e]=!0;this.dependencies[e]=i;}hasDependency(e,t){for(const i of e){const e=this.dependencies[i];if(e)for(const i of t)if(e[i])return !0}return !1}}class ae{constructor(e,t){this.max=e,this.onRemove=t,this.reset();}reset(){for(const e in this.data)for(const t of this.data[e])t.timeout&&clearTimeout(t.timeout),this.onRemove(t.value);return this.data={},this.order=[],this}add(e,t,i){const o=e.wrapped().key;void 0===this.data[o]&&(this.data[o]=[]);const r={value:t,timeout:void 0};if(void 0!==i&&(r.timeout=setTimeout((()=>{this.remove(e,r);}),i)),this.data[o].push(r),this.order.push(o),this.order.length>this.max){const e=this._getAndRemoveByKey(this.order[0]);e&&this.onRemove(e);}return this}has(e){return e.wrapped().key in this.data}getAndRemove(e){return this.has(e)?this._getAndRemoveByKey(e.wrapped().key):null}_getAndRemoveByKey(e){const t=this.data[e].shift();return t.timeout&&clearTimeout(t.timeout),0===this.data[e].length&&delete this.data[e],this.order.splice(this.order.indexOf(e),1),t.value}getByKey(e){const t=this.data[e];return t?t[0].value:null}get(e){return this.has(e)?this.data[e.wrapped().key][0].value:null}remove(e,t){if(!this.has(e))return this;const i=e.wrapped().key,o=void 0===t?0:this.data[i].indexOf(t),r=this.data[i][o];return this.data[i].splice(o,1),r.timeout&&clearTimeout(r.timeout),0===this.data[i].length&&delete this.data[i],this.onRemove(r.value),this.order.splice(this.order.indexOf(i),1),this}setMaxSize(e){for(this.max=e;this.order.length>this.max;){const e=this._getAndRemoveByKey(this.order[0]);e&&this.onRemove(e);}return this}filter(e){const t=[];for(const i in this.data)for(const o of this.data[i])e(o.value)||t.push(o);for(const e of t)this.remove(e.value.tileID,e);}}class se{constructor(){this.state={},this.stateChanges={},this.deletedStates={};}updateState(e,i,o){const r=String(i);if(this.stateChanges[e]=this.stateChanges[e]||{},this.stateChanges[e][r]=this.stateChanges[e][r]||{},t.e(this.stateChanges[e][r],o),null===this.deletedStates[e]){this.deletedStates[e]={};for(const t in this.state[e])t!==r&&(this.deletedStates[e][t]=null);}else if(this.deletedStates[e]&&null===this.deletedStates[e][r]){this.deletedStates[e][r]={};for(const t in this.state[e][r])o[t]||(this.deletedStates[e][r][t]=null);}else for(const t in o)this.deletedStates[e]&&this.deletedStates[e][r]&&null===this.deletedStates[e][r][t]&&delete this.deletedStates[e][r][t];}removeFeatureState(e,t,i){if(null===this.deletedStates[e])return;const o=String(t);if(this.deletedStates[e]=this.deletedStates[e]||{},i&&void 0!==t)null!==this.deletedStates[e][o]&&(this.deletedStates[e][o]=this.deletedStates[e][o]||{},this.deletedStates[e][o][i]=null);else if(void 0!==t)if(this.stateChanges[e]&&this.stateChanges[e][o])for(i in this.deletedStates[e][o]={},this.stateChanges[e][o])this.deletedStates[e][o][i]=null;else this.deletedStates[e][o]=null;else this.deletedStates[e]=null;}getState(e,i){const o=String(i),r=t.e({},(this.state[e]||{})[o],(this.stateChanges[e]||{})[o]);if(null===this.deletedStates[e])return {};if(this.deletedStates[e]){const t=this.deletedStates[e][i];if(null===t)return {};for(const e in t)delete r[e];}return r}initializeTileState(e,t){e.setFeatureState(this.state,t);}coalesceChanges(e,i){const o={};for(const e in this.stateChanges){this.state[e]=this.state[e]||{};const i={};for(const o in this.stateChanges[e])this.state[e][o]||(this.state[e][o]={}),t.e(this.state[e][o],this.stateChanges[e][o]),i[o]=this.state[e][o];o[e]=i;}for(const e in this.deletedStates){this.state[e]=this.state[e]||{};const i={};if(null===this.deletedStates[e])for(const t in this.state[e])i[t]={},this.state[e][t]={};else for(const t in this.deletedStates[e]){if(null===this.deletedStates[e][t])this.state[e][t]={};else for(const i of Object.keys(this.deletedStates[e][t]))delete this.state[e][t][i];i[t]=this.state[e][t];}o[e]=o[e]||{},t.e(o[e],i);}if(this.stateChanges={},this.deletedStates={},0!==Object.keys(o).length)for(const t in e)e[t].setFeatureState(o,i);}}const ne=89.25;function le(e,i){const o=t.ag(i.lat,-85.051129,t.ah);return new t.P(t.V(i.lng)*e,t.U(o)*e)}function ce(e,i){return new t.a0(i.x/e,i.y/e).toLngLat()}function he(e){return e.cameraToCenterDistance*Math.min(.85*Math.tan(t.ad(90-e.pitch)),Math.tan(t.ad(ne-e.pitch)))}function ue(e,i){const o=e.canonical,r=i/t.ae(o.z),a=o.x+Math.pow(2,o.z)*e.wrap,s=t.af(new Float64Array(16));return t.M(s,s,[a*r,o.y*r,0]),t.N(s,s,[r/t.$,r/t.$,1]),s}function de(e,i,o,r,a){const s=t.a0.fromLngLat(e,i),n=a*t.ai(1,e.lat),l=n*Math.cos(t.ad(o)),c=Math.sqrt(n*n-l*l),h=c*Math.sin(t.ad(-r)),u=c*Math.cos(t.ad(-r));return new t.a0(s.x+h,s.y+u,s.z+l)}function _e(e,t,i){const o=t.intersectsFrustum(e);if(!i||0===o)return o;const r=t.intersectsPlane(i);return 0===r?0:2===o&&2===r?2:1}function pe(e,t,i){let o=0;const r=(i-t)/10;for(let a=0;a<10;a++)o+=r*Math.pow(Math.cos(t+(a+.5)/10*(i-t)),e);return o}function me(e,i){return function(o,r,a,s,n){const l=2*((e-1)/t.aj(Math.cos(t.ad(ne-n))/Math.cos(t.ad(ne)))-1),c=Math.acos(a/s),h=2*pe(l-1,0,t.ad(n/2)),u=Math.min(t.ad(ne),c+t.ad(n/2)),d=pe(l-1,Math.min(u,c-t.ad(n/2)),u),_=Math.atan(r/a),p=Math.hypot(r,a);let m=o;return m+=t.aj(s/p/Math.max(.5,Math.cos(t.ad(n/2)))),m+=l*t.aj(Math.cos(_))/2,m-=t.aj(Math.max(1,d/h/i))/2,m}}const fe=me(9.314,3);function ge(e,i){const o=(i.roundZoom?Math.round:Math.floor)(e.zoom+t.aj(e.tileSize/i.tileSize));return Math.max(0,o)}function ve(e,i){const o=e.getCameraFrustum(),r=e.getClippingPlane(),a=e.screenPointToMercatorCoordinate(e.getCameraPoint()),s=t.a0.fromLngLat(e.center,e.elevation);a.z=s.z+Math.cos(e.pitchInRadians)*e.cameraToCenterDistance/e.worldSize;const n=e.getCoveringTilesDetailsProvider(),l=n.allowVariableZoom(e,i),c=ge(e,i),h=i.minzoom||0,u=void 0!==i.maxzoom?i.maxzoom:e.maxZoom,d=Math.min(Math.max(0,c),u),_=Math.pow(2,d),p=[_*a.x,_*a.y,0],m=[_*s.x,_*s.y,0],f=Math.hypot(s.x-a.x,s.y-a.y),g=Math.abs(s.z-a.z),v=Math.hypot(f,g),b=e=>({zoom:0,x:0,y:0,wrap:e,fullyVisible:!1}),x=[],y=[];if(e.renderWorldCopies&&n.allowWorldCopies())for(let e=1;e<=3;e++)x.push(b(-e)),x.push(b(e));for(x.push(b(0));x.length>0;){const _=x.pop(),f=_.x,b=_.y;let w=_.fullyVisible;const T={x:f,y:b,z:_.zoom},P=n.getTileBoundingVolume(T,_.wrap,e.elevation,i);if(!w){const e=_e(o,P,r);if(0===e)continue;w=2===e;}const C=n.distanceToTile2d(a.x,a.y,T,P);let I=c;l&&(I=(i.calculateTileZoom||fe)(e.zoom+t.aj(e.tileSize/i.tileSize),C,g,v,e.fov)),I=(i.roundZoom?Math.round:Math.floor)(I),I=Math.max(0,I);const M=Math.min(I,u);if(_.wrap=n.getWrap(s,T,_.wrap),_.zoom>=M){if(_.zoom<h)continue;const e=d-_.zoom,o=p[0]-.5-(f<<e),r=p[1]-.5-(b<<e),a=i.reparseOverscaled?Math.max(_.zoom,I):_.zoom;y.push({tileID:new t.Z(_.zoom===u?a:_.zoom,_.wrap,_.zoom,f,b),distanceSq:t.ak([m[0]-.5-f,m[1]-.5-b]),tileDistanceToCamera:Math.sqrt(o*o+r*r)});}else for(let e=0;e<4;e++)x.push({zoom:_.zoom+1,x:(f<<1)+e%2,y:(b<<1)+(e>>1),wrap:_.wrap,fullyVisible:w});}return y.sort(((e,t)=>e.distanceSq-t.distanceSq)).map((e=>e.tileID))}const be=t.a1.fromPoints([new t.P(0,0),new t.P(t.$,t.$)]);class xe extends t.E{constructor(e,t,i){super(),this.id=e,this.dispatcher=i,this.on(\"data\",(e=>this._dataHandler(e))),this.on(\"dataloading\",(()=>{this._sourceErrored=!1;})),this.on(\"error\",(()=>{this._sourceErrored=this._source.loaded();})),this._source=((e,t,i,o)=>{const r=new(J(t.type))(e,t,i,o);if(r.id!==e)throw new Error(`Expected Source id to be ${e} instead of ${r.id}`);return r})(e,t,i,this),this._tiles={},this._cache=new ae(0,(e=>this._unloadTile(e))),this._timers={},this._cacheTimers={},this._maxTileCacheSize=null,this._maxTileCacheZoomLevels=null,this._loadedParentTiles={},this._coveredTiles={},this._state=new se,this._didEmitContent=!1,this._updated=!1;}onAdd(e){this.map=e,this._maxTileCacheSize=e?e._maxTileCacheSize:null,this._maxTileCacheZoomLevels=e?e._maxTileCacheZoomLevels:null,this._source&&this._source.onAdd&&this._source.onAdd(e);}onRemove(e){this.clearTiles(),this._source&&this._source.onRemove&&this._source.onRemove(e);}loaded(){if(this._sourceErrored)return !0;if(!this._sourceLoaded)return !1;if(!this._source.loaded())return !1;if(!(void 0===this.used&&void 0===this.usedForTerrain||this.used||this.usedForTerrain))return !0;if(!this._updated)return !1;for(const e in this._tiles){const t=this._tiles[e];if(\"loaded\"!==t.state&&\"errored\"!==t.state)return !1}return !0}getSource(){return this._source}pause(){this._paused=!0;}resume(){if(!this._paused)return;const e=this._shouldReloadOnResume;this._paused=!1,this._shouldReloadOnResume=!1,e&&this.reload(),this.transform&&this.update(this.transform,this.terrain);}_loadTile(e,i,o){return t._(this,void 0,void 0,(function*(){try{yield this._source.loadTile(e),this._tileLoaded(e,i,o);}catch(i){e.state=\"errored\",404!==i.status?this._source.fire(new t.k(i,{tile:e})):this.update(this.transform,this.terrain);}}))}_unloadTile(e){this._source.unloadTile&&this._source.unloadTile(e);}_abortTile(e){this._source.abortTile&&this._source.abortTile(e),this._source.fire(new t.l(\"dataabort\",{tile:e,coord:e.tileID,dataType:\"source\"}));}serialize(){return this._source.serialize()}prepare(e){this._source.prepare&&this._source.prepare(),this._state.coalesceChanges(this._tiles,this.map?this.map.painter:null);for(const t in this._tiles){const i=this._tiles[t];i.upload(e),i.prepare(this.map.style.imageManager);}}getIds(){return Object.values(this._tiles).map((e=>e.tileID)).sort(ye).map((e=>e.key))}getRenderableIds(e){const i=[];for(const t in this._tiles)this._isIdRenderable(t,e)&&i.push(this._tiles[t]);return e?i.sort(((e,i)=>{const o=e.tileID,r=i.tileID,a=new t.P(o.canonical.x,o.canonical.y)._rotate(-this.transform.bearingInRadians),s=new t.P(r.canonical.x,r.canonical.y)._rotate(-this.transform.bearingInRadians);return o.overscaledZ-r.overscaledZ||s.y-a.y||s.x-a.x})).map((e=>e.tileID.key)):i.map((e=>e.tileID)).sort(ye).map((e=>e.key))}hasRenderableParent(e){const t=this.findLoadedParent(e,0);return !!t&&this._isIdRenderable(t.tileID.key)}_isIdRenderable(e,t){return this._tiles[e]&&this._tiles[e].hasData()&&!this._coveredTiles[e]&&(t||!this._tiles[e].holdingForFade())}reload(e){if(this._paused)this._shouldReloadOnResume=!0;else {this._cache.reset();for(const t in this._tiles)e?this._reloadTile(t,\"expired\"):\"errored\"!==this._tiles[t].state&&this._reloadTile(t,\"reloading\");}}_reloadTile(e,i){return t._(this,void 0,void 0,(function*(){const t=this._tiles[e];t&&(\"loading\"!==t.state&&(t.state=i),yield this._loadTile(t,e,i));}))}_tileLoaded(e,i,o){e.timeAdded=s.now(),\"expired\"===o&&(e.refreshedUponExpiration=!0),this._setTileReloadTimer(i,e),\"raster-dem\"===this.getSource().type&&e.dem&&this._backfillDEM(e),this._state.initializeTileState(e,this.map?this.map.painter:null),e.aborted||this._source.fire(new t.l(\"data\",{dataType:\"source\",tile:e,coord:e.tileID}));}_backfillDEM(e){const t=this.getRenderableIds();for(let o=0;o<t.length;o++){const r=t[o];if(e.neighboringTiles&&e.neighboringTiles[r]){const t=this.getTileByID(r);i(e,t),i(t,e);}}function i(e,t){e.needsHillshadePrepare=!0,e.needsTerrainPrepare=!0;let i=t.tileID.canonical.x-e.tileID.canonical.x;const o=t.tileID.canonical.y-e.tileID.canonical.y,r=Math.pow(2,e.tileID.canonical.z),a=t.tileID.key;0===i&&0===o||Math.abs(o)>1||(Math.abs(i)>1&&(1===Math.abs(i+r)?i+=r:1===Math.abs(i-r)&&(i-=r)),t.dem&&e.dem&&(e.dem.backfillBorder(t.dem,i,o),e.neighboringTiles&&e.neighboringTiles[a]&&(e.neighboringTiles[a].backfilled=!0)));}}getTile(e){return this.getTileByID(e.key)}getTileByID(e){return this._tiles[e]}_retainLoadedChildren(e,t,i,o){for(const r in this._tiles){let a=this._tiles[r];if(o[r]||!a.hasData()||a.tileID.overscaledZ<=t||a.tileID.overscaledZ>i)continue;let s=a.tileID;for(;a&&a.tileID.overscaledZ>t+1;){const e=a.tileID.scaledTo(a.tileID.overscaledZ-1);a=this._tiles[e.key],a&&a.hasData()&&(s=e);}let n=s;for(;n.overscaledZ>t;)if(n=n.scaledTo(n.overscaledZ-1),e[n.key]||e[n.canonical.key]){o[s.key]=s;break}}}findLoadedParent(e,t){if(e.key in this._loadedParentTiles){const i=this._loadedParentTiles[e.key];return i&&i.tileID.overscaledZ>=t?i:null}for(let i=e.overscaledZ-1;i>=t;i--){const t=e.scaledTo(i),o=this._getLoadedTile(t);if(o)return o}}findLoadedSibling(e){return this._getLoadedTile(e)}_getLoadedTile(e){const t=this._tiles[e.key];return t&&t.hasData()?t:this._cache.getByKey(e.wrapped().key)}updateCacheSize(e){const i=Math.ceil(e.width/this._source.tileSize)+1,o=Math.ceil(e.height/this._source.tileSize)+1,r=Math.floor(i*o*(null===this._maxTileCacheZoomLevels?t.a.MAX_TILE_CACHE_ZOOM_LEVELS:this._maxTileCacheZoomLevels)),a=\"number\"==typeof this._maxTileCacheSize?Math.min(this._maxTileCacheSize,r):r;this._cache.setMaxSize(a);}handleWrapJump(e){const t=Math.round((e-(void 0===this._prevLng?e:this._prevLng))/360);if(this._prevLng=e,t){const e={};for(const i in this._tiles){const o=this._tiles[i];o.tileID=o.tileID.unwrapTo(o.tileID.wrap+t),e[o.tileID.key]=o;}this._tiles=e;for(const e in this._timers)clearTimeout(this._timers[e]),delete this._timers[e];for(const e in this._tiles)this._setTileReloadTimer(e,this._tiles[e]);}}_updateCoveredAndRetainedTiles(e,t,i,o,r,a){const n={},l={},c=Object.keys(e),h=s.now();for(const i of c){const o=e[i],r=this._tiles[i];if(!r||0!==r.fadeEndTime&&r.fadeEndTime<=h)continue;const a=this.findLoadedParent(o,t),s=this.findLoadedSibling(o),c=a||s||null;c&&(this._addTile(c.tileID),n[c.tileID.key]=c.tileID),l[i]=o;}this._retainLoadedChildren(l,o,i,e);for(const t in n)e[t]||(this._coveredTiles[t]=!0,e[t]=n[t]);if(a){const t={},i={};for(const e of r)this._tiles[e.key].hasData()?t[e.key]=e:i[e.key]=e;for(const o in i){const r=i[o].children(this._source.maxzoom);this._tiles[r[0].key]&&this._tiles[r[1].key]&&this._tiles[r[2].key]&&this._tiles[r[3].key]&&(t[r[0].key]=e[r[0].key]=r[0],t[r[1].key]=e[r[1].key]=r[1],t[r[2].key]=e[r[2].key]=r[2],t[r[3].key]=e[r[3].key]=r[3],delete i[o]);}for(const o in i){const r=i[o],a=this.findLoadedParent(r,this._source.minzoom),s=this.findLoadedSibling(r),n=a||s||null;if(n){t[n.tileID.key]=e[n.tileID.key]=n.tileID;for(const e in t)t[e].isChildOf(n.tileID)&&delete t[e];}}for(const e in this._tiles)t[e]||(this._coveredTiles[e]=!0);}}update(e,i){if(!this._sourceLoaded||this._paused)return;let o;this.transform=e,this.terrain=i,this.updateCacheSize(e),this.handleWrapJump(this.transform.center.lng),this._coveredTiles={},this.used||this.usedForTerrain?this._source.tileID?o=e.getVisibleUnwrappedCoordinates(this._source.tileID).map((e=>new t.Z(e.canonical.z,e.wrap,e.canonical.z,e.canonical.x,e.canonical.y))):(o=ve(e,{tileSize:this.usedForTerrain?this.tileSize:this._source.tileSize,minzoom:this._source.minzoom,maxzoom:this._source.maxzoom,roundZoom:!this.usedForTerrain&&this._source.roundZoom,reparseOverscaled:this._source.reparseOverscaled,terrain:i,calculateTileZoom:this._source.calculateTileZoom}),this._source.hasTile&&(o=o.filter((e=>this._source.hasTile(e))))):o=[];const r=ge(e,this._source),a=Math.max(r-xe.maxOverzooming,this._source.minzoom),s=Math.max(r+xe.maxUnderzooming,this._source.minzoom);if(this.usedForTerrain){const e={};for(const t of o)if(t.canonical.z>this._source.minzoom){const i=t.scaledTo(t.canonical.z-1);e[i.key]=i;const o=t.scaledTo(Math.max(this._source.minzoom,Math.min(t.canonical.z,5)));e[o.key]=o;}o=o.concat(Object.values(e));}const n=0===o.length&&!this._updated&&this._didEmitContent;this._updated=!0,n&&this.fire(new t.l(\"data\",{sourceDataType:\"idle\",dataType:\"source\",sourceId:this.id}));const l=this._updateRetainedTiles(o,r);we(this._source.type)&&this._updateCoveredAndRetainedTiles(l,a,s,r,o,i);for(const e in l)this._tiles[e].clearFadeHold();const c=t.al(this._tiles,l);for(const e of c){const t=this._tiles[e];t.hasSymbolBuckets&&!t.holdingForFade()?t.setHoldDuration(this.map._fadeDuration):t.hasSymbolBuckets&&!t.symbolFadeFinished()||this._removeTile(e);}this._updateLoadedParentTileCache(),this._updateLoadedSiblingTileCache();}releaseSymbolFadeTiles(){for(const e in this._tiles)this._tiles[e].holdingForFade()&&this._removeTile(e);}_updateRetainedTiles(e,t){var i;const o={},r={},a=Math.max(t-xe.maxOverzooming,this._source.minzoom),s=Math.max(t+xe.maxUnderzooming,this._source.minzoom),n={};for(const i of e){const e=this._addTile(i);o[i.key]=i,e.hasData()||t<this._source.maxzoom&&(n[i.key]=i);}this._retainLoadedChildren(n,t,s,o);for(const s of e){let e=this._tiles[s.key];if(e.hasData())continue;if(t+1>this._source.maxzoom){const e=s.children(this._source.maxzoom)[0],t=this.getTile(e);if(t&&t.hasData()){o[e.key]=e;continue}}else {const e=s.children(this._source.maxzoom);if(o[e[0].key]&&o[e[1].key]&&o[e[2].key]&&o[e[3].key])continue}let n=e.wasRequested();for(let t=s.overscaledZ-1;t>=a;--t){const a=s.scaledTo(t);if(r[a.key])break;if(r[a.key]=!0,e=this.getTile(a),!e&&n&&(e=this._addTile(a)),e){const t=e.hasData();if((t||!(null===(i=this.map)||void 0===i?void 0:i.cancelPendingTileRequestsWhileZooming)||n)&&(o[a.key]=a),n=e.wasRequested(),t)break}}}return o}_updateLoadedParentTileCache(){this._loadedParentTiles={};for(const e in this._tiles){const t=[];let i,o=this._tiles[e].tileID;for(;o.overscaledZ>0;){if(o.key in this._loadedParentTiles){i=this._loadedParentTiles[o.key];break}t.push(o.key);const e=o.scaledTo(o.overscaledZ-1);if(i=this._getLoadedTile(e),i)break;o=e;}for(const e of t)this._loadedParentTiles[e]=i;}}_updateLoadedSiblingTileCache(){this._loadedSiblingTiles={};for(const e in this._tiles){const t=this._tiles[e].tileID,i=this._getLoadedTile(t);this._loadedSiblingTiles[t.key]=i;}}_addTile(e){let i=this._tiles[e.key];if(i)return i;i=this._cache.getAndRemove(e),i&&(this._setTileReloadTimer(e.key,i),i.tileID=e,this._state.initializeTileState(i,this.map?this.map.painter:null),this._cacheTimers[e.key]&&(clearTimeout(this._cacheTimers[e.key]),delete this._cacheTimers[e.key],this._setTileReloadTimer(e.key,i)));const o=i;return i||(i=new re(e,this._source.tileSize*e.overscaleFactor()),this._loadTile(i,e.key,i.state)),i.uses++,this._tiles[e.key]=i,o||this._source.fire(new t.l(\"dataloading\",{tile:i,coord:i.tileID,dataType:\"source\"})),i}_setTileReloadTimer(e,t){e in this._timers&&(clearTimeout(this._timers[e]),delete this._timers[e]);const i=t.getExpiryTimeout();i&&(this._timers[e]=setTimeout((()=>{this._reloadTile(e,\"expired\"),delete this._timers[e];}),i));}refreshTiles(e){for(const t in this._tiles)(this._isIdRenderable(t)||\"errored\"==this._tiles[t].state)&&e.some((e=>e.equals(this._tiles[t].tileID.canonical)))&&this._reloadTile(t,\"expired\");}_removeTile(e){const t=this._tiles[e];t&&(t.uses--,delete this._tiles[e],this._timers[e]&&(clearTimeout(this._timers[e]),delete this._timers[e]),t.uses>0||(t.hasData()&&\"reloading\"!==t.state?this._cache.add(t.tileID,t,t.getExpiryTimeout()):(t.aborted=!0,this._abortTile(t),this._unloadTile(t))));}_dataHandler(e){const t=e.sourceDataType;\"source\"===e.dataType&&\"metadata\"===t&&(this._sourceLoaded=!0),this._sourceLoaded&&!this._paused&&\"source\"===e.dataType&&\"content\"===t&&(this.reload(e.sourceDataChanged),this.transform&&this.update(this.transform,this.terrain),this._didEmitContent=!0);}clearTiles(){this._shouldReloadOnResume=!1,this._paused=!1;for(const e in this._tiles)this._removeTile(e);this._cache.reset();}tilesIn(e,i,o){const r=[],a=this.transform;if(!a)return r;const s=a.getCoveringTilesDetailsProvider().allowWorldCopies(),n=o?a.getCameraQueryGeometry(e):e,l=e=>a.screenPointToMercatorCoordinate(e,this.terrain),c=this.transformBbox(e,l,!s),h=this.transformBbox(n,l,!s),u=this.getIds(),d=t.a1.fromPoints(h);for(let e=0;e<u.length;e++){const o=this._tiles[u[e]];if(o.holdingForFade())continue;const n=s?[o.tileID]:[o.tileID.unwrapTo(-1),o.tileID.unwrapTo(0)],l=Math.pow(2,a.zoom-o.tileID.overscaledZ),_=i*o.queryPadding*t.$/o.tileSize/l;for(const e of n){const i=d.map((i=>e.getTilePoint(new t.a0(i.x,i.y))));if(i.expandBy(_),i.intersects(be)){const t=c.map((t=>e.getTilePoint(t))),i=h.map((t=>e.getTilePoint(t)));r.push({tile:o,tileID:s?e:e.unwrapTo(0),queryGeometry:t,cameraQueryGeometry:i,scale:l});}}}return r}transformBbox(e,i,o){let r=e.map(i);if(o){const o=t.a1.fromPoints(e);o.shrinkBy(.001*Math.min(o.width(),o.height()));const a=o.map(i);t.a1.fromPoints(r).covers(a)||(r=r.map((e=>e.x>.5?new t.a0(e.x-1,e.y,e.z):e)));}return r}getVisibleCoordinates(e){const t=this.getRenderableIds(e).map((e=>this._tiles[e].tileID));return this.transform&&this.transform.populateCache(t),t}hasTransition(){if(this._source.hasTransition())return !0;if(we(this._source.type)){const e=s.now();for(const t in this._tiles)if(this._tiles[t].fadeEndTime>=e)return !0}return !1}setFeatureState(e,t,i){this._state.updateState(e=e||\"_geojsonTileLayer\",t,i);}removeFeatureState(e,t,i){this._state.removeFeatureState(e=e||\"_geojsonTileLayer\",t,i);}getFeatureState(e,t){return this._state.getState(e=e||\"_geojsonTileLayer\",t)}setDependencies(e,t,i){const o=this._tiles[e];o&&o.setDependencies(t,i);}reloadTilesForDependencies(e,t){for(const i in this._tiles)this._tiles[i].hasDependency(e,t)&&this._reloadTile(i,\"reloading\");this._cache.filter((i=>!i.hasDependency(e,t)));}}function ye(e,t){const i=Math.abs(2*e.wrap)-+(e.wrap<0),o=Math.abs(2*t.wrap)-+(t.wrap<0);return e.overscaledZ-t.overscaledZ||o-i||t.canonical.y-e.canonical.y||t.canonical.x-e.canonical.x}function we(e){return \"raster\"===e||\"image\"===e||\"video\"===e}xe.maxOverzooming=10,xe.maxUnderzooming=3;class Te{constructor(e,t){this.reset(e,t);}reset(e,t){this.points=e||[],this._distances=[0];for(let e=1;e<this.points.length;e++)this._distances[e]=this._distances[e-1]+this.points[e].dist(this.points[e-1]);this.length=this._distances[this._distances.length-1],this.padding=Math.min(t||0,.5*this.length),this.paddedLength=this.length-2*this.padding;}lerp(e){if(1===this.points.length)return this.points[0];e=t.ag(e,0,1);let i=1,o=this._distances[i];const r=e*this.paddedLength+this.padding;for(;o<r&&i<this._distances.length;)o=this._distances[++i];const a=i-1,s=this._distances[a],n=o-s,l=n>0?(r-s)/n:0;return this.points[a].mult(1-l).add(this.points[i].mult(l))}}function Pe(e,t){let i=!0;return \"always\"===e||\"never\"!==e&&\"never\"!==t||(i=!1),i}class Ce{constructor(e,t,i){const o=this.boxCells=[],r=this.circleCells=[];this.xCellCount=Math.ceil(e/i),this.yCellCount=Math.ceil(t/i);for(let e=0;e<this.xCellCount*this.yCellCount;e++)o.push([]),r.push([]);this.circleKeys=[],this.boxKeys=[],this.bboxes=[],this.circles=[],this.width=e,this.height=t,this.xScale=this.xCellCount/e,this.yScale=this.yCellCount/t,this.boxUid=0,this.circleUid=0;}keysLength(){return this.boxKeys.length+this.circleKeys.length}insert(e,t,i,o,r){this._forEachCell(t,i,o,r,this._insertBoxCell,this.boxUid++),this.boxKeys.push(e),this.bboxes.push(t),this.bboxes.push(i),this.bboxes.push(o),this.bboxes.push(r);}insertCircle(e,t,i,o){this._forEachCell(t-o,i-o,t+o,i+o,this._insertCircleCell,this.circleUid++),this.circleKeys.push(e),this.circles.push(t),this.circles.push(i),this.circles.push(o);}_insertBoxCell(e,t,i,o,r,a){this.boxCells[r].push(a);}_insertCircleCell(e,t,i,o,r,a){this.circleCells[r].push(a);}_query(e,t,i,o,r,a,s){if(i<0||e>this.width||o<0||t>this.height)return [];const n=[];if(e<=0&&t<=0&&this.width<=i&&this.height<=o){if(r)return [{key:null,x1:e,y1:t,x2:i,y2:o}];for(let e=0;e<this.boxKeys.length;e++)n.push({key:this.boxKeys[e],x1:this.bboxes[4*e],y1:this.bboxes[4*e+1],x2:this.bboxes[4*e+2],y2:this.bboxes[4*e+3]});for(let e=0;e<this.circleKeys.length;e++){const t=this.circles[3*e],i=this.circles[3*e+1],o=this.circles[3*e+2];n.push({key:this.circleKeys[e],x1:t-o,y1:i-o,x2:t+o,y2:i+o});}}else this._forEachCell(e,t,i,o,this._queryCell,n,{hitTest:r,overlapMode:a,seenUids:{box:{},circle:{}}},s);return n}query(e,t,i,o){return this._query(e,t,i,o,!1,null)}hitTest(e,t,i,o,r,a){return this._query(e,t,i,o,!0,r,a).length>0}hitTestCircle(e,t,i,o,r){const a=e-i,s=e+i,n=t-i,l=t+i;if(s<0||a>this.width||l<0||n>this.height)return !1;const c=[];return this._forEachCell(a,n,s,l,this._queryCellCircle,c,{hitTest:!0,overlapMode:o,circle:{x:e,y:t,radius:i},seenUids:{box:{},circle:{}}},r),c.length>0}_queryCell(e,t,i,o,r,a,s,n){const{seenUids:l,hitTest:c,overlapMode:h}=s,u=this.boxCells[r];if(null!==u){const r=this.bboxes;for(const s of u)if(!l.box[s]){l.box[s]=!0;const u=4*s,d=this.boxKeys[s];if(e<=r[u+2]&&t<=r[u+3]&&i>=r[u+0]&&o>=r[u+1]&&(!n||n(d))&&(!c||!Pe(h,d.overlapMode))&&(a.push({key:d,x1:r[u],y1:r[u+1],x2:r[u+2],y2:r[u+3]}),c))return !0}}const d=this.circleCells[r];if(null!==d){const r=this.circles;for(const s of d)if(!l.circle[s]){l.circle[s]=!0;const u=3*s,d=this.circleKeys[s];if(this._circleAndRectCollide(r[u],r[u+1],r[u+2],e,t,i,o)&&(!n||n(d))&&(!c||!Pe(h,d.overlapMode))){const e=r[u],t=r[u+1],i=r[u+2];if(a.push({key:d,x1:e-i,y1:t-i,x2:e+i,y2:t+i}),c)return !0}}}return !1}_queryCellCircle(e,t,i,o,r,a,s,n){const{circle:l,seenUids:c,overlapMode:h}=s,u=this.boxCells[r];if(null!==u){const e=this.bboxes;for(const t of u)if(!c.box[t]){c.box[t]=!0;const i=4*t,o=this.boxKeys[t];if(this._circleAndRectCollide(l.x,l.y,l.radius,e[i+0],e[i+1],e[i+2],e[i+3])&&(!n||n(o))&&!Pe(h,o.overlapMode))return a.push(!0),!0}}const d=this.circleCells[r];if(null!==d){const e=this.circles;for(const t of d)if(!c.circle[t]){c.circle[t]=!0;const i=3*t,o=this.circleKeys[t];if(this._circlesCollide(e[i],e[i+1],e[i+2],l.x,l.y,l.radius)&&(!n||n(o))&&!Pe(h,o.overlapMode))return a.push(!0),!0}}}_forEachCell(e,t,i,o,r,a,s,n){const l=this._convertToXCellCoord(e),c=this._convertToYCellCoord(t),h=this._convertToXCellCoord(i),u=this._convertToYCellCoord(o);for(let d=l;d<=h;d++)for(let l=c;l<=u;l++)if(r.call(this,e,t,i,o,this.xCellCount*l+d,a,s,n))return}_convertToXCellCoord(e){return Math.max(0,Math.min(this.xCellCount-1,Math.floor(e*this.xScale)))}_convertToYCellCoord(e){return Math.max(0,Math.min(this.yCellCount-1,Math.floor(e*this.yScale)))}_circlesCollide(e,t,i,o,r,a){const s=o-e,n=r-t,l=i+a;return l*l>s*s+n*n}_circleAndRectCollide(e,t,i,o,r,a,s){const n=(a-o)/2,l=Math.abs(e-(o+n));if(l>n+i)return !1;const c=(s-r)/2,h=Math.abs(t-(r+c));if(h>c+i)return !1;if(l<=n||h<=c)return !0;const u=l-n,d=h-c;return u*u+d*d<=i*i}}function Ie(e,i,r){const a=t.L();if(!e){const{vecSouth:e,vecEast:t}=Se(i),r=o();r[0]=t[0],r[1]=t[1],r[2]=e[0],r[3]=e[1],s=r,(d=(l=(n=r)[0])*(u=n[3])-(h=n[2])*(c=n[1]))&&(s[0]=u*(d=1/d),s[1]=-c*d,s[2]=-h*d,s[3]=l*d),a[0]=r[0],a[1]=r[1],a[4]=r[2],a[5]=r[3];}var s,n,l,c,h,u,d;return t.N(a,a,[1/r,1/r,1]),a}function Me(e,i,o,r){if(e){const e=t.L();if(!i){const{vecSouth:t,vecEast:i}=Se(o);e[0]=i[0],e[1]=i[1],e[4]=t[0],e[5]=t[1];}return t.N(e,e,[r,r,1]),e}return o.pixelsToClipSpaceMatrix}function Se(e){const i=Math.cos(e.rollInRadians),o=Math.sin(e.rollInRadians),r=Math.cos(e.pitchInRadians),a=Math.cos(e.bearingInRadians),s=Math.sin(e.bearingInRadians),n=t.aq();n[0]=-a*r*o-s*i,n[1]=-s*r*o+a*i;const l=t.ar(n);l<1e-9?t.as(n):t.at(n,n,1/l);const c=t.aq();c[0]=a*r*i-s*o,c[1]=s*r*i+a*o;const h=t.ar(c);return h<1e-9?t.as(c):t.at(c,c,1/h),{vecEast:c,vecSouth:n}}function Ee(e,i,o,r){let a;r?(a=[e,i,r(e,i),1],t.av(a,a,o)):(a=[e,i,0,1],qe(a,a,o));const s=a[3];return {point:new t.P(a[0]/s,a[1]/s),signedDistanceFromCamera:s,isOccluded:!1}}function Re(e,t){return .5+e/t*.5}function ze(e,t){return e.x>=-t[0]&&e.x<=t[0]&&e.y>=-t[1]&&e.y<=t[1]}function De(e,i,o,r,a,s,n,l,c,h,u,d,_){const p=o?e.textSizeData:e.iconSizeData,m=t.am(p,i.transform.zoom),f=[256/i.width*2+1,256/i.height*2+1],g=o?e.text.dynamicLayoutVertexArray:e.icon.dynamicLayoutVertexArray;g.clear();const v=e.lineVertexArray,b=o?e.text.placedSymbolArray:e.icon.placedSymbolArray,x=i.transform.width/i.transform.height;let y=!1;for(let o=0;o<b.length;o++){const w=b.get(o);if(w.hidden||w.writingMode===t.an.vertical&&!y){$e(w.numGlyphs,g);continue}y=!1;const T=new t.P(w.anchorX,w.anchorY),P={getElevation:_,pitchedLabelPlaneMatrix:r,lineVertexArray:v,pitchWithMap:s,projectionCache:{projections:{},offsets:{},cachedAnchorPoint:void 0,anyProjectionOccluded:!1},transform:i.transform,tileAnchorPoint:T,unwrappedTileID:c,width:h,height:u,translation:d},C=Ne(w.anchorX,w.anchorY,P);if(!ze(C.point,f)){$e(w.numGlyphs,g);continue}const I=Re(i.transform.cameraToCenterDistance,C.signedDistanceFromCamera),M=t.ao(p,m,w),S=s?M*i.transform.getPitchedTextCorrection(w.anchorX,w.anchorY,c)/I:M*I,E=ke({projectionContext:P,pitchedLabelPlaneMatrixInverse:a,symbol:w,fontSize:S,flip:!1,keepUpright:n,glyphOffsetArray:e.glyphOffsetArray,dynamicLayoutVertexArray:g,aspectRatio:x,rotateToLine:l});y=E.useVertical,(E.notEnoughRoom||y||E.needsFlipping&&ke({projectionContext:P,pitchedLabelPlaneMatrixInverse:a,symbol:w,fontSize:S,flip:!0,keepUpright:n,glyphOffsetArray:e.glyphOffsetArray,dynamicLayoutVertexArray:g,aspectRatio:x,rotateToLine:l}).notEnoughRoom)&&$e(w.numGlyphs,g);}o?e.text.dynamicLayoutVertexBuffer.updateData(g):e.icon.dynamicLayoutVertexBuffer.updateData(g);}function Ae(e,t,i,o,r,a,s,n){const l=a.glyphStartIndex+a.numGlyphs,c=a.lineStartIndex,h=a.lineStartIndex+a.lineLength,u=t.getoffsetX(a.glyphStartIndex),d=t.getoffsetX(l-1),_=Ge(e*u,i,o,r,a.segment,c,h,n,s);if(!_)return null;const p=Ge(e*d,i,o,r,a.segment,c,h,n,s);return p?n.projectionCache.anyProjectionOccluded?null:{first:_,last:p}:null}function Le(e,i,o,r){return e===t.an.horizontal&&Math.abs(o.y-i.y)>Math.abs(o.x-i.x)*r?{useVertical:!0}:(e===t.an.vertical?i.y<o.y:i.x>o.x)?{needsFlipping:!0}:null}function ke(e){const{projectionContext:i,pitchedLabelPlaneMatrixInverse:o,symbol:r,fontSize:a,flip:s,keepUpright:n,glyphOffsetArray:l,dynamicLayoutVertexArray:c,aspectRatio:h,rotateToLine:u}=e,d=a/24,_=r.lineOffsetX*d,p=r.lineOffsetY*d;let m;if(r.numGlyphs>1){const e=r.glyphStartIndex+r.numGlyphs,t=r.lineStartIndex,a=r.lineStartIndex+r.lineLength,c=Ae(d,l,_,p,s,r,u,i);if(!c)return {notEnoughRoom:!0};const f=je(c.first.point.x,c.first.point.y,i,o),g=je(c.last.point.x,c.last.point.y,i,o);if(n&&!s){const e=Le(r.writingMode,f,g,h);if(e)return e}m=[c.first];for(let o=r.glyphStartIndex+1;o<e-1;o++){const e=Ge(d*l.getoffsetX(o),_,p,s,r.segment,t,a,i,u);if(!e)return {notEnoughRoom:!0};m.push(e);}m.push(c.last);}else {if(n&&!s){const e=Oe(i.tileAnchorPoint.x,i.tileAnchorPoint.y,i).point,a=r.lineStartIndex+r.segment+1,s=new t.P(i.lineVertexArray.getx(a),i.lineVertexArray.gety(a)),n=Oe(s.x,s.y,i),l=n.signedDistanceFromCamera>0?n.point:Fe(i.tileAnchorPoint,s,e,1,i),c=je(e.x,e.y,i,o),u=je(l.x,l.y,i,o),d=Le(r.writingMode,c,u,h);if(d)return d}const e=Ge(d*l.getoffsetX(r.glyphStartIndex),_,p,s,r.segment,r.lineStartIndex,r.lineStartIndex+r.lineLength,i,u);if(!e||i.projectionCache.anyProjectionOccluded)return {notEnoughRoom:!0};m=[e];}for(const e of m)t.au(c,e.point,e.angle);return {}}function Fe(e,t,i,o,r){const a=e.add(e.sub(t)._unit()),s=Oe(a.x,a.y,r).point,n=i.sub(s);return i.add(n._mult(o/n.mag()))}function Be(e,i,o){const r=i.projectionCache;if(r.projections[e])return r.projections[e];const a=new t.P(i.lineVertexArray.getx(e),i.lineVertexArray.gety(e)),s=Oe(a.x,a.y,i);if(s.signedDistanceFromCamera>0)return r.projections[e]=s.point,r.anyProjectionOccluded=r.anyProjectionOccluded||s.isOccluded,s.point;const n=e-o.direction;return Fe(0===o.distanceFromAnchor?i.tileAnchorPoint:new t.P(i.lineVertexArray.getx(n),i.lineVertexArray.gety(n)),a,o.previousVertex,o.absOffsetX-o.distanceFromAnchor+1,i)}function Oe(e,t,i){const o=e+i.translation[0],r=t+i.translation[1];let a;return i.pitchWithMap?(a=Ee(o,r,i.pitchedLabelPlaneMatrix,i.getElevation),a.isOccluded=!1):(a=i.transform.projectTileCoordinates(o,r,i.unwrappedTileID,i.getElevation),a.point.x=(.5*a.point.x+.5)*i.width,a.point.y=(.5*-a.point.y+.5)*i.height),a}function je(e,i,o,r){if(o.pitchWithMap){const a=[e,i,0,1];return t.av(a,a,r),o.transform.projectTileCoordinates(a[0]/a[3],a[1]/a[3],o.unwrappedTileID,o.getElevation).point}return {x:e/o.width*2-1,y:i/o.height*2-1}}function Ne(e,t,i){return i.transform.projectTileCoordinates(e,t,i.unwrappedTileID,i.getElevation)}function Ze(e,t,i){return e._unit()._perp()._mult(t*i)}function Ue(e,i,o,r,a,s,n,l,c){if(l.projectionCache.offsets[e])return l.projectionCache.offsets[e];const h=o.add(i);if(e+c.direction<r||e+c.direction>=a)return l.projectionCache.offsets[e]=h,h;const u=Be(e+c.direction,l,c),d=Ze(u.sub(o),n,c.direction),_=o.add(d),p=u.add(d);return l.projectionCache.offsets[e]=t.aw(s,h,_,p)||h,l.projectionCache.offsets[e]}function Ge(e,t,i,o,r,a,s,n,l){const c=o?e-t:e+t;let h=c>0?1:-1,u=0;o&&(h*=-1,u=Math.PI),h<0&&(u+=Math.PI);let d,_=h>0?a+r:a+r+1;n.projectionCache.cachedAnchorPoint?d=n.projectionCache.cachedAnchorPoint:(d=Oe(n.tileAnchorPoint.x,n.tileAnchorPoint.y,n).point,n.projectionCache.cachedAnchorPoint=d);let p,m,f=d,g=d,v=0,b=0;const x=Math.abs(c),y=[];let w;for(;v+b<=x;){if(_+=h,_<a||_>=s)return null;v+=b,g=f,m=p;const e={absOffsetX:x,direction:h,distanceFromAnchor:v,previousVertex:g};if(f=Be(_,n,e),0===i)y.push(g),w=f.sub(g);else {let t;const o=f.sub(g);t=0===o.mag()?Ze(Be(_+h,n,e).sub(f),i,h):Ze(o,i,h),m||(m=g.add(t)),p=Ue(_,t,f,a,s,m,i,n,e),y.push(m),w=p.sub(m);}b=w.mag();}const T=w._mult((x-v)/b)._add(m||g),P=u+Math.atan2(f.y-g.y,f.x-g.x);return y.push(T),{point:T,angle:l?P:0,path:y}}const Ve=new Float32Array([-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0]);function $e(e,t){for(let i=0;i<e;i++){const e=t.length;t.resize(e+4),t.float32.set(Ve,3*e);}}function qe(e,t,i){const o=t[0],r=t[1];return e[0]=i[0]*o+i[4]*r+i[12],e[1]=i[1]*o+i[5]*r+i[13],e[3]=i[3]*o+i[7]*r+i[15],e}const We=100;class He{constructor(e,t=new Ce(e.width+200,e.height+200,25),i=new Ce(e.width+200,e.height+200,25)){this.transform=e,this.grid=t,this.ignoredGrid=i,this.pitchFactor=Math.cos(e.pitch*Math.PI/180)*e.cameraToCenterDistance,this.screenRightBoundary=e.width+We,this.screenBottomBoundary=e.height+We,this.gridRightBoundary=e.width+200,this.gridBottomBoundary=e.height+200,this.perspectiveRatioCutoff=.6;}placeCollisionBox(e,t,i,o,r,a,s,n,l,c,h,u){const d=this.projectAndGetPerspectiveRatio(e.anchorPointX+n[0],e.anchorPointY+n[1],r,c,u),_=i*d.perspectiveRatio;let p;if(a||s)p=this._projectCollisionBox(e,_,o,r,a,s,n,d,c,h,u);else {const t=d.x+(h?h.x*_:0),i=d.y+(h?h.y*_:0);p={allPointsOccluded:!1,box:[t+e.x1*_,i+e.y1*_,t+e.x2*_,i+e.y2*_]};}const[m,f,g,v]=p.box,b=a?p.allPointsOccluded:d.isOccluded;let x=b;return x||(x=d.perspectiveRatio<this.perspectiveRatioCutoff),x||(x=!this.isInsideGrid(m,f,g,v)),x||\"always\"!==t&&this.grid.hitTest(m,f,g,v,t,l)?{box:[m,f,g,v],placeable:!1,offscreen:!1,occluded:b}:{box:[m,f,g,v],placeable:!0,offscreen:this.isOffscreen(m,f,g,v),occluded:b}}placeCollisionCircles(e,i,o,r,a,s,n,l,c,h,u,d,_,p){const m=[],f=new t.P(i.anchorX,i.anchorY),g=this.getPerspectiveRatio(f.x,f.y,s,p),v=(c?a*this.transform.getPitchedTextCorrection(i.anchorX,i.anchorY,s)/g:a*g)/t.aA,b={getElevation:p,pitchedLabelPlaneMatrix:n,lineVertexArray:o,pitchWithMap:c,projectionCache:{projections:{},offsets:{},cachedAnchorPoint:void 0,anyProjectionOccluded:!1},transform:this.transform,tileAnchorPoint:f,unwrappedTileID:s,width:this.transform.width,height:this.transform.height,translation:_},x=Ae(v,r,i.lineOffsetX*v,i.lineOffsetY*v,!1,i,!1,b);let y=!1,w=!1,T=!0;if(x){const i=.5*u*g+d,o=new t.P(-100,-100),r=new t.P(this.screenRightBoundary,this.screenBottomBoundary),a=new Te,s=x.first,n=x.last;let _=[];for(let e=s.path.length-1;e>=1;e--)_.push(s.path[e]);for(let e=1;e<n.path.length;e++)_.push(n.path[e]);const p=2.5*i;if(c){const e=this.projectPathToScreenSpace(_,b);_=e.some((e=>e.signedDistanceFromCamera<=0))?[]:e.map((e=>e.point));}let f=[];if(_.length>0){const e=_[0].clone(),i=_[0].clone();for(let t=1;t<_.length;t++)e.x=Math.min(e.x,_[t].x),e.y=Math.min(e.y,_[t].y),i.x=Math.max(i.x,_[t].x),i.y=Math.max(i.y,_[t].y);f=e.x>=o.x&&i.x<=r.x&&e.y>=o.y&&i.y<=r.y?[_]:i.x<o.x||e.x>r.x||i.y<o.y||e.y>r.y?[]:t.ax([_],o.x,o.y,r.x,r.y);}for(const t of f){a.reset(t,.25*i);let o=0;o=a.length<=.5*i?1:Math.ceil(a.paddedLength/p)+1;for(let t=0;t<o;t++){const r=t/Math.max(o-1,1),s=a.lerp(r),n=s.x+We,c=s.y+We;m.push(n,c,i,0);const u=n-i,d=c-i,_=n+i,p=c+i;if(T=T&&this.isOffscreen(u,d,_,p),w=w||this.isInsideGrid(u,d,_,p),\"always\"!==e&&this.grid.hitTestCircle(n,c,i,e,h)&&(y=!0,!l))return {circles:[],offscreen:!1,collisionDetected:y}}}}return {circles:!l&&y||!w||g<this.perspectiveRatioCutoff?[]:m,offscreen:T,collisionDetected:y}}projectPathToScreenSpace(e,i){const o=function(e,i){const o=t.L();return t.ap(o,i.pitchedLabelPlaneMatrix),e.map((e=>{const t=Ee(e.x,e.y,o,i.getElevation),r=i.transform.projectTileCoordinates(t.point.x,t.point.y,i.unwrappedTileID,i.getElevation);return r.point.x=(.5*r.point.x+.5)*i.width,r.point.y=(.5*-r.point.y+.5)*i.height,r}))}(e,i);return function(e){let t=0,i=0,o=0,r=0;for(let a=0;a<e.length;a++)e[a].isOccluded?(o=a+1,r=0):(r++,r>i&&(i=r,t=o));return e.slice(t,t+i)}(o)}queryRenderedSymbols(e){if(0===e.length||0===this.grid.keysLength()&&0===this.ignoredGrid.keysLength())return {};const i=[],o=new t.a1;for(const r of e){const e=new t.P(r.x+We,r.y+We);o.extend(e),i.push(e);}const{minX:r,minY:a,maxX:s,maxY:n}=o,l=this.grid.query(r,a,s,n).concat(this.ignoredGrid.query(r,a,s,n)),c={},h={};for(const e of l){const o=e.key;if(void 0===c[o.bucketInstanceId]&&(c[o.bucketInstanceId]={}),c[o.bucketInstanceId][o.featureIndex])continue;const r=[new t.P(e.x1,e.y1),new t.P(e.x2,e.y1),new t.P(e.x2,e.y2),new t.P(e.x1,e.y2)];t.ay(i,r)&&(c[o.bucketInstanceId][o.featureIndex]=!0,void 0===h[o.bucketInstanceId]&&(h[o.bucketInstanceId]=[]),h[o.bucketInstanceId].push(o.featureIndex));}return h}insertCollisionBox(e,t,i,o,r,a){(i?this.ignoredGrid:this.grid).insert({bucketInstanceId:o,featureIndex:r,collisionGroupID:a,overlapMode:t},e[0],e[1],e[2],e[3]);}insertCollisionCircles(e,t,i,o,r,a){const s=i?this.ignoredGrid:this.grid,n={bucketInstanceId:o,featureIndex:r,collisionGroupID:a,overlapMode:t};for(let t=0;t<e.length;t+=4)s.insertCircle(n,e[t],e[t+1],e[t+2]);}projectAndGetPerspectiveRatio(e,i,o,r,a){if(a){let o;r?(o=[e,i,r(e,i),1],t.av(o,o,a)):(o=[e,i,0,1],qe(o,o,a));const s=o[3];return {x:(o[0]/s+1)/2*this.transform.width+We,y:(-o[1]/s+1)/2*this.transform.height+We,perspectiveRatio:.5+this.transform.cameraToCenterDistance/s*.5,isOccluded:!1,signedDistanceFromCamera:s}}{const t=this.transform.projectTileCoordinates(e,i,o,r);return {x:(t.point.x+1)/2*this.transform.width+We,y:(1-t.point.y)/2*this.transform.height+We,perspectiveRatio:.5+this.transform.cameraToCenterDistance/t.signedDistanceFromCamera*.5,isOccluded:t.isOccluded,signedDistanceFromCamera:t.signedDistanceFromCamera}}}getPerspectiveRatio(e,t,i,o){const r=this.transform.projectTileCoordinates(e,t,i,o);return .5+this.transform.cameraToCenterDistance/r.signedDistanceFromCamera*.5}isOffscreen(e,t,i,o){return i<We||e>=this.screenRightBoundary||o<We||t>this.screenBottomBoundary}isInsideGrid(e,t,i,o){return i>=0&&e<this.gridRightBoundary&&o>=0&&t<this.gridBottomBoundary}getViewportMatrix(){const e=t.af([]);return t.M(e,e,[-100,-100,0]),e}_projectCollisionBox(e,i,o,r,a,s,n,l,c,h,u){let d=1,_=0,p=0,m=1;const f=e.anchorPointX+n[0],g=e.anchorPointY+n[1];if(s&&!a){const e=this.projectAndGetPerspectiveRatio(f+1,g,r,c,u),t=e.x-l.x,i=Math.atan((e.y-l.y)/t)+(t<0?Math.PI:0),o=Math.sin(i),a=Math.cos(i);d=a,_=o,p=-o,m=a;}else if(!s&&a){const e=Se(this.transform);d=e.vecEast[0],_=e.vecEast[1],p=e.vecSouth[0],m=e.vecSouth[1];}let v=l.x,b=l.y,x=i;a&&(v=f,b=g,x=Math.pow(2,-(this.transform.zoom-o.overscaledZ)),x*=this.transform.getPitchedTextCorrection(f,g,r),h||(x*=t.ag(.5+l.signedDistanceFromCamera/this.transform.cameraToCenterDistance*.5,0,4))),h&&(v+=d*h.x*x+p*h.y*x,b+=_*h.x*x+m*h.y*x);const y=e.x1*x,w=e.x2*x,T=(y+w)/2,P=e.y1*x,C=e.y2*x,I=(P+C)/2,M=[{offsetX:y,offsetY:P},{offsetX:T,offsetY:P},{offsetX:w,offsetY:P},{offsetX:w,offsetY:I},{offsetX:w,offsetY:C},{offsetX:T,offsetY:C},{offsetX:y,offsetY:C},{offsetX:y,offsetY:I}];let S=[];for(const{offsetX:e,offsetY:i}of M)S.push(new t.P(v+d*e+p*i,b+_*e+m*i));let E=!1;if(a){const e=S.map((e=>this.projectAndGetPerspectiveRatio(e.x,e.y,r,c,u)));E=e.some((e=>!e.isOccluded)),S=e.map((e=>new t.P(e.x,e.y)));}else E=!0;return {box:t.az(S),allPointsOccluded:!E}}}class Xe{constructor(e,t,i,o){this.opacity=e?Math.max(0,Math.min(1,e.opacity+(e.placed?t:-t))):o&&i?1:0,this.placed=i;}isHidden(){return 0===this.opacity&&!this.placed}}class Ke{constructor(e,t,i,o,r){this.text=new Xe(e?e.text:null,t,i,r),this.icon=new Xe(e?e.icon:null,t,o,r);}isHidden(){return this.text.isHidden()&&this.icon.isHidden()}}class Ye{constructor(e,t,i){this.text=e,this.icon=t,this.skipFade=i;}}class Qe{constructor(e,t,i,o,r){this.bucketInstanceId=e,this.featureIndex=t,this.sourceLayerIndex=i,this.bucketIndex=o,this.tileID=r;}}class Je{constructor(e){this.crossSourceCollisions=e,this.maxGroupID=0,this.collisionGroups={};}get(e){if(this.crossSourceCollisions)return {ID:0,predicate:null};if(!this.collisionGroups[e]){const t=++this.maxGroupID;this.collisionGroups[e]={ID:t,predicate:e=>e.collisionGroupID===t};}return this.collisionGroups[e]}}function et(e,i,o,r,a){const{horizontalAlign:s,verticalAlign:n}=t.aG(e);return new t.P(-(s-.5)*i+r[0]*a,-(n-.5)*o+r[1]*a)}class tt{constructor(e,t,i,o,r){this.transform=e.clone(),this.terrain=t,this.collisionIndex=new He(this.transform),this.placements={},this.opacities={},this.variableOffsets={},this.stale=!1,this.commitTime=0,this.fadeDuration=i,this.retainedQueryData={},this.collisionGroups=new Je(o),this.collisionCircleArrays={},this.collisionBoxArrays=new Map,this.prevPlacement=r,r&&(r.prevPlacement=void 0),this.placedOrientations={};}_getTerrainElevationFunc(e){const t=this.terrain;return t?(i,o)=>t.getElevation(e,i,o):null}getBucketParts(e,i,o,r){const a=o.getBucket(i),s=o.latestFeatureIndex;if(!a||!s||i.id!==a.layerIds[0])return;const n=o.collisionBoxArray,l=a.layers[0].layout,c=a.layers[0].paint,h=Math.pow(2,this.transform.zoom-o.tileID.overscaledZ),u=o.tileSize/t.$,d=o.tileID.toUnwrapped(),_=\"map\"===l.get(\"text-rotation-alignment\"),p=t.aB(o,1,this.transform.zoom),m=t.aC(this.collisionIndex.transform,o,c.get(\"text-translate\"),c.get(\"text-translate-anchor\")),f=t.aC(this.collisionIndex.transform,o,c.get(\"icon-translate\"),c.get(\"icon-translate-anchor\")),g=Ie(_,this.transform,p);this.retainedQueryData[a.bucketInstanceId]=new Qe(a.bucketInstanceId,s,a.sourceLayerIndex,a.index,o.tileID);const v={bucket:a,layout:l,translationText:m,translationIcon:f,unwrappedTileID:d,pitchedLabelPlaneMatrix:g,scale:h,textPixelRatio:u,holdingForFade:o.holdingForFade(),collisionBoxArray:n,partiallyEvaluatedTextSize:t.am(a.textSizeData,this.transform.zoom),collisionGroup:this.collisionGroups.get(a.sourceID)};if(r)for(const t of a.sortKeyRanges){const{sortKey:i,symbolInstanceStart:o,symbolInstanceEnd:r}=t;e.push({sortKey:i,symbolInstanceStart:o,symbolInstanceEnd:r,parameters:v});}else e.push({symbolInstanceStart:0,symbolInstanceEnd:a.symbolInstances.length,parameters:v});}attemptAnchorPlacement(e,i,o,r,a,s,n,l,c,h,u,d,_,p,m,f,g,v,b,x){const y=t.aD[e.textAnchor],w=[e.textOffset0,e.textOffset1],T=et(y,o,r,w,a),P=this.collisionIndex.placeCollisionBox(i,d,l,c,h,n,s,f,u.predicate,b,T,x);if((!v||this.collisionIndex.placeCollisionBox(v,d,l,c,h,n,s,g,u.predicate,b,T,x).placeable)&&P.placeable){let e;if(this.prevPlacement&&this.prevPlacement.variableOffsets[_.crossTileID]&&this.prevPlacement.placements[_.crossTileID]&&this.prevPlacement.placements[_.crossTileID].text&&(e=this.prevPlacement.variableOffsets[_.crossTileID].anchor),0===_.crossTileID)throw new Error(\"symbolInstance.crossTileID can't be 0\");return this.variableOffsets[_.crossTileID]={textOffset:w,width:o,height:r,anchor:y,textBoxScale:a,prevAnchor:e},this.markUsedJustification(p,y,_,m),p.allowVerticalPlacement&&(this.markUsedOrientation(p,m,_),this.placedOrientations[_.crossTileID]=m),{shift:T,placedGlyphBoxes:P}}}placeLayerBucketPart(e,i,o){const{bucket:r,layout:a,translationText:s,translationIcon:n,unwrappedTileID:l,pitchedLabelPlaneMatrix:c,textPixelRatio:h,holdingForFade:u,collisionBoxArray:d,partiallyEvaluatedTextSize:_,collisionGroup:p}=e.parameters,m=a.get(\"text-optional\"),f=a.get(\"icon-optional\"),g=t.aE(a,\"text-overlap\",\"text-allow-overlap\"),v=\"always\"===g,b=t.aE(a,\"icon-overlap\",\"icon-allow-overlap\"),x=\"always\"===b,y=\"map\"===a.get(\"text-rotation-alignment\"),w=\"map\"===a.get(\"text-pitch-alignment\"),T=\"none\"!==a.get(\"icon-text-fit\"),P=\"viewport-y\"===a.get(\"symbol-z-order\"),C=v&&(x||!r.hasIconData()||f),I=x&&(v||!r.hasTextData()||m);!r.collisionArrays&&d&&r.deserializeCollisionBoxes(d);const M=this.retainedQueryData[r.bucketInstanceId].tileID,S=this._getTerrainElevationFunc(M),E=this.transform.getFastPathSimpleProjectionMatrix(M),R=(e,d,x)=>{var P,R;if(i[e.crossTileID])return;if(u)return void(this.placements[e.crossTileID]=new Ye(!1,!1,!1));let z=!1,D=!1,A=!0,L=null,k={box:null,placeable:!1,offscreen:null,occluded:!1},F={placeable:!1},B=null,O=null,j=null,N=0,Z=0,U=0;d.textFeatureIndex?N=d.textFeatureIndex:e.useRuntimeCollisionCircles&&(N=e.featureIndex),d.verticalTextFeatureIndex&&(Z=d.verticalTextFeatureIndex);const G=d.textBox;if(G){const i=i=>{let o=t.an.horizontal;if(r.allowVerticalPlacement&&!i&&this.prevPlacement){const t=this.prevPlacement.placedOrientations[e.crossTileID];t&&(this.placedOrientations[e.crossTileID]=t,o=t,this.markUsedOrientation(r,o,e));}return o},a=(i,o)=>{if(r.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&d.verticalTextBox){for(const e of r.writingModes)if(e===t.an.vertical?(k=o(),F=k):k=i(),k&&k.placeable)break}else k=i();},c=e.textAnchorOffsetStartIndex,u=e.textAnchorOffsetEndIndex;if(u===c){const o=(t,i)=>{const o=this.collisionIndex.placeCollisionBox(t,g,h,M,l,w,y,s,p.predicate,S,void 0,E);return o&&o.placeable&&(this.markUsedOrientation(r,i,e),this.placedOrientations[e.crossTileID]=i),o};a((()=>o(G,t.an.horizontal)),(()=>{const i=d.verticalTextBox;return r.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&i?o(i,t.an.vertical):{box:null,offscreen:null}})),i(k&&k.placeable);}else {let _=t.aD[null===(R=null===(P=this.prevPlacement)||void 0===P?void 0:P.variableOffsets[e.crossTileID])||void 0===R?void 0:R.anchor];const m=(t,i,a)=>{const d=t.x2-t.x1,m=t.y2-t.y1,f=e.textBoxScale,v=T&&\"never\"===b?i:null;let x=null,P=\"never\"===g?1:2,C=\"never\";_&&P++;for(let i=0;i<P;i++){for(let i=c;i<u;i++){const o=r.textAnchorOffsets.get(i);if(_&&o.textAnchor!==_)continue;const c=this.attemptAnchorPlacement(o,t,d,m,f,y,w,h,M,l,p,C,e,r,a,s,n,v,S);if(c&&(x=c.placedGlyphBoxes,x&&x.placeable))return z=!0,L=c.shift,x}_?_=null:C=g;}return o&&!x&&(x={box:this.collisionIndex.placeCollisionBox(G,\"always\",h,M,l,w,y,s,p.predicate,S,void 0,E).box,offscreen:!1,placeable:!1,occluded:!1}),x};a((()=>m(G,d.iconBox,t.an.horizontal)),(()=>{const i=d.verticalTextBox;return r.allowVerticalPlacement&&(!k||!k.placeable)&&e.numVerticalGlyphVertices>0&&i?m(i,d.verticalIconBox,t.an.vertical):{box:null,occluded:!0,offscreen:null}})),k&&(z=k.placeable,A=k.offscreen);const f=i(k&&k.placeable);if(!z&&this.prevPlacement){const t=this.prevPlacement.variableOffsets[e.crossTileID];t&&(this.variableOffsets[e.crossTileID]=t,this.markUsedJustification(r,t.anchor,e,f));}}}if(B=k,z=B&&B.placeable,A=B&&B.offscreen,e.useRuntimeCollisionCircles){const i=r.text.placedSymbolArray.get(e.centerJustifiedTextSymbolIndex),n=t.ao(r.textSizeData,_,i),h=a.get(\"text-padding\");O=this.collisionIndex.placeCollisionCircles(g,i,r.lineVertexArray,r.glyphOffsetArray,n,l,c,o,w,p.predicate,e.collisionCircleDiameter,h,s,S),O.circles.length&&O.collisionDetected&&!o&&t.w(\"Collisions detected, but collision boxes are not shown\"),z=v||O.circles.length>0&&!O.collisionDetected,A=A&&O.offscreen;}if(d.iconFeatureIndex&&(U=d.iconFeatureIndex),d.iconBox){const e=e=>this.collisionIndex.placeCollisionBox(e,b,h,M,l,w,y,n,p.predicate,S,T&&L?L:void 0,E);F&&F.placeable&&d.verticalIconBox?(j=e(d.verticalIconBox),D=j.placeable):(j=e(d.iconBox),D=j.placeable),A=A&&j.offscreen;}const V=m||0===e.numHorizontalGlyphVertices&&0===e.numVerticalGlyphVertices,$=f||0===e.numIconVertices;V||$?$?V||(D=D&&z):z=D&&z:D=z=D&&z;const q=D&&j.placeable;if(z&&B.placeable&&this.collisionIndex.insertCollisionBox(B.box,g,a.get(\"text-ignore-placement\"),r.bucketInstanceId,F&&F.placeable&&Z?Z:N,p.ID),q&&this.collisionIndex.insertCollisionBox(j.box,b,a.get(\"icon-ignore-placement\"),r.bucketInstanceId,U,p.ID),O&&z&&this.collisionIndex.insertCollisionCircles(O.circles,g,a.get(\"text-ignore-placement\"),r.bucketInstanceId,N,p.ID),o&&this.storeCollisionData(r.bucketInstanceId,x,d,B,j,O),0===e.crossTileID)throw new Error(\"symbolInstance.crossTileID can't be 0\");if(0===r.bucketInstanceId)throw new Error(\"bucket.bucketInstanceId can't be 0\");this.placements[e.crossTileID]=new Ye((z||C)&&!(null==B?void 0:B.occluded),(D||I)&&!(null==j?void 0:j.occluded),A||r.justReloaded),i[e.crossTileID]=!0;};if(P){if(0!==e.symbolInstanceStart)throw new Error(\"bucket.bucketInstanceId should be 0\");const t=r.getSortedSymbolIndexes(-this.transform.bearingInRadians);for(let e=t.length-1;e>=0;--e){const i=t[e];R(r.symbolInstances.get(i),r.collisionArrays[i],i);}}else for(let t=e.symbolInstanceStart;t<e.symbolInstanceEnd;t++)R(r.symbolInstances.get(t),r.collisionArrays[t],t);r.justReloaded=!1;}storeCollisionData(e,t,i,o,r,a){if(i.textBox||i.iconBox){let a,s;this.collisionBoxArrays.has(e)?a=this.collisionBoxArrays.get(e):(a=new Map,this.collisionBoxArrays.set(e,a)),a.has(t)?s=a.get(t):(s={text:null,icon:null},a.set(t,s)),i.textBox&&(s.text=o.box),i.iconBox&&(s.icon=r.box);}if(a){let t=this.collisionCircleArrays[e];void 0===t&&(t=this.collisionCircleArrays[e]=[]);for(let e=0;e<a.circles.length;e+=4)t.push(a.circles[e+0]-We),t.push(a.circles[e+1]-We),t.push(a.circles[e+2]),t.push(a.collisionDetected?1:0);}}markUsedJustification(e,i,o,r){let a;a=r===t.an.vertical?o.verticalPlacedTextSymbolIndex:{left:o.leftJustifiedTextSymbolIndex,center:o.centerJustifiedTextSymbolIndex,right:o.rightJustifiedTextSymbolIndex}[t.aF(i)];const s=[o.leftJustifiedTextSymbolIndex,o.centerJustifiedTextSymbolIndex,o.rightJustifiedTextSymbolIndex,o.verticalPlacedTextSymbolIndex];for(const t of s)t>=0&&(e.text.placedSymbolArray.get(t).crossTileID=a>=0&&t!==a?0:o.crossTileID);}markUsedOrientation(e,i,o){const r=i===t.an.horizontal||i===t.an.horizontalOnly?i:0,a=i===t.an.vertical?i:0,s=[o.leftJustifiedTextSymbolIndex,o.centerJustifiedTextSymbolIndex,o.rightJustifiedTextSymbolIndex];for(const t of s)e.text.placedSymbolArray.get(t).placedOrientation=r;o.verticalPlacedTextSymbolIndex&&(e.text.placedSymbolArray.get(o.verticalPlacedTextSymbolIndex).placedOrientation=a);}commit(e){this.commitTime=e,this.zoomAtLastRecencyCheck=this.transform.zoom;const t=this.prevPlacement;let i=!1;this.prevZoomAdjustment=t?t.zoomAdjustment(this.transform.zoom):0;const o=t?t.symbolFadeChange(e):1,r=t?t.opacities:{},a=t?t.variableOffsets:{},s=t?t.placedOrientations:{};for(const e in this.placements){const t=this.placements[e],a=r[e];a?(this.opacities[e]=new Ke(a,o,t.text,t.icon),i=i||t.text!==a.text.placed||t.icon!==a.icon.placed):(this.opacities[e]=new Ke(null,o,t.text,t.icon,t.skipFade),i=i||t.text||t.icon);}for(const e in r){const t=r[e];if(!this.opacities[e]){const r=new Ke(t,o,!1,!1);r.isHidden()||(this.opacities[e]=r,i=i||t.text.placed||t.icon.placed);}}for(const e in a)this.variableOffsets[e]||!this.opacities[e]||this.opacities[e].isHidden()||(this.variableOffsets[e]=a[e]);for(const e in s)this.placedOrientations[e]||!this.opacities[e]||this.opacities[e].isHidden()||(this.placedOrientations[e]=s[e]);if(t&&void 0===t.lastPlacementChangeTime)throw new Error(\"Last placement time for previous placement is not defined\");i?this.lastPlacementChangeTime=e:\"number\"!=typeof this.lastPlacementChangeTime&&(this.lastPlacementChangeTime=t?t.lastPlacementChangeTime:e);}updateLayerOpacities(e,t){const i={};for(const o of t){const t=o.getBucket(e);t&&o.latestFeatureIndex&&e.id===t.layerIds[0]&&this.updateBucketOpacities(t,o.tileID,i,o.collisionBoxArray);}}updateBucketOpacities(e,i,o,r){e.hasTextData()&&(e.text.opacityVertexArray.clear(),e.text.hasVisibleVertices=!1),e.hasIconData()&&(e.icon.opacityVertexArray.clear(),e.icon.hasVisibleVertices=!1),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexArray.clear(),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexArray.clear();const a=e.layers[0],s=a.layout,n=new Ke(null,0,!1,!1,!0),l=s.get(\"text-allow-overlap\"),c=s.get(\"icon-allow-overlap\"),h=a._unevaluatedLayout.hasValue(\"text-variable-anchor\")||a._unevaluatedLayout.hasValue(\"text-variable-anchor-offset\"),u=\"map\"===s.get(\"text-rotation-alignment\"),d=\"map\"===s.get(\"text-pitch-alignment\"),_=\"none\"!==s.get(\"icon-text-fit\"),p=new Ke(null,0,l&&(c||!e.hasIconData()||s.get(\"icon-optional\")),c&&(l||!e.hasTextData()||s.get(\"text-optional\")),!0);!e.collisionArrays&&r&&(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData())&&e.deserializeCollisionBoxes(r);const m=(e,t,i)=>{for(let o=0;o<t/4;o++)e.opacityVertexArray.emplaceBack(i);e.hasVisibleVertices=e.hasVisibleVertices||i!==ut;},f=this.collisionBoxArrays.get(e.bucketInstanceId);for(let i=0;i<e.symbolInstances.length;i++){const r=e.symbolInstances.get(i),{numHorizontalGlyphVertices:a,numVerticalGlyphVertices:s,crossTileID:l}=r;let c=this.opacities[l];o[l]?c=n:c||(c=p,this.opacities[l]=c),o[l]=!0;const g=r.numIconVertices>0,v=this.placedOrientations[r.crossTileID],b=v===t.an.vertical,x=v===t.an.horizontal||v===t.an.horizontalOnly;if(a>0||s>0){const t=ht(c.text);m(e.text,a,b?ut:t),m(e.text,s,x?ut:t);const i=c.text.isHidden();[r.rightJustifiedTextSymbolIndex,r.centerJustifiedTextSymbolIndex,r.leftJustifiedTextSymbolIndex].forEach((t=>{t>=0&&(e.text.placedSymbolArray.get(t).hidden=i||b?1:0);})),r.verticalPlacedTextSymbolIndex>=0&&(e.text.placedSymbolArray.get(r.verticalPlacedTextSymbolIndex).hidden=i||x?1:0);const o=this.variableOffsets[r.crossTileID];o&&this.markUsedJustification(e,o.anchor,r,v);const n=this.placedOrientations[r.crossTileID];n&&(this.markUsedJustification(e,\"left\",r,n),this.markUsedOrientation(e,n,r));}if(g){const t=ht(c.icon),i=!(_&&r.verticalPlacedIconSymbolIndex&&b);r.placedIconSymbolIndex>=0&&(m(e.icon,r.numIconVertices,i?t:ut),e.icon.placedSymbolArray.get(r.placedIconSymbolIndex).hidden=c.icon.isHidden()),r.verticalPlacedIconSymbolIndex>=0&&(m(e.icon,r.numVerticalIconVertices,i?ut:t),e.icon.placedSymbolArray.get(r.verticalPlacedIconSymbolIndex).hidden=c.icon.isHidden());}const y=f&&f.has(i)?f.get(i):{text:null,icon:null};if(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData()){const o=e.collisionArrays[i];if(o){let i=new t.P(0,0);if(o.textBox||o.verticalTextBox){let t=!0;if(h){const e=this.variableOffsets[l];e?(i=et(e.anchor,e.width,e.height,e.textOffset,e.textBoxScale),u&&i._rotate(d?-this.transform.bearingInRadians:this.transform.bearingInRadians)):t=!1;}if(o.textBox||o.verticalTextBox){let r;o.textBox&&(r=b),o.verticalTextBox&&(r=x),it(e.textCollisionBox.collisionVertexArray,c.text.placed,!t||r,y.text,i.x,i.y);}}if(o.iconBox||o.verticalIconBox){const t=Boolean(!x&&o.verticalIconBox);let r;o.iconBox&&(r=t),o.verticalIconBox&&(r=!t),it(e.iconCollisionBox.collisionVertexArray,c.icon.placed,r,y.icon,_?i.x:0,_?i.y:0);}}}}if(e.sortFeatures(-this.transform.bearingInRadians),this.retainedQueryData[e.bucketInstanceId]&&(this.retainedQueryData[e.bucketInstanceId].featureSortOrder=e.featureSortOrder),e.hasTextData()&&e.text.opacityVertexBuffer&&e.text.opacityVertexBuffer.updateData(e.text.opacityVertexArray),e.hasIconData()&&e.icon.opacityVertexBuffer&&e.icon.opacityVertexBuffer.updateData(e.icon.opacityVertexArray),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexBuffer&&e.iconCollisionBox.collisionVertexBuffer.updateData(e.iconCollisionBox.collisionVertexArray),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexBuffer&&e.textCollisionBox.collisionVertexBuffer.updateData(e.textCollisionBox.collisionVertexArray),e.text.opacityVertexArray.length!==e.text.layoutVertexArray.length/4)throw new Error(`bucket.text.opacityVertexArray.length (= ${e.text.opacityVertexArray.length}) !== bucket.text.layoutVertexArray.length (= ${e.text.layoutVertexArray.length}) / 4`);if(e.icon.opacityVertexArray.length!==e.icon.layoutVertexArray.length/4)throw new Error(`bucket.icon.opacityVertexArray.length (= ${e.icon.opacityVertexArray.length}) !== bucket.icon.layoutVertexArray.length (= ${e.icon.layoutVertexArray.length}) / 4`);e.bucketInstanceId in this.collisionCircleArrays&&(e.collisionCircleArray=this.collisionCircleArrays[e.bucketInstanceId],delete this.collisionCircleArrays[e.bucketInstanceId]);}symbolFadeChange(e){return 0===this.fadeDuration?1:(e-this.commitTime)/this.fadeDuration+this.prevZoomAdjustment}zoomAdjustment(e){return Math.max(0,(this.transform.zoom-e)/1.5)}hasTransitions(e){return this.stale||e-this.lastPlacementChangeTime<this.fadeDuration}stillRecent(e,t){const i=this.zoomAtLastRecencyCheck===t?1-this.zoomAdjustment(t):1;return this.zoomAtLastRecencyCheck=t,this.commitTime+this.fadeDuration*i>e}setStale(){this.stale=!0;}}function it(e,t,i,o,r,a){o&&0!==o.length||(o=[0,0,0,0]);const s=o[0]-We,n=o[1]-We,l=o[2]-We,c=o[3]-We;e.emplaceBack(t?1:0,i?1:0,r||0,a||0,s,n),e.emplaceBack(t?1:0,i?1:0,r||0,a||0,l,n),e.emplaceBack(t?1:0,i?1:0,r||0,a||0,l,c),e.emplaceBack(t?1:0,i?1:0,r||0,a||0,s,c);}const ot=Math.pow(2,25),rt=Math.pow(2,24),at=Math.pow(2,17),st=Math.pow(2,16),nt=Math.pow(2,9),lt=Math.pow(2,8),ct=Math.pow(2,1);function ht(e){if(0===e.opacity&&!e.placed)return 0;if(1===e.opacity&&e.placed)return 4294967295;const t=e.placed?1:0,i=Math.floor(127*e.opacity);return i*ot+t*rt+i*at+t*st+i*nt+t*lt+i*ct+t}const ut=0;class dt{constructor(e){this._sortAcrossTiles=\"viewport-y\"!==e.layout.get(\"symbol-z-order\")&&!e.layout.get(\"symbol-sort-key\").isConstant(),this._currentTileIndex=0,this._currentPartIndex=0,this._seenCrossTileIDs={},this._bucketParts=[];}continuePlacement(e,t,i,o,r){const a=this._bucketParts;for(;this._currentTileIndex<e.length;)if(t.getBucketParts(a,o,e[this._currentTileIndex],this._sortAcrossTiles),this._currentTileIndex++,r())return !0;for(this._sortAcrossTiles&&(this._sortAcrossTiles=!1,a.sort(((e,t)=>e.sortKey-t.sortKey)));this._currentPartIndex<a.length;)if(t.placeLayerBucketPart(a[this._currentPartIndex],this._seenCrossTileIDs,i),this._currentPartIndex++,r())return !0;return !1}}class _t{constructor(e,t,i,o,r,a,s,n){this.placement=new tt(e,t,a,s,n),this._currentPlacementIndex=i.length-1,this._forceFullPlacement=o,this._showCollisionBoxes=r,this._done=!1;}isDone(){return this._done}continuePlacement(e,t,i){const o=s.now(),r=()=>!this._forceFullPlacement&&s.now()-o>2;for(;this._currentPlacementIndex>=0;){const o=t[e[this._currentPlacementIndex]],a=this.placement.collisionIndex.transform.zoom;if(\"symbol\"===o.type&&(!o.minzoom||o.minzoom<=a)&&(!o.maxzoom||o.maxzoom>a)){if(this._inProgressLayer||(this._inProgressLayer=new dt(o)),this._inProgressLayer.continuePlacement(i[o.source],this.placement,this._showCollisionBoxes,o,r))return;delete this._inProgressLayer;}this._currentPlacementIndex--;}this._done=!0;}commit(e){return this.placement.commit(e),this.placement}}const pt=512/t.$/2;class mt{constructor(e,i,o){this.tileID=e,this.bucketInstanceId=o,this._symbolsByKey={};const r=new Map;for(let e=0;e<i.length;e++){const t=i.get(e),o=t.key,a=r.get(o);a?a.push(t):r.set(o,[t]);}for(const[e,i]of r){const o={positions:i.map((e=>({x:Math.floor(e.anchorX*pt),y:Math.floor(e.anchorY*pt)}))),crossTileIDs:i.map((e=>e.crossTileID))};if(o.positions.length>128){const e=new t.aH(o.positions.length,16,Uint16Array);for(const{x:t,y:i}of o.positions)e.add(t,i);e.finish(),delete o.positions,o.index=e;}this._symbolsByKey[e]=o;}}getScaledCoordinates(e,i){const{x:o,y:r,z:a}=this.tileID.canonical,{x:s,y:n,z:l}=i.canonical,c=pt/Math.pow(2,l-a),h=(n*t.$+e.anchorY)*c,u=r*t.$*pt;return {x:Math.floor((s*t.$+e.anchorX)*c-o*t.$*pt),y:Math.floor(h-u)}}findMatches(e,t,i){const o=this.tileID.canonical.z<t.canonical.z?1:Math.pow(2,this.tileID.canonical.z-t.canonical.z);for(let r=0;r<e.length;r++){const a=e.get(r);if(a.crossTileID)continue;const s=this._symbolsByKey[a.key];if(!s)continue;const n=this.getScaledCoordinates(a,t);if(s.index){const e=s.index.range(n.x-o,n.y-o,n.x+o,n.y+o).sort();for(const t of e){const e=s.crossTileIDs[t];if(!i[e]){i[e]=!0,a.crossTileID=e;break}}}else if(s.positions)for(let e=0;e<s.positions.length;e++){const t=s.positions[e],r=s.crossTileIDs[e];if(Math.abs(t.x-n.x)<=o&&Math.abs(t.y-n.y)<=o&&!i[r]){i[r]=!0,a.crossTileID=r;break}}}}getCrossTileIDsLists(){return Object.values(this._symbolsByKey).map((({crossTileIDs:e})=>e))}}class ft{constructor(){this.maxCrossTileID=0;}generate(){return ++this.maxCrossTileID}}class gt{constructor(){this.indexes={},this.usedCrossTileIDs={},this.lng=0;}handleWrapJump(e){const t=Math.round((e-this.lng)/360);if(0!==t)for(const e in this.indexes){const i=this.indexes[e],o={};for(const e in i){const r=i[e];r.tileID=r.tileID.unwrapTo(r.tileID.wrap+t),o[r.tileID.key]=r;}this.indexes[e]=o;}this.lng=e;}addBucket(e,t,i){if(this.indexes[e.overscaledZ]&&this.indexes[e.overscaledZ][e.key]){if(this.indexes[e.overscaledZ][e.key].bucketInstanceId===t.bucketInstanceId)return !1;this.removeBucketCrossTileIDs(e.overscaledZ,this.indexes[e.overscaledZ][e.key]);}for(let e=0;e<t.symbolInstances.length;e++)t.symbolInstances.get(e).crossTileID=0;this.usedCrossTileIDs[e.overscaledZ]||(this.usedCrossTileIDs[e.overscaledZ]={});const o=this.usedCrossTileIDs[e.overscaledZ];for(const i in this.indexes){const r=this.indexes[i];if(Number(i)>e.overscaledZ)for(const i in r){const a=r[i];a.tileID.isChildOf(e)&&a.findMatches(t.symbolInstances,e,o);}else {const a=r[e.scaledTo(Number(i)).key];a&&a.findMatches(t.symbolInstances,e,o);}}for(let e=0;e<t.symbolInstances.length;e++){const r=t.symbolInstances.get(e);r.crossTileID||(r.crossTileID=i.generate(),o[r.crossTileID]=!0);}return void 0===this.indexes[e.overscaledZ]&&(this.indexes[e.overscaledZ]={}),this.indexes[e.overscaledZ][e.key]=new mt(e,t.symbolInstances,t.bucketInstanceId),!0}removeBucketCrossTileIDs(e,t){for(const i of t.getCrossTileIDsLists())for(const t of i)delete this.usedCrossTileIDs[e][t];}removeStaleBuckets(e){let t=!1;for(const i in this.indexes){const o=this.indexes[i];for(const r in o)e[o[r].bucketInstanceId]||(this.removeBucketCrossTileIDs(i,o[r]),delete o[r],t=!0);}return t}}class vt{constructor(){this.layerIndexes={},this.crossTileIDs=new ft,this.maxBucketInstanceId=0,this.bucketsInCurrentPlacement={};}addLayer(e,t,i){let o=this.layerIndexes[e.id];void 0===o&&(o=this.layerIndexes[e.id]=new gt);let r=!1;const a={};o.handleWrapJump(i);for(const i of t){const t=i.getBucket(e);t&&e.id===t.layerIds[0]&&(t.bucketInstanceId||(t.bucketInstanceId=++this.maxBucketInstanceId),o.addBucket(i.tileID,t,this.crossTileIDs)&&(r=!0),a[t.bucketInstanceId]=!0);}return o.removeStaleBuckets(a)&&(r=!0),r}pruneUnusedLayers(e){const t={};e.forEach((e=>{t[e]=!0;}));for(const e in this.layerIndexes)t[e]||delete this.layerIndexes[e];}}var bt=\"void main() {fragColor=vec4(1.0);}\";const xt={prelude:yt(\"#ifdef GL_ES\\nprecision mediump float;\\n#else\\n#if !defined(lowp)\\n#define lowp\\n#endif\\n#if !defined(mediump)\\n#define mediump\\n#endif\\n#if !defined(highp)\\n#define highp\\n#endif\\n#endif\\nout highp vec4 fragColor;\",\"#ifdef GL_ES\\nprecision highp float;\\n#else\\n#if !defined(lowp)\\n#define lowp\\n#endif\\n#if !defined(mediump)\\n#define mediump\\n#endif\\n#if !defined(highp)\\n#define highp\\n#endif\\n#endif\\nvec2 unpack_float(const float packedValue) {int packedIntValue=int(packedValue);int v0=packedIntValue/256;return vec2(v0,packedIntValue-v0*256);}vec2 unpack_opacity(const float packedOpacity) {int intOpacity=int(packedOpacity)/2;return vec2(float(intOpacity)/127.0,mod(packedOpacity,2.0));}vec4 decode_color(const vec2 encodedColor) {return vec4(unpack_float(encodedColor[0])/255.0,unpack_float(encodedColor[1])/255.0\\n);}float unpack_mix_vec2(const vec2 packedValue,const float t) {return mix(packedValue[0],packedValue[1],t);}vec4 unpack_mix_color(const vec4 packedColors,const float t) {vec4 minColor=decode_color(vec2(packedColors[0],packedColors[1]));vec4 maxColor=decode_color(vec2(packedColors[2],packedColors[3]));return mix(minColor,maxColor,t);}vec2 get_pattern_pos(const vec2 pixel_coord_upper,const vec2 pixel_coord_lower,const vec2 pattern_size,const float tile_units_to_pixels,const vec2 pos) {vec2 offset=mod(mod(mod(pixel_coord_upper,pattern_size)*256.0,pattern_size)*256.0+pixel_coord_lower,pattern_size);return (tile_units_to_pixels*pos+offset)/pattern_size;}mat3 rotationMatrixFromAxisAngle(vec3 u,float angle) {float c=cos(angle);float s=sin(angle);float c2=1.0-c;return mat3(u.x*u.x*c2+      c,u.x*u.y*c2-u.z*s,u.x*u.z*c2+u.y*s,u.y*u.x*c2+u.z*s,u.y*u.y*c2+    c,u.y*u.z*c2-u.x*s,u.z*u.x*c2-u.y*s,u.z*u.y*c2+u.x*s,u.z*u.z*c2+    c\\n);}\\n#ifdef TERRAIN3D\\nuniform sampler2D u_terrain;uniform float u_terrain_dim;uniform mat4 u_terrain_matrix;uniform vec4 u_terrain_unpack;uniform float u_terrain_exaggeration;uniform highp sampler2D u_depth;\\n#endif\\nconst highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitShifts=vec4(1.)/bitSh;highp float unpack(highp vec4 color) {return dot(color,bitShifts);}highp float depthOpacity(vec3 frag) {\\n#ifdef TERRAIN3D\\nhighp float d=unpack(texture(u_depth,frag.xy*0.5+0.5))+0.0001-frag.z;return 1.0-max(0.0,min(1.0,-d*500.0));\\n#else\\nreturn 1.0;\\n#endif\\n}float calculate_visibility(vec4 pos) {\\n#ifdef TERRAIN3D\\nvec3 frag=pos.xyz/pos.w;highp float d=depthOpacity(frag);if (d > 0.95) return 1.0;return (d+depthOpacity(frag+vec3(0.0,0.01,0.0)))/2.0;\\n#else\\nreturn 1.0;\\n#endif\\n}float ele(vec2 pos) {\\n#ifdef TERRAIN3D\\nvec4 rgb=(texture(u_terrain,pos)*255.0)*u_terrain_unpack;return rgb.r+rgb.g+rgb.b-u_terrain_unpack.a;\\n#else\\nreturn 0.0;\\n#endif\\n}float get_elevation(vec2 pos) {\\n#ifdef TERRAIN3D\\n#ifdef GLOBE\\nif ((pos.y <-32767.5) || (pos.y > 32766.5)) {return 0.0;}\\n#endif\\nvec2 coord=(u_terrain_matrix*vec4(pos,0.0,1.0)).xy*u_terrain_dim+1.0;vec2 f=fract(coord);vec2 c=(floor(coord)+0.5)/(u_terrain_dim+2.0);float d=1.0/(u_terrain_dim+2.0);float tl=ele(c);float tr=ele(c+vec2(d,0.0));float bl=ele(c+vec2(0.0,d));float br=ele(c+vec2(d,d));float elevation=mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);return elevation*u_terrain_exaggeration;\\n#else\\nreturn 0.0;\\n#endif\\n}const float PI=3.141592653589793;uniform mat4 u_projection_matrix;\"),projectionMercator:yt(\"\",\"float projectLineThickness(float tileY) {return 1.0;}float projectCircleRadius(float tileY) {return 1.0;}vec4 projectTile(vec2 p) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);return result;}vec4 projectTile(vec2 p,vec2 rawPos) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);if (rawPos.y <-32767.5 || rawPos.y > 32766.5) {result.z=-10000000.0;}return result;}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return u_projection_matrix*vec4(posInTile,elevation,1.0);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {return projectTileWithElevation(posInTile,elevation);}\"),projectionGlobe:yt(\"\",\"#define GLOBE_RADIUS 6371008.8\\nuniform highp vec4 u_projection_tile_mercator_coords;uniform highp vec4 u_projection_clipping_plane;uniform highp float u_projection_transition;uniform mat4 u_projection_fallback_matrix;vec3 globeRotateVector(vec3 vec,vec2 angles) {vec3 axisRight=vec3(vec.z,0.0,-vec.x);vec3 axisUp=cross(axisRight,vec);axisRight=normalize(axisRight);axisUp=normalize(axisUp);vec2 t=tan(angles);return normalize(vec+axisRight*t.x+axisUp*t.y);}mat3 globeGetRotationMatrix(vec3 spherePos) {vec3 axisRight=vec3(spherePos.z,0.0,-spherePos.x);vec3 axisDown=cross(axisRight,spherePos);axisRight=normalize(axisRight);axisDown=normalize(axisDown);return mat3(axisRight,axisDown,spherePos\\n);}float circumferenceRatioAtTileY(float tileY) {float mercator_pos_y=u_projection_tile_mercator_coords.y+u_projection_tile_mercator_coords.w*tileY;float spherical_y=2.0*atan(exp(PI-(mercator_pos_y*PI*2.0)))-PI*0.5;return cos(spherical_y);}float projectLineThickness(float tileY) {float thickness=1.0/circumferenceRatioAtTileY(tileY); \\nif (u_projection_transition < 0.999) {return mix(1.0,thickness,u_projection_transition);} else {return thickness;}}vec3 projectToSphere(vec2 translatedPos,vec2 rawPos) {vec2 mercator_pos=u_projection_tile_mercator_coords.xy+u_projection_tile_mercator_coords.zw*translatedPos;vec2 spherical;spherical.x=mercator_pos.x*PI*2.0+PI;spherical.y=2.0*atan(exp(PI-(mercator_pos.y*PI*2.0)))-PI*0.5;float len=cos(spherical.y);vec3 pos=vec3(sin(spherical.x)*len,sin(spherical.y),cos(spherical.x)*len\\n);if (rawPos.y <-32767.5) {pos=vec3(0.0,1.0,0.0);}if (rawPos.y > 32766.5) {pos=vec3(0.0,-1.0,0.0);}return pos;}vec3 projectToSphere(vec2 posInTile) {return projectToSphere(posInTile,vec2(0.0,0.0));}float globeComputeClippingZ(vec3 spherePos) {return (1.0-(dot(spherePos,u_projection_clipping_plane.xyz)+u_projection_clipping_plane.w));}vec4 interpolateProjection(vec2 posInTile,vec3 spherePos,float elevation) {vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);globePosition.z=globeComputeClippingZ(elevatedPos)*globePosition.w;if (u_projection_transition > 0.999) {return globePosition;}vec4 flatPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);const float z_globeness_threshold=0.2;vec4 result=globePosition;result.z=mix(0.0,globePosition.z,clamp((u_projection_transition-z_globeness_threshold)/(1.0-z_globeness_threshold),0.0,1.0));result.xyw=mix(flatPosition.xyw,globePosition.xyw,u_projection_transition);if ((posInTile.y <-32767.5) || (posInTile.y > 32766.5)) {result=globePosition;const float poles_hidden_anim_percentage=0.02;result.z=mix(globePosition.z,100.0,pow(max((1.0-u_projection_transition)/poles_hidden_anim_percentage,0.0),8.0));}return result;}vec4 interpolateProjectionFor3D(vec2 posInTile,vec3 spherePos,float elevation) {vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);if (u_projection_transition > 0.999) {return globePosition;}vec4 fallbackPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);return mix(fallbackPosition,globePosition,u_projection_transition);}vec4 projectTile(vec2 posInTile) {return interpolateProjection(posInTile,projectToSphere(posInTile),0.0);}vec4 projectTile(vec2 posInTile,vec2 rawPos) {return interpolateProjection(posInTile,projectToSphere(posInTile,rawPos),0.0);}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return interpolateProjection(posInTile,projectToSphere(posInTile),elevation);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {vec3 spherePos=projectToSphere(posInTile,posInTile);return interpolateProjectionFor3D(posInTile,spherePos,elevation);}\"),background:yt(\"uniform vec4 u_color;uniform float u_opacity;void main() {fragColor=u_color*u_opacity;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}\"),backgroundPattern:yt(\"uniform vec2 u_pattern_tl_a;uniform vec2 u_pattern_br_a;uniform vec2 u_pattern_tl_b;uniform vec2 u_pattern_br_b;uniform vec2 u_texsize;uniform float u_mix;uniform float u_opacity;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;void main() {vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(u_pattern_tl_a/u_texsize,u_pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(u_pattern_tl_b/u_texsize,u_pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);fragColor=mix(color1,color2,u_mix)*u_opacity;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_pattern_size_a;uniform vec2 u_pattern_size_b;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_scale_a;uniform float u_scale_b;uniform float u_tile_units_to_pixels;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;void main() {gl_Position=projectTile(a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_a*u_pattern_size_a,u_tile_units_to_pixels,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_b*u_pattern_size_b,u_tile_units_to_pixels,a_pos);}\"),circle:yt(\"in vec3 v_data;in float v_visibility;\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define mediump float radius\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define highp vec4 stroke_color\\n#pragma mapbox: define mediump float stroke_width\\n#pragma mapbox: define lowp float stroke_opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize mediump float radius\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize highp vec4 stroke_color\\n#pragma mapbox: initialize mediump float stroke_width\\n#pragma mapbox: initialize lowp float stroke_opacity\\nvec2 extrude=v_data.xy;float extrude_length=length(extrude);float antialiased_blur=v_data.z;float opacity_t=smoothstep(0.0,antialiased_blur,extrude_length-1.0);float color_t=stroke_width < 0.01 ? 0.0 : smoothstep(antialiased_blur,0.0,extrude_length-radius/(radius+stroke_width));fragColor=v_visibility*opacity_t*mix(color*opacity,stroke_color*stroke_opacity,color_t);const float epsilon=0.5/255.0;if (fragColor.r < epsilon && fragColor.g < epsilon && fragColor.b < epsilon && fragColor.a < epsilon) {discard;}\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform bool u_scale_with_map;uniform bool u_pitch_with_map;uniform vec2 u_extrude_scale;uniform highp float u_globe_extrude_scale;uniform lowp float u_device_pixel_ratio;uniform highp float u_camera_to_center_distance;uniform vec2 u_translate;in vec2 a_pos;out vec3 v_data;out float v_visibility;\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define mediump float radius\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define highp vec4 stroke_color\\n#pragma mapbox: define mediump float stroke_width\\n#pragma mapbox: define lowp float stroke_opacity\\nvoid main(void) {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize mediump float radius\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize highp vec4 stroke_color\\n#pragma mapbox: initialize mediump float stroke_width\\n#pragma mapbox: initialize lowp float stroke_opacity\\nvec2 pos_raw=a_pos+32768.0;vec2 extrude=vec2(mod(pos_raw,8.0)/7.0*2.0-1.0);vec2 circle_center=floor(pos_raw/8.0)+u_translate;float ele=get_elevation(circle_center);v_visibility=calculate_visibility(projectTileWithElevation(circle_center,ele));if (u_pitch_with_map) {\\n#ifdef GLOBE\\nvec3 center_vector=projectToSphere(circle_center);\\n#endif\\nfloat angle_scale=u_globe_extrude_scale;vec2 corner_position=circle_center;if (u_scale_with_map) {angle_scale*=(radius+stroke_width);corner_position+=extrude*u_extrude_scale*(radius+stroke_width);} else {\\n#ifdef GLOBE\\nvec4 projected_center=interpolateProjection(circle_center,center_vector,ele);\\n#else\\nvec4 projected_center=projectTileWithElevation(circle_center,ele);\\n#endif\\ncorner_position+=extrude*u_extrude_scale*(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);angle_scale*=(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);}\\n#ifdef GLOBE\\nvec2 angles=extrude*angle_scale;vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(corner_position,corner_vector,ele);\\n#else\\ngl_Position=projectTileWithElevation(corner_position,ele);\\n#endif\\n} else {gl_Position=projectTileWithElevation(circle_center,ele);if (gl_Position.z/gl_Position.w > 1.0) {gl_Position.xy=vec2(10000.0);}if (u_scale_with_map) {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*u_camera_to_center_distance;} else {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*gl_Position.w;}}float antialiasblur=-max(1.0/u_device_pixel_ratio/(radius+stroke_width),blur);v_data=vec3(extrude.x,extrude.y,antialiasblur);}\"),clippingMask:yt(bt,\"in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}\"),heatmap:yt(\"uniform highp float u_intensity;in vec2 v_extrude;\\n#pragma mapbox: define highp float weight\\n#define GAUSS_COEF 0.3989422804014327\\nvoid main() {\\n#pragma mapbox: initialize highp float weight\\nfloat d=-0.5*3.0*3.0*dot(v_extrude,v_extrude);float val=weight*u_intensity*GAUSS_COEF*exp(d);fragColor=vec4(val,1.0,1.0,1.0);\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform float u_extrude_scale;uniform float u_opacity;uniform float u_intensity;uniform highp float u_globe_extrude_scale;in vec2 a_pos;out vec2 v_extrude;\\n#pragma mapbox: define highp float weight\\n#pragma mapbox: define mediump float radius\\nconst highp float ZERO=1.0/255.0/16.0;\\n#define GAUSS_COEF 0.3989422804014327\\nvoid main(void) {\\n#pragma mapbox: initialize highp float weight\\n#pragma mapbox: initialize mediump float radius\\nvec2 pos_raw=a_pos+32768.0;vec2 unscaled_extrude=vec2(mod(pos_raw,8.0)/7.0*2.0-1.0);float S=sqrt(-2.0*log(ZERO/weight/u_intensity/GAUSS_COEF))/3.0;v_extrude=S*unscaled_extrude;vec2 extrude=v_extrude*radius*u_extrude_scale;vec2 circle_center=floor(pos_raw/8.0);\\n#ifdef GLOBE\\nvec2 angles=v_extrude*radius*u_globe_extrude_scale;vec3 center_vector=projectToSphere(circle_center);vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(circle_center+extrude,corner_vector,0.0);\\n#else\\ngl_Position=projectTileFor3D(circle_center+extrude,get_elevation(circle_center));\\n#endif\\n}\"),heatmapTexture:yt(\"uniform sampler2D u_image;uniform sampler2D u_color_ramp;uniform float u_opacity;in vec2 v_pos;void main() {float t=texture(u_image,v_pos).r;vec4 color=texture(u_color_ramp,vec2(t,0.5));fragColor=color*u_opacity;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(0.0);\\n#endif\\n}\",\"uniform mat4 u_matrix;uniform vec2 u_world;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos*u_world,0,1);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}\"),collisionBox:yt(\"in float v_placed;in float v_notUsed;void main() {float alpha=0.5;fragColor=vec4(1.0,0.0,0.0,1.0)*alpha;if (v_placed > 0.5) {fragColor=vec4(0.0,0.0,1.0,0.5)*alpha;}if (v_notUsed > 0.5) {fragColor*=.1;}}\",\"in vec2 a_anchor_pos;in vec2 a_placed;in vec2 a_box_real;uniform vec2 u_pixel_extrude_scale;out float v_placed;out float v_notUsed;void main() {gl_Position=projectTileWithElevation(a_anchor_pos,get_elevation(a_anchor_pos));gl_Position.xy=((a_box_real+0.5)*u_pixel_extrude_scale*2.0-1.0)*vec2(1.0,-1.0)*gl_Position.w;if (gl_Position.z/gl_Position.w < 1.1) {gl_Position.z=0.5;}v_placed=a_placed.x;v_notUsed=a_placed.y;}\"),collisionCircle:yt(\"in float v_radius;in vec2 v_extrude;in float v_collision;void main() {float alpha=0.5;float stroke_radius=0.9;float distance_to_center=length(v_extrude);float distance_to_edge=abs(distance_to_center-v_radius);float opacity_t=smoothstep(-stroke_radius,0.0,-distance_to_edge);vec4 color=mix(vec4(0.0,0.0,1.0,0.5),vec4(1.0,0.0,0.0,1.0),v_collision);fragColor=color*alpha*opacity_t;}\",\"in vec2 a_pos;in float a_radius;in vec2 a_flags;uniform vec2 u_viewport_size;out float v_radius;out vec2 v_extrude;out float v_collision;void main() {float radius=a_radius;float collision=a_flags.x;float vertexIdx=a_flags.y;vec2 quadVertexOffset=vec2(mix(-1.0,1.0,float(vertexIdx >=2.0)),mix(-1.0,1.0,float(vertexIdx >=1.0 && vertexIdx <=2.0)));vec2 quadVertexExtent=quadVertexOffset*radius;float padding_factor=1.2;v_radius=radius;v_extrude=quadVertexExtent*padding_factor;v_collision=collision;gl_Position=vec4((a_pos/u_viewport_size*2.0-1.0)*vec2(1.0,-1.0),0.0,1.0)+vec4(quadVertexExtent*padding_factor/u_viewport_size*2.0,0.0,0.0);}\"),colorRelief:yt(\"#ifdef GL_ES\\nprecision highp float;\\n#endif\\nuniform sampler2D u_image;uniform vec4 u_unpack;uniform sampler2D u_elevation_stops;uniform sampler2D u_color_stops;uniform float u_opacity;in vec2 v_pos;float getElevation(vec2 coord) {vec4 data=texture(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack);}float getElevationStop(int stop) {float x=(float(stop)+0.5)/float(textureSize(u_elevation_stops,0)[0]);vec4 data=texture(u_elevation_stops,vec2(x,0))*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {float el=getElevation(v_pos);int num_elevation_stops=textureSize(u_elevation_stops,0)[0];int r=(num_elevation_stops-1);int l=0;float el_l=getElevationStop(l);float el_r=getElevationStop(r);while(r-l > 1){int m=(r+l)/2;float el_m=getElevationStop(m);if(el < el_m){r=m;el_r=el_m;}else\\n{l=m;el_l=el_m;}}float x=(float(l)+(el-el_l)/(el_r-el_l)+0.5)/float(textureSize(u_color_stops,0)[0]);fragColor=u_opacity*texture(u_color_stops,vec2(x,0));\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_dimension;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_pos/8192.0)*scale+epsilon;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}\"),debug:yt(\"uniform highp vec4 u_color;uniform sampler2D u_overlay;in vec2 v_uv;void main() {vec4 overlay_color=texture(u_overlay,v_uv);fragColor=mix(u_color,overlay_color,overlay_color.a);}\",\"in vec2 a_pos;out vec2 v_uv;uniform float u_overlay_scale;void main() {v_uv=a_pos/8192.0;gl_Position=projectTileWithElevation(a_pos*u_overlay_scale,get_elevation(a_pos));}\"),depth:yt(bt,\"in vec2 a_pos;void main() {\\n#ifdef GLOBE\\ngl_Position=projectTileFor3D(a_pos,0.0);\\n#else\\ngl_Position=u_projection_matrix*vec4(a_pos,0.0,1.0);\\n#endif\\n}\"),fill:yt(\"#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float opacity\\nfragColor=color*opacity;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_fill_translate;in vec2 a_pos;\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float opacity\\ngl_Position=projectTile(a_pos+u_fill_translate,a_pos);}\"),fillOutline:yt(\"in vec2 v_pos;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 outline_color\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 outline_color\\n#pragma mapbox: initialize lowp float opacity\\nfloat dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);fragColor=outline_color*(alpha*opacity);\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_world;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 outline_color\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 outline_color\\n#pragma mapbox: initialize lowp float opacity\\ngl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n}\"),fillOutlinePattern:yt(\"uniform vec2 u_texsize;uniform sampler2D u_image;uniform float u_fade;in vec2 v_pos_a;in vec2 v_pos_b;in vec2 v_pos;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);float dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);fragColor=mix(color1,color2,u_fade)*alpha*opacity;\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_world;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;out vec2 v_pos;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n}\"),fillPattern:yt(\"#ifdef GL_ES\\nprecision highp float;\\n#endif\\nuniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);fragColor=mix(color1,color2,u_fade)*opacity;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);}\"),fillExtrusion:yt(\"in vec4 v_color;void main() {fragColor=v_color;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;in vec2 a_pos;in vec4 a_normal_ed;\\n#ifdef TERRAIN3D\\nin vec2 a_centroid;\\n#endif\\nout vec4 v_color;\\n#pragma mapbox: define highp float base\\n#pragma mapbox: define highp float height\\n#pragma mapbox: define highp vec4 color\\nvoid main() {\\n#pragma mapbox: initialize highp float base\\n#pragma mapbox: initialize highp float height\\n#pragma mapbox: initialize highp vec4 color\\nvec3 normal=a_normal_ed.xyz;\\n#ifdef TERRAIN3D\\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\\n#else\\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\\n#endif\\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=mod(normal.x,2.0);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\\n#ifdef GLOBE\\nvec3 spherePos=projectToSphere(posInTile,a_pos);gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\\n#else\\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\\n#endif\\nfloat colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;vec3 normalForLighting=normal/16384.0;float directional=clamp(dot(normalForLighting,u_lightpos),0.0,1.0);\\n#ifdef GLOBE\\nmat3 rotMatrix=globeGetRotationMatrix(spherePos);normalForLighting=rotMatrix*normalForLighting;directional=mix(directional,clamp(dot(normalForLighting,u_lightpos_globe),0.0,1.0),u_projection_transition);\\n#endif\\ndirectional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.r+=clamp(color.r*directional*u_lightcolor.r,mix(0.0,0.3,1.0-u_lightcolor.r),1.0);v_color.g+=clamp(color.g*directional*u_lightcolor.g,mix(0.0,0.3,1.0-u_lightcolor.g),1.0);v_color.b+=clamp(color.b*directional*u_lightcolor.b,mix(0.0,0.3,1.0-u_lightcolor.b),1.0);v_color*=u_opacity;}\"),fillExtrusionPattern:yt(\"uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;in vec4 v_lighting;\\n#pragma mapbox: define lowp float base\\n#pragma mapbox: define lowp float height\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float base\\n#pragma mapbox: initialize lowp float height\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);vec4 mixedColor=mix(color1,color2,u_fade);fragColor=mixedColor*v_lighting;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;in vec2 a_pos;in vec4 a_normal_ed;\\n#ifdef TERRAIN3D\\nin vec2 a_centroid;\\n#endif\\n#ifdef GLOBE\\nout vec3 v_sphere_pos;\\n#endif\\nout vec2 v_pos_a;out vec2 v_pos_b;out vec4 v_lighting;\\n#pragma mapbox: define lowp float base\\n#pragma mapbox: define lowp float height\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float base\\n#pragma mapbox: initialize lowp float height\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 normal=a_normal_ed.xyz;float edgedistance=a_normal_ed.w;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;\\n#ifdef TERRAIN3D\\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\\n#else\\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\\n#endif\\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=mod(normal.x,2.0);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\\n#ifdef GLOBE\\nvec3 spherePos=projectToSphere(posInTile,a_pos);vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);v_sphere_pos=elevatedPos;gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\\n#else\\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\\n#endif\\nvec2 pos=normal.x==1.0 && normal.y==0.0 && normal.z==16384.0\\n? a_pos\\n: vec2(edgedistance,elevation*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal/16383.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;}\"),hillshadePrepare:yt(\"#ifdef GL_ES\\nprecision highp float;\\n#endif\\nuniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(vec2 coord,float bias) {vec4 data=texture(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {vec2 epsilon=1.0/u_dimension;float tileSize=u_dimension.x-2.0;float a=getElevation(v_pos+vec2(-epsilon.x,-epsilon.y),0.0);float b=getElevation(v_pos+vec2(0,-epsilon.y),0.0);float c=getElevation(v_pos+vec2(epsilon.x,-epsilon.y),0.0);float d=getElevation(v_pos+vec2(-epsilon.x,0),0.0);float e=getElevation(v_pos,0.0);float f=getElevation(v_pos+vec2(epsilon.x,0),0.0);float g=getElevation(v_pos+vec2(-epsilon.x,epsilon.y),0.0);float h=getElevation(v_pos+vec2(0,epsilon.y),0.0);float i=getElevation(v_pos+vec2(epsilon.x,epsilon.y),0.0);float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))*tileSize/pow(2.0,exaggeration+(28.2562-u_zoom));fragColor=clamp(vec4(deriv.x/8.0+0.5,deriv.y/8.0+0.5,1.0,1.0),0.0,1.0);\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform mat4 u_matrix;uniform vec2 u_dimension;in vec2 a_pos;in vec2 a_texture_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}\"),hillshade:yt(\"uniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_latrange;uniform float u_exaggeration;uniform vec4 u_accent;uniform int u_method;uniform float u_altitudes[NUM_ILLUMINATION_SOURCES];uniform float u_azimuths[NUM_ILLUMINATION_SOURCES];uniform vec4 u_shadows[NUM_ILLUMINATION_SOURCES];uniform vec4 u_highlights[NUM_ILLUMINATION_SOURCES];\\n#define PI 3.141592653589793\\n#define STANDARD 0\\n#define COMBINED 1\\n#define IGOR 2\\n#define MULTIDIRECTIONAL 3\\n#define BASIC 4\\nfloat get_aspect(vec2 deriv){return deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);}void igor_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float aspect=get_aspect(deriv);float azimuth=u_azimuths[0]+PI;float slope_stength=atan(length(deriv))*2.0/PI;float aspect_strength=1.0-abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);float shadow_strength=slope_stength*aspect_strength;float highlight_strength=slope_stength*(1.0-aspect_strength);fragColor=u_shadows[0]*shadow_strength+u_highlights[0]*highlight_strength;}void standard_hillshade(vec2 deriv){float azimuth=u_azimuths[0]+PI;float slope=atan(0.625*length(deriv));float aspect=get_aspect(deriv);float intensity=u_exaggeration;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadows[0],u_highlights[0],shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);fragColor=accent_color*(1.0-shade_color.a)+shade_color;}void basic_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor=u_highlights[0]*(2.0*shade-1.0);}else\\n{fragColor=u_shadows[0]*(1.0-2.0*shade);}}void multidirectional_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;fragColor=vec4(0,0,0,0);for(int i=0; i < NUM_ILLUMINATION_SOURCES; i++){float cos_alt=cos(u_altitudes[i]);float sin_alt=sin(u_altitudes[i]);float cos_az=-cos(u_azimuths[i]);float sin_az=-sin(u_azimuths[i]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor+=u_highlights[i]*(2.0*shade-1.0)/float(NUM_ILLUMINATION_SOURCES);}else\\n{fragColor+=u_shadows[i]*(1.0-2.0*shade)/float(NUM_ILLUMINATION_SOURCES);}}}void combined_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=acos((sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv)));cang=clamp(cang,0.0,PI/2.0);float shade=cang*atan(length(deriv))*4.0/PI/PI;float highlight=(PI/2.0-cang)*atan(length(deriv))*4.0/PI/PI;fragColor=u_shadows[0]*shade+u_highlights[0]*highlight;}void main() {vec4 pixel=texture(u_image,v_pos);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));vec2 deriv=((pixel.rg*8.0)-4.0)/scaleFactor;switch(u_method){case BASIC:\\nbasic_hillshade(deriv);break;case COMBINED:\\ncombined_hillshade(deriv);break;case IGOR:\\nigor_hillshade(deriv);break;case MULTIDIRECTIONAL:\\nmultidirectional_hillshade(deriv);break;case STANDARD:\\ndefault:\\nstandard_hillshade(deriv);break;}\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform mat4 u_matrix;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);v_pos=a_pos/8192.0;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}\"),line:yt(\"uniform lowp float u_device_pixel_ratio;in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);fragColor=color*(alpha*opacity);\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"\\n#define scale 0.015873016\\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform vec2 u_units_to_pixels;uniform lowp float u_device_pixel_ratio;out vec2 v_normal;out vec2 v_width2;out float v_gamma_scale;out highp float v_linesofar;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define mediump float gapwidth\\n#pragma mapbox: define lowp float offset\\n#pragma mapbox: define mediump float width\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump float gapwidth\\n#pragma mapbox: initialize lowp float offset\\n#pragma mapbox: initialize mediump float width\\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;v_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*2.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n#ifdef TERRAIN3D\\nv_gamma_scale=1.0;\\n#else\\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\\n#endif\\nv_width2=vec2(outset,inset);}\"),lineGradient:yt(\"uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;in highp vec2 v_uv;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);fragColor=color*(alpha*opacity);\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"\\n#define scale 0.015873016\\nin vec2 a_pos_normal;in vec4 a_data;in float a_uv_x;in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;out vec2 v_normal;out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define mediump float gapwidth\\n#pragma mapbox: define lowp float offset\\n#pragma mapbox: define mediump float width\\nvoid main() {\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump float gapwidth\\n#pragma mapbox: initialize lowp float offset\\n#pragma mapbox: initialize mediump float width\\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;highp float texel_height=1.0/u_image_height;highp float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n#ifdef TERRAIN3D\\nv_gamma_scale=1.0;\\n#else\\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\\n#endif\\nv_width2=vec2(outset,inset);}\"),linePattern:yt(\"#ifdef GL_ES\\nprecision highp float;\\n#endif\\nuniform lowp float u_device_pixel_ratio;uniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;in vec2 v_normal;in vec2 v_width2;in float v_linesofar;in float v_gamma_scale;in float v_width;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture(u_image,pos_a),texture(u_image,pos_b),u_fade);fragColor=color*alpha*opacity;\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"\\n#define scale 0.015873016\\n#define LINE_DISTANCE_SCALE 2.0\\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform vec2 u_units_to_pixels;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;out vec2 v_normal;out vec2 v_width2;out float v_linesofar;out float v_gamma_scale;out float v_width;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp float offset\\n#pragma mapbox: define mediump float gapwidth\\n#pragma mapbox: define mediump float width\\n#pragma mapbox: define lowp float floorwidth\\n#pragma mapbox: define lowp vec4 pattern_from\\n#pragma mapbox: define lowp vec4 pattern_to\\n#pragma mapbox: define lowp float pixel_ratio_from\\n#pragma mapbox: define lowp float pixel_ratio_to\\nvoid main() {\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize lowp float offset\\n#pragma mapbox: initialize mediump float gapwidth\\n#pragma mapbox: initialize mediump float width\\n#pragma mapbox: initialize lowp float floorwidth\\n#pragma mapbox: initialize mediump vec4 pattern_from\\n#pragma mapbox: initialize mediump vec4 pattern_to\\n#pragma mapbox: initialize lowp float pixel_ratio_from\\n#pragma mapbox: initialize lowp float pixel_ratio_to\\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n#ifdef TERRAIN3D\\nv_gamma_scale=1.0;\\n#else\\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\\n#endif\\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;}\"),lineSDF:yt(\"uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform float u_sdfgamma;uniform float u_mix;in vec2 v_normal;in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;\\n#ifdef GLOBE\\nin float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define mediump float width\\n#pragma mapbox: define lowp float floorwidth\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump float width\\n#pragma mapbox: initialize lowp float floorwidth\\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture(u_image,v_tex_a).a;float sdfdist_b=texture(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);alpha*=smoothstep(0.5-u_sdfgamma/floorwidth,0.5+u_sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*opacity);\\n#ifdef GLOBE\\nif (v_depth > 1.0) {discard;}\\n#endif\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"\\n#define scale 0.015873016\\n#define LINE_DISTANCE_SCALE 2.0\\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_patternscale_a;uniform float u_tex_y_a;uniform vec2 u_patternscale_b;uniform float u_tex_y_b;uniform vec2 u_units_to_pixels;out vec2 v_normal;out vec2 v_width2;out vec2 v_tex_a;out vec2 v_tex_b;out float v_gamma_scale;\\n#ifdef GLOBE\\nout float v_depth;\\n#endif\\n#pragma mapbox: define highp vec4 color\\n#pragma mapbox: define lowp float blur\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define mediump float gapwidth\\n#pragma mapbox: define lowp float offset\\n#pragma mapbox: define mediump float width\\n#pragma mapbox: define lowp float floorwidth\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 color\\n#pragma mapbox: initialize lowp float blur\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize mediump float gapwidth\\n#pragma mapbox: initialize lowp float offset\\n#pragma mapbox: initialize mediump float width\\n#pragma mapbox: initialize lowp float floorwidth\\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\\n#ifdef GLOBE\\nv_depth=gl_Position.z/gl_Position.w;\\n#endif\\n#ifdef TERRAIN3D\\nv_gamma_scale=1.0;\\n#else\\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\\n#endif\\nv_tex_a=vec2(a_linesofar*u_patternscale_a.x/floorwidth,normal.y*u_patternscale_a.y+u_tex_y_a);v_tex_b=vec2(a_linesofar*u_patternscale_b.x/floorwidth,normal.y*u_patternscale_b.y+u_tex_y_b);v_width2=vec2(outset,inset);}\"),raster:yt(\"uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;in vec2 v_pos0;in vec2 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture(u_image0,v_pos0);vec4 color1=texture(u_image1,v_pos1);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);fragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;uniform vec4 u_coords_top;uniform vec4 u_coords_bottom;in vec2 a_pos;out vec2 v_pos0;out vec2 v_pos1;void main() {vec2 fractionalPos=a_pos/8192.0;vec2 position=mix(mix(u_coords_top.xy,u_coords_top.zw,fractionalPos.x),mix(u_coords_bottom.xy,u_coords_bottom.zw,fractionalPos.x),fractionalPos.y);gl_Position=projectTile(position,position);v_pos0=((fractionalPos-0.5)/u_buffer_scale)+0.5;\\n#ifdef GLOBE\\nif (a_pos.y <-32767.5) {v_pos0.y=0.0;}if (a_pos.y > 32766.5) {v_pos0.y=1.0;}\\n#endif\\nv_pos1=(v_pos0*u_scale_parent)+u_tl_parent;}\"),symbolIcon:yt(\"uniform sampler2D u_texture;in vec2 v_tex;in float v_fade_opacity;\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\nlowp float alpha=opacity*v_fade_opacity;fragColor=texture(u_texture,v_tex)*alpha;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"in vec4 a_pos_offset;in vec4 a_data;in vec4 a_pixeloffset;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;out vec2 v_tex;out float v_fade_opacity;\\n#pragma mapbox: define lowp float opacity\\nvoid main() {\\n#pragma mapbox: initialize lowp float opacity\\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\\ncamera_to_anchor_distance/u_camera_to_center_distance :\\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\\n#ifdef GLOBE\\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\\n#endif\\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));}\"),symbolSDF:yt(\"#define SDF_PX 8.0\\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\\n#pragma mapbox: define highp vec4 fill_color\\n#pragma mapbox: define highp vec4 halo_color\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp float halo_width\\n#pragma mapbox: define lowp float halo_blur\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 fill_color\\n#pragma mapbox: initialize highp vec4 halo_color\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize lowp float halo_width\\n#pragma mapbox: initialize lowp float halo_blur\\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float fade_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);inner_edge=inner_edge+gamma*gamma_scale;}lowp float dist=texture(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);if (u_is_halo) {lowp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha=min(smoothstep(halo_edge-gamma_scaled,halo_edge+gamma_scaled,dist),1.0-alpha);}fragColor=color*(alpha*opacity*fade_opacity);\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"in vec4 a_pos_offset;in vec4 a_data;in vec4 a_pixeloffset;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;out vec2 v_data0;out vec3 v_data1;\\n#pragma mapbox: define highp vec4 fill_color\\n#pragma mapbox: define highp vec4 halo_color\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp float halo_width\\n#pragma mapbox: define lowp float halo_blur\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 fill_color\\n#pragma mapbox: initialize highp vec4 halo_color\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize lowp float halo_width\\n#pragma mapbox: initialize lowp float halo_blur\\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\\ncamera_to_anchor_distance/u_camera_to_center_distance :\\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\\n#ifdef GLOBE\\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\\n#endif\\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,interpolated_fade_opacity);}\"),symbolTextAndIcon:yt(\"#define SDF_PX 8.0\\n#define SDF 1.0\\n#define ICON 0.0\\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec4 v_data1;\\n#pragma mapbox: define highp vec4 fill_color\\n#pragma mapbox: define highp vec4 halo_color\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp float halo_width\\n#pragma mapbox: define lowp float halo_blur\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 fill_color\\n#pragma mapbox: initialize highp vec4 halo_color\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize lowp float halo_width\\n#pragma mapbox: initialize lowp float halo_blur\\nfloat fade_opacity=v_data1[2];if (v_data1.w==ICON) {vec2 tex_icon=v_data0.zw;lowp float alpha=opacity*fade_opacity;fragColor=texture(u_texture_icon,tex_icon)*alpha;\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);buff=(6.0-halo_width/fontScale)/SDF_PX;}lowp float dist=texture(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);fragColor=color*(alpha*opacity*fade_opacity);\\n#ifdef OVERDRAW_INSPECTOR\\nfragColor=vec4(1.0);\\n#endif\\n}\",\"in vec4 a_pos_offset;in vec4 a_data;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;out vec4 v_data0;out vec4 v_data1;\\n#pragma mapbox: define highp vec4 fill_color\\n#pragma mapbox: define highp vec4 halo_color\\n#pragma mapbox: define lowp float opacity\\n#pragma mapbox: define lowp float halo_width\\n#pragma mapbox: define lowp float halo_blur\\nvoid main() {\\n#pragma mapbox: initialize highp vec4 fill_color\\n#pragma mapbox: initialize highp vec4 halo_color\\n#pragma mapbox: initialize lowp float opacity\\n#pragma mapbox: initialize lowp float halo_width\\n#pragma mapbox: initialize lowp float halo_blur\\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\\ncamera_to_anchor_distance/u_camera_to_center_distance :\\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\\n#ifdef GLOBE\\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\\n#endif\\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec4(gamma_scale,size,interpolated_fade_opacity,is_sdf);}\"),terrain:yt(\"uniform sampler2D u_texture;uniform vec4 u_fog_color;uniform vec4 u_horizon_color;uniform float u_fog_ground_blend;uniform float u_fog_ground_blend_opacity;uniform float u_horizon_fog_blend;uniform bool u_is_globe_mode;in vec2 v_texture_pos;in float v_fog_depth;const float gamma=2.2;vec4 gammaToLinear(vec4 color) {return pow(color,vec4(gamma));}vec4 linearToGamma(vec4 color) {return pow(color,vec4(1.0/gamma));}void main() {vec4 surface_color=texture(u_texture,vec2(v_texture_pos.x,1.0-v_texture_pos.y));if (!u_is_globe_mode && v_fog_depth > u_fog_ground_blend) {vec4 surface_color_linear=gammaToLinear(surface_color);float blend_color=smoothstep(0.0,1.0,max((v_fog_depth-u_horizon_fog_blend)/(1.0-u_horizon_fog_blend),0.0));vec4 fog_horizon_color_linear=mix(gammaToLinear(u_fog_color),gammaToLinear(u_horizon_color),blend_color);float factor_fog=max(v_fog_depth-u_fog_ground_blend,0.0)/(1.0-u_fog_ground_blend);fragColor=linearToGamma(mix(surface_color_linear,fog_horizon_color_linear,pow(factor_fog,2.0)*u_fog_ground_blend_opacity));} else {fragColor=surface_color;}}\",\"in vec3 a_pos3d;uniform mat4 u_fog_matrix;uniform float u_ele_delta;out vec2 v_texture_pos;out float v_fog_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,get_elevation(a_pos3d.xy)-ele_delta);vec4 pos=u_fog_matrix*vec4(a_pos3d.xy,ele,1.0);v_fog_depth=pos.z/pos.w*0.5+0.5;}\"),terrainDepth:yt(\"in float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {fragColor=pack(v_depth);}\",\"in vec3 a_pos3d;uniform float u_ele_delta;out float v_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);v_depth=gl_Position.z/gl_Position.w;}\"),terrainCoords:yt(\"precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;in vec2 v_texture_pos;void main() {vec4 rgba=texture(u_texture,v_texture_pos);fragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}\",\"in vec3 a_pos3d;uniform float u_ele_delta;out vec2 v_texture_pos;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);}\"),projectionErrorMeasurement:yt(\"in vec4 v_output_error_encoded;void main() {fragColor=v_output_error_encoded;}\",\"in vec2 a_pos;uniform highp float u_input;uniform highp float u_output_expected;out vec4 v_output_error_encoded;void main() {float real_output=2.0*atan(exp(PI-(u_input*PI*2.0)))-PI*0.5;float error=real_output-u_output_expected;float abs_error=abs(error)*128.0;v_output_error_encoded.x=min(floor(abs_error*256.0),255.0)/255.0;abs_error-=v_output_error_encoded.x;v_output_error_encoded.y=min(floor(abs_error*65536.0),255.0)/255.0;abs_error-=v_output_error_encoded.x/255.0;v_output_error_encoded.z=min(floor(abs_error*16777216.0),255.0)/255.0;v_output_error_encoded.w=error >=0.0 ? 1.0 : 0.0;gl_Position=vec4(a_pos,0.0,1.0);}\"),atmosphere:yt(\"in vec3 view_direction;uniform vec3 u_sun_pos;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;/**Shader use from https:*Made some change to adapt to MapLibre Globe geometry*/const float PI=3.141592653589793;const int iSteps=5;const int jSteps=3;/*radius of the planet*/const float EARTH_RADIUS=6371e3;/*radius of the atmosphere*/const float ATMOS_RADIUS=6471e3;vec2 rsi(vec3 r0,vec3 rd,float sr) {float a=dot(rd,rd);float b=2.0*dot(rd,r0);float c=dot(r0,r0)-(sr*sr);float d=(b*b)-4.0*a*c;if (d < 0.0) return vec2(1e5,-1e5);return vec2((-b-sqrt(d))/(2.0*a),(-b+sqrt(d))/(2.0*a));}vec4 atmosphere(vec3 r,vec3 r0,vec3 pSun,float iSun,float rPlanet,float rAtmos,vec3 kRlh,float kMie,float shRlh,float shMie,float g) {pSun=normalize(pSun);r=normalize(r);vec2 p=rsi(r0,r,rAtmos);if (p.x > p.y) {return vec4(0.0,0.0,0.0,1.0);}if (p.x < 0.0) {p.x=0.0;}vec3 pos=r0+r*p.x;vec2 p2=rsi(r0,r,rPlanet);if (p2.x <=p2.y && p2.x > 0.0) {p.y=min(p.y,p2.x);}float iStepSize=(p.y-p.x)/float(iSteps);float iTime=p.x+iStepSize*0.5;vec3 totalRlh=vec3(0,0,0);vec3 totalMie=vec3(0,0,0);float iOdRlh=0.0;float iOdMie=0.0;float mu=dot(r,pSun);float mumu=mu*mu;float gg=g*g;float pRlh=3.0/(16.0*PI)*(1.0+mumu);float pMie=3.0/(8.0*PI)*((1.0-gg)*(mumu+1.0))/(pow(1.0+gg-2.0*mu*g,1.5)*(2.0+gg));for (int i=0; i < iSteps; i++) {vec3 iPos=r0+r*iTime;float iHeight=length(iPos)-rPlanet;float odStepRlh=exp(-iHeight/shRlh)*iStepSize;float odStepMie=exp(-iHeight/shMie)*iStepSize;iOdRlh+=odStepRlh;iOdMie+=odStepMie;float jStepSize=rsi(iPos,pSun,rAtmos).y/float(jSteps);float jTime=jStepSize*0.5;float jOdRlh=0.0;float jOdMie=0.0;for (int j=0; j < jSteps; j++) {vec3 jPos=iPos+pSun*jTime;float jHeight=length(jPos)-rPlanet;jOdRlh+=exp(-jHeight/shRlh)*jStepSize;jOdMie+=exp(-jHeight/shMie)*jStepSize;jTime+=jStepSize;}vec3 attn=exp(-(kMie*(iOdMie+jOdMie)+kRlh*(iOdRlh+jOdRlh)));totalRlh+=odStepRlh*attn;totalMie+=odStepMie*attn;iTime+=iStepSize;}float opacity=exp(-(length(kRlh)*length(totalRlh)+kMie*length(totalMie)));vec3 color=iSun*(pRlh*kRlh*totalRlh+pMie*kMie*totalMie);return vec4(color,opacity);}void main() {vec3 scale_camera_pos=-u_globe_position*EARTH_RADIUS/u_globe_radius;vec4 color=atmosphere(normalize(view_direction),scale_camera_pos,u_sun_pos,22.0,EARTH_RADIUS,ATMOS_RADIUS,vec3(5.5e-6,13.0e-6,22.4e-6),21e-6,8e3,1.2e3,0.758\\n);color.rgb=1.0-exp(-1.0*color.rgb);color=pow(color,vec4(1.0/2.2));fragColor=vec4(color.rgb,1.0-color.a)*u_atmosphere_blend;}\",\"in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 view_direction;void main() {view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,0.0,1.0);}\"),sky:yt(\"uniform vec4 u_sky_color;uniform vec4 u_horizon_color;uniform vec2 u_horizon;uniform vec2 u_horizon_normal;uniform float u_sky_horizon_blend;uniform float u_sky_blend;void main() {float x=gl_FragCoord.x;float y=gl_FragCoord.y;float blend=(y-u_horizon.y)*u_horizon_normal.y+(x-u_horizon.x)*u_horizon_normal.x;if (blend > 0.0) {if (blend < u_sky_horizon_blend) {fragColor=mix(u_sky_color,u_horizon_color,pow(1.0-blend/u_sky_horizon_blend,2.0));} else {fragColor=u_sky_color;}}fragColor=mix(fragColor,vec4(vec3(0.0),0.0),u_sky_blend);}\",\"in vec2 a_pos;void main() {gl_Position=vec4(a_pos,1.0,1.0);}\")};function yt(e,t){const i=/#pragma mapbox: ([\\w]+) ([\\w]+) ([\\w]+) ([\\w]+)/g,o=t.match(/in ([\\w]+) ([\\w]+)/g),r=e.match(/uniform ([\\w]+) ([\\w]+)([\\s]*)([\\w]*)/g),a=t.match(/uniform ([\\w]+) ([\\w]+)([\\s]*)([\\w]*)/g),s=a?a.concat(r):r,n={};return {fragmentSource:e=e.replace(i,((e,t,i,o,r)=>(n[r]=!0,\"define\"===t?`\\n#ifndef HAS_UNIFORM_u_${r}\\nin ${i} ${o} ${r};\\n#else\\nuniform ${i} ${o} u_${r};\\n#endif\\n`:`\\n#ifdef HAS_UNIFORM_u_${r}\\n    ${i} ${o} ${r} = u_${r};\\n#endif\\n`))),vertexSource:t=t.replace(i,((e,t,i,o,r)=>{const a=\"float\"===o?\"vec2\":\"vec4\",s=r.match(/color/)?\"color\":a;return n[r]?\"define\"===t?`\\n#ifndef HAS_UNIFORM_u_${r}\\nuniform lowp float u_${r}_t;\\nin ${i} ${a} a_${r};\\nout ${i} ${o} ${r};\\n#else\\nuniform ${i} ${o} u_${r};\\n#endif\\n`:\"vec4\"===s?`\\n#ifndef HAS_UNIFORM_u_${r}\\n    ${r} = a_${r};\\n#else\\n    ${i} ${o} ${r} = u_${r};\\n#endif\\n`:`\\n#ifndef HAS_UNIFORM_u_${r}\\n    ${r} = unpack_mix_${s}(a_${r}, u_${r}_t);\\n#else\\n    ${i} ${o} ${r} = u_${r};\\n#endif\\n`:\"define\"===t?`\\n#ifndef HAS_UNIFORM_u_${r}\\nuniform lowp float u_${r}_t;\\nin ${i} ${a} a_${r};\\n#else\\nuniform ${i} ${o} u_${r};\\n#endif\\n`:\"vec4\"===s?`\\n#ifndef HAS_UNIFORM_u_${r}\\n    ${i} ${o} ${r} = a_${r};\\n#else\\n    ${i} ${o} ${r} = u_${r};\\n#endif\\n`:`\\n#ifndef HAS_UNIFORM_u_${r}\\n    ${i} ${o} ${r} = unpack_mix_${s}(a_${r}, u_${r}_t);\\n#else\\n    ${i} ${o} ${r} = u_${r};\\n#endif\\n`})),staticAttributes:o,staticUniforms:s}}class wt{constructor(e,t,i){this.vertexBuffer=e,this.indexBuffer=t,this.segments=i;}destroy(){this.vertexBuffer.destroy(),this.indexBuffer.destroy(),this.segments.destroy(),this.vertexBuffer=null,this.indexBuffer=null,this.segments=null;}}var Tt=t.aI([{name:\"a_pos\",type:\"Int16\",components:2}]);const Pt=\"#define PROJECTION_MERCATOR\",Ct=\"mercator\";class It{constructor(){this._cachedMesh=null;}get name(){return \"mercator\"}get useSubdivision(){return !1}get shaderVariantName(){return Ct}get shaderDefine(){return Pt}get shaderPreludeCode(){return xt.projectionMercator}get vertexShaderPreludeCode(){return xt.projectionMercator.vertexSource}get subdivisionGranularity(){return t.aJ.noSubdivision}get useGlobeControls(){return !1}get transitionState(){return 0}get latitudeErrorCorrectionRadians(){return 0}destroy(){}updateGPUdependent(e){}getMeshFromTileID(e,i,o,r,a){if(this._cachedMesh)return this._cachedMesh;const s=new t.aK;s.emplaceBack(0,0),s.emplaceBack(t.$,0),s.emplaceBack(0,t.$),s.emplaceBack(t.$,t.$);const n=e.createVertexBuffer(s,Tt.members),l=t.aL.simpleSegment(0,0,4,2),c=new t.aM;c.emplaceBack(1,0,2),c.emplaceBack(1,2,3);const h=e.createIndexBuffer(c);return this._cachedMesh=new wt(n,h,l),this._cachedMesh}recalculate(){}hasTransition(){return !1}setErrorQueryLatitudeDegrees(e){}}class Mt{constructor(e=0,t=0,i=0,o=0){if(isNaN(e)||e<0||isNaN(t)||t<0||isNaN(i)||i<0||isNaN(o)||o<0)throw new Error(\"Invalid value for edge-insets, top, bottom, left and right must all be numbers\");this.top=e,this.bottom=t,this.left=i,this.right=o;}interpolate(e,i,o){return null!=i.top&&null!=e.top&&(this.top=t.C.number(e.top,i.top,o)),null!=i.bottom&&null!=e.bottom&&(this.bottom=t.C.number(e.bottom,i.bottom,o)),null!=i.left&&null!=e.left&&(this.left=t.C.number(e.left,i.left,o)),null!=i.right&&null!=e.right&&(this.right=t.C.number(e.right,i.right,o)),this}getCenter(e,i){const o=t.ag((this.left+e-this.right)/2,0,e),r=t.ag((this.top+i-this.bottom)/2,0,i);return new t.P(o,r)}equals(e){return this.top===e.top&&this.bottom===e.bottom&&this.left===e.left&&this.right===e.right}clone(){return new Mt(this.top,this.bottom,this.left,this.right)}toJSON(){return {top:this.top,bottom:this.bottom,left:this.left,right:this.right}}}function St(e,t){if(!e.renderWorldCopies||e.lngRange)return;const i=t.lng-e.center.lng;t.lng+=i>180?-360:i<-180?360:0;}function Et(e){return Math.max(0,Math.floor(e))}class Rt{constructor(e,i,o,r,a,s){this._callbacks=e,this._tileSize=512,this._renderWorldCopies=void 0===s||!!s,this._minZoom=i||0,this._maxZoom=o||22,this._minPitch=null==r?0:r,this._maxPitch=null==a?60:a,this.setMaxBounds(),this._width=0,this._height=0,this._center=new t.S(0,0),this._elevation=0,this._zoom=0,this._tileZoom=Et(this._zoom),this._scale=t.ae(this._zoom),this._bearingInRadians=0,this._fovInRadians=.6435011087932844,this._pitchInRadians=0,this._rollInRadians=0,this._unmodified=!0,this._edgeInsets=new Mt,this._minElevationForCurrentTile=0,this._autoCalculateNearFarZ=!0;}apply(e,i,o){this._latRange=e.latRange,this._lngRange=e.lngRange,this._width=e.width,this._height=e.height,this._center=e.center,this._elevation=e.elevation,this._minElevationForCurrentTile=e.minElevationForCurrentTile,this._zoom=e.zoom,this._tileZoom=Et(this._zoom),this._scale=t.ae(this._zoom),this._bearingInRadians=e.bearingInRadians,this._fovInRadians=e.fovInRadians,this._pitchInRadians=e.pitchInRadians,this._rollInRadians=e.rollInRadians,this._unmodified=e.unmodified,this._edgeInsets=new Mt(e.padding.top,e.padding.bottom,e.padding.left,e.padding.right),this._minZoom=e.minZoom,this._maxZoom=e.maxZoom,this._minPitch=e.minPitch,this._maxPitch=e.maxPitch,this._renderWorldCopies=e.renderWorldCopies,this._cameraToCenterDistance=e.cameraToCenterDistance,this._nearZ=e.nearZ,this._farZ=e.farZ,this._autoCalculateNearFarZ=!o&&e.autoCalculateNearFarZ,i&&this._constrain(),this._calcMatrices();}get pixelsToClipSpaceMatrix(){return this._pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._clipSpaceToPixelsMatrix}get minElevationForCurrentTile(){return this._minElevationForCurrentTile}setMinElevationForCurrentTile(e){this._minElevationForCurrentTile=e;}get tileSize(){return this._tileSize}get tileZoom(){return this._tileZoom}get scale(){return this._scale}get width(){return this._width}get height(){return this._height}get bearingInRadians(){return this._bearingInRadians}get lngRange(){return this._lngRange}get latRange(){return this._latRange}get pixelsToGLUnits(){return this._pixelsToGLUnits}get minZoom(){return this._minZoom}setMinZoom(e){this._minZoom!==e&&(this._minZoom=e,this.setZoom(this.getConstrained(this._center,this.zoom).zoom));}get maxZoom(){return this._maxZoom}setMaxZoom(e){this._maxZoom!==e&&(this._maxZoom=e,this.setZoom(this.getConstrained(this._center,this.zoom).zoom));}get minPitch(){return this._minPitch}setMinPitch(e){this._minPitch!==e&&(this._minPitch=e,this.setPitch(Math.max(this.pitch,e)));}get maxPitch(){return this._maxPitch}setMaxPitch(e){this._maxPitch!==e&&(this._maxPitch=e,this.setPitch(Math.min(this.pitch,e)));}get renderWorldCopies(){return this._renderWorldCopies}setRenderWorldCopies(e){void 0===e?e=!0:null===e&&(e=!1),this._renderWorldCopies=e;}get worldSize(){return this._tileSize*this._scale}get centerOffset(){return this.centerPoint._sub(this.size._div(2))}get size(){return new t.P(this._width,this._height)}get bearing(){return this._bearingInRadians/Math.PI*180}setBearing(e){const i=t.aN(e,-180,180)*Math.PI/180;var r,a,s,n,l,c,h,u,d;this._bearingInRadians!==i&&(this._unmodified=!1,this._bearingInRadians=i,this._calcMatrices(),this._rotationMatrix=o(),r=this._rotationMatrix,s=-this._bearingInRadians,n=(a=this._rotationMatrix)[0],l=a[1],c=a[2],h=a[3],u=Math.sin(s),d=Math.cos(s),r[0]=n*d+c*u,r[1]=l*d+h*u,r[2]=n*-u+c*d,r[3]=l*-u+h*d);}get rotationMatrix(){return this._rotationMatrix}get pitchInRadians(){return this._pitchInRadians}get pitch(){return this._pitchInRadians/Math.PI*180}setPitch(e){const i=t.ag(e,this.minPitch,this.maxPitch)/180*Math.PI;this._pitchInRadians!==i&&(this._unmodified=!1,this._pitchInRadians=i,this._calcMatrices());}get rollInRadians(){return this._rollInRadians}get roll(){return this._rollInRadians/Math.PI*180}setRoll(e){const t=e/180*Math.PI;this._rollInRadians!==t&&(this._unmodified=!1,this._rollInRadians=t,this._calcMatrices());}get fovInRadians(){return this._fovInRadians}get fov(){return t.aO(this._fovInRadians)}setFov(e){e=t.ag(e,.1,150),this.fov!==e&&(this._unmodified=!1,this._fovInRadians=t.ad(e),this._calcMatrices());}get zoom(){return this._zoom}setZoom(e){const i=this.getConstrained(this._center,e).zoom;this._zoom!==i&&(this._unmodified=!1,this._zoom=i,this._tileZoom=Math.max(0,Math.floor(i)),this._scale=t.ae(i),this._constrain(),this._calcMatrices());}get center(){return this._center}setCenter(e){e.lat===this._center.lat&&e.lng===this._center.lng||(this._unmodified=!1,this._center=e,this._constrain(),this._calcMatrices());}get elevation(){return this._elevation}setElevation(e){e!==this._elevation&&(this._elevation=e,this._constrain(),this._calcMatrices());}get padding(){return this._edgeInsets.toJSON()}setPadding(e){this._edgeInsets.equals(e)||(this._unmodified=!1,this._edgeInsets.interpolate(this._edgeInsets,e,1),this._calcMatrices());}get centerPoint(){return this._edgeInsets.getCenter(this._width,this._height)}get pixelsPerMeter(){return this._pixelPerMeter}get unmodified(){return this._unmodified}get cameraToCenterDistance(){return this._cameraToCenterDistance}get nearZ(){return this._nearZ}get farZ(){return this._farZ}get autoCalculateNearFarZ(){return this._autoCalculateNearFarZ}overrideNearFarZ(e,t){this._autoCalculateNearFarZ=!1,this._nearZ=e,this._farZ=t,this._calcMatrices();}clearNearFarZOverride(){this._autoCalculateNearFarZ=!0,this._calcMatrices();}isPaddingEqual(e){return this._edgeInsets.equals(e)}interpolatePadding(e,t,i){this._unmodified=!1,this._edgeInsets.interpolate(e,t,i),this._constrain(),this._calcMatrices();}resize(e,t,i=!0){this._width=e,this._height=t,i&&this._constrain(),this._calcMatrices();}getMaxBounds(){return this._latRange&&2===this._latRange.length&&this._lngRange&&2===this._lngRange.length?new G([this._lngRange[0],this._latRange[0]],[this._lngRange[1],this._latRange[1]]):null}setMaxBounds(e){e?(this._lngRange=[e.getWest(),e.getEast()],this._latRange=[e.getSouth(),e.getNorth()],this._constrain()):(this._lngRange=null,this._latRange=[-85.051129,t.ah]);}getConstrained(e,t){return this._callbacks.getConstrained(e,t)}getCameraQueryGeometry(e,i){if(1===i.length)return [i[0],e];{const{minX:o,minY:r,maxX:a,maxY:s}=t.a1.fromPoints(i).extend(e);return [new t.P(o,r),new t.P(a,r),new t.P(a,s),new t.P(o,s),new t.P(o,r)]}}_constrain(){if(!this.center||!this._width||!this._height||this._constraining)return;this._constraining=!0;const e=this._unmodified,{center:t,zoom:i}=this.getConstrained(this.center,this.zoom);this.setCenter(t),this.setZoom(i),this._unmodified=e,this._constraining=!1;}_calcMatrices(){if(this._width&&this._height){this._pixelsToGLUnits=[2/this._width,-2/this._height];let e=t.af(new Float64Array(16));t.N(e,e,[this._width/2,-this._height/2,1]),t.M(e,e,[1,-1,0]),this._clipSpaceToPixelsMatrix=e,e=t.af(new Float64Array(16)),t.N(e,e,[1,-1,1]),t.M(e,e,[-1,-1,0]),t.N(e,e,[2/this._width,2/this._height,1]),this._pixelsToClipSpaceMatrix=e,this._cameraToCenterDistance=.5/Math.tan(this.fovInRadians/2)*this._height;}this._callbacks.calcMatrices();}calculateCenterFromCameraLngLatAlt(e,i,o,r){const a=void 0!==o?o:this.bearing,s=r=void 0!==r?r:this.pitch,n=t.a0.fromLngLat(e,i),l=-Math.cos(t.ad(s)),c=Math.sin(t.ad(s)),h=c*Math.sin(t.ad(a)),u=-c*Math.cos(t.ad(a));let d=this.elevation;const _=i-d;let p;l*_>=0||Math.abs(l)<.1?(p=1e4,d=i+p*l):p=-_/l;let m,f,g=t.aP(1,n.y),v=0;do{if(v+=1,v>10)break;f=p/g,m=new t.a0(n.x+h*f,n.y+u*f),g=1/m.meterInMercatorCoordinateUnits();}while(Math.abs(p-f*g)>1e-12);return {center:m.toLngLat(),elevation:d,zoom:t.aj(this.height/2/Math.tan(this.fovInRadians/2)/f/this.tileSize)}}recalculateZoomAndCenter(e){if(this.elevation-e==0)return;const i=t.ai(1,this.center.lat)*this.worldSize,o=this.cameraToCenterDistance/i,r=t.a0.fromLngLat(this.center,this.elevation),a=de(this.center,this.elevation,this.pitch,this.bearing,o);this._elevation=e;const s=this.calculateCenterFromCameraLngLatAlt(a.toLngLat(),t.aP(a.z,r.y),this.bearing,this.pitch);this._elevation=s.elevation,this._center=s.center,this.setZoom(s.zoom);}getCameraPoint(){const e=Math.tan(this.pitchInRadians)*(this.cameraToCenterDistance||1);return this.centerPoint.add(new t.P(e*Math.sin(this.rollInRadians),e*Math.cos(this.rollInRadians)))}getCameraAltitude(){return Math.cos(this.pitchInRadians)*this._cameraToCenterDistance/this._pixelPerMeter+this.elevation}getCameraLngLat(){const e=t.ai(1,this.center.lat)*this.worldSize;return de(this.center,this.elevation,this.pitch,this.bearing,this.cameraToCenterDistance/e).toLngLat()}getMercatorTileCoordinates(e){if(!e)return [0,0,1,1];const i=e.canonical.z>=0?1<<e.canonical.z:Math.pow(2,e.canonical.z);return [e.canonical.x/i,e.canonical.y/i,1/i/t.$,1/i/t.$]}}class zt{constructor(e,i){this.min=e,this.max=i,this.center=t.aQ([],t.aR([],this.min,this.max),.5);}quadrant(e){const i=[e%2==0,e<2],o=t.aS(this.min),r=t.aS(this.max);for(let e=0;e<i.length;e++)o[e]=i[e]?this.min[e]:this.center[e],r[e]=i[e]?this.center[e]:this.max[e];return r[2]=this.max[2],new zt(o,r)}distanceX(e){return Math.max(Math.min(this.max[0],e[0]),this.min[0])-e[0]}distanceY(e){return Math.max(Math.min(this.max[1],e[1]),this.min[1])-e[1]}intersectsFrustum(e){let t=!0;for(let i=0;i<e.planes.length;i++){const o=this.intersectsPlane(e.planes[i]);if(0===o)return 0;1===o&&(t=!1);}return t?2:e.aabb.min[0]>this.max[0]||e.aabb.min[1]>this.max[1]||e.aabb.min[2]>this.max[2]||e.aabb.max[0]<this.min[0]||e.aabb.max[1]<this.min[1]||e.aabb.max[2]<this.min[2]?0:1}intersectsPlane(e){let t=e[3],i=e[3];for(let o=0;o<3;o++)e[o]>0?(t+=e[o]*this.min[o],i+=e[o]*this.max[o]):(i+=e[o]*this.min[o],t+=e[o]*this.max[o]);return t>=0?2:i<0?0:1}}class Dt{distanceToTile2d(e,t,i,o){const r=o.distanceX([e,t]),a=o.distanceY([e,t]);return Math.hypot(r,a)}getWrap(e,t,i){return i}getTileBoundingVolume(e,i,o,r){var a,s;let n=o,l=o;if(null==r?void 0:r.terrain){const c=new t.Z(e.z,i,e.z,e.x,e.y),h=r.terrain.getMinMaxElevation(c);n=null!==(a=h.minElevation)&&void 0!==a?a:o,l=null!==(s=h.maxElevation)&&void 0!==s?s:o;}const c=1<<e.z;return new zt([i+e.x/c,e.y/c,n],[i+(e.x+1)/c,(e.y+1)/c,l])}allowVariableZoom(e,i){const o=e.fov*(Math.abs(Math.cos(e.rollInRadians))*e.height+Math.abs(Math.sin(e.rollInRadians))*e.width)/e.height,r=t.ag(78.5-o/2,0,60);return !!i.terrain||e.pitch>r}allowWorldCopies(){return !0}prepareNextFrame(){}}class At{constructor(e,t,i){this.points=e,this.planes=t,this.aabb=i;}static fromInvProjectionMatrix(e,i=1,o=0,r,a){const s=a?[[6,5,4],[0,1,2],[0,3,7],[2,1,5],[3,2,6],[0,4,5]]:[[0,1,2],[6,5,4],[0,3,7],[2,1,5],[3,2,6],[0,4,5]],n=Math.pow(2,o),l=[[-1,1,-1,1],[1,1,-1,1],[1,-1,-1,1],[-1,-1,-1,1],[-1,1,1,1],[1,1,1,1],[1,-1,1,1],[-1,-1,1,1]].map((o=>function(e,i,o,r){const a=t.av([],e,i),s=1/a[3]/o*r;return t.aX(a,a,[s,s,1/a[3],s])}(o,e,i,n)));r&&function(e,i,o,r){const a=r?4:0,s=r?0:4;let n=0;const l=[],c=[];for(let i=0;i<4;i++){const o=t.aT([],e[i+s],e[i+a]),r=t.aY(o);t.aQ(o,o,1/r),l.push(r),c.push(o);}for(let i=0;i<4;i++){const r=t.aZ(e[i+a],c[i],o);n=null!==r&&r>=0?Math.max(n,r):Math.max(n,l[i]);}const h=function(e,i){const o=t.aT([],e[i[0]],e[i[1]]),r=t.aT([],e[i[2]],e[i[1]]),a=[0,0,0,0];return t.aU(a,t.aV([],o,r)),a[3]=-t.aW(a,e[i[0]]),a}(e,i),u=function(e,i){const o=t.a_(e),r=t.a$([],e,1/o),a=t.aT([],i,t.aQ([],r,t.aW(i,r))),s=t.a_(a);if(s>0){const e=Math.sqrt(1-r[3]*r[3]),o=t.aQ([],r,-r[3]),n=t.aR([],o,t.aQ([],a,e/s));return t.b0(i,n)}return null}(o,h);if(null!==u){const e=u/t.aW(c[0],h);n=Math.min(n,e);}for(let t=0;t<4;t++){const i=Math.min(n,l[t]);e[t+s]=[e[t+a][0]+c[t][0]*i,e[t+a][1]+c[t][1]*i,e[t+a][2]+c[t][2]*i,1];}}(l,s[0],r,a);const c=s.map((e=>{const i=t.aT([],l[e[0]],l[e[1]]),o=t.aT([],l[e[2]],l[e[1]]),r=t.aU([],t.aV([],i,o)),a=-t.aW(r,l[e[1]]);return r.concat(a)})),h=[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY],u=[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];for(const e of l)for(let t=0;t<3;t++)h[t]=Math.min(h[t],e[t]),u[t]=Math.max(u[t],e[t]);return new At(l,c,new zt(h,u))}}class Lt{get pixelsToClipSpaceMatrix(){return this._helper.pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._helper.clipSpaceToPixelsMatrix}get pixelsToGLUnits(){return this._helper.pixelsToGLUnits}get centerOffset(){return this._helper.centerOffset}get size(){return this._helper.size}get rotationMatrix(){return this._helper.rotationMatrix}get centerPoint(){return this._helper.centerPoint}get pixelsPerMeter(){return this._helper.pixelsPerMeter}setMinZoom(e){this._helper.setMinZoom(e);}setMaxZoom(e){this._helper.setMaxZoom(e);}setMinPitch(e){this._helper.setMinPitch(e);}setMaxPitch(e){this._helper.setMaxPitch(e);}setRenderWorldCopies(e){this._helper.setRenderWorldCopies(e);}setBearing(e){this._helper.setBearing(e);}setPitch(e){this._helper.setPitch(e);}setRoll(e){this._helper.setRoll(e);}setFov(e){this._helper.setFov(e);}setZoom(e){this._helper.setZoom(e);}setCenter(e){this._helper.setCenter(e);}setElevation(e){this._helper.setElevation(e);}setMinElevationForCurrentTile(e){this._helper.setMinElevationForCurrentTile(e);}setPadding(e){this._helper.setPadding(e);}interpolatePadding(e,t,i){return this._helper.interpolatePadding(e,t,i)}isPaddingEqual(e){return this._helper.isPaddingEqual(e)}resize(e,t,i=!0){this._helper.resize(e,t,i);}getMaxBounds(){return this._helper.getMaxBounds()}setMaxBounds(e){this._helper.setMaxBounds(e);}overrideNearFarZ(e,t){this._helper.overrideNearFarZ(e,t);}clearNearFarZOverride(){this._helper.clearNearFarZOverride();}getCameraQueryGeometry(e){return this._helper.getCameraQueryGeometry(this.getCameraPoint(),e)}get tileSize(){return this._helper.tileSize}get tileZoom(){return this._helper.tileZoom}get scale(){return this._helper.scale}get worldSize(){return this._helper.worldSize}get width(){return this._helper.width}get height(){return this._helper.height}get lngRange(){return this._helper.lngRange}get latRange(){return this._helper.latRange}get minZoom(){return this._helper.minZoom}get maxZoom(){return this._helper.maxZoom}get zoom(){return this._helper.zoom}get center(){return this._helper.center}get minPitch(){return this._helper.minPitch}get maxPitch(){return this._helper.maxPitch}get pitch(){return this._helper.pitch}get pitchInRadians(){return this._helper.pitchInRadians}get roll(){return this._helper.roll}get rollInRadians(){return this._helper.rollInRadians}get bearing(){return this._helper.bearing}get bearingInRadians(){return this._helper.bearingInRadians}get fov(){return this._helper.fov}get fovInRadians(){return this._helper.fovInRadians}get elevation(){return this._helper.elevation}get minElevationForCurrentTile(){return this._helper.minElevationForCurrentTile}get padding(){return this._helper.padding}get unmodified(){return this._helper.unmodified}get renderWorldCopies(){return this._helper.renderWorldCopies}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}get nearZ(){return this._helper.nearZ}get farZ(){return this._helper.farZ}get autoCalculateNearFarZ(){return this._helper.autoCalculateNearFarZ}setTransitionState(e,t){}constructor(e,t,i,o,r){this._posMatrixCache=new Map,this._alignedPosMatrixCache=new Map,this._fogMatrixCacheF32=new Map,this._helper=new Rt({calcMatrices:()=>{this._calcMatrices();},getConstrained:(e,t)=>this.getConstrained(e,t)},e,t,i,o,r),this._coveringTilesDetailsProvider=new Dt;}clone(){const e=new Lt;return e.apply(this),e}apply(e,t,i){this._helper.apply(e,t,i);}get cameraPosition(){return this._cameraPosition}get projectionMatrix(){return this._projectionMatrix}get modelViewProjectionMatrix(){return this._viewProjMatrix}get inverseProjectionMatrix(){return this._invProjMatrix}get mercatorMatrix(){return this._mercatorMatrix}getVisibleUnwrappedCoordinates(e){const i=[new t.b1(0,e)];if(this._helper._renderWorldCopies){const o=this.screenPointToMercatorCoordinate(new t.P(0,0)),r=this.screenPointToMercatorCoordinate(new t.P(this._helper._width,0)),a=this.screenPointToMercatorCoordinate(new t.P(this._helper._width,this._helper._height)),s=this.screenPointToMercatorCoordinate(new t.P(0,this._helper._height)),n=Math.floor(Math.min(o.x,r.x,a.x,s.x)),l=Math.floor(Math.max(o.x,r.x,a.x,s.x)),c=1;for(let o=n-c;o<=l+c;o++)0!==o&&i.push(new t.b1(o,e));}return i}getCameraFrustum(){return At.fromInvProjectionMatrix(this._invViewProjMatrix,this.worldSize)}getClippingPlane(){return null}getCoveringTilesDetailsProvider(){return this._coveringTilesDetailsProvider}recalculateZoomAndCenter(e){const t=this.screenPointToLocation(this.centerPoint,e),i=e?e.getElevationForLngLatZoom(t,this._helper._tileZoom):0;this._helper.recalculateZoomAndCenter(i);}setLocationAtPoint(e,i){const o=t.ai(this.elevation,this.center.lat),r=this.screenPointToMercatorCoordinateAtZ(i,o),a=this.screenPointToMercatorCoordinateAtZ(this.centerPoint,o),s=t.a0.fromLngLat(e),n=new t.a0(s.x-(r.x-a.x),s.y-(r.y-a.y));this.setCenter(null==n?void 0:n.toLngLat()),this._helper._renderWorldCopies&&this.setCenter(this.center.wrap());}locationToScreenPoint(e,i){return i?this.coordinatePoint(t.a0.fromLngLat(e),i.getElevationForLngLatZoom(e,this._helper._tileZoom),this._pixelMatrix3D):this.coordinatePoint(t.a0.fromLngLat(e))}screenPointToLocation(e,t){var i;return null===(i=this.screenPointToMercatorCoordinate(e,t))||void 0===i?void 0:i.toLngLat()}screenPointToMercatorCoordinate(e,t){if(t){const i=t.pointCoordinate(e);if(null!=i)return i}return this.screenPointToMercatorCoordinateAtZ(e)}screenPointToMercatorCoordinateAtZ(e,i){const o=i||0,r=[e.x,e.y,0,1],a=[e.x,e.y,1,1];t.av(r,r,this._pixelMatrixInverse),t.av(a,a,this._pixelMatrixInverse);const s=r[3],n=a[3],l=r[1]/s,c=a[1]/n,h=r[2]/s,u=a[2]/n,d=h===u?0:(o-h)/(u-h);return new t.a0(t.C.number(r[0]/s,a[0]/n,d)/this.worldSize,t.C.number(l,c,d)/this.worldSize,o)}coordinatePoint(e,i=0,o=this._pixelMatrix){const r=[e.x*this.worldSize,e.y*this.worldSize,i,1];return t.av(r,r,o),new t.P(r[0]/r[3],r[1]/r[3])}getBounds(){const e=Math.max(0,this._helper._height/2-he(this));return (new G).extend(this.screenPointToLocation(new t.P(0,e))).extend(this.screenPointToLocation(new t.P(this._helper._width,e))).extend(this.screenPointToLocation(new t.P(this._helper._width,this._helper._height))).extend(this.screenPointToLocation(new t.P(0,this._helper._height)))}isPointOnMapSurface(e,t){return t?null!=t.pointCoordinate(e):e.y>this.height/2-he(this)}calculatePosMatrix(e,i=!1,o){var r;const a=null!==(r=e.key)&&void 0!==r?r:t.b2(e.wrap,e.canonical.z,e.canonical.z,e.canonical.x,e.canonical.y),s=i?this._alignedPosMatrixCache:this._posMatrixCache;if(s.has(a)){const e=s.get(a);return o?e.f32:e.f64}const n=ue(e,this.worldSize);t.O(n,i?this._alignedProjMatrix:this._viewProjMatrix,n);const l={f64:n,f32:new Float32Array(n)};return s.set(a,l),o?l.f32:l.f64}calculateFogMatrix(e){const i=e.key,o=this._fogMatrixCacheF32;if(o.has(i))return o.get(i);const r=ue(e,this.worldSize);return t.O(r,this._fogMatrix,r),o.set(i,new Float32Array(r)),o.get(i)}getConstrained(e,i){i=t.ag(+i,this.minZoom,this.maxZoom);const o={center:new t.S(e.lng,e.lat),zoom:i};let r=this._helper._lngRange;this._helper._renderWorldCopies||null!==r||(r=[-179.9999999999,180-1e-10]);const a=this.tileSize*t.ae(o.zoom);let s=0,n=a,l=0,c=a,h=0,u=0;const{x:d,y:_}=this.size;if(this._helper._latRange){const e=this._helper._latRange;s=t.U(e[1])*a,n=t.U(e[0])*a,n-s<_&&(h=_/(n-s));}r&&(l=t.aN(t.V(r[0])*a,0,a),c=t.aN(t.V(r[1])*a,0,a),c<l&&(c+=a),c-l<d&&(u=d/(c-l)));const{x:p,y:m}=le(a,e);let f,g;const v=Math.max(u||0,h||0);if(v){const e=new t.P(u?(c+l)/2:p,h?(n+s)/2:m);return o.center=ce(a,e).wrap(),o.zoom+=t.aj(v),o}if(this._helper._latRange){const e=_/2;m-e<s&&(g=s+e),m+e>n&&(g=n-e);}if(r){const e=(l+c)/2;let i=p;this._helper._renderWorldCopies&&(i=t.aN(p,e-a/2,e+a/2));const o=d/2;i-o<l&&(f=l+o),i+o>c&&(f=c-o);}if(void 0!==f||void 0!==g){const e=new t.P(null!=f?f:p,null!=g?g:m);o.center=ce(a,e).wrap();}return o}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}_calculateNearFarZIfNeeded(e,i,o){if(!this._helper.autoCalculateNearFarZ)return;const r=Math.min(this.elevation,this.minElevationForCurrentTile,this.getCameraAltitude()-100),a=e-r*this._helper._pixelPerMeter/Math.cos(i),s=r<0?a:e,n=Math.PI/2+this.pitchInRadians,l=t.ad(this.fov)*(Math.abs(Math.cos(t.ad(this.roll)))*this.height+Math.abs(Math.sin(t.ad(this.roll)))*this.width)/this.height*(.5+o.y/this.height),c=Math.sin(l)*s/Math.sin(t.ag(Math.PI-n-l,.01,Math.PI-.01)),h=he(this),u=Math.atan(h/this._helper.cameraToCenterDistance),d=t.ad(.75),_=u>d?2*u*(.5+o.y/(2*h)):d,p=Math.sin(_)*s/Math.sin(t.ag(Math.PI-n-_,.01,Math.PI-.01)),m=Math.min(c,p);this._helper._farZ=1.01*(Math.cos(Math.PI/2-i)*m+s),this._helper._nearZ=this._helper._height/50;}_calcMatrices(){if(!this._helper._height)return;const e=this.centerOffset,i=le(this.worldSize,this.center),o=i.x,r=i.y;this._helper._pixelPerMeter=t.ai(1,this.center.lat)*this.worldSize;const a=t.ad(Math.min(this.pitch,ne)),s=Math.max(this._helper.cameraToCenterDistance/2,this._helper.cameraToCenterDistance+this._helper._elevation*this._helper._pixelPerMeter/Math.cos(a));let n;this._calculateNearFarZIfNeeded(s,a,e),n=new Float64Array(16),t.b3(n,this.fovInRadians,this._helper._width/this._helper._height,this._helper._nearZ,this._helper._farZ),this._invProjMatrix=new Float64Array(16),t.ap(this._invProjMatrix,n),n[8]=2*-e.x/this._helper._width,n[9]=2*e.y/this._helper._height,this._projectionMatrix=t.b4(n),t.N(n,n,[1,-1,1]),t.M(n,n,[0,0,-this._helper.cameraToCenterDistance]),t.b5(n,n,-this.rollInRadians),t.b6(n,n,this.pitchInRadians),t.b5(n,n,-this.bearingInRadians),t.M(n,n,[-o,-r,0]),this._mercatorMatrix=t.N([],n,[this.worldSize,this.worldSize,this.worldSize]),t.N(n,n,[1,1,this._helper._pixelPerMeter]),this._pixelMatrix=t.O(new Float64Array(16),this.clipSpaceToPixelsMatrix,n),t.M(n,n,[0,0,-this.elevation]),this._viewProjMatrix=n,this._invViewProjMatrix=t.ap([],n);const l=[0,0,-1,1];t.av(l,l,this._invViewProjMatrix),this._cameraPosition=[l[0]/l[3],l[1]/l[3],l[2]/l[3]],this._fogMatrix=new Float64Array(16),t.b3(this._fogMatrix,this.fovInRadians,this.width/this.height,s,this._helper._farZ),this._fogMatrix[8]=2*-e.x/this.width,this._fogMatrix[9]=2*e.y/this.height,t.N(this._fogMatrix,this._fogMatrix,[1,-1,1]),t.M(this._fogMatrix,this._fogMatrix,[0,0,-this.cameraToCenterDistance]),t.b5(this._fogMatrix,this._fogMatrix,-this.rollInRadians),t.b6(this._fogMatrix,this._fogMatrix,this.pitchInRadians),t.b5(this._fogMatrix,this._fogMatrix,-this.bearingInRadians),t.M(this._fogMatrix,this._fogMatrix,[-o,-r,0]),t.N(this._fogMatrix,this._fogMatrix,[1,1,this._helper._pixelPerMeter]),t.M(this._fogMatrix,this._fogMatrix,[0,0,-this.elevation]),this._pixelMatrix3D=t.O(new Float64Array(16),this.clipSpaceToPixelsMatrix,n);const c=this._helper._width%2/2,h=this._helper._height%2/2,u=Math.cos(this.bearingInRadians),d=Math.sin(-this.bearingInRadians),_=o-Math.round(o)+u*c+d*h,p=r-Math.round(r)+u*h+d*c,m=new Float64Array(n);if(t.M(m,m,[_>.5?_-1:_,p>.5?p-1:p,0]),this._alignedProjMatrix=m,n=t.ap(new Float64Array(16),this._pixelMatrix),!n)throw new Error(\"failed to invert matrix\");this._pixelMatrixInverse=n,this._clearMatrixCaches();}_clearMatrixCaches(){this._posMatrixCache.clear(),this._alignedPosMatrixCache.clear(),this._fogMatrixCacheF32.clear();}maxPitchScaleFactor(){if(!this._pixelMatrixInverse)return 1;const e=this.screenPointToMercatorCoordinate(new t.P(0,0)),i=[e.x*this.worldSize,e.y*this.worldSize,0,1];return t.av(i,i,this._pixelMatrix)[3]/this._helper.cameraToCenterDistance}getCameraPoint(){return this._helper.getCameraPoint()}getCameraAltitude(){return this._helper.getCameraAltitude()}getCameraLngLat(){const e=t.ai(1,this.center.lat)*this.worldSize;return de(this.center,this.elevation,this.pitch,this.bearing,this._helper.cameraToCenterDistance/e).toLngLat()}lngLatToCameraDepth(e,i){const o=t.a0.fromLngLat(e),r=[o.x*this.worldSize,o.y*this.worldSize,i,1];return t.av(r,r,this._viewProjMatrix),r[2]/r[3]}getProjectionData(e){const{overscaledTileID:i,aligned:o,applyTerrainMatrix:r}=e,a=this._helper.getMercatorTileCoordinates(i),s=i?this.calculatePosMatrix(i,o,!0):null;let n;return n=i&&i.terrainRttPosMatrix32f&&r?i.terrainRttPosMatrix32f:s||t.b7(),{mainMatrix:n,tileMercatorCoords:a,clippingPlane:[0,0,0,0],projectionTransition:0,fallbackMatrix:n}}isLocationOccluded(e){return !1}getPixelScale(){return 1}getCircleRadiusCorrection(){return 1}getPitchedTextCorrection(e,t,i){return 1}transformLightDirection(e){return t.aS(e)}getRayDirectionFromPixel(e){throw new Error(\"Not implemented.\")}projectTileCoordinates(e,i,o,r){const a=this.calculatePosMatrix(o);let s;r?(s=[e,i,r(e,i),1],t.av(s,s,a)):(s=[e,i,0,1],qe(s,s,a));const n=s[3];return {point:new t.P(s[0]/n,s[1]/n),signedDistanceFromCamera:n,isOccluded:!1}}populateCache(e){for(const t of e)this.calculatePosMatrix(t);}getMatrixForModel(e,i){const o=t.a0.fromLngLat(e,i),r=o.meterInMercatorCoordinateUnits(),a=t.b8();return t.M(a,a,[o.x,o.y,o.z]),t.b5(a,a,Math.PI),t.b6(a,a,Math.PI/2),t.N(a,a,[-r,r,r]),a}getProjectionDataForCustomLayer(e=!0){const i=new t.Z(0,0,0,0,0),o=this.getProjectionData({overscaledTileID:i,applyGlobeMatrix:e}),r=ue(i,this.worldSize);t.O(r,this._viewProjMatrix,r),o.tileMercatorCoords=[0,0,1,1];const a=[t.$,t.$,this.worldSize/this._helper.pixelsPerMeter],s=t.b9();return t.N(s,r,a),o.fallbackMatrix=s,o.mainMatrix=s,o}getFastPathSimpleProjectionMatrix(e){return this.calculatePosMatrix(e)}}function kt(){t.w(\"Map cannot fit within canvas with the given bounds, padding, and/or offset.\");}function Ft(e){if(e.useSlerp)if(e.k<1){const i=t.ba(e.startEulerAngles.roll,e.startEulerAngles.pitch,e.startEulerAngles.bearing),o=t.ba(e.endEulerAngles.roll,e.endEulerAngles.pitch,e.endEulerAngles.bearing),r=new Float64Array(4);t.bb(r,i,o,e.k);const a=t.bc(r);e.tr.setRoll(a.roll),e.tr.setPitch(a.pitch),e.tr.setBearing(a.bearing);}else e.tr.setRoll(e.endEulerAngles.roll),e.tr.setPitch(e.endEulerAngles.pitch),e.tr.setBearing(e.endEulerAngles.bearing);else e.tr.setRoll(t.C.number(e.startEulerAngles.roll,e.endEulerAngles.roll,e.k)),e.tr.setPitch(t.C.number(e.startEulerAngles.pitch,e.endEulerAngles.pitch,e.k)),e.tr.setBearing(t.C.number(e.startEulerAngles.bearing,e.endEulerAngles.bearing,e.k));}function Bt(e,i,o,r,a){const s=a.padding,n=le(a.worldSize,o.getNorthWest()),l=le(a.worldSize,o.getNorthEast()),c=le(a.worldSize,o.getSouthEast()),h=le(a.worldSize,o.getSouthWest()),u=t.ad(-r),d=n.rotate(u),_=l.rotate(u),p=c.rotate(u),m=h.rotate(u),f=new t.P(Math.max(d.x,_.x,m.x,p.x),Math.max(d.y,_.y,m.y,p.y)),g=new t.P(Math.min(d.x,_.x,m.x,p.x),Math.min(d.y,_.y,m.y,p.y)),v=f.sub(g),b=(a.width-(s.left+s.right+i.left+i.right))/v.x,x=(a.height-(s.top+s.bottom+i.top+i.bottom))/v.y;if(x<0||b<0)return void kt();const y=Math.min(t.aj(a.scale*Math.min(b,x)),e.maxZoom),w=t.P.convert(e.offset),T=new t.P((i.left-i.right)/2,(i.top-i.bottom)/2).rotate(t.ad(r)),P=w.add(T).mult(a.scale/t.ae(y));return {center:ce(a.worldSize,n.add(c).div(2).sub(P)),zoom:y,bearing:r}}class Ot{get useGlobeControls(){return !1}handlePanInertia(e,t){return {easingOffset:e,easingCenter:t.center}}handleMapControlsRollPitchBearingZoom(e,t){e.bearingDelta&&t.setBearing(t.bearing+e.bearingDelta),e.pitchDelta&&t.setPitch(t.pitch+e.pitchDelta),e.rollDelta&&t.setRoll(t.roll+e.rollDelta),e.zoomDelta&&t.setZoom(t.zoom+e.zoomDelta);}handleMapControlsPan(e,t,i){e.around.distSqr(t.centerPoint)<.01||t.setLocationAtPoint(i,e.around);}cameraForBoxAndBearing(e,t,i,o,r){return Bt(e,t,i,o,r)}handleJumpToCenterZoom(e,i){e.zoom!==(void 0!==i.zoom?+i.zoom:e.zoom)&&e.setZoom(+i.zoom),void 0!==i.center&&e.setCenter(t.S.convert(i.center));}handleEaseTo(e,i){const o=e.zoom,r=e.padding,a={roll:e.roll,pitch:e.pitch,bearing:e.bearing},s={roll:void 0===i.roll?e.roll:i.roll,pitch:void 0===i.pitch?e.pitch:i.pitch,bearing:void 0===i.bearing?e.bearing:i.bearing},n=void 0!==i.zoom,l=!e.isPaddingEqual(i.padding);let c=!1;const h=n?+i.zoom:e.zoom;let u=e.centerPoint.add(i.offsetAsPoint);const d=e.screenPointToLocation(u),{center:_,zoom:p}=e.getConstrained(t.S.convert(i.center||d),null!=h?h:o);St(e,_);const m=le(e.worldSize,d),f=le(e.worldSize,_).sub(m),g=t.ae(p-o);return c=p!==o,{easeFunc:n=>{if(c&&e.setZoom(t.C.number(o,p,n)),t.bd(a,s)||Ft({startEulerAngles:a,endEulerAngles:s,tr:e,k:n,useSlerp:a.roll!=s.roll}),l&&(e.interpolatePadding(r,i.padding,n),u=e.centerPoint.add(i.offsetAsPoint)),i.around)e.setLocationAtPoint(i.around,i.aroundPoint);else {const i=t.ae(e.zoom-o),r=p>o?Math.min(2,g):Math.max(.5,g),a=Math.pow(r,1-n),s=ce(e.worldSize,m.add(f.mult(n*a)).mult(i));e.setLocationAtPoint(e.renderWorldCopies?s.wrap():s,u);}},isZooming:c,elevationCenter:_}}handleFlyTo(e,i){const o=void 0!==i.zoom,r=e.zoom,a=e.getConstrained(t.S.convert(i.center||i.locationAtOffset),o?+i.zoom:r),s=a.center,n=a.zoom;St(e,s);const l=le(e.worldSize,i.locationAtOffset),c=le(e.worldSize,s).sub(l),h=c.mag(),u=t.ae(n-r);let d;if(void 0!==i.minZoom){const o=Math.min(+i.minZoom,r,n),a=e.getConstrained(s,o).zoom;d=t.ae(a-r);}return {easeFunc:(i,o,a,h)=>{e.setZoom(1===i?n:r+t.aj(o));const u=1===i?s:ce(e.worldSize,l.add(c.mult(a)).mult(o));e.setLocationAtPoint(e.renderWorldCopies?u.wrap():u,h);},scaleOfZoom:u,targetCenter:s,scaleOfMinZoom:d,pixelPathLength:h}}}class jt{constructor(e,t,i){this.blendFunction=e,this.blendColor=t,this.mask=i;}}jt.Replace=[1,0],jt.disabled=new jt(jt.Replace,t.be.transparent,[!1,!1,!1,!1]),jt.unblended=new jt(jt.Replace,t.be.transparent,[!0,!0,!0,!0]),jt.alphaBlended=new jt([1,771],t.be.transparent,[!0,!0,!0,!0]);const Nt=2305;class Zt{constructor(e,t,i){this.enable=e,this.mode=t,this.frontFace=i;}}Zt.disabled=new Zt(!1,1029,Nt),Zt.backCCW=new Zt(!0,1029,Nt),Zt.frontCCW=new Zt(!0,1028,Nt);class Ut{constructor(e,t,i){this.func=e,this.mask=t,this.range=i;}}Ut.ReadOnly=!1,Ut.ReadWrite=!0,Ut.disabled=new Ut(519,Ut.ReadOnly,[0,1]);const Gt=7680;class Vt{constructor(e,t,i,o,r,a){this.test=e,this.ref=t,this.mask=i,this.fail=o,this.depthFail=r,this.pass=a;}}Vt.disabled=new Vt({func:519,mask:0},0,0,Gt,Gt,Gt);const $t=new WeakMap;function qt(e){var t;if($t.has(e))return $t.get(e);{const i=null===(t=e.getParameter(e.VERSION))||void 0===t?void 0:t.startsWith(\"WebGL 2.0\");return $t.set(e,i),i}}class Wt{get awaitingQuery(){return !!this._readbackQueue}constructor(e){this._readbackWaitFrames=4,this._measureWaitFrames=6,this._texWidth=1,this._texHeight=1,this._measuredError=0,this._updateCount=0,this._lastReadbackFrame=-1e3,this._readbackQueue=null,this._cachedRenderContext=e;const i=e.context,o=i.gl;this._texFormat=o.RGBA,this._texType=o.UNSIGNED_BYTE;const r=new t.aK;r.emplaceBack(-1,-1),r.emplaceBack(2,-1),r.emplaceBack(-1,2);const a=new t.aM;a.emplaceBack(0,1,2),this._fullscreenTriangle=new wt(i.createVertexBuffer(r,Tt.members),i.createIndexBuffer(a),t.aL.simpleSegment(0,0,r.length,a.length)),this._resultBuffer=new Uint8Array(4),i.activeTexture.set(o.TEXTURE1);const s=o.createTexture();o.bindTexture(o.TEXTURE_2D,s),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.NEAREST),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.NEAREST),o.texImage2D(o.TEXTURE_2D,0,this._texFormat,this._texWidth,this._texHeight,0,this._texFormat,this._texType,null),this._fbo=i.createFramebuffer(this._texWidth,this._texHeight,!1,!1),this._fbo.colorAttachment.set(s),qt(o)&&(this._pbo=o.createBuffer(),o.bindBuffer(o.PIXEL_PACK_BUFFER,this._pbo),o.bufferData(o.PIXEL_PACK_BUFFER,4,o.STREAM_READ),o.bindBuffer(o.PIXEL_PACK_BUFFER,null));}destroy(){const e=this._cachedRenderContext.context.gl;this._fullscreenTriangle.destroy(),this._fbo.destroy(),e.deleteBuffer(this._pbo),this._fullscreenTriangle=null,this._fbo=null,this._pbo=null,this._resultBuffer=null;}updateErrorLoop(e,t){const i=this._updateCount;return this._readbackQueue?i>=this._readbackQueue.frameNumberIssued+this._readbackWaitFrames&&this._tryReadback():i>=this._lastReadbackFrame+this._measureWaitFrames&&this._renderErrorTexture(e,t),this._updateCount++,this._measuredError}_bindFramebuffer(){const e=this._cachedRenderContext.context,t=e.gl;e.activeTexture.set(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this._fbo.colorAttachment.get()),e.bindFramebuffer.set(this._fbo.framebuffer);}_renderErrorTexture(e,i){const o=this._cachedRenderContext.context,r=o.gl;if(this._bindFramebuffer(),o.viewport.set([0,0,this._texWidth,this._texHeight]),o.clear({color:t.be.transparent}),this._cachedRenderContext.useProgram(\"projectionErrorMeasurement\").draw(o,r.TRIANGLES,Ut.disabled,Vt.disabled,jt.unblended,Zt.disabled,((e,t)=>({u_input:e,u_output_expected:t}))(e,i),null,null,\"$clipping\",this._fullscreenTriangle.vertexBuffer,this._fullscreenTriangle.indexBuffer,this._fullscreenTriangle.segments),this._pbo&&qt(r)){r.bindBuffer(r.PIXEL_PACK_BUFFER,this._pbo),r.readBuffer(r.COLOR_ATTACHMENT0),r.readPixels(0,0,this._texWidth,this._texHeight,this._texFormat,this._texType,0),r.bindBuffer(r.PIXEL_PACK_BUFFER,null);const e=r.fenceSync(r.SYNC_GPU_COMMANDS_COMPLETE,0);r.flush(),this._readbackQueue={frameNumberIssued:this._updateCount,sync:e};}else this._readbackQueue={frameNumberIssued:this._updateCount,sync:null};}_tryReadback(){const e=this._cachedRenderContext.context.gl;if(this._pbo&&this._readbackQueue&&qt(e)){const i=e.clientWaitSync(this._readbackQueue.sync,0,0);if(i===e.WAIT_FAILED)return t.w(\"WebGL2 clientWaitSync failed.\"),this._readbackQueue=null,void(this._lastReadbackFrame=this._updateCount);if(i===e.TIMEOUT_EXPIRED)return;e.bindBuffer(e.PIXEL_PACK_BUFFER,this._pbo),e.getBufferSubData(e.PIXEL_PACK_BUFFER,0,this._resultBuffer,0,4),e.bindBuffer(e.PIXEL_PACK_BUFFER,null);}else this._bindFramebuffer(),e.readPixels(0,0,this._texWidth,this._texHeight,this._texFormat,this._texType,this._resultBuffer);this._readbackQueue=null,this._measuredError=Wt._parseRGBA8float(this._resultBuffer),this._lastReadbackFrame=this._updateCount;}static _parseRGBA8float(e){let t=0;return t+=e[0]/256,t+=e[1]/65536,t+=e[2]/16777216,e[3]<127&&(t=-t),t/128}}const Ht=t.$/128;function Xt(e,i){const o=void 0!==e.granularity?Math.max(e.granularity,1):1,r=o+(e.generateBorders?2:0),a=o+(e.extendToNorthPole||e.generateBorders?1:0)+(e.extendToSouthPole||e.generateBorders?1:0),s=r+1,n=a+1,l=e.generateBorders?-1:0,c=e.generateBorders||e.extendToNorthPole?-1:0,h=o+(e.generateBorders?1:0),u=o+(e.generateBorders||e.extendToSouthPole?1:0),d=s*n,_=r*a*6,p=s*n>65536;if(p&&\"16bit\"===i)throw new Error(\"Granularity is too large and meshes would not fit inside 16 bit vertex indices.\");const m=p||\"32bit\"===i,f=new Int16Array(2*d);let g=0;for(let i=c;i<=u;i++)for(let r=l;r<=h;r++){let a=r/o*t.$;-1===r&&(a=-64),r===o+1&&(a=t.$+Ht);let s=i/o*t.$;-1===i&&(s=e.extendToNorthPole?t.bg:-64),i===o+1&&(s=e.extendToSouthPole?t.bh:t.$+Ht),f[g++]=a,f[g++]=s;}const v=m?new Uint32Array(_):new Uint16Array(_);let b=0;for(let e=0;e<a;e++)for(let t=0;t<r;t++){const i=t+1+e*s,o=t+(e+1)*s,r=t+1+(e+1)*s;v[b++]=t+e*s,v[b++]=o,v[b++]=i,v[b++]=i,v[b++]=o,v[b++]=r;}return {vertices:f.buffer.slice(0),indices:v.buffer.slice(0),uses32bitIndices:m}}const Kt=new t.aJ({fill:new t.bi(128,2),line:new t.bi(512,0),tile:new t.bi(128,32),stencil:new t.bi(128,1),circle:3});class Yt{constructor(){this._tileMeshCache={},this._errorCorrectionUsable=0,this._errorMeasurementLastValue=0,this._errorCorrectionPreviousValue=0,this._errorMeasurementLastChangeTime=-1e3;}get name(){return \"vertical-perspective\"}get transitionState(){return 1}get useSubdivision(){return !0}get shaderVariantName(){return \"globe\"}get shaderDefine(){return \"#define GLOBE\"}get shaderPreludeCode(){return xt.projectionGlobe}get vertexShaderPreludeCode(){return xt.projectionMercator.vertexSource}get subdivisionGranularity(){return Kt}get useGlobeControls(){return !0}get latitudeErrorCorrectionRadians(){return this._errorCorrectionUsable}destroy(){this._errorMeasurement&&this._errorMeasurement.destroy();}updateGPUdependent(e){this._errorMeasurement||(this._errorMeasurement=new Wt(e));const i=t.U(this._errorQueryLatitudeDegrees),o=2*Math.atan(Math.exp(Math.PI-i*Math.PI*2))-.5*Math.PI,r=this._errorMeasurement.updateErrorLoop(i,o),a=s.now();r!==this._errorMeasurementLastValue&&(this._errorCorrectionPreviousValue=this._errorCorrectionUsable,this._errorMeasurementLastValue=r,this._errorMeasurementLastChangeTime=a);const n=Math.min(Math.max((a-this._errorMeasurementLastChangeTime)/1e3/.5,0),1);this._errorCorrectionUsable=t.bj(this._errorCorrectionPreviousValue,-this._errorMeasurementLastValue,t.bk(n));}_getMeshKey(e){return `${e.granularity.toString(36)}_${e.generateBorders?\"b\":\"\"}${e.extendToNorthPole?\"n\":\"\"}${e.extendToSouthPole?\"s\":\"\"}`}getMeshFromTileID(e,t,i,o,r){const a=(\"stencil\"===r?Kt.stencil:Kt.tile).getGranularityForZoomLevel(t.z);return this._getMesh(e,{granularity:a,generateBorders:i,extendToNorthPole:0===t.y&&o,extendToSouthPole:t.y===(1<<t.z)-1&&o})}_getMesh(e,i){const o=this._getMeshKey(i);if(o in this._tileMeshCache)return this._tileMeshCache[o];const r=function(e,i){const o=Xt(i,\"16bit\"),r=t.aK.deserialize({arrayBuffer:o.vertices,length:o.vertices.byteLength/2/2}),a=t.aM.deserialize({arrayBuffer:o.indices,length:o.indices.byteLength/2/3});return new wt(e.createVertexBuffer(r,Tt.members),e.createIndexBuffer(a),t.aL.simpleSegment(0,0,r.length,a.length))}(e,i);return this._tileMeshCache[o]=r,r}recalculate(e){}hasTransition(){const e=s.now();let t=!1;return t=t||(e-this._errorMeasurementLastChangeTime)/1e3<.7,t=t||this._errorMeasurement&&this._errorMeasurement.awaitingQuery,t}setErrorQueryLatitudeDegrees(e){this._errorQueryLatitudeDegrees=e;}}const Qt=new t.r({type:new t.D(t.v.projection.type)});class Jt extends t.E{constructor(e){super(),this._transitionable=new t.t(Qt),this.setProjection(e),this._transitioning=this._transitionable.untransitioned(),this.recalculate(new t.F(0)),this._mercatorProjection=new It,this._verticalPerspectiveProjection=new Yt;}get transitionState(){const e=this.properties.get(\"type\");if(\"string\"==typeof e&&\"mercator\"===e)return 0;if(\"string\"==typeof e&&\"vertical-perspective\"===e)return 1;if(e instanceof t.bl){if(\"vertical-perspective\"===e.from&&\"mercator\"===e.to)return 1-e.transition;if(\"mercator\"===e.from&&\"vertical-perspective\"===e.to)return e.transition}return 1}get useGlobeRendering(){return this.transitionState>0}get latitudeErrorCorrectionRadians(){return this._verticalPerspectiveProjection.latitudeErrorCorrectionRadians}get currentProjection(){return this.useGlobeRendering?this._verticalPerspectiveProjection:this._mercatorProjection}get name(){return \"globe\"}get useSubdivision(){return this.currentProjection.useSubdivision}get shaderVariantName(){return this.currentProjection.shaderVariantName}get shaderDefine(){return this.currentProjection.shaderDefine}get shaderPreludeCode(){return this.currentProjection.shaderPreludeCode}get vertexShaderPreludeCode(){return this.currentProjection.vertexShaderPreludeCode}get subdivisionGranularity(){return this.currentProjection.subdivisionGranularity}get useGlobeControls(){return this.transitionState>0}destroy(){this._mercatorProjection.destroy(),this._verticalPerspectiveProjection.destroy();}updateGPUdependent(e){this._mercatorProjection.updateGPUdependent(e),this._verticalPerspectiveProjection.updateGPUdependent(e);}getMeshFromTileID(e,t,i,o,r){return this.currentProjection.getMeshFromTileID(e,t,i,o,r)}setProjection(e){this._transitionable.setValue(\"type\",(null==e?void 0:e.type)||\"mercator\");}updateTransitions(e){this._transitioning=this._transitionable.transitioned(e,this._transitioning);}hasTransition(){return this._transitioning.hasTransition()||this.currentProjection.hasTransition()}recalculate(e){this.properties=this._transitioning.possiblyEvaluate(e);}setErrorQueryLatitudeDegrees(e){this._verticalPerspectiveProjection.setErrorQueryLatitudeDegrees(e),this._mercatorProjection.setErrorQueryLatitudeDegrees(e);}}function ei(e){const t=oi(e.worldSize,e.center.lat);return 2*Math.PI*t}function ti(e,i,o,r,a){const s=1/(1<<a),n=i/t.$*s+r*s,l=t.bn((e/t.$*s+o*s)*Math.PI*2+Math.PI,2*Math.PI),c=2*Math.atan(Math.exp(Math.PI-n*Math.PI*2))-.5*Math.PI,h=Math.cos(c),u=new Float64Array(3);return u[0]=Math.sin(l)*h,u[1]=Math.sin(c),u[2]=Math.cos(l)*h,u}function ii(e){return function(e,t){const i=Math.cos(t),o=new Float64Array(3);return o[0]=Math.sin(e)*i,o[1]=Math.sin(t),o[2]=Math.cos(e)*i,o}(e.lng*Math.PI/180,e.lat*Math.PI/180)}function oi(e,t){return e/(2*Math.PI)/Math.cos(t*Math.PI/180)}function ri(e){const i=Math.asin(e[1])/Math.PI*180,o=Math.sqrt(e[0]*e[0]+e[2]*e[2]);if(o>1e-6){const r=e[0]/o,a=Math.acos(e[2]/o),s=(r>0?a:-a)/Math.PI*180;return new t.S(t.aN(s,-180,180),i)}return new t.S(0,i)}function ai(e){return Math.cos(e*Math.PI/180)}function si(e,i){const o=ai(e),r=ai(i);return t.aj(r/o)}function ni(e,i){const o=e.rotate(i.bearingInRadians),r=i.zoom+si(i.center.lat,0),a=t.bj(1/ai(i.center.lat),1/ai(Math.min(Math.abs(i.center.lat),60)),t.bm(r,7,3,0,1)),s=360/ei({worldSize:i.worldSize,center:{lat:i.center.lat}});return new t.S(i.center.lng-o.x*s*a,t.ag(i.center.lat+o.y*s,-85.051129,t.ah))}function li(e){const t=.5*e,i=Math.sin(t),o=Math.cos(t);return Math.log(i+o)-Math.log(o-i)}function ci(e,i,o,r){const a=e.lat+o*r;if(Math.abs(o)>1){const s=(Math.sign(e.lat+o)!==Math.sign(e.lat)?-Math.abs(e.lat):Math.abs(e.lat))*Math.PI/180,n=Math.abs(e.lat+o)*Math.PI/180,l=li(s+r*(n-s)),c=li(s),h=li(n);return new t.S(e.lng+i*((l-c)/(h-c)),a)}return new t.S(e.lng+i*r,a)}class hi{constructor(e){this._cachePrevious=new Map,this._cache=new Map,this._hadAnyChanges=!1,this._boundingVolumeFactory=e;}swapBuffers(){if(!this._hadAnyChanges)return;const e=this._cachePrevious;this._cachePrevious=this._cache,this._cache=e,this._cache.clear(),this._hadAnyChanges=!1;}getTileBoundingVolume(e,t,i,o){const r=`${e.z}_${e.x}_${e.y}_${(null==o?void 0:o.terrain)?\"t\":\"\"}`,a=this._cache.get(r);if(a)return a;const s=this._cachePrevious.get(r);if(s)return this._cache.set(r,s),s;const n=this._boundingVolumeFactory(e,t,i,o);return this._cache.set(r,n),this._hadAnyChanges=!0,n}}class ui{constructor(e,t,i,o){this.min=i,this.max=o,this.points=e,this.planes=t;}static fromAabb(e,t){const i=[];for(let o=0;o<8;o++)i.push([1&~o?e[0]:t[0],1==(o>>1&1)?t[1]:e[1],1==(o>>2&1)?t[2]:e[2]]);return new ui(i,[[-1,0,0,t[0]],[1,0,0,-e[0]],[0,-1,0,t[1]],[0,1,0,-e[1]],[0,0,-1,t[2]],[0,0,1,-e[2]]],e,t)}static fromCenterSizeAngles(e,i,o){const r=t.bq([],o[0],o[1],o[2]),a=t.br([],[i[0],0,0],r),s=t.br([],[0,i[1],0],r),n=t.br([],[0,0,i[2]],r),l=[...e],c=[...e];for(let t=0;t<8;t++)for(let i=0;i<3;i++){const o=e[i]+a[i]*(1&~t?-1:1)+s[i]*(1==(t>>1&1)?1:-1)+n[i]*(1==(t>>2&1)?1:-1);l[i]=Math.min(l[i],o),c[i]=Math.max(c[i],o);}const h=[];for(let i=0;i<8;i++){const o=[...e];t.aR(o,o,t.aQ([],a,1&~i?-1:1)),t.aR(o,o,t.aQ([],s,1==(i>>1&1)?1:-1)),t.aR(o,o,t.aQ([],n,1==(i>>2&1)?1:-1)),h.push(o);}return new ui(h,[[...a,-t.aW(a,h[0])],[...s,-t.aW(s,h[0])],[...n,-t.aW(n,h[0])],[-a[0],-a[1],-a[2],-t.aW(a,h[7])],[-s[0],-s[1],-s[2],-t.aW(s,h[7])],[-n[0],-n[1],-n[2],-t.aW(n,h[7])]],l,c)}intersectsFrustum(e){let t=!0;const i=this.points.length,o=this.planes.length,r=e.planes.length,a=e.points.length;for(let o=0;o<r;o++){const r=e.planes[o];let a=0;for(let e=0;e<i;e++){const t=this.points[e];r[0]*t[0]+r[1]*t[1]+r[2]*t[2]+r[3]>=0&&a++;}if(0===a)return 0;a<i&&(t=!1);}if(t)return 2;for(let t=0;t<o;t++){const i=this.planes[t];let o=0;for(let t=0;t<a;t++){const r=e.points[t];i[0]*r[0]+i[1]*r[1]+i[2]*r[2]+i[3]>=0&&o++;}if(0===o)return 0}return 1}intersectsPlane(e){const t=this.points.length;let i=0;for(let o=0;o<t;o++){const t=this.points[o];e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]>=0&&i++;}return i===t?2:0===i?0:1}}function di(e,t,i){const o=e-t;return o<0?-o:Math.max(0,o-i)}function _i(e,t,i,o,r){const a=e-i;let s;return s=a<0?Math.min(-a,1+a-r):a>1?Math.min(Math.max(a-r,0),1-a):0,Math.max(s,di(t,o,r))}class pi{constructor(){this._boundingVolumeCache=new hi(this._computeTileBoundingVolume);}prepareNextFrame(){this._boundingVolumeCache.swapBuffers();}distanceToTile2d(e,t,i,o){const r=1<<i.z,a=1/r,s=i.x/r,n=i.y/r;let l=2;return l=Math.min(l,_i(e,t,s,n,a)),l=Math.min(l,_i(e,t,s+.5,-n-a,a)),l=Math.min(l,_i(e,t,s+.5,2-n-a,a)),l}getWrap(e,t,i){const o=1<<t.z,r=1/o,a=t.x/o,s=di(e.x,a,r),n=di(e.x,a-1,r),l=di(e.x,a+1,r),c=Math.min(s,n,l);return c===l?1:c===n?-1:0}allowVariableZoom(e,t){return ge(e,t)>4}allowWorldCopies(){return !1}getTileBoundingVolume(e,t,i,o){return this._boundingVolumeCache.getTileBoundingVolume(e,t,i,o)}_computeTileBoundingVolume(e,i,o,r){var a,s;let n=o,l=o;if(null==r?void 0:r.terrain){const c=new t.Z(e.z,i,e.z,e.x,e.y),h=r.terrain.getMinMaxElevation(c);n=null!==(a=h.minElevation)&&void 0!==a?a:o,l=null!==(s=h.maxElevation)&&void 0!==s?s:o;}if(n/=t.bt,l/=t.bt,n+=1,l+=1,e.z<=0)return ui.fromAabb([-l,-l,-l],[l,l,l]);if(1===e.z)return ui.fromAabb([0===e.x?-l:0,0===e.y?0:-l,-l],[0===e.x?0:l,0===e.y?l:0,l]);{const i=[ti(0,0,e.x,e.y,e.z),ti(t.$,0,e.x,e.y,e.z),ti(t.$,t.$,e.x,e.y,e.z),ti(0,t.$,e.x,e.y,e.z)],o=[];for(const e of i)o.push(t.aQ([],e,l));if(l!==n)for(const e of i)o.push(t.aQ([],e,n));0===e.y&&o.push([0,1,0]),e.y===(1<<e.z)-1&&o.push([0,-1,0]);const r=[1,1,1],a=[-1,-1,-1];for(const e of o)for(let t=0;t<3;t++)r[t]=Math.min(r[t],e[t]),a[t]=Math.max(a[t],e[t]);const s=ti(t.$/2,t.$/2,e.x,e.y,e.z),c=t.aV([],[0,1,0],s);t.aU(c,c);const h=t.aV([],s,c);t.aU(h,h);const u=t.aV([],i[2],i[1]);t.aU(u,u);const d=t.aV([],i[0],i[3]);t.aU(d,d),o.push(t.aQ([],s,l)),e.y>=(1<<e.z)/2&&o.push(t.aQ([],ti(t.$/2,0,e.x,e.y,e.z),l)),e.y<(1<<e.z)/2&&o.push(t.aQ([],ti(t.$/2,t.$,e.x,e.y,e.z),l));const _=mi(s,o),p=mi(h,o),m=[-s[0],-s[1],-s[2],_.max],f=[s[0],s[1],s[2],-_.min],g=[-h[0],-h[1],-h[2],p.max],v=[h[0],h[1],h[2],-p.min],b=[...u,0],x=[...d,0],y=[];return 0===e.y?y.push(t.bs(x,b,m),t.bs(x,b,f)):y.push(t.bs(g,b,m),t.bs(g,b,f),t.bs(g,x,m),t.bs(g,x,f)),e.y===(1<<e.z)-1?y.push(t.bs(x,b,m),t.bs(x,b,f)):y.push(t.bs(v,b,m),t.bs(v,b,f),t.bs(v,x,m),t.bs(v,x,f)),new ui(y,[m,f,g,v,b,x],r,a)}}}function mi(e,i){let o=1/0,r=-1/0;for(const a of i){const i=t.aW(e,a);o=Math.min(o,i),r=Math.max(r,i);}return {min:o,max:r}}class fi{get pixelsToClipSpaceMatrix(){return this._helper.pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._helper.clipSpaceToPixelsMatrix}get pixelsToGLUnits(){return this._helper.pixelsToGLUnits}get centerOffset(){return this._helper.centerOffset}get size(){return this._helper.size}get rotationMatrix(){return this._helper.rotationMatrix}get centerPoint(){return this._helper.centerPoint}get pixelsPerMeter(){return this._helper.pixelsPerMeter}setMinZoom(e){this._helper.setMinZoom(e);}setMaxZoom(e){this._helper.setMaxZoom(e);}setMinPitch(e){this._helper.setMinPitch(e);}setMaxPitch(e){this._helper.setMaxPitch(e);}setRenderWorldCopies(e){this._helper.setRenderWorldCopies(e);}setBearing(e){this._helper.setBearing(e);}setPitch(e){this._helper.setPitch(e);}setRoll(e){this._helper.setRoll(e);}setFov(e){this._helper.setFov(e);}setZoom(e){this._helper.setZoom(e);}setCenter(e){this._helper.setCenter(e);}setElevation(e){this._helper.setElevation(e);}setMinElevationForCurrentTile(e){this._helper.setMinElevationForCurrentTile(e);}setPadding(e){this._helper.setPadding(e);}interpolatePadding(e,t,i){return this._helper.interpolatePadding(e,t,i)}isPaddingEqual(e){return this._helper.isPaddingEqual(e)}resize(e,t){this._helper.resize(e,t);}getMaxBounds(){return this._helper.getMaxBounds()}setMaxBounds(e){this._helper.setMaxBounds(e);}overrideNearFarZ(e,t){this._helper.overrideNearFarZ(e,t);}clearNearFarZOverride(){this._helper.clearNearFarZOverride();}getCameraQueryGeometry(e){return this._helper.getCameraQueryGeometry(this.getCameraPoint(),e)}get tileSize(){return this._helper.tileSize}get tileZoom(){return this._helper.tileZoom}get scale(){return this._helper.scale}get worldSize(){return this._helper.worldSize}get width(){return this._helper.width}get height(){return this._helper.height}get lngRange(){return this._helper.lngRange}get latRange(){return this._helper.latRange}get minZoom(){return this._helper.minZoom}get maxZoom(){return this._helper.maxZoom}get zoom(){return this._helper.zoom}get center(){return this._helper.center}get minPitch(){return this._helper.minPitch}get maxPitch(){return this._helper.maxPitch}get pitch(){return this._helper.pitch}get pitchInRadians(){return this._helper.pitchInRadians}get roll(){return this._helper.roll}get rollInRadians(){return this._helper.rollInRadians}get bearing(){return this._helper.bearing}get bearingInRadians(){return this._helper.bearingInRadians}get fov(){return this._helper.fov}get fovInRadians(){return this._helper.fovInRadians}get elevation(){return this._helper.elevation}get minElevationForCurrentTile(){return this._helper.minElevationForCurrentTile}get padding(){return this._helper.padding}get unmodified(){return this._helper.unmodified}get renderWorldCopies(){return this._helper.renderWorldCopies}get nearZ(){return this._helper.nearZ}get farZ(){return this._helper.farZ}get autoCalculateNearFarZ(){return this._helper.autoCalculateNearFarZ}setTransitionState(e){}constructor(){this._cachedClippingPlane=t.bu(),this._projectionMatrix=t.b8(),this._globeViewProjMatrix32f=t.b7(),this._globeViewProjMatrixNoCorrection=t.b8(),this._globeViewProjMatrixNoCorrectionInverted=t.b8(),this._globeProjMatrixInverted=t.b8(),this._cameraPosition=t.bo(),this._globeLatitudeErrorCorrectionRadians=0,this._helper=new Rt({calcMatrices:()=>{this._calcMatrices();},getConstrained:(e,t)=>this.getConstrained(e,t)}),this._coveringTilesDetailsProvider=new pi;}clone(){const e=new fi;return e.apply(this),e}apply(e,t){this._globeLatitudeErrorCorrectionRadians=t||0,this._helper.apply(e);}get projectionMatrix(){return this._projectionMatrix}get modelViewProjectionMatrix(){return this._globeViewProjMatrixNoCorrection}get inverseProjectionMatrix(){return this._globeProjMatrixInverted}get cameraPosition(){const e=t.bo();return e[0]=this._cameraPosition[0],e[1]=this._cameraPosition[1],e[2]=this._cameraPosition[2],e}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}getProjectionData(e){const{overscaledTileID:t,applyGlobeMatrix:i}=e,o=this._helper.getMercatorTileCoordinates(t);return {mainMatrix:this._globeViewProjMatrix32f,tileMercatorCoords:o,clippingPlane:this._cachedClippingPlane,projectionTransition:i?1:0,fallbackMatrix:this._globeViewProjMatrix32f}}_computeClippingPlane(e){const i=this.pitchInRadians,o=this.cameraToCenterDistance/e,r=Math.sin(i)*o,a=Math.cos(i)*o+1,s=1/Math.sqrt(r*r+a*a)*1;let n=-r,l=a;const c=Math.sqrt(n*n+l*l);n/=c,l/=c;const h=[0,n,l];t.bv(h,h,[0,0,0],-this.bearingInRadians),t.bw(h,h,[0,0,0],-1*this.center.lat*Math.PI/180),t.bx(h,h,[0,0,0],this.center.lng*Math.PI/180);const u=1/t.aY(h);return t.aQ(h,h,u),[...h,-s*u]}isLocationOccluded(e){return !this.isSurfacePointVisible(ii(e))}transformLightDirection(e){const i=this._helper._center.lng*Math.PI/180,o=this._helper._center.lat*Math.PI/180,r=Math.cos(o),a=[Math.sin(i)*r,Math.sin(o),Math.cos(i)*r],s=[a[2],0,-a[0]],n=[0,0,0];t.aV(n,s,a),t.aU(s,s),t.aU(n,n);const l=[0,0,0];return t.aU(l,[s[0]*e[0]+n[0]*e[1]+a[0]*e[2],s[1]*e[0]+n[1]*e[1]+a[1]*e[2],s[2]*e[0]+n[2]*e[1]+a[2]*e[2]]),l}getPixelScale(){return 1/Math.cos(this._helper._center.lat*Math.PI/180)}getCircleRadiusCorrection(){return Math.cos(this._helper._center.lat*Math.PI/180)}getPitchedTextCorrection(e,i,o){const r=function(e,i,o){const r=1/(1<<o.z);return new t.a0(e/t.$*r+o.x*r,i/t.$*r+o.y*r)}(e,i,o.canonical),a=(s=r.y,[t.bn(r.x*Math.PI*2+Math.PI,2*Math.PI),2*Math.atan(Math.exp(Math.PI-s*Math.PI*2))-.5*Math.PI]);var s;return this.getCircleRadiusCorrection()/Math.cos(a[1])}projectTileCoordinates(e,i,o,r){const a=o.canonical,s=ti(e,i,a.x,a.y,a.z),n=1+(r?r(e,i):0)/t.bt,l=[s[0]*n,s[1]*n,s[2]*n,1];t.av(l,l,this._globeViewProjMatrixNoCorrection);const c=this._cachedClippingPlane,h=c[0]*s[0]+c[1]*s[1]+c[2]*s[2]+c[3]<0;return {point:new t.P(l[0]/l[3],l[1]/l[3]),signedDistanceFromCamera:l[3],isOccluded:h}}_calcMatrices(){if(!this._helper._width||!this._helper._height)return;const e=oi(this.worldSize,this.center.lat),i=t.b9(),o=t.b9();this._helper.autoCalculateNearFarZ&&(this._helper._nearZ=.5,this._helper._farZ=this.cameraToCenterDistance+2*e),t.b3(i,this.fovInRadians,this.width/this.height,this._helper._nearZ,this._helper._farZ);const r=this.centerOffset;i[8]=2*-r.x/this._helper._width,i[9]=2*r.y/this._helper._height,this._projectionMatrix=t.b4(i),this._globeProjMatrixInverted=t.b9(),t.ap(this._globeProjMatrixInverted,i),t.M(i,i,[0,0,-this.cameraToCenterDistance]),t.b5(i,i,this.rollInRadians),t.b6(i,i,-this.pitchInRadians),t.b5(i,i,this.bearingInRadians),t.M(i,i,[0,0,-e]);const a=t.bo();a[0]=e,a[1]=e,a[2]=e,t.b6(o,i,this.center.lat*Math.PI/180),t.by(o,o,-this.center.lng*Math.PI/180),t.N(o,o,a),this._globeViewProjMatrixNoCorrection=o,t.b6(i,i,this.center.lat*Math.PI/180-this._globeLatitudeErrorCorrectionRadians),t.by(i,i,-this.center.lng*Math.PI/180),t.N(i,i,a),this._globeViewProjMatrix32f=new Float32Array(i),this._globeViewProjMatrixNoCorrectionInverted=t.b9(),t.ap(this._globeViewProjMatrixNoCorrectionInverted,o);const s=t.bo();this._cameraPosition=t.bo(),this._cameraPosition[2]=this.cameraToCenterDistance/e,t.bv(this._cameraPosition,this._cameraPosition,s,-this.rollInRadians),t.bw(this._cameraPosition,this._cameraPosition,s,this.pitchInRadians),t.bv(this._cameraPosition,this._cameraPosition,s,-this.bearingInRadians),t.aR(this._cameraPosition,this._cameraPosition,[0,0,1]),t.bw(this._cameraPosition,this._cameraPosition,s,-this.center.lat*Math.PI/180),t.bx(this._cameraPosition,this._cameraPosition,s,this.center.lng*Math.PI/180),this._cachedClippingPlane=this._computeClippingPlane(e);const n=t.b4(this._globeViewProjMatrixNoCorrectionInverted);t.N(n,n,[1,1,-1]),this._cachedFrustum=At.fromInvProjectionMatrix(n,1,0,this._cachedClippingPlane,!0);}calculateFogMatrix(e){t.w(\"calculateFogMatrix is not supported on globe projection.\");const i=t.b9();return t.af(i),i}getVisibleUnwrappedCoordinates(e){return [new t.b1(0,e)]}getCameraFrustum(){return this._cachedFrustum}getClippingPlane(){return this._cachedClippingPlane}getCoveringTilesDetailsProvider(){return this._coveringTilesDetailsProvider}recalculateZoomAndCenter(e){e&&t.w(\"terrain is not fully supported on vertical perspective projection.\"),this._helper.recalculateZoomAndCenter(0);}maxPitchScaleFactor(){return 1}getCameraPoint(){return this._helper.getCameraPoint()}getCameraAltitude(){return this._helper.getCameraAltitude()}getCameraLngLat(){return this._helper.getCameraLngLat()}lngLatToCameraDepth(e,i){if(!this._globeViewProjMatrixNoCorrection)return 1;const o=ii(e);t.aQ(o,o,1+i/t.bt);const r=t.bu();return t.av(r,[o[0],o[1],o[2],1],this._globeViewProjMatrixNoCorrection),r[2]/r[3]}populateCache(e){}getBounds(){const e=.5*this.width,i=.5*this.height,o=[new t.P(0,0),new t.P(e,0),new t.P(this.width,0),new t.P(this.width,i),new t.P(this.width,this.height),new t.P(e,this.height),new t.P(0,this.height),new t.P(0,i)],r=[];for(const e of o)r.push(this.unprojectScreenPoint(e));let a=0,s=0,n=0,l=0;const c=this.center;for(const e of r){const i=t.bz(c.lng,e.lng),o=t.bz(c.lat,e.lat);i<s&&(s=i),i>a&&(a=i),o<l&&(l=o),o>n&&(n=o);}const h=[c.lng+s,c.lat+l,c.lng+a,c.lat+n];return this.isSurfacePointOnScreen([0,1,0])&&(h[3]=90,h[0]=-180,h[2]=180),this.isSurfacePointOnScreen([0,-1,0])&&(h[1]=-90,h[0]=-180,h[2]=180),new G(h)}getConstrained(e,i){const o=t.ag(e.lat,-85.051129,t.ah),r=t.ag(+i,this.minZoom+si(0,o),this.maxZoom);return {center:new t.S(e.lng,o),zoom:r}}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}setLocationAtPoint(e,i){const o=ii(this.unprojectScreenPoint(i)),r=ii(e),a=t.bo();t.bA(a);const s=t.bo();t.bx(s,o,a,-this.center.lng*Math.PI/180),t.bw(s,s,a,this.center.lat*Math.PI/180);const n=r[0]*r[0]+r[2]*r[2],l=s[0]*s[0];if(n<l)return;const c=Math.sqrt(n-l),h=-c,u=t.bB(r[0],r[2],s[0],c),d=t.bB(r[0],r[2],s[0],h),_=t.bo();t.bx(_,r,a,-u);const p=t.bB(_[1],_[2],s[1],s[2]),m=t.bo();t.bx(m,r,a,-d);const f=t.bB(m[1],m[2],s[1],s[2]),g=.5*Math.PI,v=p>=-g&&p<=g,b=f>=-g&&f<=g;let x,y;if(v&&b){const e=this.center.lng*Math.PI/180,i=this.center.lat*Math.PI/180;t.bC(u,e)+t.bC(p,i)<t.bC(d,e)+t.bC(f,i)?(x=u,y=p):(x=d,y=f);}else if(v)x=u,y=p;else {if(!b)return;x=d,y=f;}const w=x/Math.PI*180,T=y/Math.PI*180,P=this.center.lat;this.setCenter(new t.S(w,t.ag(T,-90,90))),this.setZoom(this.zoom+si(P,this.center.lat));}locationToScreenPoint(e,i){const o=ii(e);if(i){const r=i.getElevationForLngLatZoom(e,this._helper._tileZoom);t.aQ(o,o,1+r/t.bt);}return this._projectSurfacePointToScreen(o)}_projectSurfacePointToScreen(e){const i=t.bu();return t.av(i,[...e,1],this._globeViewProjMatrixNoCorrection),i[0]/=i[3],i[1]/=i[3],new t.P((.5*i[0]+.5)*this.width,(.5*-i[1]+.5)*this.height)}screenPointToMercatorCoordinate(e,i){if(i){const t=i.pointCoordinate(e);if(t)return t}return t.a0.fromLngLat(this.unprojectScreenPoint(e))}screenPointToLocation(e,t){var i;return null===(i=this.screenPointToMercatorCoordinate(e,t))||void 0===i?void 0:i.toLngLat()}isPointOnMapSurface(e,t){const i=this._cameraPosition,o=this.getRayDirectionFromPixel(e);return !!this.rayPlanetIntersection(i,o)}getRayDirectionFromPixel(e){const i=t.bu();i[0]=e.x/this.width*2-1,i[1]=-1*(e.y/this.height*2-1),i[2]=1,i[3]=1,t.av(i,i,this._globeViewProjMatrixNoCorrectionInverted),i[0]/=i[3],i[1]/=i[3],i[2]/=i[3];const o=t.bo();o[0]=i[0]-this._cameraPosition[0],o[1]=i[1]-this._cameraPosition[1],o[2]=i[2]-this._cameraPosition[2];const r=t.bo();return t.aU(r,o),r}isSurfacePointVisible(e){const t=this._cachedClippingPlane;return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]>=0}isSurfacePointOnScreen(e){if(!this.isSurfacePointVisible(e))return !1;const i=t.bu();return t.av(i,[...e,1],this._globeViewProjMatrixNoCorrection),i[0]/=i[3],i[1]/=i[3],i[2]/=i[3],i[0]>-1&&i[0]<1&&i[1]>-1&&i[1]<1&&i[2]>-1&&i[2]<1}rayPlanetIntersection(e,i){const o=t.aW(e,i),r=t.bo(),a=t.bo();t.aQ(a,i,o),t.aT(r,e,a);const s=1-t.aW(r,r);if(s<0)return null;const n=t.aW(e,e)-1,l=-o+(o<0?1:-1)*Math.sqrt(s),c=n/l,h=l;return {tMin:Math.min(c,h),tMax:Math.max(c,h)}}unprojectScreenPoint(e){const i=this._cameraPosition,o=this.getRayDirectionFromPixel(e),r=this.rayPlanetIntersection(i,o);if(r){const e=t.bo();t.aR(e,i,[o[0]*r.tMin,o[1]*r.tMin,o[2]*r.tMin]);const a=t.bo();return t.aU(a,e),ri(a)}const a=this._cachedClippingPlane,s=a[0]*o[0]+a[1]*o[1]+a[2]*o[2],n=-t.b0(a,i)/s,l=t.bo();if(n>0)t.aR(l,i,[o[0]*n,o[1]*n,o[2]*n]);else {const e=t.bo();t.aR(e,i,[2*o[0],2*o[1],2*o[2]]);const r=t.b0(this._cachedClippingPlane,e);t.aT(l,e,[this._cachedClippingPlane[0]*r,this._cachedClippingPlane[1]*r,this._cachedClippingPlane[2]*r]);}const c=function(e){const i=t.bo();return i[0]=e[0]*-e[3],i[1]=e[1]*-e[3],i[2]=e[2]*-e[3],{center:i,radius:Math.sqrt(1-e[3]*e[3])}}(a);return ri(function(e,i,o){const r=t.bo();t.aT(r,o,e);const a=t.bo();return t.bp(a,e,r,i/t.a_(r)),a}(c.center,c.radius,l))}getMatrixForModel(e,i){const o=t.S.convert(e),r=1/t.bt,a=t.b8();return t.by(a,a,o.lng/180*Math.PI),t.b6(a,a,-o.lat/180*Math.PI),t.M(a,a,[0,0,1+i/t.bt]),t.b6(a,a,.5*Math.PI),t.N(a,a,[r,r,r]),a}getProjectionDataForCustomLayer(e=!0){const i=this.getProjectionData({overscaledTileID:new t.Z(0,0,0,0,0),applyGlobeMatrix:e});return i.tileMercatorCoords=[0,0,1,1],i}getFastPathSimpleProjectionMatrix(e){}}class gi{get pixelsToClipSpaceMatrix(){return this._helper.pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._helper.clipSpaceToPixelsMatrix}get pixelsToGLUnits(){return this._helper.pixelsToGLUnits}get centerOffset(){return this._helper.centerOffset}get size(){return this._helper.size}get rotationMatrix(){return this._helper.rotationMatrix}get centerPoint(){return this._helper.centerPoint}get pixelsPerMeter(){return this._helper.pixelsPerMeter}setMinZoom(e){this._helper.setMinZoom(e);}setMaxZoom(e){this._helper.setMaxZoom(e);}setMinPitch(e){this._helper.setMinPitch(e);}setMaxPitch(e){this._helper.setMaxPitch(e);}setRenderWorldCopies(e){this._helper.setRenderWorldCopies(e);}setBearing(e){this._helper.setBearing(e);}setPitch(e){this._helper.setPitch(e);}setRoll(e){this._helper.setRoll(e);}setFov(e){this._helper.setFov(e);}setZoom(e){this._helper.setZoom(e);}setCenter(e){this._helper.setCenter(e);}setElevation(e){this._helper.setElevation(e);}setMinElevationForCurrentTile(e){this._helper.setMinElevationForCurrentTile(e);}setPadding(e){this._helper.setPadding(e);}interpolatePadding(e,t,i){return this._helper.interpolatePadding(e,t,i)}isPaddingEqual(e){return this._helper.isPaddingEqual(e)}resize(e,t,i=!0){this._helper.resize(e,t,i);}getMaxBounds(){return this._helper.getMaxBounds()}setMaxBounds(e){this._helper.setMaxBounds(e);}overrideNearFarZ(e,t){this._helper.overrideNearFarZ(e,t);}clearNearFarZOverride(){this._helper.clearNearFarZOverride();}getCameraQueryGeometry(e){return this._helper.getCameraQueryGeometry(this.getCameraPoint(),e)}get tileSize(){return this._helper.tileSize}get tileZoom(){return this._helper.tileZoom}get scale(){return this._helper.scale}get worldSize(){return this._helper.worldSize}get width(){return this._helper.width}get height(){return this._helper.height}get lngRange(){return this._helper.lngRange}get latRange(){return this._helper.latRange}get minZoom(){return this._helper.minZoom}get maxZoom(){return this._helper.maxZoom}get zoom(){return this._helper.zoom}get center(){return this._helper.center}get minPitch(){return this._helper.minPitch}get maxPitch(){return this._helper.maxPitch}get pitch(){return this._helper.pitch}get pitchInRadians(){return this._helper.pitchInRadians}get roll(){return this._helper.roll}get rollInRadians(){return this._helper.rollInRadians}get bearing(){return this._helper.bearing}get bearingInRadians(){return this._helper.bearingInRadians}get fov(){return this._helper.fov}get fovInRadians(){return this._helper.fovInRadians}get elevation(){return this._helper.elevation}get minElevationForCurrentTile(){return this._helper.minElevationForCurrentTile}get padding(){return this._helper.padding}get unmodified(){return this._helper.unmodified}get renderWorldCopies(){return this._helper.renderWorldCopies}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}get nearZ(){return this._helper.nearZ}get farZ(){return this._helper.farZ}get autoCalculateNearFarZ(){return this._helper.autoCalculateNearFarZ}get isGlobeRendering(){return this._globeness>0}setTransitionState(e,t){this._globeness=e,this._globeLatitudeErrorCorrectionRadians=t,this._calcMatrices(),this._verticalPerspectiveTransform.getCoveringTilesDetailsProvider().prepareNextFrame(),this._mercatorTransform.getCoveringTilesDetailsProvider().prepareNextFrame();}get currentTransform(){return this.isGlobeRendering?this._verticalPerspectiveTransform:this._mercatorTransform}constructor(){this._globeLatitudeErrorCorrectionRadians=0,this._globeness=1,this._helper=new Rt({calcMatrices:()=>{this._calcMatrices();},getConstrained:(e,t)=>this.getConstrained(e,t)}),this._globeness=1,this._mercatorTransform=new Lt,this._verticalPerspectiveTransform=new fi;}clone(){const e=new gi;return e._globeness=this._globeness,e._globeLatitudeErrorCorrectionRadians=this._globeLatitudeErrorCorrectionRadians,e.apply(this),e}apply(e){this._helper.apply(e),this._mercatorTransform.apply(this),this._verticalPerspectiveTransform.apply(this,this._globeLatitudeErrorCorrectionRadians);}get projectionMatrix(){return this.currentTransform.projectionMatrix}get modelViewProjectionMatrix(){return this.currentTransform.modelViewProjectionMatrix}get inverseProjectionMatrix(){return this.currentTransform.inverseProjectionMatrix}get cameraPosition(){return this.currentTransform.cameraPosition}getProjectionData(e){const t=this._mercatorTransform.getProjectionData(e),i=this._verticalPerspectiveTransform.getProjectionData(e);return {mainMatrix:this.isGlobeRendering?i.mainMatrix:t.mainMatrix,clippingPlane:i.clippingPlane,tileMercatorCoords:i.tileMercatorCoords,projectionTransition:e.applyGlobeMatrix?this._globeness:0,fallbackMatrix:t.fallbackMatrix}}isLocationOccluded(e){return this.currentTransform.isLocationOccluded(e)}transformLightDirection(e){return this.currentTransform.transformLightDirection(e)}getPixelScale(){return t.bj(this._mercatorTransform.getPixelScale(),this._verticalPerspectiveTransform.getPixelScale(),this._globeness)}getCircleRadiusCorrection(){return t.bj(this._mercatorTransform.getCircleRadiusCorrection(),this._verticalPerspectiveTransform.getCircleRadiusCorrection(),this._globeness)}getPitchedTextCorrection(e,i,o){const r=this._mercatorTransform.getPitchedTextCorrection(e,i,o),a=this._verticalPerspectiveTransform.getPitchedTextCorrection(e,i,o);return t.bj(r,a,this._globeness)}projectTileCoordinates(e,t,i,o){return this.currentTransform.projectTileCoordinates(e,t,i,o)}_calcMatrices(){this._helper._width&&this._helper._height&&(this._verticalPerspectiveTransform.apply(this,this._globeLatitudeErrorCorrectionRadians),this._helper._nearZ=this._verticalPerspectiveTransform.nearZ,this._helper._farZ=this._verticalPerspectiveTransform.farZ,this._mercatorTransform.apply(this,!0,this.isGlobeRendering),this._helper._nearZ=this._mercatorTransform.nearZ,this._helper._farZ=this._mercatorTransform.farZ);}calculateFogMatrix(e){return this.currentTransform.calculateFogMatrix(e)}getVisibleUnwrappedCoordinates(e){return this.currentTransform.getVisibleUnwrappedCoordinates(e)}getCameraFrustum(){return this.currentTransform.getCameraFrustum()}getClippingPlane(){return this.currentTransform.getClippingPlane()}getCoveringTilesDetailsProvider(){return this.currentTransform.getCoveringTilesDetailsProvider()}recalculateZoomAndCenter(e){this._mercatorTransform.recalculateZoomAndCenter(e),this._verticalPerspectiveTransform.recalculateZoomAndCenter(e);}maxPitchScaleFactor(){return this._mercatorTransform.maxPitchScaleFactor()}getCameraPoint(){return this._helper.getCameraPoint()}getCameraAltitude(){return this._helper.getCameraAltitude()}getCameraLngLat(){return this._helper.getCameraLngLat()}lngLatToCameraDepth(e,t){return this.currentTransform.lngLatToCameraDepth(e,t)}populateCache(e){this._mercatorTransform.populateCache(e),this._verticalPerspectiveTransform.populateCache(e);}getBounds(){return this.currentTransform.getBounds()}getConstrained(e,t){return this.currentTransform.getConstrained(e,t)}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}setLocationAtPoint(e,t){if(!this.isGlobeRendering)return this._mercatorTransform.setLocationAtPoint(e,t),void this.apply(this._mercatorTransform);this._verticalPerspectiveTransform.setLocationAtPoint(e,t),this.apply(this._verticalPerspectiveTransform);}locationToScreenPoint(e,t){return this.currentTransform.locationToScreenPoint(e,t)}screenPointToMercatorCoordinate(e,t){return this.currentTransform.screenPointToMercatorCoordinate(e,t)}screenPointToLocation(e,t){return this.currentTransform.screenPointToLocation(e,t)}isPointOnMapSurface(e,t){return this.currentTransform.isPointOnMapSurface(e,t)}getRayDirectionFromPixel(e){return this._verticalPerspectiveTransform.getRayDirectionFromPixel(e)}getMatrixForModel(e,t){return this.currentTransform.getMatrixForModel(e,t)}getProjectionDataForCustomLayer(e=!0){const t=this._mercatorTransform.getProjectionDataForCustomLayer(e);if(!this.isGlobeRendering)return t;const i=this._verticalPerspectiveTransform.getProjectionDataForCustomLayer(e);return i.fallbackMatrix=t.mainMatrix,i}getFastPathSimpleProjectionMatrix(e){return this.currentTransform.getFastPathSimpleProjectionMatrix(e)}}class vi{get useGlobeControls(){return !0}handlePanInertia(e,i){const o=ni(e,i);return Math.abs(o.lng-i.center.lng)>180&&(o.lng=i.center.lng+179.5*Math.sign(o.lng-i.center.lng)),{easingCenter:o,easingOffset:new t.P(0,0)}}handleMapControlsRollPitchBearingZoom(e,i){const o=e.around,r=i.screenPointToLocation(o);e.bearingDelta&&i.setBearing(i.bearing+e.bearingDelta),e.pitchDelta&&i.setPitch(i.pitch+e.pitchDelta),e.rollDelta&&i.setRoll(i.roll+e.rollDelta);const a=i.zoom;e.zoomDelta&&i.setZoom(i.zoom+e.zoomDelta);const s=i.zoom-a;if(0===s)return;const n=t.bz(i.center.lng,r.lng),l=n/(Math.abs(n/180)+1),c=t.bz(i.center.lat,r.lat),h=i.getRayDirectionFromPixel(o),u=i.cameraPosition,d=-1*t.aW(u,h),_=t.bo();t.aR(_,u,[h[0]*d,h[1]*d,h[2]*d]);const p=t.aY(_)-1,m=Math.exp(.5*-Math.max(p-.3,0)),f=oi(i.worldSize,i.center.lat)/Math.min(i.width,i.height),g=t.bm(f,.9,.5,1,.25),v=(1-t.ae(-s))*Math.min(m,g),b=i.center.lat,x=i.zoom,y=new t.S(i.center.lng+l*v,t.ag(i.center.lat+c*v,-85.051129,t.ah));i.setLocationAtPoint(r,o);const w=i.center,T=t.bm(Math.abs(n),45,85,0,1),P=t.bm(f,.75,.35,0,1),C=Math.pow(Math.max(T,P),.25),I=t.bz(w.lng,y.lng),M=t.bz(w.lat,y.lat);i.setCenter(new t.S(w.lng+I*C,w.lat+M*C).wrap()),i.setZoom(x+si(b,i.center.lat));}handleMapControlsPan(e,t,i){if(!e.panDelta)return;const o=t.center.lat,r=t.zoom;t.setCenter(ni(e.panDelta,t).wrap()),t.setZoom(r+si(o,t.center.lat));}cameraForBoxAndBearing(e,i,o,r,a){const s=Bt(e,i,o,r,a),n=i.left/a.width*2-1,l=(a.width-i.right)/a.width*2-1,c=i.top/a.height*-2+1,h=(a.height-i.bottom)/a.height*-2+1,u=t.bz(o.getWest(),o.getEast())<0,d=u?o.getEast():o.getWest(),_=u?o.getWest():o.getEast(),p=Math.max(o.getNorth(),o.getSouth()),m=Math.min(o.getNorth(),o.getSouth()),f=d+.5*t.bz(d,_),g=p+.5*t.bz(p,m),v=a.clone();v.setCenter(s.center),v.setBearing(s.bearing),v.setPitch(0),v.setRoll(0),v.setZoom(s.zoom);const b=v.modelViewProjectionMatrix,x=[ii(o.getNorthWest()),ii(o.getNorthEast()),ii(o.getSouthWest()),ii(o.getSouthEast()),ii(new t.S(_,g)),ii(new t.S(d,g)),ii(new t.S(f,p)),ii(new t.S(f,m))],y=ii(s.center);let w=Number.POSITIVE_INFINITY;for(const e of x)n<0&&(w=vi.getLesserNonNegativeNonNull(w,vi.solveVectorScale(e,y,b,\"x\",n))),l>0&&(w=vi.getLesserNonNegativeNonNull(w,vi.solveVectorScale(e,y,b,\"x\",l))),c>0&&(w=vi.getLesserNonNegativeNonNull(w,vi.solveVectorScale(e,y,b,\"y\",c))),h<0&&(w=vi.getLesserNonNegativeNonNull(w,vi.solveVectorScale(e,y,b,\"y\",h)));if(Number.isFinite(w)&&0!==w)return s.zoom=v.zoom+t.aj(w),s;kt();}handleJumpToCenterZoom(e,i){const o=e.center.lat,r=e.getConstrained(i.center?t.S.convert(i.center):e.center,e.zoom).center;e.setCenter(r.wrap());const a=void 0!==i.zoom?+i.zoom:e.zoom+si(o,r.lat);e.zoom!==a&&e.setZoom(a);}handleEaseTo(e,i){const o=e.zoom,r=e.center,a=e.padding,s={roll:e.roll,pitch:e.pitch,bearing:e.bearing},n={roll:void 0===i.roll?e.roll:i.roll,pitch:void 0===i.pitch?e.pitch:i.pitch,bearing:void 0===i.bearing?e.bearing:i.bearing},l=void 0!==i.zoom,c=!e.isPaddingEqual(i.padding);let h=!1;const u=i.center?t.S.convert(i.center):r,d=e.getConstrained(u,o).center;St(e,d);const _=e.clone();_.setCenter(d),_.setZoom(l?+i.zoom:o+si(r.lat,u.lat)),_.setBearing(i.bearing);const p=new t.P(t.ag(e.centerPoint.x+i.offsetAsPoint.x,0,e.width),t.ag(e.centerPoint.y+i.offsetAsPoint.y,0,e.height));_.setLocationAtPoint(d,p);const m=(i.offset&&i.offsetAsPoint.mag())>0?_.center:d,f=l?+i.zoom:o+si(r.lat,m.lat),g=o+si(r.lat,0),v=f+si(m.lat,0),b=t.bz(r.lng,m.lng),x=t.bz(r.lat,m.lat),y=t.ae(v-g);return h=f!==o,{easeFunc:o=>{if(t.bd(s,n)||Ft({startEulerAngles:s,endEulerAngles:n,tr:e,k:o,useSlerp:s.roll!=n.roll}),c&&e.interpolatePadding(a,i.padding,o),i.around)t.w(\"Easing around a point is not supported under globe projection.\"),e.setLocationAtPoint(i.around,i.aroundPoint);else {const t=v>g?Math.min(2,y):Math.max(.5,y),i=Math.pow(t,1-o),a=ci(r,b,x,o*i);e.setCenter(a.wrap());}if(h){const i=t.C.number(g,v,o)+si(0,e.center.lat);e.setZoom(i);}},isZooming:h,elevationCenter:m}}handleFlyTo(e,i){const o=void 0!==i.zoom,r=e.center,a=e.zoom,s=e.padding,n=!e.isPaddingEqual(i.padding),l=e.getConstrained(t.S.convert(i.center||i.locationAtOffset),a).center,c=o?+i.zoom:e.zoom+si(e.center.lat,l.lat),h=e.clone();h.setCenter(l),h.setZoom(c),h.setBearing(i.bearing);const u=new t.P(t.ag(e.centerPoint.x+i.offsetAsPoint.x,0,e.width),t.ag(e.centerPoint.y+i.offsetAsPoint.y,0,e.height));h.setLocationAtPoint(l,u);const d=h.center;St(e,d);const _=function(e,i,o){const r=ii(i),a=ii(o),s=t.aW(r,a),n=Math.acos(s),l=ei(e);return n/(2*Math.PI)*l}(e,r,d),p=a+si(r.lat,0),m=c+si(d.lat,0),f=t.ae(m-p);let g;if(\"number\"==typeof i.minZoom){const o=+i.minZoom+si(d.lat,0),r=Math.min(o,p,m)+si(0,d.lat),a=e.getConstrained(d,r).zoom+si(d.lat,0);g=t.ae(a-p);}const v=t.bz(r.lng,d.lng),b=t.bz(r.lat,d.lat);return {easeFunc:(o,a,l,h)=>{const u=ci(r,v,b,l);n&&e.interpolatePadding(s,i.padding,o);const _=1===o?d:u;e.setCenter(_.wrap());const m=p+t.aj(a);e.setZoom(1===o?c:m+si(0,_.lat));},scaleOfZoom:f,targetCenter:d,scaleOfMinZoom:g,pixelPathLength:_}}static solveVectorScale(e,t,i,o,r){const a=\"x\"===o?[i[0],i[4],i[8],i[12]]:[i[1],i[5],i[9],i[13]],s=[i[3],i[7],i[11],i[15]],n=e[0]*a[0]+e[1]*a[1]+e[2]*a[2],l=e[0]*s[0]+e[1]*s[1]+e[2]*s[2],c=t[0]*a[0]+t[1]*a[1]+t[2]*a[2],h=t[0]*s[0]+t[1]*s[1]+t[2]*s[2];return c+r*l===n+r*h||s[3]*(n-c)+a[3]*(h-l)+n*h==c*l?null:(c+a[3]-r*h-r*s[3])/(c-n-r*h+r*l)}static getLesserNonNegativeNonNull(e,t){return null!==t&&t>=0&&t<e?t:e}}class bi{constructor(e){this._globe=e,this._mercatorCameraHelper=new Ot,this._verticalPerspectiveCameraHelper=new vi;}get useGlobeControls(){return this._globe.useGlobeRendering}get currentHelper(){return this.useGlobeControls?this._verticalPerspectiveCameraHelper:this._mercatorCameraHelper}handlePanInertia(e,t){return this.currentHelper.handlePanInertia(e,t)}handleMapControlsRollPitchBearingZoom(e,t){return this.currentHelper.handleMapControlsRollPitchBearingZoom(e,t)}handleMapControlsPan(e,t,i){this.currentHelper.handleMapControlsPan(e,t,i);}cameraForBoxAndBearing(e,t,i,o,r){return this.currentHelper.cameraForBoxAndBearing(e,t,i,o,r)}handleJumpToCenterZoom(e,t){this.currentHelper.handleJumpToCenterZoom(e,t);}handleEaseTo(e,t){return this.currentHelper.handleEaseTo(e,t)}handleFlyTo(e,t){return this.currentHelper.handleFlyTo(e,t)}}const xi=(e,i)=>t.y(e,i&&i.filter((e=>\"source.canvas\"!==e.identifier))),yi=t.bD();class wi extends t.E{constructor(e,i={}){super(),this._rtlPluginLoaded=()=>{for(const e in this.sourceCaches){const t=this.sourceCaches[e].getSource().type;\"vector\"!==t&&\"geojson\"!==t||this.sourceCaches[e].reload();}},this.map=e,this.dispatcher=new F(k(),e._getMapId()),this.dispatcher.registerMessageHandler(\"GG\",((e,t)=>this.getGlyphs(e,t))),this.dispatcher.registerMessageHandler(\"GI\",((e,t)=>this.getImages(e,t))),this.imageManager=new b,this.imageManager.setEventedParent(this),this.glyphManager=new T(e._requestManager,i.localIdeographFontFamily),this.lineAtlas=new E(256,512),this.crossTileSymbolIndex=new vt,this._spritesImagesIds={},this._layers={},this._order=[],this.sourceCaches={},this.zoomHistory=new t.bE,this._loaded=!1,this._availableImages=[],this._globalState={},this._resetUpdates(),this.dispatcher.broadcast(\"SR\",t.bF()),oe().on(ee,this._rtlPluginLoaded),this.on(\"data\",(e=>{if(\"source\"!==e.dataType||\"metadata\"!==e.sourceDataType)return;const t=this.sourceCaches[e.sourceId];if(!t)return;const i=t.getSource();if(i&&i.vectorLayerIds)for(const e in this._layers){const t=this._layers[e];t.source===i.id&&this._validateLayer(t);}}));}setGlobalStateProperty(e,i){var o,r,a;this._checkLoaded();const s=null===i?null!==(a=null===(r=null===(o=this.stylesheet.state)||void 0===o?void 0:o[e])||void 0===r?void 0:r.default)&&void 0!==a?a:null:i;if(t.bG(s,this._globalState[e]))return this;this._globalState[e]=s;const n=this._findGlobalStateAffectedSources([e]);for(const e in this.sourceCaches)n.has(e)&&(this._reloadSource(e),this._changed=!0);}getGlobalState(){return this._globalState}setGlobalState(e){this._checkLoaded();const i=[];for(const o in e)!t.bG(this._globalState[o],e[o].default)&&(i.push(o),this._globalState[o]=e[o].default);const o=this._findGlobalStateAffectedSources(i);for(const e in this.sourceCaches)o.has(e)&&(this._reloadSource(e),this._changed=!0);}_findGlobalStateAffectedSources(e){if(0===e.length)return new Set;const t=new Set;for(const i in this._layers){const o=this._layers[i],r=o.getLayoutAffectingGlobalStateRefs();for(const i of e)r.has(i)&&t.add(o.source);}return t}loadURL(e,i={},o){this.fire(new t.l(\"dataloading\",{dataType:\"style\"})),i.validate=\"boolean\"!=typeof i.validate||i.validate;const r=this.map._requestManager.transformRequest(e,\"Style\");this._loadStyleRequest=new AbortController;const a=this._loadStyleRequest;t.j(r,this._loadStyleRequest).then((e=>{this._loadStyleRequest=null,this._load(e.data,i,o);})).catch((e=>{this._loadStyleRequest=null,e&&!a.signal.aborted&&this.fire(new t.k(e));}));}loadJSON(e,i={},o){this.fire(new t.l(\"dataloading\",{dataType:\"style\"})),this._frameRequest=new AbortController,s.frameAsync(this._frameRequest).then((()=>{this._frameRequest=null,i.validate=!1!==i.validate,this._load(e,i,o);})).catch((()=>{}));}loadEmpty(){this.fire(new t.l(\"dataloading\",{dataType:\"style\"})),this._load(yi,{validate:!1});}_load(e,i,o){var r,a,s;const n=i.transformStyle?i.transformStyle(o,e):e;if(!i.validate||!xi(this,t.z(n))){this._loaded=!0,this.stylesheet=n;for(const e in n.sources)this.addSource(e,n.sources[e],{validate:!1});n.sprite?this._loadSprite(n.sprite):this.imageManager.setLoaded(!0),this.glyphManager.setURL(n.glyphs),this._createLayers(),this.light=new I(this.stylesheet.light),this._setProjectionInternal((null===(r=this.stylesheet.projection)||void 0===r?void 0:r.type)||\"mercator\"),this.sky=new S(this.stylesheet.sky),this.map.setTerrain(null!==(a=this.stylesheet.terrain)&&void 0!==a?a:null),this.setGlobalState(null!==(s=this.stylesheet.state)&&void 0!==s?s:null),this.fire(new t.l(\"data\",{dataType:\"style\"})),this.fire(new t.l(\"style.load\"));}}_createLayers(){const e=t.bH(this.stylesheet.layers);this.dispatcher.broadcast(\"SL\",e),this._order=e.map((e=>e.id)),this._layers={},this._serializedLayers=null;for(const i of e){const e=t.bI(i);e.setEventedParent(this,{layer:{id:i.id}}),this._layers[i.id]=e;}}_loadSprite(e,i=!1,o=void 0){let r;this.imageManager.setLoaded(!1),this._spriteRequest=new AbortController,function(e,i,o,r){return t._(this,void 0,void 0,(function*(){const a=f(e),n=o>1?\"@2x\":\"\",l={},c={};for(const{id:e,url:o}of a){const a=i.transformRequest(g(o,n,\".json\"),\"SpriteJSON\");l[e]=t.j(a,r);const s=i.transformRequest(g(o,n,\".png\"),\"SpriteImage\");c[e]=p.getImage(s,r);}return yield Promise.all([...Object.values(l),...Object.values(c)]),function(e,i){return t._(this,void 0,void 0,(function*(){const t={};for(const o in e){t[o]={};const r=s.getImageCanvasContext((yield i[o]).data),a=(yield e[o]).data;for(const e in a){const{width:i,height:s,x:n,y:l,sdf:c,pixelRatio:h,stretchX:u,stretchY:d,content:_,textFitWidth:p,textFitHeight:m}=a[e];t[o][e]={data:null,pixelRatio:h,sdf:c,stretchX:u,stretchY:d,content:_,textFitWidth:p,textFitHeight:m,spriteData:{width:i,height:s,x:n,y:l,context:r}};}}return t}))}(l,c)}))}(e,this.map._requestManager,this.map.getPixelRatio(),this._spriteRequest).then((e=>{if(this._spriteRequest=null,e)for(const t in e){this._spritesImagesIds[t]=[];const o=this._spritesImagesIds[t]?this._spritesImagesIds[t].filter((t=>!(t in e))):[];for(const e of o)this.imageManager.removeImage(e),this._changedImages[e]=!0;for(const o in e[t]){const r=\"default\"===t?o:`${t}:${o}`;this._spritesImagesIds[t].push(r),r in this.imageManager.images?this.imageManager.updateImage(r,e[t][o],!1):this.imageManager.addImage(r,e[t][o]),i&&(this._changedImages[r]=!0);}}})).catch((e=>{this._spriteRequest=null,r=e,this.fire(new t.k(r));})).finally((()=>{this.imageManager.setLoaded(!0),this._availableImages=this.imageManager.listImages(),i&&(this._changed=!0),this.dispatcher.broadcast(\"SI\",this._availableImages),this.fire(new t.l(\"data\",{dataType:\"style\"})),o&&o(r);}));}_unloadSprite(){for(const e of Object.values(this._spritesImagesIds).flat())this.imageManager.removeImage(e),this._changedImages[e]=!0;this._spritesImagesIds={},this._availableImages=this.imageManager.listImages(),this._changed=!0,this.dispatcher.broadcast(\"SI\",this._availableImages),this.fire(new t.l(\"data\",{dataType:\"style\"}));}_validateLayer(e){const i=this.sourceCaches[e.source];if(!i)return;const o=e.sourceLayer;if(!o)return;const r=i.getSource();(\"geojson\"===r.type||r.vectorLayerIds&&-1===r.vectorLayerIds.indexOf(o))&&this.fire(new t.k(new Error(`Source layer \"${o}\" does not exist on source \"${r.id}\" as specified by style layer \"${e.id}\".`)));}loaded(){if(!this._loaded)return !1;if(Object.keys(this._updatedSources).length)return !1;for(const e in this.sourceCaches)if(!this.sourceCaches[e].loaded())return !1;return !!this.imageManager.isLoaded()}_serializeByIds(e,i=!1){const o=this._serializedAllLayers();if(!e||0===e.length)return Object.values(i?t.bJ(o):o);const r=[];for(const a of e)if(o[a]){const e=i?t.bJ(o[a]):o[a];r.push(e);}return r}_serializedAllLayers(){let e=this._serializedLayers;if(e)return e;e=this._serializedLayers={};const t=Object.keys(this._layers);for(const i of t){const t=this._layers[i];\"custom\"!==t.type&&(e[i]=t.serialize());}return e}hasTransitions(){var e,t,i;if(null===(e=this.light)||void 0===e?void 0:e.hasTransition())return !0;if(null===(t=this.sky)||void 0===t?void 0:t.hasTransition())return !0;if(null===(i=this.projection)||void 0===i?void 0:i.hasTransition())return !0;for(const e in this.sourceCaches)if(this.sourceCaches[e].hasTransition())return !0;for(const e in this._layers)if(this._layers[e].hasTransition())return !0;return !1}_checkLoaded(){if(!this._loaded)throw new Error(\"Style is not done loading.\")}update(e){if(!this._loaded)return;const i=this._changed;if(i){const t=Object.keys(this._updatedLayers),i=Object.keys(this._removedLayers);(t.length||i.length)&&this._updateWorkerLayers(t,i);for(const e in this._updatedSources){const t=this._updatedSources[e];if(\"reload\"===t)this._reloadSource(e);else {if(\"clear\"!==t)throw new Error(`Invalid action ${t}`);this._clearSource(e);}}this._updateTilesForChangedImages(),this._updateTilesForChangedGlyphs();for(const t in this._updatedPaintProps)this._layers[t].updateTransitions(e);this.light.updateTransitions(e),this.sky.updateTransitions(e),this._resetUpdates();}const o={};for(const e in this.sourceCaches){const t=this.sourceCaches[e];o[e]=t.used,t.used=!1;}for(const t of this._order){const i=this._layers[t];i.recalculate(e,this._availableImages),!i.isHidden(e.zoom)&&i.source&&(this.sourceCaches[i.source].used=!0);}for(const e in o){const i=this.sourceCaches[e];!!o[e]!=!!i.used&&i.fire(new t.l(\"data\",{sourceDataType:\"visibility\",dataType:\"source\",sourceId:e}));}this.light.recalculate(e),this.sky.recalculate(e),this.projection.recalculate(e),this.z=e.zoom,i&&this.fire(new t.l(\"data\",{dataType:\"style\"}));}_updateTilesForChangedImages(){const e=Object.keys(this._changedImages);if(e.length){for(const t in this.sourceCaches)this.sourceCaches[t].reloadTilesForDependencies([\"icons\",\"patterns\"],e);this._changedImages={};}}_updateTilesForChangedGlyphs(){if(this._glyphsDidChange){for(const e in this.sourceCaches)this.sourceCaches[e].reloadTilesForDependencies([\"glyphs\"],[\"\"]);this._glyphsDidChange=!1;}}_updateWorkerLayers(e,t){this.dispatcher.broadcast(\"UL\",{layers:this._serializeByIds(e,!1),removedIds:t});}_resetUpdates(){this._changed=!1,this._updatedLayers={},this._removedLayers={},this._updatedSources={},this._updatedPaintProps={},this._changedImages={},this._glyphsDidChange=!1;}setState(e,i={}){var o;this._checkLoaded();const r=this.serialize();if(e=i.transformStyle?i.transformStyle(r,e):e,(null===(o=i.validate)||void 0===o||o)&&xi(this,t.z(e)))return !1;(e=t.bJ(e)).layers=t.bH(e.layers);const a=t.bK(r,e),s=this._getOperationsToPerform(a);if(s.unimplemented.length>0)throw new Error(`Unimplemented: ${s.unimplemented.join(\", \")}.`);if(0===s.operations.length)return !1;for(const e of s.operations)e();return this.stylesheet=e,this._serializedLayers=null,!0}_getOperationsToPerform(e){const t=[],i=[];for(const o of e)switch(o.command){case \"setCenter\":case \"setZoom\":case \"setBearing\":case \"setPitch\":case \"setRoll\":continue;case \"addLayer\":t.push((()=>this.addLayer.apply(this,o.args)));break;case \"removeLayer\":t.push((()=>this.removeLayer.apply(this,o.args)));break;case \"setPaintProperty\":t.push((()=>this.setPaintProperty.apply(this,o.args)));break;case \"setLayoutProperty\":t.push((()=>this.setLayoutProperty.apply(this,o.args)));break;case \"setFilter\":t.push((()=>this.setFilter.apply(this,o.args)));break;case \"addSource\":t.push((()=>this.addSource.apply(this,o.args)));break;case \"removeSource\":t.push((()=>this.removeSource.apply(this,o.args)));break;case \"setLayerZoomRange\":t.push((()=>this.setLayerZoomRange.apply(this,o.args)));break;case \"setLight\":t.push((()=>this.setLight.apply(this,o.args)));break;case \"setGeoJSONSourceData\":t.push((()=>this.setGeoJSONSourceData.apply(this,o.args)));break;case \"setGlyphs\":t.push((()=>this.setGlyphs.apply(this,o.args)));break;case \"setSprite\":t.push((()=>this.setSprite.apply(this,o.args)));break;case \"setTerrain\":t.push((()=>this.map.setTerrain.apply(this,o.args)));break;case \"setSky\":t.push((()=>this.setSky.apply(this,o.args)));break;case \"setProjection\":this.setProjection.apply(this,o.args);break;case \"setGlobalState\":t.push((()=>this.setGlobalState.apply(this,o.args)));break;case \"setTransition\":t.push((()=>{}));break;default:i.push(o.command);}return {operations:t,unimplemented:i}}addImage(e,i){if(this.getImage(e))return this.fire(new t.k(new Error(`An image named \"${e}\" already exists.`)));this.imageManager.addImage(e,i),this._afterImageUpdated(e);}updateImage(e,t){this.imageManager.updateImage(e,t);}getImage(e){return this.imageManager.getImage(e)}removeImage(e){if(!this.getImage(e))return this.fire(new t.k(new Error(`An image named \"${e}\" does not exist.`)));this.imageManager.removeImage(e),this._afterImageUpdated(e);}_afterImageUpdated(e){this._availableImages=this.imageManager.listImages(),this._changedImages[e]=!0,this._changed=!0,this.dispatcher.broadcast(\"SI\",this._availableImages),this.fire(new t.l(\"data\",{dataType:\"style\"}));}listImages(){return this._checkLoaded(),this.imageManager.listImages()}addSource(e,i,o={}){if(this._checkLoaded(),void 0!==this.sourceCaches[e])throw new Error(`Source \"${e}\" already exists.`);if(!i.type)throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(i).join(\", \")}.`);if([\"vector\",\"raster\",\"geojson\",\"video\",\"image\"].indexOf(i.type)>=0&&this._validate(t.z.source,`sources.${e}`,i,null,o))return;this.map&&this.map._collectResourceTiming&&(i.collectResourceTiming=!0);const r=this.sourceCaches[e]=new xe(e,i,this.dispatcher);r.style=this,r.setEventedParent(this,(()=>({isSourceLoaded:r.loaded(),source:r.serialize(),sourceId:e}))),r.onAdd(this.map),this._changed=!0;}removeSource(e){if(this._checkLoaded(),void 0===this.sourceCaches[e])throw new Error(\"There is no source with this ID\");for(const i in this._layers)if(this._layers[i].source===e)return this.fire(new t.k(new Error(`Source \"${e}\" cannot be removed while layer \"${i}\" is using it.`)));const i=this.sourceCaches[e];delete this.sourceCaches[e],delete this._updatedSources[e],i.fire(new t.l(\"data\",{sourceDataType:\"metadata\",dataType:\"source\",sourceId:e})),i.setEventedParent(null),i.onRemove(this.map),this._changed=!0;}setGeoJSONSourceData(e,t){if(this._checkLoaded(),void 0===this.sourceCaches[e])throw new Error(`There is no source with this ID=${e}`);const i=this.sourceCaches[e].getSource();if(\"geojson\"!==i.type)throw new Error(`geojsonSource.type is ${i.type}, which is !== 'geojson`);i.setData(t),this._changed=!0;}getSource(e){return this.sourceCaches[e]&&this.sourceCaches[e].getSource()}addLayer(e,i,o={}){this._checkLoaded();const r=e.id;if(this.getLayer(r))return void this.fire(new t.k(new Error(`Layer \"${r}\" already exists on this map.`)));let a;if(\"custom\"===e.type){if(xi(this,t.bL(e)))return;a=t.bI(e);}else {if(\"source\"in e&&\"object\"==typeof e.source&&(this.addSource(r,e.source),e=t.bJ(e),e=t.e(e,{source:r})),this._validate(t.z.layer,`layers.${r}`,e,{arrayIndex:-1},o))return;a=t.bI(e),this._validateLayer(a),a.setEventedParent(this,{layer:{id:r}});}const s=i?this._order.indexOf(i):this._order.length;if(i&&-1===s)this.fire(new t.k(new Error(`Cannot add layer \"${r}\" before non-existing layer \"${i}\".`)));else {if(this._order.splice(s,0,r),this._layerOrderChanged=!0,this._layers[r]=a,this._removedLayers[r]&&a.source&&\"custom\"!==a.type){const e=this._removedLayers[r];delete this._removedLayers[r],e.type!==a.type?this._updatedSources[a.source]=\"clear\":(this._updatedSources[a.source]=\"reload\",this.sourceCaches[a.source].pause());}this._updateLayer(a),a.onAdd&&a.onAdd(this.map);}}moveLayer(e,i){if(this._checkLoaded(),this._changed=!0,!this._layers[e])return void this.fire(new t.k(new Error(`The layer '${e}' does not exist in the map's style and cannot be moved.`)));if(e===i)return;const o=this._order.indexOf(e);this._order.splice(o,1);const r=i?this._order.indexOf(i):this._order.length;i&&-1===r?this.fire(new t.k(new Error(`Cannot move layer \"${e}\" before non-existing layer \"${i}\".`))):(this._order.splice(r,0,e),this._layerOrderChanged=!0);}removeLayer(e){this._checkLoaded();const i=this._layers[e];if(!i)return void this.fire(new t.k(new Error(`Cannot remove non-existing layer \"${e}\".`)));i.setEventedParent(null);const o=this._order.indexOf(e);this._order.splice(o,1),this._layerOrderChanged=!0,this._changed=!0,this._removedLayers[e]=i,delete this._layers[e],this._serializedLayers&&delete this._serializedLayers[e],delete this._updatedLayers[e],delete this._updatedPaintProps[e],i.onRemove&&i.onRemove(this.map);}getLayer(e){return this._layers[e]}getLayersOrder(){return [...this._order]}hasLayer(e){return e in this._layers}setLayerZoomRange(e,i,o){this._checkLoaded();const r=this.getLayer(e);r?r.minzoom===i&&r.maxzoom===o||(null!=i&&(r.minzoom=i),null!=o&&(r.maxzoom=o),this._updateLayer(r)):this.fire(new t.k(new Error(`Cannot set the zoom range of non-existing layer \"${e}\".`)));}setFilter(e,i,o={}){this._checkLoaded();const r=this.getLayer(e);if(r){if(!t.bG(r.filter,i))return null==i?(r.setFilter(void 0),void this._updateLayer(r)):void(this._validate(t.z.filter,`layers.${r.id}.filter`,i,null,o)||(r.setFilter(t.bJ(i)),this._updateLayer(r)))}else this.fire(new t.k(new Error(`Cannot filter non-existing layer \"${e}\".`)));}getFilter(e){return t.bJ(this.getLayer(e).filter)}setLayoutProperty(e,i,o,r={}){this._checkLoaded();const a=this.getLayer(e);a?t.bG(a.getLayoutProperty(i),o)||(a.setLayoutProperty(i,o,r),this._updateLayer(a)):this.fire(new t.k(new Error(`Cannot style non-existing layer \"${e}\".`)));}getLayoutProperty(e,i){const o=this.getLayer(e);if(o)return o.getLayoutProperty(i);this.fire(new t.k(new Error(`Cannot get style of non-existing layer \"${e}\".`)));}setPaintProperty(e,i,o,r={}){this._checkLoaded();const a=this.getLayer(e);a?t.bG(a.getPaintProperty(i),o)||(a.setPaintProperty(i,o,r)&&this._updateLayer(a),this._changed=!0,this._updatedPaintProps[e]=!0,this._serializedLayers=null):this.fire(new t.k(new Error(`Cannot style non-existing layer \"${e}\".`)));}getPaintProperty(e,t){return this.getLayer(e).getPaintProperty(t)}setFeatureState(e,i){this._checkLoaded();const o=e.source,r=e.sourceLayer,a=this.sourceCaches[o];if(void 0===a)return void this.fire(new t.k(new Error(`The source '${o}' does not exist in the map's style.`)));const s=a.getSource().type;\"geojson\"===s&&r?this.fire(new t.k(new Error(\"GeoJSON sources cannot have a sourceLayer parameter.\"))):\"vector\"!==s||r?(void 0===e.id&&this.fire(new t.k(new Error(\"The feature id parameter must be provided.\"))),a.setFeatureState(r,e.id,i)):this.fire(new t.k(new Error(\"The sourceLayer parameter must be provided for vector source types.\")));}removeFeatureState(e,i){this._checkLoaded();const o=e.source,r=this.sourceCaches[o];if(void 0===r)return void this.fire(new t.k(new Error(`The source '${o}' does not exist in the map's style.`)));const a=r.getSource().type,s=\"vector\"===a?e.sourceLayer:void 0;\"vector\"!==a||s?i&&\"string\"!=typeof e.id&&\"number\"!=typeof e.id?this.fire(new t.k(new Error(\"A feature id is required to remove its specific state property.\"))):r.removeFeatureState(s,e.id,i):this.fire(new t.k(new Error(\"The sourceLayer parameter must be provided for vector source types.\")));}getFeatureState(e){this._checkLoaded();const i=e.source,o=e.sourceLayer,r=this.sourceCaches[i];if(void 0!==r)return \"vector\"!==r.getSource().type||o?(void 0===e.id&&this.fire(new t.k(new Error(\"The feature id parameter must be provided.\"))),r.getFeatureState(o,e.id)):void this.fire(new t.k(new Error(\"The sourceLayer parameter must be provided for vector source types.\")));this.fire(new t.k(new Error(`The source '${i}' does not exist in the map's style.`)));}getTransition(){return t.e({duration:300,delay:0},this.stylesheet&&this.stylesheet.transition)}serialize(){if(!this._loaded)return;const e=t.bM(this.sourceCaches,(e=>e.serialize())),i=this._serializeByIds(this._order,!0),o=this.map.getTerrain()||void 0,r=this.stylesheet;return t.bN({version:r.version,name:r.name,metadata:r.metadata,light:r.light,sky:r.sky,center:r.center,zoom:r.zoom,bearing:r.bearing,pitch:r.pitch,sprite:r.sprite,glyphs:r.glyphs,transition:r.transition,projection:r.projection,sources:e,layers:i,terrain:o},(e=>void 0!==e))}_updateLayer(e){this._updatedLayers[e.id]=!0,e.source&&!this._updatedSources[e.source]&&\"raster\"!==this.sourceCaches[e.source].getSource().type&&(this._updatedSources[e.source]=\"reload\",this.sourceCaches[e.source].pause()),this._serializedLayers=null,this._changed=!0;}_flattenAndSortRenderedFeatures(e){const t=e=>\"fill-extrusion\"===this._layers[e].type,i={},o=[];for(let r=this._order.length-1;r>=0;r--){const a=this._order[r];if(t(a)){i[a]=r;for(const t of e){const e=t[a];if(e)for(const t of e)o.push(t);}}}o.sort(((e,t)=>t.intersectionZ-e.intersectionZ));const r=[];for(let a=this._order.length-1;a>=0;a--){const s=this._order[a];if(t(s))for(let e=o.length-1;e>=0;e--){const t=o[e].feature;if(i[t.layer.id]<a)break;r.push(t),o.pop();}else for(const t of e){const e=t[s];if(e)for(const t of e)r.push(t.feature);}}return r}queryRenderedFeatures(e,i,o){i&&i.filter&&this._validate(t.z.filter,\"queryRenderedFeatures.filter\",i.filter,null,i);const r={};if(i&&i.layers){if(!(Array.isArray(i.layers)||i.layers instanceof Set))return this.fire(new t.k(new Error(\"parameters.layers must be an Array or a Set of strings\"))),[];for(const e of i.layers){const i=this._layers[e];if(!i)return this.fire(new t.k(new Error(`The layer '${e}' does not exist in the map's style and cannot be queried for features.`))),[];r[i.source]=!0;}}const a=[];i.availableImages=this._availableImages;const s=this._serializedAllLayers(),n=i.layers instanceof Set?i.layers:Array.isArray(i.layers)?new Set(i.layers):null,l=Object.assign(Object.assign({},i),{layers:n});for(const t in this.sourceCaches)i.layers&&!r[t]||a.push(j(this.sourceCaches[t],this._layers,s,e,l,o,this.map.terrain?(e,t,i)=>this.map.terrain.getElevation(e,t,i):void 0));return this.placement&&a.push(function(e,t,i,o,r,a,s){const n={},l=a.queryRenderedSymbols(o),c=[];for(const e of Object.keys(l).map(Number))c.push(s[e]);c.sort(N);for(const i of c){const o=i.featureIndex.lookupSymbolFeatures(l[i.bucketInstanceId],t,i.bucketIndex,i.sourceLayerIndex,r.filter,r.layers,r.availableImages,e);for(const e in o){const t=n[e]=n[e]||[],r=o[e];r.sort(((e,t)=>{const o=i.featureSortOrder;if(o){const i=o.indexOf(e.featureIndex);return o.indexOf(t.featureIndex)-i}return t.featureIndex-e.featureIndex}));for(const e of r)t.push(e);}}return function(e,t,i){for(const o in e)for(const r of e[o])Z(r,i[t[o].source]);return e}(n,e,i)}(this._layers,s,this.sourceCaches,e,l,this.placement.collisionIndex,this.placement.retainedQueryData)),this._flattenAndSortRenderedFeatures(a)}querySourceFeatures(e,i){i&&i.filter&&this._validate(t.z.filter,\"querySourceFeatures.filter\",i.filter,null,i);const o=this.sourceCaches[e];return o?function(e,t){const i=e.getRenderableIds().map((t=>e.getTileByID(t))),o=[],r={};for(let e=0;e<i.length;e++){const a=i[e],s=a.tileID.canonical.key;r[s]||(r[s]=!0,a.querySourceFeatures(o,t));}return o}(o,i):[]}getLight(){return this.light.getLight()}setLight(e,i={}){this._checkLoaded();const o=this.light.getLight();let r=!1;for(const i in e)if(!t.bG(e[i],o[i])){r=!0;break}if(!r)return;const a={now:s.now(),transition:t.e({duration:300,delay:0},this.stylesheet.transition)};this.light.setLight(e,i),this.light.updateTransitions(a);}getProjection(){var e;return null===(e=this.stylesheet)||void 0===e?void 0:e.projection}setProjection(e){if(this._checkLoaded(),this.projection){if(this.projection.name===e.type)return;this.projection.destroy(),delete this.projection;}this.stylesheet.projection=e,this._setProjectionInternal(e.type);}getSky(){var e;return null===(e=this.stylesheet)||void 0===e?void 0:e.sky}setSky(e,i={}){this._checkLoaded();const o=this.getSky();let r=!1;if(!e&&!o)return;if(e&&!o)r=!0;else if(!e&&o)r=!0;else for(const i in e)if(!t.bG(e[i],o[i])){r=!0;break}if(!r)return;const a={now:s.now(),transition:t.e({duration:300,delay:0},this.stylesheet.transition)};this.stylesheet.sky=e,this.sky.setSky(e,i),this.sky.updateTransitions(a);}_setProjectionInternal(e){const i=function(e){if(Array.isArray(e)){const t=new Jt({type:e});return {projection:t,transform:new gi,cameraHelper:new bi(t)}}switch(e){case \"mercator\":return {projection:new It,transform:new Lt,cameraHelper:new Ot};case \"globe\":{const e=new Jt({type:[\"interpolate\",[\"linear\"],[\"zoom\"],11,\"vertical-perspective\",12,\"mercator\"]});return {projection:e,transform:new gi,cameraHelper:new bi(e)}}case \"vertical-perspective\":return {projection:new Yt,transform:new fi,cameraHelper:new vi};default:return t.w(`Unknown projection name: ${e}. Falling back to mercator projection.`),{projection:new It,transform:new Lt,cameraHelper:new Ot}}}(e);this.projection=i.projection,this.map.migrateProjection(i.transform,i.cameraHelper);for(const e in this.sourceCaches)this.sourceCaches[e].reload();}_validate(e,i,o,r,a={}){return (!a||!1!==a.validate)&&xi(this,e.call(t.z,t.e({key:i,style:this.serialize(),value:o,styleSpec:t.v},r)))}_remove(e=!0){this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._loadStyleRequest&&(this._loadStyleRequest.abort(),this._loadStyleRequest=null),this._spriteRequest&&(this._spriteRequest.abort(),this._spriteRequest=null),oe().off(ee,this._rtlPluginLoaded);for(const e in this._layers)this._layers[e].setEventedParent(null);for(const e in this.sourceCaches){const t=this.sourceCaches[e];t.setEventedParent(null),t.onRemove(this.map);}this.imageManager.setEventedParent(null),this.setEventedParent(null),e&&this.dispatcher.broadcast(\"RM\",void 0),this.dispatcher.remove(e);}_clearSource(e){this.sourceCaches[e].clearTiles();}_reloadSource(e){this.sourceCaches[e].resume(),this.sourceCaches[e].reload();}_updateSources(e){for(const t in this.sourceCaches)this.sourceCaches[t].update(e,this.map.terrain);}_generateCollisionBoxes(){for(const e in this.sourceCaches)this._reloadSource(e);}_updatePlacement(e,t,i,o,r=!1){let a=!1,n=!1;const l={};for(const t of this._order){const i=this._layers[t];if(\"symbol\"!==i.type)continue;if(!l[i.source]){const e=this.sourceCaches[i.source];l[i.source]=e.getRenderableIds(!0).map((t=>e.getTileByID(t))).sort(((e,t)=>t.tileID.overscaledZ-e.tileID.overscaledZ||(e.tileID.isLessThan(t.tileID)?-1:1)));}const o=this.crossTileSymbolIndex.addLayer(i,l[i.source],e.center.lng);a=a||o;}if(this.crossTileSymbolIndex.pruneUnusedLayers(this._order),((r=r||this._layerOrderChanged||0===i)||!this.pauseablePlacement||this.pauseablePlacement.isDone()&&!this.placement.stillRecent(s.now(),e.zoom))&&(this.pauseablePlacement=new _t(e,this.map.terrain,this._order,r,t,i,o,this.placement),this._layerOrderChanged=!1),this.pauseablePlacement.isDone()?this.placement.setStale():(this.pauseablePlacement.continuePlacement(this._order,this._layers,l),this.pauseablePlacement.isDone()&&(this.placement=this.pauseablePlacement.commit(s.now()),n=!0),a&&this.pauseablePlacement.placement.setStale()),n||a)for(const e of this._order){const t=this._layers[e];\"symbol\"===t.type&&this.placement.updateLayerOpacities(t,l[t.source]);}return !this.pauseablePlacement.isDone()||this.placement.hasTransitions(s.now())}_releaseSymbolFadeTiles(){for(const e in this.sourceCaches)this.sourceCaches[e].releaseSymbolFadeTiles();}getImages(e,i){return t._(this,void 0,void 0,(function*(){const e=yield this.imageManager.getImages(i.icons);this._updateTilesForChangedImages();const t=this.sourceCaches[i.source];return t&&t.setDependencies(i.tileID.key,i.type,i.icons),e}))}getGlyphs(e,i){return t._(this,void 0,void 0,(function*(){const e=yield this.glyphManager.getGlyphs(i.stacks),t=this.sourceCaches[i.source];return t&&t.setDependencies(i.tileID.key,i.type,[\"\"]),e}))}getGlyphsUrl(){return this.stylesheet.glyphs||null}setGlyphs(e,i={}){this._checkLoaded(),e&&this._validate(t.z.glyphs,\"glyphs\",e,null,i)||(this._glyphsDidChange=!0,this.stylesheet.glyphs=e,this.glyphManager.entries={},this.glyphManager.setURL(e));}addSprite(e,i,o={},r){this._checkLoaded();const a=[{id:e,url:i}],s=[...f(this.stylesheet.sprite),...a];this._validate(t.z.sprite,\"sprite\",s,null,o)||(this.stylesheet.sprite=s,this._loadSprite(a,!0,r));}removeSprite(e){this._checkLoaded();const i=f(this.stylesheet.sprite);if(i.find((t=>t.id===e))){if(this._spritesImagesIds[e])for(const t of this._spritesImagesIds[e])this.imageManager.removeImage(t),this._changedImages[t]=!0;i.splice(i.findIndex((t=>t.id===e)),1),this.stylesheet.sprite=i.length>0?i:void 0,delete this._spritesImagesIds[e],this._availableImages=this.imageManager.listImages(),this._changed=!0,this.dispatcher.broadcast(\"SI\",this._availableImages),this.fire(new t.l(\"data\",{dataType:\"style\"}));}else this.fire(new t.k(new Error(`Sprite \"${e}\" doesn't exists on this map.`)));}getSprite(){return f(this.stylesheet.sprite)}setSprite(e,i={},o){this._checkLoaded(),e&&this._validate(t.z.sprite,\"sprite\",e,null,i)||(this.stylesheet.sprite=e,e?this._loadSprite(e,!0,o):(this._unloadSprite(),o&&o(null)));}}var Ti=t.aI([{name:\"a_pos\",type:\"Int16\",components:2},{name:\"a_texture_pos\",type:\"Int16\",components:2}]);class Pi{constructor(){this.boundProgram=null,this.boundLayoutVertexBuffer=null,this.boundPaintVertexBuffers=[],this.boundIndexBuffer=null,this.boundVertexOffset=null,this.boundDynamicVertexBuffer=null,this.vao=null;}bind(e,t,i,o,r,a,s,n,l){this.context=e;let c=this.boundPaintVertexBuffers.length!==o.length;for(let e=0;!c&&e<o.length;e++)this.boundPaintVertexBuffers[e]!==o[e]&&(c=!0);!this.vao||this.boundProgram!==t||this.boundLayoutVertexBuffer!==i||c||this.boundIndexBuffer!==r||this.boundVertexOffset!==a||this.boundDynamicVertexBuffer!==s||this.boundDynamicVertexBuffer2!==n||this.boundDynamicVertexBuffer3!==l?this.freshBind(t,i,o,r,a,s,n,l):(e.bindVertexArray.set(this.vao),s&&s.bind(),r&&r.dynamicDraw&&r.bind(),n&&n.bind(),l&&l.bind());}freshBind(e,t,i,o,r,a,s,n){const l=e.numAttributes,c=this.context,h=c.gl;this.vao&&this.destroy(),this.vao=c.createVertexArray(),c.bindVertexArray.set(this.vao),this.boundProgram=e,this.boundLayoutVertexBuffer=t,this.boundPaintVertexBuffers=i,this.boundIndexBuffer=o,this.boundVertexOffset=r,this.boundDynamicVertexBuffer=a,this.boundDynamicVertexBuffer2=s,this.boundDynamicVertexBuffer3=n,t.enableAttributes(h,e);for(const t of i)t.enableAttributes(h,e);a&&a.enableAttributes(h,e),s&&s.enableAttributes(h,e),n&&n.enableAttributes(h,e),t.bind(),t.setVertexAttribPointers(h,e,r);for(const t of i)t.bind(),t.setVertexAttribPointers(h,e,r);a&&(a.bind(),a.setVertexAttribPointers(h,e,r)),o&&o.bind(),s&&(s.bind(),s.setVertexAttribPointers(h,e,r)),n&&(n.bind(),n.setVertexAttribPointers(h,e,r)),c.currentNumAttributes=l;}destroy(){this.vao&&(this.context.deleteVertexArray(this.vao),this.vao=null);}}const Ci=(e,i,o,r,a)=>({u_texture:0,u_ele_delta:e,u_fog_matrix:i,u_fog_color:o?o.properties.get(\"fog-color\"):t.be.white,u_fog_ground_blend:o?o.properties.get(\"fog-ground-blend\"):1,u_fog_ground_blend_opacity:a?0:o?o.calculateFogBlendOpacity(r):0,u_horizon_color:o?o.properties.get(\"horizon-color\"):t.be.white,u_horizon_fog_blend:o?o.properties.get(\"horizon-fog-blend\"):1,u_is_globe_mode:a?1:0}),Ii={mainMatrix:\"u_projection_matrix\",tileMercatorCoords:\"u_projection_tile_mercator_coords\",clippingPlane:\"u_projection_clipping_plane\",projectionTransition:\"u_projection_transition\",fallbackMatrix:\"u_projection_fallback_matrix\"};function Mi(e){const t=[];for(let i=0;i<e.length;i++){if(null===e[i])continue;const o=e[i].split(\" \");t.push(o.pop());}return t}class Si{constructor(e,i,o,r,a,s,n,l,c=[]){const h=e.gl;this.program=h.createProgram();const u=Mi(i.staticAttributes),d=o?o.getBinderAttributes():[],_=u.concat(d),p=xt.prelude.staticUniforms?Mi(xt.prelude.staticUniforms):[],m=n.staticUniforms?Mi(n.staticUniforms):[],f=i.staticUniforms?Mi(i.staticUniforms):[],g=o?o.getBinderUniforms():[],v=p.concat(m).concat(f).concat(g),b=[];for(const e of v)b.indexOf(e)<0&&b.push(e);const x=o?o.defines():[];qt(h)&&x.unshift(\"#version 300 es\"),a&&x.push(\"#define OVERDRAW_INSPECTOR;\"),s&&x.push(\"#define TERRAIN3D;\"),l&&x.push(l),c&&x.push(...c);let y=x.concat(xt.prelude.fragmentSource,n.fragmentSource,i.fragmentSource).join(\"\\n\"),w=x.concat(xt.prelude.vertexSource,n.vertexSource,i.vertexSource).join(\"\\n\");qt(h)||(y=function(e){return e.replace(/\\bin\\s/g,\"varying \").replace(\"out highp vec4 fragColor;\",\"\").replace(/fragColor/g,\"gl_FragColor\").replace(/texture\\(/g,\"texture2D(\")}(y),w=function(e){return e.replace(/\\bin\\s/g,\"attribute \").replace(/\\bout\\s/g,\"varying \").replace(/texture\\(/g,\"texture2D(\")}(w));const T=h.createShader(h.FRAGMENT_SHADER);if(h.isContextLost())return void(this.failedToCreate=!0);if(h.shaderSource(T,y),h.compileShader(T),!h.getShaderParameter(T,h.COMPILE_STATUS))throw new Error(`Could not compile fragment shader: ${h.getShaderInfoLog(T)}`);h.attachShader(this.program,T);const P=h.createShader(h.VERTEX_SHADER);if(h.isContextLost())return void(this.failedToCreate=!0);if(h.shaderSource(P,w),h.compileShader(P),!h.getShaderParameter(P,h.COMPILE_STATUS))throw new Error(`Could not compile vertex shader: ${h.getShaderInfoLog(P)}`);h.attachShader(this.program,P),this.attributes={};const C={};this.numAttributes=_.length;for(let e=0;e<this.numAttributes;e++)_[e]&&(h.bindAttribLocation(this.program,e,_[e]),this.attributes[_[e]]=e);if(h.linkProgram(this.program),!h.getProgramParameter(this.program,h.LINK_STATUS))throw new Error(`Program failed to link: ${h.getProgramInfoLog(this.program)}`);h.deleteShader(P),h.deleteShader(T);for(let e=0;e<b.length;e++){const t=b[e];if(t&&!C[t]){const e=h.getUniformLocation(this.program,t);e&&(C[t]=e);}}this.fixedUniforms=r(e,C),this.terrainUniforms=((e,i)=>({u_depth:new t.bO(e,i.u_depth),u_terrain:new t.bO(e,i.u_terrain),u_terrain_dim:new t.bf(e,i.u_terrain_dim),u_terrain_matrix:new t.bQ(e,i.u_terrain_matrix),u_terrain_unpack:new t.bR(e,i.u_terrain_unpack),u_terrain_exaggeration:new t.bf(e,i.u_terrain_exaggeration)}))(e,C),this.projectionUniforms=((e,i)=>({u_projection_matrix:new t.bQ(e,i.u_projection_matrix),u_projection_tile_mercator_coords:new t.bR(e,i.u_projection_tile_mercator_coords),u_projection_clipping_plane:new t.bR(e,i.u_projection_clipping_plane),u_projection_transition:new t.bf(e,i.u_projection_transition),u_projection_fallback_matrix:new t.bQ(e,i.u_projection_fallback_matrix)}))(e,C),this.binderUniforms=o?o.getUniforms(e,C):[];}draw(e,t,i,o,r,a,s,n,l,c,h,u,d,_,p,m,f,g,v){const b=e.gl;if(this.failedToCreate)return;if(e.program.set(this.program),e.setDepthMode(i),e.setStencilMode(o),e.setColorMode(r),e.setCullFace(a),n){e.activeTexture.set(b.TEXTURE2),b.bindTexture(b.TEXTURE_2D,n.depthTexture),e.activeTexture.set(b.TEXTURE3),b.bindTexture(b.TEXTURE_2D,n.texture);for(const e in this.terrainUniforms)this.terrainUniforms[e].set(n[e]);}if(l)for(const e in l)this.projectionUniforms[Ii[e]].set(l[e]);if(s)for(const e in this.fixedUniforms)this.fixedUniforms[e].set(s[e]);m&&m.setUniforms(e,this.binderUniforms,_,{zoom:p});let x=0;switch(t){case b.LINES:x=2;break;case b.TRIANGLES:x=3;break;case b.LINE_STRIP:x=1;}for(const i of d.get()){const o=i.vaos||(i.vaos={});(o[c]||(o[c]=new Pi)).bind(e,this,h,m?m.getPaintVertexBuffers():[],u,i.vertexOffset,f,g,v),b.drawElements(t,i.primitiveLength*x,b.UNSIGNED_SHORT,i.primitiveOffset*x*2);}}}function Ei(e,i,o){const r=1/t.aB(o,1,i.transform.tileZoom),a=Math.pow(2,o.tileID.overscaledZ),s=o.tileSize*Math.pow(2,i.transform.tileZoom)/a,n=s*(o.tileID.canonical.x+o.tileID.wrap*a),l=s*o.tileID.canonical.y;return {u_image:0,u_texsize:o.imageAtlasTexture.size,u_scale:[r,e.fromScale,e.toScale],u_fade:e.t,u_pixel_coord_upper:[n>>16,l>>16],u_pixel_coord_lower:[65535&n,65535&l]}}const Ri=(e,i,o,r)=>{const a=e.style.light,s=a.properties.get(\"position\"),n=[s.x,s.y,s.z],l=t.bU();\"viewport\"===a.properties.get(\"anchor\")&&t.bV(l,e.transform.bearingInRadians),t.bW(n,n,l);const c=e.transform.transformLightDirection(n),h=a.properties.get(\"color\");return {u_lightpos:n,u_lightpos_globe:c,u_lightintensity:a.properties.get(\"intensity\"),u_lightcolor:[h.r,h.g,h.b],u_vertical_gradient:+i,u_opacity:o,u_fill_translate:r}},zi=(e,i,o,r,a,s,n)=>t.e(Ri(e,i,o,r),Ei(s,e,n),{u_height_factor:-Math.pow(2,a.overscaledZ)/n.tileSize/8}),Di=(e,i,o,r)=>t.e(Ei(i,e,o),{u_fill_translate:r}),Ai=(e,t)=>({u_world:e,u_fill_translate:t}),Li=(e,i,o,r,a)=>t.e(Di(e,i,o,a),{u_world:r}),ki=(e,i,o,r,a)=>{const s=e.transform;let n,l,c=0;if(\"map\"===o.paint.get(\"circle-pitch-alignment\")){const e=t.aB(i,1,s.zoom);n=!0,l=[e,e],c=e/(t.$*Math.pow(2,i.tileID.overscaledZ))*2*Math.PI*a;}else n=!1,l=s.pixelsToGLUnits;return {u_camera_to_center_distance:s.cameraToCenterDistance,u_scale_with_map:+(\"map\"===o.paint.get(\"circle-pitch-scale\")),u_pitch_with_map:+n,u_device_pixel_ratio:e.pixelRatio,u_extrude_scale:l,u_globe_extrude_scale:c,u_translate:r}},Fi=e=>({u_pixel_extrude_scale:[1/e.width,1/e.height]}),Bi=e=>({u_viewport_size:[e.width,e.height]}),Oi=(e,t=1)=>({u_color:e,u_overlay:0,u_overlay_scale:t}),ji=(e,i,o,r)=>{const a=t.aB(e,1,i)/(t.$*Math.pow(2,e.tileID.overscaledZ))*2*Math.PI*r;return {u_extrude_scale:t.aB(e,1,i),u_intensity:o,u_globe_extrude_scale:a}},Ni=(e,i,o,r)=>{const a=t.L();t.bX(a,0,e.width,e.height,0,0,1);const s=e.context.gl;return {u_matrix:a,u_world:[s.drawingBufferWidth,s.drawingBufferHeight],u_image:o,u_color_ramp:r,u_opacity:i.paint.get(\"heatmap-opacity\")}},Zi=(e,t,i)=>{const o=i.paint.get(\"hillshade-accent-color\");let r;switch(i.paint.get(\"hillshade-method\")){case \"basic\":r=4;break;case \"combined\":r=1;break;case \"igor\":r=2;break;case \"multidirectional\":r=3;break;default:r=0;}const a=i.getIlluminationProperties();for(let t=0;t<a.directionRadians.length;t++)\"viewport\"===i.paint.get(\"hillshade-illumination-anchor\")&&(a.directionRadians[t]+=e.transform.bearingInRadians);return {u_image:0,u_latrange:Gi(0,t.tileID),u_exaggeration:i.paint.get(\"hillshade-exaggeration\"),u_altitudes:a.altitudeRadians,u_azimuths:a.directionRadians,u_accent:o,u_method:r,u_highlights:a.highlightColor,u_shadows:a.shadowColor}},Ui=(e,i)=>{const o=i.stride,r=t.L();return t.bX(r,0,t.$,-8192,0,0,1),t.M(r,r,[0,-8192,0]),{u_matrix:r,u_image:1,u_dimension:[o,o],u_zoom:e.overscaledZ,u_unpack:i.getUnpackVector()}};function Gi(e,i){const o=Math.pow(2,i.canonical.z),r=i.canonical.y;return [new t.a0(0,r/o).toLngLat().lat,new t.a0(0,(r+1)/o).toLngLat().lat]}const Vi=(e,t)=>({u_image:0,u_unpack:t.getUnpackVector(),u_dimension:[t.stride,t.stride],u_elevation_stops:1,u_color_stops:4,u_opacity:e.paint.get(\"color-relief-opacity\")}),$i=(e,i,o,r)=>{const a=e.transform;return {u_translation:Ki(e,i,o),u_ratio:r/t.aB(i,1,a.zoom),u_device_pixel_ratio:e.pixelRatio,u_units_to_pixels:[1/a.pixelsToGLUnits[0],1/a.pixelsToGLUnits[1]]}},qi=(e,i,o,r,a)=>t.e($i(e,i,o,r),{u_image:0,u_image_height:a}),Wi=(e,i,o,r,a)=>{const s=e.transform,n=Xi(i,s);return {u_translation:Ki(e,i,o),u_texsize:i.imageAtlasTexture.size,u_ratio:r/t.aB(i,1,s.zoom),u_device_pixel_ratio:e.pixelRatio,u_image:0,u_scale:[n,a.fromScale,a.toScale],u_fade:a.t,u_units_to_pixels:[1/s.pixelsToGLUnits[0],1/s.pixelsToGLUnits[1]]}},Hi=(e,i,o,r,a,s)=>{const n=e.lineAtlas,l=Xi(i,e.transform),c=\"round\"===o.layout.get(\"line-cap\"),h=n.getDash(a.from,c),u=n.getDash(a.to,c),d=h.width*s.fromScale,_=u.width*s.toScale;return t.e($i(e,i,o,r),{u_patternscale_a:[l/d,-h.height/2],u_patternscale_b:[l/_,-u.height/2],u_sdfgamma:n.width/(256*Math.min(d,_)*e.pixelRatio)/2,u_image:0,u_tex_y_a:h.y,u_tex_y_b:u.y,u_mix:s.t})};function Xi(e,i){return 1/t.aB(e,1,i.tileZoom)}function Ki(e,i,o){return t.aC(e.transform,i,o.paint.get(\"line-translate\"),o.paint.get(\"line-translate-anchor\"))}const Yi=(e,t,i,o,r)=>{return {u_tl_parent:e,u_scale_parent:t,u_buffer_scale:1,u_fade_t:i.mix,u_opacity:i.opacity*o.paint.get(\"raster-opacity\"),u_image0:0,u_image1:1,u_brightness_low:o.paint.get(\"raster-brightness-min\"),u_brightness_high:o.paint.get(\"raster-brightness-max\"),u_saturation_factor:(s=o.paint.get(\"raster-saturation\"),s>0?1-1/(1.001-s):-s),u_contrast_factor:(a=o.paint.get(\"raster-contrast\"),a>0?1/(1-a):1+a),u_spin_weights:Qi(o.paint.get(\"raster-hue-rotate\")),u_coords_top:[r[0].x,r[0].y,r[1].x,r[1].y],u_coords_bottom:[r[3].x,r[3].y,r[2].x,r[2].y]};var a,s;};function Qi(e){e*=Math.PI/180;const t=Math.sin(e),i=Math.cos(e);return [(2*i+1)/3,(-Math.sqrt(3)*t-i+1)/3,(Math.sqrt(3)*t-i+1)/3]}const Ji=(e,t,i,o,r,a,s,n,l,c,h,u,d)=>{const _=s.transform;return {u_is_size_zoom_constant:+(\"constant\"===e||\"source\"===e),u_is_size_feature_constant:+(\"constant\"===e||\"camera\"===e),u_size_t:t?t.uSizeT:0,u_size:t?t.uSize:0,u_camera_to_center_distance:_.cameraToCenterDistance,u_pitch:_.pitch/360*2*Math.PI,u_rotate_symbol:+i,u_aspect_ratio:_.width/_.height,u_fade_change:s.options.fadeDuration?s.symbolFadeChange:1,u_label_plane_matrix:n,u_coord_matrix:l,u_is_text:+h,u_pitch_with_map:+o,u_is_along_line:r,u_is_variable_anchor:a,u_texsize:u,u_texture:0,u_translation:c,u_pitched_scale:d}},eo=(e,i,o,r,a,s,n,l,c,h,u,d,_,p)=>{const m=n.transform;return t.e(Ji(e,i,o,r,a,s,n,l,c,h,u,d,p),{u_gamma_scale:r?Math.cos(m.pitch*Math.PI/180)*m.cameraToCenterDistance:1,u_device_pixel_ratio:n.pixelRatio,u_is_halo:1})},to=(e,i,o,r,a,s,n,l,c,h,u,d,_)=>t.e(eo(e,i,o,r,a,s,n,l,c,h,!0,u,0,_),{u_texsize_icon:d,u_texture_icon:1}),io=(e,t)=>({u_opacity:e,u_color:t}),oo=(e,i,o,r,a)=>t.e(function(e,i,o,r){const a=o.imageManager.getPattern(e.from.toString()),s=o.imageManager.getPattern(e.to.toString()),{width:n,height:l}=o.imageManager.getPixelSize(),c=Math.pow(2,r.tileID.overscaledZ),h=r.tileSize*Math.pow(2,o.transform.tileZoom)/c,u=h*(r.tileID.canonical.x+r.tileID.wrap*c),d=h*r.tileID.canonical.y;return {u_image:0,u_pattern_tl_a:a.tl,u_pattern_br_a:a.br,u_pattern_tl_b:s.tl,u_pattern_br_b:s.br,u_texsize:[n,l],u_mix:i.t,u_pattern_size_a:a.displaySize,u_pattern_size_b:s.displaySize,u_scale_a:i.fromScale,u_scale_b:i.toScale,u_tile_units_to_pixels:1/t.aB(r,1,o.transform.tileZoom),u_pixel_coord_upper:[u>>16,d>>16],u_pixel_coord_lower:[65535&u,65535&d]}}(o,a,i,r),{u_opacity:e}),ro=(e,t)=>{},ao={fillExtrusion:(e,i)=>({u_lightpos:new t.bS(e,i.u_lightpos),u_lightpos_globe:new t.bS(e,i.u_lightpos_globe),u_lightintensity:new t.bf(e,i.u_lightintensity),u_lightcolor:new t.bS(e,i.u_lightcolor),u_vertical_gradient:new t.bf(e,i.u_vertical_gradient),u_opacity:new t.bf(e,i.u_opacity),u_fill_translate:new t.bT(e,i.u_fill_translate)}),fillExtrusionPattern:(e,i)=>({u_lightpos:new t.bS(e,i.u_lightpos),u_lightpos_globe:new t.bS(e,i.u_lightpos_globe),u_lightintensity:new t.bf(e,i.u_lightintensity),u_lightcolor:new t.bS(e,i.u_lightcolor),u_vertical_gradient:new t.bf(e,i.u_vertical_gradient),u_height_factor:new t.bf(e,i.u_height_factor),u_opacity:new t.bf(e,i.u_opacity),u_fill_translate:new t.bT(e,i.u_fill_translate),u_image:new t.bO(e,i.u_image),u_texsize:new t.bT(e,i.u_texsize),u_pixel_coord_upper:new t.bT(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.bT(e,i.u_pixel_coord_lower),u_scale:new t.bS(e,i.u_scale),u_fade:new t.bf(e,i.u_fade)}),fill:(e,i)=>({u_fill_translate:new t.bT(e,i.u_fill_translate)}),fillPattern:(e,i)=>({u_image:new t.bO(e,i.u_image),u_texsize:new t.bT(e,i.u_texsize),u_pixel_coord_upper:new t.bT(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.bT(e,i.u_pixel_coord_lower),u_scale:new t.bS(e,i.u_scale),u_fade:new t.bf(e,i.u_fade),u_fill_translate:new t.bT(e,i.u_fill_translate)}),fillOutline:(e,i)=>({u_world:new t.bT(e,i.u_world),u_fill_translate:new t.bT(e,i.u_fill_translate)}),fillOutlinePattern:(e,i)=>({u_world:new t.bT(e,i.u_world),u_image:new t.bO(e,i.u_image),u_texsize:new t.bT(e,i.u_texsize),u_pixel_coord_upper:new t.bT(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.bT(e,i.u_pixel_coord_lower),u_scale:new t.bS(e,i.u_scale),u_fade:new t.bf(e,i.u_fade),u_fill_translate:new t.bT(e,i.u_fill_translate)}),circle:(e,i)=>({u_camera_to_center_distance:new t.bf(e,i.u_camera_to_center_distance),u_scale_with_map:new t.bO(e,i.u_scale_with_map),u_pitch_with_map:new t.bO(e,i.u_pitch_with_map),u_extrude_scale:new t.bT(e,i.u_extrude_scale),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_globe_extrude_scale:new t.bf(e,i.u_globe_extrude_scale),u_translate:new t.bT(e,i.u_translate)}),collisionBox:(e,i)=>({u_pixel_extrude_scale:new t.bT(e,i.u_pixel_extrude_scale)}),collisionCircle:(e,i)=>({u_viewport_size:new t.bT(e,i.u_viewport_size)}),debug:(e,i)=>({u_color:new t.bP(e,i.u_color),u_overlay:new t.bO(e,i.u_overlay),u_overlay_scale:new t.bf(e,i.u_overlay_scale)}),depth:ro,clippingMask:ro,heatmap:(e,i)=>({u_extrude_scale:new t.bf(e,i.u_extrude_scale),u_intensity:new t.bf(e,i.u_intensity),u_globe_extrude_scale:new t.bf(e,i.u_globe_extrude_scale)}),heatmapTexture:(e,i)=>({u_matrix:new t.bQ(e,i.u_matrix),u_world:new t.bT(e,i.u_world),u_image:new t.bO(e,i.u_image),u_color_ramp:new t.bO(e,i.u_color_ramp),u_opacity:new t.bf(e,i.u_opacity)}),hillshade:(e,i)=>({u_image:new t.bO(e,i.u_image),u_latrange:new t.bT(e,i.u_latrange),u_exaggeration:new t.bf(e,i.u_exaggeration),u_altitudes:new t.bZ(e,i.u_altitudes),u_azimuths:new t.bZ(e,i.u_azimuths),u_accent:new t.bP(e,i.u_accent),u_method:new t.bO(e,i.u_method),u_shadows:new t.bY(e,i.u_shadows),u_highlights:new t.bY(e,i.u_highlights)}),hillshadePrepare:(e,i)=>({u_matrix:new t.bQ(e,i.u_matrix),u_image:new t.bO(e,i.u_image),u_dimension:new t.bT(e,i.u_dimension),u_zoom:new t.bf(e,i.u_zoom),u_unpack:new t.bR(e,i.u_unpack)}),colorRelief:(e,i)=>({u_image:new t.bO(e,i.u_image),u_unpack:new t.bR(e,i.u_unpack),u_dimension:new t.bT(e,i.u_dimension),u_elevation_stops:new t.bO(e,i.u_elevation_stops),u_color_stops:new t.bO(e,i.u_color_stops),u_opacity:new t.bf(e,i.u_opacity)}),line:(e,i)=>({u_translation:new t.bT(e,i.u_translation),u_ratio:new t.bf(e,i.u_ratio),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.bT(e,i.u_units_to_pixels)}),lineGradient:(e,i)=>({u_translation:new t.bT(e,i.u_translation),u_ratio:new t.bf(e,i.u_ratio),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.bT(e,i.u_units_to_pixels),u_image:new t.bO(e,i.u_image),u_image_height:new t.bf(e,i.u_image_height)}),linePattern:(e,i)=>({u_translation:new t.bT(e,i.u_translation),u_texsize:new t.bT(e,i.u_texsize),u_ratio:new t.bf(e,i.u_ratio),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_image:new t.bO(e,i.u_image),u_units_to_pixels:new t.bT(e,i.u_units_to_pixels),u_scale:new t.bS(e,i.u_scale),u_fade:new t.bf(e,i.u_fade)}),lineSDF:(e,i)=>({u_translation:new t.bT(e,i.u_translation),u_ratio:new t.bf(e,i.u_ratio),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.bT(e,i.u_units_to_pixels),u_patternscale_a:new t.bT(e,i.u_patternscale_a),u_patternscale_b:new t.bT(e,i.u_patternscale_b),u_sdfgamma:new t.bf(e,i.u_sdfgamma),u_image:new t.bO(e,i.u_image),u_tex_y_a:new t.bf(e,i.u_tex_y_a),u_tex_y_b:new t.bf(e,i.u_tex_y_b),u_mix:new t.bf(e,i.u_mix)}),raster:(e,i)=>({u_tl_parent:new t.bT(e,i.u_tl_parent),u_scale_parent:new t.bf(e,i.u_scale_parent),u_buffer_scale:new t.bf(e,i.u_buffer_scale),u_fade_t:new t.bf(e,i.u_fade_t),u_opacity:new t.bf(e,i.u_opacity),u_image0:new t.bO(e,i.u_image0),u_image1:new t.bO(e,i.u_image1),u_brightness_low:new t.bf(e,i.u_brightness_low),u_brightness_high:new t.bf(e,i.u_brightness_high),u_saturation_factor:new t.bf(e,i.u_saturation_factor),u_contrast_factor:new t.bf(e,i.u_contrast_factor),u_spin_weights:new t.bS(e,i.u_spin_weights),u_coords_top:new t.bR(e,i.u_coords_top),u_coords_bottom:new t.bR(e,i.u_coords_bottom)}),symbolIcon:(e,i)=>({u_is_size_zoom_constant:new t.bO(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.bO(e,i.u_is_size_feature_constant),u_size_t:new t.bf(e,i.u_size_t),u_size:new t.bf(e,i.u_size),u_camera_to_center_distance:new t.bf(e,i.u_camera_to_center_distance),u_pitch:new t.bf(e,i.u_pitch),u_rotate_symbol:new t.bO(e,i.u_rotate_symbol),u_aspect_ratio:new t.bf(e,i.u_aspect_ratio),u_fade_change:new t.bf(e,i.u_fade_change),u_label_plane_matrix:new t.bQ(e,i.u_label_plane_matrix),u_coord_matrix:new t.bQ(e,i.u_coord_matrix),u_is_text:new t.bO(e,i.u_is_text),u_pitch_with_map:new t.bO(e,i.u_pitch_with_map),u_is_along_line:new t.bO(e,i.u_is_along_line),u_is_variable_anchor:new t.bO(e,i.u_is_variable_anchor),u_texsize:new t.bT(e,i.u_texsize),u_texture:new t.bO(e,i.u_texture),u_translation:new t.bT(e,i.u_translation),u_pitched_scale:new t.bf(e,i.u_pitched_scale)}),symbolSDF:(e,i)=>({u_is_size_zoom_constant:new t.bO(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.bO(e,i.u_is_size_feature_constant),u_size_t:new t.bf(e,i.u_size_t),u_size:new t.bf(e,i.u_size),u_camera_to_center_distance:new t.bf(e,i.u_camera_to_center_distance),u_pitch:new t.bf(e,i.u_pitch),u_rotate_symbol:new t.bO(e,i.u_rotate_symbol),u_aspect_ratio:new t.bf(e,i.u_aspect_ratio),u_fade_change:new t.bf(e,i.u_fade_change),u_label_plane_matrix:new t.bQ(e,i.u_label_plane_matrix),u_coord_matrix:new t.bQ(e,i.u_coord_matrix),u_is_text:new t.bO(e,i.u_is_text),u_pitch_with_map:new t.bO(e,i.u_pitch_with_map),u_is_along_line:new t.bO(e,i.u_is_along_line),u_is_variable_anchor:new t.bO(e,i.u_is_variable_anchor),u_texsize:new t.bT(e,i.u_texsize),u_texture:new t.bO(e,i.u_texture),u_gamma_scale:new t.bf(e,i.u_gamma_scale),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_is_halo:new t.bO(e,i.u_is_halo),u_translation:new t.bT(e,i.u_translation),u_pitched_scale:new t.bf(e,i.u_pitched_scale)}),symbolTextAndIcon:(e,i)=>({u_is_size_zoom_constant:new t.bO(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.bO(e,i.u_is_size_feature_constant),u_size_t:new t.bf(e,i.u_size_t),u_size:new t.bf(e,i.u_size),u_camera_to_center_distance:new t.bf(e,i.u_camera_to_center_distance),u_pitch:new t.bf(e,i.u_pitch),u_rotate_symbol:new t.bO(e,i.u_rotate_symbol),u_aspect_ratio:new t.bf(e,i.u_aspect_ratio),u_fade_change:new t.bf(e,i.u_fade_change),u_label_plane_matrix:new t.bQ(e,i.u_label_plane_matrix),u_coord_matrix:new t.bQ(e,i.u_coord_matrix),u_is_text:new t.bO(e,i.u_is_text),u_pitch_with_map:new t.bO(e,i.u_pitch_with_map),u_is_along_line:new t.bO(e,i.u_is_along_line),u_is_variable_anchor:new t.bO(e,i.u_is_variable_anchor),u_texsize:new t.bT(e,i.u_texsize),u_texsize_icon:new t.bT(e,i.u_texsize_icon),u_texture:new t.bO(e,i.u_texture),u_texture_icon:new t.bO(e,i.u_texture_icon),u_gamma_scale:new t.bf(e,i.u_gamma_scale),u_device_pixel_ratio:new t.bf(e,i.u_device_pixel_ratio),u_is_halo:new t.bO(e,i.u_is_halo),u_translation:new t.bT(e,i.u_translation),u_pitched_scale:new t.bf(e,i.u_pitched_scale)}),background:(e,i)=>({u_opacity:new t.bf(e,i.u_opacity),u_color:new t.bP(e,i.u_color)}),backgroundPattern:(e,i)=>({u_opacity:new t.bf(e,i.u_opacity),u_image:new t.bO(e,i.u_image),u_pattern_tl_a:new t.bT(e,i.u_pattern_tl_a),u_pattern_br_a:new t.bT(e,i.u_pattern_br_a),u_pattern_tl_b:new t.bT(e,i.u_pattern_tl_b),u_pattern_br_b:new t.bT(e,i.u_pattern_br_b),u_texsize:new t.bT(e,i.u_texsize),u_mix:new t.bf(e,i.u_mix),u_pattern_size_a:new t.bT(e,i.u_pattern_size_a),u_pattern_size_b:new t.bT(e,i.u_pattern_size_b),u_scale_a:new t.bf(e,i.u_scale_a),u_scale_b:new t.bf(e,i.u_scale_b),u_pixel_coord_upper:new t.bT(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.bT(e,i.u_pixel_coord_lower),u_tile_units_to_pixels:new t.bf(e,i.u_tile_units_to_pixels)}),terrain:(e,i)=>({u_texture:new t.bO(e,i.u_texture),u_ele_delta:new t.bf(e,i.u_ele_delta),u_fog_matrix:new t.bQ(e,i.u_fog_matrix),u_fog_color:new t.bP(e,i.u_fog_color),u_fog_ground_blend:new t.bf(e,i.u_fog_ground_blend),u_fog_ground_blend_opacity:new t.bf(e,i.u_fog_ground_blend_opacity),u_horizon_color:new t.bP(e,i.u_horizon_color),u_horizon_fog_blend:new t.bf(e,i.u_horizon_fog_blend),u_is_globe_mode:new t.bf(e,i.u_is_globe_mode)}),terrainDepth:(e,i)=>({u_ele_delta:new t.bf(e,i.u_ele_delta)}),terrainCoords:(e,i)=>({u_texture:new t.bO(e,i.u_texture),u_terrain_coords_id:new t.bf(e,i.u_terrain_coords_id),u_ele_delta:new t.bf(e,i.u_ele_delta)}),projectionErrorMeasurement:(e,i)=>({u_input:new t.bf(e,i.u_input),u_output_expected:new t.bf(e,i.u_output_expected)}),atmosphere:(e,i)=>({u_sun_pos:new t.bS(e,i.u_sun_pos),u_atmosphere_blend:new t.bf(e,i.u_atmosphere_blend),u_globe_position:new t.bS(e,i.u_globe_position),u_globe_radius:new t.bf(e,i.u_globe_radius),u_inv_proj_matrix:new t.bQ(e,i.u_inv_proj_matrix)}),sky:(e,i)=>({u_sky_color:new t.bP(e,i.u_sky_color),u_horizon_color:new t.bP(e,i.u_horizon_color),u_horizon:new t.bT(e,i.u_horizon),u_horizon_normal:new t.bT(e,i.u_horizon_normal),u_sky_horizon_blend:new t.bf(e,i.u_sky_horizon_blend),u_sky_blend:new t.bf(e,i.u_sky_blend)})};class so{constructor(e,t,i){this.context=e;const o=e.gl;this.buffer=o.createBuffer(),this.dynamicDraw=Boolean(i),this.context.unbindVAO(),e.bindElementBuffer.set(this.buffer),o.bufferData(o.ELEMENT_ARRAY_BUFFER,t.arrayBuffer,this.dynamicDraw?o.DYNAMIC_DRAW:o.STATIC_DRAW),this.dynamicDraw||delete t.arrayBuffer;}bind(){this.context.bindElementBuffer.set(this.buffer);}updateData(e){const t=this.context.gl;if(!this.dynamicDraw)throw new Error(\"Attempted to update data while not in dynamic mode.\");this.context.unbindVAO(),this.bind(),t.bufferSubData(t.ELEMENT_ARRAY_BUFFER,0,e.arrayBuffer);}destroy(){this.buffer&&(this.context.gl.deleteBuffer(this.buffer),delete this.buffer);}}const no={Int8:\"BYTE\",Uint8:\"UNSIGNED_BYTE\",Int16:\"SHORT\",Uint16:\"UNSIGNED_SHORT\",Int32:\"INT\",Uint32:\"UNSIGNED_INT\",Float32:\"FLOAT\"};class lo{constructor(e,t,i,o){this.length=t.length,this.attributes=i,this.itemSize=t.bytesPerElement,this.dynamicDraw=o,this.context=e;const r=e.gl;this.buffer=r.createBuffer(),e.bindVertexBuffer.set(this.buffer),r.bufferData(r.ARRAY_BUFFER,t.arrayBuffer,this.dynamicDraw?r.DYNAMIC_DRAW:r.STATIC_DRAW),this.dynamicDraw||delete t.arrayBuffer;}bind(){this.context.bindVertexBuffer.set(this.buffer);}updateData(e){if(e.length!==this.length)throw new Error(`Length of new data is ${e.length}, which doesn't match current length of ${this.length}`);const t=this.context.gl;this.bind(),t.bufferSubData(t.ARRAY_BUFFER,0,e.arrayBuffer);}enableAttributes(e,t){for(let i=0;i<this.attributes.length;i++){const o=t.attributes[this.attributes[i].name];void 0!==o&&e.enableVertexAttribArray(o);}}setVertexAttribPointers(e,t,i){for(let o=0;o<this.attributes.length;o++){const r=this.attributes[o],a=t.attributes[r.name];void 0!==a&&e.vertexAttribPointer(a,r.components,e[no[r.type]],!1,this.itemSize,r.offset+this.itemSize*(i||0));}}destroy(){this.buffer&&(this.context.gl.deleteBuffer(this.buffer),delete this.buffer);}}class co{constructor(e){this.gl=e.gl,this.default=this.getDefault(),this.current=this.default,this.dirty=!1;}get(){return this.current}set(e){}getDefault(){return this.default}setDefault(){this.set(this.default);}}class ho extends co{getDefault(){return t.be.transparent}set(e){const t=this.current;(e.r!==t.r||e.g!==t.g||e.b!==t.b||e.a!==t.a||this.dirty)&&(this.gl.clearColor(e.r,e.g,e.b,e.a),this.current=e,this.dirty=!1);}}class uo extends co{getDefault(){return 1}set(e){(e!==this.current||this.dirty)&&(this.gl.clearDepth(e),this.current=e,this.dirty=!1);}}class _o extends co{getDefault(){return 0}set(e){(e!==this.current||this.dirty)&&(this.gl.clearStencil(e),this.current=e,this.dirty=!1);}}class po extends co{getDefault(){return [!0,!0,!0,!0]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||e[3]!==t[3]||this.dirty)&&(this.gl.colorMask(e[0],e[1],e[2],e[3]),this.current=e,this.dirty=!1);}}class mo extends co{getDefault(){return !0}set(e){(e!==this.current||this.dirty)&&(this.gl.depthMask(e),this.current=e,this.dirty=!1);}}class fo extends co{getDefault(){return 255}set(e){(e!==this.current||this.dirty)&&(this.gl.stencilMask(e),this.current=e,this.dirty=!1);}}class go extends co{getDefault(){return {func:this.gl.ALWAYS,ref:0,mask:255}}set(e){const t=this.current;(e.func!==t.func||e.ref!==t.ref||e.mask!==t.mask||this.dirty)&&(this.gl.stencilFunc(e.func,e.ref,e.mask),this.current=e,this.dirty=!1);}}class vo extends co{getDefault(){const e=this.gl;return [e.KEEP,e.KEEP,e.KEEP]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||this.dirty)&&(this.gl.stencilOp(e[0],e[1],e[2]),this.current=e,this.dirty=!1);}}class bo extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.STENCIL_TEST):t.disable(t.STENCIL_TEST),this.current=e,this.dirty=!1;}}class xo extends co{getDefault(){return [0,1]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||this.dirty)&&(this.gl.depthRange(e[0],e[1]),this.current=e,this.dirty=!1);}}class yo extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.DEPTH_TEST):t.disable(t.DEPTH_TEST),this.current=e,this.dirty=!1;}}class wo extends co{getDefault(){return this.gl.LESS}set(e){(e!==this.current||this.dirty)&&(this.gl.depthFunc(e),this.current=e,this.dirty=!1);}}class To extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.BLEND):t.disable(t.BLEND),this.current=e,this.dirty=!1;}}class Po extends co{getDefault(){const e=this.gl;return [e.ONE,e.ZERO]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||this.dirty)&&(this.gl.blendFunc(e[0],e[1]),this.current=e,this.dirty=!1);}}class Co extends co{getDefault(){return t.be.transparent}set(e){const t=this.current;(e.r!==t.r||e.g!==t.g||e.b!==t.b||e.a!==t.a||this.dirty)&&(this.gl.blendColor(e.r,e.g,e.b,e.a),this.current=e,this.dirty=!1);}}class Io extends co{getDefault(){return this.gl.FUNC_ADD}set(e){(e!==this.current||this.dirty)&&(this.gl.blendEquation(e),this.current=e,this.dirty=!1);}}class Mo extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.CULL_FACE):t.disable(t.CULL_FACE),this.current=e,this.dirty=!1;}}class So extends co{getDefault(){return this.gl.BACK}set(e){(e!==this.current||this.dirty)&&(this.gl.cullFace(e),this.current=e,this.dirty=!1);}}class Eo extends co{getDefault(){return this.gl.CCW}set(e){(e!==this.current||this.dirty)&&(this.gl.frontFace(e),this.current=e,this.dirty=!1);}}class Ro extends co{getDefault(){return null}set(e){(e!==this.current||this.dirty)&&(this.gl.useProgram(e),this.current=e,this.dirty=!1);}}class zo extends co{getDefault(){return this.gl.TEXTURE0}set(e){(e!==this.current||this.dirty)&&(this.gl.activeTexture(e),this.current=e,this.dirty=!1);}}class Do extends co{getDefault(){const e=this.gl;return [0,0,e.drawingBufferWidth,e.drawingBufferHeight]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||e[3]!==t[3]||this.dirty)&&(this.gl.viewport(e[0],e[1],e[2],e[3]),this.current=e,this.dirty=!1);}}class Ao extends co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindFramebuffer(t.FRAMEBUFFER,e),this.current=e,this.dirty=!1;}}class Lo extends co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindRenderbuffer(t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}class ko extends co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindTexture(t.TEXTURE_2D,e),this.current=e,this.dirty=!1;}}class Fo extends co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindBuffer(t.ARRAY_BUFFER,e),this.current=e,this.dirty=!1;}}class Bo extends co{getDefault(){return null}set(e){const t=this.gl;t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,e),this.current=e,this.dirty=!1;}}class Oo extends co{getDefault(){return null}set(e){var t;if(e===this.current&&!this.dirty)return;const i=this.gl;qt(i)?i.bindVertexArray(e):null===(t=i.getExtension(\"OES_vertex_array_object\"))||void 0===t||t.bindVertexArrayOES(e),this.current=e,this.dirty=!1;}}class jo extends co{getDefault(){return 4}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_ALIGNMENT,e),this.current=e,this.dirty=!1;}}class No extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,e),this.current=e,this.dirty=!1;}}class Zo extends co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,e),this.current=e,this.dirty=!1;}}class Uo extends co{constructor(e,t){super(e),this.context=e,this.parent=t;}getDefault(){return null}}class Go extends Uo{setDirty(){this.dirty=!0;}set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,e,0),this.current=e,this.dirty=!1;}}class Vo extends Uo{set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferRenderbuffer(t.FRAMEBUFFER,t.DEPTH_ATTACHMENT,t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}class $o extends Uo{set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferRenderbuffer(t.FRAMEBUFFER,t.DEPTH_STENCIL_ATTACHMENT,t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}const qo=\"Framebuffer is not complete\";class Wo{constructor(e,t,i,o,r){this.context=e,this.width=t,this.height=i;const a=e.gl,s=this.framebuffer=a.createFramebuffer();if(this.colorAttachment=new Go(e,s),o)this.depthAttachment=r?new $o(e,s):new Vo(e,s);else if(r)throw new Error(\"Stencil cannot be set without depth\");if(a.checkFramebufferStatus(a.FRAMEBUFFER)!==a.FRAMEBUFFER_COMPLETE)throw new Error(qo)}destroy(){const e=this.context.gl,t=this.colorAttachment.get();if(t&&e.deleteTexture(t),this.depthAttachment){const t=this.depthAttachment.get();t&&e.deleteRenderbuffer(t);}e.deleteFramebuffer(this.framebuffer);}}class Ho{constructor(e){var t,i;if(this.gl=e,this.clearColor=new ho(this),this.clearDepth=new uo(this),this.clearStencil=new _o(this),this.colorMask=new po(this),this.depthMask=new mo(this),this.stencilMask=new fo(this),this.stencilFunc=new go(this),this.stencilOp=new vo(this),this.stencilTest=new bo(this),this.depthRange=new xo(this),this.depthTest=new yo(this),this.depthFunc=new wo(this),this.blend=new To(this),this.blendFunc=new Po(this),this.blendColor=new Co(this),this.blendEquation=new Io(this),this.cullFace=new Mo(this),this.cullFaceSide=new So(this),this.frontFace=new Eo(this),this.program=new Ro(this),this.activeTexture=new zo(this),this.viewport=new Do(this),this.bindFramebuffer=new Ao(this),this.bindRenderbuffer=new Lo(this),this.bindTexture=new ko(this),this.bindVertexBuffer=new Fo(this),this.bindElementBuffer=new Bo(this),this.bindVertexArray=new Oo(this),this.pixelStoreUnpack=new jo(this),this.pixelStoreUnpackPremultiplyAlpha=new No(this),this.pixelStoreUnpackFlipY=new Zo(this),this.extTextureFilterAnisotropic=e.getExtension(\"EXT_texture_filter_anisotropic\")||e.getExtension(\"MOZ_EXT_texture_filter_anisotropic\")||e.getExtension(\"WEBKIT_EXT_texture_filter_anisotropic\"),this.extTextureFilterAnisotropic&&(this.extTextureFilterAnisotropicMax=e.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)),this.maxTextureSize=e.getParameter(e.MAX_TEXTURE_SIZE),qt(e)){this.HALF_FLOAT=e.HALF_FLOAT;const o=e.getExtension(\"EXT_color_buffer_half_float\");this.RGBA16F=null!==(t=e.RGBA16F)&&void 0!==t?t:null==o?void 0:o.RGBA16F_EXT,this.RGB16F=null!==(i=e.RGB16F)&&void 0!==i?i:null==o?void 0:o.RGB16F_EXT,e.getExtension(\"EXT_color_buffer_float\");}else {e.getExtension(\"EXT_color_buffer_half_float\"),e.getExtension(\"OES_texture_half_float_linear\");const t=e.getExtension(\"OES_texture_half_float\");this.HALF_FLOAT=null==t?void 0:t.HALF_FLOAT_OES;}}setDefault(){this.unbindVAO(),this.clearColor.setDefault(),this.clearDepth.setDefault(),this.clearStencil.setDefault(),this.colorMask.setDefault(),this.depthMask.setDefault(),this.stencilMask.setDefault(),this.stencilFunc.setDefault(),this.stencilOp.setDefault(),this.stencilTest.setDefault(),this.depthRange.setDefault(),this.depthTest.setDefault(),this.depthFunc.setDefault(),this.blend.setDefault(),this.blendFunc.setDefault(),this.blendColor.setDefault(),this.blendEquation.setDefault(),this.cullFace.setDefault(),this.cullFaceSide.setDefault(),this.frontFace.setDefault(),this.program.setDefault(),this.activeTexture.setDefault(),this.bindFramebuffer.setDefault(),this.pixelStoreUnpack.setDefault(),this.pixelStoreUnpackPremultiplyAlpha.setDefault(),this.pixelStoreUnpackFlipY.setDefault();}setDirty(){this.clearColor.dirty=!0,this.clearDepth.dirty=!0,this.clearStencil.dirty=!0,this.colorMask.dirty=!0,this.depthMask.dirty=!0,this.stencilMask.dirty=!0,this.stencilFunc.dirty=!0,this.stencilOp.dirty=!0,this.stencilTest.dirty=!0,this.depthRange.dirty=!0,this.depthTest.dirty=!0,this.depthFunc.dirty=!0,this.blend.dirty=!0,this.blendFunc.dirty=!0,this.blendColor.dirty=!0,this.blendEquation.dirty=!0,this.cullFace.dirty=!0,this.cullFaceSide.dirty=!0,this.frontFace.dirty=!0,this.program.dirty=!0,this.activeTexture.dirty=!0,this.viewport.dirty=!0,this.bindFramebuffer.dirty=!0,this.bindRenderbuffer.dirty=!0,this.bindTexture.dirty=!0,this.bindVertexBuffer.dirty=!0,this.bindElementBuffer.dirty=!0,this.bindVertexArray.dirty=!0,this.pixelStoreUnpack.dirty=!0,this.pixelStoreUnpackPremultiplyAlpha.dirty=!0,this.pixelStoreUnpackFlipY.dirty=!0;}createIndexBuffer(e,t){return new so(this,e,t)}createVertexBuffer(e,t,i){return new lo(this,e,t,i)}createRenderbuffer(e,t,i){const o=this.gl,r=o.createRenderbuffer();return this.bindRenderbuffer.set(r),o.renderbufferStorage(o.RENDERBUFFER,e,t,i),this.bindRenderbuffer.set(null),r}createFramebuffer(e,t,i,o){return new Wo(this,e,t,i,o)}clear({color:e,depth:t,stencil:i}){const o=this.gl;let r=0;e&&(r|=o.COLOR_BUFFER_BIT,this.clearColor.set(e),this.colorMask.set([!0,!0,!0,!0])),void 0!==t&&(r|=o.DEPTH_BUFFER_BIT,this.depthRange.set([0,1]),this.clearDepth.set(t),this.depthMask.set(!0)),void 0!==i&&(r|=o.STENCIL_BUFFER_BIT,this.clearStencil.set(i),this.stencilMask.set(255)),o.clear(r);}setCullFace(e){!1===e.enable?this.cullFace.set(!1):(this.cullFace.set(!0),this.cullFaceSide.set(e.mode),this.frontFace.set(e.frontFace));}setDepthMode(e){e.func!==this.gl.ALWAYS||e.mask?(this.depthTest.set(!0),this.depthFunc.set(e.func),this.depthMask.set(e.mask),this.depthRange.set(e.range)):this.depthTest.set(!1);}setStencilMode(e){e.test.func!==this.gl.ALWAYS||e.mask?(this.stencilTest.set(!0),this.stencilMask.set(e.mask),this.stencilOp.set([e.fail,e.depthFail,e.pass]),this.stencilFunc.set({func:e.test.func,ref:e.ref,mask:e.test.mask})):this.stencilTest.set(!1);}setColorMode(e){t.bG(e.blendFunction,jt.Replace)?this.blend.set(!1):(this.blend.set(!0),this.blendFunc.set(e.blendFunction),this.blendColor.set(e.blendColor)),this.colorMask.set(e.mask);}createVertexArray(){var e;return qt(this.gl)?this.gl.createVertexArray():null===(e=this.gl.getExtension(\"OES_vertex_array_object\"))||void 0===e?void 0:e.createVertexArrayOES()}deleteVertexArray(e){var t;return qt(this.gl)?this.gl.deleteVertexArray(e):null===(t=this.gl.getExtension(\"OES_vertex_array_object\"))||void 0===t?void 0:t.deleteVertexArrayOES(e)}unbindVAO(){this.bindVertexArray.set(null);}}let Xo;function Ko(e,i,o,r,a){const s=e.context,n=e.transform,l=s.gl,c=e.useProgram(\"collisionBox\"),h=[];let u=0,d=0;for(let t=0;t<r.length;t++){const _=r[t],p=i.getTile(_).getBucket(o);if(!p)continue;const m=a?p.textCollisionBox:p.iconCollisionBox,f=p.collisionCircleArray;f.length>0&&(h.push({circleArray:f,circleOffset:d,coord:_}),u+=f.length/4,d=u),m&&c.draw(s,l.LINES,Ut.disabled,Vt.disabled,e.colorModeForRenderPass(),Zt.disabled,Fi(e.transform),e.style.map.terrain&&e.style.map.terrain.getTerrainData(_),n.getProjectionData({overscaledTileID:_,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),o.id,m.layoutVertexBuffer,m.indexBuffer,m.segments,null,e.transform.zoom,null,null,m.collisionVertexBuffer);}if(!a||!h.length)return;const _=e.useProgram(\"collisionCircle\"),p=new t.b_;p.resize(4*u),p._trim();let m=0;for(const e of h)for(let t=0;t<e.circleArray.length/4;t++){const i=4*t,o=e.circleArray[i+0],r=e.circleArray[i+1],a=e.circleArray[i+2],s=e.circleArray[i+3];p.emplace(m++,o,r,a,s,0),p.emplace(m++,o,r,a,s,1),p.emplace(m++,o,r,a,s,2),p.emplace(m++,o,r,a,s,3);}(!Xo||Xo.length<2*u)&&(Xo=function(e){const i=2*e,o=new t.c0;o.resize(i),o._trim();for(let e=0;e<i;e++){const t=6*e;o.uint16[t+0]=4*e+0,o.uint16[t+1]=4*e+1,o.uint16[t+2]=4*e+2,o.uint16[t+3]=4*e+2,o.uint16[t+4]=4*e+3,o.uint16[t+5]=4*e+0;}return o}(u));const f=s.createIndexBuffer(Xo,!0),g=s.createVertexBuffer(p,t.b$.members,!0);for(const i of h){const r=Bi(e.transform);_.draw(s,l.TRIANGLES,Ut.disabled,Vt.disabled,e.colorModeForRenderPass(),Zt.disabled,r,e.style.map.terrain&&e.style.map.terrain.getTerrainData(i.coord),null,o.id,g,f,t.aL.simpleSegment(0,2*i.circleOffset,i.circleArray.length,i.circleArray.length/2),null,e.transform.zoom,null,null,null);}g.destroy(),f.destroy();}const Yo=t.af(new Float32Array(16));function Qo(e,i,o,r,a,s){const{horizontalAlign:n,verticalAlign:l}=t.aG(e);return new t.P((-(n-.5)*i/a+r[0])*s,(-(l-.5)*o/a+r[1])*s)}function Jo(e,i,o,r,a,s){const n=i.tileAnchorPoint.add(new t.P(i.translation[0],i.translation[1]));if(i.pitchWithMap){let e=r.mult(s);o||(e=e.rotate(-a));const t=n.add(e);return Ee(t.x,t.y,i.pitchedLabelPlaneMatrix,i.getElevation).point}if(o){const t=Oe(i.tileAnchorPoint.x+1,i.tileAnchorPoint.y,i).point.sub(e),o=Math.atan(t.y/t.x)+(t.x<0?Math.PI:0);return e.add(r.rotate(o))}return e.add(r)}function er(e,i,o,r,a,s,n,l,c,h,u,d){const _=e.text.placedSymbolArray,p=e.text.dynamicLayoutVertexArray,m=e.icon.dynamicLayoutVertexArray,f={};p.clear();for(let m=0;m<_.length;m++){const g=_.get(m),v=g.hidden||!g.crossTileID||e.allowVerticalPlacement&&!g.placedOrientation?null:r[g.crossTileID];if(v){const r=new t.P(g.anchorX,g.anchorY),_={getElevation:d,width:a.width,height:a.height,pitchedLabelPlaneMatrix:s,pitchWithMap:o,transform:a,tileAnchorPoint:r,translation:h,unwrappedTileID:u},m=o?Ne(r.x,r.y,_):Oe(r.x,r.y,_),b=Re(a.cameraToCenterDistance,m.signedDistanceFromCamera);let x=t.ao(e.textSizeData,l,g)*b/t.aA;o&&(x*=e.tilePixelRatio/n);const{width:y,height:w,anchor:T,textOffset:P,textBoxScale:C}=v,I=Qo(T,y,w,P,C,x),M=a.getPitchedTextCorrection(r.x+h[0],r.y+h[1],u),S=Jo(m.point,_,i,I,-a.bearingInRadians,M),E=e.allowVerticalPlacement&&g.placedOrientation===t.an.vertical?Math.PI/2:0;for(let e=0;e<g.numGlyphs;e++)t.au(p,S,E);c&&g.associatedIconIndex>=0&&(f[g.associatedIconIndex]={shiftedAnchor:S,angle:E});}else $e(g.numGlyphs,p);}if(c){m.clear();const i=e.icon.placedSymbolArray;for(let e=0;e<i.length;e++){const o=i.get(e);if(o.hidden)$e(o.numGlyphs,m);else {const i=f[e];if(i)for(let e=0;e<o.numGlyphs;e++)t.au(m,i.shiftedAnchor,i.angle);else $e(o.numGlyphs,m);}}e.icon.dynamicLayoutVertexBuffer.updateData(m);}e.text.dynamicLayoutVertexBuffer.updateData(p);}function tr(e,t,i){return i.iconsInText&&t?\"symbolTextAndIcon\":e?\"symbolSDF\":\"symbolIcon\"}function ir(e,i,o,r,a,s,n,l,c,h,u,d,_){const p=e.context,m=p.gl,f=e.transform,g=\"map\"===l,v=\"map\"===c,b=\"viewport\"!==l&&\"point\"!==o.layout.get(\"symbol-placement\"),x=g&&!v&&!b,y=!o.layout.get(\"symbol-sort-key\").isConstant();let w=!1;const T=e.getDepthModeForSublayer(0,Ut.ReadOnly),P=o._unevaluatedLayout.hasValue(\"text-variable-anchor\")||o._unevaluatedLayout.hasValue(\"text-variable-anchor-offset\"),C=[],I=f.getCircleRadiusCorrection();for(const l of r){const r=i.getTile(l),c=r.getBucket(o);if(!c)continue;const u=a?c.text:c.icon;if(!u||!u.segments.get().length||!u.hasVisibleVertices)continue;const d=u.programConfigurations.get(o.id),p=a||c.sdfIcons,T=a?c.textSizeData:c.iconSizeData,M=v||0!==f.pitch,S=e.useProgram(tr(p,a,c),d),E=t.am(T,f.zoom),R=e.style.map.terrain&&e.style.map.terrain.getTerrainData(l);let z,D,A,L,k=[0,0],F=null;if(a)D=r.glyphAtlasTexture,A=m.LINEAR,z=r.glyphAtlasTexture.size,c.iconsInText&&(k=r.imageAtlasTexture.size,F=r.imageAtlasTexture,L=M||e.options.rotating||e.options.zooming||\"composite\"===T.kind||\"camera\"===T.kind?m.LINEAR:m.NEAREST);else {const t=1!==o.layout.get(\"icon-size\").constantOr(0)||c.iconsNeedLinear;D=r.imageAtlasTexture,A=p||e.options.rotating||e.options.zooming||t||M?m.LINEAR:m.NEAREST,z=r.imageAtlasTexture.size;}const B=t.aB(r,1,e.transform.zoom),O=Ie(g,e.transform,B),j=t.L();t.ap(j,O);const N=Me(v,g,e.transform,B),Z=t.aC(f,r,s,n),U=f.getProjectionData({overscaledTileID:l,applyGlobeMatrix:!_,applyTerrainMatrix:!0}),G=P&&c.hasTextData(),V=\"none\"!==o.layout.get(\"icon-text-fit\")&&G&&c.hasIconData();if(b){const t=e.style.map.terrain?(t,i)=>e.style.map.terrain.getElevation(l,t,i):null,i=\"map\"===o.layout.get(\"text-rotation-alignment\");De(c,e,a,O,j,v,h,i,l.toUnwrapped(),f.width,f.height,Z,t);}const $=a&&P||V,q=b||$?Yo:v?O:e.transform.clipSpaceToPixelsMatrix,W=p&&0!==o.paint.get(a?\"text-halo-width\":\"icon-halo-width\").constantOr(1);let H;H=p?c.iconsInText?to(T.kind,E,x,v,b,$,e,q,N,Z,z,k,I):eo(T.kind,E,x,v,b,$,e,q,N,Z,a,z,0,I):Ji(T.kind,E,x,v,b,$,e,q,N,Z,a,z,I);const X={program:S,buffers:u,uniformValues:H,projectionData:U,atlasTexture:D,atlasTextureIcon:F,atlasInterpolation:A,atlasInterpolationIcon:L,isSDF:p,hasHalo:W};if(y&&c.canOverlap){w=!0;const e=u.segments.get();for(const i of e)C.push({segments:new t.aL([i]),sortKey:i.sortKey,state:X,terrainData:R});}else C.push({segments:u.segments,sortKey:0,state:X,terrainData:R});}w&&C.sort(((e,t)=>e.sortKey-t.sortKey));for(const t of C){const i=t.state;if(p.activeTexture.set(m.TEXTURE0),i.atlasTexture.bind(i.atlasInterpolation,m.CLAMP_TO_EDGE),i.atlasTextureIcon&&(p.activeTexture.set(m.TEXTURE1),i.atlasTextureIcon&&i.atlasTextureIcon.bind(i.atlasInterpolationIcon,m.CLAMP_TO_EDGE)),i.isSDF){const r=i.uniformValues;i.hasHalo&&(r.u_is_halo=1,or(i.buffers,t.segments,o,e,i.program,T,u,d,r,i.projectionData,t.terrainData)),r.u_is_halo=0;}or(i.buffers,t.segments,o,e,i.program,T,u,d,i.uniformValues,i.projectionData,t.terrainData);}}function or(e,t,i,o,r,a,s,n,l,c,h){const u=o.context;r.draw(u,u.gl.TRIANGLES,a,s,n,Zt.backCCW,l,h,c,i.id,e.layoutVertexBuffer,e.indexBuffer,t,i.paint,o.transform.zoom,e.programConfigurations.get(i.id),e.dynamicLayoutVertexBuffer,e.opacityVertexBuffer);}function rr(e,i,o,r,a){const s=e.context,n=s.gl,l=Vt.disabled,c=new jt([n.ONE,n.ONE],t.be.transparent,[!0,!0,!0,!0]),h=i.getBucket(o);if(!h)return;const u=r.key;let d=o.heatmapFbos.get(u);d||(d=sr(s,i.tileSize,i.tileSize),o.heatmapFbos.set(u,d)),s.bindFramebuffer.set(d.framebuffer),s.viewport.set([0,0,i.tileSize,i.tileSize]),s.clear({color:t.be.transparent});const _=h.programConfigurations.get(o.id),p=e.useProgram(\"heatmap\",_,!a),m=e.transform.getProjectionData({overscaledTileID:i.tileID,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),f=e.style.map.terrain.getTerrainData(r);p.draw(s,n.TRIANGLES,Ut.disabled,l,c,Zt.disabled,ji(i,e.transform.zoom,o.paint.get(\"heatmap-intensity\"),1),f,m,o.id,h.layoutVertexBuffer,h.indexBuffer,h.segments,o.paint,e.transform.zoom,_);}function ar(e,t,i,o,r){const a=e.context,s=a.gl,n=e.transform;a.setColorMode(e.colorModeForRenderPass());const l=nr(a,t),c=i.key,h=t.heatmapFbos.get(c);if(!h)return;a.activeTexture.set(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,h.colorAttachment.get()),a.activeTexture.set(s.TEXTURE1),l.bind(s.LINEAR,s.CLAMP_TO_EDGE);const u=n.getProjectionData({overscaledTileID:i,applyTerrainMatrix:r,applyGlobeMatrix:!o});e.useProgram(\"heatmapTexture\").draw(a,s.TRIANGLES,Ut.disabled,Vt.disabled,e.colorModeForRenderPass(),Zt.disabled,Ni(e,t,0,1),null,u,t.id,e.rasterBoundsBuffer,e.quadTriangleIndexBuffer,e.rasterBoundsSegments,t.paint,n.zoom),h.destroy(),t.heatmapFbos.delete(c);}function sr(e,t,i){var o,r;const a=e.gl,s=a.createTexture();a.bindTexture(a.TEXTURE_2D,s),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);const n=null!==(o=e.HALF_FLOAT)&&void 0!==o?o:a.UNSIGNED_BYTE,l=null!==(r=e.RGBA16F)&&void 0!==r?r:a.RGBA;a.texImage2D(a.TEXTURE_2D,0,l,t,i,0,a.RGBA,n,null);const c=e.createFramebuffer(t,i,!1,!1);return c.colorAttachment.set(s),c}function nr(e,i){return i.colorRampTexture||(i.colorRampTexture=new t.T(e,i.colorRamp,e.gl.RGBA)),i.colorRampTexture}function lr(e,t,i,o,r){if(!i||!o||!o.imageAtlas)return;const a=o.imageAtlas.patternPositions;let s=a[i.to.toString()],n=a[i.from.toString()];if(!s&&n&&(s=n),!n&&s&&(n=s),!s||!n){const e=r.getPaintProperty(t);s=a[e],n=a[e];}s&&n&&e.setConstantPatternPositions(s,n);}function cr(e,i,o,r,a,s,n,l){const c=e.context.gl,h=\"fill-pattern\",u=o.paint.get(h),d=u&&u.constantOr(1),_=o.getCrossfadeParameters();let p,m,f,g,v;const b=e.transform,x=o.paint.get(\"fill-translate\"),y=o.paint.get(\"fill-translate-anchor\");n?(m=d&&!o.getPaintProperty(\"fill-outline-color\")?\"fillOutlinePattern\":\"fillOutline\",p=c.LINES):(m=d?\"fillPattern\":\"fill\",p=c.TRIANGLES);const w=u.constantOr(null);for(const u of r){const r=i.getTile(u);if(d&&!r.patternsLoaded())continue;const T=r.getBucket(o);if(!T)continue;const P=T.programConfigurations.get(o.id),C=e.useProgram(m,P),I=e.style.map.terrain&&e.style.map.terrain.getTerrainData(u);d&&(e.context.activeTexture.set(c.TEXTURE0),r.imageAtlasTexture.bind(c.LINEAR,c.CLAMP_TO_EDGE),P.updatePaintBuffers(_)),lr(P,h,w,r,o);const M=b.getProjectionData({overscaledTileID:u,applyGlobeMatrix:!l,applyTerrainMatrix:!0}),S=t.aC(b,r,x,y);if(n){g=T.indexBuffer2,v=T.segments2;const t=[c.drawingBufferWidth,c.drawingBufferHeight];f=\"fillOutlinePattern\"===m&&d?Li(e,_,r,t,S):Ai(t,S);}else g=T.indexBuffer,v=T.segments,f=d?Di(e,_,r,S):{u_fill_translate:S};const E=e.stencilModeForClipping(u);C.draw(e.context,p,a,E,s,Zt.backCCW,f,I,M,o.id,T.layoutVertexBuffer,g,v,o.paint,e.transform.zoom,P);}}function hr(e,i,o,r,a,s,n,l){const c=e.context,h=c.gl,u=\"fill-extrusion-pattern\",d=o.paint.get(u),_=d.constantOr(1),p=o.getCrossfadeParameters(),m=o.paint.get(\"fill-extrusion-opacity\"),f=d.constantOr(null),g=e.transform;for(const d of r){const r=i.getTile(d),v=r.getBucket(o);if(!v)continue;const b=e.style.map.terrain&&e.style.map.terrain.getTerrainData(d),x=v.programConfigurations.get(o.id),y=e.useProgram(_?\"fillExtrusionPattern\":\"fillExtrusion\",x);_&&(e.context.activeTexture.set(h.TEXTURE0),r.imageAtlasTexture.bind(h.LINEAR,h.CLAMP_TO_EDGE),x.updatePaintBuffers(p));const w=g.getProjectionData({overscaledTileID:d,applyGlobeMatrix:!l,applyTerrainMatrix:!0});lr(x,u,f,r,o);const T=t.aC(g,r,o.paint.get(\"fill-extrusion-translate\"),o.paint.get(\"fill-extrusion-translate-anchor\")),P=o.paint.get(\"fill-extrusion-vertical-gradient\"),C=_?zi(e,P,m,T,d,p,r):Ri(e,P,m,T);y.draw(c,c.gl.TRIANGLES,a,s,n,Zt.backCCW,C,b,w,o.id,v.layoutVertexBuffer,v.indexBuffer,v.segments,o.paint,e.transform.zoom,x,e.style.map.terrain&&v.centroidVertexBuffer);}}function ur(e,t,i,o,r,a,s,n,l){var c;const h=e.style.projection,u=e.context,d=e.transform,_=u.gl,p=[`#define NUM_ILLUMINATION_SOURCES ${i.paint.get(\"hillshade-highlight-color\").values.length}`],m=e.useProgram(\"hillshade\",null,!1,p),f=!e.options.moving;for(const p of o){const o=t.getTile(p),g=o.fbo;if(!g)continue;const v=h.getMeshFromTileID(u,p.canonical,n,!0,\"raster\"),b=null===(c=e.style.map.terrain)||void 0===c?void 0:c.getTerrainData(p);u.activeTexture.set(_.TEXTURE0),_.bindTexture(_.TEXTURE_2D,g.colorAttachment.get());const x=d.getProjectionData({overscaledTileID:p,aligned:f,applyGlobeMatrix:!l,applyTerrainMatrix:!0});m.draw(u,_.TRIANGLES,a,r[p.overscaledZ],s,Zt.backCCW,Zi(e,o,i),b,x,i.id,v.vertexBuffer,v.indexBuffer,v.segments);}}function dr(e,i,o,r,a,s,n,l,c){var h;const u=e.style.projection,d=e.context,_=e.transform,p=d.gl,m=e.useProgram(\"colorRelief\"),f=!e.options.moving;let g=!0;for(const v of r){const r=i.getTile(v),b=r.dem;if(g){const e=p.getParameter(p.MAX_TEXTURE_SIZE),{elevationTexture:t,colorTexture:i}=o.getColorRampTextures(d,e,b.getUnpackVector());d.activeTexture.set(p.TEXTURE1),t.bind(p.NEAREST,p.CLAMP_TO_EDGE),d.activeTexture.set(p.TEXTURE4),i.bind(p.LINEAR,p.CLAMP_TO_EDGE),g=!1;}if(!b||!b.data)continue;const x=b.stride,y=b.getPixels();if(d.activeTexture.set(p.TEXTURE0),d.pixelStoreUnpackPremultiplyAlpha.set(!1),r.demTexture=r.demTexture||e.getTileTexture(x),r.demTexture){const e=r.demTexture;e.update(y,{premultiply:!1}),e.bind(p.LINEAR,p.CLAMP_TO_EDGE);}else r.demTexture=new t.T(d,y,p.RGBA,{premultiply:!1}),r.demTexture.bind(p.LINEAR,p.CLAMP_TO_EDGE);const w=u.getMeshFromTileID(d,v.canonical,l,!0,\"raster\"),T=null===(h=e.style.map.terrain)||void 0===h?void 0:h.getTerrainData(v),P=_.getProjectionData({overscaledTileID:v,aligned:f,applyGlobeMatrix:!c,applyTerrainMatrix:!0});m.draw(d,p.TRIANGLES,s,a[v.overscaledZ],n,Zt.backCCW,Vi(o,r.dem),T,P,o.id,w.vertexBuffer,w.indexBuffer,w.segments);}}const _r=[new t.P(0,0),new t.P(t.$,0),new t.P(t.$,t.$),new t.P(0,t.$)];function pr(e,t,i,o,r,a,s,n,l=!1,c=!1){const h=o[o.length-1].overscaledZ,u=e.context,d=u.gl,_=e.useProgram(\"raster\"),p=e.transform,m=e.style.projection,f=e.colorModeForRenderPass(),g=!e.options.moving;for(const v of o){const o=e.getDepthModeForSublayer(v.overscaledZ-h,1===i.paint.get(\"raster-opacity\")?Ut.ReadWrite:Ut.ReadOnly,d.LESS),b=t.getTile(v);b.registerFadeDuration(i.paint.get(\"raster-fade-duration\"));const x=t.findLoadedParent(v,0),y=t.findLoadedSibling(v),w=mr(b,x||y||null,t,i,e.transform,e.style.map.terrain);let T,P;const C=\"nearest\"===i.paint.get(\"raster-resampling\")?d.NEAREST:d.LINEAR;u.activeTexture.set(d.TEXTURE0),b.texture.bind(C,d.CLAMP_TO_EDGE,d.LINEAR_MIPMAP_NEAREST),u.activeTexture.set(d.TEXTURE1),x?(x.texture.bind(C,d.CLAMP_TO_EDGE,d.LINEAR_MIPMAP_NEAREST),T=Math.pow(2,x.tileID.overscaledZ-b.tileID.overscaledZ),P=[b.tileID.canonical.x*T%1,b.tileID.canonical.y*T%1]):b.texture.bind(C,d.CLAMP_TO_EDGE,d.LINEAR_MIPMAP_NEAREST),b.texture.useMipmap&&u.extTextureFilterAnisotropic&&e.transform.pitch>20&&d.texParameterf(d.TEXTURE_2D,u.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,u.extTextureFilterAnisotropicMax);const I=e.style.map.terrain&&e.style.map.terrain.getTerrainData(v),M=p.getProjectionData({overscaledTileID:v,aligned:g,applyGlobeMatrix:!c,applyTerrainMatrix:!0}),S=Yi(P||[0,0],T||1,w,i,n),E=m.getMeshFromTileID(u,v.canonical,a,s,\"raster\");_.draw(u,d.TRIANGLES,o,r?r[v.overscaledZ]:Vt.disabled,f,l?Zt.frontCCW:Zt.backCCW,S,I,M,i.id,E.vertexBuffer,E.indexBuffer,E.segments);}}function mr(e,i,o,r,a,n){const l=r.paint.get(\"raster-fade-duration\");if(!n&&l>0){const r=s.now(),n=(r-e.timeAdded)/l,c=i?(r-i.timeAdded)/l:-1,h=o.getSource(),u=ge(a,{tileSize:h.tileSize,roundZoom:h.roundZoom}),d=!i||Math.abs(i.tileID.overscaledZ-u)>Math.abs(e.tileID.overscaledZ-u),_=d&&e.refreshedUponExpiration?1:t.ag(d?n:1-c,0,1);return e.refreshedUponExpiration&&n>=1&&(e.refreshedUponExpiration=!1),i?{opacity:1,mix:1-_}:{opacity:_,mix:0}}return {opacity:1,mix:0}}const fr=new t.be(1,0,0,1),gr=new t.be(0,1,0,1),vr=new t.be(0,0,1,1),br=new t.be(1,0,1,1),xr=new t.be(0,1,1,1);function yr(e,t,i,o){Tr(e,0,t+i/2,e.transform.width,i,o);}function wr(e,t,i,o){Tr(e,t-i/2,0,i,e.transform.height,o);}function Tr(e,t,i,o,r,a){const s=e.context,n=s.gl;n.enable(n.SCISSOR_TEST),n.scissor(t*e.pixelRatio,i*e.pixelRatio,o*e.pixelRatio,r*e.pixelRatio),s.clear({color:a}),n.disable(n.SCISSOR_TEST);}function Pr(e,i,o){const r=e.context,a=r.gl,s=e.useProgram(\"debug\"),n=Ut.disabled,l=Vt.disabled,c=e.colorModeForRenderPass(),h=\"$debug\",u=e.style.map.terrain&&e.style.map.terrain.getTerrainData(o);r.activeTexture.set(a.TEXTURE0);const d=i.getTileByID(o.key).latestRawTileData,_=Math.floor((d&&d.byteLength||0)/1024),p=i.getTile(o).tileSize,m=512/Math.min(p,512)*(o.overscaledZ/e.transform.zoom)*.5;let f=o.canonical.toString();o.overscaledZ!==o.canonical.z&&(f+=` => ${o.overscaledZ}`),function(e,t){e.initDebugOverlayCanvas();const i=e.debugOverlayCanvas,o=e.context.gl,r=e.debugOverlayCanvas.getContext(\"2d\");r.clearRect(0,0,i.width,i.height),r.shadowColor=\"white\",r.shadowBlur=2,r.lineWidth=1.5,r.strokeStyle=\"white\",r.textBaseline=\"top\",r.font=\"bold 36px Open Sans, sans-serif\",r.fillText(t,5,5),r.strokeText(t,5,5),e.debugOverlayTexture.update(i),e.debugOverlayTexture.bind(o.LINEAR,o.CLAMP_TO_EDGE);}(e,`${f} ${_}kB`);const g=e.transform.getProjectionData({overscaledTileID:o,applyGlobeMatrix:!0,applyTerrainMatrix:!0});s.draw(r,a.TRIANGLES,n,l,jt.alphaBlended,Zt.disabled,Oi(t.be.transparent,m),null,g,h,e.debugBuffer,e.quadTriangleIndexBuffer,e.debugSegments),s.draw(r,a.LINE_STRIP,n,l,c,Zt.disabled,Oi(t.be.red),u,g,h,e.debugBuffer,e.tileBorderIndexBuffer,e.debugSegments);}function Cr(e,t,i,o){const{isRenderingGlobe:r}=o,a=e.context,s=a.gl,n=e.transform,l=e.colorModeForRenderPass(),c=e.getDepthModeFor3D(),h=e.useProgram(\"terrain\");a.bindFramebuffer.set(null),a.viewport.set([0,0,e.width,e.height]);for(const o of i){const i=t.getTerrainMesh(o.tileID),u=e.renderToTexture.getTexture(o),d=t.getTerrainData(o.tileID);a.activeTexture.set(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,u.texture);const _=t.getMeshFrameDelta(n.zoom),p=n.calculateFogMatrix(o.tileID.toUnwrapped()),m=Ci(_,p,e.style.sky,n.pitch,r),f=n.getProjectionData({overscaledTileID:o.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0});h.draw(a,s.TRIANGLES,c,Vt.disabled,l,Zt.backCCW,m,d,f,\"terrain\",i.vertexBuffer,i.indexBuffer,i.segments);}}function Ir(e,i){if(!i.mesh){const o=new t.aK;o.emplaceBack(-1,-1),o.emplaceBack(1,-1),o.emplaceBack(1,1),o.emplaceBack(-1,1);const r=new t.aM;r.emplaceBack(0,1,2),r.emplaceBack(0,2,3),i.mesh=new wt(e.createVertexBuffer(o,Tt.members),e.createIndexBuffer(r),t.aL.simpleSegment(0,0,o.length,r.length));}return i.mesh}class Mr{constructor(e,i){this.context=new Ho(e),this.transform=i,this._tileTextures={},this.terrainFacilitator={dirty:!0,matrix:t.af(new Float64Array(16)),renderTime:0},this.setup(),this.numSublayers=xe.maxUnderzooming+xe.maxOverzooming+1,this.depthEpsilon=1/Math.pow(2,16),this.crossTileSymbolIndex=new vt;}resize(e,t,i){if(this.width=Math.floor(e*i),this.height=Math.floor(t*i),this.pixelRatio=i,this.context.viewport.set([0,0,this.width,this.height]),this.style)for(const e of this.style._order)this.style._layers[e].resize();}setup(){const e=this.context,i=new t.aK;i.emplaceBack(0,0),i.emplaceBack(t.$,0),i.emplaceBack(0,t.$),i.emplaceBack(t.$,t.$),this.tileExtentBuffer=e.createVertexBuffer(i,Tt.members),this.tileExtentSegments=t.aL.simpleSegment(0,0,4,2);const o=new t.aK;o.emplaceBack(0,0),o.emplaceBack(t.$,0),o.emplaceBack(0,t.$),o.emplaceBack(t.$,t.$),this.debugBuffer=e.createVertexBuffer(o,Tt.members),this.debugSegments=t.aL.simpleSegment(0,0,4,5);const r=new t.c5;r.emplaceBack(0,0,0,0),r.emplaceBack(t.$,0,t.$,0),r.emplaceBack(0,t.$,0,t.$),r.emplaceBack(t.$,t.$,t.$,t.$),this.rasterBoundsBuffer=e.createVertexBuffer(r,Ti.members),this.rasterBoundsSegments=t.aL.simpleSegment(0,0,4,2);const a=new t.aK;a.emplaceBack(0,0),a.emplaceBack(t.$,0),a.emplaceBack(0,t.$),a.emplaceBack(t.$,t.$),this.rasterBoundsBufferPosOnly=e.createVertexBuffer(a,Tt.members),this.rasterBoundsSegmentsPosOnly=t.aL.simpleSegment(0,0,4,5);const s=new t.aK;s.emplaceBack(0,0),s.emplaceBack(1,0),s.emplaceBack(0,1),s.emplaceBack(1,1),this.viewportBuffer=e.createVertexBuffer(s,Tt.members),this.viewportSegments=t.aL.simpleSegment(0,0,4,2);const n=new t.c6;n.emplaceBack(0),n.emplaceBack(1),n.emplaceBack(3),n.emplaceBack(2),n.emplaceBack(0),this.tileBorderIndexBuffer=e.createIndexBuffer(n);const l=new t.aM;l.emplaceBack(1,0,2),l.emplaceBack(1,2,3),this.quadTriangleIndexBuffer=e.createIndexBuffer(l);const c=this.context.gl;this.stencilClearMode=new Vt({func:c.ALWAYS,mask:0},0,255,c.ZERO,c.ZERO,c.ZERO),this.tileExtentMesh=new wt(this.tileExtentBuffer,this.quadTriangleIndexBuffer,this.tileExtentSegments);}clearStencil(){const e=this.context,i=e.gl;this.nextStencilID=1,this.currentStencilSource=void 0;const o=t.L();t.bX(o,0,this.width,this.height,0,0,1),t.N(o,o,[i.drawingBufferWidth,i.drawingBufferHeight,0]);const r={mainMatrix:o,tileMercatorCoords:[0,0,1,1],clippingPlane:[0,0,0,0],projectionTransition:0,fallbackMatrix:o};this.useProgram(\"clippingMask\",null,!0).draw(e,i.TRIANGLES,Ut.disabled,this.stencilClearMode,jt.disabled,Zt.disabled,null,null,r,\"$clipping\",this.viewportBuffer,this.quadTriangleIndexBuffer,this.viewportSegments);}_renderTileClippingMasks(e,t,i){if(this.currentStencilSource===e.source||!e.isTileClipped()||!t||!t.length)return;this.currentStencilSource=e.source,this.nextStencilID+t.length>256&&this.clearStencil();const o=this.context;o.setColorMode(jt.disabled),o.setDepthMode(Ut.disabled);const r={};for(const e of t)r[e.key]=this.nextStencilID++;this._renderTileMasks(r,t,i,!0),this._renderTileMasks(r,t,i,!1),this._tileClippingMaskIDs=r;}_renderTileMasks(e,t,i,o){const r=this.context,a=r.gl,s=this.style.projection,n=this.transform,l=this.useProgram(\"clippingMask\");for(const c of t){const t=e[c.key],h=this.style.map.terrain&&this.style.map.terrain.getTerrainData(c),u=s.getMeshFromTileID(this.context,c.canonical,o,!0,\"stencil\"),d=n.getProjectionData({overscaledTileID:c,applyGlobeMatrix:!i,applyTerrainMatrix:!0});l.draw(r,a.TRIANGLES,Ut.disabled,new Vt({func:a.ALWAYS,mask:0},t,255,a.KEEP,a.KEEP,a.REPLACE),jt.disabled,i?Zt.disabled:Zt.backCCW,null,h,d,\"$clipping\",u.vertexBuffer,u.indexBuffer,u.segments);}}_renderTilesDepthBuffer(){const e=this.context,t=e.gl,i=this.style.projection,o=this.transform,r=this.useProgram(\"depth\"),a=this.getDepthModeFor3D(),s=ve(o,{tileSize:o.tileSize});for(const n of s){const s=this.style.map.terrain&&this.style.map.terrain.getTerrainData(n),l=i.getMeshFromTileID(this.context,n.canonical,!0,!0,\"raster\"),c=o.getProjectionData({overscaledTileID:n,applyGlobeMatrix:!0,applyTerrainMatrix:!0});r.draw(e,t.TRIANGLES,a,Vt.disabled,jt.disabled,Zt.backCCW,null,s,c,\"$clipping\",l.vertexBuffer,l.indexBuffer,l.segments);}}stencilModeFor3D(){this.currentStencilSource=void 0,this.nextStencilID+1>256&&this.clearStencil();const e=this.nextStencilID++,t=this.context.gl;return new Vt({func:t.NOTEQUAL,mask:255},e,255,t.KEEP,t.KEEP,t.REPLACE)}stencilModeForClipping(e){const t=this.context.gl;return new Vt({func:t.EQUAL,mask:255},this._tileClippingMaskIDs[e.key],0,t.KEEP,t.KEEP,t.REPLACE)}getStencilConfigForOverlapAndUpdateStencilID(e){const t=this.context.gl,i=e.sort(((e,t)=>t.overscaledZ-e.overscaledZ)),o=i[i.length-1].overscaledZ,r=i[0].overscaledZ-o+1;if(r>1){this.currentStencilSource=void 0,this.nextStencilID+r>256&&this.clearStencil();const e={};for(let i=0;i<r;i++)e[i+o]=new Vt({func:t.GEQUAL,mask:255},i+this.nextStencilID,255,t.KEEP,t.KEEP,t.REPLACE);return this.nextStencilID+=r,[e,i]}return [{[o]:Vt.disabled},i]}stencilConfigForOverlapTwoPass(e){const t=this.context.gl,i=e.sort(((e,t)=>t.overscaledZ-e.overscaledZ)),o=i[i.length-1].overscaledZ,r=i[0].overscaledZ-o+1;if(this.clearStencil(),r>1){const e={},a={};for(let i=0;i<r;i++)e[i+o]=new Vt({func:t.GREATER,mask:255},r+1+i,255,t.KEEP,t.KEEP,t.REPLACE),a[i+o]=new Vt({func:t.GREATER,mask:255},1+i,255,t.KEEP,t.KEEP,t.REPLACE);return this.nextStencilID=2*r+1,[e,a,i]}return this.nextStencilID=3,[{[o]:new Vt({func:t.GREATER,mask:255},2,255,t.KEEP,t.KEEP,t.REPLACE)},{[o]:new Vt({func:t.GREATER,mask:255},1,255,t.KEEP,t.KEEP,t.REPLACE)},i]}colorModeForRenderPass(){const e=this.context.gl;if(this._showOverdrawInspector){const i=1/8;return new jt([e.CONSTANT_COLOR,e.ONE],new t.be(i,i,i,0),[!0,!0,!0,!0])}return \"opaque\"===this.renderPass?jt.unblended:jt.alphaBlended}getDepthModeForSublayer(e,t,i){if(!this.opaquePassEnabledForLayer())return Ut.disabled;const o=1-((1+this.currentLayer)*this.numSublayers+e)*this.depthEpsilon;return new Ut(i||this.context.gl.LEQUAL,t,[o,o])}getDepthModeFor3D(){return new Ut(this.context.gl.LEQUAL,Ut.ReadWrite,this.depthRangeFor3D)}opaquePassEnabledForLayer(){return this.currentLayer<this.opaquePassCutoff}render(e,i){var o,r;this.style=e,this.options=i,this.lineAtlas=e.lineAtlas,this.imageManager=e.imageManager,this.glyphManager=e.glyphManager,this.symbolFadeChange=e.placement.symbolFadeChange(s.now()),this.imageManager.beginFrame();const a=this.style._order,n=this.style.sourceCaches,l={},c={},h={},u={isRenderingToTexture:!1,isRenderingGlobe:(null===(o=e.projection)||void 0===o?void 0:o.transitionState)>0};for(const e in n){const t=n[e];t.used&&t.prepare(this.context),l[e]=t.getVisibleCoordinates(!1),c[e]=l[e].slice().reverse(),h[e]=t.getVisibleCoordinates(!0).reverse();}this.opaquePassCutoff=1/0;for(let e=0;e<a.length;e++)if(this.style._layers[a[e]].is3D()){this.opaquePassCutoff=e;break}this.maybeDrawDepthAndCoords(!1),this.renderToTexture&&(this.renderToTexture.prepareForRender(this.style,this.transform.zoom),this.opaquePassCutoff=0),this.renderPass=\"offscreen\";for(const e of a){const t=this.style._layers[e];if(!t.hasOffscreenPass()||t.isHidden(this.transform.zoom))continue;const i=c[t.source];(\"custom\"===t.type||i.length)&&this.renderLayer(this,n[t.source],t,i,u);}if(null===(r=this.style.projection)||void 0===r||r.updateGPUdependent({context:this.context,useProgram:e=>this.useProgram(e)}),this.context.viewport.set([0,0,this.width,this.height]),this.context.bindFramebuffer.set(null),this.context.clear({color:i.showOverdrawInspector?t.be.black:t.be.transparent,depth:1}),this.clearStencil(),this.style.sky&&function(e,t){const i=e.context,o=i.gl,r=((e,t,i)=>{const o=Math.cos(t.rollInRadians),r=Math.sin(t.rollInRadians),a=he(t),s=t.getProjectionData({overscaledTileID:null,applyGlobeMatrix:!0,applyTerrainMatrix:!0}).projectionTransition;return {u_sky_color:e.properties.get(\"sky-color\"),u_horizon_color:e.properties.get(\"horizon-color\"),u_horizon:[(t.width/2-a*r)*i,(t.height/2+a*o)*i],u_horizon_normal:[-r,o],u_sky_horizon_blend:e.properties.get(\"sky-horizon-blend\")*t.height/2*i,u_sky_blend:s}})(t,e.style.map.transform,e.pixelRatio),a=new Ut(o.LEQUAL,Ut.ReadWrite,[0,1]),s=Vt.disabled,n=e.colorModeForRenderPass(),l=e.useProgram(\"sky\"),c=Ir(i,t);l.draw(i,o.TRIANGLES,a,s,n,Zt.disabled,r,null,void 0,\"sky\",c.vertexBuffer,c.indexBuffer,c.segments);}(this,this.style.sky),this._showOverdrawInspector=i.showOverdrawInspector,this.depthRangeFor3D=[0,1-(e._order.length+2)*this.numSublayers*this.depthEpsilon],!this.renderToTexture)for(this.renderPass=\"opaque\",this.currentLayer=a.length-1;this.currentLayer>=0;this.currentLayer--){const e=this.style._layers[a[this.currentLayer]],t=n[e.source],i=l[e.source];this._renderTileClippingMasks(e,i,!1),this.renderLayer(this,t,e,i,u);}this.renderPass=\"translucent\";let d=!1;for(this.currentLayer=0;this.currentLayer<a.length;this.currentLayer++){const e=this.style._layers[a[this.currentLayer]],t=n[e.source];if(this.renderToTexture&&this.renderToTexture.renderLayer(e,u))continue;this.opaquePassEnabledForLayer()||d||(d=!0,u.isRenderingGlobe&&!this.style.map.terrain&&this._renderTilesDepthBuffer());const i=(\"symbol\"===e.type?h:c)[e.source];this._renderTileClippingMasks(e,l[e.source],!!this.renderToTexture),this.renderLayer(this,t,e,i,u);}if(u.isRenderingGlobe&&function(e,i,o){const r=e.context,a=r.gl,s=e.useProgram(\"atmosphere\"),n=new Ut(a.LEQUAL,Ut.ReadOnly,[0,1]),l=e.transform,c=function(e,i){const o=e.properties.get(\"position\"),r=[-o.x,-o.y,-o.z],a=t.af(new Float64Array(16));return \"map\"===e.properties.get(\"anchor\")&&(t.b5(a,a,i.rollInRadians),t.b6(a,a,-i.pitchInRadians),t.b5(a,a,i.bearingInRadians),t.b6(a,a,i.center.lat*Math.PI/180),t.by(a,a,-i.center.lng*Math.PI/180)),t.c4(r,r,a),r}(o,e.transform),h=l.getProjectionData({overscaledTileID:null,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),u=i.properties.get(\"atmosphere-blend\")*h.projectionTransition;if(0===u)return;const d=oi(l.worldSize,l.center.lat),_=l.inverseProjectionMatrix,p=new Float64Array(4);p[3]=1,t.av(p,p,l.modelViewProjectionMatrix),p[0]/=p[3],p[1]/=p[3],p[2]/=p[3],p[3]=1,t.av(p,p,_),p[0]/=p[3],p[1]/=p[3],p[2]/=p[3],p[3]=1;const m=((e,t,i,o,r)=>({u_sun_pos:e,u_atmosphere_blend:t,u_globe_position:i,u_globe_radius:o,u_inv_proj_matrix:r}))(c,u,[p[0],p[1],p[2]],d,_),f=Ir(r,i);s.draw(r,a.TRIANGLES,n,Vt.disabled,jt.alphaBlended,Zt.disabled,m,null,null,\"atmosphere\",f.vertexBuffer,f.indexBuffer,f.segments);}(this,this.style.sky,this.style.light),this.options.showTileBoundaries){const e=function(e,t){let i=null;const o=Object.values(e._layers).flatMap((i=>i.source&&!i.isHidden(t)?[e.sourceCaches[i.source]]:[])),r=o.filter((e=>\"vector\"===e.getSource().type)),a=o.filter((e=>\"vector\"!==e.getSource().type)),s=e=>{(!i||i.getSource().maxzoom<e.getSource().maxzoom)&&(i=e);};return r.forEach((e=>s(e))),i||a.forEach((e=>s(e))),i}(this.style,this.transform.zoom);e&&function(e,t,i){for(let o=0;o<i.length;o++)Pr(e,t,i[o]);}(this,e,e.getVisibleCoordinates());}this.options.showPadding&&function(e){const t=e.transform.padding;yr(e,e.transform.height-(t.top||0),3,fr),yr(e,t.bottom||0,3,gr),wr(e,t.left||0,3,vr),wr(e,e.transform.width-(t.right||0),3,br);const i=e.transform.centerPoint;!function(e,t,i,o){Tr(e,t-1,i-10,2,20,o),Tr(e,t-10,i-1,20,2,o);}(e,i.x,e.transform.height-i.y,xr);}(this),this.context.setDefault();}maybeDrawDepthAndCoords(e){if(!this.style||!this.style.map||!this.style.map.terrain)return;const i=this.terrainFacilitator.matrix,o=this.transform.modelViewProjectionMatrix;let r=this.terrainFacilitator.dirty;r||(r=e?!t.c7(i,o):!t.c8(i,o)),r||(r=this.style.map.terrain.sourceCache.anyTilesAfterTime(this.terrainFacilitator.renderTime)),r&&(t.c9(i,o),this.terrainFacilitator.renderTime=Date.now(),this.terrainFacilitator.dirty=!1,function(e,i){const o=e.context,r=o.gl,a=e.transform,s=jt.unblended,n=new Ut(r.LEQUAL,Ut.ReadWrite,[0,1]),l=i.sourceCache.getRenderableTiles(),c=e.useProgram(\"terrainDepth\");o.bindFramebuffer.set(i.getFramebuffer(\"depth\").framebuffer),o.viewport.set([0,0,e.width/devicePixelRatio,e.height/devicePixelRatio]),o.clear({color:t.be.transparent,depth:1});for(const e of l){const t=i.getTerrainMesh(e.tileID),l=i.getTerrainData(e.tileID),h=a.getProjectionData({overscaledTileID:e.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0}),u={u_ele_delta:i.getMeshFrameDelta(a.zoom)};c.draw(o,r.TRIANGLES,n,Vt.disabled,s,Zt.backCCW,u,l,h,\"terrain\",t.vertexBuffer,t.indexBuffer,t.segments);}o.bindFramebuffer.set(null),o.viewport.set([0,0,e.width,e.height]);}(this,this.style.map.terrain),function(e,i){const o=e.context,r=o.gl,a=e.transform,s=jt.unblended,n=new Ut(r.LEQUAL,Ut.ReadWrite,[0,1]),l=i.getCoordsTexture(),c=i.sourceCache.getRenderableTiles(),h=e.useProgram(\"terrainCoords\");o.bindFramebuffer.set(i.getFramebuffer(\"coords\").framebuffer),o.viewport.set([0,0,e.width/devicePixelRatio,e.height/devicePixelRatio]),o.clear({color:t.be.transparent,depth:1}),i.coordsIndex=[];for(const e of c){const t=i.getTerrainMesh(e.tileID),c=i.getTerrainData(e.tileID);o.activeTexture.set(r.TEXTURE0),r.bindTexture(r.TEXTURE_2D,l.texture);const u={u_terrain_coords_id:(255-i.coordsIndex.length)/255,u_texture:0,u_ele_delta:i.getMeshFrameDelta(a.zoom)},d=a.getProjectionData({overscaledTileID:e.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0});h.draw(o,r.TRIANGLES,n,Vt.disabled,s,Zt.backCCW,u,c,d,\"terrain\",t.vertexBuffer,t.indexBuffer,t.segments),i.coordsIndex.push(e.tileID.key);}o.bindFramebuffer.set(null),o.viewport.set([0,0,e.width,e.height]);}(this,this.style.map.terrain));}renderLayer(e,i,o,r,a){o.isHidden(this.transform.zoom)||(\"background\"===o.type||\"custom\"===o.type||(r||[]).length)&&(this.id=o.id,t.ca(o)?function(e,i,o,r,a,s){if(\"translucent\"!==e.renderPass)return;const{isRenderingToTexture:n}=s,l=Vt.disabled,c=e.colorModeForRenderPass();(o._unevaluatedLayout.hasValue(\"text-variable-anchor\")||o._unevaluatedLayout.hasValue(\"text-variable-anchor-offset\"))&&function(e,i,o,r,a,s,n,l,c){const h=i.transform,u=i.style.map.terrain,d=\"map\"===a,_=\"map\"===s;for(const a of e){const e=r.getTile(a),s=e.getBucket(o);if(!s||!s.text||!s.text.segments.get().length)continue;const p=t.am(s.textSizeData,h.zoom),m=t.aB(e,1,i.transform.zoom),f=Ie(d,i.transform,m),g=\"none\"!==o.layout.get(\"icon-text-fit\")&&s.hasIconData();if(p){const i=Math.pow(2,h.zoom-e.tileID.overscaledZ),o=u?(e,t)=>u.getElevation(a,e,t):null;er(s,d,_,c,h,f,i,p,g,t.aC(h,e,n,l),a.toUnwrapped(),o);}}}(r,e,o,i,o.layout.get(\"text-rotation-alignment\"),o.layout.get(\"text-pitch-alignment\"),o.paint.get(\"text-translate\"),o.paint.get(\"text-translate-anchor\"),a),0!==o.paint.get(\"icon-opacity\").constantOr(1)&&ir(e,i,o,r,!1,o.paint.get(\"icon-translate\"),o.paint.get(\"icon-translate-anchor\"),o.layout.get(\"icon-rotation-alignment\"),o.layout.get(\"icon-pitch-alignment\"),o.layout.get(\"icon-keep-upright\"),l,c,n),0!==o.paint.get(\"text-opacity\").constantOr(1)&&ir(e,i,o,r,!0,o.paint.get(\"text-translate\"),o.paint.get(\"text-translate-anchor\"),o.layout.get(\"text-rotation-alignment\"),o.layout.get(\"text-pitch-alignment\"),o.layout.get(\"text-keep-upright\"),l,c,n),i.map.showCollisionBoxes&&(Ko(e,i,o,r,!0),Ko(e,i,o,r,!1));}(e,i,o,r,this.style.placement.variableOffsets,a):t.cb(o)?function(e,i,o,r,a){if(\"translucent\"!==e.renderPass)return;const{isRenderingToTexture:s}=a,n=o.paint.get(\"circle-opacity\"),l=o.paint.get(\"circle-stroke-width\"),c=o.paint.get(\"circle-stroke-opacity\"),h=!o.layout.get(\"circle-sort-key\").isConstant();if(0===n.constantOr(1)&&(0===l.constantOr(1)||0===c.constantOr(1)))return;const u=e.context,d=u.gl,_=e.transform,p=e.getDepthModeForSublayer(0,Ut.ReadOnly),m=Vt.disabled,f=e.colorModeForRenderPass(),g=[],v=_.getCircleRadiusCorrection();for(let a=0;a<r.length;a++){const n=r[a],l=i.getTile(n),c=l.getBucket(o);if(!c)continue;const u=o.paint.get(\"circle-translate\"),d=o.paint.get(\"circle-translate-anchor\"),p=t.aC(_,l,u,d),m=c.programConfigurations.get(o.id),f=e.useProgram(\"circle\",m),b=c.layoutVertexBuffer,x=c.indexBuffer,y=e.style.map.terrain&&e.style.map.terrain.getTerrainData(n),w={programConfiguration:m,program:f,layoutVertexBuffer:b,indexBuffer:x,uniformValues:ki(e,l,o,p,v),terrainData:y,projectionData:_.getProjectionData({overscaledTileID:n,applyGlobeMatrix:!s,applyTerrainMatrix:!0})};if(h){const e=c.segments.get();for(const i of e)g.push({segments:new t.aL([i]),sortKey:i.sortKey,state:w});}else g.push({segments:c.segments,sortKey:0,state:w});}h&&g.sort(((e,t)=>e.sortKey-t.sortKey));for(const t of g){const{programConfiguration:i,program:r,layoutVertexBuffer:a,indexBuffer:s,uniformValues:n,terrainData:l,projectionData:c}=t.state;r.draw(u,d.TRIANGLES,p,m,f,Zt.backCCW,n,l,c,o.id,a,s,t.segments,o.paint,e.transform.zoom,i);}}(e,i,o,r,a):t.cc(o)?function(e,i,o,r,a){if(0===o.paint.get(\"heatmap-opacity\"))return;const s=e.context,{isRenderingToTexture:n,isRenderingGlobe:l}=a;if(e.style.map.terrain){for(const t of r){const r=i.getTile(t);i.hasRenderableParent(t)||(\"offscreen\"===e.renderPass?rr(e,r,o,t,l):\"translucent\"===e.renderPass&&ar(e,o,t,n,l));}s.viewport.set([0,0,e.width,e.height]);}else \"offscreen\"===e.renderPass?function(e,i,o,r){const a=e.context,s=a.gl,n=e.transform,l=Vt.disabled,c=new jt([s.ONE,s.ONE],t.be.transparent,[!0,!0,!0,!0]);((function(e,i,o){const r=e.gl;e.activeTexture.set(r.TEXTURE1),e.viewport.set([0,0,i.width/4,i.height/4]);let a=o.heatmapFbos.get(t.c1);a?(r.bindTexture(r.TEXTURE_2D,a.colorAttachment.get()),e.bindFramebuffer.set(a.framebuffer)):(a=sr(e,i.width/4,i.height/4),o.heatmapFbos.set(t.c1,a));}))(a,e,o),a.clear({color:t.be.transparent});for(let t=0;t<r.length;t++){const h=r[t];if(i.hasRenderableParent(h))continue;const u=i.getTile(h),d=u.getBucket(o);if(!d)continue;const _=d.programConfigurations.get(o.id),p=e.useProgram(\"heatmap\",_),m=n.getProjectionData({overscaledTileID:h,applyGlobeMatrix:!0,applyTerrainMatrix:!1}),f=n.getCircleRadiusCorrection();p.draw(a,s.TRIANGLES,Ut.disabled,l,c,Zt.backCCW,ji(u,n.zoom,o.paint.get(\"heatmap-intensity\"),f),null,m,o.id,d.layoutVertexBuffer,d.indexBuffer,d.segments,o.paint,n.zoom,_);}a.viewport.set([0,0,e.width,e.height]);}(e,i,o,r):\"translucent\"===e.renderPass&&function(e,i){const o=e.context,r=o.gl;o.setColorMode(e.colorModeForRenderPass());const a=i.heatmapFbos.get(t.c1);a&&(o.activeTexture.set(r.TEXTURE0),r.bindTexture(r.TEXTURE_2D,a.colorAttachment.get()),o.activeTexture.set(r.TEXTURE1),nr(o,i).bind(r.LINEAR,r.CLAMP_TO_EDGE),e.useProgram(\"heatmapTexture\").draw(o,r.TRIANGLES,Ut.disabled,Vt.disabled,e.colorModeForRenderPass(),Zt.disabled,Ni(e,i,0,1),null,null,i.id,e.viewportBuffer,e.quadTriangleIndexBuffer,e.viewportSegments,i.paint,e.transform.zoom));}(e,o);}(e,i,o,r,a):t.cd(o)?function(e,i,o,r,a){if(\"translucent\"!==e.renderPass)return;const{isRenderingToTexture:s}=a,n=o.paint.get(\"line-opacity\"),l=o.paint.get(\"line-width\");if(0===n.constantOr(1)||0===l.constantOr(1))return;const c=e.getDepthModeForSublayer(0,Ut.ReadOnly),h=e.colorModeForRenderPass(),u=o.paint.get(\"line-dasharray\"),d=o.paint.get(\"line-pattern\"),_=d.constantOr(1),p=o.paint.get(\"line-gradient\"),m=o.getCrossfadeParameters(),f=_?\"linePattern\":u?\"lineSDF\":p?\"lineGradient\":\"line\",g=e.context,v=g.gl,b=e.transform;let x=!0;for(const a of r){const r=i.getTile(a);if(_&&!r.patternsLoaded())continue;const n=r.getBucket(o);if(!n)continue;const l=n.programConfigurations.get(o.id),y=e.context.program.get(),w=e.useProgram(f,l),T=x||w.program!==y,P=e.style.map.terrain&&e.style.map.terrain.getTerrainData(a),C=d.constantOr(null);if(C&&r.imageAtlas){const e=r.imageAtlas,t=e.patternPositions[C.to.toString()],i=e.patternPositions[C.from.toString()];t&&i&&l.setConstantPatternPositions(t,i);}const I=b.getProjectionData({overscaledTileID:a,applyGlobeMatrix:!s,applyTerrainMatrix:!0}),M=b.getPixelScale(),S=_?Wi(e,r,o,M,m):u?Hi(e,r,o,M,u,m):p?qi(e,r,o,M,n.lineClipsArray.length):$i(e,r,o,M);if(_)g.activeTexture.set(v.TEXTURE0),r.imageAtlasTexture.bind(v.LINEAR,v.CLAMP_TO_EDGE),l.updatePaintBuffers(m);else if(u&&(T||e.lineAtlas.dirty))g.activeTexture.set(v.TEXTURE0),e.lineAtlas.bind(g);else if(p){const r=n.gradients[o.id];let s=r.texture;if(o.gradientVersion!==r.version){let l=256;if(o.stepInterpolant){const o=i.getSource().maxzoom,r=a.canonical.z===o?Math.ceil(1<<e.transform.maxZoom-a.canonical.z):1;l=t.ag(t.c2(n.maxLineLength/t.$*1024*r),256,g.maxTextureSize);}r.gradient=t.c3({expression:o.gradientExpression(),evaluationKey:\"lineProgress\",resolution:l,image:r.gradient||void 0,clips:n.lineClipsArray}),r.texture?r.texture.update(r.gradient):r.texture=new t.T(g,r.gradient,v.RGBA),r.version=o.gradientVersion,s=r.texture;}g.activeTexture.set(v.TEXTURE0),s.bind(o.stepInterpolant?v.NEAREST:v.LINEAR,v.CLAMP_TO_EDGE);}const E=e.stencilModeForClipping(a);w.draw(g,v.TRIANGLES,c,E,h,Zt.disabled,S,P,I,o.id,n.layoutVertexBuffer,n.indexBuffer,n.segments,o.paint,e.transform.zoom,l,n.layoutVertexBuffer2),x=!1;}}(e,i,o,r,a):t.ce(o)?function(e,i,o,r,a){const s=o.paint.get(\"fill-color\"),n=o.paint.get(\"fill-opacity\");if(0===n.constantOr(1))return;const{isRenderingToTexture:l}=a,c=e.colorModeForRenderPass(),h=o.paint.get(\"fill-pattern\"),u=e.opaquePassEnabledForLayer()&&!h.constantOr(1)&&1===s.constantOr(t.be.transparent).a&&1===n.constantOr(0)?\"opaque\":\"translucent\";if(e.renderPass===u){const t=e.getDepthModeForSublayer(1,\"opaque\"===e.renderPass?Ut.ReadWrite:Ut.ReadOnly);cr(e,i,o,r,t,c,!1,l);}if(\"translucent\"===e.renderPass&&o.paint.get(\"fill-antialias\")){const t=e.getDepthModeForSublayer(o.getPaintProperty(\"fill-outline-color\")?2:0,Ut.ReadOnly);cr(e,i,o,r,t,c,!0,l);}}(e,i,o,r,a):t.cf(o)?function(e,t,i,o,r){const a=i.paint.get(\"fill-extrusion-opacity\");if(0===a)return;const{isRenderingToTexture:s}=r;if(\"translucent\"===e.renderPass){const r=new Ut(e.context.gl.LEQUAL,Ut.ReadWrite,e.depthRangeFor3D);if(1!==a||i.paint.get(\"fill-extrusion-pattern\").constantOr(1))hr(e,t,i,o,r,Vt.disabled,jt.disabled,s),hr(e,t,i,o,r,e.stencilModeFor3D(),e.colorModeForRenderPass(),s);else {const a=e.colorModeForRenderPass();hr(e,t,i,o,r,Vt.disabled,a,s);}}}(e,i,o,r,a):t.cg(o)?function(e,i,o,r,a){if(\"offscreen\"!==e.renderPass&&\"translucent\"!==e.renderPass)return;const{isRenderingToTexture:s}=a,n=e.context,l=e.style.projection.useSubdivision,c=e.getDepthModeForSublayer(0,Ut.ReadOnly),h=e.colorModeForRenderPass();if(\"offscreen\"===e.renderPass)!function(e,i,o,r,a,s,n){const l=e.context,c=l.gl;for(const h of o){const o=i.getTile(h),u=o.dem;if(!u||!u.data)continue;if(!o.needsHillshadePrepare)continue;const d=u.dim,_=u.stride,p=u.getPixels();if(l.activeTexture.set(c.TEXTURE1),l.pixelStoreUnpackPremultiplyAlpha.set(!1),o.demTexture=o.demTexture||e.getTileTexture(_),o.demTexture){const e=o.demTexture;e.update(p,{premultiply:!1}),e.bind(c.NEAREST,c.CLAMP_TO_EDGE);}else o.demTexture=new t.T(l,p,c.RGBA,{premultiply:!1}),o.demTexture.bind(c.NEAREST,c.CLAMP_TO_EDGE);l.activeTexture.set(c.TEXTURE0);let m=o.fbo;if(!m){const e=new t.T(l,{width:d,height:d,data:null},c.RGBA);e.bind(c.LINEAR,c.CLAMP_TO_EDGE),m=o.fbo=l.createFramebuffer(d,d,!0,!1),m.colorAttachment.set(e.texture);}l.bindFramebuffer.set(m.framebuffer),l.viewport.set([0,0,d,d]),e.useProgram(\"hillshadePrepare\").draw(l,c.TRIANGLES,a,s,n,Zt.disabled,Ui(o.tileID,u),null,null,r.id,e.rasterBoundsBuffer,e.quadTriangleIndexBuffer,e.rasterBoundsSegments),o.needsHillshadePrepare=!1;}}(e,i,r,o,c,Vt.disabled,h),n.viewport.set([0,0,e.width,e.height]);else if(\"translucent\"===e.renderPass)if(l){const[t,a,n]=e.stencilConfigForOverlapTwoPass(r);ur(e,i,o,n,t,c,h,!1,s),ur(e,i,o,n,a,c,h,!0,s);}else {const[t,a]=e.getStencilConfigForOverlapAndUpdateStencilID(r);ur(e,i,o,a,t,c,h,!1,s);}}(e,i,o,r,a):t.ch(o)?function(e,t,i,o,r){if(\"translucent\"!==e.renderPass)return;if(!o.length)return;const{isRenderingToTexture:a}=r,s=e.style.projection.useSubdivision,n=e.getDepthModeForSublayer(0,Ut.ReadOnly),l=e.colorModeForRenderPass();if(s){const[r,s,c]=e.stencilConfigForOverlapTwoPass(o);dr(e,t,i,c,r,n,l,!1,a),dr(e,t,i,c,s,n,l,!0,a);}else {const[r,s]=e.getStencilConfigForOverlapAndUpdateStencilID(o);dr(e,t,i,s,r,n,l,!1,a);}}(e,i,o,r,a):t.ci(o)?function(e,t,i,o,r){if(\"translucent\"!==e.renderPass)return;if(0===i.paint.get(\"raster-opacity\"))return;if(!o.length)return;const{isRenderingToTexture:a}=r,s=t.getSource(),n=e.style.projection.useSubdivision;if(s instanceof X)pr(e,t,i,o,null,!1,!1,s.tileCoords,s.flippedWindingOrder,a);else if(n){const[r,s,n]=e.stencilConfigForOverlapTwoPass(o);pr(e,t,i,n,r,!1,!0,_r,!1,a),pr(e,t,i,n,s,!0,!0,_r,!1,a);}else {const[r,s]=e.getStencilConfigForOverlapAndUpdateStencilID(o);pr(e,t,i,s,r,!1,!0,_r,!1,a);}}(e,i,o,r,a):t.cj(o)?function(e,t,i,o,r){const a=i.paint.get(\"background-color\"),s=i.paint.get(\"background-opacity\");if(0===s)return;const{isRenderingToTexture:n}=r,l=e.context,c=l.gl,h=e.style.projection,u=e.transform,d=u.tileSize,_=i.paint.get(\"background-pattern\");if(e.isPatternMissing(_))return;const p=!_&&1===a.a&&1===s&&e.opaquePassEnabledForLayer()?\"opaque\":\"translucent\";if(e.renderPass!==p)return;const m=Vt.disabled,f=e.getDepthModeForSublayer(0,\"opaque\"===p?Ut.ReadWrite:Ut.ReadOnly),g=e.colorModeForRenderPass(),v=e.useProgram(_?\"backgroundPattern\":\"background\"),b=o||ve(u,{tileSize:d,terrain:e.style.map.terrain});_&&(l.activeTexture.set(c.TEXTURE0),e.imageManager.bind(e.context));const x=i.getCrossfadeParameters();for(const t of b){const o=u.getProjectionData({overscaledTileID:t,applyGlobeMatrix:!n,applyTerrainMatrix:!0}),r=_?oo(s,e,_,{tileID:t,tileSize:d},x):io(s,a),p=e.style.map.terrain&&e.style.map.terrain.getTerrainData(t),b=h.getMeshFromTileID(l,t.canonical,!1,!0,\"raster\");v.draw(l,c.TRIANGLES,f,m,g,Zt.backCCW,r,p,o,i.id,b.vertexBuffer,b.indexBuffer,b.segments);}}(e,0,o,r,a):t.ck(o)&&function(e,t,i,o){const{isRenderingGlobe:r}=o,a=e.context,s=i.implementation,n=e.style.projection,l=e.transform,c=l.getProjectionDataForCustomLayer(r),h={farZ:l.farZ,nearZ:l.nearZ,fov:l.fov*Math.PI/180,modelViewProjectionMatrix:l.modelViewProjectionMatrix,projectionMatrix:l.projectionMatrix,shaderData:{variantName:n.shaderVariantName,vertexShaderPrelude:`const float PI = 3.141592653589793;\\nuniform mat4 u_projection_matrix;\\n${n.shaderPreludeCode.vertexSource}`,define:n.shaderDefine},defaultProjectionData:c},u=s.renderingMode?s.renderingMode:\"2d\";if(\"offscreen\"===e.renderPass){const t=s.prerender;t&&(e.setCustomLayerDefaults(),a.setColorMode(e.colorModeForRenderPass()),t.call(s,a.gl,h),a.setDirty(),e.setBaseState());}else if(\"translucent\"===e.renderPass){e.setCustomLayerDefaults(),a.setColorMode(e.colorModeForRenderPass()),a.setStencilMode(Vt.disabled);const t=\"3d\"===u?e.getDepthModeFor3D():e.getDepthModeForSublayer(0,Ut.ReadOnly);a.setDepthMode(t),s.render(a.gl,h),a.setDirty(),e.setBaseState(),a.bindFramebuffer.set(null);}}(e,0,o,a));}saveTileTexture(e){const t=this._tileTextures[e.size[0]];t?t.push(e):this._tileTextures[e.size[0]]=[e];}getTileTexture(e){const t=this._tileTextures[e];return t&&t.length>0?t.pop():null}isPatternMissing(e){if(!e)return !1;if(!e.from||!e.to)return !0;const t=this.imageManager.getPattern(e.from.toString()),i=this.imageManager.getPattern(e.to.toString());return !t||!i}useProgram(e,t,i=!1,o=[]){this.cache=this.cache||{};const r=!!this.style.map.terrain,a=this.style.projection,s=i?xt.projectionMercator:a.shaderPreludeCode,n=i?Pt:a.shaderDefine,l=e+(t?t.cacheKey:\"\")+`/${i?Ct:a.shaderVariantName}`+(this._showOverdrawInspector?\"/overdraw\":\"\")+(r?\"/terrain\":\"\")+(o?`/${o.join(\"/\")}`:\"\");return this.cache[l]||(this.cache[l]=new Si(this.context,xt[e],t,ao[e],this._showOverdrawInspector,r,s,n,o)),this.cache[l]}setCustomLayerDefaults(){this.context.unbindVAO(),this.context.cullFace.setDefault(),this.context.activeTexture.setDefault(),this.context.pixelStoreUnpack.setDefault(),this.context.pixelStoreUnpackPremultiplyAlpha.setDefault(),this.context.pixelStoreUnpackFlipY.setDefault();}setBaseState(){const e=this.context.gl;this.context.cullFace.set(!1),this.context.viewport.set([0,0,this.width,this.height]),this.context.blendEquation.set(e.FUNC_ADD);}initDebugOverlayCanvas(){null==this.debugOverlayCanvas&&(this.debugOverlayCanvas=document.createElement(\"canvas\"),this.debugOverlayCanvas.width=512,this.debugOverlayCanvas.height=512,this.debugOverlayTexture=new t.T(this.context,this.debugOverlayCanvas,this.context.gl.RGBA));}destroy(){this.debugOverlayTexture&&this.debugOverlayTexture.destroy();}overLimit(){const{drawingBufferWidth:e,drawingBufferHeight:t}=this.context.gl;return this.width!==e||this.height!==t}}function Sr(e,t){let i,o=!1,r=null,a=null;const s=()=>{r=null,o&&(e.apply(a,i),r=setTimeout(s,t),o=!1);};return (...e)=>(o=!0,a=this,i=e,r||s(),r)}class Er{constructor(e){this._getCurrentHash=()=>{const e=window.location.hash.replace(\"#\",\"\");if(this._hashName){let t;return e.split(\"&\").map((e=>e.split(\"=\"))).forEach((e=>{e[0]===this._hashName&&(t=e);})),(t&&t[1]||\"\").split(\"/\")}return e.split(\"/\")},this._onHashChange=()=>{const e=this._getCurrentHash();if(!this._isValidHash(e))return !1;const t=this._map.dragRotate.isEnabled()&&this._map.touchZoomRotate.isEnabled()?+(e[3]||0):this._map.getBearing();return this._map.jumpTo({center:[+e[2],+e[1]],zoom:+e[0],bearing:t,pitch:+(e[4]||0)}),!0},this._updateHashUnthrottled=()=>{const e=window.location.href.replace(/(#.*)?$/,this.getHashString());window.history.replaceState(window.history.state,null,e);},this._removeHash=()=>{const e=this._getCurrentHash();if(0===e.length)return;const t=e.join(\"/\");let i=t;i.split(\"&\").length>0&&(i=i.split(\"&\")[0]),this._hashName&&(i=`${this._hashName}=${t}`);let o=window.location.hash.replace(i,\"\");o.startsWith(\"#&\")?o=o.slice(0,1)+o.slice(2):\"#\"===o&&(o=\"\");let r=window.location.href.replace(/(#.+)?$/,o);r=r.replace(\"&&\",\"&\"),window.history.replaceState(window.history.state,null,r);},this._updateHash=Sr(this._updateHashUnthrottled,300),this._hashName=e&&encodeURIComponent(e);}addTo(e){return this._map=e,addEventListener(\"hashchange\",this._onHashChange,!1),this._map.on(\"moveend\",this._updateHash),this}remove(){return removeEventListener(\"hashchange\",this._onHashChange,!1),this._map.off(\"moveend\",this._updateHash),clearTimeout(this._updateHash()),this._removeHash(),delete this._map,this}getHashString(e){const t=this._map.getCenter(),i=Math.round(100*this._map.getZoom())/100,o=Math.ceil((i*Math.LN2+Math.log(512/360/.5))/Math.LN10),r=Math.pow(10,o),a=Math.round(t.lng*r)/r,s=Math.round(t.lat*r)/r,n=this._map.getBearing(),l=this._map.getPitch();let c=\"\";if(c+=e?`/${a}/${s}/${i}`:`${i}/${s}/${a}`,(n||l)&&(c+=\"/\"+Math.round(10*n)/10),l&&(c+=`/${Math.round(l)}`),this._hashName){const e=this._hashName;let t=!1;const i=window.location.hash.slice(1).split(\"&\").map((i=>{const o=i.split(\"=\")[0];return o===e?(t=!0,`${o}=${c}`):i})).filter((e=>e));return t||i.push(`${e}=${c}`),`#${i.join(\"&\")}`}return `#${c}`}_isValidHash(e){if(e.length<3||e.some(isNaN))return !1;try{new t.S(+e[2],+e[1]);}catch(e){return !1}const i=+e[0],o=+(e[3]||0),r=+(e[4]||0);return i>=this._map.getMinZoom()&&i<=this._map.getMaxZoom()&&o>=-180&&o<=180&&r>=this._map.getMinPitch()&&r<=this._map.getMaxPitch()}}const Rr={linearity:.3,easing:t.cl(0,0,.3,1)},zr=t.e({deceleration:2500,maxSpeed:1400},Rr),Dr=t.e({deceleration:20,maxSpeed:1400},Rr),Ar=t.e({deceleration:1e3,maxSpeed:360},Rr),Lr=t.e({deceleration:1e3,maxSpeed:90},Rr),kr=t.e({deceleration:1e3,maxSpeed:360},Rr);class Fr{constructor(e){this._map=e,this.clear();}clear(){this._inertiaBuffer=[];}record(e){this._drainInertiaBuffer(),this._inertiaBuffer.push({time:s.now(),settings:e});}_drainInertiaBuffer(){const e=this._inertiaBuffer,t=s.now();for(;e.length>0&&t-e[0].time>160;)e.shift();}_onMoveEnd(e){if(this._drainInertiaBuffer(),this._inertiaBuffer.length<2)return;const i={zoom:0,bearing:0,pitch:0,roll:0,pan:new t.P(0,0),pinchAround:void 0,around:void 0};for(const{settings:e}of this._inertiaBuffer)i.zoom+=e.zoomDelta||0,i.bearing+=e.bearingDelta||0,i.pitch+=e.pitchDelta||0,i.roll+=e.rollDelta||0,e.panDelta&&i.pan._add(e.panDelta),e.around&&(i.around=e.around),e.pinchAround&&(i.pinchAround=e.pinchAround);const o=this._inertiaBuffer[this._inertiaBuffer.length-1].time-this._inertiaBuffer[0].time,r={};if(i.pan.mag()){const a=Or(i.pan.mag(),o,t.e({},zr,e||{})),s=i.pan.mult(a.amount/i.pan.mag()),n=this._map.cameraHelper.handlePanInertia(s,this._map.transform);r.center=n.easingCenter,r.offset=n.easingOffset,Br(r,a);}if(i.zoom){const e=Or(i.zoom,o,Dr);r.zoom=this._map.transform.zoom+e.amount,Br(r,e);}if(i.bearing){const e=Or(i.bearing,o,Ar);r.bearing=this._map.transform.bearing+t.ag(e.amount,-179,179),Br(r,e);}if(i.pitch){const e=Or(i.pitch,o,Lr);r.pitch=this._map.transform.pitch+e.amount,Br(r,e);}if(i.roll){const e=Or(i.roll,o,kr);r.roll=this._map.transform.roll+t.ag(e.amount,-179,179),Br(r,e);}if(r.zoom||r.bearing){const e=void 0===i.pinchAround?i.around:i.pinchAround;r.around=e?this._map.unproject(e):this._map.getCenter();}return this.clear(),t.e(r,{noMoveStart:!0})}}function Br(e,t){(!e.duration||e.duration<t.duration)&&(e.duration=t.duration,e.easing=t.easing);}function Or(e,i,o){const{maxSpeed:r,linearity:a,deceleration:s}=o,n=t.ag(e*a/(i/1e3),-r,r),l=Math.abs(n)/(s*a);return {easing:o.easing,duration:1e3*l,amount:n*(l/2)}}class jr extends t.l{preventDefault(){this._defaultPrevented=!0;}get defaultPrevented(){return this._defaultPrevented}constructor(e,i,o,r={}){o=o instanceof MouseEvent?o:new MouseEvent(e,o);const a=n.mousePos(i.getCanvas(),o),s=i.unproject(a);super(e,t.e({point:a,lngLat:s,originalEvent:o},r)),this._defaultPrevented=!1,this.target=i;}}class Nr extends t.l{preventDefault(){this._defaultPrevented=!0;}get defaultPrevented(){return this._defaultPrevented}constructor(e,i,o){const r=\"touchend\"===e?o.changedTouches:o.touches,a=n.touchPos(i.getCanvasContainer(),r),s=a.map((e=>i.unproject(e))),l=a.reduce(((e,t,i,o)=>e.add(t.div(o.length))),new t.P(0,0));super(e,{points:a,point:l,lngLats:s,lngLat:i.unproject(l),originalEvent:o}),this._defaultPrevented=!1;}}class Zr extends t.l{preventDefault(){this._defaultPrevented=!0;}get defaultPrevented(){return this._defaultPrevented}constructor(e,t,i){super(e,{originalEvent:i}),this._defaultPrevented=!1;}}class Ur{constructor(e,t){this._map=e,this._clickTolerance=t.clickTolerance;}reset(){delete this._mousedownPos;}wheel(e){return this._firePreventable(new Zr(e.type,this._map,e))}mousedown(e,t){return this._mousedownPos=t,this._firePreventable(new jr(e.type,this._map,e))}mouseup(e){this._map.fire(new jr(e.type,this._map,e));}click(e,t){this._mousedownPos&&this._mousedownPos.dist(t)>=this._clickTolerance||this._map.fire(new jr(e.type,this._map,e));}dblclick(e){return this._firePreventable(new jr(e.type,this._map,e))}mouseover(e){this._map.fire(new jr(e.type,this._map,e));}mouseout(e){this._map.fire(new jr(e.type,this._map,e));}touchstart(e){return this._firePreventable(new Nr(e.type,this._map,e))}touchmove(e){this._map.fire(new Nr(e.type,this._map,e));}touchend(e){this._map.fire(new Nr(e.type,this._map,e));}touchcancel(e){this._map.fire(new Nr(e.type,this._map,e));}_firePreventable(e){if(this._map.fire(e),e.defaultPrevented)return {}}isEnabled(){return !0}isActive(){return !1}enable(){}disable(){}}class Gr{constructor(e){this._map=e;}reset(){this._delayContextMenu=!1,this._ignoreContextMenu=!0,delete this._contextMenuEvent;}mousemove(e){this._map.fire(new jr(e.type,this._map,e));}mousedown(){this._delayContextMenu=!0,this._ignoreContextMenu=!1;}mouseup(){this._delayContextMenu=!1,this._contextMenuEvent&&(this._map.fire(new jr(\"contextmenu\",this._map,this._contextMenuEvent)),delete this._contextMenuEvent);}contextmenu(e){this._delayContextMenu?this._contextMenuEvent=e:this._ignoreContextMenu||this._map.fire(new jr(e.type,this._map,e)),this._map.listens(\"contextmenu\")&&e.preventDefault();}isEnabled(){return !0}isActive(){return !1}enable(){}disable(){}}class Vr{constructor(e){this._map=e;}get transform(){return this._map._requestedCameraState||this._map.transform}get center(){return {lng:this.transform.center.lng,lat:this.transform.center.lat}}get zoom(){return this.transform.zoom}get pitch(){return this.transform.pitch}get bearing(){return this.transform.bearing}unproject(e){return this.transform.screenPointToLocation(t.P.convert(e),this._map.terrain)}}class $r{constructor(e,t){this._map=e,this._tr=new Vr(e),this._el=e.getCanvasContainer(),this._container=e.getContainer(),this._clickTolerance=t.clickTolerance||1;}isEnabled(){return !!this._enabled}isActive(){return !!this._active}enable(){this.isEnabled()||(this._enabled=!0);}disable(){this.isEnabled()&&(this._enabled=!1);}mousedown(e,t){this.isEnabled()&&e.shiftKey&&0===e.button&&(n.disableDrag(),this._startPos=this._lastPos=t,this._active=!0);}mousemoveWindow(e,t){if(!this._active)return;const i=t;if(this._lastPos.equals(i)||!this._box&&i.dist(this._startPos)<this._clickTolerance)return;const o=this._startPos;this._lastPos=i,this._box||(this._box=n.create(\"div\",\"maplibregl-boxzoom\",this._container),this._container.classList.add(\"maplibregl-crosshair\"),this._fireEvent(\"boxzoomstart\",e));const r=Math.min(o.x,i.x),a=Math.max(o.x,i.x),s=Math.min(o.y,i.y),l=Math.max(o.y,i.y);n.setTransform(this._box,`translate(${r}px,${s}px)`),this._box.style.width=a-r+\"px\",this._box.style.height=l-s+\"px\";}mouseupWindow(e,i){if(!this._active)return;if(0!==e.button)return;const o=this._startPos,r=i;if(this.reset(),n.suppressClick(),o.x!==r.x||o.y!==r.y)return this._map.fire(new t.l(\"boxzoomend\",{originalEvent:e})),{cameraAnimation:e=>e.fitScreenCoordinates(o,r,this._tr.bearing,{linear:!0})};this._fireEvent(\"boxzoomcancel\",e);}keydown(e){this._active&&27===e.keyCode&&(this.reset(),this._fireEvent(\"boxzoomcancel\",e));}reset(){this._active=!1,this._container.classList.remove(\"maplibregl-crosshair\"),this._box&&(n.remove(this._box),this._box=null),n.enableDrag(),delete this._startPos,delete this._lastPos;}_fireEvent(e,i){return this._map.fire(new t.l(e,{originalEvent:i}))}}function qr(e,t){if(e.length!==t.length)throw new Error(`The number of touches and points are not equal - touches ${e.length}, points ${t.length}`);const i={};for(let o=0;o<e.length;o++)i[e[o].identifier]=t[o];return i}class Wr{constructor(e){this.reset(),this.numTouches=e.numTouches;}reset(){delete this.centroid,delete this.startTime,delete this.touches,this.aborted=!1;}touchstart(e,i,o){(this.centroid||o.length>this.numTouches)&&(this.aborted=!0),this.aborted||(void 0===this.startTime&&(this.startTime=e.timeStamp),o.length===this.numTouches&&(this.centroid=function(e){const i=new t.P(0,0);for(const t of e)i._add(t);return i.div(e.length)}(i),this.touches=qr(o,i)));}touchmove(e,t,i){if(this.aborted||!this.centroid)return;const o=qr(i,t);for(const e in this.touches){const t=o[e];(!t||t.dist(this.touches[e])>30)&&(this.aborted=!0);}}touchend(e,t,i){if((!this.centroid||e.timeStamp-this.startTime>500)&&(this.aborted=!0),0===i.length){const e=!this.aborted&&this.centroid;if(this.reset(),e)return e}}}class Hr{constructor(e){this.singleTap=new Wr(e),this.numTaps=e.numTaps,this.reset();}reset(){this.lastTime=1/0,delete this.lastTap,this.count=0,this.singleTap.reset();}touchstart(e,t,i){this.singleTap.touchstart(e,t,i);}touchmove(e,t,i){this.singleTap.touchmove(e,t,i);}touchend(e,t,i){const o=this.singleTap.touchend(e,t,i);if(o){const t=e.timeStamp-this.lastTime<500,i=!this.lastTap||this.lastTap.dist(o)<30;if(t&&i||this.reset(),this.count++,this.lastTime=e.timeStamp,this.lastTap=o,this.count===this.numTaps)return this.reset(),o}}}class Xr{constructor(e){this._tr=new Vr(e),this._zoomIn=new Hr({numTouches:1,numTaps:2}),this._zoomOut=new Hr({numTouches:2,numTaps:1}),this.reset();}reset(){this._active=!1,this._zoomIn.reset(),this._zoomOut.reset();}touchstart(e,t,i){this._zoomIn.touchstart(e,t,i),this._zoomOut.touchstart(e,t,i);}touchmove(e,t,i){this._zoomIn.touchmove(e,t,i),this._zoomOut.touchmove(e,t,i);}touchend(e,t,i){const o=this._zoomIn.touchend(e,t,i),r=this._zoomOut.touchend(e,t,i),a=this._tr;return o?(this._active=!0,e.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:t=>t.easeTo({duration:300,zoom:a.zoom+1,around:a.unproject(o)},{originalEvent:e})}):r?(this._active=!0,e.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:t=>t.easeTo({duration:300,zoom:a.zoom-1,around:a.unproject(r)},{originalEvent:e})}):void 0}touchcancel(){this.reset();}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class Kr{constructor(e){this._enabled=!!e.enable,this._moveStateManager=e.moveStateManager,this._clickTolerance=e.clickTolerance||1,this._moveFunction=e.move,this._activateOnStart=!!e.activateOnStart,e.assignEvents(this),this.reset();}reset(e){this._active=!1,this._moved=!1,delete this._lastPoint,this._moveStateManager.endMove(e);}_move(...e){const t=this._moveFunction(...e);if(t.bearingDelta||t.pitchDelta||t.rollDelta||t.around||t.panDelta)return this._active=!0,t}dragStart(e,t){this.isEnabled()&&!this._lastPoint&&this._moveStateManager.isValidStartEvent(e)&&(this._moveStateManager.startMove(e),this._lastPoint=Array.isArray(t)?t[0]:t,this._activateOnStart&&this._lastPoint&&(this._active=!0));}dragMove(e,t){if(!this.isEnabled())return;const i=this._lastPoint;if(!i)return;if(e.preventDefault(),!this._moveStateManager.isValidMoveEvent(e))return void this.reset(e);const o=Array.isArray(t)?t[0]:t;return !this._moved&&o.dist(i)<this._clickTolerance?void 0:(this._moved=!0,this._lastPoint=o,this._move(i,o))}dragEnd(e){this.isEnabled()&&this._lastPoint&&this._moveStateManager.isValidEndEvent(e)&&(this._moved&&n.suppressClick(),this.reset(e));}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}getClickTolerance(){return this._clickTolerance}}const Yr=0,Qr=2,Jr={[Yr]:1,[Qr]:2};class ea{constructor(e){this._correctEvent=e.checkCorrectEvent;}startMove(e){const t=n.mouseButton(e);this._eventButton=t;}endMove(e){delete this._eventButton;}isValidStartEvent(e){return this._correctEvent(e)}isValidMoveEvent(e){return !function(e,t){const i=Jr[t];return void 0===e.buttons||(e.buttons&i)!==i}(e,this._eventButton)}isValidEndEvent(e){return n.mouseButton(e)===this._eventButton}}class ta{constructor(){this._firstTouch=void 0;}_isOneFingerTouch(e){return 1===e.targetTouches.length}_isSameTouchEvent(e){return e.targetTouches[0].identifier===this._firstTouch}startMove(e){this._firstTouch=e.targetTouches[0].identifier;}endMove(e){delete this._firstTouch;}isValidStartEvent(e){return this._isOneFingerTouch(e)}isValidMoveEvent(e){return this._isOneFingerTouch(e)&&this._isSameTouchEvent(e)}isValidEndEvent(e){return this._isOneFingerTouch(e)&&this._isSameTouchEvent(e)}}class ia{constructor(e=new ea({checkCorrectEvent:()=>!0}),t=new ta){this.mouseMoveStateManager=e,this.oneFingerTouchMoveStateManager=t;}_executeRelevantHandler(e,t,i){return e instanceof MouseEvent?t(e):\"undefined\"!=typeof TouchEvent&&e instanceof TouchEvent?i(e):void 0}startMove(e){this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.startMove(e)),(e=>this.oneFingerTouchMoveStateManager.startMove(e)));}endMove(e){this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.endMove(e)),(e=>this.oneFingerTouchMoveStateManager.endMove(e)));}isValidStartEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidStartEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidStartEvent(e)))}isValidMoveEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidMoveEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidMoveEvent(e)))}isValidEndEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidEndEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidEndEvent(e)))}}const oa=e=>{e.mousedown=e.dragStart,e.mousemoveWindow=e.dragMove,e.mouseup=e.dragEnd,e.contextmenu=e=>{e.preventDefault();};};class ra{constructor(e,t){this._clickTolerance=e.clickTolerance||1,this._map=t,this.reset();}reset(){this._active=!1,this._touches={},this._sum=new t.P(0,0);}_shouldBePrevented(e){return e<(this._map.cooperativeGestures.isEnabled()?2:1)}touchstart(e,t,i){return this._calculateTransform(e,t,i)}touchmove(e,t,i){if(this._active){if(!this._shouldBePrevented(i.length))return e.preventDefault(),this._calculateTransform(e,t,i);this._map.cooperativeGestures.notifyGestureBlocked(\"touch_pan\",e);}}touchend(e,t,i){this._calculateTransform(e,t,i),this._active&&this._shouldBePrevented(i.length)&&this.reset();}touchcancel(){this.reset();}_calculateTransform(e,i,o){o.length>0&&(this._active=!0);const r=qr(o,i),a=new t.P(0,0),s=new t.P(0,0);let n=0;for(const e in r){const t=r[e],i=this._touches[e];i&&(a._add(t),s._add(t.sub(i)),n++,r[e]=t);}if(this._touches=r,this._shouldBePrevented(n)||!s.mag())return;const l=s.div(n);return this._sum._add(l),this._sum.mag()<this._clickTolerance?void 0:{around:a.div(n),panDelta:l}}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class aa{constructor(){this.reset();}reset(){this._active=!1,delete this._firstTwoTouches;}touchstart(e,t,i){this._firstTwoTouches||i.length<2||(this._firstTwoTouches=[i[0].identifier,i[1].identifier],this._start([t[0],t[1]]));}touchmove(e,t,i){if(!this._firstTwoTouches)return;e.preventDefault();const[o,r]=this._firstTwoTouches,a=sa(i,t,o),s=sa(i,t,r);if(!a||!s)return;const n=this._aroundCenter?null:a.add(s).div(2);return this._move([a,s],n,e)}touchend(e,t,i){if(!this._firstTwoTouches)return;const[o,r]=this._firstTwoTouches,a=sa(i,t,o),s=sa(i,t,r);a&&s||(this._active&&n.suppressClick(),this.reset());}touchcancel(){this.reset();}enable(e){this._enabled=!0,this._aroundCenter=!!e&&\"center\"===e.around;}disable(){this._enabled=!1,this.reset();}isEnabled(){return !!this._enabled}isActive(){return !!this._active}}function sa(e,t,i){for(let o=0;o<e.length;o++)if(e[o].identifier===i)return t[o]}function na(e,t){return Math.log(e/t)/Math.LN2}class la extends aa{reset(){super.reset(),delete this._distance,delete this._startDistance;}_start(e){this._startDistance=this._distance=e[0].dist(e[1]);}_move(e,t){const i=this._distance;if(this._distance=e[0].dist(e[1]),this._active||!(Math.abs(na(this._distance,this._startDistance))<.1))return this._active=!0,{zoomDelta:na(this._distance,i),pinchAround:t}}}function ca(e,t){return 180*e.angleWith(t)/Math.PI}class ha extends aa{reset(){super.reset(),delete this._minDiameter,delete this._startVector,delete this._vector;}_start(e){this._startVector=this._vector=e[0].sub(e[1]),this._minDiameter=e[0].dist(e[1]);}_move(e,t,i){const o=this._vector;if(this._vector=e[0].sub(e[1]),this._active||!this._isBelowThreshold(this._vector))return this._active=!0,{bearingDelta:ca(this._vector,o),pinchAround:t}}_isBelowThreshold(e){this._minDiameter=Math.min(this._minDiameter,e.mag());const t=25/(Math.PI*this._minDiameter)*360,i=ca(e,this._startVector);return Math.abs(i)<t}}function ua(e){return Math.abs(e.y)>Math.abs(e.x)}class da extends aa{constructor(e){super(),this._currentTouchCount=0,this._map=e;}reset(){super.reset(),this._valid=void 0,delete this._firstMove,delete this._lastPoints;}touchstart(e,t,i){super.touchstart(e,t,i),this._currentTouchCount=i.length;}_start(e){this._lastPoints=e,ua(e[0].sub(e[1]))&&(this._valid=!1);}_move(e,t,i){if(this._map.cooperativeGestures.isEnabled()&&this._currentTouchCount<3)return;const o=e[0].sub(this._lastPoints[0]),r=e[1].sub(this._lastPoints[1]);return this._valid=this.gestureBeginsVertically(o,r,i.timeStamp),this._valid?(this._lastPoints=e,this._active=!0,{pitchDelta:(o.y+r.y)/2*-.5}):void 0}gestureBeginsVertically(e,t,i){if(void 0!==this._valid)return this._valid;const o=e.mag()>=2,r=t.mag()>=2;if(!o&&!r)return;if(!o||!r)return void 0===this._firstMove&&(this._firstMove=i),i-this._firstMove<100&&void 0;const a=e.y>0==t.y>0;return ua(e)&&ua(t)&&a}}const _a={panStep:100,bearingStep:15,pitchStep:10};class pa{constructor(e){this._tr=new Vr(e);const t=_a;this._panStep=t.panStep,this._bearingStep=t.bearingStep,this._pitchStep=t.pitchStep,this._rotationDisabled=!1;}reset(){this._active=!1;}keydown(e){if(e.altKey||e.ctrlKey||e.metaKey)return;let t=0,i=0,o=0,r=0,a=0;switch(e.keyCode){case 61:case 107:case 171:case 187:t=1;break;case 189:case 109:case 173:t=-1;break;case 37:e.shiftKey?i=-1:(e.preventDefault(),r=-1);break;case 39:e.shiftKey?i=1:(e.preventDefault(),r=1);break;case 38:e.shiftKey?o=1:(e.preventDefault(),a=-1);break;case 40:e.shiftKey?o=-1:(e.preventDefault(),a=1);break;default:return}return this._rotationDisabled&&(i=0,o=0),{cameraAnimation:s=>{const n=this._tr;s.easeTo({duration:300,easeId:\"keyboardHandler\",easing:ma,zoom:t?Math.round(n.zoom)+t*(e.shiftKey?2:1):n.zoom,bearing:n.bearing+i*this._bearingStep,pitch:n.pitch+o*this._pitchStep,offset:[-r*this._panStep,-a*this._panStep],center:n.center},{originalEvent:e});}}}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}disableRotation(){this._rotationDisabled=!0;}enableRotation(){this._rotationDisabled=!1;}}function ma(e){return e*(2-e)}const fa=4.000244140625,ga=1/450;class va{constructor(e,t){this._onTimeout=e=>{this._type=\"wheel\",this._delta-=this._lastValue,this._active||this._start(e);},this._map=e,this._tr=new Vr(e),this._triggerRenderFrame=t,this._delta=0,this._defaultZoomRate=.01,this._wheelZoomRate=ga;}setZoomRate(e){this._defaultZoomRate=e;}setWheelZoomRate(e){this._wheelZoomRate=e;}isEnabled(){return !!this._enabled}isActive(){return !!this._active||void 0!==this._finishTimeout}isZooming(){return !!this._zooming}enable(e){this.isEnabled()||(this._enabled=!0,this._aroundCenter=!!e&&\"center\"===e.around);}disable(){this.isEnabled()&&(this._enabled=!1);}_shouldBePrevented(e){return !!this._map.cooperativeGestures.isEnabled()&&!(e.ctrlKey||this._map.cooperativeGestures.isBypassed(e))}wheel(e){if(!this.isEnabled())return;if(this._shouldBePrevented(e))return void this._map.cooperativeGestures.notifyGestureBlocked(\"wheel_zoom\",e);let t=e.deltaMode===WheelEvent.DOM_DELTA_LINE?40*e.deltaY:e.deltaY;const i=s.now(),o=i-(this._lastWheelEventTime||0);this._lastWheelEventTime=i,0!==t&&t%fa==0?this._type=\"wheel\":0!==t&&Math.abs(t)<4?this._type=\"trackpad\":o>400?(this._type=null,this._lastValue=t,this._timeout=setTimeout(this._onTimeout,40,e)):this._type||(this._type=Math.abs(o*t)<200?\"trackpad\":\"wheel\",this._timeout&&(clearTimeout(this._timeout),this._timeout=null,t+=this._lastValue)),e.shiftKey&&t&&(t/=4),this._type&&(this._lastWheelEvent=e,this._delta-=t,this._active||this._start(e)),e.preventDefault();}_start(e){if(!this._delta)return;this._frameId&&(this._frameId=null),this._active=!0,this.isZooming()||(this._zooming=!0),this._finishTimeout&&(clearTimeout(this._finishTimeout),delete this._finishTimeout);const i=n.mousePos(this._map.getCanvas(),e),o=this._tr;this._aroundPoint=this._aroundCenter?o.transform.locationToScreenPoint(t.S.convert(o.center)):i,this._frameId||(this._frameId=!0,this._triggerRenderFrame());}renderFrame(){if(!this._frameId)return;if(this._frameId=null,!this.isActive())return;const e=this._tr.transform;if(\"number\"==typeof this._lastExpectedZoom){const t=e.zoom-this._lastExpectedZoom;\"number\"==typeof this._startZoom&&(this._startZoom+=t),\"number\"==typeof this._targetZoom&&(this._targetZoom+=t);}if(0!==this._delta){const i=\"wheel\"===this._type&&Math.abs(this._delta)>fa?this._wheelZoomRate:this._defaultZoomRate;let o=2/(1+Math.exp(-Math.abs(this._delta*i)));this._delta<0&&0!==o&&(o=1/o);const r=\"number\"!=typeof this._targetZoom?e.scale:t.ae(this._targetZoom);this._targetZoom=e.getConstrained(e.getCameraLngLat(),t.aj(r*o)).zoom,\"wheel\"===this._type&&(this._startZoom=e.zoom,this._easing=this._smoothOutEasing(200)),this._delta=0;}const i=\"number\"!=typeof this._targetZoom?e.zoom:this._targetZoom,o=this._startZoom,r=this._easing;let a,n=!1;if(\"wheel\"===this._type&&o&&r){const e=s.now()-this._lastWheelEventTime,l=Math.min((e+5)/200,1),c=r(l);a=t.C.number(o,i,c),l<1?this._frameId||(this._frameId=!0):n=!0;}else a=i,n=!0;return this._active=!0,n&&(this._active=!1,this._finishTimeout=setTimeout((()=>{this._zooming=!1,this._triggerRenderFrame(),delete this._targetZoom,delete this._lastExpectedZoom,delete this._finishTimeout;}),200)),this._lastExpectedZoom=a,{noInertia:!0,needsRenderFrame:!n,zoomDelta:a-e.zoom,around:this._aroundPoint,originalEvent:this._lastWheelEvent}}_smoothOutEasing(e){let i=t.cn;if(this._prevEase){const e=this._prevEase,o=(s.now()-e.start)/e.duration,r=e.easing(o+.01)-e.easing(o),a=.27/Math.sqrt(r*r+1e-4)*.01,n=Math.sqrt(.0729-a*a);i=t.cl(a,n,.25,1);}return this._prevEase={start:s.now(),duration:e,easing:i},i}reset(){this._active=!1,this._zooming=!1,delete this._targetZoom,delete this._lastExpectedZoom,this._finishTimeout&&(clearTimeout(this._finishTimeout),delete this._finishTimeout);}}class ba{constructor(e,t){this._clickZoom=e,this._tapZoom=t;}enable(){this._clickZoom.enable(),this._tapZoom.enable();}disable(){this._clickZoom.disable(),this._tapZoom.disable();}isEnabled(){return this._clickZoom.isEnabled()&&this._tapZoom.isEnabled()}isActive(){return this._clickZoom.isActive()||this._tapZoom.isActive()}}class xa{constructor(e){this._tr=new Vr(e),this.reset();}reset(){this._active=!1;}dblclick(e,t){return e.preventDefault(),{cameraAnimation:i=>{i.easeTo({duration:300,zoom:this._tr.zoom+(e.shiftKey?-1:1),around:this._tr.unproject(t)},{originalEvent:e});}}}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class ya{constructor(){this._tap=new Hr({numTouches:1,numTaps:1}),this.reset();}reset(){this._active=!1,delete this._swipePoint,delete this._swipeTouch,delete this._tapTime,delete this._tapPoint,this._tap.reset();}touchstart(e,t,i){if(!this._swipePoint)if(this._tapTime){const o=t[0],r=e.timeStamp-this._tapTime<500,a=this._tapPoint.dist(o)<30;r&&a?i.length>0&&(this._swipePoint=o,this._swipeTouch=i[0].identifier):this.reset();}else this._tap.touchstart(e,t,i);}touchmove(e,t,i){if(this._tapTime){if(this._swipePoint){if(i[0].identifier!==this._swipeTouch)return;const o=t[0],r=o.y-this._swipePoint.y;return this._swipePoint=o,e.preventDefault(),this._active=!0,{zoomDelta:r/128}}}else this._tap.touchmove(e,t,i);}touchend(e,t,i){if(this._tapTime)this._swipePoint&&0===i.length&&this.reset();else {const o=this._tap.touchend(e,t,i);o&&(this._tapTime=e.timeStamp,this._tapPoint=o);}}touchcancel(){this.reset();}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class wa{constructor(e,t,i){this._el=e,this._mousePan=t,this._touchPan=i;}enable(e){this._inertiaOptions=e||{},this._mousePan.enable(),this._touchPan.enable(),this._el.classList.add(\"maplibregl-touch-drag-pan\");}disable(){this._mousePan.disable(),this._touchPan.disable(),this._el.classList.remove(\"maplibregl-touch-drag-pan\");}isEnabled(){return this._mousePan.isEnabled()&&this._touchPan.isEnabled()}isActive(){return this._mousePan.isActive()||this._touchPan.isActive()}}class Ta{constructor(e,t,i,o){this._pitchWithRotate=e.pitchWithRotate,this._rollEnabled=e.rollEnabled,this._mouseRotate=t,this._mousePitch=i,this._mouseRoll=o;}enable(){this._mouseRotate.enable(),this._pitchWithRotate&&this._mousePitch.enable(),this._rollEnabled&&this._mouseRoll.enable();}disable(){this._mouseRotate.disable(),this._mousePitch.disable(),this._mouseRoll.disable();}isEnabled(){return this._mouseRotate.isEnabled()&&(!this._pitchWithRotate||this._mousePitch.isEnabled())&&(!this._rollEnabled||this._mouseRoll.isEnabled())}isActive(){return this._mouseRotate.isActive()||this._mousePitch.isActive()||this._mouseRoll.isActive()}}class Pa{constructor(e,t,i,o){this._el=e,this._touchZoom=t,this._touchRotate=i,this._tapDragZoom=o,this._rotationDisabled=!1,this._enabled=!0;}enable(e){this._touchZoom.enable(e),this._rotationDisabled||this._touchRotate.enable(e),this._tapDragZoom.enable(),this._el.classList.add(\"maplibregl-touch-zoom-rotate\");}disable(){this._touchZoom.disable(),this._touchRotate.disable(),this._tapDragZoom.disable(),this._el.classList.remove(\"maplibregl-touch-zoom-rotate\");}isEnabled(){return this._touchZoom.isEnabled()&&(this._rotationDisabled||this._touchRotate.isEnabled())&&this._tapDragZoom.isEnabled()}isActive(){return this._touchZoom.isActive()||this._touchRotate.isActive()||this._tapDragZoom.isActive()}disableRotation(){this._rotationDisabled=!0,this._touchRotate.disable();}enableRotation(){this._rotationDisabled=!1,this._touchZoom.isEnabled()&&this._touchRotate.enable();}}class Ca{constructor(e,t){this._bypassKey=-1!==navigator.userAgent.indexOf(\"Mac\")?\"metaKey\":\"ctrlKey\",this._map=e,this._options=t,this._enabled=!1;}isActive(){return !1}reset(){}_setupUI(){if(this._container)return;const e=this._map.getCanvasContainer();e.classList.add(\"maplibregl-cooperative-gestures\"),this._container=n.create(\"div\",\"maplibregl-cooperative-gesture-screen\",e);let t=this._map._getUIString(\"CooperativeGesturesHandler.WindowsHelpText\");\"metaKey\"===this._bypassKey&&(t=this._map._getUIString(\"CooperativeGesturesHandler.MacHelpText\"));const i=this._map._getUIString(\"CooperativeGesturesHandler.MobileHelpText\"),o=document.createElement(\"div\");o.className=\"maplibregl-desktop-message\",o.textContent=t,this._container.appendChild(o);const r=document.createElement(\"div\");r.className=\"maplibregl-mobile-message\",r.textContent=i,this._container.appendChild(r),this._container.setAttribute(\"aria-hidden\",\"true\");}_destroyUI(){this._container&&(n.remove(this._container),this._map.getCanvasContainer().classList.remove(\"maplibregl-cooperative-gestures\")),delete this._container;}enable(){this._setupUI(),this._enabled=!0;}disable(){this._enabled=!1,this._destroyUI();}isEnabled(){return this._enabled}isBypassed(e){return e[this._bypassKey]}notifyGestureBlocked(e,i){this._enabled&&(this._map.fire(new t.l(\"cooperativegestureprevented\",{gestureType:e,originalEvent:i})),this._container.classList.add(\"maplibregl-show\"),setTimeout((()=>{this._container.classList.remove(\"maplibregl-show\");}),100));}}const Ia=e=>e.zoom||e.drag||e.roll||e.pitch||e.rotate;class Ma extends t.l{}function Sa(e){return e.panDelta&&e.panDelta.mag()||e.zoomDelta||e.bearingDelta||e.pitchDelta||e.rollDelta}class Ea{constructor(e,i){this.handleWindowEvent=e=>{this.handleEvent(e,`${e.type}Window`);},this.handleEvent=(e,i)=>{if(\"blur\"===e.type)return void this.stop(!0);this._updatingCamera=!0;const o=\"renderFrame\"===e.type?void 0:e,r={needsRenderFrame:!1},a={},s={};for(const{handlerName:l,handler:c,allowed:h}of this._handlers){if(!c.isEnabled())continue;let u;if(this._blockedByActive(s,h,l))c.reset();else if(c[i||e.type]){if(t.co(e,i||e.type)){const t=n.mousePos(this._map.getCanvas(),e);u=c[i||e.type](e,t);}else if(t.cp(e,i||e.type)){const t=this._getMapTouches(e.touches),o=n.touchPos(this._map.getCanvas(),t);u=c[i||e.type](e,o,t);}else t.cq(i||e.type)||(u=c[i||e.type](e));this.mergeHandlerResult(r,a,u,l,o),u&&u.needsRenderFrame&&this._triggerRenderFrame();}(u||c.isActive())&&(s[l]=c);}const l={};for(const e in this._previousActiveHandlers)s[e]||(l[e]=o);this._previousActiveHandlers=s,(Object.keys(l).length||Sa(r))&&(this._changes.push([r,a,l]),this._triggerRenderFrame()),(Object.keys(s).length||Sa(r))&&this._map._stop(!0),this._updatingCamera=!1;const{cameraAnimation:c}=r;c&&(this._inertia.clear(),this._fireEvents({},{},!0),this._changes=[],c(this._map));},this._map=e,this._el=this._map.getCanvasContainer(),this._handlers=[],this._handlersById={},this._changes=[],this._inertia=new Fr(e),this._bearingSnap=i.bearingSnap,this._previousActiveHandlers={},this._eventsInProgress={},this._addDefaultHandlers(i);const o=this._el;this._listeners=[[o,\"touchstart\",{passive:!0}],[o,\"touchmove\",{passive:!1}],[o,\"touchend\",void 0],[o,\"touchcancel\",void 0],[o,\"mousedown\",void 0],[o,\"mousemove\",void 0],[o,\"mouseup\",void 0],[document,\"mousemove\",{capture:!0}],[document,\"mouseup\",void 0],[o,\"mouseover\",void 0],[o,\"mouseout\",void 0],[o,\"dblclick\",void 0],[o,\"click\",void 0],[o,\"keydown\",{capture:!1}],[o,\"keyup\",void 0],[o,\"wheel\",{passive:!1}],[o,\"contextmenu\",void 0],[window,\"blur\",void 0]];for(const[e,t,i]of this._listeners)n.addEventListener(e,t,e===document?this.handleWindowEvent:this.handleEvent,i);}destroy(){for(const[e,t,i]of this._listeners)n.removeEventListener(e,t,e===document?this.handleWindowEvent:this.handleEvent,i);}_addDefaultHandlers(e){const i=this._map,o=i.getCanvasContainer();this._add(\"mapEvent\",new Ur(i,e));const r=i.boxZoom=new $r(i,e);this._add(\"boxZoom\",r),e.interactive&&e.boxZoom&&r.enable();const a=i.cooperativeGestures=new Ca(i,e.cooperativeGestures);this._add(\"cooperativeGestures\",a),e.cooperativeGestures&&a.enable();const s=new Xr(i),l=new xa(i);i.doubleClickZoom=new ba(l,s),this._add(\"tapZoom\",s),this._add(\"clickZoom\",l),e.interactive&&e.doubleClickZoom&&i.doubleClickZoom.enable();const c=new ya;this._add(\"tapDragZoom\",c);const h=i.touchPitch=new da(i);this._add(\"touchPitch\",h),e.interactive&&e.touchPitch&&i.touchPitch.enable(e.touchPitch);const u=()=>i.project(i.getCenter()),d=function({enable:e,clickTolerance:i,aroundCenter:o=!0,minPixelCenterThreshold:r=100,rotateDegreesPerPixelMoved:a=.8},s){const l=new ea({checkCorrectEvent:e=>0===n.mouseButton(e)&&e.ctrlKey||2===n.mouseButton(e)&&!e.ctrlKey});return new Kr({clickTolerance:i,move:(e,i)=>{const n=s();if(o&&Math.abs(n.y-e.y)>r)return {bearingDelta:t.cm(new t.P(e.x,i.y),i,n)};let l=(i.x-e.x)*a;return o&&i.y<n.y&&(l=-l),{bearingDelta:l}},moveStateManager:l,enable:e,assignEvents:oa})}(e,u),_=function({enable:e,clickTolerance:t,pitchDegreesPerPixelMoved:i=-.5}){const o=new ea({checkCorrectEvent:e=>0===n.mouseButton(e)&&e.ctrlKey||2===n.mouseButton(e)});return new Kr({clickTolerance:t,move:(e,t)=>({pitchDelta:(t.y-e.y)*i}),moveStateManager:o,enable:e,assignEvents:oa})}(e),p=function({enable:e,clickTolerance:t,rollDegreesPerPixelMoved:i=.3},o){const r=new ea({checkCorrectEvent:e=>2===n.mouseButton(e)&&e.ctrlKey});return new Kr({clickTolerance:t,move:(e,t)=>{const r=o();let a=(t.x-e.x)*i;return t.y<r.y&&(a=-a),{rollDelta:a}},moveStateManager:r,enable:e,assignEvents:oa})}(e,u);i.dragRotate=new Ta(e,d,_,p),this._add(\"mouseRotate\",d,[\"mousePitch\"]),this._add(\"mousePitch\",_,[\"mouseRotate\",\"mouseRoll\"]),this._add(\"mouseRoll\",p,[\"mousePitch\"]),e.interactive&&e.dragRotate&&i.dragRotate.enable();const m=function({enable:e,clickTolerance:t}){const i=new ea({checkCorrectEvent:e=>0===n.mouseButton(e)&&!e.ctrlKey});return new Kr({clickTolerance:t,move:(e,t)=>({around:t,panDelta:t.sub(e)}),activateOnStart:!0,moveStateManager:i,enable:e,assignEvents:oa})}(e),f=new ra(e,i);i.dragPan=new wa(o,m,f),this._add(\"mousePan\",m),this._add(\"touchPan\",f,[\"touchZoom\",\"touchRotate\"]),e.interactive&&e.dragPan&&i.dragPan.enable(e.dragPan);const g=new ha,v=new la;i.touchZoomRotate=new Pa(o,v,g,c),this._add(\"touchRotate\",g,[\"touchPan\",\"touchZoom\"]),this._add(\"touchZoom\",v,[\"touchPan\",\"touchRotate\"]),e.interactive&&e.touchZoomRotate&&i.touchZoomRotate.enable(e.touchZoomRotate);const b=i.scrollZoom=new va(i,(()=>this._triggerRenderFrame()));this._add(\"scrollZoom\",b,[\"mousePan\"]),e.interactive&&e.scrollZoom&&i.scrollZoom.enable(e.scrollZoom);const x=i.keyboard=new pa(i);this._add(\"keyboard\",x),e.interactive&&e.keyboard&&i.keyboard.enable(),this._add(\"blockableMapEvent\",new Gr(i));}_add(e,t,i){this._handlers.push({handlerName:e,handler:t,allowed:i}),this._handlersById[e]=t;}stop(e){if(!this._updatingCamera){for(const{handler:e}of this._handlers)e.reset();this._inertia.clear(),this._fireEvents({},{},e),this._changes=[];}}isActive(){for(const{handler:e}of this._handlers)if(e.isActive())return !0;return !1}isZooming(){return !!this._eventsInProgress.zoom||this._map.scrollZoom.isZooming()}isRotating(){return !!this._eventsInProgress.rotate}isMoving(){return Boolean(Ia(this._eventsInProgress))||this.isZooming()}_blockedByActive(e,t,i){for(const o in e)if(o!==i&&(!t||t.indexOf(o)<0))return !0;return !1}_getMapTouches(e){const t=[];for(const i of e)this._el.contains(i.target)&&t.push(i);return t}mergeHandlerResult(e,i,o,r,a){if(!o)return;t.e(e,o);const s={handlerName:r,originalEvent:o.originalEvent||a};void 0!==o.zoomDelta&&(i.zoom=s),void 0!==o.panDelta&&(i.drag=s),void 0!==o.rollDelta&&(i.roll=s),void 0!==o.pitchDelta&&(i.pitch=s),void 0!==o.bearingDelta&&(i.rotate=s);}_applyChanges(){const e={},i={},o={};for(const[r,a,s]of this._changes)r.panDelta&&(e.panDelta=(e.panDelta||new t.P(0,0))._add(r.panDelta)),r.zoomDelta&&(e.zoomDelta=(e.zoomDelta||0)+r.zoomDelta),r.bearingDelta&&(e.bearingDelta=(e.bearingDelta||0)+r.bearingDelta),r.pitchDelta&&(e.pitchDelta=(e.pitchDelta||0)+r.pitchDelta),r.rollDelta&&(e.rollDelta=(e.rollDelta||0)+r.rollDelta),void 0!==r.around&&(e.around=r.around),void 0!==r.pinchAround&&(e.pinchAround=r.pinchAround),r.noInertia&&(e.noInertia=r.noInertia),t.e(i,a),t.e(o,s);this._updateMapTransform(e,i,o),this._changes=[];}_updateMapTransform(e,t,i){const o=this._map,r=o._getTransformForUpdate(),a=o.terrain;if(!(Sa(e)||a&&this._terrainMovement))return this._fireEvents(t,i,!0);o._stop(!0);let{panDelta:s,zoomDelta:n,bearingDelta:l,pitchDelta:c,rollDelta:h,around:u,pinchAround:d}=e;void 0!==d&&(u=d),u=u||o.transform.centerPoint,a&&!r.isPointOnMapSurface(u)&&(u=r.centerPoint);const _={panDelta:s,zoomDelta:n,rollDelta:h,pitchDelta:c,bearingDelta:l,around:u};this._map.cameraHelper.useGlobeControls&&!r.isPointOnMapSurface(u)&&(u=r.centerPoint);const p=u.distSqr(r.centerPoint)<.01?r.center:r.screenPointToLocation(s?u.sub(s):u);a?(this._map.cameraHelper.handleMapControlsRollPitchBearingZoom(_,r),this._terrainMovement||!t.drag&&!t.zoom?t.drag&&this._terrainMovement?r.setCenter(r.screenPointToLocation(r.centerPoint.sub(s))):this._map.cameraHelper.handleMapControlsPan(_,r,p):(this._terrainMovement=!0,this._map._elevationFreeze=!0,this._map.cameraHelper.handleMapControlsPan(_,r,p))):(this._map.cameraHelper.handleMapControlsRollPitchBearingZoom(_,r),this._map.cameraHelper.handleMapControlsPan(_,r,p)),o._applyUpdatedTransform(r),this._map._update(),e.noInertia||this._inertia.record(e),this._fireEvents(t,i,!0);}_fireEvents(e,i,o){const r=Ia(this._eventsInProgress),a=Ia(e),n={};for(const t in e){const{originalEvent:i}=e[t];this._eventsInProgress[t]||(n[`${t}start`]=i),this._eventsInProgress[t]=e[t];}!r&&a&&this._fireEvent(\"movestart\",a.originalEvent);for(const e in n)this._fireEvent(e,n[e]);a&&this._fireEvent(\"move\",a.originalEvent);for(const t in e){const{originalEvent:i}=e[t];this._fireEvent(t,i);}const l={};let c;for(const e in this._eventsInProgress){const{handlerName:t,originalEvent:o}=this._eventsInProgress[e];this._handlersById[t].isActive()||(delete this._eventsInProgress[e],c=i[t]||o,l[`${e}end`]=c);}for(const e in l)this._fireEvent(e,l[e]);const h=Ia(this._eventsInProgress),u=(r||a)&&!h;if(u&&this._terrainMovement){this._map._elevationFreeze=!1,this._terrainMovement=!1;const e=this._map._getTransformForUpdate();this._map.getCenterClampedToGround()&&e.recalculateZoomAndCenter(this._map.terrain),this._map._applyUpdatedTransform(e);}if(o&&u){this._updatingCamera=!0;const e=this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions),i=e=>0!==e&&-this._bearingSnap<e&&e<this._bearingSnap;!e||!e.essential&&s.prefersReducedMotion?(this._map.fire(new t.l(\"moveend\",{originalEvent:c})),i(this._map.getBearing())&&this._map.resetNorth()):(i(e.bearing||this._map.getBearing())&&(e.bearing=0),e.freezeElevation=!0,this._map.easeTo(e,{originalEvent:c})),this._updatingCamera=!1;}}_fireEvent(e,i){this._map.fire(new t.l(e,i?{originalEvent:i}:{}));}_requestFrame(){return this._map.triggerRepaint(),this._map._renderTaskQueue.add((e=>{delete this._frameId,this.handleEvent(new Ma(\"renderFrame\",{timeStamp:e})),this._applyChanges();}))}_triggerRenderFrame(){void 0===this._frameId&&(this._frameId=this._requestFrame());}}class Ra extends t.E{constructor(e,t,i){super(),this._renderFrameCallback=()=>{const e=Math.min((s.now()-this._easeStart)/this._easeOptions.duration,1);this._onEaseFrame(this._easeOptions.easing(e)),e<1&&this._easeFrameId?this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback):this.stop();},this._moving=!1,this._zooming=!1,this.transform=e,this._bearingSnap=i.bearingSnap,this.cameraHelper=t,this.on(\"moveend\",(()=>{delete this._requestedCameraState;}));}migrateProjection(e,t){e.apply(this.transform),this.transform=e,this.cameraHelper=t;}getCenter(){return new t.S(this.transform.center.lng,this.transform.center.lat)}setCenter(e,t){return this.jumpTo({center:e},t)}getCenterElevation(){return this.transform.elevation}setCenterElevation(e,t){return this.jumpTo({elevation:e},t),this}getCenterClampedToGround(){return this._centerClampedToGround}setCenterClampedToGround(e){this._centerClampedToGround=e;}panBy(e,i,o){return e=t.P.convert(e).mult(-1),this.panTo(this.transform.center,t.e({offset:e},i),o)}panTo(e,i,o){return this.easeTo(t.e({center:e},i),o)}getZoom(){return this.transform.zoom}setZoom(e,t){return this.jumpTo({zoom:e},t),this}zoomTo(e,i,o){return this.easeTo(t.e({zoom:e},i),o)}zoomIn(e,t){return this.zoomTo(this.getZoom()+1,e,t),this}zoomOut(e,t){return this.zoomTo(this.getZoom()-1,e,t),this}getVerticalFieldOfView(){return this.transform.fov}setVerticalFieldOfView(e,i){return e!=this.transform.fov&&(this.transform.setFov(e),this.fire(new t.l(\"movestart\",i)).fire(new t.l(\"move\",i)).fire(new t.l(\"moveend\",i))),this}getBearing(){return this.transform.bearing}setBearing(e,t){return this.jumpTo({bearing:e},t),this}getPadding(){return this.transform.padding}setPadding(e,t){return this.jumpTo({padding:e},t),this}rotateTo(e,i,o){return this.easeTo(t.e({bearing:e},i),o)}resetNorth(e,i){return this.rotateTo(0,t.e({duration:1e3},e),i),this}resetNorthPitch(e,i){return this.easeTo(t.e({bearing:0,pitch:0,roll:0,duration:1e3},e),i),this}snapToNorth(e,t){return Math.abs(this.getBearing())<this._bearingSnap?this.resetNorth(e,t):this}getPitch(){return this.transform.pitch}setPitch(e,t){return this.jumpTo({pitch:e},t),this}getRoll(){return this.transform.roll}setRoll(e,t){return this.jumpTo({roll:e},t),this}cameraForBounds(e,t){e=G.convert(e).adjustAntiMeridian();const i=t&&t.bearing||0;return this._cameraForBoxAndBearing(e.getNorthWest(),e.getSouthEast(),i,t)}_cameraForBoxAndBearing(e,i,o,r){const a={top:0,bottom:0,right:0,left:0};if(\"number\"==typeof(r=t.e({padding:a,offset:[0,0],maxZoom:this.transform.maxZoom},r)).padding){const e=r.padding;r.padding={top:e,bottom:e,right:e,left:e};}const s=t.e(a,r.padding);r.padding=s;const n=this.transform,l=new G(e,i);return this.cameraHelper.cameraForBoxAndBearing(r,s,l,o,n)}fitBounds(e,t,i){return this._fitInternal(this.cameraForBounds(e,t),t,i)}fitScreenCoordinates(e,i,o,r,a){return this._fitInternal(this._cameraForBoxAndBearing(this.transform.screenPointToLocation(t.P.convert(e)),this.transform.screenPointToLocation(t.P.convert(i)),o,r),r,a)}_fitInternal(e,i,o){return e?(delete(i=t.e(e,i)).padding,i.linear?this.easeTo(i,o):this.flyTo(i,o)):this}jumpTo(e,i){this.stop();const o=this._getTransformForUpdate();let r=!1,a=!1,s=!1;const n=o.zoom;this.cameraHelper.handleJumpToCenterZoom(o,e);const l=o.zoom!==n;return \"elevation\"in e&&o.elevation!==+e.elevation&&o.setElevation(+e.elevation),\"bearing\"in e&&o.bearing!==+e.bearing&&(r=!0,o.setBearing(+e.bearing)),\"pitch\"in e&&o.pitch!==+e.pitch&&(a=!0,o.setPitch(+e.pitch)),\"roll\"in e&&o.roll!==+e.roll&&(s=!0,o.setRoll(+e.roll)),null==e.padding||o.isPaddingEqual(e.padding)||o.setPadding(e.padding),this._applyUpdatedTransform(o),this.fire(new t.l(\"movestart\",i)).fire(new t.l(\"move\",i)),l&&this.fire(new t.l(\"zoomstart\",i)).fire(new t.l(\"zoom\",i)).fire(new t.l(\"zoomend\",i)),r&&this.fire(new t.l(\"rotatestart\",i)).fire(new t.l(\"rotate\",i)).fire(new t.l(\"rotateend\",i)),a&&this.fire(new t.l(\"pitchstart\",i)).fire(new t.l(\"pitch\",i)).fire(new t.l(\"pitchend\",i)),s&&this.fire(new t.l(\"rollstart\",i)).fire(new t.l(\"roll\",i)).fire(new t.l(\"rollend\",i)),this.fire(new t.l(\"moveend\",i))}calculateCameraOptionsFromTo(e,i,o,r=0){const a=t.a0.fromLngLat(e,i),s=t.a0.fromLngLat(o,r),n=s.x-a.x,l=s.y-a.y,c=s.z-a.z,h=Math.hypot(n,l,c);if(0===h)throw new Error(\"Can't calculate camera options with same From and To\");const u=Math.hypot(n,l),d=t.aj(this.transform.cameraToCenterDistance/h/this.transform.tileSize),_=180*Math.atan2(n,-l)/Math.PI;let p=180*Math.acos(u/h)/Math.PI;return p=c<0?90-p:90+p,{center:s.toLngLat(),elevation:r,zoom:d,pitch:p,bearing:_}}calculateCameraOptionsFromCameraLngLatAltRotation(e,t,i,o,r){const a=this.transform.calculateCenterFromCameraLngLatAlt(e,t,i,o);return {center:a.center,elevation:a.elevation,zoom:a.zoom,bearing:i,pitch:o,roll:r}}easeTo(e,i){this._stop(!1,e.easeId),(!1===(e=t.e({offset:[0,0],duration:500,easing:t.cn},e)).animate||!e.essential&&s.prefersReducedMotion)&&(e.duration=0);const o=this._getTransformForUpdate(),r=this.getBearing(),a=o.pitch,n=o.roll,l=\"bearing\"in e?this._normalizeBearing(e.bearing,r):r,c=\"pitch\"in e?+e.pitch:a,h=\"roll\"in e?this._normalizeBearing(e.roll,n):n,u=\"padding\"in e?e.padding:o.padding,d=t.P.convert(e.offset);let _,p;e.around&&(_=t.S.convert(e.around),p=o.locationToScreenPoint(_));const m={moving:this._moving,zooming:this._zooming,rotating:this._rotating,pitching:this._pitching,rolling:this._rolling},f=this.cameraHelper.handleEaseTo(o,{bearing:l,pitch:c,roll:h,padding:u,around:_,aroundPoint:p,offsetAsPoint:d,offset:e.offset,zoom:e.zoom,center:e.center});return this._rotating=this._rotating||r!==l,this._pitching=this._pitching||c!==a,this._rolling=this._rolling||h!==n,this._padding=!o.isPaddingEqual(u),this._zooming=this._zooming||f.isZooming,this._easeId=e.easeId,this._prepareEase(i,e.noMoveStart,m),this.terrain&&this._prepareElevation(f.elevationCenter),this._ease((t=>{f.easeFunc(t),this.terrain&&!e.freezeElevation&&this._updateElevation(t),this._applyUpdatedTransform(o),this._fireMoveEvents(i);}),(t=>{this.terrain&&e.freezeElevation&&this._finalizeElevation(),this._afterEase(i,t);}),e),this}_prepareEase(e,i,o={}){this._moving=!0,i||o.moving||this.fire(new t.l(\"movestart\",e)),this._zooming&&!o.zooming&&this.fire(new t.l(\"zoomstart\",e)),this._rotating&&!o.rotating&&this.fire(new t.l(\"rotatestart\",e)),this._pitching&&!o.pitching&&this.fire(new t.l(\"pitchstart\",e)),this._rolling&&!o.rolling&&this.fire(new t.l(\"rollstart\",e));}_prepareElevation(e){this._elevationCenter=e,this._elevationStart=this.transform.elevation,this._elevationTarget=this.terrain.getElevationForLngLatZoom(e,this.transform.tileZoom),this._elevationFreeze=!0;}_updateElevation(e){this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter,this.transform.tileZoom));const i=this.terrain.getElevationForLngLatZoom(this._elevationCenter,this.transform.tileZoom);if(e<1&&i!==this._elevationTarget){const t=this._elevationTarget-this._elevationStart;this._elevationStart+=e*(t-(i-(t*e+this._elevationStart))/(1-e)),this._elevationTarget=i;}this.transform.setElevation(t.C.number(this._elevationStart,this._elevationTarget,e));}_finalizeElevation(){this._elevationFreeze=!1,this.getCenterClampedToGround()&&this.transform.recalculateZoomAndCenter(this.terrain);}_getTransformForUpdate(){return this.transformCameraUpdate||this.terrain?(this._requestedCameraState||(this._requestedCameraState=this.transform.clone()),this._requestedCameraState):this.transform}_elevateCameraIfInsideTerrain(e){if(!this.terrain&&e.elevation>=0&&e.pitch<=90)return {};const t=e.getCameraLngLat(),i=e.getCameraAltitude(),o=this.terrain?this.terrain.getElevationForLngLatZoom(t,e.zoom):0;if(i<o){const i=this.calculateCameraOptionsFromTo(t,o,e.center,e.elevation);return {pitch:i.pitch,zoom:i.zoom}}return {}}_applyUpdatedTransform(e){const t=[];if(t.push((e=>this._elevateCameraIfInsideTerrain(e))),this.transformCameraUpdate&&t.push((e=>this.transformCameraUpdate(e))),!t.length)return;const i=e.clone();for(const e of t){const t=i.clone(),{center:o,zoom:r,roll:a,pitch:s,bearing:n,elevation:l}=e(t);o&&t.setCenter(o),void 0!==l&&t.setElevation(l),void 0!==r&&t.setZoom(r),void 0!==a&&t.setRoll(a),void 0!==s&&t.setPitch(s),void 0!==n&&t.setBearing(n),i.apply(t);}this.transform.apply(i);}_fireMoveEvents(e){this.fire(new t.l(\"move\",e)),this._zooming&&this.fire(new t.l(\"zoom\",e)),this._rotating&&this.fire(new t.l(\"rotate\",e)),this._pitching&&this.fire(new t.l(\"pitch\",e)),this._rolling&&this.fire(new t.l(\"roll\",e));}_afterEase(e,i){if(this._easeId&&i&&this._easeId===i)return;delete this._easeId;const o=this._zooming,r=this._rotating,a=this._pitching,s=this._rolling;this._moving=!1,this._zooming=!1,this._rotating=!1,this._pitching=!1,this._rolling=!1,this._padding=!1,o&&this.fire(new t.l(\"zoomend\",e)),r&&this.fire(new t.l(\"rotateend\",e)),a&&this.fire(new t.l(\"pitchend\",e)),s&&this.fire(new t.l(\"rollend\",e)),this.fire(new t.l(\"moveend\",e));}flyTo(e,i){if(!e.essential&&s.prefersReducedMotion){const o=t.Q(e,[\"center\",\"zoom\",\"bearing\",\"pitch\",\"roll\",\"elevation\"]);return this.jumpTo(o,i)}this.stop(),e=t.e({offset:[0,0],speed:1.2,curve:1.42,easing:t.cn},e);const o=this._getTransformForUpdate(),r=o.bearing,a=o.pitch,n=o.roll,l=o.padding,c=\"bearing\"in e?this._normalizeBearing(e.bearing,r):r,h=\"pitch\"in e?+e.pitch:a,u=\"roll\"in e?this._normalizeBearing(e.roll,n):n,d=\"padding\"in e?e.padding:o.padding,_=t.P.convert(e.offset);let p=o.centerPoint.add(_);const m=o.screenPointToLocation(p),f=this.cameraHelper.handleFlyTo(o,{bearing:c,pitch:h,roll:u,padding:d,locationAtOffset:m,offsetAsPoint:_,center:e.center,minZoom:e.minZoom,zoom:e.zoom});let g=e.curve;const v=Math.max(o.width,o.height),b=v/f.scaleOfZoom,x=f.pixelPathLength;\"number\"==typeof f.scaleOfMinZoom&&(g=Math.sqrt(v/f.scaleOfMinZoom/x*2));const y=g*g;function w(e){const t=(b*b-v*v+(e?-1:1)*y*y*x*x)/(2*(e?b:v)*y*x);return Math.log(Math.sqrt(t*t+1)-t)}function T(e){return (Math.exp(e)-Math.exp(-e))/2}function P(e){return (Math.exp(e)+Math.exp(-e))/2}const C=w(!1);let I=function(e){return P(C)/P(C+g*e)},M=function(e){return v*((P(C)*(T(t=C+g*e)/P(t))-T(C))/y)/x;var t;},S=(w(!0)-C)/g;if(Math.abs(x)<2e-6||!isFinite(S)){if(Math.abs(v-b)<1e-6)return this.easeTo(e,i);const t=b<v?-1:1;S=Math.abs(Math.log(b/v))/g,M=()=>0,I=e=>Math.exp(t*g*e);}return e.duration=\"duration\"in e?+e.duration:1e3*S/(\"screenSpeed\"in e?+e.screenSpeed/g:+e.speed),e.maxDuration&&e.duration>e.maxDuration&&(e.duration=0),this._zooming=!0,this._rotating=r!==c,this._pitching=h!==a,this._rolling=u!==n,this._padding=!o.isPaddingEqual(d),this._prepareEase(i,!1),this.terrain&&this._prepareElevation(f.targetCenter),this._ease((s=>{const m=s*S,g=1/I(m),v=M(m);this._rotating&&o.setBearing(t.C.number(r,c,s)),this._pitching&&o.setPitch(t.C.number(a,h,s)),this._rolling&&o.setRoll(t.C.number(n,u,s)),this._padding&&(o.interpolatePadding(l,d,s),p=o.centerPoint.add(_)),f.easeFunc(s,g,v,p),this.terrain&&!e.freezeElevation&&this._updateElevation(s),this._applyUpdatedTransform(o),this._fireMoveEvents(i);}),(()=>{this.terrain&&e.freezeElevation&&this._finalizeElevation(),this._afterEase(i);}),e),this}isEasing(){return !!this._easeFrameId}stop(){return this._stop()}_stop(e,t){var i;if(this._easeFrameId&&(this._cancelRenderFrame(this._easeFrameId),delete this._easeFrameId,delete this._onEaseFrame),this._onEaseEnd){const e=this._onEaseEnd;delete this._onEaseEnd,e.call(this,t);}return e||null===(i=this.handlers)||void 0===i||i.stop(!1),this}_ease(e,t,i){!1===i.animate||0===i.duration?(e(1),t()):(this._easeStart=s.now(),this._easeOptions=i,this._onEaseFrame=e,this._onEaseEnd=t,this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback));}_normalizeBearing(e,i){e=t.aN(e,-180,180);const o=Math.abs(e-i);return Math.abs(e-360-i)<o&&(e-=360),Math.abs(e+360-i)<o&&(e+=360),e}queryTerrainElevation(e){return this.terrain?this.terrain.getElevationForLngLatZoom(t.S.convert(e),this.transform.tileZoom):null}}const za={compact:!0,customAttribution:'<a href=\"https://maplibre.org/\" target=\"_blank\">MapLibre</a>'};class Da{constructor(e=za){this._toggleAttribution=()=>{this._container.classList.contains(\"maplibregl-compact\")&&(this._container.classList.contains(\"maplibregl-compact-show\")?(this._container.setAttribute(\"open\",\"\"),this._container.classList.remove(\"maplibregl-compact-show\")):(this._container.classList.add(\"maplibregl-compact-show\"),this._container.removeAttribute(\"open\")));},this._updateData=e=>{!e||\"metadata\"!==e.sourceDataType&&\"visibility\"!==e.sourceDataType&&\"style\"!==e.dataType&&\"terrain\"!==e.type||this._updateAttributions();},this._updateCompact=()=>{this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1===this._compact?this._container.setAttribute(\"open\",\"\"):this._container.classList.contains(\"maplibregl-compact\")||this._container.classList.contains(\"maplibregl-attrib-empty\")||(this._container.setAttribute(\"open\",\"\"),this._container.classList.add(\"maplibregl-compact\",\"maplibregl-compact-show\")):(this._container.setAttribute(\"open\",\"\"),this._container.classList.contains(\"maplibregl-compact\")&&this._container.classList.remove(\"maplibregl-compact\",\"maplibregl-compact-show\"));},this._updateCompactMinimize=()=>{this._container.classList.contains(\"maplibregl-compact\")&&this._container.classList.contains(\"maplibregl-compact-show\")&&this._container.classList.remove(\"maplibregl-compact-show\");},this.options=e;}getDefaultPosition(){return \"bottom-right\"}onAdd(e){return this._map=e,this._compact=this.options.compact,this._container=n.create(\"details\",\"maplibregl-ctrl maplibregl-ctrl-attrib\"),this._compactButton=n.create(\"summary\",\"maplibregl-ctrl-attrib-button\",this._container),this._compactButton.addEventListener(\"click\",this._toggleAttribution),this._setElementTitle(this._compactButton,\"ToggleAttribution\"),this._innerContainer=n.create(\"div\",\"maplibregl-ctrl-attrib-inner\",this._container),this._updateAttributions(),this._updateCompact(),this._map.on(\"styledata\",this._updateData),this._map.on(\"sourcedata\",this._updateData),this._map.on(\"terrain\",this._updateData),this._map.on(\"resize\",this._updateCompact),this._map.on(\"drag\",this._updateCompactMinimize),this._container}onRemove(){n.remove(this._container),this._map.off(\"styledata\",this._updateData),this._map.off(\"sourcedata\",this._updateData),this._map.off(\"terrain\",this._updateData),this._map.off(\"resize\",this._updateCompact),this._map.off(\"drag\",this._updateCompactMinimize),this._map=void 0,this._compact=void 0,this._attribHTML=void 0;}_setElementTitle(e,t){const i=this._map._getUIString(`AttributionControl.${t}`);e.title=i,e.setAttribute(\"aria-label\",i);}_updateAttributions(){if(!this._map.style)return;let e=[];if(this.options.customAttribution&&(Array.isArray(this.options.customAttribution)?e=e.concat(this.options.customAttribution.map((e=>\"string\"!=typeof e?\"\":e))):\"string\"==typeof this.options.customAttribution&&e.push(this.options.customAttribution)),this._map.style.stylesheet){const e=this._map.style.stylesheet;this.styleOwner=e.owner,this.styleId=e.id;}const t=this._map.style.sourceCaches;for(const i in t){const o=t[i];if(o.used||o.usedForTerrain){const t=o.getSource();t.attribution&&e.indexOf(t.attribution)<0&&e.push(t.attribution);}}e=e.filter((e=>String(e).trim())),e.sort(((e,t)=>e.length-t.length)),e=e.filter(((t,i)=>{for(let o=i+1;o<e.length;o++)if(e[o].indexOf(t)>=0)return !1;return !0}));const i=e.join(\" | \");i!==this._attribHTML&&(this._attribHTML=i,e.length?(this._innerContainer.innerHTML=n.sanitize(i),this._container.classList.remove(\"maplibregl-attrib-empty\")):this._container.classList.add(\"maplibregl-attrib-empty\"),this._updateCompact(),this._editLink=null);}}class Aa{constructor(e={}){this._updateCompact=()=>{const e=this._container.children;if(e.length){const t=e[0];this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1!==this._compact&&t.classList.add(\"maplibregl-compact\"):t.classList.remove(\"maplibregl-compact\");}},this.options=e;}getDefaultPosition(){return \"bottom-left\"}onAdd(e){this._map=e,this._compact=this.options&&this.options.compact,this._container=n.create(\"div\",\"maplibregl-ctrl\");const t=n.create(\"a\",\"maplibregl-ctrl-logo\");return t.target=\"_blank\",t.rel=\"noopener nofollow\",t.href=\"https://maplibre.org/\",t.setAttribute(\"aria-label\",this._map._getUIString(\"LogoControl.Title\")),t.setAttribute(\"rel\",\"noopener nofollow\"),this._container.appendChild(t),this._container.style.display=\"block\",this._map.on(\"resize\",this._updateCompact),this._updateCompact(),this._container}onRemove(){n.remove(this._container),this._map.off(\"resize\",this._updateCompact),this._map=void 0,this._compact=void 0;}}class La{constructor(){this._queue=[],this._id=0,this._cleared=!1,this._currentlyRunning=!1;}add(e){const t=++this._id;return this._queue.push({callback:e,id:t,cancelled:!1}),t}remove(e){const t=this._currentlyRunning,i=t?this._queue.concat(t):this._queue;for(const t of i)if(t.id===e)return void(t.cancelled=!0)}run(e=0){if(this._currentlyRunning)throw new Error(\"Attempting to run(), but is already running.\");const t=this._currentlyRunning=this._queue;this._queue=[];for(const i of t)if(!i.cancelled&&(i.callback(e),this._cleared))break;this._cleared=!1,this._currentlyRunning=!1;}clear(){this._currentlyRunning&&(this._cleared=!0),this._queue=[];}}var ka=t.aI([{name:\"a_pos3d\",type:\"Int16\",components:3}]);class Fa extends t.E{constructor(e){super(),this._lastTilesetChange=s.now(),this.sourceCache=e,this._tiles={},this._renderableTilesKeys=[],this._sourceTileCache={},this.minzoom=0,this.maxzoom=22,this.deltaZoom=1,this.tileSize=e._source.tileSize*2**this.deltaZoom,e.usedForTerrain=!0,e.tileSize=this.tileSize;}destruct(){this.sourceCache.usedForTerrain=!1,this.sourceCache.tileSize=null;}update(e,i){this.sourceCache.update(e,i),this._renderableTilesKeys=[];const o={};for(const r of ve(e,{tileSize:this.tileSize,minzoom:this.minzoom,maxzoom:this.maxzoom,reparseOverscaled:!1,terrain:i,calculateTileZoom:this.sourceCache._source.calculateTileZoom}))o[r.key]=!0,this._renderableTilesKeys.push(r.key),this._tiles[r.key]||(r.terrainRttPosMatrix32f=new Float64Array(16),t.bX(r.terrainRttPosMatrix32f,0,t.$,t.$,0,0,1),this._tiles[r.key]=new re(r,this.tileSize),this._lastTilesetChange=s.now());for(const e in this._tiles)o[e]||delete this._tiles[e];}freeRtt(e){for(const t in this._tiles){const i=this._tiles[t];(!e||i.tileID.equals(e)||i.tileID.isChildOf(e)||e.isChildOf(i.tileID))&&(i.rtt=[]);}}getRenderableTiles(){return this._renderableTilesKeys.map((e=>this.getTileByID(e)))}getTileByID(e){return this._tiles[e]}getTerrainCoords(e,t){return t?this._getTerrainCoordsForTileRanges(e,t):this._getTerrainCoordsForRegularTile(e)}_getTerrainCoordsForRegularTile(e){const i={};for(const o of this._renderableTilesKeys){const r=this._tiles[o].tileID,a=e.clone(),s=t.b9();if(r.canonical.equals(e.canonical))t.bX(s,0,t.$,t.$,0,0,1);else if(r.canonical.isChildOf(e.canonical)){const i=r.canonical.z-e.canonical.z,o=r.canonical.x-(r.canonical.x>>i<<i),a=r.canonical.y-(r.canonical.y>>i<<i),n=t.$>>i;t.bX(s,0,n,n,0,0,1),t.M(s,s,[-o*n,-a*n,0]);}else {if(!e.canonical.isChildOf(r.canonical))continue;{const i=e.canonical.z-r.canonical.z,o=e.canonical.x-(e.canonical.x>>i<<i),a=e.canonical.y-(e.canonical.y>>i<<i),n=t.$>>i;t.bX(s,0,t.$,t.$,0,0,1),t.M(s,s,[o*n,a*n,0]),t.N(s,s,[1/2**i,1/2**i,0]);}}a.terrainRttPosMatrix32f=new Float32Array(s),i[o]=a;}return i}_getTerrainCoordsForTileRanges(e,i){const o={};for(const r of this._renderableTilesKeys){const a=this._tiles[r].tileID;if(!this._isWithinTileRanges(a,i))continue;const s=e.clone(),n=t.b9();if(a.canonical.z===e.canonical.z){const i=e.canonical.x-a.canonical.x,o=e.canonical.y-a.canonical.y;t.bX(n,0,t.$,t.$,0,0,1),t.M(n,n,[i*t.$,o*t.$,0]);}else if(a.canonical.z>e.canonical.z){const i=a.canonical.z-e.canonical.z,o=a.canonical.x-(a.canonical.x>>i<<i),r=a.canonical.y-(a.canonical.y>>i<<i),s=e.canonical.x-(a.canonical.x>>i),l=e.canonical.y-(a.canonical.y>>i),c=t.$>>i;t.bX(n,0,c,c,0,0,1),t.M(n,n,[-o*c+s*t.$,-r*c+l*t.$,0]);}else {const i=e.canonical.z-a.canonical.z,o=e.canonical.x-(e.canonical.x>>i<<i),r=e.canonical.y-(e.canonical.y>>i<<i),s=(e.canonical.x>>i)-a.canonical.x,l=(e.canonical.y>>i)-a.canonical.y,c=t.$<<i;t.bX(n,0,c,c,0,0,1),t.M(n,n,[o*t.$+s*c,r*t.$+l*c,0]);}s.terrainRttPosMatrix32f=new Float32Array(n),o[r]=s;}return o}getSourceTile(e,t){const i=this.sourceCache._source;let o=e.overscaledZ-this.deltaZoom;if(o>i.maxzoom&&(o=i.maxzoom),o<i.minzoom)return null;this._sourceTileCache[e.key]||(this._sourceTileCache[e.key]=e.scaledTo(o).key);let r=this.sourceCache.getTileByID(this._sourceTileCache[e.key]);if((!r||!r.dem)&&t)for(;o>=i.minzoom&&(!r||!r.dem);)r=this.sourceCache.getTileByID(e.scaledTo(o--).key);return r}anyTilesAfterTime(e=Date.now()){return this._lastTilesetChange>=e}_isWithinTileRanges(e,t){return t[e.canonical.z]&&e.canonical.x>=t[e.canonical.z].minTileX&&e.canonical.x<=t[e.canonical.z].maxTileX&&e.canonical.y>=t[e.canonical.z].minTileY&&e.canonical.y<=t[e.canonical.z].maxTileY}}class Ba{constructor(e,t,i){this._meshCache={},this.painter=e,this.sourceCache=new Fa(t),this.options=i,this.exaggeration=\"number\"==typeof i.exaggeration?i.exaggeration:1,this.qualityFactor=2,this.meshSize=128,this._demMatrixCache={},this.coordsIndex=[],this._coordsTextureSize=1024;}getDEMElevation(e,i,o,r=t.$){var a;if(!(i>=0&&i<r&&o>=0&&o<r))return 0;const s=this.getTerrainData(e),n=null===(a=s.tile)||void 0===a?void 0:a.dem;if(!n)return 0;const l=t.cr([],[i/r*t.$,o/r*t.$],s.u_terrain_matrix),c=[l[0]*n.dim,l[1]*n.dim],h=Math.floor(c[0]),u=Math.floor(c[1]),d=c[0]-h,_=c[1]-u;return n.get(h,u)*(1-d)*(1-_)+n.get(h+1,u)*d*(1-_)+n.get(h,u+1)*(1-d)*_+n.get(h+1,u+1)*d*_}getElevationForLngLatZoom(e,i){if(!t.cs(i,e.wrap()))return 0;const{tileID:o,mercatorX:r,mercatorY:a}=this._getOverscaledTileIDFromLngLatZoom(e,i);return this.getElevation(o,r%t.$,a%t.$,t.$)}getElevation(e,i,o,r=t.$){return this.getDEMElevation(e,i,o,r)*this.exaggeration}getTerrainData(e){if(!this._emptyDemTexture){const e=this.painter.context,i=new t.R({width:1,height:1},new Uint8Array(4));this._emptyDepthTexture=new t.T(e,i,e.gl.RGBA,{premultiply:!1}),this._emptyDemUnpack=[0,0,0,0],this._emptyDemTexture=new t.T(e,new t.R({width:1,height:1}),e.gl.RGBA,{premultiply:!1}),this._emptyDemTexture.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),this._emptyDemMatrix=t.af([]);}const i=this.sourceCache.getSourceTile(e,!0);if(i&&i.dem&&(!i.demTexture||i.needsTerrainPrepare)){const e=this.painter.context;i.demTexture=this.painter.getTileTexture(i.dem.stride),i.demTexture?i.demTexture.update(i.dem.getPixels(),{premultiply:!1}):i.demTexture=new t.T(e,i.dem.getPixels(),e.gl.RGBA,{premultiply:!1}),i.demTexture.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),i.needsTerrainPrepare=!1;}const o=i&&i+i.tileID.key+e.key;if(o&&!this._demMatrixCache[o]){const o=this.sourceCache.sourceCache._source.maxzoom;let r=e.canonical.z-i.tileID.canonical.z;e.overscaledZ>e.canonical.z&&(e.canonical.z>=o?r=e.canonical.z-o:t.w(\"cannot calculate elevation if elevation maxzoom > source.maxzoom\"));const a=e.canonical.x-(e.canonical.x>>r<<r),s=e.canonical.y-(e.canonical.y>>r<<r),n=t.ct(new Float64Array(16),[1/(t.$<<r),1/(t.$<<r),0]);t.M(n,n,[a*t.$,s*t.$,0]),this._demMatrixCache[e.key]={matrix:n,coord:e};}return {u_depth:2,u_terrain:3,u_terrain_dim:i&&i.dem&&i.dem.dim||1,u_terrain_matrix:o?this._demMatrixCache[e.key].matrix:this._emptyDemMatrix,u_terrain_unpack:i&&i.dem&&i.dem.getUnpackVector()||this._emptyDemUnpack,u_terrain_exaggeration:this.exaggeration,texture:(i&&i.demTexture||this._emptyDemTexture).texture,depthTexture:(this._fboDepthTexture||this._emptyDepthTexture).texture,tile:i}}getFramebuffer(e){const i=this.painter,o=i.width/devicePixelRatio,r=i.height/devicePixelRatio;return !this._fbo||this._fbo.width===o&&this._fbo.height===r||(this._fbo.destroy(),this._fboCoordsTexture.destroy(),this._fboDepthTexture.destroy(),delete this._fbo,delete this._fboDepthTexture,delete this._fboCoordsTexture),this._fboCoordsTexture||(this._fboCoordsTexture=new t.T(i.context,{width:o,height:r,data:null},i.context.gl.RGBA,{premultiply:!1}),this._fboCoordsTexture.bind(i.context.gl.NEAREST,i.context.gl.CLAMP_TO_EDGE)),this._fboDepthTexture||(this._fboDepthTexture=new t.T(i.context,{width:o,height:r,data:null},i.context.gl.RGBA,{premultiply:!1}),this._fboDepthTexture.bind(i.context.gl.NEAREST,i.context.gl.CLAMP_TO_EDGE)),this._fbo||(this._fbo=i.context.createFramebuffer(o,r,!0,!1),this._fbo.depthAttachment.set(i.context.createRenderbuffer(i.context.gl.DEPTH_COMPONENT16,o,r))),this._fbo.colorAttachment.set(\"coords\"===e?this._fboCoordsTexture.texture:this._fboDepthTexture.texture),this._fbo}getCoordsTexture(){const e=this.painter.context;if(this._coordsTexture)return this._coordsTexture;const i=new Uint8Array(this._coordsTextureSize*this._coordsTextureSize*4);for(let e=0,t=0;e<this._coordsTextureSize;e++)for(let o=0;o<this._coordsTextureSize;o++,t+=4)i[t+0]=255&o,i[t+1]=255&e,i[t+2]=o>>8<<4|e>>8,i[t+3]=0;const o=new t.R({width:this._coordsTextureSize,height:this._coordsTextureSize},new Uint8Array(i.buffer)),r=new t.T(e,o,e.gl.RGBA,{premultiply:!1});return r.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),this._coordsTexture=r,r}pointCoordinate(e){this.painter.maybeDrawDepthAndCoords(!0);const i=new Uint8Array(4),o=this.painter.context,r=o.gl,a=Math.round(e.x*this.painter.pixelRatio/devicePixelRatio),s=Math.round(e.y*this.painter.pixelRatio/devicePixelRatio),n=Math.round(this.painter.height/devicePixelRatio);o.bindFramebuffer.set(this.getFramebuffer(\"coords\").framebuffer),r.readPixels(a,n-s-1,1,1,r.RGBA,r.UNSIGNED_BYTE,i),o.bindFramebuffer.set(null);const l=i[0]+(i[2]>>4<<8),c=i[1]+((15&i[2])<<8),h=this.coordsIndex[255-i[3]],u=h&&this.sourceCache.getTileByID(h);if(!u)return null;const d=this._coordsTextureSize,_=(1<<u.tileID.canonical.z)*d;return new t.a0((u.tileID.canonical.x*d+l)/_+u.tileID.wrap,(u.tileID.canonical.y*d+c)/_,this.getElevation(u.tileID,l,c,d))}depthAtPoint(e){const t=new Uint8Array(4),i=this.painter.context,o=i.gl;return i.bindFramebuffer.set(this.getFramebuffer(\"depth\").framebuffer),o.readPixels(e.x,this.painter.height/devicePixelRatio-e.y-1,1,1,o.RGBA,o.UNSIGNED_BYTE,t),i.bindFramebuffer.set(null),(t[0]/16777216+t[1]/65536+t[2]/256+t[3])/256}getTerrainMesh(e){var i;const o=(null===(i=this.painter.style.projection)||void 0===i?void 0:i.transitionState)>0,r=o&&0===e.canonical.y,a=o&&e.canonical.y===(1<<e.canonical.z)-1,s=`m_${r?\"n\":\"\"}_${a?\"s\":\"\"}`;if(this._meshCache[s])return this._meshCache[s];const n=this.painter.context,l=new t.cu,c=new t.aM,h=this.meshSize,u=t.$/h,d=h*h;for(let e=0;e<=h;e++)for(let t=0;t<=h;t++)l.emplaceBack(t*u,e*u,0);for(let e=0;e<d;e+=h+1)for(let t=0;t<h;t++)c.emplaceBack(t+e,h+t+e+1,h+t+e+2),c.emplaceBack(t+e,h+t+e+2,t+e+1);const _=l.length,p=_+(h+1),m=(h+1)*h,f=r?t.bg:0,g=r?0:1,v=a?t.bh:t.$,b=a?0:1;for(let e=0;e<=h;e++)l.emplaceBack(e*u,f,g);for(let e=0;e<=h;e++)l.emplaceBack(e*u,v,b);for(let e=0;e<h;e++)c.emplaceBack(m+e,p+e,p+e+1),c.emplaceBack(m+e,p+e+1,m+e+1),c.emplaceBack(0+e,_+e+1,_+e),c.emplaceBack(0+e,0+e+1,_+e+1);const x=l.length,y=x+2*(h+1);for(const e of [0,1])for(let i=0;i<=h;i++)for(const o of [0,1])l.emplaceBack(e*t.$,i*u,o);for(let e=0;e<2*h;e+=2)c.emplaceBack(x+e,x+e+1,x+e+3),c.emplaceBack(x+e,x+e+3,x+e+2),c.emplaceBack(y+e,y+e+3,y+e+1),c.emplaceBack(y+e,y+e+2,y+e+3);const w=new wt(n.createVertexBuffer(l,ka.members),n.createIndexBuffer(c),t.aL.simpleSegment(0,0,l.length,c.length));return this._meshCache[s]=w,w}getMeshFrameDelta(e){return 2*Math.PI*t.bt/Math.pow(2,Math.max(e,0))/5}getMinTileElevationForLngLatZoom(e,t){var i;const{tileID:o}=this._getOverscaledTileIDFromLngLatZoom(e,t);return null!==(i=this.getMinMaxElevation(o).minElevation)&&void 0!==i?i:0}getMinMaxElevation(e){const t=this.getTerrainData(e).tile,i={minElevation:null,maxElevation:null};return t&&t.dem&&(i.minElevation=t.dem.min*this.exaggeration,i.maxElevation=t.dem.max*this.exaggeration),i}_getOverscaledTileIDFromLngLatZoom(e,i){const o=t.a0.fromLngLat(e.wrap()),r=(1<<i)*t.$,a=o.x*r,s=o.y*r,n=Math.floor(a/t.$),l=Math.floor(s/t.$);return {tileID:new t.Z(i,0,i,n,l),mercatorX:a,mercatorY:s}}}class Oa{constructor(e,t,i){this._context=e,this._size=t,this._tileSize=i,this._objects=[],this._recentlyUsed=[],this._stamp=0;}destruct(){for(const e of this._objects)e.texture.destroy(),e.fbo.destroy();}_createObject(e){const i=this._context.createFramebuffer(this._tileSize,this._tileSize,!0,!0),o=new t.T(this._context,{width:this._tileSize,height:this._tileSize,data:null},this._context.gl.RGBA);return o.bind(this._context.gl.LINEAR,this._context.gl.CLAMP_TO_EDGE),this._context.extTextureFilterAnisotropic&&this._context.gl.texParameterf(this._context.gl.TEXTURE_2D,this._context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,this._context.extTextureFilterAnisotropicMax),i.depthAttachment.set(this._context.createRenderbuffer(this._context.gl.DEPTH_STENCIL,this._tileSize,this._tileSize)),i.colorAttachment.set(o.texture),{id:e,fbo:i,texture:o,stamp:-1,inUse:!1}}getObjectForId(e){return this._objects[e]}useObject(e){e.inUse=!0,this._recentlyUsed=this._recentlyUsed.filter((t=>e.id!==t)),this._recentlyUsed.push(e.id);}stampObject(e){e.stamp=++this._stamp;}getOrCreateFreeObject(){for(const e of this._recentlyUsed)if(!this._objects[e].inUse)return this._objects[e];if(this._objects.length>=this._size)throw new Error(\"No free RenderPool available, call freeAllObjects() required!\");const e=this._createObject(this._objects.length);return this._objects.push(e),e}freeObject(e){e.inUse=!1;}freeAllObjects(){for(const e of this._objects)this.freeObject(e);}isFull(){return !(this._objects.length<this._size)&&!1===this._objects.some((e=>!e.inUse))}}const ja={background:!0,fill:!0,line:!0,raster:!0,hillshade:!0,\"color-relief\":!0};class Na{constructor(e,t){this.painter=e,this.terrain=t,this.pool=new Oa(e.context,30,t.sourceCache.tileSize*t.qualityFactor);}destruct(){this.pool.destruct();}getTexture(e){return this.pool.getObjectForId(e.rtt[this._stacks.length-1].id).texture}prepareForRender(e,t){this._stacks=[],this._prevType=null,this._rttTiles=[],this._renderableTiles=this.terrain.sourceCache.getRenderableTiles(),this._renderableLayerIds=e._order.filter((i=>!e._layers[i].isHidden(t))),this._coordsAscending={};for(const t in e.sourceCaches){this._coordsAscending[t]={};const i=e.sourceCaches[t].getVisibleCoordinates(),o=e.sourceCaches[t].getSource(),r=o instanceof X?o.terrainTileRanges:null;for(const e of i){const i=this.terrain.sourceCache.getTerrainCoords(e,r);for(const e in i)this._coordsAscending[t][e]||(this._coordsAscending[t][e]=[]),this._coordsAscending[t][e].push(i[e]);}}this._coordsAscendingStr={};for(const t of e._order){const i=e._layers[t],o=i.source;if(ja[i.type]&&!this._coordsAscendingStr[o]){this._coordsAscendingStr[o]={};for(const e in this._coordsAscending[o])this._coordsAscendingStr[o][e]=this._coordsAscending[o][e].map((e=>e.key)).sort().join();}}for(const e of this._renderableTiles)for(const t in this._coordsAscendingStr){const i=this._coordsAscendingStr[t][e.tileID.key];i&&i!==e.rttCoords[t]&&(e.rtt=[]);}}renderLayer(e,i){if(e.isHidden(this.painter.transform.zoom))return !1;const o=Object.assign(Object.assign({},i),{isRenderingToTexture:!0}),r=e.type,a=this.painter,s=this._renderableLayerIds[this._renderableLayerIds.length-1]===e.id;if(ja[r]&&(this._prevType&&ja[this._prevType]||this._stacks.push([]),this._prevType=r,this._stacks[this._stacks.length-1].push(e.id),!s))return !0;if(ja[this._prevType]||ja[r]&&s){this._prevType=r;const e=this._stacks.length-1,i=this._stacks[e]||[];for(const r of this._renderableTiles){if(this.pool.isFull()&&(Cr(this.painter,this.terrain,this._rttTiles,o),this._rttTiles=[],this.pool.freeAllObjects()),this._rttTiles.push(r),r.rtt[e]){const t=this.pool.getObjectForId(r.rtt[e].id);if(t.stamp===r.rtt[e].stamp){this.pool.useObject(t);continue}}const s=this.pool.getOrCreateFreeObject();this.pool.useObject(s),this.pool.stampObject(s),r.rtt[e]={id:s.id,stamp:s.stamp},a.context.bindFramebuffer.set(s.fbo.framebuffer),a.context.clear({color:t.be.transparent,stencil:0}),a.currentStencilSource=void 0;for(let e=0;e<i.length;e++){const t=a.style._layers[i[e]],n=t.source?this._coordsAscending[t.source][r.tileID.key]:[r.tileID];a.context.viewport.set([0,0,s.fbo.width,s.fbo.height]),a._renderTileClippingMasks(t,n,!0),a.renderLayer(a,a.style.sourceCaches[t.source],t,n,o),t.source&&(r.rttCoords[t.source]=this._coordsAscendingStr[t.source][r.tileID.key]);}}return Cr(this.painter,this.terrain,this._rttTiles,o),this._rttTiles=[],this.pool.freeAllObjects(),ja[r]}return !1}}const Za={\"AttributionControl.ToggleAttribution\":\"Toggle attribution\",\"AttributionControl.MapFeedback\":\"Map feedback\",\"FullscreenControl.Enter\":\"Enter fullscreen\",\"FullscreenControl.Exit\":\"Exit fullscreen\",\"GeolocateControl.FindMyLocation\":\"Find my location\",\"GeolocateControl.LocationNotAvailable\":\"Location not available\",\"LogoControl.Title\":\"MapLibre logo\",\"Map.Title\":\"Map\",\"Marker.Title\":\"Map marker\",\"NavigationControl.ResetBearing\":\"Reset bearing to north\",\"NavigationControl.ZoomIn\":\"Zoom in\",\"NavigationControl.ZoomOut\":\"Zoom out\",\"Popup.Close\":\"Close popup\",\"ScaleControl.Feet\":\"ft\",\"ScaleControl.Meters\":\"m\",\"ScaleControl.Kilometers\":\"km\",\"ScaleControl.Miles\":\"mi\",\"ScaleControl.NauticalMiles\":\"nm\",\"GlobeControl.Enable\":\"Enable globe\",\"GlobeControl.Disable\":\"Disable globe\",\"TerrainControl.Enable\":\"Enable terrain\",\"TerrainControl.Disable\":\"Disable terrain\",\"CooperativeGesturesHandler.WindowsHelpText\":\"Use Ctrl + scroll to zoom the map\",\"CooperativeGesturesHandler.MacHelpText\":\"Use ⌘ + scroll to zoom the map\",\"CooperativeGesturesHandler.MobileHelpText\":\"Use two fingers to move the map\"},Ua=i,Ga={hash:!1,interactive:!0,bearingSnap:7,attributionControl:za,maplibreLogo:!1,refreshExpiredTiles:!0,canvasContextAttributes:{antialias:!1,preserveDrawingBuffer:!1,powerPreference:\"high-performance\",failIfMajorPerformanceCaveat:!1,desynchronized:!1,contextType:void 0},scrollZoom:!0,minZoom:-2,maxZoom:22,minPitch:0,maxPitch:60,boxZoom:!0,dragRotate:!0,dragPan:!0,keyboard:!0,doubleClickZoom:!0,touchZoomRotate:!0,touchPitch:!0,cooperativeGestures:!1,trackResize:!0,center:[0,0],elevation:0,zoom:0,bearing:0,pitch:0,roll:0,renderWorldCopies:!0,maxTileCacheSize:null,maxTileCacheZoomLevels:t.a.MAX_TILE_CACHE_ZOOM_LEVELS,transformRequest:null,transformCameraUpdate:null,fadeDuration:300,crossSourceCollisions:!0,clickTolerance:3,localIdeographFontFamily:\"sans-serif\",pitchWithRotate:!0,rollEnabled:!1,validateStyle:!0,maxCanvasSize:[4096,4096],cancelPendingTileRequestsWhileZooming:!0,centerClampedToGround:!0},Va={showCompass:!0,showZoom:!0,visualizePitch:!1,visualizeRoll:!0};class $a{constructor(e,i,o=!1){this.mousedown=e=>{this.startMove(e,n.mousePos(this.element,e)),n.addEventListener(window,\"mousemove\",this.mousemove),n.addEventListener(window,\"mouseup\",this.mouseup);},this.mousemove=e=>{this.move(e,n.mousePos(this.element,e));},this.mouseup=e=>{this._rotatePitchHanlder.dragEnd(e),this.offTemp();},this.touchstart=e=>{1!==e.targetTouches.length?this.reset():(this._startPos=this._lastPos=n.touchPos(this.element,e.targetTouches)[0],this.startMove(e,this._startPos),n.addEventListener(window,\"touchmove\",this.touchmove,{passive:!1}),n.addEventListener(window,\"touchend\",this.touchend));},this.touchmove=e=>{1!==e.targetTouches.length?this.reset():(this._lastPos=n.touchPos(this.element,e.targetTouches)[0],this.move(e,this._lastPos));},this.touchend=e=>{0===e.targetTouches.length&&this._startPos&&this._lastPos&&this._startPos.dist(this._lastPos)<this._clickTolerance&&this.element.click(),delete this._startPos,delete this._lastPos,this.offTemp();},this.reset=()=>{this._rotatePitchHanlder.reset(),delete this._startPos,delete this._lastPos,this.offTemp();},this._clickTolerance=10,this.element=i;const r=new ia;this._rotatePitchHanlder=new Kr({clickTolerance:3,move:(e,r)=>{const a=i.getBoundingClientRect(),s=new t.P((a.bottom-a.top)/2,(a.right-a.left)/2);return {bearingDelta:t.cm(new t.P(e.x,r.y),r,s),pitchDelta:o?-.5*(r.y-e.y):void 0}},moveStateManager:r,enable:!0,assignEvents:()=>{}}),this.map=e,n.addEventListener(i,\"mousedown\",this.mousedown),n.addEventListener(i,\"touchstart\",this.touchstart,{passive:!1}),n.addEventListener(i,\"touchcancel\",this.reset);}startMove(e,t){this._rotatePitchHanlder.dragStart(e,t),n.disableDrag();}move(e,t){const i=this.map,{bearingDelta:o,pitchDelta:r}=this._rotatePitchHanlder.dragMove(e,t)||{};o&&i.setBearing(i.getBearing()+o),r&&i.setPitch(i.getPitch()+r);}off(){const e=this.element;n.removeEventListener(e,\"mousedown\",this.mousedown),n.removeEventListener(e,\"touchstart\",this.touchstart,{passive:!1}),n.removeEventListener(window,\"touchmove\",this.touchmove,{passive:!1}),n.removeEventListener(window,\"touchend\",this.touchend),n.removeEventListener(e,\"touchcancel\",this.reset),this.offTemp();}offTemp(){n.enableDrag(),n.removeEventListener(window,\"mousemove\",this.mousemove),n.removeEventListener(window,\"mouseup\",this.mouseup),n.removeEventListener(window,\"touchmove\",this.touchmove,{passive:!1}),n.removeEventListener(window,\"touchend\",this.touchend);}}let qa;function Wa(e,i,o,r=!1){if(r||!o.getCoveringTilesDetailsProvider().allowWorldCopies())return null==e?void 0:e.wrap();const a=new t.S(e.lng,e.lat);if(e=new t.S(e.lng,e.lat),i){const r=new t.S(e.lng-360,e.lat),a=new t.S(e.lng+360,e.lat),s=o.locationToScreenPoint(e).distSqr(i);o.locationToScreenPoint(r).distSqr(i)<s?e=r:o.locationToScreenPoint(a).distSqr(i)<s&&(e=a);}for(;Math.abs(e.lng-o.center.lng)>180;){const t=o.locationToScreenPoint(e);if(t.x>=0&&t.y>=0&&t.x<=o.width&&t.y<=o.height)break;e.lng>o.center.lng?e.lng-=360:e.lng+=360;}return e.lng!==a.lng&&o.isPointOnMapSurface(o.locationToScreenPoint(e))?e:a}const Ha={center:\"translate(-50%,-50%)\",top:\"translate(-50%,0)\",\"top-left\":\"translate(0,0)\",\"top-right\":\"translate(-100%,0)\",bottom:\"translate(-50%,-100%)\",\"bottom-left\":\"translate(0,-100%)\",\"bottom-right\":\"translate(-100%,-100%)\",left:\"translate(0,-50%)\",right:\"translate(-100%,-50%)\"};function Xa(e,t,i){const o=e.classList;for(const e in Ha)o.remove(`maplibregl-${i}-anchor-${e}`);o.add(`maplibregl-${i}-anchor-${t}`);}class Ka extends t.E{constructor(e){if(super(),this._onKeyPress=e=>{const t=e.code,i=e.charCode||e.keyCode;\"Space\"!==t&&\"Enter\"!==t&&32!==i&&13!==i||this.togglePopup();},this._onMapClick=e=>{const t=e.originalEvent.target,i=this._element;this._popup&&(t===i||i.contains(t))&&this.togglePopup();},this._update=e=>{if(!this._map)return;const t=this._map.loaded()&&!this._map.isMoving();(\"terrain\"===(null==e?void 0:e.type)||\"render\"===(null==e?void 0:e.type)&&!t)&&this._map.once(\"render\",this._update),this._lngLat=Wa(this._lngLat,this._flatPos,this._map.transform),this._flatPos=this._pos=this._map.project(this._lngLat)._add(this._offset),this._map.terrain&&(this._flatPos=this._map.transform.locationToScreenPoint(this._lngLat)._add(this._offset));let i=\"\";\"viewport\"===this._rotationAlignment||\"auto\"===this._rotationAlignment?i=`rotateZ(${this._rotation}deg)`:\"map\"===this._rotationAlignment&&(i=`rotateZ(${this._rotation-this._map.getBearing()}deg)`);let o=\"\";\"viewport\"===this._pitchAlignment||\"auto\"===this._pitchAlignment?o=\"rotateX(0deg)\":\"map\"===this._pitchAlignment&&(o=`rotateX(${this._map.getPitch()}deg)`),this._subpixelPositioning||e&&\"moveend\"!==e.type||(this._pos=this._pos.round()),n.setTransform(this._element,`${Ha[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${o} ${i}`),s.frameAsync(new AbortController).then((()=>{this._updateOpacity(e&&\"moveend\"===e.type);})).catch((()=>{}));},this._onMove=e=>{if(!this._isDragging){const t=this._clickTolerance||this._map._clickTolerance;this._isDragging=e.point.dist(this._pointerdownPos)>=t;}this._isDragging&&(this._pos=e.point.sub(this._positionDelta),this._lngLat=this._map.unproject(this._pos),this.setLngLat(this._lngLat),this._element.style.pointerEvents=\"none\",\"pending\"===this._state&&(this._state=\"active\",this.fire(new t.l(\"dragstart\"))),this.fire(new t.l(\"drag\")));},this._onUp=()=>{this._element.style.pointerEvents=\"auto\",this._positionDelta=null,this._pointerdownPos=null,this._isDragging=!1,this._map.off(\"mousemove\",this._onMove),this._map.off(\"touchmove\",this._onMove),\"active\"===this._state&&this.fire(new t.l(\"dragend\")),this._state=\"inactive\";},this._addDragHandler=e=>{this._element.contains(e.originalEvent.target)&&(e.preventDefault(),this._positionDelta=e.point.sub(this._pos).add(this._offset),this._pointerdownPos=e.point,this._state=\"pending\",this._map.on(\"mousemove\",this._onMove),this._map.on(\"touchmove\",this._onMove),this._map.once(\"mouseup\",this._onUp),this._map.once(\"touchend\",this._onUp));},this._anchor=e&&e.anchor||\"center\",this._color=e&&e.color||\"#3FB1CE\",this._scale=e&&e.scale||1,this._draggable=e&&e.draggable||!1,this._clickTolerance=e&&e.clickTolerance||0,this._subpixelPositioning=e&&e.subpixelPositioning||!1,this._isDragging=!1,this._state=\"inactive\",this._rotation=e&&e.rotation||0,this._rotationAlignment=e&&e.rotationAlignment||\"auto\",this._pitchAlignment=e&&e.pitchAlignment&&\"auto\"!==e.pitchAlignment?e.pitchAlignment:this._rotationAlignment,this.setOpacity(null==e?void 0:e.opacity,null==e?void 0:e.opacityWhenCovered),e&&e.element)this._element=e.element,this._offset=t.P.convert(e&&e.offset||[0,0]);else {this._defaultMarker=!0,this._element=n.create(\"div\");const i=n.createNS(\"http://www.w3.org/2000/svg\",\"svg\"),o=41,r=27;i.setAttributeNS(null,\"display\",\"block\"),i.setAttributeNS(null,\"height\",`${o}px`),i.setAttributeNS(null,\"width\",`${r}px`),i.setAttributeNS(null,\"viewBox\",`0 0 ${r} ${o}`);const a=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");a.setAttributeNS(null,\"stroke\",\"none\"),a.setAttributeNS(null,\"stroke-width\",\"1\"),a.setAttributeNS(null,\"fill\",\"none\"),a.setAttributeNS(null,\"fill-rule\",\"evenodd\");const s=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");s.setAttributeNS(null,\"fill-rule\",\"nonzero\");const l=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");l.setAttributeNS(null,\"transform\",\"translate(3.0, 29.0)\"),l.setAttributeNS(null,\"fill\",\"#000000\");const c=[{rx:\"10.5\",ry:\"5.25002273\"},{rx:\"10.5\",ry:\"5.25002273\"},{rx:\"9.5\",ry:\"4.77275007\"},{rx:\"8.5\",ry:\"4.29549936\"},{rx:\"7.5\",ry:\"3.81822308\"},{rx:\"6.5\",ry:\"3.34094679\"},{rx:\"5.5\",ry:\"2.86367051\"},{rx:\"4.5\",ry:\"2.38636864\"}];for(const e of c){const t=n.createNS(\"http://www.w3.org/2000/svg\",\"ellipse\");t.setAttributeNS(null,\"opacity\",\"0.04\"),t.setAttributeNS(null,\"cx\",\"10.5\"),t.setAttributeNS(null,\"cy\",\"5.80029008\"),t.setAttributeNS(null,\"rx\",e.rx),t.setAttributeNS(null,\"ry\",e.ry),l.appendChild(t);}const h=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");h.setAttributeNS(null,\"fill\",this._color);const u=n.createNS(\"http://www.w3.org/2000/svg\",\"path\");u.setAttributeNS(null,\"d\",\"M27,13.5 C27,19.074644 20.250001,27.000002 14.75,34.500002 C14.016665,35.500004 12.983335,35.500004 12.25,34.500002 C6.7499993,27.000002 0,19.222562 0,13.5 C0,6.0441559 6.0441559,0 13.5,0 C20.955844,0 27,6.0441559 27,13.5 Z\"),h.appendChild(u);const d=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");d.setAttributeNS(null,\"opacity\",\"0.25\"),d.setAttributeNS(null,\"fill\",\"#000000\");const _=n.createNS(\"http://www.w3.org/2000/svg\",\"path\");_.setAttributeNS(null,\"d\",\"M13.5,0 C6.0441559,0 0,6.0441559 0,13.5 C0,19.222562 6.7499993,27 12.25,34.5 C13,35.522727 14.016664,35.500004 14.75,34.5 C20.250001,27 27,19.074644 27,13.5 C27,6.0441559 20.955844,0 13.5,0 Z M13.5,1 C20.415404,1 26,6.584596 26,13.5 C26,15.898657 24.495584,19.181431 22.220703,22.738281 C19.945823,26.295132 16.705119,30.142167 13.943359,33.908203 C13.743445,34.180814 13.612715,34.322738 13.5,34.441406 C13.387285,34.322738 13.256555,34.180814 13.056641,33.908203 C10.284481,30.127985 7.4148684,26.314159 5.015625,22.773438 C2.6163816,19.232715 1,15.953538 1,13.5 C1,6.584596 6.584596,1 13.5,1 Z\"),d.appendChild(_);const p=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");p.setAttributeNS(null,\"transform\",\"translate(6.0, 7.0)\"),p.setAttributeNS(null,\"fill\",\"#FFFFFF\");const m=n.createNS(\"http://www.w3.org/2000/svg\",\"g\");m.setAttributeNS(null,\"transform\",\"translate(8.0, 8.0)\");const f=n.createNS(\"http://www.w3.org/2000/svg\",\"circle\");f.setAttributeNS(null,\"fill\",\"#000000\"),f.setAttributeNS(null,\"opacity\",\"0.25\"),f.setAttributeNS(null,\"cx\",\"5.5\"),f.setAttributeNS(null,\"cy\",\"5.5\"),f.setAttributeNS(null,\"r\",\"5.4999962\");const g=n.createNS(\"http://www.w3.org/2000/svg\",\"circle\");g.setAttributeNS(null,\"fill\",\"#FFFFFF\"),g.setAttributeNS(null,\"cx\",\"5.5\"),g.setAttributeNS(null,\"cy\",\"5.5\"),g.setAttributeNS(null,\"r\",\"5.4999962\"),m.appendChild(f),m.appendChild(g),s.appendChild(l),s.appendChild(h),s.appendChild(d),s.appendChild(p),s.appendChild(m),i.appendChild(s),i.setAttributeNS(null,\"height\",o*this._scale+\"px\"),i.setAttributeNS(null,\"width\",r*this._scale+\"px\"),this._element.appendChild(i),this._offset=t.P.convert(e&&e.offset||[0,-14]);}if(this._element.classList.add(\"maplibregl-marker\"),this._element.addEventListener(\"dragstart\",(e=>{e.preventDefault();})),this._element.addEventListener(\"mousedown\",(e=>{e.preventDefault();})),Xa(this._element,this._anchor,\"marker\"),e&&e.className)for(const t of e.className.split(\" \"))this._element.classList.add(t);this._popup=null;}addTo(e){return this.remove(),this._map=e,this._element.hasAttribute(\"aria-label\")||this._element.setAttribute(\"aria-label\",e._getUIString(\"Marker.Title\")),e.getCanvasContainer().appendChild(this._element),e.on(\"move\",this._update),e.on(\"moveend\",this._update),e.on(\"terrain\",this._update),e.on(\"projectiontransition\",this._update),this.setDraggable(this._draggable),this._update(),this._map.on(\"click\",this._onMapClick),this}remove(){return this._opacityTimeout&&(clearTimeout(this._opacityTimeout),delete this._opacityTimeout),this._map&&(this._map.off(\"click\",this._onMapClick),this._map.off(\"move\",this._update),this._map.off(\"moveend\",this._update),this._map.off(\"terrain\",this._update),this._map.off(\"projectiontransition\",this._update),this._map.off(\"mousedown\",this._addDragHandler),this._map.off(\"touchstart\",this._addDragHandler),this._map.off(\"mouseup\",this._onUp),this._map.off(\"touchend\",this._onUp),this._map.off(\"mousemove\",this._onMove),this._map.off(\"touchmove\",this._onMove),delete this._map),n.remove(this._element),this._popup&&this._popup.remove(),this}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.S.convert(e),this._pos=null,this._popup&&this._popup.setLngLat(this._lngLat),this._update(),this}getElement(){return this._element}setPopup(e){if(this._popup&&(this._popup.remove(),this._popup=null,this._element.removeEventListener(\"keypress\",this._onKeyPress),this._originalTabIndex||this._element.removeAttribute(\"tabindex\")),e){if(!(\"offset\"in e.options)){const t=38.1,i=13.5,o=Math.abs(i)/Math.SQRT2;e.options.offset=this._defaultMarker?{top:[0,0],\"top-left\":[0,0],\"top-right\":[0,0],bottom:[0,-38.1],\"bottom-left\":[o,-1*(t-i+o)],\"bottom-right\":[-o,-1*(t-i+o)],left:[i,-1*(t-i)],right:[-13.5,-1*(t-i)]}:this._offset;}this._popup=e,this._originalTabIndex=this._element.getAttribute(\"tabindex\"),this._originalTabIndex||this._element.setAttribute(\"tabindex\",\"0\"),this._element.addEventListener(\"keypress\",this._onKeyPress);}return this}setSubpixelPositioning(e){return this._subpixelPositioning=e,this}getPopup(){return this._popup}togglePopup(){const e=this._popup;return this._element.style.opacity===this._opacityWhenCovered?this:e?(e.isOpen()?e.remove():(e.setLngLat(this._lngLat),e.addTo(this._map)),this):this}_updateOpacity(e=!1){var i,o;const r=null===(i=this._map)||void 0===i?void 0:i.terrain,a=this._map.transform.isLocationOccluded(this._lngLat);if(!r||a){const e=a?this._opacityWhenCovered:this._opacity;return void(this._element.style.opacity!==e&&(this._element.style.opacity=e))}if(e)this._opacityTimeout=null;else {if(this._opacityTimeout)return;this._opacityTimeout=setTimeout((()=>{this._opacityTimeout=null;}),100);}const s=this._map,n=s.terrain.depthAtPoint(this._pos),l=s.terrain.getElevationForLngLatZoom(this._lngLat,s.transform.tileZoom);if(s.transform.lngLatToCameraDepth(this._lngLat,l)-n<.006)return void(this._element.style.opacity=this._opacity);const c=-this._offset.y/s.transform.pixelsPerMeter,h=Math.sin(s.getPitch()*Math.PI/180)*c,u=s.terrain.depthAtPoint(new t.P(this._pos.x,this._pos.y-this._offset.y)),d=s.transform.lngLatToCameraDepth(this._lngLat,l+h)-u>.006;(null===(o=this._popup)||void 0===o?void 0:o.isOpen())&&d&&this._popup.remove(),this._element.style.opacity=d?this._opacityWhenCovered:this._opacity;}getOffset(){return this._offset}setOffset(e){return this._offset=t.P.convert(e),this._update(),this}addClassName(e){this._element.classList.add(e);}removeClassName(e){this._element.classList.remove(e);}toggleClassName(e){return this._element.classList.toggle(e)}setDraggable(e){return this._draggable=!!e,this._map&&(e?(this._map.on(\"mousedown\",this._addDragHandler),this._map.on(\"touchstart\",this._addDragHandler)):(this._map.off(\"mousedown\",this._addDragHandler),this._map.off(\"touchstart\",this._addDragHandler))),this}isDraggable(){return this._draggable}setRotation(e){return this._rotation=e||0,this._update(),this}getRotation(){return this._rotation}setRotationAlignment(e){return this._rotationAlignment=e||\"auto\",this._update(),this}getRotationAlignment(){return this._rotationAlignment}setPitchAlignment(e){return this._pitchAlignment=e&&\"auto\"!==e?e:this._rotationAlignment,this._update(),this}getPitchAlignment(){return this._pitchAlignment}setOpacity(e,t){return (void 0===this._opacity||void 0===e&&void 0===t)&&(this._opacity=\"1\",this._opacityWhenCovered=\"0.2\"),void 0!==e&&(this._opacity=e),void 0!==t&&(this._opacityWhenCovered=t),this._map&&this._updateOpacity(!0),this}}const Ya={positionOptions:{enableHighAccuracy:!1,maximumAge:0,timeout:6e3},fitBoundsOptions:{maxZoom:15},trackUserLocation:!1,showAccuracyCircle:!0,showUserLocation:!0};let Qa=0,Ja=!1;const es={maxWidth:100,unit:\"metric\"};function ts(e,t,i){const o=i&&i.maxWidth||100,r=e._container.clientHeight/2,a=e._container.clientWidth/2,s=e.unproject([a-o/2,r]),n=e.unproject([a+o/2,r]),l=Math.round(e.project(n).x-e.project(s).x),c=Math.min(o,l,e._container.clientWidth),h=s.distanceTo(n);if(i&&\"imperial\"===i.unit){const i=3.2808*h;i>5280?is(t,c,i/5280,e._getUIString(\"ScaleControl.Miles\")):is(t,c,i,e._getUIString(\"ScaleControl.Feet\"));}else i&&\"nautical\"===i.unit?is(t,c,h/1852,e._getUIString(\"ScaleControl.NauticalMiles\")):h>=1e3?is(t,c,h/1e3,e._getUIString(\"ScaleControl.Kilometers\")):is(t,c,h,e._getUIString(\"ScaleControl.Meters\"));}function is(e,t,i,o){const r=function(e){const t=Math.pow(10,`${Math.floor(e)}`.length-1);let i=e/t;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:i>=1?1:function(e){const t=Math.pow(10,Math.ceil(-Math.log(e)/Math.LN10));return Math.round(e*t)/t}(i),t*i}(i);e.style.width=t*(r/i)+\"px\",e.innerHTML=`${r}&nbsp;${o}`;}const os={closeButton:!0,closeOnClick:!0,focusAfterOpen:!0,className:\"\",maxWidth:\"240px\",subpixelPositioning:!1,locationOccludedOpacity:void 0},rs=[\"a[href]\",\"[tabindex]:not([tabindex='-1'])\",\"[contenteditable]:not([contenteditable='false'])\",\"button:not([disabled])\",\"input:not([disabled])\",\"select:not([disabled])\",\"textarea:not([disabled])\"].join(\", \");function as(e){if(e){if(\"number\"==typeof e){const i=Math.round(Math.abs(e)/Math.SQRT2);return {center:new t.P(0,0),top:new t.P(0,e),\"top-left\":new t.P(i,i),\"top-right\":new t.P(-i,i),bottom:new t.P(0,-e),\"bottom-left\":new t.P(i,-i),\"bottom-right\":new t.P(-i,-i),left:new t.P(e,0),right:new t.P(-e,0)}}if(e instanceof t.P||Array.isArray(e)){const i=t.P.convert(e);return {center:i,top:i,\"top-left\":i,\"top-right\":i,bottom:i,\"bottom-left\":i,\"bottom-right\":i,left:i,right:i}}return {center:t.P.convert(e.center||[0,0]),top:t.P.convert(e.top||[0,0]),\"top-left\":t.P.convert(e[\"top-left\"]||[0,0]),\"top-right\":t.P.convert(e[\"top-right\"]||[0,0]),bottom:t.P.convert(e.bottom||[0,0]),\"bottom-left\":t.P.convert(e[\"bottom-left\"]||[0,0]),\"bottom-right\":t.P.convert(e[\"bottom-right\"]||[0,0]),left:t.P.convert(e.left||[0,0]),right:t.P.convert(e.right||[0,0])}}return as(new t.P(0,0))}const ss=i;e.AJAXError=t.cy,e.Event=t.l,e.Evented=t.E,e.LngLat=t.S,e.MercatorCoordinate=t.a0,e.Point=t.P,e.addProtocol=t.cz,e.config=t.a,e.removeProtocol=t.cA,e.AttributionControl=Da,e.BoxZoomHandler=$r,e.CanvasSource=Y,e.CooperativeGesturesHandler=Ca,e.DoubleClickZoomHandler=ba,e.DragPanHandler=wa,e.DragRotateHandler=Ta,e.EdgeInsets=Mt,e.FullscreenControl=class extends t.E{constructor(e={}){super(),this._onFullscreenChange=()=>{var e;let t=window.document.fullscreenElement||window.document.mozFullScreenElement||window.document.webkitFullscreenElement||window.document.msFullscreenElement;for(;null===(e=null==t?void 0:t.shadowRoot)||void 0===e?void 0:e.fullscreenElement;)t=t.shadowRoot.fullscreenElement;t===this._container!==this._fullscreen&&this._handleFullscreenChange();},this._onClickFullscreen=()=>{this._isFullscreen()?this._exitFullscreen():this._requestFullscreen();},this._fullscreen=!1,e&&e.container&&(e.container instanceof HTMLElement?this._container=e.container:t.w(\"Full screen control 'container' must be a DOM element.\")),\"onfullscreenchange\"in document?this._fullscreenchange=\"fullscreenchange\":\"onmozfullscreenchange\"in document?this._fullscreenchange=\"mozfullscreenchange\":\"onwebkitfullscreenchange\"in document?this._fullscreenchange=\"webkitfullscreenchange\":\"onmsfullscreenchange\"in document&&(this._fullscreenchange=\"MSFullscreenChange\");}onAdd(e){return this._map=e,this._container||(this._container=this._map.getContainer()),this._controlContainer=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-group\"),this._setupUI(),this._controlContainer}onRemove(){n.remove(this._controlContainer),this._map=null,window.document.removeEventListener(this._fullscreenchange,this._onFullscreenChange);}_setupUI(){const e=this._fullscreenButton=n.create(\"button\",\"maplibregl-ctrl-fullscreen\",this._controlContainer);n.create(\"span\",\"maplibregl-ctrl-icon\",e).setAttribute(\"aria-hidden\",\"true\"),e.type=\"button\",this._updateTitle(),this._fullscreenButton.addEventListener(\"click\",this._onClickFullscreen),window.document.addEventListener(this._fullscreenchange,this._onFullscreenChange);}_updateTitle(){const e=this._getTitle();this._fullscreenButton.setAttribute(\"aria-label\",e),this._fullscreenButton.title=e;}_getTitle(){return this._map._getUIString(this._isFullscreen()?\"FullscreenControl.Exit\":\"FullscreenControl.Enter\")}_isFullscreen(){return this._fullscreen}_handleFullscreenChange(){this._fullscreen=!this._fullscreen,this._fullscreenButton.classList.toggle(\"maplibregl-ctrl-shrink\"),this._fullscreenButton.classList.toggle(\"maplibregl-ctrl-fullscreen\"),this._updateTitle(),this._fullscreen?(this.fire(new t.l(\"fullscreenstart\")),this._prevCooperativeGesturesEnabled=this._map.cooperativeGestures.isEnabled(),this._map.cooperativeGestures.disable()):(this.fire(new t.l(\"fullscreenend\")),this._prevCooperativeGesturesEnabled&&this._map.cooperativeGestures.enable());}_exitFullscreen(){window.document.exitFullscreen?window.document.exitFullscreen():window.document.mozCancelFullScreen?window.document.mozCancelFullScreen():window.document.msExitFullscreen?window.document.msExitFullscreen():window.document.webkitCancelFullScreen?window.document.webkitCancelFullScreen():this._togglePseudoFullScreen();}_requestFullscreen(){this._container.requestFullscreen?this._container.requestFullscreen():this._container.mozRequestFullScreen?this._container.mozRequestFullScreen():this._container.msRequestFullscreen?this._container.msRequestFullscreen():this._container.webkitRequestFullscreen?this._container.webkitRequestFullscreen():this._togglePseudoFullScreen();}_togglePseudoFullScreen(){this._container.classList.toggle(\"maplibregl-pseudo-fullscreen\"),this._handleFullscreenChange(),this._map.resize();}},e.GeoJSONSource=H,e.GeolocateControl=class extends t.E{constructor(e){super(),this._onSuccess=e=>{if(this._map){if(this._isOutOfMapMaxBounds(e))return this._setErrorState(),this.fire(new t.l(\"outofmaxbounds\",e)),this._updateMarker(),void this._finish();if(this.options.trackUserLocation)switch(this._lastKnownPosition=e,this._watchState){case \"WAITING_ACTIVE\":case \"ACTIVE_LOCK\":case \"ACTIVE_ERROR\":this._watchState=\"ACTIVE_LOCK\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active-error\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-active\");break;case \"BACKGROUND\":case \"BACKGROUND_ERROR\":this._watchState=\"BACKGROUND\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background-error\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-background\");break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}this.options.showUserLocation&&\"OFF\"!==this._watchState&&this._updateMarker(e),this.options.trackUserLocation&&\"ACTIVE_LOCK\"!==this._watchState||this._updateCamera(e),this.options.showUserLocation&&this._dotElement.classList.remove(\"maplibregl-user-location-dot-stale\"),this.fire(new t.l(\"geolocate\",e)),this._finish();}},this._updateCamera=e=>{const i=new t.S(e.coords.longitude,e.coords.latitude),o=e.coords.accuracy,r=this._map.getBearing(),a=t.e({bearing:r},this.options.fitBoundsOptions),s=G.fromLngLat(i,o);this._map.fitBounds(s,a,{geolocateSource:!0});},this._updateMarker=e=>{if(e){const i=new t.S(e.coords.longitude,e.coords.latitude);this._accuracyCircleMarker.setLngLat(i).addTo(this._map),this._userLocationDotMarker.setLngLat(i).addTo(this._map),this._accuracy=e.coords.accuracy,this.options.showUserLocation&&this.options.showAccuracyCircle&&this._updateCircleRadius();}else this._userLocationDotMarker.remove(),this._accuracyCircleMarker.remove();},this._onZoom=()=>{this.options.showUserLocation&&this.options.showAccuracyCircle&&this._updateCircleRadius();},this._onError=e=>{if(this._map){if(1===e.code){this._watchState=\"OFF\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active-error\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background-error\"),this._geolocateButton.disabled=!0;const e=this._map._getUIString(\"GeolocateControl.LocationNotAvailable\");this._geolocateButton.title=e,this._geolocateButton.setAttribute(\"aria-label\",e),void 0!==this._geolocationWatchID&&this._clearWatch();}else {if(3===e.code&&Ja)return;this.options.trackUserLocation&&this._setErrorState();}\"OFF\"!==this._watchState&&this.options.showUserLocation&&this._dotElement.classList.add(\"maplibregl-user-location-dot-stale\"),this.fire(new t.l(\"error\",e)),this._finish();}},this._finish=()=>{this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=void 0;},this._setupUI=()=>{this._map&&(this._container.addEventListener(\"contextmenu\",(e=>e.preventDefault())),this._geolocateButton=n.create(\"button\",\"maplibregl-ctrl-geolocate\",this._container),n.create(\"span\",\"maplibregl-ctrl-icon\",this._geolocateButton).setAttribute(\"aria-hidden\",\"true\"),this._geolocateButton.type=\"button\",this._geolocateButton.disabled=!0);},this._finishSetupUI=e=>{if(this._map){if(!1===e){t.w(\"Geolocation support is not available so the GeolocateControl will be disabled.\");const e=this._map._getUIString(\"GeolocateControl.LocationNotAvailable\");this._geolocateButton.disabled=!0,this._geolocateButton.title=e,this._geolocateButton.setAttribute(\"aria-label\",e);}else {const e=this._map._getUIString(\"GeolocateControl.FindMyLocation\");this._geolocateButton.disabled=!1,this._geolocateButton.title=e,this._geolocateButton.setAttribute(\"aria-label\",e);}this.options.trackUserLocation&&(this._geolocateButton.setAttribute(\"aria-pressed\",\"false\"),this._watchState=\"OFF\"),this.options.showUserLocation&&(this._dotElement=n.create(\"div\",\"maplibregl-user-location-dot\"),this._userLocationDotMarker=new Ka({element:this._dotElement}),this._circleElement=n.create(\"div\",\"maplibregl-user-location-accuracy-circle\"),this._accuracyCircleMarker=new Ka({element:this._circleElement,pitchAlignment:\"map\"}),this.options.trackUserLocation&&(this._watchState=\"OFF\"),this._map.on(\"zoom\",this._onZoom)),this._geolocateButton.addEventListener(\"click\",(()=>this.trigger())),this._setup=!0,this.options.trackUserLocation&&this._map.on(\"movestart\",(e=>{e.geolocateSource||\"ACTIVE_LOCK\"!==this._watchState||e.originalEvent&&\"resize\"===e.originalEvent.type||(this._watchState=\"BACKGROUND\",this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-background\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active\"),this.fire(new t.l(\"trackuserlocationend\")),this.fire(new t.l(\"userlocationlostfocus\")));}));}},this.options=t.e({},Ya,e);}onAdd(e){return this._map=e,this._container=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-group\"),this._setupUI(),function(){return t._(this,arguments,void 0,(function*(e=!1){if(void 0!==qa&&!e)return qa;if(void 0===window.navigator.permissions)return qa=!!window.navigator.geolocation,qa;try{const e=yield window.navigator.permissions.query({name:\"geolocation\"});qa=\"denied\"!==e.state;}catch(e){qa=!!window.navigator.geolocation;}return qa}))}().then((e=>this._finishSetupUI(e))),this._container}onRemove(){void 0!==this._geolocationWatchID&&(window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0),this.options.showUserLocation&&this._userLocationDotMarker&&this._userLocationDotMarker.remove(),this.options.showAccuracyCircle&&this._accuracyCircleMarker&&this._accuracyCircleMarker.remove(),n.remove(this._container),this._map.off(\"zoom\",this._onZoom),this._map=void 0,Qa=0,Ja=!1;}_isOutOfMapMaxBounds(e){const t=this._map.getMaxBounds(),i=e.coords;return t&&(i.longitude<t.getWest()||i.longitude>t.getEast()||i.latitude<t.getSouth()||i.latitude>t.getNorth())}_setErrorState(){switch(this._watchState){case \"WAITING_ACTIVE\":this._watchState=\"ACTIVE_ERROR\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-active-error\");break;case \"ACTIVE_LOCK\":this._watchState=\"ACTIVE_ERROR\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-active-error\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-waiting\");break;case \"BACKGROUND\":this._watchState=\"BACKGROUND_ERROR\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-background-error\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-waiting\");break;case \"ACTIVE_ERROR\":break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}}_updateCircleRadius(){const e=this._map.getBounds(),t=e.getSouthEast(),i=e.getNorthEast(),o=t.distanceTo(i),r=Math.ceil(this._accuracy/(o/this._map._container.clientHeight)*2);this._circleElement.style.width=`${r}px`,this._circleElement.style.height=`${r}px`;}trigger(){if(!this._setup)return t.w(\"Geolocate control triggered before added to a map\"),!1;if(this.options.trackUserLocation){switch(this._watchState){case \"OFF\":this._watchState=\"WAITING_ACTIVE\",this.fire(new t.l(\"trackuserlocationstart\"));break;case \"WAITING_ACTIVE\":case \"ACTIVE_LOCK\":case \"ACTIVE_ERROR\":case \"BACKGROUND_ERROR\":Qa--,Ja=!1,this._watchState=\"OFF\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-active-error\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background\"),this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background-error\"),this.fire(new t.l(\"trackuserlocationend\"));break;case \"BACKGROUND\":this._watchState=\"ACTIVE_LOCK\",this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-background\"),this._lastKnownPosition&&this._updateCamera(this._lastKnownPosition),this.fire(new t.l(\"trackuserlocationstart\")),this.fire(new t.l(\"userlocationfocus\"));break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}switch(this._watchState){case \"WAITING_ACTIVE\":this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-active\");break;case \"ACTIVE_LOCK\":this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-active\");break;case \"OFF\":break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}if(\"OFF\"===this._watchState&&void 0!==this._geolocationWatchID)this._clearWatch();else if(void 0===this._geolocationWatchID){let e;this._geolocateButton.classList.add(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.setAttribute(\"aria-pressed\",\"true\"),Qa++,Qa>1?(e={maximumAge:6e5,timeout:0},Ja=!0):(e=this.options.positionOptions,Ja=!1),this._geolocationWatchID=window.navigator.geolocation.watchPosition(this._onSuccess,this._onError,e);}}else window.navigator.geolocation.getCurrentPosition(this._onSuccess,this._onError,this.options.positionOptions),this._timeoutId=setTimeout(this._finish,1e4);return !0}_clearWatch(){window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0,this._geolocateButton.classList.remove(\"maplibregl-ctrl-geolocate-waiting\"),this._geolocateButton.setAttribute(\"aria-pressed\",\"false\"),this.options.showUserLocation&&this._updateMarker(null);}},e.GlobeControl=class{constructor(){this._toggleProjection=()=>{var e;const t=null===(e=this._map.getProjection())||void 0===e?void 0:e.type;this._map.setProjection(\"mercator\"!==t&&t?{type:\"mercator\"}:{type:\"globe\"}),this._updateGlobeIcon();},this._updateGlobeIcon=()=>{var e;this._globeButton.classList.remove(\"maplibregl-ctrl-globe\"),this._globeButton.classList.remove(\"maplibregl-ctrl-globe-enabled\"),\"globe\"===(null===(e=this._map.getProjection())||void 0===e?void 0:e.type)?(this._globeButton.classList.add(\"maplibregl-ctrl-globe-enabled\"),this._globeButton.title=this._map._getUIString(\"GlobeControl.Disable\")):(this._globeButton.classList.add(\"maplibregl-ctrl-globe\"),this._globeButton.title=this._map._getUIString(\"GlobeControl.Enable\"));};}onAdd(e){return this._map=e,this._container=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-group\"),this._globeButton=n.create(\"button\",\"maplibregl-ctrl-globe\",this._container),n.create(\"span\",\"maplibregl-ctrl-icon\",this._globeButton).setAttribute(\"aria-hidden\",\"true\"),this._globeButton.type=\"button\",this._globeButton.addEventListener(\"click\",this._toggleProjection),this._updateGlobeIcon(),this._map.on(\"styledata\",this._updateGlobeIcon),this._container}onRemove(){n.remove(this._container),this._map.off(\"styledata\",this._updateGlobeIcon),this._globeButton.removeEventListener(\"click\",this._toggleProjection),this._map=void 0;}},e.Hash=Er,e.ImageSource=X,e.KeyboardHandler=pa,e.LngLatBounds=G,e.LogoControl=Aa,e.Map=class extends Ra{constructor(e){var i,o;t.cv.mark(t.cw.create);const r=Object.assign(Object.assign(Object.assign({},Ga),e),{canvasContextAttributes:Object.assign(Object.assign({},Ga.canvasContextAttributes),e.canvasContextAttributes)});if(null!=r.minZoom&&null!=r.maxZoom&&r.minZoom>r.maxZoom)throw new Error(\"maxZoom must be greater than or equal to minZoom\");if(null!=r.minPitch&&null!=r.maxPitch&&r.minPitch>r.maxPitch)throw new Error(\"maxPitch must be greater than or equal to minPitch\");if(null!=r.minPitch&&r.minPitch<0)throw new Error(\"minPitch must be greater than or equal to 0\");if(null!=r.maxPitch&&r.maxPitch>180)throw new Error(\"maxPitch must be less than or equal to 180\");const a=new Lt,s=new Ot;if(void 0!==r.minZoom&&a.setMinZoom(r.minZoom),void 0!==r.maxZoom&&a.setMaxZoom(r.maxZoom),void 0!==r.minPitch&&a.setMinPitch(r.minPitch),void 0!==r.maxPitch&&a.setMaxPitch(r.maxPitch),void 0!==r.renderWorldCopies&&a.setRenderWorldCopies(r.renderWorldCopies),super(a,s,{bearingSnap:r.bearingSnap}),this._idleTriggered=!1,this._crossFadingFactor=1,this._renderTaskQueue=new La,this._controls=[],this._mapId=t.a6(),this._contextLost=e=>{e.preventDefault(),this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this.fire(new t.l(\"webglcontextlost\",{originalEvent:e}));},this._contextRestored=e=>{this._setupPainter(),this.resize(),this._update(),this.fire(new t.l(\"webglcontextrestored\",{originalEvent:e}));},this._onMapScroll=e=>{if(e.target===this._container)return this._container.scrollTop=0,this._container.scrollLeft=0,!1},this._onWindowOnline=()=>{this._update();},this._interactive=r.interactive,this._maxTileCacheSize=r.maxTileCacheSize,this._maxTileCacheZoomLevels=r.maxTileCacheZoomLevels,this._canvasContextAttributes=Object.assign({},r.canvasContextAttributes),this._trackResize=!0===r.trackResize,this._bearingSnap=r.bearingSnap,this._centerClampedToGround=r.centerClampedToGround,this._refreshExpiredTiles=!0===r.refreshExpiredTiles,this._fadeDuration=r.fadeDuration,this._crossSourceCollisions=!0===r.crossSourceCollisions,this._collectResourceTiming=!0===r.collectResourceTiming,this._locale=Object.assign(Object.assign({},Za),r.locale),this._clickTolerance=r.clickTolerance,this._overridePixelRatio=r.pixelRatio,this._maxCanvasSize=r.maxCanvasSize,this.transformCameraUpdate=r.transformCameraUpdate,this.cancelPendingTileRequestsWhileZooming=!0===r.cancelPendingTileRequestsWhileZooming,this._imageQueueHandle=p.addThrottleControl((()=>this.isMoving())),this._requestManager=new m(r.transformRequest),\"string\"==typeof r.container){if(this._container=document.getElementById(r.container),!this._container)throw new Error(`Container '${r.container}' not found.`)}else {if(!(r.container instanceof HTMLElement))throw new Error(\"Invalid type: 'container' must be a String or HTMLElement.\");this._container=r.container;}if(r.maxBounds&&this.setMaxBounds(r.maxBounds),this._setupContainer(),this._setupPainter(),this.on(\"move\",(()=>this._update(!1))),this.on(\"moveend\",(()=>this._update(!1))),this.on(\"zoom\",(()=>this._update(!0))),this.on(\"terrain\",(()=>{this.painter.terrainFacilitator.dirty=!0,this._update(!0);})),this.once(\"idle\",(()=>{this._idleTriggered=!0;})),\"undefined\"!=typeof window){addEventListener(\"online\",this._onWindowOnline,!1);let e=!1;const t=Sr((e=>{this._trackResize&&!this._removed&&(this.resize(e),this.redraw());}),50);this._resizeObserver=new ResizeObserver((i=>{e?t(i):e=!0;})),this._resizeObserver.observe(this._container);}this.handlers=new Ea(this,r),this._hash=r.hash&&new Er(\"string\"==typeof r.hash&&r.hash||void 0).addTo(this),this._hash&&this._hash._onHashChange()||(this.jumpTo({center:r.center,elevation:r.elevation,zoom:r.zoom,bearing:r.bearing,pitch:r.pitch,roll:r.roll}),r.bounds&&(this.resize(),this.fitBounds(r.bounds,t.e({},r.fitBoundsOptions,{duration:0}))));const n=\"string\"==typeof r.style||!(\"globe\"===(null===(o=null===(i=r.style)||void 0===i?void 0:i.projection)||void 0===o?void 0:o.type));this.resize(null,n),this._localIdeographFontFamily=r.localIdeographFontFamily,this._validateStyle=r.validateStyle,r.style&&this.setStyle(r.style,{localIdeographFontFamily:r.localIdeographFontFamily}),r.attributionControl&&this.addControl(new Da(\"boolean\"==typeof r.attributionControl?void 0:r.attributionControl)),r.maplibreLogo&&this.addControl(new Aa,r.logoPosition),this.on(\"style.load\",(()=>{if(n||this._resizeTransform(),this.transform.unmodified){const e=t.Q(this.style.stylesheet,[\"center\",\"zoom\",\"bearing\",\"pitch\",\"roll\"]);this.jumpTo(e);}})),this.on(\"data\",(e=>{this._update(\"style\"===e.dataType),this.fire(new t.l(`${e.dataType}data`,e));})),this.on(\"dataloading\",(e=>{this.fire(new t.l(`${e.dataType}dataloading`,e));})),this.on(\"dataabort\",(e=>{this.fire(new t.l(\"sourcedataabort\",e));}));}_getMapId(){return this._mapId}setGlobalStateProperty(e,t){return this.style.setGlobalStateProperty(e,t),this._update(!0)}getGlobalState(){return this.style.getGlobalState()}addControl(e,i){if(void 0===i&&(i=e.getDefaultPosition?e.getDefaultPosition():\"top-right\"),!e||!e.onAdd)return this.fire(new t.k(new Error(\"Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.\")));const o=e.onAdd(this);this._controls.push(e);const r=this._controlPositions[i];return -1!==i.indexOf(\"bottom\")?r.insertBefore(o,r.firstChild):r.appendChild(o),this}removeControl(e){if(!e||!e.onRemove)return this.fire(new t.k(new Error(\"Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.\")));const i=this._controls.indexOf(e);return i>-1&&this._controls.splice(i,1),e.onRemove(this),this}hasControl(e){return this._controls.indexOf(e)>-1}calculateCameraOptionsFromTo(e,t,i,o){return null==o&&this.terrain&&(o=this.terrain.getElevationForLngLatZoom(i,this.transform.tileZoom)),super.calculateCameraOptionsFromTo(e,t,i,o)}resize(e,i=!0){const[o,r]=this._containerDimensions(),a=this._getClampedPixelRatio(o,r);if(this._resizeCanvas(o,r,a),this.painter.resize(o,r,a),this.painter.overLimit()){const e=this.painter.context.gl;this._maxCanvasSize=[e.drawingBufferWidth,e.drawingBufferHeight];const t=this._getClampedPixelRatio(o,r);this._resizeCanvas(o,r,t),this.painter.resize(o,r,t);}this._resizeTransform(i);const s=!this._moving;return s&&(this.stop(),this.fire(new t.l(\"movestart\",e)).fire(new t.l(\"move\",e))),this.fire(new t.l(\"resize\",e)),s&&this.fire(new t.l(\"moveend\",e)),this}_resizeTransform(e=!0){var t;const[i,o]=this._containerDimensions();this.transform.resize(i,o,e),null===(t=this._requestedCameraState)||void 0===t||t.resize(i,o,e);}_getClampedPixelRatio(e,t){const{0:i,1:o}=this._maxCanvasSize,r=this.getPixelRatio(),a=e*r,s=t*r;return Math.min(a>i?i/a:1,s>o?o/s:1)*r}getPixelRatio(){var e;return null!==(e=this._overridePixelRatio)&&void 0!==e?e:devicePixelRatio}setPixelRatio(e){this._overridePixelRatio=e,this.resize();}getBounds(){return this.transform.getBounds()}getMaxBounds(){return this.transform.getMaxBounds()}setMaxBounds(e){return this.transform.setMaxBounds(G.convert(e)),this._update()}setMinZoom(e){if((e=null==e?-2:e)>=-2&&e<=this.transform.maxZoom)return this.transform.setMinZoom(e),this._update(),this.getZoom()<e&&this.setZoom(e),this;throw new Error(\"minZoom must be between -2 and the current maxZoom, inclusive\")}getMinZoom(){return this.transform.minZoom}setMaxZoom(e){if((e=null==e?22:e)>=this.transform.minZoom)return this.transform.setMaxZoom(e),this._update(),this.getZoom()>e&&this.setZoom(e),this;throw new Error(\"maxZoom must be greater than the current minZoom\")}getMaxZoom(){return this.transform.maxZoom}setMinPitch(e){if((e=null==e?0:e)<0)throw new Error(\"minPitch must be greater than or equal to 0\");if(e>=0&&e<=this.transform.maxPitch)return this.transform.setMinPitch(e),this._update(),this.getPitch()<e&&this.setPitch(e),this;throw new Error(\"minPitch must be between 0 and the current maxPitch, inclusive\")}getMinPitch(){return this.transform.minPitch}setMaxPitch(e){if((e=null==e?60:e)>180)throw new Error(\"maxPitch must be less than or equal to 180\");if(e>=this.transform.minPitch)return this.transform.setMaxPitch(e),this._update(),this.getPitch()>e&&this.setPitch(e),this;throw new Error(\"maxPitch must be greater than the current minPitch\")}getMaxPitch(){return this.transform.maxPitch}getRenderWorldCopies(){return this.transform.renderWorldCopies}setRenderWorldCopies(e){return this.transform.setRenderWorldCopies(e),this._update()}project(e){return this.transform.locationToScreenPoint(t.S.convert(e),this.style&&this.terrain)}unproject(e){return this.transform.screenPointToLocation(t.P.convert(e),this.terrain)}isMoving(){var e;return this._moving||(null===(e=this.handlers)||void 0===e?void 0:e.isMoving())}isZooming(){var e;return this._zooming||(null===(e=this.handlers)||void 0===e?void 0:e.isZooming())}isRotating(){var e;return this._rotating||(null===(e=this.handlers)||void 0===e?void 0:e.isRotating())}_createDelegatedListener(e,t,i){if(\"mouseenter\"===e||\"mouseover\"===e){let o=!1;const r=r=>{const a=t.filter((e=>this.getLayer(e))),s=0!==a.length?this.queryRenderedFeatures(r.point,{layers:a}):[];s.length?o||(o=!0,i.call(this,new jr(e,this,r.originalEvent,{features:s}))):o=!1;};return {layers:t,listener:i,delegates:{mousemove:r,mouseout:()=>{o=!1;}}}}if(\"mouseleave\"===e||\"mouseout\"===e){let o=!1;const r=r=>{const a=t.filter((e=>this.getLayer(e)));(0!==a.length?this.queryRenderedFeatures(r.point,{layers:a}):[]).length?o=!0:o&&(o=!1,i.call(this,new jr(e,this,r.originalEvent)));},a=t=>{o&&(o=!1,i.call(this,new jr(e,this,t.originalEvent)));};return {layers:t,listener:i,delegates:{mousemove:r,mouseout:a}}}{const o=e=>{const o=t.filter((e=>this.getLayer(e))),r=0!==o.length?this.queryRenderedFeatures(e.point,{layers:o}):[];r.length&&(e.features=r,i.call(this,e),delete e.features);};return {layers:t,listener:i,delegates:{[e]:o}}}}_saveDelegatedListener(e,t){this._delegatedListeners=this._delegatedListeners||{},this._delegatedListeners[e]=this._delegatedListeners[e]||[],this._delegatedListeners[e].push(t);}_removeDelegatedListener(e,t,i){if(!this._delegatedListeners||!this._delegatedListeners[e])return;const o=this._delegatedListeners[e];for(let e=0;e<o.length;e++){const r=o[e];if(r.listener===i&&r.layers.length===t.length&&r.layers.every((e=>t.includes(e)))){for(const e in r.delegates)this.off(e,r.delegates[e]);return void o.splice(e,1)}}}on(e,t,i){if(void 0===i)return super.on(e,t);const o=\"string\"==typeof t?[t]:t,r=this._createDelegatedListener(e,o,i);this._saveDelegatedListener(e,r);for(const e in r.delegates)this.on(e,r.delegates[e]);return {unsubscribe:()=>{this._removeDelegatedListener(e,o,i);}}}once(e,t,i){if(void 0===i)return super.once(e,t);const o=\"string\"==typeof t?[t]:t,r=this._createDelegatedListener(e,o,i);for(const t in r.delegates){const a=r.delegates[t];r.delegates[t]=(...t)=>{this._removeDelegatedListener(e,o,i),a(...t);};}this._saveDelegatedListener(e,r);for(const e in r.delegates)this.once(e,r.delegates[e]);return this}off(e,t,i){return void 0===i?super.off(e,t):(this._removeDelegatedListener(e,\"string\"==typeof t?[t]:t,i),this)}queryRenderedFeatures(e,i){if(!this.style)return [];let o;const r=e instanceof t.P||Array.isArray(e),a=r?e:[[0,0],[this.transform.width,this.transform.height]];if(i=i||(r?{}:e)||{},a instanceof t.P||\"number\"==typeof a[0])o=[t.P.convert(a)];else {const e=t.P.convert(a[0]),i=t.P.convert(a[1]);o=[e,new t.P(i.x,e.y),i,new t.P(e.x,i.y),e];}return this.style.queryRenderedFeatures(o,i,this.transform)}querySourceFeatures(e,t){return this.style.querySourceFeatures(e,t)}setStyle(e,i){return !1!==(i=t.e({},{localIdeographFontFamily:this._localIdeographFontFamily,validate:this._validateStyle},i)).diff&&i.localIdeographFontFamily===this._localIdeographFontFamily&&this.style&&e?(this._diffStyle(e,i),this):(this._localIdeographFontFamily=i.localIdeographFontFamily,this._updateStyle(e,i))}setTransformRequest(e){return this._requestManager.setTransformRequest(e),this}_getUIString(e){const t=this._locale[e];if(null==t)throw new Error(`Missing UI string '${e}'`);return t}_updateStyle(e,t){var i,o;if(t.transformStyle&&this.style&&!this.style._loaded)return void this.style.once(\"style.load\",(()=>this._updateStyle(e,t)));const r=this.style&&t.transformStyle?this.style.serialize():void 0;return this.style&&(this.style.setEventedParent(null),this.style._remove(!e)),e?(this.style=new wi(this,t||{}),this.style.setEventedParent(this,{style:this.style}),\"string\"==typeof e?this.style.loadURL(e,t,r):this.style.loadJSON(e,t,r),this):(null===(o=null===(i=this.style)||void 0===i?void 0:i.projection)||void 0===o||o.destroy(),delete this.style,this)}_lazyInitEmptyStyle(){this.style||(this.style=new wi(this,{}),this.style.setEventedParent(this,{style:this.style}),this.style.loadEmpty());}_diffStyle(e,i){if(\"string\"==typeof e){const o=this._requestManager.transformRequest(e,\"Style\");t.j(o,new AbortController).then((e=>{this._updateDiff(e.data,i);})).catch((e=>{e&&this.fire(new t.k(e));}));}else \"object\"==typeof e&&this._updateDiff(e,i);}_updateDiff(e,i){try{this.style.setState(e,i)&&this._update(!0);}catch(o){t.w(`Unable to perform style diff: ${o.message||o.error||o}.  Rebuilding the style from scratch.`),this._updateStyle(e,i);}}getStyle(){if(this.style)return this.style.serialize()}isStyleLoaded(){return this.style?this.style.loaded():t.w(\"There is no style added to the map.\")}addSource(e,t){return this._lazyInitEmptyStyle(),this.style.addSource(e,t),this._update(!0)}isSourceLoaded(e){const i=this.style&&this.style.sourceCaches[e];if(void 0!==i)return i.loaded();this.fire(new t.k(new Error(`There is no source with ID '${e}'`)));}setTerrain(e){if(this.style._checkLoaded(),this._terrainDataCallback&&this.style.off(\"data\",this._terrainDataCallback),e){const i=this.style.sourceCaches[e.source];if(!i)throw new Error(`cannot load terrain, because there exists no source with ID: ${e.source}`);null===this.terrain&&i.reload();for(const i in this.style._layers){const o=this.style._layers[i];\"hillshade\"===o.type&&o.source===e.source&&t.w(\"You are using the same source for a hillshade layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.\"),\"color-relief\"===o.type&&o.source===e.source&&t.w(\"You are using the same source for a color-relief layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.\");}this.terrain=new Ba(this.painter,i,e),this.painter.renderToTexture=new Na(this.painter,this.terrain),this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this._terrainDataCallback=t=>{var i;\"style\"===t.dataType?this.terrain.sourceCache.freeRtt():\"source\"===t.dataType&&t.tile&&(t.sourceId!==e.source||this._elevationFreeze||(this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this._centerClampedToGround&&this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom))),\"image\"===(null===(i=t.source)||void 0===i?void 0:i.type)?this.terrain.sourceCache.freeRtt():this.terrain.sourceCache.freeRtt(t.tile.tileID));},this.style.on(\"data\",this._terrainDataCallback);}else this.terrain&&this.terrain.sourceCache.destruct(),this.terrain=null,this.painter.renderToTexture&&this.painter.renderToTexture.destruct(),this.painter.renderToTexture=null,this.transform.setMinElevationForCurrentTile(0),this._centerClampedToGround&&this.transform.setElevation(0);return this.fire(new t.l(\"terrain\",{terrain:e})),this}getTerrain(){var e,t;return null!==(t=null===(e=this.terrain)||void 0===e?void 0:e.options)&&void 0!==t?t:null}areTilesLoaded(){const e=this.style&&this.style.sourceCaches;for(const t in e){const i=e[t]._tiles;for(const e in i){const t=i[e];if(\"loaded\"!==t.state&&\"errored\"!==t.state)return !1}}return !0}removeSource(e){return this.style.removeSource(e),this._update(!0)}getSource(e){return this.style.getSource(e)}setSourceTileLodParams(e,t,i){if(i){const o=this.getSource(i);if(!o)throw new Error(`There is no source with ID \"${i}\", cannot set LOD parameters`);o.calculateTileZoom=me(Math.max(1,e),Math.max(1,t));}else for(const i in this.style.sourceCaches)this.style.sourceCaches[i].getSource().calculateTileZoom=me(Math.max(1,e),Math.max(1,t));return this._update(!0),this}refreshTiles(e,i){const o=this.style.sourceCaches[e];if(!o)throw new Error(`There is no source cache with ID \"${e}\", cannot refresh tile`);void 0===i?o.reload(!0):o.refreshTiles(i.map((e=>new t.a3(e.z,e.x,e.y))));}addImage(e,i,o={}){const{pixelRatio:r=1,sdf:a=!1,stretchX:n,stretchY:l,content:c,textFitWidth:h,textFitHeight:u}=o;if(this._lazyInitEmptyStyle(),!(i instanceof HTMLImageElement||t.b(i))){if(void 0===i.width||void 0===i.height)return this.fire(new t.k(new Error(\"Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`\")));{const{width:o,height:s,data:d}=i,_=i;return this.style.addImage(e,{data:new t.R({width:o,height:s},new Uint8Array(d)),pixelRatio:r,stretchX:n,stretchY:l,content:c,textFitWidth:h,textFitHeight:u,sdf:a,version:0,userImage:_}),_.onAdd&&_.onAdd(this,e),this}}{const{width:o,height:d,data:_}=s.getImageData(i);this.style.addImage(e,{data:new t.R({width:o,height:d},_),pixelRatio:r,stretchX:n,stretchY:l,content:c,textFitWidth:h,textFitHeight:u,sdf:a,version:0});}}updateImage(e,i){const o=this.style.getImage(e);if(!o)return this.fire(new t.k(new Error(\"The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.\")));const r=i instanceof HTMLImageElement||t.b(i)?s.getImageData(i):i,{width:a,height:n,data:l}=r;if(void 0===a||void 0===n)return this.fire(new t.k(new Error(\"Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`\")));if(a!==o.data.width||n!==o.data.height)return this.fire(new t.k(new Error(\"The width and height of the updated image must be that same as the previous version of the image\")));const c=!(i instanceof HTMLImageElement||t.b(i));return o.data.replace(l,c),this.style.updateImage(e,o),this}getImage(e){return this.style.getImage(e)}hasImage(e){return e?!!this.style.getImage(e):(this.fire(new t.k(new Error(\"Missing required image id\"))),!1)}removeImage(e){this.style.removeImage(e);}loadImage(e){return p.getImage(this._requestManager.transformRequest(e,\"Image\"),new AbortController)}listImages(){return this.style.listImages()}addLayer(e,t){return this._lazyInitEmptyStyle(),this.style.addLayer(e,t),this._update(!0)}moveLayer(e,t){return this.style.moveLayer(e,t),this._update(!0)}removeLayer(e){return this.style.removeLayer(e),this._update(!0)}getLayer(e){return this.style.getLayer(e)}getLayersOrder(){return this.style.getLayersOrder()}setLayerZoomRange(e,t,i){return this.style.setLayerZoomRange(e,t,i),this._update(!0)}setFilter(e,t,i={}){return this.style.setFilter(e,t,i),this._update(!0)}getFilter(e){return this.style.getFilter(e)}setPaintProperty(e,t,i,o={}){return this.style.setPaintProperty(e,t,i,o),this._update(!0)}getPaintProperty(e,t){return this.style.getPaintProperty(e,t)}setLayoutProperty(e,t,i,o={}){return this.style.setLayoutProperty(e,t,i,o),this._update(!0)}getLayoutProperty(e,t){return this.style.getLayoutProperty(e,t)}setGlyphs(e,t={}){return this._lazyInitEmptyStyle(),this.style.setGlyphs(e,t),this._update(!0)}getGlyphs(){return this.style.getGlyphsUrl()}addSprite(e,t,i={}){return this._lazyInitEmptyStyle(),this.style.addSprite(e,t,i,(e=>{e||this._update(!0);})),this}removeSprite(e){return this._lazyInitEmptyStyle(),this.style.removeSprite(e),this._update(!0)}getSprite(){return this.style.getSprite()}setSprite(e,t={}){return this._lazyInitEmptyStyle(),this.style.setSprite(e,t,(e=>{e||this._update(!0);})),this}setLight(e,t={}){return this._lazyInitEmptyStyle(),this.style.setLight(e,t),this._update(!0)}getLight(){return this.style.getLight()}setSky(e,t={}){return this._lazyInitEmptyStyle(),this.style.setSky(e,t),this._update(!0)}getSky(){return this.style.getSky()}setFeatureState(e,t){return this.style.setFeatureState(e,t),this._update()}removeFeatureState(e,t){return this.style.removeFeatureState(e,t),this._update()}getFeatureState(e){return this.style.getFeatureState(e)}getContainer(){return this._container}getCanvasContainer(){return this._canvasContainer}getCanvas(){return this._canvas}_containerDimensions(){let e=0,t=0;return this._container&&(e=this._container.clientWidth||400,t=this._container.clientHeight||300),[e,t]}_setupContainer(){const e=this._container;e.classList.add(\"maplibregl-map\");const t=this._canvasContainer=n.create(\"div\",\"maplibregl-canvas-container\",e);this._interactive&&t.classList.add(\"maplibregl-interactive\"),this._canvas=n.create(\"canvas\",\"maplibregl-canvas\",t),this._canvas.addEventListener(\"webglcontextlost\",this._contextLost,!1),this._canvas.addEventListener(\"webglcontextrestored\",this._contextRestored,!1),this._canvas.setAttribute(\"tabindex\",this._interactive?\"0\":\"-1\"),this._canvas.setAttribute(\"aria-label\",this._getUIString(\"Map.Title\")),this._canvas.setAttribute(\"role\",\"region\");const i=this._containerDimensions(),o=this._getClampedPixelRatio(i[0],i[1]);this._resizeCanvas(i[0],i[1],o);const r=this._controlContainer=n.create(\"div\",\"maplibregl-control-container\",e),a=this._controlPositions={};[\"top-left\",\"top-right\",\"bottom-left\",\"bottom-right\"].forEach((e=>{a[e]=n.create(\"div\",`maplibregl-ctrl-${e} `,r);})),this._container.addEventListener(\"scroll\",this._onMapScroll,!1);}_resizeCanvas(e,t,i){this._canvas.width=Math.floor(i*e),this._canvas.height=Math.floor(i*t),this._canvas.style.width=`${e}px`,this._canvas.style.height=`${t}px`;}_setupPainter(){const e=Object.assign(Object.assign({},this._canvasContextAttributes),{alpha:!0,depth:!0,stencil:!0,premultipliedAlpha:!0});let t=null;this._canvas.addEventListener(\"webglcontextcreationerror\",(i=>{t={requestedAttributes:e},i&&(t.statusMessage=i.statusMessage,t.type=i.type);}),{once:!0});let i=null;if(i=this._canvasContextAttributes.contextType?this._canvas.getContext(this._canvasContextAttributes.contextType,e):this._canvas.getContext(\"webgl2\",e)||this._canvas.getContext(\"webgl\",e),!i){const e=\"Failed to initialize WebGL\";throw t?(t.message=e,new Error(JSON.stringify(t))):new Error(e)}this.painter=new Mr(i,this.transform),l.testSupport(i);}migrateProjection(e,i){super.migrateProjection(e,i),this.painter.transform=e,this.fire(new t.l(\"projectiontransition\",{newProjection:this.style.projection.name}));}loaded(){return !this._styleDirty&&!this._sourcesDirty&&!!this.style&&this.style.loaded()}_update(e){return this.style&&this.style._loaded?(this._styleDirty=this._styleDirty||e,this._sourcesDirty=!0,this.triggerRepaint(),this):this}_requestRenderFrame(e){return this._update(),this._renderTaskQueue.add(e)}_cancelRenderFrame(e){this._renderTaskQueue.remove(e);}_render(e){var i,o,r,a,n;const l=this._idleTriggered?this._fadeDuration:0,c=(null===(i=this.style.projection)||void 0===i?void 0:i.transitionState)>0;if(this.painter.context.setDirty(),this.painter.setBaseState(),this._renderTaskQueue.run(e),this._removed)return;let h=!1;if(this.style&&this._styleDirty){this._styleDirty=!1;const e=this.transform.zoom,i=s.now();this.style.zoomHistory.update(e,i);const o=new t.F(e,{now:i,fadeDuration:l,zoomHistory:this.style.zoomHistory,transition:this.style.getTransition(),globalState:this.style.getGlobalState()}),r=o.crossFadingFactor();1===r&&r===this._crossFadingFactor||(h=!0,this._crossFadingFactor=r),this.style.update(o);}const u=(null===(o=this.style.projection)||void 0===o?void 0:o.transitionState)>0!==c;null===(r=this.style.projection)||void 0===r||r.setErrorQueryLatitudeDegrees(this.transform.center.lat),this.transform.setTransitionState(null===(a=this.style.projection)||void 0===a?void 0:a.transitionState,null===(n=this.style.projection)||void 0===n?void 0:n.latitudeErrorCorrectionRadians),this.style&&(this._sourcesDirty||u)&&(this._sourcesDirty=!1,this.style._updateSources(this.transform)),this.terrain?(this.terrain.sourceCache.update(this.transform,this.terrain),this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),!this._elevationFreeze&&this._centerClampedToGround&&this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom))):(this.transform.setMinElevationForCurrentTile(0),this._centerClampedToGround&&this.transform.setElevation(0)),this._placementDirty=this.style&&this.style._updatePlacement(this.transform,this.showCollisionBoxes,l,this._crossSourceCollisions,u),this.painter.render(this.style,{showTileBoundaries:this.showTileBoundaries,showOverdrawInspector:this._showOverdrawInspector,rotating:this.isRotating(),zooming:this.isZooming(),moving:this.isMoving(),fadeDuration:l,showPadding:this.showPadding}),this.fire(new t.l(\"render\")),this.loaded()&&!this._loaded&&(this._loaded=!0,t.cv.mark(t.cw.load),this.fire(new t.l(\"load\"))),this.style&&(this.style.hasTransitions()||h)&&(this._styleDirty=!0),this.style&&!this._placementDirty&&this.style._releaseSymbolFadeTiles();const d=this._sourcesDirty||this._styleDirty||this._placementDirty;return d||this._repaint?this.triggerRepaint():!this.isMoving()&&this.loaded()&&this.fire(new t.l(\"idle\")),!this._loaded||this._fullyLoaded||d||(this._fullyLoaded=!0,t.cv.mark(t.cw.fullLoad)),this}redraw(){return this.style&&(this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._render(0)),this}remove(){var e;this._hash&&this._hash.remove();for(const e of this._controls)e.onRemove(this);this._controls=[],this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._renderTaskQueue.clear(),this.painter.destroy(),this.handlers.destroy(),delete this.handlers,this.setStyle(null),\"undefined\"!=typeof window&&removeEventListener(\"online\",this._onWindowOnline,!1),p.removeThrottleControl(this._imageQueueHandle),null===(e=this._resizeObserver)||void 0===e||e.disconnect();const i=this.painter.context.gl.getExtension(\"WEBGL_lose_context\");(null==i?void 0:i.loseContext)&&i.loseContext(),this._canvas.removeEventListener(\"webglcontextrestored\",this._contextRestored,!1),this._canvas.removeEventListener(\"webglcontextlost\",this._contextLost,!1),n.remove(this._canvasContainer),n.remove(this._controlContainer),this._container.removeEventListener(\"scroll\",this._onMapScroll,!1),this._container.classList.remove(\"maplibregl-map\"),t.cv.clearMetrics(),this._removed=!0,this.fire(new t.l(\"remove\"));}triggerRepaint(){this.style&&!this._frameRequest&&(this._frameRequest=new AbortController,s.frame(this._frameRequest,(e=>{t.cv.frame(e),this._frameRequest=null;try{this._render(e);}catch(e){if(!t.cx(e)&&!function(e){return e.message===qo}(e))throw e}}),(()=>{})));}get showTileBoundaries(){return !!this._showTileBoundaries}set showTileBoundaries(e){this._showTileBoundaries!==e&&(this._showTileBoundaries=e,this._update());}get showPadding(){return !!this._showPadding}set showPadding(e){this._showPadding!==e&&(this._showPadding=e,this._update());}get showCollisionBoxes(){return !!this._showCollisionBoxes}set showCollisionBoxes(e){this._showCollisionBoxes!==e&&(this._showCollisionBoxes=e,e?this.style._generateCollisionBoxes():this._update());}get showOverdrawInspector(){return !!this._showOverdrawInspector}set showOverdrawInspector(e){this._showOverdrawInspector!==e&&(this._showOverdrawInspector=e,this._update());}get repaint(){return !!this._repaint}set repaint(e){this._repaint!==e&&(this._repaint=e,this.triggerRepaint());}get vertices(){return !!this._vertices}set vertices(e){this._vertices=e,this._update();}get version(){return Ua}getCameraTargetElevation(){return this.transform.elevation}getProjection(){return this.style.getProjection()}setProjection(e){return this._lazyInitEmptyStyle(),this.style.setProjection(e),this._update(!0)}},e.MapMouseEvent=jr,e.MapTouchEvent=Nr,e.MapWheelEvent=Zr,e.Marker=Ka,e.NavigationControl=class{constructor(e){this._updateZoomButtons=()=>{const e=this._map.getZoom(),t=e===this._map.getMaxZoom(),i=e===this._map.getMinZoom();this._zoomInButton.disabled=t,this._zoomOutButton.disabled=i,this._zoomInButton.setAttribute(\"aria-disabled\",t.toString()),this._zoomOutButton.setAttribute(\"aria-disabled\",i.toString());},this._rotateCompassArrow=()=>{this._compassIcon.style.transform=this.options.visualizePitch&&this.options.visualizeRoll?`scale(${1/Math.pow(Math.cos(this._map.transform.pitchInRadians),.5)}) rotateZ(${-this._map.transform.roll}deg) rotateX(${this._map.transform.pitch}deg) rotateZ(${-this._map.transform.bearing}deg)`:this.options.visualizePitch?`scale(${1/Math.pow(Math.cos(this._map.transform.pitchInRadians),.5)}) rotateX(${this._map.transform.pitch}deg) rotateZ(${-this._map.transform.bearing}deg)`:this.options.visualizeRoll?`rotate(${-this._map.transform.bearing-this._map.transform.roll}deg)`:`rotate(${-this._map.transform.bearing}deg)`;},this._setButtonTitle=(e,t)=>{const i=this._map._getUIString(`NavigationControl.${t}`);e.title=i,e.setAttribute(\"aria-label\",i);},this.options=t.e({},Va,e),this._container=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-group\"),this._container.addEventListener(\"contextmenu\",(e=>e.preventDefault())),this.options.showZoom&&(this._zoomInButton=this._createButton(\"maplibregl-ctrl-zoom-in\",(e=>this._map.zoomIn({},{originalEvent:e}))),n.create(\"span\",\"maplibregl-ctrl-icon\",this._zoomInButton).setAttribute(\"aria-hidden\",\"true\"),this._zoomOutButton=this._createButton(\"maplibregl-ctrl-zoom-out\",(e=>this._map.zoomOut({},{originalEvent:e}))),n.create(\"span\",\"maplibregl-ctrl-icon\",this._zoomOutButton).setAttribute(\"aria-hidden\",\"true\")),this.options.showCompass&&(this._compass=this._createButton(\"maplibregl-ctrl-compass\",(e=>{this.options.visualizePitch?this._map.resetNorthPitch({},{originalEvent:e}):this._map.resetNorth({},{originalEvent:e});})),this._compassIcon=n.create(\"span\",\"maplibregl-ctrl-icon\",this._compass),this._compassIcon.setAttribute(\"aria-hidden\",\"true\"));}onAdd(e){return this._map=e,this.options.showZoom&&(this._setButtonTitle(this._zoomInButton,\"ZoomIn\"),this._setButtonTitle(this._zoomOutButton,\"ZoomOut\"),this._map.on(\"zoom\",this._updateZoomButtons),this._updateZoomButtons()),this.options.showCompass&&(this._setButtonTitle(this._compass,\"ResetBearing\"),this.options.visualizePitch&&this._map.on(\"pitch\",this._rotateCompassArrow),this.options.visualizeRoll&&this._map.on(\"roll\",this._rotateCompassArrow),this._map.on(\"rotate\",this._rotateCompassArrow),this._rotateCompassArrow(),this._handler=new $a(this._map,this._compass,this.options.visualizePitch)),this._container}onRemove(){n.remove(this._container),this.options.showZoom&&this._map.off(\"zoom\",this._updateZoomButtons),this.options.showCompass&&(this.options.visualizePitch&&this._map.off(\"pitch\",this._rotateCompassArrow),this.options.visualizeRoll&&this._map.off(\"roll\",this._rotateCompassArrow),this._map.off(\"rotate\",this._rotateCompassArrow),this._handler.off(),delete this._handler),delete this._map;}_createButton(e,t){const i=n.create(\"button\",e,this._container);return i.type=\"button\",i.addEventListener(\"click\",t),i}},e.Popup=class extends t.E{constructor(e){super(),this._updateOpacity=()=>{void 0!==this.options.locationOccludedOpacity&&(this._container.style.opacity=this._map.transform.isLocationOccluded(this.getLngLat())?`${this.options.locationOccludedOpacity}`:void 0);},this.remove=()=>(this._content&&n.remove(this._content),this._container&&(n.remove(this._container),delete this._container),this._map&&(this._map.off(\"move\",this._update),this._map.off(\"move\",this._onClose),this._map.off(\"click\",this._onClose),this._map.off(\"remove\",this.remove),this._map.off(\"mousemove\",this._onMouseMove),this._map.off(\"mouseup\",this._onMouseUp),this._map.off(\"drag\",this._onDrag),this._map._canvasContainer.classList.remove(\"maplibregl-track-pointer\"),delete this._map,this.fire(new t.l(\"close\"))),this),this._onMouseUp=e=>{this._update(e.point);},this._onMouseMove=e=>{this._update(e.point);},this._onDrag=e=>{this._update(e.point);},this._update=e=>{if(!this._map||!this._lngLat&&!this._trackPointer||!this._content)return;if(!this._container){if(this._container=n.create(\"div\",\"maplibregl-popup\",this._map.getContainer()),this._tip=n.create(\"div\",\"maplibregl-popup-tip\",this._container),this._container.appendChild(this._content),this.options.className)for(const e of this.options.className.split(\" \"))this._container.classList.add(e);this._closeButton&&this._closeButton.setAttribute(\"aria-label\",this._map._getUIString(\"Popup.Close\")),this._trackPointer&&this._container.classList.add(\"maplibregl-popup-track-pointer\");}if(this.options.maxWidth&&this._container.style.maxWidth!==this.options.maxWidth&&(this._container.style.maxWidth=this.options.maxWidth),this._lngLat=Wa(this._lngLat,this._flatPos,this._map.transform,this._trackPointer),this._trackPointer&&!e)return;const t=this._flatPos=this._pos=this._trackPointer&&e?e:this._map.project(this._lngLat);this._map.terrain&&(this._flatPos=this._trackPointer&&e?e:this._map.transform.locationToScreenPoint(this._lngLat));let i=this.options.anchor;const o=as(this.options.offset);if(!i){const e=this._container.offsetWidth,r=this._container.offsetHeight;let a;a=t.y+o.bottom.y<r?[\"top\"]:t.y>this._map.transform.height-r?[\"bottom\"]:[],t.x<e/2?a.push(\"left\"):t.x>this._map.transform.width-e/2&&a.push(\"right\"),i=0===a.length?\"bottom\":a.join(\"-\");}let r=t.add(o[i]);this.options.subpixelPositioning||(r=r.round()),n.setTransform(this._container,`${Ha[i]} translate(${r.x}px,${r.y}px)`),Xa(this._container,i,\"popup\"),this._updateOpacity();},this._onClose=()=>{this.remove();},this.options=t.e(Object.create(os),e);}addTo(e){return this._map&&this.remove(),this._map=e,this.options.closeOnClick&&this._map.on(\"click\",this._onClose),this.options.closeOnMove&&this._map.on(\"move\",this._onClose),this._map.on(\"remove\",this.remove),this._update(),this._focusFirstElement(),this._trackPointer?(this._map.on(\"mousemove\",this._onMouseMove),this._map.on(\"mouseup\",this._onMouseUp),this._container&&this._container.classList.add(\"maplibregl-popup-track-pointer\"),this._map._canvasContainer.classList.add(\"maplibregl-track-pointer\")):this._map.on(\"move\",this._update),this.fire(new t.l(\"open\")),this}isOpen(){return !!this._map}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.S.convert(e),this._pos=null,this._flatPos=null,this._trackPointer=!1,this._update(),this._map&&(this._map.on(\"move\",this._update),this._map.off(\"mousemove\",this._onMouseMove),this._container&&this._container.classList.remove(\"maplibregl-popup-track-pointer\"),this._map._canvasContainer.classList.remove(\"maplibregl-track-pointer\")),this}trackPointer(){return this._trackPointer=!0,this._pos=null,this._flatPos=null,this._update(),this._map&&(this._map.off(\"move\",this._update),this._map.on(\"mousemove\",this._onMouseMove),this._map.on(\"drag\",this._onDrag),this._container&&this._container.classList.add(\"maplibregl-popup-track-pointer\"),this._map._canvasContainer.classList.add(\"maplibregl-track-pointer\")),this}getElement(){return this._container}setText(e){return this.setDOMContent(document.createTextNode(e))}setHTML(e){const t=document.createDocumentFragment(),i=document.createElement(\"body\");let o;for(i.innerHTML=e;o=i.firstChild,o;)t.appendChild(o);return this.setDOMContent(t)}getMaxWidth(){var e;return null===(e=this._container)||void 0===e?void 0:e.style.maxWidth}setMaxWidth(e){return this.options.maxWidth=e,this._update(),this}setDOMContent(e){if(this._content)for(;this._content.hasChildNodes();)this._content.firstChild&&this._content.removeChild(this._content.firstChild);else this._content=n.create(\"div\",\"maplibregl-popup-content\",this._container);return this._content.appendChild(e),this._createCloseButton(),this._update(),this._focusFirstElement(),this}addClassName(e){return this._container&&this._container.classList.add(e),this}removeClassName(e){return this._container&&this._container.classList.remove(e),this}setOffset(e){return this.options.offset=e,this._update(),this}toggleClassName(e){if(this._container)return this._container.classList.toggle(e)}setSubpixelPositioning(e){this.options.subpixelPositioning=e;}_createCloseButton(){this.options.closeButton&&(this._closeButton=n.create(\"button\",\"maplibregl-popup-close-button\",this._content),this._closeButton.type=\"button\",this._closeButton.innerHTML=\"&#215;\",this._closeButton.addEventListener(\"click\",this._onClose));}_focusFirstElement(){if(!this.options.focusAfterOpen||!this._container)return;const e=this._container.querySelector(rs);e&&e.focus();}},e.RasterDEMTileSource=W,e.RasterTileSource=q,e.ScaleControl=class{constructor(e){this._onMove=()=>{ts(this._map,this._container,this.options);},this.setUnit=e=>{this.options.unit=e,ts(this._map,this._container,this.options);},this.options=Object.assign(Object.assign({},es),e);}getDefaultPosition(){return \"bottom-left\"}onAdd(e){return this._map=e,this._container=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-scale\",e.getContainer()),this._map.on(\"move\",this._onMove),this._onMove(),this._container}onRemove(){n.remove(this._container),this._map.off(\"move\",this._onMove),this._map=void 0;}},e.ScrollZoomHandler=va,e.Style=wi,e.TerrainControl=class{constructor(e){this._toggleTerrain=()=>{this._map.getTerrain()?this._map.setTerrain(null):this._map.setTerrain(this.options),this._updateTerrainIcon();},this._updateTerrainIcon=()=>{this._terrainButton.classList.remove(\"maplibregl-ctrl-terrain\"),this._terrainButton.classList.remove(\"maplibregl-ctrl-terrain-enabled\"),this._map.terrain?(this._terrainButton.classList.add(\"maplibregl-ctrl-terrain-enabled\"),this._terrainButton.title=this._map._getUIString(\"TerrainControl.Disable\")):(this._terrainButton.classList.add(\"maplibregl-ctrl-terrain\"),this._terrainButton.title=this._map._getUIString(\"TerrainControl.Enable\"));},this.options=e;}onAdd(e){return this._map=e,this._container=n.create(\"div\",\"maplibregl-ctrl maplibregl-ctrl-group\"),this._terrainButton=n.create(\"button\",\"maplibregl-ctrl-terrain\",this._container),n.create(\"span\",\"maplibregl-ctrl-icon\",this._terrainButton).setAttribute(\"aria-hidden\",\"true\"),this._terrainButton.type=\"button\",this._terrainButton.addEventListener(\"click\",this._toggleTerrain),this._updateTerrainIcon(),this._map.on(\"terrain\",this._updateTerrainIcon),this._container}onRemove(){n.remove(this._container),this._map.off(\"terrain\",this._updateTerrainIcon),this._map=void 0;}},e.TwoFingersTouchPitchHandler=da,e.TwoFingersTouchRotateHandler=ha,e.TwoFingersTouchZoomHandler=la,e.TwoFingersTouchZoomRotateHandler=Pa,e.VectorTileSource=$,e.VideoSource=K,e.addSourceType=(e,i)=>t._(void 0,void 0,void 0,(function*(){if(J(e))throw new Error(`A source type called \"${e}\" already exists.`);((e,t)=>{Q[e]=t;})(e,i);})),e.clearPrewarmedResources=function(){const e=A;e&&(e.isPreloaded()&&1===e.numActive()?(e.release(R),A=null):console.warn(\"Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()\"));},e.createTileMesh=Xt,e.getMaxParallelImageRequests=function(){return t.a.MAX_PARALLEL_IMAGE_REQUESTS},e.getRTLTextPluginStatus=function(){return oe().getRTLTextPluginStatus()},e.getVersion=function(){return ss},e.getWorkerCount=function(){return z.workerCount},e.getWorkerUrl=function(){return t.a.WORKER_URL},e.importScriptInWorkers=function(e){return B().broadcast(\"IS\",e)},e.prewarm=function(){k().acquire(R);},e.setMaxParallelImageRequests=function(e){t.a.MAX_PARALLEL_IMAGE_REQUESTS=e;},e.setRTLTextPlugin=function(e,t){return oe().setRTLTextPlugin(e,t)},e.setWorkerCount=function(e){z.workerCount=e;},e.setWorkerUrl=function(e){t.a.WORKER_URL=e;};}));\r\n\r\n//\r\n// Our custom intro provides a specialized \"define()\" function, called by the\r\n// AMD modules below, that sets up the worker blob URL and then executes the\r\n// main module, storing its exported value as 'maplibregl'\r\n\r\n\r\nvar maplibregl$1 = maplibregl;\r\n\r\nreturn maplibregl$1;\r\n\r\n}));\r\n//# sourceMappingURL=maplibre-gl.js.map\r\n","modules/coach-world/vendor/martini/martini.mjs":"\r\nexport default class Martini {\r\n    constructor(gridSize = 257) {\r\n        this.gridSize = gridSize;\r\n        const tileSize = gridSize - 1;\r\n        if (tileSize & (tileSize - 1)) throw new Error(\r\n            `Expected grid size to be 2^n+1, got ${gridSize}.`);\r\n\r\n        this.numTriangles = tileSize * tileSize * 2 - 2;\r\n        this.numParentTriangles = this.numTriangles - tileSize * tileSize;\r\n\r\n        this.indices = new Uint32Array(this.gridSize * this.gridSize);\r\n\r\n        // coordinates for all possible triangles in an RTIN tile\r\n        this.coords = new Uint16Array(this.numTriangles * 4);\r\n\r\n        // get triangle coordinates from its index in an implicit binary tree\r\n        for (let i = 0; i < this.numTriangles; i++) {\r\n            let id = i + 2;\r\n            let ax = 0, ay = 0, bx = 0, by = 0, cx = 0, cy = 0;\r\n            if (id & 1) {\r\n                bx = by = cx = tileSize; // bottom-left triangle\r\n            } else {\r\n                ax = ay = cy = tileSize; // top-right triangle\r\n            }\r\n            while ((id >>= 1) > 1) {\r\n                const mx = (ax + bx) >> 1;\r\n                const my = (ay + by) >> 1;\r\n\r\n                if (id & 1) { // left half\r\n                    bx = ax; by = ay;\r\n                    ax = cx; ay = cy;\r\n                } else { // right half\r\n                    ax = bx; ay = by;\r\n                    bx = cx; by = cy;\r\n                }\r\n                cx = mx; cy = my;\r\n            }\r\n            const k = i * 4;\r\n            this.coords[k + 0] = ax;\r\n            this.coords[k + 1] = ay;\r\n            this.coords[k + 2] = bx;\r\n            this.coords[k + 3] = by;\r\n        }\r\n    }\r\n\r\n    createTile(terrain) {\r\n        return new Tile(terrain, this);\r\n    }\r\n}\r\n\r\nclass Tile {\r\n    constructor(terrain, martini) {\r\n        const size = martini.gridSize;\r\n        if (terrain.length !== size * size) throw new Error(\r\n            `Expected terrain data of length ${size * size} (${size} x ${size}), got ${terrain.length}.`);\r\n\r\n        this.terrain = terrain;\r\n        this.martini = martini;\r\n        this.errors = new Float32Array(terrain.length);\r\n        this.update();\r\n    }\r\n\r\n    update() {\r\n        const {numTriangles, numParentTriangles, coords, gridSize: size} = this.martini;\r\n        const {terrain, errors} = this;\r\n\r\n        // iterate over all possible triangles, starting from the smallest level\r\n        for (let i = numTriangles - 1; i >= 0; i--) {\r\n            const k = i * 4;\r\n            const ax = coords[k + 0];\r\n            const ay = coords[k + 1];\r\n            const bx = coords[k + 2];\r\n            const by = coords[k + 3];\r\n            const mx = (ax + bx) >> 1;\r\n            const my = (ay + by) >> 1;\r\n            const cx = mx + my - ay;\r\n            const cy = my + ax - mx;\r\n\r\n            // calculate error in the middle of the long edge of the triangle\r\n            const interpolatedHeight = (terrain[ay * size + ax] + terrain[by * size + bx]) / 2;\r\n            const middleIndex = my * size + mx;\r\n            const middleError = Math.abs(interpolatedHeight - terrain[middleIndex]);\r\n\r\n            errors[middleIndex] = Math.max(errors[middleIndex], middleError);\r\n\r\n            if (i < numParentTriangles) { // bigger triangles; accumulate error with children\r\n                const leftChildIndex = ((ay + cy) >> 1) * size + ((ax + cx) >> 1);\r\n                const rightChildIndex = ((by + cy) >> 1) * size + ((bx + cx) >> 1);\r\n                errors[middleIndex] = Math.max(errors[middleIndex], errors[leftChildIndex], errors[rightChildIndex]);\r\n            }\r\n        }\r\n    }\r\n\r\n    getMesh(maxError = 0) {\r\n        const {gridSize: size, indices} = this.martini;\r\n        const {errors} = this;\r\n        let numVertices = 0;\r\n        let numTriangles = 0;\r\n        const max = size - 1;\r\n\r\n        // use an index grid to keep track of vertices that were already used to avoid duplication\r\n        indices.fill(0);\r\n\r\n        // retrieve mesh in two stages that both traverse the error map:\r\n        // - countElements: find used vertices (and assign each an index), and count triangles (for minimum allocation)\r\n        // - processTriangle: fill the allocated vertices & triangles typed arrays\r\n\r\n        function countElements(ax, ay, bx, by, cx, cy) {\r\n            const mx = (ax + bx) >> 1;\r\n            const my = (ay + by) >> 1;\r\n\r\n            if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * size + mx] > maxError) {\r\n                countElements(cx, cy, ax, ay, mx, my);\r\n                countElements(bx, by, cx, cy, mx, my);\r\n            } else {\r\n                indices[ay * size + ax] = indices[ay * size + ax] || ++numVertices;\r\n                indices[by * size + bx] = indices[by * size + bx] || ++numVertices;\r\n                indices[cy * size + cx] = indices[cy * size + cx] || ++numVertices;\r\n                numTriangles++;\r\n            }\r\n        }\r\n        countElements(0, 0, max, max, max, 0);\r\n        countElements(max, max, 0, 0, 0, max);\r\n\r\n        const vertices = new Uint16Array(numVertices * 2);\r\n        const triangles = new Uint32Array(numTriangles * 3);\r\n        let triIndex = 0;\r\n\r\n        function processTriangle(ax, ay, bx, by, cx, cy) {\r\n            const mx = (ax + bx) >> 1;\r\n            const my = (ay + by) >> 1;\r\n\r\n            if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * size + mx] > maxError) {\r\n                // triangle doesn't approximate the surface well enough; drill down further\r\n                processTriangle(cx, cy, ax, ay, mx, my);\r\n                processTriangle(bx, by, cx, cy, mx, my);\r\n\r\n            } else {\r\n                // add a triangle\r\n                const a = indices[ay * size + ax] - 1;\r\n                const b = indices[by * size + bx] - 1;\r\n                const c = indices[cy * size + cx] - 1;\r\n\r\n                vertices[2 * a] = ax;\r\n                vertices[2 * a + 1] = ay;\r\n\r\n                vertices[2 * b] = bx;\r\n                vertices[2 * b + 1] = by;\r\n\r\n                vertices[2 * c] = cx;\r\n                vertices[2 * c + 1] = cy;\r\n\r\n                triangles[triIndex++] = a;\r\n                triangles[triIndex++] = b;\r\n                triangles[triIndex++] = c;\r\n            }\r\n        }\r\n        processTriangle(0, 0, max, max, max, 0);\r\n        processTriangle(max, max, 0, 0, 0, max);\r\n\r\n        return {vertices, triangles};\r\n    }\r\n}\r\n","modules/coach-world/vendor/provenance.json":"[\r\n  {\r\n    \"file\": \"h3.mjs\",\r\n    \"url\": \"https://cdn.jsdelivr.net/npm/h3-js@4.3.0/dist/h3-js.es.js\",\r\n    \"bytes\": 482898,\r\n    \"sha256\": \"5666d069cc4d1b92226c3385418a4ad1edcf3b6df9891a663a38f8b4cf57afad\",\r\n    \"upstreamSha256\": \"f7d985df3db21937e4daf1a93d9a66c3bc17dcaf925eb51f5f647514bb2b2d85\",\r\n    \"modifications\": \"Disabled Node environment detection and removed unreachable CommonJS filesystem loader. This prevents Vite SSR from injecting createRequire(import.meta.url) into Workers; embedded asm.js memory remains unchanged.\"\r\n  },\r\n  {\r\n    \"file\": \"h3-LICENSE.txt\",\r\n    \"url\": \"https://cdn.jsdelivr.net/npm/h3-js@4.3.0/LICENSE\",\r\n    \"bytes\": 11357,\r\n    \"sha256\": \"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4\"\r\n  },\r\n  {\r\n    \"file\": \"maplibre-gl.js\",\r\n    \"url\": \"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.js\",\r\n    \"bytes\": 939269,\r\n    \"sha256\": \"07e6ca71a5b6d835a7c3803fb484f0a6e5f0dcbfa131407666edb9e9316564f9\"\r\n  },\r\n  {\r\n    \"file\": \"maplibre-gl.css\",\r\n    \"url\": \"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.css\",\r\n    \"bytes\": 69422,\r\n    \"sha256\": \"792ac997dcf6ae6f643eb4e2dee4630c85e7056526bd8fb85ffe83c67d6c41b4\"\r\n  },\r\n  {\r\n    \"file\": \"maplibre-LICENSE.txt\",\r\n    \"url\": \"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/LICENSE.txt\",\r\n    \"bytes\": 5984,\r\n    \"sha256\": \"ee5fc05a0677eaf69601d2c7db0d9ecd6cc27c3abc1d0733bc9ed34707cf8ef2\"\r\n  },\r\n  {\r\n    \"file\": \"dfo-LICENSE.txt\",\r\n    \"url\": \"https://github.com/Lattymoy/daggerfall-js-source/blob/e3054735/LICENSE\",\r\n    \"bytes\": 4297,\r\n    \"sha256\": \"f17b1e672692450137ef4a8ec11880193c5ef53e6781663d684755c287c8d0ba\"\r\n  },\r\n  {\r\n    \"file\": \"../../coach-world-rts/fog-law.mjs\",\r\n    \"url\": \"https://github.com/Lattymoy/daggerfall-js-source/tree/e3054735\",\r\n    \"commit\": \"e3054735\",\r\n    \"license\": \"dfo-LICENSE.txt (MIT DFO Online + MIT Daggerfall Unity)\",\r\n    \"sources\": [\r\n      \"src/render/horizonHaze.js (HORIZON_HAZE_GLSL horizonHaze)\",\r\n      \"src/render/fogGlsl.js (weatherFogAt exp2 mode, fog = weather x haze structure)\",\r\n      \"src/render/hazeGlsl.js (HAZE_LAW_GLSL hazeMean + hazeAt, V5)\",\r\n      \"src/world/weather.js (FOG_SETTINGS exp densities, V5; per-kind map in coach-world-rts/weather-fx.mjs WEATHER_FOG)\"\r\n    ],\r\n    \"modifications\": \"Ported into three.js fog_* ShaderChunk overrides; radial eye distance from mvPosition; exp2 ramp passed into the hold/rim law; opt-in per scene. V1 (Daggerfall look). V5: weather exp2 x height-haze transmittances from an origin (eye in flight, view focus in the RTS view), multiplied like DFO ringHaze.\"\r\n  },\r\n  {\r\n    \"file\": \"../../coach-world-rts/sky-alien.mjs\",\r\n    \"url\": \"https://github.com/Lattymoy/daggerfall-js-source/tree/e3054735\",\r\n    \"commit\": \"e3054735\",\r\n    \"license\": \"dfo-LICENSE.txt (MIT DFO Online + MIT Daggerfall Unity)\",\r\n    \"sources\": [\r\n      \"src/render/enhancedSky.js (SKY_KEYS table and paletteAt interpolation verbatim; moon() lit-sphere terminator, horizon glow, sun disc/halo formulas; moon-on-the-sun-arc-by-phase placement; MOONS radii and colours)\"\r\n    ],\r\n    \"modifications\": \"Ported into the alien sky atmosphere ShaderMaterial; palette blended 55% with the faction atmosphere; real solar position and lunar phase instead of the DFO game clock; second moon on an 11.3-day cycle. V3 (Daggerfall look).\"\r\n  },\r\n  {\r\n    \"file\": \"../../coach-world-rts/far-ring.mjs\",\r\n    \"url\": \"https://github.com/Lattymoy/daggerfall-js-source/tree/e3054735\",\r\n    \"commit\": \"e3054735\",\r\n    \"license\": \"dfo-LICENSE.txt (MIT DFO Online + MIT Daggerfall Unity)\",\r\n    \"sources\": [\r\n      \"src/render/farRing.js (buildFarRingGrid one vertex per height pixel + clamped central-difference shading, buildFarRingIndices hole punch + round rim, hazed by the horizon law)\"\r\n    ],\r\n    \"modifications\": \"Terrarium z9/z10 DEM (AWS Terrain Tiles, baked by D:/myr5-work/world-ring/bake-ring.py) instead of WOODS.WLD; world-terrain height ramp + PS1 ground material; no sea layer; real depth with a sunk, overlapping hole instead of depthRange(1,1); fog through the shared V1/V5 fog law. V6 (Daggerfall look).\"\r\n  },\r\n  {\r\n    \"file\": \"dfo-grass-pixel-art.mjs\",\r\n    \"url\": \"https://github.com/Lattymoy/daggerfall-js-source/blob/e3054735/src/render/grassPixelArt.js\",\r\n    \"commit\": \"e3054735\",\r\n    \"license\": \"dfo-LICENSE.txt (MIT DFO Online + MIT Daggerfall Unity)\",\r\n    \"bytes\": 18121,\r\n    \"sha256\": \"26c659812af46f01b35106325a7b8c73cd6e0d459c3d839c164525052c86a5f4\",\r\n    \"modifications\": \"Verbatim copy with a 2-line attribution header. Used by coach-world-rts/ground-tiles.mjs (V7 near grass tufts).\"\r\n  }\r\n]\r\n","modules/coach-world/vendor/world-tiles-vendor.mjs":"var S=Uint8Array,b=Uint16Array,Ee=Int32Array,Nt=new S([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Wt=new S([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Ve=new S([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Kt=function(e,t){for(var n=new b(31),r=0;r<31;++r)n[r]=t+=1<<e[r-1];for(var i=new Ee(n[30]),r=1;r<30;++r)for(var s=n[r];s<n[r+1];++s)i[s]=s-n[r]<<5|r;return{b:n,r:i}},_t=Kt(Nt,2),Yt=_t.b,Pe=_t.r;Yt[28]=258,Pe[258]=28;var Jt=Kt(Wt,0),Ce=Jt.b,Pn=Jt.r,pt=new b(32768);for(g=0;g<32768;++g)U=(g&43690)>>1|(g&21845)<<1,U=(U&52428)>>2|(U&13107)<<2,U=(U&61680)>>4|(U&3855)<<4,pt[g]=((U&65280)>>8|(U&255)<<8)>>1;var U,g,W=(function(e,t,n){for(var r=e.length,i=0,s=new b(t);i<r;++i)e[i]&&++s[e[i]-1];var o=new b(t);for(i=1;i<t;++i)o[i]=o[i-1]+s[i-1]<<1;var a;if(n){a=new b(1<<t);var h=15-t;for(i=0;i<r;++i)if(e[i])for(var l=i<<4|e[i],c=t-e[i],u=o[e[i]-1]++<<c,f=u|(1<<c)-1;u<=f;++u)a[pt[u]>>h]=l}else for(a=new b(r),i=0;i<r;++i)e[i]&&(a[i]=pt[o[e[i]-1]++]>>15-e[i]);return a}),K=new S(288);for(g=0;g<144;++g)K[g]=8;var g;for(g=144;g<256;++g)K[g]=9;var g;for(g=256;g<280;++g)K[g]=7;var g;for(g=280;g<288;++g)K[g]=8;var g,Xt=new S(32);for(g=0;g<32;++g)Xt[g]=5;var g;var Ue=W(K,9,1);var De=W(Xt,5,1),xt=function(e){for(var t=e[0],n=1;n<e.length;++n)e[n]>t&&(t=e[n]);return t},P=function(e,t,n){var r=t/8|0;return(e[r]|e[r+1]<<8)>>(t&7)&n},vt=function(e,t){var n=t/8|0;return(e[n]|e[n+1]<<8|e[n+2]<<16)>>(t&7)},Be=function(e){return(e+7)/8|0},ke=function(e,t,n){return(t==null||t<0)&&(t=0),(n==null||n>e.length)&&(n=e.length),new S(e.subarray(t,n))};var Le=[\"unexpected EOF\",\"invalid block type\",\"invalid length/literal\",\"invalid distance\",\"stream finished\",\"no stream handler\",,\"no callback\",\"invalid UTF-8 data\",\"extra field too long\",\"date not in range 1980-2099\",\"filename too long\",\"stream finishing\",\"invalid zip data\"],T=function(e,t,n){var r=new Error(t||Le[e]);if(r.code=e,Error.captureStackTrace&&Error.captureStackTrace(r,T),!n)throw r;return r},gt=function(e,t,n,r){var i=e.length,s=r?r.length:0;if(!i||t.f&&!t.l)return n||new S(0);var o=!n,a=o||t.i!=2,h=t.i;o&&(n=new S(i*3));var l=function($t){var Gt=n.length;if($t>Gt){var jt=new S(Math.max(Gt*2,$t));jt.set(n),n=jt}},c=t.f||0,u=t.p||0,f=t.b||0,x=t.l,v=t.d,p=t.m,V=t.n,$=i*8;do{if(!x){c=P(e,u,1);var G=P(e,u+1,3);if(u+=3,G)if(G==1)x=Ue,v=De,p=9,V=5;else if(G==2){var ht=P(e,u,31)+257,kt=P(e,u+10,15)+4,Lt=ht+P(e,u+5,31)+1;u+=14;for(var j=new S(Lt),lt=new S(19),A=0;A<kt;++A)lt[Ve[A]]=P(e,u+A*3,7);u+=kt*3;for(var It=xt(lt),Me=(1<<It)-1,Te=W(lt,It,1),A=0;A<Lt;){var Zt=Te[P(e,u,Me)];u+=Zt&15;var y=Zt>>4;if(y<16)j[A++]=y;else{var I=0,tt=0;for(y==16?(tt=3+P(e,u,3),u+=2,I=j[A-1]):y==17?(tt=3+P(e,u,7),u+=3):y==18&&(tt=11+P(e,u,127),u+=7);tt--;)j[A++]=I}}var bt=j.subarray(0,ht),D=j.subarray(ht);p=xt(bt),V=xt(D),x=W(bt,p,1),v=W(D,V,1)}else T(1);else{var y=Be(u)+4,m=e[y-4]|e[y-3]<<8,at=y+m;if(at>i){h&&T(0);break}a&&l(f+m),n.set(e.subarray(y,at),f),t.b=f+=m,t.p=u=at*8,t.f=c;continue}if(u>$){h&&T(0);break}}a&&l(f+131072);for(var Se=(1<<p)-1,ze=(1<<V)-1,ut=u;;ut=u){var I=x[vt(e,u)&Se],Z=I>>4;if(u+=I&15,u>$){h&&T(0);break}if(I||T(2),Z<256)n[f++]=Z;else if(Z==256){ut=u,x=null;break}else{var Ht=Z-254;if(Z>264){var A=Z-257,N=Nt[A];Ht=P(e,u,(1<<N)-1)+Yt[A],u+=N}var ft=v[vt(e,u)&ze],ct=ft>>4;ft||T(3),u+=ft&15;var D=Ce[ct];if(ct>3){var N=Wt[ct];D+=vt(e,u)&(1<<N)-1,u+=N}if(u>$){h&&T(0);break}a&&l(f+131072);var Ot=f+Ht;if(f<D){var Rt=s-D,Ae=Math.min(D,Ot);for(Rt+f<0&&T(3);f<Ae;++f)n[f]=r[Rt+f]}for(;f<Ot;++f)n[f]=n[f-D]}}t.l=x,t.p=ut,t.b=f,t.f=c,x&&(c=1,t.m=p,t.d=v,t.n=V)}while(!c);return f!=n.length&&o?ke(n,0,f):n.subarray(0,f)};var Ie=new S(0);var Ze=function(e){(e[0]!=31||e[1]!=139||e[2]!=8)&&T(6,\"invalid gzip data\");var t=e[3],n=10;t&4&&(n+=(e[10]|e[11]<<8)+2);for(var r=(t>>3&1)+(t>>4&1);r>0;r-=!e[n++]);return n+(t&2)},be=function(e){var t=e.length;return(e[t-4]|e[t-3]<<8|e[t-2]<<16|e[t-1]<<24)>>>0};var He=function(e,t){return((e[0]&15)!=8||e[0]>>4>7||(e[0]<<8|e[1])%31)&&T(6,\"invalid zlib data\"),(e[1]>>5&1)==+!t&&T(6,\"invalid zlib data: \"+(e[1]&32?\"need\":\"unexpected\")+\" dictionary\"),(e[1]>>3&4)+2};function Oe(e,t){return gt(e,{i:2},t&&t.out,t&&t.dictionary)}function Re(e,t){var n=Ze(e);return n+8>e.length&&T(6,\"invalid gzip data\"),gt(e.subarray(n,-8),{i:2},t&&t.out||new S(be(e)),t&&t.dictionary)}function $e(e,t){return gt(e.subarray(He(e,t&&t.dictionary),-4),{i:2},t&&t.out,t&&t.dictionary)}function qt(e,t){return e[0]==31&&e[1]==139&&e[2]==8?Re(e,t):(e[0]&15)!=8||e[0]>>4>7||(e[0]<<8|e[1])%31?Oe(e,t):$e(e,t)}var Ge=typeof TextDecoder<\"u\"&&new TextDecoder,je=0;try{Ge.decode(Ie,{stream:!0}),je=1}catch{}var Ne=Object.defineProperty,We=Math.pow,d=(e,t)=>Ne(e,\"name\",{value:t,configurable:!0}),M=(e,t,n)=>new Promise((r,i)=>{var s=h=>{try{a(n.next(h))}catch(l){i(l)}},o=h=>{try{a(n.throw(h))}catch(l){i(l)}},a=h=>h.done?r(h.value):Promise.resolve(h.value).then(s,o);a((n=n.apply(e,t)).next())}),Dn=d((e,t)=>{let n=!1,r=\"\",i=L.GridLayer.extend({createTile:d((s,o)=>{let a=document.createElement(\"img\"),h=new AbortController,l=h.signal;return a.cancel=()=>{h.abort()},n||(e.getHeader().then(c=>{c.tileType===1||c.tileType===6?console.error(\"Error: archive contains vector tiles, but leafletRasterLayer is for displaying raster tiles. See https://github.com/protomaps/PMTiles/tree/main/js for details.\"):c.tileType===2?r=\"image/png\":c.tileType===3?r=\"image/jpeg\":c.tileType===4?r=\"image/webp\":c.tileType===5&&(r=\"image/avif\")}),n=!0),e.getZxy(s.z,s.x,s.y,l).then(c=>{if(c){let u=new Blob([c.data],{type:r}),f=window.URL.createObjectURL(u);a.src=f}else a.style.display=\"none\";a.cancel=void 0,o(void 0,a)}).catch(c=>{if(c.name!==\"AbortError\")throw c}),a},\"createTile\"),_removeTile:d(function(s){let o=this._tiles[s];o&&(o.el.cancel&&o.el.cancel(),o.el.src&&window.URL.revokeObjectURL(o.el.src),o.el.width=0,o.el.height=0,o.el.deleted=!0,L.DomUtil.remove(o.el),delete this._tiles[s],this.fire(\"tileunload\",{tile:o.el,coords:this._keyToTileCoords(s)}))},\"_removeTile\")});return new i(t)},\"leafletRasterLayer\"),Ke=d(e=>(t,n)=>{if(n instanceof AbortController)return e(t,n);let r=new AbortController;return e(t,r).then(i=>n(void 0,i.data,i.cacheControl||\"\",i.expires||\"\"),i=>n(i)).catch(i=>n(i)),{cancel:d(()=>r.abort(),\"cancel\")}},\"v3compat\"),_e=class{constructor(t){this.tilev4=d((n,r)=>M(this,null,function*(){if(n.type===\"json\"){let x=n.url.substr(10),v=this.tiles.get(x);if(v||(v=new yt(x),this.tiles.set(x,v)),this.metadata){let V=yield v.getTileJson(n.url);return r.signal.throwIfAborted(),{data:V}}let p=yield v.getHeader();return r.signal.throwIfAborted(),(p.minLon>=p.maxLon||p.minLat>=p.maxLat)&&console.error(`Bounds of PMTiles archive ${p.minLon},${p.minLat},${p.maxLon},${p.maxLat} are not valid.`),{data:{tiles:[`${n.url}/{z}/{x}/{y}`],minzoom:p.minZoom,maxzoom:p.maxZoom,bounds:[p.minLon,p.minLat,p.maxLon,p.maxLat]}}}let i=new RegExp(/pmtiles:\\/\\/(.+)\\/(\\d+)\\/(\\d+)\\/(\\d+)/),s=n.url.match(i);if(!s)throw new Error(\"Invalid PMTiles protocol URL\");let o=s[1],a=this.tiles.get(o);a||(a=new yt(o),this.tiles.set(o,a));let h=s[2],l=s[3],c=s[4],u=yield a?.getZxy(+h,+l,+c,r.signal);if(r.signal.throwIfAborted(),u)return{data:new Uint8Array(u.data),cacheControl:u.cacheControl,expires:u.expires};let f=yield a.getHeader();if(f.tileType===1||f.tileType===6){if(this.errorOnMissingTile)throw new Error(\"Tile not found.\");return{data:new Uint8Array}}return{data:null}}),\"tilev4\"),this.tile=Ke(this.tilev4),this.tiles=new Map,this.metadata=t?.metadata||!1,this.errorOnMissingTile=t?.errorOnMissingTile||!1}add(t){this.tiles.set(t.source.getKey(),t)}get(t){return this.tiles.get(t)}};d(_e,\"Protocol\");function Qt(e,t){return(t>>>0)*4294967296+(e>>>0)}d(Qt,\"toNum\");function te(e,t){let n=t.buf,r=n[t.pos++],i=(r&112)>>4;if(r<128||(r=n[t.pos++],i|=(r&127)<<3,r<128)||(r=n[t.pos++],i|=(r&127)<<10,r<128)||(r=n[t.pos++],i|=(r&127)<<17,r<128)||(r=n[t.pos++],i|=(r&127)<<24,r<128)||(r=n[t.pos++],i|=(r&1)<<31,r<128))return Qt(e,i);throw new Error(\"Expected varint not more than 10 bytes\")}d(te,\"readVarintRemainder\");function H(e){let t=e.buf,n=t[e.pos++],r=n&127;return n<128||(n=t[e.pos++],r|=(n&127)<<7,n<128)||(n=t[e.pos++],r|=(n&127)<<14,n<128)||(n=t[e.pos++],r|=(n&127)<<21,n<128)?r:(n=t[e.pos],r|=(n&15)<<28,te(r,e))}d(H,\"readVarint\");function wt(e,t,n,r,i){return i===0?r!==0?[e-1-n,e-1-t]:[n,t]:[t,n]}d(wt,\"rotate\");function mt(e,t,n){if(e>26)throw new Error(\"Tile zoom level exceeds max safe number limit (26)\");if(t>=1<<e||n>=1<<e)throw new Error(\"tile x/y outside zoom level bounds\");let r=((1<<e)*(1<<e)-1)/3,i=e-1,[s,o]=[t,n];for(let a=1<<i;a>0;a>>=1){let h=s&a,l=o&a;r+=(3*h^l)*(1<<i),[s,o]=wt(a,s,o,h,l),i--}return r}d(mt,\"zxyToTileId\");function ee(e){let t=3*e+1;return t<4294967296?31-Math.clz32(t):63-Math.clz32(t/4294967296)}d(ee,\"tileIdToZ\");function ne(e){let t=ee(e)>>1;if(t>26)throw new Error(\"Tile zoom level exceeds max safe number limit (26)\");let n=((1<<t)*(1<<t)-1)/3,r=e-n,i=0,s=0,o=1<<t;for(let a=1;a<o;a<<=1){let h=a&r/2,l=a&(r^h);[i,s]=wt(a,i,s,h,l),r=r/2,i+=h,s+=l}return[t,i,s]}d(ne,\"tileIdToZxy\");var Ye=(e=>(e[e.Unknown=0]=\"Unknown\",e[e.None=1]=\"None\",e[e.Gzip=2]=\"Gzip\",e[e.Brotli=3]=\"Brotli\",e[e.Zstd=4]=\"Zstd\",e))(Ye||{});function et(e,t){return M(this,null,function*(){if(t===1||t===0)return e;if(t===2){if(typeof globalThis.DecompressionStream>\"u\")return qt(new Uint8Array(e));let n=new Response(e).body;if(!n)throw new Error(\"Failed to read response stream\");let r=n.pipeThrough(new globalThis.DecompressionStream(\"gzip\"));return new Response(r).arrayBuffer()}throw new Error(\"Compression method not supported\")})}d(et,\"defaultDecompress\");var Je=(e=>(e[e.Unknown=0]=\"Unknown\",e[e.Mvt=1]=\"Mvt\",e[e.Png=2]=\"Png\",e[e.Jpeg=3]=\"Jpeg\",e[e.Webp=4]=\"Webp\",e[e.Avif=5]=\"Avif\",e[e.Mlt=6]=\"Mlt\",e))(Je||{});function re(e){return e===1?\".mvt\":e===2?\".png\":e===3?\".jpg\":e===4?\".webp\":e===5?\".avif\":e===6?\".mlt\":\"\"}d(re,\"tileTypeExt\");var Xe=127;function ie(e,t){let n=0,r=e.length-1;for(;n<=r;){let i=r+n>>1,s=t-e[i].tileId;if(s>0)n=i+1;else if(s<0)r=i-1;else return e[i]}return r>=0&&(e[r].runLength===0||t-e[r].tileId<e[r].runLength)?e[r]:null}d(ie,\"findTile\");var qe=class{constructor(t){this.file=t}getKey(){return this.file.name}getBytes(t,n){return M(this,null,function*(){return{data:yield this.file.slice(t,t+n).arrayBuffer()}})}};d(qe,\"FileSource\");var se=class{constructor(t,n=new Headers,r=void 0){var i,s;this.url=t,this.customHeaders=n,this.credentials=r,this.mustReload=!1;let o=\"\";\"navigator\"in globalThis&&(o=(s=(i=globalThis.navigator)==null?void 0:i.userAgent)!=null?s:\"\");let a=o.indexOf(\"Windows\")>-1,h=/Chrome|Chromium|Edg|OPR|Brave/.test(o);this.chromeWindowsNoCache=!1,a&&h&&(this.chromeWindowsNoCache=!0)}getKey(){return this.url}setHeaders(t){this.customHeaders=t}getBytes(t,n,r,i){return M(this,null,function*(){let s,o;r?o=r:(s=new AbortController,o=s.signal);let a=new Headers(this.customHeaders);a.set(\"range\",`bytes=${t}-${t+n-1}`);let h;this.mustReload?h=\"reload\":this.chromeWindowsNoCache&&(h=\"no-store\");let l=yield fetch(this.url,{signal:o,cache:h,headers:a,credentials:this.credentials});if(t===0&&l.status===416){let f=l.headers.get(\"Content-Range\");if(!f||!f.startsWith(\"bytes */\"))throw new Error(\"Missing content-length on 416 response\");let x=+f.substr(8);a.set(\"range\",`bytes=0-${x-1}`),l=yield fetch(this.url,{signal:o,cache:\"reload\",headers:a,credentials:this.credentials})}let c=l.headers.get(\"Etag\");if(c!=null&&c.startsWith(\"W/\")&&(c=null),l.status===416||i&&c&&c!==i)throw this.mustReload=!0,new dt(`Server returned non-matching ETag ${i} after one retry. Check browser extensions and servers for issues that may affect correct ETag headers.`);if(l.status>=300)throw new Error(`Bad response code: ${l.status}`);let u=l.headers.get(\"Content-Length\");if(l.status===200&&(!u||+u>n))throw s&&s.abort(),new Error(\"Server returned no content-length header or content-length exceeding request. Check that your storage backend supports HTTP Byte Serving.\");return{data:yield l.arrayBuffer(),etag:c||void 0,cacheControl:l.headers.get(\"Cache-Control\")||void 0,expires:l.headers.get(\"Expires\")||void 0}})}};d(se,\"FetchSource\");var oe=se;function E(e,t){let n=e.getUint32(t+4,!0),r=e.getUint32(t+0,!0);return n*We(2,32)+r}d(E,\"getUint64\");function ae(e,t){let n=new DataView(e),r=n.getUint8(7);if(r>3)throw new Error(`Archive is spec version ${r} but this library supports up to spec version 3`);return{specVersion:r,rootDirectoryOffset:E(n,8),rootDirectoryLength:E(n,16),jsonMetadataOffset:E(n,24),jsonMetadataLength:E(n,32),leafDirectoryOffset:E(n,40),leafDirectoryLength:E(n,48),tileDataOffset:E(n,56),tileDataLength:E(n,64),numAddressedTiles:E(n,72),numTileEntries:E(n,80),numTileContents:E(n,88),clustered:n.getUint8(96)===1,internalCompression:n.getUint8(97),tileCompression:n.getUint8(98),tileType:n.getUint8(99),minZoom:n.getUint8(100),maxZoom:n.getUint8(101),minLon:n.getInt32(102,!0)/1e7,minLat:n.getInt32(106,!0)/1e7,maxLon:n.getInt32(110,!0)/1e7,maxLat:n.getInt32(114,!0)/1e7,centerZoom:n.getUint8(118),centerLon:n.getInt32(119,!0)/1e7,centerLat:n.getInt32(123,!0)/1e7,etag:t}}d(ae,\"bytesToHeader\");function Ft(e){let t={buf:new Uint8Array(e),pos:0},n=H(t),r=[],i=0;for(let s=0;s<n;s++){let o=H(t);r.push({tileId:i+o,offset:0,length:0,runLength:1}),i+=o}for(let s=0;s<n;s++)r[s].runLength=H(t);for(let s=0;s<n;s++)r[s].length=H(t);for(let s=0;s<n;s++){let o=H(t);o===0&&s>0?r[s].offset=r[s-1].offset+r[s-1].length:r[s].offset=o-1}return r}d(Ft,\"deserializeIndex\");var he=class extends Error{};d(he,\"EtagMismatch\");var dt=he;function Mt(e,t){return M(this,null,function*(){let n=yield e.getBytes(0,16384);if(new DataView(n.data).getUint16(0,!0)!==19792)throw new Error(\"Wrong magic number for PMTiles archive\");let r=n.data.slice(0,Xe),i=ae(r,n.etag),s=n.data.slice(i.rootDirectoryOffset,i.rootDirectoryOffset+i.rootDirectoryLength),o=`${e.getKey()}|${i.etag||\"\"}|${i.rootDirectoryOffset}|${i.rootDirectoryLength}`,a=Ft(yield t(s,i.internalCompression));return[i,[o,a.length,a]]})}d(Mt,\"getHeaderAndRoot\");function Tt(e,t,n,r,i,s){return M(this,null,function*(){let o=yield e.getBytes(n,r,s,i.etag),a=yield t(o.data,i.internalCompression),h=Ft(a);if(h.length===0)throw new Error(\"Empty directory is invalid\");return h})}d(Tt,\"getDirectory\");var Qe=class{constructor(t=100,n=!0,r=et){this.cache=new Map,this.maxCacheEntries=t,this.counter=1,this.decompress=r}getHeader(t){return M(this,null,function*(){let n=t.getKey(),r=this.cache.get(n);if(r)return r.lastUsed=this.counter++,r.data;let i=yield Mt(t,this.decompress);return i[1]&&this.cache.set(i[1][0],{lastUsed:this.counter++,data:i[1][2]}),this.cache.set(n,{lastUsed:this.counter++,data:i[0]}),this.prune(),i[0]})}getDirectory(t,n,r,i,s){return M(this,null,function*(){let o=`${t.getKey()}|${i.etag||\"\"}|${n}|${r}`,a=this.cache.get(o);if(a)return a.lastUsed=this.counter++,a.data;let h=yield Tt(t,this.decompress,n,r,i,s);return this.cache.set(o,{lastUsed:this.counter++,data:h}),this.prune(),h})}prune(){if(this.cache.size>this.maxCacheEntries){let t=1/0,n;this.cache.forEach((r,i)=>{r.lastUsed<t&&(t=r.lastUsed,n=i)}),n&&this.cache.delete(n)}}invalidate(t){return M(this,null,function*(){this.cache.delete(t.getKey())})}};d(Qe,\"ResolvedValueCache\");var le=class{constructor(t=100,n=!0,r=et){this.cache=new Map,this.invalidations=new Map,this.pendingFetches=new Map,this.maxCacheEntries=t,this.counter=1,this.decompress=r}getHeader(t){return M(this,null,function*(){let n=t.getKey(),r=this.cache.get(n);if(r)return r.lastUsed=this.counter++,yield r.data;let i=new Promise((s,o)=>{Mt(t,this.decompress).then(a=>{a[1]&&this.cache.set(a[1][0],{lastUsed:this.counter++,data:Promise.resolve(a[1][2])}),s(a[0]),this.prune()}).catch(a=>{o(a)})});return this.cache.set(n,{lastUsed:this.counter++,data:i}),i})}trackSignal(t,n,r){n.refs++,r.addEventListener(\"abort\",()=>{--n.refs<=0&&this.pendingFetches.get(t)===n&&(n.controller.abort(),this.cache.delete(t),this.pendingFetches.delete(t))},{once:!0})}getDirectory(t,n,r,i,s){return M(this,null,function*(){let o=`${t.getKey()}|${i.etag||\"\"}|${n}|${r}`,a=this.cache.get(o);if(a){a.lastUsed=this.counter++;let u=this.pendingFetches.get(o);return u&&this.trackSignal(o,u,s??new AbortController().signal),yield a.data}let h=new AbortController,l={controller:h,refs:0};this.trackSignal(o,l,s??new AbortController().signal),this.pendingFetches.set(o,l);let c=new Promise((u,f)=>{Tt(t,this.decompress,n,r,i,h.signal).then(x=>{this.pendingFetches.delete(o),u(x),this.prune()}).catch(x=>{f(x)})});return this.cache.set(o,{lastUsed:this.counter++,data:c}),c})}prune(){if(this.cache.size>=this.maxCacheEntries){let t=1/0,n;this.cache.forEach((r,i)=>{r.lastUsed<t&&(t=r.lastUsed,n=i)}),n&&this.cache.delete(n)}}invalidate(t){return M(this,null,function*(){let n=t.getKey();if(this.invalidations.get(n))return yield this.invalidations.get(n);this.cache.delete(t.getKey());let r=new Promise((i,s)=>{this.getHeader(t).then(o=>{i(),this.invalidations.delete(n)}).catch(o=>{s(o)})});this.invalidations.set(n,r)})}};d(le,\"SharedPromiseCache\");var tn=le,ue=class{constructor(t,n,r){typeof t==\"string\"?this.source=new oe(t):this.source=t,r?this.decompress=r:this.decompress=et,n?this.cache=n:this.cache=new tn}getHeader(){return M(this,null,function*(){return yield this.cache.getHeader(this.source)})}getZxyAttempt(t,n,r,i){return M(this,null,function*(){let s=mt(t,n,r),o=yield this.cache.getHeader(this.source);if(i?.throwIfAborted(),t<o.minZoom||t>o.maxZoom)return;let a=o.rootDirectoryOffset,h=o.rootDirectoryLength;for(let l=0;l<=3;l++){let c=yield this.cache.getDirectory(this.source,a,h,o,i);i?.throwIfAborted();let u=ie(c,s);if(u){if(u.runLength>0){let f=yield this.source.getBytes(o.tileDataOffset+u.offset,u.length,i,o.etag);return{data:yield this.decompress(f.data,o.tileCompression),cacheControl:f.cacheControl,expires:f.expires}}a=o.leafDirectoryOffset+u.offset,h=u.length}else return}throw new Error(\"Maximum directory depth exceeded\")})}getZxy(t,n,r,i){return M(this,null,function*(){try{return yield this.getZxyAttempt(t,n,r,i)}catch(s){if(s instanceof dt)return this.cache.invalidate(this.source),yield this.getZxyAttempt(t,n,r,i);throw s}})}getMetadataAttempt(){return M(this,null,function*(){let t=yield this.cache.getHeader(this.source),n=yield this.source.getBytes(t.jsonMetadataOffset,t.jsonMetadataLength,void 0,t.etag),r=yield this.decompress(n.data,t.internalCompression),i=new TextDecoder(\"utf-8\");return JSON.parse(i.decode(r))})}getMetadata(){return M(this,null,function*(){try{return yield this.getMetadataAttempt()}catch(t){if(t instanceof dt)return this.cache.invalidate(this.source),yield this.getMetadataAttempt();throw t}})}getTileJson(t){return M(this,null,function*(){let n=yield this.getHeader(),r=yield this.getMetadata(),i=re(n.tileType);return{tilejson:\"3.0.0\",scheme:\"xyz\",tiles:[`${t}/{z}/{x}/{y}${i}`],vector_layers:r.vector_layers,attribution:r.attribution,description:r.description,name:r.name,version:r.version,bounds:[n.minLon,n.minLat,n.maxLon,n.maxLat],center:[n.centerLon,n.centerLat,n.centerZoom],minzoom:n.minZoom,maxzoom:n.maxZoom}})}};d(ue,\"PMTiles\");var yt=ue;function C(e,t){this.x=e,this.y=t}C.prototype={clone(){return new C(this.x,this.y)},add(e){return this.clone()._add(e)},sub(e){return this.clone()._sub(e)},multByPoint(e){return this.clone()._multByPoint(e)},divByPoint(e){return this.clone()._divByPoint(e)},mult(e){return this.clone()._mult(e)},div(e){return this.clone()._div(e)},rotate(e){return this.clone()._rotate(e)},rotateAround(e,t){return this.clone()._rotateAround(e,t)},matMult(e){return this.clone()._matMult(e)},unit(){return this.clone()._unit()},perp(){return this.clone()._perp()},round(){return this.clone()._round()},mag(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals(e){return this.x===e.x&&this.y===e.y},dist(e){return Math.sqrt(this.distSqr(e))},distSqr(e){let t=e.x-this.x,n=e.y-this.y;return t*t+n*n},angle(){return Math.atan2(this.y,this.x)},angleTo(e){return Math.atan2(this.y-e.y,this.x-e.x)},angleWith(e){return this.angleWithSep(e.x,e.y)},angleWithSep(e,t){return Math.atan2(this.x*t-this.y*e,this.x*e+this.y*t)},_matMult(e){let t=e[0]*this.x+e[1]*this.y,n=e[2]*this.x+e[3]*this.y;return this.x=t,this.y=n,this},_add(e){return this.x+=e.x,this.y+=e.y,this},_sub(e){return this.x-=e.x,this.y-=e.y,this},_mult(e){return this.x*=e,this.y*=e,this},_div(e){return this.x/=e,this.y/=e,this},_multByPoint(e){return this.x*=e.x,this.y*=e.y,this},_divByPoint(e){return this.x/=e.x,this.y/=e.y,this},_unit(){return this._div(this.mag()),this},_perp(){let e=this.y;return this.y=this.x,this.x=-e,this},_rotate(e){let t=Math.cos(e),n=Math.sin(e),r=t*this.x-n*this.y,i=n*this.x+t*this.y;return this.x=r,this.y=i,this},_rotateAround(e,t){let n=Math.cos(e),r=Math.sin(e),i=t.x+n*(this.x-t.x)-r*(this.y-t.y),s=t.y+r*(this.x-t.x)+n*(this.y-t.y);return this.x=i,this.y=s,this},_round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},constructor:C};C.convert=function(e){if(e instanceof C)return e;if(Array.isArray(e))return new C(+e[0],+e[1]);if(e.x!==void 0&&e.y!==void 0)return new C(+e.x,+e.y);throw new Error(\"Expected [x, y] or {x, y} point format\")};var nt=class{constructor(t,n,r,i,s){for(this.properties=Object.create(null),this.extent=r,this.type=0,this.id=void 0,this._pbf=t,this._geometry=-1,this._keys=i,this._values=s;t.pos<n;){let o=t.readVarint();if(o===8)this.id=t.readVarint();else if(o===18){let a=t.readVarint()+t.pos;for(;t.pos<a;){let h=i[t.readVarint()],l=s[t.readVarint()];this.properties[h]=l}}else o===24?this.type=t.readVarint():(o===34&&(this._geometry=t.pos),t.skip(o))}}loadGeometry(){if(this._geometry<0)throw new Error(\"feature has no geometry\");let t=this._pbf;t.pos=this._geometry;let n=t.readVarint()+t.pos,r=[],i,s=1,o=0,a=0,h=0;for(;t.pos<n;){if(o<=0){let l=t.readVarint();if(s=l&7,o=l>>3,o===0)continue}if(o--,s===1)a+=t.readSVarint(),h+=t.readSVarint(),i&&r.push(i),i=[new C(a,h)];else if(s===2)a+=t.readSVarint(),h+=t.readSVarint(),i&&i.push(new C(a,h));else if(s===7)i&&i.push(i[0].clone());else throw new Error(`unknown command ${s}`)}return i&&r.push(i),r}bbox(){if(this._geometry<0)throw new Error(\"feature has no geometry\");let t=this._pbf;t.pos=this._geometry;let n=t.readVarint()+t.pos,r=1,i=0,s=0,o=0,a=1/0,h=-1/0,l=1/0,c=-1/0;for(;t.pos<n;){if(i<=0){let u=t.readVarint();if(r=u&7,i=u>>3,i===0)continue}if(i--,r===1||r===2)s+=t.readSVarint(),o+=t.readSVarint(),s<a&&(a=s),s>h&&(h=s),o<l&&(l=o),o>c&&(c=o);else if(r!==7)throw new Error(`unknown command ${r}`)}return[a,l,h,c]}toGeoJSON(t,n,r){let i=this.extent*Math.pow(2,r),s=this.extent*t,o=this.extent*n,a=this.loadGeometry();function h(f){return[(f.x+s)*360/i-180,360/Math.PI*Math.atan(Math.exp((1-(f.y+o)*2/i)*Math.PI))-90]}function l(f){return f.map(h)}let c;if(this.type===1){let f=[];for(let v of a)f.push(v[0]);let x=l(f);c=f.length===1?{type:\"Point\",coordinates:x[0]}:{type:\"MultiPoint\",coordinates:x}}else if(this.type===2){let f=a.map(l);c=f.length===1?{type:\"LineString\",coordinates:f[0]}:{type:\"MultiLineString\",coordinates:f}}else if(this.type===3){let f=en(a),x=[];for(let v of f)x.push(v.map(l));c=x.length===1?{type:\"Polygon\",coordinates:x[0]}:{type:\"MultiPolygon\",coordinates:x}}else throw new Error(\"unknown feature type\");let u={type:\"Feature\",geometry:c,properties:this.properties};return this.id!=null&&(u.id=this.id),u}};nt.types=[\"Unknown\",\"Point\",\"LineString\",\"Polygon\"];function en(e){let t=e.length;if(t<=1)return[e];let n=[],r,i;for(let s=0;s<t;s++){let o=nn(e[s]);o!==0&&(i===void 0&&(i=o<0),i===o<0?(r&&n.push(r),r=[e[s]]):r&&r.push(e[s]))}return r&&n.push(r),n}function nn(e){let t=0;for(let n=0,r=e.length,i=r-1,s,o;n<r;i=n++)s=e[n],o=e[i],t+=(o.x-s.x)*(s.y+o.y);return t}var St=class{constructor(t,n){for(this.version=1,this.name=\"\",this.extent=4096,this.length=0,this._pbf=t,this._keys=[],this._values=[],this._features=[],n===void 0&&(n=t.length);t.pos<n;){let r=t.readVarint();r===10?this.name=t.readString():r===18?(this._features.push(t.pos),t.skip(r)):r===26?this._keys.push(t.readString()):r===34?this._values.push(rn(t)):r===40?this.extent=t.readVarint():r===120?this.version=t.readVarint():t.skip(r)}this.length=this._features.length}feature(t){if(t<0||t>=this._features.length)throw new Error(\"feature index out of bounds\");this._pbf.pos=this._features[t];let n=this._pbf.readVarint()+this._pbf.pos;return new nt(this._pbf,n,this.extent,this._keys,this._values)}};function rn(e){let t=null,n=e.readVarint()+e.pos;for(;e.pos<n;){let r=e.readVarint();t=r===10?e.readString():r===21?e.readFloat():r===25?e.readDouble():r===32?e.readVarint(!0):r===40?e.readVarint():r===48?e.readSVarint():r===56?e.readBoolean():(e.skip(r),null)}if(t==null)throw new Error(\"unknown feature value\");return t}var zt=class{constructor(t,n=t.length){let r=Object.create(null);for(;t.pos<n;){let i=t.readVarint();if(i===26){let s=new St(t,t.readVarint()+t.pos);s.length&&(r[s.name]=s)}else t.skip(i)}this.layers=r}};var sn=12,fe=typeof TextDecoder>\"u\"?null:new TextDecoder(\"utf-8\"),on=0,an=1,ce=2,hn=5,At=class{constructor(t){this.buf=ArrayBuffer.isView(t)?t:new Uint8Array(t),this.dataView=new DataView(this.buf.buffer,this.buf.byteOffset,this.buf.byteLength),this.pos=0,this.type=0,this._valueStart=-1,this.length=this.buf.length}readFields(t,n,r=this.length){let i;for(;i=this.nextField(r);)t(i,n,this);return n}readMessage(t,n){return this.readFields(t,n,this.readVarint()+this.pos)}readFixed32(){let t=this.dataView.getUint32(this.pos,!0);return this.pos+=4,t}readSFixed32(){let t=this.dataView.getInt32(this.pos,!0);return this.pos+=4,t}readFixed64(){let t=this.dataView.getUint32(this.pos,!0)+this.dataView.getUint32(this.pos+4,!0)*4294967296;return this.pos+=8,t}readSFixed64(){let t=this.dataView.getUint32(this.pos,!0)+this.dataView.getInt32(this.pos+4,!0)*4294967296;return this.pos+=8,t}readFloat(){let t=this.dataView.getFloat32(this.pos,!0);return this.pos+=4,t}readDouble(){let t=this.dataView.getFloat64(this.pos,!0);return this.pos+=8,t}readVarint(t){let n=this.buf,r=n[this.pos++];if(r<128)return r;let i=r&127,s;return s=n[this.pos++],i|=(s&127)<<7,s<128||(s=n[this.pos++],i|=(s&127)<<14,s<128)||(s=n[this.pos++],i|=(s&127)<<21,s<128)?i:(s=n[this.pos],i|=(s&15)<<28,ln(i,t,this))}readSVarint(){let t=this.readVarint();return t%2===1?(t+1)/-2:t/2}readBoolean(){return!!this.readVarint()}readString(){let t=this.readVarint()+this.pos,n=this.pos;return this.pos=t,t-n>=sn&&fe?fe.decode(this.buf.subarray(n,t)):un(this.buf,n,t)}readBytes(){let t=this.readVarint()+this.pos,n=this.buf.subarray(this.pos,t);return this.pos=t,n}readPackedVarint(t=[],n){let r=this.readPackedEnd();for(;this.pos<r;)t.push(this.readVarint(n));return t}readPackedSVarint(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readSVarint());return t}readPackedBoolean(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readBoolean());return t}readPackedFloat(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readFloat());return t}readPackedDouble(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readDouble());return t}readPackedFixed32(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readFixed32());return t}readPackedSFixed32(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readSFixed32());return t}readPackedFixed64(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readFixed64());return t}readPackedSFixed64(t=[]){let n=this.readPackedEnd();for(;this.pos<n;)t.push(this.readSFixed64());return t}readPackedEnd(){return this.type===ce?this.readVarint()+this.pos:this.pos+1}nextField(t=this.length){if(this.pos===this._valueStart&&this.skip(this.type),this.pos>=t)return 0;let n=this.readVarint();return this.type=n&7,this._valueStart=this.pos,n>>>3}skip(t){let n=t&7;if(n===on)for(;this.buf[this.pos++]>127;);else if(n===ce)this.pos=this.readVarint()+this.pos;else if(n===hn)this.pos+=4;else if(n===an)this.pos+=8;else throw new Error(`Unimplemented type: ${n}`)}};function ln(e,t,n){let r=n.buf,i,s;if(s=r[n.pos++],i=(s&112)>>4,s<128||(s=r[n.pos++],i|=(s&127)<<3,s<128)||(s=r[n.pos++],i|=(s&127)<<10,s<128)||(s=r[n.pos++],i|=(s&127)<<17,s<128)||(s=r[n.pos++],i|=(s&127)<<24,s<128)||(s=r[n.pos++],i|=(s&1)<<31,s<128))return O(e,i,t);throw new Error(\"Expected varint not more than 10 bytes\")}function O(e,t,n){return n?t*4294967296+(e>>>0):(t>>>0)*4294967296+(e>>>0)}function un(e,t,n){let r=\"\",i=t;for(;i<n;){let s=e[i],o=null,a=s>239?4:s>223?3:s>191?2:1;if(i+a>n)break;let h,l,c;a===1?s<128&&(o=s):a===2?(h=e[i+1],(h&192)===128&&(o=(s&31)<<6|h&63,o<=127&&(o=null))):a===3?(h=e[i+1],l=e[i+2],(h&192)===128&&(l&192)===128&&(o=(s&15)<<12|(h&63)<<6|l&63,(o<=2047||o>=55296&&o<=57343)&&(o=null))):a===4&&(h=e[i+1],l=e[i+2],c=e[i+3],(h&192)===128&&(l&192)===128&&(c&192)===128&&(o=(s&15)<<18|(h&63)<<12|(l&63)<<6|c&63,(o<=65535||o>=1114112)&&(o=null))),o===null?(o=65533,a=1):o>65535&&(o-=65536,r+=String.fromCharCode(o>>>10&1023|55296),o=56320|o&1023),r+=String.fromCharCode(o),i+=a}return r}var J=new Set,Et=!1;function ge(e,t,n=2){let r=t&&t.length,i=r?t[0]*n:e.length;J.size&&J.clear();let s=de(e,0,i,n,!0),o=[];if(!s||s.next===s.prev)return o;let a=0,h=0,l=0;if(r&&(s=pn(e,t,s,n)),e.length>80*n){a=e[0],h=e[1];let c=a,u=h;for(let f=n;f<i;f+=n){let x=e[f],v=e[f+1];x<a&&(a=x),v<h&&(h=v),x>c&&(c=x),v>u&&(u=v)}l=Math.max(c-a,u-h),l=l!==0?32767/l:0}return Vt(s,o,a,h,l),o}function de(e,t,n,r,i){let s=null;if(i===Vn(e,t,n,r)>0)for(let o=t;o<n;o+=r)s=pe(o/r|0,e[o],e[o+1],s);else for(let o=n-r;o>=t;o-=r)s=pe(o/r|0,e[o],e[o+1],s);return s&&X(s,s.next)&&(Q(s),s=s.next),s}function k(e,t=e){let n=t===e,r=e,i;do i=!1,r!==r.next&&(J.size===0||!J.has(r))&&(X(r,r.next)||F(r.prev,r,r.next)===0)?((n||r===t)&&(t=r.prev),Et=!0,Q(r),r=r.prev,i=!0):(n||r!==t)&&(r=r.next,i=!n);while(i||r!==t);return t}function Vt(e,t,n,r,i){i&&Mn(e,n,r,i);let s=e,o=!1;for(;e.prev!==e.next;){let a=e.prev,h=e.next;if(F(a,e,h)<0&&(i?cn(e,n,r,i):fn(e))){t.push(a.i,e.i,h.i),Q(e),e=h,s=h;continue}if(e=h,e===s){if(Et=!1,e=k(e),Et){s=e;continue}if(!o){e=xn(e,t),s=e,o=!0;continue}vn(e,t,n,r,i);break}}}function fn(e){let t=e.prev,n=e,r=e.next,i=t.x,s=n.x,o=r.x,a=t.y,h=n.y,l=r.y,c=Math.min(i,s,o),u=Math.min(a,h,l),f=Math.max(i,s,o),x=Math.max(a,h,l),v=r.next;for(;v!==t;){if(v.x>=c&&v.x<=f&&v.y>=u&&v.y<=x&&!(i===v.x&&a===v.y)&&ot(i,a,s,h,o,l,v.x,v.y)&&F(v.prev,v,v.next)>=0)return!1;v=v.next}return!0}function cn(e,t,n,r){let i=e.prev,s=e,o=e.next,a=i.x,h=s.x,l=o.x,c=i.y,u=s.y,f=o.y,x=Math.min(a,h,l),v=Math.min(c,u,f),p=Math.max(a,h,l),V=Math.max(c,u,f),$=Dt(x,v,t,n,r),G=Dt(p,V,t,n,r),y=e.prevZ;for(;y&&y.z>=$;){if(y.x>=x&&y.x<=p&&y.y>=v&&y.y<=V&&y!==o&&!(a===y.x&&c===y.y)&&ot(a,c,h,u,l,f,y.x,y.y)&&F(y.prev,y,y.next)>=0)return!1;y=y.prevZ}let m=e.nextZ;for(;m&&m.z<=G;){if(m.x>=x&&m.x<=p&&m.y>=v&&m.y<=V&&m!==o&&!(a===m.x&&c===m.y)&&ot(a,c,h,u,l,f,m.x,m.y)&&F(m.prev,m,m.next)>=0)return!1;m=m.nextZ}return!0}function xn(e,t){let n=e,r=!1;do{let i=n.prev,s=n.next.next;me(i,n,n.next,s,!1)&&q(i,s)&&q(s,i)&&(t.push(i.i,n.i,s.i),Q(n),Q(n.next),n=e=s,r=!0),n=n.next}while(n!==e);return r?k(n):n}function vn(e,t,n,r,i){let s=e;do{let o=s.next.next;for(;o!==s.prev;){if(s.i!==o.i&&zn(s,o)){let a=Fe(s,o);s=k(s,s.next),a=k(a,a.next),Vt(s,t,n,r,i),Vt(a,t,n,r,i);return}o=o.next}s=s.next}while(s!==e)}var Pt=!1;function pn(e,t,n,r){let i=[];for(let s=0,o=t.length;s<o;s++){let a=t[s]*r,h=s<o-1?t[s+1]*r:e.length,l=de(e,a,h,r,!1);l===l.next&&J.add(l),i.push(Sn(l))}i.sort(gn),yn(e.length/r,t.length),we(n,n),Pt=!0;for(let s=0;s<i.length;s++)n=dn(i[s],n);return Pt=!1,k(n)}function gn(e,t){return e.x-t.x||e.y-t.y||(e.next.y-e.y)/(e.next.x-e.x)-(t.next.y-t.y)/(t.next.x-t.x)}function dn(e,t){let n=mn(e,t);if(!n)return t;let r=Fe(n,e),i=r.next;return we(n,i.next),k(r,r.next),k(n,n.next)}var ye=16,w=new Float64Array(0),st=0,Ct=[],Ut=[];function yn(e,t){let n=Math.ceil((e+2*t)/ye)+t+2;w.length<n*4&&(w=new Float64Array(n*4)),st=0}function we(e,t){let n=e;do{let r=st++;Ct[r]=n;let i=n.x,s=n.y,o=n.x,a=n.y,h=0;do{let c=n.next;n.z=r,c.x<i&&(i=c.x),c.x>o&&(o=c.x),c.y<s&&(s=c.y),c.y>a&&(a=c.y),n=c}while(++h<ye&&n!==t);Ut[r]=n;let l=r*4;w[l]=i,w[l+1]=s,w[l+2]=o,w[l+3]=a}while(n!==t)}function wn(e,t){let n=e.z*4;t.x<w[n]&&(w[n]=t.x),t.y<w[n+1]&&(w[n+1]=t.y),t.x>w[n+2]&&(w[n+2]=t.x),t.y>w[n+3]&&(w[n+3]=t.y)}function xe(e){let t=Ut[e];for(;t.prev.next!==t;)t=t.next;return Ut[e]=t,t}function ve(e){let t=Ct[e];for(;t.prev.next!==t;)t=t.next;return Ct[e]=t,t}function mn(e,t){let n=t,r=e.x,i=e.y,s=-1/0,o;if(X(e,n))return n;for(let f=0,x=0;f<st;f++,x+=4){if(i<w[x+1]||i>w[x+3]||w[x]>r||w[x+2]<=s)continue;let v=xe(f);n=ve(f);do{if(n.prev.next===n){if(X(e,n.next))return n.next;if(i<=n.y&&i>=n.next.y&&n.next.y!==n.y){let p=n.x+(i-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(p<=r&&p>s&&(s=p,o=n.x<n.next.x?n:n.next,p===r))return o}}n=n.next}while(n!==v)}if(!o)return null;let a=o.x,h=o.y,l=Math.min(i,h),c=Math.max(i,h),u=1/0;for(let f=0,x=0;f<st;f++,x+=4){if(w[x+2]<a||w[x]>r||w[x+3]<l||w[x+1]>c)continue;let v=xe(f);n=ve(f);do{if(n.prev.next===n&&r>=n.x&&n.x>=a&&r!==n.x&&ot(i<h?r:s,i,a,h,i<h?s:r,i,n.x,n.y)){let p=Math.abs(i-n.y)/(r-n.x);(q(n,e)||n.y===i&&n.next.y===i&&n.next.x>r)&&(p<u||p===u&&(n.x>o.x||n.x===o.x&&Fn(o,n)))&&(o=n,u=p)}n=n.next}while(n!==v)}return o}function Fn(e,t){return F(e.prev,e,t.prev)<0&&F(t.next,e,e.next)<0}var z=[],R=[],B=new Uint32Array(0),_=new Uint32Array(0),Y=new Uint32Array(256);function Mn(e,t,n,r){let i=e,s=0;do i.z=Dt(i.x,i.y,t,n,r),z[s++]=i,i=i.next;while(i!==e);Tn(s);let o=null;for(let a=0;a<s;a++){let h=z[a];h.prevZ=o,o&&(o.nextZ=h),o=h}o.nextZ=null,z.fill(null,0,s),R.fill(null,0,s)}function Tn(e){if(e<=32){for(let t=1;t<e;t++){let n=z[t],r=n.z,i=t-1;for(;i>=0&&z[i].z>r;)z[i+1]=z[i],i--;z[i+1]=n}return}B.length<e&&(B=new Uint32Array(e),_=new Uint32Array(e),R=new Array(e));for(let t=0;t<e;t++)B[t]=z[t].z;rt(e,z,B,R,_,0),rt(e,R,_,z,B,8),rt(e,z,B,R,_,16),rt(e,R,_,z,B,24)}function rt(e,t,n,r,i,s){Y.fill(0);for(let a=0;a<e;a++)Y[n[a]>>>s&255]++;let o=0;for(let a=0;a<256;a++){let h=Y[a];Y[a]=o,o+=h}for(let a=0;a<e;a++){let h=n[a],l=Y[h>>>s&255]++;r[l]=t[a],i[l]=h}}function Dt(e,t,n,r,i){return e=(e-n)*i|0,t=(t-r)*i|0,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,t=(t|t<<8)&16711935,t=(t|t<<4)&252645135,t=(t|t<<2)&858993459,t=(t|t<<1)&1431655765,e|t<<1}function Sn(e){let t=e,n=e;do(t.x<n.x||t.x===n.x&&t.y<n.y)&&(n=t),t=t.next;while(t!==e);return n}function ot(e,t,n,r,i,s,o,a){return(i-o)*(t-a)>=(e-o)*(s-a)&&(e-o)*(r-a)>=(n-o)*(t-a)&&(n-o)*(s-a)>=(i-o)*(r-a)}function zn(e,t){let n=X(e,t)&&F(e.prev,e,e.next)>0&&F(t.prev,t,t.next)>0;return e.next.i!==t.i&&(n||q(e,t)&&q(t,e)&&(F(e.prev,e,t.prev)!==0||F(e,t.prev,t)!==0))&&!An(e,t)&&(n||En(e,t))}function F(e,t,n){return(t.y-e.y)*(n.x-t.x)-(t.x-e.x)*(n.y-t.y)}function X(e,t){return e.x===t.x&&e.y===t.y}function me(e,t,n,r,i=!0){let s=F(e,t,n),o=F(e,t,r),a=F(n,r,e),h=F(n,r,t);return(s>0&&o<0||s<0&&o>0)&&(a>0&&h<0||a<0&&h>0)?!0:i?!!(s===0&&it(e,n,t)||o===0&&it(e,r,t)||a===0&&it(n,e,r)||h===0&&it(n,t,r)):!1}function it(e,t,n){return t.x<=Math.max(e.x,n.x)&&t.x>=Math.min(e.x,n.x)&&t.y<=Math.max(e.y,n.y)&&t.y>=Math.min(e.y,n.y)}function An(e,t){let n=Math.min(e.x,t.x),r=Math.max(e.x,t.x),i=Math.min(e.y,t.y),s=Math.max(e.y,t.y),o=e;do{let a=o.next;if(o.x>r&&a.x>r||o.x<n&&a.x<n||o.y>s&&a.y>s||o.y<i&&a.y<i){o=a;continue}if(o.i!==e.i&&a.i!==e.i&&o.i!==t.i&&a.i!==t.i&&me(o,a,e,t))return!0;o=a}while(o!==e);return!1}function q(e,t){return F(e.prev,e,e.next)<0?F(e,t,e.next)>=0&&F(e,e.prev,t)>=0:F(e,t,e.prev)<0||F(e,e.next,t)<0}function En(e,t){let n=e,r=!1,i=(e.x+t.x)/2,s=(e.y+t.y)/2;do{let o=n.next;n.y>s!=o.y>s&&i<(o.x-n.x)*(s-n.y)/(o.y-n.y)+n.x&&(r=!r),n=o}while(n!==e);return r}function Fe(e,t){let n=Bt(e.i,e.x,e.y),r=Bt(t.i,t.x,t.y),i=e.next,s=t.prev;return e.next=t,t.prev=e,n.next=i,i.prev=n,r.next=n,n.prev=r,s.next=r,r.prev=s,r}function pe(e,t,n,r){let i=Bt(e,t,n);return r?(i.next=r.next,i.prev=r,r.next.prev=i,r.next=i):(i.prev=i,i.next=i),i}function Q(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ),Pt&&wn(e.prev,e.next)}function Bt(e,t,n){return{i:e,x:t,y:n,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Vn(e,t,n,r){let i=0;for(let s=t,o=n-r;s<n;s+=r)i+=(e[o]-e[s])*(e[s+1]+e[o+1]),o=s;return i}export{oe as FetchSource,yt as PMTiles,At as PbfReader,zt as VectorTile,ge as earcut,ne as tileIdToZxy,mt as zxyToTileId};\r\n","modules/coach-world/view.mjs":"import {productionQueue,trainingAvailability} from './production.mjs';\nimport {BUILDINGS,FACTIONS,FACTION_BY_ID,MAX_LEVEL,RULES} from './catalog.mjs';\nimport {constructionQuote,bestTrack} from './state.mjs';\nimport {trainingQuote,baseSystems} from './base-systems.mjs';\nimport {unitRoster,expeditionQuote,shipCapacity,SHIP_ROLES} from './expedition-catalog.mjs';\nimport {harvestResourceLabel as resourceLabel,resourceDisplayText as resourceText} from './resource-labels.mjs';\nimport {createWorldClient} from './client.mjs';\nimport {mountBaseScene,constructionCountdown} from './canvas-scene.mjs';\nimport {mountSectors} from './map-view.mjs';\nimport {mountLocationDrops} from './location-drops-view.mjs';\nimport {performanceOwner} from '../../performance-progress.mjs';\nimport {rtsFlag,rts3dFlag} from '../coach-world-rts/flag.mjs';\nimport {mountCampRescueView} from './camp-rescue-view.mjs';\nimport {captureGameGeneration} from './game-generation-client.mjs';\n\nlet panel=null,cleanup=null,openSeq=0;\nconst raf2=()=>new Promise(r=>{const f=globalThis.requestAnimationFrame;if(!f)return r();f(()=>f(r));});\nconst RTS=rtsFlag()?Promise.all([import('../coach-world-rts/runtime.mjs'),import('../coach-world-rts/hud.mjs')]).catch(()=>null):null;let rtsMods=null;let pin=null;const pinOn=!/[?&]pin=0(?:&|$)/.test(globalThis.location?.search||'');RTS?.then(m=>rtsMods=m);\nexport function expeditionIdForSceneUnit(unit){if(unit.role==='worker')return 'myr5-worker';if(!unit.section)return null;return unit.role==='scout'?`companion-${unit.section}-scout`:unit.role==='carrier'?`companion-${unit.section}-hauler`:['boss','grunt'].includes(unit.role)?`${unit.role}-${unit.section}`:null;}\nconst roleEffectText=effects=>(Array.isArray(effects)?effects:[]).map(effect=>typeof effect==='string'?effect:effect.description||effect.label).filter(Boolean).join(' / ');\nconst el=(tag,value,className)=>{const n=document.createElement(tag);if(value!=null)n.textContent=value;if(className)n.className=className;return n;};\nexport function characterWarRoomUrl(faction,environmentFaction){const id=FACTIONS.some(f=>f.id===environmentFaction)?environmentFaction:FACTIONS.some(f=>f.id===faction)?faction:FACTIONS[0].id;return `/war-room/index.html?from=world&biome=${encodeURIComponent(id)}`;}\nexport function openWorld({onEnterWarRoom,silent=false}={}){\n if(panel?.open){panel.querySelector('h2').focus();return panel;}\n cleanup?.();const openId=++openSeq;if(!document.querySelector('link[data-coach-world]')){const link=document.createElement('link');link.rel='stylesheet';link.href='/modules/coach-world/world.css';link.dataset.coachWorld='';document.head.append(link);}\n panel??=document.createElement('dialog');panel.id='coachWorldPanel';panel.className='cw-panel';panel.setAttribute('aria-labelledby','cw-title');\n panel.innerHTML='<div class=\"cw-shell\"><header class=\"cw-header\"><div><span class=\"cw-eyebrow\">MYR5 / WAR ROOM</span><h2 id=\"cw-title\" tabindex=\"-1\">Your home base.</h2></div><button data-close aria-label=\"Close Coach World\">✕</button></header><nav class=\"cw-tabs\" aria-label=\"World views\"><button data-tab=\"base\" aria-selected=\"true\">Base</button><button data-tab=\"sectors\" aria-selected=\"false\">Sectors</button></nav><div data-identity class=\"cw-identity\">Opening your world…</div><div data-content class=\"cw-content\"></div><p data-status class=\"cw-status\" role=\"status\" aria-live=\"polite\"></p></div>';\n let readyResolve;panel.worldOpenId=openId;panel.worldReady=new Promise(r=>{readyResolve=r;});\n if(!panel.isConnected)document.body.append(panel);panel.showModal();panel.querySelector('h2').focus();\n const controller=new AbortController(),signal=controller.signal,host=panel.querySelector('[data-content]'),status=panel.querySelector('[data-status]');let syncCam=()=>{},snapshot=null,tab='base',selected='hq',mode=null,rtsHud=null,busy=false,closed=false,timer=0,countdownTimer=0,clock=null,enteredWarRoom=false,selectedTroops=[],missionDraft=null;\n const client=createWorldClient({signal,onInvalidate:()=>{status.textContent='Account changed. Reopen World to continue.';panel.close();}});\n const alive=()=>!closed&&!signal.aborted&&panel.open&&openId===openSeq;\n let notified=false,modeGen=0;\n const notify=state=>{if(notified||!alive())return;notified=true;if(!silent)panel.dispatchEvent(new CustomEvent('myr5:menu-ready',{bubbles:true,detail:{surface:'world',state,openId}}));readyResolve({state,openId});};\n RTS?.then(m=>{if(m&&alive()&&tab==='base'&&!mode?.rts)show('base');});\n const serverNow=()=>client.authenticated&&clock?clock.server+performance.now()-clock.local:Date.now();\n const setSnapshot=s=>{if(!alive())return;if(client.authenticated&&Number.isFinite(s.serverTime)&&(!clock||s.serverTime>clock.server))clock={server:s.serverTime,local:performance.now()};snapshot=s;identity();};\n function enterWarRoom(){if(!alive()||enteredWarRoom)return;enteredWarRoom=true;const url=characterWarRoomUrl(snapshot?.base?.faction,snapshot?.environment?.faction),biome=new URL(url,location.origin).searchParams.get('biome');panel.close();if(typeof onEnterWarRoom==='function')onEnterWarRoom(url,{biome});else location.assign(url);}\n function beginHQEntry(){if(mode?.enterWarRoom?.()&&alive()){for(const button of host.querySelectorAll('[data-enter-war-room]'))button.disabled=true;status.textContent='Entering character War Room...';}}\n function updateCountdowns(){if(!alive()||tab!=='base')return;const now=serverNow();for(const node of host.querySelectorAll('[data-construction-due]')){const remaining=constructionCountdown(Number(node.dataset.constructionDue),now);node.textContent=remaining==='Ready to finish'?(node.dataset.countdownReady||'Ready to finish - syncing saved construction'):`${remaining} remaining`;}}\n function identity(){const f=FACTION_BY_ID[snapshot?.base?.faction]||FACTIONS[0];panel.querySelector('[data-identity]').textContent=`${f.name}  /  ${client.authenticated?'Account saved':'Personal base · saved on this device'}`;panel.style.setProperty('--cw-faction',f.color);}\n async function command(action){if(busy)return {ok:false,busy:true};busy=true;status.textContent='Saving…';try{setSnapshot(await client.command(action));if(alive()){status.textContent='Saved.';if(tab==='base')updateBase();}return {ok:true};}catch(e){if(alive()){status.textContent=e.message;rtsHud?.alert?.(e.message);}return {ok:false,error:e};}finally{busy=false;}}\n function updateBase(){if(!snapshot||tab!=='base'||!host.querySelector('[data-fuel]'))return;const base=snapshot.base,q=s=>host.querySelector(s);q('[data-fuel]').textContent=`${Math.round(base.fuel||0)} / 100`;q('[data-calm]').textContent=`${Math.round(base.calm||0)} / 100`;q('[data-charge]').textContent=String(base.launchCharge||0);q('[data-rest]').textContent=base.rest?'Resume the settlement':'Let the crew rest';q('[data-rest-note]').textContent=base.rest?'RESTING · progress and permanent levels are safe':'Crew at work · train to grow your settlement';\n  const list=q('[data-building-list]');list.replaceChildren();for(const def of BUILDINGS){const b=base.buildings[def.id],button=el('button',`${def.label} · ${b?.level===0?'Unbuilt':`${b?.level||1}/${MAX_LEVEL}`}${b?.job?' ◷':''}`);button.className=selected===def.id?'is-selected':'';button.onclick=()=>{selected=def.id;mode?.select?.(selected);updateBase();};if(b?.job){const countdown=el('span');countdown.dataset.constructionDue=String(b.job.dueAt);countdown.style.display='block';button.append(countdown);}list.append(button);}\n  const def=BUILDINGS.find(b=>b.id===selected)||BUILDINGS[0],b=base.buildings[def.id],f=FACTION_BY_ID[def.track]||FACTION_BY_ID[base.faction]||FACTIONS[0],sheet=q('[data-sheet]');sheet.replaceChildren();sheet.append(el('span',b?.level===0?`${def.kind.toUpperCase()} / UNBUILT SITE`:`${def.kind.toUpperCase()} / LEVEL ${b?.level||1} OF ${MAX_LEVEL}`,'cw-eyebrow'),el('h3',def.label),el('p',f.description,'cw-description'));\n  const select=el('select');select.setAttribute('aria-label','Protein to use for this upgrade');for(const faction of def.track?[f]:FACTIONS){const option=el('option',`${faction.label} · ${base.materials?.[faction.id]||0} Protein`);option.value=faction.id;select.append(option);}select.value=bestTrack(base,def.id);sheet.append(select);\n  const next=(b?.level??1)+1,info=el('p',null,'cw-note'),progress=el('p',null,'cw-note'),prerequisites=el('p',null,'cw-note');sheet.append(info,progress,prerequisites);\n  // HQ trip speed: the bonus in effect now, and the bonus the next completed level would give (applied after the trips in progress return).\n  if(def.id==='hq'){const now=baseSystems(base).movementSpeedPct,up=next<=MAX_LEVEL?baseSystems({...base,buildings:{...base.buildings,hq:{...base.buildings.hq,level:next}}}).movementSpeedPct:now,pace=el('p',null,'cw-note');pace.dataset.tripSpeed='';pace.textContent=`Trip speed +${now}%${next<=MAX_LEVEL&&up>now?` · next level +${up}%, applied after current trips return`:''}`;sheet.append(pace);}\n  const quoteText=()=>{const quote=constructionQuote(base,def.id,select.value),control=sheet.querySelector('[data-construction-command]');if(control){control.disabled=!quote.allowed;control.title=resourceText(quote.reason)||'';}info.textContent=b?.job?'Construction is saved and continues while this screen is closed.':next>MAX_LEVEL?'Monument complete. This permanent level is yours.':`${b?.level===0?'Build':'Next level'}: ${quote.cost} ${FACTION_BY_ID[quote.track]?.label||'matching'} Protein / ${quote.fuelCost} Glycogen / ${Math.ceil((quote.durationMs||0)/60000)} min`;progress.textContent=b?.job?`Completes ${new Date(b.job.dueAt).toLocaleTimeString()}.`:quote.allowed?'Ready now. Basic construction does not require workout days.':resourceText(quote.reason)||'Not ready yet.';prerequisites.textContent=(quote.prerequisites||[]).map(p=>`${p.met?'Ready':'Need'}: ${BUILDINGS.find(d=>d.id===p.buildingId)?.label||p.buildingId} level ${p.level}`).join(' / ')||'No building prerequisites.';if(quote.softCapMultiplier>1)prerequisites.textContent+=` Support balance: ${quote.softCapMultiplier.toFixed(2)}x cost and time.`;};select.onchange=quoteText;quoteText();\n  const actions=el('div',null,'cw-actions');if(b?.job){const due=b.job.dueAt;if(due){const countdown=el('p');countdown.dataset.constructionDue=String(due);countdown.setAttribute('role','timer');countdown.setAttribute('aria-live','off');sheet.append(countdown);}const cancel=el('button','Cancel construction');cancel.onclick=()=>command({type:'cancel',buildingId:def.id});actions.append(cancel);}else if(next<=MAX_LEVEL){const upgrade=el('button',b.level===0?'Build this site':`Upgrade to level ${next}`);upgrade.className='cw-primary';upgrade.dataset.constructionCommand='';upgrade.onclick=()=>command({type:b.level===0?'build':'upgrade',buildingId:def.id,track:select.value});actions.append(upgrade);}sheet.append(actions);quoteText();if(def.id==='hq'){const enter=el('button','Enter character War Room','cw-primary');enter.dataset.enterWarRoom='';enter.onclick=beginHQEntry;sheet.append(enter);}\n  if(def.kind==='production'){sheet.append(el('h4','Companion training'));for(const unit of unitRoster(base).filter(u=>u.faction===def.track&&u.trainable)){const card=el('div',null,'cw-training-row'),q=trainingAvailability(base,unit.id);card.append(el('strong',`${unit.label} / ${unit.count??0} owned`),el('p',unit.description,'cw-note'));const train=el('button',`Train / ${q.unitMaterialCost} Protein / ${Math.ceil(q.unitTrainingMs/60000)} min`);train.disabled=!q.allowed;train.onclick=()=>command({type:'train-unit',unitId:unit.id});card.append(train);if(!q.allowed)card.append(el('p',resourceText(q.reason),'cw-note'));sheet.append(card);}for(const job of productionQueue(base,def.id).items){const row=el('div',null,'cw-training-row'),time=el('span');time.dataset.constructionDue=String(job.dueAt);time.dataset.countdownReady='Training complete - syncing crew';const cancel=el('button','Cancel training');cancel.onclick=()=>command({type:'cancel-production',queueId:job.queueId});row.append(el('strong',job.label),time,cancel);sheet.append(row);}}\n  if(!b?.job){const move=el('div',null,'cw-actions');for(const [name,dx,dy] of [['Move left',-1,0],['Move up',0,-1],['Move down',0,1],['Move right',1,0]]){const button=el('button',name);button.onclick=()=>command({type:'move',buildingId:def.id,x:b.x+dx,y:b.y+dy});move.append(button);}sheet.append(el('p','Move one plot; every entrance must remain reachable.','cw-note'),move);}\n  if(def.kind==='hangar'){const ships=el('select');ships.setAttribute('aria-label','Owned ship');for(const [id,hull] of Object.entries(base.ships||{})){const option=el('option',`${id} · ${hull.tier===0?'starter':`tier ${hull.tier}`}`);option.value=id;ships.append(option);}ships.value=base.selectedShip;const choose=el('button','Select ship');choose.onclick=()=>command({type:'select-ship',shipId:ships.value});sheet.append(ships,choose);}\n  renderExpedition();updateCountdowns();try{rtsHud?.update(snapshot);}catch(e){console.warn('coach-world rts hud',e);}\n }\n function renderExpedition(){const node=host.querySelector('[data-expedition]');if(!node||!snapshot)return;const base=snapshot.base;node.replaceChildren();node.append(el('span','EXPEDITION STAGING','cw-eyebrow'),el('h3','Send a small crew.'));const mission=base.expedition;\n  if(mission){const f=FACTION_BY_ID[mission.factionId];const effects=roleEffectText(mission.roleEffects);if(effects)node.append(el('p',effects,'cw-note'));node.append(el('p',`${f?.name||'Civilization'} / ${mission.status.toUpperCase()} / ${mission.supply} of ${mission.capacity} Training capacity`),el('p',`${mission.shipId} / ${mission.unitIds.length} roles / ${mission.materialCost} Protein + ${mission.fuelCost} Glycogen`,'cw-note'));if(mission.status==='prepared'){const launch=el('button','Launch personal expedition','cw-primary');launch.onclick=()=>command({type:'launch-expedition'});const cancel=el('button','Cancel prepared expedition');cancel.onclick=()=>command({type:'cancel-expedition'});node.append(launch,cancel);}else if(['launched','returning'].includes(mission.status)){const timer=el('p');timer.dataset.constructionDue=String(mission.status==='returning'?mission.returnDueAt:mission.dueAt);timer.dataset.countdownReady='Journey complete - syncing your crew';timer.setAttribute('role','timer');node.append(timer);if(mission.status==='launched'){const recall=el('button','Recall crew / begin return');recall.onclick=()=>command({type:'return-expedition'});node.append(recall);}node.append(el('p','Your permanent buildings and coaches are safe while the crew travels.','cw-note'));return;}else node.append(el('p','The crew is home with a Legendary Pack. You can prepare another personal mission.','cw-note'));if(mission.status==='prepared')return;}\n  missionDraft??={factionId:base.faction,shipId:base.selectedShip,unitIds:['myr5-worker']};const faction=el('select');faction.setAttribute('aria-label','Expedition civilization');for(const f of FACTIONS){const option=el('option',f.name);option.value=f.id;faction.append(option);}faction.value=missionDraft.factionId;const ship=el('select');ship.setAttribute('aria-label','Expedition ship');for(const [id,hull]of Object.entries(base.ships)){const option=el('option',`${id} / ${SHIP_ROLES[id]?.role||'carrier'} / ${shipCapacity(base,id,missionDraft.unitIds)} Training capacity`);option.value=id;ship.append(option);}ship.value=missionDraft.shipId;node.append(faction,ship);const roster=unitRoster(base).filter(u=>!u.faction||u.faction===missionDraft.factionId),list=el('div',null,'cw-role-list'),summary=el('p',null,'cw-note'),prepare=el('button','Prepare personal mission','cw-primary');\n  const quote=()=>{const q=expeditionQuote(base,{...missionDraft,shared:false});summary.textContent=(q.allowed?`${q.supply}/${q.capacity} Training capacity / ${q.materialCost} Protein / ${q.fuelCost} Glycogen / ${Math.ceil(q.durationMs/60000)} min + ${Math.ceil((q.returnDurationMs||60000)/60000)} min return`:resourceText(q.reason))+(roleEffectText(q.roleEffects)?` / ${roleEffectText(q.roleEffects)}`:'');prepare.disabled=!q.allowed;};\n  for(const unit of roster){const label=el('label',null,'cw-role-option'),input=el('input');input.type='checkbox';input.value=unit.id;input.checked=missionDraft.unitIds.includes(unit.id);input.disabled=!unit.available;input.onchange=()=>{missionDraft.unitIds=[...list.querySelectorAll('input:checked')].map(i=>i.value);quote();};label.append(input,el('span',`${unit.label} / ${unit.role} / ${unit.supply} Training capacity${unit.reserved?' / away':!unit.owned?' / train or unlock first':''}`));list.append(label);}node.append(list,summary);faction.onchange=()=>{missionDraft.factionId=faction.value;missionDraft.unitIds=['myr5-worker'];renderExpedition();};ship.onchange=()=>{missionDraft.shipId=ship.value;quote();};prepare.onclick=()=>command({type:'prepare-expedition',...missionDraft,shared:false});\n  const stage=el('button','Stage selected troops');stage.onclick=()=>{const available=new Set(roster.filter(u=>u.available).map(u=>u.id));missionDraft.unitIds=[...new Set(selectedTroops.map(expeditionIdForSceneUnit).filter(id=>available.has(id)))];status.textContent=`Staged ${missionDraft.unitIds.length} mission-ready roles. Production training prepares garrison companions for expedition duty.`;renderExpedition();};node.append(stage,prepare,el('p','Personal missions travel from your base. Nearby public battles stay in Sectors. Training unlocks expedition duty for your home companions.','cw-note'));quote();\n }\n function renderBase(){host.innerHTML='<section class=\"cw-base-layout\"><div class=\"cw-base-main\"><div class=\"cw-vitals\"><div><span>GLYCOGEN</span><b data-fuel></b></div><div><span>COMPOSURE</span><b data-calm></b></div><div><span>READINESS</span><b data-charge></b></div></div><div class=\"cw-scene-wrap\"><canvas class=\"cw-base-canvas\" aria-label=\"Your pixel settlement. Tap a building or use the building list below.\"></canvas><span class=\"cw-scene-tag\">PERMANENT HOME BASE</span><button class=\"cw-recenter\" data-center>Center</button></div><div class=\"cw-troop-tools\"><button data-select-troops aria-pressed=\"false\">Select troops</button><button data-clear-troops>Clear selection</button><button data-rally-hq>Rally at command</button></div><p data-troop-status class=\"cw-note\" role=\"status\">Hold still, then drag to collect troops. Short drags pan the view.</p><div class=\"cw-actions\"><button data-enter-war-room class=\"cw-primary\">Enter character War Room</button></div><p class=\"cw-note\">Tap the central command habitat to enter your character War Room.</p><p class=\"cw-note\">Drag to pan · pinch to zoom · tap a hall to upgrade</p><div data-building-list class=\"cw-building-list\" aria-label=\"Select building\"></div></div><aside class=\"cw-base-side\"><section data-sheet class=\"cw-card cw-building-sheet\"></section><section data-expedition class=\"cw-card\"></section><section class=\"cw-card\"><span class=\"cw-eyebrow\">A LITTLE CARE GOES A LONG WAY</span><h3>Keep the station humming.</h3><div class=\"cw-actions\"><button data-greet>Greet crew</button><button data-feed>Nutrition station</button><button data-meditate>Composure station</button><button data-rest></button></div><p data-rest-note class=\"cw-note\"></p><p class=\"cw-note\">Glycogen uses Meal packs earned from logged food. Composure uses Breathwork tokens earned from meditation. Rest preserves every building.</p></section><section class=\"cw-card\"><h3>Your civilization</h3><select data-faction aria-label=\"Workout civilization\"></select><button data-change-faction>Choose faction</button><p class=\"cw-note\">Your workout halls stay permanent. Switching shared territory faction follows the server cooldown.</p></section></aside></section>';\n  const home=el('section',null,'cw-card');home.append(el('h3','A private home in the fog'),el('p','Set the place where your base lives. Strangers only see public sectors. This does not share your exact location with friends.','cw-note'));const setHome=el('button',snapshot.base.home?'Update private home here':'Set private home here');setHome.disabled=!client.authenticated;setHome.onclick=()=>{if(!navigator.geolocation){status.textContent='Location unavailable. Your base is still saved.';return;}status.textContent='Waiting for a location fix for your private home…';navigator.geolocation.getCurrentPosition(p=>{if(alive())void command({type:'home',latitude:p.coords.latitude,longitude:p.coords.longitude,consent:true});},()=>{if(alive())status.textContent='Home location was not changed. Allow location access and retry.';},{enableHighAccuracy:true,maximumAge:0,timeout:15000});};home.append(setHome);if(!client.authenticated)home.append(el('p','Sign in to set a private home anchor.','cw-note'));const dropsHost=el('section');dropsHost.dataset.locationDrops='';host.querySelector('.cw-base-side').append(home,dropsHost);\n  let camp=null;const getAccount=()=>globalThis.myr5AuthenticatedAccount;\n  const ownerId=()=>performanceOwner();let rts=null;try{rts=rtsMods?.[0].createRts({ownerId});}catch{rts=null;}\n  const sceneMode=mountBaseScene(host.querySelector('canvas'),{rts,command,getBase:()=>snapshot?.base,getServerTime:serverNow,onEnterWarRoom:enterWarRoom,onSelectionChange:detail=>{selectedTroops=detail.units;const node=host.querySelector('[data-troop-status]');if(node)node.textContent=detail.count?`${detail.count} troops selected. Tap empty ground to rally; drag a box to refine selection.`:'Hold still, then drag to collect troops. Short drags pan the view.';host.querySelector('[data-select-troops]')?.setAttribute('aria-pressed',String(detail.selectionMode));},onRally:detail=>{status.textContent=`${detail.count} troops moving to the rally point.`;},onSelect:id=>{selected=id;updateBase();}});let base3d=null,gone3d=false,stopFx=null,usableRes;const usable=new Promise(r=>{usableRes=r;});const gen=modeGen;if(rts&&rts3dFlag()){const spec='../coach-world-rts/base3d.mjs';setTimeout(()=>usableRes(),6000)?.unref?.();import(spec).then(m=>{if(gone3d||!alive()||gen!==modeGen){usableRes();return;}const b3=m.mountBase3d(host.querySelector('.cw-scene-wrap'),{getBase:()=>snapshot?.base,getCoachBase:()=>camp?.getBase()??snapshot?.base,getPresentationScope:()=>({owner:client.owner,epoch:client.authenticated?snapshot?.dataEpoch:'guest',gameGeneration:client.authenticated?captureGameGeneration():'guest'}),sceneMode,rts,signal,command});base3d=b3;b3.firstFrame.then(()=>usableRes());import(spec.replace('base3d','fx3d')).then(f=>{if(gone3d||base3d!==b3||!alive())return;stopFx=f.mountFx3d(b3);}).catch(e=>console.warn('coach-world fx3d',e));}).catch(e=>{console.warn('coach-world base3d',e);usableRes();});}else raf2().then(()=>usableRes()); /* non-literal spec: keeps the bundler from pulling in three */\n  const dropsMode=mountLocationDrops(dropsHost,{client,signal});try{rtsHud=rts?rtsMods[1].mountHud(panel,host,{getBase:()=>snapshot?.base,getAccount,serverNow,command,sceneMode,alive,ownerId,onRaid:()=>{const spec='../coach-world-rts/raid.mjs';import(spec).then(m=>{if(alive()&&gen===modeGen&&!panel.__raid)m.startRaid(panel,{client,getBase:()=>snapshot?.base,sceneMode,signal});}).catch(e=>console.warn('coach-world raid',e));}}):null;}catch(e){console.warn('coach-world rts hud',e);rtsHud=null;rts?.dispose();rts=null;}camp=mountCampRescueView(host.querySelector('.cw-base-side'),{getBase:()=>snapshot?.base,getAccount,signal,openHeart:()=>rtsHud?.openRunLoadout?.(),onInvalidate:()=>{if(alive())panel.close();}});mode={...sceneMode,rts:!!rtsHud,usable,recenter(){sceneMode.recenter();base3d?.recenter();},dispose(){gone3d=true;camp?.dispose();camp=null;try{stopFx?.();}catch{}stopFx=null;base3d?.dispose();base3d=null;usableRes();rtsHud?.dispose();rtsHud=null;dropsMode.dispose();sceneMode.dispose();}};for(const button of host.querySelectorAll('[data-enter-war-room]'))button.onclick=beginHQEntry;host.querySelector('[data-center]').onclick=()=>mode.recenter();host.querySelector('[data-select-troops]').onclick=()=>mode.selectTroops();host.querySelector('[data-clear-troops]').onclick=()=>mode.clearSelection();host.querySelector('[data-rally-hq]').onclick=()=>{if(!mode.rallyAtHQ())status.textContent=snapshot.base.rest?'Resume the settlement before giving rally orders.':'Select available troops first.';};host.querySelector('[data-greet]').onclick=()=>command({type:'care',kind:'greet'});host.querySelector('[data-feed]').onclick=()=>command({type:'care',kind:'fuel'});host.querySelector('[data-meditate]').onclick=()=>command({type:'care',kind:'meditate'});host.querySelector('[data-rest]').onclick=()=>command({type:'rest',value:!snapshot.base.rest});const select=host.querySelector('[data-faction]');for(const f of FACTIONS){const o=el('option',`${f.label} / ${f.name}`);o.value=f.id;select.append(o);}select.value=snapshot.base.faction;host.querySelector('[data-change-faction]').onclick=()=>command({type:'faction',factionId:select.value});updateBase();\n }\n function show(next){if(!alive())return;pin?.dispose();pin=null;tab=next;modeGen++;syncCam();mode?.dispose();mode=null;host.replaceChildren();for(const button of panel.querySelectorAll('[data-tab]'))button.setAttribute('aria-selected',String(button.dataset.tab===tab));if(!snapshot){host.append(el('p','Loading your saved base…','cw-loading'));return;}if(tab==='base')renderBase();else if(tab==='sectors')mode=mountSectors(host,{client,signal,onSnapshot:setSnapshot});}\n for(const button of panel.querySelectorAll('[data-tab]'))button.onclick=()=>show(button.dataset.tab);\n // R58: one map, no tab bar; \"Peer\" (pin-drop AR) and the settings gear are header buttons (?rts=0 keeps the tabs)\n if(RTS){panel.classList.add('cw-one-map');panel.querySelector('.cw-tabs').style.display='none';const cam=el('button','Peer','cw-cam');cam.dataset.cam='';cam.setAttribute('aria-label','Peer through a portal');panel.querySelector('[data-close]').before(cam);\n  const gear=el('button','⚙','cw-gear');gear.dataset.settings='';gear.setAttribute('aria-label','Settings');panel.querySelector('[data-close]').before(gear);gear.onclick=()=>rtsHud?.setSheet?.(true);\n  // Peer: pin-drop. Lane B/I \"Land beside\" exits with {landBeside:{pin,name,toast}} -> back on the base, pin mode reopens at that pin with a toast; DROP stays a user tap (permission gesture).\n  const flightOn=new URLSearchParams(location.search).get('flight')!=='0';\n  const launchFlight=async point=>{if(!alive())return;pin?.dispose();pin=null;modeGen++;mode?.dispose();mode=null;host.replaceChildren();try{const m=await import('../coach-flight/view.mjs');if(!alive())return;mode=m.mountFlight({host:panel,getBase:()=>snapshot.base,pin:point,owner:performanceOwner(),getFriends:async()=>(await client.map()).friends||[],getZones:p=>client.authenticated?client.zones({latitude:p.lat,longitude:p.lng}).catch(()=>null):null,signal,onExit:info=>{if(!alive())return;show('base');if(info?.landingPin)reopen(info.landBeside||{pin:info.landingPin,toast:'Choose the exact landing pin, then DROP.'});}});}catch(e){if(alive()){show('base');status.textContent=e.message;}}};\n  const peer=(initialPin=null,toast='',target=null)=>{pin?.dispose();pin=null;const w=host.querySelector('.cw-scene-wrap');if(!pinOn||!w?.__base3d){rtsHud?.toast?.('3D base still loading');return;}import('../coach-world-rts/pin-drop.mjs').then(m=>{if(!alive())return;pin=m.mountPinDrop({wrap:w,host:panel,getBase:()=>snapshot.base,signal,initialPin,target,boarding:flightOn,deps:{worldClient:client,onWorldChange:s=>{if(s)setSnapshot(s);}},onExit:info=>{pin=null;if(!alive())return;if(info?.flight){void launchFlight(info.flight.pin);return;}show('base');const lb=info?.landBeside;if(lb)reopen(lb);}});const p=pin.open();if(toast)rtsHud?.toast?.(toast,{ms:5000});return p;}).catch(()=>rtsHud?.toast?.('Peer is unavailable right now'));};\n  const reopen=(lb,t0=Date.now())=>{if(!alive()||tab!=='base')return;if(host.querySelector('.cw-scene-wrap')?.__base3d)return peer(lb.pin,lb.toast,lb.target);if(Date.now()-t0<8000)setTimeout(()=>reopen(lb,t0),100);};\n  cam.onclick=()=>{if(tab!=='base')return show('base');peer();};\n  if(flightOn){const fly=el('button','Fly','cw-cam');fly.dataset.flight='';fly.setAttribute('aria-label','Fly your ship');cam.before(fly);fly.onclick=()=>{const h=snapshot?.base?.home;void launchFlight(h?{lat:h.latitude,lng:h.longitude}:undefined);};}}\n panel.querySelector('[data-close]').onclick=()=>panel.close();\n const dispose=()=>{if(closed)return;closed=true;readyResolve({state:'closed',openId});controller.abort();mode?.dispose();mode=null;clearInterval(timer);clearInterval(countdownTimer);client.dispose();};cleanup=dispose;panel.addEventListener('close',()=>{if(panel.open||openId!==openSeq)return;dispose();},{signal});globalThis.addEventListener('pagehide',()=>panel.close(),{signal});\n void Promise.all([client.load(),RTS||null]).then(([s])=>{if(alive()){setSnapshot(s);show(tab);void (mode?.usable||raf2()).then(()=>notify('ready'));}}).catch(e=>{if(alive()){status.textContent=e.message;host.append(el('button','Retry opening World','cw-button'));host.lastChild.onclick=()=>{panel.close();openWorld({onEnterWarRoom,silent:true});};void raf2().then(()=>notify('error'));}});\n countdownTimer=setInterval(updateCountdowns,1000);\n timer=setInterval(()=>{if(alive()&&tab==='base'&&!busy&&(Object.values(snapshot?.base?.buildings||{}).some(b=>b.job)||Object.keys(snapshot?.base?.training||{}).length||Object.keys(snapshot?.base?.rtsTech?.jobs||{}).length||Object.keys(snapshot?.base?.rtsTech?.repairs||{}).length||Object.keys(snapshot?.base?.rtsEconomy?.assignments||{}).length||Object.values(snapshot?.base?.rtsEconomy?.tripState?.workers||{}).some(w=>w.phase!=='idle')||['launched','returning'].includes(snapshot?.base?.expedition?.status)))void command({type:client.authenticated?'tick':'rest',...(client.authenticated?{}:{value:snapshot.base.rest})});},10000);\n return panel;\n}\n","modules/coach-world/wild-spots-data.mjs":"// Generated by world-bake/wild-spots.py + wild-spots-to-mjs.mjs (2026-10-18). OpenStreetMap contributors, ODbL. Do not edit.\r\n// Spots = 30 m grid cells of eligible public park/natural land (A9 filters: 25 m roads, 40 m homes, 75 m institutions, 15 m inside edge). [lat,lng,land]\r\nexport const WILD_BAKE=Object.freeze({\"generated\":\"2026-10-18\",\"bbox\":\"36.08,-115.28,36.28,-115.08\",\"source\":\"OpenStreetMap via Overpass (ODbL)\",\"count\":3958,\"byLand\":{\"park\":1198,\"desert\":2760},\"cellM\":30,\"rules\":{\"minRoadM\":25,\"minHomesM\":40,\"minInstitutionM\":75,\"minEdgeM\":15,\"gridM\":30},\"spots\":3958,\"res8Cells\":149});\r\nexport const WILD_SPOTS_BY_RES8=Object.freeze({\"882986b8ddfffff\":[[36.13888,-115.23709,\"park\"],[36.1389,-115.23775,\"park\"],[36.13894,-115.23736,\"park\"],[36.13916,-115.23807,\"park\"],[36.13959,-115.23811,\"park\"],[36.13981,-115.23778,\"park\"],[36.13986,-115.2377,\"park\"],[36.14011,-115.23748,\"park\"],[36.14313,-115.24111,\"desert\"],[36.1434,-115.24111,\"desert\"]],\"882986ba93fffff\":[[36.11572,-115.27816,\"desert\"],[36.11599,-115.27816,\"desert\"],[36.11599,-115.27783,\"desert\"],[36.11626,-115.27816,\"desert\"],[36.11626,-115.27783,\"desert\"],[36.11626,-115.27749,\"desert\"],[36.11626,-115.27716,\"desert\"],[36.11654,-115.27816,\"desert\"],[36.11654,-115.27783,\"desert\"],[36.11654,-115.27749,\"desert\"],[36.11654,-115.27716,\"desert\"],[36.11654,-115.27683,\"desert\"],[36.11681,-115.27816,\"desert\"],[36.11681,-115.27783,\"desert\"],[36.11681,-115.27749,\"desert\"],[36.11681,-115.27716,\"desert\"],[36.11681,-115.27683,\"desert\"],[36.11681,-115.27649,\"desert\"],[36.11708,-115.27816,\"desert\"],[36.11708,-115.27783,\"desert\"],[36.11708,-115.27749,\"desert\"],[36.11708,-115.27716,\"desert\"],[36.11708,-115.27683,\"desert\"],[36.11708,-115.27649,\"desert\"],[36.11708,-115.27616,\"desert\"],[36.11735,-115.27816,\"desert\"],[36.11735,-115.27783,\"desert\"],[36.11735,-115.27749,\"desert\"],[36.11735,-115.27716,\"desert\"],[36.11735,-115.27683,\"desert\"],[36.11735,-115.27649,\"desert\"],[36.11735,-115.27616,\"desert\"],[36.11735,-115.27583,\"desert\"],[36.11762,-115.27816,\"desert\"],[36.11762,-115.27783,\"desert\"],[36.11762,-115.27749,\"desert\"],[36.11762,-115.27716,\"desert\"],[36.11762,-115.27683,\"desert\"],[36.11762,-115.27649,\"desert\"],[36.11762,-115.27616,\"desert\"],[36.11762,-115.27583,\"desert\"],[36.11762,-115.27549,\"desert\"],[36.11789,-115.27816,\"desert\"],[36.11789,-115.27783,\"desert\"],[36.11789,-115.27749,\"desert\"],[36.11789,-115.27716,\"desert\"],[36.11816,-115.27449,\"desert\"],[36.11843,-115.27449,\"desert\"],[36.11871,-115.27449,\"desert\"],[36.12233,-115.27713,\"park\"],[36.12233,-115.27681,\"park\"],[36.12242,-115.27744,\"park\"],[36.12263,-115.27644,\"park\"],[36.12274,-115.27569,\"park\"],[36.12277,-115.27591,\"park\"],[36.12279,-115.27625,\"park\"],[36.1229,-115.27616,\"park\"],[36.12298,-115.27546,\"park\"],[36.12305,-115.27605,\"park\"],[36.12305,-115.2757,\"park\"],[36.12308,-115.27582,\"park\"],[36.12319,-115.27649,\"park\"],[36.12327,-115.27651,\"park\"]],\"882986a369fffff\":[[36.12358,-115.27661,\"park\"],[36.12385,-115.27656,\"park\"],[36.12399,-115.27518,\"park\"],[36.124,-115.27513,\"park\"],[36.12409,-115.27633,\"park\"],[36.1241,-115.27654,\"park\"],[36.12416,-115.2751,\"park\"],[36.12419,-115.27517,\"park\"],[36.12434,-115.27382,\"park\"],[36.12438,-115.27657,\"park\"],[36.12444,-115.27382,\"park\"],[36.12465,-115.27663,\"park\"],[36.12468,-115.27402,\"park\"],[36.12477,-115.27665,\"park\"],[36.12481,-115.27684,\"park\"],[36.1249,-115.27754,\"park\"],[36.12494,-115.27717,\"park\"],[36.12496,-115.27398,\"park\"],[36.12497,-115.27772,\"park\"],[36.12517,-115.27523,\"park\"],[36.12517,-115.27512,\"park\"],[36.12519,-115.27618,\"park\"],[36.1252,-115.27554,\"park\"],[36.12521,-115.27591,\"park\"],[36.12523,-115.27774,\"park\"],[36.12525,-115.27521,\"park\"],[36.12526,-115.27408,\"park\"],[36.12546,-115.2738,\"park\"],[36.12631,-115.27215,\"desert\"],[36.12631,-115.27182,\"desert\"],[36.12631,-115.27149,\"desert\"],[36.12631,-115.27115,\"desert\"],[36.12658,-115.27215,\"desert\"],[36.12658,-115.27182,\"desert\"],[36.12658,-115.27149,\"desert\"],[36.12658,-115.27115,\"desert\"],[36.12658,-115.27082,\"desert\"],[36.12685,-115.27215,\"desert\"],[36.12685,-115.27182,\"desert\"],[36.12685,-115.27149,\"desert\"],[36.12685,-115.27115,\"desert\"],[36.12685,-115.27082,\"desert\"],[36.12712,-115.27215,\"desert\"],[36.12712,-115.27182,\"desert\"],[36.12712,-115.27149,\"desert\"],[36.12712,-115.27115,\"desert\"],[36.12712,-115.27082,\"desert\"],[36.12739,-115.27215,\"desert\"],[36.12739,-115.27182,\"desert\"],[36.12739,-115.27149,\"desert\"],[36.12739,-115.27115,\"desert\"],[36.12739,-115.27082,\"desert\"],[36.12758,-115.27565,\"park\"],[36.1276,-115.27699,\"park\"],[36.12761,-115.27544,\"park\"],[36.12763,-115.27626,\"park\"],[36.12764,-115.27475,\"park\"],[36.12766,-115.27515,\"park\"],[36.12766,-115.27249,\"desert\"],[36.12766,-115.27215,\"desert\"],[36.12766,-115.27182,\"desert\"],[36.12766,-115.27149,\"desert\"],[36.12766,-115.27115,\"desert\"],[36.12767,-115.27578,\"park\"],[36.12777,-115.27716,\"park\"],[36.12779,-115.27651,\"park\"],[36.1278,-115.27615,\"park\"],[36.12785,-115.27739,\"park\"],[36.12792,-115.27629,\"park\"],[36.12793,-115.27349,\"desert\"],[36.12793,-115.27315,\"desert\"],[36.12793,-115.27282,\"desert\"],[36.12793,-115.27249,\"desert\"],[36.12793,-115.27215,\"desert\"],[36.12793,-115.27182,\"desert\"],[36.12793,-115.27149,\"desert\"],[36.12793,-115.27115,\"desert\"],[36.12795,-115.27603,\"park\"],[36.12801,-115.27617,\"park\"],[36.12821,-115.27349,\"desert\"],[36.12821,-115.27315,\"desert\"],[36.12821,-115.27282,\"desert\"],[36.12821,-115.27249,\"desert\"],[36.12821,-115.27215,\"desert\"],[36.12821,-115.27182,\"desert\"],[36.12821,-115.27149,\"desert\"],[36.12821,-115.27115,\"desert\"],[36.12848,-115.27382,\"desert\"],[36.12848,-115.27349,\"desert\"],[36.12848,-115.27315,\"desert\"],[36.12848,-115.27282,\"desert\"],[36.12848,-115.27249,\"desert\"],[36.12848,-115.27215,\"desert\"],[36.12848,-115.27182,\"desert\"],[36.12848,-115.27149,\"desert\"],[36.12848,-115.27115,\"desert\"],[36.12875,-115.27382,\"desert\"],[36.12875,-115.27349,\"desert\"],[36.12875,-115.27315,\"desert\"],[36.12875,-115.27282,\"desert\"],[36.12875,-115.27249,\"desert\"],[36.12875,-115.27215,\"desert\"],[36.12875,-115.27182,\"desert\"],[36.12875,-115.27149,\"desert\"],[36.12875,-115.27115,\"desert\"]],\"882986ba97fffff\":[[36.11762,-115.27149,\"desert\"],[36.11762,-115.27115,\"desert\"],[36.11816,-115.27349,\"desert\"],[36.11816,-115.27315,\"desert\"],[36.11843,-115.27416,\"desert\"],[36.11843,-115.27382,\"desert\"],[36.11843,-115.27349,\"desert\"],[36.11843,-115.27315,\"desert\"],[36.11871,-115.27416,\"desert\"],[36.11871,-115.27382,\"desert\"],[36.11871,-115.27349,\"desert\"],[36.11871,-115.27315,\"desert\"],[36.11898,-115.27416,\"desert\"],[36.11898,-115.27382,\"desert\"],[36.11898,-115.27349,\"desert\"],[36.11898,-115.27315,\"desert\"],[36.11925,-115.27416,\"desert\"],[36.11925,-115.27382,\"desert\"],[36.11925,-115.27349,\"desert\"],[36.11925,-115.27315,\"desert\"],[36.11952,-115.27416,\"desert\"],[36.11952,-115.27382,\"desert\"],[36.11952,-115.27349,\"desert\"],[36.11952,-115.27315,\"desert\"],[36.11979,-115.27416,\"desert\"],[36.11979,-115.27382,\"desert\"],[36.11979,-115.27349,\"desert\"],[36.11979,-115.27315,\"desert\"],[36.12006,-115.27449,\"desert\"],[36.12006,-115.27416,\"desert\"],[36.12006,-115.27382,\"desert\"],[36.12006,-115.27349,\"desert\"],[36.12006,-115.27315,\"desert\"],[36.12033,-115.27449,\"desert\"],[36.12033,-115.27416,\"desert\"],[36.12033,-115.27382,\"desert\"],[36.12033,-115.27349,\"desert\"],[36.12033,-115.27315,\"desert\"],[36.12061,-115.27416,\"desert\"],[36.12061,-115.27382,\"desert\"],[36.12061,-115.27349,\"desert\"],[36.12061,-115.27315,\"desert\"],[36.12061,-115.27282,\"desert\"],[36.12088,-115.27349,\"desert\"],[36.12088,-115.27315,\"desert\"],[36.12088,-115.27282,\"desert\"],[36.12088,-115.27249,\"desert\"],[36.12088,-115.27215,\"desert\"],[36.12088,-115.27182,\"desert\"],[36.12088,-115.27149,\"desert\"],[36.12088,-115.27115,\"desert\"],[36.12115,-115.27282,\"desert\"],[36.12115,-115.27249,\"desert\"],[36.12115,-115.27215,\"desert\"],[36.12115,-115.27182,\"desert\"],[36.12115,-115.27149,\"desert\"],[36.12115,-115.27115,\"desert\"],[36.12142,-115.27215,\"desert\"],[36.12142,-115.27182,\"desert\"],[36.12142,-115.27149,\"desert\"],[36.12266,-115.27518,\"park\"],[36.12293,-115.2748,\"park\"],[36.12294,-115.27509,\"park\"],[36.12296,-115.27446,\"park\"],[36.12305,-115.27442,\"park\"],[36.12314,-115.27518,\"park\"],[36.12332,-115.2741,\"park\"],[36.12334,-115.27406,\"park\"],[36.12373,-115.27386,\"park\"],[36.12388,-115.27372,\"park\"],[36.12398,-115.27383,\"park\"],[36.12631,-115.27082,\"desert\"]],\"882986baabfffff\":[[36.11002,-115.23777,\"park\"],[36.11002,-115.23743,\"park\"],[36.11029,-115.23743,\"park\"],[36.11302,-115.22849,\"park\"],[36.11353,-115.22917,\"park\"]],\"882986baa1fffff\":[[36.11316,-115.2284,\"park\"],[36.11317,-115.22757,\"park\"],[36.11322,-115.22782,\"park\"],[36.11329,-115.22797,\"park\"],[36.1133,-115.22815,\"park\"],[36.11331,-115.22707,\"park\"],[36.11337,-115.22741,\"park\"],[36.11344,-115.22665,\"park\"],[36.11352,-115.22753,\"park\"],[36.11364,-115.22676,\"park\"],[36.11368,-115.22747,\"park\"],[36.11369,-115.22671,\"park\"],[36.11375,-115.22839,\"park\"],[36.11382,-115.22723,\"park\"],[36.11384,-115.22837,\"park\"]],\"882986b85bfffff\":[[36.10128,-115.21424,\"park\"],[36.10129,-115.20984,\"park\"],[36.10131,-115.21012,\"park\"],[36.10133,-115.21121,\"park\"],[36.10137,-115.21061,\"park\"],[36.10141,-115.21097,\"park\"],[36.1015,-115.21195,\"park\"],[36.10155,-115.21349,\"park\"],[36.10159,-115.21331,\"park\"],[36.1016,-115.21286,\"park\"],[36.10161,-115.21294,\"park\"],[36.10164,-115.21343,\"park\"],[36.10221,-115.20873,\"park\"],[36.10223,-115.21012,\"park\"],[36.10236,-115.21092,\"park\"],[36.10236,-115.21047,\"park\"],[36.1024,-115.21311,\"park\"],[36.10242,-115.21302,\"park\"],[36.10244,-115.21208,\"park\"],[36.10246,-115.21111,\"park\"],[36.10249,-115.21194,\"park\"],[36.10291,-115.2098,\"park\"],[36.10297,-115.20973,\"park\"],[36.10308,-115.20938,\"park\"],[36.1032,-115.20953,\"park\"],[36.10331,-115.21037,\"park\"],[36.10339,-115.21178,\"park\"],[36.1034,-115.21002,\"park\"],[36.10345,-115.2107,\"park\"],[36.10349,-115.21075,\"park\"],[36.1035,-115.20962,\"park\"],[36.10352,-115.21202,\"park\"],[36.10376,-115.21102,\"park\"],[36.10378,-115.21137,\"park\"],[36.10379,-115.21204,\"park\"]],\"882986b813fffff\":[[36.13228,-115.20505,\"park\"],[36.13228,-115.20472,\"park\"],[36.13228,-115.20438,\"park\"],[36.13228,-115.20405,\"park\"],[36.13255,-115.20505,\"park\"],[36.13255,-115.20472,\"park\"],[36.13255,-115.20438,\"park\"],[36.13255,-115.20405,\"park\"],[36.13282,-115.20505,\"park\"],[36.13282,-115.20472,\"park\"],[36.13282,-115.20438,\"park\"],[36.13282,-115.20405,\"park\"],[36.13309,-115.20505,\"park\"],[36.13309,-115.20472,\"park\"],[36.13309,-115.20438,\"park\"],[36.13309,-115.20405,\"park\"]],\"882986a327fffff\":[[36.16866,-115.26446,\"park\"],[36.16889,-115.26455,\"park\"],[36.1689,-115.26484,\"park\"],[36.16892,-115.26489,\"park\"],[36.16906,-115.26517,\"park\"],[36.16907,-115.26448,\"park\"],[36.16912,-115.26443,\"park\"],[36.16918,-115.26554,\"park\"],[36.16924,-115.26558,\"park\"],[36.16934,-115.26513,\"park\"],[36.16935,-115.26622,\"park\"],[36.1694,-115.26433,\"park\"],[36.16941,-115.26533,\"park\"],[36.16945,-115.266,\"park\"],[36.16947,-115.26532,\"park\"],[36.16952,-115.26486,\"park\"],[36.16956,-115.26595,\"park\"],[36.16964,-115.26446,\"park\"],[36.16968,-115.26471,\"park\"],[36.16975,-115.26602,\"park\"],[36.16982,-115.26621,\"park\"]],\"882986b8d1fffff\":[[36.14826,-115.2488,\"park\"],[36.14953,-115.24844,\"park\"],[36.14953,-115.24747,\"park\"],[36.15026,-115.24846,\"park\"],[36.15043,-115.24835,\"park\"],[36.15062,-115.24808,\"park\"],[36.15081,-115.24808,\"park\"],[36.1509,-115.24824,\"park\"],[36.15126,-115.24824,\"park\"]],\"882986b8a9fffff\":[[36.16159,-115.19637,\"park\"],[36.16159,-115.19604,\"park\"],[36.16186,-115.19637,\"park\"],[36.16186,-115.19604,\"park\"],[36.16213,-115.19637,\"park\"],[36.16213,-115.19604,\"park\"],[36.1624,-115.19637,\"park\"],[36.1624,-115.19604,\"park\"],[36.16267,-115.20138,\"park\"],[36.16294,-115.20171,\"park\"],[36.16294,-115.20138,\"park\"],[36.16294,-115.20105,\"park\"],[36.16322,-115.20138,\"park\"]],\"882986b99bfffff\":[[36.17777,-115.18173,\"park\"],[36.17789,-115.18177,\"park\"],[36.17796,-115.18443,\"park\"],[36.17827,-115.18661,\"park\"],[36.17832,-115.18234,\"park\"],[36.17836,-115.18634,\"park\"],[36.17843,-115.18655,\"park\"],[36.17846,-115.18252,\"park\"],[36.17848,-115.18548,\"park\"],[36.17849,-115.1871,\"park\"],[36.17856,-115.18633,\"park\"],[36.17865,-115.18274,\"park\"],[36.17868,-115.18689,\"park\"],[36.17869,-115.18647,\"park\"],[36.17871,-115.18722,\"park\"],[36.17875,-115.18538,\"park\"],[36.17882,-115.18253,\"park\"],[36.17889,-115.18302,\"park\"],[36.17894,-115.18572,\"park\"],[36.17894,-115.18523,\"park\"],[36.17895,-115.18675,\"park\"],[36.17895,-115.18616,\"park\"],[36.17898,-115.186,\"park\"],[36.17899,-115.18669,\"park\"],[36.17904,-115.18627,\"park\"],[36.17905,-115.18652,\"park\"],[36.17905,-115.1844,\"park\"],[36.17906,-115.18507,\"park\"],[36.17911,-115.18502,\"park\"],[36.17924,-115.18452,\"park\"],[36.17925,-115.18578,\"park\"],[36.17926,-115.18564,\"park\"],[36.17927,-115.1853,\"park\"],[36.17944,-115.18656,\"park\"],[36.17946,-115.185,\"park\"],[36.17947,-115.18741,\"park\"],[36.17951,-115.1874,\"park\"],[36.17955,-115.18551,\"park\"],[36.17959,-115.18477,\"park\"],[36.17967,-115.18662,\"park\"],[36.17968,-115.18431,\"park\"],[36.1797,-115.18438,\"park\"],[36.17972,-115.18503,\"park\"],[36.17974,-115.18706,\"park\"],[36.17977,-115.18698,\"park\"],[36.1798,-115.18729,\"park\"],[36.1798,-115.18547,\"park\"],[36.17981,-115.18501,\"park\"],[36.17987,-115.18468,\"park\"],[36.17988,-115.18669,\"park\"],[36.1799,-115.18406,\"park\"],[36.17994,-115.18503,\"park\"],[36.18,-115.18665,\"park\"],[36.18002,-115.184,\"park\"],[36.18005,-115.18551,\"park\"],[36.18006,-115.18693,\"park\"],[36.18006,-115.18507,\"park\"],[36.18007,-115.18447,\"park\"],[36.1801,-115.18396,\"park\"],[36.18011,-115.18491,\"park\"],[36.18011,-115.18435,\"park\"],[36.18021,-115.18193,\"park\"],[36.18028,-115.18604,\"park\"],[36.18031,-115.18571,\"park\"],[36.18032,-115.18576,\"park\"],[36.18036,-115.18508,\"park\"],[36.18037,-115.18247,\"park\"],[36.18039,-115.18421,\"park\"],[36.1804,-115.18373,\"park\"],[36.18048,-115.185,\"park\"],[36.18057,-115.18452,\"park\"],[36.18059,-115.18726,\"park\"],[36.18061,-115.18478,\"park\"],[36.18064,-115.18747,\"park\"],[36.18069,-115.18413,\"park\"],[36.18074,-115.18674,\"park\"],[36.18082,-115.18396,\"park\"],[36.18089,-115.18762,\"park\"],[36.18092,-115.18717,\"park\"]],\"882986b8a5fffff\":[[36.16946,-115.18836,\"desert\"],[36.16946,-115.18803,\"desert\"],[36.16946,-115.18769,\"desert\"],[36.16946,-115.18736,\"desert\"],[36.16973,-115.18836,\"desert\"],[36.16973,-115.18803,\"desert\"],[36.16973,-115.18769,\"desert\"],[36.16973,-115.18736,\"desert\"],[36.17,-115.18836,\"desert\"],[36.17,-115.18803,\"desert\"],[36.17,-115.18769,\"desert\"],[36.17,-115.18736,\"desert\"],[36.17027,-115.18803,\"desert\"],[36.17027,-115.18769,\"desert\"],[36.17041,-115.18835,\"desert\"],[36.1705,-115.1884,\"desert\"],[36.17054,-115.18803,\"desert\"],[36.1779,-115.18627,\"park\"],[36.17811,-115.18703,\"park\"],[36.17812,-115.18689,\"park\"],[36.17812,-115.18663,\"park\"],[36.17814,-115.18722,\"park\"]],\"882986a169fffff\":[[36.18373,-115.25445,\"park\"],[36.184,-115.2547,\"park\"],[36.18408,-115.25483,\"park\"],[36.18432,-115.25495,\"park\"],[36.18458,-115.25491,\"park\"],[36.1847,-115.25482,\"park\"],[36.18492,-115.25457,\"park\"],[36.18494,-115.25448,\"park\"],[36.18497,-115.25386,\"park\"],[36.18501,-115.25417,\"park\"],[36.18537,-115.25475,\"park\"],[36.18547,-115.25475,\"park\"],[36.18584,-115.25471,\"park\"],[36.1861,-115.25437,\"park\"],[36.18643,-115.25389,\"park\"],[36.18647,-115.25488,\"park\"],[36.18651,-115.25422,\"park\"],[36.18653,-115.25475,\"park\"],[36.18657,-115.25456,\"park\"],[36.18657,-115.25444,\"park\"],[36.18966,-115.24755,\"park\"]],\"882986a161fffff\":[[36.18976,-115.24743,\"park\"],[36.1928,-115.24578,\"desert\"],[36.19307,-115.24578,\"desert\"]],\"882986a323fffff\":[[36.17035,-115.28012,\"park\"],[36.17041,-115.28044,\"park\"]],\"882986a149fffff\":[[36.17057,-115.27852,\"park\"],[36.17065,-115.27959,\"park\"],[36.17065,-115.27908,\"park\"],[36.1707,-115.2795,\"park\"],[36.17073,-115.27987,\"park\"]],\"882986bb31fffff\":[[36.08831,-115.1226,\"park\"],[36.08858,-115.1226,\"park\"],[36.08885,-115.1226,\"park\"]],\"882986aa49fffff\":[[36.18623,-115.20266,\"park\"],[36.18623,-115.20236,\"park\"],[36.18629,-115.20235,\"park\"],[36.18637,-115.20245,\"park\"],[36.18645,-115.20273,\"park\"],[36.18658,-115.20247,\"park\"]],\"882986b893fffff\":[[36.1787,-115.24982,\"park\"],[36.1792,-115.2497,\"park\"],[36.17923,-115.24967,\"park\"]],\"882986b897fffff\":[[36.17884,-115.24892,\"park\"],[36.17897,-115.24818,\"park\"],[36.17902,-115.24585,\"park\"],[36.17926,-115.24821,\"park\"]],\"882986b953fffff\":[[36.12088,-115.12927,\"park\"],[36.12088,-115.12894,\"park\"],[36.12115,-115.12927,\"park\"],[36.12115,-115.12894,\"park\"],[36.12142,-115.12927,\"park\"],[36.12142,-115.12894,\"park\"],[36.12169,-115.12927,\"park\"],[36.12169,-115.12894,\"park\"]],\"882986b8bbfffff\":[[36.18466,-115.23076,\"park\"]],\"882986b9d9fffff\":[[36.15094,-115.18059,\"park\"]],\"882986ab4bfffff\":[[36.2042,-115.1286,\"park\"],[36.20447,-115.1286,\"park\"],[36.20474,-115.1286,\"park\"],[36.20501,-115.1286,\"park\"],[36.20528,-115.12894,\"park\"],[36.20528,-115.1286,\"park\"]],\"882986aa3dfffff\":[[36.23269,-115.19036,\"park\"],[36.23269,-115.19003,\"park\"],[36.23296,-115.19003,\"park\"],[36.23324,-115.1907,\"park\"],[36.23351,-115.1907,\"park\"],[36.23351,-115.19036,\"park\"],[36.23378,-115.19103,\"park\"],[36.23378,-115.1907,\"park\"],[36.23378,-115.19036,\"park\"],[36.23405,-115.19103,\"park\"],[36.23405,-115.1907,\"park\"],[36.23405,-115.19036,\"park\"],[36.23432,-115.19103,\"park\"],[36.23432,-115.1907,\"park\"],[36.23432,-115.19036,\"park\"],[36.23459,-115.19036,\"park\"],[36.23812,-115.18803,\"desert\"],[36.23812,-115.18769,\"desert\"],[36.23812,-115.18736,\"desert\"]],\"882986b9ebfffff\":[[36.15616,-115.13695,\"park\"],[36.15643,-115.13695,\"park\"]],\"882986b82dfffff\":[[36.12065,-115.1403,\"park\"],[36.12074,-115.14027,\"park\"],[36.12078,-115.14132,\"park\"],[36.12094,-115.13989,\"park\"],[36.12114,-115.14143,\"park\"],[36.12138,-115.1414,\"park\"],[36.12148,-115.13984,\"park\"],[36.12159,-115.13998,\"park\"]],\"882986b917fffff\":[[36.14637,-115.11838,\"park\"],[36.14641,-115.11725,\"park\"],[36.14643,-115.11788,\"park\"],[36.14644,-115.11796,\"park\"],[36.14646,-115.11726,\"park\"],[36.14662,-115.1184,\"park\"],[36.14666,-115.11792,\"park\"],[36.14666,-115.11759,\"park\"],[36.14671,-115.11801,\"park\"],[36.14675,-115.11724,\"park\"],[36.14691,-115.11836,\"park\"],[36.14693,-115.11692,\"park\"],[36.14695,-115.11799,\"park\"],[36.14696,-115.11724,\"park\"],[36.14705,-115.1176,\"park\"],[36.14705,-115.11758,\"park\"],[36.14716,-115.11837,\"park\"],[36.1472,-115.11759,\"park\"],[36.1472,-115.11692,\"park\"],[36.1472,-115.11659,\"park\"],[36.14723,-115.11725,\"park\"],[36.14732,-115.11785,\"park\"],[36.14743,-115.11807,\"park\"],[36.14746,-115.11843,\"park\"],[36.14748,-115.11799,\"park\"],[36.14758,-115.11761,\"park\"],[36.14758,-115.11518,\"park\"],[36.14759,-115.11584,\"park\"],[36.1476,-115.11539,\"park\"],[36.14763,-115.11619,\"park\"],[36.14766,-115.11726,\"park\"],[36.14771,-115.11725,\"park\"],[36.14773,-115.11831,\"park\"],[36.14774,-115.11685,\"park\"],[36.14774,-115.11644,\"park\"],[36.14775,-115.11693,\"park\"],[36.14775,-115.11625,\"park\"],[36.14777,-115.11673,\"park\"],[36.14777,-115.11658,\"park\"],[36.14781,-115.11796,\"park\"]],\"882986ab39fffff\":[[36.24491,-115.12961,\"park\"]],\"88298616d3fffff\":[[36.17753,-115.10352,\"park\"],[36.17757,-115.10471,\"park\"],[36.1776,-115.10349,\"park\"],[36.17785,-115.10493,\"park\"],[36.17785,-115.10488,\"park\"],[36.17796,-115.10354,\"park\"],[36.178,-115.10518,\"park\"],[36.1781,-115.10359,\"park\"],[36.17812,-115.10488,\"park\"],[36.17823,-115.10478,\"park\"],[36.17825,-115.10363,\"park\"],[36.17858,-115.10365,\"park\"],[36.1786,-115.1047,\"park\"],[36.17884,-115.10368,\"park\"],[36.17885,-115.1047,\"park\"],[36.17888,-115.10491,\"park\"],[36.17901,-115.10502,\"park\"],[36.1791,-115.10468,\"park\"],[36.17915,-115.10524,\"park\"],[36.1792,-115.10562,\"park\"],[36.17945,-115.10626,\"park\"],[36.17948,-115.10476,\"park\"],[36.17959,-115.10629,\"park\"],[36.17972,-115.106,\"park\"],[36.17973,-115.10472,\"park\"],[36.17988,-115.10635,\"park\"],[36.17989,-115.10595,\"park\"],[36.17998,-115.10565,\"park\"],[36.18013,-115.10578,\"park\"],[36.18017,-115.10651,\"park\"],[36.18027,-115.10614,\"park\"],[36.18036,-115.10534,\"park\"],[36.18038,-115.10639,\"park\"],[36.18043,-115.10621,\"park\"],[36.18059,-115.10602,\"park\"],[36.18069,-115.10532,\"park\"]],\"882986b903fffff\":[[36.13204,-115.10889,\"park\"],[36.13204,-115.10852,\"park\"],[36.13204,-115.10822,\"park\"],[36.13228,-115.10808,\"park\"],[36.13239,-115.10869,\"park\"],[36.13239,-115.1084,\"park\"],[36.13274,-115.10864,\"park\"],[36.13285,-115.10839,\"park\"]],\"882986b9b7fffff\":[[36.20365,-115.14196,\"park\"],[36.20365,-115.14162,\"park\"],[36.20365,-115.14129,\"park\"],[36.20392,-115.14196,\"park\"],[36.20392,-115.14162,\"park\"],[36.20392,-115.14129,\"park\"],[36.20392,-115.14096,\"park\"],[36.2042,-115.14196,\"park\"],[36.2042,-115.14162,\"park\"],[36.2042,-115.14129,\"park\"],[36.2042,-115.14096,\"park\"]],\"882986ba83fffff\":[[36.10703,-115.25339,\"park\"],[36.1071,-115.25345,\"park\"],[36.10753,-115.25375,\"park\"],[36.10756,-115.25395,\"park\"]],\"882986a32dfffff\":[[36.15226,-115.26347,\"park\"],[36.15238,-115.26334,\"park\"],[36.15244,-115.26312,\"park\"],[36.15284,-115.26235,\"park\"],[36.15288,-115.26278,\"park\"],[36.1529,-115.26298,\"park\"],[36.15291,-115.2627,\"park\"],[36.15317,-115.26299,\"park\"],[36.15323,-115.26313,\"park\"],[36.15344,-115.26316,\"park\"],[36.15358,-115.26252,\"park\"],[36.15359,-115.26236,\"park\"],[36.15363,-115.26328,\"park\"],[36.15365,-115.26281,\"park\"],[36.15446,-115.26288,\"park\"],[36.15526,-115.26468,\"park\"],[36.1555,-115.26576,\"park\"],[36.1555,-115.26484,\"park\"],[36.1556,-115.26476,\"park\"],[36.15562,-115.26524,\"park\"],[36.15564,-115.26481,\"park\"],[36.15568,-115.26512,\"park\"],[36.15636,-115.26555,\"park\"],[36.15636,-115.26402,\"park\"]],\"882986b9e5fffff\":[[36.16593,-115.11325,\"park\"],[36.16593,-115.11291,\"park\"],[36.16593,-115.11258,\"park\"]],\"882986ba95fffff\":[[36.115,-115.25939,\"park\"],[36.11503,-115.25812,\"park\"],[36.11506,-115.26005,\"park\"],[36.11506,-115.25842,\"park\"],[36.11508,-115.25969,\"park\"],[36.11508,-115.2585,\"park\"],[36.11511,-115.25901,\"park\"],[36.11517,-115.26041,\"park\"],[36.11519,-115.25785,\"park\"],[36.11525,-115.25844,\"park\"],[36.11531,-115.25848,\"park\"],[36.1154,-115.26044,\"park\"],[36.11544,-115.25994,\"park\"],[36.11546,-115.26013,\"park\"],[36.1155,-115.25915,\"park\"],[36.11552,-115.25955,\"park\"],[36.11554,-115.26022,\"park\"],[36.11568,-115.25905,\"park\"],[36.11571,-115.2605,\"park\"],[36.11572,-115.25879,\"park\"],[36.11572,-115.25712,\"park\"],[36.11575,-115.25712,\"park\"],[36.11577,-115.25898,\"park\"],[36.11578,-115.26023,\"park\"],[36.11578,-115.25869,\"park\"],[36.11592,-115.26066,\"park\"],[36.11595,-115.25733,\"park\"],[36.11611,-115.25722,\"park\"],[36.11614,-115.25759,\"park\"]],\"882986bab9fffff\":[[36.11816,-115.24778,\"park\"],[36.11816,-115.24745,\"park\"],[36.11843,-115.24845,\"park\"],[36.11843,-115.24812,\"park\"],[36.11843,-115.24778,\"park\"],[36.11871,-115.24912,\"park\"],[36.11871,-115.24878,\"park\"],[36.11871,-115.24845,\"park\"],[36.11871,-115.24812,\"park\"],[36.11871,-115.24778,\"park\"]],\"882986ab19fffff\":[[36.23622,-115.15231,\"park\"]],\"882986a84dfffff\":[[36.24925,-115.16666,\"park\"],[36.24925,-115.16633,\"park\"],[36.24952,-115.16666,\"park\"],[36.24952,-115.16633,\"park\"]],\"882986aa37fffff\":[[36.24735,-115.19771,\"desert\"],[36.24735,-115.19737,\"desert\"],[36.24735,-115.19704,\"desert\"],[36.24735,-115.19671,\"desert\"],[36.24735,-115.19637,\"desert\"],[36.24735,-115.19604,\"desert\"],[36.24735,-115.19571,\"desert\"],[36.24735,-115.19537,\"desert\"],[36.24735,-115.19504,\"desert\"],[36.24735,-115.1947,\"desert\"],[36.24762,-115.19771,\"desert\"],[36.24762,-115.19737,\"desert\"],[36.24762,-115.19704,\"desert\"],[36.24762,-115.19671,\"desert\"],[36.24762,-115.19637,\"desert\"],[36.24762,-115.19604,\"desert\"],[36.24762,-115.19571,\"desert\"],[36.24762,-115.19537,\"desert\"],[36.24762,-115.19504,\"desert\"],[36.24762,-115.1947,\"desert\"],[36.24789,-115.19771,\"desert\"],[36.24789,-115.19737,\"desert\"],[36.24789,-115.19704,\"desert\"],[36.24789,-115.19671,\"desert\"],[36.24789,-115.19637,\"desert\"],[36.24789,-115.19604,\"desert\"],[36.24789,-115.19571,\"desert\"],[36.24789,-115.19537,\"desert\"],[36.24789,-115.19504,\"desert\"],[36.24789,-115.1947,\"desert\"],[36.24789,-115.18869,\"desert\"],[36.24789,-115.18836,\"desert\"],[36.24789,-115.18803,\"desert\"],[36.24789,-115.18769,\"desert\"],[36.24816,-115.19771,\"desert\"],[36.24816,-115.19737,\"desert\"],[36.24816,-115.19704,\"desert\"],[36.24816,-115.19671,\"desert\"],[36.24816,-115.19637,\"desert\"],[36.24816,-115.19604,\"desert\"],[36.24816,-115.19571,\"desert\"],[36.24816,-115.19537,\"desert\"],[36.24816,-115.19504,\"desert\"],[36.24816,-115.1947,\"desert\"],[36.24816,-115.18836,\"desert\"],[36.24816,-115.18803,\"desert\"],[36.24843,-115.19771,\"desert\"],[36.24843,-115.19737,\"desert\"],[36.24843,-115.19704,\"desert\"],[36.24843,-115.19671,\"desert\"],[36.24843,-115.19637,\"desert\"],[36.24843,-115.19604,\"desert\"],[36.24843,-115.19571,\"desert\"],[36.24843,-115.19537,\"desert\"],[36.24843,-115.19504,\"desert\"],[36.24843,-115.1947,\"desert\"],[36.2487,-115.19771,\"desert\"],[36.2487,-115.19737,\"desert\"],[36.2487,-115.19704,\"desert\"],[36.2487,-115.19671,\"desert\"],[36.2487,-115.19637,\"desert\"],[36.2487,-115.19604,\"desert\"],[36.2487,-115.19571,\"desert\"],[36.2487,-115.19537,\"desert\"],[36.2487,-115.19504,\"desert\"],[36.2487,-115.1947,\"desert\"],[36.24898,-115.19771,\"desert\"],[36.24898,-115.19737,\"desert\"],[36.24898,-115.19704,\"desert\"],[36.24898,-115.19671,\"desert\"],[36.24898,-115.19637,\"desert\"],[36.24898,-115.19604,\"desert\"],[36.24898,-115.19571,\"desert\"],[36.24898,-115.19537,\"desert\"],[36.24898,-115.19504,\"desert\"],[36.24898,-115.1947,\"desert\"],[36.24925,-115.19771,\"desert\"],[36.24925,-115.19737,\"desert\"],[36.24925,-115.19704,\"desert\"],[36.24925,-115.19671,\"desert\"],[36.24925,-115.19637,\"desert\"],[36.24925,-115.19604,\"desert\"],[36.24925,-115.19571,\"desert\"],[36.24925,-115.19537,\"desert\"],[36.24925,-115.19504,\"desert\"],[36.24925,-115.1947,\"desert\"],[36.24925,-115.19437,\"desert\"],[36.24952,-115.19771,\"desert\"],[36.24952,-115.19737,\"desert\"],[36.24952,-115.19704,\"desert\"],[36.24952,-115.19671,\"desert\"],[36.24952,-115.19637,\"desert\"],[36.24952,-115.19604,\"desert\"],[36.24952,-115.19571,\"desert\"],[36.24952,-115.19537,\"desert\"],[36.24952,-115.19504,\"desert\"],[36.24952,-115.1947,\"desert\"],[36.24952,-115.19437,\"desert\"],[36.24979,-115.19771,\"desert\"],[36.24979,-115.19737,\"desert\"],[36.24979,-115.19704,\"desert\"],[36.24979,-115.19671,\"desert\"],[36.24979,-115.19637,\"desert\"],[36.24979,-115.19604,\"desert\"],[36.24979,-115.19571,\"desert\"],[36.24979,-115.19537,\"desert\"],[36.24979,-115.19504,\"desert\"],[36.24979,-115.1947,\"desert\"],[36.24979,-115.19437,\"desert\"],[36.25278,-115.1927,\"park\"],[36.25278,-115.19237,\"park\"],[36.25278,-115.19203,\"park\"],[36.25305,-115.1927,\"park\"],[36.25305,-115.19237,\"park\"],[36.25305,-115.19203,\"park\"],[36.25332,-115.1927,\"park\"],[36.25332,-115.19237,\"park\"],[36.25332,-115.19203,\"park\"],[36.25359,-115.1927,\"park\"]],\"882986a85dfffff\":[[36.25359,-115.19237,\"park\"],[36.25359,-115.19203,\"park\"]],\"882986baddfffff\":[[36.0818,-115.26982,\"desert\"],[36.0818,-115.26481,\"desert\"],[36.0818,-115.26447,\"desert\"],[36.0818,-115.26414,\"desert\"],[36.0818,-115.26381,\"desert\"],[36.08207,-115.26915,\"desert\"],[36.08207,-115.26748,\"desert\"],[36.08207,-115.26481,\"desert\"],[36.08207,-115.26447,\"desert\"],[36.08207,-115.26414,\"desert\"],[36.08207,-115.26381,\"desert\"],[36.08234,-115.26915,\"desert\"],[36.08234,-115.26748,\"desert\"],[36.08234,-115.26481,\"desert\"],[36.08234,-115.26447,\"desert\"],[36.08234,-115.26414,\"desert\"],[36.08234,-115.26381,\"desert\"],[36.08261,-115.26481,\"desert\"],[36.08261,-115.26447,\"desert\"],[36.08261,-115.26414,\"desert\"],[36.08261,-115.26381,\"desert\"],[36.0837,-115.27149,\"desert\"],[36.0837,-115.27115,\"desert\"],[36.0837,-115.27082,\"desert\"],[36.08397,-115.27149,\"desert\"],[36.08397,-115.27115,\"desert\"],[36.08397,-115.27082,\"desert\"],[36.08424,-115.27182,\"desert\"],[36.08424,-115.27149,\"desert\"],[36.08424,-115.27115,\"desert\"],[36.08424,-115.27082,\"desert\"],[36.08451,-115.27182,\"desert\"],[36.08451,-115.27149,\"desert\"],[36.08451,-115.27115,\"desert\"],[36.08451,-115.27082,\"desert\"],[36.08529,-115.26313,\"park\"],[36.08531,-115.26419,\"park\"],[36.08535,-115.26498,\"park\"],[36.08535,-115.26246,\"park\"],[36.08535,-115.26143,\"park\"],[36.08536,-115.26455,\"park\"],[36.08559,-115.26279,\"park\"],[36.08563,-115.26273,\"park\"],[36.08591,-115.26253,\"park\"],[36.08614,-115.26238,\"park\"],[36.08635,-115.26234,\"park\"],[36.08662,-115.26136,\"park\"],[36.08667,-115.26219,\"park\"],[36.08671,-115.26149,\"park\"],[36.08675,-115.26187,\"park\"],[36.08692,-115.26143,\"park\"]],\"882986bad5fffff\":[[36.0876,-115.26137,\"park\"],[36.0879,-115.26141,\"park\"],[36.08824,-115.26162,\"park\"],[36.08832,-115.26279,\"park\"],[36.08833,-115.26368,\"park\"],[36.08834,-115.26405,\"park\"]],\"8829861695fffff\":[[36.19985,-115.09088,\"park\"],[36.19985,-115.09055,\"park\"],[36.19985,-115.09021,\"park\"],[36.19985,-115.08988,\"park\"],[36.19985,-115.08187,\"desert\"],[36.20012,-115.09088,\"park\"],[36.20012,-115.09055,\"park\"],[36.20012,-115.09021,\"park\"],[36.20012,-115.08988,\"park\"],[36.20012,-115.08187,\"desert\"],[36.2004,-115.09055,\"park\"],[36.2004,-115.09021,\"park\"],[36.2004,-115.08988,\"park\"],[36.2004,-115.08187,\"desert\"],[36.20067,-115.09021,\"park\"],[36.20067,-115.08988,\"park\"],[36.20067,-115.08187,\"desert\"],[36.20094,-115.08988,\"park\"]],\"8829861697fffff\":[[36.2004,-115.09088,\"park\"],[36.20067,-115.09088,\"park\"],[36.20067,-115.09055,\"park\"],[36.20094,-115.09021,\"park\"],[36.20121,-115.08988,\"park\"]],\"882986b881fffff\":[[36.1624,-115.22108,\"park\"],[36.1624,-115.22074,\"park\"]],\"882986a123fffff\":[[36.22238,-115.25519,\"park\"],[36.22265,-115.25559,\"park\"],[36.22365,-115.25558,\"park\"],[36.22371,-115.25526,\"park\"],[36.22371,-115.25476,\"park\"],[36.22374,-115.25475,\"park\"],[36.22396,-115.25551,\"park\"],[36.22397,-115.25545,\"park\"],[36.224,-115.25504,\"park\"],[36.22425,-115.25506,\"park\"],[36.22426,-115.25523,\"park\"],[36.22454,-115.25519,\"park\"],[36.22469,-115.25542,\"park\"]],\"882986a129fffff\":[[36.20745,-115.24578,\"desert\"],[36.21075,-115.25331,\"park\"],[36.21088,-115.25394,\"park\"],[36.21097,-115.25369,\"park\"],[36.21098,-115.25331,\"park\"],[36.21103,-115.25381,\"park\"],[36.21106,-115.25379,\"park\"],[36.21151,-115.25334,\"park\"]],\"882986a12bfffff\":[[36.2124,-115.25437,\"park\"],[36.21259,-115.25455,\"park\"],[36.21267,-115.25439,\"park\"],[36.21285,-115.25456,\"park\"],[36.21297,-115.25474,\"park\"],[36.21343,-115.25523,\"park\"],[36.21394,-115.25558,\"park\"],[36.214,-115.2558,\"park\"],[36.21422,-115.25513,\"park\"],[36.21423,-115.25568,\"park\"],[36.21431,-115.25553,\"park\"],[36.2144,-115.25537,\"park\"],[36.21445,-115.25495,\"park\"],[36.21458,-115.25514,\"park\"],[36.21467,-115.25505,\"park\"],[36.21486,-115.25506,\"park\"],[36.2151,-115.25512,\"park\"],[36.21518,-115.25518,\"park\"],[36.21532,-115.25528,\"park\"],[36.21561,-115.25538,\"park\"],[36.21568,-115.25548,\"park\"],[36.21587,-115.25578,\"park\"],[36.2159,-115.25647,\"park\"],[36.2159,-115.25584,\"park\"],[36.21596,-115.25616,\"park\"],[36.21643,-115.2571,\"park\"],[36.21657,-115.25678,\"park\"],[36.21778,-115.25759,\"park\"]],\"882986ab61fffff\":[[36.21831,-115.10223,\"park\"],[36.21831,-115.1019,\"park\"],[36.21831,-115.10156,\"park\"],[36.21831,-115.10123,\"park\"],[36.21858,-115.10223,\"park\"],[36.21858,-115.1019,\"park\"],[36.21858,-115.10156,\"park\"],[36.21858,-115.10123,\"park\"],[36.21858,-115.1009,\"park\"],[36.21885,-115.10223,\"park\"],[36.21912,-115.10223,\"park\"],[36.21912,-115.10123,\"park\"],[36.21912,-115.1009,\"park\"],[36.21912,-115.10056,\"park\"],[36.21912,-115.10023,\"park\"],[36.21912,-115.0999,\"park\"],[36.21912,-115.09956,\"park\"],[36.21912,-115.09923,\"park\"],[36.21939,-115.10223,\"park\"],[36.21939,-115.10123,\"park\"],[36.21939,-115.1009,\"park\"],[36.21939,-115.10056,\"park\"],[36.21939,-115.10023,\"park\"],[36.21939,-115.0999,\"park\"],[36.21939,-115.09956,\"park\"],[36.21939,-115.09923,\"park\"],[36.21967,-115.10223,\"park\"],[36.21967,-115.10123,\"park\"],[36.21967,-115.1009,\"park\"],[36.21967,-115.10056,\"park\"],[36.21967,-115.10023,\"park\"],[36.21967,-115.0999,\"park\"],[36.21967,-115.09956,\"park\"],[36.21967,-115.09923,\"park\"],[36.21994,-115.10223,\"park\"],[36.21994,-115.10123,\"park\"],[36.21994,-115.1009,\"park\"],[36.21994,-115.10056,\"park\"],[36.21994,-115.10023,\"park\"],[36.21994,-115.0999,\"park\"],[36.21994,-115.09956,\"park\"],[36.21994,-115.09923,\"park\"],[36.22021,-115.10223,\"park\"],[36.22021,-115.10056,\"park\"],[36.22021,-115.10023,\"park\"],[36.22021,-115.0999,\"park\"],[36.22021,-115.09956,\"park\"],[36.22021,-115.09923,\"park\"],[36.22048,-115.10223,\"park\"],[36.22048,-115.1019,\"park\"],[36.22048,-115.10023,\"park\"],[36.22048,-115.0999,\"park\"],[36.22048,-115.09956,\"park\"],[36.22048,-115.09923,\"park\"],[36.22075,-115.10223,\"park\"],[36.22075,-115.1019,\"park\"],[36.22075,-115.10023,\"park\"],[36.22075,-115.0999,\"park\"],[36.22075,-115.09956,\"park\"],[36.22075,-115.09923,\"park\"]],\"882986ab65fffff\":[[36.22455,-115.08331,\"park\"],[36.22484,-115.08392,\"park\"]],\"88298614dbfffff\":[[36.22471,-115.08322,\"park\"],[36.22479,-115.08313,\"park\"],[36.22506,-115.08382,\"park\"],[36.22506,-115.08319,\"park\"],[36.22507,-115.08345,\"park\"],[36.22508,-115.08287,\"park\"],[36.22512,-115.0832,\"park\"],[36.22523,-115.08399,\"park\"],[36.22523,-115.08372,\"park\"],[36.22524,-115.08323,\"park\"],[36.22542,-115.08317,\"park\"],[36.22561,-115.08439,\"park\"],[36.22593,-115.08442,\"park\"]],\"882986aadbfffff\":[[36.24667,-115.28019,\"park\"],[36.24683,-115.28087,\"park\"],[36.24684,-115.28028,\"park\"],[36.24692,-115.28118,\"park\"],[36.24694,-115.28054,\"park\"],[36.24712,-115.28128,\"park\"],[36.24733,-115.28115,\"park\"],[36.24736,-115.28111,\"park\"],[36.24751,-115.28083,\"park\"],[36.2476,-115.27959,\"park\"],[36.24781,-115.28112,\"park\"],[36.24782,-115.28312,\"park\"],[36.24784,-115.28081,\"park\"],[36.24787,-115.28128,\"park\"],[36.24788,-115.28229,\"park\"],[36.24788,-115.28213,\"park\"],[36.2479,-115.28208,\"park\"],[36.24791,-115.2807,\"park\"],[36.24795,-115.28303,\"park\"],[36.24801,-115.28249,\"park\"],[36.24803,-115.28147,\"park\"],[36.24804,-115.28013,\"park\"],[36.24805,-115.28181,\"park\"],[36.24898,-115.2785,\"desert\"],[36.24898,-115.27816,\"desert\"],[36.24925,-115.2785,\"desert\"],[36.24925,-115.27816,\"desert\"],[36.24952,-115.2785,\"desert\"],[36.24952,-115.27816,\"desert\"]],\"882986a81dfffff\":[[36.27885,-115.18756,\"park\"],[36.27889,-115.18811,\"park\"],[36.27891,-115.18786,\"park\"],[36.27899,-115.18673,\"park\"],[36.27913,-115.18794,\"park\"],[36.2792,-115.18829,\"park\"],[36.27934,-115.18838,\"park\"]],\"882986aa87fffff\":[[36.26987,-115.23944,\"park\"],[36.26987,-115.2391,\"park\"],[36.26987,-115.23877,\"park\"],[36.26987,-115.23743,\"desert\"],[36.27014,-115.23743,\"desert\"]],\"882986aac9fffff\":[[36.23199,-115.25278,\"park\"],[36.23222,-115.25385,\"park\"],[36.23229,-115.25274,\"park\"],[36.23231,-115.25603,\"park\"],[36.23231,-115.25286,\"park\"],[36.23233,-115.25335,\"park\"],[36.23236,-115.25447,\"park\"],[36.23236,-115.25357,\"park\"],[36.23243,-115.25346,\"park\"],[36.23246,-115.25483,\"park\"],[36.2325,-115.25574,\"park\"],[36.2326,-115.25371,\"park\"],[36.23262,-115.25538,\"park\"],[36.23263,-115.25269,\"park\"],[36.23268,-115.25601,\"park\"],[36.23275,-115.2519,\"park\"],[36.23276,-115.25213,\"park\"]],\"882986a135fffff\":[[36.23231,-115.25851,\"park\"],[36.23242,-115.25792,\"park\"],[36.23251,-115.25877,\"park\"],[36.23263,-115.25646,\"park\"],[36.23269,-115.25663,\"park\"]],\"882986a805fffff\":[[36.27559,-115.16434,\"park\"],[36.27571,-115.16403,\"park\"],[36.27584,-115.16369,\"park\"],[36.27598,-115.16431,\"park\"],[36.27605,-115.16436,\"park\"],[36.27605,-115.16341,\"park\"],[36.27615,-115.16334,\"park\"],[36.27631,-115.1644,\"park\"],[36.27636,-115.16319,\"park\"],[36.2764,-115.16537,\"park\"],[36.27653,-115.163,\"park\"],[36.27662,-115.16288,\"park\"],[36.2767,-115.16266,\"park\"],[36.27684,-115.16243,\"park\"],[36.27693,-115.1622,\"park\"],[36.27696,-115.16199,\"park\"],[36.27704,-115.15976,\"park\"],[36.27704,-115.15941,\"park\"],[36.27705,-115.15874,\"park\"],[36.27707,-115.15908,\"park\"],[36.27753,-115.16037,\"park\"],[36.27757,-115.16017,\"park\"]],\"882986a845fffff\":[[36.25613,-115.16073,\"park\"],[36.25617,-115.16064,\"park\"],[36.25619,-115.16165,\"park\"],[36.2563,-115.16185,\"park\"],[36.25635,-115.16196,\"park\"],[36.25643,-115.16228,\"park\"],[36.25647,-115.16262,\"park\"],[36.25648,-115.16266,\"park\"],[36.25658,-115.16297,\"park\"],[36.25659,-115.1633,\"park\"],[36.25659,-115.16062,\"park\"],[36.25668,-115.16596,\"park\"],[36.25677,-115.16386,\"park\"],[36.25683,-115.16411,\"park\"],[36.25691,-115.16386,\"park\"],[36.25693,-115.1634,\"park\"],[36.25693,-115.1633,\"park\"],[36.25699,-115.16062,\"park\"],[36.25703,-115.16262,\"park\"],[36.25711,-115.16427,\"park\"],[36.2572,-115.16428,\"park\"],[36.25721,-115.16299,\"park\"],[36.25721,-115.16062,\"park\"],[36.25723,-115.16254,\"park\"],[36.25728,-115.16398,\"park\"],[36.25728,-115.16066,\"park\"],[36.2573,-115.16331,\"park\"],[36.2573,-115.16109,\"park\"],[36.25733,-115.16434,\"park\"],[36.25736,-115.16338,\"park\"],[36.2574,-115.16132,\"park\"],[36.25741,-115.16333,\"park\"],[36.25742,-115.16284,\"park\"],[36.25743,-115.16321,\"park\"],[36.25743,-115.1624,\"park\"],[36.2575,-115.16447,\"park\"],[36.25756,-115.16429,\"park\"]],\"882986bb17fffff\":[[36.08927,-115.1414,\"park\"],[36.08929,-115.1396,\"park\"],[36.08931,-115.1422,\"park\"],[36.08932,-115.14171,\"park\"],[36.08936,-115.14097,\"park\"],[36.08938,-115.14058,\"park\"],[36.08939,-115.14093,\"park\"],[36.08942,-115.14196,\"park\"],[36.08946,-115.14062,\"park\"],[36.08947,-115.14328,\"park\"],[36.08952,-115.14358,\"park\"],[36.08965,-115.14002,\"park\"],[36.08966,-115.14447,\"park\"],[36.08966,-115.14082,\"park\"],[36.08966,-115.13995,\"park\"],[36.08967,-115.143,\"park\"],[36.08968,-115.13963,\"park\"],[36.08969,-115.14026,\"park\"],[36.08974,-115.14403,\"park\"],[36.08981,-115.14364,\"park\"],[36.08982,-115.14237,\"park\"],[36.08989,-115.1435,\"park\"],[36.08992,-115.14439,\"park\"]],\"882986bb3bfffff\":[[36.08939,-115.1376,\"park\"],[36.0894,-115.13744,\"park\"],[36.08951,-115.13829,\"park\"],[36.08952,-115.13765,\"park\"],[36.08965,-115.138,\"park\"],[36.08968,-115.13797,\"park\"],[36.0897,-115.13737,\"park\"]],\"882986a951fffff\":[[36.27232,-115.11892,\"desert\"],[36.27259,-115.11859,\"desert\"],[36.27286,-115.11859,\"desert\"],[36.27286,-115.11826,\"desert\"],[36.27313,-115.12093,\"desert\"],[36.27313,-115.12059,\"desert\"],[36.27313,-115.12026,\"desert\"],[36.27313,-115.11993,\"desert\"],[36.27313,-115.11959,\"desert\"],[36.27313,-115.11926,\"desert\"],[36.27313,-115.11892,\"desert\"],[36.27313,-115.11859,\"desert\"],[36.27313,-115.11826,\"desert\"],[36.27313,-115.11792,\"desert\"],[36.2734,-115.12093,\"desert\"],[36.2734,-115.12059,\"desert\"],[36.2734,-115.12026,\"desert\"],[36.2734,-115.11993,\"desert\"],[36.2734,-115.11926,\"desert\"],[36.2734,-115.11892,\"desert\"],[36.2734,-115.11859,\"desert\"],[36.2734,-115.11826,\"desert\"],[36.2734,-115.11792,\"desert\"],[36.27367,-115.12093,\"desert\"],[36.27367,-115.12059,\"desert\"],[36.27367,-115.12026,\"desert\"],[36.27367,-115.11926,\"desert\"],[36.27367,-115.11892,\"desert\"],[36.27367,-115.11859,\"desert\"],[36.27367,-115.11826,\"desert\"],[36.27367,-115.11792,\"desert\"],[36.27394,-115.12093,\"desert\"],[36.27394,-115.12059,\"desert\"],[36.27394,-115.12026,\"desert\"],[36.27394,-115.11926,\"desert\"],[36.27394,-115.11892,\"desert\"],[36.27394,-115.11859,\"desert\"],[36.27394,-115.11826,\"desert\"],[36.27394,-115.11792,\"desert\"],[36.27465,-115.12034,\"park\"],[36.27503,-115.12096,\"park\"]],\"882986a959fffff\":[[36.26588,-115.12493,\"park\"],[36.26596,-115.12529,\"park\"],[36.26597,-115.12467,\"park\"],[36.26859,-115.1254,\"park\"]],\"882986aa15fffff\":[[36.23242,-115.2114,\"park\"],[36.23242,-115.21106,\"park\"],[36.23242,-115.21073,\"park\"],[36.23242,-115.21039,\"park\"],[36.23269,-115.2114,\"park\"],[36.23269,-115.21106,\"park\"],[36.23269,-115.21073,\"park\"],[36.23269,-115.21039,\"park\"],[36.23296,-115.2114,\"park\"],[36.23296,-115.21106,\"park\"],[36.23296,-115.21073,\"park\"],[36.23296,-115.21039,\"park\"]],\"882986a125fffff\":[[36.22374,-115.23738,\"park\"],[36.22384,-115.23745,\"park\"]],\"882986a127fffff\":[[36.22397,-115.23896,\"park\"],[36.22408,-115.24113,\"park\"],[36.22412,-115.24174,\"park\"],[36.22416,-115.23894,\"park\"],[36.22431,-115.24163,\"park\"],[36.22433,-115.24123,\"park\"],[36.22458,-115.24162,\"park\"],[36.2246,-115.24118,\"park\"],[36.22527,-115.24078,\"park\"]],\"882986aa1bfffff\":[[36.22408,-115.23565,\"park\"],[36.22419,-115.23658,\"park\"],[36.22423,-115.2371,\"park\"],[36.22426,-115.23741,\"park\"],[36.2246,-115.23573,\"park\"],[36.22461,-115.23652,\"park\"],[36.22467,-115.23579,\"park\"],[36.22477,-115.23755,\"park\"],[36.22484,-115.23748,\"park\"],[36.22486,-115.23605,\"park\"],[36.22493,-115.23684,\"park\"]],\"882986ab55fffff\":[[36.21695,-115.13762,\"park\"],[36.21695,-115.13728,\"park\"],[36.21695,-115.13695,\"park\"],[36.21695,-115.13294,\"desert\"],[36.21722,-115.13662,\"park\"]],\"882986aa9bfffff\":[[36.27046,-115.28076,\"park\"],[36.27049,-115.27983,\"park\"],[36.27052,-115.28039,\"park\"],[36.27052,-115.28007,\"park\"],[36.27053,-115.27943,\"park\"],[36.27068,-115.28084,\"park\"],[36.27069,-115.27816,\"desert\"],[36.27069,-115.27783,\"desert\"],[36.27069,-115.27749,\"desert\"],[36.27069,-115.27716,\"desert\"],[36.27088,-115.27952,\"park\"],[36.27093,-115.28076,\"park\"],[36.27096,-115.27816,\"desert\"],[36.27096,-115.27783,\"desert\"],[36.27096,-115.27749,\"desert\"],[36.27096,-115.27716,\"desert\"],[36.27096,-115.27683,\"desert\"],[36.27118,-115.27951,\"park\"],[36.2712,-115.28014,\"park\"],[36.27122,-115.2804,\"park\"],[36.27123,-115.28083,\"park\"],[36.27123,-115.27816,\"desert\"],[36.27123,-115.27783,\"desert\"],[36.27123,-115.27749,\"desert\"],[36.27123,-115.27716,\"desert\"],[36.27123,-115.27683,\"desert\"],[36.27124,-115.28025,\"park\"],[36.27131,-115.27949,\"park\"],[36.27132,-115.28081,\"park\"],[36.27143,-115.27957,\"park\"],[36.27149,-115.27986,\"park\"],[36.2715,-115.27816,\"desert\"],[36.2715,-115.27783,\"desert\"],[36.2715,-115.27749,\"desert\"],[36.2715,-115.27716,\"desert\"],[36.2715,-115.27683,\"desert\"],[36.27164,-115.28072,\"park\"],[36.27177,-115.2785,\"desert\"],[36.27177,-115.27816,\"desert\"],[36.27177,-115.27783,\"desert\"],[36.27177,-115.27749,\"desert\"],[36.27177,-115.27716,\"desert\"],[36.27177,-115.27683,\"desert\"],[36.27204,-115.2785,\"desert\"],[36.27204,-115.27816,\"desert\"],[36.27204,-115.27783,\"desert\"],[36.27204,-115.27749,\"desert\"],[36.27204,-115.27716,\"desert\"],[36.27204,-115.27683,\"desert\"],[36.27232,-115.27883,\"desert\"],[36.27232,-115.2785,\"desert\"],[36.27232,-115.27816,\"desert\"],[36.27232,-115.27783,\"desert\"],[36.27232,-115.27749,\"desert\"],[36.27232,-115.27716,\"desert\"],[36.27232,-115.27683,\"desert\"],[36.27259,-115.2785,\"desert\"],[36.27259,-115.27816,\"desert\"],[36.27259,-115.27783,\"desert\"],[36.27259,-115.27749,\"desert\"],[36.27259,-115.27716,\"desert\"],[36.27286,-115.27816,\"desert\"],[36.27286,-115.27783,\"desert\"],[36.27286,-115.27749,\"desert\"],[36.27286,-115.27716,\"desert\"],[36.27313,-115.27983,\"desert\"],[36.27313,-115.27816,\"desert\"],[36.27313,-115.27783,\"desert\"],[36.27313,-115.27749,\"desert\"],[36.27313,-115.27716,\"desert\"],[36.2734,-115.28016,\"desert\"],[36.2734,-115.27983,\"desert\"],[36.2734,-115.2795,\"desert\"],[36.27367,-115.28016,\"desert\"],[36.27367,-115.27983,\"desert\"],[36.27367,-115.2795,\"desert\"],[36.27394,-115.28016,\"desert\"],[36.27394,-115.27983,\"desert\"],[36.27394,-115.2795,\"desert\"],[36.27422,-115.27816,\"desert\"],[36.27422,-115.27783,\"desert\"],[36.27422,-115.27749,\"desert\"],[36.27449,-115.27816,\"desert\"],[36.27449,-115.27783,\"desert\"],[36.27449,-115.27749,\"desert\"],[36.27476,-115.27816,\"desert\"],[36.27476,-115.27783,\"desert\"],[36.27476,-115.27749,\"desert\"],[36.27503,-115.27816,\"desert\"],[36.27503,-115.27783,\"desert\"],[36.27503,-115.27749,\"desert\"],[36.2753,-115.27816,\"desert\"],[36.2753,-115.27783,\"desert\"],[36.27557,-115.27816,\"desert\"],[36.27557,-115.27783,\"desert\"],[36.27584,-115.27816,\"desert\"],[36.27584,-115.27783,\"desert\"],[36.27612,-115.27816,\"desert\"]],\"88298616d7fffff\":[[36.1841,-115.08995,\"park\"]],\"882986b9b1fffff\":[[36.19714,-115.1463,\"park\"],[36.19714,-115.14596,\"park\"],[36.19714,-115.14563,\"park\"],[36.19741,-115.1463,\"park\"],[36.19741,-115.14596,\"park\"],[36.19741,-115.14563,\"park\"]],\"882986aa59fffff\":[[36.2004,-115.22743,\"park\"]],\"882986a13bfffff\":[[36.22401,-115.2815,\"park\"],[36.22401,-115.28117,\"park\"],[36.22428,-115.28183,\"park\"],[36.22428,-115.2815,\"park\"],[36.22428,-115.28117,\"park\"],[36.22428,-115.28083,\"park\"],[36.22455,-115.28183,\"park\"],[36.22455,-115.2815,\"park\"],[36.22455,-115.28117,\"park\"],[36.22455,-115.28083,\"park\"],[36.22455,-115.2805,\"park\"],[36.22482,-115.28183,\"park\"],[36.22482,-115.2815,\"park\"],[36.22482,-115.28117,\"park\"],[36.22482,-115.28083,\"park\"],[36.22482,-115.2805,\"park\"],[36.22509,-115.28183,\"park\"],[36.22509,-115.2815,\"park\"],[36.22509,-115.28083,\"park\"],[36.22509,-115.2805,\"park\"],[36.22536,-115.28183,\"park\"],[36.22536,-115.2815,\"park\"],[36.22536,-115.27983,\"park\"],[36.22564,-115.28183,\"park\"],[36.22564,-115.2815,\"park\"],[36.22564,-115.28117,\"park\"],[36.22564,-115.28083,\"park\"],[36.22564,-115.2805,\"park\"],[36.22564,-115.28016,\"park\"],[36.22564,-115.27983,\"park\"]],\"882986b987fffff\":[[36.18031,-115.14763,\"park\"]],\"882986b9bbfffff\":[[36.19388,-115.15531,\"park\"],[36.19388,-115.15498,\"park\"],[36.19388,-115.15464,\"park\"],[36.19388,-115.15431,\"park\"],[36.19388,-115.15398,\"park\"],[36.19415,-115.15464,\"park\"],[36.19415,-115.15431,\"park\"],[36.19415,-115.15398,\"park\"]],\"882986aa5bfffff\":[[36.20447,-115.23076,\"park\"],[36.20474,-115.23109,\"park\"],[36.20474,-115.23076,\"park\"],[36.20501,-115.23109,\"park\"],[36.20501,-115.23076,\"park\"],[36.20528,-115.23109,\"park\"],[36.20528,-115.23076,\"park\"]],\"882986baa7fffff\":[[36.12441,-115.21707,\"desert\"],[36.12468,-115.21707,\"desert\"],[36.12495,-115.21707,\"desert\"],[36.12522,-115.21707,\"desert\"]],\"882986a36dfffff\":[[36.12766,-115.27082,\"desert\"],[36.12793,-115.27082,\"desert\"],[36.12821,-115.27082,\"desert\"],[36.12848,-115.27082,\"desert\"],[36.12875,-115.27082,\"desert\"]],\"882986a141fffff\":[[36.17896,-115.27182,\"park\"],[36.17896,-115.27149,\"park\"]],\"882986ba3bfffff\":[[36.08044,-115.20706,\"desert\"],[36.08044,-115.20672,\"desert\"],[36.08071,-115.20706,\"desert\"],[36.08071,-115.20672,\"desert\"],[36.08098,-115.20706,\"desert\"],[36.08098,-115.20672,\"desert\"],[36.08125,-115.20706,\"desert\"],[36.08125,-115.20672,\"desert\"],[36.08153,-115.20706,\"desert\"],[36.08153,-115.20672,\"desert\"],[36.0818,-115.20706,\"desert\"],[36.0818,-115.20672,\"desert\"],[36.08207,-115.20706,\"desert\"]],\"882986ba33fffff\":[[36.08207,-115.20672,\"desert\"]],\"882986bac3fffff\":[[36.0837,-115.25246,\"desert\"],[36.0837,-115.25079,\"desert\"],[36.0837,-115.25045,\"desert\"],[36.08587,-115.2578,\"desert\"],[36.08587,-115.25746,\"desert\"],[36.08587,-115.25713,\"desert\"],[36.08587,-115.25446,\"desert\"],[36.08587,-115.25413,\"desert\"],[36.08587,-115.25379,\"desert\"]],\"882986bac1fffff\":[[36.07827,-115.25346,\"desert\"],[36.07854,-115.25346,\"desert\"],[36.07881,-115.25346,\"desert\"],[36.07908,-115.25379,\"desert\"],[36.07908,-115.25346,\"desert\"],[36.08017,-115.24411,\"desert\"],[36.08017,-115.24378,\"desert\"],[36.08017,-115.24344,\"desert\"],[36.08044,-115.24411,\"desert\"],[36.08044,-115.24378,\"desert\"],[36.08044,-115.24344,\"desert\"],[36.08071,-115.24344,\"desert\"],[36.08098,-115.24344,\"desert\"],[36.08207,-115.24678,\"desert\"],[36.08207,-115.24645,\"desert\"],[36.08207,-115.24611,\"desert\"],[36.08234,-115.24678,\"desert\"],[36.08234,-115.24645,\"desert\"],[36.08234,-115.24611,\"desert\"],[36.08234,-115.24511,\"desert\"],[36.08261,-115.24678,\"desert\"],[36.08261,-115.24645,\"desert\"],[36.08261,-115.24611,\"desert\"],[36.08261,-115.24578,\"desert\"],[36.08261,-115.24545,\"desert\"],[36.08261,-115.24511,\"desert\"],[36.08288,-115.24678,\"desert\"],[36.08288,-115.24645,\"desert\"],[36.08288,-115.24611,\"desert\"],[36.08288,-115.24578,\"desert\"],[36.08288,-115.24545,\"desert\"],[36.08288,-115.24511,\"desert\"]],\"882986bac7fffff\":[[36.08288,-115.24478,\"desert\"],[36.08451,-115.24444,\"desert\"],[36.08478,-115.24444,\"desert\"],[36.08587,-115.24912,\"desert\"],[36.08614,-115.24912,\"desert\"],[36.08641,-115.24912,\"desert\"]],\"882986b85dfffff\":[[36.09672,-115.19404,\"desert\"],[36.09699,-115.19404,\"desert\"],[36.09727,-115.19404,\"desert\"],[36.09754,-115.1937,\"desert\"],[36.09754,-115.19337,\"desert\"],[36.09781,-115.1937,\"desert\"]],\"882986bad1fffff\":[[36.0875,-115.27816,\"desert\"],[36.08777,-115.27816,\"desert\"],[36.08804,-115.27816,\"desert\"],[36.08831,-115.27816,\"desert\"],[36.09108,-115.27079,\"desert\"],[36.09112,-115.27084,\"desert\"],[36.09163,-115.27175,\"desert\"],[36.09167,-115.27184,\"desert\"],[36.09182,-115.27217,\"desert\"],[36.09184,-115.27215,\"desert\"],[36.09184,-115.27182,\"desert\"],[36.09185,-115.27228,\"desert\"],[36.09192,-115.2725,\"desert\"],[36.09201,-115.27284,\"desert\"],[36.09206,-115.27698,\"desert\"],[36.09207,-115.27717,\"desert\"],[36.09208,-115.27654,\"desert\"],[36.09209,-115.2764,\"desert\"],[36.09209,-115.27323,\"desert\"],[36.0921,-115.27601,\"desert\"],[36.09211,-115.27582,\"desert\"],[36.09211,-115.27339,\"desert\"],[36.09213,-115.27734,\"desert\"],[36.09213,-115.27534,\"desert\"],[36.09213,-115.27357,\"desert\"],[36.09214,-115.27516,\"desert\"],[36.09216,-115.27394,\"desert\"],[36.09217,-115.27419,\"desert\"],[36.09218,-115.27456,\"desert\"],[36.09228,-115.27752,\"desert\"]],\"882986badbfffff\":[[36.0875,-115.2785,\"desert\"],[36.08777,-115.2785,\"desert\"],[36.08804,-115.2785,\"desert\"],[36.08831,-115.2785,\"desert\"]],\"882986aabdfffff\":[[36.27666,-115.24344,\"park\"],[36.27666,-115.24311,\"park\"],[36.27666,-115.24278,\"park\"],[36.27666,-115.24244,\"park\"],[36.27666,-115.24211,\"park\"],[36.27666,-115.24177,\"park\"],[36.27666,-115.24144,\"park\"],[36.27666,-115.24111,\"park\"],[36.27666,-115.24077,\"park\"],[36.27666,-115.24044,\"park\"],[36.27666,-115.2401,\"park\"],[36.27666,-115.23977,\"park\"],[36.27666,-115.23944,\"park\"],[36.27666,-115.2391,\"park\"],[36.27666,-115.23877,\"park\"],[36.27693,-115.24411,\"park\"],[36.27693,-115.24378,\"park\"],[36.27693,-115.24344,\"park\"],[36.27693,-115.24311,\"park\"],[36.27693,-115.24278,\"park\"],[36.27693,-115.24244,\"park\"],[36.27693,-115.24211,\"park\"],[36.27693,-115.24177,\"park\"],[36.27693,-115.24144,\"park\"],[36.27693,-115.24111,\"park\"],[36.27693,-115.24077,\"park\"],[36.27693,-115.24044,\"park\"],[36.27693,-115.2401,\"park\"],[36.27693,-115.23977,\"park\"],[36.27693,-115.23944,\"park\"],[36.27693,-115.2391,\"park\"],[36.27693,-115.23877,\"park\"],[36.2772,-115.24444,\"park\"],[36.2772,-115.24411,\"park\"],[36.2772,-115.24378,\"park\"],[36.2772,-115.24344,\"park\"],[36.2772,-115.24311,\"park\"],[36.2772,-115.24278,\"park\"],[36.2772,-115.24244,\"park\"],[36.2772,-115.24211,\"park\"],[36.2772,-115.24177,\"park\"],[36.2772,-115.24144,\"park\"],[36.2772,-115.24111,\"park\"],[36.2772,-115.24077,\"park\"],[36.2772,-115.24044,\"park\"],[36.2772,-115.2401,\"park\"],[36.2772,-115.23977,\"park\"],[36.2772,-115.23944,\"park\"],[36.2772,-115.2391,\"park\"],[36.2772,-115.23877,\"park\"],[36.27747,-115.24444,\"park\"],[36.27747,-115.24411,\"park\"],[36.27747,-115.24378,\"park\"],[36.27747,-115.24344,\"park\"],[36.27747,-115.24311,\"park\"],[36.27747,-115.24278,\"park\"],[36.27747,-115.24244,\"park\"],[36.27747,-115.24211,\"park\"],[36.27747,-115.24177,\"park\"],[36.27747,-115.24144,\"park\"],[36.27747,-115.24111,\"park\"],[36.27747,-115.24077,\"park\"],[36.27747,-115.24044,\"park\"],[36.27747,-115.2401,\"park\"],[36.27747,-115.23977,\"park\"],[36.27747,-115.23944,\"park\"],[36.27747,-115.2391,\"park\"],[36.27747,-115.23877,\"park\"],[36.27774,-115.24444,\"park\"],[36.27774,-115.24411,\"park\"],[36.27774,-115.24211,\"park\"],[36.27774,-115.24177,\"park\"],[36.27774,-115.24144,\"park\"],[36.27774,-115.24111,\"park\"],[36.27774,-115.24077,\"park\"],[36.27774,-115.24044,\"park\"],[36.27774,-115.2401,\"park\"],[36.27774,-115.23977,\"park\"],[36.27774,-115.23944,\"park\"],[36.27774,-115.2391,\"park\"],[36.27802,-115.24211,\"park\"],[36.27802,-115.24177,\"park\"],[36.27802,-115.24144,\"park\"],[36.27802,-115.24111,\"park\"],[36.27802,-115.24077,\"park\"],[36.27802,-115.24044,\"park\"],[36.27802,-115.2401,\"park\"],[36.27802,-115.23977,\"park\"],[36.27802,-115.23944,\"park\"],[36.27802,-115.2391,\"park\"],[36.27802,-115.23877,\"park\"],[36.27802,-115.23844,\"park\"],[36.27829,-115.24444,\"park\"],[36.27829,-115.24411,\"park\"],[36.27829,-115.24378,\"park\"],[36.27829,-115.24344,\"park\"],[36.27829,-115.24311,\"park\"],[36.27829,-115.24278,\"park\"],[36.27829,-115.24244,\"park\"],[36.27829,-115.24211,\"park\"],[36.27829,-115.24177,\"park\"],[36.27829,-115.24144,\"park\"],[36.27829,-115.24111,\"park\"],[36.27829,-115.24077,\"park\"],[36.27829,-115.24044,\"park\"],[36.27829,-115.2401,\"park\"],[36.27829,-115.23977,\"park\"],[36.27829,-115.23944,\"park\"],[36.27829,-115.2391,\"park\"],[36.27829,-115.23877,\"park\"],[36.27829,-115.23844,\"park\"],[36.27829,-115.23743,\"park\"],[36.27829,-115.2371,\"park\"],[36.27829,-115.23677,\"park\"],[36.27829,-115.23643,\"park\"],[36.27856,-115.24311,\"park\"],[36.27856,-115.24278,\"park\"],[36.27856,-115.24244,\"park\"],[36.27856,-115.24211,\"park\"],[36.27856,-115.24111,\"park\"],[36.27856,-115.24077,\"park\"],[36.27856,-115.24044,\"park\"],[36.27856,-115.2401,\"park\"],[36.27856,-115.23977,\"park\"],[36.27856,-115.23944,\"park\"],[36.27856,-115.2391,\"park\"],[36.27856,-115.23877,\"park\"],[36.27856,-115.23844,\"park\"],[36.27856,-115.23743,\"park\"],[36.27856,-115.2371,\"park\"],[36.27856,-115.23677,\"park\"],[36.27856,-115.23643,\"park\"],[36.27883,-115.24311,\"park\"],[36.27883,-115.24278,\"park\"],[36.27883,-115.24244,\"park\"],[36.27883,-115.24211,\"park\"],[36.27883,-115.24111,\"park\"],[36.27883,-115.24077,\"park\"],[36.27883,-115.24044,\"park\"],[36.27883,-115.2401,\"park\"],[36.27883,-115.23977,\"park\"],[36.27883,-115.23944,\"park\"],[36.27883,-115.2391,\"park\"],[36.27883,-115.23877,\"park\"],[36.27883,-115.23844,\"park\"],[36.27883,-115.23743,\"park\"],[36.27883,-115.2371,\"park\"],[36.27883,-115.23677,\"park\"],[36.27883,-115.23643,\"park\"],[36.2791,-115.24444,\"park\"],[36.2791,-115.24411,\"park\"],[36.2791,-115.24378,\"park\"],[36.2791,-115.24311,\"park\"],[36.2791,-115.24278,\"park\"],[36.2791,-115.24244,\"park\"],[36.2791,-115.24211,\"park\"],[36.2791,-115.24144,\"park\"],[36.2791,-115.24111,\"park\"],[36.2791,-115.24077,\"park\"],[36.2791,-115.24044,\"park\"],[36.2791,-115.2401,\"park\"],[36.2791,-115.23977,\"park\"],[36.2791,-115.23944,\"park\"],[36.2791,-115.2391,\"park\"],[36.2791,-115.23877,\"park\"],[36.2791,-115.23844,\"park\"],[36.2791,-115.23743,\"park\"],[36.2791,-115.2371,\"park\"],[36.2791,-115.23677,\"park\"],[36.2791,-115.23643,\"park\"]],\"882986aab9fffff\":[[36.27693,-115.24645,\"park\"],[36.27693,-115.24611,\"park\"],[36.27693,-115.24578,\"park\"],[36.27693,-115.24545,\"park\"],[36.27693,-115.24511,\"park\"],[36.27693,-115.24478,\"park\"],[36.27693,-115.24444,\"park\"],[36.2772,-115.24645,\"park\"],[36.2772,-115.24611,\"park\"],[36.2772,-115.24578,\"park\"],[36.2772,-115.24545,\"park\"],[36.2772,-115.24511,\"park\"],[36.2772,-115.24478,\"park\"],[36.27747,-115.24645,\"park\"],[36.27747,-115.24611,\"park\"],[36.27747,-115.24578,\"park\"],[36.27747,-115.24545,\"park\"],[36.27747,-115.24511,\"park\"],[36.27747,-115.24478,\"park\"],[36.27774,-115.24645,\"park\"],[36.27774,-115.24611,\"park\"],[36.27774,-115.24578,\"park\"],[36.27774,-115.24545,\"park\"],[36.27774,-115.24511,\"park\"],[36.27774,-115.24478,\"park\"],[36.27829,-115.24478,\"park\"],[36.27856,-115.24611,\"park\"],[36.27856,-115.24578,\"park\"],[36.27856,-115.24545,\"park\"],[36.27856,-115.24511,\"park\"],[36.27937,-115.24611,\"park\"],[36.27937,-115.24578,\"park\"]],\"882986ab15fffff\":[[36.23866,-115.13528,\"desert\"],[36.24029,-115.13795,\"desert\"],[36.24029,-115.13762,\"desert\"],[36.24029,-115.13728,\"desert\"],[36.24029,-115.13695,\"desert\"],[36.24029,-115.13662,\"desert\"],[36.24056,-115.13762,\"desert\"],[36.24056,-115.13728,\"desert\"],[36.24056,-115.13695,\"desert\"],[36.24056,-115.13662,\"desert\"],[36.24083,-115.13728,\"desert\"],[36.24083,-115.13695,\"desert\"],[36.24083,-115.13662,\"desert\"],[36.24111,-115.13728,\"desert\"],[36.24111,-115.13695,\"desert\"],[36.24111,-115.13662,\"desert\"],[36.24138,-115.13728,\"desert\"],[36.24138,-115.13695,\"desert\"],[36.24138,-115.13662,\"desert\"],[36.24165,-115.13695,\"desert\"]],\"88298616c3fffff\":[[36.17028,-115.08345,\"desert\"]],\"882986ba89fffff\":[[36.09727,-115.25312,\"park\"],[36.09727,-115.25279,\"park\"],[36.09727,-115.25246,\"park\"],[36.09727,-115.25212,\"park\"],[36.09727,-115.25179,\"park\"],[36.09754,-115.25279,\"park\"],[36.09754,-115.25246,\"park\"],[36.09754,-115.25212,\"park\"],[36.09754,-115.25179,\"park\"]],\"882986b8c1fffff\":[[36.13363,-115.22241,\"desert\"],[36.1339,-115.22241,\"desert\"],[36.13445,-115.22341,\"desert\"],[36.13526,-115.22341,\"desert\"],[36.13553,-115.22341,\"desert\"],[36.1358,-115.22341,\"desert\"],[36.1358,-115.22241,\"desert\"]],\"882986aa61fffff\":[[36.21071,-115.17401,\"desert\"],[36.21071,-115.17367,\"desert\"],[36.21071,-115.17334,\"desert\"],[36.21071,-115.173,\"desert\"],[36.21071,-115.17267,\"desert\"],[36.21071,-115.17234,\"desert\"],[36.21071,-115.172,\"desert\"],[36.21071,-115.17167,\"desert\"],[36.21071,-115.17134,\"desert\"],[36.21071,-115.171,\"desert\"],[36.21098,-115.17401,\"desert\"],[36.21098,-115.17367,\"desert\"],[36.21098,-115.17334,\"desert\"],[36.21098,-115.173,\"desert\"],[36.21098,-115.17267,\"desert\"],[36.21098,-115.17234,\"desert\"],[36.21098,-115.172,\"desert\"],[36.21098,-115.17167,\"desert\"],[36.21098,-115.17134,\"desert\"],[36.21098,-115.171,\"desert\"],[36.21125,-115.17367,\"desert\"],[36.21125,-115.17334,\"desert\"],[36.21125,-115.173,\"desert\"],[36.21125,-115.17267,\"desert\"],[36.21125,-115.17234,\"desert\"],[36.21125,-115.172,\"desert\"],[36.21152,-115.17334,\"desert\"],[36.21152,-115.173,\"desert\"]],\"882986aa63fffff\":[[36.21071,-115.18402,\"desert\"],[36.21071,-115.18369,\"desert\"],[36.21071,-115.18335,\"desert\"],[36.21071,-115.18302,\"desert\"],[36.21071,-115.18269,\"desert\"],[36.21071,-115.18235,\"desert\"],[36.21071,-115.18202,\"desert\"],[36.21071,-115.18168,\"desert\"],[36.21071,-115.18135,\"desert\"],[36.21071,-115.18102,\"desert\"],[36.21071,-115.18068,\"desert\"],[36.21071,-115.18035,\"desert\"],[36.21071,-115.18001,\"desert\"],[36.21098,-115.18402,\"desert\"],[36.21098,-115.18369,\"desert\"],[36.21098,-115.18335,\"desert\"],[36.21098,-115.18302,\"desert\"],[36.21098,-115.18269,\"desert\"],[36.21098,-115.18235,\"desert\"],[36.21098,-115.18202,\"desert\"],[36.21098,-115.18168,\"desert\"],[36.21098,-115.18135,\"desert\"],[36.21098,-115.18102,\"desert\"],[36.21098,-115.18068,\"desert\"],[36.21098,-115.18035,\"desert\"],[36.21098,-115.18001,\"desert\"],[36.21125,-115.18402,\"desert\"],[36.21125,-115.18369,\"desert\"],[36.21125,-115.18335,\"desert\"],[36.21125,-115.18302,\"desert\"],[36.21125,-115.18269,\"desert\"],[36.21125,-115.18235,\"desert\"],[36.21125,-115.18202,\"desert\"],[36.21125,-115.18168,\"desert\"],[36.21125,-115.18135,\"desert\"],[36.21125,-115.18102,\"desert\"],[36.21125,-115.18068,\"desert\"],[36.21125,-115.18035,\"desert\"],[36.21125,-115.18001,\"desert\"],[36.21125,-115.17401,\"desert\"],[36.21152,-115.18435,\"desert\"],[36.21152,-115.18402,\"desert\"],[36.21152,-115.18369,\"desert\"],[36.21152,-115.18335,\"desert\"],[36.21152,-115.18302,\"desert\"],[36.21152,-115.18269,\"desert\"],[36.21152,-115.18235,\"desert\"],[36.21152,-115.18202,\"desert\"],[36.21152,-115.18168,\"desert\"],[36.21152,-115.18135,\"desert\"],[36.21152,-115.18102,\"desert\"],[36.21152,-115.18068,\"desert\"],[36.21152,-115.18035,\"desert\"],[36.21152,-115.18001,\"desert\"],[36.21152,-115.17401,\"desert\"],[36.21152,-115.17367,\"desert\"],[36.21179,-115.18435,\"desert\"],[36.21179,-115.18402,\"desert\"],[36.21179,-115.18369,\"desert\"],[36.21179,-115.18335,\"desert\"],[36.21179,-115.18302,\"desert\"],[36.21179,-115.18269,\"desert\"],[36.21179,-115.18235,\"desert\"],[36.21179,-115.18202,\"desert\"],[36.21179,-115.18168,\"desert\"],[36.21179,-115.18135,\"desert\"],[36.21179,-115.18102,\"desert\"],[36.21179,-115.18068,\"desert\"],[36.21179,-115.18035,\"desert\"],[36.21179,-115.18001,\"desert\"],[36.21207,-115.18435,\"desert\"],[36.21207,-115.18402,\"desert\"],[36.21207,-115.18369,\"desert\"],[36.21207,-115.18335,\"desert\"],[36.21207,-115.18302,\"desert\"],[36.21207,-115.18269,\"desert\"],[36.21207,-115.18235,\"desert\"],[36.21207,-115.18202,\"desert\"],[36.21207,-115.18168,\"desert\"],[36.21207,-115.18135,\"desert\"],[36.21207,-115.18102,\"desert\"],[36.21207,-115.18068,\"desert\"],[36.21207,-115.18035,\"desert\"],[36.21207,-115.18001,\"desert\"],[36.21234,-115.18435,\"desert\"],[36.21234,-115.18402,\"desert\"],[36.21234,-115.18369,\"desert\"],[36.21234,-115.18335,\"desert\"],[36.21234,-115.18302,\"desert\"],[36.21234,-115.18269,\"desert\"],[36.21234,-115.18235,\"desert\"],[36.21234,-115.18202,\"desert\"],[36.21234,-115.18168,\"desert\"],[36.21234,-115.18135,\"desert\"],[36.21234,-115.18102,\"desert\"],[36.21234,-115.18068,\"desert\"],[36.21234,-115.18035,\"desert\"],[36.21234,-115.18001,\"desert\"],[36.21261,-115.18435,\"desert\"],[36.21261,-115.18402,\"desert\"],[36.21261,-115.18369,\"desert\"],[36.21261,-115.18335,\"desert\"],[36.21261,-115.18302,\"desert\"],[36.21261,-115.18269,\"desert\"],[36.21261,-115.18235,\"desert\"],[36.21261,-115.18202,\"desert\"],[36.21261,-115.18168,\"desert\"],[36.21261,-115.18135,\"desert\"],[36.21261,-115.18102,\"desert\"],[36.21261,-115.18068,\"desert\"],[36.21261,-115.18035,\"desert\"],[36.21261,-115.18001,\"desert\"],[36.21288,-115.18469,\"desert\"],[36.21288,-115.18435,\"desert\"],[36.21288,-115.18402,\"desert\"],[36.21288,-115.18369,\"desert\"],[36.21288,-115.18335,\"desert\"],[36.21288,-115.18302,\"desert\"],[36.21288,-115.18269,\"desert\"],[36.21288,-115.18235,\"desert\"],[36.21288,-115.18202,\"desert\"],[36.21288,-115.18168,\"desert\"],[36.21288,-115.18135,\"desert\"],[36.21288,-115.18102,\"desert\"],[36.21288,-115.18068,\"desert\"],[36.21288,-115.18035,\"desert\"],[36.21288,-115.18001,\"desert\"],[36.21315,-115.18469,\"desert\"],[36.21315,-115.18435,\"desert\"],[36.21315,-115.18402,\"desert\"],[36.21315,-115.18369,\"desert\"],[36.21315,-115.18335,\"desert\"],[36.21315,-115.18302,\"desert\"],[36.21315,-115.18269,\"desert\"],[36.21315,-115.18235,\"desert\"],[36.21315,-115.18202,\"desert\"],[36.21315,-115.18168,\"desert\"],[36.21315,-115.18135,\"desert\"],[36.21315,-115.18102,\"desert\"],[36.21315,-115.18068,\"desert\"],[36.21315,-115.18035,\"desert\"],[36.21315,-115.18001,\"desert\"],[36.21342,-115.18469,\"desert\"],[36.21342,-115.18435,\"desert\"],[36.21342,-115.18402,\"desert\"],[36.21342,-115.18369,\"desert\"],[36.21342,-115.18335,\"desert\"],[36.21342,-115.18302,\"desert\"],[36.21342,-115.18269,\"desert\"],[36.21342,-115.18235,\"desert\"],[36.21342,-115.18202,\"desert\"],[36.21342,-115.18168,\"desert\"],[36.21342,-115.18135,\"desert\"],[36.21342,-115.18102,\"desert\"],[36.21342,-115.18068,\"desert\"],[36.21342,-115.18035,\"desert\"],[36.21342,-115.18001,\"desert\"],[36.21369,-115.18469,\"desert\"],[36.21369,-115.18435,\"desert\"],[36.21369,-115.18402,\"desert\"],[36.21369,-115.18369,\"desert\"],[36.21369,-115.18335,\"desert\"],[36.21369,-115.18302,\"desert\"],[36.21369,-115.18269,\"desert\"],[36.21369,-115.18235,\"desert\"],[36.21369,-115.18202,\"desert\"],[36.21369,-115.18168,\"desert\"],[36.21369,-115.18135,\"desert\"],[36.21369,-115.18102,\"desert\"],[36.21369,-115.18068,\"desert\"],[36.21369,-115.18035,\"desert\"],[36.21369,-115.18001,\"desert\"],[36.21397,-115.18469,\"desert\"],[36.21397,-115.18435,\"desert\"],[36.21397,-115.18402,\"desert\"],[36.21397,-115.18369,\"desert\"],[36.21397,-115.18335,\"desert\"],[36.21397,-115.18302,\"desert\"],[36.21397,-115.18269,\"desert\"],[36.21397,-115.18235,\"desert\"],[36.21397,-115.18202,\"desert\"],[36.21397,-115.18168,\"desert\"],[36.21397,-115.18135,\"desert\"],[36.21397,-115.18102,\"desert\"],[36.21397,-115.18068,\"desert\"],[36.21397,-115.18035,\"desert\"],[36.21397,-115.18001,\"desert\"],[36.21424,-115.18502,\"desert\"],[36.21424,-115.18469,\"desert\"],[36.21424,-115.18435,\"desert\"],[36.21424,-115.18402,\"desert\"],[36.21424,-115.18369,\"desert\"],[36.21424,-115.18335,\"desert\"],[36.21424,-115.18302,\"desert\"],[36.21424,-115.18269,\"desert\"],[36.21424,-115.18235,\"desert\"],[36.21424,-115.18202,\"desert\"],[36.21424,-115.18168,\"desert\"],[36.21424,-115.18135,\"desert\"],[36.21424,-115.18102,\"desert\"],[36.21424,-115.18068,\"desert\"],[36.21424,-115.18035,\"desert\"],[36.21424,-115.18001,\"desert\"],[36.21451,-115.18502,\"desert\"],[36.21451,-115.18469,\"desert\"],[36.21451,-115.18435,\"desert\"],[36.21451,-115.18402,\"desert\"],[36.21451,-115.18369,\"desert\"],[36.21451,-115.18335,\"desert\"],[36.21451,-115.18302,\"desert\"],[36.21451,-115.18269,\"desert\"],[36.21451,-115.18235,\"desert\"],[36.21451,-115.18202,\"desert\"],[36.21451,-115.18168,\"desert\"],[36.21451,-115.18135,\"desert\"],[36.21451,-115.18102,\"desert\"],[36.21451,-115.18068,\"desert\"],[36.21451,-115.18035,\"desert\"],[36.21451,-115.18001,\"desert\"],[36.21478,-115.18502,\"desert\"],[36.21478,-115.18469,\"desert\"],[36.21478,-115.18435,\"desert\"],[36.21478,-115.18402,\"desert\"],[36.21478,-115.18369,\"desert\"],[36.21478,-115.18335,\"desert\"],[36.21478,-115.18302,\"desert\"],[36.21478,-115.18269,\"desert\"],[36.21478,-115.18235,\"desert\"],[36.21478,-115.18202,\"desert\"],[36.21478,-115.18168,\"desert\"],[36.21478,-115.18135,\"desert\"],[36.21478,-115.18102,\"desert\"],[36.21478,-115.18068,\"desert\"],[36.21478,-115.18035,\"desert\"],[36.21478,-115.18001,\"desert\"],[36.21478,-115.17401,\"desert\"],[36.21505,-115.18469,\"desert\"],[36.21505,-115.18435,\"desert\"],[36.21505,-115.18402,\"desert\"],[36.21505,-115.18369,\"desert\"],[36.21505,-115.18335,\"desert\"],[36.21505,-115.18302,\"desert\"],[36.21505,-115.18269,\"desert\"],[36.21505,-115.18235,\"desert\"],[36.21505,-115.18202,\"desert\"],[36.21505,-115.18168,\"desert\"],[36.21505,-115.18135,\"desert\"],[36.21505,-115.18102,\"desert\"],[36.21505,-115.18068,\"desert\"],[36.21505,-115.18035,\"desert\"],[36.21532,-115.18402,\"desert\"],[36.21532,-115.18369,\"desert\"],[36.21532,-115.18335,\"desert\"],[36.21532,-115.18302,\"desert\"],[36.21532,-115.18269,\"desert\"],[36.21532,-115.18235,\"desert\"],[36.21532,-115.18202,\"desert\"],[36.21532,-115.18168,\"desert\"],[36.21532,-115.18135,\"desert\"],[36.21532,-115.18102,\"desert\"],[36.21532,-115.18068,\"desert\"],[36.21532,-115.18035,\"desert\"],[36.21559,-115.18369,\"desert\"],[36.21559,-115.18335,\"desert\"],[36.21559,-115.18302,\"desert\"],[36.21559,-115.18269,\"desert\"],[36.21559,-115.18235,\"desert\"],[36.21559,-115.18202,\"desert\"],[36.21559,-115.18168,\"desert\"],[36.21559,-115.18135,\"desert\"],[36.21559,-115.18102,\"desert\"],[36.21559,-115.18068,\"desert\"],[36.21559,-115.18035,\"desert\"],[36.21587,-115.18335,\"desert\"],[36.21587,-115.18302,\"desert\"],[36.21587,-115.18269,\"desert\"],[36.21587,-115.18235,\"desert\"],[36.21587,-115.18202,\"desert\"],[36.21587,-115.18168,\"desert\"],[36.21587,-115.18135,\"desert\"],[36.21587,-115.18102,\"desert\"],[36.21587,-115.18068,\"desert\"],[36.21587,-115.18035,\"desert\"],[36.21614,-115.18302,\"desert\"],[36.21614,-115.18269,\"desert\"],[36.21614,-115.18235,\"desert\"],[36.21614,-115.18202,\"desert\"],[36.21614,-115.18168,\"desert\"],[36.21614,-115.18135,\"desert\"],[36.21614,-115.18102,\"desert\"],[36.21614,-115.18068,\"desert\"],[36.21614,-115.18035,\"desert\"],[36.21641,-115.18269,\"desert\"],[36.21641,-115.18235,\"desert\"],[36.21641,-115.18202,\"desert\"],[36.21641,-115.18168,\"desert\"],[36.21641,-115.18135,\"desert\"],[36.21641,-115.18102,\"desert\"],[36.21641,-115.18068,\"desert\"],[36.21641,-115.18035,\"desert\"],[36.21668,-115.18235,\"desert\"],[36.21668,-115.18202,\"desert\"],[36.21668,-115.18168,\"desert\"],[36.21668,-115.18135,\"desert\"],[36.21668,-115.18102,\"desert\"],[36.21668,-115.18068,\"desert\"],[36.21668,-115.18035,\"desert\"],[36.21695,-115.18168,\"desert\"],[36.21695,-115.18135,\"desert\"],[36.21695,-115.18102,\"desert\"],[36.21695,-115.18068,\"desert\"],[36.21695,-115.18035,\"desert\"]],\"882986aa67fffff\":[[36.21125,-115.17167,\"desert\"],[36.21125,-115.17134,\"desert\"],[36.21125,-115.171,\"desert\"],[36.21152,-115.17267,\"desert\"],[36.21152,-115.17234,\"desert\"],[36.21152,-115.172,\"desert\"],[36.21152,-115.17167,\"desert\"],[36.21152,-115.17134,\"desert\"],[36.21152,-115.171,\"desert\"],[36.21179,-115.17167,\"desert\"],[36.21179,-115.17134,\"desert\"],[36.21179,-115.171,\"desert\"],[36.21207,-115.17167,\"desert\"],[36.21207,-115.17134,\"desert\"],[36.21207,-115.171,\"desert\"],[36.21234,-115.17167,\"desert\"],[36.21234,-115.17134,\"desert\"],[36.21234,-115.171,\"desert\"],[36.21261,-115.17167,\"desert\"],[36.21261,-115.17134,\"desert\"],[36.21261,-115.171,\"desert\"],[36.21288,-115.17167,\"desert\"],[36.21288,-115.17134,\"desert\"],[36.21288,-115.171,\"desert\"],[36.21315,-115.17167,\"desert\"],[36.21315,-115.17134,\"desert\"],[36.21315,-115.171,\"desert\"],[36.21342,-115.17167,\"desert\"],[36.21342,-115.17134,\"desert\"],[36.21342,-115.171,\"desert\"],[36.21451,-115.17267,\"desert\"],[36.21451,-115.17234,\"desert\"],[36.21478,-115.17367,\"desert\"],[36.21478,-115.17134,\"desert\"],[36.21478,-115.171,\"desert\"],[36.21505,-115.17134,\"desert\"],[36.21505,-115.171,\"desert\"],[36.21532,-115.17134,\"desert\"],[36.21532,-115.171,\"desert\"],[36.21559,-115.17401,\"desert\"],[36.21559,-115.17367,\"desert\"],[36.21559,-115.17334,\"desert\"],[36.21559,-115.173,\"desert\"],[36.21559,-115.17267,\"desert\"],[36.21559,-115.17234,\"desert\"],[36.21559,-115.17134,\"desert\"],[36.21559,-115.171,\"desert\"],[36.21587,-115.17401,\"desert\"],[36.21587,-115.17367,\"desert\"],[36.21587,-115.17334,\"desert\"],[36.21587,-115.173,\"desert\"],[36.21587,-115.17267,\"desert\"],[36.21587,-115.17234,\"desert\"],[36.21641,-115.171,\"desert\"],[36.21668,-115.17401,\"desert\"],[36.21668,-115.17367,\"desert\"],[36.21668,-115.17334,\"desert\"],[36.21668,-115.173,\"desert\"],[36.21668,-115.17267,\"desert\"],[36.21668,-115.17234,\"desert\"],[36.21668,-115.172,\"desert\"],[36.21668,-115.171,\"desert\"],[36.21695,-115.17367,\"desert\"],[36.21695,-115.17334,\"desert\"],[36.21695,-115.173,\"desert\"],[36.21695,-115.17267,\"desert\"],[36.21695,-115.17234,\"desert\"],[36.21695,-115.172,\"desert\"],[36.21695,-115.171,\"desert\"]],\"882986aa29fffff\":[[36.21695,-115.17401,\"desert\"]],\"882986aa65fffff\":[[36.20745,-115.15565,\"desert\"],[36.20772,-115.15565,\"desert\"],[36.21207,-115.16032,\"desert\"],[36.21207,-115.15999,\"desert\"],[36.21207,-115.15965,\"desert\"],[36.21207,-115.15932,\"desert\"],[36.21207,-115.15898,\"desert\"],[36.21207,-115.15865,\"desert\"],[36.21207,-115.15832,\"desert\"],[36.21207,-115.15798,\"desert\"],[36.21207,-115.15765,\"desert\"],[36.21234,-115.16032,\"desert\"],[36.21234,-115.15999,\"desert\"],[36.21234,-115.15965,\"desert\"],[36.21234,-115.15932,\"desert\"],[36.21234,-115.15898,\"desert\"],[36.21234,-115.15865,\"desert\"],[36.21261,-115.16032,\"desert\"],[36.21261,-115.15999,\"desert\"],[36.21261,-115.15965,\"desert\"]],\"882986ab59fffff\":[[36.20745,-115.15531,\"desert\"],[36.20772,-115.15531,\"desert\"],[36.208,-115.15565,\"desert\"],[36.208,-115.15531,\"desert\"],[36.20827,-115.15565,\"desert\"],[36.20827,-115.15531,\"desert\"],[36.20854,-115.15565,\"desert\"],[36.20854,-115.15531,\"desert\"],[36.20881,-115.15565,\"desert\"],[36.20881,-115.15531,\"desert\"],[36.20908,-115.15565,\"desert\"],[36.20908,-115.15531,\"desert\"],[36.20935,-115.15565,\"desert\"],[36.20935,-115.15531,\"desert\"],[36.20935,-115.15498,\"desert\"],[36.20962,-115.15565,\"desert\"],[36.20962,-115.15531,\"desert\"],[36.20962,-115.15498,\"desert\"],[36.2099,-115.15565,\"desert\"],[36.2099,-115.15531,\"desert\"],[36.2099,-115.15498,\"desert\"],[36.21017,-115.15565,\"desert\"],[36.21017,-115.15531,\"desert\"],[36.21017,-115.15498,\"desert\"],[36.21017,-115.15464,\"desert\"],[36.21044,-115.15565,\"desert\"],[36.21044,-115.15531,\"desert\"],[36.21044,-115.15498,\"desert\"],[36.21044,-115.15464,\"desert\"],[36.21044,-115.15431,\"desert\"],[36.21044,-115.15398,\"desert\"],[36.21044,-115.15364,\"desert\"],[36.21044,-115.15331,\"desert\"],[36.21044,-115.15297,\"desert\"],[36.21071,-115.15565,\"desert\"],[36.21071,-115.15531,\"desert\"],[36.21071,-115.15498,\"desert\"],[36.21071,-115.15464,\"desert\"],[36.21071,-115.15431,\"desert\"],[36.21071,-115.15398,\"desert\"],[36.21071,-115.15364,\"desert\"],[36.21071,-115.15331,\"desert\"],[36.21071,-115.15297,\"desert\"],[36.21071,-115.15264,\"desert\"],[36.21071,-115.15231,\"desert\"],[36.21098,-115.15565,\"desert\"],[36.21098,-115.15531,\"desert\"],[36.21098,-115.15498,\"desert\"],[36.21098,-115.15464,\"desert\"],[36.21098,-115.15431,\"desert\"],[36.21098,-115.15398,\"desert\"],[36.21098,-115.15364,\"desert\"],[36.21098,-115.15331,\"desert\"],[36.21098,-115.15297,\"desert\"],[36.21098,-115.15264,\"desert\"],[36.21098,-115.15231,\"desert\"],[36.21098,-115.15131,\"desert\"],[36.21098,-115.15097,\"desert\"],[36.21098,-115.15064,\"desert\"],[36.21098,-115.1503,\"desert\"],[36.21098,-115.14997,\"desert\"],[36.21098,-115.14964,\"desert\"],[36.21098,-115.1493,\"desert\"],[36.21098,-115.14897,\"desert\"],[36.21098,-115.14863,\"desert\"],[36.21098,-115.1483,\"desert\"],[36.21098,-115.14797,\"desert\"],[36.21125,-115.15297,\"desert\"],[36.21125,-115.15264,\"desert\"],[36.21125,-115.15231,\"desert\"],[36.21125,-115.15131,\"desert\"],[36.21125,-115.15097,\"desert\"],[36.21125,-115.15064,\"desert\"],[36.21125,-115.14697,\"desert\"],[36.21125,-115.14663,\"desert\"],[36.21125,-115.1463,\"desert\"],[36.21125,-115.14596,\"desert\"],[36.21125,-115.14563,\"desert\"],[36.21125,-115.1453,\"desert\"],[36.21152,-115.14697,\"desert\"],[36.21152,-115.14663,\"desert\"],[36.21152,-115.1463,\"desert\"],[36.21152,-115.14596,\"desert\"],[36.21152,-115.14563,\"desert\"],[36.21179,-115.15598,\"desert\"],[36.21179,-115.15565,\"desert\"],[36.21179,-115.15531,\"desert\"],[36.21179,-115.15498,\"desert\"],[36.21179,-115.15464,\"desert\"],[36.21179,-115.15431,\"desert\"],[36.21179,-115.15398,\"desert\"],[36.21179,-115.15364,\"desert\"],[36.21179,-115.15331,\"desert\"],[36.21179,-115.15297,\"desert\"],[36.21179,-115.15264,\"desert\"],[36.21179,-115.15231,\"desert\"],[36.21179,-115.15164,\"desert\"],[36.21179,-115.15131,\"desert\"],[36.21179,-115.15097,\"desert\"],[36.21179,-115.15064,\"desert\"],[36.21179,-115.1503,\"desert\"],[36.21179,-115.14997,\"desert\"],[36.21179,-115.14964,\"desert\"],[36.21179,-115.1493,\"desert\"],[36.21179,-115.14897,\"desert\"],[36.21179,-115.14863,\"desert\"],[36.21179,-115.1483,\"desert\"],[36.21179,-115.14797,\"desert\"],[36.21179,-115.14697,\"desert\"],[36.21179,-115.14663,\"desert\"],[36.21179,-115.1463,\"desert\"],[36.21179,-115.14596,\"desert\"],[36.21179,-115.14563,\"desert\"],[36.21207,-115.15598,\"desert\"],[36.21207,-115.15565,\"desert\"],[36.21207,-115.15531,\"desert\"],[36.21207,-115.15498,\"desert\"],[36.21207,-115.15464,\"desert\"],[36.21207,-115.15431,\"desert\"],[36.21207,-115.15398,\"desert\"],[36.21207,-115.15364,\"desert\"],[36.21207,-115.15331,\"desert\"],[36.21207,-115.15297,\"desert\"],[36.21207,-115.15264,\"desert\"],[36.21207,-115.15231,\"desert\"],[36.21207,-115.15164,\"desert\"],[36.21207,-115.15131,\"desert\"],[36.21207,-115.15097,\"desert\"],[36.21207,-115.15064,\"desert\"],[36.21207,-115.1503,\"desert\"],[36.21207,-115.14997,\"desert\"],[36.21207,-115.14964,\"desert\"],[36.21207,-115.1493,\"desert\"],[36.21207,-115.14897,\"desert\"],[36.21207,-115.14863,\"desert\"],[36.21207,-115.1483,\"desert\"],[36.21207,-115.14797,\"desert\"],[36.21234,-115.15565,\"desert\"],[36.21234,-115.15531,\"desert\"],[36.21234,-115.15498,\"desert\"],[36.21234,-115.15464,\"desert\"],[36.21234,-115.15431,\"desert\"],[36.21234,-115.15398,\"desert\"],[36.21234,-115.15364,\"desert\"],[36.21234,-115.15331,\"desert\"],[36.21234,-115.15297,\"desert\"],[36.21234,-115.15264,\"desert\"],[36.21234,-115.15231,\"desert\"],[36.21234,-115.15164,\"desert\"],[36.21234,-115.15131,\"desert\"],[36.21234,-115.15097,\"desert\"],[36.21234,-115.15064,\"desert\"],[36.21234,-115.1503,\"desert\"],[36.21234,-115.14997,\"desert\"],[36.21234,-115.14964,\"desert\"],[36.21234,-115.1493,\"desert\"],[36.21234,-115.14897,\"desert\"],[36.21234,-115.14863,\"desert\"],[36.21234,-115.1483,\"desert\"],[36.21234,-115.14797,\"desert\"],[36.21234,-115.14763,\"desert\"],[36.21261,-115.15531,\"desert\"],[36.21261,-115.15498,\"desert\"],[36.21261,-115.15464,\"desert\"],[36.21261,-115.15431,\"desert\"],[36.21261,-115.15398,\"desert\"],[36.21261,-115.15364,\"desert\"],[36.21261,-115.15331,\"desert\"],[36.21261,-115.15297,\"desert\"],[36.21261,-115.15264,\"desert\"],[36.21261,-115.15231,\"desert\"],[36.21261,-115.15164,\"desert\"],[36.21261,-115.15131,\"desert\"],[36.21261,-115.15097,\"desert\"],[36.21261,-115.15064,\"desert\"],[36.21261,-115.1503,\"desert\"],[36.21261,-115.14997,\"desert\"],[36.21261,-115.14964,\"desert\"],[36.21261,-115.1493,\"desert\"],[36.21261,-115.14897,\"desert\"],[36.21261,-115.14863,\"desert\"],[36.21261,-115.1483,\"desert\"],[36.21261,-115.14797,\"desert\"],[36.21261,-115.14763,\"desert\"],[36.21261,-115.1473,\"desert\"],[36.21261,-115.14697,\"desert\"],[36.21261,-115.14663,\"desert\"],[36.21261,-115.1463,\"desert\"],[36.21261,-115.14596,\"desert\"],[36.21261,-115.14563,\"desert\"],[36.21288,-115.15498,\"desert\"],[36.21288,-115.15464,\"desert\"],[36.21288,-115.15431,\"desert\"],[36.21288,-115.15398,\"desert\"],[36.21288,-115.15364,\"desert\"],[36.21288,-115.15331,\"desert\"],[36.21288,-115.15297,\"desert\"],[36.21288,-115.15264,\"desert\"],[36.21288,-115.15231,\"desert\"],[36.21288,-115.15164,\"desert\"],[36.21288,-115.15131,\"desert\"],[36.21288,-115.15097,\"desert\"],[36.21288,-115.15064,\"desert\"],[36.21288,-115.1503,\"desert\"],[36.21288,-115.14997,\"desert\"],[36.21288,-115.14964,\"desert\"],[36.21288,-115.1493,\"desert\"],[36.21288,-115.14897,\"desert\"],[36.21288,-115.14863,\"desert\"],[36.21288,-115.1483,\"desert\"],[36.21288,-115.14797,\"desert\"],[36.21288,-115.14763,\"desert\"],[36.21288,-115.1473,\"desert\"],[36.21288,-115.14697,\"desert\"],[36.21288,-115.14663,\"desert\"],[36.21288,-115.1463,\"desert\"],[36.21288,-115.14596,\"desert\"],[36.21315,-115.15431,\"desert\"],[36.21315,-115.15398,\"desert\"],[36.21315,-115.15364,\"desert\"],[36.21315,-115.15331,\"desert\"],[36.21315,-115.15297,\"desert\"],[36.21315,-115.15264,\"desert\"],[36.21315,-115.15231,\"desert\"],[36.21315,-115.15164,\"desert\"],[36.21315,-115.15131,\"desert\"],[36.21315,-115.15097,\"desert\"],[36.21315,-115.15064,\"desert\"],[36.21315,-115.1503,\"desert\"],[36.21315,-115.14997,\"desert\"],[36.21315,-115.14964,\"desert\"],[36.21315,-115.1493,\"desert\"],[36.21315,-115.14897,\"desert\"],[36.21315,-115.14863,\"desert\"],[36.21315,-115.1483,\"desert\"],[36.21315,-115.14797,\"desert\"],[36.21315,-115.14763,\"desert\"],[36.21315,-115.1473,\"desert\"],[36.21315,-115.14697,\"desert\"],[36.21315,-115.14663,\"desert\"],[36.21315,-115.1463,\"desert\"],[36.21315,-115.14596,\"desert\"],[36.21342,-115.15398,\"desert\"],[36.21342,-115.15364,\"desert\"],[36.21342,-115.15331,\"desert\"],[36.21342,-115.15297,\"desert\"],[36.21342,-115.15264,\"desert\"],[36.21342,-115.15231,\"desert\"],[36.21342,-115.15164,\"desert\"],[36.21342,-115.15131,\"desert\"],[36.21342,-115.15097,\"desert\"],[36.21342,-115.15064,\"desert\"],[36.21342,-115.1503,\"desert\"],[36.21342,-115.14997,\"desert\"],[36.21342,-115.14964,\"desert\"],[36.21342,-115.1493,\"desert\"],[36.21342,-115.14897,\"desert\"],[36.21342,-115.14863,\"desert\"],[36.21342,-115.1483,\"desert\"],[36.21342,-115.14797,\"desert\"],[36.21342,-115.14763,\"desert\"],[36.21342,-115.1473,\"desert\"],[36.21342,-115.14697,\"desert\"],[36.21342,-115.14663,\"desert\"]],\"882986ab5dfffff\":[[36.21071,-115.13562,\"desert\"],[36.21071,-115.13528,\"desert\"],[36.21071,-115.13495,\"desert\"],[36.21098,-115.14496,\"desert\"],[36.21098,-115.14463,\"desert\"],[36.21098,-115.14429,\"desert\"],[36.21098,-115.14396,\"desert\"],[36.21098,-115.14363,\"desert\"],[36.21098,-115.13562,\"desert\"],[36.21098,-115.13528,\"desert\"],[36.21098,-115.13495,\"desert\"],[36.21125,-115.14496,\"desert\"],[36.21125,-115.14463,\"desert\"],[36.21125,-115.14429,\"desert\"],[36.21125,-115.14396,\"desert\"],[36.21125,-115.14363,\"desert\"],[36.21125,-115.13562,\"desert\"],[36.21125,-115.13528,\"desert\"],[36.21125,-115.13495,\"desert\"],[36.21152,-115.1453,\"desert\"],[36.21152,-115.14496,\"desert\"],[36.21152,-115.14463,\"desert\"],[36.21152,-115.14429,\"desert\"],[36.21152,-115.14396,\"desert\"],[36.21152,-115.14363,\"desert\"],[36.21152,-115.13562,\"desert\"],[36.21152,-115.13528,\"desert\"],[36.21152,-115.13495,\"desert\"],[36.21179,-115.1453,\"desert\"],[36.21179,-115.14496,\"desert\"],[36.21179,-115.14463,\"desert\"],[36.21179,-115.14429,\"desert\"],[36.21179,-115.14396,\"desert\"],[36.21179,-115.14363,\"desert\"],[36.21179,-115.13562,\"desert\"],[36.21179,-115.13528,\"desert\"],[36.21179,-115.13495,\"desert\"],[36.21207,-115.13562,\"desert\"],[36.21207,-115.13528,\"desert\"],[36.21207,-115.13495,\"desert\"],[36.21288,-115.14563,\"desert\"],[36.21315,-115.14563,\"desert\"],[36.21315,-115.14463,\"desert\"],[36.21315,-115.14429,\"desert\"],[36.21315,-115.14396,\"desert\"],[36.21315,-115.14363,\"desert\"],[36.21315,-115.13562,\"desert\"],[36.21342,-115.13562,\"desert\"],[36.21369,-115.14463,\"desert\"],[36.21369,-115.14429,\"desert\"],[36.21369,-115.14396,\"desert\"],[36.21369,-115.14363,\"desert\"]],\"882986ab5bfffff\":[[36.21207,-115.15731,\"desert\"],[36.21207,-115.15698,\"desert\"],[36.21207,-115.15665,\"desert\"],[36.21207,-115.15631,\"desert\"],[36.21234,-115.15832,\"desert\"],[36.21234,-115.15798,\"desert\"],[36.21234,-115.15765,\"desert\"],[36.21234,-115.15731,\"desert\"],[36.21234,-115.15698,\"desert\"],[36.21234,-115.15665,\"desert\"],[36.21234,-115.15631,\"desert\"],[36.21234,-115.15598,\"desert\"],[36.21261,-115.15932,\"desert\"],[36.21261,-115.15898,\"desert\"],[36.21261,-115.15865,\"desert\"],[36.21261,-115.15832,\"desert\"],[36.21261,-115.15798,\"desert\"],[36.21261,-115.15765,\"desert\"],[36.21261,-115.15731,\"desert\"],[36.21261,-115.15698,\"desert\"],[36.21261,-115.15665,\"desert\"],[36.21261,-115.15631,\"desert\"],[36.21261,-115.15598,\"desert\"],[36.21261,-115.15565,\"desert\"],[36.21288,-115.16032,\"desert\"],[36.21288,-115.15999,\"desert\"],[36.21288,-115.15965,\"desert\"],[36.21288,-115.15932,\"desert\"],[36.21288,-115.15898,\"desert\"],[36.21288,-115.15865,\"desert\"],[36.21288,-115.15832,\"desert\"],[36.21288,-115.15798,\"desert\"],[36.21288,-115.15765,\"desert\"],[36.21288,-115.15731,\"desert\"],[36.21288,-115.15698,\"desert\"],[36.21288,-115.15665,\"desert\"],[36.21288,-115.15631,\"desert\"],[36.21288,-115.15598,\"desert\"],[36.21288,-115.15565,\"desert\"],[36.21288,-115.15531,\"desert\"],[36.21315,-115.16032,\"desert\"],[36.21315,-115.15999,\"desert\"],[36.21315,-115.15965,\"desert\"],[36.21315,-115.15932,\"desert\"],[36.21315,-115.15898,\"desert\"],[36.21315,-115.15865,\"desert\"],[36.21315,-115.15832,\"desert\"],[36.21315,-115.15798,\"desert\"],[36.21315,-115.15765,\"desert\"],[36.21315,-115.15731,\"desert\"],[36.21315,-115.15698,\"desert\"],[36.21315,-115.15665,\"desert\"],[36.21315,-115.15631,\"desert\"],[36.21315,-115.15598,\"desert\"],[36.21315,-115.15565,\"desert\"],[36.21315,-115.15531,\"desert\"],[36.21315,-115.15498,\"desert\"],[36.21315,-115.15464,\"desert\"],[36.21342,-115.16032,\"desert\"],[36.21342,-115.15999,\"desert\"],[36.21342,-115.15965,\"desert\"],[36.21342,-115.15932,\"desert\"],[36.21342,-115.15898,\"desert\"],[36.21342,-115.15865,\"desert\"],[36.21342,-115.15832,\"desert\"],[36.21342,-115.15798,\"desert\"],[36.21342,-115.15765,\"desert\"],[36.21342,-115.15731,\"desert\"],[36.21342,-115.15698,\"desert\"],[36.21342,-115.15665,\"desert\"],[36.21342,-115.15631,\"desert\"],[36.21342,-115.15598,\"desert\"],[36.21342,-115.15565,\"desert\"],[36.21342,-115.15531,\"desert\"],[36.21342,-115.15498,\"desert\"],[36.21342,-115.15464,\"desert\"],[36.21342,-115.15431,\"desert\"]],\"882986bacbfffff\":[[36.07908,-115.25446,\"desert\"],[36.07908,-115.25413,\"desert\"],[36.07935,-115.25446,\"desert\"],[36.07963,-115.25446,\"desert\"],[36.0799,-115.2568,\"desert\"],[36.0799,-115.2558,\"desert\"],[36.0799,-115.25546,\"desert\"],[36.0799,-115.25513,\"desert\"],[36.0799,-115.25479,\"desert\"],[36.08017,-115.2568,\"desert\"],[36.08017,-115.25646,\"desert\"],[36.08017,-115.25613,\"desert\"],[36.08017,-115.2558,\"desert\"],[36.08044,-115.2568,\"desert\"],[36.08044,-115.25646,\"desert\"],[36.08044,-115.25613,\"desert\"],[36.08044,-115.2558,\"desert\"],[36.08071,-115.2568,\"desert\"],[36.08071,-115.25646,\"desert\"],[36.08071,-115.25613,\"desert\"],[36.08071,-115.2558,\"desert\"]],\"882986aa0bfffff\":[[36.21397,-115.20839,\"desert\"],[36.21424,-115.20839,\"desert\"],[36.21451,-115.20872,\"desert\"],[36.21451,-115.20839,\"desert\"],[36.21478,-115.20906,\"desert\"],[36.21478,-115.20872,\"desert\"],[36.21478,-115.20839,\"desert\"],[36.21505,-115.20939,\"desert\"],[36.21505,-115.20906,\"desert\"],[36.21505,-115.20872,\"desert\"],[36.21505,-115.20839,\"desert\"],[36.21532,-115.20939,\"desert\"],[36.21532,-115.20906,\"desert\"],[36.21532,-115.20872,\"desert\"],[36.21532,-115.20839,\"desert\"],[36.21559,-115.20973,\"desert\"],[36.21559,-115.20939,\"desert\"],[36.21559,-115.20906,\"desert\"],[36.21559,-115.20872,\"desert\"],[36.21559,-115.20839,\"desert\"],[36.21587,-115.21006,\"desert\"],[36.21587,-115.20973,\"desert\"],[36.21587,-115.20939,\"desert\"],[36.21587,-115.20906,\"desert\"],[36.21587,-115.20872,\"desert\"],[36.21587,-115.20839,\"desert\"],[36.21614,-115.21039,\"desert\"],[36.21614,-115.21006,\"desert\"],[36.21614,-115.20973,\"desert\"],[36.21614,-115.20939,\"desert\"],[36.21614,-115.20906,\"desert\"],[36.21614,-115.20872,\"desert\"],[36.21614,-115.20839,\"desert\"],[36.21641,-115.21039,\"desert\"],[36.21641,-115.21006,\"desert\"],[36.21641,-115.20973,\"desert\"],[36.21641,-115.20939,\"desert\"],[36.21641,-115.20906,\"desert\"],[36.21641,-115.20872,\"desert\"],[36.21641,-115.20839,\"desert\"],[36.21668,-115.21073,\"desert\"],[36.21668,-115.21039,\"desert\"],[36.21668,-115.21006,\"desert\"],[36.21668,-115.20973,\"desert\"],[36.21668,-115.20939,\"desert\"],[36.21668,-115.20906,\"desert\"],[36.21668,-115.20872,\"desert\"],[36.21668,-115.20839,\"desert\"],[36.21804,-115.20572,\"desert\"]],\"882986b80dfffff\":[[36.10649,-115.16466,\"park\"],[36.11165,-115.16499,\"desert\"],[36.11165,-115.16466,\"desert\"],[36.11192,-115.16499,\"desert\"],[36.11192,-115.16466,\"desert\"]],\"882986a361fffff\":[[36.13906,-115.27349,\"desert\"],[36.13906,-115.27315,\"desert\"],[36.13906,-115.27282,\"desert\"],[36.13906,-115.27249,\"desert\"],[36.13906,-115.27215,\"desert\"],[36.13906,-115.27182,\"desert\"],[36.13933,-115.27349,\"desert\"],[36.13933,-115.27315,\"desert\"],[36.13933,-115.27282,\"desert\"],[36.13933,-115.27249,\"desert\"],[36.13933,-115.27215,\"desert\"],[36.13933,-115.27182,\"desert\"],[36.1396,-115.27349,\"desert\"],[36.1396,-115.27315,\"desert\"],[36.1396,-115.27282,\"desert\"],[36.1396,-115.27249,\"desert\"],[36.1396,-115.27215,\"desert\"],[36.1396,-115.27182,\"desert\"],[36.13988,-115.27349,\"desert\"],[36.13988,-115.27315,\"desert\"],[36.13988,-115.27282,\"desert\"],[36.13988,-115.27249,\"desert\"]],\"882986a95dfffff\":[[36.26635,-115.11826,\"desert\"],[36.26635,-115.11792,\"desert\"],[36.26662,-115.11826,\"desert\"],[36.26662,-115.11792,\"desert\"],[36.26689,-115.11826,\"desert\"],[36.2696,-115.11892,\"desert\"],[36.2696,-115.11859,\"desert\"],[36.26987,-115.11892,\"desert\"],[36.26987,-115.11859,\"desert\"],[36.27014,-115.11892,\"desert\"],[36.27014,-115.11859,\"desert\"],[36.27014,-115.11826,\"desert\"],[36.27042,-115.11892,\"desert\"],[36.27042,-115.11859,\"desert\"],[36.27042,-115.11826,\"desert\"],[36.27042,-115.11792,\"desert\"],[36.27069,-115.11892,\"desert\"],[36.27069,-115.11859,\"desert\"],[36.27069,-115.11826,\"desert\"],[36.27069,-115.11792,\"desert\"],[36.27096,-115.11892,\"desert\"],[36.27096,-115.11859,\"desert\"],[36.27096,-115.11826,\"desert\"],[36.27096,-115.11792,\"desert\"],[36.27123,-115.11892,\"desert\"],[36.27123,-115.11859,\"desert\"],[36.27123,-115.11826,\"desert\"],[36.27123,-115.11792,\"desert\"],[36.2715,-115.11892,\"desert\"],[36.2715,-115.11859,\"desert\"],[36.2715,-115.11826,\"desert\"],[36.2715,-115.11792,\"desert\"],[36.27177,-115.11892,\"desert\"],[36.27177,-115.11859,\"desert\"],[36.27177,-115.11826,\"desert\"],[36.27177,-115.11792,\"desert\"],[36.27204,-115.11892,\"desert\"],[36.27204,-115.11859,\"desert\"],[36.27204,-115.11826,\"desert\"],[36.27204,-115.11792,\"desert\"],[36.27232,-115.11859,\"desert\"],[36.27232,-115.11826,\"desert\"],[36.27232,-115.11792,\"desert\"],[36.27259,-115.11826,\"desert\"],[36.27259,-115.11792,\"desert\"],[36.27286,-115.11792,\"desert\"]],\"882986ab37fffff\":[[36.2658,-115.11826,\"desert\"],[36.2658,-115.11792,\"desert\"],[36.26607,-115.11826,\"desert\"],[36.26607,-115.11792,\"desert\"]],\"882986a95bfffff\":[[36.2734,-115.13595,\"desert\"],[36.2734,-115.13562,\"desert\"],[36.27367,-115.13595,\"desert\"],[36.27367,-115.13562,\"desert\"],[36.27503,-115.13194,\"desert\"],[36.27503,-115.13161,\"desert\"],[36.27503,-115.13128,\"desert\"]],\"882986a941fffff\":[[36.26716,-115.09789,\"desert\"],[36.26716,-115.09756,\"desert\"],[36.26716,-115.09722,\"desert\"],[36.26743,-115.09789,\"desert\"],[36.26743,-115.09756,\"desert\"],[36.26743,-115.09722,\"desert\"],[36.2677,-115.09789,\"desert\"],[36.2677,-115.09756,\"desert\"],[36.2677,-115.09722,\"desert\"],[36.26797,-115.09789,\"desert\"],[36.26797,-115.09756,\"desert\"],[36.26797,-115.09722,\"desert\"],[36.26824,-115.09789,\"desert\"],[36.26824,-115.09756,\"desert\"],[36.26824,-115.09722,\"desert\"],[36.26852,-115.09789,\"desert\"],[36.26852,-115.09756,\"desert\"]],\"882986a909fffff\":[[36.2772,-115.10257,\"desert\"],[36.2772,-115.10223,\"desert\"],[36.2772,-115.1019,\"desert\"],[36.2772,-115.10156,\"desert\"],[36.27747,-115.10257,\"desert\"],[36.27747,-115.10223,\"desert\"],[36.27747,-115.1019,\"desert\"],[36.27747,-115.10156,\"desert\"],[36.27774,-115.10257,\"desert\"],[36.27774,-115.10223,\"desert\"],[36.27774,-115.1019,\"desert\"],[36.27774,-115.10156,\"desert\"],[36.27856,-115.1049,\"desert\"],[36.27856,-115.10457,\"desert\"],[36.27883,-115.1049,\"desert\"],[36.27883,-115.10457,\"desert\"],[36.27883,-115.10423,\"desert\"],[36.27883,-115.1039,\"desert\"],[36.27883,-115.09923,\"desert\"],[36.2791,-115.1049,\"desert\"],[36.2791,-115.10457,\"desert\"],[36.2791,-115.10423,\"desert\"],[36.2791,-115.1039,\"desert\"],[36.2791,-115.09923,\"desert\"],[36.27937,-115.10524,\"desert\"],[36.27937,-115.1049,\"desert\"],[36.27937,-115.10457,\"desert\"],[36.27937,-115.10423,\"desert\"],[36.27937,-115.1039,\"desert\"],[36.27937,-115.09923,\"desert\"],[36.27964,-115.10524,\"desert\"],[36.27964,-115.1049,\"desert\"],[36.27964,-115.10457,\"desert\"],[36.27964,-115.10423,\"desert\"],[36.27964,-115.1039,\"desert\"],[36.27964,-115.09923,\"desert\"]],\"882986aa47fffff\":[[36.20365,-115.19003,\"desert\"],[36.20365,-115.1897,\"desert\"],[36.20365,-115.18936,\"desert\"],[36.20365,-115.18903,\"desert\"],[36.20365,-115.18869,\"desert\"],[36.20365,-115.18836,\"desert\"],[36.20392,-115.1907,\"desert\"],[36.20392,-115.19036,\"desert\"],[36.20392,-115.19003,\"desert\"],[36.20392,-115.1897,\"desert\"],[36.20392,-115.18936,\"desert\"],[36.20392,-115.18903,\"desert\"],[36.20392,-115.18869,\"desert\"],[36.20392,-115.18836,\"desert\"],[36.2042,-115.19036,\"desert\"],[36.2042,-115.18936,\"desert\"],[36.2042,-115.18903,\"desert\"],[36.2042,-115.18869,\"desert\"],[36.20447,-115.18903,\"desert\"]],\"882986aa6bfffff\":[[36.20148,-115.18435,\"desert\"],[36.20175,-115.18435,\"desert\"],[36.20175,-115.18402,\"desert\"],[36.20202,-115.18435,\"desert\"],[36.20202,-115.18402,\"desert\"],[36.20202,-115.18369,\"desert\"],[36.20338,-115.18068,\"desert\"],[36.20338,-115.18035,\"desert\"],[36.20338,-115.18001,\"desert\"],[36.20338,-115.17968,\"desert\"],[36.20365,-115.18803,\"desert\"],[36.20365,-115.18769,\"desert\"],[36.20365,-115.18736,\"desert\"],[36.20365,-115.18703,\"desert\"],[36.20365,-115.18669,\"desert\"],[36.20365,-115.18636,\"desert\"],[36.20365,-115.18569,\"desert\"],[36.20365,-115.18536,\"desert\"],[36.20365,-115.18502,\"desert\"],[36.20365,-115.18469,\"desert\"],[36.20365,-115.18435,\"desert\"],[36.20365,-115.18402,\"desert\"],[36.20365,-115.17968,\"desert\"],[36.20392,-115.18703,\"desert\"],[36.20392,-115.18669,\"desert\"],[36.20392,-115.18536,\"desert\"],[36.20392,-115.18502,\"desert\"],[36.20392,-115.18469,\"desert\"],[36.20392,-115.18435,\"desert\"],[36.20392,-115.18402,\"desert\"],[36.20392,-115.17968,\"desert\"],[36.2042,-115.18502,\"desert\"],[36.2042,-115.18469,\"desert\"],[36.2042,-115.18435,\"desert\"],[36.2042,-115.18402,\"desert\"],[36.2042,-115.17968,\"desert\"],[36.20447,-115.18536,\"desert\"],[36.20447,-115.18502,\"desert\"],[36.20447,-115.18469,\"desert\"],[36.20447,-115.18435,\"desert\"],[36.20447,-115.17968,\"desert\"],[36.20474,-115.18536,\"desert\"],[36.20474,-115.18502,\"desert\"],[36.20474,-115.18469,\"desert\"],[36.20474,-115.18269,\"desert\"],[36.20474,-115.18235,\"desert\"],[36.20474,-115.18202,\"desert\"],[36.20474,-115.18168,\"desert\"],[36.20474,-115.18135,\"desert\"],[36.20474,-115.18102,\"desert\"],[36.20474,-115.18068,\"desert\"],[36.20474,-115.18035,\"desert\"],[36.20474,-115.18001,\"desert\"],[36.20474,-115.17968,\"desert\"],[36.20501,-115.18502,\"desert\"],[36.20501,-115.18335,\"desert\"],[36.20501,-115.18302,\"desert\"],[36.20501,-115.18269,\"desert\"],[36.20501,-115.18235,\"desert\"],[36.20501,-115.18202,\"desert\"],[36.20501,-115.18168,\"desert\"],[36.20501,-115.18135,\"desert\"],[36.20501,-115.18102,\"desert\"],[36.20501,-115.18068,\"desert\"],[36.20501,-115.18035,\"desert\"],[36.20501,-115.18001,\"desert\"],[36.20501,-115.17968,\"desert\"],[36.20528,-115.18369,\"desert\"],[36.20528,-115.18335,\"desert\"],[36.20528,-115.18302,\"desert\"],[36.20528,-115.18269,\"desert\"],[36.20528,-115.18235,\"desert\"],[36.20528,-115.18202,\"desert\"],[36.20528,-115.18168,\"desert\"],[36.20528,-115.18135,\"desert\"],[36.20528,-115.18102,\"desert\"],[36.20528,-115.18068,\"desert\"],[36.20528,-115.18035,\"desert\"],[36.20528,-115.18001,\"desert\"],[36.20528,-115.17968,\"desert\"],[36.20555,-115.18402,\"desert\"],[36.20555,-115.18369,\"desert\"],[36.20555,-115.18335,\"desert\"],[36.20555,-115.18302,\"desert\"],[36.20555,-115.18269,\"desert\"],[36.20555,-115.18235,\"desert\"],[36.20555,-115.18202,\"desert\"],[36.20555,-115.18168,\"desert\"],[36.20555,-115.18135,\"desert\"],[36.20555,-115.18102,\"desert\"],[36.20555,-115.18068,\"desert\"],[36.20555,-115.18035,\"desert\"],[36.20555,-115.18001,\"desert\"],[36.20555,-115.17968,\"desert\"],[36.20582,-115.18435,\"desert\"],[36.20582,-115.18402,\"desert\"],[36.20582,-115.18369,\"desert\"],[36.20582,-115.18335,\"desert\"],[36.20582,-115.18302,\"desert\"],[36.20582,-115.18269,\"desert\"],[36.20582,-115.18235,\"desert\"],[36.20582,-115.18202,\"desert\"],[36.20582,-115.18168,\"desert\"],[36.20582,-115.18135,\"desert\"],[36.20582,-115.18102,\"desert\"],[36.20582,-115.18068,\"desert\"],[36.20582,-115.18035,\"desert\"],[36.20582,-115.18001,\"desert\"],[36.20582,-115.17968,\"desert\"],[36.2061,-115.18469,\"desert\"],[36.2061,-115.18435,\"desert\"],[36.2061,-115.18402,\"desert\"],[36.2061,-115.18369,\"desert\"],[36.2061,-115.18335,\"desert\"],[36.2061,-115.18302,\"desert\"],[36.2061,-115.18001,\"desert\"],[36.2061,-115.17968,\"desert\"]],\"882986aa0dfffff\":[[36.20962,-115.19036,\"desert\"],[36.2099,-115.1907,\"desert\"],[36.2099,-115.19036,\"desert\"],[36.21017,-115.19103,\"desert\"],[36.21017,-115.1907,\"desert\"],[36.21017,-115.19036,\"desert\"],[36.21044,-115.19137,\"desert\"],[36.21044,-115.19103,\"desert\"],[36.21044,-115.1907,\"desert\"],[36.21044,-115.19036,\"desert\"],[36.21071,-115.19303,\"desert\"],[36.21071,-115.19137,\"desert\"],[36.21071,-115.19103,\"desert\"],[36.21071,-115.1907,\"desert\"],[36.21071,-115.19036,\"desert\"],[36.21071,-115.18602,\"desert\"],[36.21071,-115.18569,\"desert\"],[36.21071,-115.18536,\"desert\"],[36.21071,-115.18502,\"desert\"],[36.21071,-115.18469,\"desert\"],[36.21071,-115.18435,\"desert\"],[36.21098,-115.19337,\"desert\"],[36.21098,-115.19303,\"desert\"],[36.21098,-115.1927,\"desert\"],[36.21098,-115.19103,\"desert\"],[36.21098,-115.1907,\"desert\"],[36.21098,-115.19036,\"desert\"],[36.21098,-115.19003,\"desert\"],[36.21098,-115.1897,\"desert\"],[36.21098,-115.18936,\"desert\"],[36.21098,-115.18903,\"desert\"],[36.21098,-115.18869,\"desert\"],[36.21098,-115.18602,\"desert\"],[36.21098,-115.18569,\"desert\"],[36.21098,-115.18536,\"desert\"],[36.21098,-115.18502,\"desert\"],[36.21098,-115.18469,\"desert\"],[36.21098,-115.18435,\"desert\"],[36.21125,-115.1937,\"desert\"],[36.21125,-115.19337,\"desert\"],[36.21125,-115.19303,\"desert\"],[36.21125,-115.1927,\"desert\"],[36.21125,-115.19237,\"desert\"],[36.21125,-115.1907,\"desert\"],[36.21125,-115.19036,\"desert\"],[36.21125,-115.19003,\"desert\"],[36.21125,-115.1897,\"desert\"],[36.21125,-115.18936,\"desert\"],[36.21125,-115.18903,\"desert\"],[36.21125,-115.18869,\"desert\"],[36.21125,-115.18602,\"desert\"],[36.21125,-115.18569,\"desert\"],[36.21125,-115.18536,\"desert\"],[36.21125,-115.18502,\"desert\"],[36.21125,-115.18469,\"desert\"],[36.21125,-115.18435,\"desert\"],[36.21152,-115.19437,\"desert\"],[36.21152,-115.19404,\"desert\"],[36.21152,-115.1937,\"desert\"],[36.21152,-115.19337,\"desert\"],[36.21152,-115.19303,\"desert\"],[36.21152,-115.1927,\"desert\"],[36.21152,-115.19237,\"desert\"],[36.21152,-115.19203,\"desert\"],[36.21152,-115.19036,\"desert\"],[36.21152,-115.19003,\"desert\"],[36.21152,-115.1897,\"desert\"],[36.21152,-115.18936,\"desert\"],[36.21152,-115.18903,\"desert\"],[36.21152,-115.18869,\"desert\"],[36.21152,-115.18569,\"desert\"],[36.21152,-115.18536,\"desert\"],[36.21152,-115.18502,\"desert\"],[36.21152,-115.18469,\"desert\"],[36.21179,-115.19437,\"desert\"],[36.21179,-115.19404,\"desert\"],[36.21179,-115.1937,\"desert\"],[36.21179,-115.19337,\"desert\"],[36.21179,-115.19303,\"desert\"],[36.21179,-115.1927,\"desert\"],[36.21179,-115.19237,\"desert\"],[36.21179,-115.19203,\"desert\"],[36.21179,-115.1917,\"desert\"],[36.21179,-115.19003,\"desert\"],[36.21179,-115.1897,\"desert\"],[36.21179,-115.18936,\"desert\"],[36.21179,-115.18903,\"desert\"],[36.21179,-115.18869,\"desert\"],[36.21179,-115.18836,\"desert\"],[36.21179,-115.18569,\"desert\"],[36.21179,-115.18536,\"desert\"],[36.21179,-115.18502,\"desert\"],[36.21179,-115.18469,\"desert\"],[36.21207,-115.1937,\"desert\"],[36.21207,-115.19337,\"desert\"],[36.21207,-115.19303,\"desert\"],[36.21207,-115.1927,\"desert\"],[36.21207,-115.19237,\"desert\"],[36.21207,-115.19203,\"desert\"],[36.21207,-115.1917,\"desert\"],[36.21207,-115.19137,\"desert\"],[36.21207,-115.1897,\"desert\"],[36.21207,-115.18936,\"desert\"],[36.21207,-115.18903,\"desert\"],[36.21207,-115.18869,\"desert\"],[36.21207,-115.18836,\"desert\"],[36.21207,-115.18569,\"desert\"],[36.21207,-115.18536,\"desert\"],[36.21207,-115.18502,\"desert\"],[36.21207,-115.18469,\"desert\"],[36.21234,-115.19337,\"desert\"],[36.21234,-115.19303,\"desert\"],[36.21234,-115.1927,\"desert\"],[36.21234,-115.19237,\"desert\"],[36.21234,-115.19203,\"desert\"],[36.21234,-115.1917,\"desert\"],[36.21234,-115.19137,\"desert\"],[36.21234,-115.19103,\"desert\"],[36.21234,-115.18936,\"desert\"],[36.21234,-115.18903,\"desert\"],[36.21234,-115.18869,\"desert\"],[36.21234,-115.18569,\"desert\"],[36.21234,-115.18536,\"desert\"],[36.21234,-115.18502,\"desert\"],[36.21234,-115.18469,\"desert\"],[36.21261,-115.18569,\"desert\"],[36.21261,-115.18536,\"desert\"],[36.21261,-115.18502,\"desert\"],[36.21261,-115.18469,\"desert\"],[36.21288,-115.18569,\"desert\"],[36.21288,-115.18536,\"desert\"],[36.21288,-115.18502,\"desert\"],[36.21315,-115.18536,\"desert\"],[36.21315,-115.18502,\"desert\"],[36.21397,-115.19437,\"desert\"],[36.21397,-115.19404,\"desert\"],[36.21424,-115.19404,\"desert\"],[36.21424,-115.1937,\"desert\"],[36.21424,-115.19337,\"desert\"],[36.21424,-115.19303,\"desert\"],[36.21424,-115.1927,\"desert\"],[36.21424,-115.19237,\"desert\"],[36.21424,-115.19203,\"desert\"],[36.21424,-115.1917,\"desert\"],[36.21424,-115.19137,\"desert\"],[36.21424,-115.19103,\"desert\"],[36.21424,-115.1907,\"desert\"],[36.21424,-115.19036,\"desert\"],[36.21424,-115.19003,\"desert\"],[36.21424,-115.1897,\"desert\"],[36.21424,-115.18936,\"desert\"],[36.21424,-115.18903,\"desert\"],[36.21424,-115.18869,\"desert\"],[36.21424,-115.18836,\"desert\"],[36.21424,-115.18803,\"desert\"],[36.21424,-115.18769,\"desert\"],[36.21424,-115.18736,\"desert\"],[36.21424,-115.18703,\"desert\"],[36.21424,-115.18669,\"desert\"],[36.21424,-115.18636,\"desert\"],[36.21424,-115.18602,\"desert\"],[36.21424,-115.18569,\"desert\"],[36.21424,-115.18536,\"desert\"],[36.21451,-115.1937,\"desert\"],[36.21451,-115.19337,\"desert\"],[36.21451,-115.19303,\"desert\"],[36.21451,-115.1927,\"desert\"],[36.21451,-115.19237,\"desert\"],[36.21451,-115.19203,\"desert\"],[36.21451,-115.1917,\"desert\"],[36.21451,-115.19137,\"desert\"],[36.21451,-115.19103,\"desert\"],[36.21451,-115.1907,\"desert\"],[36.21451,-115.19036,\"desert\"],[36.21451,-115.19003,\"desert\"],[36.21451,-115.1897,\"desert\"],[36.21451,-115.18936,\"desert\"],[36.21451,-115.18903,\"desert\"],[36.21451,-115.18869,\"desert\"],[36.21451,-115.18836,\"desert\"],[36.21451,-115.18803,\"desert\"],[36.21451,-115.18769,\"desert\"],[36.21451,-115.18736,\"desert\"],[36.21451,-115.18703,\"desert\"],[36.21451,-115.18669,\"desert\"],[36.21451,-115.18636,\"desert\"],[36.21451,-115.18602,\"desert\"],[36.21451,-115.18569,\"desert\"],[36.21451,-115.18536,\"desert\"],[36.21478,-115.19303,\"desert\"],[36.21478,-115.1927,\"desert\"],[36.21478,-115.19237,\"desert\"],[36.21478,-115.19203,\"desert\"],[36.21478,-115.1917,\"desert\"],[36.21478,-115.19137,\"desert\"],[36.21478,-115.19103,\"desert\"],[36.21478,-115.1907,\"desert\"],[36.21478,-115.19036,\"desert\"],[36.21478,-115.19003,\"desert\"],[36.21478,-115.1897,\"desert\"],[36.21478,-115.18936,\"desert\"],[36.21478,-115.18903,\"desert\"],[36.21478,-115.18869,\"desert\"],[36.21478,-115.18836,\"desert\"],[36.21478,-115.18803,\"desert\"],[36.21478,-115.18769,\"desert\"],[36.21478,-115.18736,\"desert\"],[36.21478,-115.18703,\"desert\"],[36.21478,-115.18669,\"desert\"],[36.21478,-115.18636,\"desert\"],[36.21478,-115.18602,\"desert\"],[36.21478,-115.18569,\"desert\"],[36.21478,-115.18536,\"desert\"],[36.21505,-115.1927,\"desert\"],[36.21505,-115.19237,\"desert\"],[36.21505,-115.19203,\"desert\"],[36.21505,-115.1917,\"desert\"],[36.21505,-115.19137,\"desert\"],[36.21505,-115.19103,\"desert\"],[36.21505,-115.1907,\"desert\"],[36.21505,-115.19036,\"desert\"],[36.21505,-115.19003,\"desert\"],[36.21505,-115.1897,\"desert\"],[36.21505,-115.18936,\"desert\"],[36.21505,-115.18903,\"desert\"],[36.21505,-115.18869,\"desert\"],[36.21505,-115.18836,\"desert\"],[36.21505,-115.18803,\"desert\"],[36.21505,-115.18769,\"desert\"],[36.21505,-115.18736,\"desert\"],[36.21505,-115.18703,\"desert\"],[36.21505,-115.18669,\"desert\"],[36.21505,-115.18636,\"desert\"],[36.21532,-115.19237,\"desert\"],[36.21532,-115.19203,\"desert\"],[36.21532,-115.1917,\"desert\"],[36.21532,-115.19137,\"desert\"],[36.21532,-115.19103,\"desert\"],[36.21532,-115.1907,\"desert\"],[36.21532,-115.19036,\"desert\"],[36.21532,-115.19003,\"desert\"],[36.21532,-115.1897,\"desert\"],[36.21532,-115.18936,\"desert\"],[36.21532,-115.18903,\"desert\"],[36.21532,-115.18869,\"desert\"],[36.21532,-115.18836,\"desert\"],[36.21532,-115.18803,\"desert\"],[36.21532,-115.18769,\"desert\"],[36.21532,-115.18736,\"desert\"],[36.21559,-115.19203,\"desert\"],[36.21559,-115.1917,\"desert\"],[36.21559,-115.19137,\"desert\"],[36.21559,-115.19103,\"desert\"],[36.21559,-115.1907,\"desert\"],[36.21559,-115.19036,\"desert\"],[36.21559,-115.19003,\"desert\"],[36.21559,-115.1897,\"desert\"],[36.21559,-115.18936,\"desert\"],[36.21559,-115.18903,\"desert\"],[36.21559,-115.18869,\"desert\"]],\"882986aa09fffff\":[[36.21397,-115.20071,\"desert\"],[36.21397,-115.20038,\"desert\"],[36.21397,-115.20004,\"desert\"],[36.21424,-115.20105,\"desert\"]],\"882986aa01fffff\":[[36.21397,-115.19971,\"desert\"],[36.21397,-115.19938,\"desert\"],[36.21397,-115.19904,\"desert\"],[36.21397,-115.19871,\"desert\"],[36.21397,-115.19838,\"desert\"],[36.21397,-115.19804,\"desert\"],[36.21397,-115.19771,\"desert\"],[36.21397,-115.19737,\"desert\"],[36.21397,-115.19704,\"desert\"],[36.21397,-115.19671,\"desert\"],[36.21397,-115.19637,\"desert\"],[36.21397,-115.19604,\"desert\"],[36.21397,-115.19571,\"desert\"],[36.21397,-115.19537,\"desert\"],[36.21397,-115.19504,\"desert\"],[36.21397,-115.1947,\"desert\"],[36.21424,-115.20071,\"desert\"],[36.21424,-115.20038,\"desert\"],[36.21424,-115.20004,\"desert\"],[36.21424,-115.19971,\"desert\"],[36.21424,-115.19938,\"desert\"],[36.21424,-115.19904,\"desert\"],[36.21424,-115.19871,\"desert\"],[36.21424,-115.19838,\"desert\"],[36.21424,-115.19804,\"desert\"],[36.21424,-115.19771,\"desert\"],[36.21424,-115.19737,\"desert\"],[36.21424,-115.19704,\"desert\"],[36.21424,-115.19671,\"desert\"],[36.21424,-115.19637,\"desert\"],[36.21424,-115.19604,\"desert\"],[36.21424,-115.19571,\"desert\"],[36.21424,-115.19537,\"desert\"],[36.21424,-115.19504,\"desert\"],[36.21424,-115.1947,\"desert\"],[36.21424,-115.19437,\"desert\"],[36.21451,-115.20138,\"desert\"],[36.21451,-115.20105,\"desert\"],[36.21451,-115.20071,\"desert\"],[36.21451,-115.20038,\"desert\"],[36.21451,-115.20004,\"desert\"],[36.21451,-115.19971,\"desert\"],[36.21451,-115.19938,\"desert\"],[36.21451,-115.19904,\"desert\"],[36.21451,-115.19871,\"desert\"],[36.21451,-115.19838,\"desert\"],[36.21451,-115.19804,\"desert\"],[36.21451,-115.19771,\"desert\"],[36.21451,-115.19737,\"desert\"],[36.21451,-115.19704,\"desert\"],[36.21451,-115.19671,\"desert\"],[36.21451,-115.19637,\"desert\"],[36.21451,-115.19604,\"desert\"],[36.21451,-115.19571,\"desert\"],[36.21451,-115.19537,\"desert\"],[36.21451,-115.19504,\"desert\"],[36.21451,-115.1947,\"desert\"],[36.21451,-115.19437,\"desert\"],[36.21451,-115.19404,\"desert\"],[36.21478,-115.20171,\"desert\"],[36.21478,-115.20138,\"desert\"],[36.21478,-115.20105,\"desert\"],[36.21478,-115.20071,\"desert\"],[36.21478,-115.20038,\"desert\"],[36.21478,-115.20004,\"desert\"],[36.21478,-115.19971,\"desert\"],[36.21478,-115.19938,\"desert\"],[36.21478,-115.19737,\"desert\"],[36.21478,-115.19704,\"desert\"],[36.21478,-115.19671,\"desert\"],[36.21478,-115.19637,\"desert\"],[36.21478,-115.19604,\"desert\"],[36.21478,-115.19571,\"desert\"],[36.21478,-115.19537,\"desert\"],[36.21478,-115.19504,\"desert\"],[36.21478,-115.1947,\"desert\"],[36.21478,-115.19437,\"desert\"],[36.21478,-115.19404,\"desert\"],[36.21478,-115.1937,\"desert\"],[36.21478,-115.19337,\"desert\"],[36.21505,-115.20138,\"desert\"],[36.21505,-115.20105,\"desert\"],[36.21505,-115.20071,\"desert\"],[36.21505,-115.20038,\"desert\"],[36.21505,-115.20004,\"desert\"],[36.21505,-115.19971,\"desert\"],[36.21505,-115.19437,\"desert\"],[36.21505,-115.19404,\"desert\"],[36.21505,-115.1937,\"desert\"],[36.21505,-115.19337,\"desert\"],[36.21505,-115.19303,\"desert\"],[36.21532,-115.20105,\"desert\"],[36.21532,-115.20071,\"desert\"],[36.21532,-115.20038,\"desert\"],[36.21532,-115.20004,\"desert\"],[36.21532,-115.19437,\"desert\"],[36.21532,-115.19404,\"desert\"],[36.21532,-115.1937,\"desert\"],[36.21532,-115.19337,\"desert\"],[36.21532,-115.19303,\"desert\"],[36.21532,-115.1927,\"desert\"],[36.21559,-115.20071,\"desert\"],[36.21559,-115.20038,\"desert\"],[36.21559,-115.19437,\"desert\"],[36.21559,-115.19404,\"desert\"],[36.21559,-115.1937,\"desert\"],[36.21559,-115.19337,\"desert\"],[36.21559,-115.19303,\"desert\"],[36.21559,-115.1927,\"desert\"],[36.21559,-115.19237,\"desert\"]],\"882986aa05fffff\":[[36.21505,-115.18602,\"desert\"],[36.21505,-115.18569,\"desert\"],[36.21505,-115.18536,\"desert\"],[36.21505,-115.18502,\"desert\"],[36.21532,-115.18703,\"desert\"],[36.21532,-115.18669,\"desert\"],[36.21532,-115.18636,\"desert\"],[36.21532,-115.18602,\"desert\"],[36.21532,-115.18569,\"desert\"],[36.21532,-115.18536,\"desert\"],[36.21532,-115.18502,\"desert\"],[36.21532,-115.18469,\"desert\"],[36.21532,-115.18435,\"desert\"],[36.21559,-115.18836,\"desert\"],[36.21559,-115.18803,\"desert\"],[36.21559,-115.18769,\"desert\"],[36.21559,-115.18736,\"desert\"],[36.21559,-115.18703,\"desert\"],[36.21559,-115.18669,\"desert\"],[36.21559,-115.18636,\"desert\"],[36.21559,-115.18602,\"desert\"],[36.21559,-115.18569,\"desert\"],[36.21559,-115.18536,\"desert\"],[36.21559,-115.18502,\"desert\"],[36.21559,-115.18469,\"desert\"],[36.21559,-115.18435,\"desert\"],[36.21559,-115.18402,\"desert\"],[36.21587,-115.18703,\"desert\"],[36.21587,-115.18669,\"desert\"],[36.21587,-115.18636,\"desert\"],[36.21587,-115.18602,\"desert\"],[36.21587,-115.18569,\"desert\"],[36.21587,-115.18536,\"desert\"],[36.21587,-115.18502,\"desert\"],[36.21587,-115.18469,\"desert\"],[36.21587,-115.18435,\"desert\"],[36.21587,-115.18402,\"desert\"],[36.21587,-115.18369,\"desert\"],[36.21614,-115.18335,\"desert\"],[36.21641,-115.18335,\"desert\"],[36.21641,-115.18302,\"desert\"],[36.21668,-115.18335,\"desert\"],[36.21668,-115.18302,\"desert\"],[36.21668,-115.18269,\"desert\"],[36.21695,-115.18335,\"desert\"],[36.21695,-115.18302,\"desert\"],[36.21695,-115.18269,\"desert\"],[36.21695,-115.18235,\"desert\"],[36.21695,-115.18202,\"desert\"]],\"882986aa99fffff\":[[36.26906,-115.27583,\"desert\"],[36.26906,-115.27549,\"desert\"],[36.26906,-115.27516,\"desert\"],[36.26906,-115.27482,\"desert\"],[36.26933,-115.27583,\"desert\"],[36.26933,-115.27549,\"desert\"],[36.26933,-115.27516,\"desert\"],[36.26933,-115.27482,\"desert\"],[36.2696,-115.27583,\"desert\"],[36.2696,-115.27549,\"desert\"],[36.2696,-115.27516,\"desert\"],[36.2696,-115.27482,\"desert\"],[36.27069,-115.27683,\"desert\"],[36.27069,-115.27649,\"desert\"],[36.27096,-115.27649,\"desert\"]],\"882986aa91fffff\":[[36.27123,-115.27649,\"desert\"],[36.2715,-115.27649,\"desert\"],[36.27177,-115.27649,\"desert\"],[36.27204,-115.27649,\"desert\"],[36.27232,-115.27649,\"desert\"],[36.27259,-115.27683,\"desert\"],[36.27259,-115.27649,\"desert\"],[36.27286,-115.27683,\"desert\"],[36.27286,-115.27649,\"desert\"],[36.27313,-115.27683,\"desert\"],[36.27313,-115.27649,\"desert\"],[36.27422,-115.27716,\"desert\"],[36.27422,-115.27683,\"desert\"],[36.27422,-115.27649,\"desert\"],[36.27422,-115.27616,\"desert\"],[36.27449,-115.27716,\"desert\"],[36.27449,-115.27683,\"desert\"],[36.27449,-115.27649,\"desert\"],[36.27449,-115.27616,\"desert\"],[36.27449,-115.27583,\"desert\"],[36.27449,-115.27549,\"desert\"],[36.27449,-115.27516,\"desert\"],[36.27449,-115.27482,\"desert\"],[36.27476,-115.27716,\"desert\"],[36.27476,-115.27683,\"desert\"],[36.27476,-115.27649,\"desert\"],[36.27503,-115.27716,\"desert\"],[36.27503,-115.27683,\"desert\"],[36.27503,-115.27649,\"desert\"],[36.2753,-115.27749,\"desert\"],[36.2753,-115.27716,\"desert\"],[36.2753,-115.27683,\"desert\"],[36.2753,-115.27649,\"desert\"],[36.27557,-115.27749,\"desert\"],[36.27557,-115.27716,\"desert\"],[36.27557,-115.27683,\"desert\"],[36.27557,-115.27649,\"desert\"],[36.27557,-115.27616,\"desert\"],[36.27584,-115.27749,\"desert\"],[36.27584,-115.27716,\"desert\"],[36.27584,-115.27683,\"desert\"],[36.27584,-115.27649,\"desert\"],[36.27584,-115.27616,\"desert\"],[36.27584,-115.27583,\"desert\"],[36.27584,-115.27549,\"desert\"],[36.27584,-115.27516,\"desert\"],[36.27584,-115.27482,\"desert\"],[36.27612,-115.27749,\"desert\"],[36.27612,-115.27716,\"desert\"],[36.27612,-115.27683,\"desert\"],[36.27612,-115.27649,\"desert\"],[36.27612,-115.27616,\"desert\"],[36.27612,-115.27583,\"desert\"],[36.27612,-115.27549,\"desert\"],[36.27612,-115.27516,\"desert\"],[36.27612,-115.27482,\"desert\"],[36.27639,-115.27683,\"desert\"],[36.27639,-115.27649,\"desert\"],[36.27639,-115.27616,\"desert\"],[36.27639,-115.27583,\"desert\"],[36.27639,-115.27549,\"desert\"],[36.27639,-115.27516,\"desert\"],[36.27639,-115.27482,\"desert\"],[36.27666,-115.27649,\"desert\"],[36.27666,-115.27616,\"desert\"],[36.27666,-115.27583,\"desert\"],[36.27666,-115.27549,\"desert\"],[36.27666,-115.27516,\"desert\"],[36.27666,-115.27482,\"desert\"]],\"882986aa93fffff\":[[36.27612,-115.27783,\"desert\"],[36.27639,-115.27816,\"desert\"],[36.27639,-115.27783,\"desert\"],[36.27639,-115.27749,\"desert\"],[36.27639,-115.27716,\"desert\"],[36.27666,-115.27816,\"desert\"],[36.27666,-115.27783,\"desert\"],[36.27666,-115.27749,\"desert\"],[36.27666,-115.27716,\"desert\"],[36.27666,-115.27683,\"desert\"]],\"882986aad9fffff\":[[36.23975,-115.27282,\"desert\"],[36.24002,-115.27282,\"desert\"],[36.24029,-115.27282,\"desert\"],[36.24165,-115.27983,\"desert\"],[36.24192,-115.27983,\"desert\"],[36.24219,-115.27983,\"desert\"],[36.24246,-115.27983,\"desert\"],[36.24273,-115.27983,\"desert\"],[36.24301,-115.27983,\"desert\"],[36.24328,-115.27983,\"desert\"],[36.24355,-115.27983,\"desert\"],[36.24382,-115.27983,\"desert\"],[36.24409,-115.27983,\"desert\"]],\"882986a133fffff\":[[36.23432,-115.28083,\"desert\"],[36.23432,-115.2805,\"desert\"],[36.23432,-115.28016,\"desert\"],[36.23432,-115.27983,\"desert\"],[36.23459,-115.28083,\"desert\"],[36.23459,-115.2805,\"desert\"],[36.23459,-115.28016,\"desert\"],[36.23459,-115.27983,\"desert\"],[36.23486,-115.28417,\"desert\"],[36.23486,-115.28083,\"desert\"],[36.23486,-115.2805,\"desert\"],[36.23486,-115.28016,\"desert\"],[36.23486,-115.27983,\"desert\"],[36.23513,-115.28417,\"desert\"],[36.23513,-115.28384,\"desert\"],[36.23513,-115.2835,\"desert\"],[36.23513,-115.28317,\"desert\"],[36.23513,-115.28083,\"desert\"],[36.23513,-115.28016,\"desert\"],[36.23513,-115.27983,\"desert\"],[36.23541,-115.28417,\"desert\"],[36.23541,-115.28384,\"desert\"],[36.23541,-115.2835,\"desert\"],[36.23541,-115.28317,\"desert\"],[36.23568,-115.28417,\"desert\"],[36.23568,-115.28384,\"desert\"],[36.23568,-115.2835,\"desert\"],[36.23568,-115.28317,\"desert\"],[36.23595,-115.28417,\"desert\"],[36.23595,-115.28384,\"desert\"],[36.23595,-115.2835,\"desert\"],[36.23595,-115.28317,\"desert\"],[36.23595,-115.28284,\"desert\"],[36.23622,-115.28417,\"desert\"],[36.23622,-115.28384,\"desert\"],[36.23622,-115.2835,\"desert\"],[36.23622,-115.28317,\"desert\"],[36.23649,-115.28417,\"desert\"],[36.23649,-115.28384,\"desert\"],[36.23649,-115.2835,\"desert\"],[36.23649,-115.28317,\"desert\"],[36.23676,-115.28417,\"desert\"],[36.23676,-115.28384,\"desert\"],[36.23676,-115.2835,\"desert\"],[36.23676,-115.28317,\"desert\"],[36.23703,-115.28417,\"desert\"],[36.23703,-115.28384,\"desert\"],[36.23703,-115.2835,\"desert\"],[36.23703,-115.28317,\"desert\"],[36.23731,-115.28384,\"desert\"],[36.23731,-115.2835,\"desert\"],[36.23731,-115.28317,\"desert\"],[36.23758,-115.2835,\"desert\"],[36.23758,-115.28317,\"desert\"],[36.23785,-115.28317,\"desert\"]],\"882986a1e5fffff\":[[36.23731,-115.28417,\"desert\"],[36.23758,-115.28417,\"desert\"],[36.23758,-115.28384,\"desert\"],[36.23785,-115.28417,\"desert\"],[36.23785,-115.28384,\"desert\"],[36.23785,-115.2835,\"desert\"],[36.23812,-115.2845,\"desert\"],[36.23812,-115.28417,\"desert\"],[36.23812,-115.28384,\"desert\"],[36.23812,-115.2835,\"desert\"],[36.23812,-115.28317,\"desert\"],[36.23839,-115.2845,\"desert\"],[36.23839,-115.28417,\"desert\"],[36.23839,-115.28384,\"desert\"],[36.23839,-115.2835,\"desert\"],[36.23839,-115.28317,\"desert\"],[36.23866,-115.28651,\"desert\"],[36.23866,-115.28617,\"desert\"],[36.23866,-115.2845,\"desert\"],[36.23866,-115.28417,\"desert\"],[36.23866,-115.28384,\"desert\"],[36.23866,-115.2835,\"desert\"],[36.23866,-115.28317,\"desert\"],[36.23893,-115.2845,\"desert\"],[36.23893,-115.28417,\"desert\"],[36.23893,-115.28384,\"desert\"],[36.23893,-115.2835,\"desert\"],[36.23893,-115.28317,\"desert\"],[36.23921,-115.2845,\"desert\"],[36.23921,-115.28417,\"desert\"],[36.23921,-115.28384,\"desert\"],[36.23921,-115.2835,\"desert\"],[36.23921,-115.28317,\"desert\"],[36.23948,-115.2845,\"desert\"],[36.23948,-115.28417,\"desert\"],[36.23948,-115.28384,\"desert\"],[36.23948,-115.2835,\"desert\"],[36.23948,-115.28317,\"desert\"],[36.23975,-115.28751,\"desert\"],[36.23975,-115.28617,\"desert\"],[36.23975,-115.28584,\"desert\"],[36.23975,-115.28551,\"desert\"],[36.23975,-115.28517,\"desert\"],[36.23975,-115.2845,\"desert\"],[36.23975,-115.28417,\"desert\"],[36.23975,-115.28384,\"desert\"],[36.23975,-115.2835,\"desert\"],[36.23975,-115.28317,\"desert\"],[36.24002,-115.28617,\"desert\"],[36.24002,-115.28584,\"desert\"],[36.24002,-115.28551,\"desert\"],[36.24029,-115.28617,\"desert\"],[36.24029,-115.28584,\"desert\"],[36.24029,-115.28551,\"desert\"]],\"882986a955fffff\":[[36.27802,-115.10557,\"desert\"],[36.27802,-115.10524,\"desert\"],[36.27829,-115.1059,\"desert\"],[36.27829,-115.10557,\"desert\"],[36.27829,-115.10524,\"desert\"],[36.27856,-115.10624,\"desert\"],[36.27856,-115.1059,\"desert\"],[36.27856,-115.10557,\"desert\"],[36.27856,-115.10524,\"desert\"],[36.27883,-115.10691,\"desert\"],[36.27883,-115.10657,\"desert\"],[36.27883,-115.10624,\"desert\"],[36.27883,-115.1059,\"desert\"],[36.27883,-115.10557,\"desert\"],[36.27883,-115.10524,\"desert\"],[36.2791,-115.10691,\"desert\"],[36.2791,-115.10657,\"desert\"],[36.2791,-115.10624,\"desert\"],[36.2791,-115.1059,\"desert\"],[36.2791,-115.10557,\"desert\"],[36.2791,-115.10524,\"desert\"],[36.27937,-115.10691,\"desert\"],[36.27937,-115.10657,\"desert\"],[36.27937,-115.10624,\"desert\"],[36.27937,-115.1059,\"desert\"],[36.27937,-115.10557,\"desert\"],[36.27964,-115.10691,\"desert\"],[36.27964,-115.10657,\"desert\"],[36.27964,-115.10624,\"desert\"],[36.27964,-115.1059,\"desert\"],[36.27964,-115.10557,\"desert\"]],\"882986a829fffff\":[[36.27802,-115.14496,\"desert\"],[36.27802,-115.14463,\"desert\"],[36.27829,-115.14496,\"desert\"],[36.27829,-115.14463,\"desert\"],[36.27856,-115.14496,\"desert\"],[36.27856,-115.14463,\"desert\"],[36.27883,-115.14496,\"desert\"],[36.27883,-115.14463,\"desert\"]],\"882986a853fffff\":[[36.27394,-115.20272,\"desert\"]],\"882986ab43fffff\":[[36.21451,-115.13294,\"desert\"],[36.21451,-115.13261,\"desert\"],[36.21451,-115.13228,\"desert\"],[36.21451,-115.13194,\"desert\"],[36.21451,-115.13161,\"desert\"],[36.21451,-115.13128,\"desert\"],[36.21451,-115.13094,\"desert\"],[36.21478,-115.13294,\"desert\"],[36.21478,-115.13261,\"desert\"],[36.21478,-115.13228,\"desert\"],[36.21478,-115.13194,\"desert\"],[36.21478,-115.13161,\"desert\"],[36.21478,-115.13128,\"desert\"],[36.21478,-115.13094,\"desert\"],[36.21505,-115.13294,\"desert\"],[36.21505,-115.13261,\"desert\"],[36.21505,-115.13228,\"desert\"],[36.21505,-115.13194,\"desert\"],[36.21505,-115.13161,\"desert\"],[36.21505,-115.13128,\"desert\"],[36.21505,-115.13094,\"desert\"],[36.21532,-115.13294,\"desert\"],[36.21532,-115.13261,\"desert\"],[36.21532,-115.13228,\"desert\"],[36.21532,-115.13194,\"desert\"],[36.21532,-115.13161,\"desert\"],[36.21532,-115.13128,\"desert\"],[36.21532,-115.13094,\"desert\"],[36.21559,-115.13294,\"desert\"],[36.21559,-115.13261,\"desert\"],[36.21559,-115.13228,\"desert\"],[36.21559,-115.13194,\"desert\"],[36.21559,-115.13161,\"desert\"],[36.21559,-115.13128,\"desert\"],[36.21559,-115.13094,\"desert\"],[36.21587,-115.13294,\"desert\"],[36.21587,-115.13261,\"desert\"],[36.21587,-115.13228,\"desert\"],[36.21587,-115.13194,\"desert\"],[36.21587,-115.13161,\"desert\"],[36.21587,-115.13128,\"desert\"],[36.21587,-115.13094,\"desert\"],[36.21641,-115.13294,\"desert\"],[36.21641,-115.13261,\"desert\"],[36.21641,-115.13228,\"desert\"],[36.21641,-115.13194,\"desert\"],[36.21641,-115.13161,\"desert\"],[36.21641,-115.13128,\"desert\"],[36.21641,-115.13094,\"desert\"],[36.21668,-115.13294,\"desert\"],[36.21668,-115.13261,\"desert\"],[36.21668,-115.13228,\"desert\"],[36.21668,-115.13194,\"desert\"],[36.21668,-115.13161,\"desert\"],[36.21668,-115.13128,\"desert\"],[36.21668,-115.13094,\"desert\"],[36.21668,-115.13061,\"desert\"],[36.21668,-115.13027,\"desert\"],[36.21695,-115.13261,\"desert\"],[36.21695,-115.13228,\"desert\"],[36.21695,-115.13194,\"desert\"],[36.21695,-115.13161,\"desert\"],[36.21695,-115.13128,\"desert\"],[36.21695,-115.13094,\"desert\"],[36.21695,-115.13061,\"desert\"],[36.21695,-115.13027,\"desert\"]],\"882986bab1fffff\":[[36.12359,-115.25112,\"desert\"],[36.12359,-115.25079,\"desert\"],[36.12359,-115.25045,\"desert\"],[36.12359,-115.25012,\"desert\"],[36.12386,-115.25112,\"desert\"],[36.12386,-115.25079,\"desert\"],[36.12386,-115.25045,\"desert\"],[36.12386,-115.25012,\"desert\"]],\"882986b137fffff\":[[36.07827,-115.27115,\"desert\"],[36.07827,-115.27082,\"desert\"],[36.07854,-115.27115,\"desert\"],[36.07854,-115.27082,\"desert\"],[36.07881,-115.27115,\"desert\"],[36.07881,-115.27082,\"desert\"],[36.07908,-115.27115,\"desert\"],[36.07908,-115.27082,\"desert\"],[36.07935,-115.27115,\"desert\"],[36.07963,-115.27115,\"desert\"],[36.0799,-115.27115,\"desert\"],[36.08017,-115.27115,\"desert\"]],\"882986bad9fffff\":[[36.08017,-115.28083,\"desert\"],[36.08017,-115.2805,\"desert\"],[36.08017,-115.28016,\"desert\"],[36.08044,-115.27115,\"desert\"],[36.08071,-115.27115,\"desert\"],[36.0837,-115.27315,\"desert\"],[36.0837,-115.27282,\"desert\"],[36.0837,-115.27249,\"desert\"],[36.0837,-115.27215,\"desert\"],[36.0837,-115.27182,\"desert\"],[36.08397,-115.27449,\"desert\"],[36.08397,-115.27349,\"desert\"],[36.08397,-115.27315,\"desert\"],[36.08397,-115.27282,\"desert\"],[36.08397,-115.27249,\"desert\"],[36.08397,-115.27215,\"desert\"],[36.08397,-115.27182,\"desert\"],[36.08424,-115.27349,\"desert\"],[36.08424,-115.27315,\"desert\"],[36.08424,-115.27282,\"desert\"],[36.08424,-115.27249,\"desert\"],[36.08424,-115.27215,\"desert\"],[36.08451,-115.27315,\"desert\"],[36.08451,-115.27282,\"desert\"],[36.08451,-115.27249,\"desert\"],[36.08451,-115.27215,\"desert\"]],\"882986ba91fffff\":[[36.11545,-115.27816,\"desert\"],[36.11545,-115.27783,\"desert\"],[36.11545,-115.27749,\"desert\"],[36.11572,-115.27783,\"desert\"],[36.11572,-115.27749,\"desert\"],[36.11599,-115.27749,\"desert\"],[36.11599,-115.27716,\"desert\"],[36.11626,-115.27683,\"desert\"],[36.11626,-115.27649,\"desert\"],[36.11626,-115.27616,\"desert\"],[36.11654,-115.27649,\"desert\"],[36.11654,-115.27616,\"desert\"],[36.11681,-115.27616,\"desert\"],[36.11681,-115.27583,\"desert\"],[36.11681,-115.27549,\"desert\"],[36.11681,-115.27516,\"desert\"],[36.11681,-115.27482,\"desert\"],[36.11681,-115.27449,\"desert\"],[36.11681,-115.27416,\"desert\"],[36.11681,-115.27382,\"desert\"],[36.11681,-115.27349,\"desert\"],[36.11681,-115.27315,\"desert\"],[36.11681,-115.27282,\"desert\"],[36.11708,-115.27583,\"desert\"],[36.11708,-115.27549,\"desert\"],[36.11708,-115.27516,\"desert\"],[36.11708,-115.27482,\"desert\"],[36.11708,-115.27449,\"desert\"],[36.11708,-115.27416,\"desert\"],[36.11708,-115.27382,\"desert\"],[36.11708,-115.27349,\"desert\"],[36.11708,-115.27315,\"desert\"],[36.11708,-115.27282,\"desert\"],[36.11735,-115.27549,\"desert\"],[36.11735,-115.27516,\"desert\"],[36.11735,-115.27482,\"desert\"],[36.11735,-115.27449,\"desert\"],[36.11735,-115.27416,\"desert\"],[36.11735,-115.27382,\"desert\"],[36.11735,-115.27349,\"desert\"],[36.11735,-115.27315,\"desert\"],[36.11735,-115.27282,\"desert\"],[36.11762,-115.27516,\"desert\"],[36.11762,-115.27482,\"desert\"],[36.11762,-115.27449,\"desert\"],[36.11762,-115.27416,\"desert\"],[36.11762,-115.27382,\"desert\"],[36.11762,-115.27349,\"desert\"],[36.11762,-115.27315,\"desert\"],[36.11762,-115.27282,\"desert\"],[36.11762,-115.27182,\"desert\"],[36.11789,-115.27449,\"desert\"],[36.11789,-115.27416,\"desert\"],[36.11789,-115.27382,\"desert\"],[36.11789,-115.27349,\"desert\"],[36.11789,-115.27315,\"desert\"],[36.11789,-115.27282,\"desert\"],[36.11816,-115.27416,\"desert\"],[36.11816,-115.27382,\"desert\"]],\"882986bb3dfffff\":[[36.0856,-115.12093,\"desert\"],[36.0856,-115.12059,\"desert\"]],\"88298616b9fffff\":[[36.19877,-115.08087,\"desert\"],[36.19985,-115.08087,\"desert\"],[36.20012,-115.08087,\"desert\"],[36.2004,-115.08087,\"desert\"],[36.20067,-115.08087,\"desert\"]],\"882986aa43fffff\":[[36.20148,-115.20505,\"desert\"],[36.20148,-115.20472,\"desert\"],[36.20175,-115.20505,\"desert\"],[36.20175,-115.20472,\"desert\"]],\"882986aa2bfffff\":[[36.22346,-115.18269,\"desert\"],[36.22374,-115.18269,\"desert\"],[36.22401,-115.18269,\"desert\"],[36.22428,-115.18269,\"desert\"],[36.22428,-115.18235,\"desert\"]],\"882986ab1dfffff\":[[36.23649,-115.13495,\"desert\"],[36.23866,-115.13662,\"desert\"],[36.23866,-115.13628,\"desert\"]],\"88298616bbfffff\":[[36.20528,-115.07953,\"desert\"],[36.20555,-115.07953,\"desert\"],[36.20555,-115.0792,\"desert\"],[36.20555,-115.07886,\"desert\"],[36.20555,-115.07853,\"desert\"],[36.20582,-115.07953,\"desert\"],[36.20582,-115.0792,\"desert\"],[36.20582,-115.07886,\"desert\"],[36.20582,-115.07853,\"desert\"],[36.2061,-115.07953,\"desert\"],[36.2061,-115.0792,\"desert\"],[36.2061,-115.07886,\"desert\"],[36.2061,-115.07853,\"desert\"],[36.20637,-115.07953,\"desert\"],[36.20637,-115.0792,\"desert\"],[36.20637,-115.07886,\"desert\"],[36.20637,-115.07853,\"desert\"],[36.20664,-115.07953,\"desert\"],[36.20664,-115.0792,\"desert\"],[36.20664,-115.07886,\"desert\"],[36.20664,-115.07853,\"desert\"],[36.20691,-115.07953,\"desert\"],[36.20691,-115.0792,\"desert\"],[36.20691,-115.07886,\"desert\"],[36.20691,-115.07853,\"desert\"],[36.20718,-115.07953,\"desert\"],[36.20718,-115.0792,\"desert\"],[36.20718,-115.07886,\"desert\"],[36.20718,-115.07853,\"desert\"],[36.20718,-115.0782,\"desert\"],[36.20718,-115.07786,\"desert\"],[36.20718,-115.07753,\"desert\"],[36.20745,-115.07953,\"desert\"],[36.20745,-115.0792,\"desert\"],[36.20745,-115.07886,\"desert\"],[36.20745,-115.07853,\"desert\"],[36.20745,-115.0782,\"desert\"],[36.20745,-115.07786,\"desert\"],[36.20745,-115.07753,\"desert\"],[36.20772,-115.07953,\"desert\"],[36.20772,-115.0792,\"desert\"],[36.20772,-115.07886,\"desert\"],[36.20772,-115.07853,\"desert\"],[36.20772,-115.0782,\"desert\"],[36.20772,-115.07786,\"desert\"],[36.20772,-115.07753,\"desert\"],[36.208,-115.07953,\"desert\"],[36.208,-115.0792,\"desert\"],[36.208,-115.07886,\"desert\"],[36.208,-115.07853,\"desert\"],[36.208,-115.0782,\"desert\"],[36.208,-115.07786,\"desert\"],[36.208,-115.07753,\"desert\"]],\"88298616b1fffff\":[[36.20718,-115.07719,\"desert\"],[36.20718,-115.07686,\"desert\"],[36.20718,-115.07653,\"desert\"],[36.20745,-115.07719,\"desert\"],[36.20745,-115.07686,\"desert\"],[36.20745,-115.07653,\"desert\"],[36.20772,-115.07719,\"desert\"],[36.20772,-115.07686,\"desert\"],[36.20772,-115.07653,\"desert\"],[36.208,-115.07719,\"desert\"],[36.208,-115.07686,\"desert\"],[36.208,-115.07653,\"desert\"]],\"882986ab6dfffff\":[[36.21261,-115.0842,\"desert\"]],\"88298616b3fffff\":[[36.21261,-115.08387,\"desert\"],[36.21261,-115.08354,\"desert\"],[36.21261,-115.0832,\"desert\"],[36.21369,-115.08187,\"desert\"],[36.21369,-115.08153,\"desert\"],[36.21369,-115.0812,\"desert\"],[36.21369,-115.08087,\"desert\"],[36.21397,-115.08187,\"desert\"],[36.21397,-115.08153,\"desert\"],[36.21397,-115.0812,\"desert\"],[36.21397,-115.08087,\"desert\"]],\"882986aa85fffff\":[[36.26987,-115.2371,\"desert\"]],\"882986aaabfffff\":[[36.26987,-115.23677,\"desert\"],[36.26987,-115.23643,\"desert\"],[36.27014,-115.2371,\"desert\"],[36.27014,-115.23677,\"desert\"],[36.27014,-115.23643,\"desert\"],[36.27042,-115.2371,\"desert\"],[36.27042,-115.23677,\"desert\"],[36.27042,-115.23643,\"desert\"]],\"882986b8adfffff\":[[36.16919,-115.18803,\"desert\"],[36.16919,-115.18769,\"desert\"]],\"882986ab11fffff\":[[36.24246,-115.14797,\"park\"],[36.24246,-115.14763,\"park\"],[36.24246,-115.1473,\"park\"],[36.24246,-115.14697,\"park\"],[36.24246,-115.14663,\"park\"],[36.24246,-115.1463,\"park\"],[36.24246,-115.14596,\"park\"],[36.24246,-115.14563,\"park\"],[36.24273,-115.14797,\"park\"],[36.24273,-115.14763,\"park\"],[36.24273,-115.1473,\"park\"],[36.24273,-115.14697,\"park\"],[36.24273,-115.14663,\"park\"],[36.24273,-115.1463,\"park\"],[36.24273,-115.14596,\"park\"]],\"882986b8cbfffff\":[[36.13173,-115.23677,\"desert\"],[36.132,-115.23677,\"desert\"],[36.13228,-115.23677,\"desert\"]],\"882986b8d3fffff\":[[36.15589,-115.26013,\"desert\"],[36.15589,-115.2598,\"desert\"],[36.15589,-115.25947,\"desert\"],[36.15589,-115.25913,\"desert\"],[36.15616,-115.26013,\"desert\"],[36.15616,-115.2598,\"desert\"],[36.15616,-115.25947,\"desert\"],[36.15616,-115.25913,\"desert\"],[36.15643,-115.26013,\"desert\"],[36.15643,-115.2598,\"desert\"],[36.15643,-115.25947,\"desert\"]],\"882986ba17fffff\":[[36.07935,-115.21907,\"desert\"],[36.07935,-115.21574,\"desert\"],[36.07963,-115.21907,\"desert\"],[36.0799,-115.2174,\"desert\"],[36.0799,-115.21707,\"desert\"]],\"882986baedfffff\":[[36.0818,-115.21473,\"desert\"]],\"882986ab33fffff\":[[36.2601,-115.13395,\"desert\"],[36.2601,-115.13361,\"desert\"],[36.26037,-115.13395,\"desert\"],[36.26037,-115.13361,\"desert\"],[36.26065,-115.13395,\"desert\"],[36.26065,-115.13361,\"desert\"]]});\r\nexport const WILD_HA_BY_RES8=Object.freeze({\"882986b8ddfffff\":0.9,\"882986ba93fffff\":5.67,\"882986a369fffff\":9.45,\"882986ba97fffff\":6.48,\"882986baabfffff\":0.45,\"882986baa1fffff\":1.35,\"882986b85bfffff\":3.15,\"882986b813fffff\":1.44,\"882986a327fffff\":1.89,\"882986b8d1fffff\":0.81,\"882986b8a9fffff\":1.17,\"882986b99bfffff\":7.11,\"882986b8a5fffff\":1.98,\"882986a169fffff\":1.89,\"882986a161fffff\":0.27,\"882986a323fffff\":0.18,\"882986a149fffff\":0.45,\"882986bb31fffff\":0.27,\"882986aa49fffff\":0.54,\"882986b893fffff\":0.27,\"882986b897fffff\":0.36,\"882986b953fffff\":0.72,\"882986b8bbfffff\":0.09,\"882986b9d9fffff\":0.09,\"882986ab4bfffff\":0.54,\"882986aa3dfffff\":1.71,\"882986b9ebfffff\":0.18,\"882986b82dfffff\":0.72,\"882986b917fffff\":3.6,\"882986ab39fffff\":0.09,\"88298616d3fffff\":3.24,\"882986b903fffff\":0.72,\"882986b9b7fffff\":0.99,\"882986ba83fffff\":0.36,\"882986a32dfffff\":2.16,\"882986b9e5fffff\":0.27,\"882986ba95fffff\":2.61,\"882986bab9fffff\":0.9,\"882986ab19fffff\":0.09,\"882986a84dfffff\":0.36,\"882986aa37fffff\":10.71,\"882986a85dfffff\":0.18,\"882986baddfffff\":4.59,\"882986bad5fffff\":0.54,\"8829861695fffff\":1.62,\"8829861697fffff\":0.45,\"882986b881fffff\":0.18,\"882986a123fffff\":1.17,\"882986a129fffff\":0.72,\"882986a12bfffff\":2.52,\"882986ab61fffff\":5.4,\"882986ab65fffff\":0.18,\"88298614dbfffff\":1.17,\"882986aadbfffff\":2.61,\"882986a81dfffff\":0.63,\"882986aa87fffff\":0.45,\"882986aac9fffff\":1.53,\"882986a135fffff\":0.45,\"882986a805fffff\":1.98,\"882986a845fffff\":3.33,\"882986bb17fffff\":2.07,\"882986bb3bfffff\":0.63,\"882986a951fffff\":3.69,\"882986a959fffff\":0.36,\"882986aa15fffff\":1.08,\"882986a125fffff\":0.18,\"882986a127fffff\":0.81,\"882986aa1bfffff\":0.99,\"882986ab55fffff\":0.45,\"882986aa9bfffff\":8.82,\"88298616d7fffff\":0.09,\"882986b9b1fffff\":0.54,\"882986aa59fffff\":0.09,\"882986a13bfffff\":2.7,\"882986b987fffff\":0.09,\"882986b9bbfffff\":0.72,\"882986aa5bfffff\":0.63,\"882986baa7fffff\":0.36,\"882986a36dfffff\":0.45,\"882986a141fffff\":0.18,\"882986ba3bfffff\":1.17,\"882986ba33fffff\":0.09,\"882986bac3fffff\":0.81,\"882986bac1fffff\":2.88,\"882986bac7fffff\":0.54,\"882986b85dfffff\":0.54,\"882986bad1fffff\":2.7,\"882986badbfffff\":0.36,\"882986aabdfffff\":15.3,\"882986aab9fffff\":2.88,\"882986ab15fffff\":1.8,\"88298616c3fffff\":0.09,\"882986ba89fffff\":0.81,\"882986b8c1fffff\":0.63,\"882986aa61fffff\":2.52,\"882986aa63fffff\":28.08,\"882986aa67fffff\":6.21,\"882986aa29fffff\":0.09,\"882986aa65fffff\":1.8,\"882986ab59fffff\":23.76,\"882986ab5dfffff\":4.68,\"882986ab5bfffff\":6.93,\"882986bacbfffff\":1.89,\"882986aa0bfffff\":4.41,\"882986b80dfffff\":0.45,\"882986a361fffff\":1.98,\"882986a95dfffff\":4.14,\"882986ab37fffff\":0.36,\"882986a95bfffff\":0.63,\"882986a941fffff\":1.53,\"882986a909fffff\":3.24,\"882986aa47fffff\":1.71,\"882986aa6bfffff\":10.53,\"882986aa0dfffff\":23.4,\"882986aa09fffff\":0.36,\"882986aa01fffff\":9.9,\"882986aa05fffff\":4.41,\"882986aa99fffff\":1.35,\"882986aa91fffff\":6.21,\"882986aa93fffff\":0.9,\"882986aad9fffff\":1.17,\"882986a133fffff\":4.95,\"882986a1e5fffff\":4.86,\"882986a955fffff\":2.79,\"882986a829fffff\":0.72,\"882986a853fffff\":0.09,\"882986ab43fffff\":5.94,\"882986bab1fffff\":0.72,\"882986b137fffff\":1.08,\"882986bad9fffff\":2.34,\"882986ba91fffff\":5.31,\"882986bb3dfffff\":0.18,\"88298616b9fffff\":0.45,\"882986aa43fffff\":0.36,\"882986aa2bfffff\":0.45,\"882986ab1dfffff\":0.27,\"88298616bbfffff\":4.77,\"88298616b1fffff\":1.08,\"882986ab6dfffff\":0.09,\"88298616b3fffff\":0.99,\"882986aa85fffff\":0.09,\"882986aaabfffff\":0.72,\"882986b8adfffff\":0.18,\"882986ab11fffff\":1.35,\"882986b8cbfffff\":0.27,\"882986b8d3fffff\":0.99,\"882986ba17fffff\":0.45,\"882986baedfffff\":0.09,\"882986ab33fffff\":0.54});\r\n","modules/coach-world/world-nodes.mjs":"import {FACTIONS,FACTION_BY_ID} from './catalog.mjs';\r\nimport {WORLD_SITES} from './sites.mjs';\r\nimport {cellToBoundary,cellToLatLng,latLngToCell,containsPoint} from './geo.mjs';\r\nimport {isValidCell,getResolution} from './vendor/h3.mjs';\r\n\r\n// Display catalog only. These nodes never grant materials or authorize visits.\r\nexport const RESOURCE_NODE_TYPES=Object.freeze(Object.fromEntries([\r\n ['chest','press-alloy','Pressforge compression ore','pressed-ore'],\r\n ['quads','stride-basalt','Stride Citadel squatstone','stepped-crystal'],\r\n ['glutes','thrustroot-seed','Thrustroot growth pods','root-pod'],\r\n ['arms-shoulders','orbit-conductor','Orbit Arsenal conductor coils','coil-cluster'],\r\n ['yoga','stillwave-prism','Stillwave balance prisms','balanced-prism'],\r\n ['martial-arts','moltguard-chitin','Moltguard training husks','folded-husk'],\r\n ['cardio','pulseway-cell','Pulseway turbine cells','turbine-cell'],\r\n].map(([faction,kind,label,shape])=>[faction,Object.freeze({faction,kind,label,shape,spriteKey:`resource-${faction}`})])));\r\nconst NEUTRAL=Object.freeze({faction:'neutral',kind:'unclaimed-mineral',label:'Unclaimed mineral formation',shape:'mineral-cluster',spriteKey:'resource-neutral'});\r\nconst SITES=Object.freeze(Object.fromEntries(WORLD_SITES.map(site=>[site.id,site])));\r\nconst phase=value=>['stable','warzone','contested','converting','healing'].includes(value)?value:'neutral';\r\nconst knownFaction=value=>Object.hasOwn(FACTION_BY_ID,value)?value:null;\r\nfunction hash(value){let result=2166136261;for(const c of String(value))result=Math.imul(result^c.charCodeAt(0),16777619)>>>0;return result;}\r\nfunction random(seed){let state=hash(seed);return ()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};}\r\nconst wrap=longitude=>((longitude+180)%360+360)%360-180;\r\nconst unwrap=(longitude,anchor)=>anchor+wrap(longitude-anchor);\r\nconst cellsOnly=id=>typeof id==='string'&&isValidCell(id)&&getResolution(id)===10;\r\nfunction cellGeometry(id){const [lat,lng]=cellToLatLng(id),ring=cellToBoundary(id,true);ring.push([...ring[0]]);return {lat,lng,geometry:{type:'Polygon',coordinates:[ring]}};}\r\nfunction interior(geometry,seed,anchor,extra=()=>true){\r\n const reference=anchor.lng,polygons=geometry.type==='MultiPolygon'?geometry.coordinates:[geometry.coordinates],normalized={type:'MultiPolygon',coordinates:polygons.map(p=>p.map(r=>r.map(([lng,lat])=>[unwrap(lng,reference),lat])))};\r\n const rings=normalized.coordinates.map(p=>p[0]).filter(r=>r?.length>=4),points=rings.flat();if(!points.length)return null;\r\n const west=Math.min(...points.map(p=>p[0])),east=Math.max(...points.map(p=>p[0])),south=Math.min(...points.map(p=>p[1])),north=Math.max(...points.map(p=>p[1])),rng=random(seed);\r\n for(let i=0;i<96;i++){const lng=west+(east-west)*(.12+.76*rng()),lat=south+(north-south)*(.12+.76*rng());if(containsPoint(normalized,lat,lng)&&extra(lat,wrap(lng)))return {lat,lng:wrap(lng)};}\r\n if(containsPoint(normalized,anchor.lat,reference)&&extra(anchor.lat,anchor.lng))return anchor;return null;\r\n}\r\nfunction factionFor(sector,fallback=null){return knownFaction(sector?.controller)||knownFaction(sector?.faction)||knownFaction(sector?.initialFaction)||knownFaction(fallback)||'neutral';}\r\nfunction baseNode({id,type,kind,faction,point,label,sectorId=null,siteId=null,phase:state='neutral',spriteKey,shape,referenceOnly=false}){\r\n return {id,type,kind,faction,lat:point.lat,lng:point.lng,latitude:point.lat,longitude:point.lng,label,sectorId,siteId,phase:state,spriteKey,shape,interactive:type==='public-hub',inspectable:true,harvestable:false,harvestStatus:type==='resource'?'cosmetic-only-no-harvest':null,rewardAuthority:'server-workout-records',isVenue:type==='public-hub',referenceOnly,canNavigate:false,walkabilityVerified:false,actions:type==='public-hub'?['inspect-existing-hub']:['inspect-scenery']};\r\n}\r\n/** Anonymous public H3 display geometry in, original decorative nodes out.\r\n * Never pass private bases/friend landmarks. Unknown fields and input coordinates\r\n * are deliberately ignored: sector positions come from canonical H3 geometry;\r\n * hub positions come only from the existing seven-site public catalog.\r\n */\r\nexport function buildWorldNodes(sectors=[],sites=[],options={}){\r\n options=options&&typeof options==='object'?options:{};\r\n const version=Number.isSafeInteger(options.version)&&options.version>0?options.version:1,maxNodes=Number.isFinite(options.maxNodes)?Math.max(0,Math.min(512,Math.floor(options.maxNodes))):256,density=Number.isFinite(options.resourceDensity)?Math.max(0,Math.min(1,options.resourceDensity)):.65,publicModulo=Number.isSafeInteger(options.publicNodeModulo)&&options.publicNodeModulo>0?options.publicNodeModulo:1;\r\n const sectorMap=new Map();for(const value of Array.isArray(sectors)?sectors:[]){if(!value||!cellsOnly(value.id)||value.enabled===false)continue;const previous=sectorMap.get(value.id),revision=Number.isSafeInteger(value.revision)?value.revision:0,previousRevision=Number.isSafeInteger(previous?.revision)?previous.revision:0,signature=v=>JSON.stringify([factionFor(v),phase(v?.phase)]);if(!previous||revision>previousRevision||(revision===previousRevision&&signature(value)>signature(previous)))sectorMap.set(value.id,value);}\r\n const publicSites=new Map();for(const supplied of Array.isArray(sites)?sites:[]){const site=Object.hasOwn(SITES,supplied?.id)?SITES[supplied.id]:null;if(site&&site.enabled&&supplied.enabled!==false)publicSites.set(site.id,site);}\r\n const hubs=[],props=[],resources=[];\r\n for(const site of [...publicSites.values()].sort((a,b)=>a.id.localeCompare(b.id))){\r\n  const center={lat:site.latitude,lng:site.longitude},sectorId=latLngToCell(center.lat,center.lng,10),sector=sectorMap.get(sectorId),faction=factionFor(sector,site.faction),state=phase(sector?.phase),definition=FACTION_BY_ID[faction];\r\n  hubs.push(baseNode({id:`world-node:v${version}:hub:${site.id}`,type:'public-hub',kind:'existing-public-hub',faction,point:center,label:site.name,sectorId,siteId:site.id,phase:state,spriteKey:`hub-${faction}`,shape:'public-outpost',referenceOnly:true}));\r\n  if(hash(`public:${site.id}`)%publicModulo!==0)continue;\r\n  const point=interior(site.geometry,`public:${version}:${site.id}`,center);if(!point)continue;const propSectorId=latLngToCell(point.lat,point.lng,10),propSector=sectorMap.get(propSectorId),propFaction=factionFor(propSector,site.faction);\r\n  props.push(baseNode({id:`world-node:v${version}:public:${site.id}`,type:'public-node',kind:'training-relay',faction:propFaction,point,label:`${FACTION_BY_ID[propFaction]?.name||definition?.name||'Alien'} training relay`,sectorId:propSectorId,siteId:site.id,phase:phase(propSector?.phase),spriteKey:`public-${propFaction}`,shape:'training-relay'}));\r\n }\r\n for(const sector of [...sectorMap.values()].sort((a,b)=>a.id.localeCompare(b.id))){\r\n  if(hubs.length+props.length+resources.length>=maxNodes)break;\r\n  if(hash(`resource-density:${version}:${sector.id}`)/4294967296>=density)continue;\r\n  const shape=cellGeometry(sector.id),site=Object.hasOwn(SITES,sector.siteId)?SITES[sector.siteId]:null,point=interior(shape.geometry,`resource:${version}:${sector.id}`,{lat:shape.lat,lng:shape.lng},(lat,lng)=>latLngToCell(lat,lng,10)===sector.id&&(!site||containsPoint(site.geometry,lat,lng)));if(!point)continue;\r\n  const faction=factionFor(sector),resource=RESOURCE_NODE_TYPES[faction]||NEUTRAL;\r\n  resources.push(baseNode({id:`world-node:v${version}:resource:${sector.id}`,type:'resource',kind:resource.kind,faction,point,label:resource.label,sectorId:sector.id,siteId:site?.id??null,phase:phase(sector.phase),spriteKey:resource.spriteKey,shape:resource.shape}));\r\n }\r\n // Existing hub markers get priority. No generated prop becomes a new venue.\r\n return [...hubs,...props,...resources].slice(0,maxNodes);\r\n}\r\n","modules/coach-world/world-tiles.mjs":"// World tiles client (R58 WS-B / S1). Fetch 3x3 ring of vector cells -> IndexedDB cache -> Worker decode -> typed arrays.\r\n// Privacy rules: requests carry no user id / cookies / query string; the full 9-cell ring is always requested when anything is missing,\r\n// so the server cannot tell which cell is the player's centre.\r\nimport {PMTiles} from './vendor/world-tiles-vendor.mjs';\r\n\r\n// ONE config constant. kind 'static' = per-tile files served with the app (default); kind 'pmtiles' = Range reads from a PMTiles archive (R2, later).\r\nexport const WORLD_TILES_SOURCE={kind:'static',base:'/world/v1',gz:true};\r\n//   {kind:'pmtiles',url:'/world/v1/vegas.pmtiles'}   |   {kind:'static',base:'/world/v1',gz:true} for .mvt.gz files\r\nexport const WORLD_CELL_Z=16;            // bake maxzoom (manifest.json vec.maxzoom)\r\nexport const WORLD_BAKE_VERSION='v1-20261018';\r\nexport const CACHE_CAP_BYTES=50*1024*1024;\r\n\r\nconst RAD=Math.PI/180,R=6378137;\r\nexport function ringCells(lat,lng,z=WORLD_CELL_Z){\r\n const n=2**z,s=Math.sin(lat*RAD),cx=Math.floor((lng+180)/360*n),cy=Math.floor((0.5-Math.log((1+s)/(1-s))/(4*Math.PI))*n),out=[];\r\n for(let dy=-1;dy<=1;dy++)for(let dx=-1;dx<=1;dx++)out.push({z,x:cx+dx,y:cy+dy});\r\n return out;\r\n}\r\n// metres about origin: +x east, +z south\r\nexport const toLocal=(o,lat,lng)=>({x:R*(lng-o.lng)*RAD*Math.cos(o.lat*RAD),z:-R*(lat-o.lat)*RAD});\r\n\r\n// ---- caches: {getBytes,getMeta,listMeta,put,setMeta,remove}; meta={key,size,ts,pinned}\r\nexport function memCache(){\r\n const b=new Map(),m=new Map();\r\n return {async getBytes(k){return b.get(k)},async getMeta(k){return m.get(k)},async listMeta(){return [...m.values()]},\r\n  async put(k,bytes,meta){b.set(k,bytes);m.set(k,meta)},async setMeta(k,meta){m.set(k,meta)},async remove(k){b.delete(k);m.delete(k)}};\r\n}\r\nexport function idbCache(name='coach-world-tiles'){\r\n const open=new Promise((res,rej)=>{const r=indexedDB.open(name,1);r.onupgradeneeded=()=>{r.result.createObjectStore('bytes');r.result.createObjectStore('meta',{keyPath:'key'});};r.onsuccess=()=>res(r.result);r.onerror=()=>rej(r.error);});\r\n const tx=async(stores,mode,fn)=>{const db=await open;return new Promise((res,rej)=>{const t=db.transaction(stores,mode);let out;t.oncomplete=()=>res(out);t.onerror=t.onabort=()=>rej(t.error);const st=stores.map(s=>t.objectStore(s));const rq=fn(...st);if(rq&&rq.onsuccess!==undefined)rq.onsuccess=()=>{out=rq.result};});};\r\n return {getBytes:(k)=>tx(['bytes'],'readonly',s=>s.get(k)),getMeta:(k)=>tx(['meta'],'readonly',s=>s.get(k)),listMeta:()=>tx(['meta'],'readonly',s=>s.getAll()),\r\n  put:(k,bytes,meta)=>tx(['bytes','meta'],'readwrite',(b,m)=>{b.put(bytes,k);m.put(meta)}),setMeta:(k,meta)=>tx(['meta'],'readwrite',m=>{m.put(meta)}),\r\n  remove:(k)=>tx(['bytes','meta'],'readwrite',(b,m)=>{b.delete(k);m.delete(k)})};\r\n}\r\n\r\nconst keyOf=(c,v)=>`${c.z}/${c.x}/${c.y}/${v}`;\r\nconst credless={credentials:'omit',referrerPolicy:'no-referrer',cache:'default'};\r\n\r\n// PMTiles source with credentials omitted (the stock FetchSource would send same-origin cookies)\r\nconst rangeSource=(url,fetchImpl)=>({getKey:()=>url,async getBytes(offset,length,signal){\r\n const r=await fetchImpl(url,{...credless,signal,headers:{Range:`bytes=${offset}-${offset+length-1}`}});\r\n if(r.status!==206)throw new Error('range fetch '+r.status);\r\n return {data:await r.arrayBuffer(),etag:r.headers.get('etag')||undefined,cacheControl:r.headers.get('cache-control')||undefined,expires:r.headers.get('expires')||undefined};}});\r\n\r\nexport function createWorldTiles({source=WORLD_TILES_SOURCE,cache,origin,bakeVersion=WORLD_BAKE_VERSION,fetchImpl=globalThis.fetch?.bind(globalThis),decode,capBytes=CACHE_CAP_BYTES}={}){\r\n cache??=typeof indexedDB!=='undefined'?idbCache():memCache();\r\n const pinned=new Set(),pm=source.kind==='pmtiles'?new PMTiles(rangeSource(source.url,fetchImpl)):null;\r\n let worker,seq=0;const waits=new Map();\r\n const decodeIn=decode||((bytes,cell,o)=>{\r\n  if(typeof Worker==='undefined')return import('./world-tiles.worker.mjs').then(m=>m.decodeCell(bytes,cell,o));\r\n  if(!worker){worker=new Worker(new URL('./world-tiles.worker.mjs',import.meta.url),{type:'module'});\r\n   worker.onmessage=({data:{id,m,err}})=>{const w=waits.get(id);waits.delete(id);err?w.rej(new Error(err)):w.res(m);};}\r\n  return new Promise((res,rej)=>{const id=++seq;waits.set(id,{res,rej});worker.postMessage({id,bytes,cell,origin:o},[bytes.buffer]);});});\r\n\r\n async function fetchOne(c){// -> Uint8Array ('' length 0 = empty tile)\r\n  if(pm){const t=await pm.getZxy(c.z,c.x,c.y);return t?new Uint8Array(t.data):new Uint8Array(0);}\r\n  // source.gz: tiles stored as .mvt.gz (served as opaque gzip files, inflated here) - fallback if the host won't compress .mvt on the wire\r\n  const r=await fetchImpl(`${source.base}/vec/${c.z}/${c.x}/${c.y}.mvt${source.gz?'.gz':''}`,credless);\r\n  if(r.status===404)return new Uint8Array(0);\r\n  if(!r.ok)throw new Error('tile '+r.status);\r\n  return new Uint8Array(await(source.gz?new Response(r.body.pipeThrough(new DecompressionStream('gzip'))):r).arrayBuffer());}\r\n\r\n async function store(k,bytes){\r\n  await cache.put(k,bytes.buffer.slice(bytes.byteOffset,bytes.byteOffset+bytes.byteLength),{key:k,size:bytes.byteLength,ts:Date.now(),pinned:pinned.has(k)});}\r\n async function evict(){// LRU, pinned never\r\n  const all=await cache.listMeta();let total=all.reduce((s,m)=>s+m.size,0);\r\n  for(const m of all.filter(m=>!m.pinned).sort((a,b)=>a.ts-b.ts)){if(total<=capBytes)break;await cache.remove(m.key);total-=m.size;}}\r\n\r\n async function bytesFor(cells){\r\n  const ks=cells.map(c=>keyOf(c,bakeVersion)),got=await Promise.all(ks.map(k=>cache.getBytes(k)));\r\n  if(got.every(g=>g))return got.map(g=>new Uint8Array(g.slice(0)));\r\n  let fresh;\r\n  try{fresh=await Promise.all(cells.map(fetchOne));}// always the whole ring\r\n  catch(e){if(got.some(g=>g))return got.map(g=>g?new Uint8Array(g.slice(0)):null);throw e;}// offline: serve what we have\r\n  await Promise.all(fresh.map((b,i)=>store(ks[i],b)));await evict();\r\n  return fresh.map(b=>new Uint8Array(b));}\r\n\r\n return {\r\n  async load(cells,o=origin){\r\n   const bytes=await bytesFor(cells);\r\n   return Promise.all(bytes.map((b,i)=>b?decodeIn(b,cells[i],o):{cell:cells[i],missing:true}));},\r\n  async pin(cells){for(const c of cells){const k=keyOf(c,bakeVersion);pinned.add(k);const m=await cache.getMeta(k);if(m&&!m.pinned)await cache.setMeta(k,{...m,pinned:true});}},\r\n  // terrarium z14 png (WS-C decodes); static source only until terrarium.pmtiles ships\r\n  async loadDem(x,y,z=14){\r\n   if(pm)throw new Error('dem needs the static source');\r\n   const k=`dem/${z}/${x}/${y}/${bakeVersion}`,hit=await cache.getBytes(k);if(hit)return hit.slice(0);\r\n   const r=await fetchImpl(`${source.base}/dem/${z}/${x}/${y}.png`,credless);if(!r.ok)throw new Error('dem '+r.status);\r\n   const b=new Uint8Array(await r.arrayBuffer());await store(k,b);await evict();return b.buffer;},\r\n  dispose(){worker?.terminate();worker=null;for(const w of waits.values())w.rej(new Error('disposed'));waits.clear();}};\r\n}\r\n","modules/coach-world/world-tiles.worker.mjs":"// MVT cell -> typed arrays. Runs in a module Worker (also importable in Node/tests: decodeCell is pure).\r\n// Local frame: metres about `origin`, +x east, +z south, +y up. Layers: buildings, roads, landuse, water.\r\nimport {PbfReader,VectorTile,earcut} from './vendor/world-tiles-vendor.mjs';\r\n\r\nconst M_EQ=40075016.686,R=6378137,RAD=Math.PI/180;\r\nexport const CATS=['house','apartment','retail','restaurant','office','industrial','civic','school','hospital','religious','parking','tower','other'];\r\nexport const ROAD_CLASSES=['motorway','trunk','primary','secondary','tertiary','residential','service','footway','rail','other'];\r\nexport const LAND_CLASSES=['residential','commercial','industrial','park','grass','other'];\r\nconst catIdx=(s)=>CATS.indexOf(s);\r\nconst B={house:'house',detached:'house',semidetached_house:'house',terrace:'house',bungalow:'house',static_caravan:'house',apartments:'apartment',residential:'apartment',dormitory:'apartment',commercial:'office',office:'office',retail:'retail',supermarket:'retail',industrial:'industrial',warehouse:'industrial',factory:'industrial',civic:'civic',public:'civic',government:'civic',school:'school',university:'school',college:'school',hospital:'hospital',church:'religious',cathedral:'religious',mosque:'religious',synagogue:'religious',temple:'religious',chapel:'religious',parking:'parking',garage:'parking',garages:'parking',hotel:'tower'};\r\nconst A={restaurant:'restaurant',cafe:'restaurant',fast_food:'restaurant',bar:'restaurant',pub:'restaurant',school:'school',university:'school',college:'school',kindergarten:'school',hospital:'hospital',clinic:'hospital',place_of_worship:'religious',townhall:'civic',police:'civic',fire_station:'civic',library:'civic',courthouse:'civic',parking:'parking'};\r\n// OSM-ish tags (bake schema) -> category id. ponytail: coarse; WS-D mom-categories owns the real table\r\nexport const catOf=(p)=>catIdx(A[p.amenity]||(p.shop&&'retail')||(p.office&&'office')||B[p.building]||'other');\r\nconst roadId=(s)=>{s=String(s).replace(/_link$/,'');const i=ROAD_CLASSES.indexOf(s);return i<0?(['path','pedestrian','cycleway','steps','track'].includes(s)?7:9):i;};\r\nconst LU={residential:'residential',commercial:'commercial',retail:'commercial',industrial:'industrial',park:'park',garden:'park',playground:'park',common:'park',nature_reserve:'park',grass:'grass',meadow:'grass',farmland:'grass',recreation_ground:'grass',golf_course:'grass',pitch:'grass'};\r\nconst landId=(s)=>{const i=LAND_CLASSES.indexOf(LU[s]||'other');return i};\r\nexport const defaultHeight=(areaM2)=>areaM2<120?6:areaM2<600?9:areaM2<2500?15:24; // ponytail: size-class guess until the bake carries height coverage\r\nconst ringArea=(r)=>{let s=0;for(let i=0;i<r.length;i+=2){const j=(i+2)%r.length;s+=r[i]*r[j+1]-r[j]*r[i+1];}return s/2;};\r\n\r\n// group MVT rings (shoelace>0 = exterior) into polygons [[outer,hole..]], each ring a flat [x,y,..] array in metres\r\nfunction polygons(rings){const out=[];for(const r of rings){const f=new Array(r.length*2);r.forEach((p,i)=>{f[i*2]=p.x;f[i*2+1]=p.y;});if(r.length<3)continue;if(ringArea(f)>0||!out.length)out.push([f]);else out.at(-1).push(f);}return out;}\r\n\r\n// footprint (outer + holes) -> prism; roof via earcut (holes ok). Appends to pos/idx arrays.\r\nexport function extrudePolygon(poly,h,pos,idx){\r\n const flat=[],holes=[];poly.forEach((r,k)=>{if(k)holes.push(flat.length/2);flat.push(...r);});\r\n const roof=earcut(flat,holes,2),base=pos.length/3,n=flat.length/2;\r\n for(let i=0;i<n;i++)pos.push(flat[i*2],h,flat[i*2+1]);\r\n for(const t of roof)idx.push(base+t);\r\n let o=0;poly.forEach((r)=>{const m=r.length/2;for(let i=0;i<m;i++){const j=(i+1)%m,a=o+i,b=o+j,v=pos.length/3;\r\n  pos.push(flat[a*2],0,flat[a*2+1],flat[b*2],0,flat[b*2+1],flat[b*2],h,flat[b*2+1],flat[a*2],h,flat[a*2+1]);\r\n  idx.push(v,v+1,v+2,v,v+2,v+3);}o+=m;});\r\n}\r\n\r\n// footprint centroid (area-weighted; bbox centre if degenerate) of a flat [x,y,..] ring\r\nexport function centroid(r){let a=0,cx=0,cy=0;for(let i=0;i<r.length;i+=2){const j=(i+2)%r.length,c=r[i]*r[j+1]-r[j]*r[i+1];a+=c;cx+=(r[i]+r[j])*c;cy+=(r[i+1]+r[j+1])*c;}\r\n if(Math.abs(a)<1e-9){let x0=1/0,x1=-1/0,y0=1/0,y1=-1/0;for(let i=0;i<r.length;i+=2){x0=Math.min(x0,r[i]);x1=Math.max(x1,r[i]);y0=Math.min(y0,r[i+1]);y1=Math.max(y1,r[i+1]);}return [(x0+x1)/2,(y0+y1)/2];}\r\n return [cx/(3*a),cy/(3*a)];}\r\n\r\n// local metres of tile NW corner and metres per MVT unit, relative to origin\r\nfunction frame(cell,origin){\r\n const n=2**cell.z,lng=cell.x/n*360-180,lat=Math.atan(Math.sinh(Math.PI*(1-2*cell.y/n)))/RAD;\r\n return {x0:R*(lng-origin.lng)*RAD*Math.cos(origin.lat*RAD),z0:-R*(lat-origin.lat)*RAD,s:M_EQ*Math.cos(origin.lat*RAD)/n};\r\n}\r\n\r\nconst f32=(a)=>Float32Array.from(a),u32=(a)=>Uint32Array.from(a);\r\nexport function decodeCell(bytes,cell,origin){\r\n const t=performance.now(),fr=frame(cell,origin),out={cell,origin,\r\n  buildings:{pos:new Float32Array(0),idx:new Uint32Array(0),cat:new Uint8Array(0),height:new Float32Array(0),start:new Uint32Array([0])},\r\n  roads:{pts:new Float32Array(0),start:new Uint32Array([0]),cls:new Uint8Array(0),bridge:new Uint8Array(0),tunnel:new Uint8Array(0)},\r\n  landuse:{pos:new Float32Array(0),idx:new Uint32Array(0),cls:new Uint8Array(0),start:new Uint32Array([0])},\r\n  water:{pos:new Float32Array(0),idx:new Uint32Array(0),start:new Uint32Array([0])},ms:0};\r\n if(!bytes||!bytes.byteLength)return out;\r\n const tile=new VectorTile(new PbfReader(new Uint8Array(bytes))),L=tile.layers;\r\n const unit=(ext)=>fr.s/ext;\r\n const scl=(rings,ext)=>rings.map(r=>r.map(p=>({x:fr.x0+p.x*unit(ext),y:fr.z0+p.y*unit(ext)})));\r\n if(L.buildings){const pos=[],idx=[],cat=[],height=[],start=[0],lay=L.buildings;\r\n  for(let i=0;i<lay.length;i++){const f=lay.feature(i),pr=f.properties;\r\n   for(const poly of polygons(scl(f.loadGeometry(),f.extent))){\r\n    const [mx,my]=centroid(poly[0]);// seam dedupe: a building belongs to the cell holding its centroid (half-open), neighbours drop their buffer copy\r\n    if(mx<fr.x0||mx>=fr.x0+fr.s||my<fr.z0||my>=fr.z0+fr.s)continue;\r\n    const a=Math.abs(ringArea(poly[0])),h=pr.height>0?+pr.height:pr.levels>0?pr.levels*3:defaultHeight(a);\r\n    try{extrudePolygon(poly,h,pos,idx);}catch{continue;}\r\n    cat.push(catOf(pr));height.push(h);start.push(idx.length);}}\r\n  out.buildings={pos:f32(pos),idx:u32(idx),cat:Uint8Array.from(cat),height:f32(height),start:u32(start)};}\r\n if(L.roads){const pts=[],start=[0],cls=[],bridge=[],tunnel=[],lay=L.roads;\r\n  for(let i=0;i<lay.length;i++){const f=lay.feature(i),pr=f.properties;\r\n   for(const line of scl(f.loadGeometry(),f.extent)){if(line.length<2)continue;for(const p of line)pts.push(p.x,p.y);start.push(pts.length/2);cls.push(roadId(pr.class));bridge.push(pr.bridge&&pr.bridge!=='no'?1:0);tunnel.push(pr.tunnel&&pr.tunnel!=='no'?1:0);}}\r\n  out.roads={pts:f32(pts),start:u32(start),cls:Uint8Array.from(cls),bridge:Uint8Array.from(bridge),tunnel:Uint8Array.from(tunnel)};}\r\n for(const name of ['landuse','water']){const lay=L[name];if(!lay)continue;const pos=[],idx=[],cls=[],start=[0];\r\n  for(let i=0;i<lay.length;i++){const f=lay.feature(i),pr=f.properties;\r\n   for(const poly of polygons(scl(f.loadGeometry(),f.extent))){const flat=[],holes=[];poly.forEach((r,k)=>{if(k)holes.push(flat.length/2);flat.push(...r);});\r\n    const base=pos.length/2;for(let k=0;k<flat.length;k++)pos.push(flat[k]);for(const t of earcut(flat,holes,2))idx.push(base+t);cls.push(landId(pr.class));start.push(idx.length);}}\r\n  out[name]=name==='water'?{pos:f32(pos),idx:u32(idx),start:u32(start)}:{pos:f32(pos),idx:u32(idx),cls:Uint8Array.from(cls),start:u32(start)};}\r\n out.ms=performance.now()-t;return out;\r\n}\r\n\r\nexport const transferList=(m)=>{const l=[];for(const k of ['buildings','roads','landuse','water'])for(const a of Object.values(m[k]))l.push(a.buffer);return l;};\r\n\r\nif(typeof WorkerGlobalScope!=='undefined'&&self instanceof WorkerGlobalScope){\r\n self.onmessage=({data:{id,bytes,cell,origin}})=>{try{const m=decodeCell(bytes,cell,origin);self.postMessage({id,m},transferList(m));}catch(e){self.postMessage({id,err:String(e&&e.message||e)});}};\r\n}\r\n","modules/coach-world/world.css":".cw-panel{--cw-faction:#88c6ae;--cw-ink:#e5eee8;--cw-muted:#a8b8b5;color:var(--cw-ink);background:#101e24;border:1px solid #5c7466;border-radius:18px;width:min(1180px,100vw - 20px);height:min(930px,100dvh - 20px);max-height:calc(100dvh - 20px);max-width:calc(100vw - 20px);padding:0;box-shadow:0 24px 120px #000b;font:14px/1.5 system-ui,sans-serif}.cw-panel::backdrop{background:#040d15cf;backdrop-filter:blur(8px)}.cw-panel *{box-sizing:border-box}.cw-shell{min-height:100%;display:flex;flex-direction:column}.cw-header{display:flex;align-items:center;justify-content:space-between;padding:22px 24px 16px;border-bottom:1px solid #ffffff13;background:radial-gradient(ellipse at 15% 0,#36514780,transparent 70%)}.cw-panel h2{font:700 clamp(23px,4vw,36px)/1.1 system-ui;margin:8px 0 0;letter-spacing:-1.5px}.cw-panel h3{font-size:20px;line-height:1.2;margin:7px 0 12px;letter-spacing:-.5px}.cw-eyebrow{font:700 10px/1.3 ui-monospace,monospace;letter-spacing:2px;color:#c9d4ac}.cw-panel p{margin:9px 0 14px}.cw-panel button,.cw-button,.cw-panel select,.cw-panel input{font:600 13px/1.3 system-ui;color:var(--cw-ink);background:#25363c;border:1px solid #526266;border-radius:8px;min-height:44px;padding:11px 14px;max-width:100%}.cw-panel button{cursor:pointer}.cw-panel button:hover{background:#354c4e}.cw-panel button:focus-visible,.cw-panel a:focus-visible,.cw-panel select:focus-visible,.cw-panel input:focus-visible{outline:3px solid #f6d58c;outline-offset:3px}.cw-panel button:disabled{opacity:.45;cursor:default}.cw-panel input{width:190px;min-width:0}.cw-panel a{color:#b8daca}.cw-panel .cw-primary,.cw-panel button[aria-selected=true]{background:#c5d5ad;color:#182b28;border-color:#e5e8bd}.cw-tabs{display:flex;gap:6px;padding:12px 24px 0}.cw-tabs button{flex:1;max-width:180px}.cw-identity{padding:12px 24px;font:11px ui-monospace,monospace;color:var(--cw-muted);border-bottom:1px solid #ffffff12}.cw-content{padding:20px 24px;flex:1;min-height:260px}.cw-base-layout{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(270px,1fr);gap:20px}.cw-base-main,.cw-base-side{min-width:0}.cw-vitals{display:flex;gap:8px;margin-bottom:10px}.cw-vitals>div{background:#1e3138;border:1px solid #52655a66;padding:8px 12px;flex:1;border-radius:8px}.cw-vitals span{display:block;font:9px ui-monospace,monospace;color:#c8d7bb;letter-spacing:1.5px}.cw-vitals b{font:600 17px ui-monospace,monospace}.cw-scene-wrap{position:relative;border:1px solid #52655a88;border-radius:12px;overflow:hidden;background:#0d1924;isolation:isolate}.cw-base-canvas{width:100%;height:470px;display:block;touch-action:none;image-rendering:pixelated}.cw-scene-tag{position:absolute;top:12px;left:13px;color:#c5d5ad;font:9px ui-monospace,monospace;letter-spacing:1.5px;pointer-events:none}.cw-panel .cw-recenter{position:absolute;right:10px;bottom:10px;background:#12242dcc}.cw-note{font-size:12px;color:var(--cw-muted);line-height:1.55}.cw-card{background:linear-gradient(135deg,#25383c,#1c2d33);padding:18px;border:1px solid #60726366;border-radius:12px;margin-bottom:14px}.cw-card select{width:100%;margin-bottom:8px}.cw-actions{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0 4px}.cw-building-list{display:flex;gap:7px;overflow:auto;padding:3px 2px 10px;scrollbar-width:thin}.cw-building-list button{flex-shrink:0;font-size:11px}.cw-building-list .is-selected{border-color:#d4dda8;color:#e7edc5;background:#3c5148}.cw-description{color:#d5ded6;font-size:13px}.cw-status{padding:0 24px 12px;color:#dfd7a9;min-height:23px}.cw-loading{padding:50px 0;text-align:center}.cw-sector-intro h3{font-size:32px}.cw-sector-intro{max-width:650px}.cw-sector-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(135px,1fr));gap:8px;margin:15px 0 25px}.cw-sector-tile{border:1px solid #6b7b7466;border-top:4px solid var(--sector-color);padding:12px;min-height:100px;background:linear-gradient(130deg,#243338,#18282e);border-radius:8px;display:flex;flex-direction:column;gap:4px;overflow:hidden}.cw-sector-tile strong{overflow-wrap:anywhere;font-size:11px}.cw-sector-tile span,.cw-sector-tile small{font-size:11px;color:#b8c7c1}.cw-sector-tile b{font:9px ui-monospace,monospace;color:#dbc9a2}.cw-sector-tile.is-scarred{background:repeating-linear-gradient(135deg,#3b3431 0 5px,#292e32 5px 18px);box-shadow:inset 0 -30px 35px #050d14aa}.cw-map{height:340px;border-radius:12px;overflow:hidden;margin:12px 0}.cw-map .maplibregl-ctrl-attrib{color:#111;font-size:10px}.cw-map .maplibregl-ctrl-attrib a{color:#233}.cw-photo{position:fixed;inset:12px;z-index:5;background:#10232b;overflow:auto;border:1px solid #c7d7b3;padding:16px;border-radius:12px;box-shadow:0 10px 90px #000;display:flex;gap:10px;flex-wrap:wrap;align-content:center;justify-content:center}.cw-photo img{max-width:100%;max-height:75dvh;object-fit:contain;width:100%}.cw-photo a{display:inline-flex;align-items:center;text-decoration:none}.cw-photo p{width:100%;text-align:center}\r\n@media(max-width:680px){.cw-panel{width:100vw;height:100dvh;max-width:100vw;max-height:100dvh;border-radius:0;border:0}.cw-header{padding:18px 16px 14px;padding-top:max(18px,env(safe-area-inset-top))}.cw-tabs{padding:12px 16px 0}.cw-identity{padding:11px 16px}.cw-content{padding:15px 12px}.cw-base-layout{display:block}.cw-base-canvas{height:370px}.cw-card{padding:16px}.cw-base-side{margin-top:14px}.cw-status{padding:0 16px max(16px,env(safe-area-inset-bottom))}.cw-panel input{flex:1;min-width:120px}.cw-building-list{margin-bottom:6px}.cw-vitals>div{padding:8px}.cw-vitals b{font-size:15px}}\r\n@media(max-height:500px) and (orientation:landscape){.cw-panel{width:100vw;height:100dvh;max-height:100dvh;max-width:100vw;border-radius:0}.cw-header{padding:10px 18px}.cw-header h2{font-size:24px}.cw-tabs{position:absolute;right:76px;top:9px;padding:0}.cw-tabs button{min-height:38px;padding:8px 18px}.cw-content{padding-top:12px}.cw-base-layout{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(250px,1fr)}.cw-base-canvas{height:310px}.cw-base-side{margin-top:0}}\r\n@media(prefers-reduced-motion:reduce){.cw-panel *{scroll-behavior:auto;animation:none!important}}\r\n.cw-check{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--cw-muted);margin:15px 0}.cw-panel .cw-check input{min-width:22px;width:22px;height:22px;min-height:22px;flex:0 0 22px;accent-color:#bdd5a4}.cw-hub-art{float:right;width:110px;height:75px;image-rendering:pixelated;pointer-events:none}.cw-card::after{content:\"\";display:block;clear:both}\r\n.cw-panel:has(#coachDock) .cw-shell{padding-bottom:calc(96px + env(safe-area-inset-bottom,0px))}.cw-panel:has(#coachDock) .cw-status{padding-bottom:16px}\r\n\r\n.cw-troop-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.cw-troop-tools button{flex:1 1 110px;font-size:11px}.cw-troop-tools button[aria-pressed=\"true\"]{border-color:#eee1a7;background:#4a5140}.cw-base-canvas[data-troop-gesture=\"select\"]{cursor:crosshair}.cw-role-list{display:grid;gap:6px;margin:12px 0}.cw-role-option{display:flex;align-items:center;gap:10px;padding:10px;background:#07182155;border:1px solid #91aa8c33;border-radius:6px;font-size:12px;min-height:44px}.cw-panel .cw-role-option input{width:22px;min-width:22px;height:22px;min-height:22px;flex:0 0 22px;margin:0;accent-color:#c8d89a}.cw-role-option:has(input:disabled){color:#99aaa7}.cw-training-row{padding:12px 0;border-top:1px solid #91aa8c44}.cw-training-row strong{font-size:12px}.cw-training-row button{margin-top:8px}.cw-card[data-expedition]>button{margin:4px 4px 4px 0}\r\n.cw-drop-card{padding:12px;margin:10px 0;border:1px solid #91aa8c33;border-radius:8px;background:#07182144}.cw-drop-card h4{margin:0 0 8px;font-size:14px}.cw-drop-card .cw-actions button{flex:1 1 110px;font-size:11px}.cw-drop-card .cw-note{overflow-wrap:anywhere}\r\n","modules/coach-world/zones.mjs":"import { latLngToCell, cellToLatLng, cellToChildren } from './vendor/h3.mjs';\r\nimport { distanceMeters } from './geo.mjs';\r\n\r\nexport function zoneOf(latitude, longitude) {\r\n  if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return null;\r\n  try {\r\n    const id = latLngToCell(latitude, longitude, 9);\r\n    return typeof id === 'string' ? id : null;\r\n  } catch (_) {\r\n    return null;\r\n  }\r\n}\r\n\r\nexport function zoneSectors(zoneId) {\r\n  return cellToChildren(zoneId, 10);\r\n}\r\n\r\nexport function zoneCenter(zoneId) {\r\n  const [lat, lng] = cellToLatLng(zoneId);\r\n  return { latitude: lat, longitude: lng };\r\n}\r\n\r\nexport const VISIBILITY = Object.freeze({\r\n  unknown: 6,\r\n  hub: 60,\r\n  known: 305\r\n});\r\n\r\nexport function visibilityFor({ owned, friend, hub, claimedBy } = {}) {\r\n  if (hub) return VISIBILITY.hub;\r\n  if (owned || friend || claimedBy) return VISIBILITY.known;\r\n  return VISIBILITY.unknown;\r\n}\r\n\r\nexport function claimAllowed({\r\n  fix,\r\n  center,\r\n  level,\r\n  dwellMs,\r\n  lastRewarded,\r\n  now\r\n} = {}) {\r\n  // accuracy\r\n  if (!fix || !Number.isFinite(fix.accuracy) || fix.accuracy > 25) {\r\n    return { ok: false, reason: 'accuracy' };\r\n  }\r\n  // distance\r\n  if (distanceMeters(fix, center) > 30.5) {\r\n    return { ok: false, reason: 'distance' };\r\n  }\r\n  // level\r\n  if (!Number.isFinite(level) || level < 2) {\r\n    return { ok: false, reason: 'level' };\r\n  }\r\n  // dwell\r\n  if (!Number.isFinite(dwellMs) || dwellMs < 90000) {\r\n    return { ok: false, reason: 'dwell' };\r\n  }\r\n  // cooldown\r\n  if (lastRewarded && lastRewarded.at != null) {\r\n    const d = distanceMeters(fix, lastRewarded);\r\n    let need;\r\n    if (d < 1000) need = 2 * 60 * 1000;          // 2 min\r\n    else if (d < 10000) need = 10 * 60 * 1000;   // 10 min\r\n    else if (d < 100000) need = 45 * 60 * 1000;  // 45 min\r\n    else if (d < 1000000) need = 90 * 60 * 1000; // 90 min\r\n    else need = 2 * 3600 * 1000;                 // 2 h\r\n    if (!Number.isFinite(now) || now - lastRewarded.at < need) {\r\n      return { ok: false, reason: 'cooldown' };\r\n    }\r\n  }\r\n  return { ok: true };\r\n}\r\n\r\nfunction fnv1a32(str) {\r\n  let hash = 0x811c9dc5;\r\n  for (let i = 0; i < str.length; i++) {\r\n    hash ^= str.charCodeAt(i);\r\n    // Math.imul ensures 32‑bit multiplication\r\n    hash = Math.imul(hash, 0x01000193) >>> 0;\r\n  }\r\n  return hash;\r\n}\r\n\r\nexport function npcSpots(zoneId, candidates = [], bakeVersion = '') {\r\n  const kept = candidates.filter(\r\n    c => Number.isFinite(c.residentialM) && c.residentialM >= 40 &&\r\n         Number.isFinite(c.institutionM) && c.institutionM >= 75\r\n  );\r\n  kept.sort((a, b) => {\r\n    const sa = `${bakeVersion}|${zoneId}|${a.latitude.toFixed(6)}|${a.longitude.toFixed(6)}`;\r\n    const sb = `${bakeVersion}|${zoneId}|${b.latitude.toFixed(6)}|${b.longitude.toFixed(6)}`;\r\n    const ha = fnv1a32(sa);\r\n    const hb = fnv1a32(sb);\r\n    if (ha !== hb) return ha - hb;\r\n    return sa < sb ? -1 : sa > sb ? 1 : 0;\r\n  });\r\n  const result = [];\r\n  for (let i = 0; i < Math.min(3, kept.length); i++) {\r\n    const c = kept[i];\r\n    result.push({ idx: i, latitude: c.latitude, longitude: c.longitude });\r\n  }\r\n  return result;\r\n}\r\n\r\n// R1-C12: the geography module never exported npcCandidates, so zone NPC lists were always empty.\r\n// Candidates = 8 points on a 100 m ring inside the zone. institutionM is the distance to the nearest known public site;\r\n// ponytail: no baked residential layer exists, so residentialM is a fixed 40 m floor value; swap in real data when baked.\r\nexport function npcCandidates(zoneId, sites = []) {\r\n  const c = zoneCenter(zoneId), dLat = 100 / 111320, dLng = dLat / Math.cos(c.latitude * Math.PI / 180);\r\n  const refs = sites.map(s => s.referenceCoordinate || s).filter(r => Number.isFinite(r.latitude));\r\n  return Array.from({ length: 8 }, (_, i) => {\r\n    const a = i * Math.PI / 4, p = { latitude: c.latitude + dLat * Math.sin(a), longitude: c.longitude + dLng * Math.cos(a) };\r\n    return { ...p, residentialM: 40, institutionM: refs.reduce((m, r) => Math.min(m, distanceMeters(p, r)), Infinity) };\r\n  });\r\n}\r\n","modules/dance/android-game/activity.mjs":"// Forgiving visible-arm activity, never a claim of exact gesture/finger recognition.\r\nconst valid=p=>p&&Number.isFinite(p.x)&&Number.isFinite(p.y)&&(p.visibility??1)>=.5;\r\nexport class ArmActivity{\r\n constructor(){this.score=0;this.activeSeconds=0;this.combo=0;this.reset();}\r\n reset(){this.previous=null;this.lastAt=-Infinity;this.energy=0;this.coverage=0;this.streak=0;}\r\n ingest(points,atMs,{enabled=false,generation=0}={}){\r\n  if(!enabled||!Number.isFinite(atMs)||atMs<=this.lastAt){this.previous=null;this.energy=0;return;}\r\n  const oldAt=this.lastAt;this.lastAt=atMs;\r\n  if(!Array.isArray(points)||!valid(points[11])||!valid(points[12])){this.previous=null;this.energy=0;this.coverage=0;return;}\r\n  const a=points[11],b=points[12],dx=b.x-a.x,dy=b.y-a.y,width=Math.hypot(dx,dy);\r\n  if(width<.08||width>.9){this.previous=null;this.energy=0;this.coverage=0;return;}\r\n  const x=dx/width,y=dy/width,arms=[];\r\n  for(const [shoulder,elbow,wrist] of [[11,13,15],[12,14,16]]){\r\n   if(!valid(points[elbow])||!valid(points[wrist])){arms.push(null);continue;}\r\n   const project=p=>{const u=(p.x-points[shoulder].x)/width,v=(p.y-points[shoulder].y)/width;return [u*x+v*y,-u*y+v*x];};\r\n   arms.push([...project(points[elbow]),...project(points[wrist])]);\r\n  }\r\n  this.coverage=arms.filter(Boolean).length/2;\r\n  const dt=(atMs-oldAt)/1000,old=this.previous;\r\n  if(!old||old.generation!==generation||dt<.015||dt>.2){this.previous={arms,generation};this.energy=0;return;}\r\n  // A short time-based filter suppresses pose jitter while retaining broad arm motion.\r\n  const alpha=1-Math.exp(-dt/.12);\r\n  const filtered=arms.map((arm,i)=>arm&&old.arms[i]?arm.map((v,j)=>old.arms[i][j]+alpha*(v-old.arms[i][j])):arm);\r\n  const speeds=filtered.map((arm,i)=>arm&&old.arms[i]?Math.hypot(...arm.map((v,j)=>v-old.arms[i][j]))/dt:null).filter(Number.isFinite);\r\n  this.previous={arms:filtered,generation};\r\n  if(!speeds.length){this.energy=0;return;}\r\n  const speed=Math.max(...speeds);\r\n  if(speed>10){this.energy=0;this.previous=null;return;}\r\n  this.energy=Math.min(1,Math.max(0,(speed-.25)/1.5));\r\n  if(this.energy>.12){this.activeSeconds+=dt;this.score+=dt*10*Math.min(1,this.energy*2);this.streak+=dt;this.combo=Math.floor(this.streak);}\r\n  else{this.streak=Math.max(0,this.streak-dt*.3);this.combo=Math.floor(this.streak);}\r\n }\r\n snapshot(now){const fresh=now-this.lastAt>=0&&now-this.lastAt<=250;return {score:Math.floor(this.score),activeSeconds:this.activeSeconds,combo:this.combo,energy:fresh?this.energy:0,coverage:fresh?this.coverage:0,tracking:fresh&&this.coverage>0};}\r\n}\r\n","modules/dance/android-game/app.mjs":"import {MOVES,sampleMove,beatsForMove} from './moves.mjs';\r\nimport {mountGameView} from './view.mjs';\r\nimport {DanceGame} from './engine.mjs';\r\nimport {ArmActivity} from './activity.mjs';\r\nimport {createArmCamera} from './camera.mjs';\r\nimport {createBeatProbe} from '../music/beat-probe.mjs';\r\nimport {createMicrophoneCapture} from '../music-lab/capture.mjs';\r\nimport {MusicSource} from './music-source.mjs';\r\nimport {GENRES,chooseGenreRoutine} from './genres.mjs';\r\nimport {BeatScore} from './beat-score.mjs';\r\nimport {createLiveAvatarRetarget,isDanceBipedCompatible} from './live-avatar.mjs';\r\nimport {visibleBodyFraction} from './body-framing.mjs';\r\n\r\nlet shellPhase='';\r\nfunction reportShellPhase(phase){if(phase===shellPhase)return;shellPhase=phase;if(window.parent!==window)window.parent.postMessage({type:'myr5:dance-phase',phase},location.origin);}\r\nlet genre='auto';try{genre=localStorage.getItem('myr5-dance-genre')||'auto';}catch{}if(!GENRES.some(g=>g.id===genre))genre='auto';\r\nconst game=new DanceGame({moves:MOVES,chooseRoutine:options=>chooseGenreRoutine({...options,genre}),seed:crypto.randomUUID()}),activity=new ArmActivity(),probe=createBeatProbe(),source=new MusicSource(),retarget=createLiveAvatarRetarget();\r\nlet beatScore=new BeatScore(),lastSourceTick=-Infinity,liveFrame=null,liveAt=-Infinity,livePoints=null,liveCompatible=true,lastJudgedBeat=0,partyUntil=0;\r\nlet ui,camera,mic,coach=null,coachReady=false,preview=false,previewId=MOVES[0].id,status='',trackingMessage='Start camera · shoulders and hands in view',cameraState='off',calibration=[],micCandidate=null,lastGameGeneration=-1,raf=0,lastRender=0,lastTrack=null,previousSession=null;\r\n// Presentation state never feeds the game, activity, score, or music gates.\r\nconst COACH_LOAD_TIMEOUT_MS=20000;\r\n// This metadata is only a pose-validation profile for the explicit outline fallback.\r\n// It is never saved, loaded as a model, or passed to the renderer. updateCoach checks\r\n// the actual selected recipe before acquiring any 3D lease.\r\nconst CAMERA_POSE_PROFILE={rigVersion:1,recipe:{body:'camera-observation',headFrom:'camera-observation',armsFrom:'camera-observation',feetFrom:'camera-observation'}};\r\nlet coachLoadState='pending',coachError=null,coachLoadAt=performance.now(),nextCoachAttempt=0;\r\nlet observedCreature=null,observedLoadGeneration,observedLoadState,observedContextLost=false,coachSuspended=false;\r\nlet liveResult={ok:false,reason:'missing_pose'};\r\nfunction clearLivePose(){liveFrame=null;livePoints=null;liveAt=-Infinity;liveResult={ok:false,reason:'camera_off'};retarget.clear();}\r\nfunction releaseCoach(){try{(coach||window.myr5Creature)?.endDance();}catch{}coachReady=false;coach=null;}\r\nfunction resetCoachLoad(now=performance.now()){releaseCoach();clearLivePose();liveCompatible=true;coachLoadState='pending';coachError=null;coachLoadAt=now;nextCoachAttempt=0;}\r\nfunction resumeCoach(){if(coachSuspended){coachSuspended=false;resetCoachLoad();}}\r\nfunction failCoach(message){releaseCoach();coachLoadState='failed';coachError=message||'Coach could not load.';}\r\nfunction updateCoach(now){\r\n if(coachSuspended)return;\r\n const creature=window.myr5Creature;let metadata;\r\n try{metadata=creature?.stats();}catch{failCoach('Coach renderer is unavailable.');return;}\r\n if(creature&&(creature!==observedCreature||metadata?.coachLoadGeneration!==observedLoadGeneration||metadata?.coachLoadState!==observedLoadState||(observedContextLost&&!metadata?.contextLost))){\r\n  resetCoachLoad(now);observedCreature=creature;observedLoadGeneration=metadata?.coachLoadGeneration;observedLoadState=metadata?.coachLoadState;\r\n }\r\n observedContextLost=!!metadata?.contextLost;\r\n if(metadata?.coachLoadState==='failed'){failCoach(metadata.coachLoadError);return;}\r\n if(metadata?.contextLost){failCoach('Coach graphics were interrupted.');return;}\r\n if(coachLoadState==='failed')return;\r\n // Missing recipe metadata is pending, including with an older generated bundle.\r\n const loaded=metadata?.coachLoadState==='ready'||(!metadata?.coachLoadState&&!!metadata?.recipe);\r\n if(loaded){\r\n  liveCompatible=isDanceBipedCompatible(metadata);\r\n  if(!liveCompatible){releaseCoach();coachLoadState='unsupported';return;}\r\n  if(!coachReady&&now>=nextCoachAttempt){\r\n   nextCoachAttempt=now+500;\r\n   try{creature.setMaxFps?.(30);coachReady=creature.dance({host:ui.coachHost})===true;if(coachReady){coach=creature;coachLoadState='ready';}}\r\n   catch{failCoach('Coach renderer could not start.');return;}\r\n  }\r\n }else{liveCompatible=true;if(coachReady)resetCoachLoad(now);}\r\n if(!coachReady&&now-coachLoadAt>=COACH_LOAD_TIMEOUT_MS)failCoach('Coach loading timed out.');\r\n}\r\nfunction coachPresentation(now){\r\n const age=Number.isFinite(liveAt)?Math.max(0,now-liveAt):null;\r\n const cameraOff=!['starting','loading','ready'].includes(cameraState);\r\n const acquiring=!cameraOff&&(cameraState!=='ready'||!game.cameraReady);\r\n const fresh=!cameraOff&&!acquiring&&liveResult.ok&&age!==null&&age<=250;\r\n const avatarTrackingState=cameraOff?'off':acquiring?'acquiring':fresh?'fresh':'stale';\r\n const livePoseReason=cameraOff?'camera_off':acquiring?'camera_acquiring':age===null?'missing_pose':age>250?'stale_frame':liveResult.reason;\r\n const coachState=coachLoadState==='ready'?({off:'camera_off',acquiring:'camera_acquiring',fresh:'fresh',stale:'stale'}[avatarTrackingState]):coachLoadState;\r\n const coachMessage={pending:'Loading coach',failed:'Coach unavailable',unsupported:'Outline only',camera_off:'Camera off',camera_acquiring:'Find your arms',fresh:'',stale:'Tracking lost'}[coachState];\r\n return {coachState,coachLoadState,avatarTrackingState,coachMessage,coachError,coachRecovery:coachLoadState==='failed'?'Reopen Dance to retry, or import your saved coach again.':coachLoadState==='unsupported'?'Choose a two-legged coach for 3D Dance. Your saved coach is unchanged.':null,livePoseReason,livePoseAgeMs:age};\r\n}\r\nconst actions={\r\n camera:()=>{resumeCoach();void camera.start();},\r\n listen:()=>{if(mic.state==='listening'){void mic.stop();return;}probe.reset();void mic.start();},\r\n stopListening:()=>void mic.stop(),\r\n musicSource:mode=>{if(game.phase==='results')return;preview=false;game.pause();void source.select(mode).catch(()=>{status=source.error;});},\r\n nextSong:()=>void source.next().catch(()=>{status=source.error;}),\r\n genre:value=>{if(!GENRES.some(g=>g.id===value))return;genre=value;try{localStorage.setItem('myr5-dance-genre',genre);}catch{}if(game.tempo){game.routine=chooseGenreRoutine({trackKey:game.track,bpm:game.tempo.displayBpm,intensity:'easy',seed:game.seed,genre});if(['dancing','count_in'].includes(game.phase))game.pause();}status='Style selected · join when ready';},\r\n back:()=>{if(window.parent!==window){window.parent.postMessage({type:'myr5:dance-close'},location.origin);}else location.assign('/pose#workout');},\r\n tapTempo:at=>{if(game.tap(at))status='Tempo set from your taps';else status=game.taps.length>=4?'Keep tapping steadily · using your latest four taps':`Tap the steady beat · ${game.taps.length} / 4`;},\r\n begin:()=>{preview=false;if(!game.begin())status=!game.cameraReady?'Start camera and show your shoulders and hands':!game.tempo?'Hear the beat or tap it four times':'Waiting for the next beat';},\r\n pause:()=>{if(preview){previewPausedAt??=performance.now();activity.reset();return;}source.pauseOwned();game.pause();activity.reset();},\r\n resume:()=>{if(preview){if(previewPausedAt!==null)previewStartedAt+=performance.now()-previewPausedAt;previewPausedAt=null;return;}void source.resumeOwned().then(()=>{driveSource();if(game.begin())status='Joining on the beat';else status='Reconnect camera and start your music';}).catch(()=>{status='Music could not resume. Try another source.';});},\r\n finish:()=>{preview=false;game.finish();source.pauseOwned();activity.reset();void mic.stop();void camera.stop();saveSession();},\r\n restart:()=>{beatScore=new BeatScore();activity.score=0;activity.activeSeconds=0;activity.combo=0;activity.reset();songCount=0;lastCountedTrack=null;const track=game.clock.snapshot(performance.now()).trackKey;if(track!==game.track){game.track=track;game.tempo=null;game.routine=[];game.taps=[];}game.started=false;game.manualPause=false;game.phase=game.track?'prepare':'waiting';game.breakUntil=performance.now();preview=false;status='Start camera, then join the song';void source.resumeOwned().catch(()=>{});},\r\n preview:()=>{resumeCoach();preview=!preview;if(preview){previewStartedAt=performance.now();previewPausedAt=null;}status=preview?'Preview · no music connection or points':'Connect a player from the Android companion';window.scrollTo({top:0});},\r\n choosePreview:id=>{resumeCoach();preview=true;previewId=id;previewStartedAt=performance.now();previewPausedAt=null;status='Move preview · not scored';window.scrollTo({top:0});},\r\n showLibrary:open=>{if(open&&game.phase==='dancing')game.pause();},\r\n importCoach:async file=>{try{if(file.size>65536)throw Error('Choose a coach recipe smaller than 64 KB');const module=await import('/modules/dance/coach-phone.js');const recipe=module.importCreature(await file.text());if(!isDanceBipedCompatible({rigVersion:1,recipe}))throw Error('Choose a two-legged coach for Dance. Four-legged coaches are not supported.');resetCoachLoad();clearLivePose();localStorage.setItem('myr5-recipe-v1',JSON.stringify(recipe));window.dispatchEvent(new CustomEvent('myr5:recipe',{detail:recipe}));status='Your coach is changing';}catch(e){status=e.message||'Coach could not import';}}\r\n};\r\nui=mountGameView({actions,moves:MOVES,sampleMove,genres:GENRES});\r\ncamera=createArmCamera({video:ui.video,onState:(state,message)=>{cameraState=state;if(state!=='ready'){game.setCamera(false);calibration=[];clearLivePose();}trackingMessage=message||({off:'Camera off',starting:'Starting camera…',loading:'Loading movement tracker…',ready:'Show shoulders and at least one complete arm'}[state]);},onFrame:(points,atMs,latency)=>{\r\n const now=performance.now(),f=game.frame();liveAt=atMs;livePoints=points;\r\n const metadata=window.myr5Creature?.stats();\r\n const live=retarget({landmarks:points,timestampMs:atMs,nowMs:now,cameraReady:cameraState==='ready',mirrored:true,cameraGeneration:f.generation,coachMetadata:isDanceBipedCompatible(metadata)?metadata:CAMERA_POSE_PROFILE});liveFrame=live.frame;liveResult={ok:live.ok,reason:live.reason};\r\n const visible=i=>points?.[i]&&(points[i].visibility??0)>=.55;\r\n const upper=latency<250&&visible(11)&&visible(12)&&((visible(13)&&visible(15))||(visible(14)&&visible(16)));\r\n if(!upper){calibration=[];trackingMessage='Keep shoulders and a hand in the small preview';activity.ingest(null,atMs,{enabled:f.phase==='dancing',generation:f.generation});return;}\r\n calibration.push(atMs);calibration=calibration.filter(t=>atMs-t<=1500);\r\n if(!game.cameraReady&&calibration.length>=6&&atMs-calibration[0]>=600){game.setCamera(true);status='Camera ready';}\r\n trackingMessage='Arms in view · move your way';\r\n const move=MOVES.find(m=>m.id===f.moveId);\r\n activity.ingest(points,atMs,{enabled:!preview&&f.phase==='dancing'&&f.playing&&f.fresh&&!move?.optionalTurn,generation:f.generation});\r\n if(now-atMs>250)activity.reset();\r\n}});\r\nmic=createMicrophoneCapture({onPCM:(samples,rate,at)=>probe.push(samples,rate,at),onState:({state,message})=>{if(state!=='listening'){probe.reset();micCandidate=null;}status=message;}});\r\nfunction driveSource(){\r\n const snapshot=source.snapshot();\r\n if(game.receive(snapshot)){\r\n  if(game.track!==lastTrack){lastTrack=game.track;probe.reset();micCandidate=null;activity.reset();status=game.started?'New song · shake it out':'Song connected · find the beat';}\r\n  if(snapshot.knownBpm&&!game.tempo&&snapshot.playing){game.setTempo(snapshot.knownBpm,0,'original');}\r\n }\r\n}\r\nconst receive=event=>{if(source.acceptNative(event.detail))driveSource();};\r\nwindow.addEventListener('myr5:external-music',receive);\r\n// This isolated origin has its own saved coach; import a creator export to keep a personal design.\r\ntry{previousSession=JSON.parse(localStorage.getItem('myr5-android-dance-last')||'null');}catch{}\r\nconst home=document.createElement('div');home.id='view';home.style.cssText='position:fixed;width:1px;height:1px;left:-10000px;top:0;overflow:hidden';document.body.append(home);\r\nvoid import('/modules/dance/coach-phone.js').catch(()=>{failCoach('Coach module could not load.');});\r\nfunction saveSession(){\r\n try{previousSession={at:new Date().toISOString(),...beatScore.snapshot(),activeSeconds:Math.round(activity.activeSeconds),songCount:songCount};localStorage.setItem('myr5-android-dance-last',JSON.stringify(previousSession));}catch{status='Session complete · this device could not save it';}\r\n}\r\nfunction presentation(move){return {title:move.title,cue:move.cue,sideCue:''};}\r\n\r\nlet previewStartedAt=0,previewPausedAt=null;\r\nlet songCount=0,lastCountedTrack=null;\r\nfunction tick(){\r\n const now=performance.now();raf=requestAnimationFrame(tick);\r\n if(now-lastRender<33)return;lastRender=now;\r\n if(now-lastSourceTick>=200){lastSourceTick=now;driveSource();}\r\n let f=game.frame();\r\n if(f.generation!==lastGameGeneration){activity.reset();lastGameGeneration=f.generation;}\r\n if(!game.tempo&&mic.state==='listening'&&f.fresh&&f.playing&&!preview){\r\n  const pulse=probe.snapshot(now);\r\n  if(pulse.available&&pulse.confidence>=.8){\r\n   if(!micCandidate||Math.abs(micCandidate.bpm-pulse.tempoBpm)>2)micCandidate={at:now,bpm:pulse.tempoBpm};\r\n   if(now-micCandidate.at>=900){try{if(game.setTempo(pulse.tempoBpm,f.positionSeconds-pulse.phase*60/pulse.tempoBpm*f.playbackSpeed,'microphone'))status='Beat found · estimated microphone tempo';}catch{status='Try a steadier beat or tap the tempo';}}\r\n  }else micCandidate=null;\r\n }\r\n f=game.frame();\r\n if(f.phase==='dancing'&&f.trackKey!==lastCountedTrack){lastCountedTrack=f.trackKey;songCount++;}\r\n if(preview){const previewSeconds=Math.max(0,((previewPausedAt??now)-previewStartedAt)/1000),entry=MOVES.find(m=>m.id===previewId)||MOVES[0];f={...f,phase:previewPausedAt===null?'dancing':'paused',moveId:entry.id,movePhase:(previewSeconds*2/beatsForMove(entry,120))%1,positionSeconds:previewSeconds,bpm:120,upcoming:MOVES.filter(m=>m.id!==entry.id).slice(0,4),countdown:null};}\r\n let move=MOVES.find(m=>m.id===f.moveId)||MOVES[0];\r\n if(f.phase==='break'){const elapsed=Math.max(0,now-(game.breakUntil-8000))/1000;move=MOVES.find(m=>m.id==='raise-the-roof')||MOVES[0];f.moveId=move.id;f.movePhase=(elapsed*70/60/beatsForMove(move,70))%1;}\r\n updateCoach(now);\r\n let coachView=coachPresentation(now);\r\n if(coachReady){\r\n  const fresh=coachView.avatarTrackingState==='fresh';\r\n  const frame=fresh&&liveFrame?liveFrame:retarget.neutralFrame;\r\n  try{if(coach.danceFrame({...frame,visibleBody:visibleBodyFraction(fresh?livePoints:null)})===false)failCoach('Coach pose renderer is unavailable.');}\r\n  catch{failCoach('Coach pose renderer is unavailable.');}\r\n  coachView=coachPresentation(now);\r\n }\r\n const metrics=activity.snapshot(now);\r\n const stars=beatScore.ingest({positionSeconds:f.positionSeconds,bpm:game.tempo?.bpm,firstBeatSeconds:game.tempo?.firstBeatSeconds,energy:metrics.energy,tracking:metrics.tracking,coverage:metrics.coverage,observedAtMs:activity.lastAt,enabled:!preview&&f.phase==='dancing'&&f.playing&&f.fresh&&!move.optionalTurn,generation:f.generation,trackKey:f.trackKey},now);\r\n if(stars.judgedBeats!==lastJudgedBeat){lastJudgedBeat=stars.judgedBeats;partyUntil=now+260;if(window.parent!==window)window.parent.postMessage({type:'myr5:dance-grade',grade:stars.judgment==='PERFECT'?'great':stars.judgment==='GOOD'?'good':'miss'},location.origin);}\r\n if(stars.roundComplete&&f.phase==='dancing'&&!preview)actions.finish();\r\n reportShellPhase(!document.hidden&&!preview&&f.phase==='dancing'&&f.playing?'active':'paused');\r\n const beatPosition=preview?f.positionSeconds*2:game.tempo?(f.positionSeconds-game.tempo.firstBeatSeconds)*game.tempo.bpm/60:null;\r\n ui.render({...f,beatPosition,tempoBpm:preview?120:game.tempo?.displayBpm??null,...coachView,...presentation(move,f.movePhase??0),...(f.phase==='break'?{title:'Shake it out',cue:'Loosen your arms. A fresh groove is coming.'}:{}),...stars,partyActive:now<partyUntil,liveLandmarks:cameraState==='ready'&&game.cameraReady&&now-liveAt<=250&&liveResult.ok?livePoints:null,liveCompatible,musicMode:source.mode,nativeAvailable:!!source.native.data,genre,handCue:move.handCue,optionalTurn:move.optionalTurn,trackTitle:f.title,artist:f.artist,cameraReady:game.cameraReady,cameraState,coachReady,micState:mic.state,status:source.error||status,trackingMessage:preview?'Preview · no camera scoring':!liveCompatible?'Choose a two-legged coach · live outline available':move.optionalTurn?'Optional turn · not scored':metrics.tracking?trackingMessage:cameraState==='ready'?'Move an arm · no exact pose needed':trackingMessage,combo:metrics.combo,activeSeconds:metrics.activeSeconds,energy:metrics.energy,catalogueCount:MOVES.length,breakMessage:f.phase==='break'?'Shake it out!':null,preview,result:previousSession});\r\n}\r\nraf=requestAnimationFrame(tick);\r\nif(window.parent!==window){document.documentElement.dataset.embedded='true';window.parent.postMessage({type:'myr5:dance-ready'},location.origin);}\r\nfunction stopHidden(){reportShellPhase('paused');coachSuspended=true;if(activity.activeSeconds>0)saveSession();source.pauseOwned();game.hide();preview=false;activity.reset();probe.reset();calibration=[];clearLivePose();void mic.stop('Paused while away. Enable the microphone again if needed.');void camera.stop();releaseCoach();}\r\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)stopHidden();else resumeCoach();});\r\nwindow.addEventListener('myr5:game-suspend',stopHidden);\r\nwindow.addEventListener('message',event=>{if(event.origin===location.origin&&event.source===window.parent&&event.data?.type==='myr5:dance-suspend')stopHidden();});\r\nwindow.addEventListener('orientationchange',()=>{game.setCamera(false);calibration=[];activity.reset();status='Recenter shoulders and hands after rotating';});\r\nwindow.addEventListener('pagehide',()=>{stopHidden();source.dispose();cancelAnimationFrame(raf);ui.dispose();window.removeEventListener('myr5:external-music',receive);window.removeEventListener('myr5:game-suspend',stopHidden);});\r\n","modules/dance/android-game/beat-score.mjs":"// Beat timing and broad arm activity, never exact-pose recognition.\r\nexport const ROUND_BEATS=120;\r\nexport const STAR_THRESHOLDS=Object.freeze([2400,4800,7200,9000,10800]);\r\nconst finite=Number.isFinite;\r\nexport class BeatScore {\r\n constructor(){this.score=0;this.judgedBeats=0;this.judgment='KEEP MOVING';this.seen=new Set();this.pending=null;this.last=null;}\r\n snapshot(){return {score:this.score,maxScore:12000,stars:STAR_THRESHOLDS.filter(n=>this.score>=n).length,nextStarAt:STAR_THRESHOLDS.find(n=>this.score<n)??null,judgment:this.judgment,beatProgress:this.judgedBeats/ROUND_BEATS,judgedBeats:this.judgedBeats,roundComplete:this.judgedBeats>=ROUND_BEATS};}\r\n ingest(input,now){\r\n  const {positionSeconds:p,bpm,firstBeatSeconds:origin,energy,tracking,coverage,enabled,generation=0,trackKey=null,observedAtMs}=input??{};\r\n  const valid=enabled&&finite(now)&&finite(observedAtMs)&&now>=observedAtMs&&now-observedAtMs<=250&&finite(p)&&p>=0&&finite(bpm)&&bpm>=40&&bpm<=240&&finite(origin);\r\n  if(!valid){this.pending=null;this.last=null;return this.snapshot();}\r\n  if(this.last&&now<=this.last.now)return this.snapshot();\r\n  const period=60/bpm,window=Math.min(period*.22,.18),beat=(p-origin)/period;\r\n  const discontinuity=this.last&&(trackKey!==this.last.trackKey||generation!==this.last.generation||bpm!==this.last.bpm||origin!==this.last.origin||p<this.last.p||now-this.last.now>500||Math.abs((p-this.last.p)-(now-this.last.now)/1000)>.25);\r\n  if(discontinuity)this.pending=null;\r\n  const starting=!this.last||discontinuity;\r\n  this.last={now,p,generation,bpm,origin,trackKey};\r\n  if(this.judgedBeats>=ROUND_BEATS)return this.snapshot();\r\n  // Join only a beat whose center has not passed. Resuming cannot earn an old beat.\r\n  if(starting){this.pending=null;this.nextBeat=Math.max(0,Math.ceil(beat-1e-7));}\r\n  if(!this.pending){\r\n   while(this.seen.has(JSON.stringify([trackKey,this.nextBeat])))this.nextBeat++;\r\n   if(beat>=this.nextBeat-window/period)this.pending={index:this.nextBeat,quality:0,active:false};\r\n  }\r\n  const pending=this.pending;\r\n  if(!pending)return this.snapshot();\r\n  const offset=p-(origin+pending.index*period);\r\n  if(Math.abs(offset)<=window&&tracking===true&&finite(coverage)&&coverage>=.5&&finite(energy)){\r\n   if(energy>.04)pending.active=true;\r\n   if(energy>=.12){const quality=energy>=.25&&Math.abs(offset)<=window*.6?100:75;pending.quality=Math.max(pending.quality,quality);}\r\n  }\r\n  if(offset>=window){\r\n   this.seen.add(JSON.stringify([trackKey,pending.index]));this.judgedBeats++;this.score+=pending.quality;\r\n   this.judgment=pending.quality===100?'PERFECT':pending.quality===75?'GOOD':pending.active?'KEEP MOVING':'MISS';\r\n   this.pending=null;this.nextBeat=pending.index+1;\r\n   // Forward gaps are never fabricated as opportunities or scored with a fresh pose.\r\n   if(beat-this.nextBeat>window/period)this.nextBeat=Math.ceil(beat);\r\n  }\r\n  return this.snapshot();\r\n }\r\n}\r\n\r\n","modules/dance/android-game/body-framing.mjs":"/** Presentation crop only. No lower-body landmark is required or scored. */\r\nexport function visibleBodyFraction(points){\r\n if(!Array.isArray(points))return .5;\r\n const visible=index=>{\r\n  const p=points[index];\r\n  return !!p&&Number.isFinite(p.x)&&Number.isFinite(p.y)&&p.x>=0&&p.x<=1&&p.y>=0&&p.y<=1&&\r\n   Number.isFinite(p.visibility)&&p.visibility>=.5&&\r\n   (p.presence===undefined||(Number.isFinite(p.presence)&&p.presence>=.5));\r\n };\r\n const pair=(a,b)=>visible(a)&&visible(b);\r\n // Both sides must be visible: a lone inferred leg must not reveal the coach's feet.\r\n if(pair(27,28))return 1;\r\n if(pair(25,26))return .82;\r\n if(pair(23,24))return .65;\r\n return .5;\r\n}\r\n","modules/dance/android-game/camera.mjs":"// Existing MediaPipe Tasks model, one camera and CPU detector; no recording/upload.\r\nexport function createArmCamera({video,onFrame,onState=()=>{}}){\r\n let generation=0,stream=null,detector=null,raf=0,lastFrame=-1,lastAt=0;\r\n async function stop(){generation++;cancelAnimationFrame(raf);stream?.getTracks().forEach(t=>t.stop());stream=null;video.srcObject=null;detector?.close();detector=null;onState('off');}\r\n async function start(){\r\n  if(stream)return;\r\n  const ticket=++generation;onState('starting');let pendingStream=null,pendingDetector=null;\r\n  try{\r\n   pendingStream=await navigator.mediaDevices.getUserMedia({video:{facingMode:'user',width:{ideal:640},height:{ideal:480}},audio:false});\r\n   if(ticket!==generation){pendingStream.getTracks().forEach(t=>t.stop());return;}\r\n   stream=pendingStream;video.srcObject=stream;await video.play();onState('loading');\r\n   const base='/vendor/mediapipe/0.10.14';\r\n   const {FilesetResolver,PoseLandmarker}=await import(base+'/vision_bundle.mjs');\r\n   const files=await FilesetResolver.forVisionTasks(base+'/wasm');\r\n   pendingDetector=await PoseLandmarker.createFromOptions(files,{baseOptions:{modelAssetPath:base+'/pose_landmarker_lite.task',delegate:'CPU'},runningMode:'VIDEO',numPoses:1});\r\n   if(ticket!==generation){pendingDetector.close();return;}\r\n   detector=pendingDetector;lastFrame=-1;lastAt=0;\r\n   for(const track of stream.getTracks())track.onended=()=>{if(ticket===generation){void stop();onState('error','Camera disconnected. Tap camera to reconnect.');}};\r\n   onState('ready');\r\n   function tick(){\r\n    if(ticket!==generation)return;\r\n    const now=performance.now();\r\n    if(video.readyState>=2&&video.currentTime!==lastFrame&&now-lastAt>=90){\r\n     lastFrame=video.currentTime;lastAt=now;\r\n     try{const result=detector.detectForVideo(video,now);onFrame(result.landmarks?.[0]??null,now,performance.now()-now);}catch{void stop();onState('error','Camera tracking interrupted. Tap camera to retry.');return;}\r\n    }\r\n    raf=requestAnimationFrame(tick);\r\n   }\r\n   raf=requestAnimationFrame(tick);\r\n  }catch(e){if(ticket===generation){await stop();onState('error',e.name==='NotAllowedError'?'Camera permission is off. Enable camera in the Android toolbar, then retry.':e.message||'Camera could not start.');}}\r\n }\r\n return {start,stop};\r\n}\r\n","modules/dance/android-game/club-demonstrator.mjs":"import * as THREE from 'three';\r\n\r\n// Authored solid teaching mannequin. This is a visual reference, never a tracker\r\n// or a saved-coach lease. A single pooled rig is drawn into at most four slots.\r\nexport function createClubDemonstrator(panel, sampler) {\r\n  const canvas = document.createElement('canvas');\r\n  canvas.className = 'dg-solid-demonstrators';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  panel.append(canvas);\r\n  let renderer;\r\n  try { renderer = new THREE.WebGLRenderer({canvas, alpha:true, antialias:true, powerPreference:'low-power'}); }\r\n  catch { canvas.remove(); panel.dataset.demonstrator = 'unavailable'; return {render(){}, dispose(){}}; }\r\n  renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio || 1, 1.5));\r\n  renderer.setClearColor(0x000000, 0); renderer.autoClear = false;\r\n  renderer.info.autoReset = false;\r\n  const scene = new THREE.Scene();\r\n  scene.add(new THREE.HemisphereLight(0xd3faff, 0x34304f, 2.4));\r\n  const key = new THREE.DirectionalLight(0xffffff, 2.7); key.position.set(-3,6,5); scene.add(key);\r\n  const rim = new THREE.DirectionalLight(0xa17aff, 2); rim.position.set(3,2,-3); scene.add(rim);\r\n  const camera = new THREE.OrthographicCamera(-2,2,2.5,-2.5,.1,30);\r\n  camera.position.set(0,2.8,10); camera.lookAt(0,1.6,0);\r\n  const box = new THREE.BoxGeometry(1,1,1), headGeo = new THREE.IcosahedronGeometry(1,1);\r\n  const material = new THREE.MeshStandardMaterial({roughness:.36, metalness:.25});\r\n  const blocks = new THREE.InstancedMesh(box, material, 20); blocks.count = 0; blocks.frustumCulled = false;\r\n  blocks.instanceMatrix.setUsage(THREE.DynamicDrawUsage); scene.add(blocks);\r\n  const head = new THREE.Mesh(headGeo, new THREE.MeshStandardMaterial({color:0xd7f4ff, roughness:.3, metalness:.25})); scene.add(head);\r\n  const floorGeo = new THREE.CylinderGeometry(1.45,1.52,.10,32);\r\n  const floor = new THREE.Mesh(floorGeo, new THREE.MeshStandardMaterial({color:0x254452,roughness:.3,metalness:.65,emissive:0x092631}));\r\n  floor.position.y=-.08; scene.add(floor);\r\n  const shadowGeo = new THREE.CircleGeometry(.56,24), shadowMat = new THREE.MeshBasicMaterial({color:0x030612,transparent:true,opacity:.65,depthWrite:false});\r\n  const shadow = new THREE.Mesh(shadowGeo,shadowMat); shadow.rotation.x=-Math.PI/2; shadow.scale.set(1,.42,1); shadow.position.set(0,-.022,0); scene.add(shadow);\r\n  const dummy = new THREE.Object3D(), up = new THREE.Vector3(0,1,0), direction = new THREE.Vector3(), color = new THREE.Color();\r\n  let disposed=false, lost=false, width=0, height=0, lastFrameKey='';\r\n  function onLost(e){e.preventDefault();lost=true;panel.dataset.demonstrator='unavailable';}\r\n  function onRestored(){lost=false;lastFrameKey='';panel.dataset.demonstrator='solid';}\r\n  canvas.addEventListener('webglcontextlost',onLost);canvas.addEventListener('webglcontextrestored',onRestored);\r\n  const point=(p,fallback)=>new THREE.Vector3(p?.x??fallback[0],2.35-(p?.y??fallback[1]),-(p?.z??0)*.28);\r\n  function pose(sample) {\r\n    const p=sample.landmarks, demo=sample.demo||{}, sway=Number(demo.bodySway)||0, bounce=Math.max(0,Number(demo.bodyBounce)||0), step=Number(demo.step)||0, lean=Number(demo.lean)||0;\r\n    const left=point(p[11],[-.5,0]),right=point(p[12],[.5,0]);\r\n    const hips=[new THREE.Vector3(-.29+sway,1.36-bounce,0),new THREE.Vector3(.29+sway,1.36-bounce,0)];\r\n    const ankles=[new THREE.Vector3(-.36+Math.min(0,step)*.35,.13,.12),new THREE.Vector3(.36+Math.max(0,step)*.35,.13,.12)];\r\n    let n=0;\r\n    const block=(position,scale,tint,quaternion)=>{dummy.position.copy(position);dummy.scale.set(...scale);dummy.quaternion.copy(quaternion||new THREE.Quaternion());dummy.updateMatrix();blocks.setMatrixAt(n,dummy.matrix);blocks.setColorAt(n++,color.set(tint));};\r\n    const bone=(a,b,w,d,tint)=>{direction.subVectors(b,a);dummy.quaternion.setFromUnitVectors(up,direction.clone().normalize());const q=dummy.quaternion.clone();block(a.clone().add(b).multiplyScalar(.5),[w,direction.length()+.035,d],tint,q);};\r\n    const shoulder=left.clone().add(right).multiplyScalar(.5).add(new THREE.Vector3(lean*.45,-Math.abs(lean)*.3,0)),hip=hips[0].clone().add(hips[1]).multiplyScalar(.5);\r\n    bone(shoulder,hip,.79,.39,0xa9dfea);block(hip,[.66,.25,.42],0x525c86);\r\n    for(let side=0;side<2;side++){\r\n      // Bring the reference arms in front of the solid chest so crossed hands remain readable.\r\n      const a=(side?right:left).clone(),e=point(p[13+side],[side?.65:-.65,.4]),w=point(p[15+side],[side?.7:-.7,.8]);\r\n      for(const joint of [a,e,w])joint.z+=.36;\r\n      const tint=side?0xffbd69:0x63e6eb;\r\n      bone(a,e,.23,.25,tint);bone(e,w,.20,.22,tint);block(w,[.23,.26,.21],0xe1f4ed);\r\n      const knee=hips[side].clone().add(ankles[side]).multiplyScalar(.5);knee.z+=.09+bounce*.6;\r\n      bone(hips[side],knee,.27,.30,0x697aaf);bone(knee,ankles[side],.23,.25,0x8fadd2);\r\n      block(ankles[side].clone().add(new THREE.Vector3(0,-.05,.13)),[.29,.16,.46],tint);\r\n    }\r\n    head.position.copy(point(p[0],[0,-.65]));head.position.x+=lean*.8;head.position.y-=.08+Math.abs(lean)*.5;head.scale.set(.29,.36,.28);\r\n    block(head.position.clone().add(new THREE.Vector3(0,.025,.247)),[.42,.115,.075],0x142037);\r\n    block(shoulder.clone().lerp(hip,.36).add(new THREE.Vector3(0,0,.208)),[.27,.17,.035],0xc7ff86);\r\n    blocks.count=n;blocks.instanceMatrix.needsUpdate=true;blocks.instanceColor.needsUpdate=true;\r\n  }\r\n  panel.dataset.demonstrator='solid';\r\n  return {\r\n    render(slots){\r\n      if(disposed||lost||document.hidden)return;\r\n      const bounds=panel.getBoundingClientRect();if(bounds.width<1||bounds.height<1)return;\r\n      if(width!==bounds.width||height!==bounds.height){width=bounds.width;height=bounds.height;renderer.setSize(width,height,false);}\r\n      const frameKey=slots.slice(0,4).map(s=>{const r=s.element.getBoundingClientRect();return `${s.id}:${s.phase}:${r.left-bounds.left}:${r.top-bounds.top}:${r.width}:${r.height}`;}).join('|')+`:${width}:${height}`;\r\n      if(frameKey===lastFrameKey)return;lastFrameKey=frameKey;\r\n      renderer.setScissorTest(false);renderer.clear();renderer.setScissorTest(true);renderer.info.reset();\r\n      for(const slot of slots.slice(0,4)){\r\n        const r=slot.element.getBoundingClientRect(),x=r.left-bounds.left,y=bounds.bottom-r.bottom;\r\n        if(r.width<1||r.height<1||r.right<=bounds.left||r.left>=bounds.right)continue;\r\n        let sample;try{sample=sampler(slot.id,slot.phase);}catch{continue;}\r\n        pose(sample);\r\n        // Fixed full-body framing preserves the authored reach and root travel.\r\n        const aspect=r.width/r.height,vertical=Math.max(4.45,3.5/aspect);\r\n        camera.left=-vertical*aspect/2;camera.right=vertical*aspect/2;camera.top=vertical/2;camera.bottom=-vertical/2;camera.updateProjectionMatrix();\r\n        renderer.setViewport(x,y,r.width,r.height);\r\n        renderer.setScissor(Math.max(0,x),Math.max(0,y),Math.min(r.width,width-Math.max(0,x)),Math.min(r.height,height-Math.max(0,y)));\r\n        renderer.render(scene,camera);\r\n      }\r\n      renderer.setScissorTest(false);panel.dataset.demoDrawCalls=String(renderer.info.render.calls);panel.dataset.demoTriangles=String(renderer.info.render.triangles);\r\n    },\r\n    dispose(){if(disposed)return;disposed=true;canvas.removeEventListener('webglcontextlost',onLost);canvas.removeEventListener('webglcontextrestored',onRestored);scene.traverse(o=>{if(o.geometry&&! [box,headGeo,floorGeo,shadowGeo].includes(o.geometry))o.geometry.dispose();});for(const g of [box,headGeo,floorGeo,shadowGeo])g.dispose();for(const m of [material,head.material,floor.material,shadowMat])m.dispose();renderer.dispose();renderer.forceContextLoss();canvas.remove();}\r\n  };\r\n}\r\n","modules/dance/android-game/club-presentation.mjs":"// Bounded scenery: twelve alien silhouettes, six beams and twelve glints.\r\n// Every transform is a pure function of the same authoritative beat position.\r\nexport function mountClubPresentation(stage) {\r\n  const layer=document.createElement('div');layer.className='dg-alien-club';layer.setAttribute('aria-hidden','true');\r\n  layer.innerHTML=`<div class=\"dg-club-haze\"></div><div class=\"dg-club-truss\"><i></i><i></i><i></i></div><div class=\"dg-club-lasers\">${Array.from({length:6},(_,i)=>`<i style=\"--beam:${i}\"></i>`).join('')}</div><div class=\"dg-alien-crowd\">${Array.from({length:12},(_,i)=>`<svg viewBox=\"0 0 60 112\" style=\"--alien:${i};left:${i*8.5-2}%;height:${45+i%3*8}%\"><g><path class=\"dg-alien-body\" d=\"M18 51 Q30 43 42 51 L48 80 L42 84 L37 68 L37 108 L29 108 L27 80 L23 108 L15 108 L20 67 L11 82 L5 77Z\"/><path class=\"dg-alien-head\" d=\"M13 26 Q11 4 29 6 Q49 7 47 26 Q43 47 30 49 Q17 44 13 26Z\"/><path class=\"dg-alien-eyes\" d=\"M17 24 Q27 23 27 32 Q18 34 17 24M33 32 Q33 23 43 24 Q42 34 33 32\"/></g></svg>`).join('')}</div><div class=\"dg-club-glints\">${Array.from({length:12},(_,i)=>`<i style=\"left:${8+(i*31)%85}%;top:${12+(i*19)%58}%\"></i>`).join('')}</div><div class=\"dg-coach-contact\"></div>`;\r\n  stage.insertBefore(layer,stage.querySelector('.dg-coach-wrap'));\r\n  const contact=layer.querySelector('.dg-coach-contact');stage.insertBefore(contact,stage.querySelector('.dg-coach-wrap'));\r\n  const aliens=[...layer.querySelectorAll('.dg-alien-crowd svg')],beams=[...layer.querySelectorAll('.dg-club-lasers i')],glints=[...layer.querySelectorAll('.dg-club-glints i')];\r\n  let last=null;\r\n  return {update(beat,reduced=false){if(!Number.isFinite(beat)||beat===last)return;last=beat;const t=reduced?0:beat*Math.PI*2;\r\n    aliens.forEach((node,i)=>{const parity=i%2?-1:1;node.style.transform=`translateY(${Math.cos(t)*2.5}px) rotate(${Math.sin(t*.5)*parity*5}deg) translateX(${Math.sin(t*.5)*parity*3}px)`;});\r\n    beams.forEach((node,i)=>{node.style.transform=`rotate(${-36+i*14+Math.sin(t/8+i)*7}deg)`;});\r\n    glints.forEach((node,i)=>{node.style.opacity=String(.15+.4*Math.max(0,Math.sin(t/4+i)));});\r\n    layer.style.setProperty('--club-beat',String(.23+.12*(.5+.5*Math.cos(t))));\r\n  },dispose(){contact.remove();layer.remove();}};\r\n}\r\n","modules/dance/android-game/dance-references.mjs":"// Reference index for the authored short named-move demonstrations.\r\n// Links are attribution/research, never fetched by playback. No source video,\r\n// images, music, artist likenesses or captured motion are packaged.\r\nexport const DANCE_REFERENCES=Object.freeze([\r\n  {\r\n    \"id\": \"silento\",\r\n    \"title\": \"Silent\\u00f3 official Whip / Nae Nae tutorial\",\r\n    \"url\": \"https://www.youtube.com/watch?v=fI8PDrrTU4M\",\r\n    \"type\": \"artist tutorial\",\r\n    \"moves\": [\r\n      \"whip\",\r\n      \"nae-nae\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"soulja\",\r\n    \"title\": \"Soulja Boy artist-channel Crank That performance\",\r\n    \"url\": \"https://www.youtube.com/watch?v=5pGJCkCDK5A\",\r\n    \"type\": \"artist performance\",\r\n    \"moves\": [\r\n      \"superman\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"dougie\",\r\n    \"title\": \"STEEZY Dougie demonstration and breakdown\",\r\n    \"url\": \"https://www.steezy.co/posts/how-to-do-the-dougie\",\r\n    \"type\": \"dance educator\",\r\n    \"moves\": [\r\n      \"dougie\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"quan\",\r\n    \"title\": \"iLoveMemphis artist-channel Hit the Quan\",\r\n    \"url\": \"https://www.youtube.com/watch?v=JrCUEjSzLAk\",\r\n    \"type\": \"artist performance\",\r\n    \"moves\": [\r\n      \"hit-the-quan\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"griddy\",\r\n    \"title\": \"Allen Davis Griddy creator instruction interview\",\r\n    \"url\": \"https://newtralgroundz.com/2020/12/28/how-to-do-the-griddy-dance-allen-davis/amp/\",\r\n    \"type\": \"creator instruction\",\r\n    \"moves\": [\r\n      \"griddy\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"jerk\",\r\n    \"title\": \"New Boyz explain the Jerk movement\",\r\n    \"url\": \"https://hiphopdx.com/news/id.9288/title.the-new-boyz-talk-youre-a-jerk-dance-craze-album\",\r\n    \"type\": \"artist interview\",\r\n    \"moves\": [\r\n      \"jerk\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"party\",\r\n    \"title\": \"RMC Learning Landscapes dance-break toolkit\",\r\n    \"url\": \"https://www.rmc.org/wp-content/uploads/2014/08/Learning-Landscapes-Toolkit.pdf\",\r\n    \"type\": \"educator reference\",\r\n    \"moves\": [\r\n      \"sprinkler\",\r\n      \"shopping-cart\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"gangnam\",\r\n    \"title\": \"YouTube on PSY and Gangnam Style\",\r\n    \"url\": \"https://blog.youtube/news-and-events/ytvev/\",\r\n    \"type\": \"platform archive\",\r\n    \"moves\": [\r\n      \"gangnam-style\"\r\n    ]\r\n  }\r\n].map(r=>Object.freeze({...r,moves:Object.freeze(r.moves)})));\r\nexport const DEMONSTRATION_METHOD='Original volumetric teaching models and connected key-pose loops. Broad arm activity is scored; exact choreography, footwork and fingers are not recognized.';\r\n","modules/dance/android-game/engine.mjs":"import {beatsForMove} from './moves.mjs';\r\nconst finite=Number.isFinite,mod=(a,b)=>((a%b)+b)%b;\r\nexport class ExternalMusicClock{\r\n constructor(){this.data=null;this.receivedAt=-Infinity;this.lastSequence=-1;}\r\n accept(d,now){\r\n  if(!d||d.schema!=='myr5.external-music.v1'||!finite(now)||!Number.isInteger(d.sequence)||d.sequence<=this.lastSequence||!Number.isInteger(d.epoch)||typeof d.trackKey!=='string'||d.trackKey.length>256||!finite(d.positionSeconds)||!finite(d.dispatchAgeMs)||d.dispatchAgeMs>1000||d.dispatchAgeMs<0)return false;\r\n  if(this.data&&d.epoch<this.data.epoch)return false;\r\n  this.lastSequence=d.sequence;this.receivedAt=now;\r\n  const speed=finite(d.speed)?d.speed:finite(d.playbackSpeed)?d.playbackSpeed:1;\r\n  this.data={...d,title:String(d.title||'Current song').slice(0,180),artist:String(d.artist||'').slice(0,180),playbackSpeed:speed,positionSeconds:d.positionSeconds+(d.playing&&d.fresh?d.dispatchAgeMs/1000*speed:0)};return true;\r\n }\r\n snapshot(now){\r\n  const d=this.data,age=now-this.receivedAt;\r\n  if(!d)return {fresh:false,playing:false,trackKey:null,positionSeconds:0,epoch:null};\r\n  const fresh=d.fresh===true&&age>=0&&age<=1250&&d.positionSeconds>=0&&d.playbackSpeed>=0&&d.playbackSpeed<=4&&(!d.playing||d.playbackSpeed>0);\r\n  let position=d.positionSeconds+(fresh&&d.playing?age/1000*d.playbackSpeed:0);\r\n  if(d.durationSeconds>0)position=Math.min(d.durationSeconds,position);\r\n  return {...d,positionSeconds:Math.max(0,position),fresh,playing:fresh&&d.playing===true};\r\n }\r\n invalidate(){this.receivedAt=-Infinity;}\r\n}\r\n\r\nexport class DanceGame{\r\n constructor({moves,chooseRoutine,now=()=>performance.now(),seed='session'}={}){\r\n  this.moves=moves;this.chooseRoutine=chooseRoutine;this.now=now;this.seed=seed;this.clock=new ExternalMusicClock();\r\n  this.phase='waiting';this.track=null;this.tempo=null;this.routine=[];this.started=false;this.cameraReady=false;this.breakUntil=0;this.taps=[];this.manualPause=false;this.countUntil=null;this.routineStartBeat=0;this.generation=0;\r\n }\r\n receive(d){\r\n  const now=this.now(),previous=this.clock.snapshot(now);\r\n  if(!this.clock.accept(d,now))return false;\r\n  const current=this.clock.snapshot(now);\r\n  if(this.phase==='results')return true;\r\n  if(current.trackKey&&current.trackKey!==this.track){\r\n   const transitioning=this.started,wasPaused=this.manualPause;this.track=current.trackKey;this.tempo=null;this.taps=[];this.routine=[];this.generation++;\r\n   this.phase=transitioning?'break':'prepare';this.breakUntil=now+(transitioning?8000:0);this.countUntil=null;this.manualPause=wasPaused;\r\n  }else if(previous.epoch!==null&&current.epoch!==previous.epoch){\r\n   this.generation++;this.countUntil=null;\r\n   if(this.started&&this.tempo&&this.cameraReady&&!this.manualPause&&current.playing){this.phase='count_in';this.setJoin(current.positionSeconds);}\r\n  }\r\n  return true;\r\n }\r\n setCamera(ready){this.cameraReady=!!ready;if(!ready&&['dancing','count_in'].includes(this.phase)){this.phase='paused';this.countUntil=null;this.manualPause=true;this.generation++;}}\r\n setTempo(bpm,firstBeatSeconds,source='tap'){\r\n  const s=this.clock.snapshot(this.now());\r\n  if(!s.fresh||!s.playing||!finite(bpm)||bpm<70||bpm>180||!finite(firstBeatSeconds))return false;\r\n  if(this.tempo&&['dancing','count_in'].includes(this.phase))return false;\r\n  const ids=this.chooseRoutine({trackKey:s.trackKey,bpm,intensity:'easy',seed:this.seed});\r\n  if(!ids?.length)return false;\r\n  const songBpm=bpm/s.playbackSpeed;\r\n  this.tempo={bpm:songBpm,displayBpm:bpm,firstBeatSeconds:mod(firstBeatSeconds,60/songBpm),source};this.routine=ids;return true;\r\n }\r\n tap(inputAt){\r\n  const now=this.now(),s=this.clock.snapshot(now),at=finite(inputAt)&&inputAt<=now&&inputAt>=now-1000?inputAt:now;\r\n  if(!s.playing)return false;\r\n  if(this.taps.length&&at-this.taps.at(-1)>2000)this.taps=[];\r\n  if(this.taps.length&&at<=this.taps.at(-1))return false;\r\n  this.taps.push(at);this.taps=this.taps.slice(-4);\r\n  if(this.taps.length<4)return false;\r\n  const intervals=this.taps.slice(1).map((v,i)=>v-this.taps[i]).sort((a,b)=>a-b),period=intervals[Math.floor(intervals.length/2)];\r\n  if(intervals.some(i=>Math.abs(i-period)>period*.2))return false;\r\n  return this.setTempo(60000/period,s.positionSeconds-(now-at)/1000*s.playbackSpeed,'tap');\r\n }\r\n begin(){\r\n  const now=this.now(),s=this.clock.snapshot(now);\r\n  if(!s.playing||!this.tempo||!this.cameraReady||now<this.breakUntil)return false;\r\n  this.started=true;this.manualPause=false;this.phase='count_in';\r\n  this.setJoin(s.positionSeconds);this.generation++;return true;\r\n }\r\n setJoin(position){const p=60/this.tempo.bpm;this.routineStartBeat=Math.ceil((position-this.tempo.firstBeatSeconds)/p)+4;this.countUntil=this.tempo.firstBeatSeconds+this.routineStartBeat*p;}\r\n pause(){this.manualPause=true;this.phase='paused';this.countUntil=null;this.generation++;}\r\n finish(){this.phase='results';this.manualPause=true;this.generation++;}\r\n hide(){this.cameraReady=false;this.clock.invalidate();this.countUntil=null;this.manualPause=true;this.phase='lost';this.generation++;}\r\n frame(){\r\n  const now=this.now(),s=this.clock.snapshot(now);\r\n  if(this.phase==='results')return {...s,phase:'results',generation:this.generation};\r\n  if(!s.fresh){if(this.started&&this.phase!=='lost'){this.phase='lost';this.countUntil=null;this.generation++;}return {...s,phase:this.started?'lost':this.phase,generation:this.generation};}\r\n  if(!s.playing&&this.started&&['dancing','count_in'].includes(this.phase)){this.phase='paused';this.countUntil=null;this.manualPause=true;this.generation++;}\r\n  if(this.phase==='lost'&&s.fresh){this.phase=this.started?'paused':'prepare';}\r\n  if(['prepare','break'].includes(this.phase)&&this.tempo&&now>=this.breakUntil){\r\n   if(this.started&&this.cameraReady&&!this.manualPause)this.begin();else this.phase='ready';\r\n  }\r\n  if(this.phase==='count_in'&&s.playing&&this.cameraReady&&s.positionSeconds>=this.countUntil){this.phase='dancing';}\r\n  let moveId=this.moves[0]?.id,movePhase=mod(now/5000,1),upcoming=[],countdown=null,phraseIndex=null;\r\n  if(this.tempo&&this.routine.length){\r\n   const entries=this.routine.map(id=>{const move=this.moves.find(m=>m.id===id);return {...move,beats:beatsForMove(move,this.tempo.displayBpm)};});\r\n   const totalBeats=entries.reduce((sum,m)=>sum+m.beats,0);\r\n   const beat=Math.max(0,(s.positionSeconds-this.tempo.firstBeatSeconds)*this.tempo.bpm/60-this.routineStartBeat);\r\n   const cycleBeat=mod(beat,totalBeats);let phrase=0,start=0;\r\n   while(phrase<entries.length-1&&start+entries[phrase].beats<=cycleBeat){start+=entries[phrase].beats;phrase++;}\r\n   moveId=entries[phrase].id;movePhase=(cycleBeat-start)/entries[phrase].beats;phraseIndex=phrase;\r\n   upcoming=Array.from({length:4},(_,i)=>entries[(phrase+i+1)%entries.length]);\r\n   if(this.phase==='count_in')countdown=Math.max(1,Math.ceil((this.countUntil-s.positionSeconds)*this.tempo.bpm/60));\r\n  }\r\n  return {...s,phase:this.phase,tempoReady:!!this.tempo,bpm:this.tempo?.displayBpm??null,tempoSource:this.tempo?.source??null,moveId,movePhase,upcoming,countdown,generation:this.generation,tapCount:this.taps.length,phraseIndex,routineLength:this.routine.length,breakRemaining:Math.max(0,(this.breakUntil-now)/1000)};\r\n }\r\n}\r\n","modules/dance/android-game/game.css":":root{color-scheme:dark;--dg-bg:#070b13;--dg-panel:#101725;--dg-yellow:#ffe54f;--dg-cyan:#5bf5ff;--dg-pink:#ff63be;--dg-white:#f7f8ff;--dg-muted:#a7b0c1;--dg-font:'DM Sans',system-ui,sans-serif;--dg-display:'Barlow Condensed','Arial Narrow',Impact,sans-serif}\r\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:var(--dg-bg);color:var(--dg-white);font-family:var(--dg-font)}body{min-height:100svh}.disco-game{position:relative;width:min(100%,580px);min-height:100svh;margin:0 auto;padding:env(safe-area-inset-top) 16px env(safe-area-inset-bottom);display:flex;flex-direction:column;gap:10px;overflow:hidden;background:radial-gradient(ellipse at 50% 20%,#142d3c 0,#0b1420 45%,#070b13 80%)}button,input{font:inherit}.dg-header{height:52px;flex:none;display:flex;align-items:center;justify-content:space-between;gap:8px}.dg-back{color:#d0d6e5;text-decoration:none;font-size:14px;font-weight:700;padding:10px 0}.dg-brand{font:800 15px var(--dg-display);letter-spacing:.13em;display:flex;align-items:center;gap:8px}.dg-brand-dot,.dg-live-dot{width:9px;height:9px;border-radius:50%;background:var(--dg-cyan);box-shadow:0 0 14px var(--dg-cyan)}.dg-library-toggle{border:1px solid #536476;background:#14202d;color:var(--dg-yellow);font-size:12px;font-weight:800;letter-spacing:.06em;border-radius:99px;padding:9px 12px;min-height:40px}.dg-stage{position:relative;isolation:isolate;flex:1 1 auto;min-height:360px;max-height:58svh;border:1px solid #233b4c;border-radius:26px;overflow:hidden;background:linear-gradient(155deg,#102636,#09121c 68%);box-shadow:0 12px 42px #0008,inset 0 1px #ffffff12}.dg-stage-grid{position:absolute;inset:0;opacity:.24;background-image:linear-gradient(#5bf5ff15 1px,transparent 1px),linear-gradient(90deg,#5bf5ff15 1px,transparent 1px);background-size:36px 36px;mask-image:linear-gradient(transparent,#000 28%,#000 80%,transparent)}.dg-stage-glow{position:absolute;width:72%;height:44%;left:14%;top:28%;background:radial-gradient(ellipse,#14e1ff32,transparent 68%);filter:blur(16px);pointer-events:none}.dg-stage-label{position:absolute;z-index:2;top:15px;left:17px;right:17px;display:flex;align-items:center;gap:9px;font-size:11px;letter-spacing:.16em;font-weight:800;color:#d3e1ea}.dg-live-dot{width:7px;height:7px}.dg-streak{margin-left:auto;color:var(--dg-yellow);letter-spacing:.03em;font-size:16px}.dg-coach-wrap{position:absolute;inset:8% 6% 3%;display:flex;align-items:center;justify-content:center;perspective:850px}.dg-coach-depth{width:100%;height:100%;transform:rotateY(-7deg) rotateX(1deg);transform-style:preserve-3d;filter:drop-shadow(0 18px 20px #00dff522)}.dg-coach{width:100%;height:100%;display:block}.dg-cam-frame{position:absolute;z-index:3;right:13px;top:45px;width:clamp(83px,25vw,132px);aspect-ratio:4/3;border:2px solid #5bf5ff;border-radius:13px;overflow:hidden;background:#061018;box-shadow:0 5px 18px #0009,0 0 14px #5bf5ff35}.dg-cam-frame video{width:100%;height:100%;display:block;object-fit:cover;transform:scaleX(-1)}.dg-cam-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;color:#a6c7d1;background:linear-gradient(145deg,#142632,#09121b);font-size:10px;letter-spacing:.1em}.dg-cam-placeholder span{font-size:22px;color:var(--dg-cyan)}.dg-cam-placeholder[hidden]{display:none}.dg-cam-tag{position:absolute;bottom:4px;left:6px;background:#061018cc;border-radius:4px;padding:2px 5px;color:#fff;font-size:9px;font-weight:800;letter-spacing:.1em}.dg-stage-foot{position:absolute;left:15px;right:15px;bottom:10px;display:flex;align-items:center;justify-content:space-between;gap:8px;color:#aab9c7;font-size:12px;font-weight:600}.dg-minutes{color:var(--dg-cyan);font-size:10px;letter-spacing:.1em;white-space:nowrap}.dg-now{position:relative;flex:none;min-height:236px;border-radius:24px;padding:15px 17px 12px;background:linear-gradient(145deg,#182131,#101724);border:1px solid #344253;box-shadow:0 12px 30px #0005;overflow:hidden}.dg-now:before{content:\"\";position:absolute;inset:0 auto 0 0;width:4px;background:linear-gradient(var(--dg-yellow),var(--dg-pink))}.dg-now-head{display:flex;justify-content:space-between;align-items:center}.dg-now-label{font:900 24px var(--dg-display);letter-spacing:.06em;color:var(--dg-yellow);display:flex;gap:9px;align-items:center}.dg-now-label i{width:9px;height:9px;border-radius:50%;background:var(--dg-pink);box-shadow:0 0 13px var(--dg-pink)}.dg-move-count{color:#aeb8c8;font-size:11px;letter-spacing:.1em;font-weight:800}.dg-move-main{display:flex;align-items:center;min-height:82px;gap:6px}.dg-move-copy{flex:1;min-width:0}.dg-move-copy h2{font:900 clamp(34px,9vw,48px)/.95 var(--dg-display);letter-spacing:.01em;margin:6px 0 5px;color:white;text-shadow:2px 3px #03060a}.dg-move-copy p{font-size:15px;line-height:1.25;font-weight:600;color:#c9d2df;margin:0;max-width:34ch}.dg-move-glyph{flex:none;width:86px;height:76px;filter:drop-shadow(0 0 9px #5bf5ff35)}.dg-glyph{width:100%;height:100%}.dg-move-meta{height:22px;display:flex;align-items:center;justify-content:space-between;color:var(--dg-cyan);font-size:10px;letter-spacing:.1em;font-weight:900}.dg-move-meta:empty{display:none}.dg-upnext{display:flex;align-items:center;gap:11px;margin-top:5px;min-height:32px;overflow:hidden}.dg-upnext-label{flex:none;color:#91a0b4;font-size:10px;letter-spacing:.14em;font-weight:900}.dg-upnext-list{display:flex;gap:7px;overflow:auto;scrollbar-width:none}.dg-upnext-list::-webkit-scrollbar{display:none}.dg-next-chip{display:inline-flex;align-items:center;gap:6px;max-width:140px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border:1px solid #344357;border-radius:99px;padding:6px 10px;color:#c4cfdb;font-size:11px;font-weight:700}.dg-next-chip.is-next{border-color:#5bf5ff66;color:#eaffff}.dg-next-chip i{color:var(--dg-cyan);font-size:9px;font-style:normal}.dg-next-empty{color:#778498;font-size:11px}.dg-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background:#293548}.dg-progress i{height:100%;display:block;width:0;background:linear-gradient(90deg,var(--dg-cyan),var(--dg-pink));box-shadow:0 0 10px var(--dg-cyan);transition:width .2s linear}.dg-controls{flex:none;padding:0 2px 12px}.dg-setup-controls{display:flex;flex-direction:column;gap:9px}.dg-track-note{font-weight:800;font-size:13px;letter-spacing:.08em;color:var(--dg-yellow);line-height:1.35}.dg-track-note small{display:block;color:#a9b6c8;font-size:11px;letter-spacing:0;font-weight:500}.dg-tempo-row{display:grid;grid-template-columns:1fr 1fr;gap:9px}.dg-secondary,.dg-end{min-height:48px;border-radius:14px;background:#172536;border:1px solid #465a70;color:white;font-size:14px;font-weight:800}.dg-primary{min-height:54px;border:0;border-radius:15px;background:var(--dg-yellow);color:#12151a;font:900 22px var(--dg-display);letter-spacing:.08em;box-shadow:0 5px 0 #bb9822;display:flex;justify-content:center;align-items:center;gap:14px}.dg-primary span{font:700 22px var(--dg-font)}.dg-tempo-readout{min-height:15px;text-align:center;color:#a8b5c7;font-size:11px;font-weight:600}.dg-preview{align-self:center;border:0;background:none;color:#c5d1df;min-height:34px;text-decoration:underline;text-underline-offset:3px;font-size:13px;font-weight:700}.dg-extra{color:#9aa8bc;font-size:12px}.dg-extra summary{cursor:pointer;text-align:center;padding:6px}.dg-import{display:block;text-align:center;padding:10px;color:var(--dg-cyan)}.dg-import input{display:block;width:100%;margin-top:8px;color:#cad5e5}.dg-playing-controls{display:grid;grid-template-columns:1.25fr 1fr;gap:10px}.dg-playing-controls[hidden],.dg-setup-controls[hidden]{display:none}.dg-end{color:#ffb7dc;border-color:#8d4770;background:#311b2d}.dg-overlay{position:absolute;z-index:5;inset:0;display:grid;place-items:center;padding:20px;background:#050a12b8;backdrop-filter:blur(7px)}.dg-overlay[hidden]{display:none}.dg-overlay-card{width:min(100%,370px);padding:25px 22px;border:1px solid #536578;border-radius:22px;background:#101a27;box-shadow:0 20px 60px #000b;text-align:center}.dg-overlay-kicker,.dg-overline{color:var(--dg-cyan);font-size:11px;letter-spacing:.18em;font-weight:900}.dg-overlay-card h1{font:900 clamp(38px,11vw,56px)/.95 var(--dg-display);margin:11px 0;color:var(--dg-yellow)}.dg-overlay-card p{font-size:15px;line-height:1.45;color:#c5d0df;margin:0 0 18px}.dg-overlay-actions .dg-primary{width:100%}.dg-library{position:fixed;z-index:20;inset:0;overflow:auto;background:#090f19;padding:calc(env(safe-area-inset-top) + 18px) 18px calc(env(safe-area-inset-bottom) + 20px);box-shadow:0 0 0 100vmax #000b}.dg-library[hidden]{display:none}.dg-library-head{display:flex;justify-content:space-between;gap:16px;align-items:flex-start}.dg-library-head h2{font:900 37px/.95 var(--dg-display);margin:8px 0}.dg-library-head p{font-size:13px;color:#aeb9c9;margin:0 0 15px}.dg-library-head>button{width:46px;height:46px;border-radius:50%;font-size:30px;background:#172536;color:white;border:1px solid #536476}.dg-search{height:50px;border:1px solid #405168;border-radius:13px;display:flex;align-items:center;gap:9px;padding:0 14px;color:var(--dg-cyan);font-size:24px}.dg-search input{width:100%;background:none;border:0;outline:0;color:white;font-size:16px}.dg-move-list{margin-top:11px;display:flex;flex-direction:column}.dg-library-item{display:flex;align-items:center;justify-content:space-between;text-align:left;padding:12px 3px;border:0;border-bottom:1px solid #273346;background:transparent;color:white;min-height:58px}.dg-library-item b{display:block;font-size:15px}.dg-library-item small{display:block;color:#9eacbe;font-size:11px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:78vw;margin-top:3px}.dg-library-item i{font-size:22px;color:var(--dg-yellow);font-style:normal}.dg-empty{color:#aab6c7}.dg-library[hidden]{display:none}\r\n@media(min-height:900px){.dg-stage{min-height:420px;max-height:61svh}.dg-now{min-height:260px;padding-top:18px}.dg-move-copy h2{font-size:52px}}\r\n@media(max-height:740px){.disco-game{gap:6px}.dg-header{height:44px}.dg-stage{min-height:300px;max-height:53svh}.dg-now{min-height:205px;padding-top:11px}.dg-move-copy h2{font-size:36px}.dg-controls{padding-bottom:6px}.dg-setup-controls{gap:6px}.dg-preview{min-height:28px}}\r\n@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}\r\n/* In-play move card carries the primary cue across a full phone viewport. */\r\n.disco-game[data-phase=\"dancing\"] .dg-now,.disco-game[data-phase=\"count_in\"] .dg-now,.disco-game[data-phase=\"break\"] .dg-now{min-height:34svh}\r\n.dg-move-main{min-height:140px}\r\n.dg-move-copy p{font-size:18px}\r\n.dg-move-glyph{width:clamp(128px,35vw,176px);height:145px}\r\n.dg-stage[data-coach-ready=\"true\"] .dg-coach{display:none}\r\n.dg-status-setup{color:var(--dg-pink);font-size:13px;font-weight:700;text-align:center;min-height:0}\r\n.dg-status-setup:empty{display:none}\r\n.dg-score-result{color:var(--dg-yellow);font-size:17px;font-weight:900;letter-spacing:.08em;margin-top:5px}\r\n.dg-tap-tempo{width:100%}\r\n.dg-primary:disabled{opacity:.42;filter:saturate(.55);box-shadow:none}\r\n@media(max-height:740px){.disco-game[data-phase=\"dancing\"] .dg-now,.disco-game[data-phase=\"count_in\"] .dg-now,.disco-game[data-phase=\"break\"] .dg-now{min-height:34svh}.dg-move-glyph{height:120px}.dg-move-main{min-height:116px}}\r\n/* Upcoming phrases approach the NOW marker with the live phrase clock. */\r\n.dg-upnext{align-items:center;gap:9px;margin-top:5px;min-height:56px}\r\n.dg-upnext-label{font-size:10px;line-height:1;letter-spacing:.12em}\r\n.dg-lane{position:relative;flex:1;min-width:0;height:56px;overflow:hidden}\r\n.dg-lane-line{position:absolute;z-index:2;left:0;top:5px;bottom:5px;width:3px;border-radius:3px;background:var(--dg-yellow);box-shadow:0 0 8px #ffe54f66}\r\n.dg-upnext-list{position:absolute;left:0;top:0;display:flex;gap:8px;overflow:visible;scrollbar-width:none;will-change:transform;transition:none}\r\n.dg-next-card{flex:0 0 120px;width:120px;height:56px;display:flex;align-items:center;gap:7px;padding:3px 6px;border:1px solid #344357;border-radius:11px;background:#111e2b;color:#cad5e2;font-size:10px;font-weight:750;line-height:1.12}\r\n.dg-next-card:first-child{border-color:#5bf5ff65;color:#f0fcff}\r\n.dg-next-card span{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;max-width:64px}\r\n.dg-next-glyph{display:block;flex:none;width:48px;height:48px}\r\n.dg-next-empty{position:absolute;left:128px;top:18px;color:#778498;font-size:11px}\r\n.dg-lane-line{animation:none}\r\n.dg-stage:before,.dg-stage:after{content:\"\";position:absolute;inset:-30% -45%;pointer-events:none;transform-origin:50% 0;background:linear-gradient(22deg,transparent 38%,#55e9ff12 44%,#55e9ff26 49%,transparent 58%);animation:dg-beam 7s ease-in-out infinite alternate}\r\n.dg-stage:after{background:linear-gradient(-22deg,transparent 38%,#ff56c815 44%,#ff56c826 49%,transparent 58%);animation-direction:alternate-reverse;animation-duration:9s}\r\n@keyframes dg-beam{from{transform:rotate(-13deg);opacity:.45}to{transform:rotate(13deg);opacity:.85}}\r\n.disco-game[data-phase=\"break\"] .dg-overlay{align-items:start;padding-top:48px;background:linear-gradient(#050a12aa,transparent 80%);backdrop-filter:none;pointer-events:none}\r\n.disco-game[data-phase=\"break\"] .dg-overlay-card{padding:12px 16px;background:#0b1420c9}\r\n.disco-game[data-phase=\"break\"] .dg-overlay-card h1{font-size:34px;margin:6px 0}\r\n.disco-game[data-phase=\"break\"] .dg-overlay-card p{font-size:14px;margin-bottom:0}\r\n/* Keep setup within reach; the full movement cue opens when dancing begins. */\r\n.disco-game:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"],[data-phase=\"lost\"],[data-phase=\"results\"]) .dg-now{display:none}\r\n.disco-game:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"],[data-phase=\"lost\"],[data-phase=\"results\"]) .dg-stage{min-height:250px;height:34svh;max-height:34svh;flex:none}\r\n.dg-back{background:none;border:0;cursor:pointer}.dg-source-row{display:grid;grid-template-columns:1.3fr 1fr;gap:8px}.dg-source-row label{font-size:11px;color:var(--dg-cyan);font-weight:800}.dg-source-row select{display:block;width:100%;min-height:44px;margin-top:3px;border:1px solid #465a70;border-radius:10px;background:#172536;color:#fff;font:700 12px var(--dg-font);padding:7px}\r\n.dg-star-board{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:44px;flex:none;padding:2px 8px}.dg-star-board b{display:block;color:var(--dg-yellow);font-size:22px;letter-spacing:.1em}.dg-star-board span{display:block;font-size:10px;color:#c7d4e5}.dg-star-board strong{font-size:12px;color:var(--dg-cyan);max-width:110px;text-align:right}.dg-help,.dg-goals{color:#b1bfd1;font-size:11px;line-height:1.4}.dg-goals summary{min-height:34px;cursor:pointer;padding-top:7px}.dg-goals p{margin:6px 0}.dg-live-outline{position:absolute;inset:8% 6% 3%;width:88%;height:89%;z-index:2;pointer-events:none;opacity:.7}\r\n.dg-playing-controls{grid-template-columns:1fr 1fr 1fr;gap:7px}.dg-playing-controls button{font-size:12px}.disco-game:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"]) .dg-stage{min-height:260px;height:calc(66svh - 215px);max-height:calc(66svh - 215px);flex:none}\r\n.disco-game:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"]) .dg-help,.disco-game:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"]) .dg-goals{display:none}\r\n.dg-dance-floor{position:absolute;left:-25%;right:-25%;bottom:-9%;height:33%;transform:perspective(220px) rotateX(52deg);transform-origin:center bottom;background:repeating-linear-gradient(0deg,transparent 0 29px,#5bf5ff42 30px 31px),repeating-linear-gradient(90deg,#0a1d2b88 0 29px,#ff63be55 30px 31px);border-top:2px solid #5bf5ff77;box-shadow:0 -8px 38px #5bf5ff16}\r\n.dg-live-outline{filter:drop-shadow(0 0 5px #5bf5ffaa)}.dg-hit-zone{display:grid;place-items:center;width:46px;height:46px;flex:none;border:2px solid var(--hit-color,#5bf5ff);border-radius:10px;font-size:9px;font-weight:900;color:var(--hit-color,#5bf5ff);background:#071420;box-shadow:0 0 12px color-mix(in srgb,var(--hit-color,#5bf5ff) 35%,transparent)}\r\n.disco-game[data-grade=\"miss\"]{--hit-color:#ff5d77}.disco-game[data-grade=\"good\"]{--hit-color:#64ff9a}.disco-game[data-grade=\"great\"]{--hit-color:#ffe054}.dg-lane-line{background:var(--hit-color,#ffe54f)}.disco-game:not([data-grade=\"neutral\"]) .dg-now{border-color:var(--hit-color)}\r\n.disco-game[data-grade=\"great\"] .dg-star-board strong{color:#ffe054}.disco-game[data-grade=\"good\"] .dg-star-board strong{color:#64ff9a}.disco-game[data-grade=\"miss\"] .dg-star-board strong{color:#ff5d77}\r\n/* Purple/blue is the party's resting palette. Beat grades tint the lights and\r\n   the phone edge briefly; the whole image never flashes to a solid color. */\r\n.disco-game{--party-light:#9670ff;--party-rim:#7455bd;background:radial-gradient(ellipse at 18% 15%,#35205d 0,transparent 52%),radial-gradient(ellipse at 85% 40%,#122e64 0,#0b1022 70%)}\r\n.disco-game:after{content:\"\";position:fixed;z-index:500;pointer-events:none;inset:0;border:4px solid var(--party-rim);border-radius:18px;box-shadow:inset 0 0 24px color-mix(in srgb,var(--party-rim) 45%,transparent);transition:border-color .18s ease,box-shadow .3s ease}\r\n.dg-stage{background:linear-gradient(155deg,#261b48,#0c1936 68%)}.dg-stage:before{background:linear-gradient(22deg,transparent 38%,color-mix(in srgb,var(--party-light) 12%,transparent) 44%,color-mix(in srgb,var(--party-light) 32%,transparent) 49%,transparent 58%);transition:background .2s ease}.dg-stage:after{background:linear-gradient(-22deg,transparent 38%,#4548d922 44%,color-mix(in srgb,var(--party-light) 29%,#2464df22) 49%,transparent 58%)}.dg-stage-glow{background:radial-gradient(ellipse,color-mix(in srgb,var(--party-light) 35%,transparent),transparent 68%);transition:background .2s ease}\r\n.disco-game[data-party=\"miss\"]{--party-light:#ff5777;--party-rim:#ff5777}.disco-game[data-party=\"good\"]{--party-light:#50f68c;--party-rim:#50f68c}.disco-game[data-party=\"great\"]{--party-light:#ffd44d;--party-rim:#ffd44d}\r\nhtml[data-embedded=\"true\"] .disco-game:after{display:none}\r\nhtml[data-embedded=\"true\"] .disco-game:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"]) .dg-stage{height:calc(66svh - 230px);max-height:calc(66svh - 230px)}\r\n/* Original static venue landmarks sit behind the unchanged coach and cue layers. */\r\n.dg-venue-scene{position:absolute;z-index:0;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none;opacity:.8}\r\n.dg-move-copy h2{font-size:clamp(23px,6.2vw,34px);line-height:1.02}\r\n.dg-move-copy p{font-size:13px;line-height:1.2;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}\r\n.dg-stage{--venue-tone:#8062ce;border-color:color-mix(in srgb,var(--venue-tone) 52%,#233b4c);background:linear-gradient(155deg,color-mix(in srgb,var(--venue-tone) 28%,#261b48),#0c1936 68%)}\r\n.disco-game[data-genre=\"pop-disco\"] .dg-stage{--venue-tone:#dd9b63}\r\n.disco-game[data-genre=\"jazz\"] .dg-stage{--venue-tone:#c9a36a}\r\n.disco-game[data-genre=\"funk\"] .dg-stage{--venue-tone:#bd9b68}\r\n.disco-game[data-genre=\"latin\"] .dg-stage{--venue-tone:#ed796e}\r\n.disco-game[data-genre=\"hip-hop\"] .dg-stage{--venue-tone:#ad725c}\r\n.disco-game[data-genre=\"electronic\"] .dg-stage{--venue-tone:#59cbd6}\r\n.disco-game[data-genre=\"rock\"] .dg-stage{--venue-tone:#9a92ab}\r\n.disco-game[data-genre=\"chill\"] .dg-stage{--venue-tone:#a2bbd1}\r\n/* DDR-inspired timing edge, applied to the existing beat lane and grade badge. */\r\n.dg-upnext-label{display:flex;align-items:center;gap:5px}\r\n.dg-upnext-label:before{content:\"\";width:7px;height:7px;flex:none;transform:rotate(45deg);border:1px solid var(--hit-color,#ffe054);box-shadow:0 0 5px color-mix(in srgb,var(--hit-color,#ffe054) 45%,transparent)}\r\n.dg-lane-line:before,.dg-lane-line:after{content:\"\";position:absolute;left:-4px;width:11px;height:3px;border-radius:1px;background:var(--hit-color,#ffe054);box-shadow:0 0 6px color-mix(in srgb,var(--hit-color,#ffe054) 55%,transparent)}\r\n.dg-lane-line:before{top:0}.dg-lane-line:after{bottom:0}\r\n.dg-hit-zone{border-radius:5px;background:linear-gradient(145deg,#18243b,#081320);box-shadow:inset 0 0 0 2px #ffffff0b,0 0 12px color-mix(in srgb,var(--hit-color,#5bf5ff) 35%,transparent)}\r\n\r\n/* Pose-led play controls: keep engine copy in the accessibility tree while the\r\n   existing animated pose glyph carries the move at a glance. */\r\n.game-sr,.dg-move-copy h2,.dg-move-copy p,.dg-now-label,.dg-move-meta [data-bind=\"phase\"],.dg-move-meta [data-bind=\"handcue\"],.dg-upnext-label,.dg-next-card span,.dg-hit-copy,.dg-help{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\r\n.dg-now-head{min-height:24px;justify-content:flex-end}.dg-move-main{position:relative;justify-content:center;min-height:138px}.dg-move-copy{position:absolute;inset:0;width:1px;height:1px;overflow:hidden}.dg-move-glyph{width:clamp(144px,44vw,188px);height:138px;filter:drop-shadow(0 0 13px #5bf5ff55)}.dg-move-meta{height:0;min-height:0;position:absolute;overflow:hidden}\r\n.dg-upnext{position:relative;min-height:58px;margin-top:4px;padding-left:0}.dg-upnext-list{left:0;top:0;gap:7px}.dg-next-card{flex-basis:58px;width:58px;height:56px;justify-content:center;padding:2px;background:linear-gradient(145deg,#172638,#0d1724);border-radius:13px}.dg-next-glyph{width:48px;height:48px}.dg-next-empty{left:0;top:20px}\r\n.dg-hit-zone{width:48px;height:48px;border-radius:50%;border-width:2px}.dg-hit-icon .game-symbol{width:30px;height:30px;stroke-width:4}.dg-star-board strong{display:flex;align-items:center;justify-content:flex-end;gap:6px;min-height:38px}.dg-judgment-icon .game-symbol{width:28px;height:28px;stroke-width:4}.dg-star-board strong .dg-judgment-copy{font-size:10px;line-height:1.1}.disco-game[data-preview=\"true\"] .dg-star-board strong{color:#c7d4e5;border:1px solid #64768b;border-radius:999px;padding:5px 8px;background:#121d2b}.disco-game[data-preview=\"true\"] .dg-judgment-icon .game-symbol{width:20px;height:20px}\r\n.dg-header .dg-back,.dg-library-toggle{display:flex;align-items:center;justify-content:center;gap:5px;min-width:44px;min-height:44px;border:1px solid #536476;border-radius:999px;background:#14202d;color:#e5ecf5;padding:7px}.dg-header .dg-back{color:#d0d6e5}.dg-header .game-symbol{width:25px;height:25px;stroke-width:3}.dg-library-toggle b{font:900 13px var(--dg-display);color:var(--dg-yellow)}.dg-icon-button{display:flex;align-items:center;justify-content:center;gap:9px;min-width:48px;min-height:48px;cursor:pointer;touch-action:manipulation}.dg-icon-button .game-symbol,.dg-action-icon{width:34px;height:34px;flex:none;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}.dg-action-icon .dg-icon-lens{fill:currentColor;stroke:none}.dg-action-icon .dg-icon-ripple{opacity:.62}.dg-icon-button:focus-visible,.dg-header button:focus-visible{outline:3px solid #fff;outline-offset:3px}.dg-icon-button:disabled{cursor:default}.dg-secondary.is-ready{border-color:#64ff9a;color:#a8ffbf}.dg-tap-tempo{position:relative;min-height:54px}.dg-tap-count{position:absolute;right:calc(50% - 47px);top:5px;display:grid;place-items:center;width:22px;height:22px;border:1px solid currentColor;border-radius:50%;font-size:10px;font-weight:900}.dg-start{width:100%;min-height:52px;color:#12151a}.dg-start .game-symbol{stroke-width:4}.dg-preview{align-self:center;min-height:38px;text-decoration:none;border:1px solid #465a70;border-radius:999px;padding:5px 13px;color:#c5d1df}.dg-preview .game-symbol{width:23px;height:23px}.dg-playing-controls .dg-icon-button{min-height:52px;border-radius:15px}.dg-playing-controls .dg-icon-button .game-symbol{width:32px;height:32px}.dg-playing-controls .dg-end .game-symbol{width:28px;height:28px}.dg-overlay-kicker{display:flex;align-items:center;justify-content:center;gap:8px}.dg-overlay-kicker .game-symbol{width:32px;height:32px}.dg-overlay-card h1{margin:8px 0 12px}.dg-overlay-card p{max-width:35ch;margin-left:auto;margin-right:auto}.disco-game[data-phase=\"break\"] .dg-now{border-color:#f3cb67;box-shadow:0 12px 30px #0005,0 0 0 1px #f3cb6755}.disco-game[data-phase=\"break\"] .dg-now:before{background:repeating-linear-gradient(135deg,#f3cb67 0 7px,#4c412e 7px 14px)}.disco-game[data-phase=\"lost\"] .dg-overlay-card p,.disco-game[data-phase=\"waiting\"] .dg-overlay-card p{display:block}.disco-game[data-phase=\"prepare\"] .dg-overlay-card p{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important}.disco-game[data-phase=\"break\"] .dg-overlay-card{border-color:#e9c46a}.disco-game[data-phase=\"break\"] .dg-overlay-kicker{color:#f3cb67}\r\n@media(max-width:390px){.dg-move-glyph{width:clamp(140px,47vw,176px)}.dg-playing-controls{gap:7px}.dg-playing-controls .dg-icon-button{min-height:48px}}\r\n@media(prefers-reduced-motion:reduce){.dg-icon-button{animation:none!important}}\r\n\r\n.dg-secondary.is-ready{position:relative}.dg-secondary.is-ready:after{content:\"\\2713\";position:absolute;top:4px;right:9px;display:grid;place-items:center;width:17px;height:17px;border:1px solid currentColor;border-radius:50%;font-size:11px;line-height:1;font-weight:900}\r\n\r\n.dg-cam-tag:empty{display:none}\r\n/* Give the reclaimed third of the cue panel to the existing 3D coach. */\r\n.disco-game{--dg-old-cue-height:max(258px,34svh);--dg-cue-height:calc(var(--dg-old-cue-height) * 2 / 3);--dg-cue-reclaimed:calc(var(--dg-old-cue-height) / 3);--dg-old-stage-height:max(260px,calc(66svh - 215px));--dg-chrome-height:192px;--dg-safe:calc(env(safe-area-inset-top) + env(safe-area-inset-bottom))}\r\nhtml[data-embedded=\"true\"] .disco-game{--dg-old-stage-height:max(260px,calc(66svh - 230px))}\r\nhtml .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-stage{min-height:0;height:min(calc(var(--dg-old-stage-height) + var(--dg-cue-reclaimed)),calc(100svh - var(--dg-cue-height) - var(--dg-chrome-height) - var(--dg-safe)));max-height:none}\r\nhtml .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-now{height:var(--dg-cue-height);min-height:0;padding:8px 12px;display:grid;grid-template-rows:18px minmax(70px,1fr) 38px}\r\n.dg-now-head{min-height:18px}.dg-move-count{font-size:10px}.dg-move-main{min-height:0}.dg-move-glyph{width:clamp(112px,35vw,152px);height:100%;max-height:112px}.dg-upnext{min-height:38px;height:38px;margin:0}.dg-lane{height:38px}.dg-next-card{width:44px;flex-basis:44px;height:36px;padding:0}.dg-next-glyph{width:34px;height:34px}.dg-upnext-list{gap:6px}.dg-hit-zone{width:38px;height:38px}.dg-hit-icon .game-symbol{width:25px;height:25px}\r\n.dg-coach{display:none}.dg-coach-depth{transform:scale(1.22);transform-origin:center;filter:none}.dg-coach-wrap{inset:4% 3% 3%}.dg-live-outline[hidden]{display:none}.dg-live-outline{opacity:.6}.dg-coach-depth .myr5-companion-stage canvas{display:block;width:100%;height:100%}\r\n.dg-stage-label [data-bind=\"coach-label\"]{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important}.dg-live-dot{opacity:.35;box-shadow:none}.disco-game[data-coach-state=\"fresh\"] .dg-live-dot{opacity:1;box-shadow:0 0 9px var(--dg-cyan)}\r\n.dg-streak,.dg-minutes{display:flex;align-items:center;gap:5px}.dg-streak .game-symbol{width:20px;height:20px}.dg-minutes .game-symbol{width:17px;height:17px}.dg-streak b,.dg-minutes b{font:inherit;font-variant-numeric:tabular-nums}.dg-cam-placeholder .dg-action-icon{width:27px;height:27px;fill:none;stroke:currentColor;stroke-width:3}.dg-stage-foot [data-bind=\"tracking\"]{max-width:65%;font-size:11px}.dg-stage-foot [data-bind=\"tracking\"]:empty{display:none}.dg-minutes{margin-left:auto}\r\n.disco-game:not([data-preview=\"true\"]):not([data-phase=\"break\"]) .dg-judgment-copy{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important}\r\nhtml .disco-game[data-phase]:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"]) .dg-overlay{align-items:end;padding:16px;pointer-events:none;background:transparent;backdrop-filter:none}html .disco-game[data-phase]:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"]) .dg-overlay-card{width:auto;max-width:100%;padding:9px 14px}html .disco-game[data-phase]:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"]) .dg-overlay-card h1,html .disco-game[data-phase]:is([data-phase=\"waiting\"],[data-phase=\"prepare\"],[data-phase=\"ready\"],[data-phase=\"break\"]) .dg-overlay-card p,.dg-overlay-kicker span{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\r\n.disco-game[data-phase=\"break\"] .dg-overlay{padding-top:16px}.disco-game[data-phase=\"break\"] .dg-overlay-card{width:auto;padding:8px 16px}.disco-game[data-phase=\"break\"] .dg-overlay-card h1{font-size:18px}\r\n@media(max-height:740px){.disco-game{--dg-old-cue-height:max(254px,34svh);--dg-chrome-height:168px}.dg-move-glyph{max-height:100px}}\r\n@media(max-height:500px) and (min-width:600px){html .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]){width:min(100%,1000px);height:100svh;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) minmax(180px,.6fr);grid-template-rows:44px minmax(0,1fr) 56px;gap:8px;padding-bottom:env(safe-area-inset-bottom)}.dg-header{grid-column:1/-1;height:44px}html .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-stage{grid-row:2;grid-column:1;height:100%;min-height:0}html .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-now{grid-row:2;grid-column:2;height:100%;min-height:0}.dg-star-board{grid-row:3;grid-column:1;min-height:44px}.dg-controls{grid-row:3;grid-column:2}.dg-playing-controls .dg-icon-button{min-height:44px}.dg-cam-frame{width:72px;top:38px}.dg-coach-wrap{inset:3%}.dg-upnext-list{transform:none!important}}\r\n.disco-game[data-phase=\"results\"] .dg-overlay{padding:16px;align-items:center}\r\n@media(max-height:500px) and (min-width:600px){.disco-game[data-phase=\"paused\"] .dg-overlay{padding:8px;align-items:center}.disco-game[data-phase=\"paused\"] .dg-overlay-card{max-height:100%;overflow:auto;padding:8px 12px}.disco-game[data-phase=\"paused\"] .dg-overlay-card h1{font-size:22px;margin:5px 0}.disco-game[data-phase=\"paused\"] .dg-overlay-card p{font-size:12px;margin:0 0 8px}.disco-game[data-phase=\"paused\"] .dg-overlay-card button{min-height:44px}}\r\n.disco-game[data-phase=\"results\"] .dg-overlay-card{padding:14px}\r\n@media(max-height:740px){.dg-coach-depth{transform:translateY(-32px) scale(1.08)}}\r\n.disco-game[data-phase=\"results\"] .dg-overlay-card h1{font-size:30px;line-height:1.2;margin:8px 0 14px}\r\n.disco-game[data-phase=\"results\"] .dg-overlay-card h1 small{display:flex;align-items:center;justify-content:center;gap:8px;font:800 18px/1.4 var(--dg-font);color:var(--dg-white);margin-top:10px}\r\n.disco-game[data-phase=\"results\"] .dg-overlay-card h1 small .game-symbol{width:24px;height:24px}\r\n.disco-game[data-phase=\"results\"] .dg-overlay-card p,.disco-game[data-phase=\"results\"] .dg-overlay-kicker span{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\r\n\r\n/* Alien club equipment surrounds the existing saved, user-driven coach. */\r\n.dg-stage{border:3px solid #59647c;box-shadow:inset 0 0 0 2px #070914,inset 0 10px 18px #a9b2ff16,0 7px 0 #040711,0 12px 26px #0009}\r\n.dg-stage:before,.dg-stage:after{animation:none;opacity:.35;z-index:0}\r\n.dg-stage-grid{opacity:.12}\r\n.dg-alien-club{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;--club-beat:.3}\r\n.dg-club-haze{position:absolute;inset:12% -10% 9%;background:radial-gradient(ellipse at 18% 65%,#4dc6b329,transparent 50%),radial-gradient(ellipse at 83% 20%,#a96fff3a,transparent 60%),linear-gradient(transparent 50%,#09152e 94%);opacity:.9}\r\n.dg-club-truss{position:absolute;left:9%;right:9%;top:7%;height:10px;background:repeating-linear-gradient(125deg,#171827 0 8px,#566079 9px 11px,#151726 12px 20px);border-block:2px solid #7784a3;transform:perspective(400px) rotateX(-10deg);box-shadow:0 6px 10px #0008;display:flex;justify-content:space-around}\r\n.dg-club-truss i{display:block;width:21px;height:18px;margin-top:6px;border:4px solid #2c3048;border-radius:3px 3px 8px 8px;background:#b6adff;box-shadow:0 0 13px #9e7fff99}\r\n.dg-club-truss i:nth-child(2){background:#9ffff5;box-shadow:0 0 13px #5bf5ff99}\r\n.dg-club-lasers{position:absolute;inset:0;overflow:hidden;opacity:.55}\r\n.dg-club-lasers i{position:absolute;top:9%;left:calc(17% + var(--beam)*13%);width:2px;height:90%;transform-origin:top;background:linear-gradient(#bbffe7,#66ffc7aa,transparent);box-shadow:0 0 6px #67ffd7;opacity:.55}\r\n.dg-club-lasers i:nth-child(even){background:linear-gradient(#efb8ff,#ce7fff88,transparent);box-shadow:0 0 7px #a177ff}\r\n.dg-alien-crowd{position:absolute;inset:20% 0 14%;mask-image:linear-gradient(transparent,#000 15%,#000 82%,transparent)}\r\n.dg-alien-crowd svg{position:absolute;bottom:0;width:15%;overflow:visible;transform-origin:50% 100%;filter:drop-shadow(0 0 2px #9886bb55)}\r\n.dg-alien-crowd svg:nth-child(3n){bottom:13%;opacity:.5;width:12%}\r\n.dg-alien-crowd svg:nth-child(3n + 1){bottom:7%;opacity:.75}\r\n.dg-alien-body{fill:#101523;stroke:#686186;stroke-width:.7}.dg-alien-head{fill:#191b2d;stroke:#74688f;stroke-width:.8}.dg-alien-eyes{fill:#91b8af;opacity:.45}\r\n.dg-club-glints{position:absolute;inset:0}.dg-club-glints i{position:absolute;width:3px;height:3px;background:#ccffef;box-shadow:0 0 5px #b8c4ff;transform:rotate(45deg)}\r\n.dg-coach-contact{position:absolute;left:29%;width:42%;height:5%;bottom:11%;border-radius:50%;background:radial-gradient(ellipse,#02040ce8 5%,#050b19aa 38%,transparent 72%);filter:blur(2px)}\r\n.dg-dance-floor{z-index:0;left:-14%;right:-14%;bottom:4%;height:23%;transform:perspective(300px) rotateX(48deg);border-top:2px solid #b6d5f36b;border-bottom:9px solid #101829;background:repeating-linear-gradient(0deg,transparent 0 27px,#a9d9e543 28px 29px),repeating-linear-gradient(90deg,#122538 0 41px,#8d81c975 42px 44px);box-shadow:0 10px 0 #050910,0 13px 20px #000b,inset 0 0 38px #7c67ce44}\r\n.dg-coach-wrap{z-index:1}.dg-live-outline{z-index:1}.dg-stage-foot{z-index:3}\r\n.dg-now{isolation:isolate;border:3px solid #5f607a!important;border-radius:17px;background:linear-gradient(135deg,#3b3c52 0,#191d31 5%,#0c1122 45%,#282a43 97%,#8a8396 100%);box-shadow:inset 0 0 0 2px #060913,inset 0 5px 9px #d7d9ff18,0 6px 0 #030610,0 8px 15px #0008}\r\n.dg-now:after{content:\"\";position:absolute;inset:5px;pointer-events:none;border:1px solid #6b73973b;border-radius:10px;z-index:4}\r\n.dg-now:before{width:3px;left:0;opacity:.6}\r\n.dg-now-head{gap:8px;justify-content:space-between;z-index:3;min-width:0}\r\n.dg-dance-name{margin:0;min-width:0;font:800 clamp(16px,4.8vw,22px)/1.05 var(--dg-display);letter-spacing:.035em;color:#f4efff;text-shadow:0 2px 2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\nhtml .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-now{grid-template-rows:24px minmax(64px,1fr) 38px;padding:7px 12px}\r\n.dg-move-main{min-height:104px}.dg-move-glyph{width:clamp(150px,53vw,235px);height:100%;max-height:none;filter:none}\r\n.dg-glyph,.dg-next-glyph{visibility:hidden}.dg-solid-demonstrators{position:absolute;inset:0;width:100%;height:100%;z-index:2;pointer-events:none}\r\n.dg-demo-fallback{display:none;font-size:11px;color:#b4c4d8;text-align:center;padding:20px 0}.dg-now[data-demonstrator=\"unavailable\"] .dg-demo-fallback{display:block}\r\n.dg-upnext{z-index:3;overflow:visible}.dg-lane{overflow:hidden}.dg-upnext-list{transform:none!important}.dg-next-card{border-radius:5px;border:1px solid #889aba66;background:linear-gradient(135deg,#34425699,#101b2a77);box-shadow:inset 0 0 0 2px #0003,0 2px 0 #020612;flex-basis:43px;width:43px}\r\n.dg-next-glyph{width:40px;height:35px}.dg-next-card span{display:none}.dg-hit-zone{border-radius:6px;border:2px solid var(--hit-color,#92e4d7);background:linear-gradient(145deg,#44516a,#111d31 65%);box-shadow:inset 0 2px 1px #fff3,0 3px 0 #050817}\r\n.dg-beat-rail{position:absolute;left:13px;right:64px;bottom:46px;height:5px;border:1px solid #5f7391;background:#050916;z-index:3;--arrival:0}\r\n.dg-beat-rail i{position:absolute;width:12px;height:7px;top:-2px;left:calc((1 - var(--arrival)) * (100% - 12px));background:#d4ff85;border:1px solid #f7ffcd;box-shadow:0 0 7px #bdff7199;border-radius:1px}\r\n.dg-beat-rail b{position:absolute;left:0;top:-4px;width:6px;height:11px;border:1px solid #b5ff9c;background:#142a27}.dg-beat-rail[data-hit=\"true\"] b{background:#caff8c;box-shadow:0 0 8px #caff8c77}\r\n.dg-icon-button,.dg-header button{border-radius:8px;border:2px solid #707791;box-shadow:inset 0 2px 1px #fff3,inset 0 -3px 1px #0006,0 4px 0 #030711;background:linear-gradient(145deg,#3b4560,#192239 65%)}\r\n.dg-icon-button:active,.dg-header button:active{transform:translateY(2px);box-shadow:inset 0 2px 4px #0008,0 2px 0 #030711}\r\n.dg-primary{background:linear-gradient(145deg,#f2ffb0,#b6d64e 65%);border-color:#d6ef89;color:#192316}.dg-end{background:linear-gradient(145deg,#67405e,#301b30);border-color:#a77995}\r\n.dg-playing-controls .dg-icon-button{border-radius:8px}.dg-cam-frame{border-radius:8px;border:3px solid #7496a7;box-shadow:inset 0 0 0 2px #030811,0 4px 0 #060914,0 8px 14px #0008}\r\n@media(max-height:740px){.dg-move-glyph{max-height:none}.dg-dance-name{font-size:17px}}\r\n@media(max-height:500px) and (min-width:600px){.dg-move-main{min-height:0}.dg-move-glyph{width:100%}.dg-dance-name{font-size:18px}}\r\n@media(prefers-reduced-motion:reduce){.dg-club-lasers{opacity:.3}.dg-club-glints{display:none}}\r\n\r\n.dg-upnext{z-index:1}.dg-hit-zone{position:relative;z-index:3}\r\nhtml .disco-game[data-phase]:is([data-phase=\"dancing\"],[data-phase=\"count_in\"],[data-phase=\"break\"],[data-phase=\"paused\"]) .dg-move-main{min-height:0}\r\n@media(max-height:740px) and (max-width:599px){.disco-game{--dg-chrome-height:170px}}\r\n/* Floor/contact follow the saved rig's already-projected actual sole anchors. */\r\n.dg-stage[data-grounded=\"true\"] .dg-dance-floor{top:calc(var(--dg-ground-y) - 28px);bottom:auto;height:60px;transform-origin:center;transform:perspective(300px) rotateX(48deg)}\r\n.dg-stage[data-grounded=\"true\"] .dg-coach-contact{left:calc(var(--dg-ground-x) - var(--dg-ground-width) / 2);top:calc(var(--dg-ground-y) - 6px);bottom:auto;width:var(--dg-ground-width);height:14px}\r\n.dg-stage[data-grounded=\"false\"] .dg-coach-contact{opacity:0}\r\n.dg-dance-floor,.dg-coach-contact{z-index:1}.dg-coach-wrap,.dg-live-outline{z-index:2}.dg-stage-label,.dg-cam-frame,.dg-stage-foot{z-index:4}\r\n","modules/dance/android-game/genres.mjs":"import {MOVES,chooseRoutine} from './moves.mjs';\r\n// Explicit player choice: these weights do not classify the playing music.\r\nexport const GENRES=Object.freeze([['auto','Auto / mixed'],['pop-disco','Pop / disco'],['jazz','Jazz'],['funk','Funk'],['latin','Latin'],['hip-hop','Hip-hop'],['electronic','Electronic'],['rock','Rock'],['chill','Chill']].map(([id,label])=>Object.freeze({id,label})));\r\nconst weights={\r\n 'pop-disco':{reach:5,celebrate:5,groove:4,pose:3},\r\n jazz:{groove:7,pose:7,roll:7},\r\n funk:{groove:8,pull:6,press:6},\r\n latin:{crossbody:6,wave:6,groove:2,celebrate:2},\r\n 'hip-hop':{press:8,pull:8,groove:8},\r\n electronic:{circle:8,roll:8,press:5},\r\n rock:{reach:8,pull:7,celebrate:7},\r\n chill:{wave:6,roll:3,circle:3,groove:2},\r\n};\r\nexport function chooseGenreRoutine({trackKey='default',bpm=104,intensity='easy',seed='',genre='auto'}={}){\r\n // Reuse the catalogue's validation and preserve its exact mixed-mode behavior.\r\n const mixed=chooseRoutine({trackKey,bpm,intensity,seed});\r\n if(genre==='auto')return mixed;\r\n if(!weights[genre])throw new RangeError('Unknown dance genre.');\r\n const pool=MOVES.filter(m=>(intensity!=='easy'||m.intensity==='easy')&&bpm>=m.tempoRangeBpm[0]&&bpm<=m.tempoRangeBpm[1]);\r\n let state=2166136261;for(const char of `${trackKey}|${bpm}|${intensity}|${seed}|${genre}`){state^=char.charCodeAt(0);state=Math.imul(state,16777619)>>>0;}\r\n const random=()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state/4294967296;};\r\n const plan=[];const used=new Set();\r\n for(let i=0;i<mixed.length;i++){\r\n  // A weighted draw without replacement keeps the genre accent while ensuring\r\n  // a twelve-move round cannot collapse to two heavily weighted references.\r\n  const eligible=pool.filter(m=>!used.has(m.id));\r\n  const total=eligible.reduce((n,m)=>n+(weights[genre][m.family]??1),0);\r\n  let draw=random()*total,move=eligible.at(-1);\r\n  for(const candidate of eligible){draw-=weights[genre][candidate.family]??1;if(draw<0){move=candidate;break;}}\r\n  plan.push(move.id);used.add(move.id);\r\n }\r\n return Object.freeze(plan);\r\n}\r\n","modules/dance/android-game/index.html":"<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"utf-8\">\r\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\r\n  <meta name=\"theme-color\" content=\"#070b13\">\r\n  <title>Disco Coach · MYR5</title>\r\n  <link rel=\"stylesheet\" href=\"./game.css\">\r\n</head>\r\n<body>\r\n<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\"}}</script>\r\n<script type=\"module\" src=\"./app.mjs\"></script>\r\n</body>\r\n</html>\r\n","modules/dance/android-game/live-avatar.mjs":"// Retarget only a fresh, real waist-up camera observation. Invalid/offline\r\n// input returns a neutral pose frame so the existing danceFrame API clears its\r\n// last applied pose instead of holding an old position or using move samples.\r\nexport const LIVE_AVATAR_PROFILE='dance-biped.v1';\r\nexport const LIVE_AVATAR_MAX_AGE_MS=250;\r\n\r\nconst NEUTRAL_FRAME=Object.freeze({landmarks:Object.freeze([]),mirrored:false,coachYaw:0,active:true});\r\nconst QUADRUPED_BODY_PREFIXES=['roster/18-quad-all--'];\r\nconst QUADRUPED_TERMS=/quadruped|four[ -]?legged|fourlegged|robotic[ _-]?dog/i;\r\nconst profileAllowed=new Set(['biped','humanoid','dance-biped.v1','waist-up-shoulder-width.v1','myr5-arms.v1']);\r\nconst own=(o,k)=>Object.prototype.hasOwnProperty.call(o,k);\r\n\r\nfunction idOf(value){return typeof value==='string'?value:typeof value?.id==='string'?value.id:'';}\r\nfunction textOf(value){return typeof value==='string'?value:'';}\r\n\r\n/**\r\n * Check the selected MYR5 rig metadata without changing the saved recipe.\r\n * Current recipe body IDs use roster/18-quad-all-- for the four-legged roster\r\n * group. Explicit roster group/body-rig metadata takes precedence and unknown\r\n * explicit rig profiles fail closed.\r\n */\r\nexport function isDanceBipedCompatible(metadata){\r\n if(!metadata||typeof metadata!=='object'||Array.isArray(metadata))return false;\r\n const recipe=metadata.recipe&&typeof metadata.recipe==='object'?metadata.recipe:metadata;\r\n const rigVersion=metadata.rigVersion??metadata.rig?.rigVersion;\r\n if(rigVersion!==1)return false;\r\n const labels=[metadata.bodyRig,metadata.bodyRigProfile,metadata.bodyRigType,metadata.rigType,metadata.rosterGroup,metadata.group,recipe.bodyRig,recipe.bodyRigProfile,recipe.bodyRigType,recipe.rosterGroup,recipe.group];\r\n for(const value of labels){const text=textOf(value);if(!text)continue;if(QUADRUPED_TERMS.test(text))return false;if(/rig|biped|humanoid/i.test(text)&&!profileAllowed.has(text.toLowerCase()))return false;}\r\n const bodyKeys=['body','headFrom','armsFrom','feetFrom'];\r\n const ids=bodyKeys.map(key=>idOf(recipe[key]));\r\n if(ids.some(id=>!id))return false;\r\n if(ids.some(id=>QUADRUPED_BODY_PREFIXES.some(prefix=>id.startsWith(prefix))||QUADRUPED_TERMS.test(id)))return false;\r\n const roster=metadata.roster??metadata.rosterManifest;\r\n if(Array.isArray(roster)){\r\n  const selected=new Set(ids);\r\n  for(const entry of roster){if(selected.has(idOf(entry))&&QUADRUPED_TERMS.test(`${entry.group||''} ${entry.label||''}`))return false;}\r\n }\r\n return true;\r\n}\r\n\r\nconst visiblePoint=(point,minVisibility)=>!!point&&Number.isFinite(point.x)&&Number.isFinite(point.y)&&Number.isFinite(point.z)&&Number.isFinite(point.visibility)&&point.visibility>=minVisibility&&point.visibility<=1&&(!own(point,'presence')||(Number.isFinite(point.presence)&&point.presence>=minVisibility&&point.presence<=1))&&point.x>=-.5&&point.x<=1.5&&point.y>=-.5&&point.y<=1.5&&Math.abs(point.z)<=3;\r\n\r\n/** Create a stateless camera-to-coach gate. Timestamps are MediaPipe's monotonic atMs. */\r\nexport function createLiveAvatarRetarget({maxAgeMs=LIVE_AVATAR_MAX_AGE_MS,minVisibility=.6,futureToleranceMs=100}={}){\r\n if(!Number.isFinite(maxAgeMs)||maxAgeMs<0||!Number.isFinite(minVisibility)||minVisibility<0||minVisibility>1||!Number.isFinite(futureToleranceMs)||futureToleranceMs<0)throw new TypeError('Invalid live-avatar freshness policy');\r\n let lastTimestamp=-Infinity,lastGeneration;\r\n const neutral=(reason)=>({ok:false,reason,profile:LIVE_AVATAR_PROFILE,frame:NEUTRAL_FRAME});\r\n function retarget({landmarks,timestampMs,nowMs,cameraReady,mirrored=false,coachYaw=0,cameraGeneration=0,coachMetadata}={}){\r\n  if(!cameraReady){lastTimestamp=-Infinity;lastGeneration=undefined;return neutral('camera_off');}\r\n  if(lastGeneration!==cameraGeneration){lastGeneration=cameraGeneration;lastTimestamp=-Infinity;}\r\n  if(!Number.isFinite(timestampMs)||!Number.isFinite(nowMs))return neutral('missing_timestamp');\r\n  const age=nowMs-timestampMs;\r\n  if(age < -futureToleranceMs)return neutral('future_frame');\r\n  if(age>maxAgeMs)return neutral('stale_frame');\r\n  if(timestampMs<=lastTimestamp)return neutral('duplicate_or_out_of_order_frame');\r\n  lastTimestamp=timestampMs;\r\n  if(!isDanceBipedCompatible(coachMetadata))return neutral('incompatible_coach_rig');\r\n  if(!Array.isArray(landmarks)||landmarks.length<17)return neutral('missing_pose');\r\n  const shoulders=[landmarks[11],landmarks[12]],left=[11,13,15],right=[12,14,16];\r\n  if(!shoulders.every(p=>visiblePoint(p,minVisibility)))return neutral('shoulders_not_visible');\r\n  const completeLeft=left.every(i=>visiblePoint(landmarks[i],minVisibility)),completeRight=right.every(i=>visiblePoint(landmarks[i],minVisibility));\r\n  if(!completeLeft&&!completeRight)return neutral('no_complete_arm');\r\n  const safe=Array(17).fill(null);\r\n  // Nose is optional and only used for a small head orientation cue.\r\n  if(visiblePoint(landmarks[0],minVisibility))safe[0]=copyPoint(landmarks[0]);\r\n  for(const ids of [left,right])if(ids.every(i=>visiblePoint(landmarks[i],minVisibility)))for(const index of ids)safe[index]=copyPoint(landmarks[index]);\r\n  return {ok:true,reason:null,profile:LIVE_AVATAR_PROFILE,frame:{landmarks:safe,mirrored:!!mirrored,coachYaw:Number.isFinite(coachYaw)?Math.max(-Math.PI*2,Math.min(Math.PI*2,coachYaw)):0,active:true}};\r\n }\r\n retarget.clear=()=>{lastTimestamp=-Infinity;lastGeneration=undefined;};\r\n retarget.neutralFrame=NEUTRAL_FRAME;\r\n return retarget;\r\n}\r\n\r\nfunction copyPoint(point){const out={x:point.x,y:point.y,z:point.z,visibility:point.visibility};if(Number.isFinite(point.presence))out.presence=point.presence;return out;}\r\n","modules/dance/android-game/moves.mjs":"/** Named social/party dances. Original MYR5 short upper-body teaching adaptations,\r\n * authored from reference poses; not captured motion or artist-endorsed routines.\r\n * Footwork, fingers and floor contact are demonstrations, never scoring inputs. */\r\nimport {extractUpperBodyFeatures, UPPER_BODY_PROFILE} from '../content/features.mjs';\r\nconst freeze=value=>{if(value&&typeof value==='object'&&!Object.isFrozen(value)){Object.freeze(value);Object.values(value).forEach(freeze);}return value;};\r\nconst point=(x,y,z=0)=>({x,y,z,visibility:1,presence:1});\r\nconst REST_LEFT=[-.62,.95],REST_RIGHT=[.62,.95];\r\nconst smooth=t=>t*t*(3-2*t);\r\nconst authored=[\r\n {\"id\":\"sprinkler\",\"title\":\"Sprinkler\",\"cue\":\"One hand behind your head; sweep the other arm like a sprinkler.\",\"family\":\"wave\",\"poses\":[[[-0.7,-0.55],[1.35,0.05]],[[-0.7,-0.55],[0.5,0.38]],[[-0.7,-0.55],[1.35,-0.15]],[[-0.7,-0.55],[0.7,0.3]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"shopping-cart\",\"title\":\"Shopping Cart\",\"cue\":\"Hold the cart with one hand; grab and drop groceries with the other.\",\"family\":\"pull\",\"poses\":[[[-0.72,0.48],[1.35,-0.12]],[[-0.72,0.48],[0.25,0.55]],[[-0.72,0.48],[1.12,-0.48]],[[-0.72,0.48],[0.4,0.5]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"whip\",\"title\":\"Whip\",\"cue\":\"Throw your fist forward like you are steering, then pull it back.\",\"family\":\"press\",\"poses\":[[[-0.65,0.65],[0.85,-0.2]],[[-0.65,0.65],[0.68,0.2,-0.5]],[[-0.7,0.65],[0.98,-0.3]],[[-0.65,0.65],[0.68,0.2,-0.5]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"nae-nae\",\"title\":\"Nae Nae\",\"cue\":\"Raise one palm and sway it side to side with your shoulders.\",\"family\":\"wave\",\"poses\":[[[-0.65,0.55],[0.72,-0.9]],[[-0.7,0.6],[1.1,-0.65]],[[-0.6,0.55],[0.65,-0.95]],[[-0.7,0.6],[1.15,-0.62]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"dougie\",\"title\":\"Dougie\",\"cue\":\"Brush beside your hair, roll your shoulders and switch hands.\",\"family\":\"groove\",\"poses\":[[[-0.7,-0.5],[0.75,0.55]],[[-0.25,-0.72],[0.7,0.65]],[[-0.72,0.55],[0.7,-0.5]],[[-0.7,0.65],[0.25,-0.72]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"superman\",\"title\":\"Superman\",\"cue\":\"Reach both fists forward into a flying Superman pose, then pull back.\",\"family\":\"reach\",\"poses\":[[[-0.7,0.35],[0.7,0.35]],[[-1.05,-0.5,-0.25],[1.05,-0.5,-0.25]],[[-0.65,0.45],[0.65,0.45]],[[-1.12,-0.45,-0.3],[1.12,-0.45,-0.3]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"hit-the-quan\",\"title\":\"Hit the Quan\",\"cue\":\"Soften your knees and swing both arms across low, then the other way.\",\"family\":\"groove\",\"poses\":[[[-1.25,0.5],[-0.1,0.7]],[[-0.45,0.3],[0.6,0.2]],[[0.1,0.7],[1.25,0.5]],[[-0.6,0.2],[0.45,0.3]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"jerk\",\"title\":\"Jerk\",\"cue\":\"Pump your bent arms in opposite directions; the backward steps are optional.\",\"family\":\"groove\",\"poses\":[[[-0.9,0.15],[0.6,0.7]],[[-0.6,0.7],[0.9,0.15]],[[-1,0.1],[0.5,0.65]],[[-0.5,0.65],[1,0.1]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"griddy\",\"title\":\"Griddy\",\"cue\":\"Swing your arms low, then bring your fingers up like goggles. Steps are optional.\",\"family\":\"groove\",\"poses\":[[[-0.9,0.6],[0.5,0.75]],[[-0.5,0.75],[0.9,0.6]],[[-0.25,-0.55],[0.25,-0.55]],[[-0.65,0.7],[0.65,0.7]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"dab\",\"title\":\"Dab\",\"cue\":\"Tuck your face toward one bent elbow while your other arm points up and out.\",\"family\":\"pose\",\"poses\":[[[-1.25,-0.42],[0.08,-0.38]],[[-0.65,0.6],[0.65,0.6]],[[-0.08,-0.38],[1.25,-0.42]],[[-0.65,0.6],[0.65,0.6]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"floss\",\"title\":\"Floss\",\"cue\":\"Swing both arms across your hips, alternating in front and behind.\",\"family\":\"groove\",\"poses\":[[[-1.15,0.6,0.2],[-0.15,0.65,-0.2]],[[0.15,0.65,-0.2],[1.15,0.6,0.2]],[[-1.15,0.6,-0.2],[-0.15,0.65,0.2]],[[0.15,0.65,0.2],[1.15,0.6,-0.2]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"milly-rock\",\"title\":\"Milly Rock\",\"cue\":\"Rock your shoulders and sweep one bent arm across, then switch.\",\"family\":\"groove\",\"poses\":[[[-0.25,0.12],[0.95,0.28]],[[-1.1,0.1],[0.2,0.25]],[[-0.95,0.28],[0.25,0.12]],[[-0.2,0.25],[1.1,0.1]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"woah\",\"title\":\"Woah\",\"cue\":\"Roll your hands inward and stop sharply with one fist high and one low.\",\"family\":\"pose\",\"poses\":[[[-0.7,0.2],[0.7,0.2]],[[-0.25,-0.15],[0.3,0.3]],[[-0.8,-0.45],[0.5,0.65]],[[-0.8,-0.45],[0.5,0.65]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"gangnam-style\",\"title\":\"Gangnam Style\",\"cue\":\"Cross your wrists like reins, then lift one lasso hand overhead.\",\"family\":\"celebrate\",\"poses\":[[[0.15,0.35],[-0.15,0.35]],[[-0.15,0.45],[0.15,0.45]],[[-0.25,0.35],[1.1,-0.72]],[[-0.25,0.35],[0.58,-0.95]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"macarena\",\"title\":\"Macarena\",\"cue\":\"Reach out, fold your hands toward your shoulders, then place them at your hips.\",\"family\":\"crossbody\",\"poses\":[[[-0.6,0.05,-0.4],[0.6,0.05,-0.4]],[[-0.2,-0.05],[0.2,-0.05]],[[-0.72,-0.5],[0.72,-0.5]],[[-0.65,0.6],[0.65,0.6]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"robot\",\"title\":\"Robot\",\"cue\":\"Lock your elbows into right angles and move one mechanical piece at a time.\",\"family\":\"press\",\"poses\":[[[-1.05,-0.4],[1.05,0.45]],[[-1.05,0.35],[1.05,0.45]],[[-1.05,0.35],[1.05,-0.4]],[[-0.7,-0.55],[0.7,0.55]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"cabbage-patch\",\"title\":\"Cabbage Patch\",\"cue\":\"Roll both fists in a circle in front of your chest.\",\"family\":\"circle\",\"poses\":[[[-0.2,0.15],[0.85,0.15]],[[-0.6,-0.3],[0.5,-0.3]],[[-0.95,0.15],[0.1,0.15]],[[-0.55,0.6],[0.5,0.6]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"hand-jive\",\"title\":\"Hand Jive\",\"cue\":\"Pat down, cross your hands, then roll your fists.\",\"family\":\"roll\",\"poses\":[[[-0.6,0.68],[0.6,0.68]],[[0.2,0.2],[-0.2,0.2]],[[-0.15,0.1],[0.35,0.4]],[[-0.35,0.4],[0.15,0.1]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"disco-point\",\"title\":\"Disco Point\",\"cue\":\"Point one hand diagonally to the sky, then across toward the floor.\",\"family\":\"reach\",\"poses\":[[[-0.65,0.55],[1.08,-0.72]],[[-0.65,0.55],[-0.18,0.65]],[[-0.65,0.55],[1.08,-0.72]],[[-0.65,0.55],[-0.18,0.65]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"raise-the-roof\",\"title\":\"Raise the Roof\",\"cue\":\"Push both open palms upward twice, then bring them down.\",\"family\":\"celebrate\",\"poses\":[[[-0.65,-0.45],[0.65,-0.45]],[[-0.75,-0.95],[0.75,-0.95]],[[-0.65,-0.4],[0.65,-0.4]],[[-0.85,-0.88],[0.85,-0.88]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"chicken-dance\",\"title\":\"Chicken Dance\",\"cue\":\"Make little beaks, flap your elbows, then clap.\",\"family\":\"groove\",\"poses\":[[[-0.1,-0.05],[0.1,-0.05]],[[-0.6,-0.15],[0.6,-0.15]],[[-0.8,0.3],[0.8,0.3]],[[-0.05,0.2],[0.05,0.2]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"shoot\",\"title\":\"Shoot\",\"cue\":\"Pump one arm downward while the other balances your groove. Steps are optional.\",\"family\":\"press\",\"poses\":[[[-0.75,0.35],[0.85,-0.35]],[[-0.75,0.35],[0.55,0.75]],[[-0.65,0.4],[1,-0.28]],[[-0.65,0.4],[0.55,0.75]]],\"upperBodyAdaptation\":true},\r\n {\"id\":\"harlem-shake\",\"title\":\"Harlem Shake\",\"cue\":\"Shake your shoulders while your bent arms rock across your chest.\",\"family\":\"groove\",\"poses\":[[[-0.9,0.1],[0.15,0.28]],[[-0.2,0.28],[0.95,-0.05]],[[-1,0.2],[0.25,-0.1]],[[-0.25,-0.1],[1,0.2]]],\"upperBodyAdaptation\":false},\r\n {\"id\":\"ymca\",\"title\":\"YMCA\",\"cue\":\"Make Y, M, C and A with your arms.\",\"family\":\"pose\",\"poses\":[[[-1.05,-0.7],[1.05,-0.7]],[[-0.15,-0.6],[0.15,-0.6]],[[-0.65,-0.65],[-0.1,0.35]],[[-0.05,-0.95],[0.05,-0.95]]],\"upperBodyAdaptation\":false}\r\n];\r\nexport const CATALOGUE_PROVENANCE=freeze({kind:'named-dance-upper-body-adaptations',creator:'MYR5 studio',total:authored.length,description:'Recognizable named social dances, illustrated by original authored short pose loops. No mocap, finger recognition or footwork scoring.',referenceDocument:'dance-references.mjs'});\r\nexport const MOVES=authored.map((m,index)=>({...m,beats:16,intensity:'easy',tempoRangeBpm:[70,180],entry:'neutral',exit:'neutral',construction:{basePath:m.id,side:'together',index},requiredLandmarks:[11,12,13,14,15,16],profile:UPPER_BODY_PROFILE,provenance:CATALOGUE_PROVENANCE}));\r\nconst byId=new Map(MOVES.map(m=>[m.id,m]));\r\nconst KEY_PHASES=[0,1/8,3/8,5/8,7/8,1];\r\n// A two-bone arm keeps elbows connected and avoids impossible arm lengths.\r\nfunction arm(landmarks, side, wrist) {\r\n  const sign=side === 'left' ? -1 : 1, shoulderX=.5*sign;\r\n  const dx=wrist[0]*sign-shoulderX, dy=wrist[1];\r\n  const rawD=Math.hypot(dx,dy), d=Math.max(.08,Math.min(1.09,rawD));\r\n  const ux=dx/(rawD||1), uy=dy/(rawD||1);\r\n  const wx=shoulderX+ux*d, wy=uy*d;\r\n  const a=(.55*.55-.56*.56+d*d)/(2*d), h=Math.sqrt(Math.max(0,.55*.55-a*a));\r\n  const e=side === 'left' ? 13 : 14;\r\n  // Choose the outward/downward elbow at each authored pose. A fixed IK side\r\n  // pinches low/crossed hands into the sternum. Cache this branch in joint space:\r\n  // interpolation never flips a branch halfway through a rendered movement.\r\n  const candidates=[-1,1].map(bend=>point(shoulderX+ux*a-uy*h*bend,uy*a+ux*h*bend));\r\n  const preference=p=>sign*(p.x-shoulderX)+(wy<-.45?.6:2)*p.y-10*Math.max(0,.2-sign*p.x);\r\n  landmarks[e]=preference(candidates[0])>=preference(candidates[1])?candidates[0]:candidates[1];\r\n  landmarks[e+2]=point(wx,wy);\r\n}\r\n\r\n\r\nconst rigs=new Map();\r\nconst shortest=(a,b)=>Math.atan2(Math.sin(b-a),Math.cos(b-a));\r\nfunction poseAt(move,t){\r\n let nodes=rigs.get(move.id);\r\n if(!nodes){nodes=[[REST_LEFT,REST_RIGHT],...move.poses,[REST_LEFT,REST_RIGHT]].map(pair=>pair.map((pose,i)=>{const side=i?'right':'left',p=[];p[i?12:11]=point(i?.5:-.5,0);arm(p,side,[i?pose[0]:-pose[0],pose[1]]);const e=p[i?14:13],w=p[i?16:15],sx=i?.5:-.5;return [Math.atan2(e.y,e.x-sx),Math.atan2(w.y-e.y,w.x-e.x),pose[2]??0];}));rigs.set(move.id,nodes);}\r\n const i=Math.min(nodes.length-2,KEY_PHASES.findIndex((end,index)=>index>0&&t<=end)-1),u=smooth((t-KEY_PHASES[i])/(KEY_PHASES[i+1]-KEY_PHASES[i]));\r\n return [0,1].map(side=>{const a=nodes[i][side],b=nodes[i+1][side];return [a[0]+shortest(a[0],b[0])*u,a[1]+shortest(a[1],b[1])*u,a[2]+(b[2]-a[2])*u];});\r\n}\r\nexport function sampleMove(id,phase=0){\r\n const move=byId.get(id);if(!move)throw new RangeError(`Unknown named dance: ${id}`);\r\n const bounded=Number.isFinite(phase)?Math.max(0,Math.min(1,phase)):0,t=bounded===1?0:bounded;\r\n const landmarks=Array(33).fill(null);\r\n Object.assign(landmarks,{0:point(0,-.65),2:point(-.1,-.7),5:point(.1,-.7),7:point(-.2,-.6),8:point(.2,-.6),11:point(-.5,0),12:point(.5,0)});\r\n const poses=poseAt(move,t);\r\n for(const i of [0,1]){\r\n  const [upper,lower,z]=poses[i],sx=i?.5:-.5,elbow=i?14:13,wrist=i?16:15;\r\n  landmarks[elbow]=point(sx+.55*Math.cos(upper),.55*Math.sin(upper),z*.45);\r\n  landmarks[wrist]=point(landmarks[elbow].x+.56*Math.cos(lower),landmarks[elbow].y+.56*Math.sin(lower),z);\r\n }\r\n const envelope=Math.sin(Math.PI*t)**2,cycle=Math.sin(Math.PI*8*t);\r\n const armAsymmetry=landmarks[15].x+landmarks[16].x;\r\n const lean=move.id==='dab'&&Math.abs(armAsymmetry)>1e-9?Math.max(-.18,Math.min(.18,-armAsymmetry*.14)):0;\r\n return {landmarks,features:extractUpperBodyFeatures(landmarks),profile:UPPER_BODY_PROFILE,coachYaw:0,demo:{bodySway:envelope*Math.sin(Math.PI*4*t)*.12,bodyBounce:envelope*Math.abs(cycle)*.045,step:move.upperBodyAdaptation?envelope*cycle*.12:0,lean}};\r\n}\r\nfor(const move of MOVES){\r\n let speed=0,previous=sampleMove(move.id,0).landmarks;\r\n for(let i=1;i<=256;i++){const next=sampleMove(move.id,i/256).landmarks;for(const n of [13,14,15,16])speed=Math.max(speed,256*Math.hypot(next[n].x-previous[n].x,next[n].y-previous[n].y,next[n].z-previous[n].z));previous=next;}\r\n // Margin above sampled peak protects the continuous interpolation between samples.\r\n speed*=1.02;move.beats=Math.max(8,Math.ceil(speed/8)*8);move.keyPhases=[...KEY_PHASES];\r\n move.tempoRangeBpm=[70,Math.min(180,Math.floor(3*60*move.beats/speed))];\r\n move.motionLimit={maxSpeedPerCycle:speed,comfortableShoulderWidthsPerSecond:3,samples:256};\r\n}\r\nfreeze(MOVES);\r\n/** Duration at the actual music tempo, with every authored key on a whole beat.\r\n * MOVES.beats remains the conservative maximum-tempo fallback for old consumers. */\r\nexport function beatsForMove(moveOrId,bpm=120){\r\n const move=typeof moveOrId==='string'?byId.get(moveOrId):moveOrId;\r\n if(!move?.motionLimit){if(move&&Number.isFinite(move.beats)&&move.beats>0)return move.beats;throw new RangeError('Unknown named dance.');}\r\n if(!Number.isFinite(bpm)||bpm<70||bpm>180)throw new RangeError('Routine tempo must be between 70 and 180 BPM.');\r\n return Math.max(8,Math.ceil(move.motionLimit.maxSpeedPerCycle*bpm/(60*3*8))*8);\r\n}\r\nfunction hash(value){let h=2166136261;for(const c of String(value)){h^=c.charCodeAt(0);h=Math.imul(h,16777619);}return h>>>0;}\r\nexport function chooseRoutine({trackKey='default',bpm=104,intensity='easy',seed=''}={}){\r\n if(!Number.isFinite(bpm)||bpm<70||bpm>180)throw new RangeError('Routine tempo must be between 70 and 180 BPM.');\r\n if(!['easy','steady'].includes(intensity))throw new RangeError('Intensity must be easy or steady.');\r\n const pool=MOVES.filter(m=>bpm>=m.tempoRangeBpm[0]&&bpm<=m.tempoRangeBpm[1]);\r\n if(pool.length<2)throw new RangeError('No comfortable named-dance pool for this tempo.');\r\n let state=hash(`${trackKey}|${bpm}|${intensity}|${seed}`),previous;const plan=[];\r\n for(let i=0;i<(intensity==='easy'?12:16);i++){state=(Math.imul(state,1664525)+1013904223)>>>0;const eligible=pool.filter(m=>m.id!==previous);const move=eligible[state%eligible.length];plan.push(move.id);previous=move.id;}\r\n return Object.freeze(plan);\r\n}\r\n","modules/dance/android-game/music-source.mjs":"import {ExternalMusicClock} from './engine.mjs';\r\n\r\n// These are the existing original, sample-free Coach compositions.\r\nexport const ORIGINAL_TRACKS=Object.freeze([\r\n {id:'neon-step-pop',title:'Neon Step',bpm:116,genre:'pop-disco'},\r\n {id:'moonroom-swing',title:'Moonroom Swing',bpm:96,genre:'jazz'},\r\n {id:'cosmic-clave',title:'Cosmic Clave',bpm:112,genre:'latin'},\r\n].map(t=>Object.freeze({...t,url:`/modules/dance/assets/audio/${t.id}.wav`})));\r\n\r\nexport class MusicSource{\r\n constructor({now=()=>performance.now(),createAudio=()=>new Audio()}={}){this.now=now;this.createAudio=createAudio;this.native=new ExternalMusicClock();this.mode='manual';this.explicit=false;this.sequence=0;this.epoch=1;this.song=1;this.anchor=now();this.audio=null;this.index=0;this.nativeEpoch=null;this.nativeTrack=null;this.error='';this.disposed=false;}\r\n acceptNative(d){if(!this.native.accept(d,this.now()))return false;if(!this.explicit&&this.mode!=='native'){this.mode='native';this.epoch++;}return true;}\r\n async select(mode){if(!['manual','native','original'].includes(mode))throw Error('Unknown music source');this.explicit=true;this.audio?.pause();if(this.audio){this.audio.onended=null;this.audio.onerror=null;}this.audio=null;this.mode=mode;this.epoch++;this.song++;this.anchor=this.now();this.error='';if(mode==='original')await this.playOriginal();}\r\n async playOriginal(){\r\n  const ticket=this.epoch,audio=this.createAudio(),track=ORIGINAL_TRACKS[this.index];this.audio=audio;audio.preload='auto';audio.src=track.url;\r\n  audio.onended=()=>{if(this.audio===audio&&!this.disposed)void this.next().catch(e=>{this.error=e.message;});};\r\n  audio.onerror=()=>{if(this.audio===audio)this.error='Music could not load. Choose any music app and tap the beat.';};\r\n  try{await audio.play();if(this.disposed||ticket!==this.epoch)audio.pause();}catch(e){if(ticket===this.epoch)this.error='Tap Resume to start the music.';throw e;}\r\n }\r\n async next(){if(this.mode==='native')return false;this.audio?.pause();if(this.audio)this.audio.onended=null;this.epoch++;this.song++;this.anchor=this.now();if(this.mode==='original'){this.index=(this.index+1)%ORIGINAL_TRACKS.length;await this.playOriginal();}return true;}\r\n pauseOwned(){if(this.mode==='original')this.audio?.pause();}\r\n async resumeOwned(){if(this.mode==='original'&&this.audio)await this.audio.play();}\r\n snapshot(){\r\n  const now=this.now();let state;\r\n  if(this.mode==='native'){\r\n   const s=this.native.snapshot(now);if(s.epoch!==this.nativeEpoch||s.trackKey!==this.nativeTrack){this.nativeEpoch=s.epoch;this.nativeTrack=s.trackKey;this.epoch++;}\r\n   state={...s,speed:s.playbackSpeed??1};\r\n  }else if(this.mode==='original'){\r\n   const track=ORIGINAL_TRACKS[this.index],a=this.audio;\r\n   state={trackKey:`original:${track.id}:${this.song}`,title:track.title,artist:'MYR5 original music',positionSeconds:a?.currentTime??0,durationSeconds:Number.isFinite(a?.duration)?a.duration:0,speed:1,playing:!!a&&!a.paused&&!a.ended,fresh:!!a,knownBpm:track.bpm,knownGenre:track.genre};\r\n  }else state={trackKey:`manual:${this.song}`,title:'Your music',artist:'Manual sync · tap the beat; New song when it changes',positionSeconds:Math.max(0,(now-this.anchor)/1000),durationSeconds:0,speed:1,playing:true,fresh:true};\r\n  return {...state,schema:'myr5.external-music.v1',sequence:++this.sequence,epoch:this.epoch,dispatchAgeMs:0,sentAtUnixMs:Date.now()};\r\n }\r\n dispose(){this.disposed=true;if(this.audio){this.audio.onended=null;this.audio.onerror=null;this.audio.pause();this.audio.removeAttribute?.('src');this.audio.load?.();}this.audio=null;}\r\n}\r\n","modules/dance/android-game/themes.mjs":"const IDS = new Set(['auto','pop-disco','jazz','funk','latin','hip-hop','electronic','rock','chill']);\r\n\r\nexport function danceTheme(genre) {\r\n  const id = IDS.has(genre) ? genre : 'auto';\r\n  return { id };\r\n}\r\n","modules/dance/android-game/view.mjs":"import { drawFigure, projectCoach } from '../ui/skeleton.mjs';\r\nimport { createClubDemonstrator } from './club-demonstrator.mjs';\r\nimport { mountClubPresentation } from './club-presentation.mjs';\r\nimport { MOVES, sampleMove } from './moves.mjs';\r\nimport { danceTheme } from './themes.mjs';\r\nimport { symbolMarkup, VISUAL_CSS } from '../../../arcade/visual-language.mjs';\r\n\r\nconst esc = value => String(value ?? '').replace(/[&<>\"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\r\nconst fmt = n => `${Math.floor(Math.max(0, Number(n) || 0) / 60)}:${String(Math.floor(Math.max(0, Number(n) || 0) % 60)).padStart(2,'0')}`;\r\nconst actionIcon = kind => kind==='library'\r\n  ? '<svg class=\"dg-action-icon\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path d=\"M5 10c7-3 14-2 19 2v28c-5-4-12-5-19-2Zm38 0c-7-3-14-2-19 2v28c5-4 12-5 19-2Z\"/><path d=\"M12 17h7m-7 7h7m10-7h7m-7 7h7\"/></svg>'\r\n  : kind==='preview'\r\n    ? '<svg class=\"dg-action-icon\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path d=\"M3 24s7-13 21-13 21 13 21 13-7 13-21 13S3 24 3 24Z\"/><circle cx=\"24\" cy=\"24\" r=\"6\"/><path d=\"m21 24 2 2 4-5\"/></svg>'\r\n  : kind==='camera'\r\n  ? '<svg class=\"dg-action-icon\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path d=\"M8 14h7l3-5h12l3 5h7a3 3 0 0 1 3 3v21H5V17a3 3 0 0 1 3-3Z\"/><circle cx=\"24\" cy=\"26\" r=\"8\"/><circle class=\"dg-icon-lens\" cx=\"24\" cy=\"26\" r=\"3\"/></svg>'\r\n  : kind==='listen'\r\n    ? '<svg class=\"dg-action-icon\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><rect x=\"18\" y=\"5\" width=\"12\" height=\"25\" rx=\"6\"/><path d=\"M11 23a13 13 0 0 0 26 0M24 36v7m-9 0h18m-17-8 3-3m10 3-3-3\"/></svg>'\r\n    : kind==='tap'\r\n      ? '<svg class=\"dg-action-icon\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path d=\"M22 25V9a3 3 0 0 1 6 0v14-5a3 3 0 0 1 6 0v5-3a3 3 0 0 1 6 0v10c0 8-5 13-13 13h-3c-4 0-7-2-10-6l-5-7a3 3 0 0 1 5-4l5 5\"/><path class=\"dg-icon-ripple\" d=\"M12 10a13 13 0 0 1 0 14M5 7a20 20 0 0 1 0 20\"/></svg>'\r\n      : symbolMarkup(({play:'play',pause:'pause',stop:'stop',return:'return',next:'speed',again:'return'})[kind]||'check');\r\nconst sr = label => `<span class=\"game-sr\">${label}</span>`;\r\n\r\nexport function mountGameView({ root = document.body, actions = {}, moves = MOVES, sampleMove: sampler = sampleMove, genres=[] } = {}) {\r\n  const host = document.createElement('main');\r\n  host.className = 'disco-game';\r\n  host.innerHTML = `\r\n    <style>${VISUAL_CSS}</style>\r\n    <header class=\"dg-header\"><button class=\"dg-back\" type=\"button\" data-action=\"back\" aria-label=\"Back to Coach\" title=\"Back to Coach\">${actionIcon('return')}</button><div class=\"dg-brand\"><span class=\"dg-brand-dot\"></span> DANCE</div><button class=\"dg-library-toggle\" type=\"button\" data-action=\"library\" aria-label=\"Open ${moves.length} named dances\" title=\"Open movement catalogue\" aria-expanded=\"false\">${actionIcon('library')}<b>${moves.length}</b></button></header>\r\n    <section class=\"dg-stage\" aria-label=\"Dance coach and camera\">\r\n      <img class=\"dg-venue-scene\" alt=\"\" aria-hidden=\"true\"><div class=\"dg-stage-grid\" aria-hidden=\"true\"></div><div class=\"dg-stage-glow\" aria-hidden=\"true\"></div>\r\n      <div class=\"dg-dance-floor\" aria-hidden=\"true\"></div>\r\n      <div class=\"dg-stage-label\"><span class=\"dg-live-dot\"></span><span data-bind=\"coach-label\">YOUR COACH</span><span class=\"dg-streak\" data-bind=\"combo\">✦ 0</span></div>\r\n      <div class=\"dg-coach-wrap\"><div class=\"dg-coach-depth\" data-coach-host><canvas class=\"dg-coach\" aria-hidden=\"true\"></canvas></div></div>\r\n      <canvas class=\"dg-live-outline\" aria-label=\"Your live arm movement outline\"></canvas>\r\n      <div class=\"dg-cam-frame\"><video data-video playsinline muted aria-label=\"Your camera preview\"></video><div class=\"dg-cam-placeholder\" data-bind=\"cam-placeholder\"><span>${actionIcon('camera')}</span>${sr('Camera off')}</div><span class=\"dg-cam-tag\" data-bind=\"cam-tag\"></span></div>\r\n      <div class=\"dg-stage-foot\"><span data-bind=\"tracking\">Connect your camera to start</span><span class=\"dg-minutes\" data-bind=\"active\">0:00</span><span class=\"game-sr\" data-bind=\"coach-detail\" role=\"status\" aria-live=\"polite\"></span></div>\r\n      <div class=\"dg-overlay\" data-overlay hidden><div class=\"dg-overlay-card\"><span class=\"dg-overlay-kicker\" data-bind=\"overlay-kicker\">READY?</span><h1 data-bind=\"overlay-title\">Let’s dance.</h1><p data-bind=\"overlay-copy\">Open this game from your Android companion to bring in a track.</p><div class=\"dg-overlay-actions\" data-bind=\"overlay-actions\"></div></div></div>\r\n    </section>\r\n    <section class=\"dg-star-board\" aria-label=\"Round score and star goals\"><div><b data-bind=\"stars\">&#9734;&#9734;&#9734;&#9734;&#9734;</b><span data-bind=\"goal\"><span class=\"dg-goal-visual\">2,400 &#183; 0/120</span><span class=\"game-sr\">Next star at 2,400; 0 of 120 beats</span></span></div><strong data-bind=\"judgment\" aria-live=\"polite\"><span class=\"dg-judgment-icon\">${symbolMarkup('target')}</span><span class=\"dg-judgment-copy\">FIND YOUR GROOVE</span></strong></section>\r\n    <section class=\"dg-now\" aria-label=\"Current move\">\r\n      <div class=\"dg-now-head\"><h2 class=\"dg-dance-name\" data-bind=\"title\">Dance</h2><span class=\"dg-move-count\" data-bind=\"position\" hidden></span></div>\r\n      <div class=\"dg-move-main\"><div class=\"dg-move-copy\"><p data-bind=\"cue\">Choose a named dance to see its cue.</p></div><div class=\"dg-move-glyph\" role=\"img\" aria-label=\"Solid 3D dance demonstration\"><canvas class=\"dg-glyph\"></canvas><span class=\"dg-demo-fallback\">3D preview unavailable</span></div></div>\r\n      <div class=\"dg-move-meta\"><span data-bind=\"handcue\"></span><span data-bind=\"phase\">FOLLOW THE GLOW</span></div>\r\n      <div class=\"dg-beat-rail\" aria-label=\"Beat arrival\"><i></i><b></b></div><div class=\"dg-upnext\"><span class=\"dg-upnext-label\">UP NEXT</span><div class=\"dg-lane\" data-bind=\"lane\"><i class=\"dg-lane-line\" aria-hidden=\"true\"></i><div class=\"dg-upnext-list\" data-bind=\"upcoming\"></div></div><span class=\"dg-hit-zone\" data-bind=\"hit-grade\" aria-label=\"Beat timing\"><span class=\"dg-hit-icon\">${symbolMarkup('target')}</span><span class=\"game-sr dg-hit-copy\">BEAT</span></span></div><div class=\"dg-score-result\" data-bind=\"score-result\"></div>\r\n      <div class=\"dg-progress\"><i data-bind=\"progress\"></i></div>\r\n    </section>\r\n    <section class=\"dg-controls\" aria-label=\"Dance controls\"><div class=\"dg-setup-controls\" data-setup-controls>\r\n      <div class=\"dg-track-note\" data-bind=\"track\">NO TRACK LINKED <small>Open from Android companion to add a song</small></div>\r\n      <div class=\"dg-source-row\"><label>Music<select id=\"music-source\" aria-label=\"Music source\"><option value=\"manual\">Any music app · tap</option><option value=\"native\" disabled>Android linked player</option><option value=\"original\">Coach original playlist</option></select></label><label>Style<select id=\"genre\" aria-label=\"Dance genre\">${genres.map(g=>`<option value=\"${esc(g.id)}\">${esc(g.label)}</option>`).join('')}</select></label></div>\r\n      <div class=\"dg-tempo-row\"><button type=\"button\" class=\"dg-secondary dg-icon-button\" data-action=\"camera\" id=\"camera\" aria-label=\"Enable camera\" title=\"Enable camera\">${actionIcon('camera')}${sr('Enable camera')}</button><button type=\"button\" class=\"dg-secondary dg-icon-button\" data-action=\"listen\" id=\"listen\" aria-label=\"Listen to the beat\" title=\"Listen to the beat\">${actionIcon('listen')}${sr('Listen to the beat')}</button></div><button type=\"button\" class=\"dg-secondary dg-icon-button dg-tap-tempo\" data-action=\"tap\" id=\"tap\" aria-label=\"Tap four beats\" title=\"Tap four beats\">${actionIcon('tap')}<span class=\"dg-tap-count\" aria-hidden=\"true\">4</span>${sr('Tap the beat four times')}</button>\r\n      <div class=\"dg-tempo-readout\" data-bind=\"tempo\">Tempo will appear after listening or 4 taps</div>\r\n      <p class=\"dg-status-setup\" data-bind=\"status-setup\" role=\"status\" aria-live=\"polite\"></p>\r\n      <button type=\"button\" class=\"dg-primary dg-icon-button dg-start\" data-action=\"begin\" id=\"begin\" aria-label=\"Start dance\" title=\"Start dance\">${actionIcon('play')}${sr('Start dance')}</button>\r\n      <button type=\"button\" class=\"dg-preview dg-icon-button\" data-action=\"preview\" id=\"preview\" aria-label=\"Try an unscored preview\" title=\"Try an unscored preview\">${actionIcon('preview')}${sr('Try a no-music preview')}</button>\r\n      <details class=\"dg-extra\"><summary>Import a coach file</summary><label class=\"dg-import\">Choose a coach JSON file<input type=\"file\" id=\"coach-import\" accept=\"application/json,.json\" data-action=\"import\"></label></details>\r\n    </div><div class=\"dg-playing-controls\" data-playing-controls hidden><button type=\"button\" class=\"dg-secondary dg-icon-button\" data-action=\"pause\" id=\"pause\" aria-label=\"Pause dance\" title=\"Pause dance\">${actionIcon('pause')}${sr('Pause')}</button><button type=\"button\" class=\"dg-secondary dg-icon-button\" data-action=\"next\" id=\"next-song\" aria-label=\"Next song\" title=\"Next song\">${actionIcon('next')}${sr('Next song')}</button><button type=\"button\" class=\"dg-end dg-icon-button\" data-action=\"finish\" id=\"finish\" aria-label=\"Finish dance\" title=\"Finish dance\">${actionIcon('stop')}${sr('Finish dance')}</button></div><p class=\"dg-help\">Any player: start your music, choose its style, tap 4 beats. Tap New song when it changes. Headphones work with taps. Original playlist plays directly.</p><details class=\"dg-goals\"><summary>Star goals &middot; 12,000 maximum</summary><p>1&#9733; 2,400 &middot; 2&#9733; 4,800 &middot; 3&#9733; 7,200 &middot; 4&#9733; 9,000 &middot; 5&#9733; 10,800. Move your arms on the beat. Broad movement counts; no perfect pose required. Each round judges 120 beats. Early finish keeps the full target.</p><p>Want your own files? VLC plays local music without a developer account; use Any music app + taps, or Android linked player. Licensed/original audio can be used with synchronized visuals.</p></details></section>\r\n    <aside class=\"dg-library\" data-library hidden><div class=\"dg-library-head\"><div><span class=\"dg-overline\">MOVE CATALOGUE</span><h2>${moves.length} named dances</h2><p>Authored dance references. Follow at your own pace.</p></div><button type=\"button\" data-action=\"library-close\" aria-label=\"Close move catalogue\">×</button></div><label class=\"dg-search\">⌕ <input type=\"search\" placeholder=\"Search moves or styles\" aria-label=\"Search moves\"></label><div class=\"dg-move-list\" data-bind=\"library-list\"></div></aside>\r\n  `;\r\n  root.append(host);\r\n  const q = s => host.querySelector(s), bind = name => q(`[data-bind=\"${name}\"]`);\r\n  const video = q('[data-video]'), coachHost = q('[data-coach-host]'), canvas = q('.dg-coach'), glyph = q('.dg-glyph');\r\n  let view = {}, raf = 0, disposed = false, libraryOpen = false, overlayKey = '', upcomingKey = '';\r\n  const reducedMotion=matchMedia('(prefers-reduced-motion: reduce)');\r\n  const demonstrator=createClubDemonstrator(q('.dg-now'),sampler), club=mountClubPresentation(q('.dg-stage'));\r\n  let visualPhase=0, visualBeat=0, visualMove=moves[0]?.id;\r\n  const cueSlot=q('.dg-move-glyph'), beatRail=q('.dg-beat-rail'), stageElement=q('.dg-stage');\r\n  function groundSavedCoach(){\r\n    try{\r\n      const soles=window.myr5Creature?.stats?.().powerAnchors?.filter(p=>p.visible&&Number.isFinite(p.x)&&Number.isFinite(p.y)), savedCanvas=coachHost.querySelector('.myr5-companion-stage canvas');\r\n      if(!savedCanvas||!soles||soles.length<2){stageElement.dataset.grounded='false';return;}\r\n      const c=savedCanvas.getBoundingClientRect(),s=stageElement.getBoundingClientRect();\r\n      const points=soles.map(p=>({x:c.left+p.x*c.width-s.left,y:c.top+p.y*c.height-s.top}));\r\n      const x=points.reduce((sum,p)=>sum+p.x,0)/points.length,y=Math.max(...points.map(p=>p.y));\r\n      stageElement.dataset.grounded=String(y>0&&y<s.height-8);\r\n      stageElement.style.setProperty('--dg-ground-y',`${y}px`);stageElement.style.setProperty('--dg-ground-x',`${x}px`);\r\n      stageElement.style.setProperty('--dg-ground-width',`${Math.max(55,Math.abs(points[0].x-points[1].x)+54)}px`);\r\n    }catch{stageElement.dataset.grounded='false';}\r\n  }\r\n  const call = (name, ...args) => { if (typeof actions[name] === 'function') actions[name](...args); };\r\n  host.addEventListener('click', e => { const btn=e.target.closest('[data-action]'); if(!btn) return; const a=btn.dataset.action;\r\n    if(a==='library'){libraryOpen=!libraryOpen;call('showLibrary',libraryOpen);syncLibrary();}\r\n    else if(a==='library-close'){libraryOpen=false;syncLibrary();}\r\n    else if(a==='camera')call('camera'); else if(a==='listen')call('listen'); else if(a==='tap')call('tapTempo',e.timeStamp); else if(a==='begin')call('begin'); else if(a==='preview')call('preview'); else if(a==='pause')call('pause'); else if(a==='finish')call('finish');\r\n    else if(a==='back')call('back');else if(a==='next')call('nextSong');\r\n  });\r\n  q('#music-source').addEventListener('change',e=>call('musicSource',e.target.value));\r\n  q('#genre').addEventListener('change',e=>call('genre',e.target.value));\r\n  q('#coach-import').addEventListener('change', e => { if(e.target.files?.[0]) call('importCoach',e.target.files[0]); });\r\n  q('.dg-search input').addEventListener('input', syncLibrary);\r\n  function syncLibrary(){\r\n    const panel=q('[data-library]'), toggle=q('.dg-library-toggle'); panel.hidden=!libraryOpen; toggle.setAttribute('aria-expanded',String(libraryOpen));\r\n    if(!libraryOpen)return;\r\n    const term=q('.dg-search input').value.trim().toLowerCase();\r\n    const filtered=moves.filter(m=>`${m.title} ${m.family} ${m.cue}`.toLowerCase().includes(term));\r\n    bind('library-list').innerHTML=filtered.map(m=>`<button type=\"button\" class=\"dg-library-item\" data-move=\"${esc(m.id)}\"><span><b>${esc(m.title)}</b><small>${esc(m.family)} · ${esc(m.cue)}</small></span><i>›</i></button>`).join('') || '<p class=\"dg-empty\">No named dances match that search.</p>';\r\n    bind('library-list').querySelectorAll('[data-move]').forEach(b=>b.addEventListener('click',()=>{call('choosePreview',b.dataset.move);libraryOpen=false;syncLibrary();}));\r\n  }\r\n  function syncUpcoming(list){\r\n    const key=list.map(m=>`${m.id||''}:${m.title||m.name||''}`).join('|');\r\n    if(key===upcomingKey)return;\r\n    upcomingKey=key;\r\n    bind('upcoming').innerHTML=list.map(m=>`<div class=\"dg-next-card\" data-next-id=\"${esc(m.id||'')}\"><canvas class=\"dg-next-glyph\" aria-hidden=\"true\"></canvas><span>${esc(m.title||m.name||'Next move')}</span></div>`).join('')||'<span class=\"dg-next-empty\">Your next moves appear here</span>';\r\n  }\r\n  function fitCanvas(c){const box=c.getBoundingClientRect(),dpr=Math.min(devicePixelRatio||1,2);if(box.width&&box.height&&(c.width!==Math.round(box.width*dpr)||c.height!==Math.round(box.height*dpr))){c.width=Math.round(box.width*dpr);c.height=Math.round(box.height*dpr);}const ctx=c.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);return {ctx,w:box.width,h:box.height};}\r\n  function liveOutline(){const c=q('.dg-live-outline'),{ctx,w,h}=fitCanvas(c);ctx.clearRect(0,0,w,h);const p=view.liveLandmarks,a=p?.[11],b=p?.[12];if(!a||!b||!['failed','unsupported'].includes(view.coachLoadState)||view.avatarTrackingState!=='fresh')return;const width=Math.hypot(a.x-b.x,a.y-b.y);if(width<.08)return;const mid={x:(a.x+b.x)/2,y:(a.y+b.y)/2},safe=Array(33).fill(null);for(const i of [0,11,12,13,14,15,16])if(p[i]&&(p[i].visibility??0)>=.5)safe[i]={x:(p[i].x-mid.x)/width,y:(p[i].y-mid.y)/width};drawFigure(ctx,projectCoach(safe,{x:0,y:0,w,h},true),{lineWidth:4,arrows:false});}\r\n  function frame(){\r\n    if(disposed)return;\r\n    if(!document.hidden){\r\n      const moving=['dancing','count_in','break'].includes(view.phase);\r\n      if(moving){visualMove=view.moveId||moves[0]?.id;visualPhase=Number.isFinite(view.movePhase)?view.movePhase:0;visualBeat=Number.isFinite(view.beatPosition)?view.beatPosition:0;}\r\n      else if(!visualMove)visualMove=view.moveId||moves[0]?.id;\r\n      liveOutline();groundSavedCoach();club.update(visualBeat,reducedMotion.matches);\r\n      const beat=((visualBeat%1)+1)%1;\r\n      beatRail.style.setProperty('--arrival',String(reducedMotion.matches?1:beat));\r\n      beatRail.dataset.hit=String(beat<.14);\r\n      // Main reference remains useful with reduced motion; scenery stays still.\r\n      const shownPhase=reducedMotion.matches?Math.floor(visualPhase*8)/8:visualPhase;\r\n      const slots=[{element:cueSlot,id:visualMove,phase:shownPhase}];\r\n      host.querySelectorAll('.dg-next-glyph').forEach(c=>slots.push({element:c,id:c.closest('[data-next-id]').dataset.nextId,phase:reducedMotion.matches?.25:shownPhase}));\r\n      demonstrator.render(slots);\r\n    }\r\n    raf=requestAnimationFrame(frame);\r\n  }\r\n  function showOverlay(){const overlay=q('[data-overlay]'),phase=view.phase;const show=['waiting','prepare','ready','count_in','break','paused','lost','results'].includes(phase);overlay.hidden=!show;if(!show)return;\r\n    const countLabel=view.countdown!=null?String(Math.ceil(Number(view.countdown))):'Get set…';\r\n    const cfg={waiting:['START HERE','Let’s dance.','Choose music, enable the camera, then tap four beats.'],prepare:['GET READY','Find your groove.','Camera and beat are ready. Take your place.'],ready:['READY?','You’ve got this.','The coach is warmed up. Move how it feels good.'],count_in:['COMING IN',countLabel,'The beat is finding its way in.'],break:['SHAKE IT OUT','Shake it out!','Song change — keep moving, loose and easy. This break is unscored.'],paused:['PAUSED','Catch your breath.','Your dance is paused.'],lost:['PLAYER CHECK','Let’s get you back.',view.status||'Open the Android companion and reconnect your player.'],results:['NICE WORK','You showed up.','Every move counts. Take a bow.']}[phase]||['DISCO COACH','Let’s dance.',''];\r\n    const key=[phase,countLabel,view.breakMessage,view.status,view.trackTitle,view.playing].join('|');if(key===overlayKey)return;overlayKey=key;\r\n    const overlayIcon=phase==='break'?'rest':phase==='paused'?'pause':phase==='lost'?'recovery':phase==='results'?'star':'ready';bind('overlay-kicker').innerHTML=`${symbolMarkup(overlayIcon)}<span>${esc(cfg[0])}</span>`;bind('overlay-title').textContent=cfg[1];\r\n    bind('overlay-copy').textContent=phase==='results'?`${view.stars||0} of 5 stars · ${view.score||0} of 12,000 points. ${fmt(view.activeSeconds)} moving. Dance again to try for the next star.`:phase==='prepare'?'Show your shoulders and hands. Choose a style, then hear the beat or tap it four times.':phase==='break'?`New song: ${view.trackTitle||'finding your track'} \\u00b7 unscored break.`:phase==='paused'?(view.musicMode==='original'?'Your music and dance are paused.':view.playing?'Your dance is paused. External music keeps playing.':'Start your music, then resume your dance.'):phase==='lost'?'Reconnect the Android player, or choose Any music app and tap the beat.':cfg[2];\r\n    if(phase==='results'){const title=bind('overlay-title'),stars=Math.max(0,Math.min(5,Math.floor(view.stars||0)));title.innerHTML=`<span aria-hidden=\"true\">${'&#9733;'.repeat(stars)}${'&#9734;'.repeat(5-stars)}</span><small>${symbolMarkup('star')}${esc(view.score||0)} ${symbolMarkup('tracking')}${fmt(view.activeSeconds)}</small>`;title.setAttribute('aria-label',`${view.stars||0} of 5 stars; ${view.score||0} points; ${fmt(view.activeSeconds)} moving`);}else bind('overlay-title').removeAttribute('aria-label');\r\n    const acts=bind('overlay-actions');acts.innerHTML='';const add=(label,action,cls='dg-primary')=>{const b=document.createElement('button');b.type='button';b.className=`${cls} dg-icon-button`;b.dataset.action=action;b.setAttribute('aria-label',label);b.title=label;b.innerHTML=`${actionIcon(action==='resume'?'play':action==='restart'?'again':'preview')}${sr(label)}`;acts.append(b);};\r\n    // The unscored preview control already lives in setup below the stage.\r\n    if(phase==='paused')add('RESUME DANCE','resume');\r\n    if(phase==='results')add('DANCE AGAIN','restart');\r\n  }\r\n  host.addEventListener('click',e=>{const a=e.target.closest('[data-action]')?.dataset.action;if(a==='resume')call('resume');if(a==='restart')call('restart');});\r\n  function render(next={}){\r\n    view={...view,...next};const phase=view.phase||'waiting';host.dataset.phase=phase;\r\n    const theme=danceTheme(view.genre);host.dataset.genre=theme.id;const venue=q('.dg-venue-scene');const venueSrc=new URL(`./themes/${theme.id}.svg`,import.meta.url).href;if(venue.src!==venueSrc)venue.src=venueSrc;\r\n    cueSlot.setAttribute('aria-label',`Solid 3D demonstration: ${['paused','lost','results'].includes(phase)?bind('title').textContent:view.title||'dance'}`);\r\n    bind('title').textContent=(['paused','lost','results'].includes(phase)?bind('title').textContent:null)||view.title||moves.find(m=>m.id===view.moveId)?.title||'Dance';bind('cue').textContent=view.cue||moves.find(m=>m.id===view.moveId)?.cue||'Choose a named dance to see its cue.';\r\n    bind('phase').textContent=view.movePhase===undefined?'FOLLOW THE GLOW':phase==='break'?'UNSCORED BREAK':'FOLLOW THE GLOW';\r\n    bind('handcue').textContent=view.optionalTurn?'OPTIONAL TURN · NOT SCORED':view.handCue==='peace'?'✌ PEACE SIGN':view.sideCue||'';\r\n    bind('position').hidden=true;\r\n    const coachState=view.coachState||'pending',cameraError=view.cameraState==='error',outlineOnly=['failed','unsupported'].includes(view.coachLoadState)&&view.avatarTrackingState==='fresh';host.dataset.coachState=coachState;host.dataset.coachLoadState=view.coachLoadState||'pending';bind('coach-label').textContent=coachState==='fresh'?'Live 3D coach':view.coachMessage||'Loading coach';q('.dg-live-outline').hidden=!outlineOnly;const detail=[cameraError?view.trackingMessage:null,view.coachError,view.coachRecovery].filter(Boolean).join('. ');if(bind('coach-detail').textContent!==detail)bind('coach-detail').textContent=detail;\r\n    const grade=view.preview||!['dancing','results'].includes(phase)?'neutral':view.judgment==='PERFECT'?'great':view.judgment==='GOOD'?'good':view.judgment==='MISS'?'miss':'neutral';host.dataset.preview=String(!!view.preview);host.dataset.grade=grade;host.dataset.party=view.partyActive&&phase==='dancing'?grade:'neutral';const hit=bind('hit-grade'),hitLabel=grade==='neutral'?'BEAT':grade.toUpperCase(),hitKind=grade==='great'?'star':grade==='good'?'check':grade==='miss'?'cross':'target';hit.setAttribute('aria-label',hitLabel==='BEAT'?'Beat timing':`Timing: ${hitLabel}`);hit.querySelector('.dg-hit-icon').innerHTML=hitKind==='cross'?'<svg class=\"game-symbol\" viewBox=\"0 0 64 64\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"5\" stroke-linecap=\"round\"><path d=\"m18 18 28 28M46 18 18 46\"/></svg>':symbolMarkup(hitKind);hit.querySelector('.dg-hit-copy').textContent=hitLabel;\r\n    bind('stars').textContent='\\u2605'.repeat(view.stars||0)+'\\u2606'.repeat(5-(view.stars||0));const goalVisual=view.nextStarAt?`${view.nextStarAt.toLocaleString()} \\u00b7 ${view.judgedBeats||0}/120`:'5\\u2605 \\u00b7 12,000';bind('goal').querySelector('.dg-goal-visual').textContent=goalVisual;bind('goal').querySelector('.game-sr').textContent=view.nextStarAt?`Next star at ${view.nextStarAt.toLocaleString()}; ${view.judgedBeats||0} of 120 beats`:'Five stars; maximum score 12,000 points';const judgment=view.preview?'PREVIEW \\u00b7 UNSCORED':view.judgment==='PERFECT'?'GREAT':view.judgment||'FIND YOUR GROOVE',jKind=view.preview?'target':grade==='great'?'star':grade==='good'?'check':grade==='miss'?'cross':'target';bind('judgment').setAttribute('aria-label',judgment);bind('judgment').querySelector('.dg-judgment-icon').innerHTML=jKind==='cross'?'<svg class=\"game-symbol\" viewBox=\"0 0 64 64\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"5\" stroke-linecap=\"round\"><path d=\"m18 18 28 28M46 18 18 46\"/></svg>':symbolMarkup(jKind);bind('judgment').querySelector('.dg-judgment-copy').textContent=view.preview||phase==='break'?'UNSCORED':judgment;\r\n    if(document.activeElement!==q('#music-source'))q('#music-source').value=view.musicMode||'manual';q('#music-source option[value=\"native\"]').disabled=!view.nativeAvailable;if(document.activeElement!==q('#genre'))q('#genre').value=view.genre||'auto';q('#next-song').hidden=view.musicMode==='native';\r\n    if(bind('combo').getAttribute('aria-label')!==`${view.score||0} points`){bind('combo').innerHTML=`${symbolMarkup('star')}<b>${Number(view.score||0).toLocaleString()}</b>`;bind('combo').setAttribute('aria-label',`${view.score||0} points`);}if(bind('active').getAttribute('aria-label')!==`${fmt(view.activeSeconds)} moving`){bind('active').innerHTML=`${symbolMarkup('tracking')}<b>${fmt(view.activeSeconds)}</b>`;bind('active').setAttribute('aria-label',`${fmt(view.activeSeconds)} moving`);}\r\n    const cameraMessage=/permission/i.test(view.trackingMessage||'')?'Camera permission off · enable and retry':view.trackingMessage||'Tap camera to retry';const coachMessage=cameraError?cameraMessage:outlineOnly?`Outline only · ${coachState==='failed'?'reopen to retry':'choose a two-legged coach'}`:coachState==='fresh'?'':coachState==='failed'?'Reopen Dance to retry':coachState==='unsupported'?'Use a two-legged coach':view.coachMessage||'Loading coach';if(bind('tracking').textContent!==coachMessage)bind('tracking').textContent=coachMessage;bind('tracking').title=detail;\r\n    bind('cam-placeholder').hidden=!!view.cameraReady;bind('cam-tag').textContent='';\r\n    bind('track').innerHTML=view.trackTitle?`${esc(view.trackTitle)} <small>${esc(view.artist||'Music player connected')}</small>`:'NO TRACK LINKED <small>Open from Android companion to add a song</small>';\r\n    const tempo=view.tempoReady?`${Math.round(view.bpm||0)} BPM <small>${view.tempoSource==='original'?'ORIGINAL TRACK GRID':view.tempoSource==='microphone'?'LISTENING ESTIMATE':'TAP ESTIMATE'}</small>`:'Tempo will appear after listening or 4 taps';bind('tempo').innerHTML=tempo;\r\n    q('#begin').disabled=!(view.cameraReady&&view.tempoReady);\r\n    q('#camera').setAttribute('aria-label',view.cameraReady?'Camera ready':'Enable camera');q('#camera').title=view.cameraReady?'Camera ready':'Enable camera';q('#camera').classList.toggle('is-ready',!!view.cameraReady);q('#listen').classList.toggle('is-ready',!!view.tempoReady);\r\n    q('.dg-stage').dataset.coachReady=String(!!view.coachReady);\r\n    bind('score-result').textContent=phase==='results'?`${view.stars||0} / 5 STARS · ${view.score||0} / 12,000 POINTS · ${fmt(view.activeSeconds)} MOVING`:'';\r\n    bind('status-setup').textContent=view.status||'';\r\n    const list=(view.upcoming||[]).slice(0,3);if(!['paused','lost','results'].includes(phase)||!upcomingKey)syncUpcoming(list);\r\n    const pct=view.durationSeconds?Math.max(0,Math.min(100,(view.positionSeconds||0)/view.durationSeconds*100)):0;bind('progress').style.width=`${pct}%`;\r\n    const needsSetup=(phase==='break'&&!view.tempoReady)||(phase==='paused'&&(!view.cameraReady||!view.tempoReady))||phase==='lost';\r\n    q('[data-setup-controls]').hidden=phase==='results'||(['dancing','count_in','break','paused'].includes(phase)&&!needsSetup);q('[data-playing-controls]').hidden=!['dancing','count_in','break','paused'].includes(phase);\r\n    const pauseAction=view.preview?'preview':phase==='paused'?'resume':'pause',pauseLabel=view.preview?'Back to song':phase==='paused'?'Resume dance':'Pause dance';q('#pause').innerHTML=`${actionIcon(phase==='paused'?'play':view.preview?'return':'pause')}${sr(pauseLabel)}`;q('#pause').setAttribute('aria-label',pauseLabel);q('#pause').title=pauseLabel;q('#pause').dataset.action=pauseAction;\r\n    showOverlay();\r\n  }\r\n  raf=requestAnimationFrame(frame);\r\n  return {render,dispose(){disposed=true;cancelAnimationFrame(raf);demonstrator.dispose();club.dispose();host.remove();},coachHost,video,nowCanvas:q('.dg-solid-demonstrators')||glyph};\r\n}\r\n","modules/dance/classic.mjs":"import {getCatalogue,sampleTarget,validateCatalogue,UPPER_BODY_PROFILE} from './content/index.mjs';\r\nimport {builtinTracks,prepareSession,createMusicSession,createLocalTrack} from './music/index.mjs';\r\nimport {createPoseBridge} from './pose/index.mjs';\r\nimport {mountDance} from './ui/index.mjs';\r\nimport {activeSlotAt} from './contracts.mjs';\r\nimport {loadProgress,saveResult,savePractice} from './progress.mjs';\r\n\r\nlet current=null;\r\nexport async function openDance(){\r\n if(current){current.dialog.showModal();return current.dialog;}\r\n const check=validateCatalogue();if(!check.ok)throw Error('Dance moves could not load.');\r\n const dialog=document.createElement('dialog');dialog.id='dancePanel';dialog.setAttribute('aria-label','Dance cardio');\r\n dialog.style.cssText='position:fixed;inset:0;margin:0 auto;padding:0;border:1px solid #bc89ff;border-radius:20px;background:#160e24;color:#eee1ff;width:min(100%,900px);max-width:100vw;height:100dvh;max-height:100dvh;overflow:auto;';\r\n const root=document.createElement('div');dialog.append(root);document.body.append(dialog);\r\n const catalogue=getCatalogue(),teaching=catalogue.filter(m=>m.id!=='neutral-bridge'&&m.id!=='rest'),allTracks=[...builtinTracks],localUrls=new Set();\r\n let phase='select',selection={tracks:[builtinTracks[0]],style:'auto',intensity:'easy'},plan=null,music=null,error=null,feedback=null,progress=loadProgress(),raf=0,closed=false,cameraReady=false,cameraStarting=false,run=0,teachIndex=0,practiceId=null,practiceStart=0,practiceEpoch=0,playerLandmarks=null,mirrored=true,result=null,saveId=null,latestFrameMs=0,limitedSince=null;\r\n let clock={state:'idle',sessionSeconds:0,audioNowSeconds:0,performanceNowMs:performance.now(),clockEpoch:0,activeOccurrenceId:null};\r\n const observations=[];\r\n let pendingAction=null,resumeUntil=0,lastObservedSamples=-1;\r\n const getClock=()=>phase==='practice'?{state:'playing',sessionSeconds:(performance.now()-practiceStart)/1000,audioNowSeconds:performance.now()/1000,performanceNowMs:performance.now(),clockEpoch:practiceEpoch}:music?.snapshot()??{...clock,performanceNowMs:performance.now()};\r\n function slot(){if(phase==='practice'){const move=catalogue.find(m=>m.id===practiceId)||catalogue[0];const bpm=(move.tempoRangeBpm[0]+move.tempoRangeBpm[1])/2,lead=4*60/bpm;if(getClock().sessionSeconds<lead)return {id:'practice-count',moveId:null,role:'count_in',scored:false,startSessionSeconds:0,endSessionSeconds:lead,repeatCount:1};return {id:'practice-'+move.id,moveId:move.id,role:'move',scored:true,repeatCount:1000,startSessionSeconds:lead,endSessionSeconds:lead+1000*move.beats*60/bpm,startBeat:0,endBeat:1000*move.beats};}return activeSlotAt(plan,getClock().sessionSeconds);}\r\n const target=(id,p)=>p===undefined?catalogue.find(m=>m.id===id):sampleTarget(id,p);\r\n const bridge=createPoseBridge({getClock,getActiveSlot:slot,getTarget:target,onFeedback:f=>{feedback=f;}});\r\n function fail(e,next=phase){error=e?.message||String(e);phase=next;render();}\r\n async function camera(){\r\n  if(cameraStarting)return;cameraStarting=true;error=null;render();\r\n  try{\r\n   if(!window.myr5DanceCamera)throw Error('The camera is still loading. Tap camera again.');\r\n   const host=root.querySelector('[data-dance-video]');\r\n   await window.myr5DanceCamera.start({host,onError:e=>{cameraReady=false;if(['playing','count_in','practice'].includes(phase))pause();error=e?.message||'Camera stopped. Tap camera to reconnect.';render();}});\r\n   if(closed){window.myr5DanceCamera.stop();return;}\r\n   cameraReady=true;bridge.startCalibration();\r\n  }catch(e){cameraReady=false;fail(e,'calibrate');}\r\n  finally{cameraStarting=false;render();}\r\n }\r\n function calibrated(){const s=bridge.snapshot();return !!(s?.calibrated||s?.calibration?.ready||s?.calibration?.status==='ready'||s?.status==='tracking');}\r\n function render(){\r\n  if(closed)return;\r\n  clock=getClock();const active=slot();\r\n  const moveId=practiceId||teaching[teachIndex]?.id;\r\n  const countdown=resumeUntil?Math.max(1,Math.ceil((resumeUntil-performance.now())/1000)):active?.role==='count_in'?Math.max(1,Math.ceil((active.endSessionSeconds-clock.sessionSeconds)/(active.endSessionSeconds-active.startSessionSeconds)*(active.endBeat?active.endBeat-active.startBeat:4))):null;\r\n  const view={phase,selection:{...selection,queue:selection.tracks,trackIds:selection.tracks.map(t=>t.id)},plan,clock,activeSlot:active,upcomingSlots:plan?.slots.filter(s=>s.endSessionSeconds>clock.sessionSeconds).slice(0,8)||[],feedback,progress,error,camera:{state:cameraStarting?'starting':cameraReady?'ready':error?'error':'idle',message:error},cameraReady,cameraStarting,calibrated:calibrated(),cameraVideo:cameraReady?window.myr5DanceCamera?.video:null,playerLandmarks,mirrored,previewSeconds:performance.now()/1000,teachingMoveId:moveId,teachMoveId:moveId,teachingIndex:teachIndex,result,results:result,countdown,builtinTracks:allTracks};\r\n  ui.render(view);\r\n }\r\n function pose(event){\r\n  if(closed)return;const d=event.detail;\r\n  latestFrameMs=performance.now();playerLandmarks=d?.points||null;mirrored=!!d?.mirrored;\r\n  bridge.ingest(d);\r\n  if(phase==='calibrate'&&calibrated()){if(pendingAction==='practice'){pendingAction=null;phase='teach';}else phase=pendingAction&&plan?'ready':'teach';error=null;}\r\n }\r\n function metrics(){\r\n  const vals=key=>observations.map(x=>x[key]).filter(Number.isFinite);const avg=key=>{const a=vals(key);return a.length?a.reduce((x,y)=>x+y,0)/a.length:null;};\r\n  return {sequence:avg('sequence'),timing:avg('timing'),effort:avg('effort'),coverage:avg('coverage'),timingKind:observations.some(o=>o.timingKind==='estimated_input')?'estimated_input':'unavailable'};\r\n }\r\n function finish(){\r\n  if(phase==='results'||closed)return;\r\n  phase='results';limitedSince=null;clock=music?.snapshot()||clock;bridge.resetEpoch();window.myr5DanceCamera?.stop();cameraReady=false;playerLandmarks=null;\r\n  result={id:saveId||crypto.randomUUID(),schemaVersion:1,trackingProfile:UPPER_BODY_PROFILE,seed:plan.seed,contentHash:plan.contentHash,trackIds:selection.tracks.map(t=>t.id),titles:selection.tracks.map(t=>t.title),intensity:selection.intensity,completedAt:new Date().toISOString(),secondsPlayed:plan.durationSeconds,...metrics()};\r\n  const saved=saveResult(result);progress=saved.progress;if(!saved.saved)error='Your result is visible here. This browser could not save it.';\r\n  render();\r\n }\r\n async function prepare(){\r\n  if(!selection.tracks.length){fail(Error('Choose one to five tracks.'),'select');return;}\r\n  const token=++run;phase='preparing';error=null;render();\r\n  try{const frozen=await prepareSession({tracks:selection.tracks,catalogue,seed:crypto.randomUUID(),intensity:selection.intensity});if(closed||token!==run)return;\r\n   music?.dispose();plan=frozen;music=createMusicSession({plan,tracks:selection.tracks,getVolume:()=>{const s=window.myr5DanceCamera?.sound;return s?.muted?0:s?.volume??1;},onEnded:finish,onError:e=>{pause();fail(e,'paused');}});phase='ready';observations.length=0;lastMetricSecond=-1;lastObservedSamples=-1;saveId=crypto.randomUUID();bridge.resetEpoch();render();\r\n  }catch(e){if(!closed&&token===run)fail(e,'select');}\r\n }\r\n async function begin(){\r\n  if(!music)return;\r\n  if(pendingAction==='resume'){pendingAction=null;return resume();}\r\n  if(pendingAction==='restart'){pendingAction=null;return restart();}\r\n  if(!await requireCamera('begin'))return;\r\n  error=null;\r\n  try{await music.unlock();if(closed)return;await music.start();limitedSince=null;phase='count_in';bridge.resetEpoch();render();}catch(e){fail(e,'ready');}\r\n }\r\n async function requireCamera(action){if(cameraReady&&calibrated()){pendingAction=null;return true;}pendingAction=action;phase='calibrate';error='Keep your shoulders, elbows and hands in the small camera preview.';if(!cameraReady)await camera();else bridge.startCalibration();render();return false;}\r\n function recordPractice(){const m=teaching.find(t=>t.id===practiceId);const seconds=m?(4+m.beats)*60/((m.tempoRangeBpm[0]+m.tempoRangeBpm[1])/2):Infinity;if((performance.now()-practiceStart)/1000>=seconds&&bridge.snapshot().acceptedSamples>0)progress=savePractice(practiceId);}\r\n function pause(){resumeUntil=0;limitedSince=null;if(phase==='practice'){recordPractice();phase='teach';}else if(music&&['playing','count_in'].includes(phase)){void music.pause();phase='paused';}bridge.resetEpoch();render();}\r\n async function resume(){try{if(!await requireCamera('resume'))return;await music.unlock();limitedSince=null;resumeUntil=performance.now()+3000;phase='count_in';error=null;bridge.resetEpoch();render();}catch(e){fail(e,'paused');}}\r\n async function restart(){try{if(!await requireCamera('restart'))return;observations.length=0;lastMetricSecond=-1;lastObservedSamples=-1;result=null;saveId=crypto.randomUUID();await music.unlock();await music.restart();limitedSince=null;phase='count_in';error=null;bridge.resetEpoch();render();}catch(e){fail(e,'paused');}}\r\n async function practice(id){\r\n  if(id==='next'||id==='skip'){if(phase==='practice')recordPractice();teachIndex++;if(teachIndex>=teaching.length){void prepare();return;}practiceId=null;phase='teach';}\r\n  else{practiceId=typeof id==='string'?id:teaching[teachIndex]?.id;if(!await requireCamera('practice'))return;practiceStart=performance.now();practiceEpoch++;phase='practice';bridge.resetEpoch();}\r\n  error=null;render();\r\n }\r\n const actions={\r\n  selectTracks:items=>{if(!Array.isArray(items))return;const chosen=items.map(t=>typeof t==='string'?allTracks.find(a=>a.id===t):t).filter(Boolean);if(chosen.length>5){fail(Error('Choose up to five tracks.'),'select');return;}selection={...selection,tracks:chosen};error=null;render();},\r\n  importTracks:async input=>{try{const list=Array.isArray(input)?input:[input];for(const opts of list){const t=await createLocalTrack(opts);allTracks.push(t);localUrls.add(t.audioUrl);if(selection.tracks.length<5)selection.tracks.push(t);}error=null;render();}catch(e){fail(e,'select');}},\r\n  chooseStyle:style=>{selection={...selection,style};render();},chooseIntensity:intensity=>{selection={...selection,intensity};render();},\r\n  calibrate:()=>{phase='calibrate';if(cameraReady){bridge.startCalibration();render();}else void camera();},practice,prepare,begin,pause,resume,restart,\r\n  replay:async()=>{if(selection.tracks.some(t=>t.source==='local'&&!localUrls.has(t.audioUrl))){phase='select';error='Choose your local file again to replay.';render();return;}await restart();},\r\n  back:()=>{dialog.close();window.myr5Routes?.home?.();},\r\n  spotify:()=>{dialog.close();void window.myr5Routes?.go('spotify');}\r\n };\r\n const ui=mountDance({root,catalogue,builtinTracks:allTracks,actions});\r\n const onVisibility=()=>{if(document.hidden){pause();window.myr5DanceCamera?.stop();cameraReady=false;playerLandmarks=null;}};\r\n const onOrientation=()=>{bridge.startCalibration();if(['playing','count_in','practice'].includes(phase))pause();error='Center your shoulders and hands again after rotating.';render();};\r\n let lastMetricSecond=-1;\r\n function tick(){\r\n  if(closed)return;clock=getClock();\r\n  feedback=bridge.snapshot();\r\n  if(resumeUntil){if(performance.now()>=resumeUntil){resumeUntil=0;phase='paused';if(!document.hidden&&cameraReady&&calibrated())void music.resume().then(()=>{if(closed)return;phase='playing';bridge.resetEpoch();render();}).catch(e=>fail(e,'paused'));}render();raf=requestAnimationFrame(tick);return;}\r\n  if(['count_in','playing'].includes(phase)){\r\n   if(clock.state==='ended'||clock.sessionSeconds>=plan.durationSeconds){finish();}\r\n   else if(clock.state==='paused'||clock.state==='suspended'){phase='paused';bridge.resetEpoch();}\r\n   else{phase=slot()?.role==='count_in'?'count_in':'playing';\r\n    if(performance.now()-latestFrameMs>1000){playerLandmarks=null;feedback={status:'tracking_limited',sequence:null,timing:null,effort:null,coverage:0,timingKind:'unavailable',correction:'Keep your shoulders, elbows and hands in the preview. Feet can stay off screen.',reasonCodes:['no_recent_pose']};}\r\n    const sec=Math.floor(clock.sessionSeconds);if(sec!==lastMetricSecond&&slot()?.scored){lastMetricSecond=sec;const fresh=feedback?.clockEpoch===clock.clockEpoch&&feedback?.moveId===slot().moveId&&feedback.acceptedSamples>lastObservedSamples&&feedback.status==='tracking';const coverage=Number.isFinite(feedback?.coverage)?Math.max(0,Math.min(1,feedback.coverage)):0;observations.push(fresh?{...feedback,coverage}:{sequence:null,timing:null,effort:null,coverage:0,timingKind:'unavailable'});lastObservedSamples=feedback?.acceptedSamples??lastObservedSamples;}\r\n    if(cameraReady&&feedback?.status==='tracking_limited'){limitedSince??=performance.now();if(performance.now()-limitedSince>8000){bridge.startCalibration();pause();error='Tracking paused. Center your shoulders and hands, then resume.';}}else limitedSince=null;\r\n   }\r\n  }\r\n  render();raf=requestAnimationFrame(tick);\r\n }\r\n function dispose(){if(closed)return;closed=true;run++;cancelAnimationFrame(raf);music?.dispose();bridge.dispose();window.myr5DanceCamera?.stop();window.removeEventListener('myr5:pose',pose);document.removeEventListener('visibilitychange',onVisibility);window.removeEventListener('orientationchange',onOrientation);window.removeEventListener('pagehide',dispose);ui.dispose();for(const u of localUrls)URL.revokeObjectURL(u);if(current?.dialog===dialog)current=null;setTimeout(()=>dialog.remove(),0);}\r\n window.addEventListener('myr5:pose',pose);document.addEventListener('visibilitychange',onVisibility);window.addEventListener('orientationchange',onOrientation);window.addEventListener('pagehide',dispose);dialog.addEventListener('close',dispose,{once:true});\r\n current={dialog,dispose};dialog.showModal();render();\r\n if(!window.myr5Creature){\r\n  const coachModuleUrl='/creature/assets/phone.js';\r\n  void import(coachModuleUrl).catch(()=>{if(!closed){error='Your saved coach could not load. Check your connection and reopen Dance.';render();}});\r\n }\r\n raf=requestAnimationFrame(tick);return dialog;\r\n}\r\n","modules/dance/coach-entry.mjs":"// Reuse the existing #dancePanel route contract. The floor shares Coach's origin\r\n// and saved avatar, while owning and releasing its camera/audio in one frame.\r\nimport {gameShellPhase} from '../../arcade/shell-phase.mjs';\r\nlet current=null;\r\nexport async function openDance(){\r\n if(current){if(!current.dialog.open)current.dialog.showModal();return current.ready;}\r\n const dialog=document.createElement('dialog');dialog.id='dancePanel';dialog.setAttribute('aria-label','Dance cardio');\r\n dialog.style.cssText='position:fixed;inset:0;margin:0;padding:0;border:0;background:#070b13;color:#fff;width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;overflow:hidden';\r\n const back=document.createElement('button');back.type='button';back.textContent='Back to Coach';back.style.cssText='position:absolute;z-index:3;top:8px;left:12px;min-height:44px;padding:8px 14px;background:#172536;color:#fff;border:1px solid #536476;border-radius:12px';\r\n const loading=document.createElement('p');loading.textContent='Opening your dance floor…';loading.setAttribute('role','status');loading.style.cssText='position:absolute;z-index:2;inset:0;margin:0;padding:85px 24px;background:#070b13';\r\n // A visible frame under the loading cover is intentional: Coach's shared\r\n // lifecycle suspends hidden iframes by replacing their source with about:blank.\r\n const frame=document.createElement('iframe');frame.title='Disco Coach dance floor';frame.allow='camera; microphone';frame.style.cssText='position:absolute;inset:0;width:100%;height:100%;border:0;display:block';\r\n const rim=document.createElement('div');rim.setAttribute('aria-hidden','true');rim.style.cssText='position:absolute;z-index:999;pointer-events:none;inset:0;border:4px solid #7455bd;border-radius:18px;transition:border-color .18s ease,box-shadow .25s ease';let rimTimer=0;\r\n const syncDock=()=>{const dock=dialog.querySelector('#coachDock'),height=dock?Math.ceil(dock.getBoundingClientRect().height):0;frame.style.height=`calc(100% - ${height}px)`;};\r\n const Observer=window.MutationObserver;const observer=Observer?new Observer(syncDock):null;observer?.observe(dialog,{childList:true});window.addEventListener('resize',syncDock);\r\n let settle;const ready=new Promise(resolve=>{settle=resolve;});const owner={dialog,frame,ready};current=owner;let closed=false;const shell=gameShellPhase('dance',dialog);\r\n const close=()=>{if(closed)return;shell.close();closed=true;clearTimeout(timer);clearTimeout(rimTimer);observer?.disconnect();window.removeEventListener('resize',syncDock);window.removeEventListener('message',message);frame.contentWindow?.postMessage({type:'myr5:dance-suspend'},location.origin);frame.remove();dialog.remove();if(current===owner)current=null;settle(null);};\r\n const message=event=>{if(closed||current!==owner||event.origin!==location.origin||event.source!==frame.contentWindow)return;if(event.data?.type==='myr5:dance-phase'){shell.phase(event.data.phase==='active'?'active':'paused');return;}if(event.data?.type==='myr5:dance-close'){dialog.close();return;}if(event.data?.type==='myr5:dance-grade'){const color={miss:'#ff5777',good:'#50f68c',great:'#ffd44d'}[event.data.grade];if(color){clearTimeout(rimTimer);rim.style.borderColor=color;rim.style.boxShadow=`inset 0 0 24px ${color}66`;rimTimer=setTimeout(()=>{rim.style.borderColor='#7455bd';rim.style.boxShadow='none';},260);}return;}if(event.data?.type==='myr5:dance-ready'){clearTimeout(timer);loading.remove();back.remove();shell.phase('paused');syncDock();settle(dialog);}};\r\n const timer=setTimeout(()=>{if(current===owner&&!closed){shell.phase('error');loading.textContent='The dance floor is taking longer to connect. Go back and try again.';}},8000);\r\n back.onclick=()=>dialog.close();dialog.addEventListener('close',close,{once:true});dialog.addEventListener('cancel',()=>dialog.close());window.addEventListener('message',message);\r\n dialog.append(loading,back,frame,rim);document.body.append(dialog);window.myr5Creature?.park?.();dialog.showModal();frame.src='/modules/dance/android-game/';syncDock();\r\n return ready;\r\n}\r\n","modules/dance/content/catalogue.mjs":"import { extractUpperBodyFeatures, UPPER_BODY_PROFILE, UPPER_BODY_LANDMARKS } from './features.mjs';\r\nexport { extractUpperBodyFeatures, UPPER_BODY_PROFILE, UPPER_BODY_LANDMARKS } from './features.mjs';\r\nexport const AUTHORED_TARGET_SCHEMA = 'myr5.authored-upper-body.v2';\r\nconst freeze = value => {\r\n  if (value && typeof value === 'object' && !Object.isFrozen(value)) { Object.freeze(value); Object.values(value).forEach(freeze); }\r\n  return value;\r\n};\r\nconst ARM_FEATURES = ['left_wrist_x', 'left_wrist_y', 'right_wrist_x', 'right_wrist_y', 'left_elbow_x', 'left_elbow_y', 'right_elbow_x', 'right_elbow_y'];\r\nconst definitions = [\r\n  { id: 'shoulder-groove', title: 'Shoulder Groove', cue: 'Lift and lower your shoulders twice; let both hands float with the groove.',\r\n    teachingSteps: ['Face the camera with both hands visible.', 'Lift shoulders and bend both elbows on 2.', 'Soften on 4; repeat on 6 and return on 8.'],\r\n    featureIds: ['root_y', 'left_wrist_y', 'right_wrist_y', 'left_elbow_y', 'right_elbow_y'], accent: 'root_y', polarity: -1, family: 'groove' },\r\n  { id: 'sway-reach', title: 'Sway & Reach', cue: 'Sway and reach left, return to center, then sway and reach right.',\r\n    teachingSteps: ['Start centered, hands below your shoulders.', 'Sway left as your left arm opens at shoulder height.', 'Return, then sway right and open your right arm.'], featureIds: ['root_x', ...ARM_FEATURES], accent: 'root_x', polarity: -1, family: 'groove' },\r\n  { id: 'alternating-disco-reach', intensity: 'steady', title: 'Disco Reach', cue: 'Reach your left hand diagonally up, lower it, then reach your right hand up.',\r\n    teachingSteps: ['Keep your chest facing the screen.', 'Point your left hand up and out on 2; lower on 4.', 'Point your right hand up and out on 6; lower on 8.'], featureIds: ARM_FEATURES, accent: 'left_wrist_y', polarity: -1, family: 'disco-inspired' },\r\n  { id: 'overhead-pump', intensity: 'steady', title: 'Overhead Pump', cue: 'Press both hands overhead, lower, and press overhead again.',\r\n    teachingSteps: ['Keep both hands and elbows inside the camera frame.', 'Raise both arms overhead on 2 and lower on 4.', 'Repeat on 6; use a comfortable range.'], featureIds: ARM_FEATURES, accent: 'left_wrist_y', polarity: -1, family: 'cardio' },\r\n  { id: 'crossbody-press', intensity: 'steady', title: 'Crossbody Press', cue: 'Press left across your chest, return, then press right across your chest.',\r\n    teachingSteps: ['Keep your shoulders facing the camera with a small comfortable twist.', 'Press your left hand across toward the right on 2; return on 4.', 'Press your right hand across toward the left on 6; return on 8.'], featureIds: ARM_FEATURES, accent: 'left_wrist_x', polarity: 1, family: 'cardio' },\r\n  { id: 'reach-forward', title: 'Forward Reach', cue: 'Bring both hands forward toward the camera, then draw your elbows back, twice.',\r\n    teachingSteps: ['Start with your elbows open and hands visible below shoulder height.', 'Reach forward with hands moving inward and up on screen; draw back on 4.', 'Repeat on 6. The camera checks the visible arm pattern, not forward distance.'], featureIds: ARM_FEATURES, accent: 'left_wrist_x', polarity: 1, family: 'cardio',\r\n    observationLimit: 'Monocular 2D arm projection proxy only; does not measure depth, travel distance or a full turn.' },\r\n  { id: 'neutral-bridge', title: 'Reset & Breathe', cue: 'Relax your arms, stay in frame, and watch the next move.', teachingSteps: ['Relax your shoulders.', 'Keep both hands visible.', 'Watch the next arm pattern.'], featureIds: [], family: 'transition' },\r\n  { id: 'rest', title: 'Rest', cue: 'Soften your shoulders and breathe.', teachingSteps: ['Relax your arms.', 'Take a comfortable breath.', 'Stay ready for the next move.'], featureIds: [], family: 'rest' },\r\n];\r\nconst landmarksForFeature = id => id.startsWith('left_wrist') ? [11,12,15] : id.startsWith('right_wrist') ? [11,12,16] : id.startsWith('left_elbow') ? [11,12,13] : id.startsWith('right_elbow') ? [11,12,14] : [11,12];\r\nconst CATALOGUE = freeze(definitions.map(d => ({\r\n  ...d, schemaVersion: AUTHORED_TARGET_SCHEMA, profile: UPPER_BODY_PROFILE, revision: '2026-10-17.upper-body.2',\r\n  beats: 8, tempoRangeBpm: [92,120], durationSecondsAtBpm: { 96: 5, 112: 30/7, 116: 120/29 }, intensity: d.intensity || 'easy',\r\n  families: [d.family, 'upper-body'], entry: 'neutral', exit: 'neutral', requiredLandmarks: [...UPPER_BODY_LANDMARKS], optionalLandmarks: [0,2,5,7,8],\r\n  confidence: { visibility: .6, presence: .6 },\r\n  features: d.featureIds.map(id => ({ id, kind: id, landmarks: landmarksForFeature(id), side: id.startsWith('left') ? 'left' : id.startsWith('right') ? 'right' : 'both', weight: 1,\r\n    parameters: { unit: 'shoulder_width', phase: {start:0,end:1}, baseline: sampleTarget(d.id, 0).features[id],\r\n      amplitude: Math.max(...Array.from({length:41}, (_, i) => Math.abs(sampleTarget(d.id, i/40).features[id] - sampleTarget(d.id, 0).features[id]))),\r\n      tolerance: id.startsWith('root') ? .09 : .16, waveform: 'authored_upper_body_loop' } })),\r\n  accents: !d.accent ? [] : d.id === 'sway-reach' ? [{ beat: 2, featureId: 'root_x', polarity: -1 }, { beat: 6, featureId: 'root_x', polarity: 1 }]\r\n    : d.id.includes('alternating') || d.id === 'crossbody-press' ? [{beat:2,featureId:d.accent,polarity:d.polarity}, {beat:6,featureId:d.accent.replace('left','right'),polarity:d.id === 'crossbody-press' ? -1 : d.polarity}]\r\n    : [2,6].map(beat => ({beat,featureId:d.accent,polarity:d.polarity})),\r\n  provenance: { kind: 'authored', creator: 'MYR5 studio', description: 'Original digital choreography targets; authored upper-body references, not captured human observations.' }\r\n})));\r\nfunction point(x,y) { return {x,y,z:0,visibility:1}; }\r\nexport function getCatalogue() { return CATALOGUE; }\r\nexport function sampleTarget(moveId, phase01) {\r\n  if (!definitions.some(t => t.id === moveId)) throw new RangeError(`Unknown upper-body dance target: ${moveId}`);\r\n  const t = Number.isFinite(phase01) ? Math.max(0,Math.min(1,phase01)) % 1 : 0;\r\n  const wave = Math.sin(2*Math.PI*t), l = Math.max(0,wave), r = Math.max(0,-wave), both = wave*wave;\r\n  const landmarks = Array(33).fill(null);\r\n  Object.assign(landmarks, {0:point(0,-.65),2:point(-.1,-.7),5:point(.1,-.7),7:point(-.2,-.6),8:point(.2,-.6),\r\n    11:point(-.5,0),12:point(.5,0),13:point(-.58,.32),14:point(.58,.32),15:point(-.62,.65),16:point(.62,.65)});\r\n  const setArm = (side, extent, wristX, wristY, elbowX, elbowY) => {\r\n    const sign = side === 'left' ? -1 : 1, e = side === 'left' ? 13 : 14, w = e+2;\r\n    landmarks[w] = point(sign*(.62+(wristX-.62)*extent), .65+(wristY-.65)*extent);\r\n    landmarks[e] = point(sign*(.58+(elbowX-.58)*extent), .32+(elbowY-.32)*extent);\r\n  };\r\n  if (moveId === 'shoulder-groove') {\r\n    setArm('left',both,.62,.38,.58,.15); setArm('right',both,.62,.38,.58,.15);\r\n    for (const p of landmarks) if (p) p.y -= .12*both;\r\n  } else if (moveId === 'sway-reach') {\r\n    setArm('left',l,1.25,0,.9,.03); setArm('right',r,1.25,0,.9,.03);\r\n    for (const p of landmarks) if (p) p.x -= .2*wave;\r\n  } else if (moveId === 'alternating-disco-reach') {\r\n    setArm('left',l,.95,-.8,.75,-.35); setArm('right',r,.95,-.8,.75,-.35);\r\n  } else if (moveId === 'overhead-pump') {\r\n    setArm('left',both,.48,-.9,.72,-.4); setArm('right',both,.48,-.9,.72,-.4);\r\n  } else if (moveId === 'crossbody-press') {\r\n    setArm('left',l,-.55,.03,.08,.15); setArm('right',r,-.55,.03,.08,.15);\r\n  } else if (moveId === 'reach-forward') {\r\n    setArm('left',both,.2,.05,.36,.2); setArm('right',both,.2,.05,.36,.2);\r\n  }\r\n  return { landmarks, features: extractUpperBodyFeatures(landmarks), profile: UPPER_BODY_PROFILE };\r\n}\r\nexport function validateCatalogue(catalogue = CATALOGUE) {\r\n  const errors=[];\r\n  if (!Array.isArray(catalogue)) return {ok:false,errors:['catalogue must be an array']};\r\n  const ids=new Set();\r\n  for (const t of catalogue) {\r\n    if (!t || typeof t !== 'object') {errors.push('malformed target');continue;}\r\n    if (ids.has(t.id)) errors.push(`duplicate target id: ${t.id}`); ids.add(t.id);\r\n    if (!CATALOGUE.some(x=>x.id===t.id)) {errors.push(`unknown sampler target: ${t.id}`);continue;}\r\n    if (t.schemaVersion!==AUTHORED_TARGET_SCHEMA || t.profile!==UPPER_BODY_PROFILE) errors.push(`${t.id}: incorrect upper-body schema/profile`);\r\n    if (!t.title || !t.cue || !t.teachingSteps?.length) errors.push(`${t.id}: missing authored copy`);\r\n    if (t.entry!=='neutral'||t.exit!=='neutral') errors.push(`${t.id}: seams must be neutral`);\r\n    if (t.provenance?.kind!=='authored') errors.push(`${t.id}: authored provenance required`);\r\n    if (![4,8].includes(t.beats)) errors.push(`${t.id}: invalid beats`);\r\n    if (!Array.isArray(t.tempoRangeBpm)||t.tempoRangeBpm.length!==2||!t.tempoRangeBpm.every(Number.isFinite)||t.tempoRangeBpm[0]>t.tempoRangeBpm[1]) errors.push(`${t.id}: invalid tempo range`);\r\n    if (!Array.isArray(t.requiredLandmarks)||UPPER_BODY_LANDMARKS.some(i=>!t.requiredLandmarks.includes(i))||t.requiredLandmarks.some(i=>!UPPER_BODY_LANDMARKS.includes(i))) errors.push(`${t.id}: upper-body landmarks required; lower body forbidden`);\r\n    if (!Array.isArray(t.features)) {errors.push(`${t.id}: features must be an array`);continue;}\r\n    for (const f of t.features) {\r\n      if (!f || f.kind!==f.id || !landmarksForFeature(f.id).every(i=>f.landmarks?.includes(i)) || !Number.isFinite(f.weight)||f.weight<=0||!(f.parameters?.tolerance>0)||!Number.isFinite(f.parameters?.baseline)||!Number.isFinite(f.parameters?.amplitude)||f.parameters?.unit!=='shoulder_width') errors.push(`${t.id}: malformed feature`);\r\n      if (f.landmarks?.some(i=>!t.requiredLandmarks?.includes(i))) errors.push(`${t.id}: feature uses undeclared landmark`);\r\n      if (!Number.isFinite(sampleTarget(t.id,.25).features[f.id])) errors.push(`${t.id}: unobservable feature`);\r\n    }\r\n    if (!Array.isArray(t.accents)) errors.push(`${t.id}: accents must be an array`);\r\n    else for (const a of t.accents) {\r\n      if (!(a.beat>=1&&a.beat<=t.beats)||![-1,1].includes(a.polarity)||!t.features.some(f=>f.id===a.featureId)) {errors.push(`${t.id}: invalid accent`);continue;}\r\n      const phase=a.beat/t.beats, v=sampleTarget(t.id,phase).features[a.featureId];\r\n      for (const delta of [-.002,.002]) if (a.polarity*(v-sampleTarget(t.id,phase+delta).features[a.featureId]) < -1e-8) errors.push(`${t.id}: accent not at feature extremum`);\r\n    }\r\n    if (JSON.stringify(sampleTarget(t.id,0))!==JSON.stringify(sampleTarget(t.id,1))) errors.push(`${t.id}: loop seam mismatch`);\r\n  }\r\n  for (const t of CATALOGUE) if (!ids.has(t.id)) errors.push(`missing required target: ${t.id}`);\r\n  return {ok:!errors.length,errors};\r\n}\r\n","modules/dance/content/features.mjs":"/** Coordinates use shoulder-width units, shoulder-mid calibration origin, anatomical sides.\r\n * This same observation function produces authored and player features. No depth inference. */\r\nexport const UPPER_BODY_PROFILE = 'waist-up-shoulder-width.v1';\r\nexport const UPPER_BODY_LANDMARKS = Object.freeze([11, 12, 13, 14, 15, 16]);\r\nexport function extractUpperBodyFeatures(landmarks) {\r\n  const left = landmarks[11], right = landmarks[12];\r\n  if (!left || !right) return {};\r\n  const root = { x: (left.x + right.x) / 2, y: (left.y + right.y) / 2 };\r\n  const values = { root_x: root.x, root_y: root.y, shoulder_tilt: right.y - left.y };\r\n  for (const [side, elbow, wrist] of [['left', 13, 15], ['right', 14, 16]]) {\r\n    for (const [joint, index] of [['elbow', elbow], ['wrist', wrist]]) {\r\n      for (const axis of ['x', 'y']) values[`${side}_${joint}_${axis}`] = landmarks[index] ? landmarks[index][axis] - root[axis] : null;\r\n    }\r\n  }\r\n  return values;\r\n}\r\n","modules/dance/content/index.mjs":"export {\r\n  AUTHORED_TARGET_SCHEMA,\r\n  UPPER_BODY_PROFILE,\r\n  UPPER_BODY_LANDMARKS,\r\n  extractUpperBodyFeatures,\r\n  getCatalogue,\r\n  sampleTarget,\r\n  validateCatalogue,\r\n} from './catalogue.mjs';\r\n","modules/dance/contracts.mjs":"// Frozen production boundary. See release architecture for feature semantics.\r\nexport const CONTRACT_REVISION = 'myr5.dance.20261017.1';\r\nexport const PHASES = Object.freeze(['select','calibrate','teach','practice','preparing','ready','count_in','playing','paused','results']);\r\nexport const authoredTargetSchema = 'myr5.authored-target.v1';\r\nexport const sessionSchema = 'myr5.dance-session.v1';\r\nexport function activeSlotAt(plan, seconds) {\r\n  return plan?.slots.find(s => seconds >= s.startSessionSeconds && seconds < s.endSessionSeconds) ?? null;\r\n}\r\nexport function cyclePhase(slot, seconds) {\r\n  if (!slot) return 0;\r\n  const n = (seconds-slot.startSessionSeconds)/(slot.endSessionSeconds-slot.startSessionSeconds)*(slot.repeatCount||1);\r\n  return ((n%1)+1)%1;\r\n}\r\n","modules/dance/index.mjs":"export {openDance} from './coach-entry.mjs';\r\nexport {openDance as openClassicDance} from './classic.mjs';\r\n","modules/dance/music-lab/capture.mjs":"// Browser microphone only: this cannot access another app's internal audio.\r\nexport function createMicrophoneCapture({onPCM,onState=()=>{},mediaDevices=globalThis.navigator?.mediaDevices,AudioContext=globalThis.AudioContext,WorkletNode=globalThis.AudioWorkletNode,now=()=>performance.now()}={}){\r\n  let generation=0,stream=null,context=null,source=null,worklet=null,state='idle';\r\n  const emit=(next,message)=>{state=next;onState({state,message});};\r\n  async function stop(reason='Stopped'){\r\n    generation++;\r\n    const oldContext=context;context=null;\r\n    if(worklet){worklet.port.onmessage=null;worklet.disconnect();worklet=null;}\r\n    source?.disconnect();source=null;\r\n    stream?.getTracks().forEach(t=>t.stop());stream=null;\r\n    emit('idle',reason);\r\n    try{await oldContext?.close();}catch{}\r\n  }\r\n  async function start(){\r\n    if(state==='starting'||state==='listening')return;\r\n    if(!mediaDevices?.getUserMedia||!AudioContext||!WorkletNode){emit('error','Microphone analysis needs a recent browser over HTTPS.');return;}\r\n    const ticket=++generation;emit('starting','Allow microphone access');\r\n    // Create/resume the context on the button gesture, before the permission await.\r\n    let pendingContext,pendingStream;\r\n    try{\r\n      pendingContext=new AudioContext({latencyHint:'interactive'});context=pendingContext;\r\n      await pendingContext.resume();\r\n      if(ticket!==generation)return;\r\n      pendingStream=await mediaDevices.getUserMedia({audio:{echoCancellation:false,noiseSuppression:false,autoGainControl:false},video:false});\r\n      if(ticket!==generation){pendingStream.getTracks().forEach(t=>t.stop());await pendingContext.close().catch(()=>{});return;}\r\n      stream=pendingStream;\r\n      await pendingContext.audioWorklet.addModule(new URL('./pcm-worklet.mjs',import.meta.url));\r\n      if(ticket!==generation)return;\r\n      source=pendingContext.createMediaStreamSource(stream);\r\n      worklet=new WorkletNode(pendingContext,'dance-pcm',{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]});\r\n      let anchorMs=null,lastTimestamp=-Infinity;\r\n      worklet.port.onmessage=event=>{\r\n        if(ticket!==generation||state!=='listening')return;\r\n        const {samples,sampleRate,endTime}=event.data;\r\n        const receivedAt=now();anchorMs??=receivedAt-endTime*1000;\r\n        const timestamp=Math.min(receivedAt,anchorMs+endTime*1000);\r\n        if(timestamp<=lastTimestamp)return;\r\n        lastTimestamp=timestamp;\r\n        try{onPCM?.(samples,sampleRate,timestamp);}catch{void stop('Audio timing interrupted. Tap Start listening to retry.');}\r\n      };\r\n      source.connect(worklet);worklet.connect(pendingContext.destination);\r\n      for(const track of stream.getTracks())track.onended=()=>{if(ticket===generation)void stop('Microphone disconnected');};\r\n      pendingContext.onstatechange=()=>{if(ticket===generation&&pendingContext.state==='suspended')void stop('Audio paused. Tap Start listening to reconnect.');};\r\n      emit('listening','Listening on this device');\r\n    }catch(error){\r\n      if(ticket!==generation)return;\r\n      await stop();emit('error',error?.name==='NotAllowedError'?'Microphone blocked. Allow it in browser settings, then retry.':error?.message||'Microphone could not start.');\r\n    }\r\n  }\r\n  return {start,stop,get state(){return state;}};\r\n}\r\n","modules/dance/music-lab/index.html":"<!doctype html>\r\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><title>MYR5 · Music timing test</title><link rel=\"stylesheet\" href=\"./lab.css\"></head>\r\n<body><main>\r\n<header><a href=\"https://myr5.mominc.online/pose#dance\">← Dance</a><span>ANDROID FIRST · EXPERIMENT</span></header>\r\n<h1>Find the groove.</h1><p class=\"intro\">Spotify plays. Your coach leads. Move your way.</p>\r\n<section class=\"card\"><h2>1. Android dance floor</h2><p>24 named dance demonstrations, forgiving arm-movement points and a shake-out break between songs. The Android app reads your selected player’s song and playback clock. It leaves the music and DJ transitions to Spotify.</p><a class=\"button\" id=\"apk\" href=\"/downloads/myr5-dance-companion.apk\" download>Download Android dance update</a><p class=\"note\" id=\"apk-note\">Version 2 debug build · Android 8 or newer. Install over your existing MYR5 Dance Session app. Select Spotify, tap <b>Open dance floor</b>, then enable camera/mic in the Android toolbar. In the game, start the camera and choose <b>Hear the beat</b> or tap four steady beats. Microphone tempo is estimated; headphones need tap tempo.</p><p><a href=\"/modules/dance/android-game/\">Preview the dance floor in your browser →</a></p>\r\n<details><summary>What to check on Android</summary><ol><li>Install and open <b>MYR5 Dance Session</b>. Enable its notification access only if you choose to grant that broad Android permission. The app does not inspect notification contents.</li><li>Start Spotify, play a song, return to the app, and choose <b>com.spotify.music</b>.</li><li>Tap <b>Open dance floor</b>. Use the Android toolbar to allow camera/mic, then start the camera in the game. Keep shoulders and at least one arm in view; feet are not needed.</li><li>Choose <b>Hear the beat</b> for a nearby speaker, or tap four steady beats. Press <b>Let’s go</b>. Visible arm movement earns activity points without exact pose matching.</li><li>Use the notification shade or split screen to pause, resume and skip songs. Skips should start an unscored celebration and shake-out break, then prepare a fresh routine.</li><li>Switching to another app disconnects the floor. Select the player again when returning. If Android restricts notification access for the sideloaded app, its app-info menu may offer <b>Allow restricted settings</b>; that decision stays with you.</li></ol><p>The app uses player-reported timing and a separately estimated beat. Finger poses and optional turns are visual cues; turns are unscored. This Android update still needs your device check.</p></details></section>\r\n<section class=\"card\"><h2>2. Microphone rhythm</h2><p>Play music on a nearby speaker, then listen. This measures audible pulses—not the song title, exact song position or the first beat of a bar. Headphones won’t reach the microphone.</p>\r\n<div class=\"actions\"><button id=\"start\">Start listening</button><button id=\"stop\" disabled>Stop</button></div>\r\n<p id=\"status\" role=\"status\" aria-live=\"polite\">Microphone off</p>\r\n<div class=\"metrics\"><div><strong id=\"tempo\">—</strong><span>estimated BPM</span></div><div><strong id=\"confidence\">—</strong><span>pulse confidence</span></div><div><strong id=\"level\">—</strong><span>input level</span></div></div>\r\n<div class=\"floor\"><canvas id=\"figure\" width=\"600\" height=\"560\" aria-label=\"Upper-body movement preview driven only by a confident detected pulse\"></canvas><div class=\"pulse\" id=\"pulse\"></div><b id=\"move\">Waiting for a steady pulse</b></div>\r\n<p class=\"note\">Movement freezes when the pulse is missing, uncertain, or outside this move’s 92–120 BPM range. A detected pulse can be half or double the musical tempo. Confidence measures pulse consistency, not a probability that the musical tempo is correct. This test does not score your dancing.</p>\r\n<details><summary>Test against a known beat</summary><p>Play this original 116 BPM MYR5 track on another speaker. Listen to the speaker through the microphone; the player below is not connected to the detector.</p><audio controls preload=\"none\" src=\"../assets/audio/neon-step-pop.wav\"></audio></details>\r\n</section>\r\n<section class=\"card\"><h2>3. Camera + microphone check</h2><p>While listening, enable the camera to check that your Android browser can keep both running.</p><button id=\"camera\">Enable camera preview</button><video id=\"video\" autoplay muted playsinline hidden></video><p id=\"camera-status\" role=\"status\">Camera off</p></section>\r\n<section class=\"card\"><h2>What stays on your phone</h2><p>Microphone samples and camera frames stay in memory on this device. This page sends no recordings or music metadata to a server. Hiding this page stops the microphone and camera; tap the buttons again to resume.</p><button id=\"report\">Copy test report</button><pre id=\"report-text\" hidden></pre><p class=\"note\">The report contains browser details and timing diagnostics, not recordings or song titles. <a href=\"/source.json\">Source code</a> · <a href=\"/LICENSE\">License</a></p></section>\r\n</main><script type=\"module\" src=\"./lab.mjs\"></script></body></html>\r\n","modules/dance/music-lab/lab.css":":root{color-scheme:dark;font-family:system-ui,sans-serif;color:#f9fbff;background:#080c16}*{box-sizing:border-box}body{margin:0}main{max-width:720px;margin:auto;padding:24px 16px calc(30px + env(safe-area-inset-bottom))}header{display:flex;justify-content:space-between;align-items:center;gap:16px;font-size:11px;letter-spacing:.08em}a{color:#8cecf0}h1{font-size:clamp(42px,10vw,68px);line-height:1;letter-spacing:-.055em;margin:32px 0 12px}h2{font-size:21px;margin:0 0 10px}p{line-height:1.55}.intro{color:#c1cadb;font-size:18px;margin-bottom:28px}.card{border:1px solid #354257;background:#111b2a;border-radius:20px;padding:20px;margin:16px 0}.note{font-size:13px;color:#b7c6d8}.actions{display:flex;gap:10px;flex-wrap:wrap}button,.button{display:inline-flex;justify-content:center;align-items:center;min-height:50px;border:0;border-radius:12px;padding:12px 18px;font:700 16px system-ui;color:#08121a;background:#9af2ec;text-decoration:none;cursor:pointer}button:disabled{opacity:.45;cursor:default}button:focus-visible,a:focus-visible,summary:focus-visible{outline:3px solid #ffe772;outline-offset:4px}#stop,#report,#camera{background:#d8e3f6}.metrics{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:22px 0}.metrics strong{display:block;font-size:30px;font-variant-numeric:tabular-nums}.metrics span{display:block;font-size:12px;color:#c1cadb}.floor{position:relative;overflow:hidden;border-radius:16px;background:radial-gradient(ellipse at top,#214752,#09121f 70%);min-height:290px;display:grid;place-items:center}.floor canvas{width:100%;height:300px;display:block}.floor b{font-size:20px;position:absolute;bottom:16px;left:12px;right:12px;text-align:center;color:#fff8b9}.pulse{position:absolute;right:16px;top:16px;width:26px;height:26px;border:3px solid #ffe772;border-radius:50%;background:#ffe772;opacity:.2}video{display:block;width:100%;max-height:280px;object-fit:contain;margin-top:15px;border-radius:12px;transform:scaleX(-1)}[hidden]{display:none!important}audio{max-width:100%;width:100%}pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;background:#080c16;padding:14px;border-radius:12px}summary{cursor:pointer;min-height:44px;padding:10px 0}#status{min-height:48px;font-weight:600}@media(prefers-reduced-motion:reduce){.pulse{transform:none!important}}\r\n","modules/dance/music-lab/lab.mjs":"import {createBeatProbe} from '../music/beat-probe.mjs';\r\nimport {createMicrophoneCapture} from './capture.mjs';\r\nimport {sampleTarget} from '../content/index.mjs';\r\nimport {getMoveViewport,projectMoveCoach,drawFigure} from '../ui/skeleton.mjs';\r\nconst $=id=>document.getElementById(id),probe=createBeatProbe();\r\nlet frames=0,lastPCM=null,lastSnapshot={},cameraStream=null,cameraGeneration=0,raf=0,drawPhase=0;\r\nconst capture=createMicrophoneCapture({onPCM:(samples,rate,at)=>{probe.push(samples,rate,at);frames++;lastPCM=at;},onState:({state,message})=>{\r\n  $('status').textContent=message;$('start').disabled=['starting','listening'].includes(state);$('stop').disabled=!['starting','listening'].includes(state);\r\n  if(state!=='listening'){probe.reset();lastPCM=null;}\r\n}});\r\n$('start').addEventListener('click',()=>{probe.reset();void capture.start();});\r\n$('stop').addEventListener('click',()=>void capture.stop());\r\nfunction stopCamera(message='Camera off'){\r\n  cameraGeneration++;cameraStream?.getTracks().forEach(t=>t.stop());cameraStream=null;$('video').srcObject=null;$('video').hidden=true;$('camera').disabled=false;$('camera').textContent='Enable camera preview';$('camera-status').textContent=message;\r\n}\r\n$('camera').addEventListener('click',async()=>{\r\n  if(cameraStream){stopCamera();return;}\r\n  const ticket=++cameraGeneration;$('camera').disabled=true;$('camera-status').textContent='Allow camera access';\r\n  try{\r\n    const stream=await navigator.mediaDevices.getUserMedia({video:{facingMode:'user',width:{ideal:640},height:{ideal:480}},audio:false});\r\n    if(ticket!==cameraGeneration){stream.getTracks().forEach(t=>t.stop());return;}\r\n    cameraStream=stream;$('video').srcObject=stream;$('video').hidden=false;\r\n    await $('video').play();if(ticket!==cameraGeneration)return;\r\n    for(const track of stream.getTracks())track.onended=()=>{if(ticket===cameraGeneration)stopCamera('Camera disconnected');};\r\n    $('camera').disabled=false;$('camera').textContent='Stop camera preview';$('camera-status').textContent='Camera running · check your microphone still hears music';\r\n  }catch(e){if(ticket===cameraGeneration)stopCamera(e.name==='NotAllowedError'?'Camera blocked. Allow it in browser settings and retry.':'Camera could not start. Tap to retry.');}\r\n});\r\nconst move='alternating-disco-reach',viewport=getMoveViewport(move,sampleTarget),canvas=$('figure'),ctx=canvas.getContext('2d');\r\nfunction paint(){\r\n  // RAF's argument is the frame start and can precede a PCM message delivered this frame.\r\n  const now=performance.now();\r\n  const s=probe.snapshot(now);lastSnapshot=s;\r\n  $('tempo').textContent=s.available?Math.round(s.tempoBpm):'—';$('confidence').textContent=s.available?Math.round(s.confidence*100)+'%':'—';\r\n  const level=lastPCM!==null&&now-lastPCM<500?Math.min(100,Math.round(s.rms*500)):0;$('level').textContent=level+'%';\r\n  if(capture.state==='listening')$('status').textContent=s.available?(s.preferredTempo?'Pulse found · estimated tempo':'Pulse found · move preview needs 92–120 BPM'):level>1?'Listening · finding a steady pulse':'Listening · play music on a nearby speaker';\r\n  // The probe's phase has no bar/downbeat. Accumulate a continuous inferred pulse clock below.\r\n  updateMoveClock(now,{...s,available:s.available&&s.preferredTempo});\r\n  ctx.clearRect(0,0,canvas.width,canvas.height);\r\n  drawFigure(ctx,projectMoveCoach(sampleTarget(move,drawPhase).landmarks,{x:0,y:0,w:600,h:520},false,viewport),{lineWidth:14,alpha:s.available&&s.preferredTempo?1:.35,arrows:true});\r\n  $('pulse').style.opacity=s.available?String(.25+.3*(1+Math.cos(s.phase*Math.PI*2))):'.2';\r\n  $('move').textContent=s.available?(s.preferredTempo?'Disco reach · follow the pulse':'Preview supports 92–120 BPM'):'Waiting for a steady pulse';\r\n  raf=requestAnimationFrame(paint);\r\n}\r\nlet previousAt=null,beatCount=0;\r\nfunction updateMoveClock(now,s){\r\n  if(!s.available){previousAt=null;return;}\r\n  if(previousAt!==null){const elapsed=Math.max(0,Math.min(100,now-previousAt));beatCount+=elapsed/s.beatPeriodMs;}\r\n  // Correct local phase gently. Never call a guessed pulse a musical downbeat.\r\n  const fraction=beatCount-Math.floor(beatCount),error=((s.phase-fraction+.5+1)%1)-.5;\r\n  beatCount+=Math.max(-.015,Math.min(.015,error*.08));\r\n  drawPhase=((beatCount%8)+8)%8/8;previousAt=now;\r\n}\r\nraf=requestAnimationFrame(paint);\r\n$('report').addEventListener('click',async()=>{\r\n  const s=lastSnapshot,report={version:'dance-music-lab.v1',userAgent:navigator.userAgent,microphone:capture.state,camera:!!cameraStream,blocks:frames,ageMs:lastPCM===null?null:Math.round(performance.now()-lastPCM),pulseAvailable:s.available,tempoBpm:s.tempoBpm,confidence:s.confidence,reason:s.reason,limitations:['No song identification in browser','No internal player audio access','No exact song position or downbeat']};\r\n  const text=JSON.stringify(report,null,2);$('report-text').hidden=false;$('report-text').textContent=text;\r\n  try{await navigator.clipboard.writeText(text);$('report').textContent='Report copied';}catch{$('report').textContent='Report shown below';}\r\n});\r\nfunction stopAll(){void capture.stop('Paused while this page is hidden. Tap Start listening to resume.');stopCamera();}\r\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)stopAll();});\r\nwindow.addEventListener('pagehide',()=>{stopAll();cancelAnimationFrame(raf);});\r\n","modules/dance/music-lab/pcm-worklet.mjs":"// Only short PCM blocks cross this local worklet port. Nothing is recorded or uploaded.\r\nclass DancePCM extends AudioWorkletProcessor {\r\n  constructor(){super();this.samples=new Float32Array(1024);this.used=0;}\r\n  process(inputs,outputs){\r\n    for(const output of outputs)for(const channel of output)channel.fill(0);\r\n    const input=inputs[0]?.[0];if(!input)return true;\r\n    for(let i=0;i<input.length;i++){\r\n      this.samples[this.used++]=input[i];\r\n      if(this.used===this.samples.length){\r\n        const samples=this.samples;\r\n        this.port.postMessage({samples,endTime:currentTime+(i+1)/sampleRate,sampleRate},[samples.buffer]);\r\n        this.samples=new Float32Array(1024);this.used=0;\r\n      }\r\n    }\r\n    return true;\r\n  }\r\n}\r\nregisterProcessor('dance-pcm',DancePCM);\r\n","modules/dance/music/beat-probe.mjs":"/**\r\n * Local, transient PCM pulse estimator. No audio is retained, recorded or sent.\r\n * Algorithm background: energy-envelope onset detection and onset periodicity,\r\n * as described by E. Scheirer, “Tempo and beat analysis of acoustic musical\r\n * signals”, JASA 103 (1998). This is a small original implementation, not a port\r\n * of that paper's filterbank or a dependency. It deliberately cannot identify a\r\n * song, song position, meter or downbeat. Repeated speech can resemble a pulse.\r\n * Expect at least six clear attacks (~3–5 seconds); phase is an uncertain local\r\n * pulse prediction, not an authored beat grid. Half/double tempo remains ambiguous.\r\n */\r\nconst median = (values) => {\r\n  const ordered = [...values].sort((a, b) => a - b);\r\n  const middle = Math.floor(ordered.length / 2);\r\n  return ordered.length % 2 ? ordered[middle] : (ordered[middle - 1] + ordered[middle]) / 2;\r\n};\r\nconst modulo = (x, y) => ((x % y) + y) % y;\r\n\r\nexport class BeatProbe {\r\n  constructor({ staleAfterMs = 1000, minBpm = 70, maxBpm = 180 } = {}) {\r\n    if (!(staleAfterMs > 0 && minBpm >= 40 && maxBpm > minBpm && maxBpm <= 240)) throw new RangeError('Invalid beat probe bounds');\r\n    Object.assign(this, { staleAfterMs, minBpm, maxBpm });\r\n    this.reset();\r\n  }\r\n\r\n  reset() {\r\n    this.lastObservedAtMs = null;\r\n    this.sampleRate = null;\r\n    this.rms = 0;\r\n    this.peak = 0;\r\n    this.onsets = [];\r\n    this.lastSignalAtMs = -Infinity;\r\n    this.frameSum = 0;\r\n    this.frameCount = 0;\r\n    this.previousEnergy = 0;\r\n    this.baseline = 0;\r\n    this.latestOnset = false;\r\n    this.estimate = null;\r\n  }\r\n\r\n  /** observedAtMs is the monotonic time of the END of this contiguous PCM block. */\r\n  push(samples, sampleRate, observedAtMs) {\r\n    if (!samples || !Number.isInteger(samples.length) || samples.length === 0) throw new TypeError('A non-empty PCM block is required');\r\n    if (!(Number.isFinite(sampleRate) && sampleRate >= 8000 && sampleRate <= 192000)) throw new RangeError('Invalid PCM sample rate');\r\n    if (!Number.isFinite(observedAtMs) || (this.lastObservedAtMs !== null && observedAtMs <= this.lastObservedAtMs)) throw new RangeError('PCM timestamps must increase monotonically');\r\n    for (const value of samples) if (!Number.isFinite(value)) throw new TypeError('PCM samples must be finite');\r\n    if ((this.lastObservedAtMs !== null && observedAtMs - this.lastObservedAtMs > this.staleAfterMs) || (this.sampleRate !== null && sampleRate !== this.sampleRate)) this.reset();\r\n    this.sampleRate = sampleRate;\r\n    this.latestOnset = false;\r\n    let sum = 0;\r\n    let peak = 0;\r\n    const frameSize = Math.round(sampleRate * 0.01);\r\n    for (let i = 0; i < samples.length; i++) {\r\n      const value = samples[i];\r\n      sum += value * value;\r\n      peak = Math.max(peak, Math.abs(value));\r\n      this.frameSum += value * value;\r\n      this.frameCount++;\r\n      if (this.frameCount >= frameSize) {\r\n        const atMs = observedAtMs - (samples.length - i - 1) * 1000 / sampleRate;\r\n        this.consumeFrame(Math.sqrt(this.frameSum / this.frameCount), atMs);\r\n        this.frameSum = 0;\r\n        this.frameCount = 0;\r\n      }\r\n    }\r\n    this.rms = Math.sqrt(sum / samples.length);\r\n    this.peak = peak;\r\n    this.lastObservedAtMs = observedAtMs;\r\n    this.onsets = this.onsets.filter(({ atMs }) => observedAtMs - atMs < 10000);\r\n    return this.snapshot(observedAtMs);\r\n  }\r\n\r\n  consumeFrame(energy, atMs) {\r\n    if (energy > 0.004) this.lastSignalAtMs = atMs;\r\n    const flux = Math.max(0, energy - this.previousEnergy);\r\n    const last = this.onsets.at(-1)?.atMs ?? -Infinity;\r\n    if (energy > 0.012 && energy > this.baseline * 1.65 && flux > Math.max(0.009, this.baseline * 0.65) && atMs - last >= 220) {\r\n      this.onsets.push({ atMs, strength: Math.min(1, flux) });\r\n      this.latestOnset = true;\r\n      this.estimate = this.estimatePulse();\r\n    }\r\n    this.baseline += (energy - this.baseline) * 0.035;\r\n    this.previousEnergy = energy;\r\n  }\r\n\r\n  estimatePulse() {\r\n    const attacks = this.onsets.slice(-9);\r\n    if (attacks.length < 6) return null;\r\n    const intervals = attacks.slice(1).map((attack, i) => attack.atMs - attacks[i].atMs);\r\n    const period = median(intervals);\r\n    const bpm = 60000 / period;\r\n    // Ten-millisecond envelope quantization can straddle a configured boundary.\r\n    if (bpm < this.minBpm - 1 || bpm > this.maxBpm + 1) return null;\r\n    const matching = intervals.filter((value) => Math.abs(value / period - 1) <= 0.10);\r\n    // Recent coherence prevents an old tempo surviving a transition.\r\n    if (matching.length / intervals.length < 0.8 || intervals.slice(-3).some((value) => Math.abs(value / period - 1) > 0.12)) return null;\r\n    const refinedPeriod = matching.reduce((sum, value) => sum + value, 0) / matching.length;\r\n    const recent = attacks.slice(-6);\r\n    const anchor = recent.at(-1).atMs;\r\n    // Circular phase accumulation: coherent onsets must agree on one pulse phase.\r\n    const angles = recent.map(({ atMs }) => 2 * Math.PI * (atMs - anchor) / refinedPeriod);\r\n    const x = angles.reduce((sum, angle) => sum + Math.cos(angle), 0) / angles.length;\r\n    const y = angles.reduce((sum, angle) => sum + Math.sin(angle), 0) / angles.length;\r\n    const concentration = Math.hypot(x, y);\r\n    if (concentration < 0.9) return null;\r\n    const phaseAnchorMs = anchor + Math.atan2(y, x) * refinedPeriod / (2 * Math.PI);\r\n    return { period: refinedPeriod, anchor: phaseAnchorMs, confidence: Math.min(0.94, concentration * matching.length / intervals.length * 0.94) };\r\n  }\r\n\r\n  snapshot(nowMs) {\r\n    if (!Number.isFinite(nowMs) || (this.lastObservedAtMs !== null && nowMs < this.lastObservedAtMs)) throw new RangeError('Snapshot time must be at or after the latest PCM timestamp');\r\n    const ageMs = this.lastObservedAtMs === null ? Infinity : nowMs - this.lastObservedAtMs;\r\n    const lastOnsetAtMs = this.onsets.at(-1)?.atMs ?? null;\r\n    const period = this.estimate?.period ?? 650;\r\n    let reason = null;\r\n    if (this.lastObservedAtMs === null) reason = 'no-audio';\r\n    else if (ageMs > this.staleAfterMs) reason = 'stale';\r\n    else if (nowMs - this.lastSignalAtMs > Math.max(850, period * 1.5)) reason = 'silence';\r\n    else if (lastOnsetAtMs === null || nowMs - lastOnsetAtMs > period * 2.2) reason = 'no-recent-pulse';\r\n    else if (!this.estimate) reason = 'uncertain';\r\n    const available = reason === null;\r\n    const tempoBpm = available ? 60000 / period : null;\r\n    return {\r\n      available, reason, rms: ageMs > this.staleAfterMs ? 0 : this.rms, peak: ageMs > this.staleAfterMs ? 0 : this.peak,\r\n      onset: ageMs === 0 && this.latestOnset, onsets: this.onsets.filter(({ atMs }) => nowMs - atMs < 10000).map((onset) => ({ ...onset })),\r\n      tempoBpm, confidence: available ? this.estimate.confidence : 0,\r\n      phase: available ? modulo(nowMs - this.estimate.anchor, period) / period : null,\r\n      beatPeriodMs: available ? period : null, lastOnsetAtMs, ageMs,\r\n      ambiguity: available ? { halfBpm: tempoBpm / 2, doubleBpm: tempoBpm * 2, resolved: false } : null,\r\n      preferredTempo: available ? tempoBpm >= 92 - 0.5 && tempoBpm <= 120 + 0.5 : false,\r\n    };\r\n  }\r\n}\r\n\r\nexport const createBeatProbe = (options) => new BeatProbe(options);\r\n","modules/dance/music/generate_music.py":"#!/usr/bin/env python3\r\n\"\"\"Deterministic original MYR5 dance music renderer; Python standard library only.\"\"\"\r\nfrom __future__ import annotations\r\n\r\nimport hashlib\r\nimport json\r\nimport math\r\nimport random\r\nimport struct\r\nimport wave\r\nfrom array import array\r\nfrom pathlib import Path\r\n\r\nOUT = Path(__file__).resolve().parent\r\nSR = 22050\r\nBEATS = 64\r\nTAU = 2.0 * math.pi\r\n\r\n\r\ndef midi(n: int) -> float:\r\n    return 440.0 * (2.0 ** ((n - 69) / 12.0))\r\n\r\n\r\nclass Song:\r\n    def __init__(self, bpm: int, seed: int):\r\n        self.bpm = bpm\r\n        self.spb = 60.0 / bpm\r\n        self.frames = round(BEATS * self.spb * SR)\r\n        self.buf = array('f', [0.0]) * self.frames\r\n        self.rng = random.Random(seed)\r\n\r\n    def add(self, beat: float, dur_beats: float, freq: float, amp: float,\r\n            timbre: str = 'soft', pan: float = 0.0):\r\n        start = max(0, round(beat * self.spb * SR))\r\n        count = min(self.frames - start, max(0, round(dur_beats * self.spb * SR)))\r\n        if count <= 0:\r\n            return\r\n        hz = max(20.0, freq)\r\n        atk = max(1, round(SR * (0.008 if timbre in ('pluck', 'bass') else 0.035)))\r\n        rel = min(count // 2, round(SR * (0.08 if timbre == 'pluck' else 0.16)))\r\n        phase = self.rng.random() * TAU\r\n        for j in range(count):\r\n            t = j / SR\r\n            env = min(1.0, j / atk)\r\n            if rel and j > count - rel:\r\n                env *= (count - j) / rel\r\n            if timbre == 'bass':\r\n                env *= math.exp(-t * 1.6)\r\n                v = math.sin(TAU * hz * t + phase) + 0.24 * math.sin(TAU * hz * 2 * t + phase)\r\n            elif timbre == 'pluck':\r\n                env *= math.exp(-t * 3.4)\r\n                v = math.sin(TAU * hz * t + phase) + 0.3 * math.sin(TAU * hz * 2 * t + phase)\r\n            elif timbre == 'bright':\r\n                v = (math.sin(TAU * hz * t + phase) + 0.34 * math.sin(TAU * hz * 2 * t + phase)\r\n                     + 0.16 * math.sin(TAU * hz * 3 * t + phase))\r\n            else:\r\n                v = math.sin(TAU * hz * t + phase) + 0.16 * math.sin(TAU * hz * 2 * t + phase)\r\n            self.buf[start + j] += amp * env * v\r\n\r\n    def kick(self, beat: float, amp: float = 0.42):\r\n        start = round(beat * self.spb * SR)\r\n        count = min(round(.24 * SR), self.frames - start)\r\n        phase = 0.0\r\n        for j in range(max(0, count)):\r\n            t = j / SR\r\n            f = 105.0 - 65.0 * min(1.0, t / .12)\r\n            phase += TAU * f / SR\r\n            self.buf[start + j] += amp * math.exp(-t * 18.0) * math.sin(phase)\r\n\r\n    def noise_hit(self, beat: float, dur: float, amp: float, tone: str = 'hat'):\r\n        start = round(beat * self.spb * SR)\r\n        count = min(round(dur * SR), self.frames - start)\r\n        prev = 0.0\r\n        for j in range(max(0, count)):\r\n            t = j / SR\r\n            n = self.rng.uniform(-1.0, 1.0)\r\n            if tone == 'snare':\r\n                v = n * .78 + prev * .22\r\n                env = math.exp(-t * 15.0)\r\n            elif tone == 'clap':\r\n                v = n\r\n                env = (math.exp(-t * 17) + .55 * math.exp(-max(0, t - .035) * 24))\r\n            else:\r\n                v = n - prev * .8\r\n                env = math.exp(-t * 43.0)\r\n            prev = n\r\n            self.buf[start + j] += amp * env * v\r\n\r\n    def conga(self, beat: float, high: bool):\r\n        start = round(beat * self.spb * SR)\r\n        count = min(round(.16 * SR), self.frames - start)\r\n        f0 = 205.0 if high else 112.0\r\n        for j in range(max(0, count)):\r\n            t = j / SR\r\n            f = f0 * (1.0 - .16 * min(1.0, t / .025))\r\n            self.buf[start + j] += (.17 if high else .23) * math.exp(-t * 24) * math.sin(TAU * f * t)\r\n\r\n    def finish(self) -> bytes:\r\n        # Gentle 650 ms edge fades, then a conservative transparent soft limiter.\r\n        fade = round(.65 * SR)\r\n        peak = max(abs(x) for x in self.buf) or 1.0\r\n        gain = min(1.0, .78 / peak)\r\n        pcm = bytearray(self.frames * 2)\r\n        for i, x in enumerate(self.buf):\r\n            edge = min(1.0, i / max(1, fade), (self.frames - 1 - i) / max(1, fade))\r\n            y = math.tanh(x * gain * 1.08) / math.tanh(1.08) * edge\r\n            struct.pack_into('<h', pcm, i * 2, max(-32767, min(32767, round(y * 32767))))\r\n        return bytes(pcm)\r\n\r\n\r\ndef note(song: Song, beat: float, duration: float, pitch: int, amp: float,\r\n         timbre: str = 'soft'):\r\n    song.add(beat, duration, midi(pitch), amp, timbre)\r\n\r\n\r\ndef chord(song: Song, beat: float, pitches: tuple[int, ...], duration: float,\r\n          amp: float = .095, timbre: str = 'soft'):\r\n    for i, p in enumerate(pitches):\r\n        note(song, beat, duration, p, amp / (1 + .13 * i), timbre)\r\n\r\n\r\ndef pop() -> Song:\r\n    s = Song(116, 11601)\r\n    # Bright C major / add-nine dance-pop loop.\r\n    chords = [(48, (60, 64, 67, 74)), (43, (59, 62, 67, 71)),\r\n              (45, (60, 64, 69, 72)), (41, (57, 60, 65, 69))]\r\n    motif = [72, None, 76, 79, None, 76, 74, None, 72, 76, None, 79, 81, None, 79, 76]\r\n    for b in range(0, 64, 4):\r\n        root, tones = chords[(b // 4) % 4]\r\n        section = b // 16\r\n        chord(s, b, tones, 3.8, .055 if b < 8 else .09)\r\n        # Four-bar groove with varied bass pickup; bars 1–2 introduce, then full kit.\r\n        for beat in range(4):\r\n            at = b + beat\r\n            bass_pitch = root + (12 if beat == 3 and section >= 2 else 0)\r\n            note(s, at, .68, bass_pitch, .22, 'bass')\r\n            if b >= 4 and beat in (0, 2):\r\n                s.kick(at, .34 if b < 8 else .47)\r\n            if b >= 8 and beat in (1, 3):\r\n                s.noise_hit(at, .19, .17, 'snare')\r\n            # Offbeat eighth hats, opening from a sparse intro into a bright ride.\r\n            if b >= 4:\r\n                s.noise_hit(at + .5, .045, .045 if section < 3 else .06, 'hat')\r\n                if beat in (0, 2):\r\n                    s.noise_hit(at, .05, .055, 'hat')\r\n        for i, p in enumerate(motif):\r\n            at = b + i * .25\r\n            if p is not None and (b >= 4 or i in (0, 2, 5, 8, 11, 14)):\r\n                note(s, at, .22, p + (0 if section != 2 else 12), .12, 'pluck')\r\n        if b >= 48:\r\n            s.noise_hit(b, .16, .13, 'clap')\r\n    return s\r\n\r\n\r\ndef jazz() -> Song:\r\n    s = Song(96, 9602)\r\n    # Warm Bb swing, alternating ii–V–I and a turnaround.\r\n    chords = [(46, (58, 61, 65, 68)), (41, (57, 60, 63, 67)),\r\n              (43, (58, 62, 65, 69)), (43, (58, 62, 65, 69)),\r\n              (44, (56, 60, 63, 67)), (39, (55, 59, 62, 65)),\r\n              (46, (58, 61, 65, 68)), (41, (57, 60, 63, 67))]\r\n    scale = [70, 72, 73, 77, 79, 82, 84]\r\n    motif = [0, 2, 4, 3, 2, 1, 3, 5]\r\n    for b in range(0, 64, 4):\r\n        root, tones = chords[(b // 4) % len(chords)]\r\n        section = b // 16\r\n        # Soft piano-like upper extensions; comp on beats 2 and 4 after the pickup.\r\n        chord(s, b, tones, 3.6, .07)\r\n        for beat in range(4):\r\n            at = b + beat\r\n            walking = [root, root + 7, root + 4, root + 7][beat]\r\n            note(s, at, .72, walking, .20, 'bass')\r\n            if b >= 4 and beat in (1, 3):\r\n                chord(s, at, tones[1:], .34, .09, 'pluck')\r\n            # Swinging ride, offbeat eighth lands at 2/3 of a quarter-note.\r\n            if b >= 4:\r\n                s.noise_hit(at, .05, .085 if beat in (0, 2) else .055, 'hat')\r\n                if beat < 3:\r\n                    s.noise_hit(at + 2 / 3, .045, .05, 'hat')\r\n            if b >= 8 and beat in (1, 3):\r\n                s.noise_hit(at, .17, .12, 'snare')\r\n        for i, idx in enumerate(motif):\r\n            # Eight notes per measure: the offbeat eighth lands at 2/3 of each beat.\r\n            if (i + b // 4) % 5 != 0 or section >= 1:\r\n                swung = i // 2 + ((2 / 3) if i % 2 else 0)\r\n                pitch = scale[(idx + (b // 8) % 3) % len(scale)]\r\n                note(s, b + swung, .24, pitch, .09 if section == 0 else .125, 'bright')\r\n        if b in (24, 56):\r\n            s.noise_hit(b + 3, .2, .16, 'clap')\r\n    return s\r\n\r\n\r\ndef latin() -> Song:\r\n    s = Song(112, 11203)\r\n    # Sunny A minor / Dorian son montuno color; distinct clave, conga and piano ostinato.\r\n    chords = [(45, (57, 60, 64, 67)), (50, (57, 62, 65, 69)),\r\n              (43, (55, 59, 62, 65)), (48, (55, 60, 64, 67))]\r\n    melody = [76, 79, 81, None, 79, 76, 74, None, 72, 74, 76, 79, 81, None, 79, 76]\r\n    # Son clave 3-2 over two bars; repeat with light variation every four bars.\r\n    clave = [0, 1.5, 3, 4.5, 7]\r\n    for b in range(0, 64, 4):\r\n        root, tones = chords[(b // 4) % 4]\r\n        section = b // 16\r\n        chord(s, b, tones, 3.75, .055 if b < 8 else .075)\r\n        # Two-bar montuno: short piano stabs on the offbeat, alternating voicing.\r\n        for half in (0, 1):\r\n            base = b + half * 2\r\n            voicing = tones[1:] if half == 0 else tones[:3]\r\n            for off in (.5, 1.5):\r\n                if b >= 4:\r\n                    chord(s, base + off, voicing, .34, .105, 'pluck')\r\n        for beat in range(4):\r\n            at = b + beat\r\n            note(s, at, .72, root + (12 if beat == 3 and section == 2 else 0), .22, 'bass')\r\n            if b >= 4 and beat in (0, 2):\r\n                s.kick(at, .39)\r\n            # Soft hand drum pulse, with the clave lifted above the conga bed.\r\n            if b >= 4:\r\n                phase = b % 8 + beat\r\n                if phase in (0, 1, 3, 4, 7):\r\n                    s.conga(at, high=phase in (1, 4, 7))\r\n        if b % 8 == 0 and b >= 4:\r\n            for hit in clave:\r\n                at = b + hit\r\n                if at < 64:\r\n                    s.noise_hit(at, .09, .115, 'clap')\r\n        for i, p in enumerate(melody):\r\n            at = b + i * .25\r\n            if p is not None and (b >= 4 or i in (0, 2, 5, 8, 10, 12, 15)):\r\n                note(s, at, .22, p + (12 if section == 2 and i in (2, 10) else 0), .12, 'bright')\r\n        if b >= 8:\r\n            s.noise_hit(b + 1.5, .055, .035, 'hat')\r\n            s.noise_hit(b + 3.5, .055, .04, 'hat')\r\n    return s\r\n\r\n\r\ndef write_track(track_id: str, title: str, style: str, song: Song,\r\n                subdivision: str, swing: str, phrases: list[int]):\r\n    pcm = song.finish()\r\n    path = OUT / f'{track_id}.wav'\r\n    with wave.open(str(path), 'wb') as wf:\r\n        wf.setnchannels(1)\r\n        wf.setsampwidth(2)\r\n        wf.setframerate(SR)\r\n        wf.writeframes(pcm)\r\n    digest = hashlib.sha256(path.read_bytes()).hexdigest()\r\n    beat_times = [round(i * song.spb, 9) for i in range(BEATS + 1)]\r\n    return {\r\n        'id': track_id, 'title': title, 'style': style, 'bpm': song.bpm,\r\n        'beats': BEATS, 'durationSeconds': round(song.frames / SR, 9),\r\n        'sampleRate': SR, 'channels': 1, 'audioSha256': digest,\r\n        'relativepath': f'{track_id}.wav', 'meter': [4, 4],\r\n        'beatGroups': [1, 1, 1, 1], 'subdivision': subdivision,\r\n        'swingRatio': swing, 'confidence': 'authored',\r\n        'phraseStarts': phrases, 'beatTimesSeconds': beat_times,\r\n        'attribution': 'Original music authored and synthesized for MYR5; no samples.',\r\n        'sampleCount': song.frames,\r\n    }\r\n\r\n\r\ndef main():\r\n    tracks = [\r\n        write_track('neon-step-pop', 'Neon Step', 'retro alien dance-pop', pop(), 'straight eighths', '1:1', [0, 8, 16, 24, 32, 40, 48, 56]),\r\n        write_track('moonroom-swing', 'Moonroom Swing', 'retro lounge jazz swing', jazz(), 'swung eighths', '2:1', [0, 8, 16, 24, 32, 40, 48, 56]),\r\n        write_track('cosmic-clave', 'Cosmic Clave', 'retro Latin son montuno', latin(), 'straight eighths with clave', '1:1', [0, 8, 16, 24, 32, 40, 48, 56]),\r\n    ]\r\n    (OUT / 'manifest.json').write_text(json.dumps({'version': 1, 'tracks': tracks}, indent=2) + '\\n', encoding='utf-8')\r\n    print(json.dumps([{'id': t['id'], 'duration': t['durationSeconds'], 'bytes': (OUT / t['relativepath']).stat().st_size, 'sha256': t['audioSha256']} for t in tracks], indent=2))\r\n\r\n\r\nif __name__ == '__main__':\r\n    main()\r\n","modules/dance/music/index.mjs":"import { AUTHORED_TARGET_SCHEMA, UPPER_BODY_PROFILE } from '../content/index.mjs';\r\nconst SCHEMA_VERSION = 'myr5.dance-session.v1';\r\nconst GROOVES = new Set(['cardio', 'salsa-inspired', 'jazz-inspired']);\r\nconst TRACKS = [\r\n  {\r\n    id: 'neon-step-pop', title: 'Neon Step', source: 'builtin', durationSeconds: 33.103446712,\r\n    audioUrl: '/modules/dance/assets/audio/neon-step-pop.wav',\r\n    audioHash: '1017445788feac15a36aa26f3a294c38a1b755f711db53a400b5feb07467d56f',\r\n    bpm: 116, firstBeatSeconds: 0, groove: 'cardio', beatTimesSeconds: makeGrid(116, 64, 33.103446712),\r\n    capabilities: { playback: true, synchronizedGameplay: true, analyze: false, mix: false },\r\n    provenance: { kind: 'original', creator: 'MYR5', description: 'Original digitally authored music; no samples.', generator: 'music/generate_music.py', seed: 11601 },\r\n  },\r\n  {\r\n    id: 'moonroom-swing', title: 'Moonroom Swing', source: 'builtin', durationSeconds: 40,\r\n    audioUrl: '/modules/dance/assets/audio/moonroom-swing.wav',\r\n    audioHash: 'a85fd1acc0c0bec27ad082c3b1932c96dab049a3433f4ee2e500ca57ace467c3',\r\n    bpm: 96, firstBeatSeconds: 0, groove: 'jazz-inspired', beatTimesSeconds: makeGrid(96, 64, 40),\r\n    capabilities: { playback: true, synchronizedGameplay: true, analyze: false, mix: false },\r\n    provenance: { kind: 'original', creator: 'MYR5', description: 'Original digitally authored music; no samples.', generator: 'music/generate_music.py', seed: 9602 },\r\n  },\r\n  {\r\n    id: 'cosmic-clave', title: 'Cosmic Clave', source: 'builtin', durationSeconds: 34.285714286,\r\n    audioUrl: '/modules/dance/assets/audio/cosmic-clave.wav',\r\n    audioHash: 'c3d21a1c9a02e648aed5dca071da718003ee16394b23df642efb25041811fd46',\r\n    bpm: 112, firstBeatSeconds: 0, groove: 'salsa-inspired', beatTimesSeconds: makeGrid(112, 64, 34.285714286),\r\n    capabilities: { playback: true, synchronizedGameplay: true, analyze: false, mix: false },\r\n    provenance: { kind: 'original', creator: 'MYR5', description: 'Original digitally authored music; no samples.', generator: 'music/generate_music.py', seed: 11203 },\r\n  },\r\n];\r\n\r\nfunction makeGrid(bpm, beats, duration) {\r\n  const step = 60 / bpm;\r\n  return Array.from({ length: beats + 1 }, (_, i) => i === beats ? Math.min(duration, beats * step) : i * step);\r\n}\r\n\r\nfunction deepFreeze(value) {\r\n  if (value && typeof value === 'object' && !Object.isFrozen(value)) {\r\n    Object.freeze(value);\r\n    for (const child of Object.values(value)) deepFreeze(child);\r\n  }\r\n  return value;\r\n}\r\n\r\nexport const builtinTracks = deepFreeze(TRACKS);\r\n\r\nconst finite = (value) => Number.isFinite(value);\r\nconst stableValue = (value) => {\r\n  if (Array.isArray(value)) return value.map(stableValue);\r\n  if (value && typeof value === 'object') {\r\n    return Object.fromEntries(Object.keys(value).filter((key) => key !== 'audioUrl' && key !== 'contentHash').sort().map((key) => [key, stableValue(value[key])]));\r\n  }\r\n  return value;\r\n};\r\nconst canonicalJson = (value) => JSON.stringify(stableValue(value));\r\nconst sha256 = async (value) => {\r\n  if (!globalThis.crypto?.subtle) throw new Error('Web Crypto SHA-256 is required to freeze a session.');\r\n  const bytes = typeof value === 'string' ? new TextEncoder().encode(value)\r\n    : value instanceof ArrayBuffer ? value\r\n      : ArrayBuffer.isView(value) ? value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength)\r\n        : new TextEncoder().encode(canonicalJson(value));\r\n  const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);\r\n  return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\r\n};\r\n\r\nfunction trackValidation(track) {\r\n  if (!track || typeof track.id !== 'string' || !track.id || typeof track.title !== 'string') throw new TypeError('Each track needs an id and title.');\r\n  if (!['builtin', 'local'].includes(track.source)) throw new TypeError(`Track ${track.id} has an unsupported source.`);\r\n  const minDuration = track.source === 'builtin' ? 30 : 10;\r\n  const maxDuration = track.source === 'builtin' ? 90 : 20 * 60;\r\n  if (!finite(track.durationSeconds) || track.durationSeconds < minDuration || track.durationSeconds > maxDuration) throw new RangeError(`Track ${track.id} duration is outside its supported range.`);\r\n  if (!finite(track.bpm) || track.bpm < 40 || track.bpm > 220) throw new RangeError(`Track ${track.id} has an impossible BPM.`);\r\n  if (!finite(track.firstBeatSeconds) || track.firstBeatSeconds < 0 || track.firstBeatSeconds >= track.durationSeconds) throw new RangeError(`Track ${track.id} has an invalid first-beat offset.`);\r\n  if (!GROOVES.has(track.groove)) throw new TypeError(`Track ${track.id} needs an explicit supported groove.`);\r\n  if (!Array.isArray(track.beatTimesSeconds) || track.beatTimesSeconds.length < 9) throw new TypeError(`Track ${track.id} needs an authored beat grid and terminal boundary.`);\r\n  if (!track.audioUrl || typeof track.audioUrl !== 'string') throw new TypeError(`Track ${track.id} needs an audio URL.`);\r\n  if (!track.audioHash && !(track.source === 'local' && track.fileIdentity)) throw new TypeError(`Track ${track.id} needs an audio hash or local file identity.`);\r\n  const grid = track.beatTimesSeconds;\r\n  if (Math.abs(grid[0] - track.firstBeatSeconds) > 1e-6) throw new RangeError(`Track ${track.id} grid does not begin at its confirmed first beat.`);\r\n  for (let i = 0; i < grid.length; i++) {\r\n    if (!finite(grid[i]) || grid[i] < 0 || grid[i] > track.durationSeconds + 1e-5 || (i && grid[i] <= grid[i - 1])) throw new RangeError(`Track ${track.id} has an invalid or non-monotonic beat grid.`);\r\n    if (i && i < grid.length - 1 && Math.abs((grid[i] - grid[i - 1]) - 60 / track.bpm) > 0.002) throw new RangeError(`Track ${track.id} grid disagrees with its confirmed BPM.`);\r\n  }\r\n  if (grid.at(-1) > track.durationSeconds + 1e-5) throw new RangeError(`Track ${track.id} terminal boundary exceeds its audio.`);\r\n}\r\n\r\nfunction seedNumber(seed) {\r\n  const source = String(seed);\r\n  let value = 2166136261;\r\n  for (let i = 0; i < source.length; i++) value = Math.imul(value ^ source.charCodeAt(i), 16777619);\r\n  return value >>> 0;\r\n}\r\nfunction randomFor(seed) {\r\n  let state = seedNumber(seed);\r\n  return () => {\r\n    state += 0x6D2B79F5;\r\n    let t = state;\r\n    t = Math.imul(t ^ t >>> 15, t | 1);\r\n    t ^= t + Math.imul(t ^ t >>> 7, t | 61);\r\n    return ((t ^ t >>> 14) >>> 0) / 4294967296;\r\n  };\r\n}\r\n\r\nfunction getRange(target) {\r\n  const range = target?.tempoRangeBpm;\r\n  return Array.isArray(range) && range.length === 2 && range.every(finite) ? range : null;\r\n}\r\n\r\nfunction makeSlot({ occurrenceId, moveId = null, role, startBeat, endBeat, grid, sessionStart, previewLeadBeats = 0, repeatCount = 1 }) {\r\n  if (moveId === null && role === 'bridge') moveId = 'neutral-bridge';\r\n  if (moveId === null && role === 'rest') moveId = 'rest';\r\n  const start = sessionStart + (grid[startBeat] ?? 0);\r\n  const end = sessionStart + (grid[endBeat] ?? grid.at(-1));\r\n  return {\r\n    id: `${occurrenceId}-slot-${startBeat}-${endBeat}-${role}`,\r\n    occurrenceId,\r\n    moveId,\r\n    role,\r\n    startSessionSeconds: start,\r\n    endSessionSeconds: end,\r\n    startBeat,\r\n    endBeat,\r\n    repeatCount,\r\n    scored: role === 'move',\r\n    previewLeadBeats,\r\n  };\r\n}\r\n\r\nasync function resolveCatalogue(catalogue) {\r\n  if (catalogue) return catalogue;\r\n  let content;\r\n  try { content = await import('../content/index.mjs'); }\r\n  catch { content = await import('../content/catalogue.mjs'); }\r\n  return content.getCatalogue();\r\n}\r\n\r\n/** Freeze a deterministic, complete session plan. Repeated queue entries remain distinct occurrences. */\r\nexport async function prepareSession({ tracks, catalogue, seed = 'myr5-dance', intensity = 'steady' } = {}) {\r\n  if (!Array.isArray(tracks) || tracks.length < 1 || tracks.length > 5) throw new RangeError('Choose between one and five track entries.');\r\n  if (!['easy', 'steady'].includes(intensity)) throw new TypeError('Intensity must be easy or steady.');\r\n  const targets = await resolveCatalogue(catalogue);\r\n  if (!Array.isArray(targets) || targets.length === 0) throw new TypeError('An authored choreography catalogue is required.');\r\n  const orderedTargets = [...targets].sort((a, b) => String(a.id).localeCompare(String(b.id)));\r\n  if (orderedTargets.some((target) => !target || target.schemaVersion !== AUTHORED_TARGET_SCHEMA || target.profile !== UPPER_BODY_PROFILE || !target.id || ![4, 8].includes(target.beats) || !getRange(target))) throw new TypeError('Catalogue entries must use the current authored upper-body schema and profile with ids, 4- or 8-beat cycles, and tempo ranges.');\r\n  for (const track of tracks) trackValidation(track);\r\n\r\n  const revision = targets.revision ?? targets.catalogueRevision ?? orderedTargets.find((target) => target.revision)?.revision ?? 'unversioned';\r\n  const rand = randomFor(seed);\r\n  const trackOrder = [];\r\n  const segments = [];\r\n  const slots = [];\r\n  let sessionCursor = 0;\r\n  for (let trackIndex = 0; trackIndex < tracks.length; trackIndex++) {\r\n    const track = tracks[trackIndex];\r\n    const occurrenceId = `occ-${trackIndex + 1}-${track.id}`;\r\n    const audioStartSessionSeconds = sessionCursor;\r\n    trackOrder.push({ occurrenceId, trackId: track.id });\r\n    segments.push({ occurrenceId, trackId: track.id, audioStartSessionSeconds, audioOffsetSeconds: 0, durationSeconds: track.durationSeconds });\r\n    const grid = track.beatTimesSeconds;\r\n    const beatCount = grid.length - 1;\r\n    if (beatCount < 16) throw new RangeError(`${track.title} needs at least sixteen complete beats for a count-in and a whole move.`);\r\n    if (grid[0] > 1e-6) slots.push({\r\n      id: `${occurrenceId}-slot-preroll-rest`, occurrenceId, moveId: null, role: 'rest',\r\n      startSessionSeconds: sessionCursor, endSessionSeconds: sessionCursor + grid[0], startBeat: 0, endBeat: 0,\r\n      repeatCount: 1, scored: false, previewLeadBeats: 0,\r\n    });\r\n    slots.push(makeSlot({ occurrenceId, role: 'count_in', startBeat: 0, endBeat: 8, grid, sessionStart: sessionCursor }));\r\n\r\n    const eligible = orderedTargets.filter((target) => {\r\n      const families = target.families ?? [];\r\n      if (target.id === 'neutral-bridge' || target.id === 'rest' || families.includes('transition') || families.includes('rest')) return false;\r\n      const specialized = families.filter((family) => family === 'jazz-inspired' || family === 'salsa-inspired');\r\n      const grooveCompatible = specialized.length\r\n        ? specialized.includes(track.groove)\r\n        : families.includes(track.groove) || families.includes('cardio') || families.includes('upper-body');\r\n      if (!grooveCompatible) return false;\r\n      const [min, max] = getRange(target);\r\n      return track.bpm >= min && track.bpm <= max;\r\n    });\r\n    if (!eligible.length) throw new RangeError(`No authored ${track.groove} move supports ${track.bpm} BPM (${track.title}).`);\r\n    const matchingIntensity = eligible.filter((target) => target.intensity === intensity);\r\n    const movePool = (matchingIntensity.length ? matchingIntensity : eligible).slice().sort((a, b) => String(a.id).localeCompare(String(b.id)));\r\n    let beat = 8;\r\n    let block = 0;\r\n    let previousMove = null;\r\n    while (beat + 8 <= beatCount) {\r\n      if (block > 0 && block % 3 === 2) {\r\n        slots.push(makeSlot({ occurrenceId, role: 'bridge', startBeat: beat, endBeat: beat + 8, grid, sessionStart: sessionCursor }));\r\n      } else if (block > 0 && block % 5 === 4) {\r\n        slots.push(makeSlot({ occurrenceId, role: 'rest', startBeat: beat, endBeat: beat + 8, grid, sessionStart: sessionCursor }));\r\n      } else {\r\n        const candidates = movePool.filter((target) => target.id !== previousMove && 8 % target.beats === 0);\r\n        const choices = candidates.length ? candidates : movePool.filter((target) => 8 % target.beats === 0);\r\n        if (!choices.length) throw new RangeError(`No whole-cycle move fits an 8-beat phrase at ${track.bpm} BPM.`);\r\n        const target = choices[Math.floor(rand() * choices.length)];\r\n        slots.push(makeSlot({ occurrenceId, moveId: target.id, role: 'move', startBeat: beat, endBeat: beat + 8, grid, sessionStart: sessionCursor, repeatCount: 8 / target.beats, previewLeadBeats: 4 }));\r\n        previousMove = target.id;\r\n      }\r\n      beat += 8;\r\n      block++;\r\n    }\r\n    if (beat < beatCount) slots.push(makeSlot({ occurrenceId, role: 'rest', startBeat: beat, endBeat: beatCount, grid, sessionStart: sessionCursor }));\r\n    const terminalSession = sessionCursor + grid.at(-1);\r\n    if (terminalSession < sessionCursor + track.durationSeconds - 1e-6) slots.push({\r\n      id: `${occurrenceId}-slot-audio-tail-rest`, occurrenceId, moveId: null, role: 'rest',\r\n      startSessionSeconds: terminalSession, endSessionSeconds: sessionCursor + track.durationSeconds,\r\n      startBeat: beatCount, endBeat: beatCount, repeatCount: 1, scored: false, previewLeadBeats: 0,\r\n    });\r\n    sessionCursor += track.durationSeconds;\r\n  }\r\n\r\n  const content = {\r\n    schemaVersion: SCHEMA_VERSION,\r\n    seed,\r\n    catalogueRevision: revision,\r\n    intensity,\r\n    trackOrder,\r\n    segments,\r\n    durationSeconds: sessionCursor,\r\n    slots,\r\n    tracks: tracks.map((track) => ({\r\n      id: track.id, source: track.source, durationSeconds: track.durationSeconds, audioHash: track.audioHash ?? null,\r\n      fileIdentity: track.fileIdentity ?? null, bpm: track.bpm, firstBeatSeconds: track.firstBeatSeconds,\r\n      beatTimesSeconds: track.beatTimesSeconds, groove: track.groove,\r\n    })),\r\n    targetRefs: orderedTargets.map(({ id, revision: targetRevision }) => ({ id, revision: targetRevision ?? null })),\r\n  };\r\n  const plan = {\r\n    schemaVersion: SCHEMA_VERSION,\r\n    seed,\r\n    catalogueRevision: revision,\r\n    contentHash: await sha256(content),\r\n    trackOrder,\r\n    segments,\r\n    durationSeconds: sessionCursor,\r\n    slots,\r\n  };\r\n  return deepFreeze(plan);\r\n}\r\n\r\n/** Decode user-selected audio and retain it as a session-only object URL. Caller revokes audioUrl on disposal. */\r\nexport async function createLocalTrack({ file, bpm, firstBeatSeconds = 0, groove, rightsConfirmed = false } = {}) {\r\n  if (!rightsConfirmed) throw new Error('Confirm that you have permission to use this audio in Dance.');\r\n  if (!file || typeof file.arrayBuffer !== 'function') throw new TypeError('Choose an audio file.');\r\n  if (!finite(file.size) || file.size <= 0 || file.size > 100 * 1024 * 1024) throw new RangeError('Local audio must be no larger than 100 MB.');\r\n  if (!finite(bpm) || bpm < 40 || bpm > 220) throw new RangeError('Confirm a BPM between 40 and 220.');\r\n  if (!GROOVES.has(groove)) throw new TypeError('Choose a supported groove explicitly.');\r\n  const AudioContextCtor = globalThis.AudioContext || globalThis.webkitAudioContext;\r\n  if (!globalThis.crypto?.subtle || !AudioContextCtor) throw new Error('Web Audio and Web Crypto are required to prepare local audio.');\r\n  const bytes = await file.arrayBuffer();\r\n  const audioHash = await sha256(bytes);\r\n  const context = new AudioContextCtor();\r\n  let audioBuffer;\r\n  try {\r\n    audioBuffer = await context.decodeAudioData(bytes.slice(0));\r\n  } finally {\r\n    await context.close().catch(() => {});\r\n  }\r\n  const durationSeconds = audioBuffer.duration;\r\n  if (!finite(durationSeconds) || durationSeconds < 10 || durationSeconds > 20 * 60) throw new RangeError('Local audio must be between 10 seconds and 20 minutes long.');\r\n  if (!finite(firstBeatSeconds) || firstBeatSeconds < 0 || firstBeatSeconds >= durationSeconds) throw new RangeError('Confirm a first-beat offset within the audio.');\r\n  const step = 60 / bpm;\r\n  const beatCount = Math.floor((durationSeconds - firstBeatSeconds + 1e-7) / step);\r\n  if (beatCount < 12) throw new RangeError('This audio has too few beats after the confirmed offset.');\r\n  const beatTimesSeconds = Array.from({ length: beatCount + 1 }, (_, index) => firstBeatSeconds + index * step);\r\n  const audioUrl = URL.createObjectURL(file);\r\n  return deepFreeze({\r\n    id: `local-${audioHash.slice(0, 16)}`,\r\n    title: file.name?.replace(/\\.[^.]+$/, '') || 'Local track',\r\n    source: 'local', durationSeconds, audioUrl, audioHash, bpm, firstBeatSeconds,\r\n    beatTimesSeconds, groove,\r\n    capabilities: { playback: true, synchronizedGameplay: true, analyze: false, mix: false },\r\n    provenance: { kind: 'user-supplied', description: 'User selected audio; rights affirmed for this use.', rightsConfirmed: true },\r\n  });\r\n}\r\n\r\nfunction makeAudioContext() {\r\n  const Ctor = globalThis.AudioContext || globalThis.webkitAudioContext;\r\n  if (!Ctor) throw new Error('Web Audio is unavailable in this browser.');\r\n  return new Ctor();\r\n}\r\n\r\n/** Native Web Audio single clock. snapshot states are idle, playing, paused, ended, error, disposed. */\r\nexport function createMusicSession({ plan, tracks, onEnded, onError, getVolume = () => 1 } = {}) {\r\n  if (!plan || !Array.isArray(plan.segments) || !Array.isArray(tracks) || tracks.length < 1) throw new TypeError('A frozen plan and its tracks are required.');\r\n  const byId = new Map(tracks.map((track) => [track.id, track]));\r\n  for (const segment of plan.segments) if (!byId.has(segment.trackId)) throw new TypeError(`Missing audio for ${segment.trackId}.`);\r\n  let context = null;\r\n  let output = null, outputLevel = null;\r\n  const syncVolume = () => {if(!output)return;const raw=getVolume(),value=Number.isFinite(raw)?Math.max(0,Math.min(1,raw)):1;if(value!==outputLevel){output.gain.value=value;outputLevel=value;}};\r\n  let state = 'idle';\r\n  let clockEpoch = 0;\r\n  let startedAt = null;\r\n  let baseSeconds = 0;\r\n  let sources = [];\r\n  let buffers = new Map();\r\n  let endedCalled = false;\r\n  let errorCalled = false;\r\n  let disposed = false;\r\n  const visible = () => typeof document === 'undefined' || document.visibilityState === 'visible';\r\n  const sessionSeconds = () => {\r\n    if (state === 'playing' && context && startedAt !== null) return Math.max(0, Math.min(plan.durationSeconds, context.currentTime - startedAt));\r\n    return Math.max(0, Math.min(plan.durationSeconds, baseSeconds));\r\n  };\r\n  const activeOccurrence = (seconds) => {\r\n    const segment = plan.segments.find((item) => seconds >= item.audioStartSessionSeconds && seconds < item.audioStartSessionSeconds + item.durationSeconds);\r\n    return segment?.occurrenceId ?? null;\r\n  };\r\n  const reportError = (error) => {\r\n    if (disposed || errorCalled) return;\r\n    errorCalled = true;\r\n    state = 'error';\r\n    try { onError?.(error); } catch { /* consumer callback failures do not corrupt the clock */ }\r\n  };\r\n  const stopSources = () => {\r\n    for (const source of sources) {\r\n      try { source.onended = null; source.stop(); } catch { /* already stopped */ }\r\n      try { source.disconnect(); } catch { /* already disconnected */ }\r\n    }\r\n    sources = [];\r\n  };\r\n  const loadBuffers = async ({ resumeContext = true } = {}) => {\r\n    context ??= makeAudioContext();\r\n    if (resumeContext && context.state !== 'running') await context.resume();\r\n    for (const segment of plan.segments) {\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      if (buffers.has(segment.trackId)) continue;\r\n      const track = byId.get(segment.trackId);\r\n      const response = await fetch(track.audioUrl);\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      if (!response.ok) throw new Error(`Could not load ${track.title} (${response.status}).`);\r\n      const data = await response.arrayBuffer();\r\n      const buffer = await context.decodeAudioData(data);\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      const durationTolerance = 2 / (buffer.sampleRate || 48000);\r\n      if (Math.abs(buffer.duration - track.durationSeconds) > durationTolerance) throw new Error(`${track.title} audio duration does not match its confirmed grid.`);\r\n      buffers.set(segment.trackId, buffer);\r\n    }\r\n  };\r\n  const schedule = (atTime) => {\r\n    stopSources();\r\n    const scheduled = [];\r\n    for (const segment of plan.segments) {\r\n      const source = context.createBufferSource();\r\n      source.buffer = buffers.get(segment.trackId);\r\n      source.connect(output);\r\n      const startAt = atTime + segment.audioStartSessionSeconds;\r\n      const duration = segment.durationSeconds;\r\n      source.start(startAt, segment.audioOffsetSeconds ?? 0);\r\n      if (source.buffer.duration > duration) source.stop(startAt + duration);\r\n      source.onended = () => {\r\n        if (segment === plan.segments.at(-1) && state === 'playing' && !endedCalled) {\r\n          endedCalled = true;\r\n          baseSeconds = plan.durationSeconds;\r\n          state = 'ended';\r\n          clockEpoch++;\r\n          try { onEnded?.(); } catch { /* consumer callback failures do not corrupt the clock */ }\r\n        }\r\n      };\r\n      scheduled.push(source);\r\n    }\r\n    sources = scheduled;\r\n  };\r\n  const handleVisibility = () => {\r\n    if (!visible() && state === 'playing') void session.pause();\r\n  };\r\n  const handleContextState = () => {\r\n    if (context?.state === 'suspended' && state === 'playing') {\r\n      baseSeconds = sessionSeconds();\r\n      state = 'paused';\r\n      clockEpoch++;\r\n    }\r\n  };\r\n  const ensureContext = () => {\r\n    if (!context) {\r\n      context = makeAudioContext();\r\n      output = context.createGain();output.connect(context.destination);syncVolume();\r\n      context.addEventListener?.('statechange', handleContextState);\r\n    }\r\n    return context;\r\n  };\r\n\r\n  const session = {\r\n    async unlock() {\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      try {\r\n        ensureContext();\r\n        await loadBuffers({ resumeContext: state !== 'paused' });\r\n        if (disposed) throw new Error('This music session has been disposed.');\r\n        return true;\r\n      } catch (error) { reportError(error); throw error; }\r\n    },\r\n    async start() {\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      if (!visible()) throw new Error('Return to the visible page before starting Dance.');\r\n      if (state === 'playing') return;\r\n      try {\r\n        await session.unlock();\r\n        baseSeconds = 0;\r\n        endedCalled = false;\r\n        errorCalled = false;\r\n        startedAt = context.currentTime + 0.08;\r\n        schedule(startedAt);\r\n        state = 'playing';\r\n        clockEpoch++;\r\n      } catch (error) { reportError(error); throw error; }\r\n    },\r\n    async pause() {\r\n      if (disposed || state !== 'playing') return;\r\n      baseSeconds = sessionSeconds();\r\n      state = 'paused';\r\n      clockEpoch++;\r\n      await context?.suspend();\r\n    },\r\n    async resume() {\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      if (!visible()) throw new Error('Dance resumes only while the page is visible.');\r\n      if (state !== 'paused') return;\r\n      try {\r\n        if (!buffers.size) await loadBuffers({ resumeContext: false });\r\n        if (disposed) throw new Error('This music session has been disposed.');\r\n        await context.resume();\r\n        startedAt = context.currentTime - baseSeconds;\r\n        state = 'playing';\r\n        clockEpoch++;\r\n      } catch (error) { reportError(error); throw error; }\r\n    },\r\n    async restart() {\r\n      if (disposed) throw new Error('This music session has been disposed.');\r\n      if (!visible()) throw new Error('Return to the visible page before restarting Dance.');\r\n      try {\r\n        await session.unlock();\r\n        await context.resume();\r\n        if (disposed) throw new Error('This music session has been disposed.');\r\n        stopSources();\r\n        baseSeconds = 0;\r\n        startedAt = context.currentTime + 0.08;\r\n        endedCalled = false;\r\n        schedule(startedAt);\r\n        state = 'playing';\r\n        clockEpoch++;\r\n      } catch (error) { reportError(error); throw error; }\r\n    },\r\n    snapshot() {\r\n      syncVolume();\r\n      const seconds = sessionSeconds();\r\n      return {\r\n        state,\r\n        sessionSeconds: seconds,\r\n        audioNowSeconds: context?.currentTime ?? 0,\r\n        performanceNowMs: globalThis.performance?.now?.() ?? 0,\r\n        clockEpoch,\r\n        activeOccurrenceId: activeOccurrence(seconds),\r\n      };\r\n    },\r\n    async dispose() {\r\n      if (disposed) return;\r\n      disposed = true;\r\n      state = 'disposed';\r\n      clockEpoch++;\r\n      if (typeof document !== 'undefined') document.removeEventListener('visibilitychange', handleVisibility);\r\n      context?.removeEventListener?.('statechange', handleContextState);\r\n      stopSources();\r\n      buffers.clear();\r\n      output?.disconnect();output=null;\r\n      if (context && context.state !== 'closed') await context.close().catch(() => {});\r\n      context = null;\r\n    },\r\n  };\r\n  if (typeof document !== 'undefined') document.addEventListener('visibilitychange', handleVisibility);\r\n  return session;\r\n}\r\n\r\nexport const musicPlanInternals = Object.freeze({ canonicalJson });\r\n","modules/dance/music/spotify-transport.mjs":"const READ_OPTIONS = Object.freeze({ credentials: 'same-origin', cache: 'no-store' });\r\nconst COMMANDS = new Set(['pause', 'resume', 'next', 'previous', 'seek', 'volume', 'shuffle', 'repeat', 'track', 'queue', 'playlist']);\r\n\r\nconst accountKey = (account) => {\r\n  const id = account?.user?.id;\r\n  return typeof id === 'string' && id && Number.isSafeInteger(account?.dataEpoch)\r\n    ? `${id}:${account.dataEpoch}`\r\n    : null;\r\n};\r\n\r\nconst unavailableTimeline = (reason) => Object.freeze({ available: false, reason });\r\n\r\n/**\r\n * A read-only estimate of Spotify's reported progress. It never derives BPM or\r\n * a beat grid. Call observe only with a completed, ordered API response.\r\n */\r\nexport function createSpotifyTimeline({ now = () => performance.now(), maxStaleMs = 2000, maxUncertaintyMs = 250 } = {}) {\r\n  let sample = null;\r\n  let epoch = 0;\r\n\r\n  function observe(nowPlaying, { sentAt, receivedAt, owner } = {}) {\r\n    if (!nowPlaying || typeof nowPlaying !== 'object' || !Number.isFinite(sentAt) || !Number.isFinite(receivedAt) || receivedAt < sentAt || !owner) {\r\n      throw new TypeError('A Spotify response and ordered request timestamps are required.');\r\n    }\r\n    if (sample && sample.owner !== owner) throw new Error('Spotify account changed.');\r\n    if (sample && sentAt < sample.sentAt) throw new Error('Out-of-order Spotify response.');\r\n\r\n    const progressMs = Number(nowPlaying.progressMs);\r\n    const durationMs = Number(nowPlaying.durationMs);\r\n    if (typeof nowPlaying.track?.id !== 'string' || !nowPlaying.track.id || !Number.isFinite(progressMs) || progressMs < 0 || !Number.isFinite(durationMs) || durationMs <= 0) {\r\n      throw new TypeError('Spotify progress is invalid.');\r\n    }\r\n    const trackId = nowPlaying.track?.id ?? null;\r\n    let reset = !sample || sample.trackId !== trackId;\r\n    if (sample && !reset) {\r\n      const elapsed = Math.max(0, receivedAt - sample.receivedAt);\r\n      const expected = sample.progressMs + (sample.playing ? elapsed : 0);\r\n      const drift = Math.abs(progressMs - expected);\r\n      if (drift > 1200 || (sample.playing && nowPlaying.playing && progressMs + 1200 < sample.progressMs)) reset = true;\r\n    }\r\n    if (reset) epoch++;\r\n\r\n    const rttMs = receivedAt - sentAt;\r\n    sample = {\r\n      owner, sentAt, receivedAt, trackId, playing: !!nowPlaying.playing,\r\n      progressMs, durationMs, rttMs, epoch,\r\n    };\r\n    return snapshot(receivedAt);\r\n  }\r\n\r\n  function snapshot(at = now(), owner = null) {\r\n    if (!sample) return unavailableTimeline('no_playback_sample');\r\n    if (owner && sample.owner !== owner) return unavailableTimeline('account_changed');\r\n    const ageMs = at - sample.receivedAt;\r\n    if (!Number.isFinite(ageMs) || ageMs < 0 || ageMs > maxStaleMs) return unavailableTimeline('stale_playback_sample');\r\n    const uncertaintyMs = Math.min(maxUncertaintyMs, Math.ceil(sample.rttMs / 2));\r\n    const estimate = sample.progressMs + uncertaintyMs + (sample.playing ? ageMs : 0);\r\n    return Object.freeze({\r\n      available: true,\r\n      trackId: sample.trackId,\r\n      progressMs: Math.min(sample.durationMs || estimate, Math.max(0, estimate)),\r\n      durationMs: sample.durationMs,\r\n      playing: sample.playing,\r\n      uncertaintyMs,\r\n      ageMs,\r\n      epoch: sample.epoch,\r\n    });\r\n  }\r\n\r\n  function reset() { sample = null; epoch++; }\r\n\r\n  return Object.freeze({ observe, snapshot, reset });\r\n}\r\n\r\n/**\r\n * Spotify Connect transport for explicit user actions. This module keeps no\r\n * OAuth tokens, starts no polling, and cannot transfer playback to a device.\r\n */\r\nexport function createSpotifyTransport({ request = fetch, getAccount = () => null, now = () => performance.now(), syncAuthorization = false } = {}) {\r\n  if (typeof request !== 'function' || typeof getAccount !== 'function' || typeof now !== 'function') {\r\n    throw new TypeError('request, getAccount, and now must be functions.');\r\n  }\r\n\r\n  let disposed = false;\r\n  let readRevision = 0;\r\n  let lastReadStartedAt = -Infinity;\r\n  let lastReadOwner = null;\r\n  let timelineOwner = null;\r\n  const listeners = new Set();\r\n  const timeline = createSpotifyTimeline({ now });\r\n  let state = Object.freeze({ connected: null, account: null, nowPlaying: null, devices: Object.freeze([]), error: null });\r\n\r\n  const assertActive = () => { if (disposed) throw new Error('Spotify transport is disposed.'); };\r\n  const publish = (patch) => {\r\n    state = Object.freeze({ ...state, ...patch });\r\n    for (const listener of listeners) listener(state);\r\n    return state;\r\n  };\r\n  const capture = () => {\r\n    const account = getAccount();\r\n    return { account, owner: accountKey(account) };\r\n  };\r\n  const assertSameOwner = (captured) => {\r\n    if (captured.owner !== accountKey(getAccount())) throw new Error('Spotify account changed.');\r\n  };\r\n\r\n  async function call(path, { method = 'GET', body, captured = capture() } = {}) {\r\n    assertActive();\r\n    const headers = new Headers();\r\n    if (body !== undefined) headers.set('Content-Type', 'application/json');\r\n    if (method !== 'GET' && method !== 'HEAD') {\r\n      const { user, dataEpoch } = captured.account || {};\r\n      if (!captured.owner || typeof user?.id !== 'string' || !Number.isSafeInteger(dataEpoch)) {\r\n        throw new Error('Sign in to Coach before controlling Spotify.');\r\n      }\r\n      headers.set('X-Target-Account', user.id);\r\n      headers.set('X-Expected-Data-Epoch', String(dataEpoch));\r\n    }\r\n    const response = await request(path, {\r\n      ...READ_OPTIONS,\r\n      method,\r\n      headers,\r\n      ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n    });\r\n    assertSameOwner(captured);\r\n    const data = await response.json().catch(() => ({}));\r\n    if (!response.ok) throw Object.assign(new Error(data.error || 'Spotify is unavailable.'), { code: data.code, status: response.status });\r\n    return data;\r\n  }\r\n\r\n  async function status() {\r\n    const captured = capture();\r\n    try {\r\n      const data = await call('/api/spotify/status', { captured });\r\n      return publish({ connected: !!data.connected, account: data.account || null, error: null });\r\n    } catch (error) {\r\n      publish({ error: error.message });\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  async function devices() {\r\n    const captured = capture();\r\n    try {\r\n      const data = await call('/api/spotify/devices', { captured });\r\n      return publish({ devices: Object.freeze(Array.isArray(data.items) ? data.items.slice() : []), error: null });\r\n    } catch (error) {\r\n      publish({ error: error.message });\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  async function read() {\r\n    const captured = capture();\r\n    if (timelineOwner && timelineOwner !== captured.owner) {\r\n      timeline.reset();\r\n      timelineOwner = null;\r\n    }\r\n    const revision = ++readRevision;\r\n    try {\r\n      const accountState = await call('/api/spotify/status', { captured });\r\n      if (revision !== readRevision) return state;\r\n      if (!accountState.connected) {\r\n        timeline.reset();\r\n        timelineOwner = null;\r\n        return publish({ connected: false, account: null, nowPlaying: null, error: null });\r\n      }\r\n      const sentAt = now();\r\n      if (sentAt < lastReadStartedAt) throw new Error('Out-of-order Spotify request.');\r\n      lastReadStartedAt = sentAt;\r\n      lastReadOwner = captured.owner;\r\n      const data = await call('/api/spotify/now-playing', { captured });\r\n      const receivedAt = now();\r\n      assertSameOwner(captured);\r\n      if (revision !== readRevision || captured.owner !== lastReadOwner) return state;\r\n      const nowPlaying = data.nowPlaying || null;\r\n      if (nowPlaying?.track?.id && captured.owner) {\r\n        timeline.observe(nowPlaying, { sentAt, receivedAt, owner: captured.owner });\r\n        timelineOwner = captured.owner;\r\n      } else {\r\n        timeline.reset();\r\n        timelineOwner = null;\r\n      }\r\n      return publish({ connected: true, account: accountState.account || null, nowPlaying, error: null });\r\n    } catch (error) {\r\n      publish({ error: error.message });\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  async function command(action, payload = {}) {\r\n    if (!COMMANDS.has(action)) throw new TypeError('Unsupported Spotify command.');\r\n    if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw new TypeError('Spotify command payload must be an object.');\r\n    const captured = capture();\r\n    try {\r\n      const result = await call('/api/spotify/playback', { method: 'POST', body: { ...payload, action }, captured });\r\n      publish({ error: null });\r\n      return result;\r\n    } catch (error) {\r\n      publish({ error: error.message });\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  return Object.freeze({\r\n    status,\r\n    devices,\r\n    read,\r\n    command,\r\n    subscribe(listener) {\r\n      assertActive();\r\n      if (typeof listener !== 'function') throw new TypeError('Listener must be a function.');\r\n      listeners.add(listener);\r\n      listener(state);\r\n      return () => listeners.delete(listener);\r\n    },\r\n    snapshot: () => state,\r\n    timeline: () => syncAuthorization ? timeline.snapshot(now(), accountKey(getAccount())) : unavailableTimeline('spotify_approval_required'),\r\n    dispose() { disposed = true; listeners.clear(); readRevision++; },\r\n  });\r\n}\r\n","modules/dance/pose/bridge.mjs":"import { extractUpperBodyFeatures, UPPER_BODY_LANDMARKS, UPPER_BODY_PROFILE } from '../content/features.mjs';\r\n/* Real MYR5 observations only. No camera, detector, reference-to-player path or capture-time claim. */\r\nconst finite = Number.isFinite;\r\nconst clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));\r\nconst REQUIRED = UPPER_BODY_LANDMARKS;\r\nconst mid = (a, b) => ({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });\r\nconst distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);\r\nconst median = a => [...a].sort((x, y) => x - y)[Math.floor(a.length / 2)];\r\nfunction usable(p) {\r\n  return !!p && finite(p.x) && finite(p.y) && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1\r\n    && finite(p.visibility) && p.visibility >= .6 && (p.presence == null || (finite(p.presence) && p.presence >= .6));\r\n}\r\n// Monotone dynamic alignment tolerates modest reaction delay while retaining feature order.\r\n// Timing is independently scored from observed extrema, without any fitted offset.\r\nfunction sequenceError(observed, reference, features, ranges) {\r\n  const n = observed.length, m = reference.length;\r\n  let previous = new Float64Array(m + 1).fill(Infinity); previous[0] = 0;\r\n  for (let i = 1; i <= n; i++) {\r\n    const row = new Float64Array(m + 1).fill(Infinity);\r\n    for (let j = 1; j <= m; j++) {\r\n      let error = 0, weight = 0;\r\n      for (const f of features) {\r\n        const a = observed[i - 1].values[f.id], b = reference[j - 1][f.id];\r\n        if (!finite(a) || !finite(b)) continue;\r\n        const w = f.weight || 1;\r\n        const tolerance = Math.max(f.parameters?.tolerance || 0, ranges[f.id] * .35, .06);\r\n        error += Math.min(2, Math.abs(a - b) / tolerance) * w; weight += w;\r\n      }\r\n      const cost = weight ? error / weight : 2;\r\n      row[j] = cost + Math.min(previous[j - 1], previous[j] + .025, row[j - 1] + .025);\r\n    }\r\n    previous = row;\r\n  }\r\n  return previous[m] / Math.max(n, m);\r\n}\r\n\r\nexport function createPoseBridge({ getClock, getActiveSlot, getTarget, onFeedback = () => {} }) {\r\n  if (![getClock, getActiveSlot, getTarget].every(f => typeof f === 'function')) throw new TypeError('Pose bridge requires clock, slot and target getters');\r\n  let disposed = false, calibration = null, calibrating = false, calibrationFrames = [];\r\n  let clockEpoch = null, epochStartMs = -Infinity, lastInputMs = null, lastReceivedMs = null, lastSession = null;\r\n  let acceptedSamples = 0, cycle = null, playerLandmarks = null, latest = null, lastClockPerf = null, lastGeometry = null;\r\n  function clock() { return getClock() || {}; }\r\n  function output(status, reasonCodes = [], extra = {}, emit = true) {\r\n    const c = clock();\r\n    latest = { status, moveId: getActiveSlot()?.moveId || null, sequence: null, timing: null, effort: null,\r\n      coverage: 0, timingKind: 'unavailable', reasonCodes, correction: null, acceptedSamples,\r\n      clockEpoch: c.clockEpoch ?? null, calibrated: !!calibration, playerLandmarks, ...extra };\r\n    if (emit) onFeedback(latest); return latest;\r\n  }\r\n  function resetEpoch(emit = true) {\r\n    const c = clock(); clockEpoch = c.clockEpoch ?? null; epochStartMs = finite(c.performanceNowMs) ? c.performanceNowMs : -Infinity;\r\n    lastInputMs = null; lastReceivedMs = null; lastSession = null; lastClockPerf = null; lastGeometry = null; cycle = null; playerLandmarks = null;\r\n    return output(c.state === 'paused' ? 'paused' : calibration ? 'tracking_limited' : 'calibrating', ['awaiting_fresh_input'], {}, emit);\r\n  }\r\n  function startCalibration() {\r\n    if (disposed) return latest;\r\n    calibration = null; calibrating = true; calibrationFrames = []; acceptedSamples = 0;\r\n    resetEpoch(); return output('calibrating', ['hold_upper_body_in_frame'], { correction: 'Face the camera about 3 ft away; hold shoulders, elbows and both hands in view.' });\r\n  }\r\n  function invalidate(reason) {\r\n    calibration = null; calibrating = true; calibrationFrames = []; cycle = null; playerLandmarks = null;\r\n    return output('calibrating', [reason], { correction: 'Face the camera and hold shoulders, elbows and both hands in view to reframe.' });\r\n  }\r\n  function ingest(detail) {\r\n    if (disposed) return latest;\r\n    const c = clock();\r\n    if (c.clockEpoch !== clockEpoch) resetEpoch();\r\n    if (!calibrating && c.state !== 'playing') return output('paused', ['playback_not_playing']);\r\n    if (!detail || !finite(detail.now) || !finite(c.performanceNowMs)) return output('tracking_limited', ['invalid_clock_pair']);\r\n    if (lastClockPerf !== null && c.performanceNowMs < lastClockPerf) return output('tracking_limited', ['out_of_order_clock']);\r\n    lastClockPerf = c.performanceNowMs;\r\n    if (detail.now < epochStartMs) return output('tracking_limited', ['prior_epoch_input']);\r\n    if (detail.now > c.performanceNowMs) return output('tracking_limited', ['future_input']);\r\n    if (lastInputMs !== null && detail.now <= lastInputMs) return output('tracking_limited', [detail.now === lastInputMs ? 'duplicate_input' : 'out_of_order_input']);\r\n    lastInputMs = detail.now;\r\n    const age = (c.performanceNowMs - detail.now) / 1000;\r\n    if (age > .2) return output('tracking_limited', ['stale_input']);\r\n    if (!finite(detail.width) || detail.width <= 0 || !finite(detail.height) || detail.height <= 0 || typeof detail.mirrored !== 'boolean'\r\n      || !Array.isArray(detail.points) || detail.points.length !== 33) return output('tracking_limited', ['malformed_frame']);\r\n    const points = detail.points.map(p => usable(p) ? ({ x: p.x * detail.width / detail.height, y: p.y, visibility: p.visibility }) : null);\r\n    const visible = REQUIRED.filter(i => points[i]).length / REQUIRED.length;\r\n    if (visible !== 1) {\r\n      calibrationFrames = [];\r\n      if (lastReceivedMs !== null && c.performanceNowMs - lastReceivedMs > 1200) cycle = null;\r\n      lastReceivedMs = c.performanceNowMs;\r\n      return output(calibrating ? 'calibrating' : 'tracking_limited', ['upper_body_not_visible'], { coverage: visible,\r\n        correction: !points[15] || !points[16] ? 'Keep both hands inside the frame.' : 'Keep shoulders and elbows in view.' });\r\n    }\r\n    const shoulder = mid(points[11], points[12]);\r\n    const shoulderWidth = distance(points[11], points[12]), sign = Math.sign(points[12].x - points[11].x);\r\n    if (shoulderWidth < .08 || !sign || Math.abs(points[12].x - points[11].x) < shoulderWidth * .65) return output('tracking_limited', ['face_camera']);\r\n    if (calibration && (calibration.width !== detail.width || calibration.height !== detail.height || calibration.mirrored !== detail.mirrored)) return invalidate('orientation_changed');\r\n    if (calibration && sign !== calibration.sign) return invalidate('body_orientation_changed');\r\n    if (calibrating || !calibration) {\r\n      calibrating = true;\r\n      lastReceivedMs = c.performanceNowMs;\r\n      const prior = calibrationFrames.at(-1);\r\n      if (prior && (detail.now - prior.time > 900 || sign !== prior.sign || prior.width !== detail.width || prior.height !== detail.height || prior.mirrored !== detail.mirrored)) calibrationFrames = [];\r\n      calibrationFrames.push({ time: detail.now, shoulderWidth, shoulder, arms: REQUIRED.map(i => points[i]), sign, width: detail.width, height: detail.height, mirrored: detail.mirrored });\r\n      calibrationFrames = calibrationFrames.filter(f => detail.now - f.time <= 4000);\r\n      const scale = median(calibrationFrames.map(f => f.shoulderWidth));\r\n      const stable = calibrationFrames.every(f => Math.abs(f.shoulderWidth - scale) < scale * .09 && distance(f.shoulder, shoulder) < scale * .12 && f.arms.every((p, i) => distance(p, points[REQUIRED[i]]) < scale * .14));\r\n      if (!stable) { calibrationFrames = [calibrationFrames.at(-1)]; return output('calibrating', ['hold_still'], { correction: 'Hold still for a moment.' }); }\r\n      if (calibrationFrames.length < 6 || detail.now - calibrationFrames[0].time < 800) return output('calibrating', ['sustained_calibration_required'], { coverage: 1, correction: 'Hold still with shoulders, elbows and both hands visible.' });\r\n      calibration = { scale, shoulder: { x: median(calibrationFrames.map(f => f.shoulder.x)), y: median(calibrationFrames.map(f => f.shoulder.y)) }, sign,\r\n        width: detail.width, height: detail.height, mirrored: detail.mirrored };\r\n      calibrating = false; calibrationFrames = []; lastReceivedMs = c.performanceNowMs;\r\n      return output('tracking', ['calibration_ready'], { coverage: 1, correction: 'Ready. Follow the coach.' });\r\n    }\r\n    // Display mirroring never changes anatomical indices. Lock one observed lateral mapping at calibration.\r\n    const norm = points.map(p => p ? ({ x: (p.x - calibration.shoulder.x) * calibration.sign / calibration.scale,\r\n      y: (p.y - calibration.shoulder.y) / calibration.scale, visibility: p.visibility }) : null);\r\n    const geometry = detail.points.map(p => p ? `${p.x},${p.y}` : '-').join(';');\r\n    if (geometry === lastGeometry) return output('tracking_limited', ['duplicate_frame']);\r\n    lastGeometry = geometry;\r\n    playerLandmarks = norm;\r\n    const slot = getActiveSlot();\r\n    const session = c.sessionSeconds - age;\r\n    if (!finite(session) || session < 0 || (lastSession !== null && session <= lastSession)) return output('tracking_limited', ['nonmonotonic_session_time']);\r\n    lastSession = session; lastReceivedMs = c.performanceNowMs;\r\n    if (!slot || !slot.moveId || slot.scored === false || slot.role !== 'move') return output('tracking', ['unscored_interval'], { coverage: 1 });\r\n    if (session < slot.startSessionSeconds || session >= slot.endSessionSeconds) return output('tracking_limited', ['input_outside_active_slot']);\r\n    const target = getTarget(slot.moveId);\r\n    if (!target?.features?.length) return output('tracking_limited', ['target_unavailable']);\r\n    if (target.profile !== UPPER_BODY_PROFILE) return output('tracking_limited', ['unsupported_target_profile']);\r\n    const required = target.requiredLandmarks || REQUIRED;\r\n    const coverage = required.filter(i => norm[i]).length / required.length;\r\n    if (coverage < 1) return output('tracking_limited', ['required_landmark_missing'], { coverage, correction: 'Keep both shoulders, elbows and hands in view.' });\r\n    const duration = (slot.endSessionSeconds - slot.startSessionSeconds) / (slot.repeatCount || 1);\r\n    if (!finite(duration) || duration <= 0) return output('tracking_limited', ['invalid_slot']);\r\n    const number = Math.floor((session - slot.startSessionSeconds) / duration), start = slot.startSessionSeconds + number * duration;\r\n    const phase = clamp((session - start) / duration);\r\n    if (!cycle || cycle.key !== `${slot.id}:${number}`) {\r\n      const reference = Array.from({ length: 41 }, (_, i) => getTarget(slot.moveId, i / 40)?.features || {});\r\n      const features = target.features;\r\n      const ranges = Object.fromEntries(features.map(f => { const a = reference.map(r => r[f.id]).filter(finite); return [f.id, a.length ? Math.max(...a) - Math.min(...a) : 0]; }));\r\n      const expected = (target.accents || []).map((a, index) => ({ ...a, index, time: start + a.beat / target.beats * duration }));\r\n      cycle = { key: `${slot.id}:${number}`, start, duration, reference, features, ranges, expected, samples: [], extrema: {}, events: [], matched: new Set() };\r\n    }\r\n    const observed = extractUpperBodyFeatures(norm);\r\n    const values = Object.fromEntries(cycle.features.map(f => [f.id, observed[f.id]]));\r\n    if (cycle.features.some(f => !finite(values[f.id]))) return output('tracking_limited', ['feature_unavailable'], { coverage });\r\n    const priorSample = cycle.samples.at(-1);\r\n    if (priorSample && session - priorSample.time > .35) { cycle.samples = []; cycle.extrema = {}; cycle.events = []; cycle.matched.clear(); }\r\n    cycle.samples.push({ time: session, phase, values }); acceptedSamples++;\r\n    if (cycle.samples.length > 900) cycle.samples.shift();\r\n    for (const f of cycle.features) {\r\n      const range = cycle.ranges[f.id]; if (range < .035) continue;\r\n      const value = values[f.id], threshold = Math.max(.015, range * .12);\r\n      let e = cycle.extrema[f.id];\r\n      if (!e) { cycle.extrema[f.id] = { low: value, high: value, lowTime: session, highTime: session, direction: 0 }; continue; }\r\n      if (value < e.low) { e.low = value; e.lowTime = session; }\r\n      if (value > e.high) { e.high = value; e.highTime = session; }\r\n      let event = null;\r\n      if (e.direction >= 0 && value < e.high - threshold) { if (e.direction === 1) event = { featureId: f.id, polarity: 1, time: e.highTime }; e.direction = -1; e.low = value; e.lowTime = session; }\r\n      else if (e.direction <= 0 && value > e.low + threshold) { if (e.direction === -1) event = { featureId: f.id, polarity: -1, time: e.lowTime }; e.direction = 1; e.high = value; e.highTime = session; }\r\n      if (event) {\r\n        const candidates = cycle.expected.filter(a => a.featureId === event.featureId && !cycle.matched.has(a.index)\r\n          && (a.polarity === event.polarity || a.polarity === (event.polarity > 0 ? 'max' : 'min')))\r\n          .sort((a, b) => Math.abs(a.time - event.time) - Math.abs(b.time - event.time));\r\n        const expected = candidates[0], window = duration / target.beats * .8;\r\n        if (expected && Math.abs(expected.time - event.time) <= window) { cycle.matched.add(expected.index); cycle.events.push({ ...event, expected: expected.time, error: event.time - expected.time, score: clamp(1 - Math.abs(event.time - expected.time) / window) }); }\r\n      }\r\n    }\r\n    const samples = cycle.samples, span = samples.at(-1).time - samples[0].time;\r\n    const temporalCoverage = Math.min(1, span / duration);\r\n    const dynamic = cycle.features.filter(f => cycle.ranges[f.id] >= .035);\r\n    const excursion = dynamic.length && dynamic.every(f => {\r\n      const a = samples.map(s => s.values[f.id]); return Math.max(...a) - Math.min(...a) >= cycle.ranges[f.id] * .55;\r\n    });\r\n    if (samples.length < 12 || temporalCoverage < .7) return output('tracking', ['collecting_movement'], { coverage: temporalCoverage, correction: 'Keep following through the full move.' });\r\n    if (!excursion) return output('tracking', ['insufficient_excursion'], { sequence: 0, effort: 0, coverage: temporalCoverage, correction: 'Follow the full movement with the coach.' });\r\n    // Gesture order is an anatomical shape constraint, independent of beat offset.\r\n    // A half-cycle swap must not earn points by dynamic alignment around the loop.\r\n    const orderedAccents = (target.accents || []).filter(a => cycle.ranges[a.featureId] >= .035);\r\n    if (orderedAccents.length >= 2 && orderedAccents[0].featureId !== orderedAccents[1].featureId) {\r\n      const extremaTimes = orderedAccents.slice(0, 2).map(a => samples.reduce((best, s) =>\r\n        a.polarity * s.values[a.featureId] > a.polarity * best.values[a.featureId] ? s : best, samples[0]).time);\r\n      if (extremaTimes[0] >= extremaTimes[1] - duration * .08) return output('tracking', ['feature_order_mismatch'], {\r\n        sequence: 0, effort: 0, timing: null, coverage: temporalCoverage,\r\n        correction: 'Start with your left arm, return, then use your right arm.' });\r\n    }\r\n    const end = Math.max(1, Math.round(phase * 40));\r\n    const sampled = samples.filter((_, i) => i % Math.max(1, Math.floor(samples.length / 80)) === 0);\r\n    const error = sequenceError(sampled, cycle.reference.slice(0, end + 1), cycle.features, cycle.ranges);\r\n    const sequence = Math.round(100 * clamp(1 - error));\r\n    const effort = Math.round(100 * dynamic.reduce((sum, f) => { const a = samples.map(s => s.values[f.id]); return sum + clamp((Math.max(...a) - Math.min(...a)) / cycle.ranges[f.id]); }, 0) / dynamic.length);\r\n    const timing = cycle.events.length ? Math.round(100 * cycle.events.reduce((sum, e) => sum + e.score, 0) / cycle.events.length) : null;\r\n    const avgError = cycle.events.length ? cycle.events.reduce((sum, e) => sum + e.error, 0) / cycle.events.length : 0;\r\n    return output('tracking', sequence < 50 ? ['feature_order_mismatch'] : [], { sequence, effort, timing, coverage: temporalCoverage,\r\n      timingKind: timing === null ? 'unavailable' : 'estimated_input', correction: sequence < 50 ? 'Check the side and follow the order of the coach.' : timing !== null && timing < 60 ? (avgError > 0 ? 'Try moving a little earlier with the beat.' : 'Try waiting for the beat.') : 'Keep moving with the coach.' });\r\n  }\r\n  function snapshot() {\r\n    if (disposed) return latest;\r\n    const c = clock();\r\n    if (c.clockEpoch !== clockEpoch) return resetEpoch(false);\r\n    if (!calibrating && c.state !== 'playing') return output('paused', ['playback_not_playing'], {}, false);\r\n    if (lastReceivedMs === null || c.performanceNowMs - lastReceivedMs > (calibrating ? 900 : 350)) return output(calibrating ? 'calibrating' : 'tracking_limited', ['no_recent_pose'], { correction: 'Keep shoulders, elbows and both hands in view.' }, false);\r\n    return latest || output('tracking_limited', ['no_input'], {}, false);\r\n  }\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true; cycle = null; calibrationFrames = []; playerLandmarks = null;\r\n    latest = { ...latest, status: 'paused', sequence: null, timing: null, effort: null, coverage: 0,\r\n      timingKind: 'unavailable', reasonCodes: ['disposed'], correction: null, playerLandmarks: null };\r\n  }\r\n  resetEpoch(false);\r\n  return { startCalibration, ingest, resetEpoch, snapshot, dispose };\r\n}\r\n","modules/dance/pose/index.mjs":"export { createPoseBridge } from './bridge.mjs';\r\n","modules/dance/progress.mjs":"const KEY='myr5-dance-progress-v1';\r\nexport function loadProgress(storage){try{storage??=globalThis.localStorage;const v=JSON.parse(storage.getItem(KEY));return v?.version===1&&Array.isArray(v.sessions)?{...v,practice:Array.isArray(v.practice)?v.practice:[]}:{version:1,sessions:[],practice:[]};}catch{return {version:1,sessions:[],practice:[]};}}\r\nexport function saveResult(result,storage){const p=loadProgress(storage);if(!p.sessions.some(s=>s.id===result.id))p.sessions.unshift(result);p.sessions=p.sessions.slice(0,100);try{storage??=globalThis.localStorage;storage.setItem(KEY,JSON.stringify(p));return {progress:p,saved:true};}catch{return {progress:p,saved:false};}}\r\nexport function savePractice(id,storage){const p=loadProgress(storage);p.practice=[...new Set([...p.practice,id])];try{storage??=globalThis.localStorage;storage.setItem(KEY,JSON.stringify(p));}catch{}return p;}\r\n","modules/dance/ui/dance-ui.css":"/* The selected Coach owns the dance floor. Cyan + yellow arm cues stay legible across it. */\r\n.dance-root {\r\n  --dance-ink:#fff; --dance-muted:#c4d6e3; --dance-mint:#5bf5ff; --dance-amber:#ffe54f;\r\n  --dance-lilac:#e3edff; --dance-edge:#86d4ee55; --dance-glass:#071722e8;\r\n  position:relative; isolation:isolate; display:flex; flex-direction:column; gap:12px;\r\n  box-sizing:border-box; width:100%; min-width:0; min-height:100dvh;\r\n  padding:calc(10px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) calc(106px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left));\r\n  color:var(--dance-ink); background:radial-gradient(ellipse at 10% 0%,#08617466,transparent 45%),radial-gradient(ellipse at 100% 28%,#ae316c44,transparent 50%),#06121d;\r\n  font-family:inherit; overflow-x:clip;\r\n}\r\n.dance-root *, .dance-root *::before, .dance-root *::after {box-sizing:border-box;min-width:0}\r\n.dance-root h2 {margin:0 0 6px;font-size:clamp(28px,5vw,38px);line-height:1.05;letter-spacing:-.8px}\r\n.dance-root p {margin:5px 0;font-size:16px;line-height:1.4;overflow-wrap:anywhere}\r\n.dance-root a {color:var(--dance-mint);min-height:44px;display:inline-flex;align-items:center}\r\n.dance-kicker {font-size:12px!important;letter-spacing:1.6px;font-weight:850;text-transform:uppercase;color:var(--dance-mint);margin:0 0 6px!important}\r\n.dance-muted {color:var(--dance-muted)}\r\n.dance-note {font-size:13px!important}\r\n.dance-error-text {color:#ffcd89;font-size:14px}\r\n\r\n/* Persistent stage; decoration breathes slowly, never flashes. */\r\n.dance-stage {position:relative;flex:none;height:calc(100dvh - 190px - env(safe-area-inset-top) - env(safe-area-inset-bottom));min-height:420px;overflow:hidden;border:1px solid #8fdbf677;border-radius:24px;background:radial-gradient(ellipse at 50% 35%,#204e6f99,transparent 65%),#05101b;box-shadow:inset 0 1px #fff3,0 16px 48px #0005}\r\n.dance-stage[hidden] {display:none}\r\n.dance-lights {position:absolute;inset:0;overflow:hidden;pointer-events:none;opacity:var(--dance-beat,.65)}\r\n.dance-lights i {position:absolute;top:-25%;left:20%;width:26%;height:105%;background:linear-gradient(#5bf5ff88,#5bf5ff00);clip-path:polygon(42% 0,58% 0,100% 100%,0 100%);transform-origin:50% 0;filter:blur(6px);animation:dance-beam 13s ease-in-out infinite alternate}\r\n.dance-lights i:nth-child(2) {left:60%;background:linear-gradient(#ff4cb677,#ff4cb600);animation-delay:-5s;animation-duration:17s}\r\n.dance-lights i:nth-child(3) {left:42%;background:linear-gradient(#ffe54f66,#ffe54f00);animation-delay:-9s;animation-duration:19s}\r\n.dance-lights b {position:absolute;left:-40%;right:-40%;bottom:18%;height:22%;border-radius:50%;background:repeating-conic-gradient(from 30deg,#44dfff33 0deg 15deg,transparent 15deg 30deg);transform:perspective(150px) rotateX(50deg);box-shadow:0 0 70px #46dbff55}\r\n@keyframes dance-beam {from{transform:rotate(-28deg)}to{transform:rotate(28deg)}}\r\n.dance-coach-host {position:absolute;left:0;right:0;top:0;height:69%;z-index:1;pointer-events:none;overflow:hidden}\r\n.dance-coach-host canvas {display:block;width:100%!important;height:100%!important;object-fit:contain}\r\n.dance-coach-state {position:absolute;top:16%;left:20px;z-index:3;padding:7px 10px;border:1px solid #fff5;border-radius:999px;background:#06121de6;color:#e5f5ff;font-size:14px;font-weight:700}\r\n.dance-coach-state[hidden] {display:none}\r\n.dance-canvas {position:absolute;inset:0;z-index:2;width:100%;height:100%;pointer-events:none}\r\n.dance-video-host {position:absolute;top:58px;right:12px;width:24%;max-width:122px;aspect-ratio:4/3;border-radius:14px;border:2px solid #fff9;overflow:hidden;background:#06121d;z-index:4;box-shadow:0 3px 18px #0008}\r\n.dance-video-host:empty {display:none}\r\n.dance-video-host video,.dance-video-host .dance-camera {position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#06121d}\r\n.dance-video-host[data-mirrored=\"true\"] video,.dance-video-host[data-mirrored=\"true\"] .dance-camera {transform:scaleX(-1)}\r\n.dance-hud {position:absolute;top:0;left:0;right:0;z-index:5;padding:12px;min-height:48px;display:flex;gap:8px;align-items:center;pointer-events:none;background:linear-gradient(#03101dee,transparent)}\r\n.dance-status {font-size:14px;color:var(--dance-mint);font-weight:800}\r\n.dance-count {font-size:clamp(24px,5vw,36px);font-weight:950;line-height:1.05;text-shadow:0 2px 5px #000,0 0 14px #000;overflow-wrap:anywhere}\r\n.dance-progress {position:absolute;left:0;right:0;top:0;height:5px;background:#ffffff22}\r\n.dance-progress i {display:block;height:100%;width:0;background:linear-gradient(90deg,var(--dance-mint),var(--dance-amber));box-shadow:0 0 12px #5bf5ff}\r\n\r\n/* A full-width, giant current gesture owns the bottom third. Upcoming cards travel above it. */\r\n.dance-lane {position:absolute;inset:0;z-index:3;pointer-events:none}\r\n.dance-next {position:absolute;left:0;right:0;bottom:max(36%,34dvh);height:68px;display:flex;align-items:stretch;padding:3px 10px;background:linear-gradient(90deg,#06121df5,#06121d88);border-top:1px solid #fff3;overflow:hidden}\r\n.dance-next>b {align-self:center;flex:0 0 62px;font-size:11px;line-height:1.4;letter-spacing:1px;color:var(--dance-mint);padding-right:8px}\r\n.dance-track {position:relative;flex:1;overflow:hidden}\r\n.dance-card {position:absolute;left:0;top:0;width:136px;height:62px;display:flex;align-items:center;gap:4px;padding:3px 7px;border:1px solid #b6e6ff77;border-radius:12px;background:#0e2838ed;will-change:transform}\r\n.dance-card[hidden] {display:none}\r\n.dance-card canvas {width:54px;flex:0 0 54px;height:54px}\r\n.dance-card span {font-size:13px;line-height:1.1;font-weight:850;color:#fff;overflow-wrap:anywhere}\r\n.dance-now {position:absolute;left:0;right:0;bottom:0;height:36%;min-height:34dvh;display:grid;grid-template-columns:56% 44%;align-items:stretch;background:linear-gradient(125deg,#064658f5,#071a2afa 65%);border-top:3px solid var(--dance-mint);box-shadow:0 -8px 30px #0006;overflow:hidden}\r\n.dance-now canvas {width:100%;height:100%;min-height:0;align-self:stretch}\r\n.dance-now-copy {display:flex;flex-direction:column;justify-content:center;gap:3px;padding:8px 10px 8px 0}\r\n.dance-now-label {display:flex;gap:10px;align-items:center;font-size:14px;font-weight:950;letter-spacing:2px;color:var(--dance-mint)}\r\n.dance-now-label em {font-style:normal;font-size:13px;letter-spacing:0;color:#fff9}\r\n.dance-now-copy>span {font-size:clamp(25px,6.3vw,42px);font-weight:950;line-height:1;letter-spacing:-.7px;text-wrap:balance;text-shadow:0 2px 4px #000}\r\n.dance-direction {font-size:clamp(34px,8vw,58px);line-height:1;color:var(--dance-amber);letter-spacing:8px;font-family:system-ui,sans-serif}\r\n.dance-now small {font-size:24px;font-weight:750;line-height:1.1;color:#fff;text-wrap:balance}\r\n\r\n/* Setup keeps music, intensity and the queue close to hand. */\r\n.dance-panel {display:flex;flex-direction:column;gap:10px;flex:none}\r\n.dance-panel:empty {display:none}\r\n.dance-head {display:flex;align-items:center;justify-content:space-between;gap:10px}\r\n.dance-head h2 {margin:0}\r\n.dance-section {display:flex;flex-direction:column;gap:6px;padding:12px;border:1px solid var(--dance-edge);border-radius:18px;background:var(--dance-glass)}\r\n.dance-queue {list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}\r\n.dance-queue li {display:flex;flex-wrap:wrap;align-items:center;gap:5px 8px;font-size:15px;border-bottom:1px solid #fff1;padding:3px 0}\r\n.dance-queue li>span:first-child {flex:1 1 140px;font-weight:750;overflow-wrap:anywhere}\r\n.dance-queue li>.dance-muted {font-size:12px;flex:1 1 100px}\r\n.dance-tracks {display:grid;grid-template-columns:repeat(auto-fit,minmax(146px,1fr));gap:8px}\r\n.dance-track-btn {position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:5px;text-align:left;min-height:74px;padding:12px 28px 12px 12px;border-radius:14px;border:1px solid #a5d9ef55;background:linear-gradient(130deg,#164d5ccc,#153044cc);color:#fff;font:inherit;font-size:16px;cursor:pointer}\r\n.dance-track-btn::after {content:'+';position:absolute;right:10px;top:10px;color:var(--dance-mint);font-size:24px;font-weight:800}\r\n.dance-track-btn span {font-size:12px;color:var(--dance-muted)}\r\n.dance-chips {display:flex;flex-wrap:wrap;gap:8px}\r\n.dance-chip {min-height:44px;padding:9px 14px;border-radius:999px;border:1px solid var(--dance-edge);background:#112c3c;color:#e3edff;font:inherit;font-size:15px;cursor:pointer}\r\n.dance-chip.on {border-color:var(--dance-mint);color:#00191f;background:var(--dance-mint);font-weight:850}\r\n.dance-row {display:flex;flex-wrap:wrap;gap:8px;align-items:center}\r\n.dance-btn {min-height:46px;min-width:44px;padding:11px 16px;border-radius:14px;border:1px solid #a6dbed66;background:#143448;color:#fff;font:inherit;font-size:16px;font-weight:750;cursor:pointer;touch-action:manipulation}\r\n.dance-btn.primary {border-color:var(--dance-mint);background:linear-gradient(125deg,#5bf5ff,#a0ffe3);color:#02222b;font-weight:900;box-shadow:0 3px 18px #35dbec22}\r\n.dance-btn.ghost {background:#06121d77;color:#e4f4ff}\r\n.dance-btn.small {min-height:44px;padding:8px 12px;font-size:14px}\r\n.dance-btn:disabled,.dance-track-btn:disabled {opacity:.42;cursor:default}\r\n.dance-btn:focus-visible,.dance-chip:focus-visible,.dance-track-btn:focus-visible,.dance-root input:focus-visible,.dance-root select:focus-visible,.dance-root a:focus-visible {outline:3px solid var(--dance-amber);outline-offset:3px}\r\n.dance-field {display:flex;flex-direction:column;gap:4px;font-size:14px;color:var(--dance-muted)}\r\n.dance-field input,.dance-field select,.dance-sheet>input[type=\"file\"] {min-height:46px;padding:9px 11px;border-radius:12px;border:1px solid var(--dance-edge);background:#06121d;color:#fff;font:inherit;font-size:16px;width:100%}\r\n.dance-check {display:flex;gap:10px;align-items:flex-start;font-size:14px;color:var(--dance-muted);min-height:44px}\r\n.dance-check input {width:23px;height:23px;flex:none;margin-top:2px;accent-color:var(--dance-mint)}\r\n.dance-sheet {display:flex;flex-direction:column;gap:10px;padding:14px;border-radius:18px;border:1px solid var(--dance-mint);background:var(--dance-glass)}\r\n.dance-steps {margin:0;padding-left:22px;font-size:16px;color:#fff;line-height:1.4}\r\n.dance-order {list-style:none;padding-left:0}\r\n.dance-steps li {margin:5px 0}\r\n.dance-error {padding:12px;border-radius:14px;border:1px solid #ffcd89;background:#412613f5}\r\n.dance-error p {color:#ffe2c4;margin:0 0 6px}\r\n.dance-results {display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:0}\r\n.dance-results div {padding:14px;border-radius:14px;border:1px solid var(--dance-edge);background:var(--dance-glass)}\r\n.dance-results dt {font-size:12px;letter-spacing:1px;text-transform:uppercase;color:var(--dance-mint)}\r\n.dance-results dd {margin:4px 0 0;font-size:36px;font-weight:900;color:#fff}\r\n.dance-spinner,.dance-spinner-inline {display:inline-block;border-radius:50%;border:3px solid #5bf5ff33;border-top-color:var(--dance-mint);animation:dance-spin 1.2s linear infinite}\r\n.dance-spinner {width:36px;height:36px;margin:8px 0}\r\n.dance-spinner-inline {width:16px;height:16px;margin-right:8px;vertical-align:-2px}\r\n@keyframes dance-spin {to{transform:rotate(360deg)}}\r\n\r\n.dance-root[data-phase=\"select\"] .dance-stage {height:300px;min-height:300px}\r\n.dance-root[data-phase=\"select\"] .dance-now {display:none}\r\n.dance-root[data-phase=\"select\"] .dance-coach-host {height:100%}\r\n.dance-root[data-phase=\"select\"] .dance-next {display:none}\r\n.dance-root[data-phase=\"select\"] .dance-now-copy>span {font-size:24px}\r\n.dance-root[data-phase=\"select\"] .dance-direction {font-size:32px}\r\n.dance-root[data-phase=\"select\"] .dance-now small {font-size:14px}\r\n.dance-root[data-phase=\"select\"] .dance-video-host {display:none}\r\n.dance-root[data-phase=\"playing\"] .dance-panel,.dance-root[data-phase=\"count_in\"] .dance-panel {min-height:48px}\r\n.dance-root[data-phase=\"playing\"] .dance-row,.dance-root[data-phase=\"count_in\"] .dance-row {justify-content:space-between}\r\n.dance-root[data-phase=\"playing\"] .dance-btn,.dance-root[data-phase=\"count_in\"] .dance-btn {min-width:100px;font-size:18px}\r\n.dance-root[data-phase=\"teach\"] .dance-panel>p:not(.dance-kicker),.dance-root[data-phase=\"practice\"] .dance-panel>p:not(.dance-kicker) {font-size:18px}\r\n@media (min-width:760px) {.dance-stage {max-width:900px;width:100%;align-self:center}.dance-panel {max-width:900px;width:100%;align-self:center}.dance-now {grid-template-columns:56% 44%}.dance-now-copy>span {font-size:42px}.dance-now small {font-size:24px}}\r\n@media (max-width:380px) {.dance-root {padding-left:calc(8px + env(safe-area-inset-left));padding-right:calc(8px + env(safe-area-inset-right));gap:8px}.dance-stage {border-radius:18px}.dance-now {grid-template-columns:56% 44%}.dance-now-copy {padding-right:7px}.dance-now-copy>span {font-size:25px}.dance-now small {font-size:24px}.dance-row .dance-btn {flex:1 1 calc(50% - 8px)}.dance-video-host {top:60px;right:8px}}\r\n@media (orientation:landscape) and (max-height:500px) {.dance-root {display:grid;grid-template-columns:minmax(0,1.6fr) minmax(250px,1fr);align-items:start}.dance-stage {height:calc(100dvh - 40px);min-height:320px}.dance-panel {max-height:calc(100dvh - 40px);overflow-y:auto}.dance-root[data-phase=\"playing\"],.dance-root[data-phase=\"count_in\"] {grid-template-columns:minmax(0,1fr) 140px}.dance-now-copy>span {font-size:28px}.dance-now small {font-size:24px}}\r\n@media (prefers-reduced-motion:reduce) {.dance-lights i,.dance-spinner,.dance-spinner-inline {animation:none}.dance-lights {opacity:.65}.dance-card {will-change:auto}}\r\n.dance-root .dance-coach-host>.myr5-companion-stage {position:absolute!important;inset:0!important;display:block!important;width:100%!important;height:100%!important;max-width:none!important;max-height:none!important;min-height:0!important;margin:0!important;border:0!important;border-radius:0!important;background:transparent!important;visibility:visible!important;opacity:1!important;pointer-events:none!important;transform:none!important;box-shadow:none!important}\r\n","modules/dance/ui/index.mjs":"// Dance UI. Renders whatever the root coordinator hands over in view; it never advances\r\n// gameplay time. All state transitions go through actions.* supplied by the root.\r\nimport * as bundledCatalogue from '../content/index.mjs';\r\nimport { drawFigure, projectCoach, getMoveViewport, projectMoveCoach } from './skeleton.mjs';\r\nimport { choreographyPose } from './pose.mjs';\r\nimport { LOOKAHEAD_SECONDS, lanePlacement, upcomingSlots } from './lane.mjs';\r\n\r\nconst CSS_HREF = '/modules/dance/ui/dance-ui.css';\r\nconst CREDITS_HREF = '/modules/dance/CREDITS.txt';\r\nconst STAGE_PHASES = new Set(['select', 'calibrate', 'teach', 'practice', 'ready', 'count_in', 'playing', 'paused']);\r\n// Authored left is already screen-left, matching the anatomical left arm in the selfie camera.\r\n// Camera mirroring belongs only to the raw camera presentation, never to authored choreography.\r\nexport const REFERENCE_MIRRORED = false;\r\nconst SCROLL_STAGE_PHASES = new Set(['calibrate','teach','practice','ready','count_in','playing']);\r\nexport function shouldResetStageScroll(previousPhase, nextPhase) {\r\n  return previousPhase !== nextPhase && SCROLL_STAGE_PHASES.has(nextPhase);\r\n}\r\nconst GROOVES = [['cardio', 'Cardio'], ['salsa-inspired', 'Salsa-inspired'], ['jazz-inspired', 'Jazz-inspired']];\r\nconst INTENSITIES = [['easy', 'Easy'], ['steady', 'Steady']];\r\nconst MAX_QUEUE = 5;\r\nconst PREVIEW_CARDS = 4;\r\nconst NON_PRACTICE = new Set(['neutral-bridge', 'rest']);\r\nconst REDUCED_MOTION = () => globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\r\n\r\nfunction normalizeCatalogue(input) {\r\n  const src = input ?? bundledCatalogue;\r\n  if (Array.isArray(src)) return { list: src, sample: (id, p) => bundledCatalogue.sampleTarget(id, p) };\r\n  return { list: src.getCatalogue?.() ?? [], sample: (id, p) => src.sampleTarget(id, p) };\r\n}\r\n\r\nconst esc = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\r\nconst fmt = (value, digits = 0) => (Number.isFinite(value) ? value.toFixed(digits) : '—');\r\n\r\n\r\nexport function shortCue(id, phase) {\r\n  const returning = phase % 0.5 > 0.25;\r\n  if (id === 'rest' || id === 'neutral-bridge') return 'Breathe & reset';\r\n  if (returning) return 'Bring it back';\r\n  return ({\r\n    'shoulder-groove': 'Lift & soften',\r\n    'sway-reach': phase < 0.5 ? 'Left arm out' : 'Right arm out',\r\n    'alternating-disco-reach': phase < 0.5 ? 'Left hand up' : 'Right hand up',\r\n    'overhead-pump': 'Both hands up',\r\n    'crossbody-press': phase < 0.5 ? 'Left hand across' : 'Right hand across',\r\n    'reach-forward': 'Reach forward',\r\n  })[id] ?? 'Follow & repeat';\r\n}\r\n\r\nexport function directionCue(id, phase, mirrored = false) {\r\n  if (id === 'rest' || id === 'neutral-bridge') return '\\u2194';\r\n  if (phase % 0.5 > 0.25) return '\\u2193  \\u2193';\r\n  if (id === 'overhead-pump' || id === 'shoulder-groove') return '\\u2191  \\u2191';\r\n  if (id === 'reach-forward') return '\\u2197  \\u2196';\r\n  const left = (phase < 0.5) !== mirrored;\r\n  if (id === 'alternating-disco-reach') return left ? '\\u2196' : '\\u2197';\r\n  if (id === 'crossbody-press') return left ? '\\u2192' : '\\u2190';\r\n  return left ? '\\u2190' : '\\u2192';\r\n}\r\n\r\nexport function hudStatus(view) {\r\n  const phase = view.phase ?? 'select';\r\n  if (phase === 'select') return 'Preview \\u00b7 Choose music below';\r\n  if (['teach', 'ready'].includes(phase)) return view.cameraReady ? 'Camera ready' : 'Camera off';\r\n  return ({tracking:'Tracking',tracking_limited:'Tracking limited - reframe',calibrating:'Calibrating',paused:'Paused'})[view.feedback?.status] ?? '';\r\n}\r\n\r\nexport function mountDance({ root, catalogue, builtinTracks = [], actions = {} } = {}) {\r\n  if (!(root instanceof Element)) throw new TypeError('mountDance: root element required');\r\n  const cat = normalizeCatalogue(catalogue);\r\n  const builtins = [...builtinTracks];\r\n  const doc = root.ownerDocument;\r\n  const win = doc.defaultView;\r\n  const ac = new AbortController();\r\n  const { signal } = ac;\r\n  let disposed = false;\r\n  let view = {};\r\n  let lastPaintPhase = null;\r\n  let lastPanelKey = null;\r\n  let localError = null;\r\n  let importOpen = false;\r\n  let draft = null;\r\n  let preview = null;\r\n  let leasedCreature = null;\r\n  let attachingCreature = null;\r\n  let attachGeneration = 0;\r\n  let stageSample = null;\r\n  let localTeach = null; // teaching move picked on this screen; a root teaching move wins when it sends one\r\n\r\n  const link = doc.createElement('link');\r\n  link.rel = 'stylesheet';\r\n  link.href = CSS_HREF;\r\n  link.dataset.danceUi = '';\r\n  doc.head.append(link);\r\n  root.classList.add('dance-root');\r\n\r\n  // Persistent nodes: created once, never rebuilt, so the camera host and canvases survive renders.\r\n  const stage = doc.createElement('section');\r\n  stage.className = 'dance-stage';\r\n  stage.setAttribute('aria-label', 'Dance stage');\r\n  const lights = doc.createElement('div');\r\n  lights.className = 'dance-lights';\r\n  lights.setAttribute('aria-hidden', 'true');\r\n  lights.innerHTML = '<i></i><i></i><i></i><b></b>';\r\n  const coachHost = doc.createElement('div');\r\n  coachHost.className = 'dance-coach-host';\r\n  coachHost.setAttribute('aria-label', 'Your selected 3D coach');\r\n  const coachState = doc.createElement('span');\r\n  coachState.className = 'dance-coach-state';\r\n  coachState.textContent = 'Coach loading';\r\n  const videoHost = doc.createElement('div');\r\n  videoHost.className = 'dance-video-host';\r\n  videoHost.dataset.danceVideo = '';\r\n  const canvas = doc.createElement('canvas');\r\n  canvas.className = 'dance-canvas';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  const hud = doc.createElement('div');\r\n  hud.className = 'dance-hud';\r\n  hud.innerHTML = '<span class=\"dance-status\" data-hud=\"status\"></span><strong class=\"dance-count\" data-hud=\"count\"></strong><div class=\"dance-progress\"><i data-hud=\"progress\"></i></div>';\r\n  const lane = doc.createElement('div');\r\n  lane.className = 'dance-lane';\r\n  lane.innerHTML = '<div class=\"dance-next\"><b>UP NEXT \\u2192</b><div class=\"dance-track\" data-lane=\"track\"></div></div><div class=\"dance-now\" data-lane=\"now\"><canvas></canvas><div class=\"dance-now-copy\"><b class=\"dance-now-label\">NOW <em data-hud=\"beat\"></em></b><span data-hud=\"now\"></span><strong class=\"dance-direction\" data-hud=\"direction\"></strong><small data-hud=\"cue\"></small></div></div>';\r\n  stage.append(lights, coachHost, canvas, videoHost, coachState, hud, lane);\r\n  const panel = doc.createElement('div');\r\n  panel.className = 'dance-panel';\r\n  panel.setAttribute('aria-live', 'polite');\r\n  root.replaceChildren(stage, panel);\r\n\r\n  const ctx = canvas.getContext('2d');\r\n  const nowCanvas = lane.querySelector('.dance-now canvas');\r\n  const nowCtx = nowCanvas.getContext('2d');\r\n  const track = lane.querySelector('[data-lane=\"track\"]');\r\n  const cards = new Map(); // slot id -> { el, canvas, ctx, moveId, label, phase }\r\n  const hudEl = (name) => hud.querySelector(`[data-hud=\"${name}\"]`) ?? lane.querySelector(`[data-hud=\"${name}\"]`);\r\n\r\n  const call = (name, ...args) => {\r\n    const fn = actions[name];\r\n    if (typeof fn !== 'function') return undefined;\r\n    const fail = (error) => { localError = { message: error?.message || 'That did not work. Try again.', retry: name, args }; paint(true); };\r\n    try {\r\n      const out = fn(...args);\r\n      if (out && typeof out.catch === 'function') out.catch(fail);\r\n      return out;\r\n    } catch (error) {\r\n      fail(error);\r\n      return undefined;\r\n    }\r\n  };\r\n\r\n  const bpmOk = (bpm) => cat.list.some((t) => t.tempoRangeBpm[0] <= bpm && t.tempoRangeBpm[1] >= bpm);\r\n  const meta = (id) => cat.list.find((t) => t.id === id) ?? null;\r\n  const cycleSeconds = (id) => {\r\n    const t = meta(id);\r\n    if (!t) return 4;\r\n    const bpm = (t.tempoRangeBpm[0] + t.tempoRangeBpm[1]) / 2;\r\n    return (t.beats * 60) / bpm;\r\n  };\r\n  const teachingId = () => view.teachingMoveId ?? view.teachMoveId ?? null;\r\n  const teachId = () => teachingId() ?? localTeach ?? view.activeSlot?.moveId ?? view.plan?.slots?.find(slot => slot.moveId && !NON_PRACTICE.has(slot.moveId))?.moveId ?? 'shoulder-groove';\r\n  const resultOf = () => view.results ?? view.result ?? view.progress?.latest ?? null;\r\n  // Root owns the queue; it may send ids, Track objects, or a mix. Both are rendered the same way.\r\n  const queueOf = () => view.selection?.queue ?? view.selection?.tracks ?? view.selection?.trackIds ?? [];\r\n  const intensityOf = () => view.selection?.intensity ?? 'easy';\r\n  const cameraState = () => view.camera?.state ?? 'idle'; // root: 'starting' | 'ready' | 'error' | 'idle'\r\n  const titleFor = (trackId) => builtins.find((t) => t.id === trackId)?.title\r\n    ?? queueOf().map(resolveTrack).find((t) => t?.id === trackId)?.title ?? trackId;\r\n  const sessionsOf = () => view.progress?.sessions ?? [];\r\n  const resolveTrack = (entry) => (typeof entry === 'string' ? builtins.find((t) => t.id === entry) ?? null : entry ?? null);\r\n\r\n  // ---- camera host: attach the root's element once; never clone or remove it ----\r\n  function attachCamera(video) {\r\n    if (!(video instanceof Element)) {\r\n      for (const child of videoHost.children) child.hidden = true;\r\n      return;\r\n    }\r\n    if (video.parentNode !== videoHost) videoHost.append(video);\r\n    for (const child of videoHost.children) child.hidden = child !== video;\r\n    video.classList.add('dance-camera');\r\n  }\r\n\r\n  // ---- coach target for the stage (real sample from catalogue, never random) ----\r\n  function coachPose() {\r\n    return choreographyPose(view, cycleSeconds, teachId);\r\n  }\r\n\r\n  // ---- drawing (derived only from view.clock.sessionSeconds / activeSlot / previewSeconds) ----\r\n  function sizeCanvas(node, context, box) {\r\n    const dpr = Math.min(win.devicePixelRatio || 1, 2);\r\n    const w = Math.max(1, Math.round(box.width * dpr));\r\n    const h = Math.max(1, Math.round(box.height * dpr));\r\n    if (node.width !== w || node.height !== h) { node.width = w; node.height = h; }\r\n    context.setTransform(dpr, 0, 0, dpr, 0, 0);\r\n    context.clearRect(0, 0, box.width, box.height);\r\n  }\r\n\r\n  // Lease the existing selected coach. A late-loading creature API is retried on the next root render.\r\n  function releaseCoach() {\r\n    attachGeneration += 1;\r\n    if (leasedCreature || attachingCreature) {\r\n      try { (leasedCreature ?? attachingCreature).endDance?.(); } catch { /* host teardown must continue */ }\r\n    }\r\n    leasedCreature = null;\r\n    attachingCreature = null;\r\n    coachHost.dataset.ready = 'false';\r\n  }\r\n\r\n  function attachCoach() {\r\n    const creature = win.myr5Creature;\r\n    if (leasedCreature && leasedCreature !== creature) releaseCoach();\r\n    if (!creature?.dance || leasedCreature === creature || attachingCreature === creature) return;\r\n    try {\r\n      const result = creature.dance({ host: coachHost });\r\n      const attached = (value) => {\r\n        if (value === false || value?.ready === false) return;\r\n        leasedCreature = creature;\r\n        coachHost.dataset.ready = 'true';\r\n      };\r\n      if (result?.then) {\r\n        attachingCreature = creature;\r\n        const generation = attachGeneration;\r\n        result.then(value => {\r\n          if (!disposed && !stage.hidden && generation === attachGeneration) attached(value);\r\n        }).catch(() => {}).finally(() => { if (attachingCreature === creature) attachingCreature = null; });\r\n      } else attached(result);\r\n    } catch { /* selected coach can finish loading before the next render */ }\r\n  }\r\n\r\n  function drawFrame() {\r\n    if (disposed || stage.hidden) return;\r\n    attachCoach();\r\n    const pose = coachPose();\r\n    stageSample = cat.sample(pose.id, pose.phase);\r\n    const mirrored = REFERENCE_MIRRORED;\r\n    if (leasedCreature) {\r\n      try {\r\n        const applied = leasedCreature.danceFrame?.({ landmarks: stageSample.landmarks, mirrored, active: true });\r\n        if (applied === false) releaseCoach();\r\n      } catch { releaseCoach(); }\r\n    }\r\n    coachState.hidden = Boolean(leasedCreature);\r\n    stage.dataset.coachReady = String(Boolean(leasedCreature));\r\n    sizeCanvas(canvas, ctx, stage.getBoundingClientRect());\r\n    const W = stage.clientWidth || 1;\r\n    const H = stage.clientHeight || 1;\r\n    // The instructional outline stays readable over the lower coach; while loading it fills the stage.\r\n    const coachBox = leasedCreature && view.phase === 'select'\r\n      ? { x: W * 0.06, y: H * 0.48, w: W * 0.88, h: H * 0.48 }\r\n      : leasedCreature\r\n      ? { x: W * 0.16, y: H * 0.25, w: W * 0.68, h: H * 0.37 }\r\n      : { x: W * 0.04, y: H * 0.02, w: W * 0.92, h: H * 0.59 };\r\n    drawFigure(ctx, projectCoach(stageSample.landmarks, coachBox, mirrored), { lineWidth: Math.max(7, W * 0.019), alpha: 1, head: !leasedCreature, arrows: true });\r\n    if (view.phase === 'paused') {\r\n      ctx.fillStyle = '#07141a55';\r\n      ctx.fillRect(0, 0, W, H * 0.61);\r\n    }\r\n    drawNowCard();\r\n    drawCards();\r\n  }\r\n\r\n  function drawNowCard() {\r\n    const rect = nowCanvas.getBoundingClientRect();\r\n    if (!rect.width) return;\r\n    sizeCanvas(nowCanvas, nowCtx, rect);\r\n    const pose = coachPose();\r\n    const box = { x: 0, y: 0, w: rect.width, h: rect.height };\r\n    const lineWidth = Math.max(6, rect.width * 0.038);\r\n    const viewport = getMoveViewport(pose.id, cat.sample);\r\n    const points = projectMoveCoach((stageSample ?? cat.sample(pose.id, pose.phase)).landmarks, box, REFERENCE_MIRRORED, viewport, { padding: Math.max(16, lineWidth * 2) });\r\n    drawFigure(nowCtx, points, { lineWidth, head: true, arrows: true });\r\n  }\r\n\r\n  function drawCards() {\r\n    for (const card of cards.values()) {\r\n      const rect = card.canvas.getBoundingClientRect();\r\n      if (!rect.width) continue;\r\n      sizeCanvas(card.canvas, card.ctx, rect);\r\n      const box = { x: rect.width * 0.1, y: rect.height * 0.04, w: rect.width * 0.8, h: rect.height * 0.92 };\r\n      drawFigure(card.ctx, projectCoach(cat.sample(card.moveId, card.phase).landmarks, box, REFERENCE_MIRRORED), { lineWidth: 4, head: true });\r\n    }\r\n  }\r\n\r\n  // ---- lane: NOW at far left; upcoming slots scroll toward it from the right ----\r\n  function paintLane() {\r\n    // Future slots only: the active slot owns NOW. Pixel positions come from the track's real width.\r\n    const now = view.clock?.sessionSeconds ?? 0;\r\n    const upcoming = upcomingSlots(view.upcomingSlots, now, PREVIEW_CARDS);\r\n    const trackWidth = track.getBoundingClientRect().width;\r\n    const reduced = REDUCED_MOTION();\r\n    const seen = new Set();\r\n    for (const slot of upcoming) {\r\n      seen.add(slot.id);\r\n      let card = cards.get(slot.id);\r\n      if (!card) {\r\n        const el = doc.createElement('div');\r\n        el.className = 'dance-card';\r\n        const c = doc.createElement('canvas');\r\n        const label = doc.createElement('span');\r\n        el.append(c, label);\r\n        track.append(el);\r\n        card = { el, canvas: c, ctx: c.getContext('2d'), label, moveId: slot.moveId, phase: 0 };\r\n        cards.set(slot.id, card);\r\n      }\r\n      card.moveId = slot.moveId;\r\n      card.label.textContent = meta(slot.moveId)?.title ?? slot.moveId;\r\n      const place = lanePlacement(slot, now, trackWidth, { lookahead: LOOKAHEAD_SECONDS, reduced });\r\n      card.el.style.transform = `translateX(${place.x}px)`;\r\n      card.el.hidden = !place.visible;\r\n      card.phase = place.phase;\r\n    }\r\n    for (const [id, card] of cards) {\r\n      if (!seen.has(id)) { card.el.remove(); cards.delete(id); }\r\n    }\r\n    const pose = coachPose();\r\n    const m = meta(pose.id);\r\n    const label = view.activeSlot?.role === 'count_in' ? 'Get ready' : (m?.title ?? 'Reset & breathe');\r\n    hudEl('now').textContent = label;\r\n    hudEl('cue').textContent = shortCue(pose.id, pose.phase);\r\n    hudEl('direction').textContent = directionCue(pose.id, pose.phase, REFERENCE_MIRRORED);\r\n    const beats = m?.beats ?? 8;\r\n    hudEl('beat').textContent = `${Math.floor(pose.phase * beats) + 1} / ${beats}`;\r\n    const time = ['select', 'ready', 'teach', 'calibrate'].includes(view.phase) ? (view.previewSeconds ?? 0) : now;\r\n    // Soft light intensity follows the same authoritative clock as the move; no extra timer.\r\n    stage.style.setProperty('--dance-beat', String(0.55 + Math.cos(time * Math.PI * 2) * 0.1));\r\n\r\n  }\r\n\r\n  // ---- HUD: text only, updated on render ----\r\n  function paintHud() {\r\n    const phase = view.phase ?? 'select';\r\n    const feedback = view.feedback ?? {};\r\n    hudEl('status').textContent = hudStatus(view);\r\n    // Count-ins show the root's numeric countdown in practice too; a resume hold freezes on the same count.\r\n    const counting = phase === 'count_in' || (phase === 'practice' && Number.isFinite(view.countdown));\r\n    const count = phase === 'select' ? '' : counting ? (view.countdown ?? 'Get ready') : (feedback.correction ?? '');\r\n    hudEl('count').textContent = String(count ?? '');\r\n    const total = view.plan?.durationSeconds || 0;\r\n    const progress = view.songProgress ?? (total ? Math.min(1, (view.clock?.sessionSeconds ?? 0) / total) : 0);\r\n    hudEl('progress').style.width = `${Math.round(Math.min(1, Math.max(0, progress)) * 100)}%`;\r\n  }\r\n\r\n  // ---- panels: rebuilt only when their content changes, so inputs and focus survive ----\r\n  function panelHtml() {\r\n    const phase = view.phase ?? 'select';\r\n    const err = localError ?? (view.error ? { message: view.error.message ?? String(view.error), retry: view.error.retry } : null);\r\n    const errorHtml = err\r\n      ? `<div class=\"dance-error\" role=\"alert\"><p>${esc(err.message)}</p>${err.retry || localError ? '<button type=\"button\" data-act=\"retry\" class=\"dance-btn primary\">Try again</button>' : ''}</div>`\r\n      : '';\r\n    const back = '<button type=\"button\" data-act=\"back\" class=\"dance-btn ghost\">Back</button>';\r\n    if (phase === 'select') return selectHtml() + summaryHtml() + errorHtml;\r\n    if (phase === 'calibrate') {\r\n      // Button and state come from the camera state only. Sample counts update in the readout, not here.\r\n      const cam = cameraState();\r\n      const starting = cam === 'starting';\r\n      const failed = cam === 'error';\r\n      const status = starting ? 'Starting camera…'\r\n        : failed ? (view.camera?.message ?? 'Camera is unavailable. Allow camera access, then try again.')\r\n          : cam === 'ready' ? 'Camera ready. Face the camera, hands visible, and hold still.'\r\n            : 'Camera is off. Start calibration to turn it on.';\r\n      return `${errorHtml}<h2>Calibrate</h2>\r\n        <p class=\"dance-muted\">Show your shoulders, elbows and hands. Waist-up is enough; your head is optional.</p>\r\n        <p class=\"dance-muted\" data-cal>${starting ? '<span class=\"dance-spinner-inline\" aria-hidden=\"true\"></span>' : ''}<span data-readout=\"status\">${esc(status)}</span> <span data-readout=\"samples\"></span></p>\r\n        <div class=\"dance-row\"><button type=\"button\" data-act=\"calibrate\" class=\"dance-btn primary\" ${starting ? 'disabled' : ''}>${failed ? 'Try camera again' : 'Start calibration'}</button>${back}</div>`;\r\n    }\r\n    if (phase === 'teach' || phase === 'practice') {\r\n      const id = teachId();\r\n      const m = meta(id);\r\n      const moves = cat.list.filter((t) => !NON_PRACTICE.has(t.id));\r\n      return `${errorHtml}<p class=\"dance-kicker\">${phase === 'teach' ? 'Learn the move' : 'Practice'}</p>\r\n        <h2>${esc(m?.title ?? id)}</h2>\r\n        <p>${esc(m?.cue ?? '')}</p>\r\n        <ol class=\"dance-steps\">${(m?.teachingSteps ?? []).map((s) => `<li>${esc(s)}</li>`).join('')}</ol>\r\n        <p class=\"dance-muted\">Follow the looping arms. Cyan is left, yellow is right. Tap a move to practise.</p>\r\n        <div class=\"dance-chips\">${moves.map((t) => `<button type=\"button\" class=\"dance-chip${t.id === id ? ' on' : ''}\" data-act=\"practice-move\" data-move=\"${esc(t.id)}\">${esc(t.title)}</button>`).join('')}</div>\r\n        <div class=\"dance-row\">\r\n          <button type=\"button\" data-act=\"practice-skip\" class=\"dance-btn\">Skip</button>\r\n          <button type=\"button\" data-act=\"practice-next\" class=\"dance-btn\">Next</button>\r\n          <button type=\"button\" data-act=\"prepare\" class=\"dance-btn primary\">Prepare routine</button>\r\n          <button type=\"button\" data-act=\"prepare-skipall\" class=\"dance-btn\">Skip all, prepare</button>\r\n          ${back}\r\n        </div>`;\r\n    }\r\n    if (phase === 'preparing') {\r\n      return `<div class=\"dance-spinner\" aria-hidden=\"true\"></div><h2>Preparing routine</h2><p class=\"dance-muted\">Freezing the music and moves for this session.</p>${errorHtml}<div class=\"dance-row\">${back}</div>`;\r\n    }\r\n    if (phase === 'ready') {\r\n      const slots = view.plan?.slots ?? [];\r\n      const seconds = Math.round(view.plan?.durationSeconds ?? 0);\r\n      // Frozen order from the prepared plan; titles resolved for display only.\r\n      const order = (view.plan?.trackOrder ?? []).map((item, i) => `<li>${i + 1}. ${esc(titleFor(item.trackId))}</li>`).join('');\r\n      return `${errorHtml}<h2>Ready</h2>\r\n        <ol class=\"dance-steps dance-order\">${order || '<li class=\"dance-muted\">No tracks in this routine.</li>'}</ol>\r\n        <p class=\"dance-muted\">${slots.length} sections · ${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}</p>\r\n        <div class=\"dance-row\"><button type=\"button\" data-act=\"begin\" class=\"dance-btn primary\">Begin</button>${back}</div>`;\r\n    }\r\n    if (phase === 'count_in' || phase === 'playing') {\r\n      return `${errorHtml}<div class=\"dance-row\"><button type=\"button\" data-act=\"pause\" class=\"dance-btn\">Pause</button>${back}</div>`;\r\n    }\r\n    if (phase === 'paused') {\r\n      return `${errorHtml}<h2>Paused</h2><div class=\"dance-row\"><button type=\"button\" data-act=\"resume\" class=\"dance-btn primary\">Resume</button><button type=\"button\" data-act=\"restart\" class=\"dance-btn\">Restart</button>${back}</div>`;\r\n    }\r\n    if (phase === 'results') {\r\n      const r = resultOf();\r\n      const cov = Number.isFinite(r?.coverage) ? `${Math.round(r.coverage <= 1 ? r.coverage * 100 : r.coverage)}%` : '—';\r\n      return `${errorHtml}<h2>Routine complete</h2>\r\n        <dl class=\"dance-results\">\r\n          <div><dt>Sequence</dt><dd>${fmt(r?.sequence)}</dd></div>\r\n          <div><dt>Timing</dt><dd>${fmt(r?.timing)}</dd></div>\r\n          <div><dt>Effort</dt><dd>${fmt(r?.effort)}</dd></div>\r\n          <div><dt>Tracking coverage</dt><dd>${cov}</dd></div>\r\n        </dl>\r\n        <p class=\"dance-muted\">Scores appear only where they were observed. Missing values stay blank.</p>\r\n        <div class=\"dance-row\"><button type=\"button\" data-act=\"replay\" class=\"dance-btn primary\">Replay</button>${back}</div>`;\r\n    }\r\n    return `${errorHtml}${back}`;\r\n  }\r\n\r\n  // Visible progress only: totals and the last routine's titles. No raw saved records.\r\n  function summaryHtml() {\r\n    const sessions = sessionsOf();\r\n    if (!sessions.length) return '<div class=\"dance-section\"><p class=\"dance-muted\">No routines saved yet.</p></div>';\r\n    const seconds = sessions.reduce((sum, r) => sum + (Number.isFinite(r.secondsPlayed) ? r.secondsPlayed : 0), 0);\r\n    const last = sessions[0];\r\n    const when = last.completedAt ? new Date(last.completedAt).toLocaleDateString() : '';\r\n    return `<div class=\"dance-section\"><p class=\"dance-kicker\">Progress</p>\r\n      <p>${sessions.length} routine${sessions.length === 1 ? '' : 's'} · ${Math.round(seconds / 60)} min total</p>\r\n      <p class=\"dance-muted\">Last: ${esc((last.titles ?? []).join(' · '))}${when ? ` · ${esc(when)}` : ''}</p></div>`;\r\n  }\r\n\r\n  function selectHtml() {\r\n    const raw = queueOf();\r\n    const full = raw.length >= MAX_QUEUE;\r\n    const queueHtml = raw.length\r\n      ? raw.map((entry, i) => {\r\n        const t = resolveTrack(entry);\r\n        return `<li><span>${i + 1}. ${esc(t?.title ?? 'Unknown track')}</span><span class=\"dance-muted\">${t ? `${t.bpm} BPM · ${esc(t.groove)}` : ''}</span>\r\n          <button type=\"button\" class=\"dance-btn small\" data-act=\"queue-repeat\" data-index=\"${i}\" ${full ? 'disabled' : ''}>Repeat</button>\r\n          <button type=\"button\" class=\"dance-btn small\" data-act=\"queue-del\" data-index=\"${i}\" aria-label=\"Remove ${i + 1}\">×</button></li>`;\r\n      }).join('')\r\n      : '<li class=\"dance-muted\">Add 1–5 tracks.</li>';\r\n    const canContinue = raw.length >= 1 && raw.length <= MAX_QUEUE;\r\n    const builtinHtml = builtins.map((t) => `<button type=\"button\" class=\"dance-track-btn\" data-act=\"track-add\" data-id=\"${esc(t.id)}\" ${full ? 'disabled' : ''}>\r\n        <strong>${esc(t.title)}</strong><span>${t.bpm} BPM · ${esc(t.groove)}</span></button>`).join('');\r\n    const importHtml = importOpen && draft ? importSheetHtml() : '';\r\n    const intensity = intensityOf();\r\n    return `<header class=\"dance-head\"><div><p class=\"dance-kicker\">YOUR COACH \\u00b7 YOUR FLOOR</p><h2>Dance</h2></div><button type=\"button\" data-act=\"back\" class=\"dance-btn ghost\">Back</button></header>\r\n      <div class=\"dance-section\"><p class=\"dance-kicker\">Tracks · ${raw.length}/${MAX_QUEUE}</p><ul class=\"dance-queue\">${queueHtml}</ul></div>\r\n      <div class=\"dance-section\"><p class=\"dance-kicker\">Built-in music</p><div class=\"dance-tracks\">${builtinHtml || '<p class=\"dance-muted\">No built-in tracks.</p>'}</div></div>\r\n      <div class=\"dance-section\"><p class=\"dance-kicker\">Intensity</p><div class=\"dance-chips\">${INTENSITIES.map(([id, label]) => `<button type=\"button\" class=\"dance-chip${intensity === id ? ' on' : ''}\" data-act=\"intensity\" data-value=\"${id}\">${label}</button>`).join('')}</div>\r\n        <p class=\"dance-muted\">Moves follow each track's groove automatically.</p></div>\r\n      <div class=\"dance-row\">\r\n        <button type=\"button\" class=\"dance-btn\" data-act=\"import-open\">Import my audio</button>\r\n        ${typeof actions.spotify === 'function' ? '<button type=\"button\" class=\"dance-btn\" data-act=\"spotify\">Spotify</button>' : ''}\r\n      </div>\r\n      <p class=\"dance-muted dance-note\">Spotify opens music controls. Routines currently use the tracks you choose here. <a href=\"${CREDITS_HREF}\">Credits</a></p>\r\n      ${importHtml}\r\n      <div class=\"dance-row\"><button type=\"button\" class=\"dance-btn primary\" data-act=\"calibrate-open\" ${canContinue ? '' : 'disabled'}>Let\\u2019s dance \\u2192</button></div>\r\n      <p class=\"dance-muted\">${canContinue ? '' : 'Choose between 1 and 5 tracks to continue.'}</p>`;\r\n  }\r\n\r\n  // Pure checks for the import sheet. Used by both the first build and in-place validation.\r\n  function sheetChecks() {\r\n    const d = draft;\r\n    const bpmValid = Number.isFinite(d.bpm) && d.bpm >= 40 && d.bpm <= 240;\r\n    const grid = bpmValid && bpmOk(d.bpm);\r\n    const offsetValid = Number.isFinite(d.firstBeatSeconds) && d.firstBeatSeconds >= 0 && (!d.durationSeconds || d.firstBeatSeconds < d.durationSeconds);\r\n    const canConfirm = Boolean(d.file && bpmValid && grid && offsetValid && d.rightsConfirmed);\r\n    return { bpmValid, grid, offsetValid, canConfirm };\r\n  }\r\n\r\n  function sheetMessage({ bpmValid, grid, offsetValid }) {\r\n    const d = draft;\r\n    const fitting = builtins.filter((t) => Number.isFinite(d.bpm) && Math.abs(t.bpm - d.bpm) <= 8).map((t) => t.title);\r\n    const parts = [];\r\n    if (!bpmValid) parts.push('BPM must be between 40 and 240.');\r\n    else if (!grid) parts.push('No target supports this tempo. Pick a built-in track or adjust BPM.');\r\n    if (!offsetValid) parts.push('First beat must be inside the file.');\r\n    if (fitting.length && !grid) parts.push(`Close built-in fits: ${fitting.join(', ')}`);\r\n    return parts.join(' ');\r\n  }\r\n\r\n  function importSheetHtml() {\r\n    const d = draft;\r\n    const c = sheetChecks();\r\n    return `<div class=\"dance-sheet\" role=\"group\" aria-label=\"Import audio\">\r\n      <p class=\"dance-kicker\">Your audio</p>\r\n      <p class=\"dance-muted\"><span data-sheet=\"name\">${esc(d.name || 'No file selected')}</span><span data-sheet=\"dur\">${d.durationSeconds ? ` · ${Math.round(d.durationSeconds)} s` : ''}</span></p>\r\n      <input type=\"file\" accept=\"audio/*\" data-field=\"file\" aria-label=\"Choose audio file\">\r\n      <label class=\"dance-field\">BPM <input type=\"number\" inputmode=\"decimal\" min=\"40\" max=\"240\" step=\"0.1\" value=\"${esc(d.bpm)}\" data-field=\"bpm\"></label>\r\n      <label class=\"dance-field\">First beat (s) <input type=\"number\" inputmode=\"decimal\" min=\"0\" step=\"0.01\" value=\"${esc(d.firstBeatSeconds)}\" data-field=\"firstBeatSeconds\"></label>\r\n      <label class=\"dance-field\">Groove <select data-field=\"groove\">${GROOVES.map(([id, label]) => `<option value=\"${id}\"${d.groove === id ? ' selected' : ''}>${label}</option>`).join('')}</select></label>\r\n      <div class=\"dance-row\"><button type=\"button\" class=\"dance-btn\" data-act=\"import-preview\" ${d.file ? '' : 'disabled'}>${preview ? 'Stop preview' : 'Hear beat preview'}</button></div>\r\n      <p class=\"dance-error-text\" data-sheet=\"msg\" role=\"status\">${esc(sheetMessage(c))}</p>\r\n      <label class=\"dance-check\"><input type=\"checkbox\" data-field=\"rightsConfirmed\" ${d.rightsConfirmed ? 'checked' : ''}> I have the rights to use this audio in Dance.</label>\r\n      <div class=\"dance-row\">\r\n        <button type=\"button\" class=\"dance-btn primary\" data-act=\"import-confirm\" ${c.canConfirm ? '' : 'disabled'}>Use this track</button>\r\n        <button type=\"button\" class=\"dance-btn ghost\" data-act=\"import-close\">Cancel</button>\r\n      </div>\r\n    </div>`;\r\n  }\r\n\r\n  // Updates the open sheet in place: never replaces inputs, so focus, caret and typing survive.\r\n  function validateSheet() {\r\n    const sheet = panel.querySelector('.dance-sheet');\r\n    if (!sheet || !draft) return;\r\n    const c = sheetChecks();\r\n    sheet.querySelector('[data-act=\"import-confirm\"]').disabled = !c.canConfirm;\r\n    const previewBtn = sheet.querySelector('[data-act=\"import-preview\"]');\r\n    previewBtn.disabled = !draft.file;\r\n    previewBtn.textContent = preview ? 'Stop preview' : 'Hear beat preview';\r\n    sheet.querySelector('[data-sheet=\"msg\"]').textContent = sheetMessage(c);\r\n    sheet.querySelector('[data-sheet=\"dur\"]').textContent = draft.durationSeconds ? ` · ${Math.round(draft.durationSeconds)} s` : '';\r\n  }\r\n\r\n  // Rebuild the panel only for state, lesson, error or option changes. Live numbers (samples, countdown,\r\n  // correction, progress) are HUD/readout text, so a tap is never lost to a mid-gesture rebuild.\r\n  function panelKey() {\r\n    if (view.phase === 'playing' || view.phase === 'count_in') return `${view.phase}|${localError?.message ?? ''}|${view.error?.message ?? ''}`;\r\n    return JSON.stringify([\r\n      view.phase, queueOf().map((e) => (typeof e === 'string' ? e : e?.id ?? e?.title)), teachingId(),\r\n      view.phase === 'results' ? resultOf() : null,\r\n      localError?.message, view.error?.message, view.error?.retry, intensityOf(), importOpen, preview ? 1 : 0,\r\n      view.phase === 'ready' ? (view.plan?.trackOrder ?? []).map((t) => t.occurrenceId) : null,\r\n      view.phase === 'calibrate' ? [cameraState(), view.camera?.message] : null,\r\n      view.phase === 'select' ? [sessionsOf().length, sessionsOf()[0]?.id] : null,\r\n    ]);\r\n  }\r\n\r\n  // Live readout that must not rebuild the panel.\r\n  function paintReadouts() {\r\n    const status = panel.querySelector('[data-readout=\"status\"]');\r\n    if (status && cameraState() === 'ready') status.textContent = view.feedback?.correction || 'Face the camera, hands visible, and hold still.';\r\n    const el = panel.querySelector('[data-readout=\"samples\"]');\r\n    if (el) el.textContent = '';\r\n  }\r\n\r\n  function paint(force = false) {\r\n    if (disposed) return;\r\n    const phase = view.phase ?? 'select';\r\n    const resetStageScroll = shouldResetStageScroll(lastPaintPhase, phase);\r\n    lastPaintPhase = phase;\r\n    root.dataset.phase = phase;\r\n    stage.hidden = !STAGE_PHASES.has(view.phase ?? 'select');\r\n    if (stage.hidden && (leasedCreature || attachingCreature)) releaseCoach();\r\n    videoHost.dataset.mirrored = String(view.mirrored === true);\r\n    attachCamera(view.cameraVideo);\r\n    const key = panelKey();\r\n    if (force || key !== lastPanelKey) {\r\n      lastPanelKey = key;\r\n      panel.innerHTML = panelHtml();\r\n    }\r\n    if (resetStageScroll) {\r\n      const dialog = root.closest('#dancePanel');\r\n      if (dialog) dialog.scrollTop = 0;\r\n    }\r\n    paintHud();\r\n    paintReadouts();\r\n    paintLane();\r\n    drawFrame();\r\n  }\r\n\r\n  // ---- DOM listeners: one delegated set, removed on dispose ----\r\n  const handlers = {\r\n    retry() {\r\n      const e = localError ?? (view.error ? { retry: view.error.retry } : null);\r\n      localError = null;\r\n      if (e?.retry) call(e.retry, ...(e.args ?? []));\r\n      else paint(true);\r\n    },\r\n    'track-add'(d) {\r\n      if (!builtins.some((t) => t.id === d.id) || queueOf().length >= MAX_QUEUE) return;\r\n      call('selectTracks', [...queueOf(), d.id]);\r\n    },\r\n    'queue-repeat'(d) {\r\n      const q = [...queueOf()];\r\n      const i = Number(d.index);\r\n      if (q.length >= MAX_QUEUE || q[i] === undefined) return;\r\n      call('selectTracks', [...q.slice(0, i + 1), q[i], ...q.slice(i + 1)]);\r\n    },\r\n    'queue-del'(d) {\r\n      call('selectTracks', queueOf().filter((_, i) => i !== Number(d.index)));\r\n    },\r\n    intensity(d) { call('chooseIntensity', d.value); },\r\n    'import-open'() { importOpen = true; draft = draft ?? { file: null, name: '', durationSeconds: 0, bpm: 120, firstBeatSeconds: 0, groove: 'cardio', rightsConfirmed: false }; paint(true); },\r\n    'import-close'() { stopPreview(); importOpen = false; draft = null; paint(true); },\r\n    'import-preview'() { togglePreview(); },\r\n    'import-confirm'() {\r\n      if (!draft?.file) return;\r\n      const payload = { file: draft.file, bpm: draft.bpm, firstBeatSeconds: draft.firstBeatSeconds, groove: draft.groove, rightsConfirmed: true };\r\n      stopPreview();\r\n      importOpen = false;\r\n      draft = null;\r\n      call('importTracks', payload);\r\n      paint(true);\r\n    },\r\n    spotify() { call('spotify'); },\r\n    'calibrate-open'() { call('calibrate'); },\r\n    calibrate() { call('calibrate'); },\r\n    'practice-move'(d) { localTeach = d.move; call('practice', d.move); paint(true); },\r\n    'practice-next'() { call('practice', 'next'); },\r\n    'practice-skip'() { call('practice', 'skip'); },\r\n    prepare() { call('prepare'); },\r\n    'prepare-skipall'() { call('prepare', 'skipall'); },\r\n    begin() { call('begin'); },\r\n    pause() { call('pause'); },\r\n    resume() { call('resume'); },\r\n    restart() { call('restart'); },\r\n    replay() { call('replay'); },\r\n    back() { stopPreview(); call('back'); },\r\n  };\r\n\r\n  panel.addEventListener('click', (event) => {\r\n    const button = event.target.closest?.('[data-act]');\r\n    if (!button || button.disabled) return;\r\n    const fn = handlers[button.dataset.act];\r\n    if (fn) fn(button.dataset);\r\n  }, { signal });\r\n\r\n  // Number and checkbox fields update the draft silently (keeps focus); a committed change re-validates the sheet.\r\n  panel.addEventListener('input', (event) => {\r\n    const field = event.target.dataset?.field;\r\n    if (!field || !draft || field === 'file') return;\r\n    if (field === 'groove') draft.groove = event.target.value;\r\n    else if (field === 'rightsConfirmed') draft.rightsConfirmed = event.target.checked;\r\n    else draft[field] = event.target.value === '' ? NaN : Number(event.target.value);\r\n    validateSheet();\r\n  }, { signal });\r\n\r\n  panel.addEventListener('change', (event) => {\r\n    const field = event.target.dataset?.field;\r\n    if (!field) return;\r\n    if (field === 'file') { onFileChosen(event); return; }\r\n    validateSheet();\r\n  }, { signal });\r\n\r\n  function onFileChosen(event) {\r\n    const file = event.target.files?.[0];\r\n    if (!file) return;\r\n    stopPreview();\r\n    draft = { ...draft, file, name: file.name, durationSeconds: 0 };\r\n    paint(true);\r\n  }\r\n\r\n  async function togglePreview() {\r\n    if (preview) { stopPreview(); paint(true); return; }\r\n    if (!draft?.file) return;\r\n    const Ctx = win.AudioContext || win.webkitAudioContext;\r\n    if (!Ctx) { localError = { message: 'Audio preview is not available in this browser.' }; paint(true); return; }\r\n    try {\r\n      const audio = new Ctx();\r\n      const buffer = await audio.decodeAudioData(await draft.file.arrayBuffer());\r\n      if (disposed) { audio.close?.(); return; }\r\n      draft.durationSeconds = buffer.duration;\r\n      const source = audio.createBufferSource();\r\n      source.buffer = buffer;\r\n      source.connect(audio.destination);\r\n      const start = audio.currentTime + 0.05;\r\n      source.start(start);\r\n      const period = 60 / draft.bpm;\r\n      for (let k = 0; draft.firstBeatSeconds + k * period < buffer.duration && k < 600; k += 1) {\r\n        const at = start + draft.firstBeatSeconds + k * period;\r\n        const osc = audio.createOscillator();\r\n        const gain = audio.createGain();\r\n        gain.gain.value = 0.25;\r\n        osc.frequency.value = k % 4 === 0 ? 1600 : 1000;\r\n        osc.connect(gain).connect(audio.destination);\r\n        osc.start(at);\r\n        osc.stop(at + 0.04);\r\n      }\r\n      preview = { audio, source };\r\n      source.onended = () => { stopPreview(); validateSheet(); };\r\n      localError = null;\r\n      validateSheet();\r\n    } catch {\r\n      localError = { message: 'That file could not be played. Choose another file.' };\r\n      paint(true);\r\n    }\r\n  }\r\n\r\n  function stopPreview() {\r\n    if (!preview) return;\r\n    try { preview.source.stop(); } catch { /* already ended */ }\r\n    preview.audio.close?.();\r\n    preview = null;\r\n  }\r\n\r\n  // The coordinator renders each frame from its one authoritative clock.\r\n\r\n  return {\r\n    videoHost,\r\n    coachHost,\r\n    render(next) {\r\n      if (disposed) return;\r\n      view = next ?? {};\r\n      if (teachingId()) localTeach = null;\r\n      if (!importOpen) draft = null;\r\n      paint();\r\n    },\r\n    dispose() {\r\n      if (disposed) return;\r\n      disposed = true;\r\n      ac.abort();\r\n      stopPreview();\r\n      releaseCoach();\r\n      cards.clear();\r\n      link.remove();\r\n      root.replaceChildren();\r\n      root.classList.remove('dance-root');\r\n      delete root.dataset.phase;\r\n      videoHost.remove();\r\n    },\r\n  };\r\n}\r\n","modules/dance/ui/lane.mjs":"// Pure lane geometry for the preview strip. No DOM: unit-tested in tests/ui.test.mjs.\r\n// NOW owns the active slot. Preview cards are future slots only (start strictly after now),\r\n// so a slot is never drawn twice. Preview phase comes from the prepared timeline (cyclePhase on the\r\n// slot's own duration and repeat count), never from previewSeconds, so it matches the scored clock.\r\nimport { cyclePhase } from '../contracts.mjs';\r\n\r\nexport const LOOKAHEAD_SECONDS = 8;\r\n\r\nexport function upcomingSlots(slots, sessionSeconds, limit = 4) {\r\n  return (slots ?? [])\r\n    .filter((s) => s?.moveId && Number.isFinite(s.startSessionSeconds) && s.startSessionSeconds > sessionSeconds)\r\n    .slice(0, limit);\r\n}\r\n\r\n// x is in CSS pixels from the track's left edge (where NOW ends). A card reaches the NOW edge exactly\r\n// at its start time. Reduced motion steps in whole seconds instead of sliding.\r\nexport function lanePlacement(slot, sessionSeconds, trackWidth, { lookahead = LOOKAHEAD_SECONDS, reduced = false } = {}) {\r\n  const until = slot.startSessionSeconds - sessionSeconds;\r\n  const steps = reduced ? Math.floor(until) : until;\r\n  const x = Math.max(0, steps / lookahead) * trackWidth;\r\n  return {\r\n    x,\r\n    visible: until > 0 && until <= lookahead && trackWidth > 0,\r\n    phase: cyclePhase(slot, sessionSeconds),\r\n  };\r\n}\r\n","modules/dance/ui/pose.mjs":"// Which authored sample the coach shows right now. Pure, so the clock rules are unit-tested.\r\n// Scored phases (count_in, playing, paused, practice) follow the root's activeSlot and sessionSeconds,\r\n// exactly like the NOW cue. Select, ready, teach and calibrate use the local preview clock, and never score.\r\nimport { cyclePhase } from '../contracts.mjs';\r\n\r\nexport function choreographyPose(view, cycleSeconds, teachId) {\r\n  const slot = view.activeSlot;\r\n  if (['select', 'teach', 'calibrate', 'ready'].includes(view.phase)) {\r\n    const id = teachId();\r\n    return { id, phase: (((view.previewSeconds ?? 0) / cycleSeconds(id)) % 1 + 1) % 1 };\r\n  }\r\n  if (!slot) return { id: 'neutral-bridge', phase: 0 };\r\n  if (slot.role === 'rest') return { id: 'rest', phase: 0 };\r\n  if (!slot.moveId) return { id: 'neutral-bridge', phase: 0 };\r\n  return { id: slot.moveId, phase: cyclePhase(slot, view.clock?.sessionSeconds ?? 0) };\r\n}\r\n","modules/dance/ui/skeleton.mjs":"// Pure drawing math for the Dance UI. No DOM access here; callers pass a 2D context.\r\n// Canonical upper-body landmarks: y-down, shoulder midpoint at the authored root.\r\n// Player landmarks: raw normalized detector output (image-space, NOT mirrored).\r\n\r\nexport const BONES = Object.freeze([\r\n  [11, 12], [11, 13], [13, 15], [12, 14], [14, 16],\r\n]);\r\n\r\nconst LEFT_SIDE = new Set([11, 13, 15]);\r\nconst MIN_VISIBILITY = 0.35;\r\n\r\n// Largest rectangle with the video aspect that fits the stage (object-fit: contain).\r\nexport function containRect(boxW, boxH, videoW, videoH) {\r\n  const vw = videoW > 0 ? videoW : boxW;\r\n  const vh = videoH > 0 ? videoH : boxH;\r\n  const scale = Math.min(boxW / vw, boxH / vh);\r\n  const w = vw * scale;\r\n  const h = vh * scale;\r\n  return { x: (boxW - w) / 2, y: (boxH - h) / 2, w, h };\r\n}\r\n\r\n// Detector output -> pixel points inside the video rect. One X flip when mirrored, nothing else.\r\nexport function projectPlayer(landmarks, rect, mirrored) {\r\n  if (!Array.isArray(landmarks) || landmarks.length < 33) return null;\r\n  return landmarks.map((p, i) => {\r\n    if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return null;\r\n    if (Number.isFinite(p.visibility) && p.visibility < MIN_VISIBILITY) return null;\r\n    const x = mirrored ? 1 - p.x : p.x;\r\n    return { x: rect.x + x * rect.w, y: rect.y + p.y * rect.h, left: LEFT_SIDE.has(i) };\r\n  });\r\n}\r\n\r\n// Upper-body authored space: shoulder midpoint is (0,0), shoulder width is one unit.\r\n// A fixed viewport preserves authored sways and shoulder movement between every sample.\r\nexport const COACH_VIEWPORT = Object.freeze({ minX: -1.7, maxX: 1.7, minY: -1.5, maxY: 1.5 });\r\n\r\nexport function coachUnit(box) {\r\n  const v = COACH_VIEWPORT;\r\n  return Math.min(box.w / (v.maxX - v.minX), box.h / (v.maxY - v.minY));\r\n}\r\n\r\nexport function projectCoach(landmarks, box, mirrorFollow) {\r\n  if (!Array.isArray(landmarks) || landmarks.length < 33) return null;\r\n  const unit = coachUnit(box);\r\n  const originX = box.x + box.w / 2;\r\n  const originY = box.y + box.h / 2;\r\n  const sign = mirrorFollow ? -1 : 1;\r\n  return landmarks.map((p, i) => {\r\n    if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return null;\r\n    return { x: originX + sign * p.x * unit, y: originY + p.y * unit, left: LEFT_SIDE.has(i) };\r\n  });\r\n}\r\n\r\n// One fixed crop for the complete move cycle. Its cache belongs to the sampler, so custom\r\n// catalogues cannot inherit stale bounds. Include the head ring and drawn waist, not only joints.\r\nconst MOVE_VIEWPORTS = new WeakMap();\r\nexport function getMoveViewport(moveId, sample) {\r\n  let cache = MOVE_VIEWPORTS.get(sample);\r\n  if (!cache) { cache = new Map(); MOVE_VIEWPORTS.set(sample, cache); }\r\n  if (cache.has(moveId)) return cache.get(moveId);\r\n  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\r\n  const include = (x,y) => { minX = Math.min(minX,x); maxX = Math.max(maxX,x); minY = Math.min(minY,y); maxY = Math.max(maxY,y); };\r\n  for (let step = 0; step < 32; step++) {\r\n    const points = sample(moveId, step / 32).landmarks;\r\n    for (const i of [0,11,12,13,14,15,16]) {\r\n      const p = points?.[i];\r\n      if (p && Number.isFinite(p.x) && Number.isFinite(p.y)) include(p.x,p.y);\r\n    }\r\n    const left = points?.[11], right = points?.[12], nose = points?.[0];\r\n    if (!left || !right) continue;\r\n    const shoulder = Math.hypot(left.x-right.x,left.y-right.y);\r\n    const mid = {x:(left.x+right.x)/2,y:(left.y+right.y)/2};\r\n    include(mid.x-shoulder*.33,mid.y+shoulder*.94);\r\n    include(mid.x+shoulder*.33,mid.y+shoulder*.94);\r\n    if (nose) {\r\n      const radius = shoulder*.23;\r\n      include(nose.x-radius,nose.y-radius);\r\n      include(nose.x+radius,nose.y+radius);\r\n    }\r\n  }\r\n  const viewport = Object.freeze(Number.isFinite(minX) && maxX > minX && maxY > minY\r\n    ? {minX,maxX,minY,maxY} : {...COACH_VIEWPORT});\r\n  cache.set(moveId,viewport);\r\n  return viewport;\r\n}\r\n\r\n// Unlike per-frame fitting this preserves the move's travel and scale throughout its loop.\r\n// Pixel padding reserves room for the black stroke and wrist arrowheads at cycle extremes.\r\nexport function projectMoveCoach(landmarks, box, mirrorFollow, viewport, {padding = 16} = {}) {\r\n  if (!Array.isArray(landmarks) || landmarks.length < 33) return null;\r\n  const v = viewport ?? COACH_VIEWPORT;\r\n  const unit = Math.min(Math.max(1,box.w-padding*2)/(v.maxX-v.minX), Math.max(1,box.h-padding*2)/(v.maxY-v.minY));\r\n  const sign = mirrorFollow ? -1 : 1;\r\n  const originX = box.x+box.w/2-sign*(v.minX+v.maxX)/2*unit;\r\n  const originY = box.y+box.h/2-(v.minY+v.maxY)/2*unit;\r\n  return landmarks.map((p,i) => !p || !Number.isFinite(p.x) || !Number.isFinite(p.y) ? null\r\n    : {x:originX+sign*p.x*unit,y:originY+p.y*unit,left:LEFT_SIDE.has(i)});\r\n}\r\n\r\n// Torso contour is ordered in screen space, while anatomical arm identities retain their colors.\r\nexport function torsoContour(points) {\r\n  const a = points?.[11], b = points?.[12];\r\n  if (!a || !b) return null;\r\n  const shoulder = Math.hypot(a.x-b.x,a.y-b.y);\r\n  const mid = {x:(a.x+b.x)/2,y:(a.y+b.y)/2};\r\n  const screenLeft = a.x <= b.x ? a : b;\r\n  const screenRight = a.x <= b.x ? b : a;\r\n  return [screenLeft,screenRight,\r\n    {x:mid.x+shoulder*.33,y:mid.y+shoulder*.84},\r\n    {x:mid.x-shoulder*.33,y:mid.y+shoulder*.84}];\r\n}\r\n\r\n// High-contrast rounded upper-body glyph. Black backing makes every limb legible over the 3D coach.\r\nexport function drawFigure(ctx, points, { lineWidth = 8, alpha = 1, head = true, arrows = false } = {}) {\r\n  if (!points) return;\r\n  const left = points[11], right = points[12], nose = points[0];\r\n  if (!left || !right) return;\r\n  const shoulder = Math.hypot(left.x - right.x, left.y - right.y);\r\n  const mid = { x: (left.x + right.x) / 2, y: (left.y + right.y) / 2 };\r\n  ctx.save();\r\n  ctx.globalAlpha = alpha;\r\n  ctx.lineCap = 'round';\r\n  ctx.lineJoin = 'round';\r\n  const stroke = (a, b, color, width = lineWidth) => {\r\n    ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y);\r\n    ctx.strokeStyle = '#02070b'; ctx.lineWidth = width + Math.max(4, width * 0.6); ctx.stroke();\r\n    ctx.strokeStyle = color; ctx.lineWidth = width; ctx.stroke();\r\n  };\r\n  // Body ends at the waist; no lower-body target is drawn or implied.\r\n  ctx.beginPath();\r\n  const contour = torsoContour(points);\r\n  ctx.moveTo(contour[0].x, contour[0].y); ctx.lineTo(contour[1].x, contour[1].y);\r\n  ctx.lineTo(contour[2].x, contour[2].y);\r\n  ctx.quadraticCurveTo(mid.x, mid.y + shoulder * .94, contour[3].x, contour[3].y);\r\n  ctx.closePath(); ctx.fillStyle = '#061b25ba'; ctx.fill();\r\n  ctx.strokeStyle = '#02070b'; ctx.lineWidth = lineWidth + 5; ctx.stroke();\r\n  ctx.strokeStyle = '#fff'; ctx.lineWidth = Math.max(3, lineWidth * .58); ctx.stroke();\r\n  if (head && nose) {\r\n    const radius = Math.max(8, shoulder * .23);\r\n    stroke({x:mid.x,y:mid.y}, {x:nose.x,y:nose.y+radius}, '#fff', lineWidth * .65);\r\n    ctx.beginPath(); ctx.arc(nose.x, nose.y, radius, 0, Math.PI * 2);\r\n    ctx.fillStyle = '#08202b'; ctx.fill();\r\n    ctx.strokeStyle = '#02070b'; ctx.lineWidth = lineWidth + 5; ctx.stroke();\r\n    ctx.strokeStyle = '#fff'; ctx.lineWidth = Math.max(3, lineWidth * .65); ctx.stroke();\r\n  }\r\n  for (const [a,b] of BONES) {\r\n    const p=points[a], q=points[b];\r\n    if (!p || !q) continue;\r\n    const color = a === 11 && b === 12 ? '#fff' : p.left ? '#5bf5ff' : '#ffe54f';\r\n    stroke(p,q,color);\r\n  }\r\n  for (const i of [13,14,15,16]) {\r\n    const p=points[i]; if (!p) continue;\r\n    ctx.beginPath(); ctx.arc(p.x,p.y,lineWidth*.64,0,Math.PI*2);\r\n    ctx.fillStyle = p.left ? '#5bf5ff' : '#ffe54f'; ctx.fill();\r\n    ctx.strokeStyle = '#02070b'; ctx.lineWidth = Math.max(2,lineWidth*.25); ctx.stroke();\r\n  }\r\n  if (arrows) {\r\n    for (const [elbow,wrist] of [[13,15],[14,16]]) {\r\n      const p=points[elbow], q=points[wrist]; if (!p || !q) continue;\r\n      const dx=q.x-p.x, dy=q.y-p.y, len=Math.hypot(dx,dy);\r\n      if (len < 2) continue;\r\n      const ux=dx/len, uy=dy/len, size=Math.max(7,lineWidth*1.35);\r\n      const tip={x:q.x+ux*size*.9,y:q.y+uy*size*.9};\r\n      const color=q.left ? '#5bf5ff' : '#ffe54f';\r\n      stroke({x:tip.x-ux*size-uy*size*.55,y:tip.y-uy*size+ux*size*.55},tip,color,lineWidth*.55);\r\n      stroke({x:tip.x-ux*size+uy*size*.55,y:tip.y-uy*size-ux*size*.55},tip,color,lineWidth*.55);\r\n    }\r\n  }\r\n  ctx.restore();\r\n}\r\n","modules/graphics-lifecycle.mjs":"// Serialize renderer handoffs around the browser's asynchronous WebGL context-loss event.\r\n// Drivers can mark a context lost immediately while keeping its slot reserved until that event.\r\n// State lives on globalThis: app-runtime, launch-runtime and the raw modules each bundle their own copy of this file.\r\nconst G=globalThis.__myr5Graphics??={pending:new Set(),releasing:new WeakMap(),claims:new Set()};\r\nconst {pending,releasing}=G;\r\n\r\nexport function releaseGraphics(canvas,gl,release,{deadlineMs=250}={}){\r\n if(!canvas||!gl){try{release?.();}catch{}return Promise.resolve();}\r\n const existing=releasing.get(canvas);if(existing)return existing;\r\n let resolvePromise,timer=0,settled=false;\r\n const promise=new Promise(resolve=>{resolvePromise=resolve;});\r\n const finish=()=>{\r\n  if(settled)return;settled=true;clearTimeout(timer);canvas.removeEventListener('webglcontextlost',finish);pending.delete(promise);if(releasing.get(canvas)===promise)releasing.delete(canvas);resolvePromise();\r\n };\r\n pending.add(promise);releasing.set(canvas,promise);\r\n canvas.addEventListener('webglcontextlost',finish,{once:true});\r\n timer=setTimeout(finish,Math.max(0,deadlineMs)); // Broken drivers may omit the event; never stall navigation indefinitely.\r\n try{release?.();}catch{finish();}\r\n return promise;\r\n}\r\n\r\nexport async function waitForGraphicsRelease(){\r\n while(pending.size)await Promise.all([...pending]);\r\n}\r\n\r\n// A 3D screen claims the GPU before it creates its renderer: every long-lived renderer that listens for 'myr5:graphics-claim'\r\n// (the 3D coach = 'coach', the portal board/tunnel/peek = 'portal') gives its context back for real (detail.wait(promise)\r\n// until the loss lands) unless the claim keeps it, and rebuilds on 'myr5:graphics-free' once no claim excludes it.\r\n// iOS refuses new contexts while hidden ones still hold GPU memory (\"Error creating WebGL context\").\r\n// Returns release(); await release.ready before creating the renderer, call release(canvas) right after forceContextLoss\r\n// so the holders rebuild only once that context's loss has landed.\r\nexport function claimGraphics(owner,{keep=[]}={}){\r\n const claim={owner,keep};G.claims.add(claim);let done=false;const waits=[];\r\n const release=canvas=>{if(done)return;done=true;G.claims.delete(claim);\r\n  const lost=canvas?.addEventListener?new Promise(r=>{canvas.addEventListener('webglcontextlost',r,{once:true});setTimeout(r,250);}):null;\r\n  void Promise.resolve(lost).then(waitForGraphicsRelease).then(()=>{if(!G.claims.has(claim))dispatchEvent(new CustomEvent('myr5:graphics-free',{detail:{owner}}));});};\r\n try{dispatchEvent(new CustomEvent('myr5:graphics-claim',{detail:{owner,keep,wait:p=>waits.push(Promise.resolve(p).catch(()=>{}))}}));}catch{}\r\n release.ready=Promise.all(waits).then(waitForGraphicsRelease);\r\n return release;\r\n}\r\n// Is a holder ('coach', 'portal') kept off the GPU by a live claim?\r\nexport const graphicsBlocked=holder=>[...G.claims].some(c=>!c.keep.includes(holder));\r\n","modules/materials/chunk-delivery.mjs":"/** P13H: signed ordered chunks are the aggregate integrity guarantee; never assemble an asset. */\r\nimport { productionMaterialResourcePolicy } from './material-config.mjs';\r\nconst enc = new TextEncoder(), HEX = /^[a-f0-9]{64}$/i, ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62})$/, OWNER = /^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/;\r\nconst plain = x => x && typeof x === 'object' && !Array.isArray(x), stable = x => Array.isArray(x) ? x.map(stable) : plain(x) ? Object.fromEntries(Object.keys(x).sort().map(k => [k, stable(x[k])])) : x;\r\nconst id = x => typeof x === 'string' && ID.test(x), safePath = x => typeof x === 'string' && /^assets\\/[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(x) && !x.includes('..') && !x.includes('//');\r\nasync function hash(data) { const b = data instanceof Uint8Array ? data : new Uint8Array(data); if (!globalThis.crypto?.subtle) throw new Error('WebCrypto is required for material chunk verification'); return [...new Uint8Array(await crypto.subtle.digest('SHA-256', b))].map(x => x.toString(16).padStart(2, '0')).join(''); }\r\nconst b64 = x => Uint8Array.from((typeof atob === 'function' ? atob(x) : Buffer.from(x, 'base64').toString('binary')), c => c.charCodeAt(0));\r\nconst productionPolicy=productionMaterialResourcePolicy();\r\nexport const DEFAULT_LOCAL_RESOURCE_POLICY = Object.freeze({ maxChunkBytes: 1024 * 1024, maxAssetBytes: 64 * 1024 * 1024, maxPackBytes: 128 * 1024 * 1024, maxAssets: 16, maxChunksPerAsset: 256, trustedOrigins: productionPolicy?.trustedOrigins??Object.freeze(typeof location === 'undefined' ? [] : [location.origin]), allowedProtocols: productionPolicy?.allowedProtocols??Object.freeze(['https:']), pathPrefix: productionPolicy?.pathPrefix??'/materials/' });\r\nexport const canonicalChunkPayload = m => JSON.stringify(stable({ schema: 'mom-material-chunks-v1', packId: m.packId, version: m.version, keyId: m.keyId, assets: m.assets.map(a => ({ path:a.path, bytes:a.bytes, sha256:a.sha256, chunks:a.chunks.map(c => ({ index:c.index, offset:c.offset, bytes:c.bytes, sha256:c.sha256, url:c.url, etag:c.etag })) })) }));\r\nfunction trustedUrl(value, policy) { try { const u = new URL(value); return (policy.trustedOrigins || []).includes(u.origin) && (policy.allowedProtocols || []).includes(u.protocol) && typeof policy.pathPrefix === 'string' && u.pathname.startsWith(policy.pathPrefix); } catch { return false; } }\r\n// Raw GitHub serves byte ranges but does not CORS-expose Content-Range. Only\r\n// this exact immutable host may use the signed chunk digest as its offset proof.\r\n// All status, length, streamed-size, signature, hash and ownership checks remain.\r\nfunction pinnedRawCorsRange(requested,response,policy){\r\n if(response.type!=='cors'||policy.trustedOrigins?.length!==1||policy.trustedOrigins[0]!=='https://raw.githubusercontent.com'||policy.allowedProtocols?.length!==1||policy.allowedProtocols[0]!=='https:'||!/^\\/[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+\\/[a-f0-9]{40}\\/materials\\/$/.test(policy.pathPrefix))return false;\r\n try{const request=new URL(requested),final=new URL(response.url);return requested===request.href&&request.href===final.href&&trustedUrl(request.href,policy)&&trustedUrl(final.href,policy)&&!request.username&&!request.password&&!request.search&&!request.hash&&/^[A-Za-z0-9._/-]+$/.test(request.pathname)&&!request.pathname.includes('..')&&!request.pathname.includes('//');}catch{return false;}\r\n}\r\nexport function validateChunkManifest(m, policy = DEFAULT_LOCAL_RESOURCE_POLICY) { if (!plain(m) || m.schema !== 'mom-material-chunks-v1' || !id(m.packId) || !id(m.version) || !id(m.keyId) || !Array.isArray(m.assets) || typeof m.signature !== 'string') throw new Error('invalid chunk manifest shape'); if (!Number.isSafeInteger(policy.maxChunkBytes) || policy.maxChunkBytes < 1 || !Array.isArray(policy.trustedOrigins) || !Array.isArray(policy.allowedProtocols)) throw new Error('invalid local resource policy'); if (m.assets.length < 1 || m.assets.length > policy.maxAssets) throw new Error('asset count exceeds local policy'); const paths = new Set(); let total = 0; for (const a of m.assets) { if (!plain(a) || !safePath(a.path) || paths.has(a.path) || !Number.isSafeInteger(a.bytes) || a.bytes < 1 || a.bytes > policy.maxAssetBytes || !HEX.test(a.sha256) || !Array.isArray(a.chunks) || !a.chunks.length || a.chunks.length > policy.maxChunksPerAsset) throw new Error('invalid chunk asset'); paths.add(a.path); total += a.bytes; let offset = 0; for (let i=0;i<a.chunks.length;i++) { const c=a.chunks[i]; if (!plain(c) || c.index !== i || c.offset !== offset || !Number.isSafeInteger(c.bytes) || c.bytes < 1 || c.bytes > policy.maxChunkBytes || !HEX.test(c.sha256) || !trustedUrl(c.url,policy) || (c.etag !== undefined && (typeof c.etag !== 'string' || c.etag.length > 512))) throw new Error('invalid chunk ordering, URL, or record'); offset += c.bytes; } if(offset !== a.bytes) throw new Error('chunk sizes do not equal asset size'); } if(total > policy.maxPackBytes) throw new Error('pack exceeds local policy'); return true; }\r\nexport async function verifyChunkManifest(m, key, policy) { validateChunkManifest(m,policy); if(!plain(key)||key.kty!=='OKP'||key.crv!=='Ed25519') throw new Error('invalid material trust key'); const subtle=globalThis.crypto?.subtle; if(!subtle) throw new Error('WebCrypto is required for chunk manifest verification'); const trusted=await subtle.importKey('jwk',key,{name:'Ed25519'},false,['verify']); if(!await subtle.verify({name:'Ed25519'},trusted,b64(m.signature),enc.encode(canonicalChunkPayload(m)))) throw new Error('chunk manifest signature is invalid'); return true; }\r\n/** Test fixture only. Production callers use indexedDbChunkStore. */\r\nexport function memoryChunkStore({maxBytes=DEFAULT_LOCAL_RESOURCE_POLICY.maxPackBytes}={}) { const data=new Map();let used=0;return {maxBytes,get usedBytes(){return used},async get(k){const v=data.get(k);return v&&new Uint8Array(v)},async put(k,v){const b=new Uint8Array(v),old=data.get(k)?.byteLength??0;if(used-old+b.byteLength>maxBytes)throw new Error('chunk-store quota exceeded');data.set(k,new Uint8Array(b));used+=b.byteLength-old},async removePrefix(p){for(const k of [...data.keys()])if(k.startsWith(p)){used-=data.get(k).byteLength;data.delete(k)}}}; }\r\n/** Durable per-origin IndexedDB storage; read/write copies prevent mutation of saved chunks. */\r\nexport function indexedDbChunkStore({database='mom-material-chunks-v1',storeName='chunks'}={}) { if(!globalThis.indexedDB)throw new Error('IndexedDB durable storage is required');const open=new Promise((ok,no)=>{const r=indexedDB.open(database,1);r.onupgradeneeded=()=>r.result.createObjectStore(storeName);r.onsuccess=()=>ok(r.result);r.onerror=()=>no(r.error)});const run=async(mode,fn)=>{const db=await open;return new Promise((ok,no)=>{const t=db.transaction(storeName,mode),s=t.objectStore(storeName);let r;try{r=fn(s)}catch(e){t.abort();no(e);return}t.oncomplete=()=>ok(r?.result);t.onerror=()=>no(t.error||r?.error);t.onabort=()=>no(t.error||new Error('chunk-store transaction aborted'))})};return {async get(k){return run('readonly',s=>s.get(k)).then(v=>v&&new Uint8Array(v))},async put(k,v){return run('readwrite',s=>s.put(new Uint8Array(v),k))},async removePrefix(p){const db=await open;return new Promise((ok,no)=>{const t=db.transaction(storeName,'readwrite'),r=t.objectStore(storeName).openCursor();r.onsuccess=()=>{const c=r.result;if(!c)return;if(typeof c.key==='string'&&c.key.startsWith(p))c.delete();c.continue()};t.oncomplete=ok;t.onerror=()=>no(t.error)})}}; }\r\nexport class ChunkDownloader {\r\n constructor({store,fetchImpl=globalThis.fetch,policy=DEFAULT_LOCAL_RESOURCE_POLICY,ownership,online=()=>true,expectedVersion,manifestPublicKey}={}) { if(!store?.get||!store?.put||!store?.removePrefix||typeof fetchImpl!=='function')throw new Error('durable chunk store and fetch are required');if(typeof ownership!=='function')throw new Error('explicit authenticated ownership adapter is required');if(!id(expectedVersion))throw new Error('authoritative expectedVersion is required');if(fetchImpl===globalThis.fetch){const nativeFetch=fetchImpl;fetchImpl=(...args)=>Reflect.apply(nativeFetch,globalThis,args)}Object.assign(this,{store,fetchImpl,policy,ownership,online,expectedVersion,manifestPublicKey});this.maxAccountedAllocationBytes=0; }\r\n prefix(m,owner){return `material/${owner}/${m.packId}/${m.version}/${m.keyId}/`} key(m,o,a,c){return `${this.prefix(m,o)}${a.path}/${c.index}`}\r\n async ownerFor(m,prior=null){const o=await this.ownership({packId:m.packId,version:m.version});if(typeof o!=='string'||!OWNER.test(o))throw new Error('material is not owned by an authenticated account');if(prior&&o!==prior)throw new Error('authenticated account changed during material operation');return o}\r\n async prepare(m){const owner=await this.ownerFor(m);if(!this.manifestPublicKey)throw new Error('trusted manifest public key is required');await verifyChunkManifest(m,this.manifestPublicKey,this.policy);if(m.version!==this.expectedVersion)throw new Error('replayed or unexpected manifest version');return this.ownerFor(m,owner)}\r\nasync download(m,{signal,retry=1}={}) { const owner=await this.prepare(m);if(!this.online())throw new Error('offline; resume available');let received=0;for(const a of m.assets)for(const c of a.chunks){await this.ownerFor(m,owner);const key=this.key(m,owner,a,c),saved=await this.store.get(key);if(saved?.byteLength===c.bytes&&await hash(saved)===c.sha256){await this.ownerFor(m,owner);received+=saved.byteLength;continue}await this.ownerFor(m,owner);if(signal?.aborted)throw new Error('download cancelled; resume available');if(saved)await this.store.removePrefix(key);let failure;for(let attempt=0;attempt<=retry;attempt++){let reader;try{if(signal?.aborted)throw new Error('download cancelled; resume available');await this.ownerFor(m,owner);if(!this.online())throw new Error('offline; resume available');const response=await this.fetchImpl(c.url,{headers:{Range:`bytes=${c.offset}-${c.offset+c.bytes-1}`,...(c.etag?{'If-Range':c.etag}:{})},signal,redirect:'error'});if(!response.ok||response.status!==206||response.redirected||!trustedUrl(response.url||c.url,this.policy))throw new Error(`chunk HTTP/redirect policy failure (${response.status})`);if(c.etag&&response.headers.get('etag')!==c.etag)throw new Error('chunk ETag changed');const range=response.headers.get('content-range');if(range===null?!pinnedRawCorsRange(c.url,response,this.policy):!new RegExp(`^bytes ${c.offset}-${c.offset+c.bytes-1}/\\\\d+$`).test(range))throw new Error('invalid chunk Content-Range');if(Number(response.headers.get('content-length'))!==c.bytes)throw new Error('chunk response size mismatch');reader=response.body?.getReader();if(!reader)throw new Error('streaming response body required');const joined=new Uint8Array(c.bytes);let size=0,largest=0;for(;;){const part=await reader.read();if(part.done)break;const value=part.value instanceof Uint8Array?part.value:new Uint8Array(part.value);largest=Math.max(largest,value.byteLength);size+=value.byteLength;if(size>c.bytes)throw new Error('chunk response too large');joined.set(value,size-value.byteLength)}this.maxAccountedAllocationBytes=Math.max(this.maxAccountedAllocationBytes,joined.byteLength*2+largest);if(size!==c.bytes)throw new Error('chunk response truncated');if(await hash(joined)!==c.sha256)throw new Error('chunk hash mismatch');if(signal?.aborted)throw new Error('download cancelled; resume available');await this.ownerFor(m,owner);await this.store.put(key,joined);await this.ownerFor(m,owner);received+=size;failure=null;break}catch(e){failure=signal?.aborted?new Error('download cancelled; resume available'):e;if(signal?.aborted||/ETag|hash|size|cancelled|Content-Range|redirect|account changed/.test(e.message))break}finally{if(reader)await reader.cancel().catch(()=>{})}}if(failure)throw failure}await this.ownerFor(m,owner);if(signal?.aborted)throw new Error('download cancelled; resume available');return {receivedBytes:received,maxAccountedAllocationBytes:this.maxAccountedAllocationBytes,browserMemoryLimitation:'Browser network and stream buffers are implementation-controlled; this bounds only staged chunk, store-copy, and largest read segment, never an asset.'} }\r\n /** Same signed/resumable flow, with progress from verified cached or newly committed chunks. */\r\n async downloadWithProgress(m,{signal,retry=1,onProgress}={}) {\r\n  const owner=await this.prepare(m), totalBytes=m.assets.reduce((n,a)=>n+a.bytes,0),verified=new Set();let receivedBytes=0;\r\n  const report=()=>{if(signal?.aborted)return;try{onProgress?.({packId:m.packId,receivedBytes,totalBytes,percent:totalBytes?receivedBytes/totalBytes:1})}catch{/* progress observers cannot affect delivery */}};\r\n  report();\r\n  for(const asset of m.assets)for(const chunk of asset.chunks){\r\n   if(signal?.aborted)throw new Error('download cancelled; resume available');\r\n   await this.ownerFor(m,owner);const key=this.key(m,owner,asset,chunk),saved=await this.store.get(key);\r\n   const valid=saved?.byteLength===chunk.bytes&&await hash(saved)===chunk.sha256;await this.ownerFor(m,owner);\r\n   if(valid){verified.add(key);receivedBytes+=saved.byteLength;report()}\r\n  }\r\n  const baseStore=this.store;\r\n  const progressStore={\r\n   get:key=>baseStore.get(key),\r\n   async put(key,bytes){await baseStore.put(key,bytes);if(!verified.has(key)){verified.add(key);receivedBytes+=bytes.byteLength;report()}},\r\n   removePrefix:prefix=>baseStore.removePrefix(prefix),\r\n  };\r\n  const downloader=new ChunkDownloader({store:progressStore,fetchImpl:this.fetchImpl,policy:this.policy,ownership:()=>this.ownerFor(m,owner),online:this.online,expectedVersion:this.expectedVersion,manifestPublicKey:this.manifestPublicKey});\r\n  const result=await downloader.download(m,{signal,retry});this.maxAccountedAllocationBytes=Math.max(this.maxAccountedAllocationBytes,downloader.maxAccountedAllocationBytes);return result;\r\n }\r\n async verifyStored(m){const owner=await this.prepare(m);for(const a of m.assets)for(const c of a.chunks){await this.ownerFor(m,owner);const v=await this.store.get(this.key(m,owner,a,c));if(!v||v.byteLength!==c.bytes||await hash(v)!==c.sha256)throw new Error(`unverified stored chunk: ${a.path}/${c.index}`);await this.ownerFor(m,owner)}await this.ownerFor(m,owner);return {verified:true,owner,guarantee:'canonical signed ordered chunk list'}}\r\n /** Reconstruct one requested asset after the whole signed chunk set has verified, bounding working memory for large optional packs. */\r\n async readVerifiedAsset(m,path){const verified=await this.verifyStored(m),a=m.assets.find(asset=>asset.path===path);if(!a)throw new Error(`unknown asset path: ${path}`);const out=new Uint8Array(a.bytes);for(const c of a.chunks){await this.ownerFor(m,verified.owner);const value=await this.store.get(this.key(m,verified.owner,a,c));if(!value||value.byteLength!==c.bytes||await hash(value)!==c.sha256)throw new Error(`stored chunk changed: ${a.path}/${c.index}`);await this.ownerFor(m,verified.owner);out.set(value,c.offset)}if(await hash(out)!==a.sha256)throw new Error(`stored asset aggregate hash mismatch: ${a.path}`);await this.ownerFor(m,verified.owner);return Object.freeze({path:a.path,bytes:out})}\r\n /** Bytes are reconstructed only after every signed chunk and aggregate asset digest verify. */\r\n async readVerifiedAssets(m){const verified=await this.verifyStored(m),assets=[];for(const a of m.assets){const out=new Uint8Array(a.bytes);for(const c of a.chunks){await this.ownerFor(m,verified.owner);const value=await this.store.get(this.key(m,verified.owner,a,c));if(!value||value.byteLength!==c.bytes||await hash(value)!==c.sha256)throw new Error(`stored chunk changed: ${a.path}/${c.index}`);await this.ownerFor(m,verified.owner);out.set(value,c.offset)}if(await hash(out)!==a.sha256)throw new Error(`stored asset aggregate hash mismatch: ${a.path}`);await this.ownerFor(m,verified.owner);assets.push(Object.freeze({path:a.path,bytes:out}))}await this.ownerFor(m,verified.owner);return Object.freeze(assets)}\r\n async activate(m,activation){if(typeof activation!=='function')throw new Error('material activation adapter is required');const v=await this.verifyStored(m);await this.ownerFor(m,v.owner);let runtime;try{runtime=await activation({manifest:m,owner:v.owner,prefix:this.prefix(m,v.owner),store:this.store});await this.ownerFor(m,v.owner);return runtime}catch(error){runtime?.dispose?.();throw error}}\r\n async evict(m){const owner=await this.prepare(m);await this.store.removePrefix(this.prefix(m,owner))}\r\n}\r\nexport { hash as sha256Chunk };\r\n","modules/materials/installed-creature-skins.mjs":"import CATALOG from '../../creature/source/creator/creature-skins.json' with { type:'json' };\r\nimport { ACCOUNT_SCOPED_LEDGER_KINDS, isGranted, grantedIds } from '../../unlock-ledger.mjs';\r\nimport { ChunkDownloader, DEFAULT_LOCAL_RESOURCE_POLICY, indexedDbChunkStore, sha256Chunk, verifyChunkManifest } from './chunk-delivery.mjs';\r\nimport { productionMaterialTrust } from './material-config.mjs';\r\nimport { resolvePostDownloadSection } from './post-download-sections.mjs';\r\nimport { unpackVerifiedBundle } from './verified-bundle.mjs';\r\n\r\nconst safeOwner=value=>typeof value==='string'&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(value);\r\nconst mapNames=Object.freeze({basecolor:'basecolor',normal:'normal',roughness:'roughness',height:'height',metalness:'metalness',tintMask:'tintMask',ao:'ao',opacity:'opacity',emissive:'emissive',preview:'preview'});\r\n\r\n/** Read-only installed-pack seam. It verifies the hosted signature and already-stored chunks;\r\n * it never downloads missing chunks. The caller owns disposal and must replace it on account changes. */\r\nexport function createInstalledCreatureSkinSource({account=globalThis.myr5AuthenticatedAccount,fetchImpl=globalThis.fetch,store,policy=DEFAULT_LOCAL_RESOURCE_POLICY,trust=productionMaterialTrust()}={}){\r\n const owner=typeof account==='string'?account:account?.user?.id;\r\n if(!safeOwner(owner)||!trust)return Object.freeze({owner:null,list:async()=>[],resolve:async()=>null,dispose(){}});\r\n if(!ACCOUNT_SCOPED_LEDGER_KINDS?.includes('creature-skin'))return Object.freeze({owner:null,list:async()=>[],resolve:async()=>null,dispose(){}});\r\n let disposed=false,sectionStore=store;const tracks=new Map(),available=new Map(),abort=new AbortController();\r\n const current=()=>!disposed&&(typeof globalThis.myr5AuthenticatedAccount==='string'?globalThis.myr5AuthenticatedAccount:globalThis.myr5AuthenticatedAccount?.user?.id)===owner;\r\n const owned=id=>current()&&isGranted('creature-skin',id,{account:owner});\r\n async function loadTrack(track){\r\n  if(tracks.has(track))return tracks.get(track);\r\n  const work=(async()=>{\r\n   if(!current())throw new Error('Skin owner changed.');\r\n   sectionStore??=indexedDbChunkStore();\r\n   const sectionId=`track-${track}`,manifestKey=`material-manifest/${owner}/${sectionId}/1.0.0`;let resolved,networkFailed=false;\r\n   try{resolved=await resolvePostDownloadSection(sectionId,{fetchImpl:async(...args)=>{try{return await fetchImpl(...args);}catch(error){networkFailed=true;throw error;}},policy,trust,signal:abort.signal});\r\n    if(!current())throw new Error('Skin owner changed.');await sectionStore.put(manifestKey,resolved.manifestBytes);\r\n    if(!current()){await sectionStore.removePrefix(manifestKey);throw new Error('Skin owner changed.');}\r\n   }catch(error){\r\n    // Offline fallback may use only this owner's previously verified signed metadata.\r\n    // A responding server with invalid signatures/version must never be bypassed.\r\n    if(!networkFailed||!current())throw error;\r\n    const cached=await sectionStore.get(manifestKey);if(!cached||cached.byteLength>1024*1024)throw error;\r\n    const manifest=JSON.parse(new TextDecoder().decode(cached));if(manifest.packId!==sectionId||manifest.version!=='1.0.0')throw error;\r\n    await verifyChunkManifest(manifest,trust,policy);resolved={manifest,trust};\r\n   }\r\n   const downloader=new ChunkDownloader({store:sectionStore,fetchImpl,policy,expectedVersion:resolved.manifest.version,manifestPublicKey:resolved.trust,ownership:async info=>current()&&info.packId===sectionId?owner:false});\r\n   const path=`assets/${track}.m5bundle`,asset=await downloader.readVerifiedAsset(resolved.manifest,path);\r\n   const entries=await unpackVerifiedBundle(asset.bytes);\r\n   if(!current())throw new Error('Skin owner changed.');\r\n   return entries;\r\n  })();\r\n  tracks.set(track,work);try{return await work}catch(error){tracks.delete(track);throw error;}\r\n }\r\n async function list(){\r\n  if(!current())return Object.freeze([]);\r\n  const ids=new Set(grantedIds('creature-skin',{account:owner}));\r\n  const rows=CATALOG.skins.filter(skin=>ids.has(skin.id)&&owned(skin.id));\r\n  const groups=new Map();for(const skin of rows){if(!groups.has(skin.track))groups.set(skin.track,[]);groups.get(skin.track).push(skin);}\r\n  const found=[];\r\n  for(const [track,skins] of groups){\r\n   let bundle;try{bundle=await loadTrack(track)}catch{continue;}\r\n   for(const skin of skins){if(!owned(skin.id))continue;const maps={};let valid=true;for(const [name,expected] of Object.entries(skin.maps)){const bytes=bundle.get(`${skin.id}/${name}`);if(!mapNames[name]||!bytes||bytes.byteLength!==expected.bytes||await sha256Chunk(bytes)!==expected.sha256){valid=false;break;}maps[name]=bytes;}\r\n    if(valid&&Object.keys(maps).length){const item=Object.freeze({id:skin.id,displayName:skin.displayName,track,collection:skin.collection,maps:Object.freeze(maps)});available.set(skin.id,item);found.push(item);}\r\n   }\r\n  }\r\n  if(!current()){available.clear();return Object.freeze([]);}\r\n  return Object.freeze(found);\r\n }\r\n async function resolve(id){\r\n  if(!owned(id))return null;\r\n  if(!available.has(id))await list();\r\n  if(!owned(id))return null;\r\n  return available.get(id)??null;\r\n }\r\n return Object.freeze({owner,list,resolve,dispose(){disposed=true;abort.abort();available.clear();tracks.clear();}});\r\n}\r\n\r\nexport const CREATURE_SKIN_MAP_SEMANTICS=Object.freeze({\r\n basecolor:'srgb',tintMask:'srgb',emissive:'srgb',normal:'linear',roughness:'linear',height:'linear',metalness:'linear',ao:'linear',opacity:'linear',\r\n normalConvention:'OpenGL +Y',tint:'linear-space triad lookup driven by the decoded grayscale tint mask',\r\n});\r\n","modules/materials/liquid-runtime.mjs":"/** Trusted, app-owned liquid pilot. Downloaded pack bytes are data only. */\r\nexport function createLiquidPilotRuntime({ host, parameters = {} } = {}) {\r\n  if (!host?.ownerDocument) throw new Error('liquid runtime requires an owned host');\r\n  const canvas = host.ownerDocument.createElement('canvas');\r\n  canvas.width = 96; canvas.height = 64; canvas.className = 'material-liquid-pilot';\r\n  host.replaceChildren(canvas);\r\n  const ctx = canvas.getContext('2d'); let disposed = false; let energy = 0;\r\n  const hue = Number.isFinite(parameters.hue) ? Math.max(250, Math.min(310, parameters.hue)) : 278;\r\n  const strength = Number.isFinite(parameters.strength) ? Math.max(.1, Math.min(1, parameters.strength)) : .6;\r\n  const draw = () => { if (disposed || !ctx) return; const gradient = ctx.createRadialGradient(48, 32, 1, 48, 32, 58); gradient.addColorStop(0, `hsl(${hue - energy * 18} 85% ${44 + energy * 25}%)`); gradient.addColorStop(1, `hsl(${hue} 72% 10%)`); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 96, 64); };\r\n  const pointer = event => { energy = strength; draw(); event.preventDefault?.(); };\r\n  canvas.addEventListener('pointerdown', pointer); draw();\r\n  return Object.freeze({ canvas, dispose() { if (disposed) return; disposed = true; canvas.removeEventListener('pointerdown', pointer); canvas.remove(); }, getDebug: () => ({ disposed, energy, hue }) });\r\n}\r\n","modules/materials/material-config.mjs":"// Release tooling injects only public verification material. Null is valid and fail-closed.\r\nexport const BUILT_PUBLIC_MATERIAL_SIGNING_JWK = typeof __MYR5_MATERIAL_PUBLIC_JWK__ === 'undefined' ? null : __MYR5_MATERIAL_PUBLIC_JWK__;\r\nexport const BUILT_MATERIAL_RESOURCE_POLICY = typeof __MYR5_MATERIAL_RESOURCE_POLICY__ === 'undefined' ? null : __MYR5_MATERIAL_RESOURCE_POLICY__;\r\n\r\nconst usableKey = key => key && typeof key === 'object' && key.kty === 'OKP' && key.crv === 'Ed25519'\r\n  && typeof key.x === 'string' && /^[A-Za-z0-9_-]{43}$/.test(key.x) && !/^A+$/.test(key.x);\r\n\r\nexport function productionMaterialTrust() {\r\n  return usableKey(BUILT_PUBLIC_MATERIAL_SIGNING_JWK) && !('d' in BUILT_PUBLIC_MATERIAL_SIGNING_JWK) ? BUILT_PUBLIC_MATERIAL_SIGNING_JWK : null;\r\n}\r\n\r\nexport function productionMaterialResourcePolicy() {\r\n  const value=BUILT_MATERIAL_RESOURCE_POLICY;\r\n  if(!value||value.trustedOrigins?.length!==1||value.trustedOrigins[0]!=='https://raw.githubusercontent.com'||value.allowedProtocols?.length!==1||value.allowedProtocols[0]!=='https:'||typeof value.pathPrefix!=='string'||!/^\\/[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+\\/[a-f0-9]{40}\\/materials\\/$/.test(value.pathPrefix))return null;\r\n  return Object.freeze({trustedOrigins:Object.freeze([...value.trustedOrigins]),allowedProtocols:Object.freeze([...value.allowedProtocols]),pathPrefix:value.pathPrefix});\r\n}\r\n\r\n// This is application-owned policy, never remotely supplied manifest policy.\r\nexport const MATERIAL_RUNTIME_POLICY = Object.freeze({\r\n  'liquid-amethyst': Object.freeze({ runtimeType: 'liquid-v1', parameterKeys: Object.freeze(['hue', 'strength']) }),\r\n});\r\n","modules/materials/material-controller.mjs":"/** Account-scoped material lifecycle; stored pack bytes are declarative data only. */\r\nimport { ChunkDownloader, indexedDbChunkStore, DEFAULT_LOCAL_RESOURCE_POLICY, verifyChunkManifest, canonicalChunkPayload } from './chunk-delivery.mjs';\r\nimport { resolveMaterialBundle, createMaterialRuntime, verifiedMaterialAccount, canonicalMaterialPayload, isSignedMaterialManifest } from './material-pack-entry.mjs';\r\nimport { productionMaterialTrust } from './material-config.mjs';\r\nimport { existingWorkoutIdleAdapter } from '../../packs/workout-idle-adapter.mjs';\r\nconst KEY='mom.material-controller.v2',OWNER=/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/;\r\nconst mountedControls=new WeakMap();\r\nconst read=s=>{try{const x=JSON.parse(s.getItem(KEY)||'');return x?.v===2&&x.accounts?x:{v:2,accounts:{}}}catch{return {v:2,accounts:{}}}},sum=m=>m.assets.reduce((n,x)=>n+x.bytes,0);\r\nconst owner=a=>{const id=a?.user?.id;if(typeof id!=='string'||!OWNER.test(id))throw new Error('a stable authenticated account identity is required');return id};\r\nconst semver=v=>{const x=/^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/.exec(v||'');if(!x)throw new Error('unsupported app version format');return x.slice(1).map(Number)},supported=(a,b)=>{a=semver(a);b=semver(b);return a[0]>b[0]||a[0]===b[0]&&(a[1]>b[1]||a[1]===b[1]&&a[2]>=b[2])};\r\nexport class MaterialController {\r\n constructor({account,workoutOwner,host,storage=globalThis.localStorage,chunkStore=indexedDbChunkStore(),fetchImpl=globalThis.fetch,policy,appVersion='1.0.0',onChange=()=>{},bootId}={}){if(!host?.ownerDocument)throw Error('material controls require an application host');Object.assign(this,{account,owner:owner(account),host,storage,chunkStore,fetchImpl:(...args)=>fetchImpl.call(globalThis,...args),policy,appVersion,onChange,bootId:bootId||crypto.randomUUID?.()||String(Date.now()),workout:existingWorkoutIdleAdapter({owner:workoutOwner}),db:read(storage),runtime:null,abort:null,bundle:null,disposed:false})}\r\n record(){return this.db.accounts[this.owner]||{active:null,candidate:null}} save(){this.storage.setItem(KEY,JSON.stringify(this.db))} status(){const r=this.record(),x=r.candidate||r.active||{};return {state:r.candidate?.state||r.active?.state||'absent',receivedBytes:x.receivedBytes||0,totalBytes:x.chunkManifest?sum(x.chunkManifest):0,error:x.error,pending:r.candidate?.state==='pending-equip',active:!!r.active}} emit(){this.save();this.onChange(this.status())}\r\n owned(m){return verifiedMaterialAccount(this.account).getEntitlements().some(x=>x.packId===m.packId)}\r\n epoch=0; revoked=false;\r\n assertLive(m=this.bundle?.manifest||this.record().active?.materialManifest,epoch=this.epoch){if(this.disposed)throw Error('material controller is disposed');if(this.revoked||epoch!==this.epoch)throw Error('material operation was revoked');if(this.account?.user?.id!==this.owner||(m&&!this.owned(m))){this.epoch++;this.revoked=true;this.cancel();this.runtime?.dispose?.();this.runtime=null;throw Error('material ownership was revoked or account changed')}}\r\n downloader(b=this.bundle,epoch=this.epoch){if(!b)throw Error('signed material metadata has not been loaded');const {manifest,trust}=b;return new ChunkDownloader({store:this.chunkStore,fetchImpl:this.fetchImpl,policy:this.policy||DEFAULT_LOCAL_RESOURCE_POLICY,expectedVersion:manifest.version,manifestPublicKey:trust,ownership:async x=>{this.assertLive(manifest,epoch);return x.packId===manifest.packId&&x.version===manifest.version?this.owner:false}})}\r\n validateBundle(b){const {manifest:m,chunkManifest:c}=b;if(m.version!==c.version||m.packId!==c.packId||m.keyId!==c.keyId||m.assets.length!==c.assets.length||c.assets.some((a,i)=>a.path!==m.assets[i].path||a.bytes!==m.assets[i].bytes||a.sha256!==m.assets[i].sha256))throw Error('signed material metadata is not bound to signed chunk manifest');if(!supported(this.appVersion,m.minAppVersion))throw Error('material requires a newer app version')}\r\n async load(options={}){const epoch=this.epoch;this.assertLive();const b=await resolveMaterialBundle(options);this.assertLive(b.manifest,epoch);this.validateBundle(b);this.bundle=b;return this.status()}\r\n putCandidate(c){this.db.accounts[this.owner]={...this.record(),candidate:c};this.emit()}\r\n async download(){this.assertLive();const b=this.bundle,epoch=this.epoch,d=this.downloader(b,epoch),old=this.record().candidate,abort=new AbortController();this.abort=abort;const candidate={state:'downloading',receivedBytes:old?.receivedBytes||0,chunkManifest:b.chunkManifest,materialManifest:b.manifest};this.putCandidate(candidate);try{const r=await d.download(b.chunkManifest,{signal:abort.signal});this.assertLive(b.manifest,epoch);if(this.record().candidate!==candidate)throw Error('material candidate changed during download');this.putCandidate({...candidate,state:'verified',receivedBytes:r.receivedBytes});return this.status()}catch(e){this.assertLive(b.manifest,epoch);if(this.record().candidate===candidate)this.putCandidate({...candidate,state:'failed',error:e.message||'download failed'});throw e}finally{if(this.abort===abort)this.abort=null}}\r\n cancel(){this.abort?.abort()} async equipAfterRestart(){this.assertLive();const c=this.record().candidate,b=this.bundle,epoch=this.epoch;if(!b||c?.state!=='verified'||c.materialManifest?.signature!==b.manifest.signature||c.chunkManifest?.signature!==b.chunkManifest.signature||canonicalMaterialPayload(c.materialManifest)!==canonicalMaterialPayload(b.manifest)||canonicalChunkPayload(c.chunkManifest)!==canonicalChunkPayload(b.chunkManifest))throw Error('only verified material chunks can be equipped');await this.downloader(b,epoch).verifyStored(b.chunkManifest);this.assertLive(b.manifest,epoch);if(this.record().candidate!==c)throw Error('material candidate changed during verification');this.putCandidate({...c,state:'pending-equip',bootId:this.bootId});return this.status()}\r\n async savedBundle(r,options={}){\r\n  if(!r?.materialManifest||!r?.chunkManifest)throw Error('saved material metadata is incomplete');\r\n  const epoch=this.epoch;this.assertLive(r.materialManifest,epoch);\r\n  // Authenticate the exact persisted metadata using application-owned trust, never a stored key.\r\n  const trust=options.testTrust?.purpose==='test-only-signed-material'?options.testTrust.publicKey:productionMaterialTrust(),manifest=r.materialManifest,chunkManifest=r.chunkManifest;\r\n  if(!isSignedMaterialManifest(manifest)||!trust||trust.kty!=='OKP'||trust.crv!=='Ed25519')throw Error('saved material manifest signature is invalid');\r\n  const key=await crypto.subtle.importKey('jwk',trust,{name:'Ed25519'},false,['verify']);this.assertLive(manifest,epoch);\r\n  const signature=Uint8Array.from(atob(manifest.signature),x=>x.charCodeAt(0));\r\n  const valid=await crypto.subtle.verify({name:'Ed25519'},key,signature,new TextEncoder().encode(canonicalMaterialPayload(manifest)));this.assertLive(manifest,epoch);\r\n  if(!valid)throw Error('saved material manifest signature is invalid');\r\n  await verifyChunkManifest(chunkManifest,trust,this.policy||DEFAULT_LOCAL_RESOURCE_POLICY);this.assertLive(manifest,epoch);\r\n  const b={manifest,chunkManifest,trust};this.validateBundle(b);return b;\r\n }\r\n async activate(b,c,{restore=false,epoch=this.epoch}={}){\r\n  this.assertLive(b.manifest,epoch);const idle=await this.workout.saveAndConfirmIdle();let next;\r\n  try{\r\n   this.assertLive(b.manifest,epoch);if(!(idle?.saved&&idle?.idle))throw Error(idle?.reason||'workout save/idle acknowledgment is required before activation');\r\n   const assets=await this.downloader(b,epoch).readVerifiedAssets(b.chunkManifest);this.assertLive(b.manifest,epoch);\r\n   next=await createMaterialRuntime(b.manifest.packId,{verifiedAssets:assets},b.manifest,this.host);this.assertLive(b.manifest,epoch);\r\n   if((restore?this.record().active:this.record().candidate)!==c)throw Error('material record changed during activation');\r\n   const old=this.runtime;this.runtime=next;next=null;old?.dispose?.();\r\n   if(!restore){this.db.accounts[this.owner]={active:{...c,state:'active'},candidate:null};this.emit()}\r\n   return this.status();\r\n  }finally{try{next?.dispose?.()}finally{idle?.release?.()}}\r\n }\r\n async restorePending(options={}){\r\n  this.assertLive();const r=this.record(),c=r.candidate,epoch=this.epoch;\r\n  if(c?.state!=='pending-equip'||c.bootId===this.bootId)return r.active?this.restoreActive(options):this.status();\r\n  try{const b=await this.savedBundle(c,options);this.assertLive(b.manifest,epoch);return await this.activate(b,c,{epoch})}\r\n  catch(e){this.assertLive(c.materialManifest,epoch);if(this.record().candidate===c)this.putCandidate({...c,state:'pending-equip',error:`activation failed: ${e.message||e}`});if(r.active)await this.restoreActive(options);throw e}\r\n }\r\n async restoreActive(options={}){this.assertLive();const a=this.record().active,epoch=this.epoch;if(!a)return this.status();const b=await this.savedBundle(a,options);this.assertLive(b.manifest,epoch);return this.activate(b,a,{restore:true,epoch})}\r\n revoke(){this.epoch++;this.revoked=true;this.cancel();this.runtime?.dispose?.();this.runtime=null;this.bundle=null;this.db.accounts[this.owner]={active:null,candidate:null};this.emit()} dispose(){if(this.disposed)return;this.epoch++;this.disposed=true;this.cancel();this.runtime?.dispose?.();this.runtime=null}\r\n}\r\nexport function mountMaterialControls({document=globalThis.document,account,workoutOwner,fixture,testTrust,policy,fetchImpl,appVersion}={}){const panel=document?.getElementById('materialControls'),host=document?.getElementById('materialRuntime');if(!panel||!host)return null;const prior=mountedControls.get(panel);if(prior)return prior;const status=panel.querySelector('[data-material-status]'),download=panel.querySelector('[data-material-download]'),cancel=panel.querySelector('[data-material-cancel]'),equip=panel.querySelector('[data-material-equip]');const render=v=>{status.textContent=v.error||`${v.state}${v.totalBytes?` · ${v.receivedBytes}/${v.totalBytes} bytes`:''}`;download.disabled=!account||v.state==='downloading'||v.pending;cancel.hidden=v.state!=='downloading';equip.disabled=v.state!=='verified'};let c;try{c=new MaterialController({account,workoutOwner,host,policy,fetchImpl,appVersion,onChange:render})}catch(e){render({state:'failed',error:e.message});return null}const options={fixture,testTrust},prepare=async()=>{if(!c.bundle)await c.load(options);return c},dl=async()=>{try{await(await prepare()).download()}catch(e){render({...c.status(),error:e.message})}},eq=async()=>{try{await(await prepare()).equipAfterRestart();render({...c.status(),error:'Verified. Reload Coach to activate.'})}catch(e){render({...c.status(),error:e.message})}},changed=()=>{try{c.assertLive()}catch{c.revoke();render({...c.status(),error:'Material access changed.'})}},cleared=()=>{c.revoke();render({...c.status(),error:'Material access changed.'})};download.addEventListener('click',dl);cancel.addEventListener('click',()=>c.cancel());equip.addEventListener('click',eq);globalThis.addEventListener?.('myr5:account-ready',changed);globalThis.addEventListener?.('myr5:account-cleared',cleared);render(c.status());void c.restorePending(options).catch(e=>render({...c.status(),error:e.message}));const dispose=c.dispose.bind(c);c.dispose=()=>{download.removeEventListener('click',dl);equip.removeEventListener('click',eq);globalThis.removeEventListener?.('myr5:account-ready',changed);globalThis.removeEventListener?.('myr5:account-cleared',cleared);mountedControls.delete(panel);dispose()};mountedControls.set(panel,c);return c}\r\n","modules/materials/material-pack-entry.mjs":"import { createIsolatedPackControl, authenticatedPackAccount } from '../../packs/isolated-pack-control.mjs';\r\nimport { MATERIAL_RUNTIME_POLICY, productionMaterialTrust } from './material-config.mjs';\r\nimport { verifyChunkManifest } from './chunk-delivery.mjs';\r\n\r\nexport const MATERIAL_MANIFEST_SCHEMA = 'mom-material-pack-v1';\r\nconst hex = value => typeof value === 'string' && /^[a-f0-9]{64}$/i.test(value);\r\nconst b64 = value => typeof value === 'string' && /^[A-Za-z0-9+/]+={0,2}$/.test(value) && value.length >= 80;\r\nconst plain = value => value && typeof value === 'object' && !Array.isArray(value);\r\nconst resource = asset => plain(asset) && typeof asset.path === 'string' && /^assets\\/[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(asset.path) && !asset.path.includes('..') && !/\\.(?:m?js|cjs|html?)$/i.test(asset.path) && Number.isSafeInteger(asset.bytes) && asset.bytes > 0 && asset.bytes <= 512 * 1024 && hex(asset.sha256);\r\nconst stable = value => { if (Array.isArray(value)) return value.map(stable); if (plain(value)) return Object.fromEntries(Object.keys(value).sort().map(key => [key, stable(value[key]) ])); if (value === null || ['string', 'number', 'boolean'].includes(typeof value)) return value; throw new Error('parameters must be declarative JSON'); };\r\nexport const canonicalMaterialPayload = manifest => JSON.stringify(stable({ schema: manifest.schema, packId: manifest.packId, version: manifest.version, minAppVersion: manifest.minAppVersion, sha256: manifest.sha256, alg: manifest.alg, keyId: manifest.keyId, materialId: manifest.materialId, runtimeType: manifest.runtimeType, parameters: manifest.parameters, assets: manifest.assets.map(({ path, bytes, sha256 }) => ({ path, bytes, sha256 })) }));\r\nexport function isSignedMaterialManifest(m) { const policy = MATERIAL_RUNTIME_POLICY[m?.materialId]; return plain(m) && m.schema === MATERIAL_MANIFEST_SCHEMA && typeof m.packId === 'string' && m.packId === m.materialId && typeof m.version === 'string' && typeof m.minAppVersion === 'string' && hex(m.sha256) && m.alg === 'Ed25519' && m.keyId === 'mom-material-production-v1' && b64(m.signature) && policy?.runtimeType === m.runtimeType && plain(m.parameters) && Object.keys(m.parameters).every(key => policy.parameterKeys.includes(key)) && Array.isArray(m.assets) && m.assets.length > 0 && m.assets.length <= 16 && m.assets.every(resource) && m.assets.reduce((sum, asset) => sum + asset.bytes, 0) <= 2 * 1024 * 1024; }\r\nconst bytes = value => Uint8Array.from((typeof atob === 'function' ? atob(value) : Buffer.from(value, 'base64').toString('binary')), char => char.charCodeAt(0));\r\nasync function verified(m, key) { if (!isSignedMaterialManifest(m) || !plain(key) || key.kty !== 'OKP' || key.crv !== 'Ed25519') return false; try { const crypto = globalThis.crypto?.subtle; if (!crypto) return false; const trusted = await crypto.importKey('jwk', key, { name: 'Ed25519' }, false, ['verify']); return crypto.verify({ name: 'Ed25519' }, trusted, bytes(m.signature), new TextEncoder().encode(canonicalMaterialPayload(m))); } catch { return false; } }\r\nexport async function resolveMaterialManifest({ fixture = null, testTrust = null } = {}) { const trust = testTrust?.purpose === 'test-only-signed-material' ? testTrust.publicKey : (fixture ? null : productionMaterialTrust()); if (!trust) throw new Error('Material unavailable: trusted public configuration is not configured'); const manifest = fixture?.manifest; if (!fixture || !(await verified(manifest, trust))) throw new Error('Material manifest signature is invalid'); return { manifest, source: fixture.source }; }\r\nexport async function resolveMaterialBundle({ fixture = null, testTrust = null } = {}) {\r\n const { manifest } = await resolveMaterialManifest({ fixture, testTrust }); const chunks = fixture?.chunkManifest;\r\n if (!chunks) throw new Error('Material chunk manifest is unavailable');\r\n const trust = testTrust?.purpose === 'test-only-signed-material' ? testTrust.publicKey : productionMaterialTrust();\r\n await verifyChunkManifest(chunks, trust, fixture?.policy);\r\n if (chunks.packId !== manifest.packId || chunks.version !== manifest.version || chunks.keyId !== manifest.keyId || chunks.assets.length !== manifest.assets.length || chunks.assets.some((asset, i) => asset.path !== manifest.assets[i].path || asset.bytes !== manifest.assets[i].bytes || asset.sha256 !== manifest.assets[i].sha256)) throw new Error('signed material metadata is not bound to signed chunk manifest');\r\n return { manifest, chunkManifest: chunks, trust };\r\n}\r\nexport function verifiedMaterialAccount(account) {\r\n const grants=authenticatedPackAccount(account);\r\n return Object.freeze({...grants,getEntitlements:()=>grants.getEntitlements().filter(item=>Object.hasOwn(MATERIAL_RUNTIME_POLICY,item.packId))});\r\n}\r\nexport async function createMaterialRuntime(id, record, manifest, host) { if (!record?.verifiedAssets || !isSignedMaterialManifest(manifest) || manifest.materialId !== id) throw new Error('verified stored material data is required'); if (manifest.runtimeType !== 'liquid-v1') throw new Error('unknown material runtime'); const asset=record.verifiedAssets[0]; if (!asset?.bytes || asset.bytes.byteLength>64*1024) throw new Error('material declarative config is missing or too large'); let config;try{config=JSON.parse(new TextDecoder().decode(asset.bytes));}catch{throw new Error('material declarative config is invalid')}if(!plain(config)||Object.keys(config).some(key=>!['hue','strength'].includes(key))||Object.values(config).some(value=>typeof value!=='number'||!Number.isFinite(value)))throw new Error('material declarative config is invalid'); const { createLiquidPilotRuntime } = await import('./liquid-runtime.mjs'); return createLiquidPilotRuntime({ host, parameters: {...manifest.parameters,...config} }); }\r\nexport function createMaterialPackControl({ account, host, ...options } = {}) { return createIsolatedPackControl({ account: verifiedMaterialAccount(account), runtimeFactory: (id, record, manifest) => createMaterialRuntime(id, record, manifest, host), ...options }); }\r\n","modules/materials/post-download-controller.mjs":"import { ChunkDownloader, DEFAULT_LOCAL_RESOURCE_POLICY, indexedDbChunkStore } from './chunk-delivery.mjs';\r\nimport { resolvePostDownloadSection } from './post-download-sections.mjs';\r\nimport { productionMaterialTrust } from './material-config.mjs';\r\n\r\nconst stable=id=>typeof id==='string'&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(id);\r\nconst getOwner=account=>account?.user?.id;\r\n\r\n/** Resumable section delivery for a signed-in owner, invoked only after an explicit user action. */\r\nexport async function downloadPostDownloadSection({id,account=globalThis.myr5AuthenticatedAccount,authorize=()=>false,fetchImpl=globalThis.fetch,store=indexedDbChunkStore(),policy=DEFAULT_LOCAL_RESOURCE_POLICY,trust=productionMaterialTrust(),signal,onProgress}={}){\r\n const owner=getOwner(account);if(!stable(owner))throw new Error('Sign in before downloading a post-download section.');\r\n if(!authorize(id,account))throw new Error('Section access has not been authorized.');\r\n // An explicit null is a logout, never permission to reuse the captured account.\r\n const current=()=>Object.hasOwn(globalThis,'myr5AuthenticatedAccount')?globalThis.myr5AuthenticatedAccount:account;\r\n const allowed=()=>getOwner(current())===owner&&authorize(id,current());\r\n if(signal?.aborted||!allowed())throw new Error('Section download cancelled or account changed.');\r\n const resolved=await resolvePostDownloadSection(id,{fetchImpl,policy,trust,signal});\r\n const downloader=new ChunkDownloader({store,fetchImpl,policy,expectedVersion:resolved.manifest.version,manifestPublicKey:resolved.trust,ownership:async info=>info.packId===id&&allowed()?owner:false});\r\n const result=await downloader.downloadWithProgress(resolved.manifest,{signal,onProgress});await downloader.verifyStored(resolved.manifest);\r\n if(signal?.aborted||!allowed())throw new Error('Section download cancelled or account changed.');\r\n const manifestKey=`material-manifest/${owner}/${id}/${resolved.manifest.version}`;\r\n await store.put(manifestKey,resolved.manifestBytes);\r\n if(signal?.aborted||!allowed()){await store.removePrefix(manifestKey);throw new Error('Section download cancelled or account changed.');}\r\n return Object.freeze({section:resolved.section,manifest:resolved.manifest,downloader,result,isOwned:()=>allowed()});\r\n}\r\n\r\nexport async function downloadAllPostDownloadSections(options={}){\r\n options={...options,account:options.account??globalThis.myr5AuthenticatedAccount};\r\n const {fullPostDownloadSectionIds}=await import('./post-download-sections.mjs');const output=[];\r\n for(const id of fullPostDownloadSectionIds()){if(options.signal?.aborted)throw new Error('section download paused; resume is available');output.push(await downloadPostDownloadSection({...options,id}));}\r\n return Object.freeze(output);\r\n}\r\n\r\n/** Starter set: exactly the selected exercise packets plus Meditation. */\r\nexport async function downloadStarterPostDownloadSections(options={}){\r\n options={...options,account:options.account??globalThis.myr5AuthenticatedAccount};\r\n const {POST_DOWNLOAD_SECTIONS,starterPostDownloadSectionIds}=await import('./post-download-sections.mjs');\r\n const ids=starterPostDownloadSectionIds(options.account??globalThis.myr5AuthenticatedAccount);\r\n const output=[];\r\n for(const id of ids){if(options.signal?.aborted)throw new Error('section download paused; resume is available');output.push(await downloadPostDownloadSection({...options,id}));}\r\n return Object.freeze(output);\r\n}\r\n","modules/materials/post-download-sections.mjs":"import { DEFAULT_LOCAL_RESOURCE_POLICY, verifyChunkManifest } from './chunk-delivery.mjs';\r\nimport { productionMaterialTrust } from './material-config.mjs';\r\n\r\n// Metadata is intentionally small and core-safe. Track bundles combine both skin collections;\r\n// the source manifests/chunk bytes stay on the post-download host and require a real signature.\r\nimport { readSelectedTracks } from '../../chosen-styles.mjs';\r\n\r\nexport const POST_DOWNLOAD_SECTIONS = Object.freeze([\r\n  ...['chest','quads','glutes','arms','yoga','martial-arts','cardio','meditation'].map(track => Object.freeze({ id:`track-${track}`, track, title:track==='arms'?'Arms & Shoulders':track[0].toUpperCase()+track.slice(1), kind:'creature-skins', path:`/materials/track-${track}/chunk-manifest.json`, version:'1.0.0' })),\r\n  Object.freeze({ id:'coach-ships-biomes', title:'Ships and worlds', kind:'ships-biomes', path:'/materials/coach-ships-biomes/chunk-manifest.json', version:'1.0.0' }),\r\n]);\r\n\r\nconst TRACK_PACKET_ALIAS=Object.freeze({'arms-shoulders':'arms'});\r\nexport function starterPostDownloadSectionIds(account=globalThis.myr5AuthenticatedAccount){\r\n  const picked=readSelectedTracks({account});\r\n  if(picked.length!==2)return Object.freeze([]);\r\n  const tracks=[...new Set(['meditation',...picked].map(track=>TRACK_PACKET_ALIAS[track]??track))];\r\n  return Object.freeze(tracks.map(track=>`track-${track}`));\r\n}\r\nexport function fullPostDownloadSectionIds(){ return Object.freeze(POST_DOWNLOAD_SECTIONS.map(section=>section.id)); }\r\n\r\nconst base64Signature = value => typeof value === 'string' && /^[A-Za-z0-9+/]+={0,2}$/.test(value) && value.length >= 80;\r\nasync function boundedManifest(response){\r\n  const limit=1024*1024,reader=response.body?.getReader();if(!reader)throw new Error('Streaming manifest response is required.');\r\n  let total=0;const parts=[];\r\n  try{for(;;){const part=await reader.read();if(part.done)break;total+=part.value.byteLength;if(total>limit)throw new Error('Section manifest exceeds local size policy.');parts.push(part.value);}const bytes=new Uint8Array(total);let offset=0;for(const part of parts){bytes.set(part,offset);offset+=part.byteLength;}return {manifest:JSON.parse(new TextDecoder().decode(bytes)),bytes};}\r\n  finally{await reader.cancel().catch(()=>{});}\r\n}\r\n\r\nexport async function resolvePostDownloadSection(id, { fetchImpl = globalThis.fetch, trust = productionMaterialTrust(), policy = DEFAULT_LOCAL_RESOURCE_POLICY, signal } = {}) {\r\n  const section = POST_DOWNLOAD_SECTIONS.find(row => row.id === id);\r\n  if (!section) throw new Error('Unknown post-download section.');\r\n  if (!trust) throw new Error('This section is unavailable until a production signing key is configured.');\r\n  const response = await fetchImpl(section.path, { credentials:'same-origin', redirect:'error', cache:'no-store', signal });\r\n  if (!response.ok || response.redirected) throw new Error('The signed section manifest could not be loaded.');\r\n  const {manifest,bytes:manifestBytes} = await boundedManifest(response);\r\n  if (manifest.packId !== section.id || manifest.version !== section.version || !base64Signature(manifest.signature)) throw new Error('The section has no valid published signature.');\r\n  await verifyChunkManifest(manifest, trust, policy);\r\n  return Object.freeze({ section, manifest, manifestBytes, trust, policy });\r\n}\r\n\r\nexport function postDownloadSectionStatus(id, { trust = productionMaterialTrust(), manifest = null } = {}) {\r\n  const section = POST_DOWNLOAD_SECTIONS.find(row => row.id === id);\r\n  if (!section) return Object.freeze({ available:false, reason:'unknown-section' });\r\n  if (!trust) return Object.freeze({ available:false, reason:'signing-key-not-configured' });\r\n  if (!manifest || manifest.packId !== id || manifest.version !== section.version || !base64Signature(manifest.signature)) return Object.freeze({ available:false, reason:'signed-hosted-manifest-required' });\r\n  // Syntactic metadata is not evidence of a trusted signature. Only the async\r\n  // resolver may make a section available after cryptographic verification.\r\n  return Object.freeze({ available:false, reason:'signature-verification-required' });\r\n}\r\n","modules/materials/post-download-ui.mjs":"import { downloadAllPostDownloadSections, downloadPostDownloadSection, downloadStarterPostDownloadSections } from './post-download-controller.mjs';\r\nimport { POST_DOWNLOAD_SECTIONS, resolvePostDownloadSection, starterPostDownloadSectionIds } from './post-download-sections.mjs';\r\nimport { productionMaterialTrust } from './material-config.mjs';\r\n\r\nexport function postDownloadChoices(account, availableIds) {\r\n  if (!account?.user?.id || !(availableIds instanceof Set)) return Object.freeze({ starter:[], individual:[], all:[] });\r\n  const all=POST_DOWNLOAD_SECTIONS.map(section=>section.id).filter(id=>availableIds.has(id));\r\n  const wantedStarter=starterPostDownloadSectionIds(account), starter=wantedStarter.every(id=>availableIds.has(id))?wantedStarter:[];\r\n  const selected=new Set(starter);\r\n  return Object.freeze({\r\n    starter:Object.freeze(starter),\r\n    individual:Object.freeze(all.filter(id=>id==='coach-ships-biomes'||(id.startsWith('track-')&&!selected.has(id)))),\r\n    all:Object.freeze(all),\r\n  });\r\n}\r\n\r\n/** Account-scoped explicit-action controls; anonymous core download remains independent. */\r\nexport function mountPostDownloadSections({ host, account=globalThis.myr5AuthenticatedAccount, fetchImpl=globalThis.fetch, trust=productionMaterialTrust(), policy, store }={}) {\r\n  const accountId=value=>typeof value?.user?.id==='string'?value.user.id:null;\r\n  if (!host || !trust || !accountId(account)) return null;\r\n  const panel=document.createElement('section');\r\n  panel.className='post-download-sections';panel.hidden=true;\r\n  panel.innerHTML='<h3>Extra offline packs</h3><p data-status role=\"status\"></p><button type=\"button\" data-retry hidden>Try again</button><div data-actions></div><progress max=\"1\" value=\"0\" hidden aria-label=\"Extra pack download progress\"></progress><button type=\"button\" data-pause hidden>Pause</button>';\r\n  host.append(panel);\r\n  const status=panel.querySelector('[data-status]'), actions=panel.querySelector('[data-actions]'), progress=panel.querySelector('progress'), pause=panel.querySelector('[data-pause]'), retry=panel.querySelector('[data-retry]');\r\n  let currentAccount=account, owner=null, available=new Map(), controller=null, pausedIds=null,epoch=0,disposed=false,checking=null;\r\n  const allowed=()=>!disposed&&!!owner&&accountId(Object.hasOwn(globalThis,'myr5AuthenticatedAccount')?globalThis.myr5AuthenticatedAccount:currentAccount)===owner;\r\n  const labels=new Map(POST_DOWNLOAD_SECTIONS.map(section=>[section.id,section.title]));\r\n  function clear(){available=new Map();actions.replaceChildren();panel.hidden=true;retry.hidden=true;status.textContent='';}\r\n  function button(label,ids,operation){const b=document.createElement('button');b.type='button';b.disabled=!!controller;b.dataset.sections=ids.join(' ');const resume=pausedIds&&JSON.stringify(pausedIds)===JSON.stringify(ids);b.textContent=resume?label.replace(/^Download/,'Resume'):label;b.addEventListener('click',()=>{pausedIds=null;void run(ids,operation);});actions.append(b);}\r\n  function render(){\r\n    if(!allowed()){clear();return;}\r\n    const choices=postDownloadChoices(currentAccount,new Set(available.keys()));\r\n    actions.replaceChildren();\r\n    if(choices.starter.length)button('Download starter styles',choices.starter,opts=>downloadStarterPostDownloadSections(opts));\r\n    for(const id of choices.individual)button(id==='coach-ships-biomes'?'Download ships & worlds':`Download ${labels.get(id)} pack`,[id],opts=>downloadPostDownloadSection({...opts,id}));\r\n    if(choices.all.length===POST_DOWNLOAD_SECTIONS.length)button('Download every extra',choices.all,opts=>downloadAllPostDownloadSections(opts));\r\n    if(actions.childElementCount)panel.hidden=false;\r\n  }\r\n  function report(info){progress.hidden=false;progress.value=Math.max(0,Math.min(1,Number(info?.percent)||0));status.textContent=`Downloading ${labels.get(info?.packId)||'offline packs'} · ${Math.round(progress.value*100)}%`;}\r\n  async function run(ids,operation){\r\n    if(controller||!allowed())return;\r\n    const runEpoch=epoch,runOwner=owner,runAccount=currentAccount,abort=new AbortController();controller=abort;\r\n    const active=()=>runEpoch===epoch&&allowed()&&owner===runOwner;\r\n    pause.hidden=false;pause.disabled=false;pause.textContent='Pause';status.textContent='Preparing verified download…';render();\r\n    try{await operation({signal:abort.signal,account:runAccount,authorize:()=>active(),fetchImpl,trust,policy,store,onProgress:info=>{if(active())report(info);}});if(active()){status.textContent='Offline packs ready.';progress.value=1;window.dispatchEvent(new CustomEvent('myr5:sections-installed',{detail:{ownerId:runOwner,sections:[...ids]}}));}}\r\n    catch(error){if(active()){if(abort.signal.aborted){pausedIds=[...ids];status.textContent='Download paused. Resume is available.';}else status.textContent=error.message||'Download unavailable. Try again later.';}}\r\n    finally{if(controller===abort){controller=null;pause.hidden=true;if(active())render();}}\r\n  }\r\n  pause.addEventListener('click',()=>{if(controller){controller.abort();pause.disabled=true;}});\r\n  retry.addEventListener('click',()=>void refresh());\r\n  async function refresh(){\r\n    const refreshEpoch=++epoch;controller?.abort();controller=null;checking?.abort();checking=new AbortController();\r\n    pausedIds=null;pause.hidden=true;progress.hidden=true;progress.value=0;retry.hidden=true;owner=accountId(currentAccount);\r\n    if(!owner||disposed){clear();return;}\r\n    panel.hidden=false;actions.replaceChildren();status.textContent='Checking signed offline packs…';\r\n    const signal=checking.signal;\r\n    // A section that can't be resolved (bad network, unsupported browser crypto, a stale manifest)\r\n    // is logged, not swallowed: the others still render, and the status line says what to do next.\r\n    const results=await Promise.all(POST_DOWNLOAD_SECTIONS.map(async section=>{\r\n      try{return [section.id,await resolvePostDownloadSection(section.id,{fetchImpl,trust,policy,signal})];}\r\n      catch(error){if(!signal.aborted)console.warn(`Offline pack unavailable: ${section.id}`,error);return [section.id,null];}\r\n    }));\r\n    if(refreshEpoch!==epoch||!allowed()||signal.aborted)return;\r\n    checking=null;\r\n    available=new Map(results.filter(([,value])=>value));\r\n    const failed=results.length-available.size;\r\n    render();\r\n    if(!available.size){retry.hidden=false;panel.hidden=false;status.textContent='Could not check offline packs. Check your connection, then try again.';}\r\n    else if(failed){retry.hidden=false;status.textContent=`${failed} of ${results.length} packs could not be checked. Try again for the rest.`;}\r\n    else if(!actions.childElementCount)panel.hidden=true;\r\n    else status.textContent='';\r\n  }\r\n  const onReady=event=>{currentAccount=event.detail;if(owner&&accountId(currentAccount)===owner){if(!controller&&!pausedIds&&!checking&&available.size<POST_DOWNLOAD_SECTIONS.length)void refresh();else render();return;}void refresh();};\r\n  const onCleared=()=>{currentAccount=null;void refresh();};\r\n  window.addEventListener('myr5:account-ready',onReady);window.addEventListener('myr5:account-cleared',onCleared);\r\n  void refresh();\r\n  return Object.freeze({panel,refresh,dispose(){disposed=true;++epoch;controller?.abort();checking?.abort();window.removeEventListener('myr5:account-ready',onReady);window.removeEventListener('myr5:account-cleared',onCleared);panel.remove();}});\r\n}\r\n","modules/materials/verified-bundle.mjs":"import { sha256Chunk } from './chunk-delivery.mjs';\r\n\r\n/** Decode the deterministic M5 bundle only after its enclosing asset passed ChunkDownloader verification. */\r\nexport async function unpackVerifiedBundle(value) {\r\n  const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);\r\n  if (bytes.byteLength < 6 || bytes.byteLength>64*1024*1024) throw new Error('verified bundle size exceeds policy');\r\n  const headerBytes = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);\r\n  if (headerBytes < 2 || headerBytes > 1024 * 1024 || headerBytes > bytes.byteLength - 4) throw new Error('invalid verified bundle index size');\r\n  const index = JSON.parse(new TextDecoder().decode(bytes.subarray(4,4+headerBytes)));\r\n  if (!index || typeof index !== 'object' || Array.isArray(index) || Object.keys(index).length > 512) throw new Error('invalid verified bundle index');\r\n  const contentStart=4+headerBytes, values=new Map(); let expectedOffset=0;\r\n  for (const [name,entry] of Object.entries(index)) {\r\n    if (!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(name) || name.includes('..') || name.includes('//') || name.endsWith('/') || name.length>300 || !entry || !Number.isSafeInteger(entry.offset) || entry.offset!==expectedOffset || !Number.isSafeInteger(entry.bytes) || entry.bytes<1 || !/^[a-f0-9]{64}$/.test(entry.sha256||'')) throw new Error(`invalid bundled entry: ${name}`);\r\n    const end=contentStart+entry.offset+entry.bytes;\r\n    if (end>bytes.byteLength) throw new Error(`truncated bundled entry: ${name}`);\r\n    const data=bytes.slice(contentStart+entry.offset,end);\r\n    if (await sha256Chunk(data)!==entry.sha256) throw new Error(`bundled entry integrity failure: ${name}`);\r\n    values.set(name,data); expectedOffset+=entry.bytes;\r\n  }\r\n  if(contentStart+expectedOffset!==bytes.byteLength)throw new Error('verified bundle has unindexed trailing bytes');\r\n  return values;\r\n}\r\n","modules/new/expansion-config.mjs":"// This value is replaced by the release build from PUBLIC_EXPANSION_SIGNING_JWK.\r\n// It is intentionally empty in source: an unconfigured build must not pretend to\r\n// have a production trust anchor. A public verification key is safe to ship; a\r\n// private signing key must never enter this repository or the build output.\r\nexport const BUILT_PUBLIC_EXPANSION_SIGNING_JWK = null;\r\n\r\nconst isUsablePublicKey = value => value && typeof value === 'object' && !Array.isArray(value)\r\n  && value.kty === 'OKP' && value.crv === 'Ed25519' && typeof value.x === 'string'\r\n  && /^[A-Za-z0-9_-]{43}$/.test(value.x) && !/^A+$/.test(value.x);\r\n\r\nexport function productionExpansionTrust() {\r\n  return isUsablePublicKey(BUILT_PUBLIC_EXPANSION_SIGNING_JWK) ? BUILT_PUBLIC_EXPANSION_SIGNING_JWK : null;\r\n}\r\n\r\nexport function validatePublicExpansionSigningKey(value) {\r\n  if (!isUsablePublicKey(value)) throw new Error('PUBLIC_EXPANSION_SIGNING_JWK must be an Ed25519 public JWK with a non-placeholder x value');\r\n  return Object.freeze({ kty: 'OKP', crv: 'Ed25519', x: value.x, ext: true });\r\n}\r\n","modules/new/expansion-entry.mjs":"import { hasPackGrant, accountPackGrants, packAccountIdentity } from '../../packs/pack-entitlements.mjs';\r\nimport { createIsolatedPackControl } from '../../packs/isolated-pack-control.mjs';\r\nimport { productionExpansionTrust } from './expansion-config.mjs';\r\n\r\nexport const EXPANSION_ID = 'mom-paper-tear';\r\nexport const MANIFEST_URL = '/modules/new/manifest.json';\r\nconst MAX_ASSET_BYTES = 512 * 1024, MAX_PACK_BYTES = 2 * 1024 * 1024;\r\n// Production trust comes only from the validated release-build public config,\r\n// never from a manifest-provided key. Null is deliberate fail-closed state.\r\nexport const PRODUCTION_EXPANSION_KEY = productionExpansionTrust();\r\nconst hex = value => typeof value === 'string' && /^[a-f0-9]{64}$/i.test(value);\r\nconst plain = value => value && typeof value === 'object' && !Array.isArray(value);\r\nconst b64 = value => typeof value === 'string' && /^[A-Za-z0-9+/]+={0,2}$/.test(value) && value.length >= 80;\r\nconst canonical = m => JSON.stringify({ packId:m.packId,version:m.version,minAppVersion:m.minAppVersion,sha256:m.sha256,alg:m.alg,keyId:m.keyId,assets:m.assets.map(({path,bytes,sha256})=>({path,bytes,sha256})) });\r\nconst safeAsset = a => plain(a) && typeof a.path === 'string' && /^assets\\/[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(a.path) && !a.path.includes('..') && Number.isSafeInteger(a.bytes) && a.bytes > 0 && a.bytes <= MAX_ASSET_BYTES && hex(a.sha256);\r\n\r\n/** Shape checking is separate from cryptographic verification. `signed: true` never grants trust. */\r\nexport function isSignedExpansionManifest(value) {\r\n return plain(value) && value.packId === EXPANSION_ID && typeof value.version === 'string' && value.version.length <= 64 && typeof value.minAppVersion === 'string' && hex(value.sha256) && value.alg === 'Ed25519' && value.keyId === 'mom-paper-production-v1' && b64(value.signature) && Array.isArray(value.assets) && value.assets.length > 0 && value.assets.length <= 16 && value.assets.every(safeAsset) && value.assets.reduce((n,a)=>n+a.bytes,0) <= MAX_PACK_BYTES;\r\n}\r\nconst b64bytes = value => { const binary=typeof atob==='function'?atob(value):Buffer.from(value,'base64').toString('binary'); return Uint8Array.from(binary,c=>c.charCodeAt(0)); };\r\nasync function subtle(){if(!globalThis.crypto?.subtle)throw new Error('Web Crypto unavailable');return globalThis.crypto.subtle;}\r\nexport async function verifyExpansionManifest(manifest,key){\r\n if(!isSignedExpansionManifest(manifest)||!plain(key)||key.kty!=='OKP'||key.crv!=='Ed25519'||typeof key.x!=='string')return false;\r\n try{const crypto=await subtle(),trusted=await crypto.importKey('jwk',key,{name:'Ed25519'},false,['verify']);return await crypto.verify({name:'Ed25519'},trusted,b64bytes(manifest.signature),new TextEncoder().encode(canonical(manifest)));}catch{return false;}\r\n}\r\nfunction safeAssetUrl(path,base){const url=new URL(path,base);if(url.origin!==base.origin||!url.pathname.startsWith('/modules/new/assets/'))throw new Error('Expansion asset origin/path is not allowed');return url;}\r\n\r\n/** Tests may inject a test public key only with the explicit test-only purpose. Production remains fail-closed without a valid production signature. */\r\nexport async function resolveExpansionManifest({fetchImpl=globalThis.fetch,url=MANIFEST_URL,fixture=null,testTrust=null}={}){\r\n const trust=testTrust?.purpose==='test-only-signed-expansion'?testTrust.publicKey:(fixture?null:PRODUCTION_EXPANSION_KEY);\r\n if(!fixture&&!trust)throw new Error('Expansion unavailable: trusted production signing key is not configured');\r\n const manifest=fixture?.manifest??await(async()=>{if(typeof fetchImpl!=='function')throw new Error('Expansion manifest unavailable');const r=await fetchImpl(url,{cache:'no-store',credentials:'same-origin'});if(!r.ok)throw new Error(`Expansion manifest unavailable (${r.status})`);return r.json();})();\r\n if(!(await verifyExpansionManifest(manifest,trust)))throw new Error('Expansion manifest signature is invalid');\r\n if(fixture){if(!testTrust)throw new Error('Expansion fixture requires explicit test-only trust');return {manifest,source:fixture.source};}\r\n const base=new URL(url,globalThis.location?.origin||'http://localhost');\r\n return {manifest,source:{async get(asset,previous){\r\n  const offset=previous?.byteLength||0;\r\n  if(offset>=asset.bytes)throw new Error('Invalid expansion resume offset');\r\n  const r=await fetchImpl(safeAssetUrl(asset.path,base),{cache:'no-store',credentials:'same-origin',redirect:'error',headers:offset?{Range:`bytes=${offset}-`}:undefined});\r\n  if(!r.ok||r.redirected||![200,206].includes(r.status))throw new Error(`Asset unavailable (${r.status})`);\r\n  const body=new Uint8Array(await r.arrayBuffer());\r\n  // Servers may ignore Range. A 200 is a complete replacement, never a suffix.\r\n  if(r.status===200){if(body.byteLength!==asset.bytes)throw new Error('Expansion asset length does not match manifest');return body;}\r\n  const range=/^bytes (\\d+)-(\\d+)\\/(\\d+)$/.exec(r.headers?.get('content-range')||'');\r\n  if(!range||Number(range[1])!==offset||Number(range[2])!==asset.bytes-1||Number(range[3])!==asset.bytes||body.byteLength!==asset.bytes-offset)throw new Error('Expansion Content-Range does not match manifest');\r\n  return offset?new Uint8Array([...previous,...body]):body;\r\n }}};\r\n}\r\n\r\n// The pull and the reveal go down the phone, following the user's finger.\r\nexport function tearProgress(start,point,safeHeight=400){return !start||!point?0:Math.max(0,Math.min(1,(point.y-start.y)/Math.max(96,safeHeight*.28)));}\r\nexport function createPaperTearShell({document:doc=globalThis.document,onOpen=()=>{}}={}){\r\n if(!doc)return null;const shell=doc.createElement('section');shell.className='mom-paper-expansion';shell.setAttribute('aria-label','MOM paper expansion');shell.hidden=true;shell.innerHTML='<div class=\"paper-top-safe-area\"><button type=\"button\" class=\"paper-logo\" aria-label=\"Pull MOM paper expansion down to open\"><span aria-hidden=\"true\">MOM</span><small>PULL DOWN TO OPEN</small></button></div><p class=\"paper-status\" role=\"status\"></p><div class=\"dimension-shell\" hidden><div class=\"dimension-slot\" aria-label=\"Expansion scene slot\">COACH POD // PAPER DIMENSION</div><button type=\"button\" class=\"paper-reopen\">Close paper dimension</button></div>';\r\n const logo=shell.querySelector('.paper-logo'),status=shell.querySelector('.paper-status'),dimension=shell.querySelector('.dimension-shell');let start=null;const cancel=()=>{start=null;shell.classList.remove('paper-tearing');logo.style.removeProperty('--tear-progress');status.textContent='Pull canceled. Paper restored.';},open=()=>{dimension.hidden=false;logo.hidden=true;status.textContent='Paper dimension open.';onOpen(dimension);};\r\n logo.addEventListener('pointerdown',e=>{try{logo.setPointerCapture?.(e.pointerId);}catch{}start={x:e.clientX,y:e.clientY};shell.classList.add('paper-tearing');status.textContent='Pull down to tear.';});logo.addEventListener('pointermove',e=>{if(start)logo.style.setProperty('--tear-progress',tearProgress(start,{x:e.clientX,y:e.clientY},shell.clientHeight||400));});logo.addEventListener('pointerup',e=>{if(!start)return;const progress=tearProgress(start,{x:e.clientX,y:e.clientY},shell.clientHeight||400);start=null;shell.classList.remove('paper-tearing');logo.style.removeProperty('--tear-progress');if(progress>=.72)open();else cancel();});logo.addEventListener('pointercancel',cancel);shell.querySelector('.paper-reopen').addEventListener('click',()=>{dimension.hidden=true;logo.hidden=false;status.textContent='Paper restored. Pull down to reopen.';});logo.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();open();}});return shell;\r\n}\r\nexport function mountExpansion({account,workoutOwner,workout,storage,assetStore,fetchImpl,fixture,testTrust,events=globalThis.window,host=globalThis.document?.querySelector('.crew-footer')}={}){\r\n if(!host||!hasPackGrant(account,EXPANSION_ID))return null;\r\n const doc=host.ownerDocument,panel=doc.createElement('section');panel.className='expansion-control';panel.setAttribute('aria-label','Optional MOM paper expansion');panel.innerHTML='<h2>OPTIONAL PAPER DIMENSION</h2><p data-expansion-copy>Verified pack access. Download the optional expansion when ready.</p><button type=\"button\" data-expansion-download>Download optional expansion</button><p data-expansion-status role=\"status\"></p>';host.before(panel);\r\n const button=panel.querySelector('[data-expansion-download]'),status=panel.querySelector('[data-expansion-status]');\r\n const trust=testTrust?.purpose==='test-only-signed-expansion'?testTrust.publicKey:(fixture?null:PRODUCTION_EXPANSION_KEY);\r\n let control=null,shell=null,generation=0,disposed=false,ready;\r\n const identity=packAccountIdentity;\r\n let observedIdentity=identity(account),observedDataEpoch=account?.dataEpoch;\r\n const paint=v=>{if(!v)return;status.textContent=v.error||`${v.state}${v.receivedBytes?` · ${v.receivedBytes} bytes`:''}`;button.disabled=['downloading','verified','pending-equip','active'].includes(v.state);if(v.state==='active'){panel.querySelector('[data-expansion-copy]').textContent='Verified optional expansion active.';if(shell)shell.hidden=false;}};\r\n const runtimeFactory=async()=>{\r\n  const prepared=createPaperTearShell({document:doc});\r\n  return {activate(){shell=prepared;panel.append(prepared);prepared.hidden=false;},dispose(){prepared?.remove();if(shell===prepared)shell=null;}};\r\n };\r\n const setup=async({activateAtBoot=false}={})=>{\r\n  const ticket=++generation;\r\n  control?.dispose();\r\n  let life;\r\n  const current=()=>!disposed&&ticket===generation&&control===life&&hasPackGrant(account,EXPANSION_ID);\r\n  button.disabled=true;status.textContent='Checking signed expansion manifest\\u2026';\r\n  try{\r\n   life=createIsolatedPackControl({account:{getIdentity:()=>identity(account),getGeneration:()=>JSON.stringify([generation,account?.dataEpoch??null]),getEntitlements:()=>!disposed?accountPackGrants(account):[]},workout,workoutOwner,storage,assetStore,runtimeFactory,verifyManifest:manifest=>verifyExpansionManifest(manifest,trust),onChange:value=>{if(ticket===generation&&!disposed)paint(value);}});\r\n   control=life;\r\n   // Restore exact signed metadata and bytes before attempting any network call.\r\n   if(activateAtBoot){\r\n    if(life.pending)await life.restart();\r\n    if(!current())return;\r\n    if(!life.runtime&&life.equipped)await life.restore();\r\n    if(!current())return;\r\n    paint(life.status(EXPANSION_ID));\r\n   }\r\n   const entry=await resolveExpansionManifest({fetchImpl,fixture,testTrust});\r\n   if(!current())return;\r\n   life.manifest(entry.manifest,entry.source);paint(life.status(EXPANSION_ID));\r\n  }catch(error){\r\n   if(disposed||ticket!==generation||!hasPackGrant(account,EXPANSION_ID))return;\r\n   if(life?.runtime){paint(life.status(EXPANSION_ID));return;}\r\n   status.textContent=error.message||'Production expansion unavailable.';button.disabled=false;\r\n  }\r\n };\r\n button.addEventListener('click',async()=>{\r\n  const life=control,ticket=generation;\r\n  const current=()=>!disposed&&ticket===generation&&life===control&&hasPackGrant(account,EXPANSION_ID);\r\n  if(!life?.manifests.has(EXPANSION_ID)){ready=setup();return ready;}\r\n  button.disabled=true;\r\n  try{\r\n   const downloaded=await life.download(EXPANSION_ID);if(!current())return;paint(downloaded);\r\n   const verified=await life.verify(EXPANSION_ID);if(!current())return;paint(verified);\r\n   const pending=await life.equipPending(EXPANSION_ID);if(!current())return;paint(pending);\r\n   status.textContent='Verified and saved as pending. Reload Coach to activate.';button.disabled=false;\r\n  }catch(error){if(current()){status.textContent=error.message||'Download failed. Tap to retry.';button.disabled=false;}}\r\n });\r\n const clear=()=>{generation++;control?.dispose();control=null;account=null;panel.hidden=true;};\r\n const accountReady=event=>{\r\n  const next=event.detail,changed=identity(next)!==observedIdentity||next?.dataEpoch!==observedDataEpoch;\r\n  account=next;observedIdentity=identity(next);observedDataEpoch=next?.dataEpoch;\r\n  if(!hasPackGrant(account,EXPANSION_ID)){clear();return;}\r\n  panel.hidden=false;\r\n  if(changed||!control){ready=setup({activateAtBoot:true});}\r\n };\r\n events?.addEventListener?.('myr5:account-cleared',clear);\r\n events?.addEventListener?.('myr5:account-ready',accountReady);\r\n // Assets are fetched only by the explicit Download action. Restored or pending\r\n // packs still require the workout owner's held idle lease at boot.\r\n ready=setup({activateAtBoot:true});\r\n return {panel,get lifecycle(){return control;},get ready(){return ready;},dispose(){disposed=true;clear();events?.removeEventListener?.('myr5:account-cleared',clear);events?.removeEventListener?.('myr5:account-ready',accountReady);panel.remove();}};\r\n}\r\nif(typeof document!=='undefined'){const style=document.createElement('link');style.rel='stylesheet';style.href=new URL('./expansion.css',import.meta.url);document.head.append(style);}\r\n","modules/new/expansion.css":".expansion-control,.mom-paper-expansion{max-width:42rem;margin:1rem auto;padding:1rem;border:1px solid #7b5265;background:#241827;color:#f5e4ba}.expansion-control h2{font-size:.8rem;letter-spacing:.12em}.expansion-control button,.paper-reopen{min-height:48px;padding:.7rem 1rem;background:#b8e9cf;color:#17111e;border:0;font-weight:700}.paper-top-safe-area{padding:env(safe-area-inset-top,8px) 8px 8px;min-height:74px}.paper-logo{min-height:48px;width:min(142px,100%);border:2px dashed #efc56d;background:#f5e4ba;color:#241827;touch-action:none;transform:translateY(calc(var(--tear-progress,0) * 22px));clip-path:inset(0 0 calc((1 - var(--tear-progress,0)) * 42%) 0);transition:transform .12s ease,clip-path .12s ease}.paper-logo span{display:block;font-weight:900;letter-spacing:.18em}.paper-logo small{font-size:.6rem}.paper-status{min-height:1.5em}.dimension-shell{padding:1rem;border:1px solid #b8e9cf}.dimension-slot{min-height:120px;display:grid;place-items:center;border:1px dotted #efc56d;letter-spacing:.1em}.paper-reopen{margin-top:1rem;background:transparent;color:#b8e9cf;border:1px solid #b8e9cf}@media(prefers-reduced-motion:reduce){.paper-logo{transition:none}.paper-tearing .paper-logo{transform:none}}\r\n","modules/phone-orientation.mjs":"const PHONE_UA=/(?:iPhone|iPod|Android.*Mobile)/i;\r\nconst NOTICE_STYLE=`\r\n[data-phone-portrait-notice]{box-sizing:border-box;position:fixed;inset:0;width:100vw;height:100vh;height:100dvh;max-width:none;max-height:none;margin:0;padding:max(24px,env(safe-area-inset-top)) 24px max(24px,env(safe-area-inset-bottom));border:0;border-radius:0;background:#110d1df5;color:#fff4d8;box-shadow:none;font:700 clamp(22px,6vw,34px)/1.35 system-ui,sans-serif;text-align:center;display:grid;place-items:center;z-index:2147483647;}\r\n[data-phone-portrait-notice]::backdrop{background:#090611eF}\r\n[data-phone-portrait-notice][hidden]{display:none!important}\r\n`;\r\n\r\nexport function isPhone(navigator=globalThis.navigator){\r\n return navigator?.userAgentData?.mobile===true||PHONE_UA.test(navigator?.userAgent||'');\r\n}\r\n\r\n// Use physical orientation signals only. innerWidth/innerHeight and visualViewport can flip\r\n// while the on-screen keyboard is open, which must never trigger the rotate notice.\r\nexport function isPhysicalLandscape(screen=globalThis.screen,win=globalThis.window){\r\n const type=screen?.orientation?.type;\r\n if(typeof type==='string'&&type.startsWith('landscape'))return true;\r\n if(typeof type==='string'&&type.startsWith('portrait'))return false;\r\n const angle=win?.orientation;\r\n return typeof angle==='number'&&Math.abs(angle)%180===90;\r\n}\r\n\r\nexport function mountPhoneOrientation(){\r\n const win=globalThis.window,doc=globalThis.document,nav=globalThis.navigator,scr=globalThis.screen;\r\n if(!win||!doc||!isPhone(nav))return()=>{};\r\n const orientation=scr?.orientation;\r\n const notice=doc.createElement('dialog');notice.dataset.phonePortraitNotice='';notice.setAttribute('role','alert');notice.setAttribute('aria-live','assertive');\r\n notice.textContent='Rotate your phone upright to continue.';\r\n if(typeof notice.showPopover==='function')notice.setAttribute('popover','manual');\r\n let style=doc.querySelector('style[data-phone-orientation-style]'),ownsStyle=!style;\r\n if(!style){style=doc.createElement('style');style.dataset.phoneOrientationStyle='';style.textContent=NOTICE_STYLE;doc.head.append(style);}\r\n doc.body.append(notice);\r\n let disposed=false,locked=false,pending=false,gestureUsed=false,restacking=false,shown=false,modalFallback=false;\r\n const open=()=>{try{return notice.open||notice.matches(':popover-open');}catch{return notice.open||(!notice.hidden&&shown);}};\r\n const anotherModalOpen=()=>[...doc.querySelectorAll('dialog[open]')].some(dialog=>dialog!==notice&&dialog.matches(':modal'));\r\n function show(){\r\n  if(disposed)return;\r\n  notice.hidden=false;\r\n  // A popover opened outside a modal dialog's top layer can be inert in Chromium.\r\n  // Promote the guard to a modal dialog whenever an app dialog is already active.\r\n  if(anotherModalOpen()){\r\n   if(notice.matches?.(':popover-open'))try{notice.hidePopover();}catch{}\r\n   showModalFallback();return;\r\n  }\r\n  if(typeof notice.showPopover==='function'){\r\n   if(!open())try{notice.showPopover();shown=true;}catch{showModalFallback();}\r\n  }else showModalFallback();\r\n }\r\n function showModalFallback(){if(disposed)return;try{if(!notice.open){notice.showModal();modalFallback=true;}shown=true;}catch{notice.hidden=false;shown=true;}}\r\n function hide(){\r\n  if(typeof notice.hidePopover==='function'&&open())try{notice.hidePopover();}catch{}\r\n  if(notice.open)try{notice.close();}catch{}\r\n  notice.hidden=true;shown=false;\r\n }\r\n function updateNotice(){if(isPhysicalLandscape(scr,win))show();else hide();}\r\n function restack(){\r\n  if(disposed||restacking||!isPhysicalLandscape(scr,win)||!open())return;\r\n  restacking=true;\r\n  try{\r\n   if(anotherModalOpen()&&notice.matches(':popover-open')){notice.hidePopover();showModalFallback();}\r\n   else if(notice.matches(':popover-open')&&typeof notice.hidePopover==='function'){notice.hidePopover();notice.showPopover();}\r\n   else if(notice.open&&modalFallback){notice.close();notice.showModal();}\r\n   shown=true;\r\n  }catch{}\r\n  queueMicrotask(()=>{restacking=false;});\r\n }\r\n async function requestLock(){\r\n  if(disposed||pending||locked||typeof orientation?.lock!=='function'){updateNotice();return false;}\r\n  pending=true;\r\n  try{await orientation.lock.call(orientation,'portrait-primary');if(disposed){try{orientation.unlock?.call(orientation);}catch{}return false;}locked=true;updateNotice();return true;}\r\n  catch{if(!disposed)updateNotice();return false;}\r\n  finally{pending=false;}\r\n }\r\n function firstGesture(){if(gestureUsed)return;gestureUsed=true;void requestLock();}\r\n function blockLandscapeKeys(event){if(!disposed&&isPhysicalLandscape(scr,win)&&open()){event.preventDefault();event.stopImmediatePropagation();}}\r\n function onFullscreen(){if(!doc.fullscreenElement)locked=false;void requestLock();updateNotice();}\r\n function onOrientation(){updateNotice();}\r\n function onTopLayerChange(event){\r\n  if(event.target===notice)return;\r\n  const target=event.target;if(target?.matches?.('dialog[open]')){queueMicrotask(restack);return;}\r\n  try{if(target?.matches?.('[popover]:popover-open'))queueMicrotask(restack);}catch{}\r\n }\r\n const layers=new MutationObserver(records=>{\r\n  if(records.some(record=>record.target!==notice&&record.target?.tagName==='DIALOG'&&record.attributeName==='open'&&record.target.open))queueMicrotask(restack);\r\n });\r\n layers.observe(doc.body,{subtree:true,attributes:true,attributeFilter:['open']});\r\n win.addEventListener('pointerdown',firstGesture,{capture:true,once:true});\r\n win.addEventListener('keydown',firstGesture,{capture:true,once:true});\r\n win.addEventListener('keydown',blockLandscapeKeys,true);\r\n win.addEventListener('orientationchange',onOrientation);\r\n win.addEventListener('resize',onOrientation); // the physical orientation check ignores keyboard-only viewport changes\r\n doc.addEventListener('fullscreenchange',onFullscreen);\r\n doc.addEventListener('toggle',onTopLayerChange,true);\r\n orientation?.addEventListener?.('change',onOrientation);\r\n void requestLock();\r\n updateNotice();\r\n return()=>{\r\n  if(disposed)return;disposed=true;layers.disconnect();\r\n  win.removeEventListener('pointerdown',firstGesture,true);win.removeEventListener('keydown',firstGesture,true);\r\n  win.removeEventListener('keydown',blockLandscapeKeys,true);\r\n  win.removeEventListener('orientationchange',onOrientation);win.removeEventListener('resize',onOrientation);\r\n  doc.removeEventListener('fullscreenchange',onFullscreen);doc.removeEventListener('toggle',onTopLayerChange,true);\r\n  orientation?.removeEventListener?.('change',onOrientation);\r\n  if(locked)try{orientation?.unlock?.call(orientation);}catch{}\r\n  if(typeof notice.hidePopover==='function'&&open())try{notice.hidePopover();}catch{}\r\n  if(notice.open)try{notice.close();}catch{}\r\n  notice.remove();\r\n  if(ownsStyle)style?.remove();\r\n };\r\n}\r\n","modules/pod-chrome.mjs":"// A single set of controls follows the active top-layer menu; no duplicate buttons.\r\nexport function mountPodChrome(grimoireButton,{signal}={}){\r\n const doc=grimoireButton.ownerDocument;\r\n let nav=doc.getElementById('podPersistentChrome');\r\n if(nav){\r\n  const current=nav.querySelector('.metal-grimoire-key');\r\n  if(current!==grimoireButton){\r\n   grimoireButton.classList.add('metal-grimoire-key');\r\n   grimoireButton.hidden=!nav.classList.contains('frame-mounted');\r\n   current?.replaceWith(grimoireButton);\r\n   // A lazy pod key stays available if the heavyweight portal is later disposed.\r\n   const fallback=nav._lazyGrimoireButton;\r\n   if(fallback&&signal)signal.addEventListener('abort',()=>{\r\n    if(nav.isConnected&&grimoireButton.parentNode===nav){fallback.hidden=grimoireButton.hidden;grimoireButton.replaceWith(fallback);}\r\n   },{once:true});\r\n  }\r\n  return nav;\r\n }\r\n // The mark already says MOM INC. Keep the real home link and its image, but clear\r\n // the duplicate wordmark and location caption from the visible ship header.\r\n const brand=doc.querySelector('.ship-header .mom-brand');\r\n brand?.querySelectorAll(':scope > span').forEach(text=>text.remove());\r\n if(brand){brand.setAttribute('aria-label','Return to the pod');brand.querySelector('img')?.setAttribute('alt','MOM Inc');}\r\n doc.querySelectorAll('.ship-header .ship-location').forEach(location=>location.remove());\r\n const settingsKey=doc.getElementById('openSettings');\r\n if(settingsKey){\r\n  settingsKey.classList.add('ship-settings-toggle');\r\n  settingsKey.setAttribute('aria-label','Open settings terminal');\r\n  settingsKey.setAttribute('aria-haspopup','dialog');\r\n  settingsKey.setAttribute('aria-controls','settings');\r\n  const handle=doc.createElement('span');handle.className='ship-settings-handle';handle.setAttribute('aria-hidden','true');\r\n  const screen=doc.createElement('span');screen.className='ship-settings-screen';screen.setAttribute('aria-hidden','true');\r\n  const status=doc.createElement('small');status.textContent='SHIP SYSTEMS';\r\n  const title=doc.createElement('strong');title.textContent='SETTINGS';screen.append(status,title);\r\n  settingsKey.replaceChildren(handle,screen);\r\n }\r\n doc.getElementById('coachDock')?.classList.add('ship-control-deck');\r\n nav=doc.createElement('nav');nav.id='podPersistentChrome';nav.className='pod-persistent-chrome';nav.setAttribute('aria-label','Pod terminals');\r\n grimoireButton.classList.add('metal-grimoire-key');\r\n if(grimoireButton.id==='podGrimoireLazyOpen')nav._lazyGrimoireButton=grimoireButton;\r\n const playing=doc.createElement('button');playing.type='button';playing.id='spotifyNowPlayingOpen';playing.className='pod-now-playing';playing.setAttribute('aria-label','Open actual.dj');\r\n const caption=doc.createElement('small');caption.textContent='NOW PLAYING';\r\n const label=doc.createElement('span');label.id='spotifyNowPlayingLabel';label.textContent='actual.dj';playing.append(caption,label);nav.append(grimoireButton,playing);doc.body.append(nav);\r\n const move=()=>{\r\n  const settings=doc.getElementById('settings');\r\n  if(settingsKey)settingsKey.setAttribute('aria-expanded',String(!!settings?.open));\r\n  const dialogs=[...doc.querySelectorAll('dialog[open]')].filter(d=>!d.matches('[data-phone-portrait-notice],#coachGate'));\r\n  const active=dialogs.at(-1);\r\n  const portal=doc.getElementById('portalHome');\r\n  const housing=doc.getElementById('portalChrome');\r\n  const cutaway=active?.classList.contains('portal-shaped')&&!active.classList.contains('portal-fullscreen');\r\n  const frame=cutaway?housing?.querySelector('.portal-frame'):!active&&portal&&!portal.hidden?portal.querySelector('#portalBoardHost > .portal-frame'):null;\r\n  // The board's frame sits below its full-screen drawing canvas. Keep interactive\r\n  // terminals in the portal's control layer, outside that lower stacking context.\r\n  // The shell's top band (R3-01) takes the board terminals; on the pod route NOW PLAYING lives in the cockpit service bay.\r\n  const band=doc.querySelector('#myr5Shell .shell-band'),bay=doc.querySelector('.cockpit-service-host');\r\n  const owner=band&&frame?band:bay&&!active&&!frame?bay:frame&&frame.closest('#portalHome')?portal:frame||(!active?doc.querySelector('.ship-header'):null)||doc.body;\r\n  // Native modal dialogs make external controls inert, even in a raised popover.\r\n  // Hide those keys until returning to the Grimoire rather than draw dead buttons.\r\n  nav.hidden=!!active&&(!cutaway||active.matches(':modal'));\r\n  nav.classList.toggle('frame-mounted',!!frame);\r\n  const grimoireKey=nav.querySelector('.metal-grimoire-key');\r\n  if(grimoireKey)grimoireKey.hidden=!frame;\r\n  if(frame){frame.removeAttribute('aria-hidden');housing?.removeAttribute('aria-hidden');}\r\n  if(nav.parentNode!==owner)owner.append(nav);\r\n  for(const dialog of dialogs)if(dialog.classList.contains('has-pod-chrome'))dialog.classList.remove('has-pod-chrome');\r\n };\r\n const observer=new MutationObserver(records=>{if(records.some(r=>r.target!==nav&&!nav.contains(r.target)))move();});observer.observe(doc.body,{subtree:true,childList:true,attributes:true,attributeFilter:['open','hidden','class']});\r\n const close=()=>queueMicrotask(move);doc.addEventListener('close',close,true);\r\n const dispose=()=>{observer.disconnect();doc.removeEventListener('close',close,true);nav.remove();};\r\n signal?.addEventListener('abort',dispose,{once:true});move();return nav;\r\n}\r\n","modules/portal/cracked-glass-core.mjs":"// Cracked-glass fracture core, vendored unchanged from https://github.com/kossik/cracked-glass v0.7.0 (commit 6cb13c6932eba3e532c52115efdddcef2b79cbf7), MIT licence — see LICENSES/cracked-glass.txt.\r\n// src/core/math.ts\r\nvar TAU = Math.PI * 2;\r\nfunction clamp(v, lo, hi) {\r\n  return v < lo ? lo : v > hi ? hi : v;\r\n}\r\nfunction clamp01(v) {\r\n  return v < 0 ? 0 : v > 1 ? 1 : v;\r\n}\r\nfunction lerp(a, b, t) {\r\n  return a + (b - a) * t;\r\n}\r\nfunction degToRad(d) {\r\n  return d * Math.PI / 180;\r\n}\r\nfunction smoothstep(t) {\r\n  const x = clamp01(t);\r\n  return x * x * (3 - 2 * x);\r\n}\r\nfunction easeOutCubic(t) {\r\n  const x = clamp01(t);\r\n  return 1 - (1 - x) * (1 - x) * (1 - x);\r\n}\r\nfunction easeOutQuart(t) {\r\n  const x = clamp01(t);\r\n  const y = 1 - x;\r\n  return 1 - y * y * y * y;\r\n}\r\nfunction easeOutExpo(t) {\r\n  const x = clamp01(t);\r\n  return x >= 1 ? 1 : 1 - Math.pow(2, -10 * x);\r\n}\r\nfunction fmt(n, precision = 2) {\r\n  if (!Number.isFinite(n)) return \"0\";\r\n  let s = n.toFixed(precision);\r\n  if (s.indexOf(\".\") >= 0) {\r\n    s = s.replace(/0+$/, \"\");\r\n    if (s.endsWith(\".\")) s = s.slice(0, -1);\r\n  }\r\n  if (s === \"-0\") s = \"0\";\r\n  return s;\r\n}\r\n\r\n// src/core/geometry.ts\r\nfunction signedArea(poly) {\r\n  let a = 0;\r\n  for (let i = 0; i < poly.length; i++) {\r\n    const p = poly[i];\r\n    const q = poly[(i + 1) % poly.length];\r\n    a += p[0] * q[1] - q[0] * p[1];\r\n  }\r\n  return a / 2;\r\n}\r\nfunction ensurePositiveWinding(poly) {\r\n  return signedArea(poly) >= 0 ? poly : poly.slice().reverse();\r\n}\r\nfunction pointInPolygon(p, poly) {\r\n  let inside = false;\r\n  const n = poly.length;\r\n  for (let i = 0, j = n - 1; i < n; j = i++) {\r\n    const a = poly[i];\r\n    const b = poly[j];\r\n    if (a[1] > p[1] !== b[1] > p[1]) {\r\n      const x = (b[0] - a[0]) * (p[1] - a[1]) / (b[1] - a[1]) + a[0];\r\n      if (p[0] < x) inside = !inside;\r\n    }\r\n  }\r\n  return inside;\r\n}\r\nfunction polygonCentroid(poly) {\r\n  let a = 0;\r\n  let cx = 0;\r\n  let cy = 0;\r\n  for (let i = 0; i < poly.length; i++) {\r\n    const p = poly[i];\r\n    const q = poly[(i + 1) % poly.length];\r\n    const cross = p[0] * q[1] - q[0] * p[1];\r\n    a += cross;\r\n    cx += (p[0] + q[0]) * cross;\r\n    cy += (p[1] + q[1]) * cross;\r\n  }\r\n  a /= 2;\r\n  if (Math.abs(a) < 1e-9) {\r\n    let sx = 0;\r\n    let sy = 0;\r\n    for (const p of poly) {\r\n      sx += p[0];\r\n      sy += p[1];\r\n    }\r\n    return [sx / poly.length, sy / poly.length];\r\n  }\r\n  return [cx / (6 * a), cy / (6 * a)];\r\n}\r\nfunction dedupePolygon(poly, eps = 1e-7) {\r\n  const out = [];\r\n  for (const p of poly) {\r\n    const last = out[out.length - 1];\r\n    if (!last || Math.abs(last[0] - p[0]) > eps || Math.abs(last[1] - p[1]) > eps) {\r\n      out.push(p);\r\n    }\r\n  }\r\n  if (out.length > 1) {\r\n    const first = out[0];\r\n    const last = out[out.length - 1];\r\n    if (Math.abs(first[0] - last[0]) <= eps && Math.abs(first[1] - last[1]) <= eps) {\r\n      out.pop();\r\n    }\r\n  }\r\n  return out;\r\n}\r\nfunction clipPolygonToRect(poly, w, h) {\r\n  const edges = [\r\n    { inside: (p) => p[0] >= 0, cut: (a, b) => cutX(a, b, 0) },\r\n    { inside: (p) => p[0] <= w, cut: (a, b) => cutX(a, b, w) },\r\n    { inside: (p) => p[1] >= 0, cut: (a, b) => cutY(a, b, 0) },\r\n    { inside: (p) => p[1] <= h, cut: (a, b) => cutY(a, b, h) }\r\n  ];\r\n  let cur = poly;\r\n  for (const e of edges) {\r\n    if (cur.length === 0) return [];\r\n    const next = [];\r\n    for (let i = 0; i < cur.length; i++) {\r\n      const a = cur[i];\r\n      const b = cur[(i + 1) % cur.length];\r\n      const ain = e.inside(a);\r\n      const bin = e.inside(b);\r\n      if (ain) {\r\n        next.push(a);\r\n        if (!bin) next.push(e.cut(a, b));\r\n      } else if (bin) {\r\n        next.push(e.cut(a, b));\r\n      }\r\n    }\r\n    cur = next;\r\n  }\r\n  return dedupePolygon(cur);\r\n}\r\nfunction cutX(a, b, x) {\r\n  const t = (x - a[0]) / (b[0] - a[0]);\r\n  return [x, a[1] + (b[1] - a[1]) * t];\r\n}\r\nfunction cutY(a, b, y) {\r\n  const t = (y - a[1]) / (b[1] - a[1]);\r\n  return [a[0] + (b[0] - a[0]) * t, y];\r\n}\r\nfunction offsetPolygon(poly, dist, miterLimit = 3) {\r\n  const n = poly.length;\r\n  if (n < 3 || dist === 0) return poly.slice();\r\n  const out = [];\r\n  for (let i = 0; i < n; i++) {\r\n    const prev = poly[(i - 1 + n) % n];\r\n    const v = poly[i];\r\n    const next = poly[(i + 1) % n];\r\n    const n1 = edgeOutwardNormal(prev, v);\r\n    const n2 = edgeOutwardNormal(v, next);\r\n    let mx = n1[0] + n2[0];\r\n    let my = n1[1] + n2[1];\r\n    const ml = Math.hypot(mx, my);\r\n    if (ml < 1e-9) {\r\n      mx = n1[0];\r\n      my = n1[1];\r\n    } else {\r\n      mx /= ml;\r\n      my /= ml;\r\n    }\r\n    const dot = mx * n1[0] + my * n1[1];\r\n    let scale = dot > 1e-6 ? 1 / dot : miterLimit;\r\n    if (scale > miterLimit) scale = miterLimit;\r\n    out.push([v[0] + mx * dist * scale, v[1] + my * dist * scale]);\r\n  }\r\n  return out;\r\n}\r\nfunction edgeOutwardNormal(a, b) {\r\n  const dx = b[0] - a[0];\r\n  const dy = b[1] - a[1];\r\n  const l = Math.hypot(dx, dy);\r\n  if (l < 1e-9) return [0, 0];\r\n  return [dy / l, -dx / l];\r\n}\r\nfunction segmentIntersection(a1, a2, b1, b2) {\r\n  const dax = a2[0] - a1[0];\r\n  const day = a2[1] - a1[1];\r\n  const dbx = b2[0] - b1[0];\r\n  const dby = b2[1] - b1[1];\r\n  const den = dax * dby - day * dbx;\r\n  if (Math.abs(den) < 1e-12) return null;\r\n  const ex = b1[0] - a1[0];\r\n  const ey = b1[1] - a1[1];\r\n  const ta = (ex * dby - ey * dbx) / den;\r\n  const tb = (ex * day - ey * dax) / den;\r\n  if (ta < -1e-9 || ta > 1 + 1e-9 || tb < -1e-9 || tb > 1 + 1e-9) return null;\r\n  return { p: [a1[0] + dax * ta, a1[1] + day * ta], ta, tb };\r\n}\r\nfunction cumulativeLengths(pts) {\r\n  const cum = [0];\r\n  for (let i = 1; i < pts.length; i++) {\r\n    cum.push(cum[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]));\r\n  }\r\n  return cum;\r\n}\r\nfunction pointAtLength(pts, cum, len) {\r\n  const total = cum[cum.length - 1];\r\n  if (len <= 0) return pts[0];\r\n  if (len >= total) return pts[pts.length - 1];\r\n  let lo = 0;\r\n  let hi = cum.length - 1;\r\n  while (lo + 1 < hi) {\r\n    const mid = lo + hi >> 1;\r\n    if (cum[mid] <= len) lo = mid;\r\n    else hi = mid;\r\n  }\r\n  const segLen = cum[hi] - cum[lo];\r\n  const t = segLen > 1e-12 ? (len - cum[lo]) / segLen : 0;\r\n  return [\r\n    pts[lo][0] + (pts[hi][0] - pts[lo][0]) * t,\r\n    pts[lo][1] + (pts[hi][1] - pts[lo][1]) * t\r\n  ];\r\n}\r\nfunction tangentAtLength(pts, cum, len) {\r\n  let lo = 0;\r\n  let hi = cum.length - 1;\r\n  if (len <= 0) hi = 1;\r\n  else if (len >= cum[cum.length - 1]) lo = cum.length - 2;\r\n  else {\r\n    while (lo + 1 < hi) {\r\n      const mid = lo + hi >> 1;\r\n      if (cum[mid] <= len) lo = mid;\r\n      else hi = mid;\r\n    }\r\n  }\r\n  const dx = pts[hi][0] - pts[lo][0];\r\n  const dy = pts[hi][1] - pts[lo][1];\r\n  const l = Math.hypot(dx, dy) || 1;\r\n  return [dx / l, dy / l];\r\n}\r\nfunction toCssPolygon(poly, precision = 2) {\r\n  const parts = [];\r\n  for (let i = 0; i + 1 < poly.length; i += 2) {\r\n    parts.push(`${fmt(poly[i], precision)}px ${fmt(poly[i + 1], precision)}px`);\r\n  }\r\n  return `polygon(${parts.join(\", \")})`;\r\n}\r\nfunction toSvgPathD(poly, closed, precision = 2) {\r\n  if (poly.length < 2) return \"\";\r\n  let d = `M${fmt(poly[0], precision)} ${fmt(poly[1], precision)}`;\r\n  for (let i = 2; i + 1 < poly.length; i += 2) {\r\n    d += `L${fmt(poly[i], precision)} ${fmt(poly[i + 1], precision)}`;\r\n  }\r\n  return closed ? d + \"Z\" : d;\r\n}\r\nfunction insetPolygonTowardCentroid(flat, centroid, inset) {\r\n  const out = [];\r\n  for (let i = 0; i + 1 < flat.length; i += 2) {\r\n    const dx = centroid[0] - flat[i];\r\n    const dy = centroid[1] - flat[i + 1];\r\n    const d = Math.hypot(dx, dy) || 1;\r\n    const m = Math.min(inset, d * 0.45) / d;\r\n    out.push(flat[i] + dx * m, flat[i + 1] + dy * m);\r\n  }\r\n  return out;\r\n}\r\nfunction reverseFlat(flat) {\r\n  const out = [];\r\n  for (let i = flat.length - 2; i >= 0; i -= 2) {\r\n    out.push(flat[i], flat[i + 1]);\r\n  }\r\n  return out;\r\n}\r\nfunction ringPathD(outerFlat, innerFlat) {\r\n  return toSvgPathD(outerFlat, true) + toSvgPathD(reverseFlat(innerFlat), true);\r\n}\r\nfunction toSvgPoints(poly, precision = 2) {\r\n  const parts = [];\r\n  for (let i = 0; i + 1 < poly.length; i += 2) {\r\n    parts.push(`${fmt(poly[i], precision)},${fmt(poly[i + 1], precision)}`);\r\n  }\r\n  return parts.join(\" \");\r\n}\r\nfunction flattenPts(pts) {\r\n  const out = [];\r\n  for (const p of pts) {\r\n    out.push(p[0], p[1]);\r\n  }\r\n  return out;\r\n}\r\nfunction unflattenPts(flat) {\r\n  const out = [];\r\n  for (let i = 0; i + 1 < flat.length; i += 2) {\r\n    out.push([flat[i], flat[i + 1]]);\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/core/prng.ts\r\nfunction hashString(s) {\r\n  let h = 2166136261;\r\n  for (let i = 0; i < s.length; i++) {\r\n    h ^= s.charCodeAt(i);\r\n    h = Math.imul(h, 16777619);\r\n  }\r\n  return h >>> 0;\r\n}\r\nfunction hashCombine(...vals) {\r\n  let h = 2654435769;\r\n  for (let i = 0; i < vals.length; i++) {\r\n    let x = vals[i] >>> 0;\r\n    x = Math.imul(x ^ x >>> 16, 2246822507);\r\n    x = Math.imul(x ^ x >>> 13, 3266489909);\r\n    x ^= x >>> 16;\r\n    h = (Math.imul(h, 16777619) ^ x) >>> 0;\r\n  }\r\n  return h >>> 0;\r\n}\r\nfunction hashTo01(h) {\r\n  return (h >>> 0) / 4294967296;\r\n}\r\nfunction mulberry32(seed) {\r\n  let a = seed >>> 0;\r\n  return () => {\r\n    a = a + 1831565813 | 0;\r\n    let t = a;\r\n    t = Math.imul(t ^ t >>> 15, t | 1);\r\n    t ^= t + Math.imul(t ^ t >>> 7, t | 61);\r\n    return ((t ^ t >>> 14) >>> 0) / 4294967296;\r\n  };\r\n}\r\nfunction rngFor(seed, subsystem, ...idx) {\r\n  return mulberry32(hashCombine(seed >>> 0, hashString(subsystem), ...idx));\r\n}\r\nfunction rand01(seed, subsystem, ...idx) {\r\n  return hashTo01(hashCombine(seed >>> 0, hashString(subsystem), ...idx));\r\n}\r\n\r\n// src/fracture/build.ts\r\nfunction resolveFractureOptions(opts) {\r\n  if (!Number.isFinite(opts.width) || opts.width <= 0) throw new Error(\"cracked-glass: width must be > 0\");\r\n  if (!Number.isFinite(opts.height) || opts.height <= 0) throw new Error(\"cracked-glass: height must be > 0\");\r\n  if (!Number.isFinite(opts.seed)) throw new Error(\"cracked-glass: seed must be a finite number\");\r\n  const seed = opts.seed >>> 0;\r\n  const micro = opts.micro;\r\n  const defaultMicroCount = opts.mode === \"radial\" ? 110 : opts.mode === \"collapse\" ? 90 : opts.mode === \"hero\" ? 0 : 36;\r\n  const angleA = opts.collapse?.angleA ?? 14;\r\n  let angleB = opts.collapse?.angleB ?? 76;\r\n  const sep = Math.abs((angleB - angleA + 90) % 180 - 90);\r\n  if (sep < 35) angleB = angleA + (angleB >= angleA ? 35 : -35);\r\n  return {\r\n    mode: opts.mode,\r\n    width: opts.width,\r\n    height: opts.height,\r\n    seed,\r\n    instanceId: opts.instanceId ?? `cg${seed.toString(36)}`,\r\n    edgeDetail: clamp(Math.round(opts.edgeDetail ?? 2), 0, 3),\r\n    jaggedness: clamp(opts.jaggedness ?? 0.5, 0, 1),\r\n    seamOutsetPx: opts.seamOutsetPx ?? 0.4,\r\n    deviation: clamp(opts.deviation ?? 0.35, 0, 1),\r\n    microCount: micro === false ? 0 : Math.max(0, Math.round(micro?.count ?? defaultMicroCount)),\r\n    microSizeRange: micro === false ? [0, 0] : micro?.sizeRange ?? [1.5, 5],\r\n    stubs: opts.stubs === false ? false : {\r\n      maxPerCrack: Math.max(0, Math.round(opts.stubs?.maxPerCrack ?? 4)),\r\n      atJunctions: opts.stubs?.atJunctions ?? true\r\n    },\r\n    bands: {\r\n      count: opts.bands?.count ?? [3, 5],\r\n      waviness: clamp(opts.bands?.waviness ?? 0.5, 0, 1),\r\n      diagonalChance: clamp(opts.bands?.diagonalChance ?? 0.6, 0, 1),\r\n      tilt: clamp(opts.bands?.tilt ?? 0.6, 0, 1),\r\n      splitters: clamp(Math.round(opts.bands?.splitters ?? 3), 1, 3),\r\n      diagonal: clamp(opts.bands?.diagonal ?? 0.7, 0, 1)\r\n    },\r\n    impact: opts.impact ? [clamp(opts.impact.x, 0, opts.width), clamp(opts.impact.y, 0, opts.height)] : [opts.width / 2, opts.height / 2],\r\n    impactHole: clamp(opts.impactHole ?? 1, 0.3, 3),\r\n    rays: {\r\n      // lower default ray count keeps the cell count (and thus per-shard DOM cost) sane;\r\n      // the lab lets you crank it higher when you want a denser web.\r\n      count: Math.max(3, Math.round(opts.rays?.count ?? 6)),\r\n      angleJitter: clamp(opts.rays?.angleJitter ?? 0.6, 0, 1),\r\n      waviness: clamp(opts.rays?.waviness ?? 0.5, 0, 1),\r\n      doubling: opts.rays?.doubling ?? true,\r\n      doublingStartRing: Math.max(1, Math.round(opts.rays?.doublingStartRing ?? 3))\r\n    },\r\n    rings: {\r\n      count: Math.max(1, Math.round(opts.rings?.count ?? 4)),\r\n      spacing: opts.rings?.spacing ?? \"geometric\",\r\n      jitter: clamp(opts.rings?.jitter ?? 0.5, 0, 1),\r\n      partial: clamp(opts.rings?.partial ?? 0.88, 0, 1),\r\n      asymmetry: clamp(opts.rings?.asymmetry ?? 0.45, 0, 1)\r\n    },\r\n    collapse: {\r\n      angleA,\r\n      angleB,\r\n      countA: opts.collapse?.countA ?? [3, 5],\r\n      countB: opts.collapse?.countB ?? [4, 7],\r\n      waviness: clamp(opts.collapse?.waviness ?? 0.5, 0, 1),\r\n      merge: clamp(opts.collapse?.merge ?? 0.3, 0, 1)\r\n    },\r\n    hero: {\r\n      count: clamp(Math.round(opts.hero?.count ?? 1), 1, 3),\r\n      sizeFrac: clamp(opts.hero?.sizeFrac ?? 0.34, 0.05, 0.48),\r\n      spread: clamp(opts.hero?.spread ?? 0.5, 0, 1),\r\n      overlap: clamp(opts.hero?.overlap ?? 0, 0, 1)\r\n    },\r\n    web: {\r\n      rays: Math.max(3, Math.round(opts.web?.rays ?? 9)),\r\n      rings: Math.max(1, Math.round(opts.web?.rings ?? 4)),\r\n      irregularity: clamp(opts.web?.irregularity ?? 0.6, 0, 1),\r\n      dir: opts.web?.dir ?? 90,\r\n      distance: clamp(opts.web?.distance ?? 1.2, 0.2, 6)\r\n    },\r\n    corners: opts.corners === false ? false : { relief: clamp(opts.corners?.relief ?? 0.55, 0, 1) }\r\n  };\r\n}\r\nfunction makeCrack(seed, id, kind, pts, birth, growDuration, withTicks = true) {\r\n  const cum = cumulativeLengths(pts);\r\n  const totalLen = cum[cum.length - 1];\r\n  const ticks = [];\r\n  const rng = rngFor(seed, \"ticks\", hashString(id));\r\n  const spacing = 11;\r\n  for (let s = withTicks ? spacing * (0.5 + rng()) : totalLen; s < totalLen - 4; s += spacing * (0.7 + rng() * 0.8)) {\r\n    const p = pointAt(pts, cum, s);\r\n    const q = pointAt(pts, cum, Math.min(totalLen, s + 1.5));\r\n    let dx = q[0] - p[0];\r\n    let dy = q[1] - p[1];\r\n    const l = Math.hypot(dx, dy) || 1;\r\n    dx /= l;\r\n    dy /= l;\r\n    const side = rng() < 0.5 ? 1 : -1;\r\n    const len = 1.6 + rng() * 3.2;\r\n    const a = (55 + rng() * 25) * (Math.PI / 180) * side;\r\n    const cos = Math.cos(a);\r\n    const sin = Math.sin(a);\r\n    const tx = dx * cos - dy * sin;\r\n    const ty = dx * sin + dy * cos;\r\n    ticks.push(s, p[0], p[1], p[0] + tx * len, p[1] + ty * len);\r\n  }\r\n  return {\r\n    id,\r\n    kind,\r\n    points: flattenPts(pts),\r\n    cumLen: cum,\r\n    totalLen,\r\n    birth,\r\n    growDuration,\r\n    ticks\r\n  };\r\n}\r\nfunction pointAt(pts, cum, len) {\r\n  const total = cum[cum.length - 1];\r\n  if (len <= 0) return pts[0];\r\n  if (len >= total) return pts[pts.length - 1];\r\n  let lo = 0;\r\n  let hi = cum.length - 1;\r\n  while (lo + 1 < hi) {\r\n    const mid = lo + hi >> 1;\r\n    if (cum[mid] <= len) lo = mid;\r\n    else hi = mid;\r\n  }\r\n  const segLen = cum[hi] - cum[lo];\r\n  const t = segLen > 1e-12 ? (len - cum[lo]) / segLen : 0;\r\n  return [pts[lo][0] + (pts[hi][0] - pts[lo][0]) * t, pts[lo][1] + (pts[hi][1] - pts[lo][1]) * t];\r\n}\r\nfunction makeShard(seed, index, poly, ringIndex, z, seamOutsetPx) {\r\n  const cleaned = dedupePolygon(poly);\r\n  if (cleaned.length < 3) return null;\r\n  const wound = ensurePositiveWinding(cleaned);\r\n  const area = signedArea(wound);\r\n  if (area < 2) return null;\r\n  const outset = seamOutsetPx > 0 ? offsetPolygon(wound, seamOutsetPx) : wound;\r\n  return {\r\n    id: `s${index}`,\r\n    polygon: flattenPts(wound),\r\n    outsetPolygon: flattenPts(outset),\r\n    centroid: polygonCentroid(wound),\r\n    area,\r\n    ringIndex,\r\n    z,\r\n    hash: hashCombine(seed, hashString(\"shard\"), index)\r\n  };\r\n}\r\nfunction shuffledRanks(seed, key, n) {\r\n  const rng = rngFor(seed, key);\r\n  const idx = Array.from({ length: n }, (_, i) => i);\r\n  for (let i = n - 1; i > 0; i--) {\r\n    const j = Math.floor(rng() * (i + 1));\r\n    const tmp = idx[i];\r\n    idx[i] = idx[j];\r\n    idx[j] = tmp;\r\n  }\r\n  const ranks = new Array(n);\r\n  for (let rank = 0; rank < n; rank++) ranks[idx[rank]] = rank;\r\n  return ranks;\r\n}\r\nfunction stitchLoop(segments) {\r\n  const out = [];\r\n  for (const seg of segments) {\r\n    const start = out.length === 0 ? 0 : 1;\r\n    for (let i = start; i < seg.length; i++) out.push(seg[i]);\r\n  }\r\n  return dedupePolygon(out);\r\n}\r\nfunction reversePts(pts) {\r\n  return pts.slice().reverse();\r\n}\r\n\r\n// src/core/noise.ts\r\nfunction valueNoise1D(seed, x) {\r\n  const xi = Math.floor(x);\r\n  const xf = x - xi;\r\n  const r0 = hashTo01(hashCombine(seed, xi | 0));\r\n  const r1 = hashTo01(hashCombine(seed, xi + 1 | 0));\r\n  return lerp(r0, r1, smoothstep(xf)) * 2 - 1;\r\n}\r\nfunction fbm1D(seed, x, octaves = 3, lacunarity = 2, gain = 0.5) {\r\n  let amp = 1;\r\n  let freq = 1;\r\n  let sum = 0;\r\n  let norm = 0;\r\n  for (let o = 0; o < octaves; o++) {\r\n    sum += amp * valueNoise1D(hashCombine(seed, o), x * freq);\r\n    norm += amp;\r\n    amp *= gain;\r\n    freq *= lacunarity;\r\n  }\r\n  return norm > 0 ? sum / norm : 0;\r\n}\r\nfunction subdivideMidpoint(pts, depth, roughness, seed, maxOffset = Infinity) {\r\n  let cur = pts;\r\n  for (let level = 0; level < depth; level++) {\r\n    const next = [cur[0]];\r\n    const levelAmp = roughness * 0.5 * Math.pow(0.55, level);\r\n    for (let i = 0; i < cur.length - 1; i++) {\r\n      const a = cur[i];\r\n      const b = cur[i + 1];\r\n      const dx = b[0] - a[0];\r\n      const dy = b[1] - a[1];\r\n      const segLen = Math.hypot(dx, dy);\r\n      if (segLen > 1e-9) {\r\n        const nx = -dy / segLen;\r\n        const ny = dx / segLen;\r\n        const r = hashTo01(hashCombine(seed, level, i)) * 2 - 1;\r\n        let off = r * levelAmp * segLen;\r\n        if (off > maxOffset) off = maxOffset;\r\n        if (off < -maxOffset) off = -maxOffset;\r\n        next.push([(a[0] + b[0]) / 2 + nx * off, (a[1] + b[1]) / 2 + ny * off]);\r\n      }\r\n      next.push(b);\r\n    }\r\n    cur = next;\r\n  }\r\n  return cur;\r\n}\r\n\r\n// src/fracture/lines.ts\r\nfunction makeExcursions(seed, group, lineKey, deviation, ampMax) {\r\n  if (deviation <= 0) return [];\r\n  const out = [];\r\n  for (let idx = 0; idx < 2; idx++) {\r\n    const p = idx === 0 ? deviation * 0.75 : deviation * 0.35;\r\n    if (rand01(seed, `${group}:dev`, lineKey, idx) >= p) continue;\r\n    const r1 = rand01(seed, `${group}:devA`, lineKey, idx);\r\n    const r2 = rand01(seed, `${group}:devB`, lineKey, idx);\r\n    const r3 = rand01(seed, `${group}:devC`, lineKey, idx);\r\n    const r4 = rand01(seed, `${group}:devD`, lineKey, idx);\r\n    const r5 = rand01(seed, `${group}:devE`, lineKey, idx);\r\n    out.push({\r\n      c: 0.15 + 0.7 * r1,\r\n      half: 0.08 + 0.1 * r2,\r\n      amp: (0.25 + 0.45 * r3) * ampMax * (r4 < 0.5 ? -1 : 1),\r\n      kink: r5 < 0.5\r\n    });\r\n  }\r\n  return out;\r\n}\r\nfunction excursionOffset(evts, u) {\r\n  let v = 0;\r\n  for (const e of evts) {\r\n    const d = Math.abs(u - e.c);\r\n    if (d >= e.half) continue;\r\n    const x = 1 - d / e.half;\r\n    v += e.amp * (e.kink ? x : 0.5 * (1 - Math.cos(Math.PI * x)));\r\n  }\r\n  return v;\r\n}\r\nfunction baseLine(o) {\r\n  const waveSeed = hashCombine(o.seed, hashString(`${o.group}:wave`), o.key);\r\n  const evts = makeExcursions(o.seed, o.group, o.key, o.deviation, o.devAmpMax);\r\n  const u = [];\r\n  const v = [];\r\n  for (let i = 0; i <= o.segments; i++) {\r\n    const f = i / o.segments;\r\n    u.push(o.u0 + (o.u1 - o.u0) * f);\r\n    v.push(o.offset + fbm1D(waveSeed, i * 0.85, 3) * o.waveAmp + excursionOffset(evts, f));\r\n  }\r\n  for (let pass = 0; pass < 2; pass++) {\r\n    for (let i = 1; i < v.length; i++) {\r\n      const du = u[i] - u[i - 1];\r\n      v[i] = clamp(v[i], v[i - 1] - o.slopeMax * du, v[i - 1] + o.slopeMax * du);\r\n    }\r\n    for (let i = 0; i < v.length; i++) {\r\n      v[i] = clamp(v[i], o.offset - o.corridorHalf, o.offset + o.corridorHalf);\r\n    }\r\n  }\r\n  return { u, v };\r\n}\r\n\r\n// src/fracture/title.ts\r\nfunction generateTitleFracture(o) {\r\n  const { width: w, height: h, seed } = o;\r\n  const rngCount = rngFor(seed, \"title:count\");\r\n  const bandCount = Array.isArray(o.bands.count) ? Math.round(\r\n    clamp(\r\n      o.bands.count[0] + rngCount() * (o.bands.count[1] - o.bands.count[0]),\r\n      Math.min(o.bands.count[0], o.bands.count[1]),\r\n      Math.max(o.bands.count[0], o.bands.count[1])\r\n    )\r\n  ) : Math.max(2, Math.round(o.bands.count));\r\n  const gap = h / bandCount;\r\n  const tiltOn = o.bands.tilt > 0;\r\n  const offs = new Array(bandCount + 1).fill(0);\r\n  const tilts = new Array(bandCount + 1).fill(0);\r\n  for (let k = 1; k < bandCount; k++) {\r\n    const rngB = rngFor(seed, \"title:boundary\", k);\r\n    offs[k] = (rngB() - 0.5) * gap * 0.36;\r\n    if (tiltOn) {\r\n      const sign = k % 2 === 0 ? 1 : -1;\r\n      tilts[k] = sign * (0.3 + 0.7 * rand01(seed, \"title:tilt\", k)) * 0.225 * o.bands.tilt * (gap / (w / 2));\r\n    }\r\n  }\r\n  const yLine = (k, x) => {\r\n    if (k <= 0) return 0;\r\n    if (k >= bandCount) return h;\r\n    return gap * k + offs[k] + tilts[k] * (x - w / 2);\r\n  };\r\n  const boundaries = [];\r\n  const segments = Math.max(4, Math.round(w / 160));\r\n  for (let k = 0; k <= bandCount; k++) {\r\n    if (k === 0 || k === bandCount) {\r\n      boundaries.push([\r\n        [0, k === 0 ? 0 : h],\r\n        [w, k === 0 ? 0 : h]\r\n      ]);\r\n      continue;\r\n    }\r\n    const waveAmp = o.bands.waviness * gap * 0.22;\r\n    const waveSeed = hashCombine(seed, hashString(\"title:wave\"), k);\r\n    const excursions = o.deviation > 0 ? makeExcursions(seed, \"title\", k, o.deviation, gap) : [];\r\n    const base = [];\r\n    for (let i = 0; i <= segments; i++) {\r\n      const x = w * i / segments;\r\n      let y = yLine(k, x) + fbm1D(waveSeed, i * 0.85, 3) * waveAmp;\r\n      if (excursions.length > 0) y += excursionOffset(excursions, i / segments);\r\n      base.push([x, y]);\r\n    }\r\n    let jagged = subdivideMidpoint(\r\n      base,\r\n      o.edgeDetail,\r\n      o.jaggedness * 0.5,\r\n      hashCombine(seed, hashString(\"title:jag\"), k),\r\n      gap * 0.17\r\n    );\r\n    if (tiltOn) {\r\n      jagged = jagged.map(([x, y]) => {\r\n        const lo = (yLine(k - 1, x) + yLine(k, x)) / 2 + 0.06 * gap;\r\n        const hi = (yLine(k, x) + yLine(k + 1, x)) / 2 - 0.06 * gap;\r\n        return [x, y < lo ? lo : y > hi ? hi : y];\r\n      });\r\n    } else if (o.deviation > 0) {\r\n      const lo = gap * k - gap * 0.45;\r\n      const hi = gap * k + gap * 0.45;\r\n      jagged = jagged.map(([x, y]) => [x, y < lo ? lo : y > hi ? hi : y]);\r\n    }\r\n    boundaries.push(jagged);\r\n  }\r\n  const raw = [];\r\n  const cracks = [];\r\n  const junctions = [];\r\n  let crackIdx = 0;\r\n  const order = boundaryBirthOrder(bandCount);\r\n  for (let k = 1; k < bandCount; k++) {\r\n    const rngC = rngFor(seed, \"title:crack\", k);\r\n    const pts = k % 2 === 0 ? reversePts(boundaries[k]) : boundaries[k].slice();\r\n    const rank = order.indexOf(k);\r\n    const grow = 0.38 + rngC() * 0.14;\r\n    const birth = Math.min(0.04 + rank / Math.max(1, bandCount - 2) * 0.38 + rngC() * 0.06, 0.97 - grow);\r\n    cracks.push(makeCrack(seed, `c${crackIdx++}`, \"band\", pts, birth, grow));\r\n  }\r\n  for (let band = 0; band < bandCount; band++) {\r\n    const top = boundaries[band];\r\n    const bot = boundaries[band + 1];\r\n    if (o.bands.splitters === 1) {\r\n      const rngS = rngFor(seed, \"title:split\", band);\r\n      const wantSplit = rngS() < o.bands.diagonalChance && top.length > 6 && bot.length > 6;\r\n      if (!wantSplit) {\r\n        raw.push({ poly: [...top, ...reversePts(bot)], band });\r\n        continue;\r\n      }\r\n      const targetX = (0.3 + rngS() * 0.4) * w;\r\n      const iT = nearestIndexByX(top, targetX, 2);\r\n      const diag2 = o.bands.diagonal;\r\n      const lean = (rngS() * 2 - 1) * (diag2 > 0 ? diag2 * 0.6 * gap : (0.35 + 0.9 * o.deviation) * gap);\r\n      const iB = nearestIndexByX(bot, top[iT][0] + lean, 2);\r\n      const a = top[iT];\r\n      const b = bot[iB];\r\n      const splitBase = [a, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2], b];\r\n      let splitter = subdivideMidpoint(\r\n        splitBase,\r\n        o.edgeDetail,\r\n        o.jaggedness * 0.45,\r\n        hashCombine(seed, hashString(\"title:splitjag\"), band),\r\n        gap * 0.16\r\n      );\r\n      if (diag2 > 0) splitter = clampSplitterToBand(splitter, top, bot, gap);\r\n      const left = [...top.slice(0, iT + 1), ...splitter.slice(1), ...reversePts(bot.slice(0, iB))];\r\n      const right = [\r\n        ...top.slice(iT),\r\n        ...reversePts(bot.slice(iB)),\r\n        ...reversePts(splitter.slice(1, -1))\r\n      ];\r\n      raw.push({ poly: left, band });\r\n      raw.push({ poly: right, band });\r\n      const rngC = rngFor(seed, \"title:splitcrack\", band);\r\n      const sGrow = 0.22 + rngC() * 0.1;\r\n      cracks.push(\r\n        makeCrack(seed, `c${crackIdx++}`, \"split\", splitter, Math.min(0.5 + rngC() * 0.18, 0.97 - sGrow), sGrow)\r\n      );\r\n      const ci1 = cracks.length - 1;\r\n      junctions.push({ crackIndex: ci1, s: 0 }, { crackIndex: ci1, s: cracks[ci1].totalLen });\r\n      continue;\r\n    }\r\n    if (top.length <= 6 || bot.length <= 6) {\r\n      raw.push({ poly: [...top, ...reversePts(bot)], band });\r\n      continue;\r\n    }\r\n    const m = o.bands.splitters;\r\n    const q = o.bands.diagonalChance;\r\n    const probs = [q, 0.45 * q, 0.2 * q].slice(0, m);\r\n    const slotLo = 0.15 * w;\r\n    const slotW = 0.7 * w / m;\r\n    const diag = o.bands.diagonal;\r\n    const bandSign = rand01(seed, \"title:diagSign\", band) < 0.5 ? -1 : 1;\r\n    const cands = [];\r\n    for (let j = 0; j < m; j++) {\r\n      if (rand01(seed, \"title:msplitG\", band, j) >= probs[j]) continue;\r\n      const r1 = rand01(seed, \"title:msplitX\", band, j);\r\n      const r2 = rand01(seed, \"title:msplitL\", band, j);\r\n      const targetX = slotLo + slotW * j + (0.25 + 0.5 * r1) * slotW;\r\n      const iT = nearestIndexByX(top, targetX, 2);\r\n      const lean = diag > 0 ? bandSign * Math.min((0.35 + 0.65 * r2) * diag, 0.6) * gap : (r2 * 2 - 1) * Math.min((0.35 + 0.9 * o.deviation) * gap, 0.2 * slotW);\r\n      const iB = nearestIndexByX(bot, top[iT][0] + lean, 2);\r\n      const a = top[iT];\r\n      const b = bot[iB];\r\n      const splitBase = [a, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2], b];\r\n      let pts = subdivideMidpoint(\r\n        splitBase,\r\n        o.edgeDetail,\r\n        o.jaggedness * 0.45,\r\n        hashCombine(seed, hashString(\"title:msplitjag\"), band, j),\r\n        Math.min(gap * 0.16, 0.1 * slotW)\r\n      );\r\n      if (diag > 0) pts = clampSplitterToBand(pts, top, bot, gap);\r\n      cands.push({ iT, iB, pts });\r\n    }\r\n    cands.sort((a, b) => a.iT - b.iT);\r\n    const acc = [];\r\n    for (const c of cands) {\r\n      const prev = acc[acc.length - 1];\r\n      if (c.iT < 2 || c.iT > top.length - 3 || c.iB < 2 || c.iB > bot.length - 3) continue;\r\n      if (prev && (c.iT - prev.iT < 4 || c.iB - prev.iB < 4)) continue;\r\n      acc.push(c);\r\n    }\r\n    if (acc.length === 0) {\r\n      raw.push({ poly: [...top, ...reversePts(bot)], band });\r\n      continue;\r\n    }\r\n    raw.push({\r\n      poly: [...top.slice(0, acc[0].iT + 1), ...acc[0].pts.slice(1), ...reversePts(bot.slice(0, acc[0].iB))],\r\n      band\r\n    });\r\n    for (let j = 0; j + 1 < acc.length; j++) {\r\n      raw.push({\r\n        poly: [\r\n          ...top.slice(acc[j].iT, acc[j + 1].iT + 1),\r\n          ...acc[j + 1].pts.slice(1),\r\n          ...reversePts(bot.slice(acc[j].iB, acc[j + 1].iB)),\r\n          ...reversePts(acc[j].pts.slice(1, -1))\r\n        ],\r\n        band\r\n      });\r\n    }\r\n    const last = acc[acc.length - 1];\r\n    raw.push({\r\n      poly: [...top.slice(last.iT), ...reversePts(bot.slice(last.iB)), ...reversePts(last.pts.slice(1, -1))],\r\n      band\r\n    });\r\n    for (let j = 0; j < acc.length; j++) {\r\n      const rB1 = rand01(seed, \"title:msplitGrow\", band, j);\r\n      const rB2 = rand01(seed, \"title:msplitBirth\", band, j);\r\n      const sGrow = 0.22 + rB1 * 0.1;\r\n      cracks.push(\r\n        makeCrack(seed, `c${crackIdx++}`, \"split\", acc[j].pts, Math.min(0.5 + rB2 * 0.18, 0.97 - sGrow), sGrow)\r\n      );\r\n      const ciM = cracks.length - 1;\r\n      junctions.push({ crackIndex: ciM, s: 0 }, { crackIndex: ciM, s: cracks[ciM].totalLen });\r\n    }\r\n  }\r\n  const ranks = shuffledRanks(seed, \"title:z\", raw.length);\r\n  const shards = [];\r\n  for (let i = 0; i < raw.length; i++) {\r\n    const s = makeShard(seed, i, raw[i].poly, raw[i].band, ranks[i], o.seamOutsetPx);\r\n    if (s) shards.push(s);\r\n  }\r\n  return { shards, cracks, junctions, stubScale: gap };\r\n}\r\nfunction boundaryBirthOrder(bandCount) {\r\n  const ks = Array.from({ length: bandCount - 1 }, (_, i) => i + 1);\r\n  const mid = bandCount / 2;\r\n  return ks.sort((p, q) => Math.abs(p - mid) - Math.abs(q - mid) || p - q);\r\n}\r\nfunction interpY(poly, x) {\r\n  if (x <= poly[0][0]) return poly[0][1];\r\n  const last = poly[poly.length - 1];\r\n  if (x >= last[0]) return last[1];\r\n  for (let i = 0; i + 1 < poly.length; i++) {\r\n    if (x >= poly[i][0] && x <= poly[i + 1][0]) {\r\n      const t = (x - poly[i][0]) / Math.max(1e-9, poly[i + 1][0] - poly[i][0]);\r\n      return poly[i][1] + (poly[i + 1][1] - poly[i][1]) * t;\r\n    }\r\n  }\r\n  return last[1];\r\n}\r\nfunction clampSplitterToBand(pts, top, bot, gap) {\r\n  const m = 0.05 * gap;\r\n  return pts.map((p, i) => {\r\n    if (i === 0 || i === pts.length - 1) return p;\r\n    const lo = interpY(top, p[0]) + m;\r\n    const hi = interpY(bot, p[0]) - m;\r\n    return [p[0], p[1] < lo ? lo : p[1] > hi ? hi : p[1]];\r\n  });\r\n}\r\nfunction nearestIndexByX(pts, x, margin) {\r\n  let best = margin;\r\n  let bestD = Infinity;\r\n  for (let i = margin; i < pts.length - margin; i++) {\r\n    const d = Math.abs(pts[i][0] - x);\r\n    if (d < bestD) {\r\n      bestD = d;\r\n      best = i;\r\n    }\r\n  }\r\n  return best;\r\n}\r\n\r\n// src/fracture/radial.ts\r\nfunction generateRadialFracture(o) {\r\n  const { width: w, height: h, seed } = o;\r\n  const [ix, iy] = o.impact;\r\n  const N = o.rays.count;\r\n  const M = o.rings.count;\r\n  let doubling = o.rays.doubling && N >= 3;\r\n  let dS = Math.min(Math.max(1, o.rays.doublingStartRing), M);\r\n  if (dS >= M + 1) doubling = false;\r\n  if (M < 2) doubling = false;\r\n  const count = doubling ? 2 * N : N;\r\n  const R = Math.max(\r\n    Math.hypot(ix, iy),\r\n    Math.hypot(w - ix, iy),\r\n    Math.hypot(ix, h - iy),\r\n    Math.hypot(w - ix, h - iy)\r\n  ) * 1.08;\r\n  const r0 = Math.max(6, 0.05 * Math.min(w, h)) * o.impactHole;\r\n  const slot = TAU / N;\r\n  const stepAtRing = (j) => !doubling ? 1 : j >= dS ? 1 : 2;\r\n  const rayStartRing = (a) => !doubling || a % 2 === 0 ? 0 : dS;\r\n  const asym = o.rings.asymmetry;\r\n  const strongDir = rand01(seed, \"radial:strongDir\") * TAU;\r\n  const keepArc = (j, a) => {\r\n    if (j <= 0 || j >= M) return true;\r\n    if (doubling && j === dS) return true;\r\n    if (o.rings.partial <= 0) return true;\r\n    const ramp = M >= 3 ? (j - 1) / (M - 2) : 1;\r\n    let keepProb = 1 - o.rings.partial * (0.1 + 0.45 * ramp);\r\n    if (asym > 0) {\r\n      const side = 0.5 + 0.5 * Math.cos(angles[a] - strongDir);\r\n      const dropScale = 1 + asym * 1.6 * (1 - side);\r\n      keepProb = Math.max(0.02, 1 - (1 - keepProb) * dropScale);\r\n    }\r\n    return rand01(seed, \"radial:arcKeep\", j, a) < keepProb;\r\n  };\r\n  const baseAngles = [];\r\n  for (let i = 0; i < N; i++) {\r\n    const r = rand01(seed, \"radial:baseAngle\", i);\r\n    baseAngles.push(slot * i + (r - 0.5) * 2 * 0.18 * slot * o.rays.angleJitter);\r\n  }\r\n  const angles = [];\r\n  if (doubling) {\r\n    for (let i = 0; i < N; i++) {\r\n      angles.push(baseAngles[i]);\r\n      const next = i + 1 < N ? baseAngles[i + 1] : baseAngles[0] + TAU;\r\n      const r = rand01(seed, \"radial:secAngle\", i);\r\n      angles.push((baseAngles[i] + next) / 2 + (r - 0.5) * 2 * 0.1 * slot);\r\n    }\r\n  } else {\r\n    angles.push(...baseAngles);\r\n  }\r\n  const weights = [];\r\n  {\r\n    let sum = 0;\r\n    for (let j = 1; j <= M; j++) {\r\n      const wj = o.rings.spacing === \"geometric\" ? Math.pow(1.55, j - 1) : 1;\r\n      weights.push(wj);\r\n      sum += wj;\r\n    }\r\n    for (let j = 0; j < M; j++) weights[j] /= sum;\r\n  }\r\n  const radii = [];\r\n  for (let a = 0; a < count; a++) {\r\n    const row = [0];\r\n    let cum = 0;\r\n    for (let j = 1; j <= M; j++) {\r\n      const jit = (rand01(seed, \"radial:ring\", a, j) - 0.5) * 2 * 0.35 * o.rings.jitter;\r\n      cum += weights[j - 1] * (1 + jit);\r\n      row.push(cum);\r\n    }\r\n    const total = row[M];\r\n    for (let j = 0; j <= M; j++) row[j] = r0 + (R - r0) * row[j] / total;\r\n    radii.push(row);\r\n  }\r\n  const wobAmp = (j) => 0.1 * o.rays.waviness * slot * (stepAtRing(j) === 2 || !doubling ? 1 : 0.5);\r\n  const hasDeviation = o.deviation > 0;\r\n  const budget = [];\r\n  const kinkMag = [];\r\n  const kinkRing = [];\r\n  for (let a = 0; a < count; a++) {\r\n    const prev = angles[(a - 1 + count) % count] - (a === 0 ? TAU : 0);\r\n    const next = angles[(a + 1) % count] + (a === count - 1 ? TAU : 0);\r\n    budget.push(0.32 * Math.min(angles[a] - prev, next - angles[a]));\r\n    if (hasDeviation && rand01(seed, \"radial:kink\", a) < o.deviation * 0.55 && M > 1) {\r\n      const jk = 1 + Math.floor(rand01(seed, \"radial:kinkJ\", a) * (M - 1));\r\n      const sign = rand01(seed, \"radial:kinkS\", a) < 0.5 ? -1 : 1;\r\n      kinkMag.push(sign * (0.5 + 0.5 * rand01(seed, \"radial:kinkM\", a)) * budget[a]);\r\n      kinkRing.push(jk);\r\n    } else {\r\n      kinkMag.push(0);\r\n      kinkRing.push(0);\r\n    }\r\n  }\r\n  const anchorAngle = (a, j) => {\r\n    const wobble = fbm1D(hashCombine(seed, hashString(\"radial:wob\"), a), j * 0.8, 2) * wobAmp(j);\r\n    if (!hasDeviation) return angles[a] + wobble;\r\n    const kink = kinkMag[a] * Math.min(1, Math.max(0, j - kinkRing[a]) / 1.5);\r\n    const phi = Math.max(-budget[a], Math.min(budget[a], wobble + kink));\r\n    return angles[a] + phi;\r\n  };\r\n  const anchors = [];\r\n  for (let a = 0; a < count; a++) {\r\n    const row = [];\r\n    for (let j = 0; j <= M; j++) {\r\n      const ang = anchorAngle(a, j);\r\n      row.push([ix + Math.cos(ang) * radii[a][j], iy + Math.sin(ang) * radii[a][j]]);\r\n    }\r\n    anchors.push(row);\r\n  }\r\n  const rayEdges = /* @__PURE__ */ new Map();\r\n  for (let a = 0; a < count; a++) {\r\n    for (let j = rayStartRing(a); j < M; j++) {\r\n      const A = anchors[a][j];\r\n      const B = anchors[a][j + 1];\r\n      const rMid = (radii[a][j] + radii[a][j + 1]) / 2;\r\n      const angMid = angles[a] + fbm1D(hashCombine(seed, hashString(\"radial:wob\"), a), (j + 0.5) * 0.8, 2) * Math.min(wobAmp(j), wobAmp(j + 1));\r\n      const mid = [ix + Math.cos(angMid) * rMid, iy + Math.sin(angMid) * rMid];\r\n      const lateralGap = rMid * slot * (doubling && stepAtRing(j) === 1 ? 0.5 : 1);\r\n      const pts = subdivideMidpoint(\r\n        [A, mid, B],\r\n        o.edgeDetail,\r\n        o.jaggedness * 0.5,\r\n        hashCombine(seed, hashString(\"radial:rayjag\"), a, j),\r\n        0.25 * lateralGap\r\n      );\r\n      rayEdges.set(`${a}:${j}`, pts);\r\n    }\r\n  }\r\n  const arcEdges = /* @__PURE__ */ new Map();\r\n  for (let j = 0; j <= M; j++) {\r\n    const step = stepAtRing(j);\r\n    for (let a = 0; a < count; a += step) {\r\n      const b = (a + step) % count;\r\n      const A = anchors[a][j];\r\n      const B = anchors[b][j];\r\n      const angA = anchorAngle(a, j);\r\n      let angB = anchorAngle(b, j);\r\n      if (angB <= angA) angB += TAU;\r\n      const rA = radii[a][j];\r\n      const rB = radii[b][j];\r\n      const gapDown = j > 0 ? Math.min(rA - radii[a][j - 1], rB - radii[b][j - 1]) : r0;\r\n      const gapUp = j < M ? Math.min(radii[a][j + 1] - rA, radii[b][j + 1] - rB) : gapDown;\r\n      const minGap = Math.max(1, Math.min(gapDown, gapUp));\r\n      const bulgeSeed = hashCombine(seed, hashString(\"radial:bulge\"), j, a);\r\n      const base = [A];\r\n      const innerSamples = 2;\r\n      for (let s = 1; s <= innerSamples; s++) {\r\n        const f = s / (innerSamples + 1);\r\n        const ang = lerp(angA, angB, f);\r\n        const r = lerp(rA, rB, f) + fbm1D(bulgeSeed, f * 2.3, 2) * 0.14 * minGap * (0.4 + 0.6 * o.rings.jitter);\r\n        base.push([ix + Math.cos(ang) * r, iy + Math.sin(ang) * r]);\r\n      }\r\n      base.push(B);\r\n      const pts = subdivideMidpoint(\r\n        base,\r\n        o.edgeDetail,\r\n        o.jaggedness * 0.4,\r\n        hashCombine(seed, hashString(\"radial:arcjag\"), j, a),\r\n        0.22 * minGap\r\n      );\r\n      arcEdges.set(`${j}:${a}`, pts);\r\n    }\r\n  }\r\n  const raw = [];\r\n  {\r\n    const segs = [];\r\n    const step = stepAtRing(0);\r\n    for (let a = 0; a < count; a += step) segs.push(arcEdges.get(`0:${a}`));\r\n    const clipped = clipPolygonToRect(stitchLoop(segs), w, h);\r\n    if (clipped.length >= 3) raw.push({ poly: clipped, ring: 0 });\r\n  }\r\n  for (let j = 0; j < M; j++) {\r\n    const bandStep = stepAtRing(j);\r\n    for (let aL = 0; aL < count; aL += bandStep) {\r\n      if (!keepArc(j, aL)) continue;\r\n      let j1 = j;\r\n      while (j1 + 1 < M && !keepArc(j1 + 1, aL)) j1++;\r\n      const aR = (aL + bandStep) % count;\r\n      const segs = [];\r\n      for (let jj = j; jj <= j1; jj++) segs.push(rayEdges.get(`${aL}:${jj}`));\r\n      const outerStep = stepAtRing(j1 + 1);\r\n      const nOuter = bandStep / outerStep;\r\n      for (let s = 0; s < nOuter; s++) {\r\n        segs.push(arcEdges.get(`${j1 + 1}:${(aL + s * outerStep) % count}`));\r\n      }\r\n      for (let jj = j1; jj >= j; jj--) segs.push(reversePts(rayEdges.get(`${aR}:${jj}`)));\r\n      segs.push(reversePts(arcEdges.get(`${j}:${aL}`)));\r\n      const poly = stitchLoop(segs);\r\n      const clipped = clipPolygonToRect(poly, w, h);\r\n      if (clipped.length >= 3) raw.push({ poly: clipped, ring: j + 1 });\r\n    }\r\n  }\r\n  const ranks = shuffledRanks(seed, \"radial:z\", raw.length);\r\n  const shards = [];\r\n  for (let i = 0; i < raw.length; i++) {\r\n    const s = makeShard(seed, i, raw[i].poly, raw[i].ring, ranks[i], o.seamOutsetPx);\r\n    if (s) shards.push(s);\r\n  }\r\n  const cracks = [];\r\n  let ci = 0;\r\n  {\r\n    const segs = [];\r\n    const step = stepAtRing(0);\r\n    for (let a = 0; a < count; a += step) segs.push(arcEdges.get(`0:${a}`));\r\n    const loop = concatPolylines(segs);\r\n    cracks.push(makeCrack(seed, `c${ci++}`, \"crush\", loop, 0, 0.12));\r\n  }\r\n  for (let a = 0; a < count; a++) {\r\n    const start = rayStartRing(a);\r\n    if (start >= M) continue;\r\n    const segs = [];\r\n    for (let j = start; j < M; j++) segs.push(rayEdges.get(`${a}:${j}`));\r\n    const pts = concatPolylines(segs);\r\n    const rngC = rngFor(seed, \"radial:raybirth\", a);\r\n    const secondary = doubling && a % 2 === 1;\r\n    const birth = secondary ? 0.32 + rngC() * 0.08 : 0.02 + rngC() * 0.08;\r\n    const grow = secondary ? 0.3 : 0.48 + rngC() * 0.14;\r\n    cracks.push(makeCrack(seed, `c${ci++}`, \"ray\", pts, birth, grow));\r\n  }\r\n  const junctions = [];\r\n  for (let j = 1; j < M; j++) {\r\n    const step = stepAtRing(j);\r\n    const arcStarts = [];\r\n    for (let a = 0; a < count; a += step) arcStarts.push(a);\r\n    const kept = arcStarts.map((a) => keepArc(j, a));\r\n    const rngC = rngFor(seed, \"radial:ringbirth\", j);\r\n    const base = 0.34 + (j - 1) / Math.max(1, M - 2) * 0.38 + rngC() * 0.05;\r\n    if (kept.every(Boolean)) {\r\n      const segs = arcStarts.map((a) => arcEdges.get(`${j}:${a}`));\r\n      const pts = concatPolylines(segs);\r\n      cracks.push(makeCrack(seed, `c${ci++}`, \"ring\", pts, Math.min(base, 0.97 - 0.26), 0.26));\r\n      continue;\r\n    }\r\n    if (!kept.some(Boolean)) continue;\r\n    const n = arcStarts.length;\r\n    for (let i = 0; i < n; i++) {\r\n      if (!kept[i] || kept[(i - 1 + n) % n]) continue;\r\n      const segs = [];\r\n      for (let k = 0; k < n && kept[(i + k) % n]; k++) {\r\n        segs.push(arcEdges.get(`${j}:${arcStarts[(i + k) % n]}`));\r\n      }\r\n      const pts = concatPolylines(segs);\r\n      const birth = Math.min(base + 0.04 * rand01(seed, \"radial:ringrun\", j, arcStarts[i]), 0.97 - 0.26);\r\n      cracks.push(makeCrack(seed, `c${ci++}`, \"ring\", pts, birth, 0.26));\r\n      const ciRun = cracks.length - 1;\r\n      junctions.push({ crackIndex: ciRun, s: 0 }, { crackIndex: ciRun, s: cracks[ciRun].totalLen });\r\n    }\r\n  }\r\n  return {\r\n    shards,\r\n    cracks,\r\n    maxRing: M,\r\n    junctions,\r\n    stubScale: Math.sqrt(w * h / Math.max(1, shards.length))\r\n  };\r\n}\r\nfunction concatPolylines(segs) {\r\n  const out = [];\r\n  for (const seg of segs) {\r\n    const start = out.length === 0 ? 0 : 1;\r\n    for (let i = start; i < seg.length; i++) out.push(seg[i]);\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/fracture/collapse.ts\r\nfunction generateCollapseFracture(o) {\r\n  const { width: w, height: h, seed } = o;\r\n  const diag = Math.hypot(w, h);\r\n  const pad = 0.6 * diag;\r\n  const sep = Math.abs((o.collapse.angleB - o.collapse.angleA + 90) % 180 - 90);\r\n  const theta = Math.min(16, (Math.max(sep, 35) - 20) / 2);\r\n  const slopeMax = Math.tan(degToRad(theta));\r\n  const rngA = rngFor(seed, \"collapse:countA\");\r\n  const rngB = rngFor(seed, \"collapse:countB\");\r\n  const countA = resolveCount(o.collapse.countA, rngA);\r\n  const countB = resolveCount(o.collapse.countB, rngB);\r\n  const famA = buildFamily(o, \"A\", o.collapse.angleA, countA, slopeMax, w, h, pad);\r\n  const famB = buildFamily(o, \"B\", o.collapse.angleB, countB, slopeMax, w, h, pad);\r\n  const nA = famA.length;\r\n  const nB = famB.length;\r\n  const cross = [];\r\n  for (let i = 0; i < nA; i++) {\r\n    const row = [];\r\n    const A = famA[i];\r\n    for (let j = 0; j < nB; j++) {\r\n      const B = famB[j];\r\n      let found = null;\r\n      outer: for (let p = 0; p < A.pts.length - 1; p++) {\r\n        for (let q = 0; q < B.pts.length - 1; q++) {\r\n          const hit = segmentIntersection(A.pts[p], A.pts[p + 1], B.pts[q], B.pts[q + 1]);\r\n          if (hit) {\r\n            found = {\r\n              p: hit.p,\r\n              sA: A.cum[p] + hit.ta * (A.cum[p + 1] - A.cum[p]),\r\n              sB: B.cum[q] + hit.tb * (B.cum[q + 1] - B.cum[q])\r\n            };\r\n            break outer;\r\n          }\r\n        }\r\n      }\r\n      if (!found) {\r\n        const fallback = [w / 2, h / 2];\r\n        found = { p: fallback, sA: A.cum[A.cum.length - 1] / 2, sB: B.cum[B.cum.length - 1] / 2 };\r\n      }\r\n      row.push(found);\r\n    }\r\n    cross.push(row);\r\n  }\r\n  const gapA = approxGap(famA);\r\n  const gapB = approxGap(famB);\r\n  const maxOff = 0.2 * Math.min(gapA, gapB);\r\n  const sliceA = /* @__PURE__ */ new Map();\r\n  const sliceB = /* @__PURE__ */ new Map();\r\n  for (let i = 0; i < nA; i++) {\r\n    for (let j = 0; j < nB - 1; j++) {\r\n      sliceA.set(\r\n        `${i}:${j}`,\r\n        jagSlice(famA[i], cross[i][j].sA, cross[i][j + 1].sA, cross[i][j].p, cross[i][j + 1].p, o, hashCombine(seed, hashString(\"collapse:Ajag\"), i, j), maxOff)\r\n      );\r\n    }\r\n  }\r\n  for (let j = 0; j < nB; j++) {\r\n    for (let i = 0; i < nA - 1; i++) {\r\n      sliceB.set(\r\n        `${j}:${i}`,\r\n        jagSlice(famB[j], cross[i][j].sB, cross[i + 1][j].sB, cross[i][j].p, cross[i + 1][j].p, o, hashCombine(seed, hashString(\"collapse:Bjag\"), j, i), maxOff)\r\n      );\r\n    }\r\n  }\r\n  const keepB = (j, i) => {\r\n    if (j <= 0 || j >= nB - 1) return true;\r\n    if (o.collapse.merge <= 0) return true;\r\n    return rand01(o.seed, \"collapse:mergeB\", j, i) >= o.collapse.merge;\r\n  };\r\n  const rows = nA - 1;\r\n  const raw = [];\r\n  for (let i = 0; i < nA - 1; i++) {\r\n    for (let j = 0; j < nB - 1; j++) {\r\n      if (!keepB(j, i)) continue;\r\n      let j1 = j;\r\n      while (j1 + 1 < nB - 1 && !keepB(j1 + 1, i)) j1++;\r\n      const segs = [];\r\n      for (let jj = j; jj <= j1; jj++) segs.push(sliceA.get(`${i}:${jj}`));\r\n      segs.push(sliceB.get(`${j1 + 1}:${i}`));\r\n      for (let jj = j1; jj >= j; jj--) segs.push(reversePts(sliceA.get(`${i + 1}:${jj}`)));\r\n      segs.push(reversePts(sliceB.get(`${j}:${i}`)));\r\n      const poly = stitchLoop(segs);\r\n      const clipped = clipPolygonToRect(poly, w, h);\r\n      if (clipped.length >= 3) raw.push({ poly: clipped, row: rows - 1 - i });\r\n    }\r\n  }\r\n  const ranks = shuffledRanks(seed, \"collapse:z\", raw.length);\r\n  const shards = [];\r\n  for (let i = 0; i < raw.length; i++) {\r\n    const s = makeShard(seed, i, raw[i].poly, raw[i].row, ranks[i], o.seamOutsetPx);\r\n    if (s) shards.push(s);\r\n  }\r\n  const cracks = [];\r\n  const junctions = [];\r\n  let ci = 0;\r\n  for (let li = 1; li < famA.length - 1; li++) {\r\n    const segs = [];\r\n    for (let k = 0; k < nB - 1; k++) {\r\n      const s = sliceA.get(`${li}:${k}`);\r\n      if (s) segs.push(s);\r\n    }\r\n    if (segs.length === 0) continue;\r\n    let pts = concatPolylines2(segs);\r\n    const rngC = rngFor(seed, \"collapse:A:birth\", li);\r\n    if (li % 2 === 0) pts = reversePts(pts);\r\n    const grow = 0.3 + rngC() * 0.12;\r\n    const birth = Math.min(rngC() * 0.45, 0.97 - grow);\r\n    cracks.push(makeCrack(seed, `c${ci++}`, \"mesh\", pts, birth, grow));\r\n  }\r\n  for (let li = 1; li < famB.length - 1; li++) {\r\n    const rngC = rngFor(seed, \"collapse:B:birth\", li);\r\n    const grow = 0.3 + rngC() * 0.12;\r\n    const birthBase = Math.min(rngC() * 0.45, 0.97 - grow);\r\n    const spans = nA - 1;\r\n    const kept = [];\r\n    for (let i = 0; i < spans; i++) kept.push(keepB(li, i));\r\n    if (kept.every(Boolean)) {\r\n      const segs = [];\r\n      for (let k = 0; k < spans; k++) {\r\n        const s = sliceB.get(`${li}:${k}`);\r\n        if (s) segs.push(s);\r\n      }\r\n      if (segs.length === 0) continue;\r\n      let pts = concatPolylines2(segs);\r\n      if (li % 2 === 0) pts = reversePts(pts);\r\n      cracks.push(makeCrack(seed, `c${ci++}`, \"mesh\", pts, birthBase, grow));\r\n      continue;\r\n    }\r\n    for (let i0 = 0; i0 < spans; i0++) {\r\n      if (!kept[i0] || i0 > 0 && kept[i0 - 1]) continue;\r\n      const segs = [];\r\n      for (let k = i0; k < spans && kept[k]; k++) {\r\n        const s = sliceB.get(`${li}:${k}`);\r\n        if (s) segs.push(s);\r\n      }\r\n      if (segs.length === 0) continue;\r\n      let pts = concatPolylines2(segs);\r\n      if (li % 2 === 0) pts = reversePts(pts);\r\n      const birth = Math.min(birthBase + 0.05 * rand01(seed, \"collapse:Brun\", li, i0), 0.985 - grow);\r\n      cracks.push(makeCrack(seed, `c${ci++}`, \"mesh\", pts, birth, grow));\r\n      const ciRun = cracks.length - 1;\r\n      junctions.push({ crackIndex: ciRun, s: 0 }, { crackIndex: ciRun, s: cracks[ciRun].totalLen });\r\n    }\r\n  }\r\n  return { shards, cracks, rows, junctions, stubScale: Math.min(approxGap(famA), approxGap(famB)) };\r\n}\r\nfunction resolveCount(spec, rng) {\r\n  if (Array.isArray(spec)) {\r\n    const lo = Math.min(spec[0], spec[1]);\r\n    const hi = Math.max(spec[0], spec[1]);\r\n    return Math.max(1, Math.round(lo + rng() * (hi - lo)));\r\n  }\r\n  return Math.max(1, Math.round(spec));\r\n}\r\nfunction buildFamily(o, famKey, angleDeg, count, slopeMax, w, h, pad) {\r\n  const a = degToRad(angleDeg);\r\n  const dir = [Math.cos(a), Math.sin(a)];\r\n  const nrm = [-Math.sin(a), Math.cos(a)];\r\n  const corners = [\r\n    [0, 0],\r\n    [w, 0],\r\n    [w, h],\r\n    [0, h]\r\n  ];\r\n  let uMin = Infinity;\r\n  let uMax = -Infinity;\r\n  let vMin = Infinity;\r\n  let vMax = -Infinity;\r\n  for (const c of corners) {\r\n    const u = c[0] * dir[0] + c[1] * dir[1];\r\n    const v = c[0] * nrm[0] + c[1] * nrm[1];\r\n    uMin = Math.min(uMin, u);\r\n    uMax = Math.max(uMax, u);\r\n    vMin = Math.min(vMin, v);\r\n    vMax = Math.max(vMax, v);\r\n  }\r\n  const gap = (vMax - vMin) / (count + 1);\r\n  const offsets = [vMin - 0.75 * gap];\r\n  const rngJ = rngFor(o.seed, `collapse:${famKey}:offsets`);\r\n  for (let k = 0; k < count; k++) {\r\n    offsets.push(vMin + gap * (k + 1) + (rngJ() - 0.5) * gap * 0.55);\r\n  }\r\n  offsets.push(vMax + 0.75 * gap);\r\n  const u0 = uMin - pad;\r\n  const u1 = uMax + pad;\r\n  const segments = Math.max(8, Math.round((u1 - u0) / 120));\r\n  const lines = [];\r\n  for (let li = 0; li < offsets.length; li++) {\r\n    const gapPrev = li > 0 ? offsets[li] - offsets[li - 1] : gap;\r\n    const gapNext = li < offsets.length - 1 ? offsets[li + 1] - offsets[li] : gap;\r\n    const corridorHalf = 0.45 * Math.max(4, Math.min(gapPrev, gapNext));\r\n    const { u, v } = baseLine({\r\n      seed: o.seed,\r\n      group: `collapse:${famKey}`,\r\n      key: li,\r\n      u0,\r\n      u1,\r\n      segments,\r\n      offset: offsets[li],\r\n      waveAmp: o.collapse.waviness * gap * 0.2,\r\n      deviation: o.deviation,\r\n      devAmpMax: gap,\r\n      slopeMax,\r\n      corridorHalf\r\n    });\r\n    const pts = u.map((uu, k) => [dir[0] * uu + nrm[0] * v[k], dir[1] * uu + nrm[1] * v[k]]);\r\n    lines.push({ pts, cum: cumulativeLengths(pts) });\r\n  }\r\n  return lines;\r\n}\r\nfunction approxGap(fam) {\r\n  if (fam.length < 2) return 40;\r\n  const a = fam[0].pts[0];\r\n  const b = fam[fam.length - 1].pts[0];\r\n  return Math.max(8, Math.hypot(b[0] - a[0], b[1] - a[1]) / (fam.length - 1));\r\n}\r\nfunction jagSlice(line, s0, s1, p0, p1, o, jagSeed, maxOff) {\r\n  const lo = Math.min(s0, s1);\r\n  const hi = Math.max(s0, s1);\r\n  const base = [lo === s0 ? p0 : p1];\r\n  for (let i = 0; i < line.pts.length; i++) {\r\n    if (line.cum[i] > lo + 0.5 && line.cum[i] < hi - 0.5) base.push(line.pts[i]);\r\n  }\r\n  base.push(lo === s0 ? p1 : p0);\r\n  let out = subdivideMidpoint(base, o.edgeDetail, o.jaggedness * 0.45, jagSeed, maxOff);\r\n  if (lo !== s0) out = reversePts(out);\r\n  return out;\r\n}\r\nfunction concatPolylines2(segs) {\r\n  const out = [];\r\n  for (const seg of segs) {\r\n    const start = out.length === 0 ? 0 : 1;\r\n    for (let i = start; i < seg.length; i++) out.push(seg[i]);\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/fracture/hero.ts\r\nfunction generateHeroFracture(o) {\r\n  const w = o.width;\r\n  const h = o.height;\r\n  const minDim = Math.min(w, h);\r\n  const baseR = o.hero.sizeFrac * minDim;\r\n  const centers = layoutCenters(o, baseR);\r\n  const shards = [];\r\n  for (let k = 0; k < centers.length; k++) {\r\n    const rng = rngFor(o.seed, \"hero\", k);\r\n    const nV = 11 + Math.floor(rng() * 5);\r\n    const rotOff = rng() * TAU;\r\n    const rScale = 0.88 + rng() * 0.24;\r\n    const slot = TAU / nV;\r\n    const ring = [];\r\n    for (let i = 0; i < nV; i++) {\r\n      const ang = rotOff + i * slot + (rng() - 0.5) * slot * 0.55;\r\n      const rad = baseR * rScale * (0.76 + 0.46 * rng());\r\n      ring.push([centers[k][0] + Math.cos(ang) * rad, centers[k][1] + Math.sin(ang) * rad]);\r\n    }\r\n    ring.push(ring[0]);\r\n    const jagged = subdivideMidpoint(\r\n      ring,\r\n      o.edgeDetail,\r\n      o.jaggedness * 0.5,\r\n      rngFor(o.seed, \"hero:jag\", k)() * 4294967295,\r\n      baseR * 0.12\r\n    );\r\n    jagged.pop();\r\n    const shard = makeShard(o.seed, k, jagged, 0, k, o.seamOutsetPx);\r\n    if (shard) shards.push(shard);\r\n  }\r\n  return { shards, cracks: [], junctions: [], stubScale: baseR };\r\n}\r\nfunction layoutCenters(o, baseR) {\r\n  const cx = o.width / 2;\r\n  const cy = o.height / 2;\r\n  const K = o.hero.count;\r\n  if (K === 1) return [[cx, cy]];\r\n  const rng = rngFor(o.seed, \"hero:layout\");\r\n  const sep = baseR * (1.05 + 0.9 * o.hero.spread) * (1 - 0.72 * o.hero.overlap);\r\n  if (K === 2) {\r\n    const dx = baseR * 0.22 * (rng() * 2 - 1);\r\n    return [\r\n      [cx + dx, cy - sep / 2],\r\n      [cx - dx, cy + sep / 2]\r\n    ];\r\n  }\r\n  const a0 = -Math.PI / 2 + (rng() - 0.5) * 0.5;\r\n  const out = [];\r\n  for (let k = 0; k < 3; k++) {\r\n    const a = a0 + k * TAU / 3;\r\n    out.push([cx + Math.cos(a) * sep * 0.62, cy + Math.sin(a) * sep * 0.62]);\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/fracture/web.ts\r\nfunction wrapAngle(d) {\r\n  return ((d + Math.PI) % TAU + TAU) % TAU - Math.PI;\r\n}\r\nfunction webOrigin(w, h, dirDeg, distance) {\r\n  const dirRad = degToRad(dirDeg);\r\n  const maxDim = Math.max(w, h);\r\n  const cx = w / 2;\r\n  const cy = h / 2;\r\n  const ux = -Math.cos(dirRad);\r\n  const uy = -Math.sin(dirRad);\r\n  let tBound = Infinity;\r\n  if (ux > 1e-9) tBound = Math.min(tBound, (w - cx) / ux);\r\n  else if (ux < -1e-9) tBound = Math.min(tBound, -cx / ux);\r\n  if (uy > 1e-9) tBound = Math.min(tBound, (h - cy) / uy);\r\n  else if (uy < -1e-9) tBound = Math.min(tBound, -cy / uy);\r\n  if (!Number.isFinite(tBound)) tBound = maxDim;\r\n  const dist = Math.max(distance * maxDim, tBound + 0.2 * maxDim);\r\n  return [cx + ux * dist, cy + uy * dist];\r\n}\r\nfunction generateWebFracture(o) {\r\n  const { width: w, height: h, seed } = o;\r\n  const N = o.web.rays;\r\n  const M = o.web.rings;\r\n  const irr = o.web.irregularity;\r\n  const ringDetail = Math.min(1, o.edgeDetail);\r\n  const dirRad = degToRad(o.web.dir);\r\n  const maxDim = Math.max(w, h);\r\n  const P = webOrigin(w, h, o.web.dir, o.web.distance);\r\n  const [px, py] = P;\r\n  const corners = [\r\n    [0, 0],\r\n    [w, 0],\r\n    [w, h],\r\n    [0, h]\r\n  ];\r\n  let maxDev = 0;\r\n  let maxCornerDist = 0;\r\n  for (const c of corners) {\r\n    const dev = Math.abs(wrapAngle(Math.atan2(c[1] - py, c[0] - px) - dirRad));\r\n    if (dev > maxDev) maxDev = dev;\r\n    const d = Math.hypot(c[0] - px, c[1] - py);\r\n    if (d > maxCornerDist) maxCornerDist = d;\r\n  }\r\n  const slot0 = 2 * maxDev / Math.max(1, N - 1);\r\n  const spread = Math.min(TAU * 0.95, 2 * maxDev + 2 * slot0);\r\n  const slot = spread / Math.max(1, N - 1);\r\n  const nearX = Math.max(0, Math.min(w, px));\r\n  const nearY = Math.max(0, Math.min(h, py));\r\n  const dNear = Math.hypot(px - nearX, py - nearY);\r\n  const margin = 0.05 * maxDim;\r\n  const r0 = Math.max(5, dNear - margin);\r\n  const R = maxCornerDist / Math.max(0.2, Math.cos(slot / 2)) * 1.06 + margin;\r\n  const asym = o.rings.asymmetry;\r\n  const strongDir = dirRad + (rand01(seed, \"web:strongDir\") - 0.5) * spread;\r\n  const keepArc = (j, a) => {\r\n    if (j <= 0 || j >= M) return true;\r\n    if (o.rings.partial <= 0) return true;\r\n    const ramp = M >= 3 ? (j - 1) / (M - 2) : 1;\r\n    let keepProb = 1 - o.rings.partial * (0.1 + 0.45 * ramp);\r\n    if (asym > 0) {\r\n      const side = 0.5 + 0.5 * Math.cos(angles[a] - strongDir);\r\n      keepProb = Math.max(0.02, 1 - (1 - keepProb) * (1 + asym * 1.6 * (1 - side)));\r\n    }\r\n    return rand01(seed, \"web:arcKeep\", j, a) < keepProb;\r\n  };\r\n  const angles = [];\r\n  for (let a = 0; a < N; a++) {\r\n    const base = dirRad - spread / 2 + (N > 1 ? a / (N - 1) * spread : 0);\r\n    const jit = (rand01(seed, \"web:angle\", a) - 0.5) * 2 * 0.15 * slot * o.rays.angleJitter;\r\n    angles.push(base + jit);\r\n  }\r\n  const weights = [];\r\n  {\r\n    let sum = 0;\r\n    for (let j = 1; j <= M; j++) {\r\n      const wj = o.rings.spacing === \"uniform\" ? 1 : Math.pow(1.35, j - 1);\r\n      weights.push(wj);\r\n      sum += wj;\r\n    }\r\n    for (let j = 0; j < M; j++) weights[j] /= sum;\r\n  }\r\n  const ringR = [0];\r\n  {\r\n    let cum = 0;\r\n    for (let j = 1; j <= M; j++) {\r\n      const jit = (rand01(seed, \"web:ringbase\", j) - 0.5) * 2 * 0.3 * o.rings.jitter;\r\n      cum += weights[j - 1] * (1 + jit);\r\n      ringR.push(cum);\r\n    }\r\n    for (let j = 0; j <= M; j++) ringR[j] = r0 + (R - r0) * ringR[j] / cum;\r\n  }\r\n  const radii = [];\r\n  for (let a = 0; a < N; a++) {\r\n    const row = [r0];\r\n    for (let j = 1; j <= M; j++) {\r\n      if (j === M) {\r\n        row.push(ringR[M]);\r\n        continue;\r\n      }\r\n      const gUp = ringR[j + 1] - ringR[j];\r\n      const gDn = ringR[j] - ringR[j - 1];\r\n      const bound = 0.4 * Math.max(1, Math.min(gUp, gDn));\r\n      const pert = (rand01(seed, \"web:ringpert\", a, j) - 0.5) * 2 * (0.3 + 0.7 * irr) * bound;\r\n      row.push(ringR[j] + pert);\r\n    }\r\n    radii.push(row);\r\n  }\r\n  const wobAmp = (0.05 * o.rays.waviness + 0.04 * irr) * slot;\r\n  const anchorAngle = (a, j) => angles[a] + fbm1D(hashCombine(seed, hashString(\"web:wob\"), a), j * 0.8, 2) * wobAmp;\r\n  const anchors = [];\r\n  for (let a = 0; a < N; a++) {\r\n    const row = [];\r\n    for (let j = 0; j <= M; j++) {\r\n      const ang = anchorAngle(a, j);\r\n      row.push([px + Math.cos(ang) * radii[a][j], py + Math.sin(ang) * radii[a][j]]);\r\n    }\r\n    anchors.push(row);\r\n  }\r\n  const rayEdges = /* @__PURE__ */ new Map();\r\n  for (let a = 0; a < N; a++) {\r\n    for (let j = 0; j < M; j++) {\r\n      const A = anchors[a][j];\r\n      const B = anchors[a][j + 1];\r\n      const mid = [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2];\r\n      const lateralGap = Math.min((radii[a][j] + radii[a][j + 1]) / 2 * slot, 0.12 * maxDim);\r\n      rayEdges.set(\r\n        `${a}:${j}`,\r\n        subdivideMidpoint([A, mid, B], o.edgeDetail, o.jaggedness * 0.5, hashCombine(seed, hashString(\"web:rayjag\"), a, j), 0.18 * lateralGap)\r\n      );\r\n    }\r\n  }\r\n  const ringEdges = /* @__PURE__ */ new Map();\r\n  for (let j = 0; j <= M; j++) {\r\n    for (let a = 0; a + 1 < N; a++) {\r\n      const b = a + 1;\r\n      const A = anchors[a][j];\r\n      const B = anchors[b][j];\r\n      const angA = anchorAngle(a, j);\r\n      const angB = anchorAngle(b, j);\r\n      const rA = radii[a][j];\r\n      const rB = radii[b][j];\r\n      const base = [A];\r\n      for (let s = 1; s <= 2; s++) {\r\n        const f = s / 3;\r\n        const ang = lerp(angA, angB, f);\r\n        const r = lerp(rA, rB, f);\r\n        base.push([px + Math.cos(ang) * r, py + Math.sin(ang) * r]);\r\n      }\r\n      base.push(B);\r\n      const gapDown = j > 0 ? Math.min(rA - radii[a][j - 1], rB - radii[b][j - 1]) : r0;\r\n      const gapUp = j < M ? Math.min(radii[a][j + 1] - rA, radii[b][j + 1] - rB) : gapDown;\r\n      const minGap = Math.max(1, Math.min(gapDown, gapUp, 0.1 * maxDim));\r\n      ringEdges.set(\r\n        `${j}:${a}`,\r\n        subdivideMidpoint(base, ringDetail, o.jaggedness * 0.22, hashCombine(seed, hashString(\"web:ringjag\"), j, a), 0.18 * minGap)\r\n      );\r\n    }\r\n  }\r\n  const raw = [];\r\n  for (let j = 0; j < M; j++) {\r\n    for (let aL = 0; aL + 1 < N; aL++) {\r\n      if (!keepArc(j, aL)) continue;\r\n      let j1 = j;\r\n      while (j1 + 1 < M && !keepArc(j1 + 1, aL)) j1++;\r\n      const aR = aL + 1;\r\n      const segs = [];\r\n      for (let jj = j; jj <= j1; jj++) segs.push(rayEdges.get(`${aL}:${jj}`));\r\n      segs.push(ringEdges.get(`${j1 + 1}:${aL}`));\r\n      for (let jj = j1; jj >= j; jj--) segs.push(reversePts(rayEdges.get(`${aR}:${jj}`)));\r\n      segs.push(reversePts(ringEdges.get(`${j}:${aL}`)));\r\n      const clipped = clipPolygonToRect(stitchLoop(segs), w, h);\r\n      if (clipped.length >= 3) raw.push({ poly: clipped, ring: j + 1 });\r\n    }\r\n  }\r\n  const ranks = shuffledRanks(seed, \"web:z\", raw.length);\r\n  const shards = [];\r\n  for (let i = 0; i < raw.length; i++) {\r\n    const s = makeShard(seed, i, raw[i].poly, raw[i].ring, ranks[i], o.seamOutsetPx);\r\n    if (s) shards.push(s);\r\n  }\r\n  const cracks = [];\r\n  const junctions = [];\r\n  let cid = 0;\r\n  for (let a = 0; a < N; a++) {\r\n    const pts = [];\r\n    for (let j = 0; j < M; j++) {\r\n      const e = rayEdges.get(`${a}:${j}`);\r\n      for (let k = j === 0 ? 0 : 1; k < e.length; k++) pts.push(e[k]);\r\n    }\r\n    const birth = rand01(seed, \"web:raybirth\", a) * 0.06;\r\n    cracks.push(makeCrack(seed, `wr${cid++}`, \"ray\", pts, birth, 0.42 + rand01(seed, \"web:raygrow\", a) * 0.12, true));\r\n  }\r\n  for (let j = 1; j < M; j++) {\r\n    let a = 0;\r\n    while (a + 1 < N) {\r\n      if (!keepArc(j, a)) {\r\n        a++;\r\n        continue;\r\n      }\r\n      const runStart = a;\r\n      const pts = [];\r\n      while (a + 1 < N && keepArc(j, a)) {\r\n        const e = ringEdges.get(`${j}:${a}`);\r\n        for (let k = a === runStart ? 0 : 1; k < e.length; k++) pts.push(e[k]);\r\n        a++;\r\n      }\r\n      if (pts.length >= 2) {\r\n        const birth = 0.08 + j / M * 0.2 + rand01(seed, \"web:ringbirth\", j, runStart) * 0.05;\r\n        cracks.push(makeCrack(seed, `wg${cid++}`, \"ring\", pts, Math.min(birth, 0.9), 0.28, false));\r\n      }\r\n    }\r\n  }\r\n  const stubScale = Math.sqrt(w * h / Math.max(1, shards.length));\r\n  return { shards, cracks, maxRing: M, junctions, stubScale };\r\n}\r\n\r\n// src/fracture/corners.ts\r\nfunction chordIsInterior(pts, ci, vi) {\r\n  const n = pts.length;\r\n  const A = pts[ci];\r\n  const B = pts[vi];\r\n  const mid = [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2];\r\n  if (!pointInPolygon(mid, pts)) return false;\r\n  for (let j = 0; j < n; j++) {\r\n    const k = (j + 1) % n;\r\n    if (j === ci || k === ci || j === vi || k === vi) continue;\r\n    const hit = segmentIntersection(pts[j], pts[k], A, B);\r\n    if (hit && hit.ta > 1e-6 && hit.ta < 1 - 1e-6 && hit.tb > 1e-6 && hit.tb < 1 - 1e-6) return false;\r\n  }\r\n  return true;\r\n}\r\nfunction applyCornerRelief(o, shards, cracks) {\r\n  if (o.corners === false || o.corners.relief <= 0 || o.mode === \"hero\") return;\r\n  const { width: w, height: h, seed } = o;\r\n  const relief = o.corners.relief;\r\n  const eps = 1e-3;\r\n  const onBorder = (a, b) => a[0] < eps && b[0] < eps || a[0] > w - eps && b[0] > w - eps || a[1] < eps && b[1] < eps || a[1] > h - eps && b[1] > h - eps;\r\n  const baseN = shards.length;\r\n  let maxZ = 0;\r\n  for (const s of shards) if (s.z > maxZ) maxZ = s.z;\r\n  let appended = 0;\r\n  let crackId = 0;\r\n  const corners = [\r\n    [0, 0],\r\n    [w, 0],\r\n    [w, h],\r\n    [0, h]\r\n  ];\r\n  for (let c = 0; c < corners.length; c++) {\r\n    const C = corners[c];\r\n    let ownerIdx = -1;\r\n    let owners = 0;\r\n    for (let si = 0; si < baseN; si++) {\r\n      const poly = shards[si].polygon;\r\n      for (let p = 0; p + 1 < poly.length; p += 2) {\r\n        if (Math.abs(poly[p] - C[0]) < eps && Math.abs(poly[p + 1] - C[1]) < eps) {\r\n          owners++;\r\n          ownerIdx = si;\r\n          break;\r\n        }\r\n      }\r\n      if (owners > 1) break;\r\n    }\r\n    if (owners !== 1) continue;\r\n    const owner = shards[ownerIdx];\r\n    if ((o.mode === \"radial\" || o.mode === \"web\") && owner.ringIndex === 0) continue;\r\n    const raw = unflattenPts(owner.polygon);\r\n    const n = raw.length;\r\n    if (n < 5) continue;\r\n    let ci0 = -1;\r\n    for (let i = 0; i < n; i++) {\r\n      if (Math.abs(raw[i][0] - C[0]) < eps && Math.abs(raw[i][1] - C[1]) < eps) {\r\n        ci0 = i;\r\n        break;\r\n      }\r\n    }\r\n    if (ci0 < 0) continue;\r\n    if (!onBorder(raw[(ci0 - 1 + n) % n], C) || !onBorder(C, raw[(ci0 + 1) % n])) continue;\r\n    const pts = [...raw.slice(ci0), ...raw.slice(0, ci0)];\r\n    const jit = 0.8 + 0.4 * rand01(seed, \"corner\", c);\r\n    const reach = relief * Math.min(0.55 * Math.sqrt(owner.area), 95) * jit;\r\n    const distC = (i) => Math.hypot(pts[i][0] - C[0], pts[i][1] - C[1]);\r\n    const mid = Math.floor(n / 2);\r\n    const pick = (lo, hi, step) => {\r\n      let best = -1;\r\n      let bestErr = Infinity;\r\n      for (let i = lo; step > 0 ? i <= hi : i >= hi; i += step) {\r\n        if (i <= 1 || i >= n - 1) continue;\r\n        if (distC(i) < 8) continue;\r\n        if (!chordIsInterior(pts, 0, i)) continue;\r\n        const err = Math.abs(distC(i) - reach);\r\n        if (err < bestErr) {\r\n          bestErr = err;\r\n          best = i;\r\n        }\r\n      }\r\n      return best;\r\n    };\r\n    const vF = pick(2, mid, 1);\r\n    const vB = pick(n - 2, mid + 1, -1);\r\n    const targets = [];\r\n    if (vF > 0) targets.push(vF);\r\n    if (vB > 0 && vB !== vF) targets.push(vB);\r\n    targets.sort((a, b) => a - b);\r\n    if (targets.length === 0) continue;\r\n    const pieces = [];\r\n    pieces.push(pts.slice(0, targets[0] + 1));\r\n    for (let k = 0; k + 1 < targets.length; k++) {\r\n      pieces.push([pts[0], ...pts.slice(targets[k], targets[k + 1] + 1)]);\r\n    }\r\n    pieces.push([pts[0], ...pts.slice(targets[targets.length - 1])]);\r\n    const built = [];\r\n    let ok = true;\r\n    for (let k = 0; k < pieces.length; k++) {\r\n      const idx = k === 0 ? ownerIdx : baseN + appended + (k - 1);\r\n      const z = k === 0 ? owner.z : maxZ + 1 + appended + (k - 1);\r\n      const sh = makeShard(seed, idx, pieces[k], owner.ringIndex, z, o.seamOutsetPx);\r\n      if (!sh) {\r\n        ok = false;\r\n        break;\r\n      }\r\n      built.push(sh);\r\n    }\r\n    if (!ok) continue;\r\n    shards[ownerIdx] = built[0];\r\n    for (let k = 1; k < built.length; k++) shards.push(built[k]);\r\n    appended += built.length - 1;\r\n    const birth = 0 + 0.06 * rand01(seed, \"corner:birth\", c);\r\n    for (const v of targets) {\r\n      cracks.push(makeCrack(seed, `cr${crackId++}`, \"split\", [C, pts[v]], birth, 0.32, false));\r\n    }\r\n  }\r\n}\r\n\r\n// src/fracture/stubs.ts\r\nvar MAX_TOTAL_STUBS = 200;\r\nfunction addStubCracks(o, cracks, junctions, localScale) {\r\n  if (o.stubs === false || o.stubs.maxPerCrack <= 0) return;\r\n  const { width: w, height: h, seed } = o;\r\n  const mainCount = cracks.length;\r\n  const recs = [];\r\n  const forkSeeds = [];\r\n  const tryStub = (parentIdx, sStation, keyA, keyB, group, freeAngle) => {\r\n    const c = cracks[parentIdx];\r\n    const rs = (field) => rand01(seed, `${group}:${field}`, keyA, keyB);\r\n    const pts = unflattenPts(c.points);\r\n    const p = pointAtLength(pts, c.cumLen, sStation);\r\n    if (p[0] < 2 || p[0] > w - 2 || p[1] < 2 || p[1] > h - 2) return;\r\n    const tan = tangentAtLength(pts, c.cumLen, sStation);\r\n    const side = rs(\"side\") < 0.5 ? 1 : -1;\r\n    const angDeg = freeAngle ? 25 + 55 * rs(\"ang\") : 20 + 35 * rs(\"ang\");\r\n    const ang = degToRad(angDeg) * side;\r\n    const cos = Math.cos(ang);\r\n    const sin = Math.sin(ang);\r\n    const dir = [tan[0] * cos - tan[1] * sin, tan[0] * sin + tan[1] * cos];\r\n    const sinA = Math.max(0.2, Math.abs(Math.sin(ang)));\r\n    const L = Math.min(45, (0.18 + 0.45 * rs(\"len\")) * localScale, 0.55 * localScale / sinA);\r\n    if (L < 4) return;\r\n    const arrival = c.birth + c.growDuration * (1 - Math.pow(Math.max(0, 1 - sStation / Math.max(1e-6, c.totalLen)), 0.25));\r\n    if (arrival > 0.965) return;\r\n    const grow = Math.min(0.04 + 0.04 * rs(\"grow\"), 0.985 - arrival);\r\n    if (grow < 0.02) return;\r\n    const curve = (rs(\"curve\") - 0.5) * 0.24 * L;\r\n    const perp = [-dir[1], dir[0]];\r\n    const base = [\r\n      p,\r\n      [p[0] + dir[0] * 0.55 * L + perp[0] * curve, p[1] + dir[1] * 0.55 * L + perp[1] * curve],\r\n      [p[0] + dir[0] * L, p[1] + dir[1] * L]\r\n    ];\r\n    const jag = subdivideMidpoint(\r\n      base,\r\n      Math.min(2, o.edgeDetail),\r\n      o.jaggedness * 0.4,\r\n      hashCombine(seed, hashString(`${group}:jag`), keyA, keyB),\r\n      0.15 * L\r\n    );\r\n    const rec = { parent: parentIdx, pts: jag, birth: arrival, grow };\r\n    recs.push(rec);\r\n    if (rs(\"fork\") < 0.25) forkSeeds.push({ rec, key: hashCombine(keyA, keyB) });\r\n  };\r\n  for (let ci = 0; ci < mainCount; ci++) {\r\n    const c = cracks[ci];\r\n    if (c.kind === \"crush\" || c.totalLen < 30) continue;\r\n    const n = Math.min(1 + Math.floor(rand01(seed, \"stub:n\", ci) * 3.2), o.stubs.maxPerCrack);\r\n    for (let k = 0; k < n; k++) {\r\n      const s = (0.08 + 0.84 * rand01(seed, \"stub:s\", ci, k)) * c.totalLen;\r\n      tryStub(ci, s, ci, k, \"stub\", false);\r\n    }\r\n  }\r\n  if (o.stubs.atJunctions) {\r\n    for (let ji = 0; ji < junctions.length; ji++) {\r\n      if (rand01(seed, \"stub:junction\", ji) >= 0.3) continue;\r\n      const jn = junctions[ji];\r\n      tryStub(jn.crackIndex, jn.s, 1000003 + ji, 0, \"stubj\", true);\r\n    }\r\n  }\r\n  const forkRecs = [];\r\n  for (let fi = 0; fi < forkSeeds.length; fi++) {\r\n    const { rec, key } = forkSeeds[fi];\r\n    const rf = (field) => rand01(seed, `stubf:${field}`, key, fi);\r\n    const cum = cumulativeLengths(rec.pts);\r\n    const total = cum[cum.length - 1];\r\n    const sF = 0.6 * total;\r\n    const p = pointAtLength(rec.pts, cum, sF);\r\n    const tan = tangentAtLength(rec.pts, cum, sF);\r\n    const side = rf(\"side\") < 0.5 ? 1 : -1;\r\n    const ang = degToRad(15 + 15 * rf(\"ang\")) * side;\r\n    const cos = Math.cos(ang);\r\n    const sin = Math.sin(ang);\r\n    const dir = [tan[0] * cos - tan[1] * sin, tan[0] * sin + tan[1] * cos];\r\n    const L = 0.4 * total;\r\n    if (L < 4) continue;\r\n    const forkBirth = rec.birth + rec.grow * (1 - Math.pow(1 - 0.6, 0.25));\r\n    const forkGrow = Math.min(0.03 + 0.03 * rf(\"grow\"), 0.985 - forkBirth);\r\n    if (forkGrow < 0.02) continue;\r\n    const base = [\r\n      p,\r\n      [p[0] + dir[0] * 0.5 * L, p[1] + dir[1] * 0.5 * L],\r\n      [p[0] + dir[0] * L, p[1] + dir[1] * L]\r\n    ];\r\n    const jag = subdivideMidpoint(\r\n      base,\r\n      Math.min(2, o.edgeDetail),\r\n      o.jaggedness * 0.4,\r\n      hashCombine(seed, hashString(\"stubf:jag\"), key, fi),\r\n      0.15 * L\r\n    );\r\n    forkRecs.push({ parent: rec.parent, pts: jag, birth: forkBirth, grow: forkGrow });\r\n  }\r\n  const all = [...recs, ...forkRecs].slice(0, MAX_TOTAL_STUBS);\r\n  for (let si = 0; si < all.length; si++) {\r\n    const r = all[si];\r\n    const crack = makeCrack(seed, `st${si}`, \"stub\", r.pts, r.birth, r.grow, false);\r\n    crack.parent = r.parent;\r\n    cracks.push(crack);\r\n  }\r\n}\r\n\r\n// src/fracture/micro.ts\r\nfunction seedMicroShards(o, allCracks, ringIndexAt) {\r\n  const cracks = allCracks.filter((c) => c.kind !== \"stub\");\r\n  if (o.microCount <= 0 || cracks.length === 0) return [];\r\n  const cumTotals = [];\r\n  let total = 0;\r\n  for (const c of cracks) {\r\n    total += c.totalLen;\r\n    cumTotals.push(total);\r\n  }\r\n  if (total <= 0) return [];\r\n  const out = [];\r\n  for (let i = 0; i < o.microCount; i++) {\r\n    const rng = rngFor(o.seed, \"micro\", i);\r\n    const pickLen = rng() * total;\r\n    let ci = 0;\r\n    while (ci < cumTotals.length - 1 && cumTotals[ci] <= pickLen) ci++;\r\n    const crack = cracks[ci];\r\n    const pts = unflattenPts(crack.points);\r\n    const s = rng() * crack.totalLen;\r\n    const p = pointAtLength(pts, crack.cumLen, s);\r\n    const offAng = rng() * TAU;\r\n    const offDist = 1 + rng() * 4;\r\n    const origin = [p[0] + Math.cos(offAng) * offDist, p[1] + Math.sin(offAng) * offDist];\r\n    const sizeT = Math.pow(rng(), 1.6);\r\n    const size = lerp(o.microSizeRange[0], o.microSizeRange[1], sizeT);\r\n    const nPts = rng() < 0.6 ? 3 : 4;\r\n    const baseAng = rng() * TAU;\r\n    const elong = 0.45 + rng() * 0.55;\r\n    const poly = [];\r\n    for (let k = 0; k < nPts; k++) {\r\n      const a = baseAng + k / nPts * TAU + (rng() - 0.5) * 0.9;\r\n      const r = size * (0.5 + rng() * 0.6);\r\n      poly.push(Math.cos(a) * r, Math.sin(a) * r * elong);\r\n    }\r\n    const birth = clamp01(crack.birth + s / Math.max(1e-6, crack.totalLen) * crack.growDuration);\r\n    out.push({\r\n      id: `m${i}`,\r\n      polygon: poly,\r\n      origin,\r\n      hash: hashCombine(o.seed, hashString(\"micro\"), i),\r\n      birth,\r\n      ringIndex: ringIndexAt(origin)\r\n    });\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/fracture/index.ts\r\nfunction generateFracture(opts) {\r\n  const o = resolveFractureOptions(opts);\r\n  let shards;\r\n  let cracks;\r\n  let junctions;\r\n  let stubScale;\r\n  let ringIndexAt;\r\n  if (o.mode === \"title\") {\r\n    const res = generateTitleFracture(o);\r\n    shards = res.shards;\r\n    cracks = res.cracks;\r\n    junctions = res.junctions;\r\n    stubScale = res.stubScale;\r\n    const bandH = o.height / Math.max(1, Math.max(...shards.map((s) => s.ringIndex)) + 1);\r\n    ringIndexAt = (origin) => Math.max(0, Math.min(Math.floor(origin[1] / bandH), 64));\r\n  } else if (o.mode === \"hero\") {\r\n    const res = generateHeroFracture(o);\r\n    shards = res.shards;\r\n    cracks = res.cracks;\r\n    junctions = res.junctions;\r\n    stubScale = res.stubScale;\r\n    ringIndexAt = () => 0;\r\n  } else if (o.mode === \"web\") {\r\n    const res = generateWebFracture(o);\r\n    shards = res.shards;\r\n    cracks = res.cracks;\r\n    junctions = res.junctions;\r\n    stubScale = res.stubScale;\r\n    const [Px, Py] = webOrigin(o.width, o.height, o.web.dir, o.web.distance);\r\n    let dMin = Infinity;\r\n    let dMax = 0;\r\n    for (const c of [[0, 0], [o.width, 0], [o.width, o.height], [0, o.height]]) {\r\n      const d = Math.hypot(c[0] - Px, c[1] - Py);\r\n      if (d < dMin) dMin = d;\r\n      if (d > dMax) dMax = d;\r\n    }\r\n    ringIndexAt = (origin) => {\r\n      const d = Math.hypot(origin[0] - Px, origin[1] - Py);\r\n      return Math.max(0, Math.min(Math.round((d - dMin) / Math.max(1, dMax - dMin) * (res.maxRing + 1)), res.maxRing + 1));\r\n    };\r\n  } else if (o.mode === \"collapse\") {\r\n    const res = generateCollapseFracture(o);\r\n    shards = res.shards;\r\n    cracks = res.cracks;\r\n    junctions = res.junctions;\r\n    stubScale = res.stubScale;\r\n    const rowH = o.height / Math.max(1, res.rows);\r\n    ringIndexAt = (origin) => Math.max(0, Math.min(Math.floor((o.height - origin[1]) / rowH), res.rows - 1));\r\n  } else {\r\n    const res = generateRadialFracture(o);\r\n    shards = res.shards;\r\n    cracks = res.cracks;\r\n    junctions = res.junctions;\r\n    stubScale = res.stubScale;\r\n    const R = Math.hypot(Math.max(o.impact[0], o.width - o.impact[0]), Math.max(o.impact[1], o.height - o.impact[1]));\r\n    ringIndexAt = (origin) => {\r\n      const d = Math.hypot(origin[0] - o.impact[0], origin[1] - o.impact[1]);\r\n      return Math.max(0, Math.min(Math.round(d / Math.max(1, R) * (res.maxRing + 1)), res.maxRing + 1));\r\n    };\r\n  }\r\n  applyCornerRelief(o, shards, cracks);\r\n  addStubCracks(o, cracks, junctions, stubScale);\r\n  const micro = seedMicroShards(o, cracks, ringIndexAt);\r\n  const pattern = {\r\n    version: 7,\r\n    mode: o.mode,\r\n    width: o.width,\r\n    height: o.height,\r\n    seed: o.seed,\r\n    instanceId: o.instanceId,\r\n    impact: o.impact,\r\n    shards,\r\n    cracks,\r\n    micro\r\n  };\r\n  return deepFreeze(pattern);\r\n}\r\nfunction deepFreeze(obj) {\r\n  if (obj && typeof obj === \"object\" && !Object.isFrozen(obj)) {\r\n    Object.freeze(obj);\r\n    for (const key of Object.keys(obj)) {\r\n      deepFreeze(obj[key]);\r\n    }\r\n  }\r\n  return obj;\r\n}\r\n\r\n// src/motion/timeline.ts\r\nfunction resolvePhase(t, tl) {\r\n  const crackProgress = tl.crackEnd <= tl.crackStart ? t >= tl.crackEnd ? 1 : 0 : clamp01((t - tl.crackStart) / (tl.crackEnd - tl.crackStart));\r\n  const shattering = Number.isFinite(tl.shatterStart) && t >= tl.shatterStart;\r\n  const shatterTau = shattering ? t - tl.shatterStart : 0;\r\n  let phase;\r\n  if (shattering) phase = \"shattering\";\r\n  else if (crackProgress <= 0) phase = \"intact\";\r\n  else if (crackProgress < 1) phase = \"cracking\";\r\n  else phase = \"cracked\";\r\n  return { phase, crackProgress, shatterTau };\r\n}\r\nvar staticCrackedTimeline = {\r\n  crackStart: -1,\r\n  crackEnd: 0,\r\n  shatterStart: Infinity\r\n};\r\n\r\n// src/motion/kinematics.ts\r\nfunction classifyOutlier(hash, outliers) {\r\n  const r = hashTo01(hashCombine(hash, hashString(\"outlier\")));\r\n  if (r < outliers.dropFraction) return \"dropped\";\r\n  if (r < outliers.dropFraction + outliers.slipFraction) return \"slipped\";\r\n  if (r < outliers.dropFraction + outliers.slipFraction + outliers.rebelFraction) return \"rebel\";\r\n  return \"none\";\r\n}\r\nfunction assignOutliers(pattern, outliers) {\r\n  const kinds = [];\r\n  let hasException = false;\r\n  for (const s of pattern.shards) {\r\n    if (pattern.mode === \"radial\" && s.ringIndex === 0) {\r\n      kinds.push(\"none\");\r\n      continue;\r\n    }\r\n    const k = classifyOutlier(s.hash, outliers);\r\n    if (k !== \"none\") hasException = true;\r\n    kinds.push(k);\r\n  }\r\n  if (outliers.dropFraction > 0) {\r\n    const maxDrop = Math.max(1, Math.ceil(pattern.shards.length * outliers.dropFraction));\r\n    const dropped = [];\r\n    for (let i = 0; i < kinds.length; i++) {\r\n      if (kinds[i] === \"dropped\") {\r\n        dropped.push({ i, r: hashTo01(hashCombine(pattern.shards[i].hash, hashString(\"outlier\"))) });\r\n      }\r\n    }\r\n    if (dropped.length > maxDrop) {\r\n      dropped.sort((a, b) => b.r - a.r || a.i - b.i);\r\n      for (let k = 0; k < dropped.length - maxDrop; k++) kinds[dropped[k].i] = \"slipped\";\r\n    }\r\n  }\r\n  if (!hasException && pattern.mode !== \"hero\" && // free-floating heroes get no forced exception\r\n  pattern.shards.length <= 12 && outliers.slipFraction + outliers.rebelFraction > 0) {\r\n    let best = -1;\r\n    let bestR = Infinity;\r\n    for (let i = 0; i < pattern.shards.length; i++) {\r\n      if (kinds[i] !== \"none\") continue;\r\n      if (pattern.mode === \"radial\" && pattern.shards[i].ringIndex === 0) continue;\r\n      const r = hashTo01(hashCombine(pattern.shards[i].hash, hashString(\"outlier\")));\r\n      if (r < bestR) {\r\n        bestR = r;\r\n        best = i;\r\n      }\r\n    }\r\n    if (best >= 0) kinds[best] = \"slipped\";\r\n  }\r\n  return kinds;\r\n}\r\nfunction flightOffset(tau, v0, g, drag) {\r\n  if (tau <= 0) return [0, 0];\r\n  if (drag <= 1e-6) {\r\n    return [v0[0] * tau + 0.5 * g[0] * tau * tau, v0[1] * tau + 0.5 * g[1] * tau * tau];\r\n  }\r\n  const e = Math.exp(-drag * tau);\r\n  const a = (1 - e) / drag;\r\n  const b = (drag * tau - 1 + e) / (drag * drag);\r\n  return [v0[0] * a + g[0] * b, v0[1] * a + g[1] * b];\r\n}\r\nfunction flightSpeed(tau, v0, g, drag) {\r\n  if (tau < 0) tau = 0;\r\n  let vx;\r\n  let vy;\r\n  if (drag <= 1e-6) {\r\n    vx = v0[0] + g[0] * tau;\r\n    vy = v0[1] + g[1] * tau;\r\n  } else {\r\n    const e = Math.exp(-drag * tau);\r\n    vx = v0[0] * e + g[0] * (1 - e) / drag;\r\n    vy = v0[1] * e + g[1] * (1 - e) / drag;\r\n  }\r\n  return Math.hypot(vx, vy);\r\n}\r\nfunction shardMotion(pattern, shard, fx, kind = \"none\") {\r\n  const rng = rngFor(shard.hash, \"motion\");\r\n  const sh = fx.shatter;\r\n  const jitter = (amount) => 1 + (rng() * 2 - 1) * sh.jitter * amount;\r\n  let dirX;\r\n  let dirY;\r\n  let speed;\r\n  if (typeof sh.direction === \"number\") {\r\n    const a = degToRad(sh.direction);\r\n    dirX = Math.cos(a);\r\n    dirY = Math.sin(a);\r\n    speed = sh.speed * jitter(0.5);\r\n  } else if (pattern.mode === \"radial\" || pattern.mode === \"web\") {\r\n    const dx = shard.centroid[0] - pattern.impact[0];\r\n    const dy = shard.centroid[1] - pattern.impact[1];\r\n    const d = Math.hypot(dx, dy) || 1;\r\n    const scatter = (rng() * 2 - 1) * 0.22;\r\n    const cos = Math.cos(scatter);\r\n    const sin = Math.sin(scatter);\r\n    dirX = dx / d * cos - dy / d * sin;\r\n    dirY = dx / d * sin + dy / d * cos;\r\n    speed = sh.speed * jitter(0.6) / (1 + 0.35 * shard.ringIndex);\r\n  } else if (pattern.mode === \"collapse\") {\r\n    dirX = (rng() * 2 - 1) * 0.18;\r\n    dirY = 0.15 + rng() * 0.25;\r\n    const n = Math.hypot(dirX, dirY) || 1;\r\n    dirX /= n;\r\n    dirY /= n;\r\n    speed = sh.speed * 0.18 * jitter(0.6);\r\n  } else {\r\n    const side = shard.centroid[0] < pattern.width / 2 ? -1 : 1;\r\n    if (sh.spread === \"apart\") {\r\n      dirX = side * (0.85 + rng() * 0.15);\r\n      dirY = 0.12 + rng() * 0.18;\r\n    } else if (sh.spread === \"slide\") {\r\n      dirX = side * (0.5 + rng() * 0.25);\r\n      dirY = 0.35 + rng() * 0.3;\r\n    } else {\r\n      dirX = (rng() * 2 - 1) * 0.35;\r\n      dirY = 0.45 + rng() * 0.4;\r\n    }\r\n    const n = Math.hypot(dirX, dirY) || 1;\r\n    dirX /= n;\r\n    dirY /= n;\r\n    speed = sh.speed * 0.55 * jitter(0.6);\r\n  }\r\n  const sizeFactor = 1 / Math.max(0.45, Math.pow(shard.area / 12e3, 0.25));\r\n  const omega = (rng() * 2 - 1) * sh.spinDegMax * sizeFactor;\r\n  const tumbleX = (rng() * 2 - 1) * sh.tumbleDegMax * sizeFactor;\r\n  const tumbleY = (rng() * 2 - 1) * sh.tumbleDegMax * sizeFactor;\r\n  let birthDelay = shard.ringIndex * sh.staggerPerRing * jitter(1);\r\n  if (pattern.mode === \"collapse\") {\r\n    birthDelay += rng() * sh.staggerPerRing * 1.5;\r\n  }\r\n  let vx = dirX * speed;\r\n  let vy = dirY * speed;\r\n  let omegaOut = omega;\r\n  if (kind === \"rebel\") {\r\n    const rr = rngFor(shard.hash, \"rebel\");\r\n    const ang = (rr() * 2 - 1) * 0.6;\r\n    const cos = Math.cos(ang);\r\n    const sin = Math.sin(ang);\r\n    const rvx = (vx * cos - vy * sin) * 1.35;\r\n    const rvy = (vx * sin + vy * cos) * 1.35;\r\n    vx = rvx;\r\n    vy = rvy;\r\n    omegaOut = omega * 1.6;\r\n    birthDelay = rr() < 0.5 ? birthDelay * 0.15 : birthDelay * 1.8 + 0.05;\r\n  }\r\n  return { v0: [vx, vy], omega: omegaOut, tumbleX, tumbleY, birthDelay };\r\n}\r\nfunction microMotion(pattern, origin, hash, ringIndex, fx) {\r\n  const rng = rngFor(hash, \"micromotion\");\r\n  const sh = fx.shatter;\r\n  let dirX;\r\n  let dirY;\r\n  if (pattern.mode === \"radial\" || pattern.mode === \"web\") {\r\n    const dx = origin[0] - pattern.impact[0];\r\n    const dy = origin[1] - pattern.impact[1];\r\n    const d = Math.hypot(dx, dy) || 1;\r\n    const scatter = (rng() * 2 - 1) * 0.5;\r\n    const cos = Math.cos(scatter);\r\n    const sin = Math.sin(scatter);\r\n    dirX = dx / d * cos - dy / d * sin;\r\n    dirY = dx / d * sin + dy / d * cos;\r\n  } else if (pattern.mode === \"collapse\") {\r\n    dirX = (rng() * 2 - 1) * 0.3;\r\n    dirY = 0.5 + rng() * 0.5;\r\n    const n = Math.hypot(dirX, dirY) || 1;\r\n    dirX /= n;\r\n    dirY /= n;\r\n  } else if (fx.shatter.spread !== \"fall\") {\r\n    const side = origin[0] < pattern.width / 2 ? -1 : 1;\r\n    const lateral = fx.shatter.spread === \"apart\" ? 0.8 : 0.5;\r\n    dirX = side * lateral + (rng() * 2 - 1) * 0.2;\r\n    dirY = (fx.shatter.spread === \"apart\" ? 0.2 : 0.5) + rng() * 0.4;\r\n    const n = Math.hypot(dirX, dirY) || 1;\r\n    dirX /= n;\r\n    dirY /= n;\r\n  } else {\r\n    const a = rng() * TAU;\r\n    dirX = Math.cos(a) * 0.4;\r\n    dirY = 0.6 + rng() * 0.5;\r\n    const n = Math.hypot(dirX, dirY) || 1;\r\n    dirX /= n;\r\n    dirY /= n;\r\n  }\r\n  const speed = sh.speed * fx.micro.speedScale * (0.35 + rng() * 0.75) / (1 + 0.25 * ringIndex);\r\n  const omega = (rng() * 2 - 1) * sh.spinDegMax * 3;\r\n  const birthDelay = ringIndex * sh.staggerPerRing * (0.6 + rng() * 0.8);\r\n  return { v0: [dirX * speed, dirY * speed], omega, tumbleX: 0, tumbleY: 0, birthDelay };\r\n}\r\n\r\n// src/render/params.ts\r\nvar defaultEffectParams = {\r\n  quality: \"normal\",\r\n  // glass cracks fast, holds (the money frame), then the pieces fall\r\n  timeline: { crackStart: 0.02, crackEnd: 0.2, shatterStart: 0.32 },\r\n  medium: \"content\",\r\n  refraction: { offsetPx: 6, rotateDeg: 0.9, scaleAmp: 0.016, tiltDeg: 1.6, perspectivePx: 800 },\r\n  optics: { brightnessAmp: 0.13, contrastAmp: 0.07, blurPx: 0.5, lightAngleDeg: -60, grainOpacity: 0.06, trackLight: false },\r\n  chroma: {\r\n    mode: \"shadow\",\r\n    offsetPx: 2.6,\r\n    angleDeg: 14,\r\n    opacity: 0.5,\r\n    blendMode: \"screen\",\r\n    colorA: \"rgba(255,42,84,0.55)\",\r\n    colorB: \"rgba(0,212,255,0.55)\"\r\n  },\r\n  facet: {\r\n    strength: 0.55,\r\n    tint: \"rgba(185,215,255,0.05)\",\r\n    opacity: 0.55,\r\n    blendMode: \"overlay\"\r\n  },\r\n  crackStyle: {\r\n    coreColor: \"rgba(255,255,255,0.92)\",\r\n    coreWidth: 1.1,\r\n    shadowColor: \"rgba(0,0,0,0.5)\",\r\n    shadowWidth: 2.4,\r\n    shadowOffsetPx: 1.4,\r\n    widthVariance: 0.6,\r\n    doubleEdge: 0.5,\r\n    subCracks: 0.6,\r\n    brightnessVar: 0.65,\r\n    hackleDensity: 0.55,\r\n    sparkle: true,\r\n    growth: \"snap\",\r\n    blendMode: \"multiply\"\r\n  },\r\n  bevel: {\r\n    widthPx: 1.5,\r\n    intensity: 0.7,\r\n    glintStrength: 0.6,\r\n    lightColor: \"rgba(255,255,255,0.95)\",\r\n    darkColor: \"rgba(8,12,18,0.75)\",\r\n    blendMode: \"screen\",\r\n    scatter: 0.65,\r\n    facetVariation: 0.6\r\n  },\r\n  crush: { punch: true, scaleTo: 0.9 },\r\n  outliers: {\r\n    dropFraction: 0.05,\r\n    slipFraction: 0.09,\r\n    rebelFraction: 0.12,\r\n    slipPx: 10,\r\n    slipRotDeg: 2.5\r\n  },\r\n  spectrum: {\r\n    count: 2,\r\n    opacity: 0.3,\r\n    bandWidth: 0.6,\r\n    blendMode: \"screen\",\r\n    edgeOnly: 0\r\n  },\r\n  edgeDistortion: { widthPx: 10, strength: 0, blurPx: 0.6 },\r\n  shatter: {\r\n    speed: 950,\r\n    gravity: [0, 1250],\r\n    drag: 1.6,\r\n    spinDegMax: 170,\r\n    tumbleDegMax: 70,\r\n    staggerPerRing: 0.035,\r\n    jitter: 0.4,\r\n    fadeOut: [0.86, 1],\r\n    spread: \"fall\",\r\n    preSpreadPx: 0\r\n  },\r\n  motionBlur: { dt: 0.012, opacityFalloff: 0.5, speedThreshold: 140, smearPx: 14, smearBlurPx: 1.2 },\r\n  micro: { opacity: 0.85, speedScale: 1.5, fill: \"rgba(255,255,255,0.85)\", fillAlt: \"rgba(20,26,34,0.6)\" },\r\n  settle: { amplitudePx: 0, frequency: 2.2 },\r\n  float: { bobPx: 10, swayPx: 6, rotDeg: 2.5, cycles: 1 }\r\n};\r\nfunction normalizeEffectParams(fx) {\r\n  const d = defaultEffectParams;\r\n  if (!fx) return d;\r\n  return {\r\n    quality: fx.quality ?? d.quality,\r\n    timeline: { ...d.timeline, ...fx.timeline },\r\n    medium: fx.medium ?? d.medium,\r\n    refraction: { ...d.refraction, ...fx.refraction },\r\n    optics: { ...d.optics, ...fx.optics },\r\n    chroma: { ...d.chroma, ...fx.chroma },\r\n    facet: { ...d.facet, ...fx.facet },\r\n    crackStyle: { ...d.crackStyle, ...fx.crackStyle },\r\n    bevel: { ...d.bevel, ...fx.bevel },\r\n    crush: { ...d.crush, ...fx.crush },\r\n    outliers: { ...d.outliers, ...fx.outliers },\r\n    spectrum: { ...d.spectrum, ...fx.spectrum },\r\n    edgeDistortion: { ...d.edgeDistortion, ...fx.edgeDistortion },\r\n    shatter: { ...d.shatter, ...fx.shatter },\r\n    motionBlur: { ...d.motionBlur, ...fx.motionBlur },\r\n    micro: { ...d.micro, ...fx.micro },\r\n    settle: { ...d.settle, ...fx.settle },\r\n    float: { ...d.float, ...fx.float }\r\n  };\r\n}\r\n\r\n// src/render/quality.ts\r\nvar qualityPresets = {\r\n  draft: { chromaGhosts: 0, motionGhosts: 0, smearGhosts: 0, bevel: false, edgeDistortion: false, spectrum: false, stubCap: 0, maxBlurPx: 0, grain: false, microShardCap: 40 },\r\n  normal: { chromaGhosts: 2, motionGhosts: 0, smearGhosts: 2, bevel: true, edgeDistortion: true, spectrum: true, stubCap: 80, maxBlurPx: 2, grain: false, microShardCap: 140 },\r\n  high: { chromaGhosts: 2, motionGhosts: 0, smearGhosts: 3, bevel: true, edgeDistortion: true, spectrum: true, stubCap: 200, maxBlurPx: 4, grain: true, microShardCap: 400 }\r\n};\r\nfunction resolveQuality(q) {\r\n  if (q === void 0) return qualityPresets.normal;\r\n  if (typeof q === \"string\") return qualityPresets[q] ?? qualityPresets.normal;\r\n  return { ...qualityPresets.normal, ...q };\r\n}\r\n\r\n// src/render/cracks.ts\r\nfunction crackLayer(pattern, info, fx, q) {\r\n  const style = fx.crackStyle;\r\n  const growthVis = (local) => {\r\n    if (style.growth === \"quart\") return easeOutQuart(local);\r\n    if (style.growth === \"expo\") return easeOutExpo(local);\r\n    return local >= 0.4 ? 1 : easeOutQuart(local / 0.4);\r\n  };\r\n  const shAng = degToRad(fx.optics.lightAngleDeg + 180);\r\n  const sox = Math.cos(shAng) * style.shadowOffsetPx;\r\n  const soy = Math.sin(shAng) * style.shadowOffsetPx;\r\n  const shadowRatio = style.shadowWidth / Math.max(1e-3, style.coreWidth);\r\n  const bv = clamp01(style.brightnessVar);\r\n  let core = \"\";\r\n  let coreDim = \"\";\r\n  let stubD = \"\";\r\n  let shadow = \"\";\r\n  let highlight = \"\";\r\n  let hackle = \"\";\r\n  let stubOrdinal = -1;\r\n  for (let ci = 0; ci < pattern.cracks.length; ci++) {\r\n    const crack = pattern.cracks[ci];\r\n    if (crack.kind === \"stub\") {\r\n      stubOrdinal++;\r\n      if (stubOrdinal >= q.stubCap || style.subCracks <= 0) continue;\r\n      if (hashTo01(hashCombine(pattern.seed, hashString(\"stub:pick\"), stubOrdinal)) >= style.subCracks) continue;\r\n      const local2 = clamp01((info.crackProgress - crack.birth) / Math.max(1e-6, crack.growDuration));\r\n      if (local2 <= 0) continue;\r\n      const vis2 = growthVis(local2) * crack.totalLen;\r\n      const part2 = partialPolylineWithS(unflattenPts(crack.points), crack.cumLen, vis2);\r\n      if (part2.pts.length < 2) continue;\r\n      const frame2 = miterFrame(part2.pts);\r\n      const wSeed2 = hashCombine(pattern.seed, hashString(\"stubribbon\"), stubOrdinal);\r\n      const halfs = [];\r\n      for (let i = 0; i < part2.pts.length; i++) {\r\n        const profile = clamp(1 + style.widthVariance * 1.25 * fbm1D(wSeed2, part2.s[i] * 0.06), 0.35, 2.2);\r\n        const taper = Math.min(1, (vis2 - part2.s[i]) / 9);\r\n        const base = 0.5 * style.coreWidth * 0.4 * profile * Math.max(0, taper);\r\n        halfs.push(Math.min(base, frame2.maxHalf[i]));\r\n      }\r\n      stubD += ribbonPath(part2.pts, frame2, halfs, 0, 0);\r\n      continue;\r\n    }\r\n    const local = clamp01((info.crackProgress - crack.birth) / Math.max(1e-6, crack.growDuration));\r\n    if (local <= 0) continue;\r\n    const vis = growthVis(local) * crack.totalLen;\r\n    const all = unflattenPts(crack.points);\r\n    const part = partialPolylineWithS(all, crack.cumLen, vis);\r\n    if (part.pts.length < 2) continue;\r\n    let brightAt = null;\r\n    if (bv > 0) {\r\n      const rngD = rngFor(pattern.seed, \"coreDim\", ci);\r\n      const kB = 2 + Math.floor(3 * rngD());\r\n      const bounds = [];\r\n      for (let b = 0; b < kB; b++) bounds.push(rngD() * crack.totalLen);\r\n      bounds.sort((a, b) => a - b);\r\n      brightAt = (s) => {\r\n        if (s < 15) return 1;\r\n        let idx = 0;\r\n        while (idx < bounds.length && bounds[idx] <= s) idx++;\r\n        const inBright = idx % 2 === 0;\r\n        const dPrev = idx > 0 ? s - bounds[idx - 1] : Infinity;\r\n        const dNext = idx < bounds.length ? bounds[idx] - s : Infinity;\r\n        const d = Math.min(dPrev, dNext);\r\n        if (d >= 3) return inBright ? 1 : 0;\r\n        const f = 0.5 + d / 3 * 0.5;\r\n        return inBright ? f : 1 - f;\r\n      };\r\n      const stations = [];\r\n      for (const b of bounds) stations.push(b - 3, b, b + 3);\r\n      insertStations(part, stations.sort((a, b) => a - b), vis);\r\n    }\r\n    const frame = miterFrame(part.pts);\r\n    const wSeed = hashCombine(pattern.seed, hashString(\"ribbon\"), ci);\r\n    const halfCore = [];\r\n    const halfDim = [];\r\n    const halfShadow = [];\r\n    for (let i = 0; i < part.pts.length; i++) {\r\n      const profile = clamp(1 + style.widthVariance * 1.25 * fbm1D(wSeed, part.s[i] * 0.06), 0.35, 2.2);\r\n      const taper = Math.min(1, (vis - part.s[i]) / 9);\r\n      const base = 0.5 * style.coreWidth * profile * Math.max(0, taper);\r\n      const bright = brightAt ? brightAt(part.s[i]) : 1;\r\n      halfCore.push(Math.min(base * bright, frame.maxHalf[i]));\r\n      if (brightAt) halfDim.push(Math.min(base, frame.maxHalf[i]));\r\n      halfShadow.push(Math.min(base * shadowRatio, frame.maxHalf[i] * 1.6));\r\n    }\r\n    core += ribbonPath(part.pts, frame, halfCore, 0, 0);\r\n    if (brightAt) coreDim += ribbonPath(part.pts, frame, halfDim, 0, 0);\r\n    shadow += ribbonPath(part.pts, frame, halfShadow, sox, soy);\r\n    if (style.doubleEdge > 0) {\r\n      const nF = rand01(pattern.seed, \"fil\", ci) < style.doubleEdge ? rand01(pattern.seed, \"fil2\", ci) < 0.4 ? 2 : 1 : 0;\r\n      for (let k = 0; k < nF; k++) {\r\n        const r1 = rand01(pattern.seed, \"filA\", ci, k);\r\n        const r2 = rand01(pattern.seed, \"filB\", ci, k);\r\n        const r3 = rand01(pattern.seed, \"filC\", ci, k);\r\n        const r4 = rand01(pattern.seed, \"filD\", ci, k);\r\n        const s0 = r1 * crack.totalLen * 0.7;\r\n        const sEnd = Math.min(s0 + (0.12 + 0.18 * r2) * crack.totalLen, crack.totalLen, vis);\r\n        if (sEnd - s0 < 6) continue;\r\n        const side = r3 < 0.5 ? 1 : -1;\r\n        const dist = 1.5 + 1.5 * r4;\r\n        let d = \"\";\r\n        let started = false;\r\n        for (let i = 0; i < part.pts.length; i++) {\r\n          if (part.s[i] < s0 || part.s[i] > sEnd) {\r\n            started = false;\r\n            continue;\r\n          }\r\n          const x = part.pts[i][0] + frame.mx[i] * frame.scale[i] * side * dist;\r\n          const y = part.pts[i][1] + frame.my[i] * frame.scale[i] * side * dist;\r\n          d += `${started ? \"L\" : \"M\"}${fmt(x)} ${fmt(y)}`;\r\n          started = true;\r\n        }\r\n        if (d.indexOf(\"L\") >= 0) highlight += d;\r\n      }\r\n    }\r\n    if (style.hackleDensity > 0) {\r\n      const ticks = crack.ticks;\r\n      for (let ti = 0, j = 0; ti + 4 < ticks.length; ti += 5, j++) {\r\n        if (ticks[ti] > vis) continue;\r\n        if (hashTo01(hashCombine(ci, j)) >= style.hackleDensity) continue;\r\n        hackle += `M${fmt(ticks[ti + 1])} ${fmt(ticks[ti + 2])}L${fmt(ticks[ti + 3])} ${fmt(ticks[ti + 4])}`;\r\n      }\r\n    }\r\n  }\r\n  let fadeWindow = 0.07;\r\n  if (fx.medium === \"glass\") {\r\n    let maxRing = 0;\r\n    for (const s of pattern.shards) if (s.ringIndex > maxRing) maxRing = s.ringIndex;\r\n    fadeWindow = maxRing * fx.shatter.staggerPerRing * 1.4 + 0.07;\r\n  }\r\n  const opacity = info.phase === \"shattering\" ? clamp01(1 - info.shatterTau / fadeWindow) : 1;\r\n  let sparkle = null;\r\n  if (style.sparkle && pattern.mode === \"radial\") {\r\n    const [ix, iy] = pattern.impact;\r\n    const rMax = Math.max(\r\n      Math.hypot(ix, iy),\r\n      Math.hypot(pattern.width - ix, iy),\r\n      Math.hypot(ix, pattern.height - iy),\r\n      Math.hypot(pattern.width - ix, pattern.height - iy)\r\n    );\r\n    const punch = fx.crush.punch ? easeOutCubic(clamp01((info.crackProgress - 0.1) * 2.2)) : 0;\r\n    sparkle = {\r\n      cx: ix,\r\n      cy: iy,\r\n      r: rMax * 0.045 * (0.5 + 0.5 * easeOutCubic(info.crackProgress)),\r\n      opacity: easeOutCubic(info.crackProgress) * 0.9 * (1 - punch)\r\n    };\r\n  }\r\n  return {\r\n    corePath: core,\r\n    coreDimPath: coreDim,\r\n    stubPath: stubD,\r\n    shadowPath: shadow,\r\n    highlightPath: highlight,\r\n    hacklePath: hackle,\r\n    coreDimOpacity: Math.max(0.35, 1 - 0.65 * bv),\r\n    coreOpacity: bv > 0 ? Math.min(1, bv / 0.25) : 1,\r\n    opacity,\r\n    style,\r\n    sparkle\r\n  };\r\n}\r\nfunction partialPolylineWithS(pts, cum, vis) {\r\n  const total = cum[cum.length - 1];\r\n  if (vis <= 0) return { pts: [], s: [] };\r\n  if (vis >= total) return { pts: pts.slice(), s: cum.slice() };\r\n  const outP = [pts[0]];\r\n  const outS = [0];\r\n  for (let i = 1; i < pts.length; i++) {\r\n    if (cum[i] <= vis) {\r\n      outP.push(pts[i]);\r\n      outS.push(cum[i]);\r\n    } else {\r\n      const segLen = cum[i] - cum[i - 1];\r\n      const t = segLen > 1e-12 ? (vis - cum[i - 1]) / segLen : 0;\r\n      outP.push([\r\n        pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * t,\r\n        pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * t\r\n      ]);\r\n      outS.push(vis);\r\n      break;\r\n    }\r\n  }\r\n  return { pts: outP, s: outS };\r\n}\r\nfunction insertStations(part, stations, vis) {\r\n  for (const st of stations) {\r\n    if (st <= 0.05 || st >= Math.min(vis, part.s[part.s.length - 1]) - 0.05) continue;\r\n    let i = 0;\r\n    while (i < part.s.length && part.s[i] <= st) i++;\r\n    if (i === 0 || i >= part.s.length) continue;\r\n    if (st - part.s[i - 1] < 0.05 || part.s[i] - st < 0.05) continue;\r\n    const t = (st - part.s[i - 1]) / (part.s[i] - part.s[i - 1]);\r\n    part.pts.splice(i, 0, [\r\n      part.pts[i - 1][0] + (part.pts[i][0] - part.pts[i - 1][0]) * t,\r\n      part.pts[i - 1][1] + (part.pts[i][1] - part.pts[i - 1][1]) * t\r\n    ]);\r\n    part.s.splice(i, 0, st);\r\n  }\r\n}\r\nfunction miterFrame(pts) {\r\n  const n = pts.length;\r\n  const segNx = [];\r\n  const segNy = [];\r\n  const segL = [];\r\n  for (let i = 0; i < n - 1; i++) {\r\n    const dx = pts[i + 1][0] - pts[i][0];\r\n    const dy = pts[i + 1][1] - pts[i][1];\r\n    const l = Math.hypot(dx, dy) || 1e-9;\r\n    segNx.push(-dy / l);\r\n    segNy.push(dx / l);\r\n    segL.push(l);\r\n  }\r\n  const mx = [];\r\n  const my = [];\r\n  const scale = [];\r\n  const maxHalf = [];\r\n  for (let i = 0; i < n; i++) {\r\n    const a = i === 0 ? 0 : i - 1;\r\n    const b = i === n - 1 ? n - 2 : i;\r\n    let x = segNx[a] + segNx[b];\r\n    let y = segNy[a] + segNy[b];\r\n    const l = Math.hypot(x, y);\r\n    if (l < 1e-9) {\r\n      x = segNx[b];\r\n      y = segNy[b];\r\n    } else {\r\n      x /= l;\r\n      y /= l;\r\n    }\r\n    const dot = x * segNx[b] + y * segNy[b];\r\n    mx.push(x);\r\n    my.push(y);\r\n    scale.push(Math.min(2, 1 / Math.max(0.35, dot)));\r\n    maxHalf.push(0.4 * Math.min(segL[a], segL[b]));\r\n  }\r\n  return { mx, my, scale, maxHalf };\r\n}\r\nfunction ribbonPath(pts, frame, halfWs, ox, oy) {\r\n  const n = pts.length;\r\n  if (n < 2) return \"\";\r\n  let left = \"\";\r\n  let right = \"\";\r\n  for (let i = 0; i < n; i++) {\r\n    const w = halfWs[i] * frame.scale[i];\r\n    const lx = pts[i][0] + frame.mx[i] * w + ox;\r\n    const ly = pts[i][1] + frame.my[i] * w + oy;\r\n    left += `${i === 0 ? \"M\" : \"L\"}${fmt(lx)} ${fmt(ly)}`;\r\n    const rx = pts[i][0] - frame.mx[i] * w + ox;\r\n    const ry = pts[i][1] - frame.my[i] * w + oy;\r\n    right = `L${fmt(rx)} ${fmt(ry)}` + right;\r\n  }\r\n  return left + right + \"Z\";\r\n}\r\n\r\n// src/motion/settle.ts\r\nfunction floatOffset(t, shardHash, f) {\r\n  if (f.bobPx <= 0 && f.swayPx <= 0 && f.rotDeg <= 0) return { dx: 0, dy: 0, rot: 0 };\r\n  const phaseA = hashTo01(hashCombine(shardHash, 41)) * TAU;\r\n  const phaseB = hashTo01(hashCombine(shardHash, 53)) * TAU;\r\n  const phaseC = hashTo01(hashCombine(shardHash, 67)) * TAU;\r\n  const w = TAU * f.cycles;\r\n  return {\r\n    dx: Math.sin(w * t + phaseA) * f.swayPx,\r\n    dy: Math.sin(w * t + phaseB) * f.bobPx,\r\n    rot: Math.sin(w * t + phaseC) * f.rotDeg\r\n  };\r\n}\r\nfunction settleOffset(t, shardHash, amplitudePx, frequency, sinceT) {\r\n  if (amplitudePx <= 0) return { dx: 0, dy: 0, rot: 0 };\r\n  const tau = Math.max(0, t - sinceT);\r\n  const damp = Math.exp(-3.2 * tau);\r\n  const phaseA = hashTo01(hashCombine(shardHash, 11)) * TAU;\r\n  const phaseB = hashTo01(hashCombine(shardHash, 23)) * TAU;\r\n  const w = TAU * frequency;\r\n  return {\r\n    dx: Math.sin(w * tau + phaseA) * amplitudePx * damp,\r\n    dy: Math.cos(w * tau * 0.9 + phaseB) * amplitudePx * 0.7 * damp,\r\n    rot: Math.sin(w * tau * 0.75 + phaseA + phaseB) * amplitudePx * 0.12 * damp\r\n  };\r\n}\r\n\r\n// src/render/shardStyle.ts\r\nvar BEVEL_SECTORS = 12;\r\nvar staticCache = /* @__PURE__ */ new WeakMap();\r\nfunction staticShardStrings(pattern) {\r\n  let entry = staticCache.get(pattern);\r\n  if (!entry) {\r\n    entry = {\r\n      clip: pattern.shards.map((s) => toCssPolygon(s.outsetPolygon)),\r\n      innerClip: pattern.shards.map((s) => toCssPolygon(s.polygon)),\r\n      origin: pattern.shards.map((s) => `${fmt(s.centroid[0])}px ${fmt(s.centroid[1])}px`),\r\n      bevel: pattern.shards.map((s) => bevelSectors(s, pattern.width, pattern.height))\r\n    };\r\n    staticCache.set(pattern, entry);\r\n  }\r\n  return entry;\r\n}\r\nvar BEVEL_SCATTER_KEY = hashString(\"bevelScatter\");\r\nvar BEVEL_SPEC_FRACTION = 0.3;\r\nvar BEVEL_INTRINSIC_KEY = hashString(\"bevelIntrinsic\");\r\nvar BEVEL_JITTER_KEY = hashString(\"bevelJitter\");\r\nfunction bevelSectors(shard, w, h) {\r\n  const poly = shard.polygon;\r\n  const n = poly.length / 2;\r\n  const full = new Array(BEVEL_SECTORS).fill(\"\");\r\n  const spec = new Array(BEVEL_SECTORS).fill(\"\");\r\n  const shaded = new Array(BEVEL_SECTORS).fill(\"\");\r\n  const eps = 0.75;\r\n  const onSameBorder = (x1, y1, x2, y2) => x1 < eps && x2 < eps || x1 > w - eps && x2 > w - eps || y1 < eps && y2 < eps || y1 > h - eps && y2 > h - eps;\r\n  for (let i = 0; i < n; i++) {\r\n    const x1 = poly[i * 2];\r\n    const y1 = poly[i * 2 + 1];\r\n    const j = (i + 1) % n;\r\n    const x2 = poly[j * 2];\r\n    const y2 = poly[j * 2 + 1];\r\n    if (onSameBorder(x1, y1, x2, y2)) continue;\r\n    const dx = x2 - x1;\r\n    const dy = y2 - y1;\r\n    const len = Math.hypot(dx, dy);\r\n    if (len < 0.5) continue;\r\n    let ang = Math.atan2(-dx, dy);\r\n    if (ang < 0) ang += TAU;\r\n    const sector = Math.min(BEVEL_SECTORS - 1, Math.floor(ang / TAU * BEVEL_SECTORS));\r\n    const seg = `M${fmt(x1)} ${fmt(y1)}L${fmt(x2)} ${fmt(y2)}`;\r\n    full[sector] += seg;\r\n    if (hashTo01(hashCombine(shard.hash, BEVEL_SCATTER_KEY, i)) < BEVEL_SPEC_FRACTION) {\r\n      spec[sector] += seg;\r\n    } else {\r\n      shaded[sector] += seg;\r\n    }\r\n  }\r\n  const out = [];\r\n  for (let s = 0; s < BEVEL_SECTORS; s++) {\r\n    if (full[s]) {\r\n      out.push({\r\n        dFull: full[s],\r\n        dSpec: spec[s],\r\n        dShaded: shaded[s],\r\n        angle: (s + 0.5) / BEVEL_SECTORS * TAU,\r\n        intrinsic: 0.45 + 0.55 * hashTo01(hashCombine(shard.hash, BEVEL_INTRINSIC_KEY, s)),\r\n        jitter: (hashTo01(hashCombine(shard.hash, BEVEL_JITTER_KEY, s)) - 0.5) * 0.8\r\n      });\r\n    }\r\n  }\r\n  return out;\r\n}\r\nfunction shardPose(hash, fx) {\r\n  const rng = rngFor(hash, \"pose\");\r\n  return {\r\n    refrAngle: rng() * TAU,\r\n    refrMag: fx.refraction.offsetPx * (0.4 + 0.6 * rng()),\r\n    rotZ: fx.refraction.rotateDeg * (rng() * 2 - 1),\r\n    scaleDev: fx.refraction.scaleAmp * (rng() * 2 - 1),\r\n    tiltAxis: rng() * TAU,\r\n    tiltMag: fx.refraction.tiltDeg * (0.35 + 0.65 * rng()),\r\n    facetAngle: rng() * 360,\r\n    facetMag: 0.6 + 0.4 * rng(),\r\n    contrastSign: rng() < 0.5 ? -1 : 1,\r\n    chromaMagF: 0.55 + 0.45 * rng()\r\n  };\r\n}\r\nfunction rotate2D(x, y, rad) {\r\n  const c = Math.cos(rad);\r\n  const s = Math.sin(rad);\r\n  return [x * c - y * s, x * s + y * c];\r\n}\r\nfunction gravityDir(g) {\r\n  const l = Math.hypot(g[0], g[1]);\r\n  return l > 1e-6 ? [g[0] / l, g[1] / l] : [0, 1];\r\n}\r\nfunction buildShardFrame(t, pattern, shard, shardIndex, info, fx, q, pose, kind, spectral) {\r\n  const statics = staticShardStrings(pattern);\r\n  const ease = easeOutCubic(info.crackProgress);\r\n  const lightRad = degToRad(fx.optics.lightAngleDeg);\r\n  const motion = shardMotion(pattern, shard, fx, kind);\r\n  const tau = Math.max(0, info.shatterTau - motion.birthDelay);\r\n  const flying = info.phase === \"shattering\" && tau > 0;\r\n  const [fdx, fdy] = flightOffset(tau, motion.v0, fx.shatter.gravity, fx.shatter.drag);\r\n  const spin = motion.omega * tau;\r\n  const tumX = clamp(motion.tumbleX * tau, -78, 78);\r\n  const tumY = clamp(motion.tumbleY * tau, -78, 78);\r\n  const speed = flying ? flightSpeed(tau, motion.v0, fx.shatter.gravity, fx.shatter.drag) : 0;\r\n  const speedStrength = clamp01((speed - fx.motionBlur.speedThreshold) / (fx.motionBlur.speedThreshold * 2));\r\n  const st = info.phase === \"cracked\" ? settleOffset(t, shard.hash, fx.settle.amplitudePx, fx.settle.frequency, 0) : { dx: 0, dy: 0, rot: 0 };\r\n  let preDx = 0;\r\n  if (info.phase === \"cracked\" && pattern.mode === \"title\" && fx.shatter.preSpreadPx > 0) {\r\n    const side = shard.centroid[0] < pattern.width / 2 ? -1 : 1;\r\n    preDx = side * fx.shatter.preSpreadPx * ease;\r\n  }\r\n  let opacity = 1;\r\n  if (info.phase === \"shattering\") {\r\n    const [f0, f1] = fx.shatter.fadeOut;\r\n    opacity = 1 - clamp01((t - f0) / Math.max(1e-6, f1 - f0));\r\n  }\r\n  const isCrush = pattern.mode === \"radial\" && shard.ringIndex === 0;\r\n  const punch = isCrush && fx.crush.punch ? easeOutCubic(clamp01((info.crackProgress - 0.1) * 2.2)) : 0;\r\n  let dropEase = 0;\r\n  if (kind === \"dropped\") {\r\n    const dropStart = 0.45 + 0.3 * hashTo01(hashCombine(shard.hash, hashString(\"outlier:dropT\")));\r\n    dropEase = easeOutCubic(clamp01((info.crackProgress - dropStart) / 0.25));\r\n  }\r\n  const vanish = Math.max(punch, dropEase);\r\n  opacity *= 1 - vanish;\r\n  const gdir = gravityDir(fx.shatter.gravity);\r\n  const sinkPx = clamp(0.12 * Math.sqrt(shard.area / Math.PI), 1.5, 5) * vanish;\r\n  const rigidScale = (1 - (1 - fx.crush.scaleTo) * punch) * (1 - 0.05 * dropEase);\r\n  let slipDx = 0;\r\n  let slipDy = 0;\r\n  let slipRot = 0;\r\n  if (kind === \"slipped\") {\r\n    const slipMagF = hashTo01(hashCombine(shard.hash, hashString(\"outlier:slipMag\")));\r\n    const slipSign = hashTo01(hashCombine(shard.hash, hashString(\"outlier:slipRot\"))) < 0.5 ? -1 : 1;\r\n    const slipJit = hashTo01(hashCombine(shard.hash, hashString(\"outlier:slipDir\")));\r\n    let dir;\r\n    if (pattern.mode === \"radial\" || pattern.mode === \"web\") {\r\n      const dx = shard.centroid[0] - pattern.impact[0];\r\n      const dy = shard.centroid[1] - pattern.impact[1];\r\n      const l = Math.hypot(dx, dy) || 1;\r\n      dir = [dx / l, dy / l];\r\n    } else {\r\n      dir = gdir;\r\n    }\r\n    const rotated = rotate2D(dir[0], dir[1], (slipJit - 0.5) * 0.9);\r\n    const mag = fx.outliers.slipPx * (0.6 + 0.8 * slipMagF) * ease;\r\n    slipDx = rotated[0] * mag;\r\n    slipDy = rotated[1] * mag;\r\n    slipRot = slipSign * fx.outliers.slipRotDeg * (0.5 + slipMagF) * ease;\r\n  }\r\n  const fl = pattern.mode === \"hero\" ? floatOffset(t, shard.hash, fx.float) : { dx: 0, dy: 0, rot: 0 };\r\n  const rigidDx = fdx + st.dx + slipDx + fl.dx + preDx + gdir[0] * sinkPx;\r\n  const rigidDy = fdy + st.dy + slipDy + fl.dy + gdir[1] * sinkPx;\r\n  const rigidRot = spin + st.rot + slipRot + fl.rot;\r\n  const glass = fx.medium === \"glass\";\r\n  const glassInv = glass ? {\r\n    dx: Number(fmt(rigidDx)),\r\n    dy: Number(fmt(rigidDy)),\r\n    rot: Number(fmt(rigidRot, 3)),\r\n    scale: Number(fmt(Math.max(rigidScale, 0.05), 4))\r\n  } : null;\r\n  const glassWrap = (core) => glassInv ? `scale(${fmt(1 / glassInv.scale, 6)}) rotate(${fmt(-glassInv.rot, 3)}deg) ${core} translate(${fmt(-glassInv.dx)}px, ${fmt(-glassInv.dy)}px)` : core;\r\n  let shardTransform;\r\n  if (glassInv) {\r\n    shardTransform = `translate(${fmt(glassInv.dx)}px, ${fmt(glassInv.dy)}px) rotate(${fmt(glassInv.rot, 3)}deg) scale(${fmt(glassInv.scale, 4)})`;\r\n  } else {\r\n    shardTransform = `translate(${fmt(rigidDx)}px, ${fmt(rigidDy)}px)`;\r\n    if (fx.shatter.tumbleDegMax > 0 && flying) {\r\n      shardTransform += ` perspective(${fmt(fx.refraction.perspectivePx)}px) rotateX(${fmt(tumX, 3)}deg) rotateY(${fmt(tumY, 3)}deg)`;\r\n    }\r\n    shardTransform += ` rotate(${fmt(rigidRot, 3)}deg) scale(${fmt(rigidScale, 4)})`;\r\n  }\r\n  const refDx = Math.cos(pose.refrAngle) * pose.refrMag * ease;\r\n  const refDy = Math.sin(pose.refrAngle) * pose.refrMag * ease;\r\n  const refRot = pose.rotZ * ease;\r\n  const refScale = 1 + pose.scaleDev * ease;\r\n  const tiltX = Math.sin(pose.tiltAxis) * pose.tiltMag * ease;\r\n  const tiltY = Math.cos(pose.tiltAxis) * pose.tiltMag * ease;\r\n  let refrCore = `translate(${fmt(refDx)}px, ${fmt(refDy)}px)`;\r\n  if (fx.refraction.tiltDeg > 0) {\r\n    refrCore += ` perspective(${fmt(fx.refraction.perspectivePx)}px) rotateX(${fmt(tiltX, 3)}deg) rotateY(${fmt(tiltY, 3)}deg)`;\r\n  }\r\n  refrCore += ` rotate(${fmt(refRot, 3)}deg) scale(${fmt(refScale, 4)})`;\r\n  const contentTransform = glassWrap(refrCore);\r\n  const live = fx.optics.trackLight;\r\n  const lightDot = live ? Math.cos(pose.tiltAxis + degToRad(rigidRot) - lightRad) * (0.35 + 0.65 * (pose.tiltMag / Math.max(1e-3, fx.refraction.tiltDeg || 1))) : Math.cos(pose.tiltAxis - lightRad) * (0.35 + 0.65 * (pose.tiltMag / Math.max(1e-3, fx.refraction.tiltDeg || 1)));\r\n  const brightness = 1 + fx.optics.brightnessAmp * lightDot * ease;\r\n  const contrast = 1 + fx.optics.contrastAmp * pose.contrastSign * ease;\r\n  const smearBlurAdd = flying ? fx.motionBlur.smearBlurPx * speedStrength : 0;\r\n  const blurPx = clamp(clamp(fx.optics.blurPx, 0, q.maxBlurPx) * ease + smearBlurAdd, 0, q.maxBlurPx);\r\n  const chromaScale = (ease + (flying ? Math.min(1, tau * 2.2) : 0)) * pose.chromaMagF;\r\n  let chDx;\r\n  let chDy;\r\n  if (live) {\r\n    const chRadLive = degToRad(fx.chroma.angleDeg - rigidRot);\r\n    chDx = Math.cos(chRadLive) * fx.chroma.offsetPx * chromaScale;\r\n    chDy = Math.sin(chRadLive) * fx.chroma.offsetPx * chromaScale;\r\n  } else {\r\n    const chRad = degToRad(fx.chroma.angleDeg);\r\n    chDx = Math.cos(chRad) * fx.chroma.offsetPx * chromaScale;\r\n    chDy = Math.sin(chRad) * fx.chroma.offsetPx * chromaScale;\r\n  }\r\n  let contentFilter = `brightness(${fmt(brightness, 3)}) contrast(${fmt(contrast, 3)})`;\r\n  const smearFilter = contentFilter;\r\n  if (blurPx > 0.02) contentFilter += ` blur(${fmt(blurPx, 2)}px)`;\r\n  if (fx.chroma.mode === \"shadow\" && fx.chroma.offsetPx > 0) {\r\n    contentFilter += ` drop-shadow(${fmt(chDx)}px ${fmt(chDy)}px 0 ${fx.chroma.colorA}) drop-shadow(${fmt(-chDx)}px ${fmt(-chDy)}px 0 ${fx.chroma.colorB})`;\r\n  }\r\n  const chroma = [];\r\n  if (fx.chroma.mode === \"ghost\" && q.chromaGhosts > 0 && fx.chroma.offsetPx > 0) {\r\n    let gChDx = chDx;\r\n    let gChDy = chDy;\r\n    if (glass) {\r\n      const gRad = degToRad(live ? fx.chroma.angleDeg : fx.chroma.angleDeg + rigidRot);\r\n      gChDx = Math.cos(gRad) * fx.chroma.offsetPx * chromaScale;\r\n      gChDy = Math.sin(gRad) * fx.chroma.offsetPx * chromaScale;\r\n    }\r\n    const signs = q.chromaGhosts === 1 ? [1] : [1, -1];\r\n    for (const sign of signs) {\r\n      chroma.push({\r\n        transform: glassWrap(\r\n          `translate(${fmt(refDx + sign * gChDx)}px, ${fmt(refDy + sign * gChDy)}px) rotate(${fmt(refRot, 3)}deg) scale(${fmt(refScale, 4)})`\r\n        ),\r\n        filter: `hue-rotate(${sign > 0 ? 115 : -115}deg) saturate(1.7)`,\r\n        mixBlendMode: fx.chroma.blendMode,\r\n        opacity: fx.chroma.opacity * ease * opacity\r\n      });\r\n    }\r\n  }\r\n  const facetAngleOut = live ? pose.facetAngle - rigidRot : pose.facetAngle;\r\n  const facetWhite = fx.facet.strength * 0.65 * pose.facetMag;\r\n  const facetBlack = fx.facet.strength * 0.5 * pose.facetMag;\r\n  let facet = null;\r\n  if (fx.facet.opacity > 0 && fx.facet.strength > 0) {\r\n    let background = `linear-gradient(${fmt(facetAngleOut, 1)}deg, rgba(255,255,255,${fmt(facetWhite, 3)}) 0%, rgba(255,255,255,0) 38%, rgba(0,0,0,0) 62%, rgba(0,0,0,${fmt(facetBlack, 3)}) 100%)`;\r\n    if (fx.facet.tint) background += `, linear-gradient(0deg, ${fx.facet.tint}, ${fx.facet.tint})`;\r\n    facet = {\r\n      background,\r\n      mixBlendMode: fx.facet.blendMode,\r\n      opacity: fx.facet.opacity * ease * opacity\r\n    };\r\n  }\r\n  const wantEdge = q.edgeDistortion && fx.edgeDistortion.strength > 0 && fx.edgeDistortion.widthPx > 0.5;\r\n  const wantSpectrumRing = spectral !== null && fx.spectrum.edgeOnly > 0;\r\n  let ringD = \"\";\r\n  let ringClipCss = \"\";\r\n  if (wantEdge || wantSpectrumRing) {\r\n    const inner = insetPolygonTowardCentroid(shard.polygon, shard.centroid, fx.edgeDistortion.widthPx);\r\n    ringD = ringPathD(shard.polygon, inner);\r\n    ringClipCss = `path(\"${ringD}\")`;\r\n  }\r\n  let edge = null;\r\n  if (wantEdge) {\r\n    const k = 1 + 1.6 * fx.edgeDistortion.strength;\r\n    const eDx = refDx * k;\r\n    const eDy = refDy * k;\r\n    const eRot = refRot * (1 + 0.6 * fx.edgeDistortion.strength);\r\n    const eScale = refScale * (1 + 0.045 * fx.edgeDistortion.strength * ease);\r\n    let edgeCore = `translate(${fmt(eDx)}px, ${fmt(eDy)}px)`;\r\n    if (fx.refraction.tiltDeg > 0) {\r\n      edgeCore += ` perspective(${fmt(fx.refraction.perspectivePx)}px) rotateX(${fmt(tiltX, 3)}deg) rotateY(${fmt(tiltY, 3)}deg)`;\r\n    }\r\n    edgeCore += ` rotate(${fmt(eRot, 3)}deg) scale(${fmt(eScale, 4)})`;\r\n    const eBlur = clamp(fx.edgeDistortion.blurPx, 0, q.maxBlurPx);\r\n    let edgeFilter = contentFilter;\r\n    if (eBlur > 0.02) edgeFilter += ` blur(${fmt(eBlur, 2)}px)`;\r\n    edge = {\r\n      clipPath: ringClipCss,\r\n      d: ringD,\r\n      transform: glassWrap(edgeCore),\r\n      filter: edgeFilter,\r\n      opacity: ease,\r\n      // ramps in with the crack network; the wrapper already carries shard opacity\r\n      dx: eDx,\r\n      dy: eDy,\r\n      rot: eRot,\r\n      scale: eScale\r\n    };\r\n  }\r\n  let spectrum = null;\r\n  if (spectral) {\r\n    const specAngle = live ? fx.optics.lightAngleDeg - rigidRot : fx.optics.lightAngleDeg;\r\n    const cssAngle = live ? specAngle + 90 : fx.optics.lightAngleDeg + 90;\r\n    const c = spectral.center01 * 100;\r\n    const hw = spectral.width01 * 100 / 2;\r\n    const stops = `rgba(255,70,70,0) ${fmt(c - hw, 2)}%, rgba(255,70,70,0.85) ${fmt(c - hw * 0.6, 2)}%, rgba(255,210,70,0.85) ${fmt(c - hw * 0.25, 2)}%, rgba(110,255,150,0.85) ${fmt(c, 2)}%, rgba(80,200,255,0.85) ${fmt(c + hw * 0.35, 2)}%, rgba(170,100,255,0.85) ${fmt(c + hw * 0.65, 2)}%, rgba(170,100,255,0) ${fmt(c + hw, 2)}%`;\r\n    const glint = Math.max(0, Math.cos(degToRad(rigidRot)));\r\n    spectrum = {\r\n      background: `linear-gradient(${fmt(cssAngle, 1)}deg, ${stops})`,\r\n      mixBlendMode: fx.spectrum.blendMode,\r\n      opacity: fx.spectrum.opacity * ease * opacity * glint,\r\n      angleDeg: specAngle,\r\n      center01: spectral.center01,\r\n      width01: spectral.width01\r\n    };\r\n    if (wantSpectrumRing) {\r\n      spectrum.clipPath = ringClipCss;\r\n      spectrum.d = ringD;\r\n    }\r\n  }\r\n  const bevel = [];\r\n  if (q.bevel && fx.bevel.intensity > 0) {\r\n    const spinRad = degToRad(rigidRot);\r\n    const glintBoost = flying ? 1 + fx.bevel.glintStrength * Math.min(1, tau * 3) : 1;\r\n    const sigma = clamp01(fx.bevel.scatter);\r\n    const fv = clamp01(fx.bevel.facetVariation);\r\n    const fl2 = (x) => fv > 0 && x < 1e-6 ? 0 : x;\r\n    for (const sec of statics.bevel[shardIndex]) {\r\n      const Lang = fv > 0 ? sec.angle + fv * sec.jitter : sec.angle;\r\n      const L = Math.cos(Lang + spinRad - lightRad);\r\n      const im = fv > 0 ? 1 - fv + fv * sec.intrinsic : 1;\r\n      if (sigma <= 0) {\r\n        const lit = L > 0;\r\n        const mag = Math.min(1, Math.pow(Math.abs(L), 1.2) * fx.bevel.intensity * ease * opacity * glintBoost);\r\n        bevel.push({ d: sec.dFull, stroke: fx.bevel.lightColor, mixBlendMode: fx.bevel.blendMode, opacity: fl2((lit ? mag : 0) * im) });\r\n        bevel.push({ d: sec.dFull, stroke: fx.bevel.darkColor, mixBlendMode: \"normal\", opacity: fl2((lit ? 0 : Math.min(1, mag * 0.5)) * im) });\r\n        continue;\r\n      }\r\n      const base = fx.bevel.intensity * ease * opacity * im;\r\n      const gP = Math.pow(Math.max(0, L), 1.2) * glintBoost;\r\n      const gM = Math.pow(Math.max(0, -L), 1.2) * glintBoost;\r\n      const att = 1 - sigma;\r\n      if (sec.dSpec) {\r\n        const litOp = Math.min(1, base * (gP + sigma * (0.35 + 0.65 * gP - gP)));\r\n        const darkOp = Math.min(1, 0.5 * base * att * att * gM);\r\n        bevel.push({ d: sec.dSpec, stroke: fx.bevel.darkColor, mixBlendMode: \"normal\", opacity: fl2(darkOp) });\r\n        bevel.push({ d: sec.dSpec, stroke: fx.bevel.lightColor, mixBlendMode: fx.bevel.blendMode, opacity: fl2(litOp) });\r\n      }\r\n      if (sec.dShaded) {\r\n        const cross = 1 - 0.88 * sigma;\r\n        const litOp = Math.min(1, base * gP * cross * cross);\r\n        const darkOp = Math.min(1, 0.5 * base * (gM + sigma * (0.75 + 0.25 * gM - gM)));\r\n        bevel.push({ d: sec.dShaded, stroke: fx.bevel.darkColor, mixBlendMode: \"normal\", opacity: fl2(darkOp) });\r\n        bevel.push({ d: sec.dShaded, stroke: fx.bevel.lightColor, mixBlendMode: fx.bevel.blendMode, opacity: fl2(litOp) });\r\n      }\r\n    }\r\n  }\r\n  const smear = [];\r\n  if (q.smearGhosts > 0) {\r\n    const active = !glass && flying && speedStrength > 0;\r\n    let deltas = [];\r\n    if (active) {\r\n      const raw = [];\r\n      for (let k = 1; k <= q.smearGhosts; k++) {\r\n        const tk = Math.max(0, tau - k * fx.motionBlur.dt);\r\n        const [gx, gy] = flightOffset(tk, motion.v0, fx.shatter.gravity, fx.shatter.drag);\r\n        raw.push([gx - fdx, gy - fdy]);\r\n      }\r\n      const last = raw[raw.length - 1];\r\n      const lastLen = Math.hypot(last[0], last[1]);\r\n      const capScale = lastLen > 1e-6 ? Math.min(1, fx.motionBlur.smearPx / lastLen) : 0;\r\n      deltas = raw.map(([x, y]) => rotate2D(x * capScale, y * capScale, -degToRad(rigidRot)));\r\n    }\r\n    for (let k = 0; k < q.smearGhosts; k++) {\r\n      const d = deltas[k];\r\n      const op = active && d ? opacity * speedStrength * Math.pow(fx.motionBlur.opacityFalloff, k + 1) : 0;\r\n      smear.push({\r\n        transform: d ? `translate(${fmt(d[0])}px, ${fmt(d[1])}px) ${contentTransform}` : contentTransform,\r\n        dx: d ? d[0] : 0,\r\n        dy: d ? d[1] : 0,\r\n        opacity: op\r\n      });\r\n    }\r\n  }\r\n  const ghosts = [];\r\n  const ghostCount = glass ? 0 : Math.min(q.motionGhosts, fx.motionBlur.ghosts ?? q.motionGhosts);\r\n  if (flying && ghostCount > 0 && speedStrength > 0) {\r\n    for (let k = 1; k <= ghostCount; k++) {\r\n      const tk = tau - k * fx.motionBlur.dt;\r\n      if (tk <= 0) break;\r\n      const [gx, gy] = flightOffset(tk, motion.v0, fx.shatter.gravity, fx.shatter.drag);\r\n      let gt = `translate(${fmt(gx + st.dx)}px, ${fmt(gy + st.dy)}px)`;\r\n      if (fx.shatter.tumbleDegMax > 0) {\r\n        gt += ` perspective(${fmt(fx.refraction.perspectivePx)}px) rotateX(${fmt(clamp(motion.tumbleX * tk, -78, 78), 3)}deg) rotateY(${fmt(clamp(motion.tumbleY * tk, -78, 78), 3)}deg)`;\r\n      }\r\n      gt += ` rotate(${fmt(motion.omega * tk, 3)}deg)`;\r\n      ghosts.push({\r\n        shardTransform: gt,\r\n        opacity: opacity * speedStrength * Math.pow(fx.motionBlur.opacityFalloff, k)\r\n      });\r\n    }\r\n  }\r\n  return {\r\n    id: shard.id,\r\n    clipPath: statics.clip[shardIndex],\r\n    innerClipPath: statics.innerClip[shardIndex],\r\n    transformOrigin: statics.origin[shardIndex],\r\n    shardTransform,\r\n    contentTransform,\r\n    contentFilter,\r\n    opacity,\r\n    zIndex: (shard.z + 1) * 10,\r\n    facet,\r\n    spectrum,\r\n    edge,\r\n    chroma,\r\n    ghosts,\r\n    smear,\r\n    smearFilter,\r\n    bevel,\r\n    raw: {\r\n      // glass: tumble is hard-disabled and the scale is clamped (matches raw.glass and the\r\n      // emitted strings, so a consumer composing from raw.rigid stays in lockstep)\r\n      rigid: {\r\n        dx: rigidDx,\r\n        dy: rigidDy,\r\n        rotZ: rigidRot,\r\n        rotX: glass ? 0 : tumX,\r\n        rotY: glass ? 0 : tumY,\r\n        scale: glassInv ? glassInv.scale : rigidScale\r\n      },\r\n      refraction: { dx: refDx, dy: refDy, rot: refRot, scale: refScale, tiltX, tiltY },\r\n      glass: glassInv,\r\n      brightness,\r\n      contrast,\r\n      blurPx,\r\n      chromaDx: chDx,\r\n      chromaDy: chDy,\r\n      facetAngleDeg: facetAngleOut,\r\n      facetWhite,\r\n      facetBlack\r\n    }\r\n  };\r\n}\r\n\r\n// src/render/frame.ts\r\nfunction computeFrame(t, pattern, fxPartial) {\r\n  const tc = clamp01(t);\r\n  const fx = normalizeEffectParams(fxPartial);\r\n  const q = resolveQuality(fx.quality);\r\n  const info = resolvePhase(tc, fx.timeline);\r\n  const poses = pattern.shards.map((s) => shardPose(s.hash, fx));\r\n  const kinds = assignOutliers(pattern, fx.outliers);\r\n  const spectral = selectSpectral(pattern, poses, fx, q.spectrum);\r\n  const shards = pattern.shards.map(\r\n    (s, i) => buildShardFrame(tc, pattern, s, i, info, fx, q, poses[i], kinds[i], spectral[i])\r\n  );\r\n  const cracks = crackLayer(pattern, info, fx, q);\r\n  const microCount = Math.min(pattern.micro.length, q.microShardCap);\r\n  const micro = [];\r\n  for (let i = 0; i < microCount; i++) {\r\n    const m = pattern.micro[i];\r\n    const visible = clamp01((info.crackProgress - m.birth) / 0.05);\r\n    const mm = microMotion(pattern, m.origin, m.hash, m.ringIndex, fx);\r\n    const tau = info.phase === \"shattering\" ? Math.max(0, info.shatterTau - mm.birthDelay) : 0;\r\n    const [fdx, fdy] = flightOffset(tau, mm.v0, fx.shatter.gravity, fx.shatter.drag);\r\n    const twinklePhase = hashTo01(hashCombine(m.hash, 77)) * 6.283;\r\n    const twinkle = 0.8 + 0.2 * Math.sin(tc * 21 + twinklePhase);\r\n    let opacity = visible * fx.micro.opacity * twinkle;\r\n    if (info.phase === \"shattering\") {\r\n      const [f0, f1] = fx.shatter.fadeOut;\r\n      opacity *= 1 - clamp01((tc - (f0 - 0.04)) / Math.max(1e-6, f1 - f0));\r\n    }\r\n    micro.push({\r\n      id: m.id,\r\n      points: toSvgPoints(m.polygon),\r\n      transform: `translate(${fmt(m.origin[0] + fdx)} ${fmt(m.origin[1] + fdy)}) rotate(${fmt(mm.omega * tau, 2)})`,\r\n      opacity: Math.max(0, Math.min(1, opacity)),\r\n      fill: hashTo01(hashCombine(m.hash, 5)) < 0.72 ? fx.micro.fill : fx.micro.fillAlt\r\n    });\r\n  }\r\n  return {\r\n    t: tc,\r\n    phase: info.phase,\r\n    shards,\r\n    cracks,\r\n    micro,\r\n    grain: q.grain ? { opacity: fx.optics.grainOpacity } : null\r\n  };\r\n}\r\nfunction selectSpectral(pattern, poses, fx, enabled) {\r\n  const out = pattern.shards.map(() => null);\r\n  if (!enabled || fx.spectrum.count <= 0 || fx.spectrum.opacity <= 0) return out;\r\n  const lightRad = degToRad(fx.optics.lightAngleDeg);\r\n  const dirX = Math.cos(lightRad);\r\n  const dirY = Math.sin(lightRad);\r\n  const w = pattern.width;\r\n  const h = pattern.height;\r\n  const corners = [\r\n    [0, 0],\r\n    [w, 0],\r\n    [w, h],\r\n    [0, h]\r\n  ];\r\n  let tMin = Infinity;\r\n  let tMax = -Infinity;\r\n  for (const c of corners) {\r\n    const p = c[0] * dirX + c[1] * dirY;\r\n    if (p < tMin) tMin = p;\r\n    if (p > tMax) tMax = p;\r\n  }\r\n  const span = Math.max(1e-6, tMax - tMin);\r\n  const minArea = 0.02 * w * h;\r\n  let candidates = [];\r\n  for (let i = 0; i < pattern.shards.length; i++) {\r\n    if (pattern.shards[i].area >= minArea) candidates.push(i);\r\n  }\r\n  if (candidates.length === 0) candidates = pattern.shards.map((_, i) => i);\r\n  const edgeOnly = clamp(fx.spectrum.edgeOnly, 0, 1);\r\n  let maxArea = 1;\r\n  if (edgeOnly > 0) {\r\n    for (const i of candidates) maxArea = Math.max(maxArea, pattern.shards[i].area);\r\n  }\r\n  const score = (i) => {\r\n    const align = Math.cos(poses[i].tiltAxis - lightRad);\r\n    return edgeOnly > 0 ? align + 1.2 * edgeOnly * (1 - pattern.shards[i].area / maxArea) : align;\r\n  };\r\n  candidates.sort((a, b) => score(b) - score(a) || a - b);\r\n  const picked = candidates.slice(0, Math.min(fx.spectrum.count, candidates.length));\r\n  for (const i of picked) {\r\n    const s = pattern.shards[i];\r\n    const rng = rngFor(s.hash, \"spectrum\");\r\n    const centroidT = (s.centroid[0] * dirX + s.centroid[1] * dirY - tMin) / span;\r\n    let eMin = Infinity;\r\n    let eMax = -Infinity;\r\n    for (let p = 0; p + 1 < s.polygon.length; p += 2) {\r\n      const pr = s.polygon[p] * dirX + s.polygon[p + 1] * dirY;\r\n      if (pr < eMin) eMin = pr;\r\n      if (pr > eMax) eMax = pr;\r\n    }\r\n    const extent01 = (eMax - eMin) / span;\r\n    const baseCenter = clamp(centroidT + (rng() - 0.5) * 0.12, 0.05, 0.95);\r\n    const baseWidth = clamp(extent01 * fx.spectrum.bandWidth, 0.02, 0.5);\r\n    const rimCenter = clamp((eMax - tMin) / span - baseWidth * 0.3, 0.05, 0.95);\r\n    out[i] = {\r\n      center01: baseCenter + (rimCenter - baseCenter) * edgeOnly,\r\n      width01: baseWidth * (1 - 0.55 * edgeOnly)\r\n    };\r\n  }\r\n  return out;\r\n}\r\n\r\n// src/motion/presets.ts\r\nvar impactTimeline = { crackStart: 0, crackEnd: 0.2, shatterStart: 0.25 };\r\nvar suspenseTimeline = { crackStart: 0, crackEnd: 0.55, shatterStart: 0.72 };\r\nvar hardBlastShatter = {\r\n  speed: 1600,\r\n  gravity: [0, 420],\r\n  drag: 1,\r\n  spinDegMax: 200,\r\n  tumbleDegMax: 80,\r\n  staggerPerRing: 0.03\r\n};\r\nvar gentleCrumbleShatter = {\r\n  speed: 280,\r\n  gravity: [0, 2200],\r\n  drag: 0.6,\r\n  spinDegMax: 70,\r\n  tumbleDegMax: 28,\r\n  staggerPerRing: 0.07\r\n};\r\nvar motionPresets = {\r\n  impact: { label: \"Impact - hard blast\", timeline: impactTimeline, shatter: hardBlastShatter },\r\n  suspense: { label: \"Suspense - long crack, late shatter\", timeline: suspenseTimeline },\r\n  crumble: { label: \"Crumble - reluctant gravity fall\", shatter: gentleCrumbleShatter },\r\n  poster: { label: \"Poster - static cracked, no shatter\", timeline: staticCrackedTimeline }\r\n};\r\n\r\n// src/index.ts\r\nvar collapseShatterPreset = {\r\n  speed: 300,\r\n  gravity: [0, 2200],\r\n  drag: 0.6,\r\n  spinDegMax: 80,\r\n  tumbleDegMax: 30,\r\n  staggerPerRing: 0.05\r\n};\r\nexport {\r\n  collapseShatterPreset,\r\n  computeFrame,\r\n  defaultEffectParams,\r\n  flightOffset,\r\n  flightSpeed,\r\n  generateFracture,\r\n  gentleCrumbleShatter,\r\n  hardBlastShatter,\r\n  impactTimeline,\r\n  motionPresets,\r\n  normalizeEffectParams,\r\n  qualityPresets,\r\n  resolvePhase,\r\n  resolveQuality,\r\n  staticCrackedTimeline,\r\n  suspenseTimeline\r\n};\r\n","modules/portal/peer.mjs":"// #135 look-around behind the portal window. The portal's destination framing (portal.mjs) writes the eye here while a\r\n// destination is open inside the metal frame: x right, y down, each -1..1 at the tilt clamp, 0 when off (reduced\r\n// motion, camera-only, no destination). A three.js scene behind the window calls offAxis() every frame.\r\n// Standalone and dependency-free: served unbundled next to portal.mjs and imported by the scenes that use it.\r\n// AGPL-3.0-or-later.\r\nexport const eye={x:0,y:0};\r\n\r\n// Off-axis (head-coupled) projection: the eye slides parallel to the window while the window's edges stay pinned, so\r\n// the scene behind shows its sides like a real hole in a wall (Kooima's frustum for a screen-parallel window). Call it\r\n// after the scene has posed its camera for the frame, right before render. dist: the camera's distance to the window\r\n// plane (put it at the subject: nearer things swing more, farther less); deg: the view shift at full tilt. It undoes\r\n// its own last shift if the scene left the camera where it was, so a camera posed once (not every frame) is fine too,\r\n// and rebuilds the frustum every call (the scene's own updateProjectionMatrix() makes it symmetric again).\r\nexport function offAxis(camera,dist,deg=12){\r\n const u=camera.userData;\r\n if(u.peerAt&&camera.position.equals(u.peerAt)){camera.translateX(-u.peerX);camera.translateY(-u.peerY);}\r\n camera.updateProjectionMatrix();\r\n const k=Math.tan(deg*Math.PI/180)*dist,ex=eye.x*k,ey=-eye.y*k; // world units at the window plane; screen y is down\r\n u.peerX=ex;u.peerY=ey;\r\n if(ex||ey){\r\n  camera.translateX(ex);camera.translateY(ey);\r\n  const near=camera.near,top=near*Math.tan(camera.fov*Math.PI/360)/camera.zoom,half=top*camera.aspect,s=near/dist;\r\n  camera.projectionMatrix.makePerspective(-half-ex*s,half-ex*s,top-ey*s,-top-ey*s,near,camera.far);\r\n  camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert();\r\n }\r\n (u.peerAt??=camera.position.clone()).copy(camera.position);\r\n}\r\n","modules/portal/portal-apertures.mjs":"// Shared manufacturing and portal release contours, in the supplied rectangle's units.\r\n// Mechanical boards build these seams before input; other boards use the same windows.\r\nimport {SHAPES} from './portal-shapes.mjs';\r\nconst closeLoop=pts=>{const a=pts[0],b=pts.at(-1);return a[0]===b[0]&&a[1]===b[1]?pts:[...pts,a];};\r\nconst toClientPts=(pts,rect)=>pts.map(([x,y])=>[rect.left+x*rect.width,rect.top+y*rect.height]);\r\nconst LINE_IDS=new Set(['line-lr','line-rl','line-down']);\r\nexport function shapeOutlinePts(id){\r\n const pts=SHAPES[id][0].points;\r\n if(id!=='oval')return pts;\r\n const step=Math.max(1,Math.floor(pts.length/48));\r\n return pts.filter((_,i)=>i%step===0);\r\n}\r\nexport function lensPts([ax,ay],[bx,by],half,n=24){\r\n const l=Math.hypot(bx-ax,by-ay)||1,nx=-(by-ay)/l,ny=(bx-ax)/l;\r\n const side=sign=>Array.from({length:n+1},(_,i)=>{const t=sign>0?i/n:1-i/n,w=sign*half*Math.sin(Math.PI*t);return [ax+(bx-ax)*t+nx*w,ay+(by-ay)*t+ny*w];});\r\n return closeLoop([...side(1),...side(-1).slice(1,-1)]);\r\n}\r\nexport function portalWindow(id,rect){\r\n if(LINE_IDS.has(id)){\r\n  const points=SHAPES.line[id==='line-lr'||id==='line-rl'?1:0].points;\r\n  const [a,b]=toClientPts(points,rect),len=Math.hypot(b[0]-a[0],b[1]-a[1]);\r\n  return {pts:lensPts(a,b,Math.min(.17*len,.3*rect.width)),axis:[a,b]};\r\n }\r\n if(id==='x'){\r\n  const cx=rect.left+rect.width/2,cy=rect.top+rect.height/2,r=.3*Math.min(rect.width,rect.height);\r\n  return {pts:closeLoop([[cx,cy-r],[cx+r,cy],[cx,cy+r],[cx-r,cy]])};\r\n }\r\n return {pts:closeLoop(toClientPts(shapeOutlinePts(id),rect))};\r\n}\r\n","modules/portal/portal-board-clay.mjs":"// Clay board for the portal home: a carved clay tablet (Tripo GLB, simplified by scripts/clay-board-assets.mjs) that\r\n// the finger rolls purple clay rope onto. Rope is real geometry (rounded tubes draped over the tablet's relief, true\r\n// shadow-mapped contact shadows) on the 3D board and shaded stamps on the flat board; both share one pure core.\r\n// Secret (Achievement Vault): scrub clay back and forth until the whole face is covered (>= CLAY.cover of the coarse\r\n// regions) -> the wet clay dries (sheen fades), cracks, crumbles into chunks and flakes, then window event\r\n// 'myr5:portal-secret' {board:'clay'} fires exactly once per run. The tablet's rim carries hand-stamped Sakura blossoms and petals\r\n// (the walk's cherry-blossom palette, see pod/worlds/boards/clay/PROVENANCE.md) as bas-relief round the usable frame. claims(id) is true only while that secret is underway;\r\n// ordinary strokes stay with the shape recognizer. AGPL-3.0-or-later.\r\nimport {createGlbBoard} from './portal-board-glb.mjs';\r\nimport {boardLayout} from './portal-board-layout.mjs';\r\nimport {SHAPES,fromFrame} from './portal-shapes.mjs';\r\nimport {PAL} from '../../arcade/runner/worlds/sakura/palette.js'; // the established Sakura (Little Boyfriend / runner) palette\r\n\r\nexport const CLAY_ASSET='/pod/worlds/boards/clay/clay-tablet.glb';\r\nexport const CLAY_RATIO=.75; // tablet's own face width / height (GLB bbox 0.735 x 0.980); on screen it stretches to fill the whole host (portal-board-layout.mjs)\r\nexport const CLAY_FRAME={x0:.095,y0:.062,x1:.918,y1:.938}; // the carved oval's rectangle, as face fractions (portal-shapes frame)\r\nexport const CLAY={\r\n radius:.03,          // rope radius, as a fraction of face width\r\n spacing:.9,          // ring spacing along a stroke, in radii\r\n cover:.8,            // fraction of coarse regions that must be covered for the secret (tunable)\r\n regionFill:.7,       // fraction of a coarse region's fine cells that must be covered for it to count\r\n cols:44,region:4,    // fine cover mask columns; a coarse region is region x region fine cells\r\n margin:.035,         // the tablet's raised rim (face-width fraction) is not asked to be covered\r\n leg:.12,revs:6,      // a back/forth leg spans >= leg face widths; the secret needs revs reversals\r\n underway:{revs:3,cover:.1}, // past this the scribble is the secret: claims() is true and the clay stops drying out\r\n life:14000,fade:1400,idle:6000, // ms: rope lifetime, melt-in time, idle time before an unfinished secret is given up\r\n dry:1400,crack:1600,crumble:3000,hold:9000, // ms per stage; 'gone' holds before healing itself\r\n maxRings:2000,bake:500, // beyond maxRings the oldest bake into the painted layer\r\n};\r\nconst STAGES=['cover','dry','crack','crumble','gone'];\r\nconst GRAV=1.4; // face widths / s^2\r\n\r\n// --- Pure helpers (no DOM, no THREE) ---------------------------------------------------------------------\r\n// A reversal is a counter-move of `leg` off the extreme of a leg (x and y tracked separately).\r\nfunction turn(a,p,leg){\r\n let {ref,dir,ext}=a,hit=false;\r\n if(!dir){if(Math.abs(p-ref)>=leg){dir=Math.sign(p-ref);ext=p;}}\r\n else if(dir*(p-ext)>0)ext=p;\r\n else if(Math.abs(p-ext)>=leg){hit=true;ref=ext;dir=-dir;ext=p;}\r\n return {a:{ref,dir,ext},hit};\r\n}\r\n// Jittered-grid Voronoi of the face (x 0..1, y 0..aspect): the crack network and the chunks. T = break threshold\r\n// (the bottom lets go first), the same idea as the wood board's crumble.\r\nfunction clip(poly,a,b,c){ // keep a*x+b*y<=c\r\n const out=[];for(let i=0;i<poly.length;i++){const p=poly[i],q=poly[(i+1)%poly.length],dp=a*p[0]+b*p[1]-c,dq=a*q[0]+b*q[1]-c;\r\n  if(dp<=0)out.push(p);if(dp*dq<0){const t=dp/(dp-dq);out.push([p[0]+(q[0]-p[0])*t,p[1]+(q[1]-p[1])*t]);}}\r\n return out;\r\n}\r\nexport function makeCells(aspect,rnd=Math.random,gx=8){\r\n const gy=Math.max(2,Math.round(gx*aspect)),seeds=[];\r\n for(let j=0;j<gy;j++)for(let i=0;i<gx;i++)seeds.push({x:(i+.15+.7*rnd())/gx,y:(j+.15+.7*rnd())/gy*aspect});\r\n return seeds.map(s=>{\r\n  let poly=[[0,0],[1,0],[1,aspect],[0,aspect]];\r\n  for(const o of seeds)if(o!==s){const dx=o.x-s.x,dy=o.y-s.y;if(dx*dx+dy*dy<.09)poly=clip(poly,dx,dy,(o.x*o.x+o.y*o.y-s.x*s.x-s.y*s.y)/2);}\r\n  const cx=poly.reduce((a,p)=>a+p[0],0)/poly.length,cy=poly.reduce((a,p)=>a+p[1],0)/poly.length;\r\n  return {s,poly,cx,cy,T:Math.max(.02,Math.min(1,.45*rnd()+.55*(1-s.y/aspect)))};\r\n });\r\n}\r\n// RGBA bytes TW x TH: R = break threshold of the cell under each texel, G = distance to the nearest crack (0 on it, 255 far).\r\nexport function cellMap(cells,aspect,TW=128,TH=Math.round(128*aspect)){\r\n const px=new Uint8Array(TW*TH*4);\r\n for(let py=0;py<TH;py++)for(let qx=0;qx<TW;qx++){\r\n  const x=(qx+.5)/TW,y=(py+.5)/TH*aspect;let d1=9,d2=9,b=0;\r\n  for(let k=0;k<cells.length;k++){const d=Math.hypot(cells[k].s.x-x,cells[k].s.y-y);if(d<d1){d2=d1;d1=d;b=k;}else if(d<d2)d2=d;}\r\n  const o=(py*TW+qx)*4;px[o]=Math.round(cells[b].T*255);px[o+1]=Math.round(Math.min(1,(d2-d1)/2/.06)*255);px[o+3]=255;\r\n }\r\n return px;\r\n}\r\n// Height of the tablet's front face over a GW-wide grid of the face (model units, face centred on 0): the highest vertex per\r\n// cell, holes filled, then dilated by the rope radius so a rope bridges the carving instead of sinking into it, and smoothed.\r\n// at(u,v) (u right, v down) -> z; top = the tablet's highest point.\r\nexport function heightField(pos,fw,fh,rope,GW=64){\r\n const GH=Math.max(2,Math.round(GW*fh/fw)),H=new Float32Array(GW*GH).fill(-Infinity);let top=-Infinity;\r\n for(let i=0;i<pos.length;i+=3){\r\n  top=Math.max(top,pos[i+2]);\r\n  const gx=Math.min(GW-1,Math.max(0,Math.floor((pos[i]+fw/2)/fw*GW))),gy=Math.min(GH-1,Math.max(0,Math.floor((1-(pos[i+1]+fh/2)/fh)*GH))),k=gy*GW+gx;\r\n  if(pos[i+2]>H[k])H[k]=pos[i+2];\r\n }\r\n const filter=(src,r,fn)=>{const out=new Float32Array(src.length);for(let y=0;y<GH;y++)for(let x=0;x<GW;x++){let acc=fn.init;for(let j=Math.max(0,y-r);j<=Math.min(GH-1,y+r);j++)for(let i=Math.max(0,x-r);i<=Math.min(GW-1,x+r);i++)acc=fn.f(acc,src[j*GW+i]);out[y*GW+x]=fn.done(acc);}return out;};\r\n const fill={init:-Infinity,f:(a,b)=>Math.max(a,b),done:a=>a};\r\n let g=H;for(let pass=0;pass<GW&&g.some(v=>v===-Infinity);pass++){const f=filter(g,1,fill);g=g.map((v,i)=>v===-Infinity?f[i]:v);}\r\n if(g.some(v=>v===-Infinity))g=g.map(()=>0);\r\n g=filter(g,Math.max(0,Math.ceil(rope/(fw/GW))),fill);\r\n g=filter(g,1,{init:[0,0],f:(a,b)=>[a[0]+b,a[1]+1],done:a=>a[0]/a[1]});\r\n const at=(u,v)=>{const x=Math.min(GW-1.001,Math.max(0,u*GW-.5)),y=Math.min(GH-1.001,Math.max(0,v*GH-.5)),i=Math.floor(x),j=Math.floor(y),fx=x-i,fy=y-j;\r\n  return (g[j*GW+i]*(1-fx)+g[j*GW+i+1]*fx)*(1-fy)+(g[(j+1)*GW+i]*(1-fx)+g[(j+1)*GW+i+1]*fx)*fy;};\r\n return {top,at,GW,GH};\r\n}\r\n// Radius multiplier of one rope ring: melts to 0 over `fade` once older than `life`, hand-rolled lumpiness, rounded ends.\r\nexport function ringScale(p,vt,cfg=CLAY,first=false,last=false){\r\n const age=Math.min(1,Math.max(0,1-(vt-p.t-cfg.life)/cfg.fade));\r\n return age*(1+.08*p.j)*(first?.72:1)*(last&&p.end?.85:1);\r\n}\r\nconst lerp=(a,b,t)=>a+(b-a)*t;\r\nconst mix=(a,b,t)=>a.map((v,i)=>Math.round(lerp(v,b[i],t)));\r\n// Wet clay (the runner's clay-text purples) -> dry, chalky: [rim, body, highlight].\r\nexport function ropeColors(dry=0){\r\n const f=c=>`rgb(${mix(c[0],c[1],dry).join(',')})`;\r\n return [f([[58,26,74],[96,70,110]]),f([[142,91,153],[170,140,182]]),`rgba(${mix([214,168,234],[226,208,236],dry).join(',')},${lerp(.65,.3,dry).toFixed(2)})`];\r\n}\r\n// Rope onto a 2D canvas (the flat board's live rope and both boards' baked cover): per stroke a contact shadow, dark\r\n// rim, body and highlight, each as one path of stamped discs. W,H = canvas px of the face; r = rope radius in px.\r\nexport function paintRope(ctx,pts,W,H,r,{vt=0,dry=0,cfg=CLAY}={}){\r\n const [rim,body,hi]=ropeColors(dry),disc=(list,dx,dy,k)=>{ctx.beginPath();for(const [x,y,s] of list){const q=r*s*k;if(q<.4)continue;ctx.moveTo(x+dx*r*s+q,y+dy*r*s);ctx.arc(x+dx*r*s,y+dy*r*s,q,0,7);}ctx.fill();};\r\n ctx.save();\r\n for(let i=0;i<pts.length;){\r\n  let j=i+1;while(j<pts.length&&pts[j].s===pts[i].s)j++;\r\n  const list=[];for(let k=i;k<j;k++)list.push([pts[k].u*W,pts[k].v*H,ringScale(pts[k],vt,cfg,k===i,k===j-1)]);\r\n  ctx.fillStyle='rgba(18,6,26,.3)';disc(list,.3,.38,1.14);\r\n  ctx.fillStyle=rim;disc(list,0,0,1);ctx.fillStyle=body;disc(list,-.05,-.08,.78);\r\n  ctx.strokeStyle=hi;ctx.lineCap=ctx.lineJoin='round';ctx.beginPath(); // the highlight is one continuous ridge, not beads\r\n  for(let k=0,pen=false;k<list.length;k++){const [x,y,q]=list[k];if(q<.25){pen=false;continue;}ctx[pen?'lineTo':'moveTo'](x-.22*r*q,y-.3*r*q);pen=true;}\r\n  ctx.lineWidth=.42*r*Math.max(.3,list.reduce((a,l)=>a+l[2],0)/list.length);ctx.stroke();\r\n  i=j;\r\n }\r\n ctx.restore();\r\n}\r\n\r\n// Active elapsed seconds between two rAF timestamps. The boards' frame loop clamps dt to 1/30 s, so on a slow (software GL)\r\n// device stages driven by dt would crawl; wall time does not. A gap longer than maxGap is a pause, a hidden tab or a heal:\r\n// it is not counted (the stable dt stands in), so nothing jumps ahead on resume.\r\nexport function activeDt(last,now,dt,maxGap=2500){const g=now-last;return last!=null&&g>0&&g<=maxGap?g/1000:dt;}\r\n\r\n// The whole secret and the rope it draws, independent of any renderer. u,v are face fractions (v down); the face is\r\n// `aspect` (= height / width) tall in face-width units. step(dt seconds) advances a virtual clock that stands still while\r\n// the secret is underway, so rope only melts away when you are not covering the board.\r\nexport function createClayCore({aspect=1/CLAY_RATIO,cfg=CLAY,speed=1,rnd=Math.random,onSecret=()=>{},onStage=()=>{}}={}){\r\n const cols=cfg.cols,rows=Math.max(1,Math.round(cols*aspect)),R=cfg.radius,stride=R*cfg.spacing;\r\n const stamp=new Float64Array(cols*rows).fill(-1),valid=new Uint8Array(cols*rows),m=cfg.margin;\r\n for(let j=0;j<rows;j++)for(let i=0;i<cols;i++){const u=(i+.5)/cols,v=(j+.5)/rows;valid[j*cols+i]=u>=m&&1-u>=m&&v*aspect>=m&&(1-v)*aspect>=m?1:0;}\r\n const RC=Math.ceil(cols/cfg.region),RR=Math.ceil(rows/cfg.region);\r\n const strokes=new Map(),drained=[];let sid=0,cptr=0,order=[];\r\n const c={pts:[],held:new Set(),stage:'cover',stageT:0,vt:0,idle:0,revs:0,underway:false,fired:false,version:0,cells:null,\r\n  debris:{chunks:[],flakes:[]},cover:{regions:0,covered:0,frac:0},aspect,cfg};\r\n\r\n function coverage(){\r\n  const tot=new Uint16Array(RC*RR),hit=new Uint16Array(RC*RR);\r\n  for(let j=0;j<rows;j++)for(let i=0;i<cols;i++){const k=j*cols+i;if(!valid[k])continue;const q=Math.floor(j/cfg.region)*RC+Math.floor(i/cfg.region);tot[q]++;if(stamp[k]>=0&&c.vt-stamp[k]<=cfg.life)hit[q]++;}\r\n  let n=0,ok=0;for(let q=0;q<tot.length;q++)if(tot[q]){n++;if(hit[q]/tot[q]>=cfg.regionFill)ok++;}\r\n  return c.cover={regions:n,covered:ok,frac:n?ok/n:0};\r\n }\r\n function stampDisc(u,v){ // rope footprint: every fine cell whose centre is within the rope radius\r\n  const x=u,y=v*aspect;\r\n  for(let j=Math.max(0,Math.floor((y-R)/aspect*rows));j<=Math.min(rows-1,Math.floor((y+R)/aspect*rows));j++)for(let i=Math.max(0,Math.floor((x-R)*cols));i<=Math.min(cols-1,Math.floor((x+R)*cols));i++)\r\n   if(Math.hypot((i+.5)/cols-x,(j+.5)/rows*aspect-y)<=R)stamp[j*cols+i]=c.vt;\r\n }\r\n function addPoint(s,u,v,start){\r\n  const last=c.pts[c.pts.length-1],prev=last&&last.s===s.id?last:null;\r\n  c.pts.push({u,v,t:c.vt,s:s.id,start,end:false,j:prev?prev.j*.6+(rnd()*2-1)*.4:rnd()*2-1,k:prev?prev.k*.6+rnd()*.4:rnd()});\r\n  stampDisc(u,v);c.version++;\r\n  if(c.pts.length>cfg.maxRings)drained.push(...c.pts.splice(0,cfg.bake));\r\n }\r\n function begin(stage){\r\n  c.stage=stage;c.stageT=0;c.version++;\r\n  if(stage==='dry'){c.cells=makeCells(aspect,rnd);order=[...c.cells.keys()].sort((a,b)=>c.cells[a].T-c.cells[b].T);cptr=0;}\r\n  onStage(stage);\r\n }\r\n function check(){\r\n  if(c.stage!=='cover')return;\r\n  const cv=coverage();\r\n  if(!c.underway&&c.revs>=cfg.underway.revs&&cv.frac>=cfg.underway.cover){c.underway=true;c.version++;}\r\n  if(c.underway&&c.revs>=cfg.revs&&cv.frac>=cfg.cover)begin('dry');\r\n }\r\n const inset=R*.9,clampU=u=>Math.min(1-inset,Math.max(inset,u)),clampV=v=>Math.min(1-inset/aspect,Math.max(inset/aspect,v)); // the rope's edge, not its centre, stops at the tablet's edge\r\n function press(id,u,v){\r\n  u=clampU(u);v=clampV(v);\r\n  if(c.stage==='gone')heal();\r\n  if(c.stage!=='cover'||strokes.has(id))return;\r\n  const s={id:++sid,u,v,fx:u,fy:v*aspect,ax:{ref:u,dir:0,ext:u},ay:{ref:v*aspect,dir:0,ext:v*aspect}};\r\n  strokes.set(id,s);c.held.add(id);c.idle=0;addPoint(s,u,v,true);check();\r\n }\r\n function move(id,u,v){\r\n  if(c.stage!=='cover')return;\r\n  u=clampU(u);v=clampV(v);\r\n  const s=strokes.get(id);if(!s)return press(id,u,v);\r\n  c.idle=0;\r\n  const tx=turn(s.ax,u,cfg.leg),ty=turn(s.ay,v*aspect,cfg.leg);s.ax=tx.a;s.ay=ty.a;if(tx.hit||ty.hit)c.revs++;\r\n  let x0=s.u,y0=s.v*aspect,d=Math.hypot(u-x0,v*aspect-y0);\r\n  while(d>=stride){const f=stride/d;x0+=(u-x0)*f;y0+=(v*aspect-y0)*f;s.u=x0;s.v=y0/aspect;addPoint(s,s.u,s.v,false);d=Math.hypot(u-x0,v*aspect-y0);}\r\n  check();\r\n }\r\n function release(id){\r\n  const s=strokes.get(id);if(!s)return;\r\n  strokes.delete(id);c.held.delete(id);\r\n  for(let i=c.pts.length-1;i>=0&&c.pts[i].s===s.id;i--){c.pts[i].end=true;break;}\r\n  c.version++;\r\n }\r\n function releaseCells(cr){\r\n  while(cptr<order.length&&c.cells[order[cptr]].T<=cr){const i=order[cptr++],cell=c.cells[i];\r\n   c.debris.chunks.push({i,a:0,x:cell.cx,y:cell.cy,vx:(rnd()-.5)*.15,vy:-rnd()*.07,rot:0,rv:(rnd()-.5)*2,burst:.6+.25*rnd(),alive:true});}\r\n }\r\n function stepDebris(dt){\r\n  const {chunks,flakes}=c.debris;\r\n  for(const k of chunks){\r\n   if(!k.alive)continue;\r\n   k.a+=dt;const cell=c.cells[k.i];k.x=cell.cx+k.vx*k.a;k.y=cell.cy+k.vy*k.a+.5*GRAV*k.a*k.a;k.rot=k.rv*k.a*k.a*3;\r\n   if(k.a>=k.burst){k.alive=false;for(let n=0;n<5;n++)flakes.push({x:k.x+(rnd()-.5)*.08,y:k.y+(rnd()-.5)*.08,vx:k.vx+(rnd()-.5)*.4,vy:k.vy+(rnd()-.5)*.1,rot:rnd()*6,rv:(rnd()-.5)*10,size:.016+rnd()*.018,a:0});}\r\n  }\r\n  for(const f of flakes){f.a+=dt;f.vy+=GRAV*dt;f.x+=f.vx*dt;f.y+=f.vy*dt;f.rot+=f.rv*dt;}\r\n  while(flakes.length&&(flakes[0].a>2.2||flakes.length>160))flakes.shift();\r\n }\r\n function heal(){\r\n  strokes.clear();c.held.clear();c.pts.length=0;drained.length=0;stamp.fill(-1);\r\n  Object.assign(c,{stage:'cover',stageT:0,vt:0,idle:0,revs:0,underway:false,fired:false,cells:null,cover:{regions:0,covered:0,frac:0}});\r\n  c.debris={chunks:[],flakes:[]};cptr=0;order=[];c.version++;\r\n }\r\n function step(dt,wall=dt){ // dt: stable physics step; wall: active elapsed seconds, which drives every clock and stage\r\n  if(c.freeze)dt=wall=0; // debug/preview: hold the clock still on a pose\r\n  const ms=wall*1000;\r\n  if(c.stage==='cover'){\r\n   if(c.held.size)c.idle=0;else c.idle+=ms;\r\n   if(c.underway&&!c.held.size&&c.idle>cfg.idle){c.underway=false;c.revs=0;c.version++;} // gave up: the clay dries out again\r\n   if(!c.underway){c.vt+=ms;let n=0;while(n<c.pts.length&&c.vt-c.pts[n].t>cfg.life+cfg.fade)n++;if(n){c.pts.splice(0,n);}}\r\n   return c.underway||c.held.size>0||c.pts.length>0;\r\n  }\r\n  c.stageT+=ms;\r\n  const d=cfg[c.stage]/speed;\r\n  if(c.stage==='crumble')releaseCells(amounts().crumble);\r\n  stepDebris(dt);\r\n  if(c.stage!=='gone'&&c.stageT>=d){\r\n   const next=STAGES[STAGES.indexOf(c.stage)+1];begin(next);\r\n   if(next==='gone'&&!c.fired){releaseCells(2);c.fired=true;onSecret();}\r\n  }else if(c.stage==='gone'&&c.stageT>=cfg.hold/speed)heal();\r\n  return true;\r\n }\r\n // How far each stage has got: dry/crack 0..1, crumble = the break-threshold front (cells with T <= it have let go).\r\n function amounts(){\r\n  const i=STAGES.indexOf(c.stage),p=i<1?0:c.stage==='gone'?1:Math.min(1,c.stageT/(cfg[c.stage]/speed));\r\n  return {p,dry:i<1?0:i===1?p:1,crack:i<2?0:i===2?p:1,crumble:i<3?0:i===3?1.02*p**1.25:1.1};\r\n }\r\n Object.assign(c,{press,move,release,step,heal,amounts,\r\n  claims:()=>c.underway||c.stage!=='cover', // true only while the secret is underway; asked after release too\r\n  drain(){return drained.splice(0);}, // rope that outgrew maxRings: the renderer paints it into its permanent layer\r\n  snapshot(){return {stage:c.stage,p:amounts().p,revs:c.revs,underway:c.underway,fired:c.fired,rings:c.pts.length,cover:{...coverage()},chunks:c.debris.chunks.length,flakes:c.debris.flakes.length,vt:c.vt};},\r\n  coverage});\r\n return c;\r\n}\r\n\r\n// --- Sakura rim art: blossoms and petals stamped into the clay round the usable frame -----------------------------\r\nconst hex=n=>'#'+n.toString(16).padStart(6,'0');\r\nexport const SAKURA={ink:hex(PAL.ink),inkSoft:hex(PAL.inkSoft),purple:hex(PAL.purple),blossom:hex(PAL.blossom),light:hex(PAL.blossomLight),warm:hex(PAL.blossomWarm),deep:hex(PAL.blossomDeep),petal:hex(PAL.petal),petalDeep:hex(PAL.petalDeep),cloud:hex(PAL.cloud)};\r\n// Five-petal blossom (notched petals, lavender heart) or one loose petal, centred on (0,0), radius r. toon: warm light from the\r\n// upper left, cool violet shade lower right (the walk's cel shading); shadow adds the stamp's contact shade for the flat board\r\n// (the 3D board embosses the same shapes from this layer's alpha instead).\r\nfunction petalPath(g,r){g.beginPath();g.moveTo(0,0);g.bezierCurveTo(-.62*r,-.18*r,-.66*r,-.92*r,-.13*r,-r);g.lineTo(0,-.86*r);g.lineTo(.13*r,-r);g.bezierCurveTo(.66*r,-.92*r,.62*r,-.18*r,0,0);g.closePath();}\r\nfunction stamp(g,x,y,r,rot,kind,shadow){\r\n const petals=kind==='blossom'?5:1;\r\n g.save();g.translate(x,y);g.rotate(rot);g.lineJoin='round';\r\n const body=()=>{for(let k=0;k<petals;k++){g.save();g.rotate(k*2*Math.PI/petals);petalPath(g,r);g.restore();}};\r\n const paint=(fill,line,w)=>{for(let k=0;k<petals;k++){g.save();g.rotate(k*2*Math.PI/petals);petalPath(g,r);if(fill){g.fillStyle=fill;g.fill();}if(line){g.strokeStyle=line;g.lineWidth=w;g.stroke();}g.restore();}};\r\n if(shadow){g.save();g.translate(r*.09,r*.14);paint('rgba(57,50,79,.38)');g.restore();}\r\n const grad=g.createRadialGradient(-r*.25,-r*.35,r*.05,0,0,r*1.05);\r\n if(kind==='blossom'){grad.addColorStop(0,SAKURA.light);grad.addColorStop(.5,SAKURA.blossom);grad.addColorStop(1,SAKURA.deep);}else{grad.addColorStop(0,SAKURA.light);grad.addColorStop(.55,SAKURA.petal);grad.addColorStop(1,SAKURA.petalDeep);}\r\n paint(grad,SAKURA.inkSoft+'99',r*.06);\r\n g.save();g.translate(-r*.035,-r*.05);paint(null,SAKURA.cloud+'cc',r*.035);g.restore(); // warm-white lit edge\r\n if(kind==='blossom'){\r\n  g.fillStyle=SAKURA.purple;g.beginPath();g.arc(0,0,r*.2,0,7);g.fill();g.strokeStyle=SAKURA.ink+'aa';g.lineWidth=r*.04;g.stroke();\r\n  g.fillStyle=SAKURA.cloud;for(let k=0;k<5;k++){g.beginPath();g.arc(Math.cos(k*1.2566)*r*.34,Math.sin(k*1.2566)*r*.34,r*.05,0,7);g.fill();}\r\n }\r\n g.restore();\r\n}\r\n// Blossoms at the corners, blossoms and petals alternating along each rim band of the frame; the frame itself stays clear.\r\nexport function paintSakuraRim(g,W,H,{frame=CLAY_FRAME,shadow=false}={}){\r\n let seed=1020;const rnd=()=>(seed=seed*1664525+1013904223>>>0)/4294967296;\r\n const cx0=frame.x0/2,cx1=(1+frame.x1)/2,cy0=frame.y0/2,cy1=(1+frame.y1)/2,R=Math.min(frame.x0*W,frame.y0*H,(1-frame.x1)*W,(1-frame.y1)*H)*.5;\r\n const spots=[];\r\n for(const [x,y] of [[cx0,cy0],[cx1,cy0],[cx0,cy1],[cx1,cy1]])spots.push([x*W,y*H,R*1.1,'blossom']);\r\n const along=(n,fn)=>{for(let i=1;i<n;i++)spots.push(fn(i/n,i%2?'blossom':'petal'));};\r\n along(7,(t,k)=>[(cx0+(cx1-cx0)*t)*W,cy0*H,R*(k==='blossom'?.8:.7),k]);along(7,(t,k)=>[(cx0+(cx1-cx0)*t)*W,cy1*H,R*(k==='blossom'?.8:.7),k]);\r\n along(9,(t,k)=>[cx0*W,(cy0+(cy1-cy0)*t)*H,R*(k==='blossom'?.8:.7),k]);along(9,(t,k)=>[cx1*W,(cy0+(cy1-cy0)*t)*H,R*(k==='blossom'?.8:.7),k]);\r\n for(const [x,y,r,k] of spots)stamp(g,x,y,r,rnd()*6.28,k,shadow);\r\n}\r\n\r\n// --- 3D effect (rides on createGlbBoard) -------------------------------------------------------------------\r\nconst RV=9,TH=Array.from({length:RV},(_,k)=>-.8+(Math.PI+1.6)*k/(RV-1)),CS=TH.map(Math.cos),SN=TH.map(Math.sin); // half-round section: its lower arc sits inside the tablet\r\nconst GLSL='uniform float uDry;uniform float uCrack;uniform float uCrumble;uniform sampler2D uCell;\\n';\r\nconst ART_GLSL='uniform sampler2D uArt;uniform vec2 uArtPx;\\n';\r\n// Board shader (runs after the generic tint/paint injection, which greys the albedo: put the clay's own colour back and\r\n// keep the paint layer, which holds the baked cover): wet sheen on the cover, chalky when dry, cracks, then cell-by-cell crumble.\r\nconst FRAG=`vec3 clayBase=texture2D(map,vMapUv).rgb;\r\nvec4 art=texture2D(uArt,vPlanar);float artA=art.a;\r\nfloat artX=texture2D(uArt,vPlanar+vec2(uArtPx.x,0.0)).a-texture2D(uArt,vPlanar-vec2(uArtPx.x,0.0)).a,artY=texture2D(uArt,vPlanar+vec2(0.0,uArtPx.y)).a-texture2D(uArt,vPlanar-vec2(0.0,uArtPx.y)).a;\r\nnormal=normalize(normal+vec3(-artX,artY,0.0)*2.4); // bas-relief: the stamp's edge catches the light\r\nclayBase=mix(clayBase*1.05,art.rgb*1.2,artA*.94);\r\ndiffuseColor.rgb=mix(clayBase,pnt.rgb,pnt.a);\r\nvec4 ccl=texture2D(uCell,vPlanar);float cover=pnt.a;\r\nroughnessFactor=mix(mix(roughnessFactor,.22,cover*(1.0-uDry)),.95,cover*uDry);\r\ndiffuseColor.rgb=mix(diffuseColor.rgb,vec3(.66,.55,.7),cover*uDry*.5);\r\ndiffuseColor.rgb*=1.0-.85*smoothstep(.14,.0,ccl.g)*uCrack;\r\nif(uCrumble>0.0){if(ccl.r<uCrumble)discard;diffuseColor.rgb*=1.0-.6*smoothstep(.3,.0,ccl.g);totalEmissiveRadiance+=vec3(.5,.22,.03)*smoothstep(.08,.0,ccl.r-uCrumble)*smoothstep(.3,.0,ccl.g);}`;\r\nconst ROPE_FRAG='vec2 cpl=vec2((vWxy.x-uBox.x)/uBox.z,(-vWxy.y-uBox.y)/uBox.w);vec4 ccl=texture2D(uCell,cpl);if(ccl.r<uCrumble)discard;diffuseColor.rgb*=1.0-.85*smoothstep(.14,.0,ccl.g)*uCrack;';\r\n\r\nexport function clayEffect(){\r\n let S=null;\r\n const U={uDry:{value:0},uCrack:{value:0},uCrumble:{value:0},uCell:{value:null},uArt:{value:null},uArtPx:{value:null}};\r\n\r\n function init({THREE:T,scene,mesh,paint,face,toWorld,wake}){\r\n  const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n  // The tablet fills the whole host, so the rope's space is the host's aspect (a round rope stays round); a model-shaped\r\n  // core would stamp ellipses. A resize that changes the aspect swaps in a fresh core (resize() below).\r\n  const aspectOf=()=>{const [l,t]=toWorld(0,0),[r,b]=toWorld(1,1);return Math.abs(t-b)/Math.max(1,Math.abs(r-l));};\r\n  const mkCore=a=>createClayCore({aspect:a,speed:reduced?4:1,onSecret:()=>{if(S)window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'clay'}}));}});\r\n  const core=mkCore(aspectOf());\r\n  let pos=null;mesh.traverse(o=>{if(o.isMesh){o.receiveShadow=true;pos=pos||o.geometry.attributes.position.array;}});\r\n  const hf=heightField(pos,face.w,face.h,CLAY.radius*face.w);\r\n  const makeArt=a=>{ // Sakura rim stamped at the on-screen aspect so the blossoms stay round on the stretched tablet\r\n   const AW=512,AH=Math.max(1,Math.round(AW*a)),c=document.createElement('canvas');c.width=AW;c.height=AH;paintSakuraRim(c.getContext('2d'),AW,AH);\r\n   const tex=new T.CanvasTexture(c);tex.flipY=false;tex.colorSpace=T.SRGBColorSpace;U.uArt.value=tex;U.uArtPx.value=new T.Vector2(1/AW,1/AH);return tex;\r\n  };\r\n  const artTex=makeArt(core.aspect);\r\n  const white=new T.DataTexture(new Uint8Array([255,255,255,255]),1,1);white.needsUpdate=true;U.uCell.value=white;\r\n  const box=new T.Vector4(),tones=Array.from({length:6},(_,i)=>new T.Color('#7a4b88').lerp(new T.Color('#a878b4'),i/5));\r\n  // rope: one dynamic mesh, rebuilt from core.pts (<= 3 rings per point incl. end caps)\r\n  const CAP=CLAY.maxRings*3+8,V=CAP*RV,P=new Float32Array(V*3),N=new Float32Array(V*3),C=new Float32Array(V*3),UV=new Float32Array(V*2),I=new Uint16Array(CAP*(RV-1)*6);\r\n  const geo=new T.BufferGeometry(),attrs={position:new T.BufferAttribute(P,3),normal:new T.BufferAttribute(N,3),color:new T.BufferAttribute(C,3),uv:new T.BufferAttribute(UV,2)},index=new T.BufferAttribute(I,1);\r\n  for(const k in attrs){attrs[k].setUsage(T.DynamicDrawUsage);geo.setAttribute(k,attrs[k]);}\r\n  index.setUsage(T.DynamicDrawUsage);geo.setIndex(index);geo.setDrawRange(0,0);\r\n  const bump=document.createElement('canvas');bump.width=32;bump.height=8;{const x=bump.getContext('2d'),im=x.createImageData(32,8);for(let i=0;i<256;i++){const v=128+(Math.random()-.5)*110|0;im.data.set([v,v,v,255],i*4);}x.putImageData(im,0,0);}\r\n  const bumpTex=new T.CanvasTexture(bump);bumpTex.wrapS=bumpTex.wrapT=T.RepeatWrapping;\r\n  const mat=new T.MeshStandardMaterial({color:0xffffff,vertexColors:true,roughness:.2,metalness:0,bumpMap:bumpTex,bumpScale:.8});\r\n  mat.onBeforeCompile=sh=>{\r\n   Object.assign(sh.uniforms,{uCell:U.uCell,uCrack:U.uCrack,uCrumble:U.uCrumble,uBox:{value:box}});\r\n   sh.vertexShader='varying vec2 vWxy;\\n'+sh.vertexShader.replace('#include <begin_vertex>','#include <begin_vertex>\\nvWxy=(modelMatrix*vec4(transformed,1.0)).xy;');\r\n   sh.fragmentShader='varying vec2 vWxy;uniform vec4 uBox;'+GLSL+sh.fragmentShader.replace('#include <map_fragment>','#include <map_fragment>\\n'+ROPE_FRAG);\r\n  };\r\n  mat.customProgramCacheKey=()=>'clay-rope';\r\n  const rope=new T.Mesh(geo,mat);rope.frustumCulled=false;rope.castShadow=rope.receiveShadow=true;rope.visible=false;scene.add(rope);\r\n  S={T,scene,core,mkCore,aspectOf,makeArt,hf,white,artTex,box,tones,P,N,C,UV,I,attrs,index,geo,mat,rope,bumpTex,paint,face,toWorld,wake,ver:-1,cellsFor:null,chunks:null,flakes:null,cellTex:null,sun:null,dryShown:-1};\r\n  window.myr5Clay=S.debug={get core(){return S?.core;},snapshot:()=>S?.core.snapshot()};\r\n }\r\n\r\n // Paints oldest rope that outgrew the mesh budget into the permanent paint layer (also its contact shadow).\r\n function bake(list){\r\n  const {canvas,ctx,texture}=S.paint;paintRope(ctx,list,canvas.width,canvas.height,CLAY.radius*canvas.width,{vt:S.core.vt-CLAY.life*2,dry:0});texture.needsUpdate=true; // vt back-dated so baked rope is full size\r\n }\r\n function rebuild(){\r\n  const {core,hf,P,N,C,UV,I,tones,toWorld}=S,pts=core.pts,vt=core.vt,[l,ty]=toWorld(0,0),W=toWorld(1,0)[0]-l,H=ty-toWorld(0,1)[1],top=-ty,r0=CLAY.radius*W,sc=Math.min(W/S.face.w,H/S.face.h); // sc: the wrapper's depth scale (the smaller of its x and y scales)\r\n  let rr=0,ii=0;\r\n  const emit=(cx,cy,cz,tx,ty2,r,tone,len)=>{\r\n   const nx=-ty2,ny=tx,base=rr*RV,col=tones[tone];\r\n   for(let k=0;k<RV;k++){const o=(base+k)*3;\r\n    P[o]=cx+r*CS[k]*nx;P[o+1]=cy+r*CS[k]*ny;P[o+2]=cz+r*SN[k];N[o]=CS[k]*nx;N[o+1]=CS[k]*ny;N[o+2]=SN[k];C[o]=col.r;C[o+1]=col.g;C[o+2]=col.b;UV[(base+k)*2]=len;UV[(base+k)*2+1]=k/(RV-1);}\r\n   rr++;\r\n  };\r\n  for(let a=0;a<pts.length;){\r\n   let b=a+1;while(b<pts.length&&pts[b].s===pts[a].s)b++;\r\n   const n=b-a,X=new Float32Array(n),Y=new Float32Array(n);\r\n   for(let i=0;i<n;i++){X[i]=l+pts[a+i].u*W;Y[i]=-(top+pts[a+i].v*H);}\r\n   const first=rr;let len=0;\r\n   for(let i=0;i<n;i++){\r\n    const p=pts[a+i],s=ringScale(p,vt,CLAY,i===0,i===n-1),r=r0*s,pi=Math.max(0,i-1),ni=Math.min(n-1,i+1);\r\n    let tx=X[ni]-X[pi],tyy=Y[ni]-Y[pi];const d=Math.hypot(tx,tyy)||1;tx/=d;tyy/=d;if(!(Math.hypot(X[ni]-X[pi],Y[ni]-Y[pi])>0)){tx=1;tyy=0;}\r\n    const z=sc*(hf.at(p.u,p.v)-hf.top)+.55*r;\r\n    if(i===0)emit(X[0]-tx*r0*.7,Y[0],z-.55*r,tx,tyy,0,Math.min(5,p.k*6|0),0); // end cap: a ring of radius 0 just behind the first\r\n    if(i>0)len+=Math.hypot(X[i]-X[i-1],Y[i]-Y[i-1])/(r0*4);\r\n    emit(X[i],Y[i],z,tx,tyy,r,Math.min(5,p.k*6|0),len);\r\n    if(i===n-1)emit(X[i]+tx*r*.7,Y[i]+tyy*r*.7,z-.55*r,tx,tyy,0,Math.min(5,p.k*6|0),len);\r\n   }\r\n   for(let q=first;q<rr-1;q++)for(let k=0;k<RV-1;k++){const aa=q*RV+k,bb=aa+1,cc=aa+RV,dd=cc+1;I[ii++]=aa;I[ii++]=bb;I[ii++]=cc;I[ii++]=bb;I[ii++]=dd;I[ii++]=cc;}\r\n   a=b;\r\n  }\r\n  for(const at of Object.values(S.attrs)){at.clearUpdateRanges();at.addUpdateRange(0,rr*RV*at.itemSize);at.needsUpdate=true;}\r\n  S.index.clearUpdateRanges();S.index.addUpdateRange(0,ii);S.index.needsUpdate=true;S.geo.setDrawRange(0,ii);S.rope.visible=ii>0;\r\n }\r\n function buildCells(){\r\n  const T=S.T,{core,face}=S,px=cellMap(core.cells,core.aspect),TW=128,TH=Math.round(128*core.aspect),tex=new T.DataTexture(px,TW,TH);\r\n  tex.magFilter=tex.minFilter=T.LinearFilter;tex.needsUpdate=true;S.cellTex?.dispose();S.cellTex=tex;U.uCell.value=tex;\r\n  disposeDebris();\r\n  const stripe=document.createElement('canvas');stripe.width=4;stripe.height=32; // rope coils on the chunk tops: one band per rope diameter\r\n  {const x=stripe.getContext('2d'),gr=x.createLinearGradient(0,0,0,32);gr.addColorStop(0,'#6a6a6a');gr.addColorStop(.5,'#ffffff');gr.addColorStop(1,'#6a6a6a');x.fillStyle=gr;x.fillRect(0,0,4,32);}\r\n  const tex2=new T.CanvasTexture(stripe);tex2.wrapS=tex2.wrapT=T.RepeatWrapping;tex2.repeat.set(1,1/(2*CLAY.radius));tex2.colorSpace=T.SRGBColorSpace;\r\n  const mats=[0,1,2].map(k=>new T.MeshLambertMaterial({map:tex2,color:new T.Color('#a07dae').offsetHSL(0,0,(k-1)*.05)})),side=new T.MeshLambertMaterial({color:'#b98a5e'});\r\n  S.chunks={mats,side,tex:tex2,list:core.cells.map((c,i)=>{\r\n   const shape=new T.Shape(c.poly.map(p=>new T.Vector2(p[0]-c.cx,-(p[1]-c.cy)*1)));\r\n   const m=new T.Mesh(new T.ExtrudeGeometry(shape,{depth:.02,bevelEnabled:false}),[mats[i%3],side]);m.visible=false;m.renderOrder=16;S.scene.add(m);return m;})};\r\n  const fm=new T.MeshLambertMaterial({color:'#8b6a95'}),fg=new T.BoxGeometry(1,1,.3),inst=new T.InstancedMesh(fg,fm,160);\r\n  inst.instanceMatrix.setUsage(T.DynamicDrawUsage);inst.frustumCulled=false;inst.count=0;inst.renderOrder=15;S.scene.add(inst);S.flakes={inst,fm,fg,dummy:new T.Object3D()};\r\n  S.cellsFor=core.cells;\r\n }\r\n function disposeDebris(){\r\n  if(S.chunks){for(const m of S.chunks.list){m.removeFromParent();m.geometry.dispose();}S.chunks.mats.forEach(m=>m.dispose());S.chunks.side.dispose();S.chunks.tex.dispose();S.chunks=null;}\r\n  if(S.flakes){S.flakes.inst.removeFromParent();S.flakes.inst.dispose();S.flakes.fm.dispose();S.flakes.fg.dispose();S.flakes=null;}\r\n }\r\n function clearCells(){disposeDebris();S.cellTex?.dispose();S.cellTex=null;U.uCell.value=S.white;S.cellsFor=null;}\r\n function debris(W,l,top){\r\n  const {core,chunks,flakes}=S;if(!chunks)return;\r\n  for(const k of core.debris.chunks){const m=chunks.list[k.i];m.visible=k.alive;if(!k.alive)continue;m.scale.setScalar(W);m.position.set(l+k.x*W,-(top+k.y*W),-W*.012);m.rotation.set(0,0,-k.rot);}\r\n  const f=core.debris.flakes,{inst,dummy}=flakes;inst.count=f.length;\r\n  f.forEach((q,i)=>{const s=q.size*W*Math.min(1,(2.2-q.a)*2);dummy.position.set(l+q.x*W,-(top+q.y*W),W*.004);dummy.rotation.set(q.rot*.5,q.rot*.3,q.rot);dummy.scale.set(s,s*.7,s);dummy.updateMatrix();inst.setMatrixAt(i,dummy.matrix);});\r\n  inst.instanceMatrix.needsUpdate=true;\r\n }\r\n function step(dt,now=performance.now()){\r\n  if(!S)return false;\r\n  const wall=activeDt(S.lastNow,now,dt);S.lastNow=now;\r\n  const {core,toWorld}=S,busy=core.step(dt,wall),a=core.amounts(),[l,ty]=toWorld(0,0),W=toWorld(1,0)[0]-l,H=ty-toWorld(0,1)[1];\r\n  U.uDry.value=a.dry;U.uCrack.value=a.crack;U.uCrumble.value=a.crumble;S.box.set(l,-ty,W,H);\r\n  if(core.cells&&S.cellsFor!==core.cells)buildCells();else if(!core.cells&&S.cellsFor)clearCells();\r\n  const dr=core.drain();if(dr.length)bake(dr);\r\n  if(S.ver!==core.version||(core.stage==='cover'&&!core.underway&&core.pts.length)){rebuild();S.ver=core.version;}\r\n  if(S.dryShown!==a.dry){S.dryShown=a.dry;S.mat.roughness=lerp(.2,.95,a.dry);S.mat.color.setScalar(lerp(1,1.3,a.dry));}\r\n  debris(W,l,-ty);\r\n  return busy;\r\n }\r\n function heal(){\r\n  if(!S)return;\r\n  S.core.heal();S.lastNow=null;clearCells();U.uDry.value=U.uCrack.value=U.uCrumble.value=0;S.dryShown=-1;\r\n  const {canvas,ctx,texture}=S.paint;ctx.clearRect(0,0,canvas.width,canvas.height);texture.needsUpdate=true;S.ver=-1;S.wake?.();\r\n }\r\n function dispose(){\r\n  if(!S)return;\r\n  clearCells();S.rope.removeFromParent();S.geo.dispose();S.mat.dispose();S.bumpTex.dispose();S.white.dispose();S.artTex.dispose();\r\n  if(window.myr5Clay===S.debug)delete window.myr5Clay;\r\n  S=null;\r\n }\r\n return {\r\n  id:'clay',asset:CLAY_ASSET,flip:false,background:'#241a2c',guide:null,frame:CLAY_FRAME,shadows:true,ink:false,keepColors:true,\r\n  init,dispose,step,heal,healSecret:heal,uniforms:U,fragmentDecls:GLSL+ART_GLSL,fragment:FRAG,\r\n  press(id,u,v){S.core.press(id,u,v);S.wake();},\r\n  move(id,u,v){S.core.move(id,u,v);S.wake();},\r\n  release(id){S?.core.release(id);},\r\n  claims:id=>!!S&&S.core.claims(id),\r\n  resize(){ // the generic glb board resets the sun's shadow bias after init; rope-on-rope needs a bigger one\r\n   const sun=S?.scene.children.find(o=>o.isDirectionalLight);if(sun){sun.shadow.normalBias=.8;sun.shadow.bias=-.0008;}\r\n   if(S&&Math.abs(S.aspectOf()/S.core.aspect-1)>.08){ // rotated / resized to another shape: rope space and rim art follow it (the clay dries out again)\r\n    S.core=S.mkCore(S.aspectOf());S.artTex.dispose();S.artTex=S.makeArt(S.core.aspect);heal();\r\n   }\r\n  },\r\n  snapshot:()=>S?.core.snapshot()??null,\r\n };\r\n}\r\n\r\n// --- Flat board: the same core drawn on a 2D canvas (WebGL or the GLB unavailable) -----------------------------\r\nconst SHAPE_INK={oval:'#c42a3c',x:'#e0388f',up:'#2c5cc2',down:'#2f8a4c',cross:'#4ea6da'};\r\nfunction paintTablet(g,w,h){\r\n let seed=7;const rnd=()=>(seed=seed*1664525+1013904223>>>0)/4294967296;\r\n const rr=(x,y,ww,hh,r)=>{g.beginPath();g.roundRect?g.roundRect(x,y,ww,hh,r):g.rect(x,y,ww,hh);};\r\n const base=g.createLinearGradient(0,0,w,h);base.addColorStop(0,'#cf9a6c');base.addColorStop(1,'#b27a52');\r\n rr(0,0,w,h,w*.035);g.fillStyle=base;g.fill();g.save();rr(0,0,w,h,w*.035);g.clip();\r\n for(let i=0;i<w*2.2;i++){g.fillStyle=rnd()<.5?'#7a4a30':'#e6bf92';g.globalAlpha=.05+rnd()*.07;g.beginPath();g.ellipse(rnd()*w,rnd()*h,1+rnd()*w*.03,1+rnd()*w*.02,rnd()*3,0,7);g.fill();}\r\n g.globalAlpha=1;g.lineWidth=w*.012;g.strokeStyle='#e8c79c99';rr(w*.006,w*.006,w-w*.012,h-w*.012,w*.03);g.stroke();\r\n g.strokeStyle='#6b3f2a88';g.lineWidth=w*.01;rr(w*.012,w*.012,w-w*.024,h-w*.024,w*.03);g.setLineDash([]);g.stroke();\r\n g.lineCap=g.lineJoin='round';\r\n const trace=(dx,dy)=>{for(const [id,polys] of Object.entries(SHAPES)){if(id==='line')continue;\r\n  for(const {points} of polys){g.beginPath();points.forEach(([x,y],i)=>{const [fx,fy]=fromFrame(x,y,CLAY_FRAME);g[i?'lineTo':'moveTo'](fx*w+dx,fy*h+dy);});g.stroke();}}};\r\n g.strokeStyle='#4a2a1acc';g.lineWidth=w*.011;trace(w*.002,w*.003);g.strokeStyle='#f0d2a888';g.lineWidth=w*.004;trace(-w*.002,-w*.002);\r\n for(const [id,polys] of Object.entries(SHAPES)){if(id==='line')continue;g.strokeStyle=SHAPE_INK[id]||'#4ea6da';g.lineWidth=w*.005;\r\n  for(const {points} of polys){g.beginPath();points.forEach(([x,y],i)=>{const [fx,fy]=fromFrame(x,y,CLAY_FRAME);g[i?'lineTo':'moveTo'](fx*w,fy*h);});g.stroke();}}\r\n paintSakuraRim(g,w,h,{shadow:true});\r\n g.restore();\r\n}\r\nexport function createFlatClay(host){\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches,dpr=Math.min(devicePixelRatio||1,2);\r\n let disposed=false,raf=0,last=0,lastWall=null,W=1,H=1,fw=1,fh=1,holes=[],tab,cov,scn,lastLayout='',shown=-1,core=null;\r\n const pointers=new Map();\r\n // The tablet fills the host, so the rope's space has the host's aspect (a round rope stays round); layout() swaps in a fresh core when it changes.\r\n const mkCore=aspect=>createClayCore({aspect,cfg:{...CLAY,maxRings:500,bake:250},speed:reduced?4:1,onSecret:()=>{if(!disposed)window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'clay'}}));}});\r\n const canvas=document.createElement('canvas'),g=canvas.getContext('2d'),mk=()=>document.createElement('canvas');\r\n canvas.className='portal-board-canvas';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='position:absolute;inset:0;display:block;width:100%;height:100%';host.append(canvas);\r\n const face=()=>boardLayout(W,H);\r\n const rect=()=>{const b=host.getBoundingClientRect(),f=face(),sx=b.width/W,sy=b.height/H;return {left:b.left+f.left*sx,top:b.top+f.top*sy,width:f.width*sx,height:f.height*sy};};\r\n const px=(c,w,h)=>{c.width=Math.max(1,Math.round(w*dpr));c.height=Math.max(1,Math.round(h*dpr));const x=c.getContext('2d');x.setTransform(dpr,0,0,dpr,0,0);return x;};\r\n function layout(){\r\n  W=Math.max(1,host.clientWidth);H=Math.max(1,host.clientHeight);const f=face(),key=`${W}x${H}`;if(key===lastLayout)return;lastLayout=key;\r\n  fw=f.width;fh=f.height;canvas.width=Math.round(W*dpr);canvas.height=Math.round(H*dpr);\r\n  for(const k in f)host.style.setProperty('--face-'+k,f[k]+'px');\r\n  const a=fh/fw,fresh=!core||Math.abs(a/core.aspect-1)>.08;\r\n  if(fresh){core=mkCore(a);pointers.clear();shown=-1;lastWall=null;} // rotated / reshaped: the clay dries out again\r\n  const old=fresh?null:cov;tab=mk();paintTablet(px(tab,fw,fh),fw,fh);cov=mk();const cx=px(cov,fw,fh);if(old)cx.drawImage(old,0,0,fw,fh);scn=mk();px(scn,fw,fh);\r\n }\r\n const poly=(c,p,w,dx=0,dy=0)=>{c.beginPath();p.forEach(([x,y],i)=>c[i?'lineTo':'moveTo'](x*w+dx,y*w+dy));c.closePath();};\r\n function draw(){\r\n  if(disposed||!g)return;\r\n  layout();\r\n  const dr=core.drain();if(dr.length)paintRope(cov.getContext('2d'),dr,fw,fh,CLAY.radius*fw,{vt:core.vt-CLAY.life*2});\r\n  const a=core.amounts(),s=scn.getContext('2d');\r\n  s.setTransform(dpr,0,0,dpr,0,0);s.clearRect(0,0,fw,fh);s.drawImage(tab,0,0,fw,fh);s.drawImage(cov,0,0,fw,fh);\r\n  paintRope(s,core.pts,fw,fh,CLAY.radius*fw,{vt:core.vt,dry:a.dry});\r\n  if(a.crack>0&&core.cells){s.strokeStyle=`rgba(20,8,26,${.85*a.crack})`;s.lineWidth=1.6;s.lineJoin='round';for(const c of core.cells){poly(s,c.poly,fw);s.stroke();}}\r\n  if(core.debris.chunks.length){s.globalCompositeOperation='destination-out';s.fillStyle='#000';for(const k of core.debris.chunks){poly(s,core.cells[k.i].poly,fw);s.fill();}s.globalCompositeOperation='source-over';}\r\n  const f=face();g.setTransform(dpr,0,0,dpr,0,0);g.clearRect(0,0,W,H);g.drawImage(scn,f.left,f.top,fw,fh);\r\n  for(const k of core.debris.chunks){if(!k.alive)continue;const c=core.cells[k.i];g.save();g.translate(f.left+k.x*fw,f.top+k.y*fw);g.rotate(k.rot);poly(g,c.poly.map(p=>[p[0]-c.cx,p[1]-c.cy]),fw);g.fillStyle='#9b7aa8';g.fill();g.lineWidth=2;g.strokeStyle='#b98a5e';g.stroke();g.restore();}\r\n  g.fillStyle='#8b6a95';for(const q of core.debris.flakes){g.save();g.translate(f.left+q.x*fw,f.top+q.y*fw);g.rotate(q.rot);const z=q.size*fw*Math.min(1,(2.2-q.a)*2);g.fillRect(-z/2,-z/3,z,z*.7);g.restore();}\r\n  if(holes.length){g.globalCompositeOperation='destination-out';g.fillStyle='#000';for(const h of holes){g.beginPath();h.forEach(([u,v],i)=>g[i?'lineTo':'moveTo'](f.left+u*f.width,f.top+v*f.height));g.closePath();g.fill();}g.globalCompositeOperation='source-over';}\r\n }\r\n function tick(now){raf=0;if(disposed)return;const dt=Math.min(1/30,Math.max(1/240,(now-last)/1000)),busy=core.step(dt,activeDt(lastWall,now,dt));last=lastWall=now;\r\n  if(shown!==core.version||core.stage!=='cover'||(!core.underway&&core.pts.length)){shown=core.version;draw();} // a frozen cover (underway, nobody scribbling) needs no redraw\r\n  if(busy)raf=requestAnimationFrame(tick);}\r\n const wake=()=>{if(!raf&&!disposed){last=performance.now();raf=requestAnimationFrame(tick);}};\r\n const idleClock=()=>{lastWall=null;}; // pause/heal: the gap is not active time\r\n const observer=new ResizeObserver(()=>{layout();draw();wake();});observer.observe(host);layout();draw();\r\n const uv=(x,y)=>{const r=rect();return [Math.min(1,Math.max(0,(x-r.left)/r.width)),Math.min(1,Math.max(0,(y-r.top)/r.height))];};\r\n const heal=()=>{core.heal();idleClock();cov.getContext('2d').clearRect(0,0,fw,fh);holes=[];shown=-1;draw();wake();};\r\n return {\r\n  canvas,background:'#241a2c',ink:false,faceRect:rect,\r\n  patternRect(){const r=rect(),F=CLAY_FRAME;return {left:r.left+r.width*F.x0,top:r.top+r.height*F.y0,width:r.width*(F.x1-F.x0),height:r.height*(F.y1-F.y0)};},\r\n  press(id,x,y){const [u,v]=uv(x,y);if(pointers.has(id))core.move(id,u,v);else{pointers.set(id,1);core.press(id,u,v);}wake();},\r\n  release(id){if(pointers.delete(id)){core.release(id);wake();}},\r\n  claims:id=>core.claims(id),\r\n  cut(polyUv,color,ms=1100){heal();holes=[polyUv];draw();return new Promise(done=>setTimeout(done,reduced?0:ms));},\r\n  heal,frameMs:()=>0,setTint(){},\r\n  pause(){for(const id of pointers.keys())core.release(id);pointers.clear();cancelAnimationFrame(raf);raf=0;idleClock();},\r\n  resume(){if(disposed)return;lastLayout='';layout();draw();wake();},\r\n  dispose(){disposed=true;cancelAnimationFrame(raf);observer.disconnect();canvas.remove();},\r\n  snapshot:()=>core.snapshot(),flat:true,get core(){return core;},\r\n };\r\n}\r\n\r\n// Entry point (same signature as the other boards). Falls back to the flat board when flat is asked for, or when the GLB\r\n// or WebGL is unavailable (createGlbBoard tears itself down before it throws).\r\nexport async function createClayBoard(host,{flat=false}={}){\r\n if(!flat){\r\n  try{const effect=clayEffect(),board=await createGlbBoard(host,{effect});board.snapshot=effect.snapshot;return board;}\r\n  catch(error){console.warn('Clay board: 3D unavailable, using the flat board.',error);}\r\n }\r\n return createFlatClay(host);\r\n}\r\n","modules/portal/portal-board-cogs.mjs":"// Pre-sectioned mechanical metal grimoire. Existing kit gears, shared portal shapes.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {orientMatrix,pointInPolygon,GLB} from './portal-board-glb.mjs';\r\nimport {MECHANICAL_FRAME,mechanicalPaths,releasePaths,sectionPanels,mechanicalLayout,mechanismLayout,pointOnPath,signedRailImpulse} from './portal-mechanical-layout.mjs';\r\n\r\nexport const LAYOUT='/pod/worlds/boards/cogs/door-layout.json';\r\nconst PARTS='/pod/worlds/boards/cogs/parts-kit.glb';\r\n// The same inset shape frame is shared by the metal assemblies and 2D fallback.\r\nexport const DEFAULT_FRAME=MECHANICAL_FRAME;\r\nconst HUB_NODE='m19';\r\nconst TAU=Math.PI*2,FRICTION=1.6,REST=.02,TAP_IMPULSE=4,MOVE_EPS=.006;\r\n// ponytail: hard speed cap, not a torque/inertia model — keeps a wild flick from strobing the spokes.\r\nconst MAX_OMEGA=12;\r\n// Valves idle at VALVE_IDLE rad/s in the (single, shared) train direction plus VALVE_RATIO of its\r\n// omega, but (see valveActive in step()) only while the train or a finger is actually active.\r\n// ponytail: every valve and every gear tree shares one idle direction/rate rather than each valve\r\n// following its own nearest tree — give valves a `drives`-like link to their tree if two trains ever\r\n// spin opposite and it reads wrong.\r\nconst VALVE_IDLE=.25,VALVE_RATIO=.15,LIGHT_COLOR='#c59a56';\r\nconst LAMP_CORE=new THREE.Color('#ffd9a0'),LAMP_OFF=new THREE.Color('#3d2f14'),lampOn=new THREE.Color(),lampTmp=new THREE.Color();\r\nlet selectedTint='#c59a56',selectedBackplateTint='#263943',weldTrace=null;\r\n// Retained for existing callers; rebuilt metal uses its independent backplate tint.\r\nexport function doorTone(hex){\r\n const n=parseInt(hex.slice(1),16),r=(n>>16&255)/255,g=(n>>8&255)/255,b=(n&255)/255,mx=Math.max(r,g,b),mn=Math.min(r,g,b),l=(mx+mn)/2,d=mx-mn;\r\n let h=0;if(d){h=mx===r?((g-b)/d+6)%6:mx===g?(b-r)/d+2:(r-g)/d+4;h/=6;}\r\n const s=d?d/(1-Math.abs(2*l-1)):0,H=(h+.5)%1,S=s*.5,L=Math.min(l,.5)*.75,a=S*Math.min(L,1-L);\r\n const f=k=>{const t=(k+H*12)%12;return Math.round(255*(L-a*Math.max(-1,Math.min(t-3,9-t,1))));};\r\n return '#'+[f(0),f(8),f(4)].map(v=>v.toString(16).padStart(2,'0')).join('');\r\n}\r\nconst validTint=hex=>typeof hex==='string'&&/^#[0-9a-f]{6}$/i.test(hex);\r\n\r\n// All touch-reactive tuning in one place. Pipes wiggle as a damped torsion spring, angle'' =\r\n// -w0^2*angle - damping*angle' (w0 = 2*pi*wiggleFreqHz): underdamped, so a kick visibly oscillates a\r\n// few times before decaying under wiggleRestAngle/wiggleRestOmega (~1s at the defaults below).\r\n// Lights sit at lightOff until a finger comes within lightRadius (face-width fraction), ramp to\r\n// lightOn over lightRampMs, hold while near and for lightHoldMs after, then fade back over\r\n// lightFadeMs.\r\nexport const KNOBS={\r\n wiggleKick:5,          // rad/s added per (face-width/s) of sideways finger speed, per move event\r\n wiggleTapKick:.6,      // rad/s added by a plain tap on a pipe (no motion to read a speed from)\r\n wiggleMargin:.02,      // face-width fraction added to a pipe's r for \"near enough to react\"\r\n wiggleFreqHz:6,\r\n wiggleDamping:8,       // x'' + damping*x' + w0^2*x = 0\r\n wiggleMaxAngle:10*Math.PI/180, // clamp so repeated re-excites can't wind past this\r\n wiggleBob:.02,         // z bob at full amplitude, as a fraction of face width\r\n wiggleRestAngle:.004,  // rad — below this and wiggleRestOmega, a pipe counts as settled\r\n wiggleRestOmega:.05,   // rad/s\r\n lightRadius:.12,\r\n lightRampMs:120,\r\n lightHoldMs:800,\r\n lightFadeMs:1200,\r\n lightOff:0,\r\n lightOn:1.6,\r\n lightFlicker:.06,      // +/- fraction of lightOn while fully lit\r\n hubIdleAccel:.6,       // rad/s^2 \"motor\" on each hub gear, balanced by FRICTION into a steady idle\r\n                         // spin (equilibrium omega ~= hubIdleAccel/FRICTION, ~.25-.4 rad/s here)\r\n // Welding trail: sizes are given in SCREEN px (so they read at a glance on a phone) and converted to\r\n // canvas px each draw via pxScale() — the live canvas-to-screen scale (measured off the rendered\r\n // <canvas> + the same fit math portal-board-glb.mjs's computeFit() uses) — rather than baked to a\r\n // fixed texture-px size. Sparks fly at \"screen px/s\" so a frame's streak (speed*dt) reads as roughly\r\n // sparkSpeedMinPx..MaxPx/60 wide at 60fps. Timeline: hot trail .5s (weldHotMs, held near-full for\r\n // weldHotHold of that then cooling), then the bead holds (weldBeadHold of weldBeadMs) and fades —\r\n // total 2.5s end to end. The seam sits on the plate face, with raised gears occluding it.\r\n sparkSpeedMinPx:300,sparkSpeedMaxPx:1100, // screen px/s, radial spray (slow ones dribble, fast ones fly)\r\n sparkGravityPx:1500,                       // screen px/s^2, downward: sparks arc over and fall\r\n sparkLifeMin:.35,sparkLifeMax:.9,          // s — the wider range reads as \"some fly further\"\r\n sparkWidthPx:2.4,                          // screen px, streak stroke width (thin incandescent wire)\r\n sparkStreakS:.02,                          // s of velocity drawn behind each spark: its motion-blur streak\r\n sparkBounce:.35,                           // fraction of sparks that hit a \"floor\" below the torch and skitter\r\n sparkFloorMinPx:30,sparkFloorMaxPx:110,    // screen px below the torch where a bouncing spark lands\r\n sparkBounceKeep:.38,                       // vertical speed kept on the bounce (horizontal keeps .8)\r\n sparkPressCount:24,sparkReleaseCount:6,sparkIdleCount:4,sparkIdleMs:45, // press flare spray; trickle while held\r\n sparkPerFrac:.004,sparkMoveMax:12,         // sparks per face-width-fraction of travel per move event\r\n sparkMax:200,                              // ponytail: hard cap on live sparks\r\n torchCorePx:4.5,            // screen px, white-hot core radius at the contact point while a finger is down\r\n torchBloomPx:20,          // screen px, blue-white -> warm bloom around the core\r\n torchFlareMs:220,         // the press flare: core+bloom swell this long after touch-down\r\n torchFlareScale:2.2,      // how much bigger the bloom is at the instant of the press\r\n tintHaloMix:.22,          // the selected tint only tints the outer halo of the hot trail, this much\r\n weldStep:.016,            // face-width fraction: trail-point spacing\r\n weldHotMs:500,            // hot-glow portion of the trail\r\n weldHotHold:.35,           // fraction of weldHotMs held near-full brightness before it starts cooling\r\n weldBeadMs:2000,           // bead hold (weldBeadHold) + fade after the trail cools\r\n weldBeadHold:.625,         // 1.25s hold followed by .75s fade\r\n weldTrailWidthPx:3,        // screen px, hot-trail core stroke width\r\n weldTrailHaloPx:9,        // screen px, hot-trail bloom halo width\r\n weldBeadRadiusPx:2.5,      // screen px, bead half-width (~5px seam)\r\n weldRipplePx:3.8,          // screen px between \"stack of dimes\" ripples along the bead\r\n weldHaloRadiusPx:5.5,       // screen px, heat-tint band (straw next to the bead -> blue outside) half-width\r\n weldCap:260,               // ponytail: hard cap on stored trail points\r\n};\r\n// The tint picker recolours the kit parts (cogs, lamps, pipes...) as anodized metal (see metalFinish).\r\nexport const METAL={metalness:.92,roughness:.3,envIntensity:1.25};\r\n// Portal release moves whole prebuilt plate assemblies with the existing timing and tilt.\r\nconst FALL_MS=1100,FALL_TILT=35*Math.PI/180;\r\n\r\n// --- Pure train kinematics (unit-tested; no THREE/DOM) --------------------------------------\r\n// Walks a gear's `drives` chain to the train's root and returns {root,ratio} such that, once\r\n// settled, gears[i].omega === ratio*gears[root].omega (each mesh flips sign: ratio *= -r[parent]/r[self]).\r\nfunction rootRatio(gears,i){\r\n let idx=i,ratio=1;\r\n while(gears[idx].drives!=null){const p=gears[idx].drives;ratio*=gears[idx].driveRatio??(-gears[p].r/gears[idx].r);idx=p;}\r\n return {root:idx,ratio};\r\n}\r\n// Adds torque/impulse `omega` at gear i, converted to the equivalent change at the train's root —\r\n// a rigidly meshed train shares one momentum, so nudging any gear nudges the whole train.\r\nexport function applyTorque(gears,i,omega){\r\n const {root,ratio}=rootRatio(gears,i);\r\n const r=gears[root];\r\n r.omega=Math.max(-MAX_OMEGA,Math.min(MAX_OMEGA,r.omega+omega/ratio));\r\n}\r\n// Friction on the roots only (drives==null), then every meshed gear's speed is re-derived from its\r\n// parent and angles integrate. Returns true while the train is still moving fast enough to render.\r\nexport function stepTrain(gears,dt){\r\n for(const g of gears)if(g.drives==null)g.omega*=Math.exp(-dt*FRICTION);\r\n for(const g of gears){\r\n  if(g.drives!=null){const p=gears[g.drives];g.omega=p.omega*(g.driveRatio??(-p.r/g.r));}\r\n  g.angle+=g.omega*dt;\r\n }\r\n return gears.some(g=>Math.abs(g.omega)>REST);\r\n}\r\n// Hub idle motor: every gear with an idleDir is pushed along it. Pure (tested): the board calls it each frame\r\n// unless prefers-reduced-motion is on. Returns true when it is pushing, so the render loop stays awake.\r\nexport function idleSpin(gears,dt,accel,reduced){\r\n if(reduced)return false;let on=false;\r\n for(const g of gears)if(g.idleDir){g.omega+=g.idleDir*accel*dt;on=true;}\r\n return on;\r\n}\r\n// Layout parts -> a physics gears array indexed 0..n over just the kind:'gear' entries, with each\r\n// one's `drives` (a parts-array index) remapped to a gear-array index (absent/non-gear -> its own\r\n// root). No tooth data in the layout (real kit models, not procedural teeth), so a gear's initial\r\n// angle is just its `rot`; the train settles its own phase from there once it's nudged.\r\nexport function gearsFromLayout(parts){\r\n const idx=[];parts.forEach((p,i)=>{if(p.kind==='gear')idx.push(i);});\r\n const at=new Map(idx.map((partI,gi)=>[partI,gi]));\r\n return idx.map(partI=>{\r\n  const p=parts[partI];\r\n  return {u:p.u,v:p.v,r:p.r,drives:p.drives!=null?(at.get(p.drives)??null):null,angle:(p.rot||0)*Math.PI/180,omega:0,driveRatio:p.driveRatio};\r\n });\r\n}\r\n// Nearest gear (layout circle: centre u,v, radius r*face.w) to (u,v), skipping any index in `skip`\r\n// (gone with a falling cut piece); -1 if none is under the point.\r\nexport function hitGear(gears,face,u,v,skip){\r\n let best=-1,bestT=1;\r\n for(let i=0;i<gears.length;i++){\r\n  if(skip?.has(i))continue;\r\n  const g=gears[i],dx=(u-g.u)*face.w,dy=(v-g.v)*face.h,t=Math.hypot(dx,dy)/(g.r*face.w);\r\n  if(t<1&&t<bestT){bestT=t;best=i;}\r\n }\r\n return best;\r\n}\r\n// Dressing keep-out (pure, tested): drops layout parts whose circle (r+.012) comes within `clear` face-widths of any shape\r\n// path, or that overlap a keep-out circle {u,v,r} / cable segment {a,b,r}. Distances are in face-width units.\r\nexport function dressingKeep(parts,paths,face,circles=[],segments=[],clear=.012){\r\n return parts.filter(p=>{\r\n  const R=p.r+.012,hit=pointOnPath(paths,face,p.u,p.v);\r\n  if(hit&&hit.distance-R<clear)return false;\r\n  for(const c of circles)if(Math.hypot((p.u-c.u)*face.w,(p.v-c.v)*face.h)<c.r+p.r)return false;\r\n  for(const s of segments){\r\n   const dx=(s.b[0]-s.a[0])*face.w,dy=(s.b[1]-s.a[1])*face.h,l2=dx*dx+dy*dy||1,t=Math.max(0,Math.min(1,((p.u-s.a[0])*face.w*dx+(p.v-s.a[1])*face.h*dy)/l2));\r\n   if(Math.hypot((p.u-s.a[0])*face.w-dx*t,(p.v-s.a[1])*face.h-dy*t)<s.r+p.r)return false;\r\n  }\r\n  return true;\r\n });\r\n}\r\n// Parts that fail dressingKeep where the old door put them are slid to the nearest free spot (spiral search, then shrunk\r\n// to 70%/50%) so the panels fill up even though the shape lines own most of the old positions. Never overlaps an\r\n// already-placed part. Returns the placed parts (moved ones carry the new u,v,r).\r\nexport function dressingPlace(parts,paths,face,circles=[],segments=[],clear=.012,reach=.16){\r\n const placed=[];\r\n for(const p of parts)for(const k of [1,.7,.5]){\r\n  const q=dressingKeepAt(p,k,paths,face,circles.concat(placed.map(o=>({u:o.u,v:o.v,r:o.r}))),segments,clear,reach);\r\n  if(q){placed.push(q);break;}\r\n }\r\n return placed;\r\n}\r\nfunction dressingKeepAt(p,k,paths,face,circles,segments,clear,reach){\r\n const r=p.r*k;\r\n for(let d=0;d<=reach;d+=.012)for(let n=d?Math.round(d/.012)*6:1,i=0;i<n;i++){\r\n  const a=i/n*TAU,q={...p,r,u:p.u+Math.cos(a)*d,v:p.v+Math.sin(a)*d*face.w/face.h};\r\n  const ry=r*face.w/face.h;if(q.u-r<.012||q.u+r>.988||q.v-ry<.008||q.v+ry>.992)continue; // keep whole part on the door\r\n  if(dressingKeep([q],paths,face,circles,segments,clear).length)return q;\r\n }\r\n return null;\r\n}\r\n// Nearest pipe (by centre) within r+margin of (u,v) — pipe equivalent of hitGear above, but with a\r\n// margin since a pipe should react to a finger passing near it, not only dead-centre on it.\r\nexport function nearestPipe(pipes,face,u,v,margin){\r\n let best=-1,bestD=Infinity;\r\n for(let i=0;i<pipes.length;i++){\r\n  const p=pipes[i],dx=(u-p.u)*face.w,dy=(v-p.v)*face.h,d=Math.hypot(dx,dy),lim=(p.r+margin)*face.w;\r\n  if(d<lim&&d<bestD){bestD=d;best=i;}\r\n }\r\n return best;\r\n}\r\n// One step of a pipe's damped torsion spring (angle'' = -w0^2*angle - damping*angle'), clamped to\r\n// +-maxAngle so several quick re-excites can't wind it up past that.\r\nexport function wiggleStep(angle,omega,dt,freqHz,damping,maxAngle){\r\n const w0=TAU*freqHz;\r\n omega+=(-w0*w0*angle-damping*omega)*dt;\r\n angle+=omega*dt;\r\n if(angle>maxAngle){angle=maxAngle;if(omega>0)omega=0;}\r\n else if(angle<-maxAngle){angle=-maxAngle;if(omega<0)omega=0;}\r\n return {angle,omega};\r\n}\r\n// Angular kick for a pipe from a finger's raw per-event displacement (vx,vy, face-plane x/y — no\r\n// timestamp between move events to divide by, the same \"impulse per event\" approximation the gear\r\n// drag in move() already uses): the component of that motion across the pipe's long axis (rot),\r\n// signed so the pipe swings away from the swipe.\r\nexport function wiggleKick(vx,vy,rot,gain){\r\n return -gain*(-Math.sin(rot)*vx+Math.cos(rot)*vy);\r\n}\r\n// A valve's own dwell angle: idles at VALVE_IDLE in the train's shared direction, plus a small share\r\n// of its raw speed, alternating direction per valve (dir = ±1).\r\nexport function valveAngle(angle,dt,dir,valveDir,rootOmega){\r\n return (angle+dir*(VALVE_IDLE*valveDir+VALVE_RATIO*rootOmega)*dt)%TAU;\r\n}\r\n// A light's on/off level (0..1), stepped one dt toward `target` (1 near, 0 not) — ramping up over\r\n// rampMs, fading down over fadeMs. The \"how long since it was last near\" shape comes from the caller\r\n// holding target at 1 for a while after the finger leaves (see nearUntil in step()).\r\nexport function lightLevel(level,target,dt,rampMs,fadeMs){\r\n const rate=1000/(target>level?rampMs:fadeMs);\r\n return target>level?Math.min(target,level+rate*dt):Math.max(target,level-rate*dt);\r\n}\r\n// Lock-progress helpers (pure, tested). p = fraction of a path's length traced; n = stations on that path.\r\nexport const boltOut=(p,station,n)=>Math.max(0,Math.min(1,p*n-station));\r\nexport const pistonExt=(stroke,angle)=>stroke*(.5+.5*Math.sin(angle));\r\nexport const lampLit=(p,station,n)=>p>0&&p>=station/n-1e-9;\r\n// Fraction (0..1) of a polyline's length at the point (u,v) lying on segment `segment` (pointOnPath's hit).\r\nexport function pathProgress(points,face,segment,u,v){\r\n let before=0,total=0;\r\n for(let i=1;i<points.length;i++){const l=Math.hypot((points[i][0]-points[i-1][0])*face.w,(points[i][1]-points[i-1][1])*face.h);if(i-1<segment)before+=l;total+=l;}\r\n const a=points[segment];return Math.min(1,(before+Math.hypot((u-a[0])*face.w,(v-a[1])*face.h))/(total||1));\r\n}\r\n\r\n// Weld-heat colour ramp (0=freshest): white-hot -> yellow -> orange -> dull red, as {r,g,b} 0..255.\r\nconst HEAT_STOPS=[[1,1,.9],[1,.82,.24],[1,.47,.08],[.47,.14,.04]];\r\nexport function heatColor(f){\r\n f=Math.max(0,Math.min(1,f));\r\n const n=HEAT_STOPS.length-1,x=f*n,i=Math.min(n-1,Math.floor(x)),t=x-i,[r0,g0,b0]=HEAT_STOPS[i],[r1,g1,b1]=HEAT_STOPS[i+1];\r\n return {r:Math.round((r0+(r1-r0)*t)*255),g:Math.round((g0+(g1-g0)*t)*255),b:Math.round((b0+(b1-b0)*t)*255)};\r\n}\r\n// A weld bead's alpha at `age` ms since its trail point was laid: 0 until the trail cools (hotMs), 1\r\n// through a hold window (holdFrac of beadMs), then fading to 0 by hotMs+beadMs.\r\nexport function weldBeadAlpha(age,hotMs,beadMs,holdFrac){\r\n if(age<hotMs||age>hotMs+beadMs)return 0;\r\n const bf=(age-hotMs)/beadMs;\r\n return bf<holdFrac?1:Math.max(0,1-(bf-holdFrac)/(1-holdFrac));\r\n}\r\n\r\n// Linear 0..1 blend of the heat colour with the tint, as an rgba() string: the hot trail's outer halo only.\r\nfunction haloRGBA(f,tint,mix,alpha){\r\n const {r,g,b}=heatColor(f);if(!tint)return `rgba(${r},${g},${b},${Math.max(0,alpha)})`;\r\n const n=parseInt(tint.slice(1),16),m=a=>Math.round(a);\r\n return `rgba(${m(r+((n>>16)-r)*mix)},${m(g+(((n>>8)&255)-g)*mix)},${m(b+((n&255)-b)*mix)},${Math.max(0,alpha)})`;\r\n}\r\n\r\n// --- Metal finish for the kit parts --------------------------------------------------------------\r\n// One shared tint/mix uniform pair for every kit material: the selected tint turns the parts' baked\r\n// brass/copper albedo into luminance x tint (anodized metal), with real metalness/roughness and a small\r\n// studio env map so the metal has something to reflect. No tint -> mix 0 and the original PBR values,\r\n// i.e. the stock look.\r\nconst uMetalTint={value:new THREE.Color(1,1,1)},uMetalMix={value:0};\r\nlet envTex=null;\r\n// A tiny equirect \"studio\": dark floor, grey horizon, bright sky and two softbox stripes for highlights.\r\nfunction studioEnv(){\r\n if(envTex)return envTex;\r\n const c=document.createElement('canvas');c.width=128;c.height=64;const x=c.getContext('2d');\r\n const g=x.createLinearGradient(0,0,0,64);g.addColorStop(0,'#f4f6fa');g.addColorStop(.42,'#8a9098');g.addColorStop(.55,'#32353b');g.addColorStop(1,'#08090b');\r\n x.fillStyle=g;x.fillRect(0,0,128,64);x.fillStyle='#ffffff';x.fillRect(18,8,10,22);x.fillRect(80,4,22,9);x.fillRect(46,10,16,14);x.fillRect(104,14,8,18);\r\n envTex=new THREE.CanvasTexture(c);envTex.mapping=THREE.EquirectangularReflectionMapping;envTex.colorSpace=THREE.SRGBColorSpace;\r\n return envTex;\r\n}\r\nfunction metalFinish(mat){\r\n if(mat.userData.cogsMetal)return;\r\n mat.userData.cogsMetal={metalness:mat.metalness,roughness:mat.roughness,metalnessMap:mat.metalnessMap,roughnessMap:mat.roughnessMap,envMap:mat.envMap,envMapIntensity:mat.envMapIntensity};\r\n mat.onBeforeCompile=shader=>{\r\n  shader.uniforms.uMetalTint=uMetalTint;shader.uniforms.uMetalMix=uMetalMix;\r\n  shader.fragmentShader='uniform vec3 uMetalTint;\\nuniform float uMetalMix;\\n'+shader.fragmentShader\r\n   .replace('#include <map_fragment>','#include <map_fragment>\\n{float ml=dot(diffuseColor.rgb,vec3(.2126,.7152,.0722));diffuseColor.rgb=mix(diffuseColor.rgb,uMetalTint*clamp(.35+1.4*ml,0.,1.2),uMetalMix);}')\r\n   .replace('#include <emissivemap_fragment>','#include <emissivemap_fragment>\\n#ifdef USE_EMISSIVEMAP\\ntotalEmissiveRadiance=mix(totalEmissiveRadiance,emissive*(.4+dot(emissiveColor.rgb,vec3(.2126,.7152,.0722))),uMetalMix);\\n#endif');\r\n };\r\n mat.customProgramCacheKey=()=>'cogs-metal';mat.needsUpdate=true;\r\n}\r\nfunction applyMetal(mats){\r\n const on=!!selectedTint;uMetalMix.value=on?1:0;if(on)uMetalTint.value.set(selectedTint);\r\n for(const mat of mats){\r\n  const o=mat.userData.cogsMetal;if(!o)continue;\r\n  if(mat.userData.cogsOn!==on){mat.userData.cogsOn=on;mat.needsUpdate=true;} // maps/env swap: recompile once\r\n  Object.assign(mat,on?{metalness:METAL.metalness,roughness:METAL.roughness,metalnessMap:null,roughnessMap:null,envMap:studioEnv(),envMapIntensity:METAL.envIntensity}:o);\r\n }\r\n}\r\n\r\n// --- Effect glue -------------------------------------------------------------------------------\r\nlet S=null; // per-instance state; one portal board is ever active at once\r\nexport function setCogsTint(hex,selected=true){\r\n if(!validTint(hex))return;\r\n selectedTint=hex;\r\n if(S){for(const part of S.parts)if(part.kind==='light')part.mat.emissive.set(selectedTint||LIGHT_COLOR);applyMetal(S.mats);S.tint=selectedTint;S.wake?.();}\r\n if(weldTrace){weldTrace.tint=selectedTint;weldTrace.wake?.();}\r\n}\r\n\r\n// One GLTFLoader promise per file, so every slot using a kit shares a single load.\r\nconst models=new Map();\r\nconst loadModel=url=>{let pending=models.get(url);if(!pending){pending=new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(url).catch(error=>{models.delete(url);throw error;});models.set(url,pending);}return pending;};\r\nexport function setCogsBackplateTint(hex,selected=true){\r\n if(selected&&!validTint(hex))return;selectedBackplateTint=selected?hex:'#263943';\r\n if(S){S.backplateMaterial?.color.set(selectedBackplateTint);S.wake?.();}\r\n}\r\nfunction kitMesh(gltf,name){\r\n gltf.scene.updateMatrixWorld(true);\r\n let src=null;(name?gltf.scene.getObjectByName(name):gltf.scene)?.traverse(o=>{if(o.isMesh&&!src)src=o;});\r\n if(!src)throw new Error('no mesh '+(name||''));\r\n return src;\r\n}\r\n// A mesh sharing src's geometry/material, fitted by its matrix: thin axis -> +Z (if orient), centred\r\n// on its bbox, face-plane bbox radius = radius. Returns it with its fitted depth.\r\nfunction fitModel(src,radius,orient){\r\n const box=new THREE.Box3().setFromObject(src),rot=new THREE.Matrix4();\r\n if(orient){const R=orientMatrix(box.getSize(new THREE.Vector3()));rot.set(...R[0],0,...R[1],0,...R[2],0,0,0,0,1);box.applyMatrix4(rot);}\r\n const size=box.getSize(new THREE.Vector3()),k=radius/(Math.max(size.x,size.y)/2),c=box.getCenter(new THREE.Vector3());\r\n const m=new THREE.Mesh(src.geometry,src.material);m.frustumCulled=false;m.matrixAutoUpdate=false;\r\n m.matrix.makeScale(k,k,k).multiply(new THREE.Matrix4().makeTranslation(-c.x,-c.y,-c.z)).multiply(rot).multiply(src.matrixWorld);\r\n return {m,depth:size.z*k};\r\n}\r\n// Builds every layout entry as a Mesh (kit meshes lie flat, face at +Z — no thin-axis turn, same as\r\n// the old props): positioned on the door's front face, z-stacked by layer, rotated by rot. Returns\r\n// {parts,gearPivots}: parts for cut/heal (every kind); gearPivots indexed like gearsFromLayout's\r\n// output (undefined slot = that node failed to load; still driven, just invisible) for the physics\r\n// rotation each frame. ponytail: one Mesh per entry (draw calls ~= entry count, ~150-250 here); if\r\n// the pane ever shows frame drops, switch the static kinds (pipe/screw/plate/valve/light) to one\r\n// InstancedMesh per shared node — gears alone would still need their own objects to spin independently.\r\nfunction buildParts(parent,gltf,list,front,fw,fh,mats){\r\n const parts=[],gearPivots=[],batches=new Map();let gi=0;\r\n list.forEach((p,i)=>{\r\n  const isGear=p.kind==='gear';\r\n  let src;\r\n  try{src=kitMesh(gltf,p.node);}\r\n  catch(e){console.warn('portal-board-cogs: missing node',p.node,e);if(isGear)gi++;return;}\r\n  const x=(p.u-.5)*fw,y=(.5-p.v)*fh,{m,depth}=fitModel(src,p.r*fw,true),pivot=new THREE.Group();\r\n  const rot=(p.rot||0)*Math.PI/180;\r\n  const part={...p,pivot,rot,angle:0,omega:0};\r\n  pivot.position.set(x,y,front+(p.layer||0)*.006*fw+depth/2);\r\n  part.z0=pivot.position.z;part.top=part.z0+depth/2; // rest z; pipes bob off this and back\r\n  if(p.kind==='light'){\r\n   // A previous board may have left the shared kit material in its metal state: start the clone from the stock values.\r\n   const mat=src.material.clone(),stock=src.material.userData.cogsMetal;mat.userData={};if(stock)Object.assign(mat,stock);mat.emissive.set(selectedTint||LIGHT_COLOR);mat.emissiveMap=mat.map;mat.emissiveIntensity=KNOBS.lightOff;m.material=mat;\r\n   mats.push(mat);Object.assign(part,{mat,phase:i*2.3,level:0,nearUntil:-Infinity});\r\n  }else if(p.kind==='valve')part.dir=i%2?1:-1;\r\n  pivot.rotation.z=rot;\r\n  if(isGear){\r\n   let batch=batches.get(p.node);if(!batch){const mat=src.material.clone();mat.userData={};mats.push(mat);const mesh=new THREE.InstancedMesh(src.geometry,mat,list.filter(q=>q.kind==='gear'&&q.node===p.node).length);mesh.frustumCulled=false;mesh.castShadow=true;mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);parent.add(mesh);batch={mesh,next:0};batches.set(p.node,batch);}\r\n   part.instance={mesh:batch.mesh,index:batch.next++,matrix:m.matrix.clone()};part.mat=batch.mesh.material;\r\n  }else{pivot.add(m);part.mat=m.material;}\r\n  parent.add(pivot);parts.push(part);\r\n  if(isGear){gearPivots[gi]=pivot;gi++;}\r\n });\r\n return {parts,gearPivots,batches:[...batches.values()].map(b=>b.mesh)};\r\n}\r\n// The old door's pipes/screws/plates/valves, one BatchedMesh per kit material (the kit has two) = 2 draw calls total.\r\n// Parts keep their own pivot (so pipes wiggle / valves spin / panels fall) and write into the batch via the same\r\n// `instance` path as the gears; `matrix` is the fitted model matrix.\r\nfunction buildDressing(parent,gltf,list,front,fw,fh,mats){\r\n const items=[],groups=new Map();\r\n for(const p of list){\r\n  let src;try{src=kitMesh(gltf,p.node);}catch(e){console.warn('portal-board-cogs: missing node',p.node,e);continue;}\r\n  const {m,depth}=fitModel(src,p.r*fw,true),pivot=new THREE.Group(),rot=(p.rot||0)*Math.PI/180,z=front+(p.layer||0)*.006*fw+depth/2;\r\n  pivot.position.set((p.u-.5)*fw,(.5-p.v)*fh,z);pivot.rotation.z=rot;\r\n  const part={...p,pivot,rot,angle:0,omega:0,z0:z,top:z+depth/2,dir:p.kind==='valve'?(items.length%2?1:-1):undefined,instance:{mesh:null,index:-1,matrix:m.matrix.clone()}};\r\n  let g=groups.get(src.material);if(!g){g={material:src.material,geos:new Map(),items:[]};groups.set(src.material,g);}\r\n  g.geos.set(src.geometry,null);g.items.push({part,geometry:src.geometry});items.push(part);\r\n }\r\n const batches=[];\r\n for(const g of groups.values()){\r\n  let v=0,ix=0;for(const geo of g.geos.keys()){v+=geo.attributes.position.count;ix+=geo.index?geo.index.count:0;}\r\n  const mat=g.material.clone();mat.userData={};mats.push(mat);\r\n  const mesh=new THREE.BatchedMesh(g.items.length,v,ix,mat);mesh.frustumCulled=false;mesh.perObjectFrustumCulled=false;parent.add(mesh);batches.push(mesh);\r\n  for(const geo of g.geos.keys())g.geos.set(geo,mesh.addGeometry(geo));\r\n  for(const it of g.items){it.part.instance.mesh=mesh;it.part.instance.index=mesh.addInstance(g.geos.get(it.geometry));it.part.mat=mat;}\r\n }\r\n return {parts:items,batches};\r\n}\r\nconst instanceTransform=new THREE.Matrix4(),instanceZero=new THREE.Matrix4().makeScale(0,0,0);\r\nfunction updateGearInstances(state){\r\n for(const panel of state.panels){panel.group.updateMatrix();state.panelBatch.setMatrixAt(panel.batchId,panel.group.matrix);state.panelBatch.setVisibleAt(panel.batchId,panel.group.visible);}\r\n for(const part of state.parts)if(part.instance){\r\n  const {mesh,index,matrix}=part.instance;part.pivot.updateMatrix();\r\n  if(!part.pivot.parent.visible)mesh.setMatrixAt(index,instanceZero);\r\n  else{instanceTransform.multiplyMatrices(part.pivot.parent.matrix,part.pivot.matrix).multiply(matrix);mesh.setMatrixAt(index,instanceTransform);}\r\n }\r\n for(const batch of state.batches)if(batch.instanceMatrix)batch.instanceMatrix.needsUpdate=true; // BatchedMesh uploads its own matrices\r\n}\r\n\r\n// The weld trail's own two layers in 3D, floating just above the tallest kit part so gears and pipes\r\n// never hide the sparks or the bead: the bead layer (normal blend) and the glow layer (additive) on top.\r\n// The door's own paint/glow inputs stay blank, so the trail isn't drawn twice.\r\nfunction weldLayer(parent,w,h,fw,fh,z,additive){\r\n const canvas=document.createElement('canvas');canvas.width=w;canvas.height=h;\r\n const texture=new THREE.CanvasTexture(canvas);texture.colorSpace=THREE.SRGBColorSpace; // flipY default: canvas row 0 = plane top\r\n const material=new THREE.MeshBasicMaterial({map:texture,transparent:true,depthWrite:false,polygonOffset:true,polygonOffsetFactor:-2,toneMapped:false,...(additive?{blending:THREE.AdditiveBlending}:{})});\r\n const mesh=new THREE.Mesh(new THREE.PlaneGeometry(fw,fh),material);mesh.position.z=z;mesh.renderOrder=additive?11:10;mesh.frustumCulled=false;parent.add(mesh);\r\n return {canvas,ctx:canvas.getContext('2d'),texture,mesh};\r\n}\r\n\r\nasync function init({mesh,face,wake,paint,glow}){\r\n const fw=face.w,fh=face.h,parent=mesh.isMesh?mesh.parent:mesh;\r\n const originals=[];mesh.traverse(n=>{if(n.isMesh){originals.push(n);n.visible=false;}});\r\n const [layout,gltf]=await Promise.all([\r\n  fetch(cogs.layoutAsset||LAYOUT).then(r=>{if(!r.ok)throw new Error(String(r.status));return r.json();}),\r\n  loadModel(cogs.partsAsset||PARTS),\r\n ]);\r\n cogs.frame=DEFAULT_FRAME;\r\n const paths=mechanicalPaths(),panels=sectionPanels([...paths,...releasePaths(face)]),owned=new THREE.Group();parent.add(owned);\r\n const railCanvas=document.createElement('canvas');railCanvas.width=paint.canvas.width;railCanvas.height=paint.canvas.height;\r\n const ctx=railCanvas.getContext('2d');ctx.strokeStyle='#fff';ctx.lineWidth=railCanvas.width*.006;ctx.lineJoin='round';ctx.lineCap='round';\r\n for(const path of paths){ctx.beginPath();path.points.forEach(([u,v],i)=>i?ctx.lineTo(u*railCanvas.width,v*railCanvas.height):ctx.moveTo(u*railCanvas.width,v*railCanvas.height));ctx.stroke();}\r\n const railTexture=new THREE.CanvasTexture(railCanvas);railTexture.flipY=false;\r\n const backplateMaterial=new THREE.MeshStandardMaterial({color:selectedBackplateTint,metalness:.55,roughness:.42,envMap:studioEnv(),envMapIntensity:.8});\r\n backplateMaterial.onBeforeCompile=shader=>{\r\n  shader.uniforms.uRailTint=uMetalTint;shader.uniforms.uRails={value:railTexture};\r\n  shader.fragmentShader='uniform vec3 uRailTint;uniform sampler2D uRails;varying vec2 vMetalUv;\\n'+shader.fragmentShader;\r\n  shader.vertexShader='varying vec2 vMetalUv;\\n'+shader.vertexShader.replace('#include <begin_vertex>','#include <begin_vertex>\\nvMetalUv=vec2(position.x/'+fw+'+.5,.5-position.y/'+fh+');');\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <color_fragment>','#include <color_fragment>\\nfloat rail=texture2D(uRails,vMetalUv).r;diffuseColor.rgb=mix(diffuseColor.rgb,uRailTint*.7,rail);');\r\n  // Directional polishing and fine abrasion remain part of the steel material,\r\n  // so they keep the selected back-wall hue and respond to its studio reflection.\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <roughnessmap_fragment>','#include <roughnessmap_fragment>\\nfloat brush=sin(vMetalUv.y*2350.0+sin(vMetalUv.x*80.0)*.65);roughnessFactor=clamp(roughnessFactor+brush*.055,.28,.62);')\r\n   .replace('#include <normal_fragment_maps>','#include <normal_fragment_maps>\\nnormal=normalize(normal+vec3(0.,brush*.045,0.));');\r\n };\r\n backplateMaterial.customProgramCacheKey=()=>`mechanical-backplate-${fw}-${fh}`;\r\n const front=.025*fw,depth=.026*fw;\r\n for(const panel of panels){\r\n  const shape=new THREE.Shape();panel.points.forEach(([u,v],i)=>{\r\n   // Inset toward each vertex's interior bisector. Bevel width is less than the\r\n   // inset, so the real joint remains open instead of bevels growing together.\r\n   const prev=panel.points[(i+panel.points.length-1)%panel.points.length],next=panel.points[(i+1)%panel.points.length],ax=(u-prev[0])*fw,ay=(v-prev[1])*fh,bx=(next[0]-u)*fw,by=(next[1]-v)*fh,al=Math.hypot(ax,ay),bl=Math.hypot(bx,by),nx=-ay/al-by/bl,ny=ax/al+bx/bl,nl=Math.hypot(nx,ny)||1,inset=Math.min(.0012*fw,al*.08,bl*.08),x=(u-.5)*fw+nx/nl*inset,y=(.5-v)*fh-ny/nl*inset;\r\n   i?shape.lineTo(x,y):shape.moveTo(x,y);\r\n  });shape.closePath();\r\n  const geometry=new THREE.ExtrudeGeometry(shape,{depth,bevelEnabled:true,bevelThickness:.0012*fw,bevelSize:.00065*fw,bevelSegments:1,steps:1,curveSegments:1});\r\n  // Each cell is an opaque, thick manufactured plate. A small normal inset makes\r\n  // the pre-existing joints visible without eroding the small oval-edge facets.\r\n  const positions=geometry.attributes.position;for(let i=0;i<positions.count;i++)positions.setZ(i,positions.getZ(i)+front-depth);\r\n  const group=new THREE.Group();owned.add(group);panel.group=group;panel.geometry=geometry;panel.gearIndices=[];\r\n }\r\n // BatchedMesh keeps every manufactured plate distinct while sharing its one\r\n // material and draw call. Release changes whole plate transforms only.\r\n const vertexCount=panels.reduce((n,p)=>n+p.geometry.attributes.position.count,0),panelBatch=new THREE.BatchedMesh(panels.length,vertexCount,0,backplateMaterial);panelBatch.frustumCulled=false;panelBatch.perObjectFrustumCulled=false;panelBatch.receiveShadow=true;owned.add(panelBatch);\r\n for(const panel of panels){const geometryId=panelBatch.addGeometry(panel.geometry);panel.batchId=panelBatch.addInstance(geometryId);}\r\n const nodes=[HUB_NODE]; // the kit's thin, face-on twenty-spoke cog: no bulky ornamental substitutes\r\n const built=mechanicalLayout(face,paths,nodes.length?nodes:[HUB_NODE]),list=built.parts;\r\n // Coaxial/belt couplers share the developed damped momentum across all rails;\r\n // individual meshes still counter-rotate at equal rim speeds along a rail.\r\n const first=built.trains[0]?.[0];for(const train of built.trains.slice(1)){const i=train[0];list[i].drives=first;list[i].driveRatio=list[first].r/list[i].r;}\r\n const gears=gearsFromLayout(list),mats=[],mech=mechanismLayout(face,paths,built),deg=a=>-a*180/Math.PI; // layout rot is face-uv (y down); the scene is y up\r\n mech.dial=[].concat(mech.dial)[0];\r\n list.push({node:HUB_NODE,kind:'dial',u:mech.dial.u,v:mech.dial.v,r:mech.dial.r,rot:0,layer:13});\r\n if(gears[0])gears[0].idleDir=1;\r\n const {parts,gearPivots,batches}=gltf?buildParts(owned,gltf,list,front,fw,fh,mats):{parts:[],gearPivots:[],batches:[]};\r\n // The previous door's kit dressing (pipes/screws/plates/valves) fills the panels between the shape lines; gears/lights are our own.\r\n const keepOut=[...list.map(g=>({u:g.u,v:g.v,r:g.r+.006})),...mech.lamps.map(l=>({u:l.u,v:l.v,r:l.r+.006})),...mech.weights.map(w=>({u:w.u,v:w.v,r:w.r+.006})),...mech.bolts.map(b=>({u:b.u,v:b.v,r:.055})),...mech.pistons.map(p=>({u:p.u,v:p.v,r:.05}))];\r\n const dress=dressingPlace((layout.parts||[]).filter(p=>['pipe','screw','plate','valve'].includes(p.kind)).map(p=>({...p,drives:undefined,layer:{plate:.3,screw:1.6,pipe:1.4,valve:1.4}[p.kind]})),[...paths,...releasePaths(face)],face,keepOut,mech.cables.map(c=>({a:c.from,b:c.to,r:c.r+.004})));\r\n const dressed=gltf&&dress.length?buildDressing(owned,gltf,dress,front,fw,fh,mats):{parts:[],batches:[]};\r\n batches.push(...dressed.batches);\r\n const panelAt=(u,v)=>panels.find(p=>pointInPolygon(u,v,p.points))||panels.reduce((a,b)=>Math.hypot((a.center[0]-u)*fw,(a.center[1]-v)*fh)<Math.hypot((b.center[0]-u)*fw,(b.center[1]-v)*fh)?a:b);\r\n for(const p of parts)if(p.kind!=='gear')panelAt(p.u,p.v).group.add(p.pivot); // the dial rides its panel\r\n for(const p of dressed.parts){panelAt(p.u,p.v).group.add(p.pivot);parts.push(p);}\r\n for(let gi=0;gi<gearPivots.length;gi++){\r\n  const g=gears[gi],h=list[gi].hub,dx=g.u-.5,dy=g.v-.495,l=Math.hypot(dx,dy)||1,u=g.u+(h?(g.u<.5?-.015:.015):dx/l*.004),v=g.v+(h?(g.v<.5?-.01:.01):dy/l*.004),panel=panelAt(u,v); // non-hub gears nudge outward like the other seam parts\r\n  if(gearPivots[gi])panel.group.add(gearPivots[gi]);panel.gearIndices.push(gi);\r\n }\r\n // The gold channels are real raised metal mounted on each pre-existing cell,\r\n // not a floating drawing. Each half-channel belongs to one side of its joint,\r\n // so the released plate carries its physical channel away with it.\r\n const railSegments=[];\r\n for(const panel of panels)for(let i=0;i<panel.points.length;i++){\r\n  const a=panel.points[i],b=panel.points[(i+1)%panel.points.length],hit=pointOnPath(paths,face,(a[0]+b[0])/2,(a[1]+b[1])/2),dx=(b[0]-a[0])*fw,dy=-(b[1]-a[1])*fh,length=Math.hypot(dx,dy);\r\n  if(!hit||hit.distance>1e-5*fw||length<.005*fw)continue;\r\n  railSegments.push({panel,a,b,dx,dy,length});\r\n }\r\n const railGeometry=new THREE.BoxGeometry(1,1,1),railMaterial=new THREE.MeshStandardMaterial({color:'#ffffff',metalness:.92,roughness:.3,envMap:studioEnv(),envMapIntensity:1.25}),railBatch=new THREE.InstancedMesh(railGeometry,railMaterial,railSegments.length);railBatch.frustumCulled=false;railBatch.castShadow=true;railBatch.instanceMatrix.setUsage(THREE.DynamicDrawUsage);owned.add(railBatch);batches.push(railBatch);mats.push(railMaterial);\r\n railSegments.forEach(({panel,a,b,dx,dy,length},index)=>{\r\n  const pivot=new THREE.Group(),width=.0028*fw;\r\n  // Clockwise face contour: right normal points inside the panel.\r\n  pivot.position.set(((a[0]+b[0])/2-.5)*fw+dy/length*.0024*fw,(.5-(a[1]+b[1])/2)*fh-dx/length*.0024*fw,front+.0035*fw);pivot.rotation.z=Math.atan2(dy,dx);panel.group.add(pivot);\r\n  parts.push({kind:'rail',pivot,mat:railMaterial,instance:{mesh:railBatch,index,matrix:new THREE.Matrix4().makeScale(Math.max(.001*fw,length-.0012*fw),width,.007*fw)}});\r\n });\r\n const shaftGeometry=new THREE.CylinderGeometry(1,1,1,10);shaftGeometry.rotateX(Math.PI/2);\r\n const shaftBatch=new THREE.InstancedMesh(shaftGeometry,railMaterial,gears.length);shaftBatch.frustumCulled=false;shaftBatch.castShadow=true;shaftBatch.instanceMatrix.setUsage(THREE.DynamicDrawUsage);owned.add(shaftBatch);batches.push(shaftBatch);\r\n for(let gi=0;gi<gears.length;gi++){\r\n  const gear=gears[gi],gearPart=parts.find(p=>p.kind==='gear'&&p.pivot===gearPivots[gi]);if(!gearPart)continue;\r\n  const bottom=2*gearPart.z0-gearPart.top,height=Math.max(.004*fw,bottom-front),pivot=new THREE.Group();pivot.position.set((gear.u-.5)*fw,(.5-gear.v)*fh,front+height/2);gearPivots[gi].parent.add(pivot);\r\n  parts.push({kind:'shaft',pivot,mat:railMaterial,instance:{mesh:shaftBatch,index:gi,matrix:new THREE.Matrix4().makeScale(gear.r*.2*fw,gear.r*.2*fw,height)}});\r\n }\r\n const bearingGeometry=new THREE.TorusGeometry(.009*fw,.0025*fw,5,20);\r\n built.trains.forEach((train,i)=>{\r\n  const g=gears[train[0]],panel=panels.find(p=>p.gearIndices.includes(train[0])),mat=new THREE.MeshStandardMaterial({color:selectedTint,metalness:.72,roughness:.32,emissive:selectedTint,emissiveIntensity:0}),pivot=new THREE.Group();\r\n  pivot.position.set((g.u-.5)*fw,(.5-g.v)*fh,(parts.find(p=>p.pathIndex===i)?.top||front+.02*fw)+.001*fw);pivot.add(new THREE.Mesh(bearingGeometry,mat));panel.group.add(pivot);mats.push(mat);\r\n  parts.push({kind:'light',u:g.u,v:g.v,r:.02,pivot,mat,phase:i*2.3,level:0,nearUntil:-Infinity});\r\n });\r\n // Procedural mechanism pieces share the rail material: one InstancedMesh per kind; each part's pivot rides its panel, poseMech() moves the matrix.\r\n const mechGeos=[],mk=(geo,n)=>{const m=new THREE.InstancedMesh(geo,railMaterial,n);m.frustumCulled=false;m.castShadow=true;m.instanceMatrix.setUsage(THREE.DynamicDrawUsage);m.userData.n=0;owned.add(m);batches.push(m);mechGeos.push(geo);return m;};\r\n const bar=new THREE.CylinderGeometry(1,1,1,10).rotateZ(Math.PI/2),box=new THREE.BoxGeometry(1,1,1),ring=new THREE.TorusGeometry(1,.07,6,28);\r\n const boltMesh=mk(box,mech.bolts.length),tubeMesh=mk(bar,mech.tubes.length),bodyMesh=mk(bar,mech.pistons.length),rodMesh=mk(bar,mech.pistons.length),cableMesh=mk(bar,mech.cables.length),weightMesh=mk(box,mech.weights.length),notchMesh=mk(box,12),rimMesh=mk(ring,mech.pulleys.length);\r\n // Lamps: emissive core sphere + two additive halo batches (tight glow, wide \"cast light\" on the door). Cast no shadow.\r\n const lampMat=new THREE.MeshBasicMaterial({toneMapped:false}),lampMesh=new THREE.InstancedMesh(new THREE.SphereGeometry(1,12,8),lampMat,mech.lamps.length);\r\n lampMesh.frustumCulled=false;lampMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);lampMesh.userData.n=0;owned.add(lampMesh);batches.push(lampMesh);mechGeos.push(lampMesh.geometry,lampMat);\r\n const haloTex=(()=>{const c=document.createElement('canvas');c.width=c.height=64;const x=c.getContext('2d'),g=x.createRadialGradient(32,32,0,32,32,32);g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.25,'rgba(255,255,255,.45)');g.addColorStop(.6,'rgba(255,255,255,.1)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,64,64);return new THREE.CanvasTexture(c);})();\r\n const haloGeo=new THREE.PlaneGeometry(2,2),mkHalo=(order)=>{const m=new THREE.InstancedMesh(haloGeo,new THREE.MeshBasicMaterial({map:haloTex,blending:THREE.AdditiveBlending,transparent:true,depthWrite:false,toneMapped:false}),mech.lamps.length);m.frustumCulled=false;m.renderOrder=order;m.instanceMatrix.setUsage(THREE.DynamicDrawUsage);m.userData.n=0;owned.add(m);batches.push(m);mechGeos.push(m.material,haloTex);return m;};\r\n const glowMesh=mkHalo(20),castMesh=mkHalo(19);mechGeos.push(haloGeo);\r\n // Seam parts are nudged .004 outward (bolts inward) before choosing a panel, so each rides one side of its joint and falls with it.\r\n const side=(u,v,k)=>{const dx=u-.5,dy=v-.495,l=Math.hypot(dx,dy)||1,n=k==='bolt'?-.004:['tube','body','rod','lamp'].includes(k)?.004:0;return [u+dx/l*n,v+dy/l*n];};\r\n const add=(mesh,kind,u,v,rot,z,extra,group)=>{const pivot=new THREE.Group(),part={kind,pivot,mat:railMaterial,u,v,rot,instance:{mesh,index:mesh.userData.n++,matrix:new THREE.Matrix4()},...extra};pivot.position.set((u-.5)*fw,(.5-v)*fh,z);pivot.rotation.z=rot;(group||panelAt(...side(u,v,kind)).group).add(pivot);parts.push(part);return part;};\r\n const zAt=layer=>front+layer*.006*fw,uvRot=a=>-a;\r\n for(const b of mech.bolts)add(boltMesh,'bolt',b.u,b.v,uvRot(b.rot),zAt(4),{pathIndex:b.pathIndex,station:b.station,throw:b.throw,side:b.side??1});\r\n for(const t of mech.tubes)add(tubeMesh,'tube',t.u,t.v,uvRot(t.rot),zAt(3),{}).instance.matrix.makeScale(t.len*fw,(t.r||.007)*fw,(t.r||.007)*fw);\r\n for(const p of mech.pistons){const hb=1.2*p.r*fw;add(bodyMesh,'body',p.u,p.v,uvRot(p.rot),zAt(4),{}).instance.matrix.makeScale(hb*2,.012*fw,.012*fw);add(rodMesh,'rod',p.u,p.v,uvRot(p.rot),zAt(4),{stroke:p.stroke,drives:p.drives,hb,Lr:hb*1.5,rr:.005*fw});}\r\n mech.lamps.forEach((l,i)=>{add(lampMesh,'lamp',l.u,l.v,0,zAt(12),{r0:l.r*fw,pathIndex:l.pathIndex,station:l.station,phase:i*2.3,level:0,nearUntil:-Infinity,flashUntil:-Infinity}).instance.matrix.makeScale(l.r*fw,l.r*fw,l.r*fw);lampMesh.setColorAt(i,LAMP_OFF);const pt=parts[parts.length-1];for(const [key,hm] of [['glow',glowMesh],['cast',castMesh]]){const h={kind:'halo',pivot:pt.pivot,mat:railMaterial,instance:{mesh:hm,index:hm.userData.n++,matrix:new THREE.Matrix4().makeScale(0,0,0)}};parts.push(h);pt[key]=h;hm.setColorAt(h.instance.index,LAMP_OFF);}});\r\n for(const c of mech.cables){const dx=(c.to[0]-c.from[0])*fw,dy=-(c.to[1]-c.from[1])*fh,len=Math.hypot(dx,dy),pt=add(cableMesh,'cable',(c.from[0]+c.to[0])/2,(c.from[1]+c.to[1])/2,Math.atan2(dy,dx),zAt(2),{});pt.instance.matrix.makeScale(len,c.r*fw,c.r*fw);}\r\n mech.weights.forEach((w,i)=>{const c=mech.cables[i]??mech.cables[0];if(!c)return;add(weightMesh,'weight',w.u,w.v,Math.atan2(-(c.to[1]-c.from[1])*fh,(c.to[0]-c.from[0])*fw),zAt(3),{travel:w.travel,drives:w.drives,size:w.r*2*fw});});\r\n for(const p of mech.pulleys){const gp=parts.find(q=>q.pivot===gearPivots[p.drives]);if(!gp)continue;const pt=add(rimMesh,'rim',p.u,p.v,0,gp.top+.001*fw,{},gearPivots[p.drives].parent);pt.instance.matrix.makeScale(p.r*fw*1.08,p.r*fw*1.08,.005*fw);}\r\n for(let k=0;k<12;k++){const a=k*Math.PI/6,d=parts.find(q=>q.kind==='dial'),pt=add(notchMesh,'notch',mech.dial.u,mech.dial.v,a,(d?.top??zAt(14))+.001*fw,{});pt.instance.matrix.makeScale(.012*fw,.006*fw,.006*fw).setPosition(mech.dial.r*fw*.9,0,0);}\r\n for(const m of batches)if(m.userData.n!=null)m.count=m.userData.n;\r\n const tally=(a)=>a.reduce((o,x)=>(o[x.pathIndex]=(o[x.pathIndex]||0)+1,o),{});\r\n mats.push(...new Set(parts.map(p=>p.mat)));\r\n for(const m of mats)metalFinish(m);applyMetal(mats);\r\n const tw=paint.canvas.width,th=paint.canvas.height;\r\n // Weld lies on the plate face; proud gears correctly occlude it. No elevated\r\n // full-board decal can hang over an opening left by a released assembly.\r\n const beadLayer=weldLayer(owned,tw,th,fw,fh,front+.0018*fw,false),glowLayer=weldLayer(owned,tw,th,fw,fh,front+.002*fw,true);\r\n const motion=matchMedia?.('(prefers-reduced-motion: reduce)'),reducedMotion=!!motion?.matches;\r\n S={dial:mech.dial,closed:paths.map(p=>Math.hypot(p.points[0][0]-p.points.at(-1)[0],p.points[0][1]-p.points.at(-1)[1])<1e-7),prog:paths.map(()=>0),cutToken:null,cutTimer:null,cutResolve:null,boltN:tally(mech.bolts),lampN:tally(mech.lamps),mechGeos,spin:null,motion,gears,gearPivots,parts,face,aspect:fh/fw,valveDir:1,mats,drag:new Map(),fall:null,wake,reducedMotion,paths,trains:built.trains,panels,owned,originals,backplateMaterial,railTexture,batches,bearingGeometry,railGeometry,shaftGeometry,panelBatch,\r\n    paint:beadLayer,glow:glowLayer,layers:[beadLayer,glowLayer],texW:tw,texH:th,\r\n    sparks:[],weld:[],weldHeads:new Map(),weldActive:false,tint:selectedTint};\r\n updateGearInstances(S);\r\n}\r\n// --- Welding trail: torch core + sparks + hot streak (glow layer) + cooling weld bead (paint layer) ---\r\n// A spark: {x,y (canvas px),vx,vy (px/s),born (ms),life (s),floor (canvas y it bounces off once, or\r\n// Infinity)}. Speeds/gravity are screen px scaled to canvas px at spawn (S.scale, refreshed per frame).\r\nfunction spawnSparks(S,u,v,count){\r\n if(count<=0)return;\r\n const scale=S.scale||pxScale(S),cx=u*S.texW,cy=v*S.texH,now=performance.now(),K=KNOBS;\r\n for(let i=0;i<count&&S.sparks.length<K.sparkMax;i++){\r\n  const a=Math.random()*TAU,spd=(K.sparkSpeedMinPx+Math.random()*(K.sparkSpeedMaxPx-K.sparkSpeedMinPx))*scale;\r\n  const floor=Math.random()<K.sparkBounce?cy+(K.sparkFloorMinPx+Math.random()*(K.sparkFloorMaxPx-K.sparkFloorMinPx))*scale:Infinity;\r\n  S.sparks.push({x:cx,y:cy,vx:Math.cos(a)*spd,vy:Math.sin(a)*spd,born:now,floor,\r\n   life:K.sparkLifeMin+Math.random()*(K.sparkLifeMax-K.sparkLifeMin)});\r\n }\r\n}\r\n// One spark step: gravity, move, and a single damped bounce off its floor (the skitter). Pure, tested.\r\nexport function stepSpark(s,dt,grav,keep){\r\n s.vy+=grav*dt;s.x+=s.vx*dt;s.y+=s.vy*dt;\r\n if(s.y>s.floor&&s.vy>0){s.y=s.floor;s.vy*=-keep;s.vx*=.8;s.floor=Infinity;}\r\n return s;\r\n}\r\n// A weld-trail point {u,v,t (ms),prev,d}: prev links to the same finger's previous point (per id, via\r\n// weldHeads) so two simultaneous strokes never draw a segment joining them; d is the running seam\r\n// length (face widths) so the bead's ripples sit at fixed spacing however the points fall.\r\nfunction weldPushPoint(S,id,u,v){\r\n if(S.fall&&pointInPolygon(u,v,S.fall.poly)){S.weldHeads.delete(id);return;}\r\n const prev=S.weldHeads.get(id)||null,len=prev?Math.hypot(u-prev.u,(v-prev.v)*S.aspect):0;\r\n if(prev&&len<KNOBS.weldStep)return;\r\n const p={u,v,t:performance.now(),prev,d:prev?prev.d+len:0};S.weld.push(p);S.weldHeads.set(id,p);\r\n if(S.weld.length>KNOBS.weldCap)S.weld.splice(0,S.weld.length-KNOBS.weldCap); // ponytail: hard cap\r\n}\r\nfunction weldRelease(S,id,u,v){\r\n weldPushPoint(S,id,u,v);S.weldHeads.delete(id);spawnSparks(S,u,v,KNOBS.sparkReleaseCount);\r\n}\r\nfunction heatRGBA(f,alpha){const {r,g,b}=heatColor(f);return `rgba(${r},${g},${b},${Math.max(0,alpha)})`;}\r\n// Canvas px per screen px right now: measured off the live rendered <canvas> (its CSS box) with the\r\n// same margin-fit math computeFit() uses in portal-board-glb.mjs (GLB.margin), so every xxxPx knob\r\n// stays a true screen size regardless of GLB.texSize, host width or devicePixelRatio. Read once per\r\n// frame (stepWeld caches it on S.scale).\r\nfunction pxScale(S){\r\n const el=document.querySelector('canvas.portal-board-canvas'),cw=el?.clientWidth,ch=el?.clientHeight;\r\n if(!cw||!ch)return 2; // not laid out yet: a reasonable guess, corrected the moment it is\r\n const availW=cw*(1-2*GLB.margin),availH=ch*(1-2*GLB.margin),scaleFit=Math.min(availW/S.face.w,availH/S.face.h);\r\n return S.texW/(scaleFit*S.face.w);\r\n}\r\n// The bead shows from the moment a point is laid (the hot glow sits on top of it while it cools), holds,\r\n// then fades, all within weldHotMs+weldBeadMs.\r\nexport const beadAlpha=age=>{const K=KNOBS,total=K.weldHotMs+K.weldBeadMs;return weldBeadAlpha(age,0,total,(K.weldHotMs+K.weldBeadHold*K.weldBeadMs)/total);};\r\nconst HOT_BUCKETS=8,BEAD_BUCKETS=8;\r\nfunction clipMetalSurface(S,ctx){\r\n if(!S.fall)return;ctx.beginPath();ctx.rect(0,0,S.texW,S.texH);\r\n S.fall.poly.forEach(([u,v],i)=>i?ctx.lineTo(u*S.texW,v*S.texH):ctx.moveTo(u*S.texW,v*S.texH));ctx.closePath();ctx.clip('evenodd');\r\n}\r\n// Hot trail, bucketed by age: each bucket is ONE path, so overlapping segment caps don't stack into a\r\n// row of brighter dots (the old per-segment strokes did). Blurred halo pass, then a crisp core pass:\r\n// white-hot, cooling yellow->orange->red. The selected tint only leans the outer halo a little.\r\nfunction weldRenderGlow(S,now,scale){\r\n const {ctx}=S.glow,w=S.texW,h=S.texH,K=KNOBS;\r\n const coreW=K.weldTrailWidthPx*scale,haloW=K.weldTrailHaloPx*scale,sparkW=K.sparkWidthPx*scale;\r\n ctx.clearRect(0,0,w,h);\r\n ctx.save();clipMetalSurface(S,ctx);ctx.globalCompositeOperation='lighter';ctx.lineCap='round';ctx.lineJoin='round';\r\n for(let pass=0;pass<2;pass++){\r\n  ctx.filter=pass?'none':`blur(${(haloW*.35).toFixed(1)}px)`;ctx.lineWidth=pass?coreW:haloW;\r\n  for(let b=0;b<HOT_BUCKETS;b++){\r\n   let any=false;ctx.beginPath();\r\n   for(const p of S.weld){\r\n    const age=now-p.t;if(!p.prev||age>=K.weldHotMs||Math.min(HOT_BUCKETS-1,(age/K.weldHotMs*HOT_BUCKETS)|0)!==b)continue;\r\n    ctx.moveTo(p.prev.u*w,p.prev.v*h);ctx.lineTo(p.u*w,p.v*h);any=true;\r\n   }\r\n   if(!any)continue;\r\n   const f=(b+.5)/HOT_BUCKETS,af=f<K.weldHotHold?1:1-(f-K.weldHotHold)/(1-K.weldHotHold);\r\n   ctx.strokeStyle=pass?heatRGBA(f,af):haloRGBA(f,S.tint,K.tintHaloMix,af*.55);ctx.stroke();\r\n  }\r\n }\r\n ctx.filter='none';\r\n // Sparks: thin incandescent streaks (white -> yellow -> orange as they cool), each with a faint warm\r\n // sheath, drawn back along their velocity so a fast one reads as a streak and a slow one as a dot.\r\n for(const s of S.sparks){\r\n  const f=Math.min(1,(now-s.born)/1000/s.life),x0=s.x-s.vx*K.sparkStreakS,y0=s.y-s.vy*K.sparkStreakS,a=1-f*f;\r\n  ctx.lineWidth=sparkW*(2.2-f);ctx.strokeStyle=heatRGBA(.35+f*.5,a*.14);ctx.beginPath();ctx.moveTo(x0,y0);ctx.lineTo(s.x,s.y);ctx.stroke();\r\n  ctx.lineWidth=sparkW*(1-.55*f);ctx.strokeStyle=heatRGBA(f*.85,a);ctx.beginPath();ctx.moveTo(x0,y0);ctx.lineTo(s.x,s.y);ctx.stroke();\r\n }\r\n // The torch itself at each finger: a white-hot core in a blue-white bloom that fades to a warm edge,\r\n // flickering, and swollen for a moment right after touch-down (the press flare).\r\n for(const d of S.drag.values()){\r\n  if(S.fall&&pointInPolygon(d.u,d.v,S.fall.poly))continue;\r\n  const k=Math.max(0,1-(now-(d.t0||0))/K.torchFlareMs),fl=.88+Math.random()*.24,cx=d.u*w,cy=d.v*h;\r\n  const R=K.torchBloomPx*scale*(1+(K.torchFlareScale-1)*k)*fl,core=K.torchCorePx*scale*(1+.6*k)*fl;\r\n  const g=ctx.createRadialGradient(cx,cy,0,cx,cy,R);\r\n  g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(Math.min(.5,core/R),'rgba(225,238,255,.95)');\r\n  g.addColorStop(Math.min(.6,core/R*2.2),'rgba(140,180,255,.45)');g.addColorStop(.75,'rgba(255,160,60,.16)');g.addColorStop(1,'rgba(255,110,30,0)');\r\n  ctx.fillStyle=g;ctx.fillRect(cx-R,cy-R,2*R,2*R);\r\n  ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(cx,cy,core*.55,0,TAU);ctx.fill();\r\n }\r\n ctx.restore();S.glow.texture.needsUpdate=true;\r\n}\r\n// The weld bead: one continuous seam, not a row of dots. Per alpha bucket the seam is one path stroked\r\n// several times: a blue heat-tint band, a straw band inside it, a drop shadow, the steel bead body, a\r\n// lighter crown and a thin specular ridge (upper-left lit, like the rest of the board). Then the\r\n// \"stack of dimes\": overlapping ripple arcs every weldRipplePx along the seam, convex in the travel\r\n// direction, each a dark crease with a bright lip behind it.\r\nconst BEAD_STROKES=[ // [width: multiple of the bead half-width R, or a heat band; offset in R; style]\r\n ['halo',0,'rgba(58,78,170,.26)'],['wide',0,'rgba(110,96,150,.22)'],['mid',0,'rgba(196,156,72,.36)'],[2,.3,'rgba(6,5,4,.8)'],\r\n [2,0,'rgb(118,120,126)'],[1.25,-.08,'rgb(162,164,170)'],[.36,-.36,'rgba(250,250,255,.8)'],\r\n];\r\nfunction weldRenderPaint(S,now,scale){\r\n const {ctx}=S.paint,w=S.texW,h=S.texH,K=KNOBS,R=K.weldBeadRadiusPx*scale,halo=K.weldHaloRadiusPx*scale;\r\n const sp=K.weldRipplePx*scale/w,rr=R*.82; // ripple spacing in face widths, ripple radius in canvas px\r\n ctx.clearRect(0,0,w,h);\r\n ctx.save();clipMetalSurface(S,ctx);ctx.lineCap='round';ctx.lineJoin='round';\r\n for(let b=1;b<=BEAD_BUCKETS;b++){\r\n  let any=false;ctx.beginPath();\r\n  for(const p of S.weld){if(Math.ceil(beadAlpha(now-p.t)*BEAD_BUCKETS)!==b)continue;const q=p.prev||p;ctx.moveTo(q.u*w,q.v*h);ctx.lineTo(p.u*w,p.v*h);any=true;}\r\n  if(!any)continue;\r\n  ctx.globalAlpha=b/BEAD_BUCKETS;\r\n  for(const [wd,off,style] of BEAD_STROKES){\r\n   ctx.lineWidth=wd==='halo'?2*halo:wd==='wide'?R*.6+halo*1.4:wd==='mid'?R*1.2+halo*.8:wd*R;ctx.strokeStyle=style;\r\n   if(off){ctx.translate(off*R,off*R);ctx.stroke();ctx.setTransform(1,0,0,1,0,0);}else ctx.stroke();\r\n  }\r\n  // ripples: dark crease, then the bright lip just behind it\r\n  for(let pass=0;pass<2;pass++){\r\n   ctx.beginPath();\r\n   for(const p of S.weld){\r\n    if(!p.prev||Math.ceil(beadAlpha(now-p.t)*BEAD_BUCKETS)!==b)continue;\r\n    const q=p.prev,dx=(p.u-q.u)*w,dy=(p.v-q.v)*h,len=p.d-q.d;if(len<=0)continue;\r\n    const th=Math.atan2(dy,dx),back=pass?rr*.55:rr*.35,ox=Math.cos(th)*back,oy=Math.sin(th)*back;\r\n    for(let k=Math.ceil(q.d/sp);k*sp<=p.d;k++){\r\n     const t=(k*sp-q.d)/len,cx=q.u*w+dx*t-ox,cy=q.v*h+dy*t-oy;\r\n     ctx.moveTo(cx+Math.cos(th-1.15)*rr,cy+Math.sin(th-1.15)*rr);ctx.arc(cx,cy,rr,th-1.15,th+1.15);\r\n    }\r\n   }\r\n   ctx.lineWidth=(pass?.7:.9)*scale;ctx.strokeStyle=pass?'rgba(232,234,242,.55)':'rgba(34,34,40,.75)';ctx.stroke();\r\n  }\r\n }\r\n ctx.restore();S.paint.texture.needsUpdate=true;\r\n}\r\n// Redraws (clears+repaints) both canvases every frame while anything welding is live (or a finger is\r\n// down: the torch core flickers), and one frame past that to wipe the last remnants, then leaves them\r\n// alone — see the sleep note on step().\r\nfunction stepWeld(S,dt,now){\r\n const K=KNOBS,scale=S.scale=pxScale(S),grav=K.sparkGravityPx*scale;\r\n let n=0;for(const s of S.sparks)if((now-s.born)/1000<s.life)S.sparks[n++]=stepSpark(s,dt,grav,K.sparkBounceKeep);\r\n S.sparks.length=n; // compacted in place: no per-frame array\r\n for(const d of S.drag.values())if(now-(d.lastSparkT||0)>K.sparkIdleMs){d.lastSparkT=now;spawnSparks(S,d.u,d.v,K.sparkIdleCount);}\r\n let k=0;while(k<S.weld.length&&now-S.weld[k].t>K.weldHotMs+K.weldBeadMs)k++;if(k)S.weld.splice(0,k);\r\n const active=S.sparks.length>0||S.weld.length>0||S.drag.size>0;\r\n if(active||S.weldActive){weldRenderGlow(S,now,scale);weldRenderPaint(S,now,scale);}\r\n S.weldActive=active;\r\n return active;\r\n}\r\n\r\n// Lock progress: arc length a finger actually covers along ONE path (it sticks to it while within .03 face-widths), so\r\n// taps, reverse traces and crossing other shapes never jump the lock. `d` is the finger's {pi,f} memory.\r\nexport function trackFinger(paths,closed,face,prog,d,u,v){\r\n const w=face.w;let hit=null,pi=d.pi;\r\n if(pi!=null){hit=pointOnPath([paths[pi]],face,u,v);if(hit.distance>=.03*w)hit=null;}\r\n if(!hit){hit=pointOnPath(paths,face,u,v);if(!hit||hit.distance>=.03*w){d.pi=d.f=null;d.lu=u;d.lv=v;return;}pi=hit.pathIndex;}\r\n const pts=paths[pi].points,f=pathProgress(pts,face,hit.segment,hit.u,hit.v);\r\n let pf=pi===d.pi?d.f:null; // switching paths (e.g. leaving a shared corner): start from where the last sample sat on this one\r\n if(pf==null&&d.lu!=null){const h0=pointOnPath([paths[pi]],face,d.lu,d.lv);if(h0.distance<.03*w)pf=pathProgress(pts,face,h0.segment,h0.u,h0.v);}\r\n if(pf!=null&&d.lu!=null){\r\n  let df=Math.abs(f-pf);if(closed[pi])df=Math.min(df,1-df);\r\n  let L=0;for(let i=1;i<pts.length;i++)L+=Math.hypot((pts[i][0]-pts[i-1][0])*face.w,(pts[i][1]-pts[i-1][1])*face.h);\r\n  // progress can't outrun the finger: a fast swipe on a long straight line still counts, a teleport doesn't\r\n  if(df*L<=1.5*Math.hypot((u-d.lu)*face.w,(v-d.lv)*face.h)+.01*w){prog[pi]=Math.min(1,prog[pi]+df);if(prog[pi]>=.94)prog[pi]=1;}\r\n }\r\n d.pi=pi;d.f=f;d.lu=u;d.lv=v;\r\n}\r\nfunction progress(i,p){if(S&&i in S.prog){S.prog[i]=Math.max(0,Math.min(1,p));S.wake?.();}}\r\nfunction press(id,u,v){\r\n if(!S||S.fall&&pointInPolygon(u,v,S.fall.poly))return;\r\n const skip=S.fall?.gearIndices;\r\n S.drag.set(id,{gi:hitGear(S.gears,S.face,u,v,skip),moved:false,u,v,t0:performance.now(),pi:null,f:null});\r\n weldPushPoint(S,id,u,v);spawnSparks(S,u,v,KNOBS.sparkPressCount);\r\n}\r\nfunction move(id,u,v,pu,pv){\r\n if(!S)return;\r\n const d=S.drag.get(id);if(!d)return;\r\n if(Math.hypot(u-pu,v-pv)>MOVE_EPS)d.moved=true;\r\n d.u=u;d.v=v;\r\n if(S.fall&&pointInPolygon(u,v,S.fall.poly)){S.weldHeads.delete(id);return;}\r\n trackFinger(S.paths,S.closed,S.face,S.prog,d,u,v);\r\n // Acquire the rail on every move: starting between gears still turns the\r\n // entire train. Projection onto its tangent preserves forward/reverse swipes.\r\n const rail=pointOnPath(S.paths,S.face,u,v);\r\n if(rail&&rail.distance<.075*S.face.w){\r\n  const train=S.trains[rail.pathIndex],gi=train.reduce((a,b)=>Math.hypot(S.gears[a].u-u,(S.gears[a].v-v)*S.aspect)<Math.hypot(S.gears[b].u-u,(S.gears[b].v-v)*S.aspect)?a:b);\r\n  if(!S.fall?.gearIndices.has(gi)){d.gi=gi;applyTorque(S.gears,train[0],signedRailImpulse(rail,u-pu,v-pv,S.face,S.gears[gi].r,.8));}\r\n }\r\n // Pipes don't need a drag origin to react — any pipe the finger passes near this event gets a kick,\r\n // not only the one (if any) under the initial press.\r\n const vx=(u-pu)*S.face.w,vy=-(v-pv)*S.face.h;\r\n for(const part of S.parts){\r\n  if(part.kind!=='pipe'||S.fall?.pivots.has(part.pivot))continue;\r\n  const dxp=(u-part.u)*S.face.w,dyp=(v-part.v)*S.face.h,lim=(part.r+KNOBS.wiggleMargin)*S.face.w;\r\n  if(Math.hypot(dxp,dyp)<lim)part.omega+=wiggleKick(vx,vy,part.rot,KNOBS.wiggleKick);\r\n }\r\n weldPushPoint(S,id,u,v);\r\n spawnSparks(S,u,v,Math.min(KNOBS.sparkMoveMax,Math.round(Math.hypot(u-pu,v-pv)/KNOBS.sparkPerFrac)));\r\n}\r\nfunction release(id){\r\n if(!S)return;\r\n const d=S.drag.get(id);if(!d)return;\r\n if(!d.moved){\r\n  if(d.gi>=0)applyTorque(S.gears,d.gi,TAP_IMPULSE);\r\n  else{\r\n   const pipes=S.parts.filter(p=>p.kind==='pipe'&&!S.fall?.pivots.has(p.pivot));\r\n   const pi=nearestPipe(pipes,S.face,d.u,d.v,KNOBS.wiggleMargin);\r\n   if(pi>=0)pipes[pi].omega+=KNOBS.wiggleTapKick;\r\n  }\r\n }\r\n weldRelease(S,id,d.u,d.v);\r\n S.drag.delete(id);\r\n}\r\n// Moves the procedural mechanism pieces: bolts by trace progress, rods/weights/dial by their driving gear's angle.\r\nfunction poseMech(S,now){\r\n const fw=S.face.w,G=S.gears,sp=S.spin?Math.min(1,(now-S.spin.t0)/S.spin.ms):0,dialA=G[S.dial.drives].angle+TAU*sp*sp*(3-2*sp);\r\n for(const p of S.parts){\r\n  const m=p.instance?.matrix;\r\n  if(p.kind==='bolt')m.makeScale(.014*fw,2*p.throw*fw,.008*fw).setPosition(0,p.side*boltOut(S.prog[p.pathIndex],p.station,S.boltN[p.pathIndex])*p.throw*1.1*fw,0);\r\n  else if(p.kind==='rod')m.makeScale(p.Lr,p.rr,p.rr).setPosition(p.hb-p.Lr/2+pistonExt(p.stroke,G[p.drives].angle)*fw,0,0);\r\n  else if(p.kind==='weight')m.makeScale(p.size,p.size,p.size).setPosition(p.travel*fw*(.5+.5*Math.sin(G[p.drives].angle)),0,0);\r\n  else if(p.kind==='dial')p.pivot.rotation.z=dialA;\r\n  else if(p.kind==='notch')p.pivot.rotation.z=p.rot+dialA;\r\n }\r\n if(sp>=1)S.spin=null;\r\n}\r\nfunction step(dt,now){\r\n if(!S)return false;\r\n // Live media query (not just at init) and the motor runs before the rest check, so a sleeping loop can never\r\n // be left with the hubs \"at rest\" while the motor is on.\r\n const idling=idleSpin(S.gears,dt,KNOBS.hubIdleAccel,S.motion?S.motion.matches:S.reducedMotion);\r\n const moving=stepTrain(S.gears,dt)||idling;\r\n S.gears.forEach((g,gi)=>{if(S.gearPivots[gi])S.gearPivots[gi].rotation.z=g.angle%TAU;});\r\n let rootOmega=0;\r\n for(const g of S.gears)if(g.drives==null&&Math.abs(g.omega)>Math.abs(rootOmega))rootOmega=g.omega;\r\n if(Math.abs(rootOmega)>REST)S.valveDir=Math.sign(rootOmega);\r\n // ponytail: valves used to idle forever on their own VALVE_IDLE baseline, which alone kept the board\r\n // rendering every frame at rest; now they only turn while the train or a finger is actually doing\r\n // something (they've never had a canonical rest angle, so freezing wherever they stop is fine). The\r\n // five big hub gears (HUBS) still idle-spin nonstop even at rest (their own small idleDir motor,\r\n // applied below) — unlike everything else here, that keeps the board rendering permanently while\r\n // shown, except under prefers-reduced-motion, where the motor is off and it can settle/sleep too.\r\n const valveActive=Math.abs(rootOmega)>REST||S.drag.size>0;\r\n let wobbling=false,lit=false;\r\n for(const part of S.parts){\r\n  const falling=S.fall?.pivots.has(part.pivot.parent);\r\n  if(part.kind==='valve'){\r\n   if(valveActive){part.angle=valveAngle(part.angle,dt,part.dir,S.valveDir,rootOmega);part.pivot.rotation.z=part.rot+part.angle;}\r\n  }else if(part.kind==='pipe'&&!falling){\r\n   const r=wiggleStep(part.angle,part.omega,dt,KNOBS.wiggleFreqHz,KNOBS.wiggleDamping,KNOBS.wiggleMaxAngle);\r\n   part.angle=r.angle;part.omega=r.omega;\r\n   if(Math.abs(part.angle)<KNOBS.wiggleRestAngle&&Math.abs(part.omega)<KNOBS.wiggleRestOmega){\r\n    if(part.angle!==0||part.pivot.position.z!==part.z0){part.angle=0;part.omega=0;part.pivot.rotation.z=part.rot;part.pivot.position.z=part.z0;}\r\n   }else{\r\n    wobbling=true;\r\n    part.pivot.rotation.z=part.rot+part.angle;\r\n    part.pivot.position.z=part.z0+Math.abs(part.angle)/KNOBS.wiggleMaxAngle*KNOBS.wiggleBob*S.face.w;\r\n   }\r\n  }else if((part.kind==='light'||part.kind==='lamp')&&!falling){\r\n   let near=false;\r\n   for(const d of S.drag.values()){const dx=(d.u-part.u)*S.face.w,dy=(d.v-part.v)*S.face.h;if(Math.hypot(dx,dy)<KNOBS.lightRadius*S.face.w){near=true;break;}}\r\n   if(near)part.nearUntil=now+KNOBS.lightHoldMs;\r\n   const target=now<part.nearUntil||now<part.flashUntil||(part.pathIndex!=null&&lampLit(S.prog[part.pathIndex],part.station,S.lampN[part.pathIndex]))?1:0;\r\n   part.level=lightLevel(part.level,target,dt,KNOBS.lightRampMs,KNOBS.lightFadeMs);\r\n   const reduced=S.motion?S.motion.matches:S.reducedMotion;\r\n   if(part.kind==='lamp'){\r\n    const {mesh,index}=part.instance,fl=!reduced&&part.level>.95?1+Math.sin(now*.02+part.phase)*KNOBS.lightFlicker:1,L=part.level,k=part.r0*(.4+.6*L);\r\n   lampOn.set(selectedTint||LIGHT_COLOR).lerp(LAMP_CORE,.6); // warm white-amber core, 40% tint\r\n   part.instance.matrix.makeScale(k,k,k);mesh.setColorAt(index,lampTmp.copy(LAMP_OFF).lerp(lampOn,L).multiplyScalar(fl));mesh.instanceColor.needsUpdate=true;\r\n   // halos (additive: colour*level carries the fade); matrices are pivot-local, updateGearInstances composes them\r\n   for(const [h,sc,gain,z] of [[part.glow,5*k,1,part.r0*.9],[part.cast,10*part.r0*L,.28,-.068*S.face.w]]){h.instance.matrix.makeScale(sc,sc,sc).setPosition(0,0,z);h.instance.mesh.setColorAt(h.instance.index,lampTmp.set(selectedTint||LIGHT_COLOR).multiplyScalar(gain*L*fl*fl));h.instance.mesh.instanceColor.needsUpdate=true;}\r\n   }else{\r\n    const flicker=!reduced&&part.level>.95?Math.sin(now*.02+part.phase)*KNOBS.lightFlicker*KNOBS.lightOn:0;\r\n    part.mat.emissiveIntensity=Math.max(0,KNOBS.lightOff+(KNOBS.lightOn-KNOBS.lightOff)*part.level+flicker);\r\n   }\r\n   if(part.level!==target||(!reduced&&part.level>.001))lit=true; // under reduced motion a settled lit lamp lets the loop sleep\r\n  }\r\n }\r\n const f=S.fall,t=f?Math.min(1,(now-f.t0)/Math.max(1,f.ms)):1,e=t*t;\r\n if(f)for(const [p,z0] of f.pivots){p.position.z=z0-f.dist*e;p.position.y=p.userData.restY-f.dist*.3*e;p.rotation.x=FALL_TILT*e;p.visible=t<1;}\r\n poseMech(S,now);\r\n updateGearInstances(S);\r\n const weldActive=stepWeld(S,dt,now);\r\n // Sleeps (returns false) once no gear is moving, no pipe is wobbling, no light is lit, no weld spark/\r\n // trail is live and no cut animation is running — the board (portal-board-glb.mjs) stops driving rAF\r\n // at that point. In practice that's \"never\" while reducedMotion is off: the hub gears' idle motor\r\n // above keeps `moving` true forever.\r\n return moving||wobbling||lit||weldActive||t<1||!!S.spin;\r\n}\r\n// Release existing opaque assemblies at their manufactured scale.\r\n// Completion: the released outline's rings fill, lamps flash, the dial turns once (300 ms), then the panels fall.\r\n// heal() inside that window cancels the release (cutToken) and resolves the promise early.\r\nfunction cut(poly,color,ms=FALL_MS){\r\n if(!S)return Promise.resolve();\r\n const s=S,spin=s.motion?.matches?0:300,now=performance.now();heal();\r\n const ring=[...poly,poly[0]],done=s.paths.map((p,i)=>p.points.every(([u,v])=>pointOnPath([{points:ring}],s.face,u,v).distance<.01*s.face.w)?i:-1).filter(i=>i>=0);\r\n for(const i of done)s.prog[i]=1;\r\n for(const p of s.parts)if(p.kind==='light'||p.kind==='lamp')p.flashUntil=now+900;\r\n s.spin=spin?{t0:now,ms:spin}:null;const token=s.cutToken={};s.wake?.();\r\n return new Promise(res=>{s.cutResolve=res;s.cutTimer=setTimeout(()=>{\r\n  s.cutResolve=null;if(S!==s||s.cutToken!==token)return res();s.cutToken=null;\r\n  const p=s.prog.slice(),fell=fall(poly,color,ms);s.prog.splice(0,p.length,...p);\r\n  const t=performance.now();for(const q of s.parts)if(q.kind==='light'||q.kind==='lamp')q.flashUntil=t+600;\r\n  res(fell);},spin);});\r\n}\r\nfunction fall(poly,color,ms=FALL_MS){\r\n if(!S)return Promise.resolve();heal();S.weld.length=0;S.weldHeads.clear();S.sparks.length=0;S.drag.clear();\r\n // Erase both surface layers immediately, including the torch, before a plate\r\n // departs. Future points in the aperture are rejected by weldPushPoint.\r\n for(const l of S.layers){l.ctx.clearRect(0,0,S.texW,S.texH);l.texture.needsUpdate=true;}\r\n const pivots=new Map(),gearIndices=new Set();\r\n for(const panel of S.panels)if(pointInPolygon(...panel.center,poly)){\r\n  panel.group.userData.restY=panel.group.position.y;pivots.set(panel.group,panel.group.position.z);panel.gearIndices.forEach(i=>gearIndices.add(i));\r\n }\r\n if(S.motion?.matches)ms=0;\r\n let resolve;const done=new Promise(r=>{resolve=r;}),fall={pivots,gearIndices,poly,t0:performance.now(),ms,dist:Math.max(S.face.w,S.face.h)*.35,resolve,timer:null};\r\n fall.connections=S.gears.map(g=>({drives:g.drives,driveRatio:g.driveRatio,idleDir:g.idleDir}));\r\n S.gears.forEach((g,i)=>{if(g.drives!=null&&gearIndices.has(i)!==gearIndices.has(g.drives)){g.drives=null;delete g.driveRatio;}if(gearIndices.has(i))g.idleDir=0;});\r\n S.fall=fall;fall.timer=setTimeout(()=>{for(const p of pivots.keys())p.visible=false;if(S?.fall===fall)updateGearInstances(S);fall.resolve?.();fall.resolve=null;S?.wake?.();},Math.max(0,ms));S.wake?.();return done;\r\n}\r\nfunction heal(){\r\n if(!S)return;\r\n S.drag.clear();S.weld.length=0;S.weldHeads.clear();S.sparks.length=0;S.weldActive=false;S.prog.fill(0);S.spin=null;\r\n if(S.cutToken){clearTimeout(S.cutTimer);S.cutToken=null;S.cutResolve?.();S.cutResolve=null;}\r\n for(const p of S.parts)if(p.kind==='light'||p.kind==='lamp')p.flashUntil=-Infinity;\r\n for(const l of S.layers){l.ctx.clearRect(0,0,S.texW,S.texH);l.texture.needsUpdate=true;}\r\n if(!S.fall){S.wake?.();return;}\r\n clearTimeout(S.fall.timer);S.fall.resolve?.();\r\n for(const [p,z0] of S.fall.pivots){p.position.z=z0;p.position.y=p.userData.restY;p.rotation.x=0;p.visible=true;}\r\n S.gears.forEach((g,i)=>{Object.assign(g,S.fall.connections[i]);g.omega=0;});\r\n S.fall=null;\r\n updateGearInstances(S);\r\n S.wake?.();\r\n}\r\nfunction dispose(){\r\n if(!S)return;heal();\r\n for(const l of S.layers){l.mesh.removeFromParent();l.mesh.geometry.dispose();l.mesh.material.dispose();l.texture.dispose();}\r\n for(const part of S.parts)part.pivot.removeFromParent();\r\n for(const panel of S.panels)panel.geometry.dispose();S.panelBatch.dispose();for(const b of S.batches||[])b.dispose?.();\r\n S.owned.removeFromParent();S.railTexture.dispose();S.backplateMaterial.dispose();S.bearingGeometry.dispose();S.railGeometry.dispose();S.shaftGeometry.dispose();for(const g of S.mechGeos)g.dispose();\r\n for(const m of new Set(S.mats))m.dispose();\r\n S=null;\r\n}\r\n\r\n// R7: the weld trail alone in 2D, for the flat board (no gears, pipes or lights without WebGL): its own state.\r\nexport function cogsWeld(){\r\n let S=null;\r\n return {\r\n  init({paint,glow,face,wake}){S={face,aspect:face.h/face.w,paint,glow,texW:paint.canvas.width,texH:paint.canvas.height,drag:new Map(),sparks:[],weld:[],weldHeads:new Map(),weldActive:false,wake,tint:selectedTint};weldTrace=S;},\r\n  setTint(hex,selected=true){setCogsTint(hex,selected);},\r\n  press(id,u,v){S.drag.set(id,{u,v,t0:performance.now()});weldPushPoint(S,id,u,v);spawnSparks(S,u,v,KNOBS.sparkPressCount);},\r\n  move(id,u,v,pu,pv){const d=S.drag.get(id);if(d){d.u=u;d.v=v;}weldPushPoint(S,id,u,v);spawnSparks(S,u,v,Math.min(KNOBS.sparkMoveMax,Math.round(Math.hypot(u-pu,v-pv)/KNOBS.sparkPerFrac)));},\r\n  release(id,u,v){S.drag.delete(id);weldRelease(S,id,u,v);},\r\n  step:(dt,now)=>stepWeld(S,dt,now),\r\n  dispose(){if(S===weldTrace)weldTrace=null;S=null;},\r\n };\r\n}\r\n\r\ncogsWeld.metal=true; // the flat poster tints as anodized metal, not a flat wash (portal-board.mjs paintStill)\r\n\r\nexport const cogs={\r\n id:'cogs',shadows:true,asset:'/pod/worlds/boards/cogs/door.glb',flip:false,background:'#161310',\r\n guide:null,frame:DEFAULT_FRAME,ink:false, // the weld trail replaces the shared ink line\r\n presectioned:true,progress,init,press,move,release,step,cut,heal,dispose,setTint:setCogsTint,setBackplateTint:setCogsBackplateTint,doorTone,tintTarget:'trace',trace2d:cogsWeld,\r\n};\r\n","modules/portal/portal-board-glb.mjs":"// Generic 3D tablet board for the portal home: loads a decimated GLB board (Tripo -> Blender), fits\r\n// its front face to the host, and lets an effect module paint two canvas textures onto it (finger\r\n// dent + paint/glow layers injected into the GLB's own material via onBeforeCompile). Same public\r\n// interface as createQuiltBoard so portal.mjs can swap boards freely. AGPL-3.0-or-later.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {SHAPES,fromFrame} from './portal-shapes.mjs';\r\nimport {createPortalCutMask} from './portal-cut.mjs';\r\nimport {boardLayout,fillScale} from './portal-board-layout.mjs';\r\n\r\nexport const GLB={inset:.06,texSize:1024,sleepMs:1500,dent:10,dentRadius:48,margin:.02};\r\n\r\n// --- Pure helpers (no THREE dependency) -------------------------------------------------------\r\nconst AXIS={x:[1,0,0],y:[0,1,0],z:[0,0,1]};\r\nconst cross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]];\r\n// size={x,y,z} bbox dims -> row-major 3x3 rotation [ex,ey,ez] sending the thinnest local axis to\r\n// world Z and the longer of the remaining two to world Y. ex=ey×ez keeps it a proper rotation\r\n// (det +1), never a mirror.\r\nexport function orientMatrix(size){\r\n const axes=['x','y','z'],thin=axes.reduce((a,b)=>size[a]<=size[b]?a:b);\r\n const others=axes.filter(a=>a!==thin),long=size[others[0]]>=size[others[1]]?others[0]:others[1];\r\n const ez=AXIS[thin],ey=AXIS[long];\r\n return [cross(ey,ez),ey,ez];\r\n}\r\n// rect={left,top,width,height} client px -> [u,v] on the face, u right, v down, corners at 0/1.\r\nexport function faceUV(rect,clientX,clientY){return [(clientX-rect.left)/rect.width,(clientY-rect.top)/rect.height];}\r\n// Even-odd ray cast: is (u,v) inside the closed polygon poly=[[u,v],...]? A repeated closing point is harmless.\r\nexport function pointInPolygon(u,v,poly){\r\n let inside=false;\r\n for(let i=0,j=poly.length-1;i<poly.length;j=i++){const [xi,yi]=poly[i],[xj,yj]=poly[j];if((yi>v)!==(yj>v)&&u<(xj-xi)*(v-yi)/(yj-yi)+xi)inside=!inside;}\r\n return inside;\r\n}\r\n// Splits a triangle index by where each triangle's centroid lands: positions is flat xyz, toUv(x,y)->[u,v]\r\n// maps it to face coords; triangles inside poly go to `cut`, the rest to `keep` (both flat index arrays).\r\nexport function splitIndexByPolygon(positions,index,toUv,poly){\r\n const keep=[],cut=[],us=poly.map(p=>p[0]),vs=poly.map(p=>p[1]),u0=Math.min(...us),u1=Math.max(...us),v0=Math.min(...vs),v1=Math.max(...vs);\r\n for(let t=0;t<index.length;t+=3){\r\n  const a=3*index[t],b=3*index[t+1],c=3*index[t+2],[u,v]=toUv((positions[a]+positions[b]+positions[c])/3,(positions[a+1]+positions[b+1]+positions[c+1])/3);\r\n  (u>=u0&&u<=u1&&v>=v0&&v<=v1&&pointInPolygon(u,v,poly)?cut:keep).push(index[t],index[t+1],index[t+2]);\r\n }\r\n return {keep,cut};\r\n}\r\n\r\n// --- Cut-away pieces (shared with the quilt board) ----------------------------------------------\r\nconst TILT=35*Math.PI/180;\r\n// Transparent twin of a board material for a falling piece. clone() drops the instance-assigned\r\n// onBeforeCompile/customProgramCacheKey, so the paint/glow/rim injection is copied across by hand.\r\nexport function pieceMaterial(src){const m=src.clone();m.onBeforeCompile=src.onBeforeCompile;m.customProgramCacheKey=src.customProgramCacheKey;m.transparent=true;return m;}\r\n// Drops pieces [{pivot,material,drop()}] (pivot at the piece centroid): each sinks `fall` along -z into\r\n// the board, tilts about one random in-plane axis up to ~35°, shrinks to .6 and fades out, ease-in over\r\n// ms; then drop() removes it and `done` resolves. Timed by setTimeout, not rAF, so a paused or\r\n// backgrounded board can't stall the portal; the board's tick calls pose(now) while `live`.\r\nexport function fallPieces(pieces,fall,ms){\r\n const a=Math.random()*Math.PI*2,axis=new THREE.Vector3(Math.cos(a),Math.sin(a),0),t0=performance.now(),z0=pieces.map(p=>p.pivot.position.z);\r\n const f={live:true,pose(now){const t=Math.min(1,(now-t0)/Math.max(1,ms)),e=t*t;pieces.forEach((p,i)=>{p.pivot.position.z=z0[i]-fall*e;p.pivot.quaternion.setFromAxisAngle(axis,TILT*e);p.pivot.scale.setScalar(1-.4*e);p.material.opacity=1-e;});}};\r\n f.pose(t0);\r\n f.done=new Promise(resolve=>{f.end=()=>{if(!f.live)return;f.live=false;pieces.forEach(p=>p.drop());resolve();};if(ms>0)setTimeout(f.end,ms);else f.end();});\r\n return f;\r\n}\r\n// A piece's geometry rides on the door's own vertex buffers; detach them before dispose() or WebGL\r\n// frees buffers the door is still drawing with.\r\nfunction sharedGeometry(src,index){const g=new THREE.BufferGeometry();for(const k in src.attributes)g.setAttribute(k,src.attributes[k]);g.setIndex(index?.isBufferAttribute?index.clone():new THREE.BufferAttribute(index.slice?.()||index,1));return g;}\r\nfunction disposeShared(g){for(const k in g.attributes)g.deleteAttribute(k);g.dispose();}\r\n\r\n// --- Shader injection defaults -----------------------------------------------------------------\r\nconst DEFAULT_DISPLACE=`for(int di=0;di<8;di++){if(di>=uTouchCount)break;vec4 t=uTouch[di];vec2 tp=uFaceMin+vec2(t.x,1.0-t.y)*uFaceSize;float dd=distance(position.xy,tp);if(dd<uDentRadius){float f=1.0-dd/uDentRadius;transformed.z-=uDent*f*f*t.z;}}`;\r\n\r\nfunction makeCanvasTex(w,h){\r\n const canvas=document.createElement('canvas');canvas.width=w;canvas.height=h;\r\n const texture=new THREE.CanvasTexture(canvas);texture.flipY=false;texture.colorSpace=THREE.SRGBColorSpace; // canvas row 0 = board top = vPlanar.y 0; canvas colours are sRGB (sampler returns linear)\r\n return {canvas,ctx:canvas.getContext('2d'),texture};\r\n}\r\n// The eight traceable shapes, stitched onto the paint layer so every board shows them (skip 'line':\r\n// it shares geometry with 'cross').\r\n// Where the eight shapes sit on the face: the board's own carving when it has one (effect.frame,\r\n// x0/y0/x1/y1 UV fractions — or the older effect.pattern left/top/right/bottom some boards still set),\r\n// else an even inset. Traces are normalised against it (see toFrame/fromFrame in portal-shapes.mjs).\r\nexport const frameOf=(effect,knobs)=>effect.frame||(effect.pattern&&{x0:effect.pattern.left,y0:effect.pattern.top,x1:effect.pattern.right,y1:effect.pattern.bottom})||{x0:knobs.inset,y0:knobs.inset,x1:1-knobs.inset,y1:1-knobs.inset};\r\n// Each hint is drawn twice — a wide blurred halo, then a thin core — so it glows softly instead of reading as a hard wire.\r\nfunction drawGuides(paint,texW,texH,knobs,effect){\r\n if(effect.guide===null)return;\r\n const g=effect.guide||{color:'#fff',alpha:.5,width:4},scale=knobs.texSize/1024,F=frameOf(effect,knobs);\r\n const ctx=paint.ctx;ctx.save();ctx.strokeStyle=g.color;ctx.lineCap='round';ctx.lineJoin='round';\r\n const trace=()=>{for(const [id,polys] of Object.entries(SHAPES)){\r\n  if(id==='line')continue;\r\n  for(const poly of polys){ctx.beginPath();poly.points.forEach(([x,y],i)=>{const [fx,fy]=fromFrame(x,y,F),cx=fx*texW,cy=fy*texH;i?ctx.lineTo(cx,cy):ctx.moveTo(cx,cy);});ctx.stroke();}\r\n }};\r\n ctx.filter=`blur(${6*scale}px)`;ctx.globalAlpha=g.alpha*.6;ctx.lineWidth=g.width*2*scale;trace();\r\n ctx.filter='none';ctx.globalAlpha=g.alpha;ctx.lineWidth=g.width*.5*scale;trace();\r\n ctx.restore();paint.texture.needsUpdate=true;\r\n}\r\n\r\nexport async function createGlbBoard(host,{effect,knobs=GLB}={}){\r\n // Outside the try: a build that fails part-way must still stop its frame loop, release its context and undo its effect.\r\n let renderer=null,inited=false,frame=0,disposed=false,awakeUntil=0,last=0,frameMs=0,cutting=null;\r\n try{\r\n const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(42,1,1,20000);\r\n scene.add(new THREE.HemisphereLight(0xfff4e6,0x3a2f40,1.1));\r\n const sun=new THREE.DirectionalLight(0xfff0dc,2.4);sun.position.set(-.7,.55,.45);scene.add(sun);\r\n\r\n // Load, orient (thin axis -> +Z, long axis -> +Y), centre, and bake it all into the geometry.\r\n // effect.build (Pond, R21): a procedural board instead of a GLB, same orient/fit/cut path.\r\n const gltf=effect.build?{scene:effect.build(THREE)}:await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(effect.asset);\r\n gltf.scene.updateMatrixWorld(true);\r\n const meshes=[];gltf.scene.traverse(o=>{if(o.isMesh)meshes.push(o);});\r\n if(!meshes.length)throw new Error('portal-board-glb: no mesh in '+effect.asset);\r\n // The boards ship quantized (gltfpack: interleaved, normalized int16 positions). cut() reads position.array as flat float\r\n // xyz, so it found no triangle inside the shape and the wormhole never showed through a GLB board (R7); and applyMatrix4\r\n // would clamp a normalized attribute to [-1,1]. Plain float positions first.\r\n const rawGeoms=meshes.map(m=>{const g=m.geometry.clone(),p=g.attributes.position;g.setAttribute('position',new THREE.Float32BufferAttribute(Float32Array.from({length:p.count*3},(_,i)=>p.getComponent(i/3|0,i%3)),3));g.applyMatrix4(m.matrixWorld);return g;});\r\n const rawBox=new THREE.Box3();for(const g of rawGeoms){g.computeBoundingBox();rawBox.union(g.boundingBox);}\r\n const size=rawBox.getSize(new THREE.Vector3());\r\n const rot=orientMatrix({x:size.x,y:size.y,z:size.z});\r\n const rotM=new THREE.Matrix4().set(rot[0][0],rot[0][1],rot[0][2],0, rot[1][0],rot[1][1],rot[1][2],0, rot[2][0],rot[2][1],rot[2][2],0, 0,0,0,1);\r\n if(effect.flip)rotM.premultiply(new THREE.Matrix4().makeRotationY(Math.PI));\r\n const geoms=rawGeoms.map(g=>{g.applyMatrix4(rotM);return g;});\r\n const box=new THREE.Box3();for(const g of geoms){g.computeBoundingBox();box.union(g.boundingBox);}\r\n const center=box.getCenter(new THREE.Vector3());\r\n for(const g of geoms)g.translate(-center.x,-center.y,-center.z);\r\n const fw=box.max.x-box.min.x,fh=box.max.y-box.min.y,halfDepth=(box.max.z-box.min.z)/2,faceMin={x:-fw/2,y:-fh/2};\r\n\r\n const group=new THREE.Group();\r\n const meshObjs=geoms.map((g,i)=>{const mm=new THREE.Mesh(g,meshes[i].material);group.add(mm);return mm;});\r\n scene.add(group);\r\n const preservedAspect=true; // The mechanical cap fills the unused space; artwork retains its aspect.\r\n\r\n const texW=knobs.texSize,texH=Math.max(1,Math.round(knobs.texSize*fh/fw));\r\n // rim: the cut edge's neon outline, on its own half-res layer because effects clear `glow` every frame.\r\n const paint=makeCanvasTex(texW,texH),glow=makeCanvasTex(texW,texH),rim=makeCanvasTex(texW>>1,texH>>1);\r\n // Guides are drawn after layout and effect.init, which may discover a painted frame.\r\n\r\n const touchArr=Array.from({length:8},()=>new THREE.Vector4(0,0,0,0));\r\n const uBoardTint={value:new THREE.Color('#b026ff')};\r\n\r\n const uniforms={\r\n  uTime:{value:0},uTouch:{value:touchArr},uTouchCount:{value:0},\r\n  uPaint:{value:paint.texture},uGlow:{value:glow.texture},uRim:{value:rim.texture},uGlowStrength:{value:1.2},\r\n  uFaceMin:{value:new THREE.Vector2(faceMin.x,faceMin.y)},uFaceSize:{value:new THREE.Vector2(fw,fh)},\r\n  uDent:{value:knobs.dent},uDentRadius:{value:knobs.dentRadius},\r\n  uBoardTint,\r\n  ...(effect.uniforms||{}),\r\n };\r\n const portalCutMask={value:null};\r\n function setupMaterial(mat){\r\n  const portalCutSide={value:0};mat.userData.portalCutSideUniform=portalCutSide;\r\n  mat.customProgramCacheKey=()=>effect.id;\r\n  mat.onBeforeCompile=shader=>{\r\n   Object.assign(shader.uniforms,uniforms,{uPortalCutMask:portalCutMask,uPortalCutSide:portalCutSide});\r\n   // uniformDecls go to both stages; vertexDecls/fragmentDecls to one only, so a big uniform array (jelly's trail) never\r\n   // takes a second stage's uniform space on a phone GPU.\r\n   const decl=`varying vec2 vPlanar;\\nvarying vec2 vPortalCutUv;\\nuniform sampler2D uPortalCutMask;\\nuniform float uPortalCutSide;\\nuniform float uTime;\\nuniform vec4 uTouch[8];\\nuniform int uTouchCount;\\nuniform vec2 uFaceMin;\\nuniform vec2 uFaceSize;\\nuniform sampler2D uPaint;\\nuniform sampler2D uGlow;\\nuniform sampler2D uRim;\\nuniform float uGlowStrength;\\nuniform float uDent;\\nuniform float uDentRadius;\\nuniform vec3 uBoardTint;\\n${effect.uniformDecls||''}\\n`;\r\n   shader.vertexShader=decl+(effect.vertexDecls||'')+shader.vertexShader.replace('#include <begin_vertex>',\r\n    `#include <begin_vertex>\\nvec2 planar=(position.xy-uFaceMin)/uFaceSize;planar.y=1.0-planar.y;vPlanar=planar;vPortalCutUv=planar;\\n${effect.vertexDisplace||DEFAULT_DISPLACE}`);\r\n   shader.fragmentShader=decl+(effect.fragmentDecls||'')+shader.fragmentShader\r\n    .replace('#include <map_fragment>','#include <map_fragment>\\nfloat portalCutCoverage=1.0;if(abs(uPortalCutSide)>.5){portalCutCoverage=texture2D(uPortalCutMask,vPortalCutUv).r;if(uPortalCutSide>.5&&portalCutCoverage<.5)discard;if(uPortalCutSide<-.5&&portalCutCoverage>=.5)discard;}\\nfloat lum=dot(diffuseColor.rgb,vec3(.2126,.7152,.0722));diffuseColor.rgb=mix(diffuseColor.rgb,vec3(lum)*uBoardTint,1.0);vec4 pnt=texture2D(uPaint,vPlanar);diffuseColor.rgb=mix(diffuseColor.rgb,pnt.rgb,pnt.a);')\r\n\r\n    .replace('#include <emissivemap_fragment>','#include <emissivemap_fragment>\\nvec4 glo=texture2D(uGlow,vPlanar),cutRim=texture2D(uRim,vPlanar);totalEmissiveRadiance+=glo.rgb*glo.a*uGlowStrength+cutRim.rgb*cutRim.a*2.0;\\n'+(effect.fragment||'')); // optional effect GLSL: runs before lighting, may edit diffuseColor/roughnessFactor/normal (view space)/totalEmissiveRadiance\r\n  };\r\n  mat.needsUpdate=true;\r\n }\r\n\r\n let width=1,height=1,faceRect={left:0,top:0,width:1,height:1},scaleFit=1,viewFrame=frameOf(effect,knobs);\r\n function computeFit(){\r\n  width=Math.max(1,host.clientWidth);height=Math.max(1,host.clientHeight);\r\n  faceRect=boardLayout(width,height);const fit=fillScale(fw,fh,faceRect);scaleFit=fit.mean; // the face fills the whole host: x and y scale apart, depth stays physical\r\n  uniforms.uDent.value=knobs.dent/fit.sz;uniforms.uDentRadius.value=knobs.dentRadius/fit.mean;\r\n  group.scale.set(fit.sx,fit.sy,fit.sz);group.position.set(faceRect.left+faceRect.width/2,-(faceRect.top+faceRect.height/2),-fit.sz*halfDepth);\r\n  viewFrame=frameOf(effect,knobs);\r\n  for(const k in faceRect)host.style.setProperty('--face-'+k,faceRect[k]+'px');\r\n }\r\n computeFit();\r\n const toWorld=(u,v)=>[faceRect.left+u*faceRect.width,-(faceRect.top+v*faceRect.height)];\r\n await effect.init?.({THREE,scene,mesh:meshObjs.length===1?meshObjs[0]:group,material:meshObjs[0].material,uniforms,paint,glow,face:{w:fw,h:fh},toWorld,faceZ:0,wake});inited=true;\r\n computeFit();drawGuides(paint,texW,texH,knobs,{...effect,frame:viewFrame});\r\n meshObjs.forEach(m=>setupMaterial(m.material));\r\n // Transparent twins for cut pieces, drawn once at load (opacity 0, over the door itself: invisible) so\r\n // the first cut doesn't hitch on a shader compile.\r\n const pieceMats=new Map(),warm=[];\r\n meshObjs.forEach(o=>{if(!pieceMats.has(o.material)){const m=pieceMaterial(o.material),sourceCompile=m.onBeforeCompile,side={value:0};m.userData.portalCutSideUniform=side;m.onBeforeCompile=function(shader,renderer){sourceCompile.call(this,shader,renderer);shader.uniforms.uPortalCutMask=portalCutMask;shader.uniforms.uPortalCutSide=side;};m.opacity=0;pieceMats.set(o.material,m);warm.push(new THREE.Mesh(o.geometry,m));}});\r\n warm.forEach(w=>group.add(w));\r\n\r\n // The renderer (and its WebGL context) only now, once every download is in: a stalled one never holds a context.\r\n // Transparent canvas: the solid board colour is #portalHome's background (portal.mjs reads board.background),\r\n // so the neon glass can sit between the two and show only through a cut.\r\n renderer=new THREE.WebGLRenderer({antialias:true,alpha:true,powerPreference:'low-power'});\r\n renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.setClearColor(0x000000,0);\r\n if(effect.shadows){renderer.shadowMap.enabled=true;renderer.shadowMap.type=THREE.PCFShadowMap;sun.castShadow=true;sun.shadow.normalBias=.02;sun.shadow.bias=-.0004;}\r\n const canvas=renderer.domElement;canvas.className='portal-board-canvas';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='display:block;width:100%;height:100%';host.append(canvas);\r\n const startTime=performance.now(),pointers=new Map();\r\n function layout(){\r\n  computeFit();\r\n  if(effect.guide!==null){paint.ctx.clearRect(0,0,texW,texH);drawGuides(paint,texW,texH,knobs,{...effect,frame:viewFrame});}\r\n  renderer.setSize(width,height,false);camera.aspect=width/height;\r\n  camera.position.set(width/2,-height/2,(height/2)/Math.tan(THREE.MathUtils.degToRad(camera.fov/2)));camera.near=camera.position.z/10;camera.far=camera.position.z*10;camera.lookAt(width/2,-height/2,0);camera.updateProjectionMatrix();\r\n  if(effect.shadows){ // tight ortho shadow camera on the board face (world units are px; group sits at the face centre)\r\n   const cx=width/2,cy=-(faceRect.top+faceRect.height/2),R=Math.hypot(faceRect.width,faceRect.height)/2,d=Math.max(width,height)*2,sc=sun.shadow.camera,ms=Math.min(width,height)<500?512:1024;\r\n   sun.target.position.set(cx,cy,0);sun.target.updateMatrixWorld();sun.position.set(cx-.7*d,cy+.55*d,.45*d);\r\n   Object.assign(sc,{left:-R,right:R,top:R,bottom:-R,near:d*.2,far:d*2});sc.updateProjectionMatrix();\r\n   if(sun.shadow.mapSize.x!==ms){sun.shadow.mapSize.set(ms,ms);sun.shadow.map?.dispose();sun.shadow.map=null;}\r\n  }\r\n  effect.resize?.({w:fw,h:fh});wake();\r\n }\r\n function wake(){awakeUntil=performance.now()+knobs.sleepMs;if(!frame&&!disposed&&renderer){last=performance.now();frame=requestAnimationFrame(tick);}}\r\n function fillTouches(now){\r\n  let i=0;for(const p of pointers.values()){if(i>=8)break;touchArr[i].set(p.u,p.v,1,(now-p.t0)/1000);i++;}\r\n  for(;i<8;i++)touchArr[i].set(0,0,0,0);\r\n  uniforms.uTouchCount.value=Math.min(pointers.size,8);\r\n }\r\n function tick(now){\r\n  frame=0;if(disposed)return;\r\n  const dt=Math.min(1/30,Math.max(1/240,(now-last)/1000));last=now;\r\n  let animating=false;\r\n  if(!document.hidden){\r\n   const t0=performance.now();\r\n   uniforms.uTime.value=(now-startTime)/1000;fillTouches(now);\r\n   if(effect.step)animating=!!effect.step(dt,now);\r\n   if(cutting?.fall.live){cutting.fall.pose(now);animating=true;}\r\n   renderer.render(scene,camera);\r\n   frameMs=frameMs*.9+(performance.now()-t0)*.1;\r\n  }\r\n  if(pointers.size||now<awakeUntil||animating)frame=requestAnimationFrame(tick);\r\n }\r\n function faceRectClient(){const hostBox=host.getBoundingClientRect();const sx=hostBox.width/width,sy=hostBox.height/height;return {left:hostBox.left+faceRect.left*sx,top:hostBox.top+faceRect.top*sy,width:faceRect.width*sx,height:faceRect.height*sy};}\r\n const observer=new ResizeObserver(layout);observer.observe(host);layout();renderer.render(scene,camera);warm.forEach(w=>group.remove(w));\r\n\r\n // Cut-away: every triangle of the board's own mesh(es) whose centroid is inside polyUv (face coords, v down;\r\n // planar uv exactly as the shader computes it) moves into a falling piece that shares the mesh's vertex\r\n // buffers; the mesh keeps the rest, so the board has a real hole. Effect-owned meshes/props are never\r\n // sliced here: an effect takes part through its optional cut(polyUv,color)/heal() hooks (pointInPolygon is exported for them).\r\n const toUv=(x,y)=>[(x-faceMin.x)/fw,1-(y-faceMin.y)/fh];\r\n function drawRim(poly,color){\r\n  // R3-10: the shell's aura is the one rim during transit. A second neon edge baked into the board paints a doubled oval at a different perspective.\r\n  if(globalThis.myr5Shell&&!globalThis.myr5Shell.disposed)return;\r\n  const {ctx:c,canvas:{width:w,height:h}}=rim,s=w/512;c.clearRect(0,0,w,h);c.save();c.lineJoin='round';\r\n  c.beginPath();poly.forEach(([u,v],i)=>i?c.lineTo(u*w,v*h):c.moveTo(u*w,v*h));c.closePath();\r\n  c.strokeStyle=color;c.shadowColor=color;c.shadowBlur=14*s;c.lineWidth=7*s;c.stroke();c.shadowBlur=0;c.strokeStyle='#fff';c.lineWidth=2*s;c.stroke();\r\n  c.restore();rim.texture.needsUpdate=true;\r\n }\r\n function cut(polyUv,color='#ffffff',ms=1100){\r\n  heal();\r\n  // Mechanical modules already have seams and thickness. Release those assemblies\r\n  // through their own lifecycle rather than manufacturing a sliced door mesh.\r\n  if(effect.presectioned){\r\n   const done=effect.cut?.(polyUv,color,ms);\r\n   wake();\r\n   return Promise.resolve(done);\r\n  }\r\n  const pieces=[];portalCutMask.value=createPortalCutMask(polyUv);\r\n  for(const mesh of meshObjs){\r\n   const g=mesh.geometry,p=g.attributes.position.array,index=g.index||(g.setIndex([...Array(g.attributes.position.count).keys()]),g.index);\r\n   // Both copies retain every boundary triangle; complementary face-space discards produce the exact cut.\r\n   let u=0,v=0;for(const point of polyUv){u+=point[0];v+=point[1];}u/=polyUv.length;v/=polyUv.length;\r\n   const cx=faceMin.x+u*fw,cy=faceMin.y+(1-v)*fh,cz=0;\r\n   const pg=sharedGeometry(g,index.array),material=pieceMats.get(mesh.material)||pieceMats.set(mesh.material,pieceMaterial(mesh.material)).get(mesh.material);\r\n   mesh.material.userData.portalCutSideUniform.value=-1;material.userData.portalCutSideUniform.value=1;\r\n   const piece=new THREE.Mesh(pg,material),pivot=new THREE.Group();piece.frustumCulled=false;piece.position.set(-cx,-cy,-cz);pivot.position.set(cx,cy,cz);pivot.add(piece);mesh.add(pivot);\r\n   pieces.push({pivot,material,drop(){pivot.removeFromParent();disposeShared(pg);}});\r\n  }\r\n  drawRim(polyUv,color);\r\n  cutting={fall:fallPieces(pieces,Math.max(fw,fh)*.35,ms)};effect.cut?.(polyUv,color);wake();\r\n  return cutting.fall.done;\r\n }\r\n function heal(){\r\n  effect.healSecret?.(); // ice secret (L3): restore the shattered crystal (cut() heals first too)\r\n  if(effect.presectioned){effect.heal?.();if(!disposed&&renderer)renderer.render(scene,camera);return;}\r\n  if(!cutting)return;\r\n  cutting.fall.end();for(const mesh of meshObjs){mesh.material.userData.portalCutSideUniform.value=0;const piece=pieceMats.get(mesh.material);if(piece?.userData.portalCutSideUniform)piece.userData.portalCutSideUniform.value=0;}portalCutMask.value?.dispose();portalCutMask.value=null;cutting=null;effect.heal?.();\r\n  rim.ctx.clearRect(0,0,rim.canvas.width,rim.canvas.height);rim.texture.needsUpdate=true;\r\n  if(preservedAspect)computeFit();\r\n  if(!disposed)renderer.render(scene,camera); // healed frame on the canvas now, even while paused\r\n }\r\n const claimed=new Map();\r\n return {\r\n  canvas,\r\n  glContext:renderer.getContext(),\r\n  background:effect.background||'#17111e',\r\n  faceRect:faceRectClient,\r\n  cut,heal,\r\n  patternRect(){const hostBox=host.getBoundingClientRect(),F=viewFrame,sx=hostBox.width/width,sy=hostBox.height/height;if(preservedAspect)return {left:hostBox.left+(faceRect.left+faceRect.width*F.x0)*sx,top:hostBox.top+(faceRect.top+faceRect.height*F.y0)*sy,width:faceRect.width*(F.x1-F.x0)*sx,height:faceRect.height*(F.y1-F.y0)*sy};return {left:hostBox.left+hostBox.width*F.x0,top:hostBox.top+hostBox.height*F.y0,width:hostBox.width*(F.x1-F.x0),height:hostBox.height*(F.y1-F.y0)};},\r\n  setTint(hex,selected=true){\r\n   if(!/^#[0-9a-f]{6}$/i.test(hex))return;\r\n   if(effect.id==='grass'||effect.presectioned)effect.setTint?.(hex,selected);\r\n   else{uBoardTint.value.set(effect.doorTone?effect.doorTone(hex):hex);effect.setTint?.(hex,selected);}\r\n\r\n   wake();\r\n  },\r\n  setBackplateTint(hex){\r\n   if(!/^#[0-9a-f]{6}$/i.test(hex))return;\r\n   uBoardTint.value.set(hex);effect.setBackplateTint?.(hex);wake();\r\n  },\r\n  press(id,clientX,clientY){\r\n   const painted=faceRectClient();\r\n   const [u,v]=faceUV(painted,clientX,clientY),cu=Math.min(1,Math.max(0,u)),cv=Math.min(1,Math.max(0,v)),ex=pointers.get(id);\r\n   if(ex){effect.move?.(id,cu,cv,ex.u,ex.v);ex.u=cu;ex.v=cv;}\r\n   else{pointers.set(id,{u:cu,v:cv,t0:performance.now()});effect.press?.(id,cu,cv);}\r\n   wake();\r\n  },\r\n  // claims(id): asked BEFORE effect.release forgets the pointer; a released id keeps its answer until portal reads it once.\r\n  claims(id){if(claimed.has(id)){const c=claimed.get(id);claimed.delete(id);return c;}return !!effect.claims?.(id);},\r\n  release(id){const p=pointers.get(id);if(p){claimed.set(id,!!effect.claims?.(id));effect.release?.(id,p.u,p.v);pointers.delete(id);}wake();},\r\n  frameMs:()=>frameMs,\r\n  ink:effect.ink, // false: the effect draws its own trace trail, portal.mjs skips its glowing ink line\r\n  pause(){for(const [id,p] of pointers)effect.release?.(id,p.u,p.v);pointers.clear();cancelAnimationFrame(frame);frame=0;},\r\n  resume:wake,\r\n  dispose(){\r\n   cutting?.fall.end();portalCutMask.value?.dispose();portalCutMask.value=null;disposed=true;cancelAnimationFrame(frame);observer.disconnect();effect.dispose?.();pieceMats.forEach(m=>m.dispose());rim.texture.dispose();\r\n   group.traverse(n=>{n.geometry?.dispose();if(n.material)for(const mat of [n.material].flat()){for(const v of Object.values(mat))if(v?.isTexture)v.dispose();mat.dispose();}});\r\n   paint.texture.dispose();glow.texture.dispose();renderer.dispose();renderer.forceContextLoss();canvas.remove();\r\n  },\r\n };\r\n }catch(error){disposed=true;cancelAnimationFrame(frame);if(inited)effect.dispose?.();renderer?.dispose();renderer?.forceContextLoss();renderer?.domElement.remove();throw error;}\r\n}\r\n","modules/portal/portal-board-grass.mjs":"// grass board effect for portal-board-glb.mjs: a dense instanced blade lawn covers the block (hiding its\r\n// quilted diamonds so the traced-shape guides are the only pattern), blades sway in the wind, part\r\n// around a touch and spring back on release; mixed-colour flower clusters plant along a drag, pop in,\r\n// and fade away 7 s later; cut()/heal() open the lawn over the board's cut-out. JS drives touch/flower\r\n// state; all blade motion is GLSL. Vault secret (Achievement Vault L5): a tiny purple alien cap in one corner, a buried UFO in the\r\n// opposite one; a flower line drawn between them walks the alien to the ship, which cracks the ground open on a metal plate. AGPL-3.0-or-later.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {pointInPolygon} from './portal-board-glb.mjs';\r\n\r\nconst FLOWER_ASSET='/pod/worlds/boards/flower.glb',FLOWER_CAP=120,PLANT_STEP_PX=36,POP_MS=300,SPRING_LIFE=1.2;\r\nconst FADE_MS=7000; // Ian: everything returns to normal after 7 s (2026-09-22)\r\nconst FADE_OUT_MS=700,FLOWER_FRAC=.075,CLUSTER_FRAC=.04,FLOWER_TILT=1.2,FLOWER_LIFT=.02;\r\nconst BLADES=7000,BLADE_W=.015,BLOCK_EXPOSURE=.75,FIELD_INSET=.025,GUIDE_GLOW='0.6'; // inset keeps edge tufts from overhanging far\r\n\r\n// --- Pure helpers (no THREE dependency) -------------------------------------------------------\r\nconst clamp01=x=>Math.min(1,Math.max(0,x));\r\nexport const popScale=(ageMs,durMs=POP_MS)=>{const t=clamp01(ageMs/durMs);return 1-(1-t)*(1-t);};\r\n// 1 while the flower lives, then eases in to 0 over outMs (shrink + fade), 0 after.\r\nexport const fadeLife=(ageMs,liveMs=FADE_MS,outMs=FADE_OUT_MS)=>{const t=clamp01((ageMs-liveMs)/outMs);return 1-t*t;};\r\nexport const springActive=(ageS,maxS=SPRING_LIFE)=>ageS>=0&&ageS<maxS;\r\nexport const plantStep=(lx,ly,x,y,stepPx=PLANT_STEP_PX)=>Math.hypot(x-lx,y-ly)>=stepPx;\r\n// HSL triples: red, orange, yellow, pink, magenta, violet, white, sky blue.\r\nexport const FLOWER_HSL=[[0,.8,.52],[.07,.9,.55],[.14,.95,.56],[.94,.75,.74],[.86,.75,.52],[.76,.6,.6],[0,0,.95],[.56,.75,.66]];\r\nconst GREENS=[[.29,.6,.42],[.31,.62,.38],[.33,.55,.34],[.36,.5,.32],[.40,.45,.30]],YELLOW_GREENS=[[.22,.7,.48],[.24,.65,.52]];\r\nconst pickHSL=(set,rand,dh,ds,dl)=>{const [h,s,l]=set[Math.min(set.length-1,Math.floor(rand()*set.length))];return [(h+(rand()-.5)*dh+1)%1,clamp01(s+(rand()-.5)*ds),clamp01(l+(rand()-.5)*dl)];};\r\nexport const flowerHSL=(rand=Math.random)=>pickHSL(FLOWER_HSL,rand,0,.16,.12);\r\nexport const bladeHSL=(rand=Math.random)=>pickHSL(rand()<.06?YELLOW_GREENS:GREENS,rand,.02,.1,.08);\r\n// n uniform random points in a disc of radius maxR.\r\n// Indices of the roots (flat u0,v0,u1,v1,... face coords, v down) inside the closed polygon poly.\r\nexport const rootsInside=(uv,poly)=>{const out=[];for(let i=0;i<uv.length;i+=2)if(pointInPolygon(uv[i],uv[i+1],poly))out.push(i/2);return out;};\r\nexport const clusterOffsets=(n,maxR,rand=Math.random)=>Array.from({length:n},()=>{const a=rand()*Math.PI*2,r=maxR*Math.sqrt(rand());return [Math.cos(a)*r,Math.sin(a)*r];});\r\n\r\n// Block shader: copies DEFAULT_DISPLACE's finger-dent loop (vertexDisplace replaces it), adds a slab\r\n// bend away from the touch, a damped echo per released touch (uSpring ring buffer, filled in release())\r\n// and a faint breeze gated by uBreeze (off under reduced motion). Mostly hidden under the blades now.\r\nconst VERTEX_DISPLACE=\r\n`for(int di=0;di<8;di++){if(di>=uTouchCount)break;vec4 t=uTouch[di];vec2 tp=uFaceMin+vec2(t.x,1.0-t.y)*uFaceSize;float dd=distance(position.xy,tp);if(dd<uDentRadius){float f=1.0-dd/uDentRadius;transformed.z-=uDent*f*f*t.z;}}\r\nfloat tipW=smoothstep(-uHalfDepth,uHalfDepth,position.z);\r\nfloat bendR=uFaceSize.x*0.22,bendA=uFaceSize.x*0.06;\r\nvec2 bend=vec2(0.0);\r\nfor(int bi=0;bi<8;bi++){if(bi>=uTouchCount)break;vec4 t=uTouch[bi];vec2 tp=uFaceMin+vec2(t.x,1.0-t.y)*uFaceSize;vec2 d=position.xy-tp;float dist=length(d);if(dist<bendR&&dist>1e-4){float f=1.0-dist/bendR;bend+=normalize(d)*bendA*f*f;}}\r\nfor(int si=0;si<6;si++){vec4 s=uSpring[si];if(s.w<=0.0)continue;float age=uTime-s.z;if(age<0.0||age>1.2)continue;vec2 sp=uFaceMin+vec2(s.x,1.0-s.y)*uFaceSize;vec2 d=position.xy-sp;float dist=length(d);if(dist<bendR&&dist>1e-4){float f=1.0-dist/bendR,wave=exp(-age*4.0)*sin(age*14.0);bend+=normalize(d)*bendA*f*f*wave*s.w;}}\r\ntransformed.xy+=bend*tipW;\r\ntransformed.xy+=vec2(uBreeze*uFaceSize.x*0.004*sin(uTime*1.3+planar.x*9.0))*tipW;`;\r\n\r\n// Blade shader (replaces project_vertex; blade-local y is 0 at the root, 1 at the tip). Works in the\r\n// board group's local space: sway on a travelling wave, bend away from each held touch (eased in over\r\n// 120 ms), a damped cos spring-back from the release pose (the whole blade leans, so the lawn parts; wind moves tips). vPlanar is\r\n// the rest position on the face so the guide paint sits on the lawn like the block's own paint did.\r\nconst BLADE_VDECL='varying vec2 vPlanar;\\nuniform float uTime;\\nuniform vec4 uTouch[8];\\nuniform int uTouchCount;\\nuniform vec2 uFaceMin;\\nuniform vec2 uFaceSize;\\nuniform vec4 uSpring[6];\\nuniform float uBreeze;\\n';\r\nconst BLADE_VERTEX=\r\n`vec4 bw=instanceMatrix*vec4(transformed,1.0);\r\nvec2 root=instanceMatrix[3].xy,rp=(bw.xy-uFaceMin)/uFaceSize,pr=(root-uFaceMin)/uFaceSize;vPlanar=vec2(rp.x,1.0-rp.y);\r\nfloat tw=position.y*position.y,bendR=uFaceSize.x*0.2,bendA=uFaceSize.x*0.1;\r\nvec2 bend=vec2(0.0);\r\nfor(int i=0;i<8;i++){if(i>=uTouchCount)break;vec4 t=uTouch[i];vec2 d=root-(uFaceMin+vec2(t.x,1.0-t.y)*uFaceSize);float dist=length(d);if(dist<bendR&&dist>1e-4){float f=1.0-dist/bendR;bend+=d/dist*bendA*f*f*t.z*smoothstep(0.0,0.12,t.w);}}\r\nfor(int i=0;i<6;i++){vec4 s=uSpring[i];float age=uTime-s.z;if(s.w<=0.0||age<0.0||age>1.2)continue;vec2 d=root-(uFaceMin+vec2(s.x,1.0-s.y)*uFaceSize);float dist=length(d);if(dist<bendR&&dist>1e-4){float f=1.0-dist/bendR;bend+=d/dist*bendA*f*f*exp(-age*4.0)*cos(age*14.0)*s.w;}}\r\nvec2 wind=vec2(0.9,0.4)*uBreeze*uFaceSize.x*0.006*sin(uTime*1.6+pr.x*9.0+pr.y*5.0);\r\nbw.xy+=bend*position.y+wind*tw;bw.z-=length(bend)*0.5*position.y;\r\nvec4 mvPosition=modelViewMatrix*bw;gl_Position=projectionMatrix*mvPosition;`;\r\n\r\n// --- Vault secret: pure reducer (face-width units: x=u, y=v/aspect; state is replaced, never mutated) ---\r\nexport const SECRET={speed:1.5,lag:2,gapMs:3000,boardMs:700,flyMs:1400,crackMs:1700,splitMs:1900,shipR:.14,reach:1.5};\r\nconst d2=(a,b)=>Math.hypot(a[0]-b[0],a[1]-b[1]);\r\nexport const grassSecretInit=(start,ship,step)=>({phase:'idle',start,ship,step,path:[],len:0,pos:0,drawing:null,wait:0,last:null,t0:0,claimed:false,resets:0,added:0});\r\nexport const secretPoint=(path,d)=>{for(let i=1;i<path.length;i++){const l=d2(path[i-1],path[i]);if(d<=l||i===path.length-1){const k=l?Math.min(1,d/l):1,a=path[i-1],b=path[i];return [a[0]+(b[0]-a[0])*k,a[1]+(b[1]-a[1])*k];}d-=l;}return path[0]||[0,0];};\r\nconst NEXT={board:['boardMs','fly'],fly:['flyMs','crack'],crack:['crackMs','split'],split:['splitMs','done']};\r\n// ev: {t:'down'|'move',id,x,y} | {t:'up',id} | {t:'tick'} | {t:'reset'}. A stroke starts within reach of the alien and each point\r\n// must land 1..1.5 steps from the line's tip (a longer jump plants nothing: that is the gap).\r\nexport function grassSecret(s,ev,now){\r\n const C=SECRET,tip=s.path[s.path.length-1],p=[ev.x,ev.y];\r\n if(ev.t==='down'){\r\n  if(s.phase==='idle'&&d2(p,s.start)<=C.reach*s.step)return grassSecret({...s,phase:'walk',path:[s.start],len:0,pos:0,drawing:ev.id,wait:0,last:now,claimed:false},{...ev,t:'move'},now);\r\n  if(s.phase==='walk'&&s.drawing==null&&d2(p,tip)<=C.reach*s.step)return {...s,drawing:ev.id,wait:0};\r\n  return s;\r\n }\r\n if(ev.t==='move'){\r\n  if(s.phase!=='walk'||s.drawing!==ev.id)return s;\r\n  const d=d2(p,tip);if(d<s.step||d>C.reach*s.step)return s;\r\n  return {...s,path:[...s.path,p],len:s.len+d,wait:0,added:s.added+1};\r\n }\r\n if(ev.t==='up')return s.drawing===ev.id?{...s,drawing:null}:s;\r\n if(ev.t==='reset')return {...grassSecretInit(s.start,s.ship,s.step),resets:s.resets+1};\r\n if(ev.t!=='tick')return s;\r\n const dt=s.last==null?0:Math.min(.1,Math.max(0,(now-s.last)/1000)),n={...s,last:now};\r\n if(s.phase==='walk'){\r\n  const near=tip&&d2(tip,s.ship)<=C.shipR,limit=s.drawing!=null&&!near?Math.max(0,s.len-C.lag*s.step):s.len;\r\n  if(n.pos<limit-1e-9){n.pos=Math.min(limit,n.pos+C.speed*dt);n.wait=0;if(n.pos>0)n.claimed=true;}\r\n  else if(near&&s.len>0){n.phase='board';n.t0=now;}\r\n  else{n.wait+=dt*1000;if(n.wait>C.gapMs)return {...grassSecretInit(s.start,s.ship,s.step),last:now,resets:s.resets+1,added:s.added};}\r\n }else if(NEXT[s.phase]&&now-s.t0>=C[NEXT[s.phase][0]]){n.phase=NEXT[s.phase][1];n.t0=now;}\r\n return n;\r\n}\r\n// Pointer ids the board must not also treat as a tap/shape: the line stroke once the alien walks, everything after boarding.\r\nexport const grassSecretClaims=(s,id)=>s.phase!=='idle'&&(s.phase==='walk'?s.drawing===id&&s.claimed:true);\r\n// Jagged angular bolt a->b (face-width units), endpoints exact, offsets taper to 0 at the ends and alternate sides so it stays simple.\r\nexport function crackPath(a,b,rand=Math.random,n=12){\r\n const dx=b[0]-a[0],dy=b[1]-a[1],L=Math.hypot(dx,dy),nx=-dy/L,ny=dx/L,out=[a];\r\n for(let i=1;i<n;i++){const t=(i+(rand()-.5)*.5)/n,o=(i%2?1:-1)*L*(.03+rand()*.045)*Math.sin(Math.PI*t);out.push([a[0]+dx*t+nx*o,a[1]+dy*t+ny*o]);}\r\n return [...out,b];\r\n}\r\n\r\nlet S=null; // per-instance state; a single portal board is ever active at once (see portal-board-ice.mjs)\r\n\r\n// Tapered, slightly drooping strip: 4 rows x 2 verts = 3 quads; vertex colour darkens toward the root.\r\nfunction bladeGeometry(T){\r\n const pos=[],col=[],idx=[];\r\n [[0,.5],[.35,.42],[.7,.28],[1,.06]].forEach(([y,w],i)=>{const z=-.22*y*y,c=.55+.45*y;pos.push(-w,y,z,w,y,z);col.push(c,c,c,c,c,c);if(i)idx.push(2*i-2,2*i-1,2*i+1,2*i-2,2*i+1,2*i);});\r\n const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(pos,3));g.setAttribute('color',new T.Float32BufferAttribute(col,3));g.setIndex(idx);g.computeVertexNormals();\r\n return g;\r\n}\r\n// One InstancedMesh under the board group (instance matrices in its local space, so it scales with the\r\n// board). rootUV keeps each blade's root in face coords for cut(); rest is the matrix buffer heal() restores.\r\nfunction makeBlades(T,uniforms,group){\r\n const fw=uniforms.uFaceSize.value.x,fh=uniforms.uFaceSize.value.y,min=uniforms.uFaceMin.value,z0=uniforms.uHalfDepth.value-fw*.004;\r\n const cols=Math.round(Math.sqrt(BLADES*fw/fh)),rows=Math.round(BLADES/cols);\r\n const mat=new T.MeshLambertMaterial({vertexColors:true,side:T.DoubleSide});\r\n mat.customProgramCacheKey=()=>'grass-blades';\r\n mat.onBeforeCompile=sh=>{\r\n  for(const k of ['uTime','uTouch','uTouchCount','uFaceMin','uFaceSize','uSpring','uBreeze','uPaint'])sh.uniforms[k]=uniforms[k];\r\n  sh.vertexShader=BLADE_VDECL+sh.vertexShader.replace('#include <project_vertex>',BLADE_VERTEX);\r\n  sh.fragmentShader='varying vec2 vPlanar;\\nuniform sampler2D uPaint;\\n'+sh.fragmentShader\r\n   .replace('#include <normal_fragment_begin>','#include <normal_fragment_begin>\\nnormal=normalize(mix(normal,vec3(0.0,0.0,1.0),0.5));') // half the lawn normal: calmer shading\r\n   .replace('#include <color_fragment>','#include <color_fragment>\\nvec4 pnt=texture2D(uPaint,vPlanar);diffuseColor.rgb=mix(diffuseColor.rgb,pnt.rgb,pnt.a);')\r\n   .replace('#include <emissivemap_fragment>','#include <emissivemap_fragment>\\ntotalEmissiveRadiance+=pnt.rgb*pnt.a*'+GUIDE_GLOW+';'); // faint glow so the guides read over the busy blades\r\n };\r\n const im=new T.InstancedMesh(bladeGeometry(T),mat,cols*rows),rootUV=new Float32Array(cols*rows*2),o=new T.Object3D(),c=new T.Color();\r\n im.frustumCulled=false;\r\n for(let r=0,i=0;r<rows;r++)for(let q=0;q<cols;q++,i++){\r\n  const h=fw*(.04+Math.random()*.03),lean=.6+Math.random()*.62; // 4-7% of face width, 34-70 deg off the normal\r\n  const fx=FIELD_INSET+(q+Math.random())/cols*(1-2*FIELD_INSET),fy=FIELD_INSET+(r+Math.random())/rows*(1-2*FIELD_INSET);\r\n  o.position.set(min.x+fx*fw,min.y+fy*fh,z0);\r\n  o.rotation.set(Math.PI/2-lean,Math.random()-.5,Math.random()*Math.PI*2,'ZXY');\r\n  o.scale.set(fw*BLADE_W*(.8+Math.random()*.45),h,h);\r\n  o.updateMatrix();im.setMatrixAt(i,o.matrix);im.setColorAt(i,c.setHSL(...bladeHSL()));\r\n  rootUV[2*i]=fx;rootUV[2*i+1]=1-fy;\r\n }\r\n group.add(im);\r\n return {im,rootUV,rest:im.instanceMatrix.array.slice(),cutIdx:null};\r\n}\r\n\r\nasync function init({THREE:T,scene,mesh,material,uniforms,toWorld,faceZ,wake}){\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const box=new T.Box3();\r\n for(const m of mesh.isMesh?[mesh]:mesh.children.filter(c=>c.isMesh)){if(!m.geometry.boundingBox)m.geometry.computeBoundingBox();box.union(m.geometry.boundingBox);}\r\n uniforms.uHalfDepth.value=box.max.z; // slab is centred by the generic board: front face sits at +max.z\r\n uniforms.uBreeze.value=reduced?0:1;\r\n uniforms.uSpring.value.forEach(v=>v.set(0,0,0,0));\r\n material.color.multiplyScalar(BLOCK_EXPOSURE); // gaps between blades read as shade, not quilting\r\n const warm=new T.DirectionalLight(0xffe6b0,1.1);warm.position.set(.4,.85,.55);scene.add(warm);\r\n const [x0,y0]=toWorld(0,0),[x1,y1]=toWorld(1,1),faceW=Math.abs(x1-x0),faceH=Math.abs(y1-y0);\r\n const gltf=await new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(FLOWER_ASSET);\r\n const tmpl=gltf.scene,tsize=new T.Box3().setFromObject(tmpl).getSize(new T.Vector3());\r\n let petal=null;\r\n tmpl.traverse(n=>{if(!n.isMesh)return;n.material.metalness=0; // Kenney ships metalness 1: black without an env map\r\n  const c=n.material.color;if(n.material.name==='colorRed'||(c.r>.5&&c.g<.3&&c.b<.3))petal=n.material;});\r\n S={blocks:mesh.isMesh?[mesh]:mesh.children.filter(c=>c.isMesh),scene,toWorld,faceZ,uniforms,tmpl,petal,lawn:makeBlades(T,uniforms,mesh.isMesh?mesh.parent:mesh),warm,wake,reduced,faceW,aspect:faceW/faceH,\r\n  flowerScale:faceW*FLOWER_FRAC/(Math.max(tsize.x,tsize.z)||1),flowerScaleRatio:FLOWER_FRAC/(Math.max(tsize.x,tsize.z)||1),flowers:[],pool:[],lastPlanted:new Map(),springIdx:0,timer:0,petalTint:null};\r\n S.ob=buildSecret(T,scene);secReset();\r\n window.myr5GrassSecret={autoWalk,state:()=>S?.sec,claims:id=>claims(id),freeze};\r\n}\r\nfunction makeFlower(){\r\n const obj=S.tmpl.clone(true),mats=[];let petal=null;\r\n obj.traverse(n=>{if(!n.isMesh)return;const m=n.material.clone();m.transparent=true;if(n.material===S.petal)petal=m;n.material=m;mats.push(m);});\r\n return {obj,mats,petal,randomColor:new THREE.Color()};\r\n}\r\nfunction setPetalTint(hex,selected=true){\r\n if(!S)return;if(selected&&!/^#[0-9a-f]{6}$/i.test(hex||''))return;\r\n S.petalTint=selected?new THREE.Color(hex):null;\r\n for(const f of [...S.flowers,...S.pool])f.petal?.color.copy(S.petalTint||f.randomColor);\r\n}\r\nfunction plant(u,v,hold){\r\n const [wx,wy]=S.toWorld(u,v);\r\n const f=(S.flowers.length>=FLOWER_CAP?S.flowers.shift():S.pool.pop())||makeFlower();\r\n f.randomColor.setHSL(...flowerHSL());f.petal?.color.copy(S.petalTint||f.randomColor);\r\n f.obj.position.set(wx,wy,S.faceZ+S.faceW*FLOWER_LIFT);f.obj.visible=true;f.u=u;f.v=v;\r\n f.obj.rotation.set(FLOWER_TILT,Math.random()*Math.PI*2,0); // stand toward the camera, random yaw about the stem\r\n f.hold=!!hold;f.sizeFactor=.8+Math.random()*.5;f.target=S.flowerScale*f.sizeFactor;f.born=performance.now();\r\n f.obj.scale.setScalar(S.reduced?f.target:1e-4);\r\n for(const m of f.mats)m.opacity=1;\r\n S.scene.add(f.obj);S.flowers.push(f);\r\n}\r\nfunction plantCluster(u,v,n,hold){for(const [du,dv] of clusterOffsets(n,CLUSTER_FRAC))plant(clamp01(u+du),clamp01(v+dv*S.aspect),hold);}\r\n// Vault secret: a stroke the reducer owns plants only the flowers that extend its line (so planted line === walkable line).\r\nconst pt=(u,v)=>({x:u,y:v/S.aspect});\r\nfunction press(id,u,v){\r\n if(S.sec.phase==='done')secReset(); // the board was healed/resumed after the door showed: start over\r\n secEv({t:'down',id,...pt(u,v)});\r\n plantCluster(u,v,3,S.sec.drawing===id);S.lastPlanted.set(id,[u,v]);\r\n}\r\nfunction claims(id){return !!S&&grassSecretClaims(S.sec,id);}\r\nfunction move(id,u,v){\r\n if(S.sec.drawing===id){const n=S.sec.added;secEv({t:'move',id,...pt(u,v)});if(S.sec.added!==n)plantCluster(u,v,2+(Math.random()<.5),true);return;}\r\n if(S.sec.phase!=='idle'&&S.sec.phase!=='walk')return;\r\n const cur=S.toWorld(u,v),last=S.lastPlanted.get(id);\r\n if(!last){S.lastPlanted.set(id,[u,v]);return;}\r\n const old=S.toWorld(last[0],last[1]);\r\n if(plantStep(old[0],old[1],cur[0],cur[1])){plantCluster(u,v,2+(Math.random()<.5));S.lastPlanted.set(id,[u,v]);}\r\n}\r\nfunction resize(){\r\n if(!S)return;\r\n const [x0,y0]=S.toWorld(0,0),[x1,y1]=S.toWorld(1,1);S.faceW=Math.abs(x1-x0);S.aspect=S.faceW/Math.max(1,Math.abs(y1-y0));S.flowerScale=S.faceW*S.flowerScaleRatio;if(S.sec.phase==='idle')secReset();\r\n const now=performance.now();\r\n for(const f of S.flowers){\r\n  const [x,y]=S.toWorld(f.u,f.v),age=now-f.born,k=fadeLife(age),pop=S.reduced?1:popScale(age);\r\n  f.obj.position.set(x,y,S.faceZ+S.faceW*FLOWER_LIFT);f.target=S.flowerScale*f.sizeFactor;f.obj.scale.setScalar(f.target*pop*k);\r\n }\r\n}\r\nfunction release(id,u,v){\r\n secEv({t:'up',id});S.lastPlanted.delete(id);\r\n const slot=S.uniforms.uSpring.value[S.springIdx];\r\n slot.set(u,v,S.uniforms.uTime.value,1);\r\n S.springIdx=(S.springIdx+1)%S.uniforms.uSpring.value.length;\r\n}\r\nfunction step(dt,now){\r\n let animating=secStep(dt,now);\r\n for(let i=S.flowers.length-1;i>=0;i--){\r\n  const f=S.flowers[i],age=now-f.born,k=fadeLife(age);\r\n  if(k<=0||(S.reduced&&k<1)){S.scene.remove(f.obj);S.pool.push(f);S.flowers.splice(i,1);continue;}\r\n  const pop=S.reduced?1:popScale(age);\r\n  if(pop<1||k<1)animating=true;\r\n  f.obj.scale.setScalar(f.target*pop*k);\r\n  for(const m of f.mats)m.opacity=k;\r\n }\r\n const t=S.uniforms.uTime.value;\r\n for(const v of S.uniforms.uSpring.value){\r\n  if(v.w<=0)continue;\r\n  if(springActive(t-v.z))animating=true;else v.set(0,0,0,0);\r\n }\r\n // Flowers still waiting to fade: sleep, but wake when the oldest one's fade starts.\r\n clearTimeout(S.timer);\r\n if(!animating&&S.flowers.length)S.timer=setTimeout(S.wake,Math.max(0,S.flowers[0].born+FADE_MS-now));\r\n return animating;\r\n}\r\n// Board hooks: after the board cuts polyUv (face coords, v down) out of the block, collapse every blade\r\n// rooted inside it (zero the instance's 3x3, so its triangles are zero-area) and hide flowers inside it,\r\n// so the hole's edge follows the shape; heal() restores the saved matrices and shows the flowers.\r\nfunction cut(polyUv){\r\n if(!S)return;\r\n heal();\r\n const {im,rootUV}=S.lawn,m=im.instanceMatrix.array,idx=rootsInside(rootUV,polyUv);\r\n for(const i of idx)for(const k of [0,1,2,4,5,6,8,9,10])m[16*i+k]=0;\r\n im.instanceMatrix.needsUpdate=true;S.lawn.cutIdx=idx;\r\n for(const f of S.flowers)if(pointInPolygon(f.u,f.v,polyUv))f.obj.visible=false;\r\n}\r\nfunction heal(){\r\n if(!S)return;if(S.sec.phase!=='idle')secReset();\r\n if(!S.lawn.cutIdx)return;\r\n const {im,rest,cutIdx}=S.lawn,m=im.instanceMatrix.array;\r\n for(const i of cutIdx)m.set(rest.subarray(16*i,16*i+16),16*i);\r\n im.instanceMatrix.needsUpdate=true;S.lawn.cutIdx=null;\r\n for(const f of S.flowers)f.obj.visible=true;\r\n}\r\nfunction dispose(){\r\n clearTimeout(S.timer);\r\n for(const f of [...S.flowers,...S.pool]){S.scene.remove(f.obj);for(const m of f.mats)m.dispose();}\r\n S.tmpl.traverse(n=>{n.geometry?.dispose();if(n.material)for(const m of [n.material].flat())m.dispose();});\r\n const {im}=S.lawn;im.removeFromParent();im.geometry.dispose();im.material.dispose();im.dispose();\r\n S.scene.remove(S.warm);\r\n if(S.fx)secDropFx();for(const o of [S.ob.shade,S.ob.alien,S.ob.tuft,S.ob.ufo,S.ob.beam])S.scene.remove(o);for(const d of S.ob.dis)d.dispose();delete window.myr5GrassSecret;\r\n S=null;\r\n}\r\n\r\n// --- Vault secret: THREE visuals (all primitives; unit = face width, so every object is scaled by S.faceW) ---\r\nconst SEC_START=[.1,.085],SEC_SHIP=[.9,.915]; // face (u,v) corners: alien top-left, ship bottom-right\r\nconst ease=t=>{t=clamp01(t);return t*t*(3-2*t);};\r\nfunction secGeom(){const W=S.faceW,hw=1/S.aspect;return {W,hw,start:[SEC_START[0],SEC_START[1]*hw],ship:[SEC_SHIP[0],SEC_SHIP[1]*hw]};}\r\nfunction secReset(){const g=secGeom();if(S.fx)secDropFx();for(const f of S.flowers)f.obj.visible=true;S.sec=grassSecretInit(g.start,g.ship,PLANT_STEP_PX/g.W);S.emerge=0;S.fired=false;S.vis=null;S.seen=0;S.apos=g.start;}\r\nfunction secEv(ev){const before=S.sec;S.sec=grassSecret(S.sec,ev,performance.now());return S.sec!==before;}\r\nfunction buildSecret(T,scene){\r\n const dis=[],mat=m=>(dis.push(m),m),geo=g=>(dis.push(g),g),radial=col=>{const c=document.createElement('canvas');c.width=c.height=64;const g=c.getContext('2d'),r=g.createRadialGradient(32,32,2,32,32,32);r.addColorStop(0,col);r.addColorStop(1,'rgba(0,0,0,0)');g.fillStyle=r;g.fillRect(0,0,64,64);const t=new T.CanvasTexture(c);dis.push(t);return t;};\r\n const lam=(color,extra)=>mat(new T.MeshLambertMaterial({color,...extra})),blob=(r,sx,sy,m)=>{const o=new T.Mesh(geo(new T.SphereGeometry(r,16,10)),m);o.scale.set(sx,sy,1);return o;};\r\n const disc=(r,col,op)=>new T.Mesh(geo(new T.PlaneGeometry(r*2,r*2)),mat(new T.MeshBasicMaterial({map:radial(col),transparent:true,opacity:op,depthWrite:false})));\r\n // alien: the top of a MYR5 alien's head, a purple cone (MOM purple #7a2fc4) with a pale nub, half hidden by a grass tuft until it walks\r\n const coneG=geo(new T.ConeGeometry(.03,.075,20));coneG.translate(0,.0375,0);\r\n const cone=new T.Mesh(coneG,lam(0xa45cff,{emissive:0x5a24a8})),rim=new T.Mesh(coneG,mat(new T.MeshBasicMaterial({color:0xe4ccff,side:T.BackSide}))),nub=new T.Mesh(geo(new T.SphereGeometry(.006,10,8)),mat(new T.MeshBasicMaterial({color:0xc9a0ff})));nub.position.y=.076;\r\n rim.scale.set(1.28,1.1,1.28);rim.position.y=-.004;const alien=new T.Group();alien.add(rim,cone,nub);\r\n const tuft=new T.Group(),shade=disc(.12,'rgba(0,0,0,.95)',.8);tuft.add(blob(.034,1.1,.4,lam(0x2f7a24,{emissive:0x0c2a08})));\r\n // ufo: squashed sphere + glass dome + gold ring, tilted sideways, its lower side buried in a dirt mound\r\n const ufoTilt=new T.Group(),saucer=blob(.07,1,.32,lam(0xc4ccd6,{emissive:0x3a424c})),ring=new T.Mesh(geo(new T.TorusGeometry(.07,.005,6,28)),lam(0xffd36e,{emissive:0x6a4a10})),dome=new T.Mesh(geo(new T.SphereGeometry(.034,16,10,0,Math.PI*2,0,Math.PI/2)),lam(0xbfe6ff,{emissive:0x2a5a7a,transparent:true,opacity:.8}));\r\n ring.rotation.x=Math.PI/2;dome.position.y=.008;ufoTilt.add(saucer,ring,dome);ufoTilt.rotation.z=-.9;\r\n const mound=blob(.045,1.2,.4,lam(0x6b4526)),dirt=disc(.12,'rgba(70,44,24,1)',.85);mound.position.set(-.03,.048,.02);\r\n const ufo=new T.Group();ufo.add(dirt,ufoTilt,mound);\r\n const bc=document.createElement('canvas');bc.width=8;bc.height=64;{const g=bc.getContext('2d'),r=g.createLinearGradient(0,0,0,64);r.addColorStop(0,'rgba(255,236,170,.95)');r.addColorStop(1,'rgba(176,108,255,0)');g.fillStyle=r;g.fillRect(0,0,8,64);}\r\n const bt=new T.CanvasTexture(bc);dis.push(bt);const beam=new T.Mesh(geo(new T.PlaneGeometry(1,1)),mat(new T.MeshBasicMaterial({map:bt,transparent:true,blending:T.AdditiveBlending,depthWrite:false,opacity:0})));beam.visible=false;\r\n for(const o of [shade,alien,tuft,ufo,beam])scene.add(o);\r\n return {alien,tuft,shade,ufo,ufoTilt,beam,dis};\r\n}\r\n// ground crack + split, built into the living lawn: the real blades near the bolt bend away from it, dirt shows in the line, then the\r\n// blades/flowers of each side (plus a dark ground sheet each, standing in for the hidden block) slide apart and reveal a gunmetal/gold/purple\r\n// plate (stands in for the vault door poster).\r\nfunction buildCrack(){\r\n const T=THREE,{W,hw,start,ship}=secGeom(),main=crackPath(ship,start),edge=[[1,hw],...main,[0,0]],clampP=p=>[Math.min(1,Math.max(0,p[0])),Math.min(hw,Math.max(0,p[1]))];\r\n const polyA=[...edge,[1,0]],polyB=[...edge,[0,hw]],cw=384,ch=Math.round(cw*hw),dis=[];\r\n const cvs=()=>{const c=document.createElement('canvas');c.width=cw;c.height=ch;return c;},path=(g,pts)=>{g.beginPath();pts.forEach(([x,y],i)=>g[i?'lineTo':'moveTo'](x*cw,y*cw));};\r\n const tex=c=>{const t=new T.CanvasTexture(c);t.colorSpace=T.SRGBColorSpace;dis.push(t);return t;};\r\n const ground=poly=>{const c=cvs(),g=c.getContext('2d');g.save();path(g,poly);g.closePath();g.clip();g.fillStyle='#24132f';g.fillRect(0,0,cw,ch);\r\n  g.lineJoin='miter';path(g,edge);g.strokeStyle='#2e1b0e';g.lineWidth=cw*.045;g.stroke();g.strokeStyle='#6b4a2a';g.lineWidth=cw*.016;g.stroke();g.restore();return tex(c);};\r\n const plate=(()=>{const c=cvs(),g=c.getContext('2d'),gr=g.createLinearGradient(0,0,cw,ch);gr.addColorStop(0,'#1d2026');gr.addColorStop(.5,'#2b2f38');gr.addColorStop(1,'#16181d');g.fillStyle=gr;g.fillRect(0,0,cw,ch); // dark gunmetal\r\n  g.strokeStyle='rgba(255,255,255,.04)';for(let y=0;y<ch;y+=3){g.beginPath();g.moveTo(0,y);g.lineTo(cw,y);g.stroke();}\r\n  const glow=g.createRadialGradient(cw/2,ch/2,cw*.05,cw/2,ch/2,cw*.75);glow.addColorStop(0,'rgba(150,70,240,.55)');glow.addColorStop(1,'rgba(122,47,196,0)');g.fillStyle=glow;g.fillRect(0,0,cw,ch); // purple glow\r\n  g.strokeStyle='#0d0e11';g.lineWidth=7;g.strokeRect(cw*.08,ch*.1,cw*.84,ch*.8);g.strokeStyle='#ffd36e';g.lineWidth=3;g.strokeRect(cw*.08+5,ch*.1+5,cw*.84-10,ch*.8-10); // gold trim\r\n  g.fillStyle='#ffd36e';for(const [x,y] of [[.14,.14],[.86,.14],[.14,.86],[.86,.86]]){g.beginPath();g.arc(x*cw,y*ch,5,0,7);g.fill();}\r\n  g.shadowColor='#b06cff';g.shadowBlur=18;g.strokeStyle='#ffd36e';g.lineWidth=4;g.beginPath();g.arc(cw/2,ch/2,cw*.18,0,7);g.stroke();return tex(c);})();\r\n const shape=(poly,t)=>{const sh=new T.Shape();poly.forEach(([x,y],i)=>sh[i?'lineTo':'moveTo'](x-.5,hw/2-y));const g=new T.ShapeGeometry(sh),p=g.attributes.position,uv=g.attributes.uv;dis.push(g);\r\n  for(let i=0;i<p.count;i++)uv.setXY(i,p.getX(i)+.5,1-(hw/2-p.getY(i))/hw);\r\n  const m=new T.MeshBasicMaterial({map:t});dis.push(m);const o=new T.Mesh(g,m);o.scale.setScalar(W);return o;};\r\n const A=shape(polyA,ground(polyA)),B=shape(polyB,ground(polyB)),P=new T.Mesh(new T.PlaneGeometry(1,hw),new T.MeshBasicMaterial({map:plate}));dis.push(P.geometry,P.material);P.scale.setScalar(W);P.visible=false;\r\n const gc=cvs(),glow=new T.CanvasTexture(gc),G=new T.Mesh(new T.PlaneGeometry(1,hw),new T.MeshBasicMaterial({map:glow,transparent:true,depthWrite:false}));dis.push(glow,G.geometry,G.material);G.scale.setScalar(W);\r\n const branches=[2,5,8].map(i=>{const a=main[i],d=i%2?1:-1;return [a,clampP([a[0]+.06*d,a[1]+.05]),clampP([a[0]+.1*d,a[1]+.045*d+.09]),clampP([a[0]+.13*d,a[1]+.14])];});\r\n const cx=S.toWorld(.5,.5),ctr=(o,z)=>o.position.set(cx[0],cx[1],S.faceZ+W*z);\r\n ctr(A,-.0045);ctr(B,-.0045);ctr(P,-.0052);ctr(G,.08);for(const o of [A,B,P,G])S.scene.add(o);\r\n const mean=p=>p.reduce((a,q)=>[a[0]+q[0]/p.length,a[1]+q[1]/p.length],[0,0]),ca=mean(polyA),cb=mean(polyB),dx=ca[0]-cb[0],dy=-(ca[1]-cb[1]),dl=Math.hypot(dx,dy)||1;\r\n // every blade/flower: which side of the bolt and how far from it (face-width units)\r\n const uvA=polyA.map(([x,y])=>[x,y*S.aspect]),dseg=(p,a,b)=>{const vx=b[0]-a[0],vy=b[1]-a[1],k=clamp01(((p[0]-a[0])*vx+(p[1]-a[1])*vy)/(vx*vx+vy*vy||1));return Math.hypot(p[0]-a[0]-vx*k,p[1]-a[1]-vy*k);};\r\n const ru=S.lawn.rootUV,n=ru.length/2,bs=new Int8Array(n),bd=new Float32Array(n);\r\n for(let i=0;i<n;i++){const p=[ru[2*i],ru[2*i+1]/S.aspect];bs[i]=pointInPolygon(ru[2*i],ru[2*i+1],uvA)?1:-1;let d=9;for(let k=1;k<main.length;k++)d=Math.min(d,dseg(p,main[k-1],main[k]));bd[i]=d;}\r\n for(const f of S.flowers)f.side=pointInPolygon(f.u,f.v,uvA)?1:-1;\r\n for(const m of S.blocks)m.visible=false; // the ground sheets stand in for the block\r\n S.fx={A,B,P,G,main,branches,gc,glow,cw,ch,cx,dir:[dx/dl,dy/dl],dis,drawn:-1,bs,bd};\r\n}\r\nfunction drawCrack(prog){\r\n const F=S.fx;if(F.drawn===prog)return;F.drawn=prog;const g=F.gc.getContext('2d'),{cw,ch}=F;g.clearRect(0,0,cw,ch);if(prog<=0){F.glow.needsUpdate=true;return;}\r\n const seg=(pts,k)=>{g.beginPath();let tot=0;for(let i=1;i<pts.length;i++)tot+=d2(pts[i-1],pts[i]);let rem=tot*k;g.moveTo(pts[0][0]*cw,pts[0][1]*cw);for(let i=1;i<pts.length&&rem>0;i++){const l=d2(pts[i-1],pts[i]),f=Math.min(1,rem/l);g.lineTo((pts[i-1][0]+(pts[i][0]-pts[i-1][0])*f)*cw,(pts[i-1][1]+(pts[i][1]-pts[i-1][1])*f)*cw);rem-=l;}g.stroke();};\r\n g.lineJoin='miter';g.lineCap='round';g.miterLimit=3;\r\n for(const [w,col,blur] of [[cw*.045,'#1c0f06',0],[cw*.026,'#4a2f19',0],[cw*.016,'#ff8a1f',cw*.04],[cw*.006,'#fff3c2',0]]){g.lineWidth=w;g.strokeStyle=col;g.shadowColor='#ff9a2a';g.shadowBlur=blur;seg(F.main,prog);if(prog>.35)for(const b of F.branches)seg(b,Math.min(1,(prog-.35)/.5));} // dirt bed, then the glow\r\n F.glow.needsUpdate=true;\r\n}\r\n// slide each real blade along the bolt's cross direction: crack-time parting (blades near the line lean away) + split-time pull-apart\r\nfunction moveLawn(part,sp){\r\n const F=S.fx,{im,rest}=S.lawn,m=im.instanceMatrix.array,fw=S.uniforms.uFaceSize.value.x,[dx,dy]=F.dir,pa=part*.06*fw,mv=sp*.7*fw;\r\n for(let i=0;i<F.bs.length;i++){const k=Math.max(0,1-F.bd[i]/.09),o=F.bs[i]*(k*k*pa+mv);m[16*i+12]=rest[16*i+12]+dx*o;m[16*i+13]=rest[16*i+13]+dy*o;}\r\n im.instanceMatrix.needsUpdate=true;\r\n}\r\nfunction secDropFx(){const F=S.fx;S.fx=null;for(const o of [F.A,F.B,F.P,F.G])S.scene.remove(o);for(const d of F.dis)d.dispose();\r\n const {im,rest}=S.lawn,m=im.instanceMatrix.array;for(let i=0;i<F.bs.length;i++){m[16*i+12]=rest[16*i+12];m[16*i+13]=rest[16*i+13];}im.instanceMatrix.needsUpdate=true;im.visible=true;\r\n for(const b of S.blocks)b.visible=true;resize();}\r\n// per-frame: tick the reducer, then pose everything from it. Returns true while anything is moving.\r\nfunction secStep(dt,now){\r\n if(S.sec.phase==='done'&&S.fired&&!S.vis)return false;\r\n if(S.freezeT==null)S.sec=grassSecret(S.sec,{t:'tick'},now);const sec=S.sec,ph=sec.phase,W=S.faceW,wp=(x,y)=>S.toWorld(x,y*S.aspect),set=(o,[x,y],z,sc=1)=>{const [px,py]=wp(x,y);o.position.set(px,py,S.faceZ+W*z);o.scale.setScalar(W*sc);},t=S.freezeT??now-sec.t0;\r\n if(sec.resets!==S.seen){S.seen=sec.resets;S.vis={t0:now,from:S.apos};}\r\n S.emerge=ph==='walk'?Math.min(1,S.emerge+dt*3):ph==='idle'?0:S.emerge;\r\n let ap=ph==='walk'?secretPoint(sec.path,sec.pos):sec.start,asc=1;\r\n if(ph==='board'){const k=ease(t/SECRET.boardMs),e=secretPoint(sec.path,sec.len);ap=[e[0]+(sec.ship[0]-e[0])*k,e[1]+(sec.ship[1]-e[1])*k];asc=1-k;}\r\n let vis=false;\r\n if(S.vis){const k=(now-S.vis.t0)/350;vis=k<2.4;if(k<1){ap=S.vis.from||ap;asc=1-ease(k);}else asc=ease((k-1)/1.4);if(!vis)S.vis=null;}\r\n S.apos=ap;const bob=ph==='walk'?Math.abs(Math.sin(now*.012))*.01:0,{alien,tuft,shade,ufo,ufoTilt}=S.ob,em=S.emerge;\r\n alien.visible=ph==='idle'||ph==='walk'||ph==='board'||vis;alien.rotation.z=ph==='walk'?Math.sin(now*.012)*.14:0;set(alien,[ap[0],ap[1]+(1-em)*.02-bob],.03,asc);\r\n tuft.visible=alien.visible&&em<1;set(tuft,[sec.start[0],sec.start[1]+.005],.036,1-em);shade.visible=ph==='idle'||ph==='walk'||vis;set(shade,sec.start,.02);\r\n let up=sec.ship,usc=1,tilt=-.9,fk=0;const spent=ph==='crack'||ph==='split'||ph==='done';\r\n if(ph==='fly'){fk=ease(t/SECRET.flyMs);up=[sec.ship[0]+Math.sin(t*.018)*.014*(1-fk),sec.ship[1]-fk*fk*2.2];usc=1-.85*fk;tilt=-.9*(1-Math.min(1,fk*4))+Math.sin(t*.014)*.08*(1-fk);} // straight up, small wobble\r\n ufo.visible=!spent;ufoTilt.rotation.z=tilt;set(ufo,up,.016+fk*.3,usc);\r\n {const b=S.ob.beam;b.visible=ph==='fly'&&fk<.97;if(b.visible){const h=Math.max(.1,sec.ship[1]-up[1]+.08),[bx,by]=wp(up[0],up[1]+h/2);b.position.set(bx,by,S.faceZ+W*.05);b.scale.set(W*.1*usc,W*h,1);b.material.opacity=.75*Math.sin(Math.PI*Math.min(1,fk*1.1+.05));}}\r\n let anim=ph==='walk'||ph==='board'||ph==='fly'||vis;\r\n if(spent){\r\n  if(!S.fx)buildCrack();\r\n  const F=S.fx,k=ph==='crack'?t/SECRET.crackMs:1,sk=ph==='split'?t/SECRET.splitMs:ph==='done'?1:0,sp=ease(sk),prog=Math.round(ease(Math.min(1,k*1.15))*40)/40;\r\n  drawCrack(prog);F.G.material.opacity=1-clamp01(sk*3);F.G.visible=sk<.34;F.P.visible=sk>0;\r\n  const sig=prog+'|'+sp.toFixed(3);if(F.sig!==sig){F.sig=sig;moveLawn(prog,sp);}\r\n  const gone=sk>=.85;S.lawn.im.visible=!gone;\r\n  for(const [o,sg] of [[F.A,1],[F.B,-1]]){o.visible=!gone;o.position.set(F.cx[0]+F.dir[0]*sg*sp*W*.7,F.cx[1]+F.dir[1]*sg*sp*W*.7,S.faceZ-W*.0045);}\r\n  for(const f of S.flowers){const [x,y]=S.toWorld(f.u,f.v);f.obj.visible=!gone;f.obj.position.set(x+F.dir[0]*f.side*sp*W*.7,y+F.dir[1]*f.side*sp*W*.7,S.faceZ+W*FLOWER_LIFT);}\r\n  anim=ph!=='done';\r\n  if(ph==='done'&&!S.fired){S.fired=true;window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'grass'}}));}\r\n }\r\n // line flowers never fade while a walk is live: pin them at full pop age\r\n if(ph!=='idle')for(const f of S.flowers)if(f.hold&&now-f.born>FADE_MS-1500)f.born=now-POP_MS-1;\r\n return anim;\r\n}\r\n// debug hook (screenshots): hold the ship/crack sequence at phase `ph`, fraction k of its duration; freeze(null) resumes from idle.\r\nfunction freeze(ph,k=0){\r\n if(!ph){S.freezeT=null;secReset();return;}\r\n if(S.fx)secDropFx();for(const f of S.flowers)f.obj.visible=true;\r\n const g=secGeom(),e=g.start,d=SECRET[NEXT[ph]?.[0]]||1;S.freezeT=k*d;S.fired=false;\r\n S.sec={...grassSecretInit(g.start,g.ship,PLANT_STEP_PX/g.W),phase:ph,path:[e,g.ship],len:d2(e,g.ship)};S.wake();\r\n}\r\n// debug / test hook: draw a flower line from the alien to the ship through the real press/move/release path.\r\n// opts: gapAt (0..1 of the way: lift there), gapMs (stay lifted this long, then draw on; 0 = never resume), rate (steps per second).\r\nasync function autoWalk({gapAt=null,gapMs=0,rate=30}={}){\r\n const g=secGeom(),sleep=ms=>new Promise(r=>setTimeout(r,ms)),id=77,at=k=>[g.start[0]+(g.ship[0]-g.start[0])*k,g.start[1]+(g.ship[1]-g.start[1])*k],uv=([x,y])=>[x,y*S.aspect],n=Math.ceil(d2(g.start,g.ship)/(PLANT_STEP_PX/g.W*1.1));\r\n let down=false;\r\n for(let i=0;i<=n;i++){\r\n  const k=i/n,[u,v]=uv(at(k));\r\n  if(gapAt!=null&&k>=gapAt&&down){release(id,u,v);down=false;if(!gapMs)return;await sleep(gapMs);gapAt=null;}\r\n  if(!down){press(id,u,v);down=true;}else move(id,u,v);\r\n  S.wake();await sleep(1000/rate);\r\n }\r\n if(down)release(id,...uv(g.ship));\r\n}\r\n\r\n// R7: the flowers in 2D, for the flat board: the same clusters along a drag, popping in and fading out after FADE_MS,\r\n// drawn on `paint` as five-petal blooms with a dark rim so they read on the lawn. Own state, no THREE.\r\nexport function grassFlowers(){\r\n let S=null;\r\n const bloom=(g,f,k)=>{\r\n  const r=f.r*k;if(r<.5)return;\r\n  g.save();g.translate(f.x,f.y);g.rotate(f.rot);g.globalAlpha=k;g.fillStyle=f.col;g.strokeStyle='rgba(20,30,10,.55)';g.lineWidth=r*.12;\r\n  for(let i=0;i<5;i++){const a=i*Math.PI*2/5;g.beginPath();g.ellipse(Math.cos(a)*r*.55,Math.sin(a)*r*.55,r*.52,r*.34,a,0,Math.PI*2);g.fill();g.stroke();}\r\n  g.fillStyle='#ffd23a';g.beginPath();g.arc(0,0,r*.27,0,Math.PI*2);g.fill();g.stroke();g.restore();\r\n };\r\n const plantOne=(u,v)=>{const [h,s,l]=flowerHSL(),randomColor=`hsl(${h*360} ${s*100}% ${l*100}%)`;S.flowers.push({x:u*S.W,y:v*S.H,r:S.W*FLOWER_FRAC*.5*(.8+Math.random()*.5),rot:Math.random()*Math.PI,randomColor,col:S.petalTint||randomColor,born:performance.now()});if(S.flowers.length>FLOWER_CAP)S.flowers.shift();};\r\n const cluster=(u,v,n)=>{for(const [du,dv] of clusterOffsets(n,CLUSTER_FRAC))plantOne(clamp01(u+du),clamp01(v+dv*S.aspect));};\r\n return {\r\n  init({paint,toWorld,wake}){S={paint,toWorld,wake,W:paint.canvas.width,H:paint.canvas.height,aspect:paint.canvas.width/paint.canvas.height,flowers:[],last:new Map(),timer:0,reduced:matchMedia('(prefers-reduced-motion: reduce)').matches,petalTint:null};},\r\n  setTint(hex,selected=true){if(selected&&!/^#[0-9a-f]{6}$/i.test(hex||''))return;S.petalTint=selected?hex:null;for(const f of S.flowers)f.col=S.petalTint||f.randomColor;S.wake();},\r\n  press(id,u,v){cluster(u,v,3);S.last.set(id,S.toWorld(u,v));},\r\n  move(id,u,v){const cur=S.toWorld(u,v),last=S.last.get(id);if(!last){S.last.set(id,cur);return;}if(plantStep(last[0],last[1],cur[0],cur[1])){cluster(u,v,2+(Math.random()<.5));S.last.set(id,cur);}},\r\n  release(id){S.last.delete(id);},\r\n  step(dt,now){\r\n   const g=S.paint.ctx,n=S.flowers.length;let animating=false;\r\n   S.flowers=S.flowers.filter(f=>fadeLife(now-f.born)>0);\r\n   g.clearRect(0,0,S.W,S.H);\r\n   for(const f of S.flowers){const age=now-f.born,k=fadeLife(age),pop=S.reduced?1:popScale(age);if(pop<1||k<1)animating=true;bloom(g,f,pop*k);}\r\n   clearTimeout(S.timer);if(!animating&&S.flowers.length)S.timer=setTimeout(S.wake,Math.max(0,S.flowers[0].born+FADE_MS-now));\r\n   return animating||S.flowers.length!==n;\r\n  },\r\n  dispose(){clearTimeout(S?.timer);S=null;},\r\n };\r\n}\r\ngrassFlowers.tintTarget='trace';\r\n\r\nexport const grass={\r\n id:'grass',asset:'/pod/worlds/boards/grass.glb',flip:false,background:'#0b150a',\r\n guide:{color:'#d8ffc0',alpha:.2,width:4},\r\n uniforms:{uHalfDepth:{value:0},uSpring:{value:Array.from({length:6},()=>new THREE.Vector4(0,0,0,0))},uBreeze:{value:1}},\r\n uniformDecls:'uniform float uHalfDepth;\\nuniform vec4 uSpring[6];\\nuniform float uBreeze;\\n',\r\n vertexDisplace:VERTEX_DISPLACE,\r\n  init,press,move,release,claims,resize,step,cut,heal,healSecret:heal,dispose,setTint:setPetalTint,trace2d:grassFlowers,\r\n};\r\n","modules/portal/portal-board-ice.mjs":"// Cracking-ice effect for portal-board-glb.mjs. Fracture geometry for the big crack comes from the vendored\r\n// MIT cracked-glass core (cracked-glass-core.mjs) unchanged; this file only drives it: one big radial crack\r\n// where the finger lands, then, as the finger drags, a hairline along its path with small V / Y / Z cracks\r\n// shooting off sideways just behind it, each retracting tips-first back into its root. All drawn on the glow\r\n// layer only so the paint layer keeps the guides: a cyan-white core over a thin dark rim (EDGE), which emits nothing\r\n// but darkens the ice under it (ice.fragment), so the cracks read on the dark 3D ice and on the pale flat poster alike.\r\n// AGPL-3.0-or-later.\r\nimport {generateFracture,computeFrame} from './cracked-glass-core.mjs';\r\n\r\n// Big press crack: field side as a fraction of the paint width, lifecycle (ms), fracture options.\r\nexport const BIG={frac:.45,grow:300,hold:1000,shrink:400,opts:{rays:{count:7},rings:{count:3}}};\r\n// Drag trail knobs. Timings in ms: shoot out (ease-out) / hold / retract (the growth run backwards).\r\n// Ranges are [min,max] picked at random per crack. Screen px, so they look the same on any face size.\r\n// Each crack is gone 1.71 s after its birth (R7: held ~2x longer so it reads), well inside Ian's 7 s rule.\r\nexport const TRAIL={grow:110,hold:700,retract:900,\r\n spacing:[9,20],   // screen px of finger travel between cracks\r\n size:[18,40],     // screen px, a crack's unit length (V prongs ~ size, Y and Z a bit shorter overall)\r\n sweep:[10,55],    // degrees a crack leans back from square-off the path (a wake behind the finger)\r\n jag:.13,          // sideways kink of each third of a segment, as a fraction of its length\r\n // R7 (Ian 26 Sept: \"i cant see ... the ice cracks\"): 5x the first tuning; a 1024 px face is ~330 CSS px on a phone.\r\n width:9,hair:5,taper:.75, // core width at the root / of the path hairline (paint px at 1024), tip taper\r\n max:70};          // ponytail: cap on live trail cracks; the oldest (already mostly retracted) is dropped first on a very fast drag\r\nconst MAX_BIG=14; // the 10-tap chain keeps one crack per tap\r\n// ponytail: POOL big fractures reused with a random spin, LEVELS cached frames each, since\r\n// computeFrame is ~2 ms per call; raise POOL if repeats ever show.\r\nconst POOL=6,LEVELS=16,FRAME_OPTS={quality:'normal',timeline:{crackStart:0,crackEnd:1,shatterStart:Infinity}};\r\n// CORE: the crack line (a tight cyan GLINT round it); EDGE: the dark rim, EDGE_PX each side; BIG_EDGE: the big crack's\r\n// stroke. Paint px at 1024.\r\nconst CORE='#d6fbff',GLINT='rgba(120,230,255,.9)',EDGE='rgba(2,20,32,.95)',EDGE_PX=3,BIG_EDGE=4;\r\nlet selectedTint=null;\r\nconst validTint=hex=>typeof hex==='string'&&/^#[0-9a-f]{6}$/i.test(hex);\r\nfunction tintRgba(hex,alpha){const n=parseInt(hex.slice(1),16);return `rgba(${n>>16},${(n>>8)&255},${n&255},${alpha})`;}\r\n\r\n// --- Pure lifecycle helpers (age in ms since spawn) ------------------------------------------\r\n// Big crack growth 0..1: grow, hold at 1, shrink back to 0; reduced motion is fully grown until the hold ends.\r\nexport function crackT(age,reduced=false,L=BIG){\r\n if(age<0||crackAlpha(age,reduced,L)===0)return 0;\r\n if(reduced)return 1;\r\n if(age<L.grow)return age/L.grow;\r\n if(age<L.grow+L.hold)return 1;\r\n return 1-(age-L.grow-L.hold)/L.shrink;\r\n}\r\n// Big crack opacity: 1 until the shrink starts, then fades with it; 0 means dead.\r\nexport function crackAlpha(age,reduced=false,L=BIG){\r\n if(age<0)return 0;\r\n const held=L.grow+L.hold;\r\n if(age<held)return 1;\r\n if(reduced)return 0;\r\n return Math.max(0,1-(age-held)/L.shrink);\r\n}\r\n// Trail crack: fraction 0..1 of its length that is visible. No fade: it shoots out, holds, then the same\r\n// curve runs backwards so it retracts tips-first into its root. Reduced motion: whole until the hold ends.\r\nexport const trailLife=(reduced=false,K=TRAIL)=>K.grow+K.hold+(reduced?0:K.retract);\r\nconst easeOut=x=>1-(1-x)*(1-x);\r\nexport function trailReach(age,reduced=false,K=TRAIL){\r\n if(age<0||age>=trailLife(reduced,K))return 0;\r\n if(reduced)return 1;\r\n if(age<K.grow)return easeOut(age/K.grow);\r\n if(age<K.grow+K.hold)return 1;\r\n return easeOut(1-(age-K.grow-K.hold)/K.retract);\r\n}\r\n\r\n// --- Pure shape helpers ------------------------------------------------------------------------\r\n// A crack is a list of branches; a branch is points [x,y,d] from where it attaches outward, d = arc distance\r\n// from the crack's root (a Y's fork branches start at the stem's length). Unit frame: root at 0,0, pointing +x.\r\nfunction jag(pts,amt,rnd){ // split each segment in three, nudging the two inner points sideways by up to amt x its length\r\n const out=[pts[0]];\r\n for(let i=1;i<pts.length;i++){\r\n  const [ax,ay]=pts[i-1],[bx,by]=pts[i],dx=bx-ax,dy=by-ay;\r\n  for(const f of [1/3,2/3]){const o=(rnd()*2-1)*amt;out.push([ax+dx*f-dy*o,ay+dy*f+dx*o]);}\r\n  out.push(pts[i]);\r\n }\r\n return out;\r\n}\r\nfunction withD(pts,d0){let d=d0;return pts.map((p,i)=>[p[0],p[1],d+=i?Math.hypot(p[0]-pts[i-1][0],p[1]-pts[i-1][1]):0]);}\r\nexport function crackShape(kind,rnd=Math.random,j=TRAIL.jag){\r\n const r=(a,b)=>a+(b-a)*rnd(),deg=Math.PI/180,ray=([x,y],a,l)=>[x+Math.cos(a)*l,y+Math.sin(a)*l],arm=(pts,d0)=>withD(jag(pts,j,rnd),d0);\r\n if(kind==='V'){const h=r(16,32)*deg;return [1,-1].map(s=>arm([[0,0],ray([0,0],s*h,r(.7,1))],0));}\r\n if(kind==='Y'){ // short stem, then a fork\r\n  const t=r(-8,8)*deg,stem=arm([[0,0],ray([0,0],t,r(.3,.45))],0),end=stem.at(-1),h=r(18,34)*deg;\r\n  return [stem,...[1,-1].map(s=>arm([end,ray(end,t+s*h,r(.45,.65))],end[2]))];\r\n }\r\n // Z: out, a sharp turn back across, out again\r\n const a=r(30,45)*deg,b=r(0,15)*deg,p1=ray([0,0],-a,r(.45,.55)),p2=ray(p1,Math.PI/2+b,r(.5,.6)),p3=ray(p2,-a,r(.45,.55));\r\n return [arm([[0,0],p1,p2,p3],0)];\r\n}\r\n// The part of a branch within arc distance r of the root, ending on an interpolated tip.\r\nexport function visible(b,r){\r\n const out=[];\r\n for(let i=0;i<b.length;i++){\r\n  const p=b[i];\r\n  if(p[2]<=r){out.push(p);continue;}\r\n  const q=b[i-1];\r\n  if(i&&r>q[2]){const f=(r-q[2])/(p[2]-q[2]);out.push([q[0]+(p[0]-q[0])*f,q[1]+(p[1]-q[1])*f,r]);}\r\n  break;\r\n }\r\n return out;\r\n}\r\n// Add a filled tapered outline of visible points p to the current path, pointed at its last point. Every\r\n// outline winds the same way, so one nonzero fill unions them all.\r\n// pad: added to every half-width, tip included (the dark rim round a crack).\r\nfunction ribbon(gc,p,h0,taper,len,pad=0){\r\n const n=p.length,o=[];\r\n for(let i=0;i<n;i++){\r\n  const a=p[i?i-1:0],b=p[i<n-1?i+1:i],dx=b[0]-a[0],dy=b[1]-a[1],l=Math.hypot(dx,dy)||1,h=pad+(i<n-1?h0*(1-taper*p[i][2]/len):0);\r\n  o.push(-dy/l*h,dx/l*h);\r\n }\r\n gc.moveTo(p[0][0]+o[0],p[0][1]+o[1]);\r\n for(let i=1;i<n;i++)gc.lineTo(p[i][0]+o[2*i],p[i][1]+o[2*i+1]);\r\n for(let i=n-1;i>=0;i--)gc.lineTo(p[i][0]-o[2*i],p[i][1]-o[2*i+1]);\r\n gc.closePath();\r\n}\r\n\r\n// Secret (Ian 6 Oct: \"10 taps in a row ... crystal explodes and shatters\"): pure reducer, testable without WebGL.\r\n// A tap = press..release within TAP_MS and under TAP_PX of finger travel; each press must come within TAP_MS of the previous press.\r\n// Events: {type:'press'} {type:'move',dist} {type:'release'}; now in ms. The 10th tap sets shattered.\r\nexport const SECRET={taps:10,gap:600,drift:12,claimFrom:3};\r\nexport const newSecret=()=>({taps:[],lastPress:-1e9,pressAt:0,down:false,moved:0,shattered:false});\r\nexport function iceSecret(state,ev,now){\r\n const S=state||newSecret();\r\n if(S.shattered)return S;\r\n if(ev.type==='press'){\r\n  if(now-S.lastPress>SECRET.gap)S.taps=[];\r\n  S.lastPress=S.pressAt=now;S.down=true;S.moved=0;\r\n }else if(ev.type==='move'){\r\n  S.moved+=ev.dist??0;\r\n  if(S.moved>=SECRET.drift)S.taps=[]; // a drag; the release below sees moved and stays out of the chain\r\n }else if(ev.type==='release'){\r\n  const tap=S.down&&S.moved<SECRET.drift&&now-S.pressAt<=SECRET.gap;\r\n  S.down=false;\r\n  if(!tap)S.taps=[];\r\n  else{S.taps.push(now);S.lastRelease=now;if(S.taps.length>=SECRET.taps)S.shattered=true;}\r\n }\r\n return S;\r\n}\r\n// Taps 1-2 stay eligible for the stitched-outline double-tap open; a press that would be the 3rd+ tap is claimed.\r\nexport const iceClaims=S=>!!S&&(S.shattered||S.taps.length+(S.down&&S.moved<SECRET.drift?1:0)>=SECRET.claimFrom);\r\n\r\n// One instance per board layer: the 3D board's (ice itself) and the flat board's 2D trace (ice.trace2d, R7) each get their own.\r\nexport function iceEffect(){\r\nlet S=null; // per-instance state\r\nlet tapState=null; // pure reducer state for secret tracking\r\nconst unkeep=now=>{for(const c of S.live)if(c.keep){c.keep=false;c.t0=now-BIG.grow-BIG.hold;}S.dirty=true;}; // chain broke: the kept cracks heal on the usual shrink\r\n\r\nfunction frame(i,k){ // cached Path2D of big pool pattern i at growth level k (lazily computed)\r\n const f=S.field;\r\n const p=S.pool[i]??={data:generateFracture({mode:'radial',width:f,height:f,seed:i+1,impact:{x:f/2,y:f/2},impactHole:0,...BIG.opts}),paths:[]};\r\n return p.paths[k]??=new Path2D(computeFrame(k/LEVELS,p.data,FRAME_OPTS).cracks.corePath);\r\n}\r\nconst rand=([a,b])=>a+(b-a)*Math.random();\r\n// One trail crack rooted at x,y (paint px) for a finger heading dx,dy (unit), k = paint px per screen px;\r\n// its hairline runs back to the previous root p.x,p.y. Geometry is fixed here, once; frames only clip it.\r\nfunction spawnTrail(p,x,y,dx,dy,k){\r\n const side=Math.random()<.5?1:-1,w=rand(TRAIL.sweep)*Math.PI/180,m=Math.random()<.5?1:-1,size=rand(TRAIL.size)*k;\r\n const ux=side*-dy*Math.cos(w)-dx*Math.sin(w),uy=side*dx*Math.cos(w)-dy*Math.sin(w); // sideways, leaning back\r\n const shape=crackShape('VYZ'[Math.floor(Math.random()*3)]).map(b=>b.map(([px,py,d])=>[x+size*(ux*px-uy*m*py),y+size*(uy*px+ux*m*py),d*size]));\r\n const hair=withD(jag([[x,y],[p.x,p.y]],TRAIL.jag,Math.random),0);\r\n if(S.trail.length>=TRAIL.max)S.trail.shift();\r\n S.trail.push({t0:performance.now(),shape,hair,len:Math.max(hair.at(-1)[2],...shape.map(b=>b.at(-1)[2])),reach:Math.max(...shape.map(b=>b.at(-1)[2]))});\r\n p.x=x;p.y=y;S.dirty=true;\r\n}\r\n// Shatter (Ian: \"explode and shatter\"): the crystal face becomes ~48 real 3D prisms (jittered-grid triangles, same material so\r\n// they carry the ice texture and the cracks), bursting toward the camera with spin + gravity, plus a flash and sparkle dust.\r\n// The board meshes hide, leaving a dark backplate, until heal(). Units: local board units, fw = face width.\r\nconst SHARD_MS=1700,NX=4,NY=6;\r\nfunction shatter(now){\r\n redraw(now);const {THREE,mesh,face:{w:fw,h:fh}}=S,orig=mesh.isGroup?[...mesh.children]:[mesh];\r\n const root=mesh.isGroup?mesh:mesh.parent,box=new THREE.Box3();\r\n for(const o of orig)if(o.geometry){o.geometry.computeBoundingBox();box.union(o.geometry.boundingBox);}\r\n const zf=box.max.z,t=fw*.03,pt=[];\r\n for(let j=0;j<=NY;j++){pt[j]=[];for(let i=0;i<=NX;i++){const e=i%NX&&j%NY; // jitter interior vertices only so the outline stays square\r\n  pt[j][i]=[(i/NX-.5)*fw+(e?(Math.random()-.5)*.7*fw/NX:0),(.5-j/NY)*fh+(e?(Math.random()-.5)*.7*fh/NY:0)];}}\r\n const mat=orig[0].material.clone();mat.onBeforeCompile=orig[0].material.onBeforeCompile;mat.customProgramCacheKey=orig[0].material.customProgramCacheKey;mat.transparent=true;\r\n const shards=[],cx=S.hit?(S.hit[0]-.5)*fw:0,cy=S.hit?(.5-S.hit[1])*fh:0;\r\n for(let j=0;j<NY;j++)for(let i=0;i<NX;i++){\r\n  const a=pt[j][i],b=pt[j][i+1],c=pt[j+1][i+1],d=pt[j+1][i],flip=(i+j)&1;\r\n  for(const T of flip?[[a,b,d],[b,c,d]]:[[a,b,c],[a,c,d]]){const tri=T.slice().reverse(); // the grid is clockwise, faces need CCW\r\n   const mx=(tri[0][0]+tri[1][0]+tri[2][0])/3,my=(tri[0][1]+tri[1][1]+tri[2][1])/3,pos=[],uv=[];\r\n   const V=(p,z)=>{pos.push(p[0]-mx,p[1]-my,z);uv.push(p[0]/fw+.5,p[1]/fh+.5);};\r\n   tri.forEach(p=>V(p,t/2));tri.forEach((p,k)=>V(tri[2-k],-t/2)); // front CCW, back reversed\r\n   for(let k=0;k<3;k++){const p=tri[k],q=tri[(k+1)%3];V(p,t/2);V(p,-t/2);V(q,-t/2);V(p,t/2);V(q,-t/2);V(q,t/2);}\r\n   const geo=new THREE.BufferGeometry();geo.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));geo.setAttribute('uv',new THREE.Float32BufferAttribute(uv,2));geo.computeVertexNormals();\r\n   const m=new THREE.Mesh(geo,mat);m.position.set(mx,my,zf-t/2);m.frustumCulled=false;root.add(m);\r\n   const dx=mx-cx,dy=my-cy,dl=Math.hypot(dx,dy)||1,k=fw*(.4+1.1*Math.random())/(1+dl/fw);\r\n   shards.push({m,vx:dx/dl*k,vy:dy/dl*k+fh*.3*Math.random(),vz:fw*(.15+.7*Math.random()),sx:(Math.random()-.5)*14,sy:(Math.random()-.5)*14,sz:(Math.random()-.5)*10});\r\n  }}\r\n const back=new THREE.Mesh(new THREE.PlaneGeometry(fw*1.01,fh*1.01),new THREE.MeshBasicMaterial({color:0x04080c}));back.position.z=zf-t-fw*.002;root.add(back);\r\n const flash=new THREE.Mesh(new THREE.PlaneGeometry(Math.max(fw,fh)*4,Math.max(fw,fh)*4),new THREE.MeshBasicMaterial({color:0xcff8ff,transparent:true,blending:THREE.AdditiveBlending,depthTest:false,depthWrite:false}));flash.position.z=zf+fw*.6;flash.renderOrder=20;root.add(flash);\r\n const N=160,dp=new Float32Array(N*3),dv=[],dg=new THREE.BufferGeometry();\r\n for(let n=0;n<N;n++){dp[n*3]=cx+(Math.random()-.5)*fw*.3;dp[n*3+1]=cy+(Math.random()-.5)*fh*.2;dp[n*3+2]=zf;const th=Math.random()*6.283,sp=fw*(.4+2.2*Math.random());dv.push([Math.cos(th)*sp,Math.sin(th)*sp,fw*(.3+1.6*Math.random())]);}\r\n dg.setAttribute('position',new THREE.BufferAttribute(dp,3));\r\n const dust=new THREE.Points(dg,new THREE.PointsMaterial({color:0xd6fbff,size:4,sizeAttenuation:false,transparent:true,blending:THREE.AdditiveBlending,depthTest:false,depthWrite:false}));dust.frustumCulled=false;dust.renderOrder=19;root.add(dust);\r\n for(const o of orig)o.visible=false;\r\n S.shat={t0:now,orig,shards,mat,back,flash,dust,dp,dv,N,fh,done:false};\r\n}\r\nfunction unshatter(){\r\n const z=S.shat;if(!z)return;\r\n for(const s of z.shards){s.m.removeFromParent();s.m.geometry.dispose();}\r\n for(const o of [z.back,z.flash,z.dust]){o.removeFromParent();o.geometry.dispose();o.material.dispose();}\r\n z.mat.dispose();for(const o of z.orig)o.visible=true;S.shat=null;\r\n}\r\nfunction stepShatter(_,now){\r\n const z=S.shat,dt=Math.min(.1,(now-(z.last??now))/1000),age=now-z.t0,fade=Math.max(0,1-Math.max(0,age-SHARD_MS*.55)/(SHARD_MS*.45)),G=z.fh*2.4;\r\n for(const s of z.shards){const m=s.m;s.vy-=G*dt;m.position.x+=s.vx*dt;m.position.y+=s.vy*dt;m.position.z+=s.vz*dt;m.rotation.x+=s.sx*dt;m.rotation.y+=s.sy*dt;m.rotation.z+=s.sz*dt;}\r\n z.last=now;z.mat.opacity=fade;\r\n z.flash.material.opacity=Math.max(0,.75*(1-age/380));\r\n for(let n=0;n<z.N;n++){const v=z.dv[n];v[1]-=G*.4*dt;for(let k=0;k<3;k++)z.dp[n*3+k]+=v[k]*dt;}\r\n z.dust.geometry.attributes.position.needsUpdate=true;z.dust.material.opacity=fade;\r\n if(age>=SHARD_MS&&!z.done){z.done=true;for(const s of z.shards)s.m.visible=false;window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'ice'}}));}\r\n return !z.done;\r\n}\r\nfunction redraw(now){\r\n const gc=S.glow.ctx,f=S.field/2,h=.5*S.scale,pad=EDGE_PX*S.scale,core=S.tint||CORE,glintColor=S.tint?tintRgba(S.tint,.9):GLINT,glint=()=>{gc.shadowColor=glintColor;gc.shadowBlur=3*S.scale;},flat=()=>{gc.shadowBlur=0;};\r\n gc.clearRect(0,0,S.glow.canvas.width,S.glow.canvas.height);\r\n gc.save();gc.lineJoin='round';\r\n for(const c of S.live){ // dark rim, then the core\r\n  const age=c.keep?Math.min(now-c.t0,BIG.grow+BIG.hold-1):now-c.t0,k=Math.round(crackT(age,S.reduced)*LEVELS);\r\n  if(!k)continue;\r\n  gc.setTransform(1,0,0,1,0,0);gc.translate(c.x,c.y);gc.rotate(c.rot);gc.translate(-f,-f);\r\n  gc.globalAlpha=crackAlpha(age,S.reduced);const p=frame(c.i,k);\r\n  flat();gc.strokeStyle=EDGE;gc.lineWidth=(BIG_EDGE+2*EDGE_PX)*S.scale;gc.stroke(p);\r\n  glint();gc.fillStyle=gc.strokeStyle=core;gc.fill(p);gc.lineWidth=BIG_EDGE*S.scale;gc.stroke(p);\r\n }\r\n if(S.trail.length){ // every live crack in one path per pass: the rims (padded), then the cores\r\n  gc.setTransform(1,0,0,1,0,0);gc.globalAlpha=1;\r\n  const cracks=pad=>{gc.beginPath();for(const c of S.trail){\r\n   const r=trailReach(now-c.t0,S.reduced)*c.len;\r\n   for(const b of c.shape){const v=visible(b,r);if(v.length>1)ribbon(gc,v,TRAIL.width*h,TRAIL.taper,c.reach,pad);}\r\n   const v=visible(c.hair,r);if(v.length>1)ribbon(gc,v,TRAIL.hair*h,0,1,pad);\r\n  }};\r\n  flat();cracks(pad);gc.fillStyle=EDGE;gc.fill();\r\n  glint();cracks(0);gc.fillStyle=core;gc.fill();\r\n }\r\n gc.restore();\r\n S.glow.texture.needsUpdate=true;\r\n}\r\n\r\nfunction init({THREE,mesh,face,paint,glow,toWorld,wake}){\r\n const w=paint.canvas.width;\r\n S={THREE,mesh,face,glow,toWorld,live:[],trail:[],pool:[],next:Math.floor(Math.random()*POOL),drag:new Map(),dirty:false,\r\n  field:Math.round(w*BIG.frac),scale:w/1024,sx:w,sy:paint.canvas.height,\r\n  reduced:matchMedia('(prefers-reduced-motion: reduce)').matches,tint:selectedTint,wake};\r\n tapState=newSecret();\r\n}\r\nfunction setTint(hex,selected=true){if(selected&&!validTint(hex))return;selectedTint=selected?hex:null;if(S){S.tint=selectedTint;S.dirty=true;S.wake?.();}}\r\nfunction press(id,u,v){\r\n if(S.shat){if(!S.shat.done)return;heal();} // fallback if the host never called healSecret(): the next touch brings the crystal back\r\n const now=performance.now(),x=u*S.sx,y=v*S.sy;\r\n tapState=iceSecret(tapState,{type:'press'},now);\r\n if(!tapState.taps.length)unkeep(now); // gap too long: the old web heals\r\n S.drag.set(id,{u,v,x,y,left:rand(TRAIL.spacing)});\r\n if(S.live.length>=MAX_BIG)S.live.shift();\r\n const c={x,y,i:S.next++%POOL,rot:Math.random()*Math.PI*2,t0:now};S.live.push(c);S.cur=c;S.hit=[u,v];\r\n S.dirty=true;\r\n}\r\n// Walk the finger's path since the last move, dropping a trail crack every `spacing` screen px so fast drags stay continuous.\r\nfunction move(id,u,v){\r\n const p=S.drag.get(id);if(!p||S.shat)return;\r\n const [tx,ty]=[(u-p.u)*S.sx,(v-p.v)*S.sy],d=Math.hypot(tx,ty);\r\n if(!d)return;\r\n tapState=iceSecret(tapState,{type:'move',dist:Math.hypot(...S.toWorld(u,v).map((q,i)=>q-S.toWorld(p.u,p.v)[i]))},performance.now()); // screen px\r\n if(!tapState.taps.length)unkeep(performance.now());\r\n const td=d,k=S.scale;\r\n let s=p.left;\r\n for(;s<=d;s+=rand(TRAIL.spacing))spawnTrail(p,(p.u+(u-p.u)*s/d)*S.sx,(p.v+(v-p.v)*s/d)*S.sy,tx/td,ty/td,td/d);\r\n p.left=s-d;p.u=u;p.v=v;\r\n}\r\nfunction release(id){\r\n if(!S.drag.delete(id)||S.shat)return;\r\n const now=performance.now();tapState=iceSecret(tapState,{type:'release'},now);\r\n if(!tapState.taps.length)unkeep(now);else if(S.cur)S.cur.keep=true; // a tap in the chain: its crack stays\r\n if(tapState.shattered){for(const c of S.live)c.keep=true;S.THREE?shatter(now):window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'ice'}}));S.wake?.();} // the flat 2D trace has no mesh: secret at once\r\n}\r\n// Asked by the glb wrapper just before release(): true for the 3rd tap on, and for the whole shatter.\r\nfunction claims(){return !!S&&iceClaims(tapState);}\r\nfunction step(dt,now){\r\nnow=Math.max(now,performance.now()); // rAF's timestamp is the frame start, before a crack born in a slow frame (negative age = dead on arrival)\r\n if(S.shat)return stepShatter(dt,now);\r\n if(tapState.taps.length&&!S.drag.size&&now-tapState.lastRelease>SECRET.gap){tapState.taps=[];unkeep(now);} // chain timed out: the web heals\r\n const n=S.live.length+S.trail.length;\r\n if(!n)return false;\r\n const life=trailLife(S.reduced);\r\n S.live=S.live.filter(c=>c.keep||crackAlpha(now-c.t0,S.reduced)>0);\r\n S.trail=S.trail.filter(c=>now-c.t0<life);\r\n // Reduced motion: frames only change when a crack is born or dies; otherwise every frame animates.\r\n if(!S.reduced||S.dirty||S.live.length+S.trail.length!==n)redraw(now);\r\n S.dirty=false;\r\n return S.live.length+S.trail.length>0;\r\n}\r\n// Whole crystal back, chain reset. Called through the board's heal() (portal.mjs heals on every hide/show/escape).\r\nfunction heal(){if(!S)return;unshatter();S.live=[];S.trail=[];S.cur=null;tapState=newSecret();S.dirty=true;redraw(performance.now());S.wake?.();}\r\nfunction dispose(){if(S)unshatter();S=null;tapState=null;}\r\nreturn {init,setTint,press,move,release,claims,step,healSecret:heal,dispose,tapState:()=>tapState};\r\n}\r\n\r\n// fragment (3D): the ice under the glow layer darkens by its alpha, so the dark rim (which emits nothing) shows as dark.\r\nexport const ice={id:'ice',asset:'/pod/worlds/boards/ice.glb',flip:false,background:'#0b1a26',guide:{color:'#eaf7ff',alpha:.22,width:5},pattern:{left:.035,top:.025,right:.965,bottom:.78},tintTarget:'trace',\r\n fragment:'diffuseColor.rgb*=1.0-0.85*glo.a;',...iceEffect(),trace2d:iceEffect};\r\n","modules/portal/portal-board-jelly.mjs":"// Jelly board effect for portal-board-glb.mjs — Ian's green lattice-brick GLB (the \"bog swamp\r\n// bones\" asset was never made) driven to wobble like jelly. Wobble is a ring buffer of up to 6\r\n// ripples pushed on press/drag/release; vertexDisplace turns each into a decaying radial heave\r\n// (z) plus outward slosh (xy), weighted toward the front face so the back of the slab stays put.\r\n// The camera looks straight at the face, so z alone barely reads: the analytic height gradient of\r\n// dent + ripples also tilts vNormal, letting the lights (and the normal map's TBN) show the wobble.\r\n// Distance is normalised by face width (uFaceSize.x) rather than raw model units so the k/decay/\r\n// spread constants below don't depend on the GLB's arbitrary export scale.\r\n// The raised lattice rods are BONES: vertices above uBoneZ sample the field once at their grid\r\n// cell's centre (damped by follow), so each cell of rod moves as one rigid vertebra riding the\r\n// jelly. The fragment hook shades them as solid struts (ivory core, jelly-tinted rim, rim light,\r\n// brighter knuckles on the tallest nodes) and cuts the trail: a finger SPLITS the jelly (dark gash,\r\n// bright lips, normals rolling into the cut) that closes behind it, all shading, no geometry.\r\n// AGPL-3.0-or-later.\r\n\r\nconst RK=12,RW=10.5,RDECAY=1.7,RSPREAD=1.8,SLOSH=.5; // spatial freq, temporal freq (rad/s: ~2.5 wobbles in 1.5 s), time decay, spatial decay (all in \"face widths\"), lateral/heave ratio\r\nconst TAP_AMP=.065,DRAG_AMP=.03,RELEASE_AMP=.03; // ripple heave at the centre, in face widths\r\nconst DENT_AMT=2.6,DENT_RAD=1.4; // finger dent vs. DEFAULT_DISPLACE (ice): deeper, wider/softer falloff\r\nlet selectedTint=null,trail3d=null,trail2d=null;\r\nconst validTint=hex=>typeof hex==='string'&&/^#[0-9a-f]{6}$/i.test(hex);\r\nfunction tintRgb(hex,factor=1){const m=hex?.match(/^#([0-9a-f]{6})$/i);if(!m)return null;const n=parseInt(m[1],16);return `rgb(${[n>>16,(n>>8)&255,n&255].map(v=>Math.round(v*factor)).join(',')})`;}\r\nfunction writeLinearTint(color,hex){const n=parseInt((hex||'#ffffff').slice(1),16),linear=c=>{const s=c/255;return s<=.04045?s/12.92:((s+.055)/1.055)**2.4;};color.r=linear(n>>16);color.g=linear((n>>8)&255);color.b=linear(n&255);color.isColor=true;}\r\n// Ian: bones rigid + segmented, jelly wobbles (2026-09-22)\r\n// zFrac: bone threshold as a fraction of depth back->front (jelly face sits ~.89-.93, rods .94-1);\r\n// seg: segment length in face widths, with centre: a face uv (v down) the grid is centred on — the\r\n// middle rod crossing; this seg puts the straight rods (x 0/±.25, rows .162 apart) mid-cell, so\r\n// no rod is split lengthwise between two cells; follow: bones' share of the jelly motion;\r\n// ridgeAmp/ridgeFreq: per-segment sine ridge in the normal (0 = off: it banded the rods into\r\n// broken-looking beads). Shading (fragment, by undisplaced depth fraction): mask ramps over\r\n// mask[0]..mask[1], core over core[0]..core[1] (rod crest), knuckle over knuckle[0..1] (the tallest\r\n// nodes, mostly where rods cross); tint: how far the texture is pulled to the bone colours.\r\nconst BONE={zFrac:.94,seg:.0546,centre:[.5,.444],follow:.55,ridgeAmp:0,ridgeFreq:1,\r\n mask:[.925,.945],core:[.935,.965],knuckle:[.965,.985],tint:.7,\r\n coreRGB:[.95,.85,.6],rimRGB:[.24,.22,.07],rough:.45,rimLight:.25,rimLightRGB:[.55,.8,.35],knuckleGlow:.1};\r\n// Ian: the trail SPLITS the jelly and it re-heals behind the finger (2026-09-23). Face widths / s.\r\n// width: gash width at the finger; close: s until the lips meet (width -> 0, so the gash tapers\r\n// back along the trail); ease: closing curve exponent; life: s until seam + bulge are gone (points\r\n// evicted, well inside the 7 s rule); step: min spacing between trail points.\r\n// Ian 2026-09-26: \"lets make the trail shorter for the jelly\" — only the recent part of the stroke is kept (cap\r\n// points, oldest evicted: cap x step = .96 face widths) and the slit closes within close, so it's a short gash that\r\n// follows the finger. This replaces 2026-09-24's longer, slower slit.\r\n// Lips: a ridge of half-width lip on each edge (normal tilt lipTilt, glossy, lipGlow sheen) plus a\r\n// crisp edgeGlow line on the cut edge. Inside: wallRGB at the walls to deepRGB at the centre (mixed\r\n// by interior), matte (interiorRough), walls tilting wallTilt into the cut; the wall facing the key\r\n// light glows litRGB (litWall) and its edge line is brighter, the far wall stays dark.\r\n// Healing: the two lip ridges merge into one bulge (bulge x a lip, bulgeW wide, its sheen fading\r\n// with it) that wobbles at wobbleHz, dying at wobbleDecay /s, over a dark seam (half-width seam,\r\n// seamDark); all of it fades out between close and life.\r\nexport const TRAIL={\r\n // R7 (Android): cap is the GLSL uTrail array size and the fragment loop bound. At 160 it made Jelly's fragment shader\r\n // ~206 uniform vectors (the GLES 3.0 floor is 224; every other board is under ~50), dynamically indexed in a 160-pass\r\n // loop, and picking Jelly took down the phone's GPU process and with it WebGL for every board. Keep it at 32 or under\r\n // (tests/portal-board-jelly.test.mjs); uTrailBounds still skips the loop off the gash.\r\n cap:32,\r\n // R7: wider and brighter-lipped than 24 Sept so the short gash reads on a phone.\r\n step:.03,width:.055,close:.8,ease:1,life:2.3,\r\n lip:.0035,lipTilt:1,lipGlow:.45,lipRGB:[.8,1,.65],edgeGlow:1.2,edgeRGB:[.85,1,.75],\r\n wallTilt:.7,wallRGB:[.05,.15,.03],deepRGB:[.004,.025,.004],interior:.92,interiorRough:.85,litWall:.5,litRGB:[.25,.55,.12],\r\n light:[-.79,.62], // key light (portal-board-glb's sun) as a face-xy direction, y up\r\n bulge:1.2,bulgeW:.01,wobbleHz:4.5,wobbleDecay:1.6,seam:.006,seamDark:.6,\r\n};\r\nconst RIPPLE_MAX_AGE=2,DRAG_STEP_PX=30;\r\nconst F=x=>x.toFixed(4); // GLSL float literal\r\nconst V3=a=>`vec3(${a.map(F).join(',')})`;\r\n\r\n// Pure mirror of the shader's quantisation: centre of the seg-sized grid cell (corner at ox,oy)\r\n// holding (x,y).\r\nexport const segmentCentre=(x,y,seg,ox=0,oy=0)=>[(Math.floor((x-ox)/seg)+.5)*seg+ox,(Math.floor((y-oy)/seg)+.5)*seg+oy];\r\n// Pure mirror of the shader's gash: full width (face widths) at `age` s behind the finger.\r\nexport const gashWidth=(age,T=TRAIL)=>T.width*Math.max(0,1-Math.max(0,age)/T.close)**T.ease;\r\n// Trail points {u,v,t,prev}: prev is the same stroke's previous point (null = stroke start).\r\n// No-THREE helpers (edit pts in place): append, evicting the oldest past cap; drop points older\r\n// than life at time now.\r\nexport function trailPush(pts,p,cap=TRAIL.cap){pts.push(p);if(pts.length>cap)pts.splice(0,pts.length-cap);return pts;}\r\nexport function trailExpire(pts,now,life=TRAIL.life){let k=0;while(k<pts.length&&now-pts[k].t>life)k++;if(k)pts.splice(0,k);return pts;}\r\n// Pure: pack into out[i].set(u,v,birth,back) where back = how many slots behind its predecessor\r\n// sits (0: none, or evicted), so interleaved strokes from two fingers still join up.\r\nexport function trailPack(pts,out){\r\n const at=new Map(pts.map((p,i)=>[p,i]));\r\n pts.forEach((p,i)=>out[i].set(p.u,p.v,p.t,at.has(p.prev)?i-at.get(p.prev):0));\r\n return pts.length;\r\n}\r\n// Pure: axis-aligned face-uv box around the whole trail, padded by the widest the gash ever gets\r\n// (half the max width, plus a hair of slack) — marginV divided by the board's aspect since the\r\n// shader's distance test scales v by it (a non-square face reaches further in raw v than in u for\r\n// the same on-screen width). Lets the fragment shader reject a pixel nowhere near the trail with\r\n// one cheap test instead of walking the (now much bigger) point array per pixel. null when there's\r\n// no trail to bound.\r\nexport function trailBounds(pts,marginU=TRAIL.width/2+.01,marginV=marginU){\r\n if(!pts.length)return null;\r\n let minU=Infinity,minV=Infinity,maxU=-Infinity,maxV=-Infinity;\r\n for(const{u,v}of pts){if(u<minU)minU=u;if(u>maxU)maxU=u;if(v<minV)minV=v;if(v>maxV)maxV=v;}\r\n return[minU-marginU,minV-marginV,maxU+marginU,maxV+marginV];\r\n}\r\n\r\n// Dent + ripple displacement (xy slosh, z heave) at face point p, plus its height gradient.\r\n// Rides in vertexDecls: a GLSL function can't be declared inside main(), where vertexDisplace goes.\r\nconst JELLY_FIELD=`\r\nvec3 jellyField(vec2 p,out vec2 grad){\r\nvec3 d=vec3(0.0);grad=vec2(0.0);\r\nfor(int di=0;di<8;di++){if(di>=uTouchCount)break;vec4 t=uTouch[di];vec2 dv=p-(uFaceMin+vec2(t.x,1.0-t.y)*uFaceSize);float dd=length(dv),rad=uDentRadius*${F(DENT_RAD)};if(dd<rad){float s=1.0-dd/rad,f=s*s*(3.0-2.0*s),h=uDent*${F(DENT_AMT)}*t.z;d.z-=h*f*f;if(dd>1e-4)grad+=dv/dd*h*12.0*f*s*(1.0-s)/rad;}}\r\nfor(int ri=0;ri<6;ri++){vec4 r=uRipple[ri];if(r.w<=0.0)continue;float age=uTime-r.z;if(age<0.0||age>${F(RIPPLE_MAX_AGE)})continue;vec2 dv=p-(uFaceMin+vec2(r.x,1.0-r.y)*uFaceSize);float dist=length(dv),dn=dist/uFaceSize.x,env=r.w*exp(-age*${F(RDECAY)})*exp(-dn*${F(RSPREAD)}),phase=dn*${F(RK)}-age*${F(RW)},sn=sin(phase),cs=cos(phase);d.z+=env*sn;if(dist>1e-4){vec2 dir=dv/dist;d.xy+=dir*env*${F(SLOSH)}*cs*smoothstep(0.0,0.1,dn);grad+=dir*env*(${F(RK)}*cs-${F(RSPREAD)}*sn)/uFaceSize.x;}}\r\nreturn d;\r\n}\r\n`;\r\n\r\n// Bones: the whole cell shares one sample and one weight (front weight is ~1 that high, so it's\r\n// dropped to keep the segment rigid). Ridge on both in-cell axes: a straight rod sits mid-cell, so\r\n// its across term is ~0 and the along term bands it; diagonals get bands from both.\r\nconst VERTEX_DISPLACE=`\r\nbool bone=position.z>uBoneZ;vec2 q=(floor((position.xy-uGridO)/uSeg)+0.5)*uSeg+uGridO,jg;\r\nvDepthF=0.5+0.5*position.z/uHalfDepth;\r\nfloat jw=bone?uBoneFollow:smoothstep(-uHalfDepth,uHalfDepth,position.z);\r\ntransformed+=jellyField(bone?q:position.xy,jg)*jw;jg*=jw;\r\nif(bone)jg+=uRidgeAmp*sin((position.xy-q)*uRidgeFreq);\r\n#ifndef FLAT_SHADED\r\nvNormal=normalize(normalMatrix*(objectNormal-vec3(jg,0.0)));\r\n#endif\r\n`;\r\n\r\n// Runs before lighting (portal-board-glb's effect.fragment). The board is never rotated and the\r\n// camera looks straight down -z, so view-space xy = face xy (y up) and normals tilt directly.\r\nconst T=TRAIL,B=BONE;\r\nconst FRAGMENT=`\r\nfloat boneM=smoothstep(${F(B.mask[0])},${F(B.mask[1])},vDepthF),boneC=smoothstep(${F(B.core[0])},${F(B.core[1])},vDepthF),knuckle=smoothstep(${F(B.knuckle[0])},${F(B.knuckle[1])},vDepthF);\r\n{float lum=dot(diffuseColor.rgb,vec3(0.3,0.59,0.11));\r\nvec3 bc=mix(${V3(B.rimRGB)},${V3(B.coreRGB)},boneC)*(0.7+0.6*lum)*(1.0+0.25*knuckle);\r\ndiffuseColor.rgb=mix(diffuseColor.rgb,bc,boneM*${F(B.tint)});roughnessFactor=mix(roughnessFactor,${F(B.rough)},boneM);\r\nfloat fr=1.0-clamp(normal.z,0.0,1.0);totalEmissiveRadiance+=boneM*(${V3(B.coreRGB)}*${F(B.knuckleGlow)}*knuckle+${V3(B.rimLightRGB)}*${F(B.rimLight)}*fr*fr*(1.0-boneC));}\r\nif(uTrailN>0&&vPlanar.x>=uTrailBounds.x&&vPlanar.x<=uTrailBounds.z&&vPlanar.y>=uTrailBounds.y&&vPlanar.y<=uTrailBounds.w){\r\n float asp=uFaceSize.y/uFaceSize.x,best=1e9,bd=1.0,ba=1e9;vec2 P=vec2(vPlanar.x,vPlanar.y*asp),dir=vec2(0.0);\r\n for(int i=0;i<${T.cap};i++){if(i>=uTrailN)break;vec4 b=uTrail[i],a=b.w>0.5?uTrail[i-int(b.w+0.5)]:b;\r\n  if(uTime-max(a.z,b.z)>${F(T.life)})continue;\r\n  vec2 A=vec2(a.x,a.y*asp),ab=vec2(b.x,b.y*asp)-A,ap=P-A;float h=clamp(dot(ap,ab)/max(dot(ab,ab),1e-10),0.0,1.0);\r\n  vec2 off=ap-ab*h;float d=length(off),age=max(uTime-mix(a.z,b.z,h),0.0),e=d-${F(T.width/2)}*pow(max(0.0,1.0-age/${F(T.close)}),${F(T.ease)});\r\n  if(e<best){best=e;bd=d;ba=age;dir=off/max(d,1e-6);}}\r\n float open=pow(max(0.0,1.0-ba/${F(T.close)}),${F(T.ease)}),hw=${F(T.width/2)}*open,keep=(1.0-boneM)*(1.0-smoothstep(${F(T.close)},${F(T.life)},ba)),aa=fwidth(P.x);\r\n float inside=(1.0-smoothstep(hw-aa,hw+aa,bd))*smoothstep(0.0,0.15,open)*keep,dep=1.0-bd*bd/max(hw*hw,1e-10);\r\n float since=max(ba-${F(T.close)},0.0),amp=open>0.0?mix(${F(T.bulge)},1.0,open):${F(T.bulge)}*exp(-since*${F(T.wobbleDecay)})*mix(1.0,cos(since*${F(2*Math.PI*T.wobbleHz)}),uWobble);\r\n float x=(bd-hw)/mix(${F(T.bulgeW)},${F(T.lip)},open),g=exp(-x*x),edge=(1.0-smoothstep(0.0,1.5*aa,abs(bd-hw)))*keep;\r\n vec2 tilt=(dir*(${F(T.lipTilt/.4289)}*amp*x*g)-dir*${F(T.wallTilt)}*inside*bd/max(hw,1e-5))*keep;\r\n normal=normalize(normal+vec3(tilt.x,-tilt.y,0.0));\r\n float lit=max(0.0,dot(vec2(-dir.x,dir.y),vec2(${F(T.light[0])},${F(T.light[1])}))); // wall/lip whose inward normal faces the light\r\n diffuseColor.rgb=mix(diffuseColor.rgb,mix(mix(${V3(T.wallRGB)},${V3(T.deepRGB)},dep),uTrailTint,uTrailTintMix),inside*${F(T.interior)});\r\n totalEmissiveRadiance+=mix(${V3(T.litRGB)},uTrailTint,uTrailTintMix)*${F(T.litWall)}*lit*(1.0-dep)*inside*open;\r\n roughnessFactor=mix(mix(roughnessFactor,0.12,g*keep*open),${F(T.interiorRough)},inside);\r\n totalEmissiveRadiance+=mix(${V3(T.lipRGB)},uTrailTint,uTrailTintMix)*${F(T.lipGlow)}*g*keep*(1.0-inside)*abs(amp)+mix(${V3(T.edgeRGB)},uTrailTint,uTrailTintMix)*${F(T.edgeGlow)}*edge*(0.3+0.7*lit)*open;\r\n diffuseColor.rgb=mix(diffuseColor.rgb,mix(${V3(T.deepRGB)},uTrailTint,uTrailTintMix),${F(T.seamDark)}*(1.0-smoothstep(0.0,${F(T.seam)}+aa,bd))*(1.0-open)*keep);\r\n}\r\n`;\r\n\r\n// ---- Vault secret (Ian 2026-10-06): hold one finger 7 s -> bubbles spread from the finger and cluster on the bones,\r\n// the block boils, a dark door rises out of the middle and parts the jelly. Fires myr5:portal-secret {board:'jelly'}.\r\n// jellySecret is the pure reducer (ms clock): state {id,x,y,t0,skip,up,upH,done}; held() is the effective hold in ms\r\n// (a lifted finger's value drains to 0 over `subside`, so every visual below just follows it).\r\nexport const HOLD={ms:7000,drift:24,subside:1000,front:6,boil:5.5,doorFrom:6.2,door:7,sink:.7}; // seconds: front reaches every bone, boil ramp starts, door rise, lands at ms; sink = door drop per s on lift\r\nexport const held=(s,now)=>s.done?HOLD.ms:s.id==null?0:s.up!=null?Math.max(0,s.upH*(1-(now-s.up)/HOLD.subside)):Math.max(0,now-s.t0+s.skip);\r\nexport function jellySecret(s,ev,now){\r\n s=s||{};const live=s.id!=null&&s.up==null&&!s.done,mine=live&&ev.id===s.id;\r\n switch(ev.type){\r\n  case'down':return s.done||live?s:{id:ev.id,x:ev.x,y:ev.y,t0:now,skip:0,up:null,done:false};\r\n  case'move':return mine&&Math.hypot(ev.x-s.x,ev.y-s.y)>HOLD.drift?{...s,up:now,upH:held(s,now),drift:true}:s;\r\n  case'up':return mine?{...s,up:now,upH:held(s,now)}:s;\r\n  case'skip':return live?{...s,skip:s.skip+ev.ms}:s;\r\n  case'tick':return live&&held(s,now)>=HOLD.ms?{...s,done:true}:s;\r\n }\r\n return s;\r\n}\r\nconst clamp01=x=>Math.max(0,Math.min(1,x)),ease=x=>x*x*(3-2*x);\r\nfunction rng(seed){return()=>(seed=(seed*1664525+1013904223)>>>0)/4294967296;}\r\nconst NB=300,NS=36; // bubbles (bone clusters + free + boil extras), steam puffs\r\nfunction makeFx(THREE,scene,mesh,face,toWorld,boneZ){\r\n const rnd=rng(7),g=new THREE.Group(),dummy=new THREE.Object3D(),bones=[];\r\n g.frustumCulled=false;\r\n const geo=mesh.isMesh?mesh.geometry:mesh.children?.[0]?.geometry,pos=geo?.attributes?.position;\r\n if(pos&&pos.count){const st=Math.max(1,Math.floor(pos.count/400));for(let i=0;i<pos.count;i+=st)if(pos.getZ(i)>boneZ)bones.push([(pos.getX(i)+face.w/2)/face.w,(face.h/2-pos.getY(i))/face.h]);}\r\n if(!bones.length)for(let i=0;i<150;i++)bones.push([.1+.8*rnd(),.08+.84*rnd()]); // no mesh (tests): scatter\r\n const bub=Array.from({length:NB},(_,i)=>{ // 0-159 ride the bones, 160-219 free, 220+ boil extras\r\n  const b=i<160?bones[Math.floor(rnd()*bones.length)]:[.06+.88*rnd(),.05+.9*rnd()],j=i<160?.012:0;\r\n  return{u:b[0]+(rnd()-.5)*j,v:b[1]+(rnd()-.5)*j,r:3+4*rnd(),P:.8+.5*rnd(),ph:rnd(),w:rnd()*6.28,extra:i>=220?rnd():-1};});\r\n const bm=new THREE.InstancedMesh(new THREE.SphereGeometry(1,10,8),new THREE.MeshStandardMaterial({color:0xcfffb8,emissive:0x2f7a22,roughness:.1,transparent:true,opacity:.5,depthWrite:false}),NB);\r\n const sc=document.createElement('canvas');sc.width=sc.height=64;const x=sc.getContext('2d'),gr=x.createRadialGradient(32,32,0,32,32,32);gr.addColorStop(0,'rgba(235,255,225,.9)');gr.addColorStop(1,'rgba(235,255,225,0)');x.fillStyle=gr;x.fillRect(0,0,64,64);\r\n const steamTex=new THREE.CanvasTexture(sc),sm=new THREE.InstancedMesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({map:steamTex,transparent:true,opacity:.3,depthWrite:false}),NS);\r\n const steam=Array.from({length:NS},()=>({u:rnd(),P:1.6+1.2*rnd(),ph:rnd(),s:30+30*rnd(),w:rnd()*6.28}));\r\n bm.frustumCulled=sm.frustumCulled=false;g.add(bm,sm);\r\n // door: dark gap, gold frame, slab, flywheel; two jelly flaps part to either side\r\n const door=new THREE.Group(),m=(c,o={})=>new THREE.MeshStandardMaterial({color:c,...o}),B=new THREE.BoxGeometry(1,1,1);\r\n const add=(geom,mat,px,py,pz,sx,sy,sz)=>{const o=new THREE.Mesh(geom,mat);o.position.set(px,py,pz);o.scale.set(sx,sy,sz);door.add(o);return o;};\r\n const flapMat=new THREE.MeshStandardMaterial({color:0x7ddc4a,emissive:0x1d4a12,roughness:.15,transparent:true,opacity:.62,depthWrite:false});\r\n const flaps=[-1,1].map(()=>{const f=new THREE.Mesh(new THREE.SphereGeometry(1,16,12),flapMat);g.add(f);return f;});\r\n const parts={gap:add(B,new THREE.MeshBasicMaterial({color:0x04060a,transparent:true,opacity:.92}),0,0,0,1.1,1.06,.1),\r\n  frame:add(B,m(0xc9a24a,{metalness:.8,roughness:.35,emissive:0x2a1f05}),0,0,1,1.07,1.04,.6),\r\n  slab:add(B,m(0x34303c,{metalness:.7,roughness:.42,emissive:0x120a1c}),0,0,2,1,1,1),\r\n  wheel:add(new THREE.TorusGeometry(1,.14,8,24),m(0xd8b45c,{metalness:.8,roughness:.3,emissive:0x2a1f05}),0,0,2,1,1,1)};\r\n g.add(door);door.visible=false;scene.add(g);\r\n const sizes=()=>{const[x0,y0]=toWorld(0,0),[x1,y1]=toWorld(1,1);return{fw:x1-x0,fh:y0-y1,cx:(x0+x1)/2,cy:(y0+y1)/2-.03*(y0-y1)};};\r\n let doorP=0,lastT=0;\r\n return{bones:bones.length,get doorP(){return doorP;},reset(){doorP=0;},\r\n  update(h,calm,time){ // h seconds held, calm 0..1 post-done fade, time seconds (any clock)\r\n   const{fw,fh,cx,cy}=sizes(),[fu,fv]=S.fin,boil=ease(clamp01(h-HOLD.boil)),dmax=Math.max(.6,...bub.map(b=>Math.hypot(b.u-fu,(b.v-fv)*face.h/face.w))),keep=1-calm,Z=3,k=fw/300;\r\n   bub.forEach((b,i)=>{\r\n    const[wx,wy]=toWorld(b.u,b.v),d=Math.hypot(b.u-fu,(b.v-fv)*face.h/face.w);\r\n    const delay=b.extra>=0?HOLD.boil+b.extra:d<.5?d*8:4+(d-.5)/Math.max(.1,dmax-.5)*(HOLD.front-4),c=h-delay,P=b.P*(1-.45*boil);\r\n    let ph=(c/P+b.ph)%1;if(ph<0)ph+=1;\r\n    const grow=ease(clamp01(c/.3))*(ph<.82?1:(1-ph)/.18)*keep,r=b.r*(1+.7*boil)*grow*k;\r\n    dummy.position.set(wx+Math.sin(time*3+b.w)*3*(1+boil),wy+ph*fw*(.09+.1*boil),Z+r);dummy.scale.setScalar(Math.max(r,1e-4));dummy.updateMatrix();bm.setMatrixAt(i,dummy.matrix);});\r\n   bm.instanceMatrix.needsUpdate=true;\r\n   steam.forEach((s,i)=>{const ph=(time/s.P+s.ph)%1,a=boil*keep*Math.sin(ph*Math.PI),[sx,sy]=toWorld(s.u,.9-ph*.7);\r\n    dummy.position.set(sx+Math.sin(time*.8+s.w)*10,sy,Z+14);dummy.scale.setScalar(Math.max(s.s*(.6+ph)*k*a,1e-4));dummy.updateMatrix();sm.setMatrixAt(i,dummy.matrix);});\r\n   sm.instanceMatrix.needsUpdate=true;\r\n   const tp=ease(clamp01((h-HOLD.doorFrom)/(HOLD.door-HOLD.doorFrom))),dt=Math.max(0,Math.min(.1,time-lastT));lastT=time;doorP=Math.max(tp,doorP-HOLD.sink*dt);const p=doorP,W=fw*.36,H=fh*.44,D=14*k;door.visible=p>.01;\r\n   door.position.set(cx,cy,-D+p*D*1.8);door.scale.setScalar(Math.max(p,1e-3));\r\n   parts.gap.scale.set(W*1.1,H*1.06,.1);parts.frame.scale.set(W*1.07,H*1.04,D*.6);parts.slab.scale.set(W,H,D);parts.slab.position.z=D*.3;parts.wheel.scale.setScalar(W*.2);parts.wheel.position.z=D*.9;\r\n   flaps.forEach((f,i)=>{f.visible=p>.01;f.position.set(cx+(i?1:-1)*(W*p/2+W*.1),cy,Z+4);f.scale.set(W*.14*p+1e-3,H*.55*p+1e-3,10*k);});\r\n  },\r\n  dispose(){scene.remove?.(g);g.traverse(o=>{o.geometry?.dispose();o.material?.dispose?.();});steamTex.dispose();bm.dispose();sm.dispose();}};\r\n}\r\n\r\nlet S=null; // per-instance state; a single portal board is ever active at once\r\n\r\nfunction computeHalfDepth(THREE,mesh){\r\n const box=new THREE.Box3(),parts=mesh.isMesh?[mesh]:mesh.children;\r\n for(const m of parts){m.geometry.computeBoundingBox();box.union(m.geometry.boundingBox);}\r\n return box.max.z;\r\n}\r\n// uTime only advances on rendered frames, so a press on a sleeping board would stamp a stale time\r\n// and the ripple would be born already expired. Stamp with the live clock (epoch learnt in step).\r\nconst clock=()=>S.epoch==null?S.uniforms.uTime.value:performance.now()/1000-S.epoch;\r\nfunction pushRipple(u,v,amp){\r\n if(S.reduced)return; // reduced motion: no ripples (the gash still cuts, without the wobble)\r\n S.rippleVecs[S.nextSlot].set(u,v,clock(),amp);\r\n S.nextSlot=(S.nextSlot+1)%6;\r\n}\r\nfunction pushTrail(id,u,v,join){\r\n const prev=join?S.heads.get(id):null;\r\n if(prev&&Math.hypot(u-prev.u,(v-prev.v)*S.aspect)<TRAIL.step)return;\r\n const p={u,v,t:clock(),prev:prev||null};trailPush(S.trail,p);S.heads.set(id,p);\r\n}\r\n\r\nasync function init({THREE,mesh,material,uniforms,scene,face,toWorld,wake}){\r\n material.roughness=.28;material.metalness=0;\r\n const rim=new THREE.DirectionalLight(0x9fe8ff,1.1);rim.position.set(.7,.6,.5);scene.add(rim); // cool highlight, upper-right-front\r\n uniforms.uRipple.value=Array.from({length:6},()=>new THREE.Vector4(0,0,0,0));\r\n uniforms.uTrail.value=Array.from({length:TRAIL.cap},()=>new THREE.Vector4(0,0,0,0));uniforms.uTrailN.value=0;\r\n uniforms.uTrailBounds.value=new THREE.Vector4(0,0,0,0);\r\n const hd=computeHalfDepth(THREE,mesh),seg=BONE.seg*face.w; // glb centres the mesh: z spans -hd..hd\r\n uniforms.uHalfDepth.value=hd;uniforms.uBoneZ.value=hd*(2*BONE.zFrac-1);uniforms.uSeg.value=seg;uniforms.uBoneFollow.value=BONE.follow;\r\n uniforms.uGridO.value=new THREE.Vector2((BONE.centre[0]-.5)*face.w-seg/2,(.5-BONE.centre[1])*face.h-seg/2); // face centred on 0\r\n uniforms.uRidgeAmp.value=BONE.ridgeAmp;uniforms.uRidgeFreq.value=2*Math.PI*BONE.ridgeFreq/seg;\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;uniforms.uWobble.value=reduced?0:1;\r\n uniforms.uTrailTint.value={r:1,g:1,b:1,isColor:true};writeLinearTint(uniforms.uTrailTint.value,selectedTint||'#ffffff');uniforms.uTrailTintMix.value=selectedTint?1:0;\r\n S={uniforms,toWorld,faceW:face.w,aspect:face.h/face.w,rippleVecs:uniforms.uRipple.value,nextSlot:0,lastSpawn:new Map(),\r\n    reduced,trail:[],heads:new Map(),epoch:null,wake,sec:{},fin:[.5,.5],fxLast:0,calmAt:0,\r\n    fx:THREE.InstancedMesh&&typeof document!=='undefined'?makeFx(THREE,scene,mesh,face,toWorld,uniforms.uBoneZ.value):{update(){},reset(){},dispose(){}}};trail3d=S;\r\n globalThis.myr5JellyFF=ms=>{S&&secret({type:'skip',ms});S?.wake();};myr5JellyFF.held=()=>S&&held(S.sec,performance.now())/1000;myr5JellyFF.door=()=>S?.fx.doorP; // debug: fast-forward the hold / read it\r\n}\r\nfunction press(id,u,v){\r\n const[x,y]=S.toWorld(u,v);if(S.sec.id==null||S.sec.up!=null){S.fin=[u,v];}secret({type:'down',id,x,y});\r\n S.lastSpawn.set(id,[u,v]);\r\n pushRipple(u,v,S.faceW*TAP_AMP);\r\n pushTrail(id,u,v,false);\r\n}\r\nfunction move(id,u,v,pu,pv){\r\n {const[x,y]=S.toWorld(u,v);secret({type:'move',id,x,y});}\r\n const last=S.lastSpawn.get(id)||[pu,pv],[lx,ly]=S.toWorld(last[0],last[1]),[cx,cy]=S.toWorld(u,v);\r\n if(Math.hypot(cx-lx,cy-ly)>=DRAG_STEP_PX){pushRipple(u,v,S.faceW*DRAG_AMP);S.lastSpawn.set(id,[u,v]);}\r\n pushTrail(id,u,v,true);\r\n}\r\nfunction release(id,u,v){secret({type:'up',id});pushRipple(u,v,S.faceW*RELEASE_AMP);S.lastSpawn.delete(id);pushTrail(id,u,v,true);S.heads.delete(id);}\r\nconst secret=ev=>{S.sec=jellySecret(S.sec,ev,performance.now());};\r\n// claims(id): the finger that is (still) holding the secret; a drifted-off finger is just a stroke again.\r\nconst claims=id=>!!S&&S.sec.id===id&&S.sec.up==null;\r\nfunction step(dt,frameNow){\r\n let active=false;\r\n const ut=S.uniforms.uTime.value;S.epoch=frameNow/1000-ut;\r\n {const was=S.sec.done,now=frameNow;S.sec=jellySecret(S.sec,{type:'tick'},now);\r\n  if(S.sec.done&&!was){S.calmAt=now;if(typeof window!=='undefined')window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'jelly'}}));}\r\n  const h=held(S.sec,now)/1000,calm=S.sec.done?clamp01((now-S.calmAt)/1500):0;\r\n  if(h>0){ // finger bobble, then a harder boil: ripples around the finger / anywhere\r\n   const gap=h>HOLD.boil?140:450;if(now-S.fxLast>gap){S.fxLast=now;const b=h>HOLD.boil,a=Math.random();pushRipple(b?.1+.8*a:S.fin[0]+(a-.5)*.2,b?.1+.8*Math.random():S.fin[1]+(Math.random()-.5)*.2,S.faceW*(b?.03:.015)*(1-calm));}}\r\n  S.fx.update(h,calm,now/1000);active=h>0&&calm<1||!S.sec.done&&S.fx.doorP>.01;}\r\n for(const r of S.rippleVecs)if(r.w>0&&(ut-r.z)<RIPPLE_MAX_AGE)active=true;\r\n trailExpire(S.trail,ut);\r\n S.uniforms.uTrailN.value=trailPack(S.trail,S.uniforms.uTrail.value);\r\n const margin=TRAIL.width/2+.01,b=trailBounds(S.trail,margin,margin/S.aspect);\r\n S.uniforms.uTrailBounds.value.set(...(b||[0,0,0,0]));\r\n return active||S.trail.length>0;\r\n}\r\nfunction setJellyTint(hex,selected=true){\r\n if(selected&&!validTint(hex))return;\r\n selectedTint=selected?hex:null;\r\n if(trail3d){writeLinearTint(trail3d.uniforms.uTrailTint.value,selectedTint||'#ffffff');trail3d.uniforms.uTrailTintMix.value=selectedTint?1:0;trail3d.wake?.();}\r\n if(trail2d){trail2d.tint=selectedTint;trail2d.wake?.();}\r\n}\r\n// Back to a fresh block after the secret (portal.mjs / the glb host call this through heal): door, flaps and bubbles gone, hold forgotten.\r\nfunction healSecret(){if(!S)return;S.sec={};S.calmAt=0;S.fx.reset();S.fx.update(0,0,performance.now()/1000);S.wake?.();}\r\nfunction dispose(){S?.fx.dispose();delete globalThis.myr5JellyFF;if(S===trail3d)trail3d=null;S=null;}\r\n\r\n// R7: the gash in 2D, for the flat board: the same short trail (trailPush/trailExpire/gashWidth) as a translucent lime\r\n// slit on `paint` (drawn opaque, deeper down its middle, then faded as a whole by SLIT.alpha, so the round joins never\r\n// stack into beads) inside a light rim on `glow`, tapering to a thin healing seam behind the finger. Own state, no THREE.\r\nconst SLIT={lime:'rgb(150,240,70)',deep:'rgb(45,125,20)',deepFrac:.4,alpha:.62,rim:.008,seam:'rgb(195,255,140)',seamAlpha:.55};\r\nexport function jellyGash(){\r\n let S=null;\r\n const rgb=(c,k)=>`rgb(${c.map(v=>v*k*255|0)})`;\r\n const push=(id,u,v,join)=>{const prev=join?S.heads.get(id):null;if(prev&&Math.hypot(u-prev.u,(v-prev.v)*S.aspect)<TRAIL.step)return;const p={u,v,t:performance.now()/1000,prev:prev||null};trailPush(S.trail,p);S.heads.set(id,p);};\r\n return {\r\n  init({paint,glow,wake}){S={paint,glow,W:paint.canvas.width,H:paint.canvas.height,aspect:paint.canvas.height/paint.canvas.width,trail:[],heads:new Map(),wake,tint:selectedTint};trail2d=S;},\r\n  setTint(hex,selected=true){setJellyTint(hex,selected);},\r\n  press(id,u,v){push(id,u,v,false);},\r\n  move(id,u,v){push(id,u,v,true);},\r\n  release(id,u,v){push(id,u,v,true);S.heads.delete(id);},\r\n  step(dt,now){\r\n   const t=now/1000,{W,H}=S,pc=S.paint.ctx,gc=S.glow.ctx,live=new Set(trailExpire(S.trail,t));\r\n   pc.clearRect(0,0,W,H);gc.clearRect(0,0,W,H);pc.lineCap=gc.lineCap='round';\r\n   const segs=[...live].filter(b=>b.prev&&live.has(b.prev)).map(b=>{const a=b.prev,age=t-(a.t+b.t)/2;return {age,w:gashWidth(age)*W,x0:a.u*W,y0:a.v*H,x1:b.u*W,y1:b.v*H};});\r\n   const line=(c,s,width,{x0,y0,x1,y1})=>{c.strokeStyle=s;c.lineWidth=width;c.beginPath();c.moveTo(x0,y0);c.lineTo(x1,y1);c.stroke();};\r\n   const open=segs.filter(s=>s.w>.5);\r\n   const lime=S.tint?tintRgb(S.tint):SLIT.lime,deep=S.tint?tintRgb(S.tint,.42):SLIT.deep,seam=S.tint?tintRgb(S.tint,.82):SLIT.seam,edge=S.tint?tintRgb(S.tint,.92):rgb(TRAIL.edgeRGB,1);\r\n   for(const s of open)line(pc,lime,s.w,s);for(const s of open)line(pc,deep,s.w*SLIT.deepFrac,s); // the slit, opaque\r\n   pc.globalCompositeOperation='destination-in';pc.fillStyle=`rgba(0,0,0,${SLIT.alpha})`;pc.fillRect(0,0,W,H);pc.globalCompositeOperation='source-over'; // then translucent\r\n   for(const s of segs)if(s.w<=.5){pc.globalAlpha=SLIT.seamAlpha*(1-Math.min(1,(s.age-TRAIL.close)/(TRAIL.life-TRAIL.close)));line(pc,seam,W*TRAIL.seam,s);} // the healing tail\r\n   pc.globalAlpha=1;\r\n   for(const s of open)line(gc,edge,s.w+2*W*SLIT.rim,s); // the rim, the slit cut out of it\r\n   gc.globalCompositeOperation='destination-out';for(const s of open)line(gc,'#000',s.w,s);gc.globalCompositeOperation='source-over';\r\n   return S.trail.length>0;\r\n  },\r\n  dispose(){if(S===trail2d)trail2d=null;S=null;},\r\n };\r\n}\r\n\r\nexport const jelly={\r\n id:'jelly',asset:'/pod/worlds/boards/jelly.glb',flip:false,background:'#0d160a',ink:false, // the gash is the trace trail: no portal ink line over it\r\n guide:{color:'#c8ffb0',alpha:.12,width:4},\r\n pattern:{left:.07,top:.055,right:.93,bottom:.87}, // measured in-browser against the carved oval's extremes\r\n uniforms:{uRipple:{value:[]},uHalfDepth:{value:1},uBoneZ:{value:1},uSeg:{value:1},uGridO:{value:null},uBoneFollow:{value:1},uRidgeAmp:{value:0},uRidgeFreq:{value:0},uTrail:{value:[]},uTrailN:{value:0},uTrailBounds:{value:null},uWobble:{value:1},uTrailTint:{value:null},uTrailTintMix:{value:0}},\r\n uniformDecls:'varying float vDepthF;\\n',\r\n vertexDecls:'uniform vec4 uRipple[6];\\nuniform float uHalfDepth;\\nuniform float uBoneZ;\\nuniform float uSeg;\\nuniform vec2 uGridO;\\nuniform float uBoneFollow;\\nuniform float uRidgeAmp;\\nuniform float uRidgeFreq;\\n'+JELLY_FIELD,\r\n fragmentDecls:`uniform vec4 uTrail[${TRAIL.cap}];\\nuniform int uTrailN;\\nuniform vec4 uTrailBounds;\\nuniform float uWobble;\\nuniform vec3 uTrailTint;\\nuniform float uTrailTintMix;\\n`, // fragment only (portal-board-glb.mjs)\r\n vertexDisplace:VERTEX_DISPLACE,\r\n fragment:FRAGMENT,\r\n init,press,move,release,claims,step,healSecret,dispose,setTint:setJellyTint,tintTarget:'trace',trace2d:jellyGash,\r\n};\r\n","modules/portal/portal-board-layout.mjs":"// One outer footprint for every grimoire: the whole board host (the area inside the portal's glass frame), portrait or\r\n// landscape. No aspect letterbox, no mechanical cap, no blank top. Art may deform to it; every board's faceRect,\r\n// patternRect, pointer mapping and hit geometry derive from this rect. AGPL-3.0-or-later.\r\nexport function boardLayout(w,h){return {left:0,top:0,width:Math.max(1,w),height:Math.max(1,h)};}\r\n\r\n// A GLB whose front face is fw x fh model units, laid on `layout`: independent x/y scale so the face fills it, and one\r\n// depth scale (the smaller, so relief never exceeds the face) so thickness, shadows and dents stay physical.\r\n// mean is the geometric mean of sx and sy: the round-trip scale for isotropic things measured in model units.\r\nexport function fillScale(fw,fh,layout){\r\n const sx=layout.width/fw,sy=layout.height/fh;\r\n return {sx,sy,sz:Math.min(sx,sy),mean:Math.sqrt(sx*sy)};\r\n}\r\n","modules/portal/portal-board-mache.mjs":"import * as THREE from 'three';\r\nimport {boardLayout} from './portal-board-layout.mjs';\r\n\r\nconst ASSETS='/pod/worlds/boards/mache/';\r\nconst clamp=(n,a=0,b=1)=>Math.max(a,Math.min(b,n));\r\nconst distance=(a,b)=>Math.hypot(a[0]-b[0],a[1]-b[1]);\r\nexport const MACHE_SECRET_MS=1800;\r\nexport const macheLayout=boardLayout; // the sheet stretches over the whole host like every grimoire; the relief's square motif stretches with it\r\n\r\n// Resample by distance, not event count: slow fingers must not outweigh missing limbs.\r\nfunction samples(strokes){\r\n const out=[];\r\n for(const stroke of strokes){\r\n  if(!Array.isArray(stroke))continue;\r\n  const pts=stroke.filter(p=>Array.isArray(p)&&p.length>=2&&p.every(Number.isFinite));\r\n  if(pts.length)out.push(pts[0]);\r\n  for(let i=1;i<pts.length;i++){const a=pts[i-1],b=pts[i],n=Math.min(180,Math.ceil(distance(a,b)/.012));for(let j=1;j<=n;j++)out.push([a[0]+(b[0]-a[0])*j/n,a[1]+(b[1]-a[1])*j/n]);if(out.length>6000)return [];}\r\n }\r\n return out;\r\n}\r\nexport function recognizeMacheFigure(strokes){\r\n const raw=samples(strokes);if(raw.length<30)return false;\r\n const xs=raw.map(p=>p[0]),ys=raw.map(p=>p[1]),x=Math.min(...xs),y=Math.min(...ys),w=Math.max(...xs)-x,h=Math.max(...ys)-y;\r\n if(h<.15||w<.08||w/h<.3||w/h>1.65)return false;\r\n const pts=raw.map(p=>[(p[0]-x)/w,(p[1]-y)/h]);\r\n const near=(p,t=.105)=>pts.some(q=>distance(p,q)<t);\r\n // Independently require a ring, spine, two arms and two diverging legs.\r\n // Head/shoulder proportions are searched to allow ordinary children's drawings.\r\n for(const cx of [.44,.5,.56])for(const cy of [.13,.17,.21])for(const rx of [.13,.19,.25]){\r\n  const ry=cy,neck=cy+ry,hip=.65;\r\n  const ring=Array.from({length:16},(_,i)=>[cx+rx*Math.cos(i*Math.PI/8),cy+ry*Math.sin(i*Math.PI/8)]);\r\n  if(ring.filter(p=>near(p,.075)).length<14)continue;\r\n  if(![.49,.54,.59].every(y=>near([cx,y],.055)))continue;\r\n  const lines=[[[cx,neck],[cx,hip]],[[cx,neck+.09],[.04,.46]],[[cx,neck+.09],[.96,.46]],[[cx,hip],[.21,.98]],[[cx,hip],[.79,.98]]];\r\n  const targets=lines.map(([a,b])=>Array.from({length:8},(_,i)=>[a[0]+(b[0]-a[0])*i/7,a[1]+(b[1]-a[1])*i/7]));\r\n  if(targets.some(line=>line.filter(p=>near(p,.12)).length<7))continue;\r\n  const template=[...ring,...targets.flat()];\r\n  if(pts.filter(p=>template.some(q=>distance(p,q)<.14)).length/pts.length<.82)continue;\r\n  // Filled ovals / scribbles are not heads: the ring's interior must stay mostly empty.\r\n  if(pts.filter(p=>Math.hypot((p[0]-cx)/rx,(p[1]-cy)/ry)<.48).length>pts.length*.06)continue;\r\n  return true;\r\n }\r\n return false;\r\n}\r\n\r\n// The relief is built in isotropic board units (width = 1, height = aspect): the rim keeps one width all round, the well and\r\n// its slopes are true to the camera, and normals are exact on a portrait or landscape host. d is the distance from the\r\n// board's centre in the square board's terms (aspect 1: max(|x|,|y|)), so the motif is the same square-framed relief.\r\nexport function buildMacheRelief(aspect=1){\r\n const sx=Math.min(64,Math.floor(Math.sqrt(4900/aspect))),sy=Math.max(2,Math.ceil(sx*aspect)); // ~ the same triangle budget at any aspect\r\n const geo=new THREE.PlaneGeometry(1,aspect,sx,sy),p=geo.attributes.position;\r\n for(let i=0;i<p.count;i++){\r\n  const x=p.getX(i),y=p.getY(i),d=.5-Math.min(.5-Math.abs(x),.5*aspect-Math.abs(y));\r\n  const rim=Math.exp(-(((d-.455)/.026)**2))*.037;\r\n  const inset=d<.35?-.013:0;\r\n  p.setZ(i,rim+inset+Math.sin(x*117+y*83)*.0018+Math.sin(y*181-x*63)*.0013);\r\n }\r\n geo.computeVertexNormals();return geo;\r\n}\r\nfunction path(c,pts){c.beginPath();pts.forEach(([x,y],i)=>c[i?'lineTo':'moveTo'](x,y));c.closePath();}\r\n// The warning plaques: a thin box (488 x 49 art px, 1 board unit = 768 art px both ways) whose uv window is the sheet's label\r\n// strip. The base art is painted at the board's own aspect (768 x 768*aspect) and squashed into the sheet's uv square, so the\r\n// strip is 49/(768*aspect) of v high and the box is a native 49/768 tall.\r\nfunction plaqueGeo(y,aspect){\r\n const geo=new THREE.BoxGeometry(488/768,49/768,.012),uv=geo.attributes.uv,H=768*aspect;\r\n for(let i=0;i<uv.count;i++)uv.setXY(i,(140+uv.getX(i)*488)/768,1-y+(uv.getY(i)*49-15)/H);\r\n return geo;\r\n}\r\nfunction load(name){return new Promise(resolve=>{const i=new Image();i.onload=()=>resolve(i);i.onerror=()=>resolve(null);i.src=ASSETS+name;});}\r\nfunction makeHead(paper){\r\n const root=new THREE.Group(),dark=new THREE.MeshStandardMaterial({color:0x110f0b,roughness:1});\r\n const part=(geo,mat,x,y,z,sx=1,sy=1,sz=1)=>{const m=new THREE.Mesh(geo,mat);m.position.set(x,y,z);m.scale.set(sx,sy,sz);root.add(m);return m;};\r\n part(new THREE.SphereGeometry(.17,24,16),paper,0,0,0,1,1.3,.85);\r\n part(new THREE.SphereGeometry(.1,16,10),paper,0,-.16,.05,1,.6,.85);\r\n for(const s of [-1,1])part(new THREE.SphereGeometry(.033,12,8),dark,s*.063,.035,.132,1,.65,.5);\r\n const nose=part(new THREE.ConeGeometry(.032,.1,6),paper,0,-.025,.17);nose.rotation.x=Math.PI/2;\r\n part(new THREE.SphereGeometry(.055,12,8),dark,0,-.12,.13,1,1.3,.3);\r\n part(new THREE.CylinderGeometry(.19,.2,.11,8),paper,0,.22,-.025);\r\n part(new THREE.BoxGeometry(.48,.025,.3),paper,0,.16,.01);\r\n return root;\r\n}\r\n\r\nexport async function createMacheBoard(host,{flat=false}={}){\r\n const art=await Promise.all(['school-drawing-1.webp','school-drawing-2.webp','school-drawing-3.webp','corgi-game-still.webp'].map(load));\r\n let canvas=document.createElement('canvas'),renderer=null,scene,camera,mesh,texture,head,flaps,paperMap,paperMat,plaques=[],relief=1;\r\n const sheet=document.createElement('canvas');sheet.width=sheet.height=768;const ctx=sheet.getContext('2d');\r\n const paint=document.createElement('canvas');paint.width=paint.height=768;const ink=paint.getContext('2d');let PH=768; // the ink layer has the board's on-screen aspect (width 768, height PH): strokes keep one thickness every way, drawSheet squashes it into the sheet's uv square\r\n const base=document.createElement('canvas');base.width=base.height=768;const bg=base.getContext('2d');\r\n const collage=[[.055,.15,.25,.37,-.07],[.695,.16,.25,.37,.055],[.64,.57,.29,.38,-.045],[.06,.59,.34,.27,.07]];\r\n // The paper, photos and labels are painted at the board's on-screen aspect (width 768, height BH) at native pixel sizes, then\r\n // drawSheet squashes the canvas into the sheet's uv square, which the full-host pane stretches back: nothing is deformed.\r\n // Photo boxes keep their normalised centres; each photo is drawn \"contain\" at its own aspect inside its box.\r\n function paintBase(aspect){\r\n  const BH=Math.max(1,Math.round(768*aspect));if(base.height!==BH)base.height=BH;\r\n  bg.clearRect(0,0,768,BH);bg.fillStyle='#9f9479';bg.fillRect(0,0,768,BH);\r\n  // Torn newsprint strips; deterministic fibers keep the surface stable between frames.\r\n  for(let i=0;i<220*aspect;i++){\r\n   const x=(i*173)%768,y=(i*97)%768*aspect;bg.save();bg.translate(x,y);bg.rotate(Math.sin(i*7)*.4);\r\n   bg.fillStyle=['#b4aa8d','#c5bc9e','#968d72','#cec3a5'][i%4];path(bg,[[0,0],[99,2],[94,31],[3,28]]);bg.fill();\r\n   bg.fillStyle='#56513e';bg.font='5px monospace';bg.fillText(i%3?'THE HALL IS EMPTY  /  LOCAL NEWS':'MISSING • SCHOOL REPORT',5,10);bg.fillText('||||| |||| |||||| |||| |||||||',6,19);bg.restore();\r\n  }\r\n  collage.forEach(([x,y,w,h,a],i)=>{\r\n   const img=art[i];if(!img)return;\r\n   const bw=w*768,bh=h*BH,k=Math.min(bw/img.naturalWidth,bh/img.naturalHeight),dw=img.naturalWidth*k,dh=img.naturalHeight*k;\r\n   bg.save();bg.translate(x*768,y*BH);bg.rotate(a);bg.shadowColor='#19170f';bg.shadowBlur=10;bg.shadowOffsetY=5;bg.drawImage(img,(bw-dw)/2,(bh-dh)/2,dw,dh);bg.restore();\r\n  });\r\n  // Physical labels also get real geometry below; lettering belongs to the same paper map (native 49 px strip and font size).\r\n  for(const [label,y,size]of [['BEWARE',.075,42],['HE KNOWS YOUR NAME',.935,25]]){\r\n   bg.fillStyle='#d5c59a';bg.shadowColor='#211d15';bg.shadowBlur=6;bg.shadowOffsetY=5;bg.fillRect(140,y*BH-34,488,49);bg.shadowBlur=0;bg.shadowOffsetY=0;\r\n   bg.fillStyle='#4b1711';bg.font=`bold ${size}px monospace`;bg.textAlign='center';bg.fillText(label,384,y*BH+3);\r\n  }\r\n }\r\n paintBase(1);\r\n if(!flat){try{\r\n  renderer=new THREE.WebGLRenderer({canvas,alpha:true,antialias:true,powerPreference:'low-power'});renderer.setClearColor(0,0);renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio||1,1.5));\r\n }catch{canvas=document.createElement('canvas');flat=true;}}\r\n const g=flat?canvas.getContext('2d'):null;\r\n if(renderer){\r\n  scene=new THREE.Scene();camera=new THREE.OrthographicCamera(-.5,.5,.5,-.5,.01,10);camera.position.z=3;\r\n  scene.add(new THREE.HemisphereLight(0xfff2d0,0x292b20,2));const light=new THREE.DirectionalLight(0xffffff,2.6);light.position.set(-1,2,3);scene.add(light);\r\n  texture=new THREE.CanvasTexture(sheet);texture.colorSpace=THREE.SRGBColorSpace;texture.minFilter=THREE.LinearFilter;\r\n  const material=new THREE.MeshStandardMaterial({map:texture,roughness:1,transparent:true,alphaTest:.02,side:THREE.DoubleSide});\r\n  mesh=new THREE.Mesh(buildMacheRelief(1),material);scene.add(mesh);\r\n  paperMap=new THREE.CanvasTexture(base);paperMap.colorSpace=THREE.SRGBColorSpace;\r\n  const paper=paperMat=new THREE.MeshStandardMaterial({map:paperMap,color:0xd3c7a7,roughness:1,side:THREE.DoubleSide});\r\n  head=makeHead(paper);scene.add(head);head.visible=false;\r\n  // Raised warning plaques sample their exact regions of the board artwork.\r\n  for(const y of [.075,.935]){\r\n   const plaque=new THREE.Mesh(plaqueGeo(y,1),material);plaque.position.set(0,(.5-y)*relief+.012,.014);plaque.userData.y=y;plaques.push(plaque);scene.add(plaque);\r\n  }\r\n  flaps=new THREE.Group();scene.add(flaps);\r\n  for(let i=0;i<12;i++){\r\n   const a=i*Math.PI/6,b=(i+1)*Math.PI/6,r=.19;\r\n   const geo=new THREE.BufferGeometry();geo.setAttribute('position',new THREE.Float32BufferAttribute([Math.cos(a)*r,Math.sin(a)*r,0,Math.cos(b)*r,Math.sin(b)*r,0,Math.cos(a+.2)*.06,Math.sin(a+.2)*.06,.12],3));geo.setAttribute('uv',new THREE.Float32BufferAttribute([.2,.2,.4,.2,.3,.5],2));geo.computeVertexNormals();flaps.add(new THREE.Mesh(geo,paper));\r\n  }\r\n }\r\n canvas.className='portal-board-canvas mache-board';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none';host.append(canvas);\r\n let width=1,height=1,face=macheLayout(1,1),raf=0,paused=false,disposed=false,hole=null,cutting=null,busy=false,started=0,awarded=false,generation=0;\r\n const fingers=new Map(),strokes=[];let lastStrokeAt=0;\r\n const rect=()=>{const b=host.getBoundingClientRect(),f=macheLayout(b.width,b.height);return {...f,left:b.left+f.left,top:b.top+f.top};};\r\n function wake(){if(!raf&&!paused&&!disposed)raf=requestAnimationFrame(render);}\r\n function settle(){if(cutting){const c=cutting;cutting=null;c.resolve();}}\r\n function cancel(){generation++;fingers.clear();strokes.length=0;busy=false;settle();wake();}\r\n function drawSheet(now){\r\n  ctx.clearRect(0,0,768,768);ctx.drawImage(base,0,0,768,768);ctx.drawImage(paint,0,0,768,768);\r\n  // Flat mode retains the square rim's cast shadow and depressed central well.\r\n  if(flat){ctx.strokeStyle='#605745';ctx.lineWidth=14;ctx.strokeRect(29,29,710,710);ctx.strokeStyle='#d4caab';ctx.lineWidth=6;ctx.strokeRect(23,23,710,710);}\r\n  if(cutting){const p=clamp((now-cutting.start)/cutting.ms);ctx.save();path(ctx,cutting.poly.map(([x,y])=>[x*768,y*768]));ctx.fillStyle=cutting.color||'#843323';ctx.globalAlpha=1-p;ctx.fill();ctx.restore();if(p>=1){hole=cutting.poly;settle();}}\r\n  if(hole){ctx.save();ctx.globalCompositeOperation='destination-out';path(ctx,hole.map(([x,y])=>[x*768,y*768]));ctx.fill();ctx.restore();}\r\n  if(busy){const t=(now-started)/MACHE_SECRET_MS,r=clamp(t*5)*.19;\r\n   ctx.save();ctx.globalCompositeOperation='destination-out';path(ctx,Array.from({length:24},(_,i)=>{const a=i*Math.PI/12,k=i%2?1:.82;return [( .5+Math.cos(a)*r*k)*768,(.5+Math.sin(a)*r*k*width/height)*768];}));ctx.fill();ctx.restore();\r\n  }\r\n }\r\n function render(now){\r\n  raf=0;if(paused||disposed)return;drawSheet(now);\r\n  const t=busy?clamp((now-started)/MACHE_SECRET_MS):0,show=busy&&t<.83,burst=clamp((t-.12)/.16),retreat=1-clamp((t-.65)/.18),scale=(.28+burst*.85)*retreat;\r\n  if(renderer){texture.needsUpdate=true;head.visible=show;head.position.z=.02+burst*.4;head.scale.setScalar(scale);head.rotation.set(-.12,Math.sin(t*15)*.18,.07*Math.sin(t*24));flaps.visible=busy;flaps.rotation.z=t*.08;renderer.render(scene,camera);}\r\n  else{\r\n   g.clearRect(0,0,canvas.width,canvas.height);const d=canvas.width/width;g.save();g.scale(d,d);g.drawImage(sheet,face.left,face.top,face.width,face.height);\r\n   if(show){g.translate(face.left+face.width/2,face.top+face.height/2);g.scale(face.width*scale,face.width*scale);g.fillStyle='#c6bc9c';g.strokeStyle='#514936';g.lineWidth=.008;g.beginPath();g.ellipse(0,0,.17,.23,0,0,Math.PI*2);g.fill();g.stroke();g.fillRect(-.24,-.18,.48,.025);g.fillRect(-.18,-.28,.36,.1);g.fillStyle='#100f0b';for(const x of [-.063,.063]){g.beginPath();g.ellipse(x,-.035,.033,.023,0,0,Math.PI*2);g.fill();}g.beginPath();g.ellipse(0,.12,.055,.07,0,0,Math.PI*2);g.fill();}g.restore();\r\n  }\r\n  if(busy&&t>=1&&!awarded){\r\n   const ticket=generation;\r\n   // Render the empty tear before the parent destroys this context and opens the existing Vault.\r\n   queueMicrotask(()=>{if(!disposed&&!paused&&busy&&!awarded&&ticket===generation){awarded=true;window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'mache'}}));}});\r\n  }\r\n  if(cutting||(busy&&t<1))wake();\r\n }\r\n function fitRelief(a){ // the board's units are width = 1, height = a: rebuild the isotropic relief, plaques and their spots for the host's aspect\r\n  if(Math.abs(a/relief-1)<.002)return;relief=a;mesh.geometry.dispose();mesh.geometry=buildMacheRelief(a);\r\n  for(const q of plaques){q.geometry.dispose();q.geometry=plaqueGeo(q.userData.y,a);q.position.y=(.5-q.userData.y)*a+.012;}\r\n }\r\n function layout(){\r\n  const b=host.getBoundingClientRect();if(!b.width||!b.height)return;\r\n  if(width!==b.width||height!==b.height)cancel();width=b.width;height=b.height;face=macheLayout(width,height);\r\n  PH=Math.max(1,Math.round(768*height/width));if(paint.height!==PH)paint.height=PH; // resizing clears the ink, as cancel() has already dropped the strokes\r\n  if(base.height!==PH){paintBase(height/width);if(paperMap){paperMap.dispose();paperMap=new THREE.CanvasTexture(base);paperMap.colorSpace=THREE.SRGBColorSpace;paperMat.map=paperMap;}} // a canvas of a new size is a new texture\r\n  if(renderer){renderer.setSize(width,height,false);fitRelief(height/width);camera.left=-.5;camera.right=.5;camera.top=height/width/2;camera.bottom=-camera.top;camera.updateProjectionMatrix();}\r\n  else{const d=Math.min(globalThis.devicePixelRatio||1,1.5);canvas.width=Math.round(width*d);canvas.height=Math.round(height*d);}\r\n  for(const [key,value]of Object.entries(face))host.style.setProperty('--face-'+key,value+'px');wake();\r\n }\r\n function press(id,x,y){\r\n  if(disposed||paused||busy||hole)return;\r\n  const r=rect(),p=[(x-r.left)/r.width,(y-r.top)/r.height];if(p.some(v=>v<0||v>1))return;\r\n  let stroke=fingers.get(id);if(!stroke){if(performance.now()-lastStrokeAt>12000)strokes.length=0;stroke=[];fingers.set(id,stroke);}\r\n  const old=stroke.at(-1)||p;if(stroke.length>2048)return;stroke.push(p);\r\n  ink.strokeStyle='#ae3021';ink.fillStyle='#ae3021';ink.lineWidth=9;ink.lineCap='round';ink.lineJoin='round';ink.beginPath();ink.moveTo(old[0]*768,old[1]*PH);ink.lineTo(p[0]*768,p[1]*PH);ink.stroke();\r\n  ink.beginPath();ink.arc(p[0]*768,p[1]*PH,4.5,0,Math.PI*2);ink.fill();\r\n  if(stroke.length%4===0){const n=stroke.length;for(let i=1;i<=3;i++){ink.beginPath();ink.arc(old[0]*768+Math.sin(n*7+i)*i*6,old[1]*PH+Math.cos(n*3+i)*i*5,2/i,0,Math.PI*2);ink.fill();}}\r\n  wake();\r\n }\r\n function release(id,cancelled=false){\r\n  if(cancelled){cancel();return;}const stroke=fingers.get(id);fingers.delete(id);if(!stroke||paused||disposed||busy)return;\r\n  lastStrokeAt=performance.now();if(stroke.length>1){strokes.push(stroke);if(strokes.length>16)strokes.shift();}\r\n  if(!awarded&&recognizeMacheFigure(strokes)){busy=true;started=performance.now();fingers.clear();wake();}\r\n }\r\n // Parent's API has no cancellation argument; capture the native event before its release handler.\r\n const onCancel=e=>{if(fingers.has(e.pointerId)||busy)cancel();};window.addEventListener('pointercancel',onCancel,true);\r\n const observer=new ResizeObserver(layout);observer.observe(host);layout();\r\n function cut(poly,color,ms=867){\r\n  if(busy)return Promise.resolve();settle();strokes.length=0;fingers.clear();\r\n  if(!Array.isArray(poly)||poly.length<3||poly.some(p=>!Array.isArray(p)||p.length!==2||!p.every(Number.isFinite)))return Promise.resolve();\r\n  if(ms<=0||paused||disposed){hole=poly;wake();return Promise.resolve();}\r\n  return new Promise(resolve=>{cutting={poly:poly.map(p=>p.slice()),color,ms,start:performance.now(),resolve};wake();});\r\n }\r\n function heal(){cancel();hole=null;ink.clearRect(0,0,768,PH);wake();}\r\n function pause(){paused=true;cancelAnimationFrame(raf);raf=0;cancel();}\r\n function resume(){if(disposed)return;paused=false;wake();}\r\n function dispose(){if(disposed)return;pause();disposed=true;observer.disconnect();window.removeEventListener('pointercancel',onCancel,true);canvas.remove();if(renderer){const geos=new Set(),mats=new Set(),maps=new Set();scene.traverse(o=>{if(o.geometry)geos.add(o.geometry);for(const m of [].concat(o.material||[])){mats.add(m);for(const v of Object.values(m))if(v?.isTexture)maps.add(v);}});geos.forEach(o=>o.dispose());mats.forEach(o=>o.dispose());maps.forEach(o=>o.dispose());renderer.dispose();}}\r\n return {canvas,glContext:renderer?.getContext()||null,background:'#29271f',ink:false,faceRect:rect,patternRect:rect,press,release,claims:()=>busy,cut,heal,pause,resume,dispose};\r\n}\r\n","modules/portal/portal-board-paper.mjs":"// DrGirlfriend's measured graph-paper board. The supplied drawing remains the sheet's source art.\r\nimport * as THREE from 'three';\r\nimport {boardLayout} from './portal-board-layout.mjs';\r\n\r\nconst ART='/pod/worlds/boards/paper/graph-guide.png';\r\nconst FRAME={x0:17/768,y0:17/1344,x1:751/768,y1:1094/1344};\r\nconst clamp=(v,a=0,b=1)=>Math.max(a,Math.min(b,v));\r\nconst ease=t=>t*t*(3-2*t);\r\nexport const PAPER_SECRET={startX:.20,startY:.78,endX:.92,endY:.08,splitMs:850};\r\nexport const paperSecretInit=()=>({phase:'idle',id:null,claimId:null,x0:0,y0:0,x:0,y:0,progress:0});\r\nexport function paperSeam(s){if(!s||s.phase!=='drag'||s.progress<=0)return null;return {x:s.x-.035,y:s.y+.035,progress:clamp(s.progress)};}\r\nexport function paperScissorsAnchor(face,s){\r\n const progress=s?.phase==='drag'?s.progress:s?.phase==='split'||s?.phase==='done'?1:0;\r\n const tx=s?.phase==='drag'?s.x:progress?PAPER_SECRET.endX:.03,ty=s?.phase==='drag'?s.y:progress?PAPER_SECRET.endY:.97;\r\n const angle=.15+Math.abs(Math.sin(progress*10*Math.PI))*.36,reach=face.width/768*1.2*138;\r\n const targetX=face.left+tx*face.width,targetY=face.top+ty*face.height;\r\n return {x:targetX-reach*Math.sin(Math.PI/4+angle),y:targetY+reach*Math.cos(Math.PI/4+angle),progress,targetX,targetY,angle};\r\n}\r\n// Face-normalized coordinates. A started scissors pull owns its pointer through release, even if aborted.\r\nexport function paperSecret(s,e){\r\n s=s||paperSecretInit();\r\n if(e.type==='reset')return paperSecretInit();\r\n if(s.phase==='split'||s.phase==='done')return s;\r\n if(e.type==='cancel')return {...paperSecretInit(),claimId:s.claimId};\r\n if(e.type==='down')return s.phase==='idle'&&e.x<=PAPER_SECRET.startX&&e.y>=PAPER_SECRET.startY?\r\n  {phase:'drag',id:e.id,claimId:e.id,x0:e.x,y0:e.y,x:e.x,y:e.y,progress:0}:s.phase==='idle'?{...s,claimId:null}:s;\r\n if(e.id!==s.id)return s;\r\n if(e.type==='move'&&s.phase==='drag'){\r\n  const dx=e.x-s.x0,dy=s.y0-e.y;\r\n  return {...s,x:e.x,y:e.y,progress:clamp(Math.min(dx/(PAPER_SECRET.endX-s.x0),dy/(s.y0-PAPER_SECRET.endY)))};\r\n }\r\n if(e.type==='up'&&s.phase==='drag')return s.progress>=.98?{...s,phase:'split',id:null,claimId:e.id}: {...paperSecretInit(),claimId:e.id};\r\n return s;\r\n}\r\nexport const paperLayout=boardLayout;\r\nconst polygon=(c,pts)=>{c.beginPath();pts.forEach(([x,y],i)=>c[i?'lineTo':'moveTo'](x,y));c.closePath();};\r\nconst zigzag=(n=28)=>Array.from({length:n+1},(_,i)=>{const t=i/n,z=(i%2?1:-1)*.012;return [clamp(t+z),clamp(1-t+z)];});\r\nconst ZIG=zigzag();\r\n\r\nfunction imageReady(){return new Promise(resolve=>{const i=new Image();i.onload=()=>resolve(i);i.onerror=()=>resolve(null);i.src=ART;});}\r\nfunction note(c,x,y,s,size=12){c.font=`${size}px \"Courier New\", monospace`;c.fillText(s,x,y);}\r\nfunction annotations(c,f){\r\n const {left:x,top:y,width:w,height:h}=f,kx=w/768,ky=h/1344;\r\n c.save();c.translate(x,y);c.scale(kx,ky);c.strokeStyle='rgba(46,56,63,.84)';c.fillStyle='#37434a';c.lineWidth=1.4;c.lineCap='round';\r\n const line=(a,b,d,e)=>{c.beginPath();c.moveTo(a,b);c.lineTo(d,e);c.stroke();};\r\n const arrow=(a,b,d,e)=>{line(a,b,d,e);const q=Math.atan2(e-b,d-a);for(const [px,py,angle] of [[a,b,q+Math.PI],[d,e,q]]){line(px,py,px-8*Math.cos(angle-.45),py-8*Math.sin(angle-.45));line(px,py,px-8*Math.cos(angle+.45),py-8*Math.sin(angle+.45));}};\r\n // Draftsman's scale marks and dimension arrows outside the source drawing's main outlines.\r\n arrow(38,1134,730,1134);note(c,244,1124,'WIDTH  22.0 cm  +/- 0.2',17);\r\n c.globalAlpha=.55;arrow(37,34,37,1081);c.save();c.translate(59,934);c.rotate(-Math.PI/2);note(c,0,0,'HEIGHT  31.0 cm  +/- 0.2',16);c.restore();c.globalAlpha=1;\r\n for(let i=0;i<=20;i++){const xx=38+i*34.6;line(xx,1140,xx,1140+(i%5===0?13:7));}\r\n line(70,1110,70,1164);line(698,1110,698,1164);\r\n note(c,41,1191,'FIG. 01 / PORTAL GEOMETRY',19);\r\n note(c,41,1217,'ellipse: x^2/a^2 + y^2/b^2 = 1',16);\r\n note(c,41,1242,'A = pi a b    |    scale 1:2',16);\r\n note(c,41,1275,'2 H2 + O2 -> 2 H2O  [balanced]',16);\r\n note(c,41,1300,'illustrative reaction',15);\r\n note(c,41,1324,'yield = actual/theory x 100% = 9/10 x 100% = 90%',13);\r\n c.restore();\r\n}\r\nfunction paintScissors2D(c,x,y,k,travel,onlySide=null){\r\n c.save();c.translate(x,y);c.rotate(-3*Math.PI/4);c.scale(k,k);c.shadowColor='#192632';c.shadowBlur=9;c.shadowOffsetY=4;\r\n const a=.15+Math.abs(Math.sin(travel*10*Math.PI))*.36;\r\n for(const side of (onlySide===null?[-1,1]:[onlySide])){\r\n  c.save();c.rotate(side*a);c.fillStyle=side<0?'#aebdc4':'#dce8ed';c.strokeStyle='#667985';c.lineWidth=1.5;\r\n  polygon(c,[[0,0],[side*7,10],[side*3,123],[0,136],...Array.from({length:12},(_,i)=>[side*(i%2?-.8:-4),126-i*9]),[-side*2,15]]);c.fill();c.stroke();\r\n  c.fillStyle='#224753';c.beginPath();c.ellipse(side*20,-52,18,26,side*.2,0,Math.PI*2);c.fill();\r\n  c.fillStyle='#dce9e9';c.beginPath();c.ellipse(side*20,-52,10,17,side*.2,0,Math.PI*2);c.fill();c.restore();\r\n }\r\n c.fillStyle='#d8b27b';c.beginPath();c.arc(0,0,7,0,Math.PI*2);c.fill();c.fillStyle='#61717b';c.beginPath();c.arc(0,0,2,0,Math.PI*2);c.fill();c.restore();\r\n}\r\nexport function makeScissors(){\r\n const group=new THREE.Group(),steel=new THREE.MeshStandardMaterial({color:0xb9c9d0,metalness:.83,roughness:.22,side:THREE.DoubleSide}),edge=new THREE.MeshStandardMaterial({color:0xe8f2f4,metalness:.75,roughness:.18,side:THREE.DoubleSide}),grip=new THREE.MeshStandardMaterial({color:0x214754,metalness:.18,roughness:.44}),screw=new THREE.MeshStandardMaterial({color:0xc59e68,metalness:.72,roughness:.29});\r\n const blades=[];\r\n for(const side of [-1,1]){\r\n  const arm=new THREE.Group();group.add(arm);blades.push(arm);\r\n  const shape=new THREE.Shape();shape.moveTo(0,0);shape.lineTo(side*7,10);shape.lineTo(side*3,124);shape.lineTo(0,138);\r\n  for(let n=0;n<12;n++)shape.lineTo(side*(n%2?-.8:-4),126-n*9); // decorative pinking teeth along the cutting edge\r\n  shape.lineTo(-side*2,12);shape.closePath();\r\n  const blade=new THREE.Mesh(new THREE.ExtrudeGeometry(shape,{depth:2.6,bevelEnabled:true,bevelThickness:.8,bevelSize:.8,bevelSegments:2}),side<0?edge:steel);blade.position.z=side<0?4:1;arm.position.z=side<0?1:-48;arm.add(blade); // left blade rides above; opposing blade sits behind the paper\r\n  const spine=new THREE.Mesh(new THREE.BoxGeometry(3,71,4),steel);spine.position.set(side*1,-30,side<0?4:1);arm.add(spine);\r\n  const ring=new THREE.Mesh(new THREE.TorusGeometry(19,7,8,30),grip);ring.scale.set(side<0?.88:1,side<0?1.15:1.32,1);ring.position.set(side*20,-54,5);ring.rotation.z=side*.14;arm.add(ring);\r\n  const ferrule=new THREE.Mesh(new THREE.CylinderGeometry(6,7,12,12),steel);ferrule.rotation.x=Math.PI/2;ferrule.position.set(side*5,-13,5);arm.add(ferrule);\r\n }\r\n const rivet=new THREE.Mesh(new THREE.CylinderGeometry(6,6,3,20),screw);rivet.rotation.x=Math.PI/2;rivet.position.z=9;group.add(rivet);\r\n const slot=new THREE.Mesh(new THREE.BoxGeometry(5,1,1),steel);slot.position.z=11;group.add(slot);\r\n group.rotation.z=-Math.PI/4;\r\n group.userData={blades,materials:[steel,edge,grip,screw]};return group;\r\n}\r\n\r\nexport async function createPaperBoard(host,{flat=false}={}){\r\n const art=await imageReady();\r\n const canvas=document.createElement('canvas');canvas.className='portal-board-canvas';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='position:absolute;inset:0;display:block;width:100%;height:100%';\r\n const sheet=document.createElement('canvas'),sc=sheet.getContext('2d');let g=null,renderer=null,scene=null,camera=null,texture=null,plane=null,scissors=null;\r\n if(flat)g=canvas.getContext('2d');\r\n else{\r\n  renderer=new THREE.WebGLRenderer({canvas,alpha:true,antialias:true,powerPreference:'low-power'});renderer.setPixelRatio(Math.min(devicePixelRatio||1,1.5));renderer.setClearColor(0,0);\r\n  scene=new THREE.Scene();camera=new THREE.OrthographicCamera(0,1,0,-1,.1,1000);camera.position.z=400;\r\n  scene.add(new THREE.HemisphereLight(0xffffff,0x51616b,2));const lamp=new THREE.DirectionalLight(0xffffff,2);lamp.position.set(-100,150,250);scene.add(lamp);\r\n  texture=new THREE.CanvasTexture(sheet);texture.colorSpace=THREE.SRGBColorSpace;texture.minFilter=THREE.LinearFilter;\r\n  plane=new THREE.Mesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({map:texture,transparent:true,alphaTest:.01,depthWrite:true,depthTest:true}));plane.position.z=-15;plane.renderOrder=1;scene.add(plane);\r\n  scissors=makeScissors();scissors.position.z=15;scene.add(scissors);\r\n }\r\n host.append(canvas);\r\n let width=1,height=1,face=paperLayout(1,1),raf=0,disposed=false,paused=false,cutState=null,hole=null,secret=paperSecretInit(),splitAt=0,awarded=false;\r\n const fingers=new Map(),glue=[];\r\n const rect=()=>{const b=host.getBoundingClientRect(),f=paperLayout(b.width,b.height);return {left:b.left+f.left,top:b.top+f.top,width:f.width,height:f.height};};\r\n const norm=(x,y)=>{const r=rect();return {x:(x-r.left)/r.width,y:(y-r.top)/r.height};};\r\n const path=(poly,f)=>poly.map(([u,v])=>[f.left+u*f.width,f.top+v*f.height]);\r\n function sheetContent(c){\r\n  if(art)c.drawImage(art,face.left,face.top,face.width,face.height);\r\n  else{c.fillStyle='#fafaf4';c.fillRect(face.left,face.top,face.width,face.height);c.strokeStyle='#b9d5e1';for(let x=face.left;x<face.left+face.width;x+=face.width/24){c.beginPath();c.moveTo(x,face.top);c.lineTo(x,face.top+face.height);c.stroke();}for(let y=face.top;y<face.top+face.height;y+=face.width/24){c.beginPath();c.moveTo(face.left,y);c.lineTo(face.left+face.width,y);c.stroke();}}\r\n  annotations(c,face);\r\n }\r\n function drawGlue(c,now){\r\n  for(let i=glue.length-1;i>=0;i--){const p=glue[i],age=now-p.t;if(age>2700){glue.splice(i,1);continue;}\r\n   const alpha=(1-age/2700)*.92,x=face.left+p.x*face.width,y=face.top+p.y*face.height,r=Math.max(3,face.width*.009);\r\n   c.save();c.globalAlpha=alpha;c.shadowColor='#798a9b';c.shadowBlur=r*.9;c.shadowOffsetY=r*.55;c.fillStyle='#f4f6f1';c.beginPath();c.arc(x,y,r,0,Math.PI*2);c.fill();\r\n   c.shadowBlur=0;c.shadowOffsetY=0;c.fillStyle='#fff';c.beginPath();c.ellipse(x-r*.28,y-r*.3,r*.42,r*.22,-.5,0,Math.PI*2);c.fill();c.restore();\r\n  }\r\n }\r\n function drawCut(c,now){\r\n  if(!cutState)return;\r\n  const {poly,color,t0,ms}=cutState,p=clamp((now-t0)/ms),pts=path(poly,face);\r\n  // A soft approaching shadow precedes the colored paper, then the piece settles and opens the tunnel.\r\n  if(p>=.83)return;\r\n  c.save();polygon(c,pts);c.shadowColor='#1e2530';c.shadowBlur=18*(1-p)+5;c.shadowOffsetX=9*(1-p);c.shadowOffsetY=16*(1-p);c.fillStyle='rgba(20,24,30,.34)';c.fill();c.restore();\r\n  if(p>=.17&&p<.83){c.save();c.translate(0,-(1-ease(clamp((p-.17)/.66)))*Math.min(110,face.height*.16));polygon(c,pts);c.fillStyle=color||'#d47296';c.shadowColor='#222';c.shadowBlur=12;c.shadowOffsetY=7;c.fill();c.strokeStyle='rgba(255,255,255,.65)';c.lineWidth=2;c.stroke();c.restore();}\r\n }\r\n function sheetDraw(now){\r\n  if(!sc)return;sc.setTransform(1,0,0,1,0,0);sc.clearRect(0,0,sheet.width,sheet.height);const scale=sheet.width/width;sc.setTransform(scale,0,0,scale,0,0);\r\n  const split=secret.phase==='split'||secret.phase==='done';\r\n  if(split){\r\n   const q=ease(clamp((now-splitAt)/PAPER_SECRET.splitMs)),gap=q*face.width*.12,edge=path(ZIG,face);\r\n   for(const [side,poly,shift] of [[-1,[[0,0],[1,0],...ZIG.slice().reverse()],[-gap,-gap*.35]],[1,[[0,1],...ZIG,[1,1]],[gap,gap*.35]]]){\r\n    sc.save();sc.translate(...shift);polygon(sc,path(poly,face));sc.clip();sheetContent(sc);sc.restore();\r\n   }\r\n   sc.save();sc.strokeStyle='#f9fbef';sc.lineWidth=2;sc.setLineDash([3,4]);polygon(sc,edge);sc.stroke();sc.restore();\r\n  }else{\r\n   sheetContent(sc);\r\n  }\r\n  drawGlue(sc,now);drawCut(sc,now);\r\n  if(secret.phase==='drag'&&secret.progress>0){\r\n   const seam=paperSeam(secret),steps=Math.max(2,Math.ceil(seam.progress*48));\r\n   sc.save();sc.beginPath();for(let i=0;i<=steps;i++){const t=i/steps,z=i&&i<steps?(i%2?1:-1)*.004:0,u=secret.x0+(seam.x-secret.x0)*t+z,v=secret.y0+(seam.y-secret.y0)*t+z;const x=face.left+u*face.width,y=face.top+v*face.height;i?sc.lineTo(x,y):sc.moveTo(x,y);}\r\n   sc.strokeStyle='#38342d';sc.lineWidth=Math.max(2.5,face.width*.011);sc.lineCap='round';sc.lineJoin='round';sc.stroke();sc.translate(0,-1);sc.strokeStyle='#fff9e8';sc.lineWidth=Math.max(1,face.width*.003);sc.stroke();sc.restore();\r\n  }\r\n  const opening=hole||(cutState&&now>=cutState.t0+cutState.ms*.83?cutState.poly:null);\r\n  if(opening&&!split){sc.save();sc.globalCompositeOperation='destination-out';polygon(sc,path(opening,face));sc.fill();sc.restore();}\r\n }\r\n function scissorsPose(now){\r\n  const {x,y,progress:p,angle}=paperScissorsAnchor(face,secret),k=face.width/768*1.2;\r\n  if(scissors){scissors.position.set(x,-y,0);scissors.scale.set(k,k,1);scissors.userData.blades[0].rotation.z=-angle;scissors.userData.blades[1].rotation.z=angle;scissors.visible=secret.phase!=='done'&&!cutState&&!hole;}\r\n  else if(g&&!cutState&&!hole){paintScissors2D(g,x,y,k,p,1);}\r\n }\r\n function render(now=performance.now()){\r\n  raf=0;if(disposed||paused)return;\r\n  const awardNow=secret.phase==='split'&&now-splitAt>=PAPER_SECRET.splitMs&&!awarded;\r\n  if(awardNow){secret={...secret,phase:'done'};awarded=true;}\r\n  sheetDraw(now);\r\n  if(g){g.setTransform(1,0,0,1,0,0);g.clearRect(0,0,canvas.width,canvas.height);if(secret.phase!=='done'&&!cutState&&!hole){g.save();g.scale(canvas.width/width,canvas.height/height);const {x,y,progress:p}=paperScissorsAnchor(face,secret),k=face.width/768*1.2;paintScissors2D(g,x,y,k,p,-1);g.restore();}g.drawImage(sheet,0,0);g.save();g.scale(canvas.width/width,canvas.height/height);scissorsPose(now);g.restore();}\r\n  else{texture.needsUpdate=true;scissorsPose(now);renderer.render(scene,camera);}\r\n  if(cutState&&now>=cutState.t0+cutState.ms){hole=cutState.poly;cutState.resolve();cutState=null;}\r\n  if(awardNow)window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'paper'}}));\r\n  if(glue.length||cutState||secret.phase==='split'||secret.phase==='drag')wake();\r\n }\r\n function wake(){if(!raf&&!disposed&&!paused)raf=requestAnimationFrame(render);}\r\n function layout(box={width:host.clientWidth,height:host.clientHeight}){\r\n  if(!box.width||!box.height)return;width=box.width;height=box.height;face=paperLayout(width,height);\r\n  const s=Math.min(devicePixelRatio||1,1.5);sheet.width=Math.max(1,Math.round(width*s));sheet.height=Math.max(1,Math.round(height*s));\r\n  if(g){canvas.width=sheet.width;canvas.height=sheet.height;}else{renderer.setSize(width,height,false);camera.left=0;camera.right=width;camera.top=0;camera.bottom=-height;camera.updateProjectionMatrix();plane.scale.set(width,height,1);plane.position.set(width/2,-height/2,-15);}\r\n  for(const [key,value] of Object.entries(face))host.style.setProperty('--face-'+key,value+'px');wake();\r\n }\r\n const observer=new ResizeObserver(entries=>layout(entries.at(-1).contentRect));observer.observe(host);layout();\r\n function press(id,x,y){if(disposed||paused)return;const p=norm(x,y),old=fingers.get(id);if(!old){fingers.set(id,p);secret=paperSecret(secret,{type:'down',id,...p});}\r\n  else{fingers.set(id,p);secret=paperSecret(secret,{type:'move',id,...p});if(secret.id!==id){const dist=Math.hypot((p.x-old.x)*face.width,(p.y-old.y)*face.height),steps=Math.min(30,Math.max(1,Math.ceil(dist/Math.max(3,face.width*.008))));for(let i=1;i<=steps;i++)glue.push({x:old.x+(p.x-old.x)*i/steps,y:old.y+(p.y-old.y)*i/steps,t:performance.now()});}}\r\n  if(glue.length>700)glue.splice(0,glue.length-700);wake();\r\n }\r\n function release(id){fingers.delete(id);const before=secret.phase;secret=paperSecret(secret,{type:'up',id});if(before==='drag'&&secret.phase==='split')splitAt=performance.now();wake();}\r\n function cut(poly,color,ms=867){\r\n  if(cutState)cutState.resolve();\r\n  if(!Array.isArray(poly)||poly.length<3)return Promise.resolve();\r\n  if(ms<=0){hole=poly;cutState=null;wake();return Promise.resolve();}\r\n  return new Promise(resolve=>{cutState={poly,color,t0:performance.now(),ms,resolve};wake();});\r\n }\r\n function heal(){if(cutState){cutState.resolve();cutState=null;}hole=null;glue.length=0;secret=paperSecretInit();splitAt=0;fingers.clear();wake();}\r\n function pause(){paused=true;cancelAnimationFrame(raf);raf=0;fingers.clear();if(secret.phase==='drag'||secret.phase==='split'){secret=paperSecretInit();splitAt=0;}if(cutState){cutState.resolve();cutState=null;}}\r\n function resume(){paused=false;wake();}\r\n function dispose(){if(disposed)return;pause();disposed=true;observer.disconnect();canvas.remove();if(renderer){plane.geometry.dispose();plane.material.dispose();texture.dispose();scissors.traverse(o=>o.geometry?.dispose?.());scissors.userData.materials.forEach(m=>m.dispose());renderer.dispose();}}\r\n return {canvas,glContext:renderer?.getContext()||null,background:'#202d35',ink:false,faceRect:rect,patternRect:()=>{const r=rect();return {left:r.left+FRAME.x0*r.width,top:r.top+FRAME.y0*r.height,width:(FRAME.x1-FRAME.x0)*r.width,height:(FRAME.y1-FRAME.y0)*r.height};},press,release,cancel(id){fingers.delete(id);secret=paperSecret(secret,{type:'cancel',id});wake();},claims:id=>secret.claimId===id,cut,heal,pause,resume,dispose};\r\n}\r\n","modules/portal/portal-board-pixel.mjs":"// Overhead pixel dance floor: real drag wiring on foreground rafters, the\r\n// supplied geometric tablet below, and one board-owned render/GL lifecycle.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {orientMatrix} from './portal-board-glb.mjs';\r\nimport {boardLayout} from './portal-board-layout.mjs';\r\nimport {PIXEL_ANCHORS,PIXEL_FIXTURE_IDS,pixelWireInit,pixelWireConnect,pixelWireRoute} from './portal-pixel-domain.mjs';\r\nimport {PIXEL_TIMING} from './portal-pixel-config.mjs';\r\nimport {loadPixelParadeCoaches,drawPixelFloorCoaches} from './portal-pixel-coaches.mjs';\r\n\r\nconst IDS=PIXEL_FIXTURE_IDS;\r\nconst COLORS=['#fb6aff','#5cf5ee','#ffd66b','#9e81ff','#fa8467','#8effa3','#ffb3eb','#79acff','#ffe47c','#62d8ff','#ff7fa9','#90ffdb','#c7a5ff','#ffa665','#ffffff'];\r\nconst clamp=x=>Math.max(0,Math.min(1,x));\r\nconst color=id=>COLORS[IDS.indexOf(id)];\r\nconst at=id=>PIXEL_ANCHORS[id];\r\nconst floorX=(u,v)=>.5+(u-.5)*(.44+.36*v);\r\nconst floorY=v=>.18+v*.68;\r\nconst FLOOR_QUAD=[[.28,.18],[.72,.18],[.9,.86],[.1,.86]];\r\nconst poly=(g,p,w,h)=>{g.beginPath();p.forEach(([x,y],i)=>g[i?'lineTo':'moveTo'](x*w,y*h));g.closePath();};\r\nconst line=(g,p,w,h)=>{g.beginPath();p.forEach(([x,y],i)=>g[i?'lineTo':'moveTo'](x*w,y*h));g.stroke();};\r\nconst disposeTree=root=>root?.traverse(o=>{o.geometry?.dispose();for(const m of [].concat(o.material||[])){if(!m)continue;for(const v of Object.values(m))if(v?.isTexture)v.dispose();m.dispose();}});\r\n\r\nexport async function createPixelBoard(host,{flat=false}={}){\r\n let disposed=false,paused=false,raf=0,last=0,frameCost=0,state=pixelWireInit(),selected=null,drag=null,ignoreClick=false;\r\n let renderer=null,scene=null,camera=null,coaches=[],ceremony=null,hole=null,observer=null,ui=null;\r\n let floorMeshes=[],projectors=[],floorBounds=null;\r\n let rect={left:0,top:0,width:1,height:1},W=192,H=320;\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const canvas=flat?document.createElement('canvas'):null;\r\n const fx=document.createElement('canvas'),g=fx.getContext('2d');\r\n const cutMask=document.createElement('canvas'),cutContext=cutMask.getContext('2d'),cutTexture=new THREE.CanvasTexture(cutMask),cutMaterials=new Set();\r\n cutTexture.minFilter=cutTexture.magFilter=THREE.NearestFilter;\r\n const cutSize=new THREE.Vector2(W,H);\r\n function maskMaterial(material){if(!material||cutMaterials.has(material))return;cutMaterials.add(material);\r\n  const compile=material.onBeforeCompile;\r\n  material.onBeforeCompile=shader=>{compile?.(shader);shader.uniforms.pixelCutMask={value:cutTexture};shader.uniforms.pixelCutSize={value:cutSize};\r\n   shader.fragmentShader='uniform sampler2D pixelCutMask; uniform vec2 pixelCutSize;\\n'+shader.fragmentShader.replace('#include <clipping_planes_fragment>','#include <clipping_planes_fragment>\\nif(texture2D(pixelCutMask,vec2(gl_FragCoord.x/pixelCutSize.x,gl_FragCoord.y/pixelCutSize.y)).a > 0.5) discard;');\r\n  };material.needsUpdate=true;\r\n }\r\n function updateCutMask(){cutMask.width=W;cutMask.height=H;cutSize.set(W,H);cutContext.clearRect(0,0,W,H);if(hole){poly(cutContext,hole,W,H);cutContext.fillStyle='#fff';cutContext.fill();}cutTexture.needsUpdate=true;}\r\n fx.className='portal-board-canvas pixel-board-fx';fx.style.cssText='position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;pointer-events:none';\r\n let output=canvas;\r\n try{\r\n  if(!flat){\r\n   const loader=new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);\r\n   const [tablet,projector]=await Promise.all([loader.loadAsync('/pod/worlds/boards/pixel/dance-floor.glb'),loader.loadAsync('/pod/worlds/boards/pixel/projector.glb')]);\r\n   scene=new THREE.Scene();camera=new THREE.OrthographicCamera(-1,1,1,-1,.01,30);camera.position.set(0,0,6);camera.lookAt(0,0,0);\r\n   scene.add(new THREE.HemisphereLight(0xd7c6ff,0x110928,2.6));const key=new THREE.DirectionalLight(0xffd999,2.7);key.position.set(-2,4,5);scene.add(key);\r\n   const box=new THREE.Box3().setFromObject(tablet.scene),size=box.getSize(new THREE.Vector3()),r=orientMatrix(size),matrix=new THREE.Matrix4().set(...r[0],0,...r[1],0,...r[2],0,0,0,0,1);\r\n   tablet.scene.position.sub(box.getCenter(new THREE.Vector3()));tablet.scene.updateMatrixWorld(true);\r\n   const floor=new THREE.Group();floor.add(tablet.scene);floor.applyMatrix4(matrix);floor.updateMatrixWorld(true);\r\n   // Bake the supplied mesh into a screen-facing relief. On each resize its\r\n   // original XY is projected into the exact same perspective quad as the\r\n   // 2D grout, light sections, coaches and collapse mask.\r\n   floor.updateMatrixWorld(true);floorBounds=new THREE.Box3().setFromObject(floor);\r\n   floor.traverse(o=>{if(!o.isMesh)return;const geometry=o.geometry.clone();geometry.applyMatrix4(o.matrixWorld);\r\n    const original=new Float32Array(geometry.attributes.position.array),mesh=new THREE.Mesh(geometry,o.material);\r\n    for(const m of [].concat(mesh.material||[])){if(m?.color)m.color.set('#76469b');if(m){m.roughness=.73;m.metalness=.13;m.side=THREE.DoubleSide;}}\r\n    for(const material of [].concat(mesh.material||[]))maskMaterial(material);\r\n    scene.add(mesh);floorMeshes.push({mesh,original});\r\n   });\r\n   // Reuse the supplied projector geometry for a few near fixtures; the other\r\n   // fixtures are crisp 2D pixel art so phone rendering stays inexpensive.\r\n   const pbox=new THREE.Box3().setFromObject(projector.scene),ps=pbox.getSize(new THREE.Vector3()),fit=.08/Math.max(ps.x,ps.y,ps.z);\r\n   projector.scene.position.sub(pbox.getCenter(new THREE.Vector3()));\r\n   projector.scene.traverse(o=>{if(o.isMesh)for(const m of [].concat(o.material||[])){m?.color?.set('#76528c');maskMaterial(m);}});\r\n   for(const id of ['tl','tm','tr','lm','rm','bl','bm','br']){const obj=projector.scene.clone(true);obj.scale.setScalar(fit);scene.add(obj);projectors.push({id,obj});}\r\n   renderer=new THREE.WebGLRenderer({alpha:true,antialias:false,powerPreference:'low-power'});renderer.setClearColor(0x000000,0);renderer.setPixelRatio(1);renderer.outputColorSpace=THREE.SRGBColorSpace;output=renderer.domElement;\r\n  }\r\n  output.className='portal-board-canvas pixel-board';output.setAttribute('aria-hidden','true');output.style.cssText='position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;pointer-events:none';host.append(output,fx);\r\n  const uiHost=host.closest('#portalHome')||host;ui=document.createElement('div');ui.className='pixel-wire-controls';ui.style.cssText='position:absolute;inset:0;z-index:6;pointer-events:none';\r\n  const buttons=new Map();\r\n  for(const id of IDS){\r\n   const b=document.createElement('button');b.type='button';b.dataset.pixelFixture=id;\r\n   b.setAttribute('aria-label',`${id==='db'?'Disco ball':`Fixture ${id.toUpperCase()}`}: connect wire`);\r\n   b.style.cssText=`position:absolute;width:40px;height:40px;transform:translate(-50%,-50%);border:0;background:transparent;color:${color(id)};font:700 9px monospace;pointer-events:auto;border-radius:4px;text-shadow:1px 1px #000;touch-action:none`;\r\n   b.textContent='';\r\n   b.addEventListener('pointerdown',e=>{if(!available())return;drag={from:id,pointerId:e.pointerId,x:e.clientX,y:e.clientY,endX:e.clientX,endY:e.clientY,moved:false};b.setPointerCapture(e.pointerId);wake();});\r\n   b.addEventListener('pointermove',e=>{if(!drag||drag.pointerId!==e.pointerId)return;drag.endX=e.clientX;drag.endY=e.clientY;if(Math.hypot(e.clientX-drag.x,e.clientY-drag.y)>6)drag.moved=true;wake();});\r\n   b.addEventListener('pointerup',e=>{if(!drag||drag.pointerId!==e.pointerId)return;const from=drag.from,moved=drag.moved;drag=null;if(moved){ignoreClick=true;const to=nearest(e.clientX,e.clientY,from);if(to)connect(from,to);else wake();}});\r\n   b.addEventListener('pointercancel',()=>{drag=null;wake();});\r\n   b.onclick=e=>{e.stopPropagation();if(ignoreClick){ignoreClick=false;return;}tap(id);};\r\n   ui.append(b);buttons.set(id,b);\r\n  }\r\n  const message=document.createElement('span');message.setAttribute('role','status');message.style.cssText='position:absolute;left:4%;bottom:2%;width:92%;text-align:center;color:#ffe8a4;font:11px/1.35 monospace;text-shadow:1px 2px #000;pointer-events:none';ui.append(message);\r\n  const reset=document.createElement('button');reset.type='button';reset.textContent='↻';reset.setAttribute('aria-label','Reset wires');reset.title='Reset wires';reset.style.cssText='position:absolute;right:2%;top:19%;width:32px;height:32px;background:#241233;border:1px solid #98733f;color:#ffe5a3;font:20px monospace;border-radius:3px;pointer-events:auto';reset.onclick=heal;ui.append(reset);\r\n  uiHost.append(ui);\r\n  function available(){return !disposed&&!paused&&!ceremony&&!hole;}\r\n  function nearest(x,y,except){let id=null,best=31;for(const key of IDS){if(key===except)continue;const b=buttons.get(key).getBoundingClientRect(),d=Math.hypot(x-(b.left+b.width/2),y-(b.top+b.height/2));if(d<best){id=key;best=d;}}return id;}\r\n  function connect(from,to){if(!available())return;const was=state.complete;state=pixelWireConnect(state,from,to);selected=null;updateUI();wake();if(!was&&state.complete)startCeremony(false);}\r\n  function tap(id){if(!available())return;if(selected&&selected!==id)connect(selected,id);else{selected=selected===id?null:id;updateUI();wake();}}\r\n  function updateUI(){\r\n   if(!ui)return;for(const[id,b]of buttons){const on=state.powered.includes(id);b.setAttribute('aria-pressed',String(on));b.style.background=selected===id?'#8b4aae66':'transparent';b.style.boxShadow=on?`0 0 10px ${color(id)}`:'none';b.title=`${id==='db'?'Disco ball':id.toUpperCase()} ${on?'powered':'unpowered'}`;}\r\n   message.textContent=ceremony?'':`${selected?'Connect another light':'Drag wire between lights'} · ${state.powered.length}/15 powered`;\r\n   ui.style.visibility=paused||disposed||!!ceremony||!!hole?'hidden':'visible';\r\n  }\r\n  function layout(){const b=host.getBoundingClientRect();rect={...boardLayout(b.width,b.height),left:b.left,top:b.top};\r\n   W=Math.min(256,Math.max(120,Math.round(rect.width*.65)));H=Math.min(512,Math.max(160,Math.round(W*rect.height/rect.width)));fx.width=W;fx.height=H;updateCutMask();\r\n   if(flat){output.width=W;output.height=H;}\r\n   if(renderer){renderer.setSize(W,H,false);const aspect=rect.width/rect.height;camera.left=-aspect;camera.right=aspect;camera.top=1;camera.bottom=-1;camera.updateProjectionMatrix();\r\n    const bw=Math.max(.001,floorBounds.max.x-floorBounds.min.x),bh=Math.max(.001,floorBounds.max.y-floorBounds.min.y),bz=Math.max(.001,floorBounds.max.z-floorBounds.min.z);\r\n    for(const {mesh,original} of floorMeshes){const pos=mesh.geometry.attributes.position;\r\n     for(let i=0;i<pos.count;i++){const u=clamp((original[i*3]-floorBounds.min.x)/bw),v=clamp((floorBounds.max.y-original[i*3+1])/bh),relief=(original[i*3+2]-floorBounds.min.z)/bz;\r\n      pos.setXYZ(i,(floorX(u,v)-.5)*2*aspect,(.5-floorY(v))*2,-.35+relief*.09);\r\n     }pos.needsUpdate=true;mesh.geometry.computeVertexNormals();mesh.geometry.computeBoundingSphere();\r\n    }\r\n    for(const {id,obj}of projectors){const[x,y]=at(id);obj.position.set((x-.5)*2*aspect,(.5-y)*2,.5);}\r\n   }\r\n   const ub=uiHost.getBoundingClientRect();for(const[id,b]of buttons){const[x,y]=at(id);b.style.left=`${rect.left-ub.left+x*rect.width}px`;b.style.top=`${rect.top-ub.top+y*rect.height}px`;}\r\n   updateUI();wake();\r\n  }\r\n  function phase(now){if(!ceremony)return {name:'idle',crumble:0,sparks:0,blackout:0,fall:0,done:false};\r\n   const elapsed=(now-ceremony.at)/(reduced?.25:1),a=PIXEL_TIMING.party,b=a+PIXEL_TIMING.crumble,c=b+PIXEL_TIMING.sparks,d=c+PIXEL_TIMING.blackout,e=d+PIXEL_TIMING.fall;\r\n   return {name:elapsed<a?'party':elapsed<b?'crumble':elapsed<c?'sparks':elapsed<d?'blackout':'fall',crumble:clamp((elapsed-a)/PIXEL_TIMING.crumble),sparks:clamp((elapsed-b)/PIXEL_TIMING.sparks),blackout:clamp((elapsed-c)/PIXEL_TIMING.blackout),fall:clamp((elapsed-d)/PIXEL_TIMING.fall),done:elapsed>=e};\r\n  }\r\n  function drawBase(now){const p=phase(now),t=now/1000;\r\n   if(flat)output.getContext('2d').clearRect(0,0,W,H);else renderer.render(scene,camera);\r\n   g.clearRect(0,0,W,H);g.imageSmoothingEnabled=false;\r\n   // The transparent gaps between rafters look onto one distant floor.\r\n   const floor=FLOOR_QUAD;poly(g,floor,W,H);g.fillStyle=flat?'#33144d':'#2c12471f';g.fill();\r\n   g.save();poly(g,floor,W,H);g.clip();\r\n   for(let row=0;row<12;row++)for(let col=0;col<12;col++){const v0=row/12,v1=(row+1)/12,u0=col/12,u1=(col+1)/12;\r\n    const q=[[floorX(u0,v0),floorY(v0)],[floorX(u1,v0),floorY(v0)],[floorX(u1,v1),floorY(v1)],[floorX(u0,v1),floorY(v1)]];\r\n    poly(g,q,W,H);g.fillStyle=(row+col)%2?'#63308335':'#3b195a25';g.fill();g.strokeStyle='#d5a264a8';g.lineWidth=1;g.stroke();\r\n   }\r\n   for(const id of state.powered){const i=IDS.indexOf(id),col=i%5,row=Math.floor(i/5),cx=floorX((col+.5)/5,(row+.5)/3),cy=floorY((row+.5)/3),radius=W*(.07+row*.012);\r\n    const glow=g.createRadialGradient(cx*W,cy*H,1,cx*W,cy*H,radius);glow.addColorStop(0,color(id)+'9c');glow.addColorStop(1,color(id)+'00');g.fillStyle=glow;g.fillRect(cx*W-radius,cy*H-radius,radius*2,radius*2);\r\n    // Apexes project distinct pixel stamps; cardinals use broad spotlights.\r\n    if(!['tm','rm','bm','lm','db'].includes(id)){g.fillStyle=color(id)+'ad';const px=Math.round(cx*W),py=Math.round(cy*H),stamp=i%6;\r\n     for(let n=-3;n<=3;n++)for(let m=-3;m<=3;m++){\r\n      const x=Math.abs(n),y=Math.abs(m),lit=stamp===0?x+y===3:stamp===1?x===y:stamp===2?x===3||y===3:stamp===3?x===0||y===0:stamp===4?x*x+y*y>=7&&x*x+y*y<=13:(n*m)%3===0;\r\n      if(lit)g.fillRect(px+n*3,py+m*3,2,2);\r\n     }\r\n    }\r\n    if(['tl','tr','br','bl'].includes(id)){g.strokeStyle=color(id)+'50';g.lineWidth=2;line(g,[[.5,.5],[floorX((col+.5)/5,(row+.5)/3),cy]],W,H);}\r\n   }\r\n   drawPixelFloorCoaches(g,coaches,{time:t,powered:state.powered,ids:IDS,width:W,height:H,floorX,floorY,party:!!ceremony&&p.name==='party',fall:p.fall});\r\n   if(ceremony&&p.name==='party')for(let i=0;i<45;i++){const u=(i*17%47)/47,v=(i*29%43)/43,x=floorX(u,v)*W,y=floorY(v)*H;g.globalAlpha=.35+.3*Math.sin(t*13+i);g.fillStyle=COLORS[i%COLORS.length];g.fillRect(x|0,y|0,2+i%2,2+i%2);}g.globalAlpha=1;\r\n   if(ceremony&&p.crumble>0){for(let row=0;row<12;row++)for(let col=0;col<12;col++){const n=(row*17+col*29)%101/101;if(n>=p.crumble)continue;const v0=row/12,v1=(row+1)/12,u0=col/12,u1=(col+1)/12;poly(g,[[floorX(u0,v0),floorY(v0)],[floorX(u1,v0),floorY(v0)],[floorX(u1,v1),floorY(v1)],[floorX(u0,v1),floorY(v1)]],W,H);g.fillStyle='#03020b';g.fill();}}\r\n   if(ceremony&&p.fall>0){g.fillStyle='#030109';g.fillRect(0,0,W,H);g.fillStyle='#714fab';g.fillRect(W*.36,H*.58,W*.28,H*.26);g.fillStyle='#050412';g.fillRect(W*.38,H*.60,W*.24,H*.24);g.fillStyle='#ffcf75';g.fillRect(W*.45,H*.64,W*.1,2);}\r\n   g.restore();\r\n   // Pixel mist is stepped and faint enough to keep the coach sprites legible.\r\n   for(let i=0;i<14;i++){const x=((i*.137+t*.006)%1)*W,y=(.22+((i*17)%59)/100)*H;g.globalAlpha=.045;g.fillStyle='#d7beef';g.fillRect(x|0,y|0,8+i%3*3,3);g.fillRect((x+4)|0,(y-2)|0,6,2);}g.globalAlpha=1;\r\n   return p;\r\n  }\r\n  function disco(ctx,t){for(let i=0;i<64;i++){const a=i*2.399+t*.22,r=((i*29)%63)/63,xx=W*(.5+Math.cos(a)*r*.35),yy=H*(.73+Math.sin(a)*r*.16);ctx.fillStyle=COLORS[i%COLORS.length];ctx.fillRect(xx|0,yy|0,i%5?1:2,i%5?1:2);}}\r\n  function drawRafters(now,p){const t=now/1000,levels=[.08,.3236,.5,.6764,.92];\r\n   for(const x of [.08,.5,.92]){g.fillStyle='#3f2852';g.fillRect(x*W-4,0,8,H);g.fillStyle='#9b70a1';g.fillRect(x*W-4,0,2,H);g.fillStyle='#140d23';g.fillRect(x*W+2,0,2,H);}\r\n   for(const y of levels){g.fillStyle='#4b2d5e';g.fillRect(0,y*H-5,W,10);g.fillStyle='#a77dac';g.fillRect(0,y*H-5,W,2);g.fillStyle='#170d27';g.fillRect(0,y*H+3,W,2);for(let x=8;x<W;x+=24){g.fillStyle='#d6af7b';g.fillRect(x,y*H-1,2,2);}}\r\n   // Physical connected wires stay on the rail network. Each retains its own\r\n   // color; the current drag adds a free segment that stays under the finger.\r\n   for(const wire of state.wires){const path=pixelWireRoute(wire.from,wire.to);g.strokeStyle=color(wire.from);g.lineWidth=4;g.globalAlpha=.28;line(g,path,W,H);g.globalAlpha=1;g.lineWidth=2;line(g,path,W,H);}\r\n   if(drag&&drag.moved){const [x,y]=at(drag.from),tx=(drag.endX-rect.left)/rect.width,ty=(drag.endY-rect.top)/rect.height;g.strokeStyle=color(drag.from);g.lineWidth=3;g.globalAlpha=.9;line(g,[[x,y],[.5,y],[.5,clamp(ty)],[clamp(tx),clamp(ty)]],W,H);g.globalAlpha=1;g.fillStyle=color(drag.from);g.fillRect(tx*W-2,ty*H-2,5,5);}\r\n   for(const id of IDS){const [x,y]=at(id),on=state.powered.includes(id),c=color(id),px=Math.round(x*W),py=Math.round(y*H);\r\n    g.fillStyle='#080512';g.fillRect(px-5,py-5,11,11);g.fillStyle=on?c:'#706082';g.fillRect(px-3,py-3,7,7);g.fillStyle=on?'#fff0d5':'#4e395d';g.fillRect(px-1,py-1,3,3);\r\n    if(on){g.globalAlpha=.17+.06*Math.sin(t*4+IDS.indexOf(id));g.fillStyle=c;g.fillRect(px-8,py-8,17,17);g.globalAlpha=1;}\r\n   }\r\n   // Disco ball is a fixture with pixel mirror facets, not an automatic reward.\r\n   const [bx,by]=at('db'),cx=Math.round(bx*W),cy=Math.round(by*H);g.fillStyle='#160d29';g.fillRect(cx-8,cy-8,17,17);for(let y=-6;y<=6;y+=4)for(let x=-6;x<=6;x+=4){g.fillStyle=state.powered.includes('db')?COLORS[(x+y+28)/4%COLORS.length|0]:'#776284';g.fillRect(cx+x,cy+y,3,3);}\r\n   for(const id of ['tm','rm','bm','lm'])if(state.powered.includes(id)){const [x,y]=at(id),i=IDS.indexOf(id),col=color(id),target=[.5+(i%2?-.16:.16),.69];g.globalAlpha=.12;g.fillStyle=col;poly(g,[[x,y],[target[0]-.09,target[1]],[target[0]+.09,target[1]]],W,H);g.fill();g.globalAlpha=1;}\r\n   for(const id of ['tl','tr','br','bl'])if(state.powered.includes(id)){const start=at(id),ball=at('db');g.strokeStyle=color(id);g.globalAlpha=.23;g.lineWidth=5;line(g,[start,ball],W,H);g.globalAlpha=.85;g.lineWidth=1;line(g,[start,ball],W,H);g.fillStyle='#fff4cb';for(let k=0;k<8;k++){const a=(k/8+t*.55)%1;g.fillRect(((start[0]*(1-a)+ball[0]*a)*W)|0,((start[1]*(1-a)+ball[1]*a)*H)|0,2,2);}g.globalAlpha=1;}\r\n   if(ceremony&&p.name==='party')for(let i=0;i<14;i++){const id=IDS[i],[x,y]=at(id),u=.5+Math.sin(t*(1.3+i*.09)+i*2.3)*.42,v=.15+(.5+.5*Math.cos(t*(1.7+i*.07)+i))*.7,target=[floorX(u,v),floorY(v)];g.strokeStyle=color(id);g.globalAlpha=.12;g.lineWidth=6;line(g,[[x,y],target],W,H);g.globalAlpha=.65;g.lineWidth=1;line(g,[[x,y],target],W,H);g.fillStyle=color(id);g.fillRect(target[0]*W-2,target[1]*H-2,5,5);}g.globalAlpha=1;\r\n   if(ceremony&&p.name==='sparks'){for(let i=0;i<28;i++){const id=IDS[i%IDS.length],[x,y]=at(id),dx=Math.sin(t*19+i)*(.02+i%4*.01),dy=Math.cos(t*23+i)*.03;g.fillStyle=i%2?'#ffed84':'#71eaff';g.fillRect((x+dx)*W|0,(y+dy)*H|0,2,2);}}\r\n   if(ceremony&&p.name==='blackout'){g.fillStyle='#010104';g.fillRect(0,0,W,H);}\r\n  }\r\n  function draw(now){const begin=performance.now(),p=drawBase(now);drawRafters(now,p);if(hole){g.save();g.globalAlpha=1;g.globalCompositeOperation='destination-out';g.fillStyle='#000';poly(g,hole,W,H);g.fill();g.restore();}if(ceremony&&p.done&&!ceremony.sent){ceremony.sent=true;const preview=ceremony.preview;queueMicrotask(()=>{if(!disposed&&!paused)dispatchEvent(new CustomEvent(preview?'myr5:pixel-rehearsal-complete':'myr5:portal-secret',{detail:{board:'pixel'}}));});}frameCost=frameCost*.9+(performance.now()-begin)*.1;}\r\n  function tick(now){raf=0;if(disposed||paused)return;if(now-last>=1000/30){last=now;draw(now);}raf=requestAnimationFrame(tick);}\r\n  function wake(){if(!disposed&&!paused&&!raf)raf=requestAnimationFrame(tick);}\r\n  function heal(){state=pixelWireInit();selected=null;drag=null;ceremony=null;hole=null;updateCutMask();updateUI();wake();}\r\n  function startCeremony(preview){if(disposed||ceremony)return;ceremony={at:performance.now(),preview,sent:false};selected=null;drag=null;hole=null;updateCutMask();updateUI();wake();}\r\n  observer=new ResizeObserver(layout);observer.observe(host);layout();draw(performance.now());wake();\r\n  loadPixelParadeCoaches(document).then(value=>{if(!disposed){coaches=value;wake();}}).catch(()=>{});\r\n  const api={canvas:output,glContext:renderer?.getContext(),background:'#0c071b',ink:false,faceRect:()=>({...rect}),patternRect:()=>({left:rect.left+rect.width*.04,top:rect.top+rect.height*.51,width:rect.width*.92,height:rect.height*.44}),\r\n   press(){},release(){},cancel(){drag=null;selected=null;updateUI();},claims:()=>true,frameMs:()=>frameCost,setTint(){},\r\n   cut(poly,color,ms=867){hole=poly;updateCutMask();updateUI();wake();return new Promise(resolve=>setTimeout(resolve,reduced?0:ms));},heal,\r\n   pause(){if(paused)return;paused=true;cancelAnimationFrame(raf);raf=0;drag=null;ui.style.visibility='hidden';fx.style.visibility='hidden';},\r\n   resume(){if(disposed)return;const was=paused;paused=false;fx.style.visibility='';if(was&&ceremony)heal();layout();wake();},\r\n   dispose(){if(disposed)return;disposed=true;cancelAnimationFrame(raf);observer.disconnect();ui.remove();fx.remove();output.remove();disposeTree(scene);cutTexture.dispose();renderer?.dispose();renderer?.forceContextLoss();},\r\n   wire:connect,aim:tap,reset:heal,\r\n   snapshot:()=>({state:structuredClone(state),selected,drag:drag&&{from:drag.from,moved:drag.moved},coaches:coaches.length,phase:phase(performance.now()).name,flat}),\r\n   exportState:()=>({state:structuredClone(state),selected,hole,ceremony:ceremony&&{preview:ceremony.preview,sent:ceremony.sent,elapsed:performance.now()-ceremony.at}}),\r\n   restoreState(saved){if(!saved)return;state=structuredClone(saved.state||pixelWireInit());selected=saved.selected||null;hole=saved.hole||null;ceremony=saved.ceremony&&{...saved.ceremony,at:performance.now()-saved.ceremony.elapsed};updateCutMask();updateUI();wake();},\r\n   previewSecret(){startCeremony(true);},\r\n  };\r\n  return api;\r\n }catch(error){disposed=true;cancelAnimationFrame(raf);observer?.disconnect();ui?.remove();fx.remove();output?.remove();disposeTree(scene);cutTexture.dispose();renderer?.dispose();renderer?.forceContextLoss();throw error;}\r\n}\r\n","modules/portal/portal-board-pond.mjs":"// Pond grimoire (R21 L4) for portal-board-glb.mjs: a square, murky, brackish blue-green pond. The water is a\r\n// procedural plane (effect.build, no GLB) whose surface runs a CPU ripple height-field (the physics): it bends\r\n// the bed and the koi like the portal's liquid glass (GLASS bend + fringe + finger-lit rim, portal.mjs lensMap/\r\n// tunnelFragment), tilts the pads, and pushes the free ones. Small instanced low-poly 3D lily pads (with their\r\n// notch) and lily flowers float on top: anchor lilies rest on every vertex/base/apex/line end of every traceable\r\n// template (and along each template so the shapes read). All pads move aside around the finger and ripples, then\r\n// ease home; free ones also drift. Under the surface, blurred dark koi shadows wander (boids); a held finger slowly draws them\r\n// in, arriving face first under it; drawing leads them as a snaking school that grows as more join. 8 s with no\r\n// touch (idleMs 4 s wander + scatterMs 4 s): the fish scatter off the pond, one huge koi shadow drifts slowly across in a\r\n// random direction for bigMs 16 s, then they come back. SECRET (Achievement Vault): press and hold on the huge koi; it\r\n// pitches up toward the surface, grows and warms to koi colour over <= 2 s (its shadow shrinks on the bed), then bursts into\r\n// ripples + droplets and dispatches 'myr5:portal-secret' {board:'pond'}. Lift early and it gets bored: turns and swims off\r\n// the face (no award). pondSecret is the pure reducer. Reduced motion: still water, still fish. AGPL-3.0-or-later.\r\nimport {SHAPES,fromFrame} from './portal-shapes.mjs';\r\nimport {pointInPolygon} from './portal-cut.mjs';\r\n\r\n// Every tunable in one place. Lengths are in face widths (the pond's width = 1), speeds per second.\r\nexport const POND={\r\n aspect:16/9,          // face height / width (portal.mjs FACE.pond = 1/aspect)\r\n frame:{x0:.1,y0:.13,x1:.9,y1:.85}, // where the templates sit on the face (UV, v down)\r\n padR:.058,            // pad radius: ~100 pads side by side fill a phone screen\r\n anchorSpacing:.17,    // anchor lilies along each template, at most this far apart\r\n anchorMerge:.075,     // anchors closer than this are one lily (templates overlap a lot)\r\n anchorScale:.5,      // an along-the-line anchor pad vs a vertex one\r\n freePads:16,lilyShare:.35,padClear:2.1, // drifting pads, the share carrying a flower, min spacing (pad radii)\r\n drift:.014,driftHz:.045,                // free pads' slow wander about home\r\n spring:2.6,damping:2.2,pushR:.17,pushK:1.6,waveK:1.5,tilt:2.5, // pad physics: back home, finger push, ripple push, ripple tilt\r\n fish:14,koiLen:.24,koiAlpha:.72,swimHz:1.1,bend:.5, // koi shadows: count, length, darkness, tail beat, body undulation\r\n wanderSpeed:.06,attractSpeed:.17,schoolSpeed:.6,scatterSpeed:.4,maxForce:.9,arriveR:.14,\r\n jitter:2.4,margin:.12,sepR:.07,sepK:.6,\r\n attractR:.14,joinR:.085,leaveR:.22,gap:.075,trailStep:.012,trailCap:160, // stronger nearby attraction and a more persistent following school\r\n releaseMs:850,releaseSpeed:.52, // a short outward swim when the finger lifts\r\n idleMs:4000,scatterMs:4000,bigMs:16000,bigLen:1.3,bigAlpha:.62, // big fish starts at eight seconds total\r\n wave:{cols:64,damping:.982,press:.9,drag:.35,hold:.05,holdHz:1.6,gain:90}, // ripple height-field\r\n glass:{bend:.12,fringe:.3,rim:.55},     // refraction (face widths at full slope), dispersion, finger-lit rim glint\r\n lily:'#ffd3e4',lilyCore:'#ffcc33',shadow:[.01,.035,.03],\r\n};\r\n\r\n// --- Pure helpers (no THREE, no DOM) ------------------------------------------------------------\r\n// Seedable PRNG (mulberry32): the layout is the same every visit, and tests can drive the fish.\r\nexport function rng(seed=1){let a=seed>>>0;return()=>{a=a+0x6D2B79F5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};}\r\nconst TAU=Math.PI*2;\r\n// Secret reducer. s={st:'idle'|'hold'|'done'|'bored',id,t0,x0,y0}. ev (pond space): {type:'down',id,x,y,phase,big:{x,y}},\r\n// {type:'move',id,x,y}, {type:'up',id}, {type:'tick'}, {type:'reset'}. Held >= riseMs => done; early lift or > drift => bored.\r\nexport const SECRET={riseMs:2000,drift:.07,hit:.35}; // drift 24 px ~ .07 face widths on a 345 px face\r\nexport function pondSecret(s,ev,now,K=POND,C=SECRET){\r\n s=s||{st:'idle',id:null};\r\n if(ev.type==='reset')return {st:'idle',id:null};\r\n if(ev.type==='down')return s.st==='idle'&&ev.phase==='big'&&Math.hypot(ev.x-ev.big.x,ev.y-ev.big.y)<=K.bigLen*C.hit?{st:'hold',id:ev.id,t0:now,x0:ev.x,y0:ev.y}:s;\r\n if(s.st!=='hold')return ev.type==='up'&&ev.id===s.id?{...s,id:null}:s;\r\n if(ev.type==='tick')return now-s.t0>=C.riseMs?{...s,st:'done'}:s;\r\n if(ev.id!==s.id)return s;\r\n if(ev.type==='move')return Math.hypot(ev.x-s.x0,ev.y-s.y0)>C.drift?{...s,st:'bored'}:s;\r\n return {...s,st:now-s.t0>=C.riseMs?'done':'bored',id:null}; // up\r\n}\r\n// Template point -> pond space (x right, y down, both in face widths: y = v * aspect).\r\nconst toPond=(tx,ty,frame,A)=>{const [u,v]=fromFrame(tx,ty,frame);return [u,v*A];};\r\nexport const pondPoint=(u,v,A=POND.aspect)=>({x:u,y:v*A});\r\n// Stretch world Y after rotation so rendered headings follow stretched paths.\r\nexport function pondStretchMatrix(matrix,ratio){for(const i of [1,5,9])matrix.elements[i]*=ratio;return matrix;}\r\nexport function pondScreenTransform(toWorld,A=POND.aspect){\r\n const [ox,oy]=toWorld(0,0),[xx]=toWorld(1,0),[,yy]=toWorld(0,1),sx=xx-ox,sy=(oy-yy)/A;\r\n return {sx,sy,point:(x,y)=>[ox+x*sx,oy-y*sy]};\r\n}\r\n// Every vertex / base / apex / line end of every template: the oval's four extremes, every other polyline's points.\r\nexport function templateVertices(){\r\n const out=[];\r\n for(const polys of Object.values(SHAPES))for(const {points} of polys){\r\n  if(points.length>40){for(const k of [0,.25,.5,.75])out.push(points[Math.round(k*points.length)%points.length]);}\r\n  else for(const p of points)out.push(p);\r\n }\r\n return out;\r\n}\r\n// Anchor lilies [{x,y,vertex}] in pond space: a lily on every template vertex (merged when near-duplicate), then pads\r\n// along every template, spaced at most `spacing`, skipping any spot within `merge` of an anchor already placed.\r\nexport function anchorLilies(frame=POND.frame,A=POND.aspect,{spacing=POND.anchorSpacing,merge=POND.anchorMerge}={}){\r\n const out=[],near=(x,y)=>out.some(p=>Math.hypot(p.x-x,p.y-y)<merge),add=(x,y,vertex)=>{if(!near(x,y))out.push({x,y,vertex});};\r\n for(const [tx,ty] of templateVertices())add(...toPond(tx,ty,frame,A),true);\r\n for(const polys of Object.values(SHAPES))for(const {points} of polys){\r\n  const pts=points.map(([tx,ty])=>toPond(tx,ty,frame,A));if(points.length>40)pts.push(pts[0]); // close the oval\r\n  if(points.length>40){ // smooth: walk the arc length\r\n   let run=0;for(let i=1;i<pts.length;i++){const d=Math.hypot(pts[i][0]-pts[i-1][0],pts[i][1]-pts[i-1][1]);run+=d;if(run>=spacing){add(pts[i][0],pts[i][1],false);run=0;}}\r\n  }else for(let i=1;i<pts.length;i++){ // straight: even steps along each side\r\n   const [ax,ay]=pts[i-1],[bx,by]=pts[i],n=Math.ceil(Math.hypot(bx-ax,by-ay)/spacing);\r\n   for(let k=1;k<n;k++)add(ax+(bx-ax)*k/n,ay+(by-ay)*k/n,false);\r\n  }\r\n }\r\n return out;\r\n}\r\n// Free pads scattered between the anchors (rejection sampling, seeded): [{x,y}] in pond space.\r\nexport function freePads(anchors,A=POND.aspect,rand=rng(7),K=POND){\r\n const out=[],r=K.padR,min=K.padClear*r,all=[...anchors];\r\n for(let t=0;t<4000&&out.length<K.freePads;t++){\r\n  const x=r+rand()*(1-2*r),y=r+rand()*(A-2*r);\r\n  if(all.some(p=>Math.hypot(p.x-x,p.y-y)<min*(p.vertex===false?.8:1)))continue;\r\n  const p={x,y};out.push(p);all.push(p);\r\n }\r\n return out;\r\n}\r\n// Idle show by ms since the last touch: 'wander' for idleMs, then 'scatter' (fish leave), then 'big' (the huge koi\r\n// crosses), then round again (the fish return during the next wander). cycle counts shows, so each crossing differs.\r\nexport function idlePhase(ms,K=POND){\r\n const P=K.idleMs+K.scatterMs+K.bigMs,cycle=Math.floor(Math.max(0,ms)/P),m=Math.max(0,ms)-cycle*P;\r\n if(m<K.idleMs)return {phase:'wander',t:m,cycle};\r\n if(m<K.idleMs+K.scatterMs)return {phase:'scatter',t:m-K.idleMs,cycle};\r\n return {phase:'big',t:m-K.idleMs-K.scatterMs,cycle};\r\n}\r\n// The huge koi's pose t01 of the way across (pond space): a random direction per show, a line through near the\r\n// centre, starting and ending fully off the pond.\r\nexport function bigFishPose(seed,t01,A=POND.aspect,len=POND.bigLen,out={x:0,y:0,a:0}){\r\n const r=rng(seed*7919+13),a=r()*TAU,off=(r()-.5)*.5,dx=Math.cos(a),dy=Math.sin(a),R=Math.hypot(.5,A/2)+len*.6;\r\n const cx=.5-dy*off,cy=A/2+dx*off;out.x=cx+dx*R*(2*t01-1);out.y=cy+dy*R*(2*t01-1);out.a=a;return out;\r\n}\r\n// Steer (Reynolds seek with arrival) f={x,y,vx,vy} toward (tx,ty): desired speed max, slowing inside arrive; the\r\n// change of velocity is capped at force per second. Integrates position. Returns the distance left.\r\nexport function steer(f,tx,ty,max,force,arrive,dt){\r\n const dx=tx-f.x,dy=ty-f.y,d=Math.hypot(dx,dy)||1e-9,sp=d<arrive?max*d/arrive:max;\r\n let ax=dx/d*sp-f.vx,ay=dy/d*sp-f.vy;const a=Math.hypot(ax,ay),cap=force*dt;if(a>cap){ax*=cap/a;ay*=cap/a;}\r\n f.vx+=ax;f.vy+=ay;f.x+=f.vx*dt;f.y+=f.vy*dt;return d;\r\n}\r\n// The finger's trail as a ring buffer in pond space; pointAt walks back `dist` along it from the newest point.\r\nexport function makeTrail(cap=POND.trailCap){return {x:new Float32Array(cap),y:new Float32Array(cap),n:0,head:-1,cap};}\r\nexport function trailPush(T,x,y,step=POND.trailStep){\r\n if(T.n&&Math.hypot(x-T.x[T.head],y-T.y[T.head])<step)return false;\r\n T.head=(T.head+1)%T.cap;T.x[T.head]=x;T.y[T.head]=y;T.n=Math.min(T.cap,T.n+1);return true;\r\n}\r\nexport function trailAt(T,dist,out){\r\n let i=T.head,x=T.x[i],y=T.y[i],left=dist;\r\n for(let k=1;k<T.n;k++){const j=(T.head-k+T.cap)%T.cap,d=Math.hypot(T.x[j]-x,T.y[j]-y);if(d>=left){const s=left/d;out.x=x+(T.x[j]-x)*s;out.y=y+(T.y[j]-y)*s;return out;}left-=d;x=T.x[j];y=T.y[j];}\r\n out.x=x;out.y=y;return out;\r\n}\r\n// The school: koi shadows in pond space with their swim state.\r\nexport function makeFish(n=POND.fish,A=POND.aspect,rand=Math.random){\r\n return Array.from({length:n},()=>({x:.15+.7*rand(),y:A*(.1+.8*rand()),vx:0,vy:0,a:rand()*TAU,w:rand()*TAU,phase:rand()*TAU,member:-1}));\r\n}\r\nconst P0={x:0,y:0};\r\n// One step of every fish. S={fish,A,rand,touch:{x,y}|null,lastTouch(ms),trail,members}. Sets S.phase.\r\nexport function stepFish(S,dt,now,K=POND){\r\n const {fish,A,touch}=S,idle=touch?null:idlePhase(now-S.lastTouch,K),away=idle&&idle.phase!=='wander',releasing=!touch&&now<(S.releaseUntil||0);\r\n S.phase=touch?'touch':releasing?'release':idle.phase;\r\n for(const f of fish){\r\n  let tx,ty,max,arrive=K.arriveR,face=false;\r\n  if(touch){\r\n   if(f.member>=0){trailAt(S.trail,f.member*K.gap,P0);if(Math.hypot(f.x-P0.x,f.y-P0.y)>K.leaveR)f.member=-1;}\r\n   const fingerDist=Math.hypot(f.x-touch.x,f.y-touch.y);\r\n   if(f.member<0&&fingerDist<K.joinR)f.member=S.members++;\r\n   if(f.member>=0){trailAt(S.trail,f.member*K.gap,P0);tx=P0.x;ty=P0.y;max=K.schoolSpeed;arrive=K.arriveR*.6;}\r\n   else if(fingerDist<K.attractR){tx=touch.x;ty=touch.y;max=K.attractSpeed;}\r\n   if(tx!==undefined)face=Math.hypot(tx-f.x,ty-f.y)<arrive;\r\n  }else if(releasing&&f.release){ // only fish that noticed the finger fan out\r\n   const dx=f.x-S.releaseX,dy=f.y-S.releaseY,d=Math.hypot(dx,dy)||1;\r\n   tx=f.x+(dx||Math.cos(f.a)*.01)/d;ty=f.y+(dy||Math.sin(f.a)*.01)/d;max=K.releaseSpeed;arrive=.01;\r\n  }else if(away){ // swim straight out, away from the middle\r\n   let ox=f.x-.5,oy=f.y-A/2;const o=Math.hypot(ox,oy)||1,R=Math.hypot(.5,A/2)+.25;ox/=o;oy/=o;tx=.5+ox*R;ty=A/2+oy*R;max=K.scatterSpeed;arrive=.01;\r\n  }\r\n  if(tx===undefined){ // wander: a jittered heading, turned back toward the pond when near or past its edge\r\n   f.w+=(S.rand()-.5)*K.jitter*dt*4;\r\n   const m=K.margin,out=f.x<m||f.x>1-m||f.y<m||f.y>A-m;\r\n   if(out){tx=.5+(f.x<.5?-.1:.1);ty=A/2;max=K.wanderSpeed*(f.x<-.05||f.x>1.05||f.y<-.05||f.y>A+.05?2.5:1.2);}\r\n   else{const h=Math.atan2(f.vy,f.vx)||f.a;tx=f.x+Math.cos(h)*.25+Math.cos(f.w)*.15;ty=f.y+Math.sin(h)*.25+Math.sin(f.w)*.15;max=K.wanderSpeed;}\r\n   arrive=.01;\r\n  }\r\n  // separation: koi keep a body's width apart (all n², n is small)\r\n  for(const g of fish)if(g!==f){const dx=f.x-g.x,dy=f.y-g.y,d=Math.hypot(dx,dy);if(d>1e-6&&d<K.sepR){const k=K.sepK*(1-d/K.sepR)*dt;f.vx+=dx/d*k;f.vy+=dy/d*k;}}\r\n  steer(f,tx,ty,max,K.maxForce,arrive,dt);\r\n  // heading follows the velocity; arrived under the finger, it turns to face the finger (face first)\r\n  const sp=Math.hypot(f.vx,f.vy),want=face?Math.atan2(touch.y-f.y,touch.x-f.x):sp>.004?Math.atan2(f.vy,f.vx):f.a;\r\n  let da=want-f.a;da-=TAU*Math.round(da/TAU);f.a+=da*Math.min(1,dt*(face?2.5:4));\r\n  f.phase+=dt*TAU*K.swimHz*(.5+Math.min(2,sp/K.wanderSpeed)*.5);\r\n }\r\n if(touch){let n=0;for(const f of fish.filter(f=>f.member>=0).sort((a,b)=>a.member-b.member))f.member=n++;S.members=n;}\r\n}\r\n// Pointer state for stepFish: the newest finger leads.\r\nexport function fishTouch(S,x,y,now){if(!S.touch){S.trail.n=0;S.trail.head=-1;S.members=0;for(const f of S.fish){f.member=-1;f.release=false;}}S.releaseUntil=0;S.touch=S.touch||{x,y};S.touch.x=x;S.touch.y=y;trailPush(S.trail,x,y);S.lastTouch=now;}\r\nexport function fishRelease(S,now){if(S.touch){S.releaseX=S.touch.x;S.releaseY=S.touch.y;S.releaseUntil=now+POND.releaseMs;}S.touch=null;S.members=0;for(const f of S.fish){f.release=f.member>=0||Math.hypot(f.x-S.releaseX,f.y-S.releaseY)<POND.attractR;f.member=-1;}S.lastTouch=now;}\r\n\r\n// --- Ripple height-field (classic two-buffer wave), pure typed arrays ---------------------------------------\r\nexport function makeWaves(cols,rows){return {cols,rows,cur:new Float32Array(cols*rows),prev:new Float32Array(cols*rows),energy:0};}\r\nexport function disturb(W,x,y,amp){ // x,y in 0..1 face UV\r\n const cx=Math.round(x*(W.cols-1)),cy=Math.round(y*(W.rows-1));\r\n for(let j=-2;j<=2;j++)for(let i=-2;i<=2;i++){const X=cx+i,Y=cy+j;if(X<1||Y<1||X>=W.cols-1||Y>=W.rows-1)continue;W.cur[Y*W.cols+X]-=amp*Math.exp(-(i*i+j*j)/2);}\r\n W.energy=Math.max(W.energy,Math.abs(amp));\r\n}\r\nexport function stepWaves(W,damping){\r\n const {cols,rows}=W;let {cur,prev}=W,e=0;\r\n for(let y=1;y<rows-1;y++)for(let x=1,i=y*cols+1;x<cols-1;x++,i++){const v=((cur[i-1]+cur[i+1]+cur[i-cols]+cur[i+cols])*.5-prev[i])*damping;prev[i]=v;const a=v<0?-v:v;if(a>e)e=a;}\r\n W.prev=cur;W.cur=prev;W.energy=e;return e;\r\n}\r\n// Surface slope at x,y (face UV) in height per cell: [dh/du, dh/dv] (into out).\r\nexport function slopeAt(W,x,y,out){const X=Math.min(W.cols-2,Math.max(1,Math.round(x*(W.cols-1)))),Y=Math.min(W.rows-2,Math.max(1,Math.round(y*(W.rows-1)))),i=Y*W.cols+X,c=W.cur;out[0]=(c[i+1]-c[i-1])*.5;out[1]=(c[i+W.cols]-c[i-W.cols])*.5;return out;}\r\n\r\n// --- Shaders --------------------------------------------------------------------------------------------\r\nconst F=x=>(+x).toFixed(4),V3=a=>`vec3(${a.map(F).join(',')})`,G=POND.glass;\r\n// Murky bed: deep blue-green, brackish olive silt, darker in the deep middle; a dark mossy stone lip makes it a square pond.\r\nconst WATER_DECLS=`uniform sampler2D uWave;uniform vec4 uFinger;\r\nfloat pH(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nfloat pN(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.0-2.0*f);return mix(mix(pH(i),pH(i+vec2(1.0,0.0)),f.x),mix(pH(i+vec2(0.0,1.0)),pH(i+1.0),f.x),f.y);}\r\nvec3 pondBed(vec2 p,float asp){\r\n float n=pN(p*5.0+vec2(uTime*.02,0.0))*.62+pN(p*13.0-vec2(0.0,uTime*.035))*.38;\r\n vec3 c=mix(vec3(.008,.040,.062),vec3(.040,.135,.150),n);\r\n c=mix(c,vec3(.090,.130,.075),smoothstep(.62,.92,n)*.45);\r\n float deep=1.0-smoothstep(.0,.55,length((p-vec2(.5,asp*.5))*vec2(1.0,.62)));\r\n return c*(1.0-.45*deep);\r\n}\r\n`;\r\nconst WATER_FRAGMENT=`{\r\n float asp=uFaceSize.y/uFaceSize.x;vec2 P=vec2(vPlanar.x,vPlanar.y*asp);\r\n vec4 wv=texture2D(uWave,vPlanar);vec2 g=(wv.rg-0.5)*2.0;float slope=length(g);\r\n vec2 off=g*${F(G.bend)};\r\n vec3 col=slope>.02?vec3(pondBed(P+off*${F(1-G.fringe)},asp).r,pondBed(P+off,asp).g,pondBed(P+off*${F(1+G.fringe)},asp).b):pondBed(P+off,asp);\r\n vec2 L=normalize(uFinger.xy*vec2(1.0,asp)-P+vec2(1e-4));float lit=max(dot(g/max(slope,1e-4),L),0.0);\r\n totalEmissiveRadiance+=vec3(.75,.95,.9)*${F(G.rim)}*pow(lit,1.4)*smoothstep(.04,.5,slope)*(.35+.65*uFinger.z);\r\n totalEmissiveRadiance+=vec3(.6,.8,.75)*.05*smoothstep(.15,.6,slope);\r\n float e=min(min(vPlanar.x,1.0-vPlanar.x),min(vPlanar.y,1.0-vPlanar.y)*asp),lip=1.0-smoothstep(.018,.024,e);\r\n col*=.55+.45*smoothstep(.02,.09,e);\r\n vec3 stone=mix(vec3(.10,.115,.09),vec3(.20,.21,.16),pN(P*40.0))*(.75+.5*smoothstep(.0,.02,e));\r\n diffuseColor.rgb=mix(col,stone,lip);roughnessFactor=mix(.16,.85,lip);metalnessFactor=0.0;\r\n normal=normalize(normal+vec3(-g.x,g.y,0.0)*1.6*(1.0-lip));\r\n vec4 pg=texture2D(uPaint,vPlanar);totalEmissiveRadiance+=pg.rgb*pg.a*.8*(1.0-lip);\r\n}`;\r\n// Koi shadows: a segmented strip (head at +x) whose tail sways (phase travels head->tail), shifted by the same water\r\n// slope as the bed (the refraction), drawn as a dark blurred silhouette under the pads.\r\nconst FISH_VS=`attribute vec4 aFish;uniform sampler2D uWave;uniform vec4 uFace;uniform float uRefract;varying vec2 vUv;varying float vA;varying float vC;\r\nvoid main(){\r\n vec3 p=position;float t=0.5-p.x;\r\n p.y+=sin(aFish.x-t*4.5)*aFish.z*t*t*.32;p.x+=(cos(aFish.x-t*4.5)-1.0)*aFish.z*t*.03;\r\n vec4 w=instanceMatrix*vec4(p,1.0);\r\n vec2 uv=vec2((w.x-uFace.x)/uFace.z,(-w.y-uFace.y)/uFace.w);\r\n vec2 g=(texture2D(uWave,clamp(uv,0.0,1.0)).rg-0.5)*2.0;w.xy+=vec2(g.x,-g.y)*uRefract;\r\n vA=aFish.y;vC=aFish.w;vUv=vec2(position.x+0.5,position.y/.5+0.5);\r\n gl_Position=projectionMatrix*modelViewMatrix*w;\r\n}`;\r\nconst FISH_FS=`uniform sampler2D uKoi;uniform vec3 uShadow;uniform float uFade;varying vec2 vUv;varying float vA;varying float vC;\r\nvoid main(){float t=texture2D(uKoi,vUv).a,a=mix(t,smoothstep(.14,.42,t),vC)*vA*uFade;if(a<.004)discard;\r\n float p=sin(vUv.x*11.0+1.0)*.5+sin(vUv.x*4.0+vUv.y*9.0)*.5; // the risen koi: crisp edge, orange with cream patches\r\n vec3 koi=mix(vec3(.95,.42,.12),vec3(.98,.92,.82),smoothstep(.15,.4,p));\r\n koi*=.7+.3*(1.0-abs(vUv.y-.5)*1.8);koi=mix(koi,vec3(.04,.03,.03),max(smoothstep(.03,.015,length(vec2(vUv.x-.8,abs(vUv.y-.5)-.1))),0.0)*step(.6,vC));\r\n gl_FragColor=vec4(mix(uShadow,koi,vC),a);}`;\r\n\r\n// --- Procedural low-poly meshes --------------------------------------------------------------------------\r\nfunction padGeometry(THREE,rand){\r\n const seg=13,notch=.55,pos=[0,0,.05],idx=[];\r\n for(let i=0;i<=seg;i++){const a=notch/2+(TAU-notch)*i/seg;\r\n  pos.push(Math.cos(a)*.55,Math.sin(a)*.55,.03+rand()*.04);\r\n  pos.push(Math.cos(a)*(.97+rand()*.06),Math.sin(a)*(.97+rand()*.06),.1+rand()*.08);}\r\n for(let i=0;i<seg;i++){const a=1+2*i,b=a+1,c=a+2,d=a+3;idx.push(0,a,c, a,b,d, a,d,c);}\r\n idx.push(0,1,2);idx.push(0,2*seg+2,2*seg+1); // the notch's two cut edges stay closed to the centre\r\n const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));g.setIndex(idx);\r\n const flat=g.toNonIndexed();flat.computeVertexNormals();g.dispose();return flat;\r\n}\r\nfunction lilyGeometry(THREE){\r\n const pos=[],col=[],tri=(a,b,c,ca,cb,cc)=>{pos.push(...a,...b,...c);col.push(...ca,...cb,...cc);};\r\n const base=[1,.82,.88],tip=[1,1,1],mid=[1,.93,.95];\r\n for(const L of [{n:8,len:1,lift:.45,w:.36,rot:0},{n:6,len:.72,lift:.8,w:.34,rot:.4}]){\r\n  for(let i=0;i<L.n;i++){\r\n   const a=L.rot+TAU*i/L.n,c=Math.cos(a),s=Math.sin(a),at=(r,da,z)=>[Math.cos(a+da)*r,Math.sin(a+da)*r,z];\r\n   const o=[c*.06,s*.06,.1],t=[c*L.len,s*L.len,.1+L.lift*L.len],l=at(L.len*.5,L.w,.1+L.lift*L.len*.35),r=at(L.len*.5,-L.w,.1+L.lift*L.len*.35),m=at(L.len*.55,0,.06+L.lift*L.len*.42);\r\n   tri(o,r,m,base,mid,mid);tri(o,m,l,base,mid,mid);tri(r,t,m,mid,tip,mid);tri(m,t,l,mid,tip,mid);\r\n  }\r\n }\r\n const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));g.setAttribute('color',new THREE.Float32BufferAttribute(col,3));g.computeVertexNormals();return g;\r\n}\r\nfunction coreGeometry(THREE){const g=new THREE.ConeGeometry(.2,.32,6,1);g.rotateX(Math.PI/2);g.translate(0,0,.32);return g;}\r\n// The koi silhouette (alpha only, head right), blurred by the shadow trick (works where ctx.filter doesn't).\r\nfunction koiTexture(THREE){\r\n const W=256,H=128,c=document.createElement('canvas');c.width=W;c.height=H;const x=c.getContext('2d');\r\n x.translate(-W*2,0);x.shadowOffsetX=W*2;x.shadowColor='#000';x.shadowBlur=9;x.fillStyle='#000';\r\n x.beginPath();x.ellipse(150,64,74,20,0,0,TAU);x.fill(); // body\r\n x.beginPath();x.ellipse(205,64,22,15,0,0,TAU);x.fill(); // head\r\n x.beginPath();x.moveTo(84,64);x.quadraticCurveTo(48,52,22,30);x.quadraticCurveTo(40,64,22,98);x.quadraticCurveTo(48,76,84,64);x.fill(); // tail\r\n for(const s of [-1,1]){x.beginPath();x.moveTo(172,64+s*14);x.quadraticCurveTo(160,64+s*44,138,64+s*42);x.quadraticCurveTo(150,64+s*26,160,64+s*12);x.fill();} // pectoral fins\r\n for(const s of [-1,1]){x.beginPath();x.moveTo(108,64+s*12);x.quadraticCurveTo(98,64+s*30,86,64+s*28);x.quadraticCurveTo(96,64+s*18,100,64+s*8);x.fill();} // pelvic fins\r\n const t=new THREE.CanvasTexture(c);t.colorSpace=THREE.NoColorSpace;return t;\r\n}\r\n\r\n// --- The effect -------------------------------------------------------------------------------------------\r\nlet S=null; // one portal board at a time\r\nconst DROPS=72;\r\nconst reducedMotion=()=>typeof matchMedia==='function'&&matchMedia('(prefers-reduced-motion: reduce)').matches;\r\nconst validTint=hex=>typeof hex==='string'&&/^#[0-9a-f]{6}$/i.test(hex);\r\n\r\nfunction dropTexture(THREE){const c=document.createElement('canvas');c.width=c.height=32;const x=c.getContext('2d'),g=x.createRadialGradient(16,16,2,16,16,15);g.addColorStop(0,'#fff');g.addColorStop(.6,'rgba(255,255,255,.85)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,32,32);return new THREE.CanvasTexture(c);}\r\nfunction build(THREE){\r\n const A=POND.aspect,geo=new THREE.PlaneGeometry(1,A,16,28);\r\n return new THREE.Mesh(geo,new THREE.MeshStandardMaterial({color:0x0b2626,roughness:.2,metalness:0}));\r\n}\r\n\r\nfunction init({THREE,scene,mesh,uniforms,toWorld,wake}){\r\n const K=POND,A=K.aspect,rand=rng(21),reduced=reducedMotion();\r\n // Water: the ripple field, packed as slope (rg) + height (b) into a small texture the water and the koi both read.\r\n const cols=K.wave.cols,rows=Math.round(cols*A),waves=makeWaves(cols,rows),data=new Uint8Array(cols*rows*4).fill(128);\r\n const waveTex=new THREE.DataTexture(data,cols,rows,THREE.RGBAFormat,THREE.UnsignedByteType);\r\n waveTex.magFilter=waveTex.minFilter=THREE.LinearFilter;waveTex.needsUpdate=true;\r\n uniforms.uWave.value=waveTex;uniforms.uFinger.value=new THREE.Vector4(.5,.3,0,0);\r\n mesh.material&&(mesh.material.roughness=.2);\r\n\r\n // Pads and lilies: shape anchors first, then the freely drifting ones.\r\n const anchors=anchorLilies(K.frame,A),free=freePads(anchors,A,rng(7),K),pads=[...anchors.map(p=>({...p,anchor:true})),...free.map(p=>({...p,anchor:false,vertex:false}))];\r\n const lr=rng(11);\r\n for(const p of pads){p.hx=p.x;p.hy=p.y;p.vx=p.vy=0;p.rot=lr()*TAU;p.s=(p.anchor&&!p.vertex?K.anchorScale:.85+lr()*.3);p.seed=lr()*TAU;p.lily=p.vertex||(!p.anchor&&lr()<K.lilyShare);p.hidden=false;}\r\n const lilies=pads.filter(p=>p.lily);\r\n const padMat=new THREE.MeshStandardMaterial({flatShading:true,roughness:.62,metalness:0});\r\n const padMesh=new THREE.InstancedMesh(padGeometry(THREE,rng(3)),padMat,pads.length);\r\n const green=new THREE.Color();pads.forEach((p,i)=>{green.setHSL(.27+lr()*.06,.42+lr()*.15,.2+lr()*.08);padMesh.setColorAt(i,green);});\r\n const lilyMat=new THREE.MeshStandardMaterial({vertexColors:true,flatShading:true,roughness:.5,side:THREE.DoubleSide,emissive:0x2a1c22});\r\n const lilyMesh=new THREE.InstancedMesh(lilyGeometry(THREE),lilyMat,lilies.length);\r\n const coreMesh=new THREE.InstancedMesh(coreGeometry(THREE),new THREE.MeshStandardMaterial({color:K.lilyCore,flatShading:true,roughness:.5,emissive:0x332200}),lilies.length);\r\n for(const m of [padMesh,lilyMesh,coreMesh]){m.frustumCulled=false;m.instanceMatrix.setUsage(THREE.DynamicDrawUsage);scene.add(m);}\r\n\r\n // Koi shadows (+2: the huge one and its bed shadow while it rises), one instanced strip.\r\n const n=K.fish,fishGeo=new THREE.PlaneGeometry(1,.5,12,1),aFish=new THREE.InstancedBufferAttribute(new Float32Array((n+2)*4),4);\r\n aFish.setUsage(THREE.DynamicDrawUsage);fishGeo.setAttribute('aFish',aFish);\r\n const koi=koiTexture(THREE),fishMat=new THREE.ShaderMaterial({vertexShader:FISH_VS,fragmentShader:FISH_FS,transparent:true,depthWrite:false,\r\n  uniforms:{uKoi:{value:koi},uWave:uniforms.uWave,uFace:{value:new THREE.Vector4(0,0,1,1)},uRefract:{value:0},uShadow:{value:new THREE.Vector3(...K.shadow)},uFade:{value:1}}});\r\n const fishMesh=new THREE.InstancedMesh(fishGeo,fishMat,n+2);fishMesh.frustumCulled=false;fishMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);fishMesh.renderOrder=1;scene.add(fishMesh);\r\n\r\n const dropPos=new Float32Array(DROPS*3),dropGeo=new THREE.BufferGeometry();dropGeo.setAttribute('position',new THREE.BufferAttribute(dropPos,3));\r\n const dropTex=dropTexture(THREE),dropMat=new THREE.PointsMaterial({color:0xe8fbff,map:dropTex,size:15,sizeAttenuation:false,transparent:true,depthWrite:false}),dropMesh=new THREE.Points(dropGeo,dropMat);dropMesh.frustumCulled=false;dropMesh.visible=false;dropMesh.renderOrder=3;scene.add(dropMesh);\r\n const foam=new THREE.Mesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({map:dropTex,transparent:true,depthWrite:false,opacity:0}));foam.visible=false;foam.renderOrder=4;scene.add(foam);\r\n const fishS={fish:makeFish(n,A,rng(5)),A,rand:rng(9),touch:null,lastTouch:performance.now(),trail:makeTrail(K.trailCap),members:0,phase:'wander'};\r\n S={foam,dropTex,sec:{st:'idle',id:null},an:null,drops:[],dropPos,dropGeo,dropMat,dropMesh,THREE,K,A,toWorld,wake,uniforms,reduced,waves,waveTex,data,pads,lilies,padMesh,lilyMesh,coreMesh,fishMesh,fishMat,aFish,koi,fishS,\r\n  pointers:new Map(),lilyColor:new THREE.Color(K.lily),m:new THREE.Matrix4(),q:new THREE.Quaternion(),e:new THREE.Euler(),v:new THREE.Vector3(),sc:new THREE.Vector3(),\r\n  born:performance.now(),lead:null,slope:[0,0],big:{x:0,y:0,a:0},bigSeed:Math.floor(Math.random()*1e6),fade:1,fadeTo:1,cutPoly:null,lastNow:performance.now(),dirty:true};\r\n setLilyColor(K.lily);\r\n place(0,performance.now());\r\n}\r\n\r\nfunction setLilyColor(hex){if(!S)return;S.lilyColor.set(hex);for(let i=0;i<S.lilies.length;i++)S.lilyMesh.setColorAt(i,S.lilyColor);if(S.lilyMesh.instanceColor)S.lilyMesh.instanceColor.needsUpdate=true;S.wake?.();}\r\nlet selectedTint=null;\r\nfunction setTint(hex,selected=true){if(selected&&!validTint(hex))return;selectedTint=selected?hex:null;setLilyColor(selectedTint||POND.lily);}\r\n\r\n// Writes every instance's matrix: pads ride the ripples (tilt), koi sway, the huge koi crosses.\r\nfunction place(dt,now){\r\n const {K,A,toWorld,pads,lilies,padMesh,lilyMesh,coreMesh,fishMesh,aFish,m,q,e,v,sc,slope,waves}=S;\r\n const {sx:px,sy:py,point}=pondScreenTransform(toWorld,A);\r\n const put=(x,y,z,rx,ry,rz,s)=>{e.set(rx,ry,rz);q.setFromEuler(e);const [wx,wy]=point(x,y);v.set(wx,wy,z);sc.set(s,s,s);m.compose(v,q,sc);return pondStretchMatrix(m,py/px);};\r\n let li=0;\r\n pads.forEach((p,i)=>{\r\n  slopeAt(waves,p.x,p.y/A,slope);const tx=-slope[1]*K.tilt,ty=slope[0]*K.tilt,r=K.padR*px*p.s*(p.hidden?0:1);\r\n  padMesh.setMatrixAt(i,put(p.x,p.y,2+i*.03,tx,ty,p.rot,r));\r\n  if(p.lily){lilyMesh.setMatrixAt(li,put(p.x,p.y,2.4+i*.03,tx,ty,p.rot+p.seed,r*.6));coreMesh.setMatrixAt(li,m);li++;}\r\n });\r\n padMesh.instanceMatrix.needsUpdate=lilyMesh.instanceMatrix.needsUpdate=coreMesh.instanceMatrix.needsUpdate=true;\r\n const fish=S.fishS.fish,ph=S.fishS.phase,arr=aFish.array,bend=S.reduced?0:K.bend;if(ph==='big'&&!S.bigSent){S.bigSent=1;dispatchEvent(new CustomEvent('myr5:vault-bump',{detail:{counter:'pond-fish'}}));}\r\n fish.forEach((f,i)=>{fishMesh.setMatrixAt(i,put(f.x,f.y,.6+i*.01,0,0,-f.a,K.koiLen*px));arr[4*i]=f.phase;arr[4*i+1]=K.koiAlpha;arr[4*i+2]=bend;});\r\n // The huge koi: only while the fish are away.\r\n const n=fish.length,idle=ph==='big'&&S.sec.st==='idle'?idlePhase(now-S.fishS.lastTouch,K):null,b=S.an;\r\n arr[4*n+3]=arr[4*n+7]=0;arr[4*n+5]=0;\r\n if(b){\r\n  e.set(0,-b.pitch,-b.a,'ZYX');q.setFromEuler(e);e.order='XYZ';const [wx,wy]=point(b.x,b.y);v.set(wx,wy,b.z*px+.4);sc.set(b.s*px,b.s*px,b.s*px);m.compose(v,q,sc);pondStretchMatrix(m,py/px);fishMesh.setMatrixAt(n,m);\r\n  arr[4*n]=now/1000*TAU*.35*(1+6*b.e);arr[4*n+1]=b.alpha;arr[4*n+2]=bend*(.7+.8*b.e);arr[4*n+3]=b.mix;\r\n  fishMesh.setMatrixAt(n+1,put(b.sx,b.sy,.35,0,0,-b.sa,b.ss*px));arr[4*n+4]=arr[4*n];arr[4*n+5]=b.sAlpha;arr[4*n+6]=bend*.7;\r\n }else if(idle){const b=bigFishPose(S.bigSeed+idle.cycle,idle.t/K.bigMs,A,K.bigLen,S.big),fadeIn=Math.min(1,idle.t/1500,(K.bigMs-idle.t)/1500);fishMesh.setMatrixAt(n,put(b.x,b.y,.4,0,0,-b.a,K.bigLen*px));arr[4*n]=now/1000*TAU*.35;arr[4*n+1]=.8*Math.max(0,fadeIn)*(.92+.08*Math.sin(now/300));arr[4*n+2]=bend*.7;arr[4*n+3]=.6;}\r\n else arr[4*n+1]=0;\r\n fishMesh.instanceMatrix.needsUpdate=true;aFish.needsUpdate=true;\r\n const [ox,oy]=toWorld(0,0),[,bottom]=toWorld(0,1);S.fishMat.uniforms.uFace.value.set(ox,-oy,px,oy-bottom);S.fishMat.uniforms.uRefract.value=K.glass.bend*px*2;\r\n}\r\n\r\nexport function stepPads(state,dt,t){\r\n const {K,A,pads,pointers,waves,slope}=state;\r\n for(const p of pads){\r\n  if(p.hidden)continue;\r\n  const drift=p.anchor?0:K.drift;\r\n  const hx=p.hx+Math.sin(t*K.driftHz*TAU+p.seed)*drift,hy=p.hy+Math.cos(t*K.driftHz*TAU*.8+p.seed*1.7)*drift;\r\n  let fx=(hx-p.x)*K.spring-p.vx*K.damping,fy=(hy-p.y)*K.spring-p.vy*K.damping;\r\n  for(const f of pointers.values()){\r\n   let dx=p.x-f.x,dy=p.y-f.y,d=Math.hypot(dx,dy);\r\n   if(d<K.pushR){\r\n    // Even a pad directly under the fingertip needs a direction to move aside.\r\n    if(d<1e-5){dx=Math.cos(p.seed);dy=Math.sin(p.seed);d=1;}\r\n    const distance=Math.hypot(p.x-f.x,p.y-f.y),k=K.pushK*(1-distance/K.pushR)**2;\r\n    fx+=dx/d*k;fy+=dy/d*k;\r\n   }\r\n  }\r\n  slopeAt(waves,p.x,p.y/A,slope);fx-=slope[0]*K.waveK;fy-=slope[1]*K.waveK;\r\n  p.vx+=fx*dt;p.vy+=fy*dt;p.x+=p.vx*dt;p.y+=p.vy*dt;p.rot+=(p.vx-p.vy)*dt*2;\r\n }\r\n}\r\n\r\nfunction stepWater(t){\r\n const {K,waves,pointers}=S;\r\n for(const f of pointers.values()){disturb(waves,f.x,f.y/S.A,f.moved?K.wave.drag:K.wave.hold*Math.sin(t*TAU*K.wave.holdHz));f.moved=false;}\r\n const e=stepWaves(waves,K.wave.damping);\r\n if(e>1e-4||S.dirty){\r\n  const {cur,cols,rows}=waves,d=S.data,gain=K.wave.gain;\r\n  for(let y=0;y<rows;y++)for(let x=0;x<cols;x++){\r\n   const i=y*cols+x,l=cur[x>0?i-1:i],r=cur[x<cols-1?i+1:i],u=cur[y>0?i-cols:i],dn=cur[y<rows-1?i+cols:i],o=4*i;\r\n   d[o]=Math.max(0,Math.min(255,128+(r-l)*gain));d[o+1]=Math.max(0,Math.min(255,128+(dn-u)*gain));d[o+2]=Math.max(0,Math.min(255,128+cur[i]*gain));\r\n  }\r\n  S.waveTex.needsUpdate=true;S.dirty=e>1e-4;\r\n }\r\n return e>1e-4;\r\n}\r\n\r\nfunction step(dt,now){\r\n if(!S)return false;\r\n const t=now/1000,fin=S.lead;\r\n S.fade+=(S.fadeTo-S.fade)*Math.min(1,dt*4);S.fishMat.uniforms.uFade.value=S.fade;\r\n if(fin)S.uniforms.uFinger.value.set(fin.x,fin.y/S.A,1,0);else S.uniforms.uFinger.value.z=Math.max(0,S.uniforms.uFinger.value.z-dt*2);\r\n if(S.reduced){secretStep(dt,now);place(dt,now);return Math.abs(S.fade-S.fadeTo)>.01||S.sec.st!=='idle';}\r\n stepWater(t);stepPads(S,dt,t);stepFish(S.fishS,dt,now,S.K);secretStep(dt,now);place(dt,now);\r\n return true; // the koi never stop swimming\r\n}\r\n\r\n// --- Secret animation: rise (held), splash (done), bored (lifted early) --------------------------------------\r\nconst wrapA=d=>d-TAU*Math.round(d/TAU),ease=p=>p*p*(3-2*p);\r\nfunction startSecret(now){ // a hold landed on the big koi: freeze its pose and take it over from the idle show\r\n const b=S.big,K=S.K;S.an={x:b.x,y:b.y,a:b.a,a0:b.a,x0:b.x,y0:b.y,pitch:0,z:0,s:K.bigLen,alpha:.8,mix:.6,e:0,sx:b.x,sy:b.y,sa:b.a,ss:K.bigLen,sAlpha:K.bigAlpha*.8,bt:0};S.wake?.();\r\n}\r\nfunction startBored(){S.an.bt=.001;S.an.tgt=Math.hypot(S.an.x-.5,S.an.y-S.A/2)>.05?Math.atan2(S.an.y-S.A/2,S.an.x-.5):S.an.a+Math.PI/2;S.wake?.();}\r\nfunction startSplash(now){\r\n const c={x:.5,y:S.A/2};S.an=null;S.sec={...S.sec,st:'done'};S.splashT=now;S.splashFired=false;S.ringN=0;S.drops=[];\r\n for(let i=0;i<DROPS;i++){const ang=Math.random()*TAU,up=i%2===0; // even: a spray column; odd: a crown arcing out\r\n  S.drops.push({x:c.x+Math.cos(ang)*.03,y:c.y+Math.sin(ang)*.02,cx:Math.cos(ang),cy:Math.sin(ang),vr:up?.04+Math.random()*.2:.35+Math.random()*1.0,vh:up?4.2+Math.random()*2.2:2.4+Math.random()*1.8,landed:false});}\r\n for(const d of S.drops)d.life=2*d.vh/6;\r\n for(const p of S.pads){const dx=p.x-c.x,dy=p.y-c.y,d=Math.hypot(dx,dy)||1,k=.5*Math.max(0,1-d/1.3);p.vx+=dx/d*k;p.vy+=dy/d*k;} // pads ride the wave outward\r\n S.dropMesh.visible=true;S.foam.visible=true;disturb(S.waves,.5,.5,6);S.dirty=true;S.wake?.();\r\n}\r\nfunction secretStep(dt,now){\r\n const sec=S.sec,an=S.an,K=S.K;\r\n if(sec.st==='hold'){\r\n  const ns=pondSecret(sec,{type:'tick'},now);S.sec=ns;if(ns.st==='done')return startSplash(now);\r\n  const p=Math.min(1,(now-sec.t0)/SECRET.riseMs),e=ease(p),ep=ease(Math.min(1,p*1.5)); // steers to the middle a little ahead of the growth\r\n  an.e=e;an.x=an.x0+(.5-an.x0)*ep;an.y=an.y0+(S.A/2-an.y0)*ep;an.a=an.a0+wrapA(-Math.PI/2-an.a0)*ep;an.pitch=e*.55;an.z=e*.56;an.s=K.bigLen*(1+e*.4);an.alpha=.8+.2*e;an.mix=.6+.4*e;an.ss=K.bigLen*(1-e*.55);an.sAlpha=K.bigAlpha*.8*(1-e*.75);\r\n }else if(sec.st==='bored'&&an&&!an.bt)startBored();\r\n if(sec.st==='bored'&&an&&an.bt){ // turns away, levels out, swims off the face\r\n  an.bt+=dt;const k=Math.min(1,dt*3.5),f=Math.exp(-dt*5);\r\n  an.a+=wrapA(an.tgt-an.a)*k;an.pitch*=f;an.z*=f;an.s+=(K.bigLen-an.s)*Math.min(1,dt*3);an.mix+=(.6-an.mix)*k;an.alpha+=(.8-an.alpha)*k;an.e*=f;an.sAlpha*=Math.exp(-dt*6);\r\n  const sp=Math.min(.55,.1+an.bt*.5);an.x+=Math.cos(an.a)*sp*dt;an.y+=Math.sin(an.a)*sp*dt;an.sx=an.x;an.sy=an.y;an.sa=an.a;an.ss=an.s;\r\n  if(Math.hypot(an.x-.5,an.y-S.A/2)>Math.hypot(.5,S.A/2)+K.bigLen*.6||an.bt>9)endSecret(now);\r\n }\r\n if(sec.st==='done'&&!an){ // splash: rings across the face, foam flash, spray column + crown that fall back with tiny ripples\r\n  const t=now-S.splashT,rings=[[130,.5,.5,5],[300,.5,.5,4],[480,.5,.5,3.2],[680,.5,.5,2.4],[200,.4,.42,2.5],[260,.6,.58,2.5]];\r\n  while(S.ringN<rings.length&&t>=rings[S.ringN][0]){const r=rings[S.ringN++];disturb(S.waves,r[1],r[2],r[3]);S.dirty=true;}\r\n  const {sx:px,point}=pondScreenTransform(S.toWorld,S.A),ts=t/1000;\r\n  S.drops.forEach((d,i)=>{const h=d.vh*ts-3*ts*ts,ok=ts<d.life,gx=d.x+d.cx*d.vr*ts,gy=d.y+d.cy*d.vr*ts*.6;\r\n   if(!ok&&!d.landed){d.landed=true;disturb(S.waves,Math.min(.97,Math.max(.03,gx)),Math.min(.97,Math.max(.03,gy/S.A)),.7);S.dirty=true;}\r\n   const [dx,dy]=point(gx,gy);S.dropPos[3*i]=dx;S.dropPos[3*i+1]=dy+Math.max(0,h)*.35*px;S.dropPos[3*i+2]=ok?4:-9999;});\r\n  S.dropGeo.attributes.position.needsUpdate=true;S.dropMat.opacity=Math.min(1,Math.max(0,(1900-t)/500));\r\n  const {sy}=pondScreenTransform(S.toWorld,S.A),f=Math.max(0,1-t/420),fs=(.35+Math.min(1,t/420)*1.5)*px,[fx,fy]=point(.5,S.A/2);S.foam.position.set(fx,fy,4.5);S.foam.scale.set(fs,fs*sy/px,1);S.foam.material.opacity=f*.85;\r\n  if(!S.splashFired&&t>=1300){S.splashFired=true;typeof window!=='undefined'&&window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'pond'}}));}\r\n  if(t>=1900)endSecret(now);\r\n }\r\n}\r\nfunction endSecret(now){S.an=null;S.sec={st:'idle',id:null};S.dropMesh.visible=false;S.foam.visible=false;S.fishS.lastTouch=now;S.wake?.();}\r\n// Back to a calm pond after the secret (the board was paused mid-splash when the vault opened): foam, spray and the held koi gone.\r\nfunction healSecret(){if(S&&(S.an||S.sec.st!=='idle'))endSecret(performance.now());}\r\nfunction press(id,u,v){\r\n if(!S)return;const p=pondPoint(u,v,S.A),now=performance.now(),was=S.sec.st;\r\n S.sec=pondSecret(S.sec,{type:'down',id,x:p.x,y:p.y,phase:S.fishS.phase,big:S.big},now);if(was==='idle'&&S.sec.st==='hold')startSecret(now);\r\n S.lead={...p,moved:true};S.pointers.set(id,S.lead);\r\n if(!S.reduced)disturb(S.waves,u,v,S.K.wave.press);\r\n if(S.sec.id!==id)fishTouch(S.fishS,p.x,p.y,now); // a claimed hold leaves the fish show alone\r\n S.dirty=true;\r\n}\r\nfunction move(id,u,v){\r\n if(!S)return;const f=S.pointers.get(id),p=pondPoint(u,v,S.A);if(!f)return press(id,u,v);\r\n f.moved=f.moved||Math.hypot(p.x-f.x,p.y-f.y)>.002;f.x=p.x;f.y=p.y;\r\n S.sec=pondSecret(S.sec,{type:'move',id,x:p.x,y:p.y},performance.now());\r\n if(S.sec.id!==id)fishTouch(S.fishS,p.x,p.y,performance.now());\r\n}\r\nfunction release(id){\r\n if(!S)return;const mine=S.sec.id===id,now=performance.now();S.pointers.delete(id);const last=S.lead=[...S.pointers.values()].at(-1)||null;\r\n if(mine){S.sec=pondSecret(S.sec,{type:'up',id},now);if(S.sec.st==='done'&&S.an)startSplash(now);return;}\r\n if(last)fishTouch(S.fishS,last.x,last.y,now);else fishRelease(S.fishS,now);\r\n}\r\nconst claims=id=>!!S&&S.sec.id===id; // read by the board wrapper before release forgets the pointer\r\n// The cut: pads inside the shape sink away with the water; the koi dive (fade) until it heals.\r\nfunction cut(polyUv){\r\n if(!S)return;S.cutPoly=polyUv;\r\n for(const p of S.pads)p.hidden=pointInPolygon(p.x,p.y/S.A,polyUv);\r\n S.fadeTo=0;S.wake?.();\r\n}\r\nfunction heal(){if(!S)return;S.cutPoly=null;for(const p of S.pads)p.hidden=false;S.fadeTo=1;S.wake?.();}\r\nfunction dispose(){\r\n if(!S)return;\r\n for(const m of [S.padMesh,S.lilyMesh,S.coreMesh,S.fishMesh]){m.removeFromParent();m.geometry.dispose();m.material.dispose();m.dispose?.();}\r\n S.foam.removeFromParent();S.foam.geometry.dispose();S.foam.material.dispose();S.dropMesh.removeFromParent();S.dropGeo.dispose();S.dropMat.dispose();S.dropTex.dispose();\r\n S.koi.dispose();S.waveTex.dispose();S=null;\r\n}\r\n\r\nexport const pond={\r\n id:'pond',build,background:'#06110f',ink:true,\r\n guide:{color:'#bff8ee',alpha:.08,width:4}, // the hint shapes glow faintly on the water\r\n frame:POND.frame,keepColors:true,tintTarget:'trace',\r\n uniforms:{uWave:{value:null},uFinger:{value:null}},\r\n fragmentDecls:WATER_DECLS,\r\n vertexDisplace:'/* pond: flat water; the ripples live in the fragment */',\r\n fragment:WATER_FRAGMENT,\r\n init,step,press,move,release,claims,cut,heal,healSecret,dispose,setTint,\r\n forceBig(){if(!S)return;const K=S.K,now=performance.now();S.sec={st:'idle',id:null};S.an=null;S.fishS.touch=null;S.fishS.lastTouch=now-(K.idleMs+K.scatterMs+K.bigMs*.5);S.wake?.();}, // test/preview: big koi mid-crossing now\r\n debug:()=>S&&{sec:S.sec.st,big:S.big,an:S.an&&{...S.an},phase:S.fishS.phase,since:performance.now()-S.fishS.lastTouch,born:S.born,members:S.fishS.members,fish:S.fishS.fish.map(f=>[f.x,f.y,f.a]),pads:S.pads.length,lilies:S.lilies.length,anchors:S.pads.filter(p=>p.anchor).length},\r\n};\r\n","modules/portal/portal-board-wood.mjs":"// Burning-wood board effect for portal-board-glb.mjs: press/drag drops dwell-scaled embers that age\r\n// on the clock (deep orange -> dark ember -> char) on `paint`, glow on `glow` while hot, then fade out\r\n// so the board returns to just its guides; procedural smoke sprites puff while burning. AGPL-3.0-or-later.\r\n// Secret (Achievement Vault): scrub one face quadrant (>=8 reversals in 3 s) -> heat builds -> fire; one tap -> steam,\r\n// ash, crumble; then window event 'myr5:portal-secret' {board:'wood'}. The gesture is the pure reducer woodSecret().\r\n\r\nconst COOL_MS=2600; // ember age at which it reaches char\r\nconst FADE_MS=7000,FADE_OUT_MS=1200; // Ian: everything returns to normal after 7 s (2026-09-22)\r\nconst TICK_MS=80; // paint/glow redraw period while anything is changing\r\nconst STAMP_FRAC=.04,STAMP_STEP=12; // ember radius as a fraction of face width; paint-px between embers along a drag\r\nconst GLOW_GAIN=1.6;\r\n// Per-ember alpha by ms spent per STAMP_STEP: embers overlap ~7x along a drag, so a normal stroke\r\n// ends fully black and a held finger stacks a fresh ember every EMBER_PERIOD_MS.\r\nconst DWELL_CAP_MS=300,DWELL_MIN=.45,DWELL_MAX=1;\r\nconst EMBER_PERIOD_MS=90,SPRITES=32;\r\nconst SMOKE_COUNT=40,SMOKE_PERIOD_MS=90,SMOKE_LIFE_MS=2000,SMOKE_RISE=55/1000,SMOKE_DRIFT=14/1000;\r\nconst SMOKE_R0=34,SMOKE_R1=90,SMOKE_COLOR='216,220,223',SMOKE_OPACITY=.55,SMOKE_Z=14;\r\n\r\n// --- Pure helpers (no THREE dependency) -----------------------------------------------------\r\n// Ember colour by age in ms: deep orange -> red -> dark ember -> char at COOL_MS (then held).\r\nconst STOPS=[[0,255,106,0],[.35,255,45,0],[.65,122,20,0],[1,10,6,4]];\r\nexport function emberColor(age){\r\n const f=Math.min(1,Math.max(0,age/COOL_MS));\r\n let i=0;while(i<STOPS.length-2&&f>STOPS[i+1][0])i++;\r\n const lo=STOPS[i],hi=STOPS[i+1],t=(f-lo[0])/(hi[0]-lo[0]);\r\n return {r:Math.round(lo[1]+(hi[1]-lo[1])*t),g:Math.round(lo[2]+(hi[2]-lo[2])*t),b:Math.round(lo[3]+(hi[3]-lo[3])*t)};\r\n}\r\n// Scorch opacity multiplier by age: 1 until FADE_MS, then out to 0 over FADE_OUT_MS (0 = dropped).\r\nexport function emberFade(age){return Math.min(1,Math.max(0,1-(age-FADE_MS)/FADE_OUT_MS));}\r\n// ms since the previous stamp on this stroke -> per-ember alpha, capped so dwell can't overshoot.\r\nexport function dwellAlpha(msSinceLast){\r\n const c=Math.min(DWELL_CAP_MS,Math.max(0,msSinceLast));\r\n return DWELL_MIN+(DWELL_MAX-DWELL_MIN)*(c/DWELL_CAP_MS);\r\n}\r\nexport {COOL_MS,FADE_MS,FADE_OUT_MS};\r\nconst lerp=(a,b,t)=>a+(b-a)*t;\r\n\r\n// --- Secret gesture (pure) ----------------------------------------------------------------------\r\n// State: {phase:'idle'|'fire'|'ash', id:tracked pointer, q:quadrant, ax/ay:{ref,dir,ext,ts} per-axis turn trackers,\r\n// claimId:pointer to claim, at:{x,y} fire origin}. Events: {t:'down'|'move'|'up', id, x, y (face px), w, h (face px size), edge}.\r\nexport const SECRET={leg:18,revs:8,claimRevs:4,windowMs:3000};\r\nexport const woodSecretInit=()=>({phase:'idle',id:null,q:-1,ax:null,ay:null,claimId:null,at:null,fireAt:0,ashAt:0});\r\nconst axis=(a,p,now)=>{ // a leg is >= SECRET.leg px; a reversal is a counter-move of that size off the extreme\r\n let {ref,dir,ext,ts}=a;\r\n if(!dir){if(Math.abs(p-ref)>=SECRET.leg){dir=Math.sign(p-ref);ext=p;}}\r\n else if(dir*(p-ext)>0)ext=p;\r\n else if(Math.abs(p-ext)>=SECRET.leg){ts=[...ts.filter(t=>now-t<SECRET.windowMs),now];ref=ext;dir=-dir;ext=p;}\r\n return {ref,dir,ext,ts};\r\n};\r\nconst quad=e=>(e.x<e.w/2?0:1)+(e.y<e.h/2?0:2);\r\nconst fresh=(s,e)=>({...s,id:e.id,q:quad(e),ax:{ref:e.x,dir:0,ext:e.x,ts:[]},ay:{ref:e.y,dir:0,ext:e.y,ts:[]}});\r\n// Reversals still inside the 3 s window (the larger of the x and y counts).\r\nexport const revNow=(s,now)=>s.ax?Math.max(...[s.ax,s.ay].map(a=>a.ts.filter(t=>now-t<SECRET.windowMs).length)):0;\r\nexport function woodSecret(s,e,now){\r\n s=s||woodSecretInit();\r\n if(s.phase==='ash')return s;\r\n if(s.phase==='fire')return e.t==='down'?{...s,phase:'ash',ashAt:now,claimId:e.id}:s; // any tap puts it out (claimed so portal ignores it)\r\n if(e.t==='down')return s.id!=null||e.edge?s:fresh({...s,claimId:null},e); // first finger only; edge touches (back-swipe zone) ignored\r\n if(e.id!==s.id)return s;\r\n if(e.t==='up')return {...s,id:null,ax:null,ay:null};\r\n if(quad(e)!==s.q)return fresh(s,e); // left the quadrant: start over from here\r\n const n={...s,ax:axis(s.ax,e.x,now),ay:axis(s.ay,e.y,now)},rev=revNow(n,now);\r\n if(rev>=SECRET.claimRevs)n.claimId=e.id;\r\n if(rev>=SECRET.revs){n.phase='fire';n.fireAt=now;n.at={x:e.x,y:e.y};}\r\n return n;\r\n}\r\n// Does portal.mjs need to leave this pointer alone? (scrubbing, or the board is burning/ashed)\r\nexport const woodClaims=(s,id)=>!!s&&(s.claimId===id||s.phase!=='idle');\r\n\r\n// One instance per board layer: the 3D board's (wood itself) and the flat board's 2D trace (wood.trace2d, R7: embers,\r\n// char and glow; no scene, so no smoke) each get their own.\r\nexport function woodEffect(){\r\nlet S=null; // per-instance state\r\nconst U={uAsh:{value:0},uCrumble:{value:0},uCrack:{value:0},uFireR:{value:0},uAsp:{value:1},uFireO:{value:null},uCell:{value:null}}; // ash/crumble shader uniforms (portal-board-glb merges effect.uniforms)\r\n\r\nfunction softCircleTexture(THREE){\r\n const c=document.createElement('canvas');c.width=c.height=64;\r\n const ctx=c.getContext('2d'),g=ctx.createRadialGradient(32,32,0,32,32,32);\r\n g.addColorStop(0,`rgba(${SMOKE_COLOR},.9)`);g.addColorStop(1,`rgba(${SMOKE_COLOR},0)`);\r\n ctx.fillStyle=g;ctx.fillRect(0,0,64,64);\r\n const tex=new THREE.CanvasTexture(c);tex.needsUpdate=true;return tex;\r\n}\r\n// Soft ember disc at colour step k (0 = birth .. SPRITES-1 = char), cached: redraws are just drawImage.\r\nfunction sprite(k){\r\n if(S.sprites[k])return S.sprites[k];\r\n const r=S.r,d=Math.ceil(2*r),c=document.createElement('canvas');c.width=c.height=d;\r\n const {r:cr,g:cg,b:cb}=emberColor(k/(SPRITES-1)*COOL_MS),ctx=c.getContext('2d'),g=ctx.createRadialGradient(r,r,0,r,r,r);\r\n g.addColorStop(0,`rgba(${cr},${cg},${cb},1)`);g.addColorStop(.5,`rgba(${cr},${cg},${cb},.85)`);g.addColorStop(1,`rgba(${cr},${cg},${cb},0)`);\r\n ctx.fillStyle=g;ctx.fillRect(0,0,d,d);\r\n return S.sprites[k]=c;\r\n}\r\nfunction stamp(u,v,heat){if(S.heat)heat=Math.min(1,heat*(1+.6*S.heat));S.embers.push({x:u*S.paint.canvas.width,y:v*S.paint.canvas.height,heat,t0:performance.now(),k:-1,a:-1});}\r\n\r\n// One redraw pass. Paint: restore the pristine guides inside the union of every ember whose colour or\r\n// opacity changed, then redraw the live embers overlapping it (clipped, oldest first). Glow: clear and\r\n// re-draw the hot embers in their own colour (source-over, so overlaps never sum to yellow); wiped once\r\n// when the last one cools. Returns whether anything is still changing.\r\n// ponytail: redraw cost grows with embers under the dirty rect; cap S.embers if long scribbles stutter.\r\nfunction tick(now){\r\n const R=S.r,last=SPRITES-1,live=[];let x0=Infinity,y0=Infinity,x1=-Infinity,y1=-Infinity;\r\n for(const e of S.embers){\r\n  const age=now-e.t0,a=Math.round(e.heat*emberFade(age)*100)/100,k=S.reduced?last:Math.round(Math.min(1,age*(1-.7*S.heat)/COOL_MS)*last); // heat keeps embers hot\r\n  if(k!==e.k||a!==e.a){e.k=k;e.a=a;x0=Math.min(x0,e.x-R);y0=Math.min(y0,e.y-R);x1=Math.max(x1,e.x+R);y1=Math.max(y1,e.y+R);}\r\n  if(a>0)live.push(e);\r\n }\r\n S.embers=live;\r\n const pc=S.paint.ctx,W=S.paint.canvas.width,H=S.paint.canvas.height;\r\n const x=Math.max(0,Math.floor(x0)),y=Math.max(0,Math.floor(y0)),w=Math.min(W,Math.ceil(x1))-x,h=Math.min(H,Math.ceil(y1))-y,changed=w>0&&h>0;\r\n if(changed){\r\n  pc.save();pc.beginPath();pc.rect(x,y,w,h);pc.clip();pc.clearRect(x,y,w,h);pc.drawImage(S.pristine,x,y,w,h,x,y,w,h);\r\n  for(const e of live)if(e.x+R>x&&e.x-R<x+w&&e.y+R>y&&e.y-R<y+h){pc.globalAlpha=e.a;pc.drawImage(sprite(e.k),e.x-R,e.y-R);}\r\n  pc.restore();S.paint.texture.needsUpdate=true;\r\n }\r\n const hot=S.reduced?[]:live.filter(e=>now-e.t0<COOL_MS);\r\n if(hot.length||S.glowOn){\r\n  const gc=S.glow.ctx;gc.clearRect(0,0,S.glow.canvas.width,S.glow.canvas.height);\r\n  gc.save();\r\n  for(const e of hot){gc.globalAlpha=Math.min(1,GLOW_GAIN*(1+S.heat)*e.heat*(1-(now-e.t0)*(1-.7*S.heat)/COOL_MS));gc.drawImage(sprite(e.k),e.x-R,e.y-R);}\r\n  gc.restore();S.glow.texture.needsUpdate=true;S.glowOn=hot.length>0;\r\n }\r\n const busy=changed||hot.length>0;\r\n // Char sits still until FADE_MS: sleep and let a timer wake the board for the fade-out.\r\n clearTimeout(S.timer);\r\n if(!busy&&live.length)S.timer=setTimeout(()=>S?.wake?.(),Math.max(0,live[0].t0+FADE_MS-now));\r\n return busy;\r\n}\r\n\r\nfunction spawnSmoke(u,v,at,k=1,tone=0){ // k: rise speed multiplier; tone 1 = small pale ash dust, 2 = dark smoke\r\n const dead=S.smoke.find(s=>!s.alive);if(!dead)return;\r\n const [x,y]=at||S.toWorld(u,v);\r\n dead.alive=true;dead.k=k;dead.size=tone===1?.55:1;dead.t0=S.hold?S.hold-Math.random()*1200:performance.now();dead.sprite.material.color?.setScalar?.(tone===2?.3:1);dead.x0=x;dead.y0=y;dead.dir=Math.random()<.5?-1:1;\r\n dead.sprite.visible=true;dead.sprite.position.set(x,y,S.faceZ+SMOKE_Z);dead.sprite.material.opacity=0;\r\n}\r\nfunction stepSmoke(now){\r\n let any=false;\r\n for(const s of S.smoke){\r\n  if(!s.alive)continue;\r\n  const age=now-s.t0,t=age/SMOKE_LIFE_MS;\r\n  if(t>=1){s.alive=false;s.sprite.visible=false;continue;}\r\n  any=true;\r\n  const sc=lerp(SMOKE_R0,SMOKE_R1,t)*(s.size||1);\r\n  s.sprite.position.set(s.x0+s.dir*SMOKE_DRIFT*age,s.y0+SMOKE_RISE*(s.k||1)*age,S.faceZ+SMOKE_Z);\r\n  s.sprite.scale.set(sc,sc,1);\r\n  s.sprite.material.opacity=SMOKE_OPACITY*(1-t);\r\n }\r\n return any;\r\n}\r\n\r\n// --- Secret: heat -> blaze -> steam/ash -> crack & crumble ---------------------------------------\r\nconst NF=48,NS=40,NK=260,ASH_MS=1100,HOLD_MS=700,CRUMBLE_MS=3200,SPREAD_MS=1500,CHUNK_BURST=.6;\r\nconst faceW=()=>S.toWorld(1,0)[0]-S.toWorld(0,0)[0],faceH=()=>S.toWorld(0,0)[1]-S.toWorld(0,1)[1];\r\nconst wp=(x,y)=>{const [ox,oy]=S.toWorld(0,0);return [ox+x,oy-y];}; // face px -> world\r\nconst rnd=Math.random;\r\nfunction isEdge(u,v){ // first touch within 24 px of the screen edge = iOS back-swipe / home bar: never a scrub\r\n const c=document.querySelector?.('.portal-board-canvas');if(!c)return false;\r\n const r=c.getBoundingClientRect(),[x,y]=S.toWorld(u,v),sx=r.left+x,sy=r.top-y;\r\n return sx<24||sy<24||sx>innerWidth-24||sy>innerHeight-24;\r\n}\r\nfunction secretEv(t,id,u,v){\r\n if(!S.scene)return;\r\n const w=faceW(),h=faceH();S.rs=woodSecret(S.rs,{t,id,x:u*w,y:v*h,w,h,edge:t==='down'&&isEdge(u,v)},performance.now());\r\n if(t!=='up')S.finger=[u,v];\r\n}\r\nfunction flameTexture(){\r\n const c=document.createElement('canvas');c.width=64;c.height=128;const x=c.getContext('2d');\r\n const g=x.createRadialGradient(32,96,2,32,90,64);g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.5,'rgba(255,255,255,.55)');g.addColorStop(1,'rgba(255,255,255,0)');\r\n x.filter='blur(2px)';x.fillStyle=g;x.beginPath();x.moveTo(32,2);x.bezierCurveTo(30,34,6,60,8,92);x.bezierCurveTo(10,120,54,120,56,92);x.bezierCurveTo(58,60,34,34,32,2);x.fill();\r\n return c;\r\n}\r\nfunction dotTexture(){\r\n const c=document.createElement('canvas');c.width=c.height=16;const x=c.getContext('2d'),g=x.createRadialGradient(8,8,0,8,8,8);\r\n g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.4,'rgba(255,255,255,.6)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,16,16);return c;\r\n}\r\nconst LAYERS=[[0xff3a00,1,.32,.9],[0xff7a10,.62,.34,.8],[0xffd890,.32,.4,.9]]; // colour, size, base alpha, shape: outer/mid/core tongues\r\nfunction ensureFx(){\r\n if(S.fx||!S.scene)return;\r\n const T=S.THREE,mk=(map,color)=>{const m=new T.SpriteMaterial({map,color,transparent:true,opacity:0,depthWrite:false,depthTest:false,blending:T.AdditiveBlending}),sp=new T.Sprite(m);sp.visible=false;sp.renderOrder=20;S.scene.add(sp);return sp;};\r\n const ft=new T.CanvasTexture(flameTexture()),dt=new T.CanvasTexture(dotTexture());\r\n S.fx={ft,dt,flames:Array.from({length:NF},()=>({alive:false,sp:LAYERS.map(l=>{const sp=mk(ft,l[0]);sp.center.set(.5,.08);return sp;})})),\r\n  sparks:Array.from({length:NS},()=>({alive:false,sp:mk(dt,0xffa030)})),light:new T.PointLight(0xff6a1a,0,900,0)};\r\n S.scene.add(S.fx.light);\r\n}\r\n// Cracks: a jittered-grid Voronoi of the face (unit space, x 0..1, y 0..asp). Each cell has a break threshold (the bottom lets go\r\n// first); a data texture carries R = threshold, G = distance to the nearest crack, which the board shader reads for the glowing\r\n// char cracks, the ash cracks and the crumble. The same cells become the falling chunks.\r\nfunction clip(poly,a,b,c){ // keep a*x+b*y<=c\r\n const out=[];for(let i=0;i<poly.length;i++){const p=poly[i],q=poly[(i+1)%poly.length],dp=a*p[0]+b*p[1]-c,dq=a*q[0]+b*q[1]-c;\r\n  if(dp<=0)out.push(p);if(dp*dq<0){const t=dp/(dp-dq);out.push([p[0]+(q[0]-p[0])*t,p[1]+(q[1]-p[1])*t]);}}\r\n return out;\r\n}\r\nfunction ensureCells(){\r\n if(S.cells||!S.scene)return;\r\n const asp=faceH()/faceW(),gx=8,gy=Math.max(2,Math.round(8*asp)),seeds=[];\r\n for(let j=0;j<gy;j++)for(let i=0;i<gx;i++)seeds.push({x:(i+.15+.7*rnd())/gx,y:(j+.15+.7*rnd())/gy*asp});\r\n const cells=seeds.map((s,i)=>{\r\n  let poly=[[0,0],[1,0],[1,asp],[0,asp]];\r\n  for(const o of seeds)if(o!==s){const dx=o.x-s.x,dy=o.y-s.y;if(dx*dx+dy*dy<.09)poly=clip(poly,dx,dy,(o.x*o.x+o.y*o.y-s.x*s.x-s.y*s.y)/2);}\r\n  const cx=poly.reduce((a,p)=>a+p[0],0)/poly.length,cy=poly.reduce((a,p)=>a+p[1],0)/poly.length;\r\n  return {s,poly,cx,cy,T:Math.max(.02,Math.min(1,.45*rnd()+.55*(1-s.y/asp)))};\r\n });\r\n S.cells=cells;S.corder=[...cells.keys()].sort((a,b)=>cells[a].T-cells[b].T);S.cptr=0;\r\n if(!S.THREE.DataTexture)return;\r\n const TW=128,TH=Math.round(128*asp),px=new Uint8Array(TW*TH*4);\r\n for(let py=0;py<TH;py++)for(let qx=0;qx<TW;qx++){\r\n  const x=(qx+.5)/TW,y=(py+.5)/TH*asp;let d1=9,d2=9,b=0;\r\n  for(let k=0;k<seeds.length;k++){const d=Math.hypot(seeds[k].x-x,seeds[k].y-y);if(d<d1){d2=d1;d1=d;b=k;}else if(d<d2)d2=d;}\r\n  const o=(py*TW+qx)*4;px[o]=Math.round(cells[b].T*255);px[o+1]=Math.round(Math.min(1,(d2-d1)/2/.06)*255);px[o+3]=255;\r\n }\r\n const tex=new S.THREE.DataTexture(px,TW,TH);tex.magFilter=tex.minFilter=S.THREE.LinearFilter;tex.needsUpdate=true;U.uCell.value=tex;\r\n}\r\nfunction ensureChunks(){\r\n if(S.chunks||!S.scene||!S.cells)return;\r\n const T=S.THREE,W=faceW();\r\n S.chunks=S.cells.map(c=>{\r\n  const sh=new T.Shape(c.poly.map(p=>new T.Vector2((p[0]-c.cx)*W*.97,-(p[1]-c.cy)*W*.97)));\r\n  const m=new T.Mesh(new T.ExtrudeGeometry(sh,{depth:6,bevelEnabled:false}),new T.MeshLambertMaterial({color:0x6e6a64,emissive:0x000000}));\r\n  m.visible=false;m.renderOrder=16;S.scene.add(m);return {m,alive:false,c};\r\n });\r\n}\r\nfunction ensureFlakes(){\r\n if(S.flakes||!S.scene)return;\r\n const T=S.THREE,geo=new T.BoxGeometry(1,1,.3),mats=['#8f8a82','#6a655e','#4a4540'].map(c=>new T.MeshBasicMaterial({color:c}));\r\n S.flakes={geo,mats,list:Array.from({length:NK},(_,i)=>{const m=new T.Mesh(geo,mats[i%3]);m.visible=false;m.renderOrder=15;S.scene.add(m);return {m,alive:false};})};\r\n}\r\nfunction spawnFlame(cx,cy,R,sz,now){\r\n const f=S.fx.flames.find(f=>!f.alive);if(!f)return;\r\n const W=faceW(),H=faceH(),[ox,oy]=S.toWorld(0,0);let x,y;\r\n for(let i=0;i<6;i++){const a=rnd()*6.283,r=R*Math.sqrt(rnd());x=cx+Math.cos(a)*r;y=cy+Math.sin(a)*r*.9;if(x>ox+W*.03&&x<ox+W*.97&&y<oy-H*.04&&y>oy-H*.96)break;} // resample off-board picks so a big burn doesn't pile up on the rim\r\n f.alive=true;f.life=650+rnd()*600;f.t0=now-(S.hold?rnd()*f.life*.7:0);f.ph=rnd()*6.283;f.sz=sz*(.7+.7*rnd());\r\n f.x=Math.min(ox+W*.97,Math.max(ox+W*.03,x));f.y=Math.min(oy-H*.04,Math.max(oy-H*.96,y));\r\n}\r\nfunction spawnSpark(cx,cy,R,now){\r\n const k=S.fx.sparks.find(k=>!k.alive);if(!k)return;const a=rnd()*6.283,r=R*Math.sqrt(rnd());\r\n Object.assign(k,{alive:true,t0:now-(S.hold?rnd()*900:0),life:800+rnd()*900,x:cx+Math.cos(a)*r,y:cy+Math.sin(a)*r*.9,vx:(rnd()-.5)*120,vy:160+rnd()*260,s:3+rnd()*4});\r\n}\r\n// Flame field around (cx,cy) radius R at intensity I (0..1, how many tongues are alive); kill 0..1 fades everything out.\r\nfunction stepFire(now,I,cx,cy,R,kill){\r\n const fx=S.fx,W=faceW(),sc=W/340;let alive=0;\r\n if(I>0){\r\n  let n=fx.flames.filter(f=>f.alive).length;for(let i=0;n<I*NF&&i<8;i++,n++)spawnFlame(cx,cy,R,sc,now);\r\n  if(rnd()<I*.7)spawnSpark(cx,cy,R,now);\r\n }\r\n for(const f of fx.flames){\r\n  if(!f.alive)continue;const t=(now-f.t0)/f.life;\r\n  if(t>=1){f.alive=false;f.sp.forEach(s=>s.visible=false);continue;}\r\n  alive++;const env=Math.min(1,t*5)*(1-t)**.7*(1-kill),fl=.8+.2*Math.sin(now*.018+f.ph),sway=Math.sin(now*.007+f.ph)*14*sc*t;\r\n  f.sp.forEach((s,i)=>{const [,size,a,sh]=LAYERS[i];s.visible=true;s.position.set(f.x+sway*(1-i*.3),f.y+t*70*sc*(1+i*.4),S.faceZ+10);\r\n   s.scale.set(120*sc*f.sz*size*fl*(1-.45*t),(150+210*(1-i*.25))*sc*f.sz*size*(.9+.2*fl)*(1+.35*t),1);s.material.opacity=env*a*(i==2?2.2:1.6)*sh;});\r\n }\r\n for(const k of fx.sparks){\r\n  if(!k.alive)continue;const age=(now-k.t0)/1000,t=age*1000/k.life;\r\n  if(t>=1){k.alive=false;k.sp.visible=false;continue;}alive++;\r\n  k.sp.visible=true;k.sp.position.set(k.x+k.vx*age+Math.sin(age*9+k.x)*6,k.y+k.vy*age,S.faceZ+12);k.sp.scale.setScalar(k.s*sc*(1-.5*t));k.sp.material.opacity=(1-t)*(1-kill);\r\n }\r\n fx.light.position.set(cx,cy+R*.3,W*.45);fx.light.intensity=I*(1-kill)*(4+1.2*Math.sin(now*.03)+.7*Math.sin(now*.071));\r\n return alive>0;\r\n}\r\nfunction spawnFlake(x,y,vx,vy,size,age){\r\n const k=S.flakes.list.find(k=>!k.alive);if(!k)return;\r\n Object.assign(k,{alive:true,t0:S.hold?performance.now()-(age||0)*1000:performance.now(),x,y,vx,vy,rx:(rnd()-.5)*10,ry:(rnd()-.5)*10,rz:(rnd()-.5)*10,sx:size*(.7+rnd()),sy:size*(.5+rnd()*.8)});k.m.visible=true;\r\n}\r\nfunction stepFlakes(now){\r\n let any=false;\r\n for(const k of S.flakes.list){\r\n  if(!k.alive)continue;const age=(now-k.t0)/1000,m=k.m;\r\n  if(age>2.2){k.alive=false;m.visible=false;continue;}any=true;\r\n  m.position.set(k.x+k.vx*age,k.y+k.vy*age-420*age*age,S.faceZ+4);\r\n  m.rotation.set(k.rx*age,k.ry*age,k.rz*age);m.scale.set(k.sx,k.sy,k.sx).multiplyScalar(Math.min(1,(2.2-age)*2));\r\n }\r\n return any;\r\n}\r\n// A chunk lets go: it sags (tilts and drops), tumbles under gravity, then breaks into flakes and an ash-dust puff.\r\nfunction stepChunks(now){\r\n let any=false;\r\n for(const k of S.chunks||[]){\r\n  if(!k.alive)continue;const a=(now-k.t0)/1000,m=k.m;\r\n  if(a>=k.burst){\r\n   k.alive=false;m.visible=false;\r\n   const x=k.x+k.vx*a,y=k.y+k.vy*a-450*a*a;\r\n   for(let i=0;i<5;i++)spawnFlake(x+(rnd()-.5)*30,y+(rnd()-.5)*30,k.vx+(rnd()-.5)*140,k.vy-450*a+rnd()*60,6+rnd()*6,S.hold?rnd()*1.5:0);\r\n   spawnSmoke(0,0,[x,y],.35,1);continue;\r\n  }\r\n  any=true;const e=Math.min(1,a/.5);\r\n  m.position.set(k.x+k.vx*a,k.y+k.vy*a-450*a*a,S.faceZ-3);m.rotation.set(k.rx*a*a*3,0,k.rz*a*a*3);\r\n  const g=(1-e)*(1-e)*.012;m.material.emissive.setRGB(g,g*.3,g*.05); // the break edge glows briefly\r\n }\r\n return any;\r\n}\r\n// Release every cell whose threshold the crumble front c has passed (hold: also show earlier breaks as flakes, for stills).\r\nfunction breakCells(c,now){\r\n while(S.cptr<S.corder.length&&S.cells[S.corder[S.cptr]].T<=c){\r\n  const i=S.corder[S.cptr++],cell=S.cells[i],k=S.chunks[i],[x,y]=wp(cell.cx*faceW(),cell.cy*faceW());\r\n  if(S.hold&&cell.T<c-.12){for(let j=0;j<5;j++)spawnFlake(x+(rnd()-.5)*30,y+(rnd()-.5)*30,(rnd()-.5)*140,-rnd()*200,6+rnd()*6,rnd()*1.8);continue;}\r\n  Object.assign(k,{alive:true,t0:now-(S.hold?rnd()*.45:0),x,y,vx:(rnd()-.5)*50,vy:-rnd()*25,rx:(rnd()-.5)*2,rz:(rnd()-.5)*2,burst:CHUNK_BURST+rnd()*.25});k.m.visible=true;\r\n }\r\n}\r\nfunction ashPaint(){ // scorch + glow gone, guides back\r\n S.embers=[];const pc=S.paint.ctx,gc=S.glow.ctx;\r\n pc.clearRect(0,0,S.paint.canvas.width,S.paint.canvas.height);pc.drawImage(S.pristine,0,0);S.paint.texture.needsUpdate=true;\r\n gc.clearRect(0,0,S.glow.canvas.width,S.glow.canvas.height);S.glow.texture.needsUpdate=true;S.glowOn=false;S.pointers.clear();\r\n}\r\nfunction secretStep(dt,now){\r\n if(!S.scene)return false;\r\n if(S.hold)now=S.hold; // debug.pose: time frozen\r\n const rs=S.rs,U_=S.uni;let busy=false;\r\n S.heat+=((S.stage==='idle'?revNow(rs,now)/SECRET.revs:S.stage==='fire'?1:0)-S.heat)*Math.min(1,dt*5);\r\n if(S.heat<.005)S.heat=0;\r\n if(U_.glowS)U_.glowS.value=1.2+1.8*S.heat;\r\n if(S.heat>.3)ensureCells(); // build the crack map before it is needed\r\n if(S.stage==='idle'&&rs.phase==='fire'){S.stage='fire';S.t0=now;S.fc=wp(rs.at.x,rs.at.y);S.fo=[rs.at.x/faceW(),rs.at.y/faceH()];ensureCells();}\r\n if((S.stage==='idle'||S.stage==='fire')&&rs.phase==='ash'){ // tap: hiss, steam, embers out, board greys\r\n  S.stage='ash';S.t0=now;ashPaint();ensureCells();\r\n  const [cx,cy]=S.fc||wp(faceW()/2,faceH()/2),W=faceW();\r\n  for(let i=0;i<S.smoke.length;i++)spawnSmoke(0,0,[cx+(rnd()-.5)*W*.9,cy+(rnd()-.5)*W*.6],1.2);\r\n  if(typeof window!=='undefined')window.dispatchEvent(new CustomEvent('myr5:portal-sound',{detail:{kind:'hiss',board:'wood'}}));\r\n }\r\n U.uAsp.value=faceH()/faceW();\r\n if(S.stage==='idle'){\r\n  const I=Math.max(0,S.heat-.4)*.5; // small tongues at the finger while the heat builds\r\n  if(I>0||S.fx?.flames.some(f=>f.alive)){ensureFx();const [fx,fy]=S.finger?S.toWorld(...S.finger):[0,0];busy=stepFire(now,I,fx,fy,faceW()*.07,0);}\r\n }else if(S.stage==='fire'){\r\n  ensureFx();const ft=now-S.t0,W=faceW(),sp=Math.min(1,ft/SPREAD_MS),R=W*(.1+1.05*sp);\r\n  busy=stepFire(now,Math.min(1,.5+sp*.5),S.fc[0],S.fc[1],R,0);\r\n  U.uFireR.value=1.7*Math.min(1,ft/(SPREAD_MS*1.4));if(U.uFireO.value?.set)U.uFireO.value.set(S.fo[0],S.fo[1]);\r\n  if(now-S.lastSmoke>70&&!S.reduced){S.lastSmoke=now;const a=rnd()*6.283,r=R*Math.sqrt(rnd());spawnSmoke(0,0,[S.fc[0]+Math.cos(a)*r,S.fc[1]+Math.sin(a)*r*.9],3,2);}\r\n  busy=true;\r\n }else{\r\n  const t=now-S.t0;\r\n  if(S.fx)busy=stepFire(now,0,0,0,1,Math.min(1,t/350));\r\n  U.uFireR.value=0;\r\n  if(S.stage==='ash'){\r\n   U.uAsh.value=Math.min(1,t/ASH_MS);U.uCrack.value=Math.min(1,t/(ASH_MS+HOLD_MS));busy=true;\r\n   if(t>ASH_MS+HOLD_MS){S.stage='crumble';S.t0=now;ensureFlakes();ensureChunks();S.cptr=0;}\r\n  }\r\n  if(S.stage==='crumble'){\r\n   const p=Math.min(1,(now-S.t0)/CRUMBLE_MS),c=1.02*p**1.25;U.uCrumble.value=c;ensureFlakes();ensureChunks();if(S.chunks)breakCells(c,now);busy=true;\r\n   if(p>=1&&!S.hold){S.stage='gone';S.t0=now;window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'wood'}}));S.timer2=setTimeout(heal,9000);}\r\n  }\r\n  if(S.chunks)busy=stepChunks(now)||busy;\r\n  if(S.flakes)busy=stepFlakes(now)||busy;\r\n }\r\n return busy||S.heat>0;\r\n}\r\n// Back to a fresh board. portal's board.heal() only reaches us after a cut, so this also runs on the next press and 9 s after the end.\r\nfunction heal(){\r\n if(!S)return;S.hold=0;clearTimeout(S.timer2);\r\n S.stage='idle';S.rs=woodSecretInit();S.heat=0;S.fc=null;S.finger=null;S.cptr=0;\r\n U.uAsh.value=U.uCrumble.value=U.uCrack.value=U.uFireR.value=0;if(S.uni.glowS)S.uni.glowS.value=1.2;\r\n for(const f of S.fx?.flames||[]){f.alive=false;f.sp.forEach(s=>s.visible=false);}\r\n for(const k of S.fx?.sparks||[]){k.alive=false;k.sp.visible=false;}\r\n if(S.fx)S.fx.light.intensity=0;\r\n for(const k of S.flakes?.list||[]){k.alive=false;k.m.visible=false;}\r\n for(const k of S.chunks||[]){k.alive=false;k.m.visible=false;}\r\n S.wake?.();\r\n}\r\n\r\n\r\nfunction init({THREE,scene,uniforms,paint,glow,toWorld,faceZ,wake}){\r\n const pristine=document.createElement('canvas');pristine.width=paint.canvas.width;pristine.height=paint.canvas.height;\r\n pristine.getContext('2d').drawImage(paint.canvas,0,0); // the guides, as drawn before init\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const tex=reduced||!scene?null:softCircleTexture(THREE);\r\n const smoke=reduced||!scene?[]:Array.from({length:SMOKE_COUNT},()=>{\r\n  const mat=new THREE.SpriteMaterial({map:tex,color:0xffffff,transparent:true,opacity:0,depthWrite:false,depthTest:false}); // raised carving must never hide smoke\r\n  const sprite=new THREE.Sprite(mat);sprite.visible=false;scene.add(sprite);\r\n  return {sprite,alive:false,t0:0,x0:0,y0:0,dir:1};\r\n });\r\n S={THREE,scene,paint,glow,toWorld,faceZ,wake,reduced,smoke,smokeTex:tex,pristine,r:paint.canvas.width*STAMP_FRAC,sprites:[],\r\n  pointers:new Map(),embers:[],glowOn:false,busy:false,lastTick:-Infinity,lastSmoke:0,timer:0,\r\n  rs:woodSecretInit(),stage:'idle',heat:0,lastStamp:0,uni:{ash:U.uAsh,crumble:U.uCrumble,glowS:uniforms?.uGlowStrength}};\r\n if(scene){if(THREE.Vector2)U.uFireO.value=new THREE.Vector2();if(THREE.DataTexture){const t=new THREE.DataTexture(new Uint8Array([255,255,255,255]),1,1);t.needsUpdate=true;U.uCell.value=t;}} // (white = 'no cracks' until ensureCells())\r\n if(scene&&typeof window!=='undefined')window.myr5Wood=debug; // 3D board only\r\n}\r\nfunction press(id,u,v){\r\n if(S.stage==='gone')heal();if(S.stage==='ash'||S.stage==='crumble')return;secretEv('down',id,u,v);\r\n S.pointers.set(id,{u,v,t:performance.now()});stamp(u,v,dwellAlpha(EMBER_PERIOD_MS));} // one hold-tick's worth\r\n// Walk from the last ember toward the finger in STAMP_STEP paint-px hops so fast drags stay continuous;\r\n// the remainder carries to the next move, and each hop's heat scales with the ms it took.\r\nfunction move(id,u,v){\r\n if(S.stage==='ash'||S.stage==='crumble'||S.stage==='gone')return;secretEv('move',id,u,v);\r\n const p=S.pointers.get(id);const now=performance.now();\r\n if(!p){S.pointers.set(id,{u,v,t:now});return;}\r\n const W=S.paint.canvas.width,H=S.paint.canvas.height,d=Math.hypot((u-p.u)*W,(v-p.v)*H),n=Math.floor(d/STAMP_STEP);\r\n if(!n)return;\r\n const a=dwellAlpha((now-p.t)*STAMP_STEP/d),su=(u-p.u)*STAMP_STEP/d,sv=(v-p.v)*STAMP_STEP/d;\r\n for(let i=0;i<n;i++){p.u+=su;p.v+=sv;stamp(p.u,p.v,a);}\r\n p.t=now;\r\n}\r\nfunction claims(id){return woodClaims(S?.rs,id);}\r\nfunction release(id){if(S?.scene)secretEv('up',id,0,0);S.pointers.delete(id);}\r\nfunction step(dt,now){\r\n if(S.pointers.size){ // held still: keep stacking fresh embers under each finger, and puff smoke\r\n  for(const p of S.pointers.values())if(now-p.t>=EMBER_PERIOD_MS){stamp(p.u,p.v,dwellAlpha(now-p.t));p.t=now;}\r\n  if(!S.reduced&&now-S.lastSmoke>=SMOKE_PERIOD_MS*(1-.5*S.heat)){S.lastSmoke=now;for(const p of S.pointers.values())spawnSmoke(p.u,p.v);}\r\n }\r\n const smokeAlive=(S.reduced?false:stepSmoke(now))|secretStep(dt,now);\r\n if(now-S.lastTick>=TICK_MS){S.lastTick=now;S.busy=tick(now);}\r\n return smokeAlive||S.busy||S.pointers.size>0;\r\n}\r\nconst debug={ // preview/test hooks: jump to a stage\r\n fire(){const w=faceW(),h=faceH();S.rs={...woodSecretInit(),phase:'fire',fireAt:performance.now(),at:{x:w*.3,y:h*.35}};S.wake?.();},\r\n ash(){if(S.rs.phase==='idle')debug.fire();S.rs={...S.rs,phase:'ash'};S.wake?.();},\r\n pose(stage,p){ // freeze mid-stage for screenshots: 'fire'|'ash'|'crumble', p 0..1\r\n  const now=performance.now();debug.heal();S.hold=now;\r\n  if(stage==='fire'){debug.fire();S.stage='fire';S.t0=now-p*SPREAD_MS;S.fc=wp(faceW()*.3,faceH()*.35);S.fo=[.3,.35];ensureCells();}\r\n  else{debug.ash();ensureCells();S.fc=wp(faceW()*.3,faceH()*.35);S.stage=stage;S.t0=now-p*(stage==='ash'?ASH_MS+HOLD_MS:CRUMBLE_MS);U.uAsh.value=1;U.uCrack.value=1;S.cptr=0;ensureFlakes();ensureChunks();}\r\n  S.wake?.();},\r\n adv(ms){S.hold+=ms;S.wake?.();}, // debug: step frozen time\r\n heal,state:()=>({stage:S.stage,rs:S.rs,heat:S.heat}),\r\n};\r\nfunction dispose(){\r\n if(S){clearTimeout(S.timer);clearTimeout(S.timer2);if(typeof window!=='undefined'&&window.myr5Wood===debug)delete window.myr5Wood;\r\n  for(const f of S.fx?.flames||[])f.sp.forEach(s=>{s.parent?.remove(s);s.material.dispose();});for(const k of S.fx?.sparks||[]){k.sp.parent?.remove(k.sp);k.sp.material.dispose();}\r\n  if(S.fx){S.fx.light.parent?.remove(S.fx.light);S.fx.ft.dispose();S.fx.dt.dispose();}\r\n  for(const k of S.flakes?.list||[])k.m.parent?.remove(k.m);if(S.flakes){S.flakes.geo.dispose();S.flakes.mats.forEach(m=>m.dispose());}\r\n  U.uCell.value?.dispose();U.uCell.value=null;for(const k of S.chunks||[]){k.m.parent?.remove(k.m);k.m.geometry.dispose();k.m.material.dispose();}for(const s of S.smoke)s.sprite.parent?.remove(s.sprite);for(const s of S.smoke)s.sprite.material.dispose();S.smokeTex?.dispose();}\r\n S=null;\r\n}\r\nconst WGLSL=`uniform float uAsh;uniform float uCrumble;uniform float uCrack;uniform float uFireR;uniform float uAsp;uniform vec2 uFireO;uniform sampler2D uCell;\r\nfloat wh(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nfloat wn(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.0-2.0*f);return mix(mix(wh(i),wh(i+vec2(1.0,0.0)),f.x),mix(wh(i+vec2(0.0,1.0)),wh(i+vec2(1.0,1.0)),f.x),f.y);}\r\nfloat wf(vec2 p){return wn(p)*.55+wn(p*2.1+7.3)*.3+wn(p*4.3+3.1)*.15;}\r\n`;\r\n// Board shader: charring + glowing cracks/grain embers while burning, blotchy ash with a crack network, then cell-by-cell crumble with dark char edges.\r\nconst WFRAG=`vec4 wcl=texture2D(uCell,vPlanar);float wce=wcl.g;\r\nif(uFireR>0.0){\r\n float bd=length((vPlanar-uFireO)*vec2(1.0,uAsp)),ch=smoothstep(uFireR,uFireR-.25,bd+wf(vPlanar*6.0)*.3-.15),fk=.75+.25*sin(uTime*9.0+wf(vPlanar*9.0)*30.0);\r\n diffuseColor.rgb=mix(diffuseColor.rgb,diffuseColor.rgb*vec3(.2,.13,.1),ch*.92);\r\n float crk=smoothstep(.09,.0,wce)*ch,spk=smoothstep(.62,.82,wf(vPlanar*vec2(16.0,44.0)+3.0))*ch;\r\n totalEmissiveRadiance+=vec3(1.0,.36,.05)*(crk*1.1+spk*.8)*fk;\r\n}\r\nif(uAsh>0.0){\r\n float bl=smoothstep(.4,.78,wf(vPlanar*vec2(5.0,4.0)+1.7));\r\n vec3 ac=mix(vec3(.68,.66,.62),vec3(.14,.12,.11),bl*.85)*clamp(.5+lum,.4,1.25)*(.8+.4*wn(vPlanar*60.0));\r\n diffuseColor.rgb=mix(diffuseColor.rgb,ac,uAsh);roughnessFactor=mix(roughnessFactor,1.0,uAsh);\r\n diffuseColor.rgb*=1.0-.8*smoothstep(.14,.0,wce)*uCrack;\r\n}\r\nif(uCrumble>0.0){\r\n float ct=wcl.r;if(ct<uCrumble)discard;\r\n diffuseColor.rgb*=1.0-.6*smoothstep(.3,.0,wce);\r\n totalEmissiveRadiance+=vec3(1.0,.35,.05)*smoothstep(.08,.0,ct-uCrumble)*smoothstep(.35,.0,wce)*1.5;\r\n}`;\r\nreturn {init,press,move,release,step,dispose,claims,heal,healSecret:heal,uniforms:U,fragmentDecls:WGLSL,fragment:WFRAG};\r\n}\r\n\r\nexport const wood={\r\n id:'wood',asset:'/pod/worlds/boards/wood.glb',flip:false,background:'#140c06',\r\n guide:{color:'#ffe0b0',alpha:.1,width:4},\r\n pattern:{left:.07,top:.03,right:.93,bottom:.78}, // measured in-browser against the carved oval's extremes\r\n ...woodEffect(),trace2d:woodEffect,\r\n};\r\n","modules/portal/portal-board.mjs":"// Quilt cloth board for the portal home. The solver is a port of Ten Minute Physics\r\n// \"Cloth Simulation\" (c) 2022 Matthias Müller, MIT licence (see portal-board NOTICE below),\r\n// adapted to a quilt pinned at its border that fingers press and drag. AGPL-3.0-or-later wrapper.\r\n// MIT License — Copyright (c) 2022 Matthias Müller\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software\r\n// and associated documentation files (the \"Software\"), to deal in the Software without restriction,\r\n// including without limitation the rights to use, copy, modify, merge, publish, distribute,\r\n// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n// The above copyright notice and this permission notice shall be included in all copies or\r\n// substantial portions of the Software.\r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\r\n// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\n// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\r\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\nimport * as THREE from 'three';\r\nimport {pieceMaterial,fallPieces,createPortalCutMask} from './portal-cut.mjs';\r\nimport {SHAPES,fromFrame} from './portal-shapes.mjs';\r\nimport {boardLayout} from './portal-board-layout.mjs';\r\n\r\nconst IMAGE='/pod/worlds/quilt.webp',IMAGE_W=1024,IMAGE_H=1666,TRACE_PX=640;\r\n// Stitched pattern (the eight shapes) inside the quilt image, as image fractions.\r\nconst PATTERN={left:22/IMAGE_W,top:22/IMAGE_H,right:1002/IMAGE_W,bottom:1575/IMAGE_H};\r\nconst PATTERN_RATIO=(1002-22)/(1575-22);\r\n// Ian: heavier, calmer (2026-09-22)\r\nexport const QUILT={segX:24,substeps:6,compliance:3e-6,restore:1.0,stretch:1.15,damping:.955,radius:60,depth:40,drag:.7,sleepMs:1500};\r\nconst BACKGROUND='#17111e';\r\n// W2-2O: the quilt texture is Starter-pack art. Without it (offline, not downloaded) the board is a plain\r\n// stitched quilt, same layout: cream cloth in its dark binding, the traceable shapes stitched where the art has them.\r\nconst STITCHES=[['oval','#c42a3c'],['x','#e0388f'],['up','#2c5cc2'],['down','#2f8a4c'],['cross','#4ea6da']];\r\n\r\n// The cloth fills the whole host like every grimoire (portal-board-layout.mjs); the source image stretches to it.\r\nexport function quiltSurfaceLayout(width,height){\r\n const face=boardLayout(width,height),fw=face.width,fh=face.height;\r\n return {face,pattern:{left:face.left+fw*PATTERN.left,top:face.top+fh*PATTERN.top,width:fw*(PATTERN.right-PATTERN.left),height:fh*(PATTERN.bottom-PATTERN.top)}};\r\n}\r\n// Vault secret (quilt): two-step fold. Pure reducer: ev {type:'down'|'move'|'up'|'tick'|'cancel',id,x,y,face,w,h} (client px).\r\n// Step 1: two fingers in the face's bottom-left/bottom-right quadrants drag up (>=35% of face height, <=2.5 s) -> lower half folds up.\r\n// Step 2 (<=8 s later): one finger in the folded half's upper band, one in its lower band drag right (>=40% of face width).\r\n// p (0-1) is the live fold progress; release at p>=.6 completes, else falls back. Touches within 24 px of a screen edge (40 px left in step 2) are ignored.\r\nexport const QUILT_SECRET={edge:24,edgeLeft2:40,min1:.35,min2:.4,done:.6,ms1:2500,ms2:8000};\r\nexport const quiltSecretInit=()=>({phase:'idle',pts:{},pair:null,p:0,claimed:[],until:0});\r\nexport function quiltSecret(s,ev,now){\r\n const Q=QUILT_SECRET;s={...s,pts:{...s.pts}};\r\n if(s.phase==='folded1'&&now>s.until)s={...s,phase:'idle',pair:null,p:0}; // window over: the quilt unfolds\r\n const base=s.phase==='fold2'||s.phase==='folded1'?'folded1':'idle',{x,y,face:f}=ev,id=String(ev.id);\r\n if(ev.type==='cancel'){return {...s,phase:s.phase==='done'?'done':base,pts:{},pair:null,p:s.phase==='done'?1:0};}\r\n if(s.phase==='done'||!f)return s;\r\n if(ev.type==='down'){\r\n  if(x<(base==='folded1'?Q.edgeLeft2:Q.edge)||x>ev.w-Q.edge||y<Q.edge||y>ev.h-Q.edge)return s;\r\n  s.pts[id]={x0:x,y0:y,x,y};\r\n  if(!s.pair&&(s.phase==='idle'||s.phase==='folded1')){\r\n   const zone=q=>{if(q.x0<f.left||q.x0>f.left+f.width)return null;const v=(q.y0-f.top)/f.height,l=q.x0<f.left+f.width/2;\r\n    return s.phase==='idle'?(v>.5&&v<=1?(l?'L':'R'):null):(v>=0&&v<.25?'U':v>=.25&&v<=.5?'D':null);};\r\n   const ids=Object.keys(s.pts),z=ids.map(k=>zone(s.pts[k])),want=s.phase==='idle'?['L','R']:['U','D'],a=z.indexOf(want[0]),b=z.indexOf(want[1]);\r\n   if(a>=0&&b>=0)s={...s,phase:s.phase==='idle'?'fold1':'fold2',pair:{a:ids[a],b:ids[b],t0:now},p:0,claimed:[ids[a],ids[b]]};\r\n  }\r\n  return s;\r\n }\r\n if(ev.type==='move'){\r\n  const q=s.pts[id];if(!q)return s;s.pts[id]={...q,x,y};\r\n  if(s.pair&&(id===s.pair.a||id===s.pair.b)){\r\n   const A=s.pts[s.pair.a],B=s.pts[s.pair.b];\r\n   s.p=s.phase==='fold1'?Math.max(0,Math.min(1,Math.min(A.y0-A.y,B.y0-B.y)/(Q.min1*f.height/Q.done))):Math.max(0,Math.min(1,Math.min(A.x-A.x0,B.x-B.x0)/(Q.min2*f.width/Q.done)));\r\n  }\r\n  return s;\r\n }\r\n if(ev.type==='up'){\r\n  delete s.pts[id];\r\n  if(s.pair&&(id===s.pair.a||id===s.pair.b)){\r\n   const ok=s.p>=Q.done&&(s.phase==='fold2'||now-s.pair.t0<=Q.ms1);delete s.pts[s.pair.a];delete s.pts[s.pair.b];\r\n   s=ok?(s.phase==='fold1'?{...s,phase:'folded1',until:now+Q.ms2,pair:null,p:1}:{...s,phase:'done',pair:null,p:1}):{...s,phase:base,pair:null,p:0};\r\n  }\r\n }\r\n return s;\r\n}\r\n// Rigid hinge bend of a point d along a flap (and z0 above it) folded by th about a hinge of radius R: [along, up]. th=pi lays the flap back over the stay half.\r\nexport function quiltBend(d,z0,th,R){const a=Math.min(d/R,th),r=d-R*a;return [R*Math.sin(a)+r*Math.cos(a)-z0*Math.sin(a),R*(1-Math.cos(a))+r*Math.sin(a)+z0*Math.cos(a)];}\r\n\r\nexport function quiltSourceToSurface(u,v,width,height){\r\n const {face}=quiltSurfaceLayout(width,height);return [face.left+u*face.width,face.top+v*face.height];\r\n}\r\n\r\n// Client coordinates are post-transform; the cloth solver is in the host's untransformed\r\n// CSS-pixel space.  A scaled portal therefore keeps presses underneath the visible finger.\r\nexport function clientToBoardLocal(clientX,clientY,clientRect,boardWidth,boardHeight){\r\n if(!clientRect.width||!clientRect.height)return [0,0];\r\n return [(clientX-clientRect.left)*boardWidth/clientRect.width,(clientY-clientRect.top)*boardHeight/clientRect.height];\r\n}\r\n\r\nfunction axisWithAnchors(segments,...anchors){\r\n const values=[0,1,...Array.from({length:segments-1},(_,i)=>(i+1)/segments),...anchors];\r\n return [...new Set(values.map(v=>Math.round(Math.min(1,Math.max(0,v))*1e8)/1e8))].sort((a,b)=>a-b);\r\n}\r\n\r\nfunction remapMargin(t,start,end,sourceStart,sourceEnd){\r\n if(t<=start)return start?sourceStart*t/start:sourceStart;\r\n if(t>=end)return end<1?sourceEnd+(1-sourceEnd)*(t-end)/(1-end):sourceEnd;\r\n return sourceStart+(sourceEnd-sourceStart)*(t-start)/(end-start);\r\n}\r\nfunction plainQuilt(){\r\n const canvas=document.createElement('canvas'),g=canvas.getContext('2d');canvas.width=IMAGE_W/2;canvas.height=IMAGE_H/2;g.scale(.5,.5);\r\n g.fillStyle='#3b3441';g.fillRect(0,0,IMAGE_W,IMAGE_H);g.fillStyle='#efe6d3';g.fillRect(22,22,IMAGE_W-44,IMAGE_H-44);\r\n const x=PATTERN.left*IMAGE_W,y=PATTERN.top*IMAGE_H,w=(PATTERN.right-PATTERN.left)*IMAGE_W,h=(PATTERN.bottom-PATTERN.top)*IMAGE_H;\r\n g.lineWidth=8;g.lineCap='round';g.setLineDash([18,10]);\r\n for(const [id,color] of STITCHES)for(const {points} of SHAPES[id]){\r\n  g.strokeStyle=color;g.beginPath();points.forEach(([u,v],i)=>g[i?'lineTo':'moveTo'](x+u*w,y+v*h));if(id==='oval')g.closePath();g.stroke();\r\n }\r\n return canvas;\r\n}\r\n\r\n// Measured fresh from the host box, so it stays correct before ResizeObserver runs and while the portal is CSS-scaled during a dive.\r\nfunction quiltRectOf(host){const box=host.getBoundingClientRect(),f=quiltSurfaceLayout(box.width,box.height).face;return {left:box.left+f.left,top:box.top+f.top,width:f.width,height:f.height};}\r\nfunction patternRectOf(host){const q=quiltRectOf(host);return {left:q.left+q.width*PATTERN.left,top:q.top+q.height*PATTERN.top,width:q.width*(PATTERN.right-PATTERN.left),height:q.height*(PATTERN.bottom-PATTERN.top)};}\r\n\r\n// The flat board: one still picture on a plain 2D canvas with a board's face layout, shape frame, cut hole and public\r\n// interface. portal.mjs mounts it first, as the base layer under the 3D board, and shows it whenever 3D isn't drawing\r\n// (no WebGL, a lost context, a stalled or broken download), so the portal is never a blank sheet.\r\n// Quilt (the default) draws its plain stitched quilt at once and its art when that arrives, so it cannot fail. A grimoire\r\n// board passes its poster (src), face width/height (ratio), shape frame ({x0,y0,x1,y1} face fractions) and guide, and\r\n// rejects when the poster doesn't load within waitMs, so the portal can keep the board it already shows.\r\n// trace (R7): the board's own touch effect in 2D (ice cracks, flowers, embers, weld, jelly gash) for when its 3D can't\r\n// draw: a factory for an effect with portal-board-glb's interface, given 2D paint/glow layers over the face (glow adds\r\n// light, as the 3D emissive does). The canvas redraws only while that effect animates.\r\n// ponytail: no cloth ripple, dent or falling piece; the cut just opens a hole.\r\nexport async function createQuiltBoard2D(host,{src=IMAGE,ratio=IMAGE_W/IMAGE_H,frame={x0:PATTERN.left,y0:PATTERN.top,x1:PATTERN.right,y1:PATTERN.bottom},background=BACKGROUND,guide=null,fallback=src===IMAGE?plainQuilt:null,waitMs=0,trace=null,tint=null,tintSelected=true,tintTarget='poster',backplateTint='#263943',backplateTintSelected=true}={}){\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n const art=new Promise((ok,fail)=>{const img=new Image();img.onload=()=>ok(img);img.onerror=()=>fail(new Error('Board art unavailable: '+src));img.src=src;if(waitMs)setTimeout(()=>fail(new Error('Board art timed out: '+src)),waitMs);});\r\n let image=fallback?.(),width=1,height=1,holes=[],disposed=false,_tint=(typeof tint==='string'&&/^#[0-9A-Fa-f]{6}$/.test(tint))?tint:null,_traceTint=_tint,_traceSelected=!!tintSelected,_backplateTint=(typeof backplateTint==='string'&&/^#[0-9A-Fa-f]{6}$/.test(backplateTint))?backplateTint:'#263943',_backplateSelected=!!backplateTintSelected;\r\n if(image)art.then(img=>{image=img;draw();},()=>{});else image=await art;\r\n const canvas=document.createElement('canvas'),g=canvas.getContext('2d'); // no 2D context: a bare face, but the frame, layout and cuts still work\r\n canvas.className='portal-board-canvas';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='position:absolute;inset:0;display:block;width:100%;height:100%';host.append(canvas);\r\n const faceOf=boardLayout;\r\n const rectOf=()=>{const box=host.getBoundingClientRect(),f=faceOf(box.width,box.height);return {left:box.left+f.left,top:box.top+f.top,width:f.width,height:f.height};};\r\n // The poster and its guides. A board with a trace draws them once per size into `still` and blits that on every trace\r\n // frame: re-drawing the poster and the blurred guide strokes each frame starved a phone's main thread (and a test's rAF).\r\n let still=null;\r\nconst tintCache=new Map();\r\n function paintStill(c,face){\r\n  if(_tint){\r\n    const tw=Math.max(1,Math.round(face.width)),th=Math.max(1,Math.round(face.height));\r\n    const metal=!!trace?.metal&&_traceSelected,cogs=tintTarget==='cogs',key=`${image.src}-${tw}x${th}-${_tint}-${metal}-${cogs}-${_backplateTint}-${_backplateSelected}`;\r\n    let off;\r\n    if(tintCache.has(key)){\r\n      off=tintCache.get(key);\r\n    }else{\r\n      const tmp=document.createElement('canvas');\r\n      tmp.width=tw;tmp.height=th;\r\n      const tc=tmp.getContext('2d');\r\n      tc.drawImage(image,0,0,tw,th);\r\n      const imgData=tc.getImageData(0,0,tw,th);\r\n      const data=imgData.data;\r\n      const tintRGB=parseInt(_tint.slice(1),16);\r\n      const tr=(tintRGB>>16)&255, tg=(tintRGB>>8)&255, tb=tintRGB&255;\r\n      for(let i=0;i<data.length;i+=4){\r\n        const a=data[i+3];\r\n        if(a===0)continue;\r\n        const lum=data[i]*.2126+data[i+1]*.7152+data[i+2]*.0722;\r\n        if(cogs){\r\n         const hi=Math.max(data[i],data[i+1],data[i+2]),lo=Math.min(data[i],data[i+1],data[i+2]),chroma=hi-lo,l=lum/255;\r\n         if(chroma<42&&l>.055&&l<.49){ // muted dark panels in the poster stand in for the separate backplate\r\n          const rgb=parseInt(_backplateTint.slice(1),16),k=.28+.78*l;\r\n          data[i]=Math.min(255,((rgb>>16)&255)*k);data[i+1]=Math.min(255,((rgb>>8)&255)*k);data[i+2]=Math.min(255,(rgb&255)*k);\r\n         }else if(chroma>=42){ // colored gear teeth, pipes and lamps share the mechanism tint, including its default\r\n          const l0=lum/255,k=.12+1.35*l0,s=l0>.55?((l0-.55)/.45)**2*.7:0;\r\n          data[i]=Math.min(255,tr*k)*(1-s)+255*s;data[i+1]=Math.min(255,tg*k)*(1-s)+255*s;data[i+2]=Math.min(255,tb*k)*(1-s)+255*s;\r\n         }\r\n         continue;\r\n        }\r\n        if(metal){ // anodized metal (Cogs): deeper shadows, tint through the mids, a white-hot specular top\r\n         const l=lum/255,k=.12+1.35*l,s=l>.55?((l-.55)/.45)**2*.7:0;\r\n         data[i]=Math.min(255,tr*k)*(1-s)+255*s;data[i+1]=Math.min(255,tg*k)*(1-s)+255*s;data[i+2]=Math.min(255,tb*k)*(1-s)+255*s;\r\n         continue;\r\n        }\r\n        data[i]=lum*tr/255;\r\n        data[i+1]=lum*tg/255;\r\n        data[i+2]=lum*tb/255;\r\n      }\r\n      tc.putImageData(imgData,0,0);\r\n      off=tmp;\r\n      tintCache.clear();\r\ntintCache.set(key,tmp);\r\n    }\r\n    c.drawImage(off,face.left,face.top,face.width,face.height);\r\n  } else {\r\n    c.drawImage(image,face.left,face.top,face.width,face.height);\r\n  }\r\n  // A GLB board's guides are carved or painted in 3D, not in its flat poster: draw the same shape paths in its frame.\r\n  if(guide){\r\n   const trace=()=>{for(const [id,polys] of Object.entries(SHAPES)){if(id==='line')continue;for(const {points} of polys){c.beginPath();points.forEach(([u,v],i)=>{const [fu,fv]=fromFrame(u,v,frame);c[i?'lineTo':'moveTo'](face.left+fu*face.width,face.top+fv*face.height);});c.stroke();}}};\r\n   const alpha=Math.max(guide.alpha||0,.42);c.strokeStyle=guide.color||'#fff';c.lineCap=c.lineJoin='round';\r\n   c.shadowColor=c.strokeStyle;c.shadowBlur=8;c.globalAlpha=alpha*.65;c.lineWidth=5;trace();c.shadowBlur=0;c.globalAlpha=alpha;c.lineWidth=2;trace();\r\n  }\r\n }\r\n function draw(){\r\n  if(disposed||!g)return;\r\n  const scale=Math.min(devicePixelRatio||1,2),face=faceOf(width,height),W=Math.round(width*scale),H=Math.round(height*scale);\r\n  if(canvas.width!==W||canvas.height!==H){canvas.width=W;canvas.height=H;}else{g.setTransform(1,0,0,1,0,0);g.clearRect(0,0,W,H);} // resizing also clears\r\n  if(fx&&(still?.width!==W||still.height!==H||still.image!==image)){still=Object.assign(document.createElement('canvas'),{width:W,height:H,image});const c=still.getContext('2d');c.setTransform(scale,0,0,scale,0,0);paintStill(c,face);}\r\n  g.setTransform(scale,0,0,scale,0,0);g.save();\r\n  if(holes.length){g.beginPath();g.rect(0,0,width,height);for(const poly of holes){poly.forEach(([u,v],i)=>g[i?'lineTo':'moveTo'](face.left+u*face.width,face.top+v*face.height));g.closePath();}g.clip('evenodd');}\r\n  if(fx)g.drawImage(still,0,0,width,height);else paintStill(g,face);\r\n  if(fx){ // paint, a dark silhouette of the glow (so light glow still reads on a pale poster like Ice's), then the glow as light\r\n   const put=c=>g.drawImage(c,face.left,face.top,face.width,face.height);\r\n   g.globalAlpha=1;g.shadowBlur=0;put(fx.paint.canvas);g.filter='brightness(0)';g.globalAlpha=.8;put(fx.glow.canvas);g.filter='none';g.globalAlpha=1;g.globalCompositeOperation='lighter';put(fx.glow.canvas);\r\n  }\r\n  g.restore();\r\n }\r\n function layout(box={width:host.clientWidth,height:host.clientHeight}){\r\n  if(!box.width||!box.height)return; // hidden (display:none) reads 0x0: keep the last layout\r\n  width=box.width;height=box.height;\r\n  if(canvas.style.visibility!=='hidden')for(const [k,v] of Object.entries(faceOf(width,height)))host.style.setProperty('--face-'+k,v+'px'); // under a live 3D board, that board owns the frame\r\n  draw();\r\n }\r\n // The 2D trace: its layers TRACE_PX wide in face coords (u right, v down) like the 3D board's, redrawn by rAF while it runs.\r\n let fx=null,raf=0,last=0;const pointers=new Map();\r\n function tick(now){raf=0;if(disposed||!fx)return;const on=fx.step?.(Math.min(1/30,Math.max(1/240,(now-last)/1000)),now);last=now;draw();if(on||pointers.size)raf=requestAnimationFrame(tick);}\r\n const wake=()=>{if(!raf&&fx&&!disposed){last=performance.now();raf=requestAnimationFrame(tick);}};\r\n const observer=new ResizeObserver(entries=>layout(entries.at(-1).contentRect));observer.observe(host);layout();\r\n if(trace&&g){\r\n  const layer=()=>{const c=document.createElement('canvas');c.width=TRACE_PX;c.height=Math.max(1,Math.round(TRACE_PX/ratio));return {canvas:c,ctx:c.getContext('2d'),texture:{}};};\r\n  const toWorld=(u,v)=>{const f=faceOf(width,height);return [f.left+u*f.width,-(f.top+v*f.height)];};\r\n  const next=trace(),paint=layer(),glow=layer();\r\n  await next.init({paint,glow,face:{w:1,h:1/ratio},toWorld,wake});\r\n  fx=Object.assign(next,{paint,glow});if(_traceTint&&/^#[0-9a-f]{6}$/i.test(_traceTint))fx.setTint?.(_traceTint,_traceSelected);draw(); // the still is ready before the first touch\r\n }\r\n return {\r\n  canvas,background,faceRect:rectOf,quiltRect:rectOf,\r\n  patternRect(){const q=rectOf();return {left:q.left+q.width*frame.x0,top:q.top+q.height*frame.y0,width:q.width*(frame.x1-frame.x0),height:q.height*(frame.y1-frame.y0)};},\r\n  cut(poly,color,ms=1100){holes=[poly];draw();return new Promise(done=>setTimeout(done,reduced?0:ms));},\r\n  heal(){if(holes.length){holes=[];draw();}},\r\n  setTint(hex,selected=true){\r\n   if(!/^#[0-9a-f]{6}$/i.test(hex))return;\r\n   _traceTint=hex;_traceSelected=selected;fx?.setTint?.(hex,selected);\r\n   if(tintTarget==='trace')return;\r\n   _tint=hex;\r\n   still=null;\r\n   draw();\r\n  },\r\n  setBackplateTint(hex,selected=true){\r\n   if(!/^#[0-9a-f]{6}$/i.test(hex))return;\r\n   _backplateTint=selected?hex:'#263943';_backplateSelected=selected;\r\n   still=null;draw();\r\n  },\r\n  press(id,x,y){\r\n   if(!fx)return;const r=rectOf(),u=Math.min(1,Math.max(0,(x-r.left)/r.width)),v=Math.min(1,Math.max(0,(y-r.top)/r.height)),p=pointers.get(id);\r\n   if(p){fx.move?.(id,u,v,p.u,p.v);p.u=u;p.v=v;}else{pointers.set(id,{u,v});fx.press?.(id,u,v);}\r\n   wake();\r\n  },\r\n  claims(){return false;},\r\n  release(id){const p=pointers.get(id);if(p){pointers.delete(id);fx.release?.(id,p.u,p.v);wake();}},\r\n  frameMs:()=>0,\r\n  pause(){for(const [id,p] of pointers)fx.release?.(id,p.u,p.v);pointers.clear();cancelAnimationFrame(raf);raf=0;},\r\n  resume(){layout();wake();}, // back on screen (or back from under a 3D board): re-take the frame\r\n  dispose(){disposed=true;cancelAnimationFrame(raf);observer.disconnect();fx?.dispose?.();canvas.remove();},\r\n };\r\n}\r\n\r\n// The WebGL cloth quilt (portal.mjs lays it over the flat quilt once it has drawn).\r\nexport async function createQuiltBoardGL(host,{knobs=QUILT}={}){\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n // The art first, the renderer after: a stalled download never holds a WebGL context.\r\n let texture;\r\n try{texture=await new THREE.TextureLoader().loadAsync(IMAGE);}\r\n catch{texture=new THREE.CanvasTexture(plainQuilt());}\r\n // Transparent canvas; BACKGROUND goes on #portalHome (board.background) so the neon glass shows only through a cut.\r\n const renderer=new THREE.WebGLRenderer({antialias:true,alpha:true,powerPreference:'low-power'});\r\n renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.setClearColor(0x000000,0);\r\n const canvas=renderer.domElement;canvas.className='portal-board-canvas';canvas.setAttribute('aria-hidden','true');canvas.style.cssText='display:block;width:100%;height:100%';host.append(canvas);\r\n let observer,geometry,material,pieceMat,cutMask=null,frame=0,disposed=false; // outside the try: a failed build must also stop its frame loop\r\n try{ // a failure after the renderer exists must not leave its canvas and context behind\r\n texture.colorSpace=THREE.SRGBColorSpace;texture.anisotropy=4;\r\n const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(42,1,1,20000);\r\n scene.add(new THREE.HemisphereLight(0xfff4e6,0x3a2f40,1.1));\r\n const sun=new THREE.DirectionalLight(0xfff0dc,2.4);sun.position.set(-.7,.55,.45);scene.add(sun);\r\n material=new THREE.MeshStandardMaterial({map:texture,roughness:.95,metalness:0,side:THREE.DoubleSide});\r\n const cutSide={value:0},cutMaskUniform={value:null};material.userData.portalCutSideUniform=cutSide;\r\n material.onBeforeCompile=shader=>{\r\n  shader.uniforms.uPortalCutMask=cutMaskUniform;shader.uniforms.uPortalCutSide=cutSide;\r\n  shader.vertexShader=shader.vertexShader.replace('#include <common>','#include <common>\\nattribute vec2 portalCutUv;\\nvarying vec2 vPortalCutUv;')\r\n   .replace('#include <begin_vertex>','#include <begin_vertex>\\nvPortalCutUv=portalCutUv;');\r\n  shader.fragmentShader=shader.fragmentShader.replace('#include <common>','#include <common>\\nvarying vec2 vPortalCutUv;\\nuniform sampler2D uPortalCutMask;\\nuniform float uPortalCutSide;')\r\n   .replace('#include <map_fragment>','#include <map_fragment>\\nif(!gl_FrontFacing){vec2 q=vPortalCutUv;float bind=step(q.x,.022)+step(.978,q.x)+step(q.y,.015)+step(.985,q.y);float lum=dot(diffuseColor.rgb,vec3(.3,.59,.11));vec3 back=mix(vec3(lum),diffuseColor.rgb,.4)*vec3(.62,.58,.55);vec2 cell=fract(q*vec2(12.,19.5));float edge=min(min(cell.x,1.-cell.x),min(cell.y,1.-cell.y)),puff=sin(cell.x*3.14159)*sin(cell.y*3.14159);float stitch=(1.-smoothstep(.015,.05,edge))*step(.35,fract((q.x+q.y)*150.));back*=.72+.38*puff;back=mix(back,back*.3,(1.-smoothstep(.0,.09,edge))*.55);back=mix(back,vec3(.62,.55,.46),stitch*.8);diffuseColor.rgb=mix(back,vec3(.04,.035,.045),min(bind,1.));}')\r\n   .replace('#include <opaque_fragment>','if(!gl_FrontFacing)outgoingLight=max(outgoingLight,diffuseColor.rgb*.6);\\n#include <opaque_fragment>')\r\n   .replace('#include <alphatest_fragment>','float portalCutCoverage=1.0;if(abs(uPortalCutSide)>.5){portalCutCoverage=texture2D(uPortalCutMask,vPortalCutUv).r;if(uPortalCutSide>.5&&portalCutCoverage<.5)discard;if(uPortalCutSide<-.5&&portalCutCoverage>=.5)discard;}\\n#include <alphatest_fragment>');\r\n };\r\n material.customProgramCacheKey=()=> 'portal-quilt-cut-mask-v2';\r\n const segX=knobs.segX,segY=Math.round(segX*IMAGE_H/IMAGE_W);\r\n // Source-pattern anchor vertices stay in the topology.  layout() moves them to the\r\n // current pattern boundary, so resizing or rotation never interpolates across a stitch edge.\r\n const xAxis=axisWithAnchors(segX,PATTERN.left,PATTERN.right),yAxis=axisWithAnchors(segY,PATTERN.top,PATTERN.bottom),cols=xAxis.length,rows=yAxis.length,count=cols*rows;\r\n const pos=new Float32Array(count*3),prev=new Float32Array(count*3),rest=new Float32Array(count*3),invMass=new Float32Array(count),uv=new Float32Array(count*2),index=[];\r\n for(let j=0;j<rows;j++)for(let i=0;i<cols;i++){const n=j*cols+i;invMass[n]=i===0||j===0||i===cols-1||j===rows-1?0:1;}\r\n for(let j=0;j<rows-1;j++)for(let i=0;i<cols-1;i++){const a=j*cols+i,b=a+1,c=a+cols,d=c+1;index.push(a,c,b,b,c,d);}\r\n // Stretch constraints along every triangle edge; bending between the two far corners of each triangle pair.\r\n const edges=new Map();\r\n for(let t=0;t<index.length;t+=3)for(let k=0;k<3;k++){const a=index[t+k],b=index[t+(k+1)%3],o=index[t+(k+2)%3],key=Math.min(a,b)*count+Math.max(a,b);(edges.get(key)||edges.set(key,{a,b,far:[]}).get(key)).far.push(o);}\r\n const stretch=[],bend=[];for(const e of edges.values()){stretch.push(e.a,e.b);if(e.far.length===2)bend.push(e.far[0],e.far[1]);}\r\n const stretchIds=new Int32Array(stretch),bendIds=new Int32Array(bend),stretchLen=new Float32Array(stretch.length/2),bendLen=new Float32Array(bend.length/2);\r\n const cutUv=new Float32Array(count*2);\r\n geometry=new THREE.BufferGeometry();geometry.setAttribute('position',new THREE.BufferAttribute(pos,3));geometry.setAttribute('uv',new THREE.BufferAttribute(uv,2));geometry.setAttribute('portalCutUv',new THREE.BufferAttribute(cutUv,2));geometry.setIndex(index);\r\n const mesh=new THREE.Mesh(geometry,material);mesh.frustumCulled=false;scene.add(mesh);\r\n // Transparent twin for cut pieces, drawn once at load (opacity 0 over the quilt: invisible) so the first cut doesn't hitch on a compile.\r\n pieceMat=pieceMaterial(material);const pieceSide={value:0},sourceCompile=pieceMat.onBeforeCompile;pieceMat.userData.portalCutSideUniform=pieceSide;pieceMat.onBeforeCompile=function(shader,renderer){sourceCompile.call(this,shader,renderer);shader.uniforms.uPortalCutMask=cutMaskUniform;shader.uniforms.uPortalCutSide=pieceSide;};const warm=new THREE.Mesh(geometry,pieceMat);pieceMat.opacity=0;warm.frustumCulled=false;scene.add(warm);\r\n const fullIndex=geometry.index;let cutting=null;\r\n\r\n let width=1,height=1,surface=quiltSurfaceLayout(1,1),paused=false,awakeUntil=0,last=0,frameMs=0;\r\n const pointers=new Map();\r\n\r\n // Vault secret: fold state (see quiltSecret). vis = on-screen fold progress of step 1/2 (follows the fingers, eases otherwise).\r\n let qs=quiltSecretInit(),slide=0,slid=false,wasFolded=false,hing={x:0,y:0};const vis=[0,0],seen=new Set(),claimedIds=new Set(),R1=3,R2=10;\r\n const shadowTex=(()=>{const c=document.createElement('canvas');c.width=c.height=128;const g=c.getContext('2d');g.shadowColor='#140a1e';g.shadowBlur=22;g.shadowOffsetX=1000;g.fillStyle='#000';g.fillRect(-1000-60,24,1000+60+128+60,200);return new THREE.CanvasTexture(c);})();\r\n const shadow=new THREE.Mesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({map:shadowTex,transparent:true,depthWrite:false,opacity:0}));shadow.visible=false;shadow.frustumCulled=false;scene.add(shadow);\r\n // Untransformed size (the observer's contentRect, else clientWidth): the portal can be re-shown mid-dive, scaled.\r\n function layout(box={width:host.clientWidth,height:host.clientHeight}){\r\n  width=Math.max(1,box.width);height=Math.max(1,box.height);\r\n  surface=quiltSurfaceLayout(width,height);const {face,pattern}=surface;\r\n  for(const k in face)host.style.setProperty('--face-'+k,face[k]+'px'); // the portal's metal frame (#111) wraps the full cloth\r\n  for(let j=0;j<rows;j++)for(let i=0;i<cols;i++){\r\n   const n=j*cols+i,p=3*n,u=xAxis[i],v=yAxis[j];\r\n   rest[p]=face.left+u*face.width;\r\n   rest[p+1]=-(face.top+v*face.height);rest[p+2]=0;\r\n   uv[2*n]=u;uv[2*n+1]=1-v;cutUv[2*n]=u;cutUv[2*n+1]=v;\r\n  }\r\n  pos.set(rest);prev.set(rest);\r\n  {let by=1/0,bx=1/0;for(let j=0;j<rows;j++){const y=rest[3*j*cols+1];if(Math.abs(y+face.top+face.height/2)<by){by=Math.abs(y+face.top+face.height/2);hing.y=y;}}for(let i=0;i<cols;i++){const x=rest[3*i];if(Math.abs(x-face.left-face.width/2)<bx){bx=Math.abs(x-face.left-face.width/2);hing.x=x;}}} // hinge lines sit on the nearest grid row/column to the face centre\r\n  const dist=(ids,k,a=ids[2*k],b=ids[2*k+1])=>Math.hypot(rest[3*a]-rest[3*b],rest[3*a+1]-rest[3*b+1]);\r\n  for(let k=0;k<stretchLen.length;k++)stretchLen[k]=dist(stretchIds,k);for(let k=0;k<bendLen.length;k++)bendLen[k]=dist(bendIds,k);\r\n  renderer.setSize(width,height,false);camera.aspect=width/height;\r\n  // World units are CSS pixels on the z=0 plane: x right, y up (screen y negated).\r\n  camera.position.set(width/2,-height/2,(height/2)/Math.tan(THREE.MathUtils.degToRad(camera.fov/2)));camera.near=camera.position.z/10;camera.far=camera.position.z*10;camera.lookAt(width/2,-height/2,0);camera.updateProjectionMatrix();\r\n  geometry.attributes.position.needsUpdate=true;geometry.attributes.uv.needsUpdate=true;geometry.attributes.portalCutUv.needsUpdate=true;geometry.computeVertexNormals();wake();\r\n }\r\n\r\n const folded=()=>vis[0]>0||vis[1]>0||slide>0,qBusy=()=>qs.phase!=='idle'||folded()&&!(slid&&qs.phase==='done');\r\n function applyFold(){\r\n  const a1=vis[0]*Math.PI,a2=vis[1]*Math.PI,f=surface.face,ox=slide*(width-hing.x+60);\r\n  for(let n=0;n<count;n++){const p=3*n;let x=rest[p],y=rest[p+1],z=0;\r\n   if(a1>0&&y<hing.y-.01){const b=quiltBend(hing.y-y,0,a1,R1);y=hing.y-b[0];z=b[1];}\r\n   if(a2>0&&x<hing.x-.01){const b=quiltBend(hing.x-x,z,a2,R2);x=hing.x-b[0];z=b[1];}\r\n   pos[p]=prev[p]=x+ox;pos[p+1]=prev[p+1]=y;pos[p+2]=prev[p+2]=z*1.5;} // z stretched 1.5x so a lifted flap shows real perspective\r\n  // soft shadow on the half under the lifted flap: a gradient plane, hard at the hinge, fading toward the flap's far end\r\n  const two=a2>.01,a=two?a2:a1,R=two?R2:R1,far=two?hing.x-f.left:hing.y+f.top+f.height,ext=-quiltBend(far,two?R1*2:0,a,R)[0],ok=a>.01&&ext>1&&!slide;\r\n  shadow.visible=ok;if(!ok)return;\r\n  shadow.material.opacity=.7*Math.sin(a);const L=(ext*1.15+24)/.8125;\r\n  if(two){shadow.rotation.z=-Math.PI/2;shadow.scale.set(-f.top-hing.y,L,1);shadow.position.set(hing.x+L/2,(hing.y-f.top)/2,7.5);} // stay half = top-right quarter: y from face top to the hinge\r\n  else{shadow.rotation.z=0;shadow.scale.set(f.width,L,1);shadow.position.set(f.left+f.width/2,hing.y+L/2,1);}\r\n }\r\n function qAnim(dt,now){\r\n  qs=quiltSecret(qs,{type:'tick'},now);const ph=qs.phase,t=[ph==='idle'?0:ph==='fold1'?qs.p:1,ph==='fold2'?qs.p:ph==='done'?1:0];\r\n  for(let i=0;i<2;i++){const live=ph===(i?'fold2':'fold1');vis[i]=reduced||live&&Math.abs(t[i]-vis[i])<.5?t[i]:vis[i]+Math.sign(t[i]-vis[i])*Math.min(Math.abs(t[i]-vis[i]),dt*(live?6:3.2));}\r\n  if(ph==='done'&&vis[1]>=.999){slide=reduced?1:Math.min(1,slide+dt/.7);if(slide>=1&&!slid){slid=true;dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:'quilt'}}));}}\r\n  else if(ph!=='done')slide=0;\r\n }\r\n function qEv(ev){\r\n  qs=quiltSecret(qs,{...ev,face:quiltRect(),w:innerWidth,h:innerHeight},performance.now());qs.claimed.forEach(i=>claimedIds.add(i));\r\n  if(qs.pair){pointers.delete(+qs.pair.a);pointers.delete(+qs.pair.b);}wake();\r\n }\r\n function resetSecret(){\r\n  const was=wasFolded||qs.phase!=='idle'||folded();qs=quiltSecretInit();vis[0]=vis[1]=slide=0;slid=false;seen.clear();claimedIds.clear();shadow.visible=false;wasFolded=false;\r\n  if(was&&!disposed){pos.set(rest);prev.set(rest);geometry.attributes.position.needsUpdate=true;geometry.computeVertexNormals();renderer.render(scene,camera);}\r\n }\r\n function solve(ids,lengths,alpha){\r\n  for(let k=0;k<lengths.length;k++){\r\n   const a=ids[2*k],b=ids[2*k+1],wa=invMass[a],wb=invMass[b],w=wa+wb;if(!w)continue;\r\n   const dx=pos[3*a]-pos[3*b],dy=pos[3*a+1]-pos[3*b+1],dz=pos[3*a+2]-pos[3*b+2],len=Math.hypot(dx,dy,dz);if(!len)continue;\r\n   const s=-(len-lengths[k])/(w+alpha)/len;\r\n   pos[3*a]+=dx*s*wa;pos[3*a+1]+=dy*s*wa;pos[3*a+2]+=dz*s*wa;pos[3*b]-=dx*s*wb;pos[3*b+1]-=dy*s*wb;pos[3*b+2]-=dz*s*wb;\r\n  }\r\n }\r\n function step(dt){\r\n  const sdt=dt/knobs.substeps,alpha=knobs.compliance/sdt/sdt,pull=1-Math.exp(-knobs.restore*sdt),r2=knobs.radius*knobs.radius;\r\n  for(let s=0;s<knobs.substeps;s++){\r\n   for(let n=0;n<count;n++){if(!invMass[n])continue;const p=3*n;\r\n    for(let c=0;c<3;c++){const v=(pos[p+c]-prev[p+c])*knobs.damping;prev[p+c]=pos[p+c];pos[p+c]+=v+(rest[p+c]-pos[p+c])*pull;}\r\n   }\r\n   // A finger presses the fabric in and drags it a little along the stroke.\r\n   for(const touch of pointers.values()){\r\n    const tx=touch.x,ty=-touch.y,mx=(touch.x-touch.px)*knobs.drag/knobs.substeps,my=-(touch.y-touch.py)*knobs.drag/knobs.substeps;\r\n    for(let n=0;n<count;n++){if(!invMass[n])continue;const p=3*n,dx=pos[p]-tx,dy=pos[p+1]-ty,d2=dx*dx+dy*dy;if(d2>r2)continue;\r\n     const f=(1-Math.sqrt(d2)/knobs.radius)**2;pos[p+2]=Math.min(pos[p+2],pos[p+2]+(-knobs.depth*f-pos[p+2])*.5);pos[p]+=mx*f;pos[p+1]+=my*f;}\r\n   }\r\n   solve(stretchIds,stretchLen,alpha);solve(bendIds,bendLen,alpha*4);\r\n  }\r\n  for(const touch of pointers.values()){touch.px=touch.x;touch.py=touch.y;}\r\n }\r\n function wake(){awakeUntil=performance.now()+(reduced?0:knobs.sleepMs);if(!frame&&!disposed&&!paused){last=performance.now();frame=requestAnimationFrame(tick);}}\r\n function tick(now){\r\n  frame=0;if(disposed||paused)return;\r\n  const dt=Math.min(1/30,Math.max(1/240,(now-last)/1000));last=now;\r\n  if(!document.hidden){const t=performance.now();qAnim(dt,now);if(folded()){applyFold();wasFolded=true;}else{if(wasFolded){wasFolded=false;shadow.visible=false;pos.set(rest);prev.set(rest);}if(!reduced)step(dt);}if(cutting?.fall.live)cutting.fall.pose(now);geometry.attributes.position.needsUpdate=true;geometry.computeVertexNormals();renderer.render(scene,camera);frameMs=frameMs*.9+(performance.now()-t)*.1;}\r\n  if(pointers.size||now<awakeUntil||cutting?.fall.live||qBusy())frame=requestAnimationFrame(tick);\r\n }\r\n const local=(x,y)=>clientToBoardLocal(x,y,host.getBoundingClientRect(),width,height);\r\n // Hidden (display:none) reads as 0x0: keep the last layout rather than shrink the renderer and reset the cloth, only to\r\n // rebuild both at full size the moment the quilt shows again.\r\n observer=new ResizeObserver(entries=>{const box=entries.at(-1).contentRect;if(box.width&&box.height)layout(box);});observer.observe(host);layout();renderer.render(scene,camera);scene.remove(warm);\r\n const quiltRect=()=>quiltRectOf(host);\r\n // Both the cloth and its falling snapshot retain boundary triangles. A shared local face-space\r\n // mask discards complementary pixels, so cut quality does not depend on the cloth grid density.\r\n function cut(poly,color,ms=1100){\r\n  if(reduced)ms=0;\r\n  heal();\r\n  cutMask=createPortalCutMask(poly);cutMaskUniform.value=cutMask;cutSide.value=-1;pieceSide.value=1;\r\n  const P=pos.slice(),U=uv.slice(),C=cutUv.slice(),pieceGeometry=new THREE.BufferGeometry();\r\n  pieceGeometry.setAttribute('position',new THREE.Float32BufferAttribute(P,3));pieceGeometry.setAttribute('uv',new THREE.Float32BufferAttribute(U,2));pieceGeometry.setAttribute('portalCutUv',new THREE.Float32BufferAttribute(C,2));pieceGeometry.setIndex(new THREE.BufferAttribute(fullIndex.array.slice(),1));pieceGeometry.computeVertexNormals();\r\n  let u=0,v=0;for(const point of poly){u+=point[0];v+=point[1];}u/=poly.length;v/=poly.length;\r\n  const cx=surface.face.left+u*surface.face.width,cy=-(surface.face.top+v*surface.face.height),cz=0;\r\n  const piece=new THREE.Mesh(pieceGeometry,pieceMat),pivot=new THREE.Group();piece.frustumCulled=false;piece.position.set(-cx,-cy,-cz);pivot.position.set(cx,cy,cz);pivot.add(piece);scene.add(pivot);\r\n  const pieces=[{pivot,material:pieceMat,drop(){scene.remove(pivot);pieceGeometry.dispose();}}];\r\n  cutting={fall:fallPieces(pieces,height*.35,ms)};wake();\r\n  return cutting.fall.done;\r\n }\r\n function heal(){\r\n  resetSecret();\r\n  if(!cutting)return;\r\n  cutting.fall.end();cutSide.value=0;pieceSide.value=0;cutMaskUniform.value=null;cutMask?.dispose();cutMask=null;cutting=null;\r\n  if(!disposed){geometry.computeVertexNormals();renderer.render(scene,camera);} // healed frame on the canvas now, even while paused\r\n }\r\n return {\r\n  canvas,\r\n  glContext:renderer.getContext(),\r\n  background:BACKGROUND,\r\n  faceRect:quiltRect,\r\n  cut,heal,\r\n  // Stitched-shape area in client pixels; the portal normalises traces against it.\r\n  patternRect:()=>patternRectOf(host),\r\n  quiltRect,\r\n  press(id,clientX,clientY){const k=String(id),first=!seen.has(id);if(first)claimedIds.delete(k);seen.add(id);qEv({type:first?'down':'move',id,x:clientX,y:clientY});if(qs.pair&&(k===qs.pair.a||k===qs.pair.b))return;if(reduced)return;const [x,y]=local(clientX,clientY),touch=pointers.get(id);if(touch){touch.x=x;touch.y=y;}else pointers.set(id,{x,y,px:x,py:y});wake();},\r\n  claims:id=>claimedIds.has(String(id)),\r\n  release(id){seen.delete(id);qEv({type:'up',id});pointers.delete(id);wake();},\r\n  // test/preview: drive a fold step through the real reducer (p 0-1 of the gesture; release=true lifts the fingers). Step 2 needs step 1 done first.\r\n  secretDebug:{cancel:()=>qEv({type:'cancel'}),state:()=>qs,vis:()=>[...vis,slide],\r\n   fold(step,p,release=true){const f=quiltRect(),A=9001,B=9002,s=step===1,pts=s?[[.25,.85],[.75,.85]]:[[.3,.1],[.3,.35]],Q=QUILT_SECRET,d=p*(s?Q.min1*f.height:Q.min2*f.width)/Q.done;\r\n    const at=(k,dx,dy)=>[f.left+f.width*pts[k][0]+dx,f.top+f.height*pts[k][1]+dy];\r\n    for(const [k,id] of [[0,A],[1,B]]){seen.add(id);claimedIds.delete(String(id));qEv({type:'down',id,x:at(k,0,0)[0],y:at(k,0,0)[1]});}\r\n    for(const [k,id] of [[0,A],[1,B]])qEv({type:'move',id,x:at(k,s?0:d,s?-d:0)[0],y:at(k,s?0:d,s?-d:0)[1]});\r\n    if(release)for(const id of [A,B]){seen.delete(id);qEv({type:'up',id});}}},\r\n  frameMs:()=>frameMs,\r\n  pause(){paused=true;pointers.clear();if(qs.pair)qEv({type:'cancel'});cancelAnimationFrame(frame);frame=0;},\r\n  resume(){paused=false;wake();},\r\n  dispose(){cutting?.fall.end();cutMask?.dispose();cutMask=null;pieceMat.dispose();disposed=true;cancelAnimationFrame(frame);observer.disconnect();geometry.dispose();material.dispose();texture.dispose();renderer.dispose();renderer.forceContextLoss();canvas.remove();},\r\n };\r\n }catch(error){disposed=true;cancelAnimationFrame(frame);observer?.disconnect();pieceMat?.dispose();geometry?.dispose();material?.dispose();texture?.dispose();renderer.dispose();renderer.forceContextLoss();canvas.remove();throw error;}\r\n}\r\n","modules/portal/portal-cut.mjs":"import * as THREE from 'three';\r\n\r\n// One antialiased face-space mask defines both sides of a portal cut. Drawing the polygon once and\r\n// sampling it in each material keeps the board opening and the piece edge complementary even when\r\n// the falling piece translates, tilts, or scales in its own local space.\r\nexport function createPortalCutMask(poly,size=1024){\r\n const canvas=document.createElement('canvas');canvas.width=canvas.height=size;\r\n const ctx=canvas.getContext('2d');ctx.clearRect(0,0,size,size);ctx.fillStyle='#fff';ctx.beginPath();\r\n poly.forEach(([u,v],i)=>i?ctx.lineTo(u*size,v*size):ctx.moveTo(u*size,v*size));ctx.closePath();ctx.fill();\r\n const texture=new THREE.CanvasTexture(canvas);texture.flipY=false;texture.colorSpace=THREE.NoColorSpace;texture.minFilter=THREE.LinearFilter;texture.magFilter=THREE.LinearFilter;texture.generateMipmaps=false;texture.needsUpdate=true;\r\n return texture;\r\n}\r\n\r\nexport function pointInPolygon(u,v,poly){\r\n let inside=false;\r\n for(let i=0,j=poly.length-1;i<poly.length;j=i++){\r\n  const [xi,yi]=poly[i],[xj,yj]=poly[j];\r\n  if((yi>v)!==(yj>v)&&u<(xj-xi)*(v-yi)/(yj-yi)+xi)inside=!inside;\r\n }\r\n return inside;\r\n}\r\n\r\nexport function splitIndexByPolygon(positions,index,toUv,poly){\r\n const keep=[],cut=[],us=poly.map(p=>p[0]),vs=poly.map(p=>p[1]),u0=Math.min(...us),u1=Math.max(...us),v0=Math.min(...vs),v1=Math.max(...vs);\r\n for(let t=0;t<index.length;t+=3){\r\n  const a=3*index[t],b=3*index[t+1],c=3*index[t+2],[u,v]=toUv((positions[a]+positions[b]+positions[c])/3,(positions[a+1]+positions[b+1]+positions[c+1])/3);\r\n  (u>=u0&&u<=u1&&v>=v0&&v<=v1&&pointInPolygon(u,v,poly)?cut:keep).push(index[t],index[t+1],index[t+2]);\r\n }\r\n return {keep,cut};\r\n}\r\n\r\nexport function pieceMaterial(src){const m=src.clone();m.onBeforeCompile=src.onBeforeCompile;m.customProgramCacheKey=src.customProgramCacheKey;m.transparent=true;return m;}\r\nexport function fallPieces(pieces,fall,ms){\r\n const angle=Math.random()*Math.PI*2,axis=new THREE.Vector3(Math.cos(angle),Math.sin(angle),0),t0=performance.now(),z0=pieces.map(p=>p.pivot.position.z);\r\n const f={live:true,pose(now){const t=Math.min(1,(now-t0)/Math.max(1,ms)),e=t*t;pieces.forEach((p,i)=>{p.pivot.position.z=z0[i]-fall*e;p.pivot.quaternion.setFromAxisAngle(axis,(35*Math.PI/180)*e);p.pivot.scale.setScalar(1-.4*e);p.material.opacity=1-e;});}};\r\n f.pose(t0);\r\n f.done=new Promise(resolve=>{f.end=()=>{if(!f.live)return;f.live=false;pieces.forEach(p=>p.drop());resolve();};if(ms>0)setTimeout(f.end,ms);else f.end();});\r\n return f;\r\n}\r\n","modules/portal/portal-entry.mjs":"let loading,mounted;\r\n\r\nexport async function ensurePortalMounted(){\r\n  if(mounted?.disposed)loading=null;\r\n  if(!loading){\r\n    loading=(async()=>{\r\n      const style=document.createElement('link');\r\n      style.rel='stylesheet';style.href='/modules/portal/portal.css';\r\n      const styled=new Promise((resolve,reject)=>{style.onload=resolve;style.onerror=()=>reject(new Error('Portal styles unavailable.'));});\r\n      document.head.append(style);\r\n      try{\r\n        const [{mountPortal}]=await Promise.all([import('./portal.mjs'),styled]);\r\n        mounted=await mountPortal();\r\n        const dispose=mounted.dispose;mounted.dispose=()=>{dispose();style.remove();};\r\n        return mounted;\r\n      }catch(error){style.remove();throw error;}\r\n    })().catch(error=>{loading=null;throw error;});\r\n  }\r\n  return loading;\r\n}\r\n\r\nexport async function openQuiltPortal({shouldShow=()=>true}={}) {\r\n  const portal=await ensurePortalMounted();\r\n  if(!shouldShow())return null;\r\n  portal.show();\r\n  return portal;\r\n}\r\n","modules/portal/portal-look.mjs":"// Device-local portal appearance: metal colours, light-strip colour, board. Shared by the portal and War Room; guests included.\r\nexport const LOOK_KEYS={portal:'myr5.portalMetal',strip:'myr5.portalStrip'};\r\nexport const LOOK_DEFAULTS={portal:'#4d3d4f',strip:'#b026ff'};\r\nconst LEGACY_FRAME_KEY='myr5.portalFrameMetal';\r\nexport const BOARD_CHOICES=[['pixel','Pixel Dance Floor'],['clay','Little Boyfriend Clay'],['paper','Dr. Girlfriend Paper'],['mache','Cortisol Corgi'],['quilt','Quilt'],['ice','Crystal'],['grass','Grass'],['jelly','Jelly'],['wood','Wood'],['pond','Pond']];\r\nexport const cleanColor=(v,fallback)=>/^#[0-9a-f]{6}$/i.test(v)?v.toLowerCase():fallback;\r\nconst get=k=>{try{return localStorage.getItem(k)}catch{return null}};\r\nexport const readLook=()=>({portal:cleanColor(get(LOOK_KEYS.portal),cleanColor(get(LEGACY_FRAME_KEY),LOOK_DEFAULTS.portal)),strip:cleanColor(get(LOOK_KEYS.strip),LOOK_DEFAULTS.strip)});\r\n// The default (or junk) removes the key, so \"reset\" leaves nothing behind.\r\nexport function saveLook(name,value){try{if(!Object.hasOwn(LOOK_KEYS,name))return;if(name==='portal')localStorage.removeItem(LEGACY_FRAME_KEY);const c=cleanColor(value,null);c&&c!==LOOK_DEFAULTS[name]?localStorage.setItem(LOOK_KEYS[name],c):localStorage.removeItem(LOOK_KEYS[name]);}catch{}}\r\n// Flowing strip in one colour: bright, dim, bright, pale.\r\nexport const stripSeq=c=>{const mix=(t,to)=>'#'+[1,3,5].map(i=>Math.round(parseInt(c.slice(i,i+2),16)*(1-t)+to*t).toString(16).padStart(2,'0')).join('');return [c,mix(.6,0),c,mix(.55,255)];};\r\n// Sets the CSS variables the portal and shell read. Metal and strip are always set (the shell paints on pages where the portal never mounts);\r\n// --frame-metal keeps remove-on-default semantics.\r\nconst mixHex=(c,t,to)=>'#'+[1,3,5].map(i=>Math.round(parseInt(c.slice(i,i+2),16)*(1-t)+to*t).toString(16).padStart(2,'0')).join('');\r\n// Menu-text tint (R3-03): the strip colour, lightened just enough to read at 4.5:1 (WCAG AA) on the plum plate. The strip itself is user-picked, so this is measured, not guessed.\r\nexport const PLATE='#5d4a5b';\r\nconst lum=c=>{const f=i=>{const v=parseInt(c.slice(i,i+2),16)/255;return v<=.03928?v/12.92:((v+.055)/1.055)**2.4;};return .2126*f(1)+.7152*f(3)+.0722*f(5);};\r\nexport const contrast=(a,b)=>{const [x,y]=[lum(a),lum(b)].sort((p,q)=>q-p);return (x+.05)/(y+.05);};\r\nexport const dimTint=(c,plate=PLATE)=>{for(let t=0;t<=1;t+=.05){const m=mixHex(c,t,255);if(contrast(m,plate)>=4.5)return m;}return '#ffffff';};\r\nexport function applyLookVars(el=document.documentElement,look=readLook()){\r\n el.style.setProperty('--portal-metal',look.portal);\r\n if(look.portal===LOOK_DEFAULTS.portal)el.style.removeProperty('--frame-metal');else el.style.setProperty('--frame-metal',look.portal);\r\n el.style.setProperty('--portal-strip',look.strip);\r\n el.style.setProperty('--strip-dim',dimTint(look.strip));\r\n el.style.setProperty('--portal-strip-glow',`${look.strip}40`);\r\n}\r\n// One frame markup + energy constants, shared by the portal, standalone housing and the shell (portal.css .portal-frame).\r\nconst FRAME_BOLTS=[[0,0],[1,0],[0,1],[1,1],[0,.33],[0,.67],[1,.33],[1,.67]];\r\nexport const frameHtml=()=>`<div class=\"portal-frame\" aria-hidden=\"true\"><span class=\"portal-energy\">${'<span><span></span></span>'.repeat(4)}</span>${FRAME_BOLTS.map(([x,y])=>`<i style=\"--x:${x};--y:${y}\"></i>`).join('')}</div>`;\r\nexport const ENERGY={px:360,loopMs:9000,surge:4,dive:10};\r\nexport const ENERGY_MOVES=[['X',1],['Y',1],['X',-1],['Y',-1]]; // top →, right ↓, bottom ←, left ↑\r\nexport const energyStops=seq=>{const seg=ENERGY.px/seq.length;return seq.map((c,i)=>`${c} ${i*seg}px ${(i+1)*seg}px`).join(',');};\r\nexport const readBoard=()=>{const id=get('myr5.portalBoard');return BOARD_CHOICES.some(choice=>choice[0]===id)?id:'pixel';};\r\nexport const saveBoard=id=>{try{localStorage.setItem('myr5.portalBoard',BOARD_CHOICES.some(choice=>choice[0]===id)?id:'pixel');}catch{}};\r\n","modules/portal/portal-mechanical-layout.mjs":"// Shared-shape mechanical layout. All seams exist before a gesture is made.\r\nimport {SHAPES,fromFrame} from './portal-shapes.mjs';\r\nimport {portalWindow,shapeOutlinePts} from './portal-apertures.mjs';\r\nexport const MECHANICAL_FRAME={x0:.085,y0:.065,x1:.915,y1:.925};\r\nconst EPS=1e-7;\r\nexport const area=poly=>poly.reduce((a,p,i)=>{const q=poly[(i+1)%poly.length];return a+p[0]*q[1]-q[0]*p[1];},0)/2;\r\nexport function interiorPoint(poly){\r\n const cross=(a,b,c)=>(b[0]-a[0])*(c[1]-a[1])-(b[1]-a[1])*(c[0]-a[0]);\r\n for(let i=0;i<poly.length;i++){\r\n  const ai=(i+poly.length-1)%poly.length,ci=(i+1)%poly.length,a=poly[ai],b=poly[i],c=poly[ci];\r\n  if(cross(a,b,c)<=EPS)continue;\r\n  const blocked=poly.some((p,j)=>j!==ai&&j!==i&&j!==ci&&cross(a,b,p)>=-EPS&&cross(b,c,p)>=-EPS&&cross(c,a,p)>=-EPS);\r\n  if(!blocked)return [(a[0]+b[0]+c[0])/3,(a[1]+b[1]+c[1])/3];\r\n }\r\n throw new Error('Mechanical panel has no interior ear');\r\n}\r\nexport function mechanicalPaths(frame=MECHANICAL_FRAME){\r\n return Object.entries(SHAPES).flatMap(([id,lines])=>id==='line'?[]:lines.map((l,line)=>{const points=(id==='oval'?shapeOutlinePts(id):l.points).map(p=>fromFrame(...p,frame));if(id==='oval')points.push(points[0]);return {id,line,points};}));\r\n}\r\nexport function releasePaths(face,frame=MECHANICAL_FRAME){\r\n const rect={left:frame.x0*face.w,top:frame.y0*face.h,width:(frame.x1-frame.x0)*face.w,height:(frame.y1-frame.y0)*face.h};\r\n return ['rect','oval','up','down','vdiamond','hdiamond','x','line-lr','line-down'].map(id=>({id:'release-'+id,points:portalWindow(id,rect).pts.map(([x,y])=>[x/face.w,y/face.h])}));\r\n}\r\n// Split each finite segment at intersections, then walk the planar graph's bounded faces.\r\n// Unlike cutting the slab during release, these separate pieces are manufactured at init.\r\nexport function sectionPanels(paths=mechanicalPaths()){\r\n const segments=[],seen=new Set(),key=p=>p.map(v=>v.toFixed(6)).join(',');\r\n const add=(a,b)=>{const k=[key(a),key(b)].sort().join('|');if(k!==[key(a),key(a)].join('|')&&!seen.has(k)){seen.add(k);segments.push({a,b,ts:[0,1]});}};\r\n const border=[[0,0],[1,0],[1,1],[0,1],[0,0]];\r\n [border,...paths.map(p=>p.points)].forEach(ps=>{for(let i=1;i<ps.length;i++)add(ps[i-1],ps[i]);});\r\n // Join the inset rectangular frame to the outer casing so the graph has no\r\n // disconnected outer face whose geometry would incorrectly cover inner cells.\r\n const rect=paths.find(p=>p.id==='rect');if(rect)for(let i=0;i<4;i++)add(border[i],rect.points[i]);\r\n for(let i=0;i<segments.length;i++)for(let j=i+1;j<segments.length;j++){\r\n  const s=segments[i],t=segments[j],rx=s.b[0]-s.a[0],ry=s.b[1]-s.a[1],sx=t.b[0]-t.a[0],sy=t.b[1]-t.a[1],cross=rx*sy-ry*sx;\r\n  if(Math.abs(cross)<EPS){\r\n   const dx=t.a[0]-s.a[0],dy=t.a[1]-s.a[1];\r\n   if(Math.abs(dx*ry-dy*rx)<EPS){\r\n    const project=(p,a,x,y)=>(p[0]-a[0])*x/(x*x+y*y)+(p[1]-a[1])*y/(x*x+y*y);\r\n    for(const p of [t.a,t.b]){const u=project(p,s.a,rx,ry);if(u>=0&&u<=1)s.ts.push(u);}\r\n    for(const p of [s.a,s.b]){const v=project(p,t.a,sx,sy);if(v>=0&&v<=1)t.ts.push(v);}\r\n   }continue;\r\n  }\r\n  const dx=t.a[0]-s.a[0],dy=t.a[1]-s.a[1],u=(dx*sy-dy*sx)/cross,v=(dx*ry-dy*rx)/cross;\r\n  if(u>=-EPS&&u<=1+EPS&&v>=-EPS&&v<=1+EPS){s.ts.push(Math.max(0,Math.min(1,u)));t.ts.push(Math.max(0,Math.min(1,v)));}\r\n }\r\n const vertices=new Map(),edges=new Set(),buckets=new Map(),snap=1e-6;\r\n const vertex=p=>{\r\n  const x=Math.floor(p[0]/snap),y=Math.floor(p[1]/snap);\r\n  for(let dx=-1;dx<=1;dx++)for(let dy=-1;dy<=1;dy++)for(const v of buckets.get((x+dx)+','+(y+dy))||[])if(Math.hypot(p[0]-v.p[0],p[1]-v.p[1])<=snap)return v;\r\n  const k=String(vertices.size),v={k,p,neighbors:[]},bucket=x+','+y;vertices.set(k,v);if(!buckets.has(bucket))buckets.set(bucket,[]);buckets.get(bucket).push(v);return v;\r\n };\r\n for(const s of segments){const ts=[...new Set(s.ts.map(t=>+t.toFixed(12)))].sort((a,b)=>a-b);for(let i=1;i<ts.length;i++){\r\n  const at=t=>[s.a[0]+(s.b[0]-s.a[0])*t,s.a[1]+(s.b[1]-s.a[1])*t],a=vertex(at(ts[i-1])),b=vertex(at(ts[i])),ek=[a.k,b.k].sort().join('|');\r\n  if(a===b||edges.has(ek))continue;edges.add(ek);a.neighbors.push(b);b.neighbors.push(a);\r\n }}\r\n for(const v of vertices.values())v.neighbors.sort((a,b)=>Math.atan2(a.p[1]-v.p[1],a.p[0]-v.p[0])-Math.atan2(b.p[1]-v.p[1],b.p[0]-v.p[0]));\r\n const walked=new Set(),panels=[];\r\n for(const a of vertices.values())for(const b of a.neighbors){\r\n  if(walked.has(a.k+'>'+b.k))continue;let prev=a,next=b,poly=[];\r\n  for(let n=0;n<edges.size*2+2;n++){\r\n   const ek=prev.k+'>'+next.k;if(walked.has(ek))break;walked.add(ek);poly.push(prev.p);\r\n   const ns=next.neighbors,index=ns.indexOf(prev),after=ns[(index-1+ns.length)%ns.length];prev=next;next=after;\r\n   if(prev===a&&next===b)break;\r\n  }\r\n  if(poly.length>=3&&area(poly)>EPS)panels.push({points:poly,center:interiorPoint(poly)});\r\n }\r\n return panels;\r\n}\r\nexport function pointOnPath(paths,face,u,v){\r\n let best=null;\r\n paths.forEach((path,pathIndex)=>{for(let i=1;i<path.points.length;i++){\r\n  const a=path.points[i-1],b=path.points[i],dx=(b[0]-a[0])*face.w,dy=(b[1]-a[1])*face.h,len2=dx*dx+dy*dy;\r\n  if(!len2)continue;const t=Math.max(0,Math.min(1,((u-a[0])*face.w*dx+(v-a[1])*face.h*dy)/len2)),q=[a[0]+(b[0]-a[0])*t,a[1]+(b[1]-a[1])*t],distance=Math.hypot((u-q[0])*face.w,(v-q[1])*face.h);\r\n  if(!best||distance<best.distance)best={pathIndex,segment:i-1,u:q[0],v:q[1],distance,tx:dx/Math.sqrt(len2),ty:dy/Math.sqrt(len2)};\r\n }});return best;\r\n}\r\n// Paired gears mesh at each drive station. Long spans and junctions are carried\r\n// by metal transmission channels, leaving the shared shape silhouettes readable.\r\nexport function mechanicalLayout(face,paths=mechanicalPaths(),nodes=['m19']){\r\n const parts=[],trains=[];\r\n paths.forEach((path,pathIndex)=>{\r\n  const train=[],lengths=path.points.slice(1).map((b,i)=>Math.hypot((b[0]-path.points[i][0])*face.w,(b[1]-path.points[i][1])*face.h)),length=lengths.reduce((a,b)=>a+b,0),closed=Math.hypot(path.points[0][0]-path.points.at(-1)[0],path.points[0][1]-path.points.at(-1)[1])<EPS;\r\n  const count=Math.max(2,Math.round(length/(.5*face.w)));let segment=0,offset=0;\r\n  for(let i=0;i<count;i++){\r\n   const distance=(i+.5)*length/count;while(segment<lengths.length-1&&offset+lengths[segment]<distance){offset+=lengths[segment++];}\r\n   const a=path.points[segment],b=path.points[segment+1],t=(distance-offset)/lengths[segment],tx=(b[0]-a[0])*face.w/lengths[segment],ty=(b[1]-a[1])*face.h/lengths[segment],rA=i===0?.029:.021,rB=rA,gap=(rA+rB)*face.w*.94;\r\n   for(let k=0;k<2;k++){\r\n    const d=(k?1:-1)*gap/2,index=parts.length,r=k?rB:rA;\r\n    const drives=k?train.at(-1):(train.length?train.at(-2):null),driveRatio=!k&&drives!=null?parts[drives].r/r:undefined;\r\n    parts.push({node:nodes[(i+pathIndex)%nodes.length],kind:'gear',u:a[0]+(b[0]-a[0])*t+tx*d/face.w,v:a[1]+(b[1]-a[1])*t+ty*d/face.h,r,rot:k?9:0,layer:pathIndex,pathIndex,station:i,drives,driveRatio});train.push(index);\r\n   }\r\n  }\r\n  trains.push(train);\r\n });\r\n const first=trains[0]?.[0];for(const train of trains.slice(1)){const i=train[0];parts[i].drives=first;parts[i].driveRatio=parts[first].r/parts[i].r;}\r\n // Four larger flywheels are mounted on the casing around the shape frame.\r\n // They reuse the same existing kit cog and drive through their smaller axles.\r\n for(const [u,v] of [[MECHANICAL_FRAME.x0,MECHANICAL_FRAME.y0],[MECHANICAL_FRAME.x1,MECHANICAL_FRAME.y0],[MECHANICAL_FRAME.x1,MECHANICAL_FRAME.y1],[MECHANICAL_FRAME.x0,MECHANICAL_FRAME.y1]])parts.push({node:nodes[0],kind:'gear',u,v,r:.055,rot:0,layer:11,hub:true,pathIndex:-1,drives:first,driveRatio:parts[first].r/.055});\r\n return {parts,trains,paths};\r\n}\r\nexport function signedRailImpulse(hit,du,dv,face,r,gain=2){return (du*face.w*hit.tx+dv*face.h*hit.ty)/(r*face.w)*gain;}\r\n// Lock-ring machinery along the shape lines. Lengths/radii are face-WIDTH fractions, rot = tangent in face space.\r\n// bolt.side: sign so that local +y*side (3D, y up = -v) withdraws toward the path centroid; in uv the withdrawn centre is c+side*throw*(sin,-cos) in face-width units.\r\nexport function mechanismLayout(face,paths=mechanicalPaths(),gearLayout=mechanicalLayout(face,paths)){\r\n const {parts}=gearLayout,out={tubes:[],bolts:[],pistons:[],pulleys:[],cables:[],weights:[],lamps:[],dial:[]};\r\n const W=(a,b)=>Math.hypot(b[0]-a[0],(b[1]-a[1])*face.h/face.w),rotOf=(a,b)=>Math.atan2((b[1]-a[1])*face.h,(b[0]-a[0])*face.w);\r\n const nearGear=(u,v)=>parts.reduce((best,p,i)=>W([u,v],[p.u,p.v])<W([u,v],[parts[best].u,parts[best].v])?i:best,0);\r\n paths.forEach((path,pathIndex)=>{\r\n  const pts=path.points,closed=Math.hypot(pts[0][0]-pts.at(-1)[0],pts[0][1]-pts.at(-1)[1])<EPS,gs=parts.filter(p=>p.pathIndex===pathIndex),st=[];\r\n  const body=closed?pts.slice(1):pts,cu=body.reduce((a,p)=>a+p[0],0)/body.length,cv=body.reduce((a,p)=>a+p[1],0)/body.length;\r\n  for(let i=0;i<gs.length;i+=2){const a=gs[i],b=gs[i+1],u=(a.u+b.u)/2,v=(a.v+b.v)/2;st.push({u,v,segment:pointOnPath([path],face,u,v).segment,station:a.station,clear:(a.r+b.r)*.47+a.r});}\r\n  for(const s of st){\r\n   const a=pts[s.segment],b=pts[s.segment+1],rot=rotOf(a,b),L=W(a,b),at=W(a,[s.u,s.v]),to=Math.min(Math.max(at,.03),L-.03),bu=s.u+(b[0]-a[0])*(to-at)/L,bv=s.v+(b[1]-a[1])*(to-at)/L;\r\n   out.lamps.push({u:s.u,v:s.v,r:.018,pathIndex,station:s.station});\r\n   // a bolt on a vertex slides .03 along its segment so the withdrawn bar stays inside the polygon\r\n   if(closed)out.bolts.push({u:L>.06?bu:s.u,v:L>.06?bv:s.v,rot,r:.02,pathIndex,station:s.station,throw:.025,side:Math.sign(Math.sin(rot)*(cu-bu)*face.w-Math.cos(rot)*(cv-bv)*face.h)||1});\r\n  }\r\n  const mine=out.tubes.length;\r\n  for(let seg=0;seg<pts.length-1;seg++){\r\n   const a=pts[seg],b=pts[seg+1],L=W(a,b),rot=rotOf(a,b),nodes=[{s:0,c:0},...st.filter(s=>s.segment===seg).map(s=>({s:W(a,[s.u,s.v]),c:s.clear+.005})),{s:L,c:0}];\r\n   // every crossing with another path splits the tube, so a tube never spans two panels\r\n   paths.forEach((q,j)=>{if(j===pathIndex)return;for(let k=0;k<q.points.length-1;k++){\r\n    const c=q.points[k],d=q.points[k+1],rx=b[0]-a[0],ry=b[1]-a[1],sx=d[0]-c[0],sy=d[1]-c[1],x=rx*sy-ry*sx;\r\n    if(Math.abs(x)<EPS)continue;const t=((c[0]-a[0])*sy-(c[1]-a[1])*sx)/x,u=((c[0]-a[0])*ry-(c[1]-a[1])*rx)/x;\r\n    if(t>=-EPS&&t<=1+EPS&&u>=-EPS&&u<=1+EPS)nodes.push({s:t*L,c:.003});\r\n   }});\r\n   nodes.sort((p,q)=>p.s-q.s);\r\n   for(let i=1;i<nodes.length;i++){\r\n    const from=nodes[i-1].s+nodes[i-1].c,to=nodes[i].s-nodes[i].c,len=to-from;\r\n    if(len<.012)continue;const t=(from+to)/2/L;\r\n    out.tubes.push({u:a[0]+(b[0]-a[0])*t,v:a[1]+(b[1]-a[1])*t,len,rot,r:.007,pathIndex,segment:seg});\r\n   }\r\n  }\r\n  // one piston per path, at the centre of its longest tube\r\n  const t=out.tubes.slice(mine).sort((p,q)=>q.len-p.len)[0];\r\n  if(t)out.pistons.push({u:t.u,v:t.v,rot:t.rot,r:.02,stroke:.04,pathIndex,drives:nearGear(t.u,t.v)});\r\n });\r\n // Corner hubs become pulleys; cables hang in the side margin outside the frame so nothing hides them.\r\n parts.forEach((p,drives)=>{if(!p.hub)return;\r\n  const sx=p.u<.5?-1:1,sy=p.v<.5?1:-1,from=[p.u+sx*p.r,p.v],to=[from[0],p.v+sy*.22],len=W(from,to);\r\n  out.pulleys.push({u:p.u,v:p.v,r:p.r,drives});out.cables.push({from,to,r:.004});\r\n  out.weights.push({u:from[0]+(to[0]-from[0])*.4,v:from[1]+(to[1]-from[1])*.4,r:.016,travel:len*.5,drives});\r\n });\r\n const oi=paths.findIndex(p=>p.id==='oval'),op=paths[oi].points.slice(1);\r\n out.dial.push({u:op.reduce((a,p)=>a+p[0],0)/op.length,v:op.reduce((a,p)=>a+p[1],0)/op.length,r:.11,drives:gearLayout.trains[oi][0]});\r\n return out;\r\n}\r\n","modules/portal/portal-peek.mjs":"// PORTAL-PEEK (Ian 1 Oct 2026): a see-through portal. While the cut is open, the destination scene is rendered\r\n// offscreen into one reused render target and blended into the wormhole's centre (portal.mjs keeps the wormhole at\r\n// the rim). Technique from Wormhole by AidanHT (src/render/PortalRenderer.ts): RT scaled per quality (RT_SCALE),\r\n// capped at MAX_RT_DIM. Our destinations are their own scenes, so there is no portal-pair camera and nothing in\r\n// front of a portal plane to clip (no oblique near plane needed). On by default (R17); localStorage.myr5PortalPeek = \"0\" turns it off.\r\n// Served unbundled (dynamic import from portal.mjs on the first peek): plain JS, no .ts, no __MYR5_* defines.\r\nimport * as THREE from 'three';\r\nimport {recipeShipTint,applyShipTint,SHIP_ANCHOR_Y,SHIP_FACING,SHIP_REST_Z,shipPoseAbove,measureShip} from '../ships/ship-scene-domain.mjs';\r\nimport {STARTER_WONDERS,backgroundForDay,starterWonderUrl} from '../../meditation-backgrounds.mjs';\r\nimport {createCoachPreviewSpace} from '../../coach-preview-space.mjs';\r\nimport {releaseGraphics} from '../graphics-lifecycle.mjs';\r\n\r\nexport const RT_SCALE=Object.freeze({high:1,medium:.55,low:.35});\r\nexport const MAX_RT_DIM=2048,MAX_DPR=1.5;\r\nconst FLAG='myr5PortalPeek';\r\n\r\n// \"0\" = off (kill switch); \"low\"/\"medium\"/\"high\" force one (for measuring); anything else is auto: a 4-core-or-less\r\n// phone is low quality and gets no peek (plain wormhole, the tunnel's own coarse hint); everything else medium.\r\nexport function peekQuality(){\r\n let v=null;try{v=localStorage.getItem(FLAG);}catch{}\r\n if(v==='0')return null;\r\n if(RT_SCALE[v])return v;\r\n return (navigator.hardwareConcurrency||8)<=4?null:'medium';\r\n}\r\n\r\n/** One render target, reused; resize() reallocates only when the size changes, render() never allocates. */\r\nexport function createPortalPeek({renderer,quality='medium'}){\r\n const rt=new THREE.WebGLRenderTarget(16,16,{depthBuffer:true}),scale=RT_SCALE[quality]||RT_SCALE.medium;\r\n let source=null,pixels=new Uint8Array(16*16*4),disposed=false;activePeeks++;\r\n const peek={\r\n  target:rt,texture:rt.texture,width:16,height:16,\r\n  setSource(next){source=typeof next==='function'?{render:next}:next;if(source?.camera)peek.resize(peek.cssW||16,peek.cssH||16);},\r\n  // w,h: CSS px of the window the peek fills.\r\n  resize(w,h){\r\n   peek.cssW=w;peek.cssH=h;\r\n   const k=Math.min(devicePixelRatio||1,MAX_DPR)*scale,rw=Math.min(MAX_RT_DIM,Math.max(16,Math.round(w*k))),rh=Math.min(MAX_RT_DIM,Math.max(16,Math.round(h*k)));\r\n   if(source?.camera){source.camera.aspect=w/Math.max(1,h);source.camera.updateProjectionMatrix();}\r\n   if(rw===peek.width&&rh===peek.height)return;\r\n   rt.setSize(rw,rh);peek.width=rw;peek.height=rh;pixels=new Uint8Array(rw*rh*4);\r\n  },\r\n  render(now=performance.now()){\r\n   if(!source)return false;\r\n   const prev=renderer.getRenderTarget();renderer.setRenderTarget(rt);\r\n   if(source.render)source.render(renderer,now);else{source.update?.(now);renderer.render(source.scene,source.camera);}\r\n   renderer.setRenderTarget(prev);return true;\r\n  },\r\n  // The wormhole draws in its own WebGL2 context, so the frame crosses over as pixels (bottom row first, linear RGB).\r\n  lost:()=>renderer.getContext().isContextLost(),\r\n  read(){renderer.readRenderTargetPixels(rt,0,0,peek.width,peek.height,pixels);return pixels;},\r\n  dispose(){if(disposed)return;disposed=true;source?.dispose?.();source=null;rt.dispose();activePeeks=Math.max(0,activePeeks-1);releasePeekRendererIfIdle();},\r\n };\r\n return peek;\r\n}\r\n\r\n// One three.js context for the page's life (like the tunnel's), created on the first peek.\r\nlet shared=null;\r\nlet activePeeks=0;\r\nexport function peekRenderer(){\r\n if(shared&&!shared.getContext().isContextLost())return shared;\r\n const canvas=document.createElement('canvas');\r\n try{shared=new THREE.WebGLRenderer({canvas,alpha:false,antialias:false,powerPreference:'low-power'});}catch{return shared=null;}\r\n canvas.addEventListener('webglcontextlost',()=>{if(shared?.domElement===canvas)shared=null;});\r\n return shared;\r\n}\r\n// A stopped peek no longer needs its GPU context. Keep the shared renderer only while a phase uses it.\r\nexport function releasePeekRendererIfIdle(){\r\n if(activePeeks||!shared)return false;\r\n const renderer=shared;shared=null;\r\n const gl=renderer.getContext(),canvas=renderer.domElement;\r\n return releaseGraphics(canvas,gl,()=>{renderer.dispose();renderer.forceContextLoss();}).then(()=>{canvas.remove();return true;});\r\n}\r\n\r\n// scene.background with CSS \"cover\" (three stretches a background texture to the viewport otherwise).\r\nfunction cover(texture,aspect){\r\n const img=texture.image,ia=img?(img.width/img.height):1,r=aspect/ia;\r\n texture.matrixAutoUpdate=true;\r\n if(r>1){texture.repeat.set(1,1/r);texture.offset.set(0,(1-1/r)/2);}else{texture.repeat.set(r,1);texture.offset.set((1-r)/2,0);}\r\n}\r\nconst textures=new THREE.TextureLoader();\r\nasync function wonder(){\r\n const t=await textures.loadAsync(starterWonderUrl(backgroundForDay(STARTER_WONDERS)));\r\n t.colorSpace=THREE.SRGBColorSpace;\r\n return t;\r\n}\r\nfunction disposeTree(root){root.traverse(n=>{n.geometry?.dispose();for(const m of [].concat(n.material||[])){for(const v of Object.values(m))if(v?.isTexture)v.dispose();m.dispose();}});}\r\n\r\n// Parsed once per page; dispose() frees the GPU copies and the next peek re-uploads them.\r\nlet starterShip=null;\r\n// Ship pod: the starter ship hovering over the supply-drop space, as in the full ship view.\r\n// ponytail: always the starter ship; an owned pack ship needs app.mjs's bridge, which this unbundled module can't reach.\r\nasync function shipSource(){\r\n const {GLTFLoader}=await import('three/addons/loaders/GLTFLoader.js');\r\n starterShip??=new GLTFLoader().loadAsync('/pod/worlds/starter/supportive.glb').catch(e=>{starterShip=null;throw e;});\r\n const gltf=await starterShip;\r\n const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(34,1,.1,400);\r\n camera.position.set(0,.45,7);camera.lookAt(0,.9,0);\r\n const space=createCoachPreviewSpace(THREE,{pixelRatio:Math.min(devicePixelRatio||1,MAX_DPR)});\r\n scene.background=space.background;scene.fog=space.fog;scene.add(space.group);\r\n scene.add(new THREE.HemisphereLight(0xe9d9ff,0x23162d,2.5));\r\n const key=new THREE.DirectionalLight(0xffefca,4.2);key.position.set(-3,5,4);scene.add(key);\r\n const rim=new THREE.DirectionalLight(0xb58cff,3.2);rim.position.set(4,2,-3);scene.add(rim);\r\n const model=gltf.scene.clone(true),box=new THREE.Box3().setFromObject(model),size=box.getSize(new THREE.Vector3());\r\n model.position.sub(box.getCenter(new THREE.Vector3()));\r\n try{applyShipTint(model,recipeShipTint(JSON.parse(localStorage.getItem('myr5-recipe-v1')||'{}')));}catch{} // the coach's saved ship colour (the ship itself stays the starter, see above)\r\n const turn=new THREE.Group(),group=new THREE.Group(),fit=2.25/(Math.max(size.x,size.y,size.z)||1);\r\n turn.rotation.y=SHIP_FACING;turn.add(model);group.add(turn);group.scale.setScalar(fit);\r\n group.position.set(0,SHIP_ANCHOR_Y,SHIP_REST_Z);group.rotation.set(.08,-.32,0);scene.add(group);\r\n let pose=null,aspect=0;const t0=performance.now();\r\n return {scene,camera,\r\n  update(now){\r\n   if(camera.aspect!==aspect){aspect=camera.aspect;group.scale.setScalar(fit);group.position.y=SHIP_ANCHOR_Y;pose=shipPoseAbove({top:.4,bottom:-.4,aspect},measureShip(THREE,group,camera));group.scale.setScalar(fit*pose.scale);}\r\n   group.position.y=pose.y+Math.sin((now-t0)/820)*.07*pose.scale;group.rotation.y=-.32+Math.sin((now-t0)/1700)*.055;\r\n  },\r\n  dispose(){space.dispose();disposeTree(group);}};\r\n}\r\n// Meditation: the still room's far layer, today's wonder (the glass is greyscale on this route).\r\n// ponytail: the sleeping coach and your character are DOM/creature-viewer layers, not offscreen-renderable here.\r\nasync function meditationSource(){\r\n const bg=await wonder(),scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera();\r\n scene.background=bg;let aspect=0;\r\n return {scene,camera,update(){if(camera.aspect!==aspect){aspect=camera.aspect;cover(bg,aspect);}},dispose(){bg.dispose();}};\r\n}\r\n// Customizer creature: no traced shape leads there (it opens from the ship), so it has no peek.\r\nexport const PEEK_SOURCES=Object.freeze({select:shipSource,meditate:meditationSource});\r\n\r\n/** For portal.mjs: a peek for `route` sized to a w x h CSS px window, or null (switched off, low end, no source, no WebGL). */\r\nexport async function openPeek(route,w,h,{shouldOpen=()=>true}={}){\r\n const quality=peekQuality(),load=PEEK_SOURCES[route];\r\n if(!quality||!load)return null;\r\n let source;\r\n try{source=await load();}catch(error){console.warn('Portal peek unavailable',error);return null;}\r\n if(!shouldOpen()){source?.dispose?.();return null;}\r\n const renderer=peekRenderer();if(!renderer){source?.dispose?.();return null;}\r\n const peek=createPortalPeek({renderer,quality});\r\n try{peek.resize(w,h);peek.setSource(source);}catch(error){peek.dispose();console.warn('Portal peek unavailable',error);return null;}\r\n return peek;\r\n}\r\n","modules/portal/portal-pixel-coaches.mjs":"// Pixel parade for the secret celebration: real gala body coaches walk lanes\r\n// across the floor, then drop into the pit. Read-only: no unlock state is\r\n// touched. Floor and pit art belong to the parent; this only draws coaches.\r\nconst SPRITE_DIR = '/pod/gala-coaches/';\r\nconst MANIFEST_URL = SPRITE_DIR + 'manifest.json';\r\nconst CELL_W = 32;\r\nconst CELL_H = 48;\r\nconst MAX_COACHES = 20;\r\nconst MIN_COACHES = 12;\r\nconst PARADERS = 36;\r\nconst LANES = 6;\r\n\r\nconst frac = x => x - Math.floor(x);\r\nconst lerp = (a, b, t) => a + (b - a) * t;\r\n\r\n// Evenly spaced picks so the 12-20 chosen coaches span the roster.\r\nfunction pick(list, max = MAX_COACHES) {\r\n  const n = Math.min(max, list.length);\r\n  return Array.from({ length: n }, (_, i) => list[Math.floor(i * list.length / n)]);\r\n}\r\n\r\nfunction normalize(src, doc) {\r\n  const c = doc.createElement('canvas');\r\n  c.width = CELL_W;\r\n  c.height = CELL_H;\r\n  const g = c.getContext('2d');\r\n  g.imageSmoothingEnabled = false;\r\n  g.drawImage(src, 0, 0, src.width, src.height, 0, 0, CELL_W, CELL_H);\r\n  return c;\r\n}\r\n\r\n// Primary: the same loader the war room uses (filters to the real roster).\r\nasync function fromLoader(doc) {\r\n  const { loadCoachSpritePreviews } = await import('../../war-room/coach-sprites.js');\r\n  const { sprites, art } = await loadCoachSpritePreviews(doc);\r\n  return pick(sprites.filter(s => s.kind === 'body')).map(s => ({ id: s.id, src: art(s) }));\r\n}\r\n\r\n// Fallback: cut body frames straight from the bodies sheet named in the manifest.\r\nasync function fromSheet(doc) {\r\n  const manifest = await (await fetch(MANIFEST_URL)).json();\r\n  const img = doc.createElement('img');\r\n  img.src = SPRITE_DIR + manifest.sheets.bodies.image;\r\n  await img.decode();\r\n  const bodies = manifest.sprites.filter(s => s.kind === 'body' && s.sheet === 'bodies');\r\n  return pick(bodies).map(s => {\r\n    const [x, y, w, h] = s.frame;\r\n    const c = doc.createElement('canvas');\r\n    c.width = w;\r\n    c.height = h;\r\n    c.getContext('2d').drawImage(img, x, y, w, h, 0, 0, w, h);\r\n    return { id: s.id, src: c };\r\n  });\r\n}\r\n\r\nexport async function loadPixelParadeCoaches(doc = globalThis.document) {\r\n  let list = [];\r\n  try { list = await fromLoader(doc); } catch { list = []; }\r\n  if (list.length < MIN_COACHES) {\r\n    try { list = await fromSheet(doc); } catch { /* keep whatever loaded */ }\r\n  }\r\n  return list.map(({ id, src }) => ({ id, canvas: normalize(src, doc) }));\r\n}\r\n\r\n// Lanes are drawn far to near so near coaches overlap far ones.\r\n// ctx is lowres; width/height default to the canvas size.\r\nexport function drawPixelParade(ctx, coaches, {\r\n  time = 0,\r\n  progress = 0,\r\n  fall = 0,\r\n  width = ctx.canvas.width,\r\n  height = ctx.canvas.height,\r\n} = {}) {\r\n  if (!coaches?.length) return;\r\n  const entry = Math.min(1, Math.max(0, progress));\r\n  const drop = Math.min(1, Math.max(0, fall));\r\n  const ease = drop * drop * (3 - 2 * drop);\r\n  const unit = height / 160;\r\n  const floorTop = height * 0.3;\r\n  const floorBot = height * 0.7;\r\n  const pitX = width / 2;\r\n  const pitY = height * 0.52;\r\n  const perLane = PARADERS / LANES;\r\n\r\n  ctx.save();\r\n  ctx.imageSmoothingEnabled = false;\r\n  for (let lane = 0; lane < LANES; lane++) {\r\n    const depth = lane / (LANES - 1);\r\n    const scale = unit * (0.6 + 0.5 * depth) * (1 - 0.6 * ease);\r\n    const laneY = lerp(floorTop, floorBot, depth);\r\n    for (let k = 0; k < perLane; k++) {\r\n      const i = k * LANES + lane;\r\n      const seed = frac(i * 0.618034 + 0.31);\r\n      const coach = coaches[i % coaches.length].canvas;\r\n      const speed = 14 + 12 * seed;\r\n      const span = width + CELL_W * scale;\r\n      const walkX = frac(seed + time * speed / span / 4) * span - CELL_W * scale / 2;\r\n      const x = lerp(walkX - width * (1 - entry), pitX + (seed - 0.5) * width * 0.08, ease);\r\n      const y = lerp(laneY, pitY, ease);\r\n      const phase = seed * Math.PI * 2;\r\n      const stride = Math.sin(time * 7 + phase);\r\n      const bob = Math.abs(stride) * 1.2 * unit * (1 - ease);\r\n      const alpha = drop > 0.6 ? 1 - (drop - 0.6) / 0.4 : 1;\r\n      ctx.globalAlpha = alpha;\r\n      ctx.save();\r\n      ctx.translate(Math.round(x), Math.round(y - bob));\r\n      ctx.rotate(stride * 0.05 * (1 - ease));\r\n      ctx.scale(scale * (1 + 0.03 * stride), scale);\r\n      ctx.drawImage(coach, -CELL_W / 2, -CELL_H, CELL_W, CELL_H);\r\n      ctx.restore();\r\n    }\r\n  }\r\n  ctx.restore();\r\n}\r\n\r\n// Fifteen small dance positions map one-to-one to energized fixture sections.\r\n// The supplied body sprites stay pixel sharp and appear progressively while\r\n// wiring, then all keep dancing through the party before the floor drops.\r\nexport function drawPixelFloorCoaches(ctx,coaches,{time=0,powered=[],ids=[],width,height,floorX,floorY,party=false,fall=0}={}){\r\n if(!coaches?.length||!powered.length)return;\r\n ctx.save();ctx.imageSmoothingEnabled=false;\r\n for(let i=0;i<ids.length;i++){\r\n  if(!powered.includes(ids[i]))continue;\r\n  const col=i%5,row=Math.floor(i/5),u=(col+.5)/5,v=(row+.58)/3;\r\n  const x=floorX(u,v)*width,y=floorY(v)*height;\r\n  const scale=Math.max(.27,Math.min(.8,width/400))*(.55+.18*row);\r\n  const dance=Math.sin(time*6+i*2.3),hop=Math.abs(Math.sin(time*5+i))*2;\r\n  ctx.globalAlpha=Math.max(0,1-fall*1.6);\r\n  ctx.save();ctx.translate(Math.round(x+dance*(party?3:1)),Math.round(y-hop));\r\n  ctx.scale(scale*(dance>0?-1:1),scale);\r\n  ctx.drawImage(coaches[i%coaches.length].canvas,-CELL_W/2,-CELL_H,CELL_W,CELL_H);\r\n  ctx.restore();\r\n }\r\n ctx.restore();\r\n}\r\n","modules/portal/portal-pixel-config.mjs":"export const PIXEL_TIMING=Object.freeze({party:3600,crumble:1800,sparks:1200,blackout:1000,fall:1400});\r\n","modules/portal/portal-pixel-domain.mjs":"// Fifteen overhead fixtures. The first fourteen retain their original names so\r\n// workshop gestures and saved flat boards identify the same physical spots.\r\nexport const PIXEL_ANCHORS=Object.freeze({tl:[.08,.08],tm:[.5,.08],tr:[.92,.08],rm:[.92,.5],br:[.92,.92],bm:[.5,.92],bl:[.08,.92],lm:[.08,.5],ul:[.08,.3236],ur:[.92,.3236],ll:[.08,.6764],lr:[.92,.6764],ut:[.5,.3236],bt:[.5,.6764],db:[.5,.5]});\r\nexport const PIXEL_FIXTURE_IDS=Object.freeze(Object.keys(PIXEL_ANCHORS));\r\nconst valid=id=>PIXEL_FIXTURE_IDS.includes(id);\r\n\r\nexport function pixelWireInit(){return {wires:[],powered:[],complete:false,completionCount:0};}\r\n\r\n// A finished drag energizes both fixtures. Reversing or repeating a wire has no\r\n// effect; the completion edge occurs once regardless of connection order.\r\nexport function pixelWireConnect(state,from,to){\r\n if(!valid(from)||!valid(to)||from===to)throw new TypeError('wire needs two distinct fixtures');\r\n const wires=state.wires||[],powered=state.powered||[];\r\n if(wires.some(w=>w.from===from&&w.to===to||w.from===to&&w.to===from))return state;\r\n const nextPowered=[...powered];\r\n for(const id of [from,to])if(!nextPowered.includes(id))nextPowered.push(id);\r\n const complete=nextPowered.length===PIXEL_FIXTURE_IDS.length;\r\n return {wires:[...wires,{from,to}],powered:nextPowered,complete,completionCount:state.completionCount+(complete&&!state.complete?1:0)};\r\n}\r\n\r\nexport function pixelWireCancel(state){return state;}\r\n\r\n// Crossbeams and the central rail provide a visible physical wire path.\r\nexport function pixelWireRoute(from,to){\r\n if(!valid(from)||!valid(to))throw new TypeError('invalid fixture');\r\n const a=PIXEL_ANCHORS[from],b=PIXEL_ANCHORS[to];\r\n return [a,[.5,a[1]],[.5,b[1]],b].filter((p,i,all)=>i===0||p[0]!==all[i-1][0]||p[1]!==all[i-1][1]);\r\n}\r\n","modules/portal/portal-shapes.mjs":"// portal-shapes.mjs\r\n//\r\n// Minimal shape recogniser for a phone board app.\r\n// No external dependencies – pure ES‑module JavaScript.\r\n//\r\n// The recogniser works by resampling each template polyline into evenly spaced\r\n// points and then comparing the trace against those points.  A candidate is\r\n// accepted only if both the trace covers at least TOLERANCE.cover of the template and\r\n// vice‑versa, using a distance tolerance of TOLERANCE.dist in board coordinates.\r\n//\r\n// The module exports:\r\n//\r\n//   SHAPE_IDS – array of the nine shape identifiers.\r\n//   SHAPES    – mapping from id → array of polylines (each polyline is an\r\n//                object with `points` and pre‑sampled `sampled` arrays).\r\n//   recognizeShape(strokes) – returns one id string or null.\r\n//\r\n// The recogniser never throws; malformed input results in a null return.\r\n\r\n// Match slack. Ian 2026-09-22: \"the shapes should be more forgiving on all boards\" — ~40 % looser than\r\n// the original 0.09 / 0.85: dist = how far (board fractions) a trace point may sit from the template (and\r\n// vice versa); 1 - cover = the share of the trace (overshoot) and of the template (closing gap, skipped\r\n// corner) allowed to fall outside dist. maxLength: the trace's path length (wiggles under 0.05 dropped)\r\n// may be at most this many times the template's — loose coverage alone would let a scribble that fills a\r\n// thin shape (hdiamond) pass; a real trace, even one that goes round twice, stays well under it.\r\nexport const TOLERANCE = { dist: 0.125, cover: 0.79, maxLength: 3 };\r\n\r\n/** Path length of polylines, ignoring moves shorter than `step` (finger jitter). */\r\nconst pathLength = (lines, step = 0.05) => {\r\n  let len = 0;\r\n  for (const pts of lines) {\r\n    let last = null;\r\n    for (const p of pts) {\r\n      if (!last) { last = p; continue; }\r\n      const d = dist(p, last);\r\n      if (d >= step) { len += d; last = p; }\r\n    }\r\n  }\r\n  return len;\r\n};\r\n\r\n// A single-stroke straight line is direction-aware: 'line-down'/'line-up' for the vertical template,\r\n// 'line-lr'/'line-rl' for the horizontal one, named for the stroke's first vs last point (see recognizeShape).\r\nconst SHAPE_IDS = [\r\n  'cross',\r\n  'down',\r\n  'hdiamond',\r\n  'line-down',\r\n  'line-lr',\r\n  'line-rl',\r\n  'line-up',\r\n  'oval',\r\n  'rect',\r\n  'up',\r\n  'vdiamond',\r\n  'x',\r\n];\r\n\r\n// Shape frame: the sub-rect of a board's face (UV, v down) where the templates above actually sit —\r\n// e.g. a painted door with a blank panel strip below the art, or a carved pattern inset from the\r\n// edges. toFrame maps a face-UV point into the frame's own 0..1 space (a raw trace, before\r\n// recognizeShape); fromFrame is the inverse (a template point, placed back onto the face for the\r\n// cut, the portal-glass clip, and guide drawing). Default = the whole face, a no-op.\r\nexport const FULL_FRAME = { x0: 0, y0: 0, x1: 1, y1: 1 };\r\nexport const toFrame = (u, v, frame = FULL_FRAME) => [\r\n  (u - frame.x0) / (frame.x1 - frame.x0),\r\n  (v - frame.y0) / (frame.y1 - frame.y0),\r\n];\r\nexport const fromFrame = (u, v, frame = FULL_FRAME) => [\r\n  frame.x0 + u * (frame.x1 - frame.x0),\r\n  frame.y0 + v * (frame.y1 - frame.y0),\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// Utility helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Euclidean distance between two points. */\r\nconst dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);\r\n\r\n/**\r\n * Return the minimum distance from point `p` to any point in array `set`.\r\n * The set is expected to be small (< 500 points), so a linear scan is fine.\r\n */\r\nconst minDistToSet = (p, set) => {\r\n  let best = Infinity;\r\n  for (const q of set) {\r\n    const d = dist(p, q);\r\n    if (d < best) best = d;\r\n  }\r\n  return best;\r\n};\r\n\r\n/**\r\n * Sample a polyline into points spaced by `spacing` along its length.\r\n * The input is an array of [x,y] pairs.  The output includes the final\r\n * endpoint exactly.\r\n */\r\nconst samplePolyline = (pts, spacing = 0.01) => {\r\n  if (!Array.isArray(pts) || pts.length < 2) return [];\r\n  const res = [];\r\n  // Compute segment lengths and total length\r\n  const segLens = [];\r\n  let totalLen = 0;\r\n  for (let i = 0; i < pts.length - 1; ++i) {\r\n    const d = dist(pts[i], pts[i + 1]);\r\n    segLens.push(d);\r\n    totalLen += d;\r\n  }\r\n  if (totalLen === 0) return [pts[0]];\r\n\r\n  let curSeg = 0;\r\n  let segStart = pts[0];\r\n  let segDist = 0; // distance travelled along current segment\r\n  let nextSample = 0;\r\n\r\n  while (nextSample <= totalLen + 1e-9) {\r\n    // Advance to the segment that contains `nextSample`\r\n    while (\r\n      curSeg < segLens.length &&\r\n      nextSample > segDist + segLens[curSeg] + 1e-9\r\n    ) {\r\n      segDist += segLens[curSeg];\r\n      ++curSeg;\r\n      segStart = pts[curSeg];\r\n    }\r\n    if (curSeg >= segLens.length) break;\r\n\r\n    const t =\r\n      segLens[curSeg] === 0\r\n        ? 0\r\n        : (nextSample - segDist) / segLens[curSeg];\r\n    const [x1, y1] = segStart;\r\n    const [x2, y2] = pts[curSeg + 1];\r\n    res.push([x1 + (x2 - x1) * t, y1 + (y2 - y1) * t]);\r\n\r\n    nextSample += spacing;\r\n  }\r\n\r\n  // Ensure the final endpoint is present\r\n  if (\r\n    res.length === 0 ||\r\n    dist(res[res.length - 1], pts[pts.length - 1]) > 1e-9\r\n  ) {\r\n    res.push(pts[pts.length - 1]);\r\n  }\r\n  return res;\r\n};\r\n\r\n/** Helper to create a polyline object with pre‑sampled points. */\r\nconst makePoly = (points) => ({\r\n  points,\r\n  sampled: samplePolyline(points),\r\n});\r\n\r\n// Rank already-accepted candidates by shape after allowing the small placement/\r\n// size differences of the carved boards. Keep the original coverage gate and\r\n// bound this fit so it cannot turn a small/off-centre gesture into a valid one.\r\nconst registeredTemplate = (template, trace) => {\r\n  const bounds = (points) => points.reduce((b, [x, y]) => [\r\n    Math.min(b[0], x), Math.min(b[1], y), Math.max(b[2], x), Math.max(b[3], y),\r\n  ], [Infinity, Infinity, -Infinity, -Infinity]);\r\n  const a = bounds(template), b = bounds(trace);\r\n  const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));\r\n  const sx = clamp((b[2] - b[0]) / (a[2] - a[0]), 0.8, 1.15);\r\n  const sy = clamp((b[3] - b[1]) / (a[3] - a[1]), 0.8, 1.15);\r\n  const cx = (a[0] + a[2]) / 2, cy = (a[1] + a[3]) / 2;\r\n  const dx = clamp((b[0] + b[2]) / 2 - cx, -0.08, 0.08);\r\n  const dy = clamp((b[1] + b[3]) / 2 - cy, -0.08, 0.08);\r\n  return template.map(([x, y]) => [cx + (x - cx) * sx + dx, cy + (y - cy) * sy + dy]);\r\n};\r\n\r\n// Corner detection, used only to break ties when a trace passes the distance/coverage\r\n// check against more than one template (e.g. a rect traced a few % smaller than its\r\n// template happens to land closer, on raw mean distance alone, to the oval template than\r\n// to its own — see recognizeShape). A raw trace's per-point finger jitter (up to ~0.045 in\r\n// the sloppy-trace tests) is *larger* than the ~0.01 spacing between consecutive points, so\r\n// it must be smoothed — not just scanned at a coarser step — before direction can mean\r\n// anything: a box filter (smoothCorner) first, over a window well wider than the jitter,\r\n// then a scan that measures the turn at each point using a neighbour CORNER_REACH away\r\n// (rather than the adjacent sample) so a single noisy sample can't fake a corner, while a\r\n// real corner still reads as sharp regardless of exactly where the scan lands on it. Flag\r\n// direction changes sharper than CORNER_ANGLE and merge adjacent flags into one corner,\r\n// since a real corner stays sharp for a whole CORNER_REACH-ish stretch either side of it.\r\n// (Constants tuned against tests/portal-shapes.test.mjs's own jittered traces, amount<=.045.)\r\nconst CORNER_SMOOTH_RADIUS = 10; // samples of the .01-spaced fine resample, each side (~.1 window)\r\nconst CORNER_SCAN_STEP = 0.03;\r\nconst CORNER_REACH = 0.15;\r\nconst CORNER_ANGLE = 40; // degrees: real corners here are 47-90°, background noise stays well under this\r\n\r\nconst smoothPolyline = (pts, radius) => {\r\n  const n = pts.length;\r\n  const out = [];\r\n  for (let i = 0; i < n; ++i) {\r\n    let sx = 0, sy = 0, c = 0;\r\n    for (let k = -radius; k <= radius; ++k) {\r\n      const j = i + k;\r\n      if (j < 0 || j >= n) continue;\r\n      sx += pts[j][0]; sy += pts[j][1]; ++c;\r\n    }\r\n    out.push([sx / c, sy / c]);\r\n  }\r\n  return out;\r\n};\r\n\r\nconst countCorners = (points, angleDeg = CORNER_ANGLE) => {\r\n  const fine = samplePolyline(points, 0.01);\r\n  const smoothed = smoothPolyline(fine, CORNER_SMOOTH_RADIUS);\r\n  const pts = samplePolyline(smoothed, CORNER_SCAN_STEP);\r\n  const n = pts.length;\r\n  const reach = Math.round(CORNER_REACH / CORNER_SCAN_STEP);\r\n  if (n < reach * 2 + 3) return 0;\r\n  const closed = dist(pts[0], pts[n - 1]) < CORNER_SCAN_STEP * 3;\r\n  const idxs = [];\r\n  for (let i = 0; i < n; ++i) {\r\n    if (!closed && (i - reach < 0 || i + reach >= n)) continue; // no far-enough neighbour at open endpoints\r\n    idxs.push(i);\r\n  }\r\n  const isCorner = (i) => {\r\n    const prev = pts[(i - reach + n) % n];\r\n    const cur = pts[i];\r\n    const next = pts[(i + reach) % n];\r\n    const v1 = [cur[0] - prev[0], cur[1] - prev[1]];\r\n    const v2 = [next[0] - cur[0], next[1] - cur[1]];\r\n    const m1 = Math.hypot(v1[0], v1[1]);\r\n    const m2 = Math.hypot(v2[0], v2[1]);\r\n    if (m1 < 1e-6 || m2 < 1e-6) return false;\r\n    const cos = Math.max(-1, Math.min(1, (v1[0] * v2[0] + v1[1] * v2[1]) / (m1 * m2)));\r\n    return (Math.acos(cos) * 180) / Math.PI >= angleDeg;\r\n  };\r\n  const flags = idxs.map(isCorner);\r\n  let count = 0;\r\n  for (let i = 0; i < flags.length; ++i) {\r\n    const prevFlag = i === 0 ? (closed ? flags[flags.length - 1] : false) : flags[i - 1];\r\n    if (flags[i] && !prevFlag) count++;\r\n  }\r\n  return count;\r\n};\r\n\r\n/**\r\n * Generate an ellipse as a polyline of `n` points.\r\n * The ellipse is centered at (cx,cy) with radii rx and ry.\r\n */\r\nconst makeEllipse = (cx, cy, rx, ry, n = 200) => {\r\n  const pts = [];\r\n  for (let i = 0; i < n; ++i) {\r\n    const t = (2 * Math.PI * i) / n;\r\n    pts.push([cx + rx * Math.cos(t), cy + ry * Math.sin(t)]);\r\n  }\r\n  return makePoly(pts);\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shape templates\r\n// ---------------------------------------------------------------------------\r\n\r\nconst SHAPES = {\r\n  rect: [\r\n    makePoly([\r\n      [0, 0],\r\n      [1, 0],\r\n      [1, 1],\r\n      [0, 1],\r\n      [0, 0], // closed\r\n    ]),\r\n  ],\r\n\r\n  oval: [makeEllipse(0.5, 0.5, 0.5, 0.5)],\r\n\r\n  up: [\r\n    makePoly([\r\n      [0.5, 0],\r\n      [1, 0.71],\r\n      [0, 0.71],\r\n      [0.5, 0], // closed\r\n    ]),\r\n  ],\r\n\r\n  down: [\r\n    makePoly([\r\n      [0, 0.29],\r\n      [1, 0.29],\r\n      [0.5, 1],\r\n      [0, 0.29], // closed\r\n    ]),\r\n  ],\r\n\r\n  vdiamond: [\r\n    makePoly([\r\n      [0.5, 0],\r\n      [1, 0.5],\r\n      [0.5, 1],\r\n      [0, 0.5],\r\n      [0.5, 0], // closed\r\n    ]),\r\n  ],\r\n\r\n  hdiamond: [\r\n    makePoly([\r\n      [0, 0.5],\r\n      [0.5, 0.29],\r\n      [1, 0.5],\r\n      [0.5, 0.71],\r\n      [0, 0.5], // closed\r\n    ]),\r\n  ],\r\n\r\n  x: [\r\n    makePoly([[0, 0], [1, 1]]),\r\n    makePoly([[1, 0], [0, 1]]),\r\n  ],\r\n\r\n  cross: [\r\n    makePoly([[0.5, 0], [0.5, 1]]), // vertical\r\n    makePoly([[0, 0.5], [1, 0.5]]), // horizontal\r\n  ],\r\n\r\n  line: [\r\n    makePoly([[0.5, 0], [0.5, 1]]), // vertical\r\n    makePoly([[0, 0.5], [1, 0.5]]), // horizontal\r\n  ],\r\n};\r\n\r\n// Expected corner count per template, for the tie-break in recognizeShape: polygons carry\r\n// their real vertex count (closed `points` list repeats the first point, so length - 1\r\n// unique corners); oval and line are smooth/straight and never register a corner.\r\nconst CORNER_COUNTS = Object.fromEntries(\r\n  ['rect', 'up', 'down', 'vdiamond', 'hdiamond'].map((id) => [id, SHAPES[id][0].points.length - 1])\r\n);\r\nCORNER_COUNTS.oval = 0;\r\nCORNER_COUNTS.line = 0;\r\n\r\n// ---------------------------------------------------------------------------\r\n// Recogniser\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Recognise a shape from an array of strokes.\r\n *\r\n * @param {Array<Array<[number, number]>>} strokes\r\n * @returns {string|null}\r\n */\r\nexport const recognizeShape = (strokes) => {\r\n  // Basic validation – never throw on malformed input.\r\n  if (!Array.isArray(strokes)) return null;\r\n  const tracePoints = [];\r\n  for (const stroke of strokes) {\r\n    if (!Array.isArray(stroke)) return null;\r\n    for (const pt of stroke) {\r\n      if (\r\n        !Array.isArray(pt) ||\r\n        pt.length !== 2 ||\r\n        typeof pt[0] !== 'number' ||\r\n        typeof pt[1] !== 'number' ||\r\n        !Number.isFinite(pt[0]) ||\r\n        !Number.isFinite(pt[1])\r\n      )\r\n        return null;\r\n      tracePoints.push([pt[0], pt[1]]);\r\n    }\r\n  }\r\n  if (tracePoints.length === 0) return null;\r\n  // Fast swipes arrive as a few far-apart points; fill in each stroke so coverage is measured along the path.\r\n  const dense = strokes.flatMap((stroke) => stroke.length > 1 ? samplePolyline(stroke) : stroke);\r\n  tracePoints.length = 0;\r\n  tracePoints.push(...dense);\r\n\r\n  const strokeCount = strokes.length;\r\n  let candidates = [];\r\n  if (strokeCount === 1)\r\n    candidates = [\r\n      'rect',\r\n      'up',\r\n      'down',\r\n      'vdiamond',\r\n      'hdiamond',\r\n      'oval',\r\n      'line',\r\n    ];\r\n  else if (strokeCount === 2) candidates = ['x', 'cross'];\r\n  else return null;\r\n\r\n  const tolerance = TOLERANCE.dist;\r\n  const traceLength = pathLength(strokes);\r\n  const tooLong = (polys) => traceLength > TOLERANCE.maxLength * pathLength(polys.map((p) => p.points), 0);\r\n  // Corner count of the trace, computed once and used as the primary tie-break below: more\r\n  // than one template can pass the distance/coverage check at once (a board's carved\r\n  // pattern can sit a few % off the ideal template — see countCorners above), and on raw\r\n  // mean distance alone a smooth template (oval) can numerically edge out a cornered\r\n  // template that's the visually obvious match, or vice versa.\r\n  const traceCorners = strokeCount === 1 ? countCorners(tracePoints) : 0;\r\n  let bestId = null;\r\n  let bestCornerDiff = Infinity; // primary: |expected corners - trace corners|\r\n  let bestScore = Infinity; // secondary: mean distance, both ways\r\n\r\n  const consider = (id, templatePts) => {\r\n    const traceWithin =\r\n      tracePoints.filter((p) => minDistToSet(p, templatePts) <= tolerance).length /\r\n      tracePoints.length;\r\n    const templateWithin =\r\n      templatePts.filter((tp) => minDistToSet(tp, tracePoints) <= tolerance).length /\r\n      templatePts.length;\r\n    if (traceWithin < TOLERANCE.cover || templateWithin < TOLERANCE.cover) return;\r\n\r\n    let sum = 0;\r\n    for (const p of tracePoints) sum += minDistToSet(p, templatePts);\r\n    let sum2 = 0;\r\n    for (const tp of templatePts) sum2 += minDistToSet(tp, tracePoints);\r\n    const score = (sum / tracePoints.length + sum2 / templatePts.length) / 2;\r\n    const cornerDiff = strokeCount === 1 ? Math.abs((CORNER_COUNTS[id] ?? 0) - traceCorners) : 0;\r\n\r\n    if (cornerDiff < bestCornerDiff || (cornerDiff === bestCornerDiff && score < bestScore)) {\r\n      bestCornerDiff = cornerDiff;\r\n      bestScore = score;\r\n      bestId = id;\r\n    }\r\n  };\r\n\r\n  for (const id of candidates) {\r\n    if (id === 'line') {\r\n      // Evaluate vertical and horizontal orientations separately.\r\n      if (tooLong(SHAPES['line'].slice(0, 1))) continue;\r\n      for (let i=0;i<SHAPES.line.length;i++) {const raw=strokes[0],first=raw[0],last=raw[raw.length-1];const name=i===0?(last[1]>=first[1]?'line-down':'line-up'):(last[0]>=first[0]?'line-lr':'line-rl');consider(name,SHAPES.line[i].sampled);}\r\n    } else {\r\n      if (tooLong(SHAPES[id])) continue;\r\n      consider(id, SHAPES[id].flatMap((poly) => poly.sampled));\r\n    }\r\n  }\r\n\r\n  return bestId;\r\n};\r\n\r\n/**\r\n * #21 \"almost\": the closest candidate even when it fails TOLERANCE.cover, for a near-miss hint.\r\n * Reuses recognizeShape's own coverage math (same candidates, same tolerance, same tooLong guard)\r\n * without the pass/fail gate, so it stays in lockstep with what recognizeShape accepts.\r\n *\r\n * @param {Array<Array<[number, number]>>} strokes\r\n * @returns {{id:string,score:number}|null} score is min(traceWithin,templateWithin) in [0,1); recognizeShape\r\n *  would accept the same candidate once that reaches TOLERANCE.cover. Never throws; null on malformed input,\r\n *  no strokes, or a trace too long/short-stroked to score against anything.\r\n */\r\nexport const nearestShape = (strokes) => {\r\n  if (!Array.isArray(strokes)) return null;\r\n  const tracePoints = [];\r\n  for (const stroke of strokes) {\r\n    if (!Array.isArray(stroke)) return null;\r\n    for (const pt of stroke) {\r\n      if (\r\n        !Array.isArray(pt) ||\r\n        pt.length !== 2 ||\r\n        typeof pt[0] !== 'number' ||\r\n        typeof pt[1] !== 'number' ||\r\n        !Number.isFinite(pt[0]) ||\r\n        !Number.isFinite(pt[1])\r\n      )\r\n        return null;\r\n      tracePoints.push(pt);\r\n    }\r\n  }\r\n  if (tracePoints.length === 0) return null;\r\n  const dense = strokes.flatMap((stroke) => stroke.length > 1 ? samplePolyline(stroke) : stroke);\r\n  const points = dense.length ? dense : tracePoints;\r\n\r\n  const strokeCount = strokes.length;\r\n  let candidates = [];\r\n  if (strokeCount === 1)\r\n    candidates = ['rect', 'up', 'down', 'vdiamond', 'hdiamond', 'oval', 'line'];\r\n  else if (strokeCount === 2) candidates = ['x', 'cross'];\r\n  else return null;\r\n\r\n  const tolerance = TOLERANCE.dist;\r\n  const traceLength = pathLength(strokes);\r\n  const tooLong = (polys) => traceLength > TOLERANCE.maxLength * pathLength(polys.map((p) => p.points), 0);\r\n  let best = null;\r\n  const consider = (id, templatePts) => {\r\n    const traceWithin =\r\n      points.filter((p) => minDistToSet(p, templatePts) <= tolerance).length / points.length;\r\n    const templateWithin =\r\n      templatePts.filter((tp) => minDistToSet(tp, points) <= tolerance).length / templatePts.length;\r\n    const score = Math.min(traceWithin, templateWithin);\r\n    if (!best || score > best.score) best = { id, score };\r\n  };\r\n\r\n  for (const id of candidates) {\r\n    if (id === 'line') {\r\n      if (tooLong(SHAPES.line.slice(0, 1))) continue;\r\n      const raw = strokes[0], first = raw[0], last = raw[raw.length - 1];\r\n      SHAPES.line.forEach((poly, i) => {\r\n        const name = i === 0\r\n          ? (last[1] >= first[1] ? 'line-down' : 'line-up')\r\n          : (last[0] >= first[0] ? 'line-lr' : 'line-rl');\r\n        consider(name, poly.sampled);\r\n      });\r\n    } else {\r\n      if (tooLong(SHAPES[id])) continue;\r\n      consider(id, SHAPES[id].flatMap((poly) => poly.sampled));\r\n    }\r\n  }\r\n  return best;\r\n};\r\n\r\nexport { SHAPE_IDS, SHAPES };\r\n","modules/portal/portal-tunnel-clay.mjs":"// Material tunnel; PCG hash from shader-art-coding/glsl/hash.glsl (Jarzynski & Olano, JCGT 2020).\r\nexport const material=\"\\r\\n\\r\\n// Library code below is bundled from shader-skills (MIT): https://github.com/gokayfem/shader-skills\\r\\n// ---- shader-skills library: hash ----\\r\\n// PCG-style 1D integer hash.\\r\\nuint pcgHash(uint v) {\\r\\n    uint state = v * 747796405u + 2891336453u;\\r\\n    uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\\r\\n    return (word >> 22u) ^ word;\\r\\n}\\r\\n// Map a uint to [0,1) using the top 24 bits, so the result can never round up to 1.0.\\r\\nfloat uintToUnit(uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec2 uintToUnit(uvec2 h) { return vec2(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec3 uintToUnit(uvec3 h) { return vec3(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec4 uintToUnit(uvec4 h) { return vec4(h >> 8u) * (1.0 / 16777216.0); }\\r\\n// Random float in [0,1) for an integer-valued 2D input.\\r\\nfloat hash12(vec2 p) {\\r\\n    uvec2 q = uvec2(ivec2(floor(p)));\\r\\n    return uintToUnit(pcgHash(q.x + pcgHash(q.y)));\\r\\n}\\r\\n\\r\\n// ---- shader ----\\r\\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\\r\\n float roll=a*16.+v*1.8+.45*sin(v*.7),ridge=.5+.5*cos(roll),light=.5+.5*sin(roll+.8);\\r\\n vec3 clay=mix(seq(2.)*.22,seq(0.),smoothstep(.02,.8,ridge));clay*=.48+.52*light;\\r\\n float groove=pow(1.-ridge,9.);clay=mix(clay,seq(3.)*.12,groove*.8*aa);\\r\\n vec2 p=vec2(a*4.,v*.9),id=floor(p),q=fract(p)-.5;float h=hash12(id);\\r\\n float chip=step(.78,h)*(1.-smoothstep(.12,.17,max(abs(q.x+.3*q.y),abs(q.y))));\\r\\n vec3 fragment=mix(seq(1.),seq(0.),step(0.,q.y))*(.65+.35*smoothstep(-.2,.1,q.x));\\r\\n return mix(clay,fragment,chip*aa)+pow(light,24.)*.12*aa;\\r\\n}\\r\\n\";\r\n","modules/portal/portal-tunnel-cogs.mjs":"// Optional pipe-and-steam tunnel, downloaded with the mechanical door. Toothed gear bands counter-rotate as the tunnel flows.\r\nexport const material=`vec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\n float edge=abs(sin(a-.7)),pipe=1.-smoothstep(.085,.115,edge);float roundness=sqrt(max(0.,1.-pow(edge/.115,2.)));\r\n float joint=1.-smoothstep(.03,.09,abs(fract(v*.55)-.5));float steam=pow(max(0.,sin(a*5.+sin(v*1.2))*sin(v*.8+a)),4.);\r\n vec3 wall=seq(0.)*.04+seq(1.)*.04*pow(.5+.5*cos(a*12.),12.);\r\n vec3 metal=seq(2.)*(.2+.8*roundness)+seq(3.)*joint;\r\n float gi=floor(v*.3),gv=fract(v*.3)-.5,dir=mod(gi,2.)*2.-1.;\r\n float tooth=smoothstep(-.2,.2,sin(a*24.+v*2.4*dir+gi*1.7));\r\n float gear=1.-smoothstep(.2+.07*tooth,.24+.07*tooth,abs(gv)),hub=1.-smoothstep(.06,.12,abs(sin(a*12.+v*1.2*dir)));\r\n vec3 brass=seq(2.)*(.55+.45*tooth)*(1.-.28*hub);\r\n return mix(mix(wall,metal,pipe),brass,gear*aa)+seq(1.)*steam*.65;\r\n}`;\r\nexport const core='#91aaa5';\r\n","modules/portal/portal-tunnel-grass.mjs":"// Optional grimoire tunnel material; downloaded with its board. Blades and flower glints are cells in (a,v), so they stream past with travel.\r\nexport const material=`float gH(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\n float ca=a*9.549296586,cv=v*1.25;vec2 id=vec2(mod(floor(ca),60.),floor(cv)),q=vec2(fract(ca)-.5,fract(cv));\r\n float h=gH(id),n=gH(vec2(floor(a*38.2),floor(v*3.)));\r\n vec3 c=mix(seq(0.)*.52,seq(1.)*.82,n)+mix(seq(0.),seq(1.),.5)*.12*(.5+.5*sin(a*14.+v*.9));\r\n float lean=(h-.5)*.6,w=.2*(1.-q.y)+.015,blade=(1.-smoothstep(w*.6,w,abs(q.x-lean*q.y)))*step(.3,h)*aa;\r\n c=mix(c,mix(seq(0.),seq(1.)*1.18,q.y),blade);\r\n float f=gH(id+7.3),d=length((q-vec2(0.,.5))*vec2(1.,.8));\r\n float bloom=(1.-smoothstep(.07,.2,d))*step(.9,f)*aa,tw=.6+.4*sin(v*5.+f*40.);\r\n vec3 pet=f>.95?acc(1.):acc(0.);\r\n c=mix(c,pet*tw+base*.5,bloom)+pet*bloom*.35*tw;\r\n return c;\r\n}`;\r\nexport const core='#7dff5a';\r\n","modules/portal/portal-tunnel-ice.mjs":"// Optional reflective ice tunnel; packaged with the ice board.\r\nexport const material=`float iH(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\n float facet=.5+.5*sin(a*7.+sin(v*.42)*2.);float reflection=pow(max(0.,sin(a*3.+v*.75)),32.);float crack=1.-smoothstep(.014,.045,abs(sin(a*9.+sin(v*.6))));\r\n vec2 id=vec2(mod(floor(a*3.183),20.),floor(v*.9));float h=iH(id);float shard=.55+.45*h;\r\n float glint=pow(max(0.,sin(v*2.4+h*6.2832)),20.)*step(.45,h)*aa;\r\n vec3 paletteColor=seq(floor(a*3.183)+floor(v*.9));\r\n vec3 iceColor=mix(base,paletteColor,facet*shard);\r\n return vec3(.15+.42*facet*shard)+iceColor*vec3(reflection*.7+crack*.25+glint*.9);\r\n}`;\r\n","modules/portal/portal-tunnel-jelly.mjs":"// Optional grimoire tunnel material; downloaded with its board. The wall wobbles and bulges as it flows.\r\nexport const material=`vec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\n float w=.5*sin(a*6.+v*2.)+.5*sin(a*3.-v*1.3+1.);float bulge=pow(.5+.5*w,2.);\r\n float bump=pow(max(0.,sin(a*8.+sin(v))*sin(v*3.)),3.);float shine=pow(max(0.,sin(a*8.+.4+w)*sin(v*3.+.5+w)),18.);\r\n float rim=pow(1.-abs(w),5.);\r\n vec3 pal=seq(floor(a*1.273)+floor(v*.5));\r\n vec3 jellyCol=mix(pal, pal*1.2, bulge);\r\n return (.18+.35*bulge+.3*bump)*jellyCol+vec3(rim*.25)+vec3(.06+.3*shine)*aa;\r\n}`;\r\n","modules/portal/portal-tunnel-mache.mjs":"// Material tunnel; PCG hash from shader-art-coding/glsl/hash.glsl (Jarzynski & Olano, JCGT 2020).\r\nexport const material=\"\\r\\n\\r\\n// Library code below is bundled from shader-skills (MIT): https://github.com/gokayfem/shader-skills\\r\\n// ---- shader-skills library: hash ----\\r\\n// PCG-style 1D integer hash.\\r\\nuint pcgHash(uint v) {\\r\\n    uint state = v * 747796405u + 2891336453u;\\r\\n    uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\\r\\n    return (word >> 22u) ^ word;\\r\\n}\\r\\n// Map a uint to [0,1) using the top 24 bits, so the result can never round up to 1.0.\\r\\nfloat uintToUnit(uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec2 uintToUnit(uvec2 h) { return vec2(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec3 uintToUnit(uvec3 h) { return vec3(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec4 uintToUnit(uvec4 h) { return vec4(h >> 8u) * (1.0 / 16777216.0); }\\r\\n// Random float in [0,1) for an integer-valued 2D input.\\r\\nfloat hash12(vec2 p) {\\r\\n    uvec2 q = uvec2(ivec2(floor(p)));\\r\\n    return uintToUnit(pcgHash(q.x + pcgHash(q.y)));\\r\\n}\\r\\n\\r\\n// ---- shader ----\\r\\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\\r\\n vec2 p=vec2(a*2.22817,v*.6),id=floor(p),q=fract(p);float h=hash12(id);\\r\\n float torn=.018*sin(q.y*133.+h*20.)+.013*sin(q.y*53.);\\r\\n float sheet=step(.045+torn,q.x)*step(q.x,.955+torn)*step(.035,q.y)*step(q.y,.965);\\r\\n vec3 news=mix(seq(0.),seq(1.),h)*(.77+.16*q.x+.07*q.y);\\r\\n vec2 column=vec2(fract(q.x*2.),q.y);float row=floor(q.y*34.);\\r\\n float text=step(.11,column.x)*step(column.x,.72+.18*hash12(id+vec2(row,3.)))*step(.31,fract(q.y*34.))*step(fract(q.y*34.),.61);\\r\\n float words=step(.13,fract(column.x*13.+hash12(id+row)));\\r\\n float header=step(.10,q.y)*step(q.y,.16)*step(.12,q.x)*step(q.x,.88);\\r\\n news=mix(news,seq(3.)*.22,clamp(text*words+header,0.,1.)*.8*aa);\\r\\n float fiber=hash12(floor(p*vec2(220.,170.)));news*=.93+.09*fiber;\\r\\n return mix(seq(2.)*.13,news,sheet);\\r\\n}\\r\\n\";\r\n","modules/portal/portal-tunnel-palettes.mjs":"/**\r\n * Material-specific wormhole palette presets.\r\n * Pure ES module. No storage, DOM, or external dependencies.\r\n */\r\n\r\nexport { RAW_PALETTE_DATA as PALETTES };\r\nconst RAW_PALETTE_DATA = {\r\n  quilt: [{id:'patchwork',label:'Stitched cloth',colors:['#c94d70','#4f79ab','#72985e','#e5d6ad'],core:'#d9c7a6'}],\r\n  paper: [{id:'construction',label:'Graph and construction paper',colors:['#eee7cf','#73a4b8','#dc8ca9','#83a477'],core:'#d8dfd1'}],\r\n  clay: [{id:'sculpted',label:'Sakura clay',colors:['#b07acc','#8e5b99','#5e3270','#3a1a4a'],core:'#ba8cc3'}],\r\n  mache: [{id:'newsprint',label:'Torn newsprint',colors:['#d6cba7','#b9ab86','#77705a','#393329'],core:'#a99d7e'}],\r\n  pixel: [{id:'disco',label:'Pixel disco',colors:['#b64eed','#5cdaca','#b9974f','#f18bc7'],core:'#7865a8'}],\r\n  wood: [\r\n    { id: 'oak', label: 'Oak', colors: ['#D2B48C', '#C19A6B', '#A67B5B', '#8B5A2B'], core: '#8B5A2B' },\r\n    { id: 'cherry', label: 'Cherry', colors: ['#DEB887', '#CD853F', '#B22222', '#8B0000'], core: '#8B0000' },\r\n    { id: 'walnut', label: 'Walnut', colors: ['#5C4033', '#4A3020', '#3B2415', '#2C1810'], core: '#2C1810' },\r\n    { id: 'maple', label: 'Maple', colors: ['#F4A460', '#E69550', '#D88640', '#C97730'], core: '#C97730' }\r\n  ],\r\n  ice: [\r\n    { id: 'clear_crystal', label: 'Clear Crystal', colors: ['#E0F7FA', '#B2EBF2', '#80DEEA', '#4DD0E1'], core: '#4DD0E1' },\r\n    { id: 'amethyst', label: 'Amethyst', colors: ['#E1BEE7', '#CE93D8', '#BA68C8', '#9C27B0'], core: '#9C27B0' },\r\n    { id: 'ruby', label: 'Ruby', colors: ['#FFCDD2', '#EF9A9A', '#E57373', '#D32F2F'], core: '#D32F2F' },\r\n    { id: 'emerald', label: 'Emerald', colors: ['#C8E6C9', '#A5D6A7', '#81C784', '#4CAF50'], core: '#4CAF50' },\r\n    { id: 'sapphire', label: 'Sapphire', colors: ['#BBDEFB', '#90CAF9', '#64B5F6', '#2196F3'], core: '#2196F3' }\r\n  ],\r\n  cogs: [\r\n    { id: 'steel', label: 'Steel', colors: ['#CFD8DC', '#B0BEC5', '#90A4AE', '#78909C'], core: '#78909C' },\r\n    { id: 'iron', label: 'Iron', colors: ['#757575', '#616161', '#484848', '#303030'], core: '#303030' },\r\n    { id: 'copper', label: 'Copper', colors: ['#FFCC80', '#FFB74D', '#FFA726', '#FB8C00'], core: '#FB8C00' },\r\n    { id: 'bronze', label: 'Bronze', colors: ['#D7CCC8', '#BCAAA4', '#A1887F', '#8D6E63'], core: '#8D6E63' },\r\n    { id: 'silver', label: 'Silver', colors: ['#ECEFF1', '#CFD8DC', '#B0BEC5', '#90A4AE'], core: '#90A4AE' },\r\n    { id: 'gold', label: 'Gold', colors: ['#FFF9C4', '#FFF59D', '#FFF176', '#FFEE58'], core: '#FFEE58' }\r\n  ],\r\n  grass: [\r\n    { id: 'meadow', label: 'Meadow Greens', colors: ['#70A64D', '#326B3D', '#8CC25A', '#4E8A3F'], accents: ['#F3D66B', '#F09BB8'], core: '#7AC85B' },\r\n    { id: 'wildflowers', label: 'Wildflowers', colors: ['#6E9F45', '#28593A', '#86B84F', '#3F7A44'], accents: ['#E87AAE', '#9B72D2'], core: '#72BF55' },\r\n    { id: 'daisies', label: 'Daisies', colors: ['#76A84D', '#315F3A', '#8FC95F', '#4A8742'], accents: ['#FFFDF2', '#F4D64E'], core: '#80C75B' }\r\n  ],\r\n  jelly: [\r\n    { id: 'candy', label: 'Candy', colors: ['#F48FB1', '#CE93D8', '#90CAF9', '#80CBC4'], core: '#CE93D8' },\r\n    { id: 'neon', label: 'Neon', colors: ['#E040FB', '#D500F9', '#AA00FF', '#6200EA'], core: '#6200EA' },\r\n    { id: 'cosmic', label: 'Cosmic', colors: ['#7E57C2', '#5E35B1', '#4527A0', '#311B92'], core: '#311B92' },\r\n    { id: 'fruit', label: 'Fruit', colors: ['#FFAB91', '#FF8A65', '#FF7043', '#F4511E'], core: '#F4511E' }\r\n  ],\r\n  pond: [\r\n    { id: 'brackish', label: 'Brackish', colors: ['#0E3B3A', '#2E6B5C', '#7FB8A0', '#4F7A2E'], core: '#5FD3B8' },\r\n    { id: 'koi', label: 'Koi', colors: ['#0B2E33', '#1F5C66', '#FF8A3D', '#3F6F2A'], core: '#FFB067' },\r\n    { id: 'lotus', label: 'Lotus', colors: ['#12313A', '#3B6E73', '#FFC2D9', '#4D7B33'], core: '#FFD3E4' },\r\n    { id: 'moonlit', label: 'Moonlit', colors: ['#071A2B', '#1D4766', '#9CC9E0', '#2F5A4A'], core: '#CFE8F5' }\r\n  ]\r\n};\r\n\r\n// Helper to check if a string is a valid 6-digit hex color\r\nfunction isValidHexColor(color) {\r\n  return typeof color === 'string' && /^#[0-9A-Fa-f]{6}$/.test(color);\r\n}\r\n\r\n// Helper to validate a palette entry\r\nfunction isValidPaletteEntry(entry) {\r\n  if (!entry || typeof entry !== 'object') return false;\r\n  if (!entry.id || typeof entry.id !== 'string') return false;\r\n  if (!entry.label || typeof entry.label !== 'string') return false;\r\n  if (!Array.isArray(entry.colors) || entry.colors.length < 3 || entry.colors.length > 6) return false;\r\n  if (!entry.colors.every(isValidHexColor)) return false;\r\n  if (!isValidHexColor(entry.core)) return false;\r\n  // Optional flower glints kept out of the ring colours (a Grass wormhole without its material must still read green).\r\n  if (entry.accents !== undefined && !(Array.isArray(entry.accents) && entry.accents.length === 2 && entry.accents.every(isValidHexColor))) return false;\r\n  return true;\r\n}\r\n\r\n// Helper to deep clone a palette entry defensively\r\nfunction clonePaletteEntry(entry) {\r\n  return {\r\n    id: entry.id,\r\n    label: entry.label,\r\n    colors: [...entry.colors],\r\n    ...(entry.accents ? { accents: [...entry.accents] } : {}),\r\n    core: entry.core\r\n  };\r\n}\r\n\r\n// Helper to get all valid presets for a category\r\nfunction getValidPresets(category) {\r\n  const presets = RAW_PALETTE_DATA[category];\r\n  if (!Array.isArray(presets)) return [];\r\n  return presets.filter(isValidPaletteEntry).map(clonePaletteEntry);\r\n}\r\n\r\n// Helper to find the first valid preset in a category\r\nfunction getFirstValidPreset(category) {\r\n  const presets = getValidPresets(category);\r\n  return presets.length > 0 ? presets[0] : null;\r\n}\r\n\r\n/**\r\n * Get a specific palette for a board ID and preset ID.\r\n * @param {string} boardId - The board identifier.\r\n * @param {string} presetId - The preset identifier.\r\n * @returns {object|null} - A defensive copy of the palette object, or null.\r\n */\r\nexport function paletteFor(boardId, presetId) {\r\n  // Check for null/undefined boardId\r\n  if (boardId == null) return null;\r\n\r\n  // Check for prototype property access (e.g., __proto__, constructor, toString)\r\n  if (Object.prototype.hasOwnProperty.call(Object, boardId) || boardId in Object.prototype) {\r\n    return null;\r\n  }\r\n\r\n  // Check if boardId is a direct property of RAW_PALETTE_DATA\r\n  if (!Object.prototype.hasOwnProperty.call(RAW_PALETTE_DATA, boardId)) {\r\n    return null;\r\n  }\r\n\r\n  const presets = RAW_PALETTE_DATA[boardId];\r\n  if (!Array.isArray(presets)) return null;\r\n\r\n  // If presetId is provided, find the matching preset\r\n  if (presetId != null && typeof presetId === 'string') {\r\n    const found = presets.find(p => p.id === presetId);\r\n    if (found && isValidPaletteEntry(found)) {\r\n      return clonePaletteEntry(found);\r\n    }\r\n  }\r\n\r\n  // Return the first valid preset as default\r\n  return getFirstValidPreset(boardId);\r\n}\r\n\r\n/**\r\n * Get all available palette options for a board ID.\r\n * @param {string} boardId - The board identifier.\r\n * @returns {Array} - An array of defensive copies of palette objects.\r\n */\r\nexport function paletteOptions(boardId) {\r\n  // Check for null/undefined boardId\r\n  if (boardId == null) return [];\r\n\r\n  // Check for prototype property access\r\n  if (Object.prototype.hasOwnProperty.call(Object, boardId) || boardId in Object.prototype) {\r\n    return [];\r\n  }\r\n\r\n  // Check if boardId is a direct property of RAW_PALETTE_DATA\r\n  if (!Object.prototype.hasOwnProperty.call(RAW_PALETTE_DATA, boardId)) {\r\n    return [];\r\n  }\r\n\r\n  return getValidPresets(boardId);\r\n}\r\n","modules/portal/portal-tunnel-paper.mjs":"// Material tunnel; PCG hash from shader-art-coding/glsl/hash.glsl (Jarzynski & Olano, JCGT 2020).\r\nexport const material=\"\\r\\n\\r\\n// Library code below is bundled from shader-skills (MIT): https://github.com/gokayfem/shader-skills\\r\\n// ---- shader-skills library: hash ----\\r\\n// PCG-style 1D integer hash.\\r\\nuint pcgHash(uint v) {\\r\\n    uint state = v * 747796405u + 2891336453u;\\r\\n    uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\\r\\n    return (word >> 22u) ^ word;\\r\\n}\\r\\n// Map a uint to [0,1) using the top 24 bits, so the result can never round up to 1.0.\\r\\nfloat uintToUnit(uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec2 uintToUnit(uvec2 h) { return vec2(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec3 uintToUnit(uvec3 h) { return vec3(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec4 uintToUnit(uvec4 h) { return vec4(h >> 8u) * (1.0 / 16777216.0); }\\r\\n// Random float in [0,1) for an integer-valued 2D input.\\r\\nfloat hash12(vec2 p) {\\r\\n    uvec2 q = uvec2(ivec2(floor(p)));\\r\\n    return uintToUnit(pcgHash(q.x + pcgHash(q.y)));\\r\\n}\\r\\n\\r\\n// ---- shader ----\\r\\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\\r\\n vec2 p=vec2(a*2.54648,v*.65),id=floor(p),q=fract(p)-.5;\\r\\n float h=hash12(id),tear=.012*sin(q.y*91.+h*28.);\\r\\n float edge=max(abs(q.x)+tear,abs(q.y)*1.12),sheet=1.-smoothstep(.445,.46,edge);\\r\\n vec3 paper=seq(mod(floor(h*7.),4.));\\r\\n float grid=max(1.-smoothstep(.025,.055,abs(fract(q.x*12.)-.5)),1.-smoothstep(.025,.055,abs(fract(q.y*17.)-.5)));\\r\\n paper=mix(paper,vec3(.24,.42,.56),grid*.28*aa);\\r\\n float fold=exp(-abs(q.x+.17*q.y)*48.);paper*=.72+.28*smoothstep(-.46,.3,q.x+q.y);\\r\\n paper+=fold*.15;float glue=exp(-abs(edge-.442)*180.)*.22;\\r\\n return mix(seq(1.)*.13,paper,sheet)+vec3(glue)*aa;\\r\\n}\\r\\n\";\r\n","modules/portal/portal-tunnel-pixel.mjs":"// Material tunnel; PCG hash from shader-art-coding/glsl/hash.glsl (Jarzynski & Olano, JCGT 2020).\r\nexport const material=\"\\r\\n\\r\\n// Library code below is bundled from shader-skills (MIT): https://github.com/gokayfem/shader-skills\\r\\n// ---- shader-skills library: hash ----\\r\\n// PCG-style 1D integer hash.\\r\\nuint pcgHash(uint v) {\\r\\n    uint state = v * 747796405u + 2891336453u;\\r\\n    uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\\r\\n    return (word >> 22u) ^ word;\\r\\n}\\r\\n// Map a uint to [0,1) using the top 24 bits, so the result can never round up to 1.0.\\r\\nfloat uintToUnit(uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec2 uintToUnit(uvec2 h) { return vec2(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec3 uintToUnit(uvec3 h) { return vec3(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec4 uintToUnit(uvec4 h) { return vec4(h >> 8u) * (1.0 / 16777216.0); }\\r\\n// Random float in [0,1) for an integer-valued 2D input.\\r\\nfloat hash12(vec2 p) {\\r\\n    uvec2 q = uvec2(ivec2(floor(p)));\\r\\n    return uintToUnit(pcgHash(q.x + pcgHash(q.y)));\\r\\n}\\r\\n\\r\\n// ---- shader ----\\r\\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\\r\\n vec2 p=vec2(a*7.639437,v*2.),id=floor(p),q=fract(p);float h=hash12(id);\\r\\n float tile=step(.06,q.x)*step(.06,q.y),beat=step(.5,sin(v*.9+floor(id.x)*.8));\\r\\n vec3 c=mix(seq(2.)*.12,mix(seq(0.)*.3,seq(1.)*.55,mod(id.x+id.y,2.)),tile);\\r\\n float mirror=step(.88,h)*step(.27,q.x)*step(q.x,.73)*step(.27,q.y)*step(q.y,.73);\\r\\n c+=seq(mod(id.x+floor(v),4.))*mirror*(.4+.9*beat)*aa;\\r\\n float beam=step(.975,fract((a+uSpin*.3)*3.81972+v*.04));c+=seq(mod(floor(a*3.),4.))*beam*.85*aa;\\r\\n return floor(max(c,0.)*14.)/14.;\\r\\n}\\r\\n\";\r\n","modules/portal/portal-tunnel-pond.mjs":"// Optional grimoire tunnel material; downloaded with its board. A murky water well: rippling rings, silt drifting\r\n// down the walls, lily pads floating past and koi shadows sliding through the green.\r\nexport const material=`float pH(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\n float rip=.5+.5*sin(v*6.2832+sin(a*4.+v)*1.4),silt=pH(vec2(floor(a*40.),floor(v*6.)));\r\n vec3 c=mix(seq(0.)*.18,seq(1.)*.5,rip*.7+silt*.3);\r\n c+=seq(2.)*.25*pow(max(0.,sin(a*5.-v*2.3)),8.)*aa;\r\n float ca=a*4.774648,cv=v*.9;vec2 id=vec2(mod(floor(ca),30.),floor(cv)),q=vec2(fract(ca)-.5,fract(cv)-.5);float h=pH(id);\r\n float pad=(1.-smoothstep(.22,.27,length(q)))*step(.62,h)*step(.12,abs(atan(q.y,q.x)-h*6.))*aa;\r\n c=mix(c,seq(3.)*(.55+.3*h),pad);\r\n float koi=(1.-smoothstep(.1,.5,length(vec2(fract(a*.477+v*.11)-.5,(fract(v*.35)-.5)*3.))))*.6;\r\n return mix(c,c*.25,koi)+base*.12;\r\n}`;\r\nexport const core='#5fd3b8';\r\n","modules/portal/portal-tunnel-quilt.mjs":"// Material tunnel; PCG hash from shader-art-coding/glsl/hash.glsl (Jarzynski & Olano, JCGT 2020).\r\nexport const material=\"\\r\\n\\r\\n// Library code below is bundled from shader-skills (MIT): https://github.com/gokayfem/shader-skills\\r\\n// ---- shader-skills library: hash ----\\r\\n// PCG-style 1D integer hash.\\r\\nuint pcgHash(uint v) {\\r\\n    uint state = v * 747796405u + 2891336453u;\\r\\n    uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\\r\\n    return (word >> 22u) ^ word;\\r\\n}\\r\\n// Map a uint to [0,1) using the top 24 bits, so the result can never round up to 1.0.\\r\\nfloat uintToUnit(uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec2 uintToUnit(uvec2 h) { return vec2(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec3 uintToUnit(uvec3 h) { return vec3(h >> 8u) * (1.0 / 16777216.0); }\\r\\nvec4 uintToUnit(uvec4 h) { return vec4(h >> 8u) * (1.0 / 16777216.0); }\\r\\n// Random float in [0,1) for an integer-valued 2D input.\\r\\nfloat hash12(vec2 p) {\\r\\n    uvec2 q = uvec2(ivec2(floor(p)));\\r\\n    return uintToUnit(pcgHash(q.x + pcgHash(q.y)));\\r\\n}\\r\\n\\r\\n// ---- shader ----\\r\\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\\r\\n vec2 p=vec2(a*2.54648,v*.6),id=floor(p),q=fract(p);float h=hash12(id);\\r\\n vec3 cloth=seq(floor(h*4.))*(.62+.23*sin(q.x*3.14159));\\r\\n float seam=1.-smoothstep(.025,.055,min(min(q.x,1.-q.x),min(q.y,1.-q.y)));\\r\\n float stitch=seam*step(.52,fract((q.x+q.y)*31.));\\r\\n float weave=.5+.5*sin(q.x*220.)*sin(q.y*220.);\\r\\n return mix(cloth*(.88+.12*weave*aa),seq(3.)*.35,seam*.7)+vec3(.72,.66,.48)*stitch*.5*aa;\\r\\n}\\r\\n\";\r\n","modules/portal/portal-tunnel-wood.mjs":"// Optional grimoire tunnel material; downloaded with its board. Cracks pulse and embers stream past with travel.\r\nexport const material=`float wH(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\r\nvec3 material(float a,float v,float z,float r,float aa,vec3 base){\r\nfloat bark=.5+.5*sin(a*28.+sin(v*.8)*2.);float crack=pow(1.-bark,16.);float ember=(.45+.55*sin(v*2.+a*3.))*crack;float ash=fract(sin(floor(a*150.)+floor(v*42.)*17.)*437.);\r\n float ca=a*9.549296586,cv=v*1.6;vec2 id=vec2(mod(floor(ca),60.),floor(cv)),q=vec2(fract(ca)-.5,fract(cv)-.5);float h=wH(id);\r\n float spark=(1.-smoothstep(.03,.11,length(q*vec2(1.,.6))))*step(.72,h)*(.5+.5*sin(v*6.+h*40.))*aa;\r\n vec3 woodBase=mix(seq(0.)*.05,seq(1.)*.32,bark);\r\n return woodBase*(.48+.52*ash)+base*ember*.9+mix(seq(2.),seq(3.),.5)*spark*1.4;\r\n}`;\r\nexport const core='#fa5815';\r\n","modules/portal/portal.css":"#portalHome{position:fixed;inset:0;z-index:35;background:#150f1e;overflow:hidden;touch-action:none}\r\n#portalHome[hidden]{display:none}\r\n#portalHome[hidden][data-building]{display:block;visibility:hidden;pointer-events:none}\r\n/* #111 MOM Inc metal frame. The board fills the window behind the overlay controls; its rail runs at the physical\r\n   perimeter. A board sets --face-* (its face box in the host; default: the whole\r\n   host) and the frame wraps it, above the board canvas, under the glass bezel and the trace canvas: it never takes a\r\n   pointer, and it scales with #portalHome in a dive to a home/nav destination (a dialog destination keeps the frame\r\n   still: see #portalChrome). Metal after the pod capsule and control board: CSS only. */\r\n#portalHome,#portalChrome{--portal-rail:15px}\r\n/* War Room metal (portal.mjs applyLookVars): unset keeps the stock look; set, the rail shades derive from one colour. */\r\n#portalHome,#portalChrome{--pm:var(--portal-metal)}\r\n.portal-frame{--mh:color-mix(in srgb,var(--pm) 62%,#fff);--md:color-mix(in srgb,var(--pm) 75%,#000);--mx:color-mix(in srgb,var(--pm) 55%,#000);--me:color-mix(in srgb,var(--pm) 90%,#fff)}\r\n#portalHome,#portalChrome{--portal-top-band:52px;--portal-key-w:100px;--portal-key-end:124px}\r\n#portalBoardHost{position:absolute;z-index:1;inset:var(--portal-top-band) var(--portal-rail) calc(var(--myr5-bar,88px) + var(--portal-rail))}\r\n.portal-frame{--screw:#cbbab0 0 1.4px,#57474f 1.9px 3px,#0000 3.5px;position:absolute;z-index:1;box-sizing:border-box;left:var(--face-left,0px);top:var(--face-top,0px);width:var(--face-width,100%);height:var(--face-height,100%);border-radius:3px;pointer-events:none;box-shadow:0 0 0 calc(var(--portal-rail) + 1px) #0e0a12,0 8px 16px calc(var(--portal-rail) - 2px) #000b}\r\n.portal-frame:before{content:'';position:absolute;inset:calc(-1*var(--portal-rail));padding:var(--portal-rail);border-radius:calc(var(--portal-rail)/2 + 3px);background:repeating-linear-gradient(90deg,#0000 0 3px,#ffffff06 3px 4px),repeating-linear-gradient(0deg,#0000 0 2px,#00000010 2px 3px),linear-gradient(160deg,var(--mh,#7e6a7c),var(--pm,#4d3d4f) 26%,var(--md,#3b2f42) 52%,var(--mx,#2b2132) 78%,var(--me,#564458));box-shadow:inset 0 1px #e6d2da99,inset 1px 0 #d6c0cc44,inset 0 -2px #000a,inset -1px 0 #0007;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}\r\n/* The lip: a dark seam, a back-lit bevel line on the rail's inner edge, and the rail's shadow falling onto the board. */\r\n.portal-frame:after{content:'';position:absolute;inset:0;border-radius:inherit;box-shadow:0 0 0 1px #0e0a12,0 0 0 2.5px #bba4b355,inset 0 4px 6px #000b,inset 3px 0 5px #0007,inset -2px 0 4px #0006,inset 0 -2px 4px #0007}\r\n/* Hex bolts on washers at the rail's centre line: --x/--y are 0/1 for the sides, fractions along them. */\r\n.portal-frame i{position:absolute;width:10px;height:10px;margin:-5px;left:calc(var(--x)*(100% + var(--portal-rail)) - var(--portal-rail)/2);top:calc(var(--y)*(100% + var(--portal-rail)) - var(--portal-rail)/2);border-radius:50%;background:radial-gradient(circle at 50% 40%,#3a2d3e,#150f19 70%);box-shadow:0 1px 1px #000a,0 -1px #e6d2da40}\r\n.portal-frame i:before{content:'';position:absolute;inset:1px;clip-path:polygon(25% 6%,75% 6%,100% 50%,75% 94%,25% 94%,0 50%);background:radial-gradient(circle at 34% 28%,#f4e8dd,#bca6b2 32%,#6d5a6b 68%,#3a2d3e)}\r\n/* Energy (Ian 23 Sept): a recessed channel down the middle of each rail, each holding a neon stripe (JS sets --energy,\r\n   hard colour stops; --energy-px, one cycle) that slides along it by transform. Faint at rest, bright (.hot) while a\r\n   portal opens or closes; the joins sit under the corner bolts. */\r\n.portal-energy{position:absolute;inset:calc(var(--portal-rail)/-2);opacity:.55;transition:opacity .4s}\r\n.portal-energy.hot{opacity:1}\r\n.portal-energy>span{position:absolute;overflow:hidden;border-radius:2px;background:#0d0a10;box-shadow:0 0 0 1px #0000004d,0 0 5px var(--portal-strip-glow,#b026ff40)}\r\n.portal-energy>span:nth-child(odd){left:0;right:0;height:4px}\r\n.portal-energy>span:nth-child(1){top:-2px}\r\n.portal-energy>span:nth-child(3){bottom:-2px}\r\n.portal-energy>span:nth-child(even){top:0;bottom:0;width:4px}\r\n.portal-energy>span:nth-child(2){right:-2px}\r\n.portal-energy>span:nth-child(4){left:-2px}\r\n.portal-energy>span>span{position:absolute;left:0;top:0;background:repeating-linear-gradient(var(--dir),var(--energy));box-shadow:inset 0 0 1px 1px #0d0a10b0}\r\n.portal-energy>span:nth-child(odd)>span{--dir:90deg;height:100%;width:calc(100% + var(--energy-px))}\r\n.portal-energy>span:nth-child(even)>span{--dir:180deg;width:100%;height:calc(100% + var(--energy-px))}\r\n/* R20 (Ian 2 Oct): the MOM Inc logo engraved into the top rail, right of the Grimoire key's slot (#portalSettingsButton).\r\n   The logo's ink (pod/mom-inc-engrave.png: black deep, purple shallower) is a mask: a dark cut with a lit lower lip.\r\n   Its box is measured from the frame's top-left so it stays put through a dive; it fills a 52px top band, or shrinks\r\n   into a plain 15px rail. */\r\n.portal-frame b{--eh:clamp(11px,calc(var(--cap,var(--portal-rail)) - 28px),24px);position:absolute;left:calc(var(--portal-key-end,124px) - var(--side,var(--portal-rail)));top:calc(min(var(--cap,var(--portal-rail)),var(--portal-top-band,52px))/2 - var(--cap,var(--portal-rail)));translate:0 -50%;width:calc(var(--eh)*363/72);height:var(--eh);font-size:0;color:#0000;pointer-events:none}\r\n.portal-frame b:before,.portal-frame b:after{content:'';position:absolute;inset:0;-webkit-mask:url(/pod/mom-inc-engrave.png) center/contain no-repeat;mask:url(/pod/mom-inc-engrave.png) center/contain no-repeat}\r\n.portal-frame b:after{background:#e8d4e070;translate:0 1px}\r\n.portal-frame b:before{z-index:1;background:linear-gradient(#0b060eee,#2a1d30cc)}\r\n/* The border stays when a destination opens: #portalChrome, a top-layer copy of the frame at the board's rest box (JS sets\r\n   --face-*) above the destination dialog, with a matte of the board colour over the rest of the screen, so the dive\r\n   plays in the frame's window and the dialog (.portal-framed) sits in it. Camera-only mode hides it with every other\r\n   body child. The app's bottom bar sits below the frame: see the release 5 rules after these. */\r\n#portalChrome{position:fixed;inset:0;width:auto;height:auto;max-width:none;max-height:none;margin:0;padding:0;border:0;background:none;overflow:hidden;pointer-events:none}\r\n#portalChrome::backdrop{display:none}\r\n#portalChrome .portal-frame{box-shadow:0 0 0 calc(var(--portal-rail) + 1px) #0e0a12,0 8px 16px calc(var(--portal-rail) - 2px) #000b,0 0 0 100vmax #17111e}\r\ndialog.portal-framed{position:fixed!important;inset:auto!important;left:var(--face-left)!important;top:var(--face-top)!important;width:var(--face-width)!important;height:var(--face-height)!important;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;margin:0!important;border-radius:3px!important;box-sizing:border-box!important}\r\ndialog.portal-framed.portal-fullscreen{--portal-rail:15px;left:var(--face-left)!important;top:var(--face-top)!important;width:var(--face-width)!important;height:var(--face-height)!important;border:0!important;border-radius:3px!important;clip-path:none!important}\r\ndialog.portal-framed.portal-fullscreen.meditation-panel .meditation-speech{padding-inline:8px!important;overflow-wrap:anywhere}\r\ndialog.portal-framed::backdrop{background:#17111e!important}\r\n/* The dock and portal controls float above the cloth. They no longer reserve a blank band inside the frame. */\r\n/* #134 the energy round an open destination, in its portal's shape (JS: portal.mjs showAura). All masks drawn once; in\r\n   each fire layer a wheel of the neons (a conic gradient round the outline's centre, JS sizes it) turns by transform.\r\n   Takes no pointer (the chrome takes none). */\r\n.portal-aura{position:absolute;z-index:2;inset:0;pointer-events:none;--aura:#fff}\r\n.portal-aura>*{position:absolute;inset:0;width:100%;height:100%}\r\n.portal-aura-fire,.portal-aura-flash{display:block;-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}\r\n.portal-aura-fire{overflow:hidden}\r\n.portal-aura-fire>i{position:absolute;background:conic-gradient(var(--aura-seq));will-change:transform}\r\n.portal-aura-fire>i.b{opacity:.45;background:conic-gradient(from 90deg,var(--aura-seq))}\r\n.portal-aura-fire>.portal-aura-spec{border-radius:50%;background:radial-gradient(circle,#fff 0,#ffffffa0 22%,#fff0 68%);opacity:.75}\r\n.portal-aura-flash{background:radial-gradient(circle at var(--cx) var(--cy),#fff,color-mix(in srgb,var(--aura) 55%,#fff))}\r\n.portal-aura>b{position:absolute;inset:auto;width:6px;height:6px;margin:-3px;border-radius:50%;box-shadow:0 0 8px 2px var(--aura)}\r\n/* #132 a whole window's name rides its upper rail, outside the window. R7: a peer-through cut has none. */\r\n.portal-aura>svg{overflow:visible}\r\n.portal-aura-name{font:800 9.5px/1 ui-monospace,'Cascadia Mono',Consolas,Menlo,monospace;letter-spacing:.38em;fill:color-mix(in srgb,var(--aura) 70%,#fff);stroke:#0b0710;stroke-width:3px;stroke-linejoin:round;paint-order:stroke}\r\n/* #131 seen through the hole: the quilt stays on as the wall, so a shaped destination's backdrop is clear (the dialog's\r\n   clip-path is JS's). */\r\ndialog.portal-framed.portal-shaped::backdrop{background:none!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important}\r\n/* R7 (Ian 26 and 29 Sept): a scene seen through its cut shows the scene and nothing else, no text and none of \"the close\r\n   and all that is unimportant\": titles, Close/Done, labels, toggles, status lines, cards, the tilt chip. visibility (not\r\n   opacity), so nothing hidden takes a tap or focus. The scene's own objects stay tappable (the pyramid, the bosses, the\r\n   whiteboard, the ship). The ways on: phone back or Escape, the bar, and a double-tap to the full screen, which brings\r\n   it all back. (A flat menu in a cut, .portal-inset, is its text: it keeps it.) */\r\ndialog.portal-shaped:not(.portal-inset,.portal-leaned)>:not(#coachDock){visibility:hidden}\r\ndialog.portal-shaped:not(.portal-inset,.portal-leaned) :is(#pyramidScanner,.ach-stage,.classroom-stage,.ship-view-stage){visibility:visible}\r\ndialog.portal-shaped:not(.portal-inset,.portal-leaned) :is(.pyramid-ui,.paper-poster,.paper-note,.pyramid-loading-text,.classroom-note,.classroom-desks :is(button,figcaption),.classroom-board>*,.ship-scene-status){visibility:hidden}\r\n/* R7: no two-finger zoom while the portal or any menu is up (framed or opened from the bar); one finger still scrolls, taps\r\n   and traces (touch-action is intersected down the tree, so this reaches everything, top-layer menus included). */\r\nhtml:has(#portalHome:not([hidden]),dialog[open]){touch-action:pan-x pan-y}\r\n/* A flat menu in a cut: its box is the cut's largest rectangle (JS sets --face-*), its content laid out zoomed out\r\n   (--inset-zoom) and scrolling inside it, sliding a little with tilt behind the fixed wall. The bar and the portal's\r\n   buttons are not its content. */\r\ndialog.portal-framed.portal-inset{padding:10px 8px!important;overflow:auto!important;overscroll-behavior:contain}\r\ndialog.portal-framed.portal-inset>:not(#coachDock):not(.portal-peer-ui){zoom:var(--inset-zoom,1);translate:calc(var(--peer-x,0)*3px) calc(var(--peer-y,0)*3px)}\r\ndialog.portal-framed.portal-inset :is(h1,h2){white-space:normal!important;overflow-wrap:break-word}\r\n/* The phone scoreboard normally floats Done over its heading. In the narrow diamond,\r\n   give it a separate row so zoom cannot put the button over the title. */\r\n#accountPanel.portal-inset>header{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-height:0}\r\n#accountPanel.portal-inset>header>[data-close]{position:static;grid-row:1;justify-self:end}\r\n#accountPanel.portal-inset>header>div{grid-row:2;min-width:0}\r\n#accountPanel.portal-inset>header h2{padding-top:0}\r\n/* The wormhole's still depth left in the cut round a flat menu (portal.mjs quietPhase). */\r\n.portal-glass.still{background:radial-gradient(ellipse at 50% 45%,color-mix(in srgb,var(--glass) 26%,#1b1229) 0,#110a19 62%,#08050c 100%)}\r\n.portal-glass.still>b,.portal-glass.still:after{display:none}\r\n.portal-peer-ui{position:fixed;left:0;top:0;width:0;height:0;z-index:60}\r\n.portal-peer-ui>button:not(#_){all:unset;position:fixed;box-sizing:border-box;display:grid;place-items:center;width:44px;height:44px;border-radius:50%;background:#140e1ee6;color:#fff;font:700 18px/1 system-ui,sans-serif;border:2px solid color-mix(in srgb,var(--peer-ui,#b026ff) 70%,#fff);box-shadow:0 0 0 1px #0008,0 0 14px var(--peer-ui,#b026ff),0 4px 10px #0009;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}\r\n.portal-peer-ui>button:not(#_):focus-visible{outline:3px solid var(--strip-dim);outline-offset:2px}\r\n.portal-peer-ui>.portal-tilt-chip:not(#_){width:auto;height:26px;padding:0 3px 0 10px;border-radius:8px 8px 0 0;display:flex;gap:8px;font:600 11px/1 system-ui,sans-serif}\r\n.portal-tilt-chip b{padding:4px 9px;border-radius:999px;background:#fff;color:#1a1024}\r\ndialog.portal-leaned .portal-peer-ui>[data-peer-close]:not(#_){visibility:hidden}\r\ndialog.portal-leaned .portal-peer-ui>[data-peer-lean]:not(#_){width:36px;height:36px;font-size:15px;opacity:.85}\r\n/* Closing: the destination's empty shell, shrinking into the wormhole core over the reverse dive. */\r\ndialog.portal-ghost{display:block!important;z-index:36!important;pointer-events:none!important;overflow:hidden!important}\r\n#portalShadows{position:absolute;inset:0;z-index:2;pointer-events:none;mix-blend-mode:multiply;opacity:.12}\r\n#portalShadows i{position:absolute;width:60%;height:60%;border-radius:50%;background:radial-gradient(circle,#000 0%,transparent 70%);filter:blur(30px)}\r\n#portalShadows i:nth-child(1){left:-10%;top:-10%;animation:portal-drift-a 34s ease-in-out infinite alternate}\r\n#portalShadows i:nth-child(2){right:-15%;top:20%;animation:portal-drift-b 41s ease-in-out infinite alternate}\r\n#portalShadows i:nth-child(3){left:20%;bottom:-20%;animation:portal-drift-c 27s ease-in-out infinite alternate}\r\n@keyframes portal-drift-a{from{transform:translate(0,0)}to{transform:translate(18%,12%)}}\r\n@keyframes portal-drift-b{from{transform:translate(0,0)}to{transform:translate(-14%,10%)}}\r\n@keyframes portal-drift-c{from{transform:translate(0,0)}to{transform:translate(10%,-16%)}}\r\n@media (prefers-reduced-motion:reduce){#portalShadows i{animation:none!important}}\r\n#portalOverlay{position:absolute;inset:0;z-index:5;width:100%;height:100%;touch-action:none}\r\n#portalObjects{position:absolute;inset:0;z-index:8;pointer-events:none}\r\n.portal-object{position:fixed;left:0;top:0;width:64px;height:64px;margin:-32px 0 0 -32px;border-radius:50%;display:grid;place-items:center;color:#fff;background:radial-gradient(circle,color-mix(in srgb,var(--glow) 40%,#1a1024) 0%,transparent 72%);box-shadow:0 0 22px var(--glow),inset 0 0 12px var(--glow);transform:scale(1) rotate(0deg);opacity:1;transition:transform .6s cubic-bezier(.3,0,.7,1),opacity .6s ease,left .6s cubic-bezier(.3,0,.7,1),top .6s cubic-bezier(.3,0,.7,1);pointer-events:none;z-index:8}\r\n.portal-object.falling{transform:scale(.05) rotate(240deg);opacity:0}\r\n/* Neon liquid glass: sits between #portalHome's background and the (transparent) board canvas, boxed and clipped\r\n   to the cut shape (JS sets the box and clip), so it only shows through the hole. JS fades it in (portal-glass-in,\r\n   600 ms). The WebGL wormhole canvas draws the whole look (.gl); <b> and the sheen are the no-WebGL fallback. */\r\n.portal-glass{position:absolute;z-index:0;pointer-events:none;overflow:hidden;--glass:#fff}\r\n/* R7: without WebGL (Android after a GPU reset) this is the wormhole on every board: the neon rings (JS: --seq0/--seq, the\r\n   WebGL tunnel's ring colours) streaming into a bright core (--ring is registered so the stops can move), twisted streaks\r\n   turning with the rainbow, depth fog toward the rim. Compositor-cheap; still under reduced motion. */\r\n@property --ring{syntax:'<length>';inherits:false;initial-value:0px}\r\n.portal-glass>b{position:absolute;inset:-25%;background:radial-gradient(circle at 50% 46%,#fff 0,color-mix(in srgb,var(--glass) 40%,#fff) 3%,#fff0 13%),radial-gradient(circle at 50% 46%,#07040b00 22%,#07040b99 72%),repeating-conic-gradient(from 0deg at 50% 46%,#07040b00 0deg 9deg,#07040b40 13deg,#07040b00 17deg 24deg),repeating-radial-gradient(circle at 50% 46%,var(--seq0,#ff5f1f) var(--ring),var(--seq,#ffff33,#39ff14,#1f51ff,#b026ff,#ff10f0),var(--seq0,#ff5f1f) calc(var(--ring) + 168px))}\r\n.portal-glass>canvas{position:absolute;inset:0;width:100%;height:100%;display:block}\r\n.portal-glass.gl>b,.portal-glass.gl:after{display:none}\r\n.portal-glass:not(.gl)>b{animation:portal-rainbow-turn 9s linear infinite,portal-rings 2.4s linear infinite;transform-origin:50% 46%}\r\n@keyframes portal-rainbow-turn{to{transform:rotate(360deg)}}\r\n@keyframes portal-rings{from{--ring:0px}to{--ring:-168px}}\r\n.portal-glass:after{content:\"\";position:absolute;top:-20%;bottom:-20%;left:-60%;right:-60%;mix-blend-mode:screen;background:linear-gradient(105deg,transparent 42%,#ffffff55 49%,#ffffff1a 53%,transparent 60%);animation:portal-sheen 6s ease-in-out infinite alternate}\r\n/* Glass bezel over the board along the exact cut outline, wide enough (JS sets it) to cover the cloth's stair-stepped\r\n   hole edge: soft shadow, smoky glass lit top-left, a neon filament with its glow, a specular line. Static, no filters. */\r\n.portal-bezel{position:absolute;inset:0;width:100%;height:100%;z-index:3;pointer-events:none;overflow:visible}\r\n.portal-bezel polygon{fill:none;stroke-linejoin:round}\r\n.portal-bezel .shade{stroke:#000;stroke-opacity:.2}\r\n.portal-bezel .body{stroke:url(#portalBezelLit)}\r\n.portal-bezel .glow{stroke:var(--glass);stroke-opacity:.3}\r\n.portal-bezel .core{stroke:color-mix(in srgb,var(--glass) 55%,#fff)}\r\n.portal-bezel .spec{stroke:url(#portalBezelSpec)}\r\n.portal-bezel:is([data-material=\"paper\"],[data-material=\"mache\"]) .body{stroke:var(--glass)}\r\n.portal-bezel:is([data-material=\"paper\"],[data-material=\"mache\"]) .glow{stroke:#75694e;stroke-opacity:.25}\r\n.portal-bezel:is([data-material=\"paper\"],[data-material=\"mache\"]) .core{stroke:#f7efd9;stroke-width:1.5}\r\n.portal-bezel:is([data-material=\"paper\"],[data-material=\"mache\"]) .spec{stroke-opacity:.3}\r\n.portal-glass[data-tunnel=\"mache\"]:not(.gl)>b{background:repeating-linear-gradient(0deg,#332c2266 0 1px,transparent 1px 8px),repeating-conic-gradient(#d6cba7 0 14deg,#80755a 14deg 16deg,#b9ab86 16deg 30deg)}\r\n.portal-glass[data-tunnel=\"paper\"]:not(.gl)>b{background:repeating-linear-gradient(0deg,#73a4b844 0 1px,transparent 1px 18px),repeating-conic-gradient(#eee7cf 0 20deg,#dc8ca9 20deg 40deg,#83a477 40deg 60deg)}\r\n.portal-glass[data-tunnel=\"clay\"]:not(.gl)>b{background:repeating-radial-gradient(ellipse,#5e3270 0 12px,#b07acc 15px 22px,#3a1a4a 25px 29px)}\r\n.portal-bezel .lit{stop-color:color-mix(in srgb,var(--glass) 40%,#3a3048)}\r\n.portal-bezel .mid{stop-color:color-mix(in srgb,var(--glass) 22%,#140e1e)}\r\n.portal-bezel .far{stop-color:color-mix(in srgb,var(--glass) 12%,#08050c)}\r\n.portal-ripple{position:absolute;width:150px;height:150px;margin:-75px 0 0 -75px;border-radius:50%;border:3px solid color-mix(in srgb,var(--glass) 15%,#fff);box-shadow:0 0 0 2px color-mix(in srgb,var(--glass) 55%,#000),0 0 22px 3px #ffffff90,inset 0 0 26px #ffffffa0;opacity:0;pointer-events:none}\r\n/* A destination dialog growing out of the wormhole's core: its backdrop fades in over the dive (JS sets class + duration). */\r\n.portal-arriving::backdrop{animation:portal-glass-in var(--portal-arrive,.6s) ease both}\r\n@keyframes portal-sheen{from{transform:translateX(-28%)}to{transform:translateX(28%)}}\r\n@keyframes portal-glass-in{from{opacity:0}}\r\n@keyframes portal-ripple{from{transform:scale(.12);opacity:.95}to{transform:scale(2.2);opacity:0}}\r\n@media (prefers-reduced-motion:reduce){.portal-glass:after,.portal-glass>b{animation:none}}\r\n#portalStatus{position:fixed;left:16px;right:16px;bottom:calc(108px + env(safe-area-inset-bottom));z-index:10;margin:0;text-align:center;color:#f5d9ff;font:13px/1.4 var(--font,system-ui);text-shadow:0 1px 3px #000}\r\n#portalStatus:empty{display:none}\r\n#portalRetry{position:fixed;left:50%;bottom:calc(76px + env(safe-area-inset-bottom));z-index:11;transform:translateX(-50%);padding:9px 16px;border:1px solid #f5d9ff;border-radius:999px;background:#241331;color:#fff;font:600 14px/1.2 var(--font,system-ui);box-shadow:0 2px 10px #0008}\r\n#portalRetry[hidden]{display:none}\r\n#portalMenuButton{position:fixed;left:50%;bottom:max(16px,env(safe-area-inset-bottom));transform:translateX(-50%);z-index:10;min-height:48px;min-width:120px;padding:0 22px;border-radius:999px;border:1px solid #ffffff55;background:#1c1428e6;color:#f5eaff;font:600 15px/1 var(--font,system-ui);backdrop-filter:blur(10px);box-shadow:0 6px 20px #0007}\r\n#portalExitButton{position:fixed;right:12px;top:max(12px,env(safe-area-inset-top));z-index:10;min-height:44px;padding:0 14px;border-radius:999px;border:1px solid #ffffff55;background:#1c1428e6;color:#f5eaff;font:600 14px/1 var(--font,system-ui)}\r\n/* R20 (Ian 2 Oct): the Grimoire opener is a metal key set into the top rail at the left, like the dock keys (raised on a\r\n   dark shadow, pressed down on tap), with its label lit CRT-orange in a small recessed screen. The board starts below\r\n   the 52px top band (#portalBoardHost), so the key and the engraved logo beside it cover nothing. */\r\n#portalSettingsButton{position:fixed;left:12px;top:4px;z-index:10;box-sizing:border-box;height:44px;min-height:44px;width:var(--portal-key-w,100px);margin:0;padding:0 0 1px;border:0;border-radius:4px;background:none;-webkit-appearance:none;appearance:none;color:var(--strip-dim);font:700 11px/1 ui-monospace,'Cascadia Mono',Consolas,Menlo,monospace;letter-spacing:.12em;text-shadow:0 0 4px var(--portal-strip),0 0 9px var(--portal-strip);filter:drop-shadow(0 3px 0 #130d18) drop-shadow(0 0 1px #000);touch-action:manipulation;isolation:isolate;cursor:pointer}\r\n#portalSettingsButton::before{content:\"\";position:absolute;inset:4px 0;z-index:-1;pointer-events:none;border-radius:4px;background:linear-gradient(145deg,#a58a9c,#5a4560 55%,#3b2c42);box-shadow:inset 2px 2px #ffffff40,inset -2px -3px #0006}\r\n#portalSettingsButton::after{content:\"\";position:absolute;inset:10px 7px;z-index:-1;pointer-events:none;border-radius:3px;background:repeating-linear-gradient(#00000066 0 1px,#0000 1px 3px),radial-gradient(ellipse 90% 100% at 50% 40%,#3a2008,#150b06 70%,#050304);box-shadow:inset 0 0 6px 1px #000,0 1px #ffffff30}\r\n#portalSettingsButton:active{filter:drop-shadow(0 1px 0 #130d18) drop-shadow(0 0 1px #000);translate:0 2px}\r\n#portalSettingsButton:focus-visible,.portal-palette select:focus-visible{outline:3px solid var(--strip-dim);outline-offset:2px}\r\n#portalSettingsButton svg{display:block;width:20px;height:20px;margin:0 auto;filter:drop-shadow(0 0 4px var(--portal-strip))}\r\n/* The \"Updated: …\" toast (app-updates.mjs) sits over the portal's bottom controls at its default\r\n   position; while the quilt is up, lift it clear of the Menu button (it's exempted from the portal's\r\n   inert sweep in portal.mjs, so it stays tappable there instead). */\r\nbody:has(#portalHome:not([hidden])) .app-update-banner{bottom:calc(76px + env(safe-area-inset-bottom))}\r\n/* Release 5: Armie's inbox button (armie-inbox-ui.mjs) sits bottom-right above the bar, which on the quilt is the\r\n   metal frame's corner bolt and the board face. While the quilt is up it moves to the top-left of the band above the\r\n   frame, level with Pod (exempt from the inert sweep and in the focus cycle, portal.mjs, so it works there). */\r\nbody:has(#portalHome:not([hidden])) .armie-inbox-launcher{top:calc(max(12px,env(safe-area-inset-top)) + 2px);bottom:auto;left:auto;right:calc(12px + env(safe-area-inset-right,0px))} /* R20: top-right, clear of the Grimoire key */\r\n/* Any dialog opened over the quilt: the board should still glow through instead of going near-black. */\r\nbody:has(#portalHome:not([hidden])) dialog::backdrop{background:#0c081266}\r\n.portal-transition-flash{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:0;border:0;z-index:10000;pointer-events:none;background:radial-gradient(ellipse at center,#fff 0%,#fff8dc 45%,#ffd86a 100%);animation:portal-transition-flash .52s ease-in-out both}\r\n@keyframes portal-transition-flash{0%{opacity:0}35%{opacity:1}65%{opacity:1}100%{opacity:0}}\r\n@media(prefers-reduced-motion:reduce){.portal-transition-flash{animation:none;opacity:1}}\r\n.portal-menu{border:1px solid #ffffff33;border-radius:16px;background:#1a1224f5;color:#f5eaff;padding:18px;max-width:360px;width:88vw;max-height:min(88dvh,760px);overflow:auto;overscroll-behavior:contain}\r\n.portal-menu::backdrop{background:#0c0812cc}\r\n.portal-menu header{display:flex;align-items:center;justify-content:space-between;gap:12px}\r\n.portal-menu-grid{display:grid;gap:8px;margin:12px 0}\r\n.portal-menu-grid button{display:flex;align-items:center;min-height:44px;padding:10px 12px;border-radius:10px;background:#ffffff10;color:inherit;font:15px/1 var(--font,system-ui);text-align:left;touch-action:manipulation}\r\n.portal-menu-grid button i{display:inline-block;width:10px;height:10px;border-radius:50%;background:var(--dot);margin-right:10px;box-shadow:0 0 8px var(--dot)}\r\n.portal-board-chips{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin:0 0 12px}\r\n.portal-board-chips button{min-height:44px;padding:6px 9px;border-radius:999px;background:#ffffff10;color:inherit;font:13px/1 var(--font,system-ui);border:1px solid #ffffff2a;touch-action:manipulation}\r\n.portal-board-chips button[aria-pressed=\"true\"]{background:#ffffff30;border-color:#ffffff66}\r\n.portal-palette{display:grid;gap:6px;margin:12px 0;color:inherit;font:600 14px/1.3 var(--font,system-ui)}\r\n.portal-palette select{min-height:44px;padding:8px 10px;border:1px solid #ffffff44;border-radius:8px;background:#21172c;color:inherit;font:inherit}\r\n.portal-menu header [data-menu-close],.portal-look-controls [data-look-reset]{min-height:44px;padding:0 14px;border:1px solid #ffffff44;border-radius:8px;background:#ffffff12;color:inherit;font:600 13px/1 var(--font,system-ui);touch-action:manipulation}\r\n.portal-menu :is(button,summary,input,select):focus-visible{outline:3px solid var(--strip-dim);outline-offset:2px}\r\n.portal-look-controls{margin:10px 0;padding:8px;border:1px solid #ffffff26;border-radius:10px}\r\n.portal-look-controls legend{padding:0 6px;font:600 12px/1 var(--font,system-ui);opacity:.75}\r\n.portal-look-controls>div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}\r\n.portal-look-controls label{display:grid;justify-items:center;gap:4px;font:12px/1.2 var(--font,system-ui)}\r\n.portal-look-controls input{box-sizing:border-box;width:48px;height:44px;padding:3px;border:1px solid #ac91a4;border-radius:6px;background:#302338}\r\n.portal-look-controls [data-look-reset]{margin-top:8px}\r\n.portal-full-menu{margin:12px 0}\r\n.portal-full-menu>summary{display:flex;align-items:center;min-height:44px;padding:0 12px;border:1px solid #ffffff33;border-radius:9px;background:#ffffff10;font:600 14px/1 var(--font,system-ui);cursor:pointer;touch-action:manipulation}\r\n.portal-full-menu>summary::marker{color:#f5eaff}\r\n.portal-full-menu[open]>summary{border-color:#ffffff66}\r\n#portalMenuStatus:empty{display:none}\r\n/* The locked intake theme turns off every animation; the portal home is for everyone. */\r\nhtml[data-public-state] #portalHome .portal-glass:after{animation:portal-sheen 6s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] #portalShadows i:nth-child(1){animation:portal-drift-a 34s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] #portalShadows i:nth-child(2){animation:portal-drift-b 41s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] #portalShadows i:nth-child(3){animation:portal-drift-c 27s ease-in-out infinite alternate!important}\r\nhtml[data-public-state] #portalObjects .portal-object{transition:transform .6s cubic-bezier(.3,0,.7,1),opacity .6s ease,left .6s ease,top .6s ease!important}\r\n@media (prefers-reduced-motion:reduce){html[data-public-state] #portalHome *,html[data-public-state] #portalHome :after{animation:none!important;transition:none!important}}\r\n.portal-menu header button{min-height:44px;padding:0 16px;border-radius:10px;border:1px solid #ffffff44;background:#ffffff14;color:#f5eaff}\r\n\r\n/* Full device metalwork surrounds the proportionate board. The upper plate occupies the\r\n   unused height; texture pixels are never pulled into it. */\r\n#portalBoardHost .portal-frame{--cap:calc(var(--face-top,0px) + var(--portal-top-band));--side:calc(var(--face-left,0px) + var(--portal-rail))}\r\n#portalChrome .portal-frame{--cap:var(--face-top,15px);--side:var(--face-left,15px)}\r\n.portal-frame:before{top:calc(-1*var(--cap));left:calc(-1*var(--side));right:calc(-1*var(--side));bottom:calc(-1*var(--myr5-bar,88px));padding:var(--cap) var(--side) var(--myr5-bar,88px);background:repeating-linear-gradient(0deg,#0000 0 25px,#0b071966 26px,#d3bed92e 27px,#0000 29px),repeating-linear-gradient(90deg,#0000 0 3px,#ffffff06 3px 4px),linear-gradient(160deg,var(--mh,#7e6a7c),var(--pm,#4d3d4f) 26%,var(--md,#3b2f42) 52%,var(--mx,#2b2132) 78%,var(--me,#564458))}\r\n#coachDock.coach-dock{height:var(--myr5-bar,88px);gap:8px;padding:9px 12px calc(12px + env(safe-area-inset-bottom));border-top:0;background:repeating-linear-gradient(90deg,#0000 0 3px,#ffffff06 3px 4px),linear-gradient(#493a50,#2b2132);box-shadow:inset 0 2px #ad97ad66,inset 0 6px 9px #0007;backdrop-filter:none}\r\n#coachDock.coach-dock button,#coachDock.coach-dock button.dock-portal{position:relative;height:53px;min-width:0;border:2px solid #9a8295;border-top-color:#cbb5c5;border-bottom-color:#4b384d;border-radius:4px!important;background:linear-gradient(145deg,#806979,#4a394e 80%);box-shadow:0 0 0 3px #1c1421,0 6px 0 2px #130d18,inset 1px 2px #e0c3d366,inset -2px -3px #25182c99;color:#f6e5d5;transform:none;translate:none}\r\n#coachDock.coach-dock button:active,#coachDock.coach-dock button[aria-current=page]{transform:translateY(4px);box-shadow:0 0 0 3px #1c1421,0 2px 0 2px #130d18,inset 1px 2px 5px #0008;border-top-color:#9b8194;color:#fff3d3}\r\n#coachDock.coach-dock button:before{background:#211927;border-color:#100b16;border-radius:1px}\r\n#coachDock.coach-dock button[aria-current=page]:before{background:#e7b875;box-shadow:0 0 6px #f9c88088}\r\n#portalMenuButton,#portalExitButton{border-radius:4px;backdrop-filter:none;background:linear-gradient(#8b7586,#514055);border:2px outset #ae94a7;box-shadow:0 4px #211626,inset 0 1px #dbc1cd66}\r\n#portalMenuButton:active,#portalExitButton:active{box-shadow:0 1px #211626;translate:0 3px}\r\n.portal-color{display:flex;align-items:center;justify-content:space-between;gap:16px;font:13px system-ui;margin:12px 0}.portal-color input{width:48px;height:44px;padding:3px;border:1px solid #ac91a4;background:#302338}\r\n/* Full scenes stay inside the inset face; the room reserves its dock within that face. */\r\n#portalChrome.portal-housing-fullscreen .portal-frame:before{bottom:calc(-1*var(--portal-rail));padding-bottom:var(--portal-rail)}\r\n/* R20 (Ian 2 Oct): the dock is a fixed console below the housing on every page, never inside the frame or its glow. The\r\n   chrome (frame, matte, aura) stops at the dock's top edge, so it never paints over the keys. */\r\n#portalChrome{bottom:var(--myr5-bar)}\r\n/* Tracking and rest retain the shared lower hardware reservation. */\r\ndialog.portal-framed.portal-fullscreen #coachDock{left:0!important;right:0!important;bottom:0!important;width:auto!important;height:var(--myr5-bar)!important}\r\n\r\n\r\n/* A decoded meal photo and a classroom whiteboard tap step into the full destination. */\r\n#mealsPanel.portal-fullscreen #mealScanStage{inset:0!important;width:100%!important;height:100%!important}\r\n#mealsPanel.portal-fullscreen .food-sheet{padding-bottom:max(12px,env(safe-area-inset-bottom))}\r\n\r\n\r\n/* Dock keys: 44px hit boxes with smaller tactile silhouettes on the span; the Portal key sits at true centre. */\r\n#coachDock.coach-dock{box-sizing:border-box;height:var(--myr5-bar,88px);max-width:100vw;display:grid;grid-template-columns:repeat(2,44px) 1fr repeat(3,44px);grid-template-rows:44px;align-content:center;align-items:center;gap:0 4px;padding:0 max(8px,env(safe-area-inset-right)) env(safe-area-inset-bottom) max(8px,env(safe-area-inset-left));overflow:visible}\r\n#coachDock.coach-dock.coach-dock button.dock-control{grid-row:1;width:44px;height:44px;min-height:44px;padding:0;display:grid;place-items:center;background:none;border:0;box-shadow:none;border-radius:0!important;clip-path:none;filter:drop-shadow(0 3px 0 #130d18) drop-shadow(0 0 1px #000)}\r\n#coachDock.coach-dock.coach-dock button.dock-control:before{display:none}\r\n#coachDock .dock-control>span{width:36px;height:36px;display:grid;place-items:center;color:#f6e5d5;background:linear-gradient(145deg,#a58a9c,#5a4560 55%,#3b2c42);box-shadow:inset 2px 2px #ffffff40,inset -2px -3px #0006;transition:transform .08s}\r\n#coachDock .dock-control>span svg{width:22px;height:22px}\r\n#coachDock .dock-control:is(:active,[aria-current=page]){filter:drop-shadow(0 1px 0 #130d18)}\r\n#coachDock .dock-control:is(:active,[aria-current=page])>span{transform:translateY(2px);color:#ffd48a}\r\n#coachDock .dock-settings{grid-column:1}#coachDock .dock-settings>span{border-radius:50%}\r\n#coachDock .dock-scoreboard{grid-column:2}#coachDock .dock-scoreboard>span{border-radius:14px 14px 6px 6px}\r\n#coachDock .dock-food{grid-column:4}#coachDock .dock-food>span{width:40px;height:30px;border-radius:5px 5px 9px 9px}\r\n#coachDock .dock-achievements{grid-column:5}#coachDock .dock-achievements>span{clip-path:polygon(0 0,100% 0,100% 55%,50% 100%,0 55%);background:linear-gradient(145deg,#f2c46f,#b98033)}\r\n#coachDock .dock-pod{grid-column:6}#coachDock .dock-pod>span{width:30px;border-radius:15px}\r\n/* The existing pod sound switch (#toggleVoice, moved here by mountLaunch) sits in the blank dock slot beside the phone key: same 44px hit box, its ON/OFF text in a 36px key, its SOUND label in the spare strip under the row. */\r\n#coachDock .dock-sound{grid-row:1;position:relative;width:44px;height:44px}\r\n#coachDock.coach-dock.coach-dock .dock-sound button.dock-control{font:700 10px var(--pixel-font,ui-monospace,monospace);color:#f6e5d5;padding:4px;background:linear-gradient(145deg,#a58a9c,#5a4560 55%,#3b2c42) content-box;box-shadow:none}\r\n#coachDock .dock-sound button[data-on=true]{color:#ffd48a;text-shadow:0 0 6px #ffd36e}\r\n#coachDock .dock-sound button:disabled{opacity:.5}\r\n#coachDock .dock-sound .cb-label{position:absolute;top:100%;left:50%;translate:-50% 0;margin-top:4px;z-index:1;font:700 8px/1 ui-monospace,'Cascadia Mono',Consolas,Menlo,monospace;letter-spacing:.06em;color:#f6e5d5;white-space:nowrap;pointer-events:none}\r\n@media(orientation:landscape) and (max-height:500px){#coachDock .dock-sound .cb-label{margin-top:1px}} /* R20: fits the 64px landscape console */\r\n#coachDock.coach-dock button.dock-portal{position:absolute;left:50%;top:50%;translate:-50% -50%;width:62px;height:62px;min-width:62px;padding:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;font-size:10px;line-height:1;letter-spacing:.04em}\r\n#coachDock .dock-portal svg{width:22px;height:22px;flex:none}#coachDock .dock-portal span{max-width:100%}\r\n\r\n\r\n#coachDock .dock-live{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\r\n/* Menu content uses its natural line wrapping and an inner scroller instead of zoomed tabs that can clip on phones. */\r\n#portalMenu.portal-framed.portal-inset{box-sizing:border-box;min-width:0;min-height:0;overflow:auto!important;padding:10px!important;overscroll-behavior:contain}\r\n#portalMenu.portal-framed.portal-inset>:not(#coachDock):not(.portal-peer-ui){zoom:1!important;translate:none!important}\r\n#portalMenu .portal-menu-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:7px}\r\n#portalMenu .portal-menu-grid button{box-sizing:border-box;min-width:0;min-height:42px;padding:8px;font-size:clamp(12px,3.6vw,15px);overflow-wrap:anywhere}\r\n#portalMenu .portal-board-chips{max-width:100%;}\r\n#portalMenu .portal-board-chips button{padding:6px 9px}\r\n\r\n#mealsPanel.portal-shaped .pyramid-tag{display:none!important}\r\n\r\n\r\n#coachDock.coach-dock button>span{pointer-events:none}\r\n\r\n/* One recessed ship console follows every route. Existing buttons keep their IDs and handlers. */\r\n#coachDock.coach-dock.ship-control-deck{border-top:3px solid #b8a890;background:radial-gradient(circle at 12px 9px,#d4c3a9 0 2px,#292832 3px 5px,transparent 6px),radial-gradient(circle at calc(100% - 12px) 9px,#d4c3a9 0 2px,#292832 3px 5px,transparent 6px),repeating-linear-gradient(90deg,#ffffff08 0 1px,transparent 1px 5px),linear-gradient(180deg,#48545a,#252f38 38%,#171e27);box-shadow:inset 0 2px #e5d8bd72,inset 0 7px 8px #1117229c,0 -4px 18px #070b11a8}\r\n#coachDock.coach-dock.ship-control-deck:after{content:'';position:absolute;left:8px;right:8px;top:9px;height:1px;background:linear-gradient(90deg,transparent,#d5c9ad80 15%,#d5c9ad80 85%,transparent);pointer-events:none}\r\n#coachDock.coach-dock.ship-control-deck :is(button,a).dock-control{filter:none;overflow:visible}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span{position:relative;box-sizing:border-box;width:35px;height:35px;border:2px solid #c6b9a0;border-top-color:#f1e3c7;border-bottom-color:#665e56;border-radius:4px;clip-path:none;background:linear-gradient(145deg,#bcc8c2,#81918f 43%,#4b5c60);box-shadow:inset 0 1px #fff9,inset -2px -3px #22343b88,0 3px 0 #182127,0 5px 5px #000a;color:#243640}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span:after{content:'';position:absolute;right:2px;top:2px;width:4px;height:4px;border-radius:50%;background:#263d40;box-shadow:0 0 0 1px #e5e1cb77}\r\n#coachDock.coach-dock.ship-control-deck .dock-control[aria-current=page]>span:after{background:#ffd07c;box-shadow:0 0 6px #ffce78,0 0 0 1px #ffeed3}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span svg{width:21px;height:21px;filter:drop-shadow(0 1px #f2ffed77)}\r\n#coachDock.coach-dock.ship-control-deck .dock-control:active>span,#coachDock.coach-dock.ship-control-deck .dock-control[aria-current=page]>span{transform:translateY(3px);box-shadow:inset 1px 3px 4px #1e2d34ad,0 1px 0 #182127;color:#17282a}\r\n#coachDock.coach-dock.ship-control-deck .dock-control:focus-visible{outline:3px solid #ffe19a;outline-offset:2px}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings>span{border-radius:50%;background:radial-gradient(circle,#d6ddcf 0 24%,#8fa69e 25% 49%,#526d70 50% 67%,#b4bfb6 69%);box-shadow:inset 0 1px #fff9,0 3px #29363a,0 0 0 2px #132128}\r\n#coachDock.coach-dock.ship-control-deck .dock-scoreboard>span{border-radius:5px 5px 10px 10px;background:linear-gradient(90deg,#596b72 0 20%,#becac3 22% 72%,#596b72 75%);}\r\n#coachDock.coach-dock.ship-control-deck .dock-food>span{height:31px;border-radius:3px 3px 9px 9px;background:linear-gradient(#ffe0a9,#c69458 48%,#845837);color:#493322}\r\n#coachDock.coach-dock.ship-control-deck .dock-achievements>span{border-radius:3px;clip-path:polygon(0 0,100% 0,100% 71%,50% 100%,0 71%);background:linear-gradient(140deg,#ffe8ac,#b98b44 67%,#765223);color:#533e25}\r\n#coachDock.coach-dock.ship-control-deck .dock-pod>span{width:29px;border-radius:12px;background:linear-gradient(90deg,#496c70,#b8d1bd 35%,#6d9d92 65%,#35585d);color:#183b3c}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings:after,#coachDock.coach-dock.ship-control-deck .dock-scoreboard:after,#coachDock.coach-dock.ship-control-deck .dock-food:after,#coachDock.coach-dock.ship-control-deck .dock-achievements:after,#coachDock.coach-dock.ship-control-deck .dock-pod:after{position:absolute;top:calc(100% + 4px);left:50%;translate:-50% 0;color:#c5d5cb;font:700 7px/1 ui-monospace,Consolas,monospace;letter-spacing:.05em;white-space:nowrap;text-shadow:0 1px #071118;pointer-events:none}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings:after{content:'COACH'}#coachDock.coach-dock.ship-control-deck .dock-scoreboard:after{content:'SCORES'}#coachDock.coach-dock.ship-control-deck .dock-food:after{content:'FOOD'}#coachDock.coach-dock.ship-control-deck .dock-achievements:after{content:'BADGES'}#coachDock.coach-dock.ship-control-deck .dock-pod:after{content:'COMMS'}\r\n#coachDock.coach-dock.ship-control-deck :is(button,a).dock-portal{border:3px solid #9eb7b1;border-top-color:#e1eed6;border-bottom-color:#445b5b;border-radius:50%!important;background:radial-gradient(circle at 50% 40%,#1a4545 0 46%,#0b212a 47% 58%,transparent 59%),repeating-conic-gradient(#b6c6b3 0deg 8deg,#51666a 8deg 15deg);box-shadow:0 0 0 3px #17262b,0 4px 0 3px #0a141b,inset 0 0 11px color-mix(in srgb,var(--portal-strip,#b026ff) 49%,transparent),0 0 12px var(--portal-strip-glow,#b026ff40);color:#d7ffe6;text-shadow:0 0 5px var(--portal-strip,#b026ff)}\r\n#coachDock.coach-dock.ship-control-deck :is(button,a).dock-portal:before{display:none}\r\n#coachDock.coach-dock.ship-control-deck :is(button,a).dock-portal:active,#coachDock.coach-dock.ship-control-deck :is(button,a).dock-portal[aria-current=page]{transform:translateY(3px);box-shadow:0 0 0 3px #17262b,0 1px 0 3px #0a141b,inset 0 0 15px var(--portal-strip,#b026ff),0 0 18px var(--portal-strip,#b026ff)}\r\n#coachDock.coach-dock.ship-control-deck .dock-portal svg{width:20px;height:20px}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control{box-sizing:border-box;width:42px;height:43px;padding:15px 2px 2px;border:2px solid #afbbad;border-top-color:#e8e9d4;border-bottom-color:#566963;border-radius:4px!important;background:linear-gradient(145deg,#adbcb4,#526c6b 65%,#365154);box-shadow:inset 0 1px #fff9,inset 0 -2px #2a4747,0 3px #142324;color:#173f3a;font:700 8px ui-monospace,Consolas,monospace}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control:before{content:'';display:block;position:absolute;left:8px;top:5px;width:23px;height:8px;border:1px solid #172b2d;border-radius:2px;background:linear-gradient(90deg,#23383a 0 45%,#e4cfaa 48% 85%,#788c88 88%);box-shadow:inset 0 1px 2px #0008}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control[data-on=false]:before{background:linear-gradient(90deg,#e4cfaa 0 40%,#758c89 43%,#23383a 48%)}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control[data-on=true]{color:#f8e3ab;text-shadow:0 0 5px #ffe19a}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound .cb-label{color:#c5d5cb}\r\n/* The logo in the ship header is the single brand mark in a framed workout view. */\r\n.portal-frame>b{display:none}\r\n@media(orientation:landscape) and (max-height:500px){#coachDock.coach-dock.ship-control-deck .dock-control:after{display:none}}\r\n.portal-menu{background:#000a0f!important;border-color:#ffffff22!important;position:relative}\r\n.portal-menu::backdrop{background:#000a0fcc!important}\r\n.portal-menu header h2{color:var(--strip-dim)!important;font-family:monospace!important;text-shadow:0 0 4px var(--portal-strip)!important}\r\n.portal-menu,.portal-menu *{color:var(--strip-dim)!important;font-family:monospace!important}\r\n.portal-menu::before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background-image:repeating-linear-gradient(#000 0 1.5px,transparent 1.5px 3px);opacity:.22;animation:crt-roll 9s linear infinite;pointer-events:none;z-index:1}\r\n.portal-menu::after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(180deg,#ffffff2e 0%,#ffffff12 12%,transparent 30%),radial-gradient(ellipse 60% 40% at 24% 10%,#ffffff26,transparent 55%),radial-gradient(ellipse 75% 70% at 50% 50%,transparent 42%,#000000b3 100%);pointer-events:none;z-index:2}\r\n@keyframes crt-roll{to{background-position-y:12px}}\r\n\r\n/* Keep seven physical controls on one rail before and after scene loading. */\r\n#coachDock.coach-dock.ship-control-deck{display:flex;justify-content:space-between;align-items:center;gap:3px}\r\n#coachDock.coach-dock.ship-control-deck .dock-control,#coachDock.coach-dock.ship-control-deck .dock-sound{flex:0 0 44px;min-width:44px;width:44px}\r\n#coachDock.coach-dock.ship-control-deck .dock-portal{position:relative!important;left:auto!important;top:auto!important;translate:none!important;flex:0 0 56px;width:56px;min-width:56px}\r\n#coachDock.ship-control-deck .dock-settings{order:1}#coachDock.ship-control-deck .dock-food{order:2}#coachDock.ship-control-deck .dock-scoreboard{order:3}#coachDock.ship-control-deck .dock-portal{order:4}#coachDock.ship-control-deck .dock-achievements{order:5}#coachDock.ship-control-deck .dock-pod{order:6}#coachDock.ship-control-deck .dock-sound{order:7}\r\n#coachDock.ship-control-deck .dock-live{position:absolute}\r\n/* The hub uses the selected grimoire metal in every shared dock. */\r\n#coachDock.coach-dock.ship-control-deck :is(button,a).dock-portal{border-color:color-mix(in srgb,var(--portal-metal,#4d3d4f),#fff 40%);background:radial-gradient(circle at 50% 40%,var(--portal-metal,#4d3d4f) 0 46%,color-mix(in srgb,var(--portal-metal,#4d3d4f),#000 65%) 47% 58%,transparent 59%),repeating-conic-gradient(color-mix(in srgb,var(--portal-metal,#4d3d4f),#fff 40%) 0deg 8deg,color-mix(in srgb,var(--portal-metal,#4d3d4f),#000 30%) 8deg 15deg)}\r\n/* R3-07 compact Grimoire settings: one swatch row, texture-thumbnail chips, palette discs. Text is the strip colour (--strip-dim). */\r\n.portal-menu[open]{ display:flex; flex-wrap:wrap; align-items:center; align-content:flex-start; gap:8px; padding:12px; max-width:360px;\r\n}\r\n.portal-menu header{flex:1 1 100%;}\r\n.portal-menu header h2{font-size:14px;margin:0;}\r\n.portal-look-controls{display:contents}\r\n.portal-look-controls legend{ clip:rect(0,0,0,0); height:1px;width:1px; overflow:hidden; position:absolute;\r\n}\r\n.portal-look-controls>div{display:contents}\r\n.portal-look-controls label,.portal-color{ flex:0 0 calc((100% - 68px)/3); min-width:0; margin:0; display:grid; justify-items:center; gap:2px; font-size:10px;\r\n}\r\n.portal-look-controls input[type=color][data-look=portal],\r\n.portal-look-controls input[data-look=strip],\r\n.portal-color input[type=color][data-board-tint]{ width:100%; max-width:64px; height:44px;\r\n}\r\n[data-look-reset]{ flex:none; padding:0; display:grid; place-items:center; width:44px;height:44px;\r\n}\r\n[data-look-reset] svg{width:22px;height:22px;}\r\n.portal-color+.portal-color{flex:1 1 100%}\r\n.portal-board-chips{ flex:1 1 100%; display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:4px; margin:0;\r\n}\r\n.portal-board-chips button{ min-height:58px; padding:0 0 3px; display:flex; align-items:flex-end; justify-content:center; font-size:9px; letter-spacing:0; border-radius:6px; text-shadow:0 1px 2px #000; background-color:#1b1224; background-size:cover; background-position:center; background-repeat:no-repeat; border:1px solid #ffffff33; overflow:hidden;\r\n}\r\n.portal-board-chips button[data-board=quilt]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/quilt.webp');}\r\n.portal-board-chips button[data-board=ice]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/boards/ice-poster.webp');}\r\n.portal-board-chips button[data-board=grass]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/boards/grass-poster.webp');}\r\n.portal-board-chips button[data-board=jelly]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/boards/jelly-poster.webp');}\r\n.portal-board-chips button[data-board=wood]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/boards/wood-poster.webp');}\r\n.portal-board-chips button[data-board=pond]{background-image:linear-gradient(transparent 45%,#000000cc),url('/pod/worlds/boards/pond-poster.webp');}\r\n.portal-board-chips button[aria-pressed=true]{ border:2px solid var(--portal-strip); box-shadow:0 0 8px var(--portal-strip);\r\n}\r\n[data-palette-row]{ flex:1 1 100%;\r\n}\r\n[data-palette-row] .portal-palette{ margin:0; display:grid; gap:4px; font-size:11px;\r\n}\r\n.portal-discs{ display:flex; flex-wrap:wrap; gap:6px;\r\n}\r\n.portal-disc{ width:44px;height:44px; border-radius:50%; padding:0; background:var(--disc); border:2px solid #ffffff55;\r\n}\r\n.portal-disc[aria-pressed=true]{ border-color:var(--portal-strip); box-shadow:0 0 0 2px #000a0f,0 0 0 4px var(--portal-strip);\r\n}\r\n.portal-full-menu,#portalMenuStatus{ flex:1 1 100%; margin:0;\r\n}\r\n#portalMenuStatus{min-height:0;}\r\n.portal-menu :focus-visible{ outline:3px solid var(--strip-dim) !important; outline-offset:2px;\r\n}\r\n.portal-menu,.portal-menu *{ color:var(--strip-dim)!important;\r\n}\r\n.portal-menu header h2{ text-shadow:0 0 4px var(--portal-strip)!important;\r\n}\r\n/* Grimoire dialog hugs its content and ends above the dock (shell forces the full well height otherwise). */\r\nhtml.shell-ready dialog.portal-menu.portal-framed.shell-dialog[open]{height:auto!important;max-height:var(--face-height)!important;overflow:auto!important}\r\n","modules/portal/portal.mjs":"import {initShell,SHELL_ICONS} from '../shell/owner.mjs';\r\nimport {portalWindow,lensPts,shapeOutlinePts} from './portal-apertures.mjs';\r\nimport {createTiltLook} from '../../character-phone-sensor.mjs';\r\nexport {portalWindow,lensPts} from './portal-apertures.mjs';\r\n// Portal home board: the quilt cloth board replaces the app home menu. Tracing one of the eight\r\n// stitched shapes cuts that shape out of the 3D board — the piece falls in, neon liquid glass glows\r\n// through the hole behind it for a short interactive loading phase — then opens the shape's menu.\r\n// AGPL-3.0-or-later.\r\nimport {createQuiltBoard2D,createQuiltBoardGL,QUILT} from './portal-board.mjs';\r\nimport {createGlbBoard,frameOf,GLB} from './portal-board-glb.mjs';\r\nimport {ice} from './portal-board-ice.mjs';\r\nimport {grass} from './portal-board-grass.mjs';\r\nimport {cogs} from './portal-board-cogs.mjs';\r\nimport {jelly} from './portal-board-jelly.mjs';\r\nimport {wood} from './portal-board-wood.mjs';\r\nimport {pond} from './portal-board-pond.mjs';\r\nimport {createPixelBoard} from './portal-board-pixel.mjs';\r\nimport {createPaperBoard} from './portal-board-paper.mjs';\r\nimport {createMacheBoard} from './portal-board-mache.mjs';\r\nimport {createClayBoard} from './portal-board-clay.mjs';\r\nimport {readLook,saveLook,applyLookVars,stripSeq,LOOK_DEFAULTS,frameHtml,ENERGY,ENERGY_MOVES,energyStops} from './portal-look.mjs';\r\nimport {recognizeShape,nearestShape,SHAPES} from './portal-shapes.mjs';\r\nimport {pointInPolygon} from './portal-cut.mjs';\r\nimport {eye} from './peer.mjs';\r\nimport {mountPodChrome} from '../pod-chrome.mjs';\r\nimport {paletteFor,paletteOptions} from './portal-tunnel-palettes.mjs';\r\nimport {releaseGraphics,waitForGraphicsRelease,graphicsBlocked} from '../graphics-lifecycle.mjs';\r\n\r\n// Portal sequence timings (ms): the cut piece falling in, the minimum live-glass loading phase, the\r\n// dive into the wormhole (the destination appears from its core), the healed board fading back in, one touch ripple on\r\n// the glass. tunnelFrom/To: wormhole speed (rings per second) at the cut, ramping up to tunnelTo by the dive, which adds\r\n// up to tunnelDive more. short (#124): the lines' and the X's wormhole runs the same cut, glass and dive at this share of\r\n// the closed shapes' cut/loading/dive timings.\r\nexport const PORTAL={cutMs:867,loadMinMs:2333,revealMs:733,healMs:267,rippleMs:600,tunnelFrom:.35,tunnelTo:1.5,tunnelDive:6,short:.58};\r\n// Liquid-glass slab over the wormhole (CSS px): lens-map texel, bevel depth, max refraction at the rim,\r\n// rim inset inside the cut (the cloth hole's edge is ragged by about half a grid cell). #125 (Ian: \"stronger\"): thicker\r\n// bevel and deeper bend; fringe: the red/blue sample spread at the rim (blue bends furthest); magnify: centre lens.\r\nconst GLASS={mapPx:3,bevel:42,bend:36,rimInset:8,fringe:.3,magnify:.035};\r\n// #104/#105 (W2-2E): the six neons already used for the \"all menus\" glass (portal.css .portal-glass.all),\r\n// reused for the flowing finger-trail ribbon. IDLE (Ian 26 Sept: \"just the slow one with 7 seconds of idle time\"): 7s of\r\n// no touch arms the cycle, 2.5s per shape in the order below with a gapMs of nothing between them, until the next touch.\r\n// TRAIL_FADE_MS: how long a trail segment (live or just-released) stays lit before it's fully faded.\r\nconst TRAIL_NEONS=['#ff5f1f','#b026ff','#ff10f0','#1f51ff','#39ff14','#ffff33'];\r\nconst TRAIL_NEON_RGB=TRAIL_NEONS.map(h=>[parseInt(h.slice(1,3),16),parseInt(h.slice(3,5),16),parseInt(h.slice(5,7),16)]);\r\nconst IDLE={armMs:7000,slowMs:2500,gapMs:600};\r\n// Ian 2026-09-23: square, oval, triangle, inverted triangle, diamond, X, then the four lines; cross last.\r\nconst IDLE_ORDER=['rect','oval','up','down','vdiamond','x','line-lr','line-rl','line-down'];\r\nconst TRAIL_FADE_MS=800;\r\n// #20 double-tap to open: a second tap within TAP_MS and TAP_MOVE_PX of the first counts as one double-tap;\r\n// TAP_HIT_PX is how close (client px) it must land to a shape's stitched outline. #21 \"almost\": a failed\r\n// trace whose closest candidate (nearestShape) still covers at least ALMOST_COVER of both trace and template\r\n// gets the near-miss flash instead of silence; ALMOST_MS is how long that flash (with its label) stays up.\r\n// #22 first-run hint: HINT_MS is one lap of the glowing fingertip around the square.\r\n// ALMOST_COVER sits well above the ~0.55-0.60 a wrong-shape trace (a circle, an L, a stray diagonal) scores\r\n// against its closest candidate, and below the ~0.65-0.78 a genuine partial trace of the right shape (most\r\n// of a rect with the last side never closed) scores — calibrated against portal-shapes.test.mjs's near-miss\r\n// fixtures so a scribble never earns an \"Almost\".\r\nconst TAP_MS=350,TAP_MOVE_PX=32,TAP_HIT_PX=14,ALMOST_COVER=.65,ALMOST_MS=1200,HINT_MS=2600,HINT_KEY='myr5.portalHintShown';\r\n// Only the shapes that actually cut/glass/dive through portalSequence's default branch (SHAPES entries\r\n// that are single closed-area strokes, i.e. not 'x'/'cross'/'line') are tap targets — matches #20's \"same\r\n// cut, glass and dive\" as tracing.\r\nconst TAPPABLE_IDS=Object.keys(SHAPES).filter(id=>!['x','cross','line'].includes(id));\r\n\r\n// Board catalogue: add one line per wave-2 board here. Each board is two layers: `flat`, a still picture on a 2D canvas\r\n// (portal-board.mjs createQuiltBoard2D) that is always mounted and never needs WebGL, and `create`, the 3D board drawn over\r\n// it once it has rendered. A grimoire's flat picture is its poster, laid out at its GLB face's width/height (FACE, measured)\r\n// with its guides drawn in its shape frame. WAIT (ms): how long a poster, then a 3D board, may take before it counts as failed,\r\n// and how long the first mount waits for 3D before the portal comes up flat (3D then takes over when it has drawn).\r\nexport const PRODUCTION_PORTALS=Object.freeze(['pixel','clay','paper','mache','quilt','ice','grass','jelly','wood','pond']);\r\nconst FACE={ice:.5903,grass:.5625,cogs:.5715,jelly:.5892,wood:.5847,pond:.5625},WAIT={poster:6000,threeD:20000,mount:4000};\r\n// trace (R7): the flat poster keeps the board's own touch effect in 2D (ice cracks, flowers, weld, jelly gash, embers).\r\nconst grimoire=(label,effect)=>({label,flat:async host=>{const flat=await createQuiltBoard2D(host,{src:`/pod/worlds/boards/${effect.id}-poster.webp`,ratio:FACE[effect.id],frame:frameOf(effect,GLB),background:effect.background,guide:effect.guide,waitMs:WAIT.poster,trace:effect.trace2d,tint:effect.keepColors?null:boardTint(effect.id),tintSelected:hasBoardTint(effect.id),tintTarget:effect.id==='grass'||effect.keepColors?'trace':effect.id==='cogs'?'cogs':'poster',...(effect.id==='cogs'?{backplateTint:cogsBackplateTint(),backplateTintSelected:hasCogsBackplateTint()}: {})});if(effect.id==='cogs')flat.setBackplateTint?.(cogsBackplateTint(),hasCogsBackplateTint());return flat;},create:host=>{if(effect.id==='cogs')effect.setBackplateTint?.(cogsBackplateTint(),hasCogsBackplateTint());return createGlbBoard(host,{effect});}});\r\nconst BOARDS={quilt:{label:'Quilt',flat:host=>createQuiltBoard2D(host),create:host=>createQuiltBoardGL(host)},ice:grimoire('Crystal',ice),grass:grimoire('Grass',grass),cogs:grimoire('Cogs',cogs),jelly:grimoire('Jelly',jelly),wood:grimoire('Wood',wood),pond:grimoire('Pond',pond)};\r\nBOARDS.pixel={label:'Pixel Dance Floor',flat:host=>createPixelBoard(host,{flat:true}),create:host=>createPixelBoard(host)};\r\nBOARDS.paper={label:'Dr. Girlfriend Paper',flat:host=>createPaperBoard(host,{flat:true}),create:host=>createPaperBoard(host)};\r\nBOARDS.mache={label:'Cortisol Corgi',flat:host=>createMacheBoard(host,{flat:true}),create:host=>createMacheBoard(host)};\r\nBOARDS.clay={label:'Little Boyfriend Clay',flat:host=>createClayBoard(host,{flat:true}),create:host=>createClayBoard(host)};\r\n// Test-only stub board — never in PRODUCTION_PORTALS, so it's invisible to real users — letting tests drive\r\n// a non-quilt boardId (via ?board=__stub__) without a second real board existing yet. Set before this module\r\n// is imported (window.__portalTrailProbe above is the same pattern). Its create() only touches `host` at\r\n// call time, never `document` at module-eval time, so importing this module without a DOM still works.\r\nif(typeof window!=='undefined'&&window.__portalTestStubBoard===true)\r\n BOARDS.__stub__={label:'Stub',flat:host=>Promise.resolve({\r\n  background:'#000',\r\n  faceRect:()=>({left:0,top:0,width:host.clientWidth||1,height:host.clientHeight||1}),\r\n  patternRect:()=>({left:0,top:0,width:host.clientWidth||1,height:host.clientHeight||1}),\r\n  cut:()=>Promise.resolve(),heal(){},press(){},release(){},claims:id=>!!window.__portalTestClaims?.(id),pause(){},resume(){},dispose(){},\r\n })};\r\nconst BOARD_KEY='myr5.portalBoard';\r\nconst portalSound=(kind,extra={})=>window.dispatchEvent(new CustomEvent('myr5:portal-sound',{detail:{kind,board:boardId,...extra}}));\r\nconst tintKey=id=>'myr5.grimoireColor.'+id;\r\nconst validHex=value=>/^#[0-9a-f]{6}$/i.test(value||'');\r\nconst boardTint=(id=boardId)=>validHex(store.get(tintKey(id)))?store.get(tintKey(id)):(id==='cogs'?'#c59a56':'#b026ff');\r\nconst hasBoardTint=(id=boardId)=>/^#[0-9a-f]{6}$/i.test(store.get(tintKey(id))||'');\r\nconst COGS_BACKPLATE_KEY='myr5.grimoireBackplateColor.cogs';\r\nconst cogsBackplateTint=()=>validHex(store.get(COGS_BACKPLATE_KEY))?store.get(COGS_BACKPLATE_KEY):'#263943';\r\nconst hasCogsBackplateTint=()=>validHex(store.get(COGS_BACKPLATE_KEY));\r\nconst boardTintLabel=()=>boardId==='grass'?'Flower colour':boardId==='cogs'?'Mechanism colour':boardId==='pond'?'Lily colour':'Grimoire colour';\r\nconst paletteKey=id=>'myr5.wormholePalette.'+id;\r\nconst craftPortal=id=>['clay','paper','mache'].includes(id);\r\nconst portalEdgeTint=(id=boardId)=>craftPortal(id)?paletteFor(id,store.get(paletteKey(id)))?.colors[0]||boardTint(id):boardTint(id);\r\nconst materialLoads=new Map();\r\n// Sandboxed frames and private-mode Safari throw on localStorage access; never let that kill mountPortal.\r\nconst store={get(k){try{return localStorage.getItem(k)}catch{return null}},set(k,v){try{localStorage.setItem(k,v)}catch{}}};\r\nfunction initialBoardId(){\r\n const p=new URLSearchParams(location.search).get('board');if(p&&PRODUCTION_PORTALS.includes(p))return p;\r\n const stored=store.get(BOARD_KEY);if(stored&&PRODUCTION_PORTALS.includes(stored))return stored;\r\n return 'pixel';\r\n}\r\n\r\n// placeholder until Ian's Tripo models arrive — simple inline-SVG icons on a glowing disc (the X/cross \"all menus\" fall-in)\r\nconst ICONS={\r\n dumbbell:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"><rect x=\"6\" y=\"18\" width=\"8\" height=\"12\" rx=\"2\"/><rect x=\"34\" y=\"18\" width=\"8\" height=\"12\" rx=\"2\"/><path d=\"M14 24h20\"/></svg>',\r\n lotus:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M24 40c-10-4-14-12-14-18 6 2 11 6 14 12 3-6 8-10 14-12 0 6-4 14-14 18z\"/><path d=\"M24 40c-5-8-5-16 0-24 5 8 5 16 0 24z\"/></svg>',\r\n brush:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"14\" cy=\"12\" r=\"6\"/><path d=\"M30 8l10 10-14 14-10-4 4-10z\"/><path d=\"M16 28l-6 12 12-6\"/></svg>',\r\n bowl:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M8 22a16 8 0 0 0 32 0z\"/><path d=\"M8 22h32\"/><path d=\"M20 8c0 3-2 3-2 6M28 8c0 3 2 3 2 6\"/></svg>',\r\n trophy:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M16 8h16v10a8 8 0 0 1-16 0z\"/><path d=\"M16 10H8v4a8 8 0 0 0 8 8M32 10h8v4a8 8 0 0 1-8 8\"/><path d=\"M24 26v8M17 40h14M20 34h8v6h-8z\"/></svg>',\r\n rocket:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M24 4c7 6 9 15 6 26H18C15 19 17 10 24 4z\"/><circle cx=\"24\" cy=\"17\" r=\"3.5\"/><path d=\"M18 30l-6 6 7 1M30 30l6 6-7 1M21 38l3 6 3-6\"/></svg>',\r\n joystick:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><circle cx=\"24\" cy=\"14\" r=\"6\"/><path d=\"M24 20v10\"/><rect x=\"10\" y=\"30\" width=\"28\" height=\"10\" rx=\"3\"/></svg>',\r\n star:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linejoin=\"round\"><path d=\"M24 6l5.2 11.4L41 19l-9 8.3 2.4 12.2L24 33.4 13.6 39.5 16 27.3 7 19l11.8-1.6z\"/></svg>',\r\n bell:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M24 6c-6 0-9 5-9 12v6l-4 8h26l-4-8v-6c0-7-3-12-9-12z\"/><path d=\"M19 38a5 5 0 0 0 10 0\"/></svg>',\r\n gear:'<svg viewBox=\"0 0 48 48\" width=\"34\" height=\"34\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"24\" cy=\"24\" r=\"7\"/><path d=\"M24 4v6M24 38v6M44 24h-6M10 24H4M37.6 10.4l-4.2 4.2M14.6 33.4l-4.2 4.2M37.6 37.6l-4.2-4.2M14.6 14.6l-4.2-4.2\"/></svg>',\r\n};\r\n\r\n// Shape -> menu. Ian's gesture map (2026-09-22): rect/oval are camera/nav-free (\"home\") destinations —\r\n// hide the portal, then trigger the pod's own control; up/down/diamonds/lines/x open a dialog or navigate.\r\n// Neon colours: Ian picked the six most popular neons (2026-09-22) — orange, purple, pink, green, yellow,\r\n// electric blue — reused freely across destinations; #ff4fa0 is the dedicated Achievements pink.\r\n// `hidden:true` keeps an id routable (gesture lookup, MENUS[id]) without adding a duplicate row to the\r\n// Menu sheet grid — used for the second diamond (same destination as the first) and line-up (opens the\r\n// sheet itself, so it can't also be a row in it).\r\n// W2-2A: each destination opens through the app's router (modules/routes.mjs, window.myr5Routes): it sets the\r\n// route's #hash, lights the bottom bar and hands back the dialog for the dive and fade-back. A page without the\r\n// router (a bare portal page) keeps the direct opener.\r\nconst via=(route,direct)=>()=>window.myr5Routes?window.myr5Routes.go(route,{portal:true}):direct();\r\nconst LEADERBOARD={label:'Leaderboard',route:'scoreboard',color:'#ffff33',icon:ICONS.trophy,kind:'dialog',open:via('scoreboard',()=>{document.querySelector('.coach-dock [data-panel=\"account\"]')?.click();return document.getElementById('accountPanel');})};\r\n// Exported so tests can check the gesture -> destination table without a DOM.\r\nexport const MENUS={\r\n // Ian 2026-09-23: the square opens the workout start page (the pod scrolled to its viewing port, control board and\r\n // BEGIN; no auto-start); the oval is the coach's arrival in the ship view, its entrance played every time.\r\n rect:{label:'Workout',route:'workout',color:'#ff5f1f',icon:ICONS.dumbbell,kind:'dialog',open:via('workout',()=>openWorkoutHome())},\r\n oval:{label:'Choose Workout',route:'select',color:'#1f51ff',icon:ICONS.dumbbell,kind:'dialog',open:via('select',()=>window.myr5Menus?.ship?.({entrance:'always',hash:'#select'}))},\r\n up:{label:'Food',route:'food',color:'#39ff14',icon:ICONS.bowl,kind:'dialog',open:via('food',()=>{document.querySelector('.coach-dock [data-panel=\"meals\"]')?.click();return document.getElementById('mealsPanel');})},\r\n down:{label:'Achievements',route:'achievements',color:'#ff4fa0',icon:ICONS.star,kind:'dialog',open:via('achievements',()=>window.myr5Menus?.achievements?.())},\r\n vdiamond:LEADERBOARD,\r\n hdiamond:{label:'Spotify DJ',route:'spotify',color:'#ff982f',icon:ICONS.joystick,kind:'dialog',open:via('spotify',()=>window.myr5Menus?.spotify?.())},\r\n x:{label:'World',route:'world',color:'#ff10f0',icon:ICONS.joystick,kind:'dialog',open:via('world',()=>location.assign('/pose.html#world'))},\r\n 'line-lr':{label:'Meditation',route:'meditate',color:'#b026ff',icon:ICONS.lotus,kind:'dialog',open:via('meditate',()=>{document.querySelector('.meditation-entry')?.click();return document.querySelector('.meditation-panel');})},\r\n 'line-rl':{label:'Reminders',route:'reminders',color:'#ff10f0',icon:ICONS.bell,kind:'dialog',open:via('reminders',()=>{document.querySelector('.coach-dock [data-panel=\"reminders\"]')?.click();return document.getElementById('remindersPanel');})},\r\n 'line-down':{label:'Settings',route:'settings',color:'#39ff14',icon:ICONS.gear,kind:'dialog',open:via('settings',()=>{document.getElementById('openSettings')?.click();return document.getElementById('settings');})},\r\n // Line-up opens the Menu sheet; it is hidden from the sheet grid itself.\r\n // Full-screen ship view (Ian 2026-09-22: the coach capsule view, full screen, with the ship and pixel planet). Menu sheet only.\r\n // War Room/Arcade has no gesture: Menu sheet only, same lock as before.\r\n};\r\n\r\n// The locked intake theme disables transitions with !important; inline !important keeps the portal moving.\r\nconst motion=(el,value)=>value?el.style.setProperty('transition',prefersReducedMotion()?'none':value,'important'):el.style.removeProperty('transition');\r\nconst prefersReducedMotion=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n// #29: a short buzz on a recognised match (trace or tap), a double pulse on an \"almost\" near-miss. iOS\r\n// ignores navigator.vibrate; never vibrate under reduced motion.\r\nconst buzz=pattern=>{if(!prefersReducedMotion())navigator.vibrate?.(pattern);};\r\nconst raf=()=>new Promise(r=>requestAnimationFrame(r));\r\nconst sleep=ms=>new Promise(r=>setTimeout(r,ms));\r\n// Lets a just-opened dialog's layout settle before we measure it. Races two frames against a plain\r\n// timeout so a backgrounded tab (rAF paused) can't stall the whole reveal indefinitely.\r\nconst settle=()=>Promise.race([raf().then(raf),new Promise(r=>setTimeout(r,50))]);\r\nconst toClientPts=(pts,rect)=>pts.map(([x,y])=>[rect.left+x*rect.width,rect.top+y*rect.height]);\r\nconst closeLoop=pts=>{const a=pts[0],b=pts.at(-1);return(a[0]===b[0]&&a[1]===b[1])?pts:[...pts,a];};\r\nconst rectBox=el=>{const r=el.getBoundingClientRect();return{width:r.width,height:r.height};};\r\nfunction centroidOf(closedPts){const n=closedPts.length-1;let sx=0,sy=0;for(let i=0;i<n;i++){sx+=closedPts[i][0];sy+=closedPts[i][1];}return[sx/n,sy/n];}\r\nconst shapeClipPts=(id,rect)=>closeLoop(toClientPts(shapeOutlinePts(id),rect));\r\n// R21 L2: the opened window sits AURA.inset px inside the board face on every side, so the rim and its glow leave a strip of board\r\n// round them instead of touching the metal rail. Scales per axis about the box centre; the rect (the whole face) is left alone.\r\nfunction insetPts(pts,id){\r\n if(id==='rect'||!AURA.inset)return pts;\r\n const xs=pts.map(p=>p[0]),ys=pts.map(p=>p[1]),x0=Math.min(...xs),x1=Math.max(...xs),y0=Math.min(...ys),y1=Math.max(...ys),\r\n  kx=Math.max(.5,1-2*AURA.inset/(x1-x0||1)),ky=Math.max(.5,1-2*AURA.inset/(y1-y0||1)),cx=(x0+x1)/2,cy=(y0+y1)/2;\r\n return pts.map(([x,y])=>[cx+(x-cx)*kx,cy+(y-cy)*ky]);\r\n}\r\n// The glass clip bleeds GLASS_BLEED px past the shape (clamped to the board face): triangles are cut by centroid, so the\r\n// hole's edge is jagged and would otherwise show bare background in the notches beyond the exact outline.\r\nconst GLASS_BLEED=18;\r\nfunction bleedPts(pts,d=GLASS_BLEED){\r\n const c=centroidOf(pts),f=board?.faceRect(),clamp=(v,lo,len)=>f?Math.min(Math.max(v,lo),lo+len):v;\r\n return pts.map(([x,y])=>{const k=1+d/(Math.hypot(x-c[0],y-c[1])||1);return [clamp(c[0]+(x-c[0])*k,f?.left,f?.width),clamp(c[1]+(y-c[1])*k,f?.top,f?.height)];});\r\n}\r\n\r\n// Scales the shape polygon about its own centroid before cutting the hole; ponytail: a large fixed\r\n// multiplier rather than measuring the exact box coverage needed — plenty for a phone/tablet screen,\r\n// revisit with a measured scale if a very large display ever hosts this board.\r\nconst GROW=40;\r\nfunction buildHoleClip(pts,box,scale){\r\n const c=centroidOf(pts),scaled=scale===1?pts:pts.map(([x,y])=>[c[0]+(x-c[0])*scale,c[1]+(y-c[1])*scale]);\r\n const outer=[[0,0],[box.width,0],[box.width,box.height],[0,box.height],[0,0]];\r\n return `polygon(evenodd, ${[...outer,...scaled,[0,0]].map(([x,y])=>`${x}px ${y}px`).join(',')})`;\r\n}\r\n// Home destinations (the app under the portal) open from the tunnel's core during the dive: a shape-shaped hole grows\r\n// from the vanishing point to 1.15x the shape, which covers everything the dive leaves on screen.\r\nfunction growHole(el,pts,box,current=()=>true,ms=PORTAL.revealMs){\r\n return new Promise(resolve=>{\r\n  motion(el,'none');el.style.clipPath=buildHoleClip(pts,box,.02);\r\n  if(prefersReducedMotion()){el.style.clipPath=buildHoleClip(pts,box,GROW);resolve();return;}\r\n  settle().then(()=>{\r\n   if(!current()){resolve();return;}\r\n   motion(el,`clip-path ${ms}ms cubic-bezier(.4,0,.7,1)`);\r\n   el.style.clipPath=buildHoleClip(pts,box,1.15);\r\n   setTimeout(resolve,ms);\r\n  });\r\n });\r\n}\r\n\r\nlet portalHome,boardHost,overlay,ctx,objectsLayer,statusEl,menuBtn,menuSheet,boardBtn,settingsBtn,overlayObserver,lifecycle,chrome,workoutHome,workoutSlots=[];\r\nlet wantedBoard=null,sequence=0,visibilityRun=0,boardLoad=0,menuChosen=false,focusBefore=null;\r\nconst backgroundInert=new Map(),flashes=new Set();\r\n// board: what the portal cuts and measures: the 3D layer (gl3) once it has drawn, else the flat layer (base) under it.\r\nlet board=null,base=null,gl3=null,boardShown=false,boardId='quilt';\r\n// Per board, this session (never one global flag): '3d' drawing, 'lost' (its context dropped once: one more try), 'flat' (3D\r\n// failed: the flat board stays until the board is picked again or the app restarts). pending3D: 3D builds still out.\r\nconst art={},losses={},pending3D=new Set();let ready3D=Promise.resolve();\r\nlet pointers=new Map(),pendingStrokes=[],pendingTrailPts=[],finalizeTimer=0,outlineFlash=null,rafId=0;\r\n// busy: a portal sequence is running (traces ignored, touches ripple the glass); phase: the live glass {glass,pts,color,t0,pulse}.\r\nlet busy=false,phase=null;\r\n// #104: idleTimer arms after IDLE.armMs of eligibility (board shown, nothing busy, no touch, no dialog,\r\n// tab visible); idleCycle is the running cycle ({t0}) or {static:true} under reduced\r\n// motion. #105: fading holds just-released strokes still fading out, drawn alongside any live ones.\r\nlet idleTimer=0,idleCycle=null,fading=[];\r\n// #22 first-run hint: {t0} while the glowing-fingertip demo plays, else null. idleEligible() also checks\r\n// this (below) so the idle ambient flash never runs underneath it.\r\nlet hint=null,lastTap=null;\r\n\r\nfunction menuButtonsHtml(){return Object.entries(MENUS).filter(([,m])=>!m.hidden).map(([id,m])=>`<button type=\"button\" data-menu=\"${id}\"><i aria-hidden=\"true\" style=\"--dot:${m.color}\"></i>${m.label}</button>`).join('');}\r\nfunction boardChipsHtml(){return Object.entries(BOARDS).filter(([id])=>PRODUCTION_PORTALS.includes(id)).map(([id,b])=>`<button type=\"button\" data-board=\"${id}\" aria-pressed=\"${id===boardId}\">${b.label}</button>`).join('');}\r\nconst htmlSafe=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\r\nfunction paletteSelectHtml(id=boardId){\r\n if(id==='quilt')return '';\r\n const rows=paletteOptions(id),selected=paletteFor(id,store.get(paletteKey(id)))?.id;\r\n if(!rows.length)return '';\r\n // R3-07: palette discs (one per preset, painted from its own colours) drive the native select, which stays as the stored value and the test/AT hook.\r\n const disc=p=>`<button type=\"button\" class=\"portal-disc\" data-palette-disc=\"${htmlSafe(p.id)}\" aria-label=\"${htmlSafe(p.label)}\" aria-pressed=\"${p.id===selected}\" style=\"--disc:conic-gradient(${(p.colors||[p.core]).map(htmlSafe).join(',')},${htmlSafe((p.colors||[p.core])[0])})\"></button>`;\r\n return `<label class=\"portal-palette\">Wormhole palette <select aria-label=\"Wormhole palette\" data-palette hidden>${rows.map(p=>`<option value=\"${htmlSafe(p.id)}\"${p.id===selected?' selected':''}>${htmlSafe(p.label)}</option>`).join('')}</select><span class=\"portal-discs\" role=\"group\" aria-label=\"Wormhole palette colours\">${rows.map(disc).join('')}</span></label>`;\r\n}\r\nfunction lookControlsHtml(){\r\n const look=readLook();\r\n return `<fieldset class=\"portal-look-controls\"><legend>Portal appearance</legend><div>${[['portal','Metal'],['strip','Strip']].map(([key,label])=>`<label>${label}<input type=\"color\" data-look=\"${key}\" aria-label=\"${label}\" value=\"${look[key]}\"></label>`).join('')}</div><button type=\"button\" data-look-reset aria-label=\"Reset portal appearance\" title=\"Reset portal appearance\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 12a8 8 0 1 0 3-6.2M4 4v5h5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg></button></fieldset>`;\r\n}\r\nfunction boardTintControlsHtml(){\r\n const mechanism=`<label class=\"portal-color\">${boardTintLabel()} <input type=\"color\" aria-label=\"${boardTintLabel()}\" data-board-tint value=\"${boardTint()}\"></label>`;\r\n return `${mechanism}${boardId==='cogs'?`<label class=\"portal-color\">Back wall colour <input type=\"color\" aria-label=\"Back wall colour\" data-backplate-tint value=\"${cogsBackplateTint()}\"></label>`:''}`;\r\n}\r\nfunction updateBoardChips(){\r\n const tint=menuSheet?.querySelector('[data-board-tint]'),label=tint?.closest('.portal-color');\r\n if(tint){tint.value=boardTint();tint.setAttribute('aria-label',boardTintLabel());if(label?.firstChild?.nodeType===Node.TEXT_NODE)label.firstChild.textContent=boardTintLabel()+' ';}\r\n const backplate=menuSheet?.querySelector('[data-backplate-tint]')?.closest('.portal-color');\r\n if(boardId==='cogs'&&!backplate)label?.insertAdjacentHTML('afterend',boardTintControlsHtml().slice(boardTintControlsHtml().indexOf('<label',1)));\r\n else if(boardId!=='cogs')backplate?.remove();\r\n const backplateInput=menuSheet?.querySelector('[data-backplate-tint]');if(backplateInput)backplateInput.value=cogsBackplateTint();\r\n menuSheet?.querySelectorAll('[data-board]').forEach(btn=>btn.setAttribute('aria-pressed',String(btn.dataset.board===boardId)));\r\n const row=menuSheet?.querySelector('[data-palette-row]');if(row)row.innerHTML=paletteSelectHtml();\r\n}\r\nexport function setCogsBackplateTint(hex,selected=true){\r\n if(!validHex(hex))return false;\r\n if(selected)store.set(COGS_BACKPLATE_KEY,hex);else {try{localStorage.removeItem(COGS_BACKPLATE_KEY);}catch{}}\r\n if(base&&boardId==='cogs')base.setBackplateTint?.(hex,selected);\r\n if(gl3&&boardId==='cogs')gl3.setBackplateTint?.(hex,selected);\r\n return true;\r\n}\r\n// Swaps the mounted board. The flat layer goes up first: a poster that doesn't arrive keeps the board already showing (the\r\n// last good one), else the Quilt, whose own art is drawn in code when it must be, so there is always a board. Then 3D is\r\n// laid over it while the portal is on screen (threeD). Pointer listeners read the `board` variable at call time.\r\n// Loads run one at a time: effects (jelly's module state) are singletons, so the old 3D board is disposed before the next\r\n// one's build can start. A load superseded while queued does nothing.\r\nlet loadQueue=Promise.resolve();\r\nfunction loadBoard(id){\r\n if(!PRODUCTION_PORTALS.includes(id)&&id!=='__stub__')id='quilt';\r\n if(id===boardId&&base&&pending3D.has(id))return ready3D.then(()=>base);\r\n wantedBoard=id;delete art[id];losses[id]=0; // a pick (or a cold start) gives its 3D another go\r\n const load=++boardLoad,run=loadQueue.then(()=>load===boardLoad?loadBoardNow(id,load):null);\r\n loadQueue=run.catch(()=>{});return run;\r\n}\r\nfunction disposeBoard(value){\r\n if(!value)return Promise.resolve();\r\n const gl=value.glContext||null;\r\n if(!gl){try{value.dispose();}catch{}return Promise.resolve();}\r\n return releaseGraphics(value.canvas,gl,()=>value.dispose());\r\n}\r\nasync function loadBoardNow(id,load){\r\n const label=BOARDS[id].label;\r\n status(`Loading ${label} board…`);\r\n let next,failureMessage='';\r\n try{next=await BOARDS[id].flat(boardHost);}\r\n catch(error){\r\n  console.warn(`${label} board art is unavailable.`,error);\r\n  if(load!==boardLoad)return null;\r\n  if(base){status(`${label} board art is unavailable. ${BOARDS[boardId].label} stays; try again later or check Downloads.`);updateBoardChips();return base;}\r\n  failureMessage=`${label} board art is unavailable. Quilt is ready; try again later or check Downloads.`;\r\n  id='quilt';next=await BOARDS.quilt.flat(boardHost);\r\n }\r\n if(load!==boardLoad){next.dispose();return null;}\r\n clearTimeout(finalizeTimer);pendingStrokes=[];\r\n pointers.forEach((_,pid)=>board?.release(pid));pointers.clear();\r\n const old=[gl3,base];gl3=null;base=board=next;lastTap=null;for(const b of old)void disposeBoard(b);\r\n if(!boardShown)base.pause();\r\n portalHome.style.background=base.background||''; // the canvases are transparent; the board colour lives here, behind the glass\r\n portalHome.dataset.board=boardId=id;portalHome.dataset.art='flat';\r\n portalHome.setAttribute('aria-label',`${BOARDS[id].label} grimoire`);\r\n portalSound('board');\r\n store.set(BOARD_KEY,id); // the saved choice is the board on screen: a failed pick keeps the last good one, a failed start the Quilt\r\n status(failureMessage);updateBoardChips();\r\n threeD();\r\n return base;\r\n}\r\n// 3D over the flat board: while the portal is on screen (or about to be: mount), one build per board at a time. It takes over\r\n// only once it has drawn on a live context and no cut is open; a throw, a timeout or a lost context leaves the flat board (fell).\r\nfunction threeD(mounting=false){\r\n const id=boardId,make=BOARDS[id].create,generation=boardLoad;\r\n if(!make||gl3||!(boardShown||mounting)||pending3D.has(id)||art[id]==='flat'||lifecycle.signal.aborted||graphicsBlocked('portal'))return ready3D; // the Vault holds the GPU; graphics-free rebuilds\r\n pending3D.add(id);portalHome.dataset.building=''; // portal.css keeps a hidden portal laid out meanwhile: effects measure the host as they build\r\n return ready3D=(async()=>{\r\n  await waitForGraphicsRelease();\r\n  if(generation!==boardLoad||id!==boardId||lifecycle.signal.aborted||gl3||(!boardShown&&!mounting)||graphicsBlocked('portal'))return;\r\n  const build=make(boardHost);build.catch(()=>{});\r\n  let created;\r\n  try{created=await Promise.race([build,sleep(WAIT.threeD).then(()=>{throw new Error(`3D took over ${WAIT.threeD} ms`);})]);}\r\n  catch(error){build.then(late=>{void disposeBoard(late);},()=>{});if(generation!==boardLoad||id!==boardId||lifecycle.signal.aborted)return;return fell(id,error);} // stale builds cannot overwrite a newer board; late ones are released at once\r\n  created.canvas.style.opacity='0'; // drawn but out of sight until it takes over (opacity, not visibility: its frame still reaches the screen)\r\n  while((busy||phase)&&id===boardId&&!lifecycle.signal.aborted)await sleep(250); // ponytail: polls; a sequence ends within seconds\r\n  if(id!==boardId||generation!==boardLoad||gl3||lifecycle.signal.aborted||(!boardShown&&!mounting)){await disposeBoard(created);return;}\r\n  if(created.canvas.getContext('webgl2')?.isContextLost()!==false){await disposeBoard(created);return fell(id,new Error('WebGL context lost before its first frame'));}\r\n  if(id!=='quilt')created.setTint?.(boardTint(id),hasBoardTint(id));\r\n  if(id==='cogs')created.setBackplateTint?.(cogsBackplateTint(),hasCogsBackplateTint());\r\n  base.pause();created.restoreState?.(base.exportState?.());gl3=board=created;created.canvas.style.opacity='';if(base.canvas)base.canvas.style.visibility='hidden';\r\n  if(boardShown)created.resume();else created.pause(); // resume draws a fresh frame now it's in sight\r\n  art[id]='3d';portalHome.dataset.art='3d';\r\n })().finally(()=>{pending3D.delete(id);if(!pending3D.size)delete portalHome.dataset.building;});\r\n}\r\nfunction fell(id,error){\r\n console.warn(`${BOARDS[id].label} 3D is unavailable; its flat board stays.`,error);\r\n art[id]='flat';\r\n if(id===boardId&&id!=='quilt'&&!lifecycle.signal.aborted)status(`${BOARDS[id].label} is showing its flat art: its 3D didn't load.`,{label:'Retry 3D',run:()=>loadBoard(id)}); // the flat quilt is its own art\r\n}\r\n// Android drops live WebGL contexts under memory pressure: the flat board takes over at once and 3D gets one more try a\r\n// moment later; a second loss this session leaves the board flat.\r\nfunction lost3D(){\r\n const id=boardId,dead=gl3;gl3=null;board=base;\r\n if(base.canvas)base.canvas.style.visibility='';base.resume();portalHome.dataset.art='flat';void disposeBoard(dead);\r\n if((losses[id]=(losses[id]||0)+1)>1)return fell(id,new Error('WebGL context lost again'));\r\n art[id]='lost';setTimeout(()=>threeD(),1000);\r\n}\r\n\r\n// #111 metal frame (portal.css .portal-frame): the energy channel, bolts at the four corners and two down each long side,\r\n// the nameplate. One copy sits on the board; #portalChrome holds another that stays around an open destination.\r\n// Energy around the frame (Ian 23 Sept): the neons flow clockwise along a channel in the rail, one clipped channel per\r\n// side, each a neon stripe (ENERGY.px per colour cycle) sliding by transform: compositor-only, no repaint per frame.\r\n// Gentle at rest; the selected Strip sequence remains constant as the channel brightens and speeds up.\r\n// Reduced motion: static. Both frame copies share timing and rate, so handing over to the chrome is seamless; the\r\n// loops pause while neither copy is on screen.\r\nconst energyAnims=[];\r\nfunction energize(seq,rate=1,hot=false){\r\n const stops=seq&&energyStops(seq);\r\n for(const el of document.querySelectorAll('.portal-energy')){if(seq)el.style.setProperty('--energy',stops);el.classList.toggle('hot',hot);}\r\n [...energyAnims,...(window.myr5Shell?.energyAnims||[])].forEach(a=>a.updatePlaybackRate(rate));\r\n}\r\nfunction syncEnergy(){const on=boardShown||!!framed;energyAnims.forEach(a=>on?a.play():a.pause());}\r\n// War Room look (device-local, guests too): metal colours ride CSS variables, the strip colour feeds energize(). Reapplied on\r\n// load, return to the portal and cross-tab/bfcache changes; a board pick made elsewhere loads once (an uncached one falls back).\r\nconst restSeq=()=>stripSeq(readLook().strip);\r\n function applyLook(){const look=readLook();applyLookVars(document.documentElement,look);document.documentElement.style.setProperty('--portal-strip-glow',`${look.strip}40`);menuSheet?.querySelectorAll('[data-look]').forEach(input=>{input.value=look[input.dataset.look];});if(aura?.el){const seq=stripSeq(look.strip);aura.el.style.setProperty('--aura',look.strip);aura.el.style.setProperty('--aura-edge',`color-mix(in srgb,${look.strip} 45%,#fff)`);aura.el.style.setProperty('--aura-seq',[...seq,seq[0]].join(','));}if(!phase)energize(restSeq());window.dispatchEvent(new CustomEvent('myr5:look'));}\r\nfunction syncLook(){applyLook();const stored=store.get(BOARD_KEY);if(stored&&PRODUCTION_PORTALS.includes(stored)&&stored!==wantedBoard&&!busy)loadBoard(stored).catch(()=>{});}\r\nfunction buildDom(){\r\n initShell();\r\n portalHome=document.createElement('div');portalHome.id='portalHome';\r\n portalHome.hidden=true;portalHome.setAttribute('role','dialog');portalHome.setAttribute('aria-label','Quilt portal');portalHome.setAttribute('aria-modal','true');\r\n portalHome.innerHTML=`\r\n  <div id=\"portalShadows\" aria-hidden=\"true\"><i></i><i></i><i></i></div>\r\n  <div id=\"portalBoardHost\">${frameHtml()}</div>\r\n  <canvas id=\"portalOverlay\" aria-hidden=\"true\"></canvas>\r\n  <div id=\"portalObjects\" aria-hidden=\"true\"></div>\r\n  <p id=\"portalStatus\" role=\"status\"></p>\r\n  <button id=\"portalRetry\" type=\"button\" hidden></button>\r\n  <button id=\"portalSettingsButton\" type=\"button\" aria-label=\"Grimoire settings\">${SHELL_ICONS.grimoire}</button>\r\n  <button id=\"portalExitButton\" type=\"button\" aria-label=\"Return to workout controls\">${SHELL_ICONS.home}</button>`;\r\n document.body.append(portalHome);\r\n chrome=document.createElement('div');chrome.id='portalChrome';chrome.hidden=true;chrome.setAttribute('aria-hidden','true');chrome.innerHTML=frameHtml();document.body.append(chrome);\r\n // Frame ownership is the shared native-dialog shell, independent of Popover support.\r\n applyLookVars();energize(restSeq());\r\n for(const el of [...document.querySelectorAll('.portal-energy')].filter(el=>!el.closest('#myr5Shell'))){\r\n  el.style.setProperty('--energy-px',ENERGY.px+'px');\r\n  if(!prefersReducedMotion())[...el.children].forEach((channel,i)=>{const [axis,dir]=ENERGY_MOVES[i],from=`translate${axis}(${-ENERGY.px*(dir>0)}px)`,to=`translate${axis}(${-ENERGY.px*(dir<0)}px)`;energyAnims.push(channel.firstElementChild.animate([{transform:from},{transform:to}],{duration:ENERGY.loopMs,iterations:Infinity}));});\r\n }\r\n syncEnergy();\r\n boardHost=portalHome.querySelector('#portalBoardHost');\r\n boardHost.addEventListener('webglcontextlost',e=>{if(gl3&&e.target===gl3.canvas)lost3D();},true); // capture: the event doesn't bubble\r\n const resizeFullscreenFace=()=>{const dialog=framed?.dialog;if(!dialog?.classList.contains('portal-fullscreen'))return;const face=fullscreenFace();setFace(dialog,face);setFace(chrome,face);if(aura)showAura(screenLook(framed.look,face),'open');};\r\n addEventListener('resize',resizeFullscreenFace,{signal:lifecycle.signal});window.visualViewport?.addEventListener('resize',resizeFullscreenFace,{signal:lifecycle.signal});\r\n overlay=portalHome.querySelector('#portalOverlay');ctx=overlay.getContext('2d');\r\n objectsLayer=portalHome.querySelector('#portalObjects');\r\n statusEl=portalHome.querySelector('#portalStatus');\r\n const retryBtn=portalHome.querySelector('#portalRetry');\r\n retryBtn.addEventListener('click',()=>retryBtn._run?.(),{signal:lifecycle.signal});\r\n // W2-2A: the app's bottom bar (routes.mjs) is up on the quilt; its centre Portal button replaces the floating Menu\r\n // button, and the focus trap cycles the whole bar plus Pod (and Armie's inbox button, which stays live on the quilt).\r\n const barPortal=document.querySelector('.coach-dock [data-route=\"portal\"]'),bar=barPortal?.closest('.coach-dock');\r\n menuBtn=barPortal||portalHome.querySelector('#portalExitButton');\r\n const exitBtn=portalHome.querySelector('#portalExitButton');\r\n settingsBtn=portalHome.querySelector('#portalSettingsButton');\r\n settingsBtn.onclick=()=>openMenu();\r\n mountPodChrome(settingsBtn,{signal:lifecycle.signal});\r\n if(bar)exitBtn.remove(); // the centre mechanical key returns to the pod when the dock is present\r\n else exitBtn.onclick=()=>setVisible(false);\r\n const armie=()=>[...document.querySelectorAll('.armie-inbox-launcher')].filter(el=>el.getClientRects().length);\r\n const trap=e=>{if(e.key==='Escape'){e.preventDefault();setVisible(false);}else if(e.key==='Tab'){const buttons=[...new Set([...(bar?bar.querySelectorAll('button'):[menuBtn]),settingsBtn,document.getElementById('spotifyNowPlayingOpen'),...armie(),...(exitBtn.isConnected?[exitBtn]:[])])],index=buttons.indexOf(document.activeElement);e.preventDefault();buttons[(index+(e.shiftKey?-1:1)+buttons.length)%buttons.length].focus();}};\r\n portalHome.addEventListener('keydown',trap);\r\n bar?.addEventListener('keydown',e=>{if(boardShown&&!bar.closest('dialog'))trap(e);},{signal:lifecycle.signal});\r\n document.addEventListener('keydown',e=>{if(boardShown&&e.target.classList?.contains('armie-inbox-launcher'))trap(e);},{signal:lifecycle.signal});\r\n boardBtn=document.getElementById('openBoard');\r\n // The fallback menu sheet lives outside portalHome too: a dialog nested in a hidden ancestor\r\n // would be hidden along with it while open (e.g. mid-fade during the \"all\" portal reveal).\r\n menuSheet=document.createElement('dialog');menuSheet.id='portalMenu';menuSheet.className='portal-menu';menuSheet.setAttribute('aria-labelledby','portalMenuTitle');\r\n // The board picker row only earns its place once a second board ships; one option is nothing to pick from.\r\n const boardRow=PRODUCTION_PORTALS.length<2?'':`<div class=\"portal-board-chips\" role=\"group\" aria-label=\"Board\">${boardChipsHtml()}</div>`;\r\n menuSheet.innerHTML=`<header><h2 id=\"portalMenuTitle\">GRIMOIRE SETTINGS</h2><button type=\"button\" data-menu-close>Back to portal</button></header>${lookControlsHtml()}${boardTintControlsHtml()}${boardRow}<div data-palette-row>${paletteSelectHtml()}</div><details class=\"portal-full-menu\"><summary>Full menu</summary><div class=\"portal-menu-grid\">${menuButtonsHtml()}</div></details><p id=\"portalMenuStatus\" role=\"status\"></p>`;\r\n menuSheet.querySelector('[data-menu-close]').onclick=()=>menuSheet.close();\r\n menuSheet.querySelectorAll('[data-look]').forEach(input=>input.oninput=()=>{saveLook(input.dataset.look,input.value);applyLook();});\r\n menuSheet.querySelector('[data-look-reset]').onclick=()=>{for(const name of ['portal','strip'])saveLook(name,LOOK_DEFAULTS[name]);applyLook();};\r\n document.body.append(menuSheet);\r\n menuSheet.querySelectorAll('[data-menu]').forEach(btn=>btn.onclick=()=>{\r\n  const menu=MENUS[btn.dataset.menu];\r\n  if(menu.locked?.()){menuSheet.querySelector('#portalMenuStatus').textContent=menu.lockedMessage;return;}\r\n  menuSheet.querySelector('#portalMenuStatus').textContent='';menuChosen=true;menuSheet.close();\r\n  // A traced-shape dialog open fades back to the quilt when its dialog closes (openDirect). Route the\r\n  // same destinations opened from the Menu sheet through the same flow, or closing leaves the pod\r\n  // showing instead of the quilt. kind:'home' and kind:'nav' keep their plain setVisible+open.\r\n  if(menu.kind==='dialog'){const run=++sequence;openDirect(menu,()=>run===sequence&&!lifecycle.signal.aborted);return;}\r\n  setVisible(false);menu.open?.();\r\n });\r\n menuSheet.addEventListener('input',e=>{\r\n  if(e.target.matches('[data-board-tint]')){\r\n   const color=e.target.value;store.set(tintKey(boardId),color);\r\n   if(base&&boardId!=='quilt')base.setTint?.(color);\r\n   if(gl3&&boardId!=='quilt')gl3.setTint?.(color);\r\n   e.target.setAttribute('value',color);\r\n  }else if(e.target.matches('[data-backplate-tint]')){\r\n   setCogsBackplateTint(e.target.value);e.target.setAttribute('value',e.target.value);\r\n  }\r\n },{signal:lifecycle.signal});\r\n menuSheet.addEventListener('click',e=>{const disc=e.target.closest?.('[data-palette-disc]'),select=disc?.closest('.portal-palette')?.querySelector('[data-palette]');if(!disc||!select)return;select.value=disc.dataset.paletteDisc;select.dispatchEvent(new Event('change',{bubbles:true}));disc.closest('.portal-discs').querySelectorAll('[data-palette-disc]').forEach(d=>d.setAttribute('aria-pressed',String(d===disc)));},{signal:lifecycle.signal});\r\n menuSheet.addEventListener('change',e=>{if(e.target.matches('[data-palette]')){const options=paletteOptions(boardId);if(options.some(p=>p.id===e.target.value))store.set(paletteKey(boardId),e.target.value);}}, {signal:lifecycle.signal});\r\n menuSheet.querySelectorAll('[data-board]').forEach(btn=>btn.onclick=async()=>{menuChosen=true;menuSheet.close();const selected=btn.dataset.board;await loadBoard(selected);if(!lifecycle.signal.aborted)setVisible(true);});\r\n workoutHome=document.createElement('dialog');workoutHome.id='portalWorkoutHome';workoutHome.className='portal-workout-home';document.body.append(workoutHome);\r\n workoutHome.addEventListener('close',()=>{if(!workoutHome.open)restoreWorkoutHome();});\r\n}\r\n\r\nfunction restoreWorkoutHome(){\r\n for(const {node,slot} of workoutSlots){if(slot.parentNode)slot.replaceWith(node);}\r\n workoutSlots=[];\r\n}\r\nfunction openWorkoutHome(){\r\n if(!workoutHome)return null;\r\n if(workoutHome.open)return workoutHome;\r\n if(workoutSlots.length){workoutHome.showModal();return workoutHome;}\r\n const home=document.getElementById('homeScreen');if(!home)return null;\r\n backgroundBlocked(false);\r\n const nodes=[document.getElementById('podShipBackdrop'),document.querySelector('body>.ship-header'),home].filter(node=>node&&!workoutHome.contains(node));\r\n workoutSlots=nodes.map(node=>{const slot=document.createComment('pod-workout-home-slot');node.parentNode?.insertBefore(slot,node);return{node,slot};});\r\n workoutHome.append(...nodes);workoutHome.showModal();\r\n return workoutHome;\r\n}\r\n\r\nfunction backgroundBlocked(block){\r\n // Dialogs that are open or hidden are never made inert: one opened as a modal while the quilt is up (the full-download\r\n // offer, setup gate, reward reveals) sits in the top layer and must stay tappable, or the app freezes. A closed dialog\r\n // that is still drawn (styled display:grid) stays inert so it can't catch taps meant for the quilt. The \"Updated: …\"\r\n // toast (app-updates.mjs) is a non-dialog element sitting over the portal's own controls; it's exempt too, or its\r\n // \"Got it\" tap falls through to whatever is underneath (portal.css raises it above the Menu button while up).\r\n const liveDialog=el=>el.tagName==='DIALOG'&&(el.open||getComputedStyle(el).display==='none');\r\n const exempt=el=>el.id==='myr5Shell'||el===portalHome||el===menuSheet||liveDialog(el)||el.classList.contains('app-update-banner')||el.classList.contains('coach-dock')||el.classList.contains('armie-inbox-launcher')||el.classList.contains('reward-pack-launch'); // the reward-pack chip must stay tappable over the grimoire\r\n if(block){for(const el of document.body.children)if(!exempt(el)&&!backgroundInert.has(el)){backgroundInert.set(el,el.inert);el.inert=true;}}\r\n else{for(const [el,inert]of backgroundInert)el.inert=inert;backgroundInert.clear();}\r\n}\r\nfunction openMenu(){\r\n portalSound('menu');\r\n const returnToQuilt=boardShown,{face}=restFace();setVisible(false);menuChosen=false;menuSheet.showModal();\r\n if(face&&frameOn(face,windowLook('line-up',MENUS['line-up'],face)))frameDialog(menuSheet);\r\n menuSheet.addEventListener('close',()=>{if(framed?.dialog===menuSheet)frameOff();if(returnToQuilt&&!menuChosen&&!lifecycle.signal.aborted)setVisible(true);},{once:true});\r\n}\r\n\r\n// Every hide/show path heals the board (idempotent), so it always comes back whole.\r\n// Vault telemetry: raw-served file, so no import (the app bundle's vault store listens); the store listens for these.\r\nconst vaultSend=detail=>{try{dispatchEvent(new CustomEvent('myr5:vault-bump',{detail}));}catch{/* no window */}};\r\nlet visibleSince=0;const grimFlush=()=>{if(visibleSince){vaultSend({counter:'grim-time',ms:Date.now()-visibleSince});visibleSince=0;}};\r\nfunction setVisible(v){\r\n visibilityRun++;\r\n // Routes can put the quilt back while its destination's reverse dive is still awaiting its\r\n // animation. Invalidate and remove that return phase now; otherwise its glass stays mounted\r\n // until the old animation resolves. A forward phase has no backT0, so showing the quilt does\r\n // not interrupt a newly-started portal sequence.\r\n if(v&&phase?.backT0){endPhase();portalHome.style.transformOrigin='';}\r\n if(!v){sequence++;busy=false;clearTimeout(finalizeTimer);pendingStrokes=[];pendingTrailPts=[];fading.length=0;pointers.forEach((_,pid)=>board?.release(pid));pointers.clear();outlineFlash=null;objectsLayer.replaceChildren();cancelAnimationFrame(rafId);rafId=0;grimFlush();}\r\n board?.heal();\r\n portalHome.hidden=!v;\r\n if(boardBtn)boardBtn.hidden=v;\r\n if(v){if(!boardShown)focusBefore=document.activeElement;motion(portalHome,'');portalHome.style.opacity='';portalHome.style.clipPath='';board?.resume();backgroundBlocked(true);menuBtn.focus();maybeStartHint();if(!document.hidden&&!visibleSince)visibleSince=Date.now();}\r\n else{endPhase();board?.pause();backgroundBlocked(false);if(focusBefore?.isConnected)focusBefore.focus();}\r\n boardShown=v;frameOff();if(v)applyLook();syncEnergy();\r\n portalSound('scene',{visible:v});\r\n scheduleIdle();if(v)threeD();\r\n}\r\nfunction fadeOutBoard(){\r\n const run=++visibilityRun;\r\n backgroundBlocked(false);\r\n portalSound('scene',{visible:false});\r\n motion(portalHome,'opacity .3s ease');portalHome.style.opacity='0';\r\n return new Promise(r=>setTimeout(()=>{if(run===visibilityRun){portalHome.hidden=true;if(boardBtn)boardBtn.hidden=false;endPhase();board?.heal();board?.pause();boardShown=false;syncEnergy();scheduleIdle();}r();},prefersReducedMotion()?0:300));\r\n}\r\nfunction fadeInBoard(){\r\n visibilityRun++;\r\n endPhase();board?.heal();\r\n portalHome.hidden=false;portalHome.style.clipPath='';motion(portalHome,'none');portalHome.style.opacity='0';\r\n if(boardBtn)boardBtn.hidden=true;\r\n board?.resume();boardShown=true;frameOff();syncEnergy();backgroundBlocked(true);menuBtn.focus();scheduleIdle();threeD();\r\n portalSound('scene',{visible:true});\r\n const run=visibilityRun;\r\n settle().then(()=>{if(run!==visibilityRun)return;motion(portalHome,`opacity ${PORTAL.healMs}ms ease`);portalHome.style.opacity='1';});\r\n}\r\n// The border stays when a destination opens (Ian 23 Sept): #portalChrome shows a copy of the frame at the board's rest box\r\n// in the top layer, with a matte outside it, and the destination dialog is fitted into its window (.portal-framed).\r\n// look (#134): how the window reads: {id,color,label,pts: its outline (client px, closed),shaped,name: #132's rim path};\r\n// shaped (#131): the destination is seen through the cut in the quilt, which stays on as the wall around it.\r\n// ponytail: the box is measured once per destination, so a rotation while one is open keeps the old box until it closes.\r\nlet framed=null,ghostEl=null;\r\nfunction restFace(){const hidden=portalHome.hidden;portalHome.hidden=false;const face=board?.faceRect(),pattern=board?.patternRect();portalHome.hidden=hidden;return {face,pattern};}\r\nconst setFace=(el,face)=>{for(const k of ['left','top','width','height'])face?el.style.setProperty('--face-'+k,face[k]+'px'):el.style.removeProperty('--face-'+k);};\r\nconst rectPts=f=>closeLoop([[f.left,f.top],[f.left+f.width,f.top],[f.left+f.width,f.top+f.height],[f.left,f.top+f.height]]);\r\nconst menuFor=route=>Object.entries(MENUS).find(([,m])=>m.route===route)||[null,null];\r\n// A full-window look (the lines, the Menu sheet, a bottom-bar switch): the window's own rectangle, the name on the upper rail.\r\n const windowLook=(id,menu,face)=>({id,color:portalEdgeTint(),strip:readLook().strip,label:menu?.label||'',pts:rectPts(face),shaped:false,name:namePath(face)});\r\n// R7 (Ian 26 Sept): no text in a peer-through view, so the cut's rim (and the window it steps in to) carries no name.\r\n const shapeLook=(id,menu,pts)=>({id,color:portalEdgeTint(),strip:readLook().strip,label:menu.label,pts,shaped:true,name:null});\r\n// Fullscreen destinations keep the energy and feather it inward along the inset face.\r\n const screenLook=(look,face=fullscreenFace())=>({...look,pts:rectPts(face),shaped:false,fullscreen:true,name:null});\r\n // R20 (Ian 2 Oct): the housing ends above the dock, a separate console fixed below it (0 while the dock is hidden).\r\n // The console's height comes from --myr5-bar (the dock may be mid-move between dialogs, measuring 0, when a face is set);\r\n // Tracking and rest reserve exactly the same lower assembly as every other non-game phase.\r\n let barProbe=null;\r\n const dockHeight=()=>{const b=document.body;if(!document.getElementById('coachDock'))return 0;if(!barProbe?.isConnected){barProbe=document.createElement('div');barProbe.setAttribute('aria-hidden','true');barProbe.style.cssText='position:fixed;left:-9px;top:0;width:1px;height:var(--myr5-bar,88px);visibility:hidden;pointer-events:none';b.append(barProbe);}return barProbe.getBoundingClientRect().height;};\r\n function fullscreenFace(){const rail=15;return {left:rail,top:rail,width:Math.max(1,innerWidth-rail*2),height:Math.max(1,innerHeight-rail*2-dockHeight())};}\r\nfunction frameOn(face,look){\r\n frameOff();\r\n if(!face)return false;\r\n framed={face,look:look||windowLook(null,null,face),leaned:false,ctl:new AbortController()};setFace(chrome,face);chrome.hidden=false;syncEnergy();\r\n if(framed.look.shaped)framed.outlines=windowOutlines(framed.look.pts,face);\r\n // Release 5 open item: switching routes from the bottom bar while a destination is framed used to open the next page\r\n // unframed with the reverse dive playing behind it. The frame now moves to the next route's dialog (routes.mjs's\r\n // observer, created first, has already adopted it, so it carries its data-route), and a page route puts it away (closed).\r\n framed.watch=new MutationObserver(records=>{for(const {target} of records)if(framed?.dialog&&target!==framed.dialog&&target instanceof HTMLDialogElement&&target.open&&target.dataset.route)handOver(target);});\r\n framed.watch.observe(document.body,{subtree:true,attributeFilter:['open']});\r\n return true;\r\n}\r\n function frameDialog(dialog){\r\n  if(!framed||!(dialog instanceof HTMLDialogElement)||framed.dialog===dialog)return;\r\n  unframe(framed.dialog);framed.dialog=dialog;dialog.classList.add('portal-framed');\r\n  const route=framed.directRoute||dialog.dataset.route;\r\n  const full=framed.direct===true||dialog.matches('.meditation-panel')||dialog.id==='portalWorkoutHome',face=full?fullscreenFace():framed.face;setFace(dialog,face);setFace(chrome,face);\r\n  dialog.classList.toggle('portal-fullscreen',full);chrome.classList.toggle('portal-housing-fullscreen',full);chrome.classList.remove('portal-garage');if(full)chrome.dataset.destination=framed.directRoute|| (dialog.matches('.meditation-panel')?'meditation':'workout');else delete chrome.dataset.destination;\r\n // Back above the dialog, which opened on top of it. Re-shown a frame later: a popover hidden and shown in one task can\r\n // keep its old top-layer slot (under the dialog's backdrop) on WebKit. The rim is drawn then too, sized to the shown\r\n // frame (a hidden popover measures 0x0): it bursts open as the destination lands (not under the dive's scale).\r\n  window.myr5Shell?.adoptDialog(dialog);dialog.append(chrome);chrome.hidden=false;requestAnimationFrame(()=>{if(framed?.dialog!==dialog)return;showAura(full?screenLook(framed.look,face):aura?.look||framed.look,'open');});\r\n peerOn(dialog); // R7 (Ian 26 Sept): the tilt stays on every menu, full screen too\r\n if(!full&&framed.look.shaped)shapeDialog(dialog);\r\n}\r\n// The room remains visible through its cut until a double-tap or a scene action steps inside (full screen). Keep the\r\n// route/dialog identity, so Back and Close still return through their original portal. The housing remains around the inset face.\r\nfunction expandScene(dialog){\r\n if(!dialog?.open||framed?.dialog!==dialog||framed.expanded)return false;\r\n layoutInCut(dialog,false);framed.expanded=true;framed.leaned=true;\r\n dialog.classList.remove('portal-shaped','portal-inset','portal-leaned');dialog.classList.add('portal-fullscreen');chrome.classList.add('portal-housing-fullscreen');\r\n dialog.style.removeProperty('clip-path');dialog.style.removeProperty('--inset-zoom');motion(dialog,'');\r\n  const face=fullscreenFace();setFace(dialog,face);setFace(chrome,face);\r\n  chrome.classList.remove('portal-garage');showAura(screenLook(framed.look,face),'lean');\r\n stowBoard();return true;\r\n}\r\n// A destination can showModal() before its open() settles (the ship view loads after): frame it as it opens, before\r\n// its first paint (MutationObserver callbacks run ahead of rendering). Returns the disconnect.\r\nfunction watchDialog(){\r\n if(!framed)return null;\r\n const watch=new MutationObserver(records=>{for(const {target} of records)if(target instanceof HTMLDialogElement&&target.open)frameDialog(target);});\r\n watch.observe(document.body,{subtree:true,attributeFilter:['open']});\r\n return ()=>watch.disconnect();\r\n}\r\nfunction unframe(dialog){\r\n if(!dialog)return;\r\n if(framed?.dialog===dialog){peerOff();if(framed.look.shaped)layoutInCut(dialog,false);framed.dialog=null;}\r\n dialog.classList.remove('portal-framed','portal-shaped','portal-leaned','portal-inset','portal-fullscreen');setFace(dialog,null);dialog.style.removeProperty('--inset-zoom');\r\n dialog.style.removeProperty('clip-path');motion(dialog,'');\r\n dialog.querySelector(':scope>.portal-peer-ui')?.remove();\r\n}\r\nfunction frameOff(){\r\n if(!framed)return;\r\n const f=framed;f.watch?.disconnect();f.ctl.abort();\r\n if(f.directClose&&f.dialog)f.dialog.removeEventListener('close',f.directClose);\r\n unframe(f.dialog);ghostEl?.remove();ghostEl=null;framed=null;hideAura();chrome.classList.remove('portal-garage','portal-housing-fullscreen');delete chrome.dataset.destination;chrome.hidden=true;document.body.append(chrome);syncEnergy();\r\n}\r\nfunction frameDirectDestination(dialog,id){\r\n if(!(dialog instanceof HTMLDialogElement)||!dialog.open)return false;\r\n if(framed?.direct&&framed.dialog===dialog&&framed.directRoute===id)return true;\r\n const [shape,menu]=menuFor(id),route=window.myr5Routes?.ROUTES?.[id];\r\n sequence++;\r\n setVisible(false);\r\n const face=fullscreenFace(),look=windowLook(shape,menu||{label:route?.label||id},face);\r\n if(!frameOn(face,look))return false;\r\n dialog.dataset.route=id;\r\n framed.direct=true;framed.directRoute=id;\r\n frameDialog(dialog);\r\n const owner=framed;\r\n owner.directClose=()=>{if(framed===owner&&owner.dialog===dialog)frameOff();};\r\n dialog.addEventListener('close',owner.directClose);\r\n return true;\r\n}\r\n// The bar switched routes: the next route's dialog takes the frame, full window, in its own colour and name.\r\nfunction handOver(dialog){\r\n const route=dialog.dataset.route,[id,found]=menuFor(route),menu=found||{label:window.myr5Routes?.ROUTES?.[route]?.label};\r\n unframe(framed.dialog);\r\n if(framed.look.shaped)stowBoard(); // the quilt wall goes with the hole it framed\r\n Object.assign(framed,{look:windowLook(id,menu,framed.face),leaned:false,outlines:null,expanded:false});\r\n hideAura();frameDialog(dialog);\r\n const run=++sequence;\r\n dialog.addEventListener('close',()=>closed(dialog,{pts:backPts(id),color:menu?.color||'#b026ff'},()=>run===sequence&&!lifecycle.signal.aborted),{once:true});\r\n}\r\nfunction backPts(id){const {pattern}=restFace();if(!pattern)return pattern;const sid=id&&SHAPES[id]&&id!=='x'&&id!=='cross'?id:'rect';return boardId==='cogs'?shapeClipPts(sid,pattern):insetPts(shapeClipPts(sid,pattern),sid);}\r\nfunction stowBoard(){visibilityRun++;portalHome.hidden=true;if(boardBtn)boardBtn.hidden=false;endPhase();board?.heal();board?.pause();boardShown=false;syncEnergy();portalSound('scene',{visible:false});}\r\n// A destination from the portal closed: back out to the quilt (the reverse dive, or #131's fizzle for a hole), unless the\r\n// frame already moved on (handOver) or the bar went to another route (it is the active route now).\r\nfunction closed(dialog,back,current){\r\n if(!current()||(framed&&framed.dialog!==dialog))return; // no board/framing transaction: still back out\r\n const now=window.myr5Routes?.current?.();\r\n if(now&&now!==dialog.dataset.route){frameOff();return;}\r\n (back.shaped&&!framed?.expanded?fizzleBack:diveBack)({dialog,...back},current);\r\n}\r\n// The closed destination's empty shell (a shallow copy keeps its own look), shrinking into the wormhole core.\r\nfunction shrinkShell(dialog,[cx,cy],ms){\r\n if(!framed||dialog!==framed.dialog)return;\r\n const shell=dialog.cloneNode(false),f=framed.face;shell.removeAttribute('open');shell.inert=true;shell.classList.add('portal-ghost');\r\n shell.style.transformOrigin=`${cx-f.left}px ${cy-f.top}px`;document.body.append(shell);ghostEl=shell;\r\n shell.animate([{transform:'none',opacity:1},{transform:'scale(.05)',opacity:0}],{duration:ms,easing:'cubic-bezier(.7,0,.8,1)',fill:'forwards'}).finished.then(()=>shell.remove(),()=>{});\r\n}\r\n// Closing a destination flies back OUT of the wormhole (Ian 23 Sept): the destination shrinks into the tunnel core while\r\n// the portal scales back down from the dive and the tunnel slows, then the cut heals and the chrome hands back to the\r\n// board's own frame. Same revealMs as the dive in; reduced motion is the plain quick fade.\r\n// A way back that loses the screen to a newer show (routes' home-after-close can land first) drops its own glass and dive\r\n// on the way out, so none is left under the quilt, and always hands back busy (backRun: the latest way back owns it).\r\nlet backRun=0;\r\nfunction stale(run,ph,current){\r\n if(run===visibilityRun&&current())return false;\r\n if(phase===ph){endPhase();portalHome.style.transformOrigin='';}\r\n return true;\r\n}\r\nasync function diveBack({dialog,pts,color},current){\r\n if(prefersReducedMotion()||!board||!pts){fadeInBoard();return;}\r\n const run=++visibilityRun;backRun=run;\r\n auraOut();shrinkShell(dialog,centroidOf(pts),PORTAL.revealMs*.55);unframe(dialog);\r\n busy=true;\r\n try{\r\n  portalHome.hidden=false;motion(portalHome,'none');portalHome.style.opacity='';portalHome.style.clipPath='';\r\n  if(boardBtn)boardBtn.hidden=true;\r\n  board.resume();boardShown=true;syncEnergy();backgroundBlocked(true);\r\n  showGlass(pts,color);const ph=phase;\r\n  for(const el of [phase.glass,phase.bezel])el?.style.setProperty('animation','none','important'); // already there\r\n  phase.t0-=PORTAL.cutMs+PORTAL.loadMinMs; // the tunnel is already at full speed\r\n  cutBoard(pts,color,0);\r\n  await dive(pts,true);\r\n  if(stale(run,ph,current))return;\r\n  board.heal();\r\n  await phase?.bezel?.animate([{opacity:1},{opacity:0}],{duration:PORTAL.healMs}).finished.catch(()=>{});\r\n  if(stale(run,ph,current))return;\r\n  endPhase();frameOff();menuBtn.focus();scheduleIdle();\r\n }finally{if(backRun===run)busy=false;}\r\n}\r\n// #131/#130 closing a destination seen through the hole: it fizzles shut. The destination's shell shrinks into the\r\n// wormhole's core with a flash along the rim and sparks falling in, then the cut heals and the quilt is home again.\r\nasync function fizzleBack({dialog,pts,color},current){\r\n const run=++visibilityRun,ms=PORTAL.revealMs*.55;backRun=run;\r\n if(prefersReducedMotion()||!board||!pts){unframe(dialog);homeAgain();return;}\r\n busy=true;\r\n try{\r\n  auraOut();shrinkShell(dialog,centroidOf(pts),ms);unframe(dialog);\r\n  showGlass(pts,color);const ph=phase;phase.t0-=PORTAL.cutMs+PORTAL.loadMinMs;phase.backT0=performance.now();cutBoard(pts,color,0);board.resume();\r\n  await sleep(ms);\r\n  if(stale(run,ph,current))return;\r\n  board.heal();\r\n  await phase?.bezel?.animate([{opacity:1},{opacity:0}],{duration:PORTAL.healMs}).finished.catch(()=>{});\r\n  if(stale(run,ph,current))return;\r\n  homeAgain();\r\n }finally{if(backRun===run)busy=false;}\r\n}\r\nfunction homeAgain(){endPhase();board?.heal();board?.resume();frameOff();backgroundBlocked(true);menuBtn.focus();scheduleIdle();}\r\n\r\n// ---- #131 seen through the hole -------------------------------------------------------------------------------------\r\n// The oval, the diamonds and both triangles open INTO the cut: the quilt stays on screen as the wall around the hole and\r\n// the destination (fitted to the window as usual, so nothing inside it changes) is clipped to the cut outline, plus the\r\n// bottom bar and the portal's own buttons on the quilt beside the hole (children of the modal dialog, so they stay\r\n// tappable). ✕ closes through the destination's own Close; ⤢ \"steps in\" (#131's gentle zoom): the hole morphs out to the\r\n// whole window so all of the menu is in reach, and ⤡ steps back. Keyboard focus landing on something the wall hides\r\n// steps in too.\r\nconst scalePts=(pts,k)=>{const [cx,cy]=centroidOf(pts);return pts.map(([x,y])=>[cx+(x-cx)*k,cy+(y-cy)*k]);};\r\n// The farthest crossing of a ray from c at angle a with a closed polygon (the shapes are star-shaped about their centroid).\r\nexport function rayHit([cx,cy],a,poly){\r\n const dx=Math.cos(a),dy=Math.sin(a);let best=0;\r\n for(let i=0;i<poly.length-1;i++){\r\n  const [ax,ay]=poly[i],[bx,by]=poly[i+1],ex=bx-ax,ey=by-ay,den=dx*ey-dy*ex;if(Math.abs(den)<1e-9)continue;\r\n  const t=((ax-cx)*ey-(ay-cy)*ex)/den,u=((ax-cx)*dy-(ay-cy)*dx)/den;\r\n  if(t>0&&u>=-1e-9&&u<=1+1e-9)best=Math.max(best,t);\r\n }\r\n return [cx+dx*best,cy+dy*best];\r\n}\r\n// The hole and the whole window as matched outlines (the same rays from the shape's centroid, through every corner of\r\n// both), so a clip-path can morph one into the other.\r\nexport function windowOutlines(shape,face,n=48){\r\n const c=centroidOf(shape),box=rectPts(face),ang=([x,y])=>Math.atan2(y-c[1],x-c[0]);\r\n const as=[...Array.from({length:n},(_,i)=>-Math.PI+2*Math.PI*i/n),...shape.slice(0,-1).map(ang),...box.slice(0,-1).map(ang)].sort((a,b)=>a-b).filter((a,i,s)=>!i||a-s[i-1]>1e-4);\r\n return {shape:closeLoop(as.map(a=>rayHit(c,a,shape))),rect:closeLoop(as.map(a=>rayHit(c,a,box)))};\r\n}\r\nfunction peerUi(dialog){\r\n const ui=dialog.querySelector(':scope>.portal-peer-ui')||dialog.appendChild(Object.assign(document.createElement('div'),{className:'portal-peer-ui'}));\r\n if(framed)ui.style.setProperty('--peer-ui',framed.look.color);return ui;\r\n}\r\n// A flat menu (conductor 24 Sept) is laid out in the largest rectangle inside the cut (insetRect), a little zoomed out and\r\n// scrolling inside it, the rest of the cut showing the destination's still depth; a scene fills the cut and shows nothing\r\n// but itself (R7, Ian 29 Sept: portal.css hides its titles and controls until full screen), and cfg.fit sizes a scene\r\n// layer to the cut (the constellation).\r\nfunction shapeDialog(dialog){\r\n const {look}=framed,ui=peerUi(dialog),cfg=peerCfg(dialog);\r\n framed.flat=!cfg.scene;framed.cfg=cfg;\r\n dialog.classList.add('portal-shaped');\r\n const r=framed.flat&&(framed.inset??=insetRect(framed.outlines.shape)),small=r&&(r.width-2*INSET.margin<INSET.minW||r.height-2*INSET.margin<INSET.minH);\r\n if(small){framed.leaned=true;dialog.classList.add('portal-leaned');showAura({...look,pts:rectPts(framed.face),shaped:false},'lean');}\r\n else layoutInCut(dialog,true);\r\n dialog.addEventListener('focusin',({target})=>{if(framed?.dialog===dialog&&!framed.leaned&&!ui.contains(target)&&target.matches?.(':focus-visible')&&!throughCut(target))lean(true);},{signal:framed.ctl.signal});\r\n clipTo(dialog,small?framed.outlines.rect:framed.outlines.shape,{from:scalePts(framed.outlines.shape,.04),ms:prefersReducedMotion()?0:650,ease:'cubic-bezier(.2,1.25,.4,1)'});\r\n // R7 (Ian 26 Sept): a double-tap on the open menu (not on one of its controls) steps in to the full screen.\r\n const at=e=>({x:e.clientX,y:e.clientY,t:(isFinite(e.timeStamp)&&e.timeStamp>0?e.timeStamp:performance.now()),id:e.pointerId}),near=(a,b)=>a&&b&&b.t-a.t>=0&&b.t-a.t<=TAP_MS&&Math.hypot(b.x-a.x,b.y-a.y)<=TAP_MOVE_PX,opts={capture:true,signal:framed.ctl.signal};\r\n let down=null,tap=null,multitouch=false;\r\n const activePointers=new Set();\r\n dialog.addEventListener('pointerdown',e=>{\r\n   activePointers.add(e.pointerId);\r\n   if(multitouch) return;\r\n   if(activePointers.size>1){multitouch=true;down=null;tap=null;return;}\r\n   down=at(e);\r\n },opts);\r\n dialog.addEventListener('pointerup',e=>{\r\n   activePointers.delete(e.pointerId);\r\n   if(multitouch){if(activePointers.size===0)multitouch=false;return;}\r\n   if(!down||down.id!==e.pointerId){down=null;return;}\r\n   const up=at(e),t=near(down,up)&&!e.target.closest?.('button,a,input,select,textarea,label,summary')?up:null;\r\n   if(t&&near(tap,t)){tap=null;expandScene(dialog);}else tap=t;\r\n   down=null;\r\n },opts);\r\n dialog.addEventListener('pointercancel',e=>{\r\n   activePointers.delete(e.pointerId);\r\n   down=null;\r\n   tap=null;\r\n   if(activePointers.size===0){multitouch=false;}\r\n },opts);\r\n // A tap on the scene that opens one of its cards (a boss's detail, a pyramid screen or dial) steps in to show it.\r\n if(cfg.cards){\r\n  let shut=[];const open=()=>[...dialog.querySelectorAll(cfg.cards)].filter(el=>!el.hidden);\r\n  dialog.addEventListener('click',()=>{shut=open();},opts);\r\n  dialog.addEventListener('click',()=>{if(open().some(el=>!shut.includes(el)))expandScene(dialog);},{signal:framed.ctl.signal});\r\n }\r\n}\r\nfunction peerCfg(dialog){return PEER_DEPTH.find(([s])=>dialog.matches(s))?.[1]||PEER_2D;}\r\n// Largest axis-aligned rectangle inside a convex outline (client px, closed), leaning a little toward width so a menu's\r\n// text column stays readable (scored width^1.5 x height). For a convex shape the rectangle's sides are bounded by the\r\n// chords at its top and bottom edges only, so every pair of sampled chords is a candidate.\r\nexport function insetRect(poly,n=48){\r\n const ys=poly.map(p=>p[1]),y0=Math.min(...ys),y1=Math.max(...ys);\r\n const chord=y=>{let l=Infinity,r=-Infinity;for(let i=0;i<poly.length-1;i++){const [ax,ay]=poly[i],[bx,by]=poly[i+1];if(ay===by||(ay-y)*(by-y)>0)continue;const x=ax+(bx-ax)*(y-ay)/(by-ay);l=Math.min(l,x);r=Math.max(r,x);}return [l,r];};\r\n const Y=Array.from({length:n+1},(_,i)=>y0+(y1-y0)*i/n),C=Y.map(chord);\r\n let best=null;\r\n for(let i=0;i<=n;i++)for(let j=i+1;j<=n;j++){\r\n  const l=Math.max(C[i][0],C[j][0]),r=Math.min(C[i][1],C[j][1]),w=r-l,h=Y[j]-Y[i],score=w**1.5*h;\r\n  if(w>0&&(!best||score>best.score))best={score,left:l,top:Y[i],width:w,height:h};\r\n }\r\n return best&&{left:best.left,top:best.top,width:best.width,height:best.height};\r\n}\r\n// px clear of the rim; the width a menu is laid out at and its floor zoom; below minW x minH the cut is too small to hold a\r\n// menu and the portal opens stepped in (the squat diamond).\r\nconst INSET={margin:10,layoutPx:240,minZoom:.6,minW:150,minH:190};\r\n// on: lay the destination out for the cut (flat: into its rectangle; scene: fit cfg.fit); off: the whole window (step in).\r\nfunction layoutInCut(dialog,on){\r\n const f=framed;if(!f)return;\r\n if(f.flat){\r\n  if(!on){dialog.classList.remove('portal-inset');dialog.style.removeProperty('--inset-zoom');setFace(dialog,f.face);return;}\r\n  const r=f.inset??=insetRect(f.outlines.shape),m=INSET.margin,box={left:r.left+m,top:r.top+m,width:r.width-2*m,height:r.height-2*m};\r\n  setFace(dialog,box);dialog.classList.add('portal-inset');\r\n  dialog.style.setProperty('--inset-zoom',Math.max(INSET.minZoom,Math.min(1,(box.width-40)/INSET.layoutPx)).toFixed(3));\r\n  return;\r\n }\r\n const fit=f.cfg?.fit,el=fit&&dialog.querySelector(fit.sel);if(!el)return;\r\n if(!on){for(const k of ['height','margin-top','align-self'])el.style.removeProperty(k);return;}\r\n // The layer's band [top,bottom] (fractions of its height) spans the cut's height, its top at the cut's top.\r\n const ys=f.look.pts.map(p=>p[1]),top=Math.min(...ys),H=(Math.max(...ys)-top)/(fit.band[1]-fit.band[0]);\r\n el.style.setProperty('height',d2(H)+'px','important');el.style.setProperty('align-self','start','important');el.style.setProperty('margin-top',d2(top-f.face.top-fit.band[0]*H)+'px','important');\r\n}\r\nfunction throughCut(el){const r=el.getBoundingClientRect();return pointInPolygon(r.left+r.width/2,r.top+r.height/2,framed.outlines.shape);}\r\n// clip-path for a shaped dialog: the outline (client px -> the dialog's box) plus the bar's box (a separate subpath; nonzero\r\n// fill unions them). R7: nothing of a scene shows in front of the wall any more, so nothing else pokes through.\r\nfunction clipFor(dialog,pts){\r\n const r=dialog.getBoundingClientRect(),o=([x,y])=>`${d2(x-r.left)} ${d2(y-r.top)}`,box=el=>el?.getClientRects().length?el.getBoundingClientRect():null;\r\n const bar=box(document.getElementById('coachDock')),parts=bar?[`M${o([bar.left,bar.top])}L${o([bar.right,bar.top])}L${o([bar.right,bar.bottom])}L${o([bar.left,bar.bottom])}Z`]:[];\r\n return `path('M${pts.slice(0,-1).map(o).join('L')}Z${parts.join('')}')`;\r\n}\r\nfunction clipTo(dialog,pts,{from=null,ms=0,ease='ease'}={}){\r\n const go=()=>{if(framed?.dialog!==dialog)return;motion(dialog,ms?`clip-path ${ms}ms ${ease}`:'none');dialog.style.clipPath=clipFor(dialog,pts);};\r\n if(from){motion(dialog,'none');dialog.style.clipPath=clipFor(dialog,from);settle().then(go);}else go();\r\n}\r\nfunction lean(on){\r\n const f=framed;if(!f?.look.shaped||!f.dialog||f.leaned===on)return;\r\n f.leaned=on;const dialog=f.dialog;leanButton(dialog.querySelector('[data-peer-lean]'),on,f.look.label);\r\n dialog.classList.toggle('portal-leaned',on);\r\n layoutInCut(dialog,!on);\r\n clipTo(dialog,on?f.outlines.rect:f.outlines.shape,{ms:prefersReducedMotion()?0:520,ease:'cubic-bezier(.3,0,.2,1)'});\r\n showAura(on?{...f.look,pts:rectPts(f.face),shaped:false}:f.look,'lean');\r\n}\r\nfunction leanButton(step,on,label){if(!step)return;step.setAttribute('aria-pressed',String(on));step.setAttribute('aria-label',`${on?'Step back from':'Step in to'} ${label}`);step.textContent=on?'⤡':'⤢';}\r\n// #132: a whole window's name rides its upper rail, drawn left to right so it reads upright (the bottom rail is occupied by\r\n// the physical dock). R7: a cut carries none.\r\nconst d2=v=>(+v).toFixed(1);\r\nconst pathD=pts=>`M${pts.slice(0,-1).map(([x,y])=>d2(x)+' '+d2(y)).join('L')}Z`;\r\nexport function namePath(face,gap=7){const y=d2(face.top-gap);return `M${d2(face.left)} ${y}L${d2(face.left+face.width)} ${y}`;}\r\n\r\n// ---- #134 the energy round the open destination ---------------------------------------------------------------------\r\n// Ian 23 Sept: \"a vignetted colourful energy around the edge of the menu, in the shape of the portal that leads to it, but\r\n// not covering the menus\". The six neons flow round the window's outline with the shape's colour leading, as #130's\r\n// Portal-style rim: round fire tongues, a burst on open and a fizzle on close. It never covers the menu: the colour sits\r\n// in a band on the window's edge, outward over the quilt (or the frame's rail) and inward only as a faint vignette that\r\n// is gone within ~20 px (inside the menus' own padding); nothing in it takes a pointer, and the name (#132) rides outside\r\n// the window. Built from masks drawn once; the flow is a conic gradient turning inside them by transform, so running it\r\n// costs the compositor, not a repaint. Static under reduced motion.\r\nconst AURA={spinMs:16000,flickerMs:1300,sparks:18,sparkMs:800,specPx:40,maskScale:.5,\r\n // R21 L2: the inward feather fades over featherFrac of the face's short side (was ~36px) at featherAlpha peak (was .9 stacked), shadeK scales the dark depth strokes, fullFrac/fullAlpha are the same for full-screen faces, fullCurve the falloff exponent (2 = ease-out, most of it near the edge), railAlpha the faint glow left on their rail (was solid).\r\n edgeAlpha:.5,featherFrac:.3,featherAlpha:.3,fullFrac:.2,fullAlpha:.2,fullCurve:2,railAlpha:.15,shadeK:.4,outK:.5,ringAlpha:.5,inset:12}; // outK scales the outward glow bands past the first; ringAlpha is the rim's own peak opacity (was .9); inset pulls shaped windows in from the face edge (px)\r\nlet aura=null;\r\n// White-on-clear mask of the rim band: an inward vignette (clipped inside the outline), an outward glow (outside it) with\r\n// round fire tongues (a seeded dash rhythm on a wide round-capped stroke). A soft glow needs no detail, so it's drawn once\r\n// on a canvas at AURA.maskScale and handed over as a PNG: an SVG mask re-rasterised at the screen's full density cost\r\n// hundreds of ms on the first frame. The hot rim line is a crisp stroke in the aura's own svg instead.\r\nfunction rimMask(pts,w,h,seed,shaped,fullscreen=false,face=null){\r\n const k=AURA.maskScale,c=document.createElement('canvas');c.width=Math.ceil(w*k);c.height=Math.ceil(h*k);\r\n const g=c.getContext('2d',{willReadFrequently:true}),path=new Path2D(pathD(pts)),outside=new Path2D(`M0 0H${w}V${h}H0Z${pathD(pts)}`);\r\n let s=seed;const r=()=>(s=(s*16807)%2147483647)/2147483647,dash=(a,b)=>Array.from({length:12},()=>[a*(.3+r()),b*(.6+r())]).flat();\r\n g.scale(k,k);g.strokeStyle='#fff';g.lineJoin='round';\r\n const band=(width,alpha,dashes=null,cap='butt')=>{g.lineWidth=width;g.globalAlpha=alpha;g.lineCap=cap;g.setLineDash(dashes||[]);g.stroke(path);};\r\n g.save();if(face)g.clip(new Path2D(`M${face.left} ${face.top}h${face.width}v${face.height}h${-face.width}Z`)); // never over the rail, bolts or corners\r\n g.clip(outside,'evenodd');\r\n if(fullscreen){\r\n   const image=g.createImageData(c.width,c.height),smooth=t=>t*t*(3-2*t),xs=pts.map(p=>p[0]),ys=pts.map(p=>p[1]),bx=Math.min(...xs),by=Math.min(...ys),bx2=Math.max(...xs),by2=Math.max(...ys),rail=Math.max(1,Math.min(bx,by,w-bx2,h-by2));\r\n   for(let y=0;y<c.height;y++)for(let x=0;x<c.width;x++){\r\n    const X=x/k,Y=y/k,dx=Math.max(bx-X,0,X-bx2),dy=Math.max(by-Y,0,Y-by2),d=Math.hypot(dx,dy),alpha=(d==0||d>=rail)?0:Math.round(255*AURA.railAlpha*(1-smooth(d/rail))**.8),i=4*(y*c.width+x);\r\n    image.data[i]=image.data[i+1]=image.data[i+2]=255;image.data[i+3]=alpha;\r\n   }\r\n   g.putImageData(image,0,0);\r\n  }\r\n  else if(shaped){for(const [a,b] of [[5,.9],[12,.5],[24,.26],[42,.11],[66,.04]])band(a,a===5?AURA.ringAlpha:b*AURA.outK);band(24,.3,dash(5,44),'round');g.lineDashOffset=r()*60;band(14,.5,dash(3,30),'round');}\r\n else{for(const [a,b] of [[5,.85],[11,.4],[18,.16]])band(a,a===5?AURA.ringAlpha:b*AURA.outK);band(11,.32,dash(3,26),'round');}\r\n g.restore();\r\n // Last: the full-screen glow is a putImageData, which overwrites every pixel it covers, so the inward feather goes on top of it.\r\n g.save();g.clip(path);\r\n {const px=pts.map(p=>p[0]),py=pts.map(p=>p[1]),short=Math.min(Math.max(...px)-Math.min(...px),Math.max(...py)-Math.min(...py)),\r\n   D=short*(fullscreen?AURA.fullFrac:AURA.featherFrac),peak=fullscreen?AURA.fullAlpha:AURA.featherAlpha,n=10;\r\n  for(let i=0;i<n;i++)band(2*D*(1-i/n)**(fullscreen?AURA.fullCurve:1),peak/n); // stacked strokes: opacity falls off linearly from the rim to D inside\r\n  band(4,AURA.edgeAlpha);} // the thin bright neon edge that still reads as a portal\r\n g.restore();\r\n return `url(\"${c.toDataURL()}\")`;\r\n}\r\nconst setMask=(el,url)=>{el.style.webkitMaskImage=url;el.style.maskImage=url;};\r\nfunction hideAura(){aura?.anims.forEach(a=>a.cancel());aura?.el.remove();aura=null;}\r\n// Closing: the rim fizzles (burst) and goes out, so nothing but the frame rides the way back.\r\nfunction auraOut(){\r\n burst('close');const a=aura;if(!a)return;aura=null;\r\n a.el.animate([{opacity:1},{opacity:0}],{duration:prefersReducedMotion()?0:420,easing:'ease-in',fill:'forwards'}).finished.then(()=>{a.anims.forEach(x=>x.cancel());a.el.remove();},()=>{});\r\n}\r\nfunction showAura(look,why='open'){\r\n hideAura();\r\n if(!look?.pts)return;\r\n const themed=craftPortal(boardId)?paletteFor(boardId,store.get(paletteKey(boardId))):null;\r\n const w=Math.max(1,chrome.clientWidth),h=Math.max(1,chrome.clientHeight),d=pathD(look.pts),[cx,cy]=centroidOf(look.pts),strip=themed?.colors[0]||readLook().strip,seq=themed?.colors||stripSeq(strip),reduced=prefersReducedMotion();\r\n const xs=look.pts.map(p=>p[0]),ys=look.pts.map(p=>p[1]),bx=Math.min(...xs),by=Math.min(...ys),bw=Math.max(...xs)-bx,bh=Math.max(...ys)-by;\r\n const el=document.createElement('div');el.className='portal-aura';el.setAttribute('aria-hidden','true');el.classList.toggle('shaped',!!look.shaped);\r\n el.style.cssText=`--aura:${strip};--aura-edge:color-mix(in srgb,${strip} 45%,#fff);--aura-seq:${[...seq,seq[0]].join(',')};--cx:${d2(cx)}px;--cy:${d2(cy)}px`;\r\n const label=look.id==='down'?'':String(look.label||'').toUpperCase().replace(/[<&>]/g,''); // Achievements already titles the scene inside the cut.\r\n // One static svg (the aperture's depth inside the outline, the name outside it) and one masked layer holding the\r\n // flow: two neon wheels turning against each other (the second flickering, for the fire) and the specular blob.\r\n el.innerHTML=`<svg viewBox=\"0 0 ${w} ${h}\" preserveAspectRatio=\"none\"><defs><path id=\"portalAuraP\" d=\"${d}\"/><clipPath id=\"portalAuraIn\"><use href=\"#portalAuraP\"/></clipPath>${look.name?`<path id=\"portalAuraName\" d=\"${look.name}\"/>`:''}</defs><g clip-path=\"url(#portalAuraIn)\" fill=\"none\" stroke-linejoin=\"round\"><use href=\"#portalAuraP\" stroke=\"#07040b\" stroke-opacity=\"${d2(.2*AURA.shadeK)}\" stroke-width=\"34\"/><use href=\"#portalAuraP\" stroke=\"#07040b\" stroke-opacity=\"${d2(.28*AURA.shadeK)}\" stroke-width=\"15\"/><use href=\"#portalAuraP\" stroke=\"#fff\" stroke-opacity=\".6\" stroke-width=\"2.4\"/></g><use href=\"#portalAuraP\" fill=\"none\" stroke=\"var(--aura-edge)\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/>`\r\n  +(label&&look.name?`<text class=\"portal-aura-name\"><textPath href=\"#portalAuraName\" startOffset=\"50%\" text-anchor=\"middle\">${label}</textPath></text>`:'')+'</svg>'\r\n  +'<i class=\"portal-aura-fire\"><i></i><i class=\"b\"></i><i class=\"portal-aura-spec\"></i></i>';\r\n const fire=el.querySelector('.portal-aura-fire'),[wa,wb,blob]=fire.children;\r\n const fl=parseFloat(chrome.style.getPropertyValue('--face-left')),ft=parseFloat(chrome.style.getPropertyValue('--face-top')),fw=parseFloat(chrome.style.getPropertyValue('--face-width')),fh=parseFloat(chrome.style.getPropertyValue('--face-height')),\r\n  face=[fl,ft,fw,fh].every(Number.isFinite)?{left:fl,top:ft,width:fw,height:fh}:null;\r\n setMask(fire,rimMask(look.pts,w,h,7,look.shaped,look.fullscreen,look.fullscreen?null:face)); // full-screen faces keep their deliberate rail glow (R19)\r\n const R=Math.max(bw,bh)*.45,far=Math.ceil(Math.max(...[[0,0],[w,0],[0,h],[w,h]].map(([x,y])=>Math.hypot(x-cx,y-cy))));\r\n blob.style.cssText=`left:${d2(bx+bw*.2-R/2)}px;top:${d2(by+bh*.14-R/2)}px;width:${d2(R)}px;height:${d2(R)}px`;\r\n for(const wheel of [wa,wb])wheel.style.cssText=`left:${d2(cx-far)}px;top:${d2(cy-far)}px;width:${2*far}px;height:${2*far}px`;\r\n chrome.append(el);\r\n aura={el,look,spec:blob,anims:[]};\r\n if(reduced)return;\r\n aura.anims.push(wa.animate([{transform:'rotate(0deg)'},{transform:'rotate(360deg)'}],{duration:AURA.spinMs,iterations:Infinity}),\r\n  wb.animate([{transform:'rotate(360deg)'},{transform:'rotate(0deg)'}],{duration:AURA.spinMs*.62,iterations:Infinity}),\r\n  wb.animate([{opacity:.15},{opacity:.7}],{duration:AURA.flickerMs,iterations:Infinity,direction:'alternate',easing:'ease-in-out'}),\r\n  el.animate([{opacity:0},{opacity:1}],{duration:why==='open'?420:260,easing:'ease-out'}));\r\n if(why==='open')burst('open');\r\n}\r\n// #130's open burst / close fizzle: a white flash along the rim and sparks flying off it (open) or falling into the\r\n// core (close). Compositor-only (transform and opacity), gone in under a second.\r\nfunction burst(kind){\r\n if(!aura||prefersReducedMotion())return;\r\n const {el,look}=aura,pts=look.pts,[cx,cy]=centroidOf(pts),close=kind==='close';\r\n const flash=document.createElement('i');flash.className='portal-aura-flash';setMask(flash,el.querySelector('.portal-aura-fire').style.maskImage);el.append(flash);\r\n flash.animate([{opacity:close?.6:.95},{opacity:0}],{duration:close?420:620,easing:'ease-out',fill:'forwards'}).finished.then(()=>flash.remove(),()=>flash.remove());\r\n for(let k=0;k<AURA.sparks;k++){\r\n  const i=Math.floor(Math.random()*(pts.length-1)),f=Math.random(),x=pts[i][0]+(pts[i+1][0]-pts[i][0])*f,y=pts[i][1]+(pts[i+1][1]-pts[i][1])*f;\r\n  const dx=x-cx,dy=y-cy,l=Math.hypot(dx,dy)||1,go=(close?-1:1)*(16+Math.random()*48),b=document.createElement('b');\r\n  b.style.left=d2(x)+'px';b.style.top=d2(y)+'px';b.style.background=k%3?'#fff':look.color;el.append(b);\r\n  b.animate([{transform:'translate(0,0) scale(1)',opacity:1},{transform:`translate(${d2(dx/l*go)}px,${d2(dy/l*go)}px) scale(.2)`,opacity:0}],{duration:AURA.sparkMs*(.55+Math.random()*.6),easing:'cubic-bezier(.2,.7,.3,1)',fill:'forwards'}).finished.then(()=>b.remove(),()=>b.remove());\r\n }\r\n}\r\n\r\n// ---- #135 #126 looking round the scene behind the window ------------------------------------------------------------\r\n// The eye follows the phone's tilt from a baseline caught when the destination opens (createTiltLook: gravity-based,\r\n// clamped, smoothed here, and re-centring on whatever hold the phone settles at; knobs in character-phone-sensor TILT), or, on flat menus with no tilt, a finger drag\r\n// (springing back on release). It slides each destination's layers against the fixed frame (depths below) and drives the\r\n// 3D scenes' off-axis cameras through peer.mjs. iOS asks once, from a chip on the first 3D destination (never a modal),\r\n// and the answer is kept. Off under reduced motion and in camera-only mode (D24); paused while the page is hidden.\r\n// dead (R7, Ian's Android): a new reading must move the eye by this share of full tilt (about half a degree) or it is\r\n// sensor noise and ignored, so a phone held still lets the look-around stop instead of restyling every frame.\r\nconst PEER={smoothMs:110,dragPx:160,dead:.04};\r\nconst peerTilt=createTiltLook();\r\nconst TILT_KEY='myr5.tiltPermission';\r\n// Any framed destination can mark its own layers data-peer-depth=\"far|mid|near\": they slide PEER_LAYER px at full tilt\r\n// (far with the eye, near against it), times the destination's strength; the dialog also carries --peer-x/--peer-y\r\n// (-1..1) while a flat menu sits in a cut (.portal-inset, the only CSS that reads them: on anything else they would restyle\r\n// the whole menu every frame). Per destination: move: px the whole dialog slides (flat menus, a third of the scenes' range);\r\n// layers: [selector,px] for destinations that don't mark theirs (nested ones add up); scene: a 3D scene reading peer.mjs\r\n// (no drag look-around: a drag there turns the scene; it fills its cut, see shapeDialog); chip: offer iOS's tilt\r\n// permission (the scenes always do); cards/fit: shapeDialog.\r\nconst PEER_LAYER={far:14,mid:5,near:-4};\r\nconst PEER_DEPTH=[\r\n ['#accountPanel.classroom-panel',{scene:true}],\r\n ['#mealsPanel',{scene:true,cards:'.pyramid-zoom,#foodDial,#mealConfirmation',fit:{sel:'#pyramidScanner',band:[0,1]}}],\r\n ['#remindersPanel',{scene:true,layers:[['.reminders-computer-stage',8],['.reminders-computer-screen',3]]}],\r\n ['dialog.ship-view',{scene:true,layers:[['.ship-view-bg',16],['.ship-view-coach',5]]}],\r\n // The constellation's bosses (7.5-90% of the art's height) span the cut: its top row along the inverted triangle's top.\r\n ['.ach-board',{scene:true,layers:[['.ach-stage',10],['.ach-stars',-4]],cards:'.ach-detail',fit:{sel:'.ach-stage',band:[.075,.9]}}],\r\n   ['#battlePassPanel',{move:2,strength:.6,chip:true,layers:[['.pass-map-shell',8]]}],\r\n   ['#library',{move:2,strength:.6,chip:true,layers:[['#holoStage',8],['.movement-cards',4]]}],\r\n   ['#spotifyPanel',{move:2,strength:.6,chip:true}],\r\n ['#coachPathPicker',{move:2,strength:.6,chip:true}],\r\n   ['.meditation-panel',{move:2,strength:.6,chip:true}], // #127: the still room peers in too, gentler\r\n];\r\nconst PEER_2D={move:4};\r\nconst peer={dialog:null,cfg:null,els:[],depths:[],flatEls:[],x:0,y:0,tx:0,ty:0,raf:0,last:0,drag:null,sensor:false,ctl:null,watch:null};\r\nconst clamp1=v=>Math.max(-1,Math.min(1,v));\r\nfunction tiltAccess(){\r\n if(typeof DeviceOrientationEvent==='undefined')return 'none';\r\n if(typeof DeviceOrientationEvent.requestPermission!=='function')return 'free';\r\n try{return localStorage.getItem(TILT_KEY)||'ask';}catch{return 'ask';}\r\n}\r\nfunction peerOn(dialog){\r\n peerOff();\r\n if(prefersReducedMotion()||document.body.dataset.cameraWorkout==='true')return;\r\n const cfg=peerCfg(dialog),signal=(peer.ctl=new AbortController()).signal;\r\n Object.assign(peer,{dialog,cfg,x:0,y:0,tx:0,ty:0,drag:null,sensor:false});peerTilt.reset();\r\n refreshPeerTargets(dialog,cfg);\r\n peer.watch=new MutationObserver(()=>{if(peer.dialog!==dialog)return;refreshPeerTargets(dialog,cfg);kickPeer();});\r\n peer.watch.observe(dialog,{subtree:true,childList:true,attributes:true,attributeFilter:['data-peer-depth']});\r\n const access=tiltAccess();\r\n if(access==='free'||access==='granted')addEventListener('deviceorientation',onOrient,{signal});\r\n else if(access==='ask'&&(cfg.scene||cfg.chip))tiltChip(dialog);\r\n if(!cfg.scene&&!dialog.classList.contains('portal-fullscreen')){ // a full-screen room keeps its own drag (meditation's)\r\n  dialog.addEventListener('pointerdown',e=>{if(e.pointerType==='touch'&&!peer.sensor)peer.drag={x:e.clientX,y:e.clientY,id:e.pointerId};},{signal,passive:true});\r\n  dialog.addEventListener('pointermove',e=>{const g=peer.drag;if(g?.id!==e.pointerId)return;peer.tx=clamp1((e.clientX-g.x)/PEER.dragPx);peer.ty=clamp1((e.clientY-g.y)/PEER.dragPx);kickPeer();},{signal,passive:true});\r\n  for(const type of ['pointerup','pointercancel'])dialog.addEventListener(type,e=>{if(peer.drag?.id===e.pointerId){peer.drag=null;peer.tx=peer.ty=0;kickPeer();}},{signal,passive:true});\r\n }\r\n if(framed?.direct){\r\n  dialog.addEventListener('pointermove',e=>{if(peer.sensor||document.hidden)return;const r=dialog.getBoundingClientRect();peer.tx=clamp1((e.clientX-r.left-r.width/2)/(r.width*.45));peer.ty=clamp1((e.clientY-r.top-r.height/2)/(r.height*.45));kickPeer();},{signal,passive:true});\r\n  dialog.addEventListener('pointerleave',()=>{if(!peer.sensor){peer.tx=peer.ty=0;kickPeer();}},{signal,passive:true});\r\n }\r\n document.addEventListener('visibilitychange',()=>{peerTilt.reset();if(document.hidden){cancelAnimationFrame(peer.raf);peer.raf=0;peer.last=0;}else kickPeer();},{signal});\r\n addEventListener('orientationchange',()=>peerTilt.reset(),{signal});\r\n}\r\nfunction peerOff(){\r\n peer.ctl?.abort();peer.ctl=null;peer.watch?.disconnect();peer.watch=null;cancelAnimationFrame(peer.raf);peer.raf=0;\r\n const {dialog}=peer;\r\n if(dialog){\r\n  for(const k of ['left','top','--peer-x','--peer-y'])dialog.style.removeProperty(k);\r\n  dialog.querySelector(':scope>.portal-peer-ui>.portal-tilt-chip')?.remove();\r\n  for(const el of dialog.querySelectorAll('[data-peer-depth]'))el.style.translate='';\r\n }\r\n for(const [el] of peer.els)el.style.translate='';\r\n for(const el of peer.flatEls)el.style.translate='';\r\n Object.assign(peer,{dialog:null,cfg:null,els:[],depths:[],flatEls:[],x:0,y:0,tx:0,ty:0,last:0,drag:null,sensor:false});eye.x=eye.y=0;\r\n if(aura?.spec)aura.spec.style.translate='';\r\n}\r\nfunction refreshPeerTargets(dialog=peer.dialog,cfg=peer.cfg){\r\n if(!dialog||!cfg)return;\r\n peer.els=(cfg.layers||[]).flatMap(([s,px])=>[...dialog.querySelectorAll(s)].map(el=>[el,px]));\r\n peer.depths=[...dialog.querySelectorAll('[data-peer-depth]')];\r\n peer.flatEls=framed?.direct&&dialog.classList.contains('portal-fullscreen')&&!cfg.scene\r\n  ?[...dialog.children].filter(el=>el.id!=='coachDock'&&el.id!=='myr5Shell'&&el.id!=='portalChrome'&&!el.matches('.portal-peer-ui')):[];\r\n}\r\nfunction onOrient(e){\r\n if(!peer.dialog)return;\r\n const t=peerTilt.read(e,performance.now());if(!t)return;\r\n peer.sensor=true;peer.drag=null;\r\n const dG=t.x,dB=t.y;\r\n if(Math.abs(dG-peer.tx)+Math.abs(dB-peer.ty)<PEER.dead)return;\r\n // As through a real window: turn the phone's face to the left (its right edge back) and the eye is now to the window's\r\n // right, so the pyramid shows its right side (Ian); tip the top edge back and the eye drops below it.\r\n peer.tx=dG;peer.ty=dB;kickPeer();\r\n}\r\n// Each run of steps (until the eye settles) looks its depth layers up once: a scene may add layers after it opens.\r\nfunction kickPeer(){if(!peer.raf&&peer.dialog&&!document.hidden){if(!peer.last){peer.last=performance.now();refreshPeerTargets();}peer.raf=requestAnimationFrame(stepPeer);}}\r\nfunction stepPeer(now){\r\n peer.raf=0;\r\n const k=1-Math.exp(-Math.max(0,now-peer.last)/PEER.smoothMs);peer.last=now; // low-pass by time, the same at any frame rate\r\n peer.x+=(peer.tx-peer.x)*k;peer.y+=(peer.ty-peer.y)*k;\r\n if(Math.abs(peer.tx-peer.x)+Math.abs(peer.ty-peer.y)<.002){peer.x=peer.tx;peer.y=peer.ty;peer.last=0;}else kickPeer();\r\n const {x,y,cfg,dialog}=peer;if(!dialog)return;\r\n eye.x=x;eye.y=y;\r\n const full=dialog.classList.contains('portal-fullscreen');\r\n if(cfg.move&&framed?.face&&!framed.look.shaped&&!full){dialog.style.setProperty('left',d2(framed.face.left+x*cfg.move)+'px','important');dialog.style.setProperty('top',d2(framed.face.top+y*cfg.move)+'px','important');}\r\n if(dialog.classList.contains('portal-inset')){dialog.style.setProperty('--peer-x',x.toFixed(3));dialog.style.setProperty('--peer-y',y.toFixed(3));}\r\n const slide=(el,px)=>{el.style.translate=`${d2(x*px)}px ${d2(y*px)}px`;};\r\n for(const [el,px] of peer.els)slide(el,px);\r\n for(const el of peer.depths)slide(el,(PEER_LAYER[el.dataset.peerDepth]||0)*(cfg.strength??1));\r\n for(const el of peer.flatEls)slide(el,cfg.move||PEER_2D.move);\r\n if(aura?.spec)aura.spec.style.translate=`${d2(-x*AURA.specPx)}px ${d2(-y*AURA.specPx)}px`;\r\n}\r\n// iOS 13+: DeviceOrientationEvent.requestPermission() needs a tap. A small chip in the window's corner, never a modal.\r\nfunction tiltChip(dialog){\r\n const chip=document.createElement('button'),f=framed?.face;chip.type='button';chip.className='portal-tilt-chip';chip.innerHTML='Tilt to look around <b>Allow</b>';\r\n if(f){\r\n  const dockEl=document.getElementById('coachDock'),dock=dockEl?.getClientRects().length?dockEl.getBoundingClientRect():null;\r\n  chip.style.left=f.left+8+'px';\r\n  chip.style.top=Math.max(f.top+8,Math.min(f.top+f.height-26,dock?.top==null?Infinity:dock.top-26))+'px'; // R19: a slim tab sitting on the dock's top edge, left side (meditation's counter chip is bottom-right)\r\n }\r\n chip.onclick=async()=>{\r\n  let answer='denied';try{answer=await DeviceOrientationEvent.requestPermission();}catch{}\r\n  try{localStorage.setItem(TILT_KEY,answer==='granted'?'granted':'denied');}catch{}\r\n  chip.remove();\r\n  if(answer==='granted'&&peer.dialog===dialog)addEventListener('deviceorientation',onOrient,{signal:peer.ctl.signal});\r\n };\r\n peerUi(dialog).append(chip); // hidden while a scene is peered at (portal.css): full screen offers it\r\n}\r\nfunction status(text,action=null){\r\n statusEl.textContent=text;\r\n const button=portalHome?.querySelector('#portalRetry');\r\n if(button){button.hidden=!action;button.textContent=action?.label||'';button._run=action?.run||null;}\r\n}\r\n\r\n// Layout size, not the on-screen box: the observer also fires as the portal is shown mid-dive (scaled), and a backing\r\n// store sized from the scaled box drew every later trail and outline shrunk toward the corner.\r\nfunction resizeOverlay(){\r\n const dpr=Math.min(devicePixelRatio||1,2),r={width:overlay.clientWidth,height:overlay.clientHeight};\r\n overlay.width=Math.round(Math.max(1,r.width)*dpr);overlay.height=Math.round(Math.max(1,r.height)*dpr);\r\n ctx.setTransform(dpr,0,0,dpr,0,0);\r\n}\r\nfunction mapOverlayClientSpace(){\r\n const dpr=Math.min(devicePixelRatio||1,2),r=overlay.getBoundingClientRect(),w=overlay.clientWidth||1,h=overlay.clientHeight||1,sx=w/Math.max(1,r.width),sy=h/Math.max(1,r.height);\r\n ctx.setTransform(dpr*sx,0,0,dpr*sy,-r.left*dpr*sx,-r.top*dpr*sy);\r\n}\r\nfunction strokeGlow(pts,color,alpha,width){\r\n if(pts.length<2)return;\r\n ctx.save();ctx.globalAlpha=alpha;ctx.lineCap='round';ctx.lineJoin='round';\r\n ctx.shadowColor=color;ctx.shadowBlur=18;ctx.strokeStyle=color;ctx.lineWidth=width+4;\r\n ctx.beginPath();pts.forEach(([x,y],i)=>i?ctx.lineTo(x,y):ctx.moveTo(x,y));ctx.stroke();\r\n ctx.shadowBlur=0;ctx.strokeStyle='#fff';ctx.lineWidth=Math.max(1,width-2);ctx.stroke();\r\n ctx.restore();\r\n}\r\nfunction touchDot(x,y,color){\r\n ctx.save();const g=ctx.createRadialGradient(x,y,0,x,y,20);g.addColorStop(0,color+'cc');g.addColorStop(1,color+'00');\r\n ctx.fillStyle=g;ctx.beginPath();ctx.arc(x,y,20,0,Math.PI*2);ctx.fill();ctx.restore();\r\n}\r\nfunction kickRender(){if(!rafId&&!probeFrozen)rafId=requestAnimationFrame(drawFrame);}\r\n\r\n// #105 magical trail (W2-2E2). Colour flows through TRAIL_NEONS once every TRAIL_NEON_PX of stroke length (anchored\r\n// to where the finger went, drifting slowly toward the tip); alpha follows each point's age, eased so it holds\r\n// near full strength before it fades. Normal source-over throughout: additive washes out on the light quilt.\r\nconst TRAIL_NEON_PX=48,TRAIL_STOP_PX=12,TRAIL_MAX_STOPS=64,SHIMMER_MS=450;\r\nconst TAU=Math.PI*2,UNDERGLOW=[16,8,28],WHITE=[255,255,255];\r\nfunction neonRGB(t){\r\n const n=TRAIL_NEON_RGB.length,i=((Math.floor(t)%n)+n)%n,j=(i+1)%n,f=t-Math.floor(t);\r\n const[r1,g1,b1]=TRAIL_NEON_RGB[i],[r2,g2,b2]=TRAIL_NEON_RGB[j];\r\n return[r1+(r2-r1)*f|0,g1+(g2-g1)*f|0,b1+(b2-b1)*f|0];\r\n}\r\nconst trailColor=(d,now)=>neonRGB((d+now*.06)/TRAIL_NEON_PX);\r\nconst fadeAlpha=age=>{const k=Math.min(1,Math.max(0,age/TRAIL_FADE_MS));return 1-k*k*(3-2*k);};\r\n// Fixed-size sparkle pool (typed arrays, ring-buffer cursor) so shedding dust never allocates. Each mote is a\r\n// 2–5px neon disc with a white centre on a soft dark backing (so it reads on beige), twinkling on its own\r\n// phase, drifting outward and slowing to a stop as it dies.\r\nconst SPARK_N=400,SPARK_LIFE=700;\r\nconst sparkX=new Float32Array(SPARK_N),sparkY=new Float32Array(SPARK_N),sparkVX=new Float32Array(SPARK_N),sparkVY=new Float32Array(SPARK_N),sparkSize=new Float32Array(SPARK_N),sparkHue=new Int8Array(SPARK_N),sparkBorn=new Float32Array(SPARK_N).fill(-1e9);\r\nlet sparkCursor=0;\r\nfunction spawnSpark(x,y,born){\r\n const i=sparkCursor;sparkCursor=(sparkCursor+1)%SPARK_N;\r\n const a=Math.random()*TAU,s=30+Math.random()*110,off=4+Math.random()*6; // born just off the point, heading out\r\n sparkX[i]=x+Math.cos(a)*off;sparkY[i]=y+Math.sin(a)*off;sparkVX[i]=Math.cos(a)*s;sparkVY[i]=Math.sin(a)*s;\r\n sparkSize[i]=1.4+Math.random()*1.1;sparkHue[i]=(Math.random()*TRAIL_NEONS.length)|0;sparkBorn[i]=born;\r\n}\r\n// Per live frame (self-capping: a slow frame rate sheds less): three motes off the fingertip, four scattered\r\n// along the last SHED_RECENT_MS of path.\r\nconst SHED_RECENT_MS=250;\r\nfunction shedSparks(pts,now){\r\n const last=pts.length-1,tip=pts[last];for(let k=0;k<3;k++)spawnSpark(tip.x,tip.y,now);\r\n let first=last;while(first>0&&now-pts[first-1].t<SHED_RECENT_MS)first--;\r\n if(first===last)return;\r\n for(let k=0;k<4;k++){const i=first+1+((Math.random()*(last-first))|0),f=Math.random(),a=pts[i-1],b=pts[i];spawnSpark(a.x+(b.x-a.x)*f,a.y+(b.y-a.y)*f,now);}\r\n}\r\nfunction sparksAlive(now){for(let i=0;i<SPARK_N;i++)if(now-sparkBorn[i]<SPARK_LIFE)return true;return false;}\r\n// Mote sprites, drawn once: a row of the six neons in one small canvas, so each mote is a single drawImage\r\n// from one source (which the canvas batches) instead of three path fills. Cell = soft dark backing, neon\r\n// disc at MOTE_NEON of the cell radius, white centre at MOTE_WHITE.\r\nconst MOTE_PX=20,MOTE_NEON=.68,MOTE_WHITE=.3;\r\nlet moteSprites=null;\r\nfunction motes(){\r\n if(moteSprites)return moteSprites;\r\n const c=document.createElement('canvas'),g=c.getContext('2d'),R=MOTE_PX/2;c.width=MOTE_PX*TRAIL_NEONS.length;c.height=MOTE_PX;\r\n TRAIL_NEONS.forEach((color,i)=>{\r\n  const x=i*MOTE_PX+R,disc=(r,style)=>{g.fillStyle=style;g.beginPath();g.arc(x,R,r,0,TAU);g.fill();};\r\n  const back=g.createRadialGradient(x,R,0,x,R,R);back.addColorStop(0,'rgba(16,8,28,.5)');back.addColorStop(1,'rgba(16,8,28,0)');\r\n  disc(R,back);disc(R*MOTE_NEON,color);disc(R*MOTE_WHITE,'#fff');\r\n });\r\n return moteSprites=c;\r\n}\r\nfunction drawSparks(now){\r\n const sprites=motes();\r\n ctx.save();\r\n for(let i=0;i<SPARK_N;i++){\r\n  const age=now-sparkBorn[i];if(age<0||age>=SPARK_LIFE)continue;\r\n  const life=age/SPARK_LIFE,drift=age/1000*(1-life/2),x=sparkX[i]+sparkVX[i]*drift,y=sparkY[i]+sparkVY[i]*drift+16*life*life;\r\n  const tw=.5+.5*Math.sin(age*.04+i*2.4),h=sparkSize[i]*(.8+.2*tw)/MOTE_NEON;\r\n  ctx.globalAlpha=(1-life*life)*(.55+.45*tw);\r\n  ctx.drawImage(sprites,sparkHue[i]*MOTE_PX,0,MOTE_PX,MOTE_PX,x-h,y-h,2*h,2*h);\r\n }\r\n ctx.restore();\r\n}\r\nfunction starPath(p,x,y,len,waist,rot){\r\n for(let k=0;k<8;k++){const a=rot+k*Math.PI/4,rr=k%2?waist:len,px=x+Math.cos(a)*rr,py=y+Math.sin(a)*rr;k?p.lineTo(px,py):p.moveTo(px,py);}\r\n p.closePath();\r\n}\r\n// Fingertip bloom + slowly turning star flare: a dark halo (so it reads on beige), a white-hot → neon bloom,\r\n// then a long and a short four-point star in one fill. A few fills per tip per frame, not per particle.\r\n// size scales the whole flare (the release shimmer reuses it as a smaller, faster-turning glint).\r\n// `rgb` is an [r,g,b] triple so rgba() stops can be built directly.\r\nfunction drawFlare(x,y,[r,g,b],alpha,now,size=1){\r\n const neon=a=>`rgba(${r},${g},${b},${a})`;\r\n ctx.save();ctx.globalAlpha=alpha;ctx.translate(x,y);ctx.scale(size,size);\r\n let grad=ctx.createRadialGradient(0,0,0,0,0,30);grad.addColorStop(0,'rgba(16,8,28,.3)');grad.addColorStop(1,'rgba(16,8,28,0)');\r\n ctx.fillStyle=grad;ctx.fillRect(-30,-30,60,60);\r\n grad=ctx.createRadialGradient(0,0,0,0,0,20);\r\n grad.addColorStop(0,'#fff');grad.addColorStop(.25,'rgba(255,255,255,.9)');grad.addColorStop(.5,neon(.75));grad.addColorStop(1,neon(0));\r\n ctx.fillStyle=grad;ctx.fillRect(-20,-20,40,40);\r\n const star=new Path2D(),rot=now/1200;starPath(star,0,0,28,3,rot);starPath(star,0,0,14,2.5,rot+Math.PI/4);\r\n grad=ctx.createRadialGradient(0,0,0,0,0,28);\r\n grad.addColorStop(0,'#fff');grad.addColorStop(.18,'#fff');grad.addColorStop(.45,neon(.95));grad.addColorStop(1,neon(0));\r\n ctx.fillStyle=grad;ctx.fill(star);\r\n ctx.restore();\r\n}\r\nconst pathLength=pts=>{let d=0;for(let i=1;i<pts.length;i++)d+=Math.hypot(pts[i].x-pts[i-1].x,pts[i].y-pts[i-1].y);return d;};\r\nconst ribbonPath=pts=>{const p=new Path2D();pts.forEach((pt,i)=>i?p.lineTo(pt.x,pt.y):p.moveTo(pt.x,pt.y));return p;};\r\n// Gradient stops every TRAIL_STOP_PX of arc length (interpolated inside long segments, capped at\r\n// TRAIL_MAX_STOPS so cost doesn't grow with point count), each placed at its projection on the tail→head\r\n// axis — the one line a canvas gradient follows — so colour and fade track the path for any stroke that\r\n// doesn't fold back past 90°. d0: stroke length already faded off the tail (keeps colours anchored).\r\nfunction ribbonSamples(pts,now,d0){\r\n const tail=pts[0],head=pts[pts.length-1],ax=head.x-tail.x,ay=head.y-tail.y,len2=ax*ax+ay*ay;\r\n if(len2<1)return null;\r\n const total=pathLength(pts),step=Math.max(TRAIL_STOP_PX,total/TRAIL_MAX_STOPS),stops=[];\r\n let d=0,next=0,off=0;\r\n for(let i=1;i<pts.length;i++){\r\n  const a=pts[i-1],b=pts[i],seg=Math.hypot(b.x-a.x,b.y-a.y);\r\n  while(next<=d+seg){\r\n   const f=seg?(next-d)/seg:0,x=a.x+(b.x-a.x)*f,y=a.y+(b.y-a.y)*f;\r\n   off=Math.max(off,Math.min(1,((x-tail.x)*ax+(y-tail.y)*ay)/len2));\r\n   stops.push({off,alpha:fadeAlpha(now-(a.t+(b.t-a.t)*f)),d:d0+next});next+=step;\r\n  }\r\n  d+=seg;\r\n }\r\n stops.push({off:1,alpha:fadeAlpha(now-head.t),d:d0+total});\r\n return{tail,head,stops};\r\n}\r\nfunction ribbonGradient({tail,head,stops},colorAt){\r\n const g=ctx.createLinearGradient(tail.x,tail.y,head.x,head.y);\r\n for(const s of stops){const[r,gg,b]=colorAt(s.d);g.addColorStop(s.off,`rgba(${r},${gg},${b},${s.alpha.toFixed(3)})`);}\r\n return g;\r\n}\r\n// Full-motion trail: six stroke() passes over one Path2D (no shadows, so cost doesn't scale with point\r\n// count) — a soft dark underglow so the neon pops on the light quilt, a wide soft two-step neon glow, a\r\n// vivid core, a white-hot centre — then the tip flare. `live` also sheds dust; a just-released trail (in\r\n// `fading`) keeps rendering with no new tip dust until it ages out.\r\nfunction renderRibbon(pts,now,live){\r\n let start=0,d0=0;\r\n while(start<pts.length-1&&now-pts[start].t>=TRAIL_FADE_MS){d0+=Math.hypot(pts[start+1].x-pts[start].x,pts[start+1].y-pts[start].y);start++;}\r\n if(now-pts[start].t>=TRAIL_FADE_MS)return;\r\n const visible=start?pts.slice(start):pts,tip=visible[visible.length-1];\r\n const samples=visible.length>1&&ribbonSamples(visible,now,d0);\r\n if(samples){\r\n  const path=ribbonPath(visible),dark=ribbonGradient(samples,()=>UNDERGLOW),neon=ribbonGradient(samples,d=>trailColor(d,now)),hot=ribbonGradient(samples,()=>WHITE);\r\n  ctx.save();ctx.lineCap='round';ctx.lineJoin='round';\r\n  const pass=(style,alpha,width)=>{ctx.strokeStyle=style;ctx.globalAlpha=alpha;ctx.lineWidth=width;ctx.stroke(path);};\r\n  pass(dark,.08,40);pass(dark,.12,32); // soft dark underglow\r\n  pass(neon,.3,26);pass(neon,.42,17); // wide soft glow\r\n  pass(neon,1,6.5); // vivid core\r\n  pass(hot,1,1.5); // white-hot centre\r\n  ctx.restore();\r\n }\r\n drawFlare(tip.x,tip.y,trailColor(d0+pathLength(visible),now),Math.max(.35,fadeAlpha(now-tip.t)),now);\r\n if(live)shedSparks(visible,now);\r\n}\r\n// #105 release shimmer: a star glint sweeps the part of the path still lit at release, tail→tip, over\r\n// SHIMMER_MS, shaking dust loose as it goes (so the last motes are gone ~SHIMMER_MS+SPARK_LIFE after release).\r\nfunction shimmerPoint({pts,releasedAt},now){\r\n const k=(now-releasedAt)/SHIMMER_MS;if(k<0||k>1)return null;\r\n let i=0,d=0;\r\n while(i<pts.length-1&&releasedAt-pts[i].t>=TRAIL_FADE_MS){d+=Math.hypot(pts[i+1].x-pts[i].x,pts[i+1].y-pts[i].y);i++;}\r\n let left=k*pathLength(pts.slice(i));d+=left;\r\n for(;i<pts.length-1;i++){\r\n  const a=pts[i],b=pts[i+1],seg=Math.hypot(b.x-a.x,b.y-a.y);\r\n  if(seg>=left){const f=seg?left/seg:0;return{x:a.x+(b.x-a.x)*f,y:a.y+(b.y-a.y)*f,d,k};}\r\n  left-=seg;\r\n }\r\n return{x:pts[i].x,y:pts[i].y,d,k};\r\n}\r\nfunction shimmerDust(entry,now){const s=shimmerPoint(entry,now);if(s){spawnSpark(s.x,s.y,now);spawnSpark(s.x,s.y,now);}return s;}\r\nfunction drawShimmer(entry,now){\r\n const s=shimmerDust(entry,now);if(s)drawFlare(s.x,s.y,trailColor(s.d,now),1-.4*s.k,now*2,.7);\r\n}\r\nfunction renderPlainTrail(pts,now){\r\n const trail=pts.filter(pt=>now-pt.t<TRAIL_FADE_MS).map(pt=>[pt.x,pt.y]);\r\n strokeGlow(trail,'#d8f6ff',1,4);\r\n const last=pts.at(-1);if(last&&now-last.t<TRAIL_FADE_MS)touchDot(last.x,last.y,'#d8f6ff');\r\n}\r\n// Test-only trail probe: exposed as myr5Portal.trailProbe only when a test sets window.__portalTrailProbe\r\n// before mount (the app never does). It drives the trail renderer directly, skipping the cloth-board press\r\n// that makes synthetic drags slower than the fade in headless runs, so frames show the trail at full strength.\r\nconst PROBE_ID=-1;\r\nlet probeFrozen=false;\r\nconst trailProbe={\r\n // Draws one frozen frame of `xy` ([[x,y],…]) swept over durationMs ending now (or releasedAgoMs ago), with\r\n // the dust those frames would have shed replayed at 60fps. Returns how many motes are alive. dust:false\r\n // draws the ribbon and tip alone, so a cross-section measures the strokes without motes drifting across it.\r\n draw(xy,{durationMs=350,releasedAgoMs=null,dust=true}={}){\r\n  cancelAnimationFrame(rafId);rafId=0;probeFrozen=true;clearTimeout(idleTimer);idleCycle=null;\r\n  const now=performance.now(),end=now-(releasedAgoMs??0),n=xy.length;\r\n  const pts=xy.map(([x,y],i)=>({x,y,t:end-durationMs*(1-i/Math.max(1,n-1))}));\r\n  sparkBorn.fill(-1e9);\r\n  if(!dust){ctx.save();ctx.setTransform(1,0,0,1,0,0);ctx.clearRect(0,0,overlay.width,overlay.height);ctx.restore();renderRibbon(pts,now,false);return 0;}\r\n  for(let ft=pts[0].t;ft<end;ft+=1000/60)shedSparks(pts.filter(p=>p.t<=ft),ft);\r\n  pointers.delete(PROBE_ID);fading.length=0;\r\n  if(releasedAgoMs==null)pointers.set(PROBE_ID,{pts,norm:[]});\r\n  else{const entry={pts,releasedAt:end};fading.push(entry);for(let ft=end;ft<now;ft+=1000/60)shimmerDust(entry,ft);}\r\n  drawFrame(0,now);\r\n  let alive=0;for(let i=0;i<SPARK_N;i++)if(now-sparkBorn[i]<SPARK_LIFE)alive++;\r\n  return alive;\r\n },\r\n // Live strokes (fps check): down/move/up like a finger, minus the cloth press and shape matching.\r\n down(x,y){probeFrozen=false;pointers.set(PROBE_ID,{pts:[{x,y,t:performance.now()}],norm:[]});scheduleIdle();kickRender();},\r\n move(x,y){pointers.get(PROBE_ID)?.pts.push({x,y,t:performance.now()});},\r\n up(){const p=pointers.get(PROBE_ID);pointers.delete(PROBE_ID);if(p?.pts.length>1)fading.push({pts:p.pts,releasedAt:performance.now()});scheduleIdle();kickRender();},\r\n resume(){probeFrozen=false;pointers.delete(PROBE_ID);fading.length=0;kickRender();},\r\n};\r\n\r\n// #104 idle ambient flash: which id is showing right now, and how strongly, given the time since the cycle began.\r\nfunction idleFrame(now){\r\n const elapsed=now-idleCycle.t0,dur=IDLE.slowMs,slot=dur+IDLE.gapMs,t=elapsed%slot;\r\n if(t>=dur)return null; // #133: the gap between shapes\r\n return{id:IDLE_ORDER[Math.floor(elapsed/slot)%IDLE_ORDER.length],alpha:Math.max(.12,.5*Math.min(1,t/60,(dur-t)/60))};\r\n}\r\n// Outline + label (+ arrow for a line) for one idle-flash entry; `rect` is the stitched-pattern rect.\r\nfunction idleShapeInfo(id,rect){\r\n if(id==='cross'){\r\n  const polys=SHAPES.cross.map(p=>toClientPts(p.points,rect));\r\n  return{polys,color:'#ffffff',label:MENUS['line-up'].label,labelPt:[rect.left+rect.width/2,rect.top+rect.height/2],arrow:null};\r\n }\r\n const menu=MENUS[id];\r\n if(id==='x'){\r\n  const polys=SHAPES.x.map(p=>toClientPts(p.points,rect));\r\n  return{polys,color:menu.color,label:menu.label,labelPt:polys[0][0],arrow:null};\r\n }\r\n if(LINE_IDS.has(id)){\r\n  const[a,b]=toClientPts(lineTemplatePts(id),rect),reversed=id==='line-rl'||id==='line-up',start=reversed?b:a,end=reversed?a:b;\r\n  return{polys:[[a,b]],color:menu.color,label:menu.label,labelPt:start,arrow:{from:start,to:end}};\r\n }\r\n const pts=shapeClipPts(id,rect);\r\n return{polys:[pts],color:menu.color,label:menu.label,labelPt:pts[0],arrow:null};\r\n}\r\nfunction drawArrow(from,to,color,alpha){\r\n const mx=(from[0]+to[0])/2,my=(from[1]+to[1])/2,ang=Math.atan2(to[1]-from[1],to[0]-from[0]),len=10;\r\n ctx.save();ctx.globalAlpha=alpha;ctx.strokeStyle=color;ctx.lineWidth=2;ctx.lineCap='round';ctx.shadowColor=color;ctx.shadowBlur=6;\r\n ctx.translate(mx,my);ctx.rotate(ang);\r\n ctx.beginPath();ctx.moveTo(-len,0);ctx.lineTo(len,0);ctx.moveTo(len-6,-5);ctx.lineTo(len,0);ctx.lineTo(len-6,5);ctx.stroke();\r\n ctx.restore();\r\n}\r\n// A bold, pill-backed label near the shape's start point, clear of the stroke and clamped inside the\r\n// board face with a 12px margin (conductor review 2026-09-23: the old plain small text ran off-board in\r\n// a corner). Flips to whichever side keeps it fully on-board rather than clipping.\r\nfunction drawLabel(text,[x,y],color,alpha,rect){\r\n ctx.save();ctx.font='700 15px system-ui,sans-serif';ctx.textBaseline='middle';\r\n const padX=9,padY=6,h=15+padY*2,w=ctx.measureText(text).width+padX*2,margin=12;\r\n const face=rect||fallbackRect();\r\n let lx=x+12,ly=y-14;\r\n if(lx+w>face.left+face.width-margin)lx=x-12-w;\r\n lx=Math.min(Math.max(lx,face.left+margin),face.left+face.width-margin-w);\r\n ly=Math.min(Math.max(ly,face.top+margin+h/2),face.top+face.height-margin-h/2);\r\n ctx.globalAlpha=alpha;\r\n ctx.fillStyle='rgba(8,5,14,.75)';\r\n ctx.beginPath();ctx.roundRect(lx,ly-h/2,w,h,h/2);ctx.fill();\r\n ctx.fillStyle=color;ctx.shadowColor=color;ctx.shadowBlur=6;\r\n ctx.fillText(text,lx+padX,ly+1);\r\n ctx.restore();\r\n}\r\nfunction drawIdleShape({polys,color,label,labelPt,arrow},alpha,width,rect,drawRouteLabel=true){\r\n polys.forEach(p=>strokeGlow(p,color,alpha,width));\r\n if(arrow)drawArrow(arrow.from,arrow.to,color,alpha);\r\n if(drawRouteLabel)drawLabel(label,labelPt,color,alpha,rect);\r\n}\r\nfunction drawIdle(now){\r\n const rect=board?board.patternRect():fallbackRect(),face=board?board.faceRect():rect;\r\n if(idleCycle.static){IDLE_ORDER.forEach(id=>drawIdleShape(idleShapeInfo(id,rect),.35,3,face,false));return;}\r\n const f=idleFrame(now);\r\n if(f)drawIdleShape(idleShapeInfo(f.id,rect),f.alpha,3,face);\r\n}\r\n\r\n// #22 first-run hint: a glowing fingertip traces the stitched square once, labelled, the first time the\r\n// quilt shows (never again once seen). localStorage is wrapped in try/catch — sandboxed frames and\r\n// private-mode Safari throw, and a blocked flag must never crash the hint (or nag every visit: on error we\r\n// act as if it's already been seen).\r\nfunction hintSeen(){try{return localStorage.getItem(HINT_KEY)==='1';}catch{return true;}}\r\nfunction markHintSeen(){try{localStorage.setItem(HINT_KEY,'1');}catch{}}\r\n// Points on a closed polyline from its start up to `frac` of the way around (arc-length, not index).\r\nfunction tracedPrefix(pts,frac){\r\n const segs=[];let total=0;\r\n for(let i=0;i<pts.length-1;i++){total+=segs[i]=Math.hypot(pts[i+1][0]-pts[i][0],pts[i+1][1]-pts[i][1]);}\r\n let left=frac*total,out=[pts[0]];\r\n for(let i=0;i<segs.length;i++){\r\n  if(segs[i]>=left){const f=segs[i]?left/segs[i]:0;const[ax,ay]=pts[i],[bx,by]=pts[i+1];out.push([ax+(bx-ax)*f,ay+(by-ay)*f]);break;}\r\n  left-=segs[i];out.push(pts[i+1]);\r\n }\r\n return out;\r\n}\r\n// Coordinates with idle flashing (COMMON): idleEligible() also checks `!hint` so the two never overlap;\r\n// starting/stopping the hint just re-runs scheduleIdle() to pick that up, without touching idle's own state.\r\nfunction maybeStartHint(){\r\n // Ian 2026-09-23: \"the magical finger is only for the quilt\" — same board-scoping as the trail above.\r\n if(hint||idleCycle||hintSeen()||boardId!=='quilt')return;\r\n hint={t0:performance.now()};\r\n scheduleIdle();kickRender();\r\n}\r\nfunction stopHint(){\r\n if(!hint)return;\r\n hint=null;\r\n scheduleIdle();\r\n}\r\nfunction drawHint(now){\r\n const rect=board?board.patternRect():fallbackRect(),face=board?board.faceRect():rect;\r\n const elapsed=now-hint.t0;\r\n if(elapsed>=HINT_MS){stopHint();return;} // one lap done: clear it through stopHint so idle re-arms too\r\n const pts=shapeClipPts('rect',rect),reduced=prefersReducedMotion();\r\n strokeGlow(pts,'#ffffff',.18,3);\r\n const prefix=reduced?pts:tracedPrefix(pts,elapsed/HINT_MS);\r\n strokeGlow(prefix,'#ffffff',.9,4);\r\n const tip=prefix[prefix.length-1];touchDot(tip[0],tip[1],'#ffffff');\r\n drawLabel('Trace to start your workout',pts[0],'#ffffff',.85,face);\r\n}\r\n\r\n// #20 tap as well as trace: nearest tappable shape outline to a client-px point, or null past TAP_HIT_PX.\r\n// Point-to-segment distance over each candidate's own closed outline (shapeClipPts), not nearest vertex, so\r\n// a 5-corner rect/diamond still hit-tests accurately along its straight edges.\r\nfunction distToPolyline(pt,pts){\r\n let best=Infinity;\r\n for(let i=0;i<pts.length-1;i++){\r\n  const[ax,ay]=pts[i],[bx,by]=pts[i+1],dx=bx-ax,dy=by-ay,len2=dx*dx+dy*dy;\r\n  const t=len2?Math.max(0,Math.min(1,((pt[0]-ax)*dx+(pt[1]-ay)*dy)/len2)):0;\r\n  best=Math.min(best,Math.hypot(pt[0]-(ax+dx*t),pt[1]-(ay+dy*t)));\r\n }\r\n return best;\r\n}\r\nfunction nearestTapShape(x,y){\r\n const rect=board?board.patternRect():fallbackRect();\r\n let bestId=null,bestDist=TAP_HIT_PX;\r\n for(const id of TAPPABLE_IDS){\r\n  const d=distToPolyline([x,y],shapeClipPts(id,rect));\r\n  if(d<=bestDist){bestDist=d;bestId=id;}\r\n }\r\n return bestId;\r\n}\r\n\r\n// now: the trail probe draws a frozen frame at a chosen time (rAF's own timestamp arrives first and is ignored).\r\nfunction drawFrame(_,now=performance.now()){\r\n // #104 risk 4: eligibility (no open dialog, tab visible, etc.) is otherwise only rechecked when the\r\n // cycle arms/starts, so a dialog opened over a running cycle (setup gate, a reward reveal) would keep\r\n // flashing underneath it. Catch that on the very next drawn frame instead.\r\n if(idleCycle&&!idleEligible())scheduleIdle();\r\n rafId=0;\r\n ctx.save();ctx.setTransform(1,0,0,1,0,0);ctx.clearRect(0,0,overlay.width,overlay.height);ctx.restore();\r\n mapOverlayClientSpace();\r\n if(outlineFlash){\r\n  const dur=outlineFlash.duration||350,t=now-outlineFlash.start;\r\n  if(t<dur){\r\n   const alpha=1-t/dur;\r\n   outlineFlash.polys.forEach(p=>strokeGlow(p,outlineFlash.color,alpha,4));\r\n   if(outlineFlash.label)drawLabel(outlineFlash.label,outlineFlash.labelPt,outlineFlash.color,Math.min(.6,alpha),board?board.faceRect():fallbackRect());\r\n  }else outlineFlash=null;\r\n }\r\n if(idleCycle)drawIdle(now);\r\n // Any active pointer — a real touch (wirePointerEvents' own pointerdown already calls stopHint()\r\n // synchronously, before this ever runs) or the test-only trailProbe's simulated one — ends the hint for\r\n // good; probeFrozen means trailProbe wants a clean frame with nothing else drawn on it.\r\n if(hint){if(pointers.size)stopHint();else if(!probeFrozen)drawHint(now);}\r\n if(phase?.pulse&&phase.pts)strokeGlow(phase.pts,phase.color,.4+.25*Math.sin((now-phase.t0)/280),3); // soft breathing outline while loading\r\n for(let i=fading.length-1;i>=0;i--)if(now-fading[i].pts.at(-1).t>=TRAIL_FADE_MS)fading.splice(i,1);\r\n const reduced=prefersReducedMotion();\r\n // Ian 2026-09-23: \"the magical finger is only for the quilt\" — other boards (ice, grass, cogs, jelly,\r\n // wood) bring their own touch effects, so the trail only draws while the mounted board is the quilt.\r\n if(boardId==='quilt'){\r\n  for(const p of pointers.values())reduced?renderPlainTrail(p.pts,now):renderRibbon(p.pts,now,true);\r\n  for(const entry of fading){\r\n   if(reduced){renderPlainTrail(entry.pts,now);continue;}\r\n   renderRibbon(entry.pts,now,false);drawShimmer(entry,now); // #105: a shimmer ripples along the path as it fades\r\n  }\r\n  if(!reduced)drawSparks(now);\r\n }\r\n if(pointers.size||fading.length||outlineFlash||(idleCycle&&!idleCycle.static)||(phase?.pulse)||(!reduced&&sparksAlive(now))||hint)kickRender();\r\n}\r\n// opts (#21 \"almost\"): {label,labelPt,duration} — a plain match flash stays the original quick 350ms\r\n// outline-only pulse; an \"almost\" flash carries a fading label and runs ~1.2s (ALMOST_MS below).\r\nfunction flashOutline(polys,color,opts){if(prefersReducedMotion())return;outlineFlash={polys,color,start:performance.now(),...opts};kickRender();}\r\n\r\n// #104: arms/disarms the idle cycle from every place eligibility can change (touch, sequence start/end,\r\n// show/hide, tab visibility). Always safe to call — it's a no-op when nothing needs to change.\r\nfunction idleEligible(){return !['pixel','paper','clay','mache'].includes(boardId)&&boardShown&&!busy&&!hint&&pointers.size===0&&!document.hidden&&!document.querySelector('dialog[open]');}\r\nfunction scheduleIdle(){\r\n clearTimeout(idleTimer);idleTimer=0;\r\n if(idleCycle){idleCycle=null;kickRender();} // clears the drawn hint on the next frame\r\n if(idleEligible())idleTimer=setTimeout(beginIdleCycle,IDLE.armMs);\r\n}\r\nfunction beginIdleCycle(){\r\n idleTimer=0;\r\n if(!idleEligible())return;\r\n idleCycle=prefersReducedMotion()?{static:true}:{t0:performance.now()};\r\n kickRender();\r\n}\r\n\r\nfunction fallInAll([cx,cy]){\r\n const menus=Object.values(MENUS),n=menus.length,R=90;\r\n const els=menus.map((m,i)=>{\r\n  const a=(i/n)*Math.PI*2,el=document.createElement('div');el.className='portal-object';\r\n  el.style.left=(cx+Math.cos(a)*R)+'px';el.style.top=(cy+Math.sin(a)*R)+'px';\r\n  el.style.setProperty('--glow',m.color);el.innerHTML=m.icon;objectsLayer.append(el);return el;\r\n });\r\n return new Promise(resolve=>{\r\n  if(prefersReducedMotion()){els.forEach(el=>el.remove());resolve();return;}\r\n  settle().then(()=>{els.forEach(el=>{el.style.left=cx+'px';el.style.top=cy+'px';el.classList.add('falling');});setTimeout(()=>{els.forEach(el=>el.remove());resolve();},600);});\r\n });\r\n}\r\n// Neon wormhole under a liquid-glass slab (Ian 2026-09-23: \"a mix of colours with the apple glass effect\",\r\n// \"a wormhole with endless depth\"). One raw WebGL2 triangle fills a canvas the size of the glass box (the cut's\r\n// bounds + bleed): polar tunnel around the centroid, the six neons streaming inward as twisted rings with the\r\n// shape's colour leading, a glowing core and depth fog. The glass comes from a per-shape lens map: refraction\r\n// that bends the tunnel near the rim, chromatic fringe, a bright specular rim on the lit (top-left) edge, a softer\r\n// inner edge on the far side, frosting with boosted saturation and a light sweep.\r\nconst NEONS=['#ff5f1f','#ffff33','#39ff14','#1f51ff','#b026ff','#ff10f0']; // Ian's six, in hue order so neighbours mix clean\r\n// Ring colours down the tunnel: the shape's colour on about 40% of the rings, the other neons between; the cross is an even rainbow.\r\nexport function ringColours(color,all=false){\r\n if(all)return NEONS;\r\n const seq=[];NEONS.filter(c=>c!==color).forEach((c,i)=>{if(i%3!==2)seq.push(color);seq.push(c);});return seq;\r\n}\r\n// Lens map, one RGBA texel per GLASS.mapPx: rg = outward normal of the nearest rim edge, b = signed distance to the\r\n// rim (.5 on it, 1 at GLASS.bevel inside, 0 at GLASS.bevel outside). poly: closed, box-local CSS px.\r\nexport function lensMap(poly,w,h,px=GLASS.mapPx,bevel=GLASS.bevel){\r\n const mw=Math.max(1,Math.ceil(w/px)),mh=Math.max(1,Math.ceil(h/px)),data=new Uint8Array(mw*mh*4),P=Float64Array.from(poly.flat()),n=P.length-2;\r\n for(let j=0;j<mh;j++)for(let i=0;i<mw;i++){\r\n  const x=(i+.5)*px,y=(j+.5)*px;let best=Infinity,nx=0,ny=0,inside=false;\r\n  for(let k=0;k<n;k+=2){ // nearest point on each edge + even-odd crossing, in one pass (runs once per glass, at the cut)\r\n   const ax=P[k],ay=P[k+1],bx=P[k+2],by=P[k+3],dx=bx-ax,dy=by-ay,t=Math.max(0,Math.min(1,((x-ax)*dx+(y-ay)*dy)/(dx*dx+dy*dy||1))),qx=ax+t*dx-x,qy=ay+t*dy-y,d=qx*qx+qy*qy;\r\n   if(d<best){best=d;nx=qx;ny=qy;}\r\n   if((ay>y)!==(by>y)&&x<dx*(y-ay)/dy+ax)inside=!inside;\r\n  }\r\n  const d=Math.sqrt(best)||1,s=inside?1:-1,o=4*(j*mw+i);\r\n  data[o]=128+127*s*nx/d;data[o+1]=128+127*s*ny/d;data[o+2]=255*Math.min(1,Math.max(0,.5+s*d/(2*bevel)));data[o+3]=255;\r\n }\r\n return {data,mw,mh};\r\n}\r\nconst TUNNEL_VS='#version 300 es\\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0,1);}';\r\nconst tunnelFragment=(material='')=>`#version 300 es\r\nprecision highp float;\r\nuniform vec2 uRes,uC,uLP;uniform float uR,uT,uSpin,uSweep,uLens,uBevel,uPr,uFringe,uMag,uN;uniform vec3 uSeq[12],uCore,uTint;uniform float uBlur;uniform sampler2D uMap;uniform sampler2D uPeek;uniform float uPeekOn;out vec4 o;\r\nvec3 seq(float i){return uSeq[int(mod(i,uN))];}\r\nvec3 acc(float i){return uSeq[int(uN)+int(mod(i,2.))];} // accent colours sit after the ring colours (up to 10), so uSeq has 12 slots\r\n${material}\r\nvec3 tunnel(vec2 p,float fz){\r\n float aa=1.-smoothstep(.25,.9,fz); // fade ring detail that gets finer than a pixel\r\n vec2 d=(p-uC)/uR;float r=max(length(d),1e-3),z=3.4/r,a=atan(d.y,d.x)+uSpin+.12*z;\r\n float v=z-uT+.35*sin(a*3.+z*.5)*aa,i=floor(v),f=v-i;         // wavy ring edges: the colours flow, not a target\r\n vec3 c=mix(seq(i),seq(i+1.),smoothstep(.65,1.,f));\r\n c*=mix(.85,.55+.45*(.5+.5*sin(a*7.+v*6.2832)),aa); // twisted streaks\r\n c+=max(pow(f,12.),1.-smoothstep(0.,1.5*fz,f))*aa*(c*.8+.35); // bright leading edge of each ring, antialiased across the wrap\r\n${material ? ' c=material(a,v,z,r,aa,c);' : ''}\r\n c=mix(c,uCore,smoothstep(7.,40.,z));            // depth fog: the far end glows\r\n c+=uCore*exp(-r*16.)*1.1;                        // bright core at the vanishing point\r\n return c*mix(1.,.78,smoothstep(1.,1.8,r));      // walls dim a little toward the opening (more turns neon yellow olive)\r\n}\r\nvoid main(){\r\n vec2 p0=vec2(gl_FragCoord.x,uRes.y-gl_FragCoord.y);vec4 m=texture(uMap,p0/uRes);\r\n vec2 n=m.rg*2.-1.;n/=max(length(n),1e-3);\r\n float sd=(m.b-.5)*2.*uBevel,e=clamp(sd/uBevel,0.,1.),bend=(1.-e)*(1.-e);\r\n vec2 p=uC+(p0-uC)/(1.+uMag*e);                  // #125: the flat middle magnifies a little, like a lens\r\n float fz=fwidth(3.4*uR/max(length(p-uC),1e-3)); // ring depth change per pixel\r\n vec2 off=n*uLens*bend;                           // refraction: the thick bevel shows the tunnel from further out\r\n vec3 c=uFringe>0.&&bend>.02?vec3(tunnel(p+off*(1.-uFringe),fz).r,tunnel(p+off,fz).g,tunnel(p+off*(1.+uFringe),fz).b):tunnel(p+off,fz); // dispersion: blue bends furthest\r\n if(uBlur>0.)c=mix(c,tunnel(uC+(p+off-uC)*1.016,fz),uBlur); // short radial exposure\r\n if(uPeekOn>0.){vec2 q=(p+off)/uRes;c=mix(c,pow(texture(uPeek,vec2(q.x,1.-q.y)).rgb,vec3(.4545)),uPeekOn*(1.-smoothstep(.3,.75,length((p0-uC)/uRes)*2.))*step(0.,sd));} // PORTAL-PEEK: the destination in the centre, the wormhole at the rim\r\n float l=dot(c,vec3(.299,.587,.114));c=mix(vec3(l),c,1.45)*.9+.07; // frosted: lifted, saturation boosted\r\n c=mix(c,vec3(l)*.55+.42,.2*bend);                // frost in the bevel band only: a milky thick edge against the clear middle\r\n vec2 L=normalize(uLP-p0);float lit=max(dot(n,L),0.),far=max(-dot(n,L),0.); // lit from the finger (or the top-left)\r\n c+=(1.-smoothstep(0.,4.*uPr,abs(sd)))*(1.9*pow(lit,1.4)+.5*pow(far,3.)); // bright specular rim facing the light, faint glint opposite\r\n c+=bend*(1.-e)*.3*(.5+.5*lit);                   // Fresnel: the whole bevel brightens toward the rim\r\n float inner=(1.-smoothstep(0.,28.*uPr,sd))*step(0.,sd);\r\n c=mix(c,c*.45+.08,inner*far*.8)+inner*inner*lit*.4; // darker inner edge on the far side, glow inside the lit edge\r\n c+=.1*(1.-smoothstep(0.,.75,length((p0-uLP)/uRes*.6)))*step(0.,sd); // broad glare toward the light\r\n if(sd<0.)c*=.55;                                 // beyond the rim, under the cloth's ragged edge\r\n float s=dot(p0/length(uRes),normalize(vec2(1.,.45)))-uSweep;c+=exp(-s*s*160.)*.2*step(0.,sd); // light sweep\r\n o=vec4(c+(fract(sin(dot(p0,vec2(12.9898,78.233)))*43758.5453)-.5)/255.,1);\r\n}`;\r\n// One WebGL2 context for the portal's life, created on the first glass and reused; null when unavailable (CSS glass then).\r\nlet tunnel=null;\r\nfunction tunnelGL(material='',{activate=true}={}) {\r\n const uniforms=['uRes','uC','uLP','uR','uT','uSpin','uSweep','uLens','uBevel','uPr','uFringe','uMag','uN','uSeq','uCore','uTint','uBlur','uPeek','uPeekOn'];\r\n if(!tunnel){\r\n  const canvas=document.createElement('canvas'),gl=canvas.getContext('webgl2',{alpha:false,antialias:false,depth:false,stencil:false,powerPreference:'low-power'});\r\n  if(!gl)return null;\r\n  const owner={canvas,gl,programs:new Map(),texture:gl.createTexture(),active:null};tunnel=owner;\r\n  gl.bindTexture(gl.TEXTURE_2D,owner.texture);\r\n  for(const k of [gl.TEXTURE_WRAP_S,gl.TEXTURE_WRAP_T])gl.texParameteri(gl.TEXTURE_2D,k,gl.CLAMP_TO_EDGE);\r\n  for(const k of [gl.TEXTURE_MIN_FILTER,gl.TEXTURE_MAG_FILTER])gl.texParameteri(gl.TEXTURE_2D,k,gl.LINEAR);\r\n  canvas.addEventListener('webglcontextlost',()=>{\r\n   owner.programs.clear();if(tunnel===owner)tunnel=null;\r\n   canvas.parentNode?.classList.remove('gl');canvas.remove();\r\n  });\r\n }\r\n const owner=tunnel,{gl,canvas}=owner;\r\n let selected=owner.programs.get(material),compiled=false;\r\n if(!selected){\r\n  const vs=gl.createShader(gl.VERTEX_SHADER),fs=gl.createShader(gl.FRAGMENT_SHADER),program=gl.createProgram();\r\n  const compile=(shader,source)=>{gl.shaderSource(shader,source);gl.compileShader(shader);return gl.getShaderParameter(shader,gl.COMPILE_STATUS);};\r\n  const valid=compile(vs,TUNNEL_VS)&&compile(fs,tunnelFragment(material));\r\n  if(!valid){gl.deleteShader(vs);gl.deleteShader(fs);gl.deleteProgram(program);return null;}\r\n  gl.attachShader(program,vs);gl.attachShader(program,fs);gl.linkProgram(program);gl.deleteShader(vs);gl.deleteShader(fs);\r\n  if(!gl.getProgramParameter(program,gl.LINK_STATUS)){gl.deleteProgram(program);return null;}\r\n  const u={};for(const k of uniforms)u[k]=gl.getUniformLocation(program,k);\r\n  selected={program,u};owner.programs.set(material,selected);compiled=true;\r\n  // Warm each new linked program once so the first full-size frame doesn't pay lazy driver setup cost.\r\n  // Restore the prior program: a deferred cache fill can run while another material owns a live phase.\r\n  if(owner.programs.size===1)canvas.width=canvas.height=1;\r\n  const previous=gl.getParameter(gl.CURRENT_PROGRAM),previousViewport=gl.getParameter(gl.VIEWPORT);\r\n  gl.useProgram(program);gl.bindTexture(gl.TEXTURE_2D,owner.texture);gl.viewport(0,0,1,1);gl.drawArrays(gl.TRIANGLES,0,3);\r\n  gl.readPixels(0,0,1,1,gl.RGBA,gl.UNSIGNED_BYTE,new Uint8Array(4));gl.useProgram(previous);gl.viewport(...previousViewport);\r\n }\r\n if(activate){gl.useProgram(selected.program);owner.active=selected;owner.u=selected.u;}\r\n return owner;\r\n}\r\n// A secret handoff immediately allocates the Vault's own renderer. Drop the portal's offscreen\r\n// WebGL2 context first; hidden canvases still count against mobile GPU context/memory budgets.\r\nasync function releaseTunnel(){\r\n const owner=tunnel;if(!owner)return;\r\n tunnel=null;\r\n const {gl,canvas}=owner;\r\n owner.programs.clear();owner.active=null;\r\n try{if(owner.texture)gl.deleteTexture(owner.texture);}catch{}\r\n owner.texture=null;\r\n await releaseGraphics(canvas,gl,()=>gl.getExtension('WEBGL_lose_context')?.loseContext());\r\n canvas.remove();\r\n}\r\nfunction stopWormhole(){\r\n if(!wormhole)return;\r\n const current=wormhole;wormhole=null;\r\n current.ph.stop?.();current.el.remove();\r\n}\r\nconst rgb=hex=>[1,3,5].map(i=>parseInt(hex.slice(i,i+2),16)/255);\r\nconst easeInOut=t=>t<=0?0:t>=1?1:t*t*(3-2*t);\r\n// PORTAL-PEEK (R17, on by default; localStorage.myr5PortalPeek=\"0\" is the kill switch): the destination rendered offscreen (portal-peek.mjs)\r\n// and sampled in the wormhole's centre on texture unit 1. Loads after the wormhole starts; fades in once ready; any\r\n// failure, a lost context or the lite fallback leaves the plain wormhole.\r\nfunction startPeek(ph,{gl,active:{program,u}},w,h){\r\n let flag=null;try{flag=localStorage.getItem('myr5PortalPeek');}catch{}\r\n if(!ph.peek||flag==='0')return null;\r\n const s={peek:null,tex:null,on:0,n:0,dead:false};\r\n import('./portal-peek.mjs').then(m=>m.openPeek(ph.peek,w,h,{shouldOpen:()=>!s.dead&&!gl.isContextLost()})).then(peek=>{\r\n  if(!peek)return;if(s.dead||gl.isContextLost())return peek.dispose();\r\n  s.peek=peek;s.tex=gl.createTexture();gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,s.tex);\r\n  for(const k of [gl.TEXTURE_WRAP_S,gl.TEXTURE_WRAP_T])gl.texParameteri(gl.TEXTURE_2D,k,gl.CLAMP_TO_EDGE);\r\n  for(const k of [gl.TEXTURE_MIN_FILTER,gl.TEXTURE_MAG_FILTER])gl.texParameteri(gl.TEXTURE_2D,k,gl.LINEAR);\r\n  gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,peek.width,peek.height,0,gl.RGBA,gl.UNSIGNED_BYTE,null);gl.activeTexture(gl.TEXTURE0);\r\n }).catch(()=>{});\r\n s.frame=(dt,now)=>{\r\n  if(!s.peek)return;if(s.peek.lost()){s.stop();gl.useProgram(program);gl.uniform1f(u.uPeekOn,0);return;}\r\n  if(s.n++&1)return; // ponytail: half rate (each read stalls the GPU); go every frame if phones show judder\r\n  s.peek.render(now);const px=s.peek.read();\r\n  gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,s.tex);gl.texSubImage2D(gl.TEXTURE_2D,0,0,0,s.peek.width,s.peek.height,gl.RGBA,gl.UNSIGNED_BYTE,px);gl.activeTexture(gl.TEXTURE0);\r\n  s.on=Math.min(1,s.on+dt*5);gl.useProgram(program);gl.uniform1f(u.uPeekOn,s.on);\r\n };\r\n s.stop=()=>{s.dead=true;s.peek?.dispose();s.peek=null;if(s.tex)gl.deleteTexture(s.tex);s.tex=null;};\r\n return s;\r\n}\r\n// Runs the wormhole for one glass phase: speed ramps up across the cut + loading phase, the vanishing point drifts\r\n// toward the finger (and with device tilt where that needs no permission prompt). Reduced motion uses static CSS glass.\r\n// ph.stop() freezes it (at the reveal) and endPhase() always stops it.\r\nasync function startTunnel(ph,poly,color,all){\r\n const capturedBoardId=boardId,capturedSignal=lifecycle?.signal;\r\n let cancelled=false,raf=0,tiltOk=false,onTilt=null;\r\n // Install cancellation before any optional material import can yield. Standalone wormholes have their own phase object.\r\n ph.stop=()=>{cancelled=true;cancelAnimationFrame(raf);if(tiltOk&&onTilt)removeEventListener('deviceorientation',onTilt);};\r\n const palette=paletteFor(capturedBoardId,store.get(paletteKey(capturedBoardId)));\r\n const seq=palette?.colors||ringColours(color,all),core=palette?.core||color;\r\n ph.glass.dataset.tunnel=capturedBoardId;ph.glass.style.setProperty('--tint',boardTint(capturedBoardId));\r\n const [seq0,...seqN]=seq;ph.glass.style.setProperty('--seq0',seq0);ph.glass.style.setProperty('--seq',seqN.join(','));\r\n if(prefersReducedMotion())return; // CSS uses the selected sequence without creating WebGL under reduced motion.\r\n let material='';\r\n if(palette){\r\n  let pending=materialLoads.get(capturedBoardId);\r\n  if(!pending){pending=import(`./portal-tunnel-${capturedBoardId}.mjs`).then(m=>typeof m.material==='string'?m.material:'').catch(()=> '');materialLoads.set(capturedBoardId,pending);}\r\n  material=await Promise.race([pending,sleep(900).then(()=> '')]);\r\n }\r\n if(cancelled||!ph.glass.isConnected||capturedSignal?.aborted)return;\r\n const t=tunnelGL(material);if(!t)return;\r\n const {gl,canvas}=t,{program,u}=t.active,{left,top,w,h}=ph.box,reduced=prefersReducedMotion();\r\n const bindPhaseProgram=()=>gl.useProgram(program);\r\n bindPhaseProgram();\r\n gl.bindTexture(gl.TEXTURE_2D,t.texture);\r\n const tex=lensMap((ph.fullscreen?poly:bleedPts(poly,-GLASS.rimInset)).map(([x,y])=>[x-left,y-top]),w,h);\r\n gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,tex.mw,tex.mh,0,gl.RGBA,gl.UNSIGNED_BYTE,tex.data);\r\n const flat=new Float32Array(36);seq.forEach((c,i)=>flat.set(rgb(c),3*i));(palette?.accents||[seq[0],seq[0]]).forEach((c,i)=>flat.set(rgb(c),3*(seq.length+i)));gl.uniform3fv(u.uSeq,flat);gl.uniform1f(u.uN,seq.length);\r\n gl.uniform3fv(u.uTint,rgb(boardTint(capturedBoardId)));\r\n gl.uniform3fv(u.uCore,rgb(core).map(v=>v*.5+.5));\r\n const [cx,cy]=centroidOf(poly).map((v,i)=>v-(i?top:left)),xs=poly.map(p=>p[0]),ys=poly.map(p=>p[1]),R=.5*Math.min(Math.max(...xs)-Math.min(...xs),Math.max(...ys)-Math.min(...ys));\r\n // ponytail: hardwareConcurrency is a coarse low-end hint (and capped on some browsers); the measured-frame check below is the real guard.\r\n let pr=Math.min(devicePixelRatio||1,(navigator.hardwareConcurrency||8)<=4?1:1.5),lite=false,travel=.3,last=ph.t0,tilt=null;\r\n const par=[0,0],slow=[],rest=[-.35*w,-.55*h],light=[...rest]; // #125 the specular follows the finger, else sits up-left (and leans with tilt)\r\n const size=()=>{\r\n  bindPhaseProgram();\r\n  canvas.width=Math.max(1,Math.round(w*pr));canvas.height=Math.max(1,Math.round(h*pr));gl.viewport(0,0,canvas.width,canvas.height);\r\n  gl.uniform2f(u.uRes,canvas.width,canvas.height);gl.uniform1f(u.uPr,pr);gl.uniform1f(u.uR,Math.max(R,1)*pr);gl.uniform1f(u.uLens,GLASS.bend*pr);gl.uniform1f(u.uBevel,GLASS.bevel*pr);gl.uniform1f(u.uFringe,lite||reduced?0:GLASS.fringe);gl.uniform1f(u.uMag,GLASS.magnify);gl.uniform1f(u.uBlur,lite||reduced?0:.18);\r\n  gl.uniform1i(u.uPeek,1);gl.uniform1f(u.uPeekOn,0); // PORTAL-PEEK\r\n };\r\n const draw=now=>{\r\n  bindPhaseProgram();\r\n  const age=now-ph.t0;\r\n  gl.uniform1f(u.uT,travel);gl.uniform1f(u.uSpin,reduced?0:(age*.000225)%(2*Math.PI));gl.uniform1f(u.uSweep,reduced?.45:-.35+1.7*((age/3200)%1));\r\n  gl.uniform2f(u.uC,(cx+par[0])*pr,(cy+par[1])*pr);gl.uniform2f(u.uLP,light[0]*pr,light[1]*pr);gl.drawArrays(gl.TRIANGLES,0,3);\r\n };\r\n const look=createTiltLook();onTilt=e=>{const t=look.read(e,performance.now());if(t)tilt=[-t.x*R*.2,-t.y*R*.2];};\r\n const frame=now=>{\r\n  if(cancelled||!ph.glass.isConnected||capturedSignal?.aborted||gl.isContextLost())return;\r\n  const dt=Math.min(.05,Math.max(0,(now-last)/1000));last=now;\r\n  // Graceful degrade: if frames 10-40 run slow (median under ~45 fps), drop resolution and the fringe.\r\n  if(!lite&&slow.length<40&&slow.push(dt)===40&&slow.slice(10).sort((a,b)=>a-b)[15]>.022){lite=true;pr=Math.min(pr,.75);pk?.stop();size();ph.glass.dataset.lite='1';} // PORTAL-PEEK: lite drops the peek\r\n  const T=ph.T,d=ph.diveT0?Math.min(1,(now-ph.diveT0)/T.reveal):ph.backT0?Math.max(0,1-(now-ph.backT0)/T.reveal):0; // the dive adds up to tunnelDive rings/s (the dive back sheds it)\r\n  travel=(travel+dt*1.25*(PORTAL.tunnelFrom+(PORTAL.tunnelTo-PORTAL.tunnelFrom)*easeInOut((now-ph.t0)/(T.cut+T.load))+PORTAL.tunnelDive*d*d))%seq.length;\r\n  const finger=[...pointers.values()].at(-1)?.pts.at(-1);let tx=(tilt?.[0]||0)+(finger?(finger.x-left-cx)*.12:0),ty=(tilt?.[1]||0)+(finger?(finger.y-top-cy)*.12:0);\r\n  const k=Math.min(1,.2*R/(Math.hypot(tx,ty)||1));par[0]+=(tx*k-par[0])*Math.min(1,dt*5);par[1]+=(ty*k-par[1])*Math.min(1,dt*5);\r\n  const lx=finger?finger.x-left:rest[0]-(tilt?.[0]||0)*8,ly=finger?finger.y-top:rest[1]-(tilt?.[1]||0)*8;light[0]+=(lx-light[0])*Math.min(1,dt*9);light[1]+=(ly-light[1])*Math.min(1,dt*9);\r\n  pk?.frame(dt,now);draw(now);raf=requestAnimationFrame(frame); // PORTAL-PEEK\r\n };\r\n size();ph.glass.append(canvas);ph.glass.classList.add('gl');draw(ph.t0);\r\n if(reduced)return;\r\n const pk=startPeek(ph,t,w,h),stop0=ph.stop;if(pk)ph.stop=()=>{stop0();pk.stop();}; // PORTAL-PEEK\r\n tiltOk=typeof DeviceOrientationEvent!=='undefined'&&typeof DeviceOrientationEvent.requestPermission!=='function';\r\n if(tiltOk)addEventListener('deviceorientation',onTilt);\r\n raf=requestAnimationFrame(frame);\r\n}\r\n// The glass sits between #portalHome's background and the board canvas, so it shows only through the cut.\r\n// pts: client-px polygon to clip to (null = whole screen); all: even rainbow (the cross). The box is the clip's\r\n// bounds, so the wormhole only fills what can show. <b> is the CSS fallback when WebGL2 is unavailable.\r\n// edge: the cut outline. A static glass bezel is drawn along it ABOVE the board: the cut drops whole mesh\r\n// triangles, so the cloth's hole edge stair-steps up to ~12 px either side of the outline, and the bezel covers that.\r\n// T: this sequence's {cut,load,reveal} ms (#124's lines and X run PORTAL.short of them); axis: a line's [a,b], whose\r\n// glowing bezel starts as a slit along it and opens into the lens over the cut, the wormhole pouring through the tear.\r\nconst FULL_T={cut:PORTAL.cutMs,load:PORTAL.loadMinMs,reveal:PORTAL.revealMs};\r\nfunction showGlass(pts,color,all=false,edge=pts,{T=FULL_T,axis=null,monochrome=false,peek=null}={}){\r\n endPhase();\r\n if(craftPortal(boardId))color=portalEdgeTint();\r\n const poly=pts||closeLoop([[0,0],[innerWidth,0],[innerWidth,innerHeight],[0,innerHeight]]),clip=pts?bleedPts(pts):poly;\r\n const xs=clip.map(p=>p[0]),ys=clip.map(p=>p[1]),left=Math.floor(Math.min(...xs)),top=Math.floor(Math.min(...ys)),w=Math.ceil(Math.max(...xs))-left,h=Math.ceil(Math.max(...ys))-top;\r\n const el=document.createElement('div');el.className='portal-glass';el.setAttribute('aria-hidden','true');el.innerHTML='<b></b>';el.classList.toggle('all',all);\r\n el.style.cssText=`left:${left}px;top:${top}px;width:${w}px;height:${h}px`;el.style.setProperty('--glass',color);\r\n const reduced=prefersReducedMotion(),slit=axis&&!reduced;\r\n if(pts)el.style.clipPath=`polygon(${clip.map(([x,y])=>`${x-left}px ${y-top}px`).join(',')})`;\r\n if(!reduced&&!slit)el.style.setProperty('animation','portal-glass-in .6s ease both','important'); // beats the locked theme's animation:none\r\n portalHome.append(el);\r\n el.dataset.tunnel=boardId;\r\n if(monochrome){el.style.filter='grayscale(1)';if(!reduced)el.animate([{filter:'grayscale(0)'},{filter:'grayscale(1)'}],{duration:T.cut+T.load+T.reveal,easing:'linear',fill:'forwards'});}\r\n let bezel=null;\r\n if(edge){\r\n  // Width: 1.55 grid cells (24 px on a phone). Measured at 375x812, the hole edge strays up to 11.2 px (0.72 cell) from the outline.\r\n  const B=craftPortal(boardId)?Math.max(5,(board?.faceRect().width||375)/55):1.55*(board?.faceRect().width||375)/QUILT.segX,p=edge.map(q=>q.join(',')).join(' '),ring=(cls,w,more='')=>`<polygon class=\"${cls}\" stroke-width=\"${w}\" ${more} points=\"${p}\"/>`;\r\n  portalHome.insertAdjacentHTML('beforeend',`<svg class=\"portal-bezel\" aria-hidden=\"true\" style=\"--glass:${color}\"><defs><linearGradient id=\"portalBezelLit\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" class=\"lit\"/><stop offset=\".5\" class=\"mid\"/><stop offset=\"1\" class=\"far\"/></linearGradient><linearGradient id=\"portalBezelSpec\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#fff\"/><stop offset=\".55\" stop-color=\"#fff\" stop-opacity=\".2\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\".6\"/></linearGradient></defs>${ring('shade',B+10)+ring('shade',B+5)+ring('body',B)+ring('glow',B*.6)+ring('glow',B*.36)+ring('core',2.5)+ring('spec',2,`transform=\"translate(${-.3*B} ${-.3*B})\"`)}</svg>`); // stacked strokes, no blur filters: those cost a ~100 ms first paint\r\n  bezel=portalHome.lastElementChild;\r\n  bezel.dataset.material=boardId;\r\n  if(!reduced&&!slit)bezel.style.setProperty('animation','portal-glass-in .3s ease both','important');\r\n  if(slit){ // squashed flat onto the line, then out to the lens\r\n   const [[ax,ay],[bx,by]]=axis,a=Math.atan2(by-ay,bx-ax)*180/Math.PI;bezel.style.transformOrigin=`${d2((ax+bx)/2)}px ${d2((ay+by)/2)}px`;\r\n   bezel.animate([{transform:`rotate(${d2(a)}deg) scale(1,.04) rotate(${d2(-a)}deg)`},{transform:'none'}],{duration:T.cut,easing:'cubic-bezier(.5,0,.2,1)'});\r\n  }\r\n }\r\n phase={glass:el,bezel,pts,color,T,t0:performance.now(),pulse:false,box:{left,top,w,h},peek}; // PORTAL-PEEK: peek = destination route\r\n startTunnel(phase,poly,color,all);\r\n energize(restSeq(),ENERGY.surge,true);\r\n}\r\nfunction endPhase(){phase?.stop?.();phase?.dive?.cancel();phase?.glass.remove();phase?.bezel?.remove();phase=null;energize(restSeq());}\r\n// The wormhole on its own, for transitions elsewhere (a workout's rest <-> attack, leaving Meditation): 'in' opens it\r\n// from the centre of the screen until it covers everything (then it stays up, still flowing, while the caller swaps\r\n// what is under it); 'out' shrinks it back into its core and removes it. Resolves when that half is done. One wormhole\r\n// canvas is shared with the quilt's glass, so don't play it while the quilt is mid-portal. Reduced motion: instant.\r\nlet wormhole=null;\r\nexport async function playWormhole({direction='in',minMs=900,color='#b026ff',host=document.body}={}){\r\n portalSound('travel');\r\n const ms=prefersReducedMotion()?0:Math.max(0,minMs),R=Math.ceil(Math.hypot(innerWidth,innerHeight)/2);\r\n if(!wormhole){\r\n  const el=document.createElement('div'),w=innerWidth,h=innerHeight;el.className='portal-glass portal-wormhole';el.setAttribute('aria-hidden','true');el.innerHTML='<b></b>';\r\n  el.style.cssText=`position:fixed;inset:0;width:${w}px;height:${h}px;margin:0;padding:0;border:0;max-width:none;max-height:none;overflow:hidden;background:#000;pointer-events:none;z-index:10000;--glass:${color}`;\r\n  if(direction==='out')el.style.clipPath=`circle(${R}px at 50% 50%)`;\r\n  host.append(el);if(el.showPopover){el.setAttribute('popover','manual');el.showPopover();}\r\n  const ph={glass:el,fullscreen:true,box:{left:0,top:0,w,h},T:{cut:Math.max(1,ms),load:0,reveal:Math.max(1,ms)},t0:performance.now()-(direction==='out'?ms:0),pulse:false};\r\n  startTunnel(ph,closeLoop([[0,0],[w,0],[w,h],[0,h]]),color,false);\r\n  wormhole={el,ph};\r\n }\r\n const {el,ph}=wormhole,out=direction==='out';\r\n ph[out?'backT0':'diveT0']=performance.now();\r\n await el.animate([{clipPath:'circle(0px at 50% 50%)'},{clipPath:`circle(${R}px at 50% 50%)`}],{duration:ms,easing:out?'cubic-bezier(.6,0,.9,.4)':'cubic-bezier(.2,.6,.3,1)',direction:out?'reverse':'normal',fill:'forwards'}).finished.catch(()=>{});\r\n if(out&&wormhole?.el===el){ph.stop?.();el.remove();wormhole=null;}\r\n}\r\n// A destination seen through the hole: stop the wormhole and leave its still depth (what a flat menu's rectangle sits in),\r\n// the bezel staying over the cloth's ragged edge.\r\nfunction quietPhase(){if(!phase)return;phase.stop?.();phase.stop=null;phase.pulse=false;phase.glass.classList.remove('gl');phase.glass.classList.add('still');phase.glass.querySelector('canvas')?.remove();energize(restSeq());}\r\nfunction ripple(x,y){\r\n if(!phase||prefersReducedMotion())return;\r\n const r=document.createElement('i');r.className='portal-ripple';r.style.left=(x-phase.box.left)+'px';r.style.top=(y-phase.box.top)+'px';\r\n r.style.setProperty('animation',`portal-ripple ${PORTAL.rippleMs}ms cubic-bezier(.2,.6,.3,1) forwards`,'important'); // beats the locked theme's animation:none\r\n phase.glass.append(r);setTimeout(()=>r.remove(),PORTAL.rippleMs);\r\n}\r\n// Cuts a client-px polygon out of the board (board.cut takes face coords, v down) and resolves once the piece has fallen in.\r\nfunction cutBoard(pts,color,ms=prefersReducedMotion()?0:PORTAL.cutMs){\r\n const f=board?.faceRect();if(!f)return Promise.resolve();\r\n return board.cut(pts.map(([x,y])=>[(x-f.left)/f.width,(y-f.top)/f.height]),color,ms);\r\n}\r\n// Resolves once it has landed (the transition's own end: a slow frame can hold its start back well past ms).\r\nfunction revealDialogFromPoint(dialog,[cx,cy],ms=500){\r\n if(prefersReducedMotion())return Promise.resolve();\r\n const dbox=dialog.getBoundingClientRect();\r\n dialog.style.transformOrigin=`${cx-dbox.left}px ${cy-dbox.top}px`;motion(dialog,'none');dialog.style.transform='scale(.05)';dialog.style.opacity='0';\r\n return settle().then(()=>{\r\n  if(!dialog.open)return;\r\n  motion(dialog,`transform ${ms}ms cubic-bezier(.2,0,.3,1),opacity ${ms*.8}ms ease`);dialog.style.transform='';dialog.style.opacity='';\r\n  getComputedStyle(dialog).transform; // starts the transition now, so it can be awaited\r\n  const grow=dialog.getAnimations().find(a=>a.transitionProperty==='transform');\r\n  return grow?grow.finished.catch(()=>{}):undefined;\r\n });\r\n}\r\n// Dive into the wormhole (Ian 2026-09-23, #103 \"zoom into each portal once opened\"): the whole portal scales up about the\r\n// vanishing point until the shape covers the screen, easing in over revealMs while the tunnel speeds up. A compositor-only\r\n// Web Animation: the locked theme can't switch it off and it leaves `transition` free for the hole/fade. Reduced motion: a\r\n// quick fade. endPhase() cancels it. back: the same dive played in reverse, out of the wormhole (diveBack).\r\nfunction dive(pts,back=false){\r\n if(!phase)return Promise.resolve();\r\n if(back)portalSound('travel');\r\n const c=centroidOf(pts);let rIn=Infinity;\r\n for(let k=0;k<pts.length-1;k++){const [ax,ay]=pts[k],[bx,by]=pts[k+1],dx=bx-ax,dy=by-ay,t=Math.max(0,Math.min(1,((c[0]-ax)*dx+(c[1]-ay)*dy)/(dx*dx+dy*dy||1)));rIn=Math.min(rIn,Math.hypot(ax+t*dx-c[0],ay+t*dy-c[1]));}\r\n const far=Math.max(...[[0,0],[innerWidth,0],[0,innerHeight],[innerWidth,innerHeight]].map(([x,y])=>Math.hypot(x-c[0],y-c[1]))),reduced=prefersReducedMotion();\r\n portalHome.style.transformOrigin=`${c[0]}px ${c[1]}px`;phase[back?'backT0':'diveT0']=performance.now();\r\n energize(null,ENERGY.dive,true);\r\n phase.dive=portalHome.animate(reduced?[{opacity:1},{opacity:0}]:[{transform:'none'},{transform:`scale(${Math.max(1.5,1.05*far/Math.max(rIn,1))})`}],{duration:reduced?200:phase.T.reveal,easing:reduced?'ease':'cubic-bezier(.55,0,.9,.5)',fill:back?'none':'forwards',direction:back?'reverse':'normal'});\r\n return phase.dive.finished.catch(()=>{});\r\n}\r\n// The destination dialog grows out of the tunnel's core; its backdrop fades in so the dive stays visible behind it.\r\n// Release 5: the bottom bar (routes.mjs moves it into the open route's dialog) must not ride the arrival's scale. It waits\r\n// on the page, still showing below the frame (portal.css ends the framed backdrop above it), and goes back in once the\r\n// dialog has landed, unless the route moved on meanwhile (routes.mjs then already re-homed it).\r\nfunction arriveFromCore(dialog,core,ms){\r\n if(prefersReducedMotion())return;\r\n const bar=dialog.querySelector(':scope>#coachDock');if(bar)document.body.append(bar);\r\n dialog.style.setProperty('--portal-arrive',ms+'ms');dialog.classList.add('portal-arriving');\r\n // The bar goes back in only once the scale has landed (a fixed bar inside a scaled dialog would ride the scale).\r\n revealDialogFromPoint(dialog,core,ms).then(()=>{dialog.classList.remove('portal-arriving');dialog.style.removeProperty('--portal-arrive');motion(dialog,'');dialog.style.transformOrigin='';if(bar?.parentNode===document.body&&dialog.open)dialog.append(bar);});\r\n}\r\n\r\nfunction fallbackRect(){const r=overlay.getBoundingClientRect();return{left:r.left,top:r.top,width:r.width,height:r.height};}\r\n\r\nconst musicHold=hold=>window.dispatchEvent(new CustomEvent('myr5:music-hold',{detail:{hold}})); // page music: silent through the trace + tunnel, destination track fades in after\r\nasync function runShape(id){\r\n if(busy||!boardShown||!MENUS[id])return;\r\n const run=++sequence;busy=true;scheduleIdle();musicHold(true);\r\n try{await portalSequence(id,()=>run===sequence&&!lifecycle.signal.aborted);vaultSend({counter:'shape-opened',key:id});}\r\n finally{musicHold(false);if(run===sequence){busy=false;scheduleIdle();}}\r\n}\r\n// Lines are open strokes with no enclosed area; portalWindow() gives them (and the X) a window to cut (#124).\r\nconst LINE_IDS=new Set(['line-lr','line-rl','line-down']);\r\nconst lineTemplatePts=id=>SHAPES.line[(id==='line-lr'||id==='line-rl')?1:0].points;\r\nasync function openDirect(menu,current){\r\n // No dive in, but the way back out is still the wormhole: the destination's own shape, or the full square.\r\n const id=Object.keys(MENUS).find(k=>MENUS[k]===menu),{face}=restFace(),pts=backPts(id);\r\n let dialog=null;\r\n backgroundBlocked(false);\r\n frameOn(face,face&&windowLook(id,menu,face));const unwatch=watchDialog();\r\n try{dialog=await menu.open?.();}catch(error){console.warn(`${menu.label} failed to open.`,error);}finally{unwatch?.();}\r\n if(!current())return;\r\n const shown=dialog instanceof HTMLDialogElement?dialog.open:dialog?.getClientRects?.().length>0;\r\n if(!shown){status(`${menu.label} isn't available here yet.`);await fadeOutBoard();fadeInBoard();return;}\r\n if(dialog instanceof HTMLDialogElement){\r\n  frameDialog(dialog);\r\n  dialog.addEventListener('close',()=>closed(dialog,{pts,color:menu.color},current),{once:true});\r\n }else frameOff();\r\n await fadeOutBoard();\r\n}\r\n// #131: the shapes that aren't full screen open INTO their cut, the quilt staying on as the wall; the rest dive until\r\n// the destination fills the frame. #124: the four lines and the X get the wormhole too, shorter (PORTAL.short): a line\r\n// has no area to cut, so a glowing slit along it opens into a lens-shaped window; the X opens the diamond between its arms.\r\nconst SHAPED=new Set(['rect','oval','up','down','vdiamond','hdiamond']);\r\n// A lens along a -> b: two sine arcs bulging `half` px either side (pointed at the ends), n+1 points a side.\r\nasync function portalSequence(id,current){\r\n const rect=board?board.patternRect():fallbackRect(),face=board?.faceRect();\r\n if(id==='cross'){\r\n  portalSound('travel');\r\n  flashOutline(SHAPES.cross.map(p=>toClientPts(p.points,rect)),'#ffffff');\r\n  const center=[rect.left+rect.width/2,rect.top+rect.height/2];\r\n  const face=board?.faceRect();\r\n  const cut=shapeClipPts('rect',rect);\r\n  showGlass(face&&closeLoop(toClientPts([[0,0],[1,0],[1,1],[0,1]],face)),'#ffffff',true,cut); // rainbow glass behind the whole board; the whole pattern falls in over it\r\n  await Promise.all([cutBoard(cut,'#ffffff'),fallInAll(center)]);\r\n  if(!current())return;\r\n  openMenu();\r\n  await settle();\r\n  revealDialogFromPoint(menuSheet,center);\r\n  menuSheet.addEventListener('close',()=>{motion(menuSheet,'');menuSheet.style.transform='';menuSheet.style.opacity='';},{once:true});\r\n  return;\r\n }\r\n const menu=MENUS[id];if(!menu)return;\r\n const line=LINE_IDS.has(id),short=line||id==='x';\r\n if(!SHAPES[id]&&!line){if(menu.locked?.()){status(menu.lockedMessage);return;}setVisible(false);menu.open?.();return;} // menu without a traced shape (opened by id)\r\n const win=portalWindow(id,rect),pts=boardId==='cogs'?win.pts:insetPts(win.pts,id);\r\n flashOutline(id==='x'?SHAPES.x.map(p=>toClientPts(p.points,rect)):line?[toClientPts(lineTemplatePts(id),rect)]:[pts],menu.color);\r\n if(menu.locked?.()){status(menu.lockedMessage);return;}\r\n status('');\r\n portalSound('travel');\r\n const k=short?PORTAL.short:1,T={cut:PORTAL.cutMs*k,load:PORTAL.loadMinMs*k,reveal:PORTAL.revealMs*k},reduced=prefersReducedMotion();\r\n showGlass(pts,menu.color,false,pts,{T,axis:win.axis,monochrome:menu.route==='meditate',peek:menu.route}); // PORTAL-PEEK: route\r\n await cutBoard(pts,menu.color,reduced?0:T.cut);\r\n if(!current())return;\r\n // Loading phase: the glass stays live (touch ripples, breathing outline) for at least T.load.\r\n if(phase)phase.pulse=!reduced;\r\n if(phase?.pulse)kickRender();\r\n await sleep(reduced?0:T.load);\r\n if(!current())return;\r\n if(SHAPED.has(id)&&id!=='rect'&&menu.kind==='dialog'&&face){await openInHole(id,menu,pts,face,current);return;}\r\n // Reveal: dive into the wormhole; the destination appears from its core over the last `arrive` ms of the dive.\r\n const dove=dive(pts),core=centroidOf(pts),arrive=T.reveal*.55;\r\n if(menu.kind==='dialog')frameOn(face,face&&windowLook(id,menu,face)); // the frame stays put and the dive happens in its window\r\n if(reduced||menu.kind==='nav'||menu.kind==='menu')await dove;else await sleep(T.reveal-arrive);\r\n if(!current())return;\r\n if(menu.kind==='home'){await growHole(portalHome,pts,rectBox(portalHome),current,arrive);if(current()){setVisible(false);menu.open?.();}return;}\r\n if(menu.kind==='nav'){menu.open();return;} // X -> the Character editor: dive, then navigate (the glass stays up while it loads)\r\n if(menu.kind==='menu'){ // line-up: the Menu sheet grows out of the core (openMenu hides the portal itself)\r\n  openMenu();await settle();revealDialogFromPoint(menuSheet,core);\r\n  menuSheet.addEventListener('close',()=>{motion(menuSheet,'');menuSheet.style.transform='';menuSheet.style.opacity='';},{once:true});\r\n  return;\r\n }\r\n let dialog=null;\r\n backgroundBlocked(false);\r\n const unwatch=watchDialog();\r\n try{dialog=await menu.open?.();}catch(error){console.warn(`${menu.label} failed to open.`,error);}finally{unwatch?.();}\r\n if(dialog instanceof HTMLDialogElement&&dialog.open&&current()){frameDialog(dialog);arriveFromCore(dialog,core,arrive);} // before its first paint: no full-size flash\r\n await settle();\r\n if(!current())return;\r\n const shown=dialog instanceof HTMLDialogElement?dialog.open:dialog?.getClientRects?.().length>0;\r\n // Panel missing in this build (or it refused to open): never leave the screen stuck on the glass.\r\n if(!shown){status(`${menu.label} isn't available here yet.`);await fadeOutBoard();fadeInBoard();return;}\r\n if(dialog instanceof HTMLDialogElement){\r\n  await dove;\r\n  if(!current())return;\r\n  if(!dialog.open){fadeInBoard();return;} // closed mid-reveal\r\n  dialog.addEventListener('close',()=>closed(dialog,{pts,color:menu.color},current),{once:true});\r\n }else frameOff();\r\n fadeOutBoard();\r\n}\r\n// #131 the destination opens in the hole (Portal's \"open\"): the frame's chrome and the rim come up with a burst, the\r\n// dialog opens clipped to the cut, growing out of the core to the outline (shapeDialog), the wormhole under it stops and\r\n// the quilt stays on as the wall. Closing fizzles it shut (fizzleBack).\r\nasync function openInHole(id,menu,pts,face,current){\r\n frameOn(face,shapeLook(id,menu,pts));\r\n let dialog=null;\r\n backgroundBlocked(false);\r\n const unwatch=watchDialog();\r\n try{dialog=await menu.open?.();}catch(error){console.warn(`${menu.label} failed to open.`,error);}finally{unwatch?.();}\r\n if(!current())return;\r\n if(dialog instanceof HTMLDialogElement&&dialog.open)frameDialog(dialog);\r\n await settle();\r\n if(!current())return;\r\n const shown=dialog instanceof HTMLDialogElement?dialog.open:dialog?.getClientRects?.().length>0;\r\n if(!shown){frameOff();status(`${menu.label} isn't available here yet.`);await fadeOutBoard();fadeInBoard();return;}\r\n if(!(dialog instanceof HTMLDialogElement)){frameOff();fadeOutBoard();return;}\r\n dialog.addEventListener('close',()=>closed(dialog,{pts,color:menu.color,shaped:true},current),{once:true});\r\n await sleep(prefersReducedMotion()?0:700); // the wormhole shows round the destination while the hole opens out\r\n if(current()&&framed?.dialog===dialog&&!framed.expanded){quietPhase();board?.pause();}\r\n}\r\n\r\n// Vault secret: a finished board secret goes straight to the vault route and its 3D safe door (Ian 7 Oct: no flat door poster).\r\n// setVisible(false) hides the portal; every later show heals the board (glb host -> effect.healSecret) so the secret never lingers.\r\nfunction goVault(){const r=window.myr5Routes;return r?r.go('vault'):import('../routes.mjs').then(m=>m.mountRoutes().go('vault'));}\r\n// Give back every portal WebGL context (3D board, tunnel, idle peek). The flat board and its chosen ID stay, so the next\r\n// show (or 'myr5:graphics-free' when the Vault closes) builds the original 3D board again.\r\nasync function releasePortalGraphics(){\r\n boardLoad++;const previous=gl3;gl3=null;board=base;delete art[boardId];\r\n if(base?.canvas)base.canvas.style.visibility='';\r\n if(previous){portalHome.dataset.art='flat';if(boardShown)base?.resume();}\r\n stopWormhole();\r\n const boardRelease=previous?releaseGraphics(previous.canvas,previous.glContext||null,()=>previous.dispose()):Promise.resolve();\r\n await Promise.all([boardRelease,releaseTunnel()]);\r\n await waitForGraphicsRelease();\r\n try{const peek=await import('./portal-peek.mjs');await peek.releasePeekRendererIfIdle();}catch{}\r\n}\r\nasync function showVaultDoor(secretId=boardId){\r\n // Secrets fire synchronously from effect.step() (or release()). Let that call\r\n // and the board's current render finish before disposal clears the effect state.\r\n await Promise.resolve();\r\n if(!boardShown||!board)return;\r\n pointers.clear();clearTimeout(finalizeTimer);pendingStrokes=[];pendingTrailPts=[];lastTap=null;\r\n // Free the portal's WebGL contexts before the door and hall allocate theirs.\r\n const released=releasePortalGraphics();base?.resume();\r\n setVisible(false); // also stops any active portal-peek source before the Vault claims a WebGL context\r\n await released;\r\n try{\r\n  const store=await import('../vault/vault-store.mjs');await store.markSecret(secretId);\r\n }catch(error){\r\n  console.error('Could not save the Achievement Vault secret.',error);\r\n  setVisible(true);status('The secret could not be saved yet. It has not been discarded; retry the Vault handoff.',{label:'Retry Save + Vault',run:()=>showVaultDoor(secretId)});return;\r\n }\r\n try{\r\n  await goVault();\r\n  if(secretId==='mache')document.getElementById('vaultPanel')?._vault?.enter?.();\r\n }catch(error){\r\n  console.error('Could not open the Achievement Vault.',error);\r\n  setVisible(true);status('Your secret is saved. The Vault could not open; try again.',{label:'Retry Vault',run:()=>showVaultDoor(secretId)});\r\n }\r\n}\r\nfunction toNorm(x,y){const r=board.patternRect();return[(x-r.left)/r.width,(y-r.top)/r.height];}\r\nfunction endPointer(e,cancel){ if(cancel) lastTap=null;\r\n const p=pointers.get(e.pointerId);if(!p)return;\r\n pointers.delete(e.pointerId);if(cancel)board.cancel?.(e.pointerId);board.release(e.pointerId);\r\n const claimed=!!board.claims?.(e.pointerId); // vault secrets: asked after release (the glb wrapper answered before its effect forgot the pointer)\r\n if(['pixel','paper','clay','mache'].includes(boardId)&&claimed){lastTap=null;scheduleIdle();kickRender();return;}\r\n // #105: the just-released stroke keeps fading (light-painting), independent of whether it matches.\r\n if(p.pts.length>1)fading.push({pts:p.pts,releasedAt:performance.now()});\r\n scheduleIdle();kickRender();\r\n if(cancel||busy)return;\r\n markHintSeen(); // #22: any real released touch, trace or tap, ends the first-run hint window for good.\r\n const xs=p.norm.map(n=>n[0]),ys=p.norm.map(n=>n[1]);\r\n const size=Math.hypot(Math.max(...xs)-Math.min(...xs),Math.max(...ys)-Math.min(...ys));\r\n if(size<.03){\r\n  // #20: a second tap within TAP_MS and TAP_MOVE_PX of the first, near a shape's stitched outline (within\r\n  // TAP_HIT_PX), opens it exactly as tracing does. A single tap, or a double-tap far from any outline,\r\n  // just ripples the fabric (board.press on pointerdown already does that).\r\n  const tap={x:e.clientX,y:e.clientY,t:(Number.isFinite(e.timeStamp)&&e.timeStamp>0?e.timeStamp:performance.now())};\r\n  const isDouble=lastTap&&tap.t-lastTap.t>=0&&tap.t-lastTap.t<=TAP_MS&&Math.hypot(tap.x-lastTap.x,tap.y-lastTap.y)<=TAP_MOVE_PX;\r\n  lastTap=isDouble?null:tap;\r\n  if(isDouble){\r\n   const id=nearestTapShape(tap.x,tap.y);\r\n   if(MENUS[id]){buzz(12);runShape(id);return;} // an outline double-tap opens its shape before any secret may claim it\r\n  }\r\n  if(claimed)lastTap=null;\r\n  return;\r\n }\r\n lastTap=null;\r\n if(claimed)return; // a claimed stroke is a secret gesture: no shape recognition, no \"Almost\"\r\n\r\n pendingStrokes.push(p.norm);pendingTrailPts.push(p.pts);\r\n clearTimeout(finalizeTimer);\r\n finalizeTimer=setTimeout(()=>{\r\n  const strokes=pendingStrokes,trailPts=pendingTrailPts;pendingStrokes=[];pendingTrailPts=[];\r\n  const id=recognizeShape(strokes);\r\n  if(MENUS[id]){\r\n   // #105: on a match, the drawn trail itself flashes the destination colour before the cut starts.\r\n   flashOutline(trailPts.map(pts=>pts.map(pt=>[pt.x,pt.y])),id==='cross'?'#ffffff':(MENUS[id]?.color||'#ffffff'));\r\n   buzz(12); // #29\r\n   runShape(id);\r\n   return;\r\n  }\r\n  // #21 \"almost\": close but not a match — flash the nearest candidate's outline + faint label, don't open it.\r\n  const near=nearestShape(strokes);\r\n  if(near&&MENUS[near.id]&&near.score>=ALMOST_COVER){\r\n   const rect=board?board.patternRect():fallbackRect(),info=idleShapeInfo(near.id,rect);\r\n   flashOutline(info.polys,info.color,{label:`Almost: ${info.label}`,labelPt:info.labelPt,duration:ALMOST_MS});\r\n   buzz([12,40,12]); // #29 double-pulse\r\n  }\r\n },450);\r\n}\r\nfunction wirePointerEvents(){\r\n overlay.addEventListener('pointerdown',e=>{ if(pointers.size>0){lastTap=null;}\r\n  stopHint(); // #22: any touch stops the first-run hint immediately, mid-animation or not.\r\n  overlay.setPointerCapture(e.pointerId);clearTimeout(finalizeTimer);\r\n  pointers.set(e.pointerId,{pts:[{x:e.clientX,y:e.clientY,t:performance.now()}],norm:[toNorm(e.clientX,e.clientY)],soundX:e.clientX,soundY:e.clientY,soundT:performance.now()});\r\n  if(pointers.size===1&&!busy)portalSound('press');\r\n  scheduleIdle();\r\n  if(busy)ripple(e.clientX,e.clientY);else board.press(e.pointerId,e.clientX,e.clientY);\r\n  kickRender();\r\n });\r\n overlay.addEventListener('pointermove',e=>{\r\n  const p=pointers.get(e.pointerId);if(!p)return;\r\n  p.pts.push({x:e.clientX,y:e.clientY,t:performance.now()});p.norm.push(toNorm(e.clientX,e.clientY));\r\n  if(pointers.size===1&&!busy&&performance.now()-p.soundT>115&&Math.hypot(e.clientX-p.soundX,e.clientY-p.soundY)>12){p.soundX=e.clientX;p.soundY=e.clientY;p.soundT=performance.now();portalSound('drag');}\r\n  if(!busy)board.press(e.pointerId,e.clientX,e.clientY);\r\n });\r\n overlay.addEventListener('pointerup',e=>endPointer(e,false));\r\n overlay.addEventListener('pointercancel',e=>endPointer(e,true));\r\n}\r\n\r\nfunction initialVisible(){\r\n return !(new URLSearchParams(location.search).has('panel')||location.hash==='#pod'||document.body.dataset.screen==='rest');\r\n}\r\n\r\n// The cockpit service key also mounts this module directly. Load the board's layout\r\n// before any canvas measures its host; portal-entry is not the only entry point.\r\nlet portalStyles;\r\nfunction ensurePortalStyles(){\r\n if(portalStyles)return portalStyles;\r\n let style=document.querySelector('link[rel=\"stylesheet\"][href=\"/modules/portal/portal.css\"]');\r\n if(style?.sheet)return Promise.resolve();\r\n const fresh=!style;\r\n if(fresh){style=document.createElement('link');style.rel='stylesheet';style.href='/modules/portal/portal.css';}\r\n portalStyles=new Promise((resolve,reject)=>{\r\n  style.addEventListener('load',()=>{portalStyles=null;resolve();},{once:true});\r\n  style.addEventListener('error',()=>{if(fresh)style.remove();portalStyles=null;reject(new Error('Portal styles unavailable.'));},{once:true});\r\n  if(fresh)document.head.append(style);\r\n });\r\n return portalStyles;\r\n}\r\n\r\nexport async function mountPortal({visible=false}={}){\r\n if(window.myr5Portal&&!window.myr5Portal.disposed)return window.myr5Portal;\r\n await ensurePortalStyles();\r\n const lifetime=new AbortController();lifecycle=lifetime;\r\n buildDom();\r\n addEventListener('myr5:food-photo-ready',()=>expandScene(document.getElementById('mealsPanel')),{signal:lifecycle.signal});\r\n document.addEventListener('myr5:classroom-board',e=>{if(e.target?.id==='accountPanel')expandScene(e.target);},{signal:lifecycle.signal});\r\n document.addEventListener('room-ready',e=>{const f=framed;if(e.target?.id==='accountPanel'&&f?.dialog===e.target&&!f.expanded&&f.look.shaped){layoutInCut(e.target,true);clipTo(e.target,f.outlines.shape);}},{signal:lifecycle.signal});\r\n document.addEventListener('click',e=>{if(workoutHome?.open&&e.target.closest?.('#start')){setVisible(false);restoreWorkoutHome();workoutHome.close();}},{capture:true,signal:lifecycle.signal});\r\n for(const type of ['storage','pageshow'])addEventListener(type,syncLook,{signal:lifecycle.signal});\r\n if(menuBtn!==portalHome.querySelector('#portalExitButton'))menuBtn.addEventListener('click',()=>{if(busy)return;setVisible(!boardShown);},{signal:lifecycle.signal});\r\n boardBtn?.addEventListener('click',()=>setVisible(true),{signal:lifecycle.signal});\r\n addEventListener('pagehide',grimFlush,{signal:lifecycle.signal});document.addEventListener('visibilitychange',()=>{if(document.hidden)grimFlush();else if(boardShown&&!visibleSince)visibleSince=Date.now();},{signal:lifecycle.signal});\r\n addEventListener('myr5:portal-secret',e=>{const id=e.detail?.board||boardId;void showVaultDoor(id);},{signal:lifecycle.signal});\r\n addEventListener('myr5:pixel-rehearsal-complete',()=>{if(boardId==='pixel'&&boardShown){void releasePortalGraphics().then(()=>{setVisible(false);return goVault();});}},{signal:lifecycle.signal});\r\n // A 3D screen (the Vault from any route, food pyramid, ...) claims the GPU: hand the board back, rebuild 3D when it is free again.\r\n addEventListener('myr5:graphics-claim',e=>{if(!e.detail?.keep?.includes('portal'))e.detail?.wait?.(releasePortalGraphics());},{signal:lifecycle.signal});\r\n addEventListener('myr5:graphics-free',()=>{if(boardShown)threeD();},{signal:lifecycle.signal});\r\n await loadBoard(initialBoardId());\r\n await Promise.race([threeD(true),sleep(WAIT.mount)]); // come up in 3D when it's quick; never wait on a stalled one\r\n wirePointerEvents(); // Allocate the tunnel only when a transition actually needs it.\r\n resizeOverlay();overlayObserver=new ResizeObserver(resizeOverlay);overlayObserver.observe(portalHome);\r\n setVisible(visible&&initialVisible());\r\n // Back from a 'nav' portal via the bfcache: drop the stale glass and hole.\r\n let resumeAfterPageShow=false;\r\n addEventListener('pagehide',()=>{resumeAfterPageShow=boardShown;setVisible(false);},{signal:lifecycle.signal});\r\n addEventListener('pageshow',e=>{if(e.persisted&&resumeAfterPageShow)setVisible(true);},{signal:lifecycle.signal});\r\n // #104: pause/resume the idle cycle with the tab (a backgrounded tab must not keep animating).\r\n document.addEventListener('visibilitychange',scheduleIdle,{signal:lifecycle.signal});\r\n // Risk 4: a dialog over the quilt (setup gate, a reward reveal) that doesn't hide the portal still\r\n // blocks idleEligible() while open, but nothing then re-arms the idle timer once it closes. 'close'\r\n // doesn't bubble, but it still reaches a capturing listener on document for any dialog in the page.\r\n document.addEventListener('close',scheduleIdle,{capture:true,signal:lifecycle.signal});\r\n window.myr5Portal={\r\n  get disposed(){return lifetime.signal.aborted;},\r\n  dispose(){if(lifetime.signal.aborted)return;clearTimeout(idleTimer);idleTimer=0;idleCycle=null;setVisible(false);fading.length=0;boardLoad++;lifecycle.abort();overlayObserver?.disconnect();void disposeBoard(gl3);base?.dispose();gl3=base=board=null;menuChosen=true;menuSheet.close();menuSheet.remove();restoreWorkoutHome();workoutHome?.close();workoutHome?.remove();portalHome.remove();chrome.remove();energyAnims.length=0;void releaseTunnel();void import('./portal-peek.mjs').then(m=>m.releasePeekRendererIfIdle()).catch(()=>{});for(const cancel of flashes)cancel();window.myr5Portal=null;},\r\n  secret:id=>window.dispatchEvent(new CustomEvent('myr5:portal-secret',{detail:{board:id||boardId}})), // debug/test: same event the boards fire\r\n  show:()=>setVisible(true),\r\n  hide:()=>setVisible(false),\r\n  open:id=>runShape(id),\r\n  frameDirectDestination,\r\n  trace(strokes){const id=recognizeShape(strokes);if(MENUS[id])runShape(id);return MENUS[id]?id:null;},\r\n  board:id=>loadBoard(id).then(()=>ready3D), // resolves once 3D is drawing or has fallen back to the flat board\r\n  openWorkoutHome,\r\n  flashTransition:async({duration=520}={})=>{\r\n   duration=Number.isFinite(duration)?Math.max(0,Math.min(duration,10000)):520;\r\n   if(prefersReducedMotion())duration=0;\r\n   const flash=document.createElement('div');flash.className='portal-transition-flash';flash.setAttribute('aria-hidden','true');document.body.append(flash);\r\n   flash.style.setProperty('animation',`portal-transition-flash ${duration}ms ease-in-out both`,'important');\r\n   if(flash.showPopover){flash.setAttribute('popover','manual');flash.showPopover();}\r\n   window.dispatchEvent(new CustomEvent('myr5:portal-transition',{detail:{phase:'flash',duration}}));\r\n   if(!duration){flash.remove();window.dispatchEvent(new CustomEvent('myr5:portal-transition',{detail:{phase:'complete'}}));return;}\r\n   await new Promise((resolve,reject)=>{let timer;const finish=()=>{clearTimeout(timer);flashes.delete(cancel);resolve();},cancel=()=>{clearTimeout(timer);flash.remove();flashes.delete(cancel);reject(new DOMException('Portal closed.','AbortError'));};flashes.add(cancel);flash.addEventListener('animationend',finish,{once:true});timer=setTimeout(finish,duration+100);});\r\n   if(lifetime.signal.aborted){flash.remove();throw new DOMException('Portal closed.','AbortError');}\r\n   flash.remove();window.dispatchEvent(new CustomEvent('myr5:portal-transition',{detail:{phase:'complete'}}));\r\n  },\r\n  current:()=>board,\r\n  releaseTunnel, // arcade/game-portal.mjs hands the shared tunnel context back after each in-game portal\r\n  playWormhole, // #149: Meditation's early-stop smack exits through it (meditation.mjs throughWormhole)\r\n };\r\n if(window.__portalTrailProbe===true)window.myr5Portal.trailProbe=trailProbe;\r\n if(new URLSearchParams(location.search).has('vault'))void goVault(); // ?vault=1 boot, like ?board=\r\n return window.myr5Portal;\r\n}\r\n","modules/portal/standalone-housing.css":":root{--myr5-bar:calc(88px + env(safe-area-inset-bottom))}\r\n@media(orientation:landscape) and (max-height:500px){:root{--myr5-bar:calc(64px + env(safe-area-inset-bottom))}} /* R20: a lower console on landscape phones, every page alike */\r\n:is(#portalChrome,#cameraWorkout).portal-standalone{--portal-rail:15px;--pm:var(--portal-metal,#4d3d4f);}\r\n:is(#portalChrome,#cameraWorkout).portal-standalone .portal-frame{--face-left:15px;--face-top:15px;--face-width:calc(100vw - 30px);--face-height:calc(100dvh - 30px);}\r\n/* R20: the customizer's housing ends above its dock (the chrome stops at the dock's top edge: portal.css). */\r\n#portalChrome.portal-standalone .portal-frame{--face-height:calc(100dvh - 30px - var(--myr5-bar))}\r\n:is(#portalChrome,#cameraWorkout).portal-standalone .portal-standalone-aura{--ft:48px;--pk:30%;--ln:2px;position:absolute;z-index:60;inset:var(--portal-rail,15px);pointer-events:none;box-sizing:border-box;opacity:.95;background:linear-gradient(to bottom,var(--aura,#b026ff),transparent) top/100% var(--ln) no-repeat,linear-gradient(to top,var(--aura,#b026ff),transparent) bottom/100% var(--ln) no-repeat,linear-gradient(to right,var(--aura,#b026ff),transparent) left/var(--ln) 100% no-repeat,linear-gradient(to left,var(--aura,#b026ff),transparent) right/var(--ln) 100% no-repeat,linear-gradient(to bottom,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*1),transparent),color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.35),transparent) 30%,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.08),transparent) 65%,transparent) top/100% var(--ft) no-repeat,linear-gradient(to top,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*1),transparent),color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.35),transparent) 30%,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.08),transparent) 65%,transparent) bottom/100% var(--ft) no-repeat,linear-gradient(to right,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*1),transparent),color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.35),transparent) 30%,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.08),transparent) 65%,transparent) left/var(--ft) 100% no-repeat,linear-gradient(to left,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*1),transparent),color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.35),transparent) 30%,color-mix(in srgb,var(--aura,#b026ff) calc(var(--pk)*0.08),transparent) 65%,transparent) right/var(--ft) 100% no-repeat} /* R21 L2: --ft feather width, --pk peak opacity, --ln thin edge */\r\n#portalChrome.portal-standalone .portal-energy{opacity:.86}\r\n#portalChrome.portal-standalone .portal-energy.hot{opacity:1}\r\n:is(#portalChrome,#cameraWorkout).portal-standalone .portal-frame{box-shadow:0 0 0 calc(var(--portal-rail) + 1px) #0e0a12,0 8px 16px calc(var(--portal-rail) - 2px) #000b;}\r\n.editor-tilt-request{min-width:44px;min-height:40px;padding:0 10px;border:1px solid #e0c9ef;border-radius:999px;background:#201728e8;color:#fff;font:600 12px system-ui,sans-serif;pointer-events:auto;touch-action:manipulation}\r\n.editor-tilt-request:focus-visible{outline:3px solid #ffb24d;outline-offset:2px}\r\n#coachDock.coach-dock a[aria-current=page]{color:#fff7e0;border-color:#ffb24d;background:#4d305f;box-shadow:0 0 0 1px #ffb24d,0 0 14px #ffb24d80}\r\n/* R20: the same 88px console as the app (portal.css draws it), full width at the bottom, keys in the app's order. */\r\n#coachDock.coach-dock{position:fixed;z-index:40;inset:auto 0 0!important;width:auto;margin:0}\r\n#coachDock.coach-dock>a{grid-column:auto!important}\r\n#coachDock.coach-dock a{text-decoration:none}\r\n#coachDock.coach-dock a.dock-portal{font:600 10px/1 system-ui,sans-serif;letter-spacing:.4px}\r\n#coachDock.coach-dock a.dock-control{position:relative;grid-row:1;box-sizing:border-box;width:44px;height:44px;min-width:44px;padding:0;display:grid;place-items:center;border:0;border-radius:0;background:none;box-shadow:none;filter:drop-shadow(0 3px 0 #130d18) drop-shadow(0 0 1px #000);color:#f4e4fa}\r\n#coachDock .dock-control>span{width:36px;height:36px;display:grid;place-items:center;color:#f6e5d5;background:linear-gradient(145deg,#a58a9c,#5a4560 55%,#3b2c42);box-shadow:inset 2px 2px #ffffff40,inset -2px -3px #0006}\r\n#coachDock .dock-settings>span{border-radius:50%}\r\n#coachDock .dock-food>span{width:40px;height:30px;border-radius:5px 5px 9px 9px}\r\n#coachDock .dock-scoreboard>span{border-radius:14px 14px 6px 6px}\r\n#coachDock .dock-achievements>span{clip-path:polygon(0 0,100% 0,100% 55%,50% 100%,0 55%);background:linear-gradient(145deg,#f2c46f,#b98033)}\r\n#coachDock .dock-pod>span{width:30px;border-radius:15px}\r\n#coachDock.coach-dock a.dock-control span{pointer-events:none}\r\n#coachDock.coach-dock a.dock-portal{position:absolute;grid-row:1;left:50%;top:50%;translate:-50% -50%;justify-self:center;box-sizing:border-box;width:62px;height:62px;min-width:62px;padding:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;border:2px solid #9a8295;border-top-color:#cbb5c5;border-bottom-color:#4b384d;border-radius:4px;background:linear-gradient(145deg,#806979,#4a394e 80%);box-shadow:0 0 0 3px #1c1421,0 6px 0 2px #130d18,inset 1px 2px #e0c3d366,inset -2px -3px #25182c99;color:#fff;font-size:10px;line-height:1}\r\n#coachDock.coach-dock svg{display:block}\r\n.editor-shell{translate:var(--editor-peer-x,0px) var(--editor-peer-y,0px);rotate:var(--editor-peer-tilt,0deg);}\r\n@media(prefers-reduced-motion:reduce){.editor-shell{translate:none!important;rotate:none!important}#portalChrome.portal-standalone .portal-standalone-aura{opacity:.8}}\r\n","modules/portal/standalone-housing.mjs":"import {initShell} from '../shell/owner.mjs';\r\nimport {readLook,applyLookVars,stripSeq,frameHtml} from './portal-look.mjs';\r\nimport {requestPhoneMotion,createTiltLook} from '../../character-phone-sensor.mjs';\r\n\r\nexport const frameMarkup=()=>frameHtml()+'<div class=\"portal-standalone-aura\" aria-hidden=\"true\"></div>';\r\n\r\n/** Applies the saved metal colour and strip colour to a frameMarkup() host (shared with the camera workout). */\r\nexport function paintHousing(chrome,doc=document){const look=readLook();applyLookVars(doc.documentElement,look);chrome.style.setProperty('--portal-strip-glow',`${look.strip}40`);chrome.querySelector('.portal-standalone-aura')?.style.setProperty('--aura',look.strip);const seq=stripSeq(look.strip);for(const [i,channel] of [...chrome.querySelectorAll('.portal-energy>span')].entries()){channel.style.setProperty('--dir',i%2?'180deg':'90deg');channel.firstElementChild.style.background=`repeating-linear-gradient(${i%2?'180deg':'90deg'},${seq.map((color,j)=>`${color} ${j*90}px ${(j+1)*90}px`).join(',')})`;}}\r\n\r\n/** A lightweight persistent frame for the separately-loaded coach customizer. */\r\nexport async function mountStandaloneHousing({content=document.querySelector('.editor-shell'),doc=document}={}){\r\n if(!content||!doc)return ()=>{};\r\n const shell=initShell({doc});content.classList.add('shell-workbench');\r\n const view=doc.defaultView;let leftEarly=false;\r\n const beforeReady=event=>{if(!event.persisted)leftEarly=true;};view?.addEventListener('pagehide',beforeReady);\r\n const loadCss=href=>new Promise((resolve,reject)=>{const link=doc.createElement('link');link.rel='stylesheet';link.href=href;link.onload=()=>resolve(link);link.onerror=()=>reject(Error(`Could not load ${href}`));doc.head.append(link);});\r\n const links=await Promise.all(['/modules/shell/shell.css','/modules/portal/portal.css','/modules/portal/standalone-housing.css'].map(loadCss));\r\n if(leftEarly){links.forEach(link=>link.remove());view?.removeEventListener('pagehide',beforeReady);return ()=>{};}\r\n doc.getElementById('coachDock')?.classList.add('ship-control-deck');\r\n let chrome=doc.getElementById('portalChrome');if(!chrome){chrome=doc.createElement('div');chrome.id='portalChrome';chrome.setAttribute('aria-hidden','true');doc.body.append(chrome);}\r\n chrome.classList.add('portal-standalone');chrome.innerHTML=frameMarkup();\r\n const face=()=>{const rail=15;for(const [key,value] of Object.entries({left:rail,top:rail,width:Math.max(1,innerWidth-2*rail),height:Math.max(1,innerHeight-2*rail-(doc.getElementById('coachDock')?.getBoundingClientRect().height||0))}))chrome.style.setProperty('--face-'+key,value+'px');};\r\n const updateLook=()=>paintHousing(chrome,doc);\r\n face();updateLook();\r\n chrome.removeAttribute('popover');shell?.refresh();\r\n const reduced=view?.matchMedia?.('(prefers-reduced-motion: reduce)');let raf=0,tx=0,ty=0,x=0,y=0,disposed=false,last=0;const look=createTiltLook(view);\r\n const requestTilt=()=>{if(!disposed&&!raf&&!doc.hidden&&!reduced?.matches)raf=requestAnimationFrame(step);};\r\n const pointer=e=>{if(disposed||reduced?.matches||doc.hidden)return;tx=Math.max(-1,Math.min(1,(e.clientX/Math.max(1,view.innerWidth)-.5)*2));ty=Math.max(-1,Math.min(1,(e.clientY/Math.max(1,view.innerHeight)-.5)*2));requestTilt();};\r\n const step=now=>{raf=0;const k=last?1-Math.exp(-Math.min(100,now-last)/300):.055;last=now;x+=(tx-x)*k;y+=(ty-y)*k;content.style.setProperty('--editor-peer-x',(x*2.5).toFixed(2)+'px');content.style.setProperty('--editor-peer-y',(y*2.5).toFixed(2)+'px');content.style.setProperty('--editor-peer-tilt',(x*.65).toFixed(2)+'deg');if(Math.abs(tx-x)+Math.abs(ty-y)>.01)raf=requestAnimationFrame(step);else last=0;};\r\n const orientation=event=>{if(disposed||reduced?.matches||doc.hidden)return;const t=look.read(event,view.performance.now());if(!t)return;tx=t.x;ty=t.y;requestTilt();};\r\n const allowTilt=async()=>{try{const granted=await requestPhoneMotion();if(disposed)return;if(granted){view.addEventListener('deviceorientation',orientation);chip?.remove();}}catch{}}\r\n const chip=view?.DeviceOrientationEvent?.requestPermission?doc.createElement('button'):null;\r\n if(chip){chip.type='button';chip.className='editor-tilt-request';chip.textContent='Enable tilt';chip.setAttribute('aria-label','Enable device tilt to look around');(doc.querySelector('.view-buttons')||doc.querySelector('.preview-toolbar')||content).append(chip);chip.addEventListener('click',allowTilt);}\r\n else if(view?.DeviceOrientationEvent)view.addEventListener('deviceorientation',orientation);\r\n const resize=()=>face(),storage=e=>{if(!e.key||e.key.startsWith('myr5.portal'))updateLook();};\r\n const clearPeer=()=>{tx=ty=x=y=0;content.style.removeProperty('--editor-peer-x');content.style.removeProperty('--editor-peer-y');content.style.removeProperty('--editor-peer-tilt');};\r\n const onVisibility=()=>{look.reset();if(doc.hidden){clearPeer();cancelAnimationFrame(raf);raf=0;last=0;}else requestTilt();};\r\n const onReduced=()=>{if(reduced.matches){clearPeer();cancelAnimationFrame(raf);raf=0;last=0;}else requestTilt();};\r\n const dispose=()=>{if(disposed)return;disposed=true;cancelAnimationFrame(raf);doc.removeEventListener('visibilitychange',onVisibility);view?.removeEventListener('pagehide',pagehide);view?.removeEventListener('pointermove',pointer);view?.removeEventListener('resize',resize);view?.removeEventListener('storage',storage);view?.removeEventListener('focus',updateLook);view?.removeEventListener('deviceorientation',orientation);reduced?.removeEventListener?.('change',onReduced);chip?.remove();clearPeer();chrome.remove();links.forEach(link=>link.remove());};\r\n const pagehide=event=>{if(!event.persisted)dispose();};\r\n view?.removeEventListener('pagehide',beforeReady);view?.addEventListener('pagehide',pagehide);view?.addEventListener('pointermove',pointer,{passive:true});view?.addEventListener('resize',resize);view?.addEventListener('storage',storage);view?.addEventListener('focus',updateLook);doc.addEventListener('visibilitychange',onVisibility);reduced?.addEventListener?.('change',onReduced);\r\n return dispose;\r\n}\r\n","modules/roguelite/ar-room-shell.mjs":"import {createRoomAtmosphere} from './room-atmosphere.mjs';\nimport {createPs1Material} from '../coach-world-rts/ps1-material.mjs';\n\n// Borrow scene/camera/surfaces/maps. Own only the missing enclosure and atmosphere.\nexport function createARRoomShell(T,{scene,camera,bounds={minX:-3.5,maxX:3.5,minY:-.02,maxY:4,minZ:-6,maxZ:2},existingFloor=null,existingSurfaces={},textures={},color=0x777b72,seed=101,reducedMotion=false,persistentBanks}={}){\n if(!scene?.isScene||!camera?.isCamera)throw new TypeError('AR shell needs a scene and camera.');\n const {minX,maxX,minY,maxY,minZ,maxZ}=bounds;\n if(![minX,maxX,minY,maxY,minZ,maxZ,seed].every(Number.isFinite)||minX>=maxX||minY>=maxY||minZ>=maxZ)throw new RangeError('AR shell bounds must be finite and ordered.');\n const w=maxX-minX,h=maxY-minY,d=maxZ-minZ,x=(minX+maxX)/2,y=(minY+maxY)/2,z=(minZ+maxZ)/2;\n // PlaneGeometry faces +Z; rotations below point every face into the room.\n const faces={floor:[w,d,[x,minY,z],[-Math.PI/2,0,0]],ceiling:[w,d,[x,maxY,z],[Math.PI/2,0,0]],front:[w,h,[x,y,minZ],[0,0,0]],rear:[w,h,[x,y,maxZ],[0,Math.PI,0]],left:[d,h,[minX,y,z],[0,Math.PI/2,0]],right:[d,h,[maxX,y,z],[0,-Math.PI/2,0]]};\n for(const face of Object.keys(faces)){\n  const mesh=existingSurfaces[face]||(face==='floor'?existingFloor:null);\n  if(mesh&&!mesh.isMesh)throw new TypeError(`Expected an existing ${face} mesh.`);\n  const map=textures[face]||textures.wall;if(map&&!map.isTexture)throw new TypeError(`Expected a ${face} texture.`);\n }\n const r=Math.min(1.5,w*.22,d*.22),inset=r*.65;\n const lowBanks=persistentBanks??[minX+inset,maxX-inset].flatMap((bx,i)=>[minZ+inset,z,maxZ-inset].map((bz,j)=>({id:`ar-floor-${i}-${j}`,x:bx,y:minY-.2,z:bz,r})));\n // Validate/construct atmosphere first, before adding any enclosure resources.\n const atmosphere=createRoomAtmosphere(T,{scene,camera,color,seed,reducedMotion,persistentBanks:lowBanks});\n const meshes=[],maps=new Set();let disposed=false;\n for(const [face,[width,height,position,rotation]] of Object.entries(faces)){\n  if(existingSurfaces[face]||(face==='floor'&&existingFloor))continue;\n  let map=textures[face]||textures.wall;\n  if(!map){\n   // Reuse run-scene's tiny deterministic baked tile approach: no image fetch/pass.\n   const pixels=new Uint8Array(32*32*4);\n   for(let py=0;py<32;py++)for(let px=0;px<32;px++){\n    const i=(py*32+px)*4,shade=(px%8===0||py%8===0?135:200)+((px*17+py*31+seed)%35);\n    pixels[i]=pixels[i+1]=pixels[i+2]=shade;pixels[i+3]=255;\n   }\n   map=new T.DataTexture(pixels,32,32,T.RGBAFormat);map.wrapS=map.wrapT=T.RepeatWrapping;map.repeat.set(width/2,height/2);\n   map.minFilter=map.magFilter=T.NearestFilter;map.generateMipmaps=false;map.needsUpdate=true;maps.add(map);\n  }\n  const material=createPs1Material(T,{map,vertexColors:false,affine:false,snap:false});material.color.set(color);\n  const mesh=new T.Mesh(new T.PlaneGeometry(width,height),material);mesh.name=`ar-room-${face}`;\n  mesh.position.set(...position);mesh.rotation.set(...rotation);scene.add(mesh);meshes.push(mesh);\n }\n return {\n  update:(dt,options)=>{if(!disposed)atmosphere.update(dt,options);},\n  setLoading:on=>{if(!disposed)atmosphere.setLoading(on);},\n  dispose(){\n   if(disposed)return;disposed=true;atmosphere.dispose();\n   for(const mesh of meshes){mesh.removeFromParent();mesh.geometry.dispose();mesh.material.dispose();}\n   for(const map of maps)map.dispose();\n  },\n };\n}\n","modules/roguelite/arcade-input.mjs":"// Small room-specific pose bridge. It reuses the shipped detectors and emits\n// only normalized controls; it does not own cameras, scoring, or game state.\nimport {createCoverInput} from '../../arcade/cover/cover-input.mjs';\nimport {ArmActivity} from '../dance/android-game/activity.mjs';\nimport {createPoseInput} from '../../arcade/runner/pose-input.mjs';\n\nconst RESET_GAP_MS=250;\nconst ROOM_IDS=new Set(['clay','dance','jelly']);\nconst visible=p=>p&&(p.visibility??1)>=.5&&Number.isFinite(p.x)&&Number.isFinite(p.y);\nconst bodyVisible=points=>Array.isArray(points)&&points.length>=17&&[0,11,12].every(index=>visible(points[index]));\nconst emptyJelly=()=>({hidden:false,side:0,beam:{on:false,x:0},zones:{left:0,middle:0,right:0,up:0},fires:[]});\nconst emptyDance=()=>({energy:0,coverage:0});\nconst emptyClay=()=>({actions:[]});\n\nfunction cleanJelly(sample){\n const beam=sample.beam??{},zones=sample.zones??{};\n const sourceFires=sample.fire??[];\n if(!Array.isArray(sourceFires)||sourceFires.length>2)throw new TypeError('Jelly input requires at most two fire samples.');\n const fires=sourceFires.map(fire=>{\n  if(!fire||!Number.isFinite(fire.x)||!Number.isFinite(fire.power))throw new TypeError('Jelly fire samples must have finite aim and power.');\n  return {aimX:Math.max(-1,Math.min(1,fire.x)),power:Math.max(0,Math.min(100,fire.power))};\n });\n return {hidden:sample.hidden===true,side:sample.side===-1?-1:sample.side===1?1:0,\n  beam:{on:beam.on===true,x:Number.isFinite(beam.x)?Math.max(-1,Math.min(1,beam.x)):0},\n  zones:Object.fromEntries(['left','middle','right','up'].map(key=>[key,Number.isFinite(zones[key])?Math.max(0,Math.min(1,zones[key])):0])),fires};\n}\n\n/**\n * `update` accepts one normalized pose frame `{points,world,width,height,mirrored,now,tracked,paused,enabled,generation}`.\n * Clay maps only existing jump/slide/lane actions; it does not represent burpee completion.\n * Jelly preserves up to two same-frame hand punches in `arcade.fires`.\n */\nexport function createArcadeInput(roomId){\n if(!ROOM_IDS.has(roomId))throw new RangeError(`Unsupported arcade input room: ${roomId}`);\n let cover=roomId==='jelly'?createCoverInput():null;\n let dance=roomId==='dance'?new ArmActivity():null;\n let clay=roomId==='clay'?createPoseInput():null;\n let lastNow=null,wasPaused=false;\n function reset(){cover?.reset();dance?.reset();clay=roomId==='clay'?createPoseInput():null;lastNow=null;wasPaused=false;}\n function update(frame={}){\n  if(!frame||typeof frame!=='object'||!Number.isFinite(frame.now)||frame.now<0||typeof frame.tracked!=='boolean')throw new TypeError('Arcade input requires a finite timestamp and tracked flag.');\n  const now=frame.now,paused=frame.paused===true||frame.enabled===false||!frame.tracked;\n  const gap=lastNow!==null&&(now<=lastNow||now-lastNow>RESET_GAP_MS);\n  if(paused||gap){cover?.reset();dance?.reset();if(roomId==='clay')clay=createPoseInput();wasPaused=paused;lastNow=paused?null:now;\n   return {tracked:!paused&&frame.tracked,arcade:roomId==='jelly'?emptyJelly():roomId==='dance'?emptyDance():emptyClay()};}\n  if(wasPaused){reset();lastNow=now;wasPaused=false;return {tracked:frame.tracked,arcade:roomId==='jelly'?emptyJelly():roomId==='dance'?emptyDance():emptyClay()};}\n  lastNow=now;\n  if(roomId==='jelly'){\n   const sample=cover.update({points:frame.points??null,world:frame.world??null,width:frame.width??1,height:frame.height??1,mirrored:frame.mirrored!==false,now});\n   return {tracked:frame.tracked,arcade:cleanJelly(sample)};\n  }\n  if(roomId==='dance'){\n   dance.ingest(frame.points??null,now,{enabled:frame.tracked,generation:Number.isSafeInteger(frame.generation)?frame.generation:0});\n   const activity=dance.snapshot(now);\n   return {tracked:frame.tracked&&activity.tracking,arcade:{energy:activity.energy,coverage:activity.coverage}};\n  }\n  const pose=clay.update({points:frame.points??null,world:frame.world??null,width:frame.width??1,height:frame.height??1,now});\n  return {tracked:frame.tracked&&bodyVisible(frame.points),arcade:{actions:(pose.actions??[]).filter(action=>['jump','slide','left','right'].includes(action))}};\n }\n return {roomId,update,reset};\n}\n","modules/roguelite/boons.mjs":"// Per-run boon receipts and derived effects. Catalog references preserve the\n// existing game power IDs; this module does not activate or replace those engines.\nimport { POWERS as RUNNER_POWERS } from '../../arcade/runner/worlds/worlds.mjs';\nimport { POWERUPS as FLIGHT_POWERUPS } from '../../arcade/plank-flight/catalog.mjs';\n\nexport const BOON_TUNING = Object.freeze({\n  overshieldCharges: 1,\n  parryWindowFactor: 1.5,\n  bossDamageFactor: 1.25,\n  baseCatchWindows: 2,\n  flightBoonCatchWindows: 3,\n});\n\nexport const BOONS = Object.freeze({\n  overshield: Object.freeze({ id: 'overshield', label: 'Lifting Belt ? Overshield', effects: Object.freeze({ overshieldCharges: BOON_TUNING.overshieldCharges }), existingPowerIds: Object.freeze(['runner:shield', 'plank:aegis-pulse']) }),\n  longerParry: Object.freeze({ id: 'longerParry', label: 'Reaction Ball ? Longer Parry', effects: Object.freeze({ parryWindowFactor: BOON_TUNING.parryWindowFactor }), existingPowerIds: Object.freeze(['runner:phase', 'runner:recovery']) }),\n  bossPunch: Object.freeze({ id: 'bossPunch', label: 'Lifting Chalk ? Punch Power', effects: Object.freeze({ bossDamageFactor: BOON_TUNING.bossDamageFactor }), existingPowerIds: Object.freeze(['runner:boost', 'plank:visual-afterburn']) }),\n  flightClosing: Object.freeze({ id: 'flightClosing', label: 'Sprint Spikes ? Closing Speed', effects: Object.freeze({ chaseCatchWindows: BOON_TUNING.flightBoonCatchWindows }), existingPowerIds: Object.freeze(['runner:boost', 'runner:jetpack', 'plank:visual-afterburn']) }),\n});\n\nconst BOON_IDS = new Set(Object.keys(BOONS));\n// Shared NPC start receipts include a full account ID plus their namespace/relic.\nconst safeId = value => typeof value === 'string' && value.length > 0 && value.length <= 256;\nconst existingIds = new Set([\n  ...Object.keys(RUNNER_POWERS).map(id => `runner:${id}`),\n  ...FLIGHT_POWERUPS.map(power => `plank:${power.id}`),\n]);\nfor (const boon of Object.values(BOONS)) {\n  for (const id of boon.existingPowerIds) if (!existingIds.has(id)) throw new Error(`Unknown existing power reference: ${id}`);\n}\n\nexport function createBoonInventory() {\n  return { version: 1, receipts: [] };\n}\n\n/** Grant once per stable reward receipt. Repeated receipts cannot stack an effect. */\nexport function grantBoon(inventory, boonId, receiptId) {\n  if (!inventory || inventory.version !== 1 || !Array.isArray(inventory.receipts)) throw new TypeError('invalid boon inventory');\n  if (!BOON_IDS.has(boonId)) throw new RangeError(`unknown boon: ${boonId}`);\n  if (!safeId(receiptId)) throw new TypeError('receiptId must be a stable string id');\n  const prior = inventory.receipts.find(receipt => receipt.receiptId === receiptId);\n  if (prior) {\n    if (prior.boonId !== boonId) throw new RangeError('receipt already belongs to a different boon');\n    return { inventory: structuredClone(inventory), granted: false };\n  }\n  return { inventory: { version: 1, receipts: [...inventory.receipts, { boonId, receiptId }] }, granted: true };\n}\n\nexport function restoreBoonInventory(saved) {\n  if (!saved || saved.version !== 1 || !Array.isArray(saved.receipts)) throw new TypeError('invalid boon inventory save');\n  const receipts = [], seen = new Set();\n  for (const receipt of saved.receipts) {\n    if (!receipt || !BOON_IDS.has(receipt.boonId) || !safeId(receipt.receiptId) || seen.has(receipt.receiptId)) throw new RangeError('invalid or duplicate boon receipt');\n    seen.add(receipt.receiptId); receipts.push({ boonId: receipt.boonId, receiptId: receipt.receiptId });\n  }\n  return { version: 1, receipts };\n}\n\nexport function boonEffects(inventory) {\n  const valid = restoreBoonInventory(inventory);\n  const ids = new Set(valid.receipts.map(receipt => receipt.boonId));\n  return {\n    overshieldCharges: ids.has('overshield') ? BOON_TUNING.overshieldCharges : 0,\n    parryWindowFactor: ids.has('longerParry') ? BOON_TUNING.parryWindowFactor : 1,\n    bossDamageFactor: ids.has('bossPunch') ? BOON_TUNING.bossDamageFactor : 1,\n    chaseCatchWindows: ids.has('flightClosing') ? BOON_TUNING.flightBoonCatchWindows : BOON_TUNING.baseCatchWindows,\n  };\n}\n","modules/roguelite/boss-door.mjs":"// Reuse the delivered crystal board's 2D trace/art in the run renderer's overlay.\n// The exact scheduled coach is visible through it; no second WebGL context.\nimport {createQuiltBoard2D} from '../portal/portal-board.mjs';\nimport {ice} from '../portal/portal-board-ice.mjs';\nimport {coachPresentationForRoom} from './coach-presentation.mjs';\nimport {physicalSound} from '../../audio/physical-sound.mjs';\n\nexport async function mountBossDoor(host,node,{signal,scheduleVersion=1}={}){\n const coach=coachPresentationForRoom(scheduleVersion,node),overlay=document.createElement('section');\n overlay.className='run-boss-door';overlay.setAttribute('aria-label','Crystal boss entrance');\n const label=document.createElement('h2');label.textContent='PUNCH THE CRYSTAL';\n const name=document.createElement('p');name.textContent=coach.label;\n const frame=document.createElement('div');frame.className='run-crystal-frame';\n const preview=document.createElement('canvas');preview.className='run-boss-preview';frame.append(preview);\n const poster=document.createElement('div');poster.className='run-crystal-poster';frame.append(poster);\n const instruction=document.createElement('p');instruction.textContent='The boss waits inside. Punch to enter.';\n overlay.append(label,name,frame,instruction);host.append(overlay);\n let board=null,disposed=false,confirmed=false,opening=false,elapsed=0,lastNow=null,resolve,reject;\n const confirmation=new Promise((yes,no)=>{resolve=yes;reject=no;});confirmation.catch(()=>{});\n function dispose(){if(disposed)return;disposed=true;signal?.removeEventListener('abort',abort);board?.dispose();overlay.remove();if(!confirmed)reject(new DOMException('Boss entrance cancelled.','AbortError'));}\n function abort(){dispose();}\n signal?.addEventListener('abort',abort,{once:true});\n try{\n  if(signal?.aborted)throw new DOMException('Boss entrance cancelled.','AbortError');\n  const image=new Image();image.src=coach.atlasPath;await image.decode();\n  if(disposed)throw new DOMException('Boss entrance cancelled.','AbortError');\n  preview.width=coach.frame[2];preview.height=coach.frame[3];preview.getContext('2d').drawImage(image,...coach.frame,0,0,preview.width,preview.height);\n  board=await createQuiltBoard2D(poster,{src:'/pod/worlds/boards/ice-poster.webp',ratio:.5903,background:ice.background,frame:{x0:.035,y0:.025,x1:.965,y1:.78},trace:ice.trace2d,waitMs:6000});\n  if(disposed){board.dispose();throw new DOMException('Boss entrance cancelled.','AbortError');}\n  return {element:overlay,confirmation,dispose,\n   confirm(){if(disposed||opening||confirmed)return false;opening=true;overlay.dataset.opening='true';instruction.textContent='The crystal fractures. The coach is waking.';physicalSound.play('ice');const b=poster.getBoundingClientRect();board.press('punch',b.x+b.width/2,b.y+b.height/2);board.release('punch');return true;},\n   render({paused=false,now=performance.now()}={}){\n    if(disposed||!opening||confirmed)return;\n    if(paused){lastNow=null;return;}\n    const dt=lastNow===null?0:Math.min(100,Math.max(0,now-lastNow));lastNow=now;elapsed+=dt;\n    overlay.style.setProperty('--crystal-break',String(Math.min(1,elapsed/700)));\n    if(elapsed>=700){confirmed=true;resolve();}\n   }};\n }catch(error){dispose();throw error;}\n}\n","modules/roguelite/boss-finisher.mjs":"import {cinematicFinisherComplete} from './boss-state.mjs';\nimport {currentRunRoom,resolveRunBoss} from './run-controller.mjs';\nimport {coachForRoomNodeForVersion} from './run-schedule-v2-builder.mjs';\n\nconst unchanged=(state,boss,reason)=>({state,boss,applied:false,reason});\n\n/** Resolve a chip finisher transactionally: both reducers run on local values first. */\nexport function finishBossChip(state,boss){\n if(!state||typeof state!=='object'||!boss||typeof boss!=='object')return unchanged(state,boss,'invalid-input');\n if(boss.status==='complete')return unchanged(state,boss,'already-complete');\n if(boss.status!=='finisher')return unchanged(state,boss,'finisher-inactive');\n if(!state.stats||!Number.isFinite(state.stats.health)||!Array.isArray(state.pauseReasons))return unchanged(state,boss,'invalid-run-state');\n if(state.stats.health<=0)return unchanged(state,boss,'health-depleted');\n if(state.pauseReasons.length)return unchanged(state,boss,'paused');\n if(state.phase!=='active')return unchanged(state,boss,'run-inactive');\n let node,expectedCoach;\n const scheduleVersion=state.schedule?.scheduleVersion??1;\n try{node=currentRunRoom(state);const resolved=node&&coachForRoomNodeForVersion(scheduleVersion,node);expectedCoach=node&&(scheduleVersion>=2?resolved.sourceVisualId:resolved.id);}catch{return unchanged(state,boss,'invalid-finisher');}\n if(!node||boss.bossId!==node.id||boss.kind!==node.type)return unchanged(state,boss,'invalid-finisher');\n if(node.type==='mainboss'&&(boss.phase!==2||boss.phase2Pending===true))return unchanged(state,boss,'phase-two-required');\n if(boss.coachId!==expectedCoach)return unchanged(state,boss,'invalid-finisher');\n if(typeof state.id!=='string'||!state.id||typeof boss.bossId!=='string'||!boss.bossId||typeof boss.coachId!=='string'||!boss.coachId)\n return unchanged(state,boss,'invalid-finisher');\n try{\n  const stagedState=structuredClone(state),stagedBoss=structuredClone(boss);\n  const completedBoss=cinematicFinisherComplete(stagedBoss,{id:`chip:${state.id}:${boss.bossId}`,coachId:boss.coachId,bossId:boss.bossId});\n  const completedRun=resolveRunBoss(stagedState,completedBoss);\n  return {state:completedRun,boss:completedBoss,applied:true};\n }catch{\n  return unchanged(state,boss,'reducer-rejected');\n }\n}\n","modules/roguelite/boss-run-benefits.mjs":"import {armorTiming} from './run-stats.mjs';\nimport {heartEffects} from './coach-hearts.mjs';\n// Pure adapter from per-run stats/boons to one boss encounter's derived options.\r\nimport { BOSS_TIMINGS, validateTimings } from './boss-state.mjs';\r\nimport { BOON_TUNING, boonEffects, restoreBoonInventory } from './boons.mjs';\r\n\r\n/** Return encounter-local combat values without changing shared timings or run state. */\r\nexport function bossCombatOptions(stats, boons, heartModifiers=null, secretEffects=[]) {\n  if (!stats || typeof stats !== 'object' || !Number.isFinite(stats.punchDamage) || stats.punchDamage <= 0) {\r\n    throw new TypeError('stats.punchDamage must be finite and positive');\r\n  }\r\n  const inventory = restoreBoonInventory(boons);\r\n  const effects = boonEffects(inventory);\n  const heart=heartEffects(heartModifiers);\n  if(!Array.isArray(secretEffects)||secretEffects.length>128)throw new TypeError('Invalid secret effects');\n  let secretDamage=1,secretWindow=1;const secretReceipts=new Set();\n  for(const entry of secretEffects){\n    const wrapped=Object.hasOwn(entry??{},'effect'),effect=wrapped?entry.effect:entry;\n    if(wrapped){\n      if(!entry||Object.keys(entry).length!==3||typeof entry.receiptId!=='string'||!entry.receiptId.length||entry.receiptId.length>128||secretReceipts.has(entry.receiptId)||!['ruin-fist','patient-idol'].includes(entry.choiceId)\n        ||effect?.damageMultiplier!==(entry.choiceId==='ruin-fist'?2:.5)||effect?.parryWindowMultiplier!==(entry.choiceId==='ruin-fist'?.5:2))throw new TypeError('Invalid secret receipt');\n      secretReceipts.add(entry.receiptId);\n    }\n    if(!effect||Object.keys(effect).sort().join(',')!=='damageMultiplier,minParryWindowMs,parryWindowMultiplier,scope,version'||effect.version!==1||effect.scope!=='run'||![[2,0.5],[0.5,2]].some(([d,w])=>effect.damageMultiplier===d&&effect.parryWindowMultiplier===w)||effect.minParryWindowMs!==700)throw new TypeError('Invalid secret effect');\n    secretDamage*=effect.damageMultiplier;secretWindow*=effect.parryWindowMultiplier;\n  }\n  // New-policy passive bonuses share caps; explicit Secret tradeoffs remain separate.\n  const passiveDamage=heartModifiers?Math.min(1.5,effects.bossDamageFactor*heart.damageMultiplier):effects.bossDamageFactor;\n  const passiveWindow=heartModifiers?Math.min(1.5,effects.parryWindowFactor*heart.responseMultiplier):effects.parryWindowFactor;\n  const punchDamage = stats.punchDamage * passiveDamage * secretDamage;\n  if (!Number.isFinite(punchDamage) || punchDamage <= 0) throw new RangeError('derived boss punch damage must be finite and positive');\r\n  const timings = structuredClone(BOSS_TIMINGS);\r\n  for (const boss of ['miniboss', 'mainboss']) {\r\n    for (const phase of Object.keys(timings[boss])) {\r\n      if (!timings[boss][phase] || typeof timings[boss][phase] !== 'object') continue;\r\n      if (Number.isFinite(timings[boss][phase].windowMs)) {\r\n        if(heartModifiers)timings[boss][phase].windowMs=Math.max(timings[boss][phase].windowMs,armorTiming(stats,heartModifiers).responseMs)*passiveWindow;\n        else timings[boss][phase].windowMs*=passiveWindow;\n        if(stats.rulesVersion===2){timings[boss][phase].windowMs=Math.max(700,Math.max(armorTiming(stats,heartModifiers).responseMs,timings[boss][phase].windowMs)*secretWindow);timings[boss][phase].telegraphMs=Math.max(1000,timings[boss][phase].telegraphMs);}\n        else if(secretEffects.length)timings[boss][phase].windowMs=Math.max(700,timings[boss][phase].windowMs*secretWindow);\n        if (!Number.isFinite(timings[boss][phase].windowMs)) throw new RangeError('derived boss parry window is not finite');\r\n      }\r\n    }\r\n  }\r\n  validateTimings(timings);\r\n  return { punchDamage, timings, ...(heartModifiers?{actionRecoveryScale:armorTiming(stats,heartModifiers).recoveryScale*heart.recoveryMultiplier}:{}) };\n}\r\n\r\nconst validReceipt = value => typeof value === 'string' && value.length > 0 && value.length <= 128;\r\n\r\nfunction checkedProtection(protection, cap = BOON_TUNING.overshieldCharges) {\r\n  if (!protection || typeof protection !== 'object' || Array.isArray(protection)\r\n      || Object.keys(protection).length !== 2 || !Object.hasOwn(protection, 'charges') || !Object.hasOwn(protection, 'receipts')\r\n      || !Number.isSafeInteger(protection.charges) || protection.charges < 0 || protection.charges > cap\r\n      || !Array.isArray(protection.receipts)) throw new RangeError('invalid boss protection state');\r\n  const seen = new Set();\r\n  const receipts = protection.receipts.map(item => {\r\n    if (!item || typeof item !== 'object' || Array.isArray(item) || Object.keys(item).length !== 2\r\n        || !validReceipt(item.receiptId) || seen.has(item.receiptId) || typeof item.blocked !== 'boolean') {\r\n      throw new RangeError('invalid or duplicate boss protection receipt');\r\n    }\r\n    seen.add(item.receiptId);\r\n    return { receiptId: item.receiptId, blocked: item.blocked };\r\n  });\r\n  return { charges: protection.charges, receipts };\r\n}\r\n\r\n/** Initialize once for an encounter; a saved record is validated and never recharged. */\r\nexport function initializeBossProtection(boss, boons) {\r\n  if (!boss || typeof boss !== 'object' || Array.isArray(boss)) throw new TypeError('boss encounter state is required');\r\n  const effects = boonEffects(restoreBoonInventory(boons));\r\n  const next = structuredClone(boss);\r\n  next.protection = Object.hasOwn(boss, 'protection')\r\n    ? checkedProtection(boss.protection, effects.overshieldCharges)\r\n    : { charges: effects.overshieldCharges, receipts: [] };\r\n  return next;\r\n}\r\n\r\n/** Overshield absorbs before luck/HP handling; caller handles unblocked damage. */\r\nexport function resolveBossProtection(boss, receiptId) {\r\n  if (!boss || typeof boss !== 'object' || Array.isArray(boss)) throw new TypeError('boss encounter state is required');\r\n  if (!validReceipt(receiptId)) throw new TypeError('receiptId must be a stable string id');\r\n  if (!Object.hasOwn(boss, 'protection')) throw new RangeError('boss protection must be initialized first');\r\n  const next = structuredClone(boss);\r\n  next.protection = checkedProtection(boss.protection);\r\n  const previous = next.protection.receipts.find(item => item.receiptId === receiptId);\r\n  if (previous) return { boss: next, blocked: previous.blocked, duplicate: true };\r\n  const blocked = next.protection.charges > 0;\r\n  if (blocked) next.protection.charges--;\r\n  next.protection.receipts.push({ receiptId, blocked });\r\n  return { boss: next, blocked, duplicate: false };\r\n}\r\n","modules/roguelite/boss-state.mjs":"// Pure boss encounter state for the roguelite. No DOM, THREE, clock or weapon catalog here:\r\n// callers pass calibrated observations plus coach/weapon metadata, and get plain JSON state back.\r\n// Attack answers: yellow = fresh step-left parry (held left blocks); red = held jump/crouch dodge.\r\n// Opening window = the stun after a parry; punch edges during it damage the boss, nothing else does.\r\n// BOSS_TIMINGS are PROPOSALS, not phone-validated. Tune on device.\nimport {createClassCombat,pauseClassCombat,restoreClassCombat,classPhaseHp,beginClassPhase,stepClassEncounter} from './class-combat.mjs';\n\r\nexport const REASONS = Object.freeze(['stale', 'lost', 'tutorial', 'background']);\r\nexport const STATUS = Object.freeze(['fighting', 'pursuit', 'escaped', 'finisher', 'complete']);\r\nexport const ATTACK_KINDS = Object.freeze({\n  yellow_blockable: Object.freeze({ color: 'yellow', blockable: true, answer: 'parry' }),\r\n  red_jump_environment: Object.freeze({ color: 'red', blockable: false, answer: 'jump' }),\r\n  red_crouch_boss: Object.freeze({ color: 'red', blockable: false, answer: 'crouch' }),\r\n});\n// Each phase's HP proposal is derived from its existing hit count and this base HP unit.\nexport const BOSS_DAMAGE_TUNING_PROPOSAL = Object.freeze({ hpPerBaseHit: 10 });\n// What each boss phase may author. Mini never jumps or jogs; main phase 1 is yellow only.\r\nconst ALLOWED = Object.freeze({\r\n  miniboss: Object.freeze({ phase1: Object.freeze(['yellow_blockable', 'red_crouch_boss']) }),\r\n  mainboss: Object.freeze({\r\n    phase1: Object.freeze(['yellow_blockable']),\r\n    phase2: Object.freeze(['yellow_blockable', 'red_jump_environment', 'red_crouch_boss']),\r\n  }),\r\n});\r\nconst DURATION = Object.freeze({ telegraph: 'telegraphMs', window: 'windowMs', opening: 'openingMs', recovery: 'recoveryMs' });\r\nconst OBS_BOOLS = Object.freeze(['tracked', 'left', 'punchLeft', 'punchRight', 'crouch', 'jump', 'jog']);\r\nconst clone = value => JSON.parse(JSON.stringify(value));\r\n\r\n// ponytail: proposal numbers. Phase 2 must be at least as hard as phase 1 (validated below).\r\nexport const BOSS_TIMINGS = Object.freeze({\r\n  staleMs: 300, // observation older than this pauses the encounter clock\r\n  maxStepMs: 100, // clock advance per observation is capped; long gaps add no progress\r\n  miniboss: Object.freeze({\r\n    phase1: Object.freeze({ telegraphMs: 900, windowMs: 700, openingMs: 1200, recoveryMs: 900, hits: 3, script: ['yellow_blockable', 'red_crouch_boss', 'yellow_blockable'] }),\r\n  }),\r\n  mainboss: Object.freeze({\r\n    phase1: Object.freeze({ telegraphMs: 1000, windowMs: 650, openingMs: 1100, recoveryMs: 1000, hits: 4, script: ['yellow_blockable', 'yellow_blockable', 'yellow_blockable'] }),\r\n    phase2: Object.freeze({ telegraphMs: 800, windowMs: 550, openingMs: 1000, recoveryMs: 800, hits: 6, script: ['yellow_blockable', 'red_jump_environment', 'yellow_blockable', 'red_crouch_boss'] }),\r\n    pursuit: Object.freeze({ pursuitMs: 6000, escapeJogMs: 2500 }),\r\n  }),\r\n});\r\n\r\nconst isMs = v => Number.isFinite(v) && v > 0;\r\n\r\nexport function validateTimings(t) {\r\n  if (!isMs(t.staleMs) || !isMs(t.maxStepMs)) throw new RangeError('staleMs and maxStepMs must be positive');\r\n  for (const kind of Object.keys(ALLOWED)) {\r\n    for (const phase of Object.keys(ALLOWED[kind])) {\r\n      const p = t[kind]?.[phase];\r\n      const where = `${kind}.${phase}`;\r\n      if (!p) throw new RangeError(`${where} timings missing`);\r\n      for (const k of Object.values(DURATION)) {\r\n        if (!isMs(p[k]) || p[k] < t.maxStepMs) throw new RangeError(`${where}.${k} must be at least maxStepMs`);\r\n      }\r\n      if (!Number.isSafeInteger(p.hits) || p.hits < 1) throw new RangeError(`${where}.hits must be a positive integer`);\r\n      if (!p.script?.length) throw new RangeError(`${where}.script is empty`);\r\n      for (const a of p.script) if (!ALLOWED[kind][phase].includes(a)) throw new RangeError(`${where} may not author ${a}`);\r\n    }\r\n  }\r\n  const p1 = t.mainboss.phase1;\r\n  const p2 = t.mainboss.phase2;\r\n  if (p2.windowMs > p1.windowMs || p2.telegraphMs > p1.telegraphMs || p2.hits < p1.hits) {\r\n    throw new RangeError('mainboss phase2 must stay at least as hard as phase1');\r\n  }\r\n  const pu = t.mainboss.pursuit;\r\n  if (!isMs(pu?.pursuitMs) || !isMs(pu?.escapeJogMs) || pu.escapeJogMs > pu.pursuitMs) throw new RangeError('pursuit timings invalid');\r\n  return t;\r\n}\r\n\r\nfunction assertObservation(o) {\r\n  if (!o || !Number.isSafeInteger(o.id) || o.id < 0) throw new TypeError('observation id must be a non-negative integer');\r\n  for (const k of ['now', 'ts']) if (!Number.isFinite(o[k])) throw new TypeError(`observation ${k} must be a finite number`);\r\n  if (o.ts > o.now) throw new RangeError('observation ts is after now');\r\n  for (const k of OBS_BOOLS) if (typeof o[k] !== 'boolean') throw new TypeError(`observation ${k} must be boolean`);\r\n}\r\n\r\n// Which REASONS currently pause the clock. Order follows REASONS.\r\nexport function pauseReasons(obs, env = {}, staleMs = BOSS_TIMINGS.staleMs) {\r\n  const out = [];\r\n  if (obs.now - obs.ts > staleMs) out.push('stale');\r\n  if (!obs.tracked) out.push('lost');\r\n  if (env.tutorial) out.push('tutorial');\r\n  if (env.background) out.push('background');\r\n  return REASONS.filter(r => out.includes(r));\r\n}\r\n\r\nexport function createBossEncounter({ kind, bossId, coachId, weaponId, seed = 0, now, timings = BOSS_TIMINGS, punchDamage = 10, classId=null, difficultyLevel=seed, patternSeed=seed }) {\n  if (!ALLOWED[kind]) throw new RangeError(`unknown boss kind ${kind}`);\r\n  for (const [k, v] of Object.entries({ bossId, coachId, weaponId })) {\r\n    if (typeof v !== 'string' || !v) throw new TypeError(`${k} must be a non-empty string`);\r\n  }\r\n  if (!Number.isSafeInteger(seed) || seed < 0) throw new RangeError('seed must be a non-negative integer');\r\n  if (!Number.isFinite(now)) throw new TypeError('now must be a finite number');\n  if (!Number.isFinite(punchDamage) || punchDamage <= 0) throw new RangeError('punchDamage must be finite and positive');\n  const s = {\n    v: 1, kind, bossId, coachId, weaponId, seed, timings: clone(validateTimings(timings)),\n    status: 'fighting', phase: 1, phaseHits: 0, phaseDamage: 0, punchDamage, cue: null, pursuit: null,\n    now, lastObsId: -1, prev: null, pausedBy: [],\r\n    playerHits: 0, lastOutcome: null, levelPassed: false, phase2Pending: false,\r\n    unlockedCoachId: null, unlockReceipt: null,\r\n  };\n  if(classId!==null)s.combat=createClassCombat(classId,{difficultyLevel,patternSeed});\n  beginPhase(s, 1);\n  return s;\r\n}\r\n\r\n// Advances by one observation. Returns the same object for repeated or older observation ids.\r\nexport function advanceBoss(state, obs, env = {}) {\r\n  assertObservation(obs);\r\n  if (obs.id <= state.lastObsId) return state;\r\n  const s = clone(state);\r\n  const reasons = pauseReasons(obs, env, s.timings.staleMs);\r\n  const dt = Math.min(Math.max(0, obs.now - s.now), s.timings.maxStepMs);\r\n  s.now = Math.max(s.now, obs.now);\r\n  s.lastObsId = obs.id;\r\n  s.pausedBy = reasons;\r\n  if (reasons.length) {\n    s.prev = null; // no edges across a pause: no false parry or punch on resume\n    pauseClassCombat(s.combat);\n    return s;\r\n  }\r\n  const cur = Object.fromEntries(OBS_BOOLS.map(k => [k, obs[k]]));\r\n  const prev = s.prev;\r\n  const edge = k => prev !== null && cur[k] && !prev[k];\r\n  const edges = { left: edge('left'), punch: edge('punchLeft') || edge('punchRight') };\r\n  s.prev = cur;\r\n  if (s.status === 'pursuit') stepPursuit(s, dt, cur);\r\n  else if (s.status === 'fighting') {\n    if(s.combat)stepClassEncounter(s,obs,dt,timing(s),defeatPhase);\n    else stepCue(s, dt, cur, edges);\n  }\n  return s;\r\n}\r\n\r\nexport function phaseProgress(s) {\n  const health = Math.max(0, (s.combat?classPhaseHp(s,timing(s)):timing(s).hits * BOSS_DAMAGE_TUNING_PROPOSAL.hpPerBaseHit) - s.phaseDamage);\n  return { phase: s.phase, hits: s.phaseHits, hitsNeeded: timing(s).hits, health, hpRemaining: health, damage: s.phaseDamage };\n}\r\n\r\nexport function openingWindow(s) {\r\n  if (s.cue?.name !== 'opening') return null;\r\n  return { attackNo: s.cue.attackNo, remainingMs: Math.max(0,(s.combat?s.cue.durationMs:timing(s).openingMs) - s.cue.ms) };\n}\r\n\r\n// Escape already returned the player to rooms (status escaped). Phase 2 resumes with the same boss, coach and seed.\r\nexport function returnBoss(state, req) {\r\n  if (state.status !== 'escaped') throw new Error('returnBoss needs an escaped boss');\r\n  if (req.coachId !== state.coachId || req.phase !== 2 || req.seed !== state.seed) {\r\n    throw new Error('returnBoss must match coachId, phase 2 and seed');\r\n  }\r\n  const s = clone(state);\r\n  s.phase2Pending = false;\r\n  beginPhase(s, 2);\r\n  return s;\r\n}\r\n\r\n// Unlock happens only here, once per boss. receipt = { id, coachId, bossId } from the cinematic.\r\nexport function cinematicFinisherComplete(state, receipt) {\r\n  if (!receipt || typeof receipt.id !== 'string' || !receipt.id) throw new TypeError('receipt id required');\r\n  if (receipt.coachId !== state.coachId || receipt.bossId !== state.bossId) throw new Error('receipt does not match this boss');\r\n  if (state.status === 'complete') return state; // chip already broken: duplicate receipts change nothing\r\n  if (state.status !== 'finisher') throw new Error('finisher is not active');\r\n  const s = clone(state);\r\n  s.status = 'complete';\r\n  s.unlockedCoachId = state.coachId;\r\n  s.unlockReceipt = receipt.id;\r\n  return s;\r\n}\r\n\r\n// Save/load: plain JSON in, validated plain state out.\r\nexport function restoreBossState(saved, {now} = {}) {\n  const s = clone(saved);\n  if (s.v !== 1 || !STATUS.includes(s.status) || !ALLOWED[s.kind]) throw new RangeError('not a boss state');\n  if (!Number.isFinite(s.now)) throw new RangeError('boss state clock is missing');\n  validateTimings(s.timings);\n  if(s.combat)s.combat=restoreClassCombat(s.combat,s.combat.classId,{difficultyLevel:s.seed,patternSeed:s.seed});\n  // Legacy hit-count saves dealt one base HP unit per hit.\n  if (s.punchDamage === undefined) s.punchDamage = 10;\n  if (s.phaseDamage === undefined) s.phaseDamage = s.phaseHits * 10;\n  if (!Number.isFinite(s.punchDamage) || s.punchDamage <= 0 || !Number.isFinite(s.phaseDamage) || s.phaseDamage < 0\n      || !Number.isSafeInteger(s.phaseHits) || s.phaseHits < 0) throw new RangeError('boss damage state is invalid');\n  // performance.now() restarts with each page. Preserve simulated cue progress,\r\n  // but rebase the observation clock and require a fresh released input edge.\r\n  if (now !== undefined) {\r\n    if (!Number.isFinite(now)) throw new TypeError('resume clock must be finite');\r\n    s.now = now;s.prev = null;\n    pauseClassCombat(s.combat);\n  }\r\n  return s;\r\n}\r\n\r\nfunction timing(s) {\r\n  return s.timings[s.kind][`phase${s.phase}`];\r\n}\r\n\r\nfunction scriptKind(s, attackNo) {\r\n  const script = timing(s).script;\r\n  return script[(s.seed + attackNo) % script.length];\r\n}\r\n\r\nfunction enter(s, name, attackNo, kind) {\r\n  s.cue = { name, ms: 0, attackNo, kind, blocked: false, dodged: false };\r\n}\r\n\r\nfunction beginPhase(s, phase) {\n  Object.assign(s, { phase, phaseHits: 0, phaseDamage: 0, status: 'fighting', pursuit: null });\n  enter(s, 'recovery', -1, null); // short lead-in before the first telegraph\n  if(s.combat)beginClassPhase(s,timing(s));\n}\r\n\r\nfunction stepCue(s, dt, cur, edges) {\r\n  s.cue.ms += dt;\r\n  if (s.cue.ms >= timing(s)[DURATION[s.cue.name]]) timedEnd(s);\r\n  applyInput(s, cur, edges);\r\n}\r\n\r\nfunction timedEnd(s) {\r\n  const c = s.cue;\r\n  if (c.name === 'telegraph') {\r\n    enter(s, 'window', c.attackNo, c.kind);\r\n  } else if (c.name === 'window') {\r\n    const kind = ATTACK_KINDS[c.kind];\r\n    const hit = kind.blockable ? !c.blocked : !c.dodged;\r\n    if (hit) s.playerHits += 1;\r\n    if (hit) s.lastOutcome = 'hit';\r\n    else s.lastOutcome = kind.blockable ? 'blocked' : 'dodged';\r\n    enter(s, 'recovery', c.attackNo, null);\r\n  } else if (c.name === 'opening') {\r\n    enter(s, 'recovery', c.attackNo, null);\r\n  } else {\r\n    enter(s, 'telegraph', c.attackNo + 1, scriptKind(s, c.attackNo + 1));\r\n  }\r\n}\r\n\r\nfunction applyInput(s, cur, edges) {\r\n  const c = s.cue;\r\n  if (c.name === 'window') {\r\n    const kind = ATTACK_KINDS[c.kind];\r\n    if (kind.blockable) {\r\n      if (edges.left) {\r\n        enter(s, 'opening', c.attackNo, c.kind);\r\n        s.lastOutcome = 'parried';\r\n      } else if (cur.left) {\r\n        c.blocked = true; // held left blocks; only a fresh edge parries\r\n      }\r\n    } else if (cur[kind.answer]) {\r\n      c.dodged = true; // red: held jump or crouch only\r\n    }\r\n  } else if (c.name === 'opening' && edges.punch) {\n    s.phaseHits += 1; // one fresh punch edge (either hand, any weapon) is one hit\n    s.phaseDamage += s.punchDamage;\n    if (s.phaseDamage >= timing(s).hits * BOSS_DAMAGE_TUNING_PROPOSAL.hpPerBaseHit) defeatPhase(s);\n  }\n}\n\r\nfunction defeatPhase(s) {\r\n  s.cue = null;\r\n  if (s.kind === 'mainboss' && s.phase === 1) {\r\n    s.status = 'pursuit';\r\n    s.pursuit = { elapsedMs: 0, jogMs: 0 };\r\n    return;\r\n  }\r\n  s.status = 'finisher';\r\n  if (s.kind === 'mainboss') s.levelPassed = true; // mini defeat does not pass a level\r\n}\r\n\r\nfunction stepPursuit(s, dt, cur) {\r\n  const p = s.timings.mainboss.pursuit;\r\n  s.pursuit.elapsedMs += dt;\r\n  if (cur.jog) s.pursuit.jogMs += dt;\r\n  if (s.pursuit.jogMs >= p.escapeJogMs) {\r\n    Object.assign(s, { status: 'escaped', levelPassed: true, phase2Pending: true, pursuit: null, cue: null });\r\n  } else if (s.pursuit.elapsedMs >= p.pursuitMs) {\r\n    // PROPOSAL: caught means phase 2 starts immediately, not a punishment tuned yet.\r\n    beginPhase(s, 2);\r\n  }\r\n}\r\n","modules/roguelite/class-combat.mjs":"export const CLASS_IDS=Object.freeze(['knight','monk','dual','mage']);\r\nexport const CLASS_INPUT_BOOLS=Object.freeze(['right','back','guard','crossGuard','handsUp','throwBlades','slashLeft','slashRight','channel']);\r\nexport const SPELL_IDS=Object.freeze(['triangle','square','z','v','circle']);\r\nexport const COMBAT_CLASSES=Object.freeze({\r\n knight:{name:'Knight',description:'Attack behind a tower shield at half speed.',gear:['Barbell lance','Fire lance','Energy lance']},\r\n monk:{name:'Monk',description:'Chain punches, movement and a charged aura.',gear:['Wraps and brass knuckles','Aura fists','Sci-fi fists']},\r\n dual:{name:'Dual wield',description:'Cross guard, directional parries and opposite-hand counters.',gear:['Paired dumbbells','Fire daggers','Energy short swords']},\r\n mage:{name:'Mage',description:'Trace elemental shapes, punch to confirm and channel spells.',gear:['Elemental focus','Enchanted runes','Plasma focus']},\r\n});\r\nconst GEAR_IDS={knight:['barbell-lance','fire-lance','energy-lance'],monk:['brass-wraps','aura-fists','power-fists'],dual:['paired-dumbbells','fire-daggers','energy-blades'],mage:['elemental-focus','enchanted-runes','plasma-focus']};\r\nexport function classGear(classId,tier=0){if(!CLASS_IDS.includes(classId)||!Number.isInteger(tier)||tier<0||tier>2)throw new TypeError('Invalid class equipment');return {id:GEAR_IDS[classId][tier],name:COMBAT_CLASSES[classId].gear[tier],tier,damageScale:1+tier*.2};}\r\nexport function normalizeClassInput(input={}){\r\n if(!input||typeof input!=='object'||Array.isArray(input))throw new TypeError('Combat inputs must be an object');\r\n const out={};for(const key of CLASS_INPUT_BOOLS){if(input[key]!==undefined&&typeof input[key]!=='boolean')throw new TypeError(`Combat ${key} must be boolean`);out[key]=input[key]===true;}\r\n if(input.spell!=null&&!SPELL_IDS.includes(input.spell))throw new TypeError('Unknown spell shape');out.spell=input.spell??null;return out;\r\n}\r\n\r\nexport const SPELL_BURSTS=Object.freeze({triangle:3,square:4,z:3,v:2,circle:1});\r\nconst BASE_BOOLS=['left','punchLeft','punchRight','jump','crouch','jog',...CLASS_INPUT_BOOLS];\r\nconst clone=value=>JSON.parse(JSON.stringify(value));\r\nconst POLISH_DEFAULTS=Object.freeze({difficultyLevel:0,patternSeed:0,chainNumber:0,patternIndex:0,eventSeq:0,superMeter:0,superArmed:false,comboExpiresMs:0,cleanChains:0,clashReadyMs:0});\r\nconst EVENTS=['attack','scratch','hit','blocked','dodged','parried','combo','clash','clash_win','clash_loss','super'];\r\nexport const CLASS_COMBAT_RULES=Object.freeze({windowMs:700,bufferMs:180,comboMs:1500,eventLimit:64,clashMs:3000,clashPunches:4,clashJogMs:1500});\r\nexport function createClassCombat(classId,{difficultyLevel=0,patternSeed=0}={}){\r\n if(!CLASS_IDS.includes(classId))throw new TypeError('Unknown combat class');\r\n if(!Number.isSafeInteger(difficultyLevel)||difficultyLevel<0||!Number.isSafeInteger(patternSeed)||patternSeed<0)throw new TypeError('Invalid combat difficulty or seed');\r\n return {v:1,classId,clockMs:0,prev:null,attackReadyMs:0,leftReadyMs:0,rightReadyMs:0,backReadyMs:0,lostTargetUntilMs:0,\r\n  combo:0,auraHoldMs:0,auraUntilMs:0,auraLatched:false,shield:3,shieldBroken:false,shieldLowered:false,\r\n  pendingSpell:null,spell:null,lastSpell:null,burstsRemaining:0,channeling:false,channelReadyMs:0,freezeUntilMs:0,freezeImmuneUntilMs:0,\r\n  counterHand:null,counterUntilMs:0,nextRedMs:0,chainClean:true,chainPerfect:false,burnUntilMs:0,burnTickMs:0,slowUntilMs:0,shockUntilMs:0,stagger:0,staggerReadyMs:0,\r\n  ...POLISH_DEFAULTS,difficultyLevel,patternSeed,events:[],comboBuffer:[],defenseBuffer:null,clash:null};\r\n}\r\nexport function pauseClassCombat(combat){if(combat){combat.prev=null;combat.auraHoldMs=0;combat.comboBuffer=[];combat.comboExpiresMs=0;combat.defenseBuffer=null;if(combat.clash)combat.clash={punches:0,jogMs:0,lastHand:null};}}\r\nexport function restoreClassCombat(saved,classId,options={}){\r\n const base=createClassCombat(classId,options),c=clone(saved);\r\n if(c?.v!==1||c.classId!==classId)throw new TypeError('Class save mismatch');\r\n // Only new fields may be absent in pre-polish saves. Existing malformed fields never gain defaults.\r\n for(const key of [...Object.keys(POLISH_DEFAULTS),'events','comboBuffer','defenseBuffer','clash'])if(!Object.hasOwn(saved,key))c[key]=clone(base[key]);\r\n for(const [key,value] of Object.entries(base)){\r\n  if(typeof value==='number'&&(!Number.isFinite(c[key])||c[key]<0))throw new TypeError(`Invalid saved ${key}`);\r\n  if(typeof value==='boolean'&&typeof c[key]!=='boolean')throw new TypeError(`Invalid saved ${key}`);\r\n }\r\n if(!Number.isInteger(c.shield)||c.shield>3||!Number.isInteger(c.burstsRemaining)||c.burstsRemaining>4||!Number.isInteger(c.combo))throw new TypeError('Invalid class counters');\r\n for(const key of ['spell','pendingSpell','lastSpell'])if(c[key]!==null&&!SPELL_IDS.includes(c[key]))throw new TypeError('Invalid saved spell');\r\n if(c.counterHand!==null&&!['left','right'].includes(c.counterHand))throw new TypeError('Invalid counter hand');\r\n if(c.prev!==null){for(const key of BASE_BOOLS)if(typeof c.prev?.[key]!=='boolean')throw new TypeError('Invalid saved class input');normalizeClassInput(c.prev);}\r\n for(const key of ['difficultyLevel','patternSeed','chainNumber','patternIndex','eventSeq','cleanChains'])if(!Number.isSafeInteger(c[key]))throw new TypeError(`Invalid saved ${key}`);\r\n if(c.patternIndex>=PATTERNS.length||c.superMeter>100||c.superArmed&&c.superMeter<100)throw new TypeError('Invalid combat progress');\r\n if(!Array.isArray(c.comboBuffer)||c.comboBuffer.length>4||c.comboBuffer.some(side=>!['left','right'].includes(side)))throw new TypeError('Invalid combo buffer');\r\n if(!Array.isArray(c.events)||c.events.length>CLASS_COMBAT_RULES.eventLimit)throw new TypeError('Invalid combat events');\r\n let last=0;for(const e of c.events){if(!e||!Number.isSafeInteger(e.id)||e.id<=last||e.id>c.eventSeq||!EVENTS.includes(e.type)||!Number.isFinite(e.clockMs)||e.clockMs<0||e.clockMs>c.clockMs||!Number.isSafeInteger(e.phase)||e.phase<1||!Number.isSafeInteger(e.attackNo)||e.attackNo< -1||!Number.isFinite(e.amount)||e.amount<0||!['full','scratch','clean','perfect','none'].includes(e.quality)||(e.hand!==undefined&&!['left','right'].includes(e.hand)))throw new TypeError('Invalid combat event');last=e.id;}\r\n if(c.defenseBuffer!==null){const b=c.defenseBuffer;if(!b||!Number.isSafeInteger(b.attackNo)||b.attackNo<0||!Number.isFinite(b.expiresMs)||b.expiresMs<0||!b.edges||Object.keys(b.edges).some(key=>!['left','right','slashLeft','slashRight'].includes(key)||b.edges[key]!==true))throw new TypeError('Invalid defense buffer');}\r\n if(c.clash!==null){const a=c.clash;if(!a||!Number.isSafeInteger(a.punches)||a.punches<0||a.punches>4||!Number.isFinite(a.jogMs)||a.jogMs<0||a.jogMs>1500||a.lastHand!==null&&!['left','right'].includes(a.lastHand))throw new TypeError('Invalid clash');}\r\n return c;\r\n}\r\nexport function classPhaseHp(s,t){return t.hits*10*30;}\r\nconst cue=(name,attackNo=-1,chainIndex=1,chainLength=1)=>({classCue:true,name,ms:0,attackNo,kind:null,direction:null,attackName:'',chainIndex,chainLength,defended:false,blocked:false,dodged:false,durationMs:0});\r\nconst PATTERNS=Object.freeze(['LRLR','RLRL','LLRR','RRLL','LRRL','RLLR']);\r\nfunction hash(...values){let h=2166136261;for(const ch of values.join(':'))h=Math.imul(h^ch.charCodeAt(0),16777619);return h>>>0;}\r\nfunction emit(s,type,extra={}){const c=s.combat;c.events.push({id:++c.eventSeq,type,clockMs:c.clockMs,phase:s.phase,attackNo:s.cue?.attackNo??-1,quality:'none',amount:0,...extra});if(c.events.length>64)c.events.shift();}\r\nfunction clearPattern(c){c.comboBuffer=[];c.comboExpiresMs=0;}\r\nexport function beginClassPhase(s,t){s.cue=cue('recovery');s.cue.durationMs=t.recoveryMs;s.combat.chainClean=true;s.combat.chainPerfect=false;s.combat.prev=null;s.combat.clash=null;s.combat.defenseBuffer=null;s.combat.superArmed=false;clearPattern(s.combat);}\r\n\r\nfunction windup(s,t,index,length){\r\n const c=s.combat,number=(s.cue?.attackNo??-1)+1;\r\n const q=cue('telegraph',number,index,length),level=c.difficultyLevel;\r\n q.direction=PATTERNS[c.patternIndex][index-1]==='L'?'left':'right';q.kind='yellow_blockable';\r\n const redAllowed=!(s.kind==='mainboss'&&s.phase===1)&&index===length&&number%4===3&&c.clockMs>=c.nextRedMs;\r\n const canAnswer=c.classId==='dual'?c.clockMs>=c.backReadyMs:c.classId==='mage'?!c.shieldBroken||c.clockMs>=c.backReadyMs:true;\r\n if(redAllowed&&canAnswer){q.kind=s.kind==='miniboss'||number%8===3?'red_crouch_boss':'red_jump_environment';c.nextRedMs=c.clockMs+12000;}\r\n const delayed=level>=2&&hash(c.patternSeed,s.phase,c.chainNumber,index)%3===0;\r\n q.attackName=q.kind==='red_crouch_boss'?'High sweep':q.kind==='red_jump_environment'?'Ground pulse':delayed?'Delayed backhand':level>=3&&number%5===4?'Overhead cut':`${q.direction==='left'?'Left':'Right'} sweep`;\r\n q.durationMs=t.telegraphMs+(delayed?350:0);q.feint=delayed;s.cue=q;c.defenseBuffer=null;\r\n}\r\nfunction opening(s,t,duration=t.openingMs,clean=false){const old=s.cue;s.cue={...cue('opening',old?.attackNo??-1),attackName:'Punish the opening',durationMs:duration,empowered:false};s.combat.superArmed=clean&&s.combat.superMeter>=100;s.combat.defenseBuffer=null;clearPattern(s.combat);}\r\nfunction receiveHit(s){const c=s.combat;s.playerHits++;s.lastOutcome='hit';c.combo=0;c.auraHoldMs=0;clearPattern(c);emit(s,'hit');}\r\nfunction endCue(s,t){\r\n const q=s.cue,c=s.combat;\r\n if(q.name==='recovery'){\r\n  const index=q.nextChainIndex??1;\r\n  if(index===1){c.chainClean=true;c.chainPerfect=false;c.chainNumber++;let p=hash(c.patternSeed,s.bossId,s.phase,c.chainNumber)%PATTERNS.length;if(p===c.patternIndex)p=(p+1)%PATTERNS.length;c.patternIndex=p;}\r\n  const maximum=Math.min(4,1+Math.floor(c.difficultyLevel/2));\r\n  const length=q.nextChainLength??(maximum<3?maximum:maximum-(hash(c.patternSeed,c.chainNumber,'length')%2));\r\n  windup(s,t,index,length);return;\r\n }\r\n if(q.name==='telegraph'){q.name='window';q.ms=0;q.durationMs=Math.max(700,t.windowMs);return;}\r\n if(q.name==='window'){\r\n  if(!q.defended){receiveHit(s);c.chainClean=false;}\r\n  if(q.chainIndex===q.chainLength&&c.chainClean){\r\n   c.cleanChains++;\r\n   if(c.difficultyLevel>=2&&c.cleanChains%4===0&&c.clockMs>=c.clashReadyMs){\r\n    c.clash={punches:0,jogMs:0,lastHand:null};c.clashReadyMs=c.clockMs+15000;s.cue={...cue('clash',q.attackNo),durationMs:3000,attackName:'Alternate punches or jog'};c.superArmed=false;clearPattern(c);emit(s,'clash');return;\r\n   }\r\n   opening(s,t,t.openingMs*(c.chainPerfect?1.5:1),true);return;\r\n  }\r\n  const next=cue('recovery',q.attackNo);next.durationMs=q.chainIndex<q.chainLength?220:t.recoveryMs;\r\n  if(q.chainIndex<q.chainLength){next.nextChainIndex=q.chainIndex+1;next.nextChainLength=q.chainLength;}\r\n  s.cue=next;return;\r\n }\r\n s.cue=cue('recovery',q.attackNo);s.cue.durationMs=t.recoveryMs;c.superArmed=false;clearPattern(c);\r\n}\r\n\r\n/** Mutates only the cloned encounter supplied by advanceBoss; all clocks are active simulation time. */\r\nexport function stepClassEncounter(s,obs,dt,t,defeat){\r\n const c=s.combat,input={...Object.fromEntries(BASE_BOOLS.map(key=>[key,obs[key]===true])),...normalizeClassInput(obs.classInput??obs)};\r\n const prior=c.prev,edge=key=>prior!==null&&input[key]&&!prior[key];\r\n c.prev=input;c.clockMs+=dt;\r\n const id=c.classId,now=c.clockMs;\r\n if(now>c.comboExpiresMs)clearPattern(c);\r\n if(c.defenseBuffer?.expiresMs<now)c.defenseBuffer=null;\r\n // Clash is a bounded exercise, never an unavoidable extra attack or an attack-spam exploit.\r\n if(s.cue?.name==='clash'){\r\n  if(!c.clash)c.clash={punches:0,jogMs:0,lastHand:null};\r\n  const a=c.clash,l=edge('punchLeft'),r=edge('punchRight');\r\n  if(l!==r){const hand=l?'left':'right';if(hand!==a.lastHand){a.punches=Math.min(4,a.punches+1);a.lastHand=hand;}}\r\n  if(input.jog&&prior)a.jogMs=Math.min(1500,a.jogMs+dt);\r\n  s.cue.ms+=dt;\r\n  if(a.punches>=4||a.jogMs>=1500){emit(s,'clash_win',{quality:'clean'});c.clash=null;opening(s,t,2000,true);}\r\n  else if(s.cue.ms>=3000){emit(s,'clash_loss');c.clash=null;endCue(s,t);}\r\n  return;\r\n }\r\n const hit=(amount,spell=null,{hand=null,fresh=true,counter=false,stream=false,burn=false,ordered=true}={})=>{\r\n  if(s.status!=='fighting'||amount<=0)return;\r\n  const q=s.cue,opened=q?.name==='opening',incoming=q?.name==='telegraph'||q?.name==='window';\r\n  // Confirmed bursts/counters are class exceptions. Held streams scratch during an incoming strike.\r\n  const parryOpportunity=id==='dual'&&q?.defended&&c.counterHand!==null&&now<=c.counterUntilMs;\r\n  const scratch=incoming&&!counter&&!parryOpportunity&&(!spell||stream),quality=scratch?'scratch':'full';\r\n  if(fresh&&c.superArmed&&opened&&c.superMeter>=100){c.superMeter=0;c.superArmed=false;q.empowered=true;q.durationMs=Math.max(q.durationMs,q.ms+2400);emit(s,'super',{quality:'full'});}\r\n  let multiplier=scratch?.2:opened&&!burn?1.5:1;\r\n  if(opened&&q.empowered&&!burn)multiplier*=1.25;\r\n  if(hand&&ordered&&opened&&!scratch&&!burn){\r\n   c.comboBuffer.push(hand);if(c.comboBuffer.length>4)c.comboBuffer.shift();c.comboExpiresMs=now+1500;\r\n   const pattern=c.comboBuffer.map(side=>side[0].toUpperCase()).join('');\r\n   const combo=pattern==='LLRR'?2.5:pattern.endsWith('LRL')||pattern.endsWith('RLR')?2:1;\r\n   if(combo>1){multiplier*=combo;clearPattern(c);emit(s,'combo',{quality:'full',hand});}\r\n  }else if(hand)clearPattern(c);\r\n  const damage=amount*multiplier;\r\n  s.phaseDamage+=damage;s.phaseHits++;if(!burn)c.combo++;s.lastOutcome=spell?`spell_${spell}`:'attack';\r\n  if(fresh&&!burn&&!scratch)c.superMeter=Math.min(100,c.superMeter+4);\r\n  emit(s,scratch?'scratch':'attack',{quality,amount:damage,...(hand?{hand}:{})});\r\n  if(s.phaseDamage>=classPhaseHp(s,t))defeat(s);\r\n };\r\n if(id==='monk'){\r\n  if(input.handsUp&&prior){c.auraHoldMs+=dt;if(c.auraHoldMs>=3000&&!c.auraLatched){c.auraUntilMs=now+10000;c.auraLatched=true;}}\r\n  else if(!input.handsUp){c.auraHoldMs=0;c.auraLatched=false;}\r\n }\r\n if(id==='mage'&&c.shieldBroken){\r\n  if(!input.handsUp)c.shieldLowered=true;\r\n  else if(prior&&!prior.handsUp&&c.shieldLowered){c.shield=3;c.shieldBroken=false;c.shieldLowered=false;}\r\n }\r\n if((id==='dual'||id==='mage')&&edge('back')&&now>=c.backReadyMs){\r\n  // A backstep during a committed red tell cancels that strike: its required defense cannot be spent into an unavoidable hit.\r\n  c.backReadyMs=now+(id==='dual'?10000:7000)*(s.actionRecoveryScale??1);c.lostTargetUntilMs=now+2000;opening(s,t,2000);s.lastOutcome='lost_target';emit(s,'dodged',{quality:'clean'});\r\n }\r\n if(id==='mage'&&input.spell&&input.spell!==prior?.spell){c.pendingSpell=input.spell;c.spell=null;c.burstsRemaining=0;c.channeling=false;}\r\n // Timed defenses consume shield durability once per strike, independently of incoming frame rate.\r\n let q=s.cue;\r\n if(q?.name==='telegraph'&&q.durationMs-q.ms<=180){\r\n  const buffered=Object.fromEntries(['left','right','slashLeft','slashRight'].filter(edge).map(key=>[key,true]));\r\n  if(Object.keys(buffered).length)c.defenseBuffer={attackNo:q.attackNo,expiresMs:now+180,edges:buffered};\r\n }\r\n const defend=(bufferOnly=false)=>{\r\n  q=s.cue;\r\n  if(q?.name!=='window'||q.defended||now<c.freezeUntilMs||now<c.lostTargetUntilMs)return;\r\n  const buffered=c.defenseBuffer?.attackNo===q.attackNo&&c.defenseBuffer.expiresMs>=now?c.defenseBuffer.edges:{};\r\n  const defenseEdge=key=>(!bufferOnly&&edge(key))||buffered[key]===true;\r\n  const red=q.kind.startsWith('red');let defense=null;\r\n  if(!red){\r\n   if(id==='knight'&&(input.guard||input.left))defense='blocked';\r\n   if(id==='monk'&&(defenseEdge('left')||defenseEdge('right')))defense='dodged';\r\n   if(id==='dual'){\r\n    const slash=q.direction==='left'?'slashLeft':'slashRight';\r\n    if(defenseEdge(slash)){defense='parried';c.counterHand=q.direction==='left'?'right':'left';c.counterUntilMs=now+1200;c.chainPerfect=c.chainPerfect||q.ms<=q.durationMs*.4;}\r\n    else if(input.crossGuard)defense='blocked';\r\n   }\r\n  }else{\r\n   if(id==='knight'&&(defenseEdge('left')||defenseEdge('right')))defense='dodged';\r\n   if(id==='monk'&&input[q.kind==='red_jump_environment'?'jump':'crouch'])defense='dodged';\r\n  }\r\n  if(id==='mage'&&input.handsUp&&!c.shieldBroken&&c.shield>0){defense='blocked';c.shield=Math.max(0,c.shield-(red?3:1));if(!c.shield)c.shieldBroken=true;}\r\n  if(defense){q.defended=true;q.blocked=defense!=='dodged';q.dodged=defense==='dodged';s.lastOutcome=defense;c.defenseBuffer=null;c.superMeter=Math.min(100,c.superMeter+(defense==='parried'?12:6));emit(s,defense,{quality:defense==='parried'&&q.ms<=q.durationMs*.4?'perfect':'clean'});}\r\n };\r\n defend();\r\n const spellHit=(shape,stream=false)=>{\r\n  c.lastSpell=shape;\r\n  hit(s.punchDamage*(input.channel ? .35 : .9),shape,{fresh:!stream,stream});\r\n  if(shape==='circle'&&now>=c.freezeImmuneUntilMs){c.freezeUntilMs=now+2000;c.freezeImmuneUntilMs=c.freezeUntilMs+8000;}\r\n  if(shape==='triangle'){c.burnUntilMs=now+2000;if(c.burnTickMs<=now)c.burnTickMs=now+1000;}\r\n  if(shape==='v')c.slowUntilMs=now+1000;\r\n  if(shape==='z')c.shockUntilMs=now+150;\r\n  if(shape==='square'&&++c.stagger>=4&&now>=c.staggerReadyMs&&s.status==='fighting'){c.stagger=0;c.staggerReadyMs=now+6000;opening(s,t,1000);}\r\n };\r\n const punches=['left','right'].filter(side=>edge(side==='left'?'punchLeft':'punchRight'));\r\n if(id==='mage'){\r\n  if(!input.handsUp){\r\n   if(punches.length&&c.pendingSpell){c.spell=c.pendingSpell;c.pendingSpell=null;c.burstsRemaining=SPELL_BURSTS[c.spell];}\r\n   if(punches.length&&c.spell&&now>=c.attackReadyMs){const shape=c.spell;c.attackReadyMs=now+250*(s.actionRecoveryScale??1);c.channelReadyMs=Math.max(c.channelReadyMs,now+400);spellHit(shape);c.burstsRemaining--;if(!c.burstsRemaining)c.spell=null;}\r\n   if(input.channel&&c.spell&&c.spell!=='circle'){\r\n    c.channeling=true;if(now>=c.channelReadyMs){c.channelReadyMs=now+400;spellHit(c.spell,true);}\r\n   }else if(c.channeling){c.channeling=false;c.spell=null;c.burstsRemaining=0;}\r\n  }\r\n }else if(id==='dual'&&input.throwBlades){\r\n  if(edge('throwBlades')&&now>=c.attackReadyMs){c.attackReadyMs=now+500*(s.actionRecoveryScale??1);hit(s.punchDamage*1.4);}\r\n }else{\r\n  for(const side of punches){\r\n   const ready=id==='knight'?'attackReadyMs':side==='left'?'leftReadyMs':'rightReadyMs';\r\n   if(now<c[ready])continue;\r\n   c[ready]=now+Math.max(150,(id==='knight'?(input.guard||input.left?800:400):id==='monk'?150:220)*(s.actionRecoveryScale??1));\r\n   const counter=id==='dual'&&c.counterHand===side&&now<=c.counterUntilMs;\r\n   const factor=(id==='monk' ? .55 : id==='dual' ? .7 : 1)*(id==='monk'&&now<c.auraUntilMs?1.25:1)*(counter?1.5:1);\r\n   hit(s.punchDamage*factor,null,{hand:side,counter,ordered:punches.length===1});if(counter){c.counterHand=null;c.counterUntilMs=0;}\r\n   if(id==='monk'&&c.combo>0&&c.combo%10===0)c.auraUntilMs=now+10000;\r\n  }\r\n }\r\n if(s.status!=='fighting')return;\r\n if(id==='mage'&&c.burnUntilMs>now&&now>=c.burnTickMs){c.burnTickMs=now+1000;hit(s.punchDamage*.15,'triangle',{fresh:false,burn:true});if(s.status!=='fighting')return;}\r\n if(now<c.freezeUntilMs||now<c.shockUntilMs)return;\r\n q=s.cue;if(!q?.classCue){beginClassPhase(s,t);q=s.cue;}\r\n q.ms+=dt*(now<c.slowUntilMs ? .7 : 1);\r\n if(q.ms>=q.durationMs){const was=q.name;endCue(s,t);if(was==='telegraph')defend(true);}\r\n}\r\n","modules/roguelite/clay-opening.mjs":"import {createClayBoard} from '../portal/portal-board-clay.mjs';\nimport {classifyHand} from './hand-input.mjs';\n\nexport const CLAY_OPENING_DWELL_MS=350;\nexport const CLAY_OPENING_SHRINK_MS=650;\nexport const CLAY_OPENING_CRACK_MS=900;\nconst GAP_MS=250;\nconst clone=value=>structuredClone(value);\n\nexport function createClayOpeningState(){return {phase:'claw',holdMs:0,phaseMs:0,lastNow:null};}\n\nconst validHand=hand=>Array.isArray(hand)&&hand.length===21&&hand.every(point=>point&&Number.isFinite(point.x)&&Number.isFinite(point.y)\n &&point.x>=0&&point.x<=1&&point.y>=0&&point.y<=1&&(point.visibility==null||Number.isFinite(point.visibility)&&point.visibility>=.5));\nconst validBodyPoint=point=>point&&Number.isFinite(point.x)&&Number.isFinite(point.y)&&point.x>=0&&point.x<=1&&point.y>=0&&point.y<=1&&Number.isFinite(point.visibility)&&point.visibility>=.5;\n\n/** Reuses the shipped fist classifier and MediaPipe nose/wrist landmarks; this is screen-space proximity only. */\nexport function detectClayOpeningGesture({hands=[],pose,aspect=1}={}){\n if(!Array.isArray(hands)||!Number.isFinite(aspect)||aspect<=0)return {claw:false,drink:false};\n const claw=hands.some(hand=>validHand(hand)&&classifyHand(hand,null,aspect)==='fist');\n const nose=pose?.[0],wrists=[pose?.[15],pose?.[16]];\n const drink=validBodyPoint(nose)&&wrists.some(wrist=>validBodyPoint(wrist)&&Math.hypot((wrist.x-nose.x)*aspect,wrist.y-nose.y)<=.18);\n return {claw,drink};\n}\n\n/** Fresh tracked observations alone advance holds and ceremony; pauses never become active duration. */\nexport function observeClayOpening(state,{now,tracked=false,paused=false,claw=false,drink=false}={}){\n if(!state||!['claw','drink','shrink','crack','complete'].includes(state.phase)||!Number.isFinite(now)||typeof tracked!=='boolean'||typeof paused!=='boolean'||typeof claw!=='boolean'||typeof drink!=='boolean')throw new TypeError('Invalid Clay opening observation.');\n const next=clone(state);\n if(!tracked||paused){next.lastNow=null;if(next.phase==='claw'||next.phase==='drink')next.holdMs=0;return next;}\n let dt=0;\n if(next.lastNow!==null){const elapsed=now-next.lastNow;if(elapsed<0||elapsed>GAP_MS){next.lastNow=now;if(next.phase==='claw'||next.phase==='drink')next.holdMs=0;return next;}dt=Math.min(100,elapsed);}\n next.lastNow=now;\n if(next.phase==='claw'){\n  next.holdMs=claw?next.holdMs+dt:0;\n  if(next.holdMs>=CLAY_OPENING_DWELL_MS){next.phase='drink';next.holdMs=0;next.phaseMs=0;}\n }else if(next.phase==='drink'){\n  next.holdMs=drink?next.holdMs+dt:0;\n  if(next.holdMs>=CLAY_OPENING_DWELL_MS){next.phase='shrink';next.holdMs=0;next.phaseMs=0;}\n }else if(next.phase==='shrink'){\n  next.phaseMs+=dt;if(next.phaseMs>=CLAY_OPENING_SHRINK_MS){next.phase='crack';next.phaseMs=0;}\n }else if(next.phase==='crack'){\n  next.phaseMs+=dt;if(next.phaseMs>=CLAY_OPENING_CRACK_MS){next.phase='complete';next.phaseMs=CLAY_OPENING_CRACK_MS;}\n }\n return next;\n}\n\nconst CRACK_POLYGON=[[.27,.77],[.42,.77],[.47,.81],[.53,.81],[.58,.77],[.73,.77],[.7,.88],[.3,.88]];\nconst COPY={claw:'Curl your hand to summon a recovery shake.',drink:'Bring either wrist to your nose to drink.',shrink:'Shrinking…',crack:'The clay is cracking open.',complete:'The way is clear.'};\n\nexport function mountClayOpening(host,{onComplete=()=>{},boardFactory=(element)=>createClayBoard(element),reducedMotion=!!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches}={}){\n if(!host||typeof host.append!=='function')throw new TypeError('Clay opening needs a host.');\n const doc=host.ownerDocument??globalThis.document;if(!doc||typeof doc.createElement!=='function')throw new TypeError('Clay opening needs a document.');\n const element=doc.createElement('section');element.className='clay-opening';element.dataset.phase='claw';element.setAttribute('aria-label','Clay portal entrance');\n Object.assign(element.style,{position:'absolute',inset:'0',zIndex:'32',overflow:'hidden',background:'rgba(19,13,24,.9)',color:'#fff7ea',fontFamily:'system-ui,sans-serif'});\n const boardHost=doc.createElement('div');boardHost.className='clay-opening-board';Object.assign(boardHost.style,{position:'absolute',inset:'0',zIndex:'0',transformOrigin:'50% 72%',pointerEvents:'none'});\n const shade=doc.createElement('div');Object.assign(shade.style,{position:'absolute',inset:'0',zIndex:'1',background:'linear-gradient(180deg,rgba(15,11,21,.32),rgba(15,11,21,.03) 45%,rgba(15,11,21,.58))',pointerEvents:'none'});\n const status=doc.createElement('div');status.className='clay-opening-status';status.setAttribute('role','status');status.setAttribute('aria-live','polite');\n Object.assign(status.style,{position:'absolute',zIndex:'3',top:'7%',left:'7%',right:'7%',textAlign:'center',font:'800 clamp(20px,4vw,34px)/1.2 system-ui,sans-serif',textShadow:'0 2px 10px #21142c'});\n const header=doc.querySelector?.('header')??null;\n const positionStatus=()=>{\n  const headerRect=header?.getBoundingClientRect?.(),hostRect=host.getBoundingClientRect?.();\n  if(headerRect&&hostRect&&Number.isFinite(headerRect.bottom)&&Number.isFinite(hostRect.top)&&Number.isFinite(headerRect.height)&&headerRect.height>0){\n   status.style.top=`${Math.max(0,headerRect.bottom-hostRect.top+12)}px`;\n  }else status.style.top='7%';\n };\n const view=doc.defaultView??globalThis.window;\n view?.addEventListener?.('resize',positionStatus);\n const ResizeObserverCtor=view?.ResizeObserver??globalThis.ResizeObserver;\n const headerObserver=header&&typeof ResizeObserverCtor==='function'?new ResizeObserverCtor(positionStatus):null;\n headerObserver?.observe(header);\n positionStatus();\n const potion=doc.createElement('div');potion.className='clay-opening-potion';potion.setAttribute('aria-hidden','true');\n Object.assign(potion.style,{position:'absolute',zIndex:'2',left:'50%',top:'49%',width:'clamp(84px,18vw,156px)',height:'clamp(116px,25vw,210px)',transform:'translate(-50%,-50%)',opacity:'0',transition:'opacity .12s linear',pointerEvents:'none'});\n potion.innerHTML='<svg viewBox=\"0 0 100 140\" width=\"100%\" height=\"100%\" aria-hidden=\"true\"><path d=\"M38 8h24v24l8 10v71c0 9-7 16-16 16h-8c-9 0-16-7-16-16V42l8-10z\" fill=\"#bd9bea\" stroke=\"#fff1d1\" stroke-width=\"5\" stroke-linejoin=\"round\"/><path d=\"M37 78h34v35c0 7-5 12-12 12H49c-7 0-12-5-12-12z\" fill=\"#78e4c2\"/><path d=\"M34 45h32M40 18h20\" fill=\"none\" stroke=\"#fff1d1\" stroke-width=\"5\" stroke-linecap=\"round\"/><path d=\"M50 61c-8 7-7 14 0 18 7-4 8-11 0-18z\" fill=\"#fff1d1\"/></svg>';\n const shrinkMark=doc.createElement('div');shrinkMark.className='clay-opening-shrink';shrinkMark.textContent='✦';shrinkMark.setAttribute('aria-hidden','true');\n Object.assign(shrinkMark.style,{position:'absolute',zIndex:'2',left:'50%',top:'49%',transform:'translate(-50%,-50%) scale(.2)',opacity:'0',font:'900 clamp(56px,14vw,120px)/1 system-ui,sans-serif',color:'#fff0a6',textShadow:'0 0 30px #d89bff',pointerEvents:'none'});\n const crack=doc.createElement('svg');crack.className='clay-opening-crack';crack.setAttribute('viewBox','0 0 100 100');crack.setAttribute('preserveAspectRatio','none');crack.setAttribute('aria-hidden','true');\n crack.innerHTML='<path d=\"M4 78h28l8 7 9-15 9 14 8-7h30\" fill=\"none\" stroke=\"#fff0a6\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-dasharray=\"130\" stroke-dashoffset=\"130\"/>';\n Object.assign(crack.style,{position:'absolute',zIndex:'3',left:'5%',right:'5%',bottom:'3%',width:'90%',height:'31%',overflow:'visible',opacity:'0',filter:'drop-shadow(0 0 12px #d9a2ff)',pointerEvents:'none'});\n element.append(boardHost,shade,status,potion,shrinkMark,crack);host.append(element);\n let state=createClayOpeningState(),board=null,disposed=false,completed=false,cut=false,held=false;\n function paint(){\n  const ratio=state.phase==='shrink'?Math.min(1,state.phaseMs/CLAY_OPENING_SHRINK_MS):state.phase==='crack'?Math.min(1,state.phaseMs/CLAY_OPENING_CRACK_MS):state.phase==='complete'?1:0;\n  element.dataset.phase=state.phase;status.textContent=COPY[state.phase];\n  potion.style.opacity=state.phase==='drink'?'1':'0';potion.style.transform=state.phase==='shrink'?`translate(-50%,-50%) scale(${1-.65*ratio})`:'translate(-50%,-50%)';\n  shrinkMark.style.opacity=state.phase==='shrink'||state.phase==='crack'||state.phase==='complete'?'1':'0';shrinkMark.style.transform=`translate(-50%,-50%) scale(${state.phase==='shrink'?.2+.55*ratio:state.phase==='crack'?.75:1})`;\n  crack.style.opacity=state.phase==='crack'||state.phase==='complete'?'1':'0';const path=crack.firstElementChild;if(path)path.style.strokeDashoffset=String(state.phase==='crack'?130*(1-ratio):0);\n  // The person shrinks: the surrounding doorway grows around their eye line.\n  const boardScale=reducedMotion?1:state.phase==='shrink'?1+1.8*ratio:state.phase==='crack'||state.phase==='complete'?2.8:1;\n  boardHost.style.transform=`scale(${boardScale})`;\n }\n function beginCrack(){if(cut||!board)return;cut=true;try{void board.cut(CRACK_POLYGON,'#241a2c',0);}catch{}}\n function observe(input,options={}){\n  if(disposed)return state;\n  const previous=state.phase;state=observeClayOpening(state,{...input,paused:options.paused===true||held});\n  if(previous!=='crack'&&state.phase==='crack')beginCrack();paint();\n  if(state.phase==='complete'&&!completed){completed=true;onComplete();}\n  return clone(state);\n }\n function pause(){if(disposed||held)return;held=true;board?.pause?.();}\n function resume(){if(disposed||!held)return;held=false;board?.resume?.();}\n function render({paused=false}={}){if(disposed)return;if(paused)pause();else resume();paint();}\n function dispose(){if(disposed)return;disposed=true;view?.removeEventListener?.('resize',positionStatus);headerObserver?.disconnect?.();board?.dispose?.();element.remove();}\n paint();\n const ready=Promise.resolve().then(()=>boardFactory(boardHost)).then(value=>{\n  if(disposed){value?.dispose?.();return;}\n  if(!value||typeof value.dispose!=='function'||typeof value.cut!=='function')throw new TypeError('Clay board needs cut and dispose.');\n  board=value;if(held)board.pause?.();if(state.phase==='crack'||state.phase==='complete')beginCrack();\n }).catch(error=>{if(!disposed)throw error;});\n return {element,ready,observe,render,pause,resume,dispose,get state(){return clone(state);},get disposed(){return disposed;}};\n}\n","modules/roguelite/clay-room-view.mjs":"// Thin stage mount for an ACK clay checkpoint. createWorldScene owns the renderer,\r\n// camera, course meshes, and 1.5e6 pixel budget. This file does not step the run,\r\n// build a course, or run its own frame loop.\r\nimport {createWorldScene} from '../../arcade/runner/worlds/scene.mjs';\r\nimport {restoreGameRoom} from './game-room-adapters.mjs';\r\nimport {createViewTour,cameraOverview,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\nfunction fill(node) {\r\n  const style = node.style;\r\n  style.position = 'absolute';\r\n  style.inset = '0';\r\n  style.width = '100%';\r\n  style.height = '100%';\r\n  style.display = 'block';\r\n  style.pointerEvents = 'none';\r\n}\r\n\r\nfunction assignRun(target, source) {\r\n  for (const key of Object.keys(target)) if (!Object.hasOwn(source, key)) delete target[key];\r\n  for (const key of Object.keys(source)) target[key] = source[key];\r\n}\r\n\r\nexport function createClayRoomView({host, sceneFactory = createWorldScene, reducedMotion = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false} = {}) {\r\n  if (!host || typeof host.append !== 'function') throw new TypeError('clay room needs a host');\r\n  const doc = host.ownerDocument ?? globalThis.document;\r\n  if (!doc || typeof doc.createElement !== 'function') throw new TypeError('clay room needs a document');\r\n  const element = doc.createElement('div');\r\n  element.className = 'clay-room';\r\n  element.setAttribute('data-room', 'clay');\r\n  element.setAttribute('aria-hidden', 'true');\r\n  fill(element);\r\n  const canvas = doc.createElement('canvas');\r\n  canvas.className = 'clay-room-canvas';\r\n  fill(canvas);\r\n  element.append(canvas);\r\n  host.append(element);\r\n  let scene;\r\n  const tour = createViewTour((progress, camera) => cameraOverview(camera, progress));\r\n  const motion = createAcknowledgedMotion();\r\n  try {\r\n    scene = sceneFactory(canvas, {beforeRender: (_scene, camera) => tour.apply(camera)});\r\n    if (!scene || typeof scene.render !== 'function' || typeof scene.dispose !== 'function') {\r\n      throw new TypeError('world scene is missing render or dispose');\r\n    }\r\n  } catch (error) {\r\n    try { scene?.dispose?.(); } catch {}\r\n    element.remove();\r\n    throw error;\r\n  }\r\n  let disposed = false;\r\n  let ready = false;\r\n  let run = null;\r\n  let identity = null, lastClock = null;\r\n\r\n  function update(checkpoint) {\r\n    if (disposed) throw new Error('clay room is disposed');\r\n    const saved = restoreGameRoom(checkpoint);\r\n    if (saved.roomId !== 'clay' || saved.version !== 2) throw new TypeError('clay room renders authored world checkpoints only');\r\n    const nextIdentity = `${saved.encounterId}:${saved.seed}:${saved.run.world}:${saved.run.leg}:${saved.run.loop}:${saved.run.phase}`;\r\n    if (identity !== null && (identity !== nextIdentity || saved.run.time < lastClock)) tour.cancel();\r\n    identity = nextIdentity; lastClock = saved.run.time;\r\n    if (!run) run = saved.run;\r\n    else assignRun(run, saved.run);\r\n    motion.push(Object.fromEntries(['distance','time','lane','elevation','jump','slide'].filter(key => Number.isFinite(run[key])).map(key => [key,run[key]])), {identity,clock:run.time});\r\n    ready = true;\r\n  }\r\n\r\n  function render(frame) {\r\n    if (disposed || !run) return;\r\n    if (!frame || typeof frame.now !== 'number' || !Number.isFinite(frame.now) || frame.now < 0) throw new RangeError('render now must be a finite clock');\r\n    if (typeof frame.paused !== 'boolean') throw new TypeError('render paused must be a boolean');\r\n    if (typeof frame.dt !== 'number' || !Number.isFinite(frame.dt) || frame.dt < 0) throw new RangeError('render dt must be a finite duration');\r\n    const values = motion.sample(frame.dt, {paused: frame.paused, reducedMotion});\r\n    Object.assign(run, values);\r\n    scene.render(run, run.time, !frame.paused && !tour.active && run.phase === 'run', {snapCamera:frame.paused || tour.active});\r\n  }\r\n\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    tour.dispose();\r\n    ready = false;\r\n    run = null;\r\n    scene.dispose();\r\n    element.remove();\r\n  }\r\n\r\n  return {\r\n    update,\r\n    render,\r\n    dispose,\r\n    element,\r\n    intro: options => tour.intro({reducedMotion,...options}),\r\n    get ready() { return ready; },\r\n  };\r\n}\r\n","modules/roguelite/coach-dialogue.mjs":"import {canonicalCoachDefinition} from '../coach-world/coach-manifest.mjs';\nexport const RESCUE_MODE_LABEL='Camera overlay · virtual approach';\nconst instructions={approach:'Stay in place. Hold Approach with an open hand to move the virtual view.',reach:'Open your hand over the head target. Hold it steady.',grasp:'Close that same hand around the MOM Inc. control chip.',pull:'Keep your grasp. Pull that same hand away from the head target.','repeat-confirm':'Original MYR5 is repeating this coach’s strategy. Release, then make a fresh punch.'};\n// Only verbatim approved manifest phrases enter CoachVoice. Rescue-specific prose is caption-only.\nexport const RESCUE_APPROVED_LINES=Object.freeze({approach:'Keep the phone still.',reach:'Hold steady…',grasp:'Keep control.',pull:'Movement detected.',accepted:'Well done.','repeat-confirm':\"That wasn't enough to impress my clipboard. Ready for the next level?\",captured:'Take a breath.','rest-reaper':'Rest first. The next challenge can wait.'});\nexport function rescueDialogue(p){\n const coach=canonicalCoachDefinition(p?.canonicalCoachId)?.canonicalLabel??'Coach';\n if(p?.terminal==='captured')return {caption:'MOM Inc. captured you and returned you to base.',voice:RESCUE_APPROVED_LINES.captured};\n if(p?.terminal==='rest-reaper')return {caption:'The Rest Reaper has arrived. Your banked rewards are safe. Take a break; your next run starts fresh.',voice:RESCUE_APPROVED_LINES['rest-reaper']};\n if(p?.stage==='accepted'){\n  if(!p.receipt)return {caption:'Securing the rescue…',voice:null};\n  const first=p.receipt.status==='first';\n  return {caption:first?`${coach} is free. Meet your rescued coach and their Heart back at camp.`:'Another victory. Your rescued coach is safe at camp.',voice:RESCUE_APPROVED_LINES.accepted};\n }\n return {caption:instructions[p?.stage]??'Waiting for rescue progress.',voice:p?.stage==='pull'&&!(p.stageProgress>0)?RESCUE_APPROVED_LINES.grasp:RESCUE_APPROVED_LINES[p?.stage]??null};\n}\n","modules/roguelite/coach-hearts.mjs":"// Explicit catalog pinned at author time; ownership derives only from canonical rescue receipts.\nimport {BOSS_ROWS} from './run-schedule-v2.mjs';\nexport const HEART_POLICY_VERSION=1;\nexport const HEART_ROWS=Object.freeze([\n Object.freeze({\"heartId\":\"heart:roster/01-seed-pearo--3d_character_model\",\"canonicalCoachId\":\"roster/01-seed-pearo--3d_character_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/01-seed-pearo--cute_alien_figure_3d_model\",\"canonicalCoachId\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/01-seed-pearo--blank_toy_figure_3d_model\",\"canonicalCoachId\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/02-taper-tallstalk--3d_humanoid_figure\",\"canonicalCoachId\":\"roster/02-taper-tallstalk--3d_humanoid_figure\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"canonicalCoachId\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"canonicalCoachId\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--circle_center\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--circle_center\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--robot_3d_model\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--robot_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--stylized_3d_character1\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"canonicalCoachId\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/04-crest-wedge--robot_creature_3d_model\",\"canonicalCoachId\":\"roster/04-crest-wedge--robot_creature_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/04-crest-wedge--stylized_3d_character\",\"canonicalCoachId\":\"roster/04-crest-wedge--stylized_3d_character\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"canonicalCoachId\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/06-ridge-triad--geometric_robot_3d_model1\",\"canonicalCoachId\":\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/06-ridge-triad--robot_3d_model1\",\"canonicalCoachId\":\"roster/06-ridge-triad--robot_3d_model1\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/07-bulb-sphereling--cute_robot_3d_model\",\"canonicalCoachId\":\"roster/07-bulb-sphereling--cute_robot_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"canonicalCoachId\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/08-shard-asym--geometric_robot_3d_model\",\"canonicalCoachId\":\"roster/08-shard-asym--geometric_robot_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/08-shard-asym--robot_3d_model4\",\"canonicalCoachId\":\"roster/08-shard-asym--robot_3d_model4\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/08-shard-asym--fantasy_creature_3d_model2\",\"canonicalCoachId\":\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/08-shard-asym--stylized_action_figure_3d_model\",\"canonicalCoachId\":\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/09-monolith-tanka--3d_robot_model\",\"canonicalCoachId\":\"roster/09-monolith-tanka--3d_robot_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"canonicalCoachId\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/09-monolith-tanka--mini_robot_3d_model\",\"canonicalCoachId\":\"roster/09-monolith-tanka--mini_robot_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/09-monolith-tanka--robot_3d_model3\",\"canonicalCoachId\":\"roster/09-monolith-tanka--robot_3d_model3\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/09-monolith-tanka--white_horned_robot_3d_model\",\"canonicalCoachId\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/10-petal-wisp--stylized_creature_3d_model\",\"canonicalCoachId\":\"roster/10-petal-wisp--stylized_creature_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/10-petal-wisp--ghost_character_3d_model\",\"canonicalCoachId\":\"roster/10-petal-wisp--ghost_character_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/10-petal-wisp--fantasy_creature_3d_model4\",\"canonicalCoachId\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/11-anvil-cask--clay-style_robot_3d_model\",\"canonicalCoachId\":\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"canonicalCoachId\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"canonicalCoachId\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/13-fan-split--stylized_3d_character2\",\"canonicalCoachId\":\"roster/13-fan-split--stylized_3d_character2\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/13-fan-split--stylized_toy_3d_model\",\"canonicalCoachId\":\"roster/13-fan-split--stylized_toy_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/14-chisel-spire--cone_head_3d_model\",\"canonicalCoachId\":\"roster/14-chisel-spire--cone_head_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/14-chisel-spire--low_poly_robot_3d_model\",\"canonicalCoachId\":\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/15-orbital-coili--robot_character_3d_model\",\"canonicalCoachId\":\"roster/15-orbital-coili--robot_character_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/16-spade-arch--pyramid_head_figure_3d_model\",\"canonicalCoachId\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/16-spade-arch--stylized_humanoid_3d_model\",\"canonicalCoachId\":\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/17-shellcap-manyarm--3d_character_figurine\",\"canonicalCoachId\":\"roster/17-shellcap-manyarm--3d_character_figurine\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"canonicalCoachId\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"canonicalCoachId\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--dragon_creature_3d_model\",\"canonicalCoachId\":\"roster/18-quad-all--dragon_creature_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--fantasy_creature_3d_model1\",\"canonicalCoachId\":\"roster/18-quad-all--fantasy_creature_3d_model1\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--fantasy_creature_3d_model3\",\"canonicalCoachId\":\"roster/18-quad-all--fantasy_creature_3d_model3\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--four-legged_robot_3d_model\",\"canonicalCoachId\":\"roster/18-quad-all--four-legged_robot_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--quadruped_robot_3d_model1\",\"canonicalCoachId\":\"roster/18-quad-all--quadruped_robot_3d_model1\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--quadruped_robot_3d_model\",\"canonicalCoachId\":\"roster/18-quad-all--quadruped_robot_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--robotic_dog_3d_model\",\"canonicalCoachId\":\"roster/18-quad-all--robotic_dog_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"canonicalCoachId\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/19-genie-multi--fantasy_creature_3d_model\",\"canonicalCoachId\":\"roster/19-genie-multi--fantasy_creature_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"canonicalCoachId\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"canonicalCoachId\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/19-genie-multi--stylized_octopus_3d_model\",\"canonicalCoachId\":\"roster/19-genie-multi--stylized_octopus_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/20-lume--robotic_figure_3d_model\",\"canonicalCoachId\":\"roster/20-lume--robotic_figure_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/22-curve--stylized_alien_3d_model\",\"canonicalCoachId\":\"roster/22-curve--stylized_alien_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/22-curve--stylized_cartoon_figure_3d_model\",\"canonicalCoachId\":\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"effect\":\"vitality\"}),\n Object.freeze({\"heartId\":\"heart:roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"canonicalCoachId\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"canonicalCoachId\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"effect\":\"speed\"}),\n Object.freeze({\"heartId\":\"heart:roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"canonicalCoachId\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"effect\":\"damage\"}),\n Object.freeze({\"heartId\":\"heart:roster/23-blob-texture-bodies--sand_creature_3d_model\",\"canonicalCoachId\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"effect\":\"vitality\"})\n]);\nconst byId=new Map(HEART_ROWS.map(row=>[row.heartId,row]));\nconst byCoach=new Map(HEART_ROWS.map(row=>[row.canonicalCoachId,row]));\nconst roles={scout:'speed',skirmisher:'speed',frontline:'damage',commander:'damage',hauler:'vitality',support:'vitality'};\nif(HEART_ROWS.length!==BOSS_ROWS.filter(row=>row.unlockTarget).length||byId.size!==HEART_ROWS.length||byCoach.size!==HEART_ROWS.length||BOSS_ROWS.some(row=>row.unlockTarget?byCoach.get(row.canonicalCoachId)?.effect!==roles[row.raidRole]:byCoach.has(row.canonicalCoachId)))throw new Error('Heart catalog does not match canonical eligibility');\nexport const CHEST_WEARABLES=Object.freeze([\n Object.freeze({id:'rescue-wrist',slot:'wrist',effect:'damage',amount:0.25}),\n Object.freeze({id:'rescue-foot',slot:'foot',effect:'recovery',amount:0.10}),\n Object.freeze({id:'rescue-neck',slot:'neck',effect:'response',amount:0.50}),\n]);\nconst wearables=new Map(CHEST_WEARABLES.map(item=>[item.id,item]));\nconst clone=value=>structuredClone(value);\nconst exact=(v,keys)=>v&&typeof v==='object'&&!Array.isArray(v)&&Object.keys(v).length===keys.length&&keys.every(k=>Object.hasOwn(v,k));\nconst receiptId=id=>typeof id==='string'&&id.length>0&&id.length<=256;\n\nexport function heartForCoach(coachId){return byCoach.get(coachId)??null;}\nexport function projectCoachHearts(rescuedCoachIds=[],equippedHeartId=null){\n if(!Array.isArray(rescuedCoachIds)||rescuedCoachIds.some(id=>typeof id!=='string'))throw new TypeError('Canonical rescue IDs required');\n const owned=[...new Set(rescuedCoachIds)].map(id=>byCoach.get(id)).filter(Boolean).map(row=>({heartId:row.heartId,coachId:row.canonicalCoachId,name:`${row.effect[0].toUpperCase()+row.effect.slice(1)} Heart`,speedMultiplier:row.effect==='speed'?0.95:1,damageMultiplier:row.effect==='damage'?1.05:1,initialArmorBonus:row.effect==='vitality'?10:0}));\n if(equippedHeartId!==null&&!owned.some(row=>row.heartId===equippedHeartId))throw new TypeError('Equipped heart is not rescued');\n return {version:1,owned,equippedHeartId};\n}\n\n/** Only run initialization may enlarge initial armor. Restore never invokes this. */\nexport function createHeartModifiers({heartId=null,rescuedCoachIds=[],baseArmorMax=100}={}){\n projectCoachHearts(rescuedCoachIds,heartId);\n if(!Number.isSafeInteger(baseArmorMax)||baseArmorMax<1||baseArmorMax>10000)throw new TypeError('Invalid base armor');\n return {version:1,heartId,baseArmorMax,initialArmorBonus:Math.min(10000-baseArmorMax,byId.get(heartId)?.effect==='vitality'?10:0),wearables:[],receipts:[],expired:false};\n}\n\nexport function restoreHeartModifiers(value,{stats}={}){\n if(!exact(value,['version','heartId','baseArmorMax','initialArmorBonus','wearables','receipts','expired'])||value.version!==1||value.heartId!==null&&!byId.has(value.heartId)||!Number.isSafeInteger(value.baseArmorMax)||value.baseArmorMax<1||value.baseArmorMax>10000\n  ||value.initialArmorBonus!==Math.min(10000-value.baseArmorMax,byId.get(value.heartId)?.effect==='vitality'?10:0)||!Array.isArray(value.wearables)||value.wearables.length>3||!Array.isArray(value.receipts)||value.receipts.length>2048||typeof value.expired!=='boolean')throw new TypeError('Invalid heart modifiers');\n const ids=new Set(),slots=new Set(),receipts=new Map();\n for(const r of value.receipts){if(!exact(r,['receiptId','wearableId'])||!receiptId(r.receiptId)||receipts.has(r.receiptId)||!wearables.has(r.wearableId))throw new TypeError('Invalid wearable receipt');receipts.set(r.receiptId,r.wearableId);}\n for(const item of value.wearables){const row=wearables.get(item?.wearableId);if(!exact(item,['wearableId','receiptId'])||!row||ids.has(row.id)||slots.has(row.slot)||receipts.get(item.receiptId)!==item.wearableId)throw new TypeError('Invalid wearable slot');ids.add(row.id);slots.add(row.slot);}\n if(value.expired&&value.wearables.length||stats&&(stats.rulesVersion!==2||stats.armorMax!==value.baseArmorMax+value.initialArmorBonus))throw new TypeError('Heart armor/expiry mismatch');\n return clone(value);\n}\n\n/** Receipt-backed chest awards at a completed room/recovery boundary only. */\nexport function grantChestWearable(value,{wearableId,receiptId:receipt,boundary}={}){\n const state=restoreHeartModifiers(value),item=wearables.get(wearableId);\n if(!item||!receiptId(receipt)||!['room-complete','recovery'].includes(boundary))throw new TypeError('Invalid chest wearable award');\n const prior=state.receipts.find(r=>r.receiptId===receipt);\n if(prior){if(prior.wearableId!==wearableId)throw new Error('Wearable receipt conflict');return {state,granted:false};}\n if(state.expired)throw new Error('Run wearables have expired');\n if(state.receipts.length>=2048)throw new RangeError('Wearable receipt capacity');\n state.receipts.push({receiptId:receipt,wearableId});\n if(state.wearables.some(w=>w.wearableId===wearableId))return {state,granted:false};\n state.wearables=state.wearables.filter(w=>wearables.get(w.wearableId).slot!==item.slot);\n state.wearables.push({wearableId,receiptId:receipt});return {state,granted:true};\n}\n\nexport function expireRunWearables(value){const state=restoreHeartModifiers(value);state.wearables=[];state.expired=true;return state;}\n\nexport function heartEffects(value){\n if(value==null)return {damageMultiplier:1,recoveryMultiplier:1,responseMultiplier:1,initialArmorBonus:0};\n const state=restoreHeartModifiers(value),heart=byId.get(state.heartId);\n const amounts={damage:heart?.effect==='damage'?0.05:0,recovery:heart?.effect==='speed'?0.05:0,response:0};\n for(const item of state.wearables){const row=wearables.get(item.wearableId);amounts[row.effect]+=row.amount;}\n return {damageMultiplier:1+Math.min(0.50,amounts.damage),recoveryMultiplier:1-Math.min(0.20,amounts.recovery),responseMultiplier:1+Math.min(0.50,amounts.response),initialArmorBonus:state.initialArmorBonus};\n}\n","modules/roguelite/coach-presentation.mjs":"import {coachForRoomNodeForVersion} from './run-schedule-v2-builder.mjs';\nimport {canonicalCoachDefinition} from '../coach-world/coach-manifest.mjs';\n\n/** Runtime presentation bridge. V1 keeps its original unit record; V2 uses authored identity and art fields. */\nexport function coachPresentationForRoom(version,node){\n const resolved=coachForRoomNodeForVersion(version,node);\n if(version===1)return resolved;\n if(![2,3,4].includes(version))throw new RangeError('Unknown run schedule version');\n const canonical=canonicalCoachDefinition(resolved.canonicalCoachId);\n if(!canonical)throw new TypeError('V2 coach has no canonical presentation definition');\n return {...canonical,...resolved,id:resolved.sourceVisualId,label:canonical.canonicalLabel,\n  atlasPath:resolved.atlasPath,frame:[...resolved.frame],tint:resolved.section===null?'#ffffff':null};\n}\n\n/** Presentation identity only; never changes the encounter's canonical combat binding. */\nexport function rescueCoachPresentation(projection){\n const canonical=canonicalCoachDefinition(projection?.canonicalCoachId);\n if(!canonical||!['first','repeat'].includes(projection.rescueKind))throw new TypeError('Invalid rescue coach binding');\n const repeat=projection.rescueKind==='repeat',expected=repeat?'myr5':canonical.canonicalCoachId;\n if(projection.presentationCoachId!==expected)throw new TypeError('Rescue presentation identity mismatch');\n const visual=repeat?canonicalCoachDefinition('myr5'):canonical;\n return {canonicalCoachId:canonical.canonicalCoachId,presentationCoachId:expected,sourceId:visual.sourceCoachId,label:visual.canonicalLabel,repeat};\n}\n","modules/roguelite/coin-chase-state.mjs":"// Pure score/progress adapter for the existing prone-flight room.\r\n// ponytail: one capped slowdown timer refreshed by each new obstacle, without building a second runner engine.\r\nexport const DEFAULT_TOTAL_DURATION = 60;\r\nexport const DEFAULT_CATCH_BONUS = 100;\r\nexport const DEFAULT_MAX_SLOW_SECONDS = 4;\r\nexport const DEFAULT_SLOWED_RELATIVE_VELOCITY = 0.25;\r\nexport const DEFAULT_CATCH_WINDOWS = 2;\r\n\r\nconst clone = value => structuredClone(value);\r\nconst safeId = value => typeof value === 'string' && value.length > 0 && value.length <= 128;\r\nconst finitePositive = (value, name) => {\r\n  if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be finite and positive`);\r\n  return value;\r\n};\r\nconst safeAmount = (value, name, allowZero = false) => {\r\n  if (!Number.isSafeInteger(value) || value < (allowZero ? 0 : 1) || value > 1_000_000) {\r\n    throw new RangeError(`${name} must be a safe integer in range`);\r\n  }\r\n  return value;\r\n};\r\n\r\nfunction checkedPool(coinPool) {\r\n  if (!Array.isArray(coinPool)) throw new TypeError('coinPool must be a finite array');\r\n  const ids = new Set();\r\n  const pool = coinPool.map(coin => {\r\n    if (!coin || !safeId(coin.id)) throw new TypeError('each coin needs a stable string id');\r\n    if (ids.has(coin.id)) throw new RangeError(`duplicate coin id: ${coin.id}`);\r\n    ids.add(coin.id);\r\n    return { id: coin.id, amount: safeAmount(coin.amount, `coin ${coin.id} amount`) };\r\n  });\r\n  const total = pool.reduce((sum, coin) => {\r\n    const next = sum + coin.amount;\r\n    if (!Number.isSafeInteger(next)) throw new RangeError('coin pool total exceeds safe integer range');\r\n    return next;\r\n  }, 0);\r\n  return { pool, total };\r\n}\r\n\r\nexport function createCoinChaseState({\r\n  encounterId = 'grass-chase', seed = 0, totalDuration = DEFAULT_TOTAL_DURATION,\r\n  coinPool = [], catchBonus = DEFAULT_CATCH_BONUS,\r\n  maxSlowDuration = DEFAULT_MAX_SLOW_SECONDS,\r\n  slowedRelativeVelocity = DEFAULT_SLOWED_RELATIVE_VELOCITY,\r\n  catchWindows = DEFAULT_CATCH_WINDOWS,\r\n} = {}) {\r\n  if (!safeId(encounterId)) throw new TypeError('encounterId must be a stable string id');\r\n  if (!(typeof seed === 'string' || Number.isSafeInteger(seed))) throw new TypeError('seed must be a string or safe integer');\r\n  finitePositive(totalDuration, 'totalDuration');\r\n  finitePositive(maxSlowDuration, 'maxSlowDuration');\r\n  if (!Number.isSafeInteger(catchWindows) || catchWindows < 2 || catchWindows > 3) {\r\n    throw new RangeError('catchWindows must be 2 or 3');\r\n  }\r\n  if (!Number.isFinite(slowedRelativeVelocity) || slowedRelativeVelocity < -1 || slowedRelativeVelocity > 1) {\r\n    throw new RangeError('slowedRelativeVelocity must be in [-1, 1]');\r\n  }\r\n  safeAmount(catchBonus, 'catchBonus', true);\r\n  const { pool, total } = checkedPool(coinPool);\r\n  // Each new obstacle refreshes a bounded slowdown. The clean catch window is\r\n  // T/windows; cap slowdown to one quarter of that window so a max-gap clean\r\n  // run catches within its window, while the remaining full encounter can recover.\r\n  const cleanWindow = totalDuration / catchWindows;\r\n  const boundedSlow = Math.min(maxSlowDuration, cleanWindow / 4);\r\n  const closeRate = 1 / (cleanWindow - boundedSlow);\r\n  return {\r\n    version: 1, encounterId, seed, status: 'running', totalDuration, elapsed: 0,\r\n    elapsedTotal: 0,\r\n    gap: 1, catchWindows, cleanCatchFraction: 1 / catchWindows, closeRate,\r\n    slowedRelativeVelocity, maxSlowDuration: boundedSlow,\r\n    slowRemaining: 0, coinPool: pool, coinPoolTotal: total, collectedCoinIds: [],\r\n    collectedCoins: 0, catchBonus, catchBonusReceipt: null, bonusCoins: 0,\r\n    obstacleReceipts: [], eventSerial: 0, events: [],\r\n  };\r\n}\r\n\r\nfunction emit(state, type, data = {}) {\r\n  const event = { id: `${state.encounterId}:event:${state.eventSerial + 1}`, type, ...data };\r\n  const next = { ...state, eventSerial: state.eventSerial + 1, events: [...state.events, event] };\r\n  return { state: next, event };\r\n}\r\n\r\nexport function restoreCoinChaseState(saved) {\r\n  if (!saved || saved.version !== 1 || !safeId(saved.encounterId)) throw new TypeError('invalid coin chase save');\r\n  const { pool, total } = checkedPool(saved.coinPool);\r\n  const state = createCoinChaseState({\r\n    encounterId: saved.encounterId, seed: saved.seed, totalDuration: saved.totalDuration,\r\n    coinPool: pool, catchBonus: saved.catchBonus, maxSlowDuration: saved.maxSlowDuration,\r\n    slowedRelativeVelocity: saved.slowedRelativeVelocity, catchWindows: saved.catchWindows ?? DEFAULT_CATCH_WINDOWS,\r\n  });\r\n  if (!['running', 'caught', 'timeout'].includes(saved.status)) throw new RangeError('invalid saved chase status');\r\n  if (saved.maxSlowDuration !== state.maxSlowDuration || saved.closeRate !== state.closeRate\r\n      || saved.catchWindows !== state.catchWindows\r\n      || saved.cleanCatchFraction !== 1 / state.catchWindows) throw new RangeError('saved chase tuning does not match its contract');\r\n  const collectedCoinIds = [...new Set(saved.collectedCoinIds ?? [])];\r\n  if (collectedCoinIds.some(id => !pool.some(coin => coin.id === id))) throw new RangeError('saved chase contains an unknown coin receipt');\r\n  const collectedCoins = pool.reduce((sum, coin) => sum + (collectedCoinIds.includes(coin.id) ? coin.amount : 0), 0);\r\n  if (collectedCoins !== saved.collectedCoins || total !== saved.coinPoolTotal) throw new RangeError('saved chase coin totals do not match its finite pool');\r\n  if (!Number.isFinite(saved.elapsed) || saved.elapsed < 0 || saved.elapsed > saved.totalDuration\r\n      || !Number.isFinite(saved.elapsedTotal) || saved.elapsedTotal !== saved.elapsed\r\n      || !Number.isFinite(saved.gap) || saved.gap < 0 || saved.gap > 1\r\n      || !Number.isFinite(saved.slowRemaining) || saved.slowRemaining < 0\r\n      || saved.slowRemaining > saved.maxSlowDuration + 1e-9) throw new RangeError('invalid saved chase clock/gap');\r\n  if (!Number.isSafeInteger(saved.eventSerial) || saved.eventSerial < 0 || !Array.isArray(saved.events)\r\n      || saved.events.length !== saved.eventSerial) throw new RangeError('invalid saved events');\r\n  if (!Array.isArray(saved.obstacleReceipts) || saved.obstacleReceipts.some(id => !safeId(id))) throw new RangeError('invalid obstacle receipts');\r\n  if (!Number.isSafeInteger(saved.bonusCoins) || saved.bonusCoins < 0 || saved.bonusCoins > saved.catchBonus) throw new RangeError('invalid catch bonus');\r\n  const catchBonusReceipt = saved.catchBonusReceipt;\r\n  if (saved.status === 'caught' && (catchBonusReceipt !== `${saved.encounterId}:catch-bonus`\r\n      || saved.bonusCoins !== saved.catchBonus || saved.gap !== 0)) throw new RangeError('caught chase is missing its bonus receipt');\r\n  if (saved.status !== 'caught' && (catchBonusReceipt !== null || saved.bonusCoins !== 0)) throw new RangeError('unfinished chase cannot contain catch bonus');\r\n  if (saved.status === 'timeout' && saved.elapsed !== saved.totalDuration) throw new RangeError('timeout save has not completed the fixed duration');\r\n  return { ...state, ...clone(saved), coinPool: pool, coinPoolTotal: total, collectedCoinIds, collectedCoins };\r\n}\r\n\r\n/** Obstacles only spend a bounded slow budget. Repeated frame receipts do nothing. */\r\nexport function hitCoinChaseObstacle(state, obstacleId) {\r\n  if (!safeId(obstacleId)) throw new TypeError('obstacleId must be a stable string id');\r\n  if (state.status !== 'running' || state.obstacleReceipts.includes(obstacleId)) return { state: clone(state), event: null, duplicate: true };\r\n  const obstacleReceipts = [...state.obstacleReceipts, obstacleId];\r\n  const slowRemaining = state.maxSlowDuration;\r\n  const { state: next, event } = emit({ ...state, obstacleReceipts, slowRemaining }, 'obstacle-slow', {\r\n    obstacleId, slowSeconds: slowRemaining, healthDamage: 0,\r\n  });\r\n  return { state: next, event, duplicate: false };\r\n}\r\n\r\n/** Claim one caller-seeded coin once; unknown IDs and altered pool values cannot mint coins. */\r\nexport function collectCoinChaseCoin(state, coinId) {\r\n  if (!safeId(coinId)) throw new TypeError('coinId must be a stable string id');\r\n  const coin = state.coinPool.find(item => item.id === coinId);\r\n  if (!coin) throw new RangeError(`coin is outside this encounter's finite pool: ${coinId}`);\r\n  if (state.status !== 'running' || state.collectedCoinIds.includes(coinId)) {\r\n    return { state: clone(state), event: null, duplicate: true };\r\n  }\r\n  const collectedCoinIds = [...state.collectedCoinIds, coinId];\r\n  const collectedCoins = state.collectedCoins + coin.amount;\r\n  const { state: next, event } = emit({ ...state, collectedCoinIds, collectedCoins }, 'coin-collected', {\r\n    coinId, amount: coin.amount, collectedCoins,\r\n  });\r\n  return { state: next, event, duplicate: false };\r\n}\r\n\r\nfunction finishCaught(state) {\r\n  if (state.status !== 'running') return { state, events: [] };\r\n  const receipt = `${state.encounterId}:catch-bonus`;\r\n  const caught = { ...state, status: 'caught', gap: 0, catchBonusReceipt: receipt, bonusCoins: state.catchBonus };\r\n  const a = emit(caught, 'coach-caught', { gap: 0 });\r\n  const b = emit(a.state, 'catch-bonus', { receipt, amount: state.catchBonus, totalCoins: state.collectedCoins + state.catchBonus });\r\n  return { state: b.state, events: [a.event, b.event] };\r\n}\r\n\r\n/** Advance one continuous fixed-duration chase; pause conditions freeze timer and pursuit. */\r\nexport function advanceCoinChase(state, deltaSeconds, { paused = false, trackingLost = false, tutorialActive = false, backgrounded = false, pursuit = 1 } = {}) {\r\n  if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) throw new RangeError('deltaSeconds must be finite and non-negative');\r\n  if (!Number.isFinite(pursuit) || pursuit < 0 || pursuit > 1) throw new RangeError('pursuit must be in [0, 1]');\r\n  if (state.status !== 'running' || paused || trackingLost || tutorialActive || backgrounded || deltaSeconds === 0) {\r\n    return { state: clone(state), events: [] };\r\n  }\r\n  const rate = state.closeRate;\r\n  let next = clone(state), left = deltaSeconds;\r\n  const events = [];\r\n  while (left > 1e-12 && next.status === 'running') {\r\n    const timeLeft = Math.max(0, next.totalDuration - next.elapsed);\r\n    if (timeLeft <= 1e-12) {\r\n      next = { ...next, status: 'timeout', elapsed: next.totalDuration };\r\n      const result = emit(next, 'chase-timeout', { collectedCoins: next.collectedCoins, healthDamage: 0 });\r\n      next = result.state; events.push(result.event); break;\r\n    }\r\n    const slowed = next.slowRemaining > 1e-12;\r\n    const segment = Math.min(timeLeft, left, slowed ? next.slowRemaining : Infinity);\r\n    const relative = (slowed ? next.slowedRelativeVelocity : 1) * pursuit;\r\n    const signedRate = rate * relative;\r\n    const timeToCatch = signedRate > 0 ? next.gap / signedRate : Infinity;\r\n    const active = Math.min(segment, timeToCatch);\r\n    next.elapsed += active;\r\n    next.elapsedTotal += active;\r\n    next.gap = Math.max(0, Math.min(1, next.gap - signedRate * active));\r\n    left -= active;\r\n    if (slowed) {\r\n      next.slowRemaining = Math.max(0, next.slowRemaining - active);\r\n    }\r\n    if (next.gap <= 1e-12) {\r\n      const caught = finishCaught(next);\r\n      next = caught.state; events.push(...caught.events); break;\r\n    }\r\n    if (active + 1e-12 < segment) break;\r\n    if (segment <= 1e-12) break;\r\n  }\r\n  if (next.status === 'running' && left <= 1e-12 && next.elapsed >= next.totalDuration - 1e-12) {\r\n    next = { ...next, elapsed: next.totalDuration, status: 'timeout' };\r\n    const result = emit(next, 'chase-timeout', { collectedCoins: next.collectedCoins, healthDamage: 0 });\r\n    next = result.state; events.push(result.event);\r\n  }\r\n  return { state: next, events };\r\n}\r\n","modules/roguelite/combat-impact.mjs":"const priority={hit:9,parried:8,super:7,combo:6,clash_win:6,clash_loss:5,clash:5,attack:5,scratch:4,blocked:3,dodged:2};\nconst empty=()=>({event:null,flash:null,recoil:0,hitStop:false,spark:false});\n/** Consumes server-owned outcome IDs once, including repeated identical outcomes. */\nexport function createCombatImpact({reducedMotion=false}={}){\n let bossKey=null,watermark=0,lastClock=0,effect=null;\n function reset(){bossKey=null;watermark=0;lastClock=0;effect=null;}\n function update(boss,{paused=false}={}){\n  if(!boss?.combat){reset();return empty();}\n  const c=boss.combat,clock=c.clockMs??0;\n  if(bossKey!==boss.bossId||paused||clock<lastClock){bossKey=boss.bossId;watermark=c.eventSeq??0;lastClock=clock;effect=null;return empty();}\n  lastClock=clock;let fresh=null;\n  for(const event of c.events??[]){\n   const age=clock-event.clockMs;\n   if(event.id<=watermark||age<0||age>350||!priority[event.type])continue;\n   if(!fresh||priority[event.type]>priority[fresh.type]||priority[event.type]===priority[fresh.type]&&event.id>fresh.id)fresh=event;\n  }\n  watermark=Math.max(watermark,c.eventSeq??0);\n  if(fresh)effect={...fresh,start:clock};\n  if(!effect)return empty();\n  const age=clock-effect.start,visible=age<150;\n  const incoming=['telegraph','window'].includes(boss.cue?.name)&&boss.cue.attackNo!==effect.attackNo;\n  return {event:fresh,flash:visible?effect.type:null,recoil:visible&&!reducedMotion?1-age/150:0,\n   hitStop:!reducedMotion&&effect.type==='parried'&&age<65&&!incoming,\n   spark:visible&&!reducedMotion&&!['scratch','dodged'].includes(effect.type)};\n }\n return {update,reset};\n}\n","modules/roguelite/combat-input.mjs":"import {classifyHand} from './hand-input.mjs';\r\n\r\nexport const CLASS_INPUT_BOOLS=[\r\n 'left','right','crouch','jump','jog','punchLeft','punchRight','back',\r\n 'guard','crossGuard','handsUp','throwBlades','slashLeft','slashRight',\r\n 'channel','spell'\r\n];\r\n\r\nconst WRIST_L=15, WRIST_R=16, ELBOW_L=13, ELBOW_R=14, SHOULDER_L=11, SHOULDER_R=12;\r\nconst HIP_L=23, HIP_R=24;\r\nconst HIGH_CONF=0.5;\r\nconst STALE_MS=300;\r\nconst HAND_MAX_DIST=0.12;\r\nconst SHAPE_TEMPLATES={\r\n triangle:[[0,0],[1,0],[0.5,0.866],[0,0]],\r\n square:[[0,0],[1,0],[1,1],[0,1],[0,0]],\r\n circle:Array.from({length:17},(_,i)=>{const t=i/16*Math.PI*2;return [0.5+Math.cos(t)*0.5,0.5+Math.sin(t)*0.5]}),\r\n z:[[0,0],[1,0],[0,1],[1,1]],\r\n v:[[0,0],[0.5,1],[1,0]]\r\n};\r\nconst CLOSED=new Set(['triangle','square','circle']);\r\n\r\nfunction valid(p){return p&&Number.isFinite(p.x)&&Number.isFinite(p.y);}\r\nfunction dst(a,b,aspect=1){return Math.hypot((a.x-b.x)*aspect,a.y-b.y);}\r\nfunction angleAt(a,b,c,aspect=1){\r\n const v1x=(a.x-b.x)*aspect, v1y=a.y-b.y, v2x=(c.x-b.x)*aspect, v2y=c.y-b.y;\r\n const n1=Math.hypot(v1x,v1y), n2=Math.hypot(v2x,v2y);\r\n if(n1<1e-6||n2<1e-6)return 0;\r\n let d=(v1x*v2x+v1y*v2y)/(n1*n2);d=Math.max(-1,Math.min(1,d));\r\n return Math.acos(d)*180/Math.PI;\r\n}\r\nfunction wristSpeed(prev,cur,dt){if(!prev||dt<=0)return 0;return Math.hypot(cur.x-prev.x,cur.y-prev.y)/(dt/1000);}\r\n\r\n// --- shape recognition (pure, testable) ---\r\nfunction bbox(points){\r\n let x0=Infinity,y0=Infinity,x1=-Infinity,y1=-Infinity;\r\n for(const p of points){if(p.x<x0)x0=p.x;if(p.x>x1)x1=p.x;if(p.y<y0)y0=p.y;if(p.y>y1)y1=p.y;}\r\n return {x0,y0,x1,y1,w:x1-x0,h:y1-y0};\r\n}\r\nfunction resample(points,N=32){\r\n const distances=[0];for(let i=1;i<points.length;i++)distances.push(distances.at(-1)+dst(points[i],points[i-1]));\r\n const length=distances.at(-1);if(!length)return null;\r\n let segment=1;return Array.from({length:N},(_,i)=>{const target=length*i/(N-1);while(segment<points.length-1&&distances[segment]<target)segment++;\r\n const lo=points[segment-1],hi=points[segment],t=(target-distances[segment-1])/(distances[segment]-distances[segment-1]||1);\r\n return {x:lo.x+(hi.x-lo.x)*t,y:lo.y+(hi.y-lo.y)*t};});\r\n}\r\n\r\nfunction alignScore(trace,template){\r\n const N=trace.length;\r\n let best=Infinity;\r\n for(let rev=0;rev<2;rev++){\r\n  const t=rev?trace.slice().reverse():trace;\r\n  for(let rot=0;rot<N;rot++){\r\n   let s=0;\r\n   for(let i=0;i<N;i++){const a=t[(i+rot)%N];const b=template[i];s+=Math.hypot(a.x-b.x,a.y-b.y);}\r\n   s/=N;if(s<best)best=s;\r\n  }\r\n }\r\n return best;\r\n}\r\nexport function recognizeCombatShape(points){\r\n if(!points||points.length<4)return null;\r\n const norm=[];\r\n const xs=points.map(p=>p.x),ys=points.map(p=>p.y);\r\n const bb={x0:Math.min(...xs),y0:Math.min(...ys),x1:Math.max(...xs),y1:Math.max(...ys)};\r\n bb.w=bb.x1-bb.x0;bb.h=bb.y1-bb.y0;\r\n const span=Math.max(bb.w,bb.h);\r\n if(span<1e-4)return null;\r\n for(const p of points)norm.push({x:(p.x-bb.x0)/span,y:(p.y-bb.y0)/span});\r\n // reject straight line (any orientation)\r\n const diag=Math.hypot(bb.w,bb.h)/span||1e-4;\r\n let len=0;for(let i=1;i<norm.length;i++)len+=Math.hypot(norm[i].x-norm[i-1].x,norm[i].y-norm[i-1].y);\r\n if(len < diag*1.15)return null;\r\n // reject noise: too long relative to bbox\r\n if(len > diag*8)return null;\r\n const trace=resample(norm,32);\r\n if(!trace)return null;\r\n let best=null,bestScore=Infinity;\r\n for(const name of Object.keys(SHAPE_TEMPLATES)){\r\n  const tpl=resample(SHAPE_TEMPLATES[name].map(([x,y])=>({x,y})),32);\r\n  if(!tpl)continue;\r\n  const s=alignScore(trace,tpl);\r\n  if(s<bestScore){bestScore=s;best=name;}\r\n }\r\n if(bestScore>0.32)return null;\r\n const closed=CLOSED.has(best);\r\n if(closed){\r\n  const first=trace[0],last=trace[31];\r\n  const gap=Math.hypot(first.x-last.x,first.y-last.y);\r\n  const bbSpan=Math.max(bb.w,bb.h)||1e-4;\r\n  const closure=gap;\r\n  if(closure>0.35)return null;\r\n }\r\n return best;\r\n}\r\n\r\n// --- hand assignment: globally nearest valid pairs within bounded distance ---\r\nfunction assignHands(raw,aspect){\r\n const pose=raw?.pose?.[0];\r\n const hands=raw?.landmarks||[];\r\n const world=raw?.worldLandmarks||[];\r\n if(!pose||!hands.length)return [];\r\n const wrists=[];\r\n  if(valid(pose[WRIST_L])&&(pose[WRIST_L].visibility??1)>=HIGH_CONF)wrists.push({idx:WRIST_L,side:'left',p:pose[WRIST_L]});\r\n  if(valid(pose[WRIST_R])&&(pose[WRIST_R].visibility??1)>=HIGH_CONF)wrists.push({idx:WRIST_R,side:'right',p:pose[WRIST_R]});\r\n if(!wrists.length)return [];\r\n const pairs=[];\r\n for(let hi=0;hi<hands.length;hi++){\r\n  const h=hands[hi];if(!h||h.length!==21)continue;\r\n  if(!valid(h[0]))continue;\r\n  for(let wi=0;wi<wrists.length;wi++){\r\n   const d=dst(h[0],wrists[wi].p,aspect);\r\n   if(d<=HAND_MAX_DIST)pairs.push({hi,wi,d});\r\n  }\r\n }\r\n pairs.sort((a,b)=>a.d-b.d);\r\n const usedH=new Set(),usedW=new Set();\r\n const res=[];\r\n for(const p of pairs){\r\n  if(usedH.has(p.hi)||usedW.has(p.wi))continue;\r\n  usedH.add(p.hi);usedW.add(p.wi);\r\n  const h=hands[p.hi];\r\n  let cls='other';\r\n  try{cls=classifyHand(h,world[p.hi],aspect);}catch(e){cls='other';}\r\n  res.push({side:wrists[p.wi].side,wrist:wrists[p.wi].p,hand:h,world:world[p.hi],cls});\r\n }\r\n return res;\r\n}\r\n\r\nexport function createCombatInput(){\r\n const st={\r\n  prevTs:0,\r\n  lastGoodTs:0,\r\n  prevWrist:{left:null,right:null},\r\n  lastWrist:{left:null,right:null},\r\n  neutralShoulder:null,\r\n  neutralTorso:null,\r\n  backSegment:null,\r\n  backArmed:false,\r\n  punch:{left:false,right:false},\r\n  slashUntil:{left:0,right:0},\r\n  slashActive:{left:false,right:false},\r\n  extState:{left:false,right:false},\r\n  handsUpSince:0,\r\n  channelSince:0,\r\n  throwSince:0,\r\n  traceSide:null,\r\n  tracePts:[],\r\n  traceStart:0,\r\n  spellEmitted:false,\r\n  pendingShape:null,\r\n  pendingSince:0\r\n  ,runeCenter:null\r\n };\r\n\r\n function reset(){\r\n  st.prevTs=0;st.lastGoodTs=0;st.prevWrist={left:null,right:null};st.lastWrist={left:null,right:null};\r\n  st.neutralShoulder=null;st.neutralTorso=null;st.backSegment=null;st.backArmed=false;\r\n  st.punch={left:false,right:false};st.slashUntil={left:0,right:0};st.slashActive={left:false,right:false};\r\n  st.extState={left:false,right:false};st.handsUpSince=0;st.channelSince=0;st.throwSince=0;\r\n  st.traceSide=null;st.tracePts=[];st.traceStart=0;st.spellEmitted=false;st.pendingShape=null;st.pendingSince=0;\r\n  st.runeCenter=null;\r\n }\r\n\r\n function clearGestures(){\r\n  st.punch={left:false,right:false};st.slashActive={left:false,right:false};st.slashUntil={left:0,right:0};\r\n  st.extState={left:false,right:false};st.handsUpSince=0;st.channelSince=0;st.throwSince=0;st.backArmed=false;\r\n  st.traceSide=null;st.tracePts=[];st.traceStart=0;st.pendingShape=null;st.pendingSince=0;st.spellEmitted=false;\r\n }\r\n\r\n function snapshot(){return {spell:st.pendingShape||null,runeCenter:st.runeCenter?{...st.runeCenter}:null};}\r\n\r\n function observe(raw,opt={}){\r\n  const out={};\r\n  for(const k of CLASS_INPUT_BOOLS)out[k]=false;\r\n  out.spell=null;out.sourceTs=Number.isFinite(raw?.ts)?raw.ts:null;out.quality={body:false,hands:false};\r\n  const pose=raw?.pose?.[0];\r\n  const hands=raw?.landmarks||[];\r\n  const world=raw?.worldLandmarks||[];\r\n  const aspect=opt.aspect>0?opt.aspect:1;\r\n  const now=(typeof raw?.ts==='number')?raw.ts:(typeof performance!=='undefined'?performance.now():Date.now());\r\n  const dt=st.prevTs?Math.max(1,now-st.prevTs):16;\r\n  st.prevTs=now;\r\n\r\n  // body booleans always come from provided body/options\r\n  out.left=!!opt.left;out.right=!!opt.right;\r\n  out.crouch=!!opt.crouch;out.jump=!!opt.jump;out.jog=!!opt.jog;\r\n\r\n  if(!pose){clearGestures();st.lastGoodTs=0;st.neutralShoulder=null;st.neutralTorso=null;st.backSegment=null;st.prevWrist={left:null,right:null};return out;}\r\n  if(st.lastGoodTs&&now-st.lastGoodTs>STALE_MS){clearGestures();st.lastGoodTs=now;st.neutralShoulder=null;st.neutralTorso=null;st.backSegment=null;st.prevWrist={left:null,right:null};return out;}\r\n  st.lastGoodTs=now;\r\n\r\n  // confidence: require high confidence on both shoulders and hips\r\n  const sl=pose[SHOULDER_L],sr=pose[SHOULDER_R],hl=pose[HIP_L],hr=pose[HIP_R];\r\n  const confOk=[sl,sr,hl,hr].every(p=>valid(p)&&((p.visibility??1)>=HIGH_CONF));\r\n  if(!confOk){clearGestures();st.neutralShoulder=null;st.neutralTorso=null;st.prevWrist={left:null,right:null};return out;}\r\n\r\n  out.quality.body=[sl,sr,hl,hr].every(p=>Number.isFinite(p.visibility)&&p.visibility>=HIGH_CONF);\n  // baseline shoulder width\r\n  const sw=Math.abs((sl.x-sr.x)*aspect)+Math.abs(sl.y-sr.y);\r\n  const torso=Math.abs((hl.y+hr.y-sl.y-sr.y)/2);if(st.neutralTorso===null)st.neutralTorso=torso;\r\n  if(st.neutralShoulder==null)st.neutralShoulder=sw;\r\n  // Keep the calibrated baseline while back; holding still must not rearm a dodge.\r\n\r\n  // back: torso-scale reduction relative baseline, 8% shrink to arm, rearm within 4%\r\n  const ratio=sw/(st.neutralShoulder||sw);\r\n  if(ratio<0.92&&torso/st.neutralTorso<.94)st.backArmed=true;\r\n  if(st.backArmed&&ratio>0.96)st.backArmed=false;\r\n  out.back=st.backArmed;\r\n\r\n  // assign hands uniquely\r\n  const assigned=assignHands(raw,aspect);\r\n  const bySide={left:null,right:null};\r\n  for(const a of assigned)bySide[a.side]=a;\r\n\r\n  out.quality.hands=!!bySide.left&&!!bySide.right;\r\n  // per-hand features\r\n  const wristNow={left:null,right:null};\r\n  for(const side of ['left','right']){\r\n   const a=bySide[side];\r\n   if(a)wristNow[side]=a.wrist;\r\n  }\r\n\r\n  function armExtended(side){\r\n   const a=bySide[side];\r\n   if(!a)return false;\r\n   const elbowIdx=side==='left'?ELBOW_L:ELBOW_R;\r\n   const shoulderIdx=side==='left'?SHOULDER_L:SHOULDER_R;\r\n   const e=pose[elbowIdx],s=pose[shoulderIdx],w=a.wrist;\r\n   if(!valid(e)||!valid(s))return false;\r\n   const ang=angleAt(s,e,w,aspect);\r\n   const ext=st.extState[side];\r\n   if(ext)return ang>=145;\r\n   return ang>=150;\r\n  }\r\n  function handSpeed(side){\r\n   const prev=st.prevWrist[side],cur=wristNow[side];\r\n   if(!prev||!cur)return 0;\r\n   return wristSpeed(prev,cur,dt);\r\n  }\r\n\r\n  for(const side of ['left','right']){\r\n   const a=bySide[side];\r\n   if(!a){st.punch[side]=false;st.slashActive[side]=false;continue;}\r\n   const isFist=a.cls==='fist';\r\n   const isOpen=a.cls==='open'||a.cls==='pinch';\r\n   const ext=armExtended(side);\r\n   st.extState[side]=ext;\r\n   const sp=handSpeed(side);\r\n   // punch: extended elbow + matching fist with hysteresis on angle+speed\r\n   st.punch[side]=isFist&&ext;\r\n   // slash: open hand directional speed\r\n   const prev=st.prevWrist[side],horizontal=prev&&Math.abs(a.wrist.x-prev.x)>.035&&Math.abs(a.wrist.x-prev.x)>Math.abs(a.wrist.y-prev.y);\r\n   if((isOpen||isFist)&&horizontal&&sp>.6){st.slashActive[side]=true;st.slashUntil[side]=now+140;}\r\n   else if(now>st.slashUntil[side])st.slashActive[side]=false;\r\n   out[side==='left'?'punchLeft':'punchRight']=st.punch[side];\r\n   out[side==='left'?'slashLeft':'slashRight']=st.slashActive[side];\r\n  }\r\n\r\n  // guard: single or crossed wrists\r\n  const ax=bySide.left&&bySide.right;\r\n  if(ax){\r\n   const wl=bySide.left.wrist,wr=bySide.right.wrist;\r\n   const d=dst(wl,wr,aspect);\r\n   const crossedNear=(wl.x-wr.x)*(sl.x-sr.x)<0&&d<.28;\r\n   const singleNear=d<0.09;\r\n   out.guard=singleNear||crossedNear;\r\n   out.crossGuard=crossedNear&&(bySide.left.cls==='fist'||bySide.left.cls==='open'||bySide.left.cls==='pinch')&&(bySide.right.cls==='fist'||bySide.right.cls==='open'||bySide.right.cls==='pinch');\r\n   // throw: both arms forward, no latency\r\n   let throwNow=false;\r\n   if(bySide.left.cls!=='other'&&bySide.right.cls!=='other'){\r\n    const forward=(wl.y<0.5)&&(wr.y<0.5);\r\n    const spread=Math.abs(wl.x-wr.x)>0.08;\r\n    if(forward&&spread&&st.extState.left&&st.extState.right)throwNow=true;\r\n   }\r\n   if(throwNow){st.throwSince+=dt;out.throwBlades=true;}else st.throwSince=0;\r\n  }else{\r\n   st.throwSince=0;\r\n  }\r\n  out.guard=out.guard||['left','right'].some(side=>{const a=bySide[side];if(!a)return false;const e=pose[side==='left'?ELBOW_L:ELBOW_R];return valid(e)&&a.wrist.y<e.y-.06&&Math.abs(a.wrist.x-e.x)<.16;});\r\n\r\n  // handsUp: both wrists above elbows+shoulders, immediate\r\n  const upL=bySide.left&&valid(pose[ELBOW_L])&&valid(pose[SHOULDER_L])&&bySide.left.wrist.y<pose[ELBOW_L].y&&bySide.left.wrist.y<pose[SHOULDER_L].y;\r\n  const upR=bySide.right&&valid(pose[ELBOW_R])&&valid(pose[SHOULDER_R])&&bySide.right.wrist.y<pose[ELBOW_R].y&&bySide.right.wrist.y<pose[SHOULDER_R].y;\r\n  out.handsUp=!!(upL&&upR);\r\n\r\n  // channel: one hand open+extended AND stationary for 400ms\r\n  let chanSide=null;\r\n  for(const side of ['left','right']){\r\n   const a=bySide[side];\r\n   if(!a)continue;\r\n   const isOpen=a.cls==='open'||a.cls==='pinch';\r\n   const ext=st.extState[side];\r\n   const sp=handSpeed(side);\r\n   if(isOpen&&ext&&sp<0.25)chanSide=side;\r\n  }\r\n  if(chanSide){st.channelSince+=dt;if(st.channelSince>=400)out.channel=true;}else st.channelSince=0;\r\n\r\n  // tracing: lock to one hand until released; track moving open hand freely regardless of extension\r\n  if(opt.classId==='mage'&&!out.crossGuard){\r\n   updateTrace(bySide,dt,out,aspect);\r\n  }else{\r\n   // channel/crossGuard suppress tracing\r\n   st.traceSide=null;st.tracePts=[];st.pendingShape=null;st.spellEmitted=false;\r\n  }\r\n\r\n  st.prevWrist={left:wristNow.left,right:wristNow.right};\r\n  if(opt.classId==='mage'&&st.runeCenter){\r\n   for(const side of ['left','right'])if(out[side==='left'?'punchLeft':'punchRight'])out[side==='left'?'punchLeft':'punchRight']=!!wristNow[side]&&dst(wristNow[side],st.runeCenter,aspect)<.18;\r\n  }\r\n  return out;\r\n }\r\n\r\n function updateTrace(bySide,dt,out,aspect){\r\n  // choose/lock a side: prefer already locked; else the open hand\r\n  let side=st.traceSide;\r\n  if(side&&(!bySide[side]||(bySide[side].cls!=='open'&&bySide[side].cls!=='pinch'))){\r\n   // release lock -> reset trace\r\n   st.traceSide=null;st.tracePts=[];st.pendingShape=null;st.pendingSince=0;st.spellEmitted=false;\r\n   side=null;\r\n  }\r\n  if(!side){\r\n   if(bySide.left&&(bySide.left.cls==='open'||bySide.left.cls==='pinch'))side='left';\r\n   else if(bySide.right&&(bySide.right.cls==='open'||bySide.right.cls==='pinch'))side='right';\r\n   if(side){st.traceSide=side;st.tracePts=[];st.pendingShape=null;st.pendingSince=0;st.spellEmitted=false;}\r\n  }\r\n  if(!side)return;\r\n  const a=bySide[side];\r\n  if(!a)return;\r\n  const p={x:a.wrist.x,y:a.wrist.y};\r\n  const arr=st.tracePts;\r\n  const prev=arr[arr.length-1];\r\n  if(!prev||Math.hypot(p.x-prev.x,p.y-prev.y)>0.004){\r\n   st.traceLastMove=st.prevTs;\r\n   arr.push(p);\r\n   if(arr.length>140)arr.shift();\r\n  }\r\n  // attempt recognition only after 250ms of drawing\r\n  if(arr.length>5&&st.prevTs-(st.traceLastMove??st.prevTs)>=250){\r\n   const bb=bbox(arr);\r\n   const span=Math.max(bb.w,bb.h);\r\n   if(span>=0.15){\r\n    const shape=recognizeCombatShape(arr.map(p=>({x:p.x*aspect,y:p.y})));\r\n    if(shape){\r\n     if(!st.spellEmitted){\r\n      st.pendingShape=shape;\r\n      st.pendingSince+=dt;\r\n      out.spell=shape;st.spellEmitted=true;st.runeCenter={x:bb.x0+bb.w/2,y:bb.y0+bb.h/2};\r\n     }\r\n    }else{\r\n     st.pendingShape=null;st.pendingSince=0;\r\n    }\r\n   }\r\n  }\r\n }\r\n\r\n return {observe,reset,snapshot};\r\n}\r\n\r\nexport default createCombatInput;\r\n","modules/roguelite/combat-view.css":"/* The duel overlay shares the scene's pixel style without another WebGL context. */\n.combat-view{position:absolute;inset:0;z-index:3;pointer-events:none;color:#ffe671;--edge:#d5ddd1;--glow:#ffe671;image-rendering:pixelated;overflow:hidden}\n.combat-view [hidden],.combat-view[hidden]{display:none!important}\n.combat-view[data-red=true]{color:#ff6252}\n.combat-swing{position:absolute;top:35%;left:27%;width:46%;max-width:360px;text-align:center;transform-origin:50% 65%}\n.combat-swing svg{width:100%;height:140px;filter:drop-shadow(0 0 8px currentColor)}\n.combat-arrow{font:bold 20px monospace;text-shadow:0 2px 4px #000}\n.combat-attack{font:bold 13px monospace;text-transform:uppercase;text-shadow:0 2px 4px #000}\n.combat-view[data-side=right] .combat-swing svg{transform:scaleX(-1)}\n.combat-view[data-cue=telegraph] .combat-swing{opacity:.7;transform:rotate(-12deg) scale(.9)}\n.combat-view[data-cue=window] .combat-swing{opacity:1;transform:rotate(12deg) scale(1.1)}\n.combat-swing{transition:transform 140ms ease-out}\n.combat-view[data-side=left]::before,.combat-view[data-side=right]::before{content:\"\";position:absolute;top:30%;height:34%;width:12%;background:linear-gradient(90deg,currentColor,transparent);opacity:.15}\n.combat-view[data-side=left]::before{left:0}\n.combat-view[data-side=right]::before{right:0;transform:scaleX(-1)}\n.combat-view[data-cue=recovery]::before,.combat-view[data-cue=opening]::before{display:none}\n.combat-hands{position:absolute;bottom:23%;left:0;width:100%;height:130px}\n.combat-hand{position:absolute;width:68px;height:115px;bottom:0;transition:transform 100ms ease-out;filter:drop-shadow(0 0 4px var(--glow))}\n.combat-hand.left{left:13%;transform:rotate(-12deg)}\n.combat-hand.right{right:13%;transform:rotate(12deg)}\n.combat-grip{position:absolute;bottom:0;left:20%;width:60%;height:48%;background:repeating-linear-gradient(0deg,#66594c 0 7px,#ac9376 7px 10px);border:3px solid #1a2121;border-radius:6px}\n.combat-edge{position:absolute;left:29%;top:0;width:42%;height:70%;background:linear-gradient(90deg,#6e827f,var(--edge),#7f9797);clip-path:polygon(50% 0,100% 82%,60% 100%,40% 100%,0 82%)}\n.combat-view[data-class=knight] .left{left:7%;width:80px;height:112px;background:repeating-linear-gradient(90deg,#687975 0 14px,#344849 14px 19px);border:5px solid #c0cebd;clip-path:polygon(10% 0,90% 0,100% 80%,50% 100%,0 80%)}\n.combat-view[data-class=knight] .left .combat-edge,.combat-view[data-class=knight] .left .combat-grip{display:none}\n.combat-view[data-class=knight] .right{height:210px;width:45px;transform:rotate(24deg)}\n.combat-view[data-class=knight] .right .combat-grip{height:75%;width:22%;left:39%;border-width:1px}\n.combat-view[data-class=knight] .right .combat-edge{height:38%;width:70%;left:15%}\n.combat-view[data-class=monk] .left{left:calc(50% - 76px)}\n.combat-view[data-class=monk] .right{right:calc(50% - 76px)}\n.combat-view[data-class=monk] .combat-hands{bottom:34%}\n.combat-view[data-class=monk] .combat-hand{height:75px;width:65px;border:4px solid #513b2a;border-radius:16px;background:repeating-linear-gradient(90deg,#b89e82 0 13px,#695443 13px 16px)}\n.combat-view[data-class=monk] .combat-edge{top:22%;height:20%;width:90%;left:5%;clip-path:none;border-radius:5px;background:#a38b46}\n.combat-view[data-class=monk] .combat-grip{height:32%;left:0;width:100%;border:none;border-radius:0;background:repeating-linear-gradient(0deg,#ded7b7 0 5px,#9e9e86 5px 7px)}\n.combat-view[data-class=monk][data-lane=left] .combat-hand{rotate:-14deg}\n.combat-view[data-class=monk][data-lane=right] .combat-hand{rotate:14deg}\n.combat-view[data-class=dual][data-guard=true] .left{transform:translateX(85px) rotate(55deg)}\n.combat-view[data-class=dual][data-guard=true] .right{transform:translateX(-85px) rotate(-55deg)}\n.combat-view[data-throw=true] .combat-hand{transform:translateY(-110px) scale(.6);opacity:.55}\n.combat-hand[data-punch=true]{transform:translateY(-50px) scale(1.08)!important}\n.combat-view[data-class=mage] .combat-grip{height:90%;background:repeating-linear-gradient(90deg,#c3aa88 0 8px,#655947 8px 10px);border-radius:35% 35% 12% 12%;box-shadow:0 0 14px #55bfea}\n.combat-view[data-class=mage] .combat-edge{display:none}\n.combat-view[data-class=mage][data-guard=true] .combat-hands{filter:drop-shadow(0 0 18px #61bbff);transform:translateY(-50px)}\n.combat-rune{position:absolute;top:45%;left:50%;width:100px;height:100px;transform:translate(-50%,-50%);color:#6edbff;filter:drop-shadow(0 0 8px #4daaff)}\n.combat-stream{position:absolute;top:46%;bottom:23%;left:calc(50% - 12px);width:24px;background:linear-gradient(90deg,transparent,#b5efff,transparent);transform:rotate(8deg);box-shadow:0 0 16px #52afff}\n.combat-view[data-spell=triangle] .combat-stream{background:linear-gradient(90deg,transparent,#ffd576,#ff5928,transparent);box-shadow:0 0 18px #ff6622}\n.combat-view[data-spell=square] .combat-stream{background:repeating-linear-gradient(0deg,#cfb17b 0 12px,transparent 12px 20px);box-shadow:0 0 8px #c69865}\n.combat-view[data-spell=z] .combat-stream{clip-path:polygon(50% 0,100% 40%,55% 40%,100% 100%,0 55%,45% 55%,0 0);background:#f0cbff;box-shadow:0 0 14px #a157ff}\n.combat-view[data-spell=v] .combat-stream{background:repeating-linear-gradient(0deg,transparent 0 8px,#dafaff 8px 11px);width:44px;left:calc(50% - 22px)}\n.combat-view[data-tier=\"1\"]{--edge:#ffad4f;--glow:#ff6a15}\n.combat-view[data-tier=\"2\"]{--edge:#9affff;--glow:#26eaff}\n.combat-status{position:absolute;left:5%;bottom:16%;width:90%;padding:7px 9px;background:#101b20e8;border:1px solid #9ba991;border-radius:5px;color:#eef5de;font:700 12px/1.4 monospace;text-align:center;box-sizing:border-box}\n.combat-aura{position:absolute;left:50%;bottom:17%;width:260px;height:260px;transform:translateX(-50%);opacity:.8}\n.combat-view[data-impact=attack],.combat-view[data-impact=blocked]{background:radial-gradient(ellipse,transparent 30%,#e9ffc529)}\n.combat-view[data-impact=parried]{background:radial-gradient(ellipse at 50% 45%,#fff8be66,transparent 55%)}\n.combat-view[data-impact=hit]{background:radial-gradient(ellipse,transparent 30%,#ff443766)}\n.combat-view[data-impact=combo],.combat-view[data-impact=super]{background:radial-gradient(ellipse at 50% 45%,#ffce5944,transparent 60%)}\n.combat-view[data-impact=scratch]{background:radial-gradient(ellipse at 50% 45%,#c2d2c512,transparent 40%)}\n.combat-feedback{position:absolute;top:25%;left:10%;width:80%;text-align:center;font:900 22px/1.2 monospace;color:#fff3ba;text-shadow:0 3px 0 #172124,0 0 10px #ffd14a}\n.combat-view[data-impact=hit] .combat-feedback{color:#ff9b8e}\n.combat-view[data-impact=scratch] .combat-feedback{font-size:14px;color:#bec8bc}\n.combat-sparks{position:absolute;top:46%;left:50%;width:1px;height:1px}\n.combat-spark{position:absolute;width:3px;height:10px;background:#fff6bd;box-shadow:0 0 4px #ffd85b;opacity:0}\n.combat-view[data-sparks=true][data-fx=a] .combat-spark{animation:combat-spark-a 180ms ease-out both}\n.combat-view[data-sparks=true][data-fx=b] .combat-spark{animation:combat-spark-b 180ms ease-out both}\n@keyframes combat-spark-a{from{opacity:1;transform:translate(0,0) rotate(var(--angle))}to{opacity:0;transform:translate(var(--x),var(--y)) rotate(var(--angle))}}\n@keyframes combat-spark-b{from{opacity:1;transform:translate(0,0) rotate(var(--angle))}to{opacity:0;transform:translate(var(--x),var(--y)) rotate(var(--angle))}}\n.combat-view:not(.reduced)[data-impact=hit] .combat-hands{animation:combat-recoil 160ms ease-out}\n@keyframes combat-recoil{0%{translate:0 7px}40%{translate:4px 0}100%{translate:0 0}}\n.combat-view[data-frozen=true]{background:radial-gradient(ellipse at 50% 45%,#8dcaff33,transparent 50%)}\n.combat-view[data-paused=true] .combat-hands{opacity:.35}\n.combat-view.reduced *{transition:none!important;animation:none!important}\n@media(prefers-reduced-motion:reduce){.combat-view *{transition:none!important;animation:none!important}}\n@media(min-width:760px){.combat-status{left:25%;width:50%}.combat-hands{left:20%;width:60%}.combat-swing{left:calc(50% - 180px)}}\nbody[data-combat-class]:not([data-combat-class=\"\"]) #objective{bottom:24%;left:12%;right:12%;gap:3px}\nbody[data-combat-class]:not([data-combat-class=\"\"]) #goal{font-size:48px;line-height:1.05}\nbody[data-combat-class]:not([data-combat-class=\"\"]) #verb{font-size:20px}\nbody[data-combat-class]:not([data-combat-class=\"\"]) #cue{font-size:16px;line-height:1.25}\n","modules/roguelite/combat-view.mjs":"import {auraFramesSync,drawAura2d} from '../coach-world-rts/aura-sprites.mjs';\nimport {physicalSound} from '../../audio/physical-sound.mjs';\nimport {createCombatImpact} from './combat-impact.mjs';\nimport {mapPointToOverlay} from '../../arcade/workout-effects-state.mjs';\n\nconst gear={\n knight:['Barbell lance + tower shield','Fire lance + iron wall shield','Energy lance + force wall'],\n monk:['Wrist wraps + brass knuckles','Aura fists','Sci-fi power fists'],\n dual:['Paired dumbbells','Fire daggers','Energy short swords'],\n mage:['Elemental focus','Enchanted runes','Plasma runes'],\n};\nconst runes={triangle:'M50 5 L95 90 H5 Z',square:'M8 8 H92 V92 H8 Z',z:'M8 12 H92 L8 88 H92',v:'M8 12 L50 88 L92 12',circle:'M50 5 A45 45 0 1 1 49.9 5'};\nexport function combatRunePosition(center,{width,height,videoWidth,videoHeight,mirrored=false}={}){\n if(!center||![center.x,center.y,width,height,videoWidth,videoHeight].every(Number.isFinite)||\n  center.x<0||center.x>1||center.y<0||center.y>1||Math.min(width,height,videoWidth,videoHeight)<=0)return null;\n const point=mapPointToOverlay(center,{hostWidth:width,hostHeight:height,videoWidth,videoHeight,mirrored});\n return {x:point.x*width,y:point.y*height};\n}\nconst element=(tag,cls,parent)=>{const el=document.createElement(tag);el.className=cls;parent?.append(el);return el;};\nconst svg=(path,parent)=>{\n const el=document.createElementNS('http://www.w3.org/2000/svg','svg');el.setAttribute('viewBox','0 0 100 100');el.setAttribute('aria-hidden','true');\n const line=document.createElementNS(el.namespaceURI,'path');line.setAttribute('d',path);line.setAttribute('fill','none');line.setAttribute('stroke','currentColor');line.setAttribute('stroke-width','5');line.setAttribute('stroke-linecap','round');line.setAttribute('stroke-linejoin','round');el.append(line);parent.append(el);return line;\n};\n\n/** Preallocated duel cues: no extra WebGL context or per-hit particle allocation. */\nexport function mountCombatView(parent,{reducedMotion=false}={}){\n const root=element('div','combat-view',parent);root.hidden=true;root.classList.toggle('reduced',reducedMotion);\n const swing=element('div','combat-swing',root);svg('M8 80 Q10 5 90 20 M75 8 L92 20 L78 34',swing);\n const arrow=element('div','combat-arrow',swing),attack=element('div','combat-attack',swing);\n const aura=element('canvas','combat-aura',root);aura.width=aura.height=256;const ctx=aura.getContext('2d');\n const hands=element('div','combat-hands',root),left=element('div','combat-hand left',hands),right=element('div','combat-hand right',hands);\n for(const hand of [left,right]){element('div','combat-grip',hand);element('div','combat-edge',hand);}\n const rune=element('div','combat-rune',root),runePath=svg(runes.circle,rune),stream=element('div','combat-stream',root);\n const status=element('div','combat-status',root);status.setAttribute('role','status');status.setAttribute('aria-live','polite');\n const feedback=element('div','combat-feedback',root);feedback.setAttribute('aria-live','polite');\n const sparks=element('div','combat-sparks',root);\n for(let i=0;i<12;i++){const spark=element('i','combat-spark',sparks),angle=i*Math.PI/6;spark.style.setProperty('--x',`${Math.cos(angle)*(35+i%3*15)}px`);spark.style.setProperty('--y',`${Math.sin(angle)*(35+i%3*15)}px`);spark.style.setProperty('--angle',`${i*30}deg`);}\n const impact=createCombatImpact({reducedMotion});\n let disposed=false,lastCue='',timer=null,freezeUntil=0,freezeAttack=null,pausedView=false,currentCue=null,flip=false,lastAuraOn=false,lastAuraClock=-100;\n function update({boss,input={},classId,tier=0,paused=false,runeCenter=null,runeFrame=null}={}){\n  if(disposed)return;root.hidden=!boss||!gear[classId];if(root.hidden){impact.reset();freezeUntil=0;return;}\n  const c=boss.combat??{},cue=boss.cue??{},clock=c.clockMs??c.elapsedMs??0;\n  pausedView=paused;currentCue=cue;\n  const fx=impact.update(boss,{paused});\n  if(paused){freezeUntil=0;root.dataset.impact='';feedback.textContent='';clearTimeout(timer);}\n  if(fx.event){\n   const event=fx.event,type=event.type;root.dataset.impact=type;flip=!flip;root.dataset.fx=flip?'a':'b';\n   root.dataset.sparks=String(fx.spark);\n   const labels={parried:'PARRY!',blocked:'BLOCK',dodged:'DODGE',hit:'HIT TAKEN',super:'SUPER BREAK!',combo:'COMBO!',scratch:'SCRATCH',attack:'CLEAN HIT',clash:'CLASH!',clash_win:'CLASH WON!',clash_loss:'RESET YOUR STANCE'};\n   feedback.textContent=labels[type]??'';\n   if(type==='parried'){physicalSound.play('electric');globalThis.navigator?.vibrate?.(12);if(!reducedMotion&&fx.hitStop){freezeUntil=performance.now()+65;freezeAttack=event.attackNo;}}\n   else physicalSound.play(type==='hit'?'flight-impact':['super','combo','clash_win'].includes(type)?'flight-stunt':type==='dodged'?'flight-coin':type==='clash'?'flight-warning':classId==='mage'?'electric':'wood');\n   clearTimeout(timer);timer=setTimeout(()=>{root.dataset.impact='';root.dataset.sparks='false';feedback.textContent='';},180);\n  }\n  root.dataset.class=classId;root.dataset.tier=String(Math.max(0,Math.min(2,tier)));root.dataset.side=cue.direction??'left';root.dataset.cue=cue.name??'recovery';root.dataset.red=String(cue.kind?.startsWith('red')??false);root.dataset.paused=String(paused);\n  root.dataset.guard=String(classId==='dual'?!!input.crossGuard:classId==='mage'?!!input.handsUp&&!c.shieldBroken:!!(input.guard||input.left));\n  root.dataset.frozen=String(c.freezeUntilMs>clock);\n  root.dataset.channel=String(classId==='mage'&&!!input.channel&&!!(c.spell??c.activeSpell)&&!input.handsUp);\n  root.dataset.throw=String(classId==='dual'&&!!input.throwBlades);\n  root.dataset.lane=input.left?'left':input.right?'right':'center';left.dataset.punch=String(!!input.punchLeft);right.dataset.punch=String(!!input.punchRight);\n  const incoming=cue.name==='telegraph'||cue.name==='window';swing.hidden=!incoming;\n  const cueKey=`${boss.bossId}:${cue.attackNo}:${cue.name}`;\n  if(!paused&&cueKey!==lastCue&&cue.name==='telegraph')physicalSound.play('flight-warning');lastCue=cueKey;\n  arrow.textContent=cue.direction==='right'?'RIGHT →':'← LEFT';attack.textContent=cue.attackName??(root.dataset.red==='true'?'POWER STRIKE':'WEAPON SWEEP');\n  const shape=c.pendingSpell??c.spell??c.activeSpell??(c.freezeUntilMs>clock?'circle':input.spell);\n  root.dataset.spell=c.spell??c.pendingSpell??c.lastSpell??'';\n  rune.hidden=classId!=='mage'||!runes[shape];runePath.setAttribute('d',runes[shape]??runes.circle);stream.hidden=root.dataset.channel!=='true';\n  const position=combatRunePosition(runeCenter,{width:root.clientWidth,height:root.clientHeight,videoWidth:runeFrame?.width,videoHeight:runeFrame?.height,mirrored:runeFrame?.mirrored===true});\n  rune.style.left=position?`${position.x}px`:'';rune.style.top=position?`${position.y}px`:'';\n  const auraOn=classId==='monk'&&(c.auraUntilMs??c.auraUntil??0)>clock;aura.hidden=!auraOn;\n  if(ctx&&(auraOn!==lastAuraOn||auraOn&&(!reducedMotion&&clock-lastAuraClock>=100||clock<lastAuraClock))){ctx.clearRect(0,0,256,256);if(auraOn)drawAura2d(ctx,auraFramesSync('protectioncircle'),128,128,250,reducedMotion?0:clock/80);lastAuraOn=auraOn;lastAuraClock=clock;}\n  const backMs=Math.max(0,(c.backReadyMs??c.backReadyAt??0)-clock);\n  const shield=c.shieldPoints??c.shield??3;\n  const parts=[gear[classId][Math.max(0,Math.min(2,tier))]];\n  if(classId==='knight')parts.push(input.guard||input.left?'GUARD · ½ ATTACK SPEED':'SHIELD + LANCE');\n  if(classId==='monk')parts.push(`COMBO ${c.combo??0}`,auraOn?'AURA +25%':`CHARGE ${Math.min(3,Math.floor((c.auraHoldMs??c.holdMs??0)/100)/10)} / 3s`);\n  if(classId==='dual'||classId==='mage')parts.push(backMs?`BACKSTEP ${(backMs/1000).toFixed(1)}s`:'BACKSTEP READY');\n  if(c.lostTargetUntilMs>clock)parts.push('TARGET LOST · FREE HITS');\n  if(classId==='mage')parts.push(c.shieldBroken?'SHIELD BROKEN · LOWER + RAISE':`SHIELD ${shield}/3`,shape?`${shape.toUpperCase()} · ${c.burstsRemaining??'PUNCH CENTER'}`:'DRAW A SHAPE');\n  parts.push(c.superArmed?'SUPER READY · ATTACK':`SUPER ${Math.floor(c.superMeter??0)}%`);\n  if(cue.name==='clash')parts.push(`CLASH · ${c.clash?.punches??0}/4 ALTERNATING HITS OR JOG`);\n  const text=paused?'PAUSED':parts.join(' · ');if(status.textContent!==text)status.textContent=text;\n }\n return {update,isHitStopped(){return !disposed&&!pausedView&&!reducedMotion&&performance.now()<freezeUntil&&!(['telegraph','window'].includes(currentCue?.name)&&currentCue.attackNo!==freezeAttack);},dispose(){if(disposed)return;disposed=true;clearTimeout(timer);impact.reset();root.remove();}};\n}\n","modules/roguelite/corgi-break-room.mjs":"// Extracted from MOM Inc. Cortisol Corgi tv/channels/corgi.js, site reference f059bda.\r\n// The authored furniture stays in place; the shell completes the surrounding room.\nimport {createARRoomShell} from './ar-room-shell.mjs';\n\nexport function createCorgiBreakRoom(THREE,{aspect=4/3,seed=101,reducedMotion=false}={}) {\n  const viewport={clientWidth:aspect,clientHeight:1},gl={extra:[]};\r\n  const own=resource=>resource; // The extracted room's disposer walks every attached texture; no replacement maps here.\r\n  let breakRoomSet=null;\r\n        function signTexture(a, b) {\r\n          const c = document.createElement(\"canvas\"); c.width = 256; c.height = 96; const x = c.getContext(\"2d\");\r\n          x.fillStyle = \"#c0392b\"; x.fillRect(0, 0, 256, 96); x.fillStyle = \"#f4ecd2\"; x.font = \"bold 26px Georgia\"; x.textAlign = \"center\";\r\n          x.fillText(a, 128, 40); x.font = \"bold 16px Georgia\"; x.fillText(b, 128, 68);\r\n          const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace;\r\n          // owned: the door's map is REPLACED when the door opens, so the one it carried before that moment\r\n          // is unreachable from the graph and a walk would never find it.\r\n          return own(t);\r\n        }\r\n        function getBreakRoomSet() {\r\n          if (breakRoomSet) return breakRoomSet;\r\n          const rs = new THREE.Scene(); rs.background = new THREE.Color(0x777b72);\r\n          const rc = new THREE.PerspectiveCamera(68, viewport.clientWidth / viewport.clientHeight, .05, 40);\r\n          rs.add(new THREE.HemisphereLight(0xf2e7c8, 0x554b47, 2));\r\n          const box = (x,y,z,w,h,d,color) => {\r\n            const m = new THREE.Mesh(new THREE.BoxGeometry(w,h,d), new THREE.MeshStandardMaterial({color,roughness:.85}));\r\n            m.position.set(x,y,z); rs.add(m); return m;\r\n          };\r\n          // Reuse these solids, rather than placing coplanar shell planes on them.\n          const floor=box(0,-.12,-2,7,.2,12,0x76716a),front=box(0,2,-6,7,4,.2,0xa8ada0);\n          const left=box(-3.5,2,-1,.2,4,10,0x939b91),right=box(3.5,2,-1,.2,4,10,0x939b91);\n          const surfaces={floor,front,left,right};\n          for(const [name,mesh] of Object.entries(surfaces)){\n            mesh.name=`corgi-room-${name}`;\n            // Same small baked tile approach as run-scene; owned by this room.\n            const pixels=new Uint8Array(32*32*4);\n            for(let y=0;y<32;y++)for(let x=0;x<32;x++){\n              const i=(y*32+x)*4,seam=x%8===0||y%8===0,shade=(seam?135:200)+((x*17+y*31+seed)%35);\n              pixels[i]=pixels[i+1]=pixels[i+2]=shade;pixels[i+3]=255;\n            }\n            const map=new THREE.DataTexture(pixels,32,32,THREE.RGBAFormat);\n            map.wrapS=map.wrapT=THREE.RepeatWrapping;map.repeat.set(name==='floor'?7:4,name==='floor'?12:2);\n            map.minFilter=map.magFilter=THREE.NearestFilter;map.needsUpdate=true;mesh.material.map=map;\n          }\n          box(0,.65,-3,2.6,.12,1.25,0x795e3c);\r\n          for (const x of [-1.1,1.1]) for (const z of [-3.5,-2.5]) box(x,.3,z,.09,.6,.09,0x474941);\r\n          box(-2.4,.7,-4.9,1.4,1.4,.75,0xd3c9b5); box(-2.4,1.6,-4.9,.48,.5,.45,0x2e3434);\r\n          box(-2.4,1.8,-4.65,.18,.16,.08,0x965f3c);\r\n          box(2.4,.55,-4.6,1.5,1.1,1,0x665866); box(2.4,1.05,-5,1.5,.9,.25,0x665866);\r\n          for (const x of [-.5,.5]) {\r\n            const cup = new THREE.Mesh(new THREE.CylinderGeometry(.13,.1,.2,12), new THREE.MeshStandardMaterial({color:0xe5d4b9}));\r\n            cup.position.set(x,.81,-3); rs.add(cup);\r\n          }\r\n          const sign = new THREE.Mesh(new THREE.PlaneGeometry(2.1,.8), new THREE.MeshBasicMaterial({map:signTexture('BREAK ROOM','REST YOUR EYES.')}));\r\n          sign.position.set(0,2.3,-5.85); rs.add(sign);\r\n          // Capture room resources now; later merchant/display children have other owners.\n          const resources=new Set();rs.traverse(o=>{if(o.geometry)resources.add(o.geometry);if(o.material){if(o.material.map)resources.add(o.material.map);resources.add(o.material);}});\n          const shell=createARRoomShell(THREE,{scene:rs,camera:rc,seed,reducedMotion,\n            bounds:{minX:-3.4,maxX:3.4,minY:-.02,maxY:4,minZ:-5.9,maxZ:4},existingSurfaces:surfaces,textures:{wall:front.material.map}});\n          gl.extra.push({dispose(){shell.dispose();for(const resource of resources)resource.dispose();rs.clear();}});\n          breakRoomSet = {scene:rs,camera:rc,shell}; return breakRoomSet;\n        }\r\n  const room=getBreakRoomSet();\r\n  let disposed=false;\n  return {...room,dispose(){if(disposed)return;disposed=true;for(const resource of gl.extra)resource.dispose();}};\n}\r\n","modules/roguelite/corgi-merchant.mjs":"// Extracted from tv/channels/corgi.js buildMonster/newsprintTexture (reference f059bda).\r\n// Geometry/material proportions stay authored there; this adapter only owns one local instance.\r\nfunction randomFor(seed) {\r\n  let state = Number.isSafeInteger(seed) ? seed >>> 0 : 91;\r\n  return () => {\r\n    state = (Math.imul(state, 9301) + 49297) % 233280;\r\n    return state / 233280;\r\n  };\r\n}\r\n\r\nfunction makeNewsprintTexture(THREE, { paper, ink, headline, density, random, textureFactory, canvasFactory }) {\r\n  if (textureFactory) return textureFactory({ THREE, paper, ink, headline, density, random });\r\n  if (!canvasFactory) throw new Error('Corgi newsprint needs a browser canvas or injected textureFactory');\r\n  const canvas = canvasFactory(); canvas.width = canvas.height = 192;\r\n  const x = canvas.getContext('2d');\r\n  if (!x) throw new Error('Corgi newsprint canvas has no 2D context');\r\n  x.fillStyle = paper; x.fillRect(0, 0, 192, 192);\r\n  x.fillStyle = `rgba(${ink},.88)`; x.fillRect(4, 4, 184, 20);\r\n  x.fillStyle = paper; x.font = '900 15px Georgia'; x.textBaseline = 'top'; x.textAlign = 'left'; x.fillText(headline, 8, 6);\r\n  x.fillStyle = `rgba(${ink},.7)`; x.fillRect(4, 28, 184, 3);\r\n  const cols = 3, colW = 184 / cols;\r\n  for (let col = 0; col < cols; col++) {\r\n    let y = 38 + random() * 6;\r\n    while (y < 178) {\r\n      if (random() < 0.1) { y += 7; continue; }\r\n      x.fillStyle = `rgba(${ink},${(0.32 + random() * 0.3 + density * 0.3).toFixed(2)})`;\r\n      x.fillRect(4 + col * colW + 3, y, (colW - 6) * (0.5 + random() * 0.5), 3.4);\r\n      y += 6.4;\r\n    }\r\n  }\r\n  const hx = 8 + random() * 90, hy = 96 + random() * 34;\r\n  for (let py = 0; py < 46; py += 5) for (let px = 0; px < 70; px += 5) {\r\n    const shade = 0.25 + random() * 0.5;\r\n    x.fillStyle = `rgba(${ink},${shade.toFixed(2)})`;\r\n    x.beginPath(); x.arc(hx + px, hy + py, 1.1 + shade * 1.6, 0, 7); x.fill();\r\n  }\r\n  for (let i = 0; i < 5; i++) {\r\n    const sx = random() * 192, sy = random() * 192, ex = sx + (random() - 0.5) * 140, ey = sy + (random() - 0.5) * 140;\r\n    x.strokeStyle = `rgba(${ink},${(0.18 + random() * 0.18).toFixed(2)})`; x.lineWidth = 2 + random() * 2;\r\n    x.beginPath(); x.moveTo(sx, sy); x.lineTo(ex, ey); x.stroke();\r\n    x.strokeStyle = 'rgba(255,250,235,.22)'; x.lineWidth = 1;\r\n    x.beginPath(); x.moveTo(sx + 1.5, sy + 1.5); x.lineTo(ex + 1.5, ey + 1.5); x.stroke();\r\n  }\r\n  const texture = new THREE.CanvasTexture(canvas);\r\n  texture.wrapS = texture.wrapT = THREE.RepeatWrapping;\r\n  texture.generateMipmaps = false; texture.minFilter = THREE.LinearFilter; texture.colorSpace = THREE.SRGBColorSpace;\r\n  return texture;\r\n}\r\n\r\n/** Build the original Corgi businessman or his still-clothed crawling variant. */\r\nexport function createCorgiMerchant(THREE, {\r\n  crawling = false, seed = 91, random = randomFor(seed), textureFactory,\r\n  canvasFactory = globalThis.document?.createElement ? () => document.createElement('canvas') : null,\r\n} = {}) {\r\n  const geos = new Set(), materials = new Set(), textures = new Set();\r\n  let disposed = false;\r\n  const ownTexture = spec => { const texture = makeNewsprintTexture(THREE, { ...spec, random, textureFactory, canvasFactory }); textures.add(texture); return texture; };\r\n  const suitMap = ownTexture({ paper: '#7a7460', ink: '40,34,20', headline: 'SLIDES IN', density: 0.55 });\r\n  const paperMap = ownTexture({ paper: '#8f866c', ink: '30,24,14', headline: 'JOBS', density: 0.3 });\r\n  const caseMap = ownTexture({ paper: '#5a5443', ink: '20,16,8', headline: 'CASE FILE', density: 0.85 });\r\n  const suitMat = new THREE.MeshStandardMaterial({ map: suitMap, roughness: 1, metalness: 0, emissive: 0x1a1610, emissiveIntensity: 0.06 });\r\n  suitMap.repeat.set(1.4, 2.6);\r\n  const paperMat = new THREE.MeshStandardMaterial({ map: paperMap, roughness: 1, metalness: 0, emissive: 0x241f14, emissiveIntensity: 0.05 });\r\n  paperMap.repeat.set(1.2, 1.2);\r\n  const caseMat = new THREE.MeshStandardMaterial({ map: caseMap, roughness: 1, metalness: 0, emissive: 0x120f0a, emissiveIntensity: 0.05 });\r\n  const eyeMat = new THREE.MeshStandardMaterial({ color: 0x0a0806, roughness: 1, metalness: 0 });\r\n  for (const material of [suitMat, paperMat, caseMat, eyeMat]) materials.add(material);\r\n\r\n  const g = new THREE.Group();\r\n  const grp = (x, y, z, parent) => { const o = new THREE.Group(); o.position.set(x, y, z); (parent || g).add(o); return o; };\r\n  const part = (geo, mat, x, y, z, parent) => {\r\n    geos.add(geo); materials.add(mat);\r\n    const mesh = new THREE.Mesh(geo, mat); mesh.position.set(x, y, z); (parent || g).add(mesh); return mesh;\r\n  };\r\n  const dark = crawling;\r\n  const THIGH = 0.6, SHIN = 0.45, SPINE = 0.68, UARM = 0.34, FARM = 0.32;\r\n  const HIP_Y = dark ? 0.7 : 1.1, HIP_Z = dark ? -0.45 : 0, TORSO_A = dark ? Math.PI / 2 + 0.25 : 0;\r\n  part(new THREE.BoxGeometry(0.34, 0.2, 0.22), suitMat, 0, HIP_Y, HIP_Z);\r\n  const torso = grp(0, HIP_Y, HIP_Z); torso.rotation.x = TORSO_A;\r\n  const chest = part(new THREE.CylinderGeometry(0.21, 0.15, SPINE, 10), suitMat, 0, SPINE / 2, 0, torso); chest.scale.z = 0.62;\r\n  for (const s of [-1, 1]) part(new THREE.SphereGeometry(0.08, 10, 8), suitMat, s * 0.26, SPINE - 0.05, 0, torso);\r\n  const neck = grp(0, SPINE, 0, torso); part(new THREE.CylinderGeometry(0.06, 0.07, 0.16, 8), paperMat, 0, 0.06, 0, neck);\r\n  const head = grp(0, 0, 0); head.rotation.order = 'YXZ';\r\n  const skull = part(new THREE.SphereGeometry(0.17, 16, 12), paperMat, 0, 0, 0, head); skull.scale.set(1, 1.12, 0.95);\r\n  const jaw = part(new THREE.SphereGeometry(0.1, 12, 8), paperMat, 0, -0.14, 0.05, head); jaw.scale.set(1, 0.55, 0.9);\r\n  for (const s of [-1, 1]) part(new THREE.SphereGeometry(0.036, 8, 6), eyeMat, s * 0.065, 0.035, 0.135, head);\r\n  const nose = part(new THREE.ConeGeometry(0.03, 0.08, 6), paperMat, 0, -0.035, 0.19, head); nose.rotation.x = Math.PI / 2;\r\n  const crown = part(new THREE.SphereGeometry(0.175, 12, 8, 0, Math.PI * 2, 0, Math.PI * 0.4), caseMat, 0, 0.012, -0.012, head); crown.scale.set(1, 1.12, 0.97);\r\n\r\n  const limb = (px, py, pz, upLen, loLen, upR, loR) => {\r\n    const upper = grp(px, py, pz); part(new THREE.CylinderGeometry(upR, upR * 0.82, upLen, 8), suitMat, 0, -upLen / 2, 0, upper);\r\n    part(new THREE.SphereGeometry(upR * 1.05, 8, 6), suitMat, 0, -upLen, 0, upper);\r\n    const lower = grp(0, -upLen, 0, upper); part(new THREE.CylinderGeometry(loR, loR * 0.85, loLen, 8), suitMat, 0, -loLen / 2, 0, lower);\r\n    return { upper, lower };\r\n  };\r\n  const armL = limb(0, 0, 0, UARM, FARM, 0.05, 0.04), armR = limb(0, 0, 0, UARM, FARM, 0.05, 0.04);\r\n  const legL = limb(-0.1, HIP_Y, HIP_Z, THIGH, SHIN, 0.075, 0.06), legR = limb(0.1, HIP_Y, HIP_Z, THIGH, SHIN, 0.075, 0.06);\r\n  const rest = dark\r\n    ? { tA: TORSO_A, armU: -0.45, armL: -0.3, hand: -Math.PI / 2 + 0.75, legU: -0.15, legL: Math.PI / 2 - 0.1, headP: 0.3 }\r\n    : { tA: 0, armU: 0.06, armL: -0.3, hand: 0, legU: 0, legL: 0, headP: 0.05 };\r\n  for (const leg of [legL, legR]) {\r\n    const foot = grp(0, -SHIN, 0, leg.lower); foot.rotation.x = dark ? Math.PI - (rest.legU + rest.legL) : 0;\r\n    part(new THREE.BoxGeometry(0.11, 0.06, 0.27), caseMat, 0, -0.02, 0.08, foot);\r\n  }\r\n  const hands = [];\r\n  for (const arm of [armL, armR]) {\r\n    const hand = grp(0, -FARM, 0, arm.lower); hands.push(hand); hand.rotation.x = rest.hand;\r\n    part(new THREE.BoxGeometry(0.09, 0.11, 0.035), paperMat, 0, -0.055, 0, hand);\r\n    for (const fx of [-0.028, 0, 0.028]) part(new THREE.BoxGeometry(0.018, 0.075, 0.022), paperMat, fx, -0.145, 0, hand);\r\n  }\r\n  armL.upper.rotation.set(rest.armU, 0, dark ? 0 : -0.1); armR.upper.rotation.set(rest.armU, 0, dark ? 0 : 0.1);\r\n  armL.lower.rotation.x = armR.lower.rotation.x = rest.armL;\r\n  legL.upper.rotation.x = legR.upper.rotation.x = rest.legU; legL.lower.rotation.x = legR.lower.rotation.x = rest.legL;\r\n  head.rotation.x = rest.headP;\r\n  const v = new THREE.Vector3();\r\n  function layout(dip) {\r\n    torso.rotation.x = rest.tA + (dark ? 0.12 * dip : 0);\r\n    const lift = dark ? 0.1 - 0.17 * dip : 0;\r\n    v.set(0, SPINE + 0.26, 0).applyEuler(torso.rotation); head.position.set(v.x, HIP_Y + v.y + lift, HIP_Z + v.z);\r\n    v.set(-0.27, SPINE - 0.06, 0).applyEuler(torso.rotation); armL.upper.position.set(v.x, HIP_Y + v.y, HIP_Z + v.z);\r\n    v.set(0.27, SPINE - 0.06, 0).applyEuler(torso.rotation); armR.upper.position.set(v.x, HIP_Y + v.y, HIP_Z + v.z);\r\n  }\r\n  layout(0);\r\n  g.userData.rig = { head, neck, torso, armL, armR, legL, legR, hands };\r\n  g.userData.rest = rest; g.userData.layout = layout;\r\n  if (!dark) {\r\n    const brief = part(new THREE.BoxGeometry(0.22, 0.16, 0.06), caseMat, 0, -FARM - 0.2, 0.02, armR.lower); brief.rotation.y = Math.PI / 2;\r\n    const light = new THREE.PointLight(0xcfe0ff, 0.65, 6, 2); light.position.set(0.6, 1.5, -2.2); g.add(light);\r\n    const jacket = new THREE.MeshStandardMaterial({ color: 0x202c3e, roughness: 0.92 }); materials.add(jacket);\r\n    g.traverse(o => { if (o.isMesh && o.material === suitMat) o.material = jacket; });\r\n    part(new THREE.BoxGeometry(0.18, 0.38, 0.03), new THREE.MeshStandardMaterial({ color: 0xece7dc }), 0, SPINE * 0.62, 0.115, torso);\r\n    const tie = part(new THREE.ConeGeometry(0.045, 0.28, 3), new THREE.MeshStandardMaterial({ color: 0x923b46 }), 0, SPINE * 0.56, 0.13, torso); tie.rotation.z = Math.PI;\r\n    g.name = 'suited-office-boss';\r\n  } else g.name = 'school-monster';\r\n  g.visible = true;\r\n  g.userData.role = dark ? 'crawling-ambush' : 'merchant';\r\n\r\n  let lastTime = 0;\r\n  const update = timeSeconds => {\r\n    if (disposed) return;\r\n    if (!Number.isFinite(timeSeconds) || timeSeconds < 0) throw new RangeError('timeSeconds must be finite and non-negative');\r\n    const now = Math.max(lastTime, timeSeconds); lastTime = now;\r\n    if (!dark) return;\r\n    const R = g.userData.rig;\r\n    const phase = now * Math.PI * 2 / 0.9, s = Math.sin(phase), k = Math.cos(phase);\r\n    R.armL.upper.rotation.x = rest.armU - 0.35 * s; R.armR.upper.rotation.x = rest.armU + 0.35 * s;\r\n    R.legR.upper.rotation.x = rest.legU - 0.35 * s; R.legL.upper.rotation.x = rest.legU + 0.35 * s;\r\n    R.armL.lower.rotation.x = rest.armL - 0.2 * k; R.armR.lower.rotation.x = rest.armL + 0.2 * k;\r\n    R.legR.lower.rotation.x = rest.legL - 0.2 * k; R.legL.lower.rotation.x = rest.legL + 0.2 * k;\r\n    const sniffPeriod = 5.5, sniffLength = 1.1, sniffTime = now % sniffPeriod;\r\n    const sniff = sniffTime < sniffLength;\r\n    const dip = sniff ? Math.min(1, sniffTime / 0.18, (sniffLength - sniffTime) / 0.18) : 0;\r\n    const hy = sniff ? 0.3 * Math.sin((sniffTime / sniffLength) * Math.PI * 4) : 0;\r\n    R.head.rotation.y = hy; R.head.rotation.x = rest.headP + 0.6 * dip;\r\n    R.torso.rotation.z = Math.sin(phase) * 0.04;\r\n    g.userData.layout(dip);\r\n  };\r\n\r\n  return {\r\n    object: g,\r\n    update,\r\n    dispose() {\r\n      if (disposed) return;\r\n      disposed = true; g.removeFromParent(); g.clear();\r\n      for (const geometry of geos) geometry.dispose();\r\n      for (const material of materials) material.dispose();\r\n      for (const texture of textures) texture.dispose();\r\n      geos.clear(); materials.clear(); textures.clear();\r\n    },\r\n  };\r\n}\r\n","modules/roguelite/corgi-shop-display.mjs":"import {RESOURCE_LABELS} from '../coach-world/resource-labels.mjs';\nimport {weaponImage} from '../../pod/gala-rig/weapon.mjs';\nimport {petImage} from '../../pod/gala-rig/pet.mjs';\n\n// A shelf inside the original break room, using Gala's actual weapon/pet renderers.\n// The caller owns transactions and recognition; these objects never award items.\nexport function createShopDisplay(THREE,scene,look,offers=null){\n const objects=[],textures=[],targets=[];\n function sprite(canvas,x,y,z,w,h,action){\n  const texture=new THREE.CanvasTexture(canvas);texture.colorSpace=THREE.SRGBColorSpace;texture.magFilter=THREE.NearestFilter;textures.push(texture);\n  const object=new THREE.Sprite(new THREE.SpriteMaterial({map:texture,alphaTest:.1,depthWrite:false}));object.position.set(x,y,z);object.scale.set(w,h,1);scene.add(object);objects.push(object);\n  if(action){object.userData.action=action;targets.push(object);}return object;\n }\n function canvasOf(image){const c=document.createElement('canvas');c.width=image.w;c.height=image.h;const ctx=c.getContext('2d'),data=ctx.createImageData(image.w,image.h);data.data.set(image.data);ctx.putImageData(data,0,0);return c;}\n function sign(text,x,y,z,action,name=null){const c=document.createElement('canvas');c.width=512;c.height=128;const ctx=c.getContext('2d');ctx.fillStyle='#251c28';ctx.fillRect(0,0,512,128);ctx.strokeStyle='#f4d492';ctx.lineWidth=8;ctx.strokeRect(4,4,504,120);ctx.fillStyle='#fff4e5';ctx.textAlign='center';\n  if(name){const words=name.split(' '),middle=Math.ceil(words.length/2);ctx.font='700 27px monospace';ctx.fillText(words.slice(0,middle).join(' '),256,33,480);ctx.fillText(words.slice(middle).join(' '),256,65,480);ctx.font='900 30px monospace';ctx.fillText(text,256,107,480);}\n  else{ctx.font='900 58px system-ui';ctx.fillText(text,256,84,480);}return sprite(c,x,y,z,1.6,.4,action);}\n const stock=offers??[{id:'dagger',price:3},{id:'wand',price:4},{id:'hammer',price:5}];\n function productImage(item){\n  const effect=item.effectRef;\n  if(!effect||effect.kind==='weapon'){const type=effect?{knight:'spear',monk:'gauntlets',dual:'dagger',mage:'staff'}[effect.styleId]:item.id;return canvasOf(weaponImage({type,tier:effect?.tier??0},{w:64,h:96}));}\n  const c=document.createElement('canvas');c.width=64;c.height=96;const ctx=c.getContext('2d');\n  ctx.fillStyle='#dbe3bf';ctx.fillRect(18,18,28,60);ctx.fillStyle='#4d6757';ctx.fillRect(22,10,20,12);ctx.fillStyle=effect.kind==='stat'?'#dc8650':'#78bace';ctx.fillRect(18,35,28,26);\n  ctx.fillStyle='#182b32';ctx.font='bold 12px monospace';ctx.textAlign='center';ctx.fillText(effect.kind==='stat'?'DMG':effect.id==='longerParry'?'TIME':effect.id==='bossPunch'?'POW':'RUN',32,52,27);return c;\n }\n const priceTags=[];\n const products=stock.map((item,i)=>{\n  const object=sprite(productImage(item),-1.8+i*.7,1.65,-3.4,.65,.975,{type:'grab',itemId:item.id});\n  object.userData.offer=item;\n  const price=sign(`${item.price} ${RESOURCE_LABELS.runCurrency.toUpperCase()}`,-1.8+i*.7,1.05,-3.4,null,item.name);price.scale.set(.65,.163,1);priceTags.push(price);return object;\n });\n const checkout=sign('CHECKOUT',1.2,.85,-3.4,{type:'checkout'});\n const exit=sign('EXIT',1.65,2.5,-3.4,{type:'exit'});exit.scale.set(1,.25,1);\n const attack=sign('THREATEN',2.25,1.75,-3.4,{type:'attack'});attack.scale.set(1,.25,1);\n const basketLabel=sign('CARRY',-1.35,.32,-2.25);basketLabel.scale.set(.9,.22,1);\n const petLook=structuredClone(look);petLook.parts.pet=1;\n const petData=petImage(petLook,{H:'#90bca2',S:'#d9bd98',ox:32,oy:84,scale:75});\n const pet=sprite(canvasOf(petData),-.3,.45,-1.7,.7,1.05,{type:'pet'});\n let petUntil=0;\n return {stock,targets,pet,\n  petReaction(now){petUntil=now+900;},\n  update(shop,now){\n   const active=shop?.status==='open';\n   const ambush=shop?.mode==='ambush';\n   const slotCount=shop?.shelf?.length||products.length;\n   for(let i=0;i<products.length;i++){\n    priceTags[i].visible=active&&!ambush&&!!shop.shelf[i];\n    const object=products[i],id=stock[i].id,slot=active?shop.shelf.findIndex(item=>item?.id===id):-1,basketIndex=active?shop.basket.findIndex(item=>item.id===id):-1;\n    if(basketIndex>=0){object.visible=true;object.position.set(-1.45+(basketIndex%4)*.42,.72,-2.35-Math.floor(basketIndex/4)*.38);object.scale.set(.32,.48,1);object.userData.action={type:'return',itemId:id};}\n    else if(ambush){\n     const baitSlot=shop.shelf.findIndex(Boolean),bait=baitSlot>=0?shop.shelf[baitSlot]:null;\n     if(bait?.id===id){object.visible=true;object.position.set(-1.8+baitSlot*.7,1.65,-3.4);object.userData.action={type:'grab',itemId:id};}\n     else object.visible=false;\n    }else if(slot>=0){object.visible=true;object.position.set(-1.8+slot*.7,1.65,-3.4);object.userData.action={type:'grab',itemId:id};}\n    else object.visible=false;\n    if(basketIndex<0)object.scale.set(.65,.975,1);\n   }\n   checkout.visible=active&&!ambush;exit.visible=active;\n   attack.visible=active&&!ambush;\n   basketLabel.visible=active&&shop.basket.length>0;\n   pet.visible=!active||shop.mode!=='ambush';\n   pet.position.y=.45+(now<petUntil?Math.sin((petUntil-now)/900*Math.PI)*.25:0);\n  },\n  dispose(){for(const object of objects){scene.remove(object);object.material.dispose();}for(const texture of textures)texture.dispose();},\n };\n}\n","modules/roguelite/corgi-shop-view.mjs":"import * as THREE from 'three';\nimport {classifyHand,selectActiveHand} from './hand-input.mjs';\nimport {createCorgiBreakRoom} from './corgi-break-room.mjs';\nimport {createCorgiMerchant} from './corgi-merchant.mjs';\nimport {createShopDisplay} from './corgi-shop-display.mjs';\nimport {createRenderCadence} from './render-cadence.mjs';\nimport {defaultLook} from '../../pod/gala-rig/avatar.mjs';\nimport {runShopOffers} from './run-shop.mjs';\n\nconst finite=Number.isFinite;\n\n// Parent owns tracking, time, pause state, and all ledger mutations. This adapter\n// only renders the existing Corgi room and reports deliberate hand actions.\nexport function mountCorgiShopView(parent,{renderer:borrowedRenderer=null,video=null,onAction=()=>{},onTutorialOpenHand=()=>{},reducedMotion=false,seed=101}={}){\n if(!parent?.appendChild)throw new TypeError('A shop view parent element is required');\n if(typeof onAction!=='function'||typeof onTutorialOpenHand!=='function')throw new TypeError('Shop callbacks must be functions');\n const element=document.createElement('div');element.className='run-corgi-shop';\n Object.assign(element.style,{position:'absolute',inset:'0',zIndex:'2',overflow:'hidden',pointerEvents:'none'});\n const composite=document.createElement('canvas');composite.setAttribute('aria-hidden','true');\n Object.assign(composite.style,{position:'absolute',inset:'0',width:'100%',height:'100%',pointerEvents:'auto',touchAction:'none'});element.appendChild(composite);parent.appendChild(element);\n const offerInfo=document.createElement('p');offerInfo.hidden=true;offerInfo.setAttribute('role','status');\n Object.assign(offerInfo.style,{position:'absolute',left:'16px',right:'16px',bottom:'126px',margin:'0 auto',maxWidth:'420px',padding:'10px',background:'#172b30ee',color:'#fff8e6',font:'15px/1.35 Pixel,monospace',border:'1px solid #c8ff9577',pointerEvents:'none'});element.appendChild(offerInfo);\n function describeOffer(hit){const offer=hit?.userData.offer;offerInfo.hidden=!offer;offerInfo.textContent=offer?`${offer.name??offer.id} · ${offer.price} Run Tokens. ${offer.description??''}`:'';}\n const ctx=composite.getContext('2d');\n const ownsRenderer=!borrowedRenderer,renderer=borrowedRenderer||new THREE.WebGLRenderer({alpha:true,antialias:false,powerPreference:'low-power'});\n if(ownsRenderer){renderer.setPixelRatio(1);renderer.setClearColor(0x000000,0);renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.domElement.style.display='none';element.appendChild(renderer.domElement);}\n const room=createCorgiBreakRoom(THREE,{aspect:4/3,seed,reducedMotion}),scene=room.scene,camera=room.camera;\n camera.position.set(0,1.1,.5);camera.rotation.set(0,0,0);camera.fov=50;camera.aspect=4/3;camera.updateProjectionMatrix();\n let shopDisplay=createShopDisplay(THREE,scene,defaultLook),offerSignature=null;\n const merchant=createCorgiMerchant(THREE,{seed});merchant.object.position.set(1.5,0,-3.5);scene.add(merchant.object);\n const crawler=createCorgiMerchant(THREE,{crawling:true,seed:seed+1});crawler.object.visible=false;scene.add(crawler.object);\n const atmosphere=room.shell,cadence=createRenderCadence(30);\n const roomLights=[];scene.traverse(object=>{if(object.isLight)roomLights.push({object,intensity:object.intensity});});\n const ray=new THREE.Raycaster(),point=new THREE.Vector2();\n let disposed=false,paused=true,ledger=null,shopId=null,aspect=4/3,hand=null,handAt=-Infinity,activeWrist=null,previousTip=null;\n let needsRelease=true,openHeld=false,target=null,startedAt=0,now=0,visualNow=0,petReadyAt=0,lastGestureAt=null,baseQuaternion=camera.quaternion.clone();\n let active=true,yaw=0,pitch=0,drag=null;\n const lookEuler=new THREE.Euler(0,0,0,'YXZ');\n const onDown=e=>{if(!active||e.button!==undefined&&e.button!==0)return;drag={id:e.pointerId,x:e.clientX,y:e.clientY};composite.setPointerCapture?.(e.pointerId);};\n const onMove=e=>{if(!drag||drag.id!==e.pointerId)return;yaw-=(e.clientX-drag.x)*.006;pitch=Math.max(-Math.PI/2+.02,Math.min(Math.PI/2-.02,pitch-(e.clientY-drag.y)*.006));drag.x=e.clientX;drag.y=e.clientY;};\n const releaseDrag=()=>{const id=drag?.id;drag=null;if(id!==undefined&&composite.hasPointerCapture?.(id))composite.releasePointerCapture?.(id);};\n const onUp=e=>{if(drag?.id===e.pointerId)releaseDrag();};\n const pointerEvents=[['pointerdown',onDown],['pointermove',onMove],['pointerup',onUp],['pointercancel',onUp],['lostpointercapture',onUp]];\n for(const [name,handler] of pointerEvents)composite.addEventListener(name,handler);\n function clearGesture(){hand=null;activeWrist=null;previousTip=null;target=null;startedAt=0;needsRelease=true;openHeld=false;describeOffer(null);}\n function fit(){\n  const w=Math.max(1,element.clientWidth),h=Math.max(1,element.clientHeight),dpr=Math.min(1,640/w,360/h);\n  const rw=Math.max(1,Math.round(w*dpr)),rh=Math.max(1,Math.round(h*dpr));\n  if(composite.width!==w||composite.height!==h){composite.width=w;composite.height=h;}\n  if(ownsRenderer&&(renderer.domElement.width!==rw||renderer.domElement.height!==rh))renderer.setSize(rw,rh,false);\n  const sourceAspect=video?.videoWidth&&video?.videoHeight?video.videoWidth/video.videoHeight:aspect;\n  const fitW=Math.min(w,h*sourceAspect),fitH=fitW/sourceAspect,x=(w-fitW)/2,y=(h-fitH)/2;\n  camera.aspect=sourceAspect;camera.updateProjectionMatrix();\n  return {w,h,dpr,x,y,fitW,fitH};\n }\n function handMask(points,w,h){\n  if(!ctx||!points||points.length!==21)return;\n  const p=points.map(v=>[v.x*w,v.y*h]),scale=Math.hypot(p[0][0]-p[9][0],p[0][1]-p[9][1]);\n  const polygon=ids=>{ctx.beginPath();ids.forEach((id,i)=>i?ctx.lineTo(...p[id]):ctx.moveTo(...p[id]));ctx.closePath();ctx.fill();};\n  ctx.save();ctx.globalCompositeOperation='destination-out';ctx.fillStyle='#fff';ctx.strokeStyle='#fff';ctx.lineJoin='round';ctx.lineCap='round';\n  polygon([0,1,2,5,9,13,17]);ctx.lineWidth=scale*.25;ctx.stroke();\n  for(const ids of [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20]]){ctx.lineWidth=scale*.2;ctx.beginPath();ids.forEach((id,i)=>i?ctx.lineTo(...p[id]):ctx.moveTo(...p[id]));ctx.stroke();}\n  ctx.restore();\n }\n function activate(action){\n  if(!action||paused||!ledger||!shopId)return;\n  if(action.type==='pet'){shopDisplay.petReaction(visualNow);petReadyAt=visualNow+1200;return;}\n  if(action.type==='attack')onAction({type:'attack',shopId});\n  else if(['grab','return','checkout','exit'].includes(action.type))onAction({...action,shopId});\n }\n function observeHand(raw={}){\n  const ts=finite(raw.now)?raw.now:performance.now();\n  if(!active||disposed||performance.now()-ts>250||lastGestureAt!==null&&ts<=lastGestureAt){clearGesture();return;}\n  if(lastGestureAt!==null&&ts-lastGestureAt>250)clearGesture();lastGestureAt=ts;\n  const hands=raw.landmarks||[];\n  const selected=selectActiveHand(hands,activeWrist);\n  if(!raw.tracked||!selected){clearGesture();return;}\n  const i=selected.index,points=selected.points;\n  hand=points;handAt=ts;activeWrist=points[0];aspect=finite(raw.aspect)&&raw.aspect>0?raw.aspect:aspect;\n  const kind=classifyHand(points,raw.worldLandmarks?.[i],aspect);\n  if(kind==='open'){\n   target=null;startedAt=0;needsRelease=paused;previousTip=null;\n   if(!openHeld){openHeld=true;onTutorialOpenHand();}\n   if(!paused&&ledger?.shops?.[shopId]?.status==='open'){\n    // Petting is a local visual response and never changes ledger state.\n    point.set(((points[8].x+points[4].x)/2)*2-1,1-((points[8].y+points[4].y)/2)*2);ray.setFromCamera(point,camera);\n    const hit=ray.intersectObjects(shopDisplay.targets.filter(obj=>obj.visible))[0]?.object;\n    describeOffer(hit);\n    if(hit?.userData.action?.type==='pet'&&visualNow>=petReadyAt){activate({type:'pet'});}\n   }\n   return;\n  }\n  if(kind!=='pinch'&&kind!=='fist'){openHeld=false;target=null;startedAt=0;return;}\n  openHeld=false;\n  if(paused){target=null;startedAt=0;needsRelease=true;return;}\n  const tip={x:(points[4].x+points[8].x)/2,y:(points[4].y+points[8].y)/2};\n  if(previousTip&&Math.hypot(tip.x-previousTip.x,tip.y-previousTip.y)>.2){needsRelease=true;target=null;startedAt=0;}previousTip=tip;\n  if(needsRelease||paused||!ledger?.shops?.[shopId]||ledger.shops[shopId].status!=='open')return;\n  point.set(tip.x*2-1,1-tip.y*2);ray.setFromCamera(point,camera);\n  const hit=ray.intersectObjects(shopDisplay.targets.filter(obj=>obj.visible))[0]?.object,action=hit?.userData.action;\n  describeOffer(hit);const eligible=kind==='fist'?action?.type==='attack':action?.type!=='attack';\n  if(!hit||!eligible){target=null;startedAt=0;return;}\n  if(target!==hit){target=hit;startedAt=ts;return;}\n  const dwell=action.type==='attack'?900:400;\n  if(ts-startedAt>=dwell){activate(action);target=null;startedAt=0;needsRelease=true;}\n }\n function render({ledger:nextLedger,shopId:nextShopId,dt=0,paused:nextPaused=false,now:nextNow=performance.now(),applyLook,draw=true}={}){\n  if(disposed)return;\n  ledger=nextLedger||ledger;shopId=nextShopId||shopId;\n  const offers=runShopOffers(ledger,shopId),signature=JSON.stringify(offers);\n  if(signature!==offerSignature){shopDisplay.dispose();shopDisplay=createShopDisplay(THREE,scene,defaultLook,offers);offerSignature=signature;target=null;needsRelease=true;}\n  if(nextPaused&&!paused){target=null;startedAt=0;needsRelease=true;previousTip=null;}\n  paused=!!nextPaused;now=finite(nextNow)?nextNow:now;\n  const shop=ledger?.shops?.[shopId];\n  const dark=shop?.mode==='ambush';element.dataset.ambush=String(dark);\n  for(const light of roomLights)light.object.intensity=light.intensity*(dark ? .06 : 1);\n  if(scene.background?.isColor)scene.background.set(dark?'#060a10':'#777b72');\n  scene.fog?.color.set(dark?'#060a10':'#777b72');\n  if(!finite(dt)||dt<0)throw new RangeError('Shop view delta must be finite and non-negative');\n  const {w,h,x,y,fitW,fitH}=fit();\n  if(!paused)visualNow+=Math.min(.1,dt)*1000;\n  camera.quaternion.copy(baseQuaternion);camera.quaternion.multiply(new THREE.Quaternion().setFromEuler(lookEuler.set(pitch,yaw,0)));\n  applyLook?.(camera);camera.updateWorldMatrix(true,false);\n  shopDisplay.update(shop,visualNow);merchant.object.visible=!!shop&&shop.mode!=='ambush'&&shop.status!=='stolen';crawler.object.visible=!!shop&&shop.mode==='ambush'&&shop.status==='open';\n  const elapsed=shop?.elapsed||0;\n  if(crawler.object.visible){const lap=Math.min(3,elapsed/3),angle=lap*Math.PI*2;crawler.object.position.set(Math.sin(angle)*2.4,0,1+Math.cos(angle)*2.4);crawler.object.rotation.y=-angle+Math.PI/2;crawler.update(elapsed);}\n  atmosphere.update(dt,{paused:paused||!shop||shop.status!=='open'});\n  // Hand rays need current object transforms even when this tick skips drawing.\n  scene.updateMatrixWorld(true);\n  if(!active||!draw||!cadence.shouldRender(dt))return;\n  // A borrowed renderer is never resized or reparented. Restore state even on a failed draw/copy.\n  const previous={target:renderer.getRenderTarget(),face:renderer.getActiveCubeFace(),mip:renderer.getActiveMipmapLevel(),viewport:renderer.getViewport(new THREE.Vector4()),scissor:renderer.getScissor(new THREE.Vector4()),scissorTest:renderer.getScissorTest(),color:renderer.getClearColor(new THREE.Color()),alpha:renderer.getClearAlpha(),autoClear:renderer.autoClear,autoClearColor:renderer.autoClearColor,autoClearDepth:renderer.autoClearDepth,autoClearStencil:renderer.autoClearStencil};\n  try{\n   renderer.setRenderTarget(null);renderer.setScissorTest(false);renderer.autoClear=renderer.autoClearColor=renderer.autoClearDepth=renderer.autoClearStencil=true;\n   const size=renderer.getSize(new THREE.Vector2());\n   renderer.setViewport(x/w*size.x,(h-y-fitH)/h*size.y,fitW/w*size.x,fitH/h*size.y);\n   renderer.render(scene,camera);\n   ctx.clearRect(0,0,w,h);ctx.imageSmoothingEnabled=false;\n   // The WebGL viewport already contains the contain-fit letterboxing.\n   ctx.drawImage(renderer.domElement,0,0,renderer.domElement.width,renderer.domElement.height,0,0,w,h);\n   if(hand&&now-handAt<200){ctx.save();ctx.translate(x,y);handMask(hand,fitW,fitH);ctx.restore();}else hand=null;\n  }finally{\n   renderer.setRenderTarget(previous.target,previous.face,previous.mip);renderer.setViewport(previous.viewport);renderer.setScissor(previous.scissor);renderer.setScissorTest(previous.scissorTest);renderer.setClearColor(previous.color,previous.alpha);\n   for(const key of ['autoClear','autoClearColor','autoClearDepth','autoClearStencil'])renderer[key]=previous[key];\n  }\n }\n function setActive(value){if(disposed||active===!!value)return;active=!!value;element.hidden=!active;releaseDrag();clearGesture();cadence.reset();}\n function dispose(){\n  if(disposed)return;disposed=true;releaseDrag();for(const [name,handler] of pointerEvents)composite.removeEventListener(name,handler);clearGesture();shopDisplay.dispose();merchant.dispose();crawler.dispose();room.dispose();if(ownsRenderer)renderer.dispose();element.remove();\n }\n return {element,ready:Promise.resolve(),render,observeHand,setActive,setLoading:on=>atmosphere.setLoading(on),dispose,get camera(){return camera;},get targets(){return shopDisplay.targets;}};\n}\n","modules/roguelite/crisis-room-view.mjs":"// Thin stage mount for an ACK jelly checkpoint. createCoverScene owns the renderer,\r\n// camera, NPC materials, atmosphere, and 1.5e6 pixel budget. This file does not\r\n// step the sim, read a camera, or run its own frame loop.\r\nimport {LEVELS} from '../../arcade/cover/levels/index.mjs';\r\nimport {createCoverScene} from '../../arcade/cover/cover-scene.mjs';\r\nimport {restoreCoverSim} from '../../arcade/cover/cover-sim.mjs';\r\nimport {coverVolume, restoreGameRoom} from './game-room-adapters.mjs';\r\nimport {createViewTour,cameraOverview,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\nexport const CRISIS_RENDER_DT_MAX = 0.05;\r\n\r\nfunction shippedLevels(volume) {\r\n  return coverVolume(volume).map(row => {\r\n    const level = LEVELS.find(item => item.id === row.id);\r\n    if (!level) throw new Error(`shipped cover level missing: ${row.id}`);\r\n    return level;\r\n  });\r\n}\r\n\r\nfunction fill(node) {\r\n  const style = node.style;\r\n  style.position = 'absolute';\r\n  style.inset = '0';\r\n  style.width = '100%';\r\n  style.height = '100%';\r\n  style.display = 'block';\r\n  style.pointerEvents = 'none';\r\n}\r\n\r\nexport function createCrisisRoomView({host, sceneFactory = createCoverScene, reducedMotion = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false} = {}) {\r\n  if (!host || typeof host.append !== 'function') throw new TypeError('crisis room needs a host');\r\n  const doc = host.ownerDocument ?? globalThis.document;\r\n  if (!doc || typeof doc.createElement !== 'function') throw new TypeError('crisis room needs a document');\r\n  const element = doc.createElement('div');\r\n  element.className = 'crisis-room';\r\n  element.setAttribute('data-room', 'jelly');\r\n  element.setAttribute('aria-hidden', 'true');\r\n  fill(element);\r\n  const canvas = doc.createElement('canvas');\r\n  canvas.className = 'crisis-room-canvas';\r\n  fill(canvas);\r\n  element.append(canvas);\r\n  host.append(element);\r\n  let scene;\r\n  // Keep the shot inside the actual arena. Completion cuts to the authored\n  // entry camera instead of flying straight through the winding route's walls.\n  const tour = createViewTour((progress, camera) => cameraOverview(camera, progress, {point:[2*(1-progress),3-.8*progress,1],target:[0,1.2,-6],blendToGame:false}));\n  const motion = createAcknowledgedMotion();\r\n  try {\r\n    scene = sceneFactory(canvas, {beforeRender: (_scene, camera) => tour.apply(camera)});\r\n    if (!scene || typeof scene.setLevel !== 'function' || typeof scene.frame !== 'function' || typeof scene.dispose !== 'function') {\r\n      throw new TypeError('cover scene is missing setLevel, frame, or dispose');\r\n    }\r\n  } catch (error) {\r\n    try { scene?.dispose?.(); } catch {}\r\n    element.remove();\r\n    throw error;\r\n  }\r\n  let disposed = false;\r\n  let ready = false;\r\n  let shown = null;\r\n  let sim = null;\r\n  let identity = null, lastClock = null;\r\n\r\n  function update(checkpoint) {\r\n    if (disposed) throw new Error('crisis room is disposed');\r\n    const saved = restoreGameRoom(checkpoint);\r\n    if (saved.roomId !== 'jelly') throw new TypeError('crisis room renders jelly checkpoints only');\r\n    const live = restoreCoverSim(saved.sim, {levels: shippedLevels(saved.volume)});\r\n    const level = live.s.level;\r\n    if (!level || level.id !== saved.sim.s.levelId || typeof level.palette?.sky !== 'string') throw new Error('cover level was not reattached');\r\n    if (shown !== level.id) {\r\n      scene.setLevel(level);\r\n      shown = level.id;\r\n    }\r\n    const nextIdentity = `${saved.encounterId}:${saved.seed}:${level.id}:${live.s.area}:${live.s.phase}`;\r\n    if (identity !== null && (identity !== nextIdentity || live.s.t < lastClock)) tour.cancel();\r\n    identity = nextIdentity; lastClock = live.s.t;\r\n    sim = live;\r\n    const values = {t:live.s.t,timer:live.s.timer};\r\n    for (const [kind,idKey] of [['enemies','id'],['projs','id'],['shots','sid']]) {\r\n      for (const item of live.s[kind] ?? []) for (const key of ['t','x','y','z']) {\r\n        if (Number.isFinite(item[key])) values[`${kind}:${item[idKey]}:${key}`] = item[key];\r\n      }\r\n    }\r\n    motion.push(values,{identity,clock:live.s.t});\r\n    ready = true;\r\n  }\r\n\r\n  function render(frame) {\r\n    if (disposed || !sim) return;\r\n    if (!frame || typeof frame.now !== 'number' || !Number.isFinite(frame.now) || frame.now < 0) throw new RangeError('render now must be a finite clock');\r\n    if (typeof frame.paused !== 'boolean') throw new TypeError('render paused must be a boolean');\r\n    if (typeof frame.dt !== 'number' || !Number.isFinite(frame.dt) || frame.dt < 0) throw new RangeError('render dt must be a finite duration');\r\n    const dt = frame.paused ? 0 : Math.min(frame.dt, CRISIS_RENDER_DT_MAX);\r\n    const values = motion.sample(frame.dt,{paused:frame.paused,reducedMotion});\r\n    const drawn = {...sim.s,t:values.t,timer:values.timer};\r\n    for (const [kind,idKey] of [['enemies','id'],['projs','id'],['shots','sid']]) drawn[kind] = (sim.s[kind] ?? []).map(item => {\r\n      const copy = {...item};\r\n      for (const key of ['t','x','y','z']) if (Number.isFinite(values[`${kind}:${item[idKey]}:${key}`])) copy[key] = values[`${kind}:${item[idKey]}:${key}`];\r\n      return copy;\r\n    });\r\n    scene.frame(drawn, dt, frame.paused || tour.active ? sim.s.t * 1000 : values.t * 1000, {paused:frame.paused || tour.active});\r\n  }\r\n\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    tour.dispose();\r\n    ready = false;\r\n    sim = null;\r\n    shown = null;\r\n    scene.dispose();\r\n    element.remove();\r\n  }\r\n\r\n  return {\r\n    update,\r\n    render,\r\n    dispose,\r\n    element,\r\n    intro: options => tour.intro({reducedMotion,...options}),\r\n    get ready() { return ready; },\r\n  };\r\n}\r\n","modules/roguelite/dance-opening.mjs":"// Front-body dance door. Four screen corners light once, in any order.\n// All four start the existing pixel-board preview. This file does not grant,\n// unlock, wire, or aim, and it does not listen for board events.\nimport {createPixelBoard} from '../portal/portal-board-pixel.mjs';\nimport {physicalSound} from '../../audio/physical-sound.mjs';\n\nconst CORNERS = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\nconst EDGE = 0.22;\nconst STALE_MS = 250;\nconst VISIBLE = 0.5;\nconst GLYPH = {'top-left': '◤', 'top-right': '◥', 'bottom-left': '◣', 'bottom-right': '◢'};\nconst PLACE = {\n  'top-left': {top: '1%', left: '1%'},\n  'top-right': {top: '1%', right: '1%'},\n  'bottom-left': {bottom: '1%', left: '1%'},\n  'bottom-right': {bottom: '1%', right: '1%'},\n};\n\nexport function createDanceCornerState() {\n  return {lastNow: null, lit: [], complete: false};\n}\n\n// Screen boxes only. x/y are normalized to the image the person is looking at.\n// mirrored flips x into that image. There is no depth or rear-camera claim.\nfunction cornerFor(point, mirrored) {\n  if (!point || typeof point !== 'object') return null;\n  let x = point.x;\n  const y = point.y;\n  if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || x > 1 || y < 0 || y > 1) return null;\n  if (point.visibility != null && (!Number.isFinite(point.visibility) || point.visibility < VISIBLE)) return null;\n  if (mirrored) x = 1 - x;\n  const left = x <= EDGE;\n  const right = x >= 1 - EDGE;\n  const top = y <= EDGE;\n  const bottom = y >= 1 - EDGE;\n  if (top && left) return 'top-left';\n  if (top && right) return 'top-right';\n  if (bottom && left) return 'bottom-left';\n  if (bottom && right) return 'bottom-right';\n  return null;\n}\n\nexport function observeDanceCorners(state, {now, tracked = false, paused = false, mirrored = false, points = []} = {}) {\n  if (!state || !Array.isArray(state.lit) || !Number.isFinite(now)) throw new TypeError('Invalid dance corner observation');\n  const lit = state.lit.slice();\n  const next = {lastNow: now, lit, complete: lit.length === 4};\n  const stale = state.lastNow !== null && (now < state.lastNow || now - state.lastNow > STALE_MS);\n  if (paused || tracked !== true || stale) return next;\n  for (const point of Array.isArray(points) ? points : []) {\n    const corner = cornerFor(point, mirrored === true);\n    if (corner && !lit.includes(corner)) lit.push(corner);\n  }\n  next.complete = lit.length === 4;\n  return next;\n}\n\nfunction bodyWrists(body) {\n  if (!Array.isArray(body) || body.length < 33) return null;\n  return [body[15], body[16]];\n}\n\nfunction wristPoints(input) {\n  if (input && Object.hasOwn(input, 'points')) {\n    const points = input.points;\n    return bodyWrists(points) ?? (Array.isArray(points) ? points : []);\n  }\n  return bodyWrists(input?.pose ?? input?.landmarks) ?? [];\n}\n\nfunction controlsOf(node) {\n  if (typeof node?.querySelectorAll !== 'function') return [];\n  return [...node.querySelectorAll('.pixel-wire-controls')];\n}\n\nfunction silencePuzzle(node, prior) {\n  for (const control of controlsOf(node)) {\n    if (prior.has(control)) continue;\n    control.style.display = 'none';\n    control.style.pointerEvents = 'none';\n    if (typeof control.querySelectorAll !== 'function') continue;\n    for (const button of control.querySelectorAll('button')) {\n      button.disabled = true;\n      button.style.pointerEvents = 'none';\n    }\n  }\n}\n\nfunction describe(lit) {\n  if (lit.length === 4) return 'All four corners lit. Portal opening.';\n  if (lit.length === 0) return '0 of 4 lit. Reach any corner.';\n  return `${lit.length} of 4 lit. ${lit.join(', ')} on.`;\n}\n\nfunction defaultBoard(art) {\n  return createPixelBoard(art, {flat: true});\n}\n\nexport function mountDanceOpening(host, {onComplete, boardFactory = defaultBoard} = {}) {\n  if (!host || typeof host.append !== 'function') throw new TypeError('dance opening needs a host');\n  const doc = host.ownerDocument ?? globalThis.document;\n  if (!doc || typeof doc.createElement !== 'function') throw new TypeError('dance opening needs a document');\n  const element = doc.createElement('section');\n  element.className = 'dance-opening';\n  element.setAttribute('aria-label', 'Portal opening');\n  element.style.position = 'absolute';\n  element.style.inset = '0';\n  element.style.width = '100%';\n  element.style.height = '100%';\n  element.style.zIndex = '5';\n  element.style.overflow = 'hidden';\n  element.style.background = '#070b13';\n  element.style.color = '#f7f8ff';\n  const art = doc.createElement('div');\n  art.className = 'dance-opening-art';\n  art.style.position = 'absolute';\n  art.style.inset = '0';\n  art.style.zIndex = '0';\n  const help = doc.createElement('p');\n  help.className = 'dance-opening-help';\n  help.textContent = 'Move either wrist into the top-left, top-right, bottom-left, and bottom-right. Any order. Each corner lights once.';\n  help.style.position = 'absolute';\n  help.style.left = '14%';\n  help.style.right = '14%';\n  help.style.top = '46%';\n  help.style.margin = '0';\n  help.style.zIndex = '5';\n  help.style.pointerEvents = 'none';\n  help.style.textAlign = 'center';\n  help.style.font = '700 clamp(20px, 4vw, 36px)/1.3 system-ui, sans-serif';\n  const status = doc.createElement('p');\n  status.className = 'dance-opening-status';\n  status.setAttribute('role', 'status');\n  status.style.position = 'absolute';\n  status.style.left = '10%';\n  status.style.right = '10%';\n  status.style.top = '34%';\n  status.style.margin = '0';\n  status.style.zIndex = '5';\n  status.style.pointerEvents = 'none';\n  status.style.textAlign = 'center';\n  status.style.font = '800 clamp(28px, 6vw, 64px)/1.15 system-ui, sans-serif';\n  const nodes = new Map();\n  for (const name of CORNERS) {\n    const node = doc.createElement('div');\n    node.className = 'dance-opening-corner';\n    node.dataset.corner = name;\n    node.dataset.lit = 'false';\n    node.textContent = GLYPH[name];\n    node.style.position = 'absolute';\n    node.style.zIndex = '4';\n    node.style.pointerEvents = 'none';\n    node.style.font = '900 clamp(72px, 22vw, 200px)/0.8 system-ui, sans-serif';\n    node.style.color = '#3a2a55';\n    for (const [key, value] of Object.entries(PLACE[name])) node.style[key] = value;\n    nodes.set(name, node);\n  }\n  element.append(art, help, status, ...nodes.values());\n  host.append(element);\n  let gesture = createDanceCornerState();\n  let board = null;\n  let disposed = false;\n  let held = false;\n  let wantPreview = false;\n  let previewed = false;\n  let finished = false;\n  let preserved = null;\n  const uiHost = art.closest?.('#portalHome') || art;\n  const priorControls = new Set(controlsOf(uiHost));\n\n  function paint() {\n    element.dataset.lights=String(gesture.lit.length);\n    art.style.filter=gesture.lit.length?`brightness(${1+gesture.lit.length*.08})`:'';\n    for (const name of CORNERS) {\n      const node = nodes.get(name);\n      const lit = gesture.lit.includes(name);\n      node.dataset.lit = String(lit);\n      node.style.color = lit ? '#ffe56a' : '#3a2a55';\n    }\n    status.textContent = describe(gesture.lit);\n  }\n\n  function startVisual() {\n    if (disposed || previewed || !board) return;\n    previewed = true;\n    board.previewSecret();\n  }\n\n  // The pixel board sets ceremony.sent inside its own draw. Caller render ticks poll it.\n  function noteCeremony() {\n    if (disposed || finished || held || !previewed || typeof board?.exportState !== 'function') return;\n    if (board.exportState()?.ceremony?.sent !== true) return;\n    finished = true;\n    onComplete?.();\n  }\n\n  function observe(input, {paused = false} = {}) {\n    if (disposed) return;\n    const previousLit=gesture.lit.length;\n    gesture = observeDanceCorners(gesture, {\n      now: input?.now,\n      tracked: input?.tracked === true,\n      paused: paused === true || held,\n      mirrored: input?.mirrored === true,\n      points: wristPoints(input),\n    });\n    paint();\n    if(gesture.lit.length>previousLit)physicalSound.play('electric');\n    if (gesture.complete && !previewed) {\n      wantPreview = true;\n      if (board && !held) startVisual();\n    }\n  }\n\n  function render() {\n    if (disposed) return;\n    paint();\n    noteCeremony();\n  }\n\n  function pause() {\n    if (disposed || held) return;\n    held = true;\n    if (typeof board?.exportState === 'function') preserved = board.exportState();\n    board?.pause?.();\n  }\n\n  function resume() {\n    if (disposed || !held) return;\n    held = false;\n    const saved = preserved;\n    preserved = null;\n    if (!board) return;\n    // resume() heals a live ceremony. Put the pre-pause snapshot back afterward.\n    board.resume?.();\n    if (saved && typeof board.restoreState === 'function') board.restoreState(saved);\n    if (wantPreview && !previewed) startVisual();\n  }\n\n  function dispose() {\n    if (disposed) return;\n    disposed = true;\n    wantPreview = false;\n    board?.dispose?.();\n    element.remove();\n  }\n\n  paint();\n  const ready = Promise.resolve().then(() => boardFactory(art)).then(value => {\n    if (disposed) {\n      value?.dispose?.();\n      return;\n    }\n    if (!value || typeof value.dispose !== 'function' || typeof value.previewSecret !== 'function') {\n      value?.dispose?.();\n      throw new TypeError('pixel board is missing previewSecret or dispose');\n    }\n    board = value;\n    silencePuzzle(uiHost, priorControls);\n    if (wantPreview) startVisual();\n    if (held) {\n      if (typeof board.exportState === 'function') preserved = board.exportState();\n      board.pause?.();\n    }\n  });\n  return {element, ready, observe, render, pause, resume, dispose};\n}\n","modules/roguelite/dance-room-view.mjs":"// ACK mount for a dance-room checkpoint. Club lasers and crowd come from\r\n// mountClubPresentation. Their CSS stays in this shadow root. The beat passed\r\n// to the club is the Dance view beat, not music seconds. A rival sprite is\r\n// drawn only from a caller-supplied atlas frame, with nearest sampling.\r\n// This file does not advance the dance clock, grant a reward, or start a frame loop.\r\nimport {mountClubPresentation} from '../dance/android-game/club-presentation.mjs';\r\nimport {MOVES, sampleMove} from '../dance/android-game/moves.mjs';\r\nimport {drawFigure, projectCoach} from '../dance/ui/skeleton.mjs';\r\nimport {createDanceRoom} from './dance-room.mjs';\r\nimport {createViewTour,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\nfunction fill(node) {\r\n  const style = node.style;\r\n  style.position = 'absolute';\r\n  style.inset = '0';\r\n  style.width = '100%';\r\n  style.height = '100%';\r\n  style.display = 'block';\r\n  style.pointerEvents = 'none';\r\n}\r\n\r\n// Club and floor rules copied from game.css. Palette variables live on :host.\r\n// The document :root, html, body, button, and input rules are not included.\r\nconst CLUB_CSS = `:host{color-scheme:dark;background:var(--dg-bg);--dg-bg:#070b13;--dg-panel:#101725;--dg-yellow:#ffe54f;--dg-cyan:#5bf5ff;--dg-pink:#ff63be;--dg-white:#f7f8ff;--dg-muted:#a7b0c1;--dg-font:system-ui,sans-serif;--dg-display:'Arial Narrow',Impact,sans-serif}\r\n.dg-stage{border:3px solid #59647c;box-shadow:inset 0 0 0 2px #070914,inset 0 10px 18px #a9b2ff16,0 7px 0 #040711,0 12px 26px #0009}\r\n.dg-alien-club{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;--club-beat:.3}\r\n.dg-club-haze{position:absolute;inset:12% -10% 9%;background:radial-gradient(ellipse at 18% 65%,#4dc6b329,transparent 50%),radial-gradient(ellipse at 83% 20%,#a96fff3a,transparent 60%),linear-gradient(transparent 50%,#09152e 94%);opacity:.9}\r\n.dg-club-truss{position:absolute;left:9%;right:9%;top:7%;height:10px;background:repeating-linear-gradient(125deg,#171827 0 8px,#566079 9px 11px,#151726 12px 20px);border-block:2px solid #7784a3;transform:perspective(400px) rotateX(-10deg);box-shadow:0 6px 10px #0008;display:flex;justify-content:space-around}\r\n.dg-club-truss i{display:block;width:21px;height:18px;margin-top:6px;border:4px solid #2c3048;border-radius:3px 3px 8px 8px;background:#b6adff;box-shadow:0 0 13px #9e7fff99}\r\n.dg-club-truss i:nth-child(2){background:#9ffff5;box-shadow:0 0 13px #5bf5ff99}\r\n.dg-club-lasers{position:absolute;inset:0;overflow:hidden;opacity:.55}\r\n.dg-club-lasers i{position:absolute;top:9%;left:calc(17% + var(--beam)*13%);width:2px;height:90%;transform-origin:top;background:linear-gradient(#bbffe7,#66ffc7aa,transparent);box-shadow:0 0 6px #67ffd7;opacity:.55}\r\n.dg-club-lasers i:nth-child(even){background:linear-gradient(#efb8ff,#ce7fff88,transparent);box-shadow:0 0 7px #a177ff}\r\n.dg-alien-crowd{position:absolute;inset:20% 0 14%;mask-image:linear-gradient(transparent,#000 15%,#000 82%,transparent)}\r\n.dg-alien-crowd svg{position:absolute;bottom:0;width:15%;overflow:visible;transform-origin:50% 100%;filter:drop-shadow(0 0 2px #9886bb55)}\r\n.dg-alien-crowd svg:nth-child(3n){bottom:13%;opacity:.5;width:12%}\r\n.dg-alien-crowd svg:nth-child(3n + 1){bottom:7%;opacity:.75}\r\n.dg-alien-body{fill:#101523;stroke:#686186;stroke-width:.7}.dg-alien-head{fill:#191b2d;stroke:#74688f;stroke-width:.8}.dg-alien-eyes{fill:#91b8af;opacity:.45}\r\n.dg-club-glints{position:absolute;inset:0}.dg-club-glints i{position:absolute;width:3px;height:3px;background:#ccffef;box-shadow:0 0 5px #b8c4ff;transform:rotate(45deg)}\r\n.dg-coach-contact{position:absolute;left:29%;width:42%;height:5%;bottom:11%;border-radius:50%;background:radial-gradient(ellipse,#02040ce8 5%,#050b19aa 38%,transparent 72%);filter:blur(2px);z-index:1}\r\n.dg-dance-floor{position:absolute;z-index:1;left:-14%;right:-14%;bottom:4%;height:23%;transform:perspective(300px) rotateX(48deg);border-top:2px solid #b6d5f36b;border-bottom:9px solid #101829;background:repeating-linear-gradient(0deg,transparent 0 27px,#a9d9e543 28px 29px),repeating-linear-gradient(90deg,#122538 0 41px,#8d81c975 42px 44px);box-shadow:0 10px 0 #050910,0 13px 20px #000b,inset 0 0 38px #7c67ce44}\r\n.dg-coach-wrap{z-index:2}\r\n.dance-room-readout[data-layout=compact]{grid-template-columns:max-content minmax(0,1fr);align-items:center;gap:4px 8px!important}\r\n.dance-room-readout[data-layout=compact] .dance-room-score{grid-column:1;grid-row:1;font-size:clamp(30px,8vw,48px)!important;line-height:.9!important}\r\n.dance-room-readout[data-layout=compact] .dance-room-versus{grid-column:2;grid-row:1;font-size:clamp(16px,4vw,22px)!important;line-height:1.05!important}\r\n.dance-room-readout[data-layout=compact] .dance-room-beat{grid-column:1;grid-row:2;font-size:12px!important;line-height:1.15!important}\r\n.dance-room-readout[data-layout=compact] .dance-room-move{grid-column:2;grid-row:2;font-size:12px!important;line-height:1.15!important}\r\n.dance-room-readout[data-layout=compact] .dance-room-score,.dance-room-readout[data-layout=compact] .dance-room-versus,.dance-room-readout[data-layout=compact] .dance-room-beat,.dance-room-readout[data-layout=compact] .dance-room-move{min-width:0;overflow-wrap:anywhere}\r\n@media(prefers-reduced-motion:reduce){.dg-club-lasers{opacity:.3}.dg-club-glints{display:none}}`;\r\n\r\n// app.mjs passes this to the Dance view, which passes it to mountClubPresentation.\r\n// Song bpm and firstBeatSeconds; routineStartBeat is not part of that beat.\r\nfunction clubBeat(positionSeconds, tempo) {\r\n  const bpm = tempo?.bpm;\r\n  const origin = tempo?.firstBeatSeconds;\r\n  if (!Number.isFinite(positionSeconds) || !Number.isFinite(bpm) || !Number.isFinite(origin)) return 0;\r\n  return (positionSeconds - origin) * bpm / 60;\r\n}\r\n\r\nfunction readRival(visual) {\r\n  if (visual == null) return null;\r\n  if (typeof visual !== 'object' || typeof visual.atlasPath !== 'string' || visual.atlasPath.length === 0) throw new TypeError('rival visual needs an atlas path');\r\n  const frame = visual.frame;\r\n  if (!Array.isArray(frame) || frame.length !== 4 || frame.some(value => !Number.isInteger(value) || value < 0) || frame[2] < 1 || frame[3] < 1) {\r\n    throw new TypeError('rival visual frame must be four whole pixels');\r\n  }\r\n  if (visual.id != null && typeof visual.id !== 'string') throw new TypeError('rival visual id must be a string');\r\n  return {id: visual.id ?? null, atlasPath: visual.atlasPath, frame: [...frame]};\r\n}\r\n\r\nfunction whenReady(image) {\r\n  if (typeof image?.decode === 'function') return Promise.resolve(image.decode());\r\n  if (image?.complete) return Promise.resolve();\r\n  return new Promise((resolve, reject) => {\r\n    image.addEventListener?.('load', () => resolve(), {once: true});\r\n    image.addEventListener?.('error', () => reject(new Error('rival atlas failed')), {once: true});\r\n  });\r\n}\r\n\r\nexport function createDanceFigure(canvas) {\r\n  const context = canvas.getContext?.('2d');\r\n  if (!context) throw new TypeError('dance figure needs a 2d canvas');\r\n  return {\r\n    render(state) {\r\n      const width = canvas.clientWidth || canvas.width || 0;\r\n      const height = canvas.clientHeight || canvas.height || 0;\r\n      if (!(width > 0 && height > 0) || !state?.moveId) return;\r\n      if (canvas.width !== width) canvas.width = width;\r\n      if (canvas.height !== height) canvas.height = height;\r\n      context.clearRect(0, 0, width, height);\r\n      const pose = sampleMove(state.moveId, state.movePhase ?? 0);\r\n      drawFigure(context, projectCoach(pose.landmarks, {x: 8, y: 8, w: width - 16, h: height - 16}, false));\r\n    },\r\n    dispose() {},\r\n  };\r\n}\r\n\r\nfunction line(doc, className) {\r\n  const node = doc.createElement('p');\r\n  node.className = className;\r\n  node.style.margin = '0';\r\n  node.style.color = '#f4efff';\r\n  node.style.textShadow = '0 2px 0 #05070f';\r\n  return node;\r\n}\r\n\r\nfunction placeRival(canvas) {\r\n  const style = canvas.style;\r\n  style.position = 'absolute';\r\n  style.right = '8%';\r\n  style.bottom = '8%';\r\n  style.width = 'min(18%, 96px)';\r\n  style.maxWidth = '96px';\r\n  style.height = 'auto';\r\n  style.zIndex = '3';\r\n  style.pointerEvents = 'none';\r\n  style.imageRendering = 'pixelated';\r\n}\r\n\r\nexport function createDanceRoomView({\r\n  host,\r\n  presentationFactory = mountClubPresentation,\r\n  sceneFactory = createDanceFigure,\r\n  rivalVisual = null,\r\n  imageFactory = null,\r\n  reducedMotion = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false,\r\n} = {}) {\r\n  if (!host || typeof host.append !== 'function') throw new TypeError('dance room needs a host');\r\n  const doc = host.ownerDocument ?? globalThis.document;\r\n  if (!doc || typeof doc.createElement !== 'function') throw new TypeError('dance room needs a document');\r\n  const rival = readRival(rivalVisual);\r\n  const element = doc.createElement('div');\r\n  element.className = 'dance-room';\r\n  element.setAttribute('data-room', 'dance');\r\n  fill(element);\r\n  element.style.color = '#f4efff';\r\n  element.style.font = '700 1.25rem/1.2 system-ui, sans-serif';\r\n  const shadow = element.attachShadow?.({mode: 'open'});\r\n  if (!shadow || typeof shadow.append !== 'function') throw new TypeError('dance room needs a shadow root');\r\n  const style = doc.createElement('style');\r\n  style.textContent = CLUB_CSS;\r\n  const stage = doc.createElement('div');\r\n  stage.className = 'dance-room-stage dg-stage';\r\n  fill(stage);\r\n  const floor = doc.createElement('div');\r\n  floor.className = 'dg-dance-floor';\r\n  floor.setAttribute('aria-hidden', 'true');\r\n  const wrap = doc.createElement('div');\r\n  wrap.className = 'dg-coach-wrap';\r\n  fill(wrap);\r\n  const figure = doc.createElement('canvas');\r\n  figure.className = 'dance-room-figure';\r\n  fill(figure);\r\n  wrap.append(figure);\r\n  stage.append(floor, wrap);\r\n  let rivalCanvas = null;\r\n  if (rival) {\r\n    rivalCanvas = doc.createElement('canvas');\r\n    rivalCanvas.className = 'dance-room-rival';\r\n    rivalCanvas.width = rival.frame[2];\r\n    rivalCanvas.height = rival.frame[3];\r\n    placeRival(rivalCanvas);\r\n    rivalCanvas.setAttribute('aria-hidden', 'true');\r\n    if (rival.id) rivalCanvas.dataset.coachId = rival.id;\r\n    stage.append(rivalCanvas);\r\n  }\r\n  const readout = doc.createElement('div');\r\n  readout.className = 'dance-room-readout';\r\n  readout.style.position = 'absolute';\r\n  readout.style.left = '16px';\r\n  readout.style.right = '16px';\r\n  readout.style.top = '16px';\r\n  readout.style.bottom = 'auto';\r\n  readout.style.zIndex = '2';\r\n  readout.style.display = 'grid';\r\n  readout.style.gap = '8px';\r\n  const score = line(doc, 'dance-room-score');\r\n  score.style.font = '800 clamp(48px, 12vw, 96px)/0.9 system-ui, sans-serif';\r\n  const versus = line(doc, 'dance-room-versus');\r\n  versus.style.font = '800 clamp(28px, 6vw, 48px)/1.1 system-ui, sans-serif';\r\n  const beat = line(doc, 'dance-room-beat');\r\n  const move = line(doc, 'dance-room-move');\r\n  readout.append(score, versus, beat, move);\r\n  shadow.append(style, stage, readout);\r\n  host.append(element);\r\n  let presentation;\r\n  let scene;\r\n  try {\r\n    presentation = presentationFactory(stage);\r\n    if (!presentation || typeof presentation.update !== 'function' || typeof presentation.dispose !== 'function') {\r\n      throw new TypeError('club presentation is missing update or dispose');\r\n    }\r\n    scene = sceneFactory(figure);\r\n    if (!scene || typeof scene.render !== 'function' || typeof scene.dispose !== 'function') {\r\n      throw new TypeError('dance scene is missing render or dispose');\r\n    }\r\n  } catch (error) {\r\n    try { presentation?.dispose?.(); } catch {}\r\n    try { scene?.dispose?.(); } catch {}\r\n    element.remove();\r\n    throw error;\r\n  }\r\n  let disposed = false;\r\n  let ready = false;\r\n  let shown = null;\r\n  let drawn = null;\r\n  let tempo = null;\r\n  let rivalImage = null;\r\n  let rivalToken = 0;\r\n  const motion = createAcknowledgedMotion();\r\n  const tour = createViewTour(progress => {\r\n    const u = progress * progress * (3 - 2 * progress);\r\n    stage.style.transform = `translate(${Math.sin(progress*Math.PI)*3*(1-u)}%,${-5*(1-u)}%) scale(${.83+.17*u})`;\r\n  }, () => { stage.style.transform = ''; });\r\n  let identity = null, lastClock = null;\r\n  if (rival) {\r\n    const token = ++rivalToken;\r\n    const image = (imageFactory ?? (src => {\r\n      const made = new (doc.defaultView?.Image ?? globalThis.Image)();\r\n      made.src = src;\r\n      return made;\r\n    }))(rival.atlasPath);\r\n    whenReady(image).then(() => {\r\n      if (disposed || token !== rivalToken) return;\r\n      rivalImage = image;\r\n    }, () => {\r\n      if (disposed || token !== rivalToken) return;\r\n      rivalImage = null;\r\n    });\r\n  }\r\n\r\n  function layoutReadout() {\r\n    const hostRect = host.getBoundingClientRect?.();\r\n    if (!hostRect || !(hostRect.width > 0 && hostRect.height > 0) || typeof doc.querySelectorAll !== 'function') {\r\n      readout.style.top = '16px';\r\n      readout.style.bottom = 'auto';\r\n      readout.style.maxHeight = 'none';\r\n      readout.dataset.layout = 'normal';\r\n      return;\r\n    }\r\n    const rectOf = element => {\r\n      if (!element || element.hidden || typeof element.getBoundingClientRect !== 'function') return null;\r\n      const rect = element.getBoundingClientRect();\r\n      return rect.width > 0 && rect.height > 0 && Number.isFinite(rect.top) && Number.isFinite(rect.bottom) ? rect : null;\r\n    };\r\n    const bounds = (selectors, edge, choose) => {\r\n      const values = [];\r\n      for (const selector of selectors) {\r\n        for (const item of doc.querySelectorAll(selector)) {\r\n          const rect = rectOf(item);\r\n          if (rect) values.push(rect[edge]);\r\n        }\r\n      }\r\n      return values.length ? choose(...values) : null;\r\n    };\r\n    const headerBottom = bounds(['.run-progress', '#stats', '.coach-tag'], 'bottom', Math.max);\r\n    const footerTop = bounds(['#run-message', '#end-run'], 'top', Math.min);\r\n    if (!Number.isFinite(headerBottom) || !Number.isFinite(footerTop)) {\r\n      readout.style.top = '16px';\r\n      readout.style.bottom = 'auto';\r\n      readout.style.maxHeight = 'none';\r\n      readout.dataset.layout = 'normal';\r\n      return;\r\n    }\r\n    const top = Math.max(0, Math.ceil(headerBottom - hostRect.top + 10));\r\n    const safeFooter = Math.max(0, Math.floor(footerTop - hostRect.top - 10));\r\n    const available = Math.max(0, safeFooter - top);\r\n    readout.style.top = `${top}px`;\r\n    readout.style.bottom = `${Math.max(0, hostRect.height - safeFooter)}px`;\r\n    readout.style.maxHeight = `${available}px`;\r\n    readout.dataset.layout = available < 190 ? 'compact' : 'normal';\r\n    readout.style.left = available < 190 ? '12px' : '16px';\r\n    readout.style.right = available < 190 ? '12px' : '16px';\r\n  }\r\n\r\n  function paintReadout(state) {\r\n    const catalog = MOVES.find(item => item.id === state.moveId);\r\n    const flags = state.provisional ?? {};\r\n    const rivalScore = flags.rival?.score;\r\n    score.textContent = String(state.score ?? '');\r\n    versus.textContent = `You ${state.score ?? ''} · ${flags.rival?.id ?? 'rival'} ${rivalScore ?? ''}`;\r\n    beat.textContent = `${state.judgedBeats ?? 0} beats · ${state.judgment ?? ''}`;\r\n    move.textContent = catalog ? `${catalog.title}. ${catalog.cue}` : String(state.moveId ?? '');\r\n    readout.dataset.granted = String(flags.granted === true);\r\n    readout.dataset.clear = String(flags.clear === true);\r\n    readout.dataset.outdanced = String(flags.outdanced === true);\r\n    readout.dataset.special = String(flags.specialRewardEligible === true);\r\n    readout.dataset.reward = flags.reward ?? '';\r\n    readout.dataset.rivalScore = rivalScore == null ? '' : String(rivalScore);\r\n  }\r\n\r\n  function paintRival() {\r\n    if (!rivalCanvas || !rivalImage) return;\r\n    const context = rivalCanvas.getContext?.('2d');\r\n    if (!context) return;\r\n    const [x, y, w, h] = rival.frame;\r\n    context.imageSmoothingEnabled = false;\r\n    context.clearRect(0, 0, w, h);\r\n    context.drawImage(rivalImage, x, y, w, h, 0, 0, w, h);\r\n  }\r\n\r\n  function update(checkpoint) {\r\n    if (disposed) throw new Error('dance room is disposed');\r\n    const room = createDanceRoom({runId: checkpoint?.runId, seed: checkpoint?.seed, saved: checkpoint});\r\n    shown = room.state();\r\n    drawn = {...shown};\r\n    const nextIdentity = `${checkpoint.runId}:${checkpoint.seed}:${shown.moveId}:${shown.phase}`;\r\n    if (identity !== null && (identity !== nextIdentity || shown.positionSeconds < lastClock)) tour.cancel();\r\n    identity = nextIdentity; lastClock = shown.positionSeconds;\r\n    motion.push({positionSeconds:shown.positionSeconds,movePhase:shown.movePhase},{identity,clock:shown.positionSeconds});\r\n    tempo = room.game.tempo;\r\n    paintReadout(shown);\r\n    ready = true;\r\n  }\r\n\r\n  function render(frame) {\r\n    if (disposed || !shown) return;\r\n    layoutReadout();\r\n    if (!frame || typeof frame.now !== 'number' || !Number.isFinite(frame.now) || frame.now < 0) throw new RangeError('render now must be a finite clock');\r\n    if (typeof frame.paused !== 'boolean') throw new TypeError('render paused must be a boolean');\r\n    if (typeof frame.dt !== 'number' || !Number.isFinite(frame.dt) || frame.dt < 0) throw new RangeError('render dt must be a finite duration');\r\n    tour.apply();\r\n    const values = motion.sample(frame.dt,{paused:frame.paused,reducedMotion});\r\n    Object.assign(drawn, values);\r\n    presentation.update(clubBeat(drawn.positionSeconds, tempo), frame.paused === true || tour.active);\r\n    scene.render(drawn, {paused: frame.paused === true || tour.active});\r\n    paintRival();\r\n  }\r\n\r\n  function dispose() {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    tour.dispose();\r\n    ready = false;\r\n    shown = null;\r\n    tempo = null;\r\n    rivalToken += 1;\r\n    rivalImage = null;\r\n    presentation.dispose();\r\n    scene.dispose();\r\n    element.remove();\r\n  }\r\n\r\n  return {update, render, dispose, element, intro:options=>tour.intro({reducedMotion,...options}), get ready() { return ready; }};\r\n}\r\n","modules/roguelite/dance-room.mjs":"import {DanceGame} from '../dance/android-game/engine.mjs';\nimport {BeatScore,ROUND_BEATS,STAR_THRESHOLDS} from '../dance/android-game/beat-score.mjs';\nimport {MOVES,chooseRoutine} from '../dance/android-game/moves.mjs';\n\nconst finite=Number.isFinite,clone=value=>structuredClone(value);\nconst GAME_FIELDS=Object.freeze(['phase','track','tempo','routine','started','cameraReady','breakUntil','taps','manualPause','countUntil','routineStartBeat','generation']);\nconst SCORE_JUDGMENTS=new Set(['KEEP MOVING','PERFECT','GOOD','MISS']);\nconst safeRunId=value=>typeof value==='string'&&/^[A-Za-z0-9:_-]{1,96}$/.test(value);\n\nfunction assertObservation(input){\n if(!input||typeof input!=='object'||!finite(input.now)||input.now<0||typeof input.tracking!=='boolean'||typeof input.paused!=='boolean'\n  ||!finite(input.energy)||input.energy<0||input.energy>1||!finite(input.coverage)||input.coverage<0||input.coverage>1)\n  throw new TypeError('Invalid dance room observation.');\n}\nfunction validateCheckpoint(saved,runId,seed){\n if(!saved||saved.version!==1||saved.runId!==runId||saved.seed!==seed||!finite(saved.serverNow)||saved.serverNow<0\n  ||saved.lastServerNow!==null&&(!finite(saved.lastServerNow)||saved.lastServerNow<0)\n  ||!finite(saved.musicPosition)||saved.musicPosition<0||!Number.isSafeInteger(saved.sequence)||saved.sequence<1\n  ||!saved.game||typeof saved.game!=='object'||!saved.clock||typeof saved.clock!=='object'||!saved.score||typeof saved.score!=='object')\n  throw new TypeError('Invalid dance room checkpoint.');\n const game=saved.game,score=saved.score,clock=saved.clock;\n if(!['prepare','ready','count_in','dancing','paused','lost','break','results','waiting'].includes(game.phase)\n  ||game.track!==`roguelite-dance:${runId}`||typeof game.started!=='boolean'||typeof game.cameraReady!=='boolean'||typeof game.manualPause!=='boolean'\n  ||!Array.isArray(game.routine)||game.routine.some(id=>!MOVES.some(move=>move.id===id))||!Array.isArray(game.taps)\n  ||!Number.isSafeInteger(game.generation)||game.generation<0\n  ||!Number.isSafeInteger(score.judgedBeats)||score.judgedBeats<0||score.judgedBeats>ROUND_BEATS\n  ||!Number.isSafeInteger(score.score)||score.score<0||score.score>12000||score.score%25!==0||!SCORE_JUDGMENTS.has(score.judgment)\n  ||!Array.isArray(score.seen)||score.seen.length>ROUND_BEATS||new Set(score.seen).size!==score.seen.length||score.seen.some(key=>typeof key!=='string')\n  ||score.pending!==null&&(!score.pending||!Number.isSafeInteger(score.pending.index)||!finite(score.pending.quality)||score.pending.quality<0||score.pending.quality>100||typeof score.pending.active!=='boolean')\n  ||score.last!==null&&(!score.last||!finite(score.last.now)||!finite(score.last.p)||!finite(score.last.bpm)||!finite(score.last.origin)||!Number.isSafeInteger(score.last.generation)||typeof score.last.trackKey!=='string')\n  ||score.nextBeat!==null&&(!Number.isSafeInteger(score.nextBeat)||score.nextBeat<0)\n  ||clock.lastSequence!==saved.sequence-1||!Number.isSafeInteger(clock.epoch)||clock.epoch!==1\n  ||!clock.data||clock.data.schema!=='myr5.external-music.v1'||clock.data.trackKey!==game.track||clock.data.sequence!==clock.lastSequence\n  ||clock.data.epoch!==clock.epoch||!finite(clock.data.positionSeconds)||clock.data.positionSeconds!==saved.musicPosition\n  ||clock.data.playbackSpeed!==1||typeof clock.data.playing!=='boolean'||typeof clock.data.fresh!=='boolean'\n  ||!finite(clock.receivedAt)||clock.receivedAt!==saved.serverNow)\n  throw new TypeError('Invalid dance room checkpoint state.');\n for(const key of ['breakUntil','routineStartBeat'])if(!finite(game[key]))throw new TypeError('Invalid dance room checkpoint state.');\n for(const key of ['countUntil'])if(game[key]!==null&&!finite(game[key]))throw new TypeError('Invalid dance room checkpoint state.');\n if(game.tempo!==null&&(!game.tempo||!finite(game.tempo.bpm)||!finite(game.tempo.displayBpm)||!finite(game.tempo.firstBeatSeconds)||typeof game.tempo.source!=='string'))\n  throw new TypeError('Invalid dance room checkpoint tempo.');\n if(game.taps.some(value=>!finite(value)))throw new TypeError('Invalid dance room checkpoint taps.');\n}\n\n/** Server-clock dance session backed by the existing Android game and 120-beat scorer. */\nexport function createDanceRoom({runId,seed=0,saved=null}={}){\n if(!safeRunId(runId)||!(typeof seed==='string'||Number.isSafeInteger(seed)))throw new TypeError('Dance room needs a stable run ID and seed.');\n let serverNow=saved?.serverNow??0,lastServerNow=saved?.lastServerNow??null,musicPosition=saved?.musicPosition??0,sequence=saved?.sequence??0;\n const trackKey=`roguelite-dance:${runId}`;\n const game=new DanceGame({moves:MOVES,chooseRoutine,now:()=>serverNow,seed:`${runId}:${seed}`});\n const score=new BeatScore();\n if(!saved){\n  game.receive({schema:'myr5.external-music.v1',sequence:0,epoch:1,trackKey,title:'Dance Room',artist:'MYR5',positionSeconds:0,dispatchAgeMs:0,fresh:true,playing:false,speed:1,playbackSpeed:1});\n  sequence=1;\n }\n if(saved){\n  validateCheckpoint(saved,runId,seed);\n  for(const key of GAME_FIELDS)game[key]=clone(saved.game[key]);\n  game.clock.data=clone(saved.clock.data);game.clock.receivedAt=saved.clock.receivedAt;game.clock.lastSequence=saved.clock.lastSequence;\n  score.score=saved.score.score;score.judgedBeats=saved.score.judgedBeats;score.judgment=saved.score.judgment;\n  score.seen=new Set(saved.score.seen);score.pending=clone(saved.score.pending);score.last=clone(saved.score.last);\n  if(saved.score.nextBeat!==null)score.nextBeat=saved.score.nextBeat;\n }\n const rivalScore=STAR_THRESHOLDS[2],specialRewardScore=STAR_THRESHOLDS[3];\n function state(){\n  const frame=game.frame(),scored=score.snapshot(),complete=scored.roundComplete;\n  return {...frame,...scored,provisional:{clear:complete,outdanced:complete&&scored.score>rivalScore,specialRewardEligible:complete&&scored.score>=specialRewardScore,\n   rival:{id:'coach-rival',score:rivalScore},reward:complete&&scored.score>=specialRewardScore?'outdance-margin-once':null,granted:false}};\n }\n function advance(input){\n  assertObservation(input);\n  serverNow=input.now;\n  const delta=lastServerNow===null?0:serverNow-lastServerNow;\n  const continuous=lastServerNow===null||delta>0&&delta<=500;\n  const playing=score.judgedBeats<ROUND_BEATS&&!input.paused&&input.tracking&&continuous;\n  if(lastServerNow!==null&&playing)musicPosition+=delta/1000;\n  if(lastServerNow===null||delta>0)lastServerNow=serverNow;\n  const packet={schema:'myr5.external-music.v1',sequence:sequence++,epoch:1,trackKey,title:'Dance Room',artist:'MYR5',positionSeconds:musicPosition,\n   dispatchAgeMs:0,fresh:true,playing,speed:1,playbackSpeed:1};\n  if(!game.receive(packet))throw new Error('Run-owned dance clock rejected its music packet.');\n  game.setCamera(input.tracking&&!input.paused);\n  if(playing){\n   if(!game.tempo&&!game.setTempo(120,0,'run-clock'))throw new Error('Run-owned tempo could not select a dance routine.');\n   if(!game.started)game.begin();\n   else if(game.phase==='paused'&&game.cameraReady)game.begin();\n  }\n  const frame=game.frame();\n  const enabled=playing&&frame.phase==='dancing';\n  const judged=score.ingest({positionSeconds:frame.positionSeconds,bpm:game.tempo?.bpm,firstBeatSeconds:game.tempo?.firstBeatSeconds,\n   energy:input.energy,tracking:input.tracking,coverage:input.coverage,enabled,generation:frame.generation,trackKey:frame.trackKey,observedAtMs:serverNow},serverNow);\n  if(judged.roundComplete&&game.phase!=='results')game.finish();\n  return state();\n }\n function snapshot(){\n  const gameState={};for(const key of GAME_FIELDS)gameState[key]=clone(game[key]);\n  return {version:1,runId,seed,serverNow,lastServerNow,musicPosition,sequence,\n   game:gameState,clock:{data:clone(game.clock.data),receivedAt:game.clock.receivedAt,lastSequence:game.clock.lastSequence,epoch:1},\n   score:{score:score.score,judgedBeats:score.judgedBeats,judgment:score.judgment,seen:[...score.seen],pending:clone(score.pending),last:clone(score.last),nextBeat:score.nextBeat??null}};\n }\n return {advance,snapshot,state,get score(){return score.snapshot();},get game(){return game;}};\n}\n","modules/roguelite/door-choice-view.mjs":"import {createDoorChoice,observeDoorChoice} from './door-choice.mjs';\r\n\r\nconst GESTURES = new Map([\r\n  ['crystal', 'Punch'],\r\n  ['merchant', 'Open hand pinch + turn'],\n  ['papiermache', 'Open hand, pinch + turn'],\n  ['jelly', 'Raise both arms'],\n  ['grass', 'Push with both open palms'],\n  ['clay', 'Drink shrink'],\r\n  ['discolights', 'Disco lights'],\n  ['dance', 'Light all four corners'],\n  ['pond', 'Hand out'],\r\n  ['training', 'Follow coach'],\r\n]);\r\n\r\nfunction gestureLabel(offer) {\r\n  const source = offer.roomId ?? offer.doorId ?? offer.id;\r\n  const key = String(source).toLowerCase().replace(/[^a-z0-9]/g, '');\r\n  return GESTURES.get(key) ?? (offer.motion === 'crouch' ? 'Crouch' : 'Lean');\r\n}\r\n\r\nexport function mountDoorChoices(host,offers){\r\n let state=createDoorChoice(offers),disposed=false,resolve,reject;\r\n const root=document.createElement('section');root.className='run-door-choices';root.setAttribute('aria-label','Choose your next room');\r\n const title=document.createElement('h2');title.textContent='CHOOSE YOUR PATH';\r\n const hint=document.createElement('p');hint.className='run-door-hint';hint.textContent='LEAN LEFT OR RIGHT. HOLD, THEN RETURN TO CENTER TO CHOOSE.';\r\n const cards=document.createElement('div');cards.className='run-door-cards';\r\n for(const offer of offers){\r\n  const card=document.createElement('article');card.dataset.door=offer.id;\r\n  const symbol=document.createElement('strong');symbol.textContent=offer.motion==='left'?'←':offer.motion==='right'?'→':'↓';\r\n  if(offer.image){const image=document.createElement('img');image.src=offer.image;image.alt='';card.append(image);}\r\n  const label=document.createElement('h3');label.textContent=offer.label;\r\n  const description=document.createElement('p');description.textContent=offer.description;\r\n  const gesture=document.createElement('span');gesture.className='run-door-gesture';gesture.textContent=gestureLabel(offer);\r\n  card.append(symbol,label,description,gesture);cards.append(card);\r\n }\r\n root.append(title,hint,cards);host.append(root);\r\n const confirmation=new Promise((yes,no)=>{resolve=yes;reject=no;});confirmation.catch(()=>{});\r\n return {confirmation,element:root,\r\n  observe(input){if(disposed)return;const result=observeDoorChoice(state,input);state=result.state;\r\n   const armed=state.armedOfferId;\r\n   const progress=armed===null?(state.stableMs/500):(state.neutralMs/300);\r\n   root.style.setProperty('--selection',String(progress));\r\n   hint.textContent=armed===null?'LEAN LEFT OR RIGHT. HOLD, THEN RETURN TO CENTER TO CHOOSE.':'RETURN TO CENTER TO CHOOSE';\r\n   for(const card of cards.children){\r\n    const selected=card.dataset.door===state.selectedOfferId;\r\n    card.dataset.selected=String(selected);card.dataset.armed=String(card.dataset.door===armed);\r\n    card.style.setProperty('--selection',String(selected?progress:0));\r\n   }\r\n   if(result.events.length)resolve(result.events[0].offer);\r\n  },\r\n  dispose(){if(disposed)return;disposed=true;root.remove();if(!state.confirmedOfferId)reject(new DOMException('Door selection cancelled','AbortError'));},\r\n };\r\n}\r\n","modules/roguelite/door-choice.mjs":"// Pure movement-driven selector. The caller owns sensors, rendering, and offer consequences.\r\nconst MOTIONS = new Set(['left', 'right', 'crouch']);\r\nconst ID = /^[A-Za-z0-9:_-]{1,128}$/;\r\nconst STABLE_MS = 500;\r\nconst NEUTRAL_MS = 300;\r\nconst MAX_STEP_MS = 100;\r\nconst STALE_GAP_MS = 300;\r\nconst clone = value => structuredClone(value);\r\nconst validId = value => typeof value === 'string' && ID.test(value);\r\nconst fail = message => { throw new RangeError(message); };\r\n\r\nfunction jsonSafe(value, seen = new Set()) {\r\n  if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;\r\n  if (typeof value === 'number') return Number.isFinite(value);\r\n  if (typeof value !== 'object' || seen.has(value)) return false;\r\n  if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;\r\n  seen.add(value);\r\n  const safe = Array.isArray(value)\r\n    ? value.every(jsonSafeValue => jsonSafe(jsonSafeValue, seen))\r\n    : Object.keys(value).every(key => value[key] !== undefined && jsonSafe(value[key], seen));\r\n  seen.delete(value);\r\n  return safe;\r\n}\r\n\r\nfunction normalizeOffers(offers) {\r\n  if (!Array.isArray(offers) || offers.length < 2 || offers.length > 3) throw new TypeError('door choice requires two or three offers');\r\n  const ids = new Set(), motions = new Set();\r\n  return offers.map(offer => {\r\n    if (!offer || typeof offer !== 'object' || Array.isArray(offer) || !validId(offer.id) || !MOTIONS.has(offer.motion)\r\n        || ids.has(offer.id) || motions.has(offer.motion)) fail('door offers need unique IDs and unique valid motions');\r\n    ids.add(offer.id);\r\n    motions.add(offer.motion);\r\n    if (!jsonSafe(offer)) throw new TypeError('door offers must contain JSON-safe metadata');\r\n    const json = JSON.stringify(offer);\r\n    const copy = JSON.parse(json);\r\n    if (!copy || copy.id !== offer.id || copy.motion !== offer.motion) throw new TypeError('door offer is not JSON-safe');\r\n    return copy;\r\n  });\r\n}\r\n\r\nexport function createDoorChoice(offers) {\r\n  return {\r\n    version: 1,\r\n    offers: normalizeOffers(offers),\r\n    lastObservationId: -1,\r\n    lastNow: null,\r\n    selectedOfferId: null,\r\n    stableMs: 0,\r\n    armedOfferId: null,\r\n    neutralMs: 0,\r\n    prevPunch: null,\r\n    confirmedOfferId: null,\r\n  };\r\n}\r\n\r\nfunction assertSaved(state) {\r\n  if (!state || state.version !== 1 || !Array.isArray(state.offers)\r\n      || !Number.isSafeInteger(state.lastObservationId) || state.lastObservationId < -1\r\n      || !(state.lastNow === null || Number.isFinite(state.lastNow))\r\n      || !(state.selectedOfferId === null || validId(state.selectedOfferId))\r\n      || !Number.isFinite(state.stableMs) || state.stableMs < 0 || state.stableMs > STABLE_MS\r\n      || !(state.prevPunch === null || typeof state.prevPunch === 'boolean')\r\n      || !(state.confirmedOfferId === null || validId(state.confirmedOfferId))) throw new TypeError('invalid door choice state');\r\n\r\n  const migrated = { ...state };\r\n  if (!Object.hasOwn(migrated, 'armedOfferId')) migrated.armedOfferId = null;\r\n  if (!Object.hasOwn(migrated, 'neutralMs')) migrated.neutralMs = 0;\r\n  if (!(migrated.armedOfferId === null || validId(migrated.armedOfferId))) throw new TypeError('invalid armed door choice state');\r\n  if (!Number.isFinite(migrated.neutralMs) || migrated.neutralMs < 0 || migrated.neutralMs > NEUTRAL_MS) throw new TypeError('invalid neutral door choice state');\r\n\r\n  const offers = normalizeOffers(state.offers);\r\n  const offered = new Set(offers.map(offer => offer.id));\r\n  if (migrated.selectedOfferId !== null && !offered.has(migrated.selectedOfferId)) fail('selected door is not an offer');\r\n  if (migrated.armedOfferId !== null && !offered.has(migrated.armedOfferId)) fail('armed door is not an offer');\r\n  if (migrated.confirmedOfferId !== null && !offered.has(migrated.confirmedOfferId)) fail('confirmed door is not an offer');\r\n  if (migrated.selectedOfferId === null && migrated.stableMs !== 0) fail('unselected door cannot have stable time');\r\n  return { ...clone(migrated), offers };\r\n}\r\n\r\n/** Resume saved offers and confirmation, dropping transient stability and input edges. */\r\nexport function restoreDoorChoice(saved) {\r\n  const restored = assertSaved(saved);\r\n  restored.lastObservationId = -1;\r\n  restored.lastNow = null;\r\n  restored.selectedOfferId = null;\r\n  restored.stableMs = 0;\r\n  restored.armedOfferId = null;\r\n  restored.neutralMs = 0;\r\n  restored.prevPunch = null;\r\n  return restored;\r\n}\r\n\r\nexport function observeDoorChoice(state, { observationId, now, tracked, choice = null, punch, paused = false } = {}) {\r\n  const current = assertSaved(state);\r\n  if (!Number.isSafeInteger(observationId) || observationId < 0) throw new TypeError('observationId must be a nonnegative integer');\r\n  if (!Number.isFinite(now)) throw new TypeError('now must be finite');\r\n  if (typeof tracked !== 'boolean' || typeof punch !== 'boolean' || typeof paused !== 'boolean') throw new TypeError('tracked, punch, and paused must be boolean');\r\n  if (choice !== null && !MOTIONS.has(choice)) throw new RangeError('choice must be left, right, crouch, or null');\r\n  if (observationId <= current.lastObservationId) return { state: current, events: [], duplicate: true };\r\n  if (current.lastNow !== null && now < current.lastNow) throw new RangeError('observation time cannot move backward');\r\n\r\n  const next = clone(current), events = [];\r\n  const gap = current.lastNow === null ? 0 : now - current.lastNow;\r\n  const stale = current.lastNow !== null && gap > STALE_GAP_MS;\r\n  const dt = current.lastNow === null || stale ? 0 : Math.min(MAX_STEP_MS, gap);\r\n  next.lastObservationId = observationId;\r\n  next.lastNow = now;\r\n\r\n  if (paused || !tracked || stale) {\r\n    next.selectedOfferId = null;\r\n    next.stableMs = 0;\r\n    next.armedOfferId = null;\r\n    next.neutralMs = 0;\r\n    next.prevPunch = punch;\r\n    return { state: next, events, duplicate: false };\r\n  }\r\n\r\n  if (next.confirmedOfferId !== null) {\r\n    next.selectedOfferId = null;\r\n    next.stableMs = 0;\r\n    next.armedOfferId = null;\r\n    next.neutralMs = 0;\r\n    next.prevPunch = punch;\r\n    return { state: next, events, duplicate: false };\r\n  }\r\n\r\n  const offer = next.offers.find(item => item.motion === choice) ?? null;\r\n  next.prevPunch = punch;\r\n\r\n  if (offer) {\r\n    next.neutralMs = 0;\r\n    if (next.armedOfferId !== null && offer.id !== next.armedOfferId) {\r\n      next.armedOfferId = null;\r\n      next.selectedOfferId = offer.id;\r\n      next.stableMs = 0;\r\n    } else if (next.selectedOfferId !== offer.id) {\r\n      next.selectedOfferId = offer.id;\r\n      next.stableMs = 0;\r\n    } else {\r\n      next.stableMs = Math.min(STABLE_MS, next.stableMs + dt);\r\n    }\r\n\r\n    if (next.armedOfferId === null && next.stableMs >= STABLE_MS) next.armedOfferId = offer.id;\r\n    return { state: next, events, duplicate: false };\r\n  }\r\n\r\n  if (next.armedOfferId !== null) {\r\n    next.stableMs = 0;\r\n    next.neutralMs = Math.min(NEUTRAL_MS, next.neutralMs + dt);\r\n    if (next.neutralMs >= NEUTRAL_MS) {\r\n      const confirmed = next.offers.find(item => item.id === next.armedOfferId);\r\n      next.confirmedOfferId = confirmed.id;\r\n      next.selectedOfferId = null;\r\n      next.stableMs = 0;\r\n      next.armedOfferId = null;\r\n      next.neutralMs = 0;\r\n      events.push({ type: 'door-confirmed', offer: clone(confirmed) });\r\n    }\r\n    return { state: next, events, duplicate: false };\r\n  }\r\n\r\n  next.selectedOfferId = null;\r\n  next.stableMs = 0;\r\n  next.neutralMs = 0;\r\n  return { state: next, events, duplicate: false };\r\n}\r\n","modules/roguelite/fitness-item-catalog.mjs":"// Finite run offers, not permanent entitlements. The host must issue and receipt each choice.\nimport {BOONS} from './boons.mjs';\nimport {classGear} from './class-combat.mjs';\n\nexport const FITNESS_CATALOG_VERSION = 1;\nexport const MAX_SELECTED_FITNESS_RELICS = 3;\nconst boon = id=>Object.freeze({kind:'boon',id});\nconst gear = (styleId,tier)=>Object.freeze({kind:'weapon',id:classGear(styleId,tier).id,styleId,tier});\nconst damage = Object.freeze({kind:'stat',id:'damage'});\nconst item = (id,name,category,description,price,effectRef)=>Object.freeze({\n  id,name,category,description,price,effectRef,status:'active',hostIntegrated:true,scope:'run',\n});\n\n// Reviewed from nvidia-items.txt: keep the fictional game copy and three legal boons.\n// Drop its overshields and reskins. Eight distinct existing weapon choices plus a\n// damage choice provide twelve unique references without inventing a combat resource.\nexport const FITNESS_ITEMS = Object.freeze([\n  item('gymdolphin-protein-cubes','Gymdolphin Protein Cubes','supplement',\n    'Chew the clock: more time to respond to boss attacks.',3,boon('longerParry')),\n  item('jimshark-muscle-milk','JimShark Muscle Milk','supplement',\n    'Shelf-stable swagger: stronger punches against bosses.',4,boon('bossPunch')),\n  item('flex-o-matic-gummy-bars','Flex-O-Matic Gummy Bars','supplement',\n    'Suspiciously aerodynamic: a longer chase catch window.',4,boon('flightClosing')),\n  item('jimshark-fire-lance','JimShark Hot-Bar Lance','equipment',\n    'A barbell with ambition. Upgrade to a fire lance and wall shield.',5,gear('knight',1)),\n  item('gymdolphin-energy-lance','Gymdolphin Neon Pool Noodle','equipment',\n    'The pool has rules. Upgrade to an energy lance and force wall.',8,gear('knight',2)),\n  item('dojo-disposal-aura-wraps','Dojo Disposal Aura Wraps','martial-arts',\n    'Dry-clean only. Upgrade to aura fists.',5,gear('monk',1)),\n  item('boxing-bell-boss-fists','Boxing Bell Boss Fists','martial-arts',\n    'Ding ding, invoice due. Upgrade to sci-fi power fists.',8,gear('monk',2)),\n  item('jimshark-fire-daggers','JimShark Searing Superset','martial-arts',\n    'Never touch the business ends. Upgrade to paired fire daggers.',5,gear('dual',1)),\n  item('gymdolphin-energy-blades','Gymdolphin Double Trouble','martial-arts',\n    'Two membership cards, two blades. Upgrade to energy short swords.',8,gear('dual',2)),\n  item('whey-wizard-runes','Whey Wizard Shaker Runes','equipment',\n    'Spell it, then shake it. Upgrade to enchanted elemental runes.',5,gear('mage',1)),\n  item('whey-wizard-plasma-focus','Whey Wizard Plasma Blender','equipment',\n    'Do not remove the lid. Upgrade to a plasma spell focus.',8,gear('mage',2)),\n  item('pumper-tuna-crunch','Pumper Tuna Crunch','workout',\n    'Open at a safe distance. Gain one damage upgrade for this run.',4,damage),\n]);\n\n// Collected IDs remain exactly the NPC progression IDs. Selection never grants,\n// consumes, rewrites or automatically equips the rest of the collection.\nexport const FITNESS_RELICS = Object.freeze([\n  Object.freeze({id:'longerParry',name:'Reaction Ball',effectRef:boon('longerParry'),\n    description:'Legacy collected relic: a wider boss response window; duplicate effects do not stack.',status:'legacy',hostIntegrated:false}),\n  Object.freeze({id:'bossPunch',name:'Lifting Chalk',effectRef:boon('bossPunch'),\n    description:'Legacy collected relic: stronger existing boss punches; duplicate effects do not stack.',status:'legacy',hostIntegrated:false}),\n  Object.freeze({id:'flightClosing',name:'Sprint Spikes',effectRef:boon('flightClosing'),\n    description:'Legacy collected relic: one additional existing chase catch window; duplicates do not stack.',status:'legacy',hostIntegrated:false}),\n]);\nexport const INACTIVE_FITNESS_ITEMS = Object.freeze([\n  Object.freeze({id:'overshield',name:'Lifting Belt',status:'inactive',hostIntegrated:false,\n    reason:'Preserve the collected relic; passive protection is unavailable under the broken-armor policy.'}),\n]);\nconst ITEMS = new Map(FITNESS_ITEMS.map(row=>[row.id,row]));\nconst RELICS = new Map(FITNESS_RELICS.map(row=>[row.id,row]));\nconst KNOWN_RELICS = new Set([...RELICS.keys(),'overshield']);\nfor(const row of [...FITNESS_ITEMS,...FITNESS_RELICS]) {\n  if(row.effectRef.kind==='boon'&&(!Object.hasOwn(BOONS,row.effectRef.id)||row.effectRef.id==='overshield'))\n    throw new Error('Unknown or forbidden fitness effect reference');\n}\nexport const fitnessItemById = id=>ITEMS.get(id)??null;\n\n/** Validate an explicit subset of a host-owned collection; an empty selection stays empty.\n * Host should use NPC durable ownership here, never a request-supplied collection.\n */\nexport function selectedFitnessRelics(relicIds,{ownedRelicIds=[]}={}) {\n  if(!Array.isArray(ownedRelicIds)||ownedRelicIds.length>KNOWN_RELICS.size||\n    new Set(ownedRelicIds).size!==ownedRelicIds.length||ownedRelicIds.some(id=>!KNOWN_RELICS.has(id)))\n    throw new TypeError('Invalid owned relic collection');\n  if(!Array.isArray(relicIds)||relicIds.length>MAX_SELECTED_FITNESS_RELICS||\n    new Set(relicIds).size!==relicIds.length||relicIds.some(id=>!RELICS.has(id)||!ownedRelicIds.includes(id)))\n    throw new TypeError('Selected relic is unavailable or unowned');\n  return relicIds.map(id=>({id,boonId:RELICS.get(id).effectRef.id,effectRef:{...RELICS.get(id).effectRef}}));\n}\n","modules/roguelite/game-room-adapters.mjs":"// Pure room adapter over existing engines. No camera, DOM, or second rule set.\n// New clay rooms use arcade/runner/worlds/worlds.mjs (authored 600m leg).\n// The room seed is only the encounter id; that course is not seeded.\n// Pose landmarks stay in pose-input.mjs (arms up, crouch, lean). This module\n// takes jump, slide, left, and right. It does not detect burpees.\n// A version 1 clay checkpoint still continues on arcade/runner/maze-run.mjs.\nimport {ROOM_BY_ID} from './room-contracts.mjs';\nimport {LEVELS} from '../../arcade/cover/levels/index.mjs';\nimport {createCoverSim, restoreCoverSim, SIM as COVER_SIM} from '../../arcade/cover/cover-sim.mjs';\nimport {createRun, input, step, courseFor, JUMP, STUMBLE} from '../../arcade/runner/maze-run.mjs';\nimport {WORLDS, POWERS, createWorldRun, act, advance} from '../../arcade/runner/worlds/worlds.mjs';\n\nexport const GAME_ROOM_IDS = Object.freeze(['clay', 'jelly', 'dance']);\nexport const RUNNER_ACTIONS = Object.freeze(['jump', 'slide', 'left', 'right']);\n// advance() caps one call at 0.25s and slices it at 0.05s. A larger hole is stale tracking, not a skip.\nexport const STALE_SECONDS = 0.25;\nconst CLAY_ENGINE_V1 = 'arcade/runner/maze-run.mjs';\nconst CLAY_ENGINE_V2 = 'arcade/runner/worlds/worlds.mjs';\nconst CLAY_LEGS = 2;\nconst CLAY_LOOPS = 3; // course.mjs: one lane, a second lane, then extra stations\nconst COURSE_SEED_STRIDE = 977; // maze-run.mjs private courseSeed(seed, n) = (seed + n * 977) >>> 0\nconst ID = /^[A-Za-z0-9:_-]{1,96}$/;\n\nexport class GameRoomLimit extends Error {\n  constructor(roomId, detail) {\n    super(detail.summary);\n    this.name = 'GameRoomLimit';\n    this.roomId = roomId;\n    this.detail = detail;\n  }\n}\n\nexport const COVER_SNAPSHOT = Object.freeze({\n  module: 'arcade/cover/cover-sim.mjs',\n  exports: Object.freeze(['createCoverSim', 'restoreCoverSim']),\n  methods: Object.freeze(['snapshot', 'load']),\n  fields: Object.freeze(['version', 'rng', 'beamHitT', 'zT', 'req', 's']),\n  reattach: 's.level and enemy.def from the levels array passed to create/restore',\n  endSignal: \"Jelly finishes when s.phase==='portal' after area reaches SIM.areas. completePortal is not called.\",\n});\n\nexport const DANCE_ADAPTER_SEAM = Object.freeze({\n  summary: 'DanceGame is a wall-clock Android music session, not a pure step(dt) room.',\n  module: 'modules/dance/android-game/engine.mjs',\n  scoreModule: 'modules/dance/android-game/beat-score.mjs',\n  clock: 'DanceGame defaults now to performance.now. ExternalMusicClock.snapshot extrapolates position from wall-clock age since receivedAt.',\n  musicSchema: 'myr5.external-music.v1',\n  step: 'frame() takes no dt. Phase changes use this.now(), cameraReady, manualPause, and breakUntil.',\n  score: 'BeatScore.ingest drops samples older than 250ms and will not fill skipped beats.',\n  missing: 'No snapshot/restore. A pure adapter would need a caller-supplied now plus a serializable clock, tempo, routine, and score. This module does not invent that.',\n  roomId: 'dance',\n});\n\nexport const COVER_VOLUME_NAMES = Object.freeze([...(ROOM_BY_ID.jelly?.variants ?? [])]);\n\nfunction courseSeed(seed, n) {\n  return (seed + n * COURSE_SEED_STRIDE) >>> 0;\n}\n\nfunction levelMatches(volume, level) {\n  const by = level.inspiredBy || '';\n  if (volume === 'Time Crisis') return by === 'Time Crisis';\n  if (volume === 'Halo') return by.startsWith('Halo');\n  if (volume === 'Terminator') return by.includes('Terminator');\n  return false;\n}\n\nfunction volumeLevels(volume) {\n  if (!COVER_VOLUME_NAMES.includes(volume)) throw new TypeError(`unknown cover volume: ${volume}`);\n  const levels = volume === 'Crouching Crisis' ? LEVELS : LEVELS.filter(level => levelMatches(volume, level));\n  if (!levels.length) throw new Error(`cover volume matched no shipped level: ${volume}`);\n  return levels;\n}\n\n/** Shipped Cover Crisis level ids for a jelly catalog variant. Does not start a sim. */\nexport function coverVolume(volume) {\n  return volumeLevels(volume).map(level => ({id: level.id, name: level.name, inspiredBy: level.inspiredBy ?? null}));\n}\n\nfunction sameCatalog(actual, expected) {\n  return Array.isArray(actual) && actual.length === expected.length && actual.every((row, index) => row && row.id === expected[index].id && row.name === expected[index].name && row.inspiredBy === expected[index].inspiredBy);\n}\n\nfunction assertSeed(seed) {\n  if (!Number.isSafeInteger(seed) || seed < 0 || seed > 0xffffffff) throw new TypeError('seed must be an integer from 0 to 4294967295');\n}\n\nfunction readActions(frame) {\n  if (!frame || typeof frame !== 'object') throw new TypeError('step frame must be an object');\n  if (typeof frame.now !== 'number' || !Number.isFinite(frame.now) || frame.now < 0 || frame.now > 1e15) throw new RangeError('now must be a finite millisecond clock');\n  if (typeof frame.tracked !== 'boolean' || typeof frame.paused !== 'boolean') throw new TypeError('tracked and paused must be booleans');\n  if (frame.action != null && frame.actions != null) throw new TypeError('pass action or actions, not both');\n  const list = frame.actions ?? (frame.action == null ? [] : [frame.action]);\n  if (!Array.isArray(list) || list.length > RUNNER_ACTIONS.length) throw new TypeError('actions must be a short list');\n  for (const action of list) if (!RUNNER_ACTIONS.includes(action)) throw new TypeError(`unknown runner action: ${action}`);\n  return list;\n}\n\nfunction sameEvent(actual, expected) {\n  return actual && expected && actual.type === expected.type && actual.at === expected.at\n    && (actual.length || 0) === (expected.length || 0) && (actual.direction ?? null) === (expected.direction ?? null)\n    && !!actual.combo === !!expected.combo;\n}\n\nfunction validateClay(state) {\n  if (!state || state.version !== 1 || state.roomId !== 'clay' || !ID.test(state.encounterId)) throw new TypeError('invalid clay room save');\n  assertSeed(state.seed);\n  if (state.volume != null) throw new TypeError('clay room has no volume');\n  if (state.status !== 'running' && state.status !== 'cleared') throw new RangeError('invalid clay room status');\n  if (typeof state.stale !== 'boolean') throw new TypeError('invalid stale flag');\n  if (!(state.lastNow === null || (typeof state.lastNow === 'number' && Number.isFinite(state.lastNow)))) throw new RangeError('invalid lastNow');\n  const run = state.run;\n  if (!run || run.seed !== state.seed || run.status !== 'running' || run.started !== true || typeof run.paused !== 'boolean') throw new TypeError('invalid runner state');\n  if (!Number.isInteger(run.lane) || run.lane < 0 || run.lane > 2) throw new RangeError('invalid lane');\n  if (run.turn !== null && run.turn !== 'left' && run.turn !== 'right') throw new RangeError('invalid turn');\n  for (const [key, max] of [['jumpTime', JUMP.seconds], ['slideTime', 0.96], ['stumble', STUMBLE.seconds], ['duck', 1], ['height', JUMP.peak]]) {\n    if (typeof run[key] !== 'number' || run[key] < 0 || run[key] > max + 1e-9) throw new RangeError(`invalid runner ${key}`);\n  }\n  if (typeof run.pressure !== 'number' || run.pressure < STUMBLE.floor - 1e-9 || run.pressure > 1) throw new RangeError('invalid pressure');\n  if (![run.time, run.distance, run.total].every(Number.isFinite) || run.time < 0 || run.distance < 0 || run.total < 0) throw new RangeError('invalid runner clocks');\n  if (!Number.isInteger(run.hits) || run.hits < 0 || !Number.isInteger(run.cleared) || run.cleared < 0) throw new RangeError('invalid runner counts');\n  if (!Array.isArray(run.feed) || run.feed.length > 48 || run.feed.some(item => !item || typeof item.type !== 'string')) throw new RangeError('invalid runner feed');\n  const expected = courseFor(run.course?.seed, run.level);\n  if (!expected || run.course.seed !== expected.seed || run.course.level !== expected.level || run.course.length !== expected.length\n      || run.course.turnAt !== expected.turnAt || run.course.direction !== expected.direction\n      || run.course.events.length !== expected.events.length || run.events.length !== expected.events.length) throw new RangeError('runner course does not match its seed');\n  const opening = courseFor(courseSeed(state.seed, 0), 0);\n  if (state.openingLength !== opening.length || run.level !== run.courses) throw new RangeError('runner course does not match its seed');\n  let open = false;\n  for (let i = 0; i < run.events.length; i++) {\n    const event = run.events[i];\n    if (!sameEvent(event, expected.events[i]) || !sameEvent(run.course.events[i], expected.events[i]) || typeof event.cleared !== 'boolean') throw new RangeError('runner course does not match its seed');\n    if (event.cleared && open) throw new RangeError('cleared trap after an open trap');\n    if (!event.cleared) open = true;\n    if (state.status === 'running' && event.cleared && event.at > run.distance + 1e-6) throw new RangeError('cleared trap is still ahead of the runner');\n  }\n  if (state.status === 'running') {\n    if (run.courses !== 0 || run.course.seed !== courseSeed(state.seed, 0) || !(run.distance < run.course.length) || state.end != null) throw new RangeError('running room does not match an unfinished course');\n  } else if (run.courses !== 1 || run.distance !== 0 || run.paused !== true || run.course.seed !== courseSeed(state.seed, 1)\n      || !(run.total + 1e-6 >= state.openingLength) || state.end !== 'course-clear') throw new RangeError('cleared room does not match a finished course');\n  return state;\n}\n\nfunction clayPin(value, max, label) {\n  if (value == null) return 0;\n  if (!Number.isInteger(value) || value < 0 || value > max) throw new RangeError(`clay ${label} is outside the authored catalog`);\n  return value;\n}\n\nfunction worldRunRange(value, min, max, label) {\n  if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) throw new RangeError(`invalid world run ${label}`);\n}\n\nfunction flagMap(value, label) {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new RangeError(`invalid world run ${label}`);\n  const keys = Object.keys(value);\n  if (keys.length > 256) throw new RangeError(`invalid world run ${label}`);\n  for (const key of keys) if (value[key] !== true) throw new RangeError(`invalid world run ${label}`);\n}\n\nconst WORLD_RUN_KEYS = new Set(['world','leg','mirrored','time','arrivalSerial','arrivalTime','eventSerial','lastEvent','events','destroyed','distance','totalDistance','coins','score','unlocked','phase','lane','jump','jumpDuration','slide','powers','loop','stumbles','stumble','_wormholeElapsed','headstartUsed','elevation','collected','hazardsSeen','hazardSerial','lastHazard','lastPickup','jumpPeak','pickupSerial']);\n\nfunction validateClayWorld(state) {\n  if (!state || state.version !== 2 || state.roomId !== 'clay' || !ID.test(state.encounterId)) throw new TypeError('invalid clay room save');\n  assertSeed(state.seed);\n  if (state.volume != null) throw new TypeError('clay room has no volume');\n  if (state.grants != null || state.wallet != null || state.coins != null) throw new TypeError('clay room has no wallet');\n  if (state.engine !== CLAY_ENGINE_V2) throw new TypeError('invalid clay engine');\n  if (state.coach !== ROOM_BY_ID.clay.chasingCoach) throw new TypeError('invalid clay coach');\n  const world = clayPin(state.world, WORLDS.length - 1, 'world');\n  const leg = clayPin(state.leg, CLAY_LEGS - 1, 'leg');\n  const loop = clayPin(state.loop, CLAY_LOOPS - 1, 'loop');\n  if (state.status !== 'running' && state.status !== 'cleared') throw new RangeError('invalid clay room status');\n  if (typeof state.stale !== 'boolean') throw new TypeError('invalid stale flag');\n  if (!(state.lastNow === null || (typeof state.lastNow === 'number' && Number.isFinite(state.lastNow)))) throw new RangeError('invalid lastNow');\n  const run = state.run;\n  if (!run || typeof run !== 'object' || Array.isArray(run)) throw new TypeError('invalid world run');\n  if (run.seed != null) throw new TypeError('authored course is not seeded');\n  for (const key of Object.keys(run)) if (!WORLD_RUN_KEYS.has(key)) throw new TypeError(`invalid world run field: ${key}`);\n  if (run.world !== world || run.leg !== leg || run.loop !== loop) throw new RangeError('world run does not match its pin');\n  if (run.mirrored !== (leg === 1)) throw new RangeError('invalid mirrored leg');\n  if (run.phase !== 'run' && run.phase !== 'portal') throw new RangeError('clay room does not enter the wormhole');\n  if (!Number.isInteger(run.lane) || run.lane < -1 || run.lane > 1) throw new RangeError('invalid lane');\n  if (run.jumpDuration !== 1.4 && run.jumpDuration !== 2.2) throw new RangeError('invalid jump duration');\n  worldRunRange(run.jump, 0, run.jumpDuration, 'jump');\n  if (run.jumpPeak !== 1.7 && run.jumpPeak !== 3.5) throw new RangeError('invalid jump peak');\n  worldRunRange(run.slide, 0, 0.8, 'slide');\n  worldRunRange(run.stumble, 0, 0.9, 'stumble');\n  worldRunRange(run.time, 0, 1e6, 'time');\n  worldRunRange(run.distance, 0, 600, 'distance');\n  worldRunRange(run.totalDistance, 0, 1e6, 'total distance');\n  if (run.totalDistance + 1e-4 < run.distance) throw new RangeError('invalid total distance');\n  worldRunRange(run.score, 0, 1e9, 'score');\n  worldRunRange(run.elevation, 0, 3.2, 'elevation');\n  if (!Number.isInteger(run.coins) || run.coins < 0 || run.coins > 1e7) throw new RangeError('invalid coins');\n  if (!Number.isInteger(run.stumbles) || run.stumbles < 0 || run.stumbles > 1e6) throw new RangeError('invalid stumbles');\n  if (run.unlocked !== 0) throw new RangeError('invalid unlocked');\n  if (run.arrivalSerial !== 0 || run.arrivalTime !== 0 || run._wormholeElapsed !== 0) throw new RangeError('clay room does not enter the wormhole');\n  if (!Number.isInteger(run.eventSerial) || run.eventSerial < 0 || run.eventSerial > 1e7) throw new RangeError('invalid event serial');\n  if (!Number.isInteger(run.hazardSerial) || run.hazardSerial < 0 || run.hazardSerial > 1e7) throw new RangeError('invalid hazard serial');\n  if (run.pickupSerial != null && (!Number.isInteger(run.pickupSerial) || run.pickupSerial < 0 || run.pickupSerial > 1e7)) throw new RangeError('invalid pickup serial');\n  if (typeof run.headstartUsed !== 'boolean') throw new TypeError('invalid headstart flag');\n  if (run.lastPickup != null && !Object.hasOwn(POWERS, run.lastPickup)) throw new RangeError('invalid pickup');\n  if (run.lastEvent != null && (typeof run.lastEvent !== 'object' || Array.isArray(run.lastEvent))) throw new RangeError('invalid last event');\n  if (!Array.isArray(run.events) || run.events.length > 32) throw new RangeError('invalid events');\n  for (const event of run.events) if (!event || typeof event !== 'object' || typeof event.type !== 'string') throw new RangeError('invalid events');\n  if (!run.powers || typeof run.powers !== 'object' || Array.isArray(run.powers)) throw new RangeError('invalid powers');\n  for (const [id, left] of Object.entries(run.powers)) {\n    const spec = POWERS[id];\n    if (!spec || spec.instant || typeof left !== 'number' || !Number.isFinite(left) || left <= 0 || left > spec.duration + 1e-9) throw new RangeError('invalid powers');\n  }\n  flagMap(run.collected, 'collected');\n  flagMap(run.destroyed, 'destroyed');\n  flagMap(run.hazardsSeen, 'hazards seen');\n  if (run.lastHazard != null && (typeof run.lastHazard !== 'object' || Array.isArray(run.lastHazard))) throw new RangeError('invalid hazard');\n  if (state.status === 'running') {\n    if (run.phase !== 'run' || !(run.distance < 600) || state.end != null) throw new RangeError('running room is not on the authored leg');\n  } else if (run.phase !== 'portal' || run.distance !== 600 || state.end !== 'portal') throw new RangeError('cleared room is not at the portal');\n  return state;\n}\n\nfunction validateJelly(state) {\n  if (!state || state.version !== 1 || state.roomId !== 'jelly' || !ID.test(state.encounterId)) throw new TypeError('invalid jelly room save');\n  assertSeed(state.seed);\n  if (state.status !== 'running' && state.status !== 'cleared' && state.status !== 'dead') throw new RangeError('invalid jelly room status');\n  if (typeof state.stale !== 'boolean') throw new TypeError('invalid stale flag');\n  if (!(state.lastNow === null || (typeof state.lastNow === 'number' && Number.isFinite(state.lastNow)))) throw new RangeError('invalid lastNow');\n  if (state.engine !== 'arcade/cover/cover-sim.mjs') throw new TypeError('invalid jelly engine');\n  const catalog = coverVolume(state.volume);\n  if (!sameCatalog(state.levels, catalog)) throw new RangeError('jelly catalog does not match the shipped volume');\n  const sim = restoreCoverSim(state.sim, {levels: volumeLevels(state.volume)});\n  const phase = sim.s.phase;\n  if (state.status === 'cleared') {\n    if (phase !== 'portal' || state.end !== 'portal' || sim.s.area !== COVER_SIM.areas) throw new RangeError('cleared jelly room is not at the portal');\n  } else if (state.status === 'dead') {\n    if (phase !== 'dead' || state.end != null) throw new RangeError('dead jelly room does not match the sim');\n  } else if (!['intro', 'travel', 'fight'].includes(phase) || state.end != null) throw new RangeError('running jelly room does not match the sim');\n  state.sim = sim.snapshot();\n  return state;\n}\n\nfunction pack(state) {\n  const copy = JSON.parse(JSON.stringify(state));\n  if (copy?.roomId === 'clay' && copy.version === 2) return validateClayWorld(copy);\n  if (copy?.roomId === 'clay') return validateClay(copy);\n  if (copy?.roomId === 'jelly') return validateJelly(copy);\n  throw new TypeError('invalid game room save');\n}\n\nexport function checkpointGameRoom(state) {\n  return pack(state);\n}\nexport const checkpoint = checkpointGameRoom;\n\nexport function restoreGameRoom(saved) {\n  return pack(saved);\n}\n\nexport function createGameRoom({roomId, encounterId, seed, volume = null, world = null, leg = null, loop = null} = {}) {\n  if (typeof encounterId !== 'string' || !ID.test(encounterId)) throw new TypeError('encounterId must be a stable id');\n  if (!GAME_ROOM_IDS.includes(roomId)) throw new TypeError(`unknown game room: ${roomId}`);\n  if (roomId === 'jelly') {\n    if (ROOM_BY_ID.jelly?.roomType !== 'crisis_combat') throw new Error('jelly room catalog mismatch');\n    assertSeed(seed);\n    const levels = volumeLevels(volume);\n    const sim = createCoverSim({levels, seed});\n    return pack({\n      version: 1, roomId, encounterId, seed, volume, status: 'running', stale: false, lastNow: null, end: null,\n      engine: 'arcade/cover/cover-sim.mjs', levels: coverVolume(volume), sim: sim.snapshot(),\n    });\n  }\n  if (roomId === 'dance') {\n    if (ROOM_BY_ID.dance?.roomType !== 'pixel_laser') throw new Error('dance room catalog mismatch');\n    throw new GameRoomLimit('dance', DANCE_ADAPTER_SEAM);\n  }\n  if (ROOM_BY_ID.clay?.roomType !== 'shrink_burpees_maze') throw new Error('clay room catalog mismatch');\n  if (volume != null) throw new TypeError('clay room has no volume');\n  assertSeed(seed);\n  const worldPin = clayPin(world, WORLDS.length - 1, 'world');\n  const legPin = clayPin(leg, CLAY_LEGS - 1, 'leg');\n  const loopPin = clayPin(loop, CLAY_LOOPS - 1, 'loop');\n  const run = createWorldRun();\n  run.world = worldPin;\n  run.leg = legPin;\n  run.loop = loopPin;\n  run.mirrored = legPin === 1;\n  return pack({\n    version: 2, roomId, encounterId, seed, volume: null, world: worldPin, leg: legPin, loop: loopPin,\n    status: 'running', stale: false, lastNow: null, end: null, coach: ROOM_BY_ID.clay.chasingCoach,\n    engine: CLAY_ENGINE_V2, run,\n  });\n}\n\nfunction readCover(frame) {\n  if (!frame || typeof frame !== 'object') throw new TypeError('step frame must be an object');\n  if (typeof frame.now !== 'number' || !Number.isFinite(frame.now) || frame.now < 0 || frame.now > 1e15) throw new RangeError('now must be a finite millisecond clock');\n  if (typeof frame.tracked !== 'boolean' || typeof frame.paused !== 'boolean') throw new TypeError('tracked and paused must be booleans');\n  if (frame.action != null || frame.actions != null) throw new TypeError('jelly does not take runner actions');\n  const cover = {};\n  if (frame.hidden != null || frame.side != null) {\n    if (frame.hidden != null && typeof frame.hidden !== 'boolean') throw new TypeError('hidden must be a boolean');\n    if (frame.side != null && frame.side !== -1 && frame.side !== 0 && frame.side !== 1) throw new RangeError('side must be -1, 0, or 1');\n    cover.input = {side: frame.side ?? 0, hidden: !!frame.hidden};\n  }\n  if (frame.beam != null) {\n    if (typeof frame.beam !== 'object' || typeof frame.beam.on !== 'boolean' || typeof frame.beam.x !== 'number' || !Number.isFinite(frame.beam.x) || frame.beam.x < -1 || frame.beam.x > 1) throw new RangeError('beam must be {on, x in [-1,1]}');\n    cover.beam = {on: frame.beam.on, x: frame.beam.x};\n  }\n  if (frame.zones != null) {\n    if (typeof frame.zones !== 'object') throw new TypeError('zones must be an object');\n    const zones = {};\n    for (const key of ['left', 'middle', 'right', 'up']) {\n      const value = frame.zones[key] ?? 0;\n      if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1) throw new RangeError('zone intensity must be in [0,1]');\n      zones[key] = value;\n    }\n    cover.zones = zones;\n  }\n  if (frame.fire != null && frame.fires != null) throw new TypeError('pass fire or fires, not both');\n  const readFire=fire=>{\n    if (!fire || typeof fire !== 'object' || Array.isArray(fire)) throw new TypeError('fire must be {aimX, power}');\n    const aim = fire.aimX;\n    if (!(aim === null || (typeof aim === 'number' && Number.isFinite(aim) && aim >= -1 && aim <= 1))) throw new RangeError('aimX must be null or in [-1,1]');\n    if (typeof fire.power !== 'number' || !Number.isFinite(fire.power) || fire.power < 0 || fire.power > 100) throw new RangeError('fire power must be in [0,100]');\n    return {aimX: aim, power: fire.power};\n  };\n  if (frame.fire != null) cover.fires=[readFire(frame.fire)];\n  if (frame.fires != null) {\n    if (!Array.isArray(frame.fires) || frame.fires.length>2) throw new RangeError('fires must contain at most two {aimX, power} samples');\n    cover.fires=frame.fires.map(readFire);\n  }\n  return cover;\n}\n\nfunction stepJelly(state, frame) {\n  const cover = readCover(frame);\n  const next = restoreGameRoom(state);\n  if (next.status !== 'running') return next;\n  if (next.lastNow === null) {\n    next.lastNow = frame.now;\n    return next;\n  }\n  if (frame.now < next.lastNow) throw new RangeError('game room clock moved backwards');\n  if (frame.now === next.lastNow) return next;\n  const dt = (frame.now - next.lastNow) / 1000;\n  const hole = frame.tracked !== true || dt > STALE_SECONDS;\n  next.lastNow = frame.now;\n  next.stale = hole;\n  if (frame.paused || hole) return next;\n  next.stale = false;\n  const sim = restoreCoverSim(next.sim, {levels: volumeLevels(next.volume)});\n  if (cover.input) sim.setInput(cover.input);\n  if (cover.beam) sim.setBeam(cover.beam);\n  if (cover.zones) sim.setZones(cover.zones);\n  for(const fire of cover.fires??[])sim.fire(fire.aimX,fire.power);\n  sim.step(dt);\n  next.sim = sim.snapshot();\n  if (sim.s.phase === 'portal') {\n    next.status = 'cleared';\n    next.end = 'portal';\n  } else if (sim.s.phase === 'dead') {\n    next.status = 'dead';\n    next.end = null;\n  }\n  return pack(next);\n}\n\nfunction stepClock(next, frame) {\n  if (next.lastNow === null) {\n    next.lastNow = frame.now;\n    return {stop: true};\n  }\n  if (frame.now < next.lastNow) throw new RangeError('game room clock moved backwards');\n  if (frame.now === next.lastNow) return {stop: true};\n  const dt = (frame.now - next.lastNow) / 1000;\n  const hole = frame.tracked !== true || dt > STALE_SECONDS;\n  next.lastNow = frame.now;\n  next.stale = hole;\n  return {stop: false, dt, hole};\n}\n\nfunction stepClayV1(state, frame) {\n  const actions = readActions(frame);\n  const next = restoreGameRoom(state);\n  if (next.status !== 'running') return next;\n  const clock = stepClock(next, frame);\n  if (clock.stop || frame.paused || clock.hole) {\n    if (!clock.stop) next.run.paused = true;\n    return next;\n  }\n  next.run.paused = false;\n  for (const action of actions) input(next.run, action);\n  const courses = next.run.courses;\n  step(next.run, clock.dt);\n  if (next.run.courses > courses) {\n    next.status = 'cleared';\n    next.end = 'course-clear';\n    next.run.paused = true;\n  }\n  return pack(next);\n}\n\nfunction stepClayV2(state, frame) {\n  const actions = readActions(frame);\n  const next = restoreGameRoom(state);\n  if (next.status !== 'running') return next;\n  const clock = stepClock(next, frame);\n  if (clock.stop || frame.paused || clock.hole) return next;\n  next.stale = false;\n  for (const action of actions) {\n    if (action === 'jump' && next.run.phase === 'portal') continue;\n    act(next.run, action);\n  }\n  advance(next.run, clock.dt);\n  if (next.run.phase === 'wormhole' || next.run.world !== next.world || next.run.leg !== next.leg || next.run.loop !== next.loop) {\n    throw new RangeError('clay room does not enter the wormhole');\n  }\n  if (next.run.phase === 'portal') {\n    next.status = 'cleared';\n    next.end = 'portal';\n  }\n  return pack(next);\n}\n\nexport function stepGameRoom(state, frame) {\n  if (state?.roomId === 'jelly') return stepJelly(state, frame);\n  if (state?.roomId === 'clay' && state.version === 2) return stepClayV2(state, frame);\n  if (state?.roomId === 'clay' && state.version === 1) return stepClayV1(state, frame);\n  throw new TypeError('invalid game room save');\n}\n","modules/roguelite/grass-game.mjs":"// Reuse Plank Flight's course, collision detection and movement simulation.\n// Checkpoints retain the live simulation and RNG cursor instead of replaying a frame tape.\nimport {createFlight,advanceFlight,commandFlight,snapshotFlightCheckpoint,restoreFlightCheckpoint} from '../../arcade/plank-flight/sim.mjs';\nimport {createCoinChaseState,restoreCoinChaseState,advanceCoinChase,hitCoinChaseObstacle,collectCoinChaseCoin} from './coin-chase-state.mjs';\n\nconst coinPool=()=>Array.from({length:120},(_,i)=>({id:`coin-${i}`,amount:1}));\n\nexport function createGrassGame({encounterId,seed=1,catchWindows=2,saved=null}={}){\n if(typeof encounterId!=='string'||!/^[A-Za-z0-9:_-]{1,96}$/.test(encounterId)||!Number.isSafeInteger(seed)||seed<0)throw new TypeError('Grass room needs a stable encounter and seed');\n let chase,flight,coinIndex=0;\n if(saved?.version===2){\n  if(saved.encounterId!==encounterId||saved.seed!==seed||saved.catchWindows!==catchWindows||!Number.isSafeInteger(saved.coinIndex)||saved.coinIndex<0||saved.coinIndex>120)\n   throw new TypeError('Invalid saved grass room');\n  flight=restoreFlightCheckpoint(saved.flight);chase=restoreCoinChaseState(saved.chase);coinIndex=saved.coinIndex;\n  if(flight.runId!==encounterId||flight.seed!==((seed>>>0)||1)||flight.coinChase!==true||flight.paced!==true||flight.biomes!==true||chase.encounterId!==encounterId||chase.seed!==seed||chase.catchWindows!==catchWindows)\n   throw new TypeError('Saved grass simulation does not match this encounter');\n }else{\n  if(saved!==null&&saved!==undefined&&saved.version!==1)throw new TypeError('Invalid saved grass room');\n  chase=createCoinChaseState({encounterId,seed,catchWindows,coinPool:coinPool()});\n  flight=createFlight({runId:encounterId,seed,coinChase:true,paced:true,biomes:true});commandFlight(flight,'start');\n  if(saved){\n   if(saved.encounterId!==encounterId||saved.seed!==seed||saved.catchWindows!==catchWindows||!Array.isArray(saved.frames)||saved.frames.length>1000)throw new TypeError('Invalid saved grass room');\n   for(const frame of saved.frames){\n    if(chase.status!=='running'||!Number.isFinite(frame.dt)||frame.dt<=0||frame.dt>.1)throw new TypeError('Invalid saved grass frame');\n    advance(frame.dt,{...frame,valid:true});\n   }\n  }\n }\n function advance(dt,{valid=false,height01=.5,steer=0,paused=false}={}){\n  if(!Number.isFinite(dt)||dt<0||!Number.isFinite(height01)||height01<0||height01>1||!Number.isFinite(steer)||Math.abs(steer)>1||typeof valid!=='boolean'||typeof paused!=='boolean')throw new TypeError('Invalid grass observation');\n  if(paused||!valid||!dt||chase.status!=='running')return;\n  const frame={dt:Math.min(.1,dt),height01,steer};\n  advanceFlight(flight,frame.dt,{valid:true,height01,steer});\n  for(const event of flight.events.splice(0)){\n   if(event.type==='hit')chase=hitCoinChaseObstacle(chase,event.entityId).state;\n   if(event.type==='coin')for(let i=0;i<event.amount&&coinIndex<120;i++)chase=collectCoinChaseCoin(chase,`coin-${coinIndex++}`).state;\n  }\n  chase=advanceCoinChase(chase,frame.dt).state;\n }\n return {flight,advance,get chase(){return structuredClone(chase);},snapshot:()=>({version:2,encounterId,seed,catchWindows,coinIndex,flight:snapshotFlightCheckpoint(flight),chase:structuredClone(chase)})};\n}\n","modules/roguelite/grass-view.mjs":"import {mountFlightView} from '../../arcade/plank-flight/view.mjs';\r\nimport {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\nimport {createViewTour,cameraOverview,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\n/** Rendering only: the parent still owns camera, game clock, rewards and exits. */\r\nexport function mountGrassView(host,game,{onError=()=>{},reducedMotion=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches??false}={}){\r\n const root=document.createElement('section');root.className='run-grass-game plank-flight-host';host.append(root);\r\n const tour=createViewTour((progress,camera)=>cameraOverview(camera,progress,{wide:1,height:1.1,forward:-3}));\r\n const motion=createAcknowledgedMotion();let lastIdentity=null,lastClock=null;\r\n const view=mountFlightView({host:root,run:game.flight,beforeRender:(_scene,camera)=>tour.apply(camera),renderBudget:{maxWidth:640,maxHeight:360},onAction:name=>{if(name==='render-error'){tour.cancel();onError(Error('Flight graphics stopped. Your chase is paused.'));}}});\r\n const coach=FOUR_LEGGED_SOURCES.find(unit=>unit.sourceCoachId==='roster/18-quad-all--robotic_dog_3d_model');\r\n const canvas=document.createElement('canvas');canvas.className='run-chased-coach';root.append(canvas);\r\n const image=new Image();let disposed=false;image.src=coach.atlasPath;\r\n void image.decode().then(()=>{if(disposed)return;canvas.width=coach.frame[2];canvas.height=coach.frame[3];canvas.getContext('2d').drawImage(image,...coach.frame,0,0,canvas.width,canvas.height);}).catch(onError);\r\n return {element:root,ready:view.ready,intro:options=>tour.intro({reducedMotion,...options}),render({pose,paused,message,dt=0,identity}){\r\n  if(disposed)return;\r\n  const flight=game.flight,chase=game.chase,key=`${identity??chase.encounterId??''}:${flight.seed}:${flight.phase}:${flight.biome?.id}`;\r\n  if(lastIdentity!==null&&(key!==lastIdentity||flight.time<lastClock))tour.cancel();\r\n  lastIdentity=key;lastClock=flight.time;\r\n  const fields=Object.fromEntries(['distance','time','x','y','speed'].filter(k=>Number.isFinite(flight[k])).map(k=>[k,flight[k]]));\r\n  fields.gap=chase.gap;\r\n  motion.push(fields,{identity:key,clock:flight.time});\r\n  const values=motion.sample(dt,{paused,reducedMotion});\r\n  view.render({pose,paused:paused||tour.active,message,profile:{},coinsBanked:0,pendingCoins:0,presentationRun:{...flight,...values}});\r\n  canvas.style.transform=`translate(-50%,-50%) translateX(${values.x*40}px) scale(${.8+(1-values.gap)*.6})`;\r\n  canvas.dataset.slowed=String(chase.slowRemaining>0);\r\n },dispose(){if(disposed)return;disposed=true;tour.dispose();const released=view.stop();root.remove();return released;}};\r\n}\r\n","modules/roguelite/hand-input.mjs":"import {handShape} from '../../exercise-gestures.mjs';\r\n\r\n// Conservative interaction classes, not an anatomical/depth measurement. Handedness scores are never presence scores.\r\nexport function classifyHand(points,world,aspect=1){\r\n const shape=handShape(points,aspect);if(!shape)return 'other';\r\n const d=(a,b)=>Math.hypot((a.x-b.x)*aspect,a.y-b.y);\r\n const extended=[8,12,16,20].filter(tip=>d(points[tip],points[0])>d(points[tip-2],points[0])*1.25).length;\r\n if(shape.fist)return 'fist';\r\n const scale=d(points[0],points[9]);\r\n const pinch=world?.length===21?Math.hypot(world[4].x-world[8].x,world[4].y-world[8].y,world[4].z-world[8].z)<.025:d(points[4],points[8])<scale*.33;\r\n // A compact fist or edge-on curled hand must never buy an item. Thresholds need recorded phone traces.\r\n if(pinch&&extended>=2)return 'pinch';\r\n return extended>=3?'open':'other';\r\n}\r\n\r\nexport function selectActiveHand(hands,previous=null){\r\n const valid=hands.map((points,index)=>({points,index})).filter(({points})=>points?.length===21&&points.every(p=>Number.isFinite(p.x)&&Number.isFinite(p.y)));\r\n if(previous)return valid.sort((a,b)=>Math.hypot(a.points[0].x-previous.x,a.points[0].y-previous.y)-Math.hypot(b.points[0].x-previous.x,b.points[0].y-previous.y))[0]||null;\r\n const interior=valid.filter(({points})=>points.every(p=>p.x>.01&&p.x<.99&&p.y>.01&&p.y<.99));\r\n const area=({points})=>(Math.max(...points.map(p=>p.x))-Math.min(...points.map(p=>p.x)))*(Math.max(...points.map(p=>p.y))-Math.min(...points.map(p=>p.y)));\r\n return interior.sort((a,b)=>area(b)-area(a))[0]||null;\r\n}\r\n","modules/roguelite/material-opening-state.mjs":"const DURATION={jelly:900,grass:700};\nexport function createMaterialOpening(material){\n if(!Object.hasOwn(DURATION,material))throw new TypeError('Unknown entrance material');\n return {v:1,material,stage:'waiting',lastObservationId:-1,lastNow:null,released:false,dwellMs:0,ceremonyMs:0};\n}\n/** A neutral pose arms each entrance; only tracked active observations advance it. */\nexport function observeMaterialOpening(state,{observationId,now,tracked,raised=false,push=false,paused=false}={}){\n if(!state||state.v!==1||!Object.hasOwn(DURATION,state.material)||!['waiting','charging','ceremony','complete'].includes(state.stage)\n  ||!Number.isSafeInteger(state.lastObservationId)||state.lastObservationId< -1||!(state.lastNow===null||Number.isFinite(state.lastNow))\n  ||typeof state.released!=='boolean'||!Number.isFinite(state.dwellMs)||state.dwellMs<0||state.dwellMs>400\n  ||!Number.isFinite(state.ceremonyMs)||state.ceremonyMs<0||state.ceremonyMs>DURATION[state.material])throw new TypeError('Invalid material opening state');\n if(!Number.isSafeInteger(observationId)||observationId<0||!Number.isFinite(now)||[tracked,raised,push,paused].some(v=>typeof v!=='boolean'))throw new TypeError('Invalid material opening observation');\n const next=structuredClone(state),events=[];\n if(observationId<=state.lastObservationId)return {state:next,events,duplicate:true};\n if(state.lastNow!==null&&now<state.lastNow)throw new RangeError('Opening time cannot move backward');\n const gap=state.lastNow===null?0:now-state.lastNow,dt=Math.min(100,gap);\n next.lastObservationId=observationId;next.lastNow=now;\n if(state.stage==='complete')return {state:next,events,duplicate:false};\n if(paused||!tracked||gap>300){\n  next.released=false;next.dwellMs=0;if(next.stage==='charging')next.stage='waiting';\n  return {state:next,events,duplicate:false};\n }\n if(next.stage==='ceremony'){\n  next.ceremonyMs=Math.min(DURATION[next.material],next.ceremonyMs+dt);\n  if(next.ceremonyMs===DURATION[next.material]){next.stage='complete';events.push({type:'opening-complete',material:next.material});}\n }else{\n  const gesture=next.material==='jelly'?raised:push;\n  if(!gesture){next.released=true;next.dwellMs=0;next.stage='waiting';}\n  else if(next.released){\n   next.stage='charging';next.dwellMs=Math.min(400,next.dwellMs+dt);\n   if(next.dwellMs===400){next.stage='ceremony';next.ceremonyMs=0;events.push({type:'ceremony-started',material:next.material});}\n  }\n }\n return {state:next,events,duplicate:false};\n}\n","modules/roguelite/material-opening.mjs":"import {createMaterialOpening,observeMaterialOpening} from './material-opening-state.mjs';\nimport {physicalSound} from '../../audio/physical-sound.mjs';\nimport {classifyHand} from './hand-input.mjs';\n\nconst visible=p=>p&&Number.isFinite(p.x)&&p.x>=0&&p.x<=1&&Number.isFinite(p.y)&&p.y>=0&&p.y<=1&&Number.isFinite(p.visibility)&&p.visibility>=.5;\nfunction extended(s,e,w,aspect){\n const a=[(s.x-e.x)*aspect,s.y-e.y,((s.z??0)-(e.z??0))*aspect],b=[(w.x-e.x)*aspect,w.y-e.y,((w.z??0)-(e.z??0))*aspect];\n const length=Math.hypot(...a)*Math.hypot(...b);return length>0&&a.reduce((sum,n,i)=>sum+n*b[i],0)/length<-.82;\n}\n/** Pose-only entrance gestures: punches and fist classification never open these doors. */\nexport function detectMaterialOpeningGesture(points,tracked=true,{hands=[],worldHands=[],aspect=1}={}){\n const valid=tracked&&Number.isFinite(aspect)&&aspect>0&&[11,12,13,14,15,16].every(i=>visible(points?.[i]));\n if(!valid)return {tracked:false,raised:false,push:false};\n const raised=points[15].y<points[11].y-.1&&points[16].y<points[12].y-.1;\n const forward=(s,e,w)=>extended(points[s],points[e],points[w],aspect)&&Math.abs(points[w].y-points[s].y)<.3\n  &&Number.isFinite(points[w].z)&&Number.isFinite(points[s].z)&&points[w].z<points[s].z-.12;\n const palms=hands.length>=2&&hands.filter((hand,i)=>classifyHand(hand,worldHands[i],aspect)==='open').length>=2;\n return {tracked:true,raised,push:!raised&&palms&&forward(11,13,15)&&forward(12,14,16)};\n}\n\n/** Existing poster art, one tracked ceremony clock, no extra graphics context. */\nexport function mountMaterialOpening(host,{material,reducedMotion=false,onComplete=()=>{}}={}){\n let state=createMaterialOpening(material),sequence=0,disposed=false,held=false,completed=false;\n const doc=host.ownerDocument??document,element=doc.createElement('section');\n element.className='material-opening';element.dataset.material=material;element.classList.toggle('reduced',reducedMotion);\n element.setAttribute('aria-label',`${material==='jelly'?'Jelly':'Grass'} portal entrance`);\n const art=doc.createElement('div');art.className='material-opening-art';\n for(const side of ['left','right']){const leaf=doc.createElement('div');leaf.className=`material-opening-leaf ${side}`;art.append(leaf);}\n const veil=doc.createElement('div');veil.className='material-opening-veil';\n const title=doc.createElement('h2'),instruction=doc.createElement('p');instruction.setAttribute('role','status');instruction.setAttribute('aria-live','polite');\n element.append(art,veil,title,instruction);host.append(element);\n function paint(){\n  element.dataset.stage=state.stage;element.dataset.paused=String(held);\n  const duration=material==='jelly'?900:700;const progress=state.stage==='complete'?1:state.ceremonyMs/duration;\n  element.style.setProperty('--portal-progress',String(progress));element.style.setProperty('--portal-hold',String(state.dwellMs/400));\n  title.textContent=state.stage==='ceremony'?(material==='jelly'?'THE JELLY PULLS YOU IN':'THE GRASS PARTS'):state.stage==='complete'?'THE WAY IS OPEN':material==='jelly'?'RAISE BOTH ARMS':'PUSH THE GRASS';\n  instruction.textContent=held?'Entrance paused. Step into view to continue.':state.stage==='ceremony'?(material==='jelly'?'Let it carry you through.':'The path opens ahead.'):state.stage==='complete'?'Entering…':!state.released?(material==='jelly'?'Lower both arms first.':'Bring both hands back toward your chest first.'):material==='jelly'?'Raise both wrists above your shoulders and hold.':'Extend both open palms forward and hold to push it over.';\n }\n function observe(input,{paused=false}={}){\n  if(disposed)return;\n  const result=observeMaterialOpening(state,{...input,observationId:sequence++,paused:held||paused});state=result.state;paint();\n  for(const event of result.events){\n   if(event.type==='ceremony-started')physicalSound.play(material==='jelly'?'jelly':'grass');\n   if(event.type==='opening-complete'&&!completed){completed=true;onComplete();}\n  }\n }\n function pause(){if(disposed||held)return;held=true;observe({now:state.lastNow??0,tracked:false});paint();}\n function resume(){if(disposed||!held)return;held=false;paint();}\n paint();\n return {element,ready:Promise.resolve(),observe,render(){if(!disposed)paint();},pause,resume,\n  dispose(){if(disposed)return;disposed=true;element.remove();},get state(){return structuredClone(state);},get disposed(){return disposed;}};\n}\n","modules/roguelite/pond-room.mjs":"// Pure reducer for the pond's opening gesture, healing time, and wake meter.\n// Tuning below is a proposal; this module never applies health, XP, or rewards.\nimport { buildScript, phaseAt } from '../../breathing-modes.mjs';\n\nexport const POND_TUNING_PROPOSAL = Object.freeze({\n  openingHandMs: 1000,\n  healingMs: 20000,\n  healingAmount: 20,\n  wakeThresholdSeconds: 4,\n  wakePerMovementSecond: 1,\n  quietWakeDecayPerSecond: 1,\n  maxFrameSeconds: 0.1,\n});\nconst BREATH_SCRIPT = buildScript('tai-chi');\nconst clone = value => ({ ...value });\n\nfunction validateState(state) {\n  if (!state || state.version !== 1 || typeof state.encounterId !== 'string' || !/^[A-Za-z0-9:_-]{1,96}$/.test(state.encounterId) ||\n      !['opening', 'healing', 'complete', 'awoken'].includes(state.status) ||\n      !Number.isSafeInteger(state.lastObservationId) || state.lastObservationId < -1 ||\n      !(state.lastNow === null || Number.isFinite(state.lastNow)) ||\n      !Number.isFinite(state.openingHandMs) || state.openingHandMs < 0 || state.openingHandMs > POND_TUNING_PROPOSAL.openingHandMs ||\n      !Number.isFinite(state.healingMs) || state.healingMs < 0 || state.healingMs > POND_TUNING_PROPOSAL.healingMs ||\n      !Number.isFinite(state.wakeMeter) || state.wakeMeter < 0 || state.wakeMeter > POND_TUNING_PROPOSAL.wakeThresholdSeconds) {\n    throw new TypeError('Invalid pond room state');\n  }\n  if ((state.status === 'opening' && (state.healingMs !== 0 || state.wakeMeter !== 0 || state.openingHandMs >= POND_TUNING_PROPOSAL.openingHandMs)) ||\n      (state.status === 'healing' && (state.openingHandMs !== POND_TUNING_PROPOSAL.openingHandMs || state.healingMs >= POND_TUNING_PROPOSAL.healingMs || state.wakeMeter >= POND_TUNING_PROPOSAL.wakeThresholdSeconds)) ||\n      (state.status === 'complete' && (state.healingMs !== POND_TUNING_PROPOSAL.healingMs || state.openingHandMs !== POND_TUNING_PROPOSAL.openingHandMs || state.wakeMeter >= POND_TUNING_PROPOSAL.wakeThresholdSeconds)) ||\n      (state.status === 'awoken' && (state.wakeMeter < POND_TUNING_PROPOSAL.wakeThresholdSeconds || state.openingHandMs !== POND_TUNING_PROPOSAL.openingHandMs || state.healingMs >= POND_TUNING_PROPOSAL.healingMs))) {\n    throw new TypeError('Inconsistent pond room state');\n  }\n}\n\nexport function createPondRoom({ encounterId } = {}) {\n  if (typeof encounterId !== 'string' || !/^[A-Za-z0-9:_-]{1,96}$/.test(encounterId)) throw new TypeError('encounterId is required');\n  return {\n    version: 1, encounterId, status: 'opening', lastObservationId: -1, lastNow: null,\n    openingHandMs: 0, healingMs: 0, wakeMeter: 0,\n  };\n}\n\n// Clock origins are transient across reloads. Preserve earned progress and the\n// observation high-water mark, but require fresh observations to begin timing.\nexport function restorePondRoom(saved) {\n  validateState(saved);\n  return { ...saved, lastNow: null, openingHandMs:saved.status==='opening'?0:saved.openingHandMs };\n}\n\nexport function pondBreath(state) {\n  validateState(state);\n  const phase = phaseAt(BREATH_SCRIPT, state.healingMs);\n  return {\n    breath: phase.breath,\n    stanceId: phase.stanceId ?? null,\n    label: phase.breath === 'in' ? 'Inhale gently' : phase.breath === 'out' ? 'Exhale gently' : 'Breathe normally',\n  };\n}\n\nexport function observePondRoom(state, input = {}) {\n  validateState(state);\n  const { observationId, now, tracked, balanced, handOut, paused = false } = input;\n  if (!Number.isSafeInteger(observationId) || observationId < 0) throw new TypeError('observationId must be a nonnegative safe integer');\n  if (!Number.isFinite(now)) throw new TypeError('now must be finite');\n  if ([tracked, balanced, handOut, paused].some(value => typeof value !== 'boolean')) throw new TypeError('tracked, balanced, handOut, and paused must be booleans');\n  if (observationId <= state.lastObservationId) return { state, accepted: false, events: [] };\n\n  const next = clone(state), events = [];\n  next.lastObservationId = observationId;\n  if (next.lastNow !== null && now < next.lastNow) {\n    next.lastNow = null;\n    if (next.status === 'opening') next.openingHandMs = 0;\n    return { state: next, accepted: false, events };\n  }\n  const dt = next.lastNow === null ? 0 : Math.min((now - next.lastNow) / 1000, POND_TUNING_PROPOSAL.maxFrameSeconds);\n  next.lastNow = now;\n  if (!tracked || paused || next.status === 'complete' || next.status === 'awoken') {\n    if (next.status === 'opening') next.openingHandMs = 0;\n    return { state: next, accepted: true, events };\n  }\n\n  if (next.status === 'opening') {\n    if (!handOut) {\n      next.openingHandMs = 0;\n      return { state: next, accepted: true, events };\n    }\n    next.openingHandMs += dt * 1000;\n    if (next.openingHandMs >= POND_TUNING_PROPOSAL.openingHandMs) {\n      next.status = 'healing';\n      next.openingHandMs = POND_TUNING_PROPOSAL.openingHandMs;\n      events.push({ type: 'fish-nibble', encounterId: next.encounterId });\n    }\n    return { state: next, accepted: true, events };\n  }\n\n  if (balanced) {\n    next.healingMs = Math.min(POND_TUNING_PROPOSAL.healingMs, next.healingMs + dt * 1000);\n    next.wakeMeter = Math.max(0, next.wakeMeter - dt * POND_TUNING_PROPOSAL.quietWakeDecayPerSecond);\n  } else {\n    next.wakeMeter = Math.min(POND_TUNING_PROPOSAL.wakeThresholdSeconds,\n      next.wakeMeter + dt * POND_TUNING_PROPOSAL.wakePerMovementSecond);\n  }\n  if (next.wakeMeter >= POND_TUNING_PROPOSAL.wakeThresholdSeconds) {\n    next.status = 'awoken';\n    events.push({ type: 'monster-awoken', encounterId: next.encounterId });\n  } else if (next.healingMs >= POND_TUNING_PROPOSAL.healingMs) {\n    next.status = 'complete';\n    events.push({ type: 'healing-complete', encounterId: next.encounterId, amount: POND_TUNING_PROPOSAL.healingAmount });\n  }\n  return { state: next, accepted: true, events };\n}\n","modules/roguelite/pond-view.mjs":"import {FOUR_LEGGED_SOURCES} from '../coach-world/unit-catalog.mjs';\r\nimport {auraFrames,drawAura2d} from '../coach-world-rts/aura-sprites.mjs';\r\nimport {makeFish,makeTrail,rng,stepFish,fishTouch,fishRelease,POND} from '../portal/portal-board-pond.mjs';\r\nimport {physicalSound} from '../../audio/physical-sound.mjs';\r\nimport {pondBreath,POND_TUNING_PROPOSAL} from './pond-room.mjs';\r\nimport {createViewTour,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\n// The released meditation room is layered pixel art. Reuse it without borrowing\r\n// the global companion, creating account tickets, or allocating another WebGL context.\r\nexport function mountPondView(host,{onError=()=>{},reducedMotion=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches??false}={}){\r\n const root=document.createElement('section');root.className='run-pond-room';root.setAttribute('aria-label','Healing waterfall and sleeping coach');\r\n const canvas=document.createElement('canvas');root.append(canvas);host.append(root);\r\n const ctx=canvas.getContext('2d'),coach=FOUR_LEGGED_SOURCES.find(unit=>unit.sourceCoachId==='roster/18-quad-all--robotic_dog_3d_model');\r\n const tour=createViewTour(progress=>{\r\n  const u=progress*progress*(3-2*progress);\r\n  canvas.style.transformOrigin='50% 40%';canvas.style.transform=`scale(${.86+.14*u}) translateY(${5*(1-u)}%)`;\r\n },()=>{canvas.style.transform='';canvas.style.transformOrigin='';});\r\n const motion=createAcknowledgedMotion();\r\n let identity=null,lastClock=null;\r\n const backdrop=new Image(),atlas=new Image();backdrop.src='/pod/worlds/meditation-waterfall.png';atlas.src=coach.atlasPath;\r\n let disposed=false,loaded=false,auras=[],clock=0,lastStatus='opening',soundScene=null;\r\n const random=rng(21),fish={fish:makeFish(4,POND.aspect,random),A:POND.aspect,rand:random,touch:null,lastTouch:0,trail:makeTrail(),members:0};\r\n const ready=Promise.all([backdrop.decode(),atlas.decode(),auraFrames('protectioncircle')]).then(([, ,frames])=>{if(disposed)return;auras=frames;root.dataset.auraFrames=String(frames.length);loaded=true;}).catch(error=>{if(!disposed)onError(error);throw error;});\r\n ready.catch(()=>{}); // A disposed/stale owner may stop observing readiness.\r\n function cover(image,alpha=1){const scale=Math.max(canvas.width/image.width,canvas.height/image.height),w=image.width*scale,h=image.height*scale;ctx.globalAlpha=alpha;ctx.drawImage(image,(canvas.width-w)/2,(canvas.height-h)/2,w,h);ctx.globalAlpha=1;}\r\n return {ready,element:root,canvas,intro:options=>tour.intro({reducedMotion,...options}),\r\n  render(state,{dt=0,paused=false,handOut=false,pointer=null}={}){\r\n   if(disposed)return;\r\n   const key=`${state.encounterId}:${state.status}`,now=state.lastNow===null?null:state.lastNow/1000;\r\n   if(identity!==null&&(identity!==key||now===null&&lastClock!==null||now<lastClock))tour.cancel();\r\n   identity=key;lastClock=now;\r\n   motion.push({healingMs:state.healingMs,wakeMeter:state.wakeMeter,openingHandMs:state.openingHandMs},{identity:key,clock:now});\r\n   const drawn={...state,...motion.sample(dt,{paused,reducedMotion})};tour.apply();\r\n   const rect=root.getBoundingClientRect(),scale=Math.min(1,256/Math.max(1,rect.width),256/Math.max(1,rect.height));\r\n   const w=Math.max(1,Math.round(rect.width*scale)),h=Math.max(1,Math.round(rect.height*scale));\r\n   if(canvas.width!==w||canvas.height!==h){canvas.width=w;canvas.height=h;}ctx.imageSmoothingEnabled=false;\r\n   if(!paused)clock+=Math.min(.1,Math.max(0,dt));\r\n   const desired=paused?'off':'meditation';if(soundScene!==desired){soundScene=desired;physicalSound.setScene(desired,'pond');}\r\n   ctx.fillStyle='#0c394c';ctx.fillRect(0,0,w,h);if(!loaded)return;\r\n   const progress=drawn.healingMs/POND_TUNING_PROPOSAL.healingMs;\r\n   ctx.filter=`grayscale(${Math.max(0,.8-progress*.8)})`;cover(backdrop);ctx.filter='none';\r\n   const wake=drawn.wakeMeter/POND_TUNING_PROPOSAL.wakeThresholdSeconds;\r\n   ctx.save();ctx.globalAlpha=.35+wake*.5;ctx.filter='brightness(.22)';\r\n   const size=w*(state.status==='awoken'?.85:.6);ctx.drawImage(atlas,...coach.frame,w/2-size/2,h*.44-size*.25,size,size*.75);ctx.restore();\r\n   // Falling water remains in front of the sleeping silhouette.\r\n   cover(backdrop,.28);\r\n   const breath=pondBreath(state),pulse=reducedMotion?1:1+.045*Math.sin(clock*Math.PI*2/9);\r\n   if(state.status==='healing')drawAura2d(ctx,auras,w*.5,h*.77,w*.85*pulse,reducedMotion?0:clock*12);\r\n   if(state.status==='opening'){\r\n    if(!paused){\r\n     if(handOut)fishTouch(fish,pointer?.x??.5,(pointer?.y??.68)*POND.aspect,clock*1000);else if(fish.touch)fishRelease(fish,clock*1000);\r\n     if(!reducedMotion)stepFish(fish,Math.min(.1,Math.max(0,dt)),clock*1000);\r\n    }\r\n    ctx.fillStyle='#173f42e8';ctx.fillRect(0,h*.52,w,h*.48);\r\n    for(const koi of fish.fish){const x=koi.x*w,y=h*.52+(koi.y/POND.aspect)*h*.48;ctx.save();ctx.translate(x,y);ctx.rotate(koi.a);ctx.fillStyle='#ffc37b';ctx.fillRect(-5,-2,9,4);ctx.fillStyle='#fff3ba';ctx.fillRect(2,-1,2,2);ctx.restore();}\r\n    ctx.strokeStyle='#a8fff0';ctx.lineWidth=2;ctx.beginPath();ctx.arc((pointer?.x??.5)*w,(pointer?.y??.68)*h,9+state.openingHandMs/100,0,Math.PI*2);ctx.stroke();\r\n   }\r\n   if(lastStatus==='opening'&&state.status==='healing'){root.classList.add('pond-nibble');physicalSound.play('water');}\r\n   root.dataset.status=state.status;root.dataset.breath=breath.breath??'normal';lastStatus=state.status;\r\n  },\r\n  dispose(){if(disposed)return;disposed=true;tour.dispose();physicalSound.setScene('off');root.remove();},\r\n };\r\n}\r\n","modules/roguelite/render-cadence.mjs":"/** Gate drawing only. Callers must advance tracking, simulation and movement independently. */\nexport function createRenderCadence(fps=30){\n if(!Number.isFinite(fps)||fps<=0)throw new RangeError('fps must be positive');\n const period=1/fps;let elapsed=0,first=true;\n return {\n  shouldRender(delta,{force=false}={}){\n   if(!Number.isFinite(delta)||delta<0)throw new RangeError('delta must be nonnegative');\n   if(first||force){first=false;elapsed=0;return true;}\n   elapsed+=delta;\n   if(elapsed+1e-9<period)return false;\n   elapsed=Math.max(0,(elapsed+1e-9)%period-1e-9);return true;\n  },\n  reset(){elapsed=0;first=true;},\n };\n}\n","modules/roguelite/rescue-input.mjs":"import {classifyHand,selectActiveHand} from './hand-input.mjs';\nconst key=p=>p&&`${p.challengeId}:${p.generation}:${p.stage}`;\n/** Input observation only. The server owns every stage transition and grant. */\nexport function createRescueInput(){\n let projection=null,rendered=null,lastTs=-Infinity,wrist=null,handId=null,serial=0,label=null;\n const invalidate=()=>{rendered=null;wrist=null;handId=null;label=null;};\n return {\n  present(p){if(p?.challengeId!==projection?.challengeId||p?.generation!==projection?.generation)invalidate();else if(key(p)!==key(projection))rendered=null;projection=p?{...p}:null;},\n  markRendered(p){if(key(p)===key(projection))rendered=key(p);},\n  invalidate,\n  sample(raw,{now=globalThis.performance.now(),aspect=1,approachHeld=false}={}){\n   if(!projection||rendered!==key(projection)||projection.status==='accepted')return null;\n   const ts=raw?.ts;\n   if(!Number.isFinite(now)||!Number.isFinite(ts)||ts<0||ts<=lastTs||ts>now||now-ts>300){invalidate();return null;}\n   const none=()=>({challengeId:projection.challengeId,generation:projection.generation,stageAck:projection.stage,handId:null,x:null,y:null,shape:'none',approachHeld:false});\n   const gap=ts-lastTs;lastTs=ts;if(gap>300&&Number.isFinite(gap)){const dto=none();invalidate();return dto;}\n   const hands=Array.isArray(raw.landmarks)&&raw.landmarks.every(points=>Array.isArray(points)&&points.every(p=>p&&Number.isFinite(p.x)&&Number.isFinite(p.y)))?raw.landmarks:[];\n   const active=raw.tracked===false?null:selectActiveHand(hands,wrist);\n   if(!active||wrist&&Math.hypot(active.points[0].x-wrist.x,active.points[0].y-wrist.y)>.2){const dto=none();invalidate();return dto;}\n   const handed=raw.handednesses?.[active.index]??raw.handedness?.[active.index],item=Array.isArray(handed)?handed[0]:handed,nextLabel=typeof item==='string'?item:item?.categoryName??item?.label??null;\n   if(handId&&label!==nextLabel){const dto=none();invalidate();return dto;}label=nextLabel;\n   if(!handId)handId=`rescue-hand-${++serial}`;\n   wrist={x:active.points[0].x,y:active.points[0].y};\n   const world=raw.worldLandmarks?.[active.index],validWorld=Array.isArray(world)&&world.length===21&&world.every(p=>p&&[p.x,p.y,p.z].every(Number.isFinite));\n   const palm=active.points[9],classified=classifyHand(active.points,validWorld?world:null,Number.isFinite(aspect)&&aspect>0?aspect:1);\n   if(palm.x<0||palm.x>1||palm.y<0||palm.y>1){const dto=none();invalidate();return dto;}\n   const shape=classified==='open'?'open':classified==='fist'?(projection.stage==='repeat-confirm'?'punch':'grasp'):classified==='pinch'&&projection.stage!=='repeat-confirm'?'grasp':'none';\n   return shape==='none'?none():{challengeId:projection.challengeId,generation:projection.generation,stageAck:projection.stage,handId,x:palm.x,y:palm.y,shape,approachHeld:approachHeld===true};\n  },\n };\n}\n","modules/roguelite/rescue-scene.mjs":"import {rescueCoachPresentation} from './coach-presentation.mjs';\n\n/** Borrowed scene layer. Caller owns drawing and the camera lease. */\nexport async function prepareRescueLayer(T,{projection,onRendered=()=>{},reducedMotion=false,signal,loadModel}={}){\n const identity=rescueCoachPresentation(projection);\n const loader=loadModel??(await import('../../creature/assets/rescue-model.js')).loadRescueCoach;\n const model=await loader(identity.sourceId,{reducedMotion,signal});\n if(signal?.aborted){model.dispose();throw Object.assign(new Error('Rescue preparation aborted.'),{name:'AbortError'});}\n const group=new T.Group();group.name='camera-overlay-rescue';group.add(model.root);\n group.add(new T.HemisphereLight(0xffe7de,0x21151d,2.6));const light=new T.DirectionalLight(0xffddcc,3);light.position.set(-2,5,4);group.add(light);\n const geo=new T.BoxGeometry(.17,.095,.035),material=new T.MeshStandardMaterial({color:0x16302f,metalness:.7,roughness:.3});\n const chip=new T.Mesh(geo,material);chip.name='MOM-Inc-control-chip';group.add(chip);\n const pinsGeo=new T.BoxGeometry(.025,.09,.016),pinsMat=new T.MeshStandardMaterial({color:0xe4b457,metalness:.8,roughness:.2});\n for(let i=0;i<5;i++){const pin=new T.Mesh(pinsGeo,pinsMat);pin.position.set(-.068+i*.034,-.065,.023);chip.add(pin);}\n // The physical chip carries its label; no image/network dependency for lettering.\n const labelCanvas=globalThis.document?.createElement('canvas');let labelTexture=null,labelMat=null,labelGeo=null;\n if(labelCanvas){labelCanvas.width=256;labelCanvas.height=96;const ctx=labelCanvas.getContext('2d');if(ctx){ctx.fillStyle='#142b24';ctx.fillRect(0,0,256,96);ctx.fillStyle='#ffe8ae';ctx.font='bold 38px sans-serif';ctx.textAlign='center';ctx.fillText('MOM Inc.',128,60);labelTexture=new T.CanvasTexture(labelCanvas);labelMat=new T.MeshBasicMaterial({map:labelTexture});labelGeo=new T.PlaneGeometry(.16,.06);const label=new T.Mesh(labelGeo,labelMat);label.position.z=.026;chip.add(label);}}\n const count=reducedMotion?8:96,positions=new Float32Array(count*3),velocities=new Float32Array(count*3),particleGeo=new T.BufferGeometry();\n particleGeo.setAttribute('position',new T.BufferAttribute(positions,3));\n const particleMat=new T.PointsMaterial({color:0xb90823,size:reducedMotion?.022:.045,transparent:true,opacity:0,depthWrite:false});\n const blood=new T.Points(particleGeo,particleMat);blood.name='blood-and-gore-splatter';blood.frustumCulled=false;group.add(blood);\n const goreGeo=new T.IcosahedronGeometry(.035,0),goreMat=new T.MeshStandardMaterial({color:0x74091b,roughness:.42});\n const gore=new T.InstancedMesh(goreGeo,goreMat,reducedMotion?3:18);gore.name='torn-tissue-particles';gore.visible=false;group.add(gore);\n const dummy=new T.Object3D(),head=new T.Vector3(),start=new T.Vector3();\n let p=structuredClone(projection),age=0,burstAge=-1,disposed=false,accepted=false;\n const set=pending=>{if(disposed)return;if(pending.canonicalCoachId!==p.canonicalCoachId||pending.presentationCoachId!==p.presentationCoachId)throw new TypeError('Rescue model identity changed');if(pending.stage!==p.stage)age=0;p=structuredClone(pending);if(p.stage==='accepted'&&p.receipt&&!accepted){accepted=true;if(!identity.repeat){burstAge=0;model.play('awaken');}else model.play('agree');}};\n function emit(){\n  start.copy(head);for(let i=0;i<count;i++){const a=i*2.399963,r=.12+(i%7)*.045;positions.set([head.x,head.y,head.z],i*3);velocities.set([Math.cos(a)*r, .3+(i%11)*.045,.5+Math.sin(a)*r],i*3);}\n }\n function update(dt,camera){\n  if(disposed)return;dt=Math.min(.1,Math.max(0,dt));age+=dt;\n  if(!accepted){model.root.rotation.z=identity.repeat?0:-.16;model.root.position.y=identity.repeat?0:-.3;}\n  else if(!identity.repeat){\n   const t=Math.min(1,age/.8);model.root.rotation.z=reducedMotion?0:-.16*(1-t);model.root.position.y=-.3*(1-t);\n   if(age>.8){model.play('walk');model.root.rotation.y=reducedMotion?0:Math.PI;model.root.position.z=reducedMotion?0:-Math.min(10,(age-.8)*2);model.root.position.x=reducedMotion?0:Math.min(2,(age-.8)*.5);model.root.visible=age<(reducedMotion?2:5);}\n  }\n  model.update(dt);group.updateMatrixWorld(true);model.head.getWorldPosition(head);\n  const rect=p.targetRect,progress=Math.min(1,Math.max(0,p.stageProgress??0));\n  const tan=Math.tan(camera.fov*Math.PI/360),near=Math.max(3,(model.headWidth??.7)/(2*tan*camera.aspect*.65)),distance=identity.repeat?Math.max(6,near):p.stage==='approach'?near+2-progress*2:near;\n  // First-person virtual translation only. Match the actual head to the issued normalized target.\n  if(!accepted){const cx=rect.x+rect.width/2,cy=rect.y+rect.height/2;camera.position.set(head.x-(cx*2-1)*distance*tan*camera.aspect,head.y-(1-cy*2)*distance*tan,head.z+distance);camera.quaternion.identity();camera.updateMatrixWorld(true);}\n  chip.visible=!identity.repeat&&(!accepted||burstAge<1.4);chip.position.copy(accepted?start:head);chip.position.z+=.04;\n  if(p.stage==='pull'&&!identity.repeat){chip.position.z+=progress*.36;chip.rotation.z=progress*.6;}\n  if(burstAge>=0){if(burstAge===0)emit();burstAge+=dt;chip.position.z+=Math.min(.65,burstAge*.55);chip.rotation.z=burstAge*1.8;\n   particleMat.opacity=Math.max(0,1-burstAge/1.6);gore.visible=burstAge<1.6;\n   for(let i=0;i<count;i++){const j=i*3;positions[j]+=velocities[j]*dt;positions[j+1]+=velocities[j+1]*dt;positions[j+2]+=velocities[j+2]*dt;velocities[j+1]-=(reducedMotion?.15:1.8)*dt;}\n   particleGeo.attributes.position.needsUpdate=true;\n   for(let i=0;i<gore.count;i++){const j=i*3;dummy.position.fromArray(positions,j);dummy.rotation.set(burstAge*(i%3),i,burstAge*2);dummy.scale.set(1,(i%3)+.6,.8);dummy.updateMatrix();gore.setMatrixAt(i,dummy.matrix);}gore.instanceMatrix.needsUpdate=true;\n  }\n }\n set(projection);\n return {group,identity,present:set,update,afterRender(){if(!disposed)onRendered(structuredClone(p));},dispose(){if(disposed)return;disposed=true;group.removeFromParent();model.dispose();for(const item of [geo,material,pinsGeo,pinsMat,labelGeo,labelMat,labelTexture,particleGeo,particleMat,goreGeo,goreMat])item?.dispose();group.clear();}};\n}\n","modules/roguelite/rescue-state.mjs":"// Physical referee only. The authenticated host owns freshness and atomic reward settlement.\nconst STAGES = ['approach', 'reach', 'grasp', 'pull', 'repeat-confirm', 'accepted'];\nconst INPUT_KEYS = ['challengeId', 'generation', 'stageAck', 'handId', 'x', 'y', 'shape', 'approachHeld'];\nconst PROJECTION_KEYS = ['version', 'policyId', 'encounterId', 'challengeId', 'generation', 'stage', 'status', 'mode', 'targetRect', 'canonicalCoachId', 'presentationCoachId', 'rescueKind', 'stageProgress', 'receipt'];\nconst text = value => typeof value === 'string' && value.length > 0;\nconst generationOk = value => Number.isSafeInteger(value) && value >= 0;\nconst coordinate = value => Number.isFinite(value) && value >= 0 && value <= 1;\n\nexport function validateRescueInput(input) {\n  if (!input || typeof input !== 'object' || Array.isArray(input)\n    || Object.keys(input).length !== INPUT_KEYS.length || INPUT_KEYS.some(key => !Object.hasOwn(input, key))\n    || !text(input.challengeId) || !generationOk(input.generation) || !STAGES.includes(input.stageAck)\n    || !['none', 'open', 'grasp', 'punch'].includes(input.shape) || typeof input.approachHeld !== 'boolean') {\n    throw new TypeError('Invalid rescue input');\n  }\n  if (input.shape === 'none'\n    ? input.handId !== null || input.x !== null || input.y !== null\n    : !text(input.handId) || input.handId.length > 64 || !coordinate(input.x) || !coordinate(input.y)) {\n    throw new TypeError('Invalid rescue hand');\n  }\n  return input;\n}\n\nexport function createRescue({ encounterId, challengeId, generation = 0, canonicalCoachId, presentationCoachId, rescueKind }) {\n  if (![encounterId, challengeId, canonicalCoachId, presentationCoachId].every(text)\n    || !generationOk(generation) || !['first', 'repeat'].includes(rescueKind)) throw new TypeError('Invalid rescue identity');\n  const state = {\n    version: 1, policyId: 'rescue-six-v1', encounterId, challengeId, generation,\n    stage: rescueKind === 'repeat' ? 'repeat-confirm' : 'approach', status: 'pending', mode: 'overlay-virtual',\n    targetRect: { x: 0.35, y: 0.15, width: 0.30, height: 0.30 },\n    canonicalCoachId, presentationCoachId, rescueKind, stageProgress: 0, receipt: null,\n    lastObservationId: -1, lastNow: null,\n  };\n  clearGesture(state);\n  return state;\n}\n\nfunction clearGesture(state) {\n  state.released = false;\n  state.handId = null;\n  state.lastShape = null;\n  state.ackStage = null;\n  state.stageMs = 0;\n  state.pullOrigin = null;\n  state.lastInside = false;\n}\n\nfunction resetPending(state) {\n  state.stage = state.rescueKind === 'repeat' ? 'repeat-confirm' : 'approach';\n  state.stageProgress = 0;\n  clearGesture(state);\n}\n\nfunction nextStage(state, stage) {\n  state.stage = stage;\n  state.stageProgress = 0;\n  state.stageMs = 0;\n  state.ackStage = null;\n  state.lastShape = null;\n  state.lastInside = false;\n}\n\nexport function projectRescue(state) {\n  return structuredClone(Object.fromEntries(PROJECTION_KEYS.map(key => [key, state[key]])));\n}\n\nexport function restoreRescue(saved, { challengeId = saved.challengeId, generation = saved.generation } = {}) {\n  if (saved.status === 'accepted') return structuredClone(saved);\n  const state = createRescue({ ...saved, challengeId, generation });\n  if (!Number.isSafeInteger(saved.lastObservationId) || saved.lastObservationId < -1\n    || !(saved.lastNow === null || Number.isFinite(saved.lastNow))) throw new TypeError('Invalid rescue highwater');\n  state.lastObservationId = saved.lastObservationId;\n  state.lastNow = saved.lastNow;\n  return state;\n}\n\nexport function stepRescue(state, { observationId, now, tracked, paused = false, rescue } = {}) {\n  if (!Number.isSafeInteger(observationId) || observationId < 0 || !Number.isFinite(now)\n    || typeof tracked !== 'boolean' || typeof paused !== 'boolean') throw new TypeError('Invalid rescue observation');\n  if (rescue !== null && rescue !== undefined) validateRescueInput(rescue);\n  const next = structuredClone(state);\n  const result = { state: next, accepted: false, creditMs: 0 };\n  if (state.status === 'accepted' || observationId <= state.lastObservationId) return result;\n  const gap = state.lastNow === null ? 0 : now - state.lastNow;\n  next.lastObservationId = observationId;\n  next.lastNow = state.lastNow === null ? now : Math.max(now, state.lastNow);\n  if (!tracked || paused || gap > 300 || (state.lastNow !== null && gap <= 0) || !rescue\n    || rescue.challengeId !== state.challengeId || rescue.generation !== state.generation) {\n    resetPending(next);\n    return result;\n  }\n  // A batch can contain observations captured before the preceding frame changed stages.\n  // They cannot acknowledge the new stage or carry continuity, but do not undo that transition.\n  if (rescue.stageAck !== state.stage) {\n    next.ackStage = null; next.lastShape = null; next.lastInside = false;\n    next.stageMs = 0; next.stageProgress = 0;\n    return result;\n  }\n  // A newly displayed stage starts a new interval: never carry the prior stage's dt.\n  const dt = state.ackStage === state.stage ? Math.min(100, gap) : 0;\n  next.ackStage = state.stage;\n  const release = !rescue.approachHeld && ['none', 'open'].includes(rescue.shape);\n  const inside = rescue.x >= state.targetRect.x && rescue.x <= state.targetRect.x + state.targetRect.width\n    && rescue.y >= state.targetRect.y && rescue.y <= state.targetRect.y + state.targetRect.height;\n  const fail = () => { resetPending(next); return result; };\n  const accept = () => { next.stage = 'accepted'; next.status = 'accepted'; next.stageProgress = 1; clearGesture(next); result.accepted = true; return result; };\n\n  if (state.stage === 'repeat-confirm') {\n    if (release) {\n      next.released = true;\n      next.handId = rescue.handId;\n    } else if (rescue.shape === 'punch' && state.released && ['none', 'open'].includes(state.lastShape)\n      && (!state.handId || state.handId === rescue.handId)) return accept();\n    else return fail();\n  } else if (state.stage === 'approach') {\n    if (release) {\n      next.released = true;\n      next.stageMs = 0;\n      next.stageProgress = 0;\n      next.handId = null;\n    } else if (state.released && rescue.shape === 'open' && rescue.approachHeld) {\n      if (state.handId && state.handId !== rescue.handId) return fail();\n      next.handId = rescue.handId;\n      const credit = state.handId === rescue.handId && state.lastShape === 'open' ? dt : 0;\n      next.stageMs += credit;\n      // Virtual approach is an input gate, not qualified physical workout time.\n      next.stageProgress = Math.min(1, next.stageMs / 500);\n      if (next.stageMs >= 500) { nextStage(next, 'reach'); return result; }\n    } else return fail();\n  } else {\n    if (rescue.handId !== state.handId) return fail();\n    if (state.stage === 'reach') {\n      if (rescue.shape !== 'open') return fail();\n      // Reaching toward the target is allowed; only consecutive inside samples dwell.\n      const credit = inside && state.lastInside === true ? dt : 0;\n      next.stageMs = inside ? next.stageMs + credit : 0;\n      result.creditMs = credit;\n      next.stageProgress = Math.min(1, next.stageMs / 500);\n      next.lastInside = inside;\n      if (next.stageMs >= 500) { nextStage(next, 'grasp'); return result; }\n    } else if (state.stage === 'grasp') {\n      if (!inside) return fail();\n      if (rescue.shape === 'grasp' && state.ackStage === 'grasp' && state.lastShape === 'open') {\n        nextStage(next, 'pull');\n        next.pullOrigin = { x: rescue.x, y: rescue.y };\n        return result;\n      }\n      if (rescue.shape !== 'open') return fail();\n    } else if (state.stage === 'pull') {\n      if (rescue.shape !== 'grasp' || !state.pullOrigin) return fail();\n      next.stageMs += dt;\n      result.creditMs = dt;\n      const distance = Math.hypot(rescue.x - state.pullOrigin.x, rescue.y - state.pullOrigin.y);\n      next.stageProgress = Math.min(1, next.stageMs / 300, distance / 0.12);\n      if (next.stageMs >= 300 && distance >= 0.12) return accept();\n    }\n  }\n  next.lastShape = rescue.shape;\n  return result;\n}\n","modules/roguelite/rescue-view.mjs":"import {createRescueInput} from './rescue-input.mjs';\nimport {rescueCoachPresentation} from './coach-presentation.mjs';\nimport {rescueDialogue,RESCUE_MODE_LABEL} from './coach-dialogue.mjs';\n\n/** Captions and observed gestures only; host owns camera lifecycle and authenticated receipts. */\nexport function createRescueView({root,scene,voice=null,reducedMotion=false,onRetry=()=>{}}={}){\n if(!root||!scene?.prepareRescue)throw new TypeError('Rescue needs a root and the existing run scene.');\n const doc=root.ownerDocument,input=createRescueInput(),layer=doc.createElement('section');layer.className='myr5-rescue';layer.setAttribute('aria-label','Coach rescue');\n layer.innerHTML=`<style>\n .myr5-rescue{position:absolute;inset:0;pointer-events:none;color:#fff7e9;font:600 16px/1.4 system-ui,sans-serif;z-index:12;isolation:isolate}\n .myr5-rescue *{box-sizing:border-box}.myr5-rescue-panel{position:absolute;left:16px;right:16px;bottom:max(18px,env(safe-area-inset-bottom));margin:auto;max-width:520px;padding:18px;border:1px solid #9ca78a;background:#141914ee;box-shadow:0 8px 35px #0008;border-radius:14px;pointer-events:auto}\n .myr5-rescue-mode{position:absolute;left:14px;top:max(14px,env(safe-area-inset-top));padding:8px 12px;border:1px solid #e5d2ab66;background:#141914dd;border-radius:24px;font-size:12px;letter-spacing:.04em}\n body:has(.myr5-rescue) #hud,body:has(.myr5-rescue) #room-label,body:has(.myr5-rescue) #tutorial-toggle,body:has(.myr5-rescue) .brand{visibility:hidden}\n body:has(.myr5-rescue) #end-run{top:74px;bottom:auto!important}\n .myr5-rescue-title{font-family:Pixel,monospace;margin:0 0 8px;font-size:22px;line-height:1.15}.myr5-rescue-caption{margin:0 0 12px;font-weight:500}.myr5-rescue-voice{font-size:12px;color:#bdd0b4;margin:6px 0 0}.myr5-rescue-meter{width:100%;height:8px;accent-color:#d9e4a1}\n .myr5-rescue-result{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin:12px 0}.myr5-rescue-result span{border:1px solid #bccd8c77;border-radius:8px;padding:9px 3px;text-align:center;font-size:11px;color:#e3edb3}.myr5-rescue-result b{display:block;font-size:18px;line-height:1.2}\n .myr5-rescue button{font:inherit;min-height:48px;background:#e2ebaa;color:#17201a;padding:10px 20px;border:0;border-radius:9px;touch-action:none;cursor:pointer}.myr5-rescue button:focus-visible{outline:3px solid #fff;outline-offset:3px}.myr5-rescue button:disabled{opacity:.5}\n .myr5-rescue-target{position:absolute;border:2px dashed #e4efb3;border-radius:40%;box-shadow:0 0 0 1px #17201a88,inset 0 0 22px #d9ff8b22}.myr5-rescue-target:before{content:'HEAD · MOM Inc. CHIP';position:absolute;left:50%;bottom:100%;transform:translateX(-50%);white-space:nowrap;font-size:10px;background:#15251bdb;padding:4px 8px;border-radius:4px}\n .myr5-rescue [hidden]{display:none!important}@media(prefers-reduced-motion:reduce){.myr5-rescue *{scroll-behavior:auto}}\n </style><div class=\"myr5-rescue-mode\"></div><div class=\"myr5-rescue-target\" hidden></div><div class=\"myr5-rescue-panel\"><h2 class=\"myr5-rescue-title\">Preparing coach rescue</h2><p class=\"myr5-rescue-caption\" role=\"status\" aria-live=\"polite\">Loading the actual coach model…</p><div class=\"myr5-rescue-result\" aria-label=\"Accepted first rescue\" hidden><span><b aria-hidden=\"true\">✓</b>Coach saved</span><span><b aria-hidden=\"true\">♜</b>Camp statue</span><span><b aria-hidden=\"true\">♥</b>CoachHeart</span></div><progress class=\"myr5-rescue-meter\" max=\"1\" value=\"0\" aria-label=\"Current rescue stage progress\"></progress><button type=\"button\" class=\"myr5-rescue-approach\" hidden>Hold Approach</button><button type=\"button\" class=\"myr5-rescue-retry\" hidden>Retry coach model</button><p class=\"myr5-rescue-voice\">Follow the voice and the on-screen cues.</p></div>`;\n root.append(layer);layer.querySelector('.myr5-rescue-mode').textContent=RESCUE_MODE_LABEL;\n const find=name=>layer.querySelector(`.myr5-rescue-${name}`),caption=find('caption'),title=find('title'),target=find('target'),meter=find('meter'),approach=find('approach'),retry=find('retry'),voiceCaption=find('voice');\n let p=null,prepared=null,loading=false,disposed=false,serial=0,abort=null,held=false,ready=Promise.resolve(false),spoken=null;\n const release=()=>{held=false;};\n const invalidate=()=>{release();input.invalidate();};\n const setHeld=event=>{if(event.type==='keydown'&&![' ','Enter'].includes(event.key))return;event.preventDefault();held=true;if(event.pointerId!==undefined)approach.setPointerCapture?.(event.pointerId);};\n approach.addEventListener('pointerdown',setHeld);approach.addEventListener('keydown',setHeld);\n for(const name of ['pointerup','pointercancel','lostpointercapture','keyup','blur'])approach.addEventListener(name,release);\n const hidden=()=>{if(doc.hidden)invalidate();};doc.addEventListener('visibilitychange',hidden);doc.defaultView?.addEventListener('orientationchange',invalidate);\n function display(){\n  if(!p||disposed)return;const dialogue=rescueDialogue(p),accepted=p.stage==='accepted'&&!!p.receipt;\n  title.textContent=loading?'Loading coach model…':accepted?(p.receipt.status==='first'?'MOM Inc. control removed':'Repeat victory'):p.rescueKind==='repeat'?'Original MYR5 · same strategy':'Free the defeated coach';\n  caption.textContent=loading?'Your coach is coming into view…':dialogue.caption;\n  find('result').hidden=loading||!accepted||p.receipt.status!=='first';\n  meter.value=Math.max(0,Math.min(1,Number(p.stageProgress)||0));meter.hidden=loading||accepted;\n  target.hidden=loading||p.rescueKind==='repeat'||accepted;\n  const rect=p.targetRect;target.style.left=`${rect.x*100}%`;target.style.top=`${rect.y*100}%`;target.style.width=`${rect.width*100}%`;target.style.height=`${rect.height*100}%`;\n  approach.hidden=loading||p.stage!=='approach';approach.disabled=loading;\n  const cue=`${p.challengeId}:${p.generation}:${p.stage}:${p.stage==='pull'&&p.stageProgress>0?'moving':''}`;\n  if(!loading&&dialogue.voice&&spoken!==cue){spoken=cue;voiceCaption.textContent=`“${dialogue.voice}”`;Promise.resolve(voice?.say(dialogue.voice,{key:'rescue',interrupt:true})).catch(()=>{if(!disposed)voiceCaption.textContent='Voice unavailable · captions on';});}\n }\n async function prepare(){\n  const version=++serial;abort?.abort();abort=new AbortController();loading=true;retry.hidden=true;invalidate();display();\n  if(prepared){scene.releaseRescue(prepared);prepared=null;}\n  let next=null,timer=null,cancelLoad=null;const requestAbort=abort;\n  try{\n   const pending=scene.prepareRescue({projection:p,reducedMotion,signal:requestAbort.signal,onRendered:rendered=>{if(!disposed&&!loading&&prepared&&!doc.hidden)input.markRendered(rendered);}}).then(result=>{if(disposed||version!==serial||requestAbort.signal.aborted){scene.releaseRescue(result);throw Object.assign(new Error('Rescue preparation aborted.'),{name:'AbortError'});}return result;});\n   next=await Promise.race([pending,new Promise((_,reject)=>{cancelLoad=()=>reject(Object.assign(new Error('Rescue preparation aborted.'),{name:'AbortError'}));requestAbort.signal.addEventListener('abort',cancelLoad,{once:true});timer=setTimeout(()=>{reject(new Error('Coach model loading timed out.'));requestAbort.abort();},20000);})]);\n   if(disposed||version!==serial){scene.releaseRescue(next);return false;}\n   next.present(p);scene.commitRescue(next);prepared=next;loading=false;display();return true;\n  }catch(error){if(next)scene.releaseRescue(next);if(disposed||version!==serial)return false;loading=false;invalidate();caption.textContent='Coach model could not load. Check your connection and retry.';title.textContent='Rescue paused';retry.hidden=false;approach.hidden=true;target.hidden=true;meter.hidden=true;return false;}\n  finally{clearTimeout(timer);if(cancelLoad)requestAbort.signal.removeEventListener('abort',cancelLoad);}\n }\n const retryLoad=()=>{if(disposed)return;onRetry();ready=prepare();};retry.addEventListener('click',retryLoad);\n function present(projection){\n  if(disposed)return Promise.resolve(false);rescueCoachPresentation(projection);\n  if(!projection.targetRect||!['approach','reach','grasp','pull','repeat-confirm','accepted'].includes(projection.stage))throw new TypeError('Invalid rescue projection');\n  const reload=!p||p.presentationCoachId!==projection.presentationCoachId||p.canonicalCoachId!==projection.canonicalCoachId;\n  if(!p||p.challengeId!==projection.challengeId||p.generation!==projection.generation||p.stage!==projection.stage)release();\n  p=structuredClone(projection);input.present(p);if(reload||!prepared&&!loading)ready=prepare();else{prepared?.present(p);display();}return ready;\n }\n return {present,sample(raw,options={}){return disposed||loading||doc.hidden?null:input.sample(raw,{...options,approachHeld:options.approachHeld??held});},update(delta){if(!Number.isFinite(delta)||delta<0)throw new RangeError('Invalid rescue delta');},invalidate,get ready(){return ready;},dispose(){if(disposed)return;disposed=true;serial++;abort?.abort();invalidate();doc.removeEventListener('visibilitychange',hidden);doc.defaultView?.removeEventListener('orientationchange',invalidate);retry.removeEventListener('click',retryLoad);voice?.cancel?.();if(prepared)scene.releaseRescue(prepared);prepared=null;layer.remove();}};\n}\n","modules/roguelite/room-atmosphere.mjs":"import {useFogLaw,veilFar} from '../coach-world-rts/fog-law.mjs';\r\nimport {createFogBanks} from '../coach-world-rts/fog-banks.mjs';\r\nimport {createDustMotes} from '../coach-world-rts/dust-motes.mjs';\r\n\r\n// Reuse the Daggerfall-derived atmosphere in the room's existing renderer.\r\n// Loading fog conceals unfinished surroundings; the portal owns the fully covered scene swap.\r\nexport function createRoomAtmosphere(T,{scene,camera,color=0x777b72,scale=1,seed=101,reducedMotion=false,persistentBanks=[]}){\n if(!scene?.isScene||!camera?.isCamera||!Number.isFinite(scale)||scale<=0)throw new TypeError('Room atmosphere needs a scene, camera and positive scale.');\n if(!Array.isArray(persistentBanks)||persistentBanks.length>11||persistentBanks.some(b=>!b||typeof b.id!=='string'||!b.id||b.id==='room-loading'||![b.x,b.y??0,b.z,b.r].every(Number.isFinite)||b.r<=0)||new Set(persistentBanks.map(b=>b.id)).size!==persistentBanks.length)throw new TypeError('Expected at most eleven distinct finite persistent fog banks.');\n const baseline=persistentBanks.map(b=>({...b}));\n const previous={fog:scene.fog,before:scene.onBeforeRender,after:scene.onAfterRender,cwFog:scene.userData.cwFog};\r\n const tint=new T.Color(color),near=2*scale,far=18*scale;\r\n scene.fog=new T.Fog(tint,near,far);\r\n useFogLaw(T,scene,{haze:{extinction:.025/scale,floor:0,falloff:1.5*scale,clear:scale}});\r\n const banks=createFogBanks(T,{max:12,color:[tint.r,tint.g,tint.b],seed,opacity:.7,fadeNear:scale,fadeFar:far,clearSeconds:1.2});\r\n const dust=createDustMotes(T,{count:48,box:8*scale,size:.25*scale,seed,color:[.85,.81,.7],opacity:.2});\r\n dust.setWind(.025*scale,.01*scale);scene.add(banks.object,dust.object);\r\n let time=0,loading=false,veil=0,disposed=false;\n const eye=new T.Vector3(),ahead=new T.Vector3();\n // Start authored banks visible even when the entry frame is paused/reduced motion.\n banks.setBanks(baseline);banks.update(0,camera);\n for(let i=1;i<=5;i++)banks.update(i*.25,camera);\n time=1.25;\n if(reducedMotion)banks.object.material.uniforms.uTime.value=0;\n return {\r\n  setLoading(value){\r\n   if(disposed||loading===!!value)return;loading=!!value;\r\n   if(loading){camera.getWorldPosition(eye);camera.getWorldDirection(ahead);ahead.multiplyScalar(3*scale).add(eye);banks.setBanks([...baseline,{id:'room-loading',x:ahead.x,y:ahead.y,z:ahead.z,r:4*scale}]);}\n   else banks.setBanks(baseline);\n  },\r\n  update(delta,{paused=false}={}){\r\n   if(disposed)return;\r\n   if(!Number.isFinite(delta)||delta<0)throw new RangeError('Atmosphere delta must be finite and nonnegative.');\r\n   if(paused)return;\r\n   const dt=Math.min(.1,delta);time+=dt;veil+=(Number(loading)-veil)*Math.min(1,dt*3);\r\n   scene.fog.far=veilFar(near,far,veil,1.5*scale);\r\n   camera.getWorldPosition(eye);dust.update(reducedMotion?0:time,eye);banks.update(time,camera);\r\n   if(reducedMotion)banks.object.material.uniforms.uTime.value=0;\r\n  },\r\n  stats(){return {loading,veil,calls:dust.stats().calls+banks.stats().calls,banks:banks.stats().banks,persistentBanks:baseline.length};},\n  dispose(){\r\n   if(disposed)return;disposed=true;\r\n   scene.remove(banks.object,dust.object);banks.dispose();dust.dispose();\r\n   scene.fog=previous.fog;scene.onBeforeRender=previous.before;scene.onAfterRender=previous.after;\r\n   if(previous.cwFog===undefined)delete scene.userData.cwFog;else scene.userData.cwFog=previous.cwFog;\r\n  },\r\n };\r\n}\r\n","modules/roguelite/room-contracts.mjs":"// Local GPT-OSS room-data draft, reviewed/corrected against Ian's 20 Oct door brief.\n// All material boards reuse the delivered R102 portal factories and assets.\n// Core room types are discovery/achievement categories; exercise schedule owns boss eligibility.\n// Latest correction: crystal bosses only; grass is a nonlethal coin chase.\nexport const ROOM_CONTRACTS = [\n  {\n    \"id\": \"crystal\",\n    \"material\": \"crystal\",\n    \"roomType\": \"boss_encounter\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"preview-exact-boss\",\n        \"instruction\": \"See your boss through the crystal.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"punch-crystal\",\n        \"instruction\": \"Punch the crystal to confirm.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"See which boss waits inside.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Punch to enter the boss fight.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"coreRoomType\": false,\n    \"pendingArt\": false,\n    \"destinations\": [\n      \"mini_boss\",\n      \"major_boss\"\n    ],\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/pod/worlds/boards/ice.glb\",\n      \"assetKind\": \"glb\",\n      \"existingBoardId\": \"ice\",\n      \"destinationKit\": \"boss_encounter\"\n    }\n  },\n  {\n    \"id\": \"pond\",\n    \"material\": \"pond\",\n    \"roomType\": \"healing_waters\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"holdhandoverwater\",\n        \"instruction\": \"Hold hand over water\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"wait-for-nibble\",\n        \"instruction\": \"Wait for the fish to nibble\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Hold your hand in the water.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Let the fish nibble to enter.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Balance. Stay still. Breathe slowly.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Heal without waking the sleeping monster.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board-pond.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"healing_waters\"\n    }\n  },\n  {\n    \"id\": \"grass\",\n    \"material\": \"grass\",\n    \"roomType\": \"coin_chase\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"push-open-palms\",\n        \"instruction\": \"Push the grass over with both open palms.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"lie-prone\",\n        \"instruction\": \"LAY DOWN on your stomach.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"look-up\",\n        \"instruction\": \"Look up. Fly into the grass.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 3,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"While standing, push both open palms forward to fold the grass entrance over.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"After the grass opens, LAY DOWN on your stomach.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Chase the four-legged coin pooper.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Hit obstacles? Slow down, never lose health.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Catch up for a large coin bonus.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Half a clean run can catch it.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/pod/worlds/boards/grass.glb\",\n      \"assetKind\": \"glb\",\n      \"destinationKit\": \"coin_chase\"\n    },\n    \"gameRules\": {\n      \"obstacleEffect\": \"slow-only\",\n      \"healthDamage\": false,\n      \"baseCoins\": \"finite-seeded-pool\",\n      \"catchBonus\": \"once-per-encounter\",\n      \"maximumCleanCatchFraction\": 0.5,\n      \"recoveryAfterEarlyMistakes\": true,\n      \"cameraPanIntoGrass\": true,\n      \"coachKind\": \"existing-four-legged\"\n    }\n  },\n  {\n    \"id\": \"dance\",\n    \"material\": \"dance\",\n    \"roomType\": \"pixel_laser\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"touchupperleft\",\n        \"instruction\": \"Touch upper-left once, any order\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"touchupperright\",\n        \"instruction\": \"Touch upper-right once, any order\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"touchlowerleft\",\n        \"instruction\": \"Touch lower-left once, any order\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"touchlowerright\",\n        \"instruction\": \"Touch lower-right once, any order\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Touch all four corners, in any order.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Each corner lights once.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Lasers activate. The next portal opens.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Outdance your rival coach.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Win decisively for a special reward.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"existingBoardId\": \"pixel\",\n    \"anyOrder\": true,\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board-pixel.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"pixel_laser\"\n    },\n    \"gameRules\": {\n      \"reuseExistingTracking\": true,\n      \"reuseExistingScoring\": true,\n      \"visualStyle\": \"64-bit-pixel-laser-floor\",\n      \"opponent\": \"coach-rival\",\n      \"specialReward\": \"outdance-margin-once\"\n    }\n  },\n  {\n    \"id\": \"papier_mache\",\n    \"material\": \"papier_mache\",\n    \"roomType\": \"corgi_shop\",\n    \"cameraMode\": \"rear-hand\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"reachpinchturnhandle\",\n        \"instruction\": \"Reach, pinch, turn handle\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Items left. Merchant and exit right.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Pinch to carry items. Pet the pet.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Checkout buys your basket.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Short on Run Tokens? Everything goes back.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Open the exit unpaid to steal.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Steal and later shops turn dangerous.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"existingBoardId\": \"mache\",\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board-mache.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"corgi_shop\"\n    }\n  },\n  {\n    \"id\": \"clay\",\n    \"material\": \"clay\",\n    \"roomType\": \"shrink_burpees_maze\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"claw\",\n        \"instruction\": \"Claw to summon shrink potion\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"drink-potion\",\n        \"instruction\": \"Drink potion to shrink\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"pass-crack\",\n        \"instruction\": \"Pass through the tiny crack\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Claw to summon your shrink potion.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Drink it. Shrink through the crack.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Escape Coach Armie through the burpees maze.\",\n        \"graphicCue\": \"ghostpose\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"existingBoardId\": \"clay\",\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board-clay.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"shrink_burpees_maze\"\n    },\n    \"chasingCoach\": \"Coach Armie\"\n  },\n  {\n    \"id\": \"flat_paper\",\n    \"material\": \"flat_paper\",\n    \"roomType\": \"two_altars\",\n    \"cameraMode\": \"rear-hand\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"raise-hands\",\n        \"instruction\": \"Raise your free hand toward paper\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"pinch-paper\",\n        \"instruction\": \"Pinch and pull paper downward\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"gesture\": \"tear-portal\",\n        \"instruction\": \"Tear open the portal\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Pinch the paper. Tear downward.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Choose between two altars.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Check the cost before offering your palm.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Keep your hand on the chosen altar.\",\n        \"graphicCue\": \"ghostpose\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"existingBoardId\": \"paper\",\n    \"altarCount\": 2,\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board-paper.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"two_altars\"\n    }\n  },\n  {\n    \"id\": \"quilt\",\n    \"material\": \"quilt\",\n    \"roomType\": \"secret_room\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"hands-down\",\n        \"instruction\": \"Lower hands toward floor or phone bottom\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"gesture\": \"pinch-pull-curtain\",\n        \"instruction\": \"Pinch curtain and pull upward\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Crouch to choose the middle curtain.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Pinch low. Pull the curtain up.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Complete the challenge to open your portal.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Choose one powerful boon with a heavy drawback.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Two offers. Each has its own heavy cost.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Challenge missed? Rock waits behind the curtain.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": false,\n    \"pendingArt\": false,\n    \"starterGrimoire\": true,\n    \"optionalThirdDoor\": true,\n    \"rewardRules\": { \"challengeRequired\": true, \"offerCount\": 2, \"chooseCount\": 1, \"eachOffer\": [\"exceptional-boon\", \"heavy-drawback\"] },\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/modules/portal/portal-board.mjs\",\n      \"assetKind\": \"procedural-module\",\n      \"destinationKit\": \"secret_room\"\n    }\n  },\n  {\n    \"id\": \"wood\",\n    \"material\": \"wood\",\n    \"roomType\": \"nature_yoga\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"bothhandsforwardfire\",\n        \"instruction\": \"Both hands forward, fire rises\",\n        \"graphicCue\": \"ghostpose\"\n      }\n    ],\n    \"tutorialVersion\": 1,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Reach both hands forward.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Fire rises. The wood burns open.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Hold your yoga pose as animals approach.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Earn a run aura.\",\n        \"graphicCue\": \"holdring\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/pod/worlds/boards/wood.glb\",\n      \"assetKind\": \"glb\",\n      \"destinationKit\": \"nature_yoga\"\n    }\n  },\n  {\n    \"id\": \"jelly\",\n    \"material\": \"jelly\",\n    \"roomType\": \"crisis_combat\",\n    \"cameraMode\": \"front-body\",\n    \"openingSteps\": [\n      {\n        \"gesture\": \"arms-raised\",\n        \"instruction\": \"Lower your arms, then raise both wrists above your shoulders.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      }\n    ],\n    \"tutorialVersion\": 2,\n    \"tutorialSteps\": [\n      {\n        \"instruction\": \"Raise both arms and let the jelly pull you in.\",\n        \"graphicCue\": \"ghostpose\"\n      },\n      {\n        \"instruction\": \"Read the skull and level before entering.\",\n        \"graphicCue\": \"holdring\"\n      },\n      {\n        \"instruction\": \"Jelly swallows you, then drops you through.\",\n        \"graphicCue\": \"swoopingcurvearrow\"\n      },\n      {\n        \"instruction\": \"Clear the minion waves.\",\n        \"graphicCue\": \"ghostpose\"\n      }\n    ],\n    \"coreRoomType\": true,\n    \"pendingArt\": false,\n    \"variants\": [\n      \"Crouching Crisis\",\n      \"Time Crisis\",\n      \"Halo\",\n      \"Terminator\"\n    ],\n    \"doorAsset\": {\n      \"delivered\": true,\n      \"assetUrl\": \"/pod/worlds/boards/jelly.glb\",\n      \"assetKind\": \"glb\",\n      \"destinationKit\": \"crisis_combat\"\n    },\n    \"gameRules\": {\n      \"reuseExistingWaveGames\": true,\n      \"enemyRoles\": \"minions-in-waves\",\n      \"previewEnemyTypeAndLevel\": true\n    }\n  }\n];\nexport const ROOM_BY_ID = Object.freeze(Object.fromEntries(ROOM_CONTRACTS.map(room=>[room.id,room])));\n\n// Side-room content is not an extra collectible grimoire or a main-route exercise room.\nexport const TREASURE_SIDE_ROOM = Object.freeze({\n  id:'treasure_side_room',roomType:'treasure_side_room',cameraMode:'front-body',\n  countsTowardMainRooms:false,grantsBasicStatChoice:false,grimoireId:null,\n  phases:['jog-trap-chase','treasure-chests'],tutorialVersion:1,\n  tutorialSteps:[\n    {instruction:'Jog in place. Outrun the chasing trap.',graphicCue:'ghostpose'},\n    {instruction:'Reach the treasure room. Open its chests.',graphicCue:'swoopingcurvearrow'},\n    {instruction:'Find blueprints and drops for your camp.',graphicCue:'holdring'},\n    {instruction:'Bonus route: your main room count stays unchanged.',graphicCue:'holdring'},\n  ],\n});\n","modules/roguelite/room-handoff.mjs":"import {createFlightEffects} from '../coach-flight/transitions.mjs';\r\nimport {playGamePortal} from '../../arcade/game-portal.mjs';\r\n\r\n// Compose existing transition owners, rather than drawing another portal.\r\n// prepare() loads while the player walks. commit() swaps a prepared destination only under full cover.\r\nexport function createRoomHandoff(host,{\r\n reducedMotion=!!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches,\r\n effectsFactory=createFlightEffects,portalPlayer=playGamePortal,\r\n portalAvailable=()=>!globalThis.myr5Portal?.disposed&&typeof globalThis.myr5Portal?.playWormhole==='function',\r\n frame=()=>new Promise(resolve=>requestAnimationFrame(resolve)),\r\n onPause=()=>{},\r\n}={}){\r\n const effects=effectsFactory(host,{reduced:reducedMotion});\r\n let active=null,disposed=false;\r\n const aborted=()=>new DOMException('Room traversal cancelled.','AbortError');\r\n return {\r\n  async enter({prepare,commit,arrive,walk=Promise.resolve(),style='portal',material='training',signal,label='Entering next room'}={}){\n   if(disposed)throw aborted();\r\n   if(active)throw new Error('A room handoff is already active.');\r\n   if(typeof prepare!=='function'||typeof commit!=='function')throw new TypeError('A handoff needs prepare and commit.');\r\n   if(!['portal','pixel-flight'].includes(style))throw new RangeError('Unknown room transition style.');\n   if(!['training','crystal','papier_mache','jelly','grass','clay','dance','pond','treasure'].includes(material))throw new RangeError('Unknown portal material.');\n   const controller=new AbortController();active=controller;\r\n   const abort=()=>controller.abort(signal?.reason);\r\n   if(signal?.aborted)abort();else signal?.addEventListener('abort',abort,{once:true});\r\n   let payload,committed=false,cleaned=false,cover;\r\n   const check=()=>{if(controller.signal.aborted||disposed)throw aborted();};\r\n   const cleanupPayload=()=>{if(payload&&!committed&&!cleaned){cleaned=true;payload.dispose?.();}};\r\n   let rejectAbort;\r\n   const cancellation=new Promise((_,reject)=>{rejectAbort=reject;});cancellation.catch(()=>{});\r\n   const onAbort=()=>rejectAbort(aborted());controller.signal.addEventListener('abort',onAbort,{once:true});\r\n   const prepared=Promise.resolve().then(()=>{check();return prepare({signal:controller.signal});});\r\n   prepared.then(value=>{payload=value;if(controller.signal.aborted)cleanupPayload();},()=>{});\r\n   const race=promise=>Promise.race([promise,cancellation]);\r\n   try{\n    if(host.dataset)host.dataset.roomTransit=material;\n    check();onPause('camera-handoff',true);\r\n    await race(walk);check();\r\n    const covered=new Promise(resolve=>{cover=resolve;});\r\n    const ready=Promise.all([prepared,covered]).then(async([destination])=>{\r\n     check();await commit(destination,{signal:controller.signal});committed=true;\r\n     await race(frame());check(); // destination has rendered before reveal\r\n    });\r\n    ready.catch(()=>{});\r\n    if(style==='portal'&&!reducedMotion&&portalAvailable()){\r\n     await portalPlayer({host,label,swap:()=>{cover();return race(ready);}});\r\n    }else{\r\n     // Existing flight owner supplies pixel warp OR a steady reduced-motion/loading/error cover.\r\n     await effects.play('warp',{ready:race(ready),signal:controller.signal,onCover:()=>cover()});\r\n    }\r\n    check();\r\n    if(arrive)await race(arrive(payload,{signal:controller.signal})); // continue walking out of the portal\r\n    check();return payload;\r\n   }finally{\n    if(host.dataset)delete host.dataset.roomTransit;\n    controller.abort();cleanupPayload();\r\n    controller.signal.removeEventListener('abort',onAbort);signal?.removeEventListener('abort',abort);\r\n    if(active===controller)active=null;\r\n    onPause('camera-handoff',false);\r\n   }\r\n  },\r\n  dispose(){if(disposed)return;disposed=true;active?.abort();effects.dispose();},\r\n  get busy(){return active!==null;},\r\n };\r\n}\r\n","modules/roguelite/room-intro.mjs":"// modules/roguelite/room-intro.mjs\r\nimport { createWalkMotion } from './walk-motion.mjs';\r\n\r\nconst FINAL_POINT = [0, 2.1, 9];\r\nconst LOOK_TARGET = [0, 1.15, -1.2];\r\nconst finite = Number.isFinite;\r\n\r\nfunction abortError() {\r\n  return Object.assign(new Error('Room intro aborted.'), { name: 'AbortError' });\r\n}\r\n\r\n/**\r\n * Caller-driven room entrance animation.\r\n *\r\n * The coordinator owns the render loop, camera integration and scene lifetime.\r\n * This adapter only advances the authored camera path.\r\n */\r\nexport function createRoomIntro(THREE, camera, {\n  duration = 2.2,\r\n  reducedMotion = false,\r\n  signal,\r\n} = {}) {\r\n  if (!THREE?.Vector3 || !THREE?.Quaternion) {\r\n    throw new TypeError('THREE.Vector3 and THREE.Quaternion constructors are required');\r\n  }\r\n  if (!camera?.position || !camera?.quaternion || typeof camera.lookAt !== 'function') {\r\n    throw new TypeError('camera needs a Three.js position, quaternion and lookAt');\r\n  }\r\n  if (!finite(duration) || duration <= 0) {\r\n    throw new RangeError('duration must be finite and positive');\r\n  }\r\n  if (typeof reducedMotion !== 'boolean') {\r\n    throw new TypeError('reducedMotion must be boolean');\r\n  }\r\n\r\n  const finalPoint = new THREE.Vector3(...FINAL_POINT);\r\n  const lookTarget = new THREE.Vector3(...LOOK_TARGET);\r\n  let motion = null;\r\n  let state = 'active';\r\n  let resolveFinished;\r\n  let rejectFinished;\r\n\r\n  const finished = new Promise((resolve, reject) => {\r\n    resolveFinished = resolve;\r\n    rejectFinished = reject;\r\n  });\r\n  // Mark the rejection as handled internally. Consumers may still observe the\r\n  // rejection through `finished`, but an ignored cancellation never produces an\r\n  // unhandled rejection in the host process.\r\n  finished.catch(() => {});\r\n\r\n  function cleanAbortListener() {\r\n    signal?.removeEventListener?.('abort', onAbort);\r\n  }\r\n\r\n  function settleFinalPose() {\r\n    camera.position.copy(finalPoint);\r\n    camera.lookAt(lookTarget);\r\n  }\r\n\r\n  function finish() {\r\n    if (state !== 'active') return;\r\n    state = 'finished';\r\n    settleFinalPose();\r\n    cleanAbortListener();\r\n    resolveFinished();\r\n  }\r\n\r\n  function cancel() {\r\n    if (state !== 'active') return;\r\n    state = 'cancelled';\n    motion?.cancel();\n    settleFinalPose();\n    cleanAbortListener();\r\n    rejectFinished(abortError());\r\n  }\r\n\r\n  function onAbort() {\r\n    cancel();\r\n  }\r\n\r\n  if (signal?.aborted) {\r\n    cancel();\r\n  } else {\r\n    signal?.addEventListener?.('abort', onAbort, { once: true });\r\n  }\r\n\r\n  if (state === 'active' && reducedMotion) {\r\n    // Reduced motion settles immediately and never starts a traversal.\r\n    finish();\r\n  } else if (state === 'active') {\r\n    motion = createWalkMotion(THREE, camera, {\r\n      points: [\r\n        [3, 3.4, 3],\r\n        [-2, 2.8, 0],\r\n        FINAL_POINT,\r\n      ],\r\n      duration,\r\n      reducedMotion: false,\r\n      bobHeight: 0,\r\n    });\r\n  }\r\n\r\n  return {\r\n    finished,\r\n\r\n    update(deltaSeconds, { paused = false } = {}) {\r\n      if (!finite(deltaSeconds) || deltaSeconds < 0) {\r\n        throw new RangeError('deltaSeconds must be finite and non-negative');\r\n      }\r\n      if (typeof paused !== 'boolean') {\r\n        throw new TypeError('paused must be boolean');\r\n      }\r\n      if (state !== 'active' || !motion) return;\r\n\r\n      // The walk motion owns the 100 ms delta cap.\r\n      const result = motion.update(deltaSeconds, { paused });\r\n      camera.lookAt(lookTarget);\r\n      if (result.done) finish();\r\n    },\r\n\r\n    skip() {\r\n      if (state !== 'active') return;\r\n      motion?.cancel();\r\n      finish();\r\n    },\r\n\r\n    cancel,\r\n\r\n    getdone() {\r\n      return state !== 'active';\r\n    },\r\n\r\n    getprogress() {\r\n      if (state === 'finished') return 1;\r\n      return motion?.progress ?? 0;\r\n    },\r\n  };\r\n}\n\n// Actual-room views share this lease, never a clock or a simulation. The host\n// ticks it under its loading gate; replacing/disposal cancels the previous tour.\nexport function createViewTour(apply = () => {}, restore = () => {}) {\n  let lease = null, disposed = false;\n  return {\n    intro({duration = 1.8, reducedMotion = false, signal} = {}) {\n      if (disposed) throw new Error('Room tour is disposed');\n      if (!finite(duration) || duration <= 0) throw new RangeError('intro duration must be positive');\n      lease?.cancel();\n      let elapsed = 0, done = false, resolve, reject;\n      const finished = new Promise((yes, no) => { resolve = yes; reject = no; });\n      finished.catch(() => {});\n      const settle = cancelled => {\n        if (done) return;\n        done = true;\n        restore();\n        signal?.removeEventListener?.('abort', cancel);\n        if (cancelled) reject(abortError()); else resolve();\n      };\n      const cancel = () => settle(true);\n      lease = {\n        finished, cancel,\n        update(dt, {paused = false} = {}) {\n          if (!finite(dt) || dt < 0) throw new RangeError('intro delta must be nonnegative');\n          if (done || paused) return;\n          elapsed = Math.min(duration, elapsed + Math.min(.1, dt));\n          if (elapsed >= duration - 1e-9) settle(false);\n        },\n        skip: () => settle(false),\n        getdone: () => done,\n        getprogress: () => done ? 1 : elapsed / duration,\n      };\n      if (signal?.aborted) cancel();\n      else {\n        signal?.addEventListener?.('abort', cancel, {once: true});\n        if (reducedMotion) settle(false);\n      }\n      return lease;\n    },\n    apply(...args) { if (lease && !lease.getdone()) return apply(lease.getprogress(), ...args); },\n    cancel() { lease?.cancel(); },\n    dispose() { disposed = true; lease?.cancel(); },\n    get active() { return !!lease && !lease.getdone(); },\n  };\n}\n\n// Hook runs after the real game has positioned its camera. The renderer calls\n// the returned cleanup in finally, so even a failed draw leaves the game pose exact.\nexport function cameraOverview(camera, progress, {wide = 2.8, height = 2, forward = -4, point, target, blendToGame = true} = {}) {\n  const position = camera.position.clone(), quaternion = camera.quaternion.clone();\n  const fov = camera.fov;\n  const u = progress * progress * (3 - 2 * progress), retreat = 1 - u;\n  if (point && target) {\n    camera.position.set(...point);\n    if (blendToGame) camera.position.lerp(position,u);\n    camera.lookAt(...target);\n    if (blendToGame) camera.quaternion.slerp(quaternion,u);\n  } else {\n    camera.translateX(Math.cos(progress * Math.PI) * wide * retreat);\n    camera.translateY(height * retreat);\n    camera.translateZ(forward * retreat);\n    camera.rotateY(-.22 * Math.cos(progress * Math.PI) * retreat);\n    camera.rotateX(-.12 * retreat);\n  }\n  if (finite(fov)) { camera.fov = fov + 12 * retreat; camera.updateProjectionMatrix(); }\n  camera.updateMatrixWorld?.(true);\n  return () => {\n    camera.position.copy(position); camera.quaternion.copy(quaternion);\n    if (finite(fov)) { camera.fov = fov; camera.updateProjectionMatrix(); }\n    camera.updateMatrixWorld?.(true);\n  };\n}\n\n// A bounded delay between received numeric endpoints, never an extrapolator.\n// Callers select visual fields; scores, collisions and reward flags stay in ACKs.\nexport function createAcknowledgedMotion() {\n  let target = null, from = null, shown = null, identity, clock, age = .2, snapped = false;\n  return {\n    push(values, {identity: nextIdentity, clock: nextClock, snap = false} = {}) {\n      const changed = !target || Object.keys(values).some(key => values[key] !== target[key]);\n      if (!changed && identity === nextIdentity && clock === nextClock && !snap) return;\n      const reset = !target || snap || snapped || identity !== nextIdentity ||\n        !finite(nextClock) || !finite(clock) || nextClock <= clock || nextClock - clock > .75;\n      from = Object.fromEntries(Object.keys(values).map(key => [key, reset || !finite(shown?.[key]) ? values[key] : shown[key]]));\n      target = {...values}; shown = {...from}; age = reset ? .2 : 0;\n      identity = nextIdentity; clock = nextClock; snapped = false;\n    },\n    sample(dt = 0, {paused = false, reducedMotion = false} = {}) {\n      if (!target) return null;\n      if (paused || reducedMotion) { age = .2; snapped = true; }\n      else age = Math.min(.2, age + Math.min(.1, Math.max(0, finite(dt) ? dt : 0)));\n      const u = age / .2;\n      for (const key of Object.keys(target)) shown[key] = from[key] + (target[key] - from[key]) * u;\n      return shown;\n    },\n  };\n}\n","modules/roguelite/room-unlocks.mjs":"// Pure campaign unlock policy. Callers own persistence, UI, and reward application.\r\nimport { ROOM_BY_ID } from './room-contracts.mjs';\r\n\r\nexport const UNLOCK_ROOMS_BY_STAGE = Object.freeze({\r\n  1: Object.freeze(['pond', 'grass', 'papier_mache']),\r\n  2: Object.freeze(['clay']),\r\n  3: Object.freeze(['dance']),\r\n  4: Object.freeze(['jelly']),\r\n  5: Object.freeze(['wood']),\r\n  6: Object.freeze(['flat_paper']),\r\n});\r\n\r\nconst STAGES = Object.freeze([\r\n  Object.freeze({ stage: 1, kind: 'miniboss', count: 1 }),\r\n  Object.freeze({ stage: 2, kind: 'miniboss', count: 2 }),\r\n  Object.freeze({ stage: 3, kind: 'mainboss', count: 1 }),\r\n  Object.freeze({ stage: 4, kind: 'mainboss', count: 2 }),\r\n  Object.freeze({ stage: 5, kind: 'completedRuns', count: 5 }),\r\n  Object.freeze({ stage: 6, kind: 'completedRuns', count: 10 }),\r\n]);\r\nconst COLLECTIBLE_IDS = new Set(Object.values(UNLOCK_ROOMS_BY_STAGE).flat());\r\nconst KNOWN_ROOM_IDS = new Set(Object.keys(ROOM_BY_ID));\r\nconst SAFE_ID = /^[A-Za-z0-9:_-]{1,128}$/;\r\n\r\nconst clone = value => structuredClone(value);\r\nconst validId = value => typeof value === 'string' && SAFE_ID.test(value);\r\nconst fail = message => { throw new RangeError(message); };\r\n\r\nexport function createRoomUnlockProgress() {\r\n  return { version: 1, bossVictories: [], completedRuns: [], discoveries: [], quiltEncounters: [] };\r\n}\r\n\r\nfunction assertProgress(progress) {\r\n  if (!progress || progress.version !== 1 || !Array.isArray(progress.bossVictories)\r\n      || !Array.isArray(progress.completedRuns) || !Array.isArray(progress.discoveries)\r\n      || !Array.isArray(progress.quiltEncounters)) throw new TypeError('invalid room unlock progress');\r\n}\r\n\r\nfunction receiptIds(progress) {\r\n  return [\r\n    ...progress.bossVictories, ...progress.completedRuns, ...progress.discoveries,\r\n    ...progress.quiltEncounters.flatMap(event => [event.receiptId, event.choiceReceiptId].filter(Boolean)),\r\n  ].map(entry => typeof entry === 'string' ? entry : entry.receiptId);\r\n}\r\n\r\nfunction assertFreshReceipt(progress, receiptId) {\r\n  if (!validId(receiptId)) throw new TypeError('receiptId must be a stable string id');\r\n  if (receiptIds(progress).includes(receiptId)) fail('receiptId has already been used');\r\n}\r\n\r\nfunction validateEventLists(progress) {\r\n  const receipts = new Set(), encounterIds = new Set(), runIds = new Set(), discoveries = new Set(), quilts = new Set();\r\n  const receipt = id => {\r\n    if (!validId(id) || receipts.has(id)) fail('invalid or duplicate receiptId');\r\n    receipts.add(id);\r\n  };\r\n  for (const event of progress.bossVictories) {\r\n    if (!event || !['miniboss', 'mainboss'].includes(event.kind) || !validId(event.encounterId)) fail('invalid boss victory');\r\n    if (encounterIds.has(event.encounterId)) fail('duplicate boss encounter');\r\n    encounterIds.add(event.encounterId); receipt(event.receiptId);\r\n  }\r\n  for (const event of progress.completedRuns) {\r\n    if (!event || !validId(event.runId) || runIds.has(event.runId)) fail('invalid or duplicate completed run');\r\n    runIds.add(event.runId); receipt(event.receiptId);\r\n  }\r\n  for (const event of progress.discoveries) {\r\n    if (!event || !COLLECTIBLE_IDS.has(event.roomId) || discoveries.has(event.roomId)) fail('invalid or duplicate room discovery');\r\n    discoveries.add(event.roomId); receipt(event.receiptId);\r\n  }\r\n  for (const event of progress.quiltEncounters) {\r\n    if (!event || !validId(event.encounterId) || quilts.has(event.encounterId)\r\n        || !validId(event.receiptId) || typeof event.challengeEarned !== 'boolean'\r\n        || event.outcome !== (event.challengeEarned ? 'portal' : 'wall') || !Array.isArray(event.offers)) {\r\n      fail('invalid or duplicate quilt encounter');\r\n    }\r\n    quilts.add(event.encounterId); receipt(event.receiptId);\r\n    if (event.challengeEarned) {\r\n      if (event.offers.length !== 2) fail('earned quilt challenge requires exactly two bundles');\r\n      const offerIds = new Set();\r\n      for (const offer of event.offers) {\r\n        if (!offer || !validId(offer.id) || offerIds.has(offer.id)\r\n            || !offer.boon || !validId(offer.boon.id) || offer.boon.tier !== 'extreme'\r\n            || typeof offer.boon.description !== 'string' || !offer.boon.description.trim()\r\n            || !offer.drawback || !validId(offer.drawback.id) || offer.drawback.tier !== 'heavy'\r\n            || typeof offer.drawback.description !== 'string' || !offer.drawback.description.trim()) {\r\n          fail('quilt bundles must pair an extreme boon with a heavy drawback');\r\n        }\r\n        offerIds.add(offer.id);\r\n      }\r\n      if ((event.choiceId == null) !== (event.choiceReceiptId == null)) fail('invalid quilt bundle choice receipt');\r\n      if (event.choiceId != null) {\r\n        if (!offerIds.has(event.choiceId)) fail('quilt choice is not one of the offered bundles');\r\n        receipt(event.choiceReceiptId);\r\n      }\r\n    } else if (event.offers.length !== 0 || event.choiceId != null || event.choiceReceiptId != null) {\r\n      fail('failed quilt challenge cannot offer or grant a bundle');\r\n    }\r\n  }\r\n  const counts = {\r\n    miniboss: progress.bossVictories.filter(event => event.kind === 'miniboss').length,\r\n    mainboss: progress.bossVictories.filter(event => event.kind === 'mainboss').length,\r\n    completedRuns: progress.completedRuns.length,\r\n  };\r\n  let stage = 0;\r\n  for (const milestone of STAGES) {\r\n    if (counts[milestone.kind] < milestone.count) break;\r\n    stage = milestone.stage;\r\n  }\r\n  const eligible = new Set(Object.entries(UNLOCK_ROOMS_BY_STAGE)\r\n    .flatMap(([number, ids]) => Number(number) <= stage ? ids : []));\r\n  if (progress.discoveries.some(event => !eligible.has(event.roomId))) fail('discovery is ahead of unlocked stage');\r\n  return progress;\r\n}\r\n\r\nfunction checked(progress) {\r\n  assertProgress(progress);\r\n  return validateEventLists(progress);\r\n}\r\n\r\nexport function restoreRoomUnlockProgress(saved) {\r\n  return checked(clone(saved));\r\n}\r\n\r\nconst countsOf = progress => ({\r\n  miniboss: progress.bossVictories.filter(event => event.kind === 'miniboss').length,\r\n  mainboss: progress.bossVictories.filter(event => event.kind === 'mainboss').length,\r\n  completedRuns: progress.completedRuns.length,\r\n});\r\n\r\n/** Derive only the longest contiguous milestone prefix; later events can queue. */\r\nexport function unlockedRoomStage(progress) {\r\n  checked(progress);\r\n  const counts = countsOf(progress);\r\n  let unlocked = 0;\r\n  for (const milestone of STAGES) {\r\n    if (counts[milestone.kind] < milestone.count) break;\r\n    unlocked = milestone.stage;\r\n  }\r\n  return unlocked;\r\n}\r\n\r\nexport function eligibleRoomIds(progress) {\r\n  const stage = unlockedRoomStage(progress);\r\n  return Object.entries(UNLOCK_ROOMS_BY_STAGE).flatMap(([number, ids]) => Number(number) <= stage ? [...ids] : []);\r\n}\r\n\r\nexport function isGrimoireOwned(progress, roomId) {\r\n  checked(progress);\r\n  if (!KNOWN_ROOM_IDS.has(roomId)) throw new RangeError(`unknown room id: ${roomId}`);\r\n  return roomId === 'quilt' || progress.discoveries.some(event => event.roomId === roomId);\r\n}\r\n\r\n/** This policy gates collectible grimoires only. Exercises, boss entrances, and other rooms remain caller-owned. */\r\nexport function canEnterRoom(progress, roomId) {\r\n  checked(progress);\r\n  if (!KNOWN_ROOM_IDS.has(roomId)) throw new RangeError(`unknown room id: ${roomId}`);\r\n  // A newly eligible room must be enterable BEFORE discovery grants its grimoire.\r\n  return !COLLECTIBLE_IDS.has(roomId) || eligibleRoomIds(progress).includes(roomId);\r\n}\r\n\r\nexport function discoverRoom(progress, { roomId, receiptId } = {}) {\r\n  checked(progress);\r\n  if (!COLLECTIBLE_IDS.has(roomId)) throw new RangeError(`room is not a collectible grimoire: ${roomId}`);\r\n  if (progress.discoveries.some(event => event.roomId === roomId)) {\r\n    return { progress: clone(progress), discovered: false, duplicate: true, reason: 'already-owned' };\r\n  }\r\n  if (!eligibleRoomIds(progress).includes(roomId)) {\r\n    return { progress: clone(progress), discovered: false, duplicate: false, reason: 'locked' };\r\n  }\r\n  assertFreshReceipt(progress, receiptId);\r\n  const next = clone(progress);\r\n  next.discoveries.push({ roomId, receiptId });\r\n  return { progress: next, discovered: true, duplicate: false, reason: null };\r\n}\r\n\r\nexport function recordBossVictory(progress, { kind, encounterId, receiptId } = {}) {\r\n  checked(progress);\r\n  if (!['miniboss', 'mainboss'].includes(kind)) throw new RangeError('kind must be miniboss or mainboss');\r\n  if (!validId(encounterId)) throw new TypeError('encounterId must be a stable string id');\r\n  if (progress.bossVictories.some(event => event.encounterId === encounterId)) {\r\n    return { progress: clone(progress), recorded: false, duplicate: true };\r\n  }\r\n  assertFreshReceipt(progress, receiptId);\r\n  const next = clone(progress);\r\n  next.bossVictories.push({ kind, encounterId, receiptId });\r\n  return { progress: next, recorded: true, duplicate: false };\r\n}\r\n\r\nexport function recordCompletedRun(progress, { runId, receiptId } = {}) {\r\n  checked(progress);\r\n  if (!validId(runId)) throw new TypeError('runId must be a stable string id');\r\n  if (progress.completedRuns.some(event => event.runId === runId)) {\r\n    return { progress: clone(progress), recorded: false, duplicate: true };\r\n  }\r\n  assertFreshReceipt(progress, receiptId);\r\n  const next = clone(progress);\r\n  next.completedRuns.push({ runId, receiptId });\r\n  return { progress: next, recorded: true, duplicate: false };\r\n}\r\n\r\n/** Store the caller's challenge result and two paired offers without applying effects. */\r\nexport function resolveQuiltEncounter(progress, {\r\n  encounterId, receiptId, challengeEarned, offers = [],\r\n} = {}) {\r\n  checked(progress);\r\n  if (!validId(encounterId)) throw new TypeError('encounterId must be a stable string id');\r\n  const prior = progress.quiltEncounters.find(event => event.encounterId === encounterId);\r\n  if (prior) return { progress: clone(progress), outcome: prior.outcome, offers: clone(prior.offers), duplicate: true };\r\n  if (typeof challengeEarned !== 'boolean') throw new TypeError('challengeEarned must be supplied by the challenge resolver');\r\n  if (!Array.isArray(offers)) throw new TypeError('offers must be an array');\r\n  if (!challengeEarned && offers.length) throw new RangeError('wall outcome cannot carry quilt offers');\r\n  assertFreshReceipt(progress, receiptId);\r\n  const next = clone(progress);\r\n  const event = { encounterId, receiptId, challengeEarned, outcome: challengeEarned ? 'portal' : 'wall', offers: challengeEarned ? clone(offers) : [], choiceId: null, choiceReceiptId: null };\r\n  next.quiltEncounters.push(event);\r\n  checked(next);\r\n  return { progress: next, outcome: event.outcome, offers: clone(event.offers), duplicate: false };\r\n}\r\n\r\n/** Claim one of the two saved bundles once. Effects remain owned by the caller. */\r\nexport function chooseQuiltBundle(progress, { encounterId, choiceId, receiptId } = {}) {\r\n  checked(progress);\r\n  const event = progress.quiltEncounters.find(item => item.encounterId === encounterId);\r\n  if (!event) return { progress: clone(progress), chosen: false, duplicate: false, reason: 'unresolved' };\r\n  if (!event.challengeEarned) return { progress: clone(progress), chosen: false, duplicate: false, reason: 'challenge-locked' };\r\n  if (!validId(choiceId)) throw new TypeError('choiceId must be a stable string id');\r\n  const offer = event.offers.find(item => item.id === choiceId);\r\n  if (!offer) return { progress: clone(progress), chosen: false, duplicate: false, reason: 'unknown-choice' };\r\n  if (event.choiceId != null) {\r\n    const selected = event.offers.find(item => item.id === event.choiceId);\r\n    return { progress: clone(progress), chosen: false, duplicate: event.choiceId === choiceId, reason: event.choiceId === choiceId ? 'already-chosen' : 'choice-already-claimed', selected: clone(selected) };\r\n  }\r\n  assertFreshReceipt(progress, receiptId);\r\n  const next = clone(progress);\r\n  const saved = next.quiltEncounters.find(item => item.encounterId === encounterId);\r\n  saved.choiceId = choiceId;\r\n  saved.choiceReceiptId = receiptId;\r\n  checked(next);\r\n  return { progress: next, chosen: true, duplicate: false, reason: null, selected: clone(offer), firstGrantReceipt: receiptId };\r\n}\r\n","modules/roguelite/run-authored-rooms.mjs":"// Shared authenticated host composition. Leaf policies never become campaign authority.\nimport {createYogaRoom,restoreYogaRoom,stepYogaRoom,YOGA_POSES,YOGA_COMPLETION_EFFECT} from './yoga-room.mjs';\nimport {createSecretRoom,restoreSecretRoom,stepSecretRoom,SECRET_OFFERS} from './secret-room.mjs';\nimport {createTwoAltarsRoom,restoreTwoAltarsRoom,stepTwoAltarsRoom,resetTwoAltarsProposal} from './two-altars-room.mjs';\nimport {grantBoon} from './boons.mjs';\nimport {chooseRunStat} from './run-stats.mjs';\nimport {awardRoomXp} from './run-xp.mjs';\n\nconst clone=value=>structuredClone(value);\nconst adapters={yoga:[createYogaRoom,restoreYogaRoom,stepYogaRoom],secret:[createSecretRoom,restoreSecretRoom,stepSecretRoom],altar:[createTwoAltarsRoom,restoreTwoAltarsRoom,stepTwoAltarsRoom]};\nconst types={nature_yoga:'yoga',secret_room:'secret',two_altars:'altar'};\nconst keys=(value,list)=>{if(!value||typeof value!=='object'||Array.isArray(value)||Object.keys(value).sort().join(',')!==[...list].sort().join(','))throw new TypeError('Invalid authored room sample fields');};\nconst bool=(value,key)=>{if(typeof value[key]!=='boolean')throw new TypeError(`Invalid authored room ${key}`);};\nexport function authoredRoomKind(node){return types[node?.roomType]??null;}\n\n/** Exact room-only transport DTO; the host supplies time, identity and quality. */\nexport function normalizeAuthoredSample(kind,sample){\n if(kind==='yoga'){\n  keys(sample,['push','pose','eligible']);bool(sample,'push');bool(sample,'eligible');\n  if(sample.pose!==null&&!YOGA_POSES.includes(sample.pose))throw new TypeError('Invalid yoga pose');\n }else if(kind==='secret'){\n  keys(sample,['handsLow','pinching','pullUp','jogging','action','choiceId']);\n  for(const key of ['handsLow','pinching','pullUp','jogging'])bool(sample,key);\n  if(![null,'choose','decline','continue'].includes(sample.action)||sample.action==='choose'&&!SECRET_OFFERS.some(offer=>offer.id===sample.choiceId)||sample.action!=='choose'&&sample.choiceId!==null)throw new TypeError('Invalid secret action');\n }else if(kind==='altar'){\n  keys(sample,['target','gesture','y','handId']);\n  if(![null,'paper','left','right','exit'].includes(sample.target)||!['none','other','open','pinch'].includes(sample.gesture))throw new TypeError('Invalid altar target or gesture');\n  if(sample.gesture==='none'){if(sample.target!==null||sample.y!==null||sample.handId!==null)throw new TypeError('Absent altar hand has data');}\n  else if(!Number.isFinite(sample.y)||sample.y<0||sample.y>1||typeof sample.handId!=='string'||!sample.handId.length||sample.handId.length>64||sample.gesture==='other'&&sample.target!==null)throw new TypeError('Invalid altar hand');\n }else throw new TypeError('Unknown authored room');\n return clone(sample);\n}\n\nexport function createAuthoredRoom(node){\n const kind=authoredRoomKind(node);if(!kind)throw new TypeError('Not an authored room');\n const seed=String(node.id).split('').reduce((value,char)=>(value*31+char.charCodeAt(0))>>>0,1);\n return {kind,state:adapters[kind][0]({encounterId:node.id,seed})};\n}\nexport function restoreAuthoredRoom(saved,node){\n keys(saved,['kind','state']);const kind=authoredRoomKind(node);\n if(!kind||saved.kind!==kind||saved.state?.encounterId!==node.id)throw new TypeError('Authored room checkpoint binding mismatch');\n return {kind,state:adapters[kind][1](saved.state)};\n}\n\n/** Must run inside the same host transaction as its accepted frame and receipt. */\nexport function stepAuthoredRoom({runState,room,observationId,now,tracked,paused=false,sample}={}){\n const node=runState?.schedule?.nodes?.[runState.cursor],kind=authoredRoomKind(node);\n if(!kind||room?.kind!==kind||room.state?.encounterId!==node.id)throw new TypeError('Authored room checkpoint binding mismatch');\n sample=normalizeAuthoredSample(kind,sample);\n const before=room.state,blocked=paused||runState.phase!=='active'||runState.pauseReasons.length>0||runState.stats.health<=0;\n const input={observationId,now,tracked,paused:blocked,...(kind==='altar'?{altar:tracked?sample:{target:null,gesture:'none',y:null,handId:null}}:sample)};\n const result=adapters[kind][2](before,input),events=clone(result.events);\n let next=clone(runState),nextRoom={kind,state:result.state},creditMs=0;\n if(!blocked&&tracked&&!result.duplicate){\n  if(kind==='yoga')creditMs=Math.max(0,result.state.stageMs.reduce((a,b)=>a+b,0)-before.stageMs.reduce((a,b)=>a+b,0));\n  if(kind==='secret')creditMs=Math.max(0,result.state.jogMs-before.jogMs);\n }\n const complete=events.find(event=>['room-complete','secret-completed','proposed-outcome','declined'].includes(event.type));\n if(!complete||blocked||!tracked||next.completion?.roomId===node.id)return {runState:next,room:nextRoom,events,creditMs};\n const receiptId=`authored:${node.id}`;\n if(kind==='altar'&&complete.type==='proposed-outcome'){\n  const proposal=complete.outcome,armor=proposal.offerId==='armor-for-damage';\n  const affordable=armor?Number.isFinite(next.stats.armor)&&next.stats.armor>20:Number.isFinite(next.runCoins)&&next.runCoins>=10;\n  if(!affordable){\n   nextRoom.state=resetTwoAltarsProposal(nextRoom.state);\n   events.push({type:'proposal-rejected',encounterId:node.id,reason:'insufficient-resources'});\n   return {runState:next,room:nextRoom,events,creditMs};\n  }\n  if(armor){next.stats.armor-=20;next.stats=chooseRunStat(next.stats,receiptId,'damage');}\n  else {next.runCoins-=10;next.boons=grantBoon(next.boons,'longerParry',receiptId).inventory;}\n }\n if(kind==='yoga')next.boons=grantBoon(next.boons,YOGA_COMPLETION_EFFECT.boonId,receiptId).inventory;\n if(kind==='secret'&&complete.effect){\n  next.secretEffects??=[];\n  if(!next.secretEffects.some(item=>item.receiptId===receiptId))next.secretEffects.push({receiptId,choiceId:complete.choiceId,effect:clone(complete.effect)});\n }\n const award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'full-game'});\n next.xp=award.state;next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,outcome:complete.outcome??complete.type,authoredKind:kind};\n next.phase='walk';next.choice=null;next.choiceSince=null;\n events.push({type:'authored-completed',encounterId:node.id,kind,receiptId});\n return {runState:next,room:nextRoom,events,creditMs};\n}\n","modules/roguelite/run-controller.mjs":"// Local composition of the existing schedule/stat/XP/unlock reducers.\r\n// These receipts are NOT authoritative workout or permanent-wallet proof.\r\nimport {createWarmupProgress,resolveWarmup,resolveIntroBoss,restoreWarmupProgress} from './warmup-flow.mjs';\r\nimport {createRunStats,chooseRunStat,restoreRunStats} from './run-stats.mjs';\r\nimport {createBoonInventory,restoreBoonInventory} from './boons.mjs';\r\nimport {createRunXp,awardRoomXp,restoreRunXp} from './run-xp.mjs';\r\nimport {createRoomUnlockProgress,restoreRoomUnlockProgress,recordBossVictory,recordCompletedRun,canEnterRoom,discoverRoom} from './room-unlocks.mjs';\r\nimport {restoreCoinChaseState} from './coin-chase-state.mjs';\r\nimport {restoreBossState,returnBoss} from './boss-state.mjs';\r\nimport {restorePondRoom,POND_TUNING_PROPOSAL} from './pond-room.mjs';\r\nimport {createRunShop,restoreRunShop,enterRunShop,grabRunShop,returnRunShop,checkoutRunShop,exitRunShop,attackRunShop,advanceRunShop,settleRunShopPurchases,runShopOffers} from './run-shop.mjs';\nimport {beginStatEncounter,applyStatImpact} from './run-stats.mjs';\r\nimport {createRunScheduleForVersion} from './run-schedule-v2-builder.mjs';\r\nimport {ROOM_BY_ID} from './room-contracts.mjs';\r\nimport {checkpointGameRoom} from './game-room-adapters.mjs';\r\nimport {createDanceRoom} from './dance-room.mjs';\r\nimport {restoreTreasureChase} from './treasure-room.mjs';\r\nimport {resolveOwnedWeapon} from './weapon-style-catalog.mjs';\r\nimport {validateRunLoadout} from './run-loadout.mjs';\r\nimport {CLASS_IDS} from './class-combat.mjs';\nimport {createHeartModifiers,restoreHeartModifiers,expireRunWearables} from './coach-hearts.mjs';\nimport {RESCUE_ROLE_IDS} from './run-schedule-v4.mjs';\nimport {bossCombatOptions} from './boss-run-benefits.mjs';\n\r\nconst copy=value=>structuredClone(value);\r\nconst id=value=>typeof value==='string'&&/^[A-Za-z0-9:_-]{1,96}$/.test(value);\r\nconst arcadeSeed=nodeId=>String(nodeId).split('').reduce((seed,char)=>(seed*31+char.charCodeAt(0))>>>0,1);\r\nexport const RUN_PAUSE_REASONS=Object.freeze(['tracking','tutorial','background','camera-handoff','user','loading']);\r\nexport function currentRunRoom(state){return state.schedule.nodes[state.cursor]??null;}\r\nconst extraWarmups=schedule=>schedule.nodes.filter(node=>node.type==='warmup'&&!/^intro-/.test(node.id)).map(node=>node.id);\r\nexport function createRunSession({runId,accountId,dataEpoch=null,seed=0,exerciseIds=[],initialRun=null,campaign=null,tutorialsEnabled=true,scheduleVersion=1,assignment,manifest,rulesVersion=1,loadout,combatClass=null,heartId=null,rescuedCoachIds=[]}={}){\n if(![1,2].includes(rulesVersion)||(rulesVersion===2)!==([3,4].includes(scheduleVersion)))throw new TypeError('Unsupported rules/schedule combination');\n if(rulesVersion===2){loadout=validateRunLoadout(loadout,{weaponIds:initialRun?.weaponIds??[],galaWeaponTiers:initialRun?.galaWeaponTiers??{},relicIds:initialRun?.relicIds??[]});combatClass=loadout.styleId;}\n if(scheduleVersion===4&&loadout.relicIds.length)throw new TypeError('Rescue runs equip one CoachHeart instead of legacy relics');\n if(combatClass!==null&&!CLASS_IDS.includes(combatClass))throw new TypeError('Unknown combat class');\r\n if(!id(runId)||typeof accountId!=='string'||!accountId||!(dataEpoch===null||Number.isSafeInteger(dataEpoch)&&dataEpoch>=0))throw new TypeError('Run owner and stable run ID required');\r\n if(scheduleVersion===1&&assignment!==undefined)throw new TypeError('A V2 assignment requires explicit scheduleVersion 2');\r\n if(initialRun&&(initialRun.id!==runId||initialRun.accountId!==accountId||initialRun.dataEpoch!==dataEpoch))throw new Error('Starting benefits belong to another run owner');\r\n const schedule=createRunScheduleForVersion(scheduleVersion,{seed,exerciseIds,assignment,manifest});\r\n const state={version:1,id:runId,accountId,dataEpoch,seed:String(seed),exerciseIds:[...exerciseIds],combatClass,combatGearTier:rulesVersion===2?resolveOwnedWeapon(loadout.weaponId,{weaponIds:initialRun?.weaponIds??[],galaWeaponTiers:initialRun?.galaWeaponTiers??{}}).combatTier:0,\r\n  schedule,cursor:0,phase:tutorialsEnabled?'tutorial':'active',\r\n  warmups:createWarmupProgress(seed),stats:copy(initialRun?.stats??createRunStats({rulesVersion})),\r\n  boons:restoreBoonInventory(initialRun?.boons??createBoonInventory()),xp:createRunXp(runId),\r\n  campaign:restoreRoomUnlockProgress(campaign??createRoomUnlockProgress()),\r\n  tutorials:{enabled:!!tutorialsEnabled,seen:[]},pauseReasons:[],roomValue:0,lastValue:0,lastObservation:-1,lastNow:null,\r\n  elapsedMs:0,choice:null,choiceSince:null,completion:null,unlockedCoaches:[],campaignPacks:[],escapeDetour:null,routes:[],runCoins:0,shopLedger:null,shopDoorsOpened:[]};\r\n if(rulesVersion===2){Object.assign(state,{rulesVersion,loadout,activeRunMs:0,reaperStarted:false,escapeHistory:[]});state.warmups.activeWarmupIds=extraWarmups(schedule);if(state.stats.rulesVersion!==2)throw new TypeError('Rules require armor stats');}\n if(scheduleVersion===4){\n  state.heartModifiers=createHeartModifiers({heartId,rescuedCoachIds,baseArmorMax:state.stats.armorMax});\n  if(state.stats.armor!==state.stats.armorMax||state.stats.armorBroken||state.stats.health!==1||state.stats.encounter!==null||state.stats.choices.length)throw new TypeError('Heart initialization requires fresh armor');\n  state.stats.armorMax+=state.heartModifiers.initialArmorBonus;state.stats.armor=state.stats.armorMax;\n  state.secretEffects=[];state.reentryReceipts=[];\n }\n return state;\r\n}\r\nconst tutorialKey=node=>node.type==='warmup'?'basic':node.roomType??node.type;\r\nexport function setRunTutorials(state,enabled){\r\n if(typeof enabled!=='boolean')throw new TypeError('tutorial setting must be boolean');\r\n const next=copy(state);next.tutorials.enabled=enabled;\r\n if(!enabled&&next.phase==='tutorial')next.phase='active';\r\n return next;\r\n}\r\nexport function finishRunTutorial(state){\r\n if(state.phase!=='tutorial')return copy(state);\r\n const next=copy(state),key=tutorialKey(currentRunRoom(state));\r\n if(!next.tutorials.seen.includes(key))next.tutorials.seen.push(key);\r\n next.phase='active';next.lastNow=null;return next;\r\n}\r\nexport function setRunPaused(state,reason,paused){\r\n if(!RUN_PAUSE_REASONS.includes(reason)||typeof paused!=='boolean')throw new TypeError('Invalid pause reason');\r\n const next=copy(state),reasons=new Set(next.pauseReasons);\r\n if(paused)reasons.add(reason);else reasons.delete(reason);\r\n next.pauseReasons=[...reasons];next.choice=null;next.choiceSince=null;next.lastNow=null;\r\n return next;\r\n}\r\nexport function roomTarget(node){return node?.reps??node?.holdSeconds??node?.seconds??0;}\r\nexport function settleRunHealth(state){const next=copy(state);if(next.stats.health<=0){next.phase=next.schedule.scheduleVersion===4?'captured':'defeated';next.choice=null;next.choiceSince=null;if(next.heartModifiers){next.heartModifiers=expireRunWearables(next.heartModifiers);next.runCoins=0;if(next.shopLedger)next.shopLedger.coins=0;}}return next;}\nfunction completeBasic(state){\r\n const next=copy(state),node=currentRunRoom(next);\r\n const result=resolveWarmup(next.warmups,node.id);next.warmups=result.progress;\r\n const award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'basic'});\r\n next.xp=award.state;next.phase='upgrade';next.choice=null;next.choiceSince=null;\r\n next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements};return next;\r\n}\r\n/** MovementSession supplies cumulative count/held/active seconds. Never count wall time as exercise. */\r\nexport function observeRunRoom(state,{observationId,now,tracked,value=0,choice=null}={}){\r\n if(!Number.isSafeInteger(observationId)||observationId<0||!Number.isFinite(now)||!Number.isFinite(value)||value<0\r\n    ||typeof tracked!=='boolean'||![null,'left','right','crouch'].includes(choice))throw new TypeError('Invalid room observation');\r\n if(observationId<=state.lastObservation)return copy(state);\r\n if(state.stats.health<=0)return settleRunHealth(state);\r\n const next=copy(state),node=currentRunRoom(next);\r\n const dt=next.lastNow===null?0:Math.min(100,Math.max(0,now-next.lastNow));\r\n next.lastObservation=observationId;next.lastNow=now;\r\n const delta=Math.max(0,value-next.lastValue);next.lastValue=value;\r\n if(!tracked||next.pauseReasons.length||!['active','upgrade'].includes(next.phase)){\r\n  next.choice=null;next.choiceSince=null;return next;\r\n }\r\n next.elapsedMs+=dt;\r\n if(next.phase==='active'&&node?.type==='warmup'){\r\n  next.roomValue=Math.min(roomTarget(node),next.roomValue+Math.min(delta,node.reps?1:dt/1000));\r\n  if(next.roomValue+1e-8>=roomTarget(node))return completeBasic(next);\r\n }\r\n if(next.phase==='upgrade'){\r\n  // A held direction cannot fire across pauses, tutorials or room transitions.\r\n  if(choice===null){next.choice=null;next.choiceSince=null;}\r\n  else if(next.choice!==choice){next.choice=choice;next.choiceSince=next.elapsedMs;}\r\n  else if(next.elapsedMs-next.choiceSince>=700){\r\n   next.stats=chooseRunStat(next.stats,node.id,{left:'health',right:'damage',crouch:'luck'}[choice]);\r\n   next.phase='walk';next.choice=null;next.choiceSince=null;\r\n  }\r\n }\r\n return next;\r\n}\r\n/** Only the existing boss FSM's completed chip finisher resolves the boss room. */\r\nexport function resolveRunBoss(state,boss){\r\n const node=currentRunRoom(state);\r\n if(!node||!['miniboss','mainboss'].includes(node.type))throw new Error('Current room is not a boss');\r\n if(boss?.bossId!==node.id||boss.kind!==node.type)throw new Error('Boss does not match the scheduled encounter');\r\n if(state.phase==='walk'&&state.completion?.roomId===node.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length||boss.status!=='complete'||!boss.unlockReceipt||!boss.unlockedCoachId)\r\n  throw new Error('Boss chip finisher must complete before victory');\r\n const next=copy(state);\n if(next.rulesVersion===2){\n  const rescue=next.schedule.scheduleVersion===4,roleIndex=RESCUE_ROLE_IDS.indexOf(node.bossSlot);\n  const prefix=`level-${node.levelIndex}-`,count=rescue?[3,5,8,10,13,15][roleIndex]:node.bossSlot==='mini1'?5:node.bossSlot==='mini2'?10:15;\n  if(rescue&&roleIndex<0)throw new Error('Unknown rescue encounter role');\n  if(Array.from({length:count},(_,i)=>prefix+'intro-'+(i+1)).some(id=>!next.warmups.resolvedReceipts.includes(id)))throw new Error('Level warmups incomplete');\n  const earlier=rescue?RESCUE_ROLE_IDS.slice(0,roleIndex):node.bossSlot==='main'?['intro-miniboss','intro-miniboss-2']:node.bossSlot==='mini2'?['intro-miniboss']:[];\n  if(earlier.some(id=>!next.warmups.introBossReceipts.some(b=>b.bossId===prefix+id)))throw new Error('Earlier miniboss incomplete');\n  if(rescue&&(boss.coachId!==node.coachId||String(boss.seed)!==String(node.patternSeed)||node.type==='mainboss'&&(boss.phase!==2||boss.phase2Pending)))throw new Error('Rescue boss identity/phase mismatch');\n  if(next.warmups.introBossReceipts.some(b=>b.bossId===node.id||b.receiptId===boss.unlockReceipt))throw new Error('Boss receipt already resolved');\n  next.warmups.introBossReceipts.push({bossId:node.id,receiptId:boss.unlockReceipt});\n  if(rescue&&node.returning)next.escapeDetour=null;\n }else next.warmups=resolveIntroBoss(next.warmups,node.id,boss.unlockReceipt).progress;\r\n const scheduleVersion=state.schedule.scheduleVersion??1;\r\n if(scheduleVersion===1)next.campaign=recordBossVictory(next.campaign,{kind:node.type,encounterId:`${next.id}:${node.id}`,receiptId:boss.unlockReceipt}).progress;\r\n const award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`victory:${node.id}`,kind:node.type,encounterId:node.id,victory:true});\r\n next.xp=award.state;\r\n // Run equipment advances only after this verified finisher, never from elapsed time or XP.\r\n if(next.combatClass)next.combatGearTier=Math.min(2,(next.combatGearTier??0)+1);\r\n if(scheduleVersion===1&&!next.unlockedCoaches.includes(boss.unlockedCoachId))next.unlockedCoaches.push(boss.unlockedCoachId);\r\n next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,...(next.combatClass?{combatGearTier:next.combatGearTier}:{}),...(scheduleVersion>=2?{localCoachCandidate:node.canonicalCoachId}: {})};next.phase='walk';return next;\r\n}\r\n/** An escaped phase-one boss is pending, never a victory or reward receipt. */\r\nexport function escapeRunBoss(state,boss){\r\n const node=currentRunRoom(state);\r\n if(state.escapeDetour&&state.phase==='walk'&&state.completion?.escaped&&boss?.bossId===currentRunRoom(state)?.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length||node?.type!=='mainboss'||node.returning\r\n    ||boss?.bossId!==node.id||boss.kind!=='mainboss'||boss.status!=='escaped'||!boss.phase2Pending\r\n    ||boss.phase!==1||boss.unlockReceipt||state.escapeDetour)throw new Error('Only the scheduled escaped phase-one boss can open recovery rooms');\r\n const next=copy(state);next.escapeDetour={boss:restoreBossState(boss)};\r\n if(next.rulesVersion===2)next.escapeHistory.push(copy(boss));\r\n next.schedule=buildSchedule(next,boss,next.routes??[]);\r\n next.warmups.activeWarmupIds=extraWarmups(next.schedule);\r\n next.phase='walk';next.completion={roomId:node.id,xp:0,packs:[],escaped:true};return next;\r\n}\r\n/** The phase-two return is gated by the five actual recovery-room receipts. */\r\nexport function resumeRunBoss(state,{now}={}){\n const node=currentRunRoom(state),boss=state.escapeDetour?.boss;\n if(state.schedule.scheduleVersion===4){\n  if(!node?.returning||!boss||node.id!==boss.bossId||node.coachId!==boss.coachId||!state.reentryReceipts.includes(node.id+'-reentry'))throw new Error('Continue the encounter recovery before returning to the boss');\n  return restoreBossState(returnBoss(boss,{coachId:boss.coachId,phase:2,seed:boss.seed}),{now});\n }\n if(!node?.returning||!boss||node.coachId!==boss.coachId\r\n    ||Array.from({length:5},(_,i)=>`${state.rulesVersion===2?`level-${node.levelIndex}-`:\"\"}recovery-${i+1}`).some(room=>!state.warmups.resolvedReceipts.includes(room)))throw new Error('Complete all five recovery rooms before returning to the boss');\r\n return restoreBossState(returnBoss(boss,{coachId:boss.coachId,phase:2,seed:boss.seed}),{now});\r\n}\r\n/** Full exercise games retain three warmups; the non-workout shop is direct. */\r\nexport function chooseRunRoute(state,roomId){\r\n const node=currentRunRoom(state);\r\n if(state.stats.health<=0||state.phase!=='walk'||state.pauseReasons.length||!/^(?:level-[1-5]-)?intro-/.test(node?.id??'')||node.returning)throw new Error('Route choice requires a completed opening room');\r\n if(roomId==='treasure'&&node.type!=='warmup')throw new Error('Treasure routes follow a completed warm-up only');\r\n const prior=(state.routes??[]).find(route=>route.afterId===node.id);\r\n if(prior){if(prior.roomId!==roomId)throw new Error('This route is already chosen');return copy(state);}\r\n const allowed=state.schedule.scheduleVersion===4?['grass','pond','papier_mache','clay','dance','jelly','treasure','wood','quilt','flat_paper']:state.schedule.scheduleVersion>=2?['grass','pond','papier_mache','clay','dance','jelly','treasure']:['grass','pond','papier_mache'];\n if(roomId!==null&&(!allowed.includes(roomId)||roomId!=='treasure'&&!canEnterRoom(state.campaign,roomId)))throw new Error('Room is locked or not connected');\r\n // A completed last boss ends the run; it cannot insert a new encounter afterward.\r\n if(/(?:^|-)intro-mainboss$/.test(node.id))throw new Error('The final boss ends this run');\r\n const next=copy(state);next.routes??=[];next.routes.push({afterId:node.id,roomId});\r\n next.schedule=buildSchedule(next,next.escapeDetour?.boss,next.routes);\r\n next.warmups.activeWarmupIds=extraWarmups(next.schedule);return next;\r\n}\r\n/** Keep crouch available for the quilt; ordinary random room offers use left/right. */\r\nexport function runRouteOffers(state,{roomIds}={}){\r\n roomIds??=state.schedule.scheduleVersion===4?['pond','grass','papier_mache','treasure','wood','quilt','flat_paper']:state.schedule.scheduleVersion>=2?['pond','grass','papier_mache','treasure']:['pond','grass','papier_mache'];\n const node=currentRunRoom(state);\r\n if(state.phase!=='walk'||!/^(?:level-[1-5]-)?intro-/.test(node?.id??'')||node.returning||/(?:^|-)intro-mainboss$/.test(node.id)||(state.routes??[]).some(route=>route.afterId===node.id))return [];\r\n const allowed=state.schedule.scheduleVersion===4?['pond','grass','papier_mache','clay','dance','jelly','treasure','wood','quilt','flat_paper']:state.schedule.scheduleVersion>=2?['pond','grass','papier_mache','clay','dance','jelly','treasure']:['pond','grass','papier_mache'];\n if(!Array.isArray(roomIds)||roomIds.some(id=>!allowed.includes(id)))throw new TypeError('Unknown connected room selection');\r\n const available=[...new Set(roomIds)].filter(id=>id==='treasure'?node.type==='warmup':canEnterRoom(state.campaign,id));\r\n if(!available.length)return [];\r\n const undiscovered=available.filter(id=>!state.campaign.discoveries.some(event=>event.roomId===id));\r\n const candidates=undiscovered.length?undiscovered:available;\r\n const roomId=candidates[(state.cursor+node.id.length)%candidates.length];\r\n const offers={\r\n  papier_mache:{label:'CORGI SHOP',description:'Pick up your phone. Shop with your hand.'},\r\n  pond:{label:'HEALING WATERS',description:'Three warm-ups, then balance and restore health.',image:'/pod/worlds/meditation-waterfall.png'},\r\n  grass:{label:'COIN CHASE',description:'Three warm-ups, then lie down and chase.',image:'/pod/worlds/boards/grass-poster.webp'},\r\n  clay:{label:'CLAY MAZE',description:'Three warm-ups, then escape Coach Armie through the clay maze.'},\r\n  dance:{label:'DANCE FLOOR',description:'Three warm-ups, then outdance the rival coach.'},\r\n  jelly:{label:'CRISIS COMBAT',description:'Three warm-ups, then clear the pinned combat volume.'},\r\n  treasure:{label:'TREASURE CHASE',description:'Jog in place to reach the chest before the trap catches you.'},\n  wood:{label:'YOGA CLEARING',description:'Three calm poses in the forest clearing.'},\n  quilt:{label:'SECRET ROOM',description:'Pull open the curtain and earn a choice.'},\n  flat_paper:{label:'TWO ALTARS',description:'Choose a run-only trade, or leave.'},\n }[roomId];\r\n return [{id:'training',motion:'left',label:'TRAINING',description:'Continue your selected exercises.'},{id:roomId,motion:'right',...offers}];\r\n}\r\n/** The shop spends temporary run coins only; account weapon ownership is untouched. */\r\nexport function enterScheduledShop(state){\r\n const node=currentRunRoom(state);\r\n if(node?.roomType!=='corgi_shop')throw new Error('Current room is not a shop');\r\n const next=copy(state),ledger=next.shopLedger??createRunShop({runId:next.id,runCoins:next.runCoins,seed:next.seed,...(next.schedule.scheduleVersion===4?{policyVersion:4,combatClass:next.combatClass}:{})});\n next.shopLedger=enterRunShop(ledger,{shopId:node.id,runCoins:next.runCoins}).state;\r\n next.stats=beginStatEncounter(next.stats,node.id);return next;\r\n}\r\nexport function interactRunShop(state,{type,itemId,seconds=0}={}){\r\n const node=currentRunRoom(state);\r\n if(node?.roomType!=='corgi_shop'||!state.shopLedger)throw new Error('Enter the scheduled shop first');\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)return copy(state);\r\n if(type==='open-door'){const next=copy(state);if(!next.shopDoorsOpened.includes(node.id))next.shopDoorsOpened.push(node.id);return next;}\r\n if(!state.shopDoorsOpened.includes(node.id))return copy(state);\n if(state.schedule.scheduleVersion===4&&['grab','return'].includes(type)&&!runShopOffers(state.shopLedger,node.id).some(offer=>offer.id===itemId))throw new Error('Item was not issued for this Merchant visit');\n let next=copy(state);const actions={grab:grabRunShop,return:returnRunShop,checkout:checkoutRunShop,exit:exitRunShop,attack:attackRunShop};\n const result=type==='tick'?advanceRunShop(next.shopLedger,seconds,{paused:false}):actions[type]?.(next.shopLedger,node.id,itemId);\r\n if(!result)throw new TypeError('Unknown shop action');\r\n next.shopLedger=result.state;next.runCoins=result.runCoins;\n if(next.schedule.scheduleVersion===4)next=settleRunShopPurchases(next,state.shopLedger,next.shopLedger);\n const shop=next.shopLedger.shops[node.id];\r\n if(shop.status==='ambushed')next.stats=applyStatImpact(next.stats,{receiptId:`shop-ambush:${node.id}`,kind:'attack',damage:10}).state;\r\n if(['left','stolen','ambushed'].includes(shop.status)){\r\n  const award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'full-game'});next.xp=award.state;\r\n  next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,outcome:shop.status};next.phase='walk';\r\n }\r\n return settleRunHealth(next);\r\n}\r\nexport function resolveRunGame(state,savedChase){\r\n const node=currentRunRoom(state);\r\n if(node?.type!=='full-game'||node.roomType!=='coin_chase')throw new Error('Current room is not a coin chase');\r\n const chase=restoreCoinChaseState(savedChase);\r\n if(chase.encounterId!==node.id||chase.status==='running')throw new Error('Finish the scheduled chase before collecting rewards');\r\n if(state.phase==='walk'&&state.completion?.roomId===node.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)throw new Error('Clear pauses before completing the chase');\r\n const next=copy(state),award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'full-game'});\r\n next.xp=award.state;next.runCoins+=chase.collectedCoins+chase.bonusCoins;\r\n next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,coins:chase.collectedCoins+chase.bonusCoins};next.phase='walk';return next;\r\n}\r\nexport function resolveRunPond(state,savedPond){\r\n const node=currentRunRoom(state),pond=restorePondRoom(savedPond);\r\n if(node?.roomType!=='healing_waters'||pond.encounterId!==node.id||!['complete','awoken'].includes(pond.status))throw new Error('Finish the scheduled pond room first');\r\n if(state.phase==='walk'&&state.completion?.roomId===node.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)throw new Error('Clear pauses before completing the pond');\r\n const next=copy(state),award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'full-game'});\r\n next.xp=award.state;\r\n const healed=next.rulesVersion!==2&&award.xpAwarded>0&&pond.status==='complete'?Math.min(POND_TUNING_PROPOSAL.healingAmount,next.stats.maxHealth-next.stats.health):0;\r\n next.stats.health+=healed;next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,healed,outcome:pond.status};next.phase='walk';return next;\r\n}\r\nexport function resolveRunArcade(state,savedArcade){\r\n const node=currentRunRoom(state),roomId=node?.doorId;\r\n if(state.schedule.scheduleVersion<2||node?.type!=='full-game'||!['clay','dance','jelly'].includes(roomId))throw new Error('Current room is not a V2 arcade room');\r\n if(state.phase==='walk'&&state.completion?.roomId===node.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)throw new Error('Clear pauses before completing the arcade room');\r\n let outcome;\r\n if(roomId==='dance'){\r\n  const game=createDanceRoom({runId:state.id,seed:arcadeSeed(node.id),saved:savedArcade});\r\n  const result=game.state();if(!result.roundComplete)throw new Error('Finish the scheduled dance round first');\r\n  outcome={score:result.score,judgedBeats:result.judgedBeats,outdanced:result.provisional.outdanced,specialRewardEligible:result.provisional.specialRewardEligible};\r\n }else{\r\n  const game=checkpointGameRoom(savedArcade);\r\n  if(game.encounterId!==node.id||game.roomId!==roomId||game.status!=='cleared'||roomId==='jelly'&&game.volume!==(node.volume??ROOM_BY_ID.jelly.variants[0]))throw new Error('Finish the scheduled arcade room first');\r\n  outcome={status:game.status,end:game.end};\r\n }\r\n const next=copy(state),award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'full-game'});\r\n next.xp=award.state;next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements,outcome};next.phase='walk';return next;\r\n}\r\nexport function resolveRunTreasure(state,savedTreasure){\r\n const node=currentRunRoom(state),treasure=restoreTreasureChase(savedTreasure);\r\n if(state.schedule.scheduleVersion<2||node?.type!=='bonus'||node.doorId!=='treasure'||node.roomType!=='treasure_side_room'||treasure.encounterId!==node.id\r\n    ||treasure.status!=='arrived'||treasure.openedChests.length!==treasure.chestManifest.length\r\n    ||treasure.chestManifest.some(chest=>!treasure.openedChests.some(event=>event.chestId===chest.id)))throw new Error('Open the scheduled treasure chest after reaching the room');\r\n if(state.phase==='walk'&&state.completion?.roomId===node.id)return copy(state);\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)throw new Error('Clear pauses before completing the treasure room');\r\n const next=copy(state),award=awardRoomXp(next.xp,{roomId:node.id,receiptId:`room:${node.id}`,kind:'treasure'});\r\n next.xp=award.state;next.completion={roomId:node.id,xp:award.xpAwarded,packs:award.packEntitlements};next.phase='walk';return next;\r\n}\r\nexport function exitCaughtRunTreasure(state,savedTreasure){\r\n const node=currentRunRoom(state),treasure=restoreTreasureChase(savedTreasure);\r\n if(state.schedule.scheduleVersion<2||node?.type!=='bonus'||node.doorId!=='treasure'||node.roomType!=='treasure_side_room'||treasure.encounterId!==node.id||treasure.status!=='caught')throw new Error('The scheduled treasure chase was not caught');\r\n if(state.stats.health<=0||state.phase!=='active'||state.pauseReasons.length)throw new Error('Clear pauses before leaving the treasure room');\r\n const next=copy(state);next.phase='walk';next.completion=null;next.choice=null;next.choiceSince=null;return next;\r\n}\r\n/** Called only after the walking/covered-room handoff actually finishes. */\r\nexport function advanceRunRoom(state){\n if(state.stats.health<=0||state.phase!=='walk'||state.pauseReasons.length)throw new Error('Finish the room and clear pauses before its handoff');\r\n const next=copy(state),prior=currentRunRoom(state);\n if(next.schedule.scheduleVersion===4&&prior?.roomType==='boss_recovery'){\n  if(!next.completion?.recovery||next.completion.roomId!==prior.id)throw new Error('Continue recovery explicitly');\n  if(!next.reentryReceipts.includes(prior.id))next.reentryReceipts.push(prior.id);\n }\n next.cursor++;next.roomValue=0;next.lastValue=0;next.lastNow=null;\n next.choice=null;next.choiceSince=null;next.completion=null;\r\n const node=currentRunRoom(next);\r\n if(next.rulesVersion===2&&node?.type==='recovery'&&node.roomType!=='boss_recovery')next.escapeDetour=null;\n if(node?.type==='full-game'&&node.doorId!=='quilt')next.campaign=discoverRoom(next.campaign,{roomId:node.doorId,receiptId:`discover:${next.id}:${node.doorId}`}).progress;\n if(!node)next.campaign=recordCompletedRun(next.campaign,{runId:next.id,receiptId:`run:${next.id}`}).progress;\r\n next.phase=!node?'complete':next.tutorials.enabled&&!next.tutorials.seen.includes(tutorialKey(node))?'tutorial':'active';\n if(!node&&next.heartModifiers){next.heartModifiers=expireRunWearables(next.heartModifiers);next.runCoins=0;if(next.shopLedger)next.shopLedger.coins=0;}\n return next;\r\n}\r\n\r\nexport function restoreRunSession(saved,{accountId,dataEpoch=null}={}){\r\n if(saved?.version!==1||!id(saved.id)||saved.accountId!==accountId||saved.dataEpoch!==dataEpoch)throw new Error('Saved run owner/epoch mismatch');\r\n const next=copy(saved);\r\n if(next.combatClass!=null&&!CLASS_IDS.includes(next.combatClass))throw new TypeError('Unknown saved combat class');\r\n if(next.combatGearTier!==undefined&&(!Number.isInteger(next.combatGearTier)||next.combatGearTier<0||next.combatGearTier>2))throw new TypeError('Invalid saved combat gear');\r\n next.escapeDetour??=null;\r\n if(next.escapeDetour){\r\n  const boss=restoreBossState(next.escapeDetour.boss);\r\n  if(boss.kind!=='mainboss'||!(next.schedule?.scheduleVersion===4?/^level-[1-5]-pair-[1-3]-main$/:/^(?:level-[1-5]-)?intro-mainboss$/).test(boss.bossId)||boss.phase!==1||boss.status!=='escaped'\n     ||!boss.phase2Pending||boss.unlockReceipt)throw new TypeError('Invalid saved pending boss');\r\n  next.escapeDetour={boss};\r\n }\r\n next.routes??=[];next.runCoins??=0;\r\n next.shopDoorsOpened??=[];\r\n if(!Array.isArray(next.shopDoorsOpened)||new Set(next.shopDoorsOpened).size!==next.shopDoorsOpened.length||next.shopDoorsOpened.some(value=>!next.routes.some(route=>route.roomId==='papier_mache'&&value===(next.rulesVersion===2?route.afterId.replace(/^(level-[1-5]-)(.*)$/, '$1game-$2-papier_mache'):`game-${route.afterId}-papier_mache`))))throw new TypeError('Invalid shop entrance receipts');\r\n next.shopLedger=next.shopLedger?restoreRunShop(next.shopLedger,{runId:next.id,runCoins:next.runCoins,policyVersion:next.schedule?.scheduleVersion===4?4:1,combatClass:next.combatClass}):null;\n if(!Array.isArray(next.routes)||!Number.isSafeInteger(next.runCoins)||next.runCoins<0)throw new TypeError('Invalid saved routes or coins');\r\n const scheduleVersion=next.schedule?.scheduleVersion===undefined?1:next.schedule.scheduleVersion;\r\n if(scheduleVersion===2&&!next.schedule.assignment||![1,2,3,4].includes(scheduleVersion)||scheduleVersion===1&&Object.hasOwn(next.schedule??{},'assignment'))throw new TypeError('Invalid saved schedule version or assignment');\n const expected=buildSchedule(next,next.escapeDetour?.boss,next.routes,scheduleVersion);\r\n if(JSON.stringify(next.schedule)!==JSON.stringify(expected)||!Number.isSafeInteger(next.cursor)||next.cursor<0||next.cursor>expected.nodes.length\r\n    ||!['tutorial','active','upgrade','walk','complete','defeated','rest-reaper',...(scheduleVersion===4?['captured']:[])].includes(next.phase)||!Array.isArray(next.pauseReasons)\n    ||next.pauseReasons.some(reason=>!RUN_PAUSE_REASONS.includes(reason)))throw new TypeError('Invalid saved run schedule');\r\n if(next.shopLedger){\r\n  const room=currentRunRoom(next),shops=Object.values(next.shopLedger.shops);\r\n  if(shops.some(shop=>!expected.nodes.slice(0,next.cursor+1).some(node=>node.id===shop.shopId&&node.roomType==='corgi_shop')||shop.status==='open'&&(shop.shopId!==room?.id||room.roomType!=='corgi_shop'))\r\n    ||shops.filter(shop=>shop.status==='open').length>1)throw new TypeError('Saved shop is outside the current run path');\r\n }\r\n const levelBosses=next.rulesVersion===2?copy(next.warmups.introBossReceipts):null;\r\n next.warmups=restoreWarmupProgress(next.warmups);if(levelBosses){if(!Array.isArray(levelBosses)||levelBosses.some(b=>!expected.nodes.some(n=>n.id===b.bossId&&n.bossSlot)||!id(b.receiptId)))throw new TypeError('Invalid level boss receipts');next.warmups.introBossReceipts=levelBosses;}next.xp=restoreRunXp(next.xp);\r\n if(JSON.stringify(next.warmups.activeWarmupIds)!==JSON.stringify(extraWarmups(expected)))throw new TypeError('Invalid saved recovery rooms');\r\n if(next.xp.runId!==next.id)throw new Error('XP belongs to another run');\r\n next.campaign=restoreRoomUnlockProgress(next.campaign);next.boons=restoreBoonInventory(next.boons);\r\n next.stats=restoreRunStats(next.stats);\r\n if(next.rulesVersion===2){if(![3,4].includes(scheduleVersion)||next.stats.rulesVersion!==2||!Number.isFinite(next.activeRunMs)||next.activeRunMs<0||next.activeRunMs>3600000||typeof next.reaperStarted!=='boolean'||next.reaperStarted!==(next.activeRunMs===3600000)||next.reaperStarted&&next.phase!=='rest-reaper'||!Array.isArray(next.escapeHistory))throw new TypeError('Invalid versioned run');validateRunLoadout(next.loadout,{weaponIds:[next.loadout?.weaponId],galaWeaponTiers:next.startingBenefits?.galaWeaponTiers??{},relicIds:next.startingBenefits?.relics?.map(r=>r.id)??next.loadout?.relicIds});if(next.loadout.styleId!==next.combatClass)throw new TypeError('Loadout style mismatch');}\n else if(next.rulesVersion!==undefined&&next.rulesVersion!==1||[3,4].includes(scheduleVersion)||next.stats.rulesVersion===2)throw new TypeError('Invalid legacy rules');\n if(scheduleVersion===4){\n  next.heartModifiers=restoreHeartModifiers(next.heartModifiers,{stats:next.stats});\n  if(next.loadout.relicIds.length||!Array.isArray(next.reentryReceipts)||new Set(next.reentryReceipts).size!==next.reentryReceipts.length||next.reentryReceipts.some(id=>!expected.nodes.slice(0,next.cursor+1).some(n=>n.id===id&&n.roomType==='boss_recovery')))throw new TypeError('Invalid rescue recovery or loadout');\n  bossCombatOptions(next.stats,next.boons,next.heartModifiers,next.secretEffects);\n  if(['complete','captured','rest-reaper'].includes(next.phase)&&!next.heartModifiers.expired)throw new TypeError('Terminal run wearables must expire');\n }\n if(!Array.isArray(next.unlockedCoaches)||next.unlockedCoaches.some(value=>typeof value!=='string'||!value)\r\n    ||new Set(next.unlockedCoaches).size!==next.unlockedCoaches.length)throw new TypeError('Invalid coach unlocks');\r\n next.campaignPacks??=[];\r\n if(!Array.isArray(next.campaignPacks)||next.campaignPacks.some(pack=>!pack||typeof pack.id!=='string'||!Number.isSafeInteger(pack.level)||pack.level<2)\r\n    ||new Set(next.campaignPacks.map(pack=>pack.id)).size!==next.campaignPacks.length)throw new TypeError('Invalid campaign pack queue');\r\n // Reload never credits unobserved time or continues a half-held upgrade choice.\r\n next.lastNow=null;next.lastValue=0;next.choice=null;next.choiceSince=null;\r\n return settleRunHealth(next);\r\n}\r\n\r\nfunction buildSchedule(state,pendingBoss=null,routes=state.routes??[],version=state.schedule?.scheduleVersion??1){\r\n return createRunScheduleForVersion(version,{seed:state.seed,exerciseIds:state.exerciseIds,assignment:version===2?state.schedule.assignment:undefined,manifest:version>=3?state.schedule.manifest:undefined,pendingBoss:version>=3?state.escapeHistory:pendingBoss,routes});\n}\r\n\r\n/** Local campaign carry-over; temporary stats/XP reset, discovery and pending pack receipts persist. */\r\nexport function createNextRunSession(previous,options){\r\n const prior=restoreRunSession(previous,{accountId:options.accountId,dataEpoch:options.dataEpoch??null});\r\n const next=createRunSession({...options,campaign:prior.campaign});\r\n next.unlockedCoaches=[...prior.unlockedCoaches];\r\n next.campaignPacks=[...new Map([...prior.campaignPacks,...prior.xp.packEntitlements].map(pack=>[pack.id,copy(pack)])).values()];\r\n next.tutorials.seen=[...prior.tutorials.seen];\r\n if(next.tutorials.seen.includes('basic'))next.phase='active';\r\n return next;\r\n}\r\n","modules/roguelite/run-frame-quality.mjs":"// One gate for combat damage and qualified session time. No clock credit across stalls.\r\nexport const ACTIVE_RUN_LIMIT_MS=60*60*1000;\r\nexport function qualifyRunFrame(runtime,frame,{hands=false,body=true}={}){\r\n const previous=runtime.sourceTs;\r\n const valid=frame.op==='sample'&&frame.tracked&&Number.isFinite(frame.sourceTs)&&frame.sourceTs<=frame.now&&frame.now-frame.sourceTs<=300\r\n  &&(previous==null||frame.sourceTs>previous)&&(!body||frame.quality?.body===true)&&(!hands||frame.quality?.hands===true);\r\n const continuous=valid&&previous!=null&&frame.sourceTs-previous<=300;\r\n runtime.goodSamples=valid?(continuous?(runtime.goodSamples??0)+1:1):0;\r\n if(Number.isFinite(frame.sourceTs)&&frame.sourceTs<=frame.now&&(previous==null||frame.sourceTs>previous))runtime.sourceTs=frame.sourceTs;\r\n return {valid:valid&&runtime.goodSamples>=2,deltaMs:continuous?Math.min(100,frame.sourceTs-previous):0};\r\n}\r\nexport function gateBossCue(runtime,frame,qualified){\r\n const boss=runtime.boss,q=boss?.cue;\r\n if(!boss)return qualified;\r\n if(!qualified){\r\n  if(!runtime.presentationInvalid)runtime.presentationGeneration=(runtime.presentationGeneration??0)+1;\r\n  runtime.presentationInvalid=true;runtime.presentedCue=null;\r\n  if(boss.status==='fighting'&&['telegraph','window'].includes(q?.name)){\r\n   q.name='telegraph';q.ms=0;q.durationMs=Math.max(1000,boss.timings[boss.kind][`phase${boss.phase}`].telegraphMs);q.defended=false;q.blocked=false;q.dodged=false;\r\n  }\r\n  return false;\r\n }\r\n runtime.presentationInvalid=false;\r\n if(boss.status!=='fighting'||!['telegraph','window'].includes(q?.name))return true;\r\n const generation=runtime.presentationGeneration??0,key=`${generation}:${boss.phase}:${q.attackNo}`;\r\n if(runtime.presentedCue===key)return true;\r\n if(q.name==='telegraph'&&frame.cueAck?.generation===generation&&frame.cueAck?.phase===boss.phase&&frame.cueAck?.attackNo===q.attackNo){runtime.presentedCue=key;return true;}\r\n return false;\r\n}\r\n","modules/roguelite/run-loadout.mjs":"import {CLASS_IDS} from './class-combat.mjs';\r\nimport {resolveOwnedWeapon} from './weapon-style-catalog.mjs';\r\nexport const DEFAULT_RUN_LOADOUT=Object.freeze({weaponId:'brass-wraps',styleId:'monk',relicIds:Object.freeze([])});\r\nexport function validateRunLoadout(value=DEFAULT_RUN_LOADOUT,{weaponIds=[],relicIds=[],galaWeaponTiers={}}={}){\r\n if(!value||Array.isArray(value)||Object.keys(value).length!==3||!CLASS_IDS.includes(value.styleId)||!Array.isArray(value.relicIds)||value.relicIds.length>3||new Set(value.relicIds).size!==value.relicIds.length)throw new TypeError('Invalid run loadout');\r\n const weapon=resolveOwnedWeapon(value.weaponId,{weaponIds,galaWeaponTiers});\r\n if(weapon.styleId!==value.styleId||value.relicIds.some(id=>typeof id!=='string'||!relicIds.includes(id)))throw new TypeError('Run equipment is not owned or compatible');\r\n return {weaponId:value.weaponId,styleId:value.styleId,relicIds:[...value.relicIds]};\r\n}\r\n","modules/roguelite/run-proficiency.mjs":"import {createWeaponProficiency,restoreWeaponProficiency,awardWeaponProficiency,PROFICIENCY_LIMITS} from './weapon-proficiency.mjs';\r\n// Only host-verified boss receipts enter this adapter, inside the run/base CAS.\r\nexport function awardRunProficiency(base,state,receipts){\r\n if(state.rulesVersion!==2)return {base,awarded:0};\r\n let ledger=base.combatProficiency===undefined?createWeaponProficiency():restoreWeaponProficiency(base.combatProficiency),awarded=0;\r\n for(const receipt of receipts){\r\n  if(receipt.type!=='boss_defeated')continue;\r\n  if(typeof receipt.receiptId!=='string'||!receipt.receiptId.startsWith('boss-'))throw new TypeError('Invalid proficiency receipt');\r\n  // Preserve the full dedupe history; a capped mastery ledger never blocks a legitimate clear.\r\n  if(ledger.receipts.length===PROFICIENCY_LIMITS.receipts&&!ledger.receipts.some(item=>item.sourceId===receipt.receiptId))continue;\r\n  const result=awardWeaponProficiency(ledger,{sourceId:receipt.receiptId,styleId:state.loadout.styleId,units:1});\r\n  ledger=result.ledger;if(result.awarded)awarded++;\r\n }\r\n return {base:awarded?{...base,combatProficiency:ledger}:base,awarded,units:ledger.units};\r\n}\r\n","modules/roguelite/run-scene.mjs":"import {BIOMES,shapeGeometry} from '../coach-world-rts/biomes.mjs';\r\nimport {FACTION_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {idlePose,squatPose,walkPose} from '../coach-world-rts/coach-motion.mjs';\r\nimport {createRoomAtmosphere} from './room-atmosphere.mjs';\r\nimport {createWalkMotion} from './walk-motion.mjs';\r\nimport {createPs1Material} from '../coach-world-rts/ps1-material.mjs';\nimport {coachPresentationForRoom,rescueCoachPresentation} from './coach-presentation.mjs';\nimport {createRenderCadence} from './render-cadence.mjs';\nimport {createRoomIntro} from './room-intro.mjs';\nimport {COACH_UNITS} from '../coach-world/unit-catalog.mjs';\nimport {prepareRescueLayer} from './rescue-scene.mjs';\nexport {coachForRoomNode} from './run-schedule.mjs';\n\r\nconst TAU=Math.PI*2,ROOM_RADIUS=12,VIEW_W=640,VIEW_H=360;\r\nconst isBoss=node=>['miniboss','mainboss','boss_encounter'].includes(node?.type)||node?.roomType==='boss_encounter';\r\nconst hash=value=>{let h=2166136261;for(const c of String(value))h=Math.imul(h^c.charCodeAt(0),16777619);return h>>>0;};\r\nconst abortError=()=>Object.assign(new Error('Room scene operation aborted.'),{name:'AbortError'});\r\n\r\nfunction loadTexture(T,path,signal){\r\n if(signal?.aborted)return Promise.reject(abortError());\r\n const request=Promise.resolve().then(()=>new T.TextureLoader().loadAsync(path));\r\n if(!signal)return request;\r\n return new Promise((resolve,reject)=>{\r\n  let settled=false;\r\n  const clean=()=>signal.removeEventListener('abort',onAbort);\r\n  const onAbort=()=>{if(settled)return;settled=true;clean();reject(abortError());};\r\n  signal.addEventListener('abort',onAbort,{once:true});\r\n  request.then(texture=>{\r\n   if(settled||signal.aborted){texture.dispose?.();if(!settled)onAbort();return;}\r\n   settled=true;clean();resolve(texture);\r\n  },error=>{if(settled)return;settled=true;clean();reject(error);});\r\n });\r\n}\r\n\r\nfunction seeded(seed){let s=seed|0;return()=>{s=(Math.imul(s,1664525)+1013904223)|0;return(s>>>0)/4294967296;};}\r\nfunction makeRoomGroup(T,node,coach,texture){\r\n const [fx,fy,fw,fh]=coach.frame,iw=texture.image?.width,ih=texture.image?.height;\r\n if(!(iw>0&&ih>0)||fx+fw>iw||fy+fh>ih)throw new Error(`Coach atlas frame is outside ${coach.atlasPath}`);\r\n const group=new T.Group();group.name=`roguelite-room:${node.id??node.type??'room'}`;\r\n const section=coach.section&&BIOMES[coach.section]?coach.section:'chest',biome=BIOMES[node.biomeId]||BIOMES[section]||BIOMES.chest;\r\n const faction=FACTION_BY_ID[section]||FACTION_BY_ID.chest;\r\n const ownedGeometries=new Set(),ownedMaterials=new Set(),ownedTextures=new Set([texture]);\r\n const keepGeo=geo=>(ownedGeometries.add(geo),geo),keepMat=mat=>(ownedMaterials.add(mat),mat);\r\n const material=color=>{const mat=createPs1Material(T,{vertexColors:false,affine:false,resolution:[VIEW_W,VIEW_H]});mat.color.set(color);return keepMat(mat);};\n const rnd=seeded(hash(node.seed??node.id??coach.id));\n // A tiny baked-looking surface: no image download, normal map or extra render pass.\n const pixels=new Uint8Array(32*32*4);\n for(let y=0;y<32;y++)for(let x=0;x<32;x++){const i=(y*32+x)*4,seam=x%8===0||y%8===0,shade=Math.floor((seam?135:200)+rnd()*35);pixels[i]=pixels[i+1]=pixels[i+2]=shade;pixels[i+3]=255;}\n const floorTexture=new T.DataTexture(pixels,32,32,T.RGBAFormat);floorTexture.wrapS=floorTexture.wrapT=T.RepeatWrapping;floorTexture.repeat.set(12,12);floorTexture.magFilter=floorTexture.minFilter=T.NearestFilter;floorTexture.needsUpdate=true;ownedTextures.add(floorTexture);\n const groundGeo=keepGeo(new T.CircleGeometry(ROOM_RADIUS,48));\n const groundMat=keepMat(createPs1Material(T,{map:floorTexture,vertexColors:false,affine:false,resolution:[VIEW_W,VIEW_H]}));groundMat.color.set(biome.ground);\n const ground=new T.Mesh(groundGeo,groundMat);ground.rotation.x=-Math.PI/2;ground.position.y=-.12;ground.receiveShadow=true;group.add(ground);\r\n const rimMat=material(faction.dark);\r\n const rim=new T.Mesh(keepGeo(new T.TorusGeometry(ROOM_RADIUS-.12,.09,5,96)),rimMat);rim.rotation.x=Math.PI/2;rim.position.y=-.1;group.add(rim);\r\n const scatter=(spec,kind)=>{\r\n  if(!spec?.shape||!Number.isFinite(spec.count)||spec.count<=0)return;\r\n  const geom=keepGeo(shapeGeometry(T,spec.shape,1));\r\n  const mat=material(spec.color||faction.color);\r\n  const mesh=new T.InstancedMesh(geom,mat,Math.floor(spec.count));mesh.name=`room-${kind}-${spec.shape}`;\r\n  const dummy=new T.Object3D(),lo=spec.scale?.[0]??.3,hi=spec.scale?.[1]??.6,clearing=4.4;\r\n  for(let i=0;i<mesh.count;i++){\r\n   const angle=rnd()*TAU,radius=Math.sqrt(clearing*clearing+rnd()*(10.9*10.9-clearing*clearing));\r\n   const scale=lo+rnd()*(hi-lo),x=Math.cos(angle)*radius,z=Math.sin(angle)*radius;\r\n   dummy.position.set(x,kind==='plant'?scale*.55:scale*.22,z);dummy.rotation.set(kind==='plant'?0:(rnd()-.5)*.35,rnd()*TAU,kind==='plant'?(rnd()-.5)*.22:0);dummy.scale.setScalar(scale);dummy.updateMatrix();mesh.setMatrixAt(i,dummy.matrix);\r\n  }\r\n  mesh.instanceMatrix.needsUpdate=true;group.add(mesh);\r\n };\r\n scatter(biome.plant,'plant');scatter(biome.rock,'rock');\r\n const colors=new T.Color(faction.color),light=new T.HemisphereLight(0xffead3,biome.atmosphere.haze,1.25);group.add(light);\r\n const key=new T.DirectionalLight(colors,2.1);key.position.set(-4,8,5);group.add(key);\r\n const isBossRoom=isBoss(node),height=isBossRoom?6:2.5,width=height*(coach.frame[2]/coach.frame[3]);\r\n texture.colorSpace=T.SRGBColorSpace;texture.magFilter=T.NearestFilter;texture.minFilter=T.NearestFilter;texture.generateMipmaps=false;\r\n texture.wrapS=texture.wrapT=T.ClampToEdgeWrapping;\r\n texture.repeat.set(fw/iw,fh/ih);texture.offset.set(fx/iw,1-(fy+fh)/ih);texture.needsUpdate=true;\r\n const coachMaterial=keepMat(new T.SpriteMaterial({map:texture,color:coach.tint??faction.color,transparent:true,alphaTest:.04,toneMapped:false,fog:true}));\n const coachSprite=new T.Sprite(coachMaterial);coachSprite.name=`coach-billboard:${coach.id}`;coachSprite.center.set(.5,0);coachSprite.position.set(0,0,-2.2);coachSprite.scale.set(width,height,1);coachSprite.renderOrder=2;group.add(coachSprite);\n const shadow=new T.Mesh(keepGeo(new T.CircleGeometry(isBossRoom?1.5:.7,24)),keepMat(new T.MeshBasicMaterial({color:0x050b0e,transparent:true,opacity:.32,depthWrite:false})));shadow.rotation.x=-Math.PI/2;shadow.position.set(0,-.1,-2.2);shadow.scale.y=.55;group.add(shadow);\n // Background trainers use public roster frames from the already loaded atlas.\n // They remain scenery; the scheduled coach and every unlock binding stay exact.\n if(!isBossRoom){\n  const candidates=COACH_UNITS.filter(unit=>unit.atlasPath===coach.atlasPath&&unit.frame.some((value,i)=>value!==coach.frame[i]));\n  const start=hash(`${node.id}:audience`)%Math.max(1,candidates.length);\n  for(let i=0;i<Math.min(2,candidates.length);i++){\n   const other=candidates[(start+i)%candidates.length],[sx,sy,sw,sh]=other.frame;\n   if(sx+sw>iw||sy+sh>ih)continue;\n   const map=texture.clone();map.repeat.set(sw/iw,sh/ih);map.offset.set(sx/iw,1-(sy+sh)/ih);map.needsUpdate=true;ownedTextures.add(map);\n   const mat=keepMat(new T.SpriteMaterial({map,color:0xabb8b2,transparent:true,alphaTest:.04,toneMapped:false,fog:true}));\n   const npc=new T.Sprite(mat),size=1.4+rnd()*.35;npc.name=`background-trainer:${other.id}`;npc.center.set(.5,0);npc.position.set(i?1.8:-1.8,0,-3.8);npc.scale.set(size*sw/sh,size,1);group.add(npc);\n  }\n }\n let disposed=false;\r\n return {group,coach:coachSprite,biomeId:node.biomeId&&BIOMES[node.biomeId]?node.biomeId:section,coachSection:section,coachColor:coach.tint??faction.color,\n  updatePose(pose){if(disposed)return;coachSprite.position.y=pose.bodyY*height;coachSprite.rotation.z=pose.bodyZ;coachSprite.scale.set(width,height*pose.scaleY,1);},\r\n  dispose(){if(disposed)return;disposed=true;group.removeFromParent();for(const geo of ownedGeometries)geo.dispose?.();for(const mat of ownedMaterials)mat.dispose?.();for(const tex of ownedTextures)tex.dispose?.();ownedGeometries.clear();ownedMaterials.clear();ownedTextures.clear();group.clear();}};\r\n}\r\n\r\n/** One transparent, caller-ticked WebGL scene for the run controller. */\r\nexport function createRunScene(T,{canvas,reducedMotion=false}={}){\r\n if(!canvas)throw new TypeError('A scene canvas is required.');\r\n const renderer=new T.WebGLRenderer({canvas,alpha:true,antialias:false,powerPreference:'low-power'});\r\n renderer.setPixelRatio(1);renderer.setClearColor(0,0);renderer.outputColorSpace=T.SRGBColorSpace;\r\n if(canvas.style)canvas.style.imageRendering='pixelated';\r\n const scene=new T.Scene(),camera=new T.PerspectiveCamera(42,VIEW_W/VIEW_H,.05,80);\r\n camera.position.set(0,2.1,9);camera.lookAt(0,1.15,-1.2);\r\n const owned=new Set(),atmosphere=createRoomAtmosphere(T,{scene,camera,color:0x777b72,scale:1,reducedMotion});\r\n const ambient=new T.AmbientLight(0xffffff,.38);scene.add(ambient);owned.add(ambient);\r\n const cadence=createRenderCadence(30);\n let current=null,loading=0,disposed=false,clock=0,walk=null,walkPending=null,roomIntro=null,poseKind='idle',walkStopped=false,rescue=null,rescueRestore=null;\n const markLoading=delta=>{loading=Math.max(0,loading+delta);atmosphere.setLoading(loading>0);};\r\n const check=()=>{if(disposed)throw new Error('Run scene has been disposed.');};\r\n const finishWalk=(error=null)=>{const pending=walkPending;if(!pending)return;walkPending=null;walk=null;pending.signal?.removeEventListener('abort',pending.abort);error?pending.reject(error):pending.resolve();};\r\n\r\n async function prepareRoom(node,{scheduleVersion=1,presentationCoachId=null,loadModel=null}={}){\n  check();const coach=coachPresentationForRoom(scheduleVersion,node),signal=node.signal;markLoading(1);\n  let texture=null,room=null,model=null;\n  try{\r\n   texture=await loadTexture(T,coach.atlasPath,signal);\r\n   if(signal?.aborted||disposed){texture.dispose?.();throw abortError();}\r\n   room=makeRoomGroup(T,node,coach,texture);texture=null;\n   if(scheduleVersion===4&&isBoss(node)){\n    const presentation=rescueCoachPresentation({canonicalCoachId:coach.canonicalCoachId,presentationCoachId:presentationCoachId??coach.canonicalCoachId,rescueKind:presentationCoachId==='myr5'?'repeat':'first'});\n    const loader=loadModel??(await import('../../creature/assets/rescue-model.js')).loadRescueCoach;\n    model=await loader(presentation.sourceId,{reducedMotion,signal});\n    room.coach.visible=false;model.root.scale.multiplyScalar(2);model.root.position.set(0,0,-2.2);room.group.add(model.root);\n   }\n   if(signal?.aborted||disposed){room.dispose();throw abortError();}\n   let roomDisposed=false,gesture='idle';\n   const glowing=[];model?.root.traverse(object=>{for(const mat of Array.isArray(object.material)?object.material:object.material?[object.material]:[])if(mat.emissive)glowing.push({mat,color:mat.emissive.clone(),intensity:mat.emissiveIntensity});});\n   const prepared={group:room.group,coach:model?.root??room.coach,coachDefinition:coach,presentationCoachId:model?(presentationCoachId??coach.canonicalCoachId):null,biomeId:room.biomeId,coachSection:room.coachSection,coachColor:room.coachColor,\n    poseKind:node.exerciseGroup==='legs'?'squat':node.exerciseKind==='hold'?'idle':'walk',\n    updatePose:model?()=>{}:room.updatePose,\n    updateModel:model?(dt,cue)=>{const next=cue?.name==='telegraph'?'ready':cue?.name==='window'?'swipe':'idle';if(next!==gesture){gesture=next;model.play(next);}model.update(dt);}:null,\n    setCue:model?color=>{for(const {mat,color:base,intensity}of glowing){if(color){mat.emissive.set(color);mat.emissiveIntensity=.22;}else{mat.emissive.copy(base);mat.emissiveIntensity=intensity;}}}:null,\n    dispose(){if(roomDisposed)return;roomDisposed=true;if(current===prepared)current=null;owned.delete(prepared);model?.dispose();room.dispose();}};\n   owned.add(prepared);return prepared;\r\n  }catch(error){texture?.dispose?.();model?.dispose();room?.dispose();throw error;}\n  finally{markLoading(-1);}\r\n }\r\n function commitRoom(prepared){\n  check();if(!owned.has(prepared)||!prepared?.group)throw new TypeError('Prepared room does not belong to this scene.');\n  if(rescue)throw new Error('Rescue owns the scene camera.');\n  if(current===prepared)return prepared;\n  roomIntro?.cancel();roomIntro=null;\n  if(current)current.dispose();current=prepared;scene.add(prepared.group);walkStopped=false;\n  // The portal owns this covered scene swap. Begin the next room at its entrance,\r\n  // rather than retaining the exit camera beyond the old room's far boundary.\r\n  camera.position.set(0,2.1,9);camera.lookAt(0,1.15,-1.2);cadence.reset();\n  const biome=BIOMES[prepared.biomeId]||BIOMES.chest;\r\n  scene.background=new T.Color(biome.atmosphere.haze);scene.fog?.color.set(biome.atmosphere.haze);\r\n  prepared.group.traverse(object=>object.material?.userData.ps1?.setResolution(renderer.domElement.width,renderer.domElement.height));\r\n  return prepared;\r\n }\r\n function update(delta,{paused=false,introPaused=paused,active=false,bossCue=null,hitStop=false}={}){\n  check();if(!Number.isFinite(delta)||delta<0)throw new RangeError('Scene delta must be finite and nonnegative.');\n  if(rescue){\n   if(!paused)rescue.update(delta,camera);\n   if(cadence.shouldRender(delta)){renderer.render(scene,camera);if(!paused&&!renderer.getContext().isContextLost())rescue.afterRender();}\n   return;\n  }\n  const touring=roomIntro!==null;\n  if(roomIntro){roomIntro.update(delta,{paused:introPaused});if(roomIntro.getdone())roomIntro=null;}\n  const frozen=paused===true,dt=frozen?0:Math.min(.1,delta);let walkDone=false;\r\n  if(!frozen){clock+=dt;atmosphere.update(dt);if(walk){const result=walk.update(dt);walkDone=result.done;}\r\n   if(current&&!hitStop){poseKind=bossCue?.name==='telegraph'?'squat':active?current.poseKind:'idle';const pose=poseKind==='squat'?squatPose(clock):poseKind==='walk'?walkPose(clock):idlePose(clock);current.updatePose?.(pose);current.updateModel?.(dt,bossCue);}}\n  if(current){const kind=bossCue?.kind,color=bossCue?.name==='opening'?'#c8ff95':kind?.startsWith('red')?'#ff6252':kind==='yellow_blockable'?'#ffe671':null;if(current.setCue)current.setCue(color);else current.coach.material.color.set(color??current.coachColor);}\n  if(current&&!walk&&!touring&&!walkStopped&&!hitStop){\n   const directional=!!bossCue?.direction,sign=bossCue?.direction==='left'?-1:1;\n   const wind=directional&&bossCue.name==='telegraph',strike=directional&&bossCue.name==='window';\n   const progress=Math.min(1,(bossCue?.ms??0)/(bossCue?.durationMs||700)),blend=reducedMotion?1:1-Math.exp(-dt*16);\n   const rotation=directional?(reducedMotion?sign*.06:wind?-sign*.15:strike?sign*.25*Math.sin(progress*Math.PI):0):0;\n   const x=directional&&!reducedMotion?(wind?-sign*.3:strike?sign*.6*Math.sin(progress*Math.PI):0):0;\n   if(current.setCue)current.coach.rotation.z+=(rotation-current.coach.rotation.z)*blend;\n   else current.coach.material.rotation+=(rotation-current.coach.material.rotation)*blend;\n   current.coach.position.x+=(x-current.coach.position.x)*blend;\n   camera.position.z+=((!reducedMotion&&wind?8.65:!reducedMotion&&strike?8.4:9)-camera.position.z)*blend;camera.lookAt(0,1.15,-1.2);\n  }\n  // Rendering is capped independently: skipped draws never stall walks or input clocks.\n  if(cadence.shouldRender(delta))renderer.render(scene,camera);if(walkDone)finishWalk();\n }\r\n function walkOut({signal}={}){\n  check();if(rescue)throw new Error('Rescue owns the scene camera.');if(signal?.aborted)return Promise.reject(abortError());if(walkPending)return walkPending.promise;\n  roomIntro?.cancel();roomIntro=null;\n  const duration=reducedMotion?1.1:2.8;walkStopped=true;\n  walk=createWalkMotion(T,camera,{points:[[0,2.15,-4],[0,2.1,-8]],duration,reducedMotion,bobHeight:.08});\n  let resolve,reject;const promise=new Promise((res,rej)=>{resolve=res;reject=rej;});\r\n  const abort=()=>{if(!walkPending)return;walk?.cancel();finishWalk(abortError());};\r\n  walkPending={promise,resolve,reject,signal,abort};signal?.addEventListener('abort',abort,{once:true});return promise;\n }\n function intro(options={}){\n  check();if(rescue||walkPending)throw new Error('Another view owns the room camera.');\n  roomIntro?.cancel();walkStopped=false;\n  roomIntro=createRoomIntro(T,camera,{reducedMotion,...options});cadence.reset();return roomIntro;\n }\n async function prepareRescue(options){\n  check();markLoading(1);let prepared;\n  try{prepared=await prepareRescueLayer(T,{reducedMotion,...options});if(disposed||options.signal?.aborted){prepared.dispose();throw abortError();}owned.add(prepared);return prepared;}\n  finally{markLoading(-1);}\n }\n function commitRescue(prepared){\n  check();if(!owned.has(prepared)||!prepared?.afterRender)throw new TypeError('Prepared rescue does not belong to this scene.');\n  if(walkPending||roomIntro)throw new Error('Another view owns the room camera.');\n  if(rescue===prepared)return prepared;if(rescue)releaseRescue();\n  rescueRestore={position:camera.position.clone(),quaternion:camera.quaternion.clone(),background:scene.background,fog:scene.fog,visibility:scene.children.map(object=>[object,object.visible])};\n  for(const [object] of rescueRestore.visibility)object.visible=false;scene.background=null;scene.fog=null;\n  rescue=prepared;scene.add(prepared.group);cadence.reset();return prepared;\n }\n function releaseRescue(prepared=rescue){\n  if(!prepared)return;owned.delete(prepared);prepared.dispose();if(prepared!==rescue)return;\n  rescue=null;camera.position.copy(rescueRestore.position);camera.quaternion.copy(rescueRestore.quaternion);scene.background=rescueRestore.background;scene.fog=rescueRestore.fog;for(const [object,visible] of rescueRestore.visibility)object.visible=visible;rescueRestore=null;cadence.reset();\n }\n function resize(){\r\n  if(disposed)return;const cssW=Math.max(1,canvas.clientWidth||VIEW_W),cssH=Math.max(1,canvas.clientHeight||VIEW_H),ratio=Math.min(1,VIEW_W/cssW,VIEW_H/cssH);\r\n  const w=Math.max(1,Math.round(cssW*ratio)),h=Math.max(1,Math.round(cssH*ratio));\r\n  if(renderer.domElement?.width!==w||renderer.domElement?.height!==h)renderer.setSize(w,h,false);\r\n  camera.aspect=w/h;camera.updateProjectionMatrix();cadence.reset();\n  current?.group.traverse(object=>object.material?.userData.ps1?.setResolution(w,h));\r\n }\r\n function dispose(){\n  if(disposed)return;disposed=true;if(walkPending){walk?.cancel();finishWalk(abortError());}\n  roomIntro?.cancel();roomIntro=null;releaseRescue();\n  for(const item of [...owned])item.dispose?.();owned.clear();atmosphere.dispose();scene.remove(ambient);renderer.dispose();renderer.forceContextLoss?.();\r\n }\r\n resize();\r\n return {renderer,scene,camera,prepareRoom,commitRoom,prepareRescue,commitRescue,releaseRescue,update,intro,walkOut,resize,dispose,get currentRoom(){return current;}};\n}\n","modules/roguelite/run-schedule-v2-builder.mjs":"import {createRunScheduleV3} from './run-schedule-v3.mjs';\nimport {createRunScheduleV4} from './run-schedule-v4.mjs';\nimport {createRunSchedule as createRunScheduleV1,coachForRoomNode as coachForRoomNodeV1} from './run-schedule.mjs';\r\nimport {SCHEDULE_VERSION_V2,bossAssignmentForClearCount,BOSS_ROWS} from './run-schedule-v2.mjs';\r\nimport {createFullRoomBundle} from './warmup-flow.mjs';\r\nimport {ROOM_BY_ID} from './room-contracts.mjs';\r\n\r\nconst byCanonical=new Map(BOSS_ROWS.map(row=>[row.canonicalCoachId,row]));\r\nconst byVisual=new Map(BOSS_ROWS.map(row=>[row.sourceVisualId,row]));\r\nconst bossIds=new Set(['intro-miniboss','intro-miniboss-2','intro-mainboss']);\r\nconst clone=value=>structuredClone(value);\r\n\r\nfunction assertAssignment(assignment){\r\n if(!assignment||typeof assignment!=='object'||!Number.isSafeInteger(assignment.clearOrdinal)||assignment.clearOrdinal<0)throw new TypeError('Invalid V2 boss assignment');\r\n const expected=bossAssignmentForClearCount(assignment.clearOrdinal);\r\n const exactKeys=(value,keys)=>value&&typeof value==='object'&&!Array.isArray(value)&&Object.keys(value).length===keys.length&&keys.every(key=>Object.hasOwn(value,key));\r\n const scalarKeys=['policyId','scheduleVersion','groupId','groupIndex','cycle','clearOrdinal','assignmentId'];\r\n if(!exactKeys(assignment,[...scalarKeys,'slots','tableHashes'])||scalarKeys.some(key=>assignment[key]!==expected[key])||\r\n  !exactKeys(assignment.tableHashes,['groups','eligibilityRows'])||assignment.tableHashes.groups!==expected.tableHashes.groups||assignment.tableHashes.eligibilityRows!==expected.tableHashes.eligibilityRows||\r\n  !exactKeys(assignment.slots,['mini1','mini2','main'])||['mini1','mini2','main'].some(slot=>{\r\n   const actual=assignment.slots[slot],want=expected.slots[slot];\r\n   return !exactKeys(actual,['canonicalCoachId','sourceVisualId','sourceVisualKind'])||actual.canonicalCoachId!==want.canonicalCoachId||actual.sourceVisualId!==want.sourceVisualId||actual.sourceVisualKind!==want.sourceVisualKind;\r\n  }))throw new TypeError('V2 boss assignment does not match its clear ordinal');\r\n return expected;\r\n}\r\nfunction resolveBinding(binding){\r\n if(!binding||typeof binding!=='object'||Array.isArray(binding))throw new TypeError('Invalid coach binding');\r\n const row=byCanonical.get(binding.canonicalCoachId);\r\n if(!row||binding.sourceVisualId!==row.sourceVisualId||binding.sourceVisualKind!==row.sourceVisualKind)throw new TypeError('Invalid coach binding');\r\n return row;\r\n}\r\nfunction attach(node,binding){\r\n const row=resolveBinding(binding);\r\n if(node.coachId!=null&&node.coachId!==row.sourceVisualId)throw new TypeError('Boss visual does not match issued binding');\r\n if(node.canonicalCoachId!=null&&node.canonicalCoachId!==row.canonicalCoachId)throw new TypeError('Boss canonical coach does not match issued binding');\r\n if(node.sourceVisualId!=null&&node.sourceVisualId!==row.sourceVisualId)throw new TypeError('Boss visual binding does not match issued binding');\r\n if(node.sourceVisualKind!=null&&node.sourceVisualKind!==row.sourceVisualKind)throw new TypeError('Boss visual kind does not match issued binding');\r\n return {...node,coachId:row.sourceVisualId,canonicalCoachId:row.canonicalCoachId,sourceVisualId:row.sourceVisualId,sourceVisualKind:row.sourceVisualKind};\r\n}\r\n\r\nexport function createRunScheduleV2(seed,exerciseIds,assignment,pendingBoss=null,routes=[]){\r\n const issued=assertAssignment(assignment);\r\n if(pendingBoss!=null){\r\n  if(!pendingBoss||typeof pendingBoss!=='object'||Array.isArray(pendingBoss)||typeof pendingBoss.coachId!=='string')throw new TypeError('Invalid pending boss');\r\n  const main=issued.slots.main;\r\n  if(pendingBoss.coachId!==main.sourceVisualId)throw new TypeError('Pending boss does not match issued main');\r\n  if(pendingBoss.canonicalCoachId!=null&&pendingBoss.canonicalCoachId!==main.canonicalCoachId)throw new TypeError('Pending boss canonical coach does not match issued main');\r\n  if(pendingBoss.sourceVisualId!=null&&pendingBoss.sourceVisualId!==main.sourceVisualId)throw new TypeError('Pending boss visual does not match issued main');\r\n  if(pendingBoss.sourceVisualKind!=null&&pendingBoss.sourceVisualKind!==main.sourceVisualKind)throw new TypeError('Pending boss visual kind does not match issued main');\r\n }\r\n if(!Array.isArray(routes))throw new TypeError('Invalid routes');\r\n const arcadeIds=new Set(['clay','dance','jelly']),used=new Set();\r\n for(const route of routes){\r\n  if(!route||typeof route.afterId!=='string'||used.has(route.afterId)||!/^intro-/.test(route.afterId)||route.afterId==='intro-mainboss')throw new TypeError('Invalid room route');\r\n  if(![null,'grass','pond','papier_mache','treasure',...arcadeIds].includes(route.roomId))throw new TypeError('Invalid room route');\r\n  used.add(route.afterId);\r\n }\r\n const v1Routes=routes.filter(route=>!arcadeIds.has(route.roomId)&&route.roomId!=='treasure');\r\n const schedule=clone(createRunScheduleV1(seed,exerciseIds,pendingBoss==null?null:clone(pendingBoss),clone(v1Routes)));\r\n for(const route of routes){\r\n  if(route.roomId==='treasure'){\r\n   const anchor=schedule.nodes.findIndex(node=>node.id===route.afterId&&!node.returning);\r\n   if(anchor<0||anchor>=schedule.nodes.findIndex(node=>node.id==='intro-mainboss')||schedule.nodes[anchor].type!=='warmup')throw new TypeError('Invalid treasure route anchor');\r\n   schedule.nodes.splice(anchor+1,0,{id:`treasure-${route.afterId}`,type:'bonus',doorId:'treasure',roomType:'treasure_side_room'});\r\n   continue;\r\n  }\r\n  if(!arcadeIds.has(route.roomId))continue;\r\n  const anchor=schedule.nodes.findIndex(node=>node.id===route.afterId&&!node.returning);\r\n  if(anchor<0||anchor>=schedule.nodes.findIndex(node=>node.id==='intro-mainboss'))throw new TypeError('Invalid arcade room route anchor');\r\n  const room=ROOM_BY_ID[route.roomId];\r\n  const definition={id:`game-${route.afterId}-${route.roomId}`,doorId:route.roomId,roomType:room.roomType};\r\n  if(route.roomId==='jelly')definition.volume=room.variants[0];\r\n  const bundle=createFullRoomBundle(`${seed}:${route.afterId}`,definition);\r\n  schedule.nodes.splice(anchor+1,0,...clone(bundle.nodes));\r\n }\r\n schedule.nodes=schedule.nodes.map(node=>{\r\n  if(!bossIds.has(node.id))return node;\r\n  if(node.id==='intro-miniboss')return attach(node,issued.slots.mini1);\r\n  if(node.id==='intro-miniboss-2')return attach(node,issued.slots.mini2);\r\n  return attach(node,issued.slots.main);\r\n });\r\n schedule.scheduleVersion=SCHEDULE_VERSION_V2;\r\n schedule.assignment=clone(issued);\r\n return schedule;\r\n}\r\n\r\nexport function coachForRoomNodeV2(node){\r\n if(!node||typeof node!=='object'||Array.isArray(node))throw new TypeError('A room node is required.');\r\n let row;\r\n const boss=bossIds.has(node.id)||['miniboss','mainboss','boss_encounter'].includes(node.type)||node.roomType==='boss_encounter';\r\n if(boss){\r\n  if(typeof node.canonicalCoachId!=='string'||typeof node.sourceVisualId!=='string'||typeof node.sourceVisualKind!=='string')throw new TypeError('V2 boss node requires explicit coach binding');\r\n  row=resolveBinding({canonicalCoachId:node.canonicalCoachId,sourceVisualId:node.sourceVisualId,sourceVisualKind:node.sourceVisualKind});\r\n  if(node.coachId!==row.sourceVisualId)throw new TypeError('Boss visual and canonical bindings disagree');\r\n }else{\r\n  const identities=[];\r\n  for(const key of ['canonicalCoachId','sourceVisualId','coachId'])if(Object.hasOwn(node,key)){\r\n   if(typeof node[key]!=='string')throw new TypeError(`Invalid ${key}`);\r\n   const candidate=key==='canonicalCoachId'?byCanonical.get(node[key]):byVisual.get(node[key]);\r\n   if(!candidate)throw new TypeError(`Unknown ${key}`);\r\n   identities.push(candidate);\r\n  }\r\n  if(Object.hasOwn(node,'sourceVisualKind')&&typeof node.sourceVisualKind!=='string')throw new TypeError('Invalid sourceVisualKind');\r\n  if(!identities.length)throw new TypeError('Room node has no recognized coach presentation');\r\n  row=identities[0];\r\n  if(identities.some(candidate=>candidate!==row))throw new TypeError('Conflicting coach identity fields');\r\n  if(Object.hasOwn(node,'sourceVisualKind')&&node.sourceVisualKind!==row.sourceVisualKind)throw new TypeError('Coach visual kind and identity disagree');\r\n }\r\n return row;\r\n}\r\n\r\nexport function createRunScheduleForVersion(version,{seed,exerciseIds,assignment,manifest,pendingBoss=null,routes=[]}={}){\n if(version===4)return createRunScheduleV4(seed,exerciseIds,manifest,pendingBoss,routes);\n if(version===3)return createRunScheduleV3(seed,exerciseIds,manifest,pendingBoss,routes);\r\n if(version===1)return createRunScheduleV1(seed,exerciseIds,pendingBoss,routes);\r\n if(version===SCHEDULE_VERSION_V2)return createRunScheduleV2(seed,exerciseIds,assignment,pendingBoss,routes);\r\n throw new RangeError('Unknown run schedule version');\r\n}\r\nexport function coachForRoomNodeForVersion(version,node){\r\n if(version===1)return coachForRoomNodeV1(node);\r\n if(version===SCHEDULE_VERSION_V2||version===3||version===4)return coachForRoomNodeV2(node);\n throw new RangeError('Unknown run schedule version');\r\n}\r\n","modules/roguelite/run-schedule-v2-data.mjs":"// GENERATED at author time from boss-eligibility-v2-r1.json (sha256 e2ef0bc3d640b353d9c90218fa57acebd86da58c4134dabcc73dc7f51c4389ec); do not hand-edit.\n// Pure literal data: no imports, no runtime hashing, sorting or assignment. Consumed only by run-schedule-v2.mjs.\nexport const SOURCE_SHA256='e2ef0bc3d640b353d9c90218fa57acebd86da58c4134dabcc73dc7f51c4389ec';\nexport const TABLE_HASHES=Object.freeze({groups:'59b7c2a450e7f2d88dd5fa4e1c0fd2aa68457001effa1bef001ac5d886d9118d',eligibilityRows:'c878f47ea40d829c2d36e1ccd7473ad31903ba6bdf818f265479acb72840290f'});\nexport const GROUPS_LITERAL=[\n {\n  \"groupId\": \"boss-group-01\",\n  \"mini1\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"mini2\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"main\": \"roster/11-anvil-cask--clay-style_robot_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-02\",\n  \"mini1\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"mini2\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"main\": \"roster/10-petal-wisp--ghost_character_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-03\",\n  \"mini1\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"mini2\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"main\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-04\",\n  \"mini1\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"mini2\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"main\": \"roster/19-genie-multi--stylized_octopus_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-05\",\n  \"mini1\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"mini2\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"main\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-06\",\n  \"mini1\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"mini2\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"main\": \"roster/04-crest-wedge--stylized_3d_character\"\n },\n {\n  \"groupId\": \"boss-group-07\",\n  \"mini1\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"mini2\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"main\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-08\",\n  \"mini1\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"mini2\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"main\": \"roster/02-taper-tallstalk--3d_humanoid_figure\"\n },\n {\n  \"groupId\": \"boss-group-09\",\n  \"mini1\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"mini2\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"main\": \"roster/13-fan-split--stylized_3d_character2\"\n },\n {\n  \"groupId\": \"boss-group-10\",\n  \"mini1\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"mini2\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"main\": \"roster/20-lume--robotic_figure_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-11\",\n  \"mini1\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"mini2\": \"roster/01-seed-pearo--3d_character_model\",\n  \"main\": \"roster/08-shard-asym--geometric_robot_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-12\",\n  \"mini1\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"mini2\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"main\": \"roster/08-shard-asym--fantasy_creature_3d_model2\"\n },\n {\n  \"groupId\": \"boss-group-13\",\n  \"mini1\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"mini2\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"main\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-14\",\n  \"mini1\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"mini2\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"main\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-15\",\n  \"mini1\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"mini2\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"main\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-16\",\n  \"mini1\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"mini2\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"main\": \"roster/13-fan-split--stylized_toy_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-17\",\n  \"mini1\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"mini2\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"main\": \"roster/09-monolith-tanka--mini_robot_3d_model\"\n },\n {\n  \"groupId\": \"boss-group-18\",\n  \"mini1\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"mini2\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"main\": \"roster/09-monolith-tanka--robot_3d_model3\"\n },\n {\n  \"groupId\": \"boss-group-19\",\n  \"mini1\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"mini2\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"main\": \"roster/06-ridge-triad--robot_3d_model1\"\n },\n {\n  \"groupId\": \"boss-group-20\",\n  \"mini1\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"mini2\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"main\": \"roster/08-shard-asym--robot_3d_model4\"\n },\n {\n  \"groupId\": \"boss-group-21\",\n  \"mini1\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"mini2\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"main\": \"roster/16-spade-arch--stylized_humanoid_3d_model\"\n }\n];\nexport const ROWS_LITERAL=[\n {\n  \"canonicalCoachId\": \"myr5\",\n  \"baselineOwned\": true,\n  \"unlockTarget\": false,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"myr5\",\n  \"sourceVisualId\": \"worker:myr5\",\n  \"sourceVisualKind\": \"basic-worker\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   0,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"worker\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": true,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/01-seed-pearo--3d_character_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/01-seed-pearo--3d_character_model\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--3d_character_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   0,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--cute_alien_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/01-seed-pearo--blank_toy_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--3d_humanoid_figure\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"sourceVisualId\": \"coach:roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   0,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--circle_center\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--circle_center\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--stylized_3d_character1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/04-crest-wedge--robot_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/04-crest-wedge--robot_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/04-crest-wedge--stylized_3d_character\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/04-crest-wedge--stylized_3d_character\",\n  \"sourceVisualId\": \"coach:roster/04-crest-wedge--stylized_3d_character\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   96,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"sourceVisualId\": \"coach:roster/06-ridge-triad--geometric_robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/06-ridge-triad--robot_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/06-ridge-triad--robot_3d_model1\",\n  \"sourceVisualId\": \"coach:roster/06-ridge-triad--robot_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/07-bulb-sphereling--cute_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/07-bulb-sphereling--humanoid_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/08-shard-asym--geometric_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/08-shard-asym--geometric_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--geometric_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/08-shard-asym--robot_3d_model4\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/08-shard-asym--robot_3d_model4\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--robot_3d_model4\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--fantasy_creature_3d_model2\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/08-shard-asym--stylized_action_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   192,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/09-monolith-tanka--3d_robot_model\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--3d_robot_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--boxy_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--mini_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/09-monolith-tanka--robot_3d_model3\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/09-monolith-tanka--robot_3d_model3\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--robot_3d_model3\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/09-monolith-tanka--white_horned_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--stylized_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/10-petal-wisp--ghost_character_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/10-petal-wisp--ghost_character_3d_model\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--ghost_character_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"sourceVisualId\": \"coach:roster/10-petal-wisp--fantasy_creature_3d_model4\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   288,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/11-anvil-cask--clay-style_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   384,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"sourceVisualId\": \"coach:roster/12-seedpod-snailslug--stylized_slug_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   384,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"sourceVisualId\": \"coach:roster/12-seedpod-snailslug--stylized_worm_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   384,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/13-fan-split--stylized_3d_character2\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/13-fan-split--stylized_3d_character2\",\n  \"sourceVisualId\": \"coach:roster/13-fan-split--stylized_3d_character2\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   384,\n   64,\n   96\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/13-fan-split--stylized_toy_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/13-fan-split--stylized_toy_3d_model\",\n  \"sourceVisualId\": \"coach:roster/13-fan-split--stylized_toy_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   384,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/14-chisel-spire--cone_head_3d_model\",\n  \"sourceVisualId\": \"coach:roster/14-chisel-spire--cone_head_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   384,\n   64,\n   96\n  ],\n  \"section\": \"martial-arts\",\n  \"element\": {\n   \"id\": \"fire\",\n   \"sheet\": \"11_fire_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"martial-arts\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/14-chisel-spire--low_poly_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   384,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/15-orbital-coili--robot_character_3d_model\",\n  \"sourceVisualId\": \"coach:roster/15-orbital-coili--robot_character_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   384,\n   64,\n   96\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/16-spade-arch--pyramid_head_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/16-spade-arch--stylized_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"chest\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ff4a3a\"\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"chest\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--3d_character_figurine\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   128,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"sourceVisualId\": \"coach:roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--dragon_creature_3d_model\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=0,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   0,\n   0,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model1\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=48,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   48,\n   0,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--fantasy_creature_3d_model3\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=96,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   96,\n   0,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--four-legged_robot_3d_model\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=144,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   144,\n   0,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model1\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=192,0,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   192,\n   0,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--quadruped_robot_3d_model\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=0,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   0,\n   36,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"hauler\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": true,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--robotic_dog_3d_model\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=48,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   48,\n   36,\n   48,\n   36\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\n  \"sourceVisualId\": \"/pod/gala-coaches/pets.png#frame=96,36,48,36\",\n  \"sourceVisualKind\": \"four-legged-source\",\n  \"atlasPath\": \"/pod/gala-coaches/pets.png\",\n  \"frame\": [\n   96,\n   36,\n   48,\n   36\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"scout\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--fantasy_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--multi-armed_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   480,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"sourceVisualId\": \"coach:roster/19-genie-multi--stylized_octopus_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/20-lume--robotic_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/20-lume--robotic_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/20-lume--robotic_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"cardio\",\n  \"element\": {\n   \"id\": \"electric\",\n   \"sheet\": \"pixelated_tilesheet.png\",\n   \"cols\": 4,\n   \"rows\": 2\n  },\n  \"raidRole\": \"commander\",\n  \"abilityFaction\": \"cardio\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/22-curve--stylized_alien_3d_model\",\n  \"sourceVisualId\": \"coach:roster/22-curve--stylized_alien_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   192,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"yoga\",\n  \"element\": {\n   \"id\": \"nausea\",\n   \"sheet\": \"13_vortex_spritesheet.png\",\n   \"cols\": 8,\n   \"rows\": 8\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"yoga\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/22-curve--stylized_cartoon_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   256,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"quads\",\n  \"element\": {\n   \"id\": \"rocks\"\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"quads\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"baselineOwned\": true,\n  \"unlockTarget\": false,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--blob_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   320,\n   576,\n   64,\n   96\n  ],\n  \"section\": null,\n  \"element\": null,\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": null,\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   384,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   448,\n   576,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"skirmisher\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   0,\n   672,\n   64,\n   96\n  ],\n  \"section\": \"arms-shoulders\",\n  \"element\": {\n   \"id\": \"laser\",\n   \"color\": \"#ffd36b\"\n  },\n  \"raidRole\": \"frontline\",\n  \"abilityFaction\": \"arms-shoulders\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n },\n {\n  \"canonicalCoachId\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"baselineOwned\": false,\n  \"unlockTarget\": true,\n  \"eligibleForms\": [\n   \"mini\",\n   \"main\"\n  ],\n  \"sourceCoachId\": \"roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"sourceVisualId\": \"coach:roster/23-blob-texture-bodies--sand_creature_3d_model\",\n  \"sourceVisualKind\": \"coach-unit\",\n  \"atlasPath\": \"/pod/gala-coaches/bodies.png\",\n  \"frame\": [\n   64,\n   672,\n   64,\n   96\n  ],\n  \"section\": \"glutes\",\n  \"element\": {\n   \"id\": \"poison\",\n   \"sheet\": \"17_felspell_spritesheet.png\",\n   \"cols\": 10,\n   \"rows\": 10\n  },\n  \"raidRole\": \"support\",\n  \"abilityFaction\": \"glutes\",\n  \"capabilities\": {\n   \"economyWorker\": false,\n   \"expeditionCarrier\": false,\n   \"air\": false,\n   \"transportCarrier\": false\n  }\n }\n];\nexport const ASSIGNMENT_IDS=Object.freeze({\n \"boss-group-01\": \"cee1a19266e5b90aeb27f3926c5d57aaab55c632125432f60782920e0a87e70c\",\n \"boss-group-02\": \"e489f399da053f8eae6072484afd024d94e027c5d4de8725cac1fc9d20064ad6\",\n \"boss-group-03\": \"53944b8ddf0b0043bd75ba1443c6320aa6423699dd5a54d1a72234a389eef207\",\n \"boss-group-04\": \"524fe1cdf795bdfa6237c23fdca90504c43a451385c669a43aded4c8e721a308\",\n \"boss-group-05\": \"27d483e7c31a9803c7eea443f04e8d39aea84c18046614edc5681a027b9b121e\",\n \"boss-group-06\": \"cf293678a29475ed064a5cf49e6d7b0faeb75ae2e3e71851597235bff2736f10\",\n \"boss-group-07\": \"cd591a442eadad9dcd725f659eea328eaa3cedeb1969c2114a41683b5a26b079\",\n \"boss-group-08\": \"49957d9d9642024448604bc9dd1797a03513ec13ac075301a0001faf543662dc\",\n \"boss-group-09\": \"70b92ca9adec2c7b7128dfc77a3afc4d879e5656233f27a4a2de3f56859bb43e\",\n \"boss-group-10\": \"e1dad893d53684e9f053e11572b62b08341585693847e17f44037d245ab31b99\",\n \"boss-group-11\": \"90781edb11e20a06df82ba0fb4c224e0133abf1cf4deff7f24df160afee9f012\",\n \"boss-group-12\": \"29ea81245387ca8f2b3182c0aec02fc686ce0033def2e2f0f832a89ae639b322\",\n \"boss-group-13\": \"9ff6a479d2298f02ac5dd7dab8202f2a93470102612b7eb79bbd007c5463b512\",\n \"boss-group-14\": \"d1bfb4650278a38c6dca3a28576fbd332431b544904b3f7d832f89ad0c400ae2\",\n \"boss-group-15\": \"e1a4df4bcbb64befd9c4db2345fad2a7f0b908059e7ebdd34eddf9da0c53aa6b\",\n \"boss-group-16\": \"fba3112e5ca3b7b81f58bb896a9f03a56ea4e4b71007f87c7077a73c625da591\",\n \"boss-group-17\": \"437601ffaaa57fdfc95fa2a49f9783bf00dfb17c9a4387926d3e6c21b2792e54\",\n \"boss-group-18\": \"3baf753b55bbe324810f0c34355842e1b7c1292c9880df8416730541a6549583\",\n \"boss-group-19\": \"82d2b89f892571bb9e2a6248a308f96798ee2785a217bb7e6f5f8b127a8bb770\",\n \"boss-group-20\": \"fec4b553506a16161778bb59f662fad025c37e37614b2fa1503e724e873fd109\",\n \"boss-group-21\": \"2b054a0a20dd2ee72bfd8cbb03c5ad5c5b5ddb50b0ddb8c0dd49d23cb201b483\"\n});\n","modules/roguelite/run-schedule-v2.mjs":"// R1 authored-triples boss schedule (pure). Data comes only from the generated literal module.\n// No DOM, THREE, fetch, storage, clock, RNG or account authority. Not wired to any consumer yet.\nimport {GROUPS_LITERAL,ROWS_LITERAL,TABLE_HASHES,ASSIGNMENT_IDS} from './run-schedule-v2-data.mjs';\n\nexport const SCHEDULE_VERSION_V2=2;\nexport const BOSS_POLICY_ID='authored-triples-clear-v1';\n\nconst GROUP_COUNT=GROUPS_LITERAL.length;\n\nfunction deepFreeze(value){\n if(value&&typeof value==='object'&&!Object.isFrozen(value)){\n  for(const child of Object.values(value))deepFreeze(child);\n  Object.freeze(value);\n }\n return value;\n}\n\nexport const BOSS_GROUPS=deepFreeze(structuredClone(GROUPS_LITERAL));\nexport const BOSS_ROWS=deepFreeze(structuredClone(ROWS_LITERAL));\nconst ROW_BY_ID=new Map(BOSS_ROWS.map(row=>[row.canonicalCoachId,row]));\nconst ASSIGNMENT_BY_GROUP=new Map(Object.entries(ASSIGNMENT_IDS));\nconst TABLE_HASHES_FROZEN=Object.freeze({groups:TABLE_HASHES.groups,eligibilityRows:TABLE_HASHES.eligibilityRows});\n\n// ponytail: Map lookup on canonical IDs only; no prefix stripping or visual-ID inference. Add a visual index only if a caller needs it.\nexport function resolveBossPresentationV2(canonicalCoachId){\n if(typeof canonicalCoachId!=='string'||!ROW_BY_ID.has(canonicalCoachId))throw new TypeError('unknown canonical coach id');\n return ROW_BY_ID.get(canonicalCoachId);\n}\n\nfunction bindingFor(canonicalCoachId){\n const row=resolveBossPresentationV2(canonicalCoachId);\n return Object.freeze({canonicalCoachId:row.canonicalCoachId,sourceVisualId:row.sourceVisualId,sourceVisualKind:row.sourceVisualKind});\n}\n\nexport function bossAssignmentForClearCount(clearCount){\n if(!Number.isSafeInteger(clearCount)||clearCount<0)throw new RangeError('clearCount must be a non-negative safe integer');\n const groupIndex=clearCount%GROUP_COUNT;\n const group=BOSS_GROUPS[groupIndex];\n return Object.freeze({\n  policyId:BOSS_POLICY_ID,\n  scheduleVersion:SCHEDULE_VERSION_V2,\n  groupId:group.groupId,\n  groupIndex,\n  cycle:Math.floor(clearCount/GROUP_COUNT),\n  clearOrdinal:clearCount,\n  slots:Object.freeze({mini1:bindingFor(group.mini1),mini2:bindingFor(group.mini2),main:bindingFor(group.main)}),\n  tableHashes:TABLE_HASHES_FROZEN,\n  assignmentId:ASSIGNMENT_BY_GROUP.get(group.groupId),\n });\n}\n","modules/roguelite/run-schedule-v3.mjs":"// GPT-OSS draft, integrated with strict manifest and stable level identities.\r\nimport {bossAssignmentForClearCount} from './run-schedule-v2.mjs';\r\nimport {createRunScheduleV2} from './run-schedule-v2-builder.mjs';\r\nconst canonical=v=>v===null||typeof v!=='object'?JSON.stringify(v):Array.isArray(v)?`[${v.map(canonical)}]`:`{${Object.keys(v).sort().map(k=>`${JSON.stringify(k)}:${canonical(v[k])}`)}}`;\r\nexport function createRunManifest(clearOrdinal){\r\n if(!Number.isSafeInteger(clearOrdinal)||clearOrdinal<0||!Number.isSafeInteger(clearOrdinal+4))throw new TypeError('Invalid manifest ordinal');\r\n return {version:1,levels:Array.from({length:5},(_,i)=>({levelIndex:i+1,assignment:bossAssignmentForClearCount(clearOrdinal+i)}))};\r\n}\r\nexport function validateRunManifest(manifest){\r\n const expected=createRunManifest(manifest?.levels?.[0]?.assignment?.clearOrdinal);\r\n if(canonical(manifest)!==canonical(expected))throw new TypeError('Invalid issued run manifest');\r\n return expected;\r\n}\r\nexport function createRunScheduleV3(seed,exerciseIds,manifest,pendingBoss=null,routes=[]){\r\n const issued=validateRunManifest(manifest);\r\n if(!Array.isArray(routes)||routes.some(r=>!/^level-[1-5]-intro-/.test(r?.afterId)))throw new TypeError('Invalid level route');\r\n const pendingBosses=Array.isArray(pendingBoss)?pendingBoss:pendingBoss?[pendingBoss]:[];\r\n if(pendingBosses.some(b=>!/^level-[1-5]-intro-mainboss$/.test(b.bossId))||new Set(pendingBosses.map(b=>b.bossId)).size!==pendingBosses.length)throw new TypeError('Invalid level pending boss');\r\n const nodes=[];\r\n for(const {levelIndex,assignment} of issued.levels){\r\n  const prefix=`level-${levelIndex}-`,b=pendingBosses.find(b=>b.bossId.startsWith(prefix)),pending=b?{...b,bossId:b.bossId.slice(prefix.length)}:null;\r\n  const localRoutes=routes.filter(r=>r.afterId.startsWith(prefix)).map(r=>({...r,afterId:r.afterId.slice(prefix.length)}));\r\n  const local=createRunScheduleV2(`${seed}:level-${levelIndex}`,exerciseIds,assignment,pending,localRoutes);\r\n  for(const n of local.nodes){\r\n   const bossSlot=n.id==='intro-miniboss'?'mini1':n.id==='intro-miniboss-2'?'mini2':n.id==='intro-mainboss'?'main':null;\r\n   nodes.push({...n,id:prefix+n.id,levelIndex,...(bossSlot?{bossSlot}: {})});\r\n  }\r\n  nodes.push({id:prefix+'recovery',type:'recovery',roomType:'level_recovery',levelIndex});\r\n }\r\n return {scheduleVersion:3,manifest:issued,nodes};\r\n}\r\n","modules/roguelite/run-schedule-v4.mjs":"// Rescue policy is independent of authored-triples manifests and legacy saves.\nimport {BOSS_ROWS} from './run-schedule-v2.mjs';\nimport {TABLE_HASHES} from './run-schedule-v2-data.mjs';\nimport {createIntroSchedule,createFullRoomBundle} from './warmup-flow.mjs';\nimport {ROOM_BY_ID} from './room-contracts.mjs';\n\nexport const RESCUE_POLICY_ID='rescue-six-v1';\nexport const RESCUE_SCHEDULE_VERSION=4;\nexport const RESCUE_ROLE_IDS=Object.freeze(['pair-1-mini','pair-1-main','pair-2-mini','pair-2-main','pair-3-mini','pair-3-main']);\nconst rows=new Map(BOSS_ROWS.map(row=>[row.canonicalCoachId,row]));\nconst clone=value=>structuredClone(value);\nconst exact=(value,keys)=>value&&typeof value==='object'&&!Array.isArray(value)&&Object.keys(value).length===keys.length&&keys.every(k=>Object.hasOwn(value,k));\nconst required=index=>Array.from({length:15},(_,i)=>`level-${index}-intro-${i+1}`);\nconst allowedRooms=new Set(['grass','pond','papier_mache','clay','dance','jelly','treasure','wood','quilt','flat_paper']);\n\n/** The server supplies entropy once and persists this full result before issuing it. */\nexport function createRescueRunManifest({rescuedCoachIds=[],random}={}){\n if(typeof random!=='function'||!Array.isArray(rescuedCoachIds)||rescuedCoachIds.some(id=>!rows.has(id)))throw new TypeError('Manifest requires server entropy and canonical rescue IDs');\n const rescued=new Set(rescuedCoachIds),used=new Set();\n const draw=()=>{const n=random();if(!Number.isFinite(n)||n<0||n>=1)throw new TypeError('Invalid manifest entropy');return n;};\n const pool=[...BOSS_ROWS];\n for(let i=pool.length-1;i>0;i--){const j=Math.floor(draw()*(i+1));[pool[i],pool[j]]=[pool[j],pool[i]];}\n const levels=Array.from({length:5},(_,i)=>{\n  const levelIndex=i+1;\n  const encounters=RESCUE_ROLE_IDS.map(roleId=>{\n   const form=roleId.endsWith('-mini')?'mini':'main';\n   const eligible=pool.filter(row=>row.eligibleForms.includes(form));\n   const row=eligible.find(row=>!used.has(row.canonicalCoachId)&&row.unlockTarget&&!rescued.has(row.canonicalCoachId))\n    ??eligible.find(row=>!used.has(row.canonicalCoachId))??eligible[0];\n   if(!row)throw new RangeError('No eligible canonical coach');\n   used.add(row.canonicalCoachId);\n   return {roleId,nodeId:`level-${levelIndex}-${roleId}`,kind:form==='mini'?'miniboss':'mainboss',canonicalCoachId:row.canonicalCoachId,sourceVisualId:row.sourceVisualId,sourceVisualKind:row.sourceVisualKind,patternSeed:Math.floor(draw()*4294967296)};\n  });\n  return {levelIndex,encounters,requiredRoomIds:required(levelIndex)};\n });\n return {version:2,scheduleVersion:4,policyId:RESCUE_POLICY_ID,tableHashes:{...TABLE_HASHES},levels};\n}\n\n/** Strict shape/identity validation never redraws or routes through old assignments. */\nexport function validateRescueRunManifest(manifest){\n if(!exact(manifest,['version','scheduleVersion','policyId','tableHashes','levels'])||manifest.version!==2||manifest.scheduleVersion!==4||manifest.policyId!==RESCUE_POLICY_ID\n  ||!exact(manifest.tableHashes,['groups','eligibilityRows'])||Object.keys(TABLE_HASHES).some(k=>manifest.tableHashes[k]!==TABLE_HASHES[k])||!Array.isArray(manifest.levels)||manifest.levels.length!==5)throw new TypeError('Invalid rescue manifest');\n const assigned=new Set();\n for(const [i,level] of manifest.levels.entries()){\n  if(!exact(level,['levelIndex','encounters','requiredRoomIds'])||level.levelIndex!==i+1||!Array.isArray(level.encounters)||level.encounters.length!==6||JSON.stringify(level.requiredRoomIds)!==JSON.stringify(required(i+1)))throw new TypeError('Invalid rescue level');\n  for(const [j,encounter] of level.encounters.entries()){\n   const row=rows.get(encounter?.canonicalCoachId),form=j%2?'main':'mini';\n   if(!exact(encounter,['roleId','nodeId','kind','canonicalCoachId','sourceVisualId','sourceVisualKind','patternSeed'])||encounter.roleId!==RESCUE_ROLE_IDS[j]||encounter.nodeId!==`level-${i+1}-${RESCUE_ROLE_IDS[j]}`||encounter.kind!==(form==='mini'?'miniboss':'mainboss')\n    ||!row?.eligibleForms.includes(form)||assigned.has(encounter.canonicalCoachId)||encounter.sourceVisualId!==row.sourceVisualId||encounter.sourceVisualKind!==row.sourceVisualKind||!Number.isInteger(encounter.patternSeed)||encounter.patternSeed<0||encounter.patternSeed>4294967295)throw new TypeError('Invalid rescue encounter');\n   assigned.add(encounter.canonicalCoachId);\n  }\n }\n return clone(manifest);\n}\n\nexport function rescueLevelRequirements(manifest,levelIndex){\n const level=validateRescueRunManifest(manifest).levels[levelIndex-1];\n if(!Number.isInteger(levelIndex)||!level)throw new RangeError('Invalid rescue level index');\n return {bossIds:level.encounters.map(e=>e.nodeId),requiredRoomIds:[...level.requiredRoomIds]};\n}\n\nexport function createRunScheduleV4(seed,exerciseIds,manifest,pendingBoss=null,routes=[]){\n const issued=validateRescueRunManifest(manifest),pending=Array.isArray(pendingBoss)?pendingBoss:pendingBoss?[pendingBoss]:[],nodes=[];\n const encounters=issued.levels.flatMap(l=>l.encounters);\n if(new Set(pending.map(b=>b?.bossId)).size!==pending.length||pending.some(b=>{\n  const e=encounters.find(e=>e.nodeId===b?.bossId&&e.kind==='mainboss');\n  return !e||b.coachId!==e.sourceVisualId||String(b.seed)!==String(e.patternSeed)||b.phase!==1||b.status!=='escaped'||!b.phase2Pending;\n }))throw new TypeError('Invalid pending rescue boss');\n if(!Array.isArray(routes))throw new TypeError('Invalid rescue routes');\n for(const level of issued.levels){\n  const {levelIndex}=level,prefix=`level-${levelIndex}-`;\n  const warmups=createIntroSchedule(`${seed}:level-${levelIndex}`,{exerciseIds}).nodes.filter(n=>n.type==='warmup');\n  let cursor=0;\n  for(const [i,encounter] of level.encounters.entries()){\n   for(let j=0;j<(i%2?2:3);j++){const node=warmups[cursor++];nodes.push({...node,id:prefix+node.id,levelIndex});}\n   const boss={id:encounter.nodeId,type:encounter.kind,level:levelIndex,levelIndex,bossSlot:encounter.roleId,roleId:encounter.roleId,coachId:encounter.sourceVisualId,canonicalCoachId:encounter.canonicalCoachId,sourceVisualId:encounter.sourceVisualId,sourceVisualKind:encounter.sourceVisualKind,patternSeed:encounter.patternSeed,entrance:{kind:'crystal-door',grimoireAsset:'ice.glb'},coreRoomCoverageExempt:true};\n   nodes.push(boss);\n   if(pending.some(b=>b.bossId===encounter.nodeId))nodes.push({id:encounter.nodeId+'-reentry',type:'recovery',roomType:'boss_recovery',levelIndex,encounterId:encounter.nodeId},{...boss,returning:true});\n  }\n  nodes.push({id:prefix+'recovery',type:'recovery',roomType:'level_recovery',levelIndex});\n }\n const used=new Set();\n for(const route of routes){\n  const anchor=nodes.findIndex(n=>n.id===route?.afterId&&!n.returning);\n  if(!exact(route,['afterId','roomId'])||anchor<0||nodes[anchor].type!=='warmup'||used.has(route.afterId)||route.roomId!==null&&!allowedRooms.has(route.roomId))throw new TypeError('Invalid rescue route');\n  used.add(route.afterId);if(route.roomId===null)continue;\n  const room=route.roomId==='treasure'?{roomType:'treasure_side_room'}:ROOM_BY_ID[route.roomId];if(!room)throw new TypeError('Unknown rescue room');\n  const levelIndex=nodes[anchor].levelIndex,id=`level-${levelIndex}-game-${route.afterId.replace(/^level-\\d+-/,'')}-${route.roomId}`;\n  const definition={id,doorId:route.roomId,roomType:room.roomType,levelIndex};\n  if(route.roomId==='jelly')definition.volume=room.variants[0];\n  const direct=['papier_mache','treasure','wood','quilt','flat_paper'].includes(route.roomId);\n  const extra=direct?[{...definition,type:route.roomId==='treasure'?'bonus':'full-game'}]:createFullRoomBundle(`${seed}:${route.afterId}`,definition).nodes;\n  nodes.splice(anchor+1,0,...extra.map(n=>({...n,levelIndex})));\n }\n return {scheduleVersion:4,manifest:issued,nodes};\n}\n","modules/roguelite/run-schedule.mjs":"import {createIntroSchedule,createFullRoomBundle} from './warmup-flow.mjs';\nimport {COACH_UNITS} from '../coach-world/unit-catalog.mjs';\n\nexport const RUN_SCHEDULE_VERSION=1;\n\nexport function createRunSchedule(seed,exerciseIds,pendingBoss=null,routes=[]){\n const schedule=createIntroSchedule(seed,{exerciseIds});\n if(pendingBoss){\n  const rooms=createIntroSchedule(`${seed}:pursuit`,{exerciseIds}).nodes.slice(0,5)\n   .map((node,index)=>({...node,id:`recovery-${index+1}`}));\n  schedule.nodes.push(...rooms,{...schedule.nodes.at(-1),coachId:pendingBoss.coachId,returning:true});\n }\n const used=new Set();\n for(const route of routes){\n  const anchor=schedule.nodes.findIndex(node=>node.id===route.afterId&&!node.returning);\n  if(anchor<0||!/^intro-/.test(route.afterId)||route.afterId==='intro-mainboss'||used.has(route.afterId)||![null,'grass','pond','papier_mache'].includes(route.roomId))throw new TypeError('Invalid room route');\n  used.add(route.afterId);\n  if(route.roomId){\n   if(route.roomId==='papier_mache'){\n    schedule.nodes.splice(anchor+1,0,{id:`game-${route.afterId}-papier_mache`,type:'full-game',doorId:'papier_mache',roomType:'corgi_shop'});continue;\n   }\n   const bundle=createFullRoomBundle(`${seed}:${route.afterId}`,{id:`game-${route.afterId}-${route.roomId}`,doorId:route.roomId,roomType:route.roomId==='grass'?'coin_chase':'healing_waters'});\n   schedule.nodes.splice(anchor+1,0,...bundle.nodes);\n  }\n }\n return schedule;\n}\n\nconst isBoss=node=>['miniboss','mainboss','boss_encounter'].includes(node?.type)||node?.roomType==='boss_encounter';\nconst hash=value=>{let h=2166136261;for(const c of String(value))h=Math.imul(h^c.charCodeAt(0),16777619);return h>>>0;};\n\n/** Resolve the scheduled coach exactly, or pick a stable hard/expert coach for a boss node. */\nexport function coachForRoomNode(node){\n if(!node||typeof node!=='object')throw new TypeError('A room node is required.');\n let coach=null;\n if(node.coachId){coach=COACH_UNITS.find(unit=>unit.id===node.coachId);if(!coach)throw new Error(`Unknown scheduled coach: ${node.coachId}`);}\n else if(isBoss(node)){const bosses=COACH_UNITS.filter(unit=>unit.difficulty==='hard'||unit.difficulty==='expert');coach=bosses[hash(node.id??node.type)%bosses.length];}\n else coach=COACH_UNITS[0];\n return coach;\n}\n","modules/roguelite/run-shop.mjs":"// Run-scoped adapter around shop-state. The parent run remains the coin authority:\n// its balance is copied into this ledger only when a new room encounter is opened.\nimport {\n  createRun, loadRun, openShop, grab, returnItem, checkout, exitShop, attackMerchant,\n  advanceTime, pause, resume,\n} from './shop-state.mjs';\nimport {FITNESS_ITEMS,FITNESS_CATALOG_VERSION} from './fitness-item-catalog.mjs';\nimport {CLASS_IDS} from './class-combat.mjs';\nimport {grantBoon} from './boons.mjs';\nimport {chooseRunStat} from './run-stats.mjs';\n\nexport const RUN_SHOP_STOCK_PROPOSAL = Object.freeze([\n  Object.freeze({ id: 'dagger', price: 3 }),\n  Object.freeze({ id: 'wand', price: 4 }),\n  Object.freeze({ id: 'hammer', price: 5 }),\n]);\nexport const RUN_SHOP_BAIT_PROPOSAL = Object.freeze({ id: 'wand', price: 4 });\n// Short stable shelf IDs keep legacy checkout receipt keys bounded; canonical catalog identity is pinned alongside them.\nexport const RUN_SHOP_FITNESS_OFFERS=Object.freeze(FITNESS_ITEMS.map((row,i)=>Object.freeze({id:`fit${String(i+1).padStart(2,'0')}`,catalogItemId:row.id,name:row.name,description:row.description,price:row.price,effectRef:row.effectRef})));\nexport function isRunShopItemId(itemId,policyVersion=null){\n return (policyVersion===null||policyVersion===4)&&RUN_SHOP_FITNESS_OFFERS.some(row=>row.id===itemId)||(policyVersion===null||policyVersion!==4)&&RUN_SHOP_STOCK_PROPOSAL.some(row=>row.id===itemId);\n}\nconst clone=value=>structuredClone(value);\nconst canonical=value=>value===null||typeof value!=='object'?JSON.stringify(value):Array.isArray(value)?`[${value.map(canonical)}]`:`{${Object.keys(value).sort().map(k=>JSON.stringify(k)+':'+canonical(value[k])).join(',')}}`;\nfunction issuedOffers(run,shopId){\n const eligible=RUN_SHOP_FITNESS_OFFERS.filter(row=>row.effectRef.kind!=='weapon'||row.effectRef.styleId===run.combatClass);\n const anchor=/intro-(\\d+)-papier_mache$/.exec(shopId);\n const group=anchor?(Number(anchor[1])-1)%2:[...shopId].reduce((h,c)=>(h*31+c.charCodeAt(0))>>>0,0)%2;\n return clone(eligible.slice(group*3,group*3+3));\n}\nexport function runShopOffers(run,shopId){\n if(run?.policyVersion!==4)return clone(RUN_SHOP_STOCK_PROPOSAL);\n return clone(run.offers?.[shopId]??[]);\n}\nexport const RUN_SHOP_PAUSE_REASONS = Object.freeze(['tutorial', 'tracking-lost', 'tracking-stale', 'camera-handoff', 'handoff', 'background', 'user']);\nconst MAX_BALANCE = 1_000_000;\nconst safeId = value => typeof value === 'string' && value.length > 0 && value.length <= 128;\nconst balance = value => {\n  if (!Number.isSafeInteger(value) || value < 0 || value > MAX_BALANCE) throw new RangeError('runCoins must be a nonnegative safe integer');\n  return value;\n};\nconst validRun = run => {\n  if (!run || typeof run !== 'object' || !safeId(run.runId) || !Array.isArray(run.pauses) ||\n      run.pauses.some(reason => !RUN_SHOP_PAUSE_REASONS.includes(reason))) throw new TypeError('Invalid run shop ledger');\n};\nconst result = (run, op) => ({ state: run, ok: op.ok, reason: op.reason, events: op.events, runCoins: run.coins });\nconst pausedResult = run => result(run, { ok: false, reason: 'paused', events: [] });\n\nexport function createRunShop({ runId, runCoins = 0, seed = 0,policyVersion=1,combatClass=null } = {}) {\n  if (!safeId(runId)) throw new TypeError('runId must be a stable string id');\n  if(![1,4].includes(policyVersion)||policyVersion===4&&!CLASS_IDS.includes(combatClass))throw new TypeError('Invalid Merchant offer policy/style');\n  const run=createRun({ runId, coins: balance(runCoins), seed });\n  return policyVersion===4?{...run,policyVersion:4,catalogVersion:FITNESS_CATALOG_VERSION,combatClass,offers:{}}:run;\n}\n\n// Strictly restore; never sync the parent balance on reload, since that could erase\n// purchases. If a shop is still open, the active ledger balance must match the parent.\n// Outside an open shop, later parent earnings may make runCoins greater than this ledger.\nexport function restoreRunShop(saved, { runId, runCoins,policyVersion,combatClass } = {}) {\n  if (!saved || typeof saved !== 'object' || !safeId(runId) || saved.runId !== runId) throw new TypeError('run shop identity mismatch');\n  if(policyVersion===4&&saved.policyVersion!==4||policyVersion!==undefined&&policyVersion!==4&&saved.policyVersion===4)throw new TypeError('Merchant policy mismatch');\n  if(saved.policyVersion===4&&combatClass!==undefined&&saved.combatClass!==combatClass)throw new TypeError('Merchant style mismatch');\n  let run;\n  if(saved.policyVersion===4){\n    const {policyVersion:version,catalogVersion,combatClass,offers,...legacy}=saved;\n    if(version!==4||catalogVersion!==FITNESS_CATALOG_VERSION||!CLASS_IDS.includes(combatClass)||!offers||typeof offers!=='object'||Array.isArray(offers))throw new TypeError('Invalid pinned Merchant offers');\n    run={...loadRun(JSON.stringify(legacy)),policyVersion:version,catalogVersion,combatClass,offers:clone(offers)};\n    if(canonical(Object.keys(offers).sort())!==canonical(Object.keys(run.shops).sort()))throw new TypeError('Merchant offers do not match visits');\n    for(const shopId of Object.keys(run.shops)){\n      const expected=issuedOffers(run,shopId),shop=run.shops[shopId],byId=new Map(expected.map(item=>[item.id,item]));\n      if(canonical(offers[shopId])!==canonical(expected)||shop.shelf.length!==3)throw new TypeError('Merchant offer binding changed');\n      for(const item of [...shop.shelf.filter(Boolean),...shop.basket,...run.inventory.filter(item=>item.shopId===shopId)])if(byId.get(item.id)?.price!==item.price)throw new TypeError('Merchant item/price not issued');\n      for(const receipt of Object.values(run.receipts).filter(r=>r.shopId===shopId)){\n        if(receipt.items.some(id=>!byId.has(id))||receipt.type==='checkout'&&receipt.total!==receipt.items.reduce((n,id)=>n+byId.get(id).price,0))throw new TypeError('Merchant receipt does not match issued offers');\n      }\n    }\n  }else run=loadRun(JSON.stringify(saved));\n  if (runCoins !== undefined) {\n    balance(runCoins);\n    const active = run.activeShopId ? run.shops[run.activeShopId] : null;\n    if (active?.status === 'open' ? run.coins !== runCoins : run.coins > runCoins) {\n      throw new RangeError('saved shop balance is inconsistent with parent run coins');\n    }\n  }\n  return run;\n}\n\n// shopId is a unique room encounter id. Existing open encounters resume without a\n// balance sync; new encounters take one parent-coin snapshot at entry.\nexport function enterRunShop(run, { shopId, runCoins } = {}) {\n  validRun(run);\n  if (!safeId(shopId)) throw new TypeError('shopId must be a unique stable encounter id');\n  balance(runCoins);\n  if (Object.hasOwn(run.shops, shopId)) {\n    const old = run.shops[shopId];\n    if (old.status !== 'open') return result(run, { ok: false, reason: 'encounter-already-ended', events: [] });\n    const resumed = { ...run, activeShopId: shopId };\n    return result(resumed, { ok: true, reason: 'resumed', events: [] });\n  }\n  const seeded = { ...run, coins: runCoins };\n  if(seeded.policyVersion===4)seeded.offers={...seeded.offers,[shopId]:issuedOffers(seeded,shopId)};\n  const ambush = !seeded.merchantAvailable;\n  const stock=runShopOffers(seeded,shopId).map(({id,price})=>({id,price}));\n  const opened = openShop(seeded, {\n    shopId,\n    shelf: stock,\n    ...(ambush ? { bait:seeded.policyVersion===4?stock[0]:RUN_SHOP_BAIT_PROPOSAL } : {}),\n  });\n  return result(opened.run, opened);\n}\n\nexport function setRunShopPaused(run, reason, isPaused) {\n  validRun(run);\n  if (!RUN_SHOP_PAUSE_REASONS.includes(reason) || typeof isPaused !== 'boolean') throw new TypeError('invalid shop pause update');\n  const op = isPaused ? pause(run, reason) : resume(run, reason);\n  return op.run;\n}\n\nfunction act(run, shopId, operation, ...args) {\n  validRun(run);\n  if (!safeId(shopId)) throw new TypeError('shopId is required');\n  const op = operation(run, shopId, ...args);\n  return result(op.run, op);\n}\nexport const grabRunShop = (run, shopId, itemId) => act(run, shopId, grab, itemId);\nexport const returnRunShopItem = (run, shopId, itemId) => act(run, shopId, returnItem, itemId);\nexport const returnRunShop = returnRunShopItem;\nexport const checkoutRunShop = (run, shopId) => act(run, shopId, checkout);\nexport const exitRunShop = (run, shopId) => act(run, shopId, exitShop);\nexport const attackRunShop = (run, shopId) => act(run, shopId, attackMerchant);\n\n/** Existing checkout receipts settle effects in the same enclosing run transaction, never a second ledger. */\nexport function settleRunShopPurchases(runState,beforeLedger,afterLedger=runState.shopLedger){\n const next=clone(runState);\n if(afterLedger?.policyVersion!==4)return next;\n const after=restoreRunShop(afterLedger,{runId:next.id,runCoins:next.runCoins,policyVersion:4});\n const before=restoreRunShop(beforeLedger,{runId:next.id,policyVersion:4});\n if(next.schedule?.scheduleVersion!==4||next.combatClass!==after.combatClass||before.combatClass!==after.combatClass)throw new TypeError('Merchant effects do not match run style');\n for(const [key,receipt] of Object.entries(after.receipts)){\n  if(Object.hasOwn(before.receipts,key)||receipt.type!=='checkout')continue;\n  for(const itemId of receipt.items){\n   const offer=after.offers[receipt.shopId].find(item=>item.id===itemId),effect=offer.effectRef,receiptId=`merchant:${receipt.shopId}:${itemId}`;\n   if(effect.kind==='boon')next.boons=grantBoon(next.boons,effect.id,receiptId).inventory;\n   else if(effect.kind==='stat')next.stats=chooseRunStat(next.stats,receiptId,effect.id);\n   else if(effect.kind==='weapon'){\n    if(effect.styleId!==next.combatClass)throw new TypeError('Merchant weapon style mismatch');\n    next.combatGearTier=Math.max(next.combatGearTier??0,effect.tier);\n   }else throw new TypeError('Unknown issued Merchant effect');\n  }\n }\n return next;\n}\n\n// Caller supplies elapsed seconds; no timer or wall-clock source is created here.\nexport function advanceRunShop(run, elapsedSeconds, { paused: isPaused = false } = {}) {\n  validRun(run);\n  if (!Number.isFinite(elapsedSeconds) || elapsedSeconds < 0 || typeof isPaused !== 'boolean') throw new RangeError('elapsedSeconds must be finite and nonnegative');\n  if (isPaused) return pausedResult(run);\n  const op = advanceTime(run, Math.min(elapsedSeconds, 0.1));\n  return result(op.run, op);\n}\n","modules/roguelite/run-stats.mjs":"export const TUNING={baseHealth:100,healthPerLevel:20,baseDamage:10,damagePerLevel:2};\r\n\r\nconst CANONICAL_STATS=['health','damage','luck'];\r\nconst LEGACY_KEYS=['levels','startingLevels','maxHealth','health','punchDamage','choices','encounter'];\r\nconst V2_KEYS=[...LEGACY_KEYS,'rulesVersion','armorMax','armor','armorBroken'];\r\n\r\nfunction clone(o){return JSON.parse(JSON.stringify(o));}\r\n\r\nfunction validStat(s){return s==='health'||s==='damage'||s==='luck';}\r\nfunction validReceipt(r){return typeof r==='string'&&r.length>0&&r.length<=128;}\r\nfunction validImpactKind(k){return k==='attack'||k==='obstacle';}\r\nfunction isV2(state){return state?.rulesVersion===2;}\r\n\r\nfunction validateRulesVersion(value){\r\n if(!Number.isSafeInteger(value)||(value!==1&&value!==2))throw new RangeError('invalid rules version');\r\n return value;\r\n}\r\n\r\nfunction validateArmorMax(value){\r\n if(!Number.isSafeInteger(value)||value<=0||value>10000)throw new RangeError('invalid armor maximum');\r\n return value;\r\n}\r\n\r\nfunction validLevelsObject(value){\r\n return value&&typeof value==='object'&&!Array.isArray(value)\r\n  &&Object.keys(value).length===CANONICAL_STATS.length\r\n  &&CANONICAL_STATS.every(k=>Object.hasOwn(value,k))\r\n  &&CANONICAL_STATS.every(k=>Number.isSafeInteger(value[k])&&value[k]>=0);\r\n}\r\n\r\nfunction validateChoices(saved){\r\n if(!Array.isArray(saved.choices))throw new TypeError('invalid saved stat choices');\r\n const rooms=new Set();\r\n const counts={health:0,damage:0,luck:0};\r\n const choices=saved.choices.map(choice=>{\r\n  if(!choice||typeof choice!=='object'||Array.isArray(choice)||Object.keys(choice).length!==2\r\n      ||!Object.hasOwn(choice,'room')||!Object.hasOwn(choice,'stat')\r\n      ||!validReceipt(choice.room)||!validStat(choice.stat)||rooms.has(choice.room)){\r\n   throw new RangeError('invalid or duplicate saved stat choice');\r\n  }\r\n  rooms.add(choice.room);\r\n  counts[choice.stat]++;\r\n  if(!Number.isSafeInteger(counts[choice.stat]))throw new RangeError('too many saved stat choices');\r\n  return {room:choice.room,stat:choice.stat};\r\n });\r\n return {choices,counts};\r\n}\r\n\r\nfunction restoreEncounter(saved,levels,v2){\r\n let encounter=null;\r\n if(saved.encounter!==null){\r\n  const e=saved.encounter;\r\n  const maxCharges=v2?0:Math.floor(levels.luck/10);\r\n  if(!e||typeof e!=='object'||Array.isArray(e)||Object.keys(e).length!==3\r\n      ||!Object.hasOwn(e,'id')||!Object.hasOwn(e,'protectionCharges')||!Object.hasOwn(e,'impacts')\r\n      ||!validReceipt(e.id)||!Number.isSafeInteger(e.protectionCharges)||e.protectionCharges<0\r\n      ||e.protectionCharges>maxCharges||!Array.isArray(e.impacts)){\r\n   throw new RangeError('invalid saved encounter');\r\n  }\r\n  const impactIds=new Set();\r\n  const impacts=e.impacts.map(impact=>{\r\n   if(!impact||typeof impact!=='object'||Array.isArray(impact)||Object.keys(impact).length!==3\r\n       ||!Object.hasOwn(impact,'id')||!Object.hasOwn(impact,'kind')||!Object.hasOwn(impact,'blocked')\r\n       ||!validReceipt(impact.id)||impactIds.has(impact.id)||!validImpactKind(impact.kind)\r\n       ||typeof impact.blocked!=='boolean'){\r\n    throw new RangeError('invalid or duplicate saved impact');\r\n   }\r\n   impactIds.add(impact.id);\r\n   return {id:impact.id,kind:impact.kind,blocked:impact.blocked};\r\n  });\r\n  encounter={id:e.id,protectionCharges:e.protectionCharges,impacts};\r\n }\r\n return encounter;\r\n}\r\n\r\n// Fresh runs only: the account progression owner supplies permanent bonus LEVELS.\r\n// A resumed run retains its saved stats and must not reapply this initialization.\r\nexport function createRunStats({startingLevels={health:0,damage:0,luck:0},rulesVersion=1,armorMax=100}={}){\r\n const version=validateRulesVersion(rulesVersion);\r\n if(!startingLevels||typeof startingLevels!=='object'||Array.isArray(startingLevels)\r\n    ||Object.keys(startingLevels).some(key=>!validStat(key)))throw new TypeError('invalid starting stat levels');\r\n const levels={health:0,damage:0,luck:0};\r\n for(const stat of CANONICAL_STATS){\r\n  const value=startingLevels[stat]??0;\r\n  if(!Number.isSafeInteger(value)||value<0)throw new RangeError('starting stat levels must be nonnegative integers');\r\n  levels[stat]=value;\r\n }\r\n if(version===2){\r\n  const maxArmor=validateArmorMax(armorMax);\r\n  const punchDamage=TUNING.baseDamage+levels.damage*TUNING.damagePerLevel;\r\n  if(!Number.isSafeInteger(punchDamage))throw new RangeError('starting stats exceed supported range');\r\n  return {\r\n   levels,\r\n   startingLevels:{...levels},\r\n   maxHealth:1,\r\n   health:1,\r\n   punchDamage,\r\n   choices:[],\r\n   encounter:null,\r\n   rulesVersion:2,\r\n   armorMax:maxArmor,\r\n   armor:maxArmor,\r\n   armorBroken:false\r\n  };\r\n }\r\n const maxHealth=TUNING.baseHealth+levels.health*TUNING.healthPerLevel;\r\n const punchDamage=TUNING.baseDamage+levels.damage*TUNING.damagePerLevel;\r\n if(!Number.isSafeInteger(maxHealth)||!Number.isSafeInteger(punchDamage))throw new RangeError('starting stats exceed supported range');\r\n return {levels,startingLevels:{...levels},maxHealth,health:maxHealth,punchDamage,choices:[],encounter:null};\r\n}\r\n\r\n/** Strictly restore a saved run. This validates and clones; it never fills health or protection charges. */\r\nexport function restoreRunStats(saved){\r\n if(!saved||typeof saved!=='object'||Array.isArray(saved))throw new TypeError('invalid run stats save');\r\n const hasLegacy=Object.keys(saved).length===LEGACY_KEYS.length&&LEGACY_KEYS.every(k=>Object.hasOwn(saved,k));\r\n const hasV2=Object.keys(saved).length===V2_KEYS.length&&V2_KEYS.every(k=>Object.hasOwn(saved,k));\r\n if(!hasLegacy&&!hasV2)throw new TypeError('invalid run stats save');\r\n const v2=hasV2;\r\n if(v2){\r\n  if(saved.rulesVersion!==2)throw new TypeError('invalid run stats save');\r\n  validateArmorMax(saved.armorMax);\r\n }\r\n if(!validLevelsObject(saved.levels)||!validLevelsObject(saved.startingLevels))throw new RangeError('invalid saved stat levels');\r\n const {choices,counts}=validateChoices(saved);\r\n const levels={health:0,damage:0,luck:0};\r\n for(const stat of CANONICAL_STATS){\r\n  levels[stat]=saved.startingLevels[stat]+counts[stat];\r\n  if(!Number.isSafeInteger(levels[stat])||levels[stat]!==saved.levels[stat]){\r\n   throw new RangeError('saved levels do not match starting bonuses and room choices');\r\n  }\r\n }\r\n let maxHealth;\r\n let punchDamage;\r\n if(v2){\r\n  maxHealth=1;\r\n  punchDamage=TUNING.baseDamage+(saved.startingLevels.damage+counts.damage+counts.health)*TUNING.damagePerLevel;\r\n  if(!Number.isSafeInteger(punchDamage)||saved.maxHealth!==1||saved.punchDamage!==punchDamage){\r\n   throw new RangeError('saved derived stats do not match levels');\r\n  }\r\n  if(typeof saved.health!=='number'||!Number.isFinite(saved.health)||(saved.health!==0&&saved.health!==1)){\r\n   throw new RangeError('invalid saved health');\r\n  }\r\n  if(!Number.isFinite(saved.armor)||saved.armor<0||saved.armor>saved.armorMax){\r\n   throw new RangeError('invalid saved armor');\r\n  }\r\n  if(typeof saved.armorBroken!=='boolean'||saved.armorBroken!==(saved.armor===0)){\r\n   throw new RangeError('invalid saved armor state');\r\n  }\r\n }else{\r\n  maxHealth=TUNING.baseHealth+levels.health*TUNING.healthPerLevel;\r\n  punchDamage=TUNING.baseDamage+levels.damage*TUNING.damagePerLevel;\r\n  if(!Number.isSafeInteger(maxHealth)||saved.maxHealth!==maxHealth\r\n      ||!Number.isSafeInteger(punchDamage)||saved.punchDamage!==punchDamage){\r\n   throw new RangeError('saved derived stats do not match levels');\r\n  }\r\n  if(typeof saved.health!=='number'||!Number.isFinite(saved.health)||saved.health<0||saved.health>maxHealth){\r\n   throw new RangeError('invalid saved health');\r\n  }\r\n }\r\n const encounter=restoreEncounter(saved,levels,v2);\r\n const restored={\r\n  levels:{...levels},\r\n  startingLevels:{...saved.startingLevels},\r\n  maxHealth,\r\n  health:saved.health,\r\n  punchDamage,\r\n  choices,\r\n  encounter\r\n };\r\n if(v2){\r\n  restored.rulesVersion=2;\r\n  restored.armorMax=saved.armorMax;\r\n  restored.armor=saved.armor;\r\n  restored.armorBroken=saved.armorBroken;\r\n }\r\n return restored;\r\n}\r\n\r\nexport function chooseRunStat(state,roomReceipt,stat){\r\n if(!validStat(stat)||!validReceipt(roomReceipt))throw new Error('invalid input');\r\n const existing=state.choices.find(c=>c.room===roomReceipt);\r\n if(existing)return clone(state);\r\n const ns=clone(state);\r\n ns.levels[stat]++;\r\n switch(stat){\r\n  case'health':\r\n   if(isV2(ns)){\r\n    ns.punchDamage+=TUNING.damagePerLevel;\r\n   }else{\r\n    ns.maxHealth+=TUNING.healthPerLevel;\r\n    ns.health+=TUNING.healthPerLevel;\r\n   }\r\n   break;\r\n  case'damage':\r\n   ns.punchDamage+=TUNING.damagePerLevel;\r\n   break;\r\n  case'luck':\r\n   break;\r\n }\r\n ns.choices.push({room:roomReceipt,stat});\r\n return ns;\r\n}\r\n\r\nexport function beginStatEncounter(state,encounterId){\r\n if(!validReceipt(encounterId))throw new TypeError('invalid encounter id');\r\n if(!state.encounter||state.encounter.id!==encounterId){\r\n  const charges=isV2(state)?0:Math.floor(state.levels.luck/10);\r\n  return {...clone(state),encounter:{id:encounterId,protectionCharges:charges,impacts:[]}};\r\n }\r\n return clone(state);\r\n}\r\n\r\nexport function applyStatImpact(state,{receiptId,kind,damage=0}){\r\n if(!validImpactKind(kind)||!validReceipt(receiptId))throw new Error('bad id');\r\n if(typeof damage!=='number'||damage<0||!Number.isFinite(damage))throw new Error('bad dmg');\r\n if(!state.encounter)throw new Error('Begin an encounter before resolving impacts');\r\n const imp=state.encounter.impacts.find(i=>i.id===receiptId);\r\n if(imp)return {state:clone(state),blocked:imp.blocked,hpDamage:0,duplicate:true};\r\n const ns=clone(state);\r\n let blocked=false;\r\n let hpDmg=0;\r\n if(isV2(ns)){\r\n  if(damage>0){\r\n   if(ns.armor>0){\r\n    blocked=true;\r\n    const absorbed=Math.min(ns.armor,damage);\r\n    ns.armor-=absorbed;\r\n    if(ns.armor===0)ns.armorBroken=true;\r\n   }else{\r\n    hpDmg=ns.health;\r\n    ns.health=0;\r\n    if(ns.health<0)ns.health=0;\r\n   }\r\n  }\r\n }else if(damage>0&&ns.encounter.protectionCharges>0){\r\n  ns.encounter.protectionCharges--;\r\n  blocked=true;\r\n }else if(damage>0){\r\n  hpDmg=Math.min(ns.health,damage);\r\n  ns.health-=hpDmg;\r\n  if(ns.health<0)ns.health=0;\r\n }\r\n ns.encounter.impacts.push({id:receiptId,kind,blocked});\r\n return {state:ns,blocked,hpDamage:hpDmg,duplicate:false};\r\n}\r\n\r\nexport function armorTiming(stats,heartModifiers=null){\n if(!isV2(stats))return {responseMs:700,recoveryScale:1};\n const base=heartModifiers?.baseArmorMax??stats.armorMax,bonus=heartModifiers?.initialArmorBonus??0;\n if(!Number.isFinite(base)||base<=0||!Number.isFinite(bonus)||bonus<0||base+bonus!==stats.armorMax)throw new TypeError('Invalid heart armor budget');\n // Weighted armor depletes first. The initial Heart reserve never adds burden.\n const burden=Math.max(0,Math.min(base,stats.armor-bonus))/base;\n return {responseMs:800+200*(1-burden),recoveryScale:1+0.25*burden};\n}\n","modules/roguelite/run-step.mjs":"import {qualifyRunFrame,gateBossCue,ACTIVE_RUN_LIMIT_MS} from './run-frame-quality.mjs';\r\nimport {currentRunRoom,observeRunRoom,setRunPaused,settleRunHealth,restoreRunSession,enterScheduledShop,interactRunShop,resolveRunGame,resolveRunPond,resolveRunArcade,resolveRunTreasure,exitCaughtRunTreasure,escapeRunBoss,resumeRunBoss} from './run-controller.mjs';\r\nimport {createBossEncounter,advanceBoss,restoreBossState,pauseReasons} from './boss-state.mjs';\r\nimport {createPondRoom,observePondRoom,restorePondRoom} from './pond-room.mjs';\r\nimport {createGrassGame} from './grass-game.mjs';\r\nimport {bossCombatOptions,initializeBossProtection,resolveBossProtection} from './boss-run-benefits.mjs';\r\nimport {beginStatEncounter,applyStatImpact,armorTiming} from './run-stats.mjs';\r\nimport {boonEffects} from './boons.mjs';\r\nimport {ROOM_BY_ID} from './room-contracts.mjs';\r\nimport {createGameRoom,stepGameRoom,checkpointGameRoom} from './game-room-adapters.mjs';\r\nimport {createDanceRoom} from './dance-room.mjs';\r\nimport {SIM as COVER_SIM} from '../../arcade/cover/cover-sim.mjs';\r\nimport {createTreasureChase,restoreTreasureChase,stepTreasureChase} from './treasure-room.mjs';\r\nimport {normalizeClassInput,classGear} from './class-combat.mjs';\r\nimport {xpLevel} from './run-xp.mjs';\nimport {authoredRoomKind,createAuthoredRoom,restoreAuthoredRoom,stepAuthoredRoom} from './run-authored-rooms.mjs';\nimport {stepRescue,restoreRescue} from './rescue-state.mjs';\nimport {expireRunWearables} from './coach-hearts.mjs';\n\r\nconst clone=value=>structuredClone(value);\r\nconst transientPauses=['tracking','background','camera-handoff','loading'];\r\nconst validWeaponIds=new Set(['dagger','wand','hammer']);\r\n\r\n// Transition records are the minimal reducer-change DTO for the future host:\r\n// phase and pause edges, pond reducer events, boss status edges, and room completion.\r\n// They carry no server time, authorization, or reward receipt of their own.\r\n\r\nexport function selectedRunWeaponId(ledger){\r\n return ledger?.inventory?.filter(item=>validWeaponIds.has(item.id)).at(-1)?.id??'dagger';\r\n}\r\n\r\nexport function grassSeedForNode(nodeId){\r\n return String(nodeId).split('').reduce((seed,char)=>(seed*31+char.charCodeAt(0))>>>0,1);\r\n}\r\n\r\nconst baseline=(runState,nodeId,obsHighWater=0)=>({v:1,nodeId,obsHighWater,shopLastNow:null,grassLastNow:null,treasureLastNow:null,boss:null,grass:null,pond:null,treasure:null});\r\nconst seedForNode=nodeId=>String(nodeId).split('').reduce((seed,char)=>(seed*31+char.charCodeAt(0))>>>0,1);\r\nconst roomNode=runState=>currentRunRoom(runState);\r\nconst nextObservation=(runState,runtime)=>Math.max(runtime?.obsHighWater??0,(runState?.lastObservation??-1)+1,(runtime?.boss?.lastObsId??-1)+1,(runtime?.pond?.lastObservationId??-1)+1,0);\r\nfunction phaseTransitions(before,after){\r\n const out=[];\r\n if(before.phase!==after.phase)out.push({type:'phase',from:before.phase,to:after.phase});\r\n const reasons=new Set([...before.pauseReasons,...after.pauseReasons]);\r\n for(const reason of reasons){const was=before.pauseReasons.includes(reason),is=after.pauseReasons.includes(reason);if(was!==is)out.push({type:'pause',reason,paused:is});}\r\n return out;\r\n}\r\nfunction validNow(now){if(!Number.isFinite(now))throw new TypeError('now must be finite');}\r\n\r\nfunction assertTreasureManifest(node,manifest){\r\n if(node?.type!=='bonus'||node.doorId!=='treasure'||node.roomType!=='treasure_side_room'||!Array.isArray(manifest)||manifest.length!==1\r\n    ||manifest[0]?.id!=='chest-1'||!Array.isArray(manifest[0].rewards)||manifest[0].rewards.length!==1\r\n    ||manifest[0].rewards[0]?.type!=='blueprint'||manifest[0].rewards[0]?.tier!==1||typeof manifest[0].rewards[0]?.buildingId!=='string')\r\n  throw new TypeError('Treasure room requires its one server-issued tier-one blueprint chest');\r\n}\r\n\r\nexport function enterRoom({runState,now,bossVisualId,treasureManifest}={}){\r\n runState=clone(runState);validNow(now);\r\n const node=roomNode(runState);if(!node)throw new TypeError('Current run room is missing');\r\n const before=clone(runState),runtime=baseline(runState,node.id,nextObservation(runState));\r\n if(node.type==='recovery'){runState.phase='walk';runState.completion={roomId:node.id,xp:0,packs:[],recovery:true};return {runState,runtime,transitions:phaseTransitions(before,runState)};}\r\n if(authoredRoomKind(node)){\n  runtime.authored=createAuthoredRoom(node);runState=setRunPaused(runState,'loading',true);\n }else if(node.roomType==='treasure_side_room'){\n  if(runState.schedule.scheduleVersion<2)throw new TypeError('Treasure rooms require V2 schedules');\r\n  assertTreasureManifest(node,treasureManifest);\r\n  runtime.treasure=createTreasureChase({encounterId:node.id,chestManifest:treasureManifest});runtime.treasureLastNow=null;\r\n }\r\n else if(node.roomType==='corgi_shop')runState=enterScheduledShop(runState);\r\n else if(node.roomType==='healing_waters'){\r\n  runtime.pond=createPondRoom({encounterId:node.id});runState=setRunPaused(runState,'loading',true);\r\n }else if(node.roomType==='coin_chase'){\r\n  runtime.grass=createGrassGame({encounterId:node.id,seed:grassSeedForNode(node.id),catchWindows:boonEffects(runState.boons).chaseCatchWindows}).snapshot();\r\n }else if(runState.schedule.scheduleVersion>=2&&['clay','jelly','dance'].includes(node.doorId)){\r\n  const seed=seedForNode(node.id);\r\n  runState.stats=beginStatEncounter(runState.stats,node.id);\r\n  if(node.doorId==='dance'){\r\n   const dance=createDanceRoom({runId:runState.id,seed});dance.advance({now,tracking:false,paused:false,energy:0,coverage:0});runtime.arcade=dance.snapshot();\r\n  }else{\r\n   runtime.arcade=checkpointGameRoom(createGameRoom({roomId:node.doorId,encounterId:node.id,seed,...(node.doorId==='jelly'?{volume:node.volume??ROOM_BY_ID.jelly.variants[0]}:{})}));\r\n   if(node.doorId==='jelly')runtime.arcade.sim.s.hp=Math.min(COVER_SIM.hp,(runState.rulesVersion===2?Math.ceil(runState.stats.armor/10)+runState.stats.health:Math.ceil(runState.stats.health/10)));\r\n  }\r\n }else if(node.type!=='warmup'&&node.type!=='full-game'){\r\n  if(typeof bossVisualId!=='string'||!bossVisualId)throw new TypeError('bossVisualId is required for a scheduled boss');\r\n  const options=bossCombatOptions(runState.stats,runState.boons,runState.heartModifiers,runState.secretEffects);\n  const equipment=runState.combatClass?classGear(runState.combatClass,runState.combatGearTier??0):null;\r\n  if(equipment)options.punchDamage*=equipment.damageScale;\r\n  const attackLevel=runState.combatClass?Math.max(xpLevel(runState.xp)-1,Math.floor(runState.cursor/6)):node.level;\r\n  let boss=node.returning?resumeRunBoss(runState,{now}):createBossEncounter({kind:node.type,bossId:node.id,coachId:bossVisualId,weaponId:equipment?.id??selectedRunWeaponId(runState.shopLedger),seed:node.patternSeed??attackLevel,difficultyLevel:attackLevel,patternSeed:node.patternSeed??seedForNode(`${runState.seed}:${node.id}`),classId:runState.combatClass??null,now,...options});\n  boss.punchDamage=options.punchDamage;boss.timings=options.timings;\r\n  runState.stats=beginStatEncounter(runState.stats,node.id);runtime.boss=initializeBossProtection(boss,runState.boons);if(runState.rulesVersion===2){runtime.boss.protection.charges=0;runtime.boss.actionRecoveryScale=options.actionRecoveryScale??armorTiming(runState.stats).recoveryScale;}\n }\r\n runtime.obsHighWater=Math.max(runtime.obsHighWater,nextObservation(runState,runtime));\r\n return {runState,runtime,transitions:phaseTransitions(before,runState)};\r\n}\r\n\r\nexport function resumePage({runState,runtime,now,accountId,dataEpoch}={}){\r\n const before=clone(runState);runState=restoreRunSession(clone(runState),{accountId,dataEpoch});runtime=clone(runtime);validNow(now);\r\n const node=roomNode(runState);\r\n if(!runtime||runtime.v!==1||runtime.nodeId!==node?.id)throw new TypeError('Saved gameplay runtime does not match the current room');\r\n runtime.obsHighWater=nextObservation(runState,runtime);\r\n runtime.boss=runtime.boss?restoreBossState(runtime.boss,{now}):null;\n if(runtime.rescue)runtime.rescue=restoreRescue(runtime.rescue,{generation:runtime.rescue.generation+1});\n if(runtime.authored){runtime.authored=restoreAuthoredRoom(runtime.authored,node);runState=setRunPaused(runState,'loading',true);}\n for(const reason of transientPauses)runState=setRunPaused(runState,reason,false);\n if(runtime.authored)runState=setRunPaused(runState,'loading',true);\n if(node?.roomType==='healing_waters'){\r\n  runtime.pond=restorePondRoom(runtime.pond);\r\n  if(runtime.pond.encounterId!==node.id)throw new TypeError('The saved pond belongs to another room.');\r\n  runState=setRunPaused(runState,'loading',true);\r\n }\r\n if(node?.roomType==='coin_chase')runtime.grass=createGrassGame({encounterId:node.id,seed:grassSeedForNode(node.id),catchWindows:boonEffects(runState.boons).chaseCatchWindows,saved:runtime.grass}).snapshot();\r\n if(node?.roomType==='treasure_side_room'){\r\n  assertTreasureManifest(node,runtime.treasure?.chestManifest);\r\n  runtime.treasure=restoreTreasureChase(runtime.treasure);\r\n  if(runtime.treasure.encounterId!==node.id)throw new TypeError('Saved treasure belongs to another room');\r\n }\r\n if(runState.schedule.scheduleVersion>=2&&['clay','jelly','dance'].includes(node?.doorId)){\r\n  if(!runtime.arcade)throw new TypeError('Saved arcade room checkpoint is missing');\r\n  if(node.doorId==='dance')runtime.arcade=createDanceRoom({runId:runState.id,seed:seedForNode(node.id),saved:runtime.arcade}).snapshot();\r\n  else runtime.arcade=checkpointGameRoom(runtime.arcade);\r\n }\r\n if(node?.roomType==='corgi_shop')runState=enterScheduledShop(runState);\r\n if(runtime.boss&&runtime.boss.bossId===node?.id)runtime.boss=initializeBossProtection(runtime.boss,runState.boons);\r\n runtime.shopLastNow=null;runtime.grassLastNow=null;runtime.treasureLastNow=null;\r\n if(runState.rulesVersion===2){runtime.goodSamples=0;if(runState.schedule.scheduleVersion!==4)runtime.sourceTs=null;runtime.presentationInvalid=false;gateBossCue(runtime,{},false);if(runtime.boss)runtime.boss.protection.charges=0;}\n return {runState,runtime,transitions:phaseTransitions(before,runState)};\r\n}\r\n\r\nexport function resumePending({runState,accountId,dataEpoch}={}){\r\n const before=clone(runState);runState=restoreRunSession(clone(runState),{accountId,dataEpoch});\r\n for(const reason of transientPauses)runState=setRunPaused(runState,reason,false);\r\n return {runState,runtime:null,transitions:phaseTransitions(before,runState)};\r\n}\r\n\r\nfunction setTracking(runState,tracked){\r\n const paused=runState.pauseReasons.includes('tracking');\r\n return paused===!tracked?runState:setRunPaused(runState,'tracking',!tracked);\r\n}\r\n\r\nexport function stepFrame({runState,runtime,frame,env={}}={}){\r\n runState=clone(runState);runtime=clone(runtime);frame=clone(frame);env=clone(env);\r\n if(!runtime||runtime.v!==1||runtime.nodeId!==roomNode(runState)?.id)throw new TypeError('Gameplay runtime does not match the current room');\r\n if(!frame||!['track','sample'].includes(frame.op)||!Number.isSafeInteger(frame.frameSeq)||!Number.isFinite(frame.now)||typeof frame.tracked!=='boolean')throw new TypeError('Invalid frame');\r\n const beforeState=clone(runState);\r\n if(['complete','defeated','captured','rest-reaper'].includes(runState.phase))return {runState,runtime,transitions:[],creditMs:0};\n // A completed room can still have observations in the acknowledged batch tail.\n // They cannot reopen its tracking gate or earn credit after the page stops sampling it.\n if(runState.rulesVersion===2&&runState.phase==='walk')return {runState,runtime,transitions:[],creditMs:0};\n let qualifiedDelta=0;\r\n if(runState.rulesVersion===2){\r\n  const hands=runtime.boss?.status==='fighting'&&runtime.boss?.cue?.kind==='yellow_blockable'&&runState.combatClass!=='monk';\r\n  const authored=runtime.authored?.kind,handsOnly=!!runtime.rescue||authored==='altar'||roomNode(runState)?.roomType==='corgi_shop';\n  const openingHands=authored==='secret'&&runtime.authored.state.stage==='opening'||authored==='yoga'&&runtime.authored.state.status==='opening';\n  const quality=qualifyRunFrame(runtime,frame,{hands:hands||handsOnly||openingHands,body:!handsOnly});qualifiedDelta=quality.deltaMs;\n  const allowed=runtime.rescue?quality.valid:gateBossCue(runtime,frame,quality.valid);\n  frame.tracked=frame.tracked&&allowed;if(!allowed)qualifiedDelta=0;\n  if(runtime.rescue?.status==='pending'&&!frame.tracked&&!runState.pauseReasons.includes('tracking'))runtime.rescue=restoreRescue(runtime.rescue,{generation:runtime.rescue.generation+1});\n }\r\n const node=roomNode(runState);let H=runtime.obsHighWater,creditMs=0;const runtimeTransitions=[];let bossTransition=null;\r\n if(frame.op==='sample'&&node?.roomType==='treasure_side_room'){\r\n  const input=frame.treasure;\r\n  if(!input||typeof input!=='object'||Array.isArray(input)||Object.keys(input).length!==1||!Object.hasOwn(input,'jogging')||typeof input.jogging!=='boolean')throw new TypeError('Treasure samples require only a boolean jogging field');\r\n }\r\n runState=setTracking(runState,frame.tracked);\r\n if(frame.op==='track'){\r\n  runtime.obsHighWater=H;\r\n  return {runState,runtime,transitions:phaseTransitions(beforeState,runState),creditMs:0};\r\n }\r\n const {now}=frame;validNow(now);\r\n const roomElapsed=runState.elapsedMs;\r\n runState=observeRunRoom(runState,{observationId:H++,now,tracked:frame.tracked,value:frame.value??0,choice:frame.choice??null});\r\n creditMs=Math.max(creditMs,Math.max(0,runState.elapsedMs-roomElapsed));\n if(runtime.authored){\n  const kind=runtime.authored.kind,sample=frame[kind]??(kind==='yoga'?{push:false,pose:null,eligible:false}:kind==='secret'?{handsLow:false,pinching:false,pullUp:false,jogging:false,action:null,choiceId:null}:{target:null,gesture:'none',y:null,handId:null});\n  const result=stepAuthoredRoom({runState,room:runtime.authored,observationId:H++,now,tracked:frame.tracked,paused:runState.pauseReasons.length>0,sample});\n  runState=result.runState;runtime.authored=result.room;creditMs=result.creditMs;\n  runtimeTransitions.push(...result.events.map(event=>({type:'authored',event})));\n }\n if(runtime.rescue&&runtime.rescue.status!=='accepted'){\n  const sample=frame.rescue??{challengeId:runtime.rescue.challengeId,generation:runtime.rescue.generation,stageAck:runtime.rescue.stage,handId:null,x:null,y:null,shape:'none',approachHeld:false};\n  const result=stepRescue(runtime.rescue,{observationId:H++,now,tracked:frame.tracked&&!!frame.rescue,paused:runState.pauseReasons.length>0,rescue:sample});\n  runtime.rescue=result.state;creditMs=result.creditMs;\n  if(result.accepted)runtimeTransitions.push({type:'rescue-accepted'});\n }\n if(node.roomType==='corgi_shop'){\r\n  const dt=runtime.shopLastNow===null?0:Math.min(.1,Math.max(0,(now-runtime.shopLastNow)/1000));runtime.shopLastNow=now;\r\n  const tickRan=runState.phase==='active'&&!runState.pauseReasons.length;\r\n  if(tickRan){runState=interactRunShop(runState,{type:'tick',seconds:dt});creditMs=Math.max(creditMs,dt*1000);}\r\n }\r\n if(runtime.pond){\r\n  const old=runtime.pond,lastNow=old.lastNow,status=old.status,paused=runState.phase!=='active'||runState.pauseReasons.length>0;\r\n  const pondResult=observePondRoom(old,{observationId:H++,now,tracked:frame.tracked,balanced:frame.pond?.balanced===true,handOut:frame.pond?.handOut===true,paused});\r\n  runtime.pond=pondResult.state;\r\n  if(lastNow!==null&&frame.tracked&&!paused&&(status==='healing'||status==='opening'&&frame.pond?.handOut===true)&&now>=lastNow)creditMs=Math.max(creditMs,Math.min(100,now-lastNow));\r\n  for(const event of pondResult.events)runtimeTransitions.push({type:'pond',event:clone(event)});\r\n  if(runState.phase==='active'&&!runState.pauseReasons.length&&['complete','awoken'].includes(runtime.pond.status))runState=resolveRunPond(runState,runtime.pond);\r\n }\r\n if(runtime.grass){\r\n  const dt=runtime.grassLastNow===null?0:Math.max(0,(now-runtime.grassLastNow)/1000);runtime.grassLastNow=now;\r\n  const grass=createGrassGame({encounterId:node.id,seed:grassSeedForNode(node.id),catchWindows:boonEffects(runState.boons).chaseCatchWindows,saved:runtime.grass});\r\n  const chaseBefore=grass.chase;\r\n  grass.advance(dt,{valid:frame.tracked&&frame.grass?.controlValid===true,height01:frame.grass?.height01??.5,steer:Math.max(-1,Math.min(1,frame.grass?.steer??0)),paused:runState.phase!=='active'||runState.pauseReasons.length>0});\r\n  runtime.grass=grass.snapshot();if(frame.grass?.controlValid!==true||frame.grass?.exerciseEligible!==true)creditMs=0;\r\n  if(frame.tracked&&frame.grass?.controlValid===true&&frame.grass?.exerciseEligible===true&&runState.phase==='active'&&!runState.pauseReasons.length&&dt>0&&chaseBefore.status==='running')creditMs=Math.max(creditMs,Math.min(.1,dt)*1000);\r\n  if(runState.phase==='active'&&!runState.pauseReasons.length&&grass.chase.status!=='running')runState=resolveRunGame(runState,grass.chase);\r\n }\r\n if(runtime.treasure){\r\n  const dt=runtime.treasureLastNow===null?0:Math.min(.1,Math.max(0,(now-runtime.treasureLastNow)/1000));\r\n  runtime.treasureLastNow=now;\r\n  const was=runtime.treasure,paused=runState.phase!=='active'||runState.pauseReasons.length>0||!frame.tracked;\r\n  const chase=stepTreasureChase(was,{deltaSeconds:dt,jogging:!paused&&frame.treasure?.jogging===true,pauseReasons:paused?[...runState.pauseReasons,...(!frame.tracked?['tracking']:[])]:[]});\r\n  runtime.treasure=chase.run;\r\n  for(const event of chase.events)runtimeTransitions.push({type:'treasure',event:clone(event)});\r\n  if(!paused&&was.status==='chasing'&&chase.run.status==='chasing'&&dt>0)creditMs=Math.max(creditMs,dt*1000);\r\n  if(chase.run.status==='caught'&&runState.phase==='active'&&!runState.pauseReasons.length&&frame.tracked)runState=exitCaughtRunTreasure(runState,chase.run);\r\n }\r\n if(runtime.arcade){\r\n  const arcadeInput=frame.arcade&&typeof frame.arcade==='object'?frame.arcade:{};\r\n  const was=runtime.arcade,paused=runState.phase!=='active'||runState.pauseReasons.length>0||!frame.tracked;\r\n  let nextArcade;\r\n  if(node.doorId==='dance'){\r\n   const game=createDanceRoom({runId:runState.id,seed:seedForNode(node.id),saved:was});\r\n   game.advance({now,tracking:frame.tracked,paused,energy:arcadeInput.energy??0,coverage:arcadeInput.coverage??0});\r\n   nextArcade=game.snapshot();\r\n   if(!paused&&nextArcade.musicPosition>was.musicPosition)creditMs=Math.max(creditMs,Math.min(100,Math.max(0,now-was.serverNow)));\r\n  }else{\r\n   const adapterFrame={now,tracked:frame.tracked,paused};\r\n   if(node.doorId==='clay'){adapterFrame.actions=arcadeInput.actions??[];if(arcadeInput.action!=null)adapterFrame.action=arcadeInput.action;}\r\n   else for(const key of ['hidden','side','beam','zones','fire','fires'])if(Object.hasOwn(arcadeInput,key))adapterFrame[key]=arcadeInput[key];\r\n   nextArcade=checkpointGameRoom(stepGameRoom(was,adapterFrame));\r\n   if(node.doorId==='jelly'){\r\n    const delta=was.sim.s.hp-nextArcade.sim.s.hp;\r\n    for(let i=0;i<delta;i++){\r\n     const receiptId=`arcade-hit:${node.id}:${runState.stats.encounter.impacts.length+1}`;\r\n     runState.stats=applyStatImpact(runState.stats,{receiptId,kind:'attack',damage:10}).state;\r\n    }\r\n    if(runState.stats.health>0){\r\n     const simState=nextArcade.sim.s;\r\n     simState.hp=Math.min(COVER_SIM.hp,(runState.rulesVersion===2?Math.ceil(runState.stats.armor/10)+runState.stats.health:Math.ceil(runState.stats.health/10)));\r\n     if(simState.phase==='dead'){\r\n      simState.phase=was.sim.s.phase;simState.deadAt=was.sim.s.deadAt;\r\n      nextArcade.status='running';nextArcade.end=null;\r\n     }\r\n    }\r\n   }\r\n   if(!paused&&nextArcade.status==='running')creditMs=Math.max(creditMs,Math.min(50,Math.max(0,now-(was.lastNow??now))));\r\n  }\r\n  runtime.arcade=nextArcade;\r\n  const priorPhase=runState.phase;\r\n  if(node.doorId==='dance'){\r\n   const result=createDanceRoom({runId:runState.id,seed:seedForNode(node.id),saved:runtime.arcade}).state();\r\n   if(result.roundComplete&&runState.phase==='active'&&!runState.pauseReasons.length&&runState.stats.health>0)runState=resolveRunArcade(runState,runtime.arcade);\r\n  }else if(runtime.arcade.status==='cleared'&&runState.phase==='active'&&!runState.pauseReasons.length&&runState.stats.health>0)runState=resolveRunArcade(runState,runtime.arcade);\r\n  if(priorPhase!==runState.phase)runtimeTransitions.push({type:'arcade-completed',roomId:node.id});\r\n }\r\n if(runtime.treasure&&runtime.treasure.status==='arrived'){\r\n  // Arrival presents the server-issued chest for an explicit treasure_open event.\r\n }\r\n if(runtime.boss&&!runtime.rescue&&runState.phase==='active'){\n  const hits=runtime.boss.playerHits,bossBefore={status:runtime.boss.status,phase:runtime.boss.phase,phaseHits:runtime.boss.phaseHits,now:runtime.boss.now};\r\n  const bossInput=frame.boss&&typeof frame.boss==='object'?frame.boss:{};\r\n  const gestures={left:bossInput.left===true,punchLeft:bossInput.punchLeft===true,punchRight:bossInput.punchRight===true,crouch:bossInput.crouch===true,jump:bossInput.jump===true,jog:bossInput.jog===true};\r\n  const obs={...gestures,...normalizeClassInput(bossInput),id:H++,now,ts:runState.rulesVersion===2?Math.min(now,frame.sourceTs??now):now,tracked:frame.tracked};\r\n  const bossEnv={tutorial:runState.phase==='tutorial'||runState.pauseReasons.some(reason=>reason!=='tracking'),background:runState.pauseReasons.includes('background')};\r\n  if(runState.rulesVersion===2){const options=bossCombatOptions(runState.stats,runState.boons,runState.heartModifiers,runState.secretEffects);runtime.boss.timings=options.timings;runtime.boss.actionRecoveryScale=options.actionRecoveryScale??armorTiming(runState.stats).recoveryScale;runtime.boss.protection.charges=0;}\n  runtime.boss=advanceBoss(runtime.boss,obs,bossEnv);\r\n  if(!pauseReasons(obs,bossEnv,runtime.boss.timings.staleMs).length)creditMs=Math.max(creditMs,Math.min(Math.max(0,now-bossBefore.now),runtime.boss.timings.maxStepMs));\r\n  if(runtime.boss.playerHits>hits){\r\n   const receiptId=`attack:${runtime.boss.bossId}:${runtime.boss.playerHits}`,protection=resolveBossProtection(runtime.boss,receiptId);runtime.boss=protection.boss;\r\n   if(!protection.blocked&&!protection.duplicate)runState.stats=applyStatImpact(runState.stats,{receiptId,kind:'attack',damage:10}).state;\r\n  }\r\n  if(bossBefore.status!==runtime.boss.status)bossTransition={from:bossBefore.status,to:runtime.boss.status};\r\n  if(runtime.boss.status==='escaped')runState=escapeRunBoss(runState,runtime.boss);\r\n }\r\n runState=settleRunHealth(runState);\r\n if(runState.rulesVersion===2){\r\n  const active=beforeState.phase==='active'&&!beforeState.pauseReasons.some(r=>r!=='tracking')&&frame.tracked&&!runState.pauseReasons.length&&!['healing_waters','corgi_shop','level_recovery'].includes(node.roomType);\r\n  creditMs=active?Math.min(creditMs,qualifiedDelta):0;\r\n  runState.activeRunMs=Math.min(ACTIVE_RUN_LIMIT_MS,runState.activeRunMs+creditMs);\r\n  if(runState.activeRunMs===ACTIVE_RUN_LIMIT_MS){runState.reaperStarted=true;runState.phase='rest-reaper';runState.choice=null;runState.choiceSince=null;if(runState.heartModifiers){runState.heartModifiers=expireRunWearables(runState.heartModifiers);runState.runCoins=0;if(runState.shopLedger)runState.shopLedger.coins=0;}}\n }\r\n runtime.obsHighWater=H;\r\n const transitions=phaseTransitions(beforeState,runState);\r\n transitions.push(...runtimeTransitions);\r\n if(bossTransition)transitions.push({type:'boss-status',...bossTransition});\r\n if(runState.completion?.roomId&&beforeState.completion?.roomId!==runState.completion.roomId)transitions.push({type:'room-completed',roomId:runState.completion.roomId});\r\n return {runState,runtime,transitions,creditMs};\r\n}\r\n","modules/roguelite/run-view.css":"@font-face{font-family:Pixel;src:url('/modules/coach-world-rts/ui/pixelify-sans.woff2')}*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#101b20;color:#fff8e6;font-family:system-ui,sans-serif}button{font:800 17px system-ui;border:1px solid #f4f0cb66;background:#172b30;color:#fff8e6;border-radius:6px;padding:12px 18px;min-height:48px;cursor:pointer}button:hover{background:#34574b}button:disabled{opacity:.5;cursor:wait}[hidden]{display:none!important}#run-stage{position:relative;width:100%;height:100dvh;isolation:isolate;overflow:hidden}#world{display:block;width:100%;height:100%;image-rendering:pixelated}.vignette{position:absolute;inset:0;pointer-events:none;background:linear-gradient(#071016ab,transparent 25%,transparent 60%,#071016ed),radial-gradient(ellipse at center,transparent 30%,#07101655);z-index:1}header{position:absolute;z-index:40;top:max(16px,env(safe-area-inset-top));left:max(20px,env(safe-area-inset-left));right:max(20px,env(safe-area-inset-right));display:flex;align-items:center;gap:10px;flex-wrap:wrap}.brand{font-size:12px;font-weight:900;letter-spacing:.16em;display:flex;flex-direction:column;line-height:1.5;margin-right:auto}.brand b{font-family:Pixel,monospace;font-size:16px;letter-spacing:.05em;color:#c8ff95}#room-label{font:700 13px Pixel,monospace;letter-spacing:.1em;color:#c8ff95}#tutorial-toggle{font-size:12px;min-height:38px;padding:8px}#pause{font-size:21px;min-height:38px;padding:6px 13px}#camera-preview{position:absolute;right:20px;top:82px;width:120px;aspect-ratio:4/3;object-fit:contain;border:2px solid #b6d6b477;background:#142126;z-index:41;border-radius:6px}#opening,#summary{position:absolute;inset:0;z-index:10;padding:clamp(26px,6vw,76px);display:flex;flex-direction:column;justify-content:center;align-items:flex-start;background:linear-gradient(90deg,#0c171fe6,transparent)}.eyebrow{color:#c8ff95;font:700 clamp(12px,1.5vw,17px) Pixel,monospace;letter-spacing:.15em}h1{font-size:clamp(62px,12vw,150px);line-height:.86;letter-spacing:-.07em;margin:20px 0 24px;font-weight:950}h1 em{font-style:normal;color:#c8ff95}#opening p,#summary p{font-size:clamp(17px,2.2vw,26px);line-height:1.5;color:#cfddd4;margin:0 0 24px}.start-actions{display:flex;gap:10px;flex-wrap:wrap}#begin,#another{background:#c8ff95;color:#172b25;border-color:transparent}#opening small{margin-top:16px;font-size:12px;opacity:.6}#hud{position:absolute;inset:0;z-index:4;pointer-events:none}#hud button{pointer-events:auto}.run-progress{position:absolute;left:24px;right:170px;top:90px;display:flex;gap:10px;align-items:center;font:700 15px Pixel,monospace}.run-progress progress{height:9px;flex:1;min-width:40px;accent-color:#c8ff95}#stats{position:absolute;left:24px;top:120px;font:800 clamp(17px,2vw,24px) system-ui;color:#e5edd5}.coach-tag{position:absolute;left:24px;top:185px;display:flex;flex-direction:column;gap:5px;text-shadow:0 2px 5px #000}.coach-tag span{font:700 12px Pixel,monospace;letter-spacing:.15em;color:#c8ff95}.coach-tag strong{font-size:clamp(18px,2.5vw,32px);max-width:60vw}#objective{position:absolute;bottom:15%;left:4%;right:4%;display:flex;flex-direction:column;align-items:center;text-align:center;text-shadow:0 3px 0 #0c1820,0 0 22px #0c1820}#verb{font:900 clamp(22px,3.7vw,44px)/1.15 system-ui;text-transform:uppercase;letter-spacing:.03em}#goal{font:900 clamp(60px,11vw,118px)/1.1 system-ui;letter-spacing:-.055em}#cue{font-size:clamp(18px,2.6vw,28px);font-weight:800;color:#c8ff95}#run-message{position:absolute;bottom:max(20px,env(safe-area-inset-bottom));left:18px;right:18px;text-align:center;font:750 clamp(17px,2.3vw,27px)/1.25 system-ui;text-shadow:0 2px 6px #000}#motion-arrow{position:absolute;right:9%;top:44%;font:900 clamp(65px,11vw,125px) system-ui;color:#c8ff95;text-shadow:0 4px 0 #13362b;animation:swoop 1.6s ease-in-out infinite}@keyframes swoop{0%,100%{transform:translateY(-12px);opacity:.65}50%{transform:translateY(15px);opacity:1}}#upgrade{position:absolute;inset:27% 3% 10%;padding:15px 0;text-align:center;background:#101b20e8;border-top:1px solid #c8ff9577;border-bottom:1px solid #c8ff9577}#upgrade h2{font-size:clamp(20px,3vw,34px);margin:0 0 15px}.upgrade-options{height:75%;display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.upgrade-options>div{display:flex;flex-direction:column;justify-content:center;align-items:center;border-radius:10px;outline:2px solid transparent}.upgrade-options>div[data-active=true]{background:#c8ff9522;outline-color:#c8ff95}.upgrade-options b{font-size:clamp(55px,10vw,110px);line-height:1.1}.upgrade-options span{font-size:36px}.upgrade-options strong{font-size:clamp(17px,2.5vw,28px)}.upgrade-options small{font-size:clamp(15px,2vw,23px);margin-top:10px;color:#c8ff95}#held-weapon,#held-shield{position:absolute;bottom:-4%;height:38%;width:30%;object-fit:contain;image-rendering:pixelated}#held-weapon{right:3%;transform:rotate(-18deg);transform-origin:50% 95%}#held-shield{left:1%;transform:rotate(12deg)}#error{position:absolute;z-index:70;left:8%;right:8%;bottom:10%;padding:20px;background:#6b2424;border:1px solid #ffafa0;font:700 18px/1.4 system-ui}body[data-boss-color=red] #cue{color:#ff6559}body[data-boss-color=yellow] #cue{color:#ffe46d}@media(orientation:landscape) and (max-height:550px){header{top:10px}.brand{display:none}#room-label{margin-right:auto}#camera-preview{top:62px;width:95px}.run-progress{top:62px;right:140px;font-size:13px}#stats{top:84px;font-size:17px}.coach-tag{top:115px}.coach-tag strong{font-size:20px}#objective{bottom:13%}#goal{font-size:66px}#verb{font-size:27px}#cue{font-size:20px}#motion-arrow{right:15%;top:35%;font-size:76px}#run-message{font-size:18px;bottom:12px}#upgrade{inset:30% 5% 11%}#upgrade h2{font-size:20px;margin:0}.upgrade-options b{font-size:46px}.upgrade-options span{font-size:25px}.upgrade-options strong{font-size:18px}.upgrade-options small{font-size:15px;margin-top:2px}#opening{padding:48px}#opening h1{font-size:76px;margin:12px 0}#opening p{font-size:17px;margin-bottom:15px}#opening small{margin-top:8px}.run-tutorial h2{font-size:30px!important}.run-tutorial p{font-size:20px!important}}@media(prefers-reduced-motion:reduce){#motion-arrow{animation:none}}\r\n/* Run information stays readable from the workout position, including phone landscape. */\r\n#room-label{font:800 18px system-ui,sans-serif;letter-spacing:.02em}#tutorial-toggle{font-size:17px;min-height:48px}.run-progress{font:800 20px system-ui,sans-serif}#stats{font-size:24px}.coach-tag span{font-size:17px}\r\n@media(orientation:landscape) and (max-height:550px){#room-label{font-size:18px}#tutorial-toggle{font-size:17px;min-height:44px}.run-progress{font-size:18px;top:67px}#stats{font-size:22px;top:91px}.coach-tag{top:125px}.coach-tag span{font-size:16px}#run-message{font-size:21px}}\r\n@media(orientation:portrait){header{display:grid;grid-template-columns:1fr auto;gap:6px;align-items:center}.brand{display:none}#room-label{font-size:16px;grid-column:1;grid-row:1}#pause{grid-column:2;grid-row:1;justify-self:end}#tutorial-toggle{grid-column:2;grid-row:2;font-size:16px}.run-progress{top:126px;left:20px;right:20px;font-size:18px}#stats{top:157px;left:20px;font-size:22px}.coach-tag{top:211px;left:20px}.coach-tag strong{font-size:22px;max-width:55vw}.coach-tag span{font-size:16px}#camera-preview{top:210px;right:20px;width:96px}}\r\n.run-boss-door{position:absolute;inset:0;z-index:25;background:#091521eb;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:70px 20px 25px;gap:10px;text-align:center}.run-boss-door h2{font:900 clamp(26px,5vw,58px)/1.1 system-ui;margin:0;color:#d9fbff}.run-boss-door p{font:800 clamp(20px,3vw,30px)/1.2 system-ui;margin:0}.run-crystal-frame{position:relative;height:min(55vh,500px);width:min(34vh,70vw);border:2px solid #b9faff88;box-shadow:0 0 45px #7ac9ed66;background:#153552}.run-boss-preview{position:absolute;inset:10%;width:80%;height:80%;object-fit:contain;image-rendering:pixelated;filter:drop-shadow(0 0 12px #b5fbff)}.run-crystal-poster{position:absolute;inset:0;opacity:.65}@media(orientation:landscape) and (max-height:550px){.run-boss-door{padding-top:55px;gap:5px}.run-boss-door h2{font-size:28px}.run-boss-door p{font-size:20px}.run-crystal-frame{height:48vh;width:29vh}}\n.run-door-choices{position:absolute;inset:0;z-index:25;background:#0c1b24f2;display:flex;flex-direction:column;justify-content:center;padding:30px 4%;text-align:center}.run-door-choices h2{font:900 clamp(28px,5vw,54px)/1.1 system-ui;margin:12px 0}.run-door-choices>p{font:700 clamp(20px,3vw,28px)/1.3 system-ui}.run-door-cards{display:flex;gap:5%;justify-content:center}.run-door-cards article{width:45%;border:5px solid #8ba396;border-radius:18px;padding:12px;position:relative;background:#18302f;overflow:hidden}.run-door-cards article[data-selected=true]{border-color:#c7ff91;box-shadow:0 0 28px #c7ff9166}.run-door-cards article:after{content:'';height:8px;width:calc(var(--selection,0)*100%);position:absolute;bottom:0;left:0;background:#c7ff91}.run-door-cards strong{display:block;font-size:70px}.run-door-cards h3{font:900 clamp(23px,4vw,40px)/1.1 system-ui;margin:10px 0}.run-door-cards p{font:700 clamp(18px,2.6vw,24px)/1.3 system-ui}.run-door-cards img{width:100%;height:20vh;object-fit:cover;image-rendering:pixelated}.run-grass-game{position:absolute;inset:0;z-index:1}.run-grass-game .pf-hud{display:none!important}.run-chased-coach{position:absolute;left:50%;top:42%;width:100px;height:150px;object-fit:contain;image-rendering:pixelated;filter:sepia(1) saturate(4) drop-shadow(0 0 10px gold);pointer-events:none}.run-chased-coach[data-slowed=true]{opacity:.65}\n.run-grass-game.plank-flight-host{position:absolute;inset:0;height:100%;width:100%}\r\n.run-grass-game.plank-flight-host{position:absolute!important;inset:0!important}\n.run-pond-room{position:absolute;inset:0;z-index:1;background:#0c394c;overflow:hidden}.run-pond-room>canvas{display:block;width:100%;height:100%;image-rendering:pixelated}.run-pond-room.pond-nibble:after{content:'FISH NIBBLE';position:absolute;left:10%;right:10%;top:53%;text-align:center;color:#d7fff0;text-shadow:0 3px 8px #06272e;font:900 clamp(28px,5vw,54px)/1 system-ui;pointer-events:none;animation:pond-nibble 1s ease-out forwards}@keyframes pond-nibble{0%{opacity:1;transform:translateY(15px)}70%{opacity:1}100%{opacity:0;transform:translateY(-25px)}}@media(prefers-reduced-motion:reduce){.run-pond-room.pond-nibble:after{animation:none;display:none}}\n.run-pond-room:before{content:'';position:absolute;inset:0;background:linear-gradient(#061820c4,transparent 54%,#03141ea8);pointer-events:none}\nbody[data-camera-mode=environment] #camera-preview{inset:0;width:100%;height:100%;border:0;border-radius:0;z-index:0;object-fit:contain;background:#101b20;transform:none!important}\n#room-look,#camera-retry{position:absolute;z-index:45;left:18px;top:270px;font-size:16px;max-width:55vw}#camera-retry{top:auto;bottom:22%;background:#713e31}\n.run-shop-opening{position:absolute;inset:0;z-index:6;background:#29271f;pointer-events:none}.shop-door-art{position:absolute;inset:20% 10% 10%;overflow:hidden}.shop-door-art canvas{width:100%;height:100%;display:block}.run-shop-opening h2,.run-shop-opening p{position:absolute;left:6%;right:6%;text-align:center;text-shadow:0 2px 8px #000;z-index:2}.run-shop-opening h2{top:14%;font-size:38px}.run-shop-opening p{bottom:9%;font-size:24px;font-weight:850}.shop-door-handle{position:absolute;left:60%;top:48%;width:80px;height:80px;display:grid;place-items:center;border:6px solid #d2b267;border-radius:50%;background:#332d22;font-size:64px;color:#fff5c9}.run-shop-opening[data-gripped=true] .shop-door-handle{transform:rotate(35deg);border-color:#c8ff95}.run-shop-opening>canvas{position:absolute;inset:0;width:100%;height:100%;z-index:3}\n\n.material-opening{position:absolute;inset:0;z-index:32;overflow:hidden;pointer-events:none;background:#0b201e;color:#fff9df;--portal-progress:0;--portal-hold:0}\n.material-opening-art,.material-opening-veil{position:absolute;inset:0;pointer-events:none}\n.material-opening-leaf{position:absolute;inset:0;background-size:cover;background-position:center;image-rendering:pixelated;will-change:transform}\n.material-opening[data-material=jelly] .left{background-image:url('/pod/worlds/boards/jelly-poster.webp');border-radius:22%;transform:scale(calc(1 + var(--portal-progress)*2.8));filter:saturate(1.3)}\n.material-opening[data-material=jelly] .right{display:none}\n.material-opening[data-material=jelly] .material-opening-veil{background:radial-gradient(ellipse at center,transparent 20%,#702f9c99 80%);opacity:calc(.55 + var(--portal-progress)*.45)}\n.material-opening[data-material=grass] .material-opening-leaf{background-image:url('/pod/worlds/boards/grass-poster.webp');transform-origin:50% 100%}\n.material-opening[data-material=grass] .left{clip-path:polygon(0 0,55% 0,50% 100%,0 100%);transform:perspective(600px) translateX(calc(var(--portal-progress)*-20%)) rotateX(calc(var(--portal-progress)*78deg)) rotate(calc(var(--portal-progress)*-12deg))}\n.material-opening[data-material=grass] .right{clip-path:polygon(45% 0,100% 0,100% 100%,50% 100%);transform:perspective(600px) translateX(calc(var(--portal-progress)*20%)) rotateX(calc(var(--portal-progress)*78deg)) rotate(calc(var(--portal-progress)*12deg))}\n.material-opening[data-material=grass] .material-opening-veil{background:linear-gradient(#15291499,transparent 40%,#06180c99)}\n.material-opening h2,.material-opening p{position:absolute;z-index:2;left:7%;right:7%;text-align:center;text-shadow:0 2px 7px #000}\n.material-opening h2{top:24%;font:900 clamp(24px,6vw,44px)/1.1 system-ui}\n.material-opening p{bottom:16%;font:800 clamp(17px,4vw,25px)/1.35 system-ui;padding:12px;background:#0a1619dd;border-bottom:4px solid #b7f384}\n.material-opening[data-stage=charging] p{box-shadow:0 0 calc(var(--portal-hold)*28px) #b7f384}\n.material-opening[data-paused=true] .material-opening-art{filter:grayscale(.6)}\n.material-opening.reduced .material-opening-leaf{transform:none!important;opacity:calc(1 - var(--portal-progress))}\n@media(prefers-reduced-motion:reduce){.material-opening .material-opening-leaf{transform:none!important;opacity:calc(1 - var(--portal-progress))}}\n.run-boss-door{--crystal-break:0;pointer-events:none}\n.clay-opening,.dance-opening{pointer-events:none}\n.run-boss-door[data-opening=true] .run-crystal-poster{opacity:calc(.65*(1 - var(--crystal-break)));transform:translateY(calc(var(--crystal-break)*12%))}\n.run-boss-door[data-opening=true] .run-crystal-frame{box-shadow:0 0 calc(45px + var(--crystal-break)*35px) #d6faff88}\n.run-boss-door[data-opening=true] .run-boss-preview{filter:drop-shadow(0 0 16px #fff5bd)}\n.run-shop-opening{--handle-open:0}\n.run-shop-opening[data-opening=true] .shop-door-art{transform-origin:10% 50%;transform:perspective(600px) rotateY(calc(var(--handle-open)*-70deg));opacity:calc(1 - var(--handle-open))}\n.run-shop-opening[data-opening=true] .shop-door-handle{transform:rotate(calc(35deg + var(--handle-open)*80deg));opacity:calc(1 - var(--handle-open))}\n.dance-opening:after{content:\"\";position:absolute;inset:0;pointer-events:none;opacity:0;mix-blend-mode:screen;background:conic-gradient(from 30deg at 50% 10%,transparent,#d7339a55,transparent,#35c7d355,transparent,#d8d22e44,transparent)}\n.dance-opening[data-lights=\"1\"]:after{opacity:.12}.dance-opening[data-lights=\"2\"]:after{opacity:.2}.dance-opening[data-lights=\"3\"]:after{opacity:.28}.dance-opening[data-lights=\"4\"]:after{opacity:.36}\n[data-room-transit=grass] .fx{--c:#b2dc6c!important}[data-room-transit=grass] .fx-ring{border-radius:70% 0;rotate:35deg}\n[data-room-transit=jelly] .fx{--c:#d38bdf!important}[data-room-transit=jelly] .fx-ring{border-radius:40% 65%;border-width:14px}\n[data-room-transit=clay] .fx{--c:#d9a683!important}[data-room-transit=clay] .fx-ring{border-style:dashed;border-width:12px}\n[data-room-transit=pond] .fx{--c:#81d8e2!important}[data-room-transit=pond] .fx-ring{border-radius:50%;scale:1 .6}\n[data-room-transit=papier_mache] .fx{--c:#e5cf8c!important}[data-room-transit=papier_mache] .fx-ring{border-style:double;border-width:12px;rotate:14deg}\n[data-room-transit=crystal] .fx{--c:#c4f5ff!important}[data-room-transit=crystal] .fx-ring{clip-path:polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%)}\n[data-room-transit=dance] .fx{--c:#ed87d7!important}[data-room-transit=dance] .fx-ring{border-radius:6px;border-width:9px;rotate:45deg}\n.run-door-gesture{display:block;font:800 13px/1.35 monospace;color:#def1bd;margin-top:8px}\n@media(prefers-reduced-motion:reduce){.run-shop-opening[data-opening=true] .shop-door-art,.run-crystal-poster{transform:none!important}.dance-opening:after{mix-blend-mode:normal;opacity:.12!important}}\nbody[data-camera-mode=environment] #objective{bottom:14%;pointer-events:none}body[data-camera-mode=environment] #goal{font-size:clamp(36px,7vw,70px)}body[data-camera-mode=environment] #verb{font-size:clamp(20px,3vw,32px)}\n/* Reserve separate rows for progression, stats and room identity on a phone. */\n@media(orientation:portrait){\n .run-progress{display:grid;grid-template-columns:max-content minmax(36px,1fr) max-content;gap:6px 10px;align-items:center}\n .run-progress>span{white-space:nowrap}\n .run-progress progress{width:100%;min-width:0}\n #world-energy{grid-column:1/-1;font-size:17px}\n #stats{top:190px;font-size:21px}\n .coach-tag{top:226px;right:20px}\n .coach-tag span{font-size:15px;line-height:1.25;max-width:100%;overflow-wrap:anywhere}\n .coach-tag strong{font-size:21px;line-height:1.2;max-width:calc(100vw - 156px)}\n #camera-preview{top:250px}\n body[data-run-end-available=true] #run-message{bottom:max(80px,calc(env(safe-area-inset-bottom) + 70px));font-size:17px;line-height:1.3}\n body[data-run-room=corgi_shop] #objective{bottom:23%;left:5%;right:5%;gap:5px;padding:10px 12px;border-radius:8px;background:#0c1820d9}\n body[data-run-room=corgi_shop] #goal{font-size:clamp(28px,8vw,38px);line-height:1.15;letter-spacing:-.025em}\n body[data-run-room=corgi_shop] #verb{font-size:21px}\n body[data-run-room=corgi_shop] #cue{font-size:18px;line-height:1.25}\n body[data-run-room=corgi_shop] #room-look{top:300px;font-size:14px;min-height:44px;padding:8px 12px;max-width:52vw}\n}\n/* Class selection belongs to the pre-run screen; the server pins it at room entry. */\n.combat-choice{display:flex;flex-direction:column;gap:6px;max-width:420px;margin:12px auto 0;font-size:14px;font-weight:800;text-align:left}\n.combat-choice select{width:100%;min-height:44px;padding:9px;border:1px solid #b4c1a8;background:#142329;color:#eff4de;font:inherit;border-radius:6px}\n#opening #class-help{max-width:420px;margin:8px auto;font-size:14px;line-height:1.35}\n@media(max-height:760px){#opening{overflow-y:auto;padding-top:65px;padding-bottom:24px}#opening h1{font-size:clamp(44px,10vh,80px)}}\n/* Ending a run keeps its return action above any room entrance still mounted. */\n#summary{z-index:50}\n","modules/roguelite/run-view.mjs":"import * as THREE from 'three';\r\nimport {GAMES} from '../../exercise-library.mjs';\r\nimport {HandControl} from '../../gesture-controls.mjs';\r\nimport {MovementSession,MOVEMENTS,jointAngle} from '../../movement-engine.mjs';\r\nimport {createPoseInput} from '../../arcade/runner/pose-input.mjs';\r\nimport {classifyHand} from './hand-input.mjs';\r\nimport {createRunScene} from './run-scene.mjs';\r\nimport {createRunSession,createNextRunSession,currentRunRoom,observeRunRoom,setRunPaused,setRunTutorials,finishRunTutorial,advanceRunRoom,restoreRunSession,roomTarget} from './run-controller.mjs';\r\nimport {phaseProgress} from './boss-state.mjs';\r\nimport {finishBossChip as finishBossChipState} from './boss-finisher.mjs';\r\nimport {xpLevel,XP_TUNING_PROPOSAL} from './run-xp.mjs';\r\nimport {mountTutorialGuide} from './tutorial-guide.mjs';\r\nimport {createRoomHandoff} from './room-handoff.mjs';\r\nimport {mountBossDoor} from './boss-door.mjs';\r\nimport {weaponImage,weaponMats} from '../../pod/gala-rig/weapon.mjs';\r\nimport shieldDesigns from '../../pod/gala-rig/designs/shield.mjs';\r\nimport {emit} from '../../pod/gala-rig/parts.mjs';\r\nimport {rasterize} from '../../pod/gala-rig/raster.mjs';\r\nimport {claimGraphics} from '../graphics-lifecycle.mjs';\r\nimport {performanceOwner} from '../../performance-progress.mjs';\r\nimport {chooseRunRoute,resolveRunGame,runRouteOffers,settleRunHealth,interactRunShop} from './run-controller.mjs';\r\nimport {boonEffects} from './boons.mjs';\r\nimport {createGrassGame} from './grass-game.mjs';\r\nimport {mountGrassView} from './grass-view.mjs';\r\nimport {createPlankInput} from '../../arcade/plank-flight/pose-input.mjs';\r\nimport {mountDoorChoices} from './door-choice-view.mjs';\r\nimport {ROOM_BY_ID} from './room-contracts.mjs';\r\nimport {createWarmupEvidence,observeWarmupEvidence} from './warmup-evidence.mjs';\r\nimport {pondBreath,POND_TUNING_PROPOSAL} from './pond-room.mjs';\r\nimport {mountPondView} from './pond-view.mjs';\r\nimport {createCrisisRoomView} from './crisis-room-view.mjs';\r\nimport {createClayRoomView} from './clay-room-view.mjs';\r\nimport {createArcadeInput} from './arcade-input.mjs';\r\nimport {createDanceRoomView} from './dance-room-view.mjs';\r\nimport {mountDanceOpening} from './dance-opening.mjs';\r\nimport {mountTreasureRoomView} from './treasure-room-view.mjs';\r\nimport {mountClayOpening,detectClayOpeningGesture} from './clay-opening.mjs';\r\nimport {mountMaterialOpening,detectMaterialOpeningGesture} from './material-opening.mjs';\r\nimport {coachPresentationForRoom} from './coach-presentation.mjs';\r\nimport {physicalSound} from '../../audio/physical-sound.mjs';\r\nimport {mountCorgiShopView} from './corgi-shop-view.mjs';\r\nimport {createFlightLook} from '../coach-flight/look.mjs';\r\nimport {mountShopOpening} from './shop-opening.mjs';\r\nimport {enterRoom,resumePage,stepFrame,selectedRunWeaponId,grassSeedForNode} from './run-step.mjs';\r\nimport {createWorldRunClient} from './world-run-client.mjs';\r\nimport {authTransitions} from '../../auth-transition.mjs';\nimport {authFetch} from '../../auth-client.mjs';\nimport {RESOURCE_LABELS} from '../coach-world/resource-labels.mjs';\r\nimport {CLASS_IDS,COMBAT_CLASSES,normalizeClassInput,classGear} from './class-combat.mjs';\r\nimport {createCombatInput} from './combat-input.mjs';\r\nimport {mountCombatView} from './combat-view.mjs';\r\nimport {authoredRoomKind,normalizeAuthoredSample} from './run-authored-rooms.mjs';\r\nimport {mountYogaView} from './yoga-view.mjs';\r\nimport {mountSecretRoomView} from './secret-room-view.mjs';\r\nimport {mountTwoAltarsView} from './two-altars-view.mjs';\r\nimport {createTwoAltarsInput} from './two-altars-input.mjs';\r\nimport {COACH_UNITS} from '../coach-world/unit-catalog.mjs';\r\nimport {createRescueView} from './rescue-view.mjs';\r\nimport {projectRescue} from './rescue-state.mjs';\nimport {TUNING as STAT_TUNING} from './run-stats.mjs';\n\r\nconst $=id=>document.getElementById(id),stage=$('run-stage'),video=$('camera-preview');\r\nconst reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;\r\nconst combatInput=createCombatInput(),combatView=mountCombatView(stage,{reducedMotion:reduced});\r\nlet lastCombatInput={};\r\nconst classHelp={knight:'Punch behind your shield at half speed. Step aside for red attacks.',monk:'Chain punches and dodge left, right, high and low. Hold both hands vertically for 3 seconds to charge your aura.',dual:'Cross arms to guard. Slash into attacks, then counter with the other hand. Extend both arms to throw. Backstep: every 10 seconds.',mage:'Draw a triangle, square, Z, V or circle, then punch through its center. Hold a hand forward to stream. Raise both hands to shield. Backstep: every 7 seconds.'};\r\n$('combat-class').addEventListener('change',()=>{$('class-help').textContent=classHelp[$('combat-class').value];});\n$('class-help').textContent=classHelp[$('combat-class').value];\nconst requestedClass=new URLSearchParams(location.search).get('class');\r\nif(CLASS_IDS.includes(requestedClass)){$('combat-class').value=requestedClass;$('class-help').textContent=classHelp[requestedClass];}\r\nconst testMode=new URLSearchParams(location.search).get('test')==='1';\r\nconst worldRunMode=new URLSearchParams(location.search).get('worldRun')==='1';\r\nconst worldTimingEvidence=[];\r\nfunction recordWorldTiming(entry){if(!testMode)return;worldTimingEvidence.push({...entry});if(worldTimingEvidence.length>512)worldTimingEvidence.shift();}\r\nfunction markWorldFinish(stage){if(testMode)recordWorldTiming({stage,type:'boss_finish',eventId:null,clientSeq:null,monotonicMs:performance.now(),wallMs:Date.now()});}\r\nconst worldClient=worldRunMode?createWorldRunClient(testMode?{onTiming:recordWorldTiming}:{}):null;\r\nconst owner=worldRunMode?null:performanceOwner(localStorage,null),storeKey=owner?`myr5-roguelite-preview-v1/${encodeURIComponent(owner)}`:null;\r\nif(worldRunMode){document.title='MYR5 — Field Run';$('room-label').textContent='FIELD RUN';$('opening-note').textContent='Complete rooms to recharge your harvesters.';$('begin').textContent='START FIELD RUN';}\r\nconst releaseGraphics=claimGraphics('roguelite-run');await releaseGraphics.ready;\r\nconst scene=createRunScene(THREE,{canvas:$('world'),reducedMotion:reduced});\r\nlet state=null,tracker=null,movement=null,body=createPoseInput(),jumping=new MovementSession('jumping'),march=new MovementSession('march');\r\nlet frame=0,lastFrame=performance.now(),lastRaw=0,sequence=0,lastSave=0,tutorial=null,tutorialKey='',boss=null,punchHeld=false,disposed=false,transition=false;\r\nlet jumpCount=0,jumpUntil=0,marchCount=0,jogUntil=0;\r\nlet roomCoachId=null,roomCoachLabel='',bossDoor=null;\r\nlet routeDoor=null,grass=null,grassView=null,savedGrass=null,grassLastNow=null,grassPose=null,plank=createPlankInput({automatic:true});\r\nlet warmupEvidence=createWarmupEvidence();\r\nlet pond=null,pondView=null,pondBalance=new MovementSession('mountain'),pondInput=null;\r\nlet shopView=null,shopDrawing=false,shopDoor=null,cameraMode=null,cameraGeneration=0,shopLastNow=null,shopNotice='';\r\nlet activeRuntime=null,frameSeq=0;\r\nlet worldIntro=null,worldIntroTrackingPending=false;\r\nlet authoredView=null,authoredInput=null,authoredSceneNode=null,authoredGate=null,authoredGeneration=0,authoredComposite=null,authoredPose=null,authoredLastRaw=null,secretActionPending=false;\r\nlet yogaMovements={},secretHand=null;\r\nlet secretQueuedAction=null;\r\nlet worldViewTour=null;\r\nlet rescueView=null,rescuePending=false,rescueVisit=null,rescueAcceptedAt=null,rescueLoading=false,rescueVideoStyle=null;\r\nconst skipIntroButton=document.createElement('button');skipIntroButton.type='button';skipIntroButton.textContent='Skip intro';skipIntroButton.hidden=true;skipIntroButton.style.cssText='position:fixed;bottom:24px;right:20px;z-index:200';stage.append(skipIntroButton);skipIntroButton.onclick=()=>{worldIntro?.skip();worldViewTour?.lease?.skip();};\r\nconst recoveryContinueButton=document.createElement('button');recoveryContinueButton.type='button';recoveryContinueButton.textContent='Continue when ready';recoveryContinueButton.hidden=true;recoveryContinueButton.style.cssText='position:fixed;bottom:80px;left:20px;z-index:200';stage.append(recoveryContinueButton);recoveryContinueButton.onclick=()=>void advanceWorldRoom();\r\nasync function playWorldIntro(){\r\n const node=currentRunRoom(state);\r\n if(!['warmup','miniboss','mainboss'].includes(node?.type)||cameraMode==='environment'||new URLSearchParams(location.search).get('ar')==='1'||typeof scene.intro!=='function')return;\r\n const visit=worldVisitId();worldCameraTransition=true;\r\n try{\r\n  await worldClient.pause(visit,'loading',true);applyWorldEnvelope();\r\n  if(disposed||worldVisitId()!==visit)return;\r\n  worldIntro=scene.intro({reducedMotion:reduced});skipIntroButton.hidden=false;\r\n  await worldIntro.finished;\r\n  if(!disposed&&worldVisitId()===visit){await worldClient.pause(visit,'loading',false);applyWorldEnvelope();}\r\n }finally{worldIntro=null;skipIntroButton.hidden=true;worldCameraTransition=false;}\r\n}\r\n\r\nlet worldArcadeView=null,worldArcadeInput=null,worldArcadeRevision=null;\r\nlet worldDanceOpening=null,worldDanceReady=false,worldDanceLoadingActive=false,worldDanceLoadingPending=false,worldDanceTrackingPending=false,worldDanceCeremonyComplete=false,worldDancePoseTracked=false,worldDanceGateVisit=null,worldDanceGateRunId=null,worldDanceGateGeneration=0;\r\nlet worldClayOpening=null,worldClayReady=false,worldClayLoadingActive=false,worldClayLoadingPending=false,worldClayTrackingPending=false,worldClayCeremonyComplete=false,worldClayPoseTracked=false,worldClayGateVisit=null,worldClayGateRunId=null,worldClayGateGeneration=0;\r\nlet worldTreasureView=null,worldTreasureRevision=null,worldTreasureOpenPending=false;\r\nlet materialGate=null,materialGateGeneration=0;\r\nlet worldStarted=false,worldBlocked=false,worldUnsupported=false,worldFrameBuffer=[],worldFlushTimer=null,worldCameraTransition=false,worldFlushPromise=null,worldNodeId=null,worldAppliedVisitId=null,worldPondReady=false,worldLoadingClearPending=false,worldDoorObservation=0,worldShopActionPending=false;\r\nconst shopLook=createFlightLook(THREE);\r\nphysicalSound.mount();physicalSound.setScene('off');\r\nconst handoff=createRoomHandoff(stage,{reducedMotion:reduced,onPause(reason,on){if(state)state=setRunPaused(state,reason,on);}});\r\nconst fault=error=>{$('error').textContent=error?.message||String(error);$('error').hidden=false;};\r\nwindow.addEventListener('error',event=>fault(event.error||event.message));\r\nfunction save(force=false){\r\n if(worldRunMode)return;\r\n if(!state||(!force&&performance.now()-lastSave<1000))return;\r\n try{localStorage.setItem(storeKey,JSON.stringify({...state,activeBoss:boss,activeGrass:grass?.snapshot()??null,activePond:pond,activeRuntime:activeRuntime?{...activeRuntime,boss:boss?structuredClone(boss):null,pond:pond?structuredClone(pond):null,grass:null,obsHighWater:Math.max(activeRuntime.obsHighWater,sequence)}:null}));lastSave=performance.now();}catch(error){state=setRunPaused(state,'user',true);fault(Error('Run could not be saved. Free storage before continuing.'));}\r\n}\r\nfunction saved(){if(!storeKey)return null;try{return JSON.parse(localStorage.getItem(storeKey));}catch{return null;}}\r\n$('resume').hidden=!saved();\r\nconst chosenExercises=()=>{\r\n // Reuse actual exercise IDs. Existing flight-deck configuration will supply this\r\n // selection when the preview is composed into the app's switchboard owner.\r\n const selected=new URLSearchParams(location.search).get('exercises');\r\n return selected?selected.split(','):['shallow-squat','boxing','overhead-reach','mountain','march'];\r\n};\r\nconst chosenRooms=()=>new URLSearchParams(location.search).get('rooms')?.split(',')??['pond','grass','papier_mache'];\r\nconst worldOptionalRoom=node=>!!authoredRoomKind(node)||['healing_waters','corgi_shop','coin_chase','treasure_side_room'].includes(node?.roomType);\r\nconst roomCameraMode=node=>activeRuntime?.rescue&&activeRuntime.nodeId===node?.id||node?.roomType==='corgi_shop'||node?.roomType==='two_altars'&&state?.phase!=='tutorial'?'environment':'user';\r\nconst worldSupportedRoom=node=>node?.type==='recovery'||node?.type==='warmup'||['miniboss','mainboss'].includes(node?.type)||worldOptionalRoom(node)||['clay','dance','jelly','treasure'].includes(node?.doorId);\r\nasync function acquireCamera(mode){\r\n if(disposed||!state||cameraMode===mode&&tracker?.running)return;\r\n const generation=++cameraGeneration;tracker?.stop();tracker=null;cameraMode=mode;\r\n document.body.dataset.cameraMode=mode;shopLook.reset();lastRaw=performance.now();\r\n combatInput.reset();lastCombatInput={};\r\n if(testMode)return;\r\n let worldHandoff=false;\r\n if(worldRunMode&&worldStarted&&activeRuntime){\r\n  worldCameraTransition=true;worldHandoff=true;\r\n  try{await flushWorldFrames();await worldClient.pause(worldVisitId(),'camera-handoff',true);applyWorldEnvelope();await worldClient.pause(worldVisitId(),'tracking',true);applyWorldEnvelope();}\r\n  catch(error){worldCameraTransition=false;worldFailure(error);return;}\r\n }else if(!worldRunMode)state=setRunPaused(setRunPaused(state,'camera-handoff',true),'tracking',true);\r\n render();\r\n const current=new HandControl({camera:mode,video,trackPose:mode==='user',useWidestZoom:mode==='user',inferenceHz:10,onGesture(){},\r\n  message:text=>{if(generation===cameraGeneration)$('run-message').textContent=text;},\r\n  onError:message=>{if(generation!==cameraGeneration)return;if(worldRunMode&&worldStarted){worldCameraTransition=true;void flushWorldFrames().then(()=>worldClient.pause(worldVisitId(),'tracking',true)).then(applyWorldEnvelope).catch(worldFailure).finally(()=>{worldCameraTransition=false;});}else if(!worldRunMode)state=setRunPaused(state,'tracking',true);$('camera-retry').hidden=false;fault(Error(message));},\r\n  onRaw:raw=>{if(generation!==cameraGeneration||disposed)return;activeRuntime?.rescue?rawRescue(raw):mode==='environment'?(authoredRoomKind(currentRunRoom(state))==='altar'?rawAuthoredAltar(raw):rawShop(raw)):rawPose(raw);}});\r\n tracker=current;\r\n try{await current.start();if(disposed||generation!==cameraGeneration){current.stop();return;}$('camera-retry').hidden=true;}\r\n catch(error){if(generation===cameraGeneration){$('camera-retry').hidden=false;fault(error);}}\r\n finally{if(!disposed&&generation===cameraGeneration){if(!worldRunMode)state=setRunPaused(state,'camera-handoff',false);else if(worldHandoff){try{await worldClient.pause(worldVisitId(),'camera-handoff',false);applyWorldEnvelope();}catch(error){worldFailure(error);}finally{worldCameraTransition=false;}}save(true);render();}}\r\n}\r\nfunction shopAction(action){\r\n if(!state||!shopView||action.type==='pet')return;\r\n if(worldRunMode){void commitWorldShopAction(action);return;}\r\n const prior=state.shopLedger,coins=state.runCoins;\r\n state=interactRunShop(state,action);\r\n const shop=state.shopLedger.shops[currentRunRoom(state).id];\r\n shopNotice=action.type==='checkout'?(state.runCoins<coins?'PURCHASED':shop.basket.length===0&&prior.shops[shop.shopId].basket.length?'NOT ENOUGH COINS — ITEMS RETURNED':'BASKET EMPTY')\r\n  :action.type==='grab'?'ITEM CARRIED':action.type==='attack'?'MERCHANT FLED — LATER SHOPS ARE DANGEROUS':shop.status==='stolen'?'STOLEN — LATER SHOPS ARE DANGEROUS':'';\r\n save(true);render();\r\n}\r\nfunction rawShop(raw){\r\n if(!state||disposed||!shopView)return;\r\n if(!Number.isFinite(raw.ts)||performance.now()-raw.ts>250){if(worldRunMode){if(worldStarted&&!worldBlocked&&!state.pauseReasons.includes('tracking')&&!worldCameraTransition){worldCameraTransition=true;void drainWorldFrames().then(ok=>ok?worldClient.pause(worldVisitId(),'tracking',true):null).then(applyWorldEnvelope).catch(worldFailure).finally(()=>{worldCameraTransition=false;});}}else state=setTracking(state,false);render();return;}\r\n lastRaw=raw.ts;\r\n const tracked=(raw.landmarks||[]).some(hand=>hand?.length===21&&hand.every(p=>Number.isFinite(p.x)&&Number.isFinite(p.y)));\r\n if(worldRunMode){\r\n  collectWorldSample({now:performance.now(),sourceTs:raw.ts,quality:{body:false,hands:tracked},tracked,value:0});\r\n  if(shopDoor&&state.phase!=='tutorial'){\r\n   shopDoor.observe({landmarks:raw.landmarks,worldLandmarks:raw.worldLandmarks,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1,now:raw.ts,tracked},{paused:state.phase!=='active'||!!state.pauseReasons.length});render();return;\r\n  }\r\n  shopView.observeHand({landmarks:raw.landmarks,worldLandmarks:raw.worldLandmarks,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1,now:raw.ts,tracked});render();return;\r\n }\r\n sample({now:raw.ts,tracked});\r\n if(shopDoor&&state.phase!=='tutorial'){\r\n  shopDoor.observe({landmarks:raw.landmarks,worldLandmarks:raw.worldLandmarks,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1,now:raw.ts,tracked},{paused:state.phase!=='active'||!!state.pauseReasons.length});render();return;\r\n }\r\n shopView.observeHand({landmarks:raw.landmarks,worldLandmarks:raw.worldLandmarks,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1,now:raw.ts,tracked});\r\n render();\r\n}\r\nfunction drawImage(canvas,image,{crop=false}={}){\r\n canvas.width=image.w;canvas.height=image.h;const ctx=canvas.getContext('2d'),pixels=ctx.createImageData(image.w,image.h);pixels.data.set(image.data);ctx.putImageData(pixels,0,0);\r\n if(crop){\r\n  let left=image.w,top=image.h,right=0,bottom=0;\r\n  for(let y=0;y<image.h;y++)for(let x=0;x<image.w;x++)if(image.data[(y*image.w+x)*4+3]){left=Math.min(left,x);right=Math.max(right,x);top=Math.min(top,y);bottom=Math.max(bottom,y);}\r\n  if(right>left&&bottom>top){const source=document.createElement('canvas');source.width=image.w;source.height=image.h;source.getContext('2d').putImageData(pixels,0,0);ctx.clearRect(0,0,image.w,image.h);ctx.imageSmoothingEnabled=false;ctx.drawImage(source,left,top,right-left+1,bottom-top+1,0,0,image.w,image.h*1.15);}\r\n }\r\n}\r\ndrawImage($('held-weapon'),weaponImage({type:'dagger',tier:0},{w:96,h:160,yaw:.2}),{crop:true});\r\nconst shieldFaces=shieldDesigns[2].flatMap(piece=>emit(piece).faces),mats=weaponMats(0);\r\nfor(const face of shieldFaces)face.r=mats[face._mat]||mats.shell;\r\n// Existing Gala shield card; frame its authored wrist position in first person.\r\ndrawImage($('held-shield'),rasterize(shieldFaces,{w:128,h:160,scale:220,ox:139,oy:327,yaw:0,pitch:0}));\r\n// Translate the existing local save fields; no server or ownership evidence is inferred.\r\nfunction legacyRuntimeFor(data,node){\r\n const prior=data.activeRuntime;\r\n if(prior&&(prior.v!==1||prior.nodeId!==node.id))throw Error('Saved gameplay runtime does not match this room or version.');\r\n const savedBoss=Object.hasOwn(data,'activeBoss')?data.activeBoss:prior?.boss??null;\r\n const savedWater=Object.hasOwn(data,'activePond')?data.activePond:prior?.pond??null;\r\n return {v:1,nodeId:node.id,shopLastNow:null,grassLastNow:null,boss:savedBoss,pond:savedWater,grass:null,\r\n  obsHighWater:Math.max(prior?.obsHighWater??0,(data.lastObservation??-1)+1,(savedBoss?.lastObsId??-1)+1,(savedWater?.lastObservationId??-1)+1)};\r\n}\r\nfunction configureRoom(resumedRuntime=null){\r\n resetMaterialGate();worldArcadeView?.dispose();worldArcadeView=null;worldArcadeInput=null;\r\n combatInput.reset();lastCombatInput={};\r\n tutorial?.dispose();tutorial=null;tutorialKey='';body=createPoseInput();jumping=new MovementSession('jumping');march=new MovementSession('march');\r\n jumpCount=marchCount=0;jumpUntil=jogUntil=0;punchHeld=false;\r\n const node=currentRunRoom(state);movement=node?.type==='warmup'?new MovementSession(node.exerciseId):null;\r\n warmupEvidence=createWarmupEvidence();\r\n grassView?.dispose();grassView=null;grass=null;grassLastNow=null;grassPose=null;plank=createPlankInput({automatic:true});\r\n pondView?.dispose();pondView=null;pond=null;pondInput=null;pondBalance=new MovementSession('mountain');\r\n shopView?.dispose();shopView=null;shopDrawing=false;shopLastNow=null;shopNotice='';\r\n shopDoor?.dispose();shopDoor=null;\r\n if(!node){activeRuntime=null;boss=null;return;}\r\n if(node.roomType==='coin_chase'){\r\n  // Deliberate partial consumer: keep the original persistent grass simulation/save path.\r\n  // S1 tape replay is NOT used for live grass frames (cost and save limit remain unresolved).\r\n  activeRuntime=null;boss=null;\r\n  grass=createGrassGame({encounterId:node.id,seed:grassSeedForNode(node.id),catchWindows:boonEffects(state.boons).chaseCatchWindows,saved:savedGrass});savedGrass=null;\r\n  grassView=mountGrassView(stage,grass,{onError:error=>{state=setRunPaused(state,'user',true);fault(error);}});\r\n  ensureMaterialGate();\r\n  void acquireCamera('user');return;\r\n }\r\n if(resumedRuntime)activeRuntime=resumedRuntime;\r\n else{const entered=enterRoom({runState:state,now:performance.now(),bossVisualId:node.coachId??roomCoachId});state=entered.runState;activeRuntime=entered.runtime;}\r\n boss=activeRuntime.boss;pond=activeRuntime.pond;shopLastNow=activeRuntime.shopLastNow;\r\n sequence=Math.max(sequence,activeRuntime.obsHighWater);activeRuntime.obsHighWater=sequence;\r\n if(node.doorId==='jelly'){mountWorldArcade(node);worldArcadeView.update(activeRuntime.arcade);ensureMaterialGate();}\r\n if(node.roomType==='corgi_shop'){\r\n  shopDrawing=true;shopView=mountCorgiShopView(stage,{video,renderer:scene.renderer,reducedMotion:reduced,onAction:shopAction,onTutorialOpenHand(){if(state.phase==='tutorial'&&!state.pauseReasons.length)tutorial?.advance();}});\r\n  if(!state.shopDoorsOpened.includes(node.id)&&state.phase!=='walk'){\r\n   shopDoor=mountShopOpening(stage,{video,onComplete:openShopDoor});\r\n   void shopDoor.ready.catch(error=>{state=setRunPaused(state,'user',true);fault(error);});\r\n  }\r\n  void acquireCamera('environment');return;\r\n }\r\n void acquireCamera('user');\r\n if(pond){\r\n  pondView=mountPondView(stage,{reducedMotion:reduced,onError:error=>{state=setRunPaused(state,'user',true);fault(error);}});\r\n  const mounted=pondView;\r\n  void mounted.ready.then(()=>{if(disposed||pondView!==mounted)return;state=setRunPaused(state,'loading',false);save(true);render();}).catch(error=>{if(disposed||pondView!==mounted)return;state=setRunPaused(state,'loading',true);fault(error);render();});return;\r\n }\r\n if(boss)drawImage($('held-weapon'),weaponImage({type:selectedRunWeaponId(state.shopLedger),tier:0},{w:96,h:160,yaw:.2}),{crop:true});\r\n}\r\nfunction openShopDoor(){\r\n if(worldRunMode)return commitWorldShopAction({type:'open-door'});\r\n const node=currentRunRoom(state);state=interactRunShop(state,{type:'open-door'});\r\n if(!state.shopDoorsOpened.includes(node.id))return false;\r\n shopDoor?.dispose();shopDoor=null;shopLastNow=null;save(true);render();\r\n return true;\r\n}\r\nfunction worldVisitId(){return worldClient?.envelope?.visit?.visitId??worldClient?.envelope?.currentVisitId??null;}\r\nlet worldGrassFlight=null,worldGrassRevision=null;\r\nfunction configureWorldRoomView(node){\r\n resetRescueView();\r\n cancelWorldViewTour();\r\n resetAuthoredView();\r\n resetMaterialGate();\r\n tutorial?.dispose();tutorial=null;tutorialKey='';\r\n grassView?.dispose();grassView=null;grass=null;worldGrassFlight=null;worldGrassRevision=null;grassPose=null;plank=createPlankInput({automatic:true});\r\n pondView?.dispose();pondView=null;pond=null;pondInput=null;pondBalance=new MovementSession('mountain');worldPondReady=false;worldLoadingClearPending=false;\r\n shopView?.dispose();shopView=null;shopDrawing=false;shopDoor?.dispose();shopDoor=null;shopNotice='';\r\n worldArcadeView?.dispose();worldArcadeView=null;worldArcadeInput=null;worldArcadeRevision=null;\r\n worldTreasureView?.dispose();worldTreasureView=null;worldTreasureRevision=null;worldTreasureOpenPending=false;\r\n worldDanceOpening?.dispose();worldDanceOpening=null;worldDanceReady=false;worldDanceLoadingActive=false;worldDanceLoadingPending=false;worldDanceTrackingPending=false;worldDanceCeremonyComplete=false;worldDancePoseTracked=false;worldDanceGateVisit=null;worldDanceGateRunId=null;worldDanceGateGeneration++;\r\n worldClayOpening?.dispose();worldClayOpening=null;worldClayReady=false;worldClayLoadingActive=false;worldClayLoadingPending=false;worldClayTrackingPending=false;worldClayCeremonyComplete=false;worldClayPoseTracked=false;worldClayGateVisit=null;worldClayGateRunId=null;worldClayGateGeneration++;\r\n if(['clay','dance','jelly'].includes(node?.doorId))mountWorldArcade(node);\r\n if(node?.doorId==='dance')beginWorldDanceOpening();\r\n if(node?.roomType==='healing_waters'){\r\n  pond=activeRuntime?.pond??null;\r\n  pondView=mountPondView(stage,{reducedMotion:reduced,onError:worldFailure});const mounted=pondView;\r\n  void mounted.ready.then(()=>{if(disposed||pondView!==mounted)return;worldPondReady=true;clearWorldPondLoading();}).catch(worldFailure);\r\n }else if(node?.roomType==='corgi_shop'){\r\n  shopDrawing=true;shopView=mountCorgiShopView(stage,{video,renderer:scene.renderer,reducedMotion:reduced,onAction:shopAction,onTutorialOpenHand(){if(state.phase==='tutorial'&&!state.pauseReasons.length)tutorial?.advance();}});\r\n  ensureWorldShopDoor(node);\r\n }else if(node?.roomType==='coin_chase'){\r\n  syncWorldGrass(node);\r\n }else if(node?.roomType==='treasure_side_room'){\r\n  worldTreasureView=mountTreasureRoomView({host:stage,onOpen:openWorldTreasureChest,THREE,scene:scene.scene,camera:scene.camera,renderer:scene.renderer,reducedMotion:reduced});\r\n }\r\n ensureAuthoredView(node);\r\n}\r\nfunction resetAuthoredView(){\r\n if(secretQueuedAction){secretQueuedAction.reject(Error('Secret room changed.'));secretQueuedAction=null;}\r\n authoredGeneration++;authoredGate=null;authoredView?.dispose();authoredView=null;authoredInput?.dispose();authoredInput=null;\r\n authoredComposite?.remove();authoredComposite=null;$('world').style.opacity='';authoredLastRaw=null;authoredPose=null;secretHand=null;secretActionPending=false;\r\n yogaMovements=Object.fromEntries(['mountain','salute','warrior'].map(id=>[id,new MovementSession(id)]));\r\n}\r\nfunction resetRescueView(){\r\n rescueView?.dispose();rescueView=null;rescueVisit=null;rescueAcceptedAt=null;rescueLoading=false;\r\n if(rescueVideoStyle){video.style.cssText=rescueVideoStyle.style;video.hidden=rescueVideoStyle.hidden;rescueVideoStyle=null;}\r\n}\r\nasync function beginWorldRescue(){\r\n if(state?.schedule?.scheduleVersion!==4||boss?.status!=='finisher'||activeRuntime?.rescue||rescuePending||worldCameraTransition||transition||disposed||worldBlocked||state.pauseReasons.length)return;\r\n rescuePending=true;worldCameraTransition=true;const visit=worldVisitId();\n try{\r\n  if(!await drainWorldFrames()||worldVisitId()!==visit||state.pauseReasons.length)return;\r\n  await worldClient.beginRescue(visit);applyWorldEnvelope();\r\n  if(!testMode)await acquireCamera('environment');\r\n }catch(error){worldFailure(error);}finally{rescuePending=false;worldCameraTransition=false;}\n}\r\nfunction syncRescueView(){\r\n const projection=activeRuntime?.rescue;if(!projection)return;\r\n if(!rescueView){\r\n  rescueVisit=worldVisitId();rescueVideoStyle={style:video.style.cssText,hidden:video.hidden};video.hidden=false;\r\n  video.style.cssText='position:absolute;inset:0;width:100%;height:100%;object-fit:contain;border:0;border-radius:0;z-index:-1;transform:none';\r\n  rescueView=createRescueView({root:stage,scene,reducedMotion:reduced,onRetry(){queueMicrotask(()=>void prepareWorldRescueView());}});\r\n  void prepareWorldRescueView();\r\n }else void rescueView.present(projectRescue(projection));\r\n if(projection.status==='accepted'&&rescueAcceptedAt===null)rescueAcceptedAt=performance.now();\r\n if(document.hidden||state.pauseReasons.length||worldCameraTransition)rescueView.invalidate();\r\n}\r\nasync function prepareWorldRescueView(){\r\n if(rescueLoading||!rescueView)return;rescueLoading=true;const mounted=rescueView,visit=rescueVisit;\r\n try{\r\n  if(!state.pauseReasons.includes('loading')){await worldClient.pause(visit,'loading',true);applyWorldEnvelope();}\r\n  if(disposed||mounted!==rescueView||visit!==worldVisitId())return;\r\n  const ready=await mounted.present(projectRescue(activeRuntime.rescue));\r\n  if(!ready||disposed||mounted!==rescueView||visit!==worldVisitId())return;\r\n  await worldClient.pause(visit,'loading',false);applyWorldEnvelope();\r\n }catch(error){worldFailure(error);}finally{if(mounted===rescueView)rescueLoading=false;}\r\n}\r\nfunction rawRescue(raw){\r\n if(!rescueView||disposed||!activeRuntime?.rescue)return;lastRaw=raw.ts;\r\n const now=performance.now(),fresh=Number.isFinite(raw.ts)&&now-raw.ts<=250,hands=fresh&&(raw.landmarks??[]).some(hand=>hand?.length===21);\r\n const dto=rescueView.sample(raw,{now,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1});\r\n if(!dto)return;\r\n collectWorldSample({now,sourceTs:raw.ts,tracked:fresh,quality:{body:false,hands},rescueInput:dto});\r\n}\r\nfunction currentAuthoredGate(gate=authoredGate){return !!gate&&!disposed&&!worldBlocked&&gate.generation===authoredGeneration&&gate.nodeId===currentRunRoom(state)?.id&&gate.visitId===worldVisitId()&&gate.runId===worldClient.envelope?.runId;}\r\nfunction ensureAuthoredView(node=currentRunRoom(state)){\r\n const kind=authoredRoomKind(node);if(!kind||!activeRuntime?.authored||authoredSceneNode!==node.id||authoredView)return;\r\n const gate={generation:authoredGeneration,nodeId:node.id,visitId:worldVisitId(),runId:worldClient.envelope?.runId,kind,ready:false,released:false,pending:false};authoredGate=gate;\r\n if(kind==='yoga')authoredView=mountYogaView(stage,{T:THREE,roomGroup:scene.currentRoom.group,reducedMotion:reduced,onError:worldFailure});\r\n if(kind==='secret')authoredView=mountSecretRoomView({host:stage,THREE,scene:scene.scene,reducedMotion:reduced,onAction:commitSecretAction});\r\n if(kind==='altar'){\r\n  authoredView=mountTwoAltarsView(stage,{scene:scene.scene,camera:scene.camera,renderer:scene.renderer,video,reducedMotion:reduced});authoredInput=createTwoAltarsInput();\r\n  authoredComposite=document.createElement('canvas');authoredComposite.setAttribute('aria-hidden','true');authoredComposite.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1';stage.append(authoredComposite);$('world').style.opacity='0';\r\n }\r\n const mounted=authoredView;\r\n void Promise.resolve(mounted.readyPromise).then(()=>{if(currentAuthoredGate(gate)&&authoredView===mounted){gate.ready=true;syncAuthoredView();ensureWorldViewTour();}}).catch(error=>{if(currentAuthoredGate(gate)&&error?.name!=='AbortError')worldFailure(error);});\r\n syncAuthoredView();\r\n}\r\nfunction syncAuthoredView(dt=0){\r\n if(!authoredView||!activeRuntime?.authored||!currentAuthoredGate())return;\r\n const room=activeRuntime.authored,paused=state.phase!=='active'||state.pauseReasons.length>0||document.hidden;\r\n authoredView.setResolution?.(scene.renderer.domElement.width,scene.renderer.domElement.height);\r\n if(room.kind==='yoga')authoredView.render(room.state,{dt,paused,tracked:authoredLastRaw?.tracked===true,eligible:authoredPose?.eligible===true});\r\n else if(room.kind==='secret')authoredView.render(room.state,{active:state.phase==='active',paused,loading:!authoredGate.ready,touring:!!worldViewTour?.lease&&!worldViewTour.done});\r\n else authoredView.update(room.state,{armor:state.stats.armor,runTokens:state.runCoins});\r\n}\r\nasync function commitSecretAction(action){\r\n const gate=authoredGate;if(!currentAuthoredGate(gate)||gate.kind!=='secret'||!gate.released||secretActionPending)throw Error('Secret room is not ready.');\r\n secretActionPending=true;\r\n try{\r\n  if(!await drainWorldFrames()||!currentAuthoredGate(gate)||state.phase!=='active'||state.pauseReasons.length)throw Error('Clear the pause before choosing.');\r\n  // Action belongs to the NEXT fresh sensor frame, never a reused sourceTs.\r\n  return await new Promise((resolve,reject)=>{\r\n   const timeout=setTimeout(()=>{if(secretQueuedAction?.gate===gate){secretQueuedAction=null;reject(Error('Step into view and try your choice again.'));}},5000);\r\n   secretQueuedAction={gate,action,resolve:value=>{clearTimeout(timeout);resolve(value);},reject:error=>{clearTimeout(timeout);reject(error);}};\r\n  });\r\n }finally{secretActionPending=false;}\r\n}\r\nfunction rawAuthoredAltar(raw){\r\n if(!state||disposed||!authoredView||!authoredInput)return;lastRaw=raw.ts;\r\n const now=performance.now(),fresh=Number.isFinite(raw.ts)&&now-raw.ts<=250,tracked=fresh&&(raw.landmarks??[]).some(hand=>hand?.length===21);\r\n authoredView.render({dt:0,paused:!!state.pauseReasons.length});\r\n const input=authoredInput.observe({...raw,now:raw.ts,tracked,aspect:video.videoWidth/Math.max(1,video.videoHeight)||1},{camera:scene.camera,targets:authoredView.targets,now,paused:state.phase!=='active'||!!state.pauseReasons.length});\r\n authoredLastRaw={sourceTs:raw.ts,tracked:input.tracked,quality:{body:false,hands:input.tracked}};\r\n void handleWorldInput({now,sourceTs:raw.ts,tracked:input.tracked,quality:authoredLastRaw.quality,altarInput:input.altar}).catch(worldFailure);\r\n}\r\nfunction drawAuthoredAltar(dt,paused){\r\n if(!authoredComposite||!authoredView)return;\r\n const canvas=authoredComposite,scale=Math.min(1,640/Math.max(1,stage.clientWidth),360/Math.max(1,stage.clientHeight)),w=Math.max(1,Math.round(stage.clientWidth*scale)),h=Math.max(1,Math.round(stage.clientHeight*scale));\r\n if(canvas.width!==w||canvas.height!==h){canvas.width=w;canvas.height=h;}\r\n const aspect=video.videoWidth/Math.max(1,video.videoHeight)||scene.camera.aspect,width=Math.min(w,h*aspect),height=width/aspect,x=(w-width)/2,y=(h-height)/2;\r\n authoredView.render({dt,paused});scene.renderer.render(scene.scene,scene.camera);\r\n const ctx=canvas.getContext('2d');ctx.clearRect(0,0,w,h);ctx.drawImage(scene.renderer.domElement,x,y,width,height);\r\n authoredView.compositeHand(ctx,authoredInput?.hand,{x,y,width,height});\r\n if(video.readyState>=2){ctx.globalCompositeOperation='destination-over';ctx.drawImage(video,x,y,width,height);ctx.globalCompositeOperation='source-over';}\r\n}\r\nfunction cancelWorldViewTour(){worldViewTour?.lease?.cancel();worldViewTour=null;skipIntroButton.hidden=true;}\r\nconst tourIdentity=()=>`${state?.accountId}:${state?.dataEpoch}:${worldClient?.envelope?.runId}:${worldVisitId()}`;\r\nfunction ensureWorldViewTour(){\r\n if(!worldRunMode||!activeRuntime||state?.phase!=='active'||worldViewTour||disposed)return;\r\n const node=currentRunRoom(state),view=authoredView??worldTreasureView??worldArcadeView??grassView??pondView;if(!view)return;\r\n const kind=authoredRoomKind(node),identity=tourIdentity(),tour={identity,view,lease:null,pending:true,done:false};worldViewTour=tour;\r\n const current=()=>worldViewTour===tour&&!disposed&&!worldBlocked&&tourIdentity()===identity;\r\n void(async()=>{\r\n  if(!await drainWorldFrames()||!current())return;\r\n  if(!state.pauseReasons.includes('loading')){await worldClient.pause(worldVisitId(),'loading',true);if(!current())return;applyWorldEnvelope();}\r\n  const readiness=view.readyPromise??view.ready;\r\n  if(readiness&&typeof readiness.then==='function')await readiness;\r\n  if(!current())return;\r\n  // Arcade readiness arrives with the first server checkpoint; never tour missing scenery.\r\n  if(readiness===false){worldViewTour=null;return;}\r\n  const resumeProgress=kind==='yoga'&&activeRuntime.authored.state.status!=='opening'||kind==='secret'&&activeRuntime.authored.state.stage!=='opening';\r\n  if(kind!=='altar'&&!resumeProgress){\r\n   tour.sceneDriven=typeof view.intro!=='function';tour.lease=!tour.sceneDriven?view.intro({reducedMotion:reduced}):kind?scene.intro({reducedMotion:reduced}):null;\r\n   if(!tour.lease)throw Error('Room intro is unavailable. Retry the room.');\r\n   skipIntroButton.hidden=false;await tour.lease.finished;\r\n  }\r\n  if(!current())return;tour.done=true;tour.pending=false;skipIntroButton.hidden=true;\r\n  // Existing ceremonies still own loading after their actual-scene tour.\r\n  if(['clay','dance','jelly'].includes(node.doorId)||node.roomType==='coin_chase')return;\r\n  await worldClient.pause(worldVisitId(),'loading',false);if(!current())return;\r\n  if(authoredGate)authoredGate.released=true;applyWorldEnvelope();\r\n })().catch(error=>{if(current()&&error?.name!=='AbortError')worldFailure(error);});\r\n}\r\nfunction syncWorldGrass(node=currentRunRoom(state)){\r\n if(!worldRunMode||node?.roomType!=='coin_chase')return;\r\n const revision=worldClient?.envelope?.runRevision??null;\r\n if(activeRuntime?.grass&&revision!==null&&revision===worldGrassRevision)return;\r\n const options={encounterId:node.id,seed:grassSeedForNode(node.id),catchWindows:boonEffects(state.boons).chaseCatchWindows};\r\n if(activeRuntime?.grass)grass=createGrassGame({...options,saved:activeRuntime.grass});\r\n else if(!grass||grass.snapshot().encounterId!==node.id)grass=createGrassGame(options);\r\n if(!worldGrassFlight)worldGrassFlight=grass.flight;\r\n else{\r\n  for(const key of Object.keys(worldGrassFlight))if(!Object.hasOwn(grass.flight,key))delete worldGrassFlight[key];\r\n  Object.assign(worldGrassFlight,grass.flight);\r\n }\r\n if(activeRuntime?.grass)worldGrassRevision=revision;\r\n if(!grassView){\r\n  const renderGame={flight:worldGrassFlight,get chase(){return grass.chase;}};\r\n  grassView=mountGrassView(stage,renderGame,{onError:worldFailure});\r\n }\r\n}\r\nfunction ensureWorldShopDoor(node=currentRunRoom(state)){\r\n if(!worldRunMode||node?.roomType!=='corgi_shop'||!shopView)return;\r\n if(state.phase==='tutorial'||state.shopDoorsOpened?.includes(node.id)){shopDoor?.dispose();shopDoor=null;return;}\r\n if(!shopDoor){\r\n  const runId=worldClient?.envelope?.runId;let openingVisitId=null;\r\n  const mounted=mountShopOpening(stage,{video,onComplete(){\r\n   if(disposed||shopDoor!==mounted||currentRunRoom(state)?.id!==node.id||worldClient?.envelope?.runId!==runId)return false;\r\n   const visitId=worldVisitId();if(!visitId||openingVisitId!==null&&openingVisitId!==visitId)return false;\r\n   openingVisitId=visitId;return openShopDoor();\r\n  }});\r\n  shopDoor=mounted;void mounted.ready.catch(worldFailure);\r\n }\r\n}\r\nfunction clearWorldPondLoading(){\r\n if(worldRunMode)return; // The host's readiness + actual-view tour lease releases loading.\r\n if(!worldRunMode||!worldStarted||!worldPondReady||!activeRuntime||worldLoadingClearPending||!state?.pauseReasons?.includes('loading')||worldBlocked)return;\r\n worldLoadingClearPending=true;\r\n void worldClient.pause(worldVisitId(),'loading',false).then(()=>{worldLoadingClearPending=false;applyWorldEnvelope();}).catch(error=>{worldLoadingClearPending=false;worldFailure(error);});\r\n}\r\nasync function drainWorldFrames(){\r\n while(worldFlushPromise||worldFrameBuffer.length){\r\n  const pending=worldFlushPromise,before=worldFrameBuffer.length,ok=pending?await pending:await flushWorldFrames();\r\n  if(!ok||worldBlocked||!pending&&before>0&&worldFrameBuffer.length>=before)return false;\r\n }\r\n return true;\r\n}\r\nasync function commitWorldShopAction(action){\r\n const node=currentRunRoom(state),visitId=worldVisitId();\r\n if(!worldRunMode||!shopView||!activeRuntime||!node||node.roomType!=='corgi_shop'||!visitId||worldBlocked||worldShopActionPending||worldCameraTransition||transition||disposed||state.phase!=='active'||state.pauseReasons.length)return false;\r\n const type=action?.type;if(!['open-door','grab','return','checkout','exit','attack'].includes(type))return false;\r\n const payload={type};if(action.itemId!==undefined)payload.itemId=action.itemId;\r\n worldShopActionPending=true;\r\n try{\r\n  if(!await drainWorldFrames())return false;\r\n  if(worldBlocked||disposed||worldVisitId()!==visitId||state.phase!=='active'||state.pauseReasons.length)return false;\r\n  await worldClient.shopAction(visitId,payload);applyWorldEnvelope();\r\n  if(type==='open-door'&&state.shopDoorsOpened?.includes(node.id)){shopDoor?.dispose();shopDoor=null;render();}\r\n  return type!=='open-door'||state.shopDoorsOpened?.includes(node.id)===true;\r\n }catch(error){worldFailure(error);return false;}finally{worldShopActionPending=false;}\r\n}\r\nasync function openWorldTreasureChest(chestId){\r\n const node=currentRunRoom(state),run=activeRuntime?.treasure,visitId=worldVisitId(),runId=worldClient?.envelope?.runId;\r\n const chest=run?.chestManifest?.[0];\r\n if(!worldRunMode||node?.roomType!=='treasure_side_room'||!worldTreasureView||!run||!visitId||!runId||chest?.id!==chestId\r\n    ||run.status!=='arrived'||run.openedChests?.some(item=>item.chestId===chestId)||!state||state.phase!=='active'||state.stats.health<=0\r\n    ||state.pauseReasons.length||worldBlocked||worldCameraTransition||transition||disposed||worldTreasureOpenPending)return false;\r\n worldTreasureOpenPending=true;\r\n try{\r\n  if(!await drainWorldFrames())return false;\r\n  if(worldBlocked||disposed||worldClient.disposed||worldCameraTransition||transition||worldVisitId()!==visitId||worldClient.envelope?.runId!==runId\r\n     ||currentRunRoom(state)?.id!==node.id||state.phase!=='active'||state.stats.health<=0||state.pauseReasons.length\r\n     ||activeRuntime?.treasure?.status!=='arrived'||activeRuntime.treasure.openedChests?.some(item=>item.chestId===chestId))return false;\r\n  await worldClient.treasureOpen(visitId,chestId);applyWorldEnvelope();return true;\r\n }catch(error){worldFailure(error);return false;}finally{worldTreasureOpenPending=false;}\r\n}\r\nlet worldPresentedCue=null;\r\nfunction applyWorldEnvelope(){\r\n const envelope=worldClient?.envelope;if(!envelope)return;\r\n const previousPhase=state?.phase,previousVisit=worldAppliedVisitId;\r\n state=envelope.runState;activeRuntime=envelope.runtime;boss=activeRuntime?.boss??null;frameSeq=Math.max(frameSeq,(envelope.lastFrameSeq??-1)+1);worldStarted=true;\n showWorldLoadout(state);\n const nextVisit=envelope.visit?.visitId??envelope.currentVisitId??null;\r\n if(nextVisit!==previousVisit||state?.phase==='walk'&&previousPhase!=='walk'){\r\n  worldFrameBuffer=[];frameSeq=(envelope.lastFrameSeq??-1)+1;\r\n }\r\n worldAppliedVisitId=nextVisit;\r\n const node=currentRunRoom(state);\r\n if(node?.doorId==='dance'&&worldDanceOpening){if(worldDancePaused())worldDanceOpening.pause();else worldDanceOpening.resume();}\r\n if((node?.id??null)!==worldNodeId){worldNodeId=node?.id??null;movement=node?.type==='warmup'?new MovementSession(node.exerciseId):null;warmupEvidence=createWarmupEvidence();body=createPoseInput();jumping=new MovementSession('jumping');march=new MovementSession('march');jumpCount=marchCount=0;jumpUntil=jogUntil=0;punchHeld=false;configureWorldRoomView(node);}\r\n if(node?.doorId==='clay'&&activeRuntime&&!worldClayReady&&!worldClayOpening)beginWorldClayOpening();\r\n if((node?.doorId==='jelly'||node?.roomType==='coin_chase')&&activeRuntime)ensureMaterialGate();\r\n if(node?.roomType==='coin_chase'&&activeRuntime?.grass)syncWorldGrass(node);\r\n if(['clay','dance','jelly'].includes(node?.doorId)){\r\n  const revision=worldClient?.envelope?.runRevision??null;\r\n  if(activeRuntime?.arcade){\r\n   if(!worldArcadeView)mountWorldArcade(node);\r\n   if(node.doorId==='clay'&&activeRuntime.arcade.version!==2)throw Error('Clay World view requires an authored version 2 checkpoint.');\r\n   if(node.doorId==='dance'&&activeRuntime.arcade.version!==1)throw Error('Dance World view requires a server version 1 checkpoint.');\r\n   if(revision!==null&&revision!==worldArcadeRevision){worldArcadeView.update(activeRuntime.arcade);worldArcadeRevision=revision;}\r\n  }else if(worldArcadeView){worldArcadeView.dispose();worldArcadeView=null;worldArcadeInput=null;worldArcadeRevision=null;}\r\n }\r\n if(node?.roomType==='healing_waters'&&activeRuntime?.pond)pond=activeRuntime.pond;\r\n if(node?.roomType==='treasure_side_room'&&worldTreasureView){\r\n  const revision=worldClient?.envelope?.runRevision??null;\r\n  if(activeRuntime?.treasure&&(revision===null||revision!==worldTreasureRevision)){\r\n   worldTreasureView.update(activeRuntime.treasure,{active:state?.phase==='active'&&state.stats.health>0,paused:!!state.pauseReasons.length,confirmedBlueprints:worldClient?.envelope?.blueprints?.owned??[],identity:tourIdentity()});\r\n   worldTreasureRevision=revision;\r\n  }\r\n }\r\n ensureWorldShopDoor(node);\r\n ensureAuthoredView(node);syncAuthoredView();ensureWorldViewTour();\r\n syncRescueView();\r\n clearWorldPondLoading();\r\n $('tutorial-toggle').disabled=true;\r\n $('world-energy').textContent=`SAVED STAMINA · ${Math.floor((state?.energyGrantedMs??0)/1000)}s`;\r\n render();\r\n const rendered=boss?.cue?.name==='telegraph'?{phase:boss.phase,attackNo:boss.cue.attackNo,generation:activeRuntime?.presentationGeneration??0}:null;\r\n worldPresentedCue=null;requestAnimationFrame(()=>{if((activeRuntime?.presentationGeneration??0)===rendered?.generation&&boss?.phase===rendered?.phase&&boss?.cue?.attackNo===rendered?.attackNo)worldPresentedCue=rendered;});\r\n}\r\nfunction worldFailure(error){\r\n if(error?.code==='world_run_outbox_blocked'||error?.code==='auth_transition'||error?.code==='world_epoch_mismatch'||error?.code==='run_epoch_mismatch')worldBlocked=true;\r\n fault(error);if(worldBlocked)$('run-message').textContent='A saved event is unresolved. This run is blocked until server status resolves it.';\r\n}\r\nasync function inspectWorldRun(){\r\n $('opening-note').textContent='Complete rooms to recharge your harvesters.';\r\n $('begin').textContent='START FIELD RUN';$('resume').textContent='RESUME FIELD RUN';$('resume').hidden=true;\r\n $('signin').hidden=true;$('world-energy').hidden=false;$('end-run').hidden=true;\r\n $('begin').disabled=$('resume').disabled=true;\n try{\n  const account=globalThis.myr5AuthenticatedAccount;\n  if(!account?.user?.id||!Number.isSafeInteger(account.dataEpoch)||account.dataEpoch<1){\n   $('opening-note').textContent='Connecting to your account…';\n   const transitions=authTransitions(),ticket=transitions.capture(),response=await authFetch('/api/account?core=1');\n   if(!response.ok)throw Object.assign(Error('Your account could not be loaded.'),{status:response.status});\n   const restored=await response.json();transitions.assertCurrent(ticket);\n   if(!restored?.user?.id||!Number.isSafeInteger(restored.dataEpoch)||restored.dataEpoch<1)throw Error('Your account could not be confirmed. Reload to retry.');\n   globalThis.myr5AuthenticatedAccount=restored;\n  }\n  const current=await worldClient.current();$('resume').hidden=!current.envelope?.runId||current.envelope.runStatus!=='active';if(current.envelope?.runStatus==='active')showWorldLoadout(current.envelope.runState);\n  $('begin').disabled=$('resume').disabled=false;$('opening-note').textContent='Complete rooms to recharge your harvesters.';\n }\n catch(error){if(error?.status===401||error?.code==='world_run_signin_required'){$('opening-note').textContent='Sign in to restore your field run and banked rewards.';$('signin').hidden=false;$('begin').disabled=true;}\n  else{$('opening-note').textContent='Your saved run could not be checked.';fault(error);}}\r\n}\nfunction showWorldLoadout(run){\n if(!worldRunMode||!run?.loadout)return;\n const select=$('combat-class');select.value=run.loadout.styleId;select.disabled=true;\n const gear=classGear(run.loadout.styleId,run.combatGearTier??0);\n $('class-help').textContent=`Pinned weapon: ${run.loadout.weaponId.replaceAll('-',' ')}. Equipped: ${gear.name}. Choose a new loadout at base for your next run.`;\n}\nfunction showRunUpgradeChoices(){\n if(state?.rulesVersion!==2)return;\n for(const [choice,label,detail,icon] of [['left','DAMAGE',`Stand left · +${STAT_TUNING.damagePerLevel} damage`,'✊'],['right','DAMAGE',`Stand right · +${STAT_TUNING.damagePerLevel} damage`,'✊'],['crouch','LUCK LEVEL','Crouch · +1 level; no combat bonus','✦']]){\n  const option=document.querySelector(`[data-choice=\"${choice}\"]`);if(!option)continue;\n  option.querySelector('strong').textContent=label;option.querySelector('small').textContent=detail;option.querySelector('b').textContent=icon;\n }\n}\nasync function prepareWorldScene(node,scheduleVersion=state.schedule.scheduleVersion??1){\n const kind=authoredRoomKind(node);\r\n if(kind){\r\n  resetAuthoredView();cancelWorldViewTour();authoredSceneNode=null;\r\n  if(kind==='yoga'){\r\n   const coach=COACH_UNITS.find(unit=>unit.section==='yoga');\r\n   const prepared=await scene.prepareRoom({...node,coachId:coach.id,canonicalCoachId:undefined,biomeId:'yoga',exerciseKind:'hold'},{scheduleVersion:1});scene.commitRoom(prepared);\r\n  }else{scene.currentRoom?.dispose();scene.camera.position.set(0,2.1,9);scene.camera.lookAt(0,1.15,-1.2);}\r\n  authoredSceneNode=node.id;roomCoachId=null;roomCoachLabel=kind==='yoga'?'YOGA CLEARING':kind==='secret'?'QUILT VAULT':'TWO ALTARS';ensureAuthoredView(node);return;\r\n }\r\n if(node?.type==='recovery'){roomCoachId=null;roomCoachLabel='Recovery';return;}\r\n if(worldOptionalRoom(node)||['clay','dance','jelly','treasure'].includes(node?.doorId)){roomCoachId=null;roomCoachLabel=node?.doorId==='clay'?'SUBWAY BURPEES':node?.doorId==='dance'?GAMES.dance.name:node?.doorId==='jelly'?'COVER CRISIS':node?.doorId==='treasure'?'TREASURE CHASE':node.coachLabel??'';return null;}\r\n const prepared=await scene.prepareRoom(node,{scheduleVersion,...(scheduleVersion===4&&activeRuntime?.nodeId===node.id?{presentationCoachId:activeRuntime.rescuePresentation?.presentationCoachId}:{})});scene.commitRoom(prepared);\r\n roomCoachId=prepared.coachDefinition.id;roomCoachLabel=prepared.coachDefinition.label;return prepared;\r\n}\r\nasync function enterWorldRoom(classId){\r\n if(state.rulesVersion===2)classId=undefined;\r\n if(classId===undefined)await worldClient.enter(worldVisitId(),performance.now());\r\n else await worldClient.enter(worldVisitId(),performance.now(),classId);\r\n applyWorldEnvelope();\r\n if(state.schedule.scheduleVersion===4&&activeRuntime?.rescuePresentation?.presentationCoachId==='myr5')await prepareWorldScene(currentRunRoom(state),4);\r\n if(state.phase==='active')await playWorldIntro();\r\n}\r\nasync function openWorldRun(resume){\r\n $('begin').disabled=$('resume').disabled=true;$('error').hidden=true;worldBlocked=false;worldUnsupported=false;worldFrameBuffer=[];\r\n try{\r\n  const envelope=resume?await worldClient.resume():await worldClient.begin($('tutorial-toggle').getAttribute('aria-pressed')==='true',window.myr5RunLoadout??{styleId:$('combat-class').value,weaponId:classGear($('combat-class').value,0).id,relicIds:[]});\r\n  state=envelope.runState;activeRuntime=envelope.runtime;worldStarted=true;frameSeq=(envelope.lastFrameSeq??-1)+1;\r\n  const node=currentRunRoom(state);\r\n  if(!worldSupportedRoom(node)){worldUnsupported=true;$('run-message').textContent='This saved room could not be loaded. End the run to return to base.';}\r\n  else{\r\n   await prepareWorldScene(node,state.schedule.scheduleVersion??1);\r\n   body=createPoseInput();jumping=new MovementSession('jumping');march=new MovementSession('march');jumpCount=marchCount=0;jumpUntil=jogUntil=0;punchHeld=false;\r\n   movement=node.type==='warmup'?new MovementSession(node.exerciseId):null;warmupEvidence=createWarmupEvidence();\r\n   $('opening').hidden=true;$('hud').hidden=false;$('end-run').hidden=false;$('world-energy').hidden=false;\r\n   if(!testMode)await acquireCamera(roomCameraMode(node));\r\n   if(!activeRuntime&&['miniboss','mainboss'].includes(node.type))await confirmWorldBossEntry(node);\r\n   if(state.phase==='active'&&!activeRuntime){if(state.cursor===0)await enterWorldRoom($('combat-class').value);else await enterWorldRoom();}\r\n   worldFlushTimer=setInterval(()=>{if(worldFrameBuffer.length)void flushWorldFrames();},200);\r\n  }\r\n  $('opening').hidden=true;$('hud').hidden=false;$('end-run').hidden=false;$('world-energy').hidden=false;\r\n  $('room-label').textContent='FIELD RUN';\r\n  if(worldUnsupported){}else if(state.phase==='tutorial'||state.phase==='walk'){}else if(!activeRuntime)throw new Error('The server has not entered this visit.');\r\n  applyWorldEnvelope();if(!worldTreasureView&&authoredGate?.kind!=='altar')scene.update(0,{paused:true});\r\n }catch(error){worldFailure(error);}finally{$('begin').disabled=$('resume').disabled=false;}\r\n}\r\nasync function confirmWorldBossEntry(node){\r\n bossDoor=await mountBossDoor(stage,node,{scheduleVersion:state.schedule.scheduleVersion??1});\r\n try{await bossDoor.confirmation;}finally{bossDoor?.dispose();bossDoor=null;}\r\n}\r\nasync function finishWorldTutorial(){\r\n worldCameraTransition=true;\r\n try{await worldClient.tutorial(worldVisitId());applyWorldEnvelope();\r\n  if(!activeRuntime)await enterWorldRoom(state.cursor===0?$('combat-class').value:undefined);\r\n  if(!testMode){await worldClient.pause(worldVisitId(),'tracking',true);applyWorldEnvelope();}\r\n  const node=currentRunRoom(state);movement=node?.type==='warmup'?new MovementSession(node.exerciseId):null;warmupEvidence=createWarmupEvidence();\r\n  if(!testMode&&cameraMode!==roomCameraMode(node))await acquireCamera(roomCameraMode(node));\r\n }catch(error){worldFailure(error);}finally{worldCameraTransition=false;}\r\n}\r\nfunction flushWorldFrames(){\n if(worldFlushPromise)return worldFlushPromise;\n let sent=false;\n worldFlushPromise=flushWorldFrameBatch().then(result=>{sent=result;return result;}).finally(()=>{\n  worldFlushPromise=null;\n  if(sent&&worldStarted&&!worldBlocked&&activeRuntime&&!disposed&&!worldClient.disposed&&worldFrameBuffer.length)void flushWorldFrames();\n });\n return worldFlushPromise;\r\n}\r\nasync function flushWorldFrameBatch(){\r\n if(!worldStarted||worldBlocked||!activeRuntime||disposed)return false;\r\n if(!worldFrameBuffer.length)return true;\r\n const frames=worldFrameBuffer.splice(0,32),visitId=worldVisitId();if(!visitId){worldBlocked=true;fault(Error('The current server visit is missing.'));return false;}\r\n try{await worldClient.frames(visitId,frames);applyWorldEnvelope();\r\n  if(state.phase==='walk'&&currentRunRoom(state)?.type==='warmup'){worldUnsupported=false;worldFrameBuffer=[];}\r\n  if(state.energyGrantedMs)$('world-energy').textContent=`SAVED STAMINA · ${Math.floor(state.energyGrantedMs/1000)}s`;\r\n  return true;\r\n }catch(error){if(!worldClient.disposed)worldFrameBuffer.unshift(...frames);worldFailure(error);return false;}\r\n}\r\nfunction collectWorldSample({now,tracked,sourceTs,quality,value=0,choice=null,bossInput=null,pondInput:pondObservation=null,grassInput:grassObservation=null,arcadeInput:arcadeObservation=null,treasureInput:treasureObservation=null,yogaInput=null,secretInput=null,altarInput=null,rescueInput=null}){\r\n if(!worldStarted||worldBlocked||!activeRuntime||worldCameraTransition||document.hidden||transition||!['active','upgrade'].includes(state.phase))return false;\r\n const node=currentRunRoom(state),bossRoom=['miniboss','mainboss'].includes(node?.type),pondRoom=node?.roomType==='healing_waters';\r\n // Shop commits must drain a finite queue. Raw hand observations still reach\r\n // the entrance while its ACK is pending, but cannot replenish that queue.\r\n if(node?.roomType==='corgi_shop'&&worldShopActionPending)return false;\r\n if(node?.doorId==='dance'&&!worldDanceReady)return false;\r\n if(node?.doorId==='clay'&&!worldClayReady)return false;\r\n if((node?.doorId==='jelly'||node?.roomType==='coin_chase')&&!materialGateReady())return false;\r\n if(!worldSupportedRoom(node)){worldUnsupported=true;$('run-message').textContent='This saved room could not be loaded. End the run to return to base.';return false;}\r\n const sample={op:'sample',frameSeq:frameSeq++,now,tracked,value,choice};\r\n if(state.rulesVersion===2){if(Number.isFinite(sourceTs))sample.sourceTs=sourceTs;sample.quality=quality??{body:false,hands:false};sample.cueAck=worldPresentedCue;}\r\n if(bossRoom)sample.boss=bossInput??{left:false,punchLeft:false,punchRight:false,crouch:false,jump:false,jog:false};\r\n if(pondRoom)sample.pond={balanced:pondObservation?.balanced===true,handOut:pondObservation?.handOut===true};\r\n if(node?.roomType==='coin_chase')sample.grass={controlValid:grassObservation?.controlValid===true,exerciseEligible:grassObservation?.exerciseEligible===true,height01:Number.isFinite(grassObservation?.height01)?grassObservation.height01:.5,steer:Number.isFinite(grassObservation?.steer)?Math.max(-1,Math.min(1,grassObservation.steer)):0};\r\n if(node?.doorId==='jelly')sample.arcade=arcadeObservation??{hidden:false,side:0,beam:{on:false,x:0},zones:{left:0,middle:0,right:0,up:0},fires:[]};\r\n if(node?.doorId==='clay')sample.arcade=arcadeObservation??{actions:[]};\r\n if(node?.doorId==='dance')sample.arcade=arcadeObservation??{energy:0,coverage:0};\r\n if(node?.roomType==='treasure_side_room')sample.treasure={jogging:treasureObservation?.jogging===true};\r\n const kind=authoredRoomKind(node);\r\n if(kind)sample[kind]=normalizeAuthoredSample(kind,kind==='yoga'?yogaInput??{push:false,pose:null,eligible:false}:kind==='secret'?secretInput??{handsLow:false,pinching:false,pullUp:false,jogging:false,action:null,choiceId:null}:altarInput??{target:null,gesture:'none',y:null,handId:null});\r\n if(rescueInput)sample.rescue=rescueInput;\r\n worldFrameBuffer.push(sample);\r\n if(worldFrameBuffer.length>=8)void flushWorldFrames();\r\n return true;\r\n}\r\nasync function handleWorldInput(input,{flush=false}={}){\r\n if(secretActionPending&&!input.secretInput?.action)return false;\r\n if(activeRuntime?.rescue){\r\n  const rescue=input.rescueInput??input.rescue;\r\n  if(!rescue)return false;\r\n  const accepted=collectWorldSample({...input,rescueInput:rescue});return accepted&&flush?flushWorldFrames():accepted;\r\n }\r\n if(worldViewTour&&!worldViewTour.done){\r\n  const lost=!input.tracked;if(!worldIntroTrackingPending&&state.pauseReasons.includes('tracking')!==lost){worldIntroTrackingPending=true;try{await worldClient.pause(worldVisitId(),'tracking',lost);applyWorldEnvelope();}finally{worldIntroTrackingPending=false;}}\r\n  return true;\r\n }\r\n if(worldIntro){const lost=!input.tracked;if(!worldIntroTrackingPending&&state.pauseReasons.includes('tracking')!==lost){worldIntroTrackingPending=true;try{await worldClient.pause(worldVisitId(),'tracking',lost);applyWorldEnvelope();}finally{worldIntroTrackingPending=false;}}return true;}\r\n const bossInput=input.bossInput??input.boss??null,punched=!!(bossInput?.punchLeft||bossInput?.punchRight),edge=punched&&!punchHeld;\r\n punchHeld=punched;\r\n if(routeDoor){routeDoor.observe({observationId:worldDoorObservation++,now:input.now,tracked:!!input.tracked,choice:input.choice,punch:punched,paused:!!state.pauseReasons.length});return true;}\r\n if(edge&&bossDoor&&input.tracked&&!state.pauseReasons.length){bossDoor.confirm();return true;}\r\n if(state.phase==='tutorial'){if(edge&&!state.pauseReasons.length)tutorial?.advance();return true;}\r\n const node=currentRunRoom(state);let frameInput={...input,bossInput};\r\n if((node?.doorId==='jelly'||node?.roomType==='coin_chase')&&observeMaterialGate(input))return true;\r\n if(node?.type==='warmup'){\r\n  const motion=input.points?movement?.update(input.points,input.now,input.aspect):null;let value=input.value??0,tracked=input.tracked&&(!movement||!input.points||!!motion?.tracking);\r\n  if(motion){const evidence=observeWarmupEvidence(warmupEvidence,{node,motion,now:input.now,tracked,paused:state.phase!=='active'||!!state.pauseReasons.length});warmupEvidence=evidence.state;value=evidence.value;}\r\n  frameInput={...frameInput,tracked,value};\r\n }else if(['miniboss','mainboss'].includes(node?.type)){\r\n  frameInput.bossInput={left:!!bossInput?.left,punchLeft:!!bossInput?.punchLeft,punchRight:!!bossInput?.punchRight,crouch:!!bossInput?.crouch,jump:!!bossInput?.jump,jog:!!bossInput?.jog,...(state.combatClass?normalizeClassInput(bossInput??{}):{})};\r\n  if(state.combatClass)lastCombatInput=frameInput.bossInput;\r\n }else if(authoredRoomKind(node)){\r\n  if(!authoredGate?.released)return false;\r\n  frameInput={...frameInput,yogaInput:input.yogaInput??input.yoga,secretInput:input.secretInput??input.secret,altarInput:input.altarInput??input.altar};\r\n }else if(node?.roomType==='healing_waters'){\r\n  frameInput.pondInput=input.pondInput??pondInput??{balanced:false,handOut:false};\r\n }else if(node?.roomType==='corgi_shop'){\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null};\r\n }else if(node?.roomType==='coin_chase'){\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null,grassInput:input.grassInput??input.grass??{controlValid:false,height01:.5,steer:0}};grassPose=frameInput.grassInput;\r\n }else if(node?.doorId==='jelly'){\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null,arcadeInput:input.arcadeInput??input.arcade??{hidden:false,side:0,beam:{on:false,x:0},zones:{left:0,middle:0,right:0,up:0},fires:[]}};\r\n }else if(node?.doorId==='clay'){\r\n  worldClayPoseTracked=(input.clayOpeningTracked??input.tracked)===true;\r\n  if(worldClayOpening&&worldClayLoadingActive){\r\n   const paused=worldClayPaused();\r\n   if(paused)worldClayOpening.pause();else worldClayOpening.resume();\r\n   if(worldClayPoseTracked&&state.pauseReasons.includes('tracking')&&!worldClayLoadingPending&&worldClayPaused(true)===false){void resumeWorldClayTracking();return true;}\r\n   if(!worldClayLoadingPending&&!worldClayTrackingPending)worldClayOpening.observe({now:input.now,tracked:worldClayPoseTracked,claw:input.clayOpeningInput?.claw===true,drink:input.clayOpeningInput?.drink===true},{paused});\r\n  }\r\n  if(!worldClayReady)return true;\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null,arcadeInput:input.arcadeInput??input.arcade??{actions:[]}};\r\n }else if(node?.doorId==='dance'){\r\n  worldDancePoseTracked=input.tracked===true;\r\n  if(worldDanceOpening&&worldDanceLoadingActive){\r\n   const paused=worldDancePaused();\r\n   if(paused)worldDanceOpening.pause();else worldDanceOpening.resume();\r\n   if(input.tracked&&state.pauseReasons.includes('tracking')&&worldDanceLoadingPending===false&&worldDancePaused(true)===false){void resumeWorldDanceTracking();return true;}\r\n   if(input.tracked&&Array.isArray(input.points)&&!worldDanceLoadingPending&&!worldDanceTrackingPending)worldDanceOpening.observe({now:input.now,tracked:true,points:input.points,mirrored:true},{paused});\r\n  }\r\n  if(!worldDanceReady)return true;\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null,arcadeInput:input.arcadeInput??input.arcade??{energy:0,coverage:0}};\r\n }else if(node?.roomType==='treasure_side_room'){\r\n  const treasure=input.treasureInput??input.treasure;\r\n  frameInput={now:input.now,tracked:!!input.tracked,value:0,choice:null,treasureInput:{jogging:treasure?treasure.jogging===true:input.jog===true}};\r\n }else{worldUnsupported=true;render();return false;}\r\n const accepted=collectWorldSample({...frameInput,sourceTs:input.sourceTs,quality:input.quality});\r\n if(edge&&input.tracked&&accepted&&boss?.status==='finisher'){await finishWorldBossChip();return true;}\r\n return flush?flushWorldFrames():true;\r\n}\r\nasync function finishWorldBossChip(){\r\n if(state?.schedule?.scheduleVersion===4){await beginWorldRescue();return;}\r\n if(!worldRunMode||worldBlocked||worldCameraTransition||transition||disposed||worldClient.disposed||!state||state.phase!=='active'||state.stats.health<=0||state.pauseReasons.length||!boss||boss.status!=='finisher')return;\r\n const visitId=worldVisitId(),finalFrameSeq=frameSeq-1;if(!visitId)return;\r\n markWorldFinish('physical_edge_finish_entered');\r\n worldCameraTransition=true;\r\n try{\r\n  while(worldFlushPromise||worldFrameBuffer.length){\r\n   const pending=worldFlushPromise,before=worldFrameBuffer.length,sent=pending?await pending:await flushWorldFrames();\r\n   if(!sent||worldBlocked||disposed||worldClient.disposed||!pending&&worldFrameBuffer.length>=before&&before>0)return;\r\n  }\r\n  if(worldBlocked||disposed||worldClient.disposed||worldClient.envelope?.runStatus!=='active'||state.pauseReasons.length||state.phase!=='active'||state.stats.health<=0||boss?.status!=='finisher'||worldVisitId()!==visitId)return;\r\n  if(finalFrameSeq>=0&&(worldClient.envelope?.lastFrameSeq??-1)<finalFrameSeq)return;\r\n  markWorldFinish('frames_drained_finish_enqueue');\r\n  await worldClient.finishBoss(visitId);applyWorldEnvelope();\r\n }\r\n catch(error){worldFailure(error);}finally{worldCameraTransition=false;render();}\r\n}\r\nasync function advanceWorldRoom(){\r\n if(rescueView&&rescueAcceptedAt!==null&&performance.now()-rescueAcceptedAt<(reduced?2000:5200))return;\r\n if(transition||worldBlocked||state?.phase!=='walk'||state.pauseReasons.length)return;transition=true;\r\n try{\r\n  if(!await drainWorldFrames()||worldBlocked)return;\r\n  cancelWorldViewTour();resetAuthoredView();resetRescueView();worldTreasureView?.dispose();worldTreasureView=null;\r\n  if(shopView){shopView.setActive(false);shopDrawing=false;scene.resize();}\r\n  await scene.walkOut({});\r\n  const offers=runRouteOffers(state,{roomIds:['pond','grass','papier_mache','clay','dance','jelly','treasure',...(state.schedule.scheduleVersion===4?['wood','quilt','flat_paper']:[])]});\n  if(offers.length){\r\n   routeDoor=mountDoorChoices(stage,offers);const offer=await routeDoor.confirmation;routeDoor.dispose();routeDoor=null;\r\n   await worldClient.routeChosen(worldVisitId(),offer.id==='training'?null:offer.id);applyWorldEnvelope();\r\n  }\r\n  const nextNode=state.schedule.nodes[state.cursor+1];\r\n  if(nextNode&&!worldSupportedRoom(nextNode)){worldUnsupported=true;$('run-message').textContent='This saved room could not be loaded. End the run to return to base.';render();return;}\r\n  await worldClient.advance(worldVisitId());applyWorldEnvelope();\r\n  if(!nextNode){\r\n   if(worldClient.envelope?.runStatus==='completed'&&state.phase==='complete'){\r\n    clearInterval(worldFlushTimer);worldFlushTimer=null;tracker?.stop();$('hud').hidden=true;$('summary').hidden=false;$('summary').querySelector('h1').textContent='RUN COMPLETE';\r\n    $('summary-text').textContent=`${state.acceptedQualifyingUnits??0} qualifying rooms · ${Math.floor((state.energyGrantedMs??0)/1000)} seconds of harvester stamina saved.`;$('return-base').hidden=false;\r\n   }else{worldUnsupported=true;$('run-message').textContent='The server has no next supported room. End the run to return to base.';}\r\n   return;\r\n  }\r\n  const node=currentRunRoom(state);await prepareWorldScene(node,state.schedule.scheduleVersion??1);\r\n  if(['miniboss','mainboss'].includes(node.type))await confirmWorldBossEntry(node);\r\n  await enterWorldRoom();\r\n  if(!testMode)await acquireCamera(roomCameraMode(node));\r\n  movement=node.type==='warmup'?new MovementSession(node.exerciseId):null;warmupEvidence=createWarmupEvidence();body=createPoseInput();jumping=new MovementSession('jumping');march=new MovementSession('march');jumpCount=marchCount=0;jumpUntil=jogUntil=0;punchHeld=false;worldUnsupported=false;\r\n }catch(error){worldFailure(error);}finally{transition=false;}\r\n}\r\nasync function endWorldRun(){\r\n if(!worldStarted||worldBlocked)return;\r\n $('end-run').disabled=true;\r\n try{await flushWorldFrames();await worldClient.end();applyWorldEnvelope();clearInterval(worldFlushTimer);worldFlushTimer=null;\r\n  tracker?.stop();$('hud').hidden=true;$('summary').hidden=false;$('summary').querySelector('h1').textContent='RUN ENDED';\r\n  $('summary-text').textContent=`${Math.floor((state.energyGrantedMs??0)/1000)} seconds of harvester stamina saved.`;$('return-base').hidden=false;\r\n }catch(error){worldFailure(error);}finally{$('end-run').disabled=false;}\r\n}\r\nasync function start(resume=false){\r\n if(worldRunMode){await openWorldRun(resume);return;}\r\n $('begin').disabled=$('resume').disabled=true;$('error').hidden=true;\r\n try{\r\n  let resumedRuntime=null;\r\n  if(resume){\r\n   const data=saved(),{activeBoss,activeGrass,activePond,activeRuntime:storedRuntime,...runData}=data;\r\n   const node=currentRunRoom(runData);\r\n   if(node&&node.roomType!=='coin_chase'){\r\n    const resumed=resumePage({runState:runData,runtime:legacyRuntimeFor(data,node),now:performance.now(),accountId:owner});\r\n    state=resumed.runState;resumedRuntime=resumed.runtime;\r\n   }else{\r\n    // Legacy grass uses its original restore and live simulation, never pure tape stepping.\r\n    state=restoreRunSession(runData,{accountId:owner});\r\n    for(const reason of ['tracking','background','camera-handoff','loading'])state=setRunPaused(state,reason,false);\r\n   }\r\n   savedGrass=activeGrass??null;boss=null;\r\n  }else{\r\n   const options={runId:crypto.randomUUID(),accountId:owner,seed:Date.now(),exerciseIds:chosenExercises(),combatClass:$('combat-class').value,tutorialsEnabled:$('tutorial-toggle').getAttribute('aria-pressed')==='true'};\r\n   const prior=saved();if(prior){delete prior.activeBoss;delete prior.activeGrass;delete prior.activePond;delete prior.activeRuntime;}\r\n   state=prior?createNextRunSession(prior,options):createRunSession(options);boss=null;savedGrass=null;\r\n  }\r\n  sequence=Math.max(state.lastObservation,resumedRuntime?.boss?.lastObsId??-1,resumedRuntime?.pond?.lastObservationId??-1)+1;\r\n  if(resumedRuntime)sequence=Math.max(sequence,resumedRuntime.obsHighWater);\r\n  frameSeq=0;\r\n  if(currentRunRoom(state)){const prepared=await scene.prepareRoom(currentRunRoom(state),{scheduleVersion:state.schedule.scheduleVersion??1});roomCoachId=prepared.coachDefinition.id;roomCoachLabel=prepared.coachDefinition.label;scene.commitRoom(prepared);}\r\n  configureRoom(resumedRuntime);\r\n  $('opening').hidden=true;$('hud').hidden=false;\r\n  save(true);render();scene.update(0,{paused:true});\r\n }catch(error){fault(error);}finally{$('begin').disabled=$('resume').disabled=false;}\r\n}\r\nconst pondArmAngle=(a,b,c,aspect)=>jointAngle(...[a,b,c].map(p=>({x:p.x*aspect,y:p.y,z:0})));\r\nfunction controls(raw,now){\r\n const points=raw.pose?.[0],aspect=video.videoWidth/Math.max(1,video.videoHeight)||1;\r\n body.update({points,width:aspect,height:1,now});\r\n const visible=points&&[0,11,12,23,24].every(i=>points[i]?.visibility>=.5);\r\n const center=visible?(points[11].x+points[12].x)/2:.5;\r\n const crouch=!!body.body.crouching;\r\n const choice=crouch?'crouch':center>.64?'left':center<.36?'right':null;\r\n const jump=jumping.update(points,now,aspect),run=march.update(points,now,aspect);\r\n if(jump.count>jumpCount)jumpUntil=now+250;if(run.count>marchCount)jogUntil=now+450;\r\n jumpCount=jump.count;marchCount=run.count;\r\n const gestures=combatInput.observe(raw,{classId:state?.combatClass??$('combat-class').value,left:choice==='left',right:choice==='right',crouch,jump:now<jumpUntil,jog:now<jogUntil,aspect});\r\n lastCombatInput=gestures;\r\n return {points,aspect,tracked:!!visible&&Number.isFinite(raw.ts)&&performance.now()-raw.ts<=300,choice,...gestures};\r\n}\r\nfunction rawPose(raw){\r\n if(!state||disposed)return;lastRaw=raw.ts;\r\n const input={...controls(raw,raw.ts),materialHands:raw.landmarks??[],materialWorldHands:raw.worldLandmarks??[]};\r\n if(worldRunMode){\r\n  const node=currentRunRoom(state);let pondObservation=null,arcadeObservation=null;\r\n  const kind=authoredRoomKind(node);let yogaObservation=null,secretObservation=null;\r\n  if(kind==='yoga'){\r\n   const pose=['mountain','salute','warrior'][activeRuntime?.authored?.state?.stageIndex]??null,motion=pose?yogaMovements[pose]?.update(input.points,raw.ts,input.aspect):null;\r\n   const push=detectMaterialOpeningGesture(input.points,input.tracked,{hands:raw.landmarks??[],worldHands:raw.worldLandmarks??[],aspect:input.aspect});\r\n   yogaObservation={push:push.push===true,pose,eligible:!!motion?.tracking&&motion.progress===1&&motion.hold>0};authoredPose=yogaObservation;\r\n  }\r\n  if(kind==='secret'){\r\n   const hands=raw.landmarks??[],index=hands.findIndex(hand=>hand?.length===21),hand=hands[index],shape=hand?classifyHand(hand,raw.worldLandmarks?.[index],input.aspect):'none',pinching=shape==='pinch',y=hand?.[9]?.y;\r\n   const fresh=secretHand&&raw.ts>secretHand.ts&&raw.ts-secretHand.ts<=250&&Math.hypot((hand?.[0]?.x??10)-secretHand.x,(hand?.[0]?.y??10)-secretHand.y)<.2;\r\n   const origin=pinching&&fresh?secretHand.origin:y;\r\n   secretObservation={handsLow:!!hand&&y>.65,pinching,pullUp:pinching&&Number.isFinite(origin)&&origin-y>=.15,jogging:input.jog===true,action:null,choiceId:null};\r\n   secretHand=pinching&&hand?{ts:raw.ts,x:hand[0].x,y:hand[0].y,origin}:null;\r\n  }\r\n  if(kind){\r\n   input.quality={body:input.tracked===true,hands:(raw.landmarks??[]).some(hand=>hand?.length===21&&hand.every(point=>Number.isFinite(point.x)&&Number.isFinite(point.y)))};\r\n   authoredLastRaw={sourceTs:raw.ts,tracked:input.tracked,quality:input.quality};\r\n  }\r\n  const queuedSecret=kind==='secret'?secretQueuedAction:null;\r\n  if(queuedSecret){secretQueuedAction=null;secretObservation={...secretObservation,action:queuedSecret.action.action,choiceId:queuedSecret.action.choiceId??null};}\r\n  if(node?.roomType==='healing_waters'){\r\n   const balance=pondBalance.update(input.points,raw.ts,input.aspect);\r\n   if(pond?.status!=='opening')input.tracked=input.tracked&&balance.tracking;\r\n   const open=(raw.landmarks||[]).some((hand,i)=>classifyHand(hand,raw.worldLandmarks?.[i],input.aspect)==='open');\r\n   const reaching=(s,e,w)=>input.tracked&&input.points?.[w]?.visibility>=.5&&input.points?.[e]?.visibility>=.5&&pondArmAngle(input.points[s],input.points[e],input.points[w],input.aspect)>145&&Math.abs(input.points[w].y-input.points[s].y)<.35;\r\n   const palm=raw.landmarks?.find((hand,i)=>classifyHand(hand,raw.worldLandmarks?.[i],input.aspect)==='open')?.[9];\r\n   pondObservation={balanced:balance.tracking&&balance.progress===1&&balance.hold>0,handOut:open&&(reaching(11,13,15)||reaching(12,14,16)),pointer:palm?{x:1-palm.x,y:palm.y}:null};pondInput=pondObservation;\r\n  }\r\n  const grassObservation=node?.roomType==='coin_chase'?(grassPose=plank.update({points:input.points,width:input.aspect,height:1,now:raw.ts})):null;\r\n  const clayGateGesture=node?.doorId==='clay'?detectClayOpeningGesture({hands:raw.landmarks??[],pose:input.points,aspect:input.aspect}):null;\r\n  if(['clay','dance','jelly'].includes(node?.doorId))arcadeObservation=worldArcadeInput?.update({points:input.points,world:raw.worldPose?.[0]??null,width:input.aspect,height:1,mirrored:true,now:raw.ts,tracked:input.tracked,paused:state.phase!=='active'||!!state.pauseReasons.length});\r\n  const clayOpeningTracked=input.tracked&&Number.isFinite(raw.ts)&&performance.now()-raw.ts<=250;\r\n  void handleWorldInput({...input,now:performance.now(),sourceTs:raw.ts,quality:input.quality,yogaInput:yogaObservation,secretInput:secretObservation,pondInput:pondObservation,grassInput:grassObservation,arcadeInput:arcadeObservation?.arcade,clayOpeningTracked,clayOpeningInput:clayGateGesture,treasureInput:{jogging:input.jog},bossInput:{left:input.left,punchLeft:input.punchLeft,punchRight:input.punchRight,crouch:input.crouch,jump:input.jump,jog:input.jog,...normalizeClassInput(input)}},{flush:!!queuedSecret}).then(ok=>{\r\n   if(queuedSecret){if(ok&&currentAuthoredGate(queuedSecret.gate)&&activeRuntime?.authored?.state.stage==='done')queuedSecret.resolve(true);else queuedSecret.reject(Error('Choice needs a fresh tracked frame. Try again.'));}\r\n  }).catch(error=>{queuedSecret?.reject(error);worldFailure(error);});\r\n  return;\r\n }\r\n if(state.phase==='active'&&materialForRoom()&&!materialGateReady()){\r\n  state=setTracking(state,input.tracked);observeMaterialGate({...input,now:raw.ts});render();return;\r\n }\r\n if(grass){grassPose=plank.update({points:input.points,width:input.aspect,height:1,now:raw.ts});if(state.phase==='active')input.tracked=!!grassPose.controlValid;}\r\n state=setTracking(state,input.tracked);\r\n const punched=input.punchLeft||input.punchRight,edge=punched&&!punchHeld;punchHeld=punched;\r\n if(routeDoor){routeDoor.observe({observationId:sequence++,now:raw.ts,tracked:input.tracked,choice:input.choice,punch:punched,paused:!!state.pauseReasons.length});return;}\r\n if(edge&&bossDoor&&input.tracked&&!state.pauseReasons.length){bossDoor.confirm();return;}\r\n if(edge&&boss&&!reduced)$('held-weapon').animate([{transform:'rotate(-18deg) translateY(0)'},{transform:'rotate(-4deg) translateY(-45px)'},{transform:'rotate(-18deg) translateY(0)'}],{duration:220});\r\n if(state.phase==='tutorial'){\r\n  // Keep exercising clocks still while the hand demonstrates the acknowledgement.\r\n  if(edge&&input.tracked&&!state.pauseReasons.length)tutorial?.advance();\r\n  return;\r\n }\r\n const motion=movement?.update(input.points,raw.ts,input.aspect);\r\n let pondTracked=input.tracked;\r\n if(pond){\r\n  const balance=pondBalance.update(input.points,raw.ts,input.aspect);\r\n  if(pond.status!=='opening')pondTracked=pondTracked&&balance.tracking;\r\n  const open=(raw.landmarks||[]).some((hand,i)=>classifyHand(hand,raw.worldLandmarks?.[i],input.aspect)==='open');\r\n  const reaching=(s,e,w)=>input.tracked&&input.points[w]?.visibility>=.5&&input.points[e]?.visibility>=.5&&pondArmAngle(input.points[s],input.points[e],input.points[w],input.aspect)>145&&Math.abs(input.points[w].y-input.points[s].y)<.35;\r\n  const palm=raw.landmarks?.find((hand,i)=>classifyHand(hand,raw.worldLandmarks?.[i],input.aspect)==='open')?.[9];\r\n  pondInput={balanced:balance.tracking&&balance.progress===1&&balance.hold>0,handOut:open&&(reaching(11,13,15)||reaching(12,14,16)),pointer:palm?{x:1-palm.x,y:palm.y}:null};\r\n }\r\n let value=0;\r\n if(motion){const evidence=observeWarmupEvidence(warmupEvidence,{node:currentRunRoom(state),motion,now:raw.ts,tracked:input.tracked&&motion.tracking,paused:state.phase!=='active'||!!state.pauseReasons.length});warmupEvidence=evidence.state;value=evidence.value;}\r\n const localArcade=currentRunRoom(state)?.doorId==='jelly'?worldArcadeInput?.update({points:input.points,world:raw.worldPose?.[0]??null,width:input.aspect,height:1,mirrored:true,now:raw.ts,tracked:input.tracked,paused:state.phase!=='active'||!!state.pauseReasons.length}):null;\r\n sample({now:raw.ts,tracked:pondTracked&&(!movement||motion.tracking),value,choice:input.choice,bossInput:input,grassInput:grassPose,pondInput,arcadeInput:localArcade?.arcade,points:input.points});\r\n if(edge&&boss?.status==='finisher'){\r\n  finishBossChip();\r\n }\r\n render();\r\n}\r\nfunction finishBossChip(){\r\n const result=finishBossChipState(state,boss);\r\n if(!result.applied)return result;\r\n state=result.state;boss=result.boss;save(true);render();return result;\r\n}\r\nfunction setTracking(current,tracked){\r\n const paused=current.pauseReasons.includes('tracking');\r\n return paused===!tracked?current:setRunPaused(current,'tracking',!tracked);\r\n}\r\nfunction sample({now,tracked,value=0,choice=null,bossInput={},grassInput=null,pondInput:pondObservation=null,arcadeInput=null,points=null,materialOpeningInput=null}){\r\n if(!state)return;\r\n const hidden=document.hidden;if(state.pauseReasons.includes('background')!==hidden)state=setRunPaused(state,'background',hidden);\r\n if(state.phase==='active'&&materialForRoom()&&!materialGateReady()){\r\n  state=setTracking(state,tracked);observeMaterialGate({now,tracked,points,materialOpeningInput});return;\r\n }\r\n const before=state.phase,oldBoss=boss;\r\n if(pond)pondInput=pondObservation??{balanced:false,handOut:false};\r\n if(grass){\r\n  // Partial consumer seam: grass remains on its persistent live reducer; do not rehydrate its snapshot per frame.\r\n  state=setTracking(state,tracked);state=observeRunRoom(state,{observationId:sequence++,now,tracked,value,choice});\r\n  if(grassInput)grassPose=grassInput;const dt=grassLastNow===null?0:Math.max(0,(now-grassLastNow)/1000);grassLastNow=now;\r\n  grass.advance(dt,{valid:tracked&&!!grassInput?.controlValid,height01:grassInput?.height01??.5,steer:Math.max(-1,Math.min(1,grassInput?.steer??0)),paused:state.phase!=='active'||!!state.pauseReasons.length});\r\n  if(state.phase==='active'&&!state.pauseReasons.length&&grass.chase.status!=='running')state=resolveRunGame(state,grass.chase);\r\n  state=settleRunHealth(state);if(before!==state.phase)save(true);else save();return;\r\n }\r\n if(!activeRuntime||activeRuntime.nodeId!==currentRunRoom(state)?.id)throw Error('The active room runtime is missing.');\r\n // Rebuild the bridge from live view state each frame: pause/shop handlers and the chip finisher mutate these independently.\r\n activeRuntime.boss=boss?structuredClone(boss):null;activeRuntime.pond=pond?structuredClone(pond):null;\r\n activeRuntime.shopLastNow=shopLastNow;\r\n activeRuntime.obsHighWater=Math.max(activeRuntime.obsHighWater,sequence,state.lastObservation+1,(boss?.lastObsId??-1)+1,(pond?.lastObservationId??-1)+1);\r\n const result=stepFrame({runState:state,runtime:activeRuntime,frame:{op:'sample',frameSeq:frameSeq++,now,tracked,value,choice,boss:bossInput,pond:pondObservation??{balanced:false,handOut:false},...(currentRunRoom(state)?.doorId==='jelly'?{arcade:arcadeInput??{hidden:false,side:0,beam:{on:false,x:0},zones:{left:0,middle:0,right:0,up:0},fires:[]}}:{})},env:{background:document.hidden}});\r\n state=result.runState;activeRuntime=result.runtime;sequence=activeRuntime.obsHighWater;\r\n if(currentRunRoom(state)?.doorId==='jelly'&&activeRuntime.arcade)worldArcadeView?.update(activeRuntime.arcade);\r\n if(pond&&activeRuntime.pond)pond=activeRuntime.pond;\r\n if(boss&&activeRuntime.boss)boss=activeRuntime.boss;\r\n if(shopView)shopLastNow=activeRuntime.shopLastNow;\r\n const bossChanged=!!boss&&!!oldBoss&&(boss.playerHits!==oldBoss.playerHits||boss.phaseHits!==oldBoss.phaseHits||boss.status!==oldBoss.status||boss.phase!==oldBoss.phase);\r\n if(before!==state.phase||bossChanged)save(true);else save();\r\n}\r\nasync function shift(){\r\n if(worldRunMode)return advanceWorldRoom();\r\n if(transition||state.phase!=='walk'||state.pauseReasons.length)return;transition=true;\r\n let next,node;\r\n try{\r\n  if(shopView){shopView.setActive(false);shopDrawing=false;scene.resize();}\r\n  await scene.walkOut({});\r\n  const current=currentRunRoom(state);\r\n  const offers=runRouteOffers(state,{roomIds:chosenRooms()});\r\n  if(offers.length){\r\n   routeDoor=mountDoorChoices(stage,offers);\r\n   const offer=await routeDoor.confirmation;state=chooseRunRoute(state,offer.id==='training'?null:offer.id);save(true);routeDoor.dispose();routeDoor=null;\r\n  }\r\n  next=advanceRunRoom(state);node=currentRunRoom(next);\r\n }catch(error){routeDoor?.dispose();routeDoor=null;transition=false;fault(error);return;}\r\n if(!node){state=next;render();save(true);transition=false;return;}\r\n try{\r\n  let walking=Promise.resolve();\r\n  if(['miniboss','mainboss'].includes(node.type)){\r\n   await walking;walking=Promise.resolve();\r\n   bossDoor=await mountBossDoor(stage,node,{scheduleVersion:next.schedule.scheduleVersion??1});await bossDoor.confirmation;\r\n   bossDoor.dispose();bossDoor=null;\r\n  }\r\n  await handoff.enter({style:'pixel-flight',material:node.doorId??(['miniboss','mainboss'].includes(node.type)?'crystal':'training'),label:node.type==='warmup'?'Walking to the next training room':node.roomType==='corgi_shop'?'Pick up your phone — entering the shop':node.type==='full-game'?node.roomType==='healing_waters'?'Entering the healing waters':'Entering the grass':'Approaching the crystal gate',\r\n   walk:walking,prepare:({signal})=>scene.prepareRoom({...node,signal},{scheduleVersion:next.schedule.scheduleVersion??1}),commit:prepared=>{scene.commitRoom(prepared);roomCoachId=prepared.coachDefinition.id;roomCoachLabel=prepared.coachDefinition.label;next.pauseReasons=[...state.pauseReasons];state=next;configureRoom();render();}});\r\n  save(true);\r\n }catch(error){fault(error);}finally{bossDoor?.dispose();bossDoor=null;transition=false;}\r\n}\r\nfunction render(){\r\n if(!state)return;const node=currentRunRoom(state);\r\n recoveryContinueButton.hidden=!(worldRunMode&&node?.type==='recovery'&&state.phase==='walk');\r\n const quiet=document.hidden||['complete','defeated','captured','rest-reaper'].includes(state.phase)||state.pauseReasons.some(reason=>reason!=='loading');\r\n physicalSound.setScene(quiet?'off':'portal',node?.doorId??(boss?'ice':node?.biomeId==='cardio'?'grass':'quilt'));\r\n document.body.dataset.runRoom=node?.roomType??node?.type??'';\r\n document.body.dataset.combatClass=boss&&state.combatClass?state.combatClass:'';\r\n document.body.dataset.runEndAvailable=String(worldRunMode&&worldStarted&&worldClient.envelope?.runStatus==='active');\r\n $('tutorial-toggle').textContent=state.tutorials.enabled?'Tutorials on':'Tutorials off';$('tutorial-toggle').setAttribute('aria-pressed',String(state.tutorials.enabled));\r\n $('level').textContent=`LEVEL ${xpLevel(state.xp)}`;$('xp').value=state.xp.xp%XP_TUNING_PROPOSAL.xpPerLevel;$('xp-label').textContent=`${state.xp.xp} XP`;\r\n $('stats').textContent=state.rulesVersion===2?`ARMOR ${Math.ceil(state.stats.armor)}/${state.stats.armorMax}  |  ${state.stats.armorBroken?'LAST STAND  |  NEXT HIT ENDS RUN':state.loadout.styleId.toUpperCase()}  |  ${Math.floor(state.activeRunMs/60000)} ACTIVE MIN`:`♥ ${Math.round(state.stats.health)}/${state.stats.maxHealth}　✊ ${state.stats.punchDamage}　✦ ${state.stats.levels.luck}`;\r\n const progressNodes=state.schedule.nodes.filter(entry=>entry.type!=='bonus'),progressDone=state.schedule.nodes.slice(0,state.cursor+1).filter(entry=>entry.type!=='bonus').length;\r\n $('room-label').textContent=worldRunMode?`AUTHENTICATED RUN · ${Math.min(progressDone,progressNodes.length)}/${progressNodes.length}`:`LOCAL PREVIEW · ${Math.min(progressDone,progressNodes.length)}/${progressNodes.length}`;\r\n if(worldRunMode){$('world-energy').hidden=false;$('world-energy').textContent=`SAVED STAMINA · ${Math.floor((state.energyGrantedMs??0)/1000)}s`;$('end-run').hidden=!worldStarted||worldClient.envelope?.runStatus!=='active';}\r\n $('upgrade').hidden=state.phase!=='upgrade';\n showRunUpgradeChoices();\n for(const el of document.querySelectorAll('[data-choice]'))el.dataset.active=String(el.dataset.choice===state.choice);\r\n const crisisRoom=node?.doorId==='jelly',clayRoom=node?.doorId==='clay',danceRoom=node?.doorId==='dance',treasureRoom=node?.roomType==='treasure_side_room',arcadeRoom=crisisRoom||clayRoom||danceRoom;\r\n $('objective').hidden=state.phase==='upgrade'||danceRoom||treasureRoom||!!authoredView||!!rescueView;$('held-weapon').hidden=$('held-shield').hidden=!(boss||crisisRoom)||!!(boss&&state.combatClass);\r\n combatView.update({boss:rescueView||['complete','defeated','captured','rest-reaper'].includes(state.phase)?null:boss,input:lastCombatInput,runeCenter:combatInput.snapshot().runeCenter,runeFrame:{width:video.videoWidth,height:video.videoHeight,mirrored:cameraMode==='user'},classId:state.combatClass,level:node?.level??1,tier:state.combatGearTier??0,paused:state.phase!=='active'||state.pauseReasons.length>0});\r\n $('motion-arrow').hidden=!!authoredView||!!rescueView||state.phase!=='active'||!!boss||!!grass||!!pond||!!shopView||arcadeRoom;\r\n $('room-look').hidden=!shopView&&authoredGate?.kind!=='altar';\r\n if(['complete','defeated','captured','rest-reaper'].includes(state.phase)){\r\n  $('hud').hidden=true;$('summary').hidden=false;$('pause').disabled=true;\r\n  if(state.phase==='defeated')$('summary').querySelector('h1').textContent='RUN ENDED';\r\n  if(worldRunMode){$('summary').querySelector('h1').textContent=state.phase==='complete'?'RUN COMPLETE':'RUN ENDED';$('summary-text').textContent=`${state.acceptedQualifyingUnits??0} qualifying rooms · ${Math.floor((state.energyGrantedMs??0)/1000)} seconds of harvester stamina saved.`;$('return-base').hidden=false;}\r\n  else $('summary-text').textContent=`${state.xp.xp} XP · ${state.xp.packEntitlements.length} level-up packs earned in this preview.`;\r\n  if(state.phase==='captured'){$('summary').querySelector('h1').textContent='CAPTURED';$('summary-text').textContent='MOM Inc. captured you and returned you to base. Your banked rewards are retained.';}\r\n  if(state.phase==='rest-reaper'){$('summary').querySelector('h1').textContent='REST REAPER';$('summary-text').textContent='60 active minutes. Your secured rewards are banked. Take a break.';}\r\n  tracker?.stop();return;\r\n }\r\n $('biome-label').textContent=(treasureRoom?'TREASURE CHASE':clayRoom?'CLAY · SUBWAY BURPEES':danceRoom?'DANCE FLOOR':crisisRoom?'CROUCHING CRISIS':pond?'POND · HEALING WATERS':grass?'GRASS · COIN CHASE':node.biomeId??'CRYSTAL ENCOUNTER').replaceAll('-',' ').toUpperCase();\r\n $('coach-name').textContent=treasureRoom?'The chest':clayRoom?'SUBWAY BURPEES':danceRoom?GAMES.dance.name:crisisRoom?'COVER CRISIS':pond?'Do not wake Budig-ital Breath':grass?'Budig-ital Breath':node.coachLabel??roomCoachLabel;\r\n if(node.type==='recovery'){$('verb').textContent='RECOVERY';$('goal').textContent=node.roomType==='boss_recovery'?'PREPARE TO REENTER':`LEVEL ${node.levelIndex} COMPLETE`;$('cue').textContent=node.roomType==='boss_recovery'?'Catch your breath. Continue to face the same coach in phase two.':'Rest as long as you need. Continue when ready, or bank your run.';return;}\n const target=roomTarget(node),remaining=Math.max(0,target-state.roomValue);\r\n $('verb').textContent=grass?'CHASE THE SPRINT COACH':movement?MOVEMENTS[node.exerciseId].name:'Read the attack';\r\n $('goal').textContent=movement?(node.reps?`${Math.ceil(remaining)} REPS`:`${Math.ceil(remaining)} SEC`):`PHASE ${boss?.phase??1}`;\r\n $('cue').textContent=state.phase==='walk'?`+${state.completion?.xp??0} XP · KEEP GOING`:movement?'Follow the coach’s rhythm.':'';\r\n if(grass){const chase=grass.chase;$('goal').textContent=`${Math.ceil(chase.totalDuration-chase.elapsed)} SEC`;$('cue').textContent=`${chase.collectedCoins+chase.bonusCoins} ${RESOURCE_LABELS.runCurrency.toUpperCase()} · ${chase.slowRemaining>0?'SLOWED — KEEP GOING':'CATCH UP FOR THE BONUS'}`;}\r\n if(grass&&state.phase==='active'&&!grassPose?.controlValid){$('verb').textContent='LAY DOWN';$('goal').textContent='LOOK UP';$('cue').textContent='Your chase waits until you are in position.';}\r\n if(pond){\r\n  $('verb').textContent=pond.status==='opening'?'HOLD OUT AN OPEN HAND':pondBreath(pond).label.toUpperCase();\r\n  $('goal').textContent=pond.status==='opening'?`${Math.ceil(Math.max(0,POND_TUNING_PROPOSAL.openingHandMs-pond.openingHandMs)/100)/10} SEC`:pond.status==='awoken'?'AWAKE!':`${Math.ceil((POND_TUNING_PROPOSAL.healingMs-pond.healingMs)/1000)} SEC`;\r\n  $('cue').textContent=pond.status==='opening'?'Let a fish nibble to open the pond.':pond.status==='complete'?(state.rulesVersion===2?'REST COMPLETE':`+${state.completion?.healed??0} HEALTH`):`STAY STILL · WAKE ${Math.round(pond.wakeMeter/POND_TUNING_PROPOSAL.wakeThresholdSeconds*100)}%`;\r\n }\r\n if(shopView){\r\n  const shop=state.shopLedger?.shops?.[node.id];$('biome-label').textContent='PAPIER MÂCHÉ · CORGI BREAK ROOM';$('coach-name').textContent='The merchant';\r\n  if(shop){$('verb').textContent=shop.mode==='ambush'?'GRAB IT AND GET OUT':'PINCH TO PICK UP';\r\n   $('goal').textContent=shop.mode==='ambush'?`${Math.ceil(10-shop.elapsed)} SEC`:`${state.runCoins} ${RESOURCE_LABELS.runCurrency.toUpperCase()}`;\r\n   $('cue').textContent=shopNotice||`${shop.basket.length} CARRIED · ${shop.mode==='ambush'?'ONE BAIT · EXIT RIGHT':'CHECKOUT OR EXIT RIGHT'}`;\r\n  }else{$('verb').textContent='THE MERCHANT IS SETTING UP';$('goal').textContent='WAIT FOR THE SHOP';$('cue').textContent='';}\r\n }\r\n if(boss){\r\n  const phase=phaseProgress(boss);$('goal').textContent=`${Math.ceil(phase.hpRemaining)} HP`;\r\n  const cue=boss.status==='finisher'?'PUNCH TO BREAK THE CHIP':boss.status==='pursuit'?'JOG IN PLACE':boss.status==='escaped'?'BOSS ESCAPED · PHASE TWO PENDING':boss.cue?.name==='opening'?'STUNNED — PUNCH!':boss.cue?.kind==='red_crouch_boss'?'CROUCH — UNBLOCKABLE':boss.cue?.kind==='red_jump_environment'?'JUMP — UNBLOCKABLE':boss.cue?.name==='window'?'STEP LEFT — PARRY':'WAIT FOR THE OPENING';\r\n  $('cue').textContent=cue;document.body.dataset.bossColor=boss.cue?.kind?.startsWith('red')?'red':'yellow';\r\n  if(state.combatClass&&boss.status==='fighting'){\r\n   const direction=boss.cue?.direction?.toUpperCase()??'',red=boss.cue?.kind?.startsWith('red');\r\n   const answer=red?(state.combatClass==='monk'?(boss.cue.kind==='red_jump_environment'?'JUMP':'CROUCH'):state.combatClass==='knight'?'STEP ASIDE':state.combatClass==='mage'?'SHIELD OR BACKSTEP':'BACKSTEP')\r\n    :state.combatClass==='monk'?'DODGE LEFT OR RIGHT':state.combatClass==='dual'?`PARRY ${direction} OR CROSS GUARD`:state.combatClass==='mage'?'RAISE SHIELD':'SHIELD + PUNCH';\r\n   $('cue').textContent=boss.cue?.name==='clash'?'CLASH — ALTERNATE FOUR PUNCHES OR JOG!':boss.cue?.name==='opening'?(boss.combat.superArmed?'SUPER READY — ATTACK!':'OPENING — L R L OR L L R R COMBO!'):boss.cue?.name==='recovery'?'RESET YOUR STANCE · KEEP ATTACKING':`${direction} ${boss.cue?.attackName??'STRIKE'} · ${answer}`;\r\n   $('verb').textContent=state.combatClass.toUpperCase();\r\n   if(boss.combat?.lostTargetUntilMs>boss.combat?.clockMs)$('cue').textContent='BOSS LOST YOU — FREE HITS!';\r\n  }\r\n  if(state.phase==='walk'&&state.completion?.combatGearTier!==undefined)$('cue').textContent=`FOUND ${COMBAT_CLASSES[state.combatClass].gear[state.completion.combatGearTier].toUpperCase()}`;\r\n }\r\n if(crisisRoom&&activeRuntime?.arcade){$('verb').textContent='PUNCH THE HOSTILES';$('goal').textContent=`AREA ${Math.min(4,activeRuntime.arcade.sim?.s?.area??0)+1}`;$('cue').textContent=activeRuntime.arcade.sim?.s?.phase==='fight'?'HIDE TO DODGE · PUNCH TO CLEAR':'ADVANCE THROUGH THE SCENE';}\r\n if(clayRoom&&activeRuntime?.arcade){const run=activeRuntime.arcade.run??{};$('verb').textContent='JUMP · SLIDE · CHANGE LANES';$('goal').textContent=`${Math.ceil(Math.max(0,600-(run.distance??0)))} M LEFT`;$('cue').textContent='ARMS UP: JUMP · CROUCH: SLIDE · LEAN OR STEP: STEER';}\r\n if(danceRoom&&activeRuntime?.arcade){const arcade=activeRuntime.arcade;$('verb').textContent='DANCE WITH THE BEAT';$('goal').textContent=`${arcade.score?.score??0} POINTS`;$('cue').textContent=`${arcade.score?.judgedBeats??0} / 120 BEATS · ${arcade.score?.judgment??'KEEP MOVING'}`;}\r\n const message=state.pauseReasons.includes('tracking')?'Step into view. Your run is paused.':state.pauseReasons.length?'Paused':state.phase==='upgrade'?'Choose with your body. Hold the position.':state.phase==='walk'?'Walking onward…':treasureRoom?'Jog in place to reach the chest before the trap closes.':danceRoom?'Move with the beat and keep dancing through the round.':clayRoom?'Arms up to jump, crouch to slide, and lean or step to change lanes.':crisisRoom?'Punch to fire. Hide when enemies attack.':boss?'Block yellow. Dodge red. Punch during the opening.':movement?.snapshot().message??'Follow the coach.';\r\n // Opening line is from Corgi's public dialogue; achievement nag is a new shop extension.\r\n const merchantLine=state.shopLedger?.merchantAvailable?'“Everyone else got the reading. You did not.” Three flawless elites next, please.':'The lights are out. One item. Ten seconds. Get out.';\r\n $('run-message').textContent=state.pauseReasons.length?(shopView&&state.pauseReasons.includes('tracking')?'Show your open hand to the rear camera. Your shop is paused.':message):shopView?shopDoor?'Pick up your phone. Open your hand, then pinch.':merchantLine:pond&&state.phase==='active'?'Stand tall and steady. Breathe normally.':grass&&state.phase==='active'?(grassPose?.prompt||grassPose?.reason||'LAY DOWN. Look up. Raise and lean to fly.') :message;\r\n if(boss&&state.combatClass&&state.phase==='active'&&!state.pauseReasons.length){\r\n  const hints={knight:'Shield yellow. Step aside for red. Punish openings.',monk:'Dodge yellow. Jump or crouch for red. Keep punching.',dual:'Parry or cross guard yellow. Backstep red. Counter with your other hand.',mage:'Raise your shield or backstep. Draw, confirm, then channel.'};\r\n  $('run-message').textContent=boss.status==='pursuit'?'Jog in place to escape.':boss.status==='finisher'?'Punch to break the chip and finish.':boss.cue?.name==='clash'?'Four alternating punches or a short jog wins the clash.':hints[state.combatClass];\r\n }\r\n if(state.phase==='tutorial'&&tutorialKey!==`${state.cursor}`){\r\n  tutorial?.dispose();tutorialKey=`${state.cursor}`;\r\n  const steps=tutorialStepsForRoom({node,shopView,pond,grass,classId:state.combatClass});\r\n  tutorial=mountTutorialGuide(stage,{steps,onComplete(){tutorial=null;if(worldRunMode){void finishWorldTutorial();return;}state=finishRunTutorial(state);movement?.reset();save(true);render();}});\r\n }\r\n if(worldRunMode&&worldUnsupported)$('run-message').textContent='This saved room could not be loaded. End the run to return to base.';\r\n if(worldRunMode&&worldBlocked)$('run-message').textContent='A saved event is unresolved. This run is blocked until server status resolves it.';\r\n}\r\nfunction materialForRoom(node=currentRunRoom(state)){return node?.doorId==='jelly'?'jelly':node?.roomType==='coin_chase'?'grass':null;}\r\nfunction resetMaterialGate(){materialGateGeneration++;materialGate?.view?.dispose();materialGate=null;}\r\nfunction currentMaterialGate(gate=materialGate){\r\n return !!gate&&gate===materialGate&&!disposed&&!worldBlocked&&gate.generation===materialGateGeneration&&state?.id===gate.localRunId&&currentRunRoom(state)?.id===gate.nodeId\r\n  &&(!worldRunMode||worldVisitId()===gate.visitId&&worldClient?.envelope?.runId===gate.runId);\r\n}\r\nfunction materialGateReady(){return currentMaterialGate()&&materialGate.ready;}\r\nfunction materialGatePaused(gate=materialGate,allowTracking=false){\r\n return !currentMaterialGate(gate)||!gate.poseTracked||performance.now()-gate.lastSeenWall>300||document.hidden||worldCameraTransition||transition||state.phase!=='active'\r\n  ||state.pauseReasons.some(reason=>reason!=='loading'&&!(allowTracking&&reason==='tracking'));\r\n}\r\nfunction ensureMaterialGate(){\r\n const material=materialForRoom();if(!material||state.phase!=='active'||worldRunMode&&!activeRuntime)return;\r\n if(currentMaterialGate())return;\r\n resetMaterialGate();\r\n const gate={generation:materialGateGeneration,material,nodeId:currentRunRoom(state).id,localRunId:state.id,visitId:worldRunMode?worldVisitId():null,runId:worldRunMode?worldClient?.envelope?.runId:null,\r\n  view:null,ready:false,pending:true,trackingPending:false,loadingActive:false,ceremonyComplete:false,poseTracked:false,lastSeenWall:0,observationId:0};\r\n if(worldRunMode&&(!gate.visitId||!gate.runId)){worldFailure(Error('The material entrance needs the current authenticated visit.'));return;}\r\n materialGate=gate;\r\n gate.view=mountMaterialOpening(stage,{material,reducedMotion:reduced,onComplete(){if(!currentMaterialGate(gate))return;gate.ceremonyComplete=true;void completeMaterialGate(gate);}});\r\n gate.view.pause();\r\n if(!worldRunMode){state=setRunPaused(state,'loading',true);gate.loadingActive=true;save(true);void Promise.resolve(gate.view.ready).then(()=>{if(currentMaterialGate(gate))gate.pending=false;}).catch(error=>{if(currentMaterialGate(gate))fault(error);});return;}\r\n void (async()=>{\r\n  try{\r\n   await gate.view.ready;if(!currentMaterialGate(gate))return;\r\n   if(!await drainWorldFrames()||!currentMaterialGate(gate))return;\r\n   if(!state.pauseReasons.includes('loading')){await worldClient.pause(gate.visitId,'loading',true);if(!currentMaterialGate(gate))return;applyWorldEnvelope();}\r\n   if(currentMaterialGate(gate))gate.loadingActive=state.pauseReasons.includes('loading');\r\n  }catch(error){if(currentMaterialGate(gate))worldFailure(error);}finally{if(currentMaterialGate(gate))gate.pending=false;}\r\n })();\r\n}\r\nasync function resumeMaterialTracking(gate=materialGate){\r\n if(!currentMaterialGate(gate)||gate.pending||gate.trackingPending||materialGatePaused(gate,true)||!state.pauseReasons.includes('tracking'))return false;\r\n if(!worldRunMode){state=setRunPaused(state,'tracking',false);return true;}\r\n gate.trackingPending=true;\r\n try{\r\n  if(!await drainWorldFrames()||!currentMaterialGate(gate)||materialGatePaused(gate,true))return false;\r\n  await worldClient.pause(gate.visitId,'tracking',false);if(!currentMaterialGate(gate))return false;applyWorldEnvelope();return !materialGatePaused(gate);\r\n }catch(error){if(currentMaterialGate(gate))worldFailure(error);return false;}finally{if(currentMaterialGate(gate))gate.trackingPending=false;}\r\n}\r\nfunction observeMaterialGate(input){\r\n ensureMaterialGate();const gate=materialGate;if(!currentMaterialGate(gate)||gate.ready)return false;\r\n // Only the explicit test mode may supply normalized gesture flags. Production always uses visible body landmarks.\r\n const gesture=testMode&&input.materialOpeningInput?input.materialOpeningInput:detectMaterialOpeningGesture(input.points,input.tracked===true,{hands:input.materialHands??[],worldHands:input.materialWorldHands??[],aspect:input.aspect??1});\r\n gate.poseTracked=input.tracked===true&&gesture.tracked!==false;gate.lastSeenWall=performance.now();\r\n if(gate.poseTracked&&state.pauseReasons.includes('tracking')&&!gate.pending&&!gate.trackingPending&&!materialGatePaused(gate,true)){void resumeMaterialTracking(gate);return true;}\r\n const paused=materialGatePaused(gate)||gate.pending||gate.trackingPending||!gate.loadingActive;\r\n if(paused)gate.view.pause();else gate.view.resume();\r\n gate.view.observe({observationId:gate.observationId++,now:input.now,tracked:gate.poseTracked,raised:gesture.raised===true,push:gesture.push===true},{paused});\r\n if(gate.ceremonyComplete)void completeMaterialGate(gate);\r\n return true;\r\n}\r\nasync function completeMaterialGate(gate=materialGate){\r\n if(worldViewTour&&!worldViewTour.done)return false;\r\n if(!currentMaterialGate(gate)||gate.ready||gate.pending||gate.trackingPending||!gate.ceremonyComplete||!gate.loadingActive||materialGatePaused(gate))return false;\r\n gate.pending=true;\r\n try{\r\n  if(worldRunMode){\r\n   if(!await drainWorldFrames()||!currentMaterialGate(gate)||materialGatePaused(gate))return false;\r\n   await worldClient.pause(gate.visitId,'loading',false);if(!currentMaterialGate(gate))return false;applyWorldEnvelope();\r\n  }else state=setRunPaused(state,'loading',false);\r\n  if(!currentMaterialGate(gate)||materialGatePaused(gate)||state.pauseReasons.includes('loading'))return false;\r\n  gate.loadingActive=false;gate.ready=true;gate.view.dispose();gate.view=null;grassLastNow=null;worldArcadeInput?.reset?.();save(true);render();return true;\r\n }catch(error){if(currentMaterialGate(gate))worldFailure(error);return false;}finally{if(currentMaterialGate(gate))gate.pending=false;}\r\n}\r\nfunction materialGateDebug(){const gate=materialGate;return gate?{material:gate.material,current:currentMaterialGate(gate),nodeId:gate.nodeId,visitId:gate.visitId,runId:gate.runId,generation:gate.generation,ready:gate.ready,pending:gate.pending,trackingPending:gate.trackingPending,loadingActive:gate.loadingActive,ceremonyComplete:gate.ceremonyComplete,poseTracked:gate.poseTracked,opening:gate.view?.state??null}:null;}\r\nfunction currentWorldDanceGate(generation=worldDanceGateGeneration,visitId=worldDanceGateVisit,runId=worldDanceGateRunId){\r\n return !disposed&&!worldBlocked&&generation===worldDanceGateGeneration&&!!visitId&&worldVisitId()===visitId&&worldClient?.envelope?.runId===runId&&currentRunRoom(state)?.doorId==='dance';\r\n}\r\nfunction currentWorldClayGate(generation=worldClayGateGeneration,visitId=worldClayGateVisit,runId=worldClayGateRunId){\r\n return !disposed&&!worldBlocked&&generation===worldClayGateGeneration&&!!visitId&&worldVisitId()===visitId&&worldClient?.envelope?.runId===runId&&currentRunRoom(state)?.doorId==='clay';\r\n}\r\nfunction worldClayPaused(allowTracking=false){return !worldClayPoseTracked||document.hidden||worldCameraTransition||transition||state.phase!=='active'||state.pauseReasons.some(reason=>reason!=='loading'&&!(allowTracking&&reason==='tracking'));}\r\nasync function resumeWorldClayTracking(generation=worldClayGateGeneration,visitId=worldClayGateVisit,runId=worldClayGateRunId){\r\n if(worldClayTrackingPending||!currentWorldClayGate(generation,visitId,runId)||worldClayPaused(true)||!state.pauseReasons.includes('loading')||!state.pauseReasons.includes('tracking'))return false;\r\n worldClayTrackingPending=true;\r\n try{\r\n  if(!await drainWorldFrames()||!currentWorldClayGate(generation,visitId,runId)||worldClayPaused(true)||!state.pauseReasons.includes('tracking'))return false;\r\n  await worldClient.pause(visitId,'tracking',false);applyWorldEnvelope();\r\n  return currentWorldClayGate(generation,visitId,runId)&&state.pauseReasons.includes('loading')&&!state.pauseReasons.includes('tracking')&&!worldClayPaused();\r\n }catch(error){worldFailure(error);return false;}finally{if(generation===worldClayGateGeneration)worldClayTrackingPending=false;}\r\n}\r\nfunction beginWorldClayOpening(){\r\n const visitId=worldVisitId(),runId=worldClient?.envelope?.runId,generation=worldClayGateGeneration;\r\n if(!visitId||!runId){worldBlocked=true;worldFailure(Error('The Clay opening needs the current authenticated visit.'));return;}\r\n worldClayGateVisit=visitId;worldClayGateRunId=runId;worldClayReady=false;worldClayLoadingActive=false;worldClayLoadingPending=true;\r\n const mounted=mountClayOpening(stage,{onComplete(){worldClayCeremonyComplete=true;void completeWorldClayOpening(generation,visitId,runId,mounted);}});worldClayOpening=mounted;\r\n void mounted.ready.then(async()=>{\r\n  if(!currentWorldClayGate(generation,visitId,runId)||worldClayOpening!==mounted)return;\r\n  if(!state.pauseReasons.includes('loading')){\r\n   if(!await drainWorldFrames())return;\r\n   if(!currentWorldClayGate(generation,visitId,runId)||worldClayOpening!==mounted)return;\r\n   await worldClient.pause(visitId,'loading',true);applyWorldEnvelope();\r\n  }\r\n  if(currentWorldClayGate(generation,visitId,runId)&&worldClayOpening===mounted){worldClayLoadingActive=state.pauseReasons.includes('loading');if(!state.pauseReasons.some(reason=>reason!=='loading'))mounted.resume();}\r\n }).catch(error=>{if(currentWorldClayGate(generation,visitId,runId))worldFailure(error);}).finally(()=>{if(generation===worldClayGateGeneration)worldClayLoadingPending=false;});\r\n}\r\nasync function completeWorldClayOpening(generation=worldClayGateGeneration,visitId=worldClayGateVisit,runId=worldClayGateRunId,mounted=worldClayOpening){\r\n if(worldViewTour&&!worldViewTour.done)return false;\r\n if(!currentWorldClayGate(generation,visitId,runId)||worldClayOpening!==mounted||!worldClayCeremonyComplete||!worldClayLoadingActive||worldClayLoadingPending||worldClayReady||worldClayPaused()||state.pauseReasons.some(reason=>reason!=='loading'))return false;\r\n worldClayLoadingPending=true;\r\n try{\r\n  if(!await drainWorldFrames())return false;\r\n  if(!currentWorldClayGate(generation,visitId,runId)||worldClayOpening!==mounted||worldClayPaused()||!state.pauseReasons.includes('loading')||state.pauseReasons.some(reason=>reason!=='loading'))return false;\r\n  await worldClient.pause(visitId,'loading',false);applyWorldEnvelope();\r\n  if(!currentWorldClayGate(generation,visitId,runId)||worldClayOpening!==mounted||state.phase!=='active'||state.pauseReasons.includes('loading'))return false;\r\n  worldClayLoadingActive=false;worldClayReady=true;mounted.dispose();worldClayOpening=null;render();return true;\r\n }catch(error){worldFailure(error);return false;}finally{if(generation===worldClayGateGeneration)worldClayLoadingPending=false;}\r\n}\r\nfunction worldDancePaused(allowTracking=false){return !worldDancePoseTracked||document.hidden||worldCameraTransition||transition||state.phase!=='active'||state.pauseReasons.some(reason=>reason!=='loading'&&!(allowTracking&&reason==='tracking'));}\r\nasync function resumeWorldDanceTracking(generation=worldDanceGateGeneration,visitId=worldDanceGateVisit,runId=worldDanceGateRunId){\r\n if(worldDanceTrackingPending||!currentWorldDanceGate(generation,visitId,runId)||worldDancePaused(true)||!state.pauseReasons.includes('loading')||!state.pauseReasons.includes('tracking'))return false;\r\n worldDanceTrackingPending=true;\r\n try{\r\n  if(!await drainWorldFrames()||!currentWorldDanceGate(generation,visitId,runId)||worldDancePaused(true)||!state.pauseReasons.includes('tracking'))return false;\r\n  await worldClient.pause(visitId,'tracking',false);applyWorldEnvelope();\r\n  return currentWorldDanceGate(generation,visitId,runId)&&state.pauseReasons.includes('loading')&&!state.pauseReasons.includes('tracking')&&!worldDancePaused();\r\n }catch(error){worldFailure(error);return false;}finally{if(generation===worldDanceGateGeneration)worldDanceTrackingPending=false;}\r\n}\r\nfunction beginWorldDanceOpening(){\r\n const visitId=worldVisitId(),runId=worldClient?.envelope?.runId,generation=worldDanceGateGeneration;\r\n if(!visitId||!runId){worldBlocked=true;worldFailure(Error('The Dance opening needs the current authenticated visit.'));return;}\r\n worldDanceGateVisit=visitId;worldDanceGateRunId=runId;worldDanceReady=false;worldDanceLoadingActive=false;worldDanceLoadingPending=true;\r\n const mounted=mountDanceOpening(stage,{onComplete(){worldDanceCeremonyComplete=true;void completeWorldDanceOpening(generation,visitId,runId,mounted);}});worldDanceOpening=mounted;\r\n void mounted.ready.then(async()=>{\r\n  if(!currentWorldDanceGate(generation,visitId,runId)||worldDanceOpening!==mounted)return;\r\n  if(!state.pauseReasons.includes('loading')){\r\n   if(!await drainWorldFrames())return;\r\n   if(!currentWorldDanceGate(generation,visitId,runId)||worldDanceOpening!==mounted)return;\r\n   await worldClient.pause(visitId,'loading',true);applyWorldEnvelope();\r\n  }\r\n  if(currentWorldDanceGate(generation,visitId,runId)&&worldDanceOpening===mounted){worldDanceLoadingActive=state.pauseReasons.includes('loading');if(!state.pauseReasons.some(reason=>reason!=='loading'))mounted.resume();}\r\n }).catch(error=>{if(currentWorldDanceGate(generation,visitId,runId))worldFailure(error);}).finally(()=>{if(generation===worldDanceGateGeneration)worldDanceLoadingPending=false;});\r\n}\r\nasync function completeWorldDanceOpening(generation=worldDanceGateGeneration,visitId=worldDanceGateVisit,runId=worldDanceGateRunId,mounted=worldDanceOpening){\r\n if(worldViewTour&&!worldViewTour.done)return false;\r\n if(!currentWorldDanceGate(generation,visitId,runId)||worldDanceOpening!==mounted||!worldDanceCeremonyComplete||!worldDanceLoadingActive||worldDanceLoadingPending||worldDanceReady||worldDancePaused()||state.pauseReasons.some(reason=>reason!=='loading'))return false;\r\n worldDanceLoadingPending=true;\r\n try{\r\n  if(!await drainWorldFrames())return false;\r\n  if(!currentWorldDanceGate(generation,visitId,runId)||worldDanceOpening!==mounted||worldDancePaused()||!state.pauseReasons.includes('loading')||state.pauseReasons.some(reason=>reason!=='loading'))return false;\r\n  await worldClient.pause(visitId,'loading',false);applyWorldEnvelope();\r\n  if(!currentWorldDanceGate(generation,visitId,runId)||worldDanceOpening!==mounted||state.phase!=='active'||state.pauseReasons.includes('loading'))return false;\r\n  worldDanceLoadingActive=false;worldDanceReady=true;mounted.dispose();worldDanceOpening=null;render();return true;\r\n }catch(error){worldFailure(error);return false;}finally{if(generation===worldDanceGateGeneration)worldDanceLoadingPending=false;}\r\n}\r\nfunction danceRivalVisual(node){\r\n const schedule=state?.schedule,index=schedule?.nodes?.findIndex(entry=>entry.id===node?.id)??-1;\r\n const warmup=index>0?[...schedule.nodes.slice(0,index)].reverse().find(entry=>entry.type==='warmup'):null;\r\n if(!warmup)throw new Error('Dance room has no preceding scheduled warm-up rival.');\r\n const presentation=coachPresentationForRoom(schedule.scheduleVersion??1,warmup);\r\n if(!presentation?.atlasPath||!Array.isArray(presentation.frame))throw new Error('Dance rival has no scheduled visual presentation.');\r\n return {id:presentation.id,atlasPath:presentation.atlasPath,frame:[...presentation.frame]};\r\n}\r\nfunction mountWorldArcade(node){\r\n worldArcadeInput=createArcadeInput(node.doorId);\r\n worldArcadeView=node.doorId==='jelly'?createCrisisRoomView({host:stage})\r\n  :node.doorId==='dance'?createDanceRoomView({host:stage,rivalVisual:danceRivalVisual(node)})\r\n  :createClayRoomView({host:stage});\r\n worldArcadeRevision=null;\r\n}\r\nfunction tutorialStepsForRoom({node,shopView,pond,grass,classId}={}){\r\n if(node?.roomType==='nature_yoga')return [{instruction:'Release your hands, then push both open palms forward to enter. Hold Mountain, Upward Salute, and Warrior II for ten qualified seconds each.',graphicCue:'ghostpose',practiceLabel:'Punch to continue.'}];\r\n if(node?.roomType==='secret_room')return [{instruction:'Release your hands. Pinch low, then pull the curtain upward. Jog in place to open the vault; inspect both tradeoffs before choosing one relic or declining.',graphicCue:'holdring',practiceLabel:'Punch to continue.'}];\r\n if(node?.roomType==='two_altars')return [{instruction:'Use the rear camera. Open your palm on the paper, pinch, and pull down to tear it. Release to an open palm, then hold over one altar or Exit.',graphicCue:'holdring',practiceLabel:'Punch to continue.'}];\r\n if(classId&&['miniboss','mainboss'].includes(node?.type))return [{instruction:classHelp[classId],graphicCue:'punch',practiceLabel:'Punch to continue.'},{instruction:'Watch each windup. Clean openings reward L R L or L L R R punch combos. A full super meter powers your next attack after a clean defense chain.',graphicCue:'holdring',practiceLabel:'Punch to continue.'},{instruction:'In a clash, land four alternating punches or jog briefly. Fast held fists do not count as extra punches.',graphicCue:'punch',practiceLabel:'Punch to begin the fight.'}];\r\n if(node?.doorId==='dance')return [\r\n  {...ROOM_BY_ID.dance.tutorialSteps[3],practiceLabel:'Punch to continue, then reach all four screen corners with either wrist.'},\r\n  {instruction:'Keep moving through all 120 beats.',graphicCue:'holdring',practiceLabel:'Punch toward the camera to continue.'},\r\n ];\r\n if(shopView)return ROOM_BY_ID.papier_mache.tutorialSteps.map(step=>({...step,practiceLabel:'Open your hand, then lower it to continue.'}));\r\n if(pond||grass)return ROOM_BY_ID[pond?'pond':'grass'].tutorialSteps.map(step=>({...step,practiceLabel:'Punch to continue.'}));\r\n if(node?.doorId==='jelly')return ROOM_BY_ID.jelly.tutorialSteps.map(step=>({...step,practiceLabel:'Punch toward the camera to continue.'}));\r\n if(node?.doorId==='clay')return [{instruction:'Raise both arms to jump, or crouch to slide.',graphicCue:'ghostpose',practiceLabel:'Punch toward the camera to continue.'},{instruction:'Lean or step left and right to change lanes.',graphicCue:'swoopingcurvearrow',practiceLabel:'Punch toward the camera to continue.'},{instruction:'Reach the portal at the end of the course.',graphicCue:'holdring',practiceLabel:'Punch toward the camera to continue.'}];\r\n if(node?.doorId==='treasure')return [{instruction:'Jog in place to reach the chest before the trap closes.',graphicCue:'holdring',practiceLabel:'Punch toward the camera to continue.'}];\r\n if(node?.type==='warmup')return [{instruction:'Place your phone. Step back into view.',graphicCue:'holdring',practiceLabel:'Punch toward the camera to continue.'},{instruction:'Follow the coach. Finish the reps or hold.',graphicCue:'ghostpose',practiceLabel:'Punch to begin. Afterward: stand left, right, or crouch.'}];\r\n return [{instruction:'Yellow: step left to parry. Then punch.',graphicCue:'punch',practiceLabel:'Punch to continue.'},{instruction:node?.type==='miniboss'?'Red: crouch to dodge.':'Red: jump or crouch. Jog when the boss chases.',graphicCue:'crouch',practiceLabel:'Punch to begin the fight.'}];\r\n}\r\n$('begin').onclick=()=>start(false);$('resume').onclick=()=>start(true);\r\n$('end-run').onclick=()=>void endWorldRun();\r\n$('camera-retry').onclick=()=>{cameraMode=null;void acquireCamera(roomCameraMode(currentRunRoom(state)));};\r\n$('room-look').onclick=async()=>{const enabled=await (authoredGate?.kind==='altar'?authoredView.togglePhoneLook():shopLook.toggle());$('room-look').textContent=enabled?'LOOK AROUND ON':'ENABLE PHONE LOOK';};\r\n$('pause').onclick=()=>{if(state){const paused=!state.pauseReasons.includes('user');if(worldRunMode){void worldClient.pause(worldVisitId(),'user',paused).then(()=>{applyWorldEnvelope();$('pause').textContent=paused?'▶':'Ⅱ';}).catch(worldFailure);return;}state=setRunPaused(state,'user',paused);$('pause').textContent=state.pauseReasons.includes('user')?'▶':'Ⅱ';save(true);render();}};\r\n$('tutorial-toggle').onclick=()=>{if(worldRunMode&&worldStarted)return;const enabled=$('tutorial-toggle').getAttribute('aria-pressed')!=='true';$('tutorial-toggle').setAttribute('aria-pressed',String(enabled));$('tutorial-toggle').textContent=enabled?'Tutorials on':'Tutorials off';if(state){state=setRunTutorials(state,enabled);if(!enabled){tutorial?.dispose();tutorial=null;}save(true);render();}};\r\n$('another').onclick=()=>location.reload();\r\ndocument.addEventListener('visibilitychange',()=>{if(state){if(worldRunMode&&worldStarted){if(document.hidden)worldCameraTransition=true;void flushWorldFrames().then(()=>worldClient.pause(worldVisitId(),'background',document.hidden)).then(applyWorldEnvelope).catch(worldFailure).finally(()=>{if(!document.hidden)worldCameraTransition=false;});return;}state=setRunPaused(state,'background',document.hidden);save(true);}});\r\nwindow.addEventListener('resize',()=>scene.resize());\r\nfunction tick(now){\r\n if(disposed)return;const dt=Math.min(.1,Math.max(0,(now-lastFrame)/1000));lastFrame=Math.max(lastFrame,now);\r\n if(worldRunMode&&worldClient.disposed){stopWorldPresentation();return;}\r\n if(state?.schedule?.scheduleVersion===4&&boss?.status==='finisher'&&!activeRuntime?.rescue)void beginWorldRescue();\r\n rescueView?.update(dt);\r\n ensureWorldViewTour();\r\n if(worldViewTour?.lease&&!worldViewTour.done&&!worldViewTour.sceneDriven)worldViewTour.lease.update(dt,{paused:document.hidden||worldCameraTransition||transition||state.pauseReasons.some(reason=>reason!=='loading')});\r\n syncAuthoredView(dt);\r\n if(authoredGate?.kind==='altar')drawAuthoredAltar(dt,document.hidden||state.phase!=='active'||!!state.pauseReasons.length);\r\n if(worldTreasureView)worldTreasureView.render?.({dt,now,paused:document.hidden||state.phase!=='active'||!!state.pauseReasons.length});\r\n if(state&&materialForRoom())ensureMaterialGate();\r\n if(materialGate?.view){const paused=materialGatePaused()||materialGate.pending||materialGate.trackingPending||!materialGate.loadingActive;if(paused)materialGate.view.pause();else materialGate.view.resume();materialGate.view.render({paused});if(materialGate.ceremonyComplete&&!paused)void completeMaterialGate();}\r\n if(state&&!testMode&&now-lastRaw>800&&tracker?.running){if(worldRunMode&&worldStarted){if(!worldCameraTransition&&!state.pauseReasons.includes('tracking')){worldCameraTransition=true;void flushWorldFrames().then(()=>worldClient.pause(worldVisitId(),'tracking',true)).then(applyWorldEnvelope).catch(worldFailure).finally(()=>{worldCameraTransition=false;});}}else if(!worldRunMode){state=setTracking(state,false);render();}}\r\n const paused=document.hidden||!!state?.pauseReasons.some(reason=>['user','background'].includes(reason));\r\n if(grassView)grassView.render({dt,now,identity:tourIdentity(),pose:grassPose,paused:paused||state.phase!=='active'||!!state.pauseReasons.length,message:$('run-message').textContent});\r\n if(worldArcadeView)worldArcadeView.render({dt,now,paused:paused||state.phase!=='active'||!!state.pauseReasons.length});\r\n if(worldClayOpening){if(worldClayPaused())worldClayOpening.pause();else worldClayOpening.resume();worldClayOpening.render({paused:worldClayPaused()});if(worldClayCeremonyComplete&&!worldClayLoadingPending&&!worldClayTrackingPending)void completeWorldClayOpening();}\r\n if(worldDanceOpening){if(worldDancePaused())worldDanceOpening.pause();else worldDanceOpening.resume();worldDanceOpening.render();if(worldDanceCeremonyComplete&&!worldDanceLoadingPending&&!worldDanceTrackingPending)void completeWorldDanceOpening();}\r\n if(pondView&&pond)pondView.render(pond,{dt,paused:paused||state.phase!=='active'||!!state.pauseReasons.length,handOut:pondInput?.handOut===true,pointer:pondInput?.pointer});\r\n if(shopView&&state.shopLedger&&currentRunRoom(state))shopView.render({draw:shopDrawing,ledger:state.shopLedger,shopId:currentRunRoom(state).id,dt,now,paused:!!shopDoor||state.phase!=='active'||!!state.pauseReasons.length,applyLook:camera=>shopLook.apply(camera)});\r\n if(shopDoor){shopDoor.element.hidden=state.phase==='tutorial';shopDoor.render({paused:document.hidden||state.phase!=='active'||!!state.pauseReasons.length,now});}\r\n bossDoor?.render({paused:document.hidden||!!state?.pauseReasons.length,now});\r\n if(!worldTreasureView&&authoredGate?.kind!=='altar'&&(!shopView||!shopDrawing)&&(!grassView&&!pondView&&!worldArcadeView||state?.phase==='walk'))scene.update(dt,{paused:paused||!!worldIntro||!!worldViewTour&&!worldViewTour.done,introPaused:document.hidden||!!state?.pauseReasons.some(reason=>reason!=='loading'),active:state?.phase==='active',bossCue:boss?.cue,hitStop:combatView.isHitStopped()});\r\n const flatRoom=pondView?.element??grassView?.element??shopView?.element??worldArcadeView?.element;\r\n if(flatRoom&&state?.phase==='walk'&&!reduced){const travel=Math.max(0,Math.min(1,(9-scene.camera.position.z)/36));flatRoom.style.transform=`translateY(${(scene.camera.position.y-2.1)*8}px) scale(${1+travel*.12})`;}\r\n if(state?.phase==='walk'&&currentRunRoom(state)?.type!=='recovery'&&(!worldRunMode||!worldUnsupported))void shift();frame=requestAnimationFrame(tick);\r\n}\r\nframe=requestAnimationFrame(tick);\r\nfunction stopWorldPresentation(){\r\n disposed=true;worldBlocked=true;worldStarted=false;worldFrameBuffer=[];clearInterval(worldFlushTimer);worldFlushTimer=null;\r\n worldIntro?.cancel();cancelWorldViewTour();resetRescueView();resetAuthoredView();resetMaterialGate();cameraGeneration++;tracker?.stop();tracker=null;\r\n tutorial?.dispose();tutorial=null;worldArcadeView?.dispose();worldArcadeView=null;worldTreasureView?.dispose();worldTreasureView=null;\n grassView?.dispose();grassView=null;pondView?.dispose();pondView=null;shopView?.dispose();shopView=null;\n worldClayOpening?.dispose();worldClayOpening=null;worldDanceOpening?.dispose();worldDanceOpening=null;shopDoor?.dispose();shopDoor=null;bossDoor?.dispose();bossDoor=null;routeDoor?.dispose();routeDoor=null;\n combatView.dispose();scene.dispose();physicalSound.setScene('off');\n $('hud').hidden=true;$('opening').hidden=false;$('opening-note').textContent='Account or game generation changed. Refresh to continue.';$('begin').disabled=$('resume').disabled=true;\r\n}\r\nwindow.addEventListener('pagehide',resetMaterialGate,{once:true});\r\nwindow.addEventListener('pagehide',()=>{worldIntro?.cancel();cancelWorldViewTour();resetRescueView();resetAuthoredView();skipIntroButton.remove();recoveryContinueButton.remove();save(true);disposed=true;cameraGeneration++;worldClayOpening?.dispose();worldClayOpening=null;worldDanceOpening?.dispose();worldDanceOpening=null;clearInterval(worldFlushTimer);worldClient?.dispose();cancelAnimationFrame(frame);tracker?.stop();tutorial?.dispose();bossDoor?.dispose();routeDoor?.dispose();grassView?.dispose();pondView?.dispose();shopView?.dispose();worldArcadeView?.dispose();worldTreasureView?.dispose();shopDoor?.dispose();combatView.dispose();shopLook.dispose();physicalSound.dispose();handoff.dispose();scene.dispose();releaseGraphics();},{once:true});\r\n// Deterministic local-only integration seam; absent on the normal camera path.\r\nif(testMode)globalThis.myr5RunPreview={start,materialGate:materialGateDebug,read:()=>structuredClone(state),worldEnvelope:()=>worldClient?structuredClone(worldClient.envelope):null,...(worldRunMode?{worldTiming:()=>structuredClone(worldTimingEvidence),worldDebug:()=>({transition,worldCameraTransition,worldBlocked,disposed,pauseReasons:[...(state?.pauseReasons??[])],frameSeq,lastFrameSeq:worldClient?.envelope?.lastFrameSeq??null,worldFrameBufferLength:worldFrameBuffer.length,worldFlushPending:!!worldFlushPromise,clayGate:{current:currentWorldClayGate(),visitId:worldVisitId(),runId:worldClient?.envelope?.runId??null,gateVisitId:worldClayGateVisit,gateRunId:worldClayGateRunId,ready:worldClayReady,loadingActive:worldClayLoadingActive,loadingPending:worldClayLoadingPending,trackingPending:worldClayTrackingPending,ceremonyComplete:worldClayCeremonyComplete,poseTracked:worldClayPoseTracked,generation:worldClayGateGeneration,opening:worldClayOpening?structuredClone(worldClayOpening.state):null}})}:{boss:()=>structuredClone(boss),finishChip:finishBossChip,punchDoor:()=>bossDoor?.confirm()}),sample:(input,{flush=true}={})=>{if(worldRunMode)return handleWorldInput(input,{flush});sample(input);render();},tutorial:()=>{tutorial?.advance();},renderFrame:delta=>scene.update(delta,{active:state?.phase==='active'}),\r\n doorSample:input=>routeDoor?.observe({observationId:sequence++,...input}),grass:()=>grass?.chase,\r\n pond:()=>pond?structuredClone(pond):null,\r\n shop:()=>state?.shopLedger?structuredClone(state.shopLedger):null,shopAction,openShopDoor,runtime:()=>activeRuntime?structuredClone(activeRuntime):null,shopHand:raw=>shopView?.observeHand(raw),shopRaw:rawShop,\r\n shopTargetPoints:()=>shopView?.targets.filter(target=>target.visible).map(target=>{const p=target.position.clone().project(shopView.camera);return {action:target.userData.action,x:(p.x+1)/2,y:(1-p.y)/2};}),\r\n sceneStats:()=>({draw:scene.renderer.info.render,children:scene.scene.children.length,camera:scene.camera.position.toArray(),room:scene.currentRoom?.group?.children.length,canvas:[$('world').width,$('world').height],coach:scene.currentRoom?.coach?.position.toArray()}),get transitioning(){return transition;}};\r\nif(worldRunMode)void inspectWorldRun();\r\nif(worldRunMode)authTransitions().subscribe(()=>{if(worldClient.disposed)stopWorldPresentation();});\r\n","modules/roguelite/run-xp.mjs":"// Per-run XP receipts only. Wallet currency and pack ownership belong to other systems.\r\nexport const XP_TUNING_PROPOSAL = Object.freeze({\r\n  xpPerLevel: 100,\r\n  roomBaseXp: 25,\r\n  minibossBonusXp: 150,\r\n  mainbossBonusXp: 300,\r\n});\r\nconst ROOM_KINDS = new Set(['basic', 'full-game', 'treasure', 'secret', 'miniboss', 'mainboss']);\r\nconst ID = /^[A-Za-z0-9:_-]{1,128}$/;\r\nconst RUN_ID = /^[A-Za-z0-9:_-]{1,96}$/;\r\nconst clone = value => structuredClone(value);\r\nconst validId = value => typeof value === 'string' && ID.test(value);\r\nconst fail = message => { throw new RangeError(message); };\r\nconst awardAmount = kind => XP_TUNING_PROPOSAL.roomBaseXp\r\n  + (kind === 'miniboss' ? XP_TUNING_PROPOSAL.minibossBonusXp : kind === 'mainboss' ? XP_TUNING_PROPOSAL.mainbossBonusXp : 0);\r\n\r\nexport function createRunXp(runId) {\r\n  if (typeof runId !== 'string' || !RUN_ID.test(runId)) throw new TypeError('runId must be a stable string id up to 96 characters');\r\n  return { version: 1, runId, xp: 0, awards: [], bossVictories: [], packEntitlements: [] };\r\n}\r\n\r\nfunction assertState(state) {\r\n  if (!state || state.version !== 1 || typeof state.runId !== 'string' || !RUN_ID.test(state.runId)\r\n      || !Number.isSafeInteger(state.xp) || state.xp < 0 || !Array.isArray(state.awards) || !Array.isArray(state.bossVictories)\r\n      || !Array.isArray(state.packEntitlements)) {\r\n    throw new TypeError('invalid run XP state');\r\n  }\r\n  const rooms = new Map(), receipts = new Set(), bosses = new Map();\r\n  let total = 0;\r\n  for (const award of state.awards) {\r\n    if (!award || !validId(award.roomId) || !validId(award.receiptId) || !ROOM_KINDS.has(award.kind)\r\n        || award.xp !== awardAmount(award.kind) || rooms.has(award.roomId) || receipts.has(award.receiptId)) fail('invalid or duplicate XP award');\r\n    rooms.set(award.roomId, award); receipts.add(award.receiptId); total += award.xp;\r\n    if (!Number.isSafeInteger(total)) fail('run XP total exceeds safe integer range');\r\n    if (award.kind === 'miniboss' || award.kind === 'mainboss') {\r\n      if (!validId(award.encounterId) || bosses.has(award.encounterId)) fail('invalid or duplicate boss XP award');\r\n      bosses.set(award.encounterId, award);\r\n    } else if (award.encounterId != null) fail('non-boss award cannot reference a boss encounter');\r\n  }\r\n  if (total !== state.xp) fail('saved XP total does not match award receipts');\r\n  const seenBossReceipts = new Set();\r\n  for (const victory of state.bossVictories) {\r\n    if (!victory || !validId(victory.encounterId) || !validId(victory.roomId) || !validId(victory.receiptId)\r\n        || !['miniboss', 'mainboss'].includes(victory.kind) || seenBossReceipts.has(victory.encounterId)) fail('invalid or duplicate boss victory receipt');\r\n    const award = rooms.get(victory.roomId);\r\n    if (!award || award.kind !== victory.kind || award.encounterId !== victory.encounterId || award.receiptId !== victory.receiptId\r\n        || bosses.get(victory.encounterId) !== award) fail('boss victory does not match its XP award');\r\n    seenBossReceipts.add(victory.encounterId);\r\n  }\r\n  if (bosses.size !== seenBossReceipts.size) fail('boss XP award is missing its victory receipt');\r\n  const expectedEntitlements = Array.from({ length: Math.max(0, Math.floor(state.xp / XP_TUNING_PROPOSAL.xpPerLevel)) }, (_, index) => ({\r\n    id: `run-pack:${state.runId}:${index + 2}`, level: index + 2,\r\n  }));\r\n  if (JSON.stringify(state.packEntitlements) !== JSON.stringify(expectedEntitlements)) fail('pack entitlements do not match earned run levels');\r\n  return state;\r\n}\r\n\r\nexport function restoreRunXp(saved) { return assertState(clone(saved)); }\r\n\r\nexport function xpLevel(state) {\r\n  assertState(state);\r\n  return Math.floor(state.xp / XP_TUNING_PROPOSAL.xpPerLevel) + 1;\r\n}\r\n\r\n/** Award XP for a completed room. Boss kinds require an explicit victory acknowledgement. */\r\nexport function awardRoomXp(state, { roomId, receiptId, kind, encounterId = roomId, victory = false } = {}) {\r\n  assertState(state);\r\n  if (!ROOM_KINDS.has(kind)) throw new RangeError(`unknown room XP kind: ${kind}`);\r\n  if (!validId(roomId) || !validId(receiptId)) throw new TypeError('roomId and receiptId must be stable string ids');\r\n  const boss = kind === 'miniboss' || kind === 'mainboss';\r\n  if (boss && !validId(encounterId)) throw new TypeError('boss encounterId must be a stable string id');\r\n  if (boss && victory !== true) return { state: clone(state), awarded: false, duplicate: false, reason: 'boss-victory-required', xpAwarded: 0, levelsGained: [], packEntitlements: [] };\r\n  const priorRoom = state.awards.find(award => award.roomId === roomId);\r\n  if (priorRoom) {\r\n    const same = priorRoom.kind === kind && (boss ? priorRoom.encounterId === encounterId : priorRoom.encounterId == null);\r\n    return { state: clone(state), awarded: false, duplicate: same, reason: same ? 'room-already-awarded' : 'room-id-conflict', xpAwarded: 0, levelsGained: [], packEntitlements: [] };\r\n  }\r\n  if (state.awards.some(award => award.receiptId === receiptId)) fail('receiptId already belongs to another room');\r\n  if (boss && state.bossVictories.some(event => event.encounterId === encounterId)) {\r\n    const prior = state.bossVictories.find(event => event.encounterId === encounterId);\r\n    const same = prior.kind === kind;\r\n    return { state: clone(state), awarded: false, duplicate: same, reason: same ? 'encounter-already-awarded' : 'encounter-id-conflict', xpAwarded: 0, levelsGained: [], packEntitlements: [] };\r\n  }\r\n  const oldLevel = xpLevel(state), xpAwarded = awardAmount(kind), total = state.xp + xpAwarded;\r\n  if (!Number.isSafeInteger(total)) fail('run XP total exceeds safe integer range');\r\n  const next = clone(state), award = { roomId, receiptId, kind, xp: xpAwarded, ...(boss ? { encounterId } : {}) };\r\n  next.awards.push(award); next.xp = total;\r\n  if (boss) next.bossVictories.push({ encounterId, roomId, receiptId, kind });\r\n  const newLevel = Math.floor(total / XP_TUNING_PROPOSAL.xpPerLevel) + 1;\r\n  const levelsGained = Array.from({ length: newLevel - oldLevel }, (_, index) => oldLevel + index + 1);\r\n  const packEntitlements = levelsGained.map(level => ({ id: `run-pack:${state.runId}:${level}`, level }));\r\n  next.packEntitlements.push(...packEntitlements);\r\n  assertState(next);\r\n  return { state: next, awarded: true, duplicate: false, reason: null, xpAwarded, levelsGained, packEntitlements };\r\n}\r\n","modules/roguelite/shop-opening.mjs":"import {createMacheBoard} from '../portal/portal-board-mache.mjs';\nimport {classifyHand,selectActiveHand} from './hand-input.mjs';\nimport {physicalSound} from '../../audio/physical-sound.mjs';\n\n// Screen-space pinch and wrist rotation, not a depth/contact measurement.\nexport function observeShopHandle(state,{now,tracked,pinch=false,open=false,x=0,y=0,angle=0,paused=false}){\n const next={...state};\n if(!Number.isFinite(now)||!Number.isFinite(angle))throw new TypeError('Invalid handle observation');\n if(!tracked||paused||next.lastNow!==null&&(now<next.lastNow||now-next.lastNow>250)){next.armed=false;next.since=null;next.angle=null;}\n next.lastNow=now;\n if(paused||!tracked)return next;\n if(open){next.armed=true;next.since=null;next.angle=null;return next;}\n if(!pinch||x<.5||x>.9||y<.35||y>.75){next.since=null;next.angle=null;return next;}\n if(!next.armed)return next;\n if(next.since===null){next.since=now;next.angle=angle;}\n const turn=Math.abs(Math.atan2(Math.sin(angle-next.angle),Math.cos(angle-next.angle)));\n if(now-next.since>=400&&turn>=.55)next.complete=true;\n return next;\n}\nexport function mountShopOpening(host,{video,onComplete}={}){\n const element=document.createElement('section');element.className='run-shop-opening';\n element.innerHTML='<div class=\"shop-door-art\"></div><h2>PINCH + TURN</h2><p>Open your hand. Pinch the handle, then turn your wrist.</p><div class=\"shop-door-handle\">↻</div>';\n const handCanvas=document.createElement('canvas');element.append(handCanvas);host.append(element);\n const ctx=handCanvas.getContext('2d');let board=null,disposed=false,raw=null,opening=false,completed=false,completionPending=false,lastAttempt=-Infinity,elapsed=0,lastRenderNow=null;\n let gesture={armed:false,since:null,angle:null,lastNow:null,complete:false};\n // No press/release calls: the existing board paints only on mount/resize and\n // cannot trigger its unrelated drawing-secret event from this handle gate.\n const ready=createMacheBoard(element.firstElementChild,{flat:true}).then(value=>{if(disposed)value.dispose();else board=value;});\n return {element,ready,\n  observe(input,{paused=false}={}){\n   if(disposed||completed)return;raw=input;if(opening)return;\n   const active=selectActiveHand(input.landmarks||[]),hand=active?.points;\n   const kind=hand?classifyHand(hand,input.worldLandmarks?.[active.index],input.aspect):'other';\n   const x=hand?(hand[4].x+hand[8].x)/2:0,y=hand?(hand[4].y+hand[8].y)/2:0;\n   gesture=observeShopHandle(gesture,{now:input.now,tracked:!!hand&&input.tracked,pinch:kind==='pinch',open:kind==='open',x,y,angle:hand?Math.atan2(hand[9].y-hand[0].y,(hand[9].x-hand[0].x)*input.aspect):0,paused});\n   element.dataset.gripped=String(gesture.since!==null);\n   if(gesture.complete&&!disposed&&!opening){opening=true;element.dataset.opening='true';physicalSound.play('lever');}\n  },\n  render({paused=false,now=performance.now()}={}){\n   if(disposed)return;const w=element.clientWidth,h=element.clientHeight;\n   if(paused||!raw?.tracked||now-raw.now>250)lastRenderNow=null;\n   else if(opening&&!completed){\n    const dt=lastRenderNow===null?0:Math.min(100,Math.max(0,now-lastRenderNow));lastRenderNow=now;elapsed=Math.min(350,elapsed+dt);element.style.setProperty('--handle-open',String(elapsed/350));\n    // Retain the ceremony until the owner accepts it. A temporary pause or\n    // pending frame drain must not consume the only opening attempt.\n    if(elapsed>=350&&!completionPending&&now-lastAttempt>=250){\n     completionPending=true;lastAttempt=now;\n     Promise.resolve().then(()=>disposed?false:onComplete?.()).then(accepted=>{if(!disposed&&accepted!==false)completed=true;},()=>{}).finally(()=>{completionPending=false;});\n    }\n   }\n   if(handCanvas.width!==w||handCanvas.height!==h){handCanvas.width=w;handCanvas.height=h;}\n   ctx.clearRect(0,0,w,h);if(!video?.videoWidth||!raw||performance.now()-raw.now>250)return;\n   const aspect=video.videoWidth/video.videoHeight,fw=Math.min(w,h*aspect),fh=fw/aspect,ox=(w-fw)/2,oy=(h-fh)/2;\n   ctx.drawImage(video,ox,oy,fw,fh);ctx.globalCompositeOperation='destination-in';ctx.beginPath();\n   for(const points of raw.landmarks||[]){\n    const path=ids=>{ids.forEach((id,i)=>ctx[i?'lineTo':'moveTo'](ox+points[id].x*fw,oy+points[id].y*fh));ctx.closePath();};\n    path([0,1,2,5,9,13,17]);\n    for(const ids of [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20]])path(ids);\n   }\n   ctx.lineWidth=fw*.025;ctx.lineJoin=ctx.lineCap='round';ctx.stroke();ctx.fill();ctx.globalCompositeOperation='source-over';\n  },\n  dispose(){if(disposed)return;disposed=true;board?.dispose();element.remove();},\n };\n}\n","modules/roguelite/shop-state.mjs":"// Pure basket/checkout/theft/ambush state for the original Corgi shop.\r\n// No renderer, camera or recognizer: callers pass elapsed seconds and pause reasons.\r\n// ponytail: plain JSON run object; save is JSON.stringify, no migration layer until a second version exists.\r\nexport const PAUSE_REASONS = Object.freeze(['tutorial', 'tracking-lost', 'tracking-stale', 'camera-handoff', 'handoff', 'background', 'user']);\nexport const AMBUSH_LAP_COUNT = 3;\r\nexport const AMBUSH_LAP_SECONDS = 3;\r\nexport const AMBUSH_ATTACK_SECONDS = 10;\r\nconst MAX_SHELF = 64;\r\n\r\nconst clone = value => structuredClone(value);\r\nconst safeId = value => typeof value === 'string' && value.length > 0 && value.length <= 128;\r\nconst safeAmount = (value, min, name) => {\r\n  if (!Number.isSafeInteger(value) || value < min || value > 1_000_000) throw new RangeError(`${name} must be a safe integer`);\r\n  return value;\r\n};\r\n\r\nfunction checkedItems(shelf, bait) {\r\n  const ids = new Set();\r\n  const seen = item => {\r\n    if (!safeId(item.id)) throw new TypeError('each item needs a stable string id');\r\n    if (ids.has(item.id)) throw new RangeError(`duplicate item id: ${item.id}`);\r\n    ids.add(item.id);\r\n    return { id: item.id, price: safeAmount(item.price, 1, `item ${item.id} price`) };\r\n  };\r\n  const cleanShelf = shelf.map(item => (item ? seen(item) : null));\r\n  return { cleanShelf, cleanBait: bait ? seen(bait) : null };\r\n}\r\n\r\n// FNV-1a over the text: same run and shop always pick the same bait slot, different shops usually differ.\r\nfunction baitSlot(text, slots) {\r\n  let hash = 0x811c9dc5;\r\n  for (const ch of text) hash = Math.imul(hash ^ ch.codePointAt(0), 0x01000193) >>> 0;\r\n  return hash % slots;\r\n}\r\n\r\nconst result = (run, ok, reason = null, events = []) => ({ run, ok, reason, events });\r\nconst activeShop = (run, shopId) => run.activeShopId === shopId ? run.shops[shopId] : null;\r\n\r\nexport function createRun({ runId, coins, seed = 0 }) {\r\n  if (!safeId(runId)) throw new TypeError('runId must be a stable string id');\r\n  safeAmount(coins, 0, 'coins');\r\n  if (!(typeof seed === 'string' || Number.isSafeInteger(seed))) throw new TypeError('seed must be a string or safe integer');\r\n  return {\r\n    version: 1, runId, seed: String(seed), coins, inventory: [], shops: {}, receipts: {},\r\n    merchantAvailable: true, theftCount: 0, pauses: [], activeShopId: null,\r\n  };\r\n}\r\n\r\n// shelf: array of item-or-null, one entry per slot. Ambush ignores shelf contents and keeps only its length.\r\nexport function openShop(run, { shopId, shelf, bait = null }) {\r\n  if (!safeId(shopId)) throw new TypeError('shopId must be a stable string id');\r\n  if (!Array.isArray(shelf) || shelf.length < 1 || shelf.length > MAX_SHELF) throw new RangeError('shelf must hold 1-64 slots');\r\n  const next = clone(run);\r\n  next.activeShopId = shopId;\r\n  if (next.shops[shopId]) return result(next, false, 'exists');\r\n  const ambush = !next.merchantAvailable;\r\n  if (ambush && !bait) throw new TypeError('ambush shop needs one bait item');\r\n  const { cleanShelf, cleanBait } = checkedItems(ambush ? [] : shelf, bait);\r\n  const slots = Array.from({ length: shelf.length }, () => null);\r\n  if (ambush) {\r\n    const slot = baitSlot(`${next.seed}:${shopId}`, slots.length);\r\n    slots[slot] = cleanBait;\r\n  } else {\r\n    cleanShelf.forEach((item, slot) => { slots[slot] = item; });\r\n  }\r\n  next.shops[shopId] = { shopId, mode: ambush ? 'ambush' : 'normal', status: 'open', shelf: slots, basket: [], elapsed: 0 };\r\n  return result(next, true);\r\n}\r\n\r\nexport function grab(run, shopId, itemId) {\r\n  const next = clone(run);\r\n  if (next.pauses.length) return result(next, false, 'paused');\r\n  const shop = activeShop(next, shopId);\r\n  if (!shop || shop.status !== 'open') return result(next, false, 'closed');\r\n  const slot = shop.shelf.findIndex(item => item && item.id === itemId);\r\n  if (slot < 0) return result(next, false, 'not-on-shelf');\r\n  const item = shop.shelf[slot];\r\n  shop.shelf[slot] = null;\r\n  shop.basket.push({ ...item, slot });\r\n  return result(next, true);\r\n}\r\n\r\nexport function returnItem(run, shopId, itemId) {\r\n  const next = clone(run);\r\n  if (next.pauses.length) return result(next, false, 'paused');\r\n  const shop = activeShop(next, shopId);\r\n  if (!shop || shop.status !== 'open') return result(next, false, 'closed');\r\n  const index = shop.basket.findIndex(item => item.id === itemId);\r\n  if (index < 0) return result(next, false, 'not-in-basket');\r\n  const [item] = shop.basket.splice(index, 1);\r\n  shop.shelf[item.slot] = { id: item.id, price: item.price };\r\n  return result(next, true);\r\n}\r\n\r\nfunction restoreBasket(shop) {\r\n  for (const item of shop.basket) shop.shelf[item.slot] = { id: item.id, price: item.price };\r\n  shop.basket = [];\r\n}\r\n\r\n// Atomic all-basket purchase. Receipt key makes the purchase exactly-once across repeats and reloads.\r\nexport function checkout(run, shopId) {\r\n  const next = clone(run);\r\n  if (next.pauses.length) return result(next, false, 'paused');\r\n  const shop = activeShop(next, shopId);\r\n  if (!shop || shop.status !== 'open') return result(next, false, 'closed');\r\n  if (shop.mode !== 'normal') return result(next, false, 'dark-ambush');\r\n  if (shop.basket.length === 0) return result(next, !!shop.lastCheckoutReceipt, shop.lastCheckoutReceipt ? 'duplicate' : 'empty');\r\n  const key = `checkout:${shopId}:${JSON.stringify(shop.basket.map(item => item.id).sort())}`;\r\n  if (next.receipts[key]) return result(next, true, 'duplicate');\r\n  const total = shop.basket.reduce((sum, item) => sum + item.price, 0);\r\n  if (next.coins < total) {\r\n    restoreBasket(shop);\r\n    return result(next, false, 'insufficient-coins');\r\n  }\r\n  next.coins -= total;\r\n  for (const item of shop.basket) next.inventory.push({ id: item.id, price: item.price, source: 'bought', shopId });\r\n  next.receipts[key] = { type: 'checkout', shopId, total, items: shop.basket.map(item => item.id) };\r\n  shop.basket = [];\r\n  shop.lastCheckoutReceipt = key;\r\n  return result(next, true);\r\n}\r\n\r\n// Unpaid carry is stolen once per shop; the run's merchant is gone for later normal shops.\r\nfunction steal(next, shop, items, cause, events) {\r\n  const key = `theft:${shop.shopId}`;\r\n  if (next.receipts[key]) return;\r\n  for (const item of items) next.inventory.push({ id: item.id, price: item.price, source: 'stolen', shopId: shop.shopId });\r\n  next.receipts[key] = { type: 'theft', shopId: shop.shopId, cause, items: items.map(item => item.id) };\r\n  next.merchantAvailable = false;\r\n  next.theftCount += 1;\r\n  events.push({ type: 'theft', shopId: shop.shopId, cause, items: items.map(item => item.id) });\r\n}\r\n\r\n// Hand-open exit: unpaid basket is stolen; empty basket just leaves.\r\nexport function exitShop(run, shopId) {\r\n  const next = clone(run);\r\n  if (next.pauses.length) return result(next, false, 'paused');\r\n  const shop = activeShop(next, shopId);\r\n  if (!shop || shop.status !== 'open') return result(next, false, 'closed');\r\n  const events = [];\r\n  if (shop.basket.length === 0) {\r\n    shop.status = 'left';\r\n    return result(next, true, null, events);\r\n  }\r\n  const carried = shop.basket;\r\n  shop.basket = [];\r\n  shop.status = 'stolen';\r\n  steal(next, shop, carried, 'exit', events);\r\n  return result(next, true, null, events);\r\n}\r\n\r\n// Deliberate attack: scares the merchant and takes the whole finite stock plus the basket, once.\r\nexport function attackMerchant(run, shopId) {\r\n  const next = clone(run);\r\n  if (next.pauses.length) return result(next, false, 'paused');\r\n  const shop = activeShop(next, shopId);\r\n  if (!shop || shop.status !== 'open') return result(next, false, 'closed');\r\n  if (shop.mode !== 'normal') return result(next, false, 'dark-ambush');\r\n  const events = [];\r\n  const stock = shop.shelf.filter(Boolean);\r\n  const carried = shop.basket;\r\n  shop.shelf = shop.shelf.map(() => null);\r\n  shop.basket = [];\r\n  shop.status = 'stolen';\r\n  steal(next, shop, [...carried, ...stock], 'attack', events);\r\n  return result(next, true, null, events);\r\n}\r\n\r\nexport function pause(run, reason) {\r\n  if (!PAUSE_REASONS.includes(reason)) throw new TypeError(`unknown pause reason: ${reason}`);\r\n  const next = clone(run);\r\n  if (!next.pauses.includes(reason)) next.pauses.push(reason);\r\n  return result(next, true);\r\n}\r\n\r\nexport function resume(run, reason) {\r\n  const next = clone(run);\r\n  next.pauses = next.pauses.filter(item => item !== reason);\r\n  return result(next, true);\r\n}\r\n\r\nexport function crawlerView(shop) {\r\n  const lapsDone = Math.min(AMBUSH_LAP_COUNT, Math.floor(shop.elapsed / AMBUSH_LAP_SECONDS));\r\n  return {\r\n    lap: lapsDone,\r\n    lapProgress: lapsDone >= AMBUSH_LAP_COUNT ? 1 : (shop.elapsed % AMBUSH_LAP_SECONDS) / AMBUSH_LAP_SECONDS,\r\n    attackIn: Math.max(0, AMBUSH_ATTACK_SECONDS - shop.elapsed),\r\n  };\r\n}\r\n\r\n// Caller-supplied elapsed seconds only. Paused time is dropped, so resume never jumps the clock.\r\nexport function advanceTime(run, seconds) {\r\n  if (!Number.isFinite(seconds) || seconds < 0) throw new RangeError('seconds must be finite and non-negative');\r\n  const next = clone(run);\r\n  const events = [];\r\n  if (next.pauses.length > 0) return result(next, true, 'paused', events);\r\n  for (const shop of Object.values(next.shops)) {\r\n    if (shop.shopId !== next.activeShopId) continue;\r\n    if (shop.mode !== 'ambush' || shop.status !== 'open') continue;\r\n    shop.elapsed = Math.min(AMBUSH_ATTACK_SECONDS, shop.elapsed + seconds);\r\n    // Reuse the same finite bait; move only while it remains on a shelf, never out of a player's hand.\r\n    const oldSlot = shop.shelf.findIndex(Boolean);\r\n    const slot = (baitSlot(`${next.seed}:${shop.shopId}`, shop.shelf.length)\r\n      + Math.min(AMBUSH_LAP_COUNT, Math.floor(shop.elapsed / AMBUSH_LAP_SECONDS))) % shop.shelf.length;\r\n    if (oldSlot >= 0 && slot !== oldSlot && shop.elapsed < AMBUSH_ATTACK_SECONDS) {\r\n      shop.shelf[slot] = shop.shelf[oldSlot];\r\n      shop.shelf[oldSlot] = null;\r\n      events.push({ type: 'bait-moved', shopId: shop.shopId, slot });\r\n    }\r\n    if (shop.elapsed >= AMBUSH_ATTACK_SECONDS) {\r\n      restoreBasket(shop);\r\n      shop.status = 'ambushed';\r\n      events.push({ type: 'ambush-attack', shopId: shop.shopId });\r\n    }\r\n  }\r\n  return result(next, true, null, events);\r\n}\r\n\r\nexport function saveRun(run) {\r\n  return JSON.stringify(run);\r\n}\r\n\r\nexport function loadRun(json) {\n  const run = JSON.parse(json);\n  const plain = value => value !== null && typeof value === 'object' && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;\n  const keysAre = (value, expected) => plain(value) && Object.keys(value).every(key => expected.includes(key) && !['__proto__', 'constructor', 'prototype'].includes(key));\n  const exactKeys = (value, expected) => keysAre(value, expected) && expected.every(key => Object.hasOwn(value, key));\n  const itemOk = (item, inBasket = false) => {\n    if (!plain(item) || !safeId(item.id) || !Number.isSafeInteger(item.price) || item.price < 1 || item.price > 1_000_000) return false;\n    const keys = inBasket ? ['id', 'price', 'slot'] : ['id', 'price'];\n    return Object.keys(item).length === keys.length && Object.keys(item).every(key => keys.includes(key)) &&\n      (!inBasket || Number.isSafeInteger(item.slot) && item.slot >= 0 && item.slot < MAX_SHELF);\n  };\n  const validRun = () => {\n    if (!exactKeys(run, ['version', 'runId', 'seed', 'coins', 'inventory', 'shops', 'receipts', 'merchantAvailable', 'theftCount', 'pauses', 'activeShopId']) ||\n        run.version !== 1 || !safeId(run.runId) || typeof run.seed !== 'string' || !Number.isSafeInteger(run.coins) || run.coins < 0 || run.coins > 1_000_000 ||\n        typeof run.merchantAvailable !== 'boolean' || !Number.isSafeInteger(run.theftCount) || run.theftCount < 0 ||\n        !Array.isArray(run.inventory) || !Array.isArray(run.pauses) || !keysAre(run.shops, Object.keys(run.shops ?? {})) ||\n        !keysAre(run.receipts, Object.keys(run.receipts ?? {})) || !(run.activeShopId === null || safeId(run.activeShopId))) return false;\n    const allowedPauses = new Set(PAUSE_REASONS);\n    if (new Set(run.pauses).size !== run.pauses.length || run.pauses.some(reason => !allowedPauses.has(reason))) return false;\n    if (run.activeShopId !== null && !Object.hasOwn(run.shops, run.activeShopId)) return false;\n    for (const item of run.inventory) {\n      if (!exactKeys(item, ['id', 'price', 'source', 'shopId']) || !itemOk({ id: item.id, price: item.price }) ||\n          !['bought', 'stolen'].includes(item.source) || !safeId(item.shopId) || !Object.hasOwn(run.shops, item.shopId)) return false;\n    }\n    for (const [id, shop] of Object.entries(run.shops)) {\n      if (!safeId(id) || !exactKeys(shop, ['shopId', 'mode', 'status', 'shelf', 'basket', 'elapsed']) &&\n          !exactKeys(shop, ['shopId', 'mode', 'status', 'shelf', 'basket', 'elapsed', 'lastCheckoutReceipt'])) return false;\n      if (shop.shopId !== id || !['normal', 'ambush'].includes(shop.mode) || !['open', 'left', 'stolen', 'ambushed'].includes(shop.status) ||\n          !Array.isArray(shop.shelf) || shop.shelf.length < 1 || shop.shelf.length > MAX_SHELF || !Array.isArray(shop.basket) ||\n          !Number.isFinite(shop.elapsed) || shop.elapsed < 0 || shop.elapsed > AMBUSH_ATTACK_SECONDS ||\n          (shop.mode === 'normal' && shop.elapsed !== 0) ||\n          (shop.status !== 'open' && shop.basket.length !== 0) ||\n          (shop.lastCheckoutReceipt !== undefined && (!safeId(shop.lastCheckoutReceipt) || !Object.hasOwn(run.receipts, shop.lastCheckoutReceipt) ||\n            run.receipts[shop.lastCheckoutReceipt]?.type !== 'checkout' || run.receipts[shop.lastCheckoutReceipt]?.shopId !== id))) return false;\n      if (shop.shelf.some(item => item !== null && !itemOk(item)) || shop.basket.some(item => !itemOk(item, true))) return false;\n      const basketSlots = new Set();\n      for (const item of shop.basket) {\n        if (item.slot >= shop.shelf.length || basketSlots.has(item.slot) || shop.shelf[item.slot] !== null) return false;\n        basketSlots.add(item.slot);\n      }\n      const ids = [...shop.shelf.filter(Boolean).map(item => item.id), ...shop.basket.map(item => item.id)];\n      if (new Set(ids).size !== ids.length) return false;\n    }\n    let theftReceipts = 0;\n    for (const [key, receipt] of Object.entries(run.receipts)) {\n      if (!safeId(key) || !plain(receipt) || !safeId(receipt.shopId) || !Object.hasOwn(run.shops, receipt.shopId)) return false;\n      if (receipt.type === 'checkout') {\n        if (!exactKeys(receipt, ['type', 'shopId', 'total', 'items']) || !Number.isSafeInteger(receipt.total) || receipt.total < 1 ||\n            !Array.isArray(receipt.items) || receipt.items.length < 1 || receipt.items.some(item => !safeId(item)) || new Set(receipt.items).size !== receipt.items.length ||\n            key !== `checkout:${receipt.shopId}:${JSON.stringify([...receipt.items].sort())}`) return false;\n      } else if (receipt.type === 'theft') {\n        theftReceipts++;\n        if (!exactKeys(receipt, ['type', 'shopId', 'cause', 'items']) || !['exit', 'attack'].includes(receipt.cause) ||\n            !Array.isArray(receipt.items) || receipt.items.some(item => !safeId(item)) || new Set(receipt.items).size !== receipt.items.length || key !== `theft:${receipt.shopId}`) return false;\n      } else return false;\n    }\n    for (const item of run.inventory) {\n      const evidenced = Object.values(run.receipts).some(receipt => receipt.shopId === item.shopId &&\n        receipt.items.includes(item.id) && receipt.type === (item.source === 'bought' ? 'checkout' : 'theft'));\n      if (!evidenced) return false;\n    }\n    return theftReceipts === run.theftCount && run.merchantAvailable === (theftReceipts === 0);\n  };\n  if (!validRun()) throw new TypeError('not a valid shop run save');\n  return run;\n}\n","modules/roguelite/treasure-room-view.mjs":"import * as T from 'three';\r\nimport {BUILDING_BY_ID} from '../coach-world/catalog.mjs';\r\nimport {createSurfaceTexture} from '../../arcade/runner/worlds/textures.mjs';\r\nimport {createViewTour,cameraOverview,createAcknowledgedMotion} from './room-intro.mjs';\r\n\r\nfunction text(doc,tag,className,value){\r\n const node=doc.createElement(tag);node.className=className;node.textContent=value;return node;\r\n}\r\nconst clamp=value=>Math.max(0,Math.min(1,Number(value)||0));\r\n\r\n// Borrow the host renderer. Geometry is presentation only; no local chase clock.\r\nfunction buildLane(THREE,scene){\r\n const group=new THREE.Group();group.name='treasure-lane';\r\n const geos=new Set(),mats=new Set(),maps=new Set();\r\n const material=(color,kind)=>{\r\n  const map=kind?createSurfaceTexture(THREE,kind):null;\r\n  if(map){map.magFilter=THREE.NearestFilter;maps.add(map);}\r\n  const mat=new THREE.MeshBasicMaterial({color,map});mats.add(mat);return mat;\r\n };\r\n const stone=material(0x51463f,'stone'),floor=material(0x8a7050,'stone'),dark=material(0x211b1e),gold=material(0xf4be56,'gold'),wood=material(0xa85a29,'wood'),iron=material(0x9f7c70,'metal'),red=material(0xff614a);\r\n const box=(w,h,d,mat,x,y,z,parent=group)=>{\r\n  const geo=new THREE.BoxGeometry(w,h,d);geos.add(geo);\r\n  const mesh=new THREE.Mesh(geo,mat);mesh.position.set(x,y,z);parent.add(mesh);return mesh;\r\n };\r\n box(7,.3,34,floor,0,-.2,-13);\r\n for(const side of [-1,1]){\r\n  box(.4,6,34,stone,side*3.7,2.8,-13);\r\n  box(.08,.08,34,gold,side*3.44,.1,-13);\r\n  for(let z=2;z>-29;z-=4){\r\n   box(.45,4.6,.6,dark,side*3.4,2.2,z);\r\n   box(.18,.65,.18,gold,side*3.1,2.5,z);\r\n  }\r\n }\r\n for(let z=0;z>-29;z-=2){box(6.6,.012,.035,dark,0,-.035,z);box(.3,.015,.4,gold,0,-.025,z);}\r\n box(7,6,.3,dark,0,2.8,-29);box(7,.3,34,dark,0,5.8,-13);\r\n const chest=new THREE.Group();chest.name='treasure-destination-chest';chest.position.z=-24;group.add(chest);\r\n box(2,.85,1.3,wood,0,.5,0,chest);\r\n const lid=new THREE.Group();lid.position.set(0,.95,-.65);chest.add(lid);\r\n box(2.08,.32,1.4,wood,0,.1,.65,lid);\r\n for(const x of [-.7,.7]){box(.13,.9,1.34,gold,x,.52,0,chest);box(.14,.35,1.42,gold,x,.1,.65,lid);}\r\n box(.3,.35,.1,gold,0,.83,.7,chest);box(.1,.15,.12,dark,0,.85,.76,chest);\r\n const trap=new THREE.Group();trap.name='treasure-closing-spike-gate';group.add(trap);\r\n box(6.8,.3,.45,iron,0,0,0,trap);\r\n const spikeGeo=new THREE.ConeGeometry(.22,.75,4);geos.add(spikeGeo);\r\n for(let x=-3;x<=3;x+=.6){\r\n  box(.12,1.6,.12,iron,x,-.85,0,trap);\r\n  const spike=new THREE.Mesh(spikeGeo,iron);spike.rotation.z=Math.PI;spike.position.set(x,-2,0);trap.add(spike);\r\n }\r\n for(const x of [-3.15,3.15])box(.12,1.8,.2,red,x,-.8,.28,trap);\r\n scene.add(group);\r\n return {group,chest,lid,trap,dispose(){group.removeFromParent();for(const g of geos)g.dispose();for(const m of mats)m.dispose();for(const map of maps)map.dispose();group.clear();}};\r\n}\r\n\r\n/** Only ACK arrival/open receipts can enable the accessible claim control. */\r\nexport function mountTreasureRoomView({host,onOpen=()=>{},THREE=T,scene,camera,renderer,reducedMotion=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches??false}={}){\r\n if(!host||typeof host.append!=='function')throw new TypeError('treasure room needs a host');\r\n const doc=host.ownerDocument??globalThis.document;\r\n if(!doc||typeof doc.createElement!=='function')throw new TypeError('treasure room needs a document');\r\n const element=doc.createElement('section');element.className='treasure-room-view';element.setAttribute('aria-label','Treasure chase');\r\n Object.assign(element.style,{position:'absolute',inset:'0',zIndex:'24',pointerEvents:'none',boxSizing:'border-box'});\r\n const style=doc.createElement('style');style.textContent=`\r\n .treasure-room-view{--treasure-danger:0;box-shadow:inset 0 0 60px rgba(255,53,26,var(--treasure-danger));color:#fff3cf}\r\n .treasure-room-card{position:absolute;left:12px;right:12px;bottom:max(92px,env(safe-area-inset-bottom));max-width:440px;margin:auto;padding:12px;background:#211c20ed;border:2px solid #d5ad61;box-shadow:4px 4px 0 #080d13;font:600 clamp(14px,3.5vw,17px)/1.25 Pixel,monospace;pointer-events:auto;box-sizing:border-box;overflow-wrap:anywhere}\r\n .treasure-room-card h2{font:800 clamp(20px,5vw,27px)/1.05 Pixel,monospace;letter-spacing:.06em;margin:0 0 6px;color:#ffd078}\r\n .treasure-room-card p{margin:4px 0}.treasure-room-progress{width:100%;height:10px;accent-color:#ffc85b}\r\n .treasure-room-open{width:100%;min-height:44px;border:2px solid #ffe4a1;border-radius:2px;background:#ffc85b;color:#211a12;font:800 18px Pixel,monospace;padding:8px;cursor:pointer}\r\n .treasure-room-open:focus-visible{outline:3px solid #fff;outline-offset:3px}.treasure-room-open:disabled{opacity:.5;cursor:default}\r\n .treasure-room-reward:empty{display:none}.treasure-room-view[data-status=caught]{box-shadow:inset 0 0 100px #ac2816}\r\n @media(max-height:450px){.treasure-room-card{bottom:50px;left:auto;width:44%;font-size:13px}.treasure-room-card h2{font-size:18px}}\r\n `;\r\n const card=doc.createElement('div');card.className='treasure-room-card';\r\n const title=text(doc,'h2','treasure-room-title','TREASURE CHASE');\r\n const instruction=text(doc,'p','treasure-room-instruction','Jog in place. Reach the chest before the spikes close.');\r\n const progress=doc.createElement('progress');progress.className='treasure-room-progress';progress.max=1;progress.value=0;progress.setAttribute('aria-label','Jogging progress');\r\n const readout=text(doc,'p','treasure-room-readout','0% · Trap gap 8.0s');\r\n const chest=text(doc,'button','treasure-room-open','Open chest');chest.type='button';chest.disabled=true;chest.setAttribute('aria-label','Open chest');\r\n const reward=text(doc,'p','treasure-room-reward','');reward.setAttribute('role','status');reward.setAttribute('aria-live','polite');\r\n card.append(title,instruction,progress,readout,chest,reward);element.append(style,card);host.append(element);\r\n let disposed=false,current=null,enabled=false,opening=false,pausedState=true,identity=null,lastClock=null,lane=null,old=null;\r\n const motion=createAcknowledgedMotion();\r\n const tour=createViewTour((p,camera)=>cameraOverview(camera,p,{wide:1.4,height:1.4,forward:-9}));\r\n if(scene&&camera&&renderer){\r\n  old={background:scene.background,fog:scene.fog,children:scene.children.filter(child=>!child.isLight).map(child=>[child,child.visible]),position:camera.position.clone(),quaternion:camera.quaternion.clone(),fov:camera.fov,near:camera.near,far:camera.far};\r\n  try{lane=buildLane(THREE,scene);for(const [child]of old.children)child.visible=false;scene.background=new THREE.Color(0x17131a);scene.fog=new THREE.Fog(0x17131a,8,35);}\r\n  catch(error){element.remove();throw error;}\r\n }\r\n chest.onclick=()=>{\r\n  if(disposed||!enabled||opening||chest.disabled||tour.active)return;\r\n  opening=true;chest.disabled=true;\r\n  Promise.resolve().then(()=>onOpen(current?.chestManifest?.[0]?.id)).catch(()=>{}).finally(()=>{opening=false;if(!disposed)chest.disabled=!enabled;});\r\n };\r\n function update(run,{active=false,paused=false,confirmedBlueprints=[],identity:ownerIdentity}={}){\r\n  if(disposed)return;\r\n  current=run&&typeof run==='object'?run:null;pausedState=paused||!active;\r\n  const required=Math.max(.001,Number(current?.requiredJogSeconds)||6),jog=Math.max(0,Math.min(required,Number(current?.jogSeconds)||0));\r\n  progress.value=jog/required;\r\n  const gap=Math.max(0,Number(current?.trapGapSeconds)||0),status=current?.status;\r\n  const nextIdentity=`${ownerIdentity??''}:${current?.encounterId}:${status}`;\r\n  if(identity!==null&&(identity!==nextIdentity||current?.elapsedSeconds<lastClock))tour.cancel();\r\n  identity=nextIdentity;lastClock=current?.elapsedSeconds;\r\n  motion.push({fraction:jog/required,gap:clamp(gap/(current?.startingTrapGapSeconds||8))},{identity,clock:current?.elapsedSeconds,snap:pausedState});\r\n  element.dataset.status=status??'waiting';\r\n  readout.textContent=status==='caught'?'The spikes closed.':status==='arrived'?'Chest reached.':`${Math.floor(jog/required*100)}% · Trap gap ${gap.toFixed(1)}s`;\r\n  instruction.hidden=status!=='chasing';\r\n  const chestId=current?.chestManifest?.[0]?.id;\r\n  const opened=Array.isArray(current?.openedChests)&&current.openedChests.some(item=>item?.chestId===chestId);\r\n  enabled=active===true&&!paused&&status==='arrived'&&!opened&&typeof chestId==='string';\r\n  chest.disabled=!enabled||opening||tour.active;chest.hidden=status!=='arrived'||opened;\r\n  reward.textContent='';\r\n  if(opened){\r\n   const rewardData=current.chestManifest?.[0]?.rewards?.[0];\r\n   const hasConfirmed=Array.isArray(confirmedBlueprints)&&confirmedBlueprints.some(item=>item===rewardData?.buildingId||item===`blueprint:${rewardData?.buildingId}`||item?.buildingId===rewardData?.buildingId||item?.id===`blueprint:${rewardData?.buildingId}`);\r\n   if(hasConfirmed&&rewardData?.type==='blueprint')reward.textContent=`Blueprint confirmed: ${BUILDING_BY_ID[rewardData.buildingId]?.label??rewardData.buildingId}`;\r\n   else reward.textContent='Chest opened. Waiting for your saved blueprint.';\r\n  }\r\n  if(lane)lane.lid.rotation.x=opened?-1.05:0;\r\n }\r\n function render({dt=0,paused=false}={}){\r\n  if(disposed||!lane)return;\r\n  const values=motion.sample(dt,{paused:paused||pausedState,reducedMotion});\r\n  if(!values)return;\r\n  camera.position.set(0,1.65,-values.fraction*20);camera.quaternion.identity();camera.fov=65;camera.near=.08;camera.far=65;camera.updateProjectionMatrix();\r\n  lane.trap.position.set(0,2.15+values.gap*3.2,camera.position.z-5);\r\n  lane.trap.visible=current?.status!=='arrived';\r\n  element.style.setProperty('--treasure-danger',String((1-values.gap)*.7));\r\n  const restore=tour.apply(camera);\r\n  try{renderer.render(scene,camera);}finally{restore?.();}\r\n  chest.disabled=!enabled||opening||tour.active||paused;\r\n }\r\n function dispose(){\r\n  if(disposed)return;disposed=true;tour.dispose();current=null;enabled=false;chest.onclick=null;lane?.dispose();\r\n  if(old){for(const [child,visible]of old.children)child.visible=visible;scene.background=old.background;scene.fog=old.fog;camera.position.copy(old.position);camera.quaternion.copy(old.quaternion);camera.fov=old.fov;camera.near=old.near;camera.far=old.far;camera.updateProjectionMatrix();camera.updateMatrixWorld(true);}\r\n  element.remove();\r\n }\r\n return {element,update,render,dispose,intro:options=>tour.intro({reducedMotion,...options}),ready:!!lane,readyPromise:Promise.resolve(),get group(){return lane?.group;},get disposed(){return disposed;}};\r\n}\r\n","modules/roguelite/treasure-room.mjs":"// Pure state adapter for optional jog-in-place treasure rooms. Durable writes and rendering belong to callers.\r\nimport { BUILDING_BY_ID, MAX_LEVEL } from '../coach-world/catalog.mjs';\r\nimport { LEDGER_KINDS } from '../../unlock-ledger.mjs';\r\n\r\nexport const TREASURE_CHASE_TUNING_PROPOSAL = Object.freeze({\r\n  requiredJogSeconds: 6,\r\n  startingTrapGapSeconds: 8,\r\n  trapCloseSecondsPerSecondIdle: 0.5,\r\n  maxStepSeconds: 0.1,\r\n});\r\nconst ID = /^[A-Za-z0-9:_-]{1,128}$/;\r\nconst clone = value => structuredClone(value);\r\nconst validId = value => typeof value === 'string' && ID.test(value);\r\nconst fail = message => { throw new RangeError(message); };\r\n\r\nfunction validateRewards(rewards) {\r\n  if (!Array.isArray(rewards) || rewards.length > 64) throw new TypeError('rewards must be a finite array');\r\n  return rewards.map(reward => {\r\n    if (reward?.type === 'blueprint') {\r\n      if (!Object.hasOwn(BUILDING_BY_ID, reward.buildingId)) fail(`unknown blueprint building: ${reward.buildingId}`);\r\n      if (reward.tier != null && (!Number.isSafeInteger(reward.tier) || reward.tier < 1 || reward.tier > MAX_LEVEL)) fail(`blueprint tier must be an integer from 1 to ${MAX_LEVEL}`);\r\n      return { type: 'blueprint', buildingId: reward.buildingId, ...(reward.tier == null ? {} : { tier: reward.tier }) };\r\n    }\r\n    if (reward?.type === 'drop') {\r\n      if (!LEDGER_KINDS.includes(reward.kind)) fail(`unknown drop kind: ${reward.kind}`);\r\n      if (!validId(reward.id)) fail('drop id must be a stable string id');\r\n      return { type: 'drop', kind: reward.kind, id: reward.id };\r\n    }\r\n    fail('reward must be a blueprint or existing ledger drop');\r\n  });\r\n}\r\n\r\nfunction validateManifest(manifest) {\r\n  if (!Array.isArray(manifest) || manifest.length < 1 || manifest.length > 32) throw new TypeError('chestManifest must contain 1 to 32 chests');\r\n  const ids = new Set();\r\n  return manifest.map(chest => {\r\n    if (!chest || !validId(chest.id) || ids.has(chest.id)) fail('chest IDs must be valid and unique');\r\n    ids.add(chest.id);\r\n    return { id: chest.id, rewards: validateRewards(chest.rewards) };\r\n  });\r\n}\r\n\r\nexport function createTreasureChase({ encounterId, chestManifest, requiredJogSeconds = TREASURE_CHASE_TUNING_PROPOSAL.requiredJogSeconds,\r\n  startingTrapGapSeconds = TREASURE_CHASE_TUNING_PROPOSAL.startingTrapGapSeconds,\r\n  trapCloseRate = TREASURE_CHASE_TUNING_PROPOSAL.trapCloseSecondsPerSecondIdle } = {}) {\r\n  if (!validId(encounterId)) throw new TypeError('encounterId must be a stable string id');\r\n  for (const [name, value] of Object.entries({ requiredJogSeconds, startingTrapGapSeconds, trapCloseRate })) {\r\n    if (!Number.isFinite(value) || value <= 0 || value > 3600) throw new RangeError(`${name} must be finite and positive`);\r\n  }\r\n  return { version: 1, encounterId, chestManifest: validateManifest(chestManifest), requiredJogSeconds,\r\n    startingTrapGapSeconds, trapCloseRate, trapGapSeconds: startingTrapGapSeconds,\r\n    jogSeconds: 0, elapsedSeconds: 0, status: 'chasing', pauseReasons: [], openedChests: [] };\r\n}\r\n\r\nfunction checkRun(run) {\r\n  if (!run || run.version !== 1 || !validId(run.encounterId) || !['chasing', 'arrived', 'caught'].includes(run.status)\r\n      || !Array.isArray(run.chestManifest) || !Array.isArray(run.pauseReasons) || !Array.isArray(run.openedChests)\r\n      || !Number.isFinite(run.jogSeconds) || run.jogSeconds < 0 || !Number.isFinite(run.elapsedSeconds) || run.elapsedSeconds < 0\r\n      || !Number.isFinite(run.trapGapSeconds) || run.trapGapSeconds < 0\r\n      || !Number.isFinite(run.requiredJogSeconds) || run.requiredJogSeconds <= 0\r\n      || !Number.isFinite(run.startingTrapGapSeconds) || run.startingTrapGapSeconds <= 0\r\n      || !Number.isFinite(run.trapCloseRate) || run.trapCloseRate <= 0\r\n      || run.jogSeconds > run.requiredJogSeconds || run.trapGapSeconds > run.startingTrapGapSeconds\r\n      || run.pauseReasons.some(reason => typeof reason !== 'string' || !reason)) throw new TypeError('invalid treasure chase state');\r\n  const manifest = validateManifest(run.chestManifest);\r\n  const opened = new Set(), receipts = new Set();\r\n  for (const event of run.openedChests) {\r\n    if (!event || !validId(event.chestId) || opened.has(event.chestId) || !validId(event.receiptId)\r\n        || receipts.has(event.receiptId)) fail('invalid or duplicate opened chest');\r\n    const source = manifest.find(chest => chest.id === event.chestId);\r\n    if (!source || JSON.stringify(validateRewards(event.rewards)) !== JSON.stringify(source.rewards)) fail('opened chest rewards do not match the manifest');\r\n    opened.add(event.chestId); receipts.add(event.receiptId);\r\n  }\r\n  return run;\r\n}\r\n\r\nexport function restoreTreasureChase(saved) { return checkRun(clone(saved)); }\r\n\r\n/** Caller supplies tracked in-place jogging and pause reasons; wall time never advances the chase. */\r\nexport function stepTreasureChase(run, { deltaSeconds, jogging = false, pauseReasons = [] } = {}) {\r\n  checkRun(run);\r\n  if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) throw new RangeError('deltaSeconds must be finite and nonnegative');\r\n  if (typeof jogging !== 'boolean' || !Array.isArray(pauseReasons) || pauseReasons.some(reason => typeof reason !== 'string' || !reason)) throw new TypeError('invalid jogging or pause state');\r\n  const next = clone(run);\r\n  next.pauseReasons = [...new Set(pauseReasons)];\r\n  if (next.status !== 'chasing' || next.pauseReasons.length || deltaSeconds === 0) return { run: next, events: [], mainRoomCountDelta: 0, statChoicesGranted: 0 };\r\n  const dt = Math.min(deltaSeconds, TREASURE_CHASE_TUNING_PROPOSAL.maxStepSeconds);\r\n  next.elapsedSeconds += dt;\r\n  if (jogging) {\r\n    next.jogSeconds = Math.min(next.requiredJogSeconds, next.jogSeconds + dt);\r\n    next.trapGapSeconds = Math.min(next.startingTrapGapSeconds, next.trapGapSeconds + dt);\r\n  } else {\r\n    next.trapGapSeconds = Math.max(0, next.trapGapSeconds - dt * next.trapCloseRate);\r\n  }\r\n  const events = [];\r\n  if (next.jogSeconds + 1e-9 >= next.requiredJogSeconds) { next.jogSeconds = next.requiredJogSeconds; next.status = 'arrived'; events.push({ type: 'treasure-arrived' }); }\r\n  else if (next.trapGapSeconds <= 0) { next.status = 'caught'; events.push({ type: 'trap-caught' }); }\r\n  return { run: next, events, mainRoomCountDelta: 0, statChoicesGranted: 0 };\r\n}\r\n\r\nexport function openTreasureChest(run, { chestId, receiptId } = {}) {\r\n  checkRun(run);\r\n  if (run.pauseReasons.length) return { run: clone(run), opened: false, duplicate: false, reason: 'paused' };\r\n  if (run.status !== 'arrived') return { run: clone(run), opened: false, duplicate: false, reason: 'chase-not-complete' };\r\n  const chest = run.chestManifest.find(item => item.id === chestId);\r\n  if (!chest) return { run: clone(run), opened: false, duplicate: false, reason: 'unknown-chest' };\r\n  const prior = run.openedChests.find(event => event.chestId === chestId);\r\n  if (prior) return { run: clone(run), opened: false, duplicate: true, reason: 'already-opened', chest: clone(prior) };\r\n  if (!validId(receiptId)) throw new TypeError('receiptId must be a stable string id');\r\n  if (run.openedChests.some(event => event.receiptId === receiptId)) fail('receiptId has already been used');\r\n  const event = { chestId, receiptId, rewards: clone(chest.rewards) };\r\n  const next = clone(run); next.openedChests.push(event);\r\n  return { run: next, opened: true, duplicate: false, reason: null, chest: clone(event) };\r\n}\r\n\r\nexport function createTreasureCampaignInventory() { return { version: 1, blueprints: [], drops: [], grants: [] }; }\r\n\r\nfunction checkInventory(inventory) {\r\n  if (!inventory || inventory.version !== 1 || !Array.isArray(inventory.blueprints) || !Array.isArray(inventory.drops) || !Array.isArray(inventory.grants)) throw new TypeError('invalid treasure campaign inventory');\r\n  const receipts = new Set(), sources = new Set(), blueprintKeys = new Set(), dropKeys = new Set();\r\n  const earnedBlueprints = new Set(), earnedDrops = new Set();\r\n  for (const grant of inventory.grants) {\r\n    const source = `${grant.encounterId}/${grant.chestId}`;\r\n    if (!validId(grant.encounterId) || !validId(grant.chestId) || !validId(grant.receiptId) || receipts.has(grant.receiptId) || sources.has(source)) fail('invalid or duplicate campaign treasure grant');\r\n    receipts.add(grant.receiptId); sources.add(source);\r\n    for (const reward of validateRewards(grant.rewards)) {\r\n      if (reward.type === 'blueprint') earnedBlueprints.add(`${reward.buildingId}:${reward.tier ?? 1}`);\r\n      else earnedDrops.add(`${reward.kind}:${reward.id}`);\r\n    }\r\n  }\r\n  for (const blueprint of inventory.blueprints) {\r\n    validateRewards([{ type: 'blueprint', ...blueprint }]);\r\n    const key = `${blueprint.buildingId}:${blueprint.tier ?? 1}`;\r\n    if (blueprintKeys.has(key)) fail('duplicate campaign blueprint');\r\n    blueprintKeys.add(key);\r\n  }\r\n  for (const drop of inventory.drops) {\r\n    validateRewards([{ type: 'drop', ...drop }]);\r\n    const key = `${drop.kind}:${drop.id}`;\r\n    if (dropKeys.has(key)) fail('duplicate campaign drop');\r\n    dropKeys.add(key);\r\n  }\r\n  if (blueprintKeys.size !== earnedBlueprints.size || [...blueprintKeys].some(key => !earnedBlueprints.has(key))\r\n      || dropKeys.size !== earnedDrops.size || [...dropKeys].some(key => !earnedDrops.has(key))) fail('campaign inventory does not match treasure grants');\r\n  return inventory;\r\n}\r\n\r\nexport function restoreTreasureCampaignInventory(saved) { return checkInventory(clone(saved)); }\r\n\r\n/** Link a validated chase's opened chest to campaign data; storage/persistence remains the caller's job. */\r\nexport function grantTreasureChest(inventory, { run, chestId } = {}) {\r\n  checkInventory(inventory);\r\n  checkRun(run);\r\n  const encounterId = run.encounterId;\r\n  const chest = run.openedChests.find(event => event.chestId === chestId);\r\n  if (run.status !== 'arrived' || !chest) throw new TypeError('a completed chase and opened chest are required');\r\n  const prior = inventory.grants.find(grant => grant.encounterId === encounterId && grant.chestId === chest.chestId);\r\n  if (prior) return { inventory: clone(inventory), granted: false, duplicate: true, reason: 'already-granted', grant: clone(prior) };\r\n  if (inventory.grants.some(grant => grant.receiptId === chest.receiptId)) fail('receiptId has already been used');\r\n  const rewards = validateRewards(chest.rewards);\r\n  const next = clone(inventory), added = [];\r\n  for (const reward of rewards) {\r\n    if (reward.type === 'blueprint') {\r\n      const key = `${reward.buildingId}:${reward.tier ?? 1}`;\r\n      if (!next.blueprints.some(item => `${item.buildingId}:${item.tier ?? 1}` === key)) { next.blueprints.push({ buildingId: reward.buildingId, ...(reward.tier == null ? {} : { tier: reward.tier }) }); added.push(reward); }\r\n    } else if (!next.drops.some(item => item.kind === reward.kind && item.id === reward.id)) { next.drops.push({ kind: reward.kind, id: reward.id }); added.push(reward); }\r\n  }\r\n  const grant = { encounterId, chestId: chest.chestId, receiptId: chest.receiptId, rewards };\r\n  next.grants.push(grant); checkInventory(next);\r\n  return { inventory: next, granted: added.length > 0, duplicate: false, reason: null, grant: clone(grant), added: clone(added), mainRoomCountDelta: 0, statChoicesGranted: 0 };\r\n}\r\n","modules/roguelite/tutorial-guide.mjs":"import {drawFigure,projectCoach} from '../dance/ui/skeleton.mjs';\r\n\r\n// Reuse the Dance coach glyph. The room owns recognition: this component only demonstrates and pauses it.\r\nexport function mountTutorialGuide(host,{steps,onComplete=()=>{},onPause=()=>{}}){\r\n if(!Array.isArray(steps)||!steps.length)throw new TypeError('A tutorial needs illustrated steps.');\r\n const overlay=document.createElement('section');overlay.className='run-tutorial';overlay.setAttribute('aria-label','Room tutorial');\r\n const style=document.createElement('style');style.textContent=`\r\n .run-tutorial{position:absolute;inset:0;z-index:30;display:grid;grid-template-rows:auto minmax(100px,1fr) auto;gap:8px;padding:clamp(12px,3vw,28px);background:#131321ed;color:#fff9e9;border-radius:inherit;text-align:center;overflow:hidden}\r\n .run-tutorial h2{font:900 clamp(32px,6vw,76px)/1.08 system-ui;margin:0;text-wrap:balance;text-shadow:0 3px 0 #000}\r\n .run-tutorial canvas{width:100%;height:100%;min-height:0;display:block}\r\n .run-tutorial p{font:750 clamp(22px,3vw,38px)/1.2 system-ui;margin:0;color:#ffe9a8}\r\n .run-tutorial .tutorial-count{font:700 16px system-ui;color:#c6e6e9;letter-spacing:.08em}\r\n @media(prefers-reduced-motion:reduce){.run-tutorial{scroll-behavior:auto}}\r\n `;\r\n const title=document.createElement('h2'),canvas=document.createElement('canvas'),caption=document.createElement('p'),count=document.createElement('span');count.className='tutorial-count';\r\n const footer=document.createElement('div');footer.append(caption,count);overlay.append(style,title,canvas,footer);host.append(overlay);\r\n const ctx=canvas.getContext('2d'),reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n let index=0,frame=0,disposed=false,started=performance.now();\r\n function show(){\r\n  const step=steps[index];title.textContent=step.instruction;caption.textContent=step.practiceLabel||'Watch the movement, then try it.';count.textContent=`${index+1} / ${steps.length}`;started=performance.now();\r\n }\r\n function arrow(a,c,b,color){\r\n  const trace=()=>{ctx.beginPath();ctx.moveTo(...a);ctx.quadraticCurveTo(...c,...b);};\r\n  trace();ctx.strokeStyle='#050612';ctx.lineWidth=20;ctx.stroke();trace();ctx.strokeStyle=color;ctx.lineWidth=10;ctx.stroke();\r\n  const angle=Math.atan2(b[1]-c[1],b[0]-c[0]),size=28;\r\n  ctx.beginPath();ctx.moveTo(b[0]-Math.cos(angle-.65)*size,b[1]-Math.sin(angle-.65)*size);ctx.lineTo(...b);ctx.lineTo(b[0]-Math.cos(angle+.65)*size,b[1]-Math.sin(angle+.65)*size);ctx.stroke();\r\n }\r\n function render(now){\r\n  if(disposed)return;\r\n  const box=canvas.getBoundingClientRect(),w=Math.max(1,Math.round(box.width)),h=Math.max(1,Math.round(box.height));\r\n  if(canvas.width!==w||canvas.height!==h){canvas.width=w;canvas.height=h;}\r\n  ctx.clearRect(0,0,w,h);ctx.lineJoin='round';ctx.lineCap='round';\r\n  // Motion-design: calm/eased looping instruction, one primary gesture plus arrows and a quiet hold ring.\r\n  const cycle=((now-started)%2600)/2600,phase=reduced.matches ? .65 : (1-Math.cos(Math.PI*Math.min(1,cycle*1.65)))/2;\r\n  const step=steps[index],cue=`${step.gesture||''} ${step.graphicCue||''} ${step.instruction||''}`.toLowerCase();\r\n  const points=Array.from({length:33},()=>null);\r\n  const put=(i,x,y)=>points[i]={x,y};\r\n  put(0,0,-.9);put(11,-.5,-.25);put(12,.5,-.25);put(13,-.75,.15);put(14,.75,.15);put(15,-.8,.5);put(16,.8,.5);\r\n  let symbol='';\r\n  if(/up|raise|corner|laser|tear/.test(cue)){put(13,-.7,-.5-phase*.3);put(14,.7,-.5-phase*.3);put(15,-.9,-.55-phase*.6);put(16,.9,-.55-phase*.6);}\r\n  if(/tear|pull.down/.test(cue)){put(15,-.8,-.95+phase*1.1);put(16,.8,-.95+phase*1.1);symbol='↧';}\r\n  if(/quilt|curtain|pull.up/.test(cue)){put(15,-.65,.85-phase*1.25);put(16,.65,.85-phase*1.25);symbol='↥';}\r\n  if(/forward|fire|jelly|claw|punch/.test(cue)){put(13,-.7,-.25);put(14,.7,-.25);put(15,-.5-phase*.25,-.2);put(16,.5+phase*.25,-.2);symbol=/fire/.test(cue)?'🔥':/jelly/.test(cue)?'◎':'⇢';}\r\n  if(/fish|water|palm|handle|pinch/.test(cue)){put(14,.7,-.3);put(16,.9,-.3-phase*.2);symbol=/fish|water/.test(cue)?'🐟':/handle/.test(cue)?'↻':'◌';}\r\n  if(/drink|potion/.test(cue)){put(14,.65,-.1);put(16,.65-phase*.55,-.25-phase*.55);symbol='↓';}\r\n  const calm=/breath|still|balance|freeze|yoga/.test(cue),prone=/prone|stomach|lie.down/.test(cue);\r\n  if(calm){put(15,-1.15,-.1);put(16,1.15,-.1);symbol='◯';}\r\n  const screen=projectCoach(points,{x:0,y:0,w,h},false);\r\n  ctx.save();if(prone){ctx.translate(w/2,h/2);ctx.rotate(Math.PI/2);ctx.translate(-w/2,-h/2);}\r\n  drawFigure(ctx,screen,{lineWidth:Math.max(7,Math.min(w,h)*.035),arrows:!calm});ctx.restore();\r\n  const span=Math.min(w*.22,h*.4),cx=w/2,cy=h*.52;\r\n  if(/tear|pull.down/.test(cue))arrow([cx-span,cy-span],[cx-span*1.8,cy],[cx-span,cy+span],'#ffe76b');\r\n  else if(/quilt|curtain|pull.up/.test(cue))arrow([cx+span,cy+span],[cx+span*1.8,cy],[cx+span,cy-span],'#5df6ff');\r\n  else if(!calm)arrow([cx+span,cy+span*.7],[cx+span*1.9,cy-span*.4],[cx+span*.55,cy-span*.8],'#ffe76b');\r\n  if(symbol){ctx.font=`900 ${Math.min(w,h)*.2}px system-ui`;ctx.textAlign='center';ctx.fillStyle='#fff1b4';ctx.fillText(symbol,w*.5,h*.9);}\r\n  ctx.beginPath();ctx.arc(w*.16,h*.5,Math.min(w,h)*(.1+(reduced.matches?0:Math.sin(cycle*Math.PI*2)*.01)),-Math.PI/2,Math.PI*1.5);ctx.strokeStyle='#5df6ff55';ctx.lineWidth=6;ctx.stroke();\r\n  frame=requestAnimationFrame(render);\r\n }\r\n function dispose(){if(disposed)return;disposed=true;cancelAnimationFrame(frame);overlay.remove();onPause(false);}\r\n function advance(){if(disposed)return;if(index+1===steps.length){dispose();onComplete();}else{index++;show();}}\r\n onPause(true);show();frame=requestAnimationFrame(render);\r\n return {advance,dispose,get step(){return index;},get element(){return overlay;}};\r\n}\r\n","modules/roguelite/two-altars-input.mjs":"import * as THREE from 'three';\nimport {classifyHand,selectActiveHand} from './hand-input.mjs';\n\nconst none=()=>({tracked:false,altar:{target:null,gesture:'none',y:null,handId:null}});\nconst ids=[0,5,9,13,17],unit=v=>Number.isFinite(v)&&v>=0&&v<=1;\nconst labelOf=value=>{\n const item=Array.isArray(value)?value[0]:value;\n return typeof item==='string'?item:item?.categoryName??item?.label??null;\n};\n\n/** Unmirrored camera-space hand observations; no gesture timing or reward authority. */\nexport function createTwoAltarsInput(){\n const ray=new THREE.Raycaster(),ndc=new THREE.Vector2();\n let disposed=false,lastRaw=null,wrist=null,label=null,identity=null,generation=0,hand=null,wasPaused=false;\n const clear=()=>{wrist=null;label=null;identity=null;hand=null;};\n function observe(raw={}, {camera,targets=[],now=performance.now(),paused=false}={}){\n  if(disposed)return none();\n  const ts=raw.now;\n  if(!Number.isFinite(now)||!Number.isFinite(ts)||ts<0||ts>now||now-ts>250||lastRaw!==null&&ts<=lastRaw){clear();return none();}\n  if(lastRaw!==null&&ts-lastRaw>250||paused!==wasPaused)clear();\n  lastRaw=ts;wasPaused=paused;\n  const validHands=Array.isArray(raw.landmarks)&&raw.landmarks.every(points=>Array.isArray(points)&&points.length===21&&points.every(p=>p&&unit(p.x)&&unit(p.y)));\n  const active=raw.tracked===true&&validHands?selectActiveHand(raw.landmarks,wrist):null;\n  if(!active||!active.points.every(p=>unit(p.x)&&unit(p.y))){clear();return none();}\n  const points=active.points,nextLabel=labelOf(raw.handednesses?.[active.index]??raw.handedness?.[active.index]);\n  if(wrist&&Math.hypot(points[0].x-wrist.x,points[0].y-wrist.y)>.2||identity&&nextLabel!==label)clear();\n  if(!identity)identity=`altar-hand-${++generation}`;\n  wrist={x:points[0].x,y:points[0].y};label=nextLabel;\n  hand=points.map(p=>({...p}));\n  const world=raw.worldLandmarks?.[active.index],validWorld=Array.isArray(world)&&world.length===21&&world.every(p=>p&&[p.x,p.y,p.z].every(Number.isFinite));\n  const kind=classifyHand(points,validWorld?world:null,Number.isFinite(raw.aspect)&&raw.aspect>0?raw.aspect:camera?.aspect||1);\n  const gesture=['open','pinch'].includes(kind)?kind:'other';\n  const point=gesture==='pinch'?{x:(points[4].x+points[8].x)/2,y:(points[4].y+points[8].y)/2}\n   :ids.reduce((p,i)=>({x:p.x+points[i].x/ids.length,y:p.y+points[i].y/ids.length}),{x:0,y:0});\n  let target=null;\n  if(camera?.isCamera&&['pinch','open'].includes(gesture)){\n   camera.updateWorldMatrix(true,false);\n   const visible=(Array.isArray(targets)?targets:[]).filter(object=>{if(!object?.isObject3D)return false;for(let o=object;o;o=o.parent)if(!o.visible)return false;return true;});\n   for(const object of visible)object.updateWorldMatrix(true,true);\n   ndc.set(point.x*2-1,1-point.y*2);ray.setFromCamera(ndc,camera);\n   let hit=ray.intersectObjects(visible,true)[0]?.object;\n   while(hit){if(['paper','left','right','exit'].includes(hit.userData?.altarTarget)){target=hit.userData.altarTarget;break;}hit=hit.parent;}\n  }\n  return {tracked:true,altar:{target,gesture,y:point.y,handId:identity}};\n }\n return {observe,reset(){clear();lastRaw=null;wasPaused=false;},dispose(){disposed=true;clear();},get hand(){return hand?.map(p=>({...p}))??null;}};\n}\n","modules/roguelite/two-altars-room.mjs":"// Pure observations -> proposals. The authenticated host owns all spending and rewards.\nconst freeze=value=>{if(value&&typeof value==='object'){for(const child of Object.values(value))freeze(child);Object.freeze(value);}return value;};\nexport const TWO_ALTARS_POLICY=freeze({version:1,tearDistance:.2,choiceMs:1200,exitMs:900,dtCapMs:100,gapMs:300,offers:{\n left:{offerId:'armor-for-damage',cost:{resource:'armor',amount:20,minimumExclusive:20},effect:{type:'damage-level',levels:1}},\n right:{offerId:'tokens-for-parry',cost:{resource:'runTokens',amount:10},effect:{type:'boon',boonId:'longerParry'}},\n}});\nconst targets=[null,'paper','left','right','exit'],gestures=['none','other','open','pinch'];\nconst keys=['version','policyVersion','encounterId','seed','status','tearReady','tearOrigin','tearProgress','armed','handId','target','dwellMs','continuous','lastObservationId','lastNow','activeMs','outcome'];\nconst record=value=>value!==null&&typeof value==='object'&&!Array.isArray(value);\nconst exact=(value,names)=>record(value)&&Object.keys(value).length===names.length&&names.every(key=>Object.hasOwn(value,key));\nconst unit=value=>Number.isFinite(value)&&value>=0&&value<=1;\nconst nonnegative=value=>Number.isFinite(value)&&value>=0;\nconst id=(value,max)=>typeof value==='string'&&value.length>0&&value.length<=max&&value.trim()===value;\nconst validSeed=value=>Number.isSafeInteger(value)||id(value,128);\nconst sameOutcome=(value,offer)=>exact(value,['offerId','cost','effect'])&&value.offerId===offer.offerId\n &&exact(value.cost,Object.keys(offer.cost))&&Object.entries(offer.cost).every(([k,v])=>value.cost[k]===v)\n &&exact(value.effect,Object.keys(offer.effect))&&Object.entries(offer.effect).every(([k,v])=>value.effect[k]===v);\nconst clear=s=>({...s,tearReady:false,tearOrigin:null,tearProgress:s.status==='sealed'?0:.2,armed:false,handId:null,target:null,dwellMs:0,continuous:false});\n\nfunction validate(s){\n if(!exact(s,keys)||s.version!==1||s.policyVersion!==1||!id(s.encounterId,128)||!validSeed(s.seed)\n  ||!['sealed','offering','proposed','declined'].includes(s.status)\n  ||!['tearReady','armed','continuous'].every(k=>typeof s[k]==='boolean')\n  ||s.tearOrigin!==null&&!unit(s.tearOrigin)||!nonnegative(s.tearProgress)||s.tearProgress>.2\n  ||s.handId!==null&&!id(s.handId,64)||![null,'left','right','exit'].includes(s.target)\n  ||!nonnegative(s.dwellMs)||s.dwellMs>(s.target==='exit'?900:1200)\n  ||!Number.isSafeInteger(s.lastObservationId)||s.lastObservationId< -1\n  ||s.lastNow!==null&&!nonnegative(s.lastNow)||!nonnegative(s.activeMs))throw new TypeError('Invalid altar checkpoint');\n const proposed=s.status==='proposed',terminal=proposed||s.status==='declined';\n if(proposed?!Object.values(TWO_ALTARS_POLICY.offers).some(offer=>sameOutcome(s.outcome,offer)):s.outcome!==null)\n  throw new TypeError('Invalid altar outcome');\n if(s.status==='sealed'?(s.tearProgress>=.2):(s.status!=='declined'&&s.tearProgress!==.2))throw new TypeError('Invalid altar tear');\n if(s.status!=='sealed'&&(s.tearReady||s.tearOrigin!==null)||s.tearOrigin!==null&&(!s.tearReady||!s.handId)\n  ||s.tearOrigin===null&&s.status==='sealed'&&s.tearProgress!==0\n  ||s.tearReady&&(!s.handId||!s.continuous)||s.tearOrigin!==null&&!s.continuous\n  ||s.target===null&&s.dwellMs!==0||s.target!==null&&(!s.armed||!s.handId)\n  ||s.armed&&(!s.handId||!s.continuous)||s.target!==null&&!s.continuous\n  ||s.status==='sealed'&&['left','right'].includes(s.target)\n  ||s.continuous&&(s.lastNow===null||s.handId===null)\n  ||s.lastObservationId===-1&&(s.lastNow!==null||s.activeMs!==0)\n  ||terminal&&(s.armed||s.handId!==null||s.target!==null||s.dwellMs!==0||s.continuous||s.tearReady||s.tearOrigin!==null))\n  throw new TypeError('Inconsistent altar checkpoint');\n return s;\n}\nfunction observation(o){\n if(!exact(o,['observationId','now','tracked','paused','altar'])||!Number.isSafeInteger(o.observationId)||o.observationId<0\n  ||!nonnegative(o.now)||typeof o.tracked!=='boolean'||typeof o.paused!=='boolean'\n  ||!exact(o.altar,['target','gesture','y','handId']))throw new TypeError('Invalid altar observation');\n const a=o.altar;\n if(!targets.includes(a.target)||!gestures.includes(a.gesture)\n  ||(a.gesture==='none'?(a.target!==null||a.y!==null||a.handId!==null):(!unit(a.y)||!id(a.handId,64)))\n  ||a.gesture==='other'&&a.target!==null||!o.tracked&&a.gesture!=='none')throw new TypeError('Invalid altar hand observation');\n}\n\nexport function createTwoAltarsRoom({encounterId,seed=0}={}){\n const s={version:1,policyVersion:1,encounterId,seed,status:'sealed',tearReady:false,tearOrigin:null,tearProgress:0,\n  armed:false,handId:null,target:null,dwellMs:0,continuous:false,lastObservationId:-1,lastNow:null,activeMs:0,outcome:null};\n return freeze(validate(s));\n}\n\n/** Reload retains milestones/high-water, never an unfinished gesture or elapsed interval. */\nexport function restoreTwoAltarsRoom(saved){\n validate(saved);\n return freeze({...clear(structuredClone(saved)),lastNow:null});\n}\n\n/** Host calls ONLY after rejecting a proposal, never after an accepted receipt. */\nexport function resetTwoAltarsProposal(saved){\n validate(saved);\n if(saved.status!=='proposed')throw new TypeError('Only a rejected altar proposal can be rearmed');\n return freeze({...clear({...structuredClone(saved),status:'offering',outcome:null}),lastNow:null});\n}\n\nexport function stepTwoAltarsRoom(saved,input){\n validate(saved);observation(input);\n if(input.observationId<=saved.lastObservationId)return {state:saved,events:[],duplicate:true};\n if(saved.lastNow!==null&&input.now<saved.lastNow)throw new RangeError('Altar clock moved backwards');\n let s={...saved,lastObservationId:input.observationId,lastNow:input.now};\n const result=(events=[])=>({state:freeze(validate(s)),events:freeze(events),duplicate:false});\n if(['proposed','declined'].includes(s.status))return result();\n const a=input.altar,delta=saved.lastNow===null?0:input.now-saved.lastNow;\n const qualified=input.tracked&&!input.paused&&a.gesture!=='none';\n if(!qualified){s=clear(s);return result();}\n const swapped=saved.handId!==null&&saved.handId!==a.handId;\n const gap=saved.lastNow!==null&&delta>TWO_ALTARS_POLICY.gapMs;\n if(gap||swapped)s=clear(s);\n const dt=!gap&&!swapped&&saved.continuous?Math.min(TWO_ALTARS_POLICY.dtCapMs,delta):0;\n s={...s,handId:a.handId,continuous:true,activeMs:s.activeMs+dt};\n if(swapped)return result();\n if(a.gesture==='open'&&a.target==='exit'&&s.armed){\n  s.dwellMs=s.target==='exit'?s.dwellMs+dt:0;s.target='exit';\n  if(s.dwellMs>=TWO_ALTARS_POLICY.exitMs){s=clear({...s,status:'declined'});return result([{type:'declined',encounterId:s.encounterId,policyVersion:1}]);}\n  return result();\n }\n if(s.status==='sealed'){\n  s.target=null;s.dwellMs=0;\n  if(a.gesture==='open'){\n   s.tearReady=a.target==='paper';s.tearOrigin=null;s.tearProgress=0;s.armed=a.target===null;\n  }else if(a.gesture==='pinch'&&s.tearReady){\n   s.armed=false;\n   if(s.tearOrigin===null){if(a.target==='paper')s.tearOrigin=a.y;else s.tearReady=false;}\n   else{\n    s.tearProgress=Math.max(0,Math.min(.2,a.y-s.tearOrigin));\n    if(s.tearProgress+1e-9>=.2)s=clear({...s,status:'offering'});\n   }\n  }else{s=clear(s);s.handId=a.handId;s.continuous=true;}\n  return result();\n }\n if(a.gesture!=='open'){s=clear(s);s.handId=a.handId;s.continuous=true;return result();}\n if(a.target===null){s.armed=true;s.target=null;s.dwellMs=0;return result();}\n if(!s.armed||!['left','right'].includes(a.target)){s.target=null;s.dwellMs=0;return result();}\n s.dwellMs=s.target===a.target?s.dwellMs+dt:0;s.target=a.target;\n if(s.dwellMs>=TWO_ALTARS_POLICY.choiceMs){\n  const outcome=structuredClone(TWO_ALTARS_POLICY.offers[a.target]);\n  s=clear({...s,status:'proposed',outcome});\n  return result([{type:'proposed-outcome',encounterId:s.encounterId,policyVersion:1,outcome:structuredClone(outcome)}]);\n }\n return result();\n}\n","modules/roguelite/two-altars-view.mjs":"import * as THREE from 'three';\nimport {createARRoomShell} from './ar-room-shell.mjs';\nimport {createFlightLook} from '../coach-flight/look.mjs';\n\nconst clamp=(v,a,b)=>Math.max(a,Math.min(b,v));\n\n/** The host lends its real scene/renderer and ticks this view before drawing. */\nexport function mountTwoAltarsView(parent,{scene,camera,renderer,video=null,reducedMotion=false,seed=101}={}){\n if(!parent?.appendChild||!scene?.isScene||!camera?.isPerspectiveCamera||!renderer?.domElement)throw new TypeError('Altars need a parent and borrowed scene/camera/renderer');\n const doc=parent.ownerDocument||document,win=doc.defaultView||globalThis;\n const element=doc.createElement('div');element.className='run-two-altars';element.setAttribute('role','status');element.setAttribute('aria-live','polite');\n Object.assign(element.style,{position:'absolute',left:'12px',right:'12px',bottom:'12px',zIndex:'3',pointerEvents:'none',padding:'12px',color:'#f5ecd3',background:'#202820ed',border:'2px solid #b4bb8a',borderRadius:'2px',boxShadow:'4px 4px 0 #080d13',boxSizing:'border-box',maxHeight:'38%',overflowY:'auto',overflowWrap:'anywhere',font:'600 clamp(14px,3.5vw,17px)/1.3 Pixel,monospace',whiteSpace:'pre-line'});parent.appendChild(element);\n const group=new THREE.Group();group.name='two-altars-room';scene.add(group);\n const old={background:scene.background,position:camera.position.clone(),quaternion:camera.quaternion.clone(),fov:camera.fov,aspect:camera.aspect,near:camera.near,far:camera.far};\n const ownedTextures=new Set(),ownedMaterials=new Set(),ownedGeometries=new Set();\n const mesh=(geometry,material,name,x,y,z)=>{\n  ownedGeometries.add(geometry);ownedMaterials.add(material);const object=new THREE.Mesh(geometry,material);object.name=name;object.position.set(x,y,z);group.add(object);return object;\n };\n const box=(name,x,y,z,w,h,d,color)=>mesh(new THREE.BoxGeometry(w,h,d),new THREE.MeshStandardMaterial({color,roughness:.95}),name,x,y,z);\n // The shared shell supplies six stone surfaces and low haze, never Merchant furniture.\n const light=new THREE.HemisphereLight(0xf2e7c8,0x554b47,2);group.add(light);\n const plaque=(name,x,y,z,width,height)=>{\n  const canvas=doc.createElement('canvas');canvas.width=512;canvas.height=192;\n  const texture=new THREE.CanvasTexture(canvas);texture.colorSpace=THREE.SRGBColorSpace;texture.minFilter=texture.magFilter=THREE.NearestFilter;texture.generateMipmaps=false;ownedTextures.add(texture);\n  const object=mesh(new THREE.PlaneGeometry(width,height),new THREE.MeshBasicMaterial({map:texture,toneMapped:false}),`two-altars-${name}`,x,y,z);object.userData.altarTarget=name;\n  return {object,paint(lines,enabled=true){const c=canvas.getContext('2d');c.fillStyle=enabled?'#ddd3b8':'#696c65';c.fillRect(0,0,512,192);c.strokeStyle='#33372e';c.lineWidth=10;c.strokeRect(5,5,502,182);c.fillStyle=enabled?'#20251e':'#ffffff';c.textAlign='center';c.font='bold 30px monospace';lines.forEach((line,i)=>c.fillText(line,256,43+i*45));texture.needsUpdate=true;}};\n };\n const altars={};\n for(const [side,x] of [['left',-1.05],['right',1.05]]){\n  const base=box(`two-altars-${side}-stone`,x,.5,-3.6,1.3,1,1,0x66695f);\n  base.userData.altarTarget=side;\n  box(`two-altars-${side}-cap`,x,1.03,-3.6,1.5,.16,1.2,0x8e8a78);\n  altars[side]=plaque(side,x,1.48,-3.05,1.5,.6);\n }\n const exit=plaque('exit',0,1.6,3.85,1.8,.7);exit.object.rotation.y=Math.PI;exit.paint(['EXIT','OPEN PALM 0.9s','NO PURCHASE']);\n const paper=new THREE.Group();paper.name='two-altars-paper';paper.userData.altarTarget='paper';paper.position.set(0,1.8,-2.25);group.add(paper);\n const halves=[-1,1].map(sign=>{const object=mesh(new THREE.PlaneGeometry(2.4,2.8),new THREE.MeshStandardMaterial({color:0xe5dbc4,side:THREE.DoubleSide}),`two-altars-paper-${sign}`,sign*1.2,0,0);paper.add(object);return object;});\n const targets=[paper,...Object.values(altars).map(a=>a.object),exit.object];\n camera.position.set(0,1.25,2.8);camera.quaternion.identity();camera.fov=68;camera.near=.05;camera.far=40;camera.updateProjectionMatrix();\n scene.background=new THREE.Color(0x777b72);\n const shell=createARRoomShell(THREE,{scene,camera,bounds:{minX:-3.5,maxX:3.5,minY:-.02,maxY:4,minZ:-6,maxZ:4},seed,reducedMotion,color:0x777365});\n const look=createFlightLook(THREE,{win}),canvas=renderer.domElement;\n let disposed=false,state={status:'sealed',tearProgress:0,target:null,dwellMs:0},resources={},labelKey='',yaw=0,pitch=0,drag=null;\n const previousTouchAction=canvas.style.touchAction;canvas.style.touchAction='none';\n const onDown=e=>{if(e.button!==undefined&&e.button!==0)return;drag={id:e.pointerId,x:e.clientX,y:e.clientY};canvas.setPointerCapture?.(e.pointerId);};\n const onMove=e=>{if(!drag||e.pointerId!==drag.id)return;yaw-=(e.clientX-drag.x)*.006;pitch=clamp(pitch-(e.clientY-drag.y)*.006,-Math.PI/2+.02,Math.PI/2-.02);drag.x=e.clientX;drag.y=e.clientY;};\n const onUp=e=>{if(drag?.id!==e.pointerId)return;if(canvas.hasPointerCapture?.(e.pointerId))canvas.releasePointerCapture?.(e.pointerId);drag=null;};\n canvas.addEventListener('pointerdown',onDown);canvas.addEventListener('pointermove',onMove);canvas.addEventListener('pointerup',onUp);canvas.addEventListener('pointercancel',onUp);canvas.addEventListener('lostpointercapture',onUp);\n const readyPromise=new THREE.TextureLoader().loadAsync('/pod/worlds/boards/paper/graph-guide.png').then(texture=>{\n  if(disposed){texture.dispose();return;}\n  texture.colorSpace=THREE.SRGBColorSpace;texture.minFilter=texture.magFilter=THREE.NearestFilter;texture.generateMipmaps=false;ownedTextures.add(texture);\n  for(let i=0;i<2;i++){const map=texture.clone();map.repeat.x=.5;map.offset.x=i*.5;map.needsUpdate=true;ownedTextures.add(map);halves[i].material.map=map;halves[i].material.needsUpdate=true;}\n });\n // Readiness still rejects for the host's loading gate, without a late unhandled rejection.\n readyPromise.catch(()=>{});\n function update(next=state,nextResources=resources){\n  if(disposed)return;\n  state={status:next.status,tearProgress:next.tearProgress??0,target:next.target??null,dwellMs:next.dwellMs??0};\n  resources={armor:nextResources?.armor,runTokens:nextResources?.runTokens};\n  const key=`${resources.armor}:${resources.runTokens}:${state.status}:${state.target}:${Math.floor(state.dwellMs/100)}`;\n  if(key!==labelKey){labelKey=key;\n   altars.left.paint(['LEFT: 20 ARMOR','+1 DAMAGE LEVEL',resources.armor>20?'PALM 1.2s':'NEED ARMOR >20'],resources.armor>20);\n   altars.right.paint(['RIGHT: 10 RUN TOKENS','LONGER PARRY',resources.runTokens>=10?'PALM 1.2s':'NEED 10 TOKENS'],resources.runTokens>=10);\n   const instruction=state.status==='sealed'?'Open palm at the paper. Pinch and pull down.':state.status==='offering'?'Relax your hand, then hold an open palm at one altar.':state.status==='proposed'?'Saving your choice…':'Leaving without purchase.';\n   element.textContent=`${instruction}\\nLEFT: 20 ARMOR → +1 DAMAGE LEVEL · keep some armor\\nRIGHT: 10 RUN TOKENS → LONGER PARRY\\nDrag to look around. Open palm at EXIT to leave.${state.target?`\\n${state.target.toUpperCase()} · ${Math.min(100,Math.floor(state.dwellMs/(state.target==='exit'?900:1200)*100))}%`:''}`;\n  }\n  paper.visible=state.status==='sealed';\n  const tear=clamp(state.tearProgress/.2,0,1);\n  halves.forEach((half,i)=>{half.position.x=(i?1:-1)*(1.2+tear*.65);half.rotation.z=(i?-1:1)*tear*.12;});\n }\n function applyLook(targetCamera=camera){\n  if(disposed)return false;\n  targetCamera.quaternion.setFromEuler(new THREE.Euler(pitch,yaw,0,'YXZ'));\n  look.apply(targetCamera);targetCamera.updateWorldMatrix(true,false);return true;\n }\n function render({dt=0,paused=false,applyLook:hostLook}={}){\n  if(disposed)return;\n  if(!Number.isFinite(dt)||dt<0)throw new RangeError('Altar delta must be finite and nonnegative');\n  // Borrowed compositor and raycaster use the same contain-fit source camera.\n  const aspect=video?.videoWidth>0&&video?.videoHeight>0?video.videoWidth/video.videoHeight:camera.aspect;\n  if(camera.aspect!==aspect){camera.aspect=aspect;camera.updateProjectionMatrix();}\n  applyLook(camera);hostLook?.(camera);camera.updateWorldMatrix(true,false);\n  shell.update(Math.min(.1,dt),{paused});\n }\n function compositeHand(ctx,points,{x=0,y=0,width,height}={}){\n  if(disposed||!ctx||points?.length!==21||!points.every(p=>Number.isFinite(p.x)&&Number.isFinite(p.y))||!(width>0&&height>0))return;\n  const p=points.map(v=>[v.x*width,v.y*height]),scale=Math.hypot(p[0][0]-p[9][0],p[0][1]-p[9][1]);\n  ctx.save();ctx.translate(x,y);ctx.globalCompositeOperation='destination-out';ctx.fillStyle=ctx.strokeStyle='#fff';ctx.lineJoin=ctx.lineCap='round';\n  const path=indices=>{ctx.beginPath();indices.forEach((id,i)=>ctx[i?'lineTo':'moveTo'](...p[id]));};\n  path([0,1,2,5,9,13,17]);ctx.closePath();ctx.fill();ctx.lineWidth=scale*.25;ctx.stroke();\n  for(const indices of [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20]]){path(indices);ctx.lineWidth=scale*.2;ctx.stroke();}\n  ctx.restore();\n }\n function dispose(){\n  if(disposed)return;disposed=true;\n  for(const [name,handler] of [['pointerdown',onDown],['pointermove',onMove],['pointerup',onUp],['pointercancel',onUp],['lostpointercapture',onUp]])canvas.removeEventListener(name,handler);\n  if(drag&&canvas.hasPointerCapture?.(drag.id))canvas.releasePointerCapture?.(drag.id);drag=null;canvas.style.touchAction=previousTouchAction;\n  look.dispose();shell.dispose();group.removeFromParent();\n  for(const t of ownedTextures)t.dispose();for(const m of ownedMaterials)m.dispose();for(const g of ownedGeometries)g.dispose();group.clear();element.remove();\n  scene.background=old.background;camera.position.copy(old.position);camera.quaternion.copy(old.quaternion);camera.fov=old.fov;camera.aspect=old.aspect;camera.near=old.near;camera.far=old.far;camera.updateProjectionMatrix();camera.updateWorldMatrix(true,false);\n }\n update();\n return {readyPromise,render,update,dispose,camera,targets,group,element,applyLook,setLoading:on=>{if(!disposed)shell.setLoading(on);},togglePhoneLook:()=>disposed?Promise.resolve(false):look.toggle(),compositeHand};\n}\n","modules/roguelite/walk-motion.mjs":"// Caller-driven visual traversal adapter; owns no render loop, input, or graphics context.\r\nimport { WALK_S, walkPose } from '../coach-world-rts/coach-motion.mjs';\r\n\r\nconst finite = Number.isFinite;\r\nconst clamp01 = value => Math.max(0, Math.min(1, value));\r\nconst smooth = value => { const t = clamp01(value); return t * t * (3 - 2 * t); };\r\n\r\nfunction pointFrom(THREE, value, index) {\r\n  if (Array.isArray(value) && value.length >= 3 && value.slice(0, 3).every(finite)) {\r\n    return new THREE.Vector3(value[0], value[1], value[2]);\r\n  }\r\n  if (value && finite(value.x) && finite(value.y) && finite(value.z)) {\r\n    return new THREE.Vector3(value.x, value.y, value.z);\r\n  }\r\n  throw new TypeError(`points[${index}] must be a finite [x,y,z] or Vector3-like point`);\r\n}\r\n\r\n/**\r\n * Animate a camera along authored local/world coordinates. The camera's current\r\n * position is prepended so update(0) is an exact no-op; the caller owns ticking.\r\n */\r\nexport function createWalkMotion(THREE, camera, {\r\n  points, duration = 3.6, reducedMotion = false, bobHeight = 0.35,\r\n} = {}) {\r\n  if (!THREE?.Vector3 || !THREE?.Quaternion || !THREE?.Matrix4 || !THREE?.CatmullRomCurve3) {\r\n    throw new TypeError('THREE curve, vector, matrix and quaternion constructors are required');\r\n  }\r\n  if (!camera?.position || !camera?.quaternion || typeof camera.position.copy !== 'function'\r\n      || typeof camera.quaternion.copy !== 'function') throw new TypeError('camera needs Three.js position and quaternion');\r\n  if (!Array.isArray(points) || points.length < 1) throw new TypeError('points must contain at least one destination');\r\n  if (!finite(duration) || duration <= 0) throw new RangeError('duration must be finite and positive');\r\n  if (typeof reducedMotion !== 'boolean') throw new TypeError('reducedMotion must be boolean');\r\n  if (!finite(bobHeight) || bobHeight < 0 || bobHeight > 1) throw new RangeError('bobHeight must be in [0, 1]');\r\n\r\n  const origin = camera.position.clone();\r\n  const destinations = points.map(pointFrom.bind(null, THREE));\r\n  const curvePoints = [origin.clone(), ...destinations];\r\n  const curve = new THREE.CatmullRomCurve3(curvePoints, false, 'centripetal');\r\n  // Prime Three's arc-length cache before the first caller-driven frame.\r\n  curve.getLengths();\r\n  const end = destinations[destinations.length - 1].clone();\r\n  const startQuaternion = camera.quaternion.clone();\r\n  const position = new THREE.Vector3();\r\n  const tangent = new THREE.Vector3();\r\n  const lookTarget = new THREE.Vector3();\r\n  const up = new THREE.Vector3(0, 1, 0);\r\n  const orientation = new THREE.Quaternion();\r\n  const lookMatrix = new THREE.Matrix4();\r\n  const result = { done: false, progress: 0, cancelled: false };\r\n  // walkPose intentionally returns a convenient object for sprite callers. Cache\r\n  // only its authored bob curve here so this hot update path stays allocation-free.\r\n  const bobSamples = new Float32Array(65);\r\n  for (let i = 0; i < bobSamples.length; i++) bobSamples[i] = walkPose(WALK_S * i / 64).bodyY * bobHeight;\r\n  let elapsed = 0, progress = 0, done = false, cancelled = false;\r\n\r\n  function bobAt(seconds) {\r\n    const phase = (seconds % WALK_S) / WALK_S * 64;\r\n    const lo = Math.floor(phase), t = phase - lo;\r\n    return bobSamples[lo] + (bobSamples[lo + 1] - bobSamples[lo]) * t;\r\n  }\r\n\r\n  function sampleOrientation(u, blend) {\r\n    curve.getPointAt(u, position);\r\n    curve.getTangentAt(u, tangent);\r\n    if (tangent.lengthSq() < 1e-12) tangent.set(0, 0, -1);\r\n    lookTarget.copy(position).add(tangent);\r\n    lookMatrix.lookAt(position, lookTarget, up);\r\n    orientation.setFromRotationMatrix(lookMatrix);\r\n    camera.quaternion.copy(startQuaternion).slerp(orientation, blend);\r\n  }\r\n\r\n  function apply() {\r\n    const eased = smooth(progress);\r\n    curve.getPointAt(eased, position);\r\n    camera.position.copy(position);\r\n    if (!reducedMotion && !done) camera.position.y += bobAt(elapsed) * Math.sin(Math.PI * progress);\r\n    sampleOrientation(eased, eased);\r\n    if (done) {\r\n      camera.position.copy(end);\r\n      sampleOrientation(1, 1);\r\n    }\r\n  }\r\n\r\n  return {\r\n    get done() { return done; },\r\n    get progress() { return progress; },\r\n    get cancelled() { return cancelled; },\r\n    update(deltaSeconds, { paused = false } = {}) {\r\n      if (!finite(deltaSeconds) || deltaSeconds < 0) throw new RangeError('deltaSeconds must be finite and non-negative');\r\n      if (typeof paused !== 'boolean') throw new TypeError('paused must be boolean');\r\n      if (!done && !cancelled && !paused && deltaSeconds > 0) {\r\n        const dt = Math.min(0.1, deltaSeconds);\r\n        elapsed = Math.min(duration, elapsed + dt);\r\n        if (duration - elapsed <= 1e-9) elapsed = duration;\r\n        progress = clamp01(elapsed / duration);\r\n        done = progress >= 1;\r\n        apply();\r\n      }\r\n      result.done = done; result.progress = progress; result.cancelled = cancelled;\r\n      return result;\r\n    },\r\n    cancel() { if (!done) cancelled = true; },\r\n  };\r\n}\r\n\r\nexport { WALK_S };\r\n","modules/roguelite/warmup-evidence.mjs":"// Adapts existing movement-session counters to the units used by warmup nodes.\n// Timed step/jump nodes only accrue while a recent existing count pulse proves motion.\nconst MAX_DT = 0.1;\nconst PULSE_WINDOW_MS = 500;\n\nconst finiteNonnegative = (value, name) => {\n  if (!Number.isFinite(value) || value < 0) throw new RangeError(`${name} must be finite and nonnegative`);\n  return value;\n};\nconst validState = state => {\n  if (!state || state.version !== 1 || !Number.isFinite(state.activeSeconds) || state.activeSeconds < 0 ||\n      !(state.lastNow === null || Number.isFinite(state.lastNow)) ||\n      !(state.lastCount === null || (Number.isFinite(state.lastCount) && state.lastCount >= 0)) ||\n      !(state.lastPulseAt === null || Number.isFinite(state.lastPulseAt))) {\n    throw new TypeError('Invalid warmup evidence state');\n  }\n};\n\nexport function createWarmupEvidence() {\n  return { version: 1, activeSeconds: 0, lastNow: null, lastCount: null, lastPulseAt: null };\n}\n\n// Monotonic clock values are transient across page reloads. Preserve earned active\n// time but rebase all detector timing and count baselines on the first new sample.\nexport function restoreWarmupEvidence(saved) {\n  validState(saved);\n  return { version: 1, activeSeconds: saved.activeSeconds, lastNow: null, lastCount: null, lastPulseAt: null };\n}\n\nfunction metricFor(node, motion) {\n  if (!node || typeof node !== 'object' || !motion || typeof motion !== 'object') {\n    throw new TypeError('A warmup node and movement snapshot are required');\n  }\n  if (Object.hasOwn(node, 'reps')) return { kind: 'reps', value: finiteNonnegative(motion.count, 'motion.count') };\n  if (Object.hasOwn(node, 'holdSeconds')) return { kind: 'hold', value: finiteNonnegative(motion.totalHold, 'motion.totalHold') };\n  if (node.exerciseKind === 'pace') return { kind: 'pace', value: finiteNonnegative(motion.active, 'motion.active') };\n  if (Object.hasOwn(node, 'seconds') && ['steps', 'jumps'].includes(node.exerciseKind)) {\n    return { kind: 'pulsed-seconds', count: finiteNonnegative(motion.count, 'motion.count') };\n  }\n  throw new TypeError('Warmup node must define reps, holdSeconds, pace, or timed steps/jumps');\n}\n\nexport function observeWarmupEvidence(state, { now, tracked, paused = false, node, motion } = {}) {\n  validState(state);\n  if (!Number.isFinite(now)) throw new TypeError('now must be finite');\n  if (typeof tracked !== 'boolean' || typeof paused !== 'boolean') throw new TypeError('tracked and paused must be booleans');\n  const metric = metricFor(node, motion);\n  const next = { ...state };\n  if (metric.kind !== 'pulsed-seconds') {\n    // Preserve existing cumulative movement evidence for reps, holds and pace.\n    if (!tracked || paused) {\n      next.lastNow = now;\n      next.lastCount = null;\n      next.lastPulseAt = null;\n    }\n    return { state: next, value: metric.value };\n  }\n\n  const count = metric.count;\n  if (!tracked || paused) {\n    // Rebase on every paused/untracked observation so movements during the gap\n    // cannot become a pulse when tracking resumes.\n    next.lastNow = now;\n    next.lastCount = count;\n    next.lastPulseAt = null;\n    return { state: next, value: next.activeSeconds };\n  }\n\n  if (next.lastNow === null || next.lastCount === null) {\n    next.lastNow = now;\n    next.lastCount = count;\n    next.lastPulseAt = null;\n    return { state: next, value: next.activeSeconds };\n  }\n\n  const elapsedMs = now - next.lastNow;\n  if (elapsedMs < 0) {\n    // Clock origin changed without an explicit restore; safely rebase.\n    next.lastNow = now;\n    next.lastCount = count;\n    next.lastPulseAt = null;\n    return { state: next, value: next.activeSeconds };\n  }\n\n  if (count < next.lastCount) {\n    // MovementSession reset/recreated: never interpret the new baseline as motion.\n    next.lastNow = now;\n    next.lastCount = count;\n    next.lastPulseAt = null;\n    return { state: next, value: next.activeSeconds };\n  }\n  if (count > next.lastCount) next.lastPulseAt = now;\n\n  const pulseAge = next.lastPulseAt === null ? Infinity : now - next.lastPulseAt;\n  if (pulseAge >= 0 && pulseAge <= PULSE_WINDOW_MS) {\n    next.activeSeconds += Math.min(elapsedMs / 1000, MAX_DT);\n  }\n  next.lastNow = now;\n  next.lastCount = count;\n  return { state: next, value: next.activeSeconds };\n}\n","modules/roguelite/warmup-flow.mjs":"// Pure run sequencing for the roguelite's warmup and unlock waves.\n// Workouts, biome IDs, and trainer IDs are references into the shipped catalogs.\nimport { EXERCISES } from '../../exercise-library.mjs';\nimport { COACH_UNITS } from '../coach-world/unit-catalog.mjs';\nimport { BIOMES } from '../coach-world-rts/biomes.mjs';\n\nexport const WARMUP_EXERCISE_FAMILIES = Object.freeze({\n  chest: Object.freeze({ group: 'chest', exerciseIds: ['knee-pushup', 'high-incline-pushup'] }),\n  quads: Object.freeze({ group: 'legs', exerciseIds: ['shallow-squat', 'wall-sit'] }),\n  glutes: Object.freeze({ group: 'hips', exerciseIds: ['glute-bridge', 'small-hinge'] }),\n  'arms-shoulders': Object.freeze({ group: 'shoulders', exerciseIds: ['overhead-reach', 'front-raise'] }),\n  yoga: Object.freeze({ group: 'yoga', exerciseIds: ['mountain', 'chair'] }),\n  'martial-arts': Object.freeze({ group: 'stances', exerciseIds: ['high-horse', 'front-stance-left'] }),\n  cardio: Object.freeze({ group: 'cardio', exerciseIds: ['march', 'step-jack'] }),\n});\n\nconst FAMILY_IDS = Object.freeze(Object.keys(WARMUP_EXERCISE_FAMILIES));\nconst ROOM_FAMILY_ALIAS = Object.freeze({\n  healing_waters: 'yoga', coin_chase: 'cardio', pixel_laser: 'cardio',\n  shrink_burpees_maze: 'cardio', nature_yoga: 'yoga', crisis_combat: 'martial-arts',\n});\nconst EASY_COACHES = Object.freeze(COACH_UNITS.filter(c => ['starter', 'easy'].includes(c.difficulty)));\nexport const BASIC_STAT_CHOICES = Object.freeze([\n  Object.freeze({ motion: 'left', stat: 'health', label: 'HEALTH', symbol: '♥' }),\n  Object.freeze({ motion: 'right', stat: 'damage', label: 'DAMAGE', symbol: '✊' }),\n  Object.freeze({ motion: 'crouch', stat: 'luck', label: 'LUCK', symbol: '✦' }),\n]);\nconst statChoices = () => BASIC_STAT_CHOICES.map(choice => ({ ...choice }));\nconst clone = value => JSON.parse(JSON.stringify(value));\n\nfunction hashSeed(seed) {\n  const text = String(seed ?? 'default');\n  let h = 2166136261;\n  for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);\n  return h >>> 0;\n}\n\nfunction rng(seed) {\n  let state = hashSeed(seed) || 0x6d2b79f5;\n  return () => {\n    state = (state + 0x6d2b79f5) >>> 0;\n    let t = state;\n    t = Math.imul(t ^ (t >>> 15), t | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n}\n\nfunction shuffled(items, random) {\n  const out = [...items];\n  for (let i = out.length - 1; i > 0; i--) {\n    const j = Math.floor(random() * (i + 1));\n    [out[i], out[j]] = [out[j], out[i]];\n  }\n  return out;\n}\n\nfunction makeWarmup(index, seed, { intro = false, familyCycle, coachOffsets, selectedExerciseId } = {}) {\n  const familyId = familyCycle[index % familyCycle.length];\n  const family = WARMUP_EXERCISE_FAMILIES[familyId];\n  if (!BIOMES[familyId]) throw new Error(`No biome catalog entry for ${familyId}`);\n  const compatibleExercises = family.exerciseIds.filter(id => EXERCISES[id]?.group === family.group);\n  if (!compatibleExercises.length) throw new Error(`No catalog exercise for ${familyId}`);\n  const exerciseId = selectedExerciseId ?? compatibleExercises[(index + Math.floor(hashSeed(seed) % compatibleExercises.length)) % compatibleExercises.length];\n  // Quads maps to the exercise catalog's `legs`, and glutes to `hips`; the remaining\n  // families retain the catalog's section name. This keeps coaches biome-compatible.\n  const sectionForGroup = { legs: 'quads', hips: 'glutes', shoulders: 'arms-shoulders', stances: 'martial-arts' };\n  const coachSection = sectionForGroup[family.group] || family.group;\n  const eligible = EASY_COACHES.filter(c => c.section === coachSection);\n  const fallback = EASY_COACHES.filter(c => c.section == null);\n  const coaches = eligible.length ? eligible : fallback;\n  if (!coaches.length) throw new Error(`No starter/easy catalog coach for ${familyId}`);\n  const coach = coaches[coachOffsets[index % coachOffsets.length] % coaches.length];\n  const exercise = EXERCISES[exerciseId];\n  const target = exercise.kind === 'hold' ? { holdSeconds: 4 + (index % 3) }\n    : exercise.kind === 'reps' ? { reps: 3 + (index % 2) } : { seconds: 6 + (index % 3) };\n  return {\n    id: intro ? `intro-${index + 1}` : `warmup-${hashSeed(seed).toString(36)}-${index + 1}`,\n    type: 'warmup',\n    // Existing biome and exercise families share catalog keys; keeping them aligned\n    // is what makes the warmup's visual coach/terrain legible for its movement.\n    biomeId: familyId,\n    exerciseId,\n    exerciseGroup: exercise.group,\n    exerciseKind: exercise.kind,\n    coachId: coach.id,\n    coachLabel: coach.label,\n    coachDifficulty: coach.difficulty,\n    ...target,\n    cadence: { bpm: 72 + (index % 4) * 4, countInBeats: 2, restSeconds: 2 },\n    coinReward: 0,\n    upgradeOfferOnResolve: true,\n  };\n}\n\nfunction scheduleIntro(seed, exerciseIds) {\n  const random = rng(seed);\n  const families = shuffled(FAMILY_IDS, random);\n  const coaches = shuffled(EASY_COACHES.map((_, i) => i), random);\n  const selected = [...new Set(exerciseIds)].map(id => {\n    const exercise = EXERCISES[id];\n    if (!exercise || exercise.endless) throw new RangeError(`Not a basic exercise: ${id}`);\n    // Existing performance-catalog groups core/balance/yoga together on the yoga track.\n    const alias = { boxing: 'martial-arts', stances: 'martial-arts', legs: 'quads', hips: 'glutes', shoulders: 'arms-shoulders', core:'yoga', balance:'yoga' };\n    const family = alias[exercise.group] || exercise.group;\n    if (!WARMUP_EXERCISE_FAMILIES[family]) throw new RangeError(`No existing biome/coach family for ${id}`);\n    return { id, family };\n  });\n  const selectedOrder=[];\n  while(selected.length&&selectedOrder.length<15)selectedOrder.push(...shuffled(selected,random));\n  const warmups = Array.from({ length: 15 }, (_, i) => makeWarmup(i, seed, {\n    intro: true,\n    familyCycle: selected.length ? selectedOrder.map(item => item.family) : families,\n    coachOffsets: coaches,\n    selectedExerciseId: selected.length ? selectedOrder[i].id : undefined,\n  }));\n  return [\n    ...warmups.slice(0, 5),\n    { id: 'intro-miniboss', type: 'miniboss', level: 1, entrance: { kind: 'crystal-door', grimoireAsset: 'ice.glb' }, coreRoomCoverageExempt: true },\n    ...warmups.slice(5, 10),\n    { id: 'intro-miniboss-2', type: 'miniboss', level: 2, entrance: { kind: 'crystal-door', grimoireAsset: 'ice.glb' }, coreRoomCoverageExempt: true },\n    ...warmups.slice(10),\n    { id: 'intro-mainboss', type: 'mainboss', level: 2, entrance: { kind: 'crystal-door', grimoireAsset: 'ice.glb' }, coreRoomCoverageExempt: true },\n  ];\n}\n\n/** Build the initial training gauntlet once per unlock wave. */\nexport function createIntroSchedule(seed = 0, { exerciseIds = [] } = {}) {\n  if (!Array.isArray(exerciseIds)) throw new TypeError('exerciseIds must be an array');\n  const schedule = scheduleIntro(seed, exerciseIds);\n  return { id: `intro-wave-${hashSeed(seed).toString(36)}`, stage: 'initial-gauntlet', seed: String(seed), nodes: schedule };\n}\n\n/** Three family-compatible training rooms precede any later full game room. */\nexport function createFullRoomBundle(seed = 0, roomDefinition = {}) {\n  const input = clone(roomDefinition ?? {});\n  const exerciseRequested = input.exerciseId ?? input.exercise?.id;\n  const exerciseFamily = exerciseRequested\n    ? FAMILY_IDS.find(id => WARMUP_EXERCISE_FAMILIES[id].exerciseIds.includes(exerciseRequested))\n    : undefined;\n  const requested = exerciseFamily ?? input.exerciseGroup ?? input.family ?? input.section ?? input.biomeId ?? ROOM_FAMILY_ALIAS[input.roomType];\n  const familyId = WARMUP_EXERCISE_FAMILIES[requested]\n    ? requested\n    : FAMILY_IDS.find(id => WARMUP_EXERCISE_FAMILIES[id].group === requested)\n      ?? FAMILY_IDS.find(id => id === requested)\n      ?? 'cardio';\n  const random = rng(`${seed}:bundle:${input.id ?? familyId}`);\n  const familyCycle = [familyId, familyId, familyId];\n  const coachOffsets = shuffled(EASY_COACHES.map((_, i) => i), random);\n  const warmups = Array.from({ length: 3 }, (_, i) => makeWarmup(i, `${seed}:${input.id ?? ''}`, {\n    familyCycle,\n    coachOffsets,\n  }));\n  const fullGame = { ...input, id: input.id ?? `room-${hashSeed(seed).toString(36)}`, type: 'full-game' };\n  return { id: `bundle-${hashSeed(`${seed}:${fullGame.id}`).toString(36)}`, nodes: [...warmups, fullGame] };\n}\n\nfunction emptyProgress(seed = 0, stage = 'initial-gauntlet') {\n  return { version: 1, seed: String(seed), stage, introCompleted: false, unlockedFullRooms: false,\n    introWarmupsResolved: 0, introBossReceipts: [], activeBundleId: null, activeWarmupIds: [],\n    resolvedReceipts: [], upgradeOffers: [], coins: 0 };\n}\nconst openingBosses = [\n  { id:'intro-miniboss', after:5 },\n  { id:'intro-miniboss-2', after:10 },\n  { id:'intro-mainboss', after:15 },\n];\nconst introBossIds = openingBosses.map(boss => boss.id);\n// The second miniboss still completes basic onboarding; five more rooms lead to the first main boss.\nconst introCleared = progress => introBossIds.slice(0,2).every(id => progress.introBossReceipts.some(r => r.bossId === id));\nconst introReady = (progress, count) => Array.from({length:count}, (_, i) => `intro-${i+1}`)\n  .every(id => progress.resolvedReceipts.includes(id));\n\n/** Boss eligibility belongs to the exercise schedule, independent of optional room discoveries. */\nexport function canOfferBoss(progress) {\n  if (!Array.isArray(progress?.introBossReceipts) || !Array.isArray(progress?.resolvedReceipts)) {\n    throw new TypeError('Boss offers require exercise schedule progress, not room discovery progress');\n  }\n  const next = openingBosses.find(boss => !progress.introBossReceipts.some(receipt => receipt.bossId === boss.id));\n  return !!next && introReady(progress, next.after);\n}\n\nexport function createWarmupProgress(seed = 0) { return emptyProgress(seed); }\n\n/** JSON-safe restore. Unknown/malformed fields are discarded; receipt IDs stay unique. */\nexport function restoreWarmupProgress(saved = {}) {\n  if (!saved || typeof saved !== 'object' || Array.isArray(saved)) saved = {};\n  const p = emptyProgress(saved.seed, saved.stage === 'initial-gauntlet' ? saved.stage : 'full-game');\n  p.introWarmupsResolved = Math.max(0, Math.min(15, Number.isInteger(saved.introWarmupsResolved) ? saved.introWarmupsResolved : 0));\n  p.introBossReceipts = [];\n  for (const r of Array.isArray(saved.introBossReceipts) ? saved.introBossReceipts : []) {\n    if (r && introBossIds.includes(r.bossId) && isSafeId(r.receiptId)\n        && !p.introBossReceipts.some(prior => prior.bossId === r.bossId || prior.receiptId === r.receiptId)) {\n      p.introBossReceipts.push({ bossId:r.bossId, receiptId:r.receiptId });\n    }\n  }\n  p.introCompleted = introCleared(p);\n  p.unlockedFullRooms = p.introCompleted;\n  p.stage = p.introCompleted ? 'full-game' : 'initial-gauntlet';\n  p.activeBundleId = typeof saved.activeBundleId === 'string' ? saved.activeBundleId : null;\n  p.activeWarmupIds = Array.isArray(saved.activeWarmupIds) ? [...new Set(saved.activeWarmupIds.filter(isSafeId))] : [];\n  p.resolvedReceipts = [...new Set(Array.isArray(saved.resolvedReceipts) ? saved.resolvedReceipts.filter(isSafeId) : [])];\n  p.upgradeOffers = (Array.isArray(saved.upgradeOffers) ? saved.upgradeOffers : [])\n    .filter(o => o && isSafeId(o.receiptId) && p.resolvedReceipts.includes(o.receiptId))\n    .map(o => ({ receiptId: o.receiptId, source: 'warmup', choices: statChoices() }));\n  p.coins = 0;\n  return p;\n}\n\nfunction isSafeId(value) { return typeof value === 'string' && /^[a-zA-Z0-9_.:-]{1,120}$/.test(value); }\n\nexport function beginFullRoomBundle(progress, bundle) {\n  if (!progress.unlockedFullRooms) throw new Error('Full rooms are locked until the initial gauntlet is complete');\n  if (!bundle || !Array.isArray(bundle.nodes) || bundle.nodes.length !== 4\n      || bundle.nodes.slice(0, 3).some(n => n.type !== 'warmup') || bundle.nodes[3]?.type !== 'full-game') {\n    throw new TypeError('A full-room bundle must contain exactly three warmups followed by one full-game room');\n  }\n  return { ...progress, activeBundleId: bundle.id, activeWarmupIds: bundle.nodes.slice(0, 3).map(n => n.id) };\n}\n\n/** A receipt resolves once and yields one upgrade offer. Full-game receipts never offer upgrades. */\nexport function resolveWarmup(progress, receiptId) {\n  if (!isSafeId(receiptId)) throw new TypeError('receiptId must be a safe non-empty ID');\n  if (progress.resolvedReceipts.includes(receiptId)) return { progress: clone(progress), offer: null, duplicate: true };\n  if (!/^intro-(?:[1-9]|1[0-5])$/.test(receiptId) && !progress.activeWarmupIds.includes(receiptId)) {\n    throw new RangeError('Only a scheduled basic exercise room can grant a stat choice');\n  }\n  const resolvedReceipts = [...progress.resolvedReceipts, receiptId];\n  const offer = { receiptId, source: 'warmup', choices: statChoices() };\n  const isScheduledIntroWarmup = /^intro-(?:[1-9]|1[0-5])$/.test(receiptId);\n  const next = { ...progress, resolvedReceipts, upgradeOffers: [...progress.upgradeOffers, offer], coins: 0,\n    introWarmupsResolved: isScheduledIntroWarmup\n      ? Math.min(15, (progress.introWarmupsResolved ?? 0) + 1)\n      : (progress.introWarmupsResolved ?? 0) };\n  return { progress: next, offer, duplicate: false };\n}\n\n/** Record opening encounter clears: minis after5/10, first main after15. */\nexport function resolveIntroBoss(progress, bossId, receiptId) {\n  const bossIndex = openingBosses.findIndex(boss => boss.id === bossId);\n  if (bossIndex < 0) throw new RangeError(`unknown intro boss: ${bossId}`);\n  if (!isSafeId(receiptId)) throw new TypeError('receiptId must be a safe non-empty ID');\n  if (progress.introBossReceipts.some(r => r.receiptId === receiptId || r.bossId === bossId)) {\n    return { progress: clone(progress), duplicate: true };\n  }\n  const requiredWarmups = openingBosses[bossIndex].after;\n  if (!introReady(progress, requiredWarmups)) {\n    throw new Error(`The first ${requiredWarmups} intro warmups must resolve before ${bossId}`);\n  }\n  if (openingBosses.slice(0,bossIndex).some(boss => !progress.introBossReceipts.some(r => r.bossId === boss.id))) {\n    throw new Error('Each earlier miniboss must resolve before this encounter');\n  }\n  const introBossReceipts = [...progress.introBossReceipts, { bossId, receiptId }];\n  const introCompleted = introCleared({ introBossReceipts });\n  return { progress: { ...progress, introBossReceipts,\n    introCompleted, unlockedFullRooms: introCompleted, stage: introCompleted ? 'full-game' : 'initial-gauntlet' }, duplicate: false };\n}\n\n/** Completion of the full-game room only advances sequence state; its upgrades come from warmups. */\nexport function resolveFullGameRoom(progress, receiptId) {\n  if (!isSafeId(receiptId)) throw new TypeError('receiptId must be a safe non-empty ID');\n  if (!progress.activeBundleId) throw new Error('No full-room bundle is active');\n  if (progress.activeWarmupIds.some(id => !progress.resolvedReceipts.includes(id))) {\n    throw new Error('All three warmup receipts must resolve before the full-game room');\n  }\n  return { ...progress, activeBundleId: null, activeWarmupIds: [], lastFullGameReceipt: receiptId };\n}\n\n/** Start a later run without replaying the one-time initial gauntlet. */\nexport function resetWarmupRun(progress, seed = progress.seed) {\n  const completed = introCleared(progress);\n  return { ...emptyProgress(seed, completed ? 'full-game' : 'initial-gauntlet'),\n    introBossReceipts: clone(progress.introBossReceipts), introCompleted: completed, unlockedFullRooms: completed };\n}\n","modules/roguelite/weapon-proficiency.mjs":"// Bounded NVIDIA draft, reviewed and corrected: recompute totals from ZERO, strict own keys.\r\nconst PROFICIENCY_STYLES = Object.freeze(['knight', 'monk', 'dual', 'mage']);\r\n\r\nexport const PROFICIENCY_LIMITS = Object.freeze({\r\n  receipts: 4096,\r\n  unitsPerAward: 3,\r\n  unitsPerStyle: 12288\r\n});\r\n\r\nexport const MASTERY_THRESHOLDS = Object.freeze([0, 3, 9, 18, 30, 45]);\r\n\r\nconst OWN = Object.prototype.hasOwnProperty;\r\nconst OBJECT_PROTOTYPE = Object.prototype;\r\n\r\nfunction isPlainJsonObject(value) {\r\n  if (value === null || typeof value !== 'object') {\r\n    return false;\r\n  }\r\n  const prototype = Object.getPrototypeOf(value);\r\n  return prototype === OBJECT_PROTOTYPE || prototype === null;\r\n}\r\n\r\nfunction hasExactKeys(value, keys) {\r\n  if (!isPlainJsonObject(value)) {\r\n    return false;\r\n  }\r\n  const actual = Reflect.ownKeys(value);\r\n  if (actual.length !== keys.length) {\r\n    return false;\r\n  }\r\n  for (const key of keys) {\r\n    if (!OWN.call(value, key)) {\r\n      return false;\r\n    }\r\n  }\r\n  return true;\r\n}\r\n\r\nfunction isSafeNonNegativeInteger(value) {\r\n  return Number.isSafeInteger(value) && value >= 0;\r\n}\r\n\r\nfunction isValidStyleId(styleId) {\r\n  return typeof styleId === 'string' && PROFICIENCY_STYLES.includes(styleId);\r\n}\r\n\r\nfunction isValidSourceId(sourceId) {\r\n  return (\r\n    typeof sourceId === 'string' &&\r\n    sourceId.length <= 256 && /^[A-Za-z0-9]/.test(sourceId) &&\r\n    !/[^A-Za-z0-9:_.\\/-]/.test(sourceId)\r\n  );\r\n}\r\n\r\nexport function createWeaponProficiency() {\r\n  return {\r\n    version: 1,\r\n    units: {\r\n      knight: 0,\r\n      monk: 0,\r\n      dual: 0,\r\n      mage: 0\r\n    },\r\n    receipts: []\r\n  };\r\n}\r\n\r\nexport function restoreWeaponProficiency(saved) {\r\n  if (!hasExactKeys(saved, ['version', 'units', 'receipts'])) {\r\n    throw new TypeError('Invalid weapon proficiency ledger');\r\n  }\r\n\r\n  if (saved.version !== 1) {\r\n    throw new TypeError('Unsupported weapon proficiency version');\r\n  }\r\n\r\n  if (\r\n    !hasExactKeys(saved.units, PROFICIENCY_STYLES) ||\r\n    !Array.isArray(saved.receipts)\r\n  ) {\r\n    throw new TypeError('Invalid weapon proficiency ledger');\r\n  }\r\n\r\n  const totals = {\r\n    knight: 0,\r\n    monk: 0,\r\n    dual: 0,\r\n    mage: 0\r\n  };\r\n\r\n  for (const style of PROFICIENCY_STYLES) {\r\n    const value = saved.units[style];\r\n    if (\r\n      !isSafeNonNegativeInteger(value) ||\r\n      value > PROFICIENCY_LIMITS.unitsPerStyle\r\n    ) {\r\n      throw new TypeError('Invalid proficiency units');\r\n    }\r\n  }\r\n\r\n  if (saved.receipts.length > PROFICIENCY_LIMITS.receipts) {\n    throw new RangeError('Weapon proficiency receipt limit exceeded');\n  }\n  if (Object.getPrototypeOf(saved.receipts) !== Array.prototype ||\n      Reflect.ownKeys(saved.receipts).length !== saved.receipts.length + 1) {\n    throw new TypeError('Invalid proficiency receipt array');\n  }\n\n  const sourceIds = new Set();\n  const receipts = [];\r\n\r\n  for (const receipt of saved.receipts) {\r\n    if (\r\n      !hasExactKeys(receipt, ['sourceId', 'styleId', 'units']) ||\r\n      !isValidSourceId(receipt.sourceId) ||\r\n      !isValidStyleId(receipt.styleId) ||\r\n      !Number.isSafeInteger(receipt.units) ||\r\n      receipt.units < 1 ||\r\n      receipt.units > PROFICIENCY_LIMITS.unitsPerAward\r\n    ) {\r\n      throw new TypeError('Invalid weapon proficiency receipt');\r\n    }\r\n\r\n    if (sourceIds.has(receipt.sourceId)) {\r\n      throw new TypeError('Duplicate weapon proficiency sourceId');\r\n    }\r\n    sourceIds.add(receipt.sourceId);\n\r\n    totals[receipt.styleId] += receipt.units;\r\n    if (\r\n      !Number.isSafeInteger(totals[receipt.styleId]) ||\r\n      totals[receipt.styleId] > PROFICIENCY_LIMITS.unitsPerStyle\r\n    ) {\r\n      throw new RangeError('Weapon proficiency style unit limit exceeded');\r\n    }\r\n\r\n    receipts.push({\r\n      sourceId: receipt.sourceId,\r\n      styleId: receipt.styleId,\r\n      units: receipt.units\r\n    });\r\n  }\r\n\r\n  for (const style of PROFICIENCY_STYLES) {\r\n    if (saved.units[style] !== totals[style]) {\r\n      throw new TypeError('Inconsistent weapon proficiency totals');\r\n    }\r\n  }\r\n\r\n  return {\r\n    version: 1,\r\n    units: totals,\n    receipts\n  };\r\n}\r\n\r\nexport function awardWeaponProficiency(saved, award) {\r\n  const ledger = restoreWeaponProficiency(saved);\r\n\r\n  if (!hasExactKeys(award, ['sourceId', 'styleId', 'units'])) {\r\n    throw new TypeError('Invalid weapon proficiency award');\r\n  }\r\n\r\n  const { sourceId, styleId, units } = award;\r\n\r\n  if (\r\n    !isValidSourceId(sourceId) ||\r\n    !isValidStyleId(styleId) ||\r\n    !Number.isSafeInteger(units) ||\r\n    units < 1 ||\r\n    units > PROFICIENCY_LIMITS.unitsPerAward\r\n  ) {\r\n    throw new TypeError('Invalid weapon proficiency award');\r\n  }\r\n\r\n  const existing = ledger.receipts.find((receipt) => receipt.sourceId === sourceId);\r\n\r\n  if (existing) {\r\n    if (existing.styleId !== styleId || existing.units !== units) {\r\n      throw new Error('Conflicting weapon proficiency award');\r\n    }\r\n    return { ledger, awarded: false };\r\n  }\r\n\r\n  if (ledger.receipts.length >= PROFICIENCY_LIMITS.receipts) {\r\n    throw new RangeError('Weapon proficiency receipt limit exceeded');\r\n  }\r\n\r\n  const nextTotal = ledger.units[styleId] + units;\r\n  if (\r\n    !Number.isSafeInteger(nextTotal) ||\r\n    nextTotal > PROFICIENCY_LIMITS.unitsPerStyle\r\n  ) {\r\n    throw new RangeError('Weapon proficiency style unit limit exceeded');\r\n  }\r\n\r\n  ledger.receipts.push({ sourceId, styleId, units });\r\n  ledger.units[styleId] = nextTotal;\r\n\r\n  return { ledger, awarded: true };\r\n}\r\n\r\nexport function weaponMastery(saved, styleId) {\r\n  const ledger = restoreWeaponProficiency(saved);\r\n\r\n  if (!isValidStyleId(styleId)) {\r\n    throw new TypeError('Invalid weapon proficiency style');\r\n  }\r\n\r\n  const units = ledger.units[styleId];\r\n  let level = 0;\r\n\r\n  for (let index = 1; index < MASTERY_THRESHOLDS.length; index += 1) {\r\n    if (units >= MASTERY_THRESHOLDS[index]) {\r\n      level = index;\r\n    }\r\n  }\r\n\r\n  const nextThreshold =\r\n    level + 1 < MASTERY_THRESHOLDS.length\r\n      ? MASTERY_THRESHOLDS[level + 1]\r\n      : null;\r\n\r\n  let tierUnlockCandidate = 2;\r\n  if (units < MASTERY_THRESHOLDS[2]) {\r\n    tierUnlockCandidate = 0;\r\n  } else if (units < MASTERY_THRESHOLDS[4]) {\r\n    tierUnlockCandidate = 1;\r\n  }\r\n\r\n  return {\r\n    styleId,\r\n    units,\r\n    level,\r\n    nextThreshold,\r\n    tierUnlockCandidate\r\n  };\r\n}\r\n\r\n// Pure helper only: the host must derive sourceId and the equipped style only\r\n// after a verified boss clear, and persist the ledger atomically. This helper\r\n// must not be used for raw movement credit or as a network endpoint.\n","modules/roguelite/weapon-style-catalog.mjs":"// IDs adapt existing equipment; nothing here grants ownership or trusts a saved look.\nimport {CLASS_IDS, classGear} from './class-combat.mjs';\n\nexport const WEAPON_STYLE_CATALOG_VERSION = 1;\nexport const WEAPON_STYLES = Object.freeze([\n  {id:'knight', name:'Shield and lance', aura:'Retains the shield-and-lance aura.'},\n  {id:'monk', name:'Fist style', aura:'Raise both hands to charge the existing fist aura.'},\n  {id:'dual', name:'Paired blades', aura:'Retains the paired-blade aura and opposite-hand counters.'},\n  {id:'mage', name:'Elemental focus', aura:'Retains the elemental aura, traced spells and active shield.'},\n].map(Object.freeze));\n\n// Ordered exactly like pod/gala-weapons.js and pod/gala-rig/weapon.mjs TYPES.\n// The modern pixel designs are sci-fi weapons, even where the saved family ID is medieval.\nexport const GALA_WEAPON_FAMILIES = Object.freeze([\n  ['rapier','Plasma blade','knight'], ['greatsword','Ion cleaver','knight'],\n  ['dagger','Phase dagger','dual'], ['sabre','Arc pistol','mage'],\n  ['axe','Pulse rifle','mage'], ['hammer','Rail cannon','mage'],\n  ['mace','Tesla emitter','mage'], ['flail','Tether drone','mage'],\n  ['spear','Particle lance','knight'], ['trident','Tri-beam fork','knight'],\n  ['halberd','Rocket pod','mage'], ['scythe','Gravity reaper','knight'],\n  ['bow','Photon bow','mage'], ['crossbow','Gauss rifle','mage'],\n  ['chakram','Orbit disc','dual'], ['gauntlets','Power gauntlets','monk'],\n  ['staff','Gravity rod','mage'], ['wand','Sonic disruptor','mage'],\n  ['tome','Nanite hive','mage'], ['cannon','Plasma cannon','mage'],\n].map(([id,name,styleId],designIndex)=>Object.freeze({id,name,styleId,designIndex})));\nconst GALA = new Map(GALA_WEAPON_FAMILIES.map(row=>[row.id,row]));\n\n// battle-pass-rewards.mjs WEAPONS and coach-world-rts/npc-progression.mjs WEAPONS.\n// Cage rewards currently have no held pixel mesh (cage.ts weaponBay says so).\n// avatarType is an explicit rig stand-in, never a claim that these are Gala entitlements.\nexport const OWNED_WEAPON_MAPPINGS = Object.freeze([\n  ['chest-w1','Glove on a Stick','monk','gauntlets'],\n  ['chest-w2','Chrome Bar Mace','knight','mace'],\n  ['arms-w1','Dumbbellchucks','dual','dagger'],\n  ['arms-w2','Pull-Up Staff','knight','spear'],\n  ['quads-w1','Squat-Rack Axe','knight','greatsword'],\n  ['quads-w2','Kettlebell Flail','knight','flail'],\n  ['glutes-w1','Band Slingshot','mage','bow'],\n  ['glutes-w2','Hip-Thrust Hammer','knight','hammer'],\n  ['yoga-w1','Mat Bo Staff','knight','spear'],\n  ['yoga-w2','Block Gauntlets','monk','gauntlets'],\n  ['martial-arts-w1','Black-Belt Whip','dual','flail'],\n  ['martial-arts-w2','Dummy Club','knight','mace'],\n  ['cardio-w1','Jump-Rope Lasso','dual','flail'],\n  ['cardio-w2','Shoe Boomerang','dual','chakram'],\n  ['meditation-w1','Singing-Bowl Aegis','mage','tome'],\n  ['meditation-w2','Incense Wand','mage','wand'],\n].map(([id,name,styleId,avatarType])=>Object.freeze({id,name,styleId,avatarType,\n  combatTier:0,ownershipSource:'npc-or-battle-pass',visualSource:'gala-rig-substitute',visualExact:false})));\n\nconst RUN_AVATARS = {knight:'spear', monk:'gauntlets', dual:'dagger', mage:'wand'};\nexport const RUN_WEAPON_MAPPINGS = Object.freeze(CLASS_IDS.flatMap(styleId=>[0,1,2].map(tier=>{\n  const gear = classGear(styleId,tier);\n  return Object.freeze({id:gear.id,name:gear.name,styleId,combatTier:tier,starter:tier===0,\n    avatarType:RUN_AVATARS[styleId],avatarTier:0,\n    ownershipSource:tier===0?'starter':'run-gear',visualSource:'gala-rig-substitute',visualExact:false});\n})));\nconst EQUIPMENT = new Map([...RUN_WEAPON_MAPPINGS,...OWNED_WEAPON_MAPPINGS].map(row=>[row.id,row]));\nconst plain = value=>value!==null&&typeof value==='object'&&\n  [Object.prototype,null].includes(Object.getPrototypeOf(value));\nconst exact = (value,keys)=>plain(value)&&Reflect.ownKeys(value).length===keys.length&&keys.every(key=>Object.hasOwn(value,key));\n\n/** Serialization of the real {type,tier} representation, not an ownership record. */\nexport function weaponIdForGala(value) {\n  if(!exact(value,['type','tier'])||!GALA.has(value.type)||!Number.isInteger(value.tier)||value.tier<0||value.tier>20)\n    throw new TypeError('Invalid Gala weapon');\n  return `gala:${value.type}:${value.tier}`;\n}\n\nfunction authority(value) {\n  if(!plain(value)||Reflect.ownKeys(value).some(key=>!['weaponIds','galaWeaponTiers'].includes(key)))\n    throw new TypeError('Expected server-derived equipment authority');\n  const {weaponIds=[],galaWeaponTiers={}}=value;\n  if(!Array.isArray(weaponIds)||weaponIds.length>64||new Set(weaponIds).size!==weaponIds.length||\n    weaponIds.some(id=>typeof id!=='string'||!EQUIPMENT.has(id))) throw new TypeError('Invalid owned weapon IDs');\n  if(!plain(galaWeaponTiers)||Reflect.ownKeys(galaWeaponTiers).some(type=>!GALA.has(type)||\n    !Number.isInteger(galaWeaponTiers[type])||galaWeaponTiers[type]<0||galaWeaponTiers[type]>20))\n    throw new TypeError('Invalid authoritative Gala tiers');\n  return {weaponIds,galaWeaponTiers};\n}\n\n/** Host must supply ownership from durable NPC/pass grants and account performance.\n * war_room_arsenals, avatar look, local unlock-store and request body are NOT authority.\n * Visual tier never raises combat tier; mastery only produces a separate offer candidate.\n */\nexport function resolveOwnedWeapon(weaponId,owned={}) {\n  const {weaponIds,galaWeaponTiers}=authority(owned);\n  if(typeof weaponId!=='string') throw new TypeError('Invalid weapon ID');\n  const row=EQUIPMENT.get(weaponId);\n  if(row) {\n    if(!row.starter&&!weaponIds.includes(weaponId)) throw new RangeError('Weapon is not owned');\n    return {weaponId,name:row.name,styleId:row.styleId,combatTier:row.combatTier,\n      avatarWeapon:{type:row.avatarType,tier:row.avatarTier??0},\n      visualSource:row.visualSource,visualExact:row.visualExact};\n  }\n  const match=/^gala:([a-z]+):(0|[1-9]|1[0-9]|20)$/.exec(weaponId);\n  if(!match||match[0]!==weaponId||!GALA.has(match[1])) throw new TypeError('Unknown weapon ID');\n  const [,type,tierText]=match,tier=Number(tierText);\n  if(tier>(Object.hasOwn(galaWeaponTiers,type)?galaWeaponTiers[type]:0)) throw new RangeError('Gala tier is not owned');\n  return {weaponId,name:GALA.get(type).name,styleId:GALA.get(type).styleId,combatTier:0,avatarWeapon:{type,tier},\n    visualSource:'gala-rig',visualExact:true};\n}\n\n/** A selected look is intent only. Authority must come from server-derived tiers. */\nexport function resolveEquippedAvatarWeapon(avatarWeapon,owned={}) {\n  return resolveOwnedWeapon(weaponIdForGala(avatarWeapon),owned);\n}\n\n/** All existing owned tiers, in catalog order; maximum 448 options, no grants. */\nexport function listedOwnedWeapons(owned={}) {\n  const valid=authority(owned);\n  const ids=[...RUN_WEAPON_MAPPINGS,...OWNED_WEAPON_MAPPINGS]\n    .filter(row=>row.starter||valid.weaponIds.includes(row.id)).map(row=>row.id);\n  for(const {id:type} of GALA_WEAPON_FAMILIES) {\n    const max=Object.hasOwn(valid.galaWeaponTiers,type)?valid.galaWeaponTiers[type]:0;\n    for(let tier=0;tier<=max;tier++) ids.push(weaponIdForGala({type,tier}));\n  }\n  return ids.map(id=>resolveOwnedWeapon(id,valid));\n}\n","modules/roguelite/world-run-client.mjs":"import {authFetch} from '../../auth-client.mjs';\r\nimport {authTransitions} from '../../auth-transition.mjs';\r\nimport {performanceOwner} from '../../performance-progress.mjs';\r\nimport {grantUnlock,isGranted} from '../../unlock-ledger.mjs';\r\nimport {CLASS_IDS,CLASS_INPUT_BOOLS,normalizeClassInput} from './class-combat.mjs';\nimport {bootstrapGameGeneration,captureGameGeneration,gameGenerationHeaders,acceptGameGeneration,subscribeGameGeneration} from '../coach-world/game-generation-client.mjs';\nimport {normalizeAuthoredSample} from './run-authored-rooms.mjs';\nimport {validateRescueInput} from './rescue-state.mjs';\n\r\nconst canonical=value=>{\r\n if(value===null||typeof value!=='object')return JSON.stringify(value);\r\n if(Array.isArray(value))return `[${value.map(canonical).join(',')}]`;\r\n return `{${Object.keys(value).sort().map(key=>`${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;\r\n};\r\nconst digest=async value=>[...new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(canonical(value))))].map(byte=>byte.toString(16).padStart(2,'0')).join('');\r\nconst uuid=()=>crypto.randomUUID();\r\nconst finite=value=>Number.isFinite(value)&&value>=0;\r\nconst BOSS_GESTURES=Object.freeze(['left','punchLeft','punchRight','crouch','jump','jog']);\r\nconst POND_INPUTS=Object.freeze(['balanced','handOut']);\r\nconst GRASS_INPUTS=Object.freeze(['controlValid','exerciseEligible','height01','steer']);\r\nconst ARCADE_ZONES=Object.freeze(['left','middle','right','up']);\r\nconst SHOP_ACTIONS=new Set(['open-door','grab','return','checkout','exit','attack']);\r\nfunction acceptedFrames(frames){\r\n if(!Array.isArray(frames))throw new TypeError('World Run frames must be an array.');\r\n return frames.map(frame=>{\r\n  if(!frame||typeof frame!=='object'||Array.isArray(frame))throw new TypeError('Invalid World Run frame.');\r\n  const normalized={op:frame.op,frameSeq:frame.frameSeq,now:frame.now,tracked:frame.tracked};\r\n  for(const k of ['sourceTs','quality','cueAck'])if(Object.hasOwn(frame,k))normalized[k]=structuredClone(frame[k]);\n  for(const kind of ['yoga','secret','altar','rescue'])if(Object.hasOwn(frame,kind)){\n   if(frame.op!=='sample')throw new TypeError('Room inputs require sample frames');\n   normalized[kind]=kind==='rescue'?structuredClone(validateRescueInput(frame[kind])):normalizeAuthoredSample(kind,frame[kind]);\n  }\n  if(frame.op==='sample'){if(Object.hasOwn(frame,'value'))normalized.value=frame.value;if(Object.hasOwn(frame,'choice'))normalized.choice=frame.choice;}\r\n  else if(frame.value!==undefined||frame.choice!==undefined)throw new TypeError('Track frames cannot carry sample fields.');\r\n  if(Object.hasOwn(frame,'boss')){\r\n   if(frame.op!=='sample'||!frame.boss||typeof frame.boss!=='object'||Array.isArray(frame.boss))throw new TypeError('Boss gestures belong to sample frames.');\r\n   const gestures={};for(const key of BOSS_GESTURES){const value=frame.boss[key];if(value!==undefined&&typeof value!=='boolean')throw new TypeError(`Boss gesture ${key} must be boolean.`);gestures[key]=value===true;}\r\n   normalized.boss={...gestures,...(Object.hasOwn(frame.boss,'spell')||CLASS_INPUT_BOOLS.some(key=>Object.hasOwn(frame.boss,key))?normalizeClassInput(frame.boss):{})};\r\n  }\r\n  if(Object.hasOwn(frame,'pond')){\r\n   if(frame.op!=='sample'||!frame.pond||typeof frame.pond!=='object'||Array.isArray(frame.pond))throw new TypeError('Pond inputs belong to sample frames.');\r\n   const pond={};for(const key of POND_INPUTS){const value=frame.pond[key];if(value!==undefined&&typeof value!=='boolean')throw new TypeError(`Pond input ${key} must be boolean.`);pond[key]=value===true;}\r\n   normalized.pond=pond;\r\n  }\r\n  if(Object.hasOwn(frame,'grass')){\r\n   if(frame.op!=='sample'||!frame.grass||typeof frame.grass!=='object'||Array.isArray(frame.grass))throw new TypeError('Grass inputs belong to sample frames.');\r\n   const grass={};for(const key of GRASS_INPUTS){const value=frame.grass[key];\r\n    if(key==='exerciseEligible'&&value===undefined){grass[key]=false;continue;}\r\n    if(key==='controlValid'||key==='exerciseEligible'){if(typeof value!=='boolean')throw new TypeError('Grass controlValid must be boolean.');grass[key]=value;}\r\n    else {const minimum=key==='height01'?0:-1;if(!Number.isFinite(value)||value<minimum||value>1)throw new TypeError(`Grass ${key} is outside its accepted range.`);grass[key]=value;}\r\n   }\r\n   normalized.grass=grass;\r\n  }\r\n  if(Object.hasOwn(frame,'treasure')){\r\n   if(frame.op!=='sample'||!frame.treasure||typeof frame.treasure!=='object'||Array.isArray(frame.treasure)\r\n      ||Object.keys(frame.treasure).length!==1||!Object.hasOwn(frame.treasure,'jogging')||typeof frame.treasure.jogging!=='boolean')throw new TypeError('Treasure input requires only a boolean jogging value.');\r\n   normalized.treasure={jogging:frame.treasure.jogging};\r\n  }\r\n  if(Object.hasOwn(frame,'arcade')){\r\n   if(frame.op!=='sample'||!frame.arcade||typeof frame.arcade!=='object'||Array.isArray(frame.arcade))throw new TypeError('Arcade inputs belong to sample frames.');\r\n   const arcade=frame.arcade;\r\n   if(Object.hasOwn(arcade,'energy')||Object.hasOwn(arcade,'coverage')){\r\n    if(['actions','action','hidden','side','beam','zones','fires'].some(key=>Object.hasOwn(arcade,key))\r\n      ||!Object.hasOwn(arcade,'energy')||!Object.hasOwn(arcade,'coverage')\r\n      ||!Number.isFinite(arcade.energy)||arcade.energy<0||arcade.energy>1\r\n      ||!Number.isFinite(arcade.coverage)||arcade.coverage<0||arcade.coverage>1)throw new TypeError('Dance inputs require energy and coverage between zero and one.');\r\n    normalized.arcade={energy:arcade.energy,coverage:arcade.coverage};\r\n   }else if(Object.hasOwn(arcade,'actions')){\r\n    if(Object.hasOwn(arcade,'action')||['hidden','side','beam','zones','fires'].some(key=>Object.hasOwn(arcade,key)))throw new TypeError('Clay arcade frames accept actions only.');\r\n    const valid=new Set(['jump','slide','left','right']);\r\n    if(!Array.isArray(arcade.actions)||arcade.actions.length>4||arcade.actions.some(action=>!valid.has(action)))throw new TypeError('Clay actions must be at most four jump, slide, left, or right controls.');\r\n    normalized.arcade={actions:[...new Set(arcade.actions)]};\r\n   }else{\r\n    if(Object.hasOwn(arcade,'action'))throw new TypeError('Singular arcade actions are not accepted.');\r\n    if(typeof arcade.hidden!=='boolean'||![-1,0,1].includes(arcade.side))throw new TypeError('Jelly hidden and side inputs are invalid.');\r\n    if(!arcade.beam||typeof arcade.beam!=='object'||Array.isArray(arcade.beam)||typeof arcade.beam.on!=='boolean'||!Number.isFinite(arcade.beam.x)||arcade.beam.x < -1||arcade.beam.x>1)throw new TypeError('Jelly beam input is invalid.');\r\n    if(!arcade.zones||typeof arcade.zones!=='object'||Array.isArray(arcade.zones))throw new TypeError('Jelly zones are invalid.');\r\n    const zones={};for(const key of ARCADE_ZONES){const value=arcade.zones[key];if(!Number.isFinite(value)||value<0||value>1)throw new TypeError(`Jelly ${key} zone is outside its accepted range.`);zones[key]=value;}\r\n    if(!Array.isArray(arcade.fires)||arcade.fires.length>2)throw new TypeError('Jelly fires must contain at most two samples.');\r\n    const fires=arcade.fires.map(fire=>{if(!fire||typeof fire!=='object'||Array.isArray(fire)||!(fire.aimX===null||Number.isFinite(fire.aimX)&&fire.aimX>=-1&&fire.aimX<=1)||!Number.isFinite(fire.power)||fire.power<0||fire.power>100)throw new TypeError('Jelly fire sample is invalid.');return {aimX:fire.aimX,power:fire.power};});\r\n    normalized.arcade={hidden:arcade.hidden,side:arcade.side,beam:{on:arcade.beam.on,x:arcade.beam.x},zones,fires};\r\n   }\r\n  }\r\n  return normalized;\r\n });\r\n}\r\n\r\n/** Authenticated, owner fenced World Run transport with a durable exact-event outbox. */\r\nexport function createWorldRunClient({request=authFetch,transitions=authTransitions(),getAccount=()=>globalThis.myr5AuthenticatedAccount,\r\n storage=globalThis.localStorage,now=()=>performance.now(),sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms)),makeId=uuid,onTiming=null,\r\n grantRewardPack=grantUnlock,isRewardPackGranted=isGranted}={}){\r\n let captured=null,envelope=null,disposed=false,tail=Promise.resolve(),pendingFrames=[],gameGeneration=null,bootstrap=null;\n const abort=new AbortController();\n const ensureGeneration=async()=>{\n  if(gameGeneration!==null)return;\n  bootstrap??=bootstrapGameGeneration({fetchImpl:request,signal:abort.signal,storage});\n  const metadata=await bootstrap;assertLive();gameGeneration=metadata.gameGeneration;\n  if(captureGameGeneration()!==gameGeneration)disposeAndThrow();\n };\n const timing=(stage,event,extra={})=>{\r\n  if(typeof onTiming!=='function'||!['run_frame','boss_finish'].includes(event?.type))return;\r\n  try{onTiming({stage,type:event.type,eventId:event.id??null,clientSeq:event.clientSeq??null,monotonicMs:now(),wallMs:Date.now(),...extra});}catch{}\r\n };\r\n const subscriptions=[];\r\n const context=()=>{\r\n  const account=getAccount(),accountId=account?.user?.id,epoch=account?.dataEpoch;\r\n  if(typeof accountId!=='string'||!accountId||!Number.isSafeInteger(epoch)||epoch<1)throw Object.assign(new Error('Sign in to use the authenticated World Run.'),{code:'world_run_signin_required'});\r\n  return {account,accountId,epoch,owner:performanceOwner(storage,account)};\r\n };\r\n const assertLive=()=>{\r\n  if(disposed)throw Object.assign(new Error('Account changed. Refresh to continue.'),{code:'auth_transition'});\r\n  if(!captured)return;\r\n  const latest=context();\r\n  if(latest.owner!==captured.owner||latest.epoch!==captured.epoch||latest.accountId!==captured.accountId)disposeAndThrow();\r\n };\r\n const disposeAndThrow=()=>{disposed=true;throw Object.assign(new Error('Account changed. Refresh to continue.'),{code:'auth_transition'});};\r\n const capture=()=>{\r\n  const current=context();\r\n  if(captured&&(captured.owner!==current.owner||captured.epoch!==current.epoch||captured.accountId!==current.accountId))disposeAndThrow();\r\n  captured=current;return current;\r\n };\r\n const keyFor=runId=>`myr5-world-run-outbox-v1/${encodeURIComponent(captured.owner)}/${captured.epoch}/${encodeURIComponent(runId)}`;\r\n const readStore=runId=>{\r\n  let value;try{value=JSON.parse(storage.getItem(keyFor(runId))||'null');}catch{throw new Error('World Run outbox storage is unreadable; events remain blocked.');}\r\n  if(value===null)return {pending:[],archive:[]};\r\n  if(!value||!Array.isArray(value.pending)||!Array.isArray(value.archive))throw new Error('World Run outbox storage is invalid; events remain blocked.');\r\n  if(value.gameGeneration!==gameGeneration){storage.removeItem(keyFor(runId));return {gameGeneration,pending:[],archive:[]};}\n  for(const item of value.pending)if(item.gameGeneration!==gameGeneration){storage.removeItem(keyFor(runId));return {gameGeneration,pending:[],archive:[]};}\n  return value;\n };\n const writeStore=(runId,value)=>storage.setItem(keyFor(runId),JSON.stringify({...value,gameGeneration}));\n const ticket=()=>transitions.capture();\r\n async function jsonRequest(path,{method='GET',body,headers={},idempotencyKey,timingEvent,timingAttempt}={}){\r\n  assertLive();await ensureGeneration();const owner=capture(),authTicket=ticket(),requestHeaders=new Headers(headers);\n  for(const [key,value] of Object.entries(gameGenerationHeaders(gameGeneration)))requestHeaders.set(key,value);\n  requestHeaders.set('X-Target-Account',owner.accountId);requestHeaders.set('X-Expected-Data-Epoch',String(owner.epoch));\r\n  if(body!==undefined)requestHeaders.set('Content-Type','application/json');\r\n  if(idempotencyKey)requestHeaders.set('Idempotency-Key',idempotencyKey);\r\n  timing('request_send',timingEvent,{attempt:timingAttempt});\r\n  const signal=authTicket.signal?AbortSignal.any([authTicket.signal,abort.signal]):abort.signal;\n  const response=await request(path,{method,headers:requestHeaders,body:body===undefined?undefined:JSON.stringify(body),signal,cache:'no-store'});\n  timing('response_headers',timingEvent,{attempt:timingAttempt,status:response.status});\r\n  transitions.assertCurrent(authTicket);assertLive();\r\n  let value;try{value=await response.json();}catch{throw Object.assign(new Error('World Run returned an unreadable response.'),{status:response.status});}\r\n  timing('response_body',timingEvent,{attempt:timingAttempt,status:response.status});\r\n  transitions.assertCurrent(authTicket);assertLive();\r\n  if(value.gameGeneration!==undefined)acceptGameGeneration(value,gameGeneration,{storage});\n  if(!response.ok)throw Object.assign(new Error(value.error||value.code||'World Run request failed.'),{status:response.status,code:value.code||value.error,details:value.details});\n  acceptGameGeneration(value,gameGeneration,{storage});\n  return value;\r\n }\r\n function install(candidate){\r\n  if(!candidate||candidate.targetAccountId!==captured.accountId||candidate.dataEpoch!==captured.epoch||typeof candidate.runId!=='string')throw new Error('World Run owner or epoch envelope mismatch.');\r\n  if(envelope&&candidate.runId===envelope.runId&&candidate.runRevision<envelope.runRevision)return false;\r\n  if(envelope&&candidate.runId!==envelope.runId&&envelope.runStatus==='active')throw new Error('A different active World Run cannot replace this session.');\r\n  assertLive();envelope=candidate;grantConfirmedRewardPacks(candidate);return true;\r\n }\r\n function grantConfirmedRewardPacks(candidate){\r\n  const descriptors=candidate?.runState?.confirmedRewardPacks;\r\n  if(!Array.isArray(descriptors))return;\r\n  const account=captured.account;\r\n  for(const pack of descriptors){\r\n   if(!pack||typeof pack!=='object'||Array.isArray(pack)||!['uncommon','rare','legendary'].includes(pack.tier)||!Number.isSafeInteger(pack.level)||pack.level<2)continue;\r\n   const id=`reward-pack:${pack.tier}:run-${candidate.runId}-${pack.level}`;\r\n   if(pack.id!==id)continue;\r\n   const options={account};\r\n   try{if(!isRewardPackGranted('reward-pack',id,options))grantRewardPack('reward-pack',id,options);}catch{}\r\n  }\r\n }\r\n const envelopeOf=value=>value?.envelope??value?.response?.envelope??value?.response?.response?.envelope??null;\r\n async function current(){capture();const value=await jsonRequest('/api/world/runs/current');const env=value.envelope;\r\n  if(env?.runId)install(env);else envelope=null;return value;}\r\n async function claim(env){\r\n  const id=makeId(),body={id,type:'writer-claim',expectedWriterGeneration:env.writerGeneration,expectedRunRevision:env.runRevision,now:now()};\r\n  const value=await jsonRequest(`/api/world/runs/${encodeURIComponent(env.runId)}/claim`,{method:'POST',body,idempotencyKey:id});\r\n  const next=envelopeOf(value);if(!next)throw new Error('Writer claim returned no run envelope.');install(next);return value;\r\n }\r\n async function recover(runId){\r\n  const store=readStore(runId);if(!store.pending.length)return {blocked:false};\r\n  const entries=store.pending.slice(0,64).map(item=>({writerGeneration:item.event.writerGeneration,clientSeq:item.event.clientSeq,eventHash:item.eventHash}));\r\n  const value=await jsonRequest(`/api/world/runs/${encodeURIComponent(runId)}/outbox-status`,{method:'POST',body:{entries}});\r\n  const outcomes=value.entries;if(!Array.isArray(outcomes)||outcomes.length!==entries.length)throw new Error('Outbox status response did not match its query.');\r\n  let blocked=false;\r\n  for(let i=0;i<outcomes.length;i++){\r\n   const result=outcomes[i],item=store.pending.find(entry=>entry.event.clientSeq===entries[i].clientSeq&&entry.event.writerGeneration===entries[i].writerGeneration);\r\n   if(!item||result.eventHash!==item.eventHash)throw new Error('Outbox status hash mismatch; pending event remains blocked.');\r\n   if(result.outcome==='ACCEPTED_MATCHING'){\r\n    const ackEnvelope=envelopeOf(result.response);store.pending=store.pending.filter(entry=>entry!==item);store.archive.push({event:item.event,eventHash:item.eventHash,outcome:result.outcome});\r\n    if(ackEnvelope)install(ackEnvelope);\r\n   }else if(result.outcome==='FINAL_FENCED_UNACCEPTED'||result.outcome==='FINAL_TERMINAL_UNACCEPTED'){\r\n    store.pending=store.pending.filter(entry=>entry!==item);store.archive.push({event:item.event,eventHash:item.eventHash,outcome:result.outcome,proof:result.proof});\r\n    if(result.outcome==='FINAL_TERMINAL_UNACCEPTED'||envelope?.runStatus!=='active'||envelope.writerGeneration<=item.event.writerGeneration)blocked=true;\r\n   }else{blocked=true;}\r\n  }\r\n  writeStore(runId,store);return {blocked,outcomes};\r\n }\r\n async function ack(item,value){\r\n  const store=readStore(item.runId),found=store.pending.find(entry=>entry.event.id===item.event.id&&entry.eventHash===item.eventHash);\r\n  if(found){store.pending=store.pending.filter(entry=>entry!==found);store.archive.push({event:item.event,eventHash:item.eventHash,outcome:'ACKNOWLEDGED'});writeStore(item.runId,store);}\r\n  const candidate=envelopeOf(value);if(candidate)install(candidate);timing('ack_installed',item.event);return value;\r\n }\r\n async function sendExact(item){\r\n  let failure;\r\n  for(let attempt=0;attempt<3;attempt++){\r\n   try{\r\n    const value=await jsonRequest(`/api/world/runs/${encodeURIComponent(item.runId)}/events`,{method:'POST',body:item.event,idempotencyKey:item.event.id,timingEvent:item.event,timingAttempt:attempt+1});\r\n    return ack(item,value);\r\n   }catch(error){if(disposed||/^game_generation/.test(error.code??''))throw error;timing('request_failed',item.event,{attempt:attempt+1,status:Number.isInteger(error.status)?error.status:null});failure=error;if(attempt<2)await sleep(150*(attempt+1));}\n  }\r\n  const result=await recover(item.runId);\r\n  if(result.blocked)throw Object.assign(new Error('Run event is unresolved; this session remains blocked until server status resolves it.'),{code:'world_run_outbox_blocked',cause:failure});\r\n  return {recovered:true};\r\n }\r\n function event(type,payload){\r\n  const enqueued=typeof onTiming==='function'?{enqueueMonotonicMs:now(),enqueueWallMs:Date.now()}:null;\r\n  timing('event_enqueued',{type});\r\n  const operation=tail.then(async()=>{\r\n   assertLive();if(!envelope||envelope.runStatus!=='active'||envelope.writerGeneration<1)throw new Error('Claim an active run before sending events.');\r\n   const status=await recover(envelope.runId);if(status.blocked)throw Object.assign(new Error('An earlier event is still unresolved.'),{code:'world_run_outbox_blocked'});\r\n   const body={id:makeId(),clientSeq:envelope.nextClientSeq,writerGeneration:envelope.writerGeneration,clockEpoch:envelope.clockEpoch,type,payload:structuredClone(payload)};\r\n   timing('event_ready',body,enqueued??{});\r\n   const eventHash=await digest({id:body.id,type:body.type,payload:body.payload,writerGeneration:body.writerGeneration,clockEpoch:body.clockEpoch});\r\n   assertLive();\r\n   const item={runId:envelope.runId,event:body,eventHash,gameGeneration};const store=readStore(envelope.runId);store.pending.push({event:body,eventHash,gameGeneration});writeStore(envelope.runId,store);\n   return sendExact(item);\r\n  });tail=operation.catch(()=>{});return operation;\r\n }\r\n async function begin(tutorialsEnabled=true,loadout,{heartId=null}={}){\n  capture();const id=makeId(),value=await jsonRequest('/api/world/runs',{method:'POST',body:{id,tutorialsEnabled,heartId,...(loadout?{loadout}:{})},idempotencyKey:id});\n  const env=envelopeOf(value);if(!env)throw new Error('Run start returned no pinned schedule envelope.');install(env);\r\n  if(value.resumed)throw Object.assign(new Error('An active World Run already exists. Choose Resume to claim it.'),{code:'world_run_resume_required'});\r\n  await claim(envelope);const status=await recover(envelope.runId);if(status.blocked)throw Object.assign(new Error('An earlier run event is unresolved.'),{code:'world_run_outbox_blocked'});\r\n  return envelope;\r\n }\r\n async function resume(){\r\n  const value=await current();if(!value.envelope?.runId)throw new Error('There is no active World Run to resume. Choose Start to create one.');\r\n  if(value.envelope.runStatus!=='active')throw new Error('This World Run has ended. Return to base.');\r\n  await claim(value.envelope);const status=await recover(envelope.runId);if(status.blocked)throw Object.assign(new Error('An earlier run event is unresolved; no new events can be sent.'),{code:'world_run_outbox_blocked'});\r\n  return envelope;\r\n }\r\n function dispose(){disposed=true;abort.abort();for(const unsubscribe of subscriptions.splice(0))unsubscribe();}\n subscriptions.push(subscribeGameGeneration(metadata=>{if(gameGeneration!==null&&metadata.gameGeneration!==gameGeneration)dispose();}));\n subscriptions.push(transitions.subscribe(()=>{if(captured){try{const latest=context();if(latest.owner!==captured.owner||latest.epoch!==captured.epoch||latest.accountId!==captured.accountId)disposed=true;}catch{disposed=true;}}}));\r\n return {get envelope(){return envelope;},get disposed(){return disposed;},current,begin,resume,event,recover,dispose,\r\n  async tutorial(visitId){return event('tutorial',{visitId});},async enter(visitId,at=now(),classId){if(classId!==undefined&&!CLASS_IDS.includes(classId))throw new TypeError('Unknown combat class');return event('room_enter',{visitId,now:at,...(classId===undefined?{}:{classId})});},\r\n  async pause(visitId,reason,paused){return event('run_pause',{visitId,reason,paused});},\r\n  async frames(visitId,frames){return event('run_frame',{visitId,clockEpoch:envelope.clockEpoch,frames:acceptedFrames(frames)});},\r\n  async routeChosen(visitId,roomId){if(roomId!==null&&typeof roomId!=='string')throw new TypeError('Route roomId must be a string or null.');return event('route_chosen',{visitId,roomId});},\r\n  async shopAction(visitId,action){\r\n   if(!action||typeof action!=='object'||Array.isArray(action)||!SHOP_ACTIONS.has(action.type))throw new TypeError('Invalid shop action.');\r\n   const normalized={type:action.type};\r\n   if(action.itemId!==undefined){if(typeof action.itemId!=='string')throw new TypeError('Shop itemId must be a string.');normalized.itemId=action.itemId;}\r\n   return event('shop_action',{visitId,action:normalized});\r\n  },\r\n  async treasureOpen(visitId,chestId){\r\n   if(typeof visitId!=='string'||!visitId||typeof chestId!=='string'||!chestId)throw new TypeError('Treasure opening requires the current visit and chest IDs.');\r\n   return event('treasure_open',{visitId,chestId});\r\n  },\r\n  async beginRescue(visitId){return event('rescue_begin',{visitId});},\n  async finishBoss(visitId){return event('boss_finish',{visitId});},\n  async advance(visitId){return event('run_advance',{visitId});},async end(){return event('run_end',{});}};\r\n}\r\n\r\nexport {canonical as canonicalWorldRunJson,digest as worldRunEventHash};\r\n","modules/roguelite/yoga-room.mjs":"// Pure reducer for the yoga room's opening gesture and qualified pose holds.\r\n// This module does not apply health, armor, XP, currency, or permanent rewards.\r\n\r\nexport const YOGA_ROOM_TUNING = Object.freeze({\r\n  openingPushMs: 400,\r\n  stageQualifiedMs: 10_000,\r\n  maxFrameMs: 100,\r\n  continuityGapMs: 300,\r\n});\r\n\r\nexport const YOGA_POSES = Object.freeze(['mountain', 'salute', 'warrior']);\n// Metadata only. The authenticated host owns receipt deduplication and application.\nexport const YOGA_COMPLETION_EFFECT = Object.freeze({\n  boonId: 'longerParry', visualId: 'protectioncircle',\n});\n\r\nconst STATE_KEYS = Object.freeze([\r\n  'version',\r\n  'encounterId',\r\n  'seed',\r\n  'status',\r\n  'stageIndex',\r\n  'stageMs',\r\n  'openingMs',\r\n  'lastObservationId',\r\n  'lastNow',\r\n  'previousEligible',\r\n  'previousPose',\r\n  'openingReleased',\r\n  'previousPush',\r\n]);\r\n\r\nconst ENCOUNTER_ID = /^[A-Za-z0-9:_-]{1,96}$/;\r\nconst clone = value => ({ ...value });\r\n\r\nfunction assertStateObject(state) {\r\n  if (!state || typeof state !== 'object' || Array.isArray(state)) {\r\n    throw new TypeError('Yoga room state must be an object');\r\n  }\r\n  const prototype = Object.getPrototypeOf(state);\r\n  if (prototype !== Object.prototype && prototype !== null) {\r\n    throw new TypeError('Yoga room state must be a plain JSON object');\r\n  }\r\n  const keys = Object.keys(state).sort();\r\n  if (keys.length !== STATE_KEYS.length ||\r\n      keys.some((key, index) => key !== [...STATE_KEYS].sort()[index])) {\r\n    throw new TypeError('Yoga room state has invalid fields');\r\n  }\r\n}\r\n\r\nfunction validateState(state) {\r\n  assertStateObject(state);\r\n\r\n  if (state.version !== 1) throw new TypeError('Unsupported yoga room state version');\r\n  if (typeof state.encounterId !== 'string' || !ENCOUNTER_ID.test(state.encounterId)) {\r\n    throw new TypeError('Invalid encounterId');\r\n  }\r\n  if (!Number.isSafeInteger(state.seed) || state.seed < 0) {\r\n    throw new TypeError('Invalid seed');\r\n  }\r\n  if (!['opening', 'holding', 'complete'].includes(state.status)) {\r\n    throw new TypeError('Invalid status');\r\n  }\r\n  if (!Number.isInteger(state.stageIndex) || state.stageIndex < 0 || state.stageIndex > 3) {\r\n    throw new TypeError('Invalid stageIndex');\r\n  }\r\n  if (!Array.isArray(state.stageMs) || state.stageMs.length !== 3) {\r\n    throw new TypeError('Invalid stageMs');\r\n  }\r\n  for (const value of state.stageMs) {\r\n    if (!Number.isFinite(value) || value < 0 || value > YOGA_ROOM_TUNING.stageQualifiedMs) {\r\n      throw new TypeError('Invalid stage qualified time');\r\n    }\r\n  }\r\n  if (!Number.isFinite(state.openingMs) || state.openingMs < 0 ||\r\n      state.openingMs > YOGA_ROOM_TUNING.openingPushMs) {\r\n    throw new TypeError('Invalid opening time');\r\n  }\r\n  if (!Number.isSafeInteger(state.lastObservationId) || state.lastObservationId < -1) {\r\n    throw new TypeError('Invalid lastObservationId');\r\n  }\r\n  if (state.lastNow !== null && (!Number.isFinite(state.lastNow) || state.lastNow < 0)) {\n    throw new TypeError('Invalid lastNow');\r\n  }\r\n  if (typeof state.previousEligible !== 'boolean' ||\r\n      typeof state.previousPush !== 'boolean' ||\r\n      typeof state.openingReleased !== 'boolean') {\r\n    throw new TypeError('Invalid yoga room flags');\r\n  }\r\n  if (state.previousPose !== null && !YOGA_POSES.includes(state.previousPose)) {\n    throw new TypeError('Invalid previousPose');\n  }\n  if (state.previousEligible !== (state.previousPose !== null) ||\n      (state.previousEligible && (state.status !== 'holding' ||\n        state.previousPose !== YOGA_POSES[state.stageIndex])) ||\n      (state.previousPush && (state.status !== 'opening' || !state.openingReleased)) ||\n      (state.lastNow === null && (state.previousEligible || state.previousPush))) {\n    throw new TypeError('Inconsistent continuity state');\n  }\n  if (state.lastObservationId === -1 && (state.lastNow !== null ||\n      state.status !== 'opening' || state.openingReleased || state.openingMs !== 0)) {\n    throw new TypeError('Inconsistent unobserved state');\n  }\n\r\n  if (state.status === 'opening') {\r\n    if (state.stageIndex !== 0 || state.stageMs.some(value => value !== 0)) {\r\n      throw new TypeError('Inconsistent opening state');\r\n    }\r\n    if (state.openingMs === YOGA_ROOM_TUNING.openingPushMs ||\n        (state.openingMs > 0 && !state.previousPush) ||\n        (!state.openingReleased && state.openingMs !== 0)) {\n      throw new TypeError('Inconsistent opening release state');\r\n    }\r\n  } else if (state.status === 'holding') {\r\n    if (state.stageIndex > 2 || !state.openingReleased ||\n        state.openingMs !== YOGA_ROOM_TUNING.openingPushMs) {\n      throw new TypeError('Inconsistent holding state');\r\n    }\r\n    for (let index = 0; index < state.stageIndex; index += 1) {\r\n      if (state.stageMs[index] !== YOGA_ROOM_TUNING.stageQualifiedMs) {\r\n        throw new TypeError('Inconsistent holding stage progress');\r\n      }\r\n    }\r\n    if (state.stageMs[state.stageIndex] === YOGA_ROOM_TUNING.stageQualifiedMs) {\n      throw new TypeError('Qualified stage was not advanced');\n    }\n    if (state.stageMs.some((value, index) => index > state.stageIndex && value !== 0)) {\n      throw new TypeError('Future stage already has progress');\n    }\n  } else {\r\n    if (state.stageIndex !== 3 || !state.openingReleased ||\n        state.openingMs !== YOGA_ROOM_TUNING.openingPushMs ||\r\n        state.stageMs.some(value => value !== YOGA_ROOM_TUNING.stageQualifiedMs)) {\r\n      throw new TypeError('Inconsistent complete state');\r\n    }\r\n  }\r\n}\r\n\r\nfunction freezeState(state) {\r\n  state.stageMs = Object.freeze([...state.stageMs]);\r\n  return Object.freeze(state);\r\n}\r\n\r\nfunction poseForStage(index) {\r\n  return YOGA_POSES[index];\r\n}\r\n\r\nexport function createYogaRoom({ encounterId, seed = 0 } = {}) {\r\n  if (typeof encounterId !== 'string' || !ENCOUNTER_ID.test(encounterId)) {\r\n    throw new TypeError('encounterId is required');\r\n  }\r\n  if (!Number.isSafeInteger(seed) || seed < 0) {\r\n    throw new TypeError('seed must be a nonnegative safe integer');\r\n  }\r\n\r\n  return freezeState({\r\n    version: 1,\r\n    encounterId,\r\n    seed,\r\n    status: 'opening',\r\n    stageIndex: 0,\r\n    stageMs: [0, 0, 0],\r\n    openingMs: 0,\r\n    lastObservationId: -1,\r\n    lastNow: null,\r\n    previousEligible: false,\r\n    previousPose: null,\r\n    openingReleased: false,\r\n    previousPush: false,\r\n  });\r\n}\r\n\r\nexport function restoreYogaRoom(saved) {\r\n  validateState(saved);\r\n\r\n  return freezeState({\r\n    ...clone(saved),\r\n    stageMs: [...saved.stageMs],\r\n    lastNow: null,\r\n    previousEligible: false,\r\n    previousPose: null,\n    previousPush: false,\n    openingMs: saved.status === 'opening' ? 0 : saved.openingMs,\n    openingReleased: saved.status === 'opening' ? false : saved.openingReleased,\n  });\r\n}\r\n\r\nexport function stepYogaRoom(saved, {\r\n  observationId,\r\n  now,\r\n  tracked,\r\n  paused = false,\r\n  push = false,\r\n  pose = null,\r\n  eligible = false,\r\n} = {}) {\r\n  validateState(saved);\r\n\r\n  if (!Number.isSafeInteger(observationId) || observationId < 0) {\r\n    throw new TypeError('observationId must be a nonnegative safe integer');\r\n  }\r\n  if (!Number.isFinite(now) || now < 0) throw new TypeError('now must be finite and nonnegative');\n  if ([tracked, paused, push, eligible].some(value => typeof value !== 'boolean')) {\r\n    throw new TypeError('tracked, paused, push, and eligible must be booleans');\r\n  }\r\n  if (pose !== null && !YOGA_POSES.includes(pose)) {\r\n    throw new TypeError('pose must be null, mountain, salute, or warrior');\r\n  }\r\n\r\n  if (observationId <= saved.lastObservationId) {\r\n    return { state: saved, events: [], duplicate: true };\r\n  }\r\n  if (saved.lastNow !== null && now < saved.lastNow) {\r\n    throw new TypeError('now must not move backward');\r\n  }\r\n\r\n  const next = clone(saved);\r\n  next.stageMs = [...saved.stageMs];\r\n  next.lastObservationId = observationId;\r\n  next.lastNow = now;\r\n\r\n  const events = [];\r\n\r\n  if (saved.status === 'complete') {\r\n    return { state: freezeState(next), events, duplicate: false };\r\n  }\r\n\r\n  const rawDt = saved.lastNow === null ? 0 : now - saved.lastNow;\r\n  const gap = rawDt > YOGA_ROOM_TUNING.continuityGapMs;\r\n  const dt = Math.min(rawDt, YOGA_ROOM_TUNING.maxFrameMs);\r\n\r\n  if (!tracked || paused) {\r\n    next.previousEligible = false;\n    next.previousPose = null;\r\n    next.previousPush = false;\r\n    if (next.status === 'opening') { next.openingMs = 0; next.openingReleased = false; }\n    return { state: freezeState(next), events, duplicate: false };\r\n  }\r\n\r\n  if (gap) {\r\n    next.previousEligible = false;\r\n    next.previousPose = null;\r\n    next.previousPush = false;\r\n    if (next.status === 'opening') { next.openingMs = 0; next.openingReleased = false; }\n  }\r\n\r\n  if (next.status === 'opening') {\r\n    if (!push) {\r\n      next.openingReleased = true;\r\n      next.openingMs = 0;\r\n      next.previousPush = false;\r\n      return { state: freezeState(next), events, duplicate: false };\r\n    }\r\n\r\n    if (next.openingReleased && next.previousPush && !gap) {\r\n      next.openingMs = Math.min(YOGA_ROOM_TUNING.openingPushMs, next.openingMs + dt);\r\n    }\r\n    next.previousPush = next.openingReleased;\n\r\n    if (next.openingMs >= YOGA_ROOM_TUNING.openingPushMs) {\r\n      next.openingMs = YOGA_ROOM_TUNING.openingPushMs;\r\n      next.status = 'holding';\n      next.stageIndex = 0;\n      next.previousPush = false;\n      events.push({\r\n        type: 'opening-complete',\r\n        encounterId: next.encounterId,\r\n      });\r\n    }\r\n\r\n    return { state: freezeState(next), events, duplicate: false };\r\n  }\r\n\r\n  const requiredPose = poseForStage(next.stageIndex);\r\n  const qualifies = eligible && pose === requiredPose;\r\n  const previousWasQualifying = next.previousEligible && next.previousPose === requiredPose;\r\n\r\n  if (qualifies && previousWasQualifying && !gap) {\r\n    next.stageMs[next.stageIndex] = Math.min(\r\n      YOGA_ROOM_TUNING.stageQualifiedMs,\r\n      next.stageMs[next.stageIndex] + dt,\r\n    );\r\n  }\r\n\r\n  next.previousEligible = qualifies;\r\n  next.previousPose = qualifies ? pose : null;\n\r\n  if (next.stageMs[next.stageIndex] >= YOGA_ROOM_TUNING.stageQualifiedMs) {\r\n    events.push({\r\n      type: 'stage-complete',\r\n      encounterId: next.encounterId,\r\n      stageIndex: next.stageIndex,\r\n      pose: requiredPose,\r\n    });\r\n\r\n    next.stageIndex += 1;\n    next.previousEligible = false;\n    next.previousPose = null;\n    if (next.stageIndex === YOGA_POSES.length) {\r\n      next.status = 'complete';\r\n      events.push({\r\n        type: 'room-complete',\r\n        encounterId: next.encounterId,\r\n        qualifiedMs: YOGA_ROOM_TUNING.stageQualifiedMs * YOGA_POSES.length,\r\n      });\r\n    }\r\n  }\r\n\r\n  return { state: freezeState(next), events, duplicate: false };\r\n}\r\n","modules/roguelite/yoga-view.mjs":"// modules/roguelite/yoga-view.mjs\r\nimport { FOUR_LEGGED_SOURCES } from '../coach-world/unit-catalog.mjs';\r\nimport { YOGA_POSES, YOGA_ROOM_TUNING } from './yoga-room.mjs';\nimport { EXERCISES } from '../../exercise-library.mjs';\n\r\nconst POSE_CUES = ['Stand tall, arms relaxed at your sides.', 'Reach both arms overhead.',\n  'Take a comfortable wide stance, bend one knee and extend both arms to the sides.'];\nconst clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, Number.isFinite(value) ? value : min));\nconst positions = [[-1.05, 0], [0, 1.35], [1.05, 0]];\nconst abortError = () => Object.assign(new Error('Yoga view disposed before readiness.'), { name: 'AbortError' });\n\r\nexport function mountYogaView(host, { T, roomGroup, reducedMotion = false, onError = () => {} } = {}) {\r\n  if (!host || !T || !roomGroup) throw new TypeError('mountYogaView requires host, T, and roomGroup.');\r\n  if (!roomGroup.add || !T.Group) throw new TypeError('roomGroup must be a Three.Group.');\n\n  const document = host.ownerDocument ?? globalThis.document;\n  const root = document.createElement('section');\n  root.className = 'roguelite-yoga-view';\r\n  root.setAttribute('aria-label', 'Yoga clearing');\r\n  const style = document.createElement('style');\r\n  style.textContent = `\r\n    .roguelite-yoga-view{box-sizing:border-box;max-width:100%;width:min(100%,30rem);padding:.7rem;color:#effff8;background:#0d2524ed;border:2px solid #7fbd91;border-radius:2px;box-shadow:4px 4px 0 #071511;font:600 clamp(14px,3.5vw,17px)/1.3 Pixel,monospace;pointer-events:none;overflow-wrap:anywhere}\n    .roguelite-yoga-view *{box-sizing:border-box}\r\n    .roguelite-yoga-view h2{margin:0 0 .35rem;font-size:1.3rem;color:#c8ff95;letter-spacing:.04em}\n    .roguelite-yoga-view p{margin:.25rem 0}\r\n    .roguelite-yoga-view .yoga-timer{font-variant-numeric:tabular-nums}\r\n    .roguelite-yoga-view .yoga-cue{min-height:2.9em}\r\n    .roguelite-yoga-view .yoga-stages{display:grid;gap:.4rem;margin-top:.5rem}\r\n    .roguelite-yoga-view label{display:grid;grid-template-columns:7rem minmax(0,1fr);gap:.5rem;align-items:center}\n    .roguelite-yoga-view progress{width:100%;min-width:0;height:.75rem;accent-color:#b7f697}\n    .roguelite-yoga-view .yoga-animals{color:#b9f8d9}\r\n    .roguelite-yoga-view .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\r\n    .roguelite-yoga-view .yoga-limits{font-size:.75rem;color:#c6dbd5}\n    @media (max-width:30rem){.roguelite-yoga-view{width:100%;padding:.5rem}.roguelite-yoga-view label{grid-template-columns:6rem minmax(0,1fr)}}\n  `;\r\n  const heading = document.createElement('h2');\r\n  heading.textContent = 'Yoga clearing';\r\n  const cue = document.createElement('p');\r\n  cue.className = 'yoga-cue';\r\n  const timer = document.createElement('p');\r\n  timer.className = 'yoga-timer';\r\n  const animals = document.createElement('p');\r\n  animals.className = 'yoga-animals';\r\n  const stages = document.createElement('div');\r\n  stages.className = 'yoga-stages';\r\n  const progress = Array.from({ length: 3 }, (_, index) => {\r\n    const label = document.createElement('label');\r\n    const text = document.createElement('span');\r\n    text.textContent = EXERCISES[YOGA_POSES[index]].name;\n    const bar = document.createElement('progress');\r\n    bar.max = 1;\r\n    bar.value = 0;\n    bar.setAttribute('aria-label', `${text.textContent} qualified hold`);\n    label.append(text, bar);\r\n    stages.append(label);\r\n    return bar;\r\n  });\r\n  const live = document.createElement('p');\r\n  live.className = 'sr-only';\r\n  live.setAttribute('role', 'status');\n  live.setAttribute('aria-live', 'polite');\n  const limits = document.createElement('p');\n  limits.className = 'yoga-limits';\n  limits.textContent = EXERCISES.mountain.limits;\n  root.append(style, heading, cue, timer, animals, stages, limits, live);\n  host.append(root);\r\n\r\n  const group = new T.Group();\r\n  group.name = 'roguelite-yoga-content';\r\n  roomGroup.add(group);\r\n\r\n  const sources = FOUR_LEGGED_SOURCES.slice(0, 3);\r\n  if (sources.length !== 3) {\r\n    const error = new Error('Yoga clearing requires three four-legged atlas sources.');\r\n    onError(error);\r\n    group.removeFromParent();\r\n    root.remove();\r\n    throw error;\r\n  }\r\n  const atlasPath = sources[0].atlasPath;\r\n  const ownedTextures = new Set();\r\n  const ownedMaterials = new Set();\r\n  const ownedGeometries = new Set();\r\n  const keepGeo = geometry => (ownedGeometries.add(geometry), geometry);\r\n  const keepMat = material => (ownedMaterials.add(material), material);\r\n  const ringMeshes = [];\r\n  const animalSprites = [];\r\n  const animalsGroup = new T.Group();\r\n  const fireGroup = new T.Group();\r\n  group.add(animalsGroup, fireGroup);\r\n\r\n  const baseRing = keepGeo(new T.RingGeometry(.48, .57, 64));\n  const baseRingMat = keepMat(new T.MeshBasicMaterial({ color: 0x39695e, side: T.DoubleSide, depthWrite: false }));\n  for (let index = 0; index < 3; index++) {\n    const material = keepMat(new T.MeshBasicMaterial({\r\n      color: 0x7de7bd, side: T.DoubleSide, transparent: true, opacity: 0.85, depthWrite: false\r\n    }));\r\n    const mesh = new T.Mesh(keepGeo(baseRing.clone()), material);\n    mesh.rotation.x = -Math.PI / 2;\n    mesh.position.set(positions[index][0], -.085, positions[index][1]);\n    mesh.name = `yoga-progress:${YOGA_POSES[index]}`;\n    const outline = new T.Mesh(baseRing, baseRingMat);\n    outline.rotation.x = mesh.rotation.x;\n    outline.position.copy(mesh.position); outline.position.y = -.09;\n    group.add(outline);\n    mesh.renderOrder = 2;\r\n    group.add(mesh);\r\n    ringMeshes.push(mesh);\r\n  }\r\n\r\n  const logGeometry = keepGeo(new T.BoxGeometry(0.18, 0.18, 0.9));\r\n  const woodMaterial = keepMat(new T.MeshBasicMaterial({ color: 0x8b5634 }));\r\n  const logs = [];\r\n  for (let index = 0; index < 4; index++) {\r\n    const log = new T.Mesh(logGeometry, woodMaterial);\r\n    log.position.set((index - 1.5) * .35, .1, 2.4);\n    log.rotation.y = index * 0.7;\r\n    fireGroup.add(log);\r\n    logs.push(log);\r\n  }\r\n  const flameGeometry = keepGeo(new T.ConeGeometry(0.28, 0.85, 5));\r\n  const flameMaterial = keepMat(new T.MeshBasicMaterial({ color: 0xffb43f }));\r\n  const flame = new T.Mesh(flameGeometry, flameMaterial);\r\n  flame.position.set(0, 0.55, 2.4);\n  fireGroup.add(flame);\r\n\r\n  let disposed = false;\r\n  let lastAnnouncement = '';\r\n  let clock = 0;\r\n\r\n  const configure = (texture, source) => {\r\n    const [x, y, width, height] = source.frame;\r\n    const image = texture.image;\r\n    const iw = image?.width;\r\n    const ih = image?.height;\r\n    if (!(iw > 0 && ih > 0) || x < 0 || y < 0 || width <= 0 || height <= 0 ||\r\n        x + width > iw || y + height > ih) {\r\n      throw new Error(`Yoga atlas frame is outside ${atlasPath}`);\r\n    }\r\n    texture.colorSpace = T.SRGBColorSpace;\r\n    texture.magFilter = T.NearestFilter;\r\n    texture.minFilter = T.NearestFilter;\r\n    texture.generateMipmaps = false;\r\n    texture.wrapS = T.ClampToEdgeWrapping;\r\n    texture.wrapT = T.ClampToEdgeWrapping;\r\n    texture.repeat.set(width / iw, height / ih);\r\n    texture.offset.set(x / iw, 1 - (y + height) / ih);\r\n    texture.needsUpdate = true;\r\n    return texture;\r\n  };\r\n\r\n  const readyPromise = (async () => {\r\n    try {\r\n      const texture = await new T.TextureLoader().loadAsync(atlasPath);\r\n      if (disposed) {\n        texture.dispose?.();\n        throw abortError();\n      }\n      ownedTextures.add(texture);\n      const baseTexture = configure(texture, sources[0]);\n      for (let index = 0; index < sources.length; index++) {\r\n        const source = sources[index];\r\n        const clone = baseTexture.clone();\r\n        ownedTextures.add(clone);\r\n        configure(clone, source);\r\n        const material = keepMat(new T.SpriteMaterial({\r\n          map: clone, color: 0xffffff, transparent: true, alphaTest: 0.04,\r\n          toneMapped: false, depthWrite: false\r\n        }));\r\n        const sprite = new T.Sprite(material);\r\n        const frameWidth = source.frame[2] / source.frame[3];\r\n        const height = .9 + index * 0.04;\n        sprite.scale.set(height * frameWidth, height, 1);\r\n        sprite.center.set(0.5, 0);\r\n        sprite.position.set(positions[index][0], 0, positions[index][1] - 3.2);\n        sprite.name = `yoga-animal:${source.sourceCoachId}`;\n        sprite.renderOrder = 3;\r\n        animalsGroup.add(sprite);\r\n        animalSprites.push(sprite);\r\n      }\r\n    } catch (error) {\r\n      if (!disposed) onError(error);\r\n      throw error;\r\n    }\r\n  })();\n  readyPromise.catch(() => {}); // Preserve rejection for the host without an unhandled cancellation.\n\r\n  const announce = (status, stageIndex, settled, paused, tracked) => {\n    const key = `${status}:${stageIndex}:${settled}:${paused}:${tracked}`;\n    if (key === lastAnnouncement) return;\r\n    lastAnnouncement = key;\r\n    live.textContent = paused ? 'Yoga paused.' : !tracked ? 'Tracking lost. Exercise time is paused.' :\n      status === 'opening' ? 'Bring hands back, then push both open palms forward.' :\n      status === 'complete' ? 'Three animals settled. Yoga holds complete.' :\n      `${EXERCISES[YOGA_POSES[stageIndex]].name}. ${settled} of three animals settled.`;\n  };\r\n\r\n  const render = (state = {}, { paused = false, tracked = true, eligible = false, dt = 0 } = {}) => {\r\n    if (disposed) return;\r\n    const status = ['opening', 'holding', 'complete'].includes(state.status) ? state.status : 'opening';\r\n    const stageIndex = clamp(Math.trunc(state.stageIndex ?? 0), 0, 2);\n    const stageDurations = Array.from({ length: 3 }, (_, index) =>\r\n      clamp(state.stageMs?.[index] ?? 0, 0, YOGA_ROOM_TUNING.stageQualifiedMs));\n    const currentStageMs = stageDurations[stageIndex];\n    const elapsed = stageDurations.reduce((sum, value) => sum + value, 0);\n    const openingProgress = clamp((state.openingMs ?? 0) / YOGA_ROOM_TUNING.openingPushMs);\n\r\n    if (!paused) clock += clamp(Number(dt) || 0, 0, 0.1);\r\n    const bob = reducedMotion ? 0 : Math.sin(clock * 2.2) * 0.035;\n    const exercise = EXERCISES[YOGA_POSES[stageIndex]];\n    heading.textContent = status === 'holding' ? `${stageIndex + 1}/3 · ${exercise.name}` :\n      status === 'complete' ? 'Yoga holds complete' : 'Open the woodland clearing';\n    const override = paused\r\n      ? 'Paused. Resume when ready.'\r\n      : !tracked\r\n        ? 'Tracking lost. Face the camera and keep your full body visible.'\r\n        : '';\n    cue.textContent = override || (status === 'opening' ? (state.openingReleased ?\n      'Extend both open palms forward and hold for 0.4 seconds to part the wood.' :\n      'Bring both hands back toward your chest first, then push both open palms forward.') :\n      status === 'complete' ? 'All three standing holds earned. The woodland animals have settled.' :\n      `${POSE_CUES[stageIndex]} ${exercise.hint} ${eligible ? 'Pose matched. Keep holding.' : 'Match this pose to continue.'}`);\n    timer.textContent = status === 'opening' ? `Opening ${(openingProgress * .4).toFixed(1)} / 0.4 s` :\n      `${(currentStageMs / 1000).toFixed(1)} / 10 s · ${(elapsed / 1000).toFixed(1)} / 30 s earned`;\n\r\n    for (let index = 0; index < 3; index++) {\r\n      const fraction = stageDurations[index] / YOGA_ROOM_TUNING.stageQualifiedMs;\n      progress[index].value = fraction;\r\n      const mesh = ringMeshes[index];\r\n      mesh.geometry.setDrawRange(0, Math.floor(64 * fraction) * 6);\n      mesh.material.color.set(fraction >= 1 ? 0x66ff73 : 0x7de7bd);\r\n      if (animalSprites[index]) {\r\n        const sprite = animalSprites[index];\r\n        sprite.position.z = positions[index][1] - 3.2 * (1 - fraction);\n        sprite.position.y = index === stageIndex && status === 'holding' && fraction < 1 ? bob : 0;\n      }\r\n    }\r\n\r\n    const settled = stageDurations.filter(ms => ms === YOGA_ROOM_TUNING.stageQualifiedMs).length;\n    animals.textContent = `${settled} / 3 animals settled${status === 'holding' && eligible && tracked && !paused ? ' · Your hold brings an animal closer.' : '.'}`;\n    fireGroup.visible = status === 'opening';\n    logs.forEach((log, index) => {\r\n      const part = clamp(openingProgress * 4 - index);\r\n      log.position.x = (index - 1.5) * (.35 + part * .7);\n    });\r\n    flame.visible = openingProgress > 0;\n    flame.scale.setScalar(clamp(openingProgress, 0.15, 1));\r\n\r\n    root.dataset.status = status;\n    announce(status, stageIndex, settled, paused, tracked);\n  };\r\n\r\n  const dispose = () => {\r\n    if (disposed) return;\r\n    disposed = true;\r\n    group.removeFromParent();\r\n    root.remove();\r\n    for (const texture of ownedTextures) texture.dispose?.();\r\n    for (const material of ownedMaterials) material.dispose?.();\r\n    for (const geometry of ownedGeometries) geometry.dispose?.();\r\n    ownedTextures.clear();\r\n    ownedMaterials.clear();\r\n    ownedGeometries.clear();\r\n    group.clear();\r\n  };\r\n\r\n  render();\n  return {\n    element: root,\r\n    group,\r\n    readyPromise,\r\n    ready: readyPromise,\r\n    render,\r\n    update: render,\r\n    dispose\r\n  };\r\n}\r\n","modules/rooms/classroom.css":".classroom-panel[data-room=loading],.classroom-panel[data-room=ready]{height:min(100dvh - 28px,900px);overflow:hidden}\r\n.classroom-panel[data-room]:not([data-room-view=board])[open]{display:block;padding:0;position:relative}\r\n.classroom-panel[data-room]:not([data-room-view=board])>:not(header,.classroom-host,#coachDock,#myr5Shell,.portal-peer-ui){display:none!important}\r\n.classroom-panel[data-room]:not([data-room-view=board])>header{position:absolute;z-index:3;inset:0 0 auto;margin:0;padding:12px 14px 10px;background:transparent;border:0;box-shadow:none;pointer-events:none}\r\n.classroom-panel[data-room]:not([data-room-view=board])>header>*{pointer-events:auto}\r\n.classroom-host{position:relative}\r\n.classroom-panel[data-room]:not([data-room-view=board]) .classroom-host{position:absolute;inset:0;min-height:0}\r\n.classroom-stage{position:absolute;inset:0;overflow:hidden;background:#d9c9b0}\r\n.classroom-canvas,.classroom-fallback,.classroom-desks,.classroom-ui{position:absolute;inset:0;width:100%;height:100%}\r\n.classroom-fallback{background:linear-gradient(180deg,#b6a182 0 64%,#a79681 64%);pointer-events:none}\r\n.classroom-fallback[hidden]{display:none}\r\n.classroom-desks,.classroom-ui{pointer-events:none}\r\n.classroom-desks{z-index:2}.classroom-ui{z-index:1}\r\n.classroom-desks>*,.classroom-ui>*{pointer-events:auto}\r\n#accountPanel.classroom-panel .classroom-board{position:absolute;left:29%;top:12%;width:42%;height:35%;display:grid;place-items:end center;padding:0 0 8px;background:transparent;border:3px dashed transparent;border-radius:6px;box-shadow:none;cursor:pointer;transform:none}\r\n/* R3-04: a hologram (pod/holo.css), not a paper label. Marker face kept. */\r\n.classroom-panel .classroom-board .holo{font:22px/1.2 BoardHand0,cursive;letter-spacing:0;text-transform:none;margin-bottom:6px}\r\n.classroom-panel .classroom-board:hover,.classroom-panel .classroom-board:focus-visible{border-color:#174b92;outline:2px solid #174b92}\r\n.classroom-desk{position:absolute;top:63%;transform:translate(-50%,-100%);display:grid;justify-items:center}\r\n.classroom-desk:first-child{left:27%}.classroom-desk:nth-child(2){left:73%}\r\n#accountPanel .classroom-desk .crew-head{width:58px;height:58px;background:transparent;border:0;box-shadow:none}\r\n.classroom-desk .crew-head canvas{width:54px;height:54px}\r\n.classroom-desk figure{margin:0;display:grid;justify-items:center;gap:3px}\r\n.classroom-desk figure{animation:classroom-pop .55s cubic-bezier(.2,.8,.2,1) both}\r\n@keyframes classroom-pop{from{opacity:0;transform:translateY(16px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}\r\n@media(prefers-reduced-motion:reduce){.classroom-desk figure{animation:none}}\r\n.classroom-desk figcaption{font:700 13px/1.2 Arial,sans-serif;color:#2e3e56;background:#fffdf5e6;padding:2px 8px;border-radius:9px;max-width:9em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\r\n#accountPanel .classroom-desk button{display:grid;place-items:center;width:54px;height:54px;min-width:0;min-height:0;padding:0;border-radius:50%;font:17px/1 BoardHand0,cursive}\r\n#accountPanel .classroom-desk button b{font:700 29px/1 Arial,sans-serif}\r\n#accountPanel .classroom-desk button span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}\r\n.classroom-note{position:absolute;bottom:10px;left:10px;right:10px;text-align:center;background:#fffdf5dd;padding:5px;border-radius:6px;font:15px Arial,sans-serif}\r\n.classroom-panel[data-room-view=board]{height:auto;max-height:calc(100dvh - 28px);overflow-y:auto;background:linear-gradient(110deg,#fbf8ef,#f1ede2);border-color:#a7a49c}\r\n.classroom-panel[data-room-view=board] .classroom-host{display:none}\r\n/* R3-11: the board runs the full well width and its marker tray is a full-width ledge pinned to the bottom, never clipped. */\r\n#accountPanel.classroom-panel[data-room-view=board]{display:flex;flex-direction:column;overflow-x:hidden}\r\n#accountPanel.classroom-panel[data-room-view=board] .marker-tray{flex:none;width:calc(100% + 2*clamp(13px,4vw,38px))!important;max-width:none!important;box-sizing:border-box;margin:auto calc(-1*clamp(13px,4vw,38px)) -22px;bottom:-22px;min-width:0;border-radius:0}\r\n","modules/rooms/classroom.mjs":"import {buildPaperCrawler} from './paper-crawler.mjs';\r\nimport {claimGraphics} from '../graphics-lifecycle.mjs';\r\n// Optional scoreboard classroom. Mounted only while the scoreboard dialog is open.\r\nconst CLASS={fov:40,eye:[0,.5,1.6],look:[0,.5,0],desks:{at:[0,.15,.14],scale:.75},board:{min:[-.29,.52,-.42],max:[.29,.83,-.42]},heads:[[-.19,.44,.28],[.19,.44,.28]]};\r\nlet stylesheet;\r\nfunction loadStyle(){return stylesheet??=new Promise(resolve=>{const link=document.createElement('link');link.rel='stylesheet';link.href='/modules/rooms/classroom.css';link.onload=link.onerror=resolve;document.head.append(link);});}\r\nexport function mountClassroom(panel,{host=panel.querySelector('[data-room-host]')}={}){\r\n if(!host){host=document.createElement('div');host.dataset.roomHost='';panel.querySelector('header')?.after(host);}\r\n host.classList.add('classroom-host');host.innerHTML='<div class=\"classroom-stage room-stage\"><div class=\"classroom-fallback\" data-peer-depth=\"far\" aria-hidden=\"true\"></div><div class=\"classroom-desks\" data-peer-depth=\"mid\"></div><div class=\"classroom-ui\" data-peer-depth=\"near\"><button type=\"button\" class=\"classroom-board\" data-room-board aria-label=\"Open the scoreboard whiteboard\"><span class=\"holo\" data-lead style=\"--lead-angle:-90deg;--lead-len:34px;--lead-gap:14px\">Tap the board</span></button></div><p class=\"classroom-note\" role=\"status\">Entering classroom…</p></div>';\r\n panel.dataset.room='loading';delete panel.dataset.roomView;\r\n const stage=host.querySelector('.classroom-stage'),desksLayer=host.querySelector('.classroom-desks'),boardButton=host.querySelector('.classroom-board'),note=host.querySelector('.classroom-note');\r\n let disposed=false,releaseGpu=null,renderer=null,scene=null,camera=null,resizeObserver=null,raf=0,slots=[],three=null;\r\n let scary=false,crawler=null,hemisphere=null,sunlight=null,origin=performance.now(),stopAnimation=()=>{};\r\n const reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n function showBoard(){if(disposed||panel.dataset.roomView==='board')return;cancelAnimationFrame(raf);raf=0;panel.dispatchEvent(new CustomEvent('myr5:classroom-board',{bubbles:true}));panel.dataset.roomView='board';panel.scrollTop=0;(panel.querySelector('#accountContent:not([hidden]) h3, #signIn')||panel).focus?.({preventScroll:true});}\r\n function setDesks(nodes){if(disposed)return;desksLayer.replaceChildren();slots=[];for(const node of nodes.slice(0,2)){const slot=document.createElement('div');slot.className='classroom-desk';slot.append(node);desksLayer.append(slot);slots.push(slot);}place();}\r\n function place(){if(!camera||!stage.isConnected)return;const rect=stage.getBoundingClientRect();if(!rect.width||!rect.height)return;const project=(x,y,z)=>{const p=new three.Vector3(x,y,z).project(camera);return [(p.x+1)*rect.width/2,(1-p.y)*rect.height/2];};const [x0,y0]=project(CLASS.board.min[0],CLASS.board.max[1],CLASS.board.min[2]),[x1,y1]=project(CLASS.board.max[0],CLASS.board.min[1],CLASS.board.max[2]);Object.assign(boardButton.style,{left:x0+'px',top:y0+'px',width:Math.max(44,x1-x0)+'px',height:Math.max(44,y1-y0)+'px'});slots.forEach((slot,i)=>{const [x,y]=project(...CLASS.heads[i]);slot.style.left=x+'px';slot.style.top=y+'px';});}\r\n function dispose(){if(disposed)return;disposed=true;stopAnimation();cancelAnimationFrame(raf);resizeObserver?.disconnect();if(scene){scene.traverse(node=>{node.geometry?.dispose();for(const material of [].concat(node.material||[])){if(!material)continue;for(const value of Object.values(material))if(value?.isTexture)value.dispose();material.dispose();}});}renderer?.dispose();renderer?.forceContextLoss();releaseGpu?.(renderer?.domElement);releaseGpu=null;host.replaceChildren();delete panel.dataset.room;delete panel.dataset.roomView;panel.dispatchEvent(new CustomEvent('room-gone',{bubbles:true}));}\r\n boardButton.onclick=showBoard;\r\n const controller={setDesks,showBoard,dispose};\r\n const ready=(async()=>{\r\n  await loadStyle();if(disposed)return;\r\n  try{\r\n   const [THREE,{GLTFLoader}]=await Promise.all([import('three'),import('three/addons/loaders/GLTFLoader.js')]);\r\n   const loader=new GLTFLoader();const [wall,desks]=await Promise.all(['/pod/rooms/classroom-wall.glb','/pod/rooms/classroom-desks.glb'].map(url=>loader.loadAsync(url).then(g=>g.scene)));\r\n   if(disposed){for(const root of [wall,desks])root.traverse(n=>{n.geometry?.dispose();for(const m of [].concat(n.material||[]))m?.dispose();});return;}\r\n   releaseGpu=claimGraphics('classroom',{keep:['portal']});await releaseGpu.ready; // the hidden 3D coach yields; back on close\r\n   if(disposed){releaseGpu();releaseGpu=null;for(const root of [wall,desks])root.traverse(n=>{n.geometry?.dispose();for(const m of [].concat(n.material||[]))m?.dispose();});return;}\r\n   const canvas=document.createElement('canvas');canvas.className='classroom-canvas';canvas.dataset.peerDepth='far';canvas.setAttribute('aria-hidden','true');stage.prepend(canvas);\r\n   renderer=new THREE.WebGLRenderer({canvas,antialias:true,powerPreference:'low-power'});renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));\r\n   scene=new THREE.Scene();scene.background=new THREE.Color('#d9c9b0');hemisphere=new THREE.HemisphereLight(0xfff4e6,0x8a7a66,2.2);scene.add(hemisphere);const sun=new THREE.DirectionalLight(0xffe2bd,2.4);sunlight=sun;sun.position.set(-1,2,2.5);scene.add(sun);desks.position.set(...CLASS.desks.at);desks.scale.setScalar(CLASS.desks.scale);scene.add(wall,desks);\r\n   const floor=new THREE.Mesh(new THREE.PlaneGeometry(3,3),new THREE.MeshStandardMaterial({color:'#c3b6a3',roughness:1}));floor.rotation.x=-Math.PI/2;floor.position.z=.9;scene.add(floor);\r\n   const paperMat=new THREE.MeshStandardMaterial({color:0xc9b68f,roughness:1}),suitMat=new THREE.MeshStandardMaterial({color:0x544438,roughness:1}),caseMat=new THREE.MeshStandardMaterial({color:0x24231e,roughness:1});\r\n   crawler=buildPaperCrawler(THREE,{paperMat,suitMat,caseMat});crawler.root.scale.setScalar(.19);crawler.root.rotation.y=-Math.PI/2;crawler.root.visible=false;scene.add(crawler.root);\r\n   // Clip the lower three quarters at the desk edge. All parts use one renderer.\r\n   renderer.localClippingEnabled=true;const deskEdge=new THREE.Plane(new THREE.Vector3(0,1,0),-.285);\r\n   crawler.root.traverse(node=>{if(node.material){node.material.clippingPlanes=[deskEdge];node.material.needsUpdate=true;}});\r\n   camera=new THREE.PerspectiveCamera(CLASS.fov,1,.05,20);camera.position.set(...CLASS.eye);camera.lookAt(...CLASS.look);three=THREE;\r\n   function resize(){if(disposed)return;const r=stage.getBoundingClientRect();if(!r.width||!r.height)return;renderer.setSize(r.width,r.height,false);camera.aspect=r.width/r.height;camera.updateProjectionMatrix();place();renderer.render(scene,camera);}\r\n   function animate(now){raf=0;if(disposed||document.hidden||!panel.open||panel.dataset.roomView==='board')return;const elapsed=now-origin,phase=elapsed%15000,on=!reduced.matches&&elapsed>=15000&&phase<4200;\r\n    const flicker=on?(phase<450?(.12+.11*Math.abs(Math.sin(phase*.065))):phase<3600?.13:Math.min(1,.13+(phase-3600)/600)):1;\r\n    hemisphere.intensity=2.2*flicker;sunlight.intensity=2.4*flicker;stage.classList.toggle('classroom-dim',on);\r\n    if(on!==scary){scary=on;window.dispatchEvent(new CustomEvent(on?'myr5:music-cut':'myr5:music-resume'));}\r\n    crawler.root.visible=on;if(on){crawler.root.position.set(-.8+phase/4200*1.6,.18,.48);crawler.pose(phase/1000);}\r\n    renderer.render(scene,camera);if(!reduced.matches)raf=requestAnimationFrame(animate);\r\n   }\r\n   const visible=()=>{cancelAnimationFrame(raf);raf=0;if(scary&&(document.hidden||!panel.open||panel.dataset.roomView==='board')){scary=false;window.dispatchEvent(new CustomEvent('myr5:music-resume'));}if(!disposed&&!document.hidden&&panel.open&&panel.dataset.roomView!=='board')raf=requestAnimationFrame(animate);};\r\n   const roomView=new MutationObserver(visible);roomView.observe(panel,{attributes:true,attributeFilter:['data-room-view','open']});\r\n   stopAnimation=()=>{if(scary){scary=false;window.dispatchEvent(new CustomEvent('myr5:music-resume'));}roomView.disconnect();document.removeEventListener('visibilitychange',visible);cancelAnimationFrame(raf);raf=0;};\r\n   document.addEventListener('visibilitychange',visible);panel.addEventListener('close',stopAnimation,{once:true});visible();\r\n   resizeObserver=new ResizeObserver(resize);resizeObserver.observe(stage);resize();panel.dataset.room='ready';note.hidden=true;stage.querySelector('.classroom-fallback').hidden=true;\r\n   panel.dispatchEvent(new CustomEvent('room-ready',{bubbles:true,detail:controller}));\r\n  }catch(error){if(disposed)return;panel.dataset.room='ready';note.textContent='Classroom art is unavailable on this device.';const download=document.createElement('button');download.type='button';download.textContent='Download classroom';download.onclick=()=>window.myr5Packs?.open?.('room-scoreboard');note.append(' ',download);panel.dispatchEvent(new CustomEvent('room-ready',{bubbles:true,detail:controller}));console.warn('Classroom art unavailable',error);}\r\n })();\r\n return {...controller,ready};\r\n}\r\n","modules/rooms/paper-crawler.mjs":"export function buildPaperCrawler(THREE,{paperMat,suitMat,caseMat}){\r\n const g=new THREE.Group();\r\n const eyeMat=new THREE.MeshStandardMaterial({color:0x0a0806,roughness:1});\r\n // helpers\r\n const grp=(x,y,z,p)=>{const o=new THREE.Group();o.position.set(x,y,z);(p||g).add(o);return o;}\r\n const part=(geo,mat,x,y,z,p)=>{const m=new THREE.Mesh(geo,mat);m.position.set(x,y,z);(p||g).add(m);return m;}\r\n // body constants\r\n const THIGH=0.6, SHIN=0.45, SPINE=0.68, UARM=0.34, FARM=0.32;\r\n const HIP_Y=0.7, HIP_Z=-0.45; // crawling pose\r\n // pelvis & torso\r\n part(new THREE.BoxGeometry(0.34,0.2,0.22),suitMat,0,HIP_Y,HIP_Z);\r\n const torso=grp(0,HIP_Y,HIP_Z);torso.rotation.x=Math.PI/2+0.25;\r\n // chest\r\n part(new THREE.CylinderGeometry(0.21,0.15,SPINE,10),suitMat,0,SPINE/2,0,torso).scale.z=0.62;\r\n for(const s of[-1,1])part(new THREE.SphereGeometry(0.08,10,8),suitMat,s*0.26,SPINE-0.05,0,torso);\r\n // neck\r\n const neck=grp(0,SPINE,0,torso); part(new THREE.CylinderGeometry(0.06,0.07,0.16,8),paperMat,0,0.06,0,neck);\r\n // head\r\n const head=grp(0,0,0);head.rotation.order='YXZ';\r\n part(new THREE.SphereGeometry(0.17,16,12),paperMat,0,0,0,head).scale.set(1,1.12,0.95);\r\n part(new THREE.SphereGeometry(0.1,12,8),paperMat,0,-0.14,0.05,head).scale.set(1,0.55,0.9);\r\n for(const s of[-1,1])part(new THREE.SphereGeometry(0.036,8,6),eyeMat,s*0.065,0.035,0.135,head);\r\n const nose=part(new THREE.ConeGeometry(0.03,0.08,6),paperMat,0,-0.035,0.19,head);nose.rotation.x=Math.PI/2;\r\n part(new THREE.SphereGeometry(0.175,12,8,0,Math.PI*2,0,Math.PI*0.4),caseMat,0,0.012,-0.012,head).scale.set(1,1.12,0.97);\r\n // limb builder\r\n const limb=(px,py,pz,upLen,loLen,upR,loR)=>{const up=grp(px,py,pz);part(new THREE.CylinderGeometry(upR,upR*0.82,upLen,8),suitMat,0,-upLen/2,0,up);\r\n  part(new THREE.SphereGeometry(upR*1.05,8,6),suitMat,0,-upLen,0,up);const lo=grp(0,-upLen,0,up);part(new THREE.CylinderGeometry(loR,loR*0.85,loLen,8),suitMat,0,-loLen/2,0,lo);\r\n  return{upper:up,lower:lo};}\r\n const armL=limb(0,0,0,UARM,FARM,0.05,0.04),armR=limb(0,0,0,UARM,FARM,0.05,0.04);\r\n const legL=limb(-0.1,HIP_Y,HIP_Z,THIGH,SHIN,0.075,0.06),legR=limb(0.1,HIP_Y,HIP_Z,THIGH,SHIN,0.075,0.06);\r\n // feet\r\n for(const leg of[legL,legR]){const foot=grp(0,-SHIN,0,leg.lower);foot.rotation.x=Math.PI-( -0.15+Math.PI/2-0.1 );part(new THREE.BoxGeometry(0.11,0.06,0.27),caseMat,0,-0.02,0.08,foot);}\r\n // hands\r\n const handL=grp(0,-FARM,0,armL.lower);const handR=grp(0,-FARM,0,armR.lower);\r\n part(new THREE.BoxGeometry(0.09,0.11,0.035),paperMat,0,-0.055,0,handL);part(new THREE.BoxGeometry(0.09,0.11,0.035),paperMat,0,-0.055,0,handR);\r\n for(const h of[handL,handR])for(const fx of[-0.028,0,0.028])part(new THREE.BoxGeometry(0.018,0.075,0.022),paperMat,fx,-0.145,0,h);\r\n // rest rotations\r\n armL.upper.rotation.set(-0.45,0,0);armR.upper.rotation.set(-0.45,0,0);\r\n armL.lower.rotation.x=armR.lower.rotation.x=-0.3;\r\n legL.upper.rotation.x=legR.upper.rotation.x=-0.15;\r\n legL.lower.rotation.x=legR.lower.rotation.x=Math.PI/2-0.1;\r\n head.rotation.x=0.3;\r\n // layout\r\n const v=new THREE.Vector3();\r\n function layout(dip){torso.rotation.x=Math.PI/2+0.25;const lift=0.1-0.17*dip;\r\n  v.set(0,SPINE+0.26,0).applyEuler(torso.rotation);head.position.set(v.x,HIP_Y+v.y+lift,HIP_Z+v.z);\r\n  v.set(-0.27,SPINE-0.06,0).applyEuler(torso.rotation);armL.upper.position.set(v.x,HIP_Y+v.y,HIP_Z+v.z);\r\n  v.set(0.27,SPINE-0.06,0).applyEuler(torso.rotation);armR.upper.position.set(v.x,HIP_Y+v.y,HIP_Z+v.z);}\r\n layout(0);\r\n // pose function\r\n const rest={headP:0.3,armU:-0.45,armL:-0.3,legU:-0.15,legL:Math.PI/2-0.1};\r\n function pose(seconds){const t=seconds*4*Math.PI; // 2Hz\r\n  armL.upper.rotation.x=rest.armU+0.4*Math.sin(t);armR.upper.rotation.x=rest.armU+0.4*Math.cos(t);\r\n  armL.lower.rotation.x=rest.armL+0.3*Math.sin(t);armR.lower.rotation.x=rest.armL+0.3*Math.cos(t);\r\n  legL.upper.rotation.x=rest.legU+0.5*Math.sin(t+Math.PI/2);legR.upper.rotation.x=rest.legU+0.5*Math.cos(t+Math.PI/2);\r\n  legL.lower.rotation.x=rest.legL+0.4*Math.sin(t+Math.PI/2);legR.lower.rotation.x=rest.legL+0.4*Math.cos(t+Math.PI/2);\r\n  const dip=Math.abs(Math.sin(t*3));layout(dip);}\r\n g.visible=true;\r\n return{root:g,pose};\r\n}","modules/rooms/reminders-computer.css":"/* reminders-computer:glb-v2 */\r\n.reminders-computer-offer{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 12px}\r\n.reminders-computer-offer button{min-height:44px}\r\n.reminders-computer-offer span{font-size:12px}\r\n#remindersPanel[data-reminder-room=ready]{\r\n position:fixed;inset:3dvh auto auto 50%;transform:translateX(-50%);\r\n box-sizing:border-box;width:min(620px,calc(100vw - 16px));height:94dvh;max-width:none;max-height:none;\r\n padding:0!important;overflow:hidden;overscroll-behavior:contain;border:0!important;border-radius:0!important;\r\n background:transparent!important;box-shadow:none!important;color:#efe7ff;isolation:isolate;\r\n}\r\n/* A portal face can be smaller than a viewport. Keep the model and its fitted screen inside that face. */\r\n#remindersPanel.portal-framed[data-reminder-room=ready]{\r\n inset:var(--face-top) auto auto var(--face-left);width:var(--face-width);height:var(--face-height);transform:none;\r\n}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-stage,\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-screen{position:absolute;inset:0;box-sizing:border-box}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-stage{z-index:0;pointer-events:none;overflow:hidden}\r\n.reminders-computer-canvas{display:block;width:100%;height:100%;pointer-events:none}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-screen{\r\n z-index:1;inset:auto;overflow:auto;overscroll-behavior:contain;padding:8px 10px;\r\n border:1px solid #8e6cc4;border-radius:8px;background:#21172feF;scrollbar-color:#9670bc #21172f;\r\n}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen>header{\r\n display:flex;justify-content:space-between;align-items:center;gap:10px;\r\n margin:0 0 8px;padding:5px 0 8px;background:transparent;border:0;border-bottom:1px solid #8e6cc4;border-radius:0;\r\n}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen h2{font-size:18px;margin:0}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen p{margin:6px 0}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen .status{padding:5px 8px;min-height:0}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen form{gap:8px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen label{font-size:12px;gap:3px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen input,\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen select{padding:7px;font-size:16px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen .fields{gap:7px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen button{min-height:40px;padding:6px 9px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen .reminders-computer-offer{display:none}\r\n#remindersPanel[data-reminder-room=ready]:focus-visible{outline:2px solid #b99cff;outline-offset:-2px}\r\n#remindersPanel[data-reminder-room=ready]::backdrop{background:radial-gradient(ellipse at 50% 40%,#3a1d63e6,#07040de6 70%)}\r\n#remindersPanel[data-reminder-room=ready]{touch-action:pan-y}\r\n#remindersPanel[data-reminder-room=ready] :is(button,input,select,textarea,a,label,summary){cursor:auto}\r\n@media(max-width:390px){#remindersPanel[data-reminder-room=ready]>.reminders-computer-screen{padding:6px 7px}}\r\n@media(prefers-reduced-motion:reduce){#remindersPanel[data-reminder-room=ready]{scroll-behavior:auto}}\r\n\r\n/* Emission is below the aperture's opaque inner lip and reflective cover glass. */\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-screen{border:0;border-radius:16px;clip-path:inset(0 round 16px);background:#17252d;backface-visibility:hidden;padding:14px;box-shadow:inset 0 0 0 8px #080d12,inset 0 10px 18px #000c;transform-style:flat}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-glass{position:absolute;box-sizing:border-box;pointer-events:none;z-index:2;border:9px solid #070c11;border-radius:16px;background:linear-gradient(120deg,#defaff14,transparent 35%,#91d6ff08);box-shadow:inset 0 5px 12px #0009,0 1px 1px #9baebc66;backface-visibility:hidden}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-glass[hidden]{display:none}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-computer-screen[hidden]{display:none}\r\n#remindersPanel[data-reminder-room=offer]{padding:8px!important;background:#161e29!important;border:0!important;overflow:auto}\r\n#remindersPanel[data-reminder-room=offer] .reminders-computer-offer{font-size:12px}\r\n\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen :is(button,input,select){min-height:44px}\r\n\r\n/* R3-12 streamlined TV: four controls live on the glass; the rest of the form sits behind the wrench hatch knob on the casing. */\r\n#remindersPanel[data-reminder-room=ready]:not([data-hatch=open]) :is(.tv-hatch,#pushStatus){display:none}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen{font-size:13px;color:var(--strip-dim,#e3b3ff)}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen>header{min-height:0!important;margin:0 0 4px!important;padding:0 0 3px!important}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen>header [data-close]{min-height:28px;min-width:0;padding:2px 8px;font-size:11px}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen h2{font:15px/1 'VT323',ui-monospace,monospace;letter-spacing:.12em;color:var(--strip-dim,#e3b3ff)}\r\n#remindersPanel[data-reminder-room=ready] .reminders-computer-screen>.reminder-live{margin:0 0 4px;font-size:12px;min-height:0;line-height:1.2}\r\n#remindersPanel[data-reminder-room=ready] .tv-face{grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:4px 8px;margin:0}\r\n#remindersPanel[data-reminder-room=ready] .tv-times{grid-column:1;grid-row:1;gap:10px}\r\n#remindersPanel[data-reminder-room=ready] .tv-face .switch-plate{grid-column:2;grid-row:1;padding:0;border:0;background:none;box-shadow:none;min-height:0}\r\n#remindersPanel[data-reminder-room=ready] .tv-face .switch-plate strong,#remindersPanel[data-reminder-room=ready] .tv-times label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}\r\n#remindersPanel[data-reminder-room=ready] .tv-face .hardware-toggle{min-width:84px;min-height:44px}\r\n#remindersPanel[data-reminder-room=ready] .tv-quiet,#remindersPanel[data-reminder-room=ready] .tv-tones{grid-column:1/-1}\r\n#remindersPanel[data-reminder-room=ready] .tv-dial{--size:62px}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-hatch-knob{display:grid;place-items:center;position:absolute;z-index:3;width:44px;height:44px;margin:0;padding:0;border:0;border-radius:50%;color:#ffbf4a;cursor:pointer;touch-action:manipulation;background:radial-gradient(closest-side,#2b2230 0 52%,#968890 55% 60%,#0000 63%),repeating-conic-gradient(#ad99a5 0 5deg,#3a2e3f 5deg 10deg);box-shadow:0 3px 5px #000b,0 0 0 1px #0d0a10}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-hatch-knob svg{width:22px;height:22px;filter:drop-shadow(0 0 3px #ffbf4a)}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-hatch-knob[aria-expanded=true]{box-shadow:0 3px 5px #000b,0 0 0 1px #0d0a10,0 0 12px #ffbf4a}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-hatch-knob[hidden]{display:none}\r\n#remindersPanel[data-reminder-room=ready]>.reminders-hatch-knob:focus-visible{outline:3px solid #ffca62;outline-offset:2px}\r\n#remindersPanel[data-reminder-room=ready] .tv-quiet-track input{background:none!important;border:0!important;padding:0!important;box-shadow:none!important;min-height:44px}\r\n\r\n/* R3-12 reminders TV face: power, times-per-day dial, quiet-hours slider (moon to sun), three tone faces. Materials reuse the control-board\r\n   recipes (knurled dial, plum caps, gold pointer); the rest of the form sits behind the wrench hatch (.tv-hatch). */\r\n.tv-face{display:grid;gap:8px;margin:8px 0;color:var(--strip-dim,#e3b3ff)}\r\n.tv-face .switch-plate{margin:0}\r\n.tv-times{display:flex;align-items:center;gap:14px}\r\n.tv-times label{font:700 12px var(--pixel-font,monospace);letter-spacing:.12em}\r\n.tv-dial{--size:70px;position:relative;flex:none;width:var(--size);height:var(--size);border-radius:50%;touch-action:none;cursor:grab;background:radial-gradient(closest-side,#0d0a10 76%,#2f2536 79% 87%,#0000 89%)}\r\n.tv-dial:focus-visible{outline:3px solid #ffca62;outline-offset:2px}\r\n.tv-ticks{position:absolute;inset:0;pointer-events:none}\r\n.tv-ticks s{position:absolute;left:calc(50% - 1px);top:0;width:2px;height:5px;border-radius:1px;background:#8a7686;transform-origin:1px calc(var(--size) / 2);transform:rotate(var(--a,0deg))}\r\n.tv-ticks s[data-on]{background:#4dff9a;box-shadow:0 0 5px #4dff9a}\r\n.tv-dial i{position:absolute;inset:10px;border-radius:50%;transform:rotate(var(--turn,0deg));transition:transform .18s cubic-bezier(.3,1.6,.5,1);pointer-events:none;background:radial-gradient(closest-side,#7a6677 0 68%,#584656 74%,#0000 76%),repeating-conic-gradient(#ad99a5 0 5deg,#3a2e3f 5deg 10deg);box-shadow:0 3px 5px #000b,0 0 0 1px #0d0a10}\r\n.tv-dial i:before{content:'';position:absolute;left:50%;top:6%;width:3px;height:30%;margin-left:-1.5px;border-radius:2px;background:#ffbf4a;box-shadow:0 0 4px #ffbf4a}\r\n.tv-times output{font:46px/1 'VT323',var(--pixel-font,monospace);color:#4dff9a;text-shadow:0 0 8px #4dff9a}\r\n.tv-quiet{display:flex;align-items:center;gap:8px}\r\n.tv-quiet>svg{flex:none;width:20px;height:20px;filter:drop-shadow(0 0 4px var(--portal-strip,#b026ff))}\r\n.tv-quiet output{flex:none;font:14px/1 'VT323',var(--pixel-font,monospace);min-width:6.5ch;text-align:right}\r\n.tv-quiet-track{position:relative;flex:1;min-width:0;height:44px}\r\n.tv-quiet-track:before{content:'';position:absolute;left:0;right:0;top:17px;height:10px;border-radius:5px;background:#2a1a3a;box-shadow:inset 0 0 0 2px #0a0510}\r\n.tv-quiet-fill{position:absolute;top:17px;height:10px;left:var(--a,40%);right:calc(100% - var(--b,80%));border-radius:5px;background:linear-gradient(90deg,var(--portal-strip,#b026ff),#e7b6ff);box-shadow:0 0 6px var(--portal-strip,#b026ff)}\r\n.tv-quiet-track input{position:absolute;inset:0;width:100%;height:44px;margin:0;padding:0!important;border:0!important;min-height:0;background:none!important;box-shadow:none!important;appearance:none;-webkit-appearance:none;pointer-events:none}\r\n.tv-quiet-track input::-webkit-slider-runnable-track{height:44px;background:none;border:0}\r\n.tv-quiet-track input::-moz-range-track{height:44px;background:none;border:0}\r\n.tv-quiet-track input::-webkit-slider-thumb{-webkit-appearance:none;pointer-events:auto;width:44px;height:44px;border:0;border-radius:50%;background:radial-gradient(circle,#211a26 0 4px,#d9c6d2 5px 9px,#7a6677 10px 12px,#0000 13px)}\r\n.tv-quiet-track input::-moz-range-thumb{pointer-events:auto;width:44px;height:44px;border:0;border-radius:50%;background:radial-gradient(circle,#211a26 0 4px,#d9c6d2 5px 9px,#7a6677 10px 12px,#0000 13px)}\r\n.tv-quiet-track #quietTo::-webkit-slider-thumb{background:radial-gradient(circle,#4a2d0a 0 4px,#ffd08a 5px 9px,#c98a2e 10px 12px,#0000 13px)}\r\n.tv-quiet-track #quietTo::-moz-range-thumb{background:radial-gradient(circle,#4a2d0a 0 4px,#ffd08a 5px 9px,#c98a2e 10px 12px,#0000 13px)}\r\n.tv-quiet-track input:focus-visible{outline:none}.tv-quiet-track input:focus-visible::-webkit-slider-thumb{outline:3px solid #ffca62;outline-offset:-6px}\r\n.tv-tones{display:flex;align-items:center;gap:8px}\r\n.tv-tones button{flex:1 1 0;min-width:0;min-height:44px;padding:0;border:2px solid #26202d;border-top-color:#b399a8;border-radius:8px;background:linear-gradient(#5d4a5b,#3c3043);box-shadow:inset 0 2px #ffffff30,0 3px #211929;line-height:1;color:var(--strip-dim,#e3b3ff);opacity:.9}\r\n.tv-tones button svg{width:28px;height:28px;margin:auto;display:block}\r\n.tv-tones button[aria-checked=true]{color:#fff;opacity:1;box-shadow:inset 0 2px #ffffff40,0 3px #211929,0 0 12px var(--portal-strip,#b026ff)}\r\n.tv-tones button:active{transform:translateY(2px)}\r\n.tv-tones .tv-save{flex:1.2 1 0;font:700 15px/1 'VT323',var(--pixel-font,monospace);letter-spacing:.12em;color:#4dff9a;text-shadow:0 0 8px #4dff9a;opacity:1;background:linear-gradient(#2c3a33,#17231d);border-color:#46604f #0e1612 #0e1612;box-shadow:inset 0 0 10px #4dff9a33,0 3px #0c1410}\r\n.reminders-hatch-knob{display:none}\r\n@media(prefers-reduced-motion:reduce){.tv-dial i{transition:none}}\r\n.tv-status{margin:4px 0 0;min-height:0;font-size:12px;line-height:1.2;color:var(--strip-dim,#e3b3ff)}.tv-status:empty{display:none}\r\n.tv-tones .tv-save svg{display:none}\r\n.tv-dial[aria-disabled=true]{opacity:.45;cursor:default}\r\n","modules/rooms/reminders-computer.mjs":"import * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {claimGraphics} from '../graphics-lifecycle.mjs';\r\n\r\nconst MODEL='/pod/rooms/console.glb',STYLE='/modules/rooms/reminders-computer.css';\r\nconst MAX_SPIN=10,KEY_STEP=2,DRAG_DEG_PER_PX=.16,DRAG_SLOP=8;\r\nconst INTERACTIVE='button,input,select,textarea,a,label,summary,[contenteditable]';\r\nconst SCREEN={left:-.185,right:.185,bottom:.39,top:.66,z:.115};\r\nconst MODEL_CENTER=new THREE.Vector3(0,.515,.10668);\r\nconst MODEL_SIZE=new THREE.Vector3(.527069,.9804382,.481444);\r\nconst REDUCED=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n\r\nexport const spinBy=(spin,delta)=>Math.max(-MAX_SPIN,Math.min(MAX_SPIN,spin+delta));\r\n\r\n// CSS projective plane: the browser maps drawing, clipping, focus and pointer hits together.\r\nexport function screenProjection(points,width=600,height=440){\r\n const [[x0,y0],[x1,y1],[x2,y2],[x3,y3]]=points;\r\n const dx1=x1-x2,dx2=x3-x2,dx3=x0-x1+x2-x3;\r\n const dy1=y1-y2,dy2=y3-y2,dy3=y0-y1+y2-y3,det=dx1*dy2-dx2*dy1;\r\n if(!Number.isFinite(det)||Math.abs(det)<1e-8)throw new Error('Degenerate TV aperture');\r\n const g=(dx3*dy2-dx2*dy3)/det,h=(dx1*dy3-dx3*dy1)/det;\r\n const a=x1-x0+g*x1,b=x3-x0+h*x3,d=y1-y0+g*y1,e=y3-y0+h*y3;\r\n return [a/width,d/width,0,g/width,b/height,e/height,0,h/height,0,0,1,0,x0,y0,0,1];\r\n}\r\n\r\nconst current=(panel,token,epoch)=>!token.disposed&&token.epoch===epoch&&panel.open&&panel.isConnected;\r\nasync function cachedRoomAssets(isCurrent){\r\n const storage=globalThis.caches;\r\n if(!storage?.keys||!storage?.open)throw new Error('The optional Reminders room is not downloaded.');\r\n const names=await storage.keys();if(!isCurrent())return null;\r\n // CacheStorage returns names in creation order. Search newest first so an old retained shell cannot\r\n // shadow the complete packet installed later; model and stylesheet must come from the same store.\r\n for(const name of names.reverse()){\r\n  const store=await storage.open(name);if(!isCurrent())return null;\r\n  const model=await store.match(MODEL);if(!isCurrent())return null;\r\n  if(!model?.ok)continue;\r\n  let style=await store.match(STYLE);if(!isCurrent())return null;\r\n  let styleText=style?.ok?await style.clone().text():'';if(!isCurrent())return null;\r\n  if(!styleText.includes('reminders-computer:glb-v2')){\r\n   // A previous release may already have the large model. Refresh only the small stylesheet\r\n   // from the current release and place it beside that model; never redownload the GLB here.\r\n   try{\r\n    const response=await fetch(STYLE,{cache:'no-store'});if(!isCurrent())return null;\r\n    if(response.ok){const updated=response.clone(),candidate=await response.text();if(!isCurrent())return null;\r\n     if(candidate.includes('reminders-computer:glb-v2')){await store.put(STYLE,updated);if(!isCurrent())return null;styleText=candidate;}\r\n    }\r\n   }catch{}\r\n  }\r\n  if(!styleText.includes('reminders-computer:glb-v2'))continue;\r\n  return {model,styleText};\r\n }\r\n throw new Error('The optional Reminders room is not downloaded.');\r\n}\r\n\r\nfunction useStyle(text,token){\r\n let style=document.getElementById('remindersComputerStyle');\r\n if(!style){style=document.createElement('style');style.id='remindersComputerStyle';document.head.append(style);}\r\n style.textContent=text;token.style=style;\r\n}\r\nfunction loadingManager(){\r\n const manager=new THREE.LoadingManager();\r\n manager.setURLModifier(url=>{\r\n  if(/^(?:data:|blob:)/i.test(url))return url;\r\n  throw new Error('The Reminders model contains an external dependency.');\r\n });\r\n return manager;\r\n}\r\n\r\n// Replace the old printed keyboard legends in the optional cabinet with engraved return glyphs.\r\n// The original normal/roughness maps still describe the raised keycaps and worn metal.\r\nfunction prepareCabinet(root){\r\n root.traverse(node=>{if(!node.isMesh)return;for(const mat of [].concat(node.material||[])){\r\n  mat.onBeforeCompile=shader=>{\r\n   shader.vertexShader='varying vec3 vCabinetPoint;\\n'+shader.vertexShader.replace('#include <begin_vertex>','#include <begin_vertex>\\nvCabinetPoint=position;');\r\n   shader.fragmentShader='varying vec3 vCabinetPoint;\\nfloat cabinetLine(vec2 p,vec2 a,vec2 b){vec2 d=b-a;return length(p-a-d*clamp(dot(p-a,d)/dot(d,d),0.,1.));}\\n'+shader.fragmentShader.replace('#include <map_fragment>',`#include <map_fragment>\r\n    if(vCabinetPoint.x>.015&&vCabinetPoint.x<.245&&vCabinetPoint.y>.17&&vCabinetPoint.y<.37&&vCabinetPoint.z>.055){\r\n     vec2 p=vCabinetPoint.xy;float d=1.;\r\n     for(int i=0;i<2;i++){float y=.225+float(i)*.05;\r\n      d=min(d,cabinetLine(p,vec2(.145,y+.016),vec2(.145,y)));\r\n      d=min(d,cabinetLine(p,vec2(.145,y),vec2(.107,y)));\r\n      d=min(d,cabinetLine(p,vec2(.107,y),vec2(.119,y+.012)));\r\n      d=min(d,cabinetLine(p,vec2(.107,y),vec2(.119,y-.012)));\r\n     }\r\n     float ink=1.-smoothstep(.001,.0025,d);float grain=.0015*sin(p.y*5800.);\r\n     float printMask=step(.075,p.x)*step(p.x,.235)*(step(.205,p.y)*step(p.y,.245)+step(.255,p.y)*step(p.y,.295));\r\n     diffuseColor.rgb=mix(diffuseColor.rgb,vec3(.012,.009,.015)+grain,printMask);\r\n     diffuseColor.rgb=mix(diffuseColor.rgb,vec3(.66,.51,.27),ink);\r\n    }`);\r\n  };mat.customProgramCacheKey=()=>'reminders-icon-keycaps-v1';mat.needsUpdate=true;\r\n }});\r\n}\r\n\r\nfunction releaseObject(root){\r\n const geometries=new Set(),materials=new Set(),textures=new Set(),images=new Set();\r\n root?.traverse?.(node=>{\r\n  if(node.geometry)geometries.add(node.geometry);\r\n  for(const material of [].concat(node.material||[]))if(material){materials.add(material);for(const value of Object.values(material))if(value?.isTexture)textures.add(value);}\r\n });\r\n for(const texture of textures){const image=texture.source?.data??texture.image;if(image&&typeof image.close==='function')images.add(image);texture.dispose();}\r\n for(const geometry of geometries)geometry.dispose();for(const material of materials)material.dispose();\r\n for(const image of images)try{image.close();}catch{}\r\n}\r\nfunction releaseView(view){\r\n if(!view||view.disposed)return;view.disposed=true;cancelAnimationFrame(view.raf);view.resize?.disconnect();\r\n for(const [target,name,fn] of view.listeners)target.removeEventListener(name,fn);\r\n releaseObject(view.scene);view.renderer?.dispose();view.renderer?.forceContextLoss();view.releaseGpu?.(view.canvas);\r\n view.canvas?.remove();view.screen?.remove();view.glass?.remove();\r\n}\r\n\r\nexport function makeCords(scene){\r\n const colors=[0x52e4ff,0xff45d5,0xffc95c,0x9d78ff,0x52e4ff,0xff45d5,0xffc95c,0x4cc9e8,0xff6eac,0xa38aff,0xf3a765,0x53dfca,0x8cd6ff];\r\n const paths=[\r\n  [[-.22,1.02,-.28],[-.35,.92,-.32],[-.4,.75,-.34],[-.31,.65,-.32],[-.29,.55,-.31],[-.39,.47,-.34],[-.37,.3,-.32],[-.29,.12,-.31]],\r\n  [[.22,1,-.27],[.34,.9,-.32],[.4,.73,-.34],[.31,.63,-.32],[.29,.52,-.31],[.4,.43,-.34],[.37,.27,-.32],[.3,.08,-.31]],\r\n  [[-.2,.83,-.32],[-.34,.78,-.35],[-.41,.67,-.36],[-.36,.59,-.34],[-.29,.64,-.32],[-.36,.72,-.34],[-.43,.63,-.36],[-.39,.48,-.34],[-.34,.23,-.31]],\r\n  [[.2,.78,-.32],[.34,.73,-.35],[.42,.64,-.36],[.36,.55,-.34],[.29,.6,-.32],[.37,.69,-.34],[.44,.59,-.36],[.4,.42,-.34],[.34,.19,-.31]],\r\n  [[-.3,.98,-.36],[-.43,.88,-.38],[-.42,.79,-.38],[-.32,.84,-.36],[-.26,.77,-.34],[-.42,.69,-.38],[-.44,.56,-.38],[-.33,.5,-.35]],\r\n  [[.3,.96,-.36],[.43,.86,-.38],[.42,.77,-.38],[.32,.82,-.36],[.26,.74,-.34],[.43,.65,-.38],[.45,.51,-.38],[.34,.45,-.35]],\r\n  [[-.48,1.08,-.42],[-.25,1.12,-.43],[0,1.06,-.44],[.24,1.12,-.43],[.48,1.04,-.42],[.4,.94,-.42],[.18,.98,-.43],[-.05,.93,-.43],[-.3,.99,-.42],[-.48,.9,-.42]],\r\n  [[-.49,-.08,-.42],[-.28,-.14,-.43],[-.05,-.07,-.44],[.19,-.14,-.43],[.46,-.05,-.42],[.4,.05,-.42],[.16,-.01,-.43],[-.1,.06,-.43],[-.35,.01,-.42],[-.49,.12,-.42]],\r\n  [[-.5,.74,-.45],[-.42,.98,-.46],[-.26,1.07,-.47],[-.15,.98,-.46],[-.3,.83,-.45],[-.45,.61,-.46],[-.43,.42,-.47],[-.25,.34,-.46],[-.14,.43,-.45],[-.35,.64,-.46],[-.5,.74,-.45]],\r\n  [[.5,.71,-.45],[.42,.95,-.46],[.26,1.04,-.47],[.15,.95,-.46],[.3,.8,-.45],[.45,.58,-.46],[.43,.39,-.47],[.25,.31,-.46],[.14,.4,-.45],[.35,.61,-.46],[.5,.71,-.45]],\r\n  [[-.48,1.03,-.5],[-.34,.94,-.51],[-.18,.82,-.52],[0,.66,-.52],[.18,.49,-.51],[.34,.31,-.51],[.49,.12,-.5]],\r\n  [[.48,1.02,-.5],[.34,.91,-.51],[.18,.78,-.52],[0,.63,-.52],[-.18,.46,-.51],[-.34,.28,-.51],[-.49,.1,-.5]],\r\n  [[-.52,.28,-.48],[-.36,.39,-.49],[-.18,.47,-.5],[0,.53,-.51],[.2,.61,-.5],[.38,.72,-.49],[.52,.86,-.48]]\r\n ];\r\n const radii=[.0032,.0038,.0028,.0034,.0026,.003,.0035,.0028,.0042,.003,.0028,.0036,.0034];\r\n if(paths.length!==colors.length||paths.length!==radii.length||radii.some(radius=>!Number.isFinite(radius)||radius<=0||radius>.01))throw new Error('Invalid Reminders cable layout.');\r\n for(let i=0;i<paths.length;i++){\r\n  const points=paths[i].map(point=>new THREE.Vector3(...point));\r\n  const curve=new THREE.CatmullRomCurve3(points,false,'centripetal');\r\n  const radius=radii[i];\r\n  const mesh=new THREE.Mesh(new THREE.TubeGeometry(curve,48,radius,5,false),new THREE.MeshBasicMaterial({color:colors[i],transparent:true,opacity:.72,depthWrite:false}));\r\n  mesh.renderOrder=-1;scene.add(mesh);\r\n }\r\n}\r\n\r\nexport function mountRemindersComputer(panel=document.getElementById('remindersPanel')){\r\n if(!panel)return null;\r\n let disposed=false,epoch=0,pendingEpoch=-1,style=null,active=null,wrapped=null,restoreChildren=null;\r\n const token={get disposed(){return disposed;},get epoch(){return epoch;},view:null,style:null,fail:null};\r\n const offer=document.createElement('div');offer.className='reminders-computer-offer';\r\n const button=document.createElement('button');button.type='button';button.textContent='Download Reminders computer';\r\n const note=document.createElement('span');note.setAttribute('role','status');offer.append(button,note);\r\n const hint=document.createElement('span');hint.id='remindersComputerHint';hint.textContent='Drag the computer sideways, or press Left and Right arrow keys, to turn it.';\r\n Object.assign(hint.style,{position:'absolute',width:'1px',height:'1px',padding:'0',margin:'-1px',overflow:'hidden',clip:'rect(0,0,0,0)',whiteSpace:'nowrap',border:'0'});\r\n // R3-12: the wrench hatch knob on the casing opens the rest of the reminder form (only when the form has a hatch).\r\n const knob=document.createElement('button');knob.type='button';knob.className='reminders-hatch-knob';knob.setAttribute('aria-label','Service hatch: more reminder settings');knob.setAttribute('aria-expanded','false');knob.setAttribute('aria-controls','reminderHatch');\r\n knob.innerHTML='<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 4a5 5 0 0 0-5 6l-7 7 3 3 7-7a5 5 0 0 0 6-5l-3 2-3-3z\"/></svg>';\r\n knob.onclick=()=>{const open=panel.dataset.hatch!=='open';panel.dataset.hatch=open?'open':'closed';knob.setAttribute('aria-expanded',String(open));if(open)document.getElementById('reminderHatch')?.scrollIntoView({block:'nearest'});};\r\n const setFallback=message=>{\r\n  panel.dataset.reminderRoom='offer';panel.tabIndex=-1;note.textContent=message;offer.hidden=false;offer.style.removeProperty('display');\r\n  if(!restoreChildren){\r\n   const children=[...panel.children].filter(node=>node!==hint&&node!==knob&&node.id!=='coachDock'&&node.id!=='myr5Shell'&&!node.classList.contains('portal-peer-ui'));\r\n   const originalNext=new Map(children.map(node=>[node,node.nextSibling]));\r\n   const host=document.createElement('div');host.className='reminders-computer-baked';\r\n   host.style.cssText='box-sizing:border-box;padding:clamp(18px,5vw,38px);border:8px solid #30353f;border-radius:32px;background:linear-gradient(135deg,#89939e,#373f4a 16%,#151b25 70%,#626d77);box-shadow:inset 0 3px 2px #d8e3ed88,inset 0 -8px 8px #000a,0 18px 35px #0008';\r\n   const screen=document.createElement('div');screen.className='reminders-computer-baked-glass';\r\n   screen.style.cssText='box-sizing:border-box;max-height:78dvh;overflow:auto;padding:16px;border:6px solid #080b12;border-radius:18px;background:radial-gradient(ellipse at 20% 0,#344350,#14212a 65%);box-shadow:inset 0 4px 15px #000b,0 2px 1px #a6b5c266;color:#eff9ff';\r\n   screen.append(...children);const fallbackStyle=document.createElement('style');fallbackStyle.textContent='.reminders-computer-baked-glass :is(button,input,select){min-height:44px;font-size:16px;box-sizing:border-box;max-width:100%}.reminders-computer-baked-glass form{display:grid;gap:10px}.reminders-computer-baked-glass label{display:grid;gap:4px}.reminders-computer-baked-glass header{display:flex;align-items:center;justify-content:space-between;gap:10px}.reminders-computer-baked-glass .reminders-computer-offer{display:grid;gap:8px}';host.append(fallbackStyle,screen);panel.prepend(host);restoreChildren={screen,host,children,originalNext};wrapped=restoreChildren;\r\n  }\r\n };\r\n const buttonClick=()=>{if(!window.myr5Packs?.open?.('room-reminders'))note.textContent='Open Downloads from Settings to add this room.';};\r\n button.addEventListener('click',buttonClick);\r\n panel.querySelector('header')?.after(offer);\r\n const unwrap=()=>{\r\n  if(!restoreChildren)return;\r\n  const {screen,host,children,originalNext}=restoreChildren;\r\n  for(let index=children.length-1;index>=0;index--){\r\n   const node=children[index];if(node.parentNode!==screen)continue;\r\n   const original=originalNext.get(node);\r\n   const anchor=original?.parentNode===panel?original:children.slice(index+1).find(next=>next.parentNode===panel)||(screen.parentNode===panel?screen:host);\r\n   panel.insertBefore(node,anchor);\r\n  }\r\n  screen.remove();host.remove();offer.hidden=false;offer.style.removeProperty('display');restoreChildren=null;\r\n };\r\n const tearDown=()=>{\r\n  if(wrapped){unwrap();wrapped=null;}\r\n  if(active){releaseView(active);active=null;}\r\n  panel.removeAttribute('aria-describedby');delete panel.dataset.hatch;knob.setAttribute('aria-expanded','false');panel.removeAttribute('aria-keyshortcuts');panel.removeAttribute('tabindex');\r\n  delete panel.dataset.reminderRoom;\r\n  if(style){style.remove();style=null;token.style=null;}\r\n };\r\n token.fail=error=>{if(disposed)return;epoch++;tearDown();setFallback('The 3D computer is unavailable here; reminders still work.');};\r\n async function check(){\r\n  if(disposed||!panel.open||panel.dataset.reminderRoom==='ready'||pendingEpoch===epoch)return;\r\n  const mine=++epoch;pendingEpoch=mine;let parsed=null;\r\n  try{\r\n   const assets=await cachedRoomAssets(()=>current(panel,token,mine));\r\n   if(!assets||!current(panel,token,mine))return;\r\n   const modelBytes=await assets.model.arrayBuffer();\r\n   if(!current(panel,token,mine))return;\r\n   const styleText=assets.styleText;\r\n   useStyle(styleText,token);style=token.style;\r\n   const manager=loadingManager(),loader=new GLTFLoader(manager),gltf=await loader.parseAsync(modelBytes,'');parsed=gltf.scene;\r\n   if(!current(panel,token,mine)){releaseObject(parsed);return;}\r\n   prepareCabinet(parsed);\r\n   const releaseGpu=claimGraphics('reminders',{keep:['portal']});await releaseGpu.ready; // the hidden 3D coach yields; back on close\r\n   if(!current(panel,token,mine)){releaseGpu();releaseObject(parsed);return;}\r\n   if(wrapped){unwrap();wrapped=null;}\r\n   const host=document.createElement('div');host.className='reminders-computer-stage';\r\n   const children=[...panel.children].filter(node=>node!==offer&&node!==hint&&node!==knob&&node.id!=='coachDock'&&node.id!=='myr5Shell'&&!node.classList.contains('portal-peer-ui'));\r\n   const originalNext=new Map(children.map(node=>[node,node.nextSibling]));\r\n   const screen=document.createElement('div');screen.className='reminders-computer-screen';screen.dataset.computerScreen='';screen.append(...children);\r\n   panel.prepend(host);host.append(document.createElement('canvas'));const canvas=host.firstElementChild;canvas.className='reminders-computer-canvas';canvas.setAttribute('aria-hidden','true');\r\n   const glass=document.createElement('div');glass.className='reminders-computer-glass';glass.setAttribute('aria-hidden','true');panel.append(screen,glass);restoreChildren={screen,host,children,originalNext};wrapped=restoreChildren;\r\n   const scene=new THREE.Scene();const model=new THREE.Group();model.position.copy(MODEL_CENTER);gltf.scene.position.sub(MODEL_CENTER);model.add(gltf.scene);scene.add(model);makeCords(scene);\r\n   const view={releaseGpu,canvas,screen,glass,renderer:null,scene,camera:null,model,resize:null,raf:0,listeners:[],disposed:false};active=view;token.view=view;parsed=null;\r\n   const renderer=new THREE.WebGLRenderer({canvas,alpha:true,antialias:true,powerPreference:'low-power'});view.renderer=renderer;\r\n   renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.setPixelRatio(Math.min(devicePixelRatio||1,1.5));renderer.setClearColor(0x000000,0);\r\n   scene.add(new THREE.HemisphereLight(0xe8f8ff,0x241532,2.1));const keyLight=new THREE.DirectionalLight(0xffedcf,2.5);keyLight.position.set(-1.5,2,2.5);scene.add(keyLight);\r\n   const camera=new THREE.PerspectiveCamera(31,1,.05,12);view.camera=camera;let width=0,height=0,yaw=0,turn=0,lastFrame=0,start=performance.now();\r\n   function listen(target,name,fn){target.addEventListener(name,fn);view.listeners.push([target,name,fn]);}\r\n   function project(x,y,z){const point=new THREE.Vector3(x,y,z).sub(MODEL_CENTER);point.applyAxisAngle(new THREE.Vector3(0,1,0),turn).add(MODEL_CENTER).project(camera);return [(point.x+1)*width/2,(1-point.y)*height/2];}\r\n   function screenRect(){\r\n    const points=[[SCREEN.left,SCREEN.top,SCREEN.z],[SCREEN.right,SCREEN.top,SCREEN.z],[SCREEN.right,SCREEN.bottom,SCREEN.z],[SCREEN.left,SCREEN.bottom,SCREEN.z]].map(p=>project(...p));\r\n    const front=Math.cos(turn)>0;\r\n    knob.hidden=!front;\r\n    if(!front&&screen.contains(document.activeElement))panel.focus();\r\n    screen.inert=!front;screen.hidden=glass.hidden=!front;screen.setAttribute('aria-hidden',String(!front));\r\n    if(!front)return;\r\n    const logicalWidth=Math.max(160,Math.round(Math.hypot(points[1][0]-points[0][0],points[1][1]-points[0][1])/8)*8),logicalHeight=Math.max(120,Math.round(Math.hypot(points[3][0]-points[0][0],points[3][1]-points[0][1])/8)*8);\r\n    const matrix=screenProjection(points.map(([x,y])=>[x-points[0][0],y-points[0][1]]),logicalWidth,logicalHeight);\r\n    Object.assign(screen.style,{left:points[0][0]+'px',top:points[0][1]+'px',width:logicalWidth+'px',height:logicalHeight+'px',transformOrigin:'0 0',transform:`matrix3d(${matrix.join(',')})`});\r\n    glass.style.cssText=screen.style.cssText;\r\n    Object.assign(knob.style,{left:Math.round((points[2][0]+points[3][0])/2-22)+'px',top:Math.round(Math.max(points[2][1],points[3][1])+10)+'px'});\r\n    screen.dataset.aperture=JSON.stringify(points);\r\n   }\r\n   let editTurn=null;\r\n   function render(now=performance.now()){if(view.disposed||!panel.open||!panel.isConnected)return;if(width&&height){try{\r\n    const editing=screen.contains(document.activeElement);\r\n    if(editing&&editTurn===null)editTurn=turn;if(!editing)editTurn=null;\r\n    model.rotation.y=turn=editTurn??(yaw+(REDUCED()?0:Math.sin((now-start)/3300)*.012));renderer.render(scene,camera);screenRect();}catch(error){if(current(panel,token,mine))token.fail(error);}}}\r\n   function resize(){if(view.disposed)return;\r\n    // getBoundingClientRect() includes portal arrival transforms (scale(.05) -> scale(1)) and\r\n    // portal-inset zoom. Those are visual transforms, not layout changes; using them as the\r\n    // renderer's coordinate system permanently shrinks the projected screen after arrival.\r\n    width=host.clientWidth;height=host.clientHeight;if(!width||!height)return;\r\n    renderer.setSize(width,height,false);camera.aspect=width/height;const vfov=THREE.MathUtils.degToRad(camera.fov),distance=Math.max(MODEL_SIZE.y*.53/Math.tan(vfov/2),MODEL_SIZE.x*.53/Math.tan(vfov/2)/camera.aspect)*1.08;camera.position.set(0,.515,.10668+distance);camera.lookAt(MODEL_CENTER);camera.zoom=1.5;camera.updateProjectionMatrix();render();}\r\n   let drag=null;const down=event=>{if(event.target.closest?.(INTERACTIVE)||(event.pointerType==='mouse'&&event.button!==0))return;drag={id:event.pointerId,x:event.clientX,y:event.clientY,yaw,moving:false};};\r\n   const move=event=>{if(!drag||drag.id!==event.pointerId)return;const dx=event.clientX-drag.x,dy=event.clientY-drag.y;if(!drag.moving){if(Math.abs(dy)>DRAG_SLOP&&Math.abs(dy)>Math.abs(dx)){drag=null;return;}if(Math.abs(dx)<DRAG_SLOP)return;drag.moving=true;panel.setPointerCapture?.(drag.id);}yaw=THREE.MathUtils.degToRad(spinBy(THREE.MathUtils.radToDeg(drag.yaw),dx*DRAG_DEG_PER_PX));render();window.dispatchEvent(new Event('myr5:reminders-turn'));};\r\n   const end=event=>{if(drag?.id===event.pointerId){if(drag.moving)panel.releasePointerCapture?.(drag.id);drag=null;}};\r\n   const key=event=>{if(event.target!==panel||!/^Arrow(Left|Right)$/.test(event.key))return;event.preventDefault();yaw=THREE.MathUtils.degToRad(spinBy(THREE.MathUtils.radToDeg(yaw),event.key==='ArrowLeft'?-KEY_STEP:KEY_STEP));render();window.dispatchEvent(new Event('myr5:reminders-turn'));};\r\n   listen(screen,'focusin',()=>render());listen(screen,'focusout',()=>queueMicrotask(()=>render()));\r\n   listen(panel,'pointerdown',down);listen(panel,'pointermove',move);listen(panel,'pointerup',end);listen(panel,'pointercancel',end);listen(panel,'lostpointercapture',end);listen(panel,'keydown',key);\r\n   listen(canvas,'webglcontextlost',event=>{event.preventDefault();if(current(panel,token,mine))token.fail(new Error('WebGL context lost'));});\r\n   // Activate the stage/screen layout before measuring it. In the portal the dialog is\r\n   // already clipped to a small face; measuring while the ready rules are inactive can\r\n   // produce a zero-sized stage and leave the wrapped controls with no projected screen.\r\n   panel.dataset.reminderRoom='ready';panel.tabIndex=0;panel.setAttribute('aria-keyshortcuts','ArrowLeft ArrowRight');panel.setAttribute('aria-describedby',hint.id);\r\n   view.resize=new ResizeObserver(resize);view.resize.observe(host);resize();\r\n   const media=matchMedia('(prefers-reduced-motion: reduce)');\r\n   const tick=now=>{if(view.disposed||!current(panel,token,mine)||document.visibilityState!=='visible'||media.matches){view.raf=0;return;}if(now-lastFrame>=1000/24){lastFrame=now;render(now);}view.raf=requestAnimationFrame(tick);};\r\n   const startLoop=()=>{if(!view.disposed&&current(panel,token,mine)&&document.visibilityState==='visible'&&!media.matches&&!view.raf)view.raf=requestAnimationFrame(tick);};\r\n   listen(document,'visibilitychange',()=>{if(document.visibilityState!=='visible'&&view.raf){cancelAnimationFrame(view.raf);view.raf=0;}else startLoop();});\r\n   listen(media,'change',()=>{if(media.matches){if(view.raf){cancelAnimationFrame(view.raf);view.raf=0;}render();}else startLoop();});startLoop();\r\n   offer.hidden=true;offer.style.display='none';\r\n  }catch(error){if(parsed)releaseObject(parsed);if(!current(panel,token,mine))return;tearDown();setFallback('The computer room is an optional download. Reminders still work.');}\r\n  finally{if(pendingEpoch===mine)pendingEpoch=-1;}\r\n }\r\n const observer=new MutationObserver(()=>{if(panel.open){window.dispatchEvent(new Event('myr5:reminders-open'));void check();}else{epoch++;tearDown();}});observer.observe(panel,{attributes:true,attributeFilter:['open']});\r\n const onFocus=()=>{if(panel.open)void check();};const onPageHide=()=>{epoch++;tearDown();};const onPageShow=()=>{if(panel.open)void check();};\r\n window.addEventListener('focus',onFocus);window.addEventListener('pagehide',onPageHide);window.addEventListener('pageshow',onPageShow);\r\n const onAccountTransition=()=>{if(pendingEpoch<0)return;epoch++;pendingEpoch=-1;if(panel.open)queueMicrotask(()=>void check());};\r\n window.addEventListener('myr5:account-ready',onAccountTransition);window.addEventListener('myr5:account-cleared',onAccountTransition);\r\n panel.append(hint);if(panel.querySelector('.tv-hatch'))panel.append(knob);\r\n if(panel.open)void check();\r\n return()=>{if(disposed)return;disposed=true;epoch++;observer.disconnect();window.removeEventListener('focus',onFocus);window.removeEventListener('pagehide',onPageHide);window.removeEventListener('pageshow',onPageShow);window.removeEventListener('myr5:account-ready',onAccountTransition);window.removeEventListener('myr5:account-cleared',onAccountTransition);tearDown();button.removeEventListener('click',buttonClick);offer.remove();hint.remove();knob.remove();};\r\n}\r\n","modules/routes.mjs":"import {initShell} from './shell/owner.mjs';\r\n// W2-2A (#4, #5, #17, #18, #30): one hash route per scene, one bottom bar everywhere.\r\n// Each route reuses the opener the app already had (the portal's MENUS open() calls go()). A route's dialog is\r\n// adopted however it was opened (bar button, traced shape, header button, deep link): its #hash goes into\r\n// history, the bottom bar moves inside it (a modal dialog makes everything outside it inert, so the bar has to\r\n// be in there to stay tappable), the bar lights the route, focus goes to its heading and a polite live region\r\n// names it. Phone back closes it and returns to the quilt; back on the quilt asks once before leaving.\r\n// Bundled into app-runtime via app.mjs; no .ts imports, no build defines.\r\nimport {openCustomizer} from './ships/ship-scene-domain.mjs';\r\nimport {ensurePortalMounted} from './portal/portal-entry.mjs';\r\nconst $=id=>document.getElementById(id);\r\n// Kept by reference: the bar can sit inside a dialog that is being removed (portal dispose) and must come back.\r\nlet dockEl=null;\r\nconst dock=()=>dockEl||=$('coachDock');\r\nconst quiltUp=()=>$('portalHome')?.hidden===false;\r\nconst reduced=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\nconst hideQuilt=()=>{if(quiltUp())window.myr5Portal?.hide?.();};\r\nconst panel=name=>{const dialog=$(name+'Panel'),button=document.querySelector(`.coach-dock [data-panel=\"${name}\"]`);if(button)button.click();else if(dialog&&!dialog.open)dialog.showModal();return dialog;};\r\nconst bare=()=>location.pathname+location.search;\r\n// The ship view resolves only once its entrance is over; the portal's dive needs the dialog as soon as it opens.\r\nconst whenOpen=(selector,opening)=>new Promise(resolve=>{\r\n const open=()=>{const found=document.querySelector(selector);return found?.open?found:null;};\r\n const watch=new MutationObserver(()=>{const found=open();if(found){watch.disconnect();resolve(found);}});\r\n watch.observe(document.body,{subtree:true,childList:true,attributes:true,attributeFilter:['open']});\r\n Promise.resolve(opening).then(dialog=>{watch.disconnect();resolve(open()||dialog);},()=>{watch.disconnect();resolve(null);});\r\n});\r\n\r\n// id -> {label, open() -> its dialog (or a promise of it), dialog: selector of the dialog it owns (adopted\r\n// however it opens), page: a no-dialog scene, focus: what a page route focuses, nav: another page, own: the\r\n// dialog keeps its own history (ship-view.mjs pushes #ship itself), shared: adopts that dialog only when it opened it}.\r\nconst DANCE_MODULE='/modules/dance/index.mjs';\r\nexport const ROUTES={\r\n dance:{label:'Dance, Dance, and Obey',dialog:'#dancePanel',open:()=>import(DANCE_MODULE).then(m=>m.openDance())},\r\n world:{label:'War Room base',dialog:'#coachWorldPanel',open:()=>import('./coach-world/view.mjs').then(m=>m.openWorld())},\r\n // Ian 2026-09-23: the square is the workout start page (the pod's viewing port, control board and BEGIN, which\r\n // starts the camera as always) and the oval is the coach's arrival: its ship flies in and beams it down, every time.\r\n // W2-2M: from the top, but BEGIN never under the bar: the page fits at 375x812, a shorter phone scrolls just enough.\r\n workout:{label:'Workout',page:true,dialog:'#portalWorkoutHome',focus:'#view',open(){const portalHome=window.myr5Portal?.openWorkoutHome?.();if(portalHome)return portalHome;hideQuilt();const begin=$('start')?.getBoundingClientRect(),room=innerHeight-(dock()?.offsetHeight||0)-8;scrollTo({top:begin?.height?Math.max(0,scrollY+begin.bottom-room):0,behavior:reduced()?'auto':'smooth'});}},\r\n // W2-2Q: the router is the one owner of the ship view's #ship/#select history (the view only keeps its own standalone).\r\n select:{label:'Coach arrival',dialog:'dialog.ship-view',shared:true,open:()=>whenOpen('dialog.ship-view',window.myr5Menus?.ship?.({entrance:'always',hash:'#select'}))},\r\n food:{label:'Food',dialog:'#mealsPanel',open:()=>typeof window.myr5Menus?.food==='function'?window.myr5Menus.food():panel('meals')},\r\n // R-1017: one achievements page (constellation on top, XP flight below); #battlepass stays as an alias for old links.\r\n achievements:{label:'Achievements',dialog:'#battlePassPanel',open:()=>window.myr5Menus?.achievements?.()},\r\n battlepass:{label:'Achievements',dialog:'#battlePassPanel',shared:true,open:()=>window.myr5Menus?.battlePass?.()},\r\n // The existing opener owns workout shutdown, movement selection, and the hologram lifecycle.\r\n // Direct taps are adopted by the dialog observer; go('library') uses that same opener once.\r\n library:{label:'Movements',dialog:'#library',open(){$('openLibrary')?.click();return $('library');}},\r\n spotify:{label:'Spotify DJ',dialog:'#spotifyPanel',open:()=>window.myr5Menus?.spotify?.()},\r\n paths:{label:'Workout paths',dialog:'#coachPathPicker',open:()=>window.myr5Menus?.paths?.()},\r\n scoreboard:{label:'Scoreboard',dialog:'#accountPanel',open:()=>panel('account')},\r\n // #148 (Ian 2026-09-23): the customizer's one door is the oval: its ship arrives and the user taps it (ship-intro.mjs\r\n // opens /creature/index.html with the gate the editor checks). So #customize, the X and any link to it land on the arrival.\r\n customize:{label:'War Room base',alias:'world'},\r\n // Ian 26 Sep: the dock's far-left key (still the gear icon) skips the arrival and opens the customizer directly,\r\n // the same admission openCustomizer() gives the oval ship (ship-view.mjs, ship-intro.mjs) — not #customize/War Room.\r\n customizeCoach:{label:'Customize coach',open:()=>openCustomizer()},\r\n meditate:{label:'Meditation',dialog:'.meditation-panel',open(){document.querySelector('.meditation-entry')?.click();return document.querySelector('.meditation-panel');}},\r\n reminders:{label:'Reminders',dialog:'#remindersPanel',open:()=>panel('reminders')},\r\n settings:{label:'Settings',dialog:'#settings',open(){$('openSettings')?.click();return $('settings');}},\r\n ship:{label:'Ship',dialog:'dialog.ship-view',open:()=>window.myr5Menus?.ship?.()},\r\n 'war-room':{label:'War Room base',alias:'world'},\r\n pod:{label:'Training pod',page:true,focus:'#homeScreen',open:hideQuilt},\r\n history:{label:'History',dialog:'#historyPanel',open:()=>panel('history')},\r\n install:{label:'Install',dialog:'#installPanel',open:()=>panel('install')},\r\n // Achievement Vault: reached only through a grimoire secret (portal.mjs door poster); vault-door.mjs builds #vaultPanel.\r\n vault:{label:'Vault',dialog:'#vaultPanel',open:()=>import('./vault/vault-door.mjs').then(m=>m.openVault())},\r\n};\r\n// Old ?panel= deep links (reminder pushes, update emails, recovery) open the same routes.\r\nexport const PANEL_ROUTES={spotify:'spotify',meals:'food',reminders:'reminders',account:'scoreboard',history:'history',install:'install'};\r\nexport function hashRoute(hash=location.hash){const id=hash.slice(1);return Object.hasOwn(ROUTES,id)?id:'';}\r\n\r\nlet active=null,expectBack=false,guardArmed=false,guardHref='',hintTimer=0,routeGeneration=0;\r\nconst pending=new Map();\r\nlet deferredRoute=null;\r\n// The shell blocks routes under a blocking dialog, but World stays reachable from the setup gate: it is usable\r\n// without a workout plan and coach-profile.mjs hides the gate while World is open (R84 behaviour).\r\nfunction routeBlocked(id){const shell=window.myr5Shell;return !!shell?.blocked&&!(shell.dialog?.id==='coachSetupGate'&&(ROUTES[id]?.alias||id)==='world');}\r\n\r\nfunction paint(){\r\n const current=active?active.id:quiltUp()?'portal':'';\r\n window.dispatchEvent(new CustomEvent('myr5:route',{detail:{id:current}})); // page music follows the scene\r\n for(const button of dock()?.querySelectorAll('[data-route]')||[]){if(button.dataset.route===current)button.setAttribute('aria-current','page');else button.removeAttribute('aria-current');}\r\n const centre=dock()?.querySelector('.dock-portal');\r\n if(centre){const onQuilt=quiltUp()&&!active;centre.dataset.homeAction=onQuilt?'workout':'home';const caption=centre.querySelector('span');if(caption){caption.hidden=true;caption.setAttribute('aria-hidden','true');}centre.setAttribute('aria-label',onQuilt?'Open workout controls':'Return to cockpit');centre.title=centre.getAttribute('aria-label');}\r\n window.myr5Shell?.refresh();\r\n}\r\nfunction say(text){\r\n const live=dock()?.querySelector('.dock-live');if(!live)return;\r\n live.textContent='';setTimeout(()=>{live.textContent=text;},60);\r\n}\r\nfunction announce(entry){\r\n const route=ROUTES[entry.id],target=entry.dialog?[...entry.dialog.querySelectorAll('h1,h2')].find(h=>h.getClientRects().length):document.querySelector(route.focus);\r\n if(target){\r\n  if(!target.hasAttribute('tabindex'))target.setAttribute('tabindex','-1');\r\n  target.focus({preventScroll:true});\r\n  // A #hash that is also an element id (#settings is the Settings dialog's) gets the browser's fragment focus after\r\n  // popstate; take it back once the navigation is done.\r\n  setTimeout(()=>{const now=document.activeElement;if(active===entry&&(now===document.body||now===entry.dialog))target.focus({preventScroll:true});});\r\n }\r\n say(route.label);\r\n}\r\n// Home is the quilt: after a back (or closing a cold deep link) bring it up unless something else now owns the screen.\r\nfunction showQuiltLater(force=false){\r\n setTimeout(()=>{\r\n  const body=document.body.dataset;\r\n  const idle=()=>!quiltUp()&&!document.querySelector('dialog[open]')&&body.tracking!=='true'&&body.cameraWorkout!=='true'&&body.screen!=='rest';\r\n  if(idle()&&(force||window.coachPlan||window.myr5Portal))void window.myr5Menus?.portal?.({shouldShow:idle});\r\n });\r\n}\r\n\r\nfunction adopt(id,dialog){\r\n if(active&&active.dialog===dialog&&active.id===id)return active;\r\n const route=ROUTES[id],flags=pending.get(id)||{};pending.delete(id);\r\n const prev=active,entry={id,dialog,own:!!route.own,pushed:false,deepLink:!!flags.deepLink,portalOrigin:!!flags.portal,reason:''};\r\n active=entry;\r\n if(prev)finish(prev,'switch');\r\n if(!entry.own){\r\n  const state={myr5Route:id},url=bare()+'#'+id;\r\n  // Switching from one route to the next replaces its history entry, so back always lands on the quilt.\r\n  if(location.hash==='#'+id){entry.pushed=!!flags.fromHash&&!entry.deepLink;history.replaceState(state,'',url);}\r\n  else if(prev?.pushed&&!prev.own){entry.pushed=true;history.replaceState(state,'',url);}\r\n  else if(entry.deepLink)history.replaceState(state,'',url);\r\n  else{entry.pushed=true;history.pushState(state,'',url);}\r\n }\r\n if(dialog){\r\n  dialog.dataset.route=id;\r\n  const shell=initShell();if(shell)shell.adoptDialog(dialog);else {const bar=dock();if(bar)dialog.append(bar);}\r\n  entry.closeListener=()=>{\r\n   // A native close event can arrive after this shared dialog was reopened in the same task.\r\n   // Keep this session's listener armed for its eventual real close.\r\n   if(dialog.open||entry.done)return;\r\n   dialog.removeEventListener('close',entry.closeListener);\r\n   const reason=dialog.dataset.workoutBegin==='true'?'workout-begin':entry.reason||'user';delete dialog.dataset.workoutBegin;\r\n   finish(entry,reason);\r\n  };\r\n  dialog.addEventListener('close',entry.closeListener);\r\n }\r\n if(id==='history')import('./vault/vault-store.mjs').then(m=>m.bump('history-open')).catch(()=>{});\r\n window.myr5MenuLifecycle?.enter(dialog??id);\r\n if(dialog)window.myr5MenuLifecycle?.closeInactive(dialog);\r\n paint();announce(entry);return entry;\r\n}\r\nfunction finish(entry,reason){\r\n if(entry.done)return;\r\n entry.done=true;entry.reason=reason;\r\n window.myr5MenuLifecycle?.leave(entry.dialog??entry.id,reason);\r\n window.dispatchEvent(new CustomEvent('myr5:route-leave',{detail:{id:entry.id,reason}}));\r\n if(entry.dialog&&entry.closeListener)entry.dialog.removeEventListener('close',entry.closeListener);\r\n if(active===entry)active=null;\r\n const {dialog}=entry;\r\n if(dialog&&dialog!==active?.dialog){\r\n  delete dialog.dataset.route;\r\n  const bar=dock();if(!window.myr5Shell&&bar?.parentNode===dialog)(active?.dialog||document.body).append(bar);\r\n  if(dialog.open)dialog.close();window.myr5Shell?.refresh();\r\n }\r\n if(!entry.own&&(reason==='user'||reason==='portal'||reason==='workout-begin')&&location.hash==='#'+entry.id){\r\n  if(entry.pushed){expectBack=true;history.back();}else history.replaceState(null,'',bare());\r\n }\r\n if(reason==='back'||(reason==='user'&&entry.deepLink))showQuiltLater();\r\n paint();\r\n}\r\n\r\n// Any direct dialog route gets the same hardware surround and tilt as a room opened from the quilt.\r\n// Portal-origin routes carry {portal:true}, so their shaped-cut presentation stays intact.\r\nconst HOUSING_ROUTES=new Set(['food','achievements','battlepass','library','spotify','paths','reminders','scoreboard','meditate','settings','ship','select','history','install']);\r\nexport function go(id,{deepLink=false,fromHash=false,portal=false}={}){\r\n if(routeBlocked(id)){deferredRoute={id,options:{deepLink,fromHash,portal}};return undefined;}\r\n deferredRoute=null;\r\n const route=ROUTES[id];if(!route)return undefined;\r\n if(route.alias)return go(route.alias,{deepLink,fromHash,portal});\r\n if(route.nav){\r\n  if(active)active.reason='nav';\r\n  if(hashRoute())history.replaceState(history.state?.myr5Home?history.state:null,'',bare());\r\n  location.assign(route.nav);return undefined;\r\n }\r\n if(active?.id===id&&(!active.dialog||active.dialog.open)){\r\n  const current=active;\r\n  if(current.dialog&&HOUSING_ROUTES.has(id)&&!portal)return ensurePortalMounted().then(housing=>{if(active===current&&current.dialog.open)housing.frameDirectDestination?.(current.dialog,id);return current.dialog;});\r\n  return active.dialog||undefined;\r\n }\r\n const generation=++routeGeneration;pending.clear();\r\n const request={deepLink,fromHash,portal,generation};pending.set(id,request);\r\n let result;\r\n try{\r\n  result=HOUSING_ROUTES.has(id)&&!portal\r\n   ?(async()=>{\r\n     const housing=await ensurePortalMounted();if(pending.get(id)!==request||routeGeneration!==generation)return null;\r\n     const opened=route.open();\r\n     const finishHousing=dialog=>{if(dialog instanceof HTMLDialogElement&&dialog.open&&routeGeneration===generation&&(pending.get(id)===request||(active?.id===id&&active.dialog===dialog)))housing.frameDirectDestination?.(dialog,id);return dialog;};\r\n     return opened instanceof Promise?finishHousing(await opened):finishHousing(opened);\r\n    })()\r\n   :route.open();\r\n }catch(error){pending.delete(id);throw error;}\r\n // Only this request may adopt: an older, slower open of the same route (the ship view's) must not take it back.\r\n const settle=dialog=>{\r\n  if(pending.get(id)!==request)return;\r\n  if(dialog instanceof HTMLDialogElement&&dialog.open)adopt(id,dialog);\r\n  else if(route.page&&!(result instanceof Promise))adopt(id,null);\r\n  else pending.delete(id);\r\n };\r\n if(result instanceof Promise)result.then(settle,()=>{if(pending.get(id)===request)pending.delete(id);});else settle(result);\r\n return result;\r\n}\r\n\r\n// Back/forward and #links. popstate covers traversal (and fragment links in current browsers); hashchange is the\r\n// fallback for a link that fired no popstate.\r\nfunction sync(event){\r\n if(routeBlocked(hashRoute())){const id=hashRoute();deferredRoute=id?{id,options:{fromHash:true}}:null;return;}\r\n if(event.type==='popstate'&&expectBack){expectBack=false;return;}\r\n const id=hashRoute(),state=event.state??history.state;\r\n if(active){\r\n  if(active.id!==id){if(active.own)active.reason='back';else finish(active,'back');}\r\n  return;\r\n }\r\n if(state?.myr5Route&&ROUTES[state.myr5Route]){go(state.myr5Route,{fromHash:true});return;}\r\n if(event.type==='popstate'&&guardArmed&&!state?.myr5Home&&location.href===guardHref){\r\n  // The first back on the quilt lands here instead of leaving the app; the next one leaves.\r\n  guardArmed=false;\r\n  const bar=dock();\r\n  if(bar){bar.dataset.hint='Press back again to leave Coach';clearTimeout(hintTimer);hintTimer=setTimeout(()=>delete bar.dataset.hint,3000);}\r\n  say('Press back again to leave Coach');showQuiltLater();return;\r\n }\r\n // A route already opening is the same request (#customize rewrites itself to #select before its hashchange lands).\r\n if(id&&!pending.has(id))go(id,{fromHash:true});\r\n}\r\n// The back guard is pushed only after a tap or key (browsers skip history entries added without one).\r\nfunction armGuard(){\r\n if(history.state?.myr5Home){guardArmed=true;return;}\r\n if(guardArmed||active)return;\r\n guardHref=location.href;history.pushState({myr5Home:true},'',guardHref);guardArmed=true;\r\n}\r\n// Closes whatever route is open and brings up the quilt. Used by the bar's centre Portal off the quilt, and by\r\n// anything else that wants to send the user home (#56's rest exit, a stopped/paused set, the Settings PORTAL link).\r\nexport function home(){\r\n if(window.myr5Shell?.blocked){deferredRoute={home:true};return;}\r\n deferredRoute=null;\r\n routeGeneration++;pending.clear();\r\n if(active){const entry=active;if(entry.dialog?.open)entry.reason='portal';finish(entry,'portal');}\r\n showQuiltLater(true);\r\n}\r\n// The bar's centre Portal: on the quilt the portal's own handler opens the Menu sheet; anywhere else it goes\r\n// home to the quilt (closing the open route).\r\nfunction dockRoute(event){\r\nconst button=event.target.closest?.('#coachDock [data-route]');\r\n if(!button||button.dataset.route==='portal')return;\r\n // Route.open() may activate the same panel button as its legacy opener. The opener's\r\n // request owns this click; adopting it again would invalidate its housing continuation.\r\n if(button.hasAttribute('data-panel')&&pending.has(button.dataset.route))return;\r\n go(button.dataset.route);\r\n}\r\nfunction portalButton(event){\r\n if(!event.target.closest?.('#coachDock [data-route=\"portal\"]'))return;\r\n if(quiltUp()&&!active)return;\r\n event.stopPropagation();\r\n home();\r\n}\r\n\r\nexport function mountRoutes(){\r\n initShell();\r\n if(window.myr5Routes)return window.myr5Routes;\r\n import('./vault/vault-banner.mjs').catch(()=>{}).then(()=>import('./vault/vault-store.mjs')).catch(()=>{}); // banner first so it hears the drained-pending earns; loads the store so it hears myr5:vault-bump from raw-served files (portal, pond)\r\n let seen='';\r\n addEventListener('myr5:shell-navigation',event=>{if(!event.detail?.blocked&&deferredRoute){const request=deferredRoute;deferredRoute=null;queueMicrotask(()=>{if(request.home)home();else go(request.id,request.options);});}});\r\n addEventListener('popstate',event=>{seen=location.href;sync(event);});\r\n addEventListener('hashchange',()=>{const href=location.href,handled=href===seen;seen='';if(!handled)sync({type:'hashchange',state:history.state});});\r\n addEventListener('pointerup',armGuard,true);addEventListener('keydown',armGuard,true);\r\n document.addEventListener('click',portalButton,true);\r\n document.addEventListener('click',dockRoute);\r\n // Legacy War Room doors enter the base; the HQ explicitly enters its editor.\r\n document.addEventListener('click',event=>{const link=event.target.closest?.('a[href]');if(!link||event.defaultPrevented||event.button||event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)return;const url=new URL(link.href,location.href);if(url.pathname.startsWith('/war-room/')&&url.searchParams.get('from')==='world')return;if(url.origin===location.origin&&/^\\/(?:creature|war-room)\\/(index\\.html)?$/.test(url.pathname)){event.preventDefault();go('world');}});\r\n new MutationObserver(records=>{\r\n  const newlyOpen=new Set();let homeChanged=false;\r\n  for(const {target,attributeName} of records){\r\n   if(attributeName==='open'&&target.tagName==='DIALOG'&&target.open)newlyOpen.add(target);\r\n   else if(attributeName==='hidden'&&target.id==='portalHome')homeChanged=true;\r\n  }\r\n  for(const target of newlyOpen){\r\n   const owns=key=>ROUTES[key].dialog&&target.matches(ROUTES[key].dialog);\r\n   // A shared dialog can represent Ship or Coach arrival. Preserve the alias already adopted for\r\n   // this open session; a second record from close/reopen must not fall back to the first alias.\r\n   const current=active?.dialog===target&&owns(active.id)?active.id:null;\r\n   const id=[...pending.keys()].reverse().find(owns)||current||Object.keys(ROUTES).find(key=>!ROUTES[key].shared&&owns(key));\r\n   if(id){\r\n    const request=pending.get(id),generation=request?.generation??routeGeneration;\r\n    const entry=adopt(id,target);\r\n    if(HOUSING_ROUTES.has(id)&&!entry.portalOrigin){\r\n     void ensurePortalMounted().then(housing=>{if(active===entry&&target.open&&routeGeneration===generation)housing.frameDirectDestination?.(target,id);});\r\n    }\r\n   }\r\n  }\r\n  if(homeChanged){\r\n   if(quiltUp()&&active&&!active.dialog)finish(active,'user');\r\n   paint();\r\n  }\r\n }).observe(document.body,{subtree:true,attributes:true,attributeFilter:['open','hidden']});\r\n return window.myr5Routes={\r\n  go,home,ROUTES,hashRoute,\r\n  current:()=>active?.id||'',\r\n  // launch.mjs calls this once the panels exist: a ?panel= or #route deep link opens its route.\r\n  boot(panelName){const id=PANEL_ROUTES[panelName]||hashRoute();if(id&&id!=='pod')go(id,{deepLink:true});else paint();},\r\n };\r\n}\r\n","modules/shell/game-phase.mjs":"/** Explicit gameplay leases. Tracking, camera ownership and renderer mounts are never authority. */\r\nconst PHASES=new Set(['loading','active','paused','closed','error','ready','results','rest']);\r\nconst SOURCES=new Set(['runner','cover','plank-flight','dance','pyramid-jump','tub-flight']);\r\nconst CONTROLLER=Symbol.for('myr5.shell.gamePhases');\r\n\r\nexport function createGamePhaseController(doc=globalThis.document,{onChange=()=>{}}={}){\r\n const records=new Map(),tombstones=new Set();let disposed=false,active=false,serial=0,suspended=false;\r\n const connected=root=>root?.isConnected&&root.ownerDocument===doc;\r\n const visible=root=>{\r\n  if(!connected(root)||root.hidden||root.closest?.('[hidden],dialog:not([open])'))return false;\r\n  if(root.getClientRects&&!root.getClientRects().length)return false;\r\n  const style=doc.defaultView?.getComputedStyle?.(root);\r\n  return !style||style.display!=='none'&&!['hidden','collapse'].includes(style.visibility)&&Number(style.opacity)!==0;\r\n };\r\n const alive=record=>connected(record.root)&&(typeof record.key!=='string'||record.root.dataset[record.flight?'flightInstance':'gameInstance']===record.key.slice(record.key.indexOf(':')+1))&&(!record.flight||record.world===record.root.closest('#coachWorldPanel')&&record.world?.open&&record.world.worldOpenId===record.worldId);\r\n const current=record=>{\r\n  if(!alive(record)||!visible(record.root))return false;\r\n  if(record.flight){\r\n   // Use the live controller when available; data-busy is a frame-late HUD mirror.\r\n   const state=record.root.__flight?.state?.();\r\n   if(state?.busy||state?.docked||record.root.dataset.docked==='1'||record.root.querySelector?.('[data-flight-menu]')?.hidden===false)return false;\r\n  }\r\n  const shellDialog=doc.defaultView?.myr5Shell?.dialog;\r\n  const isModal=d=>{try{return d?.open&&d.matches(':modal');}catch{return false;}};\r\n  const top=shellDialog?.open?shellDialog:[...doc.querySelectorAll('dialog[open]')].filter(isModal).at(-1);\r\n  return !top||top===record.root||top.contains(record.root);\r\n };\r\n const publish=()=>{\r\n  const live=doc.hidden||suspended?[]:[...records.values()].filter(r=>r.phase==='active'&&current(r)),next=live.length>0;\r\n  // World Fly is flown from inside the cockpit: its lease keeps the shell frame (Ian, 19 Oct).\r\n  const framed=String(next&&live.every(r=>r.flight));\r\n  if(doc.documentElement.dataset.gameFramed!==framed)doc.documentElement.dataset.gameFramed=framed;\r\n  if(next!==active){active=next;doc.documentElement.dataset.gameActive=String(active);onChange(active);}\r\n  else if(doc.documentElement.dataset.gameActive!==String(active))doc.documentElement.dataset.gameActive=String(active);\r\n };\r\n const revoke=record=>{if(!record)return;record.phase='closed';records.delete(record.key);tombstones.add(record.key);};\r\n const prune=()=>{for(const r of records.values())if(!alive(r))revoke(r);publish();};\r\n const signal=(event,flight=false)=>{\r\n  const d=event.detail||{},source=d.source,key=`${source}:${d.instanceId}`;\r\n  if(disposed||!PHASES.has(d.phase)||typeof d.instanceId!=='string'||!d.instanceId||!(flight?source==='coach-flight':SOURCES.has(source)))return;\r\n  if(d.phase==='closed'){revoke(records.get(key));tombstones.add(key);publish();return;}\r\n  if(tombstones.has(key)||doc.hidden&&d.phase!=='loading')return;\r\n  const instance=flight?'flightInstance':'gameInstance',phase=flight?'flightPhase':'gamePhase';\r\n  // Compare values rather than interpolating untrusted IDs into a selector.\r\n  const roots=[...doc.querySelectorAll(flight?'[data-flight-instance]':'[data-game-instance]')].filter(r=>r.dataset[instance]===d.instanceId);\r\n  if(roots.length!==1){prune();return;}\r\n  const root=roots[0],world=flight?root.closest('#coachWorldPanel'):null;\r\n  if(!visible(root)||root.dataset[phase]!==d.phase||flight&&(!world?.open||!['city','space','world'].includes(d.mode))){publish();return;}\r\n  const old=records.get(key);\r\n  if(old&&(old.root!==root||flight&&(old.world!==world||old.worldId!==world.worldOpenId))){revoke(old);publish();return;}\r\n  // A new instance starts with loading, never with an unsolicited active message.\r\n  if(!old&&d.phase!=='loading')return;\r\n  // One lifecycle per source. Retired mounts cannot regain the shell on a late callback.\r\n  for(const r of records.values())if(r.source===source&&r.key!==key)revoke(r);\r\n  const record=old||{key,root,source,flight,world,worldId:world?.worldOpenId};\r\n  record.phase=d.phase;records.set(key,record);publish();\r\n };\r\n const game=event=>signal(event),flight=event=>signal(event,true);\r\n const account=()=>{for(const record of records.values())revoke(record);publish();};\r\n const hidden=()=>{if(doc.hidden){for(const r of records.values())if(r.phase==='active')r.phase='paused';}publish();};\r\n const observer=new (doc.defaultView.MutationObserver)(changes=>{\r\n  if(changes.some(c=>c.type==='childList'&&c.removedNodes.length||c.type==='attributes'&&(c.attributeName==='hidden'||c.attributeName==='open'||c.attributeName==='data-game-instance'||c.attributeName==='data-flight-instance')))prune();\r\n });\r\n observer.observe(doc.body,{subtree:true,childList:true,attributes:true,attributeFilter:['hidden','open','data-game-instance','data-flight-instance']});\r\n doc.addEventListener('myr5:game-phase',game);doc.addEventListener('myr5:flight-phase',flight);doc.addEventListener('visibilitychange',hidden);doc.defaultView?.addEventListener?.('myr5:auth-transition',account);\r\n publish();\r\n return {\r\n  get active(){return active;},\r\n  get disposed(){return disposed;},\r\n  /** Direct trusted adapter: owner must be its connected DOM root. Release is generation-safe. */\r\n  set(owner,phase){\r\n   if(disposed||!owner||owner.ownerDocument!==doc||!PHASES.has(phase))return ()=>{};\r\n   const key=owner,previous=records.get(key),generation=++serial;\r\n   if(phase==='closed'){revoke(previous);publish();return ()=>{};}\r\n   if(!visible(owner)||doc.hidden)return ()=>{};\r\n   records.set(key,{key,root:owner,phase,generation,source:owner});publish();\r\n   return ()=>{if(records.get(key)?.generation===generation){records.delete(key);publish();}};\r\n  },\r\n  refresh:prune,\r\n  suspend(value){suspended=!!value;publish();},\r\n  dispose(){if(disposed)return;disposed=true;observer.disconnect();doc.removeEventListener('myr5:game-phase',game);doc.removeEventListener('myr5:flight-phase',flight);doc.removeEventListener('visibilitychange',hidden);doc.defaultView?.removeEventListener?.('myr5:auth-transition',account);records.clear();if(doc[CONTROLLER]===this)delete doc[CONTROLLER];active=false;doc.documentElement.dataset.gameActive='false';doc.documentElement.dataset.gameFramed='false';onChange(false);},\r\n };\r\n}\r\nexport function initGamePhases(doc=globalThis.document,options){let owner=doc[CONTROLLER];if(!owner||owner.disposed){owner=createGamePhaseController(doc,options);doc[CONTROLLER]=owner;}return owner;}\r\nexport function setGamePhase(owner,phase){return initGamePhases(owner?.ownerDocument??globalThis.document).set(owner,phase);}\r\n","modules/shell/owner.mjs":"import {initGamePhases} from './game-phase.mjs';\r\nimport {shellEnabled} from '../visual-flags.mjs';\r\nimport {readLook,applyLookVars,stripSeq,frameHtml,ENERGY,ENERGY_MOVES,energyStops} from '../portal/portal-look.mjs';\r\nconst owners=new WeakMap();\r\nconst svg=(paths)=>`<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${paths}</svg>`;\r\nexport const SHELL_ICONS={home:svg('<path d=\"m4 10 8-6 8 6v10H4z\"/><path d=\"M9 20v-7h6v7\"/>'),service:svg('<path d=\"M4 4h16v16H4zM8 4v16M16 4v16M4 9h16M4 15h16\"/>'),sound:svg('<path d=\"M4 9h4l5-4v14l-5-4H4zM17 8q5 4 0 8\"/>'),close:svg('<path d=\"M6 6l12 12M18 6 6 18\"/>'),grimoire:svg('<path d=\"M5 4h11a3 3 0 0 1 3 3v13H8a3 3 0 0 1-3-3zM5 17a3 3 0 0 1 3-3h11M10 8h5\"/>')};\r\nconst NONBLOCKING=new Set(['dancePanel','coachWorldPanel','portalWorkoutHome','mealsPanel','battlePassPanel','library','spotifyPanel','coachPathPicker','accountPanel','remindersPanel','settings','historyPanel','installPanel','vaultPanel','portalMenu','identity']);\r\n// Dialog close controls the band Exit key can drive (same list shell.css hides while Exit is shown).\r\nconst CLOSERS='[data-close],.ach-close,.pass-close,.sp-close,.cpp-close,#closeSettings,#closeIdentity,#closeLibrary,[data-menu-close]';\r\n// Nodes that belong in the top band: adopted once by controls(), never re-parented by anything else.\r\nconst BAND_SLOT=['.mom-brand','[data-world-entry]','#openSettings','.pod-tilt-request'];\r\nconst BAND_LATE=[...BAND_SLOT,'.cockpit-panel-tools'].join(',');\r\nexport const shellMarkup=()=>`<div class=\"shell-metal\" aria-hidden=\"true\">${frameHtml()}</div><div class=\"shell-band\" role=\"toolbar\" aria-label=\"Ship controls\"><div class=\"ship-header shell-band-pod\"></div><button type=\"button\" class=\"shell-key shell-exit\" hidden aria-label=\"Close\">${SHELL_ICONS.close}</button></div>`;\r\n\r\n/** Mount once, early after body exists. Shell CSS is also a standalone, no-JS substrate. */\r\nexport function initShell({doc=globalThis.document,loadStyles=true}={}){\r\n if(!doc?.body)return null;\r\n if(!shellEnabled(doc.defaultView))return null;\r\n if(owners.has(doc)){const existing=owners.get(doc);if(!existing.disposed){existing.refresh();return existing;}}\r\n // Raw early bootstrap and bundled route code must share the same runtime owner.\r\n const shared=doc.defaultView?.myr5Shell;\r\n if(shared?.el?.ownerDocument===doc&&!shared.disposed){shared.refresh();return shared;}\r\n const win=doc.defaultView;\r\n const path=win.location?.pathname||'';\r\n if(/^\\/(?:signin|onboarding|install|recover|about|privacy)(?:\\.html)?$/.test(path)||path==='/audio/sfx/credits.html'||path==='/handborne/unlocks.html')doc.querySelector('main')?.classList.add('shell-content');\r\n if(loadStyles&&!doc.querySelector('link[data-myr5-shell],link[href$=\"/modules/shell/shell.css\"],link[href^=\"/app.css\"]')){const css=doc.createElement('link');css.rel='stylesheet';css.href='/modules/shell/shell.css';css.dataset.myr5Shell='';doc.head.append(css);}\r\n let shell=doc.getElementById('myr5Shell');if(!shell){shell=doc.createElement('div');shell.id='myr5Shell';shell.innerHTML=shellMarkup();doc.body.append(shell);}\r\n shell.removeAttribute('aria-hidden');shell.classList.add('myr5-shell');\r\n // Strip/metal vars exist on every page (the portal may never mount); the channel follows the saved strip colour live.\r\n const paint=()=>{applyLookVars(doc.documentElement,readLook());const ch=shell.querySelector('.portal-energy');if(ch){ch.style.setProperty('--energy',energyStops(stripSeq(readLook().strip)));ch.style.setProperty('--energy-px',ENERGY.px+'px');}};\r\n paint();\r\n // The shell's own four channel animations, same keyframes as the portal's, so the stripe flows on every route.\r\n const energyAnims=[];\r\n if(!win.matchMedia?.('(prefers-reduced-motion: reduce)').matches)[...(shell.querySelector('.portal-energy')?.children||[])].forEach((channel,i)=>{const [axis,dir]=ENERGY_MOVES[i],from=`translate${axis}(${-ENERGY.px*(dir>0)}px)`,to=`translate${axis}(${-ENERGY.px*(dir<0)}px)`;energyAnims.push(channel.firstElementChild.animate([{transform:from},{transform:to}],{duration:ENERGY.loopMs,iterations:Infinity}));});\r\n const onLook=e=>{if(e.type!=='storage'||!e.key||e.key.startsWith('myr5.portal'))paint();};\r\n win.addEventListener('myr5:look',onLook);win.addEventListener('storage',onLook);\r\n const stack=[],savedDisabled=new Map(),hooked=new Map();let disposed=false,blocked=false,phases,foregroundDialog=null;\r\n const frame=dialog=>{\r\n  if(!dialog?.open||dialog.ownerDocument!==doc||!dialog.isConnected)return;\r\n  dialog.classList.add('shell-dialog');\r\n  if(!stack.includes(dialog))stack.push(dialog);\r\n  place();\r\n };\r\n const block=dialog=>!!dialog&&(dialog.dataset.shellNavigation==='block'||!(dialog.dataset.shellNavigation==='allow'||dialog.dataset.route||NONBLOCKING.has(dialog.id)||dialog.matches('.ship-view,.meditation-panel')));\r\n function controls(){\r\n  const dock=doc.getElementById('coachDock');\r\n  if(dock&&dock.parentNode!==shell)shell.append(dock);\r\n  dock?.classList.add('ship-control-deck');\r\n  const band=shell.querySelector('.shell-band'),slot=band?.querySelector('.shell-band-pod');\r\n  if(slot){\r\n   for(const sel of BAND_SLOT){const n=doc.querySelector(`.ship-header:not(.shell-band-pod) ${sel}`);if(n)slot.append(n);}\r\n   const tools=doc.querySelector('.cockpit-panel-tools');if(tools&&tools.parentNode!==band)band.insertBefore(tools,band.querySelector('.shell-exit'));\r\n  }\r\n  const center=dock?.querySelector('.dock-portal');\r\n  if(center){const action=center.dataset.homeAction||'home';let icon=center.querySelector(':scope > svg');if(!icon||center.dataset.shellIcon!==action){icon?.remove();center.insertAdjacentHTML('afterbegin',action==='workout'?SHELL_ICONS.service:SHELL_ICONS.home);center.dataset.shellIcon=action;}center.querySelectorAll('span').forEach(n=>{n.hidden=true;n.setAttribute('aria-hidden','true');});}\r\n  for(const button of dock?.querySelectorAll('button,a')||[]){\r\n   if(blocked){if(!savedDisabled.has(button))savedDisabled.set(button,{disabled:button.disabled,aria:button.getAttribute('aria-disabled'),title:button.getAttribute('title')});if(button.tagName==='BUTTON')button.disabled=true;button.setAttribute('aria-disabled','true');button.title=doc.querySelector('[open][data-shell-navigation=\"block\"]')?.getAttribute('aria-label')||'Finish the open dialog to navigate';}\r\n   else if(savedDisabled.has(button)){const old=savedDisabled.get(button);if(button.tagName==='BUTTON')button.disabled=old.disabled;old.aria===null?button.removeAttribute('aria-disabled'):button.setAttribute('aria-disabled',old.aria);old.title===null?button.removeAttribute('title'):button.setAttribute('title',old.title);savedDisabled.delete(button);}\r\n  }\r\n }\r\n function place(){\r\n  if(disposed)return;\r\n  for(let i=stack.length-1;i>=0;i--)if(!stack[i].open||!stack[i].isConnected)stack.splice(i,1);\r\n  const dialog=[...stack].reverse().find(d=>{try{return d.matches(':modal');}catch{return false;}})||stack.at(-1),target=dialog||doc.body;\r\n  foregroundDialog=dialog||null;\r\n  if(shell.parentNode!==target)target.append(shell);\r\n  const previousBlocked=blocked;blocked=block(dialog);phases?.suspend(blocked);shell.dataset.navigation=blocked?'blocked':'ready';\r\n  controls();\r\n  const exit=shell.querySelector('.shell-exit'),closer=!blocked&&dialog&&dialog.id!=='portalWorkoutHome'?dialog.querySelector(CLOSERS):null;\r\n  for(const d of doc.querySelectorAll('[data-shell-exit]'))if(d!==dialog||!closer)delete d.dataset.shellExit;\r\n  if(exit){exit.hidden=!closer;if(closer)dialog.dataset.shellExit='';}\r\n  // The rails are in the current native modal's top layer, rather than an inert popover.\r\n  shell.hidden=false;doc.documentElement.classList.add('shell-ready');\r\n  if(previousBlocked!==blocked)win.dispatchEvent(new win.CustomEvent('myr5:shell-navigation',{detail:{blocked}}));\r\n }\r\n const proto=win.HTMLDialogElement?.prototype;\r\n if(proto)for(const name of ['showModal','show','close']){\r\n  const original=proto[name];if(typeof original!=='function')continue;\r\n  const wrapper=function(...args){const result=original.apply(this,args);if(this.ownerDocument===doc){if(this.open)frame(this);else place();}return result;};\r\n  hooked.set(name,{original,wrapper});proto[name]=wrapper;\r\n }\r\n const close=()=>place();doc.addEventListener('close',close,true);\r\n const observer=new win.MutationObserver(records=>{\r\n  let changed=false;\r\n  for(const r of records){if(r.type==='attributes'&&r.target.tagName==='DIALOG'){if(r.target.open)frame(r.target);changed=true;}\r\n   else if(r.type==='childList'&&([...r.addedNodes].some(n=>n.id==='coachDock'||n===shell)||[...r.addedNodes].some(n=>n.nodeType===1&&(n.matches(BAND_LATE)||n.querySelector(BAND_LATE)||(foregroundDialog&&foregroundDialog.contains(n)&&(n.matches(CLOSERS)||n.querySelector(CLOSERS)))))||[...r.removedNodes].some(n=>n.nodeType===1&&(n===shell||n.contains(shell)||stack.some(d=>n===d||n.contains(d))))))changed=true;}\r\n  if(changed)place();\r\n });\r\n observer.observe(doc.body,{subtree:true,childList:true,attributes:true,attributeFilter:['open']});\r\n const guard=event=>{if(blocked&&event.target.closest?.('#myr5Shell [data-route],#myr5Shell a')){event.preventDefault();event.stopImmediatePropagation();}};\r\n doc.addEventListener('click',guard,true);\r\n const exitClick=e=>{const key=e.target.closest?.('#myr5Shell .shell-exit');if(!key)return;const d=foregroundDialog;(d?.querySelector(CLOSERS)||{click:()=>d?.close()}).click();};\r\n doc.addEventListener('click',exitClick);\r\n const route=()=>place();win.addEventListener('myr5:route',route);win.addEventListener('myr5:shell-refresh',route);\r\n phases=initGamePhases(doc);\r\n const owner={el:shell,energyAnims,get disposed(){return disposed;},adoptDialog:frame,refresh:place,get blocked(){return blocked;},get dialog(){return foregroundDialog;},dispose(){if(disposed)return;blocked=false;controls();disposed=true;energyAnims.forEach(a=>a.cancel());win.removeEventListener('myr5:look',onLook);win.removeEventListener('storage',onLook);observer.disconnect();doc.removeEventListener('close',close,true);doc.removeEventListener('click',guard,true);doc.removeEventListener('click',exitClick);win.removeEventListener('myr5:route',route);win.removeEventListener('myr5:shell-refresh',route);for(const [name,{original,wrapper}]of hooked)if(proto[name]===wrapper)proto[name]=original;const dock=doc.getElementById('coachDock');if(dock&&shell.contains(dock))doc.body.append(dock);shell.remove();phases.dispose();doc.documentElement.classList.remove('shell-ready');owners.delete(doc);if(win.myr5Shell===owner)delete win.myr5Shell;}};\r\n owners.set(doc,owner);win.myr5Shell=owner;\r\n for(const dialog of doc.querySelectorAll('dialog[open]'))frame(dialog);\r\n place();return owner;\r\n}\r\nexport const mountShell=initShell;\r\n","modules/shell/shell.css":"/* Critical substrate: include this sheet in every first-party document before async startup.\r\n   Geometry is viewport-owned; route scenes never transform the metal or lower controls. */\r\n:root{--portal-strip:#b026ff;--strip-dim:#e3b3ff;--shell-rail:15px;--shell-top:calc(52px + env(safe-area-inset-top,0px));--shell-left:calc(15px + env(safe-area-inset-left,0px));--shell-right:calc(15px + env(safe-area-inset-right,0px));--shell-dock:calc(80px + env(safe-area-inset-bottom,0px));--shell-bottom:calc(var(--shell-dock) + 15px);--myr5-bar:var(--shell-bottom);--shell-metal:var(--portal-metal,#4d3d4f);--shell-light:#c9d1c7;--shell-dark:#1b242e;--shell-well-height:calc(100dvh - var(--shell-top) - var(--shell-bottom))}\r\nhtml{scroll-padding-block:var(--shell-top) var(--shell-bottom)}\r\n/* CSS-only rails survive unavailable JavaScript, WebGL, auth and pack loading. */\r\nhtml:not(.shell-ready):not(.shell-disabled)::before{content:'';position:fixed;inset:0;z-index:2147483000;box-sizing:border-box;pointer-events:none;border-style:ridge;border-color:#4d3d4f;border-width:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left);box-shadow:inset 0 0 0 2px #141e26,inset 0 5px 10px #000a}\r\nhtml:not(.shell-ready):not(.shell-disabled) body{padding:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left);box-sizing:border-box;min-height:100dvh}\r\n.myr5-shell#myr5Shell{position:fixed!important;inset:0!important;width:100vw!important;height:100dvh!important;max-width:none!important;max-height:none!important;box-sizing:border-box!important;margin:0!important;padding:0!important;z-index:2147483000!important;pointer-events:none!important;transform:none!important;translate:none!important;zoom:1!important;visibility:visible!important;clip-path:none!important;overflow:visible!important;contain:none!important;isolation:isolate;touch-action:auto}\r\n.shell-metal{position:absolute;inset:0;pointer-events:none;filter:drop-shadow(0 2px 3px #0009)}\r\n/* Portal frame recipe: copied from modules/portal/portal.css (:12 shades, :15 box, :16 merged with :221, :18 lip, :20-21 bolts, :25-36 energy),\r\n   every selector prefixed #myr5Shell and re-pointed at the shell well (shell vars instead of the face, cap, side and myr5-bar vars). Markup: portal-look.mjs frameHtml(). */\r\n#myr5Shell{--portal-rail:15px;--pm:var(--portal-metal,#4d3d4f)}\r\n#myr5Shell .portal-frame{--mh:color-mix(in srgb,var(--pm) 62%,#fff);--md:color-mix(in srgb,var(--pm) 75%,#000);--mx:color-mix(in srgb,var(--pm) 55%,#000);--me:color-mix(in srgb,var(--pm) 90%,#fff)}\r\n#myr5Shell .portal-frame{--screw:#cbbab0 0 1.4px,#57474f 1.9px 3px,#0000 3.5px;position:absolute;z-index:1;box-sizing:border-box;left:var(--shell-left);top:var(--shell-top);width:calc(100vw - var(--shell-left) - var(--shell-right));height:var(--shell-well-height);border-radius:3px;pointer-events:none;box-shadow:0 0 0 calc(var(--portal-rail) + 1px) #0e0a12,0 8px 16px calc(var(--portal-rail) - 2px) #000b}\r\n#myr5Shell .portal-frame:before{content:'';position:absolute;top:calc(-1*var(--shell-top));left:calc(-1*var(--shell-left));right:calc(-1*var(--shell-right));bottom:calc(-1*var(--shell-bottom));padding:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left);border-radius:calc(var(--portal-rail)/2 + 3px);background:repeating-linear-gradient(0deg,#0000 0 25px,#0b071966 26px,#d3bed92e 27px,#0000 29px),repeating-linear-gradient(90deg,#0000 0 3px,#ffffff06 3px 4px),linear-gradient(160deg,var(--mh,#7e6a7c),var(--pm,#4d3d4f) 26%,var(--md,#3b2f42) 52%,var(--mx,#2b2132) 78%,var(--me,#564458));box-shadow:inset 0 1px #e6d2da99,inset 1px 0 #d6c0cc44,inset 0 -2px #000a,inset -1px 0 #0007;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}\r\n#myr5Shell .portal-frame:after{content:'';position:absolute;inset:0;border-radius:inherit;box-shadow:0 0 0 1px #0e0a12,0 0 0 2.5px #bba4b355,inset 0 4px 6px #000b,inset 3px 0 5px #0007,inset -2px 0 4px #0006,inset 0 -2px 4px #0007}\r\n#myr5Shell .portal-frame i{position:absolute;width:10px;height:10px;margin:-5px;left:calc(var(--x)*(100% + var(--portal-rail)) - var(--portal-rail)/2);top:calc(var(--y)*(100% + var(--portal-rail)) - var(--portal-rail)/2);border-radius:50%;background:radial-gradient(circle at 50% 40%,#3a2d3e,#150f19 70%);box-shadow:0 1px 1px #000a,0 -1px #e6d2da40}\r\n#myr5Shell .portal-frame i:before{content:'';position:absolute;inset:1px;clip-path:polygon(25% 6%,75% 6%,100% 50%,75% 94%,25% 94%,0 50%);background:radial-gradient(circle at 34% 28%,#f4e8dd,#bca6b2 32%,#6d5a6b 68%,#3a2d3e)}\r\n#myr5Shell .portal-energy{position:absolute;inset:calc(var(--portal-rail)/-2);opacity:.55;transition:opacity .4s}\r\n#myr5Shell .portal-energy.hot{opacity:1}\r\n#myr5Shell .portal-energy>span{position:absolute;overflow:hidden;border-radius:2px;background:#0d0a10;box-shadow:0 0 0 1px #0000004d,0 0 5px var(--portal-strip-glow,#b026ff40)}\r\n#myr5Shell .portal-energy>span:nth-child(odd){left:0;right:0;height:4px}\r\n#myr5Shell .portal-energy>span:nth-child(1){top:-2px}\r\n#myr5Shell .portal-energy>span:nth-child(3){bottom:-2px}\r\n#myr5Shell .portal-energy>span:nth-child(even){top:0;bottom:0;width:4px}\r\n#myr5Shell .portal-energy>span:nth-child(2){right:-2px}\r\n#myr5Shell .portal-energy>span:nth-child(4){left:-2px}\r\n#myr5Shell .portal-energy>span>span{position:absolute;left:0;top:0;background:repeating-linear-gradient(var(--dir),var(--energy));box-shadow:inset 0 0 1px 1px #0d0a10b0}\r\n#myr5Shell .portal-energy>span:nth-child(odd)>span{--dir:90deg;height:100%;width:calc(100% + var(--energy-px))}\r\n#myr5Shell .portal-energy>span:nth-child(even)>span{--dir:180deg;width:100%;height:calc(100% + var(--energy-px))}\r\n/* All native modals retain focus/inert/close semantics; only their content well changes. */\r\nhtml.shell-ready dialog.shell-dialog[open]{position:fixed!important;inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important;width:calc(100vw - var(--shell-left) - var(--shell-right))!important;height:var(--shell-well-height)!important;min-width:0!important;min-height:0!important;max-width:none!important;max-height:var(--shell-well-height)!important;margin:0!important;box-sizing:border-box!important;transform:none!important;translate:none!important;scale:none!important;rotate:none!important;clip-path:none!important;contain:none!important;border-radius:3px!important;overflow:auto;overscroll-behavior:contain;scroll-padding:12px;touch-action:pan-x pan-y}\r\nhtml.shell-ready dialog.shell-dialog::backdrop{background:#111a24c9!important;backdrop-filter:none!important}\r\nhtml.shell-ready dialog.shell-dialog>:not(#myr5Shell):not(.portal-peer-ui){max-width:100%;box-sizing:border-box;zoom:1!important;visibility:visible;translate:none!important}\r\nhtml.shell-ready dialog.shell-dialog :is(form,fieldset,section,.entry,.actions){min-width:0;overflow-wrap:anywhere}\r\nhtml.shell-ready dialog.shell-dialog :is(input,select,textarea){max-width:100%;box-sizing:border-box}\r\nhtml.shell-ready dialog.shell-dialog :is(button,a,input,select,textarea):focus-visible{outline:3px solid #ffdda0;outline-offset:2px}\r\n/* Existing room-specific layouts remain intact behind one continuous housing. */\r\nhtml.shell-ready :is(#portalHome,#restScreen){position:fixed!important;inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important;width:auto!important;height:auto!important;max-height:var(--shell-well-height)}\r\nhtml.shell-ready #portalBoardHost{--portal-top-band:0px;inset:0!important}\r\nhtml.shell-ready #portalHome .portal-frame,html.shell-ready #portalChrome>.portal-frame{display:none!important}\r\nhtml.shell-ready #portalChrome.portal-standalone .portal-standalone-aura{inset:var(--shell-top) var(--shell-right) 0 var(--shell-left)}\r\nhtml.shell-ready #portalChrome{pointer-events:none!important}\r\nhtml.shell-ready #homeScreen{margin-inline:var(--shell-left) var(--shell-right);padding-top:var(--shell-top);padding-bottom:var(--shell-bottom);box-sizing:border-box}\r\n/* A dialog scene cannot float a fixed action over the hardware. */\r\nhtml.shell-ready dialog.shell-dialog :is(.ach-close,.ach-head,.cw-header){max-width:100%}\r\nhtml.shell-ready dialog.shell-dialog .portal-peer-ui{display:none!important}\r\n/* Dock is physically the lower rail, and stays fixed even when its owner scrolls. */\r\nhtml.shell-ready #myr5Shell #coachDock.coach-dock{position:fixed!important;inset:auto 0 0!important;box-sizing:border-box!important;width:100vw!important;height:var(--shell-dock)!important;max-width:none!important;display:flex!important;align-items:center!important;justify-content:space-around!important;gap:0!important;margin:0!important;padding:4px max(2px,env(safe-area-inset-right)) env(safe-area-inset-bottom) max(2px,env(safe-area-inset-left))!important;transform:none!important;translate:none!important;pointer-events:none!important;overflow:visible!important;visibility:visible!important}\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a).dock-control,html.shell-ready #myr5Shell #coachDock .dock-sound{position:relative!important;box-sizing:border-box;flex:0 0 44px!important;width:44px!important;min-width:44px!important;height:48px!important;min-height:44px!important;padding:0!important;pointer-events:auto;touch-action:manipulation;letter-spacing:0;font-size:0!important}\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a).dock-portal{position:relative!important;inset:auto!important;translate:none!important;flex:0 0 48px!important;width:48px!important;min-width:48px!important;height:48px!important;min-height:44px!important;padding:0!important;pointer-events:auto;touch-action:manipulation;font-size:0!important}\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a)>span:not(.shell-sound-icon){font-size:0!important;min-width:0!important}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-control>span{display:grid;place-items:center}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-control>span svg{width:22px;height:22px;filter:drop-shadow(0 1px #f3ffe588)}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-portal>span{display:none!important;content:none!important}\r\n/* Owner rule (PLAN-r2 §2): the dock carries engraved/lit symbols only. Names stay in aria-label/title. */\r\nhtml.shell-ready #myr5Shell #coachDock :is(.dock-settings,.dock-scoreboard,.dock-food,.dock-achievements,.dock-pod):after,html.shell-ready #myr5Shell #coachDock .dock-sound .cb-label{display:none!important;content:none!important}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-portal>svg{display:block;width:26px;height:26px}\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a):active{transform:translateY(3px)!important;box-shadow:inset 1px 3px 4px #13242a99,0 1px #13232c!important}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-control:is(:active,[aria-current=page])>span{transform:translateY(3px);box-shadow:inset 1px 3px 5px #172c33aa,0 1px #13232c}\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a):focus-visible{outline:3px solid #ffe09a!important;outline-offset:1px!important}\r\nhtml.shell-ready #myr5Shell #coachDock :is(:disabled,[aria-disabled=true]){opacity:.38!important;filter:grayscale(1)!important;cursor:not-allowed!important;transform:none!important}\r\nhtml.shell-ready #myr5Shell #coachDock [aria-busy=true]{animation:shell-pending 1.2s ease-in-out infinite}\r\n@keyframes shell-pending{50%{filter:brightness(1.6)}}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-live{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}\r\n/* The phase owner alone may release the housing. Solo tracking and rest never do. */\r\nhtml[data-game-active=true]:not([data-game-framed=true]) #myr5Shell,html[data-game-active=true]:not([data-game-framed=true])::before{display:none!important}\r\nhtml.shell-ready:is([data-game-active=false],[data-game-framed=true]) #myr5Shell.myr5-shell{display:block!important}\r\nhtml.shell-ready[data-game-active=true]:not([data-game-framed=true]) dialog.shell-dialog[open]{inset:0!important;width:100vw!important;height:100dvh!important;max-height:100dvh!important}\r\n/* Flat service content becomes inset glass, with localized text kept on the display. */\r\nhtml.shell-ready dialog.shell-dialog :is(input:not([type=checkbox],[type=range],[type=radio]),select,textarea){min-height:44px}\r\nhtml.shell-ready dialog.shell-dialog :is(.status,.hint[role=status],.entry-copy,.account-status){overflow-wrap:anywhere}\r\n/* Explicit standalone content host; attach class in documents with an existing layout. */\r\n.shell-content{box-sizing:border-box;position:relative;max-height:var(--shell-well-height);overflow:auto;overscroll-behavior:contain;margin:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left);padding:clamp(12px,3vw,28px);}\r\n.shell-content :is(input,select,textarea,button){max-width:100%;box-sizing:border-box}.shell-content :is(button,a,input,select,textarea):focus-visible{outline:3px solid #ffe09a;outline-offset:2px}\r\nhtml.shell-ready .shell-content{min-height:var(--shell-well-height)}\r\n@media(orientation:landscape) and (max-height:500px){:root{--shell-dock:calc(60px + env(safe-area-inset-bottom,0px));--shell-rail:10px;--shell-top:calc(40px + env(safe-area-inset-top,0px))}}\r\n@media(prefers-reduced-motion:reduce){#myr5Shell *,#myr5Shell *:before,#myr5Shell *:after{animation:none!important;transition:none!important}}\r\n@media(forced-colors:active){#myr5Shell .portal-frame:before,#myr5Shell .portal-frame i{border:1px solid CanvasText;background:Canvas}#myr5Shell button{border:2px solid ButtonText!important}.shell-content{border:3px solid CanvasText}}\r\n/* Common service action hardware; inset copy and destructive meaning stay readable. */\r\nhtml.shell-ready dialog.shell-dialog button:disabled{cursor:not-allowed}\r\nhtml.shell-ready dialog.shell-dialog :is(output,progress,[role=progressbar],.status,[role=alert]){max-width:100%;box-sizing:border-box;overflow-wrap:anywhere}\r\nhtml.shell-ready #portalExitButton{font-size:0!important;width:44px!important;height:44px!important;padding:8px!important}\r\nhtml.shell-ready #portalExitButton svg{display:block;width:24px;height:24px}\r\n/* Renderer mounts cannot cover the stationary rails in load/pause/error phases. */\r\nhtml.shell-ready[data-game-active=false] :is([data-game-instance],[data-flight-instance]):not(dialog),html.shell-ready[data-game-framed=true] [data-flight-instance]:not(dialog){position:fixed!important;inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important;width:auto!important;height:auto!important;max-width:calc(100vw - var(--shell-left) - var(--shell-right))!important;max-height:var(--shell-well-height)!important;z-index:2147482900!important;box-sizing:border-box;overflow:hidden}\r\nhtml.shell-ready dialog.shell-dialog>:not(#myr5Shell):not(.portal-peer-ui){visibility:visible!important}\r\n/* Keep legacy sheets from changing dock reservation after the critical sheet loads. */\r\nhtml:not(.shell-disabled):root{--myr5-bar:var(--shell-bottom)}\r\nhtml.shell-ready .shell-workbench.editor-shell{box-sizing:border-box;width:calc(100vw - var(--shell-left) - var(--shell-right))!important;max-width:none!important;height:var(--shell-well-height)!important;margin:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important;border-radius:3px!important;overflow:hidden}\r\nhtml.shell-ready:has(body>main.shell-content) body{margin:0!important;padding:0!important;display:block!important;min-height:100dvh}\r\nhtml.shell-ready main.shell-content{width:calc(100vw - var(--shell-left) - var(--shell-right))!important;max-width:none!important;height:var(--shell-well-height)!important;min-height:0!important;margin:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important}\r\nhtml.shell-ready .app-update-banner{left:calc(var(--shell-left) + 6px)!important;right:calc(var(--shell-right) + 6px)!important;max-width:calc(100vw - var(--shell-left) - var(--shell-right) - 12px)!important;box-sizing:border-box}\r\n/* The actuator cap travels; the hit region and its mounting socket stay fixed. */\r\nhtml.shell-ready #myr5Shell #coachDock :is(button,a):is(.dock-control,.dock-portal){transform:none!important}\r\nhtml.shell-ready #myr5Shell #coachDock .dock-portal:is(:active,[aria-current=page])>svg{translate:0 3px;filter:drop-shadow(0 1px 3px var(--portal-strip))}\r\n/* World Fly pod: one cockpit. The shell rails are the canopy frame in every flight phase;\r\n   the ship's own thin rails and safe-area offsets give way to the shell's. */\r\nhtml.shell-ready [data-coach-flight] [data-flight-rail]{display:none!important}\r\nhtml.shell-ready [data-coach-flight] [data-flight-top]{top:0!important}\r\nhtml.shell-ready [data-coach-flight] [data-flight-bottom]{bottom:0!important}\r\nhtml.shell-ready [data-coach-flight] [data-flight-launch]{bottom:150px!important}\r\n\r\n/* R3-01 top band: the keys live in the frame's top plate (a child of #myr5Shell like the dock). Cap = portal.css:227 key; glyphs only, names stay in aria-label. */\r\n#myr5Shell{--g-base:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 21V8h3V5h2v3h4V5h2v3h3v13zM10 21v-5h4v5'/%3E%3C/svg%3E\");--g-gear:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6zM12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M18.4 5.6l-2.1 2.1M7.7 16.3l-2.1 2.1'/%3E%3C/svg%3E\");--g-tilt:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 4h8v16H8zM11 17.5h2M4 9q-1.5 3 0 6M20 9q1.5 3 0 6'/%3E%3C/svg%3E\")}\r\n#myr5Shell .shell-band{position:absolute;inset:0 0 auto;height:var(--shell-top);z-index:2;box-sizing:border-box;display:flex;align-items:center;gap:4px;padding:env(safe-area-inset-top,0px) calc(14px + env(safe-area-inset-right,0px)) 0 calc(14px + env(safe-area-inset-left,0px));pointer-events:none}\r\n#myr5Shell .shell-band :is(button,a){pointer-events:auto}\r\n#myr5Shell .shell-band>.ship-header{position:static;display:flex;align-items:center;gap:4px;height:auto;margin:0;padding:0;border:0;background:none;box-shadow:none;backdrop-filter:none}\r\n#myr5Shell .shell-band>.cockpit-panel-tools{display:flex}\r\n#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button,[data-world-entry],.pod-tilt-request){box-sizing:border-box;display:grid;place-items:center;flex:none;width:44px;height:44px;min-width:44px;min-height:44px;margin:0!important;padding:0!important;border:2px outset #ae94a7!important;border-radius:4px;background:linear-gradient(#8b7586,#514055)!important;box-shadow:0 4px #211626,inset 0 1px #dbc1cd66!important;color:var(--strip-dim)!important;font-size:0!important;text-decoration:none;cursor:pointer;touch-action:manipulation}\r\n#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button,[data-world-entry],.pod-tilt-request):active{box-shadow:0 1px #211626!important;translate:0 3px}\r\n#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button):disabled{opacity:.45;cursor:default}\r\n#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button) svg{display:block;width:24px;height:24px}\r\nhtml #myr5Shell .shell-band>:is(.shell-exit,.cockpit-panel-tools){margin-left:auto!important}\r\n#myr5Shell .shell-band :is(button,a):focus-visible{outline:3px solid #ffe09a;outline-offset:2px}\r\n#myr5Shell .shell-band [hidden]{display:none!important}\r\n#myr5Shell .shell-band :is([data-world-entry],.pod-tilt-request)::before{content:'';width:24px;height:24px;background:currentColor;-webkit-mask:var(--g) center/contain no-repeat;mask:var(--g) center/contain no-repeat}\r\n#myr5Shell .shell-band [data-world-entry]{--g:var(--g-base)}\r\n#myr5Shell .shell-band .pod-tilt-request{--g:var(--g-tilt)}\r\n#myr5Shell .shell-band .mom-brand{display:grid;place-items:center;flex:none}\r\n#myr5Shell .shell-band .mom-brand img{width:43px;height:43px}\r\n#myr5Shell .shell-band #openSettings.ship-settings-toggle{flex:none;margin:0;width:119px;min-width:119px;min-height:44px;height:44px;padding:2px 5px}\r\n#myr5Shell .shell-band .ship-settings-screen{place-items:center;height:30px}\r\n#myr5Shell .shell-band .ship-settings-screen>*{display:none}\r\n#myr5Shell .shell-band .ship-settings-screen::before{content:'';width:20px;height:20px;background:#b7ffce;-webkit-mask:var(--g-gear) center/contain no-repeat;mask:var(--g-gear) center/contain no-repeat;filter:drop-shadow(0 0 4px #65eb95)}\r\n#myr5Shell .shell-band>#podPersistentChrome{position:static;inset:auto;flex:1;height:auto;z-index:auto;display:flex;justify-content:space-between;align-items:center}\r\n#myr5Shell .shell-band>#podPersistentChrome>button{max-width:none}\r\n/* Visibility: pod group only on the pod route and in the workout home; the board terminals only on the board; dialogs get Exit alone. */\r\nhtml:not(:has(body[data-screen=pod])) #myr5Shell .shell-band>:is(.shell-band-pod,.cockpit-panel-tools),html:has(#portalHome:not([hidden])):not(:has(#portalWorkoutHome[open])) #myr5Shell .shell-band>:is(.shell-band-pod,.cockpit-panel-tools),dialog:not(#portalWorkoutHome) #myr5Shell .shell-band>:is(.shell-band-pod,.cockpit-panel-tools),dialog:not(#portalWorkoutHome) #myr5Shell .shell-band>#podPersistentChrome{display:none}\r\n/* A dialog's own close control is driven by the band Exit while Exit is shown. It stays in place, in the DOM, focusable and tappable (tests and keyboards keep working); only its paint goes, and returns on focus. */\r\nhtml.shell-ready dialog[data-shell-exit] :is([data-close],.ach-close,.pass-close,.sp-close,.cpp-close,#closeSettings,#closeIdentity,#closeLibrary,[data-menu-close]):not(:focus-visible){opacity:0}\r\n@media(max-width:374px){#myr5Shell .shell-band #openSettings.ship-settings-toggle{width:44px;min-width:44px;grid-template-columns:27px}#myr5Shell .shell-band .ship-settings-screen{display:none}}\r\n@media(orientation:landscape) and (max-height:500px){#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button,[data-world-entry],.pod-tilt-request,#openSettings.ship-settings-toggle,#podPersistentChrome>button){height:40px;min-height:40px}#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button,[data-world-entry],.pod-tilt-request){width:40px;min-width:40px}#myr5Shell .shell-band .mom-brand img{width:36px;height:36px}}\r\n#myr5Shell#myr5Shell .shell-band :is(.shell-key,.cockpit-panel-tools button){color:var(--strip-dim)!important}\r\n/* The emptied page header (its keys were adopted into the band) draws no plate. */\r\nhtml.shell-ready .ship-header:not(.shell-band-pod):not(:has(*)){display:none!important}\r\n/* Owner rule: the band carries symbols only. The mark's baked \"MOM INC.\" wordline (bottom 24% of the webp) is clipped; heads re-centred. */\r\n#myr5Shell .shell-band .mom-brand img{clip-path:inset(0 0 24%);translate:0 8%}\r\n/* Update banner docks above the dock instead of covering the viewport (behaviour unchanged). */\r\nhtml.shell-ready .app-update-banner{top:auto!important;bottom:calc(var(--shell-bottom) + 8px)!important}\r\n/* Proxied closes: not tappable while the band Exit drives them; keyboard focus still reaches them (and shows them). */\r\nhtml.shell-ready dialog[data-shell-exit] :is([data-close],.ach-close,.pass-close,.sp-close,.cpp-close,#closeSettings,#closeIdentity,#closeLibrary,[data-menu-close]):not(:focus-visible){pointer-events:none}\r\n/* HF1: the Achievement Hall is a viewport-fixed scene; its controls and card sat under the rails. It now fills the shell well (its own safe-area offsets are already inside the well). */\r\nhtml.shell-ready:not([data-game-active=true]) .vh{inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-x{top:12px}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-archive-open{top:68px}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-pos{top:22px}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-arr{bottom:24px}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-card{bottom:20px}\r\nhtml.shell-ready:not([data-game-active=true]) .vh .vh-archive-panel{max-height:100%;min-height:0}\r\n","modules/ships/ship-access.mjs":"import * as ledger from '../../unlock-ledger.mjs';\r\nimport {shipAccess} from '../../performance-progress.mjs';\r\n\r\nexport const SHIP_REVEAL_KEY = 'myr5-ship-reveal-seen-v1';\r\nexport const SHIP_IDS = Object.freeze(['supportive','direct','analytical','playful','calm','mom']);\r\nconst stableOwner = account => {\r\n  const id = typeof account==='string'?account:account?.user?.id;\r\n  return typeof id === 'string' && /^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(id) ? id : null;\r\n};\r\nconst ownedId = (id,options) => SHIP_IDS.includes(id) && (shipAccess(id,options)||ledger.isGranted('ship', `ship-${id}`,options));\r\nfunction read(store = globalThis.localStorage) {\r\n  try { const value = JSON.parse(store?.getItem(SHIP_REVEAL_KEY) || '{}'); return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }\r\n  catch { return {}; }\r\n}\r\n\r\nexport function ownedShipIds(options) { return SHIP_IDS.filter(id=>ownedId(id,options)); }\r\nexport function hasSeenShipReveal(ship, { account = globalThis.myr5AuthenticatedAccount, storage = globalThis.localStorage } = {}) {\r\n  const owner = stableOwner(account);\r\n  return !!owner && ownedId(ship,{account}) && Array.isArray(read(storage)[owner]) && read(storage)[owner].includes(ship);\r\n}\r\nexport function markShipRevealSeen(ship, { account = globalThis.myr5AuthenticatedAccount, storage = globalThis.localStorage } = {}) {\r\n  const owner = stableOwner(account);\r\n  if (!owner || !ownedId(ship,{account}) || !storage?.setItem) return false;\r\n  const data = read(storage), seen = new Set(Array.isArray(data[owner]) ? data[owner] : []);\r\n  seen.add(ship); data[owner] = [...seen].filter(SHIP_IDS.includes.bind(SHIP_IDS)).sort();\r\n  try { storage.setItem(SHIP_REVEAL_KEY, JSON.stringify(data)); return true; } catch { return false; }\r\n}\r\nexport function coachEditorShips(options = {}) {\r\n  return ownedShipIds(options).filter(ship => hasSeenShipReveal(ship, options));\r\n}\r\nexport const canShowCoachEditorShipSection = (options = {}) => coachEditorShips(options).length > 0;\r\n\r\n/** Only the scene's post-flash completion event advances reveal-seen state. */\r\nexport function acceptShipRevealComplete(event, options = {}) {\r\n  const detail = event?.detail;\r\n  if (!detail?.revealComplete || !SHIP_IDS.includes(detail.ship) || detail.ownerId!==stableOwner(options.account??globalThis.myr5AuthenticatedAccount)) return false;\r\n  return markShipRevealSeen(detail.ship, options);\r\n}\r\n","modules/ships/ship-catalog.mjs":"// These IDs are the same keys used by recipes, rewards and the verified GLB pack.\r\nexport const SHIP_CATALOG=Object.freeze([\r\n {id:'supportive',name:'Bubble shuttle',description:'A rounded cockpit with four bulbous engine pods around a compact hull.'},\r\n {id:'direct',name:'Wing fighter',description:'A pointed cockpit between broad swept wings and long engine rails.'},\r\n {id:'analytical',name:'Ring-engine scout',description:'A narrow cockpit and small fins ahead of a large circular rear engine.'},\r\n {id:'playful',name:'Fork interceptor',description:'A dark central cockpit between long forked nose rails, with two side engine pods.'},\r\n {id:'calm',name:'Capsule pod',description:'A short rounded capsule with a forward cockpit and a thick rear housing.'},\r\n {id:'mom',name:'Cargo carrier',description:'A broad blocky hull with a raised centre tower and two large front engine housings.'},\r\n].map(ship=>Object.freeze({...ship,assetPath:`assets/ships/${ship.id}.glb`})));\r\nexport const shipDetails=id=>SHIP_CATALOG.find(ship=>ship.id===id)||SHIP_CATALOG[0];\r\n","modules/ships/ship-intro.mjs":"import * as THREE from 'three';\r\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\r\nimport { offAxis } from '../portal/peer.mjs'; // #135: tilt looks round the ship through the portal window\r\nimport { createCoachPreviewSpace } from '../../coach-preview-space.mjs';\r\nimport { initialScene,recipeShipTint,applyShipTint,normalizeBackground,normalizeShip,resolveSceneSwap,sampleApproach,SHIP_ANCHOR_Y,coachBand,shipPoseAbove,measureShip,APPROACH_MS,BEAM_CHARGE_MS,SHIP_FACING,SHIP_REST_Z,openCustomizer as openGatedCustomizer } from './ship-scene-domain.mjs';\r\n\r\nconst reduced=()=>matchMedia('(prefers-reduced-motion: reduce)').matches;\r\nconst RECIPE_KEY='myr5-recipe-v1';\r\nconst recipe=()=>{try{return JSON.parse(localStorage.getItem(RECIPE_KEY)||'{}')}catch{return{}}};\r\nconst SHIP_SETTINGS_KEY='myr5-ship-customization-v1';\r\nconst savedShip=owner=>{try{return JSON.parse(localStorage.getItem(`${SHIP_SETTINGS_KEY}/${owner}`)||'{}')}catch{return{}}};\r\nfunction disposeModel(root){const geometries=new Set(),materials=new Set(),textures=new Set();root?.traverse(node=>{if(node.geometry)geometries.add(node.geometry);for(const mat of Array.isArray(node.material)?node.material:[node.material])if(mat){materials.add(mat);for(const value of Object.values(mat))if(value?.isTexture)textures.add(value);}});for(const t of textures){t.source?.data?.close?.();t.dispose();}for(const m of materials)m.dispose();for(const g of geometries)g.dispose();}\r\n\r\n/** Mount the coach-arrival scene. `assetBridge` must come from signed, owned, verified chunk data, or with\r\n * `starter` serve Original MYR5's bundled starter ship from /pod/worlds/starter/ (no owner needed; never marks a reveal seen). */\r\nexport function mountShipScene({host=document.getElementById('view'),customizer='/creature/index.html',assetBridge,ship:initialShip,starter=false,gpu=null}={}){ // gpu: claimGraphics release, called once this scene's context is gone\r\n if(!host||!assetBridge?.getShipUrl)throw new Error('A verified owned ship section is required.');\r\n const ownerId=globalThis.myr5AuthenticatedAccount?.user?.id;if(!ownerId&&!starter)throw new Error('An authenticated ship owner is required.');\r\n const lifecycle=new AbortController();\r\n const assertLive=()=>{if(lifecycle.signal.aborted||globalThis.myr5AuthenticatedAccount?.user?.id!==ownerId)throw new DOMException('Ship scene cancelled','AbortError');assetBridge.ownedShipIds();};\r\n const pause=ms=>new Promise((resolve,reject)=>{assertLive();const cancelled=()=>{clearTimeout(timer);reject(new DOMException('Ship scene cancelled','AbortError'));},timer=setTimeout(()=>{lifecycle.signal.removeEventListener('abort',cancelled);try{assertLive();resolve();}catch(error){reject(error);}},ms);lifecycle.signal.addEventListener('abort',cancelled,{once:true});});\r\n const ownedShips=assetBridge.ownedShipIds?.()||[];if(!ownedShips.length)throw new Error('No owned ship is available for the arrival scene.');\r\n if(host.querySelector(':scope > .ship-scene'))return window.myr5ShipScene||null;\r\n const root=document.createElement('section');root.className='ship-scene';root.setAttribute('aria-label','Coach arrival');\r\n root.innerHTML='<div class=\"ship-scene-background\" aria-hidden=\"true\"></div><div class=\"ship-scene-vignette\" aria-hidden=\"true\"></div><div class=\"ship-scene-beam\" aria-hidden=\"true\"></div><canvas class=\"ship-scene-canvas\" tabindex=\"0\" role=\"button\" aria-label=\"Customize this coach\"></canvas><p class=\"ship-scene-status\" role=\"status\">Preparing coach arrival…</p>';\r\n host.prepend(root);host.classList.add('ship-intro-conceals-coach');\r\n const beam=root.querySelector('.ship-scene-beam'),canvas=root.querySelector('canvas'),status=root.querySelector('.ship-scene-status');\r\n const flash=document.createElement('div');flash.className='ship-scene-flash';flash.setAttribute('aria-hidden','true');document.body.append(flash);\r\n const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(34,1,.1,400);let renderer;\r\n try{renderer=new THREE.WebGLRenderer({canvas,alpha:true,antialias:true,powerPreference:'high-performance'});}catch(error){gpu?.();host.classList.remove('ship-intro-conceals-coach');root.remove();flash.remove();assetBridge.dispose?.();throw error;}\r\n renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.15;renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.shadowMap.enabled=true;\r\n const space=createCoachPreviewSpace(THREE,{pixelRatio:renderer.getPixelRatio()});scene.background=space.background;scene.fog=space.fog;scene.add(space.group);\r\n camera.position.set(0,.45,7);camera.lookAt(0,.9,0);scene.add(new THREE.HemisphereLight(0xe9d9ff,0x23162d,2.5));\r\n const key=new THREE.DirectionalLight(0xffefca,4.2);key.position.set(-3,5,4);key.castShadow=true;scene.add(key);const rim=new THREE.DirectionalLight(0xb58cff,3.2);rim.position.set(4,2,-3);scene.add(rim);\r\n const manager=new THREE.LoadingManager();manager.setURLModifier(url=>{if(!/^(blob:|data:)/.test(url)&&!(starter&&url.startsWith('/pod/worlds/starter/')))throw new Error('Ship assets must be embedded verified bytes');return url;});\r\n const loader=new GLTFLoader(manager),raycaster=new THREE.Raycaster(),pointer=new THREE.Vector2(),cache=new Map(),models=new Set(),custom=savedShip(ownerId);let state={...initialScene(recipe()),ship:ownedShips.includes(initialShip)?initialShip:ownedShips.includes(custom.ship)?custom.ship:ownedShips.includes(initialScene(recipe()).ship)?initialScene(recipe()).ship:ownedShips[0]},ship=null,shipMeshes=[],shipEpoch=0,disposed=false,raf=0,approachStart=0,approaching=false,hoverStart=performance.now(),swapChain=Promise.resolve(),measured=null,pose={y:SHIP_ANCHOR_Y,scale:1},shipTint=/^#[0-9a-f]{6}$/i.test(custom.tint||'')?custom.tint:recipeShipTint(recipe())||'#ffffff';\r\n const css=document.createElement('link');css.rel='stylesheet';css.href='/modules/ships/ship-scene.css';document.head.append(css);\r\n // The hull hovers above the coach card (see shipPoseAbove); recomputed whenever the layout changes.\r\n const placeShip=()=>{const card=host.querySelector('.myr5-companion-card');if(!measured||!card)return;pose=shipPoseAbove(coachBand(root.getBoundingClientRect(),card.getBoundingClientRect()),measured);root.style.setProperty('--ship-belly',`${Math.max(0,(1-pose.belly)*50)}%`);};\r\n const resize=()=>{const rect=root.getBoundingClientRect(),w=Math.max(1,rect.width),h=Math.max(1,rect.height);renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();placeShip()};\r\n const observer=new ResizeObserver(resize);observer.observe(root);resize();\r\n function tintShip(color){shipTint=/^#[0-9a-f]{6}$/i.test(color||'')?color:'#ffffff';for(const mesh of shipMeshes)applyShipTint(mesh,shipTint==='#ffffff'?null:shipTint);}\r\n async function loadShip(id){assertLive();id=normalizeShip(id);const epoch=++shipEpoch;status.textContent=`Loading ${id} ship…`;let source=cache.get(id);if(!source){source=loader.loadAsync(assetBridge.getShipUrl(id)).then(gltf=>{if(disposed){disposeModel(gltf.scene);throw new DOMException('Ship scene cancelled','AbortError');}const box=new THREE.Box3().setFromObject(gltf.scene),size=box.getSize(new THREE.Vector3()),center=box.getCenter(new THREE.Vector3()),normalized=new THREE.Group();gltf.scene.position.sub(center);normalized.add(gltf.scene);normalized.scale.setScalar(2.25/(Math.max(size.x,size.y,size.z)||1));models.add(normalized);return normalized;}).catch(error=>{cache.delete(id);throw error;});cache.set(id,source)}const loaded=await source;assertLive();if(epoch!==shipEpoch)return false;if(ship)scene.remove(ship);ship=new THREE.Group();const model=loaded.clone(true);model.rotation.y=SHIP_FACING;ship.add(model);ship.position.set(0,SHIP_ANCHOR_Y,SHIP_REST_Z);ship.rotation.set(.08,-.32,0);scene.add(ship);measured=measureShip(THREE,ship,camera);placeShip();ship.position.y=pose.y;ship.scale.setScalar(pose.scale);shipMeshes=[];ship.traverse(o=>{if(o.isMesh)shipMeshes.push(o)});tintShip(shipTint);state=resolveSceneSwap(state,{ship:id});root.dataset.ship=id;status.textContent='Ship ready.';return true;}\r\n // Background selection remains scene metadata; pixel worlds belong to the center pod viewport.\r\n function setBackground(id){id=normalizeBackground(id);root.dataset.background=id;state=resolveSceneSwap(state,{background:id});return id}\r\n function setCoach(coach){state=resolveSceneSwap(state,{coach});root.dataset.coach=typeof coach==='string'?coach:'recipe';if(coach&&typeof coach==='object')window.dispatchEvent(new CustomEvent('myr5:recipe',{detail:coach}));window.dispatchEvent(new CustomEvent('myr5:coach-scene-swap',{detail:{coach}}));return coach}\r\n async function ensureCoach(){if(!document.querySelector('.myr5-companion-card'))try{await import('/creature/assets/phone.js')}catch(error){console.warn('Coach viewer unavailable',error)}assertLive();const started=performance.now();while(!document.querySelector('.myr5-companion-card')&&performance.now()-started<8000)await pause(50);if(!document.querySelector('.myr5-companion-card'))throw new Error('Coach viewer did not become ready');}\r\n async function flashTransition(change){assertLive();flash.setAttribute('popover','manual');try{flash.showPopover?.();}catch{}flash.classList.add('is-on');window.dispatchEvent(new CustomEvent('myr5:portal-transition',{detail:{phase:'flash',duration:reduced()?112:732,source:'coach-scene'}}));try{await pause(16);await pause(reduced()?40:340);await change();assertLive();host.classList.remove('ship-intro-conceals-coach');host.classList.add('ship-intro-revealed');placeShip();await pause(16);flash.classList.remove('is-on');await pause(reduced()?40:360);assertLive();window.dispatchEvent(new CustomEvent('myr5:portal-transition',{detail:{phase:'complete',source:'coach-scene'}}));}finally{flash.classList.remove('is-on');try{flash.hidePopover?.();}catch{}}}\r\n function ready(revealComplete=false){assertLive();window.dispatchEvent(new CustomEvent('myr5:ship-scene-ready',{detail:{...state,revealComplete:revealComplete&&!starter,ownerId,starter}}))}\r\n async function revealShip(id=state.ship,coach=state.coach){assertLive();if(!assetBridge.ownedShipIds().includes(normalizeShip(id)))throw new Error('ship is not owned');state=resolveSceneSwap(state,{ship:id,coach});setBackground(state.background);const loaded=await Promise.all([loadShip(state.ship),ensureCoach()]);assertLive();if(!loaded[0])return false;placeShip();/* the coach card may land after the hull did */if(!reduced()){root.dataset.phase='approach';approaching=true;approachStart=performance.now();await pause(APPROACH_MS);root.dataset.phase='beam';beam.classList.add('is-charging');await pause(BEAM_CHARGE_MS);beam.classList.add('is-open');await pause(450);}await flashTransition(async()=>{beam.classList.remove('is-open','is-charging');setCoach(coach)});root.dataset.phase='ready';status.textContent='Coach ready. Select the ship to customize.';ready(true);return true}\r\n async function swap(next={},options={}){swapChain=swapChain.catch(()=>{}).then(async()=>{assertLive();const target=resolveSceneSwap(state,next);if(!assetBridge.ownedShipIds().includes(target.ship))throw new Error('ship is not owned');const change=async()=>{if(target.ship!==state.ship&&!await loadShip(target.ship))throw new Error('Ship changed while loading');assertLive();if(target.background!==state.background)setBackground(target.background);if(target.coach!==state.coach)setCoach(target.coach);state=target};if(options.flash===false)await change();else await flashTransition(change);ready(false);return state});return swapChain}\r\n async function play(){return revealShip(state.ship,state.coach)}\r\n function hit(event){if(!ship)return false;const rect=canvas.getBoundingClientRect();pointer.set((event.clientX-rect.left)/rect.width*2-1,-((event.clientY-rect.top)/rect.height*2-1));raycaster.setFromCamera(pointer,camera);return raycaster.intersectObjects(shipMeshes,false).length>0}\r\n const openCustomizer=()=>{if(root.dataset.phase==='ready')openGatedCustomizer(customizer);};canvas.addEventListener('pointermove',e=>canvas.classList.toggle('is-over-ship',hit(e)));canvas.addEventListener('pointerleave',()=>canvas.classList.remove('is-over-ship'));canvas.addEventListener('click',e=>{if(hit(e))openCustomizer()});canvas.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();openCustomizer()}});\r\n function tick(now){if(disposed)return;if(ship){if(reduced()){approaching=false;ship.position.set(0,pose.y,SHIP_REST_Z);ship.scale.setScalar(pose.scale);ship.rotation.set(.08,-.32,0);}else if(approaching){const p=sampleApproach(now-approachStart);ship.position.set(p.x,p.y-SHIP_ANCHOR_Y+pose.y,p.z);ship.scale.setScalar(p.scale*pose.scale);ship.rotation.z=p.roll;if(p.done){approaching=false;hoverStart=now;ship.rotation.z=0}}else{ship.position.y=pose.y+Math.sin((now-hoverStart)/820)*.07*pose.scale;ship.scale.setScalar(pose.scale);ship.rotation.y=-.32+Math.sin((now-hoverStart)/1700)*.055}}offAxis(camera,7);renderer.render(scene,camera);raf=requestAnimationFrame(tick)}raf=requestAnimationFrame(tick);\r\n const failed=error=>{if(!disposed){status.textContent='Ship unavailable. Your coach is still ready.';host.classList.remove('ship-intro-conceals-coach');beam.classList.remove('is-open','is-charging');}if(error?.name!=='AbortError')console.warn('Coach ship unavailable',error);};\r\n const storage=e=>{if(e.key===RECIPE_KEY&&e.newValue)try{const parsed=JSON.parse(e.newValue),next=initialScene(parsed);void swap(next).then(()=>tintShip(recipeShipTint(parsed)||'#ffffff')).catch(failed)}catch{}};\r\n const recolor=e=>{if(e.detail?.ownerId!==ownerId)return;if(e.detail.ship===state.ship)tintShip(e.detail.tint);else if(assetBridge.ownedShipIds().includes(e.detail.ship))void swap({ship:e.detail.ship},{flash:false}).then(()=>tintShip(e.detail.tint)).catch(failed);};\r\n const accountChanged=()=>{if(globalThis.myr5AuthenticatedAccount?.user?.id!==ownerId)api.dispose();};\r\n window.addEventListener('storage',storage);window.addEventListener('myr5:ship-customization',recolor);window.addEventListener('myr5:account-ready',accountChanged);window.addEventListener('myr5:account-cleared',accountChanged);\r\n const api={get state(){return{...state}},swap,setShip:ship=>swap({ship},{flash:false}),setBackground:background=>swap({background},{flash:false}),setCoach:coach=>swap({coach},{flash:false}),setShipTint:tint=>{assertLive();tintShip(tint)},play,reveal:revealShip,openCustomizer,dispose(){if(disposed)return;disposed=true;lifecycle.abort();++shipEpoch;cancelAnimationFrame(raf);observer.disconnect();window.removeEventListener('storage',storage);window.removeEventListener('myr5:ship-customization',recolor);window.removeEventListener('myr5:account-ready',accountChanged);window.removeEventListener('myr5:account-cleared',accountChanged);window.removeEventListener('pagehide',pagehide);for(const model of models)disposeModel(model);models.clear();cache.clear();space.dispose();renderer.dispose();renderer.forceContextLoss();gpu?.(canvas);flash.remove();root.remove();css.remove();host.classList.remove('ship-intro-conceals-coach','ship-intro-revealed');assetBridge.dispose?.();if(window.myr5ShipScene===api)delete window.myr5ShipScene;}};\r\n const pagehide=()=>api.dispose();window.addEventListener('pagehide',pagehide);\r\n window.myr5ShipScene=api;api.ready=revealShip(state.ship,state.coach).catch(error=>{failed(error);return false;});return api;\r\n}\r\n","modules/ships/ship-scene-domain.mjs":"export const SHIP_STYLES=Object.freeze(['supportive','direct','analytical','playful','calm','mom']);\r\nexport const BIOMES=Object.freeze(['original','patch','fold','lattice','wax','fungi','glass','bristle','cork','coral','metal','feather','moss','stormcloud','frost','ember','shadow','dragon','spring','vine','chitin','puff','rock','shag']);\r\nconst PART_WEIGHTS=Object.freeze({body:3,head:2,eye:1,collar:1,arms:1,feet:1});\r\nexport const normalizeShip=value=>SHIP_STYLES.includes(value)?value:'supportive';\r\nexport function normalizeBackground(value){if(typeof value==='number'&&Number.isInteger(value))return BIOMES[value]||BIOMES[0];return BIOMES.includes(value)?value:BIOMES[0];}\r\nexport function dominantFamily(design={}){const styles=design.styles&&typeof design.styles==='object'?design.styles:design,body=Number.isInteger(Number(styles.body))?Number(styles.body):0,score=new Map();for(const [part,weight]of Object.entries(PART_WEIGHTS)){const value=Number(styles[part]);if(Number.isInteger(value)&&value>=0)score.set(value,(score.get(value)||0)+weight)}if(!score.size)return body;let best=body,bestScore=score.get(body)||-1;for(const [family,value]of score)if(value>bestScore||(value===bestScore&&family===body)){best=family;bestScore=value}return best;}\r\n/** The tint the recipe saved for the coach's ship; null keeps the ship's own materials. */\r\nexport const recipeShipTint=design=>/^#[0-9a-f]{6}$/i.test(design?.shipColor||'')?design.shipColor:null;\r\n/** Multiply the hull's base colours by `color` (shading, textures and glowing windows/lights stay); null restores the originals. */\r\nexport function applyShipTint(root,color){const hex=/^#[0-9a-f]{6}$/i.test(color||'')?color:null;root?.traverse(node=>{for(const m of [].concat(node.material||[])){if(!m?.color)continue;if(m.userData.shipBase===undefined)m.userData.shipBase=m.color.getHex();if(m.emissive&&m.emissive.getHex()&&m.emissiveIntensity>0)continue;m.color.set(hex??m.userData.shipBase);m.needsUpdate=true;}});}\r\nexport function initialScene(design={}){return Object.freeze({ship:normalizeShip(design.shipId??design.coach),background:normalizeBackground(dominantFamily(design)),coach:design});}\r\nexport function resolveSceneSwap(current,next={}){return Object.freeze({ship:next.ship===undefined?normalizeShip(current.ship):normalizeShip(next.ship),background:next.background===undefined?normalizeBackground(current.background):normalizeBackground(next.background),coach:next.coach===undefined?current.coach:next.coach});}\r\n// #145 (Ian 2026-09-23): the ship shows its stern to the viewer, flies in over their head and settles nearer the\r\n// camera than the coach; the beam waits until it has landed. The duration knobs: the flight, then the beam's charge.\r\nexport const APPROACH_MS=2200,BEAM_CHARGE_MS=500;\r\n// The starter ship's nose is its model +x (cockpit; the engines are at -x): a quarter turn puts the stern to the camera.\r\nexport const SHIP_FACING=Math.PI/2,SHIP_REST_Z=1.5;\r\n// From above and behind the camera (z 7) it glides forward and down over the coach into its hover spot: depth and\r\n// drift ease out, height a little faster, so it drops into view at the top edge and slows into place.\r\nexport function sampleApproach(elapsed,duration=APPROACH_MS){const t=Math.max(0,Math.min(1,elapsed/duration)),far=Math.pow(1-t,3),high=Math.pow(1-t,5);if(t===1)return Object.freeze({x:0,y:SHIP_ANCHOR_Y,z:SHIP_REST_Z,scale:1,roll:0,done:true});return Object.freeze({x:1.4*far,y:SHIP_ANCHOR_Y+2.6*high,z:SHIP_REST_Z+8.5*far,scale:1,roll:-.25*far,done:false});}\r\n// Hover pose that keeps the whole hull above the coach. The coach card is framed body-to-edges (viewer\r\n// 'overlay' stage: feet on its bottom edge, head at or below its top), so its top edge bounds the head.\r\n// band: NDC y range above the card; measured: the ship's NDC y box (top/bottom) and origin at the base\r\n// anchor and scale 1, plus unit = NDC y per world unit of height. Returns world y, scale and the NDC y of\r\n// the posed hull's belly (where the beam starts). A band too short to use keeps the base pose.\r\nexport const SHIP_ANCHOR_Y=1.82;\r\nexport function coachBand(stage,coach,{margin=.08,gap=.02}={}){const h=stage.height||1,top=h*margin,bottom=coach.top-stage.top-h*gap;return {top:1-2*top/h,bottom:1-2*bottom/h,aspect:stage.width/h};}\r\n// A portrait phone (375x812) sees less width than height: the hull's widest side (NDC x, about the centred origin)\r\n// also caps the scale so the wings are never cut by the stage edges. `side` is the NDC half-width it may use.\r\nexport function shipPoseAbove(band,measured,fill=.85,side=.9){\r\n // NDC x shrinks as the stage widens: rescale a measurement taken at another aspect (a later resize) to this one.\r\n const height=measured.top-measured.bottom,stretch=band.aspect>0&&measured.aspect>0?measured.aspect/band.aspect:1,half=Math.max(Math.abs(measured.left??0),Math.abs(measured.right??0))*stretch,wide=half>side?side/half:1;\r\n if(!(band.top-band.bottom>.2)||!(height>0)||!(measured.unit>0))return Object.freeze({y:SHIP_ANCHOR_Y,scale:wide,belly:measured.bottom-(measured.bottom-measured.origin)*(1-wide)});\r\n const scale=Math.min(1,wide,(band.top-band.bottom)*fill/height),centre=(band.top+band.bottom)/2;\r\n return Object.freeze({y:SHIP_ANCHOR_Y+(centre-((measured.top+measured.bottom)/2-measured.origin)*scale-measured.origin)/measured.unit,scale,belly:centre-height*scale/2});\r\n}\r\n// NDC y (and x) extent of an object's projected vertices (the visible hull, not its looser bounding box), its\r\n// origin and NDC y per world unit, for shipPoseAbove.\r\nexport function measureShip(THREE,object,camera){\r\n camera.updateMatrixWorld();object.updateMatrixWorld(true);\r\n const v=new THREE.Vector3();let top=-Infinity,bottom=Infinity,left=Infinity,right=-Infinity;\r\n object.traverse(mesh=>{const position=mesh.isMesh&&mesh.geometry.attributes.position;if(position)for(let i=0;i<position.count;i++){v.fromBufferAttribute(position,i).applyMatrix4(mesh.matrixWorld).project(camera);if(v.y>top)top=v.y;if(v.y<bottom)bottom=v.y;if(v.x<left)left=v.x;if(v.x>right)right=v.x;}});\r\n const origin=object.position.clone().project(camera).y,above=object.position.clone().add(new THREE.Vector3(0,1,0)).project(camera).y;\r\n return {top,bottom,left,right,aspect:camera.aspect,origin,unit:above-origin};\r\n}\r\n// #148: each ship activation writes a one-use, versioned admission token. The editor consumes it as\r\n// soon as it loads, so a typed URL cannot reuse an old visit's sessionStorage value.\r\nexport const SHIP_GATE='myr5-ship-gate',SHIP_GATE_TOKEN='ship-admission-v2',SHIP_HISTORY_ADMISSION='myr5-ship-admitted-v2';\r\nexport function openCustomizer(url='/creature/index.html'){try{sessionStorage.setItem(SHIP_GATE,SHIP_GATE_TOKEN);location.assign(url);}catch{try{sessionStorage.removeItem(SHIP_GATE)}catch{}location.assign('/pose.html#select');}}\r\n","modules/ships/ship-scene.css":".containment-pod{isolation:isolate}\r\n.ship-scene{position:absolute;inset:0;z-index:2;overflow:hidden;border-radius:inherit;background:#100b1a}\r\n.ship-scene-background,.ship-scene-vignette,.ship-scene-beam,.ship-scene-canvas{position:absolute;inset:0}\r\n.ship-scene-background{background:radial-gradient(ellipse at 28% 24%,#50326960,transparent 50%),linear-gradient(#090815,#20162f 60%,#120d1e);image-rendering:auto;transform:scale(1.02)}\r\n.ship-scene-vignette{z-index:4;pointer-events:none;background:linear-gradient(#130b2430 0 34%,#12091f00 55%,#07040dcc),radial-gradient(circle at 50% 42%,transparent 30%,#08040d99 100%)}\r\n.ship-scene-canvas{z-index:3;width:100%;height:100%;outline:0}\r\n.ship-scene-canvas.is-over-ship{cursor:pointer;filter:drop-shadow(0 0 10px #ffd86f)}\r\n.ship-scene-canvas:focus-visible{outline:3px solid #ffd76d;outline-offset:-5px}\r\n.ship-scene-beam{z-index:4;pointer-events:none;inset:var(--ship-belly,31%) 18% -12%;opacity:0;clip-path:polygon(43% 0,57% 0,92% 100%,8% 100%);background:radial-gradient(ellipse at 50% 0,#fff 0 5%,#fff4ba 9%,#ffc641a8 34%,#ffad1820 70%,transparent 74%);filter:blur(5px);transform:scaleX(.18);transform-origin:50% 0;transition:opacity .28s,transform .55s cubic-bezier(.15,.8,.2,1)}\r\n.ship-scene-beam.is-charging{opacity:.46}\r\n.ship-scene-beam.is-open{opacity:1;transform:scaleX(1)}\r\n.ship-scene-status{position:absolute;z-index:5;left:12px;right:12px;bottom:9px;margin:0;text-align:center;color:#fff0c0;font:700 10px/1.4 ui-monospace,monospace;letter-spacing:.06em;text-shadow:0 2px 3px #000}\r\n.ship-scene-flash{position:fixed;inset:0;z-index:2147483000;pointer-events:none;opacity:0;background:radial-gradient(circle at 50% 48%,#fff 0 40%,#fffdf0 63%,#ffd65a 100%);transition:opacity .31s ease-in}\r\n.ship-scene-flash.is-on{opacity:1;transition-timing-function:cubic-bezier(.1,.9,.1,1)}\r\n#view.ship-intro-conceals-coach .coach-mount,#view.ship-intro-conceals-coach .myr5-companion-card{opacity:0!important}\r\n#view.ship-intro-revealed .coach-mount{position:absolute;z-index:4;background:transparent!important;pointer-events:none}\r\n#view.ship-intro-revealed .myr5-companion-card{position:absolute;z-index:4;left:18%;right:18%;top:29%;bottom:-2%;width:auto;height:auto;border:0;background:transparent;overflow:visible;backdrop-filter:none;pointer-events:none}\r\n#view.ship-intro-revealed .myr5-companion-stage{width:100%;height:100%;background:transparent;filter:drop-shadow(0 9px 12px #0009)}\r\n#view.ship-intro-revealed .myr5-companion-status,#view.ship-intro-revealed .myr5-customize,#view.ship-intro-revealed .myr5-hide{display:none!important}\r\n.ship-scene~.pod-floor,.ship-scene~.pod-nameplate,.ship-scene~.camera-preview{z-index:5}\r\n@media(prefers-reduced-motion:reduce){.ship-scene-beam,.ship-scene-flash{transition-duration:.01ms!important}.ship-scene-background{transform:none}}\r\n.ship-scene-flash[popover]{margin:0;border:0;padding:0;max-width:none;max-height:none;width:100vw;height:100dvh;}\r\n","modules/ships/ship-view-bridge.mjs":"// Statically imported by app.mjs ONLY (never by modules/ships/ship-view.mjs, which scripts/build.mjs\r\n// serves unbundled in production — see external list). Being a static import of app.mjs means this\r\n// module, and everything under it (material-config.mjs's build-time trust key define,\r\n// post-download-sections.mjs -> battle-pass.mjs -> a .ts file), gets compiled into app-runtime.mjs\r\n// instead of being fetched raw by the browser, where the define would be undefined and the .ts file\r\n// would 404. tests/ship-view-import-graph.test.mjs enforces that ship-view.mjs never re-acquires\r\n// this chain.\r\nimport {ownedShipIds,hasSeenShipReveal,acceptShipRevealComplete} from './ship-access.mjs';\r\n\r\nexport {ownedShipIds};\r\n// Shared with the customizer's Ship tab preview (creature/source/editor-workbench.ts).\r\nexport {localVerifiedBridge} from './verified-ship-assets.mjs';\r\n\r\n/** Called only with the view's verified local bridge. The real post-flash scene\r\n * event is the sole source of reveal completion; cancellation never marks seen. */\r\nexport async function mountFirstShipArrival({host,assetBridge,isCurrent = () => true}) {\r\n const ownerId = globalThis.myr5AuthenticatedAccount?.user?.id;\r\n const ship = assetBridge.ownedShipIds().find(id => !hasSeenShipReveal(id));\r\n if (!ownerId || !ship) return null;\r\n // Optional scene code must not become a core-only offline startup dependency.\r\n const [{mountShipScene},{claimGraphics}] = await Promise.all([import('./ship-intro.mjs'),import('../graphics-lifecycle.mjs')]);\r\n const gpu = claimGraphics('ship',{keep:['coach']});await gpu.ready; // the hidden portal board yields; the coach rides the beam\r\n if (!isCurrent() || globalThis.myr5AuthenticatedAccount?.user?.id !== ownerId) {gpu();throw new DOMException('Ship arrival cancelled', 'AbortError');}\r\n let scene, disposed = false;\r\n const complete = event => {\r\n  if (!disposed && scene && event.detail?.ownerId === ownerId && event.detail?.ship === ship) acceptShipRevealComplete(event);\r\n };\r\n window.addEventListener('myr5:ship-scene-ready', complete);\r\n try { scene = mountShipScene({host,assetBridge,ship,gpu}); }\r\n catch (error) { window.removeEventListener('myr5:ship-scene-ready', complete); throw error; }\r\n return {\r\n  ready: scene.ready.finally(() => window.removeEventListener('myr5:ship-scene-ready', complete)),\r\n  dispose() { if (disposed) return; disposed = true; window.removeEventListener('myr5:ship-scene-ready', complete); scene.dispose(); }\r\n };\r\n}\r\n","modules/ships/ship-view.css":".ship-view{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:0;border:0;background:#0b0714;color:#fff0c0}\r\n.ship-view::backdrop{background:#000}\r\n.ship-view-stage{position:absolute;inset:0;overflow:hidden}\r\n.ship-view-bg{position:absolute;inset:0;z-index:0;background:radial-gradient(ellipse at 28% 24%,#50326960,transparent 50%),linear-gradient(#090815,#20162f 60%,#120d1e);image-rendering:auto}\r\n.ship-view-canvas{position:absolute;inset:0;z-index:1;width:100%;height:100%}\r\n.ship-view-coach{position:absolute;inset:36% 0 0;z-index:2}\r\n.ship-view-coach .myr5-companion-card{position:absolute;inset:0;width:auto;height:auto;border:0;background:transparent;overflow:visible;backdrop-filter:none;border-radius:0}\r\n.ship-view-coach .myr5-companion-stage{width:100%;height:100%;background:transparent}\r\n.ship-view-coach .myr5-companion-status,.ship-view-coach .myr5-customize,.ship-view-coach .myr5-hide{display:none!important}\r\n.ship-view-note{position:absolute;z-index:3;left:12px;right:12px;bottom:9px;margin:0;text-align:center;color:#fff0c0;font:700 11px/1.4 ui-monospace,monospace;letter-spacing:.05em;text-shadow:0 2px 3px #000}\r\n.ship-view-fallback{position:absolute;z-index:3;left:12px;right:12px;bottom:max(12px,env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:8px;align-items:center;padding:8px 12px;border-radius:12px;background:#000000b3;backdrop-filter:blur(4px)}\r\n.ship-view-fallback p{margin:0;color:#fff0c0;font:600 11px/1.4 ui-monospace,monospace;text-align:center}\r\n.ship-view-error{display:block;color:#ffb4a8;font:700 11px/1.4 ui-monospace,monospace;text-align:center}\r\n.ship-view-error[hidden]{display:none}\r\n.ship-view-download,.ship-view-retry{min-height:40px;padding:0 16px;border:0;border-radius:8px;background:#ffd76d;color:#241407;font:700 13px/1 ui-monospace,monospace}\r\n.ship-view-close{position:absolute;z-index:4;top:max(12px,env(safe-area-inset-top));right:max(12px,env(safe-area-inset-right));width:40px;height:40px;border-radius:50%;border:1px solid #70568b80;background:#21162bc9;color:#fff0c0;font-size:16px}\r\n@media(prefers-reduced-motion:reduce){.ship-view *{animation:none!important;transition:none!important}}\r\n\r\n.ship-view-stage.ship-intro-conceals-coach .ship-view-coach{opacity:0;pointer-events:none}\r\n.ship-view-stage.ship-intro-revealed .ship-view-coach{z-index:3;pointer-events:none}\r\n/* The arrival scene's status line sits up top here, clear of the upgrade line at the bottom. */\r\n.ship-view-stage .ship-scene-status{top:max(24px,env(safe-area-inset-top));bottom:auto;left:60px;right:60px}\r\n/* That status already reports the load; the view's own loading note steps aside while the scene is up. */\r\n.ship-view-stage:has(> .ship-scene) + .ship-view-note{visibility:hidden}\r\n","modules/ships/ship-view.mjs":"// D-ship-route: full-screen ship view. Same coach capsule renderer as the pod's \"Show my coach\"\r\n// card (creature/assets/phone.js), reparented full-screen, with the owned ship + supply-drop space\r\n// composed behind it when the signed \"Ships and worlds\" pack is already downloaded and verified\r\n// locally. Without that pack (signed out, no ship owned, or not downloaded) Original MYR5's bundled\r\n// starter ship shows instead, with his entrance the first open of the session.\r\n// `getBridge`/`ownedShipIds` are injected by app.mjs (modules/ships/ship-view-bridge.mjs) —\r\n// scripts/build.mjs serves THIS file unbundled in production (no Vite, no build-time defines), so it\r\n// must stay free of imports that need either. tests/ship-view-import-graph.test.mjs enforces that.\r\nimport {offAxis} from '../portal/peer.mjs'; // #135: tilt looks round the ship through the portal window\r\nimport {initialScene,recipeShipTint,applyShipTint,SHIP_ANCHOR_Y,SHIP_FACING,SHIP_REST_Z,coachBand,shipPoseAbove,measureShip,openCustomizer} from './ship-scene-domain.mjs';\r\nimport {createCoachPreviewSpace} from '../../coach-preview-space.mjs';\r\nimport {claimGraphics} from '../graphics-lifecycle.mjs';\r\n// Ship scenes keep the 3D coach (it rides the beam) and take the hidden portal board's context until they close.\r\nconst shipGpu=async()=>{const release=claimGraphics('ship',{keep:['coach']});await release.ready;return release;};\r\n\r\nconst HASH = '#ship';\r\nconst RECIPE_KEY = 'myr5-recipe-v1';\r\nconst SHIP_SETTINGS_KEY = 'myr5-ship-customization-v1';\r\nconst readJSON = key => { try { const v = JSON.parse(localStorage.getItem(key) || '{}'); return v && typeof v === 'object' && !Array.isArray(v) ? v : {}; } catch { return {}; } };\r\n\r\nconst savedTint = () => recipeShipTint(readJSON(RECIPE_KEY));\r\n// Original MYR5's recipe (coach 'supportive') maps to this ship through initialScene().\r\nconst STARTER_SHIP = 'supportive', STARTER_SHIP_URL = '/pod/worlds/starter/supportive.glb';\r\nconst UPGRADE_TEXT = 'Earn ships on your tracks · Download Ships & worlds for all of them';\r\nlet starterEntranceDone = false; // once per app session (page lifetime)\r\n\r\nfunction disposeModel(root) {\r\n const geometries = new Set(), materials = new Set(), textures = new Set();\r\n root?.traverse(node => { if (node.geometry) geometries.add(node.geometry); for (const mat of [].concat(node.material || [])) if (mat) { materials.add(mat); for (const value of Object.values(mat)) if (value?.isTexture) textures.add(value); } });\r\n for (const t of textures) t.dispose(); for (const m of materials) m.dispose(); for (const g of geometries) g.dispose();\r\n}\r\n\r\n/** A still (idle hover only) ship + supply-drop space, not the full approach/beam/flash cinematic —\r\n * used after the first arrival has completed. */\r\nasync function mountRealShip({ stage, bridge, ship, tint = null }) {\r\n const [THREE, { GLTFLoader }] = await Promise.all([import('three'), import('three/addons/loaders/GLTFLoader.js')]);\r\n const canvas = document.createElement('canvas'); canvas.className = 'ship-view-canvas'; stage.append(canvas);\r\n const scene = new THREE.Scene(), camera = new THREE.PerspectiveCamera(34, 1, .1, 400);\r\n let renderer;const gpu = await shipGpu();\r\n try { renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true, powerPreference: 'high-performance' }); }\r\n catch (error) { gpu(); canvas.remove(); throw error; }\r\n renderer.outputColorSpace = THREE.SRGBColorSpace; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.15; renderer.setPixelRatio(Math.min(devicePixelRatio, 2));\r\n const space = createCoachPreviewSpace(THREE, { pixelRatio: renderer.getPixelRatio() });\r\n scene.background = space.background; scene.fog = space.fog; scene.add(space.group);\r\n camera.position.set(0, .45, 7); camera.lookAt(0, .9, 0);\r\n scene.add(new THREE.HemisphereLight(0xe9d9ff, 0x23162d, 2.5));\r\n const key = new THREE.DirectionalLight(0xffefca, 4.2); key.position.set(-3, 5, 4); scene.add(key);\r\n const rim = new THREE.DirectionalLight(0xb58cff, 3.2); rim.position.set(4, 2, -3); scene.add(rim);\r\n let place = () => {};\r\n const resize = () => { const r = stage.getBoundingClientRect(), w = Math.max(1, r.width), h = Math.max(1, r.height); renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix(); place(); };\r\n const observer = new ResizeObserver(resize); observer.observe(stage); resize();\r\n let disposed = false;\r\n let loaded;\r\n try { loaded = await new GLTFLoader().loadAsync(bridge.getShipUrl(ship)); }\r\n catch (error) { observer.disconnect(); space.dispose(); renderer.dispose(); renderer.forceContextLoss(); gpu(canvas); canvas.remove(); throw error; }\r\n if (disposed) { disposeModel(loaded.scene); observer.disconnect(); space.dispose(); renderer.dispose(); renderer.forceContextLoss(); gpu(canvas); canvas.remove(); throw new DOMException('Ship view closed', 'AbortError'); }\r\n const box = new THREE.Box3().setFromObject(loaded.scene), size = box.getSize(new THREE.Vector3()), center = box.getCenter(new THREE.Vector3());\r\n loaded.scene.position.sub(center);\r\n const group = new THREE.Group(), model = new THREE.Group(), fit = 2.25 / (Math.max(size.x, size.y, size.z) || 1); model.rotation.y = SHIP_FACING; model.add(loaded.scene); applyShipTint(model, tint); group.add(model); group.scale.setScalar(fit);\r\n group.position.set(0, SHIP_ANCHOR_Y, SHIP_REST_Z); group.rotation.set(.08, -.32, 0); scene.add(group);\r\n // Same hover pose as the arrival scene's end: the whole hull above the coach card.\r\n const measured = measureShip(THREE, group, camera); let pose;\r\n place = () => { pose = shipPoseAbove(coachBand(stage.getBoundingClientRect(), coachMount.getBoundingClientRect()), measured); group.position.y = pose.y; group.scale.setScalar(fit * pose.scale); };\r\n place();\r\n // #148: the still ship opens the customizer exactly like the arrival's ship does (tap the hull, or Enter/Space).\r\n const raycaster = new THREE.Raycaster(), pointer = new THREE.Vector2();\r\n canvas.tabIndex = 0; canvas.setAttribute('role', 'button'); canvas.setAttribute('aria-label', 'Customize this coach');\r\n canvas.addEventListener('click', e => { const r = canvas.getBoundingClientRect(); pointer.set((e.clientX - r.left) / r.width * 2 - 1, -((e.clientY - r.top) / r.height * 2 - 1)); raycaster.setFromCamera(pointer, camera); if (raycaster.intersectObject(group, true).length) openCustomizer(); });\r\n canvas.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openCustomizer(); } });\r\n const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n let raf = 0; const start = performance.now();\r\n function tick(now) {\r\n  if (disposed) return;\r\n  if (!reduced) { group.position.y = pose.y + Math.sin((now - start) / 820) * .07 * pose.scale; group.rotation.y = -.32 + Math.sin((now - start) / 1700) * .055; }\r\n  offAxis(camera, 7); renderer.render(scene, camera); raf = requestAnimationFrame(tick);\r\n }\r\n raf = requestAnimationFrame(tick);\r\n return {\r\n  dispose() {\r\n   if (disposed) return; disposed = true; cancelAnimationFrame(raf); observer.disconnect();\r\n   space.dispose(); disposeModel(scene); renderer.dispose(); renderer.forceContextLoss(); gpu(canvas); canvas.remove();\r\n   bridge.dispose?.();\r\n  }\r\n };\r\n}\r\n\r\nlet dialog = null, stage = null, bgEl = null, coachMount = null, note = null, fallback = null, downloadBtn = null, customizeBtn = null, retryBtn = null, errorLine = null;\r\nlet viewOwner = null, lastOpen = {};\r\nlet realShip = null, pushedHash = false, openEpoch = 0, coachStage = null, openHash = HASH;\r\nconst LOADING = 'Loading your ship…';\r\n\r\nfunction clearShipVisual() { realShip?.dispose(); realShip = null; bgEl.style.backgroundImage = ''; fallback.hidden = true; customizeBtn.hidden = true; retryBtn.hidden = true; errorLine.hidden = true; }\r\n// #148: no ship to tap (no WebGL, or the model failed): the customizer is still one plain button away.\r\nfunction offerCustomizer() { fallback.hidden = false; customizeBtn.hidden = false; }\r\n// A ship that failed to load says so and can be tried again (the same open, re-run in place).\r\nfunction offerRetry(text) { errorLine.textContent = text; errorLine.hidden = false; retryBtn.hidden = false; fallback.hidden = false; }\r\n// Inside the app modules/routes.mjs owns this view's #hash (it pushes and pops it: one owner of history). Standalone\r\n// (no router) the view keeps its own.\r\nconst routed = () => !!window.myr5Routes;\r\n\r\n/** W2-2O: the starter ship and wonders are Starter-pack art. When the ship didn't load because it isn't on this\r\n * phone (offline, never downloaded; not a WebGL failure), the plain backdrop and the coach stay, and the Starter\r\n * pack is offered. */\r\nasync function offerStarter(isCurrent) {\r\n if (navigator.onLine || await globalThis.caches?.match(STARTER_SHIP_URL).catch(() => null) || !isCurrent()) return false;\r\n const line = fallback.querySelector('p'); line.textContent = 'Your starter ship and its worlds come with the Starter pack.'; line.hidden = false;\r\n downloadBtn.textContent = 'Download the Starter pack'; downloadBtn.dataset.pack = 'starter'; downloadBtn.hidden = false; fallback.hidden = false;\r\n return true;\r\n}\r\n\r\n/** No verified pack: the starter ship over the supply-drop space. The upgrade line shows only when\r\n * signed in; the download button only when they own a ship. Reduced motion or a repeat open idles. */\r\nasync function showStarter({ signedIn, owned, isCurrent, always = false }) {\r\n const line = fallback.querySelector('p'); line.textContent = UPGRADE_TEXT; line.hidden = !signedIn; downloadBtn.textContent = 'Download Ships & worlds'; downloadBtn.dataset.pack = 'coach-ships-biomes'; downloadBtn.hidden = !owned.length; fallback.hidden = !signedIn;\r\n const bridge = { ownedShipIds: () => [STARTER_SHIP], getShipUrl: () => STARTER_SHIP_URL };\r\n try {\r\n  if ((always || !starterEntranceDone) && !matchMedia('(prefers-reduced-motion: reduce)').matches) {\r\n   const { mountShipScene } = await import('./ship-intro.mjs');\r\n   const gpu = await shipGpu();\r\n   if (!isCurrent()) { gpu(); return; }\r\n   const scene = realShip = mountShipScene({ host: stage, assetBridge: bridge, ship: STARTER_SHIP, starter: true, gpu });\r\n   // Offered (W2-2O): the arrival scene steps aside (its beam and status), leaving the plain backdrop, the coach and the offer.\r\n   // No ship to tap either way (#148): the plain customizer button shows too.\r\n   if (await scene.ready) { if (isCurrent()) starterEntranceDone = true; return true; }\r\n   if (await offerStarter(isCurrent) && realShip === scene) { scene.dispose(); realShip = null; } if (isCurrent()) offerCustomizer();\r\n   return false;\r\n  }\r\n  const mounted = await mountRealShip({ stage, bridge, ship: STARTER_SHIP, tint: savedTint() });\r\n  if (!isCurrent()) mounted.dispose(); else realShip = mounted;\r\n  return true;\r\n } catch { await offerStarter(isCurrent); if (isCurrent()) offerCustomizer(); /* no WebGL or model: the space fallback and coach still show */ return false; }\r\n}\r\n\r\nasync function waitForCard(timeoutMs = 8000) {\r\n const started = performance.now();\r\n while (!document.querySelector('.myr5-companion-card') && performance.now() - started < timeoutMs) await new Promise(r => setTimeout(r, 50));\r\n return document.querySelector('.myr5-companion-card');\r\n}\r\n\r\nfunction onPopState() { if (!routed() && dialog?.open && location.hash !== openHash) dialog.close(); }\r\n\r\nfunction onClose() {\r\n // The native `close` event is queued. A same-task route handoff can close and immediately reopen this\r\n // shared dialog before that event is delivered; that old event must not invalidate the new open.\r\n if (dialog?.open) return;\r\n openEpoch++; clearShipVisual();\r\n document.body.dataset.shipView = '';\r\n if (coachStage) window.myr5Creature?.stage?.(coachStage); coachStage = null;\r\n window.myr5Creature?.park?.(); // the capsule stops drawing under the quilt; its next stage() (this view, a workout) wakes it\r\n const card = coachMount.querySelector('.myr5-companion-card');\r\n if (card) document.getElementById('coachMount')?.append(card); // pod.mjs's own observer re-homes it (rest vs pod)\r\n if (!routed() && location.hash === openHash) { if (pushedHash) history.back(); else history.replaceState(null, '', location.pathname + location.search); }\r\n pushedHash = false;\r\n}\r\n\r\nfunction build() {\r\n dialog = document.createElement('dialog'); dialog.className = 'ship-view'; dialog.setAttribute('aria-label', 'Your ship');\r\n dialog.innerHTML = '<div class=\"ship-view-stage\"><div class=\"ship-view-bg\" aria-hidden=\"true\"></div><div class=\"ship-view-coach\"></div></div>'\r\n  + '<p class=\"ship-view-note\" role=\"status\"></p>'\r\n  + '<div class=\"ship-view-fallback\" hidden><strong class=\"ship-view-error\" role=\"alert\" hidden></strong><p></p><button type=\"button\" class=\"ship-view-retry\" hidden>Try again</button><button type=\"button\" class=\"ship-view-download\">Download Ships &amp; worlds</button><button type=\"button\" class=\"ship-view-customize\" hidden>Open the customizer</button></div>'\r\n  + '<button type=\"button\" class=\"ship-view-close\" aria-label=\"Close\">✕</button>';\r\n document.body.append(dialog);\r\n stage = dialog.querySelector('.ship-view-stage'); bgEl = dialog.querySelector('.ship-view-bg'); coachMount = dialog.querySelector('.ship-view-coach');\r\n note = dialog.querySelector('.ship-view-note'); fallback = dialog.querySelector('.ship-view-fallback'); downloadBtn = dialog.querySelector('.ship-view-download'); customizeBtn = dialog.querySelector('.ship-view-customize');\r\n retryBtn = dialog.querySelector('.ship-view-retry'); errorLine = dialog.querySelector('.ship-view-error');\r\n retryBtn.onclick = () => { if (dialog.open) void openShipView(lastOpen); };\r\n customizeBtn.onclick = () => openCustomizer();\r\n dialog.querySelector('.ship-view-close').onclick = () => dialog.close();\r\n downloadBtn.onclick = () => { if (typeof window.myr5Packs?.open === 'function') window.myr5Packs.open(downloadBtn.dataset.pack || 'coach-ships-biomes'); else document.querySelector('.coach-dock [data-panel=\"install\"]')?.click(); };\r\n dialog.addEventListener('close', onClose);\r\n window.addEventListener('popstate', onPopState);\r\n window.addEventListener('pagehide', () => { openEpoch++; clearShipVisual(); });\r\n const accountChanged = () => { if (dialog.open && globalThis.myr5AuthenticatedAccount?.user?.id !== viewOwner) { openEpoch++; clearShipVisual(); dialog.close(); } };\r\n window.addEventListener('myr5:account-ready', accountChanged);\r\n window.addEventListener('myr5:account-cleared', accountChanged);\r\n const css = document.createElement('link'); css.rel = 'stylesheet'; css.href = '/modules/ships/ship-view.css'; document.head.append(css);\r\n}\r\n\r\n/** window.myr5Menus.ship(): opens the full-screen ship view and returns its <dialog> (the portal\r\n * uses this for its porthole reveal / fade-back, same contract as achievements). `loadCoachViewer`\r\n * is app.mjs's existing coach-capsule loader (same function \"Show my coach\" uses); `getBridge` and\r\n * `ownedShipIds` come from app.mjs's statically-bundled modules/ships/ship-view-bridge.mjs (this\r\n * file is served unbundled and cannot import that chain itself — see the header comment). Both are\r\n * overridable for tests. W2-2A: `entrance: 'always'` plays the ship's fly-in and beam-down on every open (the\r\n * portal's oval, Ian 2026-09-23) instead of once per session / once per ship; `hash` is the route this open\r\n * owns in history (the oval's is '#select'; Menu -> Ship keeps '#ship' and the once-per-session entrance). */\r\nexport async function openShipView({ loadCoachViewer, getBridge = async () => null, ownedShipIds = () => [], mountArrival = () => null, entrance = 'first', hash = HASH } = {}) {\r\n if (!dialog) build();\r\n lastOpen = arguments[0] || {};\r\n const epoch = ++openEpoch;\r\n const owner = viewOwner = globalThis.myr5AuthenticatedAccount?.user?.id;\r\n const isCurrent = () => epoch === openEpoch && dialog.open && globalThis.myr5AuthenticatedAccount?.user?.id === owner;\r\n clearShipVisual(); note.textContent = 'Preparing your coach…';\r\n const firstOpen = !dialog.open;\r\n if (firstOpen) { openHash = hash; dialog.showModal(); }\r\n if (routed()) pushedHash = false; else if (location.hash !== openHash) { pushedHash = true; history.pushState({ myr5Ship: true }, '', openHash); } else if (firstOpen) pushedHash = false;\r\n document.body.dataset.shipView = 'true';\r\n try { await loadCoachViewer?.(); } catch { /* surfaced below via the missing card */ }\r\n const card = await waitForCard();\r\n if (!isCurrent()) return dialog;\r\n // Frame the whole body in the card (feet on its bottom edge) so the ship can hover clear above its top edge.\r\n if (card) { coachMount.append(card); note.textContent = LOADING; coachStage ??= window.myr5Creature?.stats?.().stage || null; window.myr5Creature?.stage?.('overlay'); }\r\n else note.textContent = 'Coach could not load. Check your connection and try again.';\r\n try { await showShip({ getBridge, ownedShipIds, mountArrival, entrance, owner, isCurrent }); }\r\n finally { if (epoch === openEpoch && note.textContent === LOADING) note.textContent = ''; }\r\n return dialog;\r\n}\r\n\r\nasync function showShip({ getBridge, ownedShipIds, mountArrival, entrance, owner, isCurrent }) {\r\n const signedIn = !!globalThis.myr5AuthenticatedAccount?.user?.id;\r\n const custom = readJSON(`${SHIP_SETTINGS_KEY}/${owner}`);\r\n if(custom.ship===STARTER_SHIP){await showStarter({signedIn,owned:signedIn?ownedShipIds():[],isCurrent,always:entrance==='always'});return;}\r\n let bridge = null;\r\n try { bridge = await getBridge(); } catch { bridge = null; }\r\n if (!isCurrent()) { bridge?.dispose?.(); return; }\r\n const always = entrance === 'always';\r\n if (!bridge) { if (!await showStarter({ signedIn, owned: signedIn ? ownedShipIds() : [], isCurrent, always }) && isCurrent()) offerRetry('The ship could not load.'); return; }\r\n try {\r\n  const owned = bridge.ownedShipIds();\r\n  // An explicit selection takes priority over an unrelated newly earned ship's reveal.\r\n  const arrival = owned.includes(custom.ship)?null:await mountArrival({host:stage,assetBridge:bridge,isCurrent});\r\n  if (!isCurrent()) { if (arrival) arrival.dispose(); else bridge.dispose?.(); return; }\r\n  if (arrival) {\r\n   realShip = arrival;\r\n   const completed = await arrival.ready;\r\n   if (!isCurrent()) { arrival.dispose(); return; }\r\n   if (!completed) throw new Error('Ship arrival did not complete');\r\n   fallback.hidden = true; return;\r\n  }\r\n  const scene = initialScene(readJSON(RECIPE_KEY));\r\n  const shipId = owned.includes(custom.ship) ? custom.ship : owned.includes(scene.ship) ? scene.ship : owned[0];\r\n  if (always && !matchMedia('(prefers-reduced-motion: reduce)').matches) {\r\n   const { mountShipScene } = await import('./ship-intro.mjs');\r\n   const gpu = await shipGpu();\r\n   if (!isCurrent()) { gpu(); bridge.dispose?.(); return; }\r\n   const intro = mountShipScene({ host: stage, assetBridge: bridge, ship: shipId, gpu });\r\n   realShip = { dispose() { intro.dispose(); bridge.dispose?.(); } };\r\n   const arrived = await intro.ready; if (!isCurrent()) return;\r\n   if (!arrived) throw new Error('Ship arrival did not complete'); // the owned ship failed: fall back to the starter below\r\n   fallback.hidden = true; return;\r\n  }\r\n  const mounted = await mountRealShip({ stage, bridge, ship: shipId, tint: /^#[0-9a-f]{6}$/i.test(custom.tint || '') ? custom.tint : savedTint() });\r\n  if (!isCurrent()) { mounted.dispose(); return; }\r\n  realShip = mounted; fallback.hidden = true;\r\n } catch (error) {\r\n  if (!isCurrent()) { bridge.dispose?.(); return; }\r\n  if (error?.name !== 'AbortError') console.warn('Owned ship unavailable', error);\r\n  clearShipVisual(); bridge.dispose?.();\r\n  const starter = await showStarter({ signedIn, owned: signedIn ? ownedShipIds() : [], isCurrent, always });\r\n  if (isCurrent()) offerRetry(starter ? 'Your ship did not load, so the starter ship came instead.' : 'Your ship did not load.');\r\n }\r\n}\r\n","modules/ships/verified-ship-assets.mjs":"import { unpackVerifiedBundle } from '../materials/verified-bundle.mjs';\r\nimport { SHIP_STYLES, BIOMES } from './ship-scene-domain.mjs';\r\nimport { ownedShipIds } from './ship-access.mjs';\r\nimport { SHIP_CATALOG } from './ship-catalog.mjs';\r\nimport { productionMaterialTrust } from '../materials/material-config.mjs';\r\nimport { resolvePostDownloadSection } from '../materials/post-download-sections.mjs';\r\nimport { ChunkDownloader, indexedDbChunkStore, DEFAULT_LOCAL_RESOURCE_POLICY } from '../materials/chunk-delivery.mjs';\r\n\r\nconst SHIP_PATHS=Object.freeze(Object.fromEntries(SHIP_CATALOG.map(ship=>[ship.id,ship.assetPath])));\r\nconst BIOME_BUNDLE='assets/biomes.m5bundle';\r\n\r\n/** Make scene URLs only from individually reconstructed, signed, owner-checked assets. */\r\nexport async function createVerifiedShipAssetBridge({ downloader, manifest, isOwned, canUseShip=ownedShipIds, urlApi=globalThis.URL, events=globalThis.window }={}) {\r\n  if(!downloader?.readVerifiedAsset||!manifest?.assets||typeof isOwned!=='function')throw new Error('verified ship downloader and ownership check are required');\r\n  const created=new Set(),ships=new Map(),backgrounds=new Map();let disposed=false;\r\n  const dispose=()=>{if(disposed)return;disposed=true;for(const value of created)urlApi.revokeObjectURL(value);created.clear();ships.clear();backgrounds.clear();events?.removeEventListener('myr5:account-cleared',dispose);events?.removeEventListener('myr5:account-ready',onAccount);};\r\n  const assertOwned=()=>{if(disposed||!isOwned(manifest.packId)){dispose();throw new Error('ship section ownership was revoked');}};\r\n  const onAccount=()=>{if(!isOwned(manifest.packId))dispose();};\r\n  events?.addEventListener('myr5:account-cleared',dispose);events?.addEventListener('myr5:account-ready',onAccount);\r\n  const objectUrl=blob=>{const value=urlApi.createObjectURL(blob);created.add(value);return value;};\r\n  try {\r\n    for(const id of SHIP_STYLES.filter(id=>canUseShip().includes(id))){\r\n      assertOwned();const asset=await downloader.readVerifiedAsset(manifest,SHIP_PATHS[id]);assertOwned();\r\n      ships.set(id,objectUrl(new Blob([asset.bytes],{type:'model/gltf-binary'})));\r\n    }\r\n    assertOwned();const bundle=await downloader.readVerifiedAsset(manifest,BIOME_BUNDLE);assertOwned();\r\n    const plates=await unpackVerifiedBundle(bundle.bytes);\r\n    assertOwned();for(const name of BIOMES){const bytes=plates.get(`${name}.webp`);if(!bytes)throw new Error(`verified biome plate is missing: ${name}`);backgrounds.set(name,objectUrl(new Blob([bytes],{type:'image/webp'})));}assertOwned();\r\n  } catch(error){dispose();throw error;}\r\n  const api={\r\n    getShipUrl(id){assertOwned();if(!SHIP_STYLES.includes(id)||!canUseShip().includes(id))throw new Error('ship is not owned');const url=ships.get(id);if(!url)throw new Error('ship model is unavailable');return url;},\r\n    getBackgroundUrl(id){assertOwned();const url=backgrounds.get(id);if(!url)throw new Error('biome plate is unavailable');return url;},\r\n    ownedShipIds(){assertOwned();return [...canUseShip()].filter(id=>ships.has(id));},\r\n    dispose,\r\n  };\r\n  return Object.freeze(api);\r\n}\r\n\r\n/** Read-only: resolves to a ready ship/background asset bridge only if this account's owned ship\r\n * section is already downloaded and verified on this device. Never fetches chunk bytes — a\r\n * missing/partial pack always resolves to null, so opening the ship view can never start the 60MB\r\n * download on its own. */\r\nexport async function localVerifiedBridge() {\r\n const account = globalThis.myr5AuthenticatedAccount, owner = account?.user?.id;\r\n if (!owner || !ownedShipIds().length) return null;\r\n const assertOwner = () => { if (globalThis.myr5AuthenticatedAccount?.user?.id !== owner) throw new DOMException('Ship owner changed', 'AbortError'); };\r\n const trust = productionMaterialTrust(); if (!trust) return null;\r\n const resolved = await resolvePostDownloadSection('coach-ships-biomes', { trust });\r\n assertOwner();\r\n const downloader = new ChunkDownloader({ store: indexedDbChunkStore(), policy: DEFAULT_LOCAL_RESOURCE_POLICY, expectedVersion: resolved.manifest.version, manifestPublicKey: resolved.trust, ownership: async () => { assertOwner(); return owner; } });\r\n await downloader.verifyStored(resolved.manifest); // local store reads + hash checks only, no network\r\n assertOwner();\r\n return createVerifiedShipAssetBridge({ downloader, manifest: resolved.manifest, isOwned: id => id === 'coach-ships-biomes' && globalThis.myr5AuthenticatedAccount?.user?.id === owner, canUseShip: () => { assertOwner(); return ownedShipIds(); } });\r\n}\r\n","modules/vault/quest-progress.mjs":"import {gameCacheKey,gameRewardAfterCutoff} from '../coach-world/game-generation-client.mjs';\r\n// Narrow progress bridge for skill-based Vault quests. Game/workout events carry\r\n// the owner captured when the run began; account changes invalidate late callbacks.\r\nimport { EXERCISES } from '../../exercise-library.mjs';\r\nimport { performanceOwner } from '../../performance-progress.mjs';\r\nimport { HOLD_CAP_SECONDS, REP_CAP } from '../../progression-rules.mjs';\r\nimport { powerProgress, retainedPowerStyle } from '../../arcade/power/power.mjs';\r\nimport { holdStyleForExercise } from '../../arcade/workout-effects-state.mjs';\r\nimport { bump, read as readVaultState } from './vault-store.mjs';\r\n\r\nconst LIMITS = Object.freeze({\r\n  'pyramid-level': [1, 20, true], 'pyramid-complete': [1, 1, true],\r\n  'fruit-streak': [3, 64, true], 'aura-tier': [1, 6, true],\r\n  'blizzard-tier': [1, 6, true], 'fire-tier': [1, 6, true],\r\n  'lightning-tier': [1, 6, true], 'yoga-hold-seconds': [1, 60, false],\r\n});\r\n\r\nfunction validValue(counter, value) {\r\n  const rule = LIMITS[counter];\r\n  return !!rule && Number.isFinite(value) && value >= rule[0] && value <= rule[1]\r\n    && (!rule[2] || Number.isSafeInteger(value));\r\n}\r\nlet cacheOwner = null, highWater = new Map();\r\n\r\n/** Record a bounded max metric only for the owner captured at run start. */\r\nexport function recordQuestProgress({ ownerId, counter, value } = {}) {\r\n  try {\r\n    if (typeof ownerId !== 'string' || !ownerId || ownerId !== performanceOwner()\r\n        || !validValue(counter, value)) return false;\r\n    if (cacheOwner !== gameCacheKey(ownerId)) { cacheOwner = gameCacheKey(ownerId); highWater = new Map(); }\r\n    const previous = highWater.has(counter) ? highWater.get(counter)\r\n      : Number(readVaultState()?.counters?.[counter]) || 0;\r\n    if (value <= previous) { highWater.set(counter, previous); return false; }\r\n    bump(counter, value);\r\n    const persisted=Number(readVaultState()?.counters?.[counter])||0;\r\n    if(persisted>previous)highWater.set(counter,persisted);\r\n    return persisted>=value;\r\n  } catch { return false; }\r\n}\r\n\r\n/** Whether the current owner's persisted max already covers a queued event. */\r\nexport function questProgressStored(ownerId,counter,value){\r\n  try{return ownerId===performanceOwner()&&validValue(counter,value)&&(Number(readVaultState()?.counters?.[counter])||0)>=value;}catch{return false;}\r\n}\r\n\r\n/** Consume an authoritative workout-effects snapshot without changing workout flow. */\r\nexport function recordWorkoutQuestProgress(detail = {}) {\r\n  const workout = detail?.workout;\r\n  if(!gameRewardAfterCutoff({startedAt:workout?.startedAt,completedAt:Date.now()}))return false;\r\n  if (!workout || workout.stage !== 'working' || workout.paused === true\r\n      || typeof workout.ownerId !== 'string' || workout.ownerId !== performanceOwner()) return false;\r\n  const exercise = EXERCISES[workout.mode];\r\n  if (!exercise || workout.kind !== exercise.kind) return false;\r\n  if (workout.kind === 'reps' && (!Number.isFinite(workout.workingReps)\r\n      || workout.workingReps < 0 || workout.workingReps > REP_CAP)) return false;\r\n  if (workout.kind === 'hold' && (!Number.isFinite(workout.activeSeconds)\r\n      || workout.activeSeconds < 0 || workout.activeSeconds > HOLD_CAP_SECONDS)) return false;\r\n\r\n  if (workout.kind === 'hold' && exercise.group === 'yoga'\r\n      && holdStyleForExercise(exercise) === 'living') {\r\n    return recordQuestProgress({ ownerId: workout.ownerId, counter: 'yoga-hold-seconds',\r\n      value: Math.min(60, Math.floor(Number(workout.activeSeconds) || 0)) });\r\n  }\r\n\r\n  if (workout.kind === 'hold' && ['core', 'balance'].includes(exercise.group)\r\n      && retainedPowerStyle(exercise) === 'blizzard') {\r\n    const progress = powerProgress(workout);\r\n    return recordQuestProgress({ ownerId: workout.ownerId, counter: 'blizzard-tier', value: progress.tier });\r\n  }\r\n\r\n  if (workout.kind !== 'reps') return false;\r\n  const style = retainedPowerStyle(exercise), progress = powerProgress(workout);\r\n  const counter = style === 'aura' ? 'aura-tier' : style === 'floor' ? 'fire-tier'\r\n    : style === 'lightning' ? 'lightning-tier' : null;\r\n  if (!counter || progress.kind !== 'reps') return false;\r\n  return recordQuestProgress({ ownerId: workout.ownerId, counter, value: progress.tier });\r\n}\r\n","modules/vault/vault-banner.mjs":"// \"ACHIEVEMENT UNLOCKED\" banner: listens for myr5:vault-earned {ids}, shows one goal at a time at the top for 5 s, fades 0.6 s.\r\nimport {GOALS} from './vault-goals.mjs';\r\nimport {paintStyleText} from '../../workout-symbols.mjs';\r\nexport const SHOW_MS=5000,FADE_MS=600;\r\n/** Queue + timing, DOM-free so tests can drive it: show(goal) / hide() are callbacks. */\r\nexport function createBanner({show,hide,done=()=>{},setTimer=setTimeout,clearTimer=clearTimeout}){\r\n const q=[];let busy=false,t=0;\r\n const next=()=>{const g=q.shift();if(!g){busy=false;return;}busy=true;show(g);t=setTimer(()=>{hide();t=setTimer(()=>{done();next();},FADE_MS);},SHOW_MS);};\r\n return {push(ids){for(const id of ids||[]){const g=GOALS.find(x=>x.id===id);if(g)q.push(g);}if(!busy)next();},\r\n  dismiss(){if(!busy)return;clearTimer(t);hide();t=setTimer(()=>{done();next();},FADE_MS);}};\r\n}\r\nconst CSS=`.vault-banner{position:fixed;inset:auto;margin:0;overflow:visible;left:50%;top:calc(env(safe-area-inset-top,0px) + 10px);z-index:2147483000;width:min(92vw,420px);box-sizing:border-box;padding:10px 14px;transform:translate(-50%,-14px);opacity:0;transition:opacity .6s ease,transform .6s ease;pointer-events:none;text-align:center;font:12px/1.35 ui-monospace,Menlo,Consolas,monospace;color:#ff9a3c;text-shadow:0 0 6px rgba(255,140,40,.7);background:linear-gradient(160deg,rgba(76,29,120,.88),rgba(36,12,64,.92));border:2px solid #e6b94c;border-radius:12px;box-shadow:0 0 18px rgba(176,96,255,.55),inset 0 0 14px rgba(230,185,76,.18);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}\r\n.vault-banner.on{opacity:1;transform:translate(-50%,0);pointer-events:auto}\r\n.vault-banner small{display:block;letter-spacing:.2em;font-size:10px;color:#e6b94c}\r\n.vault-banner b{display:block;font-size:17px;letter-spacing:.06em;margin:2px 0;text-transform:uppercase}\r\n.vault-banner span{display:block;opacity:.85}`;\r\nexport function mountBanner(doc=document,win=window){\r\n if(win.myr5VaultBanner)return win.myr5VaultBanner;\r\n const st=doc.createElement('style');st.textContent=CSS;doc.head.append(st);\r\n const el=doc.createElement('div');el.className='vault-banner';el.setAttribute('role','status');el.setAttribute('aria-live','polite');el.setAttribute('popover','manual');\r\n const lab=doc.createElement('small'),ti=doc.createElement('b'),de=doc.createElement('span');lab.textContent='ACHIEVEMENT UNLOCKED';el.append(lab,ti,de);doc.body.append(el);\r\n const b=createBanner({show:g=>{paintStyleText(ti,g.title);de.textContent=g.clue;try{el.showPopover();}catch{el.style.display='block';}void el.offsetWidth;el.classList.add('on');},hide:()=>el.classList.remove('on'),done:()=>{try{el.hidePopover();}catch{el.style.display='';}}});\r\n el.addEventListener('click',()=>b.dismiss());\r\n win.addEventListener('myr5:vault-earned',e=>b.push(e.detail?.ids));\r\n return win.myr5VaultBanner=b;\r\n}\r\nif(typeof document!=='undefined'&&typeof window!=='undefined')mountBanner();\r\n","modules/vault/vault-door.mjs":"// The vault door: Ian's old painted cogs door (door.glb art + kit gear trains, valves, lights, pipes, weld trail) plus the\r\n// mechanical door's pistons, bolts, tubes, pulleys/cables/weights, lamps with halos, dial and real shadows, in MOM purple/gold.\r\n// Nothing here is tied to the eight portal shapes: any touch drives whatever mechanism is near it. Top-middle a MOM logo plate and a\r\n// purple CRT (orange text) show one goal's clue; the dial knob scrolls goals; one full turn of the MOM flywheel swings the door open.\r\n// Renders on demand only (wake/awakeUntil, no free-running RAF); shadows are on only while the loop is awake. AGPL-3.0-or-later.\r\nimport * as THREE from 'three';\r\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\r\nimport {MeshoptDecoder} from 'three/addons/libs/meshopt_decoder.module.js';\r\nimport {orientMatrix} from '../portal/portal-board-glb.mjs';\r\nimport {applyTorque,stepTrain,gearsFromLayout,hitGear,nearestPipe,wiggleStep,wiggleKick,valveAngle,lightLevel,heatColor,beadAlpha,stepSpark,KNOBS,pistonExt} from '../portal/portal-board-cogs.mjs';\r\nimport {releaseGraphics,claimGraphics} from '../graphics-lifecycle.mjs';\r\nimport {exitHall} from './vault-hall.mjs';\r\n\r\nconst BOARD='/pod/worlds/boards/cogs/',DOOR_GLB=BOARD+'door.glb',KIT_GLB=BOARD+'parts-kit.glb',LAYOUT=BOARD+'door-layout.json',LOGO='/pod/mom-inc-engrave.png';\r\nconst TAU=Math.PI*2,PURPLE='#7a2fc4',GOLD='#ffd36e',WALL='#171020',CREAM='#f6eddf',ORANGE='#ff9a2b';\r\n// The painted art is 1024x1792; the square door shows rows Y0..Y0+1024 (lower neon \"V\", the plain gear strip, the bottom hub).\r\nconst IMG_H=1792,Y0=700,SLAB=.07,DETENT=Math.PI/6,SWING=100*Math.PI/180,SWING_MS=1300;\r\nconst vOf=v=>(v*IMG_H-Y0)/1024; // layout v (full image) -> door v (0 top .. 1 bottom, width units)\r\n// Door-plane layout (u across, v down, both in door widths).\r\nconst L={logo:{u:.5,v:.032,w:.44,h:.054},crt:{u:.5,v:.212,w:.72,h:.29,sw:.65,sh:.25},knob:{u:.5,v:.455,r:.062},fly:{u:.5,v:.718,r:.15}};\r\nconst LAMPS=[[.1,.47],[.1,.57],[.1,.67],[.9,.47],[.9,.57],[.9,.67]],PISTONS=[[.2,.455,1],[.8,.455,-1]],PULLEYS=[[.09,.11],[.91,.11]],BOLTS=[[.06,.3,1],[.06,.56,1],[.06,.86,1],[.94,.3,-1],[.94,.56,-1],[.94,.86,-1]];\r\n\r\n// --- Pure reducers (unit-tested) -----------------------------------------------------------------\r\nexport const wrapPi=a=>a-TAU*Math.round(a/TAU);\r\nexport const wrapIndex=(i,n)=>n?((i%n)+n)%n:0;\r\n// Dial: feed it the finger's angle round the knob. Returns {st,steps}: every DETENT (30 deg) of travel is one step (+ clockwise on screen).\r\nexport function knobStep(st,angle,detent=DETENT){\r\n if(st.last==null)return {st:{last:angle,acc:0,total:0},steps:0};\r\n const d=wrapPi(angle-st.last);let acc=st.acc+d;const steps=Math.trunc(acc/detent);acc-=steps*detent;\r\n return {st:{last:angle,acc,total:st.total+d},steps};\r\n}\r\n// Flywheel: signed accumulated turn, clamped to one full turn each way; progress 0..1, open at 1.\r\nexport function flyTurn(st,angle){\r\n if(st.last==null)return {last:angle,total:st.total||0,progress:Math.min(1,Math.abs(st.total||0)/TAU)};\r\n const total=Math.max(-TAU,Math.min(TAU,st.total+wrapPi(angle-st.last)));\r\n return {last:angle,total,progress:Math.abs(total)/TAU};\r\n}\r\nexport function wrapText(text,cols){\r\n const lines=[];let line='';\r\n for(const w of String(text||'').split(/\\s+/).filter(Boolean)){\r\n  if(line&&(line+' '+w).length>cols){lines.push(line);line=w;}else line=line?line+' '+w:w;\r\n }\r\n if(line)lines.push(line);return lines;\r\n}\r\n// How long a lost context may take to come back before the vault shows its failure screen.\r\nexport const RESTORE_MS=3000;\r\n// Renderer start (tuning knobs): attempts run in order after the GPU claim; a refused context waits retryMs and tries the\r\n// next, lighter one (iOS can refuse an antialiased high-DPR context while memory from a just-lost one is still freeing).\r\nexport const VAULT_GL={attempts:[{antialias:true,pixelRatio:2,shadows:true},{antialias:false,pixelRatio:1,shadows:false},{antialias:false,pixelRatio:.75,shadows:false}],retryMs:400};\r\n// Bottom offset (px) that clears the coach dock routes.mjs moves into the open dialog, so Retry stays tappable.\r\nexport function aboveDock(gap=24){const r=document.getElementById('coachDock')?.getBoundingClientRect();return gap+(r&&r.height&&r.top<innerHeight?Math.max(0,innerHeight-r.top):0);}\r\nconst safe=(f,...a)=>{try{return f?.(...a);}catch{return undefined;}};\r\n// What the CRT shows for goal i: header, wrapped clue, footer ({earned,progress have/need}).\r\nexport function crtView(goals,i,state,cols=24){\r\n const n=goals.length;if(!n)return {head:'NO SIGNAL',lines:[],foot:'',earned:false};\r\n const g=goals[wrapIndex(i,n)],earned=!!(state?.earned?.[g.id]||safe(g.test,state));\r\n const p=safe(g.progress,state),have=p&&typeof p==='object'?p.have:null,need=p&&typeof p==='object'?p.need:null;\r\n const foot=earned?'✓ '+(g.title||g.id):have!=null&&need?have+' / '+need:typeof p==='number'?Math.round(Math.min(1,p)*100)+'%':'';\r\n return {head:'GOAL '+String(wrapIndex(i,n)+1).padStart(2,'0')+'/'+String(n).padStart(2,'0')+' · '+String(g.tier||'').toUpperCase(),lines:wrapText(g.clue,cols).slice(0,4),foot,earned,id:g.id};\r\n}\r\n\r\n// --- Textures --------------------------------------------------------------------------------------\r\nconst ctxOf=(w,h)=>{const c=document.createElement('canvas');c.width=w;c.height=h;return c.getContext('2d');};\r\nconst tex=(c,srgb=true)=>{const t=new THREE.CanvasTexture(c.canvas||c);if(srgb)t.colorSpace=THREE.SRGBColorSpace;t.anisotropy=4;return t;};\r\nconst CW_=768,CH_=312;\r\nfunction drawCrt(c,w,h,view,t=0){\r\n const g=c.createRadialGradient(w/2,h/2,10,w/2,h/2,w*.62);g.addColorStop(0,'#4a1a7a');g.addColorStop(.7,'#2a0d4a');g.addColorStop(1,'#12041f');\r\n c.fillStyle=g;c.fillRect(0,0,w,h);\r\n c.font='bold 25px ui-monospace,Menlo,Consolas,monospace';c.textBaseline='top';c.shadowColor=ORANGE;c.shadowBlur=10;c.fillStyle=ORANGE;\r\n c.globalAlpha=.75;c.fillText(view.head,30,14);c.globalAlpha=1;\r\n c.font='bold 44px ui-monospace,Menlo,Consolas,monospace';\r\n view.lines.forEach((s,i)=>c.fillText(s,30,50+i*52));\r\n c.font='bold 30px ui-monospace,Menlo,Consolas,monospace';c.fillStyle=view.earned?'#ffd36e':ORANGE;c.shadowColor=c.fillStyle;\r\n c.fillText(view.foot,30,h-46);c.shadowBlur=0;\r\n c.fillStyle='rgba(0,0,0,.28)';for(let y=(t|0)%4;y<h;y+=4)c.fillRect(0,y,w,1.6);\r\n const v=c.createRadialGradient(w/2,h/2,h*.35,w/2,h/2,w*.62);v.addColorStop(0,'rgba(0,0,0,0)');v.addColorStop(1,'rgba(0,0,0,.7)');c.fillStyle=v;c.fillRect(0,0,w,h);\r\n}\r\nfunction doorwayTexture(){\r\n // dark gunmetal wall, gold trim, faint purple glow, one small USB-A port (~8% of the door width) in the centre\r\n const c=ctxOf(512,512);c.fillStyle='#1d1f29';c.fillRect(0,0,512,512);\r\n c.strokeStyle='#0c0d13';c.lineWidth=3;for(let i=0;i<=512;i+=128){c.beginPath();c.moveTo(i,0);c.lineTo(i,512);c.moveTo(0,i);c.lineTo(512,i);c.stroke();}\r\n c.strokeStyle=GOLD;c.globalAlpha=.7;c.lineWidth=3;c.strokeRect(14,14,484,484);c.globalAlpha=1;\r\n c.fillStyle=GOLD;for(const [x,y] of [[30,30],[482,30],[30,482],[482,482]]){c.beginPath();c.arc(x,y,5,0,TAU);c.fill();}\r\n const g=c.createRadialGradient(256,256,4,256,256,230);g.addColorStop(0,'rgba(154,75,224,.55)');g.addColorStop(1,'rgba(122,47,196,0)');c.fillStyle=g;c.fillRect(0,0,512,512);\r\n c.fillStyle='rgba(255,154,43,.95)';c.font='bold 17px ui-monospace,Menlo,Consolas,monospace';c.textAlign='center';c.fillText('TAP THE PORT',256,318);\r\n c.fillStyle='#ffd36e';c.fillRect(230,242,52,28);c.fillStyle='#07040f';c.fillRect(234,246,44,20);\r\n const p=c.createLinearGradient(0,246,0,266);p.addColorStop(0,'#d9a8ff');p.addColorStop(1,'#7a2fc4');c.fillStyle=p;c.shadowColor='#b66cff';c.shadowBlur=16;c.fillRect(236,248,40,16);c.shadowBlur=0;\r\n c.fillStyle='#07040f';c.fillRect(240,254,32,5);\r\n return tex(c);\r\n}\r\nlet envTex=null;\r\nfunction studioEnv(){\r\n if(envTex)return envTex;\r\n const c=ctxOf(128,64),g=c.createLinearGradient(0,0,0,64);g.addColorStop(0,'#f4f0fa');g.addColorStop(.42,'#8a8498');g.addColorStop(.55,'#2b2535');g.addColorStop(1,'#08070b');\r\n c.fillStyle=g;c.fillRect(0,0,128,64);c.fillStyle='#fff';[[18,8,10,22],[80,4,22,9],[46,10,16,14],[104,14,8,18]].forEach(r=>c.fillRect(...r));\r\n envTex=tex(c);envTex.mapping=THREE.EquirectangularReflectionMapping;return envTex;\r\n}\r\n// MOM logo (white-on-clear PNG) as an engraved decal: dark cut with a gold lip, or plain gold.\r\nasync function logoTexture(engrave,signal){\r\n const img=await new Promise((res,rej)=>{const i=new Image();i.onload=()=>res(i);i.onerror=rej;i.src=LOGO;}).catch(()=>null);\r\n if(signal?.aborted){const error=new Error('Vault mount canceled');error.name='AbortError';throw error;}\r\n const w=512,h=Math.round(w*(img?img.height/img.width:72/363)),c=ctxOf(w,h),pass=(dx,dy,col,a)=>{\r\n  const t=ctxOf(w,h);if(img)t.drawImage(img,8,8,w-16,h-16);t.globalCompositeOperation='source-in';t.fillStyle=col;t.fillRect(0,0,w,h);\r\n  c.globalAlpha=a;c.drawImage(t.canvas,dx,dy);c.globalAlpha=1;\r\n };\r\n if(engrave){pass(3,3,'#ffd36e',.55);pass(-1.5,-1.5,'#05020a',.95);pass(0,0,'#1a1022',.9);}else{pass(2,2,'#05020a',.8);pass(0,0,GOLD,1);}\r\n const t=tex(c);return {t,aspect:h/w};\r\n}\r\n\r\n// --- Kit helpers (copied from the cogs door: not exported there) ---------------------------------\r\nfunction kitMesh(gltf,name){\r\n gltf.scene.updateMatrixWorld(true);let src=null;(name?gltf.scene.getObjectByName(name):gltf.scene)?.traverse(o=>{if(o.isMesh&&!src)src=o;});\r\n if(!src)throw Error('no mesh '+name);return src;\r\n}\r\nfunction fitModel(src,radius){\r\n const box=new THREE.Box3().setFromObject(src),rot=new THREE.Matrix4(),R=orientMatrix(box.getSize(new THREE.Vector3()));\r\n rot.set(...R[0],0,...R[1],0,...R[2],0,0,0,0,1);box.applyMatrix4(rot);\r\n const size=box.getSize(new THREE.Vector3()),k=radius/(Math.max(size.x,size.y)/2),c=box.getCenter(new THREE.Vector3());\r\n const m=new THREE.Mesh(src.geometry,src.material);m.frustumCulled=false;m.matrixAutoUpdate=false;\r\n m.matrix.makeScale(k,k,k).multiply(new THREE.Matrix4().makeTranslation(-c.x,-c.y,-c.z)).multiply(rot).multiply(src.matrixWorld);\r\n return {m,depth:size.z*k};\r\n}\r\nconst loads=new Map(),load=u=>{let p=loads.get(u);if(!p){p=new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync(u).catch(e=>{loads.delete(u);throw e;});loads.set(u,p);}return p;};\r\nconst metal=(color,m=.85,r=.38)=>new THREE.MeshStandardMaterial({color,metalness:m,roughness:r,envMap:studioEnv(),envMapIntensity:1.1});\r\n\r\n// Is (u,v) free of the CRT stack, dial, flywheel, pistons, lamps and pulley strips? r = the part's own radius.\r\nexport function blocked(u,v,r=0){\r\n const d=(a,b)=>Math.hypot(u-a,v-b),box=(b,pad)=>Math.abs(u-b.u)<b.w/2+pad&&Math.abs(v-b.v)<b.h/2+pad;\r\n return box(L.logo,r)||box(L.crt,r)||d(L.knob.u,L.knob.v)<L.knob.r*1.5+r||d(L.fly.u,L.fly.v)<L.fly.r*1.12+r\r\n  ||PISTONS.some(([pu,pv])=>Math.abs(u-pu)<.13+r&&Math.abs(v-pv)<.04+r)||LAMPS.some(([lu,lv])=>d(lu,lv)<.04+r)\r\n  ||PULLEYS.some(([pu])=>Math.abs(u-pu)<.04+r&&v<.42)||BOLTS.some(([bu,bv])=>Math.abs(u-bu)<.1+r&&Math.abs(v-bv)<.025+r)||u<.065&&v>.07&&v<.93||u>.935&&v>.07&&v<.93;\r\n}\r\n\r\n// --- The scene -----------------------------------------------------------------------------------------\r\n// host: element to fill. goals/read: the vault contract. onEnter(api): the doorway was tapped. Resolves to the door api.\r\nexport async function mountVaultDoor(host,{goals=[],read=()=>({}),onEnter,onOpen,onFatal,signal,renderAttempt,resume}={}){\r\n // A context can be created successfully and still be killed on its first heavy\r\n // draw. Phones start light; a runtime rebuild has a smaller drawing buffer again.\r\n const compact=innerWidth<600||matchMedia('(pointer:coarse)').matches;\r\n const firstAttempt=Number.isInteger(renderAttempt)?Math.max(0,Math.min(VAULT_GL.attempts.length-1,renderAttempt)):compact?1:0;\r\n let renderer=null,glAttempt=VAULT_GL.attempts[0],releaseClaim=null,scene=null,ro=null,canvas=null,frame=0,cleaned=false,stage='renderer',onContextLost=null,onContextRestored=null,loseExt=null,lost=false,parked=false,lostTimer=0,hallSession=false,abortListener=null,S=null;\r\n let firstFrameSettled=false,resolveFirstFrame,rejectFirstFrame;\r\n const firstFrame=new Promise((resolve,reject)=>{resolveFirstFrame=resolve;rejectFirstFrame=reject;});\r\n firstFrame.catch(()=>{});\r\n const ownedTextures=new Set(),sharedGeometry=new Set(),cleanupCallbacks=new Set();\r\n const cleanup=()=>{\r\n  if(cleaned)return;cleaned=true;clearTimeout(startupTimer);cancelAnimationFrame(frame);ro?.disconnect();if(onContextLost)canvas?.removeEventListener('webglcontextlost',onContextLost);if(abortListener)signal?.removeEventListener('abort',abortListener);\r\n  if(S)S.alive=false;for(const f of cleanupCallbacks)try{f();}catch{}cleanupCallbacks.clear();\r\n  if(hallSession){hallSession=false;try{exitHall();}catch{}}\r\n  if(scene)scene.traverse(o=>{try{if(o.geometry&&!sharedGeometry.has(o.geometry))o.geometry.dispose();}catch{}for(const m of [].concat(o.material||[]))try{m?.dispose?.();}catch{}});\r\n  for(const t of ownedTextures)try{t.dispose();}catch{}ownedTextures.clear();\r\n  if(renderer){\r\n   const ownedRenderer=renderer,ownedCanvas=canvas;renderer=null;\r\n   void releaseGraphics(ownedCanvas,ownedRenderer.getContext?.(),()=>{try{ownedRenderer.dispose();}catch{}try{ownedRenderer.forceContextLoss();}catch{}});\r\n  }canvas?.remove();releaseClaim?.();releaseClaim=null;\r\n };\r\n let rejectAbort;const abortPromise=new Promise((_,reject)=>{rejectAbort=reject;});abortPromise.catch(()=>{});\r\n abortListener=()=>{const error=new Error('Vault mount canceled');error.name='AbortError';if(!firstFrameSettled){firstFrameSettled=true;rejectFirstFrame(error);}cleanup();rejectAbort(error);};\r\n // A stalled asset or a renderer that never produces its first frame must reach\r\n // the existing Retry UI rather than hold \"Opening...\" forever.\r\n const startupTimer=setTimeout(()=>{\r\n  const error=new Error('Vault startup timed out. Check your connection and retry.');error.name='TimeoutError';\r\n  if(!firstFrameSettled){firstFrameSettled=true;rejectFirstFrame(error);}cleanup();rejectAbort(error);\r\n },15000);\r\n const waitForMount=promise=>Promise.race([promise,abortPromise]);\r\n try{\r\n if(signal?.aborted)abortListener();else signal?.addEventListener('abort',abortListener,{once:true});\r\n if(signal?.aborted)throw new Error('Vault mount canceled');\r\n // Every other app renderer (3D coach, portal board/tunnel/peek) gives its context back first; they rebuild when the Vault closes.\r\n stage='graphics-release';releaseClaim=claimGraphics('vault');await waitForMount(releaseClaim.ready);if(signal?.aborted)throw new Error('Vault mount canceled');\r\n stage='renderer';\r\n for(let i=firstAttempt;!renderer;i++){\r\n  glAttempt=VAULT_GL.attempts[i];\r\n  try{renderer=new THREE.WebGLRenderer({antialias:glAttempt.antialias,alpha:false,powerPreference:'low-power'});}\r\n  catch(error){if(i>=VAULT_GL.attempts.length-1)throw error;console.warn('Vault renderer refused; trying lighter settings.',error);await waitForMount(new Promise(r=>setTimeout(r,VAULT_GL.retryMs)));if(signal?.aborted)throw new Error('Vault mount canceled');}\r\n }\r\n renderer.setPixelRatio(Math.min(devicePixelRatio||1,glAttempt.pixelRatio));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.setClearColor(WALL);\r\n renderer.shadowMap.enabled=glAttempt.shadows;renderer.shadowMap.type=THREE.PCFShadowMap;\r\n loseExt=renderer.getContext().getExtension('WEBGL_lose_context');\r\n canvas=renderer.domElement;canvas.style.cssText='display:block;width:100%;height:100%;touch-action:none';canvas.setAttribute('aria-label','Vault door');\r\n stage='scene';scene=new THREE.Scene();const camera=new THREE.PerspectiveCamera(32,1,.1,40),root=new THREE.Group();scene.add(root);\r\n scene.add(new THREE.HemisphereLight('#d9c8ff','#241532',1.1));\r\n const sun=new THREE.DirectionalLight('#fff0d8',2.6);sun.position.set(-.9,1.4,3);sun.castShadow=true;sun.shadow.normalBias=.01;sun.shadow.bias=-.0005;\r\n const small=Math.min(host.clientWidth||400,host.clientHeight||400)<500;sun.shadow.mapSize.set(small?512:1024,small?512:1024);\r\n Object.assign(sun.shadow.camera,{left:-.9,right:.9,top:.9,bottom:-.9,near:.5,far:7});scene.add(sun,sun.target);\r\n const face=new THREE.Group(),pivot=new THREE.Group();pivot.position.set(-.5,0,0);face.position.set(.5,0,0);pivot.add(face);root.add(pivot);\r\n const at=(u,v,z=0)=>new THREE.Vector3(u-.5,.5-v,z),wallMat=metal('#2a1d3c',.5,.6);\r\n const mk=(geo,mat,parent,p,shadow=true)=>{const m=new THREE.Mesh(geo,mat);if(p)m.position.copy(p);m.castShadow=shadow;m.receiveShadow=true;parent.add(m);return m;};\r\n // wall + casing + doorway (static)\r\n// the vault wall: a gunmetal space-hull bulkhead that fills the frame (1 world unit = 320 px; canvas centre = world origin)\r\n stage='wall-texture';const wallTex=(()=>{const W=1024,H=2176,c=ctxOf(W,H),X=x=>W/2+x*320,Y=y=>H/2-y*320,g=c.createLinearGradient(0,0,0,H);g.addColorStop(0,'#2a2c38');g.addColorStop(.5,'#23252f');g.addColorStop(1,'#1a1b24');c.fillStyle=g;c.fillRect(0,0,W,H);\r\n  for(let i=0;i<9000;i++){c.fillStyle=Math.random()>.5?'rgba(255,255,255,.025)':'rgba(0,0,0,.05)';c.fillRect(Math.random()*W,Math.random()*H,2+Math.random()*10,1);}\r\n  const seam=(x0,y0,x1,y1)=>{c.strokeStyle='#0b0b11';c.lineWidth=4;c.beginPath();c.moveTo(x0,y0);c.lineTo(x1,y1);c.stroke();c.strokeStyle='rgba(255,255,255,.1)';c.lineWidth=1.5;c.beginPath();c.moveTo(x0+3,y0+3);c.lineTo(x1+3,y1+3);c.stroke();\r\n   const n=Math.hypot(x1-x0,y1-y0)/56|0;for(let k=1;k<n;k++){const x=x0+(x1-x0)*k/n,y=y0+(y1-y0)*k/n+(y1===y0?10:0),r=c.createRadialGradient(x-1,y-1,0,x,y,4);r.addColorStop(0,'#9fa2b5');r.addColorStop(1,'#444656');c.fillStyle=r;c.beginPath();c.arc(x,y,3.6,0,TAU);c.fill();}};\r\n  for(let y=-8;y<=8;y++)seam(0,Y(y*.85),W,Y(y*.85));\r\n  for(let y=-8;y<8;y++)for(let x=-4;x<=4;x++)seam(X(x*.8+(y&1?.4:0)),Y(y*.85),X(x*.8+(y&1?.4:0)),Y((y+1)*.85));\r\n  const gold=(y,w)=>{c.fillStyle=GOLD;c.globalAlpha=.85;c.fillRect(0,Y(y),W,w);c.fillRect(0,Y(y)+w+7,W,w*.4);c.globalAlpha=1;};gold(1.13,6);gold(-1.1,6);\r\n  const star=(cx,cy,r)=>{const g2=c.createRadialGradient(cx,cy,0,cx,cy,r);g2.addColorStop(0,'#241040');g2.addColorStop(.6,'#0c0618');g2.addColorStop(1,'#04020a');c.save();c.beginPath();c.arc(cx,cy,r,0,TAU);c.clip();c.fillStyle=g2;c.fillRect(cx-r,cy-r,2*r,2*r);\r\n   for(let i=0;i<46;i++){const q=Math.random();c.fillStyle=q>.9?GOLD:q>.7?'#d9b8ff':'#fff';c.globalAlpha=.3+Math.random()*.7;c.fillRect(cx+(Math.random()*2-1)*r,cy+(Math.random()*2-1)*r,1.6,1.6);}c.globalAlpha=1;c.restore();\r\n   c.strokeStyle=GOLD;c.lineWidth=7;c.beginPath();c.arc(cx,cy,r+3,0,TAU);c.stroke();c.strokeStyle='#0b0b11';c.lineWidth=5;c.beginPath();c.arc(cx,cy,r+10,0,TAU);c.stroke();\r\n   for(let k=0;k<12;k++){const a=k*TAU/12;c.fillStyle='#8e91a6';c.beginPath();c.arc(cx+Math.cos(a)*(r+19),cy+Math.sin(a)*(r+19),3.4,0,TAU);c.fill();}};\r\n  star(X(-.3),Y(.88),42);star(X(.3),Y(.88),42);star(X(-.3),Y(-.91),42);star(X(.3),Y(-.91),42);\r\n  const led=(x,y,col)=>{c.shadowColor=col;c.shadowBlur=14;c.fillStyle=col;c.beginPath();c.arc(x,y,5,0,TAU);c.fill();c.shadowBlur=0;};\r\n  [-.12,-.04,.04,.12].forEach((x,i)=>led(X(x),Y(.88),i%3===2?GOLD:'#b66cff'));\r\n  [-.12,-.04,.04,.12].forEach((x,i)=>led(X(x),Y(-.91),i%3===1?GOLD:'#b66cff'));\r\n  c.fillStyle='rgba(255,211,110,.8)';c.font='bold 22px ui-monospace,Menlo,Consolas,monospace';c.textAlign='center';c.fillText('M.O.M. INC. · VAULT 01',W/2,Y(-1.19));\r\n  c.fillStyle='#ffd36e';\r\n  return tex(c);})();ownedTextures.add(wallTex);\r\n const wall=new THREE.Mesh(new THREE.PlaneGeometry(3.2,6.8),new THREE.MeshBasicMaterial({map:wallTex,toneMapped:false}));wall.position.z=-.22;root.add(wall);\r\n const cas=metal('#3a3c4a',.9,.38),jamb=metal('#15131c',.7,.5),CW=1.34,HOLE=1.02;\r\n [[0,(CW+HOLE)/4,CW,(CW-HOLE)/2],[0,-(CW+HOLE)/4,CW,(CW-HOLE)/2]].forEach(([x,y,w,h])=>mk(new THREE.BoxGeometry(w,h,.24),cas,root,new THREE.Vector3(x,y,-.1)));\r\n [[-(CW+HOLE)/4,0],[(CW+HOLE)/4,0]].forEach(([x,y])=>mk(new THREE.BoxGeometry((CW-HOLE)/2,HOLE,.24),cas,root,new THREE.Vector3(x,y,-.1)));\r\n for(const [x,y,w,h] of [[0,.5,HOLE,.02],[0,-.5,HOLE,.02],[-.5,0,.02,HOLE],[.5,0,.02,HOLE]])mk(new THREE.BoxGeometry(w,h,.2),jamb,root,new THREE.Vector3(x,y,-.12),false);\r\n for(const sx of [-1,1])for(const sy of [-1,1])mk(new THREE.CylinderGeometry(.022,.022,.03,8).rotateX(Math.PI/2),metal(GOLD,.9,.3),root,new THREE.Vector3(sx*.6,sy*.6,.025));\r\n stage='doorway-texture';const screenTex=doorwayTexture();ownedTextures.add(screenTex);const screenMat=new THREE.MeshBasicMaterial({map:screenTex,toneMapped:false}),doorway=new THREE.Mesh(new THREE.PlaneGeometry(HOLE,HOLE),screenMat);doorway.position.z=-.2;root.add(doorway);\r\n // the door slab: Ian's painted art, cropped square, duotoned to MOM purple/gold\r\n stage='door-art';const doorGltf=await waitForMount(load(DOOR_GLB).catch(()=>null)),art=doorGltf?.scene.getObjectByProperty('isMesh',true)?.material.map?.clone();if(art)ownedTextures.add(art);\r\n const side=metal('#2c2e3a',.9,.4),back=metal('#4a4d5e',.9,.35);\r\n const front=new THREE.MeshStandardMaterial({color:'#fff',metalness:.35,roughness:.55,envMap:studioEnv(),envMapIntensity:.7,map:art||null});\r\n if(art){art.repeat.set(1,-1024/IMG_H);art.offset.set(0,(Y0+1024)/IMG_H);art.wrapS=art.wrapT=THREE.ClampToEdgeWrapping;art.needsUpdate=true;}\r\n front.onBeforeCompile=sh=>{sh.fragmentShader=sh.fragmentShader.replace('#include <map_fragment>','#include <map_fragment>\\n{float l=clamp(dot(diffuseColor.rgb,vec3(.2126,.7152,.0722))*1.9,0.,1.);vec3 w=vec3(.05,.052,.075),p=vec3(.5,.2,.85),g=vec3(1.,.83,.43);vec3 d=l<.5?mix(w,p,l*2.):mix(p,g,(l-.5)*2.);diffuseColor.rgb=mix(diffuseColor.rgb*vec3(.4,.4,.5),d*1.25,.85);}');};\r\n front.customProgramCacheKey=()=>'vault-duotone';\r\n const slab=new THREE.Mesh(new THREE.BoxGeometry(1,1,SLAB),[side,side,side,side,front,back]);slab.position.z=-SLAB/2;slab.castShadow=slab.receiveShadow=true;face.add(slab);\r\n const trimMat=metal(GOLD,.9,.32);for(const [x,y,w,h] of [[0,.49,1,.02],[0,-.49,1,.02],[-.49,0,.02,1],[.49,0,.02,1]])mk(new THREE.BoxGeometry(w,h,.016),trimMat,face,new THREE.Vector3(x,y,.006),false);\r\n // A continuous machined edge catches the key light across the full swing; the painted face remains inset.\r\n const bevelShape=new THREE.Shape();bevelShape.moveTo(-.49,-.49);bevelShape.lineTo(.49,-.49);bevelShape.lineTo(.49,.49);bevelShape.lineTo(-.49,.49);bevelShape.closePath();\r\n const aperture=new THREE.Path();aperture.moveTo(-.466,-.466);aperture.lineTo(-.466,.466);aperture.lineTo(.466,.466);aperture.lineTo(.466,-.466);aperture.closePath();bevelShape.holes.push(aperture);\r\n const bevel=mk(new THREE.ExtrudeGeometry(bevelShape,{depth:.014,bevelEnabled:true,bevelThickness:.006,bevelSize:.006,bevelSegments:2,steps:1}),metal('#ad9270',.88,.29),face,new THREE.Vector3(0,0,.005));bevel.name='machined-door-bevel';\r\n // Deterministic brushed microstructure is shared by the slab/edge/casing, not a new texture per part.\r\n const brush=ctxOf(128,128);brush.fillStyle='#b6b6b6';brush.fillRect(0,0,128,128);\r\n for(let y=0;y<128;y++){const value=142+(y*37%53);brush.fillStyle=`rgb(${value},${value},${value})`;brush.fillRect(0,y,128,1);}\r\n const roughness=tex(brush);roughness.colorSpace=THREE.NoColorSpace;ownedTextures.add(roughness);\r\n for(const mat of [side,back,front,cas,jamb,trimMat,bevel.material]){mat.roughnessMap=roughness;mat.needsUpdate=true;}\r\n // Baked contact depth survives the phone's no-shadow renderer profile.\r\n const contact=ctxOf(512,512);\r\n for(const [u,v,r] of [[L.knob.u,L.knob.v,.095],[L.fly.u,L.fly.v,.19],...LAMPS.map(([u,v])=>[u,v,.038])]){\r\n  const grad=contact.createRadialGradient(u*512,v*512,r*220,u*512,v*512,r*512);grad.addColorStop(0,'#000b');grad.addColorStop(1,'#0000');contact.fillStyle=grad;contact.fillRect((u-r)*512,(v-r)*512,r*1024,r*1024);\r\n }\r\n const contactTex=tex(contact);ownedTextures.add(contactTex);\r\n const contactMesh=mk(new THREE.PlaneGeometry(.98,.98),new THREE.MeshBasicMaterial({map:contactTex,transparent:true,depthWrite:false,polygonOffset:true,polygonOffsetFactor:-1}),face,new THREE.Vector3(0,0,.002),false);contactMesh.name='hardware-contact-depth';\r\n const hit=new THREE.Mesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({visible:false}));face.add(hit);\r\n // state\r\n S={goals,read,idx:0,total:0,progress:0,open:0,opening:false,target:0,flySpin:0,phase:0,pomega:0,drag:new Map(),weld:[],sparks:[],heads:new Map(),glitch:0,alive:true,knob:{last:null,acc:0,total:0},fly:{last:null,total:0}};\r\n if(resume){S.idx=wrapIndex(resume.idx,goals.length);if(resume.open){S.open=S.target=S.progress=1;S.fly.total=TAU;}}\r\n const parts={gears:[],pipes:[],valves:[],lights:[],lamps:[],bolts:[],pistons:[],weights:[],pulleys:[],spin:[]},rest=[];\r\n // kit parts from the old door layout, cropped to the square, kept clear of the new controls\r\n stage='door-kit';const [layout,kit]=await waitForMount(Promise.all([fetch(LAYOUT).then(r=>{if(!r.ok)throw new Error('layout HTTP '+r.status);return r.json();}),load(KIT_GLB)]).catch(e=>{if(e?.name==='AbortError')throw e;console.warn('vault door kit',e);return [null,null];}));kit?.scene?.traverse(o=>{if(o.geometry)sharedGeometry.add(o.geometry);});\r\n const mats=new Map(),matFor=(src,kind)=>{const k=src.material.uuid+kind;let m=mats.get(k);if(!m){m=src.material.clone();m.envMap=studioEnv();m.envMapIntensity=1.1;m.metalness=.9;m.roughness=.34;m.map=src.material.map;\r\n  m.color.set(kind==='plate'?'#8a8da2':kind==='pipe'?'#c9a8ff':kind==='light'?'#ffffff':'#ffd9a0');if(kind==='pipe'){m.emissive.set(PURPLE);m.emissiveMap=m.map;m.emissiveIntensity=.9;}if(kind==='light'){m.emissive.set(GOLD);m.emissiveMap=m.map;m.emissiveIntensity=0;}mats.set(k,m);}return m;};\r\n let topZ=.03;\r\n if(layout&&kit){\r\n  const list=[],keep=new Map(),cyc={},pick=k=>{const a=layout.parts.filter(p=>p.kind===k);cyc[k]=((cyc[k]??-1)+1)%a.length;return a[cyc[k]];};\r\n  // trains: the old lower-left cluster squeezed (positions and radii together, so teeth still mesh) into the bottom strip, mirrored to the right\r\n  const SC=.71,xf=(p,m)=>({...p,u:m?1-(.075+(p.u-.04)*SC):.075+(p.u-.04)*SC,v:.76+(vOf(p.v)-.7307)*SC,r:p.r*SC,rot:m?-(p.rot||0):p.rot});\r\n  for(const m of [0,1])layout.parts.slice(0,16).forEach((p,i)=>{const q=xf(p,m);if(blocked(q.u,q.v,q.r*.8))return;keep.set(m*100+i,list.length);list.push({...q,drives:p.drives!=null?m*100+p.drives:undefined});});\r\n  list.forEach(q=>{q.drives=q.drives!=null?keep.get(q.drives):undefined;});\r\n  // tidy mounted fittings on a symmetric grid in the free panels beside the dial and flywheel, a light row, and screws\r\n  const slot=(kind,u,v,r,rot)=>{const p=pick(kind);list.push({node:p.node,kind,u,v,r:Math.min(p.r,r),rot,layer:list.length%9+1});};\r\n  for(const u of [.24,.76])for(const v of [.52,.67])slot('pipe',u,v,.06,0);\r\n  for(const u of [.24,.76])slot('valve',u,.595,.035,0);\r\n  for(const u of [.17,.83])for(const v of [.52,.67])slot('screw',u,v,.014,0);\r\n  for(const [u,v] of [[.12,.032],[.2,.032],[.8,.032],[.88,.032],[.47,.968],[.53,.968]])slot('light',u,v,.015,0);\r\n  parts.gears=gearsFromLayout(list);\r\n  let gi=0;\r\n  list.forEach((p,i)=>{\r\n   let src;try{src=kitMesh(kit,p.node);}catch{if(p.kind==='gear')gi++;return;}\r\n   const {m,depth}=fitModel(src,p.r),g=new THREE.Group();m.material=matFor(src,p.kind);m.castShadow=m.receiveShadow=true;g.add(m);\r\n   const z=.002+(p.layer||0)*.0035+depth/2;g.position.set(p.u-.5,.5-p.v,z);g.rotation.z=(p.rot||0)*Math.PI/180;face.add(g);topZ=Math.max(topZ,z+depth/2);\r\n   const part={...p,g,rot:g.rotation.z,angle:0,omega:0,z0:z,mat:m.material,level:0,nearUntil:-Infinity,phase:i*2.3,dir:i%2?1:-1};\r\n   if(p.kind==='gear')parts.gears[gi++].g=g;else if(p.kind==='pipe')parts.pipes.push(part);else if(p.kind==='valve')parts.valves.push(part);else if(p.kind==='light'){m.material=m.material.clone();part.mat=m.material;parts.lights.push(part);}\r\n  });\r\n  // the flywheel: the kit's twenty-spoke cog, big, over the painted bottom hub, MOM logo on its boss\r\n  try{\r\n   const src=kitMesh(kit,'m19'),{m,depth}=fitModel(src,L.fly.r),g=new THREE.Group();m.material=matFor(src,'gear');m.castShadow=m.receiveShadow=true;g.add(m);\r\n   g.position.copy(at(L.fly.u,L.fly.v,.03+depth/2));face.add(g);S.flyG=g;\r\n   const boss=mk(new THREE.CylinderGeometry(.062,.062,.02,28).rotateX(Math.PI/2),metal('#2a1d3c',.8,.4),g,new THREE.Vector3(0,0,depth/2+.006));\r\n   S.bossLogo=boss;\r\n  }catch(e){console.warn('flywheel',e);}\r\n }\r\n if(!S.flyG){const g=new THREE.Group();g.position.copy(at(L.fly.u,L.fly.v,.05));g.add(new THREE.Mesh(new THREE.TorusGeometry(L.fly.r,.02,8,36),metal(GOLD,.9,.3)));face.add(g);S.flyG=g;S.bossLogo=mk(new THREE.CylinderGeometry(.062,.062,.02,28).rotateX(Math.PI/2),metal('#2a1d3c',.8,.4),g,new THREE.Vector3(0,0,.01));}\r\n const mount=new THREE.Group();face.add(mount);S.mount=mount;\r\n stage='door-markings';const [engr,gold]=await waitForMount(Promise.all([logoTexture(true,signal),logoTexture(false,signal)]));ownedTextures.add(engr.t);ownedTextures.add(gold.t);\r\n // logo plate (engraved) + CRT bezel/screen\r\n const plate=mk(new THREE.BoxGeometry(L.logo.w,L.logo.h,.02),metal('#2b1f3a',.75,.45),face,at(L.logo.u,L.logo.v,.012));\r\n const decal=new THREE.Mesh(new THREE.PlaneGeometry(L.logo.w*.9,L.logo.w*.9*engr.aspect),new THREE.MeshBasicMaterial({map:engr.t,transparent:true,depthWrite:false,toneMapped:false}));decal.position.copy(at(L.logo.u,L.logo.v,.024));face.add(decal);\r\n const bossDecal=new THREE.Mesh(new THREE.PlaneGeometry(.1,.1*gold.aspect),new THREE.MeshBasicMaterial({map:gold.t,transparent:true,depthWrite:false,toneMapped:false}));bossDecal.position.z=.0125;S.bossLogo.add(bossDecal);bossDecal.rotation.x=0;\r\n // CylinderGeometry rotated about X: its cap faces +z; the decal sits on it\r\n mk(new THREE.BoxGeometry(L.crt.w,L.crt.h,.05),metal('#1b1226',.7,.5),face,at(L.crt.u,L.crt.v,.02));\r\n const cc=ctxOf(CW_,CH_),crtTex=tex(cc),sg=new THREE.PlaneGeometry(L.crt.sw,L.crt.sh,18,10),uv=sg.attributes.uv;ownedTextures.add(crtTex);\r\n for(let i=0;i<uv.count;i++){const x=uv.getX(i)-.5,y=uv.getY(i)-.5,k=1+.12*(x*x+y*y);uv.setXY(i,.5+x*k,.5+y*k);}\r\n const crt=new THREE.Mesh(sg,new THREE.MeshBasicMaterial({map:crtTex,toneMapped:false}));crt.position.copy(at(L.crt.u,L.crt.v,.047));face.add(crt);\r\n const drawView=()=>{S.view=crtView(S.goals,S.idx,S.read());drawCrt(cc,CW_,CH_,S.view,performance.now()/60|0);crtTex.needsUpdate=true;};\r\n // dial knob with 12 static tick marks\r\n const knob=new THREE.Group();knob.position.copy(at(L.knob.u,L.knob.v,.03));face.add(knob);S.knobG=knob;\r\n mk(new THREE.CylinderGeometry(L.knob.r,L.knob.r*1.06,.05,24).rotateX(Math.PI/2),metal('#d8b25a',.95,.28),knob,new THREE.Vector3(0,0,.03));\r\n mk(new THREE.BoxGeometry(.012,L.knob.r*.8,.012),metal('#1a1022',.4,.6),knob,new THREE.Vector3(0,L.knob.r*.5,.058),false);\r\n for(let k=0;k<12;k++){const a=k*Math.PI/6;mk(new THREE.BoxGeometry(.008,.02,.008),metal(GOLD,.9,.3),face,at(L.knob.u+Math.sin(a)*L.knob.r*1.4,L.knob.v-Math.cos(a)*L.knob.r*1.4,.03),false).rotation.z=-a;}\r\n // pistons (body + rod), bolts (latch bars), tubes, pulleys + cables + weights, lamps + halos\r\n const bodyGeo=new THREE.CylinderGeometry(1,1,1,12).rotateZ(Math.PI/2),pm=metal('#ffd36e',.95,.28),rod=metal('#e8e2f0',.95,.2);\r\n for(const [u,v,s] of PISTONS){const body=mk(bodyGeo,pm,face,at(u,v,.03));body.scale.set(.1,.022,.022);const r=mk(bodyGeo,rod,face,at(u,v,.03));r.scale.set(.06,.009,.009);parts.pistons.push({r,u,s,base:u+s*.08});}\r\n for(const [u,v,s] of BOLTS){const b=mk(new THREE.BoxGeometry(.14,.03,.02),metal('#8b7fa0',.9,.3),face,at(u-s*.02,v,.026));parts.bolts.push({b,u:u-s*.02,s});}\r\n for(const u of [.035,.965])mk(new THREE.CylinderGeometry(.011,.011,.84,10),metal(PURPLE,.7,.35),face,at(u,.5,.022));\r\n for(const [u,v] of PULLEYS){const p=mk(new THREE.TorusGeometry(.04,.012,8,24),pm,face,at(u,v,.03));parts.pulleys.push(p);mk(new THREE.CylinderGeometry(.012,.012,.02,8).rotateX(Math.PI/2),pm,face,at(u,v,.03),false);\r\n  const c=mk(new THREE.BoxGeometry(.005,1,.005),metal('#3a2a50',.5,.6),face,at(u,v+.2,.026),false),w=mk(new THREE.BoxGeometry(.05,.05,.04),pm,face,at(u,v+.3,.03));parts.weights.push({c,w,u,v});}\r\n const halo=(()=>{const c=ctxOf(64,64),g=c.createRadialGradient(32,32,0,32,32,32);g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.3,'rgba(255,255,255,.4)');g.addColorStop(1,'rgba(255,255,255,0)');c.fillStyle=g;c.fillRect(0,0,64,64);return tex(c);})();ownedTextures.add(halo);\r\n LAMPS.forEach(([u,v],i)=>{\r\n  mk(new THREE.TorusGeometry(.027,.007,6,16),metal(GOLD,.9,.3),face,at(u,v,.036),false);\r\n  const core=new THREE.Mesh(new THREE.SphereGeometry(.02,12,8),new THREE.MeshBasicMaterial({color:'#2a1d12',toneMapped:false}));core.position.copy(at(u,v,.04));face.add(core);\r\n  const h=new THREE.Mesh(new THREE.PlaneGeometry(.2,.2),new THREE.MeshBasicMaterial({map:halo,color:GOLD,blending:THREE.AdditiveBlending,transparent:true,depthWrite:false,toneMapped:false,opacity:0}));h.position.copy(at(u,v,.05));h.renderOrder=20;face.add(h);\r\n  parts.lamps.push({core,h,u,v,level:0,nearUntil:-Infinity,phase:i*2.3,station:i,n:LAMPS.length});\r\n });\r\n // weld trail layers (on the door, so they swing with it): bead (normal) and glow (additive) on the door plane\r\n const layer=(add,z)=>{const c=ctxOf(512,512),t=tex(c),m=new THREE.Mesh(new THREE.PlaneGeometry(1,1),new THREE.MeshBasicMaterial({map:t,transparent:true,depthWrite:false,toneMapped:false,blending:add?THREE.AdditiveBlending:THREE.NormalBlending}));ownedTextures.add(t);m.position.z=z;m.renderOrder=add?31:30;face.add(m);return {c,t};};\r\n const bead=layer(false,topZ+.004),glow=layer(true,topZ+.006);\r\n for(const c of [...face.children])if(c!==slab&&c!==hit&&c!==mount)mount.add(c); // everything on the front rides in one group, hidden once the door swings (edge-on bits)\r\n drawView();\r\n const ringTex=(()=>{const c=ctxOf(128,128),g=c.createRadialGradient(64,64,34,64,64,62);g.addColorStop(0,'rgba(182,108,255,0)');g.addColorStop(.55,'rgba(182,108,255,.95)');g.addColorStop(1,'rgba(122,47,196,0)');c.fillStyle=g;c.fillRect(0,0,128,128);return tex(c);})();ownedTextures.add(ringTex);\r\n const ring=new THREE.Mesh(new THREE.PlaneGeometry(.26,.26),new THREE.MeshBasicMaterial({map:ringTex,transparent:true,blending:THREE.AdditiveBlending,depthWrite:false,toneMapped:false}));ring.position.set(0,0,-.19);ring.visible=false;root.add(ring);\r\n // --- camera & layout\r\n function layout2(){\r\n  const w=Math.max(1,host.clientWidth),h=Math.max(1,host.clientHeight),aspect=w/h;renderer.setSize(w,h,false);camera.aspect=aspect;\r\n  const need=1.09,fovT=Math.tan(THREE.MathUtils.degToRad(camera.fov/2)),dist=Math.max(need/2/(fovT*aspect),1.5/2/fovT);S.base=dist;\r\n  camera.position.set(0,-.08,dist);camera.lookAt(0,-.08,0);camera.updateProjectionMatrix();S.pxPerUnit=h/(2*dist*fovT);S.scale=512/Math.max(60,S.pxPerUnit);\r\n  sun.target.position.set(0,0,0);wake();\r\n }\r\n const ray=new THREE.Raycaster(),ndc=new THREE.Vector2();\r\n const pick=(cx,cy)=>{const r=canvas.getBoundingClientRect();ndc.set((cx-r.left)/r.width*2-1,-((cy-r.top)/r.height)*2+1);ray.setFromCamera(ndc,camera);\r\n  if(S.open>.6){const h=ray.intersectObject(doorway)[0];return h&&Math.hypot(h.uv.x-.5,h.uv.y-.5)*HOLE<.14?{screen:true}:{wall:true};}\r\n  const h=ray.intersectObject(hit)[0];return h?{u:h.uv.x,v:1-h.uv.y}:{wall:true};};\r\n // --- weld (compact: hot trail + sparks on the glow layer, cooling bead on the paint layer)\r\n const spark=(u,v,n)=>{for(let i=0;i<n&&S.sparks.length<KNOBS.sparkMax;i++){const a=Math.random()*TAU,sp=(KNOBS.sparkSpeedMinPx+Math.random()*(KNOBS.sparkSpeedMaxPx-KNOBS.sparkSpeedMinPx))*S.scale;S.sparks.push({x:u*512,y:v*512,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp,born:performance.now(),life:KNOBS.sparkLifeMin+Math.random()*(KNOBS.sparkLifeMax-KNOBS.sparkLifeMin),floor:Math.random()<KNOBS.sparkBounce?v*512+(30+Math.random()*80)*S.scale:Infinity});}};\r\n const weldPush=(id,u,v)=>{const prev=S.heads.get(id)||null;if(prev&&Math.hypot(u-prev.u,v-prev.v)<KNOBS.weldStep)return;const p={u,v,t:performance.now(),prev};S.weld.push(p);S.heads.set(id,p);if(S.weld.length>KNOBS.weldCap)S.weld.shift();};\r\n function weldDraw(now){\r\n  const K=KNOBS,sc=S.scale,g=glow.c,b=bead.c;g.clearRect(0,0,512,512);b.clearRect(0,0,512,512);g.lineCap=b.lineCap='round';\r\n  g.globalCompositeOperation='lighter';\r\n  for(const p of S.weld){if(!p.prev)continue;const age=now-p.t;\r\n   if(age<K.weldHotMs){const f=age/K.weldHotMs,c=heatColor(f);g.lineWidth=K.weldTrailHaloPx*sc;g.strokeStyle=`rgba(${c.r},${c.g},${c.b},${.35*(1-f)})`;g.beginPath();g.moveTo(p.prev.u*512,p.prev.v*512);g.lineTo(p.u*512,p.v*512);g.stroke();g.lineWidth=K.weldTrailWidthPx*sc;g.strokeStyle=`rgba(${c.r},${c.g},${c.b},${1-f})`;g.stroke();}\r\n   const a=beadAlpha(age);if(a>0){b.globalAlpha=a;b.lineWidth=K.weldBeadRadiusPx*2*sc;b.strokeStyle='#767880';b.beginPath();b.moveTo(p.prev.u*512,p.prev.v*512);b.lineTo(p.u*512,p.v*512);b.stroke();b.lineWidth=K.weldBeadRadiusPx*.7*sc;b.strokeStyle='#d6d8e0';b.stroke();}}\r\n  b.globalAlpha=1;\r\n  for(const s of S.sparks){const f=Math.min(1,(now-s.born)/1000/s.life),c=heatColor(f*.85);g.lineWidth=K.sparkWidthPx*sc*(1-.5*f);g.strokeStyle=`rgba(${c.r},${c.g},${c.b},${1-f*f})`;g.beginPath();g.moveTo(s.x-s.vx*K.sparkStreakS,s.y-s.vy*K.sparkStreakS);g.lineTo(s.x,s.y);g.stroke();}\r\n  for(const d of S.drag.values())if(d.weld){const R=K.torchBloomPx*sc*(.88+Math.random()*.24),x=d.u*512,y=d.v*512,gr=g.createRadialGradient(x,y,0,x,y,R);gr.addColorStop(0,'rgba(255,255,255,1)');gr.addColorStop(.25,'rgba(225,238,255,.9)');gr.addColorStop(.5,'rgba(255,160,60,.3)');gr.addColorStop(1,'rgba(255,110,30,0)');g.fillStyle=gr;g.fillRect(x-R,y-R,2*R,2*R);}\r\n  g.globalCompositeOperation='source-over';glow.t.needsUpdate=bead.t.needsUpdate=true;\r\n }\r\n // --- input\r\n const clientToUv=(e)=>pick(e.clientX,e.clientY);\r\n const angleOf=(u,v,c)=>Math.atan2(v-c.v,u-c.u);\r\n function setIdx(i){if(!S.alive)return;const n=S.goals.length;S.idx=wrapIndex(i,n);S.glitch=performance.now()+160;drawView();}\r\n function onDown(e){\r\n  const p=clientToUv(e);canvas.setPointerCapture?.(e.pointerId);\r\n  if(p.screen){S.drag.set(e.pointerId,{tap:'enter',x:e.clientX,y:e.clientY});return;}\r\n  if(p.wall){if(S.open>.6)S.drag.set(e.pointerId,{tap:'close',x:e.clientX,y:e.clientY});return;}\r\n  const d={u:p.u,v:p.v,pu:p.u,pv:p.v,t0:performance.now(),weld:false,mode:'touch',moved:false};\r\n  if(!S.opening&&S.open<.02&&Math.hypot(p.u-L.fly.u,p.v-L.fly.v)<L.fly.r*1.15){d.mode='fly';S.fly={last:null,total:S.fly.total};S.flySpin=0;S.fly=flyTurn(S.fly,angleOf(p.u,p.v,L.fly));}\r\n  else if(Math.hypot(p.u-L.knob.u,p.v-L.knob.v)<L.knob.r*1.6){d.mode='knob';S.knob={last:null,acc:0,total:S.knob.total};S.knob=knobStep(S.knob,angleOf(p.u,p.v,L.knob)).st;}\r\n  else{d.weld=true;d.gi=hitGear(parts.gears,{w:1,h:1},p.u,p.v);weldPush(e.pointerId,p.u,p.v);spark(p.u,p.v,KNOBS.sparkPressCount);}\r\n  S.drag.set(e.pointerId,d);wake();\r\n }\r\n function onMove(e){\r\n  const d=S.drag.get(e.pointerId);if(!d||d.tap)return;const p=clientToUv(e);if(p.u==null)return;\r\n  const du=p.u-d.u,dv=p.v-d.v;d.pu=d.u;d.pv=d.v;d.u=p.u;d.v=p.v;if(Math.hypot(du,dv)>.004)d.moved=true;\r\n  if(d.mode==='fly'){S.fly=flyTurn(S.fly,angleOf(p.u,p.v,L.fly));S.progress=S.fly.progress;if(S.progress>=1)openDoor();}\r\n  else if(d.mode==='knob'){const r=knobStep(S.knob,angleOf(p.u,p.v,L.knob));S.knob=r.st;if(r.steps){setIdx(S.idx+r.steps);navigator.vibrate?.(5);}}\r\n  else{\r\n   S.pomega+=Math.hypot(du,dv)*70;\r\n   // gears near the finger: tangential drag turns the whole train (scene y is up, uv y is down)\r\n   parts.gears.forEach((g,gi)=>{const dx=d.pu-g.u,dy=d.pv-g.v,r2=dx*dx+dy*dy;if(r2<(g.r*2.2)**2&&r2>1e-8)applyTorque(parts.gears,gi,-(dx*dv-dy*du)/r2*18);});\r\n   for(const pp of parts.pipes)if(Math.hypot(p.u-pp.u,p.v-pp.v)<pp.r+KNOBS.wiggleMargin)pp.omega+=wiggleKick(du,-dv,pp.rot,KNOBS.wiggleKick);\r\n   weldPush(e.pointerId,p.u,p.v);spark(p.u,p.v,Math.min(KNOBS.sparkMoveMax,Math.round(Math.hypot(du,dv)/KNOBS.sparkPerFrac)));\r\n  }\r\n  wake();\r\n }\r\n function onUp(e){\r\n  const d=S.drag.get(e.pointerId);S.drag.delete(e.pointerId);if(!d)return;\r\n  if(d.tap){if(Math.hypot(e.clientX-d.x,e.clientY-d.y)<12){if(d.tap==='enter')enter();else closeDoor();}return;}\r\n  if(d.weld){if(!d.moved){if(d.gi>=0)applyTorque(parts.gears,d.gi,KNOBS.wiggleTapKick*6);else{const i=nearestPipe(parts.pipes,{w:1,h:1},d.u,d.v,KNOBS.wiggleMargin);if(i>=0)parts.pipes[i].omega+=KNOBS.wiggleTapKick;}}S.heads.delete(e.pointerId);spark(d.u,d.v,KNOBS.sparkReleaseCount);}\r\n  wake();\r\n }\r\n const detachInput=()=>{canvas.removeEventListener('pointerdown',onDown);canvas.removeEventListener('pointermove',onMove);canvas.removeEventListener('pointerup',onUp);canvas.removeEventListener('pointercancel',onUp);};\r\n canvas.addEventListener('pointerdown',onDown);canvas.addEventListener('pointermove',onMove);canvas.addEventListener('pointerup',onUp);canvas.addEventListener('pointercancel',onUp);cleanupCallbacks.add(detachInput);\r\n // --- door open / close / enter\r\n function openDoor(){if(!S.alive||S.opening||S.target)return;S.opening=true;S.target=1;S.t0=performance.now();S.from=S.open;\r\n  window.dispatchEvent(new CustomEvent('myr5:vault-door-open'));import('./vault-hall.mjs').catch(()=>{}); // warm the hall while the door swings\r\n  import('./vault-store.mjs').then(m=>m.bump?.('door-open')).catch(()=>{});onOpen?.(api);wake();}\r\n function closeDoor(){if(!S.alive)return;S.dolly=null;S.veil?.remove();S.veil=null;S.target=0;S.t0=performance.now();S.from=S.open;S.opening=false;S.fly={last:null,total:0};S.progress=0;wake();}\r\n function enter(){\r\n  if(!S.alive||S.dolly)return;S.dolly={t0:performance.now()};\r\n  const veil=document.createElement('div');veil.style.cssText='position:absolute;inset:0;background:radial-gradient(circle,#d9a8ff,#7a2fc4 55%,#2a0d4a);opacity:0;pointer-events:none';host.append(veil);S.veil=veil;wake();onEnter?.(api);\r\n }\r\n // The hall covers the whole screen, so the door parks its GPU context (released through the shared lifecycle) while the\r\n // hall runs and restores it when the hall exits: door + hall never hold two full-screen MSAA contexts at once.\r\n function hallOffline(error){\r\n  S.dolly=null;S.veil?.remove();S.veil=null;\r\n  const hall=host.querySelector('.vh');\r\n  if(hall){const canvas=hall.querySelector('canvas');for(const kind of ['webgl2','webgl'])try{canvas?.getContext(kind)?.getExtension('WEBGL_lose_context')?.loseContext();}catch{}hall.remove();}\r\n  S.view={head:'HALL OFFLINE',lines:['YOUR SECRETS ARE SAFE.','RETRY THE 3D HALL.'],foot:'',earned:false};drawCrt(cc,CW_,CH_,S.view,0);crtTex.needsUpdate=true;\r\n  hallRetry?.remove();hallRetry=document.createElement('button');hallRetry.type='button';hallRetry.textContent='Retry 3D Hall';hallRetry.setAttribute('aria-label','Retry the 3D Achievement Hall');\r\n  hallRetry.style.cssText=`position:absolute;left:50%;bottom:${aboveDock()}px;z-index:4;transform:translateX(-50%);padding:12px 20px;border:1px solid #d4a4ff;border-radius:999px;background:#251334;color:#fff;font:600 16px system-ui`;\r\n  hallRetry.onclick=()=>{hallRetry?.remove();hallRetry=null;enter();};host.append(hallRetry);\r\n  console.error('Achievement Hall renderer failed; retry is available.',error);unpark();\r\n }\r\n function unpark(){\r\n  if(!parked)return;parked=false;\r\n  if(lost){try{loseExt?.restoreContext();}catch{}armRestoreTimeout();}else wake();\r\n }\r\n const onHallExit=e=>{\r\n  if(!S.alive||!hallSession)return;hallSession=false;\r\n  if(e.detail?.error){hallOffline(e.detail.error);return;}\r\n  S.dolly=null;S.veil?.remove();S.veil=null;unpark(); // back at the open door, not a purple veil over a parked renderer\r\n };\r\n window.addEventListener('myr5:vault-hall-exit',onHallExit);cleanupCallbacks.add(()=>window.removeEventListener('myr5:vault-hall-exit',onHallExit));\r\n function finishDolly(){\r\n  hallSession=true;\r\n  import('./vault-hall.mjs').then(async m=>{\r\n   if(!S.alive||!host.isConnected)return null;\r\n   if(!m.enterHall)throw Error('no hall');\r\n   parked=true;cancelAnimationFrame(frame);frame=0;clearTimeout(lostTimer);reconnecting.hidden=true; // a parked door has no restore deadline\r\n   if(!lost)void releaseGraphics(canvas,renderer.getContext(),()=>loseExt?.loseContext()); // enterHall waits for this release\r\n   const hall=await m.enterHall({host,door:api,goals:S.goals,read:S.read});\r\n   if(!S.alive||!host.isConnected){hall?.exit?.();return null;}\r\n   return hall;\r\n  })\r\n   .catch(error=>{\r\n    if(!S.alive||!host.isConnected){import('./vault-hall.mjs').then(m=>m.exitHall?.()).catch(()=>{});return;}\r\n    hallSession=false;hallOffline(error);\r\n   });\r\n }\r\n // --- on-demand loop\r\n let awakeUntil=0,last=0,hallRetry=null;\r\n function wake(){awakeUntil=performance.now()+1500;if(!frame&&S.alive&&!lost&&!parked){last=performance.now();frame=requestAnimationFrame(tick);}}\r\n const reduced=matchMedia?.('(prefers-reduced-motion: reduce)');\r\n const frameFailure=error=>{\r\n  try{error.vaultStage=firstFrameSettled?'runtime-render':'first-render';}catch{}\r\n  const resume={idx:S.idx,open:S.target===1};try{error.vaultResume=resume;}catch{}\r\n  if(!firstFrameSettled){firstFrameSettled=true;rejectFirstFrame(error);return;}\r\n  if(cleaned)return;S.alive=false;cleanup();onFatal?.(error,resume);\r\n };\r\n // Context loss is survivable: Android drops contexts on app switches and GPU pressure and gives them back. Pause, say so\r\n // quietly, and let three.js rebuild on restore; only a context that stays gone past RESTORE_MS is a failure.\r\n const reconnecting=document.createElement('div');reconnecting.setAttribute('role','status');reconnecting.textContent='Reconnecting 3D…';reconnecting.hidden=true;\r\n reconnecting.style.cssText='position:absolute;left:50%;top:45%;transform:translateX(-50%);z-index:3;padding:10px 18px;border:1px solid #7a2fc4;border-radius:999px;background:#171020cc;color:#f6eddf;font:600 15px system-ui;pointer-events:none';\r\n const armRestoreTimeout=()=>{clearTimeout(lostTimer);reconnecting.hidden=false;lostTimer=setTimeout(()=>{if(lost)frameFailure(new Error('WebGL context lost'));},RESTORE_MS);};\r\n onContextLost=event=>{\r\n  event.preventDefault();lost=true;cancelAnimationFrame(frame);frame=0;\r\n  if(!parked)armRestoreTimeout();\r\n };\r\n onContextRestored=()=>{\r\n  lost=false;clearTimeout(lostTimer);reconnecting.hidden=true;\r\n  if(parked)return;\r\n  // three.js re-created its GL state on this event; canvas-backed textures re-upload on the next render.\r\n  crtTex.needsUpdate=glow.t.needsUpdate=bead.t.needsUpdate=true;wake();\r\n };\r\n canvas.addEventListener('webglcontextlost',onContextLost);canvas.addEventListener('webglcontextrestored',onContextRestored);\r\n cleanupCallbacks.add(()=>{clearTimeout(lostTimer);canvas.removeEventListener('webglcontextrestored',onContextRestored);reconnecting.remove();});\r\n host.append(canvas,reconnecting);\r\n function tick(now){\r\n  frame=0;if(!S.alive)return;\r\n  try{const rdt=Math.min(.5,(now-last)/1000||.016),dt=Math.min(.05,rdt);last=now;let busy=S.drag.size>0;\r\n  renderer.shadowMap.enabled=glAttempt.shadows;\r\n  // flywheel: follows the finger; unfinished turns spring back\r\n  if(!S.drag.size&&!S.opening&&S.open<.02&&Math.abs(S.fly.total)>.01){S.fly={last:null,total:S.fly.total*Math.exp(-rdt*5)};S.progress=Math.abs(S.fly.total)/TAU;busy=true;}\r\n  S.flyG.rotation.z=-S.fly.total;\r\n  // door swing (ease in-out)\r\n  if(S.open!==S.target){const k=Math.min(1,(now-S.t0)/SWING_MS),e=k*k*(3-2*k);S.open=S.from+(S.target-S.from)*e;if(k>=1){S.open=S.target;if(!S.target)S.opening=false;}busy=true;}\r\n  pivot.rotation.y=-S.open*SWING;mount.visible=S.open<.2;\r\n  if(S.open>=.99&&S.target===1){if(!S.pulseUntil)S.pulseUntil=now+6000;}else S.pulseUntil=0;\r\n  ring.visible=S.open>=.99&&!S.dolly;if(ring.visible){const k=Math.sin(now*.006);ring.scale.setScalar(1+.22*k);ring.material.opacity=.7+.3*k;if(now<S.pulseUntil)busy=true;}camera.position.set(-.2*S.open,-.08,S.base*(1+.2*S.open));camera.lookAt(-.2*S.open,-.08,0);\r\n  if(S.dolly){const k=Math.min(1,(now-S.dolly.t0)/1000),e=k*k*(3-2*k),z0=S.base*1.2;camera.position.set(-.2*S.open*(1-e),-.08*(1-e),z0+(-.1-z0)*e);camera.lookAt(0,0,-.2);if(S.veil)S.veil.style.opacity=Math.max(0,Math.min(1,(k-.55)/.4));if(k<1)busy=true;if(k>=1&&!S.dolly.done){S.dolly.done=true;finishDolly();}}\r\n  // knob follows the finger\r\n  S.knobG.rotation.z=-S.knob.total;\r\n  // train, valves, pipes, lights, lamps\r\n  const moving=stepTrain(parts.gears,dt);parts.gears.forEach(g=>{if(g.g)g.g.rotation.z=g.angle%TAU;});\r\n  let rootOmega=0;for(const g of parts.gears)if(g.drives==null&&Math.abs(g.omega)>Math.abs(rootOmega))rootOmega=g.omega;\r\n  if(Math.abs(rootOmega)>.02||S.drag.size)for(const v of parts.valves){v.angle=valveAngle(v.angle,dt,v.dir,Math.sign(rootOmega)||1,rootOmega);v.g.rotation.z=v.rot+v.angle;}\r\n  let wob=false;\r\n  for(const p of parts.pipes){const r=wiggleStep(p.angle,p.omega,dt,KNOBS.wiggleFreqHz,KNOBS.wiggleDamping,KNOBS.wiggleMaxAngle);p.angle=r.angle;p.omega=r.omega;\r\n   if(Math.abs(p.angle)<KNOBS.wiggleRestAngle&&Math.abs(p.omega)<KNOBS.wiggleRestOmega){if(p.angle){p.angle=p.omega=0;p.g.rotation.z=p.rot;p.g.position.z=p.z0;}}\r\n   else{wob=true;p.g.rotation.z=p.rot+p.angle;p.g.position.z=p.z0+Math.abs(p.angle)/KNOBS.wiggleMaxAngle*KNOBS.wiggleBob;}}\r\n  let lit=false;const near=(u,v)=>{for(const d of S.drag.values())if(d.u!=null&&Math.hypot(d.u-u,d.v-v)<KNOBS.lightRadius)return true;return false;};\r\n  for(const l of parts.lights){if(near(l.u,l.v))l.nearUntil=now+KNOBS.lightHoldMs;const t=now<l.nearUntil?1:0;l.level=lightLevel(l.level,t,dt,KNOBS.lightRampMs,KNOBS.lightFadeMs);l.mat.emissiveIntensity=KNOBS.lightOn*l.level;if(l.level!==t)lit=true;}\r\n  for(const l of parts.lamps){if(near(l.u,l.v))l.nearUntil=now+KNOBS.lightHoldMs;const t=now<l.nearUntil||S.progress>=(l.station+.5)/l.n*.999||S.open>.02?1:0;l.level=lightLevel(l.level,t,dt,KNOBS.lightRampMs,KNOBS.lightFadeMs);\r\n   const fl=!reduced?.matches&&l.level>.95?1+Math.sin(now*.02+l.phase)*KNOBS.lightFlicker:1,k=.6+.5*l.level;l.core.material.color.set('#2a1d12').lerp(new THREE.Color(GOLD),l.level*fl);l.core.scale.setScalar(k);l.h.material.opacity=l.level*.9*fl;if(l.level!==t)lit=true;}\r\n  // pistons/weights/pulleys: pumped by finger travel and flywheel turn\r\n  S.pomega*=Math.exp(-dt*2.2);S.phase+=(S.pomega+Math.abs(rootOmega)*.5)*dt;const ph=S.phase+S.fly.total*3;\r\n  for(const p of parts.pistons)p.r.position.x=(p.u-.5)+p.s*(.04+pistonExt(.05,ph));\r\n  for(const w of parts.weights){const t=.5+.5*Math.sin(ph),y=.2+.07*t;w.w.position.y=.5-(w.v+y);w.c.scale.y=y;w.c.position.y=.5-(w.v+y/2);}\r\n  for(const p of parts.pulleys)p.rotation.z=-ph;\r\n  for(const b of parts.bolts)b.b.position.x=(b.u-.5)+b.s*.07*Math.max(S.progress,Math.min(1,S.open*5));\r\n  // weld\r\n  S.scale=512/Math.max(60,S.pxPerUnit);let n=0;for(const s of S.sparks)if((now-s.born)/1000<s.life)S.sparks[n++]=stepSpark(s,dt,KNOBS.sparkGravityPx*S.scale,KNOBS.sparkBounceKeep);S.sparks.length=n;\r\n  for(const d of S.drag.values())if(d.weld&&now-(d.ls||0)>KNOBS.sparkIdleMs){d.ls=now;spark(d.u,d.v,KNOBS.sparkIdleCount);}\r\n  let k=0;while(k<S.weld.length&&now-S.weld[k].t>KNOBS.weldHotMs+KNOBS.weldBeadMs)k++;if(k)S.weld.splice(0,k);\r\n  const weldLive=S.sparks.length>0||S.weld.length>0||[...S.drag.values()].some(d=>d.weld);\r\n  if(weldLive||S.weldWas)weldDraw(now);S.weldWas=weldLive;\r\n  if(now<S.glitch){crt.position.x=(Math.random()-.5)*.006;drawCrt(cc,CW_,CH_,S.view,now/60|0);crtTex.needsUpdate=true;busy=true;}else if(crt.position.x!==at(L.crt.u,L.crt.v).x){crt.position.x=at(L.crt.u,L.crt.v).x;drawView();}\r\n  if(renderer.getContext().isContextLost())return; // the webglcontextlost event (queued) decides what happens next\r\n  renderer.render(scene,camera);if(!firstFrameSettled){firstFrameSettled=true;resolveFirstFrame();}\r\n  const go=busy||moving||wob||lit||weldLive||S.pomega>.05||now<awakeUntil;\r\n  if(go)frame=requestAnimationFrame(tick);else renderer.shadowMap.enabled=false; // asleep: the canvas keeps its last (shadowed) frame\r\n  }catch(error){frameFailure(error);}\r\n }\r\n stage='layout';ro=new ResizeObserver(layout2);ro.observe(host);layout2();stage='ready';\r\n const api={\r\n  canvas,renderer,state:S,goals:()=>S.goals,\r\n  goTo:setIdx,step:n=>setIdx(S.idx+n),openDoor,closeDoor,enter,wake,\r\n  // client position of a door-plane point (u,v in door widths): for scripted/driven tests\r\n  project(u,v,z=0){const p=new THREE.Vector3(u-.5,.5-v,z);face.localToWorld(p);pivot.updateMatrixWorld(true);p.project(camera);const r=canvas.getBoundingClientRect();return {x:r.left+(p.x+1)/2*r.width,y:r.top+(1-p.y)/2*r.height};},\r\n  projectWorld(x,y,z){const p=new THREE.Vector3(x,y,z).project(camera),r=canvas.getBoundingClientRect();return {x:r.left+(p.x+1)/2*r.width,y:r.top+(1-p.y)/2*r.height};},\r\n  info:()=>({idx:S.idx,open:S.open,progress:S.progress,shadows:renderer.shadowMap.enabled,awake:!!frame,view:S.view,kitParts:parts.gears.length+parts.pipes.length+parts.valves.length+parts.lights.length}),\r\n  dispose(){if(cleaned)return;cleanup();},\r\n };\r\n stage='first-render';await firstFrame;stage='ready';clearTimeout(startupTimer);if(abortListener)signal?.removeEventListener('abort',abortListener);\r\n return api;\r\n }catch(error){cleanup();try{error.vaultStage=stage;}catch{}throw error;}\r\n}\r\n\r\n// Asset-free layered steel fallback. It reads the same goals; it never awards a secret itself.\r\nexport function mountBakedVault(host,{goals=[],read=()=>({}),resume}={}){\r\n const frame=document.createElement('section');frame.setAttribute('aria-label','Achievement Vault door');\r\n frame.style.cssText='position:relative;perspective:900px;width:min(360px,80vw);aspect-ratio:1;margin:12px auto;background:radial-gradient(circle,#d9a8ff,#281039 65%);border:14px ridge #63616b;box-shadow:inset 0 0 22px #000,0 8px 20px #0008';\r\n const face=document.createElement('div');face.style.cssText='position:absolute;inset:0;transform-origin:left center;transform-style:preserve-3d;transition:transform 1300ms cubic-bezier(.4,0,.6,1);border:5px ridge #b9a174;background:repeating-linear-gradient(0deg,#ffffff05 0 1px,transparent 1px 4px),linear-gradient(135deg,#47404d,#17151e 55%,#58465d);box-shadow:inset 0 0 0 8px #24222c,inset 5px 5px 14px #0009;display:grid;grid-template-rows:1fr auto auto;gap:12px;padding:20px;box-sizing:border-box';\r\n const display=document.createElement('div');display.setAttribute('role','status');display.style.cssText='white-space:pre-wrap;overflow:auto;border:6px ridge #39303e;border-radius:12px;background:#160d21;color:#ffac42;padding:10px;font:600 14px/1.4 monospace;box-shadow:inset 0 3px 12px #000';\r\n const controls=document.createElement('div');controls.style.cssText='display:flex;justify-content:center;gap:18px';\r\n const button=(label,path,fn)=>{const el=document.createElement('button');el.type='button';el.setAttribute('aria-label',label);el.title=label;el.innerHTML=`<svg width=\"28\" height=\"28\" viewBox=\"0 0 32 32\" aria-hidden=\"true\"><path d=\"${path}\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"/></svg>`;el.style.cssText='width:48px;height:48px;border:3px ridge #a59475;border-radius:50%;background:radial-gradient(circle at 30% 20%,#776d7b,#29222e);color:#ffd36e;box-shadow:0 4px 5px #0008';el.onclick=fn;return el;};\r\n let idx=wrapIndex(resume?.idx??0,goals.length),turn=0,opened=false,alive=true,hall=null;\r\n const draw=()=>{const view=crtView(goals,idx,read());display.textContent=[view.head,...view.lines,view.foot].filter(Boolean).join('\\n');};\r\n controls.append(button('Previous clue','M20 7L10 16L20 25',()=>{idx=wrapIndex(idx-1,goals.length);draw();}),button('Next clue','M12 7L22 16L12 25',()=>{idx=wrapIndex(idx+1,goals.length);draw();}));\r\n const wheel=button('Turn flywheel one quarter revolution','M16 3A13 13 0 1 0 29 16 M16 3V10 M16 22V29 M3 16H10 M22 16H29 M7 7L12 12 M20 20L25 25 M7 25L12 20 M20 12L25 7',()=>{\r\n  if(!alive||opened)return;turn=Math.min(360,turn+90);wheel.querySelector('svg').style.transform=`rotate(${turn}deg)`;wheel.setAttribute('aria-label',`Turn flywheel: ${turn} of 360 degrees`);\r\n  if(turn===360)open();\r\n });wheel.style.width=wheel.style.height='72px';wheel.style.justifySelf='center';\r\n const enter=button('Enter Achievement Hall','M5 16H27 M19 8L27 16L19 24',async()=>{if(!alive||!opened)return;try{const mod=await import('./vault-hall.mjs');if(alive)hall=await mod.enterHall({host,door:{closeDoor:close},goals,read});}catch{display.textContent='The 3D Hall is unavailable. Your earned secrets are safe.';}});\r\n enter.style.cssText+=';position:absolute;left:calc(50% - 24px);top:calc(50% - 24px)';enter.hidden=true;\r\n const close=()=>{closeControl.hidden=true;opened=false;turn=0;face.style.transform='rotateY(0deg)';face.inert=false;enter.hidden=true;wheel.querySelector('svg').style.transform='';};\r\n const closeControl=button('Close vault door','M9 8L23 22 M23 8L9 22',close);closeControl.hidden=true;closeControl.style.cssText+=';position:absolute;right:8px;bottom:8px';\r\n function open(){if(opened||!alive)return;closeControl.hidden=false;opened=true;face.style.transform='rotateY(-100deg)';face.inert=true;enter.hidden=false;window.dispatchEvent(new CustomEvent('myr5:vault-door-open'));import('./vault-store.mjs').then(m=>m.bump?.('door-open')).catch(()=>{});}\r\n frame.append(enter,closeControl,face);face.append(display,controls,wheel);host.append(frame);draw();\r\n if(matchMedia('(prefers-reduced-motion: reduce)').matches)face.style.transition='none';\r\n if(resume?.open){closeControl.hidden=false;opened=true;face.style.transform='rotateY(-100deg)';face.inert=true;enter.hidden=false;}\r\n return {dispose(){alive=false;hall?.exit?.();frame.remove();},step(n){idx=wrapIndex(idx+n,goals.length);draw();},closeDoor:close};\r\n}\r\n\r\n// The route entry: a modal dialog (#vaultPanel) holding the door. Returns the dialog.\r\nexport async function openVault(){\r\n let dlg=document.getElementById('vaultPanel');\r\n if(!dlg){dlg=document.createElement('dialog');dlg.id='vaultPanel';document.body.append(dlg);}\r\n dlg.style.cssText='position:fixed;inset:0;width:100%;height:100%;max-width:none;max-height:none;margin:0;padding:0;border:0;background:#171020;color:#f6eddf;overflow:hidden';\r\n dlg._vaultMountAbort?.abort();dlg._vaultMountAbort=null;dlg._vault?.dispose?.();dlg._vaultFallback?.dispose?.();dlg._vaultFallback=null;dlg._vault=null;const generation=(dlg._vaultGeneration||0)+1;dlg._vaultGeneration=generation;\r\n const mountController=new AbortController();dlg._vaultMountAbort=mountController;\r\n dlg.replaceChildren();const host=document.createElement('div');host.style.cssText='position:absolute;inset:0';dlg.append(host);\r\n const loading=document.createElement('div');loading.setAttribute('role','status');loading.textContent='Opening the Achievement Vault…';loading.style.cssText='position:absolute;inset:0;display:grid;place-items:center;padding:24px;text-align:center;color:#f6eddf;font:600 18px/1.5 system-ui';host.append(loading);\r\n const close=document.createElement('button');close.textContent='✕';close.setAttribute('aria-label','Close vault');close.style.cssText='position:absolute;top:10px;right:10px;z-index:2;width:44px;height:44px;border-radius:22px;border:1px solid #7a2fc4;background:#171020cc;color:#ffd36e;font-size:18px';\r\n close.onclick=()=>dlg.close();dlg.append(close);\r\n if(!dlg.open)dlg.showModal?.();\r\n dlg._vaultCloseHandler&&dlg.removeEventListener('close',dlg._vaultCloseHandler);\r\n // Native close events are queued: a prior close can arrive after this singleton\r\n // dialog has reopened. It must not cancel/dispose the replacement mount. Keep\r\n // listening until this session actually closes, then remove this exact listener.\r\n const closeHandler=()=>{if(dlg.open||dlg._vaultGeneration!==generation)return;dlg.removeEventListener('close',closeHandler);dlg._vaultGeneration++;mountController.abort();if(dlg._vaultMountAbort===mountController)dlg._vaultMountAbort=null;dlg._vault?.dispose?.();dlg._vaultFallback?.dispose?.();dlg._vaultFallback=null;dlg._vault=null;};\r\n dlg._vaultCloseHandler=closeHandler;dlg.addEventListener('close',closeHandler);\r\n const retry=document.createElement('button');retry.type='button';retry.textContent='Retry 3D';retry.hidden=true;retry.style.cssText='position:absolute;left:50%;bottom:24px;z-index:41;transform:translateX(-50%);padding:12px 20px;border:1px solid #d4a4ff;border-radius:999px;background:#251334;color:#fff;font:600 16px system-ui';\r\n retry.addEventListener('click',()=>void mount());dlg.append(retry);\r\n const showFailure=error=>{\r\n  if(!dlg.open||dlg._vaultGeneration!==generation)return;\r\n  host.replaceChildren();\r\n  const message=document.createElement('div');message.setAttribute('role','alert');message.style.cssText='position:absolute;inset:0;display:grid;place-content:center;gap:14px;padding:24px;text-align:center;color:#f6eddf;font:600 17px/1.5 system-ui';\r\n  const heading=document.createElement('strong');heading.textContent='The 3D Vault could not start.';\r\n  const detail=document.createElement('span');detail.textContent='Your earned secrets are safe. Retry when the device is ready.';\r\n  const safePart=(v,max)=>String(v||'').replace(/https?:\\/\\/\\S+/gi,'[URL]').replace(/[\\r\\n\\t]/g,' ').replace(/[^\\x20-\\x7e]/g,'?').slice(0,max);\r\n  const diagnostic=document.createElement('small');diagnostic.textContent=`${safePart(error?.vaultStage||'open-vault',48)} · ${safePart(error?.name||'Error',48)}: ${safePart(error?.message||'Unknown startup error',180)}`;\r\n  diagnostic.dataset.vaultDiagnostic='true';message.dataset.vaultErrorName=safePart(error?.name||'Error',48);message.dataset.vaultErrorStage=safePart(error?.vaultStage||'open-vault',48);message.dataset.vaultErrorMessage=safePart(error?.message||'Unknown startup error',180);\r\n  message.style.position='relative';message.style.inset='auto';message.style.padding='16px';message.append(heading,detail,diagnostic);host.append(message);\r\n  Promise.all([import('./vault-goals.mjs'),import('./vault-store.mjs')]).then(([{GOALS},{view}])=>{if(dlg.open&&dlg._vaultGeneration===generation){dlg._vaultFallback?.dispose?.();dlg._vaultFallback=mountBakedVault(host,{goals:GOALS,read:view,resume});}}).catch(()=>{});retry.style.bottom=aboveDock()+'px';retry.hidden=false;retry.disabled=false;retry.textContent='Retry 3D';\r\n  console.error('Achievement Vault renderer failed; retry is available.',error);\r\n };\r\n let mounting=null,automaticRebuilds=0,renderAttempt=dlg._vaultRenderAttempt,resume;\r\n const recover=(error,snapshot=error?.vaultResume)=>{\r\n  if(!dlg.open||dlg._vaultGeneration!==generation)return false;\r\n  // One bounded remount for a context the browser did not restore. A manual\r\n  // retry keeps this lean profile; repeated driver loss still reaches Retry.\r\n  if(!/WebGL context lost/i.test(error?.message||''))return false;\r\n  renderAttempt=dlg._vaultRenderAttempt=VAULT_GL.attempts.length-1;resume=snapshot||resume;\r\n  if(automaticRebuilds>=1)return false;automaticRebuilds++;\r\n  loading.textContent='Reconnecting the Achievement Vault…';host.replaceChildren(loading);\r\n  void Promise.resolve(mounting).then(()=>{if(dlg.open&&dlg._vaultGeneration===generation&&!mountController.signal.aborted)void mount();});\r\n  return true;\r\n };\r\n const mount=()=>{\r\n  if(mounting)return mounting;\r\n  mounting=(async()=>{\r\n  retry.disabled=true;retry.textContent='Opening…';retry.hidden=true;host.replaceChildren(loading);\r\n  try{\r\n   const [{GOALS},{view}]=await Promise.all([import('./vault-goals.mjs'),import('./vault-store.mjs')]);\r\n   const api=await mountVaultDoor(host,{goals:GOALS,read:view,signal:mountController.signal,renderAttempt,resume,onFatal:(error,snapshot)=>{dlg._vault=null;if(!recover(error,snapshot))showFailure(error);}});\r\n   if(!dlg.open||dlg._vaultGeneration!==generation){api.dispose();return null;}\r\n   dlg._vault=api;loading.remove();return api;\r\n  }catch(error){\r\n   for(const canvas of host.querySelectorAll('canvas')){\r\n    for(const kind of ['webgl2','webgl','experimental-webgl'])try{canvas.getContext(kind)?.getExtension('WEBGL_lose_context')?.loseContext();}catch{}\r\n   }\r\n   if(!dlg.open||dlg._vaultGeneration!==generation){host.replaceChildren();return null;}\r\n   if(!recover(error))showFailure(error);\r\n   return null;\r\n  }\r\n  })().finally(()=>{mounting=null;});\r\n  return mounting;\r\n };\r\n await mount();\r\n return dlg;\r\n}\r\n","modules/vault/vault-goals.mjs":"import {COACH_REQUIREMENTS} from '../../performance-catalog.mjs';\r\n// Achievement Vault: the achievement table (hall order = array order, easy -> hard) and its tunable thresholds.\r\n// Pure. Every test(v) / progress(v) is total: an empty or missing state never throws.\r\n// v = vault-store's view of the world: {counters:{name:n}, time:{name:ms}, secrets:{board:ts}, snap:{...collection snapshot}}.\r\n//  snap (taken by vault-store from the real stores): coachesHave/coachesNeed, catsHave:{track:n earned coaches},\r\n//  texturedCoaches, coloursHave/coloursNeed, texturesHave/texturesNeed (distinct ids granted vs pool), bossSkins, vaultPacksOpened.\r\n// Clues are cryptic CRT text (L10 writes the real copy; placeholders are 'clue:<id>'); the title is revealed on earn.\r\nexport const VAULT_GOALS=Object.freeze({\r\n grimMinutes:Object.freeze([5,30,120]),timeCapMinutesPerDay:20, // addTime caps grimoire time per local day\r\n share:Object.freeze([1,25]),armieRead:Object.freeze([5,100]),armieIgnored:Object.freeze([5,100]),\r\n libraryOpen:10,djSessions:10,foodScans:10,arcadeScore:30,galaSlots:8,breathModes:2,shapes:9,\r\n pyramidLevels:Object.freeze([5,10,15]),fruitStreaks:Object.freeze([5,8,12]),powerLevels:Object.freeze([3,4,6]),\r\n effectTier:4,yogaHoldSeconds:60,\r\n});\r\n// How a counter counts (bump): 'day' = +n at most once per local day (default), 'max' = best value seen,\r\n// 'keyed' = distinct keys (one per Armie letter id, gala slot, breathing mode, portal shape).\r\nexport const COUNTER_MODES=Object.freeze({'arcade-score':'max','pyramid-level':'max','pyramid-complete':'max','fruit-streak':'max','aura-tier':'max','blizzard-tier':'max','fire-tier':'max','lightning-tier':'max','yoga-hold-seconds':'max','armie-ignored':'keyed','gala-slot':'keyed','breath-mode':'keyed','shape-opened':'keyed','library-open':'keyed'});\r\nexport const MAX_COUNTER_VALUES=Object.freeze({'pyramid-level':20,'pyramid-complete':1,'fruit-streak':64,'aura-tier':6,'blizzard-tier':6,'fire-tier':6,'lightning-tier':6,'yoga-hold-seconds':60});\r\nexport const INTEGER_MAX_COUNTERS=Object.freeze(['pyramid-level','pyramid-complete','fruit-streak','aura-tier','blizzard-tier','fire-tier','lightning-tier']);\r\nconst ORIGINAL_SECRET_BOARDS=Object.freeze(['pond','wood','ice','quilt','grass','jelly']);\r\nexport const SECRET_BOARDS=Object.freeze([...ORIGINAL_SECRET_BOARDS,'pixel','clay','paper','mache']);\r\nexport const COACH_CATEGORIES=Object.freeze({meditation:'Meditation',yoga:'Yoga',cardio:'Cardio',quads:'Quads',glutes:'Glutes',chest:'Chest',arms:'Arms','martial-arts':'Martial arts'});\r\nconst num=x=>Number.isFinite(x)?x:0;\r\nconst count=(v,c)=>num(v?.counters?.[c]);\r\nconst CLUES={\r\n 'still-pond':\"Mother says the big one only rises for hands that stay.\",\r\n 'fire-starter':\"Rub the grain the wrong way. Apologise with one tap. Sweep up.\",\r\n 'shatter':\"Knock ten times on the cold window. It was never load-bearing.\",\r\n 'fold-twice':\"Make the bed like Mother taught you: up from the bottom, then across.\",\r\n 'little-way-home':\"He is very small and very far from his car. Plant him a road.\",\r\n 'boiling-point':\"Keep one finger in the pudding until the bones complain.\",\r\n 'pixel-parade':\"Every light awake. Not a single door. Mother has invited everyone.\",\r\n 'clay-breakup':\"Cover every inch. Even something soft learns to crack.\",\r\n 'paper-cut':\"A left-handed instrument is hiding where the page begins.\",\r\n 'painted-visitor':\"Give the paper a head, a body, and somewhere to run.\",\r\n 'grim-time-1':\"Stare at the book long enough and it starts staring back.\",\r\n 'grim-time-2':\"The book has learned your name. It is still reading.\",\r\n 'grim-time-3':\"You have spent a working day in a book with no words.\",\r\n 'history-open':\"Somewhere behind you is a ledger of every rep you lied about.\",\r\n 'scoreboard-link':\"Shake hands with a number that isn't yours.\",\r\n 'door-open':\"Spin the wheel. Not that one. The heavy one.\",\r\n 'hall-end':\"Walk past every ghost until there is no one left to judge you.\",\r\n 'vault-pack-opened':\"A box from the basement. Mother says don't shake it. Open it.\",\r\n 'ar-first':\"Invite a coach into your living room. Apologise for the mess.\",\r\n 'reminder-set':\"Ask Mother to nag you. She has been waiting for permission.\",\r\n 'reminder-kept':\"Let the nagging live for seven sunrises.\",\r\n 'pond-fish-seen':\"Ten times you sat still enough for the shadow to pass.\",\r\n 'armie-read-1':\"Somebody in uniform wrote to you. Have the decency.\",\r\n 'armie-ignored-1':\"Leave the soldiers on read. They will survive. Probably.\",\r\n 'armie-read-100':\"A hundred envelopes from the front. You opened every one.\",\r\n 'armie-ignored-100':\"A hundred envelopes in the bin. Command has noticed.\",\r\n 'dj-first':\"Make the spinning man play something.\",\r\n 'dj-10':\"Ten nights at the club where nobody dances but you.\",\r\n 'gala-first-part':\"Change one thing about the little pixel person.\",\r\n 'gala-all-slots':\"Try every coat in the tiny wardrobe. Every hat. Every blade.\",\r\n 'breath-all':\"Inhale every way Mother knows how to inhale.\",\r\n 'library-10':\"Thumb through the book of bodies. Ten pages.\",\r\n 'food-scan-10':\"Hold ten dinners up to the pyramid for judgement.\",\r\n 'shapes-all':\"Trace every door in the book. Even the rude ones.\",\r\n 'arcade-30':\"Pilot the bathtub. Shoot rocks. Earn thirty reasons.\",\r\n 'share-1':\"Tell one person outside the house about the house.\",\r\n 'share-25':\"Twenty-five days of telling people. Mother is so proud.\",\r\n 'coach-meditation':\"Earn someone who is very good at sitting.\",\r\n 'coach-yoga':\"Earn someone who can lick their own elbow.\",\r\n 'coach-cardio':\"Earn someone whose heart is always in a hurry.\",\r\n 'coach-quads':\"Earn someone with thighs like a planning dispute.\",\r\n 'coach-glutes':\"Earn someone who leads from behind.\",\r\n 'coach-chest':\"Earn someone who pushes the floor away.\",\r\n 'coach-arms':\"Earn someone who carries the groceries in one trip.\",\r\n 'coach-martial-arts':\"Earn someone who bows before they hurt you.\",\r\n 'boss-skin-first':\"Beat a boss so hard they come back in eight bits.\",\r\n 'texture-every-coach':\"Dress every alien in something with a feel to it.\",\r\n 'all-colours':\"Own every crayon in the box, even the ugly beige.\",\r\n 'all-six':\"Six books, six secrets. The wood, the water, the glass, the quilt, the grass, the goo.\",\r\n 'all-coaches':\"Collect the entire family. Mother will set extra places.\",\r\n 'unlocked-everything':\"Everyone. Every coat. Every colour. Then come find me.\"\r\n , 'pyramid-level-5':\"Five floors above the office. The paper is getting thin.\",\r\n 'pyramid-level-10':\"Ten floors into the fold. Keep your footing.\",\r\n 'pyramid-level-15':\"Fifteen levels up. The desk can see you now.\",\r\n 'pyramid-level-20':\"You landed on Mother's desk. She was expecting you.\",\r\n 'fruit-streak-5':\"Five clean cuts before the fruit can fall.\",\r\n 'fruit-streak-8':\"Eight in one rush. Keep your eyes ahead of your hand.\",\r\n 'fruit-streak-12':\"Twelve hits in a streak. The blade is keeping score.\",\r\n 'aura-level-3':\"Twelve counted reps. The aura is starting to take shape.\",\r\n 'aura-level-4':\"Sixteen counted reps. Keep the light under control.\",\r\n 'aura-level-6':\"Twenty-six counted reps. The aura has reached its top tier.\",\r\n 'blizzard-level-2':\"One active minute in a core or balance hold. The cold has noticed.\",\r\n 'blizzard-level-4':\"Seven minutes and forty-five active seconds. Ice holds its shape.\",\r\n 'fire-level-4':\"Sixteen push-ups or bridges in one set. Feel the ground heat up.\",\r\n 'lightning-level-4':\"Sixteen arm or shoulder reps in one set. Hold the charge.\",\r\n 'yoga-hold-60':\"Hold a yoga pose for sixty counted seconds. Breathe normally.\"\r\n};\r\nconst goal=(id,tier,title,test,progress)=>Object.freeze({id,tier,title,clue:CLUES[id]||'clue:'+id,test:v=>{try{return !!test(v||{});}catch{return false;}},...(progress?{progress:v=>{try{const p=progress(v||{});return {have:Math.max(0,Math.min(p.need,num(p.have))),need:p.need};}catch{return {have:0,need:1};}}}:{})});\r\nconst counter=(id,tier,title,name,need)=>goal(id,tier,title,v=>count(v,name)>=need,v=>({have:count(v,name),need}));\r\nconst minutes=(id,tier,title,mins)=>goal(id,tier,title,v=>num(v.time?.['grim-time'])>=mins*60000,v=>({have:Math.floor(num(v.time?.['grim-time'])/60000),need:mins}));\r\nconst secret=(id,title,board)=>goal(id,'rare',title,v=>!!v.secrets?.[board]);\r\nconst ratio=(id,tier,title,have,need)=>goal(id,tier,title,v=>num(v.snap?.[need])>0&&num(v.snap?.[have])>=num(v.snap?.[need]),v=>({have:num(v.snap?.[have]),need:Math.max(1,num(v.snap?.[need]))}));\r\nconst G=VAULT_GOALS;\r\nconst all=v=>[['coachesHave','coachesNeed'],['texturedCoaches','coachesNeed'],['coloursHave','coloursNeed'],['texturesHave','texturesNeed']].every(([h,n])=>num(v.snap?.[n])>0&&num(v.snap?.[h])>=num(v.snap?.[n]));\r\nconst DEFINED_GOALS=Object.freeze([\r\n counter('history-open','rare','Looked Back','history-open',1),\r\n counter('scoreboard-link','rare','Linked Up','scoreboard-link',1),\r\n counter('reminder-set','rare','Remembered','reminder-set',1),\r\n counter('ar-first','rare','Out in the World','ar-session',1),\r\n counter('dj-first','rare','First Request','dj-session',1),\r\n counter('share-1','rare','Spread the Word','share',G.share[0]),\r\n counter('gala-first-part','rare','New Look','gala-part',1),\r\n counter('door-open','rare','Door Opener','door-open',1),\r\n secret('still-pond','Still Waters','pond'),\r\n secret('shatter','Shatterproof No More','ice'),\r\n minutes('grim-time-1','rare','Grimoire Gazer',G.grimMinutes[0]),\r\n secret('fire-starter','Fire Starter','wood'),\r\n counter('armie-read-1','rare','Good Correspondent','armie-read',G.armieRead[0]),\r\n counter('breath-all','rare','Every Breath','breath-mode',G.breathModes),\r\n counter('library-10','rare','Movement Scholar','library-open',G.libraryOpen),\r\n counter('food-scan-10','rare','Plate Detective','food-scan',G.foodScans),\r\n counter('shapes-all','rare','Shape Collector','shape-opened',G.shapes),\r\n secret('fold-twice','Folded Twice','quilt'),\r\n counter('armie-ignored-1','rare','Left on Read','armie-ignored',G.armieIgnored[0]),\r\n counter('gala-all-slots','rare','Try Everything','gala-slot',G.galaSlots),\r\n secret('little-way-home','Little Way Home','grass'),\r\n counter('arcade-30','rare','Tub Pilot','arcade-score',G.arcadeScore),\r\n counter('dj-10','rare','Regular Request','dj-session',G.djSessions),\r\n counter('reminder-kept','rare','Kept It Up','reminder-kept',1),\r\n counter('pond-fish-seen','rare','Big Fish Believer','pond-fish',10),\r\n secret('boiling-point','Boiling Point','jelly'),\r\n secret('pixel-parade','Everybody Into the Pit','pixel'),\r\n secret('clay-breakup','Dry Company','clay'),\r\n secret('paper-cut','Against the Grain','paper'),\r\n secret('painted-visitor','He Knows Your Name','mache'),\r\n goal('hall-end','rare','Down the Hall',v=>count(v,'hall-end')>=1),\r\n goal('vault-pack-opened','rare','Pack Rat',v=>num(v.snap?.vaultPacksOpened)>=1),\r\n minutes('grim-time-2','rare','Grimoire Regular',G.grimMinutes[1]),\r\n ...Object.entries(COACH_CATEGORIES).map(([track,label])=>goal('coach-'+track,'rare','First '+label+' Coach',v=>num(v.snap?.catsHave?.[track])>=1)),\r\n counter('pyramid-level-5','rare','Paper Climber I','pyramid-level',G.pyramidLevels[0]),\r\n counter('pyramid-level-10','rare','Paper Climber II','pyramid-level',G.pyramidLevels[1]),\r\n counter('pyramid-level-15','legendary','Paper Climber III','pyramid-level',G.pyramidLevels[2]),\r\n counter('pyramid-level-20','legendary','Desk Landing','pyramid-complete',1),\r\n ...G.fruitStreaks.map((need,index)=>counter(`fruit-streak-${need}`,index===2?'legendary':'rare',`Fruit Streak ${need}`,'fruit-streak',need)),\r\n ...G.powerLevels.map((need,index)=>counter(`aura-level-${need}`,index===2?'legendary':'rare',`Aura Tier ${need}`,'aura-tier',need)),\r\n counter('blizzard-level-2','rare','Blizzard Tier 2','blizzard-tier',2),\r\n counter('blizzard-level-4','legendary','Blizzard Tier 4','blizzard-tier',4),\r\n counter('fire-level-4','rare','Fire Tier 4','fire-tier',G.effectTier),\r\n counter('lightning-level-4','rare','Lightning Tier 4','lightning-tier',G.effectTier),\r\n counter('yoga-hold-60','rare','Living Hold','yoga-hold-seconds',G.yogaHoldSeconds),\r\n goal('boss-skin-first','rare','Pixel Perfect',v=>num(v.snap?.bossSkins)>=1),\r\n counter('share-25','rare','Town Crier','share',G.share[1]),\r\n counter('armie-read-100','rare','Pen Pal','armie-read',G.armieRead[1]),\r\n counter('armie-ignored-100','rare','Professional Ghost','armie-ignored',G.armieIgnored[1]),\r\n goal('all-six','legendary','Six Secrets',v=>ORIGINAL_SECRET_BOARDS.every(b=>!!v.secrets?.[b]),v=>({have:ORIGINAL_SECRET_BOARDS.filter(b=>v.secrets?.[b]).length,need:ORIGINAL_SECRET_BOARDS.length})),\r\n minutes('grim-time-3','legendary','Grimoire Devotee',G.grimMinutes[2]),\r\n ratio('all-coaches','legendary','Full Roster','coachesHave','coachesNeed'),\r\n ratio('texture-every-coach','legendary','Dressed to Impress','texturedCoaches','coachesNeed'),\r\n ratio('all-colours','legendary','Every Colour','coloursHave','coloursNeed'),\r\n goal('unlocked-everything','legendary','Unlocked Everything',all),\r\n]);\r\nexport const ARCHIVED_GOAL_IDS=Object.freeze(['grim-time-1','grim-time-2','grim-time-3','share-1','share-25','armie-read-1','armie-read-100','armie-ignored-1','armie-ignored-100','dj-first','dj-10','library-10']);\r\nconst ARCHIVED_SET=new Set(ARCHIVED_GOAL_IDS);\r\nexport const ARCHIVED_GOALS=Object.freeze(DEFINED_GOALS.filter(g=>ARCHIVED_SET.has(g.id)));\r\nexport const GOALS=Object.freeze(DEFINED_GOALS.filter(g=>!ARCHIVED_SET.has(g.id)));\r\nexport const goalById=id=>GOALS.find(g=>g.id===id)||ARCHIVED_GOALS.find(g=>g.id===id);\r\n/** Ids whose test passes in v and are not already earned (earned: {id:ts}). */\r\nexport const newlyEarned=(v,earned={})=>GOALS.filter(g=>!earned[g.id]&&g.test(v)).map(g=>g.id);\r\nconst RANK={easy:0,medium:1,hard:2,expert:3};\r\n/** One statue coach per goal (hall order): the HIGHEST-level coaches (difficulty, then later in the catalog = later unlock), hardest goal gets the highest. -> [{goalId,coachId}] */\r\nexport function statueCoaches(goals=GOALS){\r\n const ranked=COACH_REQUIREMENTS.map((c,i)=>[c,i]).sort((a,b)=>(RANK[a[0].difficulty]??0)-(RANK[b[0].difficulty]??0)||a[1]-b[1]).map(([c])=>c.id);\r\n const top=ranked.slice(-goals.length);\r\n return goals.map((g,i)=>({goalId:g.id,coachId:top[i+top.length-goals.length]??top[i]}));\r\n}\r\n","modules/vault/vault-hall.mjs":"// Achievement Vault atomic hall: purple screen -> shrink -> a straight hall of coach statues, one per goal\r\n// (easy -> hard). Walk/inspect feel ported from play/lilboyfriend (eased route, drag/wheel/keys/held arrows,\r\n// nearest-exhibit inspect). Lazy: loaded only by the vault door. AGPL-3.0-or-later.\r\nimport {releaseGraphics,waitForGraphicsRelease} from '../graphics-lifecycle.mjs';\r\nimport {paintStyleText} from '../../workout-symbols.mjs';\r\nexport const GAP=6, ROOT='/creature/models/roster/', TOP=1.55;\r\n// One statue per goal. Coaches are chosen by unlock level, highest first, working down (only as many as there are\r\n// goals); the hall then runs low -> high so the last goal ('unlocked-everything') wears the highest-level coach.\r\nconst RANK={easy:0,medium:1,hard:2,expert:3};\r\nexport const pickCoaches=(n,reqs)=>{const top=[...reqs].sort((a,b)=>(RANK[b.difficulty]??0)-(RANK[a.difficulty]??0)).slice(0,n);return top.reverse().map(c=>c.id);};\r\nexport const coachPath=id=>ROOT+String(id).replace(/^roster\\//,'')+'.glb';\r\nexport const statueZ=i=>-GAP*(i+1),STOP=4.8; // route value at which you stand in front of statue i\r\nexport const stopAt=i=>-statueZ(i)-STOP;\r\nexport const nearestStatue=(route,n,max=1.6)=>{let b=-1,d=max;for(let i=0;i<n;i++){const e=Math.abs(stopAt(i)-route);if(e<d){b=i;d=e;}}return b;};\r\n// <=5 resident: 1 behind, current, 3 ahead.\r\nexport const residentWindow=(cur,n)=>{const o=[];for(let i=Math.max(0,cur-1);i<=Math.min(n-1,cur+3);i++)o.push(i);return o;};\r\nconst safe=(f,d)=>{try{return f()??d;}catch{return d;}};\r\nexport const isEarned=(g,state)=>!!(safe(()=>g.test(state),false)||state?.earned?.[g.id]);\r\nexport const firstUnearned=(goals,state)=>goals.findIndex(g=>!isEarned(g,state));\r\nexport function cardModel(g,state,packs=[]){\r\n const e=isEarned(g,state),at=state?.earned?.[g.id],t=at&&(at.at??at.ts??at),p=safe(()=>g.progress(state),null);\r\n return {earned:e,tier:g.tier,clue:g.clue,title:e?g.title:'???',date:e&&t&&!isNaN(new Date(t))?new Date(t).toLocaleDateString():'',\r\n  progress:!e&&p&&p.need>1?{have:Math.max(0,Math.min(p.have|0,p.need)),need:p.need}:null,\r\n  pack:e&&packs.find(id=>String(id).endsWith(':vault-'+g.id))||null};\r\n}\r\nexport function archivedCompletionRows(entries){\r\n return (Array.isArray(entries)?entries:[]).filter(entry=>entry&&entry.earned!==undefined&&entry.earned!==null&&typeof entry.title==='string')\r\n  .map(entry=>{const stamp=entry.earned&&typeof entry.earned==='object'?(entry.earned.at??entry.earned.ts):entry.earned,date=stamp!==undefined&&stamp!==null&&!isNaN(new Date(stamp))?new Date(stamp).toLocaleDateString():'';return {id:String(entry.id??''),title:entry.title,date,tier:String(entry.tier??''),packId:String(entry.packId??'')};});\r\n}\r\nexport function archivePackLabel(row,opened=false){return row?.packId?(opened?'Reward pack opened':'Reward pack earned'):'';}\r\nexport function bindArchiveDialog({dialog,openButton,closeButton,documentRef=globalThis.document}={}){\r\n let opener=null;\r\n const open=()=>{if(!dialog.hidden)return;opener=openButton;dialog.hidden=false;closeButton.focus?.();};\r\n const close=()=>{if(dialog.hidden)return;dialog.hidden=true;if(opener?.isConnected!==false)opener?.focus?.();opener=null;};\r\n const onOpen=()=>open(),onClose=()=>close(),onKey=e=>{if(dialog.hidden)return;if(e.key==='Escape'){e.preventDefault?.();e.stopImmediatePropagation?.();close();}else if(e.key==='Tab'){e.preventDefault?.();e.stopImmediatePropagation?.();closeButton.focus?.();}};\r\n openButton?.addEventListener('click',onOpen);closeButton?.addEventListener('click',onClose);documentRef?.addEventListener('keydown',onKey);\r\n return {open,close,dispose(){openButton?.removeEventListener('click',onOpen);closeButton?.removeEventListener('click',onClose);documentRef?.removeEventListener('keydown',onKey);close();}};\r\n}\r\nlet live=null;\r\n// error: the hall's context was lost and never came back; the door shows its Retry 3D Hall state.\r\nexport function exitHall(error){const l=live;live=null;if(!l)return;l.stop();window.dispatchEvent(new CustomEvent('myr5:vault-hall-exit',{detail:error instanceof Error?{error}:{}}));}\r\nconst CSS=`.vh{position:fixed;inset:0;z-index:7000;background:#08030f;overflow:hidden;touch-action:none;font:500 15px/1.4 system-ui;color:#fff4d6;user-select:none}\r\n.vh canvas{position:absolute;inset:0;width:100%;height:100%;display:block}\r\n.vh-pur{position:absolute;inset:0;background:radial-gradient(circle at 50% 50%,#b56bff,#7a2fc4 55%,#3a0f66);pointer-events:none}\r\n.vh-x{position:absolute;left:12px;top:calc(12px + env(safe-area-inset-top,0px));min-height:44px;padding:0 16px;border:2px solid #ffd36e;border-radius:22px;background:#171020cc;color:#ffd36e;font:800 14px system-ui;z-index:3}\r\n.vh-rc{position:absolute;left:50%;top:45%;transform:translateX(-50%);padding:10px 18px;border:1px solid #7a2fc4;border-radius:999px;background:#171020cc;color:#f6eddf;font:600 15px system-ui;z-index:4;pointer-events:none}.vh-rc[hidden]{display:none}\r\n.vh-pos{position:absolute;right:14px;top:calc(22px + env(safe-area-inset-top,0px));font:800 13px ui-monospace,monospace;color:#ff9c36;letter-spacing:.1em;z-index:3}\r\n.vh-arr{position:absolute;right:14px;bottom:calc(24px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:10px;z-index:3}\r\n.vh-arr button{width:56px;height:56px;border-radius:28px;border:2px solid #ffd36e;background:#171020cc;color:#ffd36e;font-size:22px;touch-action:none}\r\n.vh-card{position:absolute;left:12px;right:84px;bottom:calc(20px + env(safe-area-inset-bottom,0px));padding:14px 16px;border:2px solid #7a2fc4;border-radius:16px;background:#171020ee;box-shadow:0 0 30px #7a2fc488;z-index:3;transition:opacity .25s,transform .25s}\r\n.vh-card[hidden]{display:block;opacity:0;transform:translateY(16px);pointer-events:none}\r\n.vh-card h3{margin:2px 0 6px;font:900 19px/1.15 system-ui;color:#ffd36e}.vh-card .t{font:800 11px ui-monospace,monospace;letter-spacing:.18em;text-transform:uppercase;color:#b56bff}.vh-card .t.legendary{color:#ff9c36}\r\n.vh-card .c{font:600 16px/1.35 ui-monospace,monospace;color:#ff9c36;text-shadow:0 0 8px #ff9c3688;margin:6px 0}\r\n.vh-bar{height:10px;border-radius:5px;background:#3a2358;overflow:hidden}.vh-bar i{display:block;height:100%;background:linear-gradient(90deg,#ff9c36,#ffd36e)}.vh-card small{display:block;margin-top:4px;color:#d8c8ee}\r\n.vh-card button{margin-top:8px;min-height:44px;padding:0 18px;border:0;border-radius:12px;background:#ffd36e;color:#15101c;font:800 15px system-ui}\r\n.vh-archive-open{position:absolute;left:12px;top:calc(68px + env(safe-area-inset-top,0px));min-height:44px;padding:0 14px;border:1px solid #b56bff;border-radius:22px;background:#171020eF;color:#f2dfff;font:750 13px system-ui;z-index:3}\r\n.vh-archive[hidden]{display:none}.vh-archive:not([hidden]){position:absolute;inset:0;z-index:8;display:grid;place-items:center;padding:18px;background:#08030fdd;box-sizing:border-box}\r\n.vh-archive-panel{width:min(600px,100%);max-height:82vh;overflow:auto;padding:18px;border:2px solid #7a2fc4;border-radius:18px;background:#171020f5;box-shadow:0 0 32px #7a2fc488;box-sizing:border-box}\r\n.vh-archive-panel h2{margin:0 56px 14px 0;color:#ffd36e;font:900 22px/1.2 system-ui}.vh-archive-panel ul{list-style:none;margin:0;padding:0}.vh-archive-panel li{padding:12px 2px;border-top:1px solid #61417b}.vh-archive-panel strong{display:block;color:#fff4d6}.vh-archive-panel small{display:block;margin-top:4px;color:#d8c8ee;overflow-wrap:anywhere}.vh-archive-panel button{float:right;min-height:44px;padding:0 14px;border:1px solid #ffd36e;border-radius:12px;background:#24182f;color:#ffd36e;font:800 14px system-ui}`;\r\nconst esc=t=>String(t??'').replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\r\nconst HOLO_V=`uniform float uT;varying vec3 vN,vV;varying float vH,vY;\r\nfloat h1(float n){return fract(sin(n*91.7)*43758.5);}\r\nvoid main(){vec4 w=modelMatrix*vec4(position,1.);float g=step(.975,h1(floor(uT*7.)+floor(w.y*9.)))*.05;w.x+=g*(h1(floor(uT*7.))-.5)*2.;vY=w.y;vH=(w.y-.5)/${TOP.toFixed(2)};vN=normalize(normalMatrix*normal);vec4 mv=viewMatrix*w;vV=-mv.xyz;gl_Position=projectionMatrix*mv;}`;\r\nconst HOLO_F=`uniform float uT,uEarn,uBoost,uSeed;uniform vec3 uP,uPr,uO,uOr;varying vec3 vN,vV;varying float vH,vY;\r\nfloat h1(float n){return fract(sin(n*91.7)*43758.5);}\r\nvoid main(){float f=pow(1.-abs(dot(normalize(vN),normalize(vV))),2.2);\r\n float front=uEarn*1.25-.1,on=1.-smoothstep(front-.04,front+.04,vH),band=exp(-pow((vH-front)*9.,2.))*step(.001,uEarn)*step(uEarn,.999);\r\n vec3 base=mix(mix(uP,uPr,f),mix(uO,uOr,f),on);\r\n float scan=.55+.45*sin(vY*95.-uT*3.2),fine=.85+.15*sin(vY*420.+uT*9.);\r\n float fl=.88+.12*sin(uT*37.+uSeed)*step(.9,h1(floor(uT*6.)+uSeed));\r\n float a=(.2+f*.95)*scan*fine*fl*uBoost*(1.+on*1.2)+band*.9; // earned orange reads brighter than the purple locked ghost\r\n gl_FragColor=vec4(base*(1.+band*2.)*a*1.4,a);}`;\r\nconst BEAM_F=`uniform vec3 uC;uniform float uT;varying vec2 vU;void main(){float a=pow(1.-vU.y,2.2)*.05*(.8+.2*sin(vU.y*30.-uT*4.));gl_FragColor=vec4(uC,a);}`;\r\nconst BEAM_V=`varying vec2 vU;void main(){vU=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`;\r\n// Inside of a fibre-optic data cable: strands along the walk direction, data packets, glyph flecks.\r\nconst CABLE_V=`varying vec2 vU;varying float vD;void main(){vU=uv;vec4 mv=modelViewMatrix*vec4(position,1.);vD=-mv.z;gl_Position=projectionMatrix*mv;}`;\r\nconst CABLE_F=`uniform float uT,uLen;varying vec2 vU;varying float vD;\r\nfloat h2(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5);}\r\nvoid main(){float S=64.,cx=vU.x*S,id=floor(cx),fx=fract(cx)-.5,hs=h2(vec2(id,1.));\r\n float line=(1.-smoothstep(0.,.09,abs(fx)))*step(.35,hs);float z=vU.y*uLen;\r\n float pk=(1.-smoothstep(0.,.16,fract(z*(.02+hs*.03)-uT*(.35+hs*.9)+hs*7.)))*step(.35,hs);\r\n vec3 c1=mix(vec3(.48,.18,.77),vec3(1.,.82,.43),step(.8,h2(vec2(id,2.))));\r\n vec3 col=c1*(line*.9+pk*line*3.5+(1.-smoothstep(.1,.45,abs(fx)))*.05);\r\n vec2 cell=vec2(id,floor(z*1.7));float fl=step(.975,h2(cell))*(.5+.5*sin(uT*3.+h2(cell)*40.))*(1.-smoothstep(.1,.3,abs(fx)));\r\n col+=vec3(1.,.85,.5)*fl*.6;float a=clamp(max(max(col.r,col.g),col.b),0.,1.)*(1.-smoothstep(12.,70.,vD));\r\n gl_FragColor=vec4(col,a);}`;\r\nexport async function enterHall(arg=document.body){\r\n const host=arg?.host||(arg instanceof Element?arg:document.body); // the door passes {host,door,goals,read}\r\n exitHall();\r\n const THREE=await import('three'),{GLTFLoader}=await import('three/addons/loaders/GLTFLoader.js'),{MeshoptDecoder}=await import('three/addons/libs/meshopt_decoder.module.js');\r\n const goalsMod=await import('./vault-goals.mjs'),{GOALS}=goalsMod,store=await import('./vault-store.mjs').catch(()=>({}));\r\n const rewardPacks=await import('../../reward-packs.mjs').catch(()=>null),{COACH_REQUIREMENTS}=await import('../../performance-catalog.mjs');\r\n void store.drainQuestPending?.();\r\n const N=GOALS.length,limit=stopAt(N-1)+2,state=()=>safe(()=>store.state(),{}),readArchive=()=>archivedCompletionRows(safe(()=>store.archivedCompletions?.(),[]));\r\n // L0's statueCoaches(goals) wins when present (ids or {id}); otherwise highest-unlock-level coaches, working down.\r\n const FILES=(safe(()=>goalsMod.statueCoaches(GOALS),null)||pickCoaches(N,COACH_REQUIREMENTS)).map(c=>coachPath(c.coachId||c.id||c));\r\n const el=document.createElement('div');el.className='vh';\r\n el.innerHTML=`<style>${CSS}</style><canvas></canvas><div class=\"vh-pur\"></div><button class=\"vh-x\" aria-label=\"Leave the hall\">&larr; Exit</button>${readArchive().length?'<button class=\"vh-archive-open\" type=\"button\">Past achievements</button>':''}<div class=\"vh-pos\"></div><div class=\"vh-rc\" role=\"status\" hidden>Reconnecting 3D…</div><div class=\"vh-arr\"><button data-d=\"-1\" aria-label=\"Walk forward\">&#9650;</button><button data-d=\"1\" aria-label=\"Walk back\">&#9660;</button></div><div class=\"vh-card\" hidden></div><section class=\"vh-archive\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"vh-archive-title\" hidden><div class=\"vh-archive-panel\" tabindex=\"-1\"><button class=\"vh-archive-close\" type=\"button\" aria-label=\"Close past achievements\">Close</button><h2 id=\"vh-archive-title\">Past achievements</h2><ul></ul></div></section>`;\r\n host.append(el);const $=s=>el.querySelector(s),canvas=$('canvas'),card=$('.vh-card'),pur=$('.vh-pur'),pos=$('.vh-pos'),archiveDialog=$('.vh-archive'),archiveOpen=$('.vh-archive-open'),archiveClose=$('.vh-archive-close'),archiveList=$('.vh-archive-panel ul');\r\n const archiveBinding=archiveOpen?bindArchiveDialog({dialog:archiveDialog,openButton:archiveOpen,closeButton:archiveClose}):null;\r\n const paintArchive=()=>{const rows=readArchive();if(archiveOpen)archiveOpen.hidden=!rows.length;if(!rows.length){archiveBinding?.close();return;}archiveList.innerHTML=rows.map(row=>{const opened=row.packId&&safe(()=>rewardPacks?.openedPack(row.packId),false);const pack=archivePackLabel(row,!!opened);return `<li><strong>${esc(row.title)}</strong><small>${row.date?`Earned ${esc(row.date)}`:'Earned date unavailable'}${row.tier?` · ${esc(row.tier)}`:''}${pack?` · ${pack}`:''}</small></li>`;}).join('');};\r\n archiveOpen?.addEventListener('click',paintArchive);paintArchive();\r\n await waitForGraphicsRelease();\r\n const compact=innerWidth<600||matchMedia('(pointer:coarse)').matches;\r\n const renderer=new THREE.WebGLRenderer({canvas,antialias:!compact});renderer.setPixelRatio(Math.min(devicePixelRatio||1,compact?1:1.75));\r\n // Survive context loss like the door: pause, show a quiet status, resume when three.js has rebuilt; give up after 3 s.\r\n let lost=false,lostTimer=0;const rc=$('.vh-rc');\r\n const onLost=e=>{e.preventDefault();lost=true;cancelAnimationFrame(raf);raf=0;rc.hidden=false;clearTimeout(lostTimer);lostTimer=setTimeout(()=>{if(lost&&live)exitHall(Object.assign(new Error('WebGL context lost'),{vaultStage:'hall-runtime-render'}));},3000);};\r\n const onRestored=()=>{lost=false;clearTimeout(lostTimer);rc.hidden=true;vis();};\r\n canvas.addEventListener('webglcontextlost',onLost);canvas.addEventListener('webglcontextrestored',onRestored);\r\n const scene=new THREE.Scene();scene.background=new THREE.Color('#050210');\r\n const camera=new THREE.PerspectiveCamera(60,1,.1,140);\r\n const own=[],glow=(c,o=1)=>{const m=new THREE.MeshBasicMaterial({color:c,transparent:true,opacity:o,blending:THREE.AdditiveBlending,depthWrite:false});own.push(m);return m;};\r\n const uT={value:0},len=limit+34,zMid=-len/2+10;\r\n // Cable tube (transparent so stars/atoms show through the gaps), floor plate, gold cable rings.\r\n const tubeG=new THREE.CylinderGeometry(4.2,4.2,len,64,1,true),tubeM=new THREE.ShaderMaterial({uniforms:{uT,uLen:{value:len}},vertexShader:CABLE_V,fragmentShader:CABLE_F,transparent:true,side:THREE.BackSide,depthWrite:false,blending:THREE.AdditiveBlending});own.push(tubeG,tubeM);\r\n const tube=new THREE.Mesh(tubeG,tubeM);tube.rotation.x=Math.PI/2;tube.position.set(0,1.4,zMid);scene.add(tube);\r\n const fl=new THREE.MeshStandardMaterial({color:'#0c0818',metalness:.85,roughness:.35,emissive:'#1a0a33'}),floorG=new THREE.PlaneGeometry(7.2,len);own.push(fl,floorG);\r\n const floor=new THREE.Mesh(floorG,fl);floor.rotation.x=-Math.PI/2;floor.position.set(0,-.01,zMid);scene.add(floor);\r\n scene.add(new THREE.HemisphereLight('#b07cff','#120824',.5));\r\n const ringG=new THREE.TorusGeometry(4.15,.07,8,72),ringM=new THREE.MeshStandardMaterial({color:'#ffd36e',metalness:1,roughness:.3,emissive:'#7a5a10',emissiveIntensity:.6}),ringHalo=glow('#ffd36e',.18),haloG=new THREE.TorusGeometry(4.15,.3,6,72);own.push(ringG,ringM,haloG);\r\n const sun=new THREE.DirectionalLight('#ffe2b0',1.2);sun.position.set(2,5,3);scene.add(sun);\r\n for(let i=0;i<=N+4;i++){const z=-GAP*i+GAP/2,r=new THREE.Mesh(ringG,ringM),h=new THREE.Mesh(haloG,ringHalo);r.position.set(0,1.4,z);h.position.copy(r.position);scene.add(r,h);}\r\n // Space beyond the cable: stars + a few atoms (nucleus particle clusters with electron rings).\r\n const stN=500,st=new Float32Array(stN*3);for(let i=0;i<stN;i++)st.set([(Math.random()-.5)*70,(Math.random()-.5)*40+1,-Math.random()*len+10],i*3);\r\n const stG=new THREE.BufferGeometry();stG.setAttribute('position',new THREE.BufferAttribute(st,3));const stM=new THREE.PointsMaterial({color:'#e6d8ff',size:.14,transparent:true,opacity:.8,depthWrite:false});own.push(stG,stM);scene.add(new THREE.Points(stG,stM));\r\n const atoms=[],elecG=new THREE.SphereGeometry(.09,8,8),thin=new THREE.TorusGeometry(1,.012,6,72),pal=['#7a2fc4','#ff9c36','#ffd36e'],white=glow('#fff4d6');own.push(elecG,thin);\r\n for(let k=0;k<Math.ceil(limit/14)+2;k++){\r\n  const side=k%2?1:-1,g=new THREE.Group();g.position.set(side*(8+(k*37%6)),(k*53%9)-1,-k*14-3);\r\n  const pts=new Float32Array(90);for(let j=0;j<30;j++){const a=Math.random()*6.28,b=Math.acos(2*Math.random()-1),r=.5*Math.cbrt(Math.random());pts.set([r*Math.sin(b)*Math.cos(a),r*Math.sin(b)*Math.sin(a),r*Math.cos(b)],j*3);}\r\n  const pg=new THREE.BufferGeometry();pg.setAttribute('position',new THREE.BufferAttribute(pts,3));const pm=new THREE.PointsMaterial({color:pal[k%3],size:.16,transparent:true,blending:THREE.AdditiveBlending,depthWrite:false});own.push(pg,pm);g.add(new THREE.Points(pg,pm));\r\n  const rm=glow(pal[(k+1)%3],.5),rings=[];\r\n  for(let j=0;j<3;j++){const rg=new THREE.Group(),rr=1.3+j*.6,ring=new THREE.Mesh(thin,rm),e=new THREE.Mesh(elecG,white);ring.scale.setScalar(rr);rg.add(ring,e);rg.rotation.set(j*1.05,j*.7+k,0);rg.userData={e,rr,sp:.6+j*.35,ph:k+j};g.add(rg);rings.push(rg);}\r\n  scene.add(g);atoms.push({g,rings});\r\n }\r\n // Warp streaks for the entry shrink.\r\n const sN=320,sp=new Float32Array(sN*6);for(let i=0;i<sN;i++){const a=Math.random()*6.28,r=.6+Math.random()*3.4,z=-Math.random()*40;sp.set([Math.cos(a)*r,Math.sin(a)*r+1.4,z,Math.cos(a)*r,Math.sin(a)*r+1.4,z-1.5],i*6);}\r\n const sg=new THREE.BufferGeometry();sg.setAttribute('position',new THREE.BufferAttribute(sp,3));const sm=new THREE.LineBasicMaterial({color:'#ffe9c0',transparent:true,blending:THREE.AdditiveBlending,depthWrite:false});const streaks=new THREE.LineSegments(sg,sm);streaks.frustumCulled=false;scene.add(streaks);own.push(sg,sm);\r\n // USB drop: two rows of gold contact pins streak past (dashed lines along z) before the cable strands resolve.\r\n const pinP=[];for(let k=0;k<10;k++)for(const y of[.95,1.85])for(let d=0;d<14;d++){const x=(k-4.5)*.4,z=-d*4.2;pinP.push(x,y,z,x,y,z-2.6);}\r\n const pg2=new THREE.BufferGeometry();pg2.setAttribute('position',new THREE.Float32BufferAttribute(pinP,3));const pinM=new THREE.LineBasicMaterial({color:'#ffd36e',transparent:true,blending:THREE.AdditiveBlending,depthWrite:false});const pins=new THREE.LineSegments(pg2,pinM);pins.frustumCulled=false;scene.add(pins);own.push(pg2,pinM);\r\n // Hologram statues: one ShaderMaterial program (cloned per statue for its own earn state), purple locked -> orange earned.\r\n const col=h=>new THREE.Color(h),holo=new THREE.ShaderMaterial({uniforms:{uT,uEarn:{value:0},uBoost:{value:1},uSeed:{value:0},uP:{value:col('#7a2fc4')},uPr:{value:col('#b07cff')},uO:{value:col('#ff9a2e')},uOr:{value:col('#ffd08a')}},vertexShader:HOLO_V,fragmentShader:HOLO_F,transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide});own.push(holo);\r\n const pedG=new THREE.CylinderGeometry(.7,.85,.5,24),pedM=new THREE.MeshStandardMaterial({color:'#15101f',metalness:.9,roughness:.3}),capG=new THREE.TorusGeometry(.72,.035,6,32),capM=new THREE.MeshStandardMaterial({color:'#ffd36e',metalness:1,roughness:.25,emissive:'#8a6a18',emissiveIntensity:.8}),haloG2=new THREE.CircleGeometry(1.1,32),nextM=glow('#b07cff',.5),beamG=new THREE.CylinderGeometry(.5,.72,2.1,24,1,true);own.push(pedG,pedM,capG,capM,haloG2,beamG);\r\n const pulseM=glow('#ffd36e',.9),pulseG=new THREE.TorusGeometry(.85,.05,8,48),pulse=new THREE.Mesh(pulseG,pulseM);own.push(pulseG);pulse.rotation.x=Math.PI/2;pulse.visible=false;scene.add(pulse);\r\n const nextLight=new THREE.PointLight('#b07cff',0,8,2);scene.add(nextLight);\r\n const statues=GOALS.map((g,i)=>{const grp=new THREE.Group(),x=i%2?1.7:-1.7;grp.position.set(x,0,statueZ(i));grp.rotation.y=i%2?-.5:.5;\r\n  const ped=new THREE.Mesh(pedG,pedM);ped.position.y=.25;const cap=new THREE.Mesh(capG,capM);cap.rotation.x=Math.PI/2;cap.position.y=.5;\r\n  const halo=new THREE.Mesh(haloG2,nextM);halo.rotation.x=-Math.PI/2;halo.position.y=.02;halo.visible=false;\r\n  const mat=holo.clone();mat.uniforms={...holo.uniforms,uEarn:{value:0},uBoost:{value:1},uSeed:{value:i*3.7}};own.push(mat);\r\n  const bm=new THREE.ShaderMaterial({uniforms:{uC:{value:col('#7a2fc4')},uT},vertexShader:BEAM_V,fragmentShader:BEAM_F,transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide});own.push(bm);\r\n  const beam=new THREE.Mesh(beamG,bm);beam.position.y=.5+1.05;beam.scale.y=-1;\r\n  grp.add(ped,cap,halo,beam);scene.add(grp);return {g,i,grp,halo,mat,bm,model:null,loading:0,tk:0,earned:false,anim:-1};});\r\n const loader=new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);\r\n // Keep model decoding bounded on mobile while preserving the same five-statue resident window.\r\n const modelQueue=[];let activeModelLoads=0;const pumpModelLoads=()=>{while(activeModelLoads<1&&modelQueue.length){const job=modelQueue.shift();activeModelLoads++;Promise.resolve().then(job.run).catch(e=>console.warn('vault statue',e)).finally(()=>{activeModelLoads--;pumpModelLoads();});}};\r\n // Earn state: instant on load/refresh, 0.6 s sweep up the statue when newly earned while the hall is open.\r\n function setEarn(s,animate){const e=!!(s.g&&isEarned(s.g,state()));if(e===s.earned)return;s.earned=e;if(e&&animate&&s.model)s.anim=0;else{s.anim=-1;s.mat.uniforms.uEarn.value=e?1:0;}s.bm.uniforms.uC.value.set(e?'#ff9a2e':'#7a2fc4');}\r\n function freeModel(m){m.traverse(o=>{if(o.isMesh)o.geometry.dispose();});}\r\n function drop(s){if(!s.model)return;s.grp.remove(s.model);freeModel(s.model);s.model=null;}\r\n function load(s){if(s.model||s.loading)return;const tk=s.loading=++s.tk;modelQueue.push({run:async()=>{if(s.loading!==tk||!live||!want.includes(s.i)){if(s.loading===tk)s.loading=0;return;}try{const m=(await loader.loadAsync(FILES[s.i])).scene;\r\n  // Original materials are never shown: free them right away, wear the hologram.\r\n  m.traverse(o=>{if(!o.isMesh)return;for(const mm of[].concat(o.material)){for(const v of Object.values(mm))if(v?.isTexture)v.dispose();mm.dispose();}o.material=s.mat;});\r\n  if(s.loading!==tk||s.model||!live||!want.includes(s.i)){freeModel(m);return;}\r\n  const box=new THREE.Box3().setFromObject(m),sz=box.getSize(new THREE.Vector3()),k=TOP/Math.max(sz.y,.001),c=box.getCenter(new THREE.Vector3());m.scale.setScalar(k);m.position.set(-c.x*k,.5-box.min.y*k,-c.z*k);\r\n  s.model=m;s.grp.add(m);}catch(e){throw e;}finally{if(s.loading===tk)s.loading=0;}}});pumpModelLoads();}\r\n let want=[];function stream(c){want=residentWindow(c,N);statues.forEach(s=>{if(want.includes(s.i))load(s);else{s.loading=0;drop(s);}});}\r\n // Walk state (ported from lilboyfriend).\r\n let fixT=-1,route=0,target=0,vel=0,held=0,cur=-1,insp=-1,blend=0,nextI=-1,started=performance.now(),last=started,raf=0,reached=false;\r\n const clamp=(v,a,b)=>Math.max(a,Math.min(b,v)),move=d=>{target=clamp(target+d,0,limit);};\r\n const packId=g=>`reward-pack:secret:vault-${g.id}`;\r\n let rsum=null;import('../../reward-pack-ui.mjs').then(m=>{rsum=m.rewardSummary;if(insp>=0)showCard(insp);},()=>{});\r\n const showCard=i=>{const g=GOALS[i],m=cardModel(g,state(),safe(()=>rewardPacks.unopenedPacks(),[]));card.hidden=false;const got=m.earned&&safe(()=>rewardPacks.openedPack(packId(g)),null),list=got&&rsum?got.rewards.map(r=>{const q=rsum({reward:r,category:r.category});return `<div class=\"c\">${esc(q.title)} <small style=\"display:inline\">${esc(q.detail)}</small></div>`;}).join(''):'';\r\n  card.innerHTML=`<div class=\"t ${m.tier}\">secret &middot; ${m.earned?'unlocked':'locked'}${i===nextI?' &middot; next':''}</div>`+(m.earned?`<h3>${esc(m.title)}</h3>${m.date?`<small>Earned ${m.date}</small>`:''}${list||(m.pack?'<button data-pack>Open pack</button>':'')}`:`<div class=\"c\">${esc(m.clue)}</div>`+(m.progress?`<div class=\"vh-bar\"><i style=\"width:${100*m.progress.have/m.progress.need}%\"></i></div><small>${m.progress.have} / ${m.progress.need}</small>`:''));const h=card.querySelector('h3');if(h)paintStyleText(h,m.title);};\r\n const refresh=()=>{nextI=firstUnearned(GOALS,state());statues.forEach(s=>{setEarn(s,true);s.halo.visible=s.i===nextI;});const n=statues[nextI];pulse.visible=!!n;nextLight.intensity=n?6:0;if(n){nextLight.position.set(n.grp.position.x*.6,1.8,n.grp.position.z+1.4);pulse.position.set(n.grp.position.x,.06,n.grp.position.z);}if(insp>=0)showCard(insp);};\r\n card.addEventListener('click',async e=>{const b=e.target.closest?.('[data-pack]');if(!b||!rewardPacks)return;const g=GOALS[insp];if(!g)return;b.disabled=true;\r\n  // The holographic drop pod plays; the pack opens when the pod is tapped. Without the pod module (offline) it opens plainly.\r\n  const item=rewardPacks.packItem('secret',packId(g)),pods=await import('../../drop-pod-opening.mjs').catch(()=>null),ui=pods&&await import('../../reward-pack-ui.mjs').catch(()=>null);\r\n  if(pods&&ui){pods.playDropPod({tier:'secret',hasNext:false,onExit:()=>showCard(insp),open:async()=>{const o=await rewardPacks.openRewardPackExclusive(item);if(!o)return null;showCard(insp);return ui.unlocked(o);}});return;}\r\n  const opened=await rewardPacks.openRewardPackExclusive(item);if(!opened){b.disabled=false;b.textContent='Try again';return;}showCard(insp);});\r\n // Input: drag up/down walks, wheel, keys, held arrows, tap a statue to walk to it.\r\n let drag=null;const ray=new THREE.Raycaster(),ndc=new THREE.Vector2();\r\n canvas.addEventListener('pointerdown',e=>{drag={id:e.pointerId,y:e.clientY,x:e.clientX,moved:0};canvas.setPointerCapture(e.pointerId);});\r\n canvas.addEventListener('pointermove',e=>{if(!drag||e.pointerId!==drag.id)return;const dy=e.clientY-drag.y;drag.moved+=Math.abs(dy)+Math.abs(e.clientX-drag.x);drag.y=e.clientY;drag.x=e.clientX;move(dy*.012);});\r\n const up=e=>{if(!drag||e.pointerId!==drag.id)return;const d=drag;drag=null;if(e.type==='pointerup'&&d.moved<10)tap(e);};\r\n canvas.addEventListener('pointerup',up);canvas.addEventListener('pointercancel',up);\r\n function tap(e){const r=canvas.getBoundingClientRect();ndc.set((e.clientX-r.left)/r.width*2-1,-((e.clientY-r.top)/r.height)*2+1);ray.setFromCamera(ndc,camera);\r\n  for(const s of statues)if(s.model&&ray.intersectObject(s.grp,true).length){target=clamp(stopAt(s.i),0,limit);return;}}\r\n canvas.addEventListener('wheel',e=>{e.preventDefault();move(clamp(e.deltaY*.008,-1.5,1.5));},{passive:false});\r\n const kd=e=>{if(!archiveDialog.hidden)return;const k=e.key;if(k==='ArrowUp'||k==='w')held=-1;else if(k==='ArrowDown'||k==='s')held=1;else if(k==='Escape')exitHall();},ku=()=>{held=0;};addEventListener('keydown',kd);addEventListener('keyup',ku);\r\n for(const b of el.querySelectorAll('.vh-arr button')){const d=+b.dataset.d;b.addEventListener('pointerdown',e=>{e.preventDefault();b.setPointerCapture(e.pointerId);held=-d;});b.addEventListener('pointerup',ku);b.addEventListener('pointercancel',ku);}\r\n $('.vh-x').onclick=exitHall;\r\n window.addEventListener('myr5:vault-earned',refresh);\r\n const resize=()=>{const w=el.clientWidth||innerWidth,h=el.clientHeight||innerHeight;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();};\r\n addEventListener('resize',resize);resize();\r\n const look=new THREE.Vector3();\r\n function frame(now){raf=0;if(!live||document.hidden||lost||renderer.getContext().isContextLost())return;const dt=Math.min(.05,(now-last)/1000);last=now;const t=fixT>=0?fixT:(now-started)/1000;uT.value=t;\r\n  // Entry: purple fades out, camera starts high + far and shrinks down into the hall with an FOV warp; streaks fade.\r\n  const e=clamp((t-.3)/2.6,0,1),ease=1-Math.pow(1-e,3),inv=1-ease;pur.style.opacity=String(1-clamp((t-.7)/1.7,0,1));\r\n  sm.opacity=Math.min(1,(1-e)*1.4);streaks.position.z=(t*40)%40;streaks.visible=e<1;pins.position.z=(t*46)%4.2*1+camera.position.z*0;pinM.opacity=Math.min(1,(1-e)*1.8);pins.visible=e<1;\r\n  if(!archiveDialog.hidden){held=0;vel=0;target=route;}else if(e>=1)move(held*dt*3);\r\n  const gap=target-route;vel+=(clamp(gap*7,-3.3,3.3)-vel)*(1-Math.exp(-dt*14));const step=Math.abs(vel*dt)<Math.abs(gap)?vel*dt:gap;route+=Math.sign(step)===Math.sign(gap)?step:0;if(Math.abs(gap)<.001)vel=0;\r\n  const near=e>=1&&Math.abs(vel)<.25?nearestStatue(route,N):-1;\r\n  if(near!==insp){insp=near;if(insp>=0)showCard(insp);else card.hidden=true;}\r\n  blend+=((insp>=0?1:0)-blend)*(1-Math.exp(-dt*6));\r\n  const c=Math.max(0,Math.round((route+STOP-GAP)/GAP));if(c!==cur){cur=c;stream(clamp(cur,0,N-1));}\r\n  pos.textContent=`${clamp(Math.floor((route+STOP+.01)/GAP),0,N)} / ${N}`;\r\n  if(!reached&&route>=limit-2.2){reached=true;window.dispatchEvent(new CustomEvent('myr5:vault-hall-end'));store.bump?.('hall-end');}\r\n  camera.fov=60+inv*60;camera.updateProjectionMatrix();\r\n  camera.position.set(0,1.55+inv*7,-route+inv*18);look.set(0,1.55-inv*6,camera.position.z-20);\r\n  if(insp>=0&&blend>.01){const s=statues[insp].grp.position;camera.position.x=s.x*.35*blend;look.lerp(new THREE.Vector3(s.x,.65,s.z),blend);}\r\n  camera.lookAt(look);\r\n  for(const s of statues){if(s.model)s.model.rotation.y=Math.sin(t*.6+s.i)*.2;\r\n   if(s.anim>=0){s.anim+=dt/.6;s.mat.uniforms.uEarn.value=Math.min(1,s.anim);if(s.anim>=1)s.anim=-1;}\r\n   s.mat.uniforms.uBoost.value=(s.earned?1:1.5)+(s.i===nextI?1+.7*Math.sin(t*3.2):0);}\r\n  for(const a of atoms){a.g.rotation.y+=dt*.15;for(const r of a.rings){const u=r.userData,p=t*u.sp+u.ph;u.e.position.set(Math.cos(p)*u.rr,Math.sin(p)*u.rr,0);}}\r\n  if(nextI>=0){nextLight.intensity=5+Math.sin(t*3)*2;nextM.opacity=.45+.25*Math.sin(t*3);const ph=(t%1.4)/1.4;pulse.scale.setScalar(1+ph*.9);pulseM.opacity=(1-ph)*.95;}\r\n  renderer.render(scene,camera);raf=requestAnimationFrame(frame);}\r\n const vis=()=>{if(!document.hidden&&live&&!raf&&!lost){last=performance.now();raf=requestAnimationFrame(frame);}};document.addEventListener('visibilitychange',vis);\r\n live={stop(){cancelAnimationFrame(raf);raf=0;clearTimeout(lostTimer);canvas.removeEventListener('webglcontextlost',onLost);canvas.removeEventListener('webglcontextrestored',onRestored);removeEventListener('resize',resize);removeEventListener('keydown',kd);removeEventListener('keyup',ku);document.removeEventListener('visibilitychange',vis);window.removeEventListener('myr5:vault-earned',refresh);\r\n  archiveBinding?.dispose();archiveOpen?.removeEventListener('click',paintArchive);statues.forEach(s=>{s.loading=0;drop(s);});own.forEach(o=>o.dispose?.());\r\n  void releaseGraphics(canvas,renderer.getContext?.(),()=>{renderer.dispose();renderer.forceContextLoss();});el.remove();delete window.myr5Hall;}};\r\n // Debug/test hooks.\r\n if(/[?&]debug/.test(location.search)||localStorage.myr5Debug)window.myr5Hall={debug:()=>({route,target,insp,cur,nextI,n:N,resident:statues.filter(s=>s.model).map(s=>s.i),earned:statues.filter(s=>s.earned).map(s=>s.i)}),go:r=>{target=clamp(r,0,limit);},jump:r=>{route=target=clamp(r,0,limit);vel=0;},skip:()=>{started-=4000;},at:x=>{fixT=x;},refresh};\r\n statues.forEach(s=>setEarn(s,false));stream(0);refresh();\r\n // Prove the scene can produce a real frame before enterHall reports success to the door.\r\n // This keeps shader/driver first-use errors inside finishDolly's existing retry path.\r\n try{frame(performance.now());}catch(error){exitHall();try{error.vaultStage='hall-first-render';}catch{}throw error;}\r\n return {exit:exitHall};\r\n}\r\n","modules/vault/vault-store.mjs":"import {gameCacheKey} from '../coach-world/game-generation-client.mjs';\r\n// Achievement Vault store (owner-scoped localStorage `myr5-vault-v1/<owner>`). Every write re-evaluates the goals,\r\n// grants a vault pack per newly earned goal (unlock-ledger, idempotent) and dispatches `myr5:vault-earned {ids}`. No timers.\r\n// Counters follow vault-goals COUNTER_MODES: once per local day by default (anti-farm), 'max', or distinct keys.\r\nimport {GOALS,ARCHIVED_GOALS,VAULT_GOALS,COUNTER_MODES,MAX_COUNTER_VALUES,INTEGER_MAX_COUNTERS,SECRET_BOARDS,COACH_CATEGORIES,newlyEarned} from './vault-goals.mjs';\r\nimport {performanceOwner,localDay,unlockedCoachIds} from '../../performance-progress.mjs';\r\nimport {COACHES,COACH_REQUIREMENTS,EXCLUDED_COACH_IDS} from '../../performance-catalog.mjs';\r\nimport {colourRewardPool,textureRewardPool} from '../../battle-pass-rewards.mjs';\r\nimport * as cosmetics from '../../creature/source/creator/unlock-store.ts';\r\nimport * as ledger from '../../unlock-ledger.mjs';\r\nimport {openedPack,noteVaultPack} from '../../reward-packs.mjs';\r\nexport const VAULT_KEY='myr5-vault-v1';\r\nconst key=()=>gameCacheKey(`${VAULT_KEY}/${encodeURIComponent(performanceOwner())}`);\r\nconst obj=v=>v&&typeof v==='object'&&!Array.isArray(v)?v:{};\r\nconst empty=()=>({counters:{},days:{},keys:{},time:{},timeDay:{},secrets:{},earned:{}});\r\nexport function read(){\r\n try{const d=obj(JSON.parse(localStorage.getItem(key())||'null')),e=empty();for(const k of Object.keys(e))e[k]=obj(d[k]);return e;}catch{return empty();}\r\n}\r\nconst write=d=>{try{localStorage.setItem(key(),JSON.stringify(d));return true;}catch{return false;}};\r\nconst finite=n=>Number.isFinite(n)?n:0;\r\n/** Collection facts from the real stores, so goal tests stay pure. */\r\nexport function snapshot(){\r\n const out={coachesHave:0,coachesNeed:0,catsHave:{},texturedCoaches:0,coloursHave:0,coloursNeed:0,texturesHave:0,texturesNeed:0,bossSkins:0,vaultPacksOpened:0};\r\n try{\r\n  const obtainable=COACHES.map(c=>c.id).filter(id=>!EXCLUDED_COACH_IDS.includes(id)),mine=new Set(unlockedCoachIds());\r\n  out.coachesNeed=obtainable.length;out.coachesHave=obtainable.filter(id=>mine.has(id)).length;\r\n  for(const track of Object.keys(COACH_CATEGORIES))out.catsHave[track]=COACH_REQUIREMENTS.filter(r=>r.tracks[0]===track&&mine.has(r.id)).length;\r\n  const textures=cosmetics.ownerGrantedIds('texture'),coachOf=s=>decodeURIComponent(/^coach:([^:]*):/.exec(s)?.[1]||''),idOf=s=>s.replace(/^coach:[^:]*:/,'');\r\n  out.texturedCoaches=new Set(textures.map(coachOf).filter(id=>obtainable.includes(id))).size;\r\n  const colourIds=new Set(colourRewardPool().map(c=>c.id)),textureIds=new Set(textureRewardPool().map(t=>t.id));\r\n  out.coloursNeed=colourIds.size;out.texturesNeed=textureIds.size;\r\n  out.coloursHave=new Set([...cosmetics.ownerGrantedIds('color'),...cosmetics.ownerGrantedIds('palette')].map(idOf).filter(id=>colourIds.has(id))).size;\r\n  out.texturesHave=new Set(textures.map(idOf).filter(id=>textureIds.has(id))).size;\r\n  out.bossSkins=ledger.grantedIds('boss-skin').length;\r\n  out.vaultPacksOpened=ledger.grantedIds('reward-pack').filter(id=>id.includes(':vault-')&&openedPack(id)).length;\r\n }catch{/* a missing store leaves zeros: goals just stay locked */}\r\n return out;\r\n}\r\nexport const view=(d=read())=>({counters:d.counters,time:d.time,secrets:d.secrets,earned:d.earned,snap:snapshot()});\r\nexport const packId=g=>`reward-pack:secret:vault-${g.id}`;\r\n/** Read-only completions for goals retired from active evaluation. Existing earned timestamps and packs stay untouched. */\r\nexport function archivedCompletions(d=read()){\r\n return ARCHIVED_GOALS.filter(g=>Object.hasOwn(d.earned||{},g.id)).map(g=>({id:g.id,title:g.title,clue:g.clue,tier:g.tier,earned:d.earned[g.id],packId:packId(g)}));\r\n}\r\nfunction grant(ids,d){\r\n const now=Date.now(),done=[];\r\n for(const id of ids){if(GOALS.some(g=>g.id===id)&&!d.earned[id]){d.earned[id]=now;done.push(id);}}\r\n if(!done.length||!write(d))return [];\r\n for(const id of done){const g=GOALS.find(x=>x.id===id);noteVaultPack(packId(g),g.tier);ledger.grantUnlock('reward-pack',packId(g));}\r\n try{window.dispatchEvent(new CustomEvent('myr5:vault-earned',{detail:{ids:done}}));}catch{/* no window in node */}\r\n return done;\r\n}\r\n/** Newly earned ids after this call (also grants their packs and fires myr5:vault-earned). */\r\nexport function evaluate(d=read()){return grant(newlyEarned(view(d),d.earned),d);}\r\nexport function bump(counter,n=1,{key:k,day=localDay()}={}){\r\n const d=read(),mode=COUNTER_MODES[counter]||'day',add=Math.max(0,finite(n));\r\n if(mode==='max'){\r\n  const limit=MAX_COUNTER_VALUES[counter];\r\n  if(limit!==undefined&&(add>limit||(INTEGER_MAX_COUNTERS.includes(counter)&&!Number.isSafeInteger(add))))return evaluate(d);\r\n  d.counters[counter]=Math.max(finite(d.counters[counter]),add);\r\n }\r\n else if(mode==='keyed'){if(k==null)return evaluate(d);const keys=Array.isArray(d.keys[counter])?d.keys[counter]:[];if(!keys.includes(String(k)))keys.push(String(k));d.keys[counter]=keys;d.counters[counter]=keys.length;}\r\n else{if(d.days[counter]===day)return evaluate(d);d.days[counter]=day;d.counters[counter]=finite(d.counters[counter])+add;}\r\n write(d);return evaluate(d);\r\n}\r\n/** Visible-time counter in ms, capped per local day (VAULT_GOALS.timeCapMinutesPerDay). */\r\nexport function addTime(counter,ms,{day=localDay()}={}){\r\n const d=read(),cap=VAULT_GOALS.timeCapMinutesPerDay*60000,t=obj(d.timeDay[counter]),used=t.day===day?finite(t.ms):0,add=Math.max(0,Math.min(finite(ms),cap-used));\r\n if(add>0){d.time[counter]=finite(d.time[counter])+add;d.timeDay[counter]={day,ms:used+add};write(d);}\r\n return evaluate(d);\r\n}\r\nexport function markSecret(board){\r\n if(!SECRET_BOARDS.includes(board))return [];\r\n const d=read();if(!d.secrets[board]){d.secrets[board]=Date.now();write(d);}\r\n return evaluate(d);\r\n}\r\nexport const state=()=>view();\r\n// Raw-served files (portal, pond) cannot import this module, and the arcade/war-room pages never load it: they send `myr5:vault-bump`\r\n// {counter,n,key|ms} or queue [counter,n,key] in `myr5-vault-pending`, drained here at load.\r\nexport function drainPending(){try{const q=JSON.parse(localStorage.getItem('myr5-vault-pending')||'[]');localStorage.removeItem('myr5-vault-pending');for(const [c,n,k] of Array.isArray(q)?q:[])bump(c,n,{key:k});}catch{/* none */}}\r\n/** Retry bounded game maxima whenever the Vault hall is entered, even if this module was already cached. */\r\nexport function drainQuestPending(options={}){return import('../../arcade/quest-progress-reporter.mjs').then(({drainQueuedQuestProgress})=>drainQueuedQuestProgress(options)).catch(()=>false);}\r\nif(typeof window!=='undefined'){\r\n window.addEventListener?.('myr5:vault-bump',e=>{const {counter,n,key,ms}=e.detail||{};if(typeof counter!=='string')return;ms!=null?addTime(counter,ms):bump(counter,n,{key});});\r\n drainPending();\r\n // Game metrics may have been earned while the optional Vault module was offline.\r\n void drainQuestPending();\r\n}\r\nif(typeof window!=='undefined')window.myr5Vault={earn:id=>grant([id],read()),reset:()=>{try{localStorage.removeItem(key());}catch{/* private mode */}},state,bump,addTime,markSecret,evaluate,archivedCompletions};\r\n","modules/visual-flags.mjs":"// Visual rollback choices stay in this tab's ephemeral session, never account storage.\r\nexport function visualFlag(name,win=globalThis.window,defaultValue=true){\r\n if(!win)return defaultValue;\r\n const value=new URLSearchParams(win.location?.search||'').get(name),key=`myr5-visual-${name}`;\r\n if(value==='0'||value==='1'){try{win.sessionStorage.setItem(key,value);}catch{}return value==='1';}\r\n try{const saved=win.sessionStorage.getItem(key);if(saved==='0'||saved==='1')return saved==='1';}catch{}\r\n return defaultValue;\r\n}\r\nexport function shellEnabled(win=globalThis.window){const enabled=visualFlag('shell',win);win?.document?.documentElement?.classList.toggle('shell-disabled',!enabled);return enabled;}\r\n","movement-engine.mjs":"// MYR5 movement rules. AGPL-3.0-or-later, like the accompanying test app.\r\n// These are counting/shape heuristics, not assessments of exercise technique.\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {createHeadBob} from './arcade/runner/pose-input.mjs';\r\nimport {evaluateMovement,JOINTS} from './movement-rules.mjs';\r\nimport {PoseClassifier} from './pose-classifier.mjs';\r\nimport POSE_SAMPLES from './pose-samples.mjs';\r\nimport {createPlankInput} from './arcade/plank-flight/pose-input.mjs';\r\nimport {applyFlightEvidence} from './arcade/plank-flight/movement.mjs';\r\nconst LEGACY_MOVEMENTS = {\r\n  squat: { name: 'Squats', kind: 'reps', hint: 'Keep shoulders and hips visible. Start standing tall. Feet can stay outside the picture.' },\r\n  pushup: { name: 'Push-ups', kind: 'reps', hint: 'Place the camera beside you. Start at the top with your arm visible.' },\r\n  tree: { name: 'Tree pose', kind: 'hold', hint: 'Show your shoulders, hips and knees. The hold is estimated from your upper-leg position.' },\r\n  warrior: { name: 'Warrior II', kind: 'hold', hint: 'Show your arms, hips and knees. Either direction works; feet are not tracked.' },\r\n  horse: { name: 'Horse stance', kind: 'hold', hint: 'Show your hips and knees in your comfortable wide stance. Feet are not tracked.' },\r\n  boxing: { name: 'Air boxing', kind: 'pace', hint: 'Keep shoulders, elbows and hands visible. Measures time and relative hand pace.' },\r\n  jogging: { name: 'Subway Burpees', kind: 'steps', hint: 'Auto-run: crouch down, raise both arms to jump, and lean left or right. Keep your upper body visible.' },\r\n  jumping: { name: 'Jumping', kind: 'jumps', hint: 'Show your shoulders and hips. Jumps are estimated from body rise. Keep the phone still.' }\r\n};\r\nexport const MOVEMENTS={...LEGACY_MOVEMENTS,...EXERCISES};\r\nconst SIDES = { left: { s:11,e:13,w:15,h:23,k:25 }, right: { s:12,e:14,w:16,h:24,k:26 } };\r\n// CPU tracking can run below 6 Hz. Treat a genuine interruption differently\r\n// from a slower, continuous stream; never require seven frames in 1.1 seconds.\r\nconst MAX_FRAME_GAP=.75;\r\nconst clamp=(v,a,b)=>Math.max(a,Math.min(b,v));\r\nconst distance=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y,(a.z??0)-(b.z??0));\r\nconst midpoint=(a,b)=>({x:(a.x+b.x)/2,y:(a.y+b.y)/2,z:((a.z??0)+(b.z??0))/2});\r\nexport function jointAngle(a,b,c){\r\n  const u=[a.x-b.x,a.y-b.y,(a.z??0)-(b.z??0)],v=[c.x-b.x,c.y-b.y,(c.z??0)-(b.z??0)];\r\n  const divisor=Math.hypot(...u)*Math.hypot(...v);\r\n  return divisor>1e-8?Math.acos(clamp(u.reduce((n,x,i)=>n+x*v[i],0)/divisor,-1,1))*180/Math.PI:NaN;\r\n}\r\nfunction median(a){const s=[...a].sort((a,b)=>a-b);return s[Math.floor(s.length/2)];}\r\nexport function features(raw,aspect=1,world=null){\r\n  if(!raw||raw.length<27)return null;\r\n  // Lower-leg landmarks never enter the movement rules, including legacy modes.\r\n  raw=raw.map((v,i)=>i>26?{x:0,y:0,visibility:0}:v);\r\n  while(raw.length<33)raw.push({x:0,y:0,visibility:0});\r\n  world=world?.map((v,i)=>i>26?null:v)??null;\r\n  // Normalized x and y have different scales in portrait video. Restore aspect.\r\n  // MediaPipe's web result publishes visibility, not a per-joint presence score.\r\n  const p=raw.map(v=>({x:v.x*aspect,y:v.y,z:0,visibility:Number.isFinite(v.visibility)?v.visibility:0}));\r\n  const inFrame=i=>p[i]&&Number.isFinite(p[i].x)&&Number.isFinite(p[i].y)&&raw[i].x>=-.03&&raw[i].x<=1.03&&raw[i].y>=-.03&&raw[i].y<=1.03;\r\n  const visible=ids=>ids.every(i=>inFrame(i)&&p[i].visibility>=.45);\r\n  const issues=ids=>ids.filter(i=>!visible([i])).map(i=>!inFrame(i)?`${JOINTS[i]} outside picture`:`${JOINTS[i]} unclear (${Math.round(p[i].visibility*100)}%)`);\r\n  const sides={};\r\n  for(const [name,id] of Object.entries(SIDES)){\r\n    const q=Object.fromEntries(Object.entries(id).map(([key,i])=>[key,p[i]]));\r\n    const torso=distance(q.s,q.h);\r\n    sides[name]={...q,id,torso,\r\n      core:visible([id.s,id.h])&&torso>.035,\r\n      coreQuality:Math.min(p[id.s].visibility,p[id.h].visibility),\r\n      arm:visible([id.s,id.e,id.w,id.h])&&torso>.035,\r\n      armQuality:Math.min(...[id.s,id.e,id.w,id.h].map(i=>p[i].visibility)),\r\n      elbow:jointAngle(q.s,q.e,q.w),\r\n      horizontal:Math.abs(q.s.x-q.h.x)/Math.max(torso,.035)\r\n    };\r\n  }\r\n  const torso=(sides.left.torso+sides.right.torso)/2;\r\n  const hip=midpoint(p[23],p[24]),shoulder=midpoint(p[11],p[12]);\r\n  return {p,world,visible,issues,sides,torso,hip,shoulder,\r\n    upright:(hip.y-shoulder.y)/Math.max(torso,.035)>.65,\r\n    knees:visible([11,12,23,24,25,26])&&torso>.035};\r\n}\r\n\r\nexport class MovementSession {\r\n  constructor(mode='squat',options={}){this.reset(mode,options);}\r\n  reset(mode=this.mode,options={}){\r\n    if(!MOVEMENTS[mode])throw new Error('Unknown movement');\r\n    this.mode=mode;this.duration=Number(options.duration)||0;this.count=0;this.elapsed=0;this.hold=0;this.totalHold=0;this.bestHold=0;this.active=0;this.speed=0;this.bestSpeed=0;\r\n    this.started=null;this.last=null;this.complete=false;this.eventTimes=[];this.side=null;this.calibration=[];this.base=null;this.filtered={};this.phase='ready';this.phaseSince=null;this.lastRep=-Infinity;this.dt=0;this.lastMatch=null;\r\n    this.candidate=null;this.missAt=null;this.previousMatch=false;this.hands={};this.kneeArmed={left:false,right:false};this.lastStep=-Infinity;this.bob=null;this.runnerPulse=this.coverPulse=false;\r\n    this.flightInput=mode==='plank-flight'?createPlankInput({automatic:true}):null;this.flightPose=null;this.flightLast=null;this.flightEligible=false;this.flightAllowed=false;\r\n    this.message=MOVEMENTS[mode].hint;this.measurement='';this.tracking=false;this.progress=0;this.setupProgress=0;this.setupReason='';this.jointReadings='';\r\n    // A recorded samples file switches this exercise's reps to the k-NN classifier; {samples:null} forces the rules.\r\n    const samples='samples' in options?options.samples:POSE_SAMPLES[mode];\r\n    this.knn=samples&&MOVEMENTS[mode].kind==='reps'?new PoseClassifier(samples):null;\r\n    return this.snapshot();\r\n  }\r\n  smooth(key,value,dt){const old=this.filtered[key];const a=1-Math.exp(-Math.max(dt,.01)/.085);return this.filtered[key]=old===undefined?value:old+(value-old)*a;}\r\n  lose(t,message='Keep the needed joints in view.'){\r\n    this.tracking=false;this.message=message;this.measurement='Tracking paused';this.phase='ready';this.phaseSince=null;this.filtered={};this.calibration=[];this.setupProgress=0;this.progress=0;this.kneeArmed={left:false,right:false};this.hands={};this.speed=0;\r\n    this.previousMatch=false;this.candidate=null;if(this.missAt===null)this.missAt=t;\r\n    if(t-this.missAt>.35)this.hold=0;\r\n  }\r\n  selectSide(f,kind,t){\r\n    const candidates=Object.entries(f.sides).filter(([,s])=>s[kind]);\r\n    if(!candidates.length){\r\n      const required=s=>kind==='core'?[s.id.s,s.id.h]:[s.id.s,s.id.e,s.id.w,s.id.h];\r\n      const nearest=Object.values(f.sides).map(s=>({s,issues:f.issues(required(s))})).sort((a,b)=>a.issues.length-b.issues.length)[0];\r\n      this.lose(t,nearest.issues.length?'Counting paused: '+nearest.issues.slice(0,2).join('; ')+'.':'Move a little closer so your torso is clear.');return null;\r\n    }\r\n    const key=kind==='core'?'coreQuality':'armQuality';\r\n    const chosen=candidates.find(([name])=>name===this.side)||candidates.sort((a,b)=>b[1][key]-a[1][key])[0];\r\n    if(this.side!==chosen[0]){this.side=chosen[0];this.base=null;this.calibration=[];this.phase='ready';this.phaseSince=null;this.filtered={};}\r\n    return chosen[1];\r\n  }\r\n  calibrate(sample,ready,t){\r\n    if(!ready){this.calibration=[];this.setupProgress=0;this.setupReason='Starting position not yet detected';return false;}\r\n    // Three samples must fit the window at any continuous rate (gaps up to MAX_FRAME_GAP). A fixed 1.1 s\r\n    // window silently needed 2 updates/s: a phone tracking at 1.4–1.9/s stayed on \"Setting start\" forever.\r\n    this.calibration.push({t,...sample});this.calibration=this.calibration.filter(v=>t-v.t<=2*MAX_FRAME_GAP);\r\n    this.setupProgress=Math.min(1,this.calibration.length/3,(t-this.calibration[0].t)/.7);this.setupReason='Hold steady';\r\n    if(this.calibration.length<3||t-this.calibration[0].t<.7)return false;\r\n    // A moving starting position is not a useful baseline.\r\n    const key=sample.hipY!==undefined?'hipY':'angle';\r\n    const values=this.calibration.map(v=>v[key]);\r\n    if(Math.max(...values)-Math.min(...values)>(key==='angle'?12:.025)){this.setupProgress=0;this.setupReason='Waiting for a steadier starting position';return false;}\r\n    this.base=Object.fromEntries(Object.keys(sample).map(k=>[k,median(this.calibration.map(v=>v[k]))]));\r\n    this.calibration=[];this.setupProgress=1;this.setupReason='Ready';this.phase='top';this.phaseSince=null;return true;\r\n  }\r\n  // Dwell is time-based: a phase first seen is credited half the gap since the previous sample (on average the body\r\n  // entered the zone midway). At phone rates (1.4–2.5 updates/s) one sample in the zone satisfies the default 0.1 s\r\n  // dwell instead of needing two; pause reps (0.8 s) still need the time. The up/down gap, a full down→up cycle,\r\n  // minDuration and the 0.45 s refractory still block double counts at 30 updates/s.\r\n  repetition(down,up,t,minDuration,dwell=.10){\r\n    if(this.phase==='ready'){\r\n      if(up){this.phase='top';this.phaseSince=null;}return;\r\n    }\r\n    const seen=t-this.dt/2;\r\n    // At <=10 Hz a terminal zone may occupy exactly one sample. Keep multi-sample\r\n    // debounce at higher rates, and retain explicit long holds for pause exercises.\r\n    const dwellFor=seconds=>this.dt>=.1-1e-9&&seconds<=.1?Math.min(seconds,this.dt/2):seconds;\r\n    if(this.phase==='top'){\r\n      if(down){if(this.phaseSince===null)this.phaseSince=seen;if(t-this.phaseSince+1e-9>=dwellFor(dwell)){this.phase='bottom';this.phaseSince=null;this.downAt=t;}}else this.phaseSince=null;\r\n    }else if(this.phase==='bottom'){\r\n      if(up){if(this.phaseSince===null)this.phaseSince=seen;if(t-this.phaseSince+1e-9>=dwellFor(.10)&&t-this.downAt>=minDuration&&t-this.lastRep>=.45){this.count++;this.lastRep=t;this.eventTimes.push(t);this.phase='top';this.phaseSince=null;}}else this.phaseSince=null;\r\n    }\r\n  }\r\n  recipeUpdate(f,t,dt){\r\n    // Rule angles get the same EMA as the legacy paths (near-raw at phone rates, calmer at 30 updates/s).\r\n    const m=MOVEMENTS[this.mode],rule=evaluateMovement(f,m,this.side,(key,value)=>this.smooth(key,value,dt));\r\n    // Hold grace: under MAX_FRAME_GAP of unseen or off-shape frames keeps the hold. Unseen time is credited when the\r\n    // shape returns (the user most likely held it); off-shape time is not, but a flicker no longer restarts the hold.\r\n    if(m.kind==='hold'&&!rule.match&&this.candidate!==null){\r\n      this.missAt??=t;\r\n      if(t-this.missAt<MAX_FRAME_GAP){if(rule.valid)this.previousMatch=false;this.tracking=rule.valid;this.progress=0;this.message='Hold paused. '+rule.message;return;}\r\n    }\r\n    if(!rule.valid){this.lose(t,rule.message);return;}\r\n    if(rule.side&&this.side!==rule.side){this.side=rule.side;this.base=null;this.calibration=[];this.phase='ready';this.phaseSince=null;this.filtered={};}\r\n    this.tracking=true;\r\n    if(m.kind==='hold'){\r\n      if(rule.match){\r\n        if(this.candidate===null)this.candidate=t;\r\n        if(t-this.candidate>=.45&&this.previousMatch){const d=t-this.lastMatch;this.hold+=d;this.totalHold+=d;this.bestHold=Math.max(this.bestHold,this.hold);}\r\n        this.previousMatch=true;this.missAt=null;this.lastMatch=t;\r\n      }else{this.previousMatch=false;this.candidate=null;this.hold=0;}\r\n      this.progress=Number(rule.match);this.message=rule.match?(rule.estimated?'Hold estimated from your upper body. Keep this position.':'Position detected. Hold comfortably.'):'Hold paused. '+rule.message;\r\n    }else{\r\n      let up=rule.up,down=rule.down;\r\n      if(rule.travel){\r\n        if(this.base){const travel=this.smooth('recipeMetric',(rule.sample.hipY-this.base.hipY)/this.base.torso,dt);down=travel>=rule.travel;up=travel<=rule.travel*.6;this.progress=clamp(travel/.5,0,1);}else up=true;\r\n      }else this.progress=clamp(rule.metric,0,1);\r\n      if(!this.base){this.calibrate(rule.sample??{angle:0},up,t);this.message=this.base?'Ready. Begin when you are ready.':m.hint+' Hold the starting position briefly.';this.progress=this.setupProgress;}\r\n      else{this.repetition(down,up,t,m.minCycle??.18,m.dwell??.10);this.message=this.phase==='bottom'?'Return to your starting position.':'Move with control. Complete the full return to count.';}\r\n    }\r\n    this.measurement=m.measurement+' · '+m.limits;\r\n  }\r\n  // k-NN reps (pose-classifier.mjs): EMA of the \"down\" vote, entering at 6/10 and leaving at 4/10 as in\r\n  // MediaPipe's repetition counter, then the same dwell/minimum-duration gate as the rules.\r\n  knnUpdate(f,t,dt){\r\n    const m=MOVEMENTS[this.mode],need=this.knn.need;\r\n    if(!f.visible(need)){this.lose(t,'Counting paused: '+f.issues(need).slice(0,2).join('; ')+'.');return;}\r\n    this.tracking=true;\r\n    const down=this.smooth('knnDown',this.knn.classify(f.p).down??0,dt);\r\n    this.repetition(down>=.6,down<=.4,t,m.minCycle??.18,m.dwell??.10);\r\n    if(this.phase!=='ready')this.base??={};\r\n    this.progress=clamp(down,0,1);\r\n    this.message=this.phase==='bottom'?'Return to your starting position.':this.phase==='ready'?'Start in your recorded top position.':'Move with control. Complete the full return to count.';\r\n    this.measurement='Matched to recorded poses · '+m.limits;\r\n  }\r\n  repUpdate(f,t,dt){\r\n    const squat=this.mode==='squat';const s=this.selectSide(f,squat?'core':'arm',t);if(!s)return;\r\n    if(!squat&&s.horizontal<.5){this.lose(t,'Place the camera beside you so it can see your push-up position.');return;}\r\n    this.tracking=true;\r\n\r\n    const a=this.smooth('angle',squat?180:s.elbow,dt),hipY=this.smooth('hipY',s.h.y,dt);\r\n    if(!this.base){\r\n      const ready=squat?(s.h.y-s.s.y)/s.torso>.65:a>145&&s.horizontal>=.5;\r\n      const calibrated=this.calibrate(squat?{hipY,torso:s.torso,angle:a}:{angle:a},ready,t);\r\n      this.message=calibrated?'Ready. Begin when you are ready.':!ready?(squat?'Start standing tall with your shoulders and hips visible.':'Start at the top with your arm extended.'):this.setupReason==='Hold steady'?`Setting start: ${Math.round(this.setupProgress*100)}%. Hold still briefly.`:this.setupReason+'.';\r\n      this.progress=this.setupProgress;\r\n      this.measurement=`${this.side} side · ${Math.round(a)}°`;return;\r\n    }\r\n\r\n    const bend=squat?0:this.base.angle-a,drop=squat?(hipY-this.base.hipY)/this.base.torso:0;\r\n    // Hip travel relative to the starting torso supports cropped frontal views.\r\n    // Counts use only torso movement; lower-leg landmarks are discarded.\r\n    const down=squat?drop>=.25:bend>=35;\r\n    const up=squat?drop<=.15:bend<=14;\r\n    this.repetition(down,up,t,.18);\r\n    this.message=this.phase==='bottom'?'Movement registered. Return to your starting position.':this.phase==='ready'?'Return to your starting position to resume.':'Ready for the next repetition.';\r\n    this.progress=clamp(squat?Math.max(bend/40,drop/.5):bend/50,0,1);\r\n    this.measurement=squat?`${this.side} hip · travel ${Math.max(0,drop).toFixed(2)} · torso only`:`${this.side} arm · bend ${Math.round(bend)}°`;\r\n  }\r\n  holdUpdate(f,t,dt){\r\n    if(!f.knees){this.lose(t,'Hold paused: '+(f.issues([11,12,23,24,25,26]).slice(0,2).join('; ')||'move a little closer')+'. Feet do not need to be visible.');return;}\r\n    this.tracking=true;const l=f.sides.left,r=f.sides.right;\r\n    const spread=Math.abs(l.k.x-r.k.x)/f.torso;\r\n    let match=false,label='';\r\n    if(this.mode==='tree'){\r\n      for(const [raised,support,name] of [[l,r,'Left leg raised'],[r,l,'Right leg raised']]){\r\n        const raisedOut=Math.abs(raised.k.x-raised.h.x)/f.torso,supportOut=Math.abs(support.k.x-support.h.x)/f.torso;\r\n        if(f.upright&&raisedOut>.4&&supportOut<.55&&(support.k.y-raised.k.y)/f.torso>.15){match=true;label=name+' · estimated from upper legs';break;}\r\n      }\r\n    }else if(this.mode==='warrior'){\r\n      const arms=f.visible([11,12,13,14,15,16])&&l.elbow>140&&r.elbow>140&&Math.abs(l.w.y-l.s.y)/f.torso<.4&&Math.abs(r.w.y-r.s.y)/f.torso<.4&&Math.abs(l.w.x-r.w.x)/f.torso>2.2;\r\n      const leftDrop=(l.k.y-l.h.y)/f.torso,rightDrop=(r.k.y-r.h.y)/f.torso;\r\n      match=f.upright&&spread>1.15&&arms&&((leftDrop<.85&&rightDrop>.85)||(rightDrop<.85&&leftDrop>.85));label='Arms and upper-leg stance · estimated hold';\r\n    }else{\r\n      match=f.upright&&spread>1.5&&Math.abs(l.k.x-l.h.x)/f.torso>.5&&Math.abs(r.k.x-r.h.x)/f.torso>.5&&(l.k.y-l.h.y)/f.torso<1&&(r.k.y-r.h.y)/f.torso<1;label='Wide upper-leg stance · estimated hold';\r\n    }\r\n    if(match){\r\n      if(this.missAt!==null&&t-this.missAt>.35)this.hold=0;\r\n      this.missAt=null;if(this.candidate===null)this.candidate=t;\r\n      if(t-this.candidate>=.45){if(this.previousMatch){this.hold+=dt;this.totalHold+=dt;this.bestHold=Math.max(this.bestHold,this.hold);}this.message='Pose detected. Hold at your own comfort level.';}\r\n      else this.message='Position found. Hold steady…';\r\n      this.previousMatch=true;this.measurement=label;\r\n    }else{\r\n      if(this.missAt===null)this.missAt=t;if(t-this.missAt>.35){this.hold=0;this.candidate=null;}\r\n      this.previousMatch=false;this.message='Hold timer paused. Reposition if needed.';this.measurement=MOVEMENTS[this.mode].hint;\r\n    }\r\n    this.progress=match?1:0;\r\n  }\r\n  boxingUpdate(f,t,dt){\r\n    const selected=MOVEMENTS[this.mode].side;\r\n    const available=Object.entries(f.sides).filter(([name,s])=>s.arm&&(!selected||selected===name));\r\n    if(!available.length){this.selectSide(f,'arm',t);return;}\r\n    this.tracking=true;const velocities=[];const next={};\r\n    for(const [name,s] of available){\r\n      const w=f.world;const ids=s.id;\r\n      const useWorld=w&&[ids.s,ids.w,ids.h].every(i=>w[i]&&Number.isFinite(w[i].x)&&Number.isFinite(w[i].y)&&Number.isFinite(w[i].z));\r\n      const hand=useWorld?w[ids.w]:s.w,shoulder=useWorld?w[ids.s]:s.s,hip=useWorld?w[ids.h]:s.h;\r\n      const scale=Math.max(distance(shoulder,hip),.035);\r\n      const point={x:(hand.x-shoulder.x)/scale,y:(hand.y-shoulder.y)/scale,z:((hand.z??0)-(shoulder.z??0))/scale};\r\n      const previous=this.hands[name];\r\n      if(previous&&previous.world===!!useWorld&&dt>.005){const speed=distance(point,previous.point)/dt;if(speed<20)velocities.push(speed);}\r\n      next[name]={point,world:!!useWorld};\r\n    }\r\n    this.hands=next;const instant=velocities.length?Math.max(...velocities):0;\r\n    this.speed+= (instant-this.speed)*(1-Math.exp(-dt/.25));\r\n    if(this.speed>.7)this.active+=dt;this.bestSpeed=Math.max(this.bestSpeed,this.speed);\r\n    this.message=this.speed>.7?'Movement detected. Keep your own rhythm.':'Active timer paused. Move your hands when ready.';\r\n    this.measurement='Hand pace is relative to torso length. No punch-accuracy score.';this.progress=clamp(this.speed/5,0,1);\r\n  }\r\n  jogUpdate(f,t,dt){\r\n    // Cover Crisis: lunging and punching are the workout, so the game reports activity and each report counts as a step (keeps active time and XP counting).\r\n    if(this.coverPulse){this.coverPulse=false;this.tracking=true;if(t-this.lastStep>.2){this.count++;this.eventTimes.push(t);this.lastStep=t;}this.message='Crouching Crisis: lunge and punch.';this.measurement='Active time from lunges and punches.';this.progress=1;return;}\r\n    // Arcade movement is credited from deliberate game gestures, never standing\r\n    // in the auto-running world. The legacy march detector remains for workouts.\r\n    if(this.runnerGame||this.coverGame){\r\n      const pulse=this.runnerGame&&this.runnerPulse;this.runnerPulse=false;\r\n      this.tracking=f.visible([11,12]);\r\n      if(pulse&&this.tracking&&t-this.lastStep>.2){this.count++;this.eventTimes.push(t);this.lastStep=t;}\r\n      this.message=this.runnerGame?'Crouch, arms up to jump, lean left or right.':'Crouching Crisis: lunge and punch.';\r\n      this.measurement='Active time from game movements.';this.progress=t-this.lastStep<1?1:0;return;\r\n    }\r\n    // Subway Burpees on a treadmill: the console hides the knees, so a head bob is the step (keeps active time and XP counting).\r\n    if(!f.knees&&EXERCISES[this.mode]?.runner&&f.visible([0,11,12])){\r\n      this.tracking=true;this.bob??=createHeadBob();if(this.bob.update(f.p,t)){this.count++;this.eventTimes.push(t);this.lastStep=t;}\r\n      this.message='Counting head bobs (treadmill).';this.measurement='Estimated steps from head bob; knees are not tracked.';this.progress=clamp((t-this.lastStep)<1?1:0,0,1);return;\r\n    }\r\n    if(!f.knees){this.lose(t,'Steps paused: '+(f.issues([11,12,23,24,25,26]).slice(0,2).join('; ')||'move a little closer')+'. Feet are not tracked.');return;}\r\n    this.tracking=true;const l=f.sides.left,r=f.sides.right;\r\n    for(const [name,a,b] of [['left',l,r],['right',r,l]]){\r\n      const lift=this.smooth(name+'Lift',(b.k.y-a.k.y)/f.torso,dt);\r\n      if(lift<.05)this.kneeArmed[name]=true;\r\n      if(this.kneeArmed[name]&&lift>(MOVEMENTS[this.mode].lift??.13)&&t-this.lastStep>.17){this.count++;this.eventTimes.push(t);this.lastStep=t;this.kneeArmed[name]=false;}\r\n    }\r\n    this.message='Counting alternating knee lifts.';this.measurement='Estimated steps from knees; feet are not tracked.';this.progress=clamp(Math.abs(l.k.y-r.k.y)/f.torso/.3,0,1);\r\n  }\r\n  jumpUpdate(f,t,dt){\r\n    const s=this.selectSide(f,'core',t);if(!s)return;this.tracking=true;\r\n    const hip=this.smooth('hip',s.h.y,dt),shoulder=this.smooth('shoulder',s.s.y,dt);\r\n    if(!this.base){this.calibrate({hipY:hip,shoulder,torso:s.torso},(s.h.y-s.s.y)/s.torso>.65,t);this.message=this.base?'Ready. Begin when you are ready.':`Setting start: ${Math.round(this.setupProgress*100)}%. Stand still briefly.`;this.progress=this.setupProgress;return;}\r\n    if(Math.abs(s.torso/this.base.torso-1)>.3){this.base=null;this.calibration=[];this.phase='ready';this.message='Distance changed. Stand still to reset your start.';return;}\r\n    const lift=(this.base.hipY-hip)/this.base.torso,shoulderLift=(this.base.shoulder-shoulder)/this.base.torso;\r\n    if(this.phase==='ready'&&lift<.07)this.phase='top';\r\n    if(this.phase==='top'&&lift>.16&&shoulderLift>.12){this.phase='air';this.airAt=t;}\r\n    else if(this.phase==='air'&&lift<.07){if(t-this.airAt>.12&&t-this.lastRep>.35){this.count++;this.eventTimes.push(t);this.lastRep=t;}this.phase='top';}\r\n    this.message=this.phase==='air'?'Body rise detected. Return to your starting height.':'Ready for the next estimated jump.';this.measurement='Estimated from body rise; landing cannot be verified without feet.';this.progress=clamp(lift/.4,0,1);\r\n  }\r\n  update(raw,timestamp,aspect=1,world=null){\r\n    if(this.flightInput){const pose=this.flightInput.update({points:raw,world,width:aspect,height:1,now:timestamp,portalRest:this.flightPortalRest===true});applyFlightEvidence(this,pose,timestamp,{allowed:this.flightAllowed&&!this.flightPortalRest});return this.snapshot();}\r\n    const t=timestamp/1000;if(!Number.isFinite(t))return this.snapshot();\r\n    if(this.last!==null&&t<=this.last)return this.snapshot();\r\n    const gap=this.last===null?0:t-this.last;const dt=gap>MAX_FRAME_GAP?0:gap;this.last=t;this.dt=dt;\r\n    if(this.complete)return this.snapshot();\r\n    const f=features(raw,aspect,world);\r\n    this.jointReadings=f?Object.entries(f.sides).map(([side,s])=>`${side}: `+[s.id.s,s.id.h,s.id.k].map(i=>`${JOINTS[i].split(' ')[1]} ${Math.round(f.p[i].visibility*100)}%${f.visible([i])?'':' ×'}`).join(', ')).join(' | '):'No body landmarks';\r\n    if(this.started===null&&f&&Object.values(f.sides).some(s=>s.core||s.arm))this.started=t;\r\n    this.elapsed=this.started===null?0:t-this.started;\r\n    if(this.duration>0&&this.elapsed>=this.duration){this.elapsed=this.duration;this.complete=true;this.message='Round complete.';return this.snapshot();}\r\n    if(gap>MAX_FRAME_GAP){this.runnerPulse=this.coverPulse=false;this.lose(t);this.phase='ready';this.hold=0;this.candidate=null;}\r\n    if(!f){this.runnerPulse=this.coverPulse=false;this.lose(t,'No body found. Step into view.');return this.snapshot();}\r\n    const recipe=MOVEMENTS[this.mode];\r\n    if(this.knn)this.knnUpdate(f,t,dt);\r\n    else if(this.mode==='squat'||this.mode==='pushup')this.repUpdate(f,t,dt);\r\n    else if(['tree','warrior','horse'].includes(this.mode))this.holdUpdate(f,t,dt);\r\n    else if(recipe.detector==='boxing')this.boxingUpdate(f,t,dt);\r\n    else if(recipe.detector==='march')this.jogUpdate(f,t,dt);\r\n    else if(EXERCISES[this.mode])this.recipeUpdate(f,t,dt);\r\n    else this.jumpUpdate(f,t,dt);\r\n    return this.snapshot();\r\n  }\r\n  snapshot(){\r\n    const window=Math.min(10,this.elapsed);const now=this.last??0;\r\n    this.eventTimes=this.eventTimes.filter(t=>now-t<=10);\r\n    return {mode:this.mode,name:MOVEMENTS[this.mode].name,kind:MOVEMENTS[this.mode].kind,count:this.count,elapsed:this.elapsed,remaining:this.duration?Math.max(0,this.duration-this.elapsed):null,\r\n      hold:this.hold,totalHold:this.totalHold,bestHold:this.bestHold,active:this.active,speed:this.speed,bestSpeed:this.bestSpeed,cadence:window>=2?this.eventTimes.length*60/window:0,\r\n      complete:this.complete,tracking:this.tracking,calibrated:!!this.base,setupProgress:this.setupProgress,phase:this.phase,message:this.message,measurement:this.measurement,progress:this.progress,side:this.side,jointReadings:this.jointReadings,...(this.flightPose?{flightPose:this.flightPose}: {})};\r\n  }\r\n}\r\n","movement-rules.mjs":"// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Deliberately coarse movement measurements, not a form or injury-risk score.\r\n// Counting rules are data: one row per exercise (or per detector family), run by the small interpreter below.\r\n// Pattern after yakupzengin/fitness-trainer-pose-estimation (MIT): joint triplets with up/down thresholds.\r\n// squat, pushup, tree, warrior and horse still count on their hand-tuned paths in movement-engine.mjs (the ones\r\n// tested on Ian's phone); boxing (pace), march (steps) and jumping (body rise) are not threshold rules.\r\n//\r\n// A condition is 'measure op number', 'both measure op number' (true for the left AND the right side), a bare\r\n// flag ('upright'), or 'clear <landmarks>' (each visible, >= .45 like the engine). A measure is a joint triplet such as\r\n// 's-e-w' (the elbow angle on the counted side: s shoulder, e elbow, w wrist, h hip, k knee; MediaPipe ids work\r\n// too) or a name from MEASURES. Row fields:\r\n//  need   landmarks the counted side must show; the clearer side is picked, or the exercise's fixed side.\r\n//         Side-view rows need only the near side: the far side is naturally hidden.\r\n//  anySide  ignore the exercise's fixed side (its far leg may be the one hidden).\r\n//  fallback  row used when no side shows every `need` landmark (plank with the knees cropped).\r\n//  gate   conditions that must hold or counting pauses.\r\n//  up/down  start and far positions of a repetition (hysteresis sits in the gap between the thresholds).\r\n//  match  hold shape; with either:true it may hold for either leg (mirror).\r\n//  metric 'measure zero span' progress, (value-zero)/span; a list takes the smallest.\r\n//  travel hip travel from the calibrated start, in torso lengths (squats).\r\n// Timing (dwell, minCycle) stays on the exercise-library row. Angles are EMA-smoothed by the engine.\r\nconst clamp=(x,a,b)=>Math.max(a,Math.min(b,x));\r\nconst angle=(a,b,c)=>{const u=[a.x-b.x,a.y-b.y,(a.z??0)-(b.z??0)],v=[c.x-b.x,c.y-b.y,(c.z??0)-(b.z??0)],d=Math.hypot(...u)*Math.hypot(...v);return d>1e-8?Math.acos(clamp(u.reduce((s,x,i)=>s+x*v[i],0)/d,-1,1))*180/Math.PI:NaN;};\r\n// World angles are optional: never combine world and image coordinates.\r\nconst a3=(f,a,b,c)=>{const w=f.world;return w&&[a,b,c].every(i=>w[i]&&[w[i].x,w[i].y,w[i].z].every(Number.isFinite))?angle(w[a],w[b],w[c]):angle(f.p[a],f.p[b],f.p[c]);};\r\n// s: counted side, o: the other side. …Drop: below its reference in the counted side's torso lengths; the rest use\r\n// the body torso T (both sides averaged).\r\nexport const JOINTS={11:'left shoulder',12:'right shoulder',13:'left elbow',14:'right elbow',15:'left hand',16:'right hand',23:'left hip',24:'right hip',25:'left knee',26:'right knee'};\r\n// What to tell the user when a gate or hold condition on this measure fails; others fall back to the hint.\r\nconst TIPS={upright:'Stand tall',hipDrop:'Stand tall',horizontal:'Get level, side-on to the camera',wristDrop:'Hands under your shoulders','s-h-k':'Straighten your back','s-e-w':'Adjust your arms',kneeDrop:'Adjust your knees',kneeY:'Adjust your knees',kneeOut:'Widen your stance',spread:'Adjust your stance',wristY:'Reach your arms overhead'};\r\nconst unseen=ids=>\"Can't see your \"+ids.slice(0,2).map(i=>JOINTS[i]).join(' or ');\r\nconst MEASURES={\r\n core:c=>c.s.core,upright:c=>c.f.upright,horizontal:c=>c.s.horizontal,imageElbow:c=>c.s.elbow,\r\n hipDrop:c=>(c.s.h.y-c.s.s.y)/c.s.torso,wristDrop:c=>(c.s.w.y-c.s.s.y)/c.s.torso,kneeDrop:c=>(c.s.k.y-c.s.h.y)/c.s.torso,shoulderDrop:c=>(c.s.s.y-c.s.k.y)/c.s.torso,\r\n wristY:c=>(c.s.w.y-c.s.s.y)/c.T,elbowY:c=>(c.s.e.y-c.s.s.y)/c.T,wristElbowY:c=>(c.s.w.y-c.s.e.y)/c.T,wristLevel:c=>Math.abs(c.s.w.y-c.s.s.y)/c.T,\r\n kneeY:c=>(c.s.k.y-c.s.h.y)/c.T,otherKneeY:c=>(c.o.k.y-c.o.h.y)/c.T,kneeOut:c=>Math.abs(c.s.k.x-c.s.h.x)/c.T,otherKneeOut:c=>Math.abs(c.o.k.x-c.o.h.x)/c.T,kneeGap:c=>(c.o.k.y-c.s.k.y)/c.T,\r\n spread:c=>Math.abs(c.l.k.x-c.r.k.x)/c.T,wristSpan:c=>Math.abs(c.l.w.x-c.r.w.x)/c.T,shoulderStackY:c=>Math.abs(c.l.s.y-c.r.s.y)/c.T,shoulderStackX:c=>Math.abs(c.l.s.x-c.r.s.x)/c.T\r\n};\r\nconst UPPER='clear 11 12 13 14 15 16 23 24',LEGS='clear 11 12 23 24 25 26',OVERHEAD=[UPPER,'both wristY < -.65'];\r\nconst push={need:'s e w h',gate:['horizontal >= .5','wristDrop >= .12'],up:['s-e-w > 150'],down:['s-e-w < 112'],metric:'s-e-w 165 -60'};\r\nconst incline={...push,gate:['horizontal >= .28','wristDrop >= .12']};\r\nconst squat={gate:['core','hipDrop >= .55'],travel:.25};\r\nconst hinge={need:'s h k',gate:['kneeDrop >= .35'],up:['s-h-k > 158'],down:['s-h-k < 135'],metric:'s-h-k 180 -65'};\r\n// Knees cropped: the shoulder–hip line and the support arm alone (ankles never reach the rules).\r\n// ponytail: without a knee this cannot tell a plank from hands-and-knees; add hip–ankle if ankles are ever kept.\r\nconst plankRow=elbow=>({need:'s e w h k',gate:['horizontal > .65'],match:['s-h-k > 155','wristDrop > .2',...elbow],fallback:{need:'s e w h',gate:['horizontal > .75'],match:['wristDrop > .2',...elbow]}});\r\nconst plank=plankRow(['s-e-w > 145']);\r\nconst raise={gate:[UPPER,'upright'],up:['both h-s-w < 30'],down:['both h-s-w > 65','both s-e-w > 145'],metric:'both h-s-w 0 90'};\r\nconst standing=[LEGS,'upright'],straightLegs=['both kneeOut < .55','both kneeY > .7','spread < 1.2'];\r\nconst balance={gate:standing,either:true,match:['otherKneeOut < .55','otherKneeY > .5','kneeGap > .15','kneeOut > .4']};\r\nconst horse=deepest=>({gate:standing,match:['spread > 1.4','both kneeOut > .5','both kneeY > .08','both kneeY < '+deepest]});\r\n// Side view: the near leg is the front (bent) or back (angled) leg; either is well out from under the hip.\r\nconst front={need:'s h k',anySide:true,gate:['hipDrop > .65'],match:['kneeOut > .35']};\r\nconst jack={gate:[UPPER,LEGS,'upright'],up:['spread < 1.3','both h-s-w < 45'],down:['spread > 1.5','both h-s-w > 120'],metric:['spread 0 1.5','both h-s-w 0 140']};\r\n// Keyed by exercise id, falling back to the exercise's detector.\r\nexport const RULES={\r\n pushup:push,'high-incline-pushup':incline,'low-incline-pushup':incline,\r\n squat,'shallow-squat':{...squat,travel:.18},\r\n split:{need:'s h k',gate:['upright','kneeDrop >= 0'],up:['s-h-k > 155'],down:['s-h-k < 135'],metric:'s-h-k 165 -60'},\r\n hinge,'small-hinge':{...hinge,down:['s-h-k < 155']},\r\n bridge:{need:'s h k',gate:['horizontal >= .5','kneeDrop < .2','shoulderDrop >= .1'],up:['s-h-k < 145'],down:['s-h-k > 162'],metric:'s-h-k 125 50'},\r\n plank,'forearm-plank':plankRow(['s-e-w > 60','s-e-w < 120']),\r\n sideplank:{need:'s e h k',gate:['clear 11 12 23 24 e k'],match:['shoulderStackY > .3','shoulderStackX < .5','horizontal > .7','s-h-k > 153','elbowY > .15']},\r\n raise,'front-raise':{need:'s e w h',gate:['hipDrop > .65'],up:['h-s-w < 30'],down:['h-s-w > 65','s-e-w > 145'],metric:'h-s-w 0 90'},'overhead-reach':{...raise,down:['both h-s-w > 150','both s-e-w > 145'],metric:'both h-s-w 0 160'},\r\n press:{gate:[UPPER,'upright'],up:['both s-e-w < 115','both wristY < .2'],down:['both wristY < -.65','both s-e-w > 155'],metric:'both h-s-w 0 90'},\r\n balance,'low-tree':{...balance,match:['otherKneeOut < .55','otherKneeY > .5','kneeGap > .08','kneeOut > .2']},\r\n 'knee-balance':{...balance,match:['otherKneeOut < .55','otherKneeY > .5','kneeGap > .35']},'overhead-tree':{...balance,match:[...balance.match,...OVERHEAD]},\r\n mountain:{gate:standing,match:[...straightLegs,UPPER,'both wristY > .5']},salute:{gate:standing,match:[...straightLegs,...OVERHEAD]},\r\n chair:{need:'s e w h k',gate:['hipDrop > .65'],match:['kneeDrop > .08','kneeDrop < .8','wristDrop < -.65']},\r\n 'warrior-one':{...front,need:'s e w h k',match:[...front.match,'wristDrop < -.65']},\r\n 'wall-sit':{need:'s h k',gate:['hipDrop > .65'],match:['s-h-k > 70','s-h-k < 120','kneeDrop < .5']},\r\n warrior:{gate:standing,either:true,match:['spread > 1.1','kneeY < .85','otherKneeY > .85',UPPER,'both imageElbow > 145','both wristLevel < .3','wristSpan > 2']},\r\n goddess:{gate:standing,match:[...horse(1).match,UPPER,'both imageElbow < 120','both wristElbowY < 0']},\r\n stance:horse(1),'high-horse':horse(1.25),'low-horse':horse(.6),'front-stance-left':front,'front-stance-right':front,\r\n jack,'step-jack':{...jack,down:['spread > 1.3','both h-s-w > 120']}\r\n};\r\nconst OPS={'<':(a,b)=>a<b,'>':(a,b)=>a>b,'<=':(a,b)=>a<=b,'>=':(a,b)=>a>=b};\r\nconst swap=c=>({...c,s:c.o,o:c.s});\r\nfunction measure(name){\r\n if(MEASURES[name])return MEASURES[name];\r\n if(!/^([sewhk]|\\d+)-([sewhk]|\\d+)-([sewhk]|\\d+)$/.test(name))throw new Error('Unknown movement measure: '+name);\r\n const keys=name.split('-');\r\n return c=>{\r\n  const [a,b,d]=keys.map(k=>c.s.id[k]??Number(k)),v=a3(c.f,a,b,d);\r\n  if(!Number.isFinite(v)){c.flags.nan=true;return v;}\r\n  return c.smooth?c.smooth(a+name,v):v;\r\n };\r\n}\r\nfunction condition(text){\r\n const t=text.split(' ');\r\n if(t[0]==='clear'){const keys=t.slice(1);return c=>keys.every(k=>c.f.visible([c.s.id[k]??Number(k)]));}\r\n const both=t[0]==='both';if(both)t.shift();\r\n const [name,op,value]=t,get=measure(name);if(op&&!OPS[op])throw new Error('Unknown movement operator: '+text);\r\n const one=op?c=>OPS[op](get(c),Number(value)):c=>!!get(c);\r\n return both?c=>[one(c),one(swap(c))].every(Boolean):one;\r\n}\r\nfunction progress(text){\r\n const t=text.split(' '),both=t[0]==='both';if(both)t.shift();\r\n const get=measure(t[0]),zero=Number(t[1]),span=Number(t[2]),one=c=>(get(c)-zero)/span;\r\n return both?c=>Math.min(one(c),one(swap(c))):one;\r\n}\r\n// Every condition is evaluated (no short-circuit) so an unusable angle anywhere in a repetition rule is seen.\r\n// The first failing condition is kept on c.failed for the pause message.\r\nconst all=list=>{const tests=list.map(text=>[text,condition(text)]);return c=>tests.map(([text,test])=>test(c)||(c.failed??=text,false)).every(Boolean);};\r\nfunction tip(c,text,hint){\r\n const t=text.split(' ');\r\n if(t[0]==='clear')return unseen(t.slice(1).map(k=>c.s.id[k]??Number(k)).filter(i=>!c.f.visible([i])));\r\n return TIPS[t[t[0]==='both'?1:0]]??hint;\r\n}\r\nconst compile=r=>({...r,fallback:r.fallback&&compile(r.fallback),need:r.need?.split(' ')??null,gate:all(r.gate??[]),match:r.match&&all(r.match),up:all(r.up??[]),down:all(r.down??[]),metric:[r.metric??[]].flat().map(progress)});\r\n// Rows compile once (a typo fails when this module loads); a row added later compiles on first use.\r\nconst compiled=new WeakMap(),ruleFor=rule=>rule&&(compiled.get(rule)??compiled.set(rule,compile(rule)).get(rule));\r\nObject.values(RULES).forEach(ruleFor);\r\n// smooth(key,value) is the engine's EMA; tests and one-off checks pass none.\r\nexport function evaluateMovement(f,m,previousSide=null,smooth=null){\r\n let rule=ruleFor(RULES[m.id]??RULES[m.detector]);\r\n const fixed=rule?.anySide?null:m.side,usable=r=>Object.entries(f.sides).filter(([name,s])=>(!fixed||fixed===name)&&s.torso>.035&&(!r.need||f.visible(r.need.map(k=>s.id[k]))));\r\n let candidates=rule?usable(rule):[],estimated=false;\r\n if(!candidates.length&&rule?.fallback){rule=rule.fallback;candidates=usable(rule);estimated=true;}\r\n const need=rule?.need,picked=candidates.find(([name])=>name===previousSide)??candidates.sort((a,b)=>b[1].coreQuality-a[1].coreQuality)[0];\r\n const out={valid:false,up:false,down:false,match:false,metric:0,side:picked?.[0]??null,message:m.hint,estimated};\r\n if(!rule)return out;\r\n if(need&&!picked){\r\n  // Name the joints the nearest side is missing.\r\n  const missing=Object.entries(f.sides).filter(([name])=>!fixed||fixed===name).map(([,s])=>need.map(k=>s.id[k]).filter(i=>!f.visible([i]))).sort((a,b)=>a.length-b.length)[0];\r\n  if(missing?.length)out.message=unseen(missing);return out;\r\n }\r\n const l=f.sides.left,r=f.sides.right,s=fixed?f.sides[fixed]:picked?.[1]??l;\r\n const c={f,l,r,s,o:s===l?r:l,T:Math.max(f.torso,.035),smooth,flags:{nan:false},failed:null};\r\n if(!rule.gate(c)){out.message=tip(c,c.failed,m.hint);return out;}\r\n if(rule.travel){out.valid=true;out.travel=rule.travel;out.sample={hipY:s.h.y,torso:s.torso,angle:0};out.metric=s.h.y;return out;}\r\n if(rule.match){\r\n  out.valid=true;out.match=rule.either?[c,swap(c)].some(x=>rule.match(x)):rule.match(c);out.metric=Number(out.match);\r\n  if(!out.match)out.message=tip(c,c.failed,m.hint);return out;\r\n }\r\n const up=rule.up(c),down=rule.down(c),metric=rule.metric.length?Math.min(...rule.metric.map(p=>p(c))):0;\r\n if(c.flags.nan)return out;\r\n return Object.assign(out,{valid:true,up,down,metric});\r\n}\r\n","movement-setup.mjs":"// Camera instructions describe the joints used by Coach, never feet or ankles.\r\nexport function movementSetup(m){\r\n const d=m.detector,side=m.side?`${m.side} side`:'near side';\r\n let joints=['Shoulders','Hips','Both knees'];\r\n if(d==='pushup')joints=['Shoulder','Elbow','Wrist','Hip'];\r\n else if(d==='squat'||m.id==='jumping')joints=['Shoulders','Hips'];\r\n else if(['hinge','split','bridge'].includes(d))joints=['Shoulder','Hip','Knee'];\r\n else if(d==='plank')joints=['Shoulder','Elbow','Wrist','Hip','Knee'];\r\n else if(d==='sideplank')joints=['Both shoulders','Both hips','Supporting elbow','Supporting knee'];\r\n else if(['raise','press','boxing'].includes(d))joints=['Both shoulders','Both elbows','Both wrists','Hips'];\r\n else if(d==='yoga'||d==='jack'||(d==='balance'&&m.overhead))joints=['Shoulders','Elbows','Wrists','Hips','Both knees'];\r\n const floor=['bridge','plank','sideplank'].includes(d)||(d==='pushup'&&!m.incline);\r\n const position=d==='sideplank'?'Chest toward camera':m.view==='side'?'Side view':m.view==='angle'?'Slight angle':'Front view';\r\n const placement=floor?'Phone low · steady support':'Phone at waist height · steady support';\r\n const framing=['pushup','hinge','split','bridge','plank'].includes(d)?`${side[0].toUpperCase()+side.slice(1)} clear · room to move`:'Keep these in frame as you move.';\r\n return {position,placement,framing,joints,feet:'Feet and ankles are not tracked.'};\r\n}\r\n","nutrition.mjs":"export const MACROS=[['calories','Calories','kcal'],['protein','Protein','g'],['carbs','Carbs','g'],['fat','Fat','g']];\r\nexport const MICROS=[['calcium','Calcium','mg'],['iron','Iron','mg'],['magnesium','Magnesium','mg'],['potassium','Potassium','mg'],['sodium','Sodium','mg'],['zinc','Zinc','mg'],['vitaminA','Vitamin A','µg RAE'],['vitaminC','Vitamin C','mg'],['vitaminD','Vitamin D','µg'],['vitaminE','Vitamin E','mg'],['vitaminB12','Vitamin B12','µg'],['folate','Folate','µg']];\r\nexport const NUTRIENTS=[...MACROS,...MICROS];\r\nconst aliases={'french fries':'potatoes french fried','ice cream':'ice creams','hamburger':'hamburger','hot dog':'frankfurter','grilled salmon':'salmon cooked','grilled cheese sandwich':'sandwich cheese','macaroni and cheese':'macaroni cheese','steak':'beef steak cooked','french toast':'french toast','fried rice':'rice fried','sashimi':'fish raw','omelette':'egg omelet'};\r\nconst words=value=>value.toLowerCase().replace(/[^a-z0-9 ]/g,' ').split(/\\s+/).filter(w=>w.length>1&&!['and','with','the'].includes(w)).map(w=>w.replace(/s$/,''));\r\nexport function findFoods(foods,query,limit=8){\r\n const terms=words(aliases[query.toLowerCase()]||query);if(!terms.length)return [];\r\n return foods.map(food=>{const tokens=words(food.name);if(!terms.every(t=>tokens.includes(t)))return null;return {food,score:tokens.length+( /babyfood|infant|dry mix|powder|dehydrated/i.test(food.name)?60:0)};}).filter(Boolean).sort((a,b)=>a.score-b.score).slice(0,limit).map(x=>x.food);\r\n}\r\nexport function portionNutrition(food,grams){\r\n if(!food||!Number.isFinite(grams)||grams<=0||grams>2000)return Object.fromEntries(NUTRIENTS.map(([k])=>[k,null]));\r\n return Object.fromEntries(NUTRIENTS.map(([k])=>[k,food[k]==null?null:Math.round(food[k]*grams/100*100)/100]));\r\n}\r\nexport function displayNutrient(value,unit){return value==null?'—':`${new Intl.NumberFormat(undefined,{maximumFractionDigits:unit==='kcal'?0:2}).format(value)} ${unit}`;}\r\n","office-domain.mjs":"export const OFFICE_STYLES=['Original MYR5','Verdant','Mycelial','Chitin','Reptilian','Abyssal','Coral','Skeletal','Spectral','Infernal','Celestial','Voidborn','Eldritch','Stone Golem','Crystal','Magma','Glacial','Stormcharged','Clockwork','Neon Synth'];\r\nexport const OFFICE_SECTIONS={fuel:'B. Caffeine & supplements',goon:'C. Support & accountability',lilboyfriend:'D. Premises & equipment',djscratch:'E. Motivation & communication',corgi:'F. Sleep & stress',girlfriend:'G. Advice & sources',armie:'H. Movement history & limits'};\r\nexport const OFFICE_REQUIRED_FIELDS=['goal','goalWeightLbs','experience','sessionMinutes','coach'];\r\nexport function withOfficeDefaults(data){\r\n const value=structuredClone(data),profile=value.profile??={};\r\n const defaults={name:'You',trainingStyle:'Gradual progression',guidance:'Balanced',restSeconds:'60',trainingTime:'17:00',timezone:'UTC',reminderTone:'gentle',reminderDays:'3'};\r\n for(const [key,fallback] of Object.entries(defaults))if(profile[key]==null||typeof profile[key]==='string'&&!profile[key].trim())profile[key]=fallback;\r\n if(profile.exercises==null||Array.isArray(profile.exercises)&&!profile.exercises.length)profile.exercises=['squat','pushup','tree','warrior','horse','boxing','jogging','jumping'];\r\n value.appearance??={};value.appearance['myr5-recipe-v1']??={version:1,styles:{head:0,eye:0,collar:0,body:0,arms:0,feet:0},eye:'sleepy',fur:1,iris:1,pupil:'round',pupilSize:1,detail:1,coach:'supportive',fingers:4,toes:3,eyeLayout:'single'};\r\n value.answers??={};value.siteChoices??={};value.officeBanter??=true;\r\n // Submitting the office form accepts its current/default appearance; games keep their explicit studio gate.\r\n value.customizationConfirmed=true;value.armieCompleted=false;\r\n return value;\r\n}\r\nexport function createOfficeDraft(timezone){\r\n return withOfficeDefaults({version:1,entryRoute:'office',profile:{timezone},answers:{}});\r\n}\r\nexport function officeBanterAllowed(data){\r\n if(data?.entryRoute!=='office'||data.officeBanter!==true||data.profile?.guidance==='Quiet')return false;\r\n const boundary=data.answers?.djscratch?.q3||'';\r\n return !/(?:no|never|don['’]?t|do not)\\s+(?:\\w+\\s+){0,2}(?:teas|mock|roast|insult|banter|jok)|(?:teas|mock|roast|banter).{0,20}(?:off limits|not allowed)/i.test(boundary);\r\n}\r\nexport function officeLine(data,stage=0){\r\n if(!officeBanterAllowed(data))return 'Your coach is reviewing the paperwork. Every answer still counts.';\r\n const lines=[\r\n  'You were offered an adventure. You requested a form. I have never been more fluorescently lit.',\r\n  'Look at you, choosing boxes over boss battles. A true employee of the month.',\r\n  'I could be in a game right now. Instead, I am emotionally attached to a stapler.',\r\n  'Another section complete. Riveting. I have scheduled my yawn for 2:30.',\r\n  'Paperwork approved. Tragically, I still have to be an excellent coach for you.'\r\n ];\r\n const line=lines[Math.min(lines.length-1,Math.max(0,Math.floor(stage)))];\r\n const blocked=(data.answers?.djscratch?.q3||'').split(/[,;\\n]/).map(s=>s.trim().toLowerCase()).filter(s=>s.length>=3&&!/^none\\.?$/.test(s));\r\n return blocked.some(s=>line.toLowerCase().includes(s))?'Your coach is reviewing the paperwork. Every answer still counts.':line;\r\n}\r\nexport function officeEncouragement(data,day=1){\r\n if(!officeBanterAllowed(data))return null;\r\n const lines=['Your paperwork was thrilling. Let’s make this movement even more exciting. Keep your own pace.','The Department of Tiny Progress has approved your next comfortable rep.','You chose forms over games. Fine. I still believe in your next small step.'];\r\n const line=lines[Math.abs(Math.floor(day)-1)%lines.length];\r\n const blocked=(data.answers?.djscratch?.q3||'').split(/[,;\\n]/).map(s=>s.trim().toLowerCase()).filter(s=>s.length>=3&&!/^none\\.?$/.test(s));\r\n return blocked.some(s=>line.toLowerCase().includes(s))?null:line;\r\n}\r\n","office-form.mjs":"import {OFFICE_STYLES,officeLine} from './office-domain.mjs?v=office-short-v1';\r\nconst node=(tag,text)=>{const e=document.createElement(tag);if(text)e.textContent=text;return e;};\r\nexport function mountOfficeAppearance(form,data,{optional=false}={}){\r\n const panel=node('aside');panel.className='office-supervisor';panel.setAttribute('aria-label','Your bored paperwork supervisor');\r\n const mascot=node('div');mascot.className='office-mascot';mascot.setAttribute('aria-hidden','true');\r\n const eye=node('span');eye.className='office-eye';mascot.append(eye,node('i'));\r\n const speech=node('div'),badge=node('strong','COACH / TEMPORARILY ASSIGNED TO ADMIN'),line=node('p');line.setAttribute('aria-live','polite');speech.append(badge,line);panel.append(mascot,speech);form.append(panel);\r\n const appearance=node('fieldset');appearance.append(node('legend','Coach appearance'));\r\n appearance.append(node('p','Choose the look your coach will wear in the app. This office sketch is your bored supervisor; the app uses your selected creature parts.'));\r\n const grid=node('div');grid.className='setup-fields';const recipe=data.appearance['myr5-recipe-v1'];\r\n for(const [key,title] of Object.entries({head:'Crown & scales',eye:'Eye style',collar:'Shaggy collar',body:'Body',arms:'Arms & hands',feet:'Legs & feet'})){\r\n  const label=node('label',title),select=node('select');select.name='appearance.'+key;\r\n  OFFICE_STYLES.forEach((text,i)=>select.append(new Option(text,String(i))));select.value=String(recipe.styles[key]);label.append(select);grid.append(label);\r\n }\r\n const expression=node('label','Expression'),eyes=node('select');eyes.name='appearance.expression';for(const [id,title] of [['sleepy','Bored by paperwork'],['open','Trying to look interested'],['wide','Shocked you picked paperwork']])eyes.append(new Option(title,id));eyes.value=recipe.eye;expression.append(eyes);grid.append(expression);appearance.append(grid);\r\n const banterLabel=node('label');banterLabel.className='office-check';const banter=node('input');banter.type='checkbox';banter.name='officeBanter';banter.checked=data.officeBanter===true;banterLabel.append(banter,document.createTextNode('Let my coach roast my choice of paperwork.'));if(!optional)appearance.append(banterLabel,node('p','Uncheck for quiet paperwork. “Quiet” guidance and requests for no teasing also silence the jokes.'));\r\n return {appearance,banterLabel,collect(){for(const k of Object.keys(recipe.styles))recipe.styles[k]=Number(form.elements.namedItem('appearance.'+k).value);recipe.eye=eyes.value;recipe.coach=data.profile.coach||'supportive';data.customizationConfirmed=true;data.officeBanter=banter.checked;},paint(missing,total){mascot.dataset.eye=recipe.eye;const next=officeLine(data,missing===0?4:Math.min(3,Math.floor((1-missing/total)*4)));if(line.textContent!==next)line.textContent=next;}};\r\n}\r\n","office-quick-form.mjs":"import {FIELDS,missingFields} from './onboarding-domain.mjs?v=office-short-v1';\r\nimport {OFFICE_REQUIRED_FIELDS,withOfficeDefaults} from './office-domain.mjs?v=office-short-v1';\r\nimport {mountOfficeAppearance} from './office-form.mjs?v=office-short-v1';\r\nconst node=(tag,text)=>{const e=document.createElement(tag);if(text)e.textContent=text;return e;};\r\nexport function mountOfficeQuickForm(host,initial,{save,label='Unlock my Coach',changed=()=>{}}){\r\n const data=withOfficeDefaults(initial),form=node('form');form.className='setup-form';\r\n const customizer=mountOfficeAppearance(form,data,{optional:true});\r\n const essentials=node('fieldset');essentials.append(node('legend','Six answers. One coach.'));\r\n const grid=node('div');grid.className='setup-fields';const controls=new Map();\r\n const keys=['goal','goalWeightLbs','experience','limits','sessionMinutes','coach'];\r\n keys.forEach((key,index)=>{\r\n  const field=FIELDS.find(f=>f.key===key),wrap=node('label',(index+1)+'. '+(field?.label||'Any injuries or movement limitations?'));let input;\r\n  if(key==='limits'){input=node('textarea');input.rows=2;input.maxLength=2000;input.name='armie.q3';input.value=data.answers.armie?.q3||'';input.placeholder='Write “none” if there are none.';}\r\n  else if(field.options){input=node('select');input.append(new Option('Choose…',''));for(const value of field.options)input.append(new Option(value[0].toUpperCase()+value.slice(1),value));input.value=data.profile[key]??'';}\r\n  else{input=node('input');input.type=field.type;input.min=field.min;input.max=field.max;input.step=key==='goalWeightLbs'?'0.1':'1';input.value=data.profile[key]??'';}\r\n  input.required=true;input.name||=key;controls.set(key,input);wrap.append(input);grid.append(wrap);\r\n });\r\n essentials.append(grid);form.append(essentials,customizer.banterLabel);\r\n const options=node('details');options.append(node('summary','Name & appearance (optional)'));\r\n const nameLabel=node('label','What should your coach call you?'),name=node('input');name.name='name';name.maxLength=60;name.value=data.profile.name==='You'?'':data.profile.name||'';name.placeholder='Optional';nameLabel.append(name);options.append(nameLabel,customizer.appearance);form.append(options);\r\n const targets=node('p');targets.className='setup-targets';\r\n const defaults=node('p','We’ll start with balanced coaching, gradual progression and 60-second rests. Choose movements and adjust any setting in Coach.');\r\n const status=node('p');status.role='status';const submit=node('button',label);submit.type='submit';submit.className='setup-submit';form.append(targets,defaults,status,submit);host.replaceChildren(form);\r\n function collect(){for(const key of OFFICE_REQUIRED_FIELDS){const input=controls.get(key);data.profile[key]=input.type==='number'?(input.value===''?null:Number(input.value)):input.value;}data.profile.name=name.value.trim();data.answers.armie??={};data.answers.armie.q3=controls.get('limits').value.trim();customizer.collect();return data;}\r\n function paint(){collect();const missing=missingFields(data);status.textContent=missing.length?`${missing.length} answer${missing.length===1?'':'s'} still needed. ${missing[0]}`:'Six answers filed. Your coach is ready.';submit.disabled=missing.length>0;const weight=data.profile.goalWeightLbs;targets.textContent=`Start at 3 reps and 9-second holds, adding 1 each day.${weight>0?` Daily targets: ${weight} g protein · ${weight} oz water.`:''}`;customizer.paint(missing.length,6);changed(structuredClone(data));}\r\n form.addEventListener('input',paint);form.addEventListener('change',paint);\r\n form.onsubmit=async event=>{event.preventDefault();collect();if(missingFields(data).length||!form.reportValidity()){paint();return;}submit.disabled=true;status.textContent='Filing your six answers…';try{await save(structuredClone(data));}catch(error){status.textContent=error.message;submit.disabled=false;}};\r\n paint();return {form,collect};\r\n}\r\n","office.css":".setup-route-choices{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:26px 0}.setup-route{display:flex;flex-direction:column;align-items:flex-start;padding:24px;border:2px solid #aa97bd;border-radius:16px}.setup-route h2{font-size:24px;margin:0}.setup-route p{flex:1}.setup-route.games{background:#312242}.setup-route.office{background:#e7e4db;color:#30312c;border:1px solid #8d8d7d;border-radius:0;font-family:'Courier New',monospace;box-shadow:4px 4px 0 #a19e92}.setup-route.office p{color:#41423b}.setup-route.office .setup-action{background:#d3d1c8;border:2px outset #f3f1e7;box-shadow:none;border-radius:0;color:#242720;font-family:inherit}.draft-notice{font-size:14px}\r\nbody.office-mode{color-scheme:light;background:#c9cbc3;color:#292d29;font-family:'Courier New',monospace}.office-mode .setup-shell{background:#f2f0e6;border:1px solid #92978d;border-top:16px solid #868d80;border-radius:0;box-shadow:8px 8px 0 #9ca295;padding:clamp(18px,4vw,38px)}.office-mode .setup-brand{color:#4f584f;font-size:14px;letter-spacing:1px;border-bottom:2px solid #535f50;padding-bottom:14px}.office-mode .setup-shell h1{font-size:clamp(26px,4vw,36px);letter-spacing:-1px}.office-mode .setup-shell p{color:#414a40}.office-mode fieldset,.office-mode details{border:1px solid #879080;border-radius:0;background:#faf9f1}.office-mode summary{color:#303b2c}.office-mode .setup-form label{font-size:16px}.office-mode .setup-form input,.office-mode .setup-form select,.office-mode .setup-form textarea{border:1px solid #818b78;background:#fffefa;color:#242c23;border-radius:0;font-family:inherit;font-size:16px;box-shadow:inset 1px 2px 0 #d9ddd2}.office-mode legend{text-transform:uppercase;letter-spacing:.5px;font-size:16px}.office-mode .setup-submit,.office-mode .setup-action{font-family:inherit;background:#d1d5c8;border:2px outset #f8f9f2;border-radius:0;box-shadow:none;color:#252f23}.office-mode .setup-submit{width:100%;text-transform:uppercase}.office-mode .setup-submit:disabled{opacity:.55}.office-mode .setup-targets{background:#e3e8da;border-left-color:#586b4b}.office-mode .office-check{display:flex;align-items:center;gap:12px}.office-mode .office-check input{width:24px;height:24px;flex:none;accent-color:#4f6344}.office-exit{margin:18px 0 24px}.office-exit .setup-action{font-size:14px;background:transparent;border:0;padding:0;text-decoration:underline;text-underline-offset:4px}.office-mode :focus-visible{outline-color:#305d32}\r\n.office-supervisor{position:sticky;top:8px;z-index:5;display:flex;align-items:center;gap:20px;background:#e3e7da;border:2px solid #7b8870;padding:18px;box-shadow:3px 4px 0 #9ea695;margin:24px 0}.office-supervisor strong{font-size:14px;letter-spacing:.4px}.office-supervisor p{font-size:16px;line-height:1.4;margin:8px 0 0}.office-mascot{width:80px;height:86px;flex:none;background:#816393;border:3px solid #493254;border-radius:40% 40% 22% 22%;position:relative;transform:rotate(-5deg);box-shadow:inset -10px -6px 0 #664977}.office-mascot:before,.office-mascot:after{content:'';position:absolute;width:16px;height:20px;border-radius:7px;background:#493254;bottom:-12px}.office-mascot:before{left:6px}.office-mascot:after{right:6px}.office-eye{position:absolute;left:16px;top:18px;width:42px;height:26px;background:#faf8dd;border:2px solid #3e2c43;border-radius:50%;overflow:hidden}.office-eye:before{content:'';position:absolute;left:14px;top:2px;width:13px;height:22px;border-radius:50%;background:#302b26}.office-eye:after{content:'';position:absolute;left:-3px;right:-3px;top:-2px;height:16px;background:#8d6c9e;border-bottom:3px solid #45304e}.office-mascot[data-eye=open] .office-eye:after{height:4px}.office-mascot[data-eye=wide] .office-eye:after{height:0;border:0}.office-mascot i{position:absolute;width:24px;height:3px;left:24px;bottom:18px;background:#38263d;transform:rotate(-8deg)}\r\n@media(max-width:600px){.setup-route-choices{grid-template-columns:1fr}.setup-route{padding:20px}.office-supervisor{padding:12px;gap:12px;top:4px}.office-supervisor strong{font-size:12px}.office-supervisor p{font-size:14px}.office-mascot{width:64px;height:72px}.office-eye{left:8px;top:12px}.office-mascot i{left:18px}.office-mode .setup-shell{padding:16px}.office-mode .setup-form label{font-size:16px}.office-mode .setup-fields{grid-template-columns:1fr}}\r\n","offline-install.mjs":"// Installation is ready only after the worker has saved the core offline app.\r\nexport async function prepareOfflineApp(serviceWorker=globalThis.navigator?.serviceWorker){\r\n if(!serviceWorker)throw Error('This browser cannot save Coach on this device. Open the installer in Chrome, Edge, or Safari.');\r\n const registration=await serviceWorker.register('/sw.js',{updateViaCache:'none'});\r\n const worker=registration.installing||registration.waiting||registration.active;\r\n if(!worker)throw Error('Coach could not finish downloading. Reconnect and retry.');\r\n await new Promise((resolve,reject)=>{\r\n  function check(){\r\n   if(['installed','activating','activated'].includes(worker.state)){worker.removeEventListener('statechange',check);resolve();}\r\n   else if(worker.state==='redundant'){worker.removeEventListener('statechange',check);reject(Error('Coach could not be saved. Check your connection and free space, then retry.'));}\r\n  }\r\n  worker.addEventListener('statechange',check);check();\r\n });\r\n return registration;\r\n}\r\n","onboarding-domain.mjs":"import {SITE_QUESTIONS} from './onboarding-questions.mjs';\r\nimport {OFFICE_REQUIRED_FIELDS,withOfficeDefaults} from './office-domain.mjs?v=office-short-v1';\r\nimport {QUICK_REQUIRED_FIELDS,withQuickDefaults} from './quick-setup.mjs';\r\nimport {EYE_STYLE_IDS} from './eye-styles.mjs';\r\nexport {SITE_QUESTIONS};\r\nexport const WEBSITE='https://mominc.online';\r\nexport const COACH_APP='https://myr5.mominc.online';\r\nexport const COACHES=['supportive','direct','analytical','playful','calm','mom'];\r\nexport const EXERCISES={squat:'Squats',pushup:'Push-ups',tree:'Tree pose',warrior:'Warrior II',horse:'Horse stance',boxing:'Air boxing',jogging:'Jogging in place',jumping:'Jumps'};\r\nexport const FIELDS=[\r\n {key:'name',label:'What should your coach call you?',max:60},\r\n {key:'goalWeightLbs',label:'Goal weight (lb)',type:'number',min:1,max:1500},\r\n {key:'goal',label:'Your main training goal',options:['Build a routine','Build strength','Improve mobility and balance','Improve stamina','Manage weight']},\r\n {key:'experience',label:'Current training experience',options:['New to training','Returning to training','Training regularly']},\r\n {key:'coach',label:'Coach personality',options:COACHES},\r\n {key:'trainingStyle',label:'How should your coach guide training?',options:['Controlled reps','Gradual progression','Technique first']},\r\n {key:'guidance',label:'How much should your coach talk?',options:['Quiet','Balanced','Detailed']},\r\n {key:'sessionMinutes',label:'Minutes available per session',type:'number',min:1,max:180},\r\n {key:'restSeconds',label:'Rest between sets',options:['30','45','60','90','120','180']},\r\n {key:'trainingTime',label:'Usual training time',type:'time'},\r\n {key:'timezone',label:'Time zone'},\r\n {key:'reminderTone',label:'Reminder wording',options:['gentle','direct','cheeky']},\r\n {key:'reminderDays',label:'Reminder days per week',options:['1','3','5','7']},\r\n {key:'foodPreference',label:'Food preference',options:['Everything','Vegetarian','Vegan','Other preference']},\r\n {key:'foodLimits',label:'Food allergies or restrictions (write “none” if there are none)',max:600},\r\n];\r\nconst object=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst validText=(v,max=600)=>typeof v==='string'&&v.trim().length>0&&v.length<=max;\r\n// Rank 4: a region may also carry a decoupled texture+colour choice (see design.ts's\r\n// MaterialChoice/parseRecipe, which this mirrors). Optional and additive - old recipes\r\n// without it, and recipes that only ever used it on some regions, both stay valid.\r\nconst validMaterialChoice=v=>object(v)&&typeof v.textureId==='string'&&typeof v.colorId==='string'&&Number.isFinite(v.sparkle)&&v.sparkle>=0&&v.sparkle<=1&&Number.isFinite(v.metallic)&&v.metallic>=0&&v.metallic<=1;\r\nconst validMaterials=m=>m===undefined||(object(m)&&Object.entries(m).every(([k,v])=>['head','eye','collar','body','arms','feet'].includes(k)&&validMaterialChoice(v)));\r\nexport function validRecipe(r){return object(r)&&r.version===1&&object(r.styles)&&['head','eye','collar','body','arms','feet'].every(k=>Number.isInteger(r.styles[k])&&r.styles[k]>=0&&r.styles[k]<23)&&COACHES.includes(r.coach)&&EYE_STYLE_IDS.includes(r.eye)&&Number.isFinite(r.fur)&&r.fur>=.65&&r.fur<=1.4&&Number.isFinite(r.iris)&&r.iris>=.7&&r.iris<=1.25&&['round','vertical','horizontal','oval','diamond','star','heart','cross'].includes(r.pupil??'round')&&Number.isFinite(r.pupilSize??1)&&(r.pupilSize??1)>=.6&&(r.pupilSize??1)<=1.15&&Number.isFinite(r.detail??1)&&(r.detail??1)>=.5&&(r.detail??1)<=1.5&&Number.isInteger(r.fingers??4)&&(r.fingers??4)>=2&&(r.fingers??4)<=6&&Number.isInteger(r.toes??3)&&(r.toes??3)>=1&&(r.toes??3)<=6&&['single','horizontal','vertical','frontBack','triangle','around','spider','square'].includes(r.eyeLayout??'single')&&validMaterials(r.materials);}\r\nexport function missingFields(v){\r\n const missing=[];if(!object(v))return ['Complete your coach setup'];if(!['games','office'].includes(v.entryRoute??'games'))return ['Choose games or the office form'];const office=v.entryRoute==='office',quick=v.setupMode==='quick';\r\n if(quick){if(v.profile!=null&&!object(v.profile)||v.answers!=null&&!object(v.answers)||v.appearance!=null&&!object(v.appearance))return ['Choose a valid coach profile'];v=withQuickDefaults(v);}\r\n if(office){if(v.profile!=null&&!object(v.profile)||v.answers!=null&&!object(v.answers)||v.appearance!=null&&!object(v.appearance))return ['Choose a valid coach profile'];v=withOfficeDefaults(v);}\r\n const p=v.profile||{};\r\n for(const f of FIELDS){const a=p[f.key];if((quick?!QUICK_REQUIRED_FIELDS.includes(f.key):office&&!OFFICE_REQUIRED_FIELDS.includes(f.key))&&(a==null||typeof a==='string'&&!a.trim()))continue;if(f.type==='number'?typeof a!=='number'||!Number.isFinite(a)||a<f.min||a>f.max:f.options?!f.options.includes(String(a)):f.type==='time'?!/^([01]\\d|2[0-3]):[0-5]\\d$/.test(a||''):!validText(a,f.max||160))missing.push(f.label);}\r\n try{if(!p.timezone)throw Error();new Intl.DateTimeFormat('en',{timeZone:p.timezone}).format();}catch{if(!missing.includes('Time zone'))missing.push('Time zone');}\r\n if(!Array.isArray(p.exercises)||!p.exercises.length||p.exercises.some(k=>!Object.hasOwn(EXERCISES,k)))missing.push('Choose at least one movement');\r\n for(const group of SITE_QUESTIONS)for(let i=0;i<group.questions.length;i++){const a=v.answers?.[group.id]?.['q'+(i+1)],required=!quick&&(!office||group.id==='armie'&&i===2);if(!required&&(a==null||typeof a==='string'&&!a.trim()))continue;if(!validText(a,2000))missing.push(group.questions[i]);}\r\n if(!validRecipe(v.appearance?.['myr5-recipe-v1']))missing.push('Save your customized coach');\r\n if(v.customizationConfirmed!==true)missing.push('Confirm your coach appearance');\r\n if((v.entryRoute??'games')==='games'&&v.armieCompleted!==true)missing.push('Finish Coach Armie');\r\n if(v.entryRoute==='office'&&typeof v.officeBanter!=='boolean')missing.push('Choose your paperwork banter setting');\r\n return missing;\r\n}\r\nexport function validateOnboarding(v){\r\n const missing=missingFields(v);if(missing.length)throw Object.assign(Error('Complete your coach setup: '+missing[0]),{status:400,missing});\r\n if(v.setupMode==='quick')v=withQuickDefaults(v);else if(v.entryRoute==='office')v=withOfficeDefaults(v);\r\n const profile=Object.fromEntries(FIELDS.map(f=>[f.key,typeof v.profile[f.key]==='string'?v.profile[f.key].trim():v.profile[f.key]]));\r\n profile.exercises=[...new Set(v.profile.exercises)];\r\n const answers=Object.fromEntries(SITE_QUESTIONS.map(g=>[g.id,Object.fromEntries(g.questions.map((_,i)=>['q'+(i+1),(v.answers?.[g.id]?.['q'+(i+1)]??'').trim()]))]));\r\n const appearance={};for(const k of ['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1'])if(v.appearance[k]!=null){if(!object(v.appearance[k])||JSON.stringify(v.appearance[k]).length>30000)throw Object.assign(Error('Choose a valid saved appearance.'),{status:400});appearance[k]=v.appearance[k];}\r\n appearance['myr5-recipe-v1'].coach=profile.coach;\r\n const context=object(v.siteChoices)?v.siteChoices:{};if(JSON.stringify(context).length>20000)throw Object.assign(Error('The website choices are too large.'),{status:400});\r\n const entryRoute=v.entryRoute??'games';\r\n return {version:1,...(v.setupMode==='quick'?{setupMode:'quick'}:{}),profile,answers,appearance,siteChoices:context,entryRoute,officeBanter:entryRoute==='office'&&v.officeBanter===true,armieCompleted:entryRoute==='games',customizationConfirmed:true};\r\n}\r\nexport function calendarDay(now,timezone){const p=Object.fromEntries(new Intl.DateTimeFormat('en-CA',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date(now)).map(x=>[x.type,x.value]));return `${p.year}-${p.month}-${p.day}`;}\r\nexport function dailyTargets(profile,startDay,now=Date.now()){\r\n const today=calendarDay(now,profile.timezone),elapsed=Math.max(0,Math.round((Date.parse(today+'T00:00:00Z')-Date.parse(startDay+'T00:00:00Z'))/86400000));\r\n const reps=3+elapsed,holdSeconds=9+elapsed;\r\n return {day:elapsed+1,date:today,reps,holdSeconds,proteinGrams:profile.goalWeightLbs??null,waterOz:profile.goalWeightLbs??null,goals:Object.fromEntries(Object.keys(EXERCISES).map(k=>[k,['tree','warrior','horse','boxing'].includes(k)?holdSeconds:reps]))};\r\n}\r\nexport function encodeHandoff(v){const bytes=new TextEncoder().encode(JSON.stringify(v));if(bytes.length>55000)throw Error('Your profile is too large to transfer. Shorten long answers and try again.');let s='';for(const b of bytes)s+=String.fromCharCode(b);return btoa(s).replaceAll('+','-').replaceAll('/','_').replace(/=+$/,'');}\r\nexport function decodeHandoff(raw){if(typeof raw!=='string'||raw.length>75000||!/^[A-Za-z0-9_-]+$/.test(raw))throw Error('This coach transfer could not be read. Return to the website and open Coach again.');return JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(raw.replaceAll('-','+').replaceAll('_','/')),c=>c.charCodeAt(0))));}\r\n","onboarding-form.mjs":"import {FIELDS,SITE_QUESTIONS,EXERCISES,missingFields,dailyTargets,calendarDay} from './onboarding-domain.mjs?v=quick-install-v1';\r\nimport {mountOfficeQuickForm} from './office-quick-form.mjs?v=office-short-v1';\r\nimport {OFFICE_SECTIONS,OFFICE_REQUIRED_FIELDS,withOfficeDefaults} from './office-domain.mjs?v=office-short-v1';\r\nimport {mountOfficeAppearance} from './office-form.mjs?v=office-short-v1';\r\nimport {mountQuickSetup} from './quick-setup-form.mjs';\r\nimport {QUICK_REQUIRED_FIELDS} from './quick-setup.mjs';\r\nconst node=(tag,text)=>{const e=document.createElement(tag);if(text)e.textContent=text;return e;};\r\nexport function mountProfileForm(host,initial,{save,label='Unlock my Coach app',changed=()=>{},fullSettings=false}){\r\n if(!fullSettings)return mountQuickSetup(host,initial,{save,label,changed});\r\n const data=initial.entryRoute==='office'?withOfficeDefaults(initial):structuredClone(initial);data.profile??={};data.answers??={};const form=node('form');form.className='setup-form';const status=node('p');status.role='status';const sections=[];const office=data.entryRoute==='office',quick=data.setupMode==='quick',customizer=office?mountOfficeAppearance(form,data):null;\r\n const basics=node('fieldset'),legend=node('legend',office?'Training':'Training');basics.append(legend);const grid=node('div');grid.className='setup-fields';\r\n for(const field of FIELDS){const wrap=node('label',field.label);let input;if(field.options){input=node('select');input.append(new Option('Choose…',''));for(const value of field.options)input.append(new Option(value[0].toUpperCase()+value.slice(1),value));}else{input=node('input');input.type=field.type||'text';if(field.min!=null){input.min=field.min;input.max=field.max;input.step=field.key==='goalWeightLbs'?'0.1':'1';}input.maxLength=field.max||160;}input.name=field.key;input.required=quick?QUICK_REQUIRED_FIELDS.includes(field.key):!office||OFFICE_REQUIRED_FIELDS.includes(field.key);input.value=data.profile[field.key]??'';wrap.append(input);grid.append(wrap);}\r\n basics.append(grid);const movement=node('fieldset');movement.append(node('legend','Starting movements'));const choices=node('div');choices.className='setup-choices';for(const [key,label] of Object.entries(EXERCISES)){const l=node('label'),i=node('input');i.type='checkbox';i.name='exercises';i.value=key;i.checked=data.profile.exercises?.includes(key)||false;l.append(i,document.createTextNode(label));choices.append(l);}movement.append(choices,node('p','Choose what fits your space and limits.'));basics.append(movement);form.append(basics);\r\n for(const group of SITE_QUESTIONS){const details=node('details'),summary=node('summary',group.title);details.append(summary);for(let i=0;i<group.questions.length;i++){const name='q'+(i+1),label=node('label',group.questions[i]),input=node('textarea');input.name=group.id+'.'+name;input.rows=2;input.maxLength=2000;input.required=(!office&&!quick)||group.id==='armie'&&name==='q3';input.value=data.answers[group.id]?.[name]||'';label.append(input);details.append(label);}sections.push({group,details,summary});form.append(details);}\r\n if(customizer)form.append(customizer.appearance);const targets=node('p');targets.className='setup-targets';form.append(targets,node('p',''));\r\n const submit=node('button',label);submit.type='submit';submit.className='setup-submit';form.append(status,submit);host.replaceChildren(form);\r\n function collect(){for(const f of FIELDS){const value=form.elements.namedItem(f.key).value;data.profile[f.key]=f.type==='number'?(value===''?null:Number(value)):value;}data.profile.exercises=[...form.querySelectorAll('[name=exercises]:checked')].map(e=>e.value);for(const g of SITE_QUESTIONS){data.answers[g.id]??={};for(let i=0;i<g.questions.length;i++)data.answers[g.id]['q'+(i+1)]=form.elements.namedItem(g.id+'.q'+(i+1)).value.trim();}customizer?.collect();return data;}\r\n function paint(){collect();const missing=missingFields(data);status.textContent=missing.length?`${missing.length} answer${missing.length===1?'':'s'} still needed. ${missing[0]}`:'Ready';submit.disabled=missing.length>0;for(const {group,details,summary} of sections){const n=office||quick?(group.id==='armie'&&!data.answers.armie.q3?.trim()?1:0):group.questions.filter((_,i)=>!data.answers[group.id]['q'+(i+1)]?.trim()).length;summary.textContent=(office?OFFICE_SECTIONS[group.id]:group.title)+((office||quick)&&group.id!=='armie'?' · Optional':n?` · ${n} to answer`:' · Complete');if(n)details.open=true;}try{const t=dailyTargets(data.profile,calendarDay(Date.now(),data.profile.timezone));targets.textContent=`Day 1: 3 reps · 9-second holds. ${t.proteinGrams?`Daily targets: ${t.proteinGrams} g protein · ${t.waterOz} oz water.`:'Weight-based targets are optional.'}`;}catch{targets.textContent='Day 1: 3 reps · 9-second holds.';}customizer?.paint(missing.length,FIELDS.length+SITE_QUESTIONS.reduce((n,g)=>n+g.questions.length,0)+2);changed(structuredClone(data));}\r\n form.addEventListener('input',paint);form.addEventListener('change',paint);form.addEventListener('invalid',e=>{const details=e.target.closest('details');if(details)details.open=true;},true);\r\n form.onsubmit=async e=>{e.preventDefault();collect();if(missingFields(data).length||!form.reportValidity()){paint();return;}submit.disabled=true;status.textContent='Saving your coach…';try{await save(structuredClone(data));}catch(error){status.textContent=error.message;submit.disabled=false;}};paint();return {form,collect};\r\n}\r\n","onboarding-questions.mjs":"// Coach questions from the character-site manifest. Preserve their q1/q2/q3 keys.\r\nexport const SITE_QUESTIONS=[\r\n  {\r\n    \"id\": \"fuel\",\r\n    \"title\": \"DRINKS\",\r\n    \"questions\": [\r\n      \"How does caffeine usually hit you: fine, jittery, anxious, sleepless, or you do not use it?\",\r\n      \"Which supplements do you already take, if any?\",\r\n      \"When do you usually train: morning, afternoon, evening, or it moves around?\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"goon\",\r\n    \"title\": \"GOON\",\r\n    \"questions\": [\r\n      \"Who, if anyone, supports what you are trying to do?\",\r\n      \"Should the coach involve them, remind you about them, or keep the plan between you and the coach?\",\r\n      \"When do you reliably have help, company or accountability?\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"lilboyfriend\",\r\n    \"title\": \"LIL BOYFRIEND\",\r\n    \"questions\": [\r\n      \"Where would you usually train: home, gym, outdoors, somewhere else, or not sure yet?\",\r\n      \"How much clear floor space do you actually have?\",\r\n      \"What equipment is available to you right now? None is a complete answer.\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"djscratch\",\r\n    \"title\": \"DJ SCRATCH\",\r\n    \"questions\": [\r\n      \"What reliably gets you to start: a deadline, a person, music, a checklist, a joke, or something else?\",\r\n      \"How should your coach speak to you: direct, calm, warm, funny, quiet, or a mix?\",\r\n      \"What should your coach never say to you?\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"corgi\",\r\n    \"title\": \"CORTISOL CORGI\",\r\n    \"questions\": [\r\n      \"How many hours do you usually sleep on a normal night?\",\r\n      \"How heavy is your stress load this week: light, manageable, heavy, or prefer not to say?\",\r\n      \"How much news do you take in on a normal day: none, under 30 minutes, 30 to 90 minutes, or more?\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"girlfriend\",\r\n    \"title\": \"DR GIRLFRIEND\",\r\n    \"questions\": [\r\n      \"What health or fitness advice do you already believe?\",\r\n      \"Where did that advice come from?\",\r\n      \"How confident are you checking a claim against its original source: not yet, sometimes, usually, or very?\"\r\n    ]\r\n  },\r\n  {\r\n    \"id\": \"armie\",\r\n    \"title\": \"COACH ARMIE\",\r\n    \"questions\": [\r\n      \"What movement do you do in a normal week right now?\",\r\n      \"What movement have you done before, even if you stopped?\",\r\n      \"Any injury, pain, disability or other limit the coach should work around? This is context, not a diagnosis.\"\r\n    ]\r\n  }\r\n];\r\n","onboarding.css":":root{color-scheme:dark;font-family:Arial,sans-serif;background:#160f23;color:#f3eee1}*{box-sizing:border-box}body{margin:0;padding:clamp(12px,4vw,48px);background:radial-gradient(ellipse at top,#49325c,#160f23 70%);min-height:100vh}.setup-shell{max-width:880px;margin:auto;border:8px solid #574738;border-radius:28px;padding:clamp(18px,4vw,38px);background:linear-gradient(140deg,#292334,#15121b);box-shadow:inset 0 3px 2px #ac9671,0 14px 0 #171110,0 24px 60px #0009}.setup-brand{color:#c9b589;font-size:12px;letter-spacing:3px}.setup-shell h1{font-size:clamp(26px,5vw,42px);margin:.5em 0}.setup-shell p{line-height:1.6;color:#cfc6d5}a{color:#bdeada}.setup-form label{display:grid;gap:8px;font-size:14px;margin:16px 0}.setup-form input,.setup-form select,.setup-form textarea{font:inherit;color:#fff;background:#14131a;border:2px solid #716777;border-radius:8px;padding:12px;width:100%;max-width:100%}.setup-fields{display:grid;grid-template-columns:1fr 1fr;gap:0 22px}fieldset{border:1px solid #7f728b;border-radius:12px;margin:24px 0;padding:18px}legend{font-weight:700;padding:0 9px}details{border:1px solid #6a5b75;border-radius:10px;padding:16px;margin:15px 0}summary{cursor:pointer;font-weight:700;color:#dcceac}.setup-choices{display:grid;grid-template-columns:1fr 1fr;gap:6px 20px}.setup-choices label{display:flex;align-items:center;margin:6px 0}.setup-choices input{width:22px;height:22px;accent-color:#d7c6a2}.setup-submit,.setup-action{display:inline-block;background:linear-gradient(#eddfb9,#bbaa7c);border:2px solid #f5e8c2;color:#201a24;border-radius:12px;padding:16px 23px;box-shadow:0 5px 0 #615039;font:700 16px Arial;cursor:pointer;text-decoration:none}.setup-submit:disabled{opacity:.5;cursor:default}.setup-submit:active,.setup-action:active{transform:translateY(3px);box-shadow:0 2px 0 #615039}.setup-targets{border-left:4px solid #a8d9ba;padding:16px;background:#172720}input:focus-visible,select:focus-visible,textarea:focus-visible,button:focus-visible,a:focus-visible,summary:focus-visible{outline:3px solid #9ce1cb;outline-offset:4px}[hidden]{display:none!important}@media(max-width:600px){.setup-fields{grid-template-columns:1fr}.setup-shell{border-width:5px}fieldset{padding:12px}.setup-submit{width:100%}}\r\n\r\n.quick-mode .setup-shell{max-width:480px}.quick-mode .setup-brand{font-size:14px;letter-spacing:1px}.quick-mode .setup-shell h1{font-size:1.75rem}.quick-setup label{font-size:1rem}.quick-setup textarea{resize:vertical}.quick-setup .setup-submit{width:100%;margin-top:8px}.draft-notice:empty{display:none}\r\n","onboarding.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta name=\"referrer\" content=\"no-referrer\"><title>Start your Coach</title><link rel=\"stylesheet\" href=\"/onboarding.css\"><link rel=\"stylesheet\" href=\"/office.css\"><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main class=\"setup-shell\"><div class=\"setup-brand\">MOM INC / COACH ACTIVATION</div><h1>Set up Coach</h1><p id=\"setupStatus\" role=\"status\">Loading…</p><div id=\"setupBody\"></div></main><script type=\"module\" src=\"/onboarding.mjs?v=quick-install-v2\"></script></body></html>\r\n","onboarding.mjs":"import {authTransitions} from './auth-transition.mjs';\r\nimport {createAccountSessionActions} from './account-session-actions.mjs';\r\nimport {signInPath} from './auth-paths.mjs';\r\nimport {authFetch} from './auth-client.mjs';\r\nimport {setupAllowed} from './install-context.mjs';\r\nimport {restoreInstall} from './install-transfer.mjs';\r\nimport {withQuickDefaults} from './quick-setup.mjs';\r\nimport {receiveCoach} from './receive-coach.mjs';\r\nimport {readIncomingCoach,saveIncomingCoach,clearIncomingCoach} from './pending-coach.mjs';\r\nimport {decodeHandoff,missingFields,WEBSITE,calendarDay} from './onboarding-domain.mjs?v=quick-install-v1';\r\nimport {mountProfileForm} from './onboarding-form.mjs?v=quick-install-v1';\r\nimport {createOfficeDraft} from './office-domain.mjs?v=office-short-v1';\r\nimport {openLocalCoach,probeOptionalAccount,probeOptionalTransfer} from './local-coach-runtime.mjs';\r\nconst status=document.getElementById('setupStatus'),host=document.getElementById('setupBody'),title=document.querySelector('h1');\r\nconst KEY='myr5-incoming-coach-v1',OFFICE_KEY='myr5-office-draft-v1',params=new URLSearchParams(location.search);\r\nlet account=null,localCoach=null,localScope=null,localIntake=null;\r\nconst transitions=authTransitions(),accountActions=createAccountSessionActions({api,transitions});\r\nasync function api(path,method='GET',data,assertions={},signal){const ticket=transitions.capture();const r=await authFetch(path,{method,guestSkip:method==='GET'&&/^\\/api\\/(?:account\\?core=1|updates\\/subscription)$/.test(path),signal:signal?AbortSignal.any([ticket.signal,signal]):ticket.signal,credentials:'same-origin',headers:{...(data?{'Content-Type':'application/json'}:{}),...assertions},body:data?JSON.stringify(data):undefined,cache:'no-store'});transitions.assertCurrent(ticket);if(!r.headers.get('content-type')?.includes('application/json'))throw Object.assign(Error(r.status>=500?'Coach service is unavailable. Please retry.':'Sign in to save your coach.'),{status:r.status});const value=await r.json();transitions.assertCurrent(ticket);if(!r.ok)throw Object.assign(Error(value.error||'Could not save. Please retry.'),{status:r.status,code:value.code});return value;}\r\nfunction link(text,href,parent=host){const a=document.createElement('a');a.className='setup-action';a.textContent=text;a.href=href;a.target='_top';parent.append(a);return a;}\r\nfunction readDraft(key){try{return JSON.parse(sessionStorage.getItem(key)||'null');}catch{return null;}}\r\nfunction removeDraft(key){try{sessionStorage.removeItem(key);}catch{}}\r\nfunction restore(appearance){for(const [key,v] of Object.entries(appearance||{}))if(['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1'].includes(key))localStorage.setItem(key,typeof v==='string'?v:JSON.stringify(v));}\r\nfunction signIn(returnTo){return signInPath(returnTo);}\r\nfunction chooseRoute(){\r\n title.textContent='Set up Coach';\r\n status.textContent='Choose your route.';\r\n const choices=document.createElement('div');choices.className='setup-route-choices';\r\n for(const route of [\r\n  {theme:'games',heading:'Play the character games',description:'Meet the crew. Build your coach.',label:'Take the fun route',href:WEBSITE+'/tv/?ch=mominc'},\r\n  {theme:'office',heading:'Quick setup',description:'Three questions.',label:readDraft(OFFICE_KEY)?'Resume my paperwork':'Start setup',href:'/onboarding.html?route=office'}\r\n ]){const card=document.createElement('section');card.className='setup-route '+route.theme;const h=document.createElement('h2'),p=document.createElement('p');h.textContent=route.heading;p.textContent=route.description;card.append(h,p);link(route.label,route.href,card);choices.append(card);}\r\n host.replaceChildren(choices);\r\n if(!account)link('Already have a coach? Sign in',signIn('/onboarding.html'));\r\n}\r\nasync function renderForm(data,{edit=false,autoSave=false}={}){\r\n const displayedAccount=account;\r\n if(!edit)data=withQuickDefaults(data);\r\n const office=data.entryRoute==='office',draftKey=office?OFFICE_KEY:KEY;\r\n document.body.classList.toggle('office-mode',office&&edit);document.body.classList.toggle('quick-mode',!edit);\r\n title.textContent=edit?'Settings':'Set up Coach';\r\n status.textContent=edit?'':office?'':'';\r\n const formHost=document.createElement('div'),notice=document.createElement('p');notice.role='status';notice.className='draft-notice';\r\n host.replaceChildren();\r\n\r\n\r\n host.append(formHost,notice);\r\n function remember(value){if(edit)return;try{if(office)sessionStorage.setItem(draftKey,JSON.stringify(value));else saveIncomingCoach(value);notice.textContent='';}catch{notice.textContent='Your browser cannot save a draft. Keep this page open and sign in before completing the form.';}}\r\n async function save(value){\r\n   if(!displayedAccount){const result=await localScope.saveSetup(value,{startDay:calendarDay(Date.now(),value.profile.timezone)});restore(result.intake.appearance);clearIncomingCoach();removeDraft(KEY);removeDraft(OFFICE_KEY);location.replace('/pose.html');return;}\r\n  const saved=await accountActions.saveOnboarding(displayedAccount,value);transitions.assertCurrent(saved.transitionTicket);\r\n  try{\r\n  const currentOwner = localStorage.getItem('myr5-coach-owner');\r\n  let priorSnap = {};\r\n  if (currentOwner === saved.ownerId) {\r\n    const raw = localStorage.getItem('myr5-synced-appearance');\r\n    if (raw) {\r\n      try{\r\n        const parsed = JSON.parse(raw);\r\n        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) priorSnap = parsed;\r\n      }catch(e){/* ignore corrupt baseline */}\r\n    }\r\n  }\r\n  const canonicalKeys = ['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1','myr5-pod-power-v1','handborne-recipe-v4','mbs-dj-identity-v1'];\r\n  const merged = {};\r\n  for(const k of canonicalKeys){\r\n    const snapVal = priorSnap[k];\r\n    if(typeof snapVal==='string') merged[k]=snapVal;\r\n\r\n\r\n  }\r\n  const overrideKeys = ['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1'];\r\n  const appearance = saved.result.appearance;\r\n  if (appearance && typeof appearance === 'object') {\r\n    for(const k of overrideKeys){\r\n      if (appearance[k] != null) {\r\n        merged[k] = typeof appearance[k] === 'object' ? JSON.stringify(appearance[k]) : appearance[k];\r\n      }\r\n    }\r\n  }\r\n  const final = Object.fromEntries(canonicalKeys.filter(k => typeof merged[k]==='string').map(k=>[k, merged[k]]));\r\n  localStorage.setItem('myr5-synced-appearance',JSON.stringify(final));\r\n  restore(saved.result.appearance);\r\n  localStorage.setItem('myr5-coach-owner',saved.ownerId);\r\n}catch{}\r\n  clearIncomingCoach();removeDraft(KEY);removeDraft(OFFICE_KEY);location.replace('/pose.html');\r\n }\r\n if(autoSave&&!missingFields(withQuickDefaults(data)).length){status.textContent=office?'Saving…':'Saving…';try{await save(data);return;}catch(e){status.textContent=e.message;}}\r\n  mountProfileForm(formHost,data,{save,label:edit?'Save settings':'Start my coach',changed:remember,fullSettings:edit});\r\n if(edit)link('Back to Coach','/pose.html');\r\n}\r\ntry{\r\n let incoming=readIncomingCoach();\r\n const raw=new URLSearchParams(location.hash.slice(1)).get('coach');\r\n if(raw){incoming=saveIncomingCoach(decodeHandoff(raw));history.replaceState(null,'',location.pathname+location.search);}\r\n if(params.get('receive')==='1'&&!incoming){status.textContent='Bringing your saved website choices aboard…';incoming=await receiveCoach(raw=>saveIncomingCoach(raw));}\r\n account=await probeOptionalAccount(({signal})=>api('/api/account?core=1','GET',undefined,{},signal));\r\n localCoach=await openLocalCoach();try{localScope=localCoach.forOwner(localCoach.guestOwnerId);localIntake=await localScope.getIntake();}catch(error){localCoach.close();localCoach=null;throw error;}window.addEventListener('pagehide',()=>localCoach?.close(),{once:true});\r\n const edit=params.get('edit')==='1',office=params.get('route')==='office';\r\n if(!edit&&!setupAllowed()&&!account?.onboarding){location.replace('/install.html'+(office?'?route=office':''));}else{\r\n if(setupAllowed()&&!edit&&!raw)incoming=(account?await restoreInstall():await probeOptionalTransfer(({signal})=>restoreInstall(fetch,sessionStorage,localStorage,{signal})))||incoming;\r\n if(edit){\r\n   if(!account&&!localIntake){status.textContent='Set up your coach on this device first.';link('Set up Coach','/onboarding.html');}\r\n   else if(!account){await renderForm(structuredClone(localIntake),{edit:true});}\r\n  else if(!account.onboarding)chooseRoute();\r\n  else {const data=structuredClone(account.onboarding.data);for(const key of ['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1'])if(account.profile[key])data.appearance[key]=JSON.parse(account.profile[key]);data.profile.coach=data.appearance['myr5-recipe-v1'].coach;await renderForm(data,{edit:true});}\r\n }else if(incoming&&!office){await renderForm(incoming,{autoSave:!!account});}\r\n  else if(account?.onboarding){try{restore(account.profile);}catch{}location.replace('/pose.html');}\r\n  else if(localIntake){try{restore(localIntake.appearance);}catch{}location.replace('/pose.html');}\r\n else if(office){let data=readDraft(OFFICE_KEY);if(data?.entryRoute!=='office')data=createOfficeDraft(Intl.DateTimeFormat().resolvedOptions().timeZone);await renderForm(data,{autoSave:params.get('submit')==='1'&&!!account});}\r\n else if(setupAllowed()){const draft=readDraft(OFFICE_KEY);await renderForm(draft?.entryRoute==='office'?draft:createOfficeDraft(Intl.DateTimeFormat().resolvedOptions().timeZone));}\r\n else chooseRoute();\r\n }\r\n}catch(e){status.textContent=e.message;link('Retry coach setup','/onboarding.html');}\r\n","package-lock.json":"{\r\n  \"name\": \"myr5-coach\",\r\n  \"version\": \"1.0.0\",\r\n  \"lockfileVersion\": 3,\r\n  \"requires\": true,\r\n  \"packages\": {\r\n    \"\": {\r\n      \"name\": \"myr5-coach\",\r\n      \"version\": \"1.0.0\",\r\n      \"dependencies\": {\r\n        \"@block65/webcrypto-web-push\": \"^2.0.0\",\r\n        \"@clerk/backend\": \"^3.17.2\",\r\n        \"dexie\": \"^4.4.6\",\r\n        \"three\": \"^0.169.0\",\r\n        \"zod\": \"^4.6.5\"\r\n      },\r\n      \"devDependencies\": {\r\n        \"@huggingface/transformers\": \"^3.8.1\",\r\n        \"@openai/sites-vite-plugin\": \"0.2.0\",\r\n        \"drizzle-kit\": \"0.31.4\",\r\n        \"drizzle-orm\": \"0.44.2\",\r\n        \"esbuild\": \"0.28.0\",\r\n        \"fake-indexeddb\": \"^6.2.5\",\r\n        \"meshoptimizer\": \"^1.3.0\",\r\n        \"miniflare\": \"4.20260515.0\",\r\n        \"playwright\": \"^1.63.0\",\r\n        \"shaders\": \"4.0.4\",\r\n        \"vite\": \"8.0.13\",\r\n        \"wrangler\": \"4.92.0\"\r\n      }\r\n    },\r\n    \"node_modules/@block65/webcrypto-web-push\": {\r\n      \"version\": \"2.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@block65/webcrypto-web-push/-/webcrypto-web-push-2.0.0.tgz\",\r\n      \"integrity\": \"sha512-BMrw8hABq5krABWPy3zFJvBC73HeRkyT/GTZ2AD7wqzo9hd9vO56MstBqjGMIxIw3e4lbRUbYxgihCcfhoTI2g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"uint8array-extras\": \"^1.5.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@clerk/backend\": {\r\n      \"version\": \"3.17.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@clerk/backend/-/backend-3.17.2.tgz\",\r\n      \"integrity\": \"sha512-pQz/+ClFBcL4OijAX3gDcXQYNqr1JbabAqY8szuU8/Dcvuuk3WgUmQDGyIU0tIqmitdd17RLppDmhN088pADew==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@clerk/shared\": \"^4.31.1\",\r\n        \"standardwebhooks\": \"^1.0.0\",\r\n        \"tslib\": \"2.8.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20.9.0\"\r\n      }\r\n    },\r\n    \"node_modules/@clerk/shared\": {\r\n      \"version\": \"4.31.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@clerk/shared/-/shared-4.31.1.tgz\",\r\n      \"integrity\": \"sha512-j3cDEZ/j7r5tAv4mmo2JhpFRtL1z0JghjCgvBJjZSR6Q4ZVIlwd2Bv0loM+opKReeN17H30u1/WHEBl7pZuxrw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@tanstack/query-core\": \"^5.101.4\",\r\n        \"dequal\": \"2.0.3\",\r\n        \"glob-to-regexp\": \"0.4.1\",\r\n        \"js-cookie\": \"3.0.8\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20.9.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0\",\r\n        \"react-dom\": \"^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"react\": {\r\n          \"optional\": true\r\n        },\r\n        \"react-dom\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/kv-asset-handler\": {\r\n      \"version\": \"0.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz\",\r\n      \"integrity\": \"sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/unenv-preset\": {\r\n      \"version\": \"2.16.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz\",\r\n      \"integrity\": \"sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"peerDependencies\": {\r\n        \"unenv\": \"2.0.0-rc.24\",\r\n        \"workerd\": \">1.20260305.0 <2.0.0-0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"workerd\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-darwin-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-Wtw44el2pNbzixvTkWdfeBDTrQwQbJRz7/JUvPKV27I0pQWXbhNJPpM8cstq/pbrU5AGcA/HjFH6yPMRTIRKig==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-darwin-arm64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-X8EqkZej6FfmhF9AVAQ3FhyQRr9acS4RcDunMU2YiuxKHF1IU8zzH3vY30/POaG+rUu9vGDp/VgUl49VPenHJQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-linux-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-CDC89QxQ7Y7t7RG1Jd9vj/qolE1sQRkI2OSEuV5BMJi0vW/gV4OVG6xjpdK3b1OYnSWDzF7NpvlR5Yg86q7k4g==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-linux-arm64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-WxbW/PToYES4fvHXzsr/5qOiETQs/Z9iZ0mjSZAiEwq5cMLZemzGN0COx+uFb9OvQwzh6Pg159qPFnw3+i9FuA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cloudflare/workerd-windows-64\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-WmV/iv+MHjYsvkcMVzpM2B5/mf06UUkdpVhZrtMfV9graWjBGPYFvE/eab8748RPVGKh1Xe1vXofLzDSwc08lA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      }\r\n    },\r\n    \"node_modules/@cspotcode/source-map-support\": {\r\n      \"version\": \"0.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz\",\r\n      \"integrity\": \"sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/trace-mapping\": \"0.3.9\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@drizzle-team/brocli\": {\r\n      \"version\": \"0.10.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz\",\r\n      \"integrity\": \"sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/@emnapi/core\": {\r\n      \"version\": \"1.10.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz\",\r\n      \"integrity\": \"sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/wasi-threads\": \"1.2.1\",\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@emnapi/runtime\": {\r\n      \"version\": \"1.11.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz\",\r\n      \"integrity\": \"sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@emnapi/wasi-threads\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils\": {\r\n      \"version\": \"3.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz\",\r\n      \"integrity\": \"sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==\",\r\n      \"deprecated\": \"Merged into tsx: https://tsx.hirok.io\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"esbuild\": \"~0.18.20\",\r\n        \"source-map-support\": \"^0.5.21\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==\",\r\n      \"cpu\": [\r\n        \"loong64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==\",\r\n      \"cpu\": [\r\n        \"mips64el\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"sunos\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/core-utils/node_modules/esbuild\": {\r\n      \"version\": \"0.18.20\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz\",\r\n      \"integrity\": \"sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"esbuild\": \"bin/esbuild\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@esbuild/android-arm\": \"0.18.20\",\r\n        \"@esbuild/android-arm64\": \"0.18.20\",\r\n        \"@esbuild/android-x64\": \"0.18.20\",\r\n        \"@esbuild/darwin-arm64\": \"0.18.20\",\r\n        \"@esbuild/darwin-x64\": \"0.18.20\",\r\n        \"@esbuild/freebsd-arm64\": \"0.18.20\",\r\n        \"@esbuild/freebsd-x64\": \"0.18.20\",\r\n        \"@esbuild/linux-arm\": \"0.18.20\",\r\n        \"@esbuild/linux-arm64\": \"0.18.20\",\r\n        \"@esbuild/linux-ia32\": \"0.18.20\",\r\n        \"@esbuild/linux-loong64\": \"0.18.20\",\r\n        \"@esbuild/linux-mips64el\": \"0.18.20\",\r\n        \"@esbuild/linux-ppc64\": \"0.18.20\",\r\n        \"@esbuild/linux-riscv64\": \"0.18.20\",\r\n        \"@esbuild/linux-s390x\": \"0.18.20\",\r\n        \"@esbuild/linux-x64\": \"0.18.20\",\r\n        \"@esbuild/netbsd-x64\": \"0.18.20\",\r\n        \"@esbuild/openbsd-x64\": \"0.18.20\",\r\n        \"@esbuild/sunos-x64\": \"0.18.20\",\r\n        \"@esbuild/win32-arm64\": \"0.18.20\",\r\n        \"@esbuild/win32-ia32\": \"0.18.20\",\r\n        \"@esbuild/win32-x64\": \"0.18.20\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild-kit/esm-loader\": {\r\n      \"version\": \"2.6.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz\",\r\n      \"integrity\": \"sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==\",\r\n      \"deprecated\": \"Merged into tsx: https://tsx.hirok.io\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@esbuild-kit/core-utils\": \"^3.3.2\",\r\n        \"get-tsconfig\": \"^4.7.0\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/aix-ppc64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"aix\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-arm\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/android-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/darwin-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/darwin-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/freebsd-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/freebsd-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-arm\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-ia32\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-loong64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==\",\r\n      \"cpu\": [\r\n        \"loong64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-mips64el\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==\",\r\n      \"cpu\": [\r\n        \"mips64el\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-ppc64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-riscv64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-s390x\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/linux-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/netbsd-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/netbsd-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openbsd-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openbsd-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/openharmony-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/sunos-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"sunos\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-arm64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-ia32\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@esbuild/win32-x64\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@huggingface/jinja\": {\r\n      \"version\": \"0.5.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.10.tgz\",\r\n      \"integrity\": \"sha512-SgS1D1bglQ94ceD4ZCL6eayUDy9uV1xuyk61OjgSxU5GJh7upqZCKII0JoTYMc6wWsg3JUgaPRX0ElQzXPk3Cw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@huggingface/transformers\": {\r\n      \"version\": \"3.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@huggingface/transformers/-/transformers-3.8.1.tgz\",\r\n      \"integrity\": \"sha512-tsTk4zVjImqdqjS8/AOZg2yNLd1z9S5v+7oUPpXaasDRwEDhB+xnglK1k5cad26lL5/ZIaeREgWWy0bs9y9pPA==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"@huggingface/jinja\": \"^0.5.3\",\r\n        \"onnxruntime-node\": \"1.21.0\",\r\n        \"onnxruntime-web\": \"1.22.0-dev.20250409-89f8206ba4\",\r\n        \"sharp\": \"^0.34.1\"\r\n      }\r\n    },\r\n    \"node_modules/@img/colour\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-darwin-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-darwin-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-darwin-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-darwin-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-darwin-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-darwin-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-arm\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-ppc64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-riscv64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-s390x\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linux-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linuxmusl-arm64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-libvips-linuxmusl-x64\": {\r\n      \"version\": \"1.2.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz\",\r\n      \"integrity\": \"sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-arm\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-arm\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-ppc64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-ppc64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-riscv64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-riscv64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-s390x\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-s390x\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linux-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linux-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linuxmusl-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linuxmusl-arm64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-linuxmusl-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"Apache-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-libvips-linuxmusl-x64\": \"1.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-wasm32\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==\",\r\n      \"cpu\": [\r\n        \"wasm32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later AND MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/runtime\": \"^1.7.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-arm64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-ia32\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@img/sharp-win32-x64\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0 AND LGPL-3.0-or-later\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      }\r\n    },\r\n    \"node_modules/@isaacs/fs-minipass\": {\r\n      \"version\": \"4.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz\",\r\n      \"integrity\": \"sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"minipass\": \"^7.0.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/resolve-uri\": {\r\n      \"version\": \"3.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz\",\r\n      \"integrity\": \"sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@jridgewell/sourcemap-codec\": {\r\n      \"version\": \"1.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz\",\r\n      \"integrity\": \"sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@jridgewell/trace-mapping\": {\r\n      \"version\": \"0.3.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz\",\r\n      \"integrity\": \"sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@jridgewell/resolve-uri\": \"^3.0.3\",\r\n        \"@jridgewell/sourcemap-codec\": \"^1.4.10\"\r\n      }\r\n    },\r\n    \"node_modules/@napi-rs/wasm-runtime\": {\r\n      \"version\": \"1.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz\",\r\n      \"integrity\": \"sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@tybys/wasm-util\": \"^0.10.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || ^22.13.0 || >=23.5.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/Brooooooklyn\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@emnapi/core\": \"^1.7.1 || ^2.0.0-alpha.4\",\r\n        \"@emnapi/runtime\": \"^1.7.1 || ^2.0.0-alpha.4\"\r\n      }\r\n    },\r\n    \"node_modules/@openai/sites-vite-plugin\": {\r\n      \"version\": \"0.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@openai/sites-vite-plugin/-/sites-vite-plugin-0.2.0.tgz\",\r\n      \"integrity\": \"sha512-4ThLMW9eaf2gd+SqBwHmqBONKBAbQ2BuOFev7mHq7Y2W5mm/rfVJOSjcgQIsY2PqdXxXNZ4Yd0+s0DlSUYehag==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=22.13.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"vite\": \"^8.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@oxc-project/types\": {\r\n      \"version\": \"0.130.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz\",\r\n      \"integrity\": \"sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/Boshen\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/colors\": {\r\n      \"version\": \"4.1.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz\",\r\n      \"integrity\": \"sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"kleur\": \"^4.1.5\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/dumper\": {\r\n      \"version\": \"0.6.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz\",\r\n      \"integrity\": \"sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/colors\": \"^4.1.5\",\r\n        \"@sindresorhus/is\": \"^7.0.2\",\r\n        \"supports-color\": \"^10.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/@poppinss/exception\": {\r\n      \"version\": \"1.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz\",\r\n      \"integrity\": \"sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@protobufjs/aspromise\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/base64\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/codegen\": {\r\n      \"version\": \"2.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz\",\r\n      \"integrity\": \"sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/eventemitter\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/fetch\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"@protobufjs/aspromise\": \"^1.1.1\"\r\n      }\r\n    },\r\n    \"node_modules/@protobufjs/float\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/path\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/pool\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@protobufjs/utf8\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/@rolldown/binding-android-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-darwin-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-darwin-x64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-freebsd-x64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-arm64-musl\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-ppc64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-s390x-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-x64-gnu\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-linux-x64-musl\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-openharmony-arm64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-wasm32-wasi\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==\",\r\n      \"cpu\": [\r\n        \"wasm32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"@emnapi/core\": \"1.10.0\",\r\n        \"@emnapi/runtime\": \"1.10.0\",\r\n        \"@napi-rs/wasm-runtime\": \"^1.1.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime\": {\r\n      \"version\": \"1.10.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz\",\r\n      \"integrity\": \"sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-win32-arm64-msvc\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/binding-win32-x64-msvc\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@rolldown/pluginutils\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@sindresorhus/is\": {\r\n      \"version\": \"7.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz\",\r\n      \"integrity\": \"sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sindresorhus/is?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/@speed-highlight/core\": {\r\n      \"version\": \"1.2.24\",\r\n      \"resolved\": \"https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.24.tgz\",\r\n      \"integrity\": \"sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==\",\r\n      \"dev\": true,\r\n      \"license\": \"CC0-1.0\"\r\n    },\r\n    \"node_modules/@stablelib/base64\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/@tanstack/query-core\": {\r\n      \"version\": \"5.102.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.102.8.tgz\",\r\n      \"integrity\": \"sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"type\": \"github\",\r\n        \"url\": \"https://github.com/sponsors/tannerlinsley\"\r\n      }\r\n    },\r\n    \"node_modules/@tybys/wasm-util\": {\r\n      \"version\": \"0.10.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz\",\r\n      \"integrity\": \"sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"dependencies\": {\r\n        \"tslib\": \"^2.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/@types/node\": {\r\n      \"version\": \"22.20.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/@types/node/-/node-22.20.2.tgz\",\r\n      \"integrity\": \"sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"undici-types\": \"~6.21.0\"\r\n      }\r\n    },\r\n    \"node_modules/blake3-wasm\": {\r\n      \"version\": \"2.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz\",\r\n      \"integrity\": \"sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/boolean\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==\",\r\n      \"deprecated\": \"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/buffer-from\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/chownr\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==\",\r\n      \"dev\": true,\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/cookie\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/express\"\r\n      }\r\n    },\r\n    \"node_modules/debug\": {\r\n      \"version\": \"4.4.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\r\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ms\": \"^2.1.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"supports-color\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/define-data-property\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-define-property\": \"^1.0.0\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"gopd\": \"^1.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/define-properties\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"define-data-property\": \"^1.0.1\",\r\n        \"has-property-descriptors\": \"^1.0.0\",\r\n        \"object-keys\": \"^1.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/dequal\": {\r\n      \"version\": \"2.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz\",\r\n      \"integrity\": \"sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/detect-libc\": {\r\n      \"version\": \"2.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz\",\r\n      \"integrity\": \"sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/detect-node\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/dexie\": {\r\n      \"version\": \"4.4.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/dexie/-/dexie-4.4.6.tgz\",\r\n      \"integrity\": \"sha512-hJP/BO6mjB+tX6hToIO1kmxYLNmun90wYbfcoAoLpKEyDYal/k33dg0TAfoUe2TDsbbLoVIzljJ3BN2UbR5EOg==\",\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/drizzle-kit\": {\r\n      \"version\": \"0.31.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz\",\r\n      \"integrity\": \"sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@drizzle-team/brocli\": \"^0.10.2\",\r\n        \"@esbuild-kit/esm-loader\": \"^2.5.5\",\r\n        \"esbuild\": \"^0.25.4\",\r\n        \"esbuild-register\": \"^3.5.0\"\r\n      },\r\n      \"bin\": {\r\n        \"drizzle-kit\": \"bin.cjs\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/aix-ppc64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"aix\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/android-arm\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/android-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/android-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/darwin-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/darwin-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/freebsd-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/freebsd-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-arm\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-ia32\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-loong64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==\",\r\n      \"cpu\": [\r\n        \"loong64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-mips64el\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==\",\r\n      \"cpu\": [\r\n        \"mips64el\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-ppc64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-riscv64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-s390x\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/linux-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/netbsd-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/netbsd-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/openbsd-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/openbsd-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/openharmony-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/sunos-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"sunos\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/win32-arm64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/win32-ia32\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/@esbuild/win32-x64\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-kit/node_modules/esbuild\": {\r\n      \"version\": \"0.25.12\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz\",\r\n      \"integrity\": \"sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"esbuild\": \"bin/esbuild\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@esbuild/aix-ppc64\": \"0.25.12\",\r\n        \"@esbuild/android-arm\": \"0.25.12\",\r\n        \"@esbuild/android-arm64\": \"0.25.12\",\r\n        \"@esbuild/android-x64\": \"0.25.12\",\r\n        \"@esbuild/darwin-arm64\": \"0.25.12\",\r\n        \"@esbuild/darwin-x64\": \"0.25.12\",\r\n        \"@esbuild/freebsd-arm64\": \"0.25.12\",\r\n        \"@esbuild/freebsd-x64\": \"0.25.12\",\r\n        \"@esbuild/linux-arm\": \"0.25.12\",\r\n        \"@esbuild/linux-arm64\": \"0.25.12\",\r\n        \"@esbuild/linux-ia32\": \"0.25.12\",\r\n        \"@esbuild/linux-loong64\": \"0.25.12\",\r\n        \"@esbuild/linux-mips64el\": \"0.25.12\",\r\n        \"@esbuild/linux-ppc64\": \"0.25.12\",\r\n        \"@esbuild/linux-riscv64\": \"0.25.12\",\r\n        \"@esbuild/linux-s390x\": \"0.25.12\",\r\n        \"@esbuild/linux-x64\": \"0.25.12\",\r\n        \"@esbuild/netbsd-arm64\": \"0.25.12\",\r\n        \"@esbuild/netbsd-x64\": \"0.25.12\",\r\n        \"@esbuild/openbsd-arm64\": \"0.25.12\",\r\n        \"@esbuild/openbsd-x64\": \"0.25.12\",\r\n        \"@esbuild/openharmony-arm64\": \"0.25.12\",\r\n        \"@esbuild/sunos-x64\": \"0.25.12\",\r\n        \"@esbuild/win32-arm64\": \"0.25.12\",\r\n        \"@esbuild/win32-ia32\": \"0.25.12\",\r\n        \"@esbuild/win32-x64\": \"0.25.12\"\r\n      }\r\n    },\r\n    \"node_modules/drizzle-orm\": {\r\n      \"version\": \"0.44.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.2.tgz\",\r\n      \"integrity\": \"sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"peerDependencies\": {\r\n        \"@aws-sdk/client-rds-data\": \">=3\",\r\n        \"@cloudflare/workers-types\": \">=4\",\r\n        \"@electric-sql/pglite\": \">=0.2.0\",\r\n        \"@libsql/client\": \">=0.10.0\",\r\n        \"@libsql/client-wasm\": \">=0.10.0\",\r\n        \"@neondatabase/serverless\": \">=0.10.0\",\r\n        \"@op-engineering/op-sqlite\": \">=2\",\r\n        \"@opentelemetry/api\": \"^1.4.1\",\r\n        \"@planetscale/database\": \">=1.13\",\r\n        \"@prisma/client\": \"*\",\r\n        \"@tidbcloud/serverless\": \"*\",\r\n        \"@types/better-sqlite3\": \"*\",\r\n        \"@types/pg\": \"*\",\r\n        \"@types/sql.js\": \"*\",\r\n        \"@upstash/redis\": \">=1.34.7\",\r\n        \"@vercel/postgres\": \">=0.8.0\",\r\n        \"@xata.io/client\": \"*\",\r\n        \"better-sqlite3\": \">=7\",\r\n        \"bun-types\": \"*\",\r\n        \"expo-sqlite\": \">=14.0.0\",\r\n        \"gel\": \">=2\",\r\n        \"knex\": \"*\",\r\n        \"kysely\": \"*\",\r\n        \"mysql2\": \">=2\",\r\n        \"pg\": \">=8\",\r\n        \"postgres\": \">=3\",\r\n        \"sql.js\": \">=1\",\r\n        \"sqlite3\": \">=5\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@aws-sdk/client-rds-data\": {\r\n          \"optional\": true\r\n        },\r\n        \"@cloudflare/workers-types\": {\r\n          \"optional\": true\r\n        },\r\n        \"@electric-sql/pglite\": {\r\n          \"optional\": true\r\n        },\r\n        \"@libsql/client\": {\r\n          \"optional\": true\r\n        },\r\n        \"@libsql/client-wasm\": {\r\n          \"optional\": true\r\n        },\r\n        \"@neondatabase/serverless\": {\r\n          \"optional\": true\r\n        },\r\n        \"@op-engineering/op-sqlite\": {\r\n          \"optional\": true\r\n        },\r\n        \"@opentelemetry/api\": {\r\n          \"optional\": true\r\n        },\r\n        \"@planetscale/database\": {\r\n          \"optional\": true\r\n        },\r\n        \"@prisma/client\": {\r\n          \"optional\": true\r\n        },\r\n        \"@tidbcloud/serverless\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/better-sqlite3\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/pg\": {\r\n          \"optional\": true\r\n        },\r\n        \"@types/sql.js\": {\r\n          \"optional\": true\r\n        },\r\n        \"@upstash/redis\": {\r\n          \"optional\": true\r\n        },\r\n        \"@vercel/postgres\": {\r\n          \"optional\": true\r\n        },\r\n        \"@xata.io/client\": {\r\n          \"optional\": true\r\n        },\r\n        \"better-sqlite3\": {\r\n          \"optional\": true\r\n        },\r\n        \"bun-types\": {\r\n          \"optional\": true\r\n        },\r\n        \"expo-sqlite\": {\r\n          \"optional\": true\r\n        },\r\n        \"gel\": {\r\n          \"optional\": true\r\n        },\r\n        \"knex\": {\r\n          \"optional\": true\r\n        },\r\n        \"kysely\": {\r\n          \"optional\": true\r\n        },\r\n        \"mysql2\": {\r\n          \"optional\": true\r\n        },\r\n        \"pg\": {\r\n          \"optional\": true\r\n        },\r\n        \"postgres\": {\r\n          \"optional\": true\r\n        },\r\n        \"prisma\": {\r\n          \"optional\": true\r\n        },\r\n        \"sql.js\": {\r\n          \"optional\": true\r\n        },\r\n        \"sqlite3\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/error-stack-parser-es\": {\r\n      \"version\": \"1.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz\",\r\n      \"integrity\": \"sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/antfu\"\r\n      }\r\n    },\r\n    \"node_modules/es-define-property\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-errors\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es6-error\": {\r\n      \"version\": \"4.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz\",\r\n      \"integrity\": \"sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/esbuild\": {\r\n      \"version\": \"0.28.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz\",\r\n      \"integrity\": \"sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"esbuild\": \"bin/esbuild\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@esbuild/aix-ppc64\": \"0.28.0\",\r\n        \"@esbuild/android-arm\": \"0.28.0\",\r\n        \"@esbuild/android-arm64\": \"0.28.0\",\r\n        \"@esbuild/android-x64\": \"0.28.0\",\r\n        \"@esbuild/darwin-arm64\": \"0.28.0\",\r\n        \"@esbuild/darwin-x64\": \"0.28.0\",\r\n        \"@esbuild/freebsd-arm64\": \"0.28.0\",\r\n        \"@esbuild/freebsd-x64\": \"0.28.0\",\r\n        \"@esbuild/linux-arm\": \"0.28.0\",\r\n        \"@esbuild/linux-arm64\": \"0.28.0\",\r\n        \"@esbuild/linux-ia32\": \"0.28.0\",\r\n        \"@esbuild/linux-loong64\": \"0.28.0\",\r\n        \"@esbuild/linux-mips64el\": \"0.28.0\",\r\n        \"@esbuild/linux-ppc64\": \"0.28.0\",\r\n        \"@esbuild/linux-riscv64\": \"0.28.0\",\r\n        \"@esbuild/linux-s390x\": \"0.28.0\",\r\n        \"@esbuild/linux-x64\": \"0.28.0\",\r\n        \"@esbuild/netbsd-arm64\": \"0.28.0\",\r\n        \"@esbuild/netbsd-x64\": \"0.28.0\",\r\n        \"@esbuild/openbsd-arm64\": \"0.28.0\",\r\n        \"@esbuild/openbsd-x64\": \"0.28.0\",\r\n        \"@esbuild/openharmony-arm64\": \"0.28.0\",\r\n        \"@esbuild/sunos-x64\": \"0.28.0\",\r\n        \"@esbuild/win32-arm64\": \"0.28.0\",\r\n        \"@esbuild/win32-ia32\": \"0.28.0\",\r\n        \"@esbuild/win32-x64\": \"0.28.0\"\r\n      }\r\n    },\r\n    \"node_modules/esbuild-register\": {\r\n      \"version\": \"3.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz\",\r\n      \"integrity\": \"sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"debug\": \"^4.3.4\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"esbuild\": \">=0.12 <1\"\r\n      }\r\n    },\r\n    \"node_modules/escape-string-regexp\": {\r\n      \"version\": \"4.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz\",\r\n      \"integrity\": \"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/fake-indexeddb\": {\r\n      \"version\": \"6.2.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-6.2.5.tgz\",\r\n      \"integrity\": \"sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/fast-sha256\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==\",\r\n      \"license\": \"Unlicense\"\r\n    },\r\n    \"node_modules/fdir\": {\r\n      \"version\": \"6.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz\",\r\n      \"integrity\": \"sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"picomatch\": \"^3 || ^4\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"picomatch\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/flatbuffers\": {\r\n      \"version\": \"25.9.23\",\r\n      \"resolved\": \"https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz\",\r\n      \"integrity\": \"sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/fsevents\": {\r\n      \"version\": \"2.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz\",\r\n      \"integrity\": \"sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \"^8.16.0 || ^10.6.0 || >=11.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/get-tsconfig\": {\r\n      \"version\": \"4.14.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz\",\r\n      \"integrity\": \"sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"resolve-pkg-maps\": \"^1.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/privatenumber/get-tsconfig?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/glob-to-regexp\": {\r\n      \"version\": \"0.4.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz\",\r\n      \"integrity\": \"sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==\",\r\n      \"license\": \"BSD-2-Clause\"\r\n    },\r\n    \"node_modules/global-agent\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"boolean\": \"^3.0.1\",\r\n        \"es6-error\": \"^4.1.1\",\r\n        \"matcher\": \"^3.0.0\",\r\n        \"roarr\": \"^2.15.3\",\r\n        \"semver\": \"^7.3.2\",\r\n        \"serialize-error\": \"^7.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10.0\"\r\n      }\r\n    },\r\n    \"node_modules/global-agent/node_modules/serialize-error\": {\r\n      \"version\": \"7.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz\",\r\n      \"integrity\": \"sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"type-fest\": \"^0.13.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/global-agent/node_modules/type-fest\": {\r\n      \"version\": \"0.13.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz\",\r\n      \"integrity\": \"sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==\",\r\n      \"dev\": true,\r\n      \"license\": \"(MIT OR CC0-1.0)\",\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/globalthis\": {\r\n      \"version\": \"1.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz\",\r\n      \"integrity\": \"sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"define-properties\": \"^1.2.1\",\r\n        \"gopd\": \"^1.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/gopd\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/guid-typescript\": {\r\n      \"version\": \"1.0.9\",\r\n      \"resolved\": \"https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz\",\r\n      \"integrity\": \"sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/has-property-descriptors\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-define-property\": \"^1.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/js-cookie\": {\r\n      \"version\": \"3.0.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz\",\r\n      \"integrity\": \"sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/json-stringify-safe\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/kleur\": {\r\n      \"version\": \"4.1.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz\",\r\n      \"integrity\": \"sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"dependencies\": {\r\n        \"detect-libc\": \"^2.0.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"lightningcss-android-arm64\": \"1.33.0\",\r\n        \"lightningcss-darwin-arm64\": \"1.33.0\",\r\n        \"lightningcss-darwin-x64\": \"1.33.0\",\r\n        \"lightningcss-freebsd-x64\": \"1.33.0\",\r\n        \"lightningcss-linux-arm-gnueabihf\": \"1.33.0\",\r\n        \"lightningcss-linux-arm64-gnu\": \"1.33.0\",\r\n        \"lightningcss-linux-arm64-musl\": \"1.33.0\",\r\n        \"lightningcss-linux-x64-gnu\": \"1.33.0\",\r\n        \"lightningcss-linux-x64-musl\": \"1.33.0\",\r\n        \"lightningcss-win32-arm64-msvc\": \"1.33.0\",\r\n        \"lightningcss-win32-x64-msvc\": \"1.33.0\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-android-arm64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-darwin-arm64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-darwin-x64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-freebsd-x64\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm-gnueabihf\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm64-gnu\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-arm64-musl\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-x64-gnu\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"glibc\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-linux-x64-musl\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"libc\": [\r\n        \"musl\"\r\n      ],\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-win32-arm64-msvc\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/lightningcss-win32-x64-msvc\": {\r\n      \"version\": \"1.33.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz\",\r\n      \"integrity\": \"sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MPL-2.0\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">= 12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"type\": \"opencollective\",\r\n        \"url\": \"https://opencollective.com/parcel\"\r\n      }\r\n    },\r\n    \"node_modules/long\": {\r\n      \"version\": \"5.3.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/long/-/long-5.3.2.tgz\",\r\n      \"integrity\": \"sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/matcher\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"escape-string-regexp\": \"^4.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/meshoptimizer\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-2oZqMAcEEiCtXWUjIGtZ71oPx1B0ZxYGTqIKsXB1wzWFhleNsCFxQuUJzgCV706bRcwPWtMGf1odmQvAcqKkIw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/miniflare\": {\r\n      \"version\": \"4.20260515.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/miniflare/-/miniflare-4.20260515.0.tgz\",\r\n      \"integrity\": \"sha512-2j0oQWizk1Eu4Cm8tDX7Z+Nsjd0nebIj1TQcQ+Oy1QKeo0Ay9+bdn8wfLAtOj9znDCybDCUlnS1+nYvKXEdfNg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@cspotcode/source-map-support\": \"0.8.1\",\r\n        \"sharp\": \"^0.34.5\",\r\n        \"undici\": \"7.24.8\",\r\n        \"workerd\": \"1.20260515.1\",\r\n        \"ws\": \"8.18.0\",\r\n        \"youch\": \"4.1.0-beta.10\"\r\n      },\r\n      \"bin\": {\r\n        \"miniflare\": \"bootstrap.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/minipass\": {\r\n      \"version\": \"7.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz\",\r\n      \"integrity\": \"sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==\",\r\n      \"dev\": true,\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"engines\": {\r\n        \"node\": \">=16 || 14 >=14.17\"\r\n      }\r\n    },\r\n    \"node_modules/minizlib\": {\r\n      \"version\": \"3.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz\",\r\n      \"integrity\": \"sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"minipass\": \"^7.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 18\"\r\n      }\r\n    },\r\n    \"node_modules/ms\": {\r\n      \"version\": \"2.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\r\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/nanoid\": {\r\n      \"version\": \"3.3.18\",\r\n      \"resolved\": \"https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz\",\r\n      \"integrity\": \"sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==\",\r\n      \"dev\": true,\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"nanoid\": \"bin/nanoid.cjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^10 || ^12 || ^13.7 || ^14 || >=15.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/object-keys\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/onnxruntime-common\": {\r\n      \"version\": \"1.21.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.21.0.tgz\",\r\n      \"integrity\": \"sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/onnxruntime-node\": {\r\n      \"version\": \"1.21.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.21.0.tgz\",\r\n      \"integrity\": \"sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"os\": [\r\n        \"win32\",\r\n        \"darwin\",\r\n        \"linux\"\r\n      ],\r\n      \"dependencies\": {\r\n        \"global-agent\": \"^3.0.0\",\r\n        \"onnxruntime-common\": \"1.21.0\",\r\n        \"tar\": \"^7.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/onnxruntime-web\": {\r\n      \"version\": \"1.22.0-dev.20250409-89f8206ba4\",\r\n      \"resolved\": \"https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.22.0-dev.20250409-89f8206ba4.tgz\",\r\n      \"integrity\": \"sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"flatbuffers\": \"^25.1.24\",\r\n        \"guid-typescript\": \"^1.0.9\",\r\n        \"long\": \"^5.2.3\",\r\n        \"onnxruntime-common\": \"1.22.0-dev.20250409-89f8206ba4\",\r\n        \"platform\": \"^1.3.6\",\r\n        \"protobufjs\": \"^7.2.4\"\r\n      }\r\n    },\r\n    \"node_modules/onnxruntime-web/node_modules/onnxruntime-common\": {\r\n      \"version\": \"1.22.0-dev.20250409-89f8206ba4\",\r\n      \"resolved\": \"https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.22.0-dev.20250409-89f8206ba4.tgz\",\r\n      \"integrity\": \"sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/path-to-regexp\": {\r\n      \"version\": \"6.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz\",\r\n      \"integrity\": \"sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/pathe\": {\r\n      \"version\": \"2.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz\",\r\n      \"integrity\": \"sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/picocolors\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/picomatch\": {\r\n      \"version\": \"4.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz\",\r\n      \"integrity\": \"sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/jonschlinkert\"\r\n      }\r\n    },\r\n    \"node_modules/platform\": {\r\n      \"version\": \"1.3.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/platform/-/platform-1.3.6.tgz\",\r\n      \"integrity\": \"sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/playwright\": {\r\n      \"version\": \"1.63.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz\",\r\n      \"integrity\": \"sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"playwright-core\": \"1.63.0\"\r\n      },\r\n      \"bin\": {\r\n        \"playwright\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      }\r\n    },\r\n    \"node_modules/playwright-core\": {\r\n      \"version\": \"1.63.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz\",\r\n      \"integrity\": \"sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"bin\": {\r\n        \"playwright-core\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=20\"\r\n      }\r\n    },\r\n    \"node_modules/postcss\": {\r\n      \"version\": \"8.5.28\",\r\n      \"resolved\": \"https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz\",\r\n      \"integrity\": \"sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==\",\r\n      \"dev\": true,\r\n      \"funding\": [\r\n        {\r\n          \"type\": \"opencollective\",\r\n          \"url\": \"https://opencollective.com/postcss/\"\r\n        },\r\n        {\r\n          \"type\": \"tidelift\",\r\n          \"url\": \"https://tidelift.com/funding/github/npm/postcss\"\r\n        },\r\n        {\r\n          \"type\": \"github\",\r\n          \"url\": \"https://github.com/sponsors/ai\"\r\n        }\r\n      ],\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"nanoid\": \"^3.3.18\",\r\n        \"picocolors\": \"^1.1.1\",\r\n        \"source-map-js\": \"^1.2.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^10 || ^12 || >=14\"\r\n      }\r\n    },\r\n    \"node_modules/protobufjs\": {\r\n      \"version\": \"7.6.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz\",\r\n      \"integrity\": \"sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"@protobufjs/aspromise\": \"^1.1.2\",\r\n        \"@protobufjs/base64\": \"^1.1.2\",\r\n        \"@protobufjs/codegen\": \"^2.0.5\",\r\n        \"@protobufjs/eventemitter\": \"^1.1.1\",\r\n        \"@protobufjs/fetch\": \"^1.1.1\",\r\n        \"@protobufjs/float\": \"^1.0.2\",\r\n        \"@protobufjs/path\": \"^1.1.2\",\r\n        \"@protobufjs/pool\": \"^1.1.0\",\r\n        \"@protobufjs/utf8\": \"^1.1.1\",\r\n        \"@types/node\": \">=13.7.0\",\r\n        \"long\": \"^5.3.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/resolve-pkg-maps\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/privatenumber/resolve-pkg-maps?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/roarr\": {\r\n      \"version\": \"2.15.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz\",\r\n      \"integrity\": \"sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"boolean\": \"^3.0.1\",\r\n        \"detect-node\": \"^2.0.4\",\r\n        \"globalthis\": \"^1.0.1\",\r\n        \"json-stringify-safe\": \"^5.0.1\",\r\n        \"semver-compare\": \"^1.0.0\",\r\n        \"sprintf-js\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8.0\"\r\n      }\r\n    },\r\n    \"node_modules/rolldown\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@oxc-project/types\": \"=0.130.0\",\r\n        \"@rolldown/pluginutils\": \"^1.0.0\"\r\n      },\r\n      \"bin\": {\r\n        \"rolldown\": \"bin/cli.mjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@rolldown/binding-android-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-darwin-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-darwin-x64\": \"1.0.1\",\r\n        \"@rolldown/binding-freebsd-x64\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm-gnueabihf\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-arm64-musl\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-ppc64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-s390x-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-x64-gnu\": \"1.0.1\",\r\n        \"@rolldown/binding-linux-x64-musl\": \"1.0.1\",\r\n        \"@rolldown/binding-openharmony-arm64\": \"1.0.1\",\r\n        \"@rolldown/binding-wasm32-wasi\": \"1.0.1\",\r\n        \"@rolldown/binding-win32-arm64-msvc\": \"1.0.1\",\r\n        \"@rolldown/binding-win32-x64-msvc\": \"1.0.1\"\r\n      }\r\n    },\r\n    \"node_modules/semver\": {\r\n      \"version\": \"7.8.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/semver/-/semver-7.8.5.tgz\",\r\n      \"integrity\": \"sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==\",\r\n      \"dev\": true,\r\n      \"license\": \"ISC\",\r\n      \"bin\": {\r\n        \"semver\": \"bin/semver.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/semver-compare\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/shaders\": {\r\n      \"version\": \"4.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/shaders/-/shaders-4.0.4.tgz\",\r\n      \"integrity\": \"sha512-NB+/RiK0oydteeeKdPqsLpR3iSoDTvt8bsdgw2wPb697QVCY/yadZV8gitCyCQx5t/kb4nJicIR+cbG9Q6JFfQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"typegpu\": \"0.12.3\"\r\n      },\r\n      \"bin\": {\r\n        \"shaders\": \"dist/cli.js\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"react\": \"^18.0.0 || ^19.0.0\",\r\n        \"react-dom\": \"^18.0.0 || ^19.0.0\",\r\n        \"solid-js\": \"^1.8.0\",\r\n        \"svelte\": \"^5\",\r\n        \"vue\": \"^3.5.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"react\": {\r\n          \"optional\": true\r\n        },\r\n        \"react-dom\": {\r\n          \"optional\": true\r\n        },\r\n        \"solid-js\": {\r\n          \"optional\": true\r\n        },\r\n        \"svelte\": {\r\n          \"optional\": true\r\n        },\r\n        \"vue\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/sharp\": {\r\n      \"version\": \"0.34.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz\",\r\n      \"integrity\": \"sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"@img/colour\": \"^1.0.0\",\r\n        \"detect-libc\": \"^2.1.2\",\r\n        \"semver\": \"^7.7.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://opencollective.com/libvips\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@img/sharp-darwin-arm64\": \"0.34.5\",\r\n        \"@img/sharp-darwin-x64\": \"0.34.5\",\r\n        \"@img/sharp-libvips-darwin-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-darwin-x64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-arm\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-ppc64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-riscv64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-s390x\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linux-x64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linuxmusl-arm64\": \"1.2.4\",\r\n        \"@img/sharp-libvips-linuxmusl-x64\": \"1.2.4\",\r\n        \"@img/sharp-linux-arm\": \"0.34.5\",\r\n        \"@img/sharp-linux-arm64\": \"0.34.5\",\r\n        \"@img/sharp-linux-ppc64\": \"0.34.5\",\r\n        \"@img/sharp-linux-riscv64\": \"0.34.5\",\r\n        \"@img/sharp-linux-s390x\": \"0.34.5\",\r\n        \"@img/sharp-linux-x64\": \"0.34.5\",\r\n        \"@img/sharp-linuxmusl-arm64\": \"0.34.5\",\r\n        \"@img/sharp-linuxmusl-x64\": \"0.34.5\",\r\n        \"@img/sharp-wasm32\": \"0.34.5\",\r\n        \"@img/sharp-win32-arm64\": \"0.34.5\",\r\n        \"@img/sharp-win32-ia32\": \"0.34.5\",\r\n        \"@img/sharp-win32-x64\": \"0.34.5\"\r\n      }\r\n    },\r\n    \"node_modules/source-map\": {\r\n      \"version\": \"0.6.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz\",\r\n      \"integrity\": \"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/source-map-js\": {\r\n      \"version\": \"1.2.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz\",\r\n      \"integrity\": \"sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/source-map-support\": {\r\n      \"version\": \"0.5.21\",\r\n      \"resolved\": \"https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz\",\r\n      \"integrity\": \"sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"buffer-from\": \"^1.0.0\",\r\n        \"source-map\": \"^0.6.0\"\r\n      }\r\n    },\r\n    \"node_modules/sprintf-js\": {\r\n      \"version\": \"1.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz\",\r\n      \"integrity\": \"sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==\",\r\n      \"dev\": true,\r\n      \"license\": \"BSD-3-Clause\"\r\n    },\r\n    \"node_modules/standardwebhooks\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@stablelib/base64\": \"^1.0.0\",\r\n        \"fast-sha256\": \"^1.3.0\"\r\n      }\r\n    },\r\n    \"node_modules/supports-color\": {\r\n      \"version\": \"10.2.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz\",\r\n      \"integrity\": \"sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/supports-color?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/tar\": {\r\n      \"version\": \"7.5.22\",\r\n      \"resolved\": \"https://registry.npmjs.org/tar/-/tar-7.5.22.tgz\",\r\n      \"integrity\": \"sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==\",\r\n      \"dev\": true,\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"dependencies\": {\r\n        \"@isaacs/fs-minipass\": \"^4.0.0\",\r\n        \"chownr\": \"^3.0.0\",\r\n        \"minipass\": \"^7.1.2\",\r\n        \"minizlib\": \"^3.1.0\",\r\n        \"yallist\": \"^5.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/three\": {\r\n      \"version\": \"0.169.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/three/-/three-0.169.0.tgz\",\r\n      \"integrity\": \"sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/tinyest\": {\r\n      \"version\": \"0.3.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/tinyest/-/tinyest-0.3.4.tgz\",\r\n      \"integrity\": \"sha512-81EcMHTCQ2IPweOGk9SpMgjbenR9ZJRsAE84btEv0DhvVKwhe7qBy28hWgatoXa9ppWzmtpHiJ+e86SiaAtY6w==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=12.20.0\"\r\n      }\r\n    },\r\n    \"node_modules/tinyglobby\": {\r\n      \"version\": \"0.2.17\",\r\n      \"resolved\": \"https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz\",\r\n      \"integrity\": \"sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"fdir\": \"^6.5.0\",\r\n        \"picomatch\": \"^4.0.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/SuperchupuDev\"\r\n      }\r\n    },\r\n    \"node_modules/tslib\": {\r\n      \"version\": \"2.8.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz\",\r\n      \"integrity\": \"sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==\",\r\n      \"license\": \"0BSD\"\r\n    },\r\n    \"node_modules/tsover-runtime\": {\r\n      \"version\": \"0.0.7\",\r\n      \"resolved\": \"https://registry.npmjs.org/tsover-runtime/-/tsover-runtime-0.0.7.tgz\",\r\n      \"integrity\": \"sha512-FHHwMJzZbnP23+fU1PxXljy7j0RRRosL2r1/CRUNTqfW+l7m35JsUkM615x/cAzw4GqRdXPIl3axKMj9qzpZtQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"Apache-2.0\"\r\n    },\r\n    \"node_modules/typed-binary\": {\r\n      \"version\": \"4.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/typed-binary/-/typed-binary-4.3.3.tgz\",\r\n      \"integrity\": \"sha512-W2hLsSzt3k/tg38gDE4Fn/QiwcoqGuUHBc2cb3mXuH7KcYxe/GM57vzW14s2/bawB4R5knGgGq8Xb57vsaJ4Sg==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/typegpu\": {\r\n      \"version\": \"0.12.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/typegpu/-/typegpu-0.12.3.tgz\",\r\n      \"integrity\": \"sha512-zYwQEhnPPwhq5vQKmollXRK1LCVjStOnL+2B0PiebqvAzYPr25rPNR4r3olLoow5sKKjyj3kzHKEcpD5SyDzGQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"tinyest\": \"~0.3.2\",\r\n        \"tsover-runtime\": \"^0.0.7\",\r\n        \"typed-binary\": \"^4.3.3\"\r\n      },\r\n      \"bin\": {\r\n        \"typegpu\": \"bin.mjs\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12.20.0\"\r\n      }\r\n    },\r\n    \"node_modules/uint8array-extras\": {\r\n      \"version\": \"1.5.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz\",\r\n      \"integrity\": \"sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/undici\": {\r\n      \"version\": \"7.24.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/undici/-/undici-7.24.8.tgz\",\r\n      \"integrity\": \"sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=20.18.1\"\r\n      }\r\n    },\r\n    \"node_modules/undici-types\": {\r\n      \"version\": \"6.21.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz\",\r\n      \"integrity\": \"sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/unenv\": {\r\n      \"version\": \"2.0.0-rc.24\",\r\n      \"resolved\": \"https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz\",\r\n      \"integrity\": \"sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"pathe\": \"^2.0.3\"\r\n      }\r\n    },\r\n    \"node_modules/vite\": {\r\n      \"version\": \"8.0.13\",\r\n      \"resolved\": \"https://registry.npmjs.org/vite/-/vite-8.0.13.tgz\",\r\n      \"integrity\": \"sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"lightningcss\": \"^1.32.0\",\r\n        \"picomatch\": \"^4.0.4\",\r\n        \"postcss\": \"^8.5.14\",\r\n        \"rolldown\": \"1.0.1\",\r\n        \"tinyglobby\": \"^0.2.16\"\r\n      },\r\n      \"bin\": {\r\n        \"vite\": \"bin/vite.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \"^20.19.0 || >=22.12.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/vitejs/vite?sponsor=1\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"fsevents\": \"~2.3.3\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@types/node\": \"^20.19.0 || >=22.12.0\",\r\n        \"@vitejs/devtools\": \"^0.1.18\",\r\n        \"esbuild\": \"^0.27.0 || ^0.28.0\",\r\n        \"jiti\": \">=1.21.0\",\r\n        \"less\": \"^4.0.0\",\r\n        \"sass\": \"^1.70.0\",\r\n        \"sass-embedded\": \"^1.70.0\",\r\n        \"stylus\": \">=0.54.8\",\r\n        \"sugarss\": \"^5.0.0\",\r\n        \"terser\": \"^5.16.0\",\r\n        \"tsx\": \"^4.8.1\",\r\n        \"yaml\": \"^2.4.2\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@types/node\": {\r\n          \"optional\": true\r\n        },\r\n        \"@vitejs/devtools\": {\r\n          \"optional\": true\r\n        },\r\n        \"esbuild\": {\r\n          \"optional\": true\r\n        },\r\n        \"jiti\": {\r\n          \"optional\": true\r\n        },\r\n        \"less\": {\r\n          \"optional\": true\r\n        },\r\n        \"sass\": {\r\n          \"optional\": true\r\n        },\r\n        \"sass-embedded\": {\r\n          \"optional\": true\r\n        },\r\n        \"stylus\": {\r\n          \"optional\": true\r\n        },\r\n        \"sugarss\": {\r\n          \"optional\": true\r\n        },\r\n        \"terser\": {\r\n          \"optional\": true\r\n        },\r\n        \"tsx\": {\r\n          \"optional\": true\r\n        },\r\n        \"yaml\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/workerd\": {\r\n      \"version\": \"1.20260515.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/workerd/-/workerd-1.20260515.1.tgz\",\r\n      \"integrity\": \"sha512-MjKOJLcvU45xXedQowvuiHtJTxu4WTHYQeIlF7YmjuqhiI6dImTFxWCEoRQHiskztxuVSNEmdO7/0UfDu6OMnQ==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"Apache-2.0\",\r\n      \"bin\": {\r\n        \"workerd\": \"bin/workerd\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=16\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@cloudflare/workerd-darwin-64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-darwin-arm64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-linux-64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-linux-arm64\": \"1.20260515.1\",\r\n        \"@cloudflare/workerd-windows-64\": \"1.20260515.1\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler\": {\r\n      \"version\": \"4.92.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/wrangler/-/wrangler-4.92.0.tgz\",\r\n      \"integrity\": \"sha512-/DKpQHPxkuZbQsO9dFW2700VTD/4DSZMHjy92fO/frNoDRi/zQsFCAd2ONCV6TGqcUoXcP3D8Bo2gj/L4M0qQQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT OR Apache-2.0\",\r\n      \"dependencies\": {\r\n        \"@cloudflare/kv-asset-handler\": \"0.5.0\",\r\n        \"@cloudflare/unenv-preset\": \"2.16.1\",\r\n        \"blake3-wasm\": \"2.1.5\",\r\n        \"esbuild\": \"0.27.3\",\r\n        \"miniflare\": \"4.20260515.0\",\r\n        \"path-to-regexp\": \"6.3.0\",\r\n        \"unenv\": \"2.0.0-rc.24\",\r\n        \"workerd\": \"1.20260515.1\"\r\n      },\r\n      \"bin\": {\r\n        \"wrangler\": \"bin/wrangler.js\",\r\n        \"wrangler2\": \"bin/wrangler.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=22.0.0\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"fsevents\": \"~2.3.2\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"@cloudflare/workers-types\": \"^4.20260515.1\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"@cloudflare/workers-types\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/aix-ppc64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"aix\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/android-arm\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/android-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/android-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"android\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/darwin-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/darwin-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"darwin\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/freebsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/freebsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"freebsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-arm\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==\",\r\n      \"cpu\": [\r\n        \"arm\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-ia32\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-loong64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==\",\r\n      \"cpu\": [\r\n        \"loong64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-mips64el\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==\",\r\n      \"cpu\": [\r\n        \"mips64el\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-ppc64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==\",\r\n      \"cpu\": [\r\n        \"ppc64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-riscv64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==\",\r\n      \"cpu\": [\r\n        \"riscv64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-s390x\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==\",\r\n      \"cpu\": [\r\n        \"s390x\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/linux-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"linux\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/netbsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/netbsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"netbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/openbsd-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/openbsd-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openbsd\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/openharmony-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"openharmony\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/sunos-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"sunos\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/win32-arm64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==\",\r\n      \"cpu\": [\r\n        \"arm64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/win32-ia32\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==\",\r\n      \"cpu\": [\r\n        \"ia32\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/@esbuild/win32-x64\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==\",\r\n      \"cpu\": [\r\n        \"x64\"\r\n      ],\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"optional\": true,\r\n      \"os\": [\r\n        \"win32\"\r\n      ],\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/wrangler/node_modules/esbuild\": {\r\n      \"version\": \"0.27.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz\",\r\n      \"integrity\": \"sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==\",\r\n      \"dev\": true,\r\n      \"hasInstallScript\": true,\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"esbuild\": \"bin/esbuild\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      },\r\n      \"optionalDependencies\": {\r\n        \"@esbuild/aix-ppc64\": \"0.27.3\",\r\n        \"@esbuild/android-arm\": \"0.27.3\",\r\n        \"@esbuild/android-arm64\": \"0.27.3\",\r\n        \"@esbuild/android-x64\": \"0.27.3\",\r\n        \"@esbuild/darwin-arm64\": \"0.27.3\",\r\n        \"@esbuild/darwin-x64\": \"0.27.3\",\r\n        \"@esbuild/freebsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/freebsd-x64\": \"0.27.3\",\r\n        \"@esbuild/linux-arm\": \"0.27.3\",\r\n        \"@esbuild/linux-arm64\": \"0.27.3\",\r\n        \"@esbuild/linux-ia32\": \"0.27.3\",\r\n        \"@esbuild/linux-loong64\": \"0.27.3\",\r\n        \"@esbuild/linux-mips64el\": \"0.27.3\",\r\n        \"@esbuild/linux-ppc64\": \"0.27.3\",\r\n        \"@esbuild/linux-riscv64\": \"0.27.3\",\r\n        \"@esbuild/linux-s390x\": \"0.27.3\",\r\n        \"@esbuild/linux-x64\": \"0.27.3\",\r\n        \"@esbuild/netbsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/netbsd-x64\": \"0.27.3\",\r\n        \"@esbuild/openbsd-arm64\": \"0.27.3\",\r\n        \"@esbuild/openbsd-x64\": \"0.27.3\",\r\n        \"@esbuild/openharmony-arm64\": \"0.27.3\",\r\n        \"@esbuild/sunos-x64\": \"0.27.3\",\r\n        \"@esbuild/win32-arm64\": \"0.27.3\",\r\n        \"@esbuild/win32-ia32\": \"0.27.3\",\r\n        \"@esbuild/win32-x64\": \"0.27.3\"\r\n      }\r\n    },\r\n    \"node_modules/ws\": {\r\n      \"version\": \"8.18.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ws/-/ws-8.18.0.tgz\",\r\n      \"integrity\": \"sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=10.0.0\"\r\n      },\r\n      \"peerDependencies\": {\r\n        \"bufferutil\": \"^4.0.1\",\r\n        \"utf-8-validate\": \">=5.0.2\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"bufferutil\": {\r\n          \"optional\": true\r\n        },\r\n        \"utf-8-validate\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/yallist\": {\r\n      \"version\": \"5.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz\",\r\n      \"integrity\": \"sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==\",\r\n      \"dev\": true,\r\n      \"license\": \"BlueOak-1.0.0\",\r\n      \"engines\": {\r\n        \"node\": \">=18\"\r\n      }\r\n    },\r\n    \"node_modules/youch\": {\r\n      \"version\": \"4.1.0-beta.10\",\r\n      \"resolved\": \"https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz\",\r\n      \"integrity\": \"sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/colors\": \"^4.1.5\",\r\n        \"@poppinss/dumper\": \"^0.6.4\",\r\n        \"@speed-highlight/core\": \"^1.2.7\",\r\n        \"cookie\": \"^1.0.2\",\r\n        \"youch-core\": \"^0.3.3\"\r\n      }\r\n    },\r\n    \"node_modules/youch-core\": {\r\n      \"version\": \"0.3.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz\",\r\n      \"integrity\": \"sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==\",\r\n      \"dev\": true,\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@poppinss/exception\": \"^1.2.2\",\r\n        \"error-stack-parser-es\": \"^1.0.5\"\r\n      }\r\n    },\r\n    \"node_modules/zod\": {\r\n      \"version\": \"4.6.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/zod/-/zod-4.6.5.tgz\",\r\n      \"integrity\": \"sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/colinhacks\"\r\n      }\r\n    }\r\n  }\r\n}\r\n","package.json":"{\r\n  \"name\": \"myr5-coach\",\r\n  \"version\": \"1.0.0\",\r\n  \"private\": true,\r\n  \"type\": \"module\",\r\n  \"scripts\": {\r\n    \"dev\": \"node scripts/dev.mjs\",\r\n    \"build\": \"node scripts/build.mjs\",\r\n    \"catalog:provision-keys\": \"node scripts/provision-pack-catalog-keys.mjs\",\r\n    \"db:generate\": \"drizzle-kit generate\",\r\n    \"test\": \"node --test tests/*.test.mjs tests/packs/*.test.mjs\",\r\n    \"test:smoke\": \"node --test tests/release-smoke.browser.test.mjs\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"@huggingface/transformers\": \"^3.8.1\",\r\n    \"@openai/sites-vite-plugin\": \"0.2.0\",\r\n    \"drizzle-kit\": \"0.31.4\",\r\n    \"drizzle-orm\": \"0.44.2\",\r\n    \"esbuild\": \"0.28.0\",\r\n    \"fake-indexeddb\": \"^6.2.5\",\r\n    \"meshoptimizer\": \"^1.3.0\",\r\n    \"miniflare\": \"4.20260515.0\",\r\n    \"playwright\": \"^1.63.0\",\r\n    \"shaders\": \"4.0.4\",\r\n    \"vite\": \"8.0.13\",\r\n    \"wrangler\": \"4.92.0\"\r\n  },\r\n  \"dependencies\": {\r\n    \"@block65/webcrypto-web-push\": \"^2.0.0\",\r\n    \"@clerk/backend\": \"^3.17.2\",\r\n    \"dexie\": \"^4.4.6\",\r\n    \"three\": \"^0.169.0\",\r\n    \"zod\": \"^4.6.5\"\r\n  }\r\n}\r\n","packs/browser-asset-store.mjs":"/**\r\n * P13C durable browser byte store.  Pack metadata remains in the lifecycle's\r\n * small metadata store; never place pack bytes in localStorage.\r\n */\r\nconst bytes = value => value instanceof Uint8Array ? value : new Uint8Array(value);\r\n\r\nexport function cacheStorageAssetStore({ cacheName = 'myr5-pack-assets-v1', cachesApi = globalThis.caches } = {}) {\r\n  if (!cachesApi?.open) throw new Error('CacheStorage is unavailable');\r\n  const request = key => new Request(`https://myr5.invalid/packs/${encodeURIComponent(key)}`);\r\n  const cache = () => cachesApi.open(cacheName);\r\n  return {\r\n    async get(key) { const response = await (await cache()).match(request(key)); return response ? new Uint8Array(await response.arrayBuffer()) : null; },\r\n    async put(key, value) {\r\n      try { await (await cache()).put(request(key), new Response(bytes(value), { headers: { 'content-type': 'application/octet-stream' } })); }\r\n      catch (error) { throw new Error(`durable asset write failed (quota/storage): ${error?.message || error}`); }\r\n    },\r\n    async remove(key) { await (await cache()).delete(request(key)); },\r\n    async removePrefix(prefix) {\r\n      const target = await cache();\r\n      await Promise.all((await target.keys()).filter(request => decodeURIComponent(new URL(request.url).pathname.split('/').pop() || '').startsWith(prefix)).map(request => target.delete(request)));\r\n    }\r\n  };\r\n}\r\n\r\n/** Test-only injection wrapper for deterministic quota/storage failures. */\r\nexport function quotaInjectingStore(store, { failPut = () => false } = {}) {\r\n  return { ...store, async put(key, value) { if (failPut(key, value)) throw new Error('injected quota exceeded'); return store.put(key, value); } };\r\n}\r\n","packs/catalog-config.mjs":"import {PUBLIC_PACK_CATALOG_KEYS} from './catalog-keys.generated.mjs';\r\n\r\nconst usable=entry=>entry&&typeof entry==='object'&&typeof entry.keyId==='string'&&entry.keyId.length>0\r\n && ['release','recovery'].includes(entry.role)&&Array.isArray(entry.protocols)&&entry.protocols.includes(1)\r\n && entry.publicKey?.kty==='OKP'&&entry.publicKey?.crv==='Ed25519'&&typeof entry.publicKey.x==='string'\r\n && /^[A-Za-z0-9_-]{43}$/.test(entry.publicKey.x)&&!/^A+$/.test(entry.publicKey.x);\r\nexport function productionCatalogKeyring(){\r\n const entries=PUBLIC_PACK_CATALOG_KEYS.filter(usable).map(entry=>Object.freeze({keyId:entry.keyId,role:entry.role,protocols:Object.freeze([...entry.protocols]),publicKey:Object.freeze({kty:'OKP',crv:'Ed25519',x:entry.publicKey.x,ext:true})}));\r\n return Object.freeze(entries);\r\n}\r\n","packs/catalog-keys.generated.mjs":"// Generated by scripts/provision-pack-catalog-keys.mjs. Private keys never belong in this repository.\r\nexport const PUBLIC_PACK_CATALOG_KEYS=Object.freeze([\r\n  {\r\n    \"keyId\": \"myr5-pack-release-2026-09-22-ab1e6c3eb95a\",\r\n    \"role\": \"release\",\r\n    \"protocols\": [\r\n      1\r\n    ],\r\n    \"publicKey\": {\r\n      \"kty\": \"OKP\",\r\n      \"crv\": \"Ed25519\",\r\n      \"x\": \"CW1fJQMSp5r6xXmbu-KdlWlPjcfmP464BT2xdpDzIPI\",\r\n      \"ext\": true\r\n    }\r\n  },\r\n  {\r\n    \"keyId\": \"myr5-pack-recovery-2026-09-22-6336b41e259d\",\r\n    \"role\": \"recovery\",\r\n    \"protocols\": [\r\n      1\r\n    ],\r\n    \"publicKey\": {\r\n      \"kty\": \"OKP\",\r\n      \"crv\": \"Ed25519\",\r\n      \"x\": \"pgBezjehRdmmNjrc4MF1X1zkrZeRhiSHVk1bOkL6Cpg\",\r\n      \"ext\": true\r\n    }\r\n  }\r\n].map(entry=>Object.freeze({...entry,protocols:Object.freeze(entry.protocols),publicKey:Object.freeze(entry.publicKey)})));\r\n","packs/catalog-state.mjs":"import {canonicalCatalogPayload,dependencyIdentity,verifyPackCatalog} from './catalog-trust.mjs';\r\n\r\nexport const PACK_CATALOG_STATE_KEY='myr5-pack-catalog-v1';\r\nconst clone=value=>JSON.parse(JSON.stringify(value));\r\nconst memory=()=>{const values=new Map();return {getItem:key=>values.get(key)??null,setItem:(key,value)=>values.set(key,String(value)),removeItem:key=>values.delete(key)};};\r\n\r\nexport async function readLastGoodCatalog({storage=globalThis.localStorage??memory(),keyring}={}){\r\n let record;try{record=JSON.parse(storage.getItem(PACK_CATALOG_STATE_KEY)||'null');}catch{return null;}\r\n if(!record?.catalog||!await verifyPackCatalog(record.catalog,keyring))return null;\r\n return clone(record.catalog);\r\n}\r\nexport async function promotePackCatalog({catalog,keyring,storage=globalThis.localStorage??memory(),now=()=>Date.now()}={}){\r\n if(!await verifyPackCatalog(catalog,keyring))throw Error('Pack catalog signature is invalid.');\r\n const previous=await readLastGoodCatalog({storage,keyring});\r\n if(previous){\r\n  if(catalog.generation<previous.generation)throw Error('Pack catalog generation cannot move backward.');\r\n  if(catalog.generation===previous.generation){if(canonicalCatalogPayload(catalog)!==canonicalCatalogPayload(previous)||catalog.signature!==previous.signature)throw Error('Pack catalog generation conflicts with the last good catalog.');return {catalog:previous,duplicate:true};}\r\n }\r\n storage.setItem(PACK_CATALOG_STATE_KEY,JSON.stringify({catalog:clone(catalog),verifiedAt:now()}));\r\n const restored=await readLastGoodCatalog({storage,keyring});if(!restored||restored.generation!==catalog.generation)throw Error('Pack catalog could not be restored after promotion.');\r\n return {catalog:restored,duplicate:false};\r\n}\r\nexport function dependencyReferenceCounts(catalog,installedPackIds){\r\n const selected=new Set(installedPackIds),counts=new Map(),records=new Map();\r\n for(const pack of catalog?.packs??[])if(selected.has(pack.packId))for(const dependency of pack.dependencies){const id=dependencyIdentity(dependency);counts.set(id,(counts.get(id)||0)+1);records.set(id,clone(dependency));}\r\n return new Map([...counts].sort(([a],[b])=>a.localeCompare(b)).map(([id,count])=>[id,Object.freeze({count,dependency:Object.freeze(records.get(id))})]));\r\n}\r\nexport function planPackRemoval(catalog,installedPackIds,removePackId){\r\n const before=dependencyReferenceCounts(catalog,installedPackIds),afterIds=[...installedPackIds].filter(id=>id!==removePackId),after=dependencyReferenceCounts(catalog,afterIds),remove=[];\r\n for(const [id,record] of before)if(!after.has(id))remove.push(record.dependency);\r\n return Object.freeze({installedPackIds:Object.freeze(afterIds),removeDependencies:Object.freeze(remove)});\r\n}\r\nexport function aggregateCatalogLicenses(catalog,installedPackIds=(catalog?.packs??[]).map(pack=>pack.packId)){\r\n const refs=dependencyReferenceCounts(catalog,installedPackIds),seen=new Set(),records=[];\r\n for(const {dependency} of refs.values()){const key=JSON.stringify([dependency.name,dependency.version,dependency.spdx,dependency.sourceUrl,dependency.attribution]);if(!seen.has(key)){seen.add(key);records.push({name:dependency.name,version:dependency.version,spdx:dependency.spdx,sourceUrl:dependency.sourceUrl,attribution:dependency.attribution});}}\r\n return Object.freeze(records.sort((a,b)=>a.name.localeCompare(b.name)||a.version.localeCompare(b.version)).map(Object.freeze));\r\n}\r\n","packs/catalog-trust.mjs":"const plain=value=>value&&typeof value==='object'&&!Array.isArray(value);\r\nconst text=(value,max=300)=>typeof value==='string'&&value.length>0&&value.length<=max&&value.trim()===value;\r\nconst hex=value=>typeof value==='string'&&/^[a-f0-9]{64}$/.test(value);\r\nconst signature=value=>typeof value==='string'&&/^[A-Za-z0-9+/]+={0,2}$/.test(value)&&value.length>=80;\r\nconst exactVersion=value=>text(value,80)&&!/[\\*\\^~<>=|\\s]/.test(value);\r\nconst safePath=value=>text(value,500)&&value.startsWith('/packs/')&&!value.includes('..')&&!/[?#]/.test(value);\r\nconst executable=value=>/\\.(?:m?js|cjs|wasm|html?)$/i.test(value);\r\nconst stable=value=>Array.isArray(value)?value.map(stable):plain(value)?Object.fromEntries(Object.keys(value).sort().map(key=>[key,stable(value[key])])):value;\r\nconst base64Bytes=value=>Uint8Array.from((typeof atob==='function'?atob(value):Buffer.from(value,'base64').toString('binary')),char=>char.charCodeAt(0));\r\nconst identity=dependency=>JSON.stringify([dependency.name,dependency.version,dependency.sha256]);\r\nconst sameOriginHashed=(path,hash)=>safePath(path)&&path.toLowerCase().includes(hash.toLowerCase());\r\n\r\nexport const PACK_CATALOG_SCHEMA='mom-pack-catalog-v1';\r\nexport const PACK_CATALOG_PROTOCOL=1;\r\nexport const PACK_ASSET_MAX_BYTES=512*1024;\r\nexport const PACK_TOTAL_MAX_BYTES=2*1024*1024;\r\nexport const canonicalCatalogPayload=catalog=>JSON.stringify(stable({schema:catalog.schema,protocol:catalog.protocol,generation:catalog.generation,publishedAt:catalog.publishedAt,keyId:catalog.keyId,alg:catalog.alg,packs:catalog.packs}));\r\n\r\nfunction validDependency(value){return plain(value)&&text(value.name,200)&&exactVersion(value.version)&&hex(value.sha256)&&Number.isSafeInteger(value.bytes)&&value.bytes>0&&value.bytes<=PACK_ASSET_MAX_BYTES&&sameOriginHashed(value.path,value.sha256)&&!executable(value.path)&&text(value.spdx,80)&&/^[-+.A-Za-z0-9]+$/.test(value.spdx)&&text(value.sourceUrl,1000)&&/^https:\\/\\//.test(value.sourceUrl)&&text(value.attribution,2000);}\r\nfunction validPack(value){\r\n if(!plain(value)||!text(value.packId,200)||!exactVersion(value.version)||!text(value.fallbackId,200)||!value.fallbackId.startsWith('builtin:')||!plain(value.manifest)||!hex(value.manifest.sha256)||!Number.isSafeInteger(value.manifest.bytes)||value.manifest.bytes<=0||value.manifest.bytes>1024*1024||!sameOriginHashed(value.manifest.path,value.manifest.sha256)||!value.manifest.path.endsWith('.json')||!Array.isArray(value.dependencies)||value.dependencies.length>128||!value.dependencies.every(validDependency)||!Number.isSafeInteger(value.totalBytes)||value.totalBytes>PACK_TOTAL_MAX_BYTES||value.totalBytes!==value.manifest.bytes+value.dependencies.reduce((sum,item)=>sum+item.bytes,0))return false;\r\n return new Set(value.dependencies.map(identity)).size===value.dependencies.length;\r\n}\r\nexport function isPackCatalog(value){return plain(value)&&value.schema===PACK_CATALOG_SCHEMA&&value.protocol===PACK_CATALOG_PROTOCOL&&Number.isSafeInteger(value.generation)&&value.generation>0&&Number.isSafeInteger(value.publishedAt)&&value.publishedAt>0&&text(value.keyId,200)&&value.alg==='Ed25519'&&signature(value.signature)&&Array.isArray(value.packs)&&value.packs.length>0&&value.packs.length<=200&&value.packs.every(validPack)&&new Set(value.packs.map(pack=>pack.packId)).size===value.packs.length;}\r\nfunction keyFor(keyring,catalog){return Array.isArray(keyring)?keyring.find(entry=>entry?.keyId===catalog.keyId&&['release','recovery'].includes(entry.role)&&entry.protocols?.includes(catalog.protocol)):null;}\r\nexport async function verifyPackCatalog(catalog,keyring){\r\n if(!isPackCatalog(catalog))return false;const entry=keyFor(keyring,catalog),key=entry?.publicKey;if(key?.kty!=='OKP'||key.crv!=='Ed25519'||typeof key.x!=='string')return false;\r\n try{const subtle=globalThis.crypto?.subtle;if(!subtle)return false;const imported=await subtle.importKey('jwk',key,{name:'Ed25519'},false,['verify']);return await subtle.verify({name:'Ed25519'},imported,base64Bytes(catalog.signature),new TextEncoder().encode(canonicalCatalogPayload(catalog)));}catch{return false;}\r\n}\r\nexport function dependencyIdentity(dependency){if(!validDependency(dependency))throw Error('Invalid catalog dependency.');return identity(dependency);}\r\n","packs/fixtures.mjs":"import { sha256 } from './pack-lifecycle.mjs';\r\nexport const FIXTURE_SOURCES = Object.freeze({ crystal: { 'crystal.json': '{\"kind\":\"crystal\",\"glow\":\"amber\"}' }, liquid: { 'liquid.json': '{\"kind\":\"liquid\",\"flow\":0.4}' }, jelly: { 'jelly.json': '{\"kind\":\"jelly\",\"squish\":0.7}' }, mechanical: { 'mechanical.json': '{\"kind\":\"mechanical\",\"gear\":3}' } });\r\nexport async function fixtureManifest(packId, version = '1.0.0') { const path = `${packId}.json`; const source = FIXTURE_SOURCES[packId]; const value = source[path]; const assetPath = Object.keys(source)[0]; const bytes = new TextEncoder().encode(source[assetPath]); return { packId, version, minAppVersion: '1.0.0', bytes: bytes.byteLength, sha256: await sha256(bytes), renderingRequirements: { webgpu: false, maxTextureSize: 64 }, fallbackPresentation: 'procedural-css', assets: [{ path: assetPath, bytes: bytes.byteLength, sha256: await sha256(bytes) }] }; }\r\n","packs/http-fixture-adapter.mjs":"/** P13C test fixture adapter; this is deliberately not a CDN integration. */\r\nconst concat = (left, right) => { const out = new Uint8Array(left.byteLength + right.byteLength); out.set(left); out.set(right, left.byteLength); return out; };\r\n\r\nexport function httpFixtureAssetSource({ baseUrl, fetchImpl = fetch, expectedEtag, expectedVersion }) {\r\n  if (!baseUrl || !expectedEtag || !expectedVersion) throw new Error('fixture source requires baseUrl, expected ETag, and pinned version');\r\n  return {\r\n    async get(asset, previous = null) {\r\n      const offset = previous?.byteLength || 0;\r\n      const headers = { 'x-fixture-version': expectedVersion };\r\n      if (offset) { headers.Range = `bytes=${offset}-`; headers['If-Range'] = expectedEtag; }\r\n      const response = await fetchImpl(new URL(asset.path, baseUrl), { headers });\r\n      if (!response.ok) throw new Error(`fixture HTTP ${response.status}`);\r\n      if (response.headers.get('etag') !== expectedEtag) throw new Error('fixture asset changed (ETag mismatch)');\r\n      if (response.headers.get('x-fixture-version') !== expectedVersion) throw new Error('fixture asset version changed');\r\n      if (offset && response.status !== 206) throw new Error('fixture server refused resumable Range response');\r\n      const received = new Uint8Array(await response.arrayBuffer());\r\n      const full = offset ? concat(previous, received) : received;\r\n      if (full.byteLength > asset.bytes) throw new Error('fixture asset exceeds manifest length');\r\n      return full;\r\n    }\r\n  };\r\n}\r\n","packs/isolated-pack-control.mjs":"/** P13C isolated opt-in pack control; app startup deliberately does not import this module. */\r\nimport { accountPackGrants, packAccountIdentity } from './pack-entitlements.mjs';\r\nimport { PackLifecycle } from './pack-lifecycle.mjs';\r\nimport { cacheStorageAssetStore } from './browser-asset-store.mjs';\r\nimport { existingWorkoutIdleAdapter } from './workout-idle-adapter.mjs';\r\n\r\nexport const fixtureEntitlementAccount = entitledPackIds => Object.freeze({\r\n  // Test-only explicit entitlement fixture.  This does not grant, mint, or fetch entitlement.\r\n  getEntitlements: () => [...entitledPackIds]\r\n});\r\n\r\n// Production adapter accepts only server serialized per-pack ownership records.\r\n// Coach unlocks, local cache, and test fixtures deliberately grant nothing.\r\nexport function authenticatedPackAccount(account) {\r\n const ownerId=packAccountIdentity(account);\r\n return Object.freeze({\r\n  getIdentity:()=>packAccountIdentity(account),\r\n  getGeneration:()=>account?.dataEpoch,\r\n  getEntitlements:()=>accountPackGrants(account,ownerId)\r\n });\r\n}\r\n\r\nexport function createIsolatedPackControl({ account = authenticatedPackAccount(), workout, workoutOwner, storage = globalThis.localStorage, assetStore = cacheStorageAssetStore(), ...options } = {}) {\r\n  // The caller supplies initPod().workoutOwner when a user explicitly opens pack controls.\r\n  return new PackLifecycle({ account, storage, assetStore, workout: workout || existingWorkoutIdleAdapter({ owner: workoutOwner }), ...options });\r\n}\r\n","packs/pack-entitlements.mjs":"// The authenticated account response is the grant authority. Never combine an\r\n// account identity with another object's globals or legacy top-level grants.\r\nexport function packAccountIdentity(account) {\r\n const id=account?.user?.id;\r\n return typeof id==='string'&&id.length>0&&id.length<=200&&id.trim()===id?id:null;\r\n}\r\nexport function accountPackGrants(account, ownerId=packAccountIdentity(account)) {\r\n if(!ownerId||packAccountIdentity(account)!==ownerId)return [];\r\n const grants=account?.entitlements?.ownedPacks;\r\n return Array.isArray(grants)?grants.filter(grant=>grant&&typeof grant.packId==='string'&&grant.packId.length>0&&grant.packId.trim()===grant.packId&&grant.status==='owned'&&Number.isSafeInteger(grant.grantedAt)):[];\r\n}\r\nexport function hasPackGrant(account,packId) { return accountPackGrants(account).some(grant=>grant.packId===packId); }\r\n","packs/pack-grant-cache.mjs":"import {accountPackGrants,packAccountIdentity} from './pack-entitlements.mjs';\r\n\r\nexport const PACK_GRANT_CACHE_KEY='myr5-pack-grants-v1';\r\nconst plain=value=>value&&typeof value==='object'&&!Array.isArray(value);\r\nconst owner=value=>typeof value==='string'&&value.length>0&&value.length<=200&&value.trim()===value?value:null;\r\nconst epoch=value=>Number.isSafeInteger(value)&&value>0?value:null;\r\nconst grant=value=>plain(value)&&typeof value.packId==='string'&&value.packId.length>0&&value.packId.length<=200&&value.packId.trim()===value.packId&&value.status==='owned'&&Number.isSafeInteger(value.grantedAt)&&value.grantedAt>=0;\r\nconst clone=value=>JSON.parse(JSON.stringify(value));\r\n\r\nexport function memoryGrantStorage(){const values=new Map();return {getItem:key=>values.get(key)??null,setItem:(key,value)=>values.set(key,String(value)),removeItem:key=>values.delete(key)};}\r\nfunction read(storage){\r\n try{\r\n  const value=JSON.parse(storage.getItem(PACK_GRANT_CACHE_KEY)||'{}');\r\n  if(!plain(value)||value.version!==1||!(value.activeOwner===null||owner(value.activeOwner))||!plain(value.owners))return {version:1,activeOwner:null,owners:{}};\r\n  const owners={};\r\n  for(const [id,record] of Object.entries(value.owners))if(owner(id)===id&&plain(record)&&epoch(record.dataEpoch)&&Number.isSafeInteger(record.confirmedAt)&&record.confirmedAt>=0&&Array.isArray(record.grants)&&record.grants.every(grant))owners[id]={dataEpoch:record.dataEpoch,confirmedAt:record.confirmedAt,grants:clone(record.grants)};\r\n  return {version:1,activeOwner:owners[value.activeOwner]?value.activeOwner:null,owners};\r\n }catch{return {version:1,activeOwner:null,owners:{}};}\r\n}\r\n\r\nexport function createPackGrantCache({storage=globalThis.localStorage??memoryGrantStorage(),now=()=>Date.now()}={}){\r\n let state=read(storage);\r\n const persist=()=>storage.setItem(PACK_GRANT_CACHE_KEY,JSON.stringify(state));\r\n return Object.freeze({\r\n  confirm(account){\r\n   const id=packAccountIdentity(account),dataEpoch=epoch(account?.dataEpoch);if(!id||!dataEpoch)throw Error('A canonical account scope is required to confirm pack grants.');\r\n   const grants=accountPackGrants(account,id).map(item=>({packId:item.packId,status:'owned',grantedAt:item.grantedAt}));\r\n   state={version:1,activeOwner:id,owners:{...state.owners,[id]:{dataEpoch,confirmedAt:now(),grants}}};persist();return account;\r\n  },\r\n  active(){const id=state.activeOwner,record=id&&state.owners[id];return record?Object.freeze({user:Object.freeze({id}),dataEpoch:record.dataEpoch,entitlements:Object.freeze({ownedPacks:Object.freeze(clone(record.grants))}),offlineGrantSnapshot:true}):null;},\r\n  forOwner(id){id=owner(id);const record=id&&state.owners[id];return record?Object.freeze({user:Object.freeze({id}),dataEpoch:record.dataEpoch,entitlements:Object.freeze({ownedPacks:Object.freeze(clone(record.grants))}),offlineGrantSnapshot:true}):null;},\r\n  deactivate(){if(state.activeOwner===null)return;state={...state,activeOwner:null};persist();},\r\n  remove(id){id=owner(id);if(!id)return;const owners={...state.owners};delete owners[id];state={version:1,activeOwner:state.activeOwner===id?null:state.activeOwner,owners};persist();},\r\n  inspect(){return clone(state);}\r\n });\r\n}\r\n","packs/pack-host-adapter.mjs":"/**\r\n * Cross-origin asset source for the public pack host (packs.mominc.online,\r\n * see pack-host-config.mjs). Unlike http-fixture-adapter.mjs (a local test\r\n * fixture that pins ETag/x-fixture-version headers no real static host\r\n * sends), this is a plain CORS GET: every asset is already pinned to an\r\n * exact sha256 + byte length by the pack's signed manifest, so\r\n * pack-lifecycle.mjs's own digest check (`validate()`) is what rejects a\r\n * tampered or mismatched file — this adapter only needs to fetch bytes.\r\n */\r\nimport { PACK_HOST_BASE_URL } from './pack-host-config.mjs';\r\n\r\nexport function packHostAssetSource({ baseUrl = PACK_HOST_BASE_URL, fetchImpl = fetch } = {}) {\r\n  if (!baseUrl) throw new Error('pack host adapter requires a baseUrl');\r\n  return {\r\n    // ponytail: no Range/resume — a retry re-fetches the whole asset. Safe to\r\n    // add later (Range + If-Range) without touching pack-lifecycle.mjs, since\r\n    // the sha256 pin already makes a bad resume detectable, just not cheap.\r\n    async get(asset) {\r\n      const response = await fetchImpl(new URL(asset.path, baseUrl), { mode: 'cors', cache: 'no-store' });\r\n      if (!response.ok) throw new Error(`pack host HTTP ${response.status} for ${asset.path}`);\r\n      const bytes = new Uint8Array(await response.arrayBuffer());\r\n      if (bytes.byteLength > asset.bytes) throw new Error(`pack host asset exceeds manifest length: ${asset.path}`);\r\n      return bytes;\r\n    }\r\n  };\r\n}\r\n","packs/pack-host-config.mjs":"// Rank 2b (D1): the one place that names where optional-art packs are served\r\n// from. Every pack consumer imports this instead of hardcoding a host, so\r\n// moving to a different static host later is a one-line change.\r\nexport const PACK_HOST_BASE_URL = 'https://packs.mominc.online';\r\n","packs/pack-license-surface.mjs":"import {productionCatalogKeyring} from './catalog-config.mjs';\r\nimport {aggregateCatalogLicenses,readLastGoodCatalog} from './catalog-state.mjs';\r\n\r\nexport async function mountPackLicenses({host,storage=globalThis.localStorage,keyring=productionCatalogKeyring()}={}){\r\n if(!host)return null;host.replaceChildren();\r\n const catalog=await readLastGoodCatalog({storage,keyring});\r\n const records=catalog?aggregateCatalogLicenses(catalog):[];\r\n if(!records.length){host.textContent='No optional pack licenses are installed.';return {catalog:null,records};}\r\n const list=host.ownerDocument.createElement('ul');\r\n for(const record of records){const item=host.ownerDocument.createElement('li'),link=host.ownerDocument.createElement('a'),copy=host.ownerDocument.createElement('span');link.href=record.sourceUrl;link.rel='noreferrer';link.textContent=record.name+' '+record.version+' ('+record.spdx+')';copy.textContent=' — '+record.attribution;item.append(link,copy);list.append(item);}\r\n host.append(list);return {catalog,records};\r\n}\r\n","packs/pack-lifecycle.mjs":"/** P13C provenance: copied from prototypes/packs/pack-lifecycle.mjs (reviewed P13B fixture lifecycle), then adapted only through its explicit asset-source adapter. */\r\nconst CACHE_KEY = 'p13b.device-cache';\r\nconst hasSubtle = globalThis.crypto?.subtle;\r\nconst nodeCrypto = 'node:crypto';\r\nasync function digest(bytes) { if (hasSubtle) { const h = await globalThis.crypto.subtle.digest('SHA-256', bytes); return [...new Uint8Array(h)].map(x => x.toString(16).padStart(2, '0')).join(''); } const { createHash } = await import(nodeCrypto); return createHash('sha256').update(Buffer.from(bytes)).digest('hex'); }\r\nconst bytesOf = value => value instanceof Uint8Array ? value : Array.isArray(value) ? new Uint8Array(value) : new TextEncoder().encode(value);\r\nconst clone = value => new Uint8Array(value);\r\nconst joinBytes = chunks => { const joined = new Uint8Array(chunks.reduce((n, data) => n + data.byteLength, 0)); let offset = 0; for (const data of chunks) { joined.set(data, offset); offset += data.byteLength; } return joined; };\r\nconst snapshot = value => JSON.parse(JSON.stringify(value));\r\nconst retire = runtime => { try { runtime?.dispose(); } catch { /* Retired runtime cleanup is best effort. */ } };\r\nconst stale = () => Object.assign(new Error('Pack account or operation changed'), { code: 'pack_operation_changed' });\r\nconst parts = value => String(value).split('.').map(x => /^\\d+$/.test(x) ? Number(x) : -1);\r\nexport const compareVersions = (left, right) => { const a = parts(left), b = parts(right); for (let i = 0; i < Math.max(a.length, b.length); i++) { const d = (a[i] ?? 0) - (b[i] ?? 0); if (d) return Math.sign(d); } return 0; };\r\nexport const PACK_STATES = Object.freeze(['absent', 'downloading', 'downloaded', 'verified', 'pending-equip', 'active', 'failed']);\r\nexport const memoryStore = () => { const map = new Map(); return { getItem: k => map.get(k) ?? null, setItem: (k, v) => map.set(k, String(v)), removeItem: k => map.delete(k) }; };\r\n\r\n/** Demo/test adapter only (64 KiB by default), never a production download cache. */\r\nexport function fixtureAssetStore({ maxBytes = 64 * 1024 } = {}) { const map = new Map(); let used = 0; return { maxBytes, async get(k) { const v = map.get(k); return v ? clone(v) : null; }, async put(k, v) { const data = bytesOf(v), old = map.get(k)?.byteLength ?? 0; if (used - old + data.byteLength > maxBytes) throw new Error('fixture asset-store quota exceeded'); map.set(k, clone(data)); used += data.byteLength - old; }, async remove(k) { const old = map.get(k); if (old) { used -= old.byteLength; map.delete(k); } }, async removePrefix(prefix) { for (const key of [...map.keys()]) if (key.startsWith(prefix)) await this.remove(key); } }; }\r\n\r\nexport class PackLifecycle {\r\n  constructor({ account, storage = globalThis.localStorage ?? memoryStore(), assetStore = fixtureAssetStore(), workout, appVersion = '1.0.0', runtimeFactory = () => ({ dispose() {} }), now = () => new Date().toISOString(), onChange = () => {}, verifyManifest = null, fail = {} } = {}) {\r\n    if (!account?.getEntitlements) throw new Error('account adapter with getEntitlements() is required'); if (!assetStore?.get || !assetStore?.put || !assetStore?.removePrefix) throw new Error('assetStore adapter is required'); if (!workout?.saveAndConfirmIdle) throw new Error('workout adapter with saveAndConfirmIdle() is required');\r\n    Object.assign(this, { account, storage, assetStore, workout, appVersion, runtimeFactory, now, onChange, verifyManifest, fail }); this.epoch = 0; this.disposed = false; this.identity = this.accountIdentity(); this.accountGeneration = this.account.getGeneration?.(); this.unsubscribe = this.account.subscribe?.(() => this.clear()); this.manifests = new Map(); this.runtime = null; const saved = this.readCache(); this.records = saved.packs ?? {}; this.candidates = saved.candidates ?? {}; this.equipped = saved.equipped ?? null; this.pending = saved.pending ?? null;\r\n  }\r\n  accountIdentity() { return this.account.getIdentity ? this.account.getIdentity() : (this.account.user?.id ?? this.account.id ?? this.account); }\r\n  clear() { this.epoch++; const old = this.runtime; this.runtime = null; retire(old); }\r\n  dispose() { this.disposed = true; this.clear(); this.unsubscribe?.(); }\r\n  capture(id) {\r\n    const identity = this.accountIdentity(), generation = this.account.getGeneration?.();\r\n    if (identity !== this.identity || generation !== this.accountGeneration) { this.clear(); this.identity = identity; this.accountGeneration = generation; }\r\n    if (this.disposed || !this.entitled(id)) { this.clear(); throw stale(); }\r\n    return { epoch: this.epoch, identity, generation };\r\n  }\r\n  current(ticket, id) {\r\n    const identity = this.accountIdentity(), generation = this.account.getGeneration?.();\r\n    if (identity !== this.identity || generation !== this.accountGeneration || !this.entitled(id)) { this.clear(); this.identity = identity; this.accountGeneration = generation; }\r\n    return !this.disposed && ticket.epoch === this.epoch && ticket.identity === identity && ticket.generation === generation && this.entitled(id);\r\n  }\r\n  assertCurrent(ticket, id) { if (!this.current(ticket, id)) throw stale(); }\r\n  async checked(promise, ticket, id) { const value = await promise; this.assertCurrent(ticket, id); return value; }\r\n  readCache() { try { return JSON.parse(this.storage.getItem(CACHE_KEY) || '{}'); } catch { return {}; } }\r\n  persist() { this.storage.setItem(CACHE_KEY, JSON.stringify({ packs: this.records, candidates: this.candidates, equipped: this.equipped, pending: this.pending })); }\r\n  emit(id) { this.persist(); this.onChange(this.status(id)); }\r\n  pin(m) { if (!m.version || !m.sha256) throw new Error('manifest requires pinned version and sha256'); return { version: m.version, manifestHash: m.sha256 }; }\r\n  key(id, pin, path) { return `${id}@${pin.version}@${pin.manifestHash}/${path}`; }\r\n  prefix(id, pin) { return `${id}@${pin.version}@${pin.manifestHash}/`; }\r\n  manifest(manifest, source = {}) { manifest = snapshot(manifest); const pin = this.pin(manifest); this.epoch++; this.manifests.set(manifest.packId, { manifest, source, pin }); this.records[manifest.packId] ??= { state: 'absent', receivedBytes: 0 }; return this.status(manifest.packId); }\r\n  entitled(id) { return !!this.account.getEntitlements().some(x => (typeof x === 'string' ? x : x.packId) === id); }\r\n  compatible(m) { return compareVersions(this.appVersion, m.minAppVersion) >= 0; }\r\n  isPinned(r, entry) { return r?.version === entry.pin.version && r?.manifestHash === entry.pin.manifestHash; }\r\n  candidateFor(id, e = this.manifests.get(id)) { const active = this.records[id]; return e && active?.state === 'active' && !this.isPinned(active, e) ? (this.candidates[id] ??= { state: 'absent', receivedBytes: 0, ...e.pin }) : active; }\r\n  status(id) { const e = this.manifests.get(id), active = this.records[id] ?? { state: 'absent', receivedBytes: 0 }, candidate = this.candidates[id]; const r = active.state === 'active' ? active : (candidate ?? active); return { ...(e ? { packId: id, version: r.version ?? e.manifest.version } : { packId: id }), ...r, entitlement: this.entitled(id), equipped: this.equipped?.packId === id, pending: this.pending?.packId === id, ...(active.state === 'active' && candidate ? { candidate: { ...candidate } } : {}) }; }\r\n  all() { return [...new Set([...this.manifests.keys(), ...Object.keys(this.records)])].map(id => this.status(id)); }\r\n  reset(id, entry) { const target = this.candidateFor(id, entry); if (target === this.records[id]) this.records[id] = { state: 'absent', receivedBytes: 0, ...entry.pin }; else this.candidates[id] = { state: 'absent', receivedBytes: 0, ...entry.pin }; }\r\n  async received(id, e, ticket) { let n = 0; for (const a of e.manifest.assets) n += (await this.checked(this.assetStore.get(this.key(id, e.pin, a.path)), ticket, id))?.byteLength ?? 0; return n; }\r\n  async download(id, { resume = true } = {}) {\r\n    const e = this.manifests.get(id); if (!e) throw new Error('unknown pack'); if (!this.entitled(id)) throw new Error('pack is not entitled');\r\n    const ticket = this.capture(id);\r\n    if (this.verifyManifest && !await this.checked(this.verifyManifest(e.manifest), ticket, id)) return this.failState(id, 'manifest signature is invalid');\r\n    if (!this.compatible(e.manifest)) return this.failState(id, 'incompatible app version'); let r = this.candidateFor(id, e);\r\n    if (!r || !this.isPinned(r, e) || !resume) { if (r && this.isPinned(r, e)) await this.checked(this.assetStore.removePrefix(this.prefix(id, e.pin)), ticket, id); this.reset(id, e); r = this.candidateFor(id, e); }\r\n    r.state = 'downloading'; r.manifest = snapshot(e.manifest); delete r.error; this.emit(id);\r\n    for (const a of e.manifest.assets) {\r\n      const key = this.key(id, e.pin, a.path); let old = resume ? await this.checked(this.assetStore.get(key), ticket, id) : null;\r\n      // A complete cached response is reusable only after its hash passes.\r\n      if (old?.byteLength === a.bytes && await this.checked(digest(old), ticket, id) === a.sha256) continue;\r\n      if (old?.byteLength >= a.bytes) old = null;\r\n      if (this.fail.download) return this.failState(id, 'download interrupted; resume available');\r\n      try {\r\n        const raw = typeof e.source?.get === 'function' ? await this.checked(e.source.get(a, old), ticket, id) : e.source[a.path];\r\n        this.assertCurrent(ticket, id);\r\n        if (raw === undefined) return this.failState(id, `missing fixture ${a.path}`);\r\n        await this.checked(this.assetStore.put(key, bytesOf(raw)), ticket, id);\r\n      } catch (error) {\r\n        this.assertCurrent(ticket, id);\r\n        return this.failState(id, `asset storage/download failed: ${error?.message || 'unknown failure'}`);\r\n      }\r\n      r.receivedBytes = await this.received(id, e, ticket); this.assertCurrent(ticket, id); this.emit(id);\r\n    }\r\n    r.receivedBytes = await this.received(id, e, ticket); this.assertCurrent(ticket, id); r.state = 'downloaded'; this.emit(id); return this.status(id);\r\n  }\r\n  async validate(id, { markFailed = true, record, entry, ticket = this.capture(id) } = {}) {\r\n    this.assertCurrent(ticket, id);\r\n    const e = entry !== undefined ? entry : this.manifests.get(id), r = record ?? this.candidateFor(id, e); let reason;\r\n    if (!e || !r) reason = 'missing manifest or cache record';\r\n    else if (e.manifest.packId !== id || !this.isPinned(r, { pin: this.pin(e.manifest) }) || !this.isPinned(r, e)) reason = 'cached pack does not match pinned manifest';\r\n    else if (this.verifyManifest && !await this.checked(this.verifyManifest(e.manifest), ticket, id)) reason = 'manifest signature is invalid';\r\n    else if (!this.compatible(e.manifest)) reason = 'incompatible app version';\r\n    const assembled = [];\r\n    if (!reason) for (const a of e.manifest.assets) {\r\n      const data = await this.checked(this.assetStore.get(this.key(id, e.pin, a.path)), ticket, id);\r\n      if (!data || data.byteLength !== a.bytes || await this.checked(digest(data), ticket, id) !== a.sha256) { reason = `integrity check failed: ${a.path}`; break; }\r\n      assembled.push(data);\r\n    }\r\n    if (!reason && await this.checked(digest(joinBytes(assembled)), ticket, id) !== e.manifest.sha256) reason = 'manifest integrity check failed';\r\n    this.assertCurrent(ticket, id); if (reason && markFailed) this.failState(id, reason); return { ok: !reason, reason };\r\n  }\r\n  async verify(id) {\r\n    const ticket = this.capture(id), r = this.candidateFor(id); if (!r || r.state !== 'downloaded') throw new Error('pack is not downloaded');\r\n    if (this.fail.verify) return this.failState(id, 'integrity check failed');\r\n    if (!(await this.validate(id, { ticket })).ok) return this.status(id); this.assertCurrent(ticket, id);\r\n    r.state = 'verified'; r.manifest = snapshot(this.manifests.get(id).manifest); r.verifiedAt = this.now(); delete r.error; this.emit(id); return this.status(id);\r\n  }\r\n  async equipPending(id) {\r\n    const ticket = this.capture(id), r = this.candidateFor(id); if (!r || r.state !== 'verified') throw new Error('only verified packs can be equipped');\r\n    if (!(await this.validate(id, { ticket })).ok) return this.status(id); this.assertCurrent(ticket, id);\r\n    if (this.pending && this.pending.packId !== id && this.records[this.pending.packId]?.state === 'pending-equip') this.records[this.pending.packId].state = 'verified';\r\n    this.pending = { packId: id, ...this.manifests.get(id).pin }; r.state = 'pending-equip'; this.emit(id); return this.status(id);\r\n  }\r\n  savedEntry(r) { return r?.manifest ? { manifest: snapshot(r.manifest), pin: this.pin(r.manifest) } : null; }\r\n  async activate(id, r, e, { restoring = false } = {}) {\r\n    const ticket = this.capture(id); let safe, candidate, published = false;\r\n    try {\r\n      safe = await this.workout.saveAndConfirmIdle(); this.assertCurrent(ticket, id);\r\n      if (!(safe === true || (safe?.saved === true && safe?.idle === true))) throw Object.assign(new Error('workout save/idle acknowledgment is required before activation'), { code: 'workout_not_idle' });\r\n      const checked = await this.validate(id, { markFailed: false, record: r, entry: e, ticket }); this.assertCurrent(ticket, id);\r\n      if (!checked.ok) {\r\n        if (r) { r.state = 'failed'; r.error = checked.reason; }\r\n        if (restoring) this.equipped = null; else this.pending = null;\r\n        this.persist(); return this.all();\r\n      }\r\n      if (this.fail.activation) throw new Error('activation failed');\r\n      candidate = await this.runtimeFactory(id, r, e.manifest); this.assertCurrent(ticket, id);\r\n      if (!candidate?.dispose) throw new Error('runtime factory returned no disposable runtime');\r\n      // Factories may prepare detached UI; only publish it after the final guard.\r\n      candidate.activate?.(); this.assertCurrent(ticket, id);\r\n      const old = this.runtime, previous = this.equipped?.packId;\r\n      this.runtime = candidate; this.equipped = { packId: id, ...e.pin }; r.state = 'active'; r.manifest = snapshot(e.manifest); delete r.error;\r\n      if (!restoring) { this.pending = null; this.records[id] = r; delete this.candidates[id]; }\r\n      if (previous && previous !== id && this.records[previous]) this.records[previous].state = 'verified';\r\n      this.persist(); published = true; retire(old); this.onChange(this.status(id)); return this.all();\r\n    } catch (error) {\r\n      if (!this.current(ticket, id)) throw stale();\r\n      if (error.code === 'workout_not_idle') throw error;\r\n      if (r) { r.state = restoring ? 'failed' : 'pending-equip'; r.error = `${restoring ? 'restore' : 'activation'} failed: ${error.message || 'runtime error'}`; }\r\n      if (restoring) this.equipped = null;\r\n      this.persist(); return this.all();\r\n    } finally { if (!published) retire(candidate); safe?.release?.(); }\r\n  }\r\n  async restart() {\r\n    if (!this.pending) return this.all(); const id = this.pending.packId;\r\n    // Pending metadata is persisted alongside its exact signature, so a network\r\n    // replacement cannot silently substitute another version at boot.\r\n    const r = [this.candidates[id], this.records[id]].find(value => this.isPinned(value, { pin: this.pending }));\r\n    const e = this.savedEntry(r) ?? this.manifests.get(id);\r\n    return this.activate(id, r, e);\r\n  }\r\n  async restore() {\r\n    if (!this.equipped) return this.all(); const id = this.equipped.packId, r = this.records[id];\r\n    // Authenticate this exact persisted manifest, never a newly fetched one.\r\n    return this.activate(id, r, this.savedEntry(r), { restoring: true });\r\n  }\r\n  async evict(id) { const ticket = this.capture(id), r = this.records[id], e = this.manifests.get(id); if (!r) return; if (this.equipped?.packId === id) throw new Error('cannot evict equipped pack'); if (e && this.isPinned(r, e)) await this.checked(this.assetStore.removePrefix(this.prefix(id, e.pin)), ticket, id); this.assertCurrent(ticket, id); this.records[id] = { state: 'absent', receivedBytes: 0 }; if (this.pending?.packId === id) this.pending = null; this.emit(id); }\r\n  failState(id, reason) { const e = this.manifests.get(id), r = this.candidateFor(id, e) ?? (this.records[id] ??= { receivedBytes: 0 }); r.state = 'failed'; r.error = reason; if (this.pending?.packId === id) this.pending = null; this.emit(id); return this.status(id); }\r\n}\r\nexport async function sha256(value) { return digest(bytesOf(value)); }\r\n\r\n","packs/workout-idle-adapter.mjs":"/**\r\n * P13C bridge point for the existing workout UI.  It never invents an idle\r\n * acknowledgement: until the workout owner exposes save + confirmed-idle,\r\n * pack activation stays pending and cannot interrupt camera tracking.\r\n */\r\nexport function existingWorkoutIdleAdapter({ owner, getWorkoutState, saveWorkout } = {}) {\r\n  return {\r\n    async saveAndConfirmIdle() {\r\n      if (owner?.acquireIdleLease && owner?.snapshot && owner?.save && owner?.releaseIdleLease) {\r\n        const lease = owner.acquireIdleLease();\r\n        if (!lease) return { saved: false, idle: false, reason: 'active workout must not be interrupted' };\r\n        try {\r\n          const saved = await owner.save();\r\n          const after = owner.snapshot();\r\n          if (saved !== true || after.phase !== 'idle' || after.revision !== lease.revision) {\r\n            owner.releaseIdleLease(lease);\r\n            return { saved: false, idle: false, reason: 'workout changed while saving' };\r\n          }\r\n          // PackLifecycle holds this through runtime activation, closing the idle-active-idle ABA gap.\r\n          return { saved: true, idle: true, local: true, accountSynced: false, lease, release: () => owner.releaseIdleLease(lease) };\r\n        } catch (error) {\r\n          owner.releaseIdleLease(lease);\r\n          return { saved: false, idle: false, reason: error?.message || 'workout save failed' };\r\n        }\r\n      }\r\n      if (typeof getWorkoutState !== 'function' || typeof saveWorkout !== 'function') return { saved: false, idle: false, reason: 'workout save/idle integration is not installed' };\r\n      const before = getWorkoutState();\r\n      if (!before || before.phase !== 'idle') return { saved: false, idle: false, reason: 'active workout must not be interrupted' };\r\n      // A state snapshot cannot prevent an idle-active-idle transition while\r\n      // activation awaits. Legacy callbacks must install the owner lease API.\r\n      return { saved: false, idle: false, reason: 'workout idle lease integration is not installed' };\r\n    }\r\n  };\r\n}\r\n","pending-coach.mjs":"import {validRecipe} from './onboarding-domain.mjs';\r\nexport const INCOMING_KEY='myr5-incoming-coach-v1';\r\nexport function validIncomingCoach(value){return !!(value&&value.version===1&&value.armieCompleted===true&&value.customizationConfirmed===true&&validRecipe(value.appearance?.['myr5-recipe-v1']));}\r\nexport function saveIncomingCoach(value,session=sessionStorage,local=localStorage){\r\n const raw=typeof value==='string'?value:JSON.stringify(value);\r\n if(new TextEncoder().encode(raw).length>55000)throw Error('This saved coach is too large to transfer. Return to the website and try again.');\r\n const data=JSON.parse(raw);\r\n if(!validIncomingCoach(data))throw Error('Your saved coach could not be read. Return to the creature studio and tap Use this coach again.');\r\n // Durable storage keeps the handoff available when the next app tab is opened.\r\n local.setItem(INCOMING_KEY,raw);\r\n try{session.setItem(INCOMING_KEY,raw);}catch{}\r\n return data;\r\n}\r\nexport function readIncomingCoach(session=sessionStorage,local=localStorage){\r\n for(const storage of [session,local])try{const value=JSON.parse(storage.getItem(INCOMING_KEY)||'null');if(validIncomingCoach(value))return value;}catch{}\r\n return null;\r\n}\r\nexport function clearIncomingCoach(session=sessionStorage,local=localStorage){for(const storage of [session,local])try{storage.removeItem(INCOMING_KEY);}catch{}}\r\n","performance-account-sync.mjs":"import {snapshotFromCompletedWorkout,prepareWorkoutImport} from './workout-import-codec.mjs';\r\nimport {performanceOwner} from './performance-progress.mjs';\r\nconst CACHE='myr5-performance-account-receipts-v2';\r\nconst identity=a=>typeof a?.user?.id==='string'&&a.user.id&&Number.isSafeInteger(a.dataEpoch)&&a.dataEpoch>0&&!a.stale?{id:a.user.id,epoch:a.dataEpoch}:null;\r\nconst same=(a,b)=>a&&b&&a.id===b.id&&a.epoch===b.epoch;\r\n\r\n/** Automatic upload applies only to workouts started for a captured account.\r\n * Guest history keeps its separate explicit import/keep-local consent flow. */\r\nexport function createPerformanceAccountSync({getAccount,getRows,api,transitions,onAccount,storage=globalThis.localStorage,maxUploads=20}={}){\r\n if(typeof getAccount!=='function'||typeof getRows!=='function'||typeof api!=='function'||!Number.isSafeInteger(maxUploads)||maxUploads<1||maxUploads>500)throw TypeError('Account workout sync requires scoped readers and API access.');\r\n let running=null,rerun=false;const memoryReceipts=new Map(),needsRefresh=new Set();\r\n async function once(attempted){\r\n  const account=getAccount(),actor=identity(account),result={synced:0,skipped:0,failed:[],aborted:false};if(!actor)return result;\r\n  const owner=performanceOwner(null,account),cacheKey=`${CACHE}/${owner}`;let receipts={};try{const value=JSON.parse(storage?.getItem(cacheKey)||'{}');if(value&&typeof value==='object'&&!Array.isArray(value))receipts=value;}catch{}receipts={...receipts,...(memoryReceipts.get(owner)??{})};memoryReceipts.set(owner,receipts);\r\n  const ticket=transitions?.capture?.(),current=()=>{if(!same(actor,identity(getAccount())))throw Object.assign(Error('Account changed during workout sync.'),{code:'account_scope_changed'});transitions?.assertCurrent?.(ticket);};\r\n  let rows;try{current();rows=await getRows();current();}catch(error){result.aborted=true;result.failed.push({id:null,error:error.message});return result;}\r\n  const eligible=(rows??[]).filter(row=>row?.status==='completed'&&row.progress?.performance?.performanceOwner===owner&&!row.progress.performance.preparation&&row.progress.performance.earned!==false&&!attempted.has(JSON.stringify([owner,row.id]))).sort((a,b)=>(a.completedAt??0)-(b.completedAt??0)||String(a.id).localeCompare(String(b.id)));\r\n  let uploads=0;\r\n  for(const row of eligible){\r\n   if(uploads>=maxUploads){rerun=true;break;}\r\n   attempted.add(JSON.stringify([owner,row.id]));\r\n   try{\r\n    current();const snapshot=snapshotFromCompletedWorkout(row);if(!snapshot.performance)throw Error('This workout lacks the performance evidence needed for account rewards.');\r\n    const prepared=await prepareWorkoutImport(snapshot,{targetAccountId:actor.id,targetDataEpoch:actor.epoch});current();\r\n    if(receipts[snapshot.clientWorkoutId]===prepared.fingerprint){result.skipped++;continue;}\r\n    uploads++;\r\n    const value=await api('/api/workouts/import','POST',{digestVersion:prepared.digestVersion,targetDataEpoch:actor.epoch,snapshot:prepared.snapshot,fingerprint:prepared.fingerprint},{'X-Target-Account':actor.id,'X-Expected-Data-Epoch':String(actor.epoch),'Idempotency-Key':prepared.idempotencyKey});current();\r\n    const receipt=value?.receipt,w=value?.workout;\r\n    if(receipt?.targetAccountId!==actor.id||receipt.targetDataEpoch!==actor.epoch||receipt.digestVersion!==prepared.digestVersion||receipt.fingerprint!==prepared.fingerprint||receipt.idempotencyKey!==prepared.idempotencyKey||w?.user_id!==actor.id||w.client_workout_id!==snapshot.clientWorkoutId||w.source!=='guest_import'||w.competitive_status!=='not_eligible')throw Error('Workout sync returned an unmatched receipt.');\r\n    receipts[snapshot.clientWorkoutId]=prepared.fingerprint;needsRefresh.add(owner);try{storage?.setItem(cacheKey,JSON.stringify(receipts));}catch{/* In-memory receipts and server idempotence keep retry behavior bounded. */}result.synced++;\r\n   }catch(error){if(!same(actor,identity(getAccount()))||['auth_transition','account_scope_changed'].includes(error.code)){result.aborted=true;break;}result.failed.push({id:row.id,error:error.message});}\r\n  }\r\n  if(needsRefresh.has(owner)){try{current();const value=await api('/api/account?core=1');current();if(!same(actor,identity(value)))throw Error('Account progress returned an unmatched identity.');await onAccount?.(value);current();needsRefresh.delete(owner);}catch(error){result.failed.push({id:null,error:error.message});if(!same(actor,identity(getAccount())))result.aborted=true;}}\r\n  return result;\r\n }\r\n async function drain(){const total={synced:0,skipped:0,failed:[],aborted:false},attempted=new Set();do{rerun=false;const result=await once(attempted);total.synced+=result.synced;total.skipped+=result.skipped;total.failed.push(...result.failed);total.aborted||=result.aborted;}while(rerun&&identity(getAccount()));return total;}\r\n return {flush(){if(running){rerun=true;return running;}running=drain().finally(()=>{running=null;});return running;},get running(){return !!running;}};\r\n}\r\n","performance-catalog.mjs":"import {EXERCISES,GROUP_EXERCISES} from './exercise-library.mjs';\r\nimport {coachName,EXCLUDED_COACH_IDS} from './coach-names.mjs';\r\nexport {EXCLUDED_COACH_IDS};\r\n// Provisional cadence defaults for modes without a user-specified unlock threshold.\r\n// These are performance/completion requirements; XP never unlocks coaches.\r\nexport const CADENCE_MILESTONES=Object.freeze({sprintRounds:5,gentleActiveSeconds:300,paceActiveSeconds:300,meditationDays:Object.freeze([3,6,9,12])});\r\n// sourceLabel is the old roster/source metadata; label is the centralized coach name.\r\nconst SOURCE_COACHES=[{\"id\":\"myr5\",\"sourceLabel\":\"Original MYR5\"},{\"id\":\"roster/01-seed-pearo--3d_character_model\",\"sourceLabel\":\"Seed · Pearo 1\",\"group\":\"Seed · Pearo\"},{\"id\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\",\"sourceLabel\":\"Seed · Pearo 2\",\"group\":\"Seed · Pearo\"},{\"id\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\",\"sourceLabel\":\"Seed · Pearo 5\",\"group\":\"Seed · Pearo\"},{\"id\":\"roster/02-taper-tallstalk--3d_humanoid_figure\",\"sourceLabel\":\"Taper · Tallstalk 1\",\"group\":\"Taper · Tallstalk\"},{\"id\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"sourceLabel\":\"Taper · Tallstalk 2\",\"group\":\"Taper · Tallstalk\"},{\"id\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"sourceLabel\":\"Taper · Tallstalk 3\",\"group\":\"Taper · Tallstalk\"},{\"id\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"sourceLabel\":\"Pearl · Orb 1\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/03-pearl-orb-ring--circle_center\",\"sourceLabel\":\"Pearl · Orb 2\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"sourceLabel\":\"Pearl · Orb 3\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/03-pearl-orb-ring--robot_3d_model\",\"sourceLabel\":\"Pearl · Orb 4\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"sourceLabel\":\"Pearl · Orb 5\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"sourceLabel\":\"Pearl · Orb 6\",\"group\":\"Pearl · Orb\"},{\"id\":\"roster/04-crest-wedge--robot_creature_3d_model\",\"sourceLabel\":\"Crest · Wedge 1\",\"group\":\"Crest · Wedge\"},{\"id\":\"roster/04-crest-wedge--stylized_3d_character\",\"sourceLabel\":\"Crest · Wedge 2\",\"group\":\"Crest · Wedge\"},{\"id\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"sourceLabel\":\"Slope · Bobble 1\",\"group\":\"Slope · Bobble\"},{\"id\":\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"sourceLabel\":\"Ridge · Triad 1\",\"group\":\"Ridge · Triad\"},{\"id\":\"roster/06-ridge-triad--robot_3d_model1\",\"sourceLabel\":\"Ridge · Triad 2\",\"group\":\"Ridge · Triad\"},{\"id\":\"roster/07-bulb-sphereling--cute_robot_3d_model\",\"sourceLabel\":\"Bulb · Sphereling 1\",\"group\":\"Bulb · Sphereling\"},{\"id\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"sourceLabel\":\"Bulb · Sphereling 2\",\"group\":\"Bulb · Sphereling\"},{\"id\":\"roster/08-shard-asym--geometric_robot_3d_model\",\"sourceLabel\":\"Shard · Asym 1\",\"group\":\"Shard · Asym\"},{\"id\":\"roster/08-shard-asym--robot_3d_model4\",\"sourceLabel\":\"Shard · Asym 2\",\"group\":\"Shard · Asym\"},{\"id\":\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"sourceLabel\":\"Shard · Asym 3\",\"group\":\"Shard · Asym\"},{\"id\":\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"sourceLabel\":\"Shard · Asym 4\",\"group\":\"Shard · Asym\"},{\"id\":\"roster/09-monolith-tanka--3d_robot_model\",\"sourceLabel\":\"Monolith · Tanka 1\",\"group\":\"Monolith · Tanka\"},{\"id\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"sourceLabel\":\"Monolith · Tanka 2\",\"group\":\"Monolith · Tanka\"},{\"id\":\"roster/09-monolith-tanka--mini_robot_3d_model\",\"sourceLabel\":\"Monolith · Tanka 3\",\"group\":\"Monolith · Tanka\"},{\"id\":\"roster/09-monolith-tanka--robot_3d_model3\",\"sourceLabel\":\"Monolith · Tanka 5\",\"group\":\"Monolith · Tanka\"},{\"id\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\",\"sourceLabel\":\"Monolith · Tanka 6\",\"group\":\"Monolith · Tanka\"},{\"id\":\"roster/10-petal-wisp--stylized_creature_3d_model\",\"sourceLabel\":\"Petal · Wisp 1\",\"group\":\"Petal · Wisp\"},{\"id\":\"roster/10-petal-wisp--ghost_character_3d_model\",\"sourceLabel\":\"Petal · Wisp 2\",\"group\":\"Petal · Wisp\"},{\"id\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"sourceLabel\":\"Petal · Wisp 3\",\"group\":\"Petal · Wisp\"},{\"id\":\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"sourceLabel\":\"Anvil · Cask 1\",\"group\":\"Anvil · Cask\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"sourceLabel\":\"Seedpod · Snailslug 1\",\"group\":\"Seedpod · Snailslug\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"sourceLabel\":\"Seedpod · Snailslug 2\",\"group\":\"Seedpod · Snailslug\"},{\"id\":\"roster/13-fan-split--stylized_3d_character2\",\"sourceLabel\":\"Fan · Split 1\",\"group\":\"Fan · Split\"},{\"id\":\"roster/13-fan-split--stylized_toy_3d_model\",\"sourceLabel\":\"Fan · Split 2\",\"group\":\"Fan · Split\"},{\"id\":\"roster/14-chisel-spire--cone_head_3d_model\",\"sourceLabel\":\"Chisel · Spire 1\",\"group\":\"Chisel · Spire\"},{\"id\":\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"sourceLabel\":\"Chisel · Spire 2\",\"group\":\"Chisel · Spire\"},{\"id\":\"roster/15-orbital-coili--robot_character_3d_model\",\"sourceLabel\":\"Orbital · Coili 1\",\"group\":\"Orbital · Coili\"},{\"id\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"sourceLabel\":\"Spade · Arch 1\",\"group\":\"Spade · Arch\"},{\"id\":\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"sourceLabel\":\"Spade · Arch 2\",\"group\":\"Spade · Arch\"},{\"id\":\"roster/17-shellcap-manyarm--3d_character_figurine\",\"sourceLabel\":\"Shellcap · Manyarm 1\",\"group\":\"Shellcap · Manyarm\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"sourceLabel\":\"Shellcap · Manyarm 2\",\"group\":\"Shellcap · Manyarm\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"sourceLabel\":\"Shellcap · Manyarm 3\",\"group\":\"Shellcap · Manyarm\"},{\"id\":\"roster/18-quad-all--dragon_creature_3d_model\",\"sourceLabel\":\"Four-legged 1\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model1\",\"sourceLabel\":\"Four-legged 2\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model3\",\"sourceLabel\":\"Four-legged 3\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--four-legged_robot_3d_model\",\"sourceLabel\":\"Four-legged 4\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model1\",\"sourceLabel\":\"Four-legged 5\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model\",\"sourceLabel\":\"Four-legged 6\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--robotic_dog_3d_model\",\"sourceLabel\":\"Four-legged 7\",\"group\":\"Four-legged\"},{\"id\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"sourceLabel\":\"Four-legged 9\",\"group\":\"Four-legged\"},{\"id\":\"roster/19-genie-multi--fantasy_creature_3d_model\",\"sourceLabel\":\"Genie · Multi 1\",\"group\":\"Genie · Multi\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"sourceLabel\":\"Genie · Multi 2\",\"group\":\"Genie · Multi\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"sourceLabel\":\"Genie · Multi 3\",\"group\":\"Genie · Multi\"},{\"id\":\"roster/19-genie-multi--stylized_octopus_3d_model\",\"sourceLabel\":\"Genie · Multi 4\",\"group\":\"Genie · Multi\"},{\"id\":\"roster/20-lume--robotic_figure_3d_model\",\"sourceLabel\":\"Lume 1\",\"group\":\"Lume\"},{\"id\":\"roster/22-curve--stylized_alien_3d_model\",\"sourceLabel\":\"Curve 1\",\"group\":\"Curve\"},{\"id\":\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"sourceLabel\":\"Curve 2\",\"group\":\"Curve\"},{\"id\":\"roster/23-blob-texture-bodies--blob_creature_3d_model\",\"sourceLabel\":\"Blob 1\",\"group\":\"Blob\"},{\"id\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"sourceLabel\":\"Blob 2\",\"group\":\"Blob\"},{\"id\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"sourceLabel\":\"Blob 3\",\"group\":\"Blob\"},{\"id\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"sourceLabel\":\"Blob 4\",\"group\":\"Blob\"},{\"id\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"sourceLabel\":\"Blob 5\",\"group\":\"Blob\"}];\r\nexport const COACHES=SOURCE_COACHES.map(c=>({...c,label:coachName(c.id)}));\r\n// The first block keeps the difficulties of the old sibling-position formula, frozen so earlier\r\n// achievements do not shift. The appended block is the former implicit starters, now earnable.\r\nconst placements=[{\"id\":\"roster/23-blob-texture-bodies--blob_creature_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"easy\"},{\"id\":\"roster/18-quad-all--dragon_creature_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"easy\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model1\",\"tracks\":[\"meditation\"],\"difficulty\":\"easy\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model3\",\"tracks\":[\"meditation\"],\"difficulty\":\"easy\"},{\"id\":\"roster/18-quad-all--four-legged_robot_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"medium\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model1\",\"tracks\":[\"meditation\"],\"difficulty\":\"medium\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"hard\"},{\"id\":\"roster/18-quad-all--robotic_dog_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"hard\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"hard\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"expert\"},{\"id\":\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"expert\"},{\"id\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"easy\"},{\"id\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"easy\"},{\"id\":\"roster/06-ridge-triad--robot_3d_model1\",\"tracks\":[\"cardio\"],\"difficulty\":\"medium\"},{\"id\":\"roster/03-pearl-orb-ring--robot_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"hard\"},{\"id\":\"roster/20-lume--robotic_figure_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"expert\"},{\"id\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"tracks\":[\"arms\"],\"difficulty\":\"easy\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"tracks\":[\"arms\"],\"difficulty\":\"easy\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"tracks\":[\"arms\"],\"difficulty\":\"medium\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"tracks\":[\"arms\"],\"difficulty\":\"medium\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"tracks\":[\"arms\"],\"difficulty\":\"hard\"},{\"id\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"tracks\":[\"arms\"],\"difficulty\":\"hard\"},{\"id\":\"roster/08-shard-asym--robot_3d_model4\",\"tracks\":[\"arms\"],\"difficulty\":\"expert\"},{\"id\":\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"tracks\":[\"chest\"],\"difficulty\":\"easy\"},{\"id\":\"roster/08-shard-asym--geometric_robot_3d_model\",\"tracks\":[\"chest\"],\"difficulty\":\"easy\"},{\"id\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"tracks\":[\"chest\",\"martial-arts\"],\"difficulty\":\"medium\"},{\"id\":\"roster/04-crest-wedge--robot_creature_3d_model\",\"tracks\":[\"chest\"],\"difficulty\":\"hard\"},{\"id\":\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"tracks\":[\"chest\"],\"difficulty\":\"expert\"},{\"id\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"tracks\":[\"glutes\"],\"difficulty\":\"easy\"},{\"id\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"tracks\":[\"glutes\"],\"difficulty\":\"medium\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"tracks\":[\"glutes\"],\"difficulty\":\"hard\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"tracks\":[\"glutes\"],\"difficulty\":\"expert\"},{\"id\":\"roster/15-orbital-coili--robot_character_3d_model\",\"tracks\":[\"quads\"],\"difficulty\":\"easy\"},{\"id\":\"roster/13-fan-split--stylized_3d_character2\",\"tracks\":[\"quads\"],\"difficulty\":\"medium\"},{\"id\":\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"tracks\":[\"quads\"],\"difficulty\":\"hard\"},{\"id\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"tracks\":[\"yoga\"],\"difficulty\":\"easy\"},{\"id\":\"roster/19-genie-multi--fantasy_creature_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"easy\"},{\"id\":\"roster/10-petal-wisp--ghost_character_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"medium\"},{\"id\":\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"medium\"},{\"id\":\"roster/22-curve--stylized_alien_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"hard\"},{\"id\":\"roster/10-petal-wisp--stylized_creature_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"hard\"},{\"id\":\"roster/19-genie-multi--stylized_octopus_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"expert\"},{\"id\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"easy\"},{\"id\":\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"easy\"},{\"id\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"medium\"},{\"id\":\"roster/14-chisel-spire--cone_head_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"medium\"},{\"id\":\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"hard\"},{\"id\":\"roster/09-monolith-tanka--mini_robot_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"hard\"},{\"id\":\"roster/09-monolith-tanka--robot_3d_model3\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"expert\"},{\"id\":\"roster/13-fan-split--stylized_toy_3d_model\",\"tracks\":[\"martial-arts\"],\"difficulty\":\"expert\"},\r\n{\"id\":\"roster/01-seed-pearo--3d_character_model\",\"tracks\":[\"meditation\"],\"difficulty\":\"medium\"},\r\n{\"id\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\",\"tracks\":[\"yoga\"],\"difficulty\":\"hard\"},\r\n{\"id\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"hard\"},\r\n{\"id\":\"roster/02-taper-tallstalk--3d_humanoid_figure\",\"tracks\":[\"quads\"],\"difficulty\":\"hard\"},\r\n{\"id\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"tracks\":[\"quads\"],\"difficulty\":\"expert\"},\r\n{\"id\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"tracks\":[\"glutes\"],\"difficulty\":\"easy\"},\r\n{\"id\":\"roster/03-pearl-orb-ring--circle_center\",\"tracks\":[\"glutes\"],\"difficulty\":\"medium\"},\r\n{\"id\":\"roster/04-crest-wedge--stylized_3d_character\",\"tracks\":[\"chest\"],\"difficulty\":\"easy\"},\r\n{\"id\":\"roster/07-bulb-sphereling--cute_robot_3d_model\",\"tracks\":[\"cardio\"],\"difficulty\":\"medium\"},\r\n{\"id\":\"roster/09-monolith-tanka--3d_robot_model\",\"tracks\":[\"chest\"],\"difficulty\":\"medium\"},\r\n{\"id\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\",\"tracks\":[\"chest\"],\"difficulty\":\"hard\"},\r\n{\"id\":\"roster/17-shellcap-manyarm--3d_character_figurine\",\"tracks\":[\"arms\"],\"difficulty\":\"expert\"},\r\n{\"id\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"tracks\":[\"quads\"],\"difficulty\":\"medium\"},\r\n{\"id\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"tracks\":[\"glutes\"],\"difficulty\":\"hard\"}];\r\nconst groups={chest:['chest'],quads:['legs'],glutes:['hips'],arms:['shoulders'],yoga:['core','balance','yoga'],'martial-arts':['stances','boxing'],cardio:['cardio'],meditation:['meditation']};\r\n// Paths the user can pick. arms-shoulders is the chosen-styles id for the catalog track arms.\r\nexport const CHOOSABLE_TRACKS=Object.freeze(['chest','quads','glutes','arms-shoulders','yoga','martial-arts','cardio']);\r\nexport const PATH_TRACK_ALIASES=Object.freeze({arms:'arms-shoulders'});\r\nconst catalogTrack=path=>path==='arms-shoulders'?'arms':path;\r\n/** Canonical, de-duplicated choosable ids; unknown ids are dropped and \"arms\" becomes \"arms-shoulders\". */\r\nexport const normalizePaths=ids=>[...new Set((Array.isArray(ids)?ids:[]).map(id=>PATH_TRACK_ALIASES[id]??id).filter(id=>CHOOSABLE_TRACKS.includes(id)))];\r\nexport const WEAPON_GROUPS={chest:['crossbow','cannon'],legs:['greatsword','hammer'],hips:['scythe','halberd'],core:['mace','tome'],shoulders:['bow','trident'],balance:['axe','sabre'],yoga:['staff','wand'],stances:['dagger','spear'],boxing:['gauntlets','rapier'],cardio:['chakram','flail']};\r\nexport const STARTER_COACH_IDS=Object.freeze(['myr5','roster/23-blob-texture-bodies--blob_creature_3d_model']);\r\nexport function exerciseDifficulty(mode){if(mode==='plank-flight')return 'easy';const e=EXERCISES[mode];if(!e)return 'easy';const rows=GROUP_EXERCISES[e.group].filter(x=>x.kind===e.kind);return ['easy','medium','hard','expert'][Math.min(3,Math.floor(rows.indexOf(e)*4/rows.length))];}\r\nexport const COACH_REQUIREMENTS=placements.filter(p=>!STARTER_COACH_IDS.includes(p.id)).map(p=>({...p,name:coachName(p.id),groups:p.tracks.flatMap(t=>groups[t])}));\r\n// One introductory coach per path: the first easy coach whose primary track it is.\r\nexport const PATH_INTRO_COACH_IDS=Object.freeze(Object.fromEntries(CHOOSABLE_TRACKS.map(path=>[path,COACH_REQUIREMENTS.find(c=>c.tracks[0]===catalogTrack(path)&&c.difficulty==='easy').id])));\r\nexport const pathIntroCoachIds=paths=>normalizePaths(paths).map(path=>PATH_INTRO_COACH_IDS[path]);\r\nexport const SHIP_REQUIREMENTS=[{id:'supportive',group:'legs',kind:'hold'},{id:'direct',group:'legs',kind:'reps'},{id:'analytical',group:'chest',kind:'reps'},{id:'playful',group:'stances',kind:'hold'},{id:'calm',group:'yoga',kind:'hold'},{id:'mom',group:'hips',kind:'reps'}];\r\nexport function matchingCoaches(group,difficulty){return COACH_REQUIREMENTS.filter(c=>c.groups.includes(group)&&c.difficulty===difficulty);}\r\n","performance-import-codec.mjs":"import {EXERCISES} from './exercise-library.mjs';\r\nimport {COACHES} from './performance-catalog.mjs';\r\nimport {DIFFICULTIES,holdXp,repXp,SPRINT_ROUND_XP,GENTLE_BLOCK_XP,clampBonusXp} from './progression-rules.mjs';\r\nconst keys=['version','kind','difficulty','maxContinuousSeconds','perDifficultyContinuous','holdBlocks','coachId','rounds'];\r\nfunction data(raw){if(!raw||typeof raw!=='object'||Array.isArray(raw)||![Object.prototype,null].includes(Object.getPrototypeOf(raw)))throw Error('Invalid performance metadata.');const descriptors=Object.getOwnPropertyDescriptors(raw);if(Reflect.ownKeys(descriptors).some(k=>typeof k!=='string'||!Object.hasOwn(descriptors[k],'value')||!descriptors[k].enumerable))throw Error('Invalid performance metadata.');return Object.fromEntries(Object.entries(descriptors).map(([k,v])=>[k,v.value]));}\r\nconst finite=(n,max)=>Number.isFinite(n)&&n>=0&&n<=max;\r\nexport function validateImportedPerformance(raw,snapshot){\r\n const p=data(raw),e=EXERCISES[snapshot.mode],active=Math.min(1800,snapshot.activeSeconds);\r\n if(Object.keys(p).length!==keys.length+(Object.hasOwn(p,'bonusXp')?1:0)||keys.some(k=>!Object.hasOwn(p,k))||p.version!==2||!e||!DIFFICULTIES.includes(p.difficulty)||!finite(p.maxContinuousSeconds,active)||!Number.isInteger(p.rounds)||p.rounds<0||p.rounds>5||!(p.coachId===null||COACHES.some(c=>c.id===p.coachId)))throw Error('Invalid performance metadata.');\r\n const allowed=e.kind==='hold'?['hold']:e.group==='cardio'?['sprint','gentle']:e.kind==='pace'?['pace','gentle']:['reps'];if(!allowed.includes(p.kind)||p.kind==='reps'&&!Number.isSafeInteger(snapshot.value))throw Error('Performance kind does not match the exercise.');\r\n const continuous=data(p.perDifficultyContinuous);if(Object.entries(continuous).some(([d,n])=>!DIFFICULTIES.includes(d)||!finite(n,active))||Object.values(continuous).reduce((a,b)=>a+b,0)>active+1e-7)throw Error('Invalid continuous hold evidence.');\r\n if(!Array.isArray(p.holdBlocks)||p.holdBlocks.length>120)throw Error('Invalid paid hold blocks.');\r\n const blocks=p.holdBlocks.map((raw,index)=>{const b=data(raw);if(Object.keys(b).length!==3||!DIFFICULTIES.includes(b.difficulty)||b.activeStart!==index*15||!finite(b.continuousStart,b.activeStart)||b.continuousStart>=(continuous[b.difficulty]??0)&&b.continuousStart>=600)throw Error('Invalid paid hold blocks.');return {difficulty:b.difficulty,activeStart:b.activeStart,continuousStart:b.continuousStart};});\r\n if(p.kind==='hold'&&blocks.length!==Math.floor(active/15)||p.kind!=='hold'&&(blocks.length||Object.keys(continuous).length||p.maxContinuousSeconds)||p.kind==='sprint'&&(snapshot.activeSeconds+1e-7<p.rounds*15||snapshot.elapsedSeconds+1e-7<p.rounds*60))throw Error('Performance exceeds completed exercise time.');\r\n return {version:2,kind:p.kind,difficulty:p.difficulty,maxContinuousSeconds:p.maxContinuousSeconds,perDifficultyContinuous:Object.fromEntries(DIFFICULTIES.filter(d=>Object.hasOwn(continuous,d)).map(d=>[d,continuous[d]])),holdBlocks:blocks,coachId:p.coachId,rounds:p.rounds,...(Number.isSafeInteger(p.bonusXp)&&p.bonusXp>0?{bonusXp:p.bonusXp}:{})};\r\n}\r\nexport function projectImportedPerformance(raw,snapshot){const p=data(raw);return validateImportedPerformance({version:2,kind:p.kind,difficulty:p.difficulty,maxContinuousSeconds:p.maxContinuousSeconds??0,perDifficultyContinuous:p.kind==='hold'?(p.perDifficultyContinuous??{[p.difficulty]:p.maxContinuousSeconds??0}):{},holdBlocks:p.holdBlocks??[],coachId:p.coachId??null,rounds:p.kind==='sprint'?(p.rounds??0):0,...(Object.hasOwn(p,'bonusXp')?{bonusXp:p.bonusXp}:{})},snapshot);}\r\nexport function importedPerformanceXp(snapshot){const p=validateImportedPerformance(snapshot.performance,snapshot);let base;if(p.kind==='hold')base=p.holdBlocks.reduce((sum,b)=>sum+holdXp({difficulty:b.difficulty,from:b.activeStart,to:b.activeStart+15,continuousAtFrom:b.continuousStart}),0);else if(p.kind==='reps')base=repXp({difficulty:p.difficulty,to:Math.floor(snapshot.value)});else if(p.kind==='sprint')base=p.rounds*SPRINT_ROUND_XP;else base=Math.floor(Math.min(1800,snapshot.activeSeconds)/15)*GENTLE_BLOCK_XP;return base+clampBonusXp(p.bonusXp,base);}\r\n","performance-progress.mjs":"import {gameCacheKey,gameRewardAfterCutoff,acceptGameGeneration,storedGameGeneration} from './modules/coach-world/game-generation-client.mjs';\r\nimport {EXERCISES} from './exercise-library.mjs';\r\nimport {COACHES,COACH_REQUIREMENTS,STARTER_COACH_IDS,EXCLUDED_COACH_IDS,PATH_INTRO_COACH_IDS,CHOOSABLE_TRACKS,normalizePaths,pathIntroCoachIds,matchingCoaches,exerciseDifficulty,WEAPON_GROUPS,SHIP_REQUIREMENTS,CADENCE_MILESTONES} from './performance-catalog.mjs';\r\nimport {holdXp,repXp,performanceMilestones,performanceWeaponTier,coachXpMultiplier,DIFFICULTIES,XP_SCALE,DAILY_TRIO_BONUS} from './progression-rules.mjs';\r\nexport const PERFORMANCE_KEY='myr5-performance-progress-v2';\r\nexport function performanceOwner(storage=globalThis.localStorage,account=globalThis.myr5AuthenticatedAccount){\r\n const id=account?.user?.id;if(id)return `account:${id}${account.dataEpoch!=null?`:${account.dataEpoch}`:''}`;\r\n if(!storage)return 'guest:unavailable';\r\n const key='myr5-local-guest-owner-v1';try{let guest=storage.getItem(key);if(!guest){guest=globalThis.crypto?.randomUUID?.()||`guest-${Date.now()}-${Math.random()}`;storage.setItem(key,guest);}return `guest:${guest}`;}catch{return 'guest:unavailable';}\r\n}\r\nexport function localDay(time=Date.now()){const date=new Date(time);return `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')}`;}\r\n// accessVersion 3: coaches are only the explicit starters plus earned coaches and the two chosen\r\n// path introductions. Older records also held every unplaced coach as an implicit starter.\r\nexport const ACCESS_VERSION=3;\r\nconst fresh=()=>({version:2,accessVersion:ACCESS_VERSION,xpScale:XP_SCALE,paths:[],sessions:{},days:{},coaches:[...STARTER_COACH_IDS],goldenCoaches:[],weapons:{},ships:[],completions:{},totalXp:0,excludedCoaches:[]});\r\nfunction scope(options={}){const storage=Object.hasOwn(options,'storage')?options.storage:globalThis.localStorage,account=Object.hasOwn(options,'account')?options.account:globalThis.myr5AuthenticatedAccount;return {storage,owner:options.owner??performanceOwner(storage,account)};}\r\nconst object=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst finite=v=>Number.isFinite(v)&&v>=0;\r\nconst validDay=day=>typeof day==='string'&&/^\\d{4}-\\d{2}-\\d{2}$/.test(day)&&new Date(`${day}T12:00:00Z`).toISOString().slice(0,10)===day;\r\nconst validPaths=p=>Array.isArray(p)&&(p.length===0||(p.length===2&&new Set(p).size===2&&p.every(id=>CHOOSABLE_TRACKS.includes(id))));\r\n/** Shape check for a stored or synced performance record (client cache and server alike). */\r\nexport function validPerformanceState(v){return validState(v);}\r\nfunction validState(v){return v?.version===2&&(v.accessVersion===undefined||v.accessVersion===ACCESS_VERSION)&&(v.xpScale===undefined||v.xpScale===XP_SCALE)&&(v.paths===undefined||validPaths(v.paths))&&(v.activities===undefined||(object(v.activities)&&Object.values(v.activities).every(a=>object(a)&&['meditation','food'].includes(a.kind)&&validDay(a.day))))&&object(v.sessions)&&object(v.days)&&object(v.weapons)&&object(v.completions)&&Array.isArray(v.coaches)&&v.coaches.every(id=>COACHES.some(c=>c.id===id)||EXCLUDED_COACH_IDS.includes(id))&&new Set(v.coaches).size===v.coaches.length&&Array.isArray(v.goldenCoaches)&&v.goldenCoaches.every(id=>v.coaches.includes(id))&&Array.isArray(v.ships)&&v.ships.every(id=>SHIP_REQUIREMENTS.some(s=>s.id===id))&&Object.values(v.weapons).every(n=>Number.isInteger(n)&&n>=0&&n<=20)&&Object.values(v.completions).every(n=>Number.isSafeInteger(n)&&n>=0)&&Object.entries(v.days).every(([day,d])=>validDay(day)&&object(d)&&finite(d.workoutXp)&&['workout','meditation','food'].every(k=>typeof d[k]==='boolean'))&&Object.values(v.sessions).every(s=>object(s)&&validDay(s.day)&&finite(s.xp)&&finite(s.value)&&finite(s.continuous));}\r\n// Coaches a record has really earned, rebuilt from its own history: the explicit starters, the\r\n// session milestones recordPerformanceSession applies, and completed meditation days.\r\nconst CADENCE_COUNT={sprint:CADENCE_MILESTONES.sprintRounds,gentle:CADENCE_MILESTONES.gentleActiveSeconds,pace:CADENCE_MILESTONES.paceActiveSeconds};\r\nfunction earnedCoaches(state){\r\n const earned=new Set(STARTER_COACH_IDS),grant=(group,level)=>{for(const coach of matchingCoaches(group,level))earned.add(coach.id);};\r\n for(const s of Object.values(state.sessions)){\r\n  if(s.kind==='hold'||s.kind==='reps'){\r\n   for(const [level,performance] of s.kind==='hold'?Object.entries(s.perDifficultyContinuous??{[s.difficulty]:s.continuous}):[[s.difficulty,s.value]])\r\n    if(finite(performance)&&performanceMilestones(s.kind,performance).coach)grant(s.group,level);\r\n  }else if(s.kind in CADENCE_COUNT&&s.value>=CADENCE_COUNT[s.kind])grant(s.group,s.difficulty);\r\n }\r\n const days=Object.values(state.days).filter(d=>d.meditation).length;\r\n DIFFICULTIES.forEach((level,i)=>{if(days>=CADENCE_MILESTONES.meditationDays[i])grant('meditation',level);});\r\n return earned;\r\n}\r\n/** Brings a validated record (client cache or synced copy) to the current access rules, in place.\r\n * Pre-v3 records are rebuilt from earnedCoaches() plus the chosen paths, so implicit starters are\r\n * dropped while sessions, days, XP, weapons and ships are untouched. Uses only the record itself. */\r\nexport function migratePerformanceAccess(state){\r\n // XP rescale: a record without the xpScale marker holds x1 XP. Multiply once, then mark; later runs skip.\r\n if(state.xpScale!==XP_SCALE){for(const d of Object.values(state.days))d.workoutXp*=XP_SCALE;for(const s of Object.values(state.sessions))s.xp*=XP_SCALE;state.xpScale=XP_SCALE;}\r\n state.excludedCoaches??=[];for(const id of EXCLUDED_COACH_IDS)if(!state.excludedCoaches.includes(id))state.excludedCoaches.push(id);\r\n state.paths??=[];\r\n if(state.accessVersion!==ACCESS_VERSION){\r\n  const keep=new Set([...earnedCoaches(state),...pathIntroCoachIds(state.paths)]);\r\n  state.coaches=COACHES.map(c=>c.id).filter(id=>keep.has(id));state.accessVersion=ACCESS_VERSION;\r\n }\r\n state.coaches=state.coaches.filter(id=>!EXCLUDED_COACH_IDS.includes(id));for(const id of STARTER_COACH_IDS)unique(state.coaches,id);\r\n state.goldenCoaches=state.goldenCoaches.filter(id=>state.coaches.includes(id));\r\n return state;\r\n}\r\nexport function readPerformanceProgress(options={}){try{const {storage,owner}=scope(options),value=JSON.parse(storage?.getItem((owner==='server-derivation'?`${PERFORMANCE_KEY}/${owner}`:gameCacheKey(`${PERFORMANCE_KEY}/${owner}`,storage)))||'null');return validState(value)?recalculate(migratePerformanceAccess(value)):fresh();}catch{return fresh();}}\r\n/** One-time choice of exactly 2 paths (chosen-styles ids) for the options owner; grants and persists\r\n * their introductory coaches. The same pair is a no-op, a different pair needs options.reset. A reset\r\n * swaps the introductions (still exactly two) and never revokes earned coaches. */\r\nexport function choosePerformancePaths(ids,options={}){\r\n const paths=normalizePaths(ids);if(paths.length!==2)throw RangeError('Exactly 2 workout paths must be selected.');\r\n const state=readPerformanceProgress(options);\r\n if(state.paths.length){if(paths.every(p=>state.paths.includes(p)))return state;if(!options.reset)throw RangeError('Workout paths are already chosen.');}\r\n const earned=earnedCoaches(state),previous=pathIntroCoachIds(state.paths);\r\n state.coaches=state.coaches.filter(id=>!previous.includes(id)||earned.has(id));\r\n for(const id of pathIntroCoachIds(paths))unique(state.coaches,id);\r\n state.paths=paths;\r\n return save(state,options);\r\n}\r\nfunction save(state,options){const {storage,owner}=scope(options);if(!storage)throw Error('Progress storage is unavailable.');storage.setItem((owner==='server-derivation'?`${PERFORMANCE_KEY}/${owner}`:gameCacheKey(`${PERFORMANCE_KEY}/${owner}`,storage)),JSON.stringify(state));if(typeof globalThis.CustomEvent==='function')globalThis.dispatchEvent?.(new CustomEvent('myr5:performance-progress',{detail:{owner,progress:state}}));return state;}\r\nfunction dayEntry(state,day){if(!validDay(day))throw RangeError('Invalid workout day.');return state.days[day]??=( {workoutXp:0,workout:false,meditation:false,food:false});}\r\nfunction recalculate(state){state.totalXp=Object.values(state.days).reduce((sum,d)=>sum+d.workoutXp*(d.meditation?2:1)+(d.workout&&d.meditation&&d.food?DAILY_TRIO_BONUS:0),0)+(Number.isFinite(state.worldBonusXp)&&state.worldBonusXp>=0?state.worldBonusXp:0);return state;}\r\nexport function recordDailyActivity(kind,{day=localDay(),id,startedAt,completedAt}={},options={}){if(options.owner!=='server-derivation'&&!gameRewardAfterCutoff({startedAt,completedAt},options.storage))return readPerformanceProgress(options);if(!['meditation','food'].includes(kind))throw RangeError('Unknown daily activity.');const state=readPerformanceProgress(options);\r\n state.activities??={};if(id!=null){if(typeof id!=='string'||!id||['__proto__','constructor','prototype'].includes(id))throw RangeError('Invalid daily activity ID.');const key=`${kind}:${id}`;if(Object.hasOwn(state.activities,key))return state;state.activities[key]={kind,day};}\r\n dayEntry(state,day)[kind]=true;\r\n // Existing meditation coaches use completed-day milestones, separate from XP.\r\n // Three, six, nine and twelve days cover the four difficulty blocks.\r\n if(kind==='meditation'){const days=Object.values(state.days).filter(d=>d.meditation).length;for(let i=0;i<4;i++)if(days>=CADENCE_MILESTONES.meditationDays[i])for(const coach of matchingCoaches('meditation',['easy','medium','hard','expert'][i]))unique(state.coaches,coach.id);}\r\n return save(recalculate(state),options);}\r\nconst unique=(list,id)=>{if(!list.includes(id))list.push(id);};\r\nexport function recordPerformanceSession(record,options={}){\r\n if(options.owner!=='server-derivation'&&!gameRewardAfterCutoff(record,options.storage))return readPerformanceProgress(options);\r\n if(!record||typeof record.id!=='string'||!record.id||['__proto__','constructor','prototype'].includes(record.id))throw RangeError('Saved workout ID is required.');\r\n const state=readPerformanceProgress(options);if(Object.hasOwn(state.sessions,record.id)||record.preparation||record.earned===false)return state;\r\n const exercise=EXERCISES[record.mode],kind=record.kind??(exercise?.kind==='hold'?'hold':'reps'),difficulty=record.difficulty??exerciseDifficulty(record.mode),group=exercise?.group??record.group;\r\n const day=record.day??localDay(record.completedAt??Date.now()),value=Number(record.value??record.activeSeconds??0),continuous=Number(record.maxContinuousSeconds??record.continuousSeconds??0);\r\n if(!Number.isFinite(value)||value<0||!Number.isFinite(continuous)||continuous<0)throw RangeError('Invalid saved performance.');\r\n if(kind==='hold'&&continuous>Number(record.activeSeconds??value))throw RangeError('Continuous time cannot exceed active hold time.');\r\n const holdPerformances=record.perDifficultyContinuous??{[difficulty]:continuous};\r\n if(kind==='hold'&&(!object(holdPerformances)||Object.entries(holdPerformances).some(([level,seconds])=>!DIFFICULTIES.includes(level)||!finite(seconds))||Object.values(holdPerformances).reduce((sum,seconds)=>sum+seconds,0)>Number(record.activeSeconds??value)+1e-7))throw RangeError('Invalid per-difficulty hold performance.');\r\n if(kind==='meditation')return recordDailyActivity('meditation',{day,id:record.id},options);\r\n const earnedCoachCount=record.earnedCoachCount??state.coaches.filter(id=>!STARTER_COACH_IDS.includes(id)).length;\r\n if(!Number.isSafeInteger(earnedCoachCount)||earnedCoachCount<0||earnedCoachCount>COACH_REQUIREMENTS.length)throw RangeError('Invalid saved coach bonus.');\r\n const multiplier=coachXpMultiplier(earnedCoachCount);\r\n let xp=record.xpBase??record.earnedXp;\r\n if(xp===undefined)xp=kind==='hold'?holdXp({difficulty,to:record.activeSeconds??value}):kind==='reps'?repXp({difficulty,to:Math.floor(value)}):0;\r\n if(!Number.isFinite(xp)||xp<0)throw RangeError('Invalid workout XP.');\r\n // Persisted controller totals may include difficulty switches. Bound the raw\r\n // total by the expert session cap without reassigning those completed segments.\r\n if(kind==='reps')xp=Math.min(xp,repXp({difficulty:'expert',to:30}));\r\n if(kind==='hold')xp=Math.min(xp,holdXp({difficulty:'expert',to:1800}));\r\n // Controllers provide base XP before coach/daily multipliers. A credited ID never earns twice.\r\n const entry=dayEntry(state,day);entry.workoutXp+=xp*multiplier;entry.workout=true;\r\n state.sessions[record.id]={mode:record.mode,group,kind,difficulty,value,continuous,day,xp:xp*multiplier,...(kind==='hold'?{perDifficultyContinuous:{...holdPerformances}}:{})};\r\n if(kind==='hold'||kind==='reps'){\r\n  for(const [performedDifficulty,performance] of kind==='hold'?Object.entries(holdPerformances):[[difficulty,value]]){\r\n  const milestones=performanceMilestones(kind,performance),matches=matchingCoaches(group,performedDifficulty);\r\n  if(milestones.coach)for(const coach of matches)unique(state.coaches,coach.id);\r\n  if(milestones.golden){for(const coach of matches)unique(state.goldenCoaches,coach.id);if(record.coachId&&state.coaches.includes(record.coachId))unique(state.goldenCoaches,record.coachId);}\r\n  const key=`${group}/${kind}/${performedDifficulty}`;\r\n  if(milestones.coach)state.completions[key]=(state.completions[key]??0)+1;\r\n  // The first coach clear keeps the two milestone tiers. Each re-clear upgrades\r\n  // once within this difficulty's five-tier block; a ten-minute hold earns its special.\r\n  const tier=milestones.special?performanceWeaponTier(performedDifficulty,5):milestones.coach?performanceWeaponTier(performedDifficulty,state.completions[key]+1):milestones.weapon2?performanceWeaponTier(performedDifficulty,0)+2:milestones.weapon1?performanceWeaponTier(performedDifficulty,0)+1:0;\r\n  for(const weapon of WEAPON_GROUPS[group]??[])if(tier)state.weapons[weapon]=Math.max(state.weapons[weapon]??0,Math.min(20,tier));\r\n  if(milestones.coach&&performedDifficulty==='expert')for(const ship of SHIP_REQUIREMENTS.filter(s=>s.group===group&&s.kind===kind))unique(state.ships,ship.id);\r\n  }\r\n }\r\n if(['sprint','gentle','pace'].includes(kind)){\r\n  const count=kind==='sprint'?Number(record.rounds??value):Number(record.activeSeconds??value),threshold=kind==='sprint'?CADENCE_MILESTONES.sprintRounds:kind==='gentle'?CADENCE_MILESTONES.gentleActiveSeconds:CADENCE_MILESTONES.paceActiveSeconds;\r\n  if(count>=threshold){for(const coach of matchingCoaches(group,difficulty))unique(state.coaches,coach.id);const key=`${group}/${kind}/${difficulty}`;state.completions[key]=(state.completions[key]??0)+1;for(const weapon of WEAPON_GROUPS[group]??[])state.weapons[weapon]=Math.max(state.weapons[weapon]??0,performanceWeaponTier(difficulty,state.completions[key]));}\r\n }\r\n return save(recalculate(state),options);\r\n}\r\n/** One workout (any, incl. Subway Burpees), one meditation and one food log open every coach, path and movement.\r\n * Derived from the stored sessions/days, so it persists and syncs exactly like the rest of progress. */\r\nexport function unlockStatus(state){\r\n const days=Object.values(state?.days??{}),workout=Object.keys(state?.sessions??{}).length>0||days.some(d=>d.workout),meditation=days.some(d=>d.meditation),food=days.some(d=>d.food);\r\n return {workout,meditation,food,done:workout&&meditation&&food};\r\n}\r\nexport const everythingUnlocked=(options={})=>unlockStatus(readPerformanceProgress(options)).done;\r\n/** Owned coaches; the unlock rule opens all of them (XP multipliers still use only earned coaches). */\r\nexport const unlockedCoachIds=(options={})=>{const s=readPerformanceProgress(options);return unlockStatus(s).done?COACHES.map(c=>c.id).filter(id=>!EXCLUDED_COACH_IDS.includes(id)):s.coaches;};\r\nexport const coachAccess=(id,options={})=>unlockedCoachIds(options).includes(id);\r\nexport const goldenCoach=(id,options={})=>readPerformanceProgress(options).goldenCoaches.includes(id);\r\nexport const weaponTierFor=(type,options={})=>readPerformanceProgress(options).weapons[type]??0;\r\nexport const shipAccess=(id,options={})=>readPerformanceProgress(options).ships.includes(id);\r\n/** Call only with the result of the authenticated account reader, never a\r\n * CustomEvent or user-supplied profile. Pending device-only sessions survive. */\r\nexport function mergeVerifiedPerformance(account,{storage=globalThis.localStorage}={}){\r\n const game=account?.gameGeneration!=null?account:account?.progress;\r\n if(game?.gameGeneration!=null)acceptGameGeneration(game,game.gameGeneration,{storage});else if((storedGameGeneration(storage)?.gameResetAt??0)>0)throw RangeError('Game generation is required before merging earned progress.');\r\n const remote=account?.progress?.performance;if(!remote)return readPerformanceProgress({storage,account});\r\n if(!account?.user?.id||!Number.isSafeInteger(account.dataEpoch)||account.dataEpoch<1)throw RangeError('Verified account identity is required.');if(!validState(remote))throw RangeError('Invalid synced performance progress.');\r\n const options={storage,account},local=game?.gameResetAt>0?fresh():readPerformanceProgress(options),before=JSON.stringify(local),next=JSON.parse(before),theirs=migratePerformanceAccess(JSON.parse(JSON.stringify(remote)));\r\n next.sessions={...local.sessions,...theirs.sessions};next.days={};\r\n next.worldBonusXp=Number.isFinite(theirs.worldBonusXp)&&theirs.worldBonusXp>=0?theirs.worldBonusXp:0;\r\n for(const [day,d] of [...Object.entries(local.days),...Object.entries(remote.days)]){const entry=dayEntry(next,day);entry.food||=d.food;entry.meditation||=d.meditation;}\r\n for(const session of Object.values(next.sessions)){const entry=dayEntry(next,session.day);entry.workout=true;entry.workoutXp+=session.xp;}\r\n // Access is rebuilt from the merged history and paths; only non-introduction coaches carry over.\r\n next.paths=theirs.paths.length?theirs.paths:local.paths;\r\n const introductions=Object.values(PATH_INTRO_COACH_IDS),want=new Set([...local.coaches,...theirs.coaches].filter(id=>!introductions.includes(id)));\r\n for(const id of [...earnedCoaches(next),...pathIntroCoachIds(next.paths)])want.add(id);\r\n next.coaches=[...new Set([...local.coaches,...theirs.coaches,...COACHES.map(c=>c.id)])].filter(id=>want.has(id));\r\n next.goldenCoaches=[...new Set([...local.goldenCoaches,...theirs.goldenCoaches])].filter(id=>next.coaches.includes(id));next.ships=[...new Set([...local.ships,...remote.ships])];\r\n for(const [key,value] of Object.entries(remote.weapons))next.weapons[key]=Math.max(next.weapons[key]??0,value);\r\n for(const [key,value] of Object.entries(remote.completions))next.completions[key]=Math.max(next.completions[key]??0,value);\r\n next.activities={...(local.activities??{}),...(remote.activities??{})};migratePerformanceAccess(next);recalculate(next);\r\n return JSON.stringify(next)===before?local:!storage?next:save(next,options);\r\n}\r\nfunction priorDay(day,offset){const d=new Date(`${day}T12:00:00`);d.setDate(d.getDate()-offset);return localDay(d.getTime());}\r\nexport function workoutEligibility(mode,{day=localDay(),kind,...options}={}){const group=EXERCISES[mode]?.group;if(!group)return {allowed:true,reason:null,group:null};const sessions=Object.values(readPerformanceProgress(options).sessions),trained=d=>sessions.some(s=>s.day===d&&s.group===group);if(trained(priorDay(day,1))&&trained(priorDay(day,2)))return {allowed:false,reason:'Your boss is recovering. Take today off this muscle group.',group};if((kind??EXERCISES[mode].kind)==='reps'&&sessions.some(s=>s.day===day&&s.group===group&&s.kind==='reps'))return {allowed:false,reason:'Working set complete for this muscle group today.',group};return {allowed:true,reason:null,group};}\r\n","performance-reconcile.mjs":"import {gameRewardAfterCutoff} from './modules/coach-world/game-generation-client.mjs';\r\nimport {XP_SCALE} from './progression-rules.mjs';\r\nimport {localDay,recordPerformanceSession,readPerformanceProgress} from './performance-progress.mjs';\r\n\r\n/** Only replay durable new-format completions for the requested progression owner.\r\n * The repository guest owner and account progression owner are separate: the\r\n * completion's captured performanceOwner is required and is never inferred. */\r\nexport function savedPerformanceRecords(rows,{owner}={}){\r\n if(typeof owner!=='string'||!owner)throw RangeError('A captured progression owner is required.');\r\n const seen=new Set(),records=[];\r\n for(const row of [...(rows??[])].sort((a,b)=>(a?.completedAt??0)-(b?.completedAt??0)||String(a?.id).localeCompare(String(b?.id)))){\r\n  if(!gameRewardAfterCutoff(row))continue;\r\n  const p=row?.progress?.performance;\r\n  if(row?.status!=='completed'||!Number.isSafeInteger(row.completedAt)||row.completedAt<0||typeof row.id!=='string'||!row.id||seen.has(row.id)||!p||typeof p!=='object'||Array.isArray(p))continue;\r\n  if(p.performanceOwner!==owner||row.progress.performanceOwner!=null&&row.progress.performanceOwner!==owner||p.id!=null&&p.id!==row.id||p.mode!==row.mode||p.preparation||p.earned===false||row.completion?.earned===false)continue;\r\n  if(!['hold','reps','sprint','gentle','pace'].includes(p.kind)||!Number.isFinite(p.xpBase)||p.xpBase<0)continue;\r\n  seen.add(row.id);records.push({...p,xpBase:p.xpScale===XP_SCALE?p.xpBase:p.xpBase*XP_SCALE,xpScale:XP_SCALE,id:row.id,startedAt:row.startedAt,completedAt:row.completedAt,day:p.day??localDay(row.completedAt)});\r\n }\r\n return records;\r\n}\r\n\r\n/** Retry-safe local repair after IndexedDB committed but the reward cache failed.\r\n * Call again on startup/history refresh. Failed writes remain retryable because\r\n * the durable completion is retained and only the reward ledger stores credit. */\r\nexport function reconcilePerformanceWorkouts(rows,options={}){\r\n const records=savedPerformanceRecords(rows,options),failed=[];let replayed=0,progress=readPerformanceProgress(options);\r\n for(const record of records){\r\n  if(Object.hasOwn(progress.sessions,record.id))continue;\r\n  try{progress=recordPerformanceSession(record,options);if(Object.hasOwn(progress.sessions,record.id))replayed++;}\r\n  catch(error){failed.push({id:record.id,error:error instanceof Error?error.message:String(error)});}\r\n }\r\n return {progress,replayed,failed};\r\n}\r\n","personal-tracker.css":".mom-tracker{margin:18px 0 10px;border:2px solid #a18796;border-radius:10px;padding:16px;background:linear-gradient(135deg,#534053,#302437);color:#f8ebd7;box-shadow:inset 0 1px #d7b9c6,0 5px 0 #191320;font:16px/1.4 Arial,sans-serif;overflow:hidden}\r\n.mom-tracker .tracker-brand{display:flex;align-items:center;gap:12px;margin:-16px -16px 18px;padding:12px 16px;background:#ead6ac;color:#362b3c;border-bottom:3px solid #9b815f}\r\n.tracker-brand img{width:48px;height:48px;object-fit:contain;flex:none}.tracker-brand>div{flex:1;min-width:0}.tracker-brand>div>span{font:700 17px/1.2 Arial,sans-serif;letter-spacing:3px}\r\n.mom-tracker h2{margin:3px 0 0;color:#574359;font:14px/1.3 Arial,sans-serif}.tracker-unit{font:700 12px/1.4 monospace;letter-spacing:1px;border:1px solid #897153;padding:5px}\r\n.tracker-identity{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:16px}.mom-tracker .tracker-label{display:block;font:700 12px/1.4 Arial,sans-serif;letter-spacing:1.4px;text-transform:uppercase;color:#d0b7c7}\r\n.tracker-member p{margin:4px 0;color:#fff3d5;font:700 clamp(24px,6vw,36px)/1.15 Arial,sans-serif;overflow-wrap:anywhere}\r\n.tracker-day{display:flex;align-items:center;gap:10px;padding:10px 14px;border:2px solid #b19465;border-radius:5px;background:#211c29;box-shadow:inset 0 2px 6px #0007}.tracker-day span{text-transform:uppercase;font-size:12px;letter-spacing:1px}.tracker-day strong{color:#ffe2a0;font:700 40px/1 monospace;font-variant-numeric:tabular-nums}\r\n.tracker-record{margin:16px 0;padding:12px 14px;border-left:4px solid #a7ccad;background:#211b2a}.tracker-record p{margin:2px 0;color:#edf6e7}.tracker-record strong{font-size:25px;font-variant-numeric:tabular-nums}\r\n.mom-tracker .tracker-target-title{font:700 12px/1.4 Arial,sans-serif;letter-spacing:1px;text-transform:uppercase;color:#eed8ba;margin:20px 0 9px}.tracker-targets{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:0}.tracker-targets>div{border:1px solid #8d7588;border-radius:4px;padding:12px;background:#3a2e41}.tracker-targets [hidden]{display:none}.tracker-targets dt{font-size:13px;color:#dfc9d7}.tracker-targets dd{margin:3px 0 0;display:flex;gap:7px;align-items:baseline;flex-wrap:wrap;color:#efdcb7}.tracker-targets strong{font:700 27px/1.2 monospace;font-variant-numeric:tabular-nums}.tracker-targets dd span{font-size:12px}\r\n.tracker-focus{padding:16px 0}.tracker-focus p{margin:4px 0 0;font-size:15px;color:#f4e6d1}.tracker-footer{display:flex;gap:8px;align-items:center;justify-content:space-between;flex-wrap:wrap;border-top:1px solid #8b6f84;padding-top:8px}.tracker-footer>span{font:12px/1.5 monospace;letter-spacing:1px;color:#d3bfcf}.tracker-footer a{display:inline-flex;min-height:44px;align-items:center;color:#ffe2a0;font-size:14px;font-weight:700}.tracker-footer a:focus-visible{outline:3px solid #ffe2a0;outline-offset:4px}\r\n@media(max-width:360px){.tracker-brand img{width:38px;height:38px}.tracker-unit{display:none}.tracker-day{padding:8px;gap:6px}.tracker-day strong{font-size:32px}}\r\n.mom-tracker{padding:0}\r\n.mom-tracker .tracker-brand{margin:0;border-bottom:0;min-height:72px;cursor:pointer;list-style:none;gap:10px}\r\n.tracker-brand::-webkit-details-marker{display:none}\r\n.tracker-brand:focus-visible{outline:3px solid #ffe2a0;outline-offset:-4px}\r\n.tracker-brand img{width:38px;height:38px}\r\n.tracker-summary-day{white-space:nowrap;font:14px/1.3 Arial,sans-serif}\r\n.tracker-summary-day strong{font-variant-numeric:tabular-nums}\r\n.tracker-chevron{font-size:24px;transition:transform .2s}\r\n.tracker-collapse[open] .tracker-chevron{transform:rotate(180deg)}\r\n.tracker-collapse[open] .tracker-brand{border-bottom:3px solid #9b815f}\r\n.tracker-content{padding:16px}\r\n","personal-tracker.mjs":"export function renderPersonalTracker(plate,profile,targets,exerciseNames,progress){\r\n if(!plate.dataset.mounted){\r\n  plate.className='mom-tracker';plate.setAttribute('aria-labelledby','personalTrackerTitle');\r\n  plate.innerHTML=`<details class=\"tracker-collapse\"><summary class=\"tracker-brand\"><img src=\"/pod/mom-inc-mark.png\" alt=\"\"><div><span>MOM INC.</span><h2 id=\"personalTrackerTitle\">Personal tracker</h2></div><span class=\"tracker-summary-day\">Day <strong data-tracker=\"day\"></strong></span><span class=\"tracker-chevron\" aria-hidden=\"true\">⌄</span></summary>\r\n   <div class=\"tracker-content\"><div class=\"tracker-identity\"><div class=\"tracker-member\"><p data-tracker=\"name\"></p></div></div>\r\n   <div class=\"tracker-record\"><p><strong id=\"trackerCompletedSets\">0</strong> sets logged</p></div>\r\n   <h3 class=\"tracker-target-title\">Today’s targets</h3><dl class=\"tracker-targets\">\r\n    <div><dt>Reps / set</dt><dd><strong data-tracker=\"reps\"></strong><span>reps</span></dd></div>\r\n    <div><dt>Hold / set</dt><dd><strong data-tracker=\"hold\"></strong><span>sec</span></dd></div>\r\n    <div data-nutrition-target><dt>Protein</dt><dd><strong data-tracker=\"protein\"></strong><span>g / day</span></dd></div>\r\n    <div data-nutrition-target><dt>Water</dt><dd><strong data-tracker=\"water\"></strong><span>oz / day</span></dd></div>\r\n   </dl><div class=\"tracker-focus\"><span class=\"tracker-label\">Your program</span><p data-tracker=\"exercises\"></p></div>\r\n   <footer class=\"tracker-footer\"><a href=\"/onboarding.html?edit=1\">Settings ↗</a></footer></div></details>`;\r\n  plate.dataset.mounted='true';\r\n }\r\n const values={name:profile.name,day:String(targets.day).padStart(2,'0'),reps:targets.reps,hold:targets.holdSeconds,protein:targets.proteinGrams,water:targets.waterOz,exercises:profile.exercises.map(k=>exerciseNames[k]||k).join(' · ')};\r\n for(const [key,value]of Object.entries(values))plate.querySelector(`[data-tracker=\"${key}\"]`).textContent=value??'—';\r\n for(const tile of plate.querySelectorAll('[data-nutrition-target]'))tile.hidden=!targets.proteinGrams;\r\n updateTrackerProgress(progress);\r\n}\r\nexport function updateTrackerProgress(progress){const count=document.getElementById('trackerCompletedSets');if(count&&Number.isFinite(progress?.completedSets))count.textContent=String(progress.completedSets);}\r\n","pet-choice.mjs":"// The cage's device-local preference. A pet must also be a currently granted\r\n// battle-pass reward; this module never grants one or changes a coach recipe.\r\nimport {BOSSES,bossRewards} from './battle-pass-rewards.mjs';\r\nimport * as unlockLedger from './unlock-ledger.mjs';\r\n\r\nexport const PET_CHOICE_KEY='myr5-cage-selection-v1';\r\nexport const PET_CHOICE_SCOPE_LABEL='Selected on this device';\r\n\r\nconst canonicalPets=Object.freeze([...new Map(BOSSES.flatMap(boss=>bossRewards(boss.id).flat()).filter(item=>item.kind==='pet').map(item=>[item.id,Object.freeze({id:item.id,name:item.name})])).values()]);\r\nconst canonicalIds=new Set(canonicalPets.map(pet=>pet.id));\r\nconst empty=()=>({version:1,petId:null});\r\nconst storageFor=options=>{try{return options.storage??globalThis.localStorage;}catch{return null;}};\r\nconst ledgerFor=options=>options.ledger??unlockLedger;\r\nconst granted=(id,options)=>{try{return canonicalIds.has(id)&&ledgerFor(options).isGranted('pet',id)===true;}catch{return false;}};\r\nconst write=(selection,options)=>{try{const storage=storageFor(options);if(!storage)return false;storage.setItem(PET_CHOICE_KEY,JSON.stringify(selection));return true;}catch{return false;}};\r\n\r\n/** Includes None and only canonical battle-pass pets currently granted. */\r\nexport function availablePetChoices(options={}){\r\n return [{id:null,name:'None'},...canonicalPets.filter(pet=>granted(pet.id,options))];\r\n}\r\n\r\n/** Revalidates a saved preference against current grants on every read. */\r\nexport function selectedPetChoice(options={}){\r\n let saved;\r\n try{saved=storageFor(options)?.getItem(PET_CHOICE_KEY);if(saved===null||saved===undefined)return empty();saved=JSON.parse(saved);}catch{return empty();}\r\n if(saved?.version===1&&(saved.petId===null||typeof saved.petId==='string'&&granted(saved.petId,options)))return {version:1,petId:saved.petId};\r\n const selection=empty();write(selection,options);return selection;\r\n}\r\n\r\n/** Returns true only when the allowed choice was saved on this device. */\r\nexport function selectPetChoice(petId,options={}){\r\n if(petId!==null&&!(typeof petId==='string'&&granted(petId,options)))return false;\r\n return write({version:1,petId},options);\r\n}\r\n","player-level.mjs":"import {COSMETIC_LEVEL_XP,COSMETIC_XP_TARGET,cosmeticLevel,BENCHMARK} from './progression-rules.mjs';\r\nimport {readPerformanceProgress} from './performance-progress.mjs';\r\nexport const XP_KNOBS=Object.freeze({levels:250,maxXp:COSMETIC_XP_TARGET,...BENCHMARK});\r\nexport const LEVEL_XP=COSMETIC_LEVEL_XP;\r\nexport const levelFor=cosmeticLevel;\r\n// Aggregate set counts cannot reconstruct performance XP. Use the saved ledger.\r\nexport const xpFromHistory=({totalXp=0}={})=>Math.max(0,Number(totalXp)||0);\r\nexport const BASELINE_KEY='myr5.xpBaseline.v1';\r\nexport function playerLevel(progress,storage=globalThis.localStorage){return cosmeticLevel(progress?.performanceVersion===2?xpFromHistory(progress):readPerformanceProgress({storage}).totalXp);}\r\n","pocket-hardware.css":"/* The viewport is the face of a pocket field unit. The rim never catches taps. */\r\nhtml{background:radial-gradient(ellipse at top,#433a42,#15131a 70%);scroll-padding-block:36px}\r\nbody{width:calc(100% - 20px);max-width:760px;margin:12px auto;min-height:calc(100dvh - 24px);padding:20px 9px max(24px,env(safe-area-inset-bottom));border-radius:36px;background:repeating-linear-gradient(0deg,#dec5d604 0 1px,transparent 1px 4px),linear-gradient(115deg,#645361,#382f3c 35%,#332b39 72%,#705b6b);box-shadow:inset 0 0 0 2px #c1acb0,inset 0 0 0 7px #211b27,0 10px 0 #131017,0 18px 35px #000b}\r\n.device-bezel{position:fixed;z-index:25;top:10px;bottom:10px;left:50%;transform:translateX(-50%);width:calc(100% - 16px);max-width:764px;pointer-events:none;border:10px solid #766774;border-top-color:#c4b6b6;border-left-color:#a497a2;border-right-color:#4b3e50;border-bottom-color:#3e3347;border-radius:38px;box-shadow:inset 0 0 0 2px #e4d6d255,inset 0 0 0 5px #1b1720,inset 5px 0 12px #08060caa,0 0 0 2px #15131b,1px 4px 0 3px #08060b,7px 9px 24px #0008}\r\n.device-bezel:before{content:'';position:absolute;inset:6px;border:1px solid #ab8e9c55;border-radius:22px;box-shadow:inset 1px 2px 3px #0008}\r\n.device-bezel:after{content:'';position:absolute;top:31%;bottom:31%;left:-10px;right:-10px;border-left:4px solid #342d39;border-right:4px solid #261f2d;border-radius:5px;box-shadow:inset 1px 0 #d3bfca55}\r\n.case-screw{position:absolute;width:10px;height:10px;border:1px solid #302633;border-radius:50%;background:linear-gradient(135deg,#e0d7c8,#8d8d86 45%,#545563);box-shadow:inset 0 1px #ffffde,1px 2px 3px #000b}.case-screw:after{content:'';position:absolute;height:2px;background:#3b3441;top:3px;left:1px;right:1px;transform:rotate(-35deg);box-shadow:0 1px #ded6be88}.case-tl{top:1px;left:7px}.case-tr{top:1px;right:7px}.case-bl{bottom:1px;left:7px}.case-br{bottom:1px;right:7px}\r\n.ship-header{margin:0 6px;border:2px solid #251e2d;border-top-color:#a38a9c;border-radius:20px 20px 7px 7px;height:auto;min-height:72px;padding:14px 12px;box-shadow:inset 0 2px #d4bac444,0 3px 5px #120d19aa}\r\n.pod-page{padding:0 14px 12px}.deck-strip{padding:0 4px}.containment-bay{border-radius:18px;margin-top:5px;box-shadow:inset 0 3px 8px #0a0611aa,0 1px #a2899d77}.workout-console{border-radius:13px;border:2px solid #1c1722;border-top-color:#b39aa8;box-shadow:inset 0 2px #c4adbe44,inset -2px -3px 7px #1b102955,0 5px 0 #211926,0 8px 12px #0e0a15aa}.coach-line{border:1px solid #201928;border-bottom-color:#9e839877;border-radius:8px;background:#221c2a;box-shadow:inset 1px 3px 6px #0008;padding:10px 12px}.crew-footer{margin-top:18px}.sync-badge{padding:12px 8px;font-size:11px}\r\n#coachDock.coach-dock{border-top:2px solid #bca4b4;box-shadow:inset 0 2px #ecdae344,0 -5px 12px #0e0a15aa;backdrop-filter:none}\r\n.launch-panel{max-height:calc(100dvh - 68px);border-width:6px;border-radius:24px;padding:24px 20px;box-shadow:inset 0 0 0 2px #251c30,inset 0 3px #f8e6d944,0 7px 0 #1e1725,5px 16px 40px #000b}.launch-panel::backdrop{background:#110d17d9}.launch-panel header{position:relative}.launch-panel:not(.accomplishments-panel) header:before{content:'';position:absolute;left:0;right:0;bottom:-5px;height:3px;background:repeating-linear-gradient(90deg,#16121d 0 5px,transparent 5px 10px);opacity:.7}.launch-panel .fields>label{min-width:0}.launch-panel input,.launch-panel select{min-width:0}.launch-panel .entry-copy{overflow-wrap:anywhere}\r\n.meal-scan-stage{position:relative;margin:18px 0;border:5px solid #252832;border-top-color:#7e8c8d;border-bottom-color:#13121c;border-radius:15px;overflow:hidden;isolation:isolate;background:#08171a;box-shadow:inset 0 0 14px #000,0 2px #acc5b866}\r\n.launch-panel .meal-scan-stage .meal-preview{height:300px;max-height:48dvh;min-height:230px;margin:0;border-radius:0;object-fit:contain;background:#0a1419}\r\n.meal-scan-overlay{position:absolute;inset:0;pointer-events:none;color:#c3ffec;font:12px/1.5 var(--pixel-font);text-shadow:0 1px 3px #000;background:linear-gradient(#00191dd9,transparent 25%,transparent 64%,#001519f2);box-shadow:inset 0 0 35px #47e6d31c}\r\n.scan-heading{position:absolute;top:12px;left:12px;right:12px;display:flex;justify-content:space-between;gap:8px;letter-spacing:1px}.scan-heading time{font-variant-numeric:tabular-nums;color:#e2ffb6;flex-shrink:0}.scan-readout{position:absolute;bottom:12px;left:12px;right:12px;background:#001b21d9;border-left:2px solid #9dffe1;padding:8px 10px}.scan-readout>span{display:block}.launch-panel .scan-readout progress{height:7px;margin-top:7px;accent-color:#a8ffc9}.scan-grid{position:absolute;inset:0;background:linear-gradient(90deg,#78fbe410 1px,transparent 1px),linear-gradient(#78fbe410 1px,transparent 1px);background-size:32px 32px;opacity:.45}.scan-reticle{position:absolute;inset:24% 20% 29%;border:1px solid #bbffe940;border-radius:16px;box-shadow:0 0 0 10px #60ecc709}.scan-reticle:before,.scan-reticle:after{content:'';position:absolute;width:23px;height:23px;border:3px solid #a3ffdf}.scan-reticle:before{left:-2px;top:-2px;border-right:0;border-bottom:0}.scan-reticle:after{right:-2px;bottom:-2px;border-left:0;border-top:0}.scan-beam{display:none;position:absolute;left:0;right:0;top:20%;height:2px;background:#b0ffe0;box-shadow:0 0 20px 7px #56ffd955}\r\n.meal-scan-overlay:is([data-state=preparing],[data-state=loading],[data-state=analyzing]) .scan-beam{display:block;animation:food-sweep 3.8s ease-in-out infinite}.meal-scan-overlay[data-state=result] .scan-reticle{border-color:#c5ffc777}.meal-scan-overlay[data-state=error]{color:#ffe4bf}.scan-actions{margin:16px 0}.scan-note{font-size:12px!important}.launch-panel #foodSuggestions button{text-transform:capitalize}\r\n@keyframes food-sweep{0%,100%{top:22%;opacity:.4}50%{top:73%;opacity:.8}}\r\n.reminder-tuning{padding:20px 16px 16px;border:3px solid #26202d;border-top-color:#b399a8;border-radius:13px;background:repeating-linear-gradient(0deg,#ecd5e306 0 1px,transparent 1px 3px),linear-gradient(130deg,#735d70,#3c3246);box-shadow:inset 0 2px #dbc6d944,0 4px #211929;margin:10px 0;display:grid;gap:24px}.reminder-tuning label{font:700 12px var(--pixel-font);letter-spacing:1px}.tone-control{text-align:center}.tone-control>output{display:block;font:750 24px var(--pixel-font);margin-top:16px}.tone-dial-row{display:flex;align-items:center;justify-content:center;gap:20px;margin-top:20px}.tone-dial-row>button{width:44px;min-width:44px;padding:0;font-size:22px}.tone-dial{position:relative;width:110px;height:110px;flex-shrink:0;border:3px solid #2b2330;border-radius:50%;background:repeating-conic-gradient(#b6a4ac 0deg 4deg,#68566a 4deg 8deg);box-shadow:0 0 0 5px #201926,0 0 0 7px #ae91a166,0 9px 9px #0009;touch-action:none;cursor:grab}.tone-dial:before{content:'';position:absolute;inset:8px;border:2px solid #c2afbc;border-radius:50%;background:radial-gradient(circle at 32% 22%,#b1a0ad,#79667f 55%,#4c3b59);box-shadow:inset 0 3px #e0cdd355,inset 0 -4px #21172f66}.tone-dial i{position:absolute;inset:12px;transform:rotate(var(--turn,0deg));transition:transform .15s}.tone-dial i:after{content:'';position:absolute;width:5px;height:21px;top:0;left:calc(50% - 2.5px);background:#ffdb95;border:1px solid #fce8be;border-radius:2px;box-shadow:0 1px 3px #000b}.tone-dial:focus-visible{outline:3px solid #ffe0a5;outline-offset:10px}.tone-dial:active{cursor:grabbing}.tone-scale{font:10px var(--pixel-font);color:#d3bacb;letter-spacing:.6px;margin-top:9px}.consistency-control output{display:block;font:600 16px/1.5 var(--pixel-font);margin-top:10px;min-height:24px}.launch-panel #consistencySlider{appearance:none;width:100%;height:46px;border:0;padding:0;margin:8px 0 0;background:none;box-shadow:none;cursor:pointer;touch-action:pan-y}.launch-panel #consistencySlider::-webkit-slider-runnable-track{height:10px;border:2px solid #1c1721;border-bottom-color:#b899ac;background:linear-gradient(90deg,#d4ad70 var(--fill),#1c1822 var(--fill));border-radius:4px;box-shadow:inset 1px 2px 4px #0009}.launch-panel #consistencySlider::-webkit-slider-thumb{appearance:none;width:30px;height:36px;margin-top:-15px;border:2px solid #dbbeb9;border-radius:5px;background:repeating-linear-gradient(90deg,#967c90 0 5px,#4b3e55 5px 6px);box-shadow:inset 0 2px #eddccf,2px 5px 5px #0008}.launch-panel #consistencySlider::-moz-range-track{height:8px;background:#211829;border:2px solid #b899ac;border-radius:4px}.launch-panel #consistencySlider::-moz-range-thumb{width:28px;height:34px;border:2px solid #dbbeb9;border-radius:5px;background:repeating-linear-gradient(90deg,#967c90 0 5px,#4b3e55 5px 6px);box-shadow:2px 4px 4px #0008}.cadence-scale{display:flex;justify-content:space-between;font:10px var(--pixel-font);color:#cdb5c8}.message-preview{padding:14px;border:2px solid #201827;border-bottom-color:#b28a9a;border-radius:7px;background:#24212b;box-shadow:inset 1px 3px 6px #0009}.message-preview small{font:10px var(--pixel-font);letter-spacing:1px;color:#b7d0bd}.launch-panel .message-preview p{font:15px/1.6 var(--pixel-font);color:#e7edc9;margin:7px 0 0}.reminder-controls{flex-wrap:wrap}\r\n.install-transfer{border-block:2px solid #241b2d;margin-top:22px;padding:0 0 24px;box-shadow:0 1px #b998a355}.install-qr{display:block;width:min(100%,294px);margin:18px auto;padding:0;background:#fff;border:7px solid #dfd8c8;border-radius:12px;box-shadow:0 4px 0 #756e78,0 8px 12px #0008;overflow:hidden}.install-qr img{display:block;width:100%;height:auto}.install-transfer .actions{margin-top:14px}.install-transfer #installLink{font:12px/1.5 var(--pixel-font)}\r\n@media(min-width:800px){body{margin-block:24px;padding:22px 14px 30px}.device-bezel{top:22px;bottom:22px}.ship-header{padding-inline:22px}.pod-page{padding-inline:24px}.reminder-tuning{grid-template-columns:1fr 1fr;align-items:center;gap:24px 22px}.message-preview{grid-column:1/-1}}\r\n@media(max-width:430px){body{width:calc(100% - 12px);padding:20px 7px max(22px,env(safe-area-inset-bottom));margin-block:8px}.device-bezel{width:calc(100% - 8px);top:6px;bottom:6px;border-width:7px;border-radius:29px}.device-bezel:before{inset:4px;border-radius:20px}.device-bezel:after{left:-7px;right:-7px;border-width:3px}.ship-header{margin-inline:3px;padding-inline:10px;gap:8px}.ship-location{font-size:8px;letter-spacing:.2px}.ship-location small{font-size:7px;letter-spacing:.4px}.mom-brand{gap:4px}.mom-brand img{width:28px}.pod-page{padding-inline:10px}.workout-console{padding:15px 10px}.calibration-grid{grid-template-columns:minmax(0,1fr) 78px;padding:15px 0;gap:8px}.exercise-control{padding-right:0}.dial-controls{gap:4px}.dial-controls button{width:28px;min-width:28px;padding:0}.exercise-dial{width:64px;height:64px;flex-shrink:0}.signal-control .rocker-switch{min-width:54px;width:54px}.signal-control>small{font-size:10px}.calibration-grid label{font-size:9px}.exercise-control>strong{font-size:15px}.launch-panel{width:calc(100% - 26px);padding:20px 12px;border-width:5px;border-radius:20px}.launch-panel h2{font-size:21px}.reminder-tuning{padding-inline:12px}.tone-dial-row{gap:18px}.scan-heading{font-size:10px}.launch-panel .meal-scan-stage .meal-preview{height:270px}.launch-panel .reminder-controls{gap:8px}}\r\n@media(prefers-reduced-motion:reduce){.scan-beam{animation:none!important;top:45%!important;opacity:.4}.tone-dial i{transition:none}}\r\n\n/* Keep the coach, camera and live count in one visible area. */\n.training-view{position:relative;border-radius:16px;background:#241b2d;padding-bottom:2px}.training-view #hud{display:flex;justify-content:center;align-items:center;margin:12px 4px 0;padding:10px 14px;border:2px solid #977b98;border-radius:12px;background:linear-gradient(#201525,#36263e);box-shadow:inset 0 2px 8px #0009,0 3px #140d1b;text-align:center}.training-view .counter-readout{width:100%;display:flex;flex-direction:column;align-items:center}.training-view #setNumber{display:block;margin:0 0 5px;font-size:14px;letter-spacing:1px}.training-view #primary{--tile-width:40px;--tile-height:54px;--digit-size:41px}.training-view .counter-target{margin-top:7px;justify-content:center}.training-view #primaryLabel,.training-view #goalValue{font-size:16px}.training-view .counter-target{font-size:16px}body[data-tracking=true] .training-view{position:sticky;top:4px;z-index:20;box-shadow:0 6px 18px #0c071dcc}body[data-tracking=true] .training-view .containment-pod{height:clamp(190px,32dvh,310px)}\n@media(max-height:580px){body[data-tracking=true] .training-view .containment-pod{height:clamp(110px,27dvh,180px)}.training-view #primary{--tile-height:44px;--tile-width:33px;--digit-size:33px}.training-view #hud{padding:6px;margin-top:8px}.training-view #setNumber{margin-bottom:3px}.training-view .counter-target{margin-top:4px}}\n","pod/cockpit-art.mjs":"// Hand-authored posture drawings used on cartridges and title teaching displays.\r\nconst head='<circle cx=\"32\" cy=\"10\" r=\"5\"/>';\r\nconst drawings={runner:`${head}<path d=\"m31 17-8 14 16 9-7 15m-8-25-10 8m16-20 14 10 10-4m-17 16 12 13\"/>`,cover:`${head}<path d=\"m31 17-7 16 16 4 12 15m-28-19-9 18m15-29 13 4 11-8M7 55h49\"/>`,dance:`${head}<path d=\"m32 18-2 18-13 18m13-18 16 18M31 22 17 15 9 4m22 18 14-7 10-11\"/>`,'plank-flight':'<circle cx=\"53\" cy=\"26\" r=\"5\"/><path d=\"m46 29-24 7-17 8m35-14 4 20h10M3 54h57\"/>'};\r\nexport function postureSVG(id){return `<svg viewBox=\"0 0 64 64\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${drawings[id]||drawings.runner}</svg>`;}\r\nexport function hardwareIcon(id){const paths={target:'<circle cx=\"12\" cy=\"12\" r=\"8\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M12 1v5m0 12v5M1 12h5m12 0h5\"/>',info:'<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 10v7m0-11v1\"/>',stop:'<path d=\"M5 5h14v14H5z\"/>',plus:'<path d=\"M12 4v16M4 12h16\"/>',minus:'<path d=\"M4 12h16\"/>',release:'<path d=\"M9 5H5v14h4m6-10 4 3-4 3M8 12h11\"/>',lock:'<rect x=\"5\" y=\"10\" width=\"14\" height=\"11\" rx=\"2\"/><path d=\"M8 10V6a4 4 0 0 1 8 0v4\"/>',motion:'<path d=\"M9 3h6v18H9zM4 7 1 12l3 5m16-10 3 5-3 5\"/>',service:'<path d=\"m3 8 9-5 9 5v13H3zM3 10h18M8 15h8m-4-3v6\"/>',play:'<path d=\"m8 4 13 8-13 8z\"/>',close:'<path d=\"m6 6 12 12M6 18 18 6\"/>',retry:'<path d=\"M4 11a8 8 0 1 1 2 7M4 4v7h7\"/>'};return `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">${paths[id]||paths.service}</svg>`;}\r\nexport function visorArt(family='upright'){return `<span class=\"cockpit-visor-object ${family}\" aria-hidden=\"true\"><i class=\"visor-hook\"></i><i class=\"visor-bridge\"></i><i class=\"visor-lens left\"></i><i class=\"visor-lens right\"></i><i class=\"visor-temple\"></i>${postureSVG(family==='plank'?'plank-flight':'runner')}</span>`;}\r\n","pod/cockpit-state.mjs":"// Unit-correct experimental menu release detector. Shared sensor remains untouched.\r\nexport function createPanelReleaseDetector({shake=false}={}) {\r\n let last=null,turnAt=null,turnSamples=0,peakAt=null,between=false,coolUntil=0,quietAt=null,armed=false;\r\n const clear=()=>{turnAt=null;turnSamples=0;peakAt=null;between=false;};\r\n return {reset(){last=null;armed=false;quietAt=null;clear();},read(sample,{suppressed=false}={}){\r\n  const {timeSeconds:t,angularSpeed:w,shake:a}=sample??{};\r\n  if(![t,w,a].every(Number.isFinite)||w<0||a<0||suppressed){last=null;armed=false;quietAt=null;clear();return false;}\r\n  const dt=last===null?null:t-last;last=t;\r\n  if(dt===null||dt<=0||dt>.100001){armed=false;quietAt=null;clear();return false;}\r\n  const quiet=w<1.6&&a<8;\r\n  if(!armed){if(quiet){quietAt??=t;if(t>=coolUntil&&t-quietAt>=.30)armed=true;}else quietAt=null;return false;}\r\n  let release=false;\r\n  if(w>3.2){turnAt??=t;turnSamples++;release=t-turnAt>=.25&&turnSamples>=3;}else{turnAt=null;turnSamples=0;}\r\n  if(shake){if(peakAt!==null&&t-peakAt>.60){peakAt=null;between=false;}if(a<8&&peakAt!==null)between=true;if(a>25){if(peakAt===null)peakAt=t;else if(between&&t-peakAt>=.10){release=true;}}}\r\n  if(release){coolUntil=t+1.2;armed=false;quietAt=null;clear();return true;}return false;\r\n }};\r\n}\r\n\r\n// Original silhouettes derived from the seven Coach World clans, not flight biomes.\r\nconst shape=(points,color,safe=[16,16,68,68])=>Object.freeze({points,color,safe:Object.freeze(safe),mask:`polygon(${points.split(' ').map(p=>p.split(',').map(v=>v+'%').join(' ')).join(',')})`});\r\nexport const CLAN_APERTURES=Object.freeze({\r\n neutral:shape('8,2 92,2 98,10 98,90 92,98 8,98 2,90 2,10','#b6c8cd'),\r\n chest:shape('2,18 15,12 23,2 38,7 50,2 62,7 77,2 85,12 98,18 94,82 77,98 23,98 6,82','#ed7855'),\r\n quads:shape('8,3 36,3 40,8 60,8 64,3 92,3 98,18 98,79 91,98 64,98 60,92 40,92 36,98 9,98 2,79 2,18','#d8b85e'),\r\n glutes:shape('14,3 35,2 50,8 65,2 86,3 98,18 95,42 98,65 90,91 71,98 50,94 29,98 10,91 2,65 5,42 2,18','#c579b6'),\r\n 'arms-shoulders':shape('2,3 30,3 35,10 65,10 70,3 98,3 98,31 91,37 94,78 82,98 18,98 6,78 9,37 2,31','#6b9ee8'),\r\n yoga:shape('3,18 13,5 32,2 46,7 70,2 94,8 98,22 94,76 84,94 64,98 48,92 26,98 5,91 2,76','#88c6ae'),\r\n 'martial-arts':shape('2,24 10,12 25,4 40,2 50,7 60,2 75,4 90,12 98,24 94,78 80,98 20,98 6,78','#d96564'),\r\n cardio:shape('18,2 39,2 43,7 57,7 61,2 82,2 98,20 98,40 93,45 93,55 98,60 98,80 82,98 18,98 2,80 2,60 7,55 7,45 2,40 2,20','#5fcdd1')\r\n});\r\nexport const clanAperture=faction=>CLAN_APERTURES[faction]??CLAN_APERTURES.neutral;\r\n","pod/cockpit.css":"/* Inner cockpit assembly. Universal rails remain the shell owner's stationary geometry. */\r\n#homeScreen.cockpit-mounted{--clan-color:#8d6ba2;position:relative;display:flex;flex-direction:column;gap:10px;min-height:0;height:100%;max-height:100%;padding:8px;box-sizing:border-box;overflow:hidden;isolation:isolate;background:transparent;color:var(--strip-dim)}\r\n.cockpit-mounted .pod-header-actions{display:none}\r\n.cockpit-identity-key{display:inline-grid;place-items:center;min-width:52px;min-height:64px;padding:6px;border:2px outset #ae94a7;border-radius:4px;background:linear-gradient(#8b7586,#514055);box-shadow:0 4px #211626,inset 0 1px #dbc1cd66;cursor:pointer}.cockpit-identity-key canvas{width:32px;height:48px;object-fit:contain}.cockpit-identity-key span{display:none}.cockpit-identity-key:active{transform:translateY(3px)}\r\n.cockpit-service-key,.cockpit-toggle-key{box-sizing:border-box;width:44px;height:44px;min-width:44px;min-height:44px;padding:9px;border:2px outset #ae94a7;border-radius:4px;background:linear-gradient(#8b7586,#514055);box-shadow:0 4px #211626,inset 0 1px #dbc1cd66;color:var(--strip-dim);cursor:pointer}\r\n.cockpit-toggle-key[aria-pressed=true]{box-shadow:0 1px #211626,inset 0 2px 5px #0008;translate:0 3px}\r\n.cockpit-mounted button:focus-visible{outline:3px solid #a9f7ee;outline-offset:2px}\r\n.cockpit-service-key svg,.cockpit-toggle-key svg{display:block;width:100%;height:100%;filter:drop-shadow(0 1px 0 #000)}\r\n.cockpit-service-key:active,.cockpit-toggle-key:active{box-shadow:0 1px #211626;translate:0 3px}\r\n.cockpit-announcement{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\r\n#homeScreen.cockpit-mounted #trainingView{position:relative;display:flex;flex:1 0 46%;min-height:180px;max-height:56%;margin:0;padding:0 39px;box-sizing:border-box;perspective:none}\r\n#homeScreen.cockpit-mounted .containment-bay{width:100%;min-height:0;margin:0;padding:0;transform:none}\r\n#homeScreen.cockpit-mounted #view{position:relative;width:100%;height:100%;min-height:170px;max-height:none;clip-path:var(--clan-mask);background-color:#132a35;image-rendering:auto}\r\n.cockpit-mounted .cockpit-clan-rim{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:12;overflow:visible}\r\n.cockpit-clan-rim polygon{fill:none;vector-effect:non-scaling-stroke;stroke-linejoin:round}.cockpit-clan-rim .rim-shadow{stroke:#03090d;stroke-width:18}.cockpit-clan-rim .rim-metal{stroke:#544352;stroke-width:12}.cockpit-clan-rim .rim-highlight{stroke:var(--clan-color,#8d6ba2);stroke-width:2;filter:drop-shadow(0 0 3px var(--clan-color,#8d6ba2))}\r\n#homeScreen.cockpit-mounted #view .coach-line{top:14%;left:13%;right:13%;border-color:#a8d9d233}\r\n#homeScreen.cockpit-mounted .home-base-window{position:absolute;left:0;right:0;bottom:0;width:100%;height:44%;object-fit:cover;object-position:center;image-rendering:pixelated;opacity:1;pointer-events:none}\r\n#homeScreen.cockpit-mounted #controls{display:grid;grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr);grid-template-rows:minmax(0,1fr);align-content:stretch;gap:10px;flex:1 1 44%;min-height:0;overflow:hidden;overscroll-behavior:contain;margin:0;padding:2px 1px 8px;perspective:640px;scrollbar-width:thin}\r\n.cockpit-mounted .cockpit-face{position:relative;min-width:0;height:fit-content;box-sizing:border-box;padding:12px 8px;border-radius:15px 15px 6px 6px;transform:rotateY(var(--look-x,0deg)) rotateX(var(--look-y,0deg));transform-origin:center top;transition:none}\r\n.cockpit-mounted :is(.cockpit-actuation,.cockpit-service-face,.cockpit-flight-strip){--screw:#cbbab0 0 1.4px,#57474f 1.9px 3px,#0000 3.5px;border:1px solid #0e0a12;background:radial-gradient(circle at 7px 7px,var(--screw)),radial-gradient(circle at calc(100% - 7px) 7px,var(--screw)),radial-gradient(circle at 7px calc(100% - 7px),var(--screw)),radial-gradient(circle at calc(100% - 7px) calc(100% - 7px),var(--screw)),repeating-linear-gradient(90deg,#0000 0 3px,#ffffff05 3px 4px),linear-gradient(165deg,#5d4a5b,#3c3043 50%,#2e2436);box-shadow:inset 0 1px #dcc6d066,inset 0 -2px #0007,0 0 0 1px #8c768966,0 4px 0 #140f19,0 7px 12px #0006}\r\n.cockpit-mounted[data-cockpit-state=locked] .cockpit-face,.cockpit-mounted[data-cockpit-suspended=true] .cockpit-face{transform:none!important}\r\n.cockpit-mounted .cockpit-face[data-locked=true]{outline:2px solid var(--clan-color);outline-offset:-3px}\r\n.cockpit-mounted .cockpit-actuation{display:flex;flex-direction:column;gap:8px}\r\n.cockpit-mounted .cockpit-actuation>.deck-strip{display:flex;flex-wrap:wrap;justify-content:center;height:auto;font-size:11px;letter-spacing:0;margin:0;padding:0;border:0;background:none}.cockpit-mounted .deck-strip>span:first-child{display:none}\r\n.cockpit-mounted #levelBadge{padding:7px}\r\n.cockpit-mounted .cockpit-actuation>.simple-counter{margin:0}.cockpit-mounted .counter-readout{padding:8px;min-height:0}\r\n.cockpit-mounted .cockpit-actuation .set-actions{display:flex;flex-wrap:wrap}.cockpit-mounted #controls #start{min-height:54px;min-width:44px}.cockpit-mounted #controls #stop{min-height:54px}\r\n.cockpit-mounted .cockpit-actuation>p{font-size:12px;line-height:1.4;margin:0;padding:6px}\r\n.cockpit-service-host{display:flex;flex-wrap:wrap;gap:6px;min-width:0}.cockpit-service-host>[data-service-board]{flex:1 0 100%}\r\n.cockpit-mounted #controls>#podFullWorkout{grid-column:1/-1;margin:0}.cockpit-mounted #controls>#podFullWorkout>.fw-menu{max-width:100%;overflow:auto}\r\n.cockpit-visors{position:absolute;inset:0;pointer-events:none;z-index:16;display:flex;justify-content:space-between;align-items:center}\r\n.cockpit-visor{position:relative;pointer-events:auto;width:50px;min-width:44px;height:138px;padding:0;border:0;background:none;box-shadow:none;perspective:350px;cursor:pointer}\r\n.cockpit-visor-object{position:relative;display:block;width:48px;height:95px;transform:rotateY(22deg) rotateZ(-8deg);transform-style:preserve-3d;animation:visor-hang 5s ease-in-out infinite;filter:drop-shadow(3px 7px 3px #0009)}\r\n.cockpit-visor-object.plank{transform:rotateY(-22deg) rotateZ(8deg);animation-delay:-2s}.visor-hook{position:absolute;left:23px;bottom:76px;height:65px;width:4px;background:linear-gradient(90deg,#263843,#bfced1,#344550);border-radius:4px;box-shadow:2px 0 #0008}\r\n.visor-lens{position:absolute;top:23px;width:21px;height:34px;box-sizing:border-box;border:4px ridge #b8c6c8;border-radius:10px 5px 12px 8px;background:linear-gradient(130deg,#d5ffeacc,#338b9777 30%,#122638 60%,#5ed7ed88);box-shadow:inset 2px 3px 4px #0008,2px 4px #09131c}\r\n.visor-lens.left{left:0}.visor-lens.right{right:0;transform:scaleX(-1)}.visor-bridge{position:absolute;left:17px;top:31px;width:14px;height:8px;background:linear-gradient(#d2e0d9,#4d6371);z-index:2;border-radius:5px}.visor-temple{position:absolute;left:3px;right:3px;top:18px;height:39px;border:3px solid #526879;border-bottom:0;border-radius:50%;transform:translateZ(-10px) rotateX(35deg)}\r\n.plank .visor-lens{top:31px;height:20px;border-color:#c7b181;border-radius:4px;background:linear-gradient(150deg,#ffefb3,#96722c55 35%,#182337 70%)}.plank .visor-bridge{top:32px}.cockpit-visor-object>svg{position:absolute;left:8px;top:62px;width:34px;height:34px;color:#d7eadb;filter:drop-shadow(0 2px #000)}\r\n.cockpit-visor.is-picked .cockpit-visor-object{animation:visor-pick .5s both}.cockpit-visor:disabled{opacity:.4}\r\n@keyframes visor-hang{0%,100%{translate:0 0;rotate:0deg}50%{translate:0 3px;rotate:3deg}}@keyframes visor-pick{to{transform:translateZ(150px) rotateY(0);opacity:0}}\r\n@media(max-width:360px){#homeScreen.cockpit-mounted{gap:6px;padding:4px}#homeScreen.cockpit-mounted #trainingView{padding:0 35px}#homeScreen.cockpit-mounted #controls{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:5px}.cockpit-mounted .cockpit-face{padding:10px 5px}.cockpit-mounted #view .coach-line{font-size:11px}}\r\n@media(orientation:landscape) and (max-height:600px){#homeScreen.cockpit-mounted{display:grid;grid-template-columns:1.1fr 1fr;grid-template-rows:auto minmax(0,1fr)}#homeScreen.cockpit-mounted #trainingView{height:100%;max-height:none;min-height:0;grid-row:1/3}.cockpit-mounted .cockpit-flight-strip{grid-column:2;grid-row:1;margin:0}#homeScreen.cockpit-mounted #controls{grid-column:2;grid-row:2;height:100%;min-height:0}.cockpit-mounted #view{min-height:0!important}}\r\n@media(prefers-reduced-motion:reduce){.cockpit-mounted .cockpit-face,.cockpit-visor-object{animation:none!important;transform:none!important;transition:none!important}}\r\n\r\n\r\n/* Shell well has a definite height; never inherit auto-height from the document. */\r\nhtml.shell-ready #homeScreen.cockpit-mounted{position:fixed!important;inset:var(--shell-top) var(--shell-right) var(--shell-bottom) var(--shell-left)!important;height:var(--shell-well-height)!important;min-height:0!important;max-height:var(--shell-well-height)!important;margin:0!important;padding:8px!important;width:calc(100vw - var(--shell-left) - var(--shell-right))!important;overflow:hidden!important}\r\n#homeScreen.cockpit-mounted #trainingView{flex:0 0 49%;height:49%;min-height:0;max-height:49%}\r\n#homeScreen.cockpit-mounted .containment-bay,#homeScreen.cockpit-mounted #view{min-height:0!important}\r\n#homeScreen.cockpit-mounted #controls{flex:1 1 0;min-height:0}\r\n.cockpit-mounted .control-board .cockpit-cartridge[data-game=runner]{order:1}.cockpit-mounted .control-board .cockpit-cartridge[data-game=cover]{order:2}.cockpit-mounted .control-board .cockpit-cartridge[data-game=dance]{order:3}.cockpit-mounted .control-board .cockpit-cartridge[data-game=plank-flight]{order:4}\r\n.cockpit-mounted .control-board .cb-part{padding-top:4px}\r\n@media(orientation:landscape) and (max-height:600px){#homeScreen.cockpit-mounted #trainingView{height:100%;max-height:100%;flex:none}}\r\n\r\n\r\n.cockpit-mounted .cockpit-actuation #podFullWorkout{position:relative;width:100%;max-width:100%;margin:0}\r\n.cockpit-mounted .cockpit-actuation #podFullWorkout>.fw-menu{position:relative!important;inset:auto!important;top:auto!important;left:auto!important;margin:6px 0 0!important;width:100%!important;max-width:100%!important;height:auto!important;max-height:none!important;box-sizing:border-box;overflow:auto}\r\n\r\n#homeScreen.cockpit-mounted .home-base-window{top:auto!important;bottom:0!important;display:block!important}\r\n.cockpit-mounted .cockpit-action-key>svg{display:block;width:28px;height:28px;margin:auto;filter:drop-shadow(0 2px #0008)}\r\n.cockpit-mounted .control-board .cb-switch{font-size:0!important}\r\n.cockpit-mounted .control-board .cb-lever .cockpit-action-key svg{width:17px;height:17px;margin:0}\r\n.cockpit-mounted .control-board .cb-lever #easier.cockpit-action-key svg{align-self:flex-end}\r\n\r\n.cockpit-mounted .cockpit-semantic-name{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\r\n.cockpit-mounted .cockpit-disclosure-key>svg{display:block;width:26px;height:26px;margin:auto;color:var(--portal-strip);filter:drop-shadow(0 2px #000)}\r\n.cockpit-mounted .cockpit-disclosure-key{min-width:44px;min-height:44px}\r\n.cockpit-mounted .cockpit-disclosure-key:before,.cockpit-mounted .cockpit-disclosure-key:after{display:none!important}\r\n\r\n/* R3-03 one text colour: free text on the plum plate is the lightened strip tint (>=4.5:1). Engraved plates, the switchboard, FOCUS/LEVEL labels and the DJ glass keep their baseline inks. */\r\n#homeScreen.cockpit-mounted :is(#variationHint,#trackingScope,#syncBadge,.sync-badge,.cockpit-actuation>p,.deck-strip,.deck-strip *){color:var(--strip-dim)}\r\nhtml.shell-ready .sync-badge{color:var(--strip-dim)}\r\n\r\n/* R3-06 triangle cockpit. Window on top (>=45% of the well), flight strip under it, two angled lower bays. Same plum plate, screws and caps as the baseline; only the layout and perspective are new. The well stays transparent so the nebula shows in every gap. */\r\n.cockpit-mounted .cockpit-flight-strip{position:relative;z-index:6;flex:0 0 auto;display:flex;align-items:center;justify-content:center;box-sizing:border-box;min-height:46px;margin:0 20px;padding:3px 12px;border-radius:3px 3px 18px 18px}\r\n.cockpit-mounted .cockpit-flight-strip .deck-strip{position:relative;flex:1;justify-content:center;height:auto;min-height:0;margin:0;padding:0}\r\n.cockpit-mounted .cockpit-flight-strip .deck-strip>#levelBadge{display:block!important;margin-left:8px;padding:4px 6px;font:700 10px/1.2 var(--font,system-ui);letter-spacing:.08em;border:1px solid #14101a;border-radius:3px;background:#150f19;box-shadow:inset 0 2px 4px #000c}\r\n.cockpit-mounted .cockpit-bay{min-width:0;min-height:0;height:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:8px;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;transition:transform .28s cubic-bezier(.3,1.3,.5,1);will-change:transform}\r\n.cockpit-mounted .cockpit-bay-left{transform:rotateY(22deg);transform-origin:left center}\r\n.cockpit-mounted .cockpit-console{transform:rotateY(-16deg);transform-origin:right center}\r\n.cockpit-mounted .cockpit-service-face{flex:1 0 auto;display:flex;flex-direction:column;gap:8px;min-width:0;box-sizing:border-box}\r\n.cockpit-mounted .cockpit-console>.cockpit-face{flex:none;margin:0}\r\n.cockpit-mounted .cockpit-service-host{justify-content:center}\r\n@media(orientation:landscape) and (max-height:600px){.cockpit-mounted .cockpit-bay-left{transform:rotateY(10deg)}.cockpit-mounted .cockpit-console{transform:rotateY(-8deg)}}\r\n/* Tap a bay and it is pulled forward: the r2 lock machine sets data-cockpit-panel (lock) then data-cockpit-expanded (after the tap). */\r\n.cockpit-mounted[data-cockpit-panel=service] .cockpit-bay-left,.cockpit-mounted:is([data-cockpit-panel=selector],[data-cockpit-panel=actuation]) .cockpit-console{transform:none;z-index:3}\r\n.cockpit-mounted[data-cockpit-expanded] .cockpit-bay{grid-column:1/-1;transform:none}\r\n.cockpit-mounted:is([data-cockpit-expanded=selector],[data-cockpit-expanded=actuation]) .cockpit-bay-left{grid-row:2}\r\n#homeScreen.cockpit-mounted[data-cockpit-expanded] #controls{overflow:auto;grid-template-rows:none;grid-auto-rows:min-content;align-content:start}\r\n.cockpit-mounted[data-cockpit-expanded] .cockpit-bay{height:auto;min-height:min-content;overflow:visible}\r\n@media(prefers-reduced-motion:reduce){.cockpit-mounted .cockpit-bay{transition:none}}\r\n/* R99: in the unpulled console bay the Full Workout hint squeezes to a 6px letter column; show it only when the bay is pulled forward. */\r\n.cockpit-mounted:not([data-cockpit-expanded]) .cockpit-console .fw-pw-l .fw-count{display:none}\r\n","pod/cockpit.mjs":"import {subscribePhoneMotion,requestPhoneMotion} from '../character-phone-sensor.mjs';\r\nimport {clanAperture,createPanelReleaseDetector} from './cockpit-state.mjs';\r\nimport {hardwareIcon,postureSVG,visorArt} from './cockpit-art.mjs';\r\nimport {launchGameEntry,preloadGameEntry} from './game-entry.mjs';\r\nimport {shellEnabled} from '../modules/visual-flags.mjs';\r\nconst BUTTONS={runner:'coolCardio',cover:'coverCrisis',dance:'openDance','plank-flight':'plankFlight'};\r\nlet lastUpright='runner',mounted=null;\r\nconst CHOICE='myr5-cockpit-upright-choice';\r\nexport function rememberGameChoice(id){if(['runner','cover','dance'].includes(id)){lastUpright=id;try{sessionStorage.setItem(CHOICE,id);}catch{/* In-memory choice still works with blocked storage. */}}}\r\nexport function clearGameChoice(){lastUpright='runner';try{sessionStorage.removeItem(CHOICE);}catch{}}\r\nexport function cockpitGameChoice(family='upright'){return family==='plank'?'plank-flight':lastUpright;}\r\nfunction stylesheet(name){const href=new URL('/pod/'+name.replace(/^\\.\\//,''),location.origin).href;if(!document.querySelector(`link[href=\"${href}\"]`)){const link=document.createElement('link');link.rel='stylesheet';link.href=href;document.head.append(link);}}\r\n\r\n/** Mount once, preserving original nodes/listeners. Only inner faces ever transform. */\r\nexport function mountCockpit({home=document.getElementById('homeScreen'),t=(_key,fallback)=>fallback,onService}={}){\r\n if(mounted)return mounted;if(!home||!shellEnabled(home.ownerDocument.defaultView))return null;\r\n try{const saved=sessionStorage.getItem(CHOICE);if(['runner','cover','dance'].includes(saved))lastUpright=saved;}catch{}\r\n const view=home.querySelector('#view'),training=home.querySelector('#trainingView'),controls=home.querySelector('#controls'),board=controls?.querySelector('.control-board');\r\n if(!view||!training||!controls||!board)return null;\r\n stylesheet('./cockpit.css');const abort=new AbortController(),signal=abort.signal,restores=[];\r\n const move=(node,parent)=>{const marker=document.createComment('cockpit return');node.before(marker);parent.append(node);restores.push(()=>{marker.replaceWith(node);});};\r\n home.classList.add('cockpit-mounted');home.dataset.cockpitState='explore';\r\n const toolbar=document.createElement('div');toolbar.className='cockpit-panel-tools';toolbar.innerHTML=`<button type=\"button\" data-release aria-label=\"${t('cockpit.release','Release panel')}\">${hardwareIcon('release')}</button><span class=\"cockpit-announcement\" role=\"status\" aria-live=\"polite\"></span>`;home.prepend(toolbar);\r\n const actuation=document.createElement('section');actuation.className='cockpit-actuation cockpit-face';actuation.dataset.panel='actuation';actuation.setAttribute('aria-label',t('cockpit.actuation','Pod actuation and service'));controls.prepend(actuation);\r\n for(const child of [...controls.children])if(child!==board&&child!==actuation)move(child,actuation);\r\n // R3-06 triangle: flight strip under the window (deck status), then two angled bays: service (left) and the workout console (right: movement keys, dial + knob, switchboard).\r\n const strip=document.createElement('div');strip.className='cockpit-flight-strip';strip.setAttribute('role','group');strip.setAttribute('aria-label',t('cockpit.strip','Flight status strip'));training.after(strip);\r\n const deck=home.querySelector('.deck-strip');if(deck)move(deck,strip);\r\n const hud=training.querySelector('#hud');if(hud)move(hud,actuation);\r\n board.classList.add('cockpit-face');board.dataset.panel='selector';training.dataset.panel='viewport';\r\n const bayLeft=document.createElement('div'),serviceFace=document.createElement('section'),consoleBay=document.createElement('div');\r\n bayLeft.className='cockpit-bay cockpit-bay-left';serviceFace.className='cockpit-service-face cockpit-face';serviceFace.dataset.panel='service';serviceFace.setAttribute('aria-label',t('cockpit.serviceBay','Pod and service bay'));bayLeft.append(serviceFace);\r\n consoleBay.className='cockpit-bay cockpit-console';consoleBay.setAttribute('role','group');consoleBay.setAttribute('aria-label',t('cockpit.console','Workout console'));\r\n controls.prepend(bayLeft,consoleBay);move(board,consoleBay);consoleBay.append(actuation);\r\n const serviceHost=document.createElement('div');serviceHost.className='cockpit-service-host';serviceFace.append(serviceHost);\r\n const identityKey=document.getElementById('openIdentity');if(identityKey){move(identityKey,serviceHost);identityKey.setAttribute('aria-label',t('cockpit.identity','Open avatar and hand editor'));identityKey.classList.add('cockpit-identity-key');}\r\n const motionKey=document.createElement('button'),shakeKey=document.createElement('button');for(const [key,name,icon,label] of [[motionKey,'motion','motion',t('cockpit.motion','Enable experimental menu motion')],[shakeKey,'shake','retry',t('cockpit.shake','Enable experimental shake release')]]){key.type='button';key.className='cockpit-toggle-key';key.dataset[name]='';key.setAttribute('aria-pressed','false');key.setAttribute('aria-label',label);key.innerHTML=hardwareIcon(icon);serviceHost.append(key);}\r\n const service=document.createElement('button');service.type='button';service.className='cockpit-service-key';service.setAttribute('aria-label',t('cockpit.service','Open service board'));service.innerHTML=hardwareIcon('service');serviceHost.append(service);\r\n service.addEventListener('click',()=>{if(onService)onService(serviceHost);else {const button=document.querySelector('#barPortal, .coach-dock [data-route=\"portal\"], #portalMenuButton');if(button)button.click();else document.getElementById('openSettings')?.click();}},{signal});\r\n // Visual rim only: no mask is applied to camera, character or operable content.\r\n const rim=document.createElementNS('http://www.w3.org/2000/svg','svg');rim.classList.add('cockpit-clan-rim');rim.setAttribute('viewBox','0 0 100 100');rim.setAttribute('preserveAspectRatio','none');rim.setAttribute('aria-hidden','true');rim.innerHTML='<polygon class=\"rim-shadow\"/><polygon class=\"rim-metal\"/><polygon class=\"rim-highlight\"/>';view.append(rim);\r\n const identity=faction=>{const shape=clanAperture(faction);home.dataset.cockpitClan=shape===clanAperture(null)?'neutral':faction;home.style.setProperty('--clan-color',shape.color);for(const p of rim.children)p.setAttribute('points',shape.points);view.style.setProperty('--clan-mask',shape.mask);view.dataset.safeRect=shape.safe.join(',');};\r\n identity(view.querySelector('[data-base-faction]')?.dataset.baseFaction||home.querySelector('[data-base-faction]')?.dataset.baseFaction);\r\n globalThis.addEventListener('myr5:hub-base',e=>identity(e.detail?.saved?e.detail.faction:null),{signal});\r\n const visors=document.createElement('div');visors.className='cockpit-visors';visors.innerHTML=`<button type=\"button\" class=\"cockpit-visor\" data-family=\"upright\" aria-label=\"${t('cockpit.upright','Pick up standing-game visor')}\">${visorArt()}</button><button type=\"button\" class=\"cockpit-visor\" data-family=\"plank\" aria-label=\"${t('cockpit.plank','Pick up plank-game visor')}\">${visorArt('plank')}</button>`;training.append(visors);\r\n const keyRestores=[];for(const [id,key] of Object.entries(BUTTONS)){const button=document.getElementById(key);if(!button)continue;const old=[...button.childNodes],gameIcon=document.createElement('span');gameIcon.className='cockpit-cartridge-art';gameIcon.innerHTML=postureSVG(id);button.replaceChildren(gameIcon);button.classList.add('cockpit-cartridge');button.dataset.game=id;button.dataset.family=id==='plank-flight'?'plank':'upright';keyRestores.push(()=>{button.replaceChildren(...old);button.classList.remove('cockpit-cartridge');delete button.dataset.game;delete button.dataset.family;});button.addEventListener('click',()=>{rememberGameChoice(id);syncChoice();},{signal});}\r\n function syncChoice(){for(const [id,key] of Object.entries(BUTTONS)){const el=document.getElementById(key);if(el)el.dataset.selected=String(id===lastUpright||id==='plank-flight');}visors.querySelector('[data-family=upright]').dataset.game=lastUpright;}\r\n // Keep semantic names while the real metal keys show only their action marks.\r\n for(const [selector,icon,label] of [['#openLibrary','service','Open movement library'],['#start','play','Begin set'],['#stop','stop','Stop set'],['#harder','plus','Increase difficulty'],['#easier','minus','Decrease difficulty']]){const key=home.querySelector(selector);if(!key)continue;const nodes=[...key.childNodes],oldLabel=key.getAttribute('aria-label');key.setAttribute('aria-label',oldLabel||t(`cockpit.${icon}`,label));key.replaceChildren();key.insertAdjacentHTML('beforeend',hardwareIcon(icon));key.classList.add('cockpit-action-key');keyRestores.push(()=>{key.replaceChildren(...nodes);key.classList.remove('cockpit-action-key');if(oldLabel===null)key.removeAttribute('aria-label');else key.setAttribute('aria-label',oldLabel);});}\r\n for(const [selector,icon,label] of [['#podGoals>summary','target','Goals'],['.movement-scope>summary','info','Tracking details']]){const key=home.querySelector(selector);if(!key)continue;const nodes=[...key.childNodes],oldLabel=key.getAttribute('aria-label');key.setAttribute('aria-label',oldLabel||t(`cockpit.${icon}`,label));for(const node of nodes)if(node.nodeType===3){const name=document.createElement('span');name.className='cockpit-semantic-name';node.replaceWith(name);name.append(node);}key.insertAdjacentHTML('afterbegin',hardwareIcon(icon));key.classList.add('cockpit-disclosure-key');keyRestores.push(()=>{key.replaceChildren(...nodes);key.classList.remove('cockpit-disclosure-key');if(oldLabel===null)key.removeAttribute('aria-label');else key.setAttribute('aria-label',oldLabel);});}\r\n syncChoice();visors.addEventListener('click',e=>{const key=e.target.closest('[data-family]');if(!key)return;const button=document.getElementById(BUTTONS[cockpitGameChoice(key.dataset.family)]);if(button&&!button.disabled){key.classList.add('is-picked');button.click();setTimeout(()=>key.classList.remove('is-picked'),600);}},{signal});\r\n let locked=null,focusBefore=null,exercise=false,motion=false,shake=false,unsubscribe=null,interacting=false,suppressUntil=0,disposed=false,restoringFocus=false,entry=null,bypassing=false;\r\n // Capture before the existing target listeners; the bypass includes Cover's\r\n // synchronous nested runner click without allowing a second title to open.\r\n document.addEventListener('click',event=>{const button=event.target.closest?.('button');const id=Object.keys(BUTTONS).find(id=>BUTTONS[id]===button?.id);if(!id||bypassing||button.disabled||busy())return;event.preventDefault();event.stopImmediatePropagation();rememberGameChoice(id);syncChoice();entry=launchGameEntry({id,trigger:button,t,load:options=>preloadGameEntry(id,options),onReady(){if(disposed||button.disabled||busy())return;bypassing=true;try{button.click();}finally{bypassing=false;}entry=null;},onCancel(){entry=null;}});},{capture:true,signal});\r\n let detector=createPanelReleaseDetector();const releaseKey=toolbar.querySelector('[data-release]'),announce=toolbar.querySelector('[role=status]');releaseKey.disabled=true;\r\n const busy=()=>exercise||document.body.dataset.tracking==='true'||document.documentElement.dataset.gameActive==='true';\r\n const modal=()=>!!document.querySelector('dialog[open]');\r\n function lock(panel){if(disposed||busy())return false;const face=typeof panel==='string'?home.querySelector(`[data-panel=\"${panel}\"]`):panel;if(!face)return false;if(locked===face)return true;focusBefore=document.activeElement;locked?.removeAttribute('data-locked');locked=face;face.dataset.locked='true';face.style.removeProperty('--look-x');face.style.removeProperty('--look-y');home.dataset.cockpitState='locked';home.dataset.cockpitPanel=face.dataset.panel;releaseKey.disabled=false;detector.reset();announce.textContent=t('cockpit.locked','Panel locked. Release control returns to exploration.');globalThis.dispatchEvent(new CustomEvent('myr5:cockpit-lock',{detail:{panel:face.dataset.panel,locked:true}}));return true;}\r\n function release(){if(disposed||busy()||modal())return false;locked?.removeAttribute('data-locked');locked=null;home.dataset.cockpitState='explore';delete home.dataset.cockpitPanel;delete home.dataset.cockpitExpanded;releaseKey.disabled=true;detector.reset();announce.textContent=t('cockpit.released','Exploration restored.');globalThis.dispatchEvent(new CustomEvent('myr5:cockpit-lock',{detail:{locked:false}}));restoringFocus=true;try{if(focusBefore?.isConnected)focusBefore.focus({preventScroll:true});}finally{restoringFocus=false;}return true;}\r\n home.addEventListener('pointerdown',e=>{if(e.target.closest('.cockpit-panel-tools,.cockpit-toggle-key'))return;const face=e.target.closest('[data-panel]');if(face)lock(face);interacting=true;suppressUntil=performance.now()+600;detector.reset();},{capture:true,signal});\r\n function expandLocked(){if(disposed||!locked||busy())return;home.dataset.cockpitExpanded=locked.dataset.panel;globalThis.dispatchEvent(new Event('resize'));}\r\n for(const event of ['pointerup','pointercancel'])globalThis.addEventListener(event,()=>{interacting=false;suppressUntil=performance.now()+600;detector.reset();requestAnimationFrame(expandLocked);},{signal});\r\n home.addEventListener('focusin',e=>{if(e.target.closest('.cockpit-panel-tools,.cockpit-toggle-key'))return;const face=e.target.closest('[data-panel]');if(face&&!restoringFocus){lock(face);if(!interacting)requestAnimationFrame(expandLocked);}},{signal});\r\n for(const event of ['wheel','scroll','input','compositionstart','compositionend','transitionrun','transitionend'])home.addEventListener(event,()=>{suppressUntil=performance.now()+600;detector.reset();},{capture:true,passive:true,signal});\r\n releaseKey.addEventListener('click',release,{signal});home.addEventListener('keydown',e=>{if(e.key==='Escape'&&locked&&!modal()){if(release())e.preventDefault();}},{signal});\r\n function subscribe(){unsubscribe?.();unsubscribe=null;detector.reset();if(!motion||document.hidden||busy()||modal()||home.hidden||home.inert||!home.getClientRects().length||matchMedia('(prefers-reduced-motion: reduce)').matches)return;unsubscribe=subscribePhoneMotion(sample=>{const typing=document.activeElement?.matches('input,textarea,select,[contenteditable=true]');const suppressed=interacting||typing||modal()||busy()||document.hidden||performance.now()<suppressUntil||home.hidden||!home.getClientRects().length;\r\n  if(locked){if(detector.read(sample,{suppressed}))release();}else if(!suppressed&&!matchMedia('(prefers-reduced-motion: reduce)').matches){const x=Math.max(-1,Math.min(1,sample.gx)),y=Math.max(-1,Math.min(1,sample.gy-1));for(const face of [board,actuation,serviceFace]){face.style.setProperty('--look-x',`${x*2}deg`);face.style.setProperty('--look-y',`${y*2}deg`);}}\r\n });}\r\n motionKey.addEventListener('click',async e=>{if(busy())return;const button=e.currentTarget;if(motion){motion=false;button.setAttribute('aria-pressed','false');subscribe();return;}const granted=await requestPhoneMotion();if(disposed)return;motion=granted;button.setAttribute('aria-pressed',String(motion));announce.textContent=granted?t('cockpit.motionEnabled','Experimental menu motion enabled. Release is always available.'):t('cockpit.motionDenied','Motion unavailable. Use Release panel.');subscribe();},{signal});\r\n shakeKey.addEventListener('click',e=>{if(busy())return;shake=!shake;e.currentTarget.setAttribute('aria-pressed',String(shake));detector=createPanelReleaseDetector({shake});},{signal});\r\n function setExerciseActive(active){exercise=!!active;home.dataset.cockpitSuspended=String(busy());releaseKey.disabled=busy()||!locked;subscribe();}\r\n globalThis.addEventListener('myr5:cockpit-exercise',e=>setExerciseActive(e.detail?.active),{signal});\r\n const trackingObserver=new MutationObserver(()=>setExerciseActive(exercise));trackingObserver.observe(document.body,{attributes:true,attributeFilter:['data-tracking']});trackingObserver.observe(document.documentElement,{attributes:true,attributeFilter:['data-game-active']});\r\n const homeVisibility=new MutationObserver(subscribe);homeVisibility.observe(home,{attributes:true,attributeFilter:['hidden','inert']});\r\n for(const event of ['toggle','close'])document.addEventListener(event,subscribe,{capture:true,signal});\r\n // App startup may attach the real circuit after the launch shell. Move that\r\n // one owner intact; its resize handler recalculates socket and cable geometry.\r\n const lateControls=new MutationObserver(records=>{let changed=false;for(const record of records)for(const child of record.addedNodes){if(child.nodeType===1&&child.parentNode===controls&&child!==bayLeft&&child!==consoleBay){move(child,actuation);changed=true;}}if(changed)requestAnimationFrame(()=>{if(!disposed)globalThis.dispatchEvent(new Event('resize'));});});lateControls.observe(controls,{childList:true});\r\n for(const event of ['visibilitychange'])document.addEventListener(event,()=>{detector.reset();subscribe();},{signal});\r\n const ownerKey=()=>{const a=globalThis.myr5AuthenticatedAccount;return a?.user?.id?`${a.user.id}:${a.dataEpoch}`:'guest';};let choiceOwner=ownerKey();\r\n for(const event of ['orientationchange','myr5:auth-transition','myr5:account-ready','myr5:login-ready'])globalThis.addEventListener(event,()=>{detector.reset();const next=ownerKey();if(event==='myr5:auth-transition'||next!==choiceOwner){choiceOwner=next;entry?.dispose();entry=null;clearGameChoice();syncChoice();identity(null);}},{signal});\r\n mounted={lock,release,setExerciseActive,serviceHost,dispose(){if(disposed)return;entry?.dispose();disposed=true;unsubscribe?.();trackingObserver.disconnect();homeVisibility.disconnect();lateControls.disconnect();abort.abort();rim.remove();visors.remove();toolbar.remove();serviceHost.remove();keyRestores.forEach(fn=>fn());restores.reverse().forEach(fn=>fn());actuation.remove();bayLeft.remove();consoleBay.remove();strip.remove();board.classList.remove('cockpit-face');delete board.dataset.panel;delete training.dataset.panel;home.classList.remove('cockpit-mounted');for(const key of ['cockpitState','cockpitPanel','cockpitClan','cockpitSuspended','cockpitExpanded'])delete home.dataset[key];mounted=null;}};return mounted;\r\n}\r\n","pod/continue-workout.mjs":"// #19 (revised): today's newest paused/interrupted workout is offered as a Continue popup on the pod page\r\n// itself, never on the portal quilt. The popup lives inside #homeScreen, so it travels with the pod when the\r\n// portal hosts it in its full-screen workout dialog and is never left outside a modal (inert).\r\nexport function podShown(doc=document){\r\n const home=doc.getElementById('homeScreen');if(!home||home.hidden||doc.hidden||doc.body.dataset.screen==='rest')return false;\r\n const host=home.closest('dialog');if(host)return host.open;\r\n return doc.getElementById('portalHome')?.hidden!==false;\r\n}\r\nexport const continueVisible=({shown,idle,row,dismissed})=>!!row&&shown&&idle&&dismissed!==row.id;\r\nexport function continuePrompt(row,label,access){\r\n const legacyManual=row.metadata?.control==='manual';\r\n return {allowed:access.allowed,text:access.allowed?(legacyManual?'Saved manual set · Start with camera · ':'Continue · ')+label:`Saved ${label}. ${access.reason||'This workout path is locked.'} Your set remains in history.`,action:legacyManual?'Start with camera':'Continue'};\r\n}\r\nexport function mountContinueWorkout({doc=document,unfinished,idle,label,onContinue,canContinue=()=>({allowed:true})}){\r\n const home=doc.getElementById('homeScreen');\r\n const card=doc.createElement('section');card.className='continue-workout';card.hidden=true;card.setAttribute('role','dialog');card.setAttribute('aria-labelledby','continueWorkoutTitle');\r\n card.innerHTML='<p class=\"continue-eyebrow\">UNFINISHED SET</p><h2 id=\"continueWorkoutTitle\"></h2><div class=\"continue-actions\"><button type=\"button\" class=\"primary-action\" data-continue>Continue</button><button type=\"button\" data-dismiss>Not now</button></div>';\r\n home.append(card);\r\n let row=null,dismissed=null,queued=0,run=0;\r\n const hide=()=>{if(!card.hidden)card.hidden=true;};\r\n async function refresh(){\r\n  const ticket=++run;\r\n  if(!podShown(doc)||!idle()){hide();return;}\r\n  let next=null;try{next=await unfinished();}catch{}\r\n  if(ticket!==run)return;\r\n  row=next;\r\n  if(!continueVisible({shown:podShown(doc),idle:idle(),row,dismissed})){hide();return;}\r\n  const prompt=continuePrompt(row,label(row),canContinue(row));if(card.querySelector('h2').textContent!==prompt.text)card.querySelector('h2').textContent=prompt.text;\r\n  card.querySelector('[data-continue]').hidden=!prompt.allowed;\r\n  card.querySelector('[data-continue]').textContent=prompt.action;\r\n  // Sit just above the bottom bar, whatever height it has on this phone.\r\n  const bar=doc.getElementById('coachDock')?.getBoundingClientRect(),bottom=bar?.height?Math.max(16,innerHeight-bar.top+12)+'px':'';\r\n  if(card.style.bottom!==bottom)card.style.bottom=bottom;\r\n  if(card.hidden)card.hidden=false;\r\n }\r\n const queue=()=>{queued||=setTimeout(()=>{queued=0;void refresh();});};\r\n const observer=new MutationObserver(queue);\r\n const onVisibility=()=>{if(doc.hidden){run++;hide();}else queue();};\r\n doc.addEventListener('visibilitychange',onVisibility);\r\n observer.observe(doc.body,{subtree:true,attributes:true,attributeFilter:['hidden','open','aria-current','data-screen','data-tracking']});\r\n card.querySelector('[data-dismiss]').onclick=()=>{dismissed=row?.id??null;hide();};\r\n card.querySelector('[data-continue]').onclick=()=>{const chosen=row;hide();if(!chosen||!idle()||doc.hidden)return;if(!canContinue(chosen).allowed){dismissed=chosen.id;return;}onContinue(chosen);};\r\n queue();\r\n return {refresh,dispose(){observer.disconnect();doc.removeEventListener('visibilitychange',onVisibility);clearTimeout(queued);card.remove();}};\r\n}\r\n","pod/control-board.css":"/* #106 workout selector: one compact control board (readout, focus dial, level knob, lever, sound switch).\r\n   CSS only, no images. Self-contained under .control-board so it can move to the oval route (#60) as one block. */\r\n.control-board{--cb-ink:#f1dcb4;--cb-dim:#b9a1b0;--cb-gold:#ffd36e;--cb-mono:ui-monospace,'Cascadia Mono',Consolas,Menlo,monospace;\r\n --screw:#cbbab0 0 1.4px,#57474f 1.9px 3px,#0000 3.5px;\r\n position:relative;margin:0 0 10px;padding:10px 12px 2px;border:1px solid #0e0a12;border-radius:12px;\r\n background:radial-gradient(circle at 7px 7px,var(--screw)),radial-gradient(circle at calc(100% - 7px) 7px,var(--screw)),radial-gradient(circle at 7px calc(100% - 7px),var(--screw)),radial-gradient(circle at calc(100% - 7px) calc(100% - 7px),var(--screw)),\r\n  repeating-linear-gradient(90deg,#0000 0 3px,#ffffff05 3px 4px),linear-gradient(165deg,#5d4a5b,#3c3043 50%,#2e2436);\r\n box-shadow:inset 0 1px #dcc6d066,inset 0 -2px #0007,0 0 0 1px #8c768966,0 4px 0 #140f19,0 7px 12px #0006;\r\n font:700 8px/1 var(--cb-mono);letter-spacing:.14em;color:var(--cb-ink);-webkit-user-select:none;user-select:none}\r\n.control-board button{font:inherit;letter-spacing:inherit}\r\n\r\n/* Readout window + MOVEMENTS key */\r\n.control-board .cb-top{display:flex;gap:8px;align-items:stretch}\r\n.control-board .cb-readout{position:relative;flex:1;min-width:0;padding:7px 9px 5px;border:1px solid #09070b;border-radius:6px;background:repeating-linear-gradient(0deg,#ffd36e07 0 1px,#0000 1px 3px),radial-gradient(ellipse at 30% 0,#3a2a1a55,#0000 70%),#150f19;box-shadow:inset 0 2px 6px #000d,0 1px #cbb3bf44}\r\n.control-board .cb-line{display:flex;justify-content:space-between;gap:8px;color:#d2ae62}\r\n.control-board #exerciseName,.control-board #difficultySetting{font:inherit;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase}\r\n.control-board #difficultySetting{flex:none}\r\n.control-board #variationName{position:static;display:block;width:100%;min-height:0;margin:4px 0 0;padding:0;border:0;border-radius:0;background:none;box-shadow:none;color:var(--cb-gold);font:700 15px/1.25 system-ui,-apple-system,'Segoe UI',sans-serif;letter-spacing:0;text-align:left;text-decoration:underline dotted #ffd36e66;text-underline-offset:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 0 8px #ffd36e40;cursor:pointer}\r\n.control-board #variationName:after{content:'';position:absolute;inset:0;border-radius:6px}\r\n.control-board #variationName:focus-visible{outline:0}.control-board #variationName:focus-visible:after{outline:3px solid var(--cb-gold);outline-offset:2px}\r\n.control-board .cb-key{flex:none;width:66px;min-height:44px;padding:4px 3px;border:1px solid #120d16;border-radius:6px;background:linear-gradient(#716070,#4a3b4b);box-shadow:inset 0 1px #dcc4cf99,inset 0 -2px #291f2d,0 3px 0 #140f19;color:var(--cb-ink);line-height:1.4;white-space:normal;transition:transform .07s,box-shadow .07s}\r\n.control-board .cb-key:active:not(:disabled){transform:translateY(2px);border-color:#120d16;box-shadow:inset 0 1px #dcc4cf55,0 1px 0 #140f19}\r\n\r\n/* The deck: stencilled plates over each part, a panel seam above */\r\n.control-board .cb-deck{display:flex;justify-content:space-between;align-items:flex-end;gap:4px;margin-top:8px;padding-top:7px;border-top:1px solid #150f19;box-shadow:inset 0 1px #c8b0bd30}\r\n.control-board .cb-part{display:flex;flex-direction:column;align-items:center;gap:5px}\r\n.control-board .cb-label{padding:3px 5px 2px;border-radius:2px;background:#1a141f;box-shadow:inset 0 1px 2px #000a,0 1px #c8b0bd33;font-size:10px}\r\n\r\n/* Rotaries: recessed well, detent ticks, a cap that turns to --angle and snaps with a little overshoot */\r\n.control-board .cb-rotary{--size:76px;--angle:-135deg;position:relative;flex:none;width:var(--size);height:var(--size);border-radius:50%;touch-action:none;cursor:grab;background:radial-gradient(closest-side,#0d0a10 76%,#2f2536 79% 87%,#0000 89%)}\r\n.control-board .cb-rotary:focus-visible{outline:3px solid var(--cb-gold);outline-offset:2px}\r\n.control-board .cb-rotary.is-turning{cursor:grabbing}\r\n.control-board .cb-rotary[aria-disabled=true]{opacity:.45;cursor:default}\r\n.control-board .cb-ticks{position:absolute;inset:0;pointer-events:none}\r\n.control-board .cb-ticks b{position:absolute;left:calc(50% - 1px);top:0;width:2px;height:5px;border-radius:1px;background:#8a7686;transform-origin:1px calc(var(--size) / 2);transform:rotate(var(--a))}\r\n.control-board .cb-ticks b[data-on]{background:var(--cb-gold);box-shadow:0 0 5px var(--cb-gold)}\r\n.control-board .cb-rotary i{position:absolute;inset:10px;border-radius:50%;transform:rotate(var(--angle));transition:transform .18s cubic-bezier(.3,1.6,.5,1),scale .08s;pointer-events:none}\r\n.control-board .cb-rotary.is-turning i{transition:scale .08s}\r\n.control-board .cb-rotary:active:not([aria-disabled=true]) i{scale:.95}\r\n.control-board .cb-rotary:after{content:'';position:absolute;inset:10px;border-radius:50%;background:radial-gradient(circle at 34% 28%,#ffffff40,#0000 48%);pointer-events:none}\r\n/* Focus dial: knurled edge, gold pointer */\r\n.control-board .cb-dial i{background:radial-gradient(closest-side,#7a6677 0 68%,#584656 74%,#0000 76%),repeating-conic-gradient(#ad99a5 0 5deg,#3a2e3f 5deg 10deg);box-shadow:0 3px 5px #000b,0 0 0 1px #0d0a10}\r\n.control-board .cb-dial i:after{content:'';position:absolute;left:calc(50% - 2px);top:3px;width:4px;height:16px;border-radius:2px;background:var(--cb-gold);box-shadow:0 0 6px #ffd36eaa}\r\n/* Level knob: bakelite pointer knob with min/max figures */\r\n.control-board .cb-knob{--size:58px}\r\n.control-board .cb-knob i{inset:9px;background:radial-gradient(closest-side,#302533 0 82%,#17111b 86%,#0000 90%);box-shadow:0 3px 4px #000b}\r\n.control-board .cb-knob i:before{content:'';position:absolute;left:calc(50% - 6px);top:-5px;bottom:-5px;width:12px;border-radius:6px;background:linear-gradient(90deg,#1e1822,#65536a 45%,#2b2231);box-shadow:0 2px 3px #000a}\r\n.control-board .cb-knob i:after{content:'';position:absolute;left:calc(50% - 1px);top:-3px;width:2px;height:15px;border-radius:1px;background:#fbefd9}\r\n.control-board .cb-knob:after{inset:9px}\r\n.control-board .cb-scale{display:flex;justify-content:space-between;width:58px;margin-top:-9px;padding:0 1px;color:var(--cb-dim);font-size:10px;letter-spacing:0}\r\n\r\n/* Lever: spring-loaded T-handle in a slot; HARDER above, EASIER below */\r\n.control-board .cb-lever{--pull:0px;position:relative;gap:0;width:46px;height:94px;border-radius:8px;background:linear-gradient(90deg,#19131e,#2c2332 50%,#19131e);box-shadow:inset 0 2px 5px #000c,0 1px #c8b0bd44;touch-action:none}\r\n.control-board .cb-lever:before{content:'';position:absolute;left:calc(50% - 3px);top:21px;bottom:21px;width:6px;border-radius:3px;background:#060408;box-shadow:inset 0 1px 2px #000,0 1px #c8b0bd33}\r\n.control-board .cb-lever button{position:relative;z-index:1;display:flex;justify-content:center;width:100%;height:47px;min-height:0;padding:5px 0;border:0;border-radius:8px;background:none;box-shadow:none;color:var(--cb-ink);font-size:10px;letter-spacing:.02em}\r\n.control-board .cb-lever #harder{align-items:flex-start}.control-board .cb-lever #easier{align-items:flex-end}\r\n.control-board .cb-lever button:focus-visible{outline-offset:-3px}\r\n.control-board .cb-lever button:active:not(:disabled){border-color:transparent;box-shadow:none;background:#ffd36e14}\r\n.control-board .cb-lever i{position:absolute;z-index:2;left:7px;right:7px;top:calc(50% - 7px);height:14px;border-radius:7px;background:linear-gradient(#ffd691,#c98640 55%,#8a5430);box-shadow:inset 0 1px #fff5,0 3px 4px #000b;transform:translateY(var(--pull));transition:transform .2s cubic-bezier(.3,1.6,.5,1);pointer-events:none}\r\n.control-board .cb-lever.is-pulling i{transition:none}\r\n.control-board .cb-lever:has(button:disabled){opacity:.45}\r\n\r\n/* Sound: bat-handle toggle on a plate; up is ON. app.mjs writes ON/OFF as the lamp text. */\r\n.control-board .cb-switch{position:relative;display:flex;align-items:flex-end;justify-content:center;width:44px;height:76px;min-height:0;padding:0 0 6px;border:1px solid #0d0a10;border-radius:7px;background:linear-gradient(#524352,#30273a);box-shadow:inset 0 1px #d4bdca66,inset 0 -2px #0006,0 3px 0 #140f19;color:#7d6c79;transition:transform .07s}\r\n.control-board .cb-switch:before{content:'';position:absolute;left:calc(50% - 4px);top:calc(40% - 26px);width:8px;height:26px;border-radius:4px 4px 2px 2px;background:linear-gradient(90deg,#6b5d69,#f4ebf0 45%,#857583);box-shadow:2px 2px 3px #0009;transform-origin:50% 100%;transition:transform .14s cubic-bezier(.3,1.5,.5,1)}\r\n.control-board .cb-switch:after{content:'';position:absolute;left:calc(50% - 8px);top:calc(40% - 8px);width:16px;height:16px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#dccbd4,#6e5f6c 60%,#382d3a);box-shadow:0 2px 3px #000a}\r\n.control-board .cb-switch[data-on=false]:before{transform:scaleY(-1)}\r\n.control-board .cb-switch[data-on=true]{color:var(--cb-gold);text-shadow:0 0 6px #ffd36e}\r\n.control-board .cb-switch:active:not(:disabled){transform:translateY(1px);border-color:#0d0a10;box-shadow:inset 0 1px #d4bdca44,0 2px 0 #140f19}\r\n\r\n/* Tracking notes: a slim hatch along the bottom edge */\r\n.control-board .movement-scope{margin:3px 8px 0;color:#e3cfde;font:12px/1.45 system-ui,-apple-system,'Segoe UI',sans-serif;letter-spacing:0;-webkit-user-select:text;user-select:text}\r\n.control-board .movement-scope summary{display:flex;align-items:center;gap:6px;min-height:24px;color:var(--cb-dim);font:700 8px/1 var(--cb-mono);letter-spacing:.14em;text-transform:uppercase;list-style:none;cursor:pointer}\r\n.control-board .movement-scope summary::-webkit-details-marker{display:none}\r\n.control-board .movement-scope summary:before{content:'▸'}.control-board .movement-scope[open] summary:before{content:'▾'}\r\n.control-board .movement-scope p{margin:4px 0}\r\n\r\n@media(max-width:370px){.control-board{padding:10px 9px 2px}.control-board .cb-dial{--size:68px}.control-board .cb-knob{--size:52px}.control-board .cb-scale{width:52px}.control-board .cb-key{width:58px}.control-board #variationName{font-size:13px}}\r\n@media(prefers-reduced-motion:reduce){.control-board *,.control-board *:before,.control-board *:after{transition:none!important}}\r\n\r\n/* Subway Burpees key: its own pressable metal bar under the dials, reachable on every coach path */\r\n.control-board .cb-cardio{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;margin-top:9px;padding:6px 10px;border:1px solid #120d16;border-radius:7px;background:linear-gradient(#716070,#4a3b4b);box-shadow:inset 0 1px #dcc4cf99,inset 0 -2px #291f2d,0 4px 0 #140f19;color:var(--cb-ink);font:700 12px/1 var(--cb-mono);letter-spacing:.16em;transition:transform .07s,box-shadow .07s}\r\n.control-board .cb-cardio small{font:inherit;font-size:9px;color:var(--cb-gold);letter-spacing:.2em}\r\n.control-board .cb-cardio-led{width:9px;height:9px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#ffe9a8,#ffb42e 60%,#6b3d00);box-shadow:0 0 6px #ffd36e}\r\n.control-board .cb-cardio:active:not(:disabled){transform:translateY(3px);box-shadow:inset 0 1px #dcc4cf55,0 1px 0 #140f19}\r\n.control-board .cb-cardio:disabled{opacity:.55}\r\n.control-board .cb-cardio:focus-visible{outline:3px solid var(--cb-gold);outline-offset:2px}\r\n.control-board .cb-arcade-keys{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin:0 0 5px}\r\n.control-board .cb-arcade-keys .cb-cardio{margin-top:0;flex-wrap:wrap;column-gap:6px;row-gap:1px;min-width:0;padding:3px 8px}\r\n.control-board .cb-arcade-keys .cb-cardio small{flex-basis:100%}\r\n.control-board .cb-arcade-keys .cb-cardio:nth-child(3):last-child{grid-column:1/-1}\r\n@media(max-width:370px){.control-board .cb-cardio{letter-spacing:.1em;font-size:11px}}\r\n/* Keep the four arcade keys on two compact rows at phone width. The longest title stays on\r\n   one line so the second row remains a 44px target and BEGIN clears the fixed coach dock. */\r\n@media(max-width:480px){\r\n .control-board{margin-bottom:6px;padding-top:9px}\r\n .control-board .cb-deck{margin-top:6px;padding-top:5px}\r\n .control-board .cb-arcade-keys{gap:4px;margin:0 0 2px}\r\n .control-board .cb-arcade-keys .cb-cardio strong{font-size:10px;letter-spacing:.02em;white-space:nowrap}\r\n}\r\n\r\n/* One console: the circuit switchboard rides in-flow under BEGIN instead of dropping from the deck strip. */\r\n#controls>#podFullWorkout{position:relative;margin-top:10px}\r\n#controls>#podFullWorkout>.fw-menu{position:static;margin-top:6px;box-shadow:none;height:550px!important}\r\n\r\n/* Cockpit instruments use the original controls, detents, values and handlers. */\r\n.cockpit-mounted .control-board .cb-deck{flex-wrap:wrap;justify-content:space-around;gap:8px}\r\n.cockpit-mounted .control-board .cb-top{flex-wrap:wrap}.cockpit-mounted .control-board .cb-readout{flex-basis:90px}\r\n.cockpit-mounted .control-board .cb-key{min-height:44px}\r\n.cockpit-mounted .control-board .cb-arcade-keys{grid-template-columns:repeat(3,minmax(44px,1fr));gap:7px;padding:7px 0 12px;border-bottom:2px groove #8a989b}\r\n.cockpit-mounted .control-board .cockpit-cartridge{position:relative;display:flex;align-items:center;justify-content:center;min-width:44px;min-height:66px;margin:0;padding:7px 2px 12px;border:1px solid #120d16;border-radius:5px 5px 2px 2px;background:linear-gradient(#716070,#4a3b4b);box-shadow:inset 0 1px #dcc4cf99,inset 0 -2px #291f2d,0 4px 0 #140f19;color:#bce4d1}\r\n.cockpit-mounted .control-board .cockpit-cartridge[data-game=cover]{color:#ffd289}.cockpit-mounted .control-board .cockpit-cartridge[data-game=dance]{color:#f1b5ff}\r\n.cockpit-mounted .control-board .cockpit-cartridge[data-game=plank-flight]{grid-column:1/-1;min-height:50px;width:100%;color:#ffe3a1}\r\n.cockpit-cartridge-art{display:block;width:40px;height:40px;filter:drop-shadow(0 2px 0 #000)}.cockpit-cartridge-art svg{width:100%;height:100%}\r\n.cockpit-cartridge:after{content:'';position:absolute;left:8px;right:8px;bottom:3px;height:5px;background:repeating-linear-gradient(90deg,#aa8c4a 0 3px,#101920 3px 6px);border-top:1px solid #000}\r\n.cockpit-mounted .control-board .cockpit-cartridge[data-selected=true]:before{content:\"\";position:absolute;top:6px;right:6px;width:9px;height:9px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#ffe9a8,#ffb42e 60%,#6b3d00);box-shadow:0 0 6px #ffd36e}.cockpit-mounted .control-board .cockpit-cartridge:active{transform:translateY(3px);box-shadow:inset 0 3px 8px #0008,0 1px #060b0f}\r\n.cockpit-mounted .control-board .movement-scope summary{min-height:44px;font-size:10px;letter-spacing:.02em}\r\n","pod/dj-identity.js":"/* One identity follows the player from the Music Desk to the Gala. */\r\n(()=>{'use strict';\r\n const KEY='mbs-dj-identity-v1';\r\n const moniker=value=>String(value||'').normalize('NFKC').toUpperCase().replace(/[^A-Z0-9]/g,'').slice(0,3);\r\n function valid(value){return !!(value&&value.version===1&&typeof value.base==='string'&&value.base.length>0&&value.base.length<=48&&/^[A-Z0-9]{3}$/.test(value.moniker)&&Array.isArray(value.parts)&&value.parts.length===3&&value.parts.every(p=>typeof p==='string'&&p.length<24)&&Number.isSafeInteger(value.chosenAt)&&value.chosenAt>0&&(value.completedAt===null||Number.isSafeInteger(value.completedAt)&&value.completedAt>=value.chosenAt));}\r\n function read(){try{const value=JSON.parse(localStorage.getItem(KEY));return valid(value)?value:null;}catch{return null;}}\r\n function write(value){localStorage.setItem(KEY,JSON.stringify(value));window.dispatchEvent(new CustomEvent('mbs:dj-identity',{detail:value}));return value;}\r\n function choose(base,tag,parts){tag=moniker(tag);if(tag.length!==3)throw Error('Add three letters or numbers for your moniker.');base=String(base||'').trim().slice(0,48);if(!base||!Array.isArray(parts)||parts.length!==3||parts.some(p=>!p))throw Error('Choose all three parts of your DJ name.');const old=read();if(old?.completedAt&&(old.base!==base||old.moniker!==tag))throw Error('Your prisoner name is locked to this run.');return write({version:1,base,moniker:tag,parts,chosenAt:old?.chosenAt||Date.now(),completedAt:old?.completedAt||null});}\r\n function complete(){const current=read();if(!current)throw Error('Choose your DJ name first.');if(current.completedAt)return current;return write({...current,completedAt:Date.now()});}\r\n function name(value=read()){return value?`${value.base} · ${value.moniker}`:'Anonymous guest';}\r\n function display(value=read()){return value?.completedAt?`PRISONER ${name(value)}`:'Anonymous guest';}\r\n function retag(tag){const old=read();tag=moniker(tag);if(!old||tag.length!==3)throw Error('Use three letters or numbers.');return write({...old,moniker:tag});}\r\n window.MBS_DJ={KEY,moniker,valid,read,choose,complete,name,display,retag};\r\n})();\r\n","pod/encouragement.mjs":"import {valueOf} from './set-flow.mjs';\r\nconst LINES={reps:['Steady work.','One movement at a time.','Keep your own pace.'],hold:['Stay comfortable.','Take your time.','Steady work.'],pace:['Find your rhythm.','Keep your own pace.','Steady work.'],steps:['Find your rhythm.','One step at a time.','Keep your own pace.'],jumps:['Keep your own pace.','One movement at a time.','Steady work.']};\r\nexport class SetEncouragement {\r\n constructor(){this.reset();}\r\n reset(){this.first=null;this.last=-Infinity;this.halfway=false;this.index=0;}\r\n update(m,goal,now,otherCues=[]){\r\n  if(!m.tracking||m.complete||valueOf(m)>=goal)return null;\r\n  if(['squat','pushup','jumping'].includes(m.mode)&&!m.calibrated)return null;\r\n  if(m.kind==='hold'&&m.progress!==1)return null;\r\n  this.first??=now;\r\n  // Speak around counts; give setup, recovery and timer announcements priority.\r\n  if(otherCues.some(c=>c.key!=='count')||now-this.last<8000)return null;\r\n  if(!this.halfway&&valueOf(m)>=goal/2){this.halfway=true;this.last=now;return {text:'Halfway. Keep your own pace.',key:'encouragement'};}\r\n  if(now-this.first>=18000&&now-this.last>=20000){this.last=now;const choices=LINES[m.kind]||LINES.reps;return {text:choices[this.index++%choices.length],key:'encouragement'};}\r\n  return null;\r\n }\r\n}\r\n","pod/gala-avatar.js":"/*! MOM Inc Gala avatar: a procedural character rig drawn as a 64x96 pixel sprite.\n Rig, pieces, animation and pixelize law ported from DFO Online (github.com/Lattymoy/daggerfall-js-source),\n MIT (c) 2026 Lattymoy; includes Daggerfall Unity-derived code, MIT (c) 2009-2023 Daggerfall Workshop.\n Full notices: LICENSE-DFO.txt beside this file */\n(()=>{var Sr=.4*Math.PI,m1=1.655,Z1=Object.freeze({torso:1,shoulder:1,arm:1,hand:1,neck:1,skull:1,jaw:1,leg:1}),Er=.6,Ir=1.6;function q1(t,r={}){let a=[196,154,116],s=[92,74,58],m=[],i=\"body\",n=(H,E)=>Math.sign(H)*Math.pow(Math.abs(H),E),e=(H,E,N,W,Y,G,F=1)=>{let j=G/28*Math.PI*2;return[H+W*n(Math.cos(j),F),E,N+Y*n(Math.sin(j),F)]};function p(H,E,N,W){let Y=H.slice().sort((G,F)=>G.y-F.y);for(let G=0;G+1<Y.length;G++){let F=Y[G],j=Y[G+1];if(Math.abs(F.y-j.y)<1e-6)continue;let q=N?N(F):F.cx??0,B=N?N(j):j.cx??0,Q=W?W(F):F.cz??0,r0=W?W(j):j.cz??0,K=F.p??1,m0=j.p??1;for(let c0=0;c0<28;c0++){let d0=e(q,F.y,Q,F.rx,F.rz,c0,K),X0=e(q,F.y,Q,F.rx,F.rz,c0+1,K),V0=e(B,j.y,r0,j.rx,j.rz,c0+1,m0),j0=e(B,j.y,r0,j.rx,j.rz,c0,m0),s1=X0[0]-d0[0],j1=X0[1]-d0[1],G1=X0[2]-d0[2],K1=j0[0]-d0[0],U1=j0[1]-d0[1],V1=j0[2]-d0[2],Z0=j1*V1-G1*U1,n1=G1*K1-s1*V1,q0=s1*U1-j1*K1,L1=Math.hypot(Z0,n1,q0)||1;Z0/=L1,n1/=L1,q0/=L1;let Ar=(d0[0]+V0[0])/2-q,Rr=(d0[2]+V0[2])/2-Q;Z0*Ar+q0*Rr<0&&(Z0=-Z0,n1=-n1,q0=-q0),m.push({p:[...d0,...X0,...V0,...j0],n:[Z0,n1,q0],c:E,g:i})}}}let g={...Z1,...r.build||{}};for(let H of Object.keys(Z1))g[H]=Math.min(Ir,Math.max(Er,Number.isFinite(g[H])?g[H]:1));let b=(H,E)=>E===1?H:H.map(N=>({...N,rx:N.rx*E,rz:N.rz*E})),f=.58;p(b([{y:1.66,rx:.205,rz:.09,p:f},{y:1.57,rx:.215,rz:.096,p:f},{y:1.46,rx:.208,rz:.098,p:f},{y:1.33,rx:.182,rz:.092,p:f},{y:1.19,rx:.16,rz:.082,p:f},{y:1.09,rx:.162,rz:.084,p:f},{y:1.04,rx:.168,rz:.088,p:f},{y:.99,rx:.17,rz:.09,p:f}],g.torso),a),p(b([{y:1.66,rx:.086,rz:.058,p:.5,cx:0},{y:1.64,rx:.15,rz:.07,p:.5,cx:0},{y:1.61,rx:.215,rz:.082,p:.5,cx:0}],g.shoulder),a);let d=[{y:1.64,rx:.07,rz:.072,p:.55},{y:1.6,rx:.09,rz:.082,p:.55},{y:1.55,rx:.082,rz:.076,p:.55},{y:1.5,rx:.066,rz:.068,p:.6}];{let H=b(d,g.shoulder);i=\"armL\",p(H,a,()=>-.24,()=>0),i=\"armR\",p(H,a,()=>.24,()=>0)}i=\"body\",p(b([{y:1.63,rx:.078,rz:.078,p:.8},{y:1.68,rx:.07,rz:.072,p:.8},{y:1.73,rx:.072,rz:.074,p:.8}],g.neck),a,()=>0,()=>-.01),i=\"head\";let h=[{y:1.7,rx:.052,rz:.07,cz:.01},{y:1.74,rx:.082,rz:.105,cz:.005}],l=[{y:1.79,rx:.102,rz:.13,cz:-.005}],c=[{y:1.85,rx:.108,rz:.14,cz:-.015},{y:1.92,rx:.1,rz:.135,cz:-.02},{y:1.98,rx:.082,rz:.108,cz:-.018},{y:2.03,rx:.045,rz:.058,cz:-.012}],z=r.head?.jaw||h,x=r.head?.seam||l,k=r.head?.skull||c;p([...b(z,g.jaw),...b(x,(g.jaw+g.skull)/2),...b(k,g.skull)],a,()=>0,H=>H.cz),i=\"body\";let w=[{y:1.61,rx:.072,rz:.075},{y:1.5,rx:.065,rz:.068},{y:1.42,rx:.058,rz:.06},{y:1.35,rx:.05,rz:.056,cz:-.006},{y:1.3,rx:.056,rz:.062,cz:-.008},{y:1.22,rx:.056,rz:.058},{y:1.16,rx:.05,rz:.052},{y:1.06,rx:.044,rz:.046},{y:.99,rx:.036,rz:.038}];{let H=b(w,g.arm);i=\"armL\",p(H,a,()=>-.235,()=>0),i=\"armR\",p(H,a,()=>.235,()=>0)}i=\"body\";function M(H){let E=H*.235;p(b([{y:.985,rx:.036,rz:.038},{y:.95,rx:.052,rz:.03},{y:.905,rx:.054,rz:.03},{y:.86,rx:.048,rz:.026},{y:.82,rx:.036,rz:.02}],g.hand),a,()=>E,()=>0);let N=H;p(b([{y:.955,rx:.016,rz:.02,cz:.01},{y:.935,rx:.02,rz:.024,cz:.018},{y:.912,rx:.014,rz:.018,cz:.014}],g.hand),a,()=>E+N*.052*g.hand,W=>W.cz)}i=\"armL\",M(-1),i=\"armR\",M(1),i=\"body\";let L=.1,P=[{y:.1,rx:.048,rz:.048},{y:.22,rx:.06,rz:.063},{y:.34,rx:.084,rz:.088},{y:.44,rx:.07,rz:.072},{y:.5,rx:.076,rz:.088,cz:.01},{y:.55,rx:.072,rz:.08},{y:.62,rx:.086,rz:.09},{y:.72,rx:.096,rz:.1},{y:.82,rx:.112,rz:.114},{y:.95,rx:.12,rz:.12}],S=.082,A=[{y:.9,rx:P[P.length-1].rx,rz:P[P.length-1].rz,dx:0},{y:.96,rx:.12,rz:.112,dx:.006},{y:1,rx:.108,rz:.1,dx:.012}];function _(H){p(b(P,g.leg),a,()=>H*S,()=>0),p(b(A,g.leg),a,E=>H*(S-E.dx),()=>0)}i=\"legL\",_(-1),i=\"legR\",_(1),i=\"body\";let R=.045,T=(H,E,N,W,Y)=>{let G=E[0]-H[0],F=E[1]-H[1],j=E[2]-H[2],q=W[0]-H[0],B=W[1]-H[1],Q=W[2]-H[2],r0=F*Q-j*B,K=j*q-G*Q,m0=G*B-F*q,c0=Math.hypot(r0,K,m0)||1;r0/=c0,K/=c0,m0/=c0,m.push({p:[...H,...E,...N,...W],n:[r0,K,m0],c:Y,g:i})};function v(H){let N=[[-.062,.082,.052],[-.025,.09,.055],[.06,.074,.058],[.155,.056,.058],[.235,.036,.044]];for(let G=0;G+1<N.length;G++){let[F,j,q]=N[G],[B,Q,r0]=N[G+1],K=[H-q,.005,F],m0=[H+q,.005,F],c0=[H-q,j,F],d0=[H+q,j,F],X0=[H-r0,.005,B],V0=[H+r0,.005,B],j0=[H-r0,Q,B],s1=[H+r0,Q,B];T(K,m0,V0,X0,s),T(c0,j0,s1,d0,s),T(K,X0,j0,c0,s),T(m0,d0,s1,V0,s)}let W=N[0],Y=N[N.length-1];T([H-W[2],.005,W[0]],[H-W[2],W[1],W[0]],[H+W[2],W[1],W[0]],[H+W[2],.005,W[0]],s),T([H-Y[2],.005,Y[0]],[H+Y[2],.005,Y[0]],[H+Y[2],Y[1],Y[0]],[H-Y[2],Y[1],Y[0]],s),p([{y:L,rx:R,rz:R,cz:0},{y:.085,rx:.046,rz:.05,cz:0}],s,()=>H,()=>0)}i=\"legL\",v(-S),i=\"legR\",v(S),i=\"body\";let C=.5,I=.55,X=.67,t0=Math.hypot(C,I,X),e0=(H,E)=>H[Math.max(0,Math.min(H.length-1,Math.round(E*(H.length-1))))],u0=(H,E,N,W,Y,G=1,F=\"steel\",j=-1/0,q=1/0)=>{for(let B of m){if(!H.includes(B.g))continue;let Q=0;for(let K=0;K<4;K++)Q+=B.p[K*3+1];if(Q/=4,Q<E||Q>N)continue;if(j!==-1/0||q!==1/0){let K=0;for(let m0=0;m0<4;m0++)K+=B.p[m0*3+2];if(K/=4,K<j||K>q)continue}let r0=Y?Y(B.g):0;for(let K=0;K<4;K++){let m0=B.p[K*3]-r0,c0=B.p[K*3+2],d0=Math.hypot(m0,c0)||1;B.p[K*3]=r0+m0+m0/d0*W,B.p[K*3+2]=c0+c0/d0*W*G}B._mat=F}},b0=H=>H===\"legL\"?-S:H===\"legR\"?S:0,f0=H=>H===\"armL\"?-.235:H===\"armR\"?.235:0;for(let H of r.clothZones||[]){let E=H.leg?b0:H.arm?f0:null;u0(H.groups,H.yLo,H.yHi,H.th??.008,E,1,H.mat||\"cloth\",H.zLo??-1/0,H.zHi??1/0)}for(let H of r.armorZones||[]){let E=H.leg?b0:H.arm?f0:null;u0(H.groups,H.yLo,H.yHi,H.th??.02,E,H.zScale??1,H.mat||\"steel\",H.zLo??-1/0,H.zHi??1/0)}let M0=.9;for(let H of m)for(let E=0;E<4;E++)H.p[E*3+1]*=M0;{let H=.9875*M0;for(let E of m){if(E.g!==\"armL\"&&E.g!==\"armR\")continue;let N=0;for(let B=0;B<4;B++)N+=E.p[B*3+1];if(N/=4,N>=H)continue;let W=E.g===\"armL\"?-.235:.235,Y=(E.g===\"armL\"?1:-1)*Sr,G=Math.cos(Y),F=Math.sin(Y);for(let B=0;B<4;B++){let Q=E.p[B*3]-W,r0=E.p[B*3+2];E.p[B*3]=W+G*Q+F*r0,E.p[B*3+2]=-F*Q+G*r0}let j=E.n[0],q=E.n[2];E.n[0]=G*j+F*q,E.n[2]=-F*j+G*q}}let p0=m.map(H=>{let E=0,N=0,W=0;for(let Y=0;Y<4;Y++)E+=H.p[Y*3],N+=H.p[Y*3+1],W+=H.p[Y*3+2];return[E/4,N/4,W/4]}),P0=.11,_0=.55,T0=new Array(m.length).fill(1),x0=P0,n0=(H,E,N)=>((H+512)*1024+(E+512))*1024+(N+512),Z=H=>Math.floor(H/x0),a0=new Map,F0=new Array(m.length);for(let H=0;H<m.length;H++){let E=n0(Z(p0[H][0]),Z(p0[H][1]),Z(p0[H][2]));F0[H]=E;let N=a0.get(E);N?N.push(H):a0.set(E,[H])}let X1=new Map;for(let H=0;H<m.length;H++){let E=p0[H],N=m[H].n,W=0,Y=X1.get(F0[H]);if(Y===void 0){Y=[];let G=Z(E[0]),F=Z(E[1]),j=Z(E[2]);for(let q=-1;q<=1;q++)for(let B=-1;B<=1;B++)for(let Q=-1;Q<=1;Q++){let r0=a0.get(n0(G+q,F+B,j+Q));if(r0)for(let K=0;K<r0.length;K++)Y.push(r0[K])}Y.sort((q,B)=>q-B),X1.set(F0[H],Y)}for(let G=0;G<Y.length;G++){let F=Y[G];if(F===H)continue;let j=p0[F][0]-E[0],q=p0[F][1]-E[1],B=p0[F][2]-E[2],Q=Math.hypot(j,q,B);if(Q<1e-4||Q>P0)continue;let r0=(j*N[0]+q*N[1]+B*N[2])/Q;r0>.25&&(W+=(1-Q/P0)*r0)}T0[H]=Math.max(.45,1-Math.min(1,W*_0*.12))}for(let H=0;H<m.length;H++){let E=m[H],N=Math.max(.08,(E.n[0]*C+E.n[1]*I+E.n[2]*X)/t0*.9+.15);N*=T0[H];let W=E._mat?r.mats?.[E._mat]||t[E._mat]||r.cloth||t.skin:E.c===s?t.boot:t.skin,Y=Math.min(1,Math.max(.04,N));E._i=Y,E.c=e0(W,Y),E.r=W,E.ao=T0[H],E.fb=[.08,.15]}return m}function Q1(t,r=[],o=[]){let m=e=>e===\"legL\"?-.082:e===\"legR\"?.082:0,i=e=>e===\"armL\"?-.235:e===\"armR\"?.235:0,n=(e,p,g,b,f,y,u,d,h)=>{for(let l of t){if(!e.includes(l.g))continue;let c=0;for(let x=0;x<4;x++)c+=l.p[x*3+1];if(c/=3.6,c<p||c>g)continue;if(d!==-1/0||h!==1/0){let x=0;for(let k=0;k<4;k++)x+=l.p[k*3+2];if(x/=4,x<d||x>h)continue}let z=f?f(l.g):0;for(let x=0;x<4;x++){let k=l.p[x*3]-z,w=l.p[x*3+2],M=Math.hypot(k,w)||1;l.p[x*3]=z+k+k/M*b,l.p[x*3+2]=w+w/M*b*y}l._mat=u}};for(let e of r)n(e.groups,e.yLo,e.yHi,e.th??.008,e.leg?m:e.arm?i:null,1,e.mat||\"cloth\",e.zLo??-1/0,e.zHi??1/0);for(let e of o)n(e.groups,e.yLo,e.yHi,e.th??.02,e.leg?m:e.arm?i:null,e.zScale??1,e.mat||\"steel\",e.zLo??-1/0,e.zHi??1/0);return t}var _r=24,J1=(t,r)=>Math.sign(t)*Math.pow(Math.abs(t),r),Q0=(t,r,o,a,s,m,i,n,e=0,p=Math.PI*2)=>{let g=e+m/i*p;return[t+a*J1(Math.cos(g),n),r,o+s*J1(Math.sin(g),n)]};function D(t,r,{cx:o=0,seg:a=_r,group:s,capTop:m=!0,capBottom:i=!0,arc:n=null}={}){let e=n?n[0]:0,p=n?n[1]-n[0]:Math.PI*2;n&&(m=!1,i=!1);let g=r.slice().sort((f,y)=>f.y-y.y),b=(f,y)=>{let u=f.cz??0,d=f.p??1,h=[o,f.y,u];for(let l=0;l<a;l++){let c=Q0(o,f.y,u,f.rx,f.rz,l,a,d,e,p),z=Q0(o,f.y,u,f.rx,f.rz,l+1,a,d,e,p),x=y>0?[h,z,c]:[h,c,z];t.push({p:[...x[0],...x[1],...x[2],...x[2]],n:[0,y,0],g:s})}};i&&b(g[0],-1),m&&b(g[g.length-1],1);for(let f=0;f+1<g.length;f++){let y=g[f],u=g[f+1];if(Math.abs(y.y-u.y)<1e-6)continue;let d=y.p??1,h=u.p??1,l=y.cz??0,c=u.cz??0;for(let z=0;z<a;z++){let x=Q0(o,y.y,l,y.rx,y.rz,z,a,d,e,p),k=Q0(o,y.y,l,y.rx,y.rz,z+1,a,d,e,p),w=Q0(o,u.y,c,u.rx,u.rz,z+1,a,h,e,p),M=Q0(o,u.y,c,u.rx,u.rz,z,a,h,e,p),L=k[0]-x[0],P=k[1]-x[1],S=k[2]-x[2],A=M[0]-x[0],_=M[1]-x[1],R=M[2]-x[2],T=P*R-S*_,v=S*A-L*R,C=L*_-P*A,I=Math.hypot(T,v,C)||1;T/=I,v/=I,C/=I;let X=(x[0]+w[0])/2-o,t0=(x[2]+w[2])/2-(l+c)/2;T*X+C*t0<0&&(T=-T,v=-v,C=-C),t.push({p:[...x,...k,...w,...M],n:[T,v,C],g:s})}}}var $1=(t,r)=>t[Math.max(0,Math.min(t.length-1,Math.round(r*(t.length-1))))];function J(t,r,o=.05,a=.15){let n=Math.hypot(.5,.55,.67);for(let e of t){let p=Math.max(o,(e.n[0]*.5+e.n[1]*.55+e.n[2]*.67)/n*.9+a);e._i=Math.min(1,p),e.c=$1(r,e._i),e.r=r,e.fb=[o,a]}return t}function g0(t,r,o,a,s,m){let i=o[0]-r[0],n=o[1]-r[1],e=o[2]-r[2],p=s[0]-r[0],g=s[1]-r[1],b=s[2]-r[2],f=n*b-e*g,y=e*p-i*b,u=i*g-n*p,d=Math.hypot(f,y,u)||1;t.push({p:[...r,...o,...a,...s],n:[f/d,y/d,u/d],g:m})}function h0(t,r,o=\"body\"){return(a,s,m,i,n)=>{g0(t,a,s,m,i,o);let e=t[t.length-1];e.c=[...$1(r,n)],e._i=n,e.r=r,e.fixed=n}}function O0({side:t=.66,under:r=.3,back:o=.7,flank:a=.85}={}){return(s,m,i,n,e,p,g,b=.95,f=t,y=r)=>{s([m,p,g],[e,p,g],[e,i,g],[m,i,g],f),s([e,p,n],[m,p,n],[m,i,n],[e,i,n],f*o),s([e,p,g],[e,p,n],[e,i,n],[e,i,g],f*a),s([m,p,n],[m,p,g],[m,i,g],[m,i,n],f*a),s([m,p,n],[e,p,n],[e,p,g],[m,p,g],b),s([m,i,g],[e,i,g],[e,i,n],[m,i,n],y)}}var O=.9;function s0(t){for(let r of t)for(let o=0;o<4;o++)r.p[o*3+1]*=O;return t}var L0=-.235,o0=.9,R0=-2.4+Math.PI/4,C0=Object.freeze([{y:.72,rx:.026,rz:.027},{y:.74,rx:.028,rz:.029},{y:.9,rx:.028,rz:.029},{y:1.058,rx:.026,rz:.027}].map(Object.freeze));function H0(t,r,o){let a=Math.cos(o),s=Math.sin(o);for(let m of t){for(let e=0;e<4;e++){let p=m.p[e*3+1]-r,g=m.p[e*3+2];m.p[e*3+1]=r+a*p-s*g,m.p[e*3+2]=s*p+a*g}let i=m.n[1],n=m.n[2];m.n[1]=a*i-s*n,m.n[2]=s*i+a*n}return t}var S0=[[1.55,.072,.084],[1.5,.205,.09],[1.45,.215,.088],[1.4,.215,.096],[1.3,.208,.098],[1.2,.182,.092],[1.05,.16,.082],[.95,.168,.088],[.9,.178,.1],[.85,.202,.12],[.8,.202,.12],[.75,.194,.114],[.65,.178,.1],[.55,.168,.09],[.5,.154,.08],[.45,.158,.088],[.4,.152,.072],[.3,.166,.088],[.2,.142,.063],[.1,.137,.05]],tt=.038;function Tr(t){if(t>=S0[0][0])return[S0[0][1],S0[0][2]];if(t<=S0[S0.length-1][0]){let r=S0[S0.length-1];return[r[1],r[2]]}for(let r=0;r+1<S0.length;r++){let o=S0[r],a=S0[r+1];if(t<=o[0]&&t>=a[0]){let s=(t-o[0])/(a[0]-o[0]);return[o[1]+(a[1]-o[1])*s,o[2]+(a[2]-o[2])*s]}}return[.2,.1]}function P1(t,r,o){let[a,s]=Tr(t);return[Math.max(r,a+tt),Math.max(o,s+tt)]}function z0(t,r,o,a,s,m){let i=[],n=Math.max(6,Math.min(24,Math.round((t-r)/.055)));for(let e=0;e<=n;e++){let p=e/n,g=t-p*(t-r),b=p*p,[f,y]=P1(g,o+b*(s-o),a+b*(m-a));i.push({y:g,rx:f,rz:y})}return i}function h1(t,r){let o=[],s=r?1.16:1,m=Math.max(6,Math.min(24,Math.round((1.5-t)/.055)));for(let i=0;i<=m;i++){let n=i/m,e=1.5-n*(1.5-t),p=Math.pow(n,.85),[g,b]=P1(e,(.088+p*.23)*s,(.1+p*.185)*s);o.push({y:e,rx:g,rz:b})}return o}var rt=[Math.PI/2+1.25,Math.PI/2-1.25+Math.PI*2];function vr(){let t=[];for(let s=0;s<=10;s++){let m=s/10,i=1.56-m*(1.56-1.04),n=Math.pow(m,.65),[e,p]=P1(i,.19+n*.07,.14+n*.085);t.push({y:i,rx:e,rz:p})}return t}var Dr=[Math.PI/2+.55,Math.PI/2-.55+Math.PI*2],Or={\"Short Skirt\":{rows:z0(.98,.56,.175,.115,.27,.19),wrap:!0,seg:24},\"Long Skirt\":{rows:z0(.98,.13,.175,.115,.32,.22),wrap:!0,seg:24},\"Casual Dress\":{rows:z0(1.32,.18,.15,.11,.32,.22),wrap:!0,seg:24},\"Strapless Dress\":{rows:z0(1.3,.2,.15,.11,.31,.215),wrap:!0,seg:24},\"Plain Robes\":{rows:z0(1.545,.12,.16,.12,.3,.21),wrap:!0,seg:26},\"Priest Robes\":{rows:z0(1.545,.11,.165,.125,.315,.22),wrap:!0,seg:26},\"Priestess Robes\":{rows:z0(1.545,.11,.165,.125,.315,.22),wrap:!0,seg:26},Kimono:{rows:z0(1.52,.22,.175,.135,.29,.22),wrap:!0,seg:26},\"Mummy Wrappings\":{rows:z0(1.56,.06,.155,.12,.19,.15),wrap:!0,seg:22},\"Casual Cloak\":{rows:h1(.45,!1),wrap:!1,seg:26,arc:rt},\"Formal Cloak\":{rows:h1(.36,!0),wrap:!1,seg:26,arc:rt},Wrap:{rows:vr(),wrap:!1,seg:28,arc:Dr},Toga:{rows:z0(1.52,.33,.15,.115,.255,.19),wrap:!1,seg:24,arc:[Math.PI*.12,Math.PI*.12+Math.PI*1.5]},\"Dwynnen Surcoat\":{rows:z0(1.5,.6,.15,.125,.205,.17),wrap:!0,seg:24},\"Anticlere Surcoat\":{rows:z0(1.5,.56,.15,.125,.208,.172),wrap:!0,seg:24}};var Fo=[...Object.keys(Or),\"Sash\"];function ot(t){let r=[];D(r,[{y:2.07,rx:.052,rz:.058,p:.78,cz:-.012},{y:2.01,rx:.108,rz:.122,p:.78,cz:-.016},{y:1.945,rx:.14,rz:.152,p:.78,cz:-.014},{y:1.885,rx:.144,rz:.156,p:.78,cz:-.008},{y:1.855,rx:.14,rz:.15,p:.78,cz:-.004}],{group:\"head\",seg:26,capBottom:!1});let a=[{y:1.855,rx:.142,rz:.15,p:.78,cz:-.004},{y:1.79,rx:.14,rz:.146,p:.78,cz:0},{y:1.74,rx:.132,rz:.138,p:.78,cz:.004}];return D(r,a,{group:\"head\",seg:8,arc:[-.6,.6]}),D(r,a,{group:\"head\",seg:8,arc:[Math.PI-.6,Math.PI+.6]}),s0(r),J(r,t)}var Cr=.235;function et(t){let r=[],a=s=>D(r,[{y:1.68,rx:.072,rz:.078,p:.6},{y:1.635,rx:.118,rz:.118,p:.6},{y:1.57,rx:.108,rz:.104,p:.6},{y:1.52,rx:.082,rz:.08,p:.6}],{cx:s*Cr,group:s<0?\"armL\":\"armR\",seg:20});return a(-1),a(1),s0(r),J(r,t)}function at(t,r=\"argonian\"){let o=[],a=(u,d,h,l)=>g0(o,u,d,h,l,\"body\"),s=(u,d,h)=>a(u,d,h,h),m=(r===\"khajiit\"?[{y:.93,z:.01,r:.04},{y:.968,z:-.068,r:.038},{y:1.004,z:-.14,r:.037},{y:1.024,z:-.22,r:.036},{y:1.01,z:-.302,r:.035},{y:.958,z:-.376,r:.034},{y:.882,z:-.436,r:.033},{y:.796,z:-.478,r:.031},{y:.706,z:-.498,r:.03},{y:.62,z:-.494,r:.029},{y:.544,z:-.468,r:.03},{y:.486,z:-.428,r:.033},{y:.452,z:-.38,r:.034},{y:.436,z:-.332,r:.024},{y:.43,z:-.292,r:.008}]:[{y:.93,z:.01,r:.104},{y:.958,z:-.078,r:.1},{y:.968,z:-.166,r:.093},{y:.94,z:-.252,r:.084},{y:.878,z:-.336,r:.074},{y:.792,z:-.416,r:.064},{y:.7,z:-.492,r:.055},{y:.624,z:-.568,r:.046},{y:.572,z:-.648,r:.037},{y:.538,z:-.73,r:.028},{y:.516,z:-.812,r:.019},{y:.502,z:-.892,r:.011},{y:.496,z:-.96,r:.004}]).map(u=>({x:0,...u})),i=m.map((u,d)=>{let h=m[Math.max(0,d-1)],l=m[Math.min(m.length-1,d+1)],c=[l.x-h.x,l.y-h.y,l.z-h.z],z=Math.hypot(...c)||1;return[c[0]/z,c[1]/z,c[2]/z]}),n=[1,0,0],e=i.map(u=>{let d=[n[1]*u[2]-n[2]*u[1],n[2]*u[0]-n[0]*u[2],n[0]*u[1]-n[1]*u[0]],h=Math.hypot(...d)||1;return d=[d[0]/h,d[1]/h,d[2]/h],{s:n,d}}),p=10,g=(u,d)=>{let h=m[u],{s:l,d:c}=e[u],z=d/p*2*Math.PI,x=Math.cos(z),k=Math.sin(z),w=h.r,M=h.r*1.06;return[h.x+w*x*l[0]+M*k*c[0],h.y+w*x*l[1]+M*k*c[1],h.z+w*x*l[2]+M*k*c[2]]},b=m.map((u,d)=>Array.from({length:p},(h,l)=>g(d,l)));for(let u=0;u+1<b.length;u++)for(let d=0;d<p;d++){let h=(d+1)%p;a(b[u][d],b[u+1][d],b[u+1][h],b[u][h])}let f=m[m.length-1],y=[f.x,f.y,f.z];for(let u=0;u<p;u++){let d=(u+1)%p;s(y,b[b.length-1][d],b[b.length-1][u])}if(r===\"argonian\"){for(let u=1;u+1<m.length;u++){let d=m[u],{d:h}=e[u],l=i[u],c=[d.x+h[0]*d.r,d.y+h[1]*d.r,d.z+h[2]*d.r],z=Math.max(.006,d.r*1.5),x=[c[0]+h[0]*z-l[0]*d.r*.5,c[1]+h[1]*z-l[1]*d.r*.5,c[2]+h[2]*z-l[2]*d.r*.5],k=Math.max(.003,d.r*.18),w=[c[0]+k,c[1],c[2]],M=[c[0]-k,c[1],c[2]],L=m[u+1],P=[L.x+e[u+1].d[0]*L.r,L.y+e[u+1].d[1]*L.r,L.z+e[u+1].d[2]*L.r];s(w,x,M),s(M,x,P),s(P,x,w)}for(let u=1;u+1<m.length;u+=1){let d=m[u],{s:h,d:l}=e[u],c=-1,z=[d.x+l[0]*c*d.r,d.y+l[1]*c*d.r,d.z+l[2]*c*d.r],x=d.r*.9,k=d.r*.1,w=[l[0]*c,l[1]*c,l[2]*c],M=[z[0]+h[0]*x+w[0]*k,z[1]+h[1]*x+w[1]*k,z[2]+h[2]*x+w[2]*k],L=[z[0]-h[0]*x+w[0]*k,z[1]-h[1]*x+w[1]*k,z[2]-h[2]*x+w[2]*k],P=m[u+1],S=e[u+1],A=[P.x+S.d[0]*c*P.r,P.y+S.d[1]*c*P.r,P.z+S.d[2]*c*P.r],_=P.r*.9,R=[A[0]+S.s[0]*_+S.d[0]*c*P.r*.1,A[1]+S.s[1]*_+S.d[1]*c*P.r*.1,A[2]+S.s[2]*_+S.d[2]*c*P.r*.1],T=[A[0]-S.s[0]*_+S.d[0]*c*P.r*.1,A[1]-S.s[1]*_+S.d[1]*c*P.r*.1,A[2]-S.s[2]*_+S.d[2]*c*P.r*.1];a(M,L,T,R)}for(let u=1;u+1<m.length;u++){let d=m[u],{s:h,d:l}=e[u],c=i[u];if(d.r<.02)continue;let z=u%2*.5;for(let x=0;x<p;x++){let k=x+z,w=k/p*2*Math.PI,M=Math.sin(w);if(Math.abs(M)>.7)continue;let L=Math.cos(w),P=Math.sin(w),S=L*h[0]+P*l[0],A=L*h[1]+P*l[1],_=L*h[2]+P*l[2],R=[d.x+d.r*S,d.y+d.r*A,d.z+d.r*_],T=Math.max(.004,d.r*.16),v=[R[0]+S*T,R[1]+A*T,R[2]+_*T],C=(k+.9)/p*2*Math.PI,I=Math.cos(C),X=Math.sin(C),t0=I*h[0]+X*l[0],e0=I*h[1]+X*l[1],u0=I*h[2]+X*l[2],b0=[d.x+d.r*t0,d.y+d.r*e0,d.z+d.r*u0],f0=[R[0]+c[0]*d.r*.5,R[1]+c[1]*d.r*.5,R[2]+c[2]*d.r*.5];s(R,b0,v),s(b0,f0,v),s(f0,R,v)}}}if(r===\"khajiit\")for(let u=1;u+1<m.length;u++){let d=m[u],{s:h,d:l}=e[u],c=i[u],z=u>m.length-5?1.7:1,x=7;for(let k=0;k<x;k++){let w=k/x*2*Math.PI+u%2*.4,M=Math.cos(w),L=Math.sin(w),P=M*h[0]+L*l[0],S=M*h[1]+L*l[1],A=M*h[2]+L*l[2],_=[d.x+d.r*P,d.y+d.r*S,d.z+d.r*A],R=d.r*.85*z,T=[_[0]+P*R-c[0]*R*.7,_[1]+S*R-c[1]*R*.7,_[2]+A*R-c[2]*R*.7],v=w+.5,C=Math.cos(v),I=Math.sin(v),X=C*h[0]+I*l[0],t0=C*h[1]+I*l[1],e0=C*h[2]+I*l[2],u0=[d.x+d.r*X,d.y+d.r*t0,d.z+d.r*e0];s(_,u0,T)}}return J(o,t,.08,.18)}var Nr=[[52,36,34],[80,56,50],[110,78,68],[140,104,90],[170,132,114],[198,162,142]];function st(t=Nr,r={}){let{span:o=.55,at:a=1.34,fold:s=.2,fingers:m=4,droop:i=.24,rib:n=.012}=r,e=[],p=h0(e,t),g=a*O;for(let b of[-1,1]){let f=b*.1,y=-.02,u=[];for(let d=0;d<=m;d++){let h=d/m,l=(1-s)*(.95-h*.5)+s*.12,c=h*.9+s*.8,z=f+b*o*l,x=y-o*c*.75,k=g-i*(h*.7+.15)*(1-s*.5);u.push([z,k,x])}for(let d=0;d<m;d++){let h=u[d],l=u[d+1],c=.86-d/m*.36;p([f,g,y],h,l,[f,g,y],c);let z=[(h[0]+l[0])/2,(h[1]+l[1])/2-i*.12,(h[2]+l[2])/2];p(h,z,l,h,c*.72)}for(let d of u){let h=d[0]-f,l=d[1]-g,c=d[2]-y,z=Math.hypot(h,c)||1e-6,x=-c/z*n,k=h/z*n;p([f-x,g+n,y-k],[f+x,g+n,y+k],[f+h+x,g+l+n,y+c+k],[f+h-x,g+l+n,y+c-k],.95)}}return e}var Yr=[[92,84,66],[124,114,92],[156,146,120],[188,178,150],[214,206,180],[236,230,210]],Br=1.7,nt=1.74,ct=1.79,mt=1.85,Wr=.082,Fr=.105,Xr=.102,jr=.13;function it(t=Yr,{jaw:r=1,size:o=1}={}){let a=[],s=(p,g,b,f)=>g0(a,p,g,b,f,\"head\"),m=(Br+nt)/2*O,i=Wr*.62*r,n=Fr*.72*r,e=(ct-nt)*O*1.45*o;for(let p of[-1,1]){let g=[{x:p*i,y:m,z:n,r:.019*o},{x:p*i*1.12,y:m+e*.38,z:n*.98,r:.016*o},{x:p*i*1.3,y:m+e*.74,z:n*.88,r:.011*o},{x:p*i*1.46,y:m+e,z:n*.74,r:.0025*o}],b=u=>[[u.x-u.r,u.y,u.z-u.r],[u.x+u.r,u.y,u.z-u.r],[u.x+u.r,u.y,u.z+u.r],[u.x-u.r,u.y,u.z+u.r]],f=g.map(b);for(let u=0;u+1<f.length;u++)for(let d=0;d<4;d++){let h=(d+1)%4;s(f[u][d],f[u][h],f[u+1][h],f[u+1][d])}let y=f[0];s(y[3],y[2],y[1],y[0])}return J(a,t,.08,.18)}function lt(t,{skull:r=1,jut:o=1}={}){let a=[],s=(y,u,d,h)=>g0(a,y,u,d,h,\"head\"),m=(ct+mt)/2*O,i=(mt+.02)*O,n=Xr*.94*r,e=jr*.86*r,p=e+.03*o*r,g=5,b=y=>-n+2*n*(y/g),f=y=>p-(Math.abs(y)/n)**2*(p-e)*.55;for(let y=0;y<g;y++){let u=b(y),d=b(y+1),h=f(u),l=f(d);s([u,m,h],[d,m,l],[d,i,l],[u,i,h]),s([u,m,e],[d,m,e],[d,m,l],[u,m,h]),s([u,i,h],[d,i,l],[d,i,e],[u,i,e])}for(let y of[-1,1]){let u=y*n,d=f(u);s([u,m,e],[u,m,d],[u,i,d],[u,i,e])}return J(a,t,.08,.18)}var Gr={brown:[[22,15,10],[40,28,18],[60,44,28],[84,62,42]],black:[[12,10,12],[24,20,24],[38,33,40],[54,48,56]],blonde:[[70,54,28],[104,82,44],[140,114,66],[178,150,96]]};function A1(t=Gr.brown,r=\"Human\",o=null,a=\"short\"){let s=[];if((r===\"Human\"||r===\"Elf\")&&a!==\"bald\"){let g=(y,u)=>{D(s,[{y:2.055,rx:.058,rz:.064,p:.8,cz:-.012},{y:2,rx:.114,rz:.128,p:.8,cz:-.018},{y:1.945,rx:.146,rz:.158,p:.8,cz:-.018},{y:1.91,rx:.148,rz:.16,p:.8,cz:-.014}],{group:\"head\",seg:26,capBottom:!1});let d=[{y:1.91,rx:.148,rz:.16,p:.8,cz:-.014},{y:1.82,rx:.146+u,rz:.156+u,p:.8,cz:-.02}],h=Math.max(1,Math.round((1.82-y)/.09));for(let c=1;c<=h;c++){let z=c/h,x=1.82-z*(1.82-y);d.push({y:x,rx:(.146+u)*(1-.28*z),rz:(.156+u)*(1-.2*z),p:.8,cz:-.02-z*.02})}let l=1;D(s,d,{group:\"head\",seg:22,arc:[Math.PI/2+l,Math.PI/2-l+Math.PI*2]})},b=y=>{let u=y.map((l,c)=>{let z=y[Math.max(0,c-1)],x=y[Math.min(y.length-1,c+1)],k=[x.x-z.x,x.y-z.y,x.z-z.z],w=Math.hypot(...k)||1;return[k[0]/w,k[1]/w,k[2]/w]}),d=[1,0,0],h=y.map((l,c)=>{let z=u[c],x=[z[1]*d[2]-z[2]*d[1],z[2]*d[0]-z[0]*d[2],z[0]*d[1]-z[1]*d[0]],k=Math.hypot(...x)||1;x=[x[0]/k,x[1]/k,x[2]/k];let w=8,M=[];for(let L=0;L<w;L++){let P=L/w*2*Math.PI,S=Math.cos(P)*l.r,A=Math.sin(P)*l.r;M.push([l.x+S*d[0]+A*x[0],l.y+S*d[1]+A*x[1],l.z+S*d[2]+A*x[2]])}return M});for(let l=0;l+1<h.length;l++)for(let c=0;c<8;c++){let z=(c+1)%8;f(h[l][c],h[l][z],h[l+1][z],h[l+1][c])}},f=(y,u,d,h)=>g0(s,y,u,d,h,\"head\");if(a===\"short\")g(1.74,0);else if(a===\"buzz\")g(1.8,-.02);else if(a===\"medium\")g(1.64,.006);else if(a===\"long\")g(1.47,.012);else if(a===\"ponytail\")g(1.76,0),b([{x:0,y:1.86,z:-.15,r:.03},{x:0,y:1.8,z:-.21,r:.032},{x:0,y:1.7,z:-.25,r:.03},{x:0,y:1.58,z:-.256,r:.026},{x:0,y:1.47,z:-.236,r:.02},{x:0,y:1.39,z:-.2,r:.01}]);else if(a===\"topknot\")g(1.78,0),b([{x:0,y:2.055,z:-.01,r:.03},{x:0,y:2.11,z:-.01,r:.044},{x:0,y:2.15,z:-.012,r:.036},{x:0,y:2.168,z:-.014,r:.014}]);else if(a===\"mohawk\"){let y=[[1.905,.15],[1.995,.09],[2.055,0],[2.045,-.09],[1.965,-.165]];for(let u=0;u+1<y.length;u++){let[d,h]=y[u],[l,c]=y[u+1],z=.075,x=.075,k=.02;f([-k,d,h],[k,d,h],[k,d+z,h],[-k,d+z,h]),f([-k,d,h],[-k,d+z,h],[-k,l+x,c],[-k,l,c]),f([k,d,h],[k,l,c],[k,l+x,c],[k,d+z,h]),f([-k,d+z,h],[k,d+z,h],[k,l+x,c],[-k,l+x,c])}}else g(1.74,0)}J(s,t,.1,.2);let n=[],e=(g,b,f,y)=>g0(n,g,b,f,y,\"head\"),p=(g,b=6)=>{let f=g.map((y,u)=>{let d=g[Math.max(0,u-1)],h=g[Math.min(g.length-1,u+1)],l=h.x-d.x,c=h.y-d.y,z=h.z-d.z,x=Math.hypot(l,c,z)||1;l/=x,c/=x,z/=x;let k=0,w=1,M=0,L=k*l+w*c+M*z;k-=L*l,w-=L*c,M-=L*z;let P=Math.hypot(k,w,M)||1;k/=P,w/=P,M/=P;let S=c*M-z*w,A=z*k-l*M,_=l*w-c*k,R=[];for(let T=0;T<b;T++){let v=T/b*2*Math.PI,C=Math.cos(v)*y.r,I=Math.sin(v)*y.r;R.push([y.x+C*k+I*S,y.y+C*w+I*A,y.z+C*M+I*_])}return R});for(let y=0;y+1<f.length;y++)for(let u=0;u<b;u++){let d=(u+1)%b;e(f[y][u],f[y][d],f[y+1][d],f[y+1][u])}};if(r===\"Elf\"){let g=b=>{let f=[{o:.086,y:1.836,z:.006,w:.028},{o:.116,y:1.874,z:-.01,w:.034},{o:.15,y:1.912,z:-.03,w:.036},{o:.17,y:1.95,z:-.058,w:.03},{o:.178,y:1.984,z:-.088,w:.018},{o:.176,y:2.008,z:-.112,w:.006}],y=h=>{let l=b*h.o,c=b*(h.o-.028);return[[l,h.y,h.z+h.w],[c,h.y,h.z+h.w*.45],[c,h.y,h.z-h.w*.45],[l,h.y,h.z-h.w]]},u=f.map(y);for(let h=0;h+1<u.length;h++)for(let l=0;l<3;l++)e(u[h][l],u[h][l+1],u[h+1][l+1],u[h+1][l]);for(let h=0;h+1<u.length;h++)e(u[h][0],u[h][3],u[h+1][3],u[h+1][0]);let d=u[0];e([b*.07,1.858,.03],d[0],d[3],[b*.07,1.82,-.03])};g(-1),g(1)}else if(r===\"Khajiit\"){let b=(M,L,P,S)=>{let A=[];for(let R=0;R<10;R++){let T=R/10*2*Math.PI;A.push([Math.cos(T)*P,M+Math.sin(T)*S,L])}return A},f=[b(1.826,.096,.078,.044),b(1.82,.114,.066,.038),b(1.814,.126,.05,.03),b(1.808,.134,.03,.021),b(1.803,.138,.014,.011)];for(let M=0;M+1<f.length;M++)for(let L=0;L<10;L++){let P=(L+1)%10;e(f[M][L],f[M][P],f[M+1][P],f[M+1][L])}let y=[0,1.806,.184];for(let M=0;M<10;M++){let L=(M+1)%10;e(y,f[4][L],f[4][M],f[4][M])}let u=1.826,d=.134,h=.016,l=[-h,u,d],c=[h,u,d],z=[0,u-.02,d+.008];e(l,c,z,z);let x=M=>{let L=[M*.042,1.972,.034],P=[M*.086,1.958,-.058],S=[M*.06,1.95,-.012],A=[M*.108,2.088,-.03];e(L,A,S,S),e(S,A,P,P),e(P,A,L,L),e(L,S,P,P)};x(-1),x(1),e([-.086,1.898,.126],[.086,1.898,.126],[.086*.86,1.898-.02,.126+.02],[-.086*.86,1.898-.02,.126+.02]),e([-.086*.86,1.898-.02,.126+.02],[.086*.86,1.898-.02,.126+.02],[.086*.7,1.898-.03,.126-.02],[-.086*.7,1.898-.03,.126-.02]);let k=(M,L)=>{let P=1.77-L*.028,S=M*(.086-L*.004),A=.052-L*.014,_=[M*(.128+L*.006),P-.034,A-.03];e([S,P+.018,A],_,[S,P-.018,A-.006],[S,P-.018,A-.006])};for(let M of[-1,1])for(let L=0;L<3;L++)k(M,L);let w=(M,L)=>{let P=1.822-L*.016,S=.0022,A=[M*.052,P,.176-.03],_=[M*(.15+L*.024),P-.006-L*.01,.176-.07-L*.026];e([A[0],A[1]+S,A[2]],[_[0],_[1],_[2]],[_[0],_[1],_[2]],[A[0],A[1]-S,A[2]])};for(let M of[-1,1])for(let L=0;L<3;L++)w(M,L)}else if(r===\"Argonian\"){let g=[[1.82,.045,.046],[1.78,.082,.09],[1.72,.1,.115],[1.66,.108,.125],[1.62,.102,.125],[1.56,.082,.11]],b=c=>{for(let z=0;z+1<g.length;z++){let x=g[z],k=g[z+1];if(c<=x[0]&&c>=k[0]){let w=(c-x[0])/(k[0]-x[0]);return[x[1]+(k[1]-x[1])*w,x[2]+(k[2]-x[2])*w]}}return c>g[0][0]?[g[0][1],g[0][2]]:[g[5][1],g[5][2]]},f=(c,z,x,k,w,M)=>{let L=z/.9,P=w,S=-k,A=(I,X)=>[c+P*I,L+X,x+S*I],_=[c+k*.016,L,x+w*.016],R=A(-M,-M),T=A(M,-M),v=A(M,M),C=A(-M,M);e(R,T,_,_),e(T,v,_,_),e(v,C,_,_),e(C,R,_,_)},y=0;for(let c=1.59;c<=1.795;c+=.032,y++){let[z,x]=b(c),k=y%2*.5;for(let w=-3+k;w<=3;w+=1){let M=w*.4;if(Math.abs(M)>1.3)continue;let L=Math.sin(M),P=z*L*.97,S=x*Math.cos(M)*.98,A=Math.hypot(L,Math.cos(M))||1;f(P,c,S,L/A,Math.cos(M)/A,.013)}}let u=(c,z,x,k)=>{let w=[];for(let M=0;M<8;M++){let L=M/8*2*Math.PI;w.push([Math.cos(L)*x,c+Math.sin(L)*k,z])}return w},d=[u(1.816,.104,.066,.048),u(1.808,.124,.054,.038),u(1.8,.138,.042,.028),u(1.794,.148,.028,.018),u(1.79,.152,.014,.01)];for(let c=0;c+1<d.length;c++)for(let z=0;z<8;z++){let x=(z+1)%8;e(d[c][z],d[c][x],d[c+1][x],d[c+1][z])}{let c=[0,1.79,.154];for(let z=0;z<8;z++){let x=(z+1)%8;e(c,d[4][x],d[4][z],d[4][z])}}for(let c of[-1,1]){let z=c*.016;e([z-.006,1.812,.146],[z+.006,1.812,.146],[z+.004,1.8,.15],[z-.004,1.8,.15])}let h=c=>p([{x:c*.066,y:1.892,z:0,r:.036},{x:c*.088,y:1.922,z:-.058,r:.029},{x:c*.092,y:1.93,z:-.124,r:.021},{x:c*.08,y:1.918,z:-.186,r:.012},{x:c*.062,y:1.894,z:-.238,r:.005}]);h(-1),h(1);for(let c=0;c<7;c++){let z=c/6,x=2.035-z*.34,k=0-z*.14,w=.075-z*.03,M=.02-z*.008;e([-M,x,k],[M,x,k],[0,x+w,k-.055],[0,x+w,k-.055]),e([M,x,k],[0,x+w,k-.055],[-M,x,k],[-M,x,k])}for(let c=0;c<5;c++){let z=(c-2)/2*.096,x=1.878,k=.126,w=.022,M=.018;e([z-w,x-M*.5,k-.01],[z+w,x-M*.5,k-.01],[z+w*.5,x+M*.5,k+.02],[z-w*.5,x+M*.5,k+.02]),e([z-w*.5,x+M*.5,k+.02],[z+w*.5,x+M*.5,k+.02],[z,x+M,k-.005],[z,x+M,k-.005])}for(let c=0;c<3;c++){let z=1.858-c*.026,x=.116+c*.012,k=.012,w=.02;e([-k,z,x],[k,z,x],[0,z+w*.4,x+.03],[0,z+w*.4,x+.03])}let l=c=>{let z=[[c*.092,1.836,-.044],[c*.086,1.768,-.082],[c*.074,1.704,-.104]],x=[[c*.118,1.844,-.108],[c*.126,1.768,-.14],[c*.114,1.704,-.166]];for(let k=0;k+1<z.length;k++)e(z[k],x[k],x[k+1],z[k+1])};l(-1),l(1)}return n.length&&s.push(...J(n,o||t,.1,.2)),s0(s),s}var ut=[[38,30,22],[62,50,36],[88,72,52],[116,96,70],[146,122,92],[176,150,116]],v0=O0();function ft(t=ut,r={}){let{back:o=.62,len:a=.9,girth:s=.17,legs:m=.05,crouch:i=.3,head:n=.12,snout:e=.1,tail:p=.3,tailUp:g=.3}=r,b=[],f=h0(b,t),y=o*O,u=y-s*(1.5-i*.5),d=a/2;for(let[x,k,w]of[[d*.55,d*.05,.86],[d*.1,-d*.45,1],[-d*.4,-d,.84]])v0(f,-s*w,u,k,s*w,y,x);let h=d*.55,l=h+n*1.4,c=y-s*.15+n*.2,z=s*.42;if(v0(f,-z,y-s*.7,h,z,y+n*.1,l-n*.5,.9,.62),v0(f,-n,c-n,l-n*.7,n,c+n*.6,l+n*.5,.95,.66),e>0){let x=n*.5;v0(f,-x,c-n*.75,l+n*.4,x,c+n*.05,l+n*.4+e,.85,.58)}for(let[x,k]of[[-1,d*.4],[1,d*.4],[-1,-d*.6],[1,-d*.6]]){let w=x*s*.66;v0(f,w-m,.02*O,k-m,w+m,u+.02,k+m,.7,.6,.35),v0(f,w-m*1.5,0,k-m*1.4,w+m*1.5,.03*O,k+m*1.8,.4,.34,.24)}if(p>0){let x=s*.16,k=u+(y-u)*(.4+g*.5);v0(f,-x,k-x,-d-p,x,k+x,-d+.02,.6,.5,.3)}return b}function pt(t=ut,r={}){let{len:o=.3,up:a=.3,thick:s=.03,at:m=.98}=r,i=[],n=h0(i,t),e=m*O,p=(1-a)*o*.5;return v0(n,-s,e-s,-o*.55,s,e+s,-.01,.7,.55,.34),v0(n,-s*.7,e-s*.7-p,-o,s*.7,e+s*.7-p,-o*.5,.62,.48,.3),i}var dt=[[34,32,30],[56,53,50],[82,78,74],[110,105,100],[140,134,128],[170,164,157]],c1=O0({under:.32,back:.68,flank:.86});function gt(t=dt,r={}){let{skull:o=.1,snout:a=.14,depth:s=.6,ears:m=1,tusks:i=0}=r,n=[],e=h0(n,t,\"head\"),g=m1*O+o*.55;c1(e,-o,g-o*.9,-o*.9,o,g+o*.9,o*.7);let b=o*(.42+s*.34),f=g-o*.36;if(c1(e,-b,f-o*s*.9,o*.5,b,f+o*.34,o*.5+a,.88,.6),c1(e,-b*.7,f-o*s*.75,o*.5+a,b*.7,f+o*.12,o*.5+a+o*.12,.3,.24,.18),m>0){let y=o*.26,u=o*1.1*m;for(let d of[-1,1]){let h=d*o*.6;c1(e,h-y,g+o*.7,-o*.5,h+y,g+o*.7+u,-o*.5+y*1.6,.9,.58)}}if(i>0){let y=o*.09,u=o*.7*i;for(let d of[-1,1]){let h=d*b*.7;c1(e,h-y,f-o*s*.7,o*.5+a*.55,h+y,f-o*s*.7+u,o*.5+a*.55+y*2,1,.86,.6)}}return n}function ht(t=dt,r={}){let{len:o=.22,thick:a=.022,sweep:s=.7,skull:m=.1}=r,i=[],n=h0(i,t,\"head\"),e=m1*O+m*.55,p=4;for(let g of[-1,1]){let b=g*m*.72,f=e+m*.5,y=-m*.2;for(let u=1;u<=p;u++){let d=u/p,h=d*Math.PI*.55*s,l=b+g*o*.1*d,c=f+Math.cos(h)*(o/p),z=y-Math.sin(h)*(o/p)*1.4,x=a*(1.1-d*.75);n([b-x,f,y+x],[b+x,f,y+x],[l+x,c,z+x],[l-x,c,z+x],.92),n([b+x,f,y-x],[b-x,f,y-x],[l-x,c,z-x],[l+x,c,z-x],.5),n([b+x,f,y+x],[b+x,f,y-x],[l+x,c,z-x],[l+x,c,z+x],.72),n([b-x,f,y-x],[b-x,f,y+x],[l-x,c,z+x],[l-x,c,z-x],.72),b=l,f=c,y=z}}return i}var yt=[[44,38,34],[70,60,52],[98,84,72],[128,110,94],[158,138,118],[186,166,144]],R1=O0({side:.64,flank:.86});function S1(t,r,o,a,s,m,i,n,e){let p=s-r,g=i-a,b=Math.hypot(p,g)||1e-6,f=-g/b*n,y=p/b*n;t([r-f,o+n,a-y],[r+f,o+n,a+y],[s+f,m+n,i+y],[s-f,m+n,i-y],e),t([r+f,o-n,a+y],[r-f,o-n,a-y],[s-f,m-n,i-y],[s+f,m-n,i+y],e*.4),t([r+f,o+n,a+y],[r+f,o-n,a+y],[s+f,m-n,i+y],[s+f,m+n,i+y],e*.72),t([r-f,o-n,a-y],[r-f,o+n,a-y],[s-f,m+n,i-y],[s-f,m-n,i-y],e*.72)}function bt(t=yt,r={}){let{span:o=.34,ride:a=.16,thorax:s=.1,abdomen:m=.13,legR:i=.016,arch:n=.5,claws:e=0,sting:p=0,legPairs:g=4}=r,b=[],f=h0(b,t),y=a*O;R1(f,-s,y-s*.7,0,s,y+s*.7,s*1.7,.92,.62),R1(f,-m,y-m*.75,-m*2,m,y+m*.8,-m*.1,.88,.58);let u=g;for(let d of[-1,1])for(let h=0;h<u;h++){let l=u>1?h/(u-1):.5,c=.75-l*1.9,z=d*Math.cos(c),x=Math.sin(c),k=d*s*.8,w=s*(1.1-l*1.1),M=k+z*o*.5,L=w+x*o*.5,P=y+o*n*.55,S=k+z*o,A=w+x*o;S1(f,k,y,w,M,P,L,i,.8),S1(f,M,P,L,S,.01*O,A,i*.75,.62)}if(e>0){let d=s*.34*e;for(let h of[-1,1]){let l=h*s*1.1;S1(f,h*s*.7,y,s*1.4,l,y,s*2.4,i*1.4,.74),R1(f,l-d,y-d*.7,s*2.3,l+d,y+d*.7,s*2.3+d*2.4,.9,.6)}}return b}function xt(t=yt,r={}){let{sting:o=.5,abdomen:a=.1,ride:s=.11,legR:m=.015,strike:i=0}=r,n=[],e=h0(n,t),p=s*O,g=5,b=0,f=p+a*.4,y=-a*1.9;for(let d=1;d<=g;d++){let h=d/g,l=h*Math.PI*.72,c=h*Math.PI*.16,z=l+(c-l)*i,x=1+i*.5,k=0,w=f+Math.sin(z)*o*.34*(1-i*.55),M=y+((1-Math.cos(z))*o*.2+o*.06)*x+i*o*.42*h,L=m*(2.1-h*1.2);e([b-L,f,y+L],[b+L,f,y+L],[k+L,w,M+L],[k-L,w,M+L],.9),e([b+L,f,y-L],[b-L,f,y-L],[k-L,w,M-L],[k+L,w,M-L],.46),e([b+L,f,y+L],[b+L,f,y-L],[k+L,w,M-L],[k+L,w,M+L],.7),e([b-L,f,y-L],[b-L,f,y+L],[k-L,w,M+L],[k-L,w,M-L],.7),b=k,f=w,y=M}let u=m*1.4;return e([b-u,f-u,y],[b+u,f-u,y],[b+u,f+u,y+o*.16],[b-u,f+u,y+o*.16],1),n}var Kr=[[46,60,54],[70,92,82],[98,124,110],[128,154,138],[158,182,166],[188,208,194]];function zt(t=Kr,r={}){let{len:o=.9,girth:a=.14,at:s=.5,from:m=0,fin:i=.16,dorsal:n=.1,jaw:e=0}=r,p=[],g=h0(p,t),b=s*O,f=m,y=6,u=h=>{let l=h/y,c=a*(.45+Math.sin((.2+l*.8)*Math.PI)*.75)*(1-l*.72);return{z:f-o*l,w:c}};for(let h=0;h<y;h++){let l=u(h),c=u(h+1),z=.92-h/y*.3;g([0,b+l.w,l.z],[l.w,b,l.z],[c.w,b,c.z],[0,b+c.w,c.z],z),g([-l.w,b,l.z],[0,b+l.w,l.z],[0,b+c.w,c.z],[-c.w,b,c.z],z*.82),g([l.w,b,l.z],[0,b-l.w,l.z],[0,b-c.w,c.z],[c.w,b,c.z],z*.5),g([0,b-l.w,l.z],[-l.w,b,l.z],[-c.w,b,c.z],[0,b-c.w,c.z],z*.44)}let d=f-o;for(let h of[1,-1])g([0,b,d],[0,b+h*i,d-i*1.1],[0,b+h*i*.25,d-i*1.5],[0,b,d],h>0?.9:.6);if(n>0){let h=f-o*.3;g([0,b+a*.5,h+o*.16],[0,b+a*.5+n,h],[0,b+a*.5,h-o*.2],[0,b+a*.5,h+o*.16],.86)}if(e>0){let h=a*.5*e;g([0,b+h*.2,f],[h,b-h*.6,f+h*1.6],[-h,b-h*.6,f+h*1.6],[0,b+h*.2,f],1),g([0,b-h*.4,f],[-h,b-h*1.1,f+h*1.3],[h,b-h*1.1,f+h*1.3],[0,b-h*.4,f],.42)}return p}var Ur=[[46,30,20],[72,48,30],[102,68,42],[132,92,58],[162,118,78],[190,148,104]],kt=1.02,Vr=.62,y1=O0();function wt(t=Ur,{girth:r=1,len:o=1,legs:a=1}={}){let s=[],m=h0(s,t),i=.19*r,n=Vr*o,e=kt*O,p=(kt-.42)*O,g=[[.02,-.2,1],[-.2,-.46,1.06],[-.46,-n,.92]];for(let[u,d,h]of g)y1(m,-i*h,p,d,i*h,e,u);let b=.05*a,f=0;for(let[u,d]of[[-1,-.12],[1,-.12],[-1,-n+.1],[1,-n+.1]]){let h=u*i*.62;y1(m,h-b,f+.06*O,d-b,h+b,p+.02,d+b,.7,.6,.35),y1(m,h-b*1.5,f,d-b*1.5,h+b*1.5,f+.06*O,d+b*1.5,.32,.26,.2)}let y=.035*r;return y1(m,-y,p+.04,-n-.06,y,e-.04,-n+.02,.5,.42,.3),s}function Zr(t,r){let o=[],a={group:\"armL\",cx:L0,seg:8},s=r.gripR??.018,m;r.twoHand?(m=C0[0].y,D(o,C0,{...a,capTop:!1,capBottom:!1}),D(o,[{y:1.058,rx:.014,rz:.014},{y:1.082,rx:.038,rz:.038},{y:1.108,rx:.02,rz:.02}],a)):(m=r.gripLo??.79,D(o,[{y:m,rx:s,rz:s+.001},{y:.9,rx:s+.0015,rz:s+.0025},{y:.998,rx:s,rz:s+.001}],{...a,capTop:!1,capBottom:!1}),D(o,[{y:.998,rx:.01,rz:.01},{y:1.018,rx:r.pommelR??.028,rz:r.pommelR??.028},{y:1.042,rx:.014,rz:.014}],a)),D(o,[{y:m-.024,rx:r.guardT,rz:r.guardW,p:.45},{y:m-.002,rx:r.guardT+.002,rz:r.guardW+.004,p:.45}],a);let i=m-.03,n=r.blade,e=7,p=[];for(let g=0;g<=e;g++){let b=g/e;p.push({y:i-n*b,rx:r.thick*(1-.45*b),rz:r.wRoot*(1-b)+(r.wTip??.01)*b,p:.55,cz:(r.curve??0)*Math.sin(Math.PI*b)})}return p.push({y:i-n-.028,rx:.003,rz:.003,p:.55,cz:0}),D(o,p,a),s0(J(H0(o,o0,R0),t))}var qr={Dagger:{blade:.3,wRoot:.03,thick:.008,guardW:.036,guardT:.01,pommelR:.023,gripLo:.81,gripR:.016},Tanto:{blade:.34,wRoot:.026,thick:.008,guardW:.024,guardT:.02,pommelR:.019,gripLo:.8,gripR:.016},Shortsword:{blade:.46,wRoot:.04,thick:.009,guardW:.052,guardT:.011},Wakazashi:{blade:.52,wRoot:.03,thick:.008,curve:.022,guardW:.03,guardT:.03,pommelR:.02},Broadsword:{blade:.62,wRoot:.058,thick:.01,guardW:.066,guardT:.012},Saber:{blade:.74,wRoot:.034,thick:.008,curve:.05,guardW:.048,guardT:.013},Katana:{blade:.8,wRoot:.03,thick:.008,curve:.035,guardW:.032,guardT:.032,pommelR:.019,gripLo:.76},Dai_Katana:{blade:1.05,wRoot:.034,thick:.009,curve:.05,guardW:.04,guardT:.04,twoHand:!0}};function Mt(t,r){return Zr(t,qr[r])}function Qr(t,r){let o=[],a={group:\"armL\",cx:L0,seg:8},s=r.shaftR??.02,m;r.twoHand?(m=C0[0].y,D(o,C0,{...a,capTop:!1,capBottom:!1})):(m=.79,D(o,[{y:.79,rx:.019,rz:.02},{y:.9,rx:.021,rz:.022},{y:.998,rx:.019,rz:.02}],{...a,capTop:!1,capBottom:!1}));let i=r.twoHand?1.058:.998;D(o,[{y:i,rx:s+.003,rz:s+.003},{y:i+(r.staffUp??.028),rx:r.staffUp?s:.006,rz:r.staffUp?s:.006}],a);let n=m-.02-r.shaft;if(D(o,[{y:m-.002,rx:s,rz:s},{y:n+.02,rx:s,rz:s}],{...a,capTop:!1,capBottom:!1}),r.head===\"mace\")D(o,[{y:n+.02,rx:.022,rz:.022},{y:n-.02,rx:.07,rz:.07},{y:n-.12,rx:.078,rz:.078},{y:n-.17,rx:.026,rz:.026}],a);else if(r.head===\"axe\"||r.head===\"waraxe\"){let e=r.head===\"waraxe\"?[.082,-.082]:[.075];for(let p of e)D(o,[{y:n+.08,rx:.013,rz:.082,p:.5,cz:p*.45},{y:n-.03,rx:.016,rz:.14,p:.5,cz:p},{y:n-.14,rx:.013,rz:.082,p:.5,cz:p*.45}],a);D(o,[{y:n+.02,rx:.016,rz:.016},{y:n-.05,rx:.012,rz:.012}],a)}else r.head===\"hammer\"?D(o,[{y:n+.04,rx:.06,rz:.06,cz:.026},{y:n-.08,rx:.068,rz:.068,cz:.032},{y:n-.13,rx:.03,rz:.03,cz:.016}],a):r.head===\"ball\"?(D(o,[{y:n+.02,rx:.009,rz:.009},{y:n-.1,rx:.009,rz:.009}],{...a,capTop:!1,capBottom:!1}),D(o,[{y:n-.1,rx:.016,rz:.016},{y:n-.16,rx:.055,rz:.055},{y:n-.22,rx:.018,rz:.018}],a)):D(o,[{y:n+.02,rx:s+.002,rz:s+.002},{y:n-.02,rx:.007,rz:.007}],a);return s0(J(H0(o,o0,R0),t))}var Jr={Mace:{shaft:.44,head:\"mace\"},Battle_Axe:{shaft:.56,head:\"axe\"},Staff:{shaft:.72,shaftR:.011,staffUp:.34,twoHand:!0},Flail:{shaft:.36,head:\"ball\",twoHand:!0},Warhammer:{shaft:.62,head:\"hammer\",twoHand:!0},War_Axe:{shaft:.64,head:\"waraxe\",twoHand:!0}};function Lt(t,r){return Qr(t,Jr[r])}var $r=-Math.PI/2,b1=.3;function Ht(t,r){let o=[],a={group:\"armL\",cx:L0,seg:8},s=o0-r,m=o0+r,i=p=>-.05+.082*Math.sin(Math.PI*p),n=[],e=8;for(let p=0;p<=e;p++){let g=p/e,b=s+(m-s)*g,f=.009+.016*Math.sin(Math.PI*g);n.push({y:b,rx:f,rz:f+.004,cz:i(g)})}return n.push({y:o0,rx:.028,rz:.032,cz:i(.5)}),D(o,n,a),D(o,[{y:s+.012,rx:.0035,rz:.0035,cz:-.05},{y:o0-.02,rx:.0035,rz:.0035,cz:b1-.012}],{...a,seg:6,capTop:!1,capBottom:!1}),D(o,[{y:o0+.02,rx:.0035,rz:.0035,cz:b1-.012},{y:m-.012,rx:.0035,rz:.0035,cz:-.05}],{...a,seg:6,capTop:!1,capBottom:!1}),D(o,[{y:o0-.012,rx:.007,rz:.007,cz:b1},{y:o0+.012,rx:.007,rz:.007,cz:b1}],{...a,seg:8,capTop:!0,capBottom:!0}),s0(J(H0(o,o0,$r),t))}function Pt(t){return Ht(t,.62)}function At(t){return Ht(t,.44)}function Rt(t){let r=[],o={group:\"armL\",cx:L0,seg:10};return D(r,[{y:1.058,rx:.016,rz:.016},{y:1.078,rx:.03,rz:.03},{y:1.104,rx:.022,rz:.022}],o),D(r,C0,{...o,capTop:!1,capBottom:!1}),D(r,[{y:.694,rx:.018,rz:.118,p:.45},{y:.724,rx:.02,rz:.126,p:.45}],o),D(r,[{y:.694,rx:.01,rz:.036,p:.55},{y:.42,rx:.0095,rz:.034,p:.55},{y:.12,rx:.009,rz:.03,p:.55},{y:-.15,rx:.007,rz:.02,p:.55},{y:-.256,rx:.003,rz:.003,p:.55}],{...o,capTop:!1}),s0(J(H0(r,o0,R0),t))}function St(t){let r=[],o={group:\"armL\",cx:L0,seg:10};return D(r,[{y:.998,rx:.014,rz:.014},{y:1.018,rx:.024,rz:.024},{y:1.042,rx:.018,rz:.018}],o),D(r,[{y:.79,rx:.021,rz:.022},{y:.9,rx:.023,rz:.024},{y:.998,rx:.021,rz:.022}],{...o,capTop:!1,capBottom:!1}),D(r,[{y:.766,rx:.015,rz:.084,p:.45},{y:.792,rx:.017,rz:.09,p:.45}],o),D(r,[{y:.766,rx:.009,rz:.03,p:.55},{y:.56,rx:.0085,rz:.028,p:.55},{y:.34,rx:.008,rz:.024,p:.55},{y:.15,rx:.006,rz:.016,p:.55},{y:.068,rx:.003,rz:.003,p:.55}],{...o,capTop:!1}),s0(J(H0(r,o0,R0),t))}function i1(t,r=!0){for(let o of t)for(let a=0;a<4;a++)o.p[a*3+1]/=O;r&&H0(t,o0,-R0);for(let o of t)for(let a=0;a<4;a++)o.p[a*3]-=L0,o.p[a*3+1]=-(o.p[a*3+1]-o0),o.p[a*3]=-o.p[a*3];for(let o of t)for(let a=0;a<4;a++)o.p[a*3+1]*=O;return t}function to(t,r=1,o=null,a=null){if(a)for(let s of t)for(let m=0;m<4;m++){let i=s.p[m*3],n=s.p[m*3+1],e=s.p[m*3+2],p,g;a[0]&&(p=Math.cos(a[0]),g=Math.sin(a[0]),[n,e]=[p*n-g*e,g*n+p*e]),a[1]&&(p=Math.cos(a[1]),g=Math.sin(a[1]),[i,e]=[p*i+g*e,-g*i+p*e]),a[2]&&(p=Math.cos(a[2]),g=Math.sin(a[2]),[i,n]=[p*i-g*n,g*i+p*n]),s.p[m*3]=i,s.p[m*3+1]=n,s.p[m*3+2]=e}for(let s of t)for(let m=0;m<4;m++)s.p[m*3]=s.p[m*3]*r+(o?.[0]??0),s.p[m*3+1]=s.p[m*3+1]*r+(o?.[1]??0)*O,s.p[m*3+2]=s.p[m*3+2]*r+(o?.[2]??0);return t}var $=[[40,40,40],[80,80,80],[120,120,120],[160,160,160],[200,200,200],[240,240,240]],l1=(t,r)=>t===\"arm\"?r<0?\"armL\":\"armR\":t===\"leg\"?r<0?\"legL\":\"legR\":t||\"body\",ro=(t,r)=>{for(let o of t)o._mat=o._mat||r;return t},oo={torso:{groups:[\"body\"]},pelvis:{groups:[\"body\"]},neck:{groups:[\"body\"]},head:{groups:[\"head\"]},sleeves:{groups:[\"armL\",\"armR\"],arm:!0},hands:{groups:[\"armL\",\"armR\"],arm:!0},legs:{groups:[\"legL\",\"legR\"],leg:!0},feet:{groups:[\"legL\",\"legR\"],leg:!0}};function eo(t){let r=oo[t.zone];if(!r)return null;let o=t.yLo??{hands:.8,feet:0,neck:1.62,head:1.7}[t.zone]??0,a=t.yHi??{hands:.99,feet:.16,neck:1.75,head:2.1}[t.zone]??2.2;return{...r,yLo:o,yHi:a,th:t.th??.008,zScale:t.zScale??1,zLo:t.zLo,zHi:t.zHi,mat:t.mat||\"dye\"}}function ao(t,r,o,a=6){let s=r.map(([e,p,g,b])=>({x:e,y:p*O,z:g,r:b})),m=s.map((e,p)=>{let g=s[Math.max(0,p-1)],b=s[Math.min(s.length-1,p+1)],f=[b.x-g.x,b.y-g.y,b.z-g.z],y=Math.hypot(...f)||1;f=f.map(c=>c/y);let u=Math.abs(f[1])>.9?[1,0,0]:[0,1,0],d=u[0]*f[0]+u[1]*f[1]+u[2]*f[2];u=[u[0]-d*f[0],u[1]-d*f[1],u[2]-d*f[2]];let h=Math.hypot(...u)||1;u=u.map(c=>c/h);let l=[f[1]*u[2]-f[2]*u[1],f[2]*u[0]-f[0]*u[2],f[0]*u[1]-f[1]*u[0]];return Array.from({length:a},(c,z)=>{let x=z/a*Math.PI*2,k=Math.cos(x)*e.r,w=Math.sin(x)*e.r;return[e.x+k*u[0]+w*l[0],e.y+k*u[1]+w*l[1],e.z+k*u[2]+w*l[2]]})});for(let e=0;e+1<m.length;e++)for(let p=0;p<a;p++){let g=(p+1)%a;g0(t,m[e][p],m[e][g],m[e+1][g],m[e+1][p],o)}let i=(e,p)=>{for(let g=0;g<a;g++)g0(t,p,e[(g+1)%a],e[g],e[g],o)};i(m[0],[s[0].x,s[0].y,s[0].z]);let n=s[s.length-1];i(m[m.length-1],[n.x,n.y,n.z])}function so(t,[r,o,a,s,m,i],n){o*=O,m*=O;let e=(p,g,b,f)=>g0(t,p,g,b,f,n);e([r,m,i],[s,m,i],[s,o,i],[r,o,i]),e([s,m,a],[r,m,a],[r,o,a],[s,o,a]),e([s,m,i],[s,m,a],[s,o,a],[s,o,i]),e([r,m,a],[r,m,i],[r,o,i],[r,o,a]),e([r,m,a],[s,m,a],[s,m,i],[r,m,i]),e([r,o,i],[s,o,i],[s,o,a],[r,o,a])}function no(t,r,o){let a=r.map(([s,m,i])=>[s,m*O,i]);for(let s=1;s+1<a.length;s++)g0(t,a[0],a[s],a[s+1],a[s+1],o)}var Et=t=>Array.isArray(t[0])?t.map(Et):[-t[0],...t.slice(1)];function J0(t,r={}){let o=[],a=[],s=[],m=t.mat||\"dye\",i=t.mirror?[1,-1]:[1];if(t.zone){let n=eo(t);return n&&s.push(n),{faces:o,marks:a,zones:s}}for(let n of i){let e=[];if(t.loft){let p=(t.cx??0)*n,g=t.arc&&n<0?[Math.PI-t.arc[1],Math.PI-t.arc[0]]:t.arc;D(e,t.loft.map(([b,f,y,u=1,d=0])=>({y:b,rx:f,rz:y,p:u,cz:d})),{cx:p,seg:t.seg??12,group:l1(t.group,p||n),arc:g,capTop:t.caps!==!1,capBottom:t.caps!==!1}),s0(e)}else if(t.tube){let p=n<0?t.tube.map(([g,...b])=>[-g,...b]):t.tube;ao(e,p,l1(t.group,p[0][0]||n),t.sides??6)}else if(t.box){let[p,g,b,f,y,u]=t.box,d=n<0?[-f,g,b,-p,y,u]:t.box;so(e,d,l1(t.group,(d[0]+d[3])/2||n))}else if(t.fan){let p=n<0?Et(t.fan):t.fan;no(e,p,l1(t.group,p[0][0]||n))}else if(t.drape){let p=t.drape,g=p.kind===\"cape\"?h1(p.hem??.45,!!p.wide):z0(p.top??.98,p.hem??.56,...p.flare||[.175,.115,.27,.19]),b=p.kind===\"cape\"?[Math.PI/2+1.25,Math.PI/2-1.25+Math.PI*2]:p.arc;D(e,g.map(f=>({...f,p:1})),{seg:p.seg??16,group:\"body\",arc:b,capTop:!1,capBottom:!1}),s0(e)}else if(t.mark){let[p,g,b]=t.at,f=[p*n,g*O,b];e.push({p:[...f,...f,...f,...f],n:t.n||[0,0,1],g:l1(t.group||\"head\",f[0]),mark:t.mark,free:!!t.free,slack:t.slack,r:$})}else if(t.dfo){if(n<0)continue;let[p,g]=t.dfo.split(\":\"),b=t.opts||{};if(p===\"helm\")e.push(...ot($));else if(p===\"pauldrons\")e.push(...et($));else if(p===\"tail\")e.push(...at($,g||\"argonian\"));else if(p===\"wings\")e.push(...st($,b));else if(p===\"tusks\")e.push(...it($,{jaw:r.build?.jaw??1,...b}));else if(p===\"brow\")e.push(...lt($,{skull:r.build?.skull??1,...b}));else if(p===\"hair\")e.push(...A1($,\"Human\",null,g||\"short\"));else if(p===\"race\"){let f=A1($,g,$,\"bald\");e.push(...f)}else{let f=p===\"beast\"?ft($,b):p===\"beastTail\"?pt($,b):p===\"beastHead\"?gt($,b):p===\"horns\"?ht($,b):p===\"arachnid\"?bt($,b):p===\"sting\"?xt($,b):p===\"fish\"?zt($,b):p===\"horse\"?wt($,b):p===\"blade\"?i1(Mt($,g)):p===\"hafted\"?i1(Lt($,g)):p===\"bow\"?i1(g===\"short\"?At($):Pt($),!1):p===\"claymore\"?i1(Rt($)):p===\"sword\"?i1(St($)):[];for(let y of f)y.g=t.group||y.g,y.r||J([y],$);e.push(...to(f,t.scale??1,t.at,t.rot))}}!t.mark&&!t.dfo&&J(e,$,t.floor??.08,t.bias??.16);for(let p of e)p.mark?a.push(p):o.push(p)}ro(o,m);for(let n of a)n._mat=m;return{faces:o,marks:a,zones:s}}var mo=t=>{let r=parseInt(t.slice(1),16);return[r>>16,r>>8&255,r&255]};function co([t,r,o]){t/=255,r/=255,o/=255;let a=Math.max(t,r,o),s=Math.min(t,r,o),m=(a+s)/2;if(a===s)return[0,0,m];let i=a-s,n=m>.5?i/(2-a-s):i/(a+s);return[(a===t?(r-o)/i+(r<o?6:0):a===r?(o-t)/i+2:(t-r)/i+4)*60,n,m]}function io([t,r,o]){if(t=(t%360+360)%360/360,!r)return[o,o,o].map(i=>Math.round(i*255));let a=o<.5?o*(1+r):o+r-o*r,s=2*o-a,m=i=>(i=(i+1)%1,i<1/6?s+(a-s)*6*i:i<.5?a:i<2/3?s+(a-s)*(2/3-i)*6:s);return[m(t+1/3),m(t),m(t-1/3)].map(i=>Math.round(Math.max(0,Math.min(1,i))*255))}var It=(t,r,o)=>{let a=(r-t+540)%360-180;return t+a*o};function k0(t,{lift:r=0,spread:o=1}={}){let[a,s,m]=co(typeof t==\"string\"?mo(t):t),i=[];for(let n=0;n<6;n++){let e=n/5,p=(e-.62)*o,g=Math.max(.05,Math.min(.9,m+r+p*(p<0?.85:.55))),b=p<0?It(a,262,-p*.35):It(a,48,p*.25),f=Math.max(0,Math.min(1,s*(p<0?1+-p*.25:1-p*.35)));i.push(io([b,f,g]))}return i}var x1=[[\"Ferro\",\"#7c7f88\"],[\"Steelite\",\"#9aa6b8\"],[\"Silvered\",\"#c9d0dc\"],[\"Lumen\",\"#b9d9b0\"],[\"Duranium\",\"#c79a5c\"],[\"Mithrite\",\"#9fc6e8\"],[\"Adamant\",\"#7fa39a\"],[\"Obsidian\",\"#3c3550\"],[\"Orkan\",\"#6f8a4e\"],[\"Voidsteel\",\"#9a3c4a\"]],_t={gold:k0(\"#e6c880\"),pearl:k0(\"#f2e6dc\",{lift:.04}),glow:k0(\"#88e4d9\",{lift:.05,spread:.8}),dark:k0(\"#3a2f4c\"),ivory:k0(\"#e4dcc0\"),white:k0(\"#fbebce\",{lift:.08,spread:.5}),ink:[[33,23,46],[33,23,46],[40,30,56],[40,30,56],[48,38,64],[48,38,64]],glass:k0(\"#9fe8ff\",{lift:.06}),membrane:k0(\"#8a6a90\")};var Tt=[33,23,46],vt=(t,r)=>t[Math.max(0,Math.min(t.length-1,Math.round(r*(t.length-1))))];function u1(t=0,r=.1){let o=Math.cos(t),a=Math.sin(t),s=Math.cos(r),m=Math.sin(r);return(i,n,e)=>{let p=o*i+a*e,g=-a*i+o*e;return[p,s*n-m*g,m*n+s*g]}}function $0(t,{w:r=64,h:o=96,scale:a=36,ox:s=32,oy:m=84,yaw:i=0,pitch:n=.1,pos:e=null,light:p=[-.5,.55,.67],outline:g=Tt,marks:b=[]}={}){let f=new Uint8ClampedArray(r*o*4),y=new Float32Array(r*o).fill(-1/0),u=u1(i,n),d=Math.hypot(...p),h=p.map(z=>z/d),l=new Float32Array(12),c=(z,x,k,w)=>{let M=l[z],L=l[z+1],P=l[z+2],S=l[x],A=l[x+1],_=l[x+2],R=l[k],T=l[k+1],v=l[k+2],C=(S-M)*(T-L)-(A-L)*(R-M);if(Math.abs(C)<1e-9)return;let I=Math.max(0,Math.floor(Math.min(M,S,R))),X=Math.min(r-1,Math.ceil(Math.max(M,S,R))),t0=Math.max(0,Math.floor(Math.min(L,A,T))),e0=Math.min(o-1,Math.ceil(Math.max(L,A,T)));for(let u0=t0;u0<=e0;u0++)for(let b0=I;b0<=X;b0++){let f0=b0+.5,M0=u0+.5,p0=(S-f0)*(T-M0)-(A-M0)*(R-f0),P0=(R-f0)*(L-M0)-(T-M0)*(M-f0),_0=(M-f0)*(A-M0)-(L-M0)*(S-f0);if(C<0&&(p0=-p0,P0=-P0,_0=-_0),p0<0||P0<0||_0<0)continue;let T0=Math.abs(C),x0=(p0*P+P0*_+_0*v)/T0,n0=u0*r+b0;if(x0<=y[n0])continue;y[n0]=x0;let Z=n0*4;f[Z]=w[0],f[Z+1]=w[1],f[Z+2]=w[2],f[Z+3]=255}};for(let z=0;z<t.length;z++){let x=t[z],k=e?e.subarray(z*12,z*12+12):x.p;for(let M=0;M<4;M++){let[L,P,S]=u(k[M*3],k[M*3+1],k[M*3+2]);l[M*3]=s+L*a,l[M*3+1]=m-P*a,l[M*3+2]=S}let w;if(x.fixed!=null)w=vt(x.r,x.fixed);else if(x.r){let M=k[3]-k[0],L=k[4]-k[1],P=k[5]-k[2],S=k[9]-k[0],A=k[10]-k[1],_=k[11]-k[2];Math.abs(S)+Math.abs(A)+Math.abs(_)<1e-9&&(S=k[6]-k[0],A=k[7]-k[1],_=k[8]-k[2]);let R=[L*_-P*A,P*S-M*_,M*A-L*S],T=Math.hypot(...R)||1;R=u(R[0]/T,R[1]/T,R[2]/T),R[2]<0&&(R=[-R[0],-R[1],-R[2]]);let[v,C]=x.fb||[.08,.15],I=Math.max(v,(R[0]*h[0]+R[1]*h[1]+R[2]*h[2])*.9+C)*(x.ao??1);w=vt(x.r,Math.min(1,Math.max(.04,I)))}else w=x.c||Tt;c(0,3,6,w),c(0,6,9,w)}for(let z of b){let[x,k,w]=u(...z.at);if((z.n?u(...z.n):[0,0,1])[2]<(z.minFacing??.15))continue;let L=Math.floor(s+x*a),P=Math.floor(m-k*a),S=P*r+L;if(!(L<0||P<0||L>=r||P>=o||y[S]>w+(z.slack??.05)))for(let[A,_,R]of z.px){let T=L+A,v=P+_;if(T<0||v<0||T>=r||v>=o)continue;let C=v*r+T,I=C*4;!f[I+3]&&!z.free||(f[I]=R[0],f[I+1]=R[1],f[I+2]=R[2],f[I+3]=255,y[C]=Math.max(y[C],w))}}if(g){let z=new Uint8Array(r*o);for(let x=0;x<r*o;x++)z[x]=f[x*4+3]?1:0;for(let x=0;x<o;x++)for(let k=0;k<r;k++){let w=x*r+k;if(!z[w]&&(k>0&&z[w-1]||k<r-1&&z[w+1]||x>0&&z[w-r]||x<o-1&&z[w+r])){let M=w*4;f[M]=g[0],f[M+1]=g[1],f[M+2]=g[2],f[M+3]=255}}}return{data:f,w:r,h:o,depth:y}}var Dt=[[],[{layer:\"armor\",mat:\"metal\",zone:\"torso\",yLo:1.12,yHi:1.62,th:.022},{box:[-.025,1.2,.112,.025,1.56,.135],group:\"body\",mat:\"accent\"},{mark:\"gem\",at:[0,1.5,.14],group:\"body\",mat:\"glow\"},{layer:\"armor\",mat:\"metal\",zone:\"neck\",yLo:1.62,yHi:1.72,th:.02}],[{dfo:\"pauldrons\",mat:\"metal\"},{box:[.19,1.6,-.085,.335,1.7,.085],mirror:!0,group:\"arm\",mat:\"metal\"},{box:[.3,1.46,-.075,.35,1.62,.075],mirror:!0,group:\"arm\",mat:\"metal\"},{box:[.2,1.7,-.03,.32,1.725,.03],mirror:!0,group:\"arm\",mat:\"accent\"},{mark:\"gem\",at:[.255,1.64,.05],mirror:!0,group:\"arm\",mat:\"accent\"}],[{layer:\"armor\",mat:\"metal\",zone:\"hands\",th:.018},{layer:\"armor\",mat:\"metal\",zone:\"sleeves\",yLo:.83,yHi:1.22,th:.018,group:\"arm\"},{loft:[[1.1,.07,.07],[1.15,.07,.07]],cx:.235,mirror:!0,group:\"arm\",mat:\"accent\",seg:10,caps:!1}],[{layer:\"armor\",mat:\"metal\",zone:\"legs\",yLo:.56,yHi:1,th:.022,zScale:.28},{layer:\"armor\",mat:\"metal\",zone:\"pelvis\",yLo:.9,yHi:1.14,th:.022},{box:[.04,.46,.06,.125,.58,.13],mirror:!0,group:\"leg\",mat:\"metal\"}],[{layer:\"armor\",mat:\"metal\",zone:\"feet\",yLo:0,yHi:.42,th:.02},{loft:[[.4,.12,.105],[.45,.12,.105]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{box:[.045,0,.17,.12,.07,.25],mirror:!0,group:\"leg\",mat:\"accent\"}],[{dfo:\"helm\",mat:\"metal\"},{loft:[[1.92,.125,.15,1,-.014],[2,.13,.17,1,.01],[2.08,.12,.17,1,.03],[2.12,.1,.14,1,.03]],group:\"head\",mat:\"glass\",seg:14,arc:[.5,2.64],caps:!1},{layer:\"armor\",mat:\"metal\",zone:\"neck\",yLo:1.62,yHi:1.72,th:.02}],[{dfo:\"helm\",mat:\"metal\"},{loft:[[1.72,.12,.148,1,-.014],[1.82,.123,.152,1,-.014],[1.94,.12,.148,1,-.014]],group:\"head\",mat:\"glass\",seg:14,arc:[.45,2.69],caps:!1},{loft:[[1.7,.122,.15,1,-.014],[1.73,.122,.15,1,-.014]],group:\"head\",mat:\"accent\",seg:14,arc:[.45,2.69],caps:!1},{layer:\"armor\",mat:\"metal\",zone:\"neck\",yLo:1.62,yHi:1.72,th:.02}],[{layer:\"armor\",mat:\"metal\",zone:\"torso\",yLo:1.12,yHi:1.62,th:.022},{box:[-.025,1.2,.112,.025,1.56,.135],group:\"body\",mat:\"accent\"},{mark:\"gem\",at:[0,1.5,.14],group:\"body\",mat:\"glow\"},{dfo:\"pauldrons\",mat:\"metal\"},{box:[.19,1.6,-.085,.335,1.7,.085],mirror:!0,group:\"arm\",mat:\"metal\"},{box:[.3,1.46,-.075,.35,1.62,.075],mirror:!0,group:\"arm\",mat:\"metal\"},{box:[.2,1.7,-.03,.32,1.725,.03],mirror:!0,group:\"arm\",mat:\"accent\"},{layer:\"armor\",mat:\"metal\",zone:\"hands\",th:.018},{layer:\"armor\",mat:\"metal\",zone:\"sleeves\",yLo:.83,yHi:1.22,th:.018,group:\"arm\"},{loft:[[1.1,.07,.07],[1.15,.07,.07]],cx:.235,mirror:!0,group:\"arm\",mat:\"accent\",seg:10,caps:!1}],[{layer:\"armor\",mat:\"metal\",zone:\"torso\",yLo:1.12,yHi:1.62,th:.022},{box:[-.025,1.2,.112,.025,1.56,.135],group:\"body\",mat:\"accent\"},{dfo:\"pauldrons\",mat:\"metal\"},{box:[.19,1.6,-.085,.335,1.7,.085],mirror:!0,group:\"arm\",mat:\"metal\"},{layer:\"armor\",mat:\"metal\",zone:\"hands\",th:.018},{layer:\"armor\",mat:\"metal\",zone:\"sleeves\",yLo:.83,yHi:1.22,th:.018,group:\"arm\"},{layer:\"armor\",mat:\"metal\",zone:\"legs\",yLo:.56,yHi:1,th:.022,zScale:.28},{layer:\"armor\",mat:\"metal\",zone:\"pelvis\",yLo:.9,yHi:1.14,th:.022},{layer:\"armor\",mat:\"metal\",zone:\"feet\",yLo:0,yHi:.42,th:.02},{loft:[[.4,.12,.105],[.45,.12,.105]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{dfo:\"helm\",mat:\"metal\"},{loft:[[1.72,.12,.148,1,-.014],[1.82,.123,.152,1,-.014],[1.94,.12,.148,1,-.014]],group:\"head\",mat:\"glass\",seg:14,arc:[.45,2.69],caps:!1},{loft:[[1.7,.122,.15,1,-.014],[1.73,.122,.15,1,-.014]],group:\"head\",mat:\"accent\",seg:14,arc:[.45,2.69],caps:!1}]];var Ot=[[{zone:\"sleeves\",yLo:.99,yHi:1.08,th:.018,mat:\"trim\"}],[{zone:\"sleeves\",yLo:1,yHi:1.6,th:.012,mat:\"dye\"},{loft:[[.88,.14,.14],[.95,.115,.115],[1.03,.075,.075],[1.12,.06,.06]],cx:.235,seg:12,caps:!1,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[1.01,.082,.082],[1.05,.082,.082]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"sleeves\",yLo:.98,yHi:1.1,th:.02,mat:\"metal\"},{box:[.16,.96,-.075,.31,1.12,.075],mat:\"metal\",mirror:!0,group:\"arm\"},{box:[.155,1.1,-.07,.315,1.13,.07],mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"stud\",at:[.235,1.04,.08],mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"stud\",at:[.19,1.04,.08],mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"sleeves\",yLo:.99,yHi:1.64,th:.008,mat:\"white\"},{loft:[[.93,.115,.115],[1,.085,.085],[1.06,.06,.06]],cx:.235,seg:12,caps:!1,mat:\"white\",mirror:!0,group:\"arm\"},{loft:[[1.1,.08,.08],[1.17,.08,.08]],cx:.235,seg:12,caps:!1,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[1.22,.082,.082],[1.29,.082,.082]],cx:.235,seg:12,caps:!1,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[1.48,.105,.105],[1.55,.105,.105]],cx:.235,seg:12,caps:!1,mat:\"trim\",mirror:!0,group:\"arm\"}],[{zone:\"sleeves\",yLo:.99,yHi:1.64,th:.012,mat:\"dye\"},{loft:[[1.05,.082,.082],[1.12,.082,.082]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{loft:[[1.19,.085,.085],[1.26,.085,.085]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{loft:[[1.4,.105,.105],[1.47,.105,.105]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{loft:[[1.54,.105,.105],[1.61,.105,.105]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"sleeves\",yLo:.99,yHi:1.64,th:.016,mat:\"glass\"},{loft:[[1.58,.09,.09],[1.42,.125,.125],[1.2,.13,.13],[1,.125,.125],[.92,.15,.15]],cx:.235,seg:12,caps:!1,mat:\"glass\",mirror:!0,group:\"arm\"},{loft:[[.92,.15,.15],[.95,.15,.15]],cx:.235,seg:12,caps:!1,mat:\"pearl\",mirror:!0,group:\"arm\"}],[{box:[.285,1.34,-.065,.335,1.58,.065],mat:\"metal\",mirror:!0,group:\"arm\"},{box:[.2,1.36,.06,.27,1.56,.09],mat:\"metal\",mirror:!0,group:\"arm\"},{box:[.27,1,-.055,.315,1.25,.055],mat:\"metal\",mirror:!0,group:\"arm\"},{loft:[[1.26,.05,.05],[1.31,.085,.085],[1.37,.05,.05]],cx:.26,seg:8,mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"stud\",at:[.335,1.48,0],n:[1,0,0],mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"stud\",at:[.315,1.12,0],n:[1,0,0],mat:\"accent\",mirror:!0,group:\"arm\"}],[{loft:[[1.36,.08,.08],[1.46,.125,.125],[1.56,.125,.12],[1.64,.08,.075]],cx:.235,seg:12,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[.88,.14,.14],[.95,.11,.11],[1.03,.07,.07],[1.1,.06,.06]],cx:.235,seg:12,caps:!1,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[1.3,.08,.08],[1.35,.08,.08]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"}],[{loft:[[1.02,.11,.11],[1.06,.11,.11]],cx:.235,seg:12,caps:!1,mat:\"glow\",mirror:!0,group:\"arm\"},{loft:[[1.13,.115,.115],[1.17,.115,.115]],cx:.235,seg:12,caps:!1,mat:\"glow\",mirror:!0,group:\"arm\"},{loft:[[1.24,.12,.12],[1.28,.12,.12]],cx:.235,seg:12,caps:!1,mat:\"glow\",mirror:!0,group:\"arm\"},{loft:[[1.45,.13,.13],[1.49,.13,.13]],cx:.235,seg:12,caps:!1,mat:\"glow\",mirror:!0,group:\"arm\"},{zone:\"sleeves\",yLo:.99,yHi:1.64,th:.006,mat:\"dark\"}],[{tube:[[.3,1.36,.04,.026],[.33,1.2,.064,.028],[.35,1,.1,.03],[.34,.8,.076,.03],[.36,.66,.052,.022]],sides:6,mat:\"trim\",mirror:!0,group:\"arm\"},{tube:[[.3,1.36,-.04,.026],[.33,1.2,-.064,.028],[.35,1,-.1,.03],[.34,.8,-.076,.03],[.36,.66,-.052,.022]],sides:6,mat:\"trim\",mirror:!0,group:\"arm\"},{loft:[[1.28,.075,.075],[1.35,.085,.085],[1.4,.075,.075]],cx:.235,seg:10,mat:\"accent\",mirror:!0,group:\"arm\"},{zone:\"sleeves\",yLo:.99,yHi:1.64,th:.008,mat:\"trim\"}]];var Ct=[[{tube:[[.08,1.55,-.1,.02],[.11,1.2,-.16,.022],[.13,.85,-.2,.02],[.14,.6,-.22,.012]],mirror:!0,group:\"body\",mat:\"trim\"}],[{drape:{kind:\"cape\",hem:.36,wide:!0},mat:\"dye\"},{loft:[[1.6,.2,.15],[1.68,.17,.13]],group:\"body\",mat:\"trim\",seg:12,caps:!1},{mark:\"stud\",at:[.1,1.6,.14],group:\"body\",mat:\"accent\",mirror:!0}],[{fan:[[.05,1.45,-.11],[.09,1.98,-.2],[.24,1.9,-.2],[.36,1.62,-.16],[.14,1.3,-.11]],mirror:!0,group:\"body\",mat:\"trim\"},{fan:[[.05,1.45,-.11],[.14,1.3,-.11],[.36,1.62,-.16],[.24,1.9,-.2],[.09,1.98,-.2]],mirror:!0,group:\"body\",mat:\"trim\"},{fan:[[.06,1.5,-.12],[.1,1.82,-.18],[.2,1.72,-.18],[.12,1.42,-.12]],mirror:!0,group:\"body\",mat:\"accent\"}],[{tube:[[.07,1.5,-.09,.028],[.21,1.725,-.15,.024],[.3,1.91,-.12,.02]],mirror:!0,group:\"body\",mat:\"accent\"},{loft:[[1.875,.008,.008,1,-.12],[1.95,.075,.075,1,-.12],[2.025,.008,.008,1,-.12]],cx:.3,mirror:!0,group:\"body\",mat:\"accent\",seg:8},{tube:[[.07,1.5,-.09,.028],[.28,1.59,-.15,.024],[.4,1.64,-.12,.02]],mirror:!0,group:\"body\",mat:\"accent\"},{loft:[[1.605,.008,.008,1,-.12],[1.68,.075,.075,1,-.12],[1.755,.008,.008,1,-.12]],cx:.4,mirror:!0,group:\"body\",mat:\"accent\",seg:8},{tube:[[.07,1.5,-.09,.028],[.14,1.79,-.15,.024],[.2,2.04,-.12,.02]],mirror:!0,group:\"body\",mat:\"accent\"},{loft:[[2.005,.008,.008,1,-.12],[2.08,.075,.075,1,-.12],[2.155,.008,.008,1,-.12]],cx:.2,mirror:!0,group:\"body\",mat:\"accent\",seg:8}],[{tube:[[0,1.62,-.1,.032],[0,1.64,-.2,.02],[0,1.67,-.3,.006]],group:\"body\",mat:\"ivory\"},{tube:[[0,1.5,-.1,.032],[0,1.52,-.193,.02],[0,1.55,-.285,.006]],group:\"body\",mat:\"ivory\"},{tube:[[0,1.38,-.1,.032],[0,1.4,-.185,.02],[0,1.43,-.27,.006]],group:\"body\",mat:\"ivory\"},{tube:[[0,1.26,-.1,.032],[0,1.28,-.178,.02],[0,1.31,-.255,.006]],group:\"body\",mat:\"ivory\"},{tube:[[0,1.14,-.1,.032],[0,1.16,-.17,.02],[0,1.19,-.24,.006]],group:\"body\",mat:\"ivory\"},{tube:[[0,1.02,-.1,.032],[0,1.04,-.163,.02],[0,1.07,-.225,.006]],group:\"body\",mat:\"ivory\"}],[{dfo:\"wings\",mat:\"membrane\",opts:{span:.85,at:1.62,fold:.1,fingers:4,droop:.42,rib:.02}}],[{fan:[[0,1.35,.15],[.19,1.63,.125],[.07,1.64,.125],[-.17,1.02,.14],[-.04,1,.145]],group:\"body\",mat:\"accent\"},{fan:[[0,1.35,-.14],[.19,1.63,-.115],[-.04,1,-.135],[-.17,1.02,-.13],[.07,1.64,-.115]],group:\"body\",mat:\"accent\"},{mark:\"gem\",at:[-.12,1.08,.15],group:\"body\",mat:\"white\"}],[{tube:[[.06,1.68,-.12,.03],[.22,1.56,-.15,.03],[.07,1.44,-.19,.03],[.24,1.32,-.15,.03],[.07,1.2,-.19,.03],[.24,1.08,-.15,.03],[.07,.96,-.18,.03]],mirror:!0,group:\"body\",mat:\"metal\"},{mark:\"gem\",at:[0,1.5,-.14],group:\"body\",mat:\"accent\"}],[{loft:[[1.695,.008,.008,1,-.03],[1.78,.085,.085,1,-.03],[1.865,.008,.008,1,-.03]],cx:-.36,group:\"body\",mat:\"accent\",seg:8},{loft:[[1.78,.132,.132,1,-.03],[1.81,.132,.132,1,-.03]],cx:-.36,group:\"body\",mat:\"trim\",seg:12,caps:!1},{loft:[[1.88,.008,.008,1,-.05],[1.98,.1,.1,1,-.05],[2.08,.008,.008,1,-.05]],cx:.37,group:\"body\",mat:\"accent\",seg:8},{loft:[[1.98,.155,.155,1,-.05],[2.01,.155,.155,1,-.05]],cx:.37,group:\"body\",mat:\"trim\",seg:12,caps:!1},{loft:[[1.46,.008,.008,1,-.1],[1.52,.06,.06,1,-.1],[1.58,.008,.008,1,-.1]],cx:.3,group:\"body\",mat:\"accent\",seg:8},{loft:[[1.52,.093,.093,1,-.1],[1.55,.093,.093,1,-.1]],cx:.3,group:\"body\",mat:\"trim\",seg:12,caps:!1}],[{loft:[[1.855,.008,.008,1,-.1],[1.925,.07,.07,1,-.1],[1.995,.008,.008,1,-.1]],cx:.351,group:\"body\",mat:\"glow\",seg:6},{loft:[[2.061,.008,.008,1,-.1],[2.131,.07,.07,1,-.1],[2.201,.008,.008,1,-.1]],cx:.145,group:\"body\",mat:\"glow\",seg:6},{loft:[[2.061,.008,.008,1,-.1],[2.131,.07,.07,1,-.1],[2.201,.008,.008,1,-.1]],cx:-.145,group:\"body\",mat:\"glow\",seg:6},{loft:[[1.855,.008,.008,1,-.1],[1.925,.07,.07,1,-.1],[1.995,.008,.008,1,-.1]],cx:-.351,group:\"body\",mat:\"glow\",seg:6},{loft:[[1.565,.008,.008,1,-.1],[1.635,.07,.07,1,-.1],[1.705,.008,.008,1,-.1]],cx:-.351,group:\"body\",mat:\"glow\",seg:6},{loft:[[1.359,.008,.008,1,-.1],[1.429,.07,.07,1,-.1],[1.499,.008,.008,1,-.1]],cx:-.145,group:\"body\",mat:\"glow\",seg:6},{loft:[[1.359,.008,.008,1,-.1],[1.429,.07,.07,1,-.1],[1.499,.008,.008,1,-.1]],cx:.145,group:\"body\",mat:\"glow\",seg:6},{loft:[[1.565,.008,.008,1,-.1],[1.635,.07,.07,1,-.1],[1.705,.008,.008,1,-.1]],cx:.351,group:\"body\",mat:\"glow\",seg:6}]];var Nt=[[],[{tube:[[.05,1.98,.07,.024],[.07,2.06,.1,.02],[.09,2.09,.18,.016]],mirror:!0,group:\"head\",mat:\"skinDark\"},{loft:[[2.075,.015,.015,1,.19],[2.105,.03,.03,1,.19],[2.135,.015,.015,1,.19]],cx:.09,mirror:!0,group:\"head\",mat:\"skinDark\",seg:8}],[{box:[-.016,2,.05,.016,2.1,.13],group:\"head\",mat:\"trim\"},{box:[-.016,2,-.04,.016,2.125,.05],group:\"head\",mat:\"trim\"},{box:[-.016,1.96,-.13,.016,2.1,-.04],group:\"head\",mat:\"trim\"},{box:[-.016,1.86,-.18,.016,2,-.13],group:\"head\",mat:\"trim\"}],[{tube:[[.1,1.97,-.02,.04],[.17,2.05,-.04,.034],[.23,2,-.04,.03],[.23,1.9,-.02,.024],[.18,1.85,0,.014]],mirror:!0,group:\"head\",mat:\"ivory\"}],[{tube:[[0,2,.086,.026],[0,2.115,.101,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[-.059,2,.048,.026],[-.073,2.115,.058,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[-.073,2,-.036,.026],[-.091,2.115,-.04,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[-.033,2,-.104,.026],[-.041,2.115,-.118,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[.033,2,-.104,.026],[.041,2.115,-.118,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[.073,2,-.036,.026],[.091,2.115,-.04,.006]],group:\"head\",mat:\"skinDark\"},{tube:[[.059,2,.048,.026],[.073,2.115,.058,.006]],group:\"head\",mat:\"skinDark\"}],[{fan:[[0,1.85,-.13],[-.1,2.1,-.17],[.1,2.1,-.17],[.21,1.92,-.22],[.15,1.7,-.2],[-.15,1.7,-.2],[-.21,1.92,-.22]],group:\"head\",mat:\"skin\"},{fan:[[.09,1.88,-.08],[.16,2.1,-.12],[.27,2,-.2],[.3,1.82,-.22],[.2,1.7,-.14]],mirror:!0,group:\"head\",mat:\"skin\"},{fan:[[.12,1.85,-.1],[.2,2.02,-.14],[.24,1.84,-.2]],mirror:!0,group:\"head\",mat:\"skinDark\"}],[{tube:[[.045,1.98,.02,.018],[.06,2.04,.04,.016]],mirror:!0,group:\"head\",mat:\"skinDark\"},{loft:[[2.045,.02,.02,1,.04],[2.085,.04,.04,1,.04],[2.125,.02,.02,1,.04]],cx:.06,mirror:!0,group:\"head\",mat:\"glow\",seg:10}],[{tube:[[.045,1.96,.08,.04],[.07,2.04,.14,.03],[.09,2.115,.2,.01]],mirror:!0,group:\"head\",mat:\"dark\"}],[{box:[-.014,2,.03,.014,2.1,.08],group:\"head\",mat:\"skinDark\"},{box:[-.014,2,-.04,.014,2.125,.01],group:\"head\",mat:\"skinDark\"},{box:[-.014,1.96,-.12,.014,2.09,-.07],group:\"head\",mat:\"skinDark\"},{box:[-.014,1.86,-.19,.014,1.99,-.14],group:\"head\",mat:\"skinDark\"},{box:[-.014,1.74,-.21,.014,1.85,-.16],group:\"head\",mat:\"skinDark\"}],[{loft:[[2.074,.013,.013,1,.1],[2.1,.026,.026,1,.1],[2.126,.013,.013,1,.1]],cx:0,group:\"head\",mat:\"glow\",seg:8},{loft:[[2.074,.013,.013,1,-.11],[2.1,.026,.026,1,-.11],[2.126,.013,.013,1,-.11]],cx:0,group:\"head\",mat:\"glow\",seg:8},{loft:[[2.074,.013,.013,1,-.01],[2.1,.026,.026,1,-.01],[2.126,.013,.013,1,-.01]],cx:.1,group:\"head\",mat:\"glow\",seg:8},{loft:[[2.074,.013,.013,1,-.01],[2.1,.026,.026,1,-.01],[2.126,.013,.013,1,-.01]],cx:-.1,group:\"head\",mat:\"glow\",seg:8},{tube:[[.04,1.99,.02,.018],[.05,2.04,0,.014]],mirror:!0,group:\"head\",mat:\"glow\"}]];var Yt=[[],[{tube:[[.07,1.82,.105,.014],[.118,1.87,.1,.012],[.178,1.93,.07,.009]],mirror:!0,group:\"head\",mat:\"accent\"},{tube:[[.075,1.78,.105,.014],[.13,1.79,.1,.012],[.19,1.8,.07,.009]],mirror:!0,group:\"head\",mat:\"accent\"},{tube:[[.07,1.74,.1,.014],[.118,1.72,.095,.012],[.178,1.68,.07,.009]],mirror:!0,group:\"head\",mat:\"accent\"}],[{loft:[[1.77,.1,.12,1,-.01],[1.7,.095,.125,1,0],[1.62,.07,.1,1,.01],[1.54,.03,.05,1,.03]],group:\"head\",mat:\"hair\",seg:14,arc:[.15,2.99],caps:!0}],[{fan:[[.1,1.76,0],[.16,1.93,-.02],[.18,1.8,-.03],[.26,1.88,-.04],[.23,1.74,-.04],[.3,1.74,-.05],[.22,1.64,-.04],[.16,1.62,-.02]],mirror:!0,group:\"head\",mat:\"trim\"},{fan:[[.1,1.74,0],[.2,1.7,-.03],[.17,1.56,-.03],[.1,1.6,-.01]],mirror:!0,group:\"head\",mat:\"accent\"}],[{loft:[[1.9,.02,.02,1,.125],[1.93,.03,.03,1,.125],[1.96,.02,.02,1,.125]],group:\"head\",mat:\"accent\",seg:6},{loft:[[1.79,.018,.018,1,.1],[1.82,.028,.028,1,.1],[1.85,.018,.018,1,.1]],cx:.085,mirror:!0,group:\"head\",mat:\"accent\",seg:6},{loft:[[1.74,.018,.018,1,.1],[1.765,.026,.026,1,.1],[1.79,.018,.018,1,.1]],cx:.115,mirror:!0,group:\"head\",mat:\"accent\",seg:6},{mark:\"dot\",at:[.07,1.74,.11],mirror:!0,mat:\"accent\"},{mark:\"dot\",at:[0,1.715,.1],mat:\"accent\"}],[{tube:[[.04,1.72,.08,.026],[.06,1.6,.1,.022],[.08,1.46,.09,.016]],mirror:!0,group:\"head\",mat:\"skinDark\"},{tube:[[.09,1.74,.04,.024],[.14,1.62,.05,.02],[.17,1.48,.04,.014]],mirror:!0,group:\"head\",mat:\"skinDark\"}],[{tube:[[.005,1.79,.125,.022],[.06,1.775,.125,.02],[.13,1.79,.1,.016],[.17,1.84,.07,.012]],mirror:!0,group:\"head\",mat:\"hair\"}],[{box:[-.1,1.655,.03,.1,1.72,.125],group:\"head\",mat:\"metal\"},{box:[.092,1.66,-.07,.125,1.8,.1],mirror:!0,group:\"head\",mat:\"metal\"},{mark:\"stud\",at:[.1,1.74,.101],mirror:!0,mat:\"accent\"},{mark:\"stud\",at:[.05,1.685,.126],mirror:!0,mat:\"accent\"},{mark:\"stud\",at:[0,1.685,.126],mat:\"accent\"}],[{loft:[[1.82,.115,.16,1,-.015],[1.74,.11,.165,1,-.01],[1.65,.09,.14,1,0]],group:\"head\",mat:\"pearl\",seg:16,arc:[.25,2.89],caps:!1},{loft:[[1.825,.117,.162,1,-.015],[1.835,.117,.162,1,-.015]],group:\"head\",mat:\"accent\",seg:16,arc:[.25,2.89],caps:!1}],[{loft:[[1.77,.012,.012,1,.105],[1.8,.022,.022,1,.105],[1.83,.012,.012,1,.105]],cx:.095,mirror:!0,group:\"head\",mat:\"glow\",seg:6},{loft:[[1.89,.01,.01,1,.125],[1.92,.018,.018,1,.125],[1.95,.01,.01,1,.125]],cx:0,group:\"head\",mat:\"glow\",seg:6},{mark:\"gem\",at:[.06,1.9,.125],mirror:!0,mat:\"glow\"},{mark:\"gem\",at:[.13,1.74,.07],mirror:!0,mat:\"glow\"}]];var Bt=[[{zone:\"feet\",yLo:0,yHi:.11,th:.01,mat:\"trim\"},{mirror:!0,group:\"leg\",box:[.06,0,.17,.104,.06,.24],mat:\"accent\"}],[{zone:\"feet\",yLo:0,yHi:.16,th:.02,mat:\"trim\"},{mirror:!0,group:\"leg\",box:[.025,0,-.085,.14,.085,.26],mat:\"dark\"},{mirror:!0,group:\"leg\",box:[.025,.06,-.085,.14,.075,.26],mat:\"accent\"},{mirror:!0,group:\"leg\",box:[.05,.085,.14,.115,.13,.24],mat:\"trim\"}],[{zone:\"feet\",yLo:0,yHi:.11,th:.012,mat:\"dye\"},{mirror:!0,group:\"leg\",tube:[[.082,.05,.2,.03],[.082,.07,.3,.032],[.082,.17,.36,.03],[.082,.28,.32,.027],[.082,.3,.24,.024]],mat:\"accent\",sides:8},{mirror:!0,group:\"leg\",loft:[[.28,.05,.05,1,.235],[.34,.05,.05,1,.235]],mat:\"gold\",seg:8}],[{zone:\"feet\",yLo:0,yHi:.42,th:.02,mat:\"dye\"},{mirror:!0,group:\"leg\",loft:[[.38,.118,.105],[.47,.118,.105]],cx:.082,mat:\"accent\",seg:12,caps:!1},{mirror:!0,group:\"leg\",box:[.04,0,.15,.125,.07,.245],mat:\"dark\"},{mirror:!0,group:\"leg\",box:[.04,0,-.07,.125,.06,-.03],mat:\"dark\"}],[{zone:\"feet\",yLo:0,yHi:.11,th:.012,mat:\"trim\"},{mirror:!0,group:\"leg\",loft:[[.09,.05,.02,1,.1],[.19,.06,.03,1,.12],[.3,.03,.02,1,.14],[.35,.005,.005,1,.15]],cx:.082,mat:\"trim\",seg:8},{mirror:!0,group:\"leg\",loft:[[.09,.02,.04],[.19,.03,.08],[.3,.02,.05],[.35,.005,.01]],cx:.15,mat:\"accent\",seg:8},{mirror:!0,group:\"leg\",loft:[[.09,.05,.02,1,-.07],[.19,.06,.03,1,-.09],[.3,.03,.02,1,-.11],[.35,.005,.005,1,-.12]],cx:.082,mat:\"trim\",seg:8},{mirror:!0,group:\"leg\",mark:\"gem\",at:[.082,.17,.152],mat:\"white\"}],[{zone:\"feet\",yLo:0,yHi:.12,th:.012,mat:\"dye\"},{mirror:!0,group:\"leg\",box:[.07,0,-.07,.1,.12,-.03],mat:\"accent\"},{mirror:!0,group:\"leg\",box:[.045,.1,-.06,.12,.135,0],mat:\"dye\"},{mirror:!0,group:\"leg\",box:[.05,0,.19,.115,.09,.245],mat:\"trim\"},{mirror:!0,group:\"leg\",loft:[[.12,.1,.09],[.15,.1,.09]],cx:.082,mat:\"accent\",seg:10,caps:!1}],[{zone:\"feet\",yLo:0,yHi:.5,th:.024,mat:\"metal\"},{mirror:!0,group:\"leg\",box:[.04,.45,.04,.125,.6,.14],mat:\"metal\"},{mirror:!0,group:\"leg\",box:[.05,0,.18,.115,.08,.25],mat:\"dark\"},{mirror:!0,group:\"leg\",loft:[[.18,.115,.1],[.22,.115,.1]],cx:.082,mat:\"dark\",seg:10,caps:!1},{mirror:!0,group:\"leg\",mark:\"gem\",at:[.082,.52,.145],mat:\"accent\"}],[{mirror:!0,group:\"leg\",loft:[[0,.1,.17],[.05,.13,.2],[.12,.12,.18],[.19,.09,.13]],cx:.082,mat:\"white\",seg:12},{mirror:!0,group:\"leg\",loft:[[.1,.12,.08],[.16,.14,.1],[.25,.1,.08]],cx:.082,mat:\"white\",seg:12},{mirror:!0,group:\"leg\",loft:[[0,.06,.04],[.04,.07,.06]],cx:.13,mat:\"accent\",seg:8}],[{zone:\"feet\",yLo:0,yHi:.2,th:.012,mat:\"dye\"},{mirror:!0,group:\"leg\",box:[.072,-.045,-.06,.092,-.005,.25],mat:\"glow\"},{mirror:!0,group:\"leg\",box:[.07,-.005,-.04,.094,.04,-.01],mat:\"metal\"},{mirror:!0,group:\"leg\",box:[.07,-.005,.17,.094,.04,.2],mat:\"metal\"},{mirror:!0,group:\"leg\",loft:[[.18,.1,.09],[.22,.1,.09]],cx:.082,mat:\"trim\",seg:10,caps:!1}],[{mirror:!0,group:\"leg\",loft:[[0,.07,.12,1,.08],[.06,.1,.16,1,.07],[.15,.1,.14,1,.05],[.24,.08,.1,1,0]],cx:.082,mat:\"pearl\",seg:14},{mirror:!0,group:\"leg\",loft:[[0,.1,.19,1,.07],[.035,.105,.2,1,.07]],cx:.082,mat:\"glow\",seg:14},{mirror:!0,group:\"leg\",loft:[[.22,.09,.1],[.26,.09,.1]],cx:.082,mat:\"accent\",seg:12,caps:!1}]];var Wt=[[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{tube:[[-.1,1.99,.08,.035],[-.03,2.03,.115,.04],[.06,2,.11,.035],[.11,1.93,.07,.025]],group:\"head\",mat:\"hair\"}],[{dfo:\"hair:mohawk\",mat:\"hair\"},{tube:[[0,2,-.02,.04],[0,2.07,-.1,.032],[0,2.1,-.19,.02]],group:\"head\",mat:\"hair\"},{tube:[[.05,1.98,-.07,.03],[.1,2.02,-.15,.022],[.14,2.03,-.22,.012]],mirror:!0,group:\"head\",mat:\"hair\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[1.66,.13,.15,.8,-.04],[1.76,.15,.17,.8,-.04],[1.9,.14,.17,.8,-.03]],group:\"head\",mat:\"hair\",seg:16,arc:[2.4,6.9],caps:!1},{loft:[[1.93,.115,.15,.9,-.01],[1.99,.115,.15,.9,-.01]],group:\"head\",mat:\"hair\",seg:16,arc:[.5,2.64],caps:!1}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[1.98,.05,.05],[2.05,.075,.075],[2.12,.05,.05]],cx:.13,mirror:!0,group:\"head\",mat:\"hair\",seg:10},{loft:[[2,.085,.085],[2.015,.085,.085]],cx:.13,mirror:!0,group:\"head\",mat:\"accent\",seg:10,caps:!1}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[1.5,.1,.07,.8,-.1],[1.7,.14,.12,.8,-.07],[1.9,.14,.16,.8,-.03]],group:\"head\",mat:\"hair\",seg:14,arc:[3.3,6.1],caps:!1},{tube:[[.1,1.95,.08,.04],[.14,1.8,.1,.045],[.15,1.6,.1,.045],[.12,1.4,.1,.04]],group:\"head\",mat:\"hair\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[2,.04,.04],[2.06,.06,.06],[2.12,.04,.04]],cx:0,group:\"head\",mat:\"hair\",seg:10},{loft:[[2,.04,.04],[2.06,.06,.06],[2.12,.04,.04]],cx:.09,mirror:!0,group:\"head\",mat:\"hair\",seg:10},{loft:[[1.98,.04,.04],[2.04,.055,.055],[2.1,.04,.04]],cx:.06,mirror:!0,group:\"head\",mat:\"hair\",seg:10},{mark:\"gem\",at:[0,2.12,0],mat:\"accent\"},{mark:\"gem\",at:[.09,2.12,0],mirror:!0,mat:\"accent\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{tube:[[0,2.02,.08,.04],[0,2.07,-.05,.045],[0,2.02,-.17,.04],[.02,1.9,-.26,.032],[.05,1.78,-.28,.022]],group:\"head\",mat:\"hair\"},{tube:[[.07,1.98,.04,.03],[.09,1.97,-.1,.03],[.07,1.86,-.21,.022]],mirror:!0,group:\"head\",mat:\"hair\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[1.84,.05,.05,1,-.15],[1.9,.06,.06,1,-.15],[1.96,.05,.05,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{loft:[[1.74,.045,.045,1,-.15],[1.8,.055,.055,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{loft:[[1.64,.045,.045,1,-.15],[1.7,.055,.055,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{loft:[[1.54,.045,.045,1,-.15],[1.6,.055,.055,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{loft:[[1.44,.04,.04,1,-.15],[1.5,.05,.05,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{loft:[[1.36,.04,.04,1,-.15],[1.4,.05,.05,1,-.15]],group:\"head\",mat:\"hair\",seg:8},{mark:\"gem\",at:[0,1.33,-.15],mat:\"accent\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{tube:[[0,2,0,.03],[0,2.18,0,.004]],group:\"head\",mat:\"hair\"},{tube:[[.06,1.99,.07,.025],[.13,2.14,.14,.004]],mirror:!0,group:\"head\",mat:\"hair\"},{tube:[[.09,1.99,-.05,.025],[.2,2.12,-.1,.004]],mirror:!0,group:\"head\",mat:\"hair\"},{tube:[[0,1.99,-.1,.025],[0,2.1,-.24,.004]],group:\"head\",mat:\"hair\"}],[{zone:\"head\",yLo:1.93,yHi:2.3,th:.03,mat:\"hair\"},{zone:\"head\",yLo:1.76,yHi:1.93,zHi:.03,th:.03,mat:\"hair\"},{loft:[[2.03,.05,.05],[2.1,.07,.07],[2.16,.04,.04]],group:\"head\",mat:\"hair\",seg:10},{fan:[[.12,1.9,.07],[.15,1.9,-.02],[.17,1.62,0],[.15,1.58,.06],[.13,1.62,.1]],mirror:!0,group:\"head\",mat:\"hair\"}]];var Ft=[[{zone:\"hands\",th:.008,mat:\"white\"}],[{zone:\"hands\",th:.01,mat:\"skinDark\"},{tube:[[.2,.84,.04,.03],[.2,.72,.05,.021],[.2,.64,.06,.008]],sides:5,mat:\"ivory\",mirror:!0,group:\"arm\"},{tube:[[.235,.84,.06,.03],[.235,.708,.07,.021],[.235,.62,.08,.008]],sides:5,mat:\"ivory\",mirror:!0,group:\"arm\"},{tube:[[.27,.84,.04,.03],[.27,.72,.05,.021],[.27,.64,.06,.008]],sides:5,mat:\"ivory\",mirror:!0,group:\"arm\"},{loft:[[.98,.075,.075],[1.02,.075,.075]],cx:.235,seg:8,caps:!1,mat:\"ivory\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.008,mat:\"white\"},{box:[.15,.93,-.075,.32,1.07,.075],mat:\"accent\",mirror:!0,group:\"arm\"},{box:[.145,1.05,-.07,.325,1.09,.07],mat:\"trim\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.014,mat:\"dark\"},{loft:[[.93,.068,.068],[.97,.068,.068]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"dot\",at:[.235,.88,.09],mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"dot\",at:[.235,.83,.09],mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.03,mat:\"trim\"},{loft:[[.84,.07,.07],[.9,.09,.085],[.97,.075,.07]],cx:.235,seg:10,mat:\"trim\",mirror:!0,group:\"arm\"},{mark:\"stud\",at:[.235,.92,.1],mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.008,mat:\"white\"},{loft:[[.83,.075,.075],[.87,.075,.075]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{loft:[[.91,.075,.075],[.95,.075,.075]],cx:.235,seg:12,caps:!1,mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"gem\",at:[.235,.89,.1],mat:\"accent\",mirror:!0,group:\"arm\"},{mark:\"gem\",at:[.31,.9,0],n:[1,0,0],mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.024,mat:\"metal\"},{zone:\"sleeves\",yLo:.98,yHi:1.24,th:.024,mat:\"metal\"},{loft:[[.96,.1,.1],[1.08,.075,.075],[1.2,.07,.07]],cx:.235,seg:10,caps:!1,mat:\"metal\",mirror:!0,group:\"arm\"},{box:[.19,.89,.07,.28,.96,.1],mat:\"accent\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.008,mat:\"trim\"},{fan:[[.285,1.02,0],[.364,.95,-.055],[.405,.9,0],[.364,.95,.055]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.27,1.02,.035],[.365,.95,.053],[.355,.9,.12],[.288,.95,.13]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.235,1.02,.05],[.29,.95,.129],[.235,.9,.17],[.18,.95,.129]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.2,1.02,.035],[.182,.95,.13],[.115,.9,.12],[.105,.95,.053]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.185,1.02,0],[.106,.95,.055],[.065,.9,0],[.106,.95,-.055]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.2,1.02,-.035],[.105,.95,-.053],[.115,.9,-.12],[.182,.95,-.13]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.235,1.02,-.05],[.18,.95,-.129],[.235,.9,-.17],[.29,.95,-.129]],mat:\"white\",mirror:!0,group:\"arm\"},{fan:[[.27,1.02,-.035],[.288,.95,-.13],[.355,.9,-.12],[.365,.95,-.053]],mat:\"white\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.01,mat:\"skinDark\"},{tube:[[.19,.84,.04,.028],[.19,.696,.05,.02],[.19,.6,.06,.007]],sides:5,mat:\"dark\",mirror:!0,group:\"arm\"},{tube:[[.215,.84,.06,.028],[.215,.684,.07,.02],[.215,.58,.08,.007]],sides:5,mat:\"dark\",mirror:!0,group:\"arm\"},{tube:[[.255,.84,.06,.028],[.255,.684,.07,.02],[.255,.58,.08,.007]],sides:5,mat:\"dark\",mirror:!0,group:\"arm\"},{tube:[[.28,.84,.04,.028],[.28,.696,.05,.02],[.28,.6,.06,.007]],sides:5,mat:\"dark\",mirror:!0,group:\"arm\"}],[{zone:\"hands\",th:.012,mat:\"glow\"},{loft:[[.97,.07,.07],[1,.07,.07]],cx:.235,seg:12,caps:!1,mat:\"white\",mirror:!0,group:\"arm\"},{loft:[[.78,.02,.02],[.82,.045,.045],[.86,.02,.02]],cx:.235,seg:6,mat:\"glow\",mirror:!0,group:\"arm\"}]];var Xt=[[{loft:[[1.905,.118,.15,1,-.014],[1.93,.118,.15,1,-.014]],group:\"head\",mat:\"accent\",seg:16,caps:!1},{mark:\"gem\",at:[0,1.92,.138],mat:\"accent\"}],[{loft:[[1.96,.118,.15,1,-.014],[2,.118,.15,1,-.014]],group:\"head\",mat:\"accent\",seg:16,caps:!1},{tube:[[0,1.99,.135,.03],[0,2.1,.135,.008]],group:\"head\",mat:\"accent\"},{tube:[[.11,1.99,0,.03],[.115,2.09,0,.008]],mirror:!0,group:\"head\",mat:\"accent\"},{tube:[[.065,1.99,-.13,.03],[.065,2.09,-.13,.008]],mirror:!0,group:\"head\",mat:\"accent\"},{mark:\"gem\",at:[0,1.975,.14],mat:\"glow\"}],[{loft:[[2.075,.13,.13,1,-.01],[2.1,.135,.135,1,-.01],[2.125,.13,.13,1,-.01]],group:\"head\",mat:\"glow\",seg:18,caps:!1},{loft:[[2.1,.085,.085,1,-.01],[2.11,.085,.085,1,-.01]],group:\"head\",mat:\"glow\",seg:18,caps:!1}],[{loft:[[2.07,.05,.06,1,-.02],[2.02,.11,.14,1,-.02],[1.86,.14,.17,1,-.02],[1.7,.12,.15,1,-.03],[1.6,.09,.11,1,-.04]],group:\"head\",mat:\"pearl\",seg:16,arc:[2.75,6.67],caps:!1},{loft:[[1.97,.116,.148,1,-.014],[1.99,.116,.148,1,-.014]],group:\"head\",mat:\"accent\",seg:16,arc:[.3,2.84],caps:!1},{mark:\"gem\",at:[0,1.98,.14],mat:\"accent\"}],[{loft:[[1.97,.1,.13,1,-.014],[1.99,.118,.15,1,-.014],[2.11,.118,.15,1,-.014],[2.12,.09,.12,1,-.014]],group:\"head\",mat:\"dye\",seg:16},{loft:[[2,.123,.155,1,-.014],[2.035,.123,.155,1,-.014]],group:\"head\",mat:\"accent\",seg:16,caps:!1}],[{tube:[[.09,1.94,.03,.035],[.17,1.98,0,.03],[.2,2.06,-.04,.022],[.16,2.12,-.09,.01]],mirror:!0,group:\"head\",mat:\"ivory\"}],[{loft:[[1.99,.07,.07,1,.03],[2.03,.075,.075,1,.03]],cx:-.09,group:\"head\",mat:\"trim\",seg:14},{tube:[[-.09,2.03,.03,.014],[-.14,2.09,.03,.012],[-.2,2.1,.02,.008]],group:\"head\",mat:\"accent\"},{tube:[[-.09,2.03,.03,.014],[-.11,2.1,0,.012],[-.15,2.12,-.04,.008]],group:\"head\",mat:\"accent\"},{tube:[[-.09,2.03,.03,.014],[-.16,2.06,.07,.012],[-.24,2.04,.08,.008]],group:\"head\",mat:\"trim\"},{mark:\"gem\",at:[-.09,2.04,.04],mat:\"accent\"}],[{dfo:\"helm\",mat:\"metal\"},{box:[-.014,2,-.05,.014,2.12,.06],group:\"head\",mat:\"accent\"},{box:[-.014,2,-.14,.014,2.07,-.05],group:\"head\",mat:\"accent\"}],[{loft:[[2.03,.16,.18,1,-.014],[2.055,.16,.18,1,-.014]],group:\"head\",mat:\"accent\",seg:18,caps:!1},{loft:[[1.86,.19,.21,1,-.014],[1.885,.19,.21,1,-.014]],group:\"head\",mat:\"glow\",seg:18,caps:!1}],[{tube:[[.05,1.99,.02,.022],[.09,2.04,0,.018],[.14,2.08,-.03,.015]],mirror:!0,group:\"head\",mat:\"dye\"},{loft:[[2.06,.02,.02],[2.085,.035,.035],[2.11,.02,.02]],cx:.14,mirror:!0,group:\"head\",mat:\"glow\",seg:8}]];var jt=[[],[{tube:[[.09,1.76,.03,.03],[.105,1.71,.08,.028],[.08,1.67,.14,.024],[.03,1.655,.19,.016]],group:\"head\",mat:\"skinDark\"},{tube:[[-.09,1.76,.03,.03],[-.105,1.71,.08,.028],[-.08,1.67,.14,.024],[-.03,1.655,.19,.016]],group:\"head\",mat:\"skinDark\"}],[{dfo:\"tusks\",mat:\"ivory\",group:\"head\",opts:{size:1.5,jaw:1.1}}],[{loft:[[1.67,.06,.07,1,.13],[1.71,.09,.09,1,.14],[1.76,.085,.085,1,.15],[1.8,.06,.06,1,.15]],group:\"head\",mat:\"belly\",seg:10},{mark:\"dot\",at:[.03,1.765,.24],mat:\"dark\",mirror:!0,group:\"head\"},{mark:\"mouth\",at:[0,1.7,.225],mat:\"dark\",group:\"head\"}],[{fan:[[0,1.725,.32],[-.07,1.8,.09],[.07,1.8,.09]],group:\"head\",mat:\"accent\"},{fan:[[0,1.725,.32],[.07,1.8,.09],[.07,1.65,.09]],group:\"head\",mat:\"accent\"},{fan:[[0,1.725,.32],[.07,1.65,.09],[-.07,1.65,.09]],group:\"head\",mat:\"accent\"},{fan:[[0,1.725,.32],[-.07,1.65,.09],[-.07,1.8,.09]],group:\"head\",mat:\"accent\"}],[{tube:[[-.05,1.7,.07,.02],[-.065,1.62,.08,.013],[-.06,1.55,.085,.01]],group:\"head\",mat:\"skinDark\"},{tube:[[-.025,1.69,.09,.014],[-.03,1.6,.105,.013],[-.025,1.5,.11,.01]],group:\"head\",mat:\"skinDark\"},{tube:[[0,1.69,.095,.014],[0,1.58,.11,.013],[0,1.46,.115,.01]],group:\"head\",mat:\"skinDark\"},{tube:[[.025,1.69,.09,.014],[.03,1.6,.105,.013],[.025,1.5,.11,.01]],group:\"head\",mat:\"skinDark\"},{tube:[[.05,1.7,.07,.014],[.065,1.62,.08,.013],[.06,1.55,.085,.01]],group:\"head\",mat:\"skinDark\"}],[{tube:[[.03,1.78,.115,.024],[.03,1.7,.13,.008]],mirror:!0,group:\"head\",mat:\"white\"}],[{box:[.075,1.69,-.02,.12,1.8,.1],mirror:!0,group:\"head\",mat:\"metal\"},{box:[-.05,1.665,.07,.05,1.7,.115],group:\"head\",mat:\"metal\"}],[{fan:[[.08,1.76,0],[.2,1.84,-.04],[.19,1.78,-.06],[.1,1.72,-.03]],mirror:!0,group:\"head\",mat:\"trim\"},{fan:[[.08,1.74,0],[.2,1.76,-.04],[.19,1.7,-.06],[.1,1.68,-.03]],mirror:!0,group:\"head\",mat:\"accent\"},{fan:[[.08,1.72,0],[.19,1.68,-.04],[.17,1.62,-.05],[.1,1.66,-.03]],mirror:!0,group:\"head\",mat:\"trim\"}],[{tube:[[0,1.73,.07,.045],[0,1.67,.14,.034],[0,1.6,.2,.014]],group:\"head\",mat:\"ivory\"}]];var Gt=[[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dye\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"}],[{drape:{kind:\"flare\",top:.98,hem:.45,flare:[.175,.115,.34,.24]},mat:\"dye\"},{loft:[[.45,.345,.245],[.5,.33,.235]],group:\"body\",mat:\"trim\",seg:20,caps:!1},{loft:[[.98,.185,.125],[1.04,.18,.12]],group:\"body\",mat:\"trim\",seg:16,caps:!1},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"}],[{drape:{kind:\"flare\",top:.98,hem:.5,flare:[.18,.12,.32,.22]},mat:\"dye\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[.5707949999999999,.7707949999999999],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[.870795,1.070795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[1.1707949999999998,1.370795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[1.4707949999999999,1.670795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[1.770795,1.970795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[2.070795,2.270795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{drape:{kind:\"flare\",top:.98,hem:.5,arc:[2.3707949999999998,2.570795],seg:3,flare:[.186,.126,.335,.235]},mat:\"trim\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"trim\"}],[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dye\"},{zone:\"legs\",yLo:.42,yHi:.78,th:0,mat:\"skin\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"},{loft:[[.38,.105,.095],[.42,.105,.095]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{loft:[[.78,.14,.12],[.8200000000000001,.14,.12]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1}],[{loft:[[.98,.18,.12],[.88,.26,.18],[.74,.31,.22],[.62,.29,.2],[.54,.21,.15]],group:\"body\",mat:\"trim\",seg:20},{loft:[[.54,.22,.16],[.6,.22,.16]],group:\"body\",mat:\"dye\",seg:20,caps:!1},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"trim\"}],[{drape:{kind:\"flare\",top:1,hem:.12,arc:[1.9707949999999999,7.453975],flare:[.18,.12,.28,.2]},mat:\"dye\"},{zone:\"legs\",yLo:.12,yHi:1,th:.008,mat:\"dark\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dark\"},{drape:{kind:\"flare\",top:1,hem:.12,arc:[1.9707949999999999,2.120795],seg:2,flare:[.185,.125,.285,.205]},mat:\"trim\"},{drape:{kind:\"flare\",top:1,hem:.12,arc:[7.303974999999999,7.453975],seg:2,flare:[.185,.125,.285,.205]},mat:\"trim\"}],[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dye\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"},{loft:[[.17,.1,.085],[.21000000000000002,.1,.085]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{loft:[[.3,.1,.09],[.33999999999999997,.1,.09]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{loft:[[.43,.105,.095],[.47,.105,.095]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{loft:[[.56,.115,.105],[.6000000000000001,.115,.105]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1},{loft:[[.69,.125,.115],[.73,.125,.115]],cx:.082,mirror:!0,group:\"leg\",mat:\"accent\",seg:12,caps:!1}],[{loft:[[.48,.1,.09],[.58,.15,.125],[.78,.175,.14],[.96,.155,.12]],cx:.082,mirror:!0,group:\"leg\",mat:\"dye\",seg:14},{zone:\"legs\",yLo:.1,yHi:.5,th:.02,mat:\"dark\"},{loft:[[.46,.115,.1],[.5,.115,.1]],cx:.082,mirror:!0,group:\"leg\",mat:\"trim\",seg:12,caps:!1},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"}],[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dye\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"},{drape:{kind:\"flare\",top:.98,hem:.03,arc:[3.49159,5.93318],flare:[.18,.12,.3,.5]},mat:\"trim\"},{loft:[[1,.18,.12],[1.05,.18,.12]],group:\"body\",mat:\"accent\",seg:16,caps:!1}],[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dark\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dark\"},{mark:\"gem\",at:[.07,.25,.1],mirror:!0,group:\"leg\",mat:\"accent\"},{mark:\"gem\",at:[.1,.45,.11],mirror:!0,group:\"leg\",mat:\"accent\"},{mark:\"gem\",at:[.06,.62,.115],mirror:!0,group:\"leg\",mat:\"accent\"},{mark:\"gem\",at:[.1,.8,.13],mirror:!0,group:\"leg\",mat:\"accent\"},{mark:\"gem\",at:[.05,.93,.13],mirror:!0,group:\"leg\",mat:\"accent\"},{mark:\"dot\",at:[.12,.35,.1],mirror:!0,group:\"leg\",mat:\"white\"},{mark:\"dot\",at:[.04,.53,.11],mirror:!0,group:\"leg\",mat:\"white\"},{mark:\"dot\",at:[.13,.7,.12],mirror:!0,group:\"leg\",mat:\"white\"}]];var Kt=[[{loft:[[1.62,.04,.03],[1.66,.05,.04],[1.7,.04,.03]],group:\"body\",mat:\"white\",seg:8},{fan:[[0,1.6,.11],[-.03,1.45,.115],[0,1.4,.118],[.03,1.45,.115]],mat:\"white\"}],[{fan:[[0,1.57,.14],[.13,1.66,.13],[.13,1.48,.13]],group:\"body\",mat:\"accent\"},{fan:[[0,1.57,.14],[-.13,1.48,.13],[-.13,1.66,.13]],group:\"body\",mat:\"accent\"},{box:[-.03,1.54,.12,.03,1.6,.16],group:\"body\",mat:\"white\"}],[{loft:[[1.622,.012,.012,1,.125],[1.64,.026,.026,1,.125],[1.658,.012,.012,1,.125]],cx:-.12,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.578,.012,.012,1,.129],[1.596,.026,.026,1,.129],[1.614,.012,.012,1,.129]],cx:-.09,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.547,.012,.012,1,.133],[1.565,.026,.026,1,.133],[1.583,.012,.012,1,.133]],cx:-.06,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.528,.012,.012,1,.136],[1.546,.026,.026,1,.136],[1.564,.012,.012,1,.136]],cx:-.03,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.522,.012,.012,1,.14],[1.54,.026,.026,1,.14],[1.558,.012,.012,1,.14]],cx:0,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.528,.012,.012,1,.136],[1.546,.026,.026,1,.136],[1.564,.012,.012,1,.136]],cx:.03,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.547,.012,.012,1,.133],[1.565,.026,.026,1,.133],[1.583,.012,.012,1,.133]],cx:.06,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.578,.012,.012,1,.129],[1.596,.026,.026,1,.129],[1.614,.012,.012,1,.129]],cx:.09,group:\"body\",mat:\"pearl\",seg:6},{loft:[[1.622,.012,.012,1,.125],[1.64,.026,.026,1,.125],[1.658,.012,.012,1,.125]],cx:.12,group:\"body\",mat:\"pearl\",seg:6}],[{loft:[[1.52,.15,.13],[1.58,.25,.19],[1.63,.24,.18],[1.69,.12,.1]],group:\"body\",mat:\"white\",seg:10},{loft:[[1.575,.245,.188],[1.59,.26,.2]],group:\"body\",mat:\"trim\",seg:10,caps:!1}],[{loft:[[1.52,.17,.15],[1.58,.19,.16],[1.65,.14,.12]],group:\"body\",mat:\"trim\",seg:10},{fan:[[0,1.45,.15],[-.06,1.58,.16],[.06,1.58,.16],[.065,1.32,.15],[-.04,1.3,.15]],group:\"body\",mat:\"trim\"},{fan:[[.09,1.48,.16],[.05,1.57,.165],[.13,1.57,.165],[.14,1.36,.155],[.07,1.34,.155]],group:\"body\",mat:\"white\"}],[{loft:[[1.55,.17,.15],[1.66,.19,.16],[1.78,.23,.19]],group:\"body\",mat:\"white\",seg:14,arc:[2.17,7.25],caps:!1},{loft:[[1.755,.225,.185],[1.785,.235,.195]],group:\"body\",mat:\"trim\",seg:14,arc:[2.17,7.25],caps:!1}],[{tube:[[-.09,1.66,.12,.014],[-.05,1.56,.14,.014],[0,1.51,.15,.014],[.05,1.56,.14,.014],[.09,1.66,.12,.014]],group:\"body\",mat:\"accent\"},{loft:[[1.38,.006,.006,1,.15],[1.44,.045,.04,1,.15],[1.5,.006,.006,1,.15]],group:\"body\",mat:\"accent\",seg:8},{mark:\"gem\",at:[0,1.44,.2],group:\"body\",mat:\"glow\"}],[{loft:[[1.56,.16,.14],[1.64,.16,.14]],group:\"body\",mat:\"trim\",seg:10,caps:!1},{fan:[[0,1.57,.15],[-.11,1.64,.14],[-.11,1.5,.14]],group:\"body\",mat:\"trim\"},{fan:[[0,1.57,.15],[.11,1.5,.14],[.11,1.64,.14]],group:\"body\",mat:\"trim\"},{fan:[[0,1.55,.15],[-.03,1.58,.15],[-.08,1.34,.14],[-.02,1.33,.14]],group:\"body\",mat:\"trim\"},{fan:[[0,1.55,.15],[.03,1.58,.15],[.02,1.33,.14],[.08,1.34,.14]],group:\"body\",mat:\"trim\"}],[{box:[-.184,1.584,.115,-.116,1.656,.175],group:\"body\",mat:\"metal\"},{box:[-.134,1.517,.115,-.066,1.589,.175],group:\"body\",mat:\"gold\"},{box:[-.084,1.477,.115,-.016,1.549,.175],group:\"body\",mat:\"metal\"},{box:[-.034,1.464,.115,.034,1.536,.175],group:\"body\",mat:\"gold\"},{box:[.016,1.477,.115,.084,1.549,.175],group:\"body\",mat:\"metal\"},{box:[.066,1.517,.115,.134,1.589,.175],group:\"body\",mat:\"gold\"},{box:[.116,1.584,.115,.184,1.656,.175],group:\"body\",mat:\"metal\"},{mark:\"gem\",at:[0,1.46,.18],group:\"body\",mat:\"accent\"}],[{loft:[[1.53,.15,.13],[1.64,.15,.13]],group:\"body\",mat:\"dark\",seg:12,caps:!1},{loft:[[1.5,.006,.006,1,.14],[1.56,.07,.055,1,.14],[1.62,.006,.006,1,.14]],group:\"body\",mat:\"glow\",seg:8}]];var Ut=[{top:.14,head:[0,.1,.12],pieces:[{tube:[[0,.07,-.12,.06],[0,.08,0,.075],[0,.09,.1,.06]],mat:\"fur\",sides:8},{tube:[[.03,.14,.12,.014],[.04,.19,.13,.016]],mirror:!0,mat:\"belly\",sides:5},{loft:[[.168,.012,.012,1,.13],[.1724,.0132,.0132,1,.13],[.1812,.0202,.0202,1,.13],[.19,.022,.022,1,.13],[.1988,.0202,.0202,1,.13],[.2076,.0132,.0132,1,.13],[.212,.012,.012,1,.13]],cx:.04,mat:\"white\",seg:6,mirror:!0}]},{top:.4,head:[0,.33,.14],pieces:[{tube:[[0,.17,-.12,.085],[0,.18,0,.1],[0,.19,.08,.09]],mat:\"fur\",sides:8},{tube:[[.06,.11,.07,.032],[.06,.01,.08,.0352]],mirror:!0,mat:\"furDark\",sides:6},{tube:[[.06,.11,-.1,.032],[.06,.01,-.09,.0352]],mirror:!0,mat:\"furDark\",sides:6},{loft:[[.23,.012,.012,1,.15],[.248,.06,.057,1,.15],[.284,.092,.0874,1,.15],[.32,.1,.095,1,.15],[.356,.092,.0874,1,.15],[.392,.06,.057,1,.15],[.41,.012,.012,1,.15]],cx:0,mat:\"fur\",seg:10},{loft:[[.245,.012,.012,1,.24],[.252,.027,.03,1,.24],[.266,.0414,.046,1,.24],[.28,.045,.05,1,.24],[.294,.0414,.046,1,.24],[.308,.027,.03,1,.24],[.315,.012,.012,1,.24]],cx:0,mat:\"belly\",seg:10},{loft:[[.28,.012,.012,1,.285],[.283,.012,.012,1,.285],[.289,.0184,.0138,1,.285],[.295,.02,.015,1,.285],[.301,.0184,.0138,1,.285],[.307,.012,.012,1,.285],[.31,.012,.012,1,.285]],cx:0,mat:\"dark\",seg:6},{tube:[[.08,.38,.12,.03],[.12,.3,.12,.035]],mirror:!0,mat:\"furDark\",sides:6},{tube:[[0,.22,-.2,.03],[0,.31,-.27,.025]],mat:\"furDark\",sides:5},{loft:[[.311,.012,.012,1,.208],[.3158,.0144,.0144,1,.208],[.3254,.0221,.0221,1,.208],[.335,.024,.024,1,.208],[.3446,.0221,.0221,1,.208],[.3542,.0144,.0144,1,.208],[.359,.012,.012,1,.208]],cx:.075,mat:\"dark\",seg:6,mirror:!0}]},{top:.42,head:[0,.3,.1],pieces:[{loft:[[.09,.012,.012,1,-.02],[.112,.042,.042,1,-.02],[.156,.0644,.0644,1,-.02],[.2,.07,.07,1,-.02],[.244,.0644,.0644,1,-.02],[.288,.042,.042,1,-.02],[.31,.012,.012,1,-.02]],cx:0,mat:\"fur\",seg:10},{loft:[[.26,.012,.012,1,.1],[.272,.039,.036,1,.1],[.296,.0598,.0552,1,.1],[.32,.065,.06,1,.1],[.344,.0598,.0552,1,.1],[.368,.039,.036,1,.1],[.38,.012,.012,1,.1]],cx:0,mat:\"belly\",seg:10},{fan:[[.05,.28,-.02],[.24,.46,-.06],[.3,.3,-.1],[.24,.12,-.08],[.05,.15,-.02]],mirror:!0,mat:\"furDark\"},{fan:[[.05,.26,-.03],[.17,.4,-.05],[.2,.28,-.06],[.15,.16,-.06]],mirror:!0,mat:\"fur\"},{tube:[[.025,.36,.13,.014],[.07,.46,.1,.012]],mirror:!0,mat:\"belly\",sides:5},{tube:[[.04,.1,.04,.016],[.06,.01,.07,.016]],mirror:!0,mat:\"dark\",sides:4},{tube:[[.04,.1,-.03,.016],[.06,.01,0,.016]],mirror:!0,mat:\"dark\",sides:4},{loft:[[.303,.012,.012,1,.138],[.3074,.0132,.0132,1,.138],[.3162,.0202,.0202,1,.138],[.325,.022,.022,1,.138],[.3338,.0202,.0202,1,.138],[.3426,.0132,.0132,1,.138],[.347,.012,.012,1,.138]],cx:.045,mat:\"glow\",seg:6,mirror:!0}]},{top:.3,head:[0,.25,.1],pieces:[{loft:[[.05,.02,.02,1,-.02],[.1,.125,.15,1,-.02],[.18,.15,.17,1,-.02],[.25,.1,.12,1,-.02],[.3,.03,.04,1,-.02]],mat:\"fur\",seg:12},{loft:[[.065,.012,.012,1,.17],[.076,.042,.036,1,.17],[.098,.0644,.0552,1,.17],[.12,.07,.06,1,.17],[.142,.0644,.0552,1,.17],[.164,.042,.036,1,.17],[.175,.012,.012,1,.17]],cx:0,mat:\"furDark\",seg:10},{tube:[[0,.17,.2,.02],[0,.26,.25,.015]],mat:\"ivory\",sides:5},{tube:[[.1,.1,.13,.017],[.15,.06,.15,.017],[.16,.01,.16,.02]],mirror:!0,mat:\"dark\",sides:4},{tube:[[.1,.1,.03,.017],[.15,.06,.05,.017],[.16,.01,.06,.02]],mirror:!0,mat:\"dark\",sides:4},{tube:[[.1,.1,-.08,.017],[.15,.06,-.06,.017],[.16,.01,-.05,.02]],mirror:!0,mat:\"dark\",sides:4},{loft:[[.108,.012,.012,1,.207],[.1124,.0132,.0132,1,.207],[.1212,.0202,.0202,1,.207],[.13,.022,.022,1,.207],[.1388,.0202,.0202,1,.207],[.1476,.0132,.0132,1,.207],[.152,.012,.012,1,.207]],cx:.05,mat:\"glow\",seg:6,mirror:!0}]},{top:.42,head:[0,.33,.14],pieces:[{tube:[[0,.19,-.14,.07],[0,.2,0,.08],[0,.2,.09,.07]],mat:\"fur\",sides:8},{tube:[[.045,.13,.07,.026],[.045,.01,.08,.0286]],mirror:!0,mat:\"furDark\",sides:6},{tube:[[.045,.13,-.11,.026],[.045,.01,-.1,.0286]],mirror:!0,mat:\"furDark\",sides:6},{loft:[[.235,.012,.012,1,.15],[.25,.051,.048,1,.15],[.28,.0782,.0736,1,.15],[.31,.085,.08,1,.15],[.34,.0782,.0736,1,.15],[.37,.051,.048,1,.15],[.385,.012,.012,1,.15]],cx:0,mat:\"fur\",seg:10},{loft:[[.247,.012,.012,1,.225],[.2526,.021,.021,1,.225],[.2638,.0322,.0322,1,.225],[.275,.035,.035,1,.225],[.2862,.0322,.0322,1,.225],[.2974,.021,.021,1,.225],[.303,.012,.012,1,.225]],cx:0,mat:\"belly\",seg:10},{fan:[[.03,.36,.14],[.085,.37,.13],[.07,.46,.13],[.01,.4,.14]],mirror:!0,mat:\"furDark\"},{tube:[[0,.2,-.2,.025],[0,.17,-.3,.025],[0,.26,-.36,.025],[0,.38,-.34,.025]],mat:\"furDark\",sides:5},{loft:[[.31,.012,.012,1,.2],[.314,.012,.012,1,.2],[.322,.0184,.0184,1,.2],[.33,.02,.02,1,.2],[.338,.0184,.0184,1,.2],[.346,.012,.012,1,.2],[.35,.012,.012,1,.2]],cx:.06,mat:\"glow\",seg:6,mirror:!0}]},{top:.44,head:[0,.37,.03],pieces:[{loft:[[.18,.02,.02],[.22,.1,.1],[.29,.15,.15],[.36,.13,.13],[.42,.06,.06],[.44,.015,.015]],mat:\"glass\",seg:12},{loft:[[.2,.1,.1],[.25,.115,.115]],mat:\"fur\",seg:12},{tube:[[.07,.2,.04,.022],[.091,.1,.06,.018],[.056,.02,.03,.014]],mat:\"fur\",sides:5},{tube:[[-.07,.2,.04,.022],[-.091,.1,.06,.018],[-.056,.02,.03,.014]],mat:\"fur\",sides:5},{tube:[[0,.2,.09,.022],[0,.1,.11,.018],[0,.02,.08,.014]],mat:\"fur\",sides:5},{tube:[[0,.2,-.06,.022],[0,.1,-.04,.018],[0,.02,-.07,.014]],mat:\"fur\",sides:5},{loft:[[.282,.012,.012,1,.1],[.2876,.0168,.0168,1,.1],[.2988,.0258,.0258,1,.1],[.31,.028,.028,1,.1],[.3212,.0258,.0258,1,.1],[.3324,.0168,.0168,1,.1],[.338,.012,.012,1,.1]],cx:.08,mat:\"dark\",seg:6,mirror:!0}]},{top:.3,head:[0,.2,.24],pieces:[{tube:[[0,.13,-.1,.06],[0,.14,.04,.07],[0,.17,.14,.05]],mat:\"fur\",sides:8},{tube:[[0,.12,-.1,.05],[0,.08,-.25,.04],[0,.05,-.38,.028],[0,.1,-.46,.02]],mat:\"fur\",sides:6},{fan:[[0,.19,.14],[0,.26,.1],[0,.19,.06]],mat:\"accent\"},{fan:[[0,.19,.04],[0,.26,0],[0,.19,-.04]],mat:\"accent\"},{fan:[[0,.16,-.06],[0,.23,-.1],[0,.16,-.14]],mat:\"accent\"},{tube:[[.075,.1,.1,.03],[.1,.05,.13,.03],[.1,.01,.16,.03]],mirror:!0,mat:\"furDark\",sides:5},{tube:[[.075,.1,-.09,.03],[.1,.05,-.06,.03],[.1,.01,-.03,.03]],mirror:!0,mat:\"furDark\",sides:5},{loft:[[.145,.012,.012,1,.22],[.156,.039,.045,1,.22],[.178,.0598,.069,1,.22],[.2,.065,.075,1,.22],[.222,.0598,.069,1,.22],[.244,.039,.045,1,.22],[.255,.012,.012,1,.22]],cx:0,mat:\"fur\",seg:10},{loft:[[.15,.012,.012,1,.3],[.156,.021,.024,1,.3],[.168,.0322,.0368,1,.3],[.18,.035,.04,1,.3],[.192,.0322,.0368,1,.3],[.204,.021,.024,1,.3],[.21,.012,.012,1,.3]],cx:0,mat:\"belly\",seg:10},{fan:[[.05,.23,.2],[.1,.3,.19],[.1,.19,.19],[.06,.17,.2]],mirror:!0,mat:\"accent\"},{loft:[[.205,.012,.012,1,.262],[.209,.012,.012,1,.262],[.217,.0184,.0184,1,.262],[.225,.02,.02,1,.262],[.233,.0184,.0184,1,.262],[.241,.012,.012,1,.262],[.245,.012,.012,1,.262]],cx:.05,mat:\"glow\",seg:6,mirror:!0}]},{top:.42,head:[0,.3,.05],pieces:[{loft:[[.03,.012,.012,1,0],[.064,.102,.096,1,0],[.132,.1564,.1472,1,0],[.2,.17,.16,1,0],[.268,.1564,.1472,1,0],[.336,.102,.096,1,0],[.37,.012,.012,1,0]],cx:0,mat:\"fur\",seg:10},{loft:[[.05,.012,.012,1,.08],[.07,.06,.054,1,.08],[.11,.092,.0828,1,.08],[.15,.1,.09,1,.08],[.19,.092,.0828,1,.08],[.23,.06,.054,1,.08],[.25,.012,.012,1,.08]],cx:0,mat:\"belly\",seg:10},{tube:[[.06,.34,-.01,.035],[.1,.46,-.01,.03],[.12,.52,-.01,.02]],mirror:!0,mat:\"fur\",sides:6},{tube:[[.06,.35,.01,.015],[.095,.44,.01,.012]],mirror:!0,mat:\"belly\",sides:4},{tube:[[.08,.04,.1,.04],[.08,.01,.12,.045]],mirror:!0,mat:\"furDark\",sides:6},{loft:[[.212,.012,.012,1,.112],[.2176,.0168,.0168,1,.112],[.2288,.0258,.0258,1,.112],[.24,.028,.028,1,.112],[.2512,.0258,.0258,1,.112],[.2624,.0168,.0168,1,.112],[.268,.012,.012,1,.112]],cx:.1,mat:\"dark\",seg:6,mirror:!0},{mark:\"mouth\",at:[0,.1889,.165],mat:\"dark\"}]},{top:.5,head:[0,.33,.03],pieces:[{box:[-.1,.07,-.08,.1,.22,.09],mat:\"metal\"},{box:[-.085,.09,.09,.085,.2,.1],mat:\"dye\"},{box:[-.12,0,-.08,-.07,.07,.09],mat:\"dark\"},{box:[.07,0,-.08,.12,.07,.09],mat:\"dark\"},{box:[-.11,.22,-.09,.11,.4,.1],mat:\"metal\"},{box:[-.085,.29,.1,.085,.36,.115],mat:\"dark\"},{tube:[[0,.4,0,.012],[0,.48,0,.012]],mat:\"metal\",sides:4},{loft:[[.47,.012,.012,1,0],[.476,.018,.018,1,0],[.488,.0276,.0276,1,0],[.5,.03,.03,1,0],[.512,.0276,.0276,1,0],[.524,.018,.018,1,0],[.53,.012,.012,1,0]],cx:0,mat:\"glow\",seg:6},{box:[.11,.28,-.03,.15,.34,.03],mirror:!0,mat:\"dye\"},{loft:[[.303,.012,.012,1,.105],[.3074,.0132,.0132,1,.105],[.3162,.0202,.0202,1,.105],[.325,.022,.022,1,.105],[.3338,.0202,.0202,1,.105],[.3426,.0132,.0132,1,.105],[.347,.012,.012,1,.105]],cx:.045,mat:\"glow\",seg:6,mirror:!0}]},{top:.5,head:[0,.28,.05],pieces:[{loft:[[.03,.08,.08],[.1,.12,.12],[.2,.15,.15],[.27,.13,.13]],mat:\"fur\",seg:12},{loft:[[.27,.15,.15],[.3,.15,.15]],mat:\"furDark\",seg:12},{tube:[[.06,.04,.05,.035],[.06,.01,.07,.04]],mirror:!0,mat:\"furDark\",sides:5},{tube:[[0,.3,0,.018],[0,.4,0,.016]],mat:\"dye\",sides:5},{fan:[[0,.38,0],[.12,.47,.02],[.17,.4,.02],[.1,.34,0]],mirror:!0,mat:\"dye\"},{fan:[[0,.4,0],[.02,.5,0],[.07,.52,0],[.07,.42,0]],mat:\"trim\"},{loft:[[.174,.012,.012,1,.115],[.1792,.0156,.0156,1,.115],[.1896,.0239,.0239,1,.115],[.2,.026,.026,1,.115],[.2104,.0239,.0239,1,.115],[.2208,.0156,.0156,1,.115],[.226,.012,.012,1,.115]],cx:.09,mat:\"dark\",seg:6,mirror:!0},{mark:\"mouth\",at:[0,.1556,.155],mat:\"dark\"}]}];var Vt=[[],[{fan:[[-.34,1.04,.08],[-.25,1.04,.08],[-.262,1.085,.08],[-.295,1.118,.08],[-.34,1.13,.08],[-.385,1.118,.08],[-.418,1.085,.08],[-.43,1.04,.08],[-.418,.995,.08],[-.385,.962,.08],[-.34,.95,.08],[-.295,.962,.08],[-.262,.995,.08],[-.25,1.04,.08]],mat:\"metal\",group:\"arm\"},{tube:[[-.25,1.04,.08,.014],[-.262,1.085,.08,.014],[-.295,1.118,.08,.014],[-.34,1.13,.08,.014],[-.385,1.118,.08,.014],[-.418,1.085,.08,.014],[-.43,1.04,.08,.014],[-.418,.995,.08,.014],[-.385,.962,.08,.014],[-.34,.95,.08,.014],[-.295,.962,.08,.014],[-.262,.995,.08,.014],[-.25,1.04,.08,.014]],sides:5,mat:\"accent\",group:\"arm\"},{loft:[[1.005,.011,.011,1,.08],[1.04,.035,.035,1,.098],[1.075,.011,.011,1,.08]],cx:-.34,seg:8,mat:\"accent\",group:\"arm\"}],[{fan:[[-.34,1.14,.095],[-.13,1.14,.095],[-.151,1.231,.095],[-.209,1.304,.095],[-.293,1.345,.095],[-.387,1.345,.095],[-.471,1.304,.095],[-.529,1.231,.095],[-.55,1.14,.095],[-.529,1.049,.095],[-.471,.976,.095],[-.387,.935,.095],[-.293,.935,.095],[-.209,.976,.095],[-.151,1.049,.095],[-.13,1.14,.095]],mat:\"metal\",group:\"arm\"},{tube:[[-.13,1.14,.095,.022],[-.151,1.231,.095,.022],[-.209,1.304,.095,.022],[-.293,1.345,.095,.022],[-.387,1.345,.095,.022],[-.471,1.304,.095,.022],[-.529,1.231,.095,.022],[-.55,1.14,.095,.022],[-.529,1.049,.095,.022],[-.471,.976,.095,.022],[-.387,.935,.095,.022],[-.293,.935,.095,.022],[-.209,.976,.095,.022],[-.151,1.049,.095,.022],[-.13,1.14,.095,.022]],sides:5,mat:\"trim\",group:\"arm\"},{loft:[[1.09,.015,.015,1,.095],[1.14,.05,.05,1,.12],[1.19,.015,.015,1,.095]],cx:-.34,seg:8,mat:\"trim\",group:\"arm\"},{tube:[[-.24,1.14,.1,.01],[-.253,1.19,.1,.01],[-.29,1.227,.1,.01],[-.34,1.24,.1,.01],[-.39,1.227,.1,.01],[-.427,1.19,.1,.01],[-.44,1.14,.1,.01],[-.427,1.09,.1,.01],[-.39,1.053,.1,.01],[-.34,1.04,.1,.01],[-.29,1.053,.1,.01],[-.253,1.09,.1,.01],[-.24,1.14,.1,.01]],sides:5,mat:\"trim\",group:\"arm\"}],[{fan:[[-.34,1.11,.095],[-.53,1.42,.095],[-.15,1.42,.095],[-.131,1.13,.095],[-.34,.8,.095],[-.549,1.13,.095],[-.53,1.42,.095]],mat:\"glow\",group:\"arm\"},{tube:[[-.5,1.34,.1,.016],[-.51,1.12,.1,.016],[-.34,.84,.1,.016],[-.17,1.12,.1,.016],[-.18,1.34,.1,.016],[-.5,1.34,.1,.016]],sides:5,mat:\"white\",group:\"arm\"}],[{box:[-.45,.82,.09,-.23,1.42,.1],mat:\"glass\",group:\"arm\"},{box:[-.47,1.4,.08,-.21,1.45,.11],mat:\"metal\",group:\"arm\"},{box:[-.47,.8,.08,-.21,.85,.11],mat:\"metal\",group:\"arm\"},{box:[-.47,.8,.08,-.43,1.45,.11],mat:\"metal\",group:\"arm\"},{box:[-.25,.8,.08,-.21,1.45,.11],mat:\"metal\",group:\"arm\"},{mark:\"gem\",at:[-.34,1.42,.115],group:\"arm\",mat:\"glow\"}],[{fan:[[-.34,1.14,.095],[-.158,1.245,.095],[-.34,1.35,.095],[-.522,1.245,.095],[-.522,1.035,.095],[-.34,.93,.095],[-.158,1.035,.095],[-.158,1.245,.095]],mat:\"metal\",group:\"arm\"},{fan:[[-.34,1.14,.105],[-.245,1.195,.105],[-.34,1.25,.105],[-.435,1.195,.105],[-.435,1.085,.105],[-.34,1.03,.105],[-.245,1.085,.105],[-.245,1.195,.105]],mat:\"accent\",group:\"arm\"},{loft:[[1.1,.012,.012,1,.105],[1.14,.04,.04,1,.125],[1.18,.012,.012,1,.105]],cx:-.34,seg:8,mat:\"metal\",group:\"arm\"},{tube:[[-.13,1.14,.1,.018],[-.235,1.322,.1,.018],[-.445,1.322,.1,.018],[-.55,1.14,.1,.018],[-.445,.958,.1,.018],[-.235,.958,.1,.018],[-.13,1.14,.1,.018]],sides:5,mat:\"accent\",group:\"arm\"}],[{tube:[[-.13,1.14,.09,.03],[-.151,1.231,.09,.03],[-.209,1.304,.09,.03],[-.293,1.345,.09,.03],[-.387,1.345,.09,.03],[-.471,1.304,.09,.03],[-.529,1.231,.09,.03],[-.55,1.14,.09,.03],[-.529,1.049,.09,.03],[-.471,.976,.09,.03],[-.387,.935,.09,.03],[-.293,.935,.09,.03],[-.209,.976,.09,.03],[-.151,1.049,.09,.03],[-.13,1.14,.09,.03]],sides:5,mat:\"accent\",group:\"arm\"},{mark:\"gem\",at:[-.34,1.35,.12],mat:\"accent\",group:\"arm\"},{mark:\"gem\",at:[-.34,.93,.12],mat:\"accent\",group:\"arm\"},{mark:\"gem\",at:[-.55,1.14,.12],mat:\"accent\",group:\"arm\"},{loft:[[1.11,.009,.009,1,.09],[1.14,.03,.03,1,.105],[1.17,.009,.009,1,.09]],cx:-.34,seg:8,mat:\"accent\",group:\"arm\"}],[{fan:[[-.34,1.14,.095],[-.14,1.14,.095],[-.16,1.227,.095],[-.215,1.296,.095],[-.295,1.335,.095],[-.385,1.335,.095],[-.465,1.296,.095],[-.52,1.227,.095],[-.54,1.14,.095],[-.52,1.053,.095],[-.465,.984,.095],[-.385,.945,.095],[-.295,.945,.095],[-.215,.984,.095],[-.16,1.053,.095],[-.14,1.14,.095]],mat:\"metal\",group:\"arm\"},{tube:[[-.14,1.14,.095,.02],[-.16,1.227,.095,.02],[-.215,1.296,.095,.02],[-.295,1.335,.095,.02],[-.385,1.335,.095,.02],[-.465,1.296,.095,.02],[-.52,1.227,.095,.02],[-.54,1.14,.095,.02],[-.52,1.053,.095,.02],[-.465,.984,.095,.02],[-.385,.945,.095,.02],[-.295,.945,.095,.02],[-.215,.984,.095,.02],[-.16,1.053,.095,.02],[-.14,1.14,.095,.02]],sides:5,mat:\"trim\",group:\"arm\"},{tube:[[-.34,1.14,.09,.055],[-.34,1.14,.17,.035],[-.34,1.14,.3,.008]],sides:6,mat:\"metal\",group:\"arm\"},{mark:\"stud\",at:[-.34,1.29,.105],mat:\"accent\",group:\"arm\"},{mark:\"stud\",at:[-.34,.99,.105],mat:\"accent\",group:\"arm\"},{mark:\"stud\",at:[-.53,1.14,.105],mat:\"accent\",group:\"arm\"},{mark:\"stud\",at:[-.15,1.14,.105],mat:\"accent\",group:\"arm\"}],[{fan:[[-.34,1.11,.095],[-.54,1.42,.095],[-.14,1.42,.095],[-.12,1.13,.095],[-.34,.8,.095],[-.56,1.13,.095],[-.54,1.42,.095]],mat:\"glow\",group:\"arm\"},{fan:[[-.34,1.11,.105],[-.47,1.34,.105],[-.21,1.34,.105],[-.197,1.13,.105],[-.34,.88,.105],[-.483,1.13,.105],[-.47,1.34,.105]],mat:\"glass\",group:\"arm\"},{mark:\"gem\",at:[-.34,1.2,.115],mat:\"glow\",group:\"arm\"}],[{fan:[[-.34,1.14,.095],[-.13,1.14,.095],[-.151,1.231,.095],[-.209,1.304,.095],[-.293,1.345,.095],[-.387,1.345,.095],[-.471,1.304,.095],[-.529,1.231,.095],[-.55,1.14,.095],[-.529,1.049,.095],[-.471,.976,.095],[-.387,.935,.095],[-.293,.935,.095],[-.209,.976,.095],[-.151,1.049,.095],[-.13,1.14,.095]],mat:\"pearl\",group:\"arm\"},{tube:[[-.13,1.14,.095,.022],[-.151,1.231,.095,.022],[-.209,1.304,.095,.022],[-.293,1.345,.095,.022],[-.387,1.345,.095,.022],[-.471,1.304,.095,.022],[-.529,1.231,.095,.022],[-.55,1.14,.095,.022],[-.529,1.049,.095,.022],[-.471,.976,.095,.022],[-.387,.935,.095,.022],[-.293,.935,.095,.022],[-.209,.976,.095,.022],[-.151,1.049,.095,.022],[-.13,1.14,.095,.022]],sides:5,mat:\"glow\",group:\"arm\"},{fan:[[-.34,1.14,.105],[-.26,1.14,.105],[-.275,1.187,.105],[-.315,1.216,.105],[-.365,1.216,.105],[-.405,1.187,.105],[-.42,1.14,.105],[-.405,1.093,.105],[-.365,1.064,.105],[-.315,1.064,.105],[-.275,1.093,.105],[-.26,1.14,.105]],mat:\"white\",group:\"arm\"},{mark:\"gem\",at:[-.28,1.22,.115],mat:\"glow\",group:\"arm\"}]];var Zt=[[{box:[.17,1.63,-.06,.31,1.67,.06],mirror:!0,group:\"arm\",mat:\"accent\"},{box:[.29,1.56,-.05,.31,1.63,.05],mirror:!0,group:\"arm\",mat:\"accent\"}],[{fan:[[.22,1.63,.05],[.149,1.799,.05],[.251,1.989,.08],[.319,1.784,.05]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.22,1.63,0],[.253,1.802,0],[.439,1.89,.03],[.383,1.693,0]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.22,1.63,-.05],[.322,1.753,-.05],[.505,1.723,-.02],[.375,1.591,-.05]],mirror:!0,group:\"arm\",mat:\"trim\"},{loft:[[1.6,.075,.075],[1.68,.075,.075]],cx:.235,group:\"arm\",mirror:!0,mat:\"accent\",seg:8}],[{tube:[[.22,1.58,.05,.05],[.285,1.81,.07,.034],[.33,1.98,.09,.008]],mirror:!0,group:\"arm\",mat:\"ivory\"},{tube:[[.25,1.58,0,.05],[.365,1.76,0,.034],[.46,1.88,0,.008]],mirror:!0,group:\"arm\",mat:\"ivory\"},{tube:[[.22,1.58,-.05,.05],[.3,1.74,-.075,.034],[.36,1.84,-.1,.008]],mirror:!0,group:\"arm\",mat:\"ivory\"},{loft:[[1.56,.08,.08],[1.62,.09,.09]],cx:.24,group:\"arm\",mirror:!0,mat:\"metal\",seg:8}],[{loft:[[1.5,.07,.07],[1.56,.115,.105],[1.62,.125,.115],[1.68,.1,.09],[1.715,.05,.045]],cx:.245,seg:10,group:\"arm\",mirror:!0,mat:\"dye\"},{loft:[[1.485,.085,.085],[1.52,.09,.09]],cx:.24,seg:10,group:\"arm\",mirror:!0,mat:\"trim\"}],[{drape:{kind:\"flare\",top:1.62,hem:1.3,flare:[.2,.12,.36,.2]},mat:\"trim\"},{loft:[[1.27,.36,.2],[1.32,.37,.21]],seg:16,caps:!1,group:\"body\",mat:\"dye\"},{mark:\"gem\",at:[0,1.58,.115],group:\"body\",mat:\"accent\"}],[{fan:[[.2,1.64,-.07],[.173,1.844,-.07],[.261,2.076,-.17],[.282,1.828,-.07]],mirror:!0,group:\"arm\",mat:\"accent\"},{fan:[[.2,1.64,-.07],[.249,1.822,-.07],[.412,1.979,-.17],[.342,1.764,-.07]],mirror:!0,group:\"arm\",mat:\"white\"},{fan:[[.2,1.64,-.07],[.299,1.767,-.07],[.482,1.83,-.17],[.355,1.684,-.07]],mirror:!0,group:\"arm\",mat:\"accent\"},{fan:[[.2,1.64,-.07],[.305,1.708,-.07],[.454,1.694,-.17],[.324,1.62,-.07]],mirror:!0,group:\"arm\",mat:\"white\"},{box:[.19,1.58,-.11,.25,1.67,-.05],mirror:!0,group:\"arm\",mat:\"accent\"}],[{dfo:\"pauldrons\",mat:\"metal\",group:\"arm\",mirror:!0},{loft:[[1.56,.09,.08],[1.62,.13,.11],[1.68,.1,.08],[1.7,.05,.04]],cx:.25,seg:10,group:\"arm\",mirror:!0,mat:\"metal\"},{loft:[[1.5,.1,.09],[1.54,.11,.1]],cx:.25,seg:10,group:\"arm\",mirror:!0,mat:\"accent\"},{mark:\"gem\",at:[.27,1.62,.11],group:\"arm\",mirror:!0,mat:\"glow\"}],[{fan:[[.27,1.66,.09],[.457,1.537,.09],[.52,1.227,.09],[.283,1.437,.09]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.2,1.66,.09],[.256,1.45,.09],[.13,1.165,.09],[.088,1.474,.09]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.24,1.66,.095],[.361,1.494,.095],[.32,1.207,.095],[.183,1.463,.095]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.31,1.64,.02],[.34,1.44,.135],[.36,1.14,.07],[.34,1.44,-.045]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.31,1.64,-.03],[.34,1.464,.035],[.36,1.2,-.08],[.34,1.464,-.145]],mirror:!0,group:\"arm\",mat:\"trim\"},{fan:[[.25,1.66,-.09],[.369,1.526,-.09],[.354,1.274,-.09],[.214,1.485,-.09]],mirror:!0,group:\"arm\",mat:\"trim\"},{loft:[[1.6,.08,.08],[1.67,.085,.085]],cx:.235,group:\"arm\",mirror:!0,mat:\"accent\",seg:8}],[{loft:[[1.78,.115,.115],[1.86,.115,.115]],cx:.31,caps:!1,seg:12,group:\"arm\",mirror:!0,mat:\"glow\"},{loft:[[1.7,.025,.025],[1.76,.035,.035],[1.82,.025,.025]],cx:.31,seg:8,group:\"arm\",mirror:!0,mat:\"glow\"},{mark:\"gem\",at:[.31,1.84,.11],group:\"arm\",mirror:!0,mat:\"glow\"}],[{drape:{kind:\"cape\",hem:.45,wide:!0},mat:\"trim\"},{loft:[[1.58,.23,.13],[1.66,.23,.13]],seg:16,caps:!1,group:\"body\",mat:\"dye\"},{mark:\"gem\",at:[0,1.63,.11],group:\"body\",mat:\"accent\"}]];var qt=[[],[{dfo:\"tail:argonian\",mat:\"skin\"}],[{dfo:\"tail:khajiit\",mat:\"skin\"}],[{group:\"body\",tube:[[0,.98,-.05,.055],[0,.95,-.16,.055],[0,1.08,-.27,.05],[0,1.3,-.31,.045],[0,1.52,-.26,.04],[0,1.66,-.15,.035]],mat:\"skinDark\",sides:7},{group:\"body\",loft:[[.94,.07,.07,1,-.15],[1.02,.065,.065,1,-.2]],mat:\"skinDark\",seg:8},{group:\"body\",loft:[[1.16,.06,.06,1,-.3],[1.22,.06,.06,1,-.3]],mat:\"accent\",seg:8},{group:\"body\",loft:[[1.38,.055,.055,1,-.29],[1.44,.055,.055,1,-.29]],mat:\"accent\",seg:8},{group:\"body\",tube:[[0,1.64,-.16,.05],[0,1.66,-.08,.03],[0,1.62,0,.006]],mat:\"accent\",sides:6}],[{group:\"body\",tube:[[0,.98,-.05,.055],[0,.9,-.2,.05],[0,.7,-.34,.04],[0,.5,-.42,.03],[0,.36,-.5,.02]],mat:\"skin\",sides:7},{group:\"body\",fan:[[0,.78,-.4],[0,.96,-.2],[0,1.12,-.32],[0,.98,-.5],[0,.72,-.56],[0,.5,-.56],[0,.55,-.45]],mat:\"trim\"},{group:\"body\",fan:[[0,.45,-.58],[0,.55,-.5],[0,.4,-.7],[0,.22,-.7],[0,.28,-.55]],mat:\"trim\"}],[{group:\"body\",tube:[[0,.98,-.05,.045],[0,.9,-.2,.04],[0,.62,-.4,.032],[0,.3,-.6,.026],[0,.1,-.8,.022],[0,.03,-.98,.02]],mat:\"skinDark\",sides:5},{group:\"body\",loft:[[0,.035,.035,1,-1],[.06,.035,.035,1,-1]],mat:\"accent\",seg:8}],[{group:\"body\",tube:[[0,.98,-.05,.055],[0,.9,-.2,.055],[0,.68,-.36,.05],[0,.5,-.46,.05]],mat:\"skin\",sides:7},{group:\"body\",loft:[[.26,.06,.06,1,-.52],[.34,.12,.12,1,-.52],[.44,.15,.15,1,-.52],[.54,.12,.12,1,-.52],[.6,.05,.05,1,-.52]],mat:\"skin\",seg:12},{group:\"body\",tube:[[.12,.44,-.52,.035],[.26,.44,-.52,.006]],mirror:!0,mat:\"metal\",sides:5},{group:\"body\",tube:[[0,.62,-.52,.032],[0,.78,-.52,.006]],mat:\"metal\",sides:5},{group:\"body\",tube:[[0,.44,-.67,.032],[0,.44,-.82,.006]],mat:\"metal\",sides:5},{group:\"body\",tube:[[0,.28,-.52,.032],[0,.14,-.52,.006]],mat:\"metal\",sides:5},{group:\"body\",tube:[[.1,.54,-.6,.03],[.2,.62,-.7,.006]],mirror:!0,mat:\"metal\",sides:5}],[{group:\"body\",tube:[[0,.98,-.05,.055],[0,.9,-.2,.05],[0,.8,-.3,.045],[0,.74,-.36,.05]],mat:\"skin\",sides:7},{group:\"body\",tube:[[0,.74,-.36,.06],[0,.99,-.51,.051],[0,1.24,-.6599999999999999,.01]],mat:\"trim\",sides:6},{group:\"body\",tube:[[0,.74,-.36,.06],[.1,.95,-.485,.051],[.2,1.16,-.61,.01]],mat:\"accent\",sides:6},{group:\"body\",tube:[[0,.74,-.36,.06],[-.1,.95,-.485,.051],[-.2,1.16,-.61,.01]],mat:\"accent\",sides:6},{group:\"body\",tube:[[0,.74,-.36,.06],[.18,.88,-.45999999999999996,.051],[.36,1.02,-.56,.01]],mat:\"trim\",sides:6},{group:\"body\",tube:[[0,.74,-.36,.06],[-.18,.88,-.45999999999999996,.051],[-.36,1.02,-.56,.01]],mat:\"trim\",sides:6}],[{group:\"body\",tube:[[.06,.98,-.05,.045],[.1,.9,-.2,.042],[.14,.66,-.33,.036],[.17,.42,-.38,.03],[.17,.24,-.33,.018]],mirror:!0,mat:\"skin\",sides:6},{group:\"body\",loft:[[.22,.04,.04,1,-.32],[.28,.04,.04,1,-.32]],cx:.17,mirror:!0,mat:\"trim\",seg:8}],[{group:\"body\",tube:[[0,.98,-.05,.04],[0,.93,-.14,.04],[0,.9,-.15,.04],[.076,.865,-.188,.039],[.099,.829,-.276,.039],[.054,.794,-.353,.038],[-.028,.758,-.366,.037],[-.091,.723,-.306,.036],[-.091,.687,-.214,.036],[-.028,.652,-.154,.035],[.054,.616,-.167,.034],[.099,.581,-.244,.033],[.076,.545,-.332,.033],[0,.51,-.37,.032],[-.076,.475,-.332,.031],[-.099,.439,-.244,.031],[-.054,.404,-.167,.03],[.028,.368,-.154,.029],[.091,.333,-.214,.028],[.091,.297,-.306,.028],[.028,.262,-.366,.027],[-.054,.226,-.353,.026],[-.099,.191,-.276,.025],[-.076,.155,-.188,.025],[0,.12,-.15,.024]],mat:\"skin\",sides:6},{group:\"body\",loft:[[.1,.04,.04,1,-.2],[.15,.04,.04,1,-.2]],cx:0,mat:\"accent\",seg:8}]];var Qt=[[{zone:\"torso\",yLo:1,yHi:1.64,th:.012,mat:\"dye\"},{zone:\"sleeves\",yLo:1.02,yHi:1.64,th:.01,mat:\"dye\"},{fan:[[0,1.3,.11],[-.07,1.6,.1],[.07,1.6,.1]],mat:\"white\"},{fan:[[0,1.28,.112],[.075,1.6,.105],[.12,1.58,.095],[.05,1.4,.11]],mirror:!0,mat:\"trim\"},{mark:\"stud\",at:[0,1.22,.1],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.14,.098],group:\"body\",mat:\"accent\"}],[{zone:\"torso\",yLo:1.1,yHi:1.6,th:.012,mat:\"dye\"},{drape:{kind:\"flare\",top:1.3,hem:.14,flare:[.15,.11,.33,.23]},mat:\"dye\"},{zone:\"torso\",yLo:1.45,yHi:1.6,th:.016,mat:\"trim\"},{loft:[[1.26,.165,.125],[1.35,.165,.125]],group:\"body\",mat:\"trim\",seg:12,caps:!1},{mark:\"gem\",at:[0,1.31,.14],group:\"body\",mat:\"accent\"}],[{zone:\"torso\",yLo:1,yHi:1.64,th:.012,mat:\"dye\"},{zone:\"sleeves\",yLo:1.02,yHi:1.64,th:.01,mat:\"dye\"},{zone:\"torso\",yLo:1,yHi:1.07,th:.018,mat:\"trim\"},{zone:\"sleeves\",yLo:1.02,yHi:1.12,th:.018,mat:\"trim\"},{zone:\"torso\",yLo:1.55,yHi:1.64,th:.016,mat:\"trim\"},{fan:[[0,1,.108],[-.012,1.55,.105],[.012,1.55,.105]],mat:\"trim\"},{mark:\"gem\",at:[.07,1.46,.125],group:\"body\",mat:\"accent\",mirror:!0},{mark:\"gem\",at:[.12,1.34,.115],group:\"body\",mat:\"accent\",mirror:!0},{mark:\"gem\",at:[.07,1.22,.115],group:\"body\",mat:\"accent\",mirror:!0},{mark:\"gem\",at:[0,1.34,.125],group:\"body\",mat:\"accent\"},{mark:\"gem\",at:[0,1.1,.118],group:\"body\",mat:\"accent\"}],[{zone:\"torso\",yLo:.95,yHi:1.64,th:.012,mat:\"dye\"},{zone:\"sleeves\",yLo:.97,yHi:1.64,th:.01,mat:\"dye\"},{zone:\"legs\",yLo:.12,yHi:1,th:.012,mat:\"dye\"},{zone:\"torso\",yLo:1.16,yHi:1.24,th:.02,mat:\"dark\"},{zone:\"torso\",yLo:1.57,yHi:1.64,th:.018,mat:\"trim\"},{box:[-.014,1,.1,.014,1.57,.13],group:\"body\",mat:\"white\"},{box:[-.03,1.19,.1,.03,1.22,.14],group:\"body\",mat:\"accent\"},{box:[.11,.2,-.02,.145,.98,.04],mirror:!0,group:\"leg\",mat:\"trim\"}],[{zone:\"torso\",yLo:1.05,yHi:1.45,th:.018,mat:\"trim\"},{zone:\"torso\",yLo:1.45,yHi:1.6,th:.01,mat:\"dye\"},{box:[-.012,1.08,.105,.012,1.44,.13],group:\"body\",mat:\"dark\"},{box:[-.05,1.38,.115,.05,1.405,.14],group:\"body\",mat:\"white\"},{box:[-.05,1.29,.115,.05,1.315,.14],group:\"body\",mat:\"white\"},{box:[-.05,1.2,.115,.05,1.225,.14],group:\"body\",mat:\"white\"},{box:[-.05,1.11,.115,.05,1.135,.14],group:\"body\",mat:\"white\"},{drape:{kind:\"flare\",top:1.12,hem:.8,flare:[.18,.12,.28,.2]},mat:\"dye\"}],[{drape:{kind:\"flare\",top:1.55,hem:.12,flare:[.18,.13,.34,.24]},mat:\"dye\"},{zone:\"torso\",yLo:1.05,yHi:1.64,th:.014,mat:\"dye\"},{zone:\"sleeves\",yLo:1.1,yHi:1.64,th:.012,mat:\"dye\"},{loft:[[1.4,.07,.07],[1.15,.12,.1],[.95,.13,.1]],cx:.255,mirror:!0,group:\"arm\",mat:\"dye\",seg:10},{fan:[[0,.8,.2],[-.045,1.55,.125],[.045,1.55,.125],[.07,.16,.255],[-.07,.16,.255]],mat:\"trim\"},{loft:[[1.2,.2,.15],[1.3,.2,.15]],group:\"body\",mat:\"trim\",seg:12,caps:!1},{zone:\"torso\",yLo:1.55,yHi:1.64,th:.018,mat:\"trim\"}],[{zone:\"torso\",yLo:1.05,yHi:1.64,th:.02,mat:\"dye\"},{zone:\"sleeves\",yLo:1.38,yHi:1.64,th:.032,mat:\"trim\"},{zone:\"sleeves\",yLo:1.05,yHi:1.38,th:.01,mat:\"white\"},{zone:\"torso\",yLo:1.05,yHi:1.12,th:.03,mat:\"trim\"},{zone:\"torso\",yLo:1.55,yHi:1.64,th:.026,mat:\"white\"},{mark:\"stud\",at:[0,1.5,.13],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.4,.13],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.3,.13],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.2,.13],group:\"body\",mat:\"accent\"}],[{zone:\"torso\",yLo:1.1,yHi:1.64,th:.014,mat:\"dark\"},{zone:\"sleeves\",yLo:1.12,yHi:1.64,th:.012,mat:\"dark\"},{fan:[[0,1.12,.11],[-.075,1.6,.102],[.075,1.6,.102],[.06,1.12,.11]],mat:\"white\"},{fan:[[0,1.3,.116],[.08,1.6,.108],[.125,1.58,.098],[.06,1.35,.114]],mirror:!0,mat:\"trim\"},{fan:[[0,1.16,-.1],[.02,1.16,-.1],[.17,.66,-.14],[.02,.6,-.12],[0,.6,-.12]],mirror:!0,group:\"body\",mat:\"dark\"},{fan:[[.17,.64,-.14],[.02,1.16,-.1],[.12,1.16,-.1]],mirror:!0,group:\"body\",mat:\"trim\"},{mark:\"stud\",at:[0,1.26,.12],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.18,.12],group:\"body\",mat:\"accent\"}],[{drape:{kind:\"flare\",top:1.52,hem:.26,flare:[.15,.115,.27,.2],arc:[.9,5.5]},mat:\"dye\"},{zone:\"torso\",yLo:1.05,yHi:1.6,th:.01,mat:\"trim\"},{fan:[[0,1.42,.125],[-.15,1.64,.1],[-.02,1.66,.1],[.17,1.1,.12],[.02,1.08,.125]],group:\"body\",mat:\"trim\"},{loft:[[1.7,.07,.08],[1.55,.09,.1],[1.25,.075,.09]],cx:-.235,group:\"arm\",mat:\"dye\",seg:10},{mark:\"gem\",at:[.1,1.2,.13],group:\"body\",mat:\"accent\"}],[{zone:\"torso\",yLo:1,yHi:1.64,th:.012,mat:\"dye\"},{zone:\"sleeves\",yLo:1.02,yHi:1.64,th:.01,mat:\"dye\"},{zone:\"torso\",yLo:1.46,yHi:1.64,th:.018,mat:\"accent\"},{zone:\"sleeves\",yLo:1.02,yHi:1.12,th:.018,mat:\"accent\"},{zone:\"torso\",yLo:1,yHi:1.08,th:.018,mat:\"accent\"},{mark:\"gem\",at:[.05,1.34,.115],group:\"body\",mat:\"white\",mirror:!0},{mark:\"gem\",at:[.1,1.26,.11],group:\"body\",mat:\"white\",mirror:!0},{mark:\"gem\",at:[.04,1.18,.11],group:\"body\",mat:\"white\",mirror:!0},{mark:\"dot\",at:[.235,1.3,.07],group:\"arm\",mat:\"white\",mirror:!0}]];var Jt=(()=>{let t=(i=-.14,n=.1,e=.034)=>({box:[-e,i,-e,e,n,e],mat:\"dark\"}),r=(i,n,e=.03,p=\"energy\",g=n)=>({loft:[[i,n,g,.7],[i+e,n,g,.7]],mat:p,seg:10}),o=(i,n,e=\"glass\",p=0,g=0)=>({loft:[[i-n,.01,.01],[i-n*.7,n*.8,n*.8],[i,n,n],[i+n*.7,n*.8,n*.8],[i+n,.01,.01]].map(([b,f,y])=>[b,f,y,1,g]),cx:p,mat:e,seg:10}),a=(i,n,e,p,g=16,b=0)=>Array.from({length:g+1},(f,y)=>[i+Math.cos(y/g*6.2832)*e,n+Math.sin(y/g*6.2832)*e,b,p]),s=(i,n,e,p,g=0,b=10)=>({loft:Array.from({length:b+1},(f,y)=>{let u=y/b*Math.PI;return[i+Math.cos(u)*n,Math.max(.012,Math.sin(u)*n),e,.8]}).reverse(),cx:g,mat:p,seg:16}),m=(i,n,e=.032)=>({tube:[[0,i,0,e],[0,n,0,e]],mat:\"shell\",sides:6});return[{len:1,ring:.03,carry:\"up\",pieces:[t(-.16,.1,.04),o(-.2,.05,\"shell\"),{loft:[[.1,.15,.04,.6],[.15,.15,.04,.6]],mat:\"shell\",seg:8},{loft:[[.15,.09,.035,.6],[.6,.09,.035,.6],[.98,.06,.03,.6],[1.06,.005,.01,.6]],mat:\"shell\",seg:8},{loft:[[.17,.06,.05,.7],[.6,.06,.05,.7],[.94,.04,.04,.7],[1.02,.004,.01,.7]],mat:\"energy\",seg:8},{box:[-.015,.2,-.05,.015,.9,.05],mat:\"white\"}]},{len:1.3,ring:.034,carry:\"up\",pieces:[t(-.2,.1,.04),o(-.24,.06,\"shell\"),{box:[-.2,.1,-.05,.2,.18,.05],mat:\"shell\"},{box:[-.19,.12,.05,-.15,.2,.08],mat:\"energy\"},{box:[.15,.12,.05,.19,.2,.08],mat:\"energy\"},{loft:[[.18,.09,.03,.5],[.8,.11,.03,.5],[1.15,.11,.03,.5],[1.32,.01,.01,.5]],mat:\"shell\",seg:8},{loft:[[.22,.04,.045,.5],[.8,.05,.045,.5],[1.2,.04,.04,.5],[1.28,.005,.01,.5]],mat:\"energy\",seg:8}]},{len:.6,ring:.03,carry:\"up\",pieces:[t(-.14,.06,.04),o(-.17,.05,\"energy\"),{box:[-.12,.06,-.04,.12,.12,.04],mat:\"shell\"},{loft:[[.12,.08,.03,.5],[.4,.075,.03,.5],[.62,.005,.01,.5]],mat:\"shell\",seg:8},{loft:[[.14,.04,.04,.5],[.4,.04,.04,.5],[.58,.004,.01,.5]],mat:\"energy\",seg:8},{fan:[[0,.3,.04],[.14,.22,.04],[.1,.4,.04]],mirror:!0,mat:\"glass\"}]},{len:.62,ring:.04,carry:\"up\",pieces:[{box:[-.05,-.2,-.05,.05,.08,.05],mat:\"dark\"},{box:[-.09,0,-.09,.09,.3,.09],mat:\"shell\"},{box:[-.045,.3,-.05,.045,.4,.05],mat:\"shell\"},{box:[-.085,.04,.09,.085,.22,.12],mat:\"glass\"},{box:[-.1,.1,-.1,-.09,.25,.1],mat:\"energy\"},{tube:[[-.05,.38,0,.026],[-.05,.62,0,.026]],mat:\"energy\",sides:6},{tube:[[.05,.38,0,.026],[.05,.62,0,.026]],mat:\"energy\",sides:6},{box:[-.05,.5,-.012,.05,.54,.012],mat:\"white\"}]},{len:1.02,ring:.04,carry:\"up\",pieces:[{box:[-.055,-.34,-.06,.055,.06,.06],mat:\"dark\"},{box:[-.08,0,-.1,.08,.42,.1],mat:\"shell\"},{box:[-.04,.42,-.05,.04,.95,.05],mat:\"shell\"},{box:[-.09,.3,.1,.09,.4,.13],mat:\"energy\"},{box:[-.03,.1,.1,.03,.28,.17],mat:\"dark\"},{box:[-.06,.55,.05,.06,.62,.09],mat:\"energy\"},{box:[-.06,.78,.05,.06,.85,.09],mat:\"energy\"},{box:[-.1,.06,-.06,-.08,.36,.06],mat:\"energy\"},{box:[.08,.06,-.06,.1,.36,.06],mat:\"energy\"},{box:[-.06,.95,-.06,.06,1.02,.06],mat:\"energy\"}]},{len:1.04,ring:.045,carry:\"up\",pieces:[{box:[-.09,-.3,-.09,.09,.15,.09],mat:\"dark\"},{box:[-.13,.15,-.14,.13,.45,.14],mat:\"shell\"},{box:[-.12,.45,-.045,-.05,1,.12],mat:\"shell\"},{box:[.05,.45,-.045,.12,1,.12],mat:\"shell\"},{box:[-.05,.45,0,.05,1,.07],mat:\"energy\"},{box:[-.14,.9,-.06,.14,1.04,.14],mat:\"shell\"},{box:[-.1,.2,.14,.1,.4,.17],mat:\"glass\"},{box:[-.14,.3,-.12,-.13,.42,.12],mat:\"energy\"}]},{len:1.1,ring:.034,carry:\"up\",pieces:[t(-.2,.2,.04),{tube:[[0,.2,0,.04],[0,.6,0,.04]],mat:\"shell\",sides:6},r(.3,.07,.04,\"energy\"),o(.75,.15,\"energy\"),o(.75,.09,\"white\"),{tube:[[.1,.8,0,.025],[.2,.95,0,.025],[.12,1.08,0,.02]],mat:\"edge\",sides:5},{tube:[[-.1,.7,0,.025],[-.22,.6,.06,.025],[-.16,.48,0,.02]],mat:\"edge\",sides:5},{tube:[[0,.88,.08,.025],[.04,1,.16,.025],[0,1.1,.12,.02]],mat:\"edge\",sides:5}]},{len:1.25,ring:.034,carry:\"up\",pieces:[t(-.2,.15,.04),o(.2,.06,\"shell\"),{tube:[[0,.22,0,.022],[.1,.45,.05,.022],[.04,.7,.1,.022],[.2,.85,.1,.022]],mat:\"energy\",sides:5},o(.98,.14,\"shell\",.22,.1),{box:[.04,.94,.04,.4,1.02,.16],mat:\"shell\"},o(.98,.07,\"energy\",.22,.22),{box:[.19,1.08,.05,.25,1.22,.15],mat:\"dark\"},{fan:[[.22,.98,.1],[.5,.9,.1],[.5,1.06,.1]],mat:\"energy\"}]},{len:1.4,ring:.03,carry:\"up\",pieces:[m(-.5,.9,.045),r(0,.07,.06,\"dark\"),r(.6,.075,.05,\"energy\"),{loft:[[.85,.06,.06,.8],[.92,.15,.07,.7],[1.15,.12,.065,.7],[1.4,.005,.01,.7]],mat:\"energy\",seg:8},{loft:[[.92,.06,.09,.7],[1.15,.045,.09,.7],[1.3,.004,.03,.7]],mat:\"white\",seg:8},{loft:[[-.45,.07,.07],[-.52,.1,.1],[-.59,.01,.01]],mat:\"energy\",seg:8}]},{len:1.3,ring:.03,carry:\"up\",pieces:[m(-.45,.7,.045),{box:[-.26,.66,-.06,.26,.78,.06],mat:\"shell\"},{box:[-.26,.78,-.05,-.18,.95,.05],mat:\"shell\"},{box:[.18,.78,-.05,.26,.95,.05],mat:\"shell\"},{loft:[[.78,.07,.06,.7],[1.05,.07,.06,.7],[1.32,.005,.01,.7]],mat:\"energy\",seg:8},{loft:[[.9,.06,.055,.7],[1.08,.06,.055,.7],[1.2,.005,.01,.7]],cx:-.22,mat:\"energy\",seg:8},{loft:[[.9,.06,.055,.7],[1.08,.06,.055,.7],[1.2,.005,.01,.7]],cx:.22,mat:\"energy\",seg:8},r(.1,.07,.05,\"energy\")]},{len:1.3,ring:.03,carry:\"up\",pieces:[m(-.5,.7),{box:[-.16,.5,-.12,.16,1,.12],mat:\"shell\"},{loft:[[.9,.05,.05],[1,.05,.05],[1.15,.004,.004]],cx:-.09,mat:\"energy\",seg:8},{loft:[[1,.05,.05],[1.15,.004,.004]],cx:.09,mat:\"energy\",seg:8},{box:[-.14,.52,.12,.14,.98,.15],mat:\"dark\"},{box:[-.14,.7,.15,-.04,.78,.19],mat:\"energy\"},{box:[.04,.7,.15,.14,.78,.19],mat:\"energy\"},{box:[-.14,.82,.15,-.04,.9,.19],mat:\"energy\"},{box:[.04,.82,.15,.14,.9,.19],mat:\"energy\"}]},{len:1.3,ring:.03,carry:\"up\",pieces:[m(-.5,1,.042),r(.1,.065,.06,\"dark\"),{box:[-.06,.95,-.06,.12,1.05,.06],mat:\"shell\"},{tube:[[.05,1.1,0,.07],[.3,1.28,0,.075],[.58,1.2,0,.06],[.76,.98,0,.04],[.82,.8,0,.015]],mat:\"energy\",sides:6},{tube:[[.05,1,0,.04],[.3,1.16,0,.05],[.56,1.1,0,.04]],mat:\"shell\",sides:5},o(1.12,.09,\"glass\")]},{len:1.15,ring:.03,carry:\"up\",pieces:[t(-.12,.12,.045),{tube:[[0,.1,0,.045],[.1,.3,0,.04],[.22,.6,0,.035],[.26,.85,0,.03],[.18,1.15,0,.02]],mat:\"shell\",sides:6},{tube:[[0,-.1,0,.045],[.1,-.2,0,.04],[.22,-.35,0,.035],[.26,-.48,0,.03],[.18,-.58,0,.02]],mat:\"shell\",sides:6},{tube:[[.22,.45,0,.026],[.25,.7,0,.026],[.22,.95,0,.026]],mat:\"energy\",sides:5},{tube:[[.18,1.15,0,.018],[-.1,.3,0,.018],[.18,-.58,0,.018]],mat:\"energy\",sides:4},{box:[-.04,0,-.05,.08,.2,.05],mat:\"energy\"}]},{len:1,ring:.045,carry:\"up\",pieces:[{box:[-.06,-.4,-.07,.06,.08,.07],mat:\"dark\"},{box:[-.09,0,-.1,.09,.4,.1],mat:\"shell\"},{tube:[[0,.4,0,.05],[0,1,0,.05]],mat:\"shell\",sides:6},r(.5,.1,.05),r(.65,.1,.05),r(.8,.1,.05),{box:[-.1,.92,-.1,.1,1,.1],mat:\"energy\"},{box:[-.1,.06,.1,.1,.26,.14],mat:\"glass\"},{box:[-.03,.2,-.17,.03,.34,-.1],mat:\"dark\"}]},{len:.9,ring:.026,carry:\"up\",pieces:[t(-.2,.2,.04),{box:[-.25,.22,-.035,.25,.3,.035],mat:\"shell\"},s(.52,.34,.04,\"energy\"),s(.52,.22,.07,\"shell\"),s(.52,.1,.09,\"white\"),{fan:[[0,.52,.09],[.1,.3,.09],[.05,.14,.09]],mat:\"dark\"}]},{len:.3,ring:.06,carry:\"worn\",pieces:[{loft:[[-.4,.13,.12,.8],[-.2,.15,.14,.8],[0,.13,.12,.8]],mat:\"shell\",seg:10},r(-.3,.16,.05,\"energy\",.15),r(-.1,.16,.05,\"energy\",.15),{loft:[[0,.16,.15,.7],[.18,.19,.18,.6],[.32,.15,.15,.6]],mat:\"shell\",seg:10},{box:[-.15,.16,.15,.15,.3,.22],mat:\"energy\"},{box:[-.06,.02,.17,.06,.12,.23],mat:\"white\"},{box:[-.2,.2,-.03,-.15,.36,.08],mat:\"dark\"},{box:[.15,.2,-.03,.2,.36,.08],mat:\"dark\"}]},{len:1.4,ring:.03,carry:\"up\",pieces:[m(-.55,.85,.045),r(0,.07,.06,\"dark\"),r(.45,.07,.05,\"energy\"),r(-.4,.07,.05,\"energy\"),{tube:a(0,1.12,.22,.05,14,0),mat:\"shell\",sides:5},{tube:a(0,1.12,.22,.03,14,.05),mat:\"energy\",sides:4},o(1.12,.13,\"glass\"),o(1.12,.07,\"white\"),{box:[-.07,.85,-.07,.07,.9,.07],mat:\"shell\"}]},{len:.8,ring:.03,carry:\"up\",pieces:[{box:[-.045,-.2,-.045,.045,.3,.045],mat:\"dark\"},r(0,.06,.04,\"energy\"),r(.15,.06,.04,\"energy\"),{loft:[[.3,.05,.05,.8],[.45,.09,.09,.8],[.62,.17,.17,.8],[.7,.2,.2,.8]],mat:\"shell\",seg:12,caps:!1},{loft:[[.5,.06,.06,.8],[.66,.15,.15,.8]],mat:\"energy\",seg:12,caps:!1},o(.52,.07,\"white\"),r(.68,.2,.025,\"energy\")]},{len:.5,ring:.04,carry:\"up\",pieces:[{box:[-.14,-.06,-.07,.14,.5,.07],mat:\"shell\"},{box:[-.12,-.04,-.075,.12,.48,.075],mat:\"dark\"},{box:[-.14,-.06,-.08,-.1,.5,.08],mat:\"shell\"},{box:[-.12,0,.07,.12,.44,.09],mat:\"shell\"},o(.22,.1,\"energy\",0,.1),o(.22,.05,\"white\",0,.1),{box:[-.1,.34,.09,-.04,.4,.12],mat:\"energy\"},{box:[.04,.34,.09,.1,.4,.12],mat:\"energy\"},{box:[-.1,.04,.09,-.04,.1,.12],mat:\"energy\"},{box:[.04,.04,.09,.1,.1,.12],mat:\"energy\"}]},{len:1.17,ring:.05,carry:\"up\",pieces:[{box:[-.1,-.35,-.1,.1,.1,.1],mat:\"dark\"},{box:[-.19,.1,-.19,.19,.4,.19],mat:\"shell\"},{tube:[[0,.4,0,.12],[0,1.1,0,.12]],mat:\"shell\",sides:8},r(.5,.15,.06),r(.7,.15,.06),r(.9,.15,.06),{loft:[[1.1,.15,.15],[1.16,.15,.15]],mat:\"energy\",seg:10},{loft:[[1.1,.07,.07],[1.17,.07,.07]],mat:\"white\",seg:8},o(.25,.12,\"energy\",0,.19),{box:[-.2,.15,-.17,-.19,.38,.17],mat:\"energy\"},{box:[.19,.15,-.17,.2,.38,.17],mat:\"energy\"}]}]})();var t1={armor:Dt,arms:Ot,back:Ct,crown:Nt,facial:Yt,feet:Bt,hair:Wt,hands:Ft,headwear:Xt,jaw:jt,legs:Gt,neck:Kt,pets:Ut,shield:Vt,shoulders:Zt,tail:qt,torso:Qt,weapons:Jt};var lo=[\"rapier\",\"greatsword\",\"dagger\",\"sabre\",\"axe\",\"hammer\",\"mace\",\"flail\",\"spear\",\"trident\",\"halberd\",\"scythe\",\"bow\",\"crossbow\",\"chakram\",\"gauntlets\",\"staff\",\"wand\",\"tome\",\"cannon\"],uo=[\"#59677e\",\"#889ab4\",\"#c4cae3\"],fo=[\"#58e8ff\",\"#76ffc8\",\"#ba8cff\",\"#ff879f\",\"#ffd76c\"],E1=new Map,A0=(t,r)=>{let o=t+JSON.stringify(r||0);return E1.has(o)||E1.set(o,k0(t,r)),E1.get(o)};function _1(t,r=null){let o=r?.shell||(t>=20?\"#e6c880\":uo[t<8?0:t<16?1:2]),a=r?.energy||fo[Math.min(4,Math.floor(t/5))];return{shell:A0(o),metal:A0(o),energy:A0(a,{lift:.08,spread:.7}),glow:A0(a,{lift:.08,spread:.7}),dark:A0(\"#283147\"),edge:A0(\"#dbe8ff\",{lift:.06,spread:.5}),white:A0(\"#f3ffff\",{spread:.4}),gold:A0(\"#e6c880\"),glass:A0(\"#9fe8ff\",{lift:.06}),trim:A0(a),accent:A0(a,{lift:.08,spread:.7})}}function po(t,r){let o=[],a=t.carry===\"worn\",s=a?.2:(t.ring??.032)*1.9+.03,m=t.len??.6,i=a?-.5:-.1;for(let n=0;n<r%5;n++){let e=i-n*.05;o.push({loft:[[e-.03,s,s],[e,s,s]],mat:\"energy\",seg:10,caps:!1})}return r>=5&&!a&&o.push({loft:[[-.36,.02,.02],[-.32,s*.9,s*.9],[-.27,s*.9,s*.9],[-.23,.02,.02]],mat:\"energy\",seg:10}),r>=10&&o.push({fan:[[0,i,0],[s+.1,i-.2,0],[s+.14,i+.08,0],[0,i+.14,0]],mirror:!0,mat:\"shell\"}),r>=15&&o.push({loft:[[m,0,0],[m+.03,.045,.045],[m+.08,.045,.045],[m+.11,0,0]],mat:\"energy\",seg:10}),o}var r1=new Map;function $t(t){let r=t.type+\":\"+t.tier;if(r1.has(r))return r1.get(r);let o=(t1.weapons||[])[lo.indexOf(t.type)],a=[];if(o)for(let m of[...o.pieces,...po(o,t.tier)]){let{faces:i}=J0({group:\"armR\",...m});for(let n of i){for(let e=0;e<4;e++)n.p[e*3+1]/=O;a.push(n)}}let s={faces:a,design:o||{carry:\"up\"}};return r1.set(r,s),r1.size>64&&r1.delete(r1.keys().next().value),s}var I1=t=>{let r=Math.hypot(...t)||1;return t.map(o=>o/r)},go=(t,r)=>[t[1]*r[2]-t[2]*r[1],t[2]*r[0]-t[0]*r[2],t[0]*r[1]-t[1]*r[0]];function tr(t,r,o){let a=r||t.base,s=.9875*O,m=t.anim.elbowY,i=[0,0,0],n=[0,0,0],e=0,p=0;if(t.faces.forEach((c,z)=>{if(c.g!==\"armR\"||c.mark)return;let x=t.base[z*12+1];for(let k=0;k<4;k++){let w=z*12+k*3;x<s?(i[0]+=a[w],i[1]+=a[w+1],i[2]+=a[w+2],e++):x<m&&(n[0]+=a[w],n[1]+=a[w+1],n[2]+=a[w+2],p++)}}),!e||!p)return null;for(let c=0;c<3;c++)i[c]/=e,n[c]/=p;let g=I1([i[0]-n[0],i[1]-n[1],i[2]-n[2]]),{faces:b,design:f}=$t(o),y=f.carry===\"worn\"?g:I1([-g[0]+.75,-g[1],-g[2]+.35]),u=[0,0,1],d=u[0]*y[0]+u[1]*y[1]+u[2]*y[2];u=I1([u[0]-d*y[0],u[1]-d*y[1],u[2]-d*y[2]]);let h=go(y,u),l=new Float32Array(b.length*12);return b.forEach((c,z)=>{for(let x=0;x<4;x++){let k=c.p[x*3],w=c.p[x*3+1],M=c.p[x*3+2],L=z*12+x*3;for(let P=0;P<3;P++)l[L+P]=i[P]+k*h[P]+w*y[P]+M*u[P]}}),{faces:b,pos:l}}function rr(t,{w:r=40,h:o=72,palette:a=null,yaw:s=.5,tilt:m=0}={}){let{faces:i}=$t(t),n=_1(t.tier,a);for(let l of i)l.r=n[l._mat]||n.shell;if(!i.length)return{data:new Uint8ClampedArray(r*o*4),w:r,h:o};let e=u1(s,0),p=Math.cos(m),g=Math.sin(m),b=new Float32Array(i.length*12),f=1/0,y=-1/0,u=1/0,d=-1/0;i.forEach((l,c)=>{for(let z=0;z<4;z++){let x=l.p[z*3],k=l.p[z*3+1],w=c*12+z*3;b[w]=p*x-g*k,b[w+1]=g*x+p*k,b[w+2]=l.p[z*3+2];let[M,L]=e(b[w],b[w+1],b[w+2]);f=Math.min(f,M),y=Math.max(y,M),u=Math.min(u,L),d=Math.max(d,L)}});let h=Math.min(60,(r-4)/Math.max(.05,y-f),(o-4)/Math.max(.05,d-u));return $0(i,{w:r,h:o,scale:h,ox:r/2-(f+y)/2*h,oy:o/2+(u+d)/2*h,yaw:s,pitch:0,pos:b})}var ho=[[\"body\",\"Body\",\"A silhouette from somewhere very far away.\",[\"Zorbian\",\"Vexling\",\"Orryx\",\"Mollu\",\"Krell\",\"Nymbi\",\"Quorlan\",\"Xelith\",\"Dravox\",\"Ulumi\"]],[\"skin\",\"Skin\",\"Twenty tones of otherworldly complexion.\",[\"Nebula lilac\",\"Reactor mint\",\"Solar coral\",\"Lunar porcelain\",\"Void indigo\",\"Comet gold\",\"Plasma rose\",\"Tidal teal\",\"Martian ochre\",\"Ghost ice\"]],[\"face\",\"Face\",\"Make an unforgettable first impression.\",[\"Vela gaze\",\"Solo orb\",\"Triune sight\",\"Obsidian visor\",\"Quasar quartet\",\"Mothkin eyes\",\"Ziggy grin\",\"Mollu blush\",\"Starborn mask\",\"Oracle six\"]],[\"hair\",\"Hair\",\"Coiffed, grown, or very carefully hatched.\",[\"Vex wave\",\"Quasar crest\",\"Nebula bob\",\"Orbital knots\",\"Plasma cascade\",\"Spore crown\",\"Void slick\",\"Comet braid\",\"Prism spikes\",\"Lunar fringe\"]],[\"facial\",\"Facial details\",\"Whiskers, frills, and social signals.\",[\"Vela bare\",\"Zor whiskers\",\"Krell beard\",\"Mollu frill\",\"Oracle dots\",\"Quor tendrils\",\"Vex moustache\",\"Dravox jaw\",\"Nymbi veil\",\"Ulumi sparkle\"]],[\"headwear\",\"Headwear\",\"MOM can spot good headwear across a galaxy.\",[\"Vela circlet\",\"Orryx crown\",\"Quasar halo\",\"Nymbi veil\",\"Krell cap\",\"Xelith horns\",\"Mollu fascinator\",\"Dravox helm\",\"Orbital rings\",\"Comet antennae\"]],[\"neck\",\"Neckwear\",\"The smallest, most unreasonable finishing touch.\",[\"Zor cravat\",\"Vela bow\",\"Orryx pearls\",\"Quasar ruff\",\"Mollu scarf\",\"Krell collar\",\"Xelith pendant\",\"Nymbi ribbon\",\"Dravox chain\",\"Ulumi choker\"]],[\"torso\",\"Formalwear\",\"Alien eveningwear. Excellent tailoring.\",[\"Vex tuxedo\",\"Mollu gown\",\"Orryx brocade\",\"Quasar jumpsuit\",\"Nymbi corset\",\"Krell robe\",\"Xelith doublet\",\"Dravox tailcoat\",\"Ulumi wrap\",\"Zor sequin suit\"]],[\"shoulders\",\"Shoulders\",\"A little room for dramatic entrances.\",[\"Vela epaulettes\",\"Quor petals\",\"Krell spikes\",\"Nymbi puffs\",\"Orryx mantle\",\"Vex wings\",\"Dravox plates\",\"Mollu fronds\",\"Xelith orbitals\",\"Ulumi cape\"]],[\"arms\",\"Sleeves\",\"Twenty ways to wave at someone important.\",[\"Vela silk\",\"Quasar flares\",\"Krell cuffs\",\"Mollu lace\",\"Orryx stripes\",\"Vex sheer\",\"Dravox panels\",\"Nymbi bells\",\"Xelith rings\",\"Ulumi ribbons\"]],[\"hands\",\"Gloves\",\"Keep your hands where MOM can see them.\",[\"Vela gloves\",\"Krell talons\",\"Orryx cuffs\",\"Quasar mesh\",\"Mollu mitts\",\"Vex rings\",\"Dravox gauntlets\",\"Nymbi ruffles\",\"Xelith claws\",\"Ulumi glow\"]],[\"legs\",\"Lower half\",\"Trousers, trains, and several extra possibilities.\",[\"Vex trousers\",\"Mollu bell skirt\",\"Orryx pleats\",\"Quasar split\",\"Nymbi bubble\",\"Krell drape\",\"Xelith stripes\",\"Dravox breeches\",\"Ulumi train\",\"Zor shimmer\"]],[\"feet\",\"Footwear\",\"For the marble floors of an alien palace.\",[\"Vela slippers\",\"Krell platforms\",\"Orryx curltoes\",\"Quasar boots\",\"Mollu petals\",\"Vex heels\",\"Dravox greaves\",\"Nymbi clouds\",\"Xelith skates\",\"Ulumi moonsteps\"]],[\"held\",\"Held accessory\",\"An arrival accessory, not a combat loadout.\",[\"Vela flute\",\"Orryx fan\",\"Quasar clutch\",\"Mollu bouquet\",\"Krell cane\",\"Nymbi lantern\",\"Vex invitation\",\"Dravox orb\",\"Xelith parasol\",\"Ulumi familiar\"]],[\"back\",\"Back accessory\",\"Make leaving the room just as interesting.\",[\"Vela ribbons\",\"Orryx cape\",\"Quasar fins\",\"Mollu spores\",\"Krell spines\",\"Nymbi wings\",\"Vex sash\",\"Dravox coils\",\"Xelith satellites\",\"Ulumi starlight\"]],[\"base\",\"Display base\",\"A tiny piece of the palace to call your own.\",[\"Vela marble\",\"Orryx dais\",\"Quasar moon\",\"Mollu garden\",\"Krell obsidian\",\"Nymbi cloud\",\"Vex carpet\",\"Dravox grille\",\"Xelith crystal\",\"Ulumi orbit\"]],[\"pet\",\"Pet\",\"A companion dressed for the same questionable occasion.\",[\"No companion\",\"Mollu pup\",\"Vex moth\",\"Orryx beetle\",\"Quasar cat\",\"Nymbi jelly\",\"Krell lizard\",\"Xelith puff\",\"Dravox bot\",\"Ulumi sprout\"]]],or=[[\"build\",\"Build\",\"Proportions from a gravity you would not believe.\",[\"Species build\",\"Slight\",\"Stout\",\"Broad\",\"Lanky\",\"Heavy\",\"Wiry\",\"Hulking\",\"Petite\",\"Long-limbed\"]],[\"head\",\"Head shape\",\"A skull that tells the room where you are from.\",[\"Species head\",\"Dome\",\"Long skull\",\"Hammerhead\",\"Crest ridge\",\"Bulb\",\"Flat-top\",\"Wedge\",\"Mantis\",\"Twin lobe\"]],[\"crown\",\"Antennae & crests\",\"Sensors, crests and the occasional horn.\",[\"No crest\",\"Feelers\",\"Crest fin\",\"Ram horns\",\"Spike crown\",\"Neck frill\",\"Bulb antennae\",\"Twin horns\",\"Ridge spines\",\"Halo sensors\"]],[\"jaw\",\"Jaw\",\"Mandibles, tusks and other table manners.\",[\"No jaw piece\",\"Mandibles\",\"Tusks\",\"Snout\",\"Beak\",\"Tendril beard\",\"Fangs\",\"Mouth plates\",\"Gill frills\",\"Chin horn\"]],[\"tail\",\"Tail\",\"Every species looks better leaving.\",[\"No tail\",\"Lizard tail\",\"Cat tail\",\"Stinger\",\"Fin tail\",\"Whip tail\",\"Club tail\",\"Plume tail\",\"Twin tails\",\"Coil tail\"]],[\"armor\",\"Armour\",\"Hardsuit pieces over the formalwear.\",[\"No armour\",\"Hardsuit torso\",\"Shoulder pods\",\"Bracer gauntlets\",\"Thigh plates\",\"Armoured boots\",\"Pressure helm (open)\",\"Pressure helm (closed)\",\"Half hardsuit\",\"Full hardsuit\"]],[\"alloy\",\"Alloy\",\"What the armour is made of.\",x1.map(([t])=>t)],[\"shield\",\"Shield\",\"Held on the off hand.\",[\"No shield\",\"Wrist buckler\",\"Disc\",\"Shaped barrier\",\"Tower field\",\"Hex shield\",\"Ring guard\",\"Spike targe\",\"Energy kite\",\"Mirror disc\"]]],yo=new Set(or.map(([t])=>t)),bo=([t,r,o,a])=>({id:t,label:r,note:o,names:[...a,...a.map(s=>\"Xyrr \"+(s===\"No companion\"?\"slug\":s)),...a.map(s=>\"Auv \"+(s===\"No companion\"?\"slug\":s)),...a.map(s=>\"Oth \"+(s===\"No companion\"?\"slug\":s))]}),xo={body:[0,11,2,12,32,3,13,33,14,34,15,35,6,16,36,37,18,28,19,39],skin:[0,20,11,12,32,23,33,14,34,5,25,35,6,26,36,7,37,38,19,39],face:[0,21,31,22,32,23,33,14,24,34,25,35,26,36,27,28,38,19,29,39],hair:[0,11,31,12,22,32,13,33,14,34,15,35,36,17,27,37,18,38,19,29],facial:[0,11,21,31,12,32,13,23,33,14,34,15,25,35,26,17,27,18,38,39],headwear:[0,11,21,12,22,32,13,23,33,14,24,34,25,16,36,37,38,19,29,39],neck:[0,20,30,11,2,13,23,33,14,24,34,15,25,36,7,27,37,28,19,29],torso:[0,11,31,12,22,32,13,24,34,25,35,26,36,17,27,37,18,28,38,9],shoulders:[0,30,11,12,23,33,24,15,25,35,16,36,17,27,37,18,38,19,29,39],arms:[0,11,12,13,33,14,15,25,6,26,36,7,27,37,18,28,38,19,29,39],hands:[0,11,21,31,12,22,33,14,24,34,25,36,17,27,37,18,28,38,9,29],legs:[0,11,21,31,22,32,23,24,34,15,25,35,16,36,27,37,18,28,38,29],feet:[0,11,21,31,12,32,3,13,14,24,34,25,26,36,17,37,28,38,9,29],held:[0,11,21,31,32,23,33,14,24,34,25,16,17,27,37,18,28,38,29,39],back:[0,11,21,31,12,22,13,23,33,14,34,15,35,16,26,17,37,38,19,29],base:[0,11,2,22,32,23,4,34,5,25,35,16,26,36,37,18,28,38,19,39],pet:[0,10,20,30,11,12,32,3,24,15,25,35,36,27,37,28,38,19,29,39],build:[0,1,2,3,4,5,6,7,8,9],head:[0,1,2,3,4,5,6,7,8,9],alloy:[0,1,2,3,4,5,6,7,8,9],crown:[0,1,2,3,4,5,6,7,8,9,21,33,17,39],jaw:[0,1,2,3,4,5,6,7,8,9,22,31,16],tail:[0,1,2,3,4,5,6,7,8,9,21,32,17,38],armor:[0,1,2,3,4,5,6,7,8,9,18,29,39],shield:[0,1,2,3,4,5,6,7,8,9,22,33,17]},G0=[...ho,...or].map(bo),T1=(t,r)=>t===\"pet\"&&r===0?\"none\":r%10;G0.forEach(t=>{let r=new Map;for(let o of xo[t.id])r.has(T1(t.id,o))||r.set(T1(t.id,o),o);t.choices=[...r.values()],t.canonical=o=>r.get(T1(t.id,o))??o});var o1=[\"#9762b6\",\"#bd476e\",\"#467f9e\",\"#4b9478\",\"#d39d46\",\"#485aa0\",\"#d17e52\",\"#c7adba\",\"#5f596d\",\"#83b8b6\"],zo=[\"#b18fc8\",\"#80ba98\",\"#d78989\",\"#ddd3c5\",\"#7371ae\",\"#c6a55e\",\"#ce84b6\",\"#67a5a6\",\"#b77857\",\"#a5c8d8\"],v1={schema:\"mominc-avatar\",version:2,name:\"Velora of the Ninth Moon\",dye:0,trim:4,parts:Object.fromEntries(G0.map(t=>[t.id,0]))};function D1(t,r=globalThis.window?.GalaWeapons){if(!t||t.schema!==\"mominc-avatar\"||t.version!==1&&t.version!==2||!t.parts)throw Error(\"Choose a MOM Inc avatar file.\");let o={schema:\"mominc-avatar\",version:2,name:typeof t.name==\"string\"?t.name.trim().slice(0,64):\"\",dye:t.dye,trim:t.version===2&&t.trim!=null?t.trim:t.dye,parts:{}};if(!Number.isInteger(o.dye)||o.dye<0||o.dye>9)throw Error(\"This look has an unknown silk colour.\");if(!Number.isInteger(o.trim)||o.trim<0||o.trim>9)throw Error(\"This look has an unknown trim colour.\");for(let a of G0){let s=t.parts[a.id]??(a.id===\"pet\"||yo.has(a.id)?0:void 0);if(!Number.isInteger(s)||s<0||s>=a.names.length)throw Error(\"This look has an unknown wardrobe piece.\");o.parts[a.id]=a.canonical(s)}if(t.weapon!=null){if(!r)throw Error(\"Open the updated Gala to import this weapon.\");o.weapon=r.normalize(t.weapon)}return o}var z1=(t,r)=>{let o=parseInt(t.slice(1),16);return\"#\"+[o>>16,o>>8&255,o&255].map(a=>Math.max(0,Math.min(255,a+r)).toString(16).padStart(2,\"0\")).join(\"\")};function p1(t){let r=t.parts.skin||0,o=Math.floor(r/10);return z1(zo[r%10],o*14-(o===3?60:0))}var er={jaw:[{y:1.7,rx:.052,rz:.07,cz:.01},{y:1.74,rx:.082,rz:.105,cz:.005}],seam:[{y:1.79,rx:.102,rz:.13,cz:-.005}]},E0=(t,r=er.jaw,o=er.seam)=>({jaw:r,seam:o,skull:t.map(([a,s,m,i])=>({y:a,rx:s,rz:m,cz:i}))}),Y0=[null,E0([[1.85,.112,.138,-.015],[1.94,.118,.14,-.02],[2.03,.1,.118,-.02],[2.1,.06,.07,-.015],[2.13,.02,.025,-.01]]),E0([[1.85,.104,.15,-.03],[1.92,.098,.175,-.06],[1.98,.08,.185,-.09],[2.03,.05,.15,-.12],[2.06,.02,.08,-.13]]),E0([[1.85,.15,.12,-.015],[1.9,.18,.1,-.02],[1.96,.15,.09,-.02],[2,.08,.07,-.015]]),E0([[1.85,.108,.14,-.015],[1.93,.088,.135,-.02],[2,.048,.12,-.025],[2.07,.012,.095,-.03]]),E0([[1.85,.12,.15,-.02],[1.95,.142,.168,-.03],[2.05,.132,.152,-.03],[2.13,.09,.1,-.025],[2.18,.03,.035,-.02]]),E0([[1.85,.112,.14,-.015],[1.95,.114,.138,-.02],[1.99,.108,.13,-.02],[2,.04,.05,-.02]]),E0([[1.85,.125,.14,-.015],[1.93,.118,.13,-.02],[1.99,.085,.1,-.018],[2.03,.04,.05,-.012]],[{y:1.67,rx:.015,rz:.03,cz:.02},{y:1.74,rx:.06,rz:.09,cz:.01}]),E0([[1.86,.14,.12,-.01],[1.92,.165,.11,-.015],[1.96,.125,.09,-.015],[1.99,.04,.04,-.01]],[{y:1.69,rx:.02,rz:.04,cz:.03},{y:1.74,rx:.05,rz:.08,cz:.02}],[{y:1.79,rx:.09,rz:.11,cz:0}]),E0([[1.85,.12,.14,-.015],[1.93,.125,.13,-.02],[1.98,.1,.11,-.02],[2.01,.05,.06,-.02]])],ko=[{loft:[[1.93,.04,.05],[1.99,.075,.085],[2.06,.07,.08],[2.11,.03,.035]],cx:.07,mirror:!0,group:\"head\",mat:\"skin\",seg:10}],D0=[null,{torso:.8,shoulder:.8,arm:.78,hand:.85,neck:.85,leg:.82},{torso:1.38,shoulder:1.1,arm:1.2,neck:1.35,jaw:1.2,leg:1.3},{torso:1.15,shoulder:1.55,arm:1.3,hand:1.2,neck:1.15,leg:1.05},{torso:.78,shoulder:.95,arm:.72,hand:1.1,neck:.75,leg:.72},{torso:1.55,shoulder:1.25,arm:1.35,hand:1.25,neck:1.4,jaw:1.3,leg:1.45},{torso:.9,shoulder:1.15,arm:.75,hand:1,neck:.8,leg:.8},{torso:1.6,shoulder:1.6,arm:1.6,hand:1.5,neck:1.5,jaw:1.4,skull:.9,leg:1.5},{torso:.7,shoulder:.72,arm:.7,hand:.8,neck:.8,skull:1.15,jaw:.8,leg:.7},{torso:.95,shoulder:1,arm:1.15,hand:1.5,leg:.9}],O1=[{build:{skull:1.04},head:null,pieces:[]},{build:D0[1],head:null,pieces:[{fan:[[.095,1.86,-.01],[.13,1.83,-.03],[.205,2,-.08],[.11,1.93,-.04]],mirror:!0,group:\"head\",mat:\"skin\"},{fan:[[.1,1.86,0],[.17,1.95,-.06],[.12,1.9,-.035]],mirror:!0,group:\"head\",mat:\"skinDark\"}]},{build:{torso:1.15,shoulder:1.15},head:Y0[1],pieces:[{tube:[[.045,1.98,0,.022],[.075,2.06,.02,.018],[.095,2.11,.045,.015]],mirror:!0,group:\"head\",mat:\"skinDark\"},{loft:[[2.095,0,0],[2.105,.03,.03],[2.135,.03,.03],[2.145,0,0]],cx:.095,mirror:!0,group:\"head\",mat:\"accent\",seg:8}]},{build:D0[2],head:Y0[5],pieces:[{dfo:\"tail:khajiit\",mat:\"skin\"},{loft:[[1.83,0,0],[1.85,.035,.025],[1.9,.04,.03],[1.94,0,0]],cx:.12,mirror:!0,group:\"head\",mat:\"skin\",seg:8}]},{build:D0[7],head:E0([[1.85,.118,.14,-.015],[1.92,.108,.135,-.02],[1.98,.088,.108,-.018],[2.03,.05,.058,-.012]]),pieces:[{fan:[[.1,1.86,-.01],[.24,1.9,-.05],[.25,1.8,-.07],[.11,1.8,-.03]],mirror:!0,group:\"head\",mat:\"skin\"},{dfo:\"brow\",mat:\"skinDark\"}]},{build:D0[1],head:null,pieces:[{fan:[[.09,1.8,.02],[.2,1.88,-.03],[.23,1.78,-.05],[.19,1.68,-.04],[.08,1.73,0]],mirror:!0,group:\"head\",mat:\"trim\"},{tube:[[.1,1.79,.01,.012],[.21,1.8,-.04,.01]],mirror:!0,group:\"head\",mat:\"accent\"}]},{build:{torso:1.05,leg:.8},head:Y0[5],pieces:[{tube:[[.07,.6,.04,.05],[.11,.35,.09,.045],[.12,.12,.16,.035],[.06,.04,.26,.022],[0,.08,.3,.012]],mirror:!0,group:\"body\",mat:\"skinDark\"},{tube:[[0,.62,.05,.05],[0,.35,.12,.04],[0,.1,.2,.03],[0,.04,.3,.015]],group:\"body\",mat:\"skin\"},{tube:[[.06,.6,-.05,.045],[.12,.3,-.12,.04],[.14,.06,-.2,.025]],mirror:!0,group:\"body\",mat:\"skinDark\"}]},{build:{torso:.82,shoulder:.85,arm:.85,leg:.88,skull:1.08},head:Y0[2],pieces:[{dfo:\"tail:argonian\",mat:\"skin\"}]},{build:D0[3],head:null,pieces:[{dfo:\"race:Khajiit\",mat:\"skin\"},{mark:\"stud\",at:[.085,1.88,.09],n:[.6,0,.8],mirror:!0,mat:\"gold\"}]},{build:{skull:1.1},head:Y0[1],pieces:[{tube:[[.03,2,-.02,.018],[.05,2.07,-.04,.015],[.08,2.11,-.02,.013]],mirror:!0,group:\"head\",mat:\"skinDark\"},{loft:[[2.09,0,0],[2.1,.026,.026],[2.13,.026,.026],[2.14,0,0]],cx:.08,mirror:!0,group:\"head\",mat:\"glow\",seg:8},{mark:\"dot\",at:[.07,1.79,.1],mirror:!0,mat:\"accent\"}]}],w0=(t=\"eye\",r=1.86,o=.045,a=.122,s=\"dark\")=>({mark:t,at:[o,r,a],mirror:o!==0,mat:s}),N0={mark:\"mouth\",at:[0,1.765,.105],mat:\"dark\"},wo=[[w0(\"eye\",1.86,.045,.122,\"trim\"),N0],[w0(\"orb\",1.865,0,.13,\"glow\"),N0],[w0(\"eye\",1.85,.05,.122,\"dye\"),w0(\"orb\",1.935,0,.12,\"glow\"),N0],[{mark:\"visor\",at:[0,1.865,.13],mat:\"glow\"},N0],[w0(\"eye\",1.88,.05,.12,\"dye\"),w0(\"eye\",1.83,.04,.122,\"glow\"),N0],[w0(\"compound\",1.87,.055,.115,\"dark\"),N0],[w0(\"eye\",1.865,.045,.122,\"accent\"),{mark:\"grin\",at:[0,1.765,.105],mat:\"dark\"}],[w0(\"eye\",1.86,.045,.122,\"dark\"),{mark:\"blush\",at:[.07,1.8,.105],mirror:!0,mat:\"dark\"},N0],[{loft:[[1.83,.11,.14,1,-.012],[1.9,.115,.145,1,-.014]],group:\"head\",mat:\"accent\",arc:[-.2,Math.PI+.2],seg:14,caps:!1},w0(\"eye\",1.865,.045,.14,\"glow\")],[w0(\"dot\",1.89,.03,.125,\"glow\"),w0(\"dot\",1.89,.07,.112,\"glow\"),w0(\"dot\",1.845,.05,.12,\"glow\"),N0]],d1={body:O1.map(t=>t.pieces),face:wo,head:Y0.map((t,r)=>r===9?ko:[]),build:D0.map(()=>[]),alloy:D0.map(()=>[])};var Mo={torso:[{zone:\"torso\",yLo:1,yHi:1.64,th:.012,mat:\"dye\"},{zone:\"sleeves\",yLo:1.02,yHi:1.64,th:.01,mat:\"dye\"},{fan:[[0,1.3,.11],[-.07,1.6,.1],[.07,1.6,.1]],mat:\"white\"},{fan:[[0,1.28,.112],[.075,1.6,.105],[.12,1.58,.095],[.05,1.4,.11]],mirror:!0,mat:\"trim\"},{mark:\"stud\",at:[0,1.22,.1],group:\"body\",mat:\"accent\"},{mark:\"stud\",at:[0,1.14,.098],group:\"body\",mat:\"accent\"}],legs:[{zone:\"legs\",yLo:.12,yHi:1,th:.01,mat:\"dye\"},{zone:\"pelvis\",yLo:.9,yHi:1.12,th:.012,mat:\"dye\"}],feet:[{zone:\"feet\",yLo:0,yHi:.11,th:.01,mat:\"trim\"},{box:[.06,0,.17,.104,.06,.24],mirror:!0,group:\"leg\",mat:\"accent\"}],hair:[{dfo:\"hair:short\",mat:\"hair\"},{tube:[[-.09,2,.08,.03],[-.02,2.05,.12,.035],[.07,2.02,.11,.03],[.12,1.94,.07,.02]],group:\"head\",mat:\"hair\"}],headwear:[{loft:[[1.905,.118,.15,1,-.014],[1.93,.118,.15,1,-.014]],group:\"head\",mat:\"accent\",seg:16,caps:!1},{mark:\"gem\",at:[0,1.92,.138],mat:\"accent\"}],neck:[{loft:[[1.62,.04,.03],[1.66,.05,.04],[1.7,.04,.03]],group:\"body\",mat:\"white\",seg:8},{fan:[[0,1.6,.11],[-.03,1.45,.115],[0,1.4,.118],[.03,1.45,.115]],mat:\"white\"}],shoulders:[{box:[.17,1.63,-.06,.31,1.67,.06],mirror:!0,group:\"arm\",mat:\"accent\"},{box:[.29,1.56,-.05,.31,1.63,.05],mirror:!0,group:\"arm\",mat:\"accent\"}],arms:[{zone:\"sleeves\",yLo:.99,yHi:1.08,th:.018,mat:\"trim\"}],hands:[{zone:\"hands\",th:.008,mat:\"white\"}],back:[{tube:[[.08,1.55,-.1,.02],[.11,1.2,-.16,.022],[.13,.85,-.2,.02],[.14,.6,-.22,.012]],mirror:!0,group:\"body\",mat:\"trim\"}],facial:[]};for(let[t,r]of Object.entries(t1))d1[t]=r;for(let[t,r]of Object.entries(Mo))d1[t]=d1[t]||[r];function e1(t,r,o,a){let s=Math.cos(a),m=Math.sin(a),i=t-o;return[o+s*i-m*r,m*i+s*r]}function ar(t,r,o,a,s){let m=Math.cos(s),i=Math.sin(s),n=t-o,e=r-a;return[o+m*n-i*e,a+i*n+m*e]}function k1(t,r,o,a,s){let m=Math.cos(s),i=Math.sin(s),n=t-o,e=r-a;return[o+m*n+i*e,a-i*n+m*e]}var sr={strideAmp:.05,cadence:1.7,counterFrac:1,bobAmp:.006,kneeAmp:.12,kneeBase:0,elbowAmp:.12,elbowBase:0,lean:0},C1={strideAmp:.44,cadence:6,counterFrac:1.1,bobAmp:.024,kneeAmp:.9,kneeBase:0,elbowAmp:.35,elbowBase:.18,lean:.05,kneeR:4,elbowR:2};function nr({basePos:t,vgrp:r,armX:o,wristY:a,neckY:s}){let m=b=>{let f=1/0,y=-1/0;for(let u=0;u<r.length;u++)if(r[u]===b){let d=t[u*3+1];d<f&&(f=d),d>y&&(y=d)}return[f,y]},i=b=>{let f=0,y=0;for(let u=0;u<r.length;u++)r[u]===b&&(f+=t[u*3],y++);return y?f/y:0},[n,e]=m(4),[p,g]=m(2);return{hipY:e,shoulderY:g,ankleY:n,kneeY:e-.52*(e-n),elbowY:g-.5*(g-p),wristY:a,armX:o,neckY:s,armRootX:{2:i(2),3:i(3)}}}function mr(t,r,o,a,s,m,i){let{armRootX:n,wristY:e,elbowY:p,kneeY:g,hipY:b,shoulderY:f,armX:y,neckY:u}=t,d=r.pos,h=r.base,l=r.vgrp,c=b+.1,z=(a.lean||0)+(m&&m.lean||0),x=m&&m.twist||0,k=(a.headPitch||0)+(m&&m.headPitch||0),w=m&&m.rootX||0,M=m&&m.rootY||0,L=m&&m.rootZ||0,P=!!r.held,S=(A,_,R,T)=>{let v=m&&m[T],C,I,X=-a.strideAmp*(R?1:a.counterFrac)*Math.sin(o+_),t0=R?a.kneeBase+a.kneeAmp*Math.max(0,Math.sin(o+_+1.2)):a.elbowBase+a.elbowAmp*(.5+.5*Math.sin(o+_-.6));v&&!R?(C=v.sw+(i?(m.gaitArm??0)*X:0),I=v.bd+(i?(m.runElbow??0)+(m.gaitElbow??0)*(t0-a.elbowBase-.5*a.elbowAmp):0)):v&&!i?(C=v.sw,I=v.bd):i?(C=X,I=t0):(C=0,I=0);let e0=R?g:p,u0=R?b:f,b0=R?1:-1,f0=v&&!R?(A===2?-1:1)*(v.handRoll||0):0,M0=v&&!R&&v.handPitch||0,p0=v&&!R?(A===2?-1:1)*(v.handYaw||0):0,P0=v&&!R?(A===2?-1:1)*(v.spread||0):0,_0=v&&!R?(A===2?-1:1)*(v.roll||0):0,T0=A===2?-y:y;for(let x0=0;x0<l.length;x0++)if(l[x0]===A){let n0=h[x0*3],Z=h[x0*3+1],a0=h[x0*3+2],F0=Z;!R&&(P||F0<e)&&(f0&&([n0,a0]=k1(n0,a0,T0,0,f0)),M0&&([Z,a0]=e1(Z,a0,e,M0)),p0&&([n0,Z]=ar(n0,Z,T0,e,p0))),(P||F0<e0)&&([Z,a0]=e1(Z,a0,e0,b0*I)),_0&&(P||F0<e0)&&([n0,a0]=k1(n0,a0,T0,0,_0)),[Z,a0]=e1(Z,a0,u0,C),!R&&P0&&([n0,Z]=ar(n0,Z,n[A],f,P0)),!R&&x&&([n0,a0]=k1(n0,a0,0,0,x)),R||([Z,a0]=e1(Z,a0,c,z)),d[x0*3]=n0+w,d[x0*3+1]=Z+s+M,d[x0*3+2]=a0+L}};S(4,0,!0,\"legL\"),S(5,Math.PI,!0,\"legR\"),S(2,Math.PI,!1,\"armL\"),S(3,0,!1,\"armR\");for(let A=0;A<l.length;A++)if(l[A]===0||l[A]===1){let _=h[A*3],R=h[A*3+1],T=h[A*3+2];l[A]===1&&k&&([R,T]=e1(R,T,u,k)),x&&([_,T]=k1(_,T,0,0,x)),[R,T]=e1(R,T,c,z),d[A*3]=_+w,d[A*3+1]=R+s+M,d[A*3+2]=T+L}r.dirty=!0}var Lo={body:0,head:1,armL:2,armR:3,legL:4,legR:5},cr=[[40,40,40],[80,80,80],[120,120,120],[160,160,160],[200,200,200],[240,240,240]],ir=1.6,Ho=1.3,lr=1.7*O,K0=new Map,g1=new Map;function Po(t,r){let o=JSON.stringify([t,r]),a=g1.get(o);if(!a){let s={skin:cr.slice(),boot:cr.slice()};a=q1(s,{build:t,head:r});for(let m of a)m.foot=m.r===s.boot;g1.set(o,a),g1.size>12&&g1.delete(g1.keys().next().value)}return a}function ur(t){let r=JSON.stringify(t),o=K0.get(r);if(o)return K0.delete(r),K0.set(r,o),o;let a=[],s=[],m=[],i=[];for(let f of t.pieces){let y=J0(f,{build:t.build});for(let u of y.zones)(f.layer===\"armor\"?s:a).push(u);m.push(...y.faces),i.push(...y.marks)}let n=Q1(Po(t.build,t.head).map(f=>({...f,p:f.p.slice(),n:f.n.slice()})),a,s);for(let f of n)f._mat||(f._mat=f.foot?\"feet\":\"skin\"),f._mat===\"skin\"&&f.g===\"body\"&&f.n[2]>.62&&(f._mat=\"belly\");let e=[...n,...m,...i];for(let f of e)if(f.g===\"head\")for(let y=0;y<4;y++)f.p[y*3]*=ir,f.p[y*3+1]=lr+(f.p[y*3+1]-lr)*ir,f.p[y*3+2]*=Ho;let p=new Float32Array(e.length*12),g=new Uint8Array(e.length*4);e.forEach((f,y)=>{p.set(f.p,y*12),g.fill(Lo[f.g]??0,y*4,y*4+4)});let b=nr({basePos:p,vgrp:g,armX:.235,wristY:.9875*O,neckY:m1*O});return o={faces:e,base:p,vgrp:g,anim:b},K0.set(r,o),K0.size>24&&K0.delete(K0.keys().next().value),o}function fr(t,r={},o=0){let a={},s=!1,m=sr,i=0;r.walk!=null&&r.walk!==0&&(s=!0,m=C1,i=Math.asin(Math.max(-1,Math.min(1,r.walk)))),r.gait!=null&&(s=!0,m=C1,i=r.gait),r.greet!=null&&(a.armR={sw:-.2,bd:.9,spread:2.45+r.greet*.25,roll:0,handRoll:0,handPitch:0,handYaw:0}),r.weapon&&(a.armR={sw:-1.15,bd:.45,spread:.25,roll:0,handRoll:0,handPitch:0,handYaw:0}),r.petting!=null&&(a.armL={sw:-.25,bd:.25,spread:.5+r.petting*.1,roll:0,handRoll:0,handPitch:0,handYaw:0}),r.meditationStanding&&(a.armL={sw:-.35,bd:.6,spread:.35},a.armR={sw:-.35,bd:.6,spread:.35}),r.meditate&&(a.legL={sw:-1.45,bd:2.35},a.legR={sw:-1.45,bd:2.35},a.rootY=-.36,a.armL={sw:-.75,bd:.35,spread:.3},a.armR={sw:-.75,bd:.35,spread:.3});for(let g of[\"armL\",\"armR\"])a[g]||(a[g]={sw:0,bd:.08,spread:.1});let n=!0,e={pos:new Float32Array(t.base.length),base:t.base,vgrp:t.vgrp},p=n?{lean:0,gaitArm:.3,gaitElbow:.3,...a}:null;return mr(t.anim,e,i,m,s?m.bobAmp*Math.abs(Math.sin(i)):0,p,s),e.pos}var B0=\"#21172e\",y0=\"#fbebce\",U=\"#e6c880\";function N1(t){let r=(i,n,e,p,g)=>{t.fillStyle=g,t.fillRect(Math.round(i),Math.round(n),Math.round(e),Math.round(p))},o=(i,n,e=B0)=>{t.beginPath(),i.forEach(([p,g],b)=>b?t.lineTo(p+.5,g+.5):t.moveTo(p+.5,g+.5)),t.closePath(),t.fillStyle=n,t.fill(),e&&(t.strokeStyle=e,t.lineWidth=1,t.stroke())},a=(i,n,e=U)=>r(i,n,1,1,e);return{rect:r,poly:o,dot:a,gem:(i,n,e=U)=>{o([[i,n-2],[i+2,n],[i,n+2],[i-2,n]],e),a(i,n-1,y0)},box:(i,n,e,p,g)=>{r(i,n,e,p,B0),r(i+1,n+1,e-2,p-2,g)}}}var pr=t=>t===1?U:t===2?y0:\"#88e4d9\";function dr(t,r,o,{H:a}){let{rect:s,poly:m,gem:i,box:n}=N1(t),e=!0;if(e){let p=r.base;m([[13-p%3,87],[22,84],[43,84],[52+p%3,88],[45,93],[20,93]],p===6?\"#913f58\":p===4?\"#383341\":p===5?\"#aea6c9\":\"#756580\");for(let g=18;g<48;g+=3)s(g,89,2,2,p%2?U:\"#bca7bd\"),(p===3||p===9)&&i(g,84-g%3,p===3?\"#81b791\":a);if(p===1&&s(18,85,29,2,U),p===2&&n(20,87,5,3,\"#a4a0bd\"),p===7)for(let g=18;g<49;g+=4)s(g,86,1,6,B0);if(p===8)for(let g=16;g<49;g+=8)m([[g,88],[g+2,79],[g+5,88]],\"#8db9d0\")}if(o){let p=pr(o);if(e){if(o===1&&s(18,92,29,2,p),o===2)for(let g=15;g<52;g+=6)i(g,91,p);o===3&&(s(12,94,41,1,p),s(10,91,2,3,p),s(53,91,2,3,p))}}}function gr(t,r,o,{S:a,H:s,B:m},i=0){let{rect:n,poly:e,dot:p,gem:g,box:b}=N1(t);if(!0)switch(r.held){case 0:b(48+i,47,5,8,\"#c4bdcf\"),n(50+i,55,1,8,U),n(48+i,63,5,1,U),n(49+i,49,3,3,s);break;case 1:e([[48,59],[43,46],[49,40],[58,45],[60,53]],s);for(let y=0;y<4;y++)e([[48,59],[45+y*4,44+y%2]],U);break;case 2:b(46,56,13,9,s),n(47,59,11,1,U),g(53,59);break;case 3:e([[49,63],[45,47],[56,47],[51,63]],\"#548267\");for(let[y,u]of[[45,45],[51,42],[57,46],[49,49]])g(y,u,s);break;case 4:n(51,47,2,36,U),b(47,45,7,3,U);break;case 5:n(51,47,1,6,U),b(47,53,10,13,m),n(49,56,6,7,U),g(52,59,y0);break;case 6:b(47,53,12,9,y0),n(49,55,7,1,m),g(53,59,s);break;case 7:b(48,47,9,9,s),g(52,51,y0),n(50,56,5,2,U);break;case 8:n(52,29,1,35,U),e([[40,33],[43,25],[52,22],[62,29],[63,33]],s),n(42,32,20,1,U);break;case 9:e([[48,48],[48,43],[51,46],[56,44],[59,48],[57,55],[50,54]],a),p(51,49,B0),p(56,49,B0),n(51,55,1,4,U),n(56,55,1,4,U)}if(o){let y=pr(o);if(o===1&&(n(48,58,12,1,y),g(59,58,y)),o===2&&e([[51,61],[57,71],[55,61]],s),o===3)for(let[u,d]of[[45,45],[59,48],[58,62]])g(u,d,y)}}function hr(t,r){if({meditationStanding:!0}.meditationStanding&&!!1){let s=document.createElement(\"canvas\");s.width=t.width,s.height=96,s.getContext(\"2d\").drawImage(t,0,0),r.clearRect(0,56,t.width,40),r.drawImage(s,0,56,t.width,40,-10,56,t.width+20,40)}}function yr(t,r,{S:o,H:a,B:s}){let{rect:m,poly:i,dot:n,gem:e,box:p}=N1(t),g=r.parts,b=!0,f={pet:g.pet%10},y=()=>Math.floor((g.pet||0)/10);if(b&&g.pet){let u=f.pet,d=y(\"pet\"),h=[a,o,U,\"#90bca2\",\"#d99bae\",\"#ad9ac8\",\"#81a891\",\"#b1accb\",\"#9caabb\",\"#83b587\"][u],l=9,c=u===2?67:79;if(u===0&&(i([[l-6,c+5],[l-3,c],[l+3,c-1],[l+8,c+5]],h),m(l+6,c-3,1,4,h),n(l+6,c-3,y0)),[1,4,6].includes(u)&&(p(l-5,c-3,11,8,h),p(l+3,c-7,7,7,h),m(l-4,c+5,2,3,h),m(l+3,c+5,2,3,h),i([[l+3,c-7],[l+3,c-11],[l+6,c-7]],h),i([[l+7,c-7],[l+10,c-11],[l+10,c-6]],h),n(l+7,c-4,B0),u===6?i([[l-5,c],[l-11,c-4],[l-8,c+3]],h):m(l-7,c-4,2,7,h),u===4&&m(l+8,c-2,5,1,y0)),u===2&&(i([[l,c],[l-9,c-8],[l-9,c+6],[l,c+2]],h),i([[l+2,c],[l+10,c-8],[l+10,c+6],[l+2,c+2]],h),p(l,c-3,3,10,s),e(l-5,c-1,U),e(l+7,c-1,U)),u===3){p(l-5,c-6,12,11,h),m(l,c-5,1,9,s);for(let z=0;z<3;z++)m(l-8,c-4+z*4,3,1,U),m(l+7,c-4+z*4,3,1,U);p(l-2,c-9,5,4,s),n(l-1,c-8,y0),n(l+1,c-8,y0)}if(u===5){i([[l-6,c],[l-6,c-6],[l-2,c-10],[l+4,c-10],[l+8,c-5],[l+8,c]],h);for(let z=0;z<4;z++)m(l-4+z*3,c,1,7-z%2*3,h);n(l-1,c-5,B0),n(l+4,c-5,B0)}if(u===7&&(i([[l-7,c-2],[l-9,c-5],[l-5,c-6],[l-3,c-10],[l,c-7],[l+5,c-10],[l+6,c-6],[l+9,c-3],[l+6,c+4],[l-5,c+4]],h),p(l-3,c-4,3,3,y0),p(l+2,c-4,3,3,y0)),u===8&&(p(l-6,c-10,13,12,h),p(l-3,c-7,7,3,s),n(l-2,c-6,y0),n(l+2,c-6,y0),p(l-5,c+2,3,4,s),p(l+3,c+2,3,4,s),m(l,c-14,1,4,U),e(l,c-14,a)),u===9&&(p(l-4,c-5,10,10,\"#956e55\"),m(l,c-15,2,11,h),i([[l,c-10],[l-7,c-15],[l-6,c-8]],h),i([[l+2,c-12],[l+8,c-17],[l+8,c-10]],h),n(l-1,c-2,y0),n(l+3,c-2,y0)),m(l-3,c,8,1,U),e(l+1,c+1,U),d===1&&i([[l-5,c-8],[l-5,c-13],[l,c-10],[l+4,c-14],[l+6,c-8]],U),d===2&&(i([[l-6,c],[l-12,c-6],[l-11,c+4]],a),i([[l+6,c],[l+12,c-6],[l+11,c+4]],a)),d===3){for(let[z,x]of[[l-9,c-10],[l+10,c-8],[l,c-17]])e(z,x,\"#88e4d9\");m(l-5,c-7,10,1,\"#88e4d9\")}}}var Y1=new Map,i0=(t,r)=>{let o=t+JSON.stringify(r||0);return Y1.has(o)||Y1.set(o,k0(t,r)),Y1.get(o)},Ro=(t,r)=>[t,r,\"#e6c880\",\"#90bca2\",\"#d99bae\",\"#ad9ac8\",\"#81a891\",\"#b1accb\",\"#9caabb\",\"#83b587\"];function So(t,r){let o=t.top??.4,a=t.head?.[0]??0,s=t.head?.[1]??o-.07,m=t.head?.[2]??0;if(r===1){let i=s+.07;return[{box:[a-.06,i-.01,m-.03,a+.06,i+.025,m+.03],mat:\"gold\"},...[-.045,0,.045].map((n,e)=>({box:[a+n-.014,i+.02,m-.014,a+n+.014,i+(e===1?.085:.065),m+.014],mat:\"gold\"})),{mark:\"gem\",at:[a,(i+.01)/.9,m+.034],mat:\"glow\",free:!0}]}return r===2?[{fan:[[.04,o*.7,-.03],[.22,o+.12,-.08],[.28,o*.7,-.1],[.2,o*.4,-.07]],mirror:!0,mat:\"pearl\"}]:r===3?[-.14,0,.14].map((i,n)=>{let e=o+.08+n%2*.06;return{loft:[[e-.04,0,0],[e-.015,.03,.03],[e+.015,.03,.03],[e+.04,0,0]],cx:a+i,mat:\"glow\",seg:6}}):[]}var a1=new Map;function br(t,{H:r,S:o,yaw:a=1,ox:s=11,oy:m=84,scale:i=36,w:n=64,h:e=96}={}){let p=t.parts.pet||0,g=p%10,b=Math.floor(p/10),f=(t1.pets||[])[g];if(!p||!f)return null;let y=JSON.stringify([p,r,o,a,s,m,i,n,e]);if(a1.has(y))return a1.get(y);let u=Ro(r,o)[g],d={gold:i0(\"#e6c880\"),pearl:i0(\"#f2e6dc\",{lift:.04}),glow:i0(\"#88e4d9\",{lift:.05,spread:.8}),dark:i0(\"#3a2f4c\"),white:i0(\"#fbebce\",{lift:.08,spread:.5}),ivory:i0(\"#e4dcc0\"),metal:i0(\"#9aa6b8\"),glass:i0(\"#9fe8ff\",{lift:.06}),membrane:i0(\"#8a6a90\"),fur:i0(u),furDark:i0(u,{lift:-.14}),belly:i0(u,{lift:.12,spread:.8}),skin:i0(u),dye:i0(r),trim:i0(r,{lift:-.1}),accent:i0(\"#e6c880\")},h=[];for(let c of[...f.pieces,...So(f,b)])for(let z of J0({group:\"body\",...c}).faces)z.r=d[z._mat]||d.fur,h.push(z);let l=$0(h,{w:n,h:e,scale:i,ox:s,oy:m,yaw:a});return a1.set(y,l),a1.size>48&&a1.delete(a1.keys().next().value),l}var w1=-.35,Eo=36,Io=32,_o=84,Mr={back:2.55,tail:2.1},xr=[\"headwear\",\"neck\",\"torso\",\"shoulders\",\"arms\",\"hands\",\"legs\",\"feet\",\"back\",\"armor\",\"shield\"],Lr=Object.fromEntries([...[\"body\",\"build\",\"head\",\"crown\",\"jaw\",\"tail\",\"skin\"].map(t=>[t,[...xr,\"hair\"]]),...[\"face\",\"facial\",\"hair\"].map(t=>[t,xr])]),To=new Set([\"held\",\"base\",\"pet\",\"skin\",\"build\",\"alloy\"]),vo=[\"trim\",\"gold\",\"pearl\",\"glow\"],Do={body:[.075,1.8,.1,1],face:[0,1.715,.085],hair:[.06,2,.12],facial:[0,1.715,.075],headwear:[0,1.93,.165],neck:[0,1.6,.105],torso:[-.09,1.48,.115],shoulders:[.25,1.66,.06,1],arms:[.235,1.32,.07,1],hands:[.235,.9,.055,1],legs:[.082,.52,.11,1],feet:[.082,.07,.2,1],back:[.12,1.5,-.12,1,-1],crown:[0,2.02,.07],jaw:[0,1.72,.085],tail:[.1,1,-.1,1,-1],armor:[0,1.38,.135],shield:[-.3,1.05,.1]},Oo={face:\"head\",hair:\"head\",facial:\"head\",headwear:\"head\",crown:\"head\",jaw:\"head\",head:\"head\",arms:\"arm\",hands:\"arm\",shield:\"arm\",feet:\"leg\"};function W1(t,{bare:r=null}={}){let o=t.parts,a=n=>(o[n]||0)%10,s=n=>Math.floor((o[n]||0)/10),m=O1[a(\"body\")],i=[];for(let n of G0){if(To.has(n.id)||r?.has(n.id))continue;let e=d1[n.id]?.[a(n.id)];if(!e)continue;let p=vo[s(n.id)],g=Oo[n.id]||\"body\",b=s(n.id),f=Do[n.id];b&&f&&i.push({mark:[\"\",\"stud\",\"pip\",\"spark\"][b],at:f.slice(0,3),mirror:!!f[3],group:g,mat:p,n:[0,0,f[4]??1],slack:.6});for(let y of e)i.push({...y,group:y.group||g,...y.mat===\"accent\"?{mat:p}:{}})}return{build:a(\"build\")?D0[a(\"build\")]:m.build,head:a(\"head\")&&Y0[a(\"head\")]||m.head,pieces:i}}var B1=new Map,W0=(t,r)=>{let o=t+JSON.stringify(r||0);return B1.has(o)||B1.set(o,k0(t,r)),B1.get(o)};function Co(t){let r=p1(t),o=x1[(t.parts.alloy||0)%10][1],a=o1[t.trim??t.dye];return{..._t,skin:W0(r),skinDark:W0(r,{lift:-.1}),belly:W0(r,{lift:.1,spread:.8}),feet:W0(r,{lift:-.12}),dye:W0(o1[t.dye]),trim:W0(a),hair:W0(a,{lift:-.04}),metal:W0(o)}}var l0=[251,235,206],V=[33,23,46],zr=[223,139,155],kr={eye:(t,r)=>r?[[0,1,V],[1,1,V]]:[[0,0,l0],[1,0,l0],[0,1,t],[1,1,V]],compound:(t,r)=>r?[[-1,0,V],[0,0,V],[1,0,V]]:[[-1,-1,t],[0,-1,t],[1,-1,t],[-1,0,t],[0,0,l0],[1,0,t],[-1,1,V],[0,1,t],[1,1,V]],orb:(t,r)=>r?[[-1,0,V],[0,0,V],[1,0,V]]:[[-1,-1,l0],[0,-1,l0],[1,-1,l0],[-1,0,l0],[0,0,t],[1,0,l0],[-1,1,l0],[0,1,V],[1,1,l0]],slit:(t,r)=>r?[[0,1,V],[1,1,V]]:[[0,0,t],[1,0,V],[0,1,V],[1,1,t]],dot:(t,r)=>r?[[0,0,V]]:[[0,0,t]],visor:t=>[[-2,0,t],[-1,0,t],[0,0,l0],[1,0,t],[2,0,t],[-2,1,V],[-1,1,V],[0,1,V],[1,1,V],[2,1,V]],mouth:()=>[[-1,0,V],[0,0,V],[1,0,V]],grin:()=>[[-2,0,V],[-1,0,l0],[0,0,l0],[1,0,l0],[2,0,V]],blush:()=>[[0,0,zr],[1,0,zr]],stud:t=>[[0,0,t]],pip:t=>[[0,0,t],[1,0,t],[0,1,t]],spark:t=>[[0,0,l0],[1,0,t],[0,1,t]],gem:t=>[[0,-1,t],[-1,0,t],[0,0,l0],[1,0,t],[0,1,t]]},U0=new Map,F1=(t,r)=>{let o=globalThis.document?document.createElement(\"canvas\"):new OffscreenCanvas(t,r);return o.width=t,o.height=r,o},wr=(t,r)=>Math.round(t/r)*r;function M1(t,{pose:r={},yaw:o=w1,blink:a=!1,w:s=64,h:m=96,scale:i=Eo,ox:n=Io,oy:e=_o,weapon:p=null,bare:g=null}={}){let b=W1(t,{bare:g?new Set(g):null}),f=Co(t),y={};for(let[I,X]of Object.entries(r||{}))y[I]=typeof X==\"number\"?wr(X,.125):X;let u=JSON.stringify([b,t.dye,t.trim,t.parts.skin,t.parts.alloy,y,wr(o,Math.PI/32),a,s,m,i,n,e,p]),d=U0.get(u);if(d)return U0.delete(u),U0.set(u,d),d;let h=ur(b),l=fr(h,y),c=[];for(let I of h.faces)I.mark||(I.r=f[I._mat]||f.dye);h.faces.forEach((I,X)=>{if(!I.mark)return;let t0=l?l.subarray(X*12,X*12+3):I.p,e0=(f[I._mat]||f.dark)[4];c.push({at:[t0[0],t0[1],t0[2]],n:I.n,free:I.free,slack:I.slack,px:(kr[I.mark]||kr.dot)(e0,a)})});let z=h.faces.filter(I=>!I.mark),x=l?new Float32Array(z.length*12):null;if(l){let I=0;h.faces.forEach((X,t0)=>{X.mark||x.set(l.subarray(t0*12,t0*12+12),12*I++)})}let k=z,w=x,M=p&&tr(h,l,p);if(M){let I=_1(p.tier);for(let X of M.faces)X.r=I[X._mat]||I.shell;w=new Float32Array((z.length+M.faces.length)*12),x?w.set(x):z.forEach((X,t0)=>w.set(X.p,t0*12)),w.set(M.pos,z.length*12),k=[...z,...M.faces]}let L=$0(k,{w:s,h:m,scale:i,ox:n,oy:e,yaw:o,pos:w,marks:c}),P=F1(s,m),S=P.getContext(\"2d\"),A=S.createImageData(s,m);A.data.set(L.data),S.putImageData(A,0,0);let _=u1(o,.1),R=.9875*O,T=0,v=0,C=0;return h.faces.forEach((I,X)=>{if(I.g!==\"armR\"||I.mark)return;let t0=l?l.subarray(X*12,X*12+12):I.p;if(!(h.base[X*12+1]>=R))for(let e0=0;e0<4;e0++){let[u0,b0]=_(t0[e0*3],t0[e0*3+1],t0[e0*3+2]);T+=u0,v+=b0,C++}}),d={canvas:P,hand:C?[n+T/C*i,e-v/C*i]:[44,59]},U0.set(u,d),U0.size>160&&U0.delete(U0.keys().next().value),d}function Hr(t,r,{base:o=!0,weapon:a=!0,time:s=0,companion:m=!0,petOnly:i=!1,prop:n=!0,blink:e=!1,pose:p={},yaw:g=w1,bare:b=null}={}){let f=globalThis.window?.GalaWeapons,y=!i&&a&&r.weapon&&f?.unlocked(r.weapon)?r.weapon:null;t.width=y?96:64,t.height=96;let u=t.getContext(\"2d\");u.imageSmoothingEnabled=!1;let d=r.parts,h=Object.fromEntries(Object.entries(d).map(([w,M])=>[w,M%10])),l=w=>Math.floor((d[w]||0)/10),c=p1(r),z=o1[r.dye],x=z1(z,42),k=z1(z,-40);if(!i){o&&dr(u,h,l(\"base\"),{H:x});let w=M1(r,{pose:p,yaw:g,blink:e,weapon:y,w:t.width,bare:b});u.drawImage(w.canvas,0,0),n&&(u.save(),u.translate(Math.round(w.hand[0]-44),Math.round(w.hand[1]-59)),gr(u,h,l(\"held\"),{S:c,H:x,B:k}),u.restore()),p.meditationStanding&&hr(t,u)}if(m&&d.pet){let w=br(r,{H:x,S:c});if(w){let M=F1(w.w,w.h),L=M.getContext(\"2d\"),P=L.createImageData(w.w,w.h);P.data.set(w.data),L.putImageData(P,0,0),u.drawImage(M,0,0)}else yr(u,r,{S:c,H:x,B:k})}}var I0={facings:[0,Math.PI/4,Math.PI/2,Math.PI*3/4,Math.PI],frames:[\"idle0\",\"idle1\",\"walk0\",\"walk1\",\"walk2\",\"walk3\",\"walk4\",\"walk5\"],w:64,h:96};function Pr(t){let r=F1(I0.w*I0.frames.length,I0.h*I0.facings.length),o=r.getContext(\"2d\");return I0.facings.forEach((a,s)=>I0.frames.forEach((m,i)=>{let n=Number(m.slice(-1)),e=m.startsWith(\"walk\")?{gait:n/6*Math.PI*2}:{breath:n};o.drawImage(M1(t,{yaw:a,pose:m.startsWith(\"walk\")?e:{}}).canvas,i*I0.w,s*I0.h)})),r}window.GalaAvatar={sections:G0,dyes:o1,defaultLook:v1,normalize:D1,skinColor:p1,draw:Hr,figure:M1,sheet:Pr,recipe:W1,SHEET:I0,DEFAULT_YAW:w1,PREVIEW_YAW:Mr,PREVIEW_BARE:Lr,renderer:\"dfo-rig-1\",weaponImage:rr};})();\n","pod/gala-coaches/manifest.json":"{\"version\":1,\"ramp\":[78,128,184,238],\"regions\":{\"outline\":0,\"body\":64,\"head\":128,\"eyes\":192},\"sheets\":{\"bodies\":{\"image\":\"bodies.png\",\"mask\":\"bodies-mask.png\",\"frame\":[64,96]},\"pets\":{\"image\":\"pets.png\",\"mask\":\"pets-mask.png\",\"frame\":[48,36]}},\"sprites\":[{\"id\":\"myr5\",\"family\":\"MYR5\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,0,64,96],\"unlock\":\"myr5\"},{\"id\":\"roster/01-seed-pearo--3d_character_model\",\"family\":\"Seed \\u00b7 Pearo\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,0,64,96],\"unlock\":\"roster/01-seed-pearo--3d_character_model\"},{\"id\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\",\"family\":\"Seed \\u00b7 Pearo\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,0,64,96],\"unlock\":\"roster/01-seed-pearo--cute_alien_figure_3d_model\"},{\"id\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\",\"family\":\"Seed \\u00b7 Pearo\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,0,64,96],\"unlock\":\"roster/01-seed-pearo--blank_toy_figure_3d_model\"},{\"id\":\"roster/02-taper-tallstalk--3d_humanoid_figure\",\"family\":\"Taper \\u00b7 Tallstalk\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,0,64,96],\"unlock\":\"roster/02-taper-tallstalk--3d_humanoid_figure\"},{\"id\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"family\":\"Taper \\u00b7 Tallstalk\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,0,64,96],\"unlock\":\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\"},{\"id\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",\"family\":\"Taper \\u00b7 Tallstalk\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,0,64,96],\"unlock\":\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\"},{\"id\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,0,64,96],\"unlock\":\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\"},{\"id\":\"roster/03-pearl-orb-ring--circle_center\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,96,64,96],\"unlock\":\"roster/03-pearl-orb-ring--circle_center\"},{\"id\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,96,64,96],\"unlock\":\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\"},{\"id\":\"roster/03-pearl-orb-ring--robot_3d_model\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,96,64,96],\"unlock\":\"roster/03-pearl-orb-ring--robot_3d_model\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,96,64,96],\"unlock\":\"roster/03-pearl-orb-ring--stylized_3d_character1\"},{\"id\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"family\":\"Pearl \\u00b7 Orb\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,96,64,96],\"unlock\":\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\"},{\"id\":\"roster/04-crest-wedge--robot_creature_3d_model\",\"family\":\"Crest \\u00b7 Wedge\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,96,64,96],\"unlock\":\"roster/04-crest-wedge--robot_creature_3d_model\"},{\"id\":\"roster/04-crest-wedge--stylized_3d_character\",\"family\":\"Crest \\u00b7 Wedge\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,96,64,96],\"unlock\":\"roster/04-crest-wedge--stylized_3d_character\"},{\"id\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"family\":\"Slope \\u00b7 Bobble\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,96,64,96],\"unlock\":\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\"},{\"id\":\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"family\":\"Ridge \\u00b7 Triad\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,192,64,96],\"unlock\":\"roster/06-ridge-triad--geometric_robot_3d_model1\"},{\"id\":\"roster/06-ridge-triad--robot_3d_model1\",\"family\":\"Ridge \\u00b7 Triad\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,192,64,96],\"unlock\":\"roster/06-ridge-triad--robot_3d_model1\"},{\"id\":\"roster/07-bulb-sphereling--cute_robot_3d_model\",\"family\":\"Bulb \\u00b7 Sphereling\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,192,64,96],\"unlock\":\"roster/07-bulb-sphereling--cute_robot_3d_model\"},{\"id\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"family\":\"Bulb \\u00b7 Sphereling\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,192,64,96],\"unlock\":\"roster/07-bulb-sphereling--humanoid_robot_3d_model\"},{\"id\":\"roster/08-shard-asym--geometric_robot_3d_model\",\"family\":\"Shard \\u00b7 Asym\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,192,64,96],\"unlock\":\"roster/08-shard-asym--geometric_robot_3d_model\"},{\"id\":\"roster/08-shard-asym--robot_3d_model4\",\"family\":\"Shard \\u00b7 Asym\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,192,64,96],\"unlock\":\"roster/08-shard-asym--robot_3d_model4\"},{\"id\":\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"family\":\"Shard \\u00b7 Asym\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,192,64,96],\"unlock\":\"roster/08-shard-asym--fantasy_creature_3d_model2\"},{\"id\":\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"family\":\"Shard \\u00b7 Asym\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,192,64,96],\"unlock\":\"roster/08-shard-asym--stylized_action_figure_3d_model\"},{\"id\":\"roster/09-monolith-tanka--3d_robot_model\",\"family\":\"Monolith \\u00b7 Tanka\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,288,64,96],\"unlock\":\"roster/09-monolith-tanka--3d_robot_model\"},{\"id\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"family\":\"Monolith \\u00b7 Tanka\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,288,64,96],\"unlock\":\"roster/09-monolith-tanka--boxy_humanoid_3d_model\"},{\"id\":\"roster/09-monolith-tanka--mini_robot_3d_model\",\"family\":\"Monolith \\u00b7 Tanka\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,288,64,96],\"unlock\":\"roster/09-monolith-tanka--mini_robot_3d_model\"},{\"id\":\"roster/09-monolith-tanka--robot_3d_model3\",\"family\":\"Monolith \\u00b7 Tanka\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,288,64,96],\"unlock\":\"roster/09-monolith-tanka--robot_3d_model3\"},{\"id\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\",\"family\":\"Monolith \\u00b7 Tanka\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,288,64,96],\"unlock\":\"roster/09-monolith-tanka--white_horned_robot_3d_model\"},{\"id\":\"roster/10-petal-wisp--stylized_creature_3d_model\",\"family\":\"Petal \\u00b7 Wisp\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,288,64,96],\"unlock\":\"roster/10-petal-wisp--stylized_creature_3d_model\"},{\"id\":\"roster/10-petal-wisp--ghost_character_3d_model\",\"family\":\"Petal \\u00b7 Wisp\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,288,64,96],\"unlock\":\"roster/10-petal-wisp--ghost_character_3d_model\"},{\"id\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"family\":\"Petal \\u00b7 Wisp\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,288,64,96],\"unlock\":\"roster/10-petal-wisp--fantasy_creature_3d_model4\"},{\"id\":\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"family\":\"Anvil \\u00b7 Cask\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,384,64,96],\"unlock\":\"roster/11-anvil-cask--clay-style_robot_3d_model\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"family\":\"Seedpod \\u00b7 Snailslug\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,384,64,96],\"unlock\":\"roster/12-seedpod-snailslug--stylized_slug_3d_model\"},{\"id\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"family\":\"Seedpod \\u00b7 Snailslug\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,384,64,96],\"unlock\":\"roster/12-seedpod-snailslug--stylized_worm_3d_model\"},{\"id\":\"roster/13-fan-split--stylized_3d_character2\",\"family\":\"Fan \\u00b7 Split\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,384,64,96],\"unlock\":\"roster/13-fan-split--stylized_3d_character2\"},{\"id\":\"roster/13-fan-split--stylized_toy_3d_model\",\"family\":\"Fan \\u00b7 Split\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,384,64,96],\"unlock\":\"roster/13-fan-split--stylized_toy_3d_model\"},{\"id\":\"roster/14-chisel-spire--cone_head_3d_model\",\"family\":\"Chisel \\u00b7 Spire\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,384,64,96],\"unlock\":\"roster/14-chisel-spire--cone_head_3d_model\"},{\"id\":\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"family\":\"Chisel \\u00b7 Spire\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,384,64,96],\"unlock\":\"roster/14-chisel-spire--low_poly_robot_3d_model\"},{\"id\":\"roster/15-orbital-coili--robot_character_3d_model\",\"family\":\"Orbital \\u00b7 Coili\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,384,64,96],\"unlock\":\"roster/15-orbital-coili--robot_character_3d_model\"},{\"id\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"family\":\"Spade \\u00b7 Arch\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,480,64,96],\"unlock\":\"roster/16-spade-arch--pyramid_head_figure_3d_model\"},{\"id\":\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"family\":\"Spade \\u00b7 Arch\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,480,64,96],\"unlock\":\"roster/16-spade-arch--stylized_humanoid_3d_model\"},{\"id\":\"roster/17-shellcap-manyarm--3d_character_figurine\",\"family\":\"Shellcap \\u00b7 Manyarm\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,480,64,96],\"unlock\":\"roster/17-shellcap-manyarm--3d_character_figurine\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"family\":\"Shellcap \\u00b7 Manyarm\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,480,64,96],\"unlock\":\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\"},{\"id\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"family\":\"Shellcap \\u00b7 Manyarm\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,480,64,96],\"unlock\":\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\"},{\"id\":\"roster/19-genie-multi--fantasy_creature_3d_model\",\"family\":\"Genie \\u00b7 Multi\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,480,64,96],\"unlock\":\"roster/19-genie-multi--fantasy_creature_3d_model\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"family\":\"Genie \\u00b7 Multi\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,480,64,96],\"unlock\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\"},{\"id\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"family\":\"Genie \\u00b7 Multi\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,480,64,96],\"unlock\":\"roster/19-genie-multi--multi-armed_humanoid_3d_model\"},{\"id\":\"roster/19-genie-multi--stylized_octopus_3d_model\",\"family\":\"Genie \\u00b7 Multi\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,576,64,96],\"unlock\":\"roster/19-genie-multi--stylized_octopus_3d_model\"},{\"id\":\"roster/20-lume--robotic_figure_3d_model\",\"family\":\"Lume\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,576,64,96],\"unlock\":\"roster/20-lume--robotic_figure_3d_model\"},{\"id\":\"roster/21-flyer--winged_humanoid_3d_model\",\"family\":\"Flyer\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[128,576,64,96],\"unlock\":\"roster/21-flyer--winged_humanoid_3d_model\"},{\"id\":\"roster/22-curve--stylized_alien_3d_model\",\"family\":\"Curve\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[192,576,64,96],\"unlock\":\"roster/22-curve--stylized_alien_3d_model\"},{\"id\":\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"family\":\"Curve\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[256,576,64,96],\"unlock\":\"roster/22-curve--stylized_cartoon_figure_3d_model\"},{\"id\":\"roster/23-blob-texture-bodies--blob_creature_3d_model\",\"family\":\"Blob\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[320,576,64,96],\"unlock\":\"roster/23-blob-texture-bodies--blob_creature_3d_model\"},{\"id\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",\"family\":\"Blob\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[384,576,64,96],\"unlock\":\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\"},{\"id\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"family\":\"Blob\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[448,576,64,96],\"unlock\":\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\"},{\"id\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"family\":\"Blob\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[0,672,64,96],\"unlock\":\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\"},{\"id\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"family\":\"Blob\",\"kind\":\"body\",\"sheet\":\"bodies\",\"frame\":[64,672,64,96],\"unlock\":\"roster/23-blob-texture-bodies--sand_creature_3d_model\"},{\"id\":\"roster/18-quad-all--dragon_creature_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[0,0,48,36],\"unlock\":\"roster/18-quad-all--dragon_creature_3d_model\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model1\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[48,0,48,36],\"unlock\":\"roster/18-quad-all--fantasy_creature_3d_model1\"},{\"id\":\"roster/18-quad-all--fantasy_creature_3d_model3\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[96,0,48,36],\"unlock\":\"roster/18-quad-all--fantasy_creature_3d_model3\"},{\"id\":\"roster/18-quad-all--four-legged_robot_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[144,0,48,36],\"unlock\":\"roster/18-quad-all--four-legged_robot_3d_model\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model1\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[192,0,48,36],\"unlock\":\"roster/18-quad-all--quadruped_robot_3d_model1\"},{\"id\":\"roster/18-quad-all--quadruped_robot_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[0,36,48,36],\"unlock\":\"roster/18-quad-all--quadruped_robot_3d_model\"},{\"id\":\"roster/18-quad-all--robotic_dog_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[48,36,48,36],\"unlock\":\"roster/18-quad-all--robotic_dog_3d_model\"},{\"id\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[96,36,48,36],\"unlock\":\"roster/18-quad-all--wooden_four-legged_robot_3d_model\"},{\"id\":\"roster/18-quad-all--wooden_robot_3d_model\",\"family\":\"Four-legged\",\"kind\":\"pet\",\"sheet\":\"pets\",\"frame\":[144,36,48,36],\"unlock\":\"roster/18-quad-all--wooden_robot_3d_model\"}]}","pod/gala-performer.js":"/* Cosmetic scenes for the player's saved Gala character. */\r\n(()=>{\r\n 'use strict';\r\n const A=window.GalaAvatar,W=window.GalaWeapons,TAU=Math.PI*2;\r\n const ease=value=>{const t=Math.max(0,Math.min(1,value));return t*t*(3-2*t);};\r\n function playlist(look){\r\n  const scenes=[{name:'idle',duration:6000}];\r\n  if(look.weapon&&W?.unlocked(look.weapon))scenes.push({name:'weapon',duration:4500},{name:'idle',duration:2500});\r\n  if(look.parts.pet)scenes.push({name:'pet',duration:5500},{name:'idle',duration:2500});\r\n  scenes.push({name:'face',duration:5500},{name:'idle',duration:2500},{name:'walk',duration:8000});return scenes;\r\n }\r\n function moment(scenes,time){\r\n  let remaining=Math.max(0,time)%scenes.reduce((sum,scene)=>sum+scene.duration,0);\r\n  for(const scene of scenes){if(remaining<scene.duration)return {...scene,time:remaining,progress:remaining/scene.duration};remaining-=scene.duration;}\r\n }\r\n function create(look,{draw=A.draw,hasPet=false,hasCoachBody=false}={}){\r\n  const body=document.createElement('canvas'),pet=document.createElement('canvas'),weapon=document.createElement('canvas');\r\n  const scenes=playlist(hasPet?{...look,parts:{...look.parts,pet:1}}:look),hasWeapon=scenes.some(scene=>scene.name==='weapon');let specialAt=-Infinity,powerTransform={offset:0,flip:false,zoom:0,gait:0};\r\n  weapon.width=40;weapon.height=72;if(hasWeapon)W.draw(weapon.getContext('2d'),look.weapon);\r\n  draw(pet,look,{base:false,weapon:false,petOnly:true});let lastPose='';\r\n  function paint(canvas,time=0,still=false,motion={}){\r\n   if(canvas.width!==160||canvas.height!==168){canvas.width=160;canvas.height=168;}\r\n   const specialPlaying=hasWeapon&&window.GalaWeaponMotion&&window.performance.now()-specialAt<(window.GalaWeaponMotion.abilityFor(look.weapon)?.animationMs||0);\r\n   const moving=motion.active===true&&motion.phase!=='gone';\r\n   const ctx=canvas.getContext('2d'),scene=still||moving?{name:'idle',time:0,progress:0,duration:6000}:specialPlaying?{name:'weapon',time:0,progress:0,duration:2000}:moment(scenes,time);\r\n   ctx.clearRect(0,0,160,168);ctx.imageSmoothingEnabled=false;canvas.dataset.scene=scene.name;\r\n   const motionPose=motion.pose||'idle',movement=still?'idle':({ 'air-run':'wiggle',bounce:'wiggle',wave:'greet',fall:'look-down','climb-out':'climb',recover:'walk' }[motionPose]||motionPose),active=movement!=='idle'&&movement!=='gone';\r\n   const seconds=scene.time/1000,wave=Math.sin(seconds*TAU*1.15),blink=!still&&(movement==='look-down'||(scene.name==='face'?(seconds>1.5&&seconds<1.68)||(seconds>3.2&&seconds<3.37):(time%4900>4570&&time%4900<4710)));\r\n   const pose={};if(scene.name==='walk'||movement==='walk'||movement==='wiggle'||movement==='climb')pose.walk=movement==='walk'?Math.sin(time/430)*.45:active?Math.sin(time/85)*.8:wave;\r\n   if(scene.name==='pet')pose.petting=wave;\r\n   if(movement==='climb')pose.petting=Math.sin(time/120)*.95;\r\n   if(movement==='greet')pose.greet=Math.sin(time/95);\r\n   if(scene.name==='weapon')pose.weapon=true;\r\n   // The DFO rig turns: walking scenes face along the path, the War Room passes the camera's facing (motion.yaw).\r\n   const rig=A?.renderer==='dfo-rig-1',walking=scene.name==='walk'&&!still,yaw=!rig?undefined:walking&&scene.time/1000<6.5?-Math.PI/2:motion.yaw??A.DEFAULT_YAW;\r\n   const poseKey=JSON.stringify([blink,scene.name,movement,Math.round((pose.walk||pose.petting||0)*10),Math.round((pose.greet||0)*10),yaw==null?0:Math.round(yaw*16/Math.PI)]);\r\n   if(poseKey!==lastPose){draw(body,look,{base:false,weapon:false,companion:false,prop:scene.name!=='weapon',blink,pose,...(yaw==null?{}:{yaw})});lastPose=poseKey;}\r\n   let offset=0,flip=false,zoom=0;\r\n   if(scene.name==='walk'){\r\n    if(seconds<2.6){offset=170*ease(seconds/2.6);flip=true;}\r\n    else if(seconds<3.5)offset=170;\r\n    else if(seconds<6.5){offset=170*(1-ease((seconds-3.5)/3));}\r\n   }\r\n   if(scene.name==='face')zoom=ease(Math.min(seconds/.9,(5.5-seconds)/.9));\r\n   const bob=still?0:Math.sin(time/850)*.8;\r\n   if(zoom<1){ctx.globalAlpha=(1-zoom)*.5;ctx.fillStyle='#b69adb';ctx.beginPath();ctx.ellipse(80+offset,153,35,3,0,0,TAU);ctx.fill();ctx.globalAlpha=1;}\r\n   let gait=0;\r\n   if(scene.name==='walk'||movement==='walk'||movement==='wiggle'||movement==='climb'||movement==='look-down'){\r\n    gait=movement==='climb'?Math.sin(time/110)*7:movement==='wiggle'?Math.sin(time/72)*4:movement==='look-down'?Math.sin(time/90)*2:movement==='walk'?Math.sin(time/430)*3:wave*2;\r\n   }\r\n   powerTransform={offset,flip,zoom,gait};\r\n   ctx.save();ctx.translate(offset,0);if(flip){ctx.translate(160,0);ctx.scale(-1,1);}\r\n   if(rig&&(scene.name==='walk'||movement==='walk'))ctx.drawImage(body,0,0,64,96,32,13+bob,96,144); // the rig walks its own legs\r\n   else if(scene.name==='walk'||movement==='walk'||movement==='wiggle'||movement==='climb'||movement==='look-down'){\r\n    ctx.drawImage(body,0,0,64,70,32,13+bob,96,105);\r\n    ctx.drawImage(body,0,70,32,26,32,118+gait,48,39);\r\n    ctx.drawImage(body,32,70,32,26,80,118-gait,48,39);\r\n   }else ctx.drawImage(body,17*zoom,10*zoom,64-34*zoom,96-66*zoom,32-24*zoom,13-3*zoom+bob,96+48*zoom,144);\r\n   if(hasCoachBody&&['greet','climb','wiggle'].includes(movement))drawCoachArms(ctx,body,movement,time);\r\n   if((look.parts.pet||hasPet)&&zoom<1){\r\n    ctx.globalAlpha=1-zoom;const affection=scene.name==='pet'?Math.max(0,wave)*1.3:0;ctx.drawImage(pet,32,13-affection,96,144);ctx.globalAlpha=1;\r\n    if(scene.name==='pet'){const rise=(seconds%1.25)/1.25,hx=45,hy=113-rise*18;ctx.globalAlpha=Math.sin(rise*Math.PI)*.85;ctx.fillStyle='#ff97c2';ctx.fillRect(hx-3,hy-2,3,3);ctx.fillRect(hx+1,hy-2,3,3);ctx.fillRect(hx-2,hy+1,5,2);ctx.fillRect(hx,hy+3,1,1);ctx.globalAlpha=1;}\r\n   }\r\n   if(motion.phase==='bounce'&&!still){ctx.save();ctx.globalAlpha=.76;ctx.fillStyle='#ead8bb';for(const [x,y] of [[34,151],[43,146],[53,151],[105,151],[116,145],[126,150]])ctx.fillRect(x+(Math.sin(time/70+x)*3|0),y,3,2);ctx.restore();}\r\n   if(hasWeapon&&zoom<1&&window.GalaWeaponMotion){\r\n    const M=window.GalaWeaponMotion,ability=M.abilityFor(look.weapon),special=ability&&window.performance.now()-specialAt<ability.animationMs;\r\n    const attack=scene.name==='weapon',beat=scene.time%1350;\r\n    ctx.save();ctx.globalAlpha=1-zoom;ctx.translate(112,88+bob*2);ctx.rotate(-Math.PI/2);\r\n    M.drawAnimatedWeapon(ctx,look.weapon,{weapons:W,x:0,y:0,scale:.55,now:special?window.performance.now():scene.time,action:special?{startedAt:specialAt,special:true}:attack?{startedAt:scene.time-beat,special:false}:null,reducedMotion:still});ctx.restore();\r\n   }else if(hasWeapon&&zoom<1){\r\n    ctx.globalAlpha=1-zoom;ctx.save();\r\n    if(scene.name==='weapon'){\r\n     const melee=['rapier','greatsword','dagger','spear','trident','scythe'].includes(look.weapon.type),beat=(seconds%1.35)/1.35;\r\n     ctx.translate(115,79);ctx.rotate(Math.sin(beat*TAU)*(melee ? .8 : .09));ctx.drawImage(weapon,-19,-36,38,68);ctx.restore();\r\n     ctx.strokeStyle='#87efff';ctx.lineWidth=2;ctx.globalAlpha=(1-zoom)*Math.sin(beat*Math.PI);\r\n     if(melee){ctx.beginPath();ctx.arc(106,77,30,-1.6,-.15);ctx.stroke();}\r\n     else{ctx.beginPath();ctx.ellipse(116,67,8+beat*16,6+beat*13,0,0,TAU);ctx.stroke();ctx.fillStyle='#d8ffff';ctx.fillRect(122+beat*24,62-beat*24,5,2);}\r\n    }else{ctx.translate(115,78+bob*2);ctx.rotate(-.1);ctx.drawImage(weapon,-16,-31,32,58);ctx.restore();}\r\n    ctx.globalAlpha=1;\r\n   }\r\n   ctx.restore();return scene;\r\n  }\r\n  return {paint,scenes,weapon:hasWeapon?look.weapon:null,get powerTransform(){return {...powerTransform};},get feetFraction(){\r\n   // Saved coach designs have different transparent margins below their feet.\r\n   const context=body.getContext('2d');if(!context.getImageData)return null;\r\n   const pixels=context.getImageData(0,0,body.width,body.height).data;\r\n   for(let y=body.height-1;y>=0;y--)for(let x=0;x<body.width;x++)if(pixels[(y*body.width+x)*4+3]>200)return (13+(y+1)*1.5)/168;\r\n   return null;\r\n  },triggerSpecial(){if(hasWeapon)specialAt=window.performance.now();}};\r\n }\r\n  function drawCoachArms(ctx,body,movement,time){\r\n   // Move the outer limb pixels; center-column crops also include the coach's face and torso.\r\n   const wave=Math.sin(time/170),climb=Math.sin(time/110),sway=Math.sin(time/72),arms=movement==='greet'?\r\n    [{x:48,y:43,w:16,h:29,px:48,py:62,angle:wave*.4}]:\r\n    [{x:0,y:52,w:16,h:25,px:16,py:52,angle:movement==='climb'?.5+climb*.25:sway*.3},\r\n     {x:48,y:52,w:16,h:25,px:48,py:52,angle:movement==='climb'?-.5-climb*.25:-sway*.3}];\r\n  ctx.save();ctx.translate(32,13);ctx.scale(1.5,1.5);\r\n  for(const arm of arms){\r\n    ctx.save();ctx.globalCompositeOperation='destination-out';ctx.fillRect(arm.x,arm.y,arm.w,arm.h);ctx.restore();\r\n    ctx.save();ctx.translate(arm.px,arm.py);ctx.rotate(arm.angle);ctx.drawImage(body,arm.x,arm.y,arm.w,arm.h,arm.x-arm.px,arm.y-arm.py,arm.w,arm.h);ctx.restore();\r\n  }\r\n  ctx.restore();\r\n }\r\n window.GalaPerformance={playlist,moment,create};\r\n})();\r\n","pod/gala-rig/avatar.mjs":"// window.GalaAvatar: the player's Gala character, now a DFO-style procedural rig drawn into\r\n// the same 64x96 pixel sprite every consumer already uses (draw(canvas, look, options)).\r\nimport { sections, dyes, defaultLook, normalize, skinColor, tone } from './look.mjs';\r\nimport { DESIGNS, SPECIES, HEADS, BUILDS } from './designs.mjs';\r\nimport { ramp, FIXED, ALLOYS } from './palette.mjs';\r\nimport { buildRig, poseRig } from './rig.mjs';\r\nimport { rasterize, cameraOf } from './raster.mjs';\r\nimport { WRIST_JUNCTION_Y } from './dfo/neutralBody.mjs';\r\nimport { HSCALE } from './dfo/pieceLoft.mjs';\r\nimport * as legacy from './legacy.mjs';\r\nimport { seatWeapon, weaponMats } from './weapon.mjs';\r\nimport { petImage } from './pet.mjs';\r\n\r\nexport const DEFAULT_YAW = -0.35, SCALE = 36, OX = 32, OY = 84;\r\n// Menu previews of things worn behind the body turn the figure so the piece shows.\r\nexport const PREVIEW_YAW = { back: 2.55, tail: 2.1 };\r\n// Menu tiles for alien-form sections show the bare alien (no outfit; no hair where the skull matters), and\r\n// body tiles wear the species' own skin, so 40 species read as 40 different aliens at tile size.\r\nconst WARDROBE = ['headwear', 'neck', 'torso', 'shoulders', 'arms', 'hands', 'legs', 'feet', 'back', 'armor', 'shield'];\r\nexport const PREVIEW_BARE = Object.fromEntries([...['body', 'build', 'head', 'crown', 'jaw', 'tail', 'skin'].map((id) => [id, [...WARDROBE, 'hair']]), ...['face', 'facial', 'hair'].map((id) => [id, WARDROBE])]);\r\nconst FLAT = new Set(['held', 'base', 'pet', 'skin', 'build', 'alloy']);\r\nconst ACCENT = ['trim', 'gold', 'pearl', 'glow'];\r\nconst COLLECTION_AT = { body: [0.075, 1.80, 0.1, 1], face: [0, 1.715, 0.085], hair: [0.06, 2.0, 0.12], facial: [0, 1.715, 0.075], headwear: [0, 1.93, 0.165],\r\n  neck: [0, 1.6, 0.105], torso: [-0.09, 1.48, 0.115], shoulders: [0.25, 1.66, 0.06, 1], arms: [0.235, 1.32, 0.07, 1], hands: [0.235, 0.9, 0.055, 1],\r\n  legs: [0.082, 0.52, 0.11, 1], feet: [0.082, 0.07, 0.2, 1], back: [0.12, 1.5, -0.12, 1, -1], crown: [0, 2.02, 0.07], jaw: [0, 1.72, 0.085],\r\n  tail: [0.1, 1.0, -0.1, 1, -1], armor: [0, 1.38, 0.135], shield: [-0.3, 1.05, 0.1] };\r\n// A piece without a group rides the part of the rig its section dresses.\r\nconst GROUP_OF = { face: 'head', hair: 'head', facial: 'head', headwear: 'head', crown: 'head', jaw: 'head', head: 'head', arms: 'arm', hands: 'arm', shield: 'arm', feet: 'leg' };\r\n\r\n/** Look -> rig recipe (pure data: build, head rows, pieces). */\r\nexport function recipe(look, { bare = null } = {}) {\r\n  const p = look.parts, base = (id) => (p[id] || 0) % 10, grp = (id) => Math.floor((p[id] || 0) / 10);\r\n  const species = SPECIES[base('body')];\r\n  const pieces = [];\r\n  for (const s of sections) {\r\n    if (FLAT.has(s.id) || bare?.has(s.id)) continue;\r\n    const design = DESIGNS[s.id]?.[base(s.id)];\r\n    if (!design) continue;\r\n    const accent = ACCENT[grp(s.id)];\r\n    const group = GROUP_OF[s.id] || 'body', g = grp(s.id), at = COLLECTION_AT[s.id];\r\n    // Collections (Xyrr/Auv/Oth) also add a small signature detail, so all 40 cuts read apart.\r\n    if (g && at) pieces.push({ mark: ['', 'stud', 'pip', 'spark'][g], at: at.slice(0, 3), mirror: !!at[3], group, mat: accent, n: [0, 0, at[4] ?? 1], slack: 0.6 });\r\n    for (const piece of design) pieces.push({ ...piece, group: piece.group || group, ...(piece.mat === 'accent' ? { mat: accent } : {}) });\r\n  }\r\n  return { build: base('build') ? BUILDS[base('build')] : species.build, head: base('head') ? HEADS[base('head')] || species.head : species.head, pieces };\r\n}\r\n\r\nconst ramps = new Map();\r\nconst R = (hex, opts) => { const k = hex + JSON.stringify(opts || 0); if (!ramps.has(k)) ramps.set(k, ramp(hex, opts)); return ramps.get(k); };\r\nexport function materials(look) {\r\n  const S = skinColor(look), alloy = ALLOYS[(look.parts.alloy || 0) % 10][1], trim = dyes[look.trim ?? look.dye];\r\n  return { ...FIXED, skin: R(S), skinDark: R(S, { lift: -0.1 }), belly: R(S, { lift: 0.1, spread: 0.8 }), feet: R(S, { lift: -0.12 }),\r\n    dye: R(dyes[look.dye]), trim: R(trim), hair: R(trim, { lift: -0.04 }), metal: R(alloy) };\r\n}\r\n\r\nconst W = [251, 235, 206], K = [33, 23, 46], PINK = [223, 139, 155];\r\nexport const MARKS = {\r\n  eye: (M, blink) => blink ? [[0, 1, K], [1, 1, K]] : [[0, 0, W], [1, 0, W], [0, 1, M], [1, 1, K]],\r\n  compound: (M, blink) => blink ? [[-1, 0, K], [0, 0, K], [1, 0, K]] : [[-1, -1, M], [0, -1, M], [1, -1, M], [-1, 0, M], [0, 0, W], [1, 0, M], [-1, 1, K], [0, 1, M], [1, 1, K]],\r\n  orb: (M, blink) => blink ? [[-1, 0, K], [0, 0, K], [1, 0, K]] : [[-1, -1, W], [0, -1, W], [1, -1, W], [-1, 0, W], [0, 0, M], [1, 0, W], [-1, 1, W], [0, 1, K], [1, 1, W]],\r\n  slit: (M, blink) => blink ? [[0, 1, K], [1, 1, K]] : [[0, 0, M], [1, 0, K], [0, 1, K], [1, 1, M]],\r\n  dot: (M, blink) => blink ? [[0, 0, K]] : [[0, 0, M]],\r\n  visor: (M) => [[-2, 0, M], [-1, 0, M], [0, 0, W], [1, 0, M], [2, 0, M], [-2, 1, K], [-1, 1, K], [0, 1, K], [1, 1, K], [2, 1, K]],\r\n  mouth: () => [[-1, 0, K], [0, 0, K], [1, 0, K]],\r\n  grin: () => [[-2, 0, K], [-1, 0, W], [0, 0, W], [1, 0, W], [2, 0, K]],\r\n  blush: () => [[0, 0, PINK], [1, 0, PINK]],\r\n  stud: (M) => [[0, 0, M]],\r\n  pip: (M) => [[0, 0, M], [1, 0, M], [0, 1, M]],\r\n  spark: (M) => [[0, 0, W], [1, 0, M], [0, 1, M]],\r\n  gem: (M) => [[0, -1, M], [-1, 0, M], [0, 0, W], [1, 0, M], [0, 1, M]],\r\n};\r\n\r\nconst frames = new Map();\r\nconst canvasOf = (w, h) => { const c = globalThis.document ? document.createElement('canvas') : new OffscreenCanvas(w, h); c.width = w; c.height = h; return c; };\r\nconst q = (v, s) => Math.round(v / s) * s;\r\n\r\n/** Render the rig figure (no plinth/prop/pet) -> {canvas, hand:[x,y]}; cached per look+pose+facing. */\r\nexport function figure(look, { pose = {}, yaw = DEFAULT_YAW, blink = false, w = 64, h = 96, scale = SCALE, ox = OX, oy = OY, weapon = null, bare = null } = {}) {\r\n  const rec = recipe(look, { bare: bare ? new Set(bare) : null }), mats = materials(look);\r\n  const qp = {}; for (const [k, v] of Object.entries(pose || {})) qp[k] = typeof v === 'number' ? q(v, 0.125) : v;\r\n  const key = JSON.stringify([rec, look.dye, look.trim, look.parts.skin, look.parts.alloy, qp, q(yaw, Math.PI / 32), blink, w, h, scale, ox, oy, weapon]);\r\n  let hit = frames.get(key);\r\n  if (hit) { frames.delete(key); frames.set(key, hit); return hit; }\r\n  const rig = buildRig(rec), pos = poseRig(rig, qp);\r\n  const marks = [];\r\n  for (const f of rig.faces) {\r\n    if (f.mark) continue;\r\n    f.r = mats[f._mat] || mats.dye;\r\n  }\r\n  rig.faces.forEach((f, i) => {\r\n    if (!f.mark) return;\r\n    const P = pos ? pos.subarray(i * 12, i * 12 + 3) : f.p, M = (mats[f._mat] || mats.dark)[4];\r\n    marks.push({ at: [P[0], P[1], P[2]], n: f.n, free: f.free, slack: f.slack, px: (MARKS[f.mark] || MARKS.dot)(M, blink) });\r\n  });\r\n  const solid = rig.faces.filter((f) => !f.mark), solidPos = pos ? new Float32Array(solid.length * 12) : null;\r\n  if (pos) { let k = 0; rig.faces.forEach((f, i) => { if (!f.mark) solidPos.set(pos.subarray(i * 12, i * 12 + 12), 12 * k++); }); }\r\n  let faces = solid, facePos = solidPos;\r\n  const held = weapon && seatWeapon(rig, pos, weapon);\r\n  if (held) { // the weapon rides the right fist (held in every pose)\r\n    const wm = weaponMats(weapon.tier); for (const f of held.faces) f.r = wm[f._mat] || wm.shell;\r\n    facePos = new Float32Array((solid.length + held.faces.length) * 12);\r\n    if (solidPos) facePos.set(solidPos); else solid.forEach((f, i) => facePos.set(f.p, i * 12));\r\n    facePos.set(held.pos, solid.length * 12); faces = [...solid, ...held.faces];\r\n  }\r\n  const img = rasterize(faces, { w, h, scale, ox, oy, yaw, pos: facePos, marks });\r\n  const canvas = canvasOf(w, h), ctx = canvas.getContext('2d'), data = ctx.createImageData(w, h);\r\n  data.data.set(img.data); ctx.putImageData(data, 0, 0);\r\n  // Right hand on screen (the party prop rides it).\r\n  const cam = cameraOf(yaw, 0.1), wY = WRIST_JUNCTION_Y * HSCALE; let hx = 0, hy = 0, n = 0;\r\n  rig.faces.forEach((f, i) => { if (f.g !== 'armR' || f.mark) return; const P = pos ? pos.subarray(i * 12, i * 12 + 12) : f.p; if (rig.base[i * 12 + 1] >= wY) return; for (let k = 0; k < 4; k++) { const [x, y] = cam(P[k * 3], P[k * 3 + 1], P[k * 3 + 2]); hx += x; hy += y; n++; } });\r\n  hit = { canvas, hand: n ? [ox + hx / n * scale, oy - hy / n * scale] : [44, 59] };\r\n  frames.set(key, hit); if (frames.size > 160) frames.delete(frames.keys().next().value);\r\n  return hit;\r\n}\r\n\r\nexport function draw(canvas, look, { base = true, weapon = true, time = 0, companion = true, petOnly = false, prop = true, blink = false, pose = {}, yaw = DEFAULT_YAW, bare = null } = {}) {\r\n  const GW = globalThis.window?.GalaWeapons;\r\n  const equipment = !petOnly && weapon && look.weapon && GW?.unlocked(look.weapon) ? look.weapon : null;\r\n  canvas.width = equipment ? 96 : 64; canvas.height = 96;\r\n  const c = canvas.getContext('2d'); c.imageSmoothingEnabled = false;\r\n  const original = look.parts, p = Object.fromEntries(Object.entries(original).map(([k, v]) => [k, v % 10])), group = (k) => Math.floor((original[k] || 0) / 10);\r\n  const S = skinColor(look), F = dyes[look.dye], H = tone(F, 42), B = tone(F, -40);\r\n  if (!petOnly) {\r\n    if (base) legacy.drawBase(c, p, group('base'), { H });\r\n    const fig = figure(look, { pose, yaw, blink, weapon: equipment, w: canvas.width, bare });\r\n    c.drawImage(fig.canvas, 0, 0);\r\n    if (prop) { c.save(); c.translate(Math.round(fig.hand[0] - 44), Math.round(fig.hand[1] - 59)); legacy.drawProp(c, p, group('held'), { S, H, B }); c.restore(); }\r\n    if (pose.meditationStanding) legacy.drawStandingFlare(canvas, c);\r\n  }\r\n  if (companion && original.pet) {\r\n    const pet = petImage(look, { H, S });\r\n    if (pet) { const layer = canvasOf(pet.w, pet.h), lc = layer.getContext('2d'), d = lc.createImageData(pet.w, pet.h); d.data.set(pet.data); lc.putImageData(d, 0, 0); c.drawImage(layer, 0, 0); }\r\n    else legacy.drawPet(c, look, { S, H, B });\r\n  }\r\n}\r\n\r\n/** Bake-on-save sheet: 5 facings (front, front-3/4, side, back-3/4, back; mirror for the other 3) x idle 2 + walk 6. */\r\nexport const SHEET = { facings: [0, Math.PI / 4, Math.PI / 2, Math.PI * 3 / 4, Math.PI], frames: ['idle0', 'idle1', 'walk0', 'walk1', 'walk2', 'walk3', 'walk4', 'walk5'], w: 64, h: 96 };\r\nexport function sheet(look) {\r\n  const out = canvasOf(SHEET.w * SHEET.frames.length, SHEET.h * SHEET.facings.length), ctx = out.getContext('2d');\r\n  SHEET.facings.forEach((yaw, row) => SHEET.frames.forEach((name, col) => {\r\n    const k = Number(name.slice(-1)), pose = name.startsWith('walk') ? { gait: k / 6 * Math.PI * 2 } : { breath: k };\r\n    ctx.drawImage(figure(look, { yaw, pose: name.startsWith('walk') ? pose : {} }).canvas, col * SHEET.w, row * SHEET.h);\r\n  }));\r\n  return out;\r\n}\r\n\r\nexport { sections, dyes, defaultLook, normalize, skinColor };\r\n","pod/gala-rig/designs.mjs":"// Gala designs as DATA over DFO's neutral rig (the way DFO's humanClasses / villagerDesigns\r\n// are data). DESIGNS[section][base] = list of pieces (see parts.mjs); base = choice % 10,\r\n// collection = floor(choice / 10) recolours every 'accent' piece (trim / gold / pearl / glow).\r\n//\r\n// ANATOMY (rig units, BEFORE the 0.9 height squash; y up, ground 0; +z = the face's front;\r\n// x = sideways, pieces with mirror:true are copied to -x):\r\n//   feet: sole y 0, ankle y 0.10, foot columns x +-0.082, toe z +0.235, heel z -0.06\r\n//   legs: columns x +-0.082, knee y 0.50, thigh top y 0.95 (thigh half-width 0.12, calf 0.085)\r\n//   pelvis y 0.99-1.09 (half-width 0.17, depth 0.09); waist y 1.19 (0.16 x 0.082)\r\n//   chest y 1.46 (half-width 0.21, depth 0.098); shoulder line y 1.61-1.66 (trapezius to x +-0.215)\r\n//   shoulder caps x +-0.24 y 1.50-1.64; arms hang at x +-0.235; elbow y 1.30-1.35; wrist y 0.99\r\n//   hands (mittens) y 0.82-0.985 at x +-0.235; upper arm radius 0.065, forearm 0.045\r\n//   neck y 1.63-1.73 radius 0.075\r\n//   head: chin y 1.70, jaw 1.74, cheek 1.79, temple 1.85 (half-width 0.108, half-depth 0.14,\r\n//   centre z -0.015 -> the face plane is z ~ +0.12), crown top y 2.03. Eyes y 1.86 x +-0.045.\r\n//   Head pieces are authored at this size; the whole head is enlarged 1.3x afterwards.\r\n// Groups: 'body' (torso; follows lean), 'head', 'arm' (one per side), 'leg' (one per side).\r\n// Materials: skin skinDark belly dye trim accent hair metal dark gold pearl glow ivory white glass membrane\r\n\r\nconst SKULL = { jaw: [{ y: 1.70, rx: 0.052, rz: 0.070, cz: 0.010 }, { y: 1.74, rx: 0.082, rz: 0.105, cz: 0.005 }], seam: [{ y: 1.79, rx: 0.102, rz: 0.130, cz: -0.005 }] };\r\nconst head = (skull, jaw = SKULL.jaw, seam = SKULL.seam) => ({ jaw, seam, skull: skull.map(([y, rx, rz, cz]) => ({ y, rx, rz, cz })) });\r\n\r\nexport const HEADS = [\r\n  null,\r\n  head([[1.85, 0.112, 0.138, -0.015], [1.94, 0.118, 0.140, -0.02], [2.03, 0.100, 0.118, -0.02], [2.10, 0.060, 0.070, -0.015], [2.13, 0.02, 0.025, -0.01]]),\r\n  head([[1.85, 0.104, 0.150, -0.03], [1.92, 0.098, 0.175, -0.06], [1.98, 0.080, 0.185, -0.09], [2.03, 0.050, 0.150, -0.12], [2.06, 0.02, 0.08, -0.13]]),\r\n  head([[1.85, 0.150, 0.120, -0.015], [1.90, 0.180, 0.100, -0.02], [1.96, 0.150, 0.090, -0.02], [2.00, 0.080, 0.070, -0.015]]),\r\n  head([[1.85, 0.108, 0.140, -0.015], [1.93, 0.088, 0.135, -0.02], [2.00, 0.048, 0.120, -0.025], [2.07, 0.012, 0.095, -0.03]]),\r\n  head([[1.85, 0.120, 0.150, -0.02], [1.95, 0.142, 0.168, -0.03], [2.05, 0.132, 0.152, -0.03], [2.13, 0.090, 0.100, -0.025], [2.18, 0.03, 0.035, -0.02]]),\r\n  head([[1.85, 0.112, 0.140, -0.015], [1.95, 0.114, 0.138, -0.02], [1.99, 0.108, 0.130, -0.02], [2.00, 0.04, 0.05, -0.02]]),\r\n  head([[1.85, 0.125, 0.140, -0.015], [1.93, 0.118, 0.130, -0.02], [1.99, 0.085, 0.100, -0.018], [2.03, 0.040, 0.050, -0.012]], [{ y: 1.67, rx: 0.015, rz: 0.03, cz: 0.02 }, { y: 1.74, rx: 0.06, rz: 0.09, cz: 0.01 }]),\r\n  head([[1.86, 0.14, 0.12, -0.01], [1.92, 0.165, 0.11, -0.015], [1.96, 0.125, 0.09, -0.015], [1.99, 0.04, 0.04, -0.01]], [{ y: 1.69, rx: 0.02, rz: 0.04, cz: 0.03 }, { y: 1.74, rx: 0.05, rz: 0.08, cz: 0.02 }], [{ y: 1.79, rx: 0.09, rz: 0.11, cz: 0 }]),\r\n  head([[1.85, 0.120, 0.140, -0.015], [1.93, 0.125, 0.130, -0.02], [1.98, 0.100, 0.110, -0.02], [2.01, 0.05, 0.06, -0.02]]),\r\n];\r\n// Head 9 (twin lobe) adds two skull domes as pieces.\r\nconst TWIN_LOBES = [{ loft: [[1.93, 0.04, 0.05], [1.99, 0.075, 0.085], [2.06, 0.07, 0.08], [2.11, 0.03, 0.035]], cx: 0.07, mirror: true, group: 'head', mat: 'skin', seg: 10 }];\r\n\r\nexport const BUILDS = [\r\n  null,\r\n  { torso: 0.8, shoulder: 0.8, arm: 0.78, hand: 0.85, neck: 0.85, leg: 0.82 },                       // Slight\r\n  { torso: 1.38, shoulder: 1.1, arm: 1.2, neck: 1.35, jaw: 1.2, leg: 1.3 },                          // Stout\r\n  { torso: 1.15, shoulder: 1.55, arm: 1.3, hand: 1.2, neck: 1.15, leg: 1.05 },                       // Broad\r\n  { torso: 0.78, shoulder: 0.95, arm: 0.72, hand: 1.1, neck: 0.75, leg: 0.72 },                      // Lanky\r\n  { torso: 1.55, shoulder: 1.25, arm: 1.35, hand: 1.25, neck: 1.4, jaw: 1.3, leg: 1.45 },            // Heavy\r\n  { torso: 0.9, shoulder: 1.15, arm: 0.75, hand: 1.0, neck: 0.8, leg: 0.8 },                         // Wiry\r\n  { torso: 1.6, shoulder: 1.6, arm: 1.6, hand: 1.5, neck: 1.5, jaw: 1.4, skull: 0.9, leg: 1.5 },     // Hulking\r\n  { torso: 0.7, shoulder: 0.72, arm: 0.7, hand: 0.8, neck: 0.8, skull: 1.15, jaw: 0.8, leg: 0.7 },   // Petite\r\n  { torso: 0.95, shoulder: 1.0, arm: 1.15, hand: 1.5, leg: 0.9 },                                    // Long-limbed\r\n];\r\n\r\n// Body = species: build + head + race features (DFO race pieces re-skinned).\r\nconst SPECIES = [\r\n  { build: { skull: 1.04 }, head: null, pieces: [] },\r\n  // Vexling: slight, tall swept ears.\r\n  { build: BUILDS[1], head: null, pieces: [{ fan: [[0.095, 1.86, -0.01], [0.13, 1.83, -0.03], [0.205, 2.0, -0.08], [0.11, 1.93, -0.04]], mirror: true, group: 'head', mat: 'skin' },\r\n    { fan: [[0.1, 1.86, 0.0], [0.17, 1.95, -0.06], [0.12, 1.9, -0.035]], mirror: true, group: 'head', mat: 'skinDark' }] },\r\n  // Orryx: broad, domed, two antennae with accent knobs.\r\n  { build: { torso: 1.15, shoulder: 1.15 }, head: HEADS[1], pieces: [\r\n    { tube: [[0.045, 1.98, 0.0, 0.022], [0.075, 2.06, 0.02, 0.018], [0.095, 2.11, 0.045, 0.015]], mirror: true, group: 'head', mat: 'skinDark' },\r\n    { loft: [[2.095, 0.0, 0.0], [2.105, 0.03, 0.03], [2.135, 0.03, 0.03], [2.145, 0.0, 0.0]], cx: 0.095, mirror: true, group: 'head', mat: 'accent', seg: 8 }] },\r\n  // Mollu: stout and round, round ear nubs, a cat tail.\r\n  { build: BUILDS[2], head: HEADS[5], pieces: [{ dfo: 'tail:khajiit', mat: 'skin' },\r\n    { loft: [[1.83, 0.0, 0.0], [1.85, 0.035, 0.025], [1.9, 0.04, 0.03], [1.94, 0.0, 0.0]], cx: 0.12, mirror: true, group: 'head', mat: 'skin', seg: 8 }] },\r\n  // Krell: hulking, heavy brow, big drooping ears.\r\n  { build: BUILDS[7], head: head([[1.85, 0.118, 0.140, -0.015], [1.92, 0.108, 0.135, -0.02], [1.98, 0.088, 0.108, -0.018], [2.03, 0.05, 0.058, -0.012]]), pieces: [\r\n    { fan: [[0.1, 1.86, -0.01], [0.24, 1.9, -0.05], [0.25, 1.8, -0.07], [0.11, 1.8, -0.03]], mirror: true, group: 'head', mat: 'skin' },\r\n    { dfo: 'brow', mat: 'skinDark' }] },\r\n  // Nymbi: slight, big translucent cheek fins.\r\n  { build: BUILDS[1], head: null, pieces: [{ fan: [[0.09, 1.80, 0.02], [0.2, 1.88, -0.03], [0.23, 1.78, -0.05], [0.19, 1.68, -0.04], [0.08, 1.73, 0.0]], mirror: true, group: 'head', mat: 'trim' },\r\n    { tube: [[0.1, 1.79, 0.01, 0.012], [0.21, 1.8, -0.04, 0.01]], mirror: true, group: 'head', mat: 'accent' }] },\r\n  // Quorlan: bulb head, tentacles instead of feet.\r\n  { build: { torso: 1.05, leg: 0.8 }, head: HEADS[5], pieces: [\r\n    { tube: [[0.07, 0.6, 0.04, 0.05], [0.11, 0.35, 0.09, 0.045], [0.12, 0.12, 0.16, 0.035], [0.06, 0.04, 0.26, 0.022], [0.0, 0.08, 0.3, 0.012]], mirror: true, group: 'body', mat: 'skinDark' },\r\n    { tube: [[0.0, 0.62, 0.05, 0.05], [0.0, 0.35, 0.12, 0.04], [0.0, 0.1, 0.2, 0.03], [0.0, 0.04, 0.3, 0.015]], group: 'body', mat: 'skin' },\r\n    { tube: [[0.06, 0.6, -0.05, 0.045], [0.12, 0.3, -0.12, 0.04], [0.14, 0.06, -0.2, 0.025]], mirror: true, group: 'body', mat: 'skinDark' }] },\r\n  // Xelith: slender, long skull, lizard tail.\r\n  { build: { torso: 0.82, shoulder: 0.85, arm: 0.85, leg: 0.88, skull: 1.08 }, head: HEADS[2], pieces: [{ dfo: 'tail:argonian', mat: 'skin' }] },\r\n  // Dravox: broad, feline muzzle and ears, gold implants.\r\n  { build: BUILDS[3], head: null, pieces: [{ dfo: 'race:Khajiit', mat: 'skin' },\r\n    { mark: 'stud', at: [0.085, 1.88, 0.09], n: [0.6, 0, 0.8], mirror: true, mat: 'gold' }] },\r\n  // Ulumi: domed, glowing antennae, cheek freckles.\r\n  { build: { skull: 1.1 }, head: HEADS[1], pieces: [\r\n    { tube: [[0.03, 2.0, -0.02, 0.018], [0.05, 2.07, -0.04, 0.015], [0.08, 2.11, -0.02, 0.013]], mirror: true, group: 'head', mat: 'skinDark' },\r\n    { loft: [[2.09, 0.0, 0.0], [2.1, 0.026, 0.026], [2.13, 0.026, 0.026], [2.14, 0.0, 0.0]], cx: 0.08, mirror: true, group: 'head', mat: 'glow', seg: 8 },\r\n    { mark: 'dot', at: [0.07, 1.79, 0.1], mirror: true, mat: 'accent' }] },\r\n];\r\n\r\nconst eye = (kind = 'eye', y = 1.86, x = 0.045, z = 0.122, mat = 'dark') => ({ mark: kind, at: [x, y, z], mirror: x !== 0, mat });\r\nconst M = { mark: 'mouth', at: [0, 1.765, 0.105], mat: 'dark' };\r\nconst FACES = [\r\n  [eye('eye', 1.86, 0.045, 0.122, 'trim'), M],                                            // Vela gaze: trim irises\r\n  [eye('orb', 1.865, 0, 0.13, 'glow'), M],                                                // Solo orb: one big glowing eye\r\n  [eye('eye', 1.85, 0.05, 0.122, 'dye'), eye('orb', 1.935, 0, 0.12, 'glow'), M],          // Triune sight: third eye on the brow\r\n  [{ mark: 'visor', at: [0, 1.865, 0.13], mat: 'glow' }, M],                              // Obsidian visor\r\n  [eye('eye', 1.88, 0.05, 0.12, 'dye'), eye('eye', 1.83, 0.04, 0.122, 'glow'), M],        // Quasar quartet: two pairs\r\n  [eye('compound', 1.87, 0.055, 0.115, 'dark'), M],                                       // Mothkin: big compound eyes\r\n  [eye('eye', 1.865, 0.045, 0.122, 'accent'), { mark: 'grin', at: [0, 1.765, 0.105], mat: 'dark' }], // Ziggy grin\r\n  [eye('eye', 1.86, 0.045, 0.122, 'dark'), { mark: 'blush', at: [0.07, 1.80, 0.105], mirror: true, mat: 'dark' }, M], // Mollu blush\r\n  [{ loft: [[1.83, 0.11, 0.14, 1, -0.012], [1.90, 0.115, 0.145, 1, -0.014]], group: 'head', mat: 'accent', arc: [-0.2, Math.PI + 0.2], seg: 14, caps: false }, eye('eye', 1.865, 0.045, 0.14, 'glow')], // Starborn mask\r\n  [eye('dot', 1.89, 0.03, 0.125, 'glow'), eye('dot', 1.89, 0.07, 0.112, 'glow'), eye('dot', 1.845, 0.05, 0.12, 'glow'), M], // Oracle six\r\n];\r\n\r\nimport CARDS from './designs/index.mjs';\r\n// Seed designs; the remaining sections are authored by local cards (see .gala/cards).\r\nexport const DESIGNS = {\r\n  body: SPECIES.map((s) => s.pieces),\r\n  face: FACES,\r\n  head: HEADS.map((_, i) => i === 9 ? TWIN_LOBES : []),\r\n  build: BUILDS.map(() => []),\r\n  alloy: BUILDS.map(() => []),\r\n};\r\nexport { SPECIES };\r\n\r\n// Calibration examples (choice 0 of each wardrobe section); cards follow these patterns.\r\nexport const EXAMPLES = {\r\n  torso: [ // Vex tuxedo: jacket + sleeves (dye), white shirt V, trim lapels, accent buttons\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.64, th: 0.01, mat: 'dye' },\r\n    { fan: [[0, 1.30, 0.11], [-0.07, 1.6, 0.1], [0.07, 1.6, 0.1]], mat: 'white' },\r\n    { fan: [[0.0, 1.28, 0.112], [0.075, 1.6, 0.105], [0.12, 1.58, 0.095], [0.05, 1.40, 0.11]], mirror: true, mat: 'trim' },\r\n    { mark: 'stud', at: [0, 1.22, 0.1], group: 'body', mat: 'accent' }, { mark: 'stud', at: [0, 1.14, 0.098], group: 'body', mat: 'accent' },\r\n  ],\r\n  legs: [ // Vex trousers: legs + pelvis, dark trim side stripe\r\n    { zone: 'legs', yLo: 0.12, yHi: 1.0, th: 0.01, mat: 'dye' },\r\n    { zone: 'pelvis', yLo: 0.9, yHi: 1.12, th: 0.012, mat: 'dye' },\r\n  ],\r\n  feet: [ // Vela slippers: thin trim shoe + accent toe\r\n    { zone: 'feet', yLo: 0, yHi: 0.11, th: 0.01, mat: 'trim' },\r\n    { box: [0.06, 0.0, 0.17, 0.104, 0.06, 0.24], mirror: true, group: 'leg', mat: 'accent' },\r\n  ],\r\n  hair: [ // Vex wave: DFO short cap in hair colour + a swept front wave\r\n    { dfo: 'hair:short', mat: 'hair' },\r\n    { tube: [[-0.09, 2.0, 0.08, 0.03], [-0.02, 2.05, 0.12, 0.035], [0.07, 2.02, 0.11, 0.03], [0.12, 1.94, 0.07, 0.02]], group: 'head', mat: 'hair' },\r\n  ],\r\n  headwear: [ // Vela circlet: a ring round the brow with a front gem\r\n    { loft: [[1.905, 0.118, 0.15, 1, -0.014], [1.93, 0.118, 0.15, 1, -0.014]], group: 'head', mat: 'accent', seg: 16, caps: false },\r\n    { mark: 'gem', at: [0, 1.92, 0.138], mat: 'accent' },\r\n  ],\r\n  neck: [ // Zor cravat: white knot + falling tail\r\n    { loft: [[1.62, 0.04, 0.03], [1.66, 0.05, 0.04], [1.70, 0.04, 0.03]], group: 'body', mat: 'white', seg: 8 },\r\n    { fan: [[0, 1.6, 0.11], [-0.03, 1.45, 0.115], [0.0, 1.40, 0.118], [0.03, 1.45, 0.115]], mat: 'white' },\r\n  ],\r\n  shoulders: [ // Vela epaulettes: flat accent boards on each shoulder + fringe\r\n    { box: [0.17, 1.63, -0.06, 0.31, 1.67, 0.06], mirror: true, group: 'arm', mat: 'accent' },\r\n    { box: [0.29, 1.56, -0.05, 0.31, 1.63, 0.05], mirror: true, group: 'arm', mat: 'accent' },\r\n  ],\r\n  arms: [ // Vela silk: a cuff band at each wrist\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.08, th: 0.018, mat: 'trim' },\r\n  ],\r\n  hands: [ // Vela gloves\r\n    { zone: 'hands', th: 0.008, mat: 'white' },\r\n  ],\r\n  back: [ // Vela ribbons: two ribbons from the shoulder blades\r\n    { tube: [[0.08, 1.55, -0.1, 0.02], [0.11, 1.2, -0.16, 0.022], [0.13, 0.85, -0.2, 0.02], [0.14, 0.6, -0.22, 0.012]], mirror: true, group: 'body', mat: 'trim' },\r\n  ],\r\n  facial: [],\r\n};\r\nfor (const [id, list] of Object.entries(CARDS)) DESIGNS[id] = list;\r\nfor (const [id, list] of Object.entries(EXAMPLES)) DESIGNS[id] = DESIGNS[id] || [list];\r\n","pod/gala-rig/designs/armor.mjs":"// Gala armor designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 No armour */ [\r\n\r\n  ],\r\n  /* 1 Hardsuit torso */ [\r\n    {layer:'armor',mat:'metal',zone:'torso',yLo:1.12,yHi:1.62,th:0.022},\r\n    {box:[-0.025,1.2,0.112,0.025,1.56,0.135],group:'body',mat:'accent'},\r\n    {mark:'gem',at:[0,1.5,0.14],group:'body',mat:'glow'},\r\n    {layer:'armor',mat:'metal',zone:'neck',yLo:1.62,yHi:1.72,th:0.02}\r\n  ],\r\n  /* 2 Shoulder pods */ [\r\n    {dfo:'pauldrons',mat:'metal'},\r\n    {box:[0.19,1.6,-0.085,0.335,1.7,0.085],mirror:true,group:'arm',mat:'metal'},\r\n    {box:[0.3,1.46,-0.075,0.35,1.62,0.075],mirror:true,group:'arm',mat:'metal'},\r\n    {box:[0.2,1.7,-0.03,0.32,1.725,0.03],mirror:true,group:'arm',mat:'accent'},\r\n    {mark:'gem',at:[0.255,1.64,0.05],mirror:true,group:'arm',mat:'accent'}\r\n  ],\r\n  /* 3 Bracer gauntlets */ [\r\n    {layer:'armor',mat:'metal',zone:'hands',th:0.018},\r\n    {layer:'armor',mat:'metal',zone:'sleeves',yLo:0.83,yHi:1.22,th:0.018,group:'arm'},\r\n    {loft:[[1.1,0.07,0.07],[1.15,0.07,0.07]],cx:0.235,mirror:true,group:'arm',mat:'accent',seg:10,caps:false}\r\n  ],\r\n  /* 4 Thigh plates */ [\r\n    {layer:'armor',mat:'metal',zone:'legs',yLo:0.56,yHi:1,th:0.022,zScale:0.28},\r\n    {layer:'armor',mat:'metal',zone:'pelvis',yLo:0.9,yHi:1.14,th:0.022},\r\n    {box:[0.04,0.46,0.06,0.125,0.58,0.13],mirror:true,group:'leg',mat:'metal'}\r\n  ],\r\n  /* 5 Armoured boots */ [\r\n    {layer:'armor',mat:'metal',zone:'feet',yLo:0,yHi:0.42,th:0.02},\r\n    {loft:[[0.4,0.12,0.105],[0.45,0.12,0.105]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {box:[0.045,0,0.17,0.12,0.07,0.25],mirror:true,group:'leg',mat:'accent'}\r\n  ],\r\n  /* 6 Pressure helm (open) */ [\r\n    {dfo:'helm',mat:'metal'},\r\n    {loft:[[1.92,0.125,0.15,1,-0.014],[2,0.13,0.17,1,0.01],[2.08,0.12,0.17,1,0.03],[2.12,0.1,0.14,1,0.03]],group:'head',mat:'glass',seg:14,arc:[0.5,2.64],caps:false},\r\n    {layer:'armor',mat:'metal',zone:'neck',yLo:1.62,yHi:1.72,th:0.02}\r\n  ],\r\n  /* 7 Pressure helm (closed) */ [\r\n    {dfo:'helm',mat:'metal'},\r\n    {loft:[[1.72,0.12,0.148,1,-0.014],[1.82,0.123,0.152,1,-0.014],[1.94,0.12,0.148,1,-0.014]],group:'head',mat:'glass',seg:14,arc:[0.45,2.69],caps:false},\r\n    {loft:[[1.7,0.122,0.15,1,-0.014],[1.73,0.122,0.15,1,-0.014]],group:'head',mat:'accent',seg:14,arc:[0.45,2.69],caps:false},\r\n    {layer:'armor',mat:'metal',zone:'neck',yLo:1.62,yHi:1.72,th:0.02}\r\n  ],\r\n  /* 8 Half hardsuit */ [\r\n    {layer:'armor',mat:'metal',zone:'torso',yLo:1.12,yHi:1.62,th:0.022},\r\n    {box:[-0.025,1.2,0.112,0.025,1.56,0.135],group:'body',mat:'accent'},\r\n    {mark:'gem',at:[0,1.5,0.14],group:'body',mat:'glow'},\r\n    {dfo:'pauldrons',mat:'metal'},\r\n    {box:[0.19,1.6,-0.085,0.335,1.7,0.085],mirror:true,group:'arm',mat:'metal'},\r\n    {box:[0.3,1.46,-0.075,0.35,1.62,0.075],mirror:true,group:'arm',mat:'metal'},\r\n    {box:[0.2,1.7,-0.03,0.32,1.725,0.03],mirror:true,group:'arm',mat:'accent'},\r\n    {layer:'armor',mat:'metal',zone:'hands',th:0.018},\r\n    {layer:'armor',mat:'metal',zone:'sleeves',yLo:0.83,yHi:1.22,th:0.018,group:'arm'},\r\n    {loft:[[1.1,0.07,0.07],[1.15,0.07,0.07]],cx:0.235,mirror:true,group:'arm',mat:'accent',seg:10,caps:false}\r\n  ],\r\n  /* 9 Full hardsuit */ [\r\n    {layer:'armor',mat:'metal',zone:'torso',yLo:1.12,yHi:1.62,th:0.022},\r\n    {box:[-0.025,1.2,0.112,0.025,1.56,0.135],group:'body',mat:'accent'},\r\n    {dfo:'pauldrons',mat:'metal'},\r\n    {box:[0.19,1.6,-0.085,0.335,1.7,0.085],mirror:true,group:'arm',mat:'metal'},\r\n    {layer:'armor',mat:'metal',zone:'hands',th:0.018},\r\n    {layer:'armor',mat:'metal',zone:'sleeves',yLo:0.83,yHi:1.22,th:0.018,group:'arm'},\r\n    {layer:'armor',mat:'metal',zone:'legs',yLo:0.56,yHi:1,th:0.022,zScale:0.28},\r\n    {layer:'armor',mat:'metal',zone:'pelvis',yLo:0.9,yHi:1.14,th:0.022},\r\n    {layer:'armor',mat:'metal',zone:'feet',yLo:0,yHi:0.42,th:0.02},\r\n    {loft:[[0.4,0.12,0.105],[0.45,0.12,0.105]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {dfo:'helm',mat:'metal'},\r\n    {loft:[[1.72,0.12,0.148,1,-0.014],[1.82,0.123,0.152,1,-0.014],[1.94,0.12,0.148,1,-0.014]],group:'head',mat:'glass',seg:14,arc:[0.45,2.69],caps:false},\r\n    {loft:[[1.7,0.122,0.15,1,-0.014],[1.73,0.122,0.15,1,-0.014]],group:'head',mat:'accent',seg:14,arc:[0.45,2.69],caps:false}\r\n  ]\r\n];\r\n","pod/gala-rig/designs/arms.mjs":"// Gala arms designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela silk */ [\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.08, th: 0.018, mat: 'trim' }\r\n  ],\r\n  /* 1 Quasar flares */ [\r\n    { zone: 'sleeves', yLo: 1, yHi: 1.6, th: 0.012, mat: 'dye' },\r\n    { loft: [[0.88,0.14,0.14],[0.95,0.115,0.115],[1.03,0.075,0.075],[1.12,0.06,0.06]], cx: 0.235, seg: 12, caps: false, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[1.01,0.082,0.082],[1.05,0.082,0.082]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 2 Krell cuffs */ [\r\n    { zone: 'sleeves', yLo: 0.98, yHi: 1.1, th: 0.02, mat: 'metal' },\r\n    { box: [0.16,0.96,-0.075,0.31,1.12,0.075], mat: 'metal', mirror: true, group: 'arm' },\r\n    { box: [0.155,1.1,-0.07,0.315,1.13,0.07], mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'stud', at: [0.235,1.04,0.08], mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'stud', at: [0.19,1.04,0.08], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 3 Mollu lace */ [\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.64, th: 0.008, mat: 'white' },\r\n    { loft: [[0.93,0.115,0.115],[1,0.085,0.085],[1.06,0.06,0.06]], cx: 0.235, seg: 12, caps: false, mat: 'white', mirror: true, group: 'arm' },\r\n    { loft: [[1.1,0.08,0.08],[1.17,0.08,0.08]], cx: 0.235, seg: 12, caps: false, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[1.22,0.082,0.082],[1.29,0.082,0.082]], cx: 0.235, seg: 12, caps: false, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[1.48,0.105,0.105],[1.55,0.105,0.105]], cx: 0.235, seg: 12, caps: false, mat: 'trim', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 4 Orryx stripes */ [\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { loft: [[1.05,0.082,0.082],[1.12,0.082,0.082]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { loft: [[1.19,0.085,0.085],[1.26,0.085,0.085]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { loft: [[1.4,0.105,0.105],[1.47,0.105,0.105]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { loft: [[1.54,0.105,0.105],[1.61,0.105,0.105]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 5 Vex sheer */ [\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.64, th: 0.016, mat: 'glass' },\r\n    { loft: [[1.58,0.09,0.09],[1.42,0.125,0.125],[1.2,0.13,0.13],[1,0.125,0.125],[0.92,0.15,0.15]], cx: 0.235, seg: 12, caps: false, mat: 'glass', mirror: true, group: 'arm' },\r\n    { loft: [[0.92,0.15,0.15],[0.95,0.15,0.15]], cx: 0.235, seg: 12, caps: false, mat: 'pearl', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 6 Dravox panels */ [\r\n    { box: [0.285,1.34,-0.065,0.335,1.58,0.065], mat: 'metal', mirror: true, group: 'arm' },\r\n    { box: [0.2,1.36,0.06,0.27,1.56,0.09], mat: 'metal', mirror: true, group: 'arm' },\r\n    { box: [0.27,1,-0.055,0.315,1.25,0.055], mat: 'metal', mirror: true, group: 'arm' },\r\n    { loft: [[1.26,0.05,0.05],[1.31,0.085,0.085],[1.37,0.05,0.05]], cx: 0.26, seg: 8, mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'stud', at: [0.335,1.48,0], n: [1,0,0], mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'stud', at: [0.315,1.12,0], n: [1,0,0], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 7 Nymbi bells */ [\r\n    { loft: [[1.36,0.08,0.08],[1.46,0.125,0.125],[1.56,0.125,0.12],[1.64,0.08,0.075]], cx: 0.235, seg: 12, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[0.88,0.14,0.14],[0.95,0.11,0.11],[1.03,0.07,0.07],[1.1,0.06,0.06]], cx: 0.235, seg: 12, caps: false, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[1.3,0.08,0.08],[1.35,0.08,0.08]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 8 Xelith rings */ [\r\n    { loft: [[1.02,0.11,0.11],[1.06,0.11,0.11]], cx: 0.235, seg: 12, caps: false, mat: 'glow', mirror: true, group: 'arm' },\r\n    { loft: [[1.13,0.115,0.115],[1.17,0.115,0.115]], cx: 0.235, seg: 12, caps: false, mat: 'glow', mirror: true, group: 'arm' },\r\n    { loft: [[1.24,0.12,0.12],[1.28,0.12,0.12]], cx: 0.235, seg: 12, caps: false, mat: 'glow', mirror: true, group: 'arm' },\r\n    { loft: [[1.45,0.13,0.13],[1.49,0.13,0.13]], cx: 0.235, seg: 12, caps: false, mat: 'glow', mirror: true, group: 'arm' },\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.64, th: 0.006, mat: 'dark' }\r\n  ],\r\n  /* 9 Ulumi ribbons */ [\r\n    { tube: [[0.3,1.36,0.04,0.026],[0.33,1.2,0.064,0.028],[0.35,1,0.1,0.03],[0.34,0.8,0.076,0.03],[0.36,0.66,0.052,0.022]], sides: 6, mat: 'trim', mirror: true, group: 'arm' },\r\n    { tube: [[0.3,1.36,-0.04,0.026],[0.33,1.2,-0.064,0.028],[0.35,1,-0.1,0.03],[0.34,0.8,-0.076,0.03],[0.36,0.66,-0.052,0.022]], sides: 6, mat: 'trim', mirror: true, group: 'arm' },\r\n    { loft: [[1.28,0.075,0.075],[1.35,0.085,0.085],[1.4,0.075,0.075]], cx: 0.235, seg: 10, mat: 'accent', mirror: true, group: 'arm' },\r\n    { zone: 'sleeves', yLo: 0.99, yHi: 1.64, th: 0.008, mat: 'trim' }\r\n  ]\r\n];\r\n","pod/gala-rig/designs/back.mjs":"// Gala back designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela ribbons */ [\r\n    { tube: [[0.08, 1.55, -0.1, 0.02], [0.11, 1.2, -0.16, 0.022], [0.13, 0.85, -0.2, 0.02], [0.14, 0.6, -0.22, 0.012]], mirror: true, group: 'body', mat: 'trim' },\r\n  ],\r\n  /* 1 Orryx cape: long dye cape with gold collar band and clasps */ [\r\n    { drape: { kind: 'cape', hem: 0.36, wide: true }, mat: 'dye' },\r\n    { loft: [[1.60, 0.2, 0.15], [1.68, 0.17, 0.13]], group: 'body', mat: 'trim', seg: 12, caps: false },\r\n    { mark: 'stud', at: [0.1, 1.6, 0.14], group: 'body', mat: 'accent', mirror: true },\r\n  ],\r\n  /* 2 Quasar fins: big swept dorsal fins */ [\r\n    { fan: [[0.05, 1.45, -0.11], [0.09, 1.98, -0.2], [0.24, 1.9, -0.2], [0.36, 1.62, -0.16], [0.14, 1.3, -0.11]], mirror: true, group: 'body', mat: 'trim' },\r\n    { fan: [[0.05, 1.45, -0.11], [0.14, 1.3, -0.11], [0.36, 1.62, -0.16], [0.24, 1.9, -0.2], [0.09, 1.98, -0.2]], mirror: true, group: 'body', mat: 'trim' },\r\n    { fan: [[0.06, 1.5, -0.12], [0.1, 1.82, -0.18], [0.2, 1.72, -0.18], [0.12, 1.42, -0.12]], mirror: true, group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 3 Mollu spores: glowing spore pods on stalks arching behind the shoulders */ [\r\n    { tube: [[0.07,1.5,-0.09,0.028],[0.210,1.725,-0.15,0.024],[0.300,1.910,-0.12,0.02]], mirror: true, group: 'body', mat: 'accent' },\r\n    { loft: [[1.875,0.008,0.008,1,-0.120],[1.950,0.075,0.075,1,-0.120],[2.025,0.008,0.008,1,-0.120]], cx: 0.300, mirror: true, group: 'body', mat: 'accent', seg: 8 },\r\n    { tube: [[0.07,1.5,-0.09,0.028],[0.280,1.590,-0.15,0.024],[0.400,1.640,-0.12,0.02]], mirror: true, group: 'body', mat: 'accent' },\r\n    { loft: [[1.605,0.008,0.008,1,-0.120],[1.680,0.075,0.075,1,-0.120],[1.755,0.008,0.008,1,-0.120]], cx: 0.400, mirror: true, group: 'body', mat: 'accent', seg: 8 },\r\n    { tube: [[0.07,1.5,-0.09,0.028],[0.140,1.790,-0.15,0.024],[0.200,2.040,-0.12,0.02]], mirror: true, group: 'body', mat: 'accent' },\r\n    { loft: [[2.005,0.008,0.008,1,-0.120],[2.080,0.075,0.075,1,-0.120],[2.155,0.008,0.008,1,-0.120]], cx: 0.200, mirror: true, group: 'body', mat: 'accent', seg: 8 },\r\n  ],\r\n  /* 4 Krell spines: row of bone spines down the spine pointing back */ [\r\n    { tube: [[0,1.620,-0.1,0.032],[0,1.640,-0.200,0.02],[0,1.670,-0.300,0.006]], group: 'body', mat: 'ivory' },\r\n    { tube: [[0,1.500,-0.1,0.032],[0,1.520,-0.193,0.02],[0,1.550,-0.285,0.006]], group: 'body', mat: 'ivory' },\r\n    { tube: [[0,1.380,-0.1,0.032],[0,1.400,-0.185,0.02],[0,1.430,-0.270,0.006]], group: 'body', mat: 'ivory' },\r\n    { tube: [[0,1.260,-0.1,0.032],[0,1.280,-0.178,0.02],[0,1.310,-0.255,0.006]], group: 'body', mat: 'ivory' },\r\n    { tube: [[0,1.140,-0.1,0.032],[0,1.160,-0.170,0.02],[0,1.190,-0.240,0.006]], group: 'body', mat: 'ivory' },\r\n    { tube: [[0,1.020,-0.1,0.032],[0,1.040,-0.163,0.02],[0,1.070,-0.225,0.006]], group: 'body', mat: 'ivory' },\r\n  ],\r\n  /* 5 Nymbi wings: membrane wings */ [\r\n    { dfo: 'wings', mat: 'membrane', opts: { span: 0.85, at: 1.62, fold: 0.1, fingers: 4, droop: 0.42, rib: 0.02 } },\r\n  ],\r\n  /* 6 Vex sash: accent sash from right shoulder to left hip, front and back */ [\r\n    { fan: [[0.0, 1.35, 0.15], [0.19, 1.63, 0.125], [0.07, 1.64, 0.125], [-0.17, 1.02, 0.14], [-0.04, 1.0, 0.145]], group: 'body', mat: 'accent' },\r\n    { fan: [[0.0, 1.35, -0.14], [0.19, 1.63, -0.115], [-0.04, 1.0, -0.135], [-0.17, 1.02, -0.13], [0.07, 1.64, -0.115]], group: 'body', mat: 'accent' },\r\n    { mark: 'gem', at: [-0.12, 1.08, 0.15], group: 'body', mat: 'white' },\r\n  ],\r\n  /* 7 Dravox coils: two metal coils spiralling down the back, loops poke past the sides */ [\r\n    { tube: [[0.06, 1.68, -0.12, 0.03], [0.22, 1.56, -0.15, 0.03], [0.07, 1.44, -0.19, 0.03], [0.24, 1.32, -0.15, 0.03], [0.07, 1.20, -0.19, 0.03], [0.24, 1.08, -0.15, 0.03], [0.07, 0.96, -0.18, 0.03]], mirror: true, group: 'body', mat: 'metal' },\r\n    { mark: 'gem', at: [0.0, 1.5, -0.14], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 8 Xelith satellites: three accent orbs with rings floating round the head */ [\r\n    { loft: [[1.695,0.008,0.008,1,-0.030],[1.780,0.085,0.085,1,-0.030],[1.865,0.008,0.008,1,-0.030]], cx: -0.360, group: 'body', mat: 'accent', seg: 8 },\r\n    { loft: [[1.780,0.132,0.132,1,-0.030],[1.810,0.132,0.132,1,-0.030]], cx: -0.360, group: 'body', mat: 'trim', seg: 12, caps: false },\r\n    { loft: [[1.880,0.008,0.008,1,-0.050],[1.980,0.100,0.100,1,-0.050],[2.080,0.008,0.008,1,-0.050]], cx: 0.370, group: 'body', mat: 'accent', seg: 8 },\r\n    { loft: [[1.980,0.155,0.155,1,-0.050],[2.010,0.155,0.155,1,-0.050]], cx: 0.370, group: 'body', mat: 'trim', seg: 12, caps: false },\r\n    { loft: [[1.460,0.008,0.008,1,-0.100],[1.520,0.060,0.060,1,-0.100],[1.580,0.008,0.008,1,-0.100]], cx: 0.300, group: 'body', mat: 'accent', seg: 8 },\r\n    { loft: [[1.520,0.093,0.093,1,-0.100],[1.550,0.093,0.093,1,-0.100]], cx: 0.300, group: 'body', mat: 'trim', seg: 12, caps: false },\r\n  ],\r\n  /* 9 Ulumi starlight: a halo ring of glow stars behind the head and shoulders */ [\r\n    { loft: [[1.855,0.008,0.008,1,-0.100],[1.925,0.070,0.070,1,-0.100],[1.995,0.008,0.008,1,-0.100]], cx: 0.351, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[2.061,0.008,0.008,1,-0.100],[2.131,0.070,0.070,1,-0.100],[2.201,0.008,0.008,1,-0.100]], cx: 0.145, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[2.061,0.008,0.008,1,-0.100],[2.131,0.070,0.070,1,-0.100],[2.201,0.008,0.008,1,-0.100]], cx: -0.145, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[1.855,0.008,0.008,1,-0.100],[1.925,0.070,0.070,1,-0.100],[1.995,0.008,0.008,1,-0.100]], cx: -0.351, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[1.565,0.008,0.008,1,-0.100],[1.635,0.070,0.070,1,-0.100],[1.705,0.008,0.008,1,-0.100]], cx: -0.351, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[1.359,0.008,0.008,1,-0.100],[1.429,0.070,0.070,1,-0.100],[1.499,0.008,0.008,1,-0.100]], cx: -0.145, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[1.359,0.008,0.008,1,-0.100],[1.429,0.070,0.070,1,-0.100],[1.499,0.008,0.008,1,-0.100]], cx: 0.145, group: 'body', mat: 'glow', seg: 6 },\r\n    { loft: [[1.565,0.008,0.008,1,-0.100],[1.635,0.070,0.070,1,-0.100],[1.705,0.008,0.008,1,-0.100]], cx: 0.351, group: 'body', mat: 'glow', seg: 6 },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/crown.mjs":"// Gala crown designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 No crest */ [],\r\n  /* 1 Feelers */ [\r\n    { tube: [[0.05, 1.98, 0.07, 0.024], [0.07, 2.06, 0.1, 0.02], [0.09, 2.09, 0.18, 0.016]], mirror: true, group: 'head', mat: 'skinDark' },\r\n    { loft: [[2.075, 0.015, 0.015, 1, 0.190], [2.105, 0.030, 0.030, 1, 0.190], [2.135, 0.015, 0.015, 1, 0.190]], cx: 0.090, mirror: true, group: 'head', mat: 'skinDark', seg: 8 },\r\n  ],\r\n  /* 2 Crest fin */ [\r\n    { box: [-0.016, 2.0, 0.05, 0.016, 2.1, 0.13], group: 'head', mat: 'trim' },\r\n    { box: [-0.016, 2.0, -0.04, 0.016, 2.125, 0.05], group: 'head', mat: 'trim' },\r\n    { box: [-0.016, 1.96, -0.13, 0.016, 2.1, -0.04], group: 'head', mat: 'trim' },\r\n    { box: [-0.016, 1.86, -0.18, 0.016, 2.0, -0.13], group: 'head', mat: 'trim' },\r\n  ],\r\n  /* 3 Ram horns */ [\r\n    { tube: [[0.1, 1.97, -0.02, 0.04], [0.17, 2.05, -0.04, 0.034], [0.23, 2.0, -0.04, 0.03], [0.23, 1.9, -0.02, 0.024], [0.18, 1.85, 0.0, 0.014]], mirror: true, group: 'head', mat: 'ivory' },\r\n  ],\r\n  /* 4 Spike crown */ [\r\n    { tube: [[0.000, 2.0, 0.086, 0.026], [0.000, 2.115, 0.101, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[-0.059, 2.0, 0.048, 0.026], [-0.073, 2.115, 0.058, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[-0.073, 2.0, -0.036, 0.026], [-0.091, 2.115, -0.040, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[-0.033, 2.0, -0.104, 0.026], [-0.041, 2.115, -0.118, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[0.033, 2.0, -0.104, 0.026], [0.041, 2.115, -0.118, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[0.073, 2.0, -0.036, 0.026], [0.091, 2.115, -0.040, 0.006]], group: 'head', mat: 'skinDark' },\r\n    { tube: [[0.059, 2.0, 0.048, 0.026], [0.073, 2.115, 0.058, 0.006]], group: 'head', mat: 'skinDark' },\r\n  ],\r\n  /* 5 Neck frill */ [\r\n    { fan: [[0, 1.85, -0.13], [-0.1, 2.1, -0.17], [0.1, 2.1, -0.17], [0.21, 1.92, -0.22], [0.15, 1.7, -0.2], [-0.15, 1.7, -0.2], [-0.21, 1.92, -0.22]], group: 'head', mat: 'skin' },\r\n    { fan: [[0.09, 1.88, -0.08], [0.16, 2.1, -0.12], [0.27, 2.0, -0.2], [0.3, 1.82, -0.22], [0.2, 1.7, -0.14]], mirror: true, group: 'head', mat: 'skin' },\r\n    { fan: [[0.12, 1.85, -0.1], [0.2, 2.02, -0.14], [0.24, 1.84, -0.2]], mirror: true, group: 'head', mat: 'skinDark' },\r\n  ],\r\n  /* 6 Bulb antennae */ [\r\n    { tube: [[0.045, 1.98, 0.02, 0.018], [0.06, 2.04, 0.04, 0.016]], mirror: true, group: 'head', mat: 'skinDark' },\r\n    { loft: [[2.045, 0.020, 0.020, 1, 0.040], [2.085, 0.040, 0.040, 1, 0.040], [2.125, 0.020, 0.020, 1, 0.040]], cx: 0.060, mirror: true, group: 'head', mat: 'glow', seg: 10 },\r\n  ],\r\n  /* 7 Twin horns */ [\r\n    { tube: [[0.045, 1.96, 0.08, 0.04], [0.07, 2.04, 0.14, 0.03], [0.09, 2.115, 0.2, 0.01]], mirror: true, group: 'head', mat: 'dark' },\r\n  ],\r\n  /* 8 Ridge spines */ [\r\n    { box: [-0.014, 2.0, 0.03, 0.014, 2.1, 0.08], group: 'head', mat: 'skinDark' },\r\n    { box: [-0.014, 2.0, -0.04, 0.014, 2.125, 0.01], group: 'head', mat: 'skinDark' },\r\n    { box: [-0.014, 1.96, -0.12, 0.014, 2.09, -0.07], group: 'head', mat: 'skinDark' },\r\n    { box: [-0.014, 1.86, -0.19, 0.014, 1.99, -0.14], group: 'head', mat: 'skinDark' },\r\n    { box: [-0.014, 1.74, -0.21, 0.014, 1.85, -0.16], group: 'head', mat: 'skinDark' },\r\n  ],\r\n  /* 9 Halo sensors */ [\r\n    { loft: [[2.074, 0.013, 0.013, 1, 0.100], [2.100, 0.026, 0.026, 1, 0.100], [2.126, 0.013, 0.013, 1, 0.100]], cx: 0.000, group: 'head', mat: 'glow', seg: 8 },\r\n    { loft: [[2.074, 0.013, 0.013, 1, -0.110], [2.100, 0.026, 0.026, 1, -0.110], [2.126, 0.013, 0.013, 1, -0.110]], cx: 0.000, group: 'head', mat: 'glow', seg: 8 },\r\n    { loft: [[2.074, 0.013, 0.013, 1, -0.010], [2.100, 0.026, 0.026, 1, -0.010], [2.126, 0.013, 0.013, 1, -0.010]], cx: 0.100, group: 'head', mat: 'glow', seg: 8 },\r\n    { loft: [[2.074, 0.013, 0.013, 1, -0.010], [2.100, 0.026, 0.026, 1, -0.010], [2.126, 0.013, 0.013, 1, -0.010]], cx: -0.100, group: 'head', mat: 'glow', seg: 8 },\r\n    { tube: [[0.04, 1.99, 0.02, 0.018], [0.05, 2.04, 0.0, 0.014]], mirror: true, group: 'head', mat: 'glow' },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/facial.mjs":"// Gala facial designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela bare */ [],\r\n  /* 1 Zor whiskers */ [\r\n    { tube: [[0.07, 1.82, 0.105, 0.014], [0.118, 1.87, 0.1, 0.012], [0.178, 1.93, 0.07, 0.009]], mirror: true, group: 'head', mat: 'accent' },\r\n    { tube: [[0.075, 1.78, 0.105, 0.014], [0.13, 1.79, 0.1, 0.012], [0.19, 1.8, 0.07, 0.009]], mirror: true, group: 'head', mat: 'accent' },\r\n    { tube: [[0.07, 1.74, 0.1, 0.014], [0.118, 1.72, 0.095, 0.012], [0.178, 1.68, 0.07, 0.009]], mirror: true, group: 'head', mat: 'accent' },\r\n  ],\r\n  /* 2 Krell beard */ [\r\n    { loft: [[1.77, 0.1, 0.12, 1, -0.01], [1.7, 0.095, 0.125, 1, 0.0], [1.62, 0.07, 0.1, 1, 0.01], [1.54, 0.03, 0.05, 1, 0.03]], group: 'head', mat: 'hair', seg: 14, arc: [0.15, 2.99], caps: true },\r\n  ],\r\n  /* 3 Mollu frill */ [\r\n    { fan: [[0.1, 1.76, 0.0], [0.16, 1.93, -0.02], [0.18, 1.8, -0.03], [0.26, 1.88, -0.04], [0.23, 1.74, -0.04], [0.3, 1.74, -0.05], [0.22, 1.64, -0.04], [0.16, 1.62, -0.02]], mirror: true, group: 'head', mat: 'trim' },\r\n    { fan: [[0.1, 1.74, 0.0], [0.2, 1.7, -0.03], [0.17, 1.56, -0.03], [0.1, 1.6, -0.01]], mirror: true, group: 'head', mat: 'accent' },\r\n  ],\r\n  /* 4 Oracle dots */ [\r\n    { loft: [[1.9, 0.02, 0.02, 1, 0.125], [1.93, 0.03, 0.03, 1, 0.125], [1.96, 0.02, 0.02, 1, 0.125]], group: 'head', mat: 'accent', seg: 6 },\r\n    { loft: [[1.79, 0.018, 0.018, 1, 0.1], [1.82, 0.028, 0.028, 1, 0.1], [1.85, 0.018, 0.018, 1, 0.1]], cx: 0.085, mirror: true, group: 'head', mat: 'accent', seg: 6 },\r\n    { loft: [[1.74, 0.018, 0.018, 1, 0.1], [1.765, 0.026, 0.026, 1, 0.1], [1.79, 0.018, 0.018, 1, 0.1]], cx: 0.115, mirror: true, group: 'head', mat: 'accent', seg: 6 },\r\n    { mark: 'dot', at: [0.07, 1.74, 0.11], mirror: true, mat: 'accent' },\r\n    { mark: 'dot', at: [0, 1.715, 0.1], mat: 'accent' },\r\n  ],\r\n  /* 5 Quor tendrils */ [\r\n    { tube: [[0.04, 1.72, 0.08, 0.026], [0.06, 1.6, 0.1, 0.022], [0.08, 1.46, 0.09, 0.016]], mirror: true, group: 'head', mat: 'skinDark' },\r\n    { tube: [[0.09, 1.74, 0.04, 0.024], [0.14, 1.62, 0.05, 0.02], [0.17, 1.48, 0.04, 0.014]], mirror: true, group: 'head', mat: 'skinDark' },\r\n  ],\r\n  /* 6 Vex moustache */ [\r\n    { tube: [[0.005, 1.79, 0.125, 0.022], [0.06, 1.775, 0.125, 0.02], [0.13, 1.79, 0.1, 0.016], [0.17, 1.84, 0.07, 0.012]], mirror: true, group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 7 Dravox jaw */ [\r\n    { box: [-0.1, 1.655, 0.03, 0.1, 1.72, 0.125], group: 'head', mat: 'metal' },\r\n    { box: [0.092, 1.66, -0.07, 0.125, 1.8, 0.1], mirror: true, group: 'head', mat: 'metal' },\r\n    { mark: 'stud', at: [0.1, 1.74, 0.101], mirror: true, mat: 'accent' },\r\n    { mark: 'stud', at: [0.05, 1.685, 0.126], mirror: true, mat: 'accent' },\r\n    { mark: 'stud', at: [0, 1.685, 0.126], mat: 'accent' },\r\n  ],\r\n  /* 8 Nymbi veil */ [\r\n    { loft: [[1.82, 0.115, 0.16, 1, -0.015], [1.74, 0.11, 0.165, 1, -0.01], [1.65, 0.09, 0.14, 1, 0.0]], group: 'head', mat: 'pearl', seg: 16, arc: [0.25, 2.89], caps: false },\r\n    { loft: [[1.825, 0.117, 0.162, 1, -0.015], [1.835, 0.117, 0.162, 1, -0.015]], group: 'head', mat: 'accent', seg: 16, arc: [0.25, 2.89], caps: false },\r\n  ],\r\n  /* 9 Ulumi sparkle */ [\r\n    { loft: [[1.77, 0.012, 0.012, 1, 0.105], [1.8, 0.022, 0.022, 1, 0.105], [1.83, 0.012, 0.012, 1, 0.105]], cx: 0.095, mirror: true, group: 'head', mat: 'glow', seg: 6 },\r\n    { loft: [[1.89, 0.01, 0.01, 1, 0.125], [1.92, 0.018, 0.018, 1, 0.125], [1.95, 0.01, 0.01, 1, 0.125]], cx: 0.0, group: 'head', mat: 'glow', seg: 6 },\r\n    { mark: 'gem', at: [0.06, 1.9, 0.125], mirror: true, mat: 'glow' },\r\n    { mark: 'gem', at: [0.13, 1.74, 0.07], mirror: true, mat: 'glow' },\r\n  ],\r\n  ];\r\n","pod/gala-rig/designs/feet.mjs":"// Gala feet designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela slippers */ [\r\n    {zone:'feet',yLo:0,yHi:0.11,th:0.01,mat:'trim'},\r\n    {mirror:true,group:'leg',box:[0.06,0,0.17,0.104,0.06,0.24],mat:'accent'}\r\n  ],\r\n  /* 1 Krell platforms */ [\r\n    {zone:'feet',yLo:0,yHi:0.16,th:0.02,mat:'trim'},\r\n    {mirror:true,group:'leg',box:[0.025,0,-0.085,0.14,0.085,0.26],mat:'dark'},\r\n    {mirror:true,group:'leg',box:[0.025,0.06,-0.085,0.14,0.075,0.26],mat:'accent'},\r\n    {mirror:true,group:'leg',box:[0.05,0.085,0.14,0.115,0.13,0.24],mat:'trim'}\r\n  ],\r\n  /* 2 Orryx curltoes */ [\r\n    {zone:'feet',yLo:0,yHi:0.11,th:0.012,mat:'dye'},\r\n    {mirror:true,group:'leg',tube:[[0.082,0.05,0.2,0.03],[0.082,0.07,0.3,0.032],[0.082,0.17,0.36,0.03],[0.082,0.28,0.32,0.027],[0.082,0.3,0.24,0.024]],mat:'accent',sides:8},\r\n    {mirror:true,group:'leg',loft:[[0.28,0.05,0.05,1,0.235],[0.34,0.05,0.05,1,0.235]],mat:'gold',seg:8}\r\n  ],\r\n  /* 3 Quasar boots */ [\r\n    {zone:'feet',yLo:0,yHi:0.42,th:0.02,mat:'dye'},\r\n    {mirror:true,group:'leg',loft:[[0.38,0.118,0.105],[0.47,0.118,0.105]],cx:0.082,mat:'accent',seg:12,caps:false},\r\n    {mirror:true,group:'leg',box:[0.04,0,0.15,0.125,0.07,0.245],mat:'dark'},\r\n    {mirror:true,group:'leg',box:[0.04,0,-0.07,0.125,0.06,-0.03],mat:'dark'}\r\n  ],\r\n  /* 4 Mollu petals */ [\r\n    {zone:'feet',yLo:0,yHi:0.11,th:0.012,mat:'trim'},\r\n    {mirror:true,group:'leg',loft:[[0.09,0.05,0.02,1,0.1],[0.19,0.06,0.03,1,0.12],[0.3,0.03,0.02,1,0.14],[0.35,0.005,0.005,1,0.15]],cx:0.082,mat:'trim',seg:8},\r\n    {mirror:true,group:'leg',loft:[[0.09,0.02,0.04],[0.19,0.03,0.08],[0.3,0.02,0.05],[0.35,0.005,0.01]],cx:0.15,mat:'accent',seg:8},\r\n    {mirror:true,group:'leg',loft:[[0.09,0.05,0.02,1,-0.07],[0.19,0.06,0.03,1,-0.09],[0.3,0.03,0.02,1,-0.11],[0.35,0.005,0.005,1,-0.12]],cx:0.082,mat:'trim',seg:8},\r\n    {mirror:true,group:'leg',mark:'gem',at:[0.082,0.17,0.152],mat:'white'}\r\n  ],\r\n  /* 5 Vex heels */ [\r\n    {zone:'feet',yLo:0,yHi:0.12,th:0.012,mat:'dye'},\r\n    {mirror:true,group:'leg',box:[0.07,0,-0.07,0.1,0.12,-0.03],mat:'accent'},\r\n    {mirror:true,group:'leg',box:[0.045,0.1,-0.06,0.12,0.135,0],mat:'dye'},\r\n    {mirror:true,group:'leg',box:[0.05,0,0.19,0.115,0.09,0.245],mat:'trim'},\r\n    {mirror:true,group:'leg',loft:[[0.12,0.1,0.09],[0.15,0.1,0.09]],cx:0.082,mat:'accent',seg:10,caps:false}\r\n  ],\r\n  /* 6 Dravox greaves */ [\r\n    {zone:'feet',yLo:0,yHi:0.5,th:0.024,mat:'metal'},\r\n    {mirror:true,group:'leg',box:[0.04,0.45,0.04,0.125,0.6,0.14],mat:'metal'},\r\n    {mirror:true,group:'leg',box:[0.05,0,0.18,0.115,0.08,0.25],mat:'dark'},\r\n    {mirror:true,group:'leg',loft:[[0.18,0.115,0.1],[0.22,0.115,0.1]],cx:0.082,mat:'dark',seg:10,caps:false},\r\n    {mirror:true,group:'leg',mark:'gem',at:[0.082,0.52,0.145],mat:'accent'}\r\n  ],\r\n  /* 7 Nymbi clouds */ [\r\n    {mirror:true,group:'leg',loft:[[0,0.1,0.17],[0.05,0.13,0.2],[0.12,0.12,0.18],[0.19,0.09,0.13]],cx:0.082,mat:'white',seg:12},\r\n    {mirror:true,group:'leg',loft:[[0.1,0.12,0.08],[0.16,0.14,0.1],[0.25,0.1,0.08]],cx:0.082,mat:'white',seg:12},\r\n    {mirror:true,group:'leg',loft:[[0,0.06,0.04],[0.04,0.07,0.06]],cx:0.13,mat:'accent',seg:8}\r\n  ],\r\n  /* 8 Xelith skates */ [\r\n    {zone:'feet',yLo:0,yHi:0.2,th:0.012,mat:'dye'},\r\n    {mirror:true,group:'leg',box:[0.072,-0.045,-0.06,0.092,-0.005,0.25],mat:'glow'},\r\n    {mirror:true,group:'leg',box:[0.07,-0.005,-0.04,0.094,0.04,-0.01],mat:'metal'},\r\n    {mirror:true,group:'leg',box:[0.07,-0.005,0.17,0.094,0.04,0.2],mat:'metal'},\r\n    {mirror:true,group:'leg',loft:[[0.18,0.1,0.09],[0.22,0.1,0.09]],cx:0.082,mat:'trim',seg:10,caps:false}\r\n  ],\r\n  /* 9 Ulumi moonsteps */ [\r\n    {mirror:true,group:'leg',loft:[[0,0.07,0.12,1,0.08],[0.06,0.1,0.16,1,0.07],[0.15,0.1,0.14,1,0.05],[0.24,0.08,0.1,1,0]],cx:0.082,mat:'pearl',seg:14},\r\n    {mirror:true,group:'leg',loft:[[0,0.1,0.19,1,0.07],[0.035,0.105,0.2,1,0.07]],cx:0.082,mat:'glow',seg:14},\r\n    {mirror:true,group:'leg',loft:[[0.22,0.09,0.1],[0.26,0.09,0.1]],cx:0.082,mat:'accent',seg:12,caps:false}\r\n  ]\r\n];\r\n","pod/gala-rig/designs/hair.mjs":"// Gala hair designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vex wave */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { tube: [[-0.1, 1.99, 0.08, 0.035], [-0.03, 2.03, 0.115, 0.04], [0.06, 2.0, 0.11, 0.035], [0.11, 1.93, 0.07, 0.025]], group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 1 Quasar crest */ [\r\n    { dfo: 'hair:mohawk', mat: 'hair' },\r\n    { tube: [[0, 2.0, -0.02, 0.04], [0, 2.07, -0.10, 0.032], [0, 2.1, -0.19, 0.02]], group: 'head', mat: 'hair' },\r\n    { tube: [[0.05, 1.98, -0.07, 0.03], [0.1, 2.02, -0.15, 0.022], [0.14, 2.03, -0.22, 0.012]], mirror: true, group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 2 Nebula bob */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[1.66, 0.13, 0.15, 0.8, -0.04], [1.76, 0.15, 0.17, 0.8, -0.04], [1.9, 0.14, 0.17, 0.8, -0.03]], group: 'head', mat: 'hair', seg: 16, arc: [2.4, 6.9], caps: false },\r\n    { loft: [[1.93, 0.115, 0.15, 0.9, -0.01], [1.99, 0.115, 0.15, 0.9, -0.01]], group: 'head', mat: 'hair', seg: 16, arc: [0.5, 2.64], caps: false },\r\n  ],\r\n  /* 3 Orbital knots */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[1.98, 0.05, 0.05], [2.05, 0.075, 0.075], [2.12, 0.05, 0.05]], cx: 0.13, mirror: true, group: 'head', mat: 'hair', seg: 10 },\r\n    { loft: [[2.0, 0.085, 0.085], [2.015, 0.085, 0.085]], cx: 0.13, mirror: true, group: 'head', mat: 'accent', seg: 10, caps: false },\r\n  ],\r\n  /* 4 Plasma cascade */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[1.5, 0.1, 0.07, 0.8, -0.1], [1.7, 0.14, 0.12, 0.8, -0.07], [1.9, 0.14, 0.16, 0.8, -0.03]], group: 'head', mat: 'hair', seg: 14, arc: [3.3, 6.1], caps: false },\r\n    { tube: [[0.1, 1.95, 0.08, 0.04], [0.14, 1.8, 0.1, 0.045], [0.15, 1.6, 0.1, 0.045], [0.12, 1.4, 0.1, 0.04]], group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 5 Spore crown */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[2.0, 0.04, 0.04], [2.06, 0.06, 0.06], [2.12, 0.04, 0.04]], cx: 0.0, group: 'head', mat: 'hair', seg: 10 },\r\n    { loft: [[2.0, 0.04, 0.04], [2.06, 0.06, 0.06], [2.12, 0.04, 0.04]], cx: 0.09, mirror: true, group: 'head', mat: 'hair', seg: 10 },\r\n    { loft: [[1.98, 0.04, 0.04], [2.04, 0.055, 0.055], [2.1, 0.04, 0.04]], cx: 0.06, mirror: true, group: 'head', mat: 'hair', seg: 10 },\r\n    { mark: 'gem', at: [0, 2.12, 0], mat: 'accent' },\r\n    { mark: 'gem', at: [0.09, 2.12, 0], mirror: true, mat: 'accent' },\r\n  ],\r\n  /* 6 Void slick */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { tube: [[0, 2.02, 0.08, 0.04], [0, 2.07, -0.05, 0.045], [0, 2.02, -0.17, 0.04], [0.02, 1.9, -0.26, 0.032], [0.05, 1.78, -0.28, 0.022]], group: 'head', mat: 'hair' },\r\n    { tube: [[0.07, 1.98, 0.04, 0.03], [0.09, 1.97, -0.1, 0.03], [0.07, 1.86, -0.21, 0.022]], mirror: true, group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 7 Comet braid */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[1.84, 0.05, 0.05, 1, -0.15], [1.9, 0.06, 0.06, 1, -0.15], [1.96, 0.05, 0.05, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { loft: [[1.74, 0.045, 0.045, 1, -0.15], [1.8, 0.055, 0.055, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { loft: [[1.64, 0.045, 0.045, 1, -0.15], [1.7, 0.055, 0.055, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { loft: [[1.54, 0.045, 0.045, 1, -0.15], [1.6, 0.055, 0.055, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { loft: [[1.44, 0.04, 0.04, 1, -0.15], [1.5, 0.05, 0.05, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { loft: [[1.36, 0.04, 0.04, 1, -0.15], [1.4, 0.05, 0.05, 1, -0.15]], group: 'head', mat: 'hair', seg: 8 },\r\n    { mark: 'gem', at: [0, 1.33, -0.15], mat: 'accent' },\r\n  ],\r\n  /* 8 Prism spikes */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { tube: [[0, 2.0, 0, 0.03], [0, 2.18, 0, 0.004]], group: 'head', mat: 'hair' },\r\n    { tube: [[0.06, 1.99, 0.07, 0.025], [0.13, 2.14, 0.14, 0.004]], mirror: true, group: 'head', mat: 'hair' },\r\n    { tube: [[0.09, 1.99, -0.05, 0.025], [0.2, 2.12, -0.1, 0.004]], mirror: true, group: 'head', mat: 'hair' },\r\n    { tube: [[0.0, 1.99, -0.1, 0.025], [0.0, 2.1, -0.24, 0.004]], group: 'head', mat: 'hair' },\r\n  ],\r\n  /* 9 Lunar fringe */ [\r\n    { zone: 'head', yLo: 1.93, yHi: 2.3, th: 0.03, mat: 'hair' },\r\n    { zone: 'head', yLo: 1.76, yHi: 1.93, zHi: 0.03, th: 0.03, mat: 'hair' },\r\n    { loft: [[2.03, 0.05, 0.05], [2.1, 0.07, 0.07], [2.16, 0.04, 0.04]], group: 'head', mat: 'hair', seg: 10 },\r\n    { fan: [[0.12, 1.9, 0.07], [0.15, 1.9, -0.02], [0.17, 1.62, 0.0], [0.15, 1.58, 0.06], [0.13, 1.62, 0.1]], mirror: true, group: 'head', mat: 'hair' },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/hands.mjs":"// Gala hands designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela gloves */ [\r\n    { zone: 'hands', th: 0.008, mat: 'white' }\r\n  ],\r\n  /* 1 Krell talons */ [\r\n    { zone: 'hands', th: 0.01, mat: 'skinDark' },\r\n    { tube: [[0.2,0.84,0.04,0.03],[0.2,0.72,0.05,0.021],[0.2,0.64,0.06,0.008]], sides: 5, mat: 'ivory', mirror: true, group: 'arm' },\r\n    { tube: [[0.235,0.84,0.06,0.03],[0.235,0.708,0.07,0.021],[0.235,0.62,0.08,0.008]], sides: 5, mat: 'ivory', mirror: true, group: 'arm' },\r\n    { tube: [[0.27,0.84,0.04,0.03],[0.27,0.72,0.05,0.021],[0.27,0.64,0.06,0.008]], sides: 5, mat: 'ivory', mirror: true, group: 'arm' },\r\n    { loft: [[0.98,0.075,0.075],[1.02,0.075,0.075]], cx: 0.235, seg: 8, caps: false, mat: 'ivory', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 2 Orryx cuffs */ [\r\n    { zone: 'hands', th: 0.008, mat: 'white' },\r\n    { box: [0.15,0.93,-0.075,0.32,1.07,0.075], mat: 'accent', mirror: true, group: 'arm' },\r\n    { box: [0.145,1.05,-0.07,0.325,1.09,0.07], mat: 'trim', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 3 Quasar mesh */ [\r\n    { zone: 'hands', th: 0.014, mat: 'dark' },\r\n    { loft: [[0.93,0.068,0.068],[0.97,0.068,0.068]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'dot', at: [0.235,0.88,0.09], mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'dot', at: [0.235,0.83,0.09], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 4 Mollu mitts */ [\r\n    { zone: 'hands', th: 0.03, mat: 'trim' },\r\n    { loft: [[0.84,0.07,0.07],[0.9,0.09,0.085],[0.97,0.075,0.07]], cx: 0.235, seg: 10, mat: 'trim', mirror: true, group: 'arm' },\r\n    { mark: 'stud', at: [0.235,0.92,0.1], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 5 Vex rings */ [\r\n    { zone: 'hands', th: 0.008, mat: 'white' },\r\n    { loft: [[0.83,0.075,0.075],[0.87,0.075,0.075]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { loft: [[0.91,0.075,0.075],[0.95,0.075,0.075]], cx: 0.235, seg: 12, caps: false, mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'gem', at: [0.235,0.89,0.1], mat: 'accent', mirror: true, group: 'arm' },\r\n    { mark: 'gem', at: [0.31,0.9,0], n: [1,0,0], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 6 Dravox gauntlets */ [\r\n    { zone: 'hands', th: 0.024, mat: 'metal' },\r\n    { zone: 'sleeves', yLo: 0.98, yHi: 1.24, th: 0.024, mat: 'metal' },\r\n    { loft: [[0.96,0.1,0.1],[1.08,0.075,0.075],[1.2,0.07,0.07]], cx: 0.235, seg: 10, caps: false, mat: 'metal', mirror: true, group: 'arm' },\r\n    { box: [0.19,0.89,0.07,0.28,0.96,0.1], mat: 'accent', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 7 Nymbi ruffles */ [\r\n    { zone: 'hands', th: 0.008, mat: 'trim' },\r\n    { fan: [[0.285,1.02,0],[0.364,0.95,-0.055],[0.405,0.9,0],[0.364,0.95,0.055]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.27,1.02,0.035],[0.365,0.95,0.053],[0.355,0.9,0.12],[0.288,0.95,0.13]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.235,1.02,0.05],[0.29,0.95,0.129],[0.235,0.9,0.17],[0.18,0.95,0.129]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.2,1.02,0.035],[0.182,0.95,0.13],[0.115,0.9,0.12],[0.105,0.95,0.053]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.185,1.02,0],[0.106,0.95,0.055],[0.065,0.9,0],[0.106,0.95,-0.055]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.2,1.02,-0.035],[0.105,0.95,-0.053],[0.115,0.9,-0.12],[0.182,0.95,-0.13]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.235,1.02,-0.05],[0.18,0.95,-0.129],[0.235,0.9,-0.17],[0.29,0.95,-0.129]], mat: 'white', mirror: true, group: 'arm' },\r\n    { fan: [[0.27,1.02,-0.035],[0.288,0.95,-0.13],[0.355,0.9,-0.12],[0.365,0.95,-0.053]], mat: 'white', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 8 Xelith claws */ [\r\n    { zone: 'hands', th: 0.01, mat: 'skinDark' },\r\n    { tube: [[0.19,0.84,0.04,0.028],[0.19,0.696,0.05,0.02],[0.19,0.6,0.06,0.007]], sides: 5, mat: 'dark', mirror: true, group: 'arm' },\r\n    { tube: [[0.215,0.84,0.06,0.028],[0.215,0.684,0.07,0.02],[0.215,0.58,0.08,0.007]], sides: 5, mat: 'dark', mirror: true, group: 'arm' },\r\n    { tube: [[0.255,0.84,0.06,0.028],[0.255,0.684,0.07,0.02],[0.255,0.58,0.08,0.007]], sides: 5, mat: 'dark', mirror: true, group: 'arm' },\r\n    { tube: [[0.28,0.84,0.04,0.028],[0.28,0.696,0.05,0.02],[0.28,0.6,0.06,0.007]], sides: 5, mat: 'dark', mirror: true, group: 'arm' }\r\n  ],\r\n  /* 9 Ulumi glow */ [\r\n    { zone: 'hands', th: 0.012, mat: 'glow' },\r\n    { loft: [[0.97,0.07,0.07],[1,0.07,0.07]], cx: 0.235, seg: 12, caps: false, mat: 'white', mirror: true, group: 'arm' },\r\n    { loft: [[0.78,0.02,0.02],[0.82,0.045,0.045],[0.86,0.02,0.02]], cx: 0.235, seg: 6, mat: 'glow', mirror: true, group: 'arm' }\r\n  ]\r\n];\r\n","pod/gala-rig/designs/headwear.mjs":"// Gala headwear designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /*0 Vela circlet*/ [\r\n    { loft: [[1.905, 0.118, 0.15, 1, -0.014], [1.93, 0.118, 0.15, 1, -0.014]], group: 'head', mat: 'accent', seg: 16, caps: false },\r\n    { mark: 'gem', at: [0, 1.92, 0.138], mat: 'accent' },\r\n  ],\r\n  /*1 Orryx crown*/ [\r\n    { loft: [[1.96, 0.118, 0.15, 1, -0.014], [2.0, 0.118, 0.15, 1, -0.014]], group: 'head', mat: 'accent', seg: 16, caps: false },\r\n    { tube: [[0, 1.99, 0.135, 0.03], [0, 2.1, 0.135, 0.008]], group: 'head', mat: 'accent' },\r\n    { tube: [[0.11, 1.99, 0.0, 0.03], [0.115, 2.09, 0.0, 0.008]], mirror: true, group: 'head', mat: 'accent' },\r\n    { tube: [[0.065, 1.99, -0.13, 0.03], [0.065, 2.09, -0.13, 0.008]], mirror: true, group: 'head', mat: 'accent' },\r\n    { mark: 'gem', at: [0, 1.975, 0.14], mat: 'glow' },\r\n  ],\r\n  /*2 Quasar halo*/ [\r\n    { loft: [[2.075, 0.13, 0.13, 1, -0.01], [2.1, 0.135, 0.135, 1, -0.01], [2.125, 0.13, 0.13, 1, -0.01]], group: 'head', mat: 'glow', seg: 18, caps: false },\r\n    { loft: [[2.1, 0.085, 0.085, 1, -0.01], [2.11, 0.085, 0.085, 1, -0.01]], group: 'head', mat: 'glow', seg: 18, caps: false },\r\n  ],\r\n  /*3 Nymbi veil*/ [\r\n    { loft: [[2.07, 0.05, 0.06, 1, -0.02], [2.02, 0.11, 0.14, 1, -0.02], [1.86, 0.14, 0.17, 1, -0.02], [1.7, 0.12, 0.15, 1, -0.03], [1.6, 0.09, 0.11, 1, -0.04]], group: 'head', mat: 'pearl', seg: 16, arc: [2.75, 6.67], caps: false },\r\n    { loft: [[1.97, 0.116, 0.148, 1, -0.014], [1.99, 0.116, 0.148, 1, -0.014]], group: 'head', mat: 'accent', seg: 16, arc: [0.3, 2.84], caps: false },\r\n    { mark: 'gem', at: [0, 1.98, 0.14], mat: 'accent' },\r\n  ],\r\n  /*4 Krell cap*/ [\r\n    { loft: [[1.97, 0.1, 0.13, 1, -0.014], [1.99, 0.118, 0.15, 1, -0.014], [2.11, 0.118, 0.15, 1, -0.014], [2.12, 0.09, 0.12, 1, -0.014]], group: 'head', mat: 'dye', seg: 16 },\r\n    { loft: [[2.0, 0.123, 0.155, 1, -0.014], [2.035, 0.123, 0.155, 1, -0.014]], group: 'head', mat: 'accent', seg: 16, caps: false },\r\n  ],\r\n  /*5 Xelith horns*/ [\r\n    { tube: [[0.09, 1.94, 0.03, 0.035], [0.17, 1.98, 0.0, 0.03], [0.2, 2.06, -0.04, 0.022], [0.16, 2.12, -0.09, 0.01]], mirror: true, group: 'head', mat: 'ivory' },\r\n  ],\r\n  /*6 Mollu fascinator*/ [\r\n    { loft: [[1.99, 0.07, 0.07, 1, 0.03], [2.03, 0.075, 0.075, 1, 0.03]], cx: -0.09, group: 'head', mat: 'trim', seg: 14 },\r\n    { tube: [[-0.09, 2.03, 0.03, 0.014], [-0.14, 2.09, 0.03, 0.012], [-0.2, 2.1, 0.02, 0.008]], group: 'head', mat: 'accent' },\r\n    { tube: [[-0.09, 2.03, 0.03, 0.014], [-0.11, 2.1, 0.0, 0.012], [-0.15, 2.12, -0.04, 0.008]], group: 'head', mat: 'accent' },\r\n    { tube: [[-0.09, 2.03, 0.03, 0.014], [-0.16, 2.06, 0.07, 0.012], [-0.24, 2.04, 0.08, 0.008]], group: 'head', mat: 'trim' },\r\n    { mark: 'gem', at: [-0.09, 2.04, 0.04], mat: 'accent' },\r\n  ],\r\n  /*7 Dravox helm*/ [\r\n    { dfo: 'helm', mat: 'metal' },\r\n    { box: [-0.014, 2.0, -0.05, 0.014, 2.12, 0.06], group: 'head', mat: 'accent' },\r\n    { box: [-0.014, 2.0, -0.14, 0.014, 2.07, -0.05], group: 'head', mat: 'accent' },\r\n  ],\r\n  /*8 Orbital rings*/ [\r\n    { loft: [[2.03, 0.16, 0.18, 1, -0.014], [2.055, 0.16, 0.18, 1, -0.014]], group: 'head', mat: 'accent', seg: 18, caps: false },\r\n    { loft: [[1.86, 0.19, 0.21, 1, -0.014], [1.885, 0.19, 0.21, 1, -0.014]], group: 'head', mat: 'glow', seg: 18, caps: false },\r\n  ],\r\n  /*9 Comet antennae*/ [\r\n    { tube: [[0.05, 1.99, 0.02, 0.022], [0.09, 2.04, 0.0, 0.018], [0.14, 2.08, -0.03, 0.015]], mirror: true, group: 'head', mat: 'dye' },\r\n    { loft: [[2.06, 0.02, 0.02], [2.085, 0.035, 0.035], [2.11, 0.02, 0.02]], cx: 0.14, mirror: true, group: 'head', mat: 'glow', seg: 8 },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/index.mjs":"import armor from './armor.mjs';\r\nimport arms from './arms.mjs';\r\nimport back from './back.mjs';\r\nimport crown from './crown.mjs';\r\nimport facial from './facial.mjs';\r\nimport feet from './feet.mjs';\r\nimport hair from './hair.mjs';\r\nimport hands from './hands.mjs';\r\nimport headwear from './headwear.mjs';\r\nimport jaw from './jaw.mjs';\r\nimport legs from './legs.mjs';\r\nimport neck from './neck.mjs';\r\nimport pets from './pets.mjs';\r\nimport shield from './shield.mjs';\r\nimport shoulders from './shoulders.mjs';\r\nimport tail from './tail.mjs';\r\nimport torso from './torso.mjs';\r\nimport weapons from './weapons.mjs';\r\nexport default { armor, arms, back, crown, facial, feet, hair, hands, headwear, jaw, legs, neck, pets, shield, shoulders, tail, torso, weapons };\r\n","pod/gala-rig/designs/jaw.mjs":"// Gala jaw designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 no jaw */ [],\r\n  /* 1 mandibles */ [\r\n    { tube: [[0.09,1.76,0.03,0.03],[0.105,1.71,0.08,0.028],[0.08,1.67,0.14,0.024],[0.03,1.655,0.19,0.016]], group:'head', mat:'skinDark' },\r\n    { tube: [[-0.09,1.76,0.03,0.03],[-0.105,1.71,0.08,0.028],[-0.08,1.67,0.14,0.024],[-0.03,1.655,0.19,0.016]], group:'head', mat:'skinDark' },\r\n  ],\r\n  /* 2 tusks */ [\r\n    { dfo:'tusks', mat:'ivory', group:'head', opts:{ size:1.5, jaw:1.1 } },\r\n  ],\r\n  /* 3 snout */ [\r\n    { loft: [[1.67,0.06,0.07,1,0.13],[1.71,0.09,0.09,1,0.14],[1.76,0.085,0.085,1,0.15],[1.80,0.06,0.06,1,0.15]], group:'head', mat:'belly', seg:10 },\r\n    { mark:'dot', at:[0.03,1.765,0.24], mat:'dark', mirror:true, group:'head' },\r\n    { mark:'mouth', at:[0,1.70,0.225], mat:'dark', group:'head' },\r\n  ],\r\n  /* 4 beak */ [\r\n    { fan: [[0,1.725,0.32],[-0.07,1.80,0.09],[0.07,1.80,0.09]], group:'head', mat:'accent' },\r\n    { fan: [[0,1.725,0.32],[0.07,1.80,0.09],[0.07,1.65,0.09]], group:'head', mat:'accent' },\r\n    { fan: [[0,1.725,0.32],[0.07,1.65,0.09],[-0.07,1.65,0.09]], group:'head', mat:'accent' },\r\n    { fan: [[0,1.725,0.32],[-0.07,1.65,0.09],[-0.07,1.80,0.09]], group:'head', mat:'accent' },\r\n  ],\r\n  /* 5 tendril beard */ [\r\n    { tube: [[-0.05,1.70,0.07,0.02],[-0.065,1.62,0.08,0.013],[-0.06,1.55,0.085,0.01]], group:'head', mat:'skinDark' },\r\n    { tube: [[-0.025,1.69,0.09,0.014],[-0.03,1.60,0.105,0.013],[-0.025,1.50,0.11,0.01]], group:'head', mat:'skinDark' },\r\n    { tube: [[0,1.69,0.095,0.014],[0,1.58,0.11,0.013],[0,1.46,0.115,0.01]], group:'head', mat:'skinDark' },\r\n    { tube: [[0.025,1.69,0.09,0.014],[0.03,1.60,0.105,0.013],[0.025,1.50,0.11,0.01]], group:'head', mat:'skinDark' },\r\n    { tube: [[0.05,1.70,0.07,0.014],[0.065,1.62,0.08,0.013],[0.06,1.55,0.085,0.01]], group:'head', mat:'skinDark' },\r\n  ],\r\n  /* 6 fangs */ [\r\n    { tube: [[0.03,1.78,0.115,0.024],[0.03,1.70,0.13,0.008]], mirror:true, group:'head', mat:'white' },\r\n  ],\r\n  /* 7 mouth plates */ [\r\n    { box:[0.075,1.69,-0.02,0.12,1.80,0.10], mirror:true, group:'head', mat:'metal' },\r\n    { box:[-0.05,1.665,0.07,0.05,1.70,0.115], group:'head', mat:'metal' },\r\n  ],\r\n  /* 8 gill frills */ [\r\n    { fan: [[0.08,1.76,0.0],[0.20,1.84,-0.04],[0.19,1.78,-0.06],[0.10,1.72,-0.03]], mirror:true, group:'head', mat:'trim' },\r\n    { fan: [[0.08,1.74,0.0],[0.20,1.76,-0.04],[0.19,1.70,-0.06],[0.10,1.68,-0.03]], mirror:true, group:'head', mat:'accent' },\r\n    { fan: [[0.08,1.72,0.0],[0.19,1.68,-0.04],[0.17,1.62,-0.05],[0.10,1.66,-0.03]], mirror:true, group:'head', mat:'trim' },\r\n  ],\r\n  /* 9 chin horn */ [\r\n    { tube: [[0,1.73,0.07,0.045],[0,1.67,0.14,0.034],[0,1.60,0.2,0.014]], group:'head', mat:'ivory' },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/legs.mjs":"// Gala legs designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vex trousers */ [\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.01,mat:'dye'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'}\r\n  ],\r\n  /* 1 Mollu bell skirt */ [\r\n    {drape:{kind:'flare',top:0.98,hem:0.45,flare:[0.175,0.115,0.34,0.24]},mat:'dye'},\r\n    {loft:[[0.45,0.345,0.245],[0.5,0.33,0.235]],group:'body',mat:'trim',seg:20,caps:false},\r\n    {loft:[[0.98,0.185,0.125],[1.04,0.18,0.12]],group:'body',mat:'trim',seg:16,caps:false},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'}\r\n  ],\r\n  /* 2 Orryx pleats */ [\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,flare:[0.18,0.12,0.32,0.22]},mat:'dye'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[0.5707949999999999,0.7707949999999999],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[0.870795,1.070795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[1.1707949999999998,1.370795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[1.4707949999999999,1.670795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[1.770795,1.970795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[2.070795,2.270795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.5,arc:[2.3707949999999998,2.570795],seg:3,flare:[0.186,0.126,0.335,0.235]},mat:'trim'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'trim'}\r\n  ],\r\n  /* 3 Quasar split */ [\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.01,mat:'dye'},\r\n    {zone:'legs',yLo:0.42,yHi:0.78,th:0,mat:'skin'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'},\r\n    {loft:[[0.38,0.105,0.095],[0.42,0.105,0.095]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {loft:[[0.78,0.14,0.12],[0.8200000000000001,0.14,0.12]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false}\r\n  ],\r\n  /* 4 Nymbi bubble */ [\r\n    {loft:[[0.98,0.18,0.12],[0.88,0.26,0.18],[0.74,0.31,0.22],[0.62,0.29,0.2],[0.54,0.21,0.15]],group:'body',mat:'trim',seg:20},\r\n    {loft:[[0.54,0.22,0.16],[0.6,0.22,0.16]],group:'body',mat:'dye',seg:20,caps:false},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'trim'}\r\n  ],\r\n  /* 5 Krell drape */ [\r\n    {drape:{kind:'flare',top:1,hem:0.12,arc:[1.9707949999999999,7.453975],flare:[0.18,0.12,0.28,0.2]},mat:'dye'},\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.008,mat:'dark'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dark'},\r\n    {drape:{kind:'flare',top:1,hem:0.12,arc:[1.9707949999999999,2.120795],seg:2,flare:[0.185,0.125,0.285,0.205]},mat:'trim'},\r\n    {drape:{kind:'flare',top:1,hem:0.12,arc:[7.303974999999999,7.453975],seg:2,flare:[0.185,0.125,0.285,0.205]},mat:'trim'}\r\n  ],\r\n  /* 6 Xelith stripes */ [\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.01,mat:'dye'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'},\r\n    {loft:[[0.17,0.1,0.085],[0.21000000000000002,0.1,0.085]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {loft:[[0.3,0.1,0.09],[0.33999999999999997,0.1,0.09]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {loft:[[0.43,0.105,0.095],[0.47,0.105,0.095]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {loft:[[0.56,0.115,0.105],[0.6000000000000001,0.115,0.105]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false},\r\n    {loft:[[0.69,0.125,0.115],[0.73,0.125,0.115]],cx:0.082,mirror:true,group:'leg',mat:'accent',seg:12,caps:false}\r\n  ],\r\n  /* 7 Dravox breeches */ [\r\n    {loft:[[0.48,0.1,0.09],[0.58,0.15,0.125],[0.78,0.175,0.14],[0.96,0.155,0.12]],cx:0.082,mirror:true,group:'leg',mat:'dye',seg:14},\r\n    {zone:'legs',yLo:0.1,yHi:0.5,th:0.02,mat:'dark'},\r\n    {loft:[[0.46,0.115,0.1],[0.5,0.115,0.1]],cx:0.082,mirror:true,group:'leg',mat:'trim',seg:12,caps:false},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'}\r\n  ],\r\n  /* 8 Ulumi train */ [\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.01,mat:'dye'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dye'},\r\n    {drape:{kind:'flare',top:0.98,hem:0.03,arc:[3.49159,5.93318],flare:[0.18,0.12,0.3,0.5]},mat:'trim'},\r\n    {loft:[[1,0.18,0.12],[1.05,0.18,0.12]],group:'body',mat:'accent',seg:16,caps:false}\r\n  ],\r\n  /* 9 Zor shimmer */ [\r\n    {zone:'legs',yLo:0.12,yHi:1,th:0.01,mat:'dark'},\r\n    {zone:'pelvis',yLo:0.9,yHi:1.12,th:0.012,mat:'dark'},\r\n    {mark:'gem',at:[0.07,0.25,0.1],mirror:true,group:'leg',mat:'accent'},\r\n    {mark:'gem',at:[0.1,0.45,0.11],mirror:true,group:'leg',mat:'accent'},\r\n    {mark:'gem',at:[0.06,0.62,0.115],mirror:true,group:'leg',mat:'accent'},\r\n    {mark:'gem',at:[0.1,0.8,0.13],mirror:true,group:'leg',mat:'accent'},\r\n    {mark:'gem',at:[0.05,0.93,0.13],mirror:true,group:'leg',mat:'accent'},\r\n    {mark:'dot',at:[0.12,0.35,0.1],mirror:true,group:'leg',mat:'white'},\r\n    {mark:'dot',at:[0.04,0.53,0.11],mirror:true,group:'leg',mat:'white'},\r\n    {mark:'dot',at:[0.13,0.7,0.12],mirror:true,group:'leg',mat:'white'}\r\n  ]\r\n];\r\n","pod/gala-rig/designs/neck.mjs":"// Gala neck designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Zor cravat */ [\r\n    { loft: [[1.62, 0.04, 0.03], [1.66, 0.05, 0.04], [1.70, 0.04, 0.03]], group: 'body', mat: 'white', seg: 8 },\r\n    { fan: [[0, 1.6, 0.11], [-0.03, 1.45, 0.115], [0.0, 1.40, 0.118], [0.03, 1.45, 0.115]], mat: 'white' },\r\n  ],\r\n  /* 1 Vela bow */ [\r\n    { fan: [[0, 1.57, 0.14], [0.13, 1.66, 0.13], [0.13, 1.48, 0.13]], group: 'body', mat: 'accent' },\r\n    { fan: [[0, 1.57, 0.14], [-0.13, 1.48, 0.13], [-0.13, 1.66, 0.13]], group: 'body', mat: 'accent' },\r\n    { box: [-0.03, 1.54, 0.12, 0.03, 1.60, 0.16], group: 'body', mat: 'white' },\r\n  ],\r\n  /* 2 Orryx pearls */ [\r\n    { loft: [[1.622,0.012,0.012,1,0.125],[1.640,0.026,0.026,1,0.125],[1.658,0.012,0.012,1,0.125]], cx:-0.120, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.578,0.012,0.012,1,0.129],[1.596,0.026,0.026,1,0.129],[1.614,0.012,0.012,1,0.129]], cx:-0.090, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.547,0.012,0.012,1,0.133],[1.565,0.026,0.026,1,0.133],[1.583,0.012,0.012,1,0.133]], cx:-0.060, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.528,0.012,0.012,1,0.136],[1.546,0.026,0.026,1,0.136],[1.564,0.012,0.012,1,0.136]], cx:-0.030, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.522,0.012,0.012,1,0.140],[1.540,0.026,0.026,1,0.140],[1.558,0.012,0.012,1,0.140]], cx:0.000, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.528,0.012,0.012,1,0.136],[1.546,0.026,0.026,1,0.136],[1.564,0.012,0.012,1,0.136]], cx:0.030, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.547,0.012,0.012,1,0.133],[1.565,0.026,0.026,1,0.133],[1.583,0.012,0.012,1,0.133]], cx:0.060, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.578,0.012,0.012,1,0.129],[1.596,0.026,0.026,1,0.129],[1.614,0.012,0.012,1,0.129]], cx:0.090, group:'body', mat:'pearl', seg:6 },\r\n    { loft: [[1.622,0.012,0.012,1,0.125],[1.640,0.026,0.026,1,0.125],[1.658,0.012,0.012,1,0.125]], cx:0.120, group:'body', mat:'pearl', seg:6 },\r\n  ],\r\n  /* 3 Quasar ruff */ [\r\n    { loft: [[1.52, 0.15, 0.13], [1.58, 0.25, 0.19], [1.63, 0.24, 0.18], [1.69, 0.12, 0.10]], group: 'body', mat: 'white', seg: 10 },\r\n    { loft: [[1.575, 0.245, 0.188], [1.59, 0.26, 0.2]], group: 'body', mat: 'trim', seg: 10, caps: false },\r\n  ],\r\n  /* 4 Mollu scarf */ [\r\n    { loft: [[1.52, 0.17, 0.15], [1.58, 0.19, 0.16], [1.65, 0.14, 0.12]], group: 'body', mat: 'trim', seg: 10 },\r\n    { fan: [[0.0, 1.45, 0.15], [-0.06, 1.58, 0.16], [0.06, 1.58, 0.16], [0.065, 1.32, 0.15], [-0.04, 1.30, 0.15]], group: 'body', mat: 'trim' },\r\n    { fan: [[0.09, 1.48, 0.16], [0.05, 1.57, 0.165], [0.13, 1.57, 0.165], [0.14, 1.36, 0.155], [0.07, 1.34, 0.155]], group: 'body', mat: 'white' },\r\n  ],\r\n  /* 5 Krell collar */ [\r\n    { loft: [[1.55, 0.17, 0.15], [1.66, 0.19, 0.16], [1.78, 0.23, 0.19]], group: 'body', mat: 'white', seg: 14, arc: [2.17, 7.25], caps: false },\r\n    { loft: [[1.755, 0.225, 0.185], [1.785, 0.235, 0.195]], group: 'body', mat: 'trim', seg: 14, arc: [2.17, 7.25], caps: false },\r\n  ],\r\n  /* 6 Xelith pendant */ [\r\n    { tube: [[-0.09, 1.66, 0.12, 0.014], [-0.05, 1.56, 0.14, 0.014], [0, 1.51, 0.15, 0.014], [0.05, 1.56, 0.14, 0.014], [0.09, 1.66, 0.12, 0.014]], group: 'body', mat: 'accent' },\r\n    { loft: [[1.38, 0.006, 0.006, 1, 0.15], [1.44, 0.045, 0.04, 1, 0.15], [1.50, 0.006, 0.006, 1, 0.15]], group: 'body', mat: 'accent', seg: 8 },\r\n    { mark: 'gem', at: [0, 1.44, 0.2], group: 'body', mat: 'glow' },\r\n  ],\r\n  /* 7 Nymbi ribbon */ [\r\n    { loft: [[1.56, 0.16, 0.14], [1.64, 0.16, 0.14]], group: 'body', mat: 'trim', seg: 10, caps: false },\r\n    { fan: [[0, 1.57, 0.15], [-0.11, 1.64, 0.14], [-0.11, 1.50, 0.14]], group: 'body', mat: 'trim' },\r\n    { fan: [[0, 1.57, 0.15], [0.11, 1.50, 0.14], [0.11, 1.64, 0.14]], group: 'body', mat: 'trim' },\r\n    { fan: [[0.0, 1.55, 0.15], [-0.03, 1.58, 0.15], [-0.08, 1.34, 0.14], [-0.02, 1.33, 0.14]], group: 'body', mat: 'trim' },\r\n    { fan: [[0.0, 1.55, 0.15], [0.03, 1.58, 0.15], [0.02, 1.33, 0.14], [0.08, 1.34, 0.14]], group: 'body', mat: 'trim' },\r\n  ],\r\n  /* 8 Dravox chain */ [\r\n    { box: [-0.184,1.584,0.115,-0.116,1.656,0.175], group:'body', mat:'metal' },\r\n    { box: [-0.134,1.517,0.115,-0.066,1.589,0.175], group:'body', mat:'gold' },\r\n    { box: [-0.084,1.477,0.115,-0.016,1.549,0.175], group:'body', mat:'metal' },\r\n    { box: [-0.034,1.464,0.115,0.034,1.536,0.175], group:'body', mat:'gold' },\r\n    { box: [0.016,1.477,0.115,0.084,1.549,0.175], group:'body', mat:'metal' },\r\n    { box: [0.066,1.517,0.115,0.134,1.589,0.175], group:'body', mat:'gold' },\r\n    { box: [0.116,1.584,0.115,0.184,1.656,0.175], group:'body', mat:'metal' },\r\n    { mark: 'gem', at: [0, 1.46, 0.18], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 9 Ulumi choker */ [\r\n    { loft: [[1.53, 0.15, 0.13], [1.64, 0.15, 0.13]], group: 'body', mat: 'dark', seg: 12, caps: false },\r\n    { loft: [[1.50, 0.006, 0.006, 1, 0.14], [1.56, 0.07, 0.055, 1, 0.14], [1.62, 0.006, 0.006, 1, 0.14]], group: 'body', mat: 'glow', seg: 8 },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/pets.mjs":"// Gala pets designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [ { \"top\": 0.14, \"head\": [ 0, 0.1, 0.12 ], \"pieces\": [ { \"tube\": [ [ 0, 0.07, -0.12, 0.06 ], [ 0, 0.08, 0, 0.075 ], [ 0, 0.09, 0.1, 0.06 ] ], \"mat\": \"fur\", \"sides\": 8 }, { \"tube\": [ [ 0.03, 0.14, 0.12, 0.014 ], [ 0.04, 0.19, 0.13, 0.016 ] ], \"mirror\": true, \"mat\": \"belly\", \"sides\": 5 }, { \"loft\": [ [ 0.168, 0.012, 0.012, 1, 0.13 ], [ 0.1724, 0.0132, 0.0132, 1, 0.13 ], [ 0.1812, 0.0202, 0.0202, 1, 0.13 ], [ 0.19, 0.022, 0.022, 1, 0.13 ], [ 0.1988, 0.0202, 0.0202, 1, 0.13 ], [ 0.2076, 0.0132, 0.0132, 1, 0.13 ], [ 0.212, 0.012, 0.012, 1, 0.13 ] ], \"cx\": 0.04, \"mat\": \"white\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.4, \"head\": [ 0, 0.33, 0.14 ], \"pieces\": [ { \"tube\": [ [ 0, 0.17, -0.12, 0.085 ], [ 0, 0.18, 0, 0.1 ], [ 0, 0.19, 0.08, 0.09 ] ], \"mat\": \"fur\", \"sides\": 8 }, { \"tube\": [ [ 0.06, 0.11, 0.07, 0.032 ], [ 0.06, 0.01, 0.08, 0.0352 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"tube\": [ [ 0.06, 0.11, -0.1, 0.032 ], [ 0.06, 0.01, -0.09, 0.0352 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"loft\": [ [ 0.23, 0.012, 0.012, 1, 0.15 ], [ 0.248, 0.06, 0.057, 1, 0.15 ], [ 0.284, 0.092, 0.0874, 1, 0.15 ], [ 0.32, 0.1, 0.095, 1, 0.15 ], [ 0.356, 0.092, 0.0874, 1, 0.15 ], [ 0.392, 0.06, 0.057, 1, 0.15 ], [ 0.41, 0.012, 0.012, 1, 0.15 ] ], \"cx\": 0, \"mat\": \"fur\", \"seg\": 10 }, { \"loft\": [ [ 0.245, 0.012, 0.012, 1, 0.24 ], [ 0.252, 0.027, 0.03, 1, 0.24 ], [ 0.266, 0.0414, 0.046, 1, 0.24 ], [ 0.28, 0.045, 0.05, 1, 0.24 ], [ 0.294, 0.0414, 0.046, 1, 0.24 ], [ 0.308, 0.027, 0.03, 1, 0.24 ], [ 0.315, 0.012, 0.012, 1, 0.24 ] ], \"cx\": 0, \"mat\": \"belly\", \"seg\": 10 }, { \"loft\": [ [ 0.28, 0.012, 0.012, 1, 0.285 ], [ 0.283, 0.012, 0.012, 1, 0.285 ], [ 0.289, 0.0184, 0.0138, 1, 0.285 ], [ 0.295, 0.02, 0.015, 1, 0.285 ], [ 0.301, 0.0184, 0.0138, 1, 0.285 ], [ 0.307, 0.012, 0.012, 1, 0.285 ], [ 0.31, 0.012, 0.012, 1, 0.285 ] ], \"cx\": 0, \"mat\": \"dark\", \"seg\": 6 }, { \"tube\": [ [ 0.08, 0.38, 0.12, 0.03 ], [ 0.12, 0.3, 0.12, 0.035 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"tube\": [ [ 0, 0.22, -0.2, 0.03 ], [ 0, 0.31, -0.27, 0.025 ] ], \"mat\": \"furDark\", \"sides\": 5 }, { \"loft\": [ [ 0.311, 0.012, 0.012, 1, 0.208 ], [ 0.3158, 0.0144, 0.0144, 1, 0.208 ], [ 0.3254, 0.0221, 0.0221, 1, 0.208 ], [ 0.335, 0.024, 0.024, 1, 0.208 ], [ 0.3446, 0.0221, 0.0221, 1, 0.208 ], [ 0.3542, 0.0144, 0.0144, 1, 0.208 ], [ 0.359, 0.012, 0.012, 1, 0.208 ] ], \"cx\": 0.075, \"mat\": \"dark\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.42, \"head\": [ 0, 0.3, 0.1 ], \"pieces\": [ { \"loft\": [ [ 0.09, 0.012, 0.012, 1, -0.02 ], [ 0.112, 0.042, 0.042, 1, -0.02 ], [ 0.156, 0.0644, 0.0644, 1, -0.02 ], [ 0.2, 0.07, 0.07, 1, -0.02 ], [ 0.244, 0.0644, 0.0644, 1, -0.02 ], [ 0.288, 0.042, 0.042, 1, -0.02 ], [ 0.31, 0.012, 0.012, 1, -0.02 ] ], \"cx\": 0, \"mat\": \"fur\", \"seg\": 10 }, { \"loft\": [ [ 0.26, 0.012, 0.012, 1, 0.1 ], [ 0.272, 0.039, 0.036, 1, 0.1 ], [ 0.296, 0.0598, 0.0552, 1, 0.1 ], [ 0.32, 0.065, 0.06, 1, 0.1 ], [ 0.344, 0.0598, 0.0552, 1, 0.1 ], [ 0.368, 0.039, 0.036, 1, 0.1 ], [ 0.38, 0.012, 0.012, 1, 0.1 ] ], \"cx\": 0, \"mat\": \"belly\", \"seg\": 10 }, { \"fan\": [ [ 0.05, 0.28, -0.02 ], [ 0.24, 0.46, -0.06 ], [ 0.3, 0.3, -0.1 ], [ 0.24, 0.12, -0.08 ], [ 0.05, 0.15, -0.02 ] ], \"mirror\": true, \"mat\": \"furDark\" }, { \"fan\": [ [ 0.05, 0.26, -0.03 ], [ 0.17, 0.4, -0.05 ], [ 0.2, 0.28, -0.06 ], [ 0.15, 0.16, -0.06 ] ], \"mirror\": true, \"mat\": \"fur\" }, { \"tube\": [ [ 0.025, 0.36, 0.13, 0.014 ], [ 0.07, 0.46, 0.1, 0.012 ] ], \"mirror\": true, \"mat\": \"belly\", \"sides\": 5 }, { \"tube\": [ [ 0.04, 0.1, 0.04, 0.016 ], [ 0.06, 0.01, 0.07, 0.016 ] ], \"mirror\": true, \"mat\": \"dark\", \"sides\": 4 }, { \"tube\": [ [ 0.04, 0.1, -0.03, 0.016 ], [ 0.06, 0.01, 0, 0.016 ] ], \"mirror\": true, \"mat\": \"dark\", \"sides\": 4 }, { \"loft\": [ [ 0.303, 0.012, 0.012, 1, 0.138 ], [ 0.3074, 0.0132, 0.0132, 1, 0.138 ], [ 0.3162, 0.0202, 0.0202, 1, 0.138 ], [ 0.325, 0.022, 0.022, 1, 0.138 ], [ 0.3338, 0.0202, 0.0202, 1, 0.138 ], [ 0.3426, 0.0132, 0.0132, 1, 0.138 ], [ 0.347, 0.012, 0.012, 1, 0.138 ] ], \"cx\": 0.045, \"mat\": \"glow\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.3, \"head\": [ 0, 0.25, 0.1 ], \"pieces\": [ { \"loft\": [ [ 0.05, 0.02, 0.02, 1, -0.02 ], [ 0.1, 0.125, 0.15, 1, -0.02 ], [ 0.18, 0.15, 0.17, 1, -0.02 ], [ 0.25, 0.1, 0.12, 1, -0.02 ], [ 0.3, 0.03, 0.04, 1, -0.02 ] ], \"mat\": \"fur\", \"seg\": 12 }, { \"loft\": [ [ 0.065, 0.012, 0.012, 1, 0.17 ], [ 0.076, 0.042, 0.036, 1, 0.17 ], [ 0.098, 0.0644, 0.0552, 1, 0.17 ], [ 0.12, 0.07, 0.06, 1, 0.17 ], [ 0.142, 0.0644, 0.0552, 1, 0.17 ], [ 0.164, 0.042, 0.036, 1, 0.17 ], [ 0.175, 0.012, 0.012, 1, 0.17 ] ], \"cx\": 0, \"mat\": \"furDark\", \"seg\": 10 }, { \"tube\": [ [ 0, 0.17, 0.2, 0.02 ], [ 0, 0.26, 0.25, 0.015 ] ], \"mat\": \"ivory\", \"sides\": 5 }, { \"tube\": [ [ 0.1, 0.1, 0.13, 0.017 ], [ 0.15, 0.06, 0.15, 0.017 ], [ 0.16, 0.01, 0.16, 0.02 ] ], \"mirror\": true, \"mat\": \"dark\", \"sides\": 4 }, { \"tube\": [ [ 0.1, 0.1, 0.03, 0.017 ], [ 0.15, 0.06, 0.05, 0.017 ], [ 0.16, 0.01, 0.06, 0.02 ] ], \"mirror\": true, \"mat\": \"dark\", \"sides\": 4 }, { \"tube\": [ [ 0.1, 0.1, -0.08, 0.017 ], [ 0.15, 0.06, -0.06, 0.017 ], [ 0.16, 0.01, -0.05, 0.02 ] ], \"mirror\": true, \"mat\": \"dark\", \"sides\": 4 }, { \"loft\": [ [ 0.108, 0.012, 0.012, 1, 0.207 ], [ 0.1124, 0.0132, 0.0132, 1, 0.207 ], [ 0.1212, 0.0202, 0.0202, 1, 0.207 ], [ 0.13, 0.022, 0.022, 1, 0.207 ], [ 0.1388, 0.0202, 0.0202, 1, 0.207 ], [ 0.1476, 0.0132, 0.0132, 1, 0.207 ], [ 0.152, 0.012, 0.012, 1, 0.207 ] ], \"cx\": 0.05, \"mat\": \"glow\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.42, \"head\": [ 0, 0.33, 0.14 ], \"pieces\": [ { \"tube\": [ [ 0, 0.19, -0.14, 0.07 ], [ 0, 0.2, 0, 0.08 ], [ 0, 0.2, 0.09, 0.07 ] ], \"mat\": \"fur\", \"sides\": 8 }, { \"tube\": [ [ 0.045, 0.13, 0.07, 0.026 ], [ 0.045, 0.01, 0.08, 0.0286 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"tube\": [ [ 0.045, 0.13, -0.11, 0.026 ], [ 0.045, 0.01, -0.1, 0.0286 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"loft\": [ [ 0.235, 0.012, 0.012, 1, 0.15 ], [ 0.25, 0.051, 0.048, 1, 0.15 ], [ 0.28, 0.0782, 0.0736, 1, 0.15 ], [ 0.31, 0.085, 0.08, 1, 0.15 ], [ 0.34, 0.0782, 0.0736, 1, 0.15 ], [ 0.37, 0.051, 0.048, 1, 0.15 ], [ 0.385, 0.012, 0.012, 1, 0.15 ] ], \"cx\": 0, \"mat\": \"fur\", \"seg\": 10 }, { \"loft\": [ [ 0.247, 0.012, 0.012, 1, 0.225 ], [ 0.2526, 0.021, 0.021, 1, 0.225 ], [ 0.2638, 0.0322, 0.0322, 1, 0.225 ], [ 0.275, 0.035, 0.035, 1, 0.225 ], [ 0.2862, 0.0322, 0.0322, 1, 0.225 ], [ 0.2974, 0.021, 0.021, 1, 0.225 ], [ 0.303, 0.012, 0.012, 1, 0.225 ] ], \"cx\": 0, \"mat\": \"belly\", \"seg\": 10 }, { \"fan\": [ [ 0.03, 0.36, 0.14 ], [ 0.085, 0.37, 0.13 ], [ 0.07, 0.46, 0.13 ], [ 0.01, 0.4, 0.14 ] ], \"mirror\": true, \"mat\": \"furDark\" }, { \"tube\": [ [ 0, 0.2, -0.2, 0.025 ], [ 0, 0.17, -0.3, 0.025 ], [ 0, 0.26, -0.36, 0.025 ], [ 0, 0.38, -0.34, 0.025 ] ], \"mat\": \"furDark\", \"sides\": 5 }, { \"loft\": [ [ 0.31, 0.012, 0.012, 1, 0.2 ], [ 0.314, 0.012, 0.012, 1, 0.2 ], [ 0.322, 0.0184, 0.0184, 1, 0.2 ], [ 0.33, 0.02, 0.02, 1, 0.2 ], [ 0.338, 0.0184, 0.0184, 1, 0.2 ], [ 0.346, 0.012, 0.012, 1, 0.2 ], [ 0.35, 0.012, 0.012, 1, 0.2 ] ], \"cx\": 0.06, \"mat\": \"glow\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.44, \"head\": [ 0, 0.37, 0.03 ], \"pieces\": [ { \"loft\": [ [ 0.18, 0.02, 0.02 ], [ 0.22, 0.1, 0.1 ], [ 0.29, 0.15, 0.15 ], [ 0.36, 0.13, 0.13 ], [ 0.42, 0.06, 0.06 ], [ 0.44, 0.015, 0.015 ] ], \"mat\": \"glass\", \"seg\": 12 }, { \"loft\": [ [ 0.2, 0.1, 0.1 ], [ 0.25, 0.115, 0.115 ] ], \"mat\": \"fur\", \"seg\": 12 }, { \"tube\": [ [ 0.07, 0.2, 0.04, 0.022 ], [ 0.091, 0.1, 0.06, 0.018 ], [ 0.056, 0.02, 0.03, 0.014 ] ], \"mat\": \"fur\", \"sides\": 5 }, { \"tube\": [ [ -0.07, 0.2, 0.04, 0.022 ], [ -0.091, 0.1, 0.06, 0.018 ], [ -0.056, 0.02, 0.03, 0.014 ] ], \"mat\": \"fur\", \"sides\": 5 }, { \"tube\": [ [ 0, 0.2, 0.09, 0.022 ], [ 0, 0.1, 0.11, 0.018 ], [ 0, 0.02, 0.08, 0.014 ] ], \"mat\": \"fur\", \"sides\": 5 }, { \"tube\": [ [ 0, 0.2, -0.06, 0.022 ], [ 0, 0.1, -0.04, 0.018 ], [ 0, 0.02, -0.07, 0.014 ] ], \"mat\": \"fur\", \"sides\": 5 }, { \"loft\": [ [ 0.282, 0.012, 0.012, 1, 0.1 ], [ 0.2876, 0.0168, 0.0168, 1, 0.1 ], [ 0.2988, 0.0258, 0.0258, 1, 0.1 ], [ 0.31, 0.028, 0.028, 1, 0.1 ], [ 0.3212, 0.0258, 0.0258, 1, 0.1 ], [ 0.3324, 0.0168, 0.0168, 1, 0.1 ], [ 0.338, 0.012, 0.012, 1, 0.1 ] ], \"cx\": 0.08, \"mat\": \"dark\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.3, \"head\": [ 0, 0.2, 0.24 ], \"pieces\": [ { \"tube\": [ [ 0, 0.13, -0.1, 0.06 ], [ 0, 0.14, 0.04, 0.07 ], [ 0, 0.17, 0.14, 0.05 ] ], \"mat\": \"fur\", \"sides\": 8 }, { \"tube\": [ [ 0, 0.12, -0.1, 0.05 ], [ 0, 0.08, -0.25, 0.04 ], [ 0, 0.05, -0.38, 0.028 ], [ 0, 0.1, -0.46, 0.02 ] ], \"mat\": \"fur\", \"sides\": 6 }, { \"fan\": [ [ 0, 0.19, 0.14 ], [ 0, 0.26, 0.1 ], [ 0, 0.19, 0.06 ] ], \"mat\": \"accent\" }, { \"fan\": [ [ 0, 0.19, 0.04 ], [ 0, 0.26, 0 ], [ 0, 0.19, -0.04 ] ], \"mat\": \"accent\" }, { \"fan\": [ [ 0, 0.16, -0.06 ], [ 0, 0.23, -0.1 ], [ 0, 0.16, -0.14 ] ], \"mat\": \"accent\" }, { \"tube\": [ [ 0.075, 0.1, 0.1, 0.03 ], [ 0.1, 0.05, 0.13, 0.03 ], [ 0.1, 0.01, 0.16, 0.03 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 5 }, { \"tube\": [ [ 0.075, 0.1, -0.09, 0.03 ], [ 0.1, 0.05, -0.06, 0.03 ], [ 0.1, 0.01, -0.03, 0.03 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 5 }, { \"loft\": [ [ 0.145, 0.012, 0.012, 1, 0.22 ], [ 0.156, 0.039, 0.045, 1, 0.22 ], [ 0.178, 0.0598, 0.069, 1, 0.22 ], [ 0.2, 0.065, 0.075, 1, 0.22 ], [ 0.222, 0.0598, 0.069, 1, 0.22 ], [ 0.244, 0.039, 0.045, 1, 0.22 ], [ 0.255, 0.012, 0.012, 1, 0.22 ] ], \"cx\": 0, \"mat\": \"fur\", \"seg\": 10 }, { \"loft\": [ [ 0.15, 0.012, 0.012, 1, 0.3 ], [ 0.156, 0.021, 0.024, 1, 0.3 ], [ 0.168, 0.0322, 0.0368, 1, 0.3 ], [ 0.18, 0.035, 0.04, 1, 0.3 ], [ 0.192, 0.0322, 0.0368, 1, 0.3 ], [ 0.204, 0.021, 0.024, 1, 0.3 ], [ 0.21, 0.012, 0.012, 1, 0.3 ] ], \"cx\": 0, \"mat\": \"belly\", \"seg\": 10 }, { \"fan\": [ [ 0.05, 0.23, 0.2 ], [ 0.1, 0.3, 0.19 ], [ 0.1, 0.19, 0.19 ], [ 0.06, 0.17, 0.2 ] ], \"mirror\": true, \"mat\": \"accent\" }, { \"loft\": [ [ 0.205, 0.012, 0.012, 1, 0.262 ], [ 0.209, 0.012, 0.012, 1, 0.262 ], [ 0.217, 0.0184, 0.0184, 1, 0.262 ], [ 0.225, 0.02, 0.02, 1, 0.262 ], [ 0.233, 0.0184, 0.0184, 1, 0.262 ], [ 0.241, 0.012, 0.012, 1, 0.262 ], [ 0.245, 0.012, 0.012, 1, 0.262 ] ], \"cx\": 0.05, \"mat\": \"glow\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.42, \"head\": [ 0, 0.3, 0.05 ], \"pieces\": [ { \"loft\": [ [ 0.03, 0.012, 0.012, 1, 0 ], [ 0.064, 0.102, 0.096, 1, 0 ], [ 0.132, 0.1564, 0.1472, 1, 0 ], [ 0.2, 0.17, 0.16, 1, 0 ], [ 0.268, 0.1564, 0.1472, 1, 0 ], [ 0.336, 0.102, 0.096, 1, 0 ], [ 0.37, 0.012, 0.012, 1, 0 ] ], \"cx\": 0, \"mat\": \"fur\", \"seg\": 10 }, { \"loft\": [ [ 0.05, 0.012, 0.012, 1, 0.08 ], [ 0.07, 0.06, 0.054, 1, 0.08 ], [ 0.11, 0.092, 0.0828, 1, 0.08 ], [ 0.15, 0.1, 0.09, 1, 0.08 ], [ 0.19, 0.092, 0.0828, 1, 0.08 ], [ 0.23, 0.06, 0.054, 1, 0.08 ], [ 0.25, 0.012, 0.012, 1, 0.08 ] ], \"cx\": 0, \"mat\": \"belly\", \"seg\": 10 }, { \"tube\": [ [ 0.06, 0.34, -0.01, 0.035 ], [ 0.1, 0.46, -0.01, 0.03 ], [ 0.12, 0.52, -0.01, 0.02 ] ], \"mirror\": true, \"mat\": \"fur\", \"sides\": 6 }, { \"tube\": [ [ 0.06, 0.35, 0.01, 0.015 ], [ 0.095, 0.44, 0.01, 0.012 ] ], \"mirror\": true, \"mat\": \"belly\", \"sides\": 4 }, { \"tube\": [ [ 0.08, 0.04, 0.1, 0.04 ], [ 0.08, 0.01, 0.12, 0.045 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 6 }, { \"loft\": [ [ 0.212, 0.012, 0.012, 1, 0.112 ], [ 0.2176, 0.0168, 0.0168, 1, 0.112 ], [ 0.2288, 0.0258, 0.0258, 1, 0.112 ], [ 0.24, 0.028, 0.028, 1, 0.112 ], [ 0.2512, 0.0258, 0.0258, 1, 0.112 ], [ 0.2624, 0.0168, 0.0168, 1, 0.112 ], [ 0.268, 0.012, 0.012, 1, 0.112 ] ], \"cx\": 0.1, \"mat\": \"dark\", \"seg\": 6, \"mirror\": true }, { \"mark\": \"mouth\", \"at\": [ 0, 0.1889, 0.165 ], \"mat\": \"dark\" } ] }, { \"top\": 0.5, \"head\": [ 0, 0.33, 0.03 ], \"pieces\": [ { \"box\": [ -0.1, 0.07, -0.08, 0.1, 0.22, 0.09 ], \"mat\": \"metal\" }, { \"box\": [ -0.085, 0.09, 0.09, 0.085, 0.2, 0.1 ], \"mat\": \"dye\" }, { \"box\": [ -0.12, 0, -0.08, -0.07, 0.07, 0.09 ], \"mat\": \"dark\" }, { \"box\": [ 0.07, 0, -0.08, 0.12, 0.07, 0.09 ], \"mat\": \"dark\" }, { \"box\": [ -0.11, 0.22, -0.09, 0.11, 0.4, 0.1 ], \"mat\": \"metal\" }, { \"box\": [ -0.085, 0.29, 0.1, 0.085, 0.36, 0.115 ], \"mat\": \"dark\" }, { \"tube\": [ [ 0, 0.4, 0, 0.012 ], [ 0, 0.48, 0, 0.012 ] ], \"mat\": \"metal\", \"sides\": 4 }, { \"loft\": [ [ 0.47, 0.012, 0.012, 1, 0 ], [ 0.476, 0.018, 0.018, 1, 0 ], [ 0.488, 0.0276, 0.0276, 1, 0 ], [ 0.5, 0.03, 0.03, 1, 0 ], [ 0.512, 0.0276, 0.0276, 1, 0 ], [ 0.524, 0.018, 0.018, 1, 0 ], [ 0.53, 0.012, 0.012, 1, 0 ] ], \"cx\": 0, \"mat\": \"glow\", \"seg\": 6 }, { \"box\": [ 0.11, 0.28, -0.03, 0.15, 0.34, 0.03 ], \"mirror\": true, \"mat\": \"dye\" }, { \"loft\": [ [ 0.303, 0.012, 0.012, 1, 0.105 ], [ 0.3074, 0.0132, 0.0132, 1, 0.105 ], [ 0.3162, 0.0202, 0.0202, 1, 0.105 ], [ 0.325, 0.022, 0.022, 1, 0.105 ], [ 0.3338, 0.0202, 0.0202, 1, 0.105 ], [ 0.3426, 0.0132, 0.0132, 1, 0.105 ], [ 0.347, 0.012, 0.012, 1, 0.105 ] ], \"cx\": 0.045, \"mat\": \"glow\", \"seg\": 6, \"mirror\": true } ] }, { \"top\": 0.5, \"head\": [ 0, 0.28, 0.05 ], \"pieces\": [ { \"loft\": [ [ 0.03, 0.08, 0.08 ], [ 0.1, 0.12, 0.12 ], [ 0.2, 0.15, 0.15 ], [ 0.27, 0.13, 0.13 ] ], \"mat\": \"fur\", \"seg\": 12 }, { \"loft\": [ [ 0.27, 0.15, 0.15 ], [ 0.3, 0.15, 0.15 ] ], \"mat\": \"furDark\", \"seg\": 12 }, { \"tube\": [ [ 0.06, 0.04, 0.05, 0.035 ], [ 0.06, 0.01, 0.07, 0.04 ] ], \"mirror\": true, \"mat\": \"furDark\", \"sides\": 5 }, { \"tube\": [ [ 0, 0.3, 0, 0.018 ], [ 0, 0.4, 0, 0.016 ] ], \"mat\": \"dye\", \"sides\": 5 }, { \"fan\": [ [ 0, 0.38, 0 ], [ 0.12, 0.47, 0.02 ], [ 0.17, 0.4, 0.02 ], [ 0.1, 0.34, 0 ] ], \"mirror\": true, \"mat\": \"dye\" }, { \"fan\": [ [ 0, 0.4, 0 ], [ 0.02, 0.5, 0 ], [ 0.07, 0.52, 0 ], [ 0.07, 0.42, 0 ] ], \"mat\": \"trim\" }, { \"loft\": [ [ 0.174, 0.012, 0.012, 1, 0.115 ], [ 0.1792, 0.0156, 0.0156, 1, 0.115 ], [ 0.1896, 0.0239, 0.0239, 1, 0.115 ], [ 0.2, 0.026, 0.026, 1, 0.115 ], [ 0.2104, 0.0239, 0.0239, 1, 0.115 ], [ 0.2208, 0.0156, 0.0156, 1, 0.115 ], [ 0.226, 0.012, 0.012, 1, 0.115 ] ], \"cx\": 0.09, \"mat\": \"dark\", \"seg\": 6, \"mirror\": true }, { \"mark\": \"mouth\", \"at\": [ 0, 0.1556, 0.155 ], \"mat\": \"dark\" } ] } ];\r\n","pod/gala-rig/designs/shield.mjs":"// Gala shield designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 none */ [],\r\n  /* 1 wrist buckler */ [\r\n    { fan: [[-0.34,1.04,0.08],[-0.25,1.04,0.08],[-0.262,1.085,0.08],[-0.295,1.118,0.08],[-0.34,1.13,0.08],[-0.385,1.118,0.08],[-0.418,1.085,0.08],[-0.43,1.04,0.08],[-0.418,0.995,0.08],[-0.385,0.962,0.08],[-0.34,0.95,0.08],[-0.295,0.962,0.08],[-0.262,0.995,0.08],[-0.25,1.04,0.08]], mat: 'metal', group: 'arm' },\r\n    { tube: [[-0.25,1.04,0.08,0.014],[-0.262,1.085,0.08,0.014],[-0.295,1.118,0.08,0.014],[-0.34,1.13,0.08,0.014],[-0.385,1.118,0.08,0.014],[-0.418,1.085,0.08,0.014],[-0.43,1.04,0.08,0.014],[-0.418,0.995,0.08,0.014],[-0.385,0.962,0.08,0.014],[-0.34,0.95,0.08,0.014],[-0.295,0.962,0.08,0.014],[-0.262,0.995,0.08,0.014],[-0.25,1.04,0.08,0.014]], sides: 5, mat: 'accent', group: 'arm' },\r\n    { loft: [[1.005,0.011,0.011,1,0.08],[1.04,0.035,0.035,1,0.098],[1.075,0.011,0.011,1,0.08]], cx: -0.34, seg: 8, mat: 'accent', group: 'arm' }\r\n  ],\r\n  /* 2 disc */ [\r\n    { fan: [[-0.34,1.14,0.095],[-0.13,1.14,0.095],[-0.151,1.231,0.095],[-0.209,1.304,0.095],[-0.293,1.345,0.095],[-0.387,1.345,0.095],[-0.471,1.304,0.095],[-0.529,1.231,0.095],[-0.55,1.14,0.095],[-0.529,1.049,0.095],[-0.471,0.976,0.095],[-0.387,0.935,0.095],[-0.293,0.935,0.095],[-0.209,0.976,0.095],[-0.151,1.049,0.095],[-0.13,1.14,0.095]], mat: 'metal', group: 'arm' },\r\n    { tube: [[-0.13,1.14,0.095,0.022],[-0.151,1.231,0.095,0.022],[-0.209,1.304,0.095,0.022],[-0.293,1.345,0.095,0.022],[-0.387,1.345,0.095,0.022],[-0.471,1.304,0.095,0.022],[-0.529,1.231,0.095,0.022],[-0.55,1.14,0.095,0.022],[-0.529,1.049,0.095,0.022],[-0.471,0.976,0.095,0.022],[-0.387,0.935,0.095,0.022],[-0.293,0.935,0.095,0.022],[-0.209,0.976,0.095,0.022],[-0.151,1.049,0.095,0.022],[-0.13,1.14,0.095,0.022]], sides: 5, mat: 'trim', group: 'arm' },\r\n    { loft: [[1.09,0.015,0.015,1,0.095],[1.14,0.05,0.05,1,0.12],[1.19,0.015,0.015,1,0.095]], cx: -0.34, seg: 8, mat: 'trim', group: 'arm' },\r\n    { tube: [[-0.24,1.14,0.1,0.01],[-0.253,1.19,0.1,0.01],[-0.29,1.227,0.1,0.01],[-0.34,1.24,0.1,0.01],[-0.39,1.227,0.1,0.01],[-0.427,1.19,0.1,0.01],[-0.44,1.14,0.1,0.01],[-0.427,1.09,0.1,0.01],[-0.39,1.053,0.1,0.01],[-0.34,1.04,0.1,0.01],[-0.29,1.053,0.1,0.01],[-0.253,1.09,0.1,0.01],[-0.24,1.14,0.1,0.01]], sides: 5, mat: 'trim', group: 'arm' }\r\n  ],\r\n  /* 3 shaped barrier */ [\r\n    { fan: [[-0.34,1.11,0.095],[-0.53,1.42,0.095],[-0.15,1.42,0.095],[-0.131,1.13,0.095],[-0.34,0.8,0.095],[-0.549,1.13,0.095],[-0.53,1.42,0.095]], mat: 'glow', group: 'arm' },\r\n    { tube: [[-0.5,1.34,0.1,0.016],[-0.51,1.12,0.1,0.016],[-0.34,0.84,0.1,0.016],[-0.17,1.12,0.1,0.016],[-0.18,1.34,0.1,0.016],[-0.5,1.34,0.1,0.016]], sides: 5, mat: 'white', group: 'arm' }\r\n  ],\r\n  /* 4 tower field */ [\r\n    { box: [-0.45,0.82,0.09,-0.23,1.42,0.1], mat: 'glass', group: 'arm' },\r\n    { box: [-0.47,1.4,0.08,-0.21,1.45,0.11], mat: 'metal', group: 'arm' },\r\n    { box: [-0.47,0.8,0.08,-0.21,0.85,0.11], mat: 'metal', group: 'arm' },\r\n    { box: [-0.47,0.8,0.08,-0.43,1.45,0.11], mat: 'metal', group: 'arm' },\r\n    { box: [-0.25,0.8,0.08,-0.21,1.45,0.11], mat: 'metal', group: 'arm' },\r\n    { mark: 'gem', at: [-0.34,1.42,0.115], group: 'arm', mat: 'glow' }\r\n  ],\r\n  /* 5 hex shield */ [\r\n    { fan: [[-0.34,1.14,0.095],[-0.158,1.245,0.095],[-0.34,1.35,0.095],[-0.522,1.245,0.095],[-0.522,1.035,0.095],[-0.34,0.93,0.095],[-0.158,1.035,0.095],[-0.158,1.245,0.095]], mat: 'metal', group: 'arm' },\r\n    { fan: [[-0.34,1.14,0.105],[-0.245,1.195,0.105],[-0.34,1.25,0.105],[-0.435,1.195,0.105],[-0.435,1.085,0.105],[-0.34,1.03,0.105],[-0.245,1.085,0.105],[-0.245,1.195,0.105]], mat: 'accent', group: 'arm' },\r\n    { loft: [[1.1,0.012,0.012,1,0.105],[1.14,0.04,0.04,1,0.125],[1.18,0.012,0.012,1,0.105]], cx: -0.34, seg: 8, mat: 'metal', group: 'arm' },\r\n    { tube: [[-0.13,1.14,0.1,0.018],[-0.235,1.322,0.1,0.018],[-0.445,1.322,0.1,0.018],[-0.55,1.14,0.1,0.018],[-0.445,0.958,0.1,0.018],[-0.235,0.958,0.1,0.018],[-0.13,1.14,0.1,0.018]], sides: 5, mat: 'accent', group: 'arm' }\r\n  ],\r\n  /* 6 ring guard */ [\r\n    { tube: [[-0.13,1.14,0.09,0.03],[-0.151,1.231,0.09,0.03],[-0.209,1.304,0.09,0.03],[-0.293,1.345,0.09,0.03],[-0.387,1.345,0.09,0.03],[-0.471,1.304,0.09,0.03],[-0.529,1.231,0.09,0.03],[-0.55,1.14,0.09,0.03],[-0.529,1.049,0.09,0.03],[-0.471,0.976,0.09,0.03],[-0.387,0.935,0.09,0.03],[-0.293,0.935,0.09,0.03],[-0.209,0.976,0.09,0.03],[-0.151,1.049,0.09,0.03],[-0.13,1.14,0.09,0.03]], sides: 5, mat: 'accent', group: 'arm' },\r\n    { mark: 'gem', at: [-0.34,1.35,0.12], mat: 'accent', group: 'arm' },\r\n    { mark: 'gem', at: [-0.34,0.93,0.12], mat: 'accent', group: 'arm' },\r\n    { mark: 'gem', at: [-0.55,1.14,0.12], mat: 'accent', group: 'arm' },\r\n    { loft: [[1.11,0.009,0.009,1,0.09],[1.14,0.03,0.03,1,0.105],[1.17,0.009,0.009,1,0.09]], cx: -0.34, seg: 8, mat: 'accent', group: 'arm' }\r\n  ],\r\n  /* 7 spike targe */ [\r\n    { fan: [[-0.34,1.14,0.095],[-0.14,1.14,0.095],[-0.16,1.227,0.095],[-0.215,1.296,0.095],[-0.295,1.335,0.095],[-0.385,1.335,0.095],[-0.465,1.296,0.095],[-0.52,1.227,0.095],[-0.54,1.14,0.095],[-0.52,1.053,0.095],[-0.465,0.984,0.095],[-0.385,0.945,0.095],[-0.295,0.945,0.095],[-0.215,0.984,0.095],[-0.16,1.053,0.095],[-0.14,1.14,0.095]], mat: 'metal', group: 'arm' },\r\n    { tube: [[-0.14,1.14,0.095,0.02],[-0.16,1.227,0.095,0.02],[-0.215,1.296,0.095,0.02],[-0.295,1.335,0.095,0.02],[-0.385,1.335,0.095,0.02],[-0.465,1.296,0.095,0.02],[-0.52,1.227,0.095,0.02],[-0.54,1.14,0.095,0.02],[-0.52,1.053,0.095,0.02],[-0.465,0.984,0.095,0.02],[-0.385,0.945,0.095,0.02],[-0.295,0.945,0.095,0.02],[-0.215,0.984,0.095,0.02],[-0.16,1.053,0.095,0.02],[-0.14,1.14,0.095,0.02]], sides: 5, mat: 'trim', group: 'arm' },\r\n    { tube: [[-0.34,1.14,0.09,0.055],[-0.34,1.14,0.17,0.035],[-0.34,1.14,0.3,0.008]], sides: 6, mat: 'metal', group: 'arm' },\r\n    { mark: 'stud', at: [-0.34,1.29,0.105], mat: 'accent', group: 'arm' },\r\n    { mark: 'stud', at: [-0.34,0.99,0.105], mat: 'accent', group: 'arm' },\r\n    { mark: 'stud', at: [-0.53,1.14,0.105], mat: 'accent', group: 'arm' },\r\n    { mark: 'stud', at: [-0.15,1.14,0.105], mat: 'accent', group: 'arm' }\r\n  ],\r\n  /* 8 energy kite */ [\r\n    { fan: [[-0.34,1.11,0.095],[-0.54,1.42,0.095],[-0.14,1.42,0.095],[-0.12,1.13,0.095],[-0.34,0.8,0.095],[-0.56,1.13,0.095],[-0.54,1.42,0.095]], mat: 'glow', group: 'arm' },\r\n    { fan: [[-0.34,1.11,0.105],[-0.47,1.34,0.105],[-0.21,1.34,0.105],[-0.197,1.13,0.105],[-0.34,0.88,0.105],[-0.483,1.13,0.105],[-0.47,1.34,0.105]], mat: 'glass', group: 'arm' },\r\n    { mark: 'gem', at: [-0.34,1.2,0.115], mat: 'glow', group: 'arm' }\r\n  ],\r\n  /* 9 mirror disc */ [\r\n    { fan: [[-0.34,1.14,0.095],[-0.13,1.14,0.095],[-0.151,1.231,0.095],[-0.209,1.304,0.095],[-0.293,1.345,0.095],[-0.387,1.345,0.095],[-0.471,1.304,0.095],[-0.529,1.231,0.095],[-0.55,1.14,0.095],[-0.529,1.049,0.095],[-0.471,0.976,0.095],[-0.387,0.935,0.095],[-0.293,0.935,0.095],[-0.209,0.976,0.095],[-0.151,1.049,0.095],[-0.13,1.14,0.095]], mat: 'pearl', group: 'arm' },\r\n    { tube: [[-0.13,1.14,0.095,0.022],[-0.151,1.231,0.095,0.022],[-0.209,1.304,0.095,0.022],[-0.293,1.345,0.095,0.022],[-0.387,1.345,0.095,0.022],[-0.471,1.304,0.095,0.022],[-0.529,1.231,0.095,0.022],[-0.55,1.14,0.095,0.022],[-0.529,1.049,0.095,0.022],[-0.471,0.976,0.095,0.022],[-0.387,0.935,0.095,0.022],[-0.293,0.935,0.095,0.022],[-0.209,0.976,0.095,0.022],[-0.151,1.049,0.095,0.022],[-0.13,1.14,0.095,0.022]], sides: 5, mat: 'glow', group: 'arm' },\r\n    { fan: [[-0.34,1.14,0.105],[-0.26,1.14,0.105],[-0.275,1.187,0.105],[-0.315,1.216,0.105],[-0.365,1.216,0.105],[-0.405,1.187,0.105],[-0.42,1.14,0.105],[-0.405,1.093,0.105],[-0.365,1.064,0.105],[-0.315,1.064,0.105],[-0.275,1.093,0.105],[-0.26,1.14,0.105]], mat: 'white', group: 'arm' },\r\n    { mark: 'gem', at: [-0.28,1.22,0.115], mat: 'glow', group: 'arm' }\r\n  ]\r\n];\r\n","pod/gala-rig/designs/shoulders.mjs":"// Gala shoulders designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vela epaulettes */ [\r\n    { box: [0.17,1.63,-0.06,0.31,1.67,0.06], mirror: true, group: 'arm', mat: 'accent' },\r\n    { box: [0.29,1.56,-0.05,0.31,1.63,0.05], mirror: true, group: 'arm', mat: 'accent' }\r\n  ],\r\n  /* 1 Quor petals */ [\r\n    { fan: [[0.22,1.63,0.05],[0.149,1.799,0.05],[0.251,1.989,0.08],[0.319,1.784,0.05]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.22,1.63,0],[0.253,1.802,0],[0.439,1.89,0.03],[0.383,1.693,0]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.22,1.63,-0.05],[0.322,1.753,-0.05],[0.505,1.723,-0.02],[0.375,1.591,-0.05]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { loft: [[1.6,0.075,0.075],[1.68,0.075,0.075]], cx: 0.235, group: 'arm', mirror: true, mat: 'accent', seg: 8 }\r\n  ],\r\n  /* 2 Krell spikes */ [\r\n    { tube: [[0.22,1.58,0.05,0.05],[0.285,1.81,0.07,0.034],[0.33,1.98,0.09,0.008]], mirror: true, group: 'arm', mat: 'ivory' },\r\n    { tube: [[0.25,1.58,0,0.05],[0.365,1.76,0,0.034],[0.46,1.88,0,0.008]], mirror: true, group: 'arm', mat: 'ivory' },\r\n    { tube: [[0.22,1.58,-0.05,0.05],[0.3,1.74,-0.075,0.034],[0.36,1.84,-0.1,0.008]], mirror: true, group: 'arm', mat: 'ivory' },\r\n    { loft: [[1.56,0.08,0.08],[1.62,0.09,0.09]], cx: 0.24, group: 'arm', mirror: true, mat: 'metal', seg: 8 }\r\n  ],\r\n  /* 3 Nymbi puffs */ [\r\n    { loft: [[1.5,0.07,0.07],[1.56,0.115,0.105],[1.62,0.125,0.115],[1.68,0.1,0.09],[1.715,0.05,0.045]], cx: 0.245, seg: 10, group: 'arm', mirror: true, mat: 'dye' },\r\n    { loft: [[1.485,0.085,0.085],[1.52,0.09,0.09]], cx: 0.24, seg: 10, group: 'arm', mirror: true, mat: 'trim' }\r\n  ],\r\n  /* 4 Orryx mantle */ [\r\n    { drape: {'kind':'flare','top':1.62,'hem':1.3,'flare':[0.2,0.12,0.36,0.2]}, mat: 'trim' },\r\n    { loft: [[1.27,0.36,0.2],[1.32,0.37,0.21]], seg: 16, caps: false, group: 'body', mat: 'dye' },\r\n    { mark: 'gem', at: [0,1.58,0.115], group: 'body', mat: 'accent' }\r\n  ],\r\n  /* 5 Vex wings */ [\r\n    { fan: [[0.2,1.64,-0.07],[0.173,1.844,-0.07],[0.261,2.076,-0.17],[0.282,1.828,-0.07]], mirror: true, group: 'arm', mat: 'accent' },\r\n    { fan: [[0.2,1.64,-0.07],[0.249,1.822,-0.07],[0.412,1.979,-0.17],[0.342,1.764,-0.07]], mirror: true, group: 'arm', mat: 'white' },\r\n    { fan: [[0.2,1.64,-0.07],[0.299,1.767,-0.07],[0.482,1.83,-0.17],[0.355,1.684,-0.07]], mirror: true, group: 'arm', mat: 'accent' },\r\n    { fan: [[0.2,1.64,-0.07],[0.305,1.708,-0.07],[0.454,1.694,-0.17],[0.324,1.62,-0.07]], mirror: true, group: 'arm', mat: 'white' },\r\n    { box: [0.19,1.58,-0.11,0.25,1.67,-0.05], mirror: true, group: 'arm', mat: 'accent' }\r\n  ],\r\n  /* 6 Dravox plates */ [\r\n    { dfo: 'pauldrons', mat: 'metal', group: 'arm', mirror: true },\r\n    { loft: [[1.56,0.09,0.08],[1.62,0.13,0.11],[1.68,0.1,0.08],[1.7,0.05,0.04]], cx: 0.25, seg: 10, group: 'arm', mirror: true, mat: 'metal' },\r\n    { loft: [[1.5,0.1,0.09],[1.54,0.11,0.1]], cx: 0.25, seg: 10, group: 'arm', mirror: true, mat: 'accent' },\r\n    { mark: 'gem', at: [0.27,1.62,0.11], group: 'arm', mirror: true, mat: 'glow' }\r\n  ],\r\n  /* 7 Mollu fronds */ [\r\n    { fan: [[0.27,1.66,0.09],[0.457,1.537,0.09],[0.52,1.227,0.09],[0.283,1.437,0.09]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.2,1.66,0.09],[0.256,1.45,0.09],[0.13,1.165,0.09],[0.088,1.474,0.09]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.24,1.66,0.095],[0.361,1.494,0.095],[0.32,1.207,0.095],[0.183,1.463,0.095]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.31,1.64,0.02],[0.34,1.44,0.135],[0.36,1.14,0.07],[0.34,1.44,-0.045]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.31,1.64,-0.03],[0.34,1.464,0.035],[0.36,1.2,-0.08],[0.34,1.464,-0.145]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { fan: [[0.25,1.66,-0.09],[0.369,1.526,-0.09],[0.354,1.274,-0.09],[0.214,1.485,-0.09]], mirror: true, group: 'arm', mat: 'trim' },\r\n    { loft: [[1.6,0.08,0.08],[1.67,0.085,0.085]], cx: 0.235, group: 'arm', mirror: true, mat: 'accent', seg: 8 }\r\n  ],\r\n  /* 8 Xelith orbitals */ [\r\n    { loft: [[1.78,0.115,0.115],[1.86,0.115,0.115]], cx: 0.31, caps: false, seg: 12, group: 'arm', mirror: true, mat: 'glow' },\r\n    { loft: [[1.7,0.025,0.025],[1.76,0.035,0.035],[1.82,0.025,0.025]], cx: 0.31, seg: 8, group: 'arm', mirror: true, mat: 'glow' },\r\n    { mark: 'gem', at: [0.31,1.84,0.11], group: 'arm', mirror: true, mat: 'glow' }\r\n  ],\r\n  /* 9 Ulumi cape */ [\r\n    { drape: {'kind':'cape','hem':0.45,'wide':true}, mat: 'trim' },\r\n    { loft: [[1.58,0.23,0.13],[1.66,0.23,0.13]], seg: 16, caps: false, group: 'body', mat: 'dye' },\r\n    { mark: 'gem', at: [0,1.63,0.11], group: 'body', mat: 'accent' }\r\n  ]\r\n];\r\n","pod/gala-rig/designs/tail.mjs":"// Gala tail designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 No tail */ [\r\n\r\n  ],\r\n  /* 1 Lizard tail */ [\r\n    {dfo:'tail:argonian',mat:'skin'}\r\n  ],\r\n  /* 2 Cat tail */ [\r\n    {dfo:'tail:khajiit',mat:'skin'}\r\n  ],\r\n  /* 3 Stinger */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.055],[0,0.95,-0.16,0.055],[0,1.08,-0.27,0.05],[0,1.3,-0.31,0.045],[0,1.52,-0.26,0.04],[0,1.66,-0.15,0.035]],mat:'skinDark',sides:7},\r\n    {group:'body',loft:[[0.94,0.07,0.07,1,-0.15],[1.02,0.065,0.065,1,-0.2]],mat:'skinDark',seg:8},\r\n    {group:'body',loft:[[1.16,0.06,0.06,1,-0.3],[1.22,0.06,0.06,1,-0.3]],mat:'accent',seg:8},\r\n    {group:'body',loft:[[1.38,0.055,0.055,1,-0.29],[1.44,0.055,0.055,1,-0.29]],mat:'accent',seg:8},\r\n    {group:'body',tube:[[0,1.64,-0.16,0.05],[0,1.66,-0.08,0.03],[0,1.62,0,0.006]],mat:'accent',sides:6}\r\n  ],\r\n  /* 4 Fin tail */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.055],[0,0.9,-0.2,0.05],[0,0.7,-0.34,0.04],[0,0.5,-0.42,0.03],[0,0.36,-0.5,0.02]],mat:'skin',sides:7},\r\n    {group:'body',fan:[[0,0.78,-0.4],[0,0.96,-0.2],[0,1.12,-0.32],[0,0.98,-0.5],[0,0.72,-0.56],[0,0.5,-0.56],[0,0.55,-0.45]],mat:'trim'},\r\n    {group:'body',fan:[[0,0.45,-0.58],[0,0.55,-0.5],[0,0.4,-0.7],[0,0.22,-0.7],[0,0.28,-0.55]],mat:'trim'}\r\n  ],\r\n  /* 5 Whip tail */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.045],[0,0.9,-0.2,0.04],[0,0.62,-0.4,0.032],[0,0.3,-0.6,0.026],[0,0.1,-0.8,0.022],[0,0.03,-0.98,0.02]],mat:'skinDark',sides:5},\r\n    {group:'body',loft:[[0,0.035,0.035,1,-1],[0.06,0.035,0.035,1,-1]],mat:'accent',seg:8}\r\n  ],\r\n  /* 6 Club tail */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.055],[0,0.9,-0.2,0.055],[0,0.68,-0.36,0.05],[0,0.5,-0.46,0.05]],mat:'skin',sides:7},\r\n    {group:'body',loft:[[0.26,0.06,0.06,1,-0.52],[0.34,0.12,0.12,1,-0.52],[0.44,0.15,0.15,1,-0.52],[0.54,0.12,0.12,1,-0.52],[0.6,0.05,0.05,1,-0.52]],mat:'skin',seg:12},\r\n    {group:'body',tube:[[0.12,0.44,-0.52,0.035],[0.26,0.44,-0.52,0.006]],mirror:true,mat:'metal',sides:5},\r\n    {group:'body',tube:[[0,0.62,-0.52,0.032],[0,0.78,-0.52,0.006]],mat:'metal',sides:5},\r\n    {group:'body',tube:[[0,0.44,-0.67,0.032],[0,0.44,-0.82,0.006]],mat:'metal',sides:5},\r\n    {group:'body',tube:[[0,0.28,-0.52,0.032],[0,0.14,-0.52,0.006]],mat:'metal',sides:5},\r\n    {group:'body',tube:[[0.1,0.54,-0.6,0.03],[0.2,0.62,-0.7,0.006]],mirror:true,mat:'metal',sides:5}\r\n  ],\r\n  /* 7 Plume tail */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.055],[0,0.9,-0.2,0.05],[0,0.8,-0.3,0.045],[0,0.74,-0.36,0.05]],mat:'skin',sides:7},\r\n    {group:'body',tube:[[0,0.74,-0.36,0.06],[0,0.99,-0.51,0.051],[0,1.24,-0.6599999999999999,0.01]],mat:'trim',sides:6},\r\n    {group:'body',tube:[[0,0.74,-0.36,0.06],[0.1,0.95,-0.485,0.051],[0.2,1.16,-0.61,0.01]],mat:'accent',sides:6},\r\n    {group:'body',tube:[[0,0.74,-0.36,0.06],[-0.1,0.95,-0.485,0.051],[-0.2,1.16,-0.61,0.01]],mat:'accent',sides:6},\r\n    {group:'body',tube:[[0,0.74,-0.36,0.06],[0.18,0.88,-0.45999999999999996,0.051],[0.36,1.02,-0.56,0.01]],mat:'trim',sides:6},\r\n    {group:'body',tube:[[0,0.74,-0.36,0.06],[-0.18,0.88,-0.45999999999999996,0.051],[-0.36,1.02,-0.56,0.01]],mat:'trim',sides:6}\r\n  ],\r\n  /* 8 Twin tails */ [\r\n    {group:'body',tube:[[0.06,0.98,-0.05,0.045],[0.1,0.9,-0.2,0.042],[0.14,0.66,-0.33,0.036],[0.17,0.42,-0.38,0.03],[0.17,0.24,-0.33,0.018]],mirror:true,mat:'skin',sides:6},\r\n    {group:'body',loft:[[0.22,0.04,0.04,1,-0.32],[0.28,0.04,0.04,1,-0.32]],cx:0.17,mirror:true,mat:'trim',seg:8}\r\n  ],\r\n  /* 9 Coil tail */ [\r\n    {group:'body',tube:[[0,0.98,-0.05,0.04],[0,0.93,-0.14,0.04],[0,0.9,-0.15,0.04],[0.076,0.865,-0.188,0.039],[0.099,0.829,-0.276,0.039],[0.054,0.794,-0.353,0.038],[-0.028,0.758,-0.366,0.037],[-0.091,0.723,-0.306,0.036],[-0.091,0.687,-0.214,0.036],[-0.028,0.652,-0.154,0.035],[0.054,0.616,-0.167,0.034],[0.099,0.581,-0.244,0.033],[0.076,0.545,-0.332,0.033],[0,0.51,-0.37,0.032],[-0.076,0.475,-0.332,0.031],[-0.099,0.439,-0.244,0.031],[-0.054,0.404,-0.167,0.03],[0.028,0.368,-0.154,0.029],[0.091,0.333,-0.214,0.028],[0.091,0.297,-0.306,0.028],[0.028,0.262,-0.366,0.027],[-0.054,0.226,-0.353,0.026],[-0.099,0.191,-0.276,0.025],[-0.076,0.155,-0.188,0.025],[0,0.12,-0.15,0.024]],mat:'skin',sides:6},\r\n    {group:'body',loft:[[0.1,0.04,0.04,1,-0.2],[0.15,0.04,0.04,1,-0.2]],cx:0,mat:'accent',seg:8}\r\n  ]\r\n];\r\n","pod/gala-rig/designs/torso.mjs":"// Gala torso designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default [\r\n  /* 0 Vex tuxedo */ [\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.64, th: 0.01, mat: 'dye' },\r\n    { fan: [[0, 1.30, 0.11], [-0.07, 1.6, 0.1], [0.07, 1.6, 0.1]], mat: 'white' },\r\n    { fan: [[0.0, 1.28, 0.112], [0.075, 1.6, 0.105], [0.12, 1.58, 0.095], [0.05, 1.40, 0.11]], mirror: true, mat: 'trim' },\r\n    { mark: 'stud', at: [0, 1.22, 0.1], group: 'body', mat: 'accent' },\r\n    { mark: 'stud', at: [0, 1.14, 0.098], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 1 Mollu gown: strapless bodice, gold bandeau, waist sash, long flared skirt */ [\r\n    { zone: 'torso', yLo: 1.10, yHi: 1.60, th: 0.012, mat: 'dye' },\r\n    { drape: { kind: 'flare', top: 1.30, hem: 0.14, flare: [0.15, 0.11, 0.33, 0.23] }, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.45, yHi: 1.60, th: 0.016, mat: 'trim' },\r\n    { loft: [[1.26, 0.165, 0.125], [1.35, 0.165, 0.125]], group: 'body', mat: 'trim', seg: 12, caps: false },\r\n    { mark: 'gem', at: [0, 1.31, 0.14], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 2 Orryx brocade: jacket with gold hem/cuffs/collar bands and a gem lattice */ [\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.64, th: 0.01, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.07, th: 0.018, mat: 'trim' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.12, th: 0.018, mat: 'trim' },\r\n    { zone: 'torso', yLo: 1.55, yHi: 1.64, th: 0.016, mat: 'trim' },\r\n    { fan: [[0, 1.0, 0.108], [-0.012, 1.55, 0.105], [0.012, 1.55, 0.105]], mat: 'trim' },\r\n    { mark: 'gem', at: [0.07, 1.46, 0.125], group: 'body', mat: 'accent', mirror: true },\r\n    { mark: 'gem', at: [0.12, 1.34, 0.115], group: 'body', mat: 'accent', mirror: true },\r\n    { mark: 'gem', at: [0.07, 1.22, 0.115], group: 'body', mat: 'accent', mirror: true },\r\n    { mark: 'gem', at: [0, 1.34, 0.125], group: 'body', mat: 'accent' },\r\n    { mark: 'gem', at: [0, 1.10, 0.118], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 3 Quasar jumpsuit: whole suit, dark belt, white zipper, collar, leg stripes */ [\r\n    { zone: 'torso', yLo: 0.95, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 0.97, yHi: 1.64, th: 0.01, mat: 'dye' },\r\n    { zone: 'legs', yLo: 0.12, yHi: 1.00, th: 0.012, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.16, yHi: 1.24, th: 0.02, mat: 'dark' },\r\n    { zone: 'torso', yLo: 1.57, yHi: 1.64, th: 0.018, mat: 'trim' },\r\n    { box: [-0.014, 1.0, 0.1, 0.014, 1.57, 0.13], group: 'body', mat: 'white' },\r\n    { box: [-0.03, 1.19, 0.1, 0.03, 1.22, 0.14], group: 'body', mat: 'accent' },\r\n    { box: [0.11, 0.2, -0.02, 0.145, 0.98, 0.04], mirror: true, group: 'leg', mat: 'trim' },\r\n  ],\r\n  /* 4 Nymbi corset: gold corset with white lacing, bare shoulders, short peplum skirt */ [\r\n    { zone: 'torso', yLo: 1.05, yHi: 1.45, th: 0.018, mat: 'trim' },\r\n    { zone: 'torso', yLo: 1.45, yHi: 1.60, th: 0.01, mat: 'dye' },\r\n    { box: [-0.012, 1.08, 0.105, 0.012, 1.44, 0.13], group: 'body', mat: 'dark' },\r\n    { box: [-0.05, 1.38, 0.115, 0.05, 1.405, 0.14], group: 'body', mat: 'white' },\r\n    { box: [-0.05, 1.29, 0.115, 0.05, 1.315, 0.14], group: 'body', mat: 'white' },\r\n    { box: [-0.05, 1.20, 0.115, 0.05, 1.225, 0.14], group: 'body', mat: 'white' },\r\n    { box: [-0.05, 1.11, 0.115, 0.05, 1.135, 0.14], group: 'body', mat: 'white' },\r\n    { drape: { kind: 'flare', top: 1.12, hem: 0.80, flare: [0.18, 0.12, 0.28, 0.2] }, mat: 'dye' },\r\n  ],\r\n  /* 5 Krell robe: floor robe, open front trim placket, belt, hanging sleeves */ [\r\n    { drape: { kind: 'flare', top: 1.55, hem: 0.12, flare: [0.18, 0.13, 0.34, 0.24] }, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.05, yHi: 1.64, th: 0.014, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.1, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { loft: [[1.40, 0.07, 0.07], [1.15, 0.12, 0.1], [0.95, 0.13, 0.1]], cx: 0.255, mirror: true, group: 'arm', mat: 'dye', seg: 10 },\r\n    { fan: [[0, 0.80, 0.2], [-0.045, 1.55, 0.125], [0.045, 1.55, 0.125], [0.07, 0.16, 0.255], [-0.07, 0.16, 0.255]], mat: 'trim' },\r\n    { loft: [[1.2, 0.2, 0.15], [1.3, 0.2, 0.15]], group: 'body', mat: 'trim', seg: 12, caps: false },\r\n    { zone: 'torso', yLo: 1.55, yHi: 1.64, th: 0.018, mat: 'trim' },\r\n  ],\r\n  /* 6 Xelith doublet: padded dye doublet, big puffed gold sleeves, white cuffs, button row */ [\r\n    { zone: 'torso', yLo: 1.05, yHi: 1.64, th: 0.02, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.38, yHi: 1.64, th: 0.032, mat: 'trim' },\r\n    { zone: 'sleeves', yLo: 1.05, yHi: 1.38, th: 0.01, mat: 'white' },\r\n    { zone: 'torso', yLo: 1.05, yHi: 1.12, th: 0.03, mat: 'trim' },\r\n    { zone: 'torso', yLo: 1.55, yHi: 1.64, th: 0.026, mat: 'white' },\r\n    { mark: 'stud', at: [0, 1.50, 0.13], group: 'body', mat: 'accent' },\r\n    { mark: 'stud', at: [0, 1.40, 0.13], group: 'body', mat: 'accent' },\r\n    { mark: 'stud', at: [0, 1.30, 0.13], group: 'body', mat: 'accent' },\r\n    { mark: 'stud', at: [0, 1.20, 0.13], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 7 Dravox tailcoat: black tails to the knee, white waistcoat, gold lapels */ [\r\n    { zone: 'torso', yLo: 1.1, yHi: 1.64, th: 0.014, mat: 'dark' },\r\n    { zone: 'sleeves', yLo: 1.12, yHi: 1.64, th: 0.012, mat: 'dark' },\r\n    { fan: [[0, 1.12, 0.11], [-0.075, 1.6, 0.102], [0.075, 1.6, 0.102], [0.06, 1.12, 0.11]], mat: 'white' },\r\n    { fan: [[0.0, 1.30, 0.116], [0.08, 1.6, 0.108], [0.125, 1.58, 0.098], [0.06, 1.35, 0.114]], mirror: true, mat: 'trim' },\r\n    { fan: [[0, 1.16, -0.1], [0.02, 1.16, -0.1], [0.17, 0.66, -0.14], [0.02, 0.60, -0.12], [0.0, 0.60, -0.12]], mirror: true, group: 'body', mat: 'dark' },\r\n    { fan: [[0.17, 0.64, -0.14], [0.02, 1.16, -0.1], [0.12, 1.16, -0.1]], mirror: true, group: 'body', mat: 'trim' },\r\n    { mark: 'stud', at: [0.0, 1.26, 0.12], group: 'body', mat: 'accent' },\r\n    { mark: 'stud', at: [0.0, 1.18, 0.12], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 8 Ulumi wrap: slit skirt, trim bodice, one-shoulder cross wrap and shoulder cape */ [\r\n    { drape: { kind: 'flare', top: 1.52, hem: 0.26, flare: [0.15, 0.115, 0.27, 0.2], arc: [0.9, 5.5] }, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.05, yHi: 1.60, th: 0.01, mat: 'trim' },\r\n    { fan: [[0.0, 1.42, 0.125], [-0.15, 1.64, 0.1], [-0.02, 1.66, 0.1], [0.17, 1.1, 0.12], [0.02, 1.08, 0.125]], group: 'body', mat: 'trim' },\r\n    { loft: [[1.70, 0.07, 0.08], [1.55, 0.09, 0.1], [1.25, 0.075, 0.09]], cx: -0.235, group: 'arm', mat: 'dye', seg: 10 },\r\n    { mark: 'gem', at: [0.1, 1.2, 0.13], group: 'body', mat: 'accent' },\r\n  ],\r\n  /* 9 Zor sequin suit: dye suit with glittering accent yoke, cuffs and hip band plus sparkles */ [\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.64, th: 0.012, mat: 'dye' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.64, th: 0.01, mat: 'dye' },\r\n    { zone: 'torso', yLo: 1.46, yHi: 1.64, th: 0.018, mat: 'accent' },\r\n    { zone: 'sleeves', yLo: 1.02, yHi: 1.12, th: 0.018, mat: 'accent' },\r\n    { zone: 'torso', yLo: 1.0, yHi: 1.08, th: 0.018, mat: 'accent' },\r\n    { mark: 'gem', at: [0.05, 1.34, 0.115], group: 'body', mat: 'white', mirror: true },\r\n    { mark: 'gem', at: [0.1, 1.26, 0.11], group: 'body', mat: 'white', mirror: true },\r\n    { mark: 'gem', at: [0.04, 1.18, 0.11], group: 'body', mat: 'white', mirror: true },\r\n    { mark: 'dot', at: [0.235, 1.3, 0.07], group: 'arm', mat: 'white', mirror: true },\r\n  ],\r\n];\r\n","pod/gala-rig/designs/weapons.mjs":"// Gala weapons designs (authored by local gpt-oss-20b card, reviewed).\r\nexport default (() => {\r\nconst grip = (lo = -0.14, hi = 0.1, w = 0.034) => ({ box: [-w, lo, -w, w, hi, w], mat: 'dark' });\r\nconst ring = (y, r, h = 0.03, mat = 'energy', rz = r) => ({ loft: [[y, r, rz, 0.7], [y + h, r, rz, 0.7]], mat, seg: 10 });\r\nconst orb = (y, r, mat = 'glass', cx = 0, cz = 0) => ({ loft: [[y - r, 0.01, 0.01], [y - r * 0.7, r * 0.8, r * 0.8], [y, r, r], [y + r * 0.7, r * 0.8, r * 0.8], [y + r, 0.01, 0.01]].map(([a, b, c]) => [a, b, c, 1, cz]), cx, mat, seg: 10 });\r\nconst circle = (cx, cy, r, tr, n = 16, z = 0) => Array.from({ length: n + 1 }, (_, i) => [cx + Math.cos(i / n * 6.2832) * r, cy + Math.sin(i / n * 6.2832) * r, z, tr]);\r\nconst disc = (cy, R, rz, mat, cx = 0, n = 10) => ({ loft: Array.from({ length: n + 1 }, (_, i) => { const a = i / n * Math.PI, y = cy + Math.cos(a) * R; return [y, Math.max(0.012, Math.sin(a) * R), rz, 0.8]; }).reverse(), cx, mat, seg: 16 });\r\nconst shaft = (lo, hi, r = 0.032) => ({ tube: [[0, lo, 0, r], [0, hi, 0, r]], mat: 'shell', sides: 6 });\r\nreturn [\r\n  /* 0 rapier · Plasma blade */ { len: 1.0, ring: 0.03, carry: 'up', pieces: [\r\n    grip(-0.16, 0.1, 0.04), orb(-0.2, 0.05, 'shell'), { loft: [[0.1, 0.15, 0.04, 0.6], [0.15, 0.15, 0.04, 0.6]], mat: 'shell', seg: 8 },\r\n    { loft: [[0.15, 0.09, 0.035, 0.6], [0.6, 0.09, 0.035, 0.6], [0.98, 0.06, 0.03, 0.6], [1.06, 0.005, 0.01, 0.6]], mat: 'shell', seg: 8 },\r\n    { loft: [[0.17, 0.06, 0.05, 0.7], [0.6, 0.06, 0.05, 0.7], [0.94, 0.04, 0.04, 0.7], [1.02, 0.004, 0.01, 0.7]], mat: 'energy', seg: 8 },\r\n    { box: [-0.015, 0.2, -0.05, 0.015, 0.9, 0.05], mat: 'white' } ] },\r\n  /* 1 greatsword · Ion cleaver */ { len: 1.3, ring: 0.034, carry: 'up', pieces: [\r\n    grip(-0.2, 0.1, 0.04), orb(-0.24, 0.06, 'shell'), { box: [-0.2, 0.1, -0.05, 0.2, 0.18, 0.05], mat: 'shell' },\r\n    { box: [-0.19, 0.12, 0.05, -0.15, 0.2, 0.08], mat: 'energy' }, { box: [0.15, 0.12, 0.05, 0.19, 0.2, 0.08], mat: 'energy' },\r\n    { loft: [[0.18, 0.09, 0.03, 0.5], [0.8, 0.11, 0.03, 0.5], [1.15, 0.11, 0.03, 0.5], [1.32, 0.01, 0.01, 0.5]], mat: 'shell', seg: 8 },\r\n    { loft: [[0.22, 0.04, 0.045, 0.5], [0.8, 0.05, 0.045, 0.5], [1.2, 0.04, 0.04, 0.5], [1.28, 0.005, 0.01, 0.5]], mat: 'energy', seg: 8 } ] },\r\n  /* 2 dagger · Phase dagger */ { len: 0.6, ring: 0.03, carry: 'up', pieces: [\r\n    grip(-0.14, 0.06, 0.04), orb(-0.17, 0.05, 'energy'), { box: [-0.12, 0.06, -0.04, 0.12, 0.12, 0.04], mat: 'shell' },\r\n    { loft: [[0.12, 0.08, 0.03, 0.5], [0.4, 0.075, 0.03, 0.5], [0.62, 0.005, 0.01, 0.5]], mat: 'shell', seg: 8 },\r\n    { loft: [[0.14, 0.04, 0.04, 0.5], [0.4, 0.04, 0.04, 0.5], [0.58, 0.004, 0.01, 0.5]], mat: 'energy', seg: 8 },\r\n    { fan: [[0, 0.3, 0.04], [0.14, 0.22, 0.04], [0.1, 0.4, 0.04]], mirror: true, mat: 'glass' } ] },\r\n  /* 3 sabre · Arc pistol */ { len: 0.62, ring: 0.04, carry: 'up', pieces: [\r\n    { box: [-0.05, -0.2, -0.05, 0.05, 0.08, 0.05], mat: 'dark' }, { box: [-0.09, 0.0, -0.09, 0.09, 0.3, 0.09], mat: 'shell' },\r\n    { box: [-0.045, 0.3, -0.05, 0.045, 0.4, 0.05], mat: 'shell' },\r\n    { box: [-0.085, 0.04, 0.09, 0.085, 0.22, 0.12], mat: 'glass' }, { box: [-0.1, 0.1, -0.1, -0.09, 0.25, 0.1], mat: 'energy' },\r\n    { tube: [[-0.05, 0.38, 0, 0.026], [-0.05, 0.62, 0, 0.026]], mat: 'energy', sides: 6 }, { tube: [[0.05, 0.38, 0, 0.026], [0.05, 0.62, 0, 0.026]], mat: 'energy', sides: 6 },\r\n    { box: [-0.05, 0.5, -0.012, 0.05, 0.54, 0.012], mat: 'white' } ] },\r\n  /* 4 axe · Pulse rifle */ { len: 1.02, ring: 0.04, carry: 'up', pieces: [\r\n    { box: [-0.055, -0.34, -0.06, 0.055, 0.06, 0.06], mat: 'dark' }, { box: [-0.08, 0.0, -0.1, 0.08, 0.42, 0.1], mat: 'shell' },\r\n    { box: [-0.04, 0.42, -0.05, 0.04, 0.95, 0.05], mat: 'shell' }, { box: [-0.09, 0.3, 0.1, 0.09, 0.4, 0.13], mat: 'energy' },\r\n    { box: [-0.03, 0.1, 0.1, 0.03, 0.28, 0.17], mat: 'dark' }, { box: [-0.06, 0.55, 0.05, 0.06, 0.62, 0.09], mat: 'energy' }, { box: [-0.06, 0.78, 0.05, 0.06, 0.85, 0.09], mat: 'energy' },\r\n    { box: [-0.1, 0.06, -0.06, -0.08, 0.36, 0.06], mat: 'energy' }, { box: [0.08, 0.06, -0.06, 0.1, 0.36, 0.06], mat: 'energy' },\r\n    { box: [-0.06, 0.95, -0.06, 0.06, 1.02, 0.06], mat: 'energy' } ] },\r\n  /* 5 hammer · Rail cannon */ { len: 1.04, ring: 0.045, carry: 'up', pieces: [\r\n    { box: [-0.09, -0.3, -0.09, 0.09, 0.15, 0.09], mat: 'dark' }, { box: [-0.13, 0.15, -0.14, 0.13, 0.45, 0.14], mat: 'shell' },\r\n    { box: [-0.12, 0.45, -0.045, -0.05, 1.0, 0.12], mat: 'shell' }, { box: [0.05, 0.45, -0.045, 0.12, 1.0, 0.12], mat: 'shell' },\r\n    { box: [-0.05, 0.45, 0.0, 0.05, 1.0, 0.07], mat: 'energy' }, { box: [-0.14, 0.9, -0.06, 0.14, 1.04, 0.14], mat: 'shell' },\r\n    { box: [-0.1, 0.2, 0.14, 0.1, 0.4, 0.17], mat: 'glass' }, { box: [-0.14, 0.3, -0.12, -0.13, 0.42, 0.12], mat: 'energy' } ] },\r\n  /* 6 mace · Tesla emitter */ { len: 1.1, ring: 0.034, carry: 'up', pieces: [\r\n    grip(-0.2, 0.2, 0.04), { tube: [[0, 0.2, 0, 0.04], [0, 0.6, 0, 0.04]], mat: 'shell', sides: 6 }, ring(0.3, 0.07, 0.04, 'energy'),\r\n    orb(0.75, 0.15, 'energy'), orb(0.75, 0.09, 'white'),\r\n    { tube: [[0.1, 0.8, 0, 0.025], [0.2, 0.95, 0, 0.025], [0.12, 1.08, 0, 0.02]], mat: 'edge', sides: 5 },\r\n    { tube: [[-0.1, 0.7, 0, 0.025], [-0.22, 0.6, 0.06, 0.025], [-0.16, 0.48, 0, 0.02]], mat: 'edge', sides: 5 },\r\n    { tube: [[0, 0.88, 0.08, 0.025], [0.04, 1.0, 0.16, 0.025], [0, 1.1, 0.12, 0.02]], mat: 'edge', sides: 5 } ] },\r\n  /* 7 flail · Tether drone */ { len: 1.25, ring: 0.034, carry: 'up', pieces: [\r\n    grip(-0.2, 0.15, 0.04), orb(0.2, 0.06, 'shell'),\r\n    { tube: [[0, 0.22, 0, 0.022], [0.1, 0.45, 0.05, 0.022], [0.04, 0.7, 0.1, 0.022], [0.2, 0.85, 0.1, 0.022]], mat: 'energy', sides: 5 },\r\n    orb(0.98, 0.14, 'shell', 0.22, 0.1), { box: [0.04, 0.94, 0.04, 0.4, 1.02, 0.16], mat: 'shell' }, orb(0.98, 0.07, 'energy', 0.22, 0.22),\r\n    { box: [0.19, 1.08, 0.05, 0.25, 1.22, 0.15], mat: 'dark' }, { fan: [[0.22, 0.98, 0.1], [0.5, 0.9, 0.1], [0.5, 1.06, 0.1]], mat: 'energy' } ] },\r\n  /* 8 spear · Particle lance */ { len: 1.4, ring: 0.03, carry: 'up', pieces: [\r\n    shaft(-0.5, 0.9, 0.045), ring(0.0, 0.07, 0.06, 'dark'), ring(0.6, 0.075, 0.05, 'energy'),\r\n    { loft: [[0.85, 0.06, 0.06, 0.8], [0.92, 0.15, 0.07, 0.7], [1.15, 0.12, 0.065, 0.7], [1.4, 0.005, 0.01, 0.7]], mat: 'energy', seg: 8 },\r\n    { loft: [[0.92, 0.06, 0.09, 0.7], [1.15, 0.045, 0.09, 0.7], [1.3, 0.004, 0.03, 0.7]], mat: 'white', seg: 8 },\r\n    { loft: [[-0.45, 0.07, 0.07], [-0.52, 0.1, 0.1], [-0.59, 0.01, 0.01]], mat: 'energy', seg: 8 } ] },\r\n  /* 9 trident · Tri-beam fork */ { len: 1.3, ring: 0.03, carry: 'up', pieces: [\r\n    shaft(-0.45, 0.7, 0.045), { box: [-0.26, 0.66, -0.06, 0.26, 0.78, 0.06], mat: 'shell' },\r\n    { box: [-0.26, 0.78, -0.05, -0.18, 0.95, 0.05], mat: 'shell' }, { box: [0.18, 0.78, -0.05, 0.26, 0.95, 0.05], mat: 'shell' },\r\n    { loft: [[0.78, 0.07, 0.06, 0.7], [1.05, 0.07, 0.06, 0.7], [1.32, 0.005, 0.01, 0.7]], mat: 'energy', seg: 8 },\r\n    { loft: [[0.9, 0.06, 0.055, 0.7], [1.08, 0.06, 0.055, 0.7], [1.2, 0.005, 0.01, 0.7]], cx: -0.22, mat: 'energy', seg: 8 },\r\n    { loft: [[0.9, 0.06, 0.055, 0.7], [1.08, 0.06, 0.055, 0.7], [1.2, 0.005, 0.01, 0.7]], cx: 0.22, mat: 'energy', seg: 8 },\r\n    ring(0.1, 0.07, 0.05, 'energy') ] },\r\n  /* 10 halberd · Rocket pod */ { len: 1.3, ring: 0.03, carry: 'up', pieces: [\r\n    shaft(-0.5, 0.7), { box: [-0.16, 0.5, -0.12, 0.16, 1.0, 0.12], mat: 'shell' },\r\n    { loft: [[0.9, 0.05, 0.05], [1.0, 0.05, 0.05], [1.15, 0.004, 0.004]], cx: -0.09, mat: 'energy', seg: 8 },\r\n    { loft: [[1.0, 0.05, 0.05], [1.15, 0.004, 0.004]], cx: 0.09, mat: 'energy', seg: 8 },\r\n    { box: [-0.14, 0.52, 0.12, 0.14, 0.98, 0.15], mat: 'dark' },\r\n    { box: [-0.14, 0.7, 0.15, -0.04, 0.78, 0.19], mat: 'energy' }, { box: [0.04, 0.7, 0.15, 0.14, 0.78, 0.19], mat: 'energy' }, { box: [-0.14, 0.82, 0.15, -0.04, 0.9, 0.19], mat: 'energy' }, { box: [0.04, 0.82, 0.15, 0.14, 0.9, 0.19], mat: 'energy' } ] },\r\n  /* 11 scythe · Gravity reaper */ { len: 1.3, ring: 0.03, carry: 'up', pieces: [\r\n    shaft(-0.5, 1.0, 0.042), ring(0.1, 0.065, 0.06, 'dark'),\r\n    { box: [-0.06, 0.95, -0.06, 0.12, 1.05, 0.06], mat: 'shell' },\r\n    { tube: [[0.05, 1.1, 0, 0.07], [0.3, 1.28, 0, 0.075], [0.58, 1.2, 0, 0.06], [0.76, 0.98, 0, 0.04], [0.82, 0.8, 0, 0.015]], mat: 'energy', sides: 6 },\r\n    { tube: [[0.05, 1.0, 0, 0.04], [0.3, 1.16, 0, 0.05], [0.56, 1.1, 0, 0.04]], mat: 'shell', sides: 5 },\r\n    orb(1.12, 0.09, 'glass') ] },\r\n  /* 12 bow · Photon bow */ { len: 1.15, ring: 0.03, carry: 'up', pieces: [\r\n    grip(-0.12, 0.12, 0.045),\r\n    { tube: [[0, 0.1, 0, 0.045], [0.1, 0.3, 0, 0.04], [0.22, 0.6, 0, 0.035], [0.26, 0.85, 0, 0.03], [0.18, 1.15, 0, 0.02]], mat: 'shell', sides: 6 },\r\n    { tube: [[0, -0.1, 0, 0.045], [0.1, -0.2, 0, 0.04], [0.22, -0.35, 0, 0.035], [0.26, -0.48, 0, 0.03], [0.18, -0.58, 0, 0.02]], mat: 'shell', sides: 6 },\r\n    { tube: [[0.22, 0.45, 0, 0.026], [0.25, 0.7, 0, 0.026], [0.22, 0.95, 0, 0.026]], mat: 'energy', sides: 5 },\r\n    { tube: [[0.18, 1.15, 0, 0.018], [-0.1, 0.3, 0, 0.018], [0.18, -0.58, 0, 0.018]], mat: 'energy', sides: 4 },\r\n    { box: [-0.04, 0.0, -0.05, 0.08, 0.2, 0.05], mat: 'energy' } ] },\r\n  /* 13 crossbow · Gauss rifle */ { len: 1.0, ring: 0.045, carry: 'up', pieces: [\r\n    { box: [-0.06, -0.4, -0.07, 0.06, 0.08, 0.07], mat: 'dark' }, { box: [-0.09, 0.0, -0.1, 0.09, 0.4, 0.1], mat: 'shell' },\r\n    { tube: [[0, 0.4, 0, 0.05], [0, 1.0, 0, 0.05]], mat: 'shell', sides: 6 },\r\n    ring(0.5, 0.1, 0.05), ring(0.65, 0.1, 0.05), ring(0.8, 0.1, 0.05),\r\n    { box: [-0.1, 0.92, -0.1, 0.1, 1.0, 0.1], mat: 'energy' }, { box: [-0.1, 0.06, 0.1, 0.1, 0.26, 0.14], mat: 'glass' }, { box: [-0.03, 0.2, -0.17, 0.03, 0.34, -0.1], mat: 'dark' } ] },\r\n  /* 14 chakram · Orbit disc */ { len: 0.9, ring: 0.026, carry: 'up', pieces: [\r\n    grip(-0.2, 0.2, 0.04), { box: [-0.25, 0.22, -0.035, 0.25, 0.3, 0.035], mat: 'shell' },\r\n    disc(0.52, 0.34, 0.04, 'energy'), disc(0.52, 0.22, 0.07, 'shell'), disc(0.52, 0.1, 0.09, 'white'),\r\n    { fan: [[0, 0.52, 0.09], [0.1, 0.3, 0.09], [0.05, 0.14, 0.09]], mat: 'dark' } ] },\r\n  /* 15 gauntlets · Power gauntlets */ { len: 0.3, ring: 0.06, carry: 'worn', pieces: [\r\n    { loft: [[-0.4, 0.13, 0.12, 0.8], [-0.2, 0.15, 0.14, 0.8], [0.0, 0.13, 0.12, 0.8]], mat: 'shell', seg: 10 },\r\n    ring(-0.3, 0.16, 0.05, 'energy', 0.15), ring(-0.1, 0.16, 0.05, 'energy', 0.15),\r\n    { loft: [[0.0, 0.16, 0.15, 0.7], [0.18, 0.19, 0.18, 0.6], [0.32, 0.15, 0.15, 0.6]], mat: 'shell', seg: 10 },\r\n    { box: [-0.15, 0.16, 0.15, 0.15, 0.3, 0.22], mat: 'energy' }, { box: [-0.06, 0.02, 0.17, 0.06, 0.12, 0.23], mat: 'white' },\r\n    { box: [-0.2, 0.2, -0.03, -0.15, 0.36, 0.08], mat: 'dark' }, { box: [0.15, 0.2, -0.03, 0.2, 0.36, 0.08], mat: 'dark' } ] },\r\n  /* 16 staff · Gravity rod */ { len: 1.4, ring: 0.03, carry: 'up', pieces: [\r\n    shaft(-0.55, 0.85, 0.045), ring(0.0, 0.07, 0.06, 'dark'), ring(0.45, 0.07, 0.05, 'energy'), ring(-0.4, 0.07, 0.05, 'energy'),\r\n    { tube: circle(0, 1.12, 0.22, 0.05, 14, 0), mat: 'shell', sides: 5 }, { tube: circle(0, 1.12, 0.22, 0.03, 14, 0.05), mat: 'energy', sides: 4 },\r\n    orb(1.12, 0.13, 'glass'), orb(1.12, 0.07, 'white'), { box: [-0.07, 0.85, -0.07, 0.07, 0.9, 0.07], mat: 'shell' } ] },\r\n  /* 17 wand · Sonic disruptor */ { len: 0.8, ring: 0.03, carry: 'up', pieces: [\r\n    { box: [-0.045, -0.2, -0.045, 0.045, 0.3, 0.045], mat: 'dark' }, ring(0.0, 0.06, 0.04, 'energy'), ring(0.15, 0.06, 0.04, 'energy'),\r\n    { loft: [[0.3, 0.05, 0.05, 0.8], [0.45, 0.09, 0.09, 0.8], [0.62, 0.17, 0.17, 0.8], [0.7, 0.2, 0.2, 0.8]], mat: 'shell', seg: 12, caps: false },\r\n    { loft: [[0.5, 0.06, 0.06, 0.8], [0.66, 0.15, 0.15, 0.8]], mat: 'energy', seg: 12, caps: false },\r\n    orb(0.52, 0.07, 'white'), ring(0.68, 0.2, 0.025, 'energy') ] },\r\n  /* 18 tome · Nanite hive */ { len: 0.5, ring: 0.04, carry: 'up', pieces: [\r\n    { box: [-0.14, -0.06, -0.07, 0.14, 0.5, 0.07], mat: 'shell' }, { box: [-0.12, -0.04, -0.075, 0.12, 0.48, 0.075], mat: 'dark' },\r\n    { box: [-0.14, -0.06, -0.08, -0.1, 0.5, 0.08], mat: 'shell' }, { box: [-0.12, 0.0, 0.07, 0.12, 0.44, 0.09], mat: 'shell' },\r\n    orb(0.22, 0.1, 'energy', 0, 0.1), orb(0.22, 0.05, 'white', 0, 0.1),\r\n    { box: [-0.1, 0.34, 0.09, -0.04, 0.4, 0.12], mat: 'energy' }, { box: [0.04, 0.34, 0.09, 0.1, 0.4, 0.12], mat: 'energy' }, { box: [-0.1, 0.04, 0.09, -0.04, 0.1, 0.12], mat: 'energy' }, { box: [0.04, 0.04, 0.09, 0.1, 0.1, 0.12], mat: 'energy' } ] },\r\n  /* 19 cannon · Plasma cannon */ { len: 1.17, ring: 0.05, carry: 'up', pieces: [\r\n    { box: [-0.1, -0.35, -0.1, 0.1, 0.1, 0.1], mat: 'dark' }, { box: [-0.19, 0.1, -0.19, 0.19, 0.4, 0.19], mat: 'shell' },\r\n    { tube: [[0, 0.4, 0, 0.12], [0, 1.1, 0, 0.12]], mat: 'shell', sides: 8 }, ring(0.5, 0.15, 0.06), ring(0.7, 0.15, 0.06), ring(0.9, 0.15, 0.06),\r\n    { loft: [[1.1, 0.15, 0.15], [1.16, 0.15, 0.15]], mat: 'energy', seg: 10 }, { loft: [[1.1, 0.07, 0.07], [1.17, 0.07, 0.07]], mat: 'white', seg: 8 },\r\n    orb(0.25, 0.12, 'energy', 0, 0.19), { box: [-0.2, 0.15, -0.17, -0.19, 0.38, 0.17], mat: 'energy' }, { box: [0.19, 0.15, -0.17, 0.2, 0.38, 0.17], mat: 'energy' } ] },\r\n];\r\n})();\r\n","pod/gala-rig/dfo/animate.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/animate.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The animation runtime, extracted from the viewer (engine-world\r\n// integration slice 1): renderer-AGNOSTIC. animateTarget writes\r\n// T.pos from T.base under gait + pose + clip state and sets T.dirty;\r\n// the host (THREE viewer today, the raw-GL engine next) flushes\r\n// dirty targets to its own buffers. createAnimContext binds the\r\n// joint constants once from the body geometry - identical math to\r\n// the template's former locals (grpRange/grpMeanX).\r\n\r\nexport function rotX(y, z, pivotY, ang) { const c=Math.cos(ang), sn=Math.sin(ang); const dy=y-pivotY; return [pivotY + c*dy - sn*z, sn*dy + c*z]; }\r\nexport function rotZ(x, y, px, py, ang) { const c=Math.cos(ang), s=Math.sin(ang), dx=x-px, dy=y-py; return [px + c*dx - s*dy, py + s*dx + c*dy]; }\r\nexport function rotY(x, z, px, pz, ang) { const c=Math.cos(ang), s=Math.sin(ang), dx=x-px, dz=z-pz; return [px + c*dx + s*dz, pz - s*dx + c*dz]; }\r\n\r\nexport const POSE_L = { strideAmp: 0, counterFrac: 0, kneeBase: 0, kneeAmp: 0, elbowBase: 0, elbowAmp: 0, lean: 0 }; // zero loco for the posed idle\r\n/** Additive pose composition: base + clip deltas per channel.\r\n *  Ported VERBATIM from the viewer's effectivePose combinator (the\r\n *  viewer template keeps its inlined copy until the next template\r\n *  regeneration - this is the source now). Clips zero the gait\r\n *  couplings so a strike owns the arms outright. */\r\nconst ZERO_ARM = { sw: 0, bd: 0, spread: 0, handRoll: 0, handPitch: 0, handYaw: 0 };\r\nexport function combinePose(base, d) {\r\n  if (!d) return base;\r\n  const b = base || { lean: 0, armL: ZERO_ARM, armR: ZERO_ARM };\r\n  const limb = (k, zero) => { const has = b[k] || d[k]; if (!has) return b[k]; const o = { ...(b[k] || zero) }; const dd = d[k]; if (dd) for (const c in dd) o[c] = (o[c] || 0) + dd[c]; return o; };\r\n  return { ...b, gaitArm: 0, gaitElbow: 0, runElbow: 0,\r\n    lean: (b.lean || 0) + (d.lean || 0), twist: (b.twist || 0) + (d.twist || 0), headPitch: (b.headPitch || 0) + (d.headPitch || 0),\r\n    rootX: (b.rootX || 0) + (d.rootX || 0), rootY: (b.rootY || 0) + (d.rootY || 0), rootZ: (b.rootZ || 0) + (d.rootZ || 0),\r\n    armL: limb('armL', ZERO_ARM), armR: limb('armR', ZERO_ARM),\r\n    legL: limb('legL', { sw: 0, bd: 0 }), legR: limb('legR', { sw: 0, bd: 0 }) };\r\n}\r\n\r\nexport const IDLE = { strideAmp: 0.05, cadence: 1.7, counterFrac: 1.0, bobAmp: 0.006, kneeAmp: 0.12, kneeBase: 0.0, elbowAmp: 0.12, elbowBase: 0.0, lean: 0 }; // standing sway (canonicalized from interiorContext's pre-animate.js inline loco)\r\nexport const WALK = { strideAmp: 0.44, cadence: 6.0, counterFrac: 1.1, bobAmp: 0.024, kneeAmp: 0.9, kneeBase: 0.0,  elbowAmp: 0.35, elbowBase: 0.18, lean: 0.05, kneeR: 4, elbowR: 2 };\r\nexport const RUN  = { strideAmp: 0.60, cadence: 9.6, counterFrac: 0.95, bobAmp: 0.075, kneeAmp: 1.5, kneeBase: 0.18, elbowAmp: 0.55, elbowBase: 1.42, lean: 0.42, headPitch: -0.30, kneeR: 4, elbowR: 2 };\r\n\r\nexport function createAnimContext({ basePos, vgrp, armX, wristY, neckY }) {\r\n  const grpRange = (g) => { let mn=Infinity, mx=-Infinity; for (let i=0;i<vgrp.length;i++) if (vgrp[i]===g) { const y=basePos[i*3+1]; if(y<mn)mn=y; if(y>mx)mx=y; } return [mn,mx]; };\r\n  const grpMeanX = (g) => { let sx=0, n=0; for (let i=0;i<vgrp.length;i++) if (vgrp[i]===g) { sx+=basePos[i*3]; n++; } return n ? sx/n : 0; };\r\n  const [ankL, hipY] = grpRange(4);\r\n  const [wristL, shoulderY] = grpRange(2);\r\n  return {\r\n    // ankleY is the bottom of the leg group - already measured for\r\n    // kneeY, and the drape colliders need it by name. Additive: no\r\n    // existing reader of this context looks at it.\r\n    hipY, shoulderY, ankleY: ankL,\r\n    kneeY: hipY - 0.52*(hipY-ankL),\r\n    elbowY: shoulderY - 0.50*(shoulderY-wristL),\r\n    wristY, armX, neckY,\r\n    armRootX: { 2: grpMeanX(2), 3: grpMeanX(3) },\r\n  };\r\n}\r\n\r\nexport function animateTarget(ctx, T, wt, L, bob, pose, moving) {\r\n  const { armRootX, wristY, elbowY, kneeY, hipY, shoulderY, armX, neckY } = ctx;\r\n\r\n  const P = T.pos, B = T.base, VG = T.vgrp;\r\n  const leanPivot = hipY + 0.10;                     // lean the upper body about the low spine\r\n  const lean = (L.lean || 0) + (pose && pose.lean || 0);   // pose crouch adds to the gait's own lean\r\n  const twist = (pose && pose.twist) || 0;                 // attack body commitment (rotY about the spine)\r\n  const headPitch = (L.headPitch || 0) + (pose && pose.headPitch || 0); // run look-up + attack follow\r\n  const rX = (pose && pose.rootX) || 0, rY = (pose && pose.rootY) || 0, rZ = (pose && pose.rootZ) || 0; // ROOT MOTION: whole-body translation\r\n  const held = !!T.held;                                   // gripped rigid object: whole hand chain\r\n  const applyLimb = (g, phase, isLeg, limbKey) => {\r\n    const pp = pose && pose[limbKey];\r\n    let swing, bend;\r\n    const gaitSwing = -L.strideAmp * (isLeg ? 1 : L.counterFrac) * Math.sin(wt + phase);\r\n    const gaitBend  = isLeg ? L.kneeBase  + L.kneeAmp  * Math.max(0, Math.sin(wt + phase + 1.2))\r\n                            : L.elbowBase + L.elbowAmp * (0.5 + 0.5*Math.sin(wt + phase - 0.6));\r\n    if (pp && !isLeg) {   // posed arms: pose is the BASE; moving adds runElbow + a damped gait pump (alive, grip kept)\r\n      swing = pp.sw + (moving ? (pose.gaitArm ?? 0) * gaitSwing : 0);\r\n      bend  = pp.bd + (moving ? (pose.runElbow ?? 0) + (pose.gaitElbow ?? 0) * (gaitBend - L.elbowBase - 0.5 * L.elbowAmp) : 0);\r\n    } else if (pp && !moving) { swing = pp.sw; bend = pp.bd; }   // posed legs: stance at rest\r\n    else if (moving) { swing = gaitSwing; bend = gaitBend; }     // gait\r\n    else { swing = 0; bend = 0; }\r\n    const jointY = isLeg ? kneeY : elbowY, rootY = isLeg ? hipY : shoulderY, bendSign = isLeg ? 1 : -1;\r\n    const rollA = pp && !isLeg ? (g === 2 ? -1 : 1) * (pp.handRoll || 0) : 0;\r\n    const pitchA = pp && !isLeg ? (pp.handPitch || 0) : 0;\r\n    const yawA = pp && !isLeg ? (g === 2 ? -1 : 1) * (pp.handYaw || 0) : 0;\r\n    const sprA = pp && !isLeg ? (g === 2 ? -1 : 1) * (pp.spread || 0) : 0;\r\n    const rollH = pp && !isLeg ? (g === 2 ? -1 : 1) * (pp.roll || 0) : 0;   // humeral rotation\r\n    const wpx = g === 2 ? -armX : armX;\r\n    for (let i=0;i<VG.length;i++) if (VG[i]===g) {\r\n      let x = B[i*3], y = B[i*3+1], z = B[i*3+2]; const by = y;   // joint gates read REST y\r\n      if (!isLeg && (held || by < wristY)) {                      // hand chain (pose joints), forearm-axis pivots\r\n        if (rollA)  [x,z] = rotY(x, z, wpx, 0, rollA);\r\n        if (pitchA) [y,z] = rotX(y, z, wristY, pitchA);\r\n        if (yawA)   [x,y] = rotZ(x, y, wpx, wristY, yawA);\r\n      }\r\n      if (held || by < jointY) [y,z] = rotX(y, z, jointY, bendSign*bend);   // knee / elbow\r\n      if (rollH && (held || by < jointY)) [x,z] = rotY(x, z, wpx, 0, rollH); // HUMERAL roll: the bent forearm folds about the upper-arm axis\r\n      [y,z] = rotX(y, z, rootY, swing);                            // hip / shoulder swing\r\n      if (!isLeg && sprA) [x,y] = rotZ(x, y, armRootX[g], shoulderY, sprA); // abduction\r\n      if (!isLeg && twist) [x,z] = rotY(x, z, 0, 0, twist);        // body commitment (arms + held follow the spine)\r\n      if (!isLeg) [y,z] = rotX(y, z, leanPivot, lean);             // arms lean with the torso\r\n      P[i*3] = x + rX; P[i*3+1] = y + bob + rY; P[i*3+2] = z + rZ;\r\n    }\r\n  };\r\n  applyLimb(4, 0, true, 'legL'); applyLimb(5, Math.PI, true, 'legR');   // legs\r\n  applyLimb(2, Math.PI, false, 'armL'); applyLimb(3, 0, false, 'armR'); // arms\r\n  for (let i=0;i<VG.length;i++) if (VG[i]===0 || VG[i]===1) {      // torso + head: twist + lean + bob (head adds pitch)\r\n    let x = B[i*3], y = B[i*3+1], z = B[i*3+2];\r\n    if (VG[i] === 1 && headPitch) [y,z] = rotX(y, z, neckY, headPitch); // look up/down about the neck\r\n    if (twist) [x,z] = rotY(x, z, 0, 0, twist);\r\n    [y,z] = rotX(y, z, leanPivot, lean);\r\n    P[i*3] = x + rX; P[i*3+1] = y + bob + rY; P[i*3+2] = z + rZ;\r\n  }\r\n  T.dirty = true;   // renderer-agnostic: the host flushes dirty targets to its own buffers\r\n}\r\n","pod/gala-rig/dfo/arachnid.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/arachnid.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// AN ARACHNID: eight legs, and no spine to hang them off.\r\n//\r\n// The quadruped builder assumes a BACKBONE — a barrel running nose to\r\n// rump with a leg near each corner — and every animal it makes is a\r\n// variation on that. A spider has no such axis. Its legs radiate from a\r\n// single joint in a ring, its body is two lumps rather than a tube, and\r\n// the whole thing is wider than it is long.\r\n//\r\n// So this is a different builder rather than more parameters on the old\r\n// one. The test of whether a mechanism is right is what happens when\r\n// something does not fit it: the beasts stretched from a rat to a bear\r\n// on numbers alone, and an arachnid does not stretch at all.\r\n//\r\n// A SCORPION IS THE SAME PLAN WITH THINGS ADDED. Claws forward, and a\r\n// tail that arches up and over rather than dragging — which is why the\r\n// tail here is a chain of shrinking segments on an arc, and not the\r\n// beast tail's two boxes.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'body'.\r\n\r\nimport { HSCALE, quadder, boxer } from './pieceLoft.mjs';\r\n\r\n/** Dark chitin. Bright enough to keep its silhouette — the wereboar\r\n *  taught that a dark thing on a dark ground has no shape at all. */\r\nexport const CHITIN_RAMP = [\r\n  [44, 38, 34],\r\n  [70, 60, 52],\r\n  [98, 84, 72],\r\n  [128, 110, 94],\r\n  [158, 138, 118],\r\n  [186, 166, 144],\r\n];\r\n\r\n// Chitin boxes: a darker default side and brighter flanks than the beasts'.\r\nconst box = boxer({ side: 0.64, flank: 0.86 });\r\n\r\n/** A limb segment between two points, as a thin box aligned to the run. */\r\nfunction limb(quad, ax, ay, az, bx, by, bz, r, shade) {\r\n  const dx = bx - ax,\r\n    dz = bz - az;\r\n  const L = Math.hypot(dx, dz) || 1e-6;\r\n  // Perpendicular in the ground plane, so the segment has width across\r\n  // its own run rather than across the world.\r\n  const px = (-dz / L) * r,\r\n    pz = (dx / L) * r;\r\n  quad([ax - px, ay + r, az - pz], [ax + px, ay + r, az + pz], [bx + px, by + r, bz + pz], [bx - px, by + r, bz - pz], shade);\r\n  quad([ax + px, ay - r, az + pz], [ax - px, ay - r, az - pz], [bx - px, by - r, bz - pz], [bx + px, by - r, bz + pz], shade * 0.4);\r\n  quad([ax + px, ay + r, az + pz], [ax + px, ay - r, az + pz], [bx + px, by - r, bz + pz], [bx + px, by + r, bz + pz], shade * 0.72);\r\n  quad([ax - px, ay - r, az - pz], [ax - px, ay + r, az - pz], [bx - px, by + r, bz - pz], [bx - px, by - r, bz - pz], shade * 0.72);\r\n}\r\n\r\n/**\r\n * @param {number[][]} ramp\r\n * @param {object} s\r\n *   span     — how far the legs reach from the body\r\n *   ride     — how high the body sits off the ground\r\n *   thorax   — the front lump, where the legs join\r\n *   abdomen  — the back lump; a spider's is the bigger of the two\r\n *   legR     — leg thickness\r\n *   arch     — how high the knee rises above the body (0 splays flat)\r\n *   claws    — 0 none, 1 a scorpion's pair\r\n *   sting    — 0 none; otherwise how far the tail arches over\r\n */\r\nexport function buildArachnid(ramp = CHITIN_RAMP, s = {}) {\r\n  const {\r\n    span = 0.34,\r\n    ride = 0.16,\r\n    thorax = 0.1,\r\n    abdomen = 0.13,\r\n    legR = 0.016,\r\n    arch = 0.5,\r\n    claws = 0,\r\n    sting = 0,\r\n    legPairs = 4,\r\n  } = s;\r\n\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n  const y = ride * HSCALE;\r\n\r\n  // ── THE TWO LUMPS ───────────────────────────────────────────────\r\n  // Cephalothorax forward, abdomen behind and larger. One box would be\r\n  // a beetle.\r\n  box(quad, -thorax, y - thorax * 0.7, 0, thorax, y + thorax * 0.7, thorax * 1.7, 0.92, 0.62);\r\n  box(\r\n    quad,\r\n    -abdomen,\r\n    y - abdomen * 0.75,\r\n    -abdomen * 2.0,\r\n    abdomen,\r\n    y + abdomen * 0.8,\r\n    -abdomen * 0.1,\r\n    0.88,\r\n    0.58,\r\n  );\r\n\r\n  // ── EIGHT LEGS, RADIALLY ────────────────────────────────────────\r\n  // Four a side, fanned from the thorax. Each is TWO segments with a\r\n  // knee above the body: a spider's legs go up before they come down,\r\n  // and legs that run straight out read as a starfish.\r\n  // LEGS ARE OPTIONAL, because a dreugh borrows the CLAWS and nothing\r\n  // else. Reusing this builder for them brought eight small spider legs\r\n  // along with the pair that was wanted — a crustacean standing on its\r\n  // own legs with a spider's underneath it.\r\n  const n = legPairs;\r\n  for (const side of [-1, 1]) {\r\n    for (let i = 0; i < n; i++) {\r\n      // Fan from forward-ish to backward-ish along the body's side.\r\n      // AUDIT 68 S06-arachnid-legs-point-symmetric: the angle is measured\r\n      // off the lateral axis and MIRRORED by side (43deg ahead of lateral\r\n      // for the front pair to 66deg behind it for the rear). It was a\r\n      // 180deg turn of the right side, so the front legs crossed the body.\r\n      const t = n > 1 ? i / (n - 1) : 0.5;\r\n      const ang = 0.75 - t * 1.9;\r\n      const dirX = side * Math.cos(ang);\r\n      const dirZ = Math.sin(ang);\r\n      const rootX = side * thorax * 0.8;\r\n      const rootZ = thorax * (1.1 - t * 1.1);\r\n      const kneeX = rootX + dirX * span * 0.5;\r\n      const kneeZ = rootZ + dirZ * span * 0.5;\r\n      const kneeY = y + span * arch * 0.55;\r\n      const footX = rootX + dirX * span;\r\n      const footZ = rootZ + dirZ * span;\r\n      limb(quad, rootX, y, rootZ, kneeX, kneeY, kneeZ, legR, 0.8);\r\n      limb(quad, kneeX, kneeY, kneeZ, footX, 0.01 * HSCALE, footZ, legR * 0.75, 0.62);\r\n    }\r\n  }\r\n\r\n  // ── CLAWS ───────────────────────────────────────────────────────\r\n  if (claws > 0) {\r\n    const cr = thorax * 0.34 * claws;\r\n    for (const side of [-1, 1]) {\r\n      const x = side * thorax * 1.1;\r\n      limb(quad, side * thorax * 0.7, y, thorax * 1.4, x, y, thorax * 2.4, legR * 1.4, 0.74);\r\n      box(quad, x - cr, y - cr * 0.7, thorax * 2.3, x + cr, y + cr * 0.7, thorax * 2.3 + cr * 2.4, 0.9, 0.6);\r\n    }\r\n  }\r\n\r\n  // The sting is NOT built here any more. A scorpion's tail is the only\r\n  // part of it that moves on its own — the body rocks back and the tail\r\n  // comes over the top — and a piece welded into the body can only move\r\n  // when the body does. See buildSting below.\r\n\r\n  return faces;\r\n}\r\n\r\n/**\r\n * THE STING, ON ITS OWN.\r\n *\r\n * It was part of buildArachnid, which meant the only way to animate it\r\n * was to move the whole scorpion — so a sting looked like the animal\r\n * rocking backwards, and the one part of it anybody actually watches did\r\n * nothing.\r\n *\r\n * A chain of shrinking segments on an arc, up and over the back, ending\r\n * in the barb. `strike` runs 0 (coiled over the back) to 1 (thrown\r\n * forward past the head), which is the whole motion of a scorpion and\r\n * the reason this had to come out of the body.\r\n *\r\n * @param {number[][]} ramp\r\n * @param {object} s\r\n *   sting   how far the tail reaches\r\n *   abdomen where on the body it is rooted\r\n *   ride    body height\r\n *   legR    segment thickness\r\n *   strike  0..1, coiled to thrown\r\n */\r\nexport function buildSting(ramp = CHITIN_RAMP, s = {}) {\r\n  const { sting = 0.5, abdomen = 0.1, ride = 0.11, legR = 0.015, strike = 0 } = s;\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n  const y = ride * HSCALE;\r\n\r\n  const segs = 5;\r\n  let px = 0,\r\n    py = y + abdomen * 0.4,\r\n    pz = -abdomen * 1.9;\r\n  for (let i = 1; i <= segs; i++) {\r\n    const t = i / segs;\r\n    // COILED: the arc curls up and over the back. THROWN: it straightens\r\n    // and drives forward, which is what puts the barb past the head.\r\n    const coil = t * Math.PI * 0.72;\r\n    const thrown = t * Math.PI * 0.16;\r\n    const a = coil + (thrown - coil) * strike;\r\n    const reach = 1 + strike * 0.5;\r\n    const nx = 0;\r\n    const ny = py + Math.sin(a) * sting * 0.34 * (1 - strike * 0.55);\r\n    const nz = pz + ((1 - Math.cos(a)) * sting * 0.2 + sting * 0.06) * reach + strike * sting * 0.42 * t;\r\n    const r = legR * (2.1 - t * 1.2);\r\n    quad([px - r, py, pz + r], [px + r, py, pz + r], [nx + r, ny, nz + r], [nx - r, ny, nz + r], 0.9);\r\n    quad([px + r, py, pz - r], [px - r, py, pz - r], [nx - r, ny, nz - r], [nx + r, ny, nz - r], 0.46);\r\n    quad([px + r, py, pz + r], [px + r, py, pz - r], [nx + r, ny, nz - r], [nx + r, ny, nz + r], 0.7);\r\n    quad([px - r, py, pz - r], [px - r, py, pz + r], [nx - r, ny, nz + r], [nx - r, ny, nz - r], 0.7);\r\n    px = nx;\r\n    py = ny;\r\n    pz = nz;\r\n  }\r\n  // The barb.\r\n  const br = legR * 1.4;\r\n  quad([px - br, py - br, pz], [px + br, py - br, pz], [px + br, py + br, pz + sting * 0.16], [px - br, py + br, pz + sting * 0.16], 1);\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/armorSet.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/armorSet.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Armour on the neutral rig, 1:1 with the item DB. Body-hugging pieces\r\n// (cuirass, greaves, gauntlets, boots) thicken the body's own surface\r\n// via displace ZONES - same as clothing, but a metal/leather material\r\n// and thicker, so armour sits OVER clothing. Standoff pieces (pauldrons,\r\n// helm) are separate meshes - see pieces/. Shields are held items.\r\n//\r\n// Material follows the armour family (paperdollArt MATERIAL_FAMILY):\r\n// Plate -> steel, Chain -> mail, Leather -> leather.\r\n// MYR5: the three armour families, inlined (paperdollArt reads ARENA2 archives).\r\nconst MATERIAL_FAMILY = { Leather: 0, Chain: 1, Plate: 2 };\r\n\r\nconst B = 'body', LL = 'legL', LR = 'legR', AL = 'armL', AR = 'armR';\r\n\r\n// Template index -> body-hugging zones (rig group + Y band + thickness\r\n// + optional front/back squash). Tuned in the viewer.\r\nconst ZONES = {\r\n  102: [{ groups: [B], yLo: 1.12, yHi: 1.62, th: 0.022 }],                               // Cuirass\r\n  104: [{ groups: [LL, LR], yLo: 0.56, yHi: 1.00, th: 0.020, zScale: 0.28, leg: true },  // Greaves (legs)\r\n        { groups: [B], yLo: 0.90, yHi: 1.14, th: 0.020 }],                               //   + hip fauld\r\n  103: [{ groups: [AL, AR], yLo: 0.83, yHi: 1.22, th: 0.016, arm: true }],               // Gauntlets\r\n  108: [{ groups: [LL, LR], yLo: 0.00, yHi: 0.42, th: 0.016, leg: true }],               // Boots\r\n};\r\n\r\nconst materialOf = (family) =>\r\n  family === MATERIAL_FAMILY.Leather ? 'leather'\r\n  : family === MATERIAL_FAMILY.Chain ? 'mail'\r\n  : 'steel'; // Plate (default)\r\n\r\n/** Body-hugging displace zones for an armour template + family, each\r\n *  stamped with the family material. [] for standoff/held pieces. */\r\nexport function armorZones(templateIndex, family = MATERIAL_FAMILY.Plate) {\r\n  const zs = ZONES[templateIndex];\r\n  if (!zs) return [];\r\n  const mat = materialOf(family);\r\n  return zs.map((z) => ({ ...z, mat }));\r\n}\r\n\r\nexport { materialOf };\r\n","pod/gala-rig/dfo/beastBody.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/beastBody.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// ═══════════════════════════════════════════════════════════════════\r\n// THE BEAST BODY\r\n//\r\n// A quadruped, built to a spec. Head, neck, barrel, four legs, tail —\r\n// and the proportions between them are the whole difference between a\r\n// rat and a bear, so they are parameters rather than three files.\r\n//\r\n// WHY THIS IS NOT THE CENTAUR'S HORSE. That piece is a horse HALF: it\r\n// has no head and no neck, because the human rig supplies both and the\r\n// animal starts at the withers. An animal has to bring its own\r\n// everything, and the human rig has to get out of the way completely —\r\n// which the centaur's collapse mechanism already does, now applied to\r\n// every group rather than the legs.\r\n//\r\n// ONE BUILDER, SEVERAL ANIMALS. A rat is long and low with a pointed\r\n// head and a bare tail; a bear is a mountain on short legs; a tiger is\r\n// the same length as the bear on longer ones and carries its head lower.\r\n// Those are numbers, not geometry, and the moment they are numbers the\r\n// next quadruped costs a design rather than a file. Which is the same\r\n// bet the class file made and won.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'body', so the whole\r\n// animal rides the rig's torso through a swing rather than staying put\r\n// while it turns.\r\n\r\nimport { HSCALE, quadder, boxer } from './pieceLoft.mjs';\r\n\r\n/** A brown coat. Each design overrides it; this is the fallback. */\r\nexport const PELT_RAMP = [\r\n  [38, 30, 22],\r\n  [62, 50, 36],\r\n  [88, 72, 52],\r\n  [116, 96, 70],\r\n  [146, 122, 92],\r\n  [176, 150, 116],\r\n];\r\n\r\n/** A box between two corners: six faces, lit brightest on top. */\r\nconst box = boxer();\r\n\r\n/**\r\n * @param {number[][]} ramp coat colours, dark -> light\r\n * @param {object} s the animal, in numbers\r\n *   back    — height of the spine off the ground\r\n *   len     — nose to rump\r\n *   girth   — how wide through the ribs\r\n *   legs    — leg thickness\r\n *   crouch  — 0 stands tall, 1 slings the belly low (a rat, a lizard)\r\n *   head    — head size relative to the body\r\n *   snout   — how far the muzzle runs forward\r\n *   tail    — length; 0 for none\r\n *   tailUp  — 0 drags, 1 carried high\r\n */\r\nexport function buildBeastBody(ramp = PELT_RAMP, s = {}) {\r\n  const {\r\n    back = 0.62,\r\n    len = 0.9,\r\n    girth = 0.17,\r\n    legs = 0.05,\r\n    crouch = 0.3,\r\n    head = 0.12,\r\n    snout = 0.1,\r\n    tail = 0.3,\r\n    tailUp = 0.3,\r\n  } = s;\r\n\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n\r\n  const spine = back * HSCALE;\r\n  const belly = spine - girth * (1.5 - crouch * 0.5);\r\n  const half = len / 2;\r\n\r\n  // ── THE BARREL ──────────────────────────────────────────────────\r\n  // Three segments: deepest at the shoulder, drawing in at the haunch.\r\n  // A single box is a crate on legs, which is the same mistake the\r\n  // centaur's horse taught.\r\n  for (const [z0, z1, f] of [\r\n    [half * 0.55, half * 0.05, 0.86],\r\n    [half * 0.1, -half * 0.45, 1.0],\r\n    [-half * 0.4, -half, 0.84],\r\n  ]) {\r\n    box(quad, -girth * f, belly, z1, girth * f, spine, z0);\r\n  }\r\n\r\n  // ── NECK AND HEAD ───────────────────────────────────────────────\r\n  // The neck runs forward and slightly up from the shoulder; the head\r\n  // sits on the end of it and the snout runs on from that. A head stuck\r\n  // straight onto the barrel reads as a bus.\r\n  const neckZ = half * 0.55;\r\n  const headZ = neckZ + head * 1.4;\r\n  const headY = spine - girth * 0.15 + head * 0.2;\r\n  const nw = girth * 0.42;\r\n  box(quad, -nw, spine - girth * 0.7, neckZ, nw, spine + head * 0.1, headZ - head * 0.5, 0.9, 0.62);\r\n  box(quad, -head, headY - head, headZ - head * 0.7, head, headY + head * 0.6, headZ + head * 0.5, 0.95, 0.66);\r\n  if (snout > 0) {\r\n    const sw = head * 0.5;\r\n    box(\r\n      quad,\r\n      -sw,\r\n      headY - head * 0.75,\r\n      headZ + head * 0.4,\r\n      sw,\r\n      headY + head * 0.05,\r\n      headZ + head * 0.4 + snout,\r\n      0.85,\r\n      0.58,\r\n    );\r\n  }\r\n\r\n  // ── FOUR LEGS ───────────────────────────────────────────────────\r\n  // Fore pair under the shoulder, hind under the haunch, and thinner\r\n  // than the barrel by a long way: that contrast is what makes a body\r\n  // read as a body rather than a block.\r\n  for (const [side, z] of [\r\n    [-1, half * 0.4],\r\n    [1, half * 0.4],\r\n    [-1, -half * 0.6],\r\n    [1, -half * 0.6],\r\n  ]) {\r\n    const x = side * girth * 0.66;\r\n    box(quad, x - legs, 0.02 * HSCALE, z - legs, x + legs, belly + 0.02, z + legs, 0.7, 0.6, 0.35);\r\n    // A paw, wider than the leg, or the animal stands on pins.\r\n    box(\r\n      quad,\r\n      x - legs * 1.5,\r\n      0,\r\n      z - legs * 1.4,\r\n      x + legs * 1.5,\r\n      0.03 * HSCALE,\r\n      z + legs * 1.8,\r\n      0.4,\r\n      0.34,\r\n      0.24,\r\n    );\r\n  }\r\n\r\n  // ── THE TAIL ────────────────────────────────────────────────────\r\n  if (tail > 0) {\r\n    const tw = girth * 0.16;\r\n    const y0 = belly + (spine - belly) * (0.4 + tailUp * 0.5);\r\n    box(quad, -tw, y0 - tw, -half - tail, tw, y0 + tw, -half + 0.02, 0.6, 0.5, 0.3);\r\n  }\r\n\r\n  return faces;\r\n}\r\n\r\n/**\r\n * A TAIL ON ITS OWN, for a body that is otherwise human.\r\n *\r\n * The first cut of this reused buildBeastBody with everything but the\r\n * tail set near zero — which still built a barrel, a head and four legs\r\n * to get one appendage: 84 faces for something that needs six, and all\r\n * of them somewhere behind the wearer's knees.\r\n *\r\n * Tagged 'body' so it rides the hips rather than the skull.\r\n *\r\n * @param {number[][]} ramp\r\n * @param {{len?:number, up?:number, thick?:number, at?:number}} s\r\n *   at — height of the root, in the rig's own (uncompressed) units\r\n */\r\n// NAMED beastTail, NOT buildTail. pieces/tail.js already exports a\r\n// buildTail — the Khajiit and Argonian tails, which are part of a race\r\n// rather than a piece bolted onto one — and two of that name in the\r\n// payload is a SyntaxError that takes the whole module down.\r\nexport function buildBeastTail(ramp = PELT_RAMP, s = {}) {\r\n  const { len = 0.3, up = 0.3, thick = 0.03, at = 0.98 } = s;\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n  const y = at * HSCALE;\r\n  // Two segments, the second thinner and lower: a tail that is one box\r\n  // is a plank, and a tail that does not droop is a broom handle.\r\n  const drop = (1 - up) * len * 0.5;\r\n  box(quad, -thick, y - thick, -len * 0.55, thick, y + thick, -0.01, 0.7, 0.55, 0.34);\r\n  box(\r\n    quad,\r\n    -thick * 0.7,\r\n    y - thick * 0.7 - drop,\r\n    -len,\r\n    thick * 0.7,\r\n    y + thick * 0.7 - drop,\r\n    -len * 0.5,\r\n    0.62,\r\n    0.48,\r\n    0.3,\r\n  );\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/beastHead.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/beastHead.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// A BEAST HEAD, for a body that walks upright.\r\n//\r\n// A Daggerfall werewolf is not a wolf on four legs. It is a man with the\r\n// wrong head, and that distinction is the whole design: the human rig\r\n// keeps its arms, its legs and its stance, and only the SKULL is\r\n// replaced. So this is the narrowest collapse in the project — one group\r\n// of six, where the beasts take all of them.\r\n//\r\n// WHICH MEANS IT SITS WHERE THE HUMAN HEAD SAT. The rig's own\r\n// NECK_PIVOT_Y is 1.655 and everything that animates a head derives from\r\n// it, so the muzzle is anchored there rather than at some height that\r\n// happens to look right. A head pinned anywhere else swings wrong the\r\n// moment the thing looks up.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'head', so it rides\r\n// headPitch with the skull it replaced.\r\n\r\nimport { HSCALE, quadder, boxer } from './pieceLoft.mjs';\r\nimport { NECK_PIVOT_Y } from './neutralBody.mjs';\r\n\r\n/** Grey wolf. Each design overrides it. */\r\nexport const WOLF_RAMP = [\r\n  [34, 32, 30],\r\n  [56, 53, 50],\r\n  [82, 78, 74],\r\n  [110, 105, 100],\r\n  [140, 134, 128],\r\n  [170, 164, 157],\r\n];\r\n\r\n// Head boxes: a brighter underside, darker back and brighter flanks\r\n// than the body's.\r\nconst box = boxer({ under: 0.32, back: 0.68, flank: 0.86 });\r\n\r\n/**\r\n * @param {number[][]} ramp coat colours, dark -> light\r\n * @param {object} s\r\n *   skull  — how big the cranium is\r\n *   snout  — how far the muzzle runs forward\r\n *   depth  — how deep the muzzle is (a boar's is blunt, a wolf's fine)\r\n *   ears   — 0 none, 1 tall and pricked\r\n *   tusks  — 0 none, 1 a boar's pair\r\n */\r\nexport function buildBeastHead(ramp = WOLF_RAMP, s = {}) {\r\n  const { skull = 0.1, snout = 0.14, depth = 0.6, ears = 1, tusks = 0 } = s;\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp, 'head');\r\n\r\n  // ANCHORED ON THE RIG'S OWN NECK PIVOT, not on a height that looks\r\n  // right. Everything that animates a head derives from this constant.\r\n  const baseY = NECK_PIVOT_Y * HSCALE;\r\n  const cy = baseY + skull * 0.55;\r\n\r\n  // The cranium.\r\n  box(quad, -skull, cy - skull * 0.9, -skull * 0.9, skull, cy + skull * 0.9, skull * 0.7);\r\n\r\n  // The muzzle, running forward and DOWN — a head that points its snout\r\n  // level reads as a crocodile.\r\n  const mw = skull * (0.42 + depth * 0.34);\r\n  const my = cy - skull * 0.36;\r\n  box(\r\n    quad,\r\n    -mw,\r\n    my - skull * depth * 0.9,\r\n    skull * 0.5,\r\n    mw,\r\n    my + skull * 0.34,\r\n    skull * 0.5 + snout,\r\n    0.88,\r\n    0.6,\r\n  );\r\n  // The nose, darker and blunter than the muzzle it ends.\r\n  box(\r\n    quad,\r\n    -mw * 0.7,\r\n    my - skull * depth * 0.75,\r\n    skull * 0.5 + snout,\r\n    mw * 0.7,\r\n    my + skull * 0.12,\r\n    skull * 0.5 + snout + skull * 0.12,\r\n    0.3,\r\n    0.24,\r\n    0.18,\r\n  );\r\n\r\n  // Ears: up off the back of the skull, angled out.\r\n  if (ears > 0) {\r\n    const ew = skull * 0.26;\r\n    const eh = skull * 1.1 * ears;\r\n    for (const side of [-1, 1]) {\r\n      const x = side * skull * 0.6;\r\n      box(quad, x - ew, cy + skull * 0.7, -skull * 0.5, x + ew, cy + skull * 0.7 + eh, -skull * 0.5 + ew * 1.6, 0.9, 0.58);\r\n    }\r\n  }\r\n\r\n  // Tusks: up out of the lower jaw, which is what makes a boar a boar.\r\n  if (tusks > 0) {\r\n    const tw = skull * 0.09;\r\n    const tl = skull * 0.7 * tusks;\r\n    for (const side of [-1, 1]) {\r\n      const x = side * mw * 0.7;\r\n      box(\r\n        quad,\r\n        x - tw,\r\n        my - skull * depth * 0.7,\r\n        skull * 0.5 + snout * 0.55,\r\n        x + tw,\r\n        my - skull * depth * 0.7 + tl,\r\n        skull * 0.5 + snout * 0.55 + tw * 2,\r\n        1,\r\n        0.86,\r\n        0.6,\r\n      );\r\n    }\r\n  }\r\n\r\n  return faces;\r\n}\r\n\r\n/**\r\n * HORNS, which are the one thing a daedra lord has that nothing else in\r\n * this project does.\r\n *\r\n * Swept back off the temples on an arc rather than straight up: a horn\r\n * that rises vertically is a party hat, and the sweep is what makes it\r\n * read as grown rather than worn.\r\n *\r\n * Tagged 'head' so they ride the skull, same as the ears and tusks.\r\n *\r\n * @param {number[][]} ramp\r\n * @param {{len?:number, thick?:number, sweep?:number, skull?:number}} s\r\n */\r\nexport function buildHorns(ramp = WOLF_RAMP, s = {}) {\r\n  const { len = 0.22, thick = 0.022, sweep = 0.7, skull = 0.1 } = s;\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp, 'head');\r\n  const baseY = NECK_PIVOT_Y * HSCALE + skull * 0.55;\r\n\r\n  const SEGS = 4;\r\n  for (const side of [-1, 1]) {\r\n    let px = side * skull * 0.72,\r\n      py = baseY + skull * 0.5,\r\n      pz = -skull * 0.2;\r\n    for (let i = 1; i <= SEGS; i++) {\r\n      const t = i / SEGS;\r\n      // Up first, then back: the arc tips over as it goes.\r\n      const a = t * Math.PI * 0.55 * sweep;\r\n      const nx = px + side * len * 0.1 * t;\r\n      const ny = py + Math.cos(a) * (len / SEGS);\r\n      const nz = pz - Math.sin(a) * (len / SEGS) * 1.4;\r\n      const r = thick * (1.1 - t * 0.75);\r\n      // A tapering box per segment, laid along its own run.\r\n      quad([px - r, py, pz + r], [px + r, py, pz + r], [nx + r, ny, nz + r], [nx - r, ny, nz + r], 0.92);\r\n      quad([px + r, py, pz - r], [px - r, py, pz - r], [nx - r, ny, nz - r], [nx + r, ny, nz - r], 0.5);\r\n      quad([px + r, py, pz + r], [px + r, py, pz - r], [nx + r, ny, nz - r], [nx + r, ny, nz + r], 0.72);\r\n      quad([px - r, py, pz - r], [px - r, py, pz + r], [nx - r, ny, nz + r], [nx - r, ny, nz - r], 0.72);\r\n      px = nx;\r\n      py = ny;\r\n      pz = nz;\r\n    }\r\n  }\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/blades.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/blades.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The blade family (Daggerfall 113-121 + 123 Dai-Katana): ONE\r\n// parameterized builder. Every blade shares the grip system:\r\n//   - authored pre-HSCALE at the left-fist column, every face 'armL'\r\n//   - GRIP STATION ring at the bake pivot (0.90) - centroid IS the\r\n//     anchor (phantom-axis rule)\r\n//   - Mac's +45 point-forward carry seat (identical to the longsword)\r\n//   - 1H blades inherit melee1H + ATTACKS_1H for free\r\n//   - the Dai-Katana carries the CLAYMORE-STANDARD two-hand grip\r\n//     block (off-hand station pre-bake y 0.74) so the solved melee2H\r\n//     pose and the station-coupled 2H attack tracks hold for it BY\r\n//     CONSTRUCTION - the off-hand station location is the 2H contract.\r\n// Curved blades bow through per-row cz (saber/wakazashi/katana/dai).\r\nimport { loftPiece, shadePiece, compress, LEFT_FIST_X, GRIP_Y, SEAT_PITCH, TWO_HAND_GRIP_ROWS, pitchAbout } from './pieceLoft.mjs';\r\n\r\n// spec: { blade, wRoot, wTip, thick, curve, guardW, guardT, pommelR,\r\n//         gripLo, gripR, twoHand }\r\nfunction buildBlade(ramp, spec) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 8 };\r\n  const gripR = spec.gripR ?? 0.018;   // girth pass (Mac 2026-07-06): hilts read toothpick-thin against the 0.074 fist\r\n  let gripLo;\r\n  if (spec.twoHand) {\r\n    // claymore-standard two-hand grip block: stations at 0.90 (grip)\r\n    // + 0.74 (OFF-HAND - the 2H pose/attack contract)\r\n    gripLo = TWO_HAND_GRIP_ROWS[0].y;\r\n    loftPiece(faces, TWO_HAND_GRIP_ROWS, { ...G, capTop: false, capBottom: false });\r\n    loftPiece(faces, [   // pommel bulge above the grip\r\n      { y: 1.058, rx: 0.014, rz: 0.014 },\r\n      { y: 1.082, rx: 0.038, rz: 0.038 },\r\n      { y: 1.108, rx: 0.020, rz: 0.020 },\r\n    ], G);\r\n  } else {\r\n    gripLo = spec.gripLo ?? 0.790;\r\n    loftPiece(faces, [\r\n      { y: gripLo, rx: gripR, rz: gripR + 0.001 },\r\n      { y: 0.900, rx: gripR + 0.0015, rz: gripR + 0.0025 },\r\n      { y: 0.998, rx: gripR, rz: gripR + 0.001 },\r\n    ], { ...G, capTop: false, capBottom: false });\r\n    loftPiece(faces, [   // pommel bulge above the grip (house layout)\r\n      { y: 0.998, rx: 0.010, rz: 0.010 },\r\n      { y: 1.018, rx: spec.pommelR ?? 0.028, rz: spec.pommelR ?? 0.028 },\r\n      { y: 1.042, rx: 0.014, rz: 0.014 },\r\n    ], G);\r\n  }\r\n  // guard: wide-rz thin pair just below the grip (blade side)\r\n  loftPiece(faces, [\r\n    { y: gripLo - 0.024, rx: spec.guardT, rz: spec.guardW, p: 0.45 },\r\n    { y: gripLo - 0.002, rx: spec.guardT + 0.002, rz: spec.guardW + 0.004, p: 0.45 },\r\n  ], G);\r\n  // blade DESCENDS from the guard (post-bake = point-forward carry);\r\n  // width in rz, thickness in rx, ring squash p 0.55; curve bows the\r\n  // flat via per-row cx?? no - the flat spans rz: curve offsets cz.\r\n  const b0 = gripLo - 0.030, bl = spec.blade;\r\n  const N = 7;\r\n  const rows = [];\r\n  for (let k = 0; k <= N; k++) {\r\n    const t = k / N;\r\n    rows.push({\r\n      y: b0 - bl * t,\r\n      rx: spec.thick * (1 - 0.45 * t),\r\n      rz: spec.wRoot * (1 - t) + (spec.wTip ?? 0.010) * t,\r\n      p: 0.55,\r\n      cz: (spec.curve ?? 0) * Math.sin(Math.PI * t),\r\n    });\r\n  }\r\n  rows.push({ y: b0 - bl - 0.028, rx: 0.003, rz: 0.003, p: 0.55, cz: 0 });   // point\r\n  loftPiece(faces, rows, G);\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, SEAT_PITCH), ramp));\r\n}\r\n\r\nexport const BLADE_SPECS = {\r\n  Dagger:     { blade: 0.30, wRoot: 0.030, thick: 0.008, guardW: 0.036, guardT: 0.010, pommelR: 0.023, gripLo: 0.810, gripR: 0.016 },\r\n  Tanto:      { blade: 0.34, wRoot: 0.026, thick: 0.008, guardW: 0.024, guardT: 0.020, pommelR: 0.019, gripLo: 0.800, gripR: 0.016 },\r\n  Shortsword: { blade: 0.46, wRoot: 0.040, thick: 0.009, guardW: 0.052, guardT: 0.011 },\r\n  Wakazashi:  { blade: 0.52, wRoot: 0.030, thick: 0.008, curve: 0.022, guardW: 0.030, guardT: 0.030, pommelR: 0.020 },\r\n  Broadsword: { blade: 0.62, wRoot: 0.058, thick: 0.010, guardW: 0.066, guardT: 0.012 },\r\n  Saber:      { blade: 0.74, wRoot: 0.034, thick: 0.008, curve: 0.050, guardW: 0.048, guardT: 0.013 },\r\n  Katana:     { blade: 0.80, wRoot: 0.030, thick: 0.008, curve: 0.035, guardW: 0.032, guardT: 0.032, pommelR: 0.019, gripLo: 0.760 },\r\n  Dai_Katana: { blade: 1.05, wRoot: 0.034, thick: 0.009, curve: 0.050, guardW: 0.040, guardT: 0.040, twoHand: true },\r\n};\r\n\r\nexport function buildBladeWeapon(ramp, name) {\r\n  return buildBlade(ramp, BLADE_SPECS[name]);\r\n}\r\n","pod/gala-rig/dfo/bodyScales.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/bodyScales.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Argonian body detail: a layer of raised scale bumps tiled over the\r\n// whole body plus wider belly scutes down the front. Generated by\r\n// sampling the actual body faces, so each scale sits exactly on the\r\n// surface and inherits that face's animation group (body/armL/armR/\r\n// legL/legR) - it moves with the rig and never floats. Built in FINAL\r\n// (compressed) body space; shown for Argonian.\r\n\r\nimport { shadePiece, pushQuad } from './pieceLoft.mjs';\r\nimport { KHAJIIT_FURS, ARGONIAN_HIDES } from './palettes.mjs';\r\n\r\n// AUDIT 68 S06-race-ramp-table-dup: the defaults are the palette\r\n// table's FIRST swatch (Tabby coat/belly - cats are counter-shaded: dark\r\n// back/top, pale belly/chest/inner legs - and Swamp hide), not copies.\r\nexport const KHAJIIT_FUR   = KHAJIIT_FURS[0].coat;\r\nexport const KHAJIIT_BELLY = KHAJIIT_FURS[0].belly;\r\nexport const ARGONIAN_HIDE = ARGONIAN_HIDES[0].ramp;\r\n\r\n// small raised pyramid scale at point P with outward normal n, tagged g.\r\nfunction scaleBump(out, P, n, size, lift, g) {\r\n  // tangent basis\r\n  let up = Math.abs(n[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0];\r\n  let t1 = [n[1]*up[2]-n[2]*up[1], n[2]*up[0]-n[0]*up[2], n[0]*up[1]-n[1]*up[0]];\r\n  let l1 = Math.hypot(...t1) || 1; t1 = [t1[0]/l1, t1[1]/l1, t1[2]/l1];\r\n  let t2 = [n[1]*t1[2]-n[2]*t1[1], n[2]*t1[0]-n[0]*t1[2], n[0]*t1[1]-n[1]*t1[0]];\r\n  const base = [P[0]+n[0]*lift, P[1]+n[1]*lift, P[2]+n[2]*lift];\r\n  const apex = [base[0]+n[0]*size*0.9, base[1]+n[1]*size*0.9, base[2]+n[2]*size*0.9];\r\n  // diamond base (points up-tail): top vertex biased along +t2\r\n  const s = size;\r\n  const A = [base[0]+t2[0]*s, base[1]+t2[1]*s, base[2]+t2[2]*s];\r\n  const B = [base[0]+t1[0]*s, base[1]+t1[1]*s, base[2]+t1[2]*s];\r\n  const C = [base[0]-t2[0]*s, base[1]-t2[1]*s, base[2]-t2[2]*s];\r\n  const D = [base[0]-t1[0]*s, base[1]-t1[1]*s, base[2]-t1[2]*s];\r\n  const tri = (a, b, c) => pushQuad(out, a, b, c, c, g);\r\n  tri(A, B, apex); tri(B, C, apex); tri(C, D, apex); tri(D, A, apex);\r\n}\r\n\r\nexport function buildBodyScales(bodyFaces, skin) {\r\n  const faces = [];\r\n  let idx = 0;\r\n  for (const f of bodyFaces) {\r\n    const n = f.n;\r\n    if (f.g === 'head') continue;                // head is scaled separately\r\n    if (n[2] < -0.35) continue;                 // skip the pure back\r\n    let cx=0, cy=0, cz=0; for (let i=0;i<4;i++){ cx+=f.p[i*3]; cy+=f.p[i*3+1]; cz+=f.p[i*3+2]; }\r\n    cx/=4; cy/=4; cz/=4;\r\n    if (cy > 1.62) continue;                     // head handled separately\r\n    if ((idx++ % 2) !== 0) continue;             // subsample -> tidy density\r\n    // scale size by region: torso a touch bigger than limbs\r\n    const big = (f.g === 'body' && cy < 1.35 && cy > 0.85);\r\n    scaleBump(faces, [cx, cy, cz], n, big ? 0.024 : 0.017, 0.006, f.g);\r\n  }\r\n  // Belly scutes: wider banded plates down the FRONT centre (chest ->\r\n  // abdomen), overlapping like a lizard's underside.\r\n  for (let y = 1.52; y > 0.86; y -= 0.058) {\r\n    // find the frontmost torso point near the centreline at this height\r\n    let best = null, bz = -1e9;\r\n    for (const f of bodyFaces) {\r\n      if (f.g !== 'body') continue;\r\n      let mx=0,my=0,mz=0; for (let i=0;i<4;i++){ mx+=f.p[i*3]; my+=f.p[i*3+1]; mz+=f.p[i*3+2]; }\r\n      mx/=4; my/=4; mz/=4;\r\n      if (Math.abs(my - y) > 0.03 || Math.abs(mx) > 0.05 || f.n[2] < 0.4) continue;\r\n      if (mz > bz) { bz = mz; best = [mx, my, mz]; }\r\n    }\r\n    if (!best) continue;\r\n    const w = 0.052, z = best[2] + 0.006, py = best[1];\r\n    // a shield: wide top, point at the bottom, raised centre\r\n    const tl=[-w, py+0.026, z], tr=[w, py+0.026, z];\r\n    const ml=[-w*0.9, py, z+0.010], mr=[w*0.9, py, z+0.010];\r\n    const bot=[0, py-0.034, z];\r\n    const q = (a, b, c, d) => pushQuad(faces, a, b, c, d, 'body');\r\n    q(tl, tr, mr, ml); q(ml, mr, bot, bot);\r\n  }\r\n  return shadePiece(faces, skin, 0.08, 0.18);\r\n}\r\n\r\n// short fur tuft at P: a thin blade angled outward then swept DOWN (fur\r\n// lies down the body) and a touch back. One tri, tagged g.\r\nfunction furTuft(out, P, n, len, g) {\r\n  let up = Math.abs(n[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0];\r\n  let t1 = [n[1]*up[2]-n[2]*up[1], n[2]*up[0]-n[0]*up[2], n[0]*up[1]-n[1]*up[0]];\r\n  const l1 = Math.hypot(...t1) || 1; t1 = [t1[0]/l1, t1[1]/l1, t1[2]/l1];\r\n  const base = [P[0]+n[0]*0.003, P[1]+n[1]*0.003, P[2]+n[2]*0.003];\r\n  const w = len * 0.22;\r\n  const a = [base[0]+t1[0]*w, base[1]+t1[1]*w, base[2]+t1[2]*w];\r\n  const b = [base[0]-t1[0]*w, base[1]-t1[1]*w, base[2]-t1[2]*w];\r\n  // tip: out along normal, then down + slightly back\r\n  const tip = [base[0]+n[0]*len*0.5, base[1]+n[1]*len*0.5 - len*0.85, base[2]+n[2]*len*0.5 - len*0.15];\r\n  const ux=a[0]-base[0],uy=a[1]-base[1],uz=a[2]-base[2], vx=tip[0]-base[0],vy=tip[1]-base[1],vz=tip[2]-base[2];\r\n  let nx=uy*vz-uz*vy,ny=uz*vx-ux*vz,nz=ux*vy-uy*vx; const L=Math.hypot(nx,ny,nz)||1;\r\n  out.push({ p:[...a,...b,...tip,...tip], n:[nx/L,ny/L,nz/L], g });\r\n}\r\n\r\n// Khajiit fur pass: dense short tufts over the whole body, tagged per\r\n// group so they move with the rig. No scutes.\r\nexport function buildBodyFur(bodyFaces, coat = KHAJIIT_FUR, belly = KHAJIIT_BELLY, region = 'all') {\r\n  const coatF = [], bellyF = [];\r\n  for (const f of bodyFaces) {\r\n    if (f.g === 'head') continue;              // head furred separately\r\n    const n = f.n;\r\n    if (n[2] < -0.45) continue;                // skip deep back\r\n    let cx=0, cy=0, cz=0; for (let i=0;i<4;i++){ cx+=f.p[i*3]; cy+=f.p[i*3+1]; cz+=f.p[i*3+2]; }\r\n    cx/=4; cy/=4; cz/=4;\r\n    if (cy > 1.62) continue;                    // neck/head boundary\r\n    const len = (f.g === 'body') ? 0.030 : 0.024;\r\n    // counter-shading: underside (normal points down) + front-lower chest\r\n    // + inner limbs read as the pale belly tone; everything else the coat.\r\n    const ventral = n[1] < -0.05 || (n[2] > 0.30 && cy < 1.30) || (Math.abs(cx) < 0.10 && cy < 1.15);\r\n    if (region === 'coat' && ventral) continue;\r\n    if (region === 'belly' && !ventral) continue;\r\n    furTuft(ventral ? bellyF : coatF, [cx, cy, cz], n, len, f.g);\r\n  }\r\n  shadePiece(coatF, coat, 0.08, 0.18); shadePiece(bellyF, belly, 0.08, 0.18);\r\n  return coatF.concat(bellyF);\r\n}\r\n","pod/gala-rig/dfo/bow.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/bow.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Bows (templates 129 Short Bow, 130 Long Bow) - ranged weapons on\r\n// the same grip system: authored at the LEFT-fist column, every face\r\n// 'armL' (the bow hand); the DRAW hand is POSED onto the STRING\r\n// STATION by rangedAim, exactly like the claymore's off hand.\r\n//\r\n// GRIP: a bow is held handshake-style - the stave PERPENDICULAR to\r\n// the forearm. GRIP_PITCH is -pi/2 about the grip point, so the\r\n// carried bow lies fore-aft at a hanging arm and stands VERTICAL\r\n// when the aim pose extends the arm forward.\r\n//\r\n// STATIONS (real on-axis rings, per the phantom-axis rule):\r\n//   grip ring at pre-bake y 0.90 (the fist), string ring at the\r\n//   string's midpoint - its centroid is the nock point the draw hand\r\n//   targets. The string is rigid (a thin loft, undrawn line); the\r\n//   DRAW reads through the arm, not string deformation - a geometric\r\n//   simplification at mitten fidelity, same class as the fused hand.\r\nimport { loftPiece, shadePiece, compress, LEFT_FIST_X, GRIP_Y, pitchAbout } from './pieceLoft.mjs';\r\n\r\nconst GRIP_PITCH = -Math.PI / 2;\r\n// The NOCK: authoring-space + = the ARCHER side after the -pi/2 bake\r\n// (rest-above-grip -> behind the fist at aim). The bow's string and nock\r\n// station AND the nocked arrow's tail read this one value.\r\nconst NOCK_CZ = 0.30;\r\n\r\nfunction buildBow(ramp, half) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 8 };\r\n  const lo = GRIP_Y - half, hi = GRIP_Y + half;\r\n  // STAVE: recurve - riser bulges forward at the grip, limbs sweep\r\n  // back to the tips (where the string meets them).\r\n  const limb = (t) => -0.05 + 0.082 * Math.sin(Math.PI * t);   // cz along the stave (t 0..1)\r\n  const rows = [];\r\n  const N = 8;\r\n  for (let k = 0; k <= N; k++) {\r\n    const t = k / N, y = lo + (hi - lo) * t;\r\n    const taper = 0.009 + 0.016 * Math.sin(Math.PI * t);\r\n    rows.push({ y, rx: taper, rz: taper + 0.004, cz: limb(t) });\r\n  }\r\n  // grip station: a fatter wrap ring exactly at the bake pivot\r\n  rows.push({ y: GRIP_Y, rx: 0.028, rz: 0.032, cz: limb(0.5) });\r\n  loftPiece(faces, rows, G);\r\n  // STRING: DRAWN - two thin segments tip -> NOCK -> tip, the nock\r\n  // pulled 0.25 toward the archer. The aim pose IS the drawn\r\n  // bow (the pose is aiming); the NOCK STATION ring sits at the pull\r\n  // point and is what the draw hand targets - an undrawn string put\r\n  // the station out by the bow, outside the draw arm's reach.\r\n  loftPiece(faces, [\r\n    { y: lo + 0.012, rx: 0.0035, rz: 0.0035, cz: -0.05 },\r\n    { y: GRIP_Y - 0.02, rx: 0.0035, rz: 0.0035, cz: NOCK_CZ - 0.012 },\r\n  ], { ...G, seg: 6, capTop: false, capBottom: false });\r\n  loftPiece(faces, [\r\n    { y: GRIP_Y + 0.02, rx: 0.0035, rz: 0.0035, cz: NOCK_CZ - 0.012 },\r\n    { y: hi - 0.012, rx: 0.0035, rz: 0.0035, cz: -0.05 },\r\n  ], { ...G, seg: 6, capTop: false, capBottom: false });\r\n  // NOCK STATION: pre-bake (0.90, +0.30) -> post-bake above the grip\r\n  // at rest, at the CHEEK LINE when the arm extends to aim.\r\n  loftPiece(faces, [\r\n    { y: GRIP_Y - 0.012, rx: 0.007, rz: 0.007, cz: NOCK_CZ },\r\n    { y: GRIP_Y + 0.012, rx: 0.007, rz: 0.007, cz: NOCK_CZ },\r\n  ], { ...G, seg: 8, capTop: true, capBottom: true });\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, GRIP_PITCH), ramp));\r\n}\r\n\r\n// THE NOCKED ARROW (template 131) - its OWN piece/target since the\r\n// projectile arc: nocked, it rides the armL chain exactly like the\r\n// bow (alignment by construction); at the verbatim bow hit frame it\r\n// LOOSES - detaches into straight flight along its own axis at the\r\n// DFU missile speed. One arrow serves both bows.\r\nexport function buildNockedArrow(ramp) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 6 };\r\n  // loftPiece is Y-only: author the arrow along Y, then rotate it\r\n  // locally onto the cz (aim) axis about the grip line before the\r\n  // shared bake carries everything. Y +0.29 (tail) .. -0.24 (head)\r\n  // maps to cz +0.29 .. -0.24.\r\n  loftPiece(faces, [\r\n    { y: GRIP_Y - 0.24, rx: 0.002, rz: 0.002 },       // point\r\n    { y: GRIP_Y - 0.20, rx: 0.013, rz: 0.013 },       // head base\r\n    { y: GRIP_Y - 0.19, rx: 0.0045, rz: 0.0045 },     // shaft\r\n    { y: GRIP_Y + 0.19, rx: 0.0045, rz: 0.0045 },\r\n    { y: GRIP_Y + 0.20, rx: 0.012, rz: 0.012 },       // fletching\r\n    { y: GRIP_Y + 0.27, rx: 0.012, rz: 0.012 },\r\n    { y: GRIP_Y + NOCK_CZ - 0.01, rx: 0.004, rz: 0.004 }, // tail at the nock\r\n  ], { ...G, seg: 6 });\r\n  pitchAbout(faces, GRIP_Y, Math.PI / 2);   // +y -> +cz (sign probed at the module)\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, GRIP_PITCH), ramp));\r\n}\r\n\r\nexport function buildLongBow(ramp) { return buildBow(ramp, 0.62); }\r\nexport function buildShortBow(ramp) { return buildBow(ramp, 0.44); }\r\n","pod/gala-rig/dfo/centaurBody.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/centaurBody.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The centaur's HORSE HALF: a barrel, four legs and a tail, hung off\r\n// the human waist.\r\n//\r\n// This is a different problem from the skeleton's ribcage. Ribs were\r\n// geometry the loft could not express — holes in a closed surface — but\r\n// they sat ON the rig's own chest. A horse body is not a detail on the\r\n// human rig at all: it is a SECOND BODY, and the rig has exactly one.\r\n//\r\n// WHY THE HUMAN LEGS ARE BURIED RATHER THAN REMOVED. buildNeutralBody\r\n// clamps every build key into BUILD_MIN..BUILD_MAX and has no way to\r\n// drop a limb group — the face list is fixed, which is the whole reason\r\n// every design in this file can ship as a delta. So the legs go to the\r\n// floor of the clamp band, 0.6, and the barrel is built wide enough to\r\n// contain them. Two 0.6-girth legs inside a horse's chest are not\r\n// visible from outside it, and burying them costs 0 faces where teaching\r\n// the rig to drop a group would cost every caller that depends on the\r\n// count.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'body', so the horse\r\n// rides the human waist through a swing rather than staying behind while\r\n// the torso turns — a centaur that leaves its own hindquarters when it\r\n// twists is worse than one that has none.\r\n\r\nimport { HSCALE, quadder, boxer } from './pieceLoft.mjs';\r\n\r\n/** A bay coat: dark points, warm body. Its own ramp — a horse shaded off\r\n *  a human skin ramp reads as a pink animal. */\r\nexport const BAY_RAMP = [\r\n  [46, 30, 20],\r\n  [72, 48, 30],\r\n  [102, 68, 42],\r\n  [132, 92, 58],\r\n  [162, 118, 78],\r\n  [190, 148, 104],\r\n];\r\n\r\n/** Where the human ends and the horse begins, in the rig's own heights. */\r\nconst WITHERS_Y = 1.02;\r\n/** How far back the barrel runs from the human spine. */\r\nconst BARREL_LEN = 0.62;\r\n\r\n/** A box between two corners, six faces, shaded top-bright. */\r\nconst box = boxer();\r\n\r\n/**\r\n * @param {number[][]} ramp coat colours, dark -> light\r\n * @param {{girth?:number, len?:number, legs?:number}} opts\r\n *   girth — barrel width, so a heavier centaur reads heavier\r\n *   len   — how far the body runs back from the human spine\r\n *   legs  — leg thickness\r\n */\r\nexport function buildHorseBody(ramp = BAY_RAMP, { girth = 1, len = 1, legs = 1 } = {}) {\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n\r\n  const wx = 0.19 * girth;\r\n  const back = BARREL_LEN * len;\r\n  const topY = WITHERS_Y * HSCALE;\r\n  const belly = (WITHERS_Y - 0.42) * HSCALE;\r\n\r\n  // ── THE BARREL ──────────────────────────────────────────────────\r\n  // Three segments rather than one box: a horse is deepest at the girth\r\n  // just behind the shoulder and tapers to the haunch, and a single box\r\n  // reads as a crate on legs.\r\n  const seg = [\r\n    [0.02, -0.2, 1.0], // chest, under the human waist\r\n    [-0.2, -0.46, 1.06], // the girth, deepest\r\n    [-0.46, -back, 0.92], // the haunch, drawing in\r\n  ];\r\n  for (const [z0, z1, f] of seg) {\r\n    box(quad, -wx * f, belly, z1, wx * f, topY, z0);\r\n  }\r\n\r\n  // ── FOUR LEGS ───────────────────────────────────────────────────\r\n  // Fore pair under the girth, hind pair under the haunch. Thinner than\r\n  // the barrel by a long way — the contrast is what makes the body read\r\n  // as a body rather than as a block.\r\n  const lw = 0.05 * legs;\r\n  const hoofY = 0.0;\r\n  for (const [side, z] of [\r\n    [-1, -0.12],\r\n    [1, -0.12],\r\n    [-1, -back + 0.1],\r\n    [1, -back + 0.1],\r\n  ]) {\r\n    const x = side * wx * 0.62;\r\n    box(quad, x - lw, hoofY + 0.06 * HSCALE, z - lw, x + lw, belly + 0.02, z + lw, 0.7, 0.6, 0.35);\r\n    // The hoof: darker, and wider than the leg so it reads as a foot.\r\n    box(quad, x - lw * 1.5, hoofY, z - lw * 1.5, x + lw * 1.5, hoofY + 0.06 * HSCALE, z + lw * 1.5, 0.32, 0.26, 0.2);\r\n  }\r\n\r\n  // ── THE TAIL ────────────────────────────────────────────────────\r\n  // Off the haunch and down. Without it the hindquarters end in a flat\r\n  // wall and the whole animal reads as unfinished from behind.\r\n  const tw = 0.035 * girth;\r\n  box(quad, -tw, belly + 0.04, -back - 0.06, tw, topY - 0.04, -back + 0.02, 0.5, 0.42, 0.3);\r\n\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/claymore.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/claymore.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The Claymore (template 122, 2H) - a greatsword on the same grip\r\n// system as the Longsword: authored at the LEFT-fist column, every\r\n// face tagged 'armL' (the dominant-hand chain carries it; the OFF\r\n// hand is POSED onto the hilt by melee2H, not parented). Same baked\r\n// GRIP SEAT as the Longsword (Mac's +45: -2.40 + pi/4 about the grip\r\n// point y 0.90), so the seat sliders and every pose/gait/attack\r\n// transform apply unchanged.\r\n//\r\n// Proportions vs the Longsword: 0.95-unit blade (~171cm overall),\r\n// broad quillons, a LONG two-hand grip (0.34 units - two fists and a\r\n// gap, pommel-ward of the guard) and a heavy wheel pommel. Blade wide\r\n// axis in Z (swing plane) per the reference convention.\r\nimport { loftPiece, shadePiece, compress, LEFT_FIST_X, GRIP_Y, SEAT_PITCH, TWO_HAND_GRIP_ROWS, pitchAbout } from './pieceLoft.mjs';\r\n\r\nexport function buildClaymore(ramp) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 10 };\r\n  // WHEEL POMMEL: heavy counterweight at the hilt's top.\r\n  loftPiece(faces, [\r\n    { y: 1.058, rx: 0.016, rz: 0.016 },\r\n    { y: 1.078, rx: 0.030, rz: 0.030 },\r\n    { y: 1.104, rx: 0.022, rz: 0.022 },\r\n  ], G);\r\n  // TWO-HAND GRIP: dominant fist sits at the grip point (0.82..0.985\r\n  // through the mitten); the hilt runs pommel-ward 0.72..1.058 so the\r\n  // off hand has a full fist of handle above it.\r\n  // 0.90 = the GRIP STATION (bake pivot; dominant fist), 0.74 = the\r\n  // OFF-HAND STATION - real on-axis rings so the true hilt anchor and\r\n  // axis read as ring centroids (the phantom-axis bug: end-only tubes\r\n  // put \"nearest vert to the grip\" 0.1+ off axis).\r\n  loftPiece(faces, TWO_HAND_GRIP_ROWS, { ...G, capTop: false, capBottom: false });\r\n  // BROAD CRUCIFORM GUARD: quillons in the flat plane (wide z).\r\n  loftPiece(faces, [\r\n    { y: 0.694, rx: 0.018, rz: 0.118, p: 0.45 },\r\n    { y: 0.724, rx: 0.020, rz: 0.126, p: 0.45 },\r\n  ], G);\r\n  // BLADE: 0.95 units, wide-in-z flat, straight taper to the point.\r\n  loftPiece(faces, [\r\n    { y: 0.694, rx: 0.010, rz: 0.036, p: 0.55 },\r\n    { y: 0.420, rx: 0.0095, rz: 0.034, p: 0.55 },\r\n    { y: 0.120, rx: 0.009, rz: 0.030, p: 0.55 },\r\n    { y: -0.150, rx: 0.007, rz: 0.020, p: 0.55 },\r\n    { y: -0.256, rx: 0.003, rz: 0.003, p: 0.55 }, // point\r\n  ], { ...G, capTop: false });\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, SEAT_PITCH), ramp));\r\n}\r\n","pod/gala-rig/dfo/clothing.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/clothing.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Clothing on the neutral rig, 1:1 with the item DB. Each garment TYPE\r\n// maps to a set of displace ZONES (rig group + Y band + thickness) that\r\n// thicken the body's own surface with cloth - the same body-as-armour\r\n// approach, so it animates for free and layers under armour. Template\r\n// indices resolve to a type by NAME (the DB carries the names), so\r\n// every wearable index is covered without duplicating specs.\r\n//\r\n// Y bands are in the body's pre-HSCALE space (see neutralBody.js).\r\n// Draped garments (skirts, robes, cloaks, togas, dresses, surcoats,\r\n// kimono, sash, mummy wrappings) are DEFERRED - they need standoff\r\n// geometry, handled elsewhere. They resolve to [] here.\r\n\r\n\r\nconst TH = 0.008;            // cloth thickness (thinner than armour)\r\nconst B = 'body', LL = 'legL', LR = 'legR', AL = 'armL', AR = 'armR';\r\n\r\n// Reusable zone builders.\r\nconst torso = (yLo, yHi = 1.62, th = TH) => ({ groups: [B], yLo, yHi, th });\r\nconst sleeves = (yLo, yHi = 1.62, th = TH) => ({ groups: [AL, AR], yLo, yHi, th, arm: true });\r\nconst legs = (yLo, yHi = 1.00, th = TH) => ({ groups: [LL, LR], yLo, yHi, th, leg: true });\r\nconst pelvis = (yLo = 0.90, yHi = 1.14, th = TH) => ({ groups: [B], yLo, yHi, th });\r\nconst feet = (yLo = 0.00, yHi = 0.16, th = TH) => ({ groups: [LL, LR], yLo, yHi, th, leg: true });\r\n\r\n// Garment TYPE -> zones. Distinct body-hugging silhouettes by coverage.\r\nconst TYPES = {\r\n  'Short Shirt':      [torso(1.20), sleeves(1.36)],\r\n  'Long Shirt':       [torso(1.02), sleeves(1.16)],\r\n  'Short Tunic':      [torso(1.06), sleeves(1.40)],\r\n  'Formal Tunic':     [torso(1.02), sleeves(1.20)],\r\n  'Reversible Tunic': [torso(1.04), sleeves(1.22)],\r\n  'Open Tunic':       [torso(1.10), sleeves(1.40)],\r\n  'Vest':             [torso(1.16, 1.58)],\r\n  'Straps':           [torso(1.30, 1.56, 0.006)],\r\n  'Challenger Straps':[torso(1.30, 1.56, 0.006)],\r\n  'Champion Straps':  [torso(1.30, 1.56, 0.006)],\r\n  'Armbands':         [sleeves(1.24, 1.50)],\r\n  'Fancy Armbands':   [sleeves(1.24, 1.50)],\r\n  'Brassiere':        [torso(1.40, 1.56)],\r\n  'Formal Brassiere': [torso(1.40, 1.56)],\r\n  'Eodoric':          [torso(1.10, 1.60)],\r\n  'Formal Eodoric':   [torso(1.10, 1.60)],\r\n  'Casual Pants':     [legs(0.40), pelvis()],\r\n  'Loincloth':        [pelvis(0.72, 1.08), legs(0.72, 0.92)],\r\n  'Khajiit Suit':     [torso(1.02), sleeves(1.16), legs(0.16), pelvis(0.90, 1.14)],\r\n  'Shoes':            [feet(0.00, 0.10)],\r\n  'Sandals':          [feet(0.00, 0.08)],\r\n  'Boots':            [feet(0.00, 0.16)],\r\n  'Tall Boots':       [feet(0.00, 0.34)],\r\n};\r\n\r\n// Deferred (draped) - resolve to no displacement here.\r\nconst DRAPED = new Set(['Toga', 'Dwynnen Surcoat', 'Anticlere Surcoat', 'Casual Dress', 'Strapless Dress', 'Kimono', 'Sash', 'Mummy Wrappings', 'Short Skirt', 'Long Skirt', 'Wrap', 'Plain Robes', 'Priest Robes', 'Priestess Robes', 'Casual Cloak', 'Formal Cloak']);\r\n\r\n\r\n/** Zones for a clothing template index (or name). [] if draped/unknown. */\r\nexport function clothingZones(indexOrName) {\r\n  const name = indexOrName; // MYR5: our wardrobe resolves by type name (no Daggerfall item DB)\r\n  if (!name || DRAPED.has(name)) return [];\r\n  return TYPES[name] || [];\r\n}\r\n\r\nexport { TYPES as CLOTHING_TYPES };\r\n","pod/gala-rig/dfo/draped.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/draped.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Draped garments - standoff cloth that hangs OFF the body (skirts, robes,\r\n// cloaks, dresses, surcoats, togas, sashes, wraps, mummy wraps). Unlike\r\n// body-hugging clothing (vertex displacement), each is a separate GRID\r\n// that the verlet cloth sim (clothSim.js) simulates + collides against the\r\n// body. This module owns the geometry: the measured body collider\r\n// (BODY_CORE), the per-garment grids (drapedGrid), and materials\r\n// (DRAPE_MATERIAL). Two grid kinds: ring lofts (GRIDSPEC - skirts, robes,\r\n// capes, tunics) and centreline strips (stripGrid - the sash).\r\nimport { shadePiece, pushQuad } from './pieceLoft.mjs';\r\n\r\n// Body-core half-extents (torso+legs, NO arms), measured from the rig.\r\n// Drapes are clamped OUTSIDE this + a cloth standoff so they never clip.\r\nexport const BODY_CORE = [[1.55,0.072,0.084],[1.50,0.205,0.090],[1.45,0.215,0.088],[1.40,0.215,0.096],[1.30,0.208,0.098],[1.20,0.182,0.092],[1.05,0.160,0.082],[0.95,0.168,0.088],[0.90,0.178,0.100],[0.85,0.202,0.120],[0.80,0.202,0.120],[0.75,0.194,0.114],[0.65,0.178,0.100],[0.55,0.168,0.090],[0.50,0.154,0.080],[0.45,0.158,0.088],[0.40,0.152,0.072],[0.30,0.166,0.088],[0.20,0.142,0.063],[0.10,0.137,0.050]];\r\nconst STANDOFF = 0.038;\r\nexport function coreHalfExtents(y) {\r\n  if (y >= BODY_CORE[0][0]) return [BODY_CORE[0][1], BODY_CORE[0][2]];\r\n  if (y <= BODY_CORE[BODY_CORE.length-1][0]) { const l = BODY_CORE[BODY_CORE.length-1]; return [l[1], l[2]]; }\r\n  for (let i = 0; i+1 < BODY_CORE.length; i++) { const a = BODY_CORE[i], b = BODY_CORE[i+1]; if (y <= a[0] && y >= b[0]) { const t = (y-a[0])/(b[0]-a[0]); return [a[1]+(b[1]-a[1])*t, a[2]+(b[2]-a[2])*t]; } }\r\n  return [0.2, 0.1];\r\n}\r\n// clamp a ring's rx/rz to sit outside the body core at height y\r\nfunction clip(y, rx, rz) { const [cx, cz] = coreHalfExtents(y); return [Math.max(rx, cx + STANDOFF), Math.max(rz, cz + STANDOFF)]; }\r\n\r\n\r\n// ---- Ring-loft grids: rings of points swept down the body -> a pinned\r\n// cloth mesh. Row count scales with garment length (~constant spacing).\r\n// flareRows: waist/chest/shoulder -> hem cones. capeRows/shawlRows: arcs. --\r\nexport function flareRows( // MYR5: exported for our drapes\r\ntopY, hemY, tRx, tRz, hRx, hRz) {\r\n  const rows = [];\r\n  const steps = Math.max(6, Math.min(24, Math.round((topY - hemY) / 0.055)));  // ~constant ring spacing\r\n  for (let k = 0; k <= steps; k++) { const t = k/steps, y = topY - t*(topY-hemY), e = t*t; const [rx, rz] = clip(y, tRx + e*(hRx-tRx), tRz + e*(hRz-tRz)); rows.push({ y, rx, rz }); }\r\n  return rows;\r\n}\r\nexport function capeRows( // MYR5: exported\r\nhemY, wide) {\r\n  const rows = [], neckY = 1.500, W = wide ? 1.16 : 1.0;\r\n  const steps = Math.max(6, Math.min(24, Math.round((neckY - hemY) / 0.055)));\r\n  for (let k = 0; k <= steps; k++) { const t = k/steps, y = neckY - t*(neckY-hemY), grow = Math.pow(t, 0.85); const [rx, rz] = clip(y, (0.088 + grow*0.230)*W, (0.100 + grow*0.185)*W); rows.push({ y, rx, rz }); }\r\n  return rows;\r\n}\r\nconst CAPE_ARC = [Math.PI/2 + 1.25, Math.PI/2 - 1.25 + Math.PI*2];\r\n// Shawl: a wide wrap sitting over the shoulders, draping the back + sides\r\n// + around the front (small front gap), to mid-torso. One cohesive piece.\r\nfunction shawlRows() {\r\n  const rows = [], topY = 1.560, hemY = 1.040, steps = 10;\r\n  for (let k = 0; k <= steps; k++) { const t = k/steps, y = topY - t*(topY-hemY), grow = Math.pow(t, 0.65);\r\n    const [rx, rz] = clip(y, 0.190 + grow*0.070, 0.140 + grow*0.085); rows.push({ y, rx, rz }); }\r\n  return rows;\r\n}\r\nconst SHAWL_ARC = [Math.PI/2 + 0.55, Math.PI/2 - 0.55 + Math.PI*2];\r\nconst GRIDSPEC = {\r\n  'Short Skirt':     { rows: flareRows(0.980, 0.560, 0.175, 0.115, 0.270, 0.190), wrap: true, seg: 24 },\r\n  'Long Skirt':      { rows: flareRows(0.980, 0.130, 0.175, 0.115, 0.320, 0.220), wrap: true, seg: 24 },\r\n  'Casual Dress':    { rows: flareRows(1.320, 0.180, 0.150, 0.110, 0.320, 0.220), wrap: true, seg: 24 },\r\n  'Strapless Dress': { rows: flareRows(1.300, 0.200, 0.150, 0.110, 0.310, 0.215), wrap: true, seg: 24 },\r\n  'Plain Robes':     { rows: flareRows(1.545, 0.120, 0.160, 0.120, 0.300, 0.210), wrap: true, seg: 26 },\r\n  'Priest Robes':    { rows: flareRows(1.545, 0.110, 0.165, 0.125, 0.315, 0.220), wrap: true, seg: 26 },\r\n  'Priestess Robes': { rows: flareRows(1.545, 0.110, 0.165, 0.125, 0.315, 0.220), wrap: true, seg: 26 },\r\n  'Kimono':          { rows: flareRows(1.520, 0.220, 0.175, 0.135, 0.290, 0.220), wrap: true, seg: 26 },\r\n  'Mummy Wrappings': { rows: flareRows(1.560, 0.060, 0.155, 0.120, 0.190, 0.150), wrap: true, seg: 22 },\r\n  'Casual Cloak':    { rows: capeRows(0.450, false), wrap: false, seg: 26, arc: CAPE_ARC },\r\n  'Formal Cloak':    { rows: capeRows(0.360, true),  wrap: false, seg: 26, arc: CAPE_ARC },\r\n  'Wrap':            { rows: shawlRows(), wrap: false, seg: 28, arc: SHAWL_ARC },\r\n  // one-shoulder diagonal wrap: an arc covering ~270deg, open on one side\r\n  'Toga':            { rows: flareRows(1.520, 0.330, 0.150, 0.115, 0.255, 0.190), wrap: false, seg: 24, arc: [Math.PI*0.12, Math.PI*0.12 + Math.PI*1.5] },\r\n  // sleeveless over-tunics to mid-thigh\r\n  'Dwynnen Surcoat': { rows: flareRows(1.500, 0.600, 0.150, 0.125, 0.205, 0.170), wrap: true, seg: 24 },\r\n  'Anticlere Surcoat': { rows: flareRows(1.500, 0.560, 0.150, 0.125, 0.208, 0.172), wrap: true, seg: 24 },\r\n};\r\n\r\n// ---- Strip garments: a ribbon of cloth along a centreline path -----\r\n// Builds a (rows x cols) grid by sweeping a width across each path point,\r\n// perpendicular to the path and tangent to the body surface. Supports an\r\n// explicit pin mask (which rows are fixed to the body). These give the\r\n// detailed, asymmetric drapes (scarves, sashes) the ring grids can't.\r\nfunction stripGrid(center, halfW, cols, pinRows) {\r\n  const R = center.length, pos = new Float32Array(R * cols * 3);\r\n  const sub=(a,b)=>[a[0]-b[0],a[1]-b[1],a[2]-b[2]], cross=(a,b)=>[a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]], dot=(a,b)=>a[0]*b[0]+a[1]*b[1]+a[2]*b[2];\r\n  const norm=(a)=>{const l=Math.hypot(a[0],a[1],a[2])||1;return [a[0]/l,a[1]/l,a[2]/l];};\r\n  const T = [];\r\n  for (let r = 0; r < R; r++) T.push(norm(sub(center[Math.min(R-1,r+1)], center[Math.max(0,r-1)])));\r\n  // stable start frame; then PARALLEL-TRANSPORT it so the ribbon never flips\r\n  let p0 = cross(T[0], [0,0,1]); if (Math.hypot(p0[0],p0[1],p0[2]) < 0.1) p0 = cross(T[0], [1,0,0]);\r\n  const P = [norm(p0)];\r\n  for (let r = 1; r < R; r++) { const prev = P[r-1], d = dot(prev, T[r]); P.push(norm([prev[0]-d*T[r][0], prev[1]-d*T[r][1], prev[2]-d*T[r][2]])); }\r\n  for (let r = 0; r < R; r++) { const p = center[r], w = P[r], hw = Array.isArray(halfW) ? halfW[r] : halfW;\r\n    for (let c = 0; c < cols; c++) { const u = (c/(cols-1) - 0.5) * 2 * hw, i = (r*cols+c)*3;\r\n      pos[i] = p[0] + w[0]*u; pos[i+1] = p[1] + w[1]*u; pos[i+2] = p[2] + w[2]*u; }\r\n  }\r\n  const faces = [];\r\n  for (let r = 0; r+1 < R; r++) for (let c = 0; c+1 < cols; c++) faces.push([r*cols+c, r*cols+c+1, (r+1)*cols+c+1, (r+1)*cols+c]);\r\n  const pin = new Uint8Array(R * cols);\r\n  for (const r of pinRows) for (let c = 0; c < cols; c++) pin[r*cols + c] = 1;\r\n  return { rows: R, cols, wrap: false, pos, faces, pin };\r\n}\r\n\r\n// Sash: a wide band from the right shoulder diagonally across the chest to a\r\n// knot at the left hip, then a tapering tail hanging free below the knot.\r\nfunction sashGrid() {\r\n  const c = [\r\n    [0.150, 1.505, 0.095], [0.108, 1.395, 0.116], [0.048, 1.280, 0.124], [-0.020, 1.165, 0.122], // R shoulder -> chest\r\n    [-0.082, 1.050, 0.112], [-0.128, 0.955, 0.108],                                               // down to the waist\r\n    [-0.158, 0.878, 0.168], [-0.150, 0.850, 0.176], [-0.172, 0.828, 0.166],                        // KNOT: bulges forward, pinched\r\n    [-0.188, 0.712, 0.120], [-0.194, 0.590, 0.092], [-0.188, 0.470, 0.068], [-0.176, 0.352, 0.050], // tail, free\r\n  ];\r\n  const hw = [0.052,0.055,0.058,0.056, 0.054,0.056, 0.082,0.090,0.078, 0.050,0.044,0.040,0.052];   // knot fat, tail tapers then frays\r\n  return stripGrid(c, hw, 5, [0, 6, 7, 8]);   // pin shoulder + the knot rows\r\n}\r\n\r\n/** Cloth grid for a garment name: { rows, cols, wrap, pos (rows*cols*3),\r\n *  faces (quad indices), pin? }. Ring loft (GRIDSPEC) or centreline strip\r\n *  (sash). null only for an unknown name. */\r\nexport function drapedGrid(name) {\r\n  if (name === 'Sash') return sashGrid();\r\n  const spec = GRIDSPEC[name];\r\n  if (!spec) return null;\r\n  const { rows, wrap, seg, arc } = spec, R = rows.length, cols = wrap ? seg : seg + 1;\r\n  const pos = new Float32Array(R * cols * 3);\r\n  const a0 = arc ? arc[0] : 0, a1 = arc ? arc[1] : Math.PI*2;\r\n  for (let r = 0; r < R; r++) { const { y, rx, rz } = rows[r];\r\n    for (let c = 0; c < cols; c++) { const t = wrap ? c/seg : c/(cols-1), a = a0 + (a1-a0)*t, i = (r*cols+c)*3;\r\n      pos[i] = Math.cos(a)*rx; pos[i+1] = y; pos[i+2] = Math.sin(a)*rz; } }\r\n  const faces = [];\r\n  for (let r = 0; r+1 < R; r++) for (let c = 0; c < (wrap ? cols : cols-1); c++) {\r\n    const c2 = (c+1) % cols; faces.push([r*cols+c, r*cols+c2, (r+1)*cols+c2, (r+1)*cols+c]);\r\n  }\r\n  return { rows: R, cols, wrap, pos, faces };\r\n}\r\n// One grid quad -> a pieceLoft-convention face ({p: 12 floats, n, g}).\r\n// AUDIT 68 S06-draped-group-tag: g is the RIG GROUP every packer and\r\n// animator keys on - a drape hangs off the torso, so 'body' (it carried\r\n// the DRAPE_MATERIAL table itself, and packed as body only by accident).\r\nfunction facesFromGrid(g) {\r\n  const f = []; const P = (k) => [g.pos[k*3], g.pos[k*3+1], g.pos[k*3+2]];\r\n  for (const [a, b, c, d] of g.faces) pushQuad(f, P(a), P(b), P(c), P(d), 'body');\r\n  return f;\r\n}\r\n\r\nexport const DRAPED_NAMES = [...Object.keys(GRIDSPEC), 'Sash'];\r\n\r\n/** Standoff faces for a draped garment name, shaded with `ramp`. [] if not draped. */\r\nexport function drapedPiece(name, ramp) {\r\n  const g = drapedGrid(name);\r\n  return g ? shadePiece(facesFromGrid(g), ramp) : [];\r\n}\r\n\r\n// Per-garment MATERIAL: a colour ramp (dark->light, for the normal shade)\r\n// plus sheen (specular highlight -> silk/satin) and rim (edge light). Gives\r\n// every drape a distinct look instead of one shared grey cloth.\r\nexport const DRAPE_MATERIAL = {\r\n  'Short Skirt':      { ramp: [[52,44,32],[86,74,54],[122,106,78],[158,140,104],[190,174,138],[214,202,172]], sheen: 0.03, rim: 0.0 },\r\n  'Long Skirt':       { ramp: [[30,32,20],[54,58,34],[82,86,50],[114,118,74],[150,154,106],[186,188,146]], sheen: 0.04, rim: 0.0 },\r\n  'Casual Dress':     { ramp: [[12,34,36],[22,62,64],[34,96,96],[56,134,132],[104,172,168],[164,206,200]], sheen: 0.14, rim: 0.05 },\r\n  'Strapless Dress':  { ramp: [[34,16,38],[64,30,68],[98,50,102],[138,84,140],[178,130,178],[212,180,210]], sheen: 0.20, rim: 0.08 },\r\n  'Plain Robes':      { ramp: [[38,28,20],[70,52,36],[102,78,54],[140,110,78],[176,146,108],[206,180,140]], sheen: 0.04, rim: 0.0 },\r\n  'Priest Robes':     { ramp: [[18,20,42],[32,34,72],[50,54,108],[78,84,150],[120,128,186],[168,176,214]], sheen: 0.16, rim: 0.06 },\r\n  'Priestess Robes':  { ramp: [[60,54,38],[104,92,60],[150,134,88],[192,176,120],[220,208,160],[240,232,200]], sheen: 0.22, rim: 0.08 },\r\n  'Kimono':           { ramp: [[46,12,18],[92,24,32],[140,40,48],[186,64,72],[214,110,112],[236,168,166]], sheen: 0.35, rim: 0.15 },\r\n  'Mummy Wrappings':  { ramp: [[46,40,30],[78,70,54],[110,100,78],[142,130,102],[172,160,130],[198,188,160]], sheen: 0.0, rim: 0.0 },\r\n  'Casual Cloak':     { ramp: [[16,16,18],[32,32,36],[52,52,58],[76,76,84],[104,104,114],[140,140,150]], sheen: 0.02, rim: 0.03 },\r\n  'Formal Cloak':     { ramp: [[10,22,16],[20,42,30],[32,66,46],[50,96,68],[84,130,98],[132,170,138]], sheen: 0.08, rim: 0.05 },\r\n  'Toga':             { ramp: [[70,68,62],[112,110,102],[152,150,140],[190,188,178],[216,214,206],[238,236,230]], sheen: 0.06, rim: 0.04 },\r\n  'Dwynnen Surcoat':  { ramp: [[52,14,16],[96,26,28],[142,44,44],[184,74,72],[212,120,116],[234,170,166]], sheen: 0.06, rim: 0.05 },\r\n  'Anticlere Surcoat':{ ramp: [[12,20,48],[22,38,86],[36,60,128],[62,92,166],[110,140,196],[166,190,222]], sheen: 0.06, rim: 0.05 },\r\n  'Wrap':             { ramp: [[54,34,40],[92,60,66],[130,88,96],[168,120,128],[200,158,164],[224,196,200]], sheen: 0.10, rim: 0.05 },\r\n  'Sash':             { ramp: [[54,42,14],[96,76,24],[140,112,36],[184,152,56],[214,190,104],[238,222,168]], sheen: 0.42, rim: 0.20 },\r\n};\r\n","pod/gala-rig/dfo/fishBody.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/fishBody.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// A FISH: a body that tapers to a tail, with fins instead of limbs.\r\n//\r\n// The last thing in ENEMY_BASICS the existing builders cannot say. A\r\n// quadruped tapers nose to rump and stands on four legs; a fish tapers\r\n// the same way and stands on NOTHING, and the difference is not a\r\n// parameter — a leg length of zero leaves you with a barrel lying on the\r\n// floor, not a fish.\r\n//\r\n// WHAT MAKES IT READ IS THE FINS, and a fin is a thin vertical sheet\r\n// rather than a box: a tail fin is two triangles meeting at the spine, a\r\n// dorsal is one standing on it. Boxes would give a submarine.\r\n//\r\n// IT ALSO SERVES A LAMIA, which is a woman to the waist and a fish\r\n// below — the centaur's composition with a different animal on the back\r\n// of it. So the tail can start anywhere along the body rather than at\r\n// its own nose, which is what `from` is for.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'body'.\r\n\r\nimport { HSCALE, quadder } from './pieceLoft.mjs';\r\n\r\n/** Cold scales: green-grey, and light enough to keep a silhouette. */\r\nexport const SCALE_RAMP = [\r\n  [46, 60, 54],\r\n  [70, 92, 82],\r\n  [98, 124, 110],\r\n  [128, 154, 138],\r\n  [158, 182, 166],\r\n  [188, 208, 194],\r\n];\r\n\r\n/**\r\n * @param {number[][]} ramp\r\n * @param {object} s\r\n *   len    — nose (or waist) to the fork of the tail\r\n *   girth  — how deep through the body at its thickest\r\n *   at     — height of the spine\r\n *   from   — where along the body it starts: 0 is its own nose, and a\r\n *            lamia hands it a waist instead\r\n *   fin    — tail fin size\r\n *   dorsal — dorsal fin height; 0 for none\r\n *   jaw    — 0 none, 1 a slaughterfish's\r\n */\r\nexport function buildFishBody(ramp = SCALE_RAMP, s = {}) {\r\n  const { len = 0.9, girth = 0.14, at = 0.5, from = 0, fin = 0.16, dorsal = 0.1, jaw = 0 } = s;\r\n\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n  const y = at * HSCALE;\r\n  const z0 = from;\r\n\r\n  // ── THE BODY ────────────────────────────────────────────────────\r\n  // Rings down the length, each narrower than the last, joined as a\r\n  // tube. A fish is a taper and nothing else — the moment it has a\r\n  // constant section it is a pipe.\r\n  const RINGS = 6;\r\n  const ringAt = (i) => {\r\n    const t = i / RINGS;\r\n    // Fattest a third of the way back, then away to nothing.\r\n    const w = girth * (0.45 + Math.sin((0.2 + t * 0.8) * Math.PI) * 0.75) * (1 - t * 0.72);\r\n    return { z: z0 - len * t, w };\r\n  };\r\n  for (let i = 0; i < RINGS; i++) {\r\n    const a = ringAt(i);\r\n    const b = ringAt(i + 1);\r\n    const shade = 0.92 - (i / RINGS) * 0.3;\r\n    // Four sides, so the section is a diamond rather than a box: it\r\n    // catches light along the flank the way a fish does.\r\n    quad([0, y + a.w, a.z], [a.w, y, a.z], [b.w, y, b.z], [0, y + b.w, b.z], shade);\r\n    quad([-a.w, y, a.z], [0, y + a.w, a.z], [0, y + b.w, b.z], [-b.w, y, b.z], shade * 0.82);\r\n    quad([a.w, y, a.z], [0, y - a.w, a.z], [0, y - b.w, b.z], [b.w, y, b.z], shade * 0.5);\r\n    quad([0, y - a.w, a.z], [-a.w, y, a.z], [-b.w, y, b.z], [0, y - b.w, b.z], shade * 0.44);\r\n  }\r\n\r\n  // ── THE TAIL FIN ────────────────────────────────────────────────\r\n  // Two triangles off the fork, one up and one down. A thin sheet, not\r\n  // a box: the whole point of a fin is that it has no thickness.\r\n  const tz = z0 - len;\r\n  for (const up of [1, -1]) {\r\n    quad(\r\n      [0, y, tz],\r\n      [0, y + up * fin, tz - fin * 1.1],\r\n      [0, y + up * fin * 0.25, tz - fin * 1.5],\r\n      [0, y, tz],\r\n      up > 0 ? 0.9 : 0.6,\r\n    );\r\n  }\r\n\r\n  // ── THE DORSAL ──────────────────────────────────────────────────\r\n  if (dorsal > 0) {\r\n    const dz = z0 - len * 0.3;\r\n    quad(\r\n      [0, y + girth * 0.5, dz + len * 0.16],\r\n      [0, y + girth * 0.5 + dorsal, dz],\r\n      [0, y + girth * 0.5, dz - len * 0.2],\r\n      [0, y + girth * 0.5, dz + len * 0.16],\r\n      0.86,\r\n    );\r\n  }\r\n\r\n  // ── THE JAW ─────────────────────────────────────────────────────\r\n  // A slaughterfish is mostly teeth, and the gape is what says so.\r\n  if (jaw > 0) {\r\n    const jw = girth * 0.5 * jaw;\r\n    quad(\r\n      [0, y + jw * 0.2, z0],\r\n      [jw, y - jw * 0.6, z0 + jw * 1.6],\r\n      [-jw, y - jw * 0.6, z0 + jw * 1.6],\r\n      [0, y + jw * 0.2, z0],\r\n      1,\r\n    );\r\n    quad(\r\n      [0, y - jw * 0.4, z0],\r\n      [-jw, y - jw * 1.1, z0 + jw * 1.3],\r\n      [jw, y - jw * 1.1, z0 + jw * 1.3],\r\n      [0, y - jw * 0.4, z0],\r\n      0.42,\r\n    );\r\n  }\r\n\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/hafted.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/hafted.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The hafted family (Daggerfall 115 Staff, 124 Mace, 125 Flail, 126\r\n// Warhammer, 127 Battle Axe, 128 War Axe): ONE parameterized builder\r\n// on the grip system. House layout: the business end DESCENDS below\r\n// the grip (the seat bake maps down to point-forward carry).\r\n//   - 1H (Mace, Battle Axe): longsword grip + station, inherit\r\n//     melee1H + ATTACKS_1H free.\r\n//   - 2H (Staff, Flail, Warhammer, War Axe): the CLAYMORE-STANDARD\r\n//     grip block - the off-hand station at (-0.235, 0.8038, -0.1578)\r\n//     is the 2H CONTRACT: solved melee2H pose + station-coupled\r\n//     attack tracks transfer by construction.\r\n// Fidelity notes: the flail's chain-and-ball is RIGID (a thin tube +\r\n// ball along the haft line - same simplification class as the drawn\r\n// bow string); axe blades are flat plates hanging forward of the\r\n// shaft (single-bit) or both sides (double-bit war axe).\r\nimport { loftPiece, shadePiece, compress, LEFT_FIST_X, GRIP_Y, SEAT_PITCH, TWO_HAND_GRIP_ROWS, pitchAbout } from './pieceLoft.mjs';\r\n\r\n// spec: { twoHand, shaft, shaftR, head, staffUp }\r\nfunction buildHafted(ramp, spec) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 8 };\r\n  const sR = spec.shaftR ?? 0.020;   // girth pass (Mac 2026-07-06)\r\n  let gripLo;\r\n  if (spec.twoHand) {\r\n    gripLo = TWO_HAND_GRIP_ROWS[0].y;\r\n    loftPiece(faces, TWO_HAND_GRIP_ROWS, { ...G, capTop: false, capBottom: false });\r\n  } else {\r\n    gripLo = 0.790;\r\n    loftPiece(faces, [\r\n      { y: 0.790, rx: 0.019, rz: 0.020 },\r\n      { y: 0.900, rx: 0.021, rz: 0.022 },\r\n      { y: 0.998, rx: 0.019, rz: 0.020 },\r\n    ], { ...G, capTop: false, capBottom: false });\r\n  }\r\n  // butt cap above the grip\r\n  const bTop = spec.twoHand ? 1.058 : 0.998;\r\n  loftPiece(faces, [\r\n    { y: bTop, rx: sR + 0.003, rz: sR + 0.003 },\r\n    { y: bTop + (spec.staffUp ?? 0.028), rx: spec.staffUp ? sR : 0.006, rz: spec.staffUp ? sR : 0.006 },\r\n  ], G);\r\n  // shaft descending to the head seat\r\n  const headY = gripLo - 0.02 - spec.shaft;\r\n  loftPiece(faces, [\r\n    { y: gripLo - 0.002, rx: sR, rz: sR },\r\n    { y: headY + 0.02, rx: sR, rz: sR },\r\n  ], { ...G, capTop: false, capBottom: false });\r\n  // heads\r\n  if (spec.head === 'mace') {\r\n    loftPiece(faces, [\r\n      { y: headY + 0.02, rx: 0.022, rz: 0.022 },\r\n      { y: headY - 0.02, rx: 0.070, rz: 0.070 },\r\n      { y: headY - 0.12, rx: 0.078, rz: 0.078 },\r\n      { y: headY - 0.17, rx: 0.026, rz: 0.026 },\r\n    ], G);\r\n  } else if (spec.head === 'axe' || spec.head === 'waraxe') {\r\n    const sides = spec.head === 'waraxe' ? [0.082, -0.082] : [0.075];\r\n    for (const off of sides) {\r\n      loftPiece(faces, [\r\n        { y: headY + 0.08, rx: 0.013, rz: 0.082, p: 0.5, cz: off * 0.45 },\r\n        { y: headY - 0.03, rx: 0.016, rz: 0.140, p: 0.5, cz: off },\r\n        { y: headY - 0.14, rx: 0.013, rz: 0.082, p: 0.5, cz: off * 0.45 },\r\n      ], G);\r\n    }\r\n    loftPiece(faces, [   // poll/spike cap\r\n      { y: headY + 0.02, rx: 0.016, rz: 0.016 },\r\n      { y: headY - 0.05, rx: 0.012, rz: 0.012 },\r\n    ], G);\r\n  } else if (spec.head === 'hammer') {\r\n    loftPiece(faces, [\r\n      { y: headY + 0.04, rx: 0.060, rz: 0.060, cz: 0.026 },\r\n      { y: headY - 0.08, rx: 0.068, rz: 0.068, cz: 0.032 },\r\n      { y: headY - 0.13, rx: 0.030, rz: 0.030, cz: 0.016 },\r\n    ], G);\r\n  } else if (spec.head === 'ball') {\r\n    // rigid chain + ball continuing down the haft line\r\n    loftPiece(faces, [\r\n      { y: headY + 0.02, rx: 0.009, rz: 0.009 },\r\n      { y: headY - 0.10, rx: 0.009, rz: 0.009 },\r\n    ], { ...G, capTop: false, capBottom: false });\r\n    loftPiece(faces, [\r\n      { y: headY - 0.10, rx: 0.016, rz: 0.016 },\r\n      { y: headY - 0.16, rx: 0.055, rz: 0.055 },\r\n      { y: headY - 0.22, rx: 0.018, rz: 0.018 },\r\n    ], G);\r\n  } else {\r\n    loftPiece(faces, [   // staff foot\r\n      { y: headY + 0.02, rx: sR + 0.002, rz: sR + 0.002 },\r\n      { y: headY - 0.02, rx: 0.007, rz: 0.007 },\r\n    ], G);\r\n  }\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, SEAT_PITCH), ramp));\r\n}\r\n\r\nexport const HAFTED_SPECS = {\r\n  Mace:       { shaft: 0.44, head: 'mace' },\r\n  Battle_Axe: { shaft: 0.56, head: 'axe' },\r\n  Staff:      { shaft: 0.72, shaftR: 0.011, staffUp: 0.34, twoHand: true },\r\n  Flail:      { shaft: 0.36, head: 'ball', twoHand: true },\r\n  Warhammer:  { shaft: 0.62, head: 'hammer', twoHand: true },\r\n  War_Axe:    { shaft: 0.64, head: 'waraxe', twoHand: true },\r\n};\r\n\r\nexport function buildHaftedWeapon(ramp, name) {\r\n  return buildHafted(ramp, HAFTED_SPECS[name]);\r\n}\r\n","pod/gala-rig/dfo/hair.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/hair.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Head details (hair + race features) on the neutral rig, geometric.\r\n// Sits just outside the head, front left open (the face). Race-keyed;\r\n// this is the human base + hooks for elf ears etc. Separate mesh,\r\n// tagged 'head' so it bobs with the head.\r\nimport { loftPiece, compress, shadePiece, pushQuad } from './pieceLoft.mjs';\r\n\r\n// Hair ramps by broad colour; race/character will pick one later.\r\nexport const HAIR_RAMPS = {\r\n  brown: [[22, 15, 10], [40, 28, 18], [60, 44, 28], [84, 62, 42]],\r\n  black: [[12, 10, 12], [24, 20, 24], [38, 33, 40], [54, 48, 56]],\r\n  blonde:[[70, 54, 28], [104, 82, 44], [140, 114, 66], [178, 150, 96]],\r\n};\r\n\r\n// race: 'Human' | 'Elf' | 'Khajiit' | 'Argonian' (morphology groups).\r\nexport function buildHair(ramp = HAIR_RAMPS.brown, race = 'Human', skin = null, style = 'short') {\r\n  const faces = [];\r\n  const P = 0.8;\r\n  const hasHair = race === 'Human' || race === 'Elf'; // Khajiit=fur, Argonian=scales/crest\r\n  if (hasHair && style !== 'bald') {\r\n    const cap = (dropTo, backThick) => {\r\n      // crown cap down to the hairline band\r\n      loftPiece(faces, [\r\n        { y: 2.055, rx: 0.058, rz: 0.064, p: P, cz: -0.012 },\r\n        { y: 2.000, rx: 0.114, rz: 0.128, p: P, cz: -0.018 },\r\n        { y: 1.945, rx: 0.146, rz: 0.158, p: P, cz: -0.018 },\r\n        { y: 1.910, rx: 0.148, rz: 0.160, p: P, cz: -0.014 },\r\n      ], { group: 'head', seg: 26, capBottom: false });\r\n      // back + sides drop to `dropTo`\r\n      const rows = [\r\n        { y: 1.910, rx: 0.148, rz: 0.160, p: P, cz: -0.014 },\r\n        { y: 1.820, rx: 0.146 + backThick, rz: 0.156 + backThick, p: P, cz: -0.020 },\r\n      ];\r\n      const steps = Math.max(1, Math.round((1.820 - dropTo) / 0.09));\r\n      for (let k = 1; k <= steps; k++) { const t = k/steps, y = 1.820 - t*(1.820-dropTo);\r\n        rows.push({ y, rx: (0.146 + backThick)*(1-0.28*t), rz: (0.156 + backThick)*(1-0.20*t), p: P, cz: -0.020 - t*0.02 }); }\r\n      const GAP = 1.0;\r\n      loftPiece(faces, rows, { group: 'head', seg: 22, arc: [Math.PI/2 + GAP, Math.PI/2 - GAP + Math.PI*2] });\r\n    };\r\n    const strand = (spine) => { // a rounded tube (ponytail/braid), tangent frames\r\n      const T = spine.map((_,i)=>{const a=spine[Math.max(0,i-1)],b=spine[Math.min(spine.length-1,i+1)];let t=[b.x-a.x,b.y-a.y,b.z-a.z];const l=Math.hypot(...t)||1;return[t[0]/l,t[1]/l,t[2]/l];});\r\n      const side=[1,0,0];\r\n      const rings=spine.map((pp,i)=>{const t=T[i];let d=[t[1]*side[2]-t[2]*side[1],t[2]*side[0]-t[0]*side[2],t[0]*side[1]-t[1]*side[0]];const dl=Math.hypot(...d)||1;d=[d[0]/dl,d[1]/dl,d[2]/dl];const N=8,ring=[];for(let k=0;k<N;k++){const a=k/N*2*Math.PI,c=Math.cos(a)*pp.r,sn=Math.sin(a)*pp.r;ring.push([pp.x+c*side[0]+sn*d[0],pp.y+c*side[1]+sn*d[1],pp.z+c*side[2]+sn*d[2]]);}return ring;});\r\n      for(let i=0;i+1<rings.length;i++)for(let k=0;k<8;k++){const j=(k+1)%8;quad(rings[i][k],rings[i][j],rings[i+1][j],rings[i+1][k]);}\r\n    };\r\n    const quad = (a, b, c, d) => pushQuad(faces, a, b, c, d, 'head');\r\n\r\n    if (style === 'short')    cap(1.740, 0.0);\r\n    else if (style === 'buzz') cap(1.800, -0.02);\r\n    else if (style === 'medium') cap(1.640, 0.006);\r\n    else if (style === 'long') cap(1.470, 0.012);\r\n    else if (style === 'ponytail') { cap(1.760, 0.0); strand([\r\n        {x:0,y:1.860,z:-0.150,r:0.030},{x:0,y:1.800,z:-0.210,r:0.032},{x:0,y:1.700,z:-0.250,r:0.030},\r\n        {x:0,y:1.580,z:-0.256,r:0.026},{x:0,y:1.470,z:-0.236,r:0.020},{x:0,y:1.390,z:-0.200,r:0.010}]); }\r\n    else if (style === 'topknot') { cap(1.780, 0.0); strand([\r\n        {x:0,y:2.055,z:-0.010,r:0.030},{x:0,y:2.110,z:-0.010,r:0.044},{x:0,y:2.150,z:-0.012,r:0.036},{x:0,y:2.168,z:-0.014,r:0.014}]); }\r\n    else if (style === 'mohawk') {\r\n      // central sagittal crest, no full cap: a tall fin front->back\r\n      const cr = [[1.905,0.150],[1.995,0.090],[2.055,0.000],[2.045,-0.090],[1.965,-0.165]]; // [y,z] base along the scalp\r\n      for (let i=0;i+1<cr.length;i++){ const [y0,z0]=cr[i],[y1,z1]=cr[i+1]; const h0=0.075,h1=0.075,w=0.020;\r\n        quad([-w,y0,z0],[w,y0,z0],[w,y0+h0,z0],[-w,y0+h0,z0]);\r\n        quad([-w,y0,z0],[-w,y0+h0,z0],[-w,y1+h1,z1],[-w,y1,z1]);\r\n        quad([w,y0,z0],[w,y1,z1],[w,y1+h1,z1],[w,y0+h0,z0]);\r\n        quad([-w,y0+h0,z0],[w,y0+h0,z0],[w,y1+h1,z1],[-w,y1+h1,z1]); }\r\n    }\r\n    else cap(1.740, 0.0); // fallback\r\n  }\r\n\r\n  shadePiece(faces, ramp, 0.1, 0.2);\r\n\r\n  // ── RACE FEATURES ── flesh-coloured geometry (own ramp), shaded like\r\n  // the hair bands. skin ramp passed so ears/muzzle match the head.\r\n  const flesh = [];\r\n  const quad = (a, b, c, d) => pushQuad(flesh, a, b, c, d, 'head');\r\n  // curved tube along a 3D spine [{x,y,z,r}] - shared by horns/muzzle.\r\n  const tube = (spine, N = 6) => {\r\n    const rings = spine.map((pp, i) => {\r\n      const a = spine[Math.max(0, i-1)], b = spine[Math.min(spine.length-1, i+1)];\r\n      let tx=b.x-a.x, ty=b.y-a.y, tz=b.z-a.z; const tl=Math.hypot(tx,ty,tz)||1; tx/=tl;ty/=tl;tz/=tl;\r\n      let ux=0, uy=1, uz=0; const d=ux*tx+uy*ty+uz*tz; ux-=d*tx; uy-=d*ty; uz-=d*tz; const ul=Math.hypot(ux,uy,uz)||1; ux/=ul;uy/=ul;uz/=ul;\r\n      const bx=ty*uz-tz*uy, by=tz*ux-tx*uz, bz=tx*uy-ty*ux;\r\n      const ring = [];\r\n      for (let k=0;k<N;k++){ const ang=k/N*2*Math.PI, c=Math.cos(ang)*pp.r, sn=Math.sin(ang)*pp.r; ring.push([pp.x+c*ux+sn*bx, pp.y+c*uy+sn*by, pp.z+c*uz+sn*bz]); }\r\n      return ring;\r\n    });\r\n    for (let i=0;i+1<rings.length;i++) for (let k=0;k<N;k++){ const j=(k+1)%N; quad(rings[i][k], rings[i][j], rings[i+1][j], rings[i+1][k]); }\r\n  };\r\n  if (race === 'Elf') {\r\n    // Detailed pointed ear: a curled pinna lofted from the lobe up to a\r\n    // back-swept point. Each cross-section is a shallow C (the ear\r\n    // curl) - outer edge = helix rim, inner = concha hollow. Attaches\r\n    // to the head at the canal.\r\n    const ear = (sx) => {\r\n      // spine: [x-out, y, z] from lobe (front-low) to tip (back-high)\r\n      const spine = [\r\n        { o: 0.086, y: 1.836, z:  0.006, w: 0.028 }, // lobe - ON the head side\r\n        { o: 0.116, y: 1.874, z: -0.010, w: 0.034 }, // lower pinna (leaving surface)\r\n        { o: 0.150, y: 1.912, z: -0.030, w: 0.036 }, // mid\r\n        { o: 0.170, y: 1.950, z: -0.058, w: 0.030 }, // upper\r\n        { o: 0.178, y: 1.984, z: -0.088, w: 0.018 }, // near tip\r\n        { o: 0.176, y: 2.008, z: -0.112, w: 0.006 }, // point\r\n      ];\r\n      // each cross-section: a C-curl of 4 points (front-rim -> concha ->\r\n      // back-rim), the rim standing OUT (+x*sx), concha pulled IN.\r\n      const sect = (r) => {\r\n        const X = sx * r.o, cIn = sx * (r.o - 0.028);\r\n        return [\r\n          [X,           r.y, r.z + r.w],       // front helix rim\r\n          [cIn,         r.y, r.z + r.w*0.45],  // front concha\r\n          [cIn,         r.y, r.z - r.w*0.45],  // back concha\r\n          [X,           r.y, r.z - r.w],       // back helix rim\r\n        ];\r\n      };\r\n      const S = spine.map(sect);\r\n      // loft the four edge-strips between sections\r\n      for (let k = 0; k + 1 < S.length; k++) for (let j = 0; j < 3; j++) {\r\n        quad(S[k][j], S[k][j+1], S[k+1][j+1], S[k+1][j]);\r\n      }\r\n      // outer surface (back of the ear): rim-front to rim-back across\r\n      for (let k = 0; k + 1 < S.length; k++) quad(S[k][0], S[k][3], S[k+1][3], S[k+1][0]);\r\n      // attach the lobe/base to the head side\r\n      const b = S[0];\r\n      quad([sx*0.070, 1.858, 0.030], b[0], b[3], [sx*0.070, 1.820, -0.030]); // weld lobe into the head\r\n    };\r\n    ear(-1); ear(1);\r\n  } else if (race === 'Khajiit') {\r\n    // FELINE head: a pronounced muzzle with a nose, and high-set upright\r\n    // pointed ears cupped forward. (feline eyes/whiskers are texture.)\r\n    // Muzzle: a ROUNDED mound (loft, not a box) - wide whisker-pad base\r\n    // embedded in the face, tapering forward+down to a small nose. Each\r\n    // ring is an ellipse (wider than tall) so it reads as a cat snout.\r\n    const fZ = 0.176;\r\n    const mring = (y, z, rx, ry) => {\r\n      const r = []; const N = 10;\r\n      for (let k = 0; k < N; k++) { const a = k/N*2*Math.PI; r.push([Math.cos(a)*rx, y + Math.sin(a)*ry, z]); }\r\n      return r;\r\n    };\r\n    const mrows = [\r\n      mring(1.826, 0.096, 0.078, 0.044),  // base - WIDE whisker pads (barely protruding)\r\n      mring(1.820, 0.114, 0.066, 0.038),\r\n      mring(1.814, 0.126, 0.050, 0.030),\r\n      mring(1.808, 0.134, 0.030, 0.021),  // nose shelf\r\n      mring(1.803, 0.138, 0.014, 0.011),  // tip\r\n    ];\r\n    for (let i = 0; i + 1 < mrows.length; i++) for (let k = 0; k < 10; k++) { const j = (k+1)%10; quad(mrows[i][k], mrows[i][j], mrows[i+1][j], mrows[i+1][k]); }\r\n    // cap the tip\r\n    const tipC = [0, 1.806, 0.184]; for (let k = 0; k < 10; k++) { const j=(k+1)%10; quad(tipC, mrows[4][j], mrows[4][k], mrows[4][k]); }\r\n    // nose: a small dark heart/triangle on the upper front of the snout.\r\n    const ny = 1.826, nz = 0.134, nw = 0.016;\r\n    const nL=[-nw,ny,nz], nR=[nw,ny,nz], nB=[0,ny-0.020,nz+0.008];\r\n    quad(nL, nR, nB, nB);\r\n    // High-set pointed ears: broad triangular, cupped forward. Bases sit\r\n    // ON the upper skull (maxX ~0.075 there) and dip INTO it, so they're\r\n    // welded to the head - not floating beside it.\r\n    const kear = (sx) => {\r\n      // large cat ear set WIDE on the skull corner, splayed outward: the\r\n      // tip sits further out than the base (like the reference).\r\n      const fb   = [sx*0.042, 1.972, 0.034];   // front base, on the corner\r\n      const bb   = [sx*0.086, 1.958, -0.058];  // back base, at the skull edge\r\n      const midb = [sx*0.060, 1.950, -0.012];  // base centre, dipped IN (cup)\r\n      const tip  = [sx*0.108, 2.088, -0.030];  // point splayed OUT and up\r\n      quad(fb, tip, midb, midb);          // front face\r\n      quad(midb, tip, bb, bb);            // outer face\r\n      quad(bb, tip, fb, fb);              // back face (wraps)\r\n      quad(fb, midb, bb, bb);             // base floor (cup, embedded)\r\n    };\r\n    kear(-1); kear(1);\r\n    // heavy brow ridge over the eyes.\r\n    { const by = 1.898, bz = 0.126, bw = 0.086;\r\n      quad([-bw,by,bz],[bw,by,bz],[bw*0.86,by-0.020,bz+0.020],[-bw*0.86,by-0.020,bz+0.020]);\r\n      quad([-bw*0.86,by-0.020,bz+0.020],[bw*0.86,by-0.020,bz+0.020],[bw*0.7,by-0.030,bz-0.02],[-bw*0.7,by-0.030,bz-0.02]); }\r\n    // cheek + neck ruff: fur flaring out and down, framing the face.\r\n    const ruff = (sx, i) => {\r\n      const y0 = 1.770 - i*0.028, rx = sx*(0.086 - i*0.004), z0 = 0.052 - i*0.014;\r\n      const tip = [sx*(0.128 + i*0.006), y0 - 0.034, z0 - 0.030];\r\n      quad([rx, y0 + 0.018, z0], tip, [rx, y0 - 0.018, z0 - 0.006], [rx, y0 - 0.018, z0 - 0.006]);\r\n    };\r\n    for (const sx of [-1, 1]) for (let i = 0; i < 3; i++) ruff(sx, i);\r\n    // whiskers: thin tapered strands from the muzzle sides, swept out and\r\n    // slightly back+down (not square tufts). Three per side.\r\n    const whisker = (sx, i) => {\r\n      const y0 = 1.822 - i*0.016, th = 0.0022;\r\n      const root = [sx*0.052, y0, fZ - 0.030];\r\n      const tip  = [sx*(0.150 + i*0.024), y0 - 0.006 - i*0.010, fZ - 0.070 - i*0.026];\r\n      quad([root[0], root[1]+th, root[2]], [tip[0], tip[1], tip[2]], [tip[0], tip[1], tip[2]], [root[0], root[1]-th, root[2]]);\r\n    };\r\n    for (const sx of [-1, 1]) for (let i = 0; i < 3; i++) whisker(sx, i);\r\n  } else if (race === 'Argonian') {\r\n    // reptilian head: overlapping SCALE PLATES over the whole head, a\r\n    // reptilian SNOUT, clustered HORNS, a tall CREST, spiky BROW, and a\r\n    // neck FRILL.\r\n    // ---- SCALE PLATES: raised overlapping tiles across the head ----\r\n    const prof = [[1.820,0.045,0.046],[1.780,0.082,0.090],[1.720,0.100,0.115],[1.660,0.108,0.125],[1.620,0.102,0.125],[1.560,0.082,0.110]];\r\n    const interp = (cy) => { for (let i=0;i+1<prof.length;i++){ const a=prof[i],b=prof[i+1]; if (cy<=a[0]&&cy>=b[0]){ const t=(cy-a[0])/(b[0]-a[0]); return [a[1]+(b[1]-a[1])*t, a[2]+(b[2]-a[2])*t]; } } return cy>prof[0][0]?[prof[0][1],prof[0][2]]:[prof[5][1],prof[5][2]]; };\r\n    // one scale: a small raised pyramid (4 tris to an apex lifted off the\r\n    // surface) -> a discrete bump. Placed with GAPS so they don't merge.\r\n    const scale = (x, cy, z, nx, nz, r) => {\r\n      const py = cy/0.9;\r\n      const tx = nz, tz = -nx;                 // sideways tangent (xz)\r\n      const B = (dt, dv) => [x + tx*dt, py + dv, z + tz*dt]; // base ring point\r\n      const apex = [x + nx*0.016, py, z + nz*0.016];         // lifted centre\r\n      const a1=B(-r,-r), a2=B(r,-r), a3=B(r,r), a4=B(-r,r);\r\n      quad(a1, a2, apex, apex); quad(a2, a3, apex, apex); quad(a3, a4, apex, apex); quad(a4, a1, apex, apex);\r\n    };\r\n    let rowi = 0;\r\n    for (let cy=1.590; cy<=1.795; cy+=0.032, rowi++) {\r\n      const [rx,fz]=interp(cy);\r\n      const off = (rowi%2)*0.5;\r\n      for (let step=-3+off; step<=3; step+=1.0) {\r\n        const a = step*0.40;\r\n        if (Math.abs(a) > 1.30) continue;\r\n        const sxx=Math.sin(a), x=rx*sxx*0.97, z=fz*Math.cos(a)*0.98;\r\n        const nl=Math.hypot(sxx,Math.cos(a))||1;\r\n        scale(x, cy, z, sxx/nl, Math.cos(a)/nl, 0.013);\r\n      }\r\n    }\r\n    // ---- SNOUT: a reptilian snout projecting from the lower face ----\r\n    const snring = (y,z,rx2,ry2)=>{ const r=[]; for(let k=0;k<8;k++){const ang=k/8*2*Math.PI; r.push([Math.cos(ang)*rx2, y+Math.sin(ang)*ry2, z]);} return r; };\r\n    const sn = [ snring(1.816,0.104,0.066,0.048), snring(1.808,0.124,0.054,0.038), snring(1.800,0.138,0.042,0.028), snring(1.794,0.148,0.028,0.018), snring(1.790,0.152,0.014,0.010) ];\r\n    for(let i=0;i+1<sn.length;i++) for(let k=0;k<8;k++){const j=(k+1)%8; quad(sn[i][k],sn[i][j],sn[i+1][j],sn[i+1][k]);}\r\n    { const tc=[0,1.790,0.154]; for(let k=0;k<8;k++){const j=(k+1)%8; quad(tc,sn[4][j],sn[4][k],sn[4][k]);} }\r\n    // nostrils: two small dark dents near the snout tip\r\n    for (const sxn of [-1,1]) { const nx2=sxn*0.016; quad([nx2-0.006,1.812,0.146],[nx2+0.006,1.812,0.146],[nx2+0.004,1.800,0.150],[nx2-0.004,1.800,0.150]); }\r\n    // horns: from the top-sides, curving up then back to a point.\r\n    const horn = (sx) => tube([\r\n      { x: sx*0.066, y: 1.892, z:  0.000, r: 0.036 }, // base on the temple (skull maxX~0.10 here)\r\n      { x: sx*0.088, y: 1.922, z: -0.058, r: 0.029 }, // rising slightly, curving back\r\n      { x: sx*0.092, y: 1.930, z: -0.124, r: 0.021 }, // hugging the skull side, back\r\n      { x: sx*0.080, y: 1.918, z: -0.186, r: 0.012 }, // sweeping past the back of the head\r\n      { x: sx*0.062, y: 1.894, z: -0.238, r: 0.005 }, // tip\r\n    ]);\r\n    horn(-1); horn(1);\r\n    // tall spinal crest: 7 back-swept spines, bigger than before.\r\n    for (let i = 0; i < 7; i++) {\r\n      const t = i/6, y = 2.035 - t*0.34, z = 0.0 - t*0.14, h = 0.075 - t*0.03, w = 0.020 - t*0.008;\r\n      quad([-w,y,z],[w,y,z],[0,y+h,z-0.055],[0,y+h,z-0.055]);\r\n      quad([w,y,z],[0,y+h,z-0.055],[-w,y,z],[-w,y,z]);\r\n    }\r\n    // brow scales: a row of raised angular plates across the brow.\r\n    for (let i = 0; i < 5; i++) {\r\n      const sx = (i-2)/2 * 0.096, y = 1.878, z = 0.126, w = 0.022, h = 0.018;\r\n      quad([sx-w,y-h*0.5,z-0.01],[sx+w,y-h*0.5,z-0.01],[sx+w*0.5,y+h*0.5,z+0.02],[sx-w*0.5,y+h*0.5,z+0.02]); // plate\r\n      quad([sx-w*0.5,y+h*0.5,z+0.02],[sx+w*0.5,y+h*0.5,z+0.02],[sx,y+h,z-0.005],[sx,y+h,z-0.005]);              // ridge\r\n    }\r\n    // nose-ridge spikes: small spikes down the centre of the snout.\r\n    for (let i = 0; i < 3; i++) {\r\n      const y = 1.858 - i*0.026, z = 0.116 + i*0.012, w = 0.012, h = 0.020;\r\n      quad([-w,y,z],[w,y,z],[0,y+h*0.4,z+0.03],[0,y+h*0.4,z+0.03]);\r\n    }\r\n    // neck frill: a fan of membrane spikes sweeping out+back from behind\r\n    // the jaw on each side (frilled-lizard silhouette).\r\n    const frill = (sx) => {\r\n      // inner edge sits ON the head (within maxX at each height); outer\r\n      // edge fans out a modest amount - a connected membrane, not flaps.\r\n      const inner = [[sx*0.092, 1.836, -0.044], [sx*0.086, 1.768, -0.082], [sx*0.074, 1.704, -0.104]];\r\n      const outer = [[sx*0.118, 1.844, -0.108], [sx*0.126, 1.768, -0.140], [sx*0.114, 1.704, -0.166]];\r\n      for (let i = 0; i + 1 < inner.length; i++) quad(inner[i], outer[i], outer[i+1], inner[i+1]);\r\n    };\r\n    frill(-1); frill(1);\r\n  }\r\n  if (flesh.length) faces.push(...shadePiece(flesh, skin || ramp, 0.1, 0.2));\r\n\r\n  compress(faces);\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/helm.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/helm.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Helm (107): a simple medieval helm - a rounded dome that stops at the\r\n// brow, plus two cheek guards hanging down the sides. One loft, no\r\n// carving. Separate mesh, tagged 'head'.\r\nimport { loftPiece, shadePiece, compress } from './pieceLoft.mjs';\r\n\r\nexport function buildHelm(ramp) {\r\n  const faces = [];\r\n  const P = 0.78;\r\n  // Dome: crown down to the brow, covers the whole skull. Capped top.\r\n  loftPiece(faces, [\r\n    { y: 2.070, rx: 0.052, rz: 0.058, p: P, cz: -0.012 }, // crown\r\n    { y: 2.010, rx: 0.108, rz: 0.122, p: P, cz: -0.016 }, // upper skull\r\n    { y: 1.945, rx: 0.140, rz: 0.152, p: P, cz: -0.014 }, // widest\r\n    { y: 1.885, rx: 0.144, rz: 0.156, p: P, cz: -0.008 }, // brow line\r\n    { y: 1.855, rx: 0.140, rz: 0.150, p: P, cz: -0.004 }, // just below brow\r\n  ], { group: 'head', seg: 26, capBottom: false });\r\n\r\n  // Cheek guards: short plates hanging down the SIDES from the brow\r\n  // (skip the front face and the back of the neck). Two arcs.\r\n  const cheeks = [\r\n    { y: 1.855, rx: 0.142, rz: 0.150, p: P, cz: -0.004 },\r\n    { y: 1.790, rx: 0.140, rz: 0.146, p: P, cz: 0 },\r\n    { y: 1.740, rx: 0.132, rz: 0.138, p: P, cz: 0.004 }, // jaw\r\n  ];\r\n  // right side arc (around a=0), left side arc (around a=PI): each a\r\n  // ~70-degree band, leaving the front and back open.\r\n  loftPiece(faces, cheeks, { group: 'head', seg: 8, arc: [-0.6, 0.6] });\r\n  loftPiece(faces, cheeks, { group: 'head', seg: 8, arc: [Math.PI - 0.6, Math.PI + 0.6] });\r\n\r\n  compress(faces);\r\n  return shadePiece(faces, ramp);\r\n}\r\n","pod/gala-rig/dfo/neutralBody.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/neutralBody.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Neutral-pose paperdoll geometry (redesign, not sprite-constrained).\r\n// Pure + browser-safe: buildNeutralBody(ramps) returns faces\r\n// [{ p:[12], n:[3], c:[r,g,b] }] with AO + snapped-ramp palette shading\r\n// baked in. `ramps` = { skin:[[r,g,b]...], boot:[[r,g,b]...] } supplied\r\n// by the caller (engine passes ramps from the loaded ART_PAL; the\r\n// viewer tool passes them from the sprite). Geometry is authored as\r\n// loft profiles - see tools/neutral for the viewer.\r\n\r\n// The hand/forearm JUNCTION (pre-HSCALE): the hand's top row sits at\r\n// y 0.985 and the arm loft's wrist row at y 0.99 - the wrist joint is\r\n// the midline between them. Any per-height wrist cut MUST use this,\r\n// not a fraction of the arm span: a cut inside 0.738..0.985 bisects\r\n// the mitten (palm static, knuckles rotating, thumb torn).\r\nexport const WRIST_JUNCTION_Y = 0.9875;\r\n\r\n// The arm/fist column (half-extent). Hand rotations (rest bake + the\r\n// viewer tuner) MUST pivot on this forearm axis at z 0 - the hand's\r\n// own centroid sits off-axis (the outer thumb pulls it ~0.011 wide),\r\n// and rolling about the centroid translates the wrist ring off the\r\n// forearm (~0.013 disconnect at Mac's -40).\r\nexport const ARM_X = 0.235;\r\n\r\n// REST HAND ROLL (Mac: \"hand roll -40\" in the viewer tuner) - baked\r\n// into the rig so it holds through poses and gaits, not just the idle\r\n// tuner. Same semantics the slider applied: -40/100 * pi, mirrored\r\n// (left +, right -), rolled about the forearm axis.\r\nexport const HAND_REST_ROLL = 0.4 * Math.PI;\r\n\r\n// Neck pivot (pre-HSCALE): between the shoulder line (1.66) and the\r\n// neck rows - headPitch (run look-up, attack follow) rotates the head\r\n// group about this.\r\nexport const NECK_PIVOT_Y = 1.655;\r\n\r\n/** The rig's proportion vocabulary: per-part GIRTH multipliers, all 1\r\n *  = the human neutral figure. Passed as `opts.build`. Keys map to the\r\n *  loft groups a silhouette actually reads by:\r\n *    torso    - ribcage/waist/pelvis barrel\r\n *    shoulder - trap yoke + deltoid caps (the shoulder LINE)\r\n *    arm      - upper arm + forearm profile\r\n *    hand     - mitten + thumb\r\n *    neck     - the column\r\n *    skull    - cranium above the cheek row\r\n *    jaw      - chin/jaw/cheek rows (a heavy jaw without a swollen head)\r\n *    leg      - thigh/calf profile and the pelvis connector\r\n *  Anything outside 0.6..1.6 is a different creature, not a build -\r\n *  buildNeutralBody clamps to that band rather than tear a seam. */\r\nexport const BUILD_IDENTITY = Object.freeze({\r\n  torso: 1, shoulder: 1, arm: 1, hand: 1, neck: 1, skull: 1, jaw: 1, leg: 1,\r\n});\r\nexport const BUILD_MIN = 0.6, BUILD_MAX = 1.6;\r\n\r\nexport function buildNeutralBody(ramps, opts = {}) {\r\n  const SEG = 28;\r\n  const SKIN = [196, 154, 116], BOOT = [92, 74, 58];\r\n  const faces = [];\r\n  let grp = 'body'; // limb tag for animation (body/head/armL/armR/legL/legR)\r\n  const sq = (u, p) => Math.sign(u) * Math.pow(Math.abs(u), p);\r\n  const ring = (cx, y, cz, rx, rz, i, p = 1) => { const a = (i / SEG) * Math.PI * 2; return [cx + rx * sq(Math.cos(a), p), y, cz + rz * sq(Math.sin(a), p)]; };\r\n  function loft(rows, col, cxOf, czOf) {\r\n    const R = rows.slice().sort((a, b) => a.y - b.y);\r\n    for (let k = 0; k + 1 < R.length; k++) {\r\n      const a = R[k], b = R[k + 1];\r\n      if (Math.abs(a.y - b.y) < 1e-6) continue;\r\n      const ax = cxOf ? cxOf(a) : (a.cx ?? 0), bx = cxOf ? cxOf(b) : (b.cx ?? 0);\r\n      const az = czOf ? czOf(a) : (a.cz ?? 0), bz = czOf ? czOf(b) : (b.cz ?? 0);\r\n      const pa = a.p ?? 1, pb2 = b.p ?? 1;\r\n      for (let i = 0; i < SEG; i++) {\r\n        const p0 = ring(ax, a.y, az, a.rx, a.rz, i, pa), p1 = ring(ax, a.y, az, a.rx, a.rz, i + 1, pa);\r\n        const p2 = ring(bx, b.y, bz, b.rx, b.rz, i + 1, pb2), p3 = ring(bx, b.y, bz, b.rx, b.rz, i, pb2);\r\n        const ux = p1[0] - p0[0], uy = p1[1] - p0[1], uz = p1[2] - p0[2];\r\n        const vx = p3[0] - p0[0], vy = p3[1] - p0[1], vz = p3[2] - p0[2];\r\n        let nx = uy * vz - uz * vy, ny = uz * vx - ux * vz, nz = ux * vy - uy * vx;\r\n        const L = Math.hypot(nx, ny, nz) || 1; nx /= L; ny /= L; nz /= L;\r\n        // outward: flip if pointing toward the tube centre\r\n        const mx = (p0[0] + p2[0]) / 2 - ax, mz = (p0[2] + p2[2]) / 2 - az;\r\n        if (nx * mx + nz * mz < 0) { nx = -nx; ny = -ny; nz = -nz; }\r\n        faces.push({ p: [...p0, ...p1, ...p2, ...p3], n: [nx, ny, nz], c: col, g: grp });\r\n      }\r\n    }\r\n  }\r\n\r\n  // BUILD: per-part GIRTH multipliers (half-extents only - rx/rz).\r\n  // Y IS NEVER TOUCHED here. Every exported constant on this rig\r\n  // (WRIST_JUNCTION_Y, NECK_PIVOT_Y) and every consumer that derives\r\n  // from them (the viewer's wristY/neckY, animate's limb pivots) is a\r\n  // HEIGHT, so scaling y would silently invalidate all of them across\r\n  // callers that never asked for a build. Girth carries the mass a\r\n  // non-human silhouette needs (a thicker torso, a heavier jaw, a\r\n  // wider shoulder shelf) without moving a single joint.\r\n  //\r\n  // Default is exactly 1 for every key, so `buildNeutralBody(ramps)`\r\n  // and `buildNeutralBody(ramps, { build: {} })` return byte-identical\r\n  // face lists - asserted in test/neutralbody.build.test.js.\r\n  const BD = { ...BUILD_IDENTITY, ...(opts.build || {}) };\r\n  for (const k of Object.keys(BUILD_IDENTITY)) {\r\n    BD[k] = Math.min(BUILD_MAX, Math.max(BUILD_MIN, Number.isFinite(BD[k]) ? BD[k] : 1));\r\n  }\r\n  // Scaled COPY of a row list: rx/rz only. y, p, cx, cz, dx pass\r\n  // through untouched - cz is a DEPTH OFFSET (where the ring sits),\r\n  // not a radius, and scaling it would swing the head off the neck.\r\n  const girth = (rows, k) => (k === 1 ? rows : rows.map((r) => ({ ...r, rx: r.rx * k, rz: r.rz * k })));\r\n\r\n  // TORSO: designed, not the sprite barrel. V-taper from broad\r\n  // shoulders to a narrow waist with a slight hip flare, and a\r\n  // FLATTENED squarish cross-section (p 0.72) so the chest reads broad\r\n  // and flat, not as a round egg. Widths are half-extents.\r\n  const TP = 0.58; // low power = squarish, flat-faced chest (not an egg)\r\n  const torso = [\r\n    { y: 1.66, rx: 0.205, rz: 0.090, p: TP }, // clavicle / trap line\r\n    { y: 1.57, rx: 0.215, rz: 0.096, p: TP }, // upper chest / pecs\r\n    { y: 1.46, rx: 0.208, rz: 0.098, p: TP }, // chest\r\n    { y: 1.33, rx: 0.182, rz: 0.092, p: TP }, // lower ribs\r\n    { y: 1.19, rx: 0.160, rz: 0.082, p: TP }, // waist (narrowest)\r\n    { y: 1.09, rx: 0.162, rz: 0.084, p: TP }, // upper hip (holds waist width)\r\n    { y: 1.04, rx: 0.168, rz: 0.088, p: TP }, // pelvis\r\n    { y: 0.99, rx: 0.170, rz: 0.090, p: TP }, // pelvis floor (caps the thigh tops)\r\n  ];\r\n  loft(girth(torso, BD.torso), SKIN);\r\n\r\n  // DELTOID shoulder caps: connect the torso top out to each arm so the\r\n  // shoulders are a distinct broad line, and the arm reads as separate\r\n  // below - not fused into one oval. Small rounded mass over each joint.\r\n  // TRAPEZIUS yoke: slopes from the neck base DOWN and OUT to each\r\n  // shoulder, giving the shoulder LINE (not a flat lid). Flat-ish top\r\n  // (low p) so it reads as a plane, not a dome.\r\n  const TRAP = [\r\n    { y: 1.66, rx: 0.086, rz: 0.058, p: 0.5, cx: 0.00 }, // at the neck\r\n    { y: 1.64, rx: 0.150, rz: 0.070, p: 0.5, cx: 0.00 }, // sloping out\r\n    { y: 1.61, rx: 0.215, rz: 0.082, p: 0.5, cx: 0.00 }, // shoulder shelf\r\n  ];\r\n  loft(girth(TRAP, BD.shoulder), SKIN);\r\n  // DELTOID: an angular capped wedge over the joint (low p = planar\r\n  // facets, flatter than deep), defined edge into the arm - not a ball.\r\n  const DELT = [\r\n    { y: 1.64, rx: 0.070, rz: 0.072, p: 0.55 }, // top, tucks under the trap shelf\r\n    { y: 1.60, rx: 0.090, rz: 0.082, p: 0.55 }, // deltoid belly (planar)\r\n    { y: 1.55, rx: 0.082, rz: 0.076, p: 0.55 }, // mid deltoid\r\n    { y: 1.50, rx: 0.066, rz: 0.068, p: 0.6 },  // defined edge into the arm\r\n  ];\r\n  {\r\n    const D = girth(DELT, BD.shoulder);\r\n    grp = 'armL'; loft(D, SKIN, () => -0.240, () => 0);\r\n    grp = 'armR'; loft(D, SKIN, () => 0.240, () => 0);\r\n  }\r\n  grp = 'body';\r\n  // NECK: narrow column from the shoulders up into the head base.\r\n  loft(girth([\r\n    { y: 1.63, rx: 0.078, rz: 0.078, p: 0.8 },\r\n    { y: 1.68, rx: 0.070, rz: 0.072, p: 0.8 },\r\n    { y: 1.73, rx: 0.072, rz: 0.074, p: 0.8 },\r\n  ], BD.neck), SKIN, () => 0, () => -0.01);\r\n  // HEAD: a proper head (the sprite hair-blob was rx 0.35, wider than\r\n  // the shoulders - a mushroom). Rounded, sized like a head, sits on\r\n  // the neck. cz back a touch so the face plane faces forward.\r\n  grp = 'head';\r\n  // Egg-shaped: DEEPER than wide (face-to-skull longer than ear-to-ear),\r\n  // tapered jaw/chin at the bottom, widest at the temples, rounded\r\n  // crown. Skull sits back a touch (neck is forward), face plane fwd.\r\n  // JAW and SKULL scale SEPARATELY (BD.jaw / BD.skull). A heavy jaw is\r\n  // the single strongest non-human read on a humanoid head, and tying\r\n  // it to the cranium just inflates the whole skull into a balloon.\r\n  // The cheek row (1.79) is the seam: it takes the MEAN of the two so\r\n  // the jaw meets the temple on a continuous ring instead of a step.\r\n  const HEAD_JAW = [\r\n    { y: 1.70, rx: 0.052, rz: 0.070, cz: 0.010 }, // chin (narrow, forward)\r\n    { y: 1.74, rx: 0.082, rz: 0.105, cz: 0.005 }, // jaw\r\n  ];\r\n  const HEAD_SEAM = [\r\n    { y: 1.79, rx: 0.102, rz: 0.130, cz: -0.005 }, // cheek (the seam ring)\r\n  ];\r\n  const HEAD_SKULL = [\r\n    { y: 1.85, rx: 0.108, rz: 0.140, cz: -0.015 }, // temple (widest, deepest)\r\n    { y: 1.92, rx: 0.100, rz: 0.135, cz: -0.020 }, // upper skull (back-heavy)\r\n    { y: 1.98, rx: 0.082, rz: 0.108, cz: -0.018 }, // crown curve\r\n    { y: 2.03, rx: 0.045, rz: 0.058, cz: -0.012 }, // crown top\r\n  ];\r\n  // MYR5: opts.head = {jaw, seam, skull} row overrides (alien head shapes).\r\n  const HJ = opts.head?.jaw || HEAD_JAW, HS = opts.head?.seam || HEAD_SEAM, HK = opts.head?.skull || HEAD_SKULL;\r\n  loft([\r\n    ...girth(HJ, BD.jaw),\r\n    ...girth(HS, (BD.jaw + BD.skull) / 2),\r\n    ...girth(HK, BD.skull),\r\n  ], SKIN, () => 0, (r) => r.cz);\r\n  grp = 'body';\r\n\r\n  // ARMS: straight down at the sides. Measured arm radius profile, cx\r\n  // pinned just outside the torso, cz centred. Symmetric L/R.\r\n  const armProf = [\r\n    { y: 1.61, rx: 0.072, rz: 0.075 }, // deltoid / shoulder\r\n    { y: 1.50, rx: 0.065, rz: 0.068 }, // bicep (swell)\r\n    { y: 1.42, rx: 0.058, rz: 0.060 }, // lower bicep\r\n    { y: 1.35, rx: 0.050, rz: 0.056, cz: -0.006 }, // elbow crease (pinch, point back)\r\n    { y: 1.30, rx: 0.056, rz: 0.062, cz: -0.008 }, // olecranon / elbow tip\r\n    { y: 1.22, rx: 0.056, rz: 0.058 }, // forearm belly (swell)\r\n    { y: 1.16, rx: 0.050, rz: 0.052 }, // mid forearm\r\n    { y: 1.06, rx: 0.044, rz: 0.046 }, // lower forearm\r\n    { y: 0.99, rx: 0.036, rz: 0.038 }, // wrist\r\n  ];\r\n  {\r\n    const A = girth(armProf, BD.arm);\r\n    grp = 'armL'; loft(A, SKIN, () => -ARM_X, () => 0);\r\n    grp = 'armR'; loft(A, SKIN, () => ARM_X, () => 0);\r\n  }\r\n  grp = 'body';\r\n  // HAND: small block at the wrist\r\n  function hand(sign) {\r\n    const cx = sign * ARM_X;\r\n    // MITTEN: flat palm + fused fingers, hanging down. Flat = rx > rz.\r\n    loft(girth([\r\n      { y: 0.985, rx: 0.036, rz: 0.038 }, // wrist\r\n      { y: 0.950, rx: 0.052, rz: 0.030 }, // palm (wide, flat)\r\n      { y: 0.905, rx: 0.054, rz: 0.030 }, // knuckles\r\n      { y: 0.860, rx: 0.048, rz: 0.026 }, // fingers\r\n      { y: 0.820, rx: 0.036, rz: 0.020 }, // fingertips (rounded)\r\n    ], BD.hand), SKIN, () => cx, () => 0);\r\n    // THUMB: short nub off the OUTER side (away from the body), upper\r\n    // palm, angled slightly forward.\r\n    const tside = sign; // outer\r\n    // The thumb's OWN cx offset rides BD.hand too - a scaled mitten\r\n    // with an unscaled offset seats the thumb inside the palm.\r\n    loft(girth([\r\n      { y: 0.955, rx: 0.016, rz: 0.020, cz: 0.010 },\r\n      { y: 0.935, rx: 0.020, rz: 0.024, cz: 0.018 },\r\n      { y: 0.912, rx: 0.014, rz: 0.018, cz: 0.014 },\r\n    ], BD.hand), SKIN, () => cx + tside * 0.052 * BD.hand, (r) => r.cz);\r\n  }\r\n  grp = 'armL'; hand(-1); grp = 'armR'; hand(1); grp = 'body';\r\n\r\n  // LEGS: full measured taper down to a THIN ankle (my earlier clip at\r\n  // y0.14 kept the calf width -> fat ankle circles). Plus upper-thigh\r\n  // connector rows that ramp UP into the pelvis and toward centre, so\r\n  // the thighs merge with the waist instead of the waist sitting on\r\n  // top of two poles. Parallel, cx constant, cz centred.\r\n  const ANKLE_Y = 0.10;\r\n  const legProf = [\r\n    { y: 0.10, rx: 0.048, rz: 0.048 }, // ankle (thin)\r\n    { y: 0.22, rx: 0.060, rz: 0.063 }, // lower calf (slim)\r\n    { y: 0.34, rx: 0.084, rz: 0.088 }, // calf belly (swell)\r\n    { y: 0.44, rx: 0.070, rz: 0.072 }, // below knee (taper in)\r\n    { y: 0.50, rx: 0.076, rz: 0.088, cz: 0.010 }, // kneecap (bulge forward)\r\n    { y: 0.55, rx: 0.072, rz: 0.080 }, // knee joint line (pinch)\r\n    { y: 0.62, rx: 0.086, rz: 0.090 }, // lower thigh (swell)\r\n    { y: 0.72, rx: 0.096, rz: 0.100 }, // thigh\r\n    { y: 0.82, rx: 0.112, rz: 0.114 }, // mid thigh\r\n    { y: 0.95, rx: 0.120, rz: 0.120 }, // upper thigh (thickest, slim)\r\n  ];\r\n  const LEG_X = 0.082; // thighs gathered toward the narrow waist-width pelvis\r\n  // connector: from the measured thigh top (y1.034) up into the pelvis,\r\n  // widening and pulling toward centre so it fuses with the hip.\r\n  const CONNECT = [\r\n    { y: 0.90, rx: legProf[legProf.length - 1].rx, rz: legProf[legProf.length - 1].rz, dx: 0 },\r\n    { y: 0.96, rx: 0.120, rz: 0.112, dx: 0.006 },\r\n    { y: 1.00, rx: 0.108, rz: 0.100, dx: 0.012 }, // meets the pelvis floor - does NOT rise past it\r\n  ];\r\n  function leg(sign) {\r\n    loft(girth(legProf, BD.leg), SKIN, () => sign * LEG_X, () => 0);\r\n    loft(girth(CONNECT, BD.leg), SKIN, (r) => sign * (LEG_X - r.dx), () => 0); // dx pulls the top toward centre\r\n  }\r\n  grp = 'legL'; leg(-1); grp = 'legR'; leg(1); grp = 'body';\r\n\r\n  // FEET: forward, from the TRUE thin ankle (rx ~0.066). ANK is the\r\n  // ankle stump's top ring. AUDIT 68 S05-neutralbody-dead-writes: a\r\n  // \"slim ankle\" rewrite of legProf's bottom rows stood here, AFTER\r\n  // leg() had lofted them - it never reached the geometry.\r\n  const ANK = 0.045;\r\n  // FOOT: an explicit FLAT-SOLED volume extruded forward (a box, not a\r\n  // stack of ellipse rings - rings around a short span make a ball).\r\n  // Cross-sections are taken along Z (heel->toe), each a small vertical\r\n  // rounded rect, so the sole stays flat on the ground.\r\n  const quad = (a, b, c, d, col) => {\r\n    const ux = b[0]-a[0], uy = b[1]-a[1], uz = b[2]-a[2];\r\n    const vx = d[0]-a[0], vy = d[1]-a[1], vz = d[2]-a[2];\r\n    let nx = uy*vz-uz*vy, ny = uz*vx-ux*vz, nz = ux*vy-uy*vx;\r\n    const L = Math.hypot(nx,ny,nz)||1; nx/=L; ny/=L; nz/=L;\r\n    faces.push({ p: [...a, ...b, ...c, ...d], n: [nx,ny,nz], c: col, g: grp });\r\n  };\r\n  function foot(cx) {\r\n    const SOLE = 0.005;      // sole height off the ground\r\n    // profile along the foot length: [z, topY, halfWidth]\r\n    const prof = [\r\n      [-0.062, 0.082, 0.052], // heel back\r\n      [-0.025, 0.090, 0.055], // heel\r\n      [ 0.06,  0.074, 0.058], // arch/mid\r\n      [ 0.155, 0.056, 0.058], // ball\r\n      [ 0.235, 0.036, 0.044], // toe\r\n    ];\r\n    for (let k = 0; k + 1 < prof.length; k++) {\r\n      const [z0, t0, w0] = prof[k], [z1, t1, w1] = prof[k+1];\r\n      const L0 = [cx-w0, SOLE, z0], R0 = [cx+w0, SOLE, z0], TL0 = [cx-w0, t0, z0], TR0 = [cx+w0, t0, z0];\r\n      const L1 = [cx-w1, SOLE, z1], R1 = [cx+w1, SOLE, z1], TL1 = [cx-w1, t1, z1], TR1 = [cx+w1, t1, z1];\r\n      quad(L0, R0, R1, L1, BOOT);       // sole (bottom)\r\n      quad(TL0, TL1, TR1, TR0, BOOT);   // top\r\n      quad(L0, L1, TL1, TL0, BOOT);     // left side\r\n      quad(R0, TR0, TR1, R1, BOOT);     // right side\r\n    }\r\n    // cap heel and toe\r\n    const h = prof[0], t = prof[prof.length-1];\r\n    quad([cx-h[2],SOLE,h[0]], [cx-h[2],h[1],h[0]], [cx+h[2],h[1],h[0]], [cx+h[2],SOLE,h[0]], BOOT);\r\n    quad([cx-t[2],SOLE,t[0]], [cx+t[2],SOLE,t[0]], [cx+t[2],t[1],t[0]], [cx-t[2],t[1],t[0]], BOOT);\r\n    // ankle stump: connect leg bottom (ANK ring) down to the foot top at the ankle z\r\n    loft([\r\n      { y: ANKLE_Y, rx: ANK, rz: ANK, cz: 0 },\r\n      { y: 0.085, rx: 0.046, rz: 0.05, cz: 0 },\r\n    ], BOOT, () => cx, () => 0);\r\n  }\r\n  grp = 'legL'; foot(-LEG_X); grp = 'legR'; foot(LEG_X); grp = 'body';\r\n\r\n  // SHADING: bake ART_PAL palette ramps per face by lighting intensity\r\n  // (upper-right key, like the game) - the retro banded skin/boot look,\r\n  // not smooth material lighting. Colour tag on each face selects the ramp.\r\n\r\n  const Lx = 0.5, Ly = 0.55, Lz = 0.67, Ln = Math.hypot(Lx, Ly, Lz);\r\n  const shade = (ramp, it) => ramp[Math.max(0, Math.min(ramp.length-1, Math.round(it*(ramp.length-1))))]; // SNAP to a ramp step: hard palette bands (blocky shader look), no lerp\r\n  // AMBIENT OCCLUSION (geometry-derived): a face is occluded when other\r\n  // faces sit IN FRONT of it (its normal hemisphere) within a short\r\n  // radius - flat surfaces see none, crevice walls (armpit, crotch,\r\n  // neck, behind knee/elbow, under pec/delt) face a close opposing wall\r\n  // and darken. No hardcoded positions.\r\n\r\n  // ARMOR-AS-BODY (prototype): instead of a separate mesh, thicken the\r\n  // rig's own surface where a piece sits and recolour it. No gaps, and\r\n  // it animates with the body for free. Cuirass = the torso ('body')\r\n  // faces in the chest->waist band pushed radially out + tagged steel.\r\n  // zLo/zHi are the DEPTH gate, exactly parallel to yLo/yHi and\r\n  // unbounded by default (every existing caller is unchanged). A\r\n  // garment that must leave an opening needs it: a hood gated on\r\n  // height alone paints the whole skull, face included.\r\n  const displace = (groups, yLo, yHi, th, cxFor, zScale = 1, mat = 'steel', zLo = -Infinity, zHi = Infinity) => {\r\n    for (const f of faces) {\r\n      if (!groups.includes(f.g)) continue;\r\n      let cy = 0; for (let i = 0; i < 4; i++) cy += f.p[i*3+1]; cy /= 4;\r\n      if (cy < yLo || cy > yHi) continue;\r\n      if (zLo !== -Infinity || zHi !== Infinity) {\r\n        let cz = 0; for (let i = 0; i < 4; i++) cz += f.p[i*3+2]; cz /= 4;\r\n        if (cz < zLo || cz > zHi) continue;\r\n      }\r\n      const cx = cxFor ? cxFor(f.g) : 0;\r\n      for (let i = 0; i < 4; i++) {\r\n        const x = f.p[i*3] - cx, z = f.p[i*3+2], r = Math.hypot(x, z) || 1;\r\n        f.p[i*3] = cx + x + (x/r)*th; f.p[i*3+2] = z + (z/r)*th*zScale;\r\n      }\r\n      f._mat = mat;\r\n    }\r\n  };\r\n  const legCx = (g) => g === 'legL' ? -LEG_X : g === 'legR' ? LEG_X : 0;\r\n  const armCx = (g) => g === 'armL' ? -ARM_X : g === 'armR' ? ARM_X : 0;\r\n  // cuirass = torso chest->waist; greaves = legs + hip fauld; boots =\r\n  // feet/ankle; gauntlets = forearm+hand. Each thickens the rig's own\r\n  // surface and recolours steel (no separate mesh, animates for free).\r\n  // clothing (base layer, thin): zone list from the item DB via\r\n  // clothingZones() - each garment resolves to displace zones. Cloth\r\n  // material, sits UNDER the armour. Legs/arms carry their own centre.\r\n  for (const z of (opts.clothZones || [])) {\r\n    const cxFor = z.leg ? legCx : z.arm ? armCx : null;\r\n    // A cloth zone may name its OWN material, exactly as an armour zone\r\n    // does below - without it an entire outfit resolves to one colour,\r\n    // so a tunic and its hose could never differ. Defaults to 'cloth',\r\n    // which is what every caller got before, and no caller in src/\r\n    // passes clothZones at all (raceCharacter and engineRig both call\r\n    // buildNeutralBody(ramps) with no opts), so this loop does not run\r\n    // on any game path.\r\n    displace(z.groups, z.yLo, z.yHi, z.th ?? 0.008, cxFor, 1, z.mat || 'cloth', z.zLo ?? -Infinity, z.zHi ?? Infinity);\r\n  }\r\n\r\n  // armour zones (from armorSet via armorZones) - over the clothing.\r\n  // Each zone carries its family material (steel/mail/leather).\r\n  for (const z of (opts.armorZones || [])) {\r\n    const cxFor = z.leg ? legCx : z.arm ? armCx : null;\r\n    displace(z.groups, z.yLo, z.yHi, z.th ?? 0.02, cxFor, z.zScale ?? 1, z.mat || 'steel', z.zLo ?? -Infinity, z.zHi ?? Infinity);\r\n  }\r\n\r\n  // HEIGHT: vertical compress for a shorter, stockier (Daggerfall)\r\n  // build. Applied before AO so crevice distances stay consistent.\r\n  const HSCALE = 0.9;\r\n  for (const f of faces) for (let i = 0; i < 4; i++) f.p[i*3+1] *= HSCALE;\r\n\r\n  // REST HAND ROLL bake: rotate every hand face (below the wrist\r\n  // junction, arm groups) about ITS forearm axis (+/-ARM_X, z 0) by\r\n  // the mirrored HAND_REST_ROLL - same rotY the viewer tuner applies,\r\n  // so slider 0 now shows Mac's tuned -40. Normals rotate with the\r\n  // verts (baked shading + AO read them). No face spans the junction\r\n  // (hand tops at 0.985, forearm starts at 0.99), so faces rotate\r\n  // whole - no shear.\r\n  {\r\n    const wY = WRIST_JUNCTION_Y * HSCALE;\r\n    for (const f of faces) {\r\n      if (f.g !== 'armL' && f.g !== 'armR') continue;\r\n      let cy = 0; for (let i = 0; i < 4; i++) cy += f.p[i*3+1]; cy /= 4;\r\n      if (cy >= wY) continue;\r\n      const px = f.g === 'armL' ? -ARM_X : ARM_X;\r\n      const a = (f.g === 'armL' ? 1 : -1) * HAND_REST_ROLL;\r\n      const c = Math.cos(a), s = Math.sin(a);\r\n      for (let i = 0; i < 4; i++) {\r\n        const dx = f.p[i*3] - px, dz = f.p[i*3+2];\r\n        f.p[i*3] = px + c*dx + s*dz; f.p[i*3+2] = -s*dx + c*dz;\r\n      }\r\n      const nx = f.n[0], nz = f.n[2];\r\n      f.n[0] = c*nx + s*nz; f.n[2] = -s*nx + c*nz;\r\n    }\r\n  }\r\n\r\n  const cen = faces.map((f) => {\r\n    let x = 0, y = 0, z = 0;\r\n    for (let i = 0; i < 4; i++) { x += f.p[i*3]; y += f.p[i*3+1]; z += f.p[i*3+2]; }\r\n    return [x/4, y/4, z/4];\r\n  });\r\n  const AO_R = 0.11, AO_K = 0.55; // radius, strength\r\n  const ao = new Array(faces.length).fill(1);\r\n  // AO NEIGHBOURS, BINNED. This was a bare face-against-every-face\r\n  // walk: 2136^2 pairs, ~160ms a build, and buildPaperdollPayload runs\r\n  // ~90 of them in one synchronous statement - the editor page froze\r\n  // for ~15s before it painted a pixel.\r\n  //\r\n  // Only faces inside AO_R can contribute, so the centroids go into a\r\n  // uniform grid of exactly that cell size and each face reads its own\r\n  // cell plus the 26 around it. The gathered indices are SORTED before\r\n  // the sum: float addition is not associative, and this pass feeds a\r\n  // ramp index and the villager DELTAS, so the accumulation order has\r\n  // to stay the ascending-j order the flat walk had - the numbers out\r\n  // are bit-identical, only the pairs never examined are gone.\r\n  //\r\n  // The candidate list is a property of the CELL, not the face, so it\r\n  // is built and sorted once per occupied cell and shared by every\r\n  // face in it - ~170 sorts instead of one per face.\r\n  const CELL = AO_R;\r\n  const key = (ix, iy, iz) => ((ix + 512) * 1024 + (iy + 512)) * 1024 + (iz + 512);\r\n  const cellOf = (v) => Math.floor(v / CELL);\r\n  const grid = new Map();\r\n  const cellKey = new Array(faces.length);\r\n  for (let i = 0; i < faces.length; i++) {\r\n    const k = key(cellOf(cen[i][0]), cellOf(cen[i][1]), cellOf(cen[i][2]));\r\n    cellKey[i] = k;\r\n    const bucket = grid.get(k);\r\n    if (bucket) bucket.push(i); else grid.set(k, [i]);\r\n  }\r\n  const nearOf = new Map();\r\n  for (let i = 0; i < faces.length; i++) {\r\n    const c = cen[i], n = faces[i].n; let occ = 0;\r\n    let near = nearOf.get(cellKey[i]);\r\n    if (near === undefined) {\r\n      near = [];\r\n      const bx = cellOf(c[0]), by = cellOf(c[1]), bz = cellOf(c[2]);\r\n      for (let ox = -1; ox <= 1; ox++) for (let oy = -1; oy <= 1; oy++) for (let oz = -1; oz <= 1; oz++) {\r\n        const bucket = grid.get(key(bx + ox, by + oy, bz + oz));\r\n        if (bucket) for (let b = 0; b < bucket.length; b++) near.push(bucket[b]);\r\n      }\r\n      near.sort((a, b) => a - b);\r\n      nearOf.set(cellKey[i], near);\r\n    }\r\n    for (let t = 0; t < near.length; t++) {\r\n      const j = near[t];\r\n      if (j === i) continue;\r\n      const dx = cen[j][0]-c[0], dy = cen[j][1]-c[1], dz = cen[j][2]-c[2];\r\n      const d = Math.hypot(dx, dy, dz);\r\n      if (d < 1e-4 || d > AO_R) continue;\r\n      const facing = (dx*n[0] + dy*n[1] + dz*n[2]) / d; // >0 = other is in front\r\n      if (facing > 0.25) occ += (1 - d/AO_R) * facing;\r\n    }\r\n    ao[i] = Math.max(0.45, 1 - Math.min(1, occ * AO_K * 0.12));\r\n  }\r\n  for (let i = 0; i < faces.length; i++) {\r\n    const fc = faces[i];\r\n    let it = Math.max(0.08, (fc.n[0]*Lx + fc.n[1]*Ly + fc.n[2]*Lz) / Ln * 0.9 + 0.15);\r\n    it *= ao[i]; // crevices darken\r\n    const ramp = fc._mat ? (opts.mats?.[fc._mat] || ramps[fc._mat] || opts.cloth || ramps.skin)\r\n      : (fc.c === BOOT ? ramps.boot : ramps.skin);\r\n    const iv = Math.min(1, Math.max(0.04, it)); fc._i = iv; fc.c = shade(ramp, iv);\r\n    fc.r = ramp; fc.ao = ao[i]; fc.fb = [0.08, 0.15]; // MYR5: relit per facing by gala-rig/raster.mjs\r\n  }\r\n\r\n  // Payload (quantized, same format as the viewer expects).\r\n\r\n\r\n  return faces;\r\n}\r\n\r\n// MYR5: the same clothing/armour displacement buildNeutralBody runs (DFO's `displace`), applied to an\r\n// already-built (HSCALE'd, AO-baked) body, so a wardrobe change re-uses the cached body and its AO\r\n// instead of rebuilding the rig. Bands stay in pre-HSCALE units, as authored.\r\nexport function applyBodyZones(faces, clothZones = [], armorZones = []) {\r\n  const HS = 0.9, LEG_X = 0.082;\r\n  const legCx = (g) => g === 'legL' ? -LEG_X : g === 'legR' ? LEG_X : 0;\r\n  const armCx = (g) => g === 'armL' ? -ARM_X : g === 'armR' ? ARM_X : 0;\r\n  const displace = (groups, yLo, yHi, th, cxFor, zScale, mat, zLo, zHi) => {\r\n    for (const f of faces) {\r\n      if (!groups.includes(f.g)) continue;\r\n      let cy = 0; for (let i = 0; i < 4; i++) cy += f.p[i*3+1]; cy /= 4 * HS;\r\n      if (cy < yLo || cy > yHi) continue;\r\n      if (zLo !== -Infinity || zHi !== Infinity) {\r\n        let cz = 0; for (let i = 0; i < 4; i++) cz += f.p[i*3+2]; cz /= 4;\r\n        if (cz < zLo || cz > zHi) continue;\r\n      }\r\n      const cx = cxFor ? cxFor(f.g) : 0;\r\n      for (let i = 0; i < 4; i++) {\r\n        const x = f.p[i*3] - cx, z = f.p[i*3+2], r = Math.hypot(x, z) || 1;\r\n        f.p[i*3] = cx + x + (x/r)*th; f.p[i*3+2] = z + (z/r)*th*zScale;\r\n      }\r\n      f._mat = mat;\r\n    }\r\n  };\r\n  for (const z of clothZones) displace(z.groups, z.yLo, z.yHi, z.th ?? 0.008, z.leg ? legCx : z.arm ? armCx : null, 1, z.mat || 'cloth', z.zLo ?? -Infinity, z.zHi ?? Infinity);\r\n  for (const z of armorZones) displace(z.groups, z.yLo, z.yHi, z.th ?? 0.02, z.leg ? legCx : z.arm ? armCx : null, z.zScale ?? 1, z.mat || 'steel', z.zLo ?? -Infinity, z.zHi ?? Infinity);\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/orcHead.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/orcHead.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Orc head furniture: the two lower TUSKS and the heavy BROW shelf.\r\n// These are the only pieces of an orc that are not expressible as a\r\n// girth multiplier on the neutral rig - everything else (the barrel\r\n// chest, the slab shoulders, the heavy jaw) is BUILD_IDENTITY scaling,\r\n// which is why this file is small and the design file is data.\r\n//\r\n// Built in FINAL (compressed) body space - same convention as tail.js\r\n// and hair.js - and tagged 'head' so both ride headPitch with the\r\n// skull instead of hanging in the air when the orc looks up mid-swing.\r\n//\r\n// ANCHORING: the jaw rows this sits on are scaled by BD.jaw at build\r\n// time, so the anchors take the same multiplier. A tusk pinned to the\r\n// unscaled human jaw line sinks into a 1.35-jaw orc's cheek and floats\r\n// off a 0.9-jaw one; passing the build through is what keeps the root\r\n// buried in the lip at every jaw width in the clamp band.\r\n\r\nimport { HSCALE, shadePiece, pushQuad } from './pieceLoft.mjs';\r\n\r\n// Ivory: warm bone, its own ramp - a tusk shaded off the hide ramp\r\n// reads as a green growth rather than a tooth.\r\nexport const IVORY_RAMP = [[92, 84, 66], [124, 114, 92], [156, 146, 120], [188, 178, 150], [214, 206, 180], [236, 230, 210]];\r\n\r\n// Head landmarks, PRE-compress (mirroring neutralBody's head loft rows\r\n// so a change there is a one-line change here, not a hunt).\r\nconst CHIN_Y = 1.70, JAW_Y = 1.74, CHEEK_Y = 1.79, TEMPLE_Y = 1.85;\r\nconst JAW_RX = 0.082, JAW_RZ = 0.105;      // jaw row half-extents\r\nconst CHEEK_RX = 0.102, CHEEK_RZ = 0.130;  // cheek row half-extents\r\n\r\n/** Tusks: a pair of lower canines rooted in the jaw and curving UP and\r\n *  slightly OUT past the upper lip. Tapered square prisms, not cones -\r\n *  the rig's whole language is faceted, and a smooth cone reads as a\r\n *  different renderer's asset dropped onto the model.\r\n *\r\n *  `size` scales length/thickness only (a warlord's are heavier than a\r\n *  shaman's); the ROOT position is fixed by the jaw so a big tusk grows\r\n *  out of the same socket rather than sliding down the chin. */\r\nexport function buildTusks(ramp = IVORY_RAMP, { jaw = 1, size = 1 } = {}) {\r\n  const faces = [];\r\n  const quad = (a, b, c, d) => pushQuad(faces, a, b, c, d, 'head');\r\n\r\n  // Root: on the jaw ring, off to each side of the chin and forward.\r\n  // Both offsets ride `jaw`, which is what keeps the socket in the lip.\r\n  const rootY = ((CHIN_Y + JAW_Y) / 2) * HSCALE;\r\n  const rootX = JAW_RX * 0.62 * jaw;\r\n  const rootZ = JAW_RZ * 0.72 * jaw;\r\n  // Tip: up past the cheek line, splayed out and back a touch. Height\r\n  // is measured in the SAME compressed space, so a tusk cannot outgrow\r\n  // the face when HSCALE changes.\r\n  const len = (CHEEK_Y - JAW_Y) * HSCALE * 1.45 * size;\r\n\r\n  for (const sgn of [-1, 1]) {\r\n    // Four spine nodes, curving up/out/back - a curve, not a spike.\r\n    const nodes = [\r\n      { x: sgn * rootX,               y: rootY,              z: rootZ,         r: 0.019 * size },\r\n      { x: sgn * rootX * 1.12,        y: rootY + len * 0.38, z: rootZ * 0.98,  r: 0.016 * size },\r\n      { x: sgn * rootX * 1.30,        y: rootY + len * 0.74, z: rootZ * 0.88,  r: 0.011 * size },\r\n      { x: sgn * rootX * 1.46,        y: rootY + len,        z: rootZ * 0.74,  r: 0.0025 * size },\r\n    ];\r\n    // Square cross-section (4 sides) = faceted ivory that catches the\r\n    // cel bands the way every other piece on this rig does.\r\n    const ringOf = (n) => [\r\n      [n.x - n.r, n.y, n.z - n.r], [n.x + n.r, n.y, n.z - n.r],\r\n      [n.x + n.r, n.y, n.z + n.r], [n.x - n.r, n.y, n.z + n.r],\r\n    ];\r\n    const rings = nodes.map(ringOf);\r\n    for (let i = 0; i + 1 < rings.length; i++) {\r\n      for (let k = 0; k < 4; k++) {\r\n        const j = (k + 1) % 4;\r\n        quad(rings[i][k], rings[i][j], rings[i + 1][j], rings[i + 1][k]);\r\n      }\r\n    }\r\n    // Cap the root so the socket is not see-through from below.\r\n    const r0 = rings[0];\r\n    quad(r0[3], r0[2], r0[1], r0[0]);\r\n  }\r\n  return shadePiece(faces, ramp, 0.08, 0.18);\r\n}\r\n\r\n/** Brow: a heavy shelf across the forehead, above the eye line and\r\n *  proud of the face plane. Scales with the SKULL (it is cranium, not\r\n *  jaw) so it tracks the head it sits on. */\r\nexport function buildBrow(ramp, { skull = 1, jut = 1 } = {}) {\r\n  const faces = [];\r\n  const quad = (a, b, c, d) => pushQuad(faces, a, b, c, d, 'head');\r\n\r\n  const yLo = ((CHEEK_Y + TEMPLE_Y) / 2) * HSCALE;   // eye line\r\n  const yHi = (TEMPLE_Y + 0.020) * HSCALE;           // just under the crown curve\r\n  const halfW = CHEEK_RX * 0.94 * skull;\r\n  const zFace = CHEEK_RZ * 0.86 * skull;             // the face plane\r\n  const zOut = zFace + 0.030 * jut * skull;          // the shelf's leading edge\r\n\r\n  // Five columns across the brow so it arcs with the skull instead of\r\n  // sitting as one flat plank bolted to the front of the head.\r\n  const COLS = 5;\r\n  const colX = (i) => -halfW + (2 * halfW) * (i / COLS);\r\n  // Arc: deepest at centre, receding at the temples.\r\n  const arcZ = (x) => zOut - (Math.abs(x) / halfW) ** 2 * (zOut - zFace) * 0.55;\r\n\r\n  for (let i = 0; i < COLS; i++) {\r\n    const x0 = colX(i), x1 = colX(i + 1);\r\n    const z0 = arcZ(x0), z1 = arcZ(x1);\r\n    // front face of the shelf\r\n    quad([x0, yLo, z0], [x1, yLo, z1], [x1, yHi, z1], [x0, yHi, z0]);\r\n    // underside (the overhang that shadows the eyes - the whole point)\r\n    quad([x0, yLo, zFace], [x1, yLo, zFace], [x1, yLo, z1], [x0, yLo, z0]);\r\n    // top, blending back into the forehead\r\n    quad([x0, yHi, z0], [x1, yHi, z1], [x1, yHi, zFace], [x0, yHi, zFace]);\r\n  }\r\n  // Cap each end so the shelf does not read as a floating slab.\r\n  for (const sgn of [-1, 1]) {\r\n    const x = sgn * halfW, z = arcZ(x);\r\n    quad([x, yLo, zFace], [x, yLo, z], [x, yHi, z], [x, yHi, zFace]);\r\n  }\r\n  return shadePiece(faces, ramp, 0.08, 0.18);\r\n}\r\n","pod/gala-rig/dfo/palettes.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/palettes.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Selectable colour palettes per race. Each ramp is dark -> light (6\r\n// steps); the viewer snaps a per-face shading intensity onto the chosen\r\n// ramp, so any tone recolours the whole figure with correct shading.\r\n// Khajiit entries carry a paler belly ramp for counter-shading.\r\n\r\nexport const HUMAN_SKINS = [\r\n  { name: 'Pale',   ramp: [[120, 88, 72], [150, 112, 92], [178, 138, 114], [202, 164, 138], [222, 188, 162], [238, 210, 186]] },\r\n  { name: 'Fair',   ramp: [[104, 72, 56], [134, 96, 74], [162, 120, 94], [188, 146, 116], [210, 172, 140], [230, 196, 166]] },\r\n  { name: 'Tan',    ramp: [[84, 56, 40], [112, 78, 54], [140, 100, 70], [166, 124, 88], [192, 150, 110], [214, 176, 134]] },\r\n  { name: 'Olive',  ramp: [[70, 52, 34], [96, 74, 48], [122, 96, 64], [148, 120, 82], [172, 144, 104], [196, 168, 128]] },\r\n  { name: 'Brown',  ramp: [[52, 34, 24], [74, 50, 34], [98, 68, 46], [124, 90, 62], [150, 114, 82], [176, 138, 104]] },\r\n  { name: 'Deep',   ramp: [[34, 22, 16], [52, 34, 24], [72, 48, 34], [94, 66, 46], [118, 88, 64], [144, 112, 84]] },\r\n];\r\n\r\nexport const ELF_TONES = [\r\n  { name: 'High (gold)',  ramp: [[92, 74, 44], [124, 102, 62], [156, 130, 82], [186, 160, 106], [212, 188, 134], [234, 214, 166]] },\r\n  { name: 'Wood (copper)',ramp: [[74, 44, 28], [102, 64, 40], [130, 86, 54], [158, 110, 72], [184, 136, 94], [208, 164, 120]] },\r\n  { name: 'Dark (ash)',   ramp: [[44, 44, 54], [62, 62, 74], [84, 84, 98], [108, 108, 124], [134, 134, 150], [162, 162, 178]] },\r\n  { name: 'Dark (grey)',  ramp: [[40, 40, 44], [58, 58, 64], [80, 80, 88], [104, 104, 114], [130, 130, 142], [158, 158, 172]] },\r\n  { name: 'Pale',         ramp: [[112, 96, 92], [142, 122, 118], [170, 148, 144], [196, 174, 170], [218, 198, 194], [238, 222, 218]] },\r\n];\r\n\r\nexport const KHAJIIT_FURS = [\r\n  { name: 'Tabby', coat: [[34, 26, 18], [58, 44, 30], [88, 66, 44], [120, 92, 62], [154, 122, 86], [188, 156, 116]], belly: [[70, 60, 46], [104, 90, 70], [138, 122, 96], [170, 154, 124], [200, 186, 156], [226, 214, 188]] },\r\n  { name: 'Sand',  coat: [[70, 56, 34], [102, 84, 54], [134, 112, 76], [164, 142, 102], [192, 172, 132], [216, 200, 164]], belly: [[112, 100, 78], [146, 132, 106], [176, 162, 134], [202, 190, 162], [222, 212, 188], [238, 230, 210]] },\r\n  { name: 'Grey',  coat: [[38, 38, 42], [58, 60, 66], [82, 84, 92], [108, 112, 120], [138, 142, 150], [170, 174, 182]], belly: [[92, 94, 98], [122, 126, 132], [152, 156, 162], [180, 184, 190], [206, 210, 214], [228, 230, 234]] },\r\n  { name: 'Black', coat: [[16, 16, 18], [28, 28, 32], [44, 44, 50], [64, 64, 72], [88, 88, 98], [116, 116, 128]], belly: [[40, 40, 44], [60, 60, 66], [84, 84, 92], [110, 110, 120], [138, 138, 150], [168, 168, 180]] },\r\n  { name: 'Ginger',coat: [[72, 38, 18], [104, 58, 28], [138, 82, 42], [170, 108, 60], [198, 138, 86], [222, 170, 118]], belly: [[128, 96, 72], [160, 128, 100], [190, 160, 130], [214, 190, 162], [232, 214, 190], [244, 232, 214]] },\r\n  { name: 'White', coat: [[96, 94, 92], [130, 128, 126], [162, 160, 158], [192, 190, 188], [216, 216, 214], [238, 238, 238]], belly: [[130, 130, 128], [162, 162, 160], [192, 192, 190], [216, 216, 216], [234, 234, 234], [248, 248, 248]] },\r\n];\r\n\r\nexport const ARGONIAN_HIDES = [\r\n  { name: 'Swamp',  ramp: [[16, 26, 16], [28, 44, 26], [44, 64, 38], [64, 90, 52], [90, 118, 70], [118, 148, 92]] },\r\n  { name: 'Olive',  ramp: [[28, 30, 16], [46, 50, 26], [66, 72, 38], [90, 98, 54], [116, 126, 74], [144, 156, 98]] },\r\n  { name: 'Bronze', ramp: [[34, 24, 14], [56, 40, 22], [80, 58, 32], [108, 80, 46], [138, 108, 66], [168, 138, 92]] },\r\n  { name: 'Rust',   ramp: [[44, 22, 16], [70, 34, 24], [98, 50, 34], [128, 72, 48], [158, 98, 68], [186, 130, 94]] },\r\n  { name: 'Teal',   ramp: [[14, 30, 30], [24, 48, 48], [38, 70, 70], [56, 96, 94], [78, 122, 118], [104, 150, 144]] },\r\n  { name: 'Ash',    ramp: [[26, 30, 28], [42, 48, 44], [62, 70, 64], [86, 96, 88], [112, 124, 114], [140, 154, 142]] },\r\n];\r\n\r\nexport const PALETTES = { human: HUMAN_SKINS, elf: ELF_TONES, khajiit: KHAJIIT_FURS, argonian: ARGONIAN_HIDES };\r\n","pod/gala-rig/dfo/pauldrons.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/pauldrons.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Pauldrons (shoulder armour): standoff plate caps over each deltoid.\r\n// Separate mesh (they stand off the silhouette). Tagged armL/armR so\r\n// they swing with the shoulder. Left = 105, Right = 106.\r\nimport { loftPiece, shadePiece, compress } from './pieceLoft.mjs';\r\n\r\nconst SH_X = 0.235; // mirror neutralBody arm centre\r\n\r\nexport function buildPauldrons(ramp) {\r\n  const faces = [];\r\n  const P = 0.6;\r\n  const cap = (sign) => loftPiece(faces, [\r\n    { y: 1.680, rx: 0.072, rz: 0.078, p: P }, // crown\r\n    { y: 1.635, rx: 0.118, rz: 0.118, p: P }, // widest over the deltoid\r\n    { y: 1.570, rx: 0.108, rz: 0.104, p: P }, // skirt over the upper arm\r\n    { y: 1.520, rx: 0.082, rz: 0.080, p: P }, // lower edge\r\n  ], { cx: sign * SH_X, group: sign < 0 ? 'armL' : 'armR', seg: 20 });\r\n  cap(-1); cap(1);\r\n  compress(faces);\r\n  return shadePiece(faces, ramp);\r\n}\r\n","pod/gala-rig/dfo/pieceLoft.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/pieceLoft.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Shared geometry for armour pieces on the neutral rig. ONE correct\r\n// loft so the outward-normal flip (the thing that makes shading face\r\n// the right way) lives in a single place - the per-piece copies kept\r\n// dropping it, which inverted the front/back shading. The same rule\r\n// holds the key-light shade, the creature quad/box emitters and the\r\n// weapon grip system below: one home each, never a per-piece copy.\r\nimport { ARM_X } from './neutralBody.mjs';\r\n\r\nexport const SEG_DEFAULT = 24;\r\n\r\n// super-ellipse ring; p<1 = squarish (plated), p=1 = round.\r\nconst sq = (u, p) => Math.sign(u) * Math.pow(Math.abs(u), p);\r\nconst ring = (cx, y, cz, rx, rz, i, seg, p, a0 = 0, aSpan = Math.PI * 2) => { const a = a0 + (i / seg) * aSpan; return [cx + rx * sq(Math.cos(a), p), y, cz + rz * sq(Math.sin(a), p)]; };\r\n\r\n// Loft `rows` into `faces`, centred at (cx, cz-per-row), normals forced\r\n// OUTWARD from the ring centre. rows: {y, rx, rz, p?, cz?}.\r\nexport function loftPiece(faces, rows, { cx = 0, seg = SEG_DEFAULT, group, capTop = true, capBottom = true, arc = null } = {}) {\r\n  const a0 = arc ? arc[0] : 0, aSpan = arc ? (arc[1] - arc[0]) : Math.PI * 2;\r\n  if (arc) { capTop = false; capBottom = false; }\r\n  const R = rows.slice().sort((a, b) => a.y - b.y);\r\n  // cap an end ring with a triangle fan to its centre so the tube isn't\r\n  // see-through. `up` = +1 for the top ring, -1 for the bottom.\r\n  const cap = (row, up) => {\r\n    const cz = row.cz ?? 0, p = row.p ?? 1;\r\n    const c = [cx, row.y, cz];\r\n    for (let i = 0; i < seg; i++) {\r\n      const a = ring(cx, row.y, cz, row.rx, row.rz, i, seg, p, a0, aSpan);\r\n      const b = ring(cx, row.y, cz, row.rx, row.rz, i + 1, seg, p, a0, aSpan);\r\n      // wind so the normal faces along the axis (up/down)\r\n      const tri = up > 0 ? [c, b, a] : [c, a, b];\r\n      faces.push({ p: [...tri[0], ...tri[1], ...tri[2], ...tri[2]], n: [0, up, 0], g: group });\r\n    }\r\n  };\r\n  if (capBottom) cap(R[0], -1);\r\n  if (capTop) cap(R[R.length - 1], +1);\r\n  for (let k = 0; k + 1 < R.length; k++) {\r\n    const a = R[k], b = R[k + 1]; if (Math.abs(a.y - b.y) < 1e-6) continue;\r\n    const pa = a.p ?? 1, pb = b.p ?? 1, az = a.cz ?? 0, bz = b.cz ?? 0;\r\n    for (let i = 0; i < seg; i++) {\r\n      const p0 = ring(cx, a.y, az, a.rx, a.rz, i, seg, pa, a0, aSpan), p1 = ring(cx, a.y, az, a.rx, a.rz, i + 1, seg, pa, a0, aSpan);\r\n      const p2 = ring(cx, b.y, bz, b.rx, b.rz, i + 1, seg, pb, a0, aSpan), p3 = ring(cx, b.y, bz, b.rx, b.rz, i, seg, pb, a0, aSpan);\r\n      const ux = p1[0]-p0[0], uy = p1[1]-p0[1], uz = p1[2]-p0[2];\r\n      const vx = p3[0]-p0[0], vy = p3[1]-p0[1], vz = p3[2]-p0[2];\r\n      let nx = uy*vz-uz*vy, ny = uz*vx-ux*vz, nz = ux*vy-uy*vx;\r\n      const L = Math.hypot(nx, ny, nz) || 1; nx/=L; ny/=L; nz/=L;\r\n      // force outward: flip if pointing toward the ring centre (cx, cz)\r\n      const mx = (p0[0]+p2[0])/2 - cx, mz = (p0[2]+p2[2])/2 - (az+bz)/2;\r\n      if (nx*mx + nz*mz < 0) { nx=-nx; ny=-ny; nz=-nz; }\r\n      faces.push({ p: [...p0, ...p1, ...p2, ...p3], n: [nx, ny, nz], g: group });\r\n    }\r\n  }\r\n}\r\n\r\n// Ramp entry for intensity t (0..1), clamped to the ends.\r\nconst rampAt = (ramp, t) => ramp[Math.max(0, Math.min(ramp.length - 1, Math.round(t * (ramp.length - 1))))];\r\n\r\n// Bake per-face colour: metal ramp by normal (upper-right key), snapped.\r\n// AUDIT 68 S06-snap-shade-dup: the ONE key-light snap for every piece -\r\n// hair (0.1/0.2) and tail/scales/fur/tusks (0.08/0.18) pass their own\r\n// floor/bias instead of keeping a copy of the light.\r\nexport function shadePiece(faces, ramp, floor = 0.05, bias = 0.15) {\r\n  const Lx = 0.5, Ly = 0.55, Lz = 0.67, Ln = Math.hypot(Lx, Ly, Lz);\r\n  for (const f of faces) {\r\n    const it = Math.max(floor, (f.n[0]*Lx + f.n[1]*Ly + f.n[2]*Lz) / Ln * 0.9 + bias);\r\n    f._i = Math.min(1, it);\r\n    f.c = rampAt(ramp, f._i);\r\n    f.r = ramp; f.fb = [floor, bias]; // MYR5: relit per facing by gala-rig/raster.mjs\r\n  }\r\n  return faces;\r\n}\r\n\r\n// One flat face ({p: 12 floats, n, g}) with its normal from the quad's\r\n// edges (b-a) x (d-a); a triangle passes its last vertex twice. The\r\n// key-lit pieces (tail, hair, scales, tusks, drapes) all emit through it.\r\nexport function pushQuad(out, a, b, c, d, g) {\r\n  const ux = b[0]-a[0], uy = b[1]-a[1], uz = b[2]-a[2];\r\n  const vx = d[0]-a[0], vy = d[1]-a[1], vz = d[2]-a[2];\r\n  const nx = uy*vz-uz*vy, ny = uz*vx-ux*vz, nz = ux*vy-uy*vx;\r\n  const L = Math.hypot(nx, ny, nz) || 1;\r\n  out.push({ p: [...a, ...b, ...c, ...d], n: [nx/L, ny/L, nz/L], g });\r\n}\r\n\r\n// AUDIT 68 S06-creature-quadder-box-dup: the creature pieces' hand-shaded\r\n// quad emitter - pushQuad's face, colour snapped straight from `shade`\r\n// (no key light). One home; `group` is the rig tag.\r\nexport function quadder(faces, ramp, group = 'body') {\r\n  return (a, b, c, d, shade) => {\r\n    pushQuad(faces, a, b, c, d, group);\r\n    const f = faces[faces.length - 1];\r\n    f.c = [...rampAt(ramp, shade)];\r\n    f._i = shade;\r\n    f.r = ramp; f.fixed = shade; // MYR5: hand-shaded piece, kept as authored\r\n  };\r\n}\r\n\r\n// Six-sided box through a quadder, front (z1) face at `side`. A piece\r\n// family's own shading (default side/under, back and flank factors) is\r\n// named ONCE in its boxer({...}) call instead of in a private copy.\r\nexport function boxer({ side: sideDefault = 0.66, under: underDefault = 0.3, back = 0.7, flank = 0.85 } = {}) {\r\n  return (quad, x0, y0, z0, x1, y1, z1, top = 0.95, side = sideDefault, under = underDefault) => {\r\n    quad([x0, y1, z1], [x1, y1, z1], [x1, y0, z1], [x0, y0, z1], side); // front\r\n    quad([x1, y1, z0], [x0, y1, z0], [x0, y0, z0], [x1, y0, z0], side * back); // back\r\n    quad([x1, y1, z1], [x1, y1, z0], [x1, y0, z0], [x1, y0, z1], side * flank); // right\r\n    quad([x0, y1, z0], [x0, y1, z1], [x0, y0, z1], [x0, y0, z0], side * flank); // left\r\n    quad([x0, y1, z0], [x1, y1, z0], [x1, y1, z1], [x0, y1, z1], top); // top\r\n    quad([x0, y0, z1], [x1, y0, z1], [x1, y0, z0], [x0, y0, z0], under); // underside\r\n  };\r\n}\r\n\r\n// Body vertical compress - mirror src/characters/neutralBody.js.\r\nexport const HSCALE = 0.9;\r\nexport function compress(faces) { for (const f of faces) for (let i = 0; i < 4; i++) f.p[i*3+1] *= HSCALE; return faces; }\r\n\r\nexport const STEEL_RAMP = [[38, 40, 46], [64, 68, 78], [102, 108, 120], [146, 152, 166], [184, 192, 206], [214, 222, 236]];\r\n// Linen/tan cloth (procedural; item dye colours come from data later).\r\nexport const CLOTH_RAMP = [[58, 48, 38], [86, 72, 54], [118, 100, 74], [150, 128, 96], [180, 158, 122], [206, 186, 150]];\r\n// Chain mail (darker, cooler than plate) and leather (brown). STAGED (no\r\n// consumer yet) with cuirass/greaves - the armour-material seam's colours.\r\nexport const MAIL_RAMP = [[32, 34, 40], [52, 56, 64], [80, 86, 96], [112, 120, 132], [146, 154, 168], [180, 188, 202]];\r\nexport const LEATHER_RAMP = [[40, 28, 20], [62, 44, 30], [88, 62, 42], [116, 84, 56], [146, 110, 76], [176, 140, 100]];\r\n\r\n// ---- THE WEAPON GRIP SYSTEM (AUDIT 68 S06-weapon-grip-bake-dup: the\r\n// sword, claymore, blade, hafted and bow builders each kept their own\r\n// copy of all of this). Every weapon is authored pre-HSCALE at the LEFT\r\n// fist, every face 'armL', and baked about the grip point in the fist.\r\nexport const LEFT_FIST_X = -ARM_X;   // left-fist column (neutralBody's arm axis)\r\nexport const GRIP_Y = 0.90;          // grip point inside the fist (pre-HSCALE)\r\n// THE SEAT (Mac's reference photo, 2026-07-05): a sword is NOT held\r\n// collinear with the forearm - the handle crosses the palm, so with\r\n// the arm hanging the blade sweeps UP-FORWARD and the pommel sits low\r\n// behind the fist. Mac: swd pitch +45deg baked (2026-07-05) - the\r\n// near-horizontal point-forward carry, family-wide.\r\nexport const SEAT_PITCH = -2.40 + Math.PI / 4;\r\n// The CLAYMORE-STANDARD two-hand grip block: stations at 0.90 (grip, the\r\n// bake pivot) and 0.74 (the OFF-HAND station - the 2H pose/attack\r\n// contract), real on-axis rings so each anchor reads as a ring centroid\r\n// (the phantom-axis rule). Every 2H family lofts THESE rows.\r\nexport const TWO_HAND_GRIP_ROWS = Object.freeze([\r\n  { y: 0.720, rx: 0.026, rz: 0.027 },\r\n  { y: 0.740, rx: 0.028, rz: 0.029 },\r\n  { y: 0.900, rx: 0.028, rz: 0.029 },\r\n  { y: 1.058, rx: 0.026, rz: 0.027 },\r\n].map(Object.freeze));\r\n\r\n// Rotate faces (verts + normals) in the y-z plane by `angle` about the\r\n// line y = pivotY, z = 0 - the grip bake, and the arrow's lay-down.\r\nexport function pitchAbout(faces, pivotY, angle) {\r\n  const c = Math.cos(angle), s = Math.sin(angle);\r\n  for (const f of faces) {\r\n    for (let i = 0; i < 4; i++) {\r\n      const dy = f.p[i*3+1] - pivotY, z = f.p[i*3+2];\r\n      f.p[i*3+1] = pivotY + c*dy - s*z;\r\n      f.p[i*3+2] = s*dy + c*z;\r\n    }\r\n    const ny = f.n[1], nz = f.n[2];\r\n    f.n[1] = c*ny - s*nz; f.n[2] = s*ny + c*nz;\r\n  }\r\n  return faces;\r\n}\r\n","pod/gala-rig/dfo/sword.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/sword.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// The Longsword, held in the rig's LEFT fist. Authored in rest space\r\n// (pre-HSCALE, like every piece): the mitten grips a vertical hilt, so\r\n// at rest the blade hangs point-down beside the leg (a natural carry),\r\n// and the melee1H pose / gait transforms carry it rigidly - every face\r\n// is tagged 'armL', so it folds with the forearm about the elbow and\r\n// swings about the shoulder exactly like the arm (no separate weapon\r\n// transform). Left fist: cx = -ARM_X 0.235, palm y 0.82..0.985.\r\n//\r\n// Proportions from the body's 2.0-unit (~180cm) height: a big 0.70-unit\r\n// blade (~126cm - a hand-and-a-half presence), broad cruciform guard,\r\n// round grip + pommel. Shading = the classic metal ramp for the item's\r\n// material (weapons.js weaponMaterialRamp over dyes METAL_TABLES +\r\n// ART_PAL).\r\nimport { loftPiece, shadePiece, compress, LEFT_FIST_X, GRIP_Y, SEAT_PITCH, pitchAbout } from './pieceLoft.mjs';\r\n\r\n// THE GRIP: the seat (pieceLoft's SEAT_PITCH) is baked as a rotation of\r\n// the whole sword (verts + normals) about the GRIP POINT in the y-z\r\n// plane; every arm/pose/gait transform then carries the correct grip\r\n// for free.\r\nexport function buildSword(ramp) {\r\n  const faces = [];\r\n  const G = { group: 'armL', cx: LEFT_FIST_X, seg: 10 };\r\n  // POMMEL: round counterweight above the fist.\r\n  loftPiece(faces, [\r\n    { y: 0.998, rx: 0.014, rz: 0.014 },\r\n    { y: 1.018, rx: 0.024, rz: 0.024 },\r\n    { y: 1.042, rx: 0.018, rz: 0.018 },\r\n  ], G);\r\n  // GRIP: through the fist (hidden inside the mitten), a leather-thin\r\n  // column; visible only in the gap between fingertips and guard.\r\n  // 0.90 = the GRIP STATION: real on-axis vertices at the bake pivot\r\n  // (a leather collar) so the true hilt anchor reads as the ring\r\n  // centroid - end-only tubes put \"nearest vert to the grip\" 0.1 off\r\n  // axis (the phantom-axis bug).\r\n  loftPiece(faces, [\r\n    { y: 0.790, rx: 0.021, rz: 0.022 },\r\n    { y: 0.900, rx: 0.023, rz: 0.024 },\r\n    { y: 0.998, rx: 0.021, rz: 0.022 },\r\n  ], { ...G, capTop: false, capBottom: false });\r\n  // CROSSGUARD: broad cruciform bar, wide across x, squarish section.\r\n  loftPiece(faces, [\r\n    { y: 0.766, rx: 0.015, rz: 0.084, p: 0.45 },\r\n    { y: 0.792, rx: 0.017, rz: 0.090, p: 0.45 },\r\n  ], G);\r\n  // BLADE: flat tapered loft - wide edge-to-edge in Z (the swing\r\n  // plane, so the FLAT faces sideways like the reference once the\r\n  // grip pitch is baked), thin through in X; straight taper to a point.\r\n  loftPiece(faces, [\r\n    { y: 0.766, rx: 0.009, rz: 0.030, p: 0.55 },\r\n    { y: 0.560, rx: 0.0085, rz: 0.028, p: 0.55 },\r\n    { y: 0.340, rx: 0.008, rz: 0.024, p: 0.55 },\r\n    { y: 0.150, rx: 0.006, rz: 0.016, p: 0.55 },\r\n    { y: 0.068, rx: 0.003, rz: 0.003, p: 0.55 }, // point\r\n  ], { ...G, capTop: false });\r\n  return compress(shadePiece(pitchAbout(faces, GRIP_Y, SEAT_PITCH), ramp));\r\n}\r\n","pod/gala-rig/dfo/tail.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/tail.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// Argonian tail: a long, curved, tapering tube that roots at the lower\r\n// back and drops down + back, with a dorsal spine-fin running its\r\n// length, raised scale bumps over the surface, and wider ventral scutes\r\n// underneath. Built in FINAL (compressed) body space and tagged 'body'\r\n// so it moves with the pelvis. Highly detailed - the focus piece.\r\nimport { shadePiece, pushQuad } from './pieceLoft.mjs';\r\n\r\nexport function buildTail(skin, kind = 'argonian') {\r\n  const faces = [];\r\n  const quad = (a, b, c, d) => pushQuad(faces, a, b, c, d, 'body');\r\n  const tri = (a, b, c) => quad(a, b, c, c);\r\n\r\n  // Spine: roots at the pelvis back (y~0.90, z-0.11), sweeps back + down,\r\n  // bellies out, then curls back up to a fine tip. (compressed space)\r\n  const spine = (kind === 'khajiit' ? [\r\n    { y: 0.930, z:  0.010, r: 0.040 }, // root - buried INSIDE the pelvis\r\n    { y: 0.968, z: -0.068, r: 0.038 }, // emerging, lifting (cats carry it up)\r\n    { y: 1.004, z: -0.140, r: 0.037 },\r\n    { y: 1.024, z: -0.220, r: 0.036 }, // crest of the arch\r\n    { y: 1.010, z: -0.302, r: 0.035 }, // starting down\r\n    { y: 0.958, z: -0.376, r: 0.034 },\r\n    { y: 0.882, z: -0.436, r: 0.033 },\r\n    { y: 0.796, z: -0.478, r: 0.031 },\r\n    { y: 0.706, z: -0.498, r: 0.030 }, // gentle S\r\n    { y: 0.620, z: -0.494, r: 0.029 },\r\n    { y: 0.544, z: -0.468, r: 0.030 }, // curling forward\r\n    { y: 0.486, z: -0.428, r: 0.033 }, // bushy tip swells\r\n    { y: 0.452, z: -0.380, r: 0.034 }, // bushiest\r\n    { y: 0.436, z: -0.332, r: 0.024 },\r\n    { y: 0.430, z: -0.292, r: 0.008 }, // tip\r\n  ] : [\r\n    { y: 0.930, z:  0.010, r: 0.104 }, // root - buried INSIDE the pelvis (front of the ring is well inside)\r\n    { y: 0.958, z: -0.078, r: 0.100 }, // emerging through the lower back, arching up\r\n    { y: 0.968, z: -0.166, r: 0.093 }, // crest of the arch\r\n    { y: 0.940, z: -0.252, r: 0.084 }, // starting down\r\n    { y: 0.878, z: -0.336, r: 0.074 }, // descending  (the \"\\\")\r\n    { y: 0.792, z: -0.416, r: 0.064 },\r\n    { y: 0.700, z: -0.492, r: 0.055 },\r\n    { y: 0.624, z: -0.568, r: 0.046 }, // leveling out\r\n    { y: 0.572, z: -0.648, r: 0.037 }, // trailing back  (the \"_\")\r\n    { y: 0.538, z: -0.730, r: 0.028 },\r\n    { y: 0.516, z: -0.812, r: 0.019 },\r\n    { y: 0.502, z: -0.892, r: 0.011 },\r\n    { y: 0.496, z: -0.960, r: 0.004 }, // tip, trailing behind\r\n  ]).map((p) => ({ x: 0, ...p }));\r\n\r\n  // Tangent per node.\r\n  const T = spine.map((_, i) => {\r\n    const a = spine[Math.max(0, i-1)], b = spine[Math.min(spine.length-1, i+1)];\r\n    let t = [b.x-a.x, b.y-a.y, b.z-a.z]; const l = Math.hypot(...t) || 1; return [t[0]/l, t[1]/l, t[2]/l];\r\n  });\r\n  // Planar curve (x=0): width axis = X; dorsal axis = X x tangent, a 90deg\r\n  // in-plane turn of the tangent - continuous along the curve, so no twist.\r\n  // AUDIT 68 S06-khajiit-tail-frame-flip: the sign was chosen PER RING\r\n  // (\"flip so it points up\"), which mirrored every ring past the point\r\n  // where the Khajiit tip curls forward - a bow-tie pinch in the tube.\r\n  const side = [1, 0, 0];\r\n  const frame = T.map((t) => {\r\n    let d = [side[1]*t[2]-side[2]*t[1], side[2]*t[0]-side[0]*t[2], side[0]*t[1]-side[1]*t[0]];\r\n    const dl = Math.hypot(...d) || 1; d = [d[0]/dl, d[1]/dl, d[2]/dl];\r\n    return { s: side, d };\r\n  });\r\n  const N = 10; // sides around the tube\r\n  const ringPt = (i, k) => {\r\n    const p = spine[i], { s, d } = frame[i], a = (k / N) * 2 * Math.PI, c = Math.cos(a), sn = Math.sin(a);\r\n    // slightly taller than wide (oval), belly flattened a touch\r\n    const rw = p.r, rh = p.r * 1.06;\r\n    return [p.x + rw*c*s[0] + rh*sn*d[0], p.y + rw*c*s[1] + rh*sn*d[1], p.z + rw*c*s[2] + rh*sn*d[2]];\r\n  };\r\n  const rings = spine.map((_, i) => Array.from({ length: N }, (_, k) => ringPt(i, k)));\r\n\r\n  // Tube surface, wound so the normal (tangent x circumference) faces\r\n  // OUT like the cap, fin and scutes - the old ring-first winding lit the\r\n  // whole tube inside-out (the character shader is one-sided).\r\n  for (let i = 0; i + 1 < rings.length; i++) for (let k = 0; k < N; k++) {\r\n    const j = (k + 1) % N; quad(rings[i][k], rings[i+1][k], rings[i+1][j], rings[i][j]);\r\n  }\r\n  // Tip cap.\r\n  const last = spine[spine.length - 1];\r\n  const tipC = [last.x, last.y, last.z];\r\n  for (let k = 0; k < N; k++) { const j = (k+1)%N; tri(tipC, rings[rings.length-1][j], rings[rings.length-1][k]); }\r\n\r\n  if (kind === 'argonian') {\r\n  // Dorsal spine-fin: a triangular sail-spine at each segment along the\r\n  // TOP (dorsal dir), tallest near the base, shrinking to the tip.\r\n  for (let i = 1; i + 1 < spine.length; i++) {\r\n    const p = spine[i], { d } = frame[i], t = T[i];\r\n    const top = [p.x + d[0]*p.r, p.y + d[1]*p.r, p.z + d[2]*p.r]; // surface top\r\n    const h = Math.max(0.006, p.r * 1.5); // fin height\r\n    const apex = [top[0] + d[0]*h - t[0]*p.r*0.5, top[1] + d[1]*h - t[1]*p.r*0.5, top[2] + d[2]*h - t[2]*p.r*0.5]; // raked back\r\n    // front + back faces of the fin, plus a slight side thickness\r\n    const w = Math.max(0.003, p.r * 0.18);\r\n    const fL = [top[0]+w, top[1], top[2]], fR = [top[0]-w, top[1], top[2]];\r\n    const bpt = spine[i+1]; const back = [bpt.x + frame[i+1].d[0]*bpt.r, bpt.y + frame[i+1].d[1]*bpt.r, bpt.z + frame[i+1].d[2]*bpt.r];\r\n    tri(fL, apex, fR);                 // one side\r\n    tri(fR, apex, back);               // trailing edge to next\r\n    tri(back, apex, fL);               // leading wrap\r\n  }\r\n\r\n  // Ventral scutes: wider raised plates on the UNDERSIDE (belly, -dorsal),\r\n  // banded across the tail like a snake's belly.\r\n  for (let i = 1; i + 1 < spine.length; i += 1) {\r\n    const p = spine[i], { s, d } = frame[i];\r\n    const by = -1; // ventral\r\n    const cz = [p.x + d[0]*by*p.r, p.y + d[1]*by*p.r, p.z + d[2]*by*p.r];\r\n    const wsc = p.r * 0.9, lift = p.r * 0.10;\r\n    const n = [d[0]*by, d[1]*by, d[2]*by];\r\n    const L = [cz[0] + s[0]*wsc + n[0]*lift, cz[1] + s[1]*wsc + n[1]*lift, cz[2] + s[2]*wsc + n[2]*lift];\r\n    const R = [cz[0] - s[0]*wsc + n[0]*lift, cz[1] - s[1]*wsc + n[1]*lift, cz[2] - s[2]*wsc + n[2]*lift];\r\n    const np = spine[i+1], nf = frame[i+1];\r\n    const czn = [np.x + nf.d[0]*by*np.r, np.y + nf.d[1]*by*np.r, np.z + nf.d[2]*by*np.r];\r\n    const wn = np.r * 0.9;\r\n    const Ln = [czn[0] + nf.s[0]*wn + nf.d[0]*by*np.r*0.10, czn[1] + nf.s[1]*wn + nf.d[1]*by*np.r*0.10, czn[2] + nf.s[2]*wn + nf.d[2]*by*np.r*0.10];\r\n    const Rn = [czn[0] - nf.s[0]*wn + nf.d[0]*by*np.r*0.10, czn[1] - nf.s[1]*wn + nf.d[1]*by*np.r*0.10, czn[2] - nf.s[2]*wn + nf.d[2]*by*np.r*0.10];\r\n    quad(L, R, Rn, Ln); // scute band (raised, catches light as a ridge)\r\n  }\r\n\r\n  // Scale bumps: small raised pyramids around the tube (skip dorsal +\r\n  // ventral where the fin/scutes already are), brick-offset per ring.\r\n  for (let i = 1; i + 1 < spine.length; i++) {\r\n    const p = spine[i], { s, d } = frame[i], t = T[i];\r\n    if (p.r < 0.02) continue; // too fine near the tip\r\n    const off = (i % 2) * 0.5;\r\n    for (let kk = 0; kk < N; kk++) {\r\n      const k = kk + off, a = (k / N) * 2 * Math.PI;\r\n      const upness = Math.sin(a); if (Math.abs(upness) > 0.7) continue; // leave top/bottom\r\n      const c = Math.cos(a), sn = Math.sin(a);\r\n      const nx = c*s[0] + sn*d[0], ny = c*s[1] + sn*d[1], nz = c*s[2] + sn*d[2];\r\n      const base = [p.x + p.r*nx, p.y + p.r*ny, p.z + p.r*nz];\r\n      const bump = Math.max(0.004, p.r * 0.16);\r\n      const apex = [base[0] + nx*bump, base[1] + ny*bump, base[2] + nz*bump];\r\n      // small quad-ish scale around the base using tangent + a circumferential step\r\n      const a2 = ((k + 0.9) / N) * 2 * Math.PI, c2 = Math.cos(a2), s2 = Math.sin(a2);\r\n      const nx2 = c2*s[0] + s2*d[0], ny2 = c2*s[1] + s2*d[1], nz2 = c2*s[2] + s2*d[2];\r\n      const side2 = [p.x + p.r*nx2, p.y + p.r*ny2, p.z + p.r*nz2];\r\n      const fwd = [base[0] + t[0]*p.r*0.5, base[1] + t[1]*p.r*0.5, base[2] + t[2]*p.r*0.5];\r\n      tri(base, side2, apex); tri(side2, fwd, apex); tri(fwd, base, apex);\r\n    }\r\n  }\r\n  } // end argonian decorations\r\n\r\n  if (kind === 'khajiit') {\r\n    // FUR: short soft tufts all around the tube, angled back along the\r\n    // tail, denser than scales -> a fuzzy silhouette. Bushier near the tip.\r\n    for (let i = 1; i + 1 < spine.length; i++) {\r\n      const p = spine[i], { s: sd, d } = frame[i], t = T[i];\r\n      const bushy = i > spine.length - 5 ? 1.7 : 1.0; // fluff the tip\r\n      const M = 7;\r\n      for (let m = 0; m < M; m++) {\r\n        const a = (m / M) * 2 * Math.PI + (i % 2) * 0.4, c = Math.cos(a), sn = Math.sin(a);\r\n        const nx = c*sd[0] + sn*d[0], ny = c*sd[1] + sn*d[1], nz = c*sd[2] + sn*d[2];\r\n        const base = [p.x + p.r*nx, p.y + p.r*ny, p.z + p.r*nz];\r\n        const len = p.r * 0.85 * bushy;\r\n        // tuft points outward and sweeps back along the tail\r\n        const tip = [base[0] + nx*len - t[0]*len*0.7, base[1] + ny*len - t[1]*len*0.7, base[2] + nz*len - t[2]*len*0.7];\r\n        const a2 = a + 0.5, c2 = Math.cos(a2), s2 = Math.sin(a2);\r\n        const sx2 = c2*sd[0] + s2*d[0], sy2 = c2*sd[1] + s2*d[1], sz2 = c2*sd[2] + s2*d[2];\r\n        const b2 = [p.x + p.r*sx2, p.y + p.r*sy2, p.z + p.r*sz2];\r\n        tri(base, b2, tip);\r\n      }\r\n    }\r\n  }\r\n\r\n  return shadePiece(faces, skin, 0.08, 0.18);\r\n}\r\n","pod/gala-rig/dfo/wings.mjs":"// Ported from DFO Online (github.com/Lattymoy/daggerfall-js-source @ e3054735, src/characters/pieces/wings.js),\r\n// MIT (c) 2026 Lattymoy, with Daggerfall Unity MIT (c) 2009-2023 Daggerfall Workshop. See LICENSE-DFO.txt.\r\n// MYR5 changes are marked 'MYR5:'.\r\n// WINGS.\r\n//\r\n// The last body plan in the game that nothing else can express. A\r\n// quadruped is a spine with legs, an arachnid is a ring of them, and a\r\n// werebeast is a man with the wrong head — all of them things that\r\n// stand on the ground. `behaviour: Flying` is the one category in\r\n// ENEMY_BASICS that is neither foot nor fin, and it needs a surface\r\n// rather than a limb.\r\n//\r\n// A WING IS A MEMBRANE ON FINGERS. That is the whole of it, and it is\r\n// why a wing cannot be built out of the boxes everything else here is\r\n// made of: what the eye reads is the SHEET between the bones, not the\r\n// bones. So this is panels — a fan of triangular quads running from a\r\n// shoulder root out to a row of tips — and the fingers are drawn on top\r\n// as thin ribs rather than being the structure.\r\n//\r\n// FOLD IS A PARAMETER, NOT A POSE. A bat at rest is wrapped in its own\r\n// wings and a bat in flight is twice as wide as it is long, and the\r\n// difference is one number rather than two pieces. It also means the\r\n// same wing serves an imp, which is a small man who happens to have a\r\n// pair, at a fold nothing like a bat's.\r\n//\r\n// Built in FINAL (compressed) body space and tagged 'body', so the wings\r\n// ride the torso rather than staying put while it turns.\r\n\r\nimport { HSCALE, quadder } from './pieceLoft.mjs';\r\n\r\n/** Membrane: darker than fur and warmer than chitin, and thin enough\r\n *  that light is behind it as often as on it. */\r\nexport const MEMBRANE_RAMP = [\r\n  [52, 36, 34],\r\n  [80, 56, 50],\r\n  [110, 78, 68],\r\n  [140, 104, 90],\r\n  [170, 132, 114],\r\n  [198, 162, 142],\r\n];\r\n\r\n/**\r\n * @param {number[][]} ramp\r\n * @param {object} s\r\n *   span   — how far each wing reaches from the shoulder\r\n *   at     — shoulder height, in the rig's own (uncompressed) units\r\n *   fold   — 0 spread wide, 1 wrapped in against the body\r\n *   fingers— how many ribs the membrane hangs on\r\n *   droop  — how far the tips fall below the shoulder line\r\n *   rib    — rib thickness\r\n */\r\nexport function buildWings(ramp = MEMBRANE_RAMP, s = {}) {\r\n  const { span = 0.55, at = 1.34, fold = 0.2, fingers = 4, droop = 0.24, rib = 0.012 } = s;\r\n\r\n  const faces = [];\r\n  const quad = quadder(faces, ramp);\r\n  const y0 = at * HSCALE;\r\n\r\n  for (const side of [-1, 1]) {\r\n    const rootX = side * 0.1;\r\n    const rootZ = -0.02;\r\n\r\n    // The tips, fanned from forward-and-out to back-and-in. Folding\r\n    // swings the whole fan backwards and pulls it in against the body,\r\n    // which is what a wrapped bat actually does — it does not shrink its\r\n    // wings, it puts them behind itself.\r\n    const tip = [];\r\n    for (let i = 0; i <= fingers; i++) {\r\n      const t = i / fingers;\r\n      const spread = (1 - fold) * (0.95 - t * 0.5) + fold * 0.12;\r\n      const backness = t * 0.9 + fold * 0.8;\r\n      const x = rootX + side * span * spread;\r\n      const z = rootZ - span * backness * 0.75;\r\n      const y = y0 - droop * (t * 0.7 + 0.15) * (1 - fold * 0.5);\r\n      tip.push([x, y, z]);\r\n    }\r\n\r\n    // THE MEMBRANE, panel by panel between neighbouring tips. Each panel\r\n    // is a quad from the root to two tips — it is a sheet, so it has no\r\n    // thickness and is drawn from both sides by the double-sided\r\n    // material the rest of the rig uses.\r\n    for (let i = 0; i < fingers; i++) {\r\n      const a = tip[i];\r\n      const b = tip[i + 1];\r\n      // Shade darkens along the wing: the far end is thinner and further\r\n      // from any light, and a flat membrane reads as cardboard.\r\n      const shade = 0.86 - (i / fingers) * 0.36;\r\n      quad([rootX, y0, rootZ], a, b, [rootX, y0, rootZ], shade);\r\n      // The same panel a shade darker at its trailing edge, so the sheet\r\n      // has a gradient across it rather than a flat tone.\r\n      const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2 - droop * 0.12, (a[2] + b[2]) / 2];\r\n      quad(a, mid, b, a, shade * 0.72);\r\n    }\r\n\r\n    // THE FINGERS, drawn on top as thin ribs. They are not the\r\n    // structure — the membrane is — but without them a wing is a flag.\r\n    for (const t of tip) {\r\n      const dx = t[0] - rootX;\r\n      const dy = t[1] - y0;\r\n      const dz = t[2] - rootZ;\r\n      const L = Math.hypot(dx, dz) || 1e-6;\r\n      const px = (-dz / L) * rib;\r\n      const pz = (dx / L) * rib;\r\n      quad(\r\n        [rootX - px, y0 + rib, rootZ - pz],\r\n        [rootX + px, y0 + rib, rootZ + pz],\r\n        [rootX + dx + px, y0 + dy + rib, rootZ + dz + pz],\r\n        [rootX + dx - px, y0 + dy + rib, rootZ + dz - pz],\r\n        0.95,\r\n      );\r\n    }\r\n  }\r\n\r\n  return faces;\r\n}\r\n","pod/gala-rig/entry.mjs":"// Classic-script entry: window.GalaAvatar for the app (pod/gala-avatar.js) and the site (gala/avatar.js).\r\nimport { weaponImage } from './weapon.mjs';\r\nimport { sections, dyes, defaultLook, normalize, skinColor, draw, figure, sheet, recipe, SHEET, DEFAULT_YAW, PREVIEW_YAW, PREVIEW_BARE } from './avatar.mjs';\r\nwindow.GalaAvatar = { sections, dyes, defaultLook, normalize, skinColor, draw, figure, sheet, recipe, SHEET, DEFAULT_YAW, PREVIEW_YAW, PREVIEW_BARE, renderer: 'dfo-rig-1', weaponImage };\r\n","pod/gala-rig/legacy.mjs":"// The Gala's 2D accessories, kept from the original 64x96 sprite (base plinth, party prop,\r\n// companion, standing-meditation robe flare). The figure itself is the DFO rig.\r\nconst ink='#21172e',white='#fbebce',gold='#e6c880';\r\nexport function helpers(c){\r\n const rect=(x,y,w,h,col)=>{c.fillStyle=col;c.fillRect(Math.round(x),Math.round(y),Math.round(w),Math.round(h));};\r\n const poly=(points,col,line=ink)=>{c.beginPath();points.forEach(([x,y],i)=>i?c.lineTo(x+.5,y+.5):c.moveTo(x+.5,y+.5));c.closePath();c.fillStyle=col;c.fill();if(line){c.strokeStyle=line;c.lineWidth=1;c.stroke();}};\r\n const dot=(x,y,col=gold)=>rect(x,y,1,1,col);\r\n const gem=(x,y,col=gold)=>{poly([[x,y-2],[x+2,y],[x,y+2],[x-2,y]],col);dot(x,y-1,white);};\r\n const box=(x,y,w,h,col)=>{rect(x,y,w,h,ink);rect(x+1,y+1,w-2,h-2,col);};\r\n return {rect,poly,dot,gem,box};\r\n}\r\nconst accentOf=g=>g===1?gold:g===2?white:'#88e4d9';\r\nexport function drawBase(c,p,g,{H}){const {rect,poly,gem,box}=helpers(c);const base=true;\r\n if(base){const i=p.base;poly([[13-i%3,87],[22,84],[43,84],[52+i%3,88],[45,93],[20,93]],i===6?'#913f58':i===4?'#383341':i===5?'#aea6c9':'#756580');for(let x=18;x<48;x+=3){rect(x,89,2,2,i%2?gold:'#bca7bd');if(i===3||i===9)gem(x,84-(x%3),i===3?'#81b791':H);}if(i===1)rect(18,85,29,2,gold);if(i===2)box(20,87,5,3,'#a4a0bd');if(i===7)for(let x=18;x<49;x+=4)rect(x,86,1,6,ink);if(i===8)for(let x=16;x<49;x+=8)poly([[x,88],[x+2,79],[x+5,88]],'#8db9d0');}\r\n if(g){const accent=accentOf(g);if(base){if(g===1)rect(18,92,29,2,accent);if(g===2)for(let x=15;x<52;x+=6)gem(x,91,accent);if(g===3){rect(12,94,41,1,accent);rect(10,91,2,3,accent);rect(53,91,2,3,accent);}}}\r\n}\r\nexport function drawProp(c,p,g,{S,H,B},width=0){const {rect,poly,dot,gem,box}=helpers(c);const prop=true;\r\n if(prop)switch(p.held){case 0:box(48+width,47,5,8,'#c4bdcf');rect(50+width,55,1,8,gold);rect(48+width,63,5,1,gold);rect(49+width,49,3,3,H);break;case 1:poly([[48,59],[43,46],[49,40],[58,45],[60,53]],H);for(let i=0;i<4;i++)poly([[48,59],[45+i*4,44+i%2]],gold);break;case 2:box(46,56,13,9,H);rect(47,59,11,1,gold);gem(53,59);break;case 3:poly([[49,63],[45,47],[56,47],[51,63]],'#548267');for(const [x,y] of [[45,45],[51,42],[57,46],[49,49]])gem(x,y,H);break;case 4:rect(51,47,2,36,gold);box(47,45,7,3,gold);break;case 5:rect(51,47,1,6,gold);box(47,53,10,13,B);rect(49,56,6,7,gold);gem(52,59,white);break;case 6:box(47,53,12,9,white);rect(49,55,7,1,B);gem(53,59,H);break;case 7:box(48,47,9,9,H);gem(52,51,white);rect(50,56,5,2,gold);break;case 8:rect(52,29,1,35,gold);poly([[40,33],[43,25],[52,22],[62,29],[63,33]],H);rect(42,32,20,1,gold);break;case 9:poly([[48,48],[48,43],[51,46],[56,44],[59,48],[57,55],[50,54]],S);dot(51,49,ink);dot(56,49,ink);rect(51,55,1,4,gold);rect(56,55,1,4,gold);}\r\n if(g){const accent=accentOf(g);if(g===1){rect(48,58,12,1,accent);gem(59,58,accent);}if(g===2)poly([[51,61],[57,71],[55,61]],H);if(g===3)for(const [x,y] of [[45,45],[59,48],[58,62]])gem(x,y,accent);}\r\n}\r\nexport function drawStandingFlare(canvas,c){const pose={meditationStanding:true},petOnly=false;\r\n if(pose.meditationStanding&&!petOnly){const o=document.createElement('canvas');o.width=canvas.width;o.height=96;o.getContext('2d').drawImage(canvas,0,0);c.clearRect(0,56,canvas.width,40);c.drawImage(o,0,56,canvas.width,40,-10,56,canvas.width+20,40);}\r\n}\r\nexport function drawPet(c,look,{S,H,B}){const {rect,poly,dot,gem,box}=helpers(c);const original=look.parts,companion=true,p={pet:original.pet%10},group=()=>Math.floor((original.pet||0)/10);\r\n if(companion&&original.pet){const k=p.pet,g=group('pet'),fur=[H,S,gold,'#90bca2','#d99bae','#ad9ac8','#81a891','#b1accb','#9caabb','#83b587'][k],x=9,y=k===2?67:79;\r\n if(k===0){poly([[x-6,y+5],[x-3,y],[x+3,y-1],[x+8,y+5]],fur);rect(x+6,y-3,1,4,fur);dot(x+6,y-3,white);}\r\n if([1,4,6].includes(k)){box(x-5,y-3,11,8,fur);box(x+3,y-7,7,7,fur);rect(x-4,y+5,2,3,fur);rect(x+3,y+5,2,3,fur);poly([[x+3,y-7],[x+3,y-11],[x+6,y-7]],fur);poly([[x+7,y-7],[x+10,y-11],[x+10,y-6]],fur);dot(x+7,y-4,ink);if(k===6)poly([[x-5,y],[x-11,y-4],[x-8,y+3]],fur);else rect(x-7,y-4,2,7,fur);if(k===4)rect(x+8,y-2,5,1,white);}\r\n if(k===2){poly([[x,y],[x-9,y-8],[x-9,y+6],[x,y+2]],fur);poly([[x+2,y],[x+10,y-8],[x+10,y+6],[x+2,y+2]],fur);box(x,y-3,3,10,B);gem(x-5,y-1,gold);gem(x+7,y-1,gold);}\r\n if(k===3){box(x-5,y-6,12,11,fur);rect(x,y-5,1,9,B);for(let z=0;z<3;z++){rect(x-8,y-4+z*4,3,1,gold);rect(x+7,y-4+z*4,3,1,gold);}box(x-2,y-9,5,4,B);dot(x-1,y-8,white);dot(x+1,y-8,white);}\r\n if(k===5){poly([[x-6,y],[x-6,y-6],[x-2,y-10],[x+4,y-10],[x+8,y-5],[x+8,y]],fur);for(let z=0;z<4;z++)rect(x-4+z*3,y,1,7-z%2*3,fur);dot(x-1,y-5,ink);dot(x+4,y-5,ink);}\r\n if(k===7){poly([[x-7,y-2],[x-9,y-5],[x-5,y-6],[x-3,y-10],[x,y-7],[x+5,y-10],[x+6,y-6],[x+9,y-3],[x+6,y+4],[x-5,y+4]],fur);box(x-3,y-4,3,3,white);box(x+2,y-4,3,3,white);}\r\n if(k===8){box(x-6,y-10,13,12,fur);box(x-3,y-7,7,3,B);dot(x-2,y-6,white);dot(x+2,y-6,white);box(x-5,y+2,3,4,B);box(x+3,y+2,3,4,B);rect(x,y-14,1,4,gold);gem(x,y-14,H);}\r\n if(k===9){box(x-4,y-5,10,10,'#956e55');rect(x,y-15,2,11,fur);poly([[x,y-10],[x-7,y-15],[x-6,y-8]],fur);poly([[x+2,y-12],[x+8,y-17],[x+8,y-10]],fur);dot(x-1,y-2,white);dot(x+3,y-2,white);}\r\n rect(x-3,y,8,1,gold);gem(x+1,y+1,gold);\r\n if(g===1){poly([[x-5,y-8],[x-5,y-13],[x,y-10],[x+4,y-14],[x+6,y-8]],gold);}\r\n if(g===2){poly([[x-6,y],[x-12,y-6],[x-11,y+4]],H);poly([[x+6,y],[x+12,y-6],[x+11,y+4]],H);}\r\n if(g===3){for(const [xx,yy] of [[x-9,y-10],[x+10,y-8],[x,y-17]])gem(xx,yy,'#88e4d9');rect(x-5,y-7,10,1,'#88e4d9');}\r\n }\r\n}\r\n","pod/gala-rig/look.mjs":"// The saved Gala look. v1 (17 sections, one silk dye) is read losslessly; every save is\r\n// v2: the same parts plus the new species/armour sections and a second (trim) dye.\r\n// Storage key stays 'mominc-avatar-v1' (profile sync allow-list); `normalize` keeps its\r\n// v1 error text and the 'mominc-avatar-change' contract gala-handoff.mjs relies on.\r\nimport { ALLOYS } from './palette.mjs';\r\n\r\nconst V1 = [\r\n ['body','Body','A silhouette from somewhere very far away.',['Zorbian','Vexling','Orryx','Mollu','Krell','Nymbi','Quorlan','Xelith','Dravox','Ulumi']],\r\n ['skin','Skin','Twenty tones of otherworldly complexion.',['Nebula lilac','Reactor mint','Solar coral','Lunar porcelain','Void indigo','Comet gold','Plasma rose','Tidal teal','Martian ochre','Ghost ice']],\r\n ['face','Face','Make an unforgettable first impression.',['Vela gaze','Solo orb','Triune sight','Obsidian visor','Quasar quartet','Mothkin eyes','Ziggy grin','Mollu blush','Starborn mask','Oracle six']],\r\n ['hair','Hair','Coiffed, grown, or very carefully hatched.',['Vex wave','Quasar crest','Nebula bob','Orbital knots','Plasma cascade','Spore crown','Void slick','Comet braid','Prism spikes','Lunar fringe']],\r\n ['facial','Facial details','Whiskers, frills, and social signals.',['Vela bare','Zor whiskers','Krell beard','Mollu frill','Oracle dots','Quor tendrils','Vex moustache','Dravox jaw','Nymbi veil','Ulumi sparkle']],\r\n ['headwear','Headwear','MOM can spot good headwear across a galaxy.',['Vela circlet','Orryx crown','Quasar halo','Nymbi veil','Krell cap','Xelith horns','Mollu fascinator','Dravox helm','Orbital rings','Comet antennae']],\r\n ['neck','Neckwear','The smallest, most unreasonable finishing touch.',['Zor cravat','Vela bow','Orryx pearls','Quasar ruff','Mollu scarf','Krell collar','Xelith pendant','Nymbi ribbon','Dravox chain','Ulumi choker']],\r\n ['torso','Formalwear','Alien eveningwear. Excellent tailoring.',['Vex tuxedo','Mollu gown','Orryx brocade','Quasar jumpsuit','Nymbi corset','Krell robe','Xelith doublet','Dravox tailcoat','Ulumi wrap','Zor sequin suit']],\r\n ['shoulders','Shoulders','A little room for dramatic entrances.',['Vela epaulettes','Quor petals','Krell spikes','Nymbi puffs','Orryx mantle','Vex wings','Dravox plates','Mollu fronds','Xelith orbitals','Ulumi cape']],\r\n ['arms','Sleeves','Twenty ways to wave at someone important.',['Vela silk','Quasar flares','Krell cuffs','Mollu lace','Orryx stripes','Vex sheer','Dravox panels','Nymbi bells','Xelith rings','Ulumi ribbons']],\r\n ['hands','Gloves','Keep your hands where MOM can see them.',['Vela gloves','Krell talons','Orryx cuffs','Quasar mesh','Mollu mitts','Vex rings','Dravox gauntlets','Nymbi ruffles','Xelith claws','Ulumi glow']],\r\n ['legs','Lower half','Trousers, trains, and several extra possibilities.',['Vex trousers','Mollu bell skirt','Orryx pleats','Quasar split','Nymbi bubble','Krell drape','Xelith stripes','Dravox breeches','Ulumi train','Zor shimmer']],\r\n ['feet','Footwear','For the marble floors of an alien palace.',['Vela slippers','Krell platforms','Orryx curltoes','Quasar boots','Mollu petals','Vex heels','Dravox greaves','Nymbi clouds','Xelith skates','Ulumi moonsteps']],\r\n ['held','Held accessory','An arrival accessory, not a combat loadout.',['Vela flute','Orryx fan','Quasar clutch','Mollu bouquet','Krell cane','Nymbi lantern','Vex invitation','Dravox orb','Xelith parasol','Ulumi familiar']],\r\n ['back','Back accessory','Make leaving the room just as interesting.',['Vela ribbons','Orryx cape','Quasar fins','Mollu spores','Krell spines','Nymbi wings','Vex sash','Dravox coils','Xelith satellites','Ulumi starlight']],\r\n ['base','Display base','A tiny piece of the palace to call your own.',['Vela marble','Orryx dais','Quasar moon','Mollu garden','Krell obsidian','Nymbi cloud','Vex carpet','Dravox grille','Xelith crystal','Ulumi orbit']],\r\n ['pet','Pet','A companion dressed for the same questionable occasion.',['No companion','Mollu pup','Vex moth','Orryx beetle','Quasar cat','Nymbi jelly','Krell lizard','Xelith puff','Dravox bot','Ulumi sprout']]\r\n];\r\n// v2 additions: DFO's build/race/armour axes, re-skinned. Choice 0 is always \"as before\".\r\nconst V2 = [\r\n ['build','Build','Proportions from a gravity you would not believe.',['Species build','Slight','Stout','Broad','Lanky','Heavy','Wiry','Hulking','Petite','Long-limbed']],\r\n ['head','Head shape','A skull that tells the room where you are from.',['Species head','Dome','Long skull','Hammerhead','Crest ridge','Bulb','Flat-top','Wedge','Mantis','Twin lobe']],\r\n ['crown','Antennae & crests','Sensors, crests and the occasional horn.',['No crest','Feelers','Crest fin','Ram horns','Spike crown','Neck frill','Bulb antennae','Twin horns','Ridge spines','Halo sensors']],\r\n ['jaw','Jaw','Mandibles, tusks and other table manners.',['No jaw piece','Mandibles','Tusks','Snout','Beak','Tendril beard','Fangs','Mouth plates','Gill frills','Chin horn']],\r\n ['tail','Tail','Every species looks better leaving.',['No tail','Lizard tail','Cat tail','Stinger','Fin tail','Whip tail','Club tail','Plume tail','Twin tails','Coil tail']],\r\n ['armor','Armour','Hardsuit pieces over the formalwear.',['No armour','Hardsuit torso','Shoulder pods','Bracer gauntlets','Thigh plates','Armoured boots','Pressure helm (open)','Pressure helm (closed)','Half hardsuit','Full hardsuit']],\r\n ['alloy','Alloy','What the armour is made of.',ALLOYS.map(([name]) => name)],\r\n ['shield','Shield','Held on the off hand.',['No shield','Wrist buckler','Disc','Shaped barrier','Tower field','Hex shield','Ring guard','Spike targe','Energy kite','Mirror disc']]\r\n];\r\nconst NEW_IDS = new Set(V2.map(([id]) => id));\r\nconst named = ([id, label, note, names]) => ({ id, label, note, names: [...names, ...names.map((n) => 'Xyrr ' + (n === 'No companion' ? 'slug' : n)), ...names.map((n) => 'Auv ' + (n === 'No companion' ? 'slug' : n)), ...names.map((n) => 'Oth ' + (n === 'No companion' ? 'slug' : n))] });\r\n// Twenty visually distinct choices per v1 category (unchanged ids, so saves and coach imports hold).\r\nconst curated = {\r\n body:[0,11,2,12,32,3,13,33,14,34,15,35,6,16,36,37,18,28,19,39], skin:[0,20,11,12,32,23,33,14,34,5,25,35,6,26,36,7,37,38,19,39],\r\n face:[0,21,31,22,32,23,33,14,24,34,25,35,26,36,27,28,38,19,29,39], hair:[0,11,31,12,22,32,13,33,14,34,15,35,36,17,27,37,18,38,19,29],\r\n facial:[0,11,21,31,12,32,13,23,33,14,34,15,25,35,26,17,27,18,38,39], headwear:[0,11,21,12,22,32,13,23,33,14,24,34,25,16,36,37,38,19,29,39],\r\n neck:[0,20,30,11,2,13,23,33,14,24,34,15,25,36,7,27,37,28,19,29], torso:[0,11,31,12,22,32,13,24,34,25,35,26,36,17,27,37,18,28,38,9],\r\n shoulders:[0,30,11,12,23,33,24,15,25,35,16,36,17,27,37,18,38,19,29,39], arms:[0,11,12,13,33,14,15,25,6,26,36,7,27,37,18,28,38,19,29,39],\r\n hands:[0,11,21,31,12,22,33,14,24,34,25,36,17,27,37,18,28,38,9,29], legs:[0,11,21,31,22,32,23,24,34,15,25,35,16,36,27,37,18,28,38,29],\r\n feet:[0,11,21,31,12,32,3,13,14,24,34,25,26,36,17,37,28,38,9,29], held:[0,11,21,31,32,23,33,14,24,34,25,16,17,27,37,18,28,38,29,39],\r\n back:[0,11,21,31,12,22,13,23,33,14,34,15,35,16,26,17,37,38,19,29], base:[0,11,2,22,32,23,4,34,5,25,35,16,26,36,37,18,28,38,19,39],\r\n pet:[0,10,20,30,11,12,32,3,24,15,25,35,36,27,37,28,38,19,29,39],\r\n build:[0,1,2,3,4,5,6,7,8,9], head:[0,1,2,3,4,5,6,7,8,9], alloy:[0,1,2,3,4,5,6,7,8,9],\r\n crown:[0,1,2,3,4,5,6,7,8,9,21,33,17,39], jaw:[0,1,2,3,4,5,6,7,8,9,22,31,16], tail:[0,1,2,3,4,5,6,7,8,9,21,32,17,38],\r\n armor:[0,1,2,3,4,5,6,7,8,9,18,29,39], shield:[0,1,2,3,4,5,6,7,8,9,22,33,17],\r\n};\r\nexport const sections = [...V1, ...V2].map(named);\r\n// One choice per distinct design (Ian, 20 Oct): a collection (choice / 10) only recolours a base design's\r\n// accents, so the Xyrr/Auv/Oth repeats of a base are cut; the first curated pick of each base is the one kept.\r\n// Pet 0 is \"no companion\"; pets 10/20/30 are the slug, a real design. Saved duplicates migrate in normalize().\r\nconst designOf = (id, v) => (id === 'pet' && v === 0 ? 'none' : v % 10);\r\nsections.forEach((s) => {\r\n  const kept = new Map();\r\n  for (const v of curated[s.id]) if (!kept.has(designOf(s.id, v))) kept.set(designOf(s.id, v), v);\r\n  s.choices = [...kept.values()];\r\n  s.canonical = (v) => kept.get(designOf(s.id, v)) ?? v;\r\n});\r\nexport const dyes = ['#9762b6', '#bd476e', '#467f9e', '#4b9478', '#d39d46', '#485aa0', '#d17e52', '#c7adba', '#5f596d', '#83b8b6'];\r\nexport const skin = ['#b18fc8', '#80ba98', '#d78989', '#ddd3c5', '#7371ae', '#c6a55e', '#ce84b6', '#67a5a6', '#b77857', '#a5c8d8'];\r\nexport const defaultLook = { schema: 'mominc-avatar', version: 2, name: 'Velora of the Ninth Moon', dye: 0, trim: 4, parts: Object.fromEntries(sections.map((s) => [s.id, 0])) };\r\n\r\n/** v1 or v2 -> v2. Throws the v1 messages on anything malformed. */\r\nexport function normalize(raw, weapons = globalThis.window?.GalaWeapons) {\r\n  if (!raw || raw.schema !== 'mominc-avatar' || (raw.version !== 1 && raw.version !== 2) || !raw.parts) throw Error('Choose a MOM Inc avatar file.');\r\n  const n = { schema: 'mominc-avatar', version: 2, name: typeof raw.name === 'string' ? raw.name.trim().slice(0, 64) : '', dye: raw.dye, trim: raw.version === 2 && raw.trim != null ? raw.trim : raw.dye, parts: {} };\r\n  if (!Number.isInteger(n.dye) || n.dye < 0 || n.dye > 9) throw Error('This look has an unknown silk colour.');\r\n  if (!Number.isInteger(n.trim) || n.trim < 0 || n.trim > 9) throw Error('This look has an unknown trim colour.');\r\n  for (const s of sections) {\r\n    const v = raw.parts[s.id] ?? (s.id === 'pet' || NEW_IDS.has(s.id) ? 0 : undefined);\r\n    if (!Number.isInteger(v) || v < 0 || v >= s.names.length) throw Error('This look has an unknown wardrobe piece.');\r\n    n.parts[s.id] = s.canonical(v); // a cut duplicate reads as its kept design\r\n  }\r\n  if (raw.weapon != null) { if (!weapons) throw Error('Open the updated Gala to import this weapon.'); n.weapon = weapons.normalize(raw.weapon); }\r\n  return n;\r\n}\r\n\r\nconst tone = (hex, amount) => { const n = parseInt(hex.slice(1), 16); return '#' + [n >> 16, (n >> 8) & 255, n & 255].map((v) => Math.max(0, Math.min(255, v + amount)).toString(16).padStart(2, '0')).join(''); };\r\nexport { tone };\r\nexport function skinColor(look) { const value = look.parts.skin || 0, group = Math.floor(value / 10); return tone(skin[value % 10], group * 14 - (group === 3 ? 60 : 0)); }\r\n","pod/gala-rig/palette.mjs":"// Gala colour ramps. DFO's palettes.js law (6 steps, dark -> light; a face's light is\r\n// snapped onto one step) re-authored for aliens: every ramp is generated from one swatch\r\n// with hue-shifted shadows (toward violet) and warm highlights, so a single dye or skin\r\n// pick recolours a whole figure with banded pixel shading. Belly ramps are the paler\r\n// counter-shade pair (DFO's Khajiit coat/belly idea) used on chest, belly and inner faces.\r\n\r\nconst hex = (h) => { const n = parseInt(h.slice(1), 16); return [n >> 16, (n >> 8) & 255, n & 255]; };\r\nfunction toHsl([r, g, b]) {\r\n  r /= 255; g /= 255; b /= 255; const mx = Math.max(r, g, b), mn = Math.min(r, g, b), l = (mx + mn) / 2;\r\n  if (mx === mn) return [0, 0, l];\r\n  const d = mx - mn, s = l > 0.5 ? d / (2 - mx - mn) : d / (mx + mn);\r\n  const h = mx === r ? (g - b) / d + (g < b ? 6 : 0) : mx === g ? (b - r) / d + 2 : (r - g) / d + 4;\r\n  return [h * 60, s, l];\r\n}\r\nfunction toRgb([h, s, l]) {\r\n  h = ((h % 360) + 360) % 360 / 360;\r\n  if (!s) return [l, l, l].map((v) => Math.round(v * 255));\r\n  const q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q;\r\n  const f = (t) => { t = (t + 1) % 1; return t < 1 / 6 ? p + (q - p) * 6 * t : t < 0.5 ? q : t < 2 / 3 ? p + (q - p) * (2 / 3 - t) * 6 : p; };\r\n  return [f(h + 1 / 3), f(h), f(h - 1 / 3)].map((v) => Math.round(Math.max(0, Math.min(1, v)) * 255));\r\n}\r\nconst lerpHue = (a, b, t) => { const d = ((b - a + 540) % 360) - 180; return a + d * t; };\r\n\r\n/** Six-step ramp around a swatch. `lift` raises the whole ramp (belly / pearl). */\r\nexport function ramp(swatch, { lift = 0, spread = 1 } = {}) {\r\n  const [h, s, l] = toHsl(typeof swatch === 'string' ? hex(swatch) : swatch);\r\n  const out = [];\r\n  for (let i = 0; i < 6; i++) {\r\n    const t = i / 5, k = (t - 0.62) * spread;               // the swatch sits near step 3\r\n    const L = Math.max(0.05, Math.min(0.9, l + lift + k * (k < 0 ? 0.85 : 0.55)));\r\n    const H = k < 0 ? lerpHue(h, 262, -k * 0.35) : lerpHue(h, 48, k * 0.25);\r\n    const S = Math.max(0, Math.min(1, s * (k < 0 ? 1 + -k * 0.25 : 1 - k * 0.35)));\r\n    out.push(toRgb([H, S, L]));\r\n  }\r\n  return out;\r\n}\r\n\r\n// Ten alloy tiers (DFO's Iron..Daedric ladder, re-skinned) - plain metal ramps.\r\nexport const ALLOYS = [\r\n  ['Ferro', '#7c7f88'], ['Steelite', '#9aa6b8'], ['Silvered', '#c9d0dc'], ['Lumen', '#b9d9b0'], ['Duranium', '#c79a5c'],\r\n  ['Mithrite', '#9fc6e8'], ['Adamant', '#7fa39a'], ['Obsidian', '#3c3550'], ['Orkan', '#6f8a4e'], ['Voidsteel', '#9a3c4a'],\r\n];\r\n\r\nexport const FIXED = {\r\n  gold: ramp('#e6c880'), pearl: ramp('#f2e6dc', { lift: 0.04 }), glow: ramp('#88e4d9', { lift: 0.05, spread: 0.8 }),\r\n  dark: ramp('#3a2f4c'), ivory: ramp('#e4dcc0'), white: ramp('#fbebce', { lift: 0.08, spread: 0.5 }),\r\n  ink: [[33, 23, 46], [33, 23, 46], [40, 30, 56], [40, 30, 56], [48, 38, 64], [48, 38, 64]],\r\n  glass: ramp('#9fe8ff', { lift: 0.06 }), membrane: ramp('#8a6a90'),\r\n};\r\n","pod/gala-rig/parts.mjs":"// The Gala piece language: every wardrobe/species choice is DATA over DFO's neutral rig\r\n// (the way DFO's humanClasses/villagerDesigns/beasts are data). A design is a list of\r\n// pieces; this file turns each into rig faces tagged with a rig group and a material name.\r\n// All coordinates are the rig's pre-HSCALE space (see ANATOMY in designs.mjs).\r\nimport { loftPiece, pushQuad, shadePiece, compress, HSCALE } from './dfo/pieceLoft.mjs';\r\nimport { flareRows, capeRows } from './dfo/draped.mjs';\r\nimport { buildHelm } from './dfo/helm.mjs';\r\nimport { buildPauldrons } from './dfo/pauldrons.mjs';\r\nimport { buildTail } from './dfo/tail.mjs';\r\nimport { buildWings } from './dfo/wings.mjs';\r\nimport { buildTusks, buildBrow } from './dfo/orcHead.mjs';\r\nimport { buildHair } from './dfo/hair.mjs';\r\nimport { buildBeastBody, buildBeastTail } from './dfo/beastBody.mjs';\r\nimport { buildBeastHead, buildHorns } from './dfo/beastHead.mjs';\r\nimport { buildArachnid, buildSting } from './dfo/arachnid.mjs';\r\nimport { buildFishBody } from './dfo/fishBody.mjs';\r\nimport { buildHorseBody } from './dfo/centaurBody.mjs';\r\nimport { buildBladeWeapon } from './dfo/blades.mjs';\r\nimport { buildHaftedWeapon } from './dfo/hafted.mjs';\r\nimport { buildLongBow, buildShortBow } from './dfo/bow.mjs';\r\nimport { buildClaymore } from './dfo/claymore.mjs';\r\nimport { buildSword } from './dfo/sword.mjs';\r\nimport { LEFT_FIST_X, GRIP_Y, SEAT_PITCH, pitchAbout } from './dfo/pieceLoft.mjs';\r\n\r\n// DFO's held weapons are baked at the left fist with the point-forward seat. Undo that into a neutral\r\n// weapon frame: grip at the origin, weapon along +y (blade up), y in rig units (no height squash).\r\nfunction weaponFrame(faces, pitched = true) {\r\n  for (const f of faces) for (let i = 0; i < 4; i++) f.p[i * 3 + 1] /= HSCALE;\r\n  if (pitched) pitchAbout(faces, GRIP_Y, -SEAT_PITCH);\r\n  for (const f of faces) for (let i = 0; i < 4; i++) { f.p[i * 3] -= LEFT_FIST_X; f.p[i * 3 + 1] = -(f.p[i * 3 + 1] - GRIP_Y); f.p[i * 3] = -f.p[i * 3]; }\r\n  for (const f of faces) for (let i = 0; i < 4; i++) f.p[i * 3 + 1] *= HSCALE; // back to rig space (emit squashes like every piece)\r\n  return faces;\r\n}\r\n// Scale / move a whole DFO piece (pets are small creatures; weapons sit in their own frame).\r\nfunction place(faces, scale = 1, at = null, rot = null) {\r\n  if (rot) for (const f of faces) for (let i = 0; i < 4; i++) {\r\n    let x = f.p[i * 3], y = f.p[i * 3 + 1], z = f.p[i * 3 + 2], c, s;\r\n    if (rot[0]) { c = Math.cos(rot[0]); s = Math.sin(rot[0]); [y, z] = [c * y - s * z, s * y + c * z]; }\r\n    if (rot[1]) { c = Math.cos(rot[1]); s = Math.sin(rot[1]); [x, z] = [c * x + s * z, -s * x + c * z]; }\r\n    if (rot[2]) { c = Math.cos(rot[2]); s = Math.sin(rot[2]); [x, y] = [c * x - s * y, s * x + c * y]; }\r\n    f.p[i * 3] = x; f.p[i * 3 + 1] = y; f.p[i * 3 + 2] = z;\r\n  }\r\n  for (const f of faces) for (let i = 0; i < 4; i++) {\r\n    f.p[i * 3] = f.p[i * 3] * scale + (at?.[0] ?? 0); f.p[i * 3 + 1] = f.p[i * 3 + 1] * scale + (at?.[1] ?? 0) * HSCALE; f.p[i * 3 + 2] = f.p[i * 3 + 2] * scale + (at?.[2] ?? 0);\r\n  }\r\n  return faces;\r\n}\r\n\r\nconst GREY = [[40, 40, 40], [80, 80, 80], [120, 120, 120], [160, 160, 160], [200, 200, 200], [240, 240, 240]];\r\nconst side = (group, x) => group === 'arm' ? (x < 0 ? 'armL' : 'armR') : group === 'leg' ? (x < 0 ? 'legL' : 'legR') : (group || 'body');\r\nconst tag = (faces, mat) => { for (const f of faces) f._mat = f._mat || mat; return faces; };\r\n\r\n// Zone names -> neutralBody displace zone (groups + which limb column it pivots on).\r\nconst ZONE = {\r\n  torso: { groups: ['body'] }, pelvis: { groups: ['body'] }, neck: { groups: ['body'] }, head: { groups: ['head'] },\r\n  sleeves: { groups: ['armL', 'armR'], arm: true }, hands: { groups: ['armL', 'armR'], arm: true },\r\n  legs: { groups: ['legL', 'legR'], leg: true }, feet: { groups: ['legL', 'legR'], leg: true },\r\n};\r\nexport function zoneOf(z) {\r\n  const base = ZONE[z.zone]; if (!base) return null;\r\n  const yLo = z.yLo ?? { hands: 0.80, feet: 0, neck: 1.62, head: 1.70 }[z.zone] ?? 0;\r\n  const yHi = z.yHi ?? { hands: 0.99, feet: 0.16, neck: 1.75, head: 2.10 }[z.zone] ?? 2.2;\r\n  return { ...base, yLo, yHi, th: z.th ?? 0.008, zScale: z.zScale ?? 1, zLo: z.zLo, zHi: z.zHi, mat: z.mat || 'dye' };\r\n}\r\n\r\nfunction tube(out, pts, group, sides = 6) {\r\n  const P = pts.map(([x, y, z, r]) => ({ x, y: y * HSCALE, z, r }));\r\n  const rings = P.map((p, i) => {\r\n    const a = P[Math.max(0, i - 1)], b = P[Math.min(P.length - 1, i + 1)];\r\n    let t = [b.x - a.x, b.y - a.y, b.z - a.z]; const tl = Math.hypot(...t) || 1; t = t.map((v) => v / tl);\r\n    let u = Math.abs(t[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0]; const d = u[0] * t[0] + u[1] * t[1] + u[2] * t[2];\r\n    u = [u[0] - d * t[0], u[1] - d * t[1], u[2] - d * t[2]]; const ul = Math.hypot(...u) || 1; u = u.map((v) => v / ul);\r\n    const bv = [t[1] * u[2] - t[2] * u[1], t[2] * u[0] - t[0] * u[2], t[0] * u[1] - t[1] * u[0]];\r\n    return Array.from({ length: sides }, (_, k) => { const an = k / sides * Math.PI * 2, c = Math.cos(an) * p.r, s = Math.sin(an) * p.r; return [p.x + c * u[0] + s * bv[0], p.y + c * u[1] + s * bv[1], p.z + c * u[2] + s * bv[2]]; });\r\n  });\r\n  for (let i = 0; i + 1 < rings.length; i++) for (let k = 0; k < sides; k++) { const j = (k + 1) % sides; pushQuad(out, rings[i][k], rings[i][j], rings[i + 1][j], rings[i + 1][k], group); }\r\n  const end = (ring, c) => { for (let k = 0; k < sides; k++) pushQuad(out, c, ring[(k + 1) % sides], ring[k], ring[k], group); };\r\n  end(rings[0], [P[0].x, P[0].y, P[0].z]); const L = P[P.length - 1]; end(rings[rings.length - 1], [L.x, L.y, L.z]);\r\n}\r\n\r\nfunction box(out, [x0, y0, z0, x1, y1, z1], group) {\r\n  y0 *= HSCALE; y1 *= HSCALE;\r\n  const q = (a, b, c, d) => pushQuad(out, a, b, c, d, group);\r\n  q([x0, y1, z1], [x1, y1, z1], [x1, y0, z1], [x0, y0, z1]); q([x1, y1, z0], [x0, y1, z0], [x0, y0, z0], [x1, y0, z0]);\r\n  q([x1, y1, z1], [x1, y1, z0], [x1, y0, z0], [x1, y0, z1]); q([x0, y1, z0], [x0, y1, z1], [x0, y0, z1], [x0, y0, z0]);\r\n  q([x0, y1, z0], [x1, y1, z0], [x1, y1, z1], [x0, y1, z1]); q([x0, y0, z1], [x1, y0, z1], [x1, y0, z0], [x0, y0, z0]);\r\n}\r\n\r\n// Flat fan/fin: a polygon of points (x,y,z) given as a triangle fan from the first point.\r\nfunction fan(out, pts, group) {\r\n  const P = pts.map(([x, y, z]) => [x, y * HSCALE, z]);\r\n  for (let i = 1; i + 1 < P.length; i++) pushQuad(out, P[0], P[i], P[i + 1], P[i + 1], group);\r\n}\r\n\r\nconst mirrorX = (v) => Array.isArray(v[0]) ? v.map(mirrorX) : [-v[0], ...v.slice(1)];\r\n\r\n/** One piece -> {faces, marks, zones}. ctx = {build, scale}. */\r\nexport function emit(piece, ctx = {}) {\r\n  const faces = [], marks = [], zones = [];\r\n  const mat = piece.mat || 'dye';\r\n  const sides = piece.mirror ? [1, -1] : [1];\r\n  if (piece.zone) { const z = zoneOf(piece); if (z) zones.push(z); return { faces, marks, zones }; }\r\n  for (const sx of sides) {\r\n    const out = [];\r\n    if (piece.loft) {\r\n      const cx = (piece.cx ?? 0) * sx;\r\n      const arc = piece.arc && sx < 0 ? [Math.PI - piece.arc[1], Math.PI - piece.arc[0]] : piece.arc;\r\n      loftPiece(out, piece.loft.map(([y, rx, rz, p = 1, cz = 0]) => ({ y, rx, rz, p, cz })), { cx, seg: piece.seg ?? 12, group: side(piece.group, cx || sx), arc, capTop: piece.caps !== false, capBottom: piece.caps !== false });\r\n      compress(out);\r\n    } else if (piece.tube) {\r\n      const pts = sx < 0 ? piece.tube.map(([x, ...r]) => [-x, ...r]) : piece.tube;\r\n      tube(out, pts, side(piece.group, pts[0][0] || sx), piece.sides ?? 6);\r\n    } else if (piece.box) {\r\n      const [x0, y0, z0, x1, y1, z1] = piece.box, b = sx < 0 ? [-x1, y0, z0, -x0, y1, z1] : piece.box;\r\n      box(out, b, side(piece.group, (b[0] + b[3]) / 2 || sx));\r\n    } else if (piece.fan) {\r\n      const pts = sx < 0 ? mirrorX(piece.fan) : piece.fan;\r\n      fan(out, pts, side(piece.group, pts[0][0] || sx));\r\n    } else if (piece.drape) {\r\n      const d = piece.drape, rows = d.kind === 'cape' ? capeRows(d.hem ?? 0.45, !!d.wide) : flareRows(d.top ?? 0.98, d.hem ?? 0.56, ...(d.flare || [0.175, 0.115, 0.27, 0.19]));\r\n      const arc = d.kind === 'cape' ? [Math.PI / 2 + 1.25, Math.PI / 2 - 1.25 + Math.PI * 2] : d.arc;\r\n      loftPiece(out, rows.map((r) => ({ ...r, p: 1 })), { seg: d.seg ?? 16, group: 'body', arc, capTop: false, capBottom: false });\r\n      compress(out);\r\n    } else if (piece.mark) {\r\n      const [x, y, z] = piece.at, at = [x * sx, y * HSCALE, z];\r\n      out.push({ p: [...at, ...at, ...at, ...at], n: piece.n || [0, 0, 1], g: side(piece.group || 'head', at[0]), mark: piece.mark, free: !!piece.free, slack: piece.slack, r: GREY });\r\n    } else if (piece.dfo) {\r\n      if (sx < 0) continue; // DFO builders are already symmetric\r\n      const [kind, arg] = piece.dfo.split(':'), o = piece.opts || {};\r\n      if (kind === 'helm') out.push(...buildHelm(GREY));\r\n      else if (kind === 'pauldrons') out.push(...buildPauldrons(GREY));\r\n      else if (kind === 'tail') out.push(...buildTail(GREY, arg || 'argonian'));\r\n      else if (kind === 'wings') out.push(...buildWings(GREY, o));\r\n      else if (kind === 'tusks') out.push(...buildTusks(GREY, { jaw: ctx.build?.jaw ?? 1, ...o }));\r\n      else if (kind === 'brow') out.push(...buildBrow(GREY, { skull: ctx.build?.skull ?? 1, ...o }));\r\n      else if (kind === 'hair') out.push(...buildHair(GREY, 'Human', null, arg || 'short'));\r\n      else if (kind === 'race') { const fs = buildHair(GREY, arg, GREY, 'bald'); out.push(...fs); }\r\n      else {\r\n        const made = kind === 'beast' ? buildBeastBody(GREY, o) : kind === 'beastTail' ? buildBeastTail(GREY, o) : kind === 'beastHead' ? buildBeastHead(GREY, o)\r\n          : kind === 'horns' ? buildHorns(GREY, o) : kind === 'arachnid' ? buildArachnid(GREY, o) : kind === 'sting' ? buildSting(GREY, o)\r\n          : kind === 'fish' ? buildFishBody(GREY, o) : kind === 'horse' ? buildHorseBody(GREY, o)\r\n          : kind === 'blade' ? weaponFrame(buildBladeWeapon(GREY, arg)) : kind === 'hafted' ? weaponFrame(buildHaftedWeapon(GREY, arg))\r\n          : kind === 'bow' ? weaponFrame(arg === 'short' ? buildShortBow(GREY) : buildLongBow(GREY), false)\r\n          : kind === 'claymore' ? weaponFrame(buildClaymore(GREY)) : kind === 'sword' ? weaponFrame(buildSword(GREY)) : [];\r\n        for (const f of made) { f.g = piece.group || f.g; if (!f.r) shadePiece([f], GREY); }\r\n        out.push(...place(made, piece.scale ?? 1, piece.at, piece.rot));\r\n      }\r\n    }\r\n    if (!piece.mark && !piece.dfo) shadePiece(out, GREY, piece.floor ?? 0.08, piece.bias ?? 0.16);\r\n    for (const f of out) if (f.mark) marks.push(f); else faces.push(f);\r\n  }\r\n  tag(faces, mat); for (const m of marks) m._mat = mat;\r\n  return { faces, marks, zones };\r\n}\r\n","pod/gala-rig/pet.mjs":"// Gala companions as small DFO creatures (beast body/head, wings, arachnid, fish, tails) in the piece\r\n// language, standing beside the character. Same 40 pet ids; collections add a crown, wings or gems.\r\nimport { emit } from './parts.mjs';\r\nimport { ramp } from './palette.mjs';\r\nimport { rasterize } from './raster.mjs';\r\nimport CARDS from './designs/index.mjs';\r\n\r\nconst rampCache = new Map();\r\nconst R = (hex, o) => { const k = hex + JSON.stringify(o || 0); if (!rampCache.has(k)) rampCache.set(k, ramp(hex, o)); return rampCache.get(k); };\r\n// The original sprite's coat per companion (0 = slug: dye highlight, 1 = skin, 2 = gold, ...).\r\nconst COAT = (H, S) => [H, S, '#e6c880', '#90bca2', '#d99bae', '#ad9ac8', '#81a891', '#b1accb', '#9caabb', '#83b587'];\r\n\r\nfunction collection(design, g) {\r\n  // 1 crown on the head (base = head height + ~0.07, sunk a little), 2 little pearl wings, 3 three floating glow gems\r\n  const top = design.top ?? 0.4, hx = design.head?.[0] ?? 0, hy = design.head?.[1] ?? top - 0.07, hz = design.head?.[2] ?? 0;\r\n  if (g === 1) { const b = hy + 0.07; return [{ box: [hx - 0.06, b - 0.01, hz - 0.03, hx + 0.06, b + 0.025, hz + 0.03], mat: 'gold' }, ...[-0.045, 0, 0.045].map((x, i) => ({ box: [hx + x - 0.014, b + 0.02, hz - 0.014, hx + x + 0.014, b + (i === 1 ? 0.085 : 0.065), hz + 0.014], mat: 'gold' })), { mark: 'gem', at: [hx, (b + 0.01) / 0.9, hz + 0.034], mat: 'glow', free: true }]; }\r\n  if (g === 2) return [{ fan: [[0.04, top * 0.7, -0.03], [0.22, top + 0.12, -0.08], [0.28, top * 0.7, -0.1], [0.2, top * 0.4, -0.07]], mirror: true, mat: 'pearl' }];\r\n  if (g === 3) return [-0.14, 0, 0.14].map((x, i) => { const y = top + 0.08 + (i % 2) * 0.06; return { loft: [[y - 0.04, 0.0, 0.0], [y - 0.015, 0.03, 0.03], [y + 0.015, 0.03, 0.03], [y + 0.04, 0.0, 0.0]], cx: hx + x, mat: 'glow', seg: 6 }; });\r\n  return [];\r\n}\r\n\r\nconst cache = new Map();\r\n/** The companion as a 64x96 sprite layer (feet on the plinth line, beside the character). */\r\nexport function petImage(look, { H, S, yaw = 1.0, ox = 11, oy = 84, scale = 36, w = 64, h = 96 } = {}) {\r\n  const value = look.parts.pet || 0, k = value % 10, g = Math.floor(value / 10), design = (CARDS.pets || [])[k];\r\n  if (!value || !design) return null;\r\n  const key = JSON.stringify([value, H, S, yaw, ox, oy, scale, w, h]);\r\n  if (cache.has(key)) return cache.get(key);\r\n  const coat = COAT(H, S)[k], mats = { gold: R('#e6c880'), pearl: R('#f2e6dc', { lift: 0.04 }), glow: R('#88e4d9', { lift: 0.05, spread: 0.8 }), dark: R('#3a2f4c'), white: R('#fbebce', { lift: 0.08, spread: 0.5 }),\r\n    ivory: R('#e4dcc0'), metal: R('#9aa6b8'), glass: R('#9fe8ff', { lift: 0.06 }), membrane: R('#8a6a90'), fur: R(coat), furDark: R(coat, { lift: -0.14 }), belly: R(coat, { lift: 0.12, spread: 0.8 }), skin: R(coat), dye: R(H), trim: R(H, { lift: -0.1 }), accent: R('#e6c880') };\r\n  const faces = [];\r\n  for (const piece of [...design.pieces, ...collection(design, g)]) for (const f of emit({ group: 'body', ...piece }).faces) { f.r = mats[f._mat] || mats.fur; faces.push(f); }\r\n  const img = rasterize(faces, { w, h, scale, ox, oy, yaw });\r\n  cache.set(key, img); if (cache.size > 48) cache.delete(cache.keys().next().value);\r\n  return img;\r\n}\r\n","pod/gala-rig/raster.mjs":"// The Gala pixelize pass. Same law as DFO Online's characterSprite.js + CHAR_FS\r\n// (MIT (c) 2026 Lattymoy, see dfo/LICENSE-DFO.txt): the 3D rig is drawn straight into\r\n// a tiny NEAREST target under an orthographic camera taken from the viewer's azimuth,\r\n// Lambert from one key light SNAPPED to the face's palette ramp (no lerp, no specular),\r\n// times the rig's baked geometric AO; the consumer scales it up NEAREST.\r\n// MYR5: rasterised on the CPU with a z-buffer instead of a WebGL render target, so every\r\n// Gala surface (340 menu previews, rest scene, Fruit Ninja, scoreboard heads) stays\r\n// synchronous and costs no GPU context on the phone. One silhouette outline in the\r\n// War Room ink keeps it reading as our 64-bit sprites.\r\n\r\nconst INK = [33, 23, 46];\r\nconst rampAt = (ramp, t) => ramp[Math.max(0, Math.min(ramp.length - 1, Math.round(t * (ramp.length - 1))))];\r\n\r\n/** Rotate model space into camera space: yaw about Y (0 = facing the viewer), then pitch. */\r\nexport function cameraOf(yaw = 0, pitch = 0.1) {\r\n  const cy = Math.cos(yaw), sy = Math.sin(yaw), cp = Math.cos(pitch), sp = Math.sin(pitch);\r\n  return (x, y, z) => {\r\n    const x1 = cy * x + sy * z, z1 = -sy * x + cy * z;\r\n    return [x1, cp * y - sp * z1, sp * y + cp * z1];\r\n  };\r\n}\r\n\r\n/**\r\n * faces: [{p:[12], r:ramp, ao?, fb?:[floor,bias], fixed?, c?}] positions in rig space (y up, +z = face front).\r\n * pos (optional Float32Array, 12 per face) overrides f.p for animated frames.\r\n * Returns {data:Uint8ClampedArray RGBA, w, h, depth:Float32Array}.\r\n */\r\nexport function rasterize(faces, { w = 64, h = 96, scale = 36, ox = 32, oy = 84, yaw = 0, pitch = 0.1, pos = null, light = [-0.5, 0.55, 0.67], outline = INK, marks = [] } = {}) {\r\n  const data = new Uint8ClampedArray(w * h * 4), depth = new Float32Array(w * h).fill(-Infinity);\r\n  const cam = cameraOf(yaw, pitch), Ln = Math.hypot(...light), L = light.map((v) => v / Ln);\r\n  const V = new Float32Array(12);\r\n  const tri = (a, b, c, col) => {\r\n    const ax = V[a], ay = V[a + 1], az = V[a + 2], bx = V[b], by = V[b + 1], bz = V[b + 2], cx = V[c], cyy = V[c + 1], cz = V[c + 2];\r\n    const area = (bx - ax) * (cyy - ay) - (by - ay) * (cx - ax);\r\n    if (Math.abs(area) < 1e-9) return;\r\n    const x0 = Math.max(0, Math.floor(Math.min(ax, bx, cx))), x1 = Math.min(w - 1, Math.ceil(Math.max(ax, bx, cx)));\r\n    const y0 = Math.max(0, Math.floor(Math.min(ay, by, cyy))), y1 = Math.min(h - 1, Math.ceil(Math.max(ay, by, cyy)));\r\n    for (let py = y0; py <= y1; py++) for (let px = x0; px <= x1; px++) {\r\n      const sx = px + 0.5, sy = py + 0.5;\r\n      let w0 = (bx - sx) * (cyy - sy) - (by - sy) * (cx - sx), w1 = (cx - sx) * (ay - sy) - (cyy - sy) * (ax - sx), w2 = (ax - sx) * (by - sy) - (ay - sy) * (bx - sx);\r\n      if (area < 0) { w0 = -w0; w1 = -w1; w2 = -w2; }\r\n      if (w0 < 0 || w1 < 0 || w2 < 0) continue;\r\n      const A = Math.abs(area), z = (w0 * az + w1 * bz + w2 * cz) / A, i = py * w + px;\r\n      if (z <= depth[i]) continue;\r\n      depth[i] = z; const o = i * 4; data[o] = col[0]; data[o + 1] = col[1]; data[o + 2] = col[2]; data[o + 3] = 255;\r\n    }\r\n  };\r\n  for (let f = 0; f < faces.length; f++) {\r\n    const face = faces[f], P = pos ? pos.subarray(f * 12, f * 12 + 12) : face.p;\r\n    for (let k = 0; k < 4; k++) {\r\n      const [x, y, z] = cam(P[k * 3], P[k * 3 + 1], P[k * 3 + 2]);\r\n      V[k * 3] = ox + x * scale; V[k * 3 + 1] = oy - y * scale; V[k * 3 + 2] = z;\r\n    }\r\n    let col;\r\n    if (face.fixed != null) col = rampAt(face.r, face.fixed);\r\n    else if (face.r) {\r\n      // Normal from the (possibly animated) quad; two-sided: the side facing the camera is lit.\r\n      const ux = P[3] - P[0], uy = P[4] - P[1], uz = P[5] - P[2];\r\n      let vx = P[9] - P[0], vy = P[10] - P[1], vz = P[11] - P[2];\r\n      if (Math.abs(vx) + Math.abs(vy) + Math.abs(vz) < 1e-9) { vx = P[6] - P[0]; vy = P[7] - P[1]; vz = P[8] - P[2]; }\r\n      let n = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];\r\n      const nl = Math.hypot(...n) || 1; n = cam(n[0] / nl, n[1] / nl, n[2] / nl);\r\n      if (n[2] < 0) n = [-n[0], -n[1], -n[2]];\r\n      const [floor, bias] = face.fb || [0.08, 0.15];\r\n      const it = Math.max(floor, (n[0] * L[0] + n[1] * L[1] + n[2] * L[2]) * 0.9 + bias) * (face.ao ?? 1);\r\n      col = rampAt(face.r, Math.min(1, Math.max(0.04, it)));\r\n    } else col = face.c || INK;\r\n    tri(0, 3, 6, col); tri(0, 6, 9, col);\r\n  }\r\n  // Marks: tiny painted details (eyes, gems) stamped where their anchor is visible.\r\n  for (const m of marks) {\r\n    const [x, y, z] = cam(...m.at), n = m.n ? cam(...m.n) : [0, 0, 1];\r\n    if (n[2] < (m.minFacing ?? 0.15)) continue;\r\n    const cx = Math.floor(ox + x * scale), cyy = Math.floor(oy - y * scale), ci = cyy * w + cx;\r\n    if (cx < 0 || cyy < 0 || cx >= w || cyy >= h || depth[ci] > z + (m.slack ?? 0.05)) continue;\r\n    for (const [dx, dy, col] of m.px) {\r\n      const px = cx + dx, py = cyy + dy; if (px < 0 || py < 0 || px >= w || py >= h) continue;\r\n      const i = py * w + px, o = i * 4; if (!data[o + 3] && !m.free) continue;\r\n      data[o] = col[0]; data[o + 1] = col[1]; data[o + 2] = col[2]; data[o + 3] = 255; depth[i] = Math.max(depth[i], z);\r\n    }\r\n  }\r\n  if (outline) {\r\n    const filled = new Uint8Array(w * h); for (let i = 0; i < w * h; i++) filled[i] = data[i * 4 + 3] ? 1 : 0;\r\n    for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {\r\n      const i = y * w + x; if (filled[i]) continue;\r\n      if ((x > 0 && filled[i - 1]) || (x < w - 1 && filled[i + 1]) || (y > 0 && filled[i - w]) || (y < h - 1 && filled[i + w])) {\r\n        const o = i * 4; data[o] = outline[0]; data[o + 1] = outline[1]; data[o + 2] = outline[2]; data[o + 3] = 255;\r\n      }\r\n    }\r\n  }\r\n  return { data, w, h, depth };\r\n}\r\n","pod/gala-rig/rig.mjs":"// Assemble a Gala look into one animated DFO rig: neutral body (build + head shape +\r\n// clothing/armour displace zones) + standoff pieces + painted marks, cached by recipe.\r\nimport { buildNeutralBody, applyBodyZones, ARM_X, WRIST_JUNCTION_Y, NECK_PIVOT_Y } from './dfo/neutralBody.mjs';\r\nimport { HSCALE } from './dfo/pieceLoft.mjs';\r\nimport { createAnimContext, animateTarget, WALK, IDLE } from './dfo/animate.mjs';\r\nimport { emit } from './parts.mjs';\r\n\r\nconst GID = { body: 0, head: 1, armL: 2, armR: 3, legL: 4, legR: 5 };\r\nconst GREY = [[40, 40, 40], [80, 80, 80], [120, 120, 120], [160, 160, 160], [200, 200, 200], [240, 240, 240]];\r\nexport const HEAD_SCALE = 1.6, HEAD_DEPTH = 1.3; // shallower in depth so the profile is not a balloon          // MYR5 style: alien heads read at 64x96\r\nconst CHIN_Y = 1.70 * HSCALE;\r\n\r\nconst cache = new Map(), bodies = new Map();\r\n// The bare neutral body (geometry + baked AO) per build/head; wardrobe zones are applied to a copy.\r\nfunction bareBody(build, head) {\r\n  const key = JSON.stringify([build, head]);\r\n  let body = bodies.get(key);\r\n  if (!body) {\r\n    const ramps = { skin: GREY.slice(), boot: GREY.slice() };\r\n    body = buildNeutralBody(ramps, { build, head });\r\n    for (const f of body) f.foot = f.r === ramps.boot;\r\n    bodies.set(key, body); if (bodies.size > 12) bodies.delete(bodies.keys().next().value);\r\n  }\r\n  return body;\r\n}\r\n/** recipe = {build, head, pieces:[...]} -> rig {faces, base, vgrp, anim} (cached, 24 entries). */\r\nexport function buildRig(recipe) {\r\n  const key = JSON.stringify(recipe);\r\n  let rig = cache.get(key);\r\n  if (rig) { cache.delete(key); cache.set(key, rig); return rig; }\r\n  const cloth = [], armor = [], extra = [], marks = [];\r\n  for (const piece of recipe.pieces) {\r\n    const out = emit(piece, { build: recipe.build });\r\n    for (const z of out.zones) (piece.layer === 'armor' ? armor : cloth).push(z);\r\n    extra.push(...out.faces); marks.push(...out.marks);\r\n  }\r\n  const body = applyBodyZones(bareBody(recipe.build, recipe.head).map((f) => ({ ...f, p: f.p.slice(), n: f.n.slice() })), cloth, armor);\r\n  for (const f of body) {\r\n    if (!f._mat) f._mat = f.foot ? 'feet' : 'skin';\r\n    // Counter-shade: forward-facing torso and inner-limb skin takes the paler belly ramp.\r\n    if (f._mat === 'skin' && f.g === 'body' && f.n[2] > 0.62) f._mat = 'belly';\r\n  }\r\n  const faces = [...body, ...extra, ...marks];\r\n  // Head scale about the chin (head-tagged faces + marks), so faces and eyes read at sprite size.\r\n  for (const f of faces) if (f.g === 'head') for (let i = 0; i < 4; i++) {\r\n    f.p[i * 3] *= HEAD_SCALE; f.p[i * 3 + 1] = CHIN_Y + (f.p[i * 3 + 1] - CHIN_Y) * HEAD_SCALE; f.p[i * 3 + 2] *= HEAD_DEPTH;\r\n  }\r\n  const base = new Float32Array(faces.length * 12), vgrp = new Uint8Array(faces.length * 4);\r\n  faces.forEach((f, i) => { base.set(f.p, i * 12); vgrp.fill(GID[f.g] ?? 0, i * 4, i * 4 + 4); });\r\n  const anim = createAnimContext({ basePos: base, vgrp, armX: ARM_X, wristY: WRIST_JUNCTION_Y * HSCALE, neckY: NECK_PIVOT_Y * HSCALE });\r\n  rig = { faces, base, vgrp, anim };\r\n  cache.set(key, rig); if (cache.size > 24) cache.delete(cache.keys().next().value);\r\n  return rig;\r\n}\r\n\r\n/** Gala pose -> DFO animate.js inputs. Returns animated positions or null (rest pose). */\r\nexport function poseRig(rig, pose = {}, t = 0) {\r\n  const p = {};\r\n  let moving = false, L = IDLE, wt = 0;\r\n  if (pose.walk != null && pose.walk !== 0) { moving = true; L = WALK; wt = Math.asin(Math.max(-1, Math.min(1, pose.walk))); }\r\n  if (pose.gait != null) { moving = true; L = WALK; wt = pose.gait; }\r\n  if (pose.greet != null) p.armR = { sw: -0.2, bd: 0.9, spread: 2.45 + pose.greet * 0.25, roll: 0, handRoll: 0, handPitch: 0, handYaw: 0 };\r\n  if (pose.weapon) p.armR = { sw: -1.15, bd: 0.45, spread: 0.25, roll: 0, handRoll: 0, handPitch: 0, handYaw: 0 };\r\n  if (pose.petting != null) p.armL = { sw: -0.25, bd: 0.25, spread: 0.5 + pose.petting * 0.1, roll: 0, handRoll: 0, handPitch: 0, handYaw: 0 };\r\n  if (pose.meditationStanding) { p.armL = { sw: -0.35, bd: 0.6, spread: 0.35 }; p.armR = { sw: -0.35, bd: 0.6, spread: 0.35 }; }\r\n  if (pose.meditate) { // seated: thighs forward, shins folded under, hands on the knees, whole body dropped\r\n    p.legL = { sw: -1.45, bd: 2.35 }; p.legR = { sw: -1.45, bd: 2.35 }; p.rootY = -0.36;\r\n    p.armL = { sw: -0.75, bd: 0.35, spread: 0.3 }; p.armR = { sw: -0.75, bd: 0.35, spread: 0.3 };\r\n  }\r\n  // MYR5 rest: arms a touch off the torso so the silhouette reads at sprite size.\r\n  for (const k of ['armL', 'armR']) if (!p[k]) p[k] = { sw: 0, bd: 0.08, spread: 0.1 };\r\n  const posed = true;\r\n  const T = { pos: new Float32Array(rig.base.length), base: rig.base, vgrp: rig.vgrp };\r\n  const pz = posed ? { lean: 0, gaitArm: 0.3, gaitElbow: 0.3, ...p } : null;\r\n  animateTarget(rig.anim, T, wt, L, moving ? L.bobAmp * Math.abs(Math.sin(wt)) : 0, pz, moving);\r\n  return T.pos;\r\n}\r\n","pod/gala-rig/weapon.mjs":"// Gala weapons as DFO-style rig pieces: DFO's blade/hafted/bow builders (re-seated into a neutral weapon\r\n// frame) plus sci-fi pieces in the same piece language, held in the rig's right hand. The 20 families and\r\n// 21 tiers (and their locks) stay in pod/gala-weapons.js; tiers here only change how the piece looks.\r\nimport { emit } from './parts.mjs';\r\nimport { HSCALE } from './dfo/pieceLoft.mjs';\r\nimport { WRIST_JUNCTION_Y } from './dfo/neutralBody.mjs';\r\nimport { ramp } from './palette.mjs';\r\nimport { rasterize, cameraOf } from './raster.mjs';\r\nimport CARDS from './designs/index.mjs';\r\n\r\nexport const TYPES = ['rapier', 'greatsword', 'dagger', 'sabre', 'axe', 'hammer', 'mace', 'flail', 'spear', 'trident', 'halberd', 'scythe', 'bow', 'crossbow', 'chakram', 'gauntlets', 'staff', 'wand', 'tome', 'cannon'];\r\nconst SHELL = ['#59677e', '#889ab4', '#c4cae3'], ENERGY = ['#58e8ff', '#76ffc8', '#ba8cff', '#ff879f', '#ffd76c'];\r\nconst rampCache = new Map();\r\nconst R = (hex, o) => { const k = hex + JSON.stringify(o || 0); if (!rampCache.has(k)) rampCache.set(k, ramp(hex, o)); return rampCache.get(k); };\r\n\r\n/** Materials for a tier: shell metal by band (gold at tier 20), energy colour every five tiers. */\r\nexport function weaponMats(tier, palette = null) {\r\n  const shell = palette?.shell || (tier >= 20 ? '#e6c880' : SHELL[tier < 8 ? 0 : tier < 16 ? 1 : 2]), energy = palette?.energy || ENERGY[Math.min(4, Math.floor(tier / 5))];\r\n  return { shell: R(shell), metal: R(shell), energy: R(energy, { lift: 0.08, spread: 0.7 }), glow: R(energy, { lift: 0.08, spread: 0.7 }), dark: R('#283147'), edge: R('#dbe8ff', { lift: 0.06, spread: 0.5 }), white: R('#f3ffff', { spread: 0.4 }), gold: R('#e6c880'), glass: R('#9fe8ff', { lift: 0.06 }), trim: R(energy), accent: R(energy, { lift: 0.08, spread: 0.7 }) };\r\n}\r\n\r\n/** Tier read: collar rings below the grip (tier % 5, standing proud of the body), a pommel orb from tier 5, side fins from tier 10, an emitter orb from tier 15. */\r\nfunction tierPieces(design, tier) {\r\n  const out = [], worn = design.carry === 'worn', r = worn ? 0.2 : (design.ring ?? 0.032) * 1.9 + 0.03, len = design.len ?? 0.6, y0 = worn ? -0.5 : -0.1;\r\n  for (let k = 0; k < tier % 5; k++) { const y = y0 - k * 0.05; out.push({ loft: [[y - 0.03, r, r], [y, r, r]], mat: 'energy', seg: 10, caps: false }); }\r\n  if (tier >= 5 && !worn) out.push({ loft: [[-0.36, 0.02, 0.02], [-0.32, r * 0.9, r * 0.9], [-0.27, r * 0.9, r * 0.9], [-0.23, 0.02, 0.02]], mat: 'energy', seg: 10 });\r\n  if (tier >= 10) out.push({ fan: [[0, y0, 0], [r + 0.1, y0 - 0.2, 0], [r + 0.14, y0 + 0.08, 0], [0, y0 + 0.14, 0]], mirror: true, mat: 'shell' });\r\n  if (tier >= 15) out.push({ loft: [[len, 0, 0], [len + 0.03, 0.045, 0.045], [len + 0.08, 0.045, 0.045], [len + 0.11, 0, 0]], mat: 'energy', seg: 10 });\r\n  return out;\r\n}\r\n\r\nconst cache = new Map();\r\n/** Weapon faces in the weapon frame (grip at origin, +y along the weapon), cached per family+tier. */\r\nexport function weaponFaces(value) {\r\n  const key = value.type + ':' + value.tier;\r\n  if (cache.has(key)) return cache.get(key);\r\n  const design = (CARDS.weapons || [])[TYPES.indexOf(value.type)];\r\n  const faces = [];\r\n  if (design) for (const piece of [...design.pieces, ...tierPieces(design, value.tier)]) {\r\n    const { faces: f } = emit({ group: 'armR', ...piece });\r\n    for (const face of f) { for (let i = 0; i < 4; i++) face.p[i * 3 + 1] /= HSCALE; faces.push(face); }\r\n  }\r\n  const out = { faces, design: design || { carry: 'up' } };\r\n  cache.set(key, out); if (cache.size > 64) cache.delete(cache.keys().next().value);\r\n  return out;\r\n}\r\n\r\nconst norm = (v) => { const l = Math.hypot(...v) || 1; return v.map((x) => x / l); };\r\nconst cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];\r\n/** Seat the weapon in the right fist of an (animated) rig; returns world positions (12 per face). */\r\nexport function seatWeapon(rig, pos, weapon) {\r\n  const P = pos || rig.base, wY = WRIST_JUNCTION_Y * HSCALE, elbow = rig.anim.elbowY;\r\n  const H = [0, 0, 0], F = [0, 0, 0]; let nh = 0, nf = 0;\r\n  rig.faces.forEach((f, i) => {\r\n    if (f.g !== 'armR' || f.mark) return;\r\n    const by = rig.base[i * 12 + 1];\r\n    for (let k = 0; k < 4; k++) { const o = i * 12 + k * 3; if (by < wY) { H[0] += P[o]; H[1] += P[o + 1]; H[2] += P[o + 2]; nh++; } else if (by < elbow) { F[0] += P[o]; F[1] += P[o + 1]; F[2] += P[o + 2]; nf++; } }\r\n  });\r\n  if (!nh || !nf) return null;\r\n  for (let k = 0; k < 3; k++) { H[k] /= nh; F[k] /= nf; }\r\n  const down = norm([H[0] - F[0], H[1] - F[1], H[2] - F[2]]), { faces, design } = weaponFaces(weapon);\r\n  const Y = design.carry === 'worn' ? down : norm([-down[0] + 0.75, -down[1], -down[2] + 0.35]);\r\n  let Z = [0, 0, 1]; const d = Z[0] * Y[0] + Z[1] * Y[1] + Z[2] * Y[2]; Z = norm([Z[0] - d * Y[0], Z[1] - d * Y[1], Z[2] - d * Y[2]]);\r\n  const X = cross(Y, Z), out = new Float32Array(faces.length * 12);\r\n  faces.forEach((f, i) => { for (let k = 0; k < 4; k++) { const x = f.p[k * 3], y = f.p[k * 3 + 1], z = f.p[k * 3 + 2], o = i * 12 + k * 3; for (let a = 0; a < 3; a++) out[o + a] = H[a] + x * X[a] + y * Y[a] + z * Z[a]; } });\r\n  return { faces, pos: out };\r\n}\r\n\r\n/** A standalone weapon sprite (the menu tiles and every GalaWeapons.draw), fitted into w x h. */\r\nexport function weaponImage(value, { w = 40, h = 72, palette = null, yaw = 0.5, tilt = 0 } = {}) {\r\n  const { faces } = weaponFaces(value), mats = weaponMats(value.tier, palette);\r\n  for (const f of faces) f.r = mats[f._mat] || mats.shell;\r\n  if (!faces.length) return { data: new Uint8ClampedArray(w * h * 4), w, h };\r\n  const cam = cameraOf(yaw, 0), c = Math.cos(tilt), s = Math.sin(tilt), pos = new Float32Array(faces.length * 12);\r\n  let x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;\r\n  faces.forEach((f, i) => { for (let k = 0; k < 4; k++) { const x = f.p[k * 3], y = f.p[k * 3 + 1], o = i * 12 + k * 3; pos[o] = c * x - s * y; pos[o + 1] = s * x + c * y; pos[o + 2] = f.p[k * 3 + 2]; const [px, py] = cam(pos[o], pos[o + 1], pos[o + 2]); x0 = Math.min(x0, px); x1 = Math.max(x1, px); y0 = Math.min(y0, py); y1 = Math.max(y1, py); } });\r\n  const scale = Math.min(60, (w - 4) / Math.max(0.05, x1 - x0), (h - 4) / Math.max(0.05, y1 - y0));\r\n  return rasterize(faces, { w, h, scale, ox: w / 2 - (x0 + x1) / 2 * scale, oy: h / 2 + (y0 + y1) / 2 * scale, yaw, pitch: 0, pos });\r\n}\r\n","pod/gala-weapons.js":"/* Original modular sci-fi pixel weapons. Stable family IDs preserve saved loadouts. Tier zero plus twenty upgrades per family. */\r\n(()=>{'use strict';\r\nconst types=[['rapier','Plasma blade'],['greatsword','Ion cleaver'],['dagger','Phase dagger'],['sabre','Arc pistol'],['axe','Pulse rifle'],['hammer','Rail cannon'],['mace','Tesla emitter'],['flail','Tether drone'],['spear','Particle lance'],['trident','Tri-beam fork'],['halberd','Rocket pod'],['scythe','Gravity reaper'],['bow','Photon bow'],['crossbow','Gauss rifle'],['chakram','Orbit disc'],['gauntlets','Power gauntlets'],['staff','Gravity rod'],['wand','Sonic disruptor'],['tome','Nanite hive'],['cannon','Plasma cannon']].map(([id,name])=>({id,name}));\r\nconst tiers=['Field','Charged','Calibrated','Overclocked','Cryo-cooled','Twin-core','Ionized','Supercharged','Plasma-fed','Phase-linked','Quantum','Antimatter','Gravitic','Drone-linked','Neural','Singularity','Orbital','Rift-tech','Dark-matter','Starbreaker','MOM’s Impossible'];\r\nconst strength=[1,2,3,4,5,6,8,10,12,15,18,20,23,27,31,35,40,45,50,60,75];\r\nfunction normalize(value){if(!value||!types.some(t=>t.id===value.type)||!Number.isInteger(value.tier)||value.tier<0||value.tier>20)throw Error('This look has an unknown weapon.');return {type:value.type,tier:value.tier};}\r\n// R21: one global player level (player-level.mjs, bundled as MYR5Training); level N unlocks tier N-1 for every weapon.\r\nfunction level(progress=window.GalaProgress?.read()){return globalThis.MYR5Training?.playerLevel(progress)||{level:1,xp:0,floor:0,next:200,into:0,need:200,max:false};}\r\nfunction requirements(value){const w=normalize(value),block=Math.min(4,Math.ceil(w.tier/5));return {level:block,xp:0,strength:strength[w.tier],sets:0,label:w.tier===0?'Starter weapon':['Shallow','Medium','Hard','Expert'][block-1]+' performance · tier '+w.tier};}\r\nfunction unlocked(value){const w=normalize(value);return w.tier===0||(globalThis.MYR5Training?.weaponTierFor?.(w.type)||0)>=w.tier;}\r\nfunction name(value){const w=normalize(value);return tiers[w.tier]+' '+types.find(t=>t.id===w.type).name;}\r\nconst rigSprites=new Map();\r\n// The DFO-rig weapon (pod/gala-rig/weapon.mjs, via GalaAvatar) replaces the flat sprite wherever a weapon is drawn.\r\nfunction rigSprite(value,palette){const A=window.GalaAvatar;if(!A?.weaponImage||!globalThis.document)return null;const key=value.type+':'+value.tier+':'+(palette?JSON.stringify(palette):'');let c=rigSprites.get(key);if(!c){const img=A.weaponImage(value,{palette});c=document.createElement('canvas');c.width=img.w;c.height=img.h;const x=c.getContext('2d'),d=x.createImageData(img.w,img.h);d.data.set(img.data);x.putImageData(d,0,0);rigSprites.set(key,c);if(rigSprites.size>80)rigSprites.delete(rigSprites.keys().next().value);}return c;}\r\nfunction draw(ctx,value,{x=0,y=0,scale=1,palette=null}={}){\r\n {const sprite=rigSprite(normalize(value),palette);if(sprite){ctx.save();ctx.imageSmoothingEnabled=false;ctx.drawImage(sprite,x,y,40*scale,72*scale);ctx.restore();return;}}\r\n const w=normalize(value),i=types.findIndex(type=>type.id===w.type),t=w.tier;\r\n const ink='#111625',shell=palette?.shell||(t<8?'#59677e':t<16?'#889ab4':'#c4cae3'),dark='#283147',edge='#dbe8ff';\r\n const energy=palette?.energy||['#58e8ff','#76ffc8','#ba8cff','#ff879f','#ffd76c'][Math.min(4,Math.floor(t/5))],white='#f3ffff';\r\n ctx.save();ctx.translate(x,y);ctx.scale(scale,scale);ctx.imageSmoothingEnabled=false;\r\n const r=(x,y,w,h,c)=>{ctx.fillStyle=c;ctx.fillRect(Math.round(x),Math.round(y),Math.round(w),Math.round(h));};\r\n const p=(points,c,line=ink)=>{ctx.beginPath();points.forEach(([x,y],j)=>j?ctx.lineTo(x+.5,y+.5):ctx.moveTo(x+.5,y+.5));ctx.closePath();ctx.fillStyle=c;ctx.fill();if(line){ctx.strokeStyle=line;ctx.lineWidth=1;ctx.stroke();}};\r\n const panel=(x,y,w,h,c=shell)=>{r(x,y,w,h,ink);r(x+1,y+1,w-2,h-2,c);r(x+1,y+1,w-2,1,edge);};\r\n const core=(x,y,size=3)=>{r(x-size-1,y-size-1,size*2+3,size*2+3,ink);r(x-size,y-size,size*2+1,size*2+1,energy);r(x-size+1,y-size+1,Math.max(1,size-1),Math.max(1,size-1),white);};\r\n const vents=(x,y,count=3,vertical=false)=>{for(let k=0;k<count;k++)r(x+(vertical?0:k*3),y+(vertical?k*3:0),vertical?4:1,vertical?1:4,dark);};\r\n const grip=(x=17,y=48,h=14)=>{panel(x,y,7,h,dark);for(let k=3;k<h-2;k+=3)r(x+1,y+k,5,1,shell);r(x+2,y+h-3,3,1,energy);};\r\n const rail=(x=18,y=27,h=38)=>{panel(x,y,5,h,dark);r(x+2,y+2,1,h-5,energy);};\r\n const ring=(cx,cy,rx,ry,col=energy)=>{ctx.beginPath();ctx.ellipse(cx,cy,rx,ry,0,0,Math.PI*2);ctx.strokeStyle=col;ctx.lineWidth=1;ctx.stroke();};\r\n // Detached field stabilizers and an orbital frame arrive at advanced tiers.\r\n if(t>=12){for(const side of [-1,1]){const xx=side<0?2:34;panel(xx,20,4,23,dark);r(xx+1,22,2,6,energy);r(xx+1,34,2,6,energy);}}\r\n if(t>=16){ring(20,33,18,27);r(1,31,3,5,white);r(36,31,3,5,white);}\r\n switch(i){\r\n case 0: // Plasma blade: an emitter and a contained beam, without a metal blade.\r\n  grip();panel(13,42,15,8);core(20,45,2);p([[17,40],[17,14],[20,5],[23,14],[23,40]],energy);r(19,13,2,26,white);panel(15,35,3,7,dark);panel(23,35,3,7,dark);break;\r\n case 1: // Ion cleaver with split rails and a broad energy plane.\r\n  grip(17,53,12);panel(11,46,19,8);panel(9,13,5,35);panel(26,13,5,35);p([[14,44],[14,14],[20,5],[26,14],[26,44]],energy);r(18,14,4,29,white);core(20,49,2);break;\r\n case 2: // A compact phase blade carried in a powered socket.\r\n  grip(16,43,15);panel(12,38,17,7);p([[15,36],[15,27],[20,15],[25,27],[25,36]],energy);r(19,25,2,11,white);panel(12,31,4,8);panel(25,31,4,8);core(20,41,2);break;\r\n case 3: // Arc pistol, visibly horizontal and compact.\r\n  p([[10,33],[22,35],[18,53],[10,53],[13,40]],dark);panel(5,24,29,13);panel(30,22,7,16,dark);r(32,25,3,10,energy);panel(9,20,7,4);core(20,30,3);vents(8,27,3,true);r(15,43,3,6,energy);break;\r\n case 4: // Pulse rifle, vertical storage pose, magazine and forward handguard.\r\n  panel(17,8,6,16,dark);r(18,9,4,3,energy);panel(12,22,15,26);panel(8,27,4,15);vents(14,25,4,true);core(22,34,2);p([[14,47],[24,47],[28,63],[14,65]],shell);panel(24,41,9,13,dark);r(26,43,5,2,energy);break;\r\n case 5: // Rail cannon with open parallel acceleration rails.\r\n  grip(16,52,12);panel(9,35,23,17);panel(7,7,6,34);panel(27,7,6,34);r(13,10,2,29,energy);r(25,10,2,29,energy);r(19,14,2,23,white);for(let yy=15;yy<34;yy+=7){r(13,yy,4,2,energy);r(23,yy,4,2,energy);}core(20,44,4);break;\r\n case 6: // Tesla emitter: coil stack surrounding a live conductor.\r\n  grip(17,49,14);panel(11,37,19,12);rail(18,13,27);for(let yy=15;yy<36;yy+=6){panel(9,yy,23,3);r(10,yy+1,21,1,energy);}core(20,9,3);p([[19,17],[14,24],[20,23],[17,31],[24,22],[20,22]],white,null);break;\r\n case 7: // A tethered attack drone and its remote grip.\r\n  grip(6,47,14);panel(4,40,12,8);core(10,43,2);for(let k=0;k<9;k++)r(11+k,38-k*2,2,2,energy);panel(20,13,13,13);core(26,19,3);panel(14,16,5,8);panel(34,16,5,8);r(21,28,3,4,energy);r(29,28,3,4,energy);break;\r\n case 8: // Particle lance, twin emitter prongs and a narrow beam.\r\n  rail(18,29,38);panel(13,26,15,7);panel(11,16,5,13);panel(25,16,5,13);r(18,6,4,20,energy);r(19,4,2,22,white);core(20,30,2);break;\r\n case 9: // Three separately powered beam emitters.\r\n  rail(18,34,31);panel(8,28,25,8);for(const xx of [8,18,28]){panel(xx,17,5,13);r(xx+1,7-(xx===18?4:0),3,10+(xx===18?4:0),energy);r(xx+2,8,1,16,white);}core(20,32,2);break;\r\n case 10: // Rocket pod: six launch cells with guidance lights.\r\n  panel(7,15,27,31);for(let yy=20;yy<=34;yy+=14)for(let xx=12;xx<=26;xx+=7){panel(xx-2,yy-2,6,10,dark);r(xx,yy,2,4,energy);}panel(13,46,15,7);grip(17,52,11);r(4,23,2,15,energy);break;\r\n case 11: // Gravity reaper bends a field between three suspended pods.\r\n  rail(26,27,37);panel(22,19,13,9);p([[5,12],[18,8],[31,15],[27,19],[17,14],[7,17]],shell);panel(2,20,6,11);r(3,22,4,7,energy);ring(17,27,12,15);core(17,27,4);r(13,26,9,2,white);r(16,23,2,8,white);break;\r\n case 12: // Photon bow: floating emitter limbs and a laser string.\r\n  panel(18,29,8,12);p([[20,27],[25,17],[20,6],[14,7],[20,18],[15,27]],shell);p([[20,42],[25,52],[20,64],[14,63],[20,52],[15,42]],shell);r(13,9,1,53,energy);r(6,34,29,2,white);p([[34,31],[39,35],[34,39]],energy);core(21,34,2);break;\r\n case 13: // Gauss launcher uses sideways capacitors around an acceleration tube.\r\n  grip(17,49,14);panel(15,16,11,33);panel(4,27,10,13);panel(27,27,10,13);r(5,29,8,2,energy);r(28,29,8,2,energy);panel(17,9,7,9,dark);r(19,10,3,6,white);for(let yy=21;yy<43;yy+=6)r(17,yy,7,2,energy);break;\r\n case 14: // Orbit disc, segmented casing with an open energy aperture.\r\n  p([[13,15],[27,15],[35,23],[35,41],[27,49],[13,49],[5,41],[5,23]],shell);p([[15,22],[25,22],[28,27],[28,37],[24,42],[16,42],[12,37],[12,27]],ink);ring(20,32,10,12);for(const [xx,yy] of [[18,15],[31,30],[18,45],[5,30]])r(xx,yy,5,3,energy);core(20,32,2);break;\r\n case 15: // Powered gauntlets with knuckle emitters and battery cuffs.\r\n  for(const xx of [4,23]){panel(xx,27,13,25);for(let k=0;k<3;k++){panel(xx+1+k*4,20,3,10);r(xx+2+k*4,21,1,6,energy);}panel(xx-1,48,15,7,dark);core(xx+6,37,3);r(xx+3,50,7,2,energy);}break;\r\n case 16: // Gravity rod containing a miniature black-hole chamber.\r\n  rail(18,32,34);panel(11,27,19,7);panel(8,12,5,17);panel(28,12,5,17);ring(20,18,8,10);core(20,18,5);r(17,15,7,7,ink);r(18,14,5,1,white);r(14,19,12,1,energy);break;\r\n case 17: // Sonic disruptor with broad dish and expanding pulse rings.\r\n  grip(12,45,16);panel(8,30,17,16);p([[23,32],[30,23],[33,24],[33,45],[30,46],[23,38]],shell);r(30,27,2,15,energy);core(15,36,3);for(let k=0;k<3;k++){r(35+k*2,25+k*3,1,19-k*6,energy);}panel(6,33,3,8,dark);break;\r\n case 18: // Nanite hive replaces the spellbook with a floating drone cluster.\r\n  panel(10,27,21,23);panel(14,22,13,6);core(20,36,5);vents(14,44,4);for(const [xx,yy] of [[7,16],[28,10],[34,24],[5,42],[29,58]]){panel(xx-2,yy-2,5,5);r(xx,yy,1,1,energy);r(xx,yy+4,1,2,energy);}r(17,51,2,6,energy);r(23,51,2,6,energy);break;\r\n case 19: // Plasma cannon with a large chamber and bright muzzle aperture.\r\n  grip(12,43,16);panel(5,23,29,22);panel(29,19,9,29,dark);r(31,22,5,23,energy);r(33,25,2,17,white);core(16,33,6);vents(6,26,4,true);panel(9,17,17,6);r(11,19,13,2,energy);break;\r\n }\r\n // Progressive hardware: charge indicators, cooling fins, extra emitters, then drones.\r\n if(t>0){const rows=1+((t-1)%5);for(let k=0;k<rows;k++){r(15,54-k*3,10,2,ink);r(16,54-k*3,1+Math.min(7,t),1,energy);}}\r\n if(t>=4){panel(7,46,5,10,dark);r(8,48,3,5,energy);r(11,45,5,1,energy);}\r\n if(t>=8){\r\n  const row=types[i].id==='chakram'?19:12;\r\n  p([[8,row],[3,row-7],[3,row+9],[8,row+12]],shell);p([[32,row],[37,row-7],[37,row+9],[32,row+12]],shell);\r\n  r(4,row-3,2,9,energy);r(34,row-3,2,9,energy);\r\n  // Extra family-specific modules amplify the defining shape.\r\n  if([0,1,2,8,9].includes(i)){r(13,11,2,19,energy);r(26,11,2,19,energy);}\r\n  else if([3,4,5,10,13,19].includes(i)){panel(3,51,8,6);panel(29,51,8,6);r(4,53,6,2,energy);r(30,53,6,2,energy);}\r\n  else if([6,11,14,16,17].includes(i)){ring(20,29,14,19,energy);}\r\n  else {for(const xx of [7,32]){core(xx,57,2);r(xx,61,1,4,energy);}}\r\n }\r\n if(t>=13){for(let k=0;k<t-11;k++){const angle=k*Math.PI*2/(t-11);const xx=20+Math.cos(angle)*17,yy=33+Math.sin(angle)*28;r(xx-1,yy-1,3,3,energy);r(xx,yy,1,1,white);}}\r\n if(t===20){\r\n  // The final impossible machine opens a contained portal above the weapon.\r\n  ring(20,7,10,5,white);ring(20,7,7,3,energy);r(17,6,7,2,ink);\r\n  for(const xx of [6,33]){panel(xx-2,61,5,5);r(xx,67,1,3,energy);}r(18,64,5,2,white);r(20,66,1,5,energy);\r\n }\r\n ctx.restore();\r\n}\r\n\r\nwindow.GalaWeapons={types,tiers,normalize,requirements,level,unlocked,name,draw};\r\n})();\r\n","pod/game-entry.css":"/* Title-only display: the universal shell adopts this native modal around its inner face. */\r\n.cockpit-game-entry{--entry-gold:#ffe58f;position:fixed;inset:var(--shell-top,24px) var(--shell-side,20px) var(--shell-bottom,84px);box-sizing:border-box;width:auto;height:auto;max-width:1100px;max-height:none;margin:auto;padding:0;border:2px solid #6e858d;border-radius:14px;background:#07141e;color:#f0eee2;overflow:auto;box-shadow:inset 0 0 0 5px #172d39,0 0 0 2px #050b11;font-family:ui-monospace,'Cascadia Mono',Consolas,monospace}\r\n.cockpit-game-entry::backdrop{background:#02080bcc}.entry-display{position:relative;display:flex;flex-direction:column;justify-content:space-between;min-height:100%;padding:22px 18px;box-sizing:border-box;isolation:isolate;overflow:hidden}\r\n.entry-display:after{content:'';position:absolute;inset:0;pointer-events:none;z-index:2;background:repeating-linear-gradient(0deg,#0002 0 1px,transparent 1px 4px)}\r\n.entry-display header,.entry-teaching,.entry-load,.entry-display footer{position:relative;z-index:3}.entry-subtitle{font-size:11px;letter-spacing:.15em;text-transform:uppercase;text-align:center;color:var(--entry-gold)}\r\n.cockpit-game-entry h1{margin:12px 0 24px;text-align:center;text-transform:uppercase;font-size:clamp(28px,7vw,76px);line-height:1.02;letter-spacing:-.055em;color:var(--entry-gold);text-shadow:1px 1px #392910,2px 2px #392910,3px 3px #392910,4px 4px #392910,5px 5px #392910,6px 6px #392910,8px 10px #02040b;font-weight:1000;transform:perspective(600px) rotateX(8deg)}\r\n.entry-scene{position:absolute;inset:0;z-index:-1;background:#07111d var(--entry-poster) center/cover no-repeat}.entry-scene:after{content:\"\";position:absolute;inset:0;background:linear-gradient(#02081266,transparent 35%,#020812bb)}\r\n.entry-teaching{padding:12px;border:1px solid #9bc5c844;border-radius:6px;background:#04101add;box-shadow:inset 0 2px 6px #0008;max-width:580px;margin:0 auto;width:100%;box-sizing:border-box}.entry-teaching p{font:14px/1.55 system-ui,sans-serif;margin:6px 0;color:#e1ede6}.entry-postures{display:flex;justify-content:center;align-items:center;gap:12px;color:var(--entry-gold)}.entry-postures svg{width:56px;height:56px;filter:drop-shadow(2px 3px #000)}.entry-step-arrow{font-size:24px}\r\n.entry-load{max-width:580px;width:100%;margin:12px auto;font:13px/1.5 system-ui,sans-serif}.entry-load p{margin:6px 0;text-shadow:0 1px 3px #000,0 0 6px #000}.entry-load progress{width:100%;height:10px;accent-color:var(--entry-gold)}.entry-delayed{padding:8px;border-left:3px solid #ffcb79;background:#251c11}\r\n.entry-display footer{display:flex;gap:8px;align-items:center;justify-content:center}.entry-display button{display:flex;gap:10px;align-items:center;justify-content:center;min-width:48px;min-height:48px;padding:10px;border:1px solid #17252e;border-radius:8px;background:linear-gradient(#91a5aa,#4c626c 45%,#2a3d47);box-shadow:inset 0 1px #fff9,0 4px #02080d;color:#fff0b7;font:700 14px/1.2 system-ui,sans-serif;cursor:pointer}.entry-display button svg{width:24px;height:24px;flex:none}.entry-display button:active{translate:0 3px;box-shadow:inset 0 2px 5px #0008,0 1px #02080d}.entry-display button:disabled{opacity:.4;cursor:default}.entry-display button:focus-visible{outline:3px solid #befdec;outline-offset:3px}\r\n.entry-optics{position:absolute;inset:0;display:grid;place-items:center;z-index:5;pointer-events:none;animation:entry-optics .65s both;background:#091723}.entry-optics .cockpit-visor-object{scale:4;animation:none}.cockpit-game-entry:not([data-phase=pickup]) .entry-optics{animation:entry-optics .65s both}@keyframes entry-optics{0%{opacity:1;transform:scale(.7)}65%{opacity:1;transform:scale(2.8)}100%{opacity:0;visibility:hidden;transform:scale(4)}}\r\n/* Transit: tunnel perspective, station strips and deep stacked yellow lettering. */\r\n.cockpit-game-entry[data-game=runner] .entry-display{background:repeating-linear-gradient(90deg,transparent 0 45px,#fea92e11 45px 47px)}.cockpit-game-entry[data-game=runner] h1{font-style:italic;rotate:-3deg}.cockpit-game-entry[data-game=runner]{--entry-poster:url(\"/pod/entry/runner.webp\")}\r\n/* Cover: clay-orange action cabinet, hazard edge and block shadow silhouette. */\r\n.cockpit-game-entry[data-game=cover]{--entry-gold:#ffe1a3;background:#211510}.cockpit-game-entry[data-game=cover] h1{font-family:Impact,'Arial Black',sans-serif;letter-spacing:.015em;transform:rotate(-4deg) skew(-5deg);text-shadow:3px 3px #a54527,6px 6px #542b25,9px 9px #160f16}.cockpit-game-entry[data-game=cover]{--entry-poster:url(\"/pod/entry/cover.webp\")}.cockpit-game-entry[data-game=cover] .entry-teaching{border-left:6px ridge #cb9867;rotate:1deg}\r\n/* Dance: disco tiles and offset cyan/magenta tube lettering, standing only. */\r\n.cockpit-game-entry[data-game=dance]{--entry-gold:#f8c7ff}.cockpit-game-entry[data-game=dance] h1{font-family:'Arial Black',system-ui,sans-serif;font-style:italic;letter-spacing:-.06em;text-shadow:3px 0 #06c4d1,-3px 0 #b93493,0 4px #49235b,0 8px #221a3b,0 0 20px #e565c344}.cockpit-game-entry[data-game=dance]{--entry-poster:url(\"/pod/entry/dance.webp\")}.cockpit-game-entry[data-game=dance] .entry-postures svg:nth-of-type(2){color:#5be6f2;rotate:8deg}\r\n/* Plank: winged brass flight plate and layered horizon rather than transit art. */\r\n.cockpit-game-entry[data-game=plank-flight]{--entry-gold:#e8e6a5}.cockpit-game-entry[data-game=plank-flight] h1{font-family:Georgia,serif;font-weight:900;letter-spacing:.025em;border-block:4px double #a7a875;padding:10px 0;transform:perspective(600px) rotateX(12deg);text-shadow:0 2px #787944,0 4px #515c42,0 6px #24383b,0 9px #07151e}.cockpit-game-entry[data-game=plank-flight]{--entry-poster:url(\"/pod/entry/plank-flight.webp\")}.cockpit-game-entry[data-game=plank-flight] .entry-postures svg{color:#e3ecab}\r\n@media(prefers-reduced-motion:reduce){.entry-optics{display:none!important}.cockpit-game-entry h1,.entry-teaching{transform:none!important;rotate:none!important}.entry-display *{animation:none!important;transition:none!important}}\r\n@media(max-width:360px){.entry-display{padding:14px 10px}.entry-teaching p{font-size:13px}.entry-display h1{font-size:29px}.entry-postures svg{width:44px;height:44px}.entry-postures{gap:6px}}\r\n\r\n.cockpit-game-entry .entry-title-name{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.cockpit-game-entry h1>canvas{display:block;width:100%;max-width:min(100%,520px);height:auto!important;margin:0 auto 4px;filter:drop-shadow(4px 6px #100b17)}\r\n","pod/game-entry.mjs":"import {postureSVG,visorArt,hardwareIcon} from './cockpit-art.mjs';\r\nimport {clayText} from '../arcade/runner/clay-text.mjs';\r\nexport const GAME_ENTRIES=Object.freeze({\r\n runner:{name:'Subway Burpees',subtitle:'An endless transit escape',teach:'Stand upright. Jog, squat, jump and punch when the course teaches each move.',steps:['runner','cover','runner'],module:'../arcade/runner/runner.mjs'},\r\n cover:{name:'Crouching Crisis',subtitle:'Take cover. Make your opening.',teach:'Stand upright. Follow the lunge, crouch and punch teaching before play.',steps:['cover','runner','cover'],module:'../arcade/cover/cover.mjs'},\r\n dance:{name:'Dance, Dance, and Obey',subtitle:'The floor is listening',teach:'Stand upright with space around you. Choose your music and calibrate on the dance floor.',steps:['dance','cover','dance'],module:'../modules/dance/coach-entry.mjs'},\r\n 'plank-flight':{name:'Jet Plank Joyride',subtitle:'A flight from the floor',teach:'Place your camera where it can see you. Use the plank calibration and follow the hold and lean teaching.',steps:['plank-flight','plank-flight','plank-flight'],module:'../arcade/plank-flight/flight.mjs'}\r\n});\r\nconst ALIASES={coolCardio:'runner',jogging:'runner',coverCrisis:'cover',openDance:'dance',plankFlight:'plank-flight'};\r\nlet current=null;\r\nfunction css(){const href=new URL('/pod/game-entry.css',location.origin).href;if(document.querySelector(`link[href=\"${href}\"]`))return;const link=document.createElement('link');link.rel='stylesheet';link.href=href;document.head.append(link);}\r\n/** Truthful framed entry. load resolves imported code only; existing game owns camera/GPU/calibration. */\r\nexport function launchGameEntry({id,trigger,load,onCancel,onRetry,onReady,host=document.body,t=(_key,text)=>text}={}){\r\n id=ALIASES[id]||id;const game=GAME_ENTRIES[id];if(!game)throw new TypeError(`Unknown game entry: ${id}`);\r\n current?.dispose();css();const lifetime=new AbortController();let task=null,disposed=false,ready=false,stalledTimer=0,run=0,lastStage='';\r\n const dialog=document.createElement('dialog');dialog.className='cockpit-game-entry';dialog.dataset.game=id;dialog.dataset.phase='pickup';dialog.setAttribute('aria-label',game.name);\r\n dialog.innerHTML=`<div class=\"entry-optics\" aria-hidden=\"true\">${visorArt(id==='plank-flight'?'plank':'upright')}</div><section class=\"entry-display\"><div class=\"entry-scene\" aria-hidden=\"true\"></div><header><p class=\"entry-subtitle\"></p><h1></h1></header><div class=\"entry-teaching\"><div class=\"entry-postures\" aria-hidden=\"true\">${game.steps.map(step=>postureSVG(step)).join('<span class=\"entry-step-arrow\">›</span>')}</div><p></p></div><div class=\"entry-load\"><p role=\"status\" aria-live=\"polite\"></p><progress aria-label=\"${t('game.loading','Game module loading')}\"></progress><p class=\"entry-delayed\" hidden></p></div><footer><button type=\"button\" data-cancel aria-label=\"${t('game.cancel','Cancel and return')}\">${hardwareIcon('close')}</button><button type=\"button\" data-retry hidden aria-label=\"${t('game.retry','Retry module loading')}\">${hardwareIcon('retry')}</button><button type=\"button\" data-continue disabled>${hardwareIcon('play')}<span></span></button></footer></section>`;\r\n dialog.querySelector('h1').textContent=t(`game.${id}.title`,game.name);dialog.querySelector('.entry-subtitle').textContent=t(`game.${id}.subtitle`,game.subtitle);dialog.querySelector('.entry-teaching p').textContent=t(`game.${id}.teach`,game.teach);dialog.querySelector('[data-continue] span').textContent=t('game.continue','Continue to setup');\r\n {const heading=dialog.querySelector('h1'),name=document.createElement('span'),text=heading.textContent,words=text.split(' ');name.className='entry-title-name';name.textContent=text;\r\n  // R3-09: giant clay title for every game, split into two balanced lines so each fills the card width.\r\n  let cut=0,best=1e9;for(let i=1;i<words.length;i++){const d=Math.abs(words.slice(0,i).join(' ').length-words.slice(i).join(' ').length);if(d<best){best=d;cut=i;}}\r\n  const lines=cut?[words.slice(0,cut).join(' '),words.slice(cut).join(' ')]:[text],arts=lines.map(line=>{const art=clayText(line,110);art.setAttribute('aria-hidden','true');return art;});heading.replaceChildren(name,...arts);}\r\n const status=dialog.querySelector('[role=status]'),meter=dialog.querySelector('progress'),delayed=dialog.querySelector('.entry-delayed'),retry=dialog.querySelector('[data-retry]'),proceed=dialog.querySelector('[data-continue]');\r\n const remove=({focus=true}={})=>{if(disposed)return;disposed=true;run++;clearTimeout(stalledTimer);task?.abort();lifetime.abort();dialog.close();dialog.remove();if(current===api)current=null;if(focus&&trigger?.isConnected)trigger.focus({preventScroll:true});};\r\n const stalled=()=>{clearTimeout(stalledTimer);stalledTimer=setTimeout(()=>{if(disposed||ready)return;delayed.hidden=false;delayed.textContent=t('game.delayed','Module loading has not advanced for 8 seconds. You can cancel or retry.');retry.hidden=false;},8000);};\r\n function stage(text,{loaded,total}={}){if(disposed)return;lastStage=String(text);status.textContent=lastStage;if(Number.isFinite(loaded)&&Number.isFinite(total)&&total>0&&loaded>=0&&loaded<=total){meter.max=total;meter.value=loaded;}else meter.removeAttribute('value');stalled();}\r\n function markReady(){if(disposed)return;ready=true;clearTimeout(stalledTimer);dialog.dataset.phase='ready';status.textContent=t('game.moduleReady','Game module ready. Camera, permissions and calibration follow in setup.');meter.hidden=true;retry.hidden=true;delayed.hidden=true;proceed.disabled=false;}\r\n function fail(error){if(disposed)return;ready=false;clearTimeout(stalledTimer);dialog.dataset.phase='error';status.textContent=t('game.failed','The game module could not load. Check your connection or downloaded materials and retry.');if(error?.message){delayed.textContent=String(error.message);delayed.hidden=false;}retry.hidden=false;proceed.disabled=true;meter.hidden=true;}\r\n async function start(){task?.abort();task=new AbortController();const generation=++run;ready=false;proceed.disabled=true;meter.hidden=false;retry.hidden=true;delayed.hidden=true;dialog.dataset.phase='loading';stage(t('game.importing','Loading game code…'));if(!load)return;try{await load({signal:task.signal,stage:(...args)=>{if(generation===run&&!task.signal.aborted)stage(...args);}});if(!disposed&&generation===run&&!task.signal.aborted)markReady();}catch(error){if(!disposed&&generation===run&&!task.signal.aborted)fail(error);}}\r\n const api={id,dialog,signal:lifetime.signal,stage,ready:markReady,fail,dispose:()=>remove(),get lastStage(){return lastStage;}};current=api;\r\n dialog.querySelector('[data-cancel]').addEventListener('click',()=>{remove();onCancel?.();},{signal:lifetime.signal});dialog.addEventListener('cancel',e=>{e.preventDefault();remove();onCancel?.();},{signal:lifetime.signal});retry.addEventListener('click',()=>{onRetry?.();void start();},{signal:lifetime.signal});proceed.addEventListener('click',()=>{if(!ready||disposed)return;remove({focus:false});onReady?.();},{signal:lifetime.signal});\r\n // Account transitions invalidate late imports and never start another owner's session.\r\n for(const event of ['myr5:auth-transition','pagehide'])globalThis.addEventListener(event,()=>remove(),{signal:lifetime.signal});\r\n host.append(dialog);dialog.showModal();dialog.querySelector('[data-cancel]').focus({preventScroll:true});void start();return api;\r\n}\r\nexport async function preloadGameEntry(id,{signal}={}){const game=GAME_ENTRIES[ALIASES[id]||id];if(!game)throw new TypeError('Unknown game');signal?.throwIfAborted();await import(game.module);signal?.throwIfAborted();}\r\n","pod/hardware.css":"/* Powder-coated metal, exposed fasteners, and controls with visible travel. */\r\nbody{background:repeating-linear-gradient(90deg,#0000 0 2px,#b89bb403 2px 3px),radial-gradient(ellipse at 50% 15%,#3d2a43,#19141e 64%);max-width:680px}.ship-header{height:62px;border-bottom:3px solid #110d15;background:linear-gradient(#645163,#352b3e 15%,#2a2031 85%,#786476);box-shadow:inset 0 1px #b49eae,0 5px 10px #0005}.ship-location{font-size:8px}.ship-header .icon-button{border:2px solid #857381;background:linear-gradient(130deg,#716073,#2b2331);box-shadow:inset 1px 1px #c5afbd55,3px 4px #130f18;color:#f1d3a2}\r\n.pod-page{padding:0 14px 16px}.deck-strip{height:31px;font-size:9px}.containment-bay{position:relative;padding:15px 18px 8px}.bay-label{position:absolute;top:0;left:38px;font:8px var(--pixel-font);letter-spacing:1px;color:#a994a9}.bay-wires{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;filter:drop-shadow(2px 5px 2px #0009)}\r\n.containment-bay .containment-pod{height:clamp(240px,37dvh,360px);min-height:0;border:13px solid #544352;border-top-width:24px;border-bottom-width:25px;border-radius:25px 25px 18px 18px;background:radial-gradient(ellipse at 50% 76%,#b997b729,transparent 53%),repeating-linear-gradient(90deg,#0000 0 3px,#d7bde203 3px 4px),linear-gradient(130deg,#241d2b,#0e0d14 55%,#302234);box-shadow:inset 0 0 0 3px #141018,inset 2px 3px 6px #000,0 0 0 2px #9b8495,0 0 0 5px #241c2a,3px 8px 0 4px #0e0b12,5px 13px 18px #0009;overflow:visible;isolation:isolate}.containment-bay .containment-pod:before{inset:-11px;border:2px solid #aa92a477;border-radius:18px;box-shadow:inset 0 3px #b29aa233;z-index:5;background:linear-gradient(112deg,transparent 16%,#fff4df0b 17%,transparent 28%);pointer-events:none}.containment-bay .coach-mount{inset:-9px -5px -9px;overflow:hidden;border-radius:10px}.containment-bay .pod-rim{width:9px;top:22px;bottom:22px;background:linear-gradient(90deg,#100e14,#917783,#302738);border-color:#a28a9a77;z-index:6}.containment-bay .rim-left{left:-6px}.containment-bay .rim-right{right:-6px}.containment-bay .pod-rim:before{inset:10px 2px;background:repeating-linear-gradient(0deg,#e5c791 0 6px,#302333 6px 24px);filter:none;border-radius:0}.containment-bay .pod-inscription{top:-20px;font-size:8px;color:#d5c2cd;letter-spacing:3px}.containment-bay .pod-nameplate{bottom:-23px;left:25px;right:25px;height:23px;border:0;background:#211824;box-shadow:inset 0 1px 4px #000;color:#ceb992}.containment-bay .pod-nameplate a{font:9px var(--pixel-font);color:#eccb8f}.containment-bay .pod-floor{display:none}.frame-bolt{position:absolute;width:11px;height:11px;border-radius:50%;background:linear-gradient(130deg,#b6a696,#534644);border:1px solid #302727;box-shadow:1px 2px 2px #000b;z-index:8}.frame-bolt:after{content:'';position:absolute;left:2px;right:2px;top:4px;height:1px;transform:rotate(-35deg);background:#241c21}.frame-bolt.tl{left:-9px;top:-18px}.frame-bolt.tr{right:-9px;top:-18px}.frame-bolt.bl{left:-9px;bottom:-18px}.frame-bolt.br{right:-9px;bottom:-18px}.pod-top-vent{position:absolute;top:-22px;left:32%;right:32%;height:4px;background:repeating-linear-gradient(90deg,#110d17 0 3px,#a08b9633 3px 7px);z-index:7}.camera-preview{width:64px;height:84px;bottom:23px;right:12px}\r\n.coach-line{margin:10px 6px 7px;min-height:45px;padding:8px 13px;background:#15111b;border:1px solid #76607066;box-shadow:inset 2px 3px 6px #0007}.coach-line #coachCaption{font:11px/1.55 var(--pixel-font);color:#dfc89e}.voice-led{flex-shrink:0;background:#e0bb77;border-radius:0;box-shadow:0 0 8px #e8be7644}\r\n.workout-console{position:relative;padding:17px 15px 13px;border:2px solid #796773;border-radius:10px!important;background:repeating-linear-gradient(0deg,#c5abc604 0 1px,#0000 1px 3px),linear-gradient(125deg,#5d4a5b,#382e40 30%,#443545 70%,#645161);box-shadow:inset 0 2px #b09aa3aa,inset 1px 0 #bfaaac44,0 5px #18131e,0 9px 13px #0005}.workout-console:before,.workout-console:after{content:'×';position:absolute;top:8px;color:#282029;font:13px monospace;text-shadow:1px 1px #ab959f}.workout-console:before{left:8px}.workout-console:after{right:8px}\r\n.goal-control label{font:9px var(--pixel-font);letter-spacing:1px;color:#d4bec9}\r\n.goal-control{padding:11px 0 14px;border-top:1px solid #af929744}.goal-control>div:first-child{display:flex;justify-content:space-between;align-items:center;gap:8px}.goal-control output{font:700 14px var(--pixel-font);color:#efcd96}.goal-control input{display:block;appearance:none;width:100%;height:32px;background:transparent;margin:9px 0 1px;padding:0;cursor:pointer;touch-action:pan-y}.goal-control input::-webkit-slider-runnable-track{height:10px;border:2px solid #18121d;border-radius:3px;background:linear-gradient(90deg,#bc9158 0 var(--fill,31%),#1c1723 var(--fill,31%) 100%);box-shadow:0 1px #a28490,inset 0 2px 3px #0007}.goal-control input::-webkit-slider-thumb{appearance:none;width:26px;height:30px;margin-top:-12px;border:2px solid #b49aa3;border-radius:4px;background:repeating-linear-gradient(90deg,#6a5569 0 4px,#392c3e 4px 5px);box-shadow:inset 0 2px #d4bac2,2px 4px 4px #0007}.goal-control input::-moz-range-track{height:8px;background:#1c1723;border:2px solid #18121d}.goal-control input::-moz-range-thumb{width:24px;height:28px;border:2px solid #b49aa3;border-radius:3px;background:#6a5569}.slider-scale{display:flex;justify-content:space-between;font:8px var(--pixel-font);color:#b59aaa}.slider-scale span:nth-child(2){font-size:7px;letter-spacing:.7px}\r\n.workout-console .set-actions{display:grid;grid-template-columns:1fr 59px;gap:11px;min-width:0;align-items:stretch}.workout-console .set-actions .primary-action{display:flex;flex-direction:column;gap:5px;min-height:90px;padding:11px 8px;border:2px solid #dfac70;border-radius:7px;background:linear-gradient(#d9a66e,#b77749 50%,#945232);box-shadow:inset 0 2px #ffe1a9,inset -2px -3px #70422f,0 5px #462b27,0 7px 0 1px #1c1420;color:#221621;text-shadow:0 1px #ffca8677;white-space:normal}.set-actions .primary-action small{font:9px var(--pixel-font);letter-spacing:2px}.set-actions .primary-action strong{font:900 23px var(--pixel-font);letter-spacing:1px}.set-actions .primary-action span{font:7px var(--pixel-font);letter-spacing:.3px}.workout-console .set-actions .primary-action:active:not(:disabled){transform:translateY(4px);box-shadow:inset 1px 3px 6px #66321e88,0 1px #462b27}.workout-console #stop{padding:5px;font:700 10px var(--pixel-font);border:2px solid #8b6773;border-radius:6px;background:linear-gradient(#805768,#432f41);box-shadow:inset 0 1px #d0a0ab77,0 5px #201723;min-width:0}.workout-console #stop:disabled{opacity:.38}.set-track{margin-top:15px;height:5px;border-radius:0;background:#1b1422}.workout-console #status{font:10px/1.5 var(--pixel-font);margin:9px 0 0;min-height:0;color:#d5bece}\r\n.training-paths{gap:10px}.training-paths button{background:linear-gradient(140deg,#534253,#312636);border:2px solid #7d6576;border-radius:5px;box-shadow:inset 0 1px #b798a433,0 4px #1a1321;font-size:12px}.training-paths span{font-size:8px}.crew-footer{margin-top:5px}.settings-note{font:12px/1.5 var(--pixel-font);color:#c8b1c3}.pod-dialog button,.pod-dialog select{box-shadow:inset 0 1px #b99cb23b,2px 3px #140f19;border-width:2px;border-radius:4px}.pod-dialog{background:repeating-linear-gradient(0deg,#c5abc603 0 1px,#0000 1px 3px),linear-gradient(125deg,#403046,#241a31);border:2px solid #9a8095;box-shadow:inset 0 2px #c1a3b14f,8px 12px 40px #000a}\r\n@media(min-width:600px){.pod-page{padding:0 28px 20px}.containment-bay{padding-left:32px;padding-right:32px}.containment-bay .containment-pod{height:350px}.workout-console{padding:20px 28px}.goal-control{padding-top:15px}}\r\n@media(max-width:370px){.pod-page{padding-left:10px;padding-right:10px}.workout-console{padding:15px 10px}}\r\n","pod/hardware.mjs":"import {FOCUS_GROUPS,GROUP_EXERCISES,EXERCISES,exerciseAt,focusFor} from '../exercise-library.mjs';\r\nimport {workoutLevel} from '../workout-levels.mjs';\r\nconst $=id=>document.getElementById(id),clamp=(x,min,max)=>Math.max(min,Math.min(max,x));\r\n// #106 control board: a focus dial, a level knob, an easier/harder lever and a sound switch, all\r\n// driving the same #movement/#goal state the pod already uses. Rotaries sweep 270° (−135° … +135°).\r\nconst SWEEP=270,tick=(kind='dial')=>{try{navigator.vibrate?.(8);}catch{}window.dispatchEvent(new CustomEvent('myr5:hardware-detent',{detail:{kind}}));};\r\nexport const detentAngle=(i,n)=>n>1?-SWEEP/2+SWEEP*i/(n-1):0;\r\n// Relative turn: the detent reached by rotating detent `start` of `n` by `turn` degrees.\r\nexport const detentFor=(start,turn,n)=>clamp(Math.round(start+turn*(n-1)/SWEEP),0,Math.max(0,n-1));\r\n\r\n// Drag to turn (snaps and ticks at each detent), tap the left/right side to step, arrows/Home/End.\r\nexport function rotary(el,{count,value,preview,commit,locked}){\r\n const ticks=el.querySelector('.cb-ticks');let drag=null;\r\n function paint(index,angle=detentAngle(index,count())){\r\n  const n=count();\r\n  if(ticks.childElementCount!==n)ticks.replaceChildren(...Array.from({length:n},(_,i)=>{const b=document.createElement('b');b.style.setProperty('--a',`${detentAngle(i,n)}deg`);return b;}));\r\n  [...ticks.children].forEach((b,i)=>b.toggleAttribute('data-on',i===index));el.style.setProperty('--angle',`${angle}deg`);\r\n }\r\n function set(index){index=clamp(index,0,count()-1);if(locked())return;if(index!==value()){tick();commit(index);}else paint(index);}\r\n const angleOf=e=>{const r=el.getBoundingClientRect();return Math.atan2(e.clientX-r.left-r.width/2,-(e.clientY-r.top-r.height/2))*180/Math.PI;};\r\n el.addEventListener('pointerdown',e=>{if(locked())return;e.preventDefault();el.focus({preventScroll:true,focusVisible:false});el.setPointerCapture(e.pointerId);drag={id:e.pointerId,x:e.clientX,y:e.clientY,last:angleOf(e),turn:0,start:value(),at:value(),moved:false};el.classList.add('is-turning');});\r\n el.addEventListener('pointermove',e=>{\r\n  if(drag?.id!==e.pointerId)return;const a=angleOf(e);let d=a-drag.last;d-=360*Math.round(d/360);drag.last=a;drag.turn+=d;\r\n  if(!drag.moved&&Math.hypot(e.clientX-drag.x,e.clientY-drag.y)<6)return;drag.moved=true;\r\n  const n=count(),at=detentFor(drag.start,drag.turn,n);if(at!==drag.at){drag.at=at;tick();preview(at);}\r\n  paint(at,clamp(detentAngle(drag.start,n)+drag.turn,-SWEEP/2,SWEEP/2));\r\n });\r\n function end(e,cancelled){\r\n  if(drag?.id!==e.pointerId)return;const {moved,at,x}=drag;drag=null;el.classList.remove('is-turning');\r\n  if(cancelled){paint(value());return preview(value());}\r\n  if(moved)return set(at);const r=el.getBoundingClientRect();set(value()+(x<r.left+r.width/2?-1:1));\r\n }\r\n el.addEventListener('pointerup',e=>end(e,false));el.addEventListener('pointercancel',e=>end(e,true));\r\n el.addEventListener('keydown',e=>{\r\n  const step={ArrowLeft:-1,ArrowDown:-1,PageDown:-1,ArrowRight:1,ArrowUp:1,PageUp:1}[e.key];\r\n  if(step){e.preventDefault();set(value()+step);}else if(e.key==='Home'||e.key==='End'){e.preventDefault();set(e.key==='Home'?0:count()-1);}\r\n });\r\n return paint;\r\n}\r\n\r\nexport function initHardware({canUse=()=>true}={}){\r\n const select=$('movement'),goal=$('goal'),dial=$('exerciseDial'),knob=$('difficultySlider'),lever=document.querySelector('.cb-lever');\r\n let group=focusFor(select.value);const remembered=new Map();\r\n const groupIndex=()=>Math.max(0,FOCUS_GROUPS.findIndex(g=>g.id===group));\r\n const levelIndex=()=>Math.max(0,GROUP_EXERCISES[group].findIndex(m=>m.id===select.value));\r\n const levelText=(i,n)=>`${workoutLevel(GROUP_EXERCISES[group][i].id).label} ${i+1}/${n}`;\r\n function selectExercise(id){if(goal.disabled||!canUse(id))return;window.dispatchEvent(new Event('myr5:exercise-selected'));if(id!==select.value){select.value=id;select.dispatchEvent(new Event('change',{bubbles:true}));}sync();}\r\n const locked=()=>goal.disabled;\r\n const paintDial=rotary(dial,{count:()=>FOCUS_GROUPS.length,value:groupIndex,locked,preview:i=>{const next=FOCUS_GROUPS[i];$('exerciseName').textContent=next.name+(GROUP_EXERCISES[next.id].some(m=>canUse(m.id))?'':' · locked');},\r\n  commit:i=>{const next=FOCUS_GROUPS[i].id,preferred=exerciseAt(next,remembered.get(next)??0),exercise=canUse(preferred.id)?preferred:GROUP_EXERCISES[next].find(m=>canUse(m.id));if(exercise)selectExercise(exercise.id);else sync();}});\r\n const paintKnob=rotary(knob,{count:()=>GROUP_EXERCISES[group].length,value:levelIndex,locked:()=>locked()||!canUse(select.value),preview:i=>{$('difficultySetting').textContent=levelText(i,GROUP_EXERCISES[group].length);},\r\n  commit:i=>selectExercise(exerciseAt(group,i).id)});\r\n function sync(){\r\n  group=focusFor(select.value);const choices=GROUP_EXERCISES[group],index=levelIndex(),n=choices.length,focus=FOCUS_GROUPS[groupIndex()];\r\n  remembered.set(group,index);const isLocked=locked(),pathLocked=!canUse(select.value),m=EXERCISES[select.value]??choices[index];\r\n  dial.setAttribute('aria-valuenow',String(groupIndex()));dial.setAttribute('aria-valuemax',String(FOCUS_GROUPS.length-1));dial.setAttribute('aria-valuetext',focus.name);$('exerciseName').textContent=focus.name;\r\n  knob.setAttribute('aria-valuenow',String(index+1));knob.setAttribute('aria-valuemax',String(n));knob.setAttribute('aria-valuetext',`${workoutLevel(m.id).label}, ${m.name}, variation ${index+1} of ${n}`);$('difficultyMax').textContent=n;\r\n  dial.setAttribute('aria-disabled',String(isLocked));knob.setAttribute('aria-disabled',String(isLocked||pathLocked));\r\n  for(const id of ['harder','easier','goalSlider'])$(id).disabled=isLocked||pathLocked;\r\n  paintDial(groupIndex());paintKnob(index);\r\n  $('variationName').textContent=m.name;$('difficultySetting').textContent=levelText(index,n);\r\n  $('variationHint').textContent=pathLocked?'This workout path is locked. Choose it as one of your two paths or earn its coach.':m.hint+' '+workoutLevel(m.id).unlock;$('trackingScope').textContent='Camera estimates; not a form or safety check. '+m.measurement+' · '+m.limits;\r\n  const target=$('goalSlider');target.max=Math.max(0,goal.options.length-1);target.value=goal.selectedIndex;\r\n  const chosen=goal.selectedOptions[0];if(chosen){target.setAttribute('aria-valuetext',chosen.textContent);$('goalSetting').textContent=chosen.textContent;$('goalMin').textContent=goal.options[0].value;$('goalMax').textContent=goal.options[goal.options.length-1].value;target.style.setProperty('--fill',`${100*goal.selectedIndex/Math.max(1,goal.options.length-1)}%`);}\r\n }\r\n // Spring-loaded lever: tap HARDER/EASIER, or throw the handle up/down and let go. Arrow keys work on either end.\r\n const handle=lever.querySelector('i');let pull=null,thrown=-Infinity;\r\n function nudge(step,animate=true){\r\n  if(locked()||!canUse(select.value))return;const n=GROUP_EXERCISES[group].length,next=clamp(levelIndex()+step,0,n-1);\r\n  if(animate&&!matchMedia('(prefers-reduced-motion: reduce)').matches)handle.animate([{transform:'translateY(0)'},{transform:`translateY(${-step*24}px)`},{transform:'translateY(0)'}],{duration:260,easing:'ease-out'});\r\n  if(next!==levelIndex()){tick('lever');selectExercise(exerciseAt(group,next).id);}\r\n }\r\n $('harder').onclick=()=>{if(performance.now()-thrown>400)nudge(1);};$('easier').onclick=()=>{if(performance.now()-thrown>400)nudge(-1);};\r\n lever.addEventListener('pointerdown',e=>{if(!locked())pull={id:e.pointerId,y:e.clientY,held:false};});\r\n lever.addEventListener('pointermove',e=>{\r\n  if(pull?.id!==e.pointerId)return;const dy=clamp(e.clientY-pull.y,-26,26);\r\n  if(!pull.held&&Math.abs(dy)>6){pull.held=true;lever.setPointerCapture(e.pointerId);lever.classList.add('is-pulling');}\r\n  if(pull.held)lever.style.setProperty('--pull',`${dy}px`);\r\n });\r\n function release(e,cancelled){\r\n  if(pull?.id!==e.pointerId)return;const dy=clamp(e.clientY-pull.y,-26,26),held=pull.held;pull=null;\r\n  lever.classList.remove('is-pulling');lever.style.removeProperty('--pull');\r\n  if(held){thrown=performance.now();if(!cancelled&&Math.abs(dy)>=14)nudge(dy<0?1:-1,false);}\r\n }\r\n lever.addEventListener('pointerup',e=>release(e,false));lever.addEventListener('pointercancel',e=>release(e,true));\r\n lever.addEventListener('keydown',e=>{const step={ArrowUp:1,ArrowRight:1,ArrowDown:-1,ArrowLeft:-1}[e.key];if(step){e.preventDefault();nudge(step);}});\r\n // Toggle switch: app.mjs owns the click (voice on/off); the board adds the tick and arrow keys.\r\n const sound=$('toggleVoice');sound.addEventListener('click',()=>tick('switch'));\r\n sound.addEventListener('keydown',e=>{const on={ArrowUp:true,ArrowRight:true,ArrowDown:false,ArrowLeft:false}[e.key];if(on===undefined)return;e.preventDefault();if(!sound.disabled&&(sound.dataset.on==='true')!==on)sound.click();});\r\n $('goalSlider').addEventListener('input',()=>{if(goal.disabled)return;goal.selectedIndex=Number($('goalSlider').value);goal.dispatchEvent(new Event('change',{bubbles:true}));sync();});\r\n select.addEventListener('change',sync);goal.addEventListener('change',sync);window.addEventListener('myr5:movement-configured',sync);window.addEventListener('myr5:performance-progress',sync);window.addEventListener('myr5:account-ready',sync);\r\n const observer=new MutationObserver(sync);observer.observe(goal,{attributes:true,attributeFilter:['disabled'],childList:true});window.addEventListener('pagehide',()=>observer.disconnect());sync();\r\n}\r\n","pod/holo.css":"/* R3-04 hologram tier: glyph + short word glowing in the strip colour. No box, no metal, 44px hit area. Optional leader line via [data-lead] (--lead-angle, --lead-len, --lead-gap). */\r\n.holo{display:inline-flex;align-items:center;justify-content:center;gap:6px;position:relative;min-width:44px;min-height:44px;padding:0 10px;box-sizing:border-box;cursor:pointer;font:700 16px/1 'VT323',ui-monospace,monospace;letter-spacing:1px;text-transform:uppercase;color:#fff;-webkit-tap-highlight-color:transparent;background:none!important;background-color:transparent!important;background-image:none!important;border:0!important;border-style:none!important;box-shadow:none!important;border-radius:0!important;text-shadow:0 0 2px #12061f,0 0 5px #12061f,0 0 10px var(--portal-strip,#b026ff),0 0 18px var(--portal-strip,#b026ff)}\r\n.holo svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:2;filter:drop-shadow(0 0 3px var(--portal-strip,#b026ff)) drop-shadow(0 0 7px var(--portal-strip,#b026ff));flex:none}\r\n@keyframes holo-flicker{0%,100%{opacity:1}50%{opacity:.86}}\r\n.holo{animation:holo-flicker 3s infinite}\r\n@media(prefers-reduced-motion:reduce){.holo{animation:none}}\r\n.holo:active{transform:translateY(1px)}\r\n.holo:focus-visible{outline:2px solid var(--strip-dim,#e3b3ff);outline-offset:2px}\r\n.holo:disabled,.holo[aria-disabled=true]{opacity:.45}\r\n.holo[data-lead]::after{content:'';position:absolute;left:50%;top:50%;width:var(--lead-len,40px);height:1px;background:linear-gradient(90deg,var(--portal-strip,#b026ff),transparent);box-shadow:0 0 4px var(--portal-strip,#b026ff);transform-origin:0 50%;transform:rotate(var(--lead-angle,90deg)) translateX(calc(var(--lead-gap,22px)));pointer-events:none}\r\n\r\n#pyramidScanner[data-holo] .pyramid-tag{background:none!important;border:0!important;box-shadow:none!important;color:#fff;text-shadow:0 0 2px #12061f,0 0 5px #12061f,0 0 10px var(--portal-strip,#b026ff),0 0 18px var(--portal-strip,#b026ff);min-height:44px}\r\n","pod/home-character.css":"/* W2-2M #117: your Gala character stands in the pod's viewing port (#view), over the pixel world. Tap it for the weapon attack. */\r\n.home-character{position:absolute;inset:0;z-index:3;min-width:0;overflow:visible;pointer-events:none}\r\n.home-character[hidden],#hud[hidden]{display:none!important}\r\n.home-character button{position:absolute;inset:0;display:block;width:100%;height:100%;min-height:0;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;color:#f1e8ff;cursor:pointer;pointer-events:auto;overflow:visible}\r\n.home-character button:focus-visible{outline:3px solid #b4efcf;outline-offset:-4px}\r\n.home-character canvas{position:absolute;left:50%;top:auto;bottom:calc(14% + 9.6px);display:block;min-height:0;width:auto;height:calc(125% - 120px);max-width:none;object-fit:contain;image-rendering:pixelated;filter:drop-shadow(3px 3px #211b31aa);transform:translateX(-50%);transform-origin:50% 50%;z-index:2}\r\n.home-character-platform{position:absolute;z-index:3;left:0;right:0;bottom:0;height:24%;border:2px solid #7b6689;border-bottom:0;background:repeating-linear-gradient(90deg,#bca7bd 0 18px,#86728f 18px 24px,#54465f 24px 30px);box-shadow:inset 0 4px #ead8bb55,inset 0 -5px #211b31,0 -3px 0 #211b31;image-rendering:pixelated;pointer-events:none}\r\n.home-character-platform:before{content:\"\";position:absolute;left:0;right:0;top:-4px;height:3px;background:repeating-linear-gradient(90deg,#ead8bb 0 16px,#ad98aa 16px 22px);border-top:1px solid #fff2d0}\r\n.home-character[data-phase=\"gone\"] .home-character-platform{opacity:.8}\r\n/* The weapon's name stays in the canvas's label and the Avatar dialog (the plate's \"Your avatar\"). */\r\n.home-character [data-weapon]{display:none}\r\n\r\n.home-character .home-base-window{position:absolute;inset:0;width:100%;height:100%;max-width:100%;object-fit:cover;image-rendering:pixelated;transform:none;filter:none;z-index:0;pointer-events:none}.home-character:has(.home-base-window) .home-character-platform{background:repeating-linear-gradient(90deg,#36454c 0 18px,#293b43 18px 24px,#1c303a 24px 30px);border-color:#728582;box-shadow:inset 0 4px #b7c8b633,inset 0 -5px #12252b,0 -3px 0 #12252b}\r\n/* The opaque 2D base window sits over the 3D coach card; once the card exists the window steps aside. */\r\n.containment-pod:has(.coach-mount .myr5-companion-card) .home-base-window{display:none}\r\n","pod/home-character.mjs":"import {GALA_KEY,loadGala} from './identity.mjs';\r\nimport {drawAnimatedWeapon} from './weapon-animator.mjs';\r\nimport {abilityFor,evolution} from './weapon-evolution.mjs';\r\nimport {createCharacterPhysics} from '../character-phone-physics.mjs';\r\nimport {subscribePhoneMotion} from '../character-phone-sensor.mjs';\r\nimport {createOptionalModuleLoader} from '../arcade/workout-effects-state.mjs';\r\n\r\n// R26: the War Room's coach body/pet (64-bit coach sprites in the saved coach's colours) stands here too. Loaded\r\n// lazily from the War Room bundle so the core app stays small; offline without it the Gala body shows.\r\nconst COACH_SPRITES='/war-room/coach-sprites.js',RECIPE_KEY='myr5-recipe-v1',COACH_CHOICE='myr5-war-room-coaches-v2/';\r\nconst loadPowerState=createOptionalModuleLoader('/arcade/power/avatar-power.mjs');\r\nconst loadPowerRenderer=createOptionalModuleLoader('/arcade/power/power.mjs');\r\nconst loadBlizzardRenderer=createOptionalModuleLoader('/arcade/hold/blizzard.mjs');\r\nexport function mountHomeCharacter(){\r\n const host=document.getElementById('homeCharacter'),canvas=host.querySelector('canvas'),hud=document.getElementById('hud');\r\n const W=window.GalaWeapons,A=window.GalaAvatar,reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n window.GalaWeaponMotion={drawAnimatedWeapon,abilityFor,evolution};\r\n let baseWindow=null;\r\n import('/modules/coach-world/hub-window.mjs').then(module=>{if(!disposed)baseWindow=module.mountHubBaseWindow(host);}).catch(error=>console.warn('Hub base window could not load',error));\r\n let performer,frame=0,last=0,origin=performance.now(),visible=true,disposed=false,coaches=null,feetFraction=.835;\r\n let powerCanvas=null,powerCtx=null,powerModule=null,powerRenderer=null,blizzardModule=null,powerSchedule=null,powerRecord={version:1,equipped:null,styles:{}},powerOwnerId=null,powerFingerprint='',powerLoading=null,powerLoadToken=0,powerActive=false,powerTime=0,powerPulse=0,lastPowerAt=0,lastReducedPowerAt=0;\r\n const hasPower=()=>!!powerRecord.styles?.[powerRecord.equipped];\r\n function clearPower(){powerActive=false;powerPulse=0;if(powerCtx)powerCtx.clearRect(0,0,160,168);if(powerCanvas)powerCanvas.style.display='none';}\r\n function resizePower(){if(!powerCanvas||!powerCtx)return;const dpr=Math.max(1,Math.min(2,window.devicePixelRatio||1)),width=Math.round(160*dpr),height=Math.round(168*dpr);if(powerCanvas.width!==width)powerCanvas.width=width;if(powerCanvas.height!==height)powerCanvas.height=height;powerCtx.setTransform(dpr,0,0,dpr,0,0);powerCtx.imageSmoothingEnabled=false;}\r\n function makePowerCanvas(){if(powerCanvas)return;powerCanvas=document.createElement('canvas');powerCanvas.className='myr5-home-power-overlay';powerCanvas.width=160;powerCanvas.height=168;powerCanvas.setAttribute('aria-hidden','true');Object.assign(powerCanvas.style,{filter:'none',opacity:'.55',pointerEvents:'none',zIndex:'2',display:'none',bottom:canvas.style.bottom||'',transform:canvas.style.transform||'translateX(-50%)'});const button=host.querySelector('button');if(button?.appendChild)button.appendChild(powerCanvas);else host.append(powerCanvas);powerCtx=powerCanvas.getContext('2d',{alpha:true});if(!powerCtx){powerCanvas.remove();powerCanvas=null;return;}resizePower();}\r\n function ensurePowerRenderer(){if(powerModule&&powerRenderer&&powerSchedule){makePowerCanvas();sync();return;}if(powerLoading)return;const token=++powerLoadToken;powerLoading=Promise.all([loadPowerState(),loadPowerRenderer(),loadBlizzardRenderer()]).then(([avatar,power,blizzard])=>{if(disposed||token!==powerLoadToken||!hasPower())return;powerModule=avatar;powerRenderer=power;blizzardModule=blizzard;powerSchedule=avatar.createAvatarPowerSchedule({seed:0x4d595235});makePowerCanvas();resize();sync();}).catch(()=>{}).finally(()=>{if(token===powerLoadToken)powerLoading=null;});}\r\n function requestPowerSnapshot(){if(typeof CustomEvent==='function')window.dispatchEvent(new CustomEvent('myr5:retained-character-power-request'));}\r\n function paintPower(now,reducedMotion=false){\r\n  if(!powerCtx||!powerModule||!powerSchedule)return;\r\n  const dt=lastPowerAt?Math.max(0,Math.min(.25,(now-lastPowerAt)/1000)):0;lastPowerAt=now;\r\n  const canShow=hasPower()&&visible;\r\n  const active=powerSchedule.step(dt,canShow,reducedMotion);\r\n  if(!active){clearPower();return;}\r\n  powerCanvas.style.display='block';if(!powerActive)powerPulse=1;powerActive=true;\r\n  if(reducedMotion)powerPulse=0;else{powerTime+=dt;powerPulse=Math.max(0,powerPulse-dt*2.5);}\r\n  const transform=performer?.powerTransform||{offset:0,flip:false,zoom:0,gait:0};\r\n  const feetVisible=transform.zoom<.001;\r\n  const anchors=[{index:27,x:.46,y:feetFraction+(transform.gait||0)/168,visible:feetVisible},{index:28,x:.54,y:feetFraction-(transform.gait||0)/168,visible:feetVisible}];\r\n  const state=powerModule.avatarPowerState(powerRecord,160,168,reducedMotion?0:powerTime,reducedMotion?0:powerPulse,false,anchors);\r\n  powerCtx.clearRect(0,0,160,168);\r\n  powerCtx.save();powerCtx.translate(transform.offset||0,0);if(transform.flip){powerCtx.translate(160,0);powerCtx.scale(-1,1);}\r\n  if(state?.blizzard)blizzardModule?.renderBlizzard(powerCtx,state);else if(state)powerRenderer.renderPower(powerCtx,state);\r\n  powerCtx.restore();\r\n }\r\n function acceptPowerRecord(event){const detail=event?.detail;if(!detail||typeof detail.ownerId!=='string')return;const changedOwner=detail.ownerId!==powerOwnerId;powerOwnerId=detail.ownerId;const next=detail.record&&typeof detail.record==='object'?detail.record:{version:1,equipped:null,styles:{}},fingerprint=JSON.stringify(next);if(changedOwner||fingerprint!==powerFingerprint){powerFingerprint=fingerprint;powerLoadToken++;powerLoading=null;powerRecord=next;powerSchedule?.reset();powerTime=0;lastPowerAt=0;clearPower();}if(hasPower()){ensurePowerRenderer();sync();}}\r\n const platform=document.createElement('div');platform.className='home-character-platform';platform.setAttribute('aria-hidden','true');host.append(platform);\r\n const physics=createCharacterPhysics({width:host.clientWidth||320,height:host.clientHeight||220,bodyWidth:canvas.clientWidth||120,bodyHeight:canvas.clientHeight||240,buffer:0,ship:false,climb:false,contain:true});\r\n let motion={gx:0,gy:0,shake:0,angularSpeed:0,timeSeconds:0},unsubscribeMotion=()=>{};\r\n function load(){\r\n  let storage;try{storage=localStorage;}catch{}\r\n  const look=loadGala(storage,A).look,chosen=look.weapon||{type:'rapier',tier:0};\r\n  look.weapon=W.unlocked(chosen)?chosen:{type:chosen.type,tier:0};\r\n  performer=window.GalaPerformance.create(look,coaches?{draw:(canvas,value,options)=>coaches.draw(A.draw,canvas,value,options),hasPet:coaches.hasPet,hasCoachBody:coaches.hasBody}:{});origin=performance.now();\r\n  feetFraction=(coaches?.hasBody||coaches?.hasPet)? .935 : .835;\r\n  host.querySelector('[data-weapon]').textContent=W.name(look.weapon);\r\n  canvas.setAttribute('aria-label',`${look.name||'Your Gala character'} with ${W.name(look.weapon)}`);\r\n  performer.paint(canvas,0,reduced.matches,{pose:physics.state.pose,phase:physics.state.phase,active:physics.state.active});feetFraction=performer.feetFraction||feetFraction;resize();sync();\r\n }\r\n function shown(){return document.body.dataset.tracking!=='true'&&document.body.dataset.screen!=='rest';}\r\n function animate(now){\r\n  frame=0;if(disposed||!shown()||!visible||document.hidden||document.querySelector('dialog[open]'))return;\r\n  if(reduced.matches){if(physics.state.phase!=='idle'||canvas.style.transform!=='translateX(-50%)')resetForReducedMotion();if(now-lastReducedPowerAt>=100){paintPower(now,true);lastReducedPowerAt=now;}if(powerModule&&powerSchedule&&hasPower())frame=requestAnimationFrame(animate);return;}\r\n  if(now-last>32){\r\n   const dt=last?Math.min(.05,(now-last)/1000):1/60;last=now;\r\n   physics.sample(motion,now/1000);physics.step(dt);\r\n   const state=physics.state;\r\n   // CSS uses clockwise positive angles; the domain heading points the feet toward gravity.\r\n   const groundedLean=state.phase==='slide'?(canvas.clientHeight||0)*(feetFraction-.5)*(1-Math.cos(state.angle)):0;\r\n   const transform=`translate(calc(-50% + ${state.x}px),${state.y+groundedLean}px) rotate(${-state.angle}rad)`;canvas.style.transform=transform;if(powerCanvas)powerCanvas.style.transform=transform;\r\n   canvas.dataset.pose=state.pose;canvas.dataset.phase=state.phase;host.dataset.phase=state.phase;host.dataset.phonePhase=state.phase;host.dataset.phoneX=Math.round(state.x);host.dataset.phoneY=Math.round(state.y);\r\n   performer.paint(canvas,now-origin,false,{pose:state.pose,phase:state.phase,active:state.active});paintPower(now,false);\r\n  }\r\n  if(!reduced.matches||(powerModule&&powerSchedule&&hasPower()))frame=requestAnimationFrame(animate);\r\n }\r\n function sync(){\r\n  const show=shown();host.hidden=!show;hud.hidden=show;\r\n  if(!show||!visible||document.hidden){powerSchedule?.step(0,false,reduced.matches);lastPowerAt=0;clearPower();}\r\n  if(frame)cancelAnimationFrame(frame);frame=0;\r\n  if(!disposed&&show&&visible&&!document.hidden)frame=requestAnimationFrame(animate);\r\n }\r\n function listenForMotion(){unsubscribeMotion();unsubscribeMotion=()=>{};if(!disposed&&!reduced.matches)unsubscribeMotion=subscribePhoneMotion(sample=>{motion=sample;if(shown()&&visible&&!document.hidden&&!document.querySelector('dialog[open]'))physics.sample(sample,sample.timeSeconds);});}\r\n function resetForReducedMotion(){\r\n  unsubscribeMotion();unsubscribeMotion=()=>{};physics.reset();motion={gx:0,gy:1,shake:0,angularSpeed:0,timeSeconds:0};last=0;\r\n  canvas.style.transform='translateX(-50%)';canvas.dataset.pose='idle';canvas.dataset.phase='idle';\r\n  host.dataset.phase='idle';host.dataset.phonePhase='idle';host.dataset.phoneX='0';host.dataset.phoneY='0';\r\n   platform.style.transform='';if(powerCanvas)powerCanvas.style.transform=canvas.style.transform;\r\n  performer?.paint(canvas,0,true,{pose:'idle',phase:'idle',active:false});\r\n }\r\n function reducedChanged(){if(reduced.matches)resetForReducedMotion();else listenForMotion();sync();}\r\n function attack(){\r\n  if(!performer?.weapon)return;\r\n  const index=performer.scenes.findIndex(scene=>scene.name==='weapon');\r\n  if(index<0)return;\r\n  origin=performance.now()-performer.scenes.slice(0,index).reduce((sum,scene)=>sum+scene.duration,0);sync();\r\n }\r\n const observer=new MutationObserver(sync);observer.observe(document.body,{subtree:true,attributes:true,attributeFilter:['data-tracking','data-screen','open']});\r\n const intersection=new IntersectionObserver(entries=>{visible=entries[0].isIntersecting;sync();});intersection.observe(host);\r\n const storage=event=>{if(event.key===GALA_KEY||event.key===RECIPE_KEY||event.key?.startsWith(COACH_CHOICE))load();},shownAgain=event=>{if(event.persisted){listenForMotion();load();requestPowerSnapshot();}};\r\n listenForMotion();\r\n const resize=()=>{const width=host.clientWidth||320,height=host.clientHeight||220,bodyWidth=canvas.clientWidth||120,bodyHeight=canvas.clientHeight||240,bottom=height*.24-bodyHeight*(1-feetFraction);canvas.style.bottom=`${bottom}px`;if(powerCanvas){powerCanvas.style.bottom=`${bottom}px`;resizePower();}physics.setRestCenter(width/2,height-bottom-bodyHeight/2);physics.resize(width,height,bodyWidth,bodyHeight);};\r\n const layoutObserver=typeof ResizeObserver==='function'?new ResizeObserver(resize):null;\r\n layoutObserver?.observe(host);layoutObserver?.observe(canvas);\r\n resize();window.addEventListener('resize',resize);\r\n window.addEventListener('mominc-avatar-change',load);window.addEventListener('myr5:recipe',load);window.addEventListener('pageshow',shownAgain);window.addEventListener('myr5:account-progress',load);window.addEventListener('storage',storage);\r\n document.addEventListener('visibilitychange',sync);reduced.addEventListener('change',reducedChanged);window.addEventListener('myr5:retained-character-power',acceptPowerRecord);\r\n const entry=host.querySelector('button');entry.setAttribute('aria-label','Open your War Room base');\r\n entry.addEventListener('click',()=>{if(window.myr5Routes?.go)window.myr5Routes.go('world');else location.href='/pose.html#world';});\r\n load();requestPowerSnapshot();\r\n import(COACH_SPRITES).then(m=>m.loadWarRoomCoaches(document)).then(value=>{if(!disposed){coaches=value;load();}}).catch(()=>{});\r\n  window.addEventListener('pagehide',event=>{if(event.persisted){unsubscribeMotion();powerSchedule?.step(0,false,reduced.matches);lastPowerAt=0;clearPower();return;}disposed=true;baseWindow?.dispose();baseWindow=null;powerLoadToken++;powerSchedule?.dispose();powerCanvas?.remove();powerCanvas=null;powerCtx=null;unsubscribeMotion();sync();observer.disconnect();intersection.disconnect();layoutObserver?.disconnect();window.removeEventListener('resize',resize);window.removeEventListener('mominc-avatar-change',load);window.removeEventListener('myr5:recipe',load);window.removeEventListener('pageshow',shownAgain);window.removeEventListener('myr5:account-progress',load);window.removeEventListener('storage',storage);window.removeEventListener('myr5:retained-character-power',acceptPowerRecord);document.removeEventListener('visibilitychange',sync);reduced.removeEventListener('change',reducedChanged);}); // a bfcache hide keeps it alive for pageshow\r\n}\r\n","pod/identity.mjs":"export const GALA_KEY='mominc-avatar-v1';\r\nexport const LIVE_GALA='https://mominc.online/gala/';\r\nexport function loadGala(storage,avatar){\r\n try{const raw=storage.getItem(GALA_KEY);if(raw)return {look:avatar.normalize(JSON.parse(raw)),linked:true};}catch{}\r\n return {look:structuredClone(avatar.defaultLook),linked:false};\r\n}\r\nexport function importGala(text,avatar){if(typeof text!=='string'||new TextEncoder().encode(text).length>30000)throw Error('Choose a Gala look file smaller than 30 KB.');return avatar.normalize(JSON.parse(text));}\r\nexport const POWERS={shield:{name:'Shield',line:'The shield holds.'},ember:{name:'Ember',line:'The ember field holds.'},arc:{name:'Arc',line:'The arc field holds.'},frost:{name:'Frost',line:'The frost field holds.'}};\r\nexport function loadPower(storage){try{const p=storage.getItem('myr5-pod-power-v1');if(POWERS[p])return p;}catch{}return 'shield';}\r\n","pod/instrument-controls.css":"/* Mounted service keys with a separate inset legend below the metal actuator. */\r\n.reward-pack-launch{left:auto!important;right:var(--shell-right,14px)!important;bottom:var(--shell-bottom,80px)!important;max-width:64px!important;min-width:52px!important;min-height:52px!important;padding:5px!important;gap:2px!important;z-index:40!important}\r\n.reward-pack-launch canvas{width:28px!important;height:28px!important}.reward-pack-launch span{font:700 12px ui-monospace,monospace!important;padding:2px 4px}\r\n/* The old page header is replaced by mounted cockpit and rail controls. */\r\nhtml.shell-ready:has(#homeScreen.cockpit-mounted) .ship-header:not(.shell-band-pod){display:none!important}\r\n/* The original secret-bearing board keeps its own geometry inside a service drawer.\r\n   It expands into the shared well, rather than moving individual secret hit regions. */\r\n#portalHome[data-service-drawer=true]{transform-origin:12% 92%;animation:service-drawer-open .32s cubic-bezier(.16,1,.3,1)}\r\n@keyframes service-drawer-open{from{opacity:.5;translate:0 40px;scale:.97}to{opacity:1;translate:0 0;scale:1}}\r\n.app-update-banner{left:auto!important;right:calc(var(--shell-right,14px) + 4px)!important;top:calc(var(--shell-top,14px) + 58px)!important;bottom:auto!important;max-width:min(340px,calc(100vw - 44px))!important;max-height:125px;overflow:auto!important;box-sizing:border-box;font-size:12px!important;padding:8px!important}\r\n.app-update-banner button{min-height:44px!important;}\r\n@media(prefers-reduced-motion:reduce){#portalHome[data-service-drawer=true]{animation:none}}\r\n","pod/instrument-controls.mjs":"// Shared service hardware: retain the real controls and their existing listeners.\r\nconst P={close:'M6 6l12 12M18 6 6 18',back:'m14 5-7 7 7 7M7 12h14',play:'m8 5 11 7-11 7z',stop:'M6 6h12v12H6z',save:'M5 3h12l4 4v14H3V3zM7 3v6h10V3M7 21v-8h10v8',retry:'M5 8a8 8 0 1 1-1 8M3 3v6h6',add:'M12 4v16M4 12h16',remove:'M4 12h16',power:'M12 2v10M6 5a9 9 0 1 0 12 0',tool:'M5 3l4 4-2 2-4-4v5l5 3 9 9 4-4-9-9-3-6z',drawer:'M3 5h18v14H3zM3 12h18M9 8h6M9 15h6',download:'M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5',camera:'M3 7h5l2-3h4l2 3h5v14H3zM16 14a4 4 0 1 1-8 0 4 4 0 0 1 8 0',speaker:'M3 9h5l5-5v16l-5-5H3zM17 7q6 5 0 10'};\r\nexport function mountInstrumentControls(doc=document){\r\n if(doc.documentElement.dataset.instrumentsMounted)return;\r\n doc.documentElement.dataset.instrumentsMounted='true';\r\n let queued=false;\r\n const patch=()=>{\r\n  queued=false;\r\n  if(doc.documentElement.dataset.gameActive==='true')return;\r\n  for(const b of doc.querySelectorAll('[data-controls=panel] button')){\r\n   if(b.closest('#myr5Shell,.cockpit-visors,.cockpit-panel-tools,.cb-deck,.full-workout,.fw-board')||b.querySelector('canvas,svg:not(.instrument-glyph),video,img')||b.matches('[role=switch],.cb-rotary,.cb-switch')||b.children.length>2)continue;\r\n   const text=[...b.childNodes].filter(n=>n.nodeType===3).map(n=>n.textContent).join(' ').trim()||b.querySelector('.instrument-legend')?.textContent?.trim();\r\n   if(!text||text.length>100)continue;\r\n   // Write only real changes: even a same-value attribute write queues a mutation record, and\r\n   // hosts that watch their own subtree (World RTS HUD watches class) would re-render forever.\r\n   const set=(attr,value)=>{if(b.getAttribute(attr)!==value)b.setAttribute(attr,value);};\r\n   if(!b.classList.contains('instrument-key'))b.classList.add('instrument-key');\r\n   // A field's current value remains a display, never an ambiguous unlabeled icon.\r\n   // Names this patcher wrote follow later text changes; author-supplied names are never touched.\r\n   const ours=b.dataset.instrumentLabel;\r\n   if(!b.hasAttribute('aria-label')||b.getAttribute('aria-label')===ours)set('aria-label',text);\r\n   if(!b.hasAttribute('title')||b.title===ours)set('title',text);\r\n   if(b.getAttribute('aria-label')===text)set('data-instrument-label',text);\r\n   // Opt-in only ([data-controls=panel] hosts): glyph from data-glyph, legend only with data-legend. No fallback glyph.\r\n   const kind=b.dataset.glyph;\r\n   if(P[kind]){let glyph=b.querySelector('.instrument-glyph');\r\n    if(!glyph){glyph=doc.createElementNS('http://www.w3.org/2000/svg','svg');glyph.classList.add('instrument-glyph');glyph.setAttribute('viewBox','0 0 24 24');glyph.setAttribute('aria-hidden','true');glyph.innerHTML='<path/>';b.prepend(glyph);}\r\n    const path=glyph.firstElementChild;if(path.getAttribute('d')!==P[kind])path.setAttribute('d',P[kind]);}\r\n   if(!b.hasAttribute('data-legend'))continue;\r\n   let legend=b.querySelector('.instrument-legend');if(!legend){legend=doc.createElement('span');legend.className='instrument-legend';legend.setAttribute('aria-hidden','true');b.append(legend);}\r\n   if(legend.textContent!==text)legend.textContent=text;\r\n   for(const node of [...b.childNodes])if(node.nodeType===3)node.remove();\r\n  }\r\n };\r\n const observer=new MutationObserver(()=>{if(!queued){queued=true;queueMicrotask(patch);}});\r\n // Observe text/child changes only. Attribute changes from pressed states never retrigger work.\r\n observer.observe(doc.body,{subtree:true,childList:true,characterData:true});patch();\r\n observer.observe(doc.documentElement,{attributes:true,attributeFilter:['data-game-active']});\r\n return ()=>observer.disconnect();\r\n}\r\n","pod/performance-workout.mjs":"import {EXERCISES} from '../exercise-library.mjs';\r\nimport {holdXp,repXp,HOLD_TARGET_SECONDS,HOLD_CAP_SECONDS,HOLD_RECOVERY_SECONDS,WORKOUT_REST_SECONDS,REP_CAP,PREPARATION_REPS,SECOND_PREPARATION_TEMPO_SECONDS,SPRINT_ROUNDS,SPRINT_SECONDS,SPRINT_REST_SECONDS,HOLD_BLOCK_SECONDS,SPRINT_ROUND_XP,GENTLE_BLOCK_XP,clampBonusXp,XP_SCALE} from '../progression-rules.mjs';\r\n\r\nexport function exerciseDifficulty(mode){\r\n if(mode==='plank-flight')return 'easy';\r\n const exercise=EXERCISES[mode];if(!exercise)return 'easy';\r\n const rows=Object.values(EXERCISES).filter(row=>row.group===exercise.group&&row.kind===exercise.kind);\r\n const index=rows.findIndex(row=>row.id===mode);\r\n return ['easy','medium','hard','expert'][Math.min(3,Math.floor(Math.max(0,index)*4/Math.max(1,rows.length)))];\r\n}\r\nexport function preparationVariants(mode){\r\n const chosen=EXERCISES[mode],rows=Object.values(EXERCISES).filter(row=>row.group===chosen?.group&&row.kind==='reps');\r\n const index=rows.findIndex(row=>row.id===mode);\r\n return {easy:rows[Math.max(0,index-1)]??chosen,hard:rows[Math.min(rows.length-1,index+1)]??chosen};\r\n}\r\nexport const CIRCUIT_HOLD_GRACE_SECONDS=10;\r\nconst number=value=>Math.max(0,Number(value)||0);\r\n/** One persisted workout contains preparations and one working set. Only the\r\n * working result is eligible for history/XP. Raw tracker counts are monotonic;\r\n * baselines exclude everything performed during a preparation or recovery. */\r\nexport class PerformanceWorkout {\r\n constructor({mode,kind,difficulty=exerciseDifficulty(mode),cardio='gentle',snapshot=null,target}={}){\r\n  // Subway Burpees (the runner) is endless gentle cardio: no sprint rounds, no 600 s finish; it ends on stop or when Coach catches you.\r\n  this.mode=mode;this.endless=!!EXERCISES[mode]?.endless;this.kind=kind==='hold'?'hold':kind==='reps'?'reps':cardio==='sprint'&&!this.endless?'sprint':'gentle';this.difficulty=difficulty;\r\n  this.target=target>0?Number(target):undefined;this.holdProgress=0;this.frozen=false; // target = circuit only: ends the set at N reps/seconds and skips the warm-ups\r\n  this.stage=this.kind==='reps'&&!this.target?'preparation-easy':'working';this.raw=0;this.baseline=0;this.value=0;this.activeSeconds=0;this.continuousSeconds=0;this.maxContinuousSeconds=0;this.xpBase=0;this.recoveryUntil=0;this.paused=false;this.finished=false;this.round=1;this.sprintSeconds=0;this.lastNow=null;this.lastMovementNow=-Infinity;this.lastPreparationRepAt=null;this.slowAccepted=0;this.perDifficultyContinuous={};this.lastCameraHold=0;this.continuousRawOffset=0;this.holdBlocks=[];this.notice='';this.noticeUntil=0;\r\n  if(snapshot&&snapshot.mode===mode)Object.assign(this,snapshot,{finished:false,continuousSeconds:0,lastNow:null,lastCameraHold:0,continuousRawOffset:0,frozen:false,target:this.target});\r\n  if(snapshot?.activeSeconds>=HOLD_BLOCK_SECONDS&&!Array.isArray(snapshot.holdBlocks))this.holdBlocks=null;\r\n  if(!Object.keys(this.perDifficultyContinuous).length&&this.maxContinuousSeconds>0)this.perDifficultyContinuous[this.difficulty]=this.maxContinuousSeconds;\r\n }\r\n snapshot(){return Object.fromEntries(Object.entries(this).filter(([key])=>!['lastNow','lastMovementNow'].includes(key)));}\r\n /** Reps or seconds left to the circuit target; null when there is no target (single-exercise mode). */\r\n get left(){if(!this.target)return null;const done=this.kind==='reps'?this.value:this.kind==='hold'?this.holdProgress:this.activeSeconds;return Math.max(0,Math.ceil(this.target-done));}\r\n get counting(){return !this.finished&&!this.paused&&this.stage!=='preparation-rest'&&this.stage!=='sprint-rest';}\r\n get instruction(){\r\n  if(this.notice&&Date.now()<this.noticeUntil)return this.notice;\r\n  const variants=preparationVariants(this.mode);\r\n  if(this.paused)return 'Shake out and recover. Resume whenever ready; recovery adds no XP.';\r\n  if(this.stage==='preparation-easy')return `Warm up: ${PREPARATION_REPS} ${variants.easy?.name??this.mode}, full comfortable range. Good job, superstar. No XP for preparation.`;\r\n  if(this.stage==='preparation-slow')return `Slow preparation: ${PREPARATION_REPS} ${variants.hard?.name??this.mode}. Take ${SECOND_PREPARATION_TEMPO_SECONDS} seconds for each full movement. No XP for preparation.`;\r\n  if(this.stage==='preparation-rest')return 'Recover for 30 seconds. Preparation does not count toward training history.';\r\n  if(this.stage==='sprint-rest')return `Round ${this.round}/${SPRINT_ROUNDS}: recover for 45 seconds.`;\r\n  if(this.kind==='sprint')return `Round ${this.round}/${SPRINT_ROUNDS}: sprint for 15 seconds. Warm up first.`;\r\n  if(this.kind==='reps')return this.value>=REP_CAP?'Please stop. Thirty reps is the counting limit.':'Working set: aim for 8–12 controlled reps. Finish when you choose.';\r\n  return this.activeSeconds>=HOLD_CAP_SECONDS?'Please stop. Thirty active minutes is the XP limit.':this.kind==='hold'?'Hold: active time counts up. Break whenever needed. Ten uninterrupted minutes earns gold.':'Gentle cardio: active movement earns XP; rest does not.';\r\n }\r\n breakHold(now=Date.now()){if(this.kind!=='hold'||this.finished)return;this.paused=true;this.continuousSeconds=0;this.recoveryUntil=now+(this.target?CIRCUIT_HOLD_GRACE_SECONDS:HOLD_RECOVERY_SECONDS)*1000;}\r\n // Circuit: a break shorter than the grace keeps the hold; a longer one restarts the hold toward the target (earned XP stays).\r\n resumeHold(now=Date.now()){if(this.target&&this.recoveryUntil&&now>this.recoveryUntil)this.holdProgress=0;this.paused=false;this.continuousSeconds=0;this.recoveryUntil=0;}\r\n extendRecovery(seconds=15,now=Date.now()){this.recoveryUntil=Math.max(now,this.recoveryUntil)+seconds*1000;}\r\n advancePreparationRecovery(now=Date.now()){\r\n  if(this.stage!=='preparation-rest'||now<this.recoveryUntil)return false;\r\n  this.stage=this.nextStage;this.value=0;this.baseline=this.raw;this.lastPreparationRepAt=now;this.lastNow=null;return true;\r\n }\r\n rebaseTracking(){this.raw=0;this.baseline=0;this.value=0;this.lastNow=null;this.lastMovementNow=-Infinity;}\r\n switchDifficulty(difficulty){if(!['easy','medium','hard','expert'].includes(difficulty))throw Error('Unknown exercise difficulty.');if(difficulty===this.difficulty)return;if(this.kind!=='hold')throw Error('Choose repetition difficulty before the working set.');this.perDifficultyContinuous[this.difficulty]=Math.max(this.perDifficultyContinuous[this.difficulty]||0,this.maxContinuousSeconds);this.difficulty=difficulty;this.continuousSeconds=0;this.maxContinuousSeconds=0;this.continuousRawOffset=this.lastCameraHold;}\r\n update(m,now=Date.now()){\r\n  if(this.finished)return null;\r\n  const raw=number(this.kind==='hold'?m.totalHold:this.kind==='reps'?m.count:m.manual?m.elapsed:['steps','jumps','reps'].includes(m.kind)?m.count:m.active);\r\n  if(this.frozen){this.raw=raw;this.lastNow=now;return null;} // circuit: pose lost, the count holds still and nothing earned meanwhile is counted\r\n  const delta=Math.max(0,raw-this.raw),elapsed=this.lastNow===null?0:Math.max(0,Math.min(1,(now-this.lastNow)/1000));this.raw=raw;this.lastNow=now;if(delta>0)this.lastMovementNow=now;const moving=m.manual?!this.paused:now-this.lastMovementNow<=1250;\r\n  if(this.stage==='preparation-rest'){\r\n   this.baseline=raw;this.advancePreparationRecovery(now);return null;\r\n  }\r\n  if(this.kind==='reps'){\r\n   this.value=Math.min(REP_CAP,Math.max(0,raw-this.baseline));\r\n   if(this.stage.startsWith('preparation-')){\r\n    if(this.stage==='preparation-slow'){if(raw>this.baseline){if(now-(this.lastPreparationRepAt??now)>=SECOND_PREPARATION_TEMPO_SECONDS*1000){this.slowAccepted++;this.lastPreparationRepAt=now;}this.baseline=raw;}this.value=this.slowAccepted;}\r\n    if(this.value>=PREPARATION_REPS){this.nextStage=this.stage==='preparation-easy'?'preparation-slow':'working';this.stage='preparation-rest';this.recoveryUntil=now+WORKOUT_REST_SECONDS*1000;this.value=0;this.baseline=raw;}\r\n    return null;\r\n   }\r\n   this.xpBase=repXp({difficulty:this.difficulty,to:Math.floor(this.value)});\r\n   if(this.value>=Math.min(REP_CAP,this.target||REP_CAP))return this.finish();return null;\r\n  }\r\n  if(this.kind==='hold'){\r\n   const wasHolding=this.continuousSeconds>0,cameraHold=number(m.hold);if(!m.manual&&cameraHold<this.lastCameraHold){this.continuousRawOffset=0;this.continuousSeconds=0;}this.lastCameraHold=cameraHold;const cameraContinuous=m.manual?null:Math.max(0,cameraHold-this.continuousRawOffset);\r\n   if(!m.manual&&cameraContinuous===0&&wasHolding)this.breakHold(now);\r\n   if(!m.manual&&this.paused&&cameraContinuous>0)this.resumeHold(now);\r\n   if(this.paused)return null;\r\n   const before=this.activeSeconds,continuousBefore=this.continuousSeconds;\r\n   this.activeSeconds=Math.min(HOLD_CAP_SECONDS,before+delta);\r\n   this.continuousSeconds=m.manual?continuousBefore+delta:cameraContinuous;\r\n   this.maxContinuousSeconds=Math.max(this.maxContinuousSeconds,this.continuousSeconds);\r\n   this.perDifficultyContinuous[this.difficulty]=Math.max(this.perDifficultyContinuous[this.difficulty]||0,this.continuousSeconds);\r\n   if(Array.isArray(this.holdBlocks))for(let block=Math.floor(before/HOLD_BLOCK_SECONDS);block<Math.floor(this.activeSeconds/HOLD_BLOCK_SECONDS);block++){const activeStart=block*HOLD_BLOCK_SECONDS;this.holdBlocks.push({difficulty:this.difficulty,activeStart,continuousStart:Math.max(0,continuousBefore+activeStart-before)});}\r\n   if(this.target)this.holdProgress+=delta;\r\n   this.xpBase+=holdXp({difficulty:this.difficulty,from:before,to:this.activeSeconds,continuousAtFrom:continuousBefore});this.value=this.activeSeconds;\r\n   if(this.activeSeconds>=HOLD_CAP_SECONDS||this.target&&this.holdProgress>=this.target)return this.finish();return null;\r\n  }\r\n  if(this.kind==='sprint'){\r\n   if(this.stage==='sprint-rest'){\r\n    if(now>=this.recoveryUntil){if(this.round>=SPRINT_ROUNDS)return this.finish();this.round++;this.sprintSeconds=0;this.stage='working';}return null;\r\n   }\r\n   // Movement must be observed. An unattended wall-clock cannot award a sprint.\r\n   this.sprintSeconds+=moving?elapsed:0;this.activeSeconds+=moving?elapsed:0;this.value=this.activeSeconds;\r\n   if(this.sprintSeconds>=SPRINT_SECONDS){this.stage='sprint-rest';this.recoveryUntil=now+SPRINT_REST_SECONDS*1000;this.xpBase+=SPRINT_ROUND_XP;}\r\n   return null;\r\n  }\r\n  this.activeSeconds+=moving?elapsed:0;this.value=this.activeSeconds;this.xpBase=Math.floor(this.activeSeconds/15)*GENTLE_BLOCK_XP;if(!this.endless&&this.activeSeconds>=Math.min(HOLD_TARGET_SECONDS,this.target||HOLD_TARGET_SECONDS))return this.finish();return null;\r\n }\r\n finish(){\r\n  if(this.finished||this.stage.startsWith('preparation-')||this.xpBase<=0||this.kind==='reps'&&this.value<1||this.kind!=='reps'&&this.activeSeconds<15){\r\n   // Refusals are shown via `instruction` for 4 s so a tap on Finish never fails silently.\r\n   if(!this.finished){this.notice=this.stage.startsWith('preparation-')?'Finish the preparation set first.':this.kind==='reps'?'Do at least one rep before finishing the set.':this.kind!=='reps'&&this.activeSeconds<15?'Keep moving: 15 active seconds are needed before this set can be saved.':'Nothing to save yet.';this.noticeUntil=Date.now()+4000;}\r\n   return null;\r\n  }\r\n  this.finished=true;const bonusXp=clampBonusXp(this.bonusXp,this.xpBase);return {mode:this.mode,kind:this.kind,difficulty:this.difficulty,value:this.value,activeSeconds:this.activeSeconds,maxContinuousSeconds:this.maxContinuousSeconds,perDifficultyContinuous:{...this.perDifficultyContinuous},...(this.kind==='hold'&&Array.isArray(this.holdBlocks)?{holdBlocks:this.holdBlocks.map(block=>({...block}))}:{}),xpBase:this.xpBase+bonusXp,xpScale:XP_SCALE,...(bonusXp>0?{bonusXp}:{}),earned:this.xpBase>0,preparation:false,working:true,rounds:this.kind==='sprint'?Math.min(SPRINT_ROUNDS,this.round):0};\r\n }\r\n}\r\n","pod/persistent-chrome.css":".pod-persistent-chrome{position:fixed;inset:max(7px,env(safe-area-inset-top)) 18px auto;z-index:2147483000;display:flex;justify-content:space-between;align-items:start;gap:12px;pointer-events:none;height:46px}\r\n.pod-persistent-chrome>button{pointer-events:auto!important;margin:0!important;position:relative!important;inset:auto!important;min-height:42px;max-width:45%;border-radius:4px;font:700 10px/1.2 monospace;letter-spacing:.1em;cursor:pointer}\r\n.pod-persistent-chrome .metal-grimoire-key{padding:10px 16px;color:#28231f!important;border:1px solid #514b41!important;background:linear-gradient(145deg,#bbb7a8,#80796b 48%,#b5ad99)!important;text-shadow:0 1px #e9e3ce;box-shadow:inset 0 1px #e8e3d0,inset 0 -3px #4c463d,0 3px 4px #0008!important}\r\n.pod-persistent-chrome .metal-grimoire-key:before,.pod-persistent-chrome .metal-grimoire-key:after{content:'+';position:absolute;top:4px;color:#514b41;font:9px monospace}.metal-grimoire-key:before{left:4px}.metal-grimoire-key:after{right:4px}\r\n.pod-now-playing{display:grid;gap:3px;padding:6px 9px;color:#ffb568;border:3px ridge #857563;background:repeating-linear-gradient(0deg,#24170f 0 2px,#170e09 2px 4px);text-align:left;box-shadow:inset 0 0 12px #000,0 2px 4px #0009;text-shadow:0 0 6px #ff8b37}\r\n.pod-now-playing small{font-size:8px;color:#cb8c56}.pod-now-playing span{display:block;max-width:135px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\r\n.pod-header-actions{display:none!important}.deck-strip>span{display:none!important}.deck-strip{justify-content:flex-end!important}.containment-pod .coach-line{visibility:hidden}\r\n@media(max-width:480px){.pod-persistent-chrome{left:13px;right:13px}.pod-persistent-chrome .metal-grimoire-key{padding:10px}.pod-now-playing span{max-width:112px}}\r\n\r\n/* The stamped image is the brand. The small route caption and a second wordmark crowded the cockpit. */\r\n.ship-header .mom-brand>span,.ship-header .ship-location{display:none!important}\r\n.ship-header .mom-brand{gap:0;flex:none}.ship-header .mom-brand img{width:43px;height:43px;object-fit:contain;filter:drop-shadow(0 0 8px #e3a9f866)}\r\n.ship-header #openSettings.ship-settings-toggle{box-sizing:border-box;display:grid;grid-template-columns:27px minmax(0,1fr);align-items:center;gap:7px;width:132px;min-width:132px;min-height:48px;margin-left:auto;padding:5px 7px;border:2px solid #b5a0a3;border-top-color:#e4d4c8;border-bottom-color:#62545c;border-radius:5px;background:linear-gradient(135deg,#756d70,#38343d);box-shadow:inset 0 1px #fff8,inset 0 -3px #161a23,0 3px 0 #1b1720,0 5px 8px #0008;color:#c8fbd7;text-align:left;cursor:pointer}\r\n.ship-settings-handle{position:relative;display:block;width:25px;height:33px;border:2px solid #1b2226;border-radius:4px;background:linear-gradient(90deg,#191f25,#3b464d);box-shadow:inset 1px 2px 5px #000a,0 1px #c6b8af}\r\n.ship-settings-handle:before{content:'';position:absolute;left:3px;top:4px;width:15px;height:12px;border:1px solid #eee1cb;border-radius:2px;background:repeating-linear-gradient(90deg,#d4c8b7 0 3px,#918880 3px 4px);box-shadow:0 2px 2px #0009;transition:translate .12s}\r\n.ship-settings-screen{position:relative;display:grid;align-content:center;gap:2px;min-width:0;height:32px;padding:0 5px;border:2px inset #777569;border-radius:3px;background:repeating-linear-gradient(0deg,#1b382a 0 2px,#102719 2px 4px);box-shadow:inset 1px 2px 6px #000b,0 0 7px #89e8a837;color:#b7ffce;text-shadow:0 0 5px #65eb95;font:700 9px/1.1 ui-monospace,Consolas,monospace;letter-spacing:.09em;overflow:hidden}\r\n.ship-settings-screen small{font-size:6px;color:#82b995}.ship-settings-screen strong{font-size:9px}\r\n.ship-header #openSettings.ship-settings-toggle[aria-expanded=true] .ship-settings-handle:before{translate:0 10px;background:repeating-linear-gradient(90deg,#f6d19a 0 3px,#b98e5b 3px 4px)}\r\n.ship-header #openSettings.ship-settings-toggle[aria-expanded=true] .ship-settings-screen{color:#ffe7b6;text-shadow:0 0 6px #ffc66a;background:repeating-linear-gradient(0deg,#453322 0 2px,#2a211a 2px 4px)}\r\n.ship-header #openSettings.ship-settings-toggle:focus-visible{outline:3px solid #ffe49b;outline-offset:3px}\r\n.ship-header .pod-tilt-request{box-sizing:border-box;min-height:40px;padding:5px 9px;border:2px solid #aaa3a1;border-top-color:#e2d9c6;border-bottom-color:#615d62;border-radius:4px;background:linear-gradient(#6f7775,#414b4b);box-shadow:inset 0 1px #fff7,0 3px #222b30;color:#eaf4da;font:700 9px ui-monospace,Consolas,monospace;letter-spacing:.04em;text-shadow:0 1px #1d2b2a}\r\n/* The floating grimoire and DJ terminals sit below the header plate while the pod is in view. */\r\nbody>.pod-persistent-chrome,#portalWorkoutHome>.pod-persistent-chrome{top:calc(78px + env(safe-area-inset-top));left:18px;right:18px}\r\n@media(max-height:720px) and (orientation:portrait){body>.pod-persistent-chrome,#portalWorkoutHome>.pod-persistent-chrome{top:calc(66px + env(safe-area-inset-top))}}\r\n@media(max-width:480px){.ship-header #openSettings.ship-settings-toggle{width:119px;min-width:119px}.ship-settings-screen{padding:0 3px}body>.pod-persistent-chrome,#portalWorkoutHome>.pod-persistent-chrome{left:13px;right:13px}}\r\n\r\n/* Physical dock controls are available before the portal scene loads. */\r\n#coachDock.coach-dock.ship-control-deck{border-top:3px solid #b8a890;background:radial-gradient(circle at 12px 9px,#d4c3a9 0 2px,#292832 3px 5px,transparent 6px),radial-gradient(circle at calc(100% - 12px) 9px,#d4c3a9 0 2px,#292832 3px 5px,transparent 6px),repeating-linear-gradient(90deg,#ffffff08 0 1px,transparent 1px 5px),linear-gradient(180deg,#48545a,#252f38 38%,#171e27);box-shadow:inset 0 2px #e5d8bd72,inset 0 7px 8px #1117229c,0 -4px 18px #070b11a8}\r\n#coachDock.coach-dock.ship-control-deck:after{content:'';position:absolute;left:8px;right:8px;top:9px;height:1px;background:linear-gradient(90deg,transparent,#d5c9ad80 15%,#d5c9ad80 85%,transparent);pointer-events:none}\r\n#coachDock.coach-dock.ship-control-deck button.dock-control{filter:none;overflow:visible}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span{position:relative;box-sizing:border-box;width:35px;height:35px;border:2px solid #c6b9a0;border-top-color:#f1e3c7;border-bottom-color:#665e56;border-radius:4px;clip-path:none;background:linear-gradient(145deg,#bcc8c2,#81918f 43%,#4b5c60);box-shadow:inset 0 1px #fff9,inset -2px -3px #22343b88,0 3px 0 #182127,0 5px 5px #000a;color:#243640}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span:after{content:'';position:absolute;right:2px;top:2px;width:4px;height:4px;border-radius:50%;background:#263d40;box-shadow:0 0 0 1px #e5e1cb77}\r\n#coachDock.coach-dock.ship-control-deck .dock-control[aria-current=page]>span:after{background:#ffd07c;box-shadow:0 0 6px #ffce78,0 0 0 1px #ffeed3}\r\n#coachDock.coach-dock.ship-control-deck .dock-control>span svg{width:21px;height:21px;filter:drop-shadow(0 1px #f2ffed77)}\r\n#coachDock.coach-dock.ship-control-deck .dock-control:active>span,#coachDock.coach-dock.ship-control-deck .dock-control[aria-current=page]>span{transform:translateY(3px);box-shadow:inset 1px 3px 4px #1e2d34ad,0 1px 0 #182127;color:#17282a}\r\n#coachDock.coach-dock.ship-control-deck .dock-control:focus-visible{outline:3px solid #ffe19a;outline-offset:2px}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings>span{border-radius:50%;background:radial-gradient(circle,#d6ddcf 0 24%,#8fa69e 25% 49%,#526d70 50% 67%,#b4bfb6 69%);box-shadow:inset 0 1px #fff9,0 3px #29363a,0 0 0 2px #132128}\r\n#coachDock.coach-dock.ship-control-deck .dock-scoreboard>span{border-radius:5px 5px 10px 10px;background:linear-gradient(90deg,#596b72 0 20%,#becac3 22% 72%,#596b72 75%);}\r\n#coachDock.coach-dock.ship-control-deck .dock-food>span{height:31px;border-radius:3px 3px 9px 9px;background:linear-gradient(#ffe0a9,#c69458 48%,#845837);color:#493322}\r\n#coachDock.coach-dock.ship-control-deck .dock-achievements>span{border-radius:3px;clip-path:polygon(0 0,100% 0,100% 71%,50% 100%,0 71%);background:linear-gradient(140deg,#ffe8ac,#b98b44 67%,#765223);color:#533e25}\r\n#coachDock.coach-dock.ship-control-deck .dock-pod>span{width:29px;border-radius:12px;background:linear-gradient(90deg,#496c70,#b8d1bd 35%,#6d9d92 65%,#35585d);color:#183b3c}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings:after,#coachDock.coach-dock.ship-control-deck .dock-scoreboard:after,#coachDock.coach-dock.ship-control-deck .dock-food:after,#coachDock.coach-dock.ship-control-deck .dock-achievements:after,#coachDock.coach-dock.ship-control-deck .dock-pod:after{position:absolute;top:calc(100% + 4px);left:50%;translate:-50% 0;color:#c5d5cb;font:700 7px/1 ui-monospace,Consolas,monospace;letter-spacing:.05em;white-space:nowrap;text-shadow:0 1px #071118;pointer-events:none}\r\n#coachDock.coach-dock.ship-control-deck .dock-settings:after{content:'COACH'}#coachDock.coach-dock.ship-control-deck .dock-scoreboard:after{content:'SCORES'}#coachDock.coach-dock.ship-control-deck .dock-food:after{content:'FOOD'}#coachDock.coach-dock.ship-control-deck .dock-achievements:after{content:'BADGES'}#coachDock.coach-dock.ship-control-deck .dock-pod:after{content:'COMMS'}\r\n#coachDock.coach-dock.ship-control-deck button.dock-portal{border:3px solid #9eb7b1;border-top-color:#e1eed6;border-bottom-color:#445b5b;border-radius:50%!important;background:radial-gradient(circle at 50% 40%,#1a4545 0 46%,#0b212a 47% 58%,transparent 59%),repeating-conic-gradient(#b6c6b3 0deg 8deg,#51666a 8deg 15deg);box-shadow:0 0 0 3px #17262b,0 4px 0 3px #0a141b,inset 0 0 11px color-mix(in srgb,var(--portal-strip,#b026ff) 49%,transparent),0 0 12px var(--portal-strip-glow,#b026ff40);color:#d7ffe6;text-shadow:0 0 5px var(--portal-strip,#b026ff)}\r\n#coachDock.coach-dock.ship-control-deck button.dock-portal:before{display:none}\r\n#coachDock.coach-dock.ship-control-deck button.dock-portal:active,#coachDock.coach-dock.ship-control-deck button.dock-portal[aria-current=page]{transform:translateY(3px);box-shadow:0 0 0 3px #17262b,0 1px 0 3px #0a141b,inset 0 0 15px var(--portal-strip,#b026ff),0 0 18px var(--portal-strip,#b026ff)}\r\n#coachDock.coach-dock.ship-control-deck .dock-portal svg{width:20px;height:20px}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control{box-sizing:border-box;width:42px;height:43px;padding:15px 2px 2px;border:2px solid #afbbad;border-top-color:#e8e9d4;border-bottom-color:#566963;border-radius:4px!important;background:linear-gradient(145deg,#adbcb4,#526c6b 65%,#365154);box-shadow:inset 0 1px #fff9,inset 0 -2px #2a4747,0 3px #142324;color:#173f3a;font:700 8px ui-monospace,Consolas,monospace}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control:before{content:'';display:block;position:absolute;left:8px;top:5px;width:23px;height:8px;border:1px solid #172b2d;border-radius:2px;background:linear-gradient(90deg,#23383a 0 45%,#e4cfaa 48% 85%,#788c88 88%);box-shadow:inset 0 1px 2px #0008}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control[data-on=false]:before{background:linear-gradient(90deg,#e4cfaa 0 40%,#758c89 43%,#23383a 48%)}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound button.dock-control[data-on=true]{color:#f8e3ab;text-shadow:0 0 5px #ffe19a}\r\n#coachDock.coach-dock.ship-control-deck .dock-sound .cb-label{color:#c5d5cb}\r\n\r\n/* Keep seven physical controls on one rail before and after scene loading. */\r\n#coachDock.coach-dock.ship-control-deck{display:flex;justify-content:space-between;align-items:center;gap:3px}\r\n#coachDock.coach-dock.ship-control-deck .dock-control,#coachDock.coach-dock.ship-control-deck .dock-sound{flex:0 0 40px;min-width:0;width:40px}\r\n#coachDock.coach-dock.ship-control-deck .dock-portal{position:relative!important;left:auto!important;top:auto!important;translate:none!important;flex:0 0 56px;width:56px;min-width:56px}\r\n#coachDock.ship-control-deck .dock-settings{order:1}#coachDock.ship-control-deck .dock-food{order:2}#coachDock.ship-control-deck .dock-scoreboard{order:3}#coachDock.ship-control-deck .dock-portal{order:4}#coachDock.ship-control-deck .dock-achievements{order:5}#coachDock.ship-control-deck .dock-pod{order:6}#coachDock.ship-control-deck .dock-sound{order:7}\r\n#coachDock.ship-control-deck .dock-live{position:absolute}\r\n\r\n#homeScreen .deck-strip{justify-content:center!important}\r\nbody>#podPersistentChrome .pod-now-playing{width:112px;min-width:112px;max-width:112px}\r\n/* Terminals are attached to their housing, so they inherit its movement. */\r\n.pod-persistent-chrome[hidden]{display:none!important}\r\n.ship-header{position:relative}\r\n.ship-header>#podPersistentChrome{position:absolute;top:calc(100% + 8px);left:0;right:0;z-index:5;justify-content:flex-end}\r\n/* Returning from a modal mounts the terminal below this plate, over the deck strip. */\r\n.ship-header:has(>#podPersistentChrome:not([hidden])){z-index:13}\r\n.portal-frame>.pod-persistent-chrome{position:absolute;top:-22px;left:10px;right:10px;z-index:5;height:44px}\r\n.portal-frame>.pod-persistent-chrome>button{min-height:44px;font-size:9px}\r\n#portalHome>#podPersistentChrome{position:absolute;top:calc(var(--portal-top-band,52px) - 22px);left:calc(var(--portal-rail,15px) + 10px);right:calc(var(--portal-rail,15px) + 10px);z-index:10;height:44px}\r\n#portalHome>#podPersistentChrome>button{flex:0 0 auto;min-width:100px;min-height:44px}\r\n#coachDock.coach-dock.ship-control-deck button.dock-portal{border-color:color-mix(in srgb,var(--portal-metal,#4d3d4f),#fff 40%);background:radial-gradient(circle at 50% 40%,var(--portal-metal,#4d3d4f) 0 46%,color-mix(in srgb,var(--portal-metal,#4d3d4f),#000 65%) 47% 58%,transparent 59%),repeating-conic-gradient(color-mix(in srgb,var(--portal-metal,#4d3d4f),#fff 40%) 0deg 8deg,color-mix(in srgb,var(--portal-metal,#4d3d4f),#000 30%) 8deg 15deg)}\r\n/* R3-01: on the pod route the DJ terminal sits in the cockpit service bay (mount position only). */\r\n.cockpit-service-host>#podPersistentChrome{position:static;inset:auto;flex:1 0 100%;height:auto;z-index:auto;justify-content:flex-start}\r\n.cockpit-service-host>#podPersistentChrome>button{max-width:100%}\r\n","pod/pod-places.mjs":"import {STARTER_WONDERS,starterWonderUrl} from '../meditation-backgrounds.mjs';\r\n\r\nexport const POD_PLACE_KEY='myr5-pod-place-v1';\r\nexport const POD_PLACE_INTERVAL=20000;\r\nexport function choosePodPlace(previous,random=Math.random){\r\n const choices=STARTER_WONDERS.filter(id=>id!==previous);\r\n return choices[Math.min(choices.length-1,Math.max(0,Math.floor(random()*choices.length)))];\r\n}\r\nfunction available(url,ImageCtor){\r\n if(!ImageCtor)return Promise.resolve(true);\r\n return new Promise(resolve=>{\r\n  let done=false;\r\n  const finish=ok=>{if(done)return;done=true;clearTimeout(timer);resolve(ok);};\r\n  const image=new ImageCtor(),timer=setTimeout(()=>finish(false),3000);\r\n  image.onload=()=>finish(true);image.onerror=()=>finish(false);image.src=url;\r\n });\r\n}\r\n// Pixel photos belong only to the workout pod, not the shared room or oval ship scene.\r\nexport function mountPodPlaces(view,{documentRef=globalThis.document,windowRef=globalThis.window,storageRef,ImageCtor=globalThis.Image,random=Math.random}={}){\r\n if(!view)return ()=>{};\r\n let storage=storageRef;try{storage??=windowRef.localStorage;}catch{}\r\n let previous='';try{previous=storage?.getItem(POD_PLACE_KEY)||'';}catch{}\r\n let timer=0,generation=0,disposed=false,visible=!globalThis.IntersectionObserver,active=false;\r\n const shown=()=>!documentRef.hidden&&visible&&documentRef.body.dataset.tracking!=='true'&&documentRef.body.dataset.screen!=='rest'&&!documentRef.querySelector('dialog[open]');\r\n const rotate=async()=>{\r\n  const id=choosePodPlace(previous,random),token=++generation,url=starterWonderUrl(id);\r\n  if(!await available(url,ImageCtor)||disposed||token!==generation)return;\r\n  previous=id;view.style.setProperty('--pod-world',`url(\"${url}\")`);view.dataset.podPlace=id;\r\n  try{storage?.setItem(POD_PLACE_KEY,id);}catch{}\r\n };\r\n const schedule=()=>{windowRef.clearTimeout(timer);timer=0;if(active&&!disposed)timer=windowRef.setTimeout(()=>{void rotate();schedule();},POD_PLACE_INTERVAL);};\r\n const sync=()=>{const next=shown();if(next&&!active)void rotate();if(!next&&active)++generation;active=next;schedule();};\r\n const visibility=()=>sync();\r\n const pageShow=()=>sync();\r\n const dispose=()=>{disposed=true;++generation;windowRef.clearTimeout(timer);observer?.disconnect();intersection?.disconnect();documentRef.removeEventListener('visibilitychange',visibility);windowRef.removeEventListener('pageshow',pageShow);windowRef.removeEventListener('pagehide',pageHide);};\r\n const pageHide=event=>{if(event.persisted){active=false;windowRef.clearTimeout(timer);}else dispose();};\r\n const observer=globalThis.MutationObserver?new MutationObserver(sync):null;\r\n observer?.observe(documentRef.body,{subtree:true,attributes:true,attributeFilter:['data-screen','data-tracking','open']});\r\n const intersection=globalThis.IntersectionObserver?new IntersectionObserver(entries=>{visible=entries[0].isIntersecting;sync();}):null;\r\n intersection?.observe(view);\r\n documentRef.addEventListener('visibilitychange',visibility);windowRef.addEventListener('pageshow',pageShow);windowRef.addEventListener('pagehide',pageHide);\r\n sync();return dispose;\r\n}\r\n","pod/pod.css":"/* MOM's purple hardware. Gold readouts. Orange only on interaction. */\r\n.workout-route{margin:14px 0;padding:14px;border:1px solid #90769b55;border-radius:12px;background:#23182de0;font:14px/1.5 system-ui,sans-serif}.workout-route>small{color:#ffd36e;letter-spacing:.1em}.workout-route p{margin:6px 0}.workout-route [data-rounds]{font-size:12px;color:#cbbbd5}.workout-route button{width:100%;margin-top:8px}.workout-route details{margin-top:10px}.workout-route summary{cursor:pointer;min-height:32px}.workout-route ol{list-style:none;padding:0;margin:8px 0}.workout-route li{padding:5px 8px;border-left:2px solid #705082}.workout-route li[aria-current]{border-color:#ffd36e;background:#ffd36e12;color:#ffd36e}.rest-challenge{margin:10px 0}.rest-challenge strong{display:block;color:#ffd36e}\r\n:root{--wall:#171020;--purple:#7a2fc4;--gold:#ffd36e;--cream:#f6eddf;--muted:#bcb0cd;--power:#eacb8b;--font:system-ui,-apple-system,Segoe UI,sans-serif;color-scheme:dark}\r\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#120e19;color:var(--cream);font-family:var(--font)}\r\nbody{background:radial-gradient(ellipse at 50% 25%,#38224d 0%,#171020 60%),#171020;max-width:640px;margin:auto;min-height:100dvh;box-shadow:0 0 120px #0009}\r\n[hidden]{display:none!important}button,select,input{font:inherit}button,a,input,select{-webkit-tap-highlight-color:transparent}button,a{touch-action:manipulation}button,select,.file-label{min-height:44px;border:1px solid #6a537b;border-radius:10px;color:var(--cream);background:linear-gradient(160deg,#3b2b49,#271c32);padding:10px 14px;cursor:pointer}button:disabled{opacity:.4;cursor:default}button:focus-visible,a:focus-visible,select:focus-visible,input:focus-visible{outline:3px solid var(--gold);outline-offset:3px}a{color:var(--gold);text-decoration:none}select{min-width:0}button:active:not(:disabled){border-color:#ff9f4a;box-shadow:0 0 14px #ff8b3833}\r\n.ship-header{height:72px;display:flex;align-items:center;gap:14px;padding:12px 20px;border-bottom:1px solid #90769b44;background:linear-gradient(#44324e,#21172c);box-shadow:inset 0 1px #ae95ba44,inset 0 -5px #0b07123d;position:relative}\r\n.mom-brand{display:flex;align-items:center;gap:8px;color:#e9d3b1;font-weight:950;font-size:17px;line-height:.9;letter-spacing:-.6px}.mom-brand img{width:35px;height:36px;object-fit:contain}.mom-brand>span>span{display:block;font-size:10px;letter-spacing:5px;margin-top:5px}.ship-location{margin-left:auto;font:9px ui-monospace,monospace;letter-spacing:1px;color:#d6bddf}.ship-location small{display:block;font-size:8px;color:#9d8aaf;margin-top:6px;letter-spacing:1.2px}.icon-button{min-width:44px;font-size:22px;padding:6px;background:#24192e;box-shadow:inset 0 1px #ffffff13}\r\n.pod-page{padding:0 18px 8px}.deck-strip{display:flex;justify-content:space-between;align-items:center;height:36px;font:9px ui-monospace,monospace;letter-spacing:1.4px;color:#b8a6c8}.status-light,.voice-led{display:inline-block;width:5px;height:5px;background:#d7aaf5;box-shadow:0 0 9px #d7aaf5;border-radius:50%;margin-right:8px}\r\n.containment-pod{height:clamp(275px,43dvh,460px);position:relative;border:7px solid #49345c;border-radius:70px 70px 38px 38px;background:radial-gradient(ellipse at 50% 65%,#62308a50,transparent 60%),repeating-linear-gradient(0deg,#c4a2f104 0 1px,transparent 1px 4px),linear-gradient(120deg,#3e2b5066,#120e1ddd 28%,#171021 75%,#57376166);box-shadow:inset 0 0 0 2px #9a79ac77,inset 0 0 30px #0009,0 0 0 2px #100919,0 0 0 4px #574465,0 6px 18px #0006;isolation:isolate;overflow:hidden}\r\n.containment-pod:before{content:'';position:absolute;inset:8px;border:1px solid #b88de755;border-radius:56px 56px 28px 28px;pointer-events:none;z-index:4;background:linear-gradient(110deg,transparent 12%,#dfcdff0b 16%,transparent 25%)}\r\n.pod-rim{position:absolute;top:46px;bottom:24px;width:13px;border:1px solid #8d6ba266;border-radius:8px;background:linear-gradient(90deg,#191022,#6e4b84,#271931);z-index:5;box-shadow:0 0 12px #bb61ff33}.rim-left{left:7px}.rim-right{right:7px}.pod-rim:before{content:'';position:absolute;inset:12px 4px;background:repeating-linear-gradient(0deg,#debcff 0 4px,transparent 4px 23px);border-radius:5px;filter:drop-shadow(0 0 3px #d087ff)}\r\n.pod-inscription{position:absolute;top:17px;left:0;right:0;text-align:center;color:#b28fc1;font:9px ui-monospace,monospace;letter-spacing:5px;z-index:3}.coach-mount{position:absolute;inset:26px 18px 39px;z-index:2}#coachLoading{position:absolute;top:40%;left:0;right:0;text-align:center;font-size:12px;color:#aa92be}\r\n.pod-floor{position:absolute;bottom:2%;left:5%;right:5%;height:25%;border-radius:50%;transform:perspective(130px) rotateX(28deg);background:radial-gradient(ellipse,#9365bd30 20%,transparent 60%);border:1px solid #d7b8ff22;z-index:0}\r\n.pod-nameplate{position:absolute;bottom:0;left:32px;right:32px;display:flex;justify-content:space-between;align-items:center;height:34px;padding:0 14px;background:linear-gradient(#493253,#25172f);border:1px solid #a1849166;border-radius:8px 8px 0 0;font:8px ui-monospace,monospace;letter-spacing:1.2px;z-index:6}.pod-nameplate a{font:10px var(--font);letter-spacing:0}\r\n.myr5-companion-card{position:relative!important;inset:auto!important;width:100%!important;height:100%!important;border:0!important;border-radius:0!important;background:none!important;backdrop-filter:none!important;overflow:visible!important;box-shadow:none!important;pointer-events:none}\r\n.myr5-companion-stage{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;background:none!important}.myr5-companion-status,.myr5-customize,.myr5-hide{display:none!important}.myr5-companion-stage canvas{position:static!important;width:100%!important;height:100%!important;pointer-events:none!important}\r\n.camera-preview{position:absolute;bottom:48px;right:23px;width:84px;height:108px;border-radius:13px;overflow:hidden;background:#191222;box-shadow:0 3px 14px #0006;border:1px solid #b08bc577;z-index:6;display:none}body[data-tracking=true] .camera-preview{display:block}.camera-preview video,.camera-preview canvas{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}.camera-preview canvas{pointer-events:none}.camera-preview span{position:absolute;bottom:0;left:0;right:0;background:#17101bd9;text-align:center;padding:4px;color:#e6d4f3;font:7px ui-monospace,monospace}\r\n.coach-line{display:flex;align-items:center;justify-content:center;min-height:52px;padding:10px 8px;gap:2px}.coach-line p{margin:0;font-size:12px;line-height:1.45;color:#d6c3e4;text-align:center;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.voice-led{flex:none;width:4px;height:4px}\r\n.workout-console{padding:14px 16px 11px;background:linear-gradient(160deg,#493150,#30213a 55%,#23192d);border:1px solid #93789566;border-radius:20px;box-shadow:inset 0 1px #e1c7e12b,inset 0 -5px #09071144,0 7px 16px #0004}\r\n.set-heading{display:grid;grid-template-columns:1fr auto;align-items:center;gap:4px 10px}.set-heading label{grid-column:1/-1;font:8px ui-monospace,monospace;letter-spacing:1.3px;color:#bba4c7}.set-heading select{border:0;background:transparent;font-weight:650;font-size:20px;letter-spacing:-.6px;padding:4px 0;min-height:36px}.set-heading button{background:transparent;border:0;color:#e0c49c;font-size:11px;padding:5px;min-height:36px}\r\n.simple-counter{display:flex;align-items:center;justify-content:space-between;gap:15px;padding-top:8px;position:static!important;max-width:none!important;background:none!important}.simple-counter small{display:block;font:8px ui-monospace,monospace;letter-spacing:1.4px;color:#b19abf}.counter-number{display:flex;gap:9px;align-items:baseline;font-variant-numeric:tabular-nums;line-height:1.13}#primary{font-size:58px;font-weight:700;letter-spacing:-3px;color:var(--gold)}.goal-divider{font-size:24px;color:#8b739b;font-weight:200}#goalValue{font-size:29px;color:#ad94bd;letter-spacing:-1px}#primaryLabel{font-size:10px;color:#d4bfdb}.set-actions{display:flex;flex-direction:column;gap:4px;min-width:120px}.primary-action{display:flex;align-items:center;justify-content:center;gap:15px;border:1px solid #fff0bb88;background:linear-gradient(155deg,#ffe1a0,#d4ae64);color:#281b25;font-weight:750;font-size:13px;box-shadow:inset 0 1px #fff8,0 3px 0 #755237;min-height:48px;border-radius:10px;padding:10px 16px}.set-actions #stop{min-height:30px;padding:5px;background:none;border:0;color:#c7b0d4;font-size:11px}.set-track{height:3px;background:#190e22;border-radius:3px;margin-top:13px;overflow:hidden}#activity{height:100%;width:0;background:var(--gold);transition:width .2s}#status{font-size:10px;color:#bca6c9;line-height:1.4;margin:8px 0 0;min-height:14px}\r\n.crew-footer{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-top:9px}.crew-footer button{display:flex;gap:5px;align-items:center;text-align:left;min-height:44px;background:none;border:0;padding:0;font-size:10px;color:#cbb6da}.crew-footer button span:first-of-type{max-width:155px}.crew-footer>span{font:7px ui-monospace,monospace;letter-spacing:.7px;color:#90789e}.crew-footer canvas{position:static!important;width:24px;height:36px;image-rendering:pixelated}\r\n.pod-dialog{width:min(440px,calc(100vw - 24px));max-height:calc(100dvh - 24px);padding:22px;border:1px solid #a387b5;border-radius:23px;color:var(--cream);background:linear-gradient(140deg,#392640,#1c1426 65%);box-shadow:0 25px 80px #0009}.pod-dialog::backdrop{background:#100b19d9;backdrop-filter:blur(8px)}.pod-dialog header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:18px}.pod-dialog h2{font-size:21px;letter-spacing:-.6px;margin:0}.pod-dialog p{font-size:12px;line-height:1.5;color:#c8b4d4}.settings-fields{display:grid;grid-template-columns:1fr 1fr;gap:16px 10px}.settings-fields label{display:flex;flex-direction:column;gap:6px;font-size:11px;color:#c7b0d3}.settings-fields select{width:100%;font-size:12px;padding:9px}.voice-row{display:flex;flex-wrap:wrap;gap:8px;margin:18px 0 14px}.voice-row span{width:100%;font-size:10px;color:#b9a2c6}.voice-row button{flex:1;font-size:12px}.settings-actions{display:flex;gap:8px}.settings-actions button{flex:1;font-size:11px}.pod-dialog details{border-top:1px solid #70578188;margin-top:18px;padding-top:15px;color:#bfa5d1;font-size:12px}.pod-dialog summary{cursor:pointer;min-height:35px}.pod-dialog pre{font-size:9px;white-space:pre-wrap;overflow-wrap:anywhere}#roundControl{display:none!important}#identity{text-align:center}#identityAvatar{position:static;width:96px;height:144px;image-rendering:pixelated}#identityName{font-size:17px;color:var(--gold)}.file-label{position:relative;cursor:pointer}.file-label input{position:absolute;inset:0;opacity:0;width:100%;cursor:pointer}.pod-dialog>a{display:block;padding:16px;font-size:13px}#identityStatus{font-size:10px}\r\n.rest-page{padding:20px 22px 14px;min-height:calc(100dvh - 72px);display:flex;flex-direction:column;background:radial-gradient(ellipse at 50% 45%,#52247855,transparent 60%)}.rest-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.eyebrow{font:8px ui-monospace,monospace;letter-spacing:2px;color:#c59cda;margin:0 0 8px}.rest-heading h1{font-size:27px;letter-spacing:-1px;line-height:1.1;margin:0;font-weight:600;outline:none}.rest-receipt{display:flex;align-items:center;justify-content:space-between;gap:15px;font-size:10px;color:#ad93bf;padding:15px 0 0}#earnedXp{color:var(--gold);font:10px ui-monospace,monospace;white-space:nowrap}\r\n.encounter{position:relative;flex:1;min-height:360px;height:48dvh;max-height:600px;isolation:isolate;overflow:hidden;margin:8px -12px 0}.encounter-orbit{position:absolute;width:75%;height:55%;border:1px solid #9870b633;left:12.5%;top:16%;border-radius:50%;box-shadow:0 0 80px #853eda15}.encounter-orbit:after{content:'';position:absolute;inset:12px;border:1px dashed #a184b422;border-radius:50%}.encounter-grid{position:absolute;left:-30%;right:-30%;bottom:-5%;height:45%;transform:perspective(150px) rotateX(55deg);background:linear-gradient(#b184d022 1px,transparent 1px),linear-gradient(90deg,#b184d022 1px,transparent 1px);background-size:36px 36px;mask-image:linear-gradient(transparent,black)}\r\n.boss-status{position:absolute;top:11px;left:24px;right:24px;display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px;font:8px ui-monospace,monospace;letter-spacing:1px;color:#c8b0d8;z-index:5}.boss-health{width:100%;height:3px;background:#1b1224}.boss-health i{display:block;width:100%;height:100%;background:linear-gradient(90deg,#7c3ca5,var(--power));box-shadow:0 0 5px #bb80e566;transition:width .25s}.rest-coach-mount{position:absolute;top:3%;left:-8%;width:116%;height:81%;z-index:2}.rest-coach-mount .myr5-companion-stage{filter:drop-shadow(0 0 14px color-mix(in srgb,var(--power) 14%,transparent))}\r\n.attack-target{position:absolute;inset:11% 8% 28%;background:none!important;border:0!important;box-shadow:none!important;z-index:6;border-radius:45%;touch-action:manipulation}.attack-target span{position:absolute;bottom:3px;left:0;right:0;text-align:center;font:8px ui-monospace,monospace;letter-spacing:2px;color:#d0afd7;opacity:.8}.attack-target:focus-visible{outline:2px solid var(--gold)}\r\n.player-platform{position:absolute;bottom:7%;left:50%;width:166px;height:45px;transform:translateX(-50%);z-index:7;pointer-events:none}.platform-top{position:absolute;inset:0;border-radius:50%;background:repeating-radial-gradient(ellipse,#5b466a 0 10px,#6e547e 11px 12px);border:2px solid #d0aec6;box-shadow:0 -4px 22px #d6a3ff11,inset 0 2px #f4d9ff66;z-index:2}.platform-edge{position:absolute;inset:4px 0 -10px;border-radius:50%;background:linear-gradient(#6b4b7e,#20132c);border:2px solid #8b638f;box-shadow:0 4px 0 #b083d7,0 8px 26px #a661e544}.platform-thruster{position:absolute;top:40px;left:45px;right:45px;height:35px;border-radius:0 0 50% 50%;background:linear-gradient(#d8a3ff88,transparent);filter:blur(5px)}#restAvatar{position:absolute;width:90px;height:135px;bottom:12px;left:38px;z-index:3;image-rendering:pixelated;transform-origin:50% 100%;pointer-events:none}.player-name{position:absolute;bottom:0;left:0;right:0;display:flex;gap:10px;justify-content:center;align-items:center;color:#ceb6db;font-size:10px;z-index:9}#restLevel{color:var(--gold);font:9px ui-monospace,monospace}\r\n.attack-flash{position:absolute;left:34%;top:36%;width:32%;height:25%;border:2px solid var(--power);border-radius:50%;opacity:0;z-index:5;pointer-events:none;box-shadow:0 0 16px color-mix(in srgb,var(--power) 30%,transparent)}.projectile{position:absolute;left:calc(50% - 3px);bottom:23%;width:5px;height:20px;border-radius:50%;background:#ffc478;box-shadow:0 0 9px #ffaa57;opacity:0;z-index:8;pointer-events:none}.damage-float{position:absolute;top:38%;left:0;right:0;text-align:center;color:var(--power);font:700 17px ui-monospace,monospace;z-index:9;opacity:0;pointer-events:none}.encounter.hit .attack-flash{animation:shield-hit .45s ease-out}.encounter.hit .projectile{animation:attack .28s ease-in}.encounter.hit .damage-float{animation:damage .65s ease-out}.encounter.hit #restAvatar{animation:player-hit .28s ease-out}\r\n.rest-feedback{text-align:center;min-height:28px;color:#c4accf;font-size:11px;line-height:1.5;margin:18px 0 5px}.rest-bottom{display:flex;align-items:center;justify-content:space-between;gap:15px;border-top:1px solid #87659255;padding-top:14px}.rest-clock small{display:block;font:8px ui-monospace,monospace;letter-spacing:1.5px;color:#b596c6}.rest-clock strong{display:block;color:var(--gold);font:500 37px var(--font);font-variant-numeric:tabular-nums;letter-spacing:-1.5px}.rest-bottom>div:last-child{min-width:155px}.rest-bottom .primary-action{width:100%}.text-button{display:block;background:none;border:0;box-shadow:none!important;font-size:10px;color:#c2a5d0;min-height:35px;padding:8px}.shield-note{text-align:center;color:#91789f;font:9px var(--font);line-height:1.4;margin:10px 0 0}\r\nbody[data-power=ember]{--power:#ff9d66}body[data-power=arc]{--power:#a4caff}body[data-power=frost]{--power:#b9eadf}\r\n@keyframes shield-hit{0%{opacity:.8;transform:scale(.65)}100%{opacity:0;transform:scale(1.3)}}@keyframes attack{0%{opacity:1;transform:translateY(0)}100%{opacity:.3;transform:translateY(-155px)}}@keyframes damage{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-28px)}}@keyframes player-hit{40%{transform:translateY(-9px) rotate(-6deg)}}\r\n#library{background:radial-gradient(ellipse at 20% 0%,#67437988,transparent 55%),#171020f7}#library .library-header small{color:#dbc095}#library .movement-card{background:linear-gradient(135deg,#60407244,#24152f99);border-color:#a783bd55}#library .holo-link{color:#e7c79a!important}#library .use-movement{background:#654978bb!important;border-color:#c0a0d388!important}#library #holoStage{background-color:#1a1026}#library button{border-color:#b493c455}\r\n@media(min-width:700px){body{border-left:1px solid #a57aad33;border-right:1px solid #a57aad33}.pod-page{padding:0 34px 10px}.ship-header{padding:12px 32px}.containment-pod{height:45dvh;min-height:310px}.rest-page{padding:24px 38px 18px}.encounter{min-height:390px}.crew-footer>span{font-size:8px}}\r\n@media(max-width:370px){.ship-header{padding:10px 14px;gap:8px}.ship-location{font-size:8px;letter-spacing:.3px}.pod-page{padding:0 13px 7px}.workout-console{padding:12px}.set-actions{min-width:108px}.set-heading select{font-size:18px}.rest-page{padding:17px}.rest-heading h1{font-size:24px}.counter-number{gap:6px}#primary{font-size:52px}}\r\n@media(max-height:720px) and (orientation:portrait){.ship-header{height:60px}.containment-pod{height:36dvh;min-height:235px}.coach-line{min-height:44px}.rest-page{min-height:calc(100dvh - 60px)}.encounter{min-height:290px}.rest-heading h1{font-size:23px}.rest-feedback{margin-top:10px}}\r\n@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important}.encounter.hit .damage-float{opacity:1}.encounter.hit .attack-flash{opacity:.3}}\r\n#restCoachLoading{position:absolute;top:40%;left:10%;right:10%;text-align:center;font-size:12px;color:#c9add8;z-index:3}\r\n.set-actions .primary-action{gap:8px;padding:10px 11px;white-space:nowrap}\r\n.coach-line #coachCaption{color:#e8d5b3}\r\nbody:has(#library[open]) .myr5-companion-card{display:none!important}\r\n/* W2-2M #117: the pod is only the viewing port (your character over the pixel world), the control board and BEGIN,\r\n   sized so BEGIN sits above the bar without scrolling (the square's #workout start page scrolls to the top).\r\n   The viewing port takes whatever height is left; ids beat the optional hardware skins' .containment-pod heights. */\r\n#homeScreen{display:flex;flex-direction:column;min-height:calc(100dvh - 72px - var(--myr5-bar) - 12px)}\r\n#homeScreen>.training-view{flex:1 1 0;display:flex;flex-direction:column}\r\n#homeScreen .containment-bay{flex:1;display:flex;min-height:0}\r\n#homeScreen #view{flex:1;height:auto;min-height:150px;background:radial-gradient(ellipse at 50% 45%,transparent 58%,#120c1ccc),var(--pod-world,var(--room-world)) center 86%/cover no-repeat;background-position:center 45%,center bottom;background-size:auto,auto 125%;image-rendering:pixelated;container-type:size}\r\n#homeScreen #view #homeCharacter{inset:0}\r\n#view .pod-floor{display:none}\r\n/* The coach is parked here between scenes (its reveal is the oval's ship intro); the pre-set film still shows it. */\r\nbody:not([data-cinematic=pre]) #view .coach-mount,body[data-cinematic=pre] #view :is(.home-character,.coach-line){display:none}\r\n/* Pod header actions: the player's name (opens the avatar) and Customize coach, as plain text links. */\r\n.pod-header-actions{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:8px 2px 2px}\r\n.pod-header-actions #openIdentity,.pod-header-actions a{min-height:44px;display:flex;align-items:center;padding:0 4px;border:0;border-radius:6px;background:none;box-shadow:none;color:var(--gold);font:600 13px var(--font);text-decoration:underline;text-underline-offset:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60%}\r\n.pod-header-actions #miniAvatar{display:none}\r\n/* Continue popup for today's unfinished set: on the pod, above the bottom bar. */\r\n.continue-workout{position:fixed;z-index:30;left:16px;right:16px;bottom:calc(var(--myr5-bar,0px) + 16px + env(safe-area-inset-bottom,0px));max-width:420px;margin:0 auto;padding:14px 16px;border:2px solid var(--gold);border-radius:14px;background:#21172ff2;box-shadow:0 10px 30px #000a}\r\n.continue-workout .continue-eyebrow{margin:0;color:var(--gold);font:800 11px ui-monospace,monospace;letter-spacing:.16em}.continue-workout h2{margin:4px 0 10px;font:800 19px/1.2 var(--font)}\r\n.continue-workout .continue-actions{display:flex;gap:10px}.continue-workout .continue-actions button{flex:1}\r\n/* Settings open folded: each group is a one-line summary until tapped. */\r\n#settings .settings-group>summary{min-height:44px;display:flex;align-items:center;cursor:pointer;font-weight:700}\r\n#settings .settings-group>summary::before{content:'▸';margin-right:8px}#settings .settings-group[open]>summary::before{content:'▾'}\r\n/* The coach's voice line is a radio band across the top of the viewing port. */\r\n#view .coach-line{position:absolute;z-index:5;top:32px;left:30px;right:30px;min-height:0;margin:0;padding:5px 10px;border:1px solid #b88de755;border-radius:8px;background:#140e1fd9;box-shadow:none}\r\n@container (max-height:220px){#view .coach-line{top:8px}#view .coach-line p{-webkit-line-clamp:1}#view .home-character{top:44px}#view .camera-preview{bottom:40px;width:60px;height:62px}}\r\n#controls>p:not([id]){margin:0 2px 8px;font-size:11px;line-height:1.35;color:#bca6c9}\r\n#controls .set-actions{flex-direction:row;align-items:stretch;gap:8px}#controls #start{flex:1;min-height:66px}#controls #stop{flex:none;min-width:64px}\r\n/* Goals: the daily circuit and the next-challenge card as a compact drop-down in the deck strip, over the viewing port. */\r\n.deck-strip{position:relative;z-index:12;height:44px;gap:8px}\r\n.pod-goals>summary{display:flex;align-items:center;gap:7px;min-height:36px;padding:0 12px;border:1px solid #6a537b;border-radius:8px;background:linear-gradient(#3b2b49,#271c32);box-shadow:inset 0 1px #ffffff14,0 2px 0 #0b0712;list-style:none;cursor:pointer;color:var(--gold);font:700 10px/1 ui-monospace,monospace;letter-spacing:.16em}\r\n.pod-goals>summary::-webkit-details-marker{display:none}\r\n.pod-goals>summary:after{content:'▾';font-size:11px;letter-spacing:0}.pod-goals[open]>summary:after{content:'▴'}\r\n.pod-goals>summary:focus-visible{outline:3px solid var(--gold);outline-offset:2px}\r\n.pod-goals [data-goals-status]{color:var(--cream);letter-spacing:.05em}\r\n.pod-goals-menu{position:absolute;left:0;right:0;top:calc(100% + 2px);max-height:calc(100dvh - 140px - var(--myr5-bar));overflow:auto;overscroll-behavior:contain;padding:12px 14px;border:1px solid #a387b5;border-radius:14px;background:linear-gradient(160deg,#3a2742,#1d1426);box-shadow:0 14px 34px #000c;font:14px/1.45 var(--font);letter-spacing:0;color:var(--cream)}\r\n.pod-goals-menu .daily-circuit>small{color:var(--gold);letter-spacing:.1em}.pod-goals-menu .daily-circuit meter{width:100%}.pod-goals-menu .daily-circuit p{margin:6px 0}.pod-goals-menu .daily-circuit ol{margin:6px 0;padding-left:22px}.pod-goals-menu .daily-circuit button{width:100%}\r\n.pod-goals-menu .workout-route{margin:12px 0 0}\r\n/* The pod's extras ride along to Records (coach-hub.mjs); Meditation there is the #meditate route's opener only. */\r\n.pod-extras .crew-footer{display:none}.pod-extras .meditation-entry{display:none}\r\n@media(max-height:720px) and (orientation:portrait){#homeScreen{min-height:calc(100dvh - 60px - var(--myr5-bar) - 12px)}}\r\n/* The one-time \"Updated\" notice sits above the bar, which on the pod is exactly where BEGIN is: off the quilt it sits\r\n   over the viewing port instead, clear of BEGIN below and of the downloads bar (.full-download-bar) at the top. */\r\nbody:not(:has(#portalHome:not([hidden]))):not([data-screen=rest]) .app-update-banner{top:calc(128px + env(safe-area-inset-top));bottom:auto}\r\n/* FULL WORKOUT circuit dropdown + runner overlay */\r\n#podFullWorkout{position:static}.fw-menu .fw-bar,.fw-menu .fw-style-bar{display:flex;gap:8px;margin:0 0 8px}.fw-menu button{min-height:34px;padding:0 12px;border:1px solid #6a537b;border-radius:8px;background:#3b2b49;color:var(--cream);font:700 12px/1 var(--font)}\r\n.fw-menu .fw-bar button{flex:1}.fw-menu .fw-count{margin:0 0 8px;font-size:12px;color:var(--gold)}\r\n.fw-style{margin:4px 0;border:1px solid #4a3858;border-radius:10px;padding:0 10px}.fw-style>summary{min-height:38px;display:flex;align-items:center;justify-content:space-between;cursor:pointer;font-weight:700;list-style:none}.fw-style>summary::-webkit-details-marker{display:none}.fw-style>summary span{font-size:11px;color:var(--gold)}\r\n.fw-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-height:36px}.fw-row input{width:20px;height:20px;accent-color:var(--gold)}.fw-row small{flex-basis:100%;margin:-4px 0 6px 28px;font-size:11px;color:#bca6c9}.fw-locked{opacity:.5}\r\n.fw-menu .fw-start{width:100%;min-height:48px;margin-top:10px;background:linear-gradient(#e0b04a,#a87a1f);color:#1b1022;border-color:#f5d27a;font-size:14px;letter-spacing:.08em}.fw-menu .fw-start:disabled{opacity:.4}\r\n#fullWorkoutHud{position:fixed;z-index:2147483000;left:0;right:0;top:0;font:14px/1.4 var(--font);color:var(--cream)}\r\n#fullWorkoutHud[data-mode=exercise]{pointer-events:none}#fullWorkoutHud .fw-top{pointer-events:auto;display:flex;align-items:center;gap:10px;margin:6px;padding:6px 10px;border:1px solid #a387b5;border-radius:10px;background:#1d1426e6}.fw-top span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fw-top button{min-height:32px;padding:0 12px;border-radius:8px;border:1px solid #6a537b;background:#3b2b49;color:var(--cream)}\r\n#fullWorkoutHud[data-mode=break],#fullWorkoutHud[data-mode=summary],#fullWorkoutHud[data-mode=paused]{bottom:0;display:grid;place-items:center;background:#0b0712f2}\r\n.fw-panel{width:min(92vw,420px);max-height:92dvh;overflow:auto;text-align:center}.fw-panel h2{margin:0 0 6px}.fw-panel button{min-height:44px;margin:6px 4px;padding:0 18px;border:1px solid #a387b5;border-radius:10px;background:#3b2b49;color:var(--cream);font-weight:700}\r\n.fw-eyebrow{margin:0;color:var(--gold);letter-spacing:.2em;font:700 12px ui-monospace,monospace}.fw-clock{font:700 72px/1.1 ui-monospace,monospace;color:var(--gold)}.fw-big{font-size:22px;font-weight:700;margin:2px 0}.fw-sub{margin:0 0 10px;color:#bca6c9}\r\n.fw-panel ol{text-align:left;padding-left:24px}.fw-panel li span{float:right;color:var(--gold)}\r\n/* FULL WORKOUT switchboard: one brushed-metal plate, bat-handle toggles (same metal as .cb-switch), engraved plates, amber lamps */\r\n.fw-menu{contain:paint;isolation:isolate;background:repeating-linear-gradient(90deg,#ffffff0a 0 1px,#0000 1px 3px),linear-gradient(#5a4b5a,#34293b 40%,#2a2131);border:2px solid #14101a;box-shadow:inset 0 1px #d4bdca77,inset 0 -3px #0007,0 14px 34px #000c;color:#eadde6;padding-top:14px}\r\n.fw-menu .fw-bar{position:relative;padding:0 16px}.fw-screw{position:absolute;top:50%;width:9px;height:9px;margin-top:-4px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#dccbd4,#6e5f6c 60%,#382d3a);box-shadow:0 1px 2px #000a;pointer-events:none}.fw-screw:before{content:'';position:absolute;left:1px;right:1px;top:4px;height:1px;background:#2a2030;transform:rotate(35deg)}.fw-screw.l{left:0}.fw-screw.r{right:0}\r\n.fw-menu button:not(.fw-start):not(.fw-plug):not(.fw-hub){min-height:44px;border:1px solid #0d0a10;border-radius:7px;background:linear-gradient(#524352,#30273a);box-shadow:inset 0 1px #d4bdca66,inset 0 -2px #0006,0 3px 0 #140f19;color:#d9c9d3;letter-spacing:.12em;text-transform:uppercase;font-size:11px;transition:transform .07s}.fw-menu button:not(.fw-start):not(.fw-plug):not(.fw-hub):active{transform:translateY(2px);box-shadow:inset 0 1px #d4bdca44,0 1px 0 #140f19}\r\n.fw-menu .fw-count{text-align:center;font:700 11px ui-monospace,monospace;letter-spacing:.12em;text-transform:uppercase;color:var(--gold)}\r\n.fw-style{border:1px solid #14101a;background:linear-gradient(#3b2f41,#2b2232);box-shadow:inset 0 1px #d4bdca33,0 2px 0 #140f19}.fw-style>summary{min-height:48px}\r\n.fw-plate{padding:4px 10px;border:1px solid #0d0a10;border-radius:4px;background:linear-gradient(#b9a9b3,#7d6e7a);color:#2a2030;text-shadow:0 1px #ffffff55;font:800 12px/1 ui-monospace,monospace;letter-spacing:.14em;text-transform:uppercase;box-shadow:inset 0 1px #fff8}\r\n.fw-row{position:relative;min-height:48px;padding:0 4px;border-top:1px solid #ffffff10;gap:10px;cursor:pointer}.fw-row input{position:absolute;opacity:0;inset:0;width:100%;height:100%;margin:0;cursor:pointer}.fw-row input:disabled{cursor:not-allowed}\r\n.fw-name{flex:1;min-width:0}\r\n.fw-lamp{flex:none;width:10px;height:10px;border-radius:50%;background:radial-gradient(circle at 35% 30%,#6d5a50,#2a1d17);border:1px solid #0d0a10;box-shadow:inset 0 1px #fff3}.fw-row:has(input:checked) .fw-lamp{background:radial-gradient(circle at 35% 30%,#fff2b8,#ffb42e 55%,#a85a00);box-shadow:0 0 8px #ffb42ecc,inset 0 1px #fff8}\r\n.fw-tog{position:relative;flex:none;order:3;width:44px;height:48px;border:1px solid #0d0a10;border-radius:7px;background:linear-gradient(#524352,#30273a);box-shadow:inset 0 1px #d4bdca66,inset 0 -2px #0006,0 3px 0 #140f19}\r\n.fw-tog:before{content:'';position:absolute;left:calc(50% - 4px);top:calc(50% - 21px);width:8px;height:21px;border-radius:4px 4px 2px 2px;background:linear-gradient(90deg,#6b5d69,#f4ebf0 45%,#857583);box-shadow:2px 2px 3px #0009;transform-origin:50% 100%;transform:translateY(0) scaleY(-1);transition:transform .14s cubic-bezier(.3,1.5,.5,1)}\r\n.fw-tog:after{content:'';position:absolute;left:calc(50% - 7px);top:calc(50% - 7px);width:14px;height:14px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#dccbd4,#6e5f6c 60%,#382d3a);box-shadow:0 2px 3px #000a}\r\n.fw-row input:checked~.fw-tog:before{transform:scaleY(1)}.fw-row input:focus-visible~.fw-tog{outline:3px solid var(--gold);outline-offset:2px}\r\n.fw-locked .fw-tog{filter:brightness(.55) saturate(.5)}.fw-guard{position:absolute;inset:-1px;z-index:2;display:grid;place-items:center;border:1px solid #0d0a10;border-radius:7px;background:repeating-linear-gradient(45deg,#8a7a28 0 5px,#2a2412 5px 10px);color:#ffe9a0;font:800 8px/1 ui-monospace,monospace;letter-spacing:.1em;text-shadow:0 1px #000}\r\n.fw-locked small{color:#c9b0bd}.fw-menu .fw-style-bar{gap:6px}.fw-menu .fw-style-bar button{flex:1}\r\n@media (prefers-reduced-motion:reduce){.fw-tog:before,.fw-menu button{transition:none}}\r\n/* FULL WORKOUT power bus + cables + level ladder */\r\n.fw-body{position:relative}.fw-wires{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;pointer-events:none;z-index:3}\r\n.fw-power{position:relative;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 22px;border:2px solid #14101a;border-radius:10px;background:repeating-linear-gradient(90deg,#ffffff0d 0 1px,#0000 1px 4px),linear-gradient(#6a5a6a,#3a2e42);box-shadow:inset 0 1px #e6d3de88,inset 0 -3px #0008,0 4px 0 #140f19}\r\n.fw-power .fw-screw.l{left:7px}.fw-power .fw-screw.r{right:7px}.fw-power .fw-screw{top:50%}\r\n.fw-hub{flex:none;width:64px;height:64px;margin:0 auto;border-radius:50%!important;border:3px solid #14101a!important;background:radial-gradient(circle at 38% 30%,#e8dbe3,#8c7b88 45%,#3a2e3c)!important;box-shadow:inset 0 2px #fffa,inset 0 -4px #0008,0 4px 0 #140f19!important;color:#2a2030!important;font:700 30px/1 system-ui!important;letter-spacing:0!important;display:grid;place-items:center}\r\n.fw-hub[data-lit=true]{color:#ffb42e!important;text-shadow:0 0 10px #ffb42e;background:radial-gradient(circle at 38% 30%,#4a3a2a,#2a1d17 60%,#150f0b)!important;box-shadow:inset 0 2px #ffd36e55,0 0 18px #ffb42e99,0 4px 0 #140f19!important}\r\n.fw-bay{height:210px}\r\n.fw-pick{flex:1;min-width:0;margin:0 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;font:700 11px ui-monospace,monospace;letter-spacing:.06em;color:var(--gold);text-transform:uppercase}\r\n.fw-socket{flex:none;position:relative;width:30px;height:30px;margin-right:2px;border-radius:50%;border:2px solid #0d0a10;background:radial-gradient(circle,#050308 35%,#4a3d4c 40%,#2c2330);box-shadow:inset 0 2px 4px #000,0 1px #ffffff22}\r\n.fw-style[data-power=on] .fw-socket{box-shadow:inset 0 2px 4px #000,0 0 10px #ffb42e}.fw-spark:after{content:'';position:absolute;inset:-14px;border-radius:50%;background:radial-gradient(circle,#fff8c8,#ffb42e66 40%,#0000 70%);animation:fwspark .5s ease-out both}\r\n@keyframes fwspark{from{transform:scale(.2);opacity:1}to{transform:scale(1.5);opacity:0}}\r\n.fw-style>summary{padding-right:8px}.fw-hint{margin:2px 0 8px;padding:6px 8px;border-radius:6px;background:#0005;color:#e8cf86;font-size:12px}\r\n.fw-plug{position:absolute;left:0;top:0;z-index:5;width:44px;height:44px;padding:0;border:0;background:none;touch-action:none;cursor:grab}\r\n.fw-plug i{position:absolute;left:12px;top:2px;width:20px;height:40px;border-radius:5px 5px 9px 9px;border:1px solid #0d0a10;background:linear-gradient(90deg,#6b5d69,#f4ebf0 45%,#857583);box-shadow:0 3px 5px #000a}\r\n.fw-plug i:before{content:'';position:absolute;left:3px;right:3px;bottom:-9px;height:10px;background:linear-gradient(90deg,#9a8a96,#fff 50%,#9a8a96);border-radius:0 0 3px 3px;box-shadow:inset 0 -3px #0005}\r\n.fw-plug:focus-visible{outline:3px solid var(--gold);outline-offset:-2px;border-radius:10px}.fw-plug[aria-pressed=true] i{background:linear-gradient(90deg,#8a6a2a,#ffe9a0 45%,#a8802f)}\r\n.fw-capped i{background:repeating-linear-gradient(45deg,#8a7a28 0 5px,#2a2412 5px 10px)!important;filter:grayscale(.4)}.fw-capped i:after{content:'LOCK';position:absolute;inset:12px -6px auto;text-align:center;font:800 8px/1 ui-monospace,monospace;color:#ffe9a0;background:#000a;border-radius:3px;padding:2px 0}\r\n.fw-row.fw-dead{opacity:.55}.fw-row.fw-dead .fw-tog{filter:brightness(.6)}\r\n.fw-lv{display:inline-grid;place-items:center;width:20px;height:20px;margin-right:4px;border-radius:50%;background:#0006;color:var(--gold);font:700 11px ui-monospace,monospace}\r\n.fw-tog:before,.fw-lamp{transition-delay:calc(var(--d,0)*55ms)}.fw-lamp{transition:background .15s,box-shadow .15s}\r\n.fw-style:not([data-power=on]) .fw-row:has(input:checked) .fw-lamp{background:radial-gradient(circle at 35% 30%,#6d5a50,#2a1d17);box-shadow:inset 0 1px #fff3}\r\n.fw-style.fw-pulse[data-power=on] .fw-row:has(input:checked) .fw-lamp{animation:fwlamp .45s both;animation-delay:calc(var(--r)*80ms)}\r\n@keyframes fwlamp{from{background:#2a1d17;box-shadow:none;transform:scale(.6)}to{transform:scale(1)}}\r\n@media (prefers-reduced-motion:reduce){.fw-style.fw-pulse .fw-lamp{animation:none!important}.fw-spark:after{animation:none;opacity:.6}.fw-tog:before,.fw-lamp{transition:none;transition-delay:0s}}\r\n\r\n/* FULL WORKOUT: top cable rail, exposed switch banks, protected right-hand loom. */\r\n.fw-body{padding-right:58px}\r\n.fw-power{position:relative;z-index:4;margin-right:-58px;padding:8px 18px 22px;min-height:76px;border-radius:4px}\r\n.fw-pw-l{display:flex;align-items:center;gap:8px;min-width:0}.fw-pw-l .fw-count{margin:0;text-align:left;font-size:10px}\r\n.fw-hub{width:44px;height:44px;min-height:44px;margin:0!important;font-size:24px!important}\r\n.fw-anchors{position:absolute;left:14px;right:14px;bottom:-5px;display:flex;justify-content:space-between;border-top:5px solid #211b25;box-shadow:0 -2px #a89d9e,0 2px 3px #000}\r\n.fw-anchors i{width:12px;height:14px;border:2px solid #0c0b10;border-radius:2px;background:repeating-linear-gradient(#85787d 0 2px,#312b33 2px 4px);box-shadow:1px 2px 2px #000}\r\n.fw-bay{height:208px;margin-right:-58px;border-bottom:2px solid #18141c;background:linear-gradient(#0e0b1344,#0e0b1300)}\r\n.fw-style{position:relative;margin:8px 0;padding:6px;border-radius:4px}\r\n.fw-section-head{height:38px;display:flex;align-items:center;gap:8px}\r\n.fw-section-head .fw-plate{display:grid;place-items:center;padding:2px;width:38px;height:34px}\r\n.fw-section-head .fw-pick{display:flex;justify-content:flex-end;align-items:center;min-height:32px}\r\n.fw-name img,.fw-section-head img{object-fit:contain}\r\n.fw-socket{position:absolute;right:-45px;top:22px;margin:-15px 0 0;width:30px;height:30px;border:3px solid #b5a7a2;box-shadow:0 0 0 3px #18131c,inset 0 2px 5px #000;background:radial-gradient(circle,#08060a 40%,#64575f 45%,#211b28 65%)}\r\n.fw-socket[data-locked=true]{border-color:#81756e}\r\n.fw-switches{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:4px}\r\n.fw-row{display:grid;grid-template-columns:1fr;grid-template-rows:10px 42px 32px;justify-items:center;align-items:center;gap:4px;padding:7px 0 3px;min-height:100px;border-top:1px solid #ffffff17}\r\n.fw-row input{z-index:2}\r\n.fw-lamp{grid-row:1;width:7px;height:7px}\r\n.fw-name{grid-row:3;display:grid;place-items:center;width:32px;height:32px}\r\n.fw-tog{grid-row:2;order:initial;width:36px;height:36px;border-radius:50%;background:radial-gradient(circle,#211a26 22%,#a29498 26%,#d7c7cd 34%,#645761 48%,#1d1723 53%,#423642 60%);box-shadow:0 2px 2px #0009,inset 0 1px #fff5}\r\n.fw-tog:before{top:calc(50% - 18px);height:18px;width:7px;left:calc(50% - 3.5px);border-radius:2px;background:linear-gradient(90deg,#5e5258,#e8e0dd 45%,#897d80);z-index:1}\r\n.fw-tog:after{width:12px;height:12px;left:calc(50% - 6px);top:calc(50% - 6px)}\r\n.fw-guard{inset:8px -1px;border-radius:2px;font-size:10px}\r\n/* The button is only a transparent hit target: the short coupler is the physical draggable object. */\r\n.fw-menu .fw-plug{z-index:8;min-height:44px;width:44px;height:44px;padding:0;border:0!important;border-radius:0;background:transparent!important;box-shadow:none!important}\r\n.fw-plug i{left:11px;top:12px;width:22px;height:22px;border-radius:2px;border:1px solid #17131b;background:linear-gradient(90deg,#514952,#ddd9d6 30%,#8f858b 65%,#e3dcd6 78%,#514952);box-shadow:2px 3px 3px #0009;clip-path:polygon(15% 0,85% 0,100% 25%,100% 80%,85% 100%,15% 100%,0 80%,0 25%)}\r\n.fw-plug i:before{left:3px;right:3px;top:4px;bottom:auto;height:3px;border-radius:0;background:#322b35;box-shadow:0 10px #322b35}\r\n.fw-plug[aria-pressed=true] i{background:linear-gradient(90deg,#635339,#e6d7aa 35%,#8d8062 70%,#dbd2b5)}\r\n.fw-cord path{fill:none;stroke-linecap:round;stroke-linejoin:round}\r\n.fw-c-sh{stroke:#000;stroke-opacity:.45;stroke-width:11;transform:translate(2px,3px)}\r\n.fw-c-jk{stroke:#221a28;stroke-width:7}\r\n.fw-c-hl{stroke:#8d7f8c;stroke-opacity:.8;stroke-width:1.8;transform:translate(-1.6px,-1.6px)}\r\n.fw-c-pl{stroke:#ffc24a;stroke-width:2.4;stroke-dasharray:5 26;opacity:0;filter:drop-shadow(0 0 3px #ffb42e)}\r\n.fw-cord.on .fw-c-jk{stroke:#2e2018}.fw-cord.on .fw-c-hl{stroke:#ffb42e;stroke-opacity:.55}.fw-cord.on .fw-c-pl{opacity:1;animation:fwpulse 1.1s linear infinite}\r\n@keyframes fwpulse{to{stroke-dashoffset:-31}}\r\n@media (prefers-reduced-motion:reduce){.fw-cord.on .fw-c-pl{animation:none;stroke-dasharray:none;opacity:.5}}\r\n.fw-menu,.fw-menu *{-webkit-user-select:none;user-select:none}\r\n\r\n#fullWorkoutHud .fw-workout-symbol{display:inline-flex;vertical-align:middle;align-items:center;justify-content:center}#fullWorkoutHud .fw-workout-symbol img{width:40px;height:40px;object-fit:contain;filter:grayscale(1) contrast(1.5)}\r\n\r\n/* Explicitly retire descendants too: native closed-details clipping can leak transformed layers on iOS. */\r\n#podFullWorkout:not([open]) .fw-menu,#podFullWorkout:not([open]) .fw-menu *{visibility:hidden!important}\r\n#podFullWorkout:not([open]) .fw-menu{display:none}\r\n\r\n/* FULL WORKOUT compact ten-style switchboard: one touch-sized control set per tile. */\r\n.fw-menu .fw-power{min-height:54px;padding:2px 12px 8px;margin-right:-58px;gap:8px}\r\n.fw-menu .fw-power .fw-hub{width:44px;height:44px;min-height:44px;font-size:21px!important}\r\n.fw-menu .fw-power .fw-anchors{left:12px;right:12px;bottom:-5px}\r\n.fw-menu .fw-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px;position:relative;padding-bottom:2px}\r\n.fw-menu .fw-style{position:relative;display:block;min-width:0;height:80px;margin:0;padding:0;border:1px solid #14101a;border-radius:5px;overflow:visible;background:linear-gradient(145deg,#403345,#29212f 72%);box-shadow:inset 0 1px #d4bdca33,0 2px 0 #140f19}\r\n.fw-menu .fw-section-head{position:absolute;inset:0 auto auto 0;width:30px;height:30px;display:grid;place-items:center;z-index:4}\r\n.fw-menu .fw-section-head .fw-socket{position:relative;inset:auto;width:24px;height:24px;margin:0;border-width:2px;box-shadow:inset 0 2px 4px #000,0 1px #ffffff22}\r\n.fw-menu .fw-enable{position:absolute;top:0;right:0;z-index:7;width:44px;height:44px;display:grid;place-items:center;cursor:pointer}\r\n.fw-menu .fw-enable input{position:absolute;inset:0;width:44px;height:44px;margin:0;opacity:0;z-index:3;cursor:pointer;accent-color:var(--gold)}\r\n.fw-menu .fw-enable .fw-lamp{position:absolute;top:3px;left:50%;width:7px;height:7px;transform:translateX(-50%);z-index:2}\r\n.fw-menu .fw-enable .fw-tog{width:32px;height:32px;min-width:32px;min-height:32px;border-radius:50%;background:radial-gradient(circle,#211a26 22%,#a29498 26%,#d7c7cd 34%,#645761 48%,#1d1723 53%,#423642 60%);box-shadow:0 2px 2px #0009,inset 0 1px #fff5}\r\n.fw-menu .fw-enable .fw-tog:before{top:calc(50% - 16px);left:calc(50% - 3px);width:6px;height:16px;border-radius:2px;background:linear-gradient(90deg,#5e5258,#e8e0dd 45%,#897d80);z-index:1}\r\n.fw-menu .fw-enable .fw-tog:after{left:calc(50% - 5px);top:calc(50% - 5px);width:10px;height:10px}\r\n.fw-menu .fw-enable:has(input:checked) .fw-lamp{background:radial-gradient(circle at 35% 30%,#fff2b8,#ffb42e 55%,#a85a00);box-shadow:0 0 8px #ffb42ecc,inset 0 1px #fff8}\r\n.fw-menu .fw-enable:has(input:checked) .fw-tog:before{transform:scaleY(1)}\r\n.fw-menu .fw-enable:has(input:focus-visible) .fw-tog{outline:3px solid var(--gold);outline-offset:2px}\r\n.fw-menu .fw-dial-wrap{position:absolute;left:50%;top:50%;width:44px;height:44px;transform:translate(-50%,-50%);z-index:4;border-radius:50%;background:radial-gradient(circle,#211a26 0 48%,#968890 51% 56%,#302636 59% 67%,#110e16 70%);box-shadow:0 2px 4px #000b,inset 0 1px #fff4}\r\n.fw-menu .fw-dial-wrap:before{content:'';position:absolute;inset:3px;border-radius:50%;background:conic-gradient(from -135deg,#ffca62 0deg var(--angle,0deg),#554858 var(--angle,0deg) 360deg);opacity:.75;pointer-events:none}\r\n.fw-menu .fw-dial-wrap:after{content:'';position:absolute;left:50%;top:50%;width:5px;height:13px;margin-left:-2.5px;margin-top:-18px;border-radius:3px;background:linear-gradient(90deg,#60545d,#f0e6e9 48%,#83767e);box-shadow:0 1px 2px #000;transform:rotate(var(--angle,0deg));transform-origin:50% 18px;pointer-events:none}\r\n.fw-menu .fw-dial{appearance:none;-webkit-appearance:none;position:absolute;inset:0;width:44px;height:44px;margin:0;padding:0;border:0;border-radius:50%;background:transparent;opacity:.015;z-index:2;touch-action:manipulation;cursor:pointer}\r\n.fw-menu .fw-dial::-webkit-slider-runnable-track{height:42px;background:transparent;border:0}\r\n.fw-menu .fw-dial::-webkit-slider-thumb{-webkit-appearance:none;width:42px;height:42px;background:transparent;border:0}\r\n.fw-menu .fw-dial::-moz-range-track{height:42px;background:transparent;border:0}\r\n.fw-menu .fw-dial::-moz-range-thumb{width:42px;height:42px;background:transparent;border:0}\r\n.fw-menu .fw-dial-wrap:has(.fw-dial:focus-visible){outline:3px solid var(--gold);outline-offset:3px}\r\n.fw-menu .fw-holograms{position:absolute;left:50%;top:50%;width:1px;height:1px;z-index:5;pointer-events:none}\r\n.fw-menu .fw-hologram{position:absolute;left:0;top:0;width:18px;height:18px;display:grid;place-items:center;border:1px solid #7f7180;border-radius:50%;background:radial-gradient(circle,#47384e,#1c1622 75%);box-shadow:0 1px 3px #000b;transform:rotate(var(--a,0deg)) translateY(-29px) rotate(calc(-1 * var(--a,0deg)));opacity:.62}\r\n.fw-menu .fw-hologram img{width:13px;height:13px;object-fit:contain}\r\n.fw-menu .fw-hologram[data-level]{font:700 7px/1 ui-monospace,monospace;color:#e7d9ed}\r\n.fw-menu .fw-hologram.selected{opacity:1;border-color:#ffd36e;box-shadow:0 0 7px #ffb42e99,0 1px 3px #000b;transform:rotate(var(--a,0deg)) translateY(-29px) rotate(calc(-1 * var(--a,0deg))) scale(1.18)}\r\n.fw-menu .fw-level{position:absolute;bottom:2px;left:50%;z-index:6;transform:translateX(-50%);min-width:16px;padding:1px 4px;border:1px solid #746674;border-radius:8px;background:#17121dde;color:var(--gold);text-align:center;font:700 8px/1.2 ui-monospace,monospace;pointer-events:none}\r\n.fw-menu .fw-style:has(.fw-dial:focus-visible) .fw-level{border-color:var(--gold)}\r\n.fw-menu .fw-start{min-height:44px;height:44px;margin-top:6px;padding:8px 10px;font-size:12px}\r\n@media(max-width:370px){.fw-menu .fw-grid{gap:3px}.fw-menu .fw-style{height:78px}.fw-menu .fw-dial-wrap,.fw-menu .fw-dial{width:44px;height:44px}.fw-menu .fw-hologram,.fw-menu .fw-hologram.selected{transform:rotate(var(--a,0deg)) translateY(-27px) rotate(calc(-1 * var(--a,0deg)))}}\r\n/* Fit the entire instrument plate between the deck strip and navigation. */\r\n#podFullWorkout .fw-menu{height:var(--fw-height,550px);max-height:none;overflow:hidden;padding:8px;box-sizing:border-box}\r\n.fw-menu .fw-body{height:100%;padding:0;display:flex;flex-direction:column;gap:5px}\r\n.fw-menu .fw-power{flex:none;box-sizing:border-box;min-height:44px;height:44px;margin:0;padding:0 10px}\r\n.fw-menu .fw-grid{flex:1;min-height:0;grid-template-rows:repeat(5,minmax(0,1fr));padding:0}\r\n.fw-menu .fw-style{height:auto;min-height:0}\r\n.fw-menu .fw-start{flex:none;box-sizing:border-box;height:44px;min-height:44px;margin:0}\r\n.fw-menu .fw-section-head{width:26px;height:26px}\r\n.fw-menu [data-anchor]{position:absolute;left:5px;top:4px;width:5px;height:5px;background:#a6999f;border:1px solid #17111c;border-radius:50%}\r\n.fw-menu .fw-enable{left:28px;right:auto;top:calc(50% - 22px)}\r\n.fw-menu .fw-dial-wrap{left:calc(100% - 42px)}\r\n.fw-menu .fw-hologram{margin:-9px 0 0 -9px}\r\n.fw-menu .fw-dial{touch-action:none}\r\n.fw-menu .fw-style[data-capped] .fw-dial-wrap{opacity:.45}\r\n.fw-menu .fw-hologram.fw-locked{opacity:.22}\r\n.fw-menu .fw-dial-wrap:before{background:repeating-conic-gradient(from -135deg,#c8b8c7 0deg 2deg,#0000 2deg 38deg);opacity:.5}\r\n@media(max-height:720px){.fw-menu .fw-dial-wrap{left:calc(100% - 35px)}.fw-menu .fw-hologram{width:14px;height:14px;margin:-7px 0 0 -7px}.fw-menu .fw-hologram img{width:12px;height:12px}.fw-menu .fw-hologram,.fw-menu .fw-hologram.selected{transform:rotate(var(--a)) translateY(-23px) rotate(calc(-1 * var(--a)))}}\r\n/* circuit: remaining count, lost tracking, done stinger */\r\n#fullWorkoutHud .fw-left{text-align:center;font:900 clamp(96px,34vw,200px)/1 ui-monospace,monospace;color:var(--gold);text-shadow:0 0 18px #000,0 0 40px #000c;pointer-events:none}\r\n#fullWorkoutHud .fw-lost{margin:8px auto;width:max-content;max-width:92vw;padding:8px 14px;border-radius:10px;background:#7a1020ee;border:1px solid #ff8da0;color:#fff;font:800 16px/1.2 var(--font);letter-spacing:.12em;pointer-events:none}\r\n#fullWorkoutHud[data-mode=stinger]{bottom:0;display:grid;place-items:center;pointer-events:none}\r\n.fw-stinger{font:900 clamp(64px,22vw,140px)/1 var(--font);color:var(--gold);text-shadow:0 0 24px #000,0 0 50px #ffca62aa;animation:fw-stinger 1s ease-out both}\r\n@keyframes fw-stinger{0%{transform:scale(.4);opacity:0}25%{transform:scale(1.15);opacity:1}100%{transform:scale(1);opacity:0}}\r\n/* circuit knobs: the control-board rotary look, detents only */\r\n.fw-knobs{display:flex;justify-content:space-around;gap:6px;margin:8px 0 4px;padding:8px 6px;border:2px solid #14101a;border-radius:8px;background:linear-gradient(#4a3d50,#30273a)}\r\n.fw-knob{display:grid;justify-items:center;gap:2px}.fw-knob-l{font:700 10px ui-monospace,monospace;letter-spacing:.2em;color:#d9c9d3}\r\n.fw-knob-v{font:900 24px/1 ui-monospace,monospace;color:var(--gold)}\r\n.fw-rotary{--size:52px;position:relative;width:var(--size);height:var(--size);border-radius:50%;touch-action:none;outline:none;background:radial-gradient(circle,#211a26 0 48%,#968890 51% 56%,#302636 59% 67%,#110e16 70%);box-shadow:0 2px 4px #000b,inset 0 1px #fff4}\r\n.fw-rotary:focus-visible{outline:3px solid var(--gold);outline-offset:3px}\r\n.fw-rotary .cb-ticks{position:absolute;inset:0;pointer-events:none}.fw-rotary .cb-ticks b{position:absolute;left:calc(50% - 1px);top:0;width:2px;height:5px;border-radius:1px;background:#8a7686;transform-origin:1px calc(var(--size) / 2);transform:rotate(var(--a))}.fw-rotary .cb-ticks b[data-on]{background:var(--gold);box-shadow:0 0 5px var(--gold)}\r\n.fw-rotary i{position:absolute;inset:9px;border-radius:50%;background:radial-gradient(closest-side,#302533 0 82%,#17111b 86%,#0000 90%);transform:rotate(var(--angle,0deg))}\r\n.fw-rotary i:after{content:'';position:absolute;left:calc(50% - 1px);top:-3px;width:2px;height:15px;border-radius:1px;background:#fbefd9}\r\n","pod/pod.mjs":"import {paintWorkoutSymbol} from '../workout-symbols.mjs';\r\nimport {captureRestBossContext,hasRestBossDefeat,readRestBossRewards,recordRestBossDefeatExclusive} from '../rest-boss-rewards.mjs';\r\nimport {initRestArena} from './rest-arena.mjs';\r\nimport {abilityFor} from './weapon-evolution.mjs';\r\nimport {SetFlow,DEFAULT_GOALS,valueOf,bossHealthMax,shouldStartRestTargets} from './set-flow.mjs';\r\nimport {SetEncouragement} from './encouragement.mjs';\r\nimport {setFlipValue,clockDigits} from '../flip-display.mjs';\r\nimport {GALA_KEY,loadGala,importGala,loadPower,POWERS} from './identity.mjs';\r\nimport {mountWorkoutRoute} from '../workout-route-ui.mjs';\r\nimport {mountCircuitUI} from '../circuit-ui.mjs';\r\nimport {ROUTE_LINES,exerciseFamily} from '../workout-route.mjs';\r\nimport {physicalSound} from '../audio/physical-sound.mjs';\r\nimport {VOICE_MANIFEST} from '../robot-audio.mjs';\r\nimport {WorkoutSessionOwner} from './workout-session-owner.mjs';\r\nimport {combatLevel} from '../battle-pass.mjs';\r\nimport {throughWormhole,LINES} from './set-transition.mjs';\r\nimport {SPECIAL_LEVEL} from '../combat-config.mjs';\r\nimport {recordPerformanceSession,readPerformanceProgress,workoutEligibility,performanceOwner,localDay} from '../performance-progress.mjs';\r\nimport {STARTER_COACH_IDS} from '../performance-catalog.mjs';\r\nimport {HOLD_TARGET_SECONDS,REP_CAP} from '../progression-rules.mjs';\r\nimport {mountShipBackdropMotion} from './ship-backdrop.mjs';\r\nimport {coach3dEnabled} from '../coach3d-flag.mjs';\r\nimport {mountRestCoachSprite} from './rest-coach-sprite.mjs';\r\nlet voiceManifest=null;\r\n// Fetch the clips this set will say while the camera opens; sw.js stores /voice/* in the voice cache, so RobotAudio's later fetch is a hit.\r\nfunction warmVoice(goal){try{voiceManifest??=fetch(VOICE_MANIFEST).then(r=>r.json()).catch(()=>{voiceManifest=null;return null;});voiceManifest.then(m=>{if(!m)return;const say=['Get into position.',...Array.from({length:Math.min(60,Number(goal)||0)},(_,i)=>String(i+1))];for(const p of say){const u=m.phrases[p];if(u)fetch(u,{priority:'low'}).catch(()=>{});}});}catch{}}\r\nconst $=id=>document.getElementById(id),PROGRESS='myr5-workout-progress-v1',COOLDOWN='myr5-special-cooldown-v1';\r\nconst time=s=>`${Math.floor(s/60)}:${String(Math.floor(s%60)).padStart(2,'0')}`;\r\nconst safeRead=key=>{try{return localStorage.getItem(key);}catch{return null;}};\r\nexport const idleStatus=text=>{const value=String(text||'').trim();return !value||value==='Ready';};\r\nexport function initPod({voice,movements,onStop,onNext,workouts}){\r\n mountShipBackdropMotion();\r\n let hand={enter(){},leave(){},hit(){},dispose(){},edit(){}};let handOptionalLoaded=false;const arena=initRestArena();\r\n window.addEventListener('myr5:optional-materials-ready',async()=>{if(handOptionalLoaded)return;handOptionalLoaded=true;const {initHandCompanion}=await import('../hand-companion.mjs');hand=initHandCompanion();},{once:true});\r\n // safeRead(PROGRESS) restores the last-synced circuit snapshot (and completedSets) so the\r\n // circuit meter can render immediately on reopen, before the next account-progress event lands.\r\n const flow=new SetFlow(safeRead(PROGRESS),{cooldown:safeRead(COOLDOWN)}),encourage=new SetEncouragement();\r\n const workoutOwner=new WorkoutSessionOwner({saveProgress:async data=>{\r\n  try{return await workouts.complete(data.id,{value:data.value,activeSeconds:data.activeSeconds,elapsedSeconds:data.elapsedSeconds,earned:data.earned,progress:data.progress});}\r\n  catch(error){return {local:false,reason:error?.message||'Workout progress was not saved on this device.'};}\r\n }});\r\n let currentMode=null,card=null,observedCard=null,restTimer=0,hitTimer=0,lastSpoken=-Infinity,restCalled=false,look,preparationRest=false;\r\n let gameCard=false,circuitRun=null,awaitingRound=null,pendingChallenge=null,pausedLocal=null,cardioStyle='gentle';\r\n const route=mountWorkoutRoute({mode:()=>currentMode||'squat',busy:()=>flow.phase!=='pod'||document.body.dataset.tracking==='true',pending:()=>!!awaitingRound,onNext});\r\n const circuit=mountCircuitUI({voice,onNext,busy:()=>flow.phase!=='pod'||document.body.dataset.tracking==='true',pending:()=>!!awaitingRound,cached:()=>flow.progress.circuit});\r\n window.addEventListener('myr5:coach-plan',()=>{if(flow.phase==='set')return;const mode=currentMode||'squat';currentMode=null;configure(mode);});\r\n const avatar=window.GalaAvatar;let storageAvailable=true;\r\n const store=(key,value)=>{try{localStorage.setItem(key,value);return true;}catch{storageAvailable=false;return false;}};\r\n function updateProgress(){flow.performanceXp=readPerformanceProgress().totalXp;const level=String(flow.level).padStart(2,'0');$('levelBadge').textContent='WORKOUT LV. '+level;$('restLevel').textContent='LV. '+level;$('setNumber').textContent='SET '+String(flow.progress.completedSets+1).padStart(2,'0');}\r\n function paintGuest(){\r\n  if(!avatar)return;\r\n  try{look=loadGala(localStorage,avatar);}catch{look={look:structuredClone(avatar.defaultLook),linked:false};}\r\n  for(const id of ['miniAvatar','identityAvatar'])avatar.draw($(id),look.look,{base:id!=='restAvatar'});\r\n  const name=window.MBS_DJ?.display()||(look.linked?(look.look.name||'Gala guest'):'Guest preview');arena.load();$('guestLabel').textContent=window.MBS_DJ?.display()||(look.linked?name:'Anonymous guest');$('restGuest').textContent=name;$('identityName').textContent=name;\r\n  $('restAvatar').setAttribute('aria-label',name+' on the floating platform');$('identityStatus').textContent=look.linked?'Appearance saved':'Guest appearance';\r\n }\r\n function moveCoach(){\r\n  // 3D coach off (coach3d-flag.mjs): the rest scene shows the 64-bit coach sprite; the 3D card never enters it.\r\n  if(!coach3dEnabled()&&(flow.phase==='rest'||preparationRest)){mountRestCoachSprite($('restCoachMount'));$('restCoachLoading').hidden=true;return;}\r\n  card??=document.querySelector('.myr5-companion-card');if(!card)return;\r\n  if(observedCard!==card){observer.observe(card,{childList:true,subtree:true,attributes:true,attributeFilter:['data-ready']});observedCard=card;}\r\n  // Full-screen workout mode parks the card in #coachOverlay (created at runtime by camera-workout.mjs,\r\n  // not a static pose.html id, hence document.getElementById here rather than the $() shorthand); leave it there while tracking.\r\n  if(!preparationRest&&((document.body.dataset.tracking==='true'&&document.getElementById('coachOverlay'))||document.body.dataset.shipView==='true')){}else{\r\n   const inRest=flow.phase==='rest'||preparationRest,mount=inRest?$('restCoachMount'):$('coachMount');if(card.parentElement!==mount)mount.append(card);\r\n   if(window.myr5Creature?.stats().stage!==(inRest?'encounter':'pod'))window.myr5Creature?.stage(inRest?'encounter':'pod');\r\n  }\r\n  const label=card.querySelector('.myr5-companion-status')?.textContent||'';\r\n  // Errors from the finished companion still need a visible place in the new shell.\r\n  for(const id of ['coachLoading','restCoachLoading']){const loading=$(id);loading.hidden=card.dataset.ready==='true';if(!loading.hidden&&loading.textContent!==label)loading.textContent=label;}\r\n }\r\n const observer=new MutationObserver(moveCoach);observer.observe($('view'),{childList:true,subtree:true});moveCoach();\r\n function configure(mode){\r\n  if(mode!==currentMode){currentMode=mode;const kind=movements[mode].kind,unit=kind==='hold'||kind==='pace'?'seconds':kind==='steps'?'steps':kind==='jumps'?'jumps':'reps';\r\n   const next=window.coachProgress?.exerciseRoute?.groups?.[exerciseFamily(mode)]?.next;\r\n   const planned=kind==='hold'?HOLD_TARGET_SECONDS:kind==='reps'?REP_CAP:HOLD_TARGET_SECONDS;\r\n   const values=[...new Set([1,2,3,5,9,10,15,20,30,45,60,90,120,180,planned,Math.max(1,planned-1),planned+1])].sort((a,b)=>a-b);\r\n   $('goal').replaceChildren(...values.map(n=>{const o=document.createElement('option');o.value=n;o.textContent=n+' '+unit;return o;}));$('goal').value=planned;\r\n  }\r\n  encourage.reset();$('goalValue').textContent=['hold','pace'].includes(movements[mode].kind)?time(Number($('goal').value)):String($('goal').value);updateProgress();route.render();circuit.render();if(document.body.dataset.tracking!=='true')$('start').disabled=!workoutEligibility(mode).allowed;\r\n }\r\n async function beginSet(mode,{manual=false}={}){if(manual)throw Error('Camera tracking is required for workout XP.');const startingOwner=performanceOwner(),restRewardsContext=captureRestBossContext();if(workoutOwner.snapshot().transitioning)throw Error('Coach is updating. Please wait.');configure(mode);pausedLocal=manual?await workouts.paused():null;if(performanceOwner()!==startingOwner)throw Error('Your account changed while the workout was opening. Start again.');const pausedOwner=pausedLocal?.progress?.performanceOwner??pausedLocal?.ownerId,legacyGuest=pausedLocal&&!pausedLocal.progress?.performanceOwner&&startingOwner.startsWith('guest:')&&pausedLocal.ownerId===workouts.ownerId;if(pausedLocal&&pausedOwner&&pausedOwner!==startingOwner&&!legacyGuest)throw Error('Resume this workout with the account or guest profile that started it.');if(workoutOwner.snapshot().transitioning)throw Error('Coach is updating. Please wait.');if(pausedLocal&&(!manual||pausedLocal.mode!==mode))throw Error('Resume or complete the paused manual workout first.');if(!pausedLocal&&!workoutOwner.canStart())throw Error('Workout activation is in progress. Try again.');const eligibility=workoutEligibility(mode);if(!eligibility.allowed)throw Error(eligibility.reason);const ownerStarted=!pausedLocal;if(ownerStarted&&!workoutOwner.start())throw Error('Workout activation is in progress. Try again.');let ticket=null;try{const goal=Number($('goal').value),restSeconds=circuitRun?.restSeconds??30;ticket=await workouts.start({mode,goal,restSeconds,progress:{...(pausedLocal?.progress??{}),performanceOwner:startingOwner},metadata:{name:movements[mode].name},control:manual?'manual':'camera'});if(performanceOwner()!==startingOwner)throw Error('Your account changed while the workout was opening. Start again.');flow.start(mode,goal,restSeconds,{performance:true,target:circuitRun?.target,kind:movements[mode].kind,snapshot:ticket.progress?.performanceWorkout,cardio:cardioStyle});if(legacyGuest){flow.workout.raw=Number(ticket.progress?.value)||0;flow.workout.baseline=flow.workout.raw;}flow.active.performanceOwner=startingOwner;flow.active.restRewardsContext=restRewardsContext;flow.active.localId=ticket.id;flow.active.startedAt=ticket.startedAt;flow.active.control=manual?'manual':'camera';flow.active.savedProgress=ticket.progress??{};pausedLocal=null;if(!manual)warmVoice($('goal').value);document.body.dataset.screen='pod';clearInterval(restTimer);return ticket;}catch(error){if(ticket){await workouts.interrupt(ticket.id,ticket.progress??{}).catch(()=>{});if(flow.phase==='set'&&flow.active?.localId===ticket.id)flow.leave();pausedLocal=null;}if(ownerStarted||ticket)workoutOwner.stop();throw error;}}\r\n function setGoal(goal){if(![...$('goal').options].some(o=>Number(o.value)===goal)){const option=document.createElement('option');option.value=goal;option.textContent=goal+' '+(['hold','pace'].includes(movements[currentMode].kind)?'seconds':movements[currentMode].kind==='steps'?'steps':'reps');$('goal').append(option);}$('goal').value=goal;$('goal').dispatchEvent(new Event('change',{bubbles:true}));}\r\n function render(m){const goal=flow.active?.mode===m.mode?flow.active.goal:Number($('goal').value)||DEFAULT_GOALS[m.mode];$('activity').style.width=Math.min(100,valueOf(m)/goal*100)+'%';}\r\n function power(){const p=$('coachPower').value;document.body.dataset.power=p;$('powerName').textContent=POWERS[p].name.toUpperCase()+' ACTIVE';store('myr5-pod-power-v1',p);}\r\n // Each real preparation/working rest has one stable boss. Reopening a view\r\n // cannot create a new defeat, and practice rests never grant packs.\r\n function restEncounter(now){const active=flow.active,c=active?.restRewardsContext;if(!active?.localId||!c||flow.preview||performanceOwner()!==c.owner)return null;const stage=flow.workout?.stage==='preparation-rest'?(flow.workout.nextStage==='working'?'preparation-2':'preparation-1'):'working';return {id:`${active.localId}:${stage}`,context:{...c,day:localDay(now)}};}\r\n flow.beforeRestAttack=now=>{const fight=restEncounter(now);if(!fight){flow.restBossId=null;return;}const identity=fight.context.day+':'+fight.id;if(flow.restBossId!==identity){flow.restBossId=identity;flow.damage=hasRestBossDefeat(fight.id,fight.context)?bossHealthMax(flow.kitLevel):0;flow.lastPetTick=now;}};\r\n flow.onRestDamage=now=>{if(flow.coachHealth>0)return;const fight=restEncounter(now);if(!fight)return;void recordRestBossDefeatExclusive(fight.id,fight.context).then(result=>{if(performanceOwner()!==fight.context.owner)return;if(result.recorded){const message=result.granted.length?`Boss defeated! ${result.granted.map(p=>p.tier+' pack').join(' + ')} earned.`:`Boss defeated! ${result.count}/5 today.`;$('restFeedback').textContent=message;voice.say(message,{interrupt:true});}}).catch(()=>{});};\r\n function syncCombat(){flow.weapon=arena.weapon;const p=flow.combat;$('shieldNote').textContent=p?`${p.loginStreak} login days × weapon level ${arena.weapon.tier+1}${p.breathingCompleted?' ×100 breathing':''} · ${flow.attackDamage} damage`:`BASE POWER · ${flow.attackDamage} damage`;}\r\n function specialControls(){syncCombat();const weapon=arena.weapon,ability=abilityFor(weapon),remaining=flow.abilities.remaining(),button=$('weaponSpecial');const locked=flow.kitLevel<SPECIAL_LEVEL;button.disabled=flow.phase!=='rest'||locked||!ability||remaining>0;button.textContent=locked?`Special · level ${SPECIAL_LEVEL}`:!ability?'Special · tier 4':remaining?`${ability.name} · ${Math.ceil(remaining/1000)}s`:ability.name;$('weaponCooldown').value=remaining?Math.max(0,1-remaining/Math.max(1,flow.abilities.durationMs)):1;}\r\n $('coachPower').value=loadPower({getItem:safeRead});power();\r\n function paintHealth(){const hp=Math.ceil(flow.coachHealth),max=bossHealthMax(flow.kitLevel);$('coachHealth').textContent=hp.toLocaleString()+' HP';$('bossHealth').style.width=(hp/max*100)+'%';$('bossHealth').parentElement.setAttribute('aria-valuenow',String(hp));}\r\n function speakChallenge(){if(pendingChallenge&&flow.phase==='rest'&&!document.hidden&&!document.body.dataset.cinematic){const text=pendingChallenge;pendingChallenge=null;voice.say(text,{key:'challenge',interrupt:true});}}\r\n window.addEventListener('myr5:cinematic-end',speakChallenge);\r\n function tick(){\r\n  if(preparationRest){\r\n   const w=flow.workout,now=Date.now();w?.advancePreparationRecovery(now);\r\n   if(w?.stage!=='preparation-rest'){exitPreparation();return;}\r\n   setFlipValue($('restTime'),clockDigits(Math.max(0,Math.ceil((w.recoveryUntil-now)/1000))),'preparation recovery remaining');\r\n   specialControls();return;\r\n  }\r\n  if(flow.phase!=='rest')return;speakChallenge();const now=Date.now();if(flow.shouldEndRest(now)){leave();return;}specialControls();const remaining=flow.remaining(now),next=route.suggestion();setFlipValue($('restTime'),clockDigits(remaining),'recovery remaining');$('nextSet').disabled=remaining>0||!next;$('nextSet').textContent=remaining?'Recovering…':awaitingRound?'Waiting to sync…':next?'Preview next round →':'Finished for today';if(!remaining&&!restCalled&&!document.hidden){restCalled=true;voice.say('Rest complete. Keep tapping to stay.',{interrupt:true});}\r\n }\r\n\r\n // Fruit Ninja rest layer: lazy so the game stays out of the core bundle.\r\n const SLICE_GAME='/arcade/slice/rest-game.mjs'; // variable path: esbuild leaves it alone, so the game stays out of the core bundle\r\n let slice=null,sliceLoadGeneration=0;\r\n const restTargetsActive=()=>shouldStartRestTargets(flow.phase,preparationRest),sliceHost=()=>document.querySelector('.encounter');\r\n function retrySlice(){if(!slice&&restTargetsActive())startSlice();}\r\n function startSlice(){\r\n  if(slice||!restTargetsActive())return;\r\n  const generation=++sliceLoadGeneration;\r\n  import(SLICE_GAME).then(m=>{\r\n   if(generation!==sliceLoadGeneration||slice||!restTargetsActive())return;\r\n   const instance=m.mountRestGame(sliceHost(),{avatar:$('restAvatar'),attackButton:$('attackCoach'),feedback:$('restFeedback'),sound:k=>physicalSound.play(k)});\r\n   slice=instance;instance.start();\r\n  }).catch(error=>{\r\n   if(generation!==sliceLoadGeneration||!restTargetsActive())return;\r\n   slice?.stop();slice=null;console.error('Rest targets failed to start',error);\r\n   $('restFeedback').textContent='Targets failed to load. Tap the scene to retry.';\r\n   sliceHost()?.addEventListener('pointerdown',retrySlice,{once:true});\r\n  });\r\n }\r\n function stopSlice(){sliceLoadGeneration++;sliceHost()?.removeEventListener('pointerdown',retrySlice);slice?.stop();slice=null;}\r\n function enterRest(result=null,{game=false}={}){preparationRest=false;setPreparationControls(false);\r\n  // Games (Subway Burpees, Jet Plank Joyride, Crouching Crisis): a 4 s result card, no rest boss, no rewards from it.\r\n  gameCard=game;if(game){setPreparationControls(true);if(flow.active)flow.active.restRewardsContext=null;}\r\n  flow.kitLevel=combatLevel(result?.mode||currentMode||'squat');flow.beforeRestAttack(Date.now()); // real battle-pass level for this boss's track (D8/D22)\r\n  for(const id of ['settings','identity'])if($(id).open)$(id).close();\r\n  document.body.dataset.screen='rest';$('homeScreen').hidden=true;$('restScreen').hidden=false;\r\n  $('restEyebrow').textContent=result?'SET COMPLETE':'REST PRACTICE';$('restHeading').textContent='Rest';\r\n  if(result)route.showResult();else route.hideResult();\r\n  if(result){\r\n   const symbol=document.createElement('span');symbol.setAttribute('role','img');paintWorkoutSymbol(symbol,result.mode,result.name);\r\n   const unit=['hold','sprint','gentle'].includes(result.kind)||movements[result.mode].kind==='pace'?'seconds':movements[result.mode].kind==='steps'?'steps':movements[result.mode].kind==='jumps'?'jumps':'reps';\r\n   $('setReceipt').replaceChildren(symbol,document.createTextNode(` · ${Math.round(result.value)} ${unit}`));\r\n  }else $('setReceipt').textContent='Practice';\r\n  $('earnedXp').textContent=result?.earned?`+${result.xp} XP`:'NO XP';$('damageTotal').textContent=Math.round(flow.damage)+' DAMAGE';paintHealth();\r\n  syncCombat();\r\n  $('restFeedback').textContent=game?'':'Every third tap: team strike';if(!game){hand.enter();arena.start();startSlice();}restCalled=game;lastSpoken=-Infinity;paintGuest();updateProgress();moveCoach();\r\n  clearInterval(restTimer);restTimer=setInterval(tick,250);tick();\r\n  history.replaceState(null,'','#rest');window.myr5Creature?.play(result?'celebrate':'rest');\r\n }\r\n // #150: into rest through the wormhole. The rest timer started with the set, so the time until rest shows (the save, the trip in) is added back;\r\n // the heading's focus and the voice line wait until the tunnel has opened.\r\n async function toRest(result=null,{silent=false,started=Date.now(),game=false}={}){\r\n  await throughWormhole(result?LINES.rest:LINES.practice,()=>{if(flow.phase!=='rest')return;flow.restUntil+=Date.now()-started;if(game)flow.restUntil=Date.now()+1000;enterRest(result,{game});});\r\n  if(flow.phase!=='rest')return;\r\n  $('restHeading').focus();if(!silent)voice.say(result?'Set complete. Take a breath.':'Tap to strike.',{interrupt:true});\r\n }\r\n async function persistResult(result,m,now,{game=false}={}){\r\n  const before={...flow.progress},id=flow.active?.localId,manual=flow.active?.control==='manual',owner=flow.active?.performanceOwner;\r\n  let coachId;try{coachId=JSON.parse(safeRead('myr5-recipe-v1')||'null')?.body;}catch{}\r\n  const earningProgress=readPerformanceProgress({owner}),earnedCoachCount=new Set(earningProgress.coaches.filter(id=>!STARTER_COACH_IDS.includes(id))).size;\r\n  const performance={...result,id,startedAt:flow.active?.startedAt,completedAt:now,day:localDay(now),earnedCoachCount,performanceOwner:owner,...(coachId?{coachId}: {})};\r\n  const saved=await workoutOwner.complete({id,value:result.value,activeSeconds:result.activeSeconds||m.active||0,elapsedSeconds:m.elapsed||0,earned:result.earned,progress:{...flow.progress,value:result.value,performanceOwner:owner,performance,performanceWorkout:flow.workout?.snapshot()}});\r\n  if(!saved.saved){flow.progress=before;flow.phase='set';if(flow.workout)flow.workout.finished=false;throw Error(saved.reason);}\r\n  const priorXp=readPerformanceProgress({owner}).totalXp,progress=recordPerformanceSession(performance,{owner});result.xp=Math.max(0,progress.totalXp-priorXp);flow.performanceXp=progress.totalXp;flow.progress.completedSets++;\r\n  window.dispatchEvent(new Event('myr5:local-history-refresh'));window.dispatchEvent(new CustomEvent('myr5:set-complete',{detail:{mode:result.mode,value:result.value,xp:result.xp,earned:result.earned}}));onStop();await toRest(result,{silent:manual,started:now,game});return true;\r\n }\r\n async function consume(m,now){const result=flow.consume({...m,manual:flow.active?.control==='manual'},now);publishInstruction();if(!result)return false;return persistResult(result,m,now);}\r\n async function finishWorking(m,now=Date.now(),opts){const result=flow.finishWorking({...m,manual:flow.active?.control==='manual'},now);return result?persistResult(result,m,now,opts):false;}\r\n function setPreparationControls(active){\r\n  for(const id of ['leaveRest','nextSet','moreRest'])$(id).hidden=active;\r\n  $('restTime').style.fontSize=active?'28px':'';\r\n }\r\n function enterPreparation(){\r\n  const w=flow.workout;if(preparationRest||flow.phase!=='set'||w?.stage!=='preparation-rest')return;\r\n  preparationRest=true;flow.kitLevel=combatLevel(flow.active.mode);flow.beforeRestAttack(Date.now());\r\n  document.body.dataset.screen='rest';document.body.dataset.preparationRest='true';$('homeScreen').hidden=true;$('restScreen').hidden=false;\r\n  $('restEyebrow').textContent='PREPARATION';$('restHeading').textContent='Recovery';\r\n  $('setReceipt').textContent=w.nextStage==='working'?'Final warm-up complete':'Warm-up complete';\r\n  $('earnedXp').textContent='NO XP';$('restFeedback').textContent='Recover, then training resumes automatically.';\r\n  route.hideResult();setPreparationControls(true);$('damageTotal').textContent=Math.round(flow.damage)+' DAMAGE';paintHealth();syncCombat();\r\n  hand.enter();arena.start();startSlice();paintGuest();updateProgress();moveCoach();\r\n  clearInterval(restTimer);restTimer=setInterval(tick,250);\r\n  window.dispatchEvent(new CustomEvent('myr5:preparation-rest-start',{detail:{stage:w.stage,nextStage:w.nextStage,recoveryUntil:w.recoveryUntil}}));tick();\r\n }\r\n function exitPreparation({resume=true}={}){\r\n  if(!preparationRest)return;\r\n  preparationRest=false;delete document.body.dataset.preparationRest;hand.leave();arena.stop();stopSlice();clearInterval(restTimer);clearTimeout(hitTimer);\r\n  setPreparationControls(false);$('restScreen').hidden=true;$('homeScreen').hidden=false;document.body.dataset.screen='pod';\r\n  const coachOverlay=document.getElementById('coachOverlay');if(coachOverlay&&card&&document.body.dataset.tracking==='true')coachOverlay.append(card);\r\n  moveCoach();\r\n  if(resume){window.dispatchEvent(new CustomEvent('myr5:preparation-rest-end',{detail:{stage:flow.workout?.stage,mode:flow.active?.mode}}));publishInstruction();}\r\n }\r\n\r\n let lastInstruction='';\r\n function publishInstruction(){if(flow.phase==='set'&&flow.workout?.stage==='preparation-rest')enterPreparation();else if(preparationRest)exitPreparation();const instruction=flow.workout?.instruction??'';if(instruction===lastInstruction)return;lastInstruction=instruction;window.dispatchEvent(new CustomEvent('myr5:workout-stage',{detail:{stage:flow.workout?.stage,instruction,mode:flow.active?.mode}}));}\r\n function workoutMotion(m){const w=flow.workout;if(!w)return m;const cardio=['sprint','gentle'].includes(w.kind);return {...m,kind:cardio?'pace':m.kind,active:cardio?w.activeSeconds:m.active,count:w.kind==='reps'?w.value:m.count,totalHold:w.kind==='hold'?w.activeSeconds:m.totalHold,message:w.instruction,remaining:Math.max(0,(w.recoveryUntil-Date.now())/1000)};}\r\n async function saveManual(m){const id=flow.active?.localId;if(!id)return null;const value=valueOf(m),progress={...flow.progress,value,performanceOwner:flow.active?.performanceOwner,activeSeconds:m.active||0,elapsedSeconds:m.elapsed||0,performanceWorkout:flow.workout?.snapshot()};await workouts.update(id,progress);flow.active.savedProgress=progress;return progress;}\r\n async function pauseManual(m){const id=flow.active?.localId;if(!id)return null;const progress=await saveManual(m),workout=await workouts.pause(id,progress);exitPreparation({resume:false});pausedLocal=workout;flow.leave();return workout;}\r\n async function interruptCurrent(m){exitPreparation({resume:false});const id=flow.active?.localId;let timer;try{if(id)await Promise.race([workouts.interrupt(id,{...flow.progress,performanceOwner:flow.active?.performanceOwner,performanceWorkout:flow.workout?.snapshot(),value:m?valueOf(m):0,activeSeconds:m?.active||0,elapsedSeconds:m?.elapsed||0}),new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error('Workout cleanup timed out. Reload Coach before starting again.')),5000);})]);}finally{clearTimeout(timer);if(flow.phase==='set')flow.leave();pausedLocal=null;workoutOwner.stop();route.render();circuit.render();}}\r\n // #150: the trips out of rest stop its timer first, so the idle end can't leave() under the tunnel.\r\n // #56: the ↗ exit goes home to the quilt (clears the hash, closes the route); \"Next set →\" stays in the pod.\r\n function leave({home=false}={}){exitPreparation({resume:false});if(gameCard){gameCard=false;setPreparationControls(false);}pendingChallenge=null;hand.leave();arena.stop();stopSlice();clearInterval(restTimer);clearTimeout(hitTimer);voice.cancel();flow.leave();window.dispatchEvent(new Event('myr5:rest-end'));document.body.dataset.screen='pod';$('restScreen').hidden=true;$('homeScreen').hidden=false;moveCoach();if(home){history.replaceState(null,'',location.pathname+location.search);window.myr5Routes?.home?.();}else history.replaceState(null,'','#pod');$('start').disabled=!workoutEligibility(currentMode).allowed;route.render();circuit.render();$('start').focus();}\r\n document.querySelector('.encounter').addEventListener('pointerdown',()=>flow.touchRest(Date.now()),{passive:true});\r\n $('attackCoach').addEventListener('click',()=>{\r\n  syncCombat();const now=Date.now(),hit=preparationRest?flow.preparationTap(now,true):flow.tap(now,true);if(!hit)return;\r\n  if(preparationRest)window.dispatchEvent(new CustomEvent('myr5:preparation-hit',{detail:hit}));\r\n  // Spectacle only (D7): no gameplay listens for this, boss attacks never touch player HP.\r\n  if(hit.bossAttack)window.dispatchEvent(new CustomEvent('myr5:boss-attack',{detail:{level:flow.kitLevel}}));\r\n  hand.hit(hit);arena.attack(hit);window.myr5Creature?.play(hit.assisted?'encourage':hit.blocked?'agree':'rest');\r\n  $('damageTotal').textContent=Math.round(hit.totalDamage)+' DAMAGE';paintHealth();$('damageFloat').textContent=hit.blocked?(hit.assisted?'TEAM STRIKE · BLOCKED':'BLOCKED · 0'):(hit.assisted?'TEAM −':'−')+Math.round(hit.damage);\r\n  const scene=document.querySelector('.encounter');scene.classList.remove('hit');void scene.offsetWidth;scene.classList.add('hit');clearTimeout(hitTimer);hitTimer=setTimeout(()=>scene.classList.remove('hit'),650);\r\n  $('restFeedback').textContent=hit.blocked?`${POWERS[$('coachPower').value].line} ${hit.hits} ${hit.hits===1?'hit':'hits'}, zero damage.`:`${hit.assisted?'Helping Hand lands a team strike! ':''}${hit.hits} hits. ${hit.totalDamage} damage.`;\r\n  if(now-lastSpoken>10000){lastSpoken=now;window.myr5Creature?.play(hit.blocked?'agree':'encourage');voice.say(hit.blocked?'Nice teamwork. My shield is still intact. Keep training.':'You and that hand make quite a team. That one connected.',{key:'rest'});}\r\n });\r\n $('leaveRest').addEventListener('click',()=>{clearInterval(restTimer);void throughWormhole(LINES.home,()=>leave({home:true}));});$('moreRest').addEventListener('click',()=>{flow.extend(30,Date.now());restCalled=false;tick();voice.say('Thirty more seconds. Take your time.',{interrupt:true});});\r\n $('weaponSpecial').addEventListener('click',async()=>{\r\n  const activate=()=>{syncCombat();flow.abilities.merge(safeRead(COOLDOWN));const result=flow.special(arena.weapon,{now:Date.now(),progress:arena.progress,catalog:window.GalaWeapons});if(result.ok)store(COOLDOWN,JSON.stringify(flow.abilities.snapshot()));return result;};\r\n  const hit=navigator.locks?.request?await navigator.locks.request('myr5-weapon-special',activate):activate();if(!hit.ok){specialControls();return;}\r\n  arena.attack(hit);window.myr5Creature?.play(hit.blocked?'agree':'rest');\r\n  $('damageTotal').textContent=Math.round(hit.totalDamage)+' DAMAGE';paintHealth();\r\n  $('damageFloat').textContent=hit.blocked?'BLOCKED':'−'+hit.damage;$('restFeedback').textContent=hit.ability.name+(hit.blocked?' · Shielded':'');\r\n  const scene=document.querySelector('.encounter');scene.classList.remove('hit');void scene.offsetWidth;scene.classList.add('hit');clearTimeout(hitTimer);hitTimer=setTimeout(()=>scene.classList.remove('hit'),650);specialControls();\r\n });\r\n $('nextSet').addEventListener('click',async()=>{const next=route.suggestion();if(flow.remaining(Date.now())>0||!next)return;clearInterval(restTimer);await throughWormhole(LINES.next,leave);onNext(next);});\r\n $('visitRest').hidden=true;$('visitRest').disabled=true;\r\n $('openSettings').addEventListener('click',()=>{$('settings').showModal();voice.say('Pod controls.',{interrupt:true});});$('closeSettings').addEventListener('click',()=>$('settings').close());\r\n $('coachPower').addEventListener('change',()=>{power();voice.say(POWERS[$('coachPower').value].name+' selected.',{interrupt:true});});\r\n $('openIdentity').addEventListener('click',()=>{paintGuest();$('identity').showModal();voice.say('Your Gala character.',{interrupt:true});});$('closeIdentity').addEventListener('click',()=>$('identity').close());\r\n $('importGala').addEventListener('change',async event=>{const f=event.target.files?.[0];if(!f)return;try{if(f.size>30000)throw Error('Choose your exported Gala look file.');const imported=importGala(await f.text(),avatar);if(!store(GALA_KEY,JSON.stringify(imported)))throw Error('Could not save this appearance on your device.');paintGuest();$('identityStatus').textContent='Appearance saved';voice.say('Appearance saved',{interrupt:true});}catch(error){$('identityStatus').textContent=error instanceof SyntaxError?'That file is not a Gala look.':error.message;}finally{event.target.value='';}});\r\n window.addEventListener('storage',e=>{if(e.key===GALA_KEY)paintGuest();});window.addEventListener('mominc-avatar-change',paintGuest);\r\n window.addEventListener('storage',e=>{if(e.key===COOLDOWN){flow.abilities.merge(e.newValue);if(flow.phase==='rest')specialControls();}});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden){voice.cancel();if(flow.phase==='rest')leave();}else tick();});\r\n window.addEventListener('myr5:route-leave',()=>{if(flow.phase==='rest')leave();});\r\n window.addEventListener('pagehide',()=>{hand.dispose();arena.dispose();clearInterval(restTimer);clearTimeout(hitTimer);observer.disconnect();});\r\n document.querySelector('.mom-brand').addEventListener('click',event=>{event.preventDefault();if(flow.phase==='rest')leave();});\r\n paintGuest();updateProgress();document.body.dataset.screen='pod';\r\n // The idle pod shows no \"Ready\" line: #status only appears while it has something to say (a set's hints, an error).\r\n const statusLine=$('status'),syncStatus=()=>{const quiet=idleStatus(statusLine.textContent);if(statusLine.hidden!==quiet)statusLine.hidden=quiet;};\r\n new MutationObserver(syncStatus).observe(statusLine,{childList:true,characterData:true,subtree:true});syncStatus();\r\n const requestedPanel=new URLSearchParams(location.search).get('panel');if(['avatar','hand'].includes(requestedPanel)){$('identity').showModal();if(requestedPanel==='hand')hand.edit();}\r\n window.addEventListener('myr5:account-progress',({detail:p})=>{flow.combat=p.combat;flow.progress.completedSets=p.completedSets;flow.progress.circuit=p.circuit||null;store(PROGRESS,JSON.stringify(flow.progress));for(const option of $('coachPower').options){if(option.value!=='shield'){option.disabled=!p.unlocks[option.value];option.textContent=POWERS[option.value].name+(option.disabled?' · Locked':'');}}if($('coachPower').selectedOptions[0]?.disabled)$('coachPower').value='shield';power();updateProgress();});\r\n window.addEventListener('myr5:performance-progress',()=>{updateProgress();if(flow.phase==='pod')$('start').disabled=!workoutEligibility(currentMode).allowed;});\r\n window.addEventListener('myr5:round-rejected',({detail})=>{if(awaitingRound?.id!==detail.id)return;awaitingRound=null;pendingChallenge=null;$('setReceipt').textContent=detail.message;$('earnedXp').textContent='NO XP';route.render();circuit.render();});\r\n window.addEventListener('myr5:account-progress',({detail:p})=>{if(awaitingRound&&p.lastSyncedWorkoutId===awaitingRound.id){awaitingRound=null;route.render();if(flow.phase==='rest'&&!flow.preview){const next=route.suggestion();pendingChallenge=(next?.line||ROUTE_LINES.limit)+' '+ROUTE_LINES.rest;speakChallenge();}}if(document.body.dataset.tracking!=='true')$('start').disabled=!workoutEligibility(currentMode).allowed;route.render();});\r\n // Reading a paused draft must never hold the only Begin lease. IndexedDB may be slow or\r\n // blocked after a tab was closed, while a fresh camera set can still be requested.\r\n void workouts.paused().then(paused=>{if(flow.phase==='pod')pausedLocal=paused;route.render();}).catch(()=>{});\r\n return {setCircuit:run=>{circuitRun=run;},freeze:on=>{if(flow.workout)flow.workout.frozen=!!on;},leave:()=>{if(flow.phase==='rest'){clearInterval(restTimer);leave();}},flow,workoutOwner,rebaseTracking:()=>flow.workout?.rebaseTracking(),preparationTap:()=>{const hit=flow.preparationTap();if(hit)window.dispatchEvent(new CustomEvent('myr5:preparation-hit',{detail:hit}));return hit;},configure,beginSet,consume,setCardioStyle:style=>{if(!['gentle','sprint'].includes(style))throw Error('Choose gentle cardio or sprint intervals.');if(flow.phase==='set')throw Error('Choose cardio style before starting.');cardioStyle=style;},switchDifficulty:style=>flow.workout?.switchDifficulty(style),finishWorking,workoutMotion,canCount:()=>flow.workout?.counting??true,breakHold:()=>{flow.workout?.breakHold();publishInstruction();},resumeHold:()=>{flow.workout?.resumeHold();publishInstruction();},extendHoldRecovery:()=>flow.workout?.extendRecovery(),saveManual,pauseManual,interruptCurrent,render,setGoal,canStart:mode=>(workoutOwner.canStart()||pausedLocal?.mode===mode)&&workoutEligibility(mode).allowed,encouragement:(m,now,events)=>flow.phase==='set'?encourage.update(m,flow.active.goal,now,events):null,stopped:interruptCurrent,goal:()=>Number($('goal').value)};\r\n}\r\n","pod/rest-arena.css":".encounter{min-height:390px;border:1px solid #b797c655;border-radius:26px;overflow:hidden;background:radial-gradient(ellipse at 68% 29%,#673d7838,transparent 45%),linear-gradient(#171026,#261c38);box-shadow:inset 0 0 45px #05020f99}.encounter-grid{opacity:.45}.rest-coach-mount{inset:6% 6% 29% 26%}.attack-target{inset:10% 5% 31% 28%}.coach-battle-platform{position:absolute;left:32%;right:7%;bottom:32%;height:45px;border-radius:50%;border:2px solid #edbd8555;background:repeating-radial-gradient(ellipse,#574155 0 8px,#8b686a 9px 10px);box-shadow:0 7px 0 #2a2037,0 9px 0 #977ca9,0 18px 24px #03000888;z-index:1}.player-platform{left:30%;bottom:12%;width:185px;height:50px}.platform-top{background:repeating-radial-gradient(ellipse,#324353 0 11px,#568b9a 12px 13px);border-color:#8de8d5;box-shadow:0 0 19px #64f5d344}.platform-edge{background:linear-gradient(#537287,#192031);border-color:#8bddd7;box-shadow:0 4px 0 #57b8ba,0 10px 24px #47cecf22}.platform-thruster{background:linear-gradient(#89ffde99,transparent)}#restAvatar{width:150px;height:158px;left:18px;bottom:15px}.rest-hand{left:auto;right:3%;bottom:10%;width:clamp(115px,32vw,175px);height:clamp(135px,38vw,200px)}.player-name{bottom:12px;gap:5px;flex-wrap:wrap;padding:0 8px;text-align:center;line-height:1.3}.player-name #restGuest{max-width:75%;overflow-wrap:anywhere}.rest-weapon-name{position:absolute;left:14px;bottom:30%;color:#9debd9;font:9px monospace;max-width:145px;z-index:9}.boss-status{z-index:9;padding:9px 12px}.attack-target span{font-size:9px;letter-spacing:1px;bottom:12px}.attack-flash{left:44%;top:27%}.projectile{left:31%;bottom:42%;background:#afffe7;box-shadow:0 0 12px #63ffc6;transform:rotate(38deg)}.encounter.hit .projectile{animation:arena-bolt .3s ease-in}.encounter[data-weapon=melee].hit .projectile{height:65px;width:65px;border-top:4px solid #b8ffeb;border-radius:50%;background:none;box-shadow:none;animation:arena-slash .35s ease-out}.encounter.team-strike.hit .attack-flash{border-width:5px;box-shadow:0 0 30px #e6b4ff99}.encounter.team-strike.hit .projectile{filter:drop-shadow(8px 5px 2px #cf95ff)}.encounter.hit #restAvatar{animation:none}.weapon-subsection{padding:15px;margin-top:15px;border:1px solid #7c8eaa;border-radius:12px;background:#13253166;text-align:left}.weapon-subsection h3{margin:0 0 10px;color:#b7fce4}.weapon-subsection label{display:block;font-size:13px;margin:10px 0}.level-bar{display:block;width:100%;height:10px;accent-color:#7cf0cf}.weapon-subsection select{display:block;width:100%;margin-top:5px}.weapon-subsection p{margin-bottom:0!important}@keyframes arena-bolt{0%{opacity:1;transform:translate(0,0) rotate(38deg)}100%{opacity:.3;transform:translate(105px,-130px) rotate(38deg)}}@keyframes arena-slash{0%{opacity:0;transform:translate(40px,-50px) rotate(-70deg)}50%{opacity:1}100%{opacity:0;transform:translate(100px,-100px) rotate(65deg)}}@media(max-width:370px){.encounter{min-height:350px}.player-platform{width:160px}.rest-hand{right:0;width:112px;height:138px}#restAvatar{left:5px}.rest-weapon-name{max-width:110px;font-size:8px}}@media(prefers-reduced-motion:reduce){.encounter.hit .projectile{animation:none;opacity:.7}.encounter.hit .attack-flash{animation:none;opacity:.4}}\r\n","pod/rest-arena.mjs":"import {GALA_KEY,loadGala} from './identity.mjs';\r\nimport {abilityFor,evolution} from './weapon-evolution.mjs';\r\nimport {drawAnimatedWeapon} from './weapon-animator.mjs';\r\nimport {createOptionalModuleLoader} from '../arcade/workout-effects-state.mjs';\r\nconst loadPowerState=createOptionalModuleLoader('/arcade/power/avatar-power.mjs');\r\nconst loadPowerRenderer=createOptionalModuleLoader('/arcade/power/power.mjs');\r\nconst loadBlizzardRenderer=createOptionalModuleLoader('/arcade/hold/blizzard.mjs');\r\nexport const MELEE=new Set(['rapier','greatsword','dagger','spear','trident','scythe','gauntlets']);\r\nexport function equipmentProgress(value={}){return {activeDays:Math.max(0,Number(value.activeDays)||0),totalXp:Math.max(0,Number(value.activeDays)||0)*100,completedSets:Math.max(0,Number(value.completedSets)||0),strength:1+Math.floor(Math.max(0,Number(value.completedSets)||0)/4),...(value.trainingVersion===1?{trainingVersion:1,training:value.training}:{})};}\r\n\r\nexport function initRestArena(){\r\n const A=window.GalaAvatar,W=window.GalaWeapons,canvas=document.getElementById('restAvatar'),scene=document.querySelector('.encounter');\r\n const fx=document.getElementById('restWeaponFx'),type=document.getElementById('weaponType'),tier=document.getElementById('weaponTier'),note=document.getElementById('weaponStatus'),bar=document.getElementById('weaponLevelBar');\r\n const body=document.createElement('canvas'),reduced=matchMedia('(prefers-reduced-motion: reduce)');\r\n let progress=equipmentProgress(),look=null,equipped={type:'rapier',tier:0},running=false,frame=0,last=0,action=null,queued=false;\r\n let powerRecord={version:1,equipped:null,styles:{}},powerOwnerId=null,powerFingerprint='',powerModule=null,powerRenderer=null,blizzardModule=null,powerSchedule=null,powerLoading=null,powerLoadToken=0,powerTime=0,powerPulse=0,lastPowerAt=0,powerArt=null,powerArtCtx=null;\r\n let anchor={x:0,y:0,scale:1},lastWeapon='';\r\n canvas.width=160;canvas.height=168;\r\n window.GalaProgress={read:()=>progress};\r\n for(const item of W.types){const option=document.createElement('option');option.value=item.id;option.textContent=item.name;type.append(option);}\r\n function measure(){\r\n  const bounds=scene.getBoundingClientRect(),avatar=canvas.getBoundingClientRect();\r\n  if(!bounds.width||!bounds.height)return;\r\n  const ratio=Math.min(1,Math.max(320,Math.round(bounds.width/2))/bounds.width);\r\n  fx.width=Math.round(bounds.width*ratio);fx.height=Math.round(bounds.height*ratio);\r\n  anchor={x:(avatar.left+avatar.width*.78-bounds.left)*ratio,y:(avatar.top+avatar.height*.48-bounds.top)*ratio,scale:ratio*1.15};\r\n }\r\n const hasPower=()=>!!powerRecord.styles?.[powerRecord.equipped];\r\n function resetPower(){powerSchedule?.reset();powerTime=0;powerPulse=0;lastPowerAt=0;}\r\n function makePowerArt(){if(powerArtCtx)return;powerArt=document.createElement('canvas');powerArt.width=160;powerArt.height=168;powerArtCtx=powerArt.getContext('2d',{alpha:true});if(!powerArtCtx){powerArt=null;return;}powerArtCtx.imageSmoothingEnabled=false;}\r\n function ensurePowerRenderer(){\r\n  if(powerModule&&powerRenderer&&blizzardModule&&powerSchedule){if(running)draw(performance.now());return;}\r\n  if(powerLoading||!hasPower())return;\r\n  const token=++powerLoadToken;\r\n  powerLoading=Promise.all([loadPowerState(),loadPowerRenderer(),loadBlizzardRenderer()])\r\n   .then(([avatar,power,blizzard])=>{if(token!==powerLoadToken||!hasPower())return;powerModule=avatar;powerRenderer=power;blizzardModule=blizzard;powerSchedule=avatar.createAvatarPowerSchedule({seed:0x525354});makePowerArt();resetPower();if(running)draw(performance.now());})\r\n   .catch(()=>{}).finally(()=>{if(token===powerLoadToken)powerLoading=null;});\r\n }\r\n function requestPowerSnapshot(){if(typeof CustomEvent==='function')window.dispatchEvent(new CustomEvent('myr5:retained-character-power-request'));}\r\n function retainedPower(event){const detail=event?.detail;if(!detail||typeof detail.ownerId!=='string')return;const next=detail.record&&typeof detail.record==='object'?detail.record:{version:1,equipped:null,styles:{}};const fingerprint=JSON.stringify(next);if(detail.ownerId===powerOwnerId&&fingerprint===powerFingerprint){if(hasPower())ensurePowerRenderer();return;}powerOwnerId=detail.ownerId;powerFingerprint=fingerprint;powerLoadToken++;powerLoading=null;powerRecord=next;resetPower();if(hasPower())ensurePowerRenderer();else if(look)draw(performance.now());}\r\n function load(){\r\n  try{look=loadGala(localStorage,A).look;}catch{look=structuredClone(A.defaultLook);}\r\n  const selected=look.weapon||{type:'rapier',tier:0};\r\n  equipped=W.unlocked(selected,progress)?selected:{type:selected.type,tier:0};\r\n  const key=equipped.type+':'+equipped.tier;if(lastWeapon!==key){action=null;queued=false;lastWeapon=key;}\r\n  A.draw(body,look,{base:false,weapon:false,prop:false});type.value=selected.type;tier.replaceChildren();\r\n  for(let i=0;i<W.tiers.length;i++){const item={type:selected.type,tier:i},option=document.createElement('option');option.value=i;option.disabled=!W.unlocked(item,progress);option.textContent=`${W.tiers[i]} · Level ${i+1}`;tier.append(option);}\r\n  tier.value=equipped.tier;const ability=abilityFor(equipped);\r\n  const lv=W.level(progress);\r\n  note.textContent=`Level ${lv.level} · `+(lv.max?'max level':`${lv.into}/${lv.need} XP to next`)+' · '+(ability?`${ability.name} · ${ability.cooldownMs/1000}s`:'Special at tier 4');\r\n  if(bar){bar.max=lv.max?1:lv.need;bar.value=lv.max?1:lv.into;bar.setAttribute('aria-valuetext',`Level ${lv.level}`+(lv.max?'':`, ${lv.into} of ${lv.need} XP`));}\r\n  if(selected.tier!==equipped.tier)note.textContent+=' Upgrade not yet earned.';\r\n  document.getElementById('restWeaponName').textContent=W.types.find(w=>w.id===equipped.type).name;\r\n  scene.dataset.weapon=MELEE.has(equipped.type)?'melee':'ranged';scene.classList.add('weapon-evolution');\r\n  scene.style.setProperty('--weapon-energy',evolution(equipped).energy);\r\n  measure();draw(performance.now());\r\n }\r\n function equip(){try{const next={type:type.value,tier:Number(tier.value)};if(!W.unlocked(next,progress))return;localStorage.setItem(GALA_KEY,JSON.stringify({...look,weapon:next}));window.dispatchEvent(new Event('mominc-avatar-change'));}catch{note.textContent='Could not save this weapon.';}}\r\n type.addEventListener('change',()=>{if(!W.unlocked({type:type.value,tier:Number(tier.value)},progress))tier.value='0';equip();});tier.addEventListener('change',equip);\r\n function duration(){return action?.special?abilityFor(equipped)?.animationMs||1100:evolution(equipped).attackMs;}\r\n function draw(now){\r\n  if(action&&now-action.startedAt>=duration()){action=queued?{startedAt:now,special:false}:null;queued=false;}\r\n  const ctx=canvas.getContext('2d');ctx.clearRect(0,0,160,168);ctx.imageSmoothingEnabled=false;\r\n  const pulse=action&&!reduced.matches?Math.sin(Math.min(1,(now-action.startedAt)/duration())*Math.PI):0;\r\n  ctx.drawImage(body,26+pulse*4,14-pulse*5,96,144);\r\n  if(powerSchedule&&powerModule&&powerRenderer&&blizzardModule){\r\n   const dt=lastPowerAt?Math.max(0,Math.min(.25,(now-lastPowerAt)/1000)):0;lastPowerAt=now;\r\n   const canShow=running&&!document.hidden&&hasPower(),active=canShow?powerSchedule.step(dt,true,reduced.matches):0;\r\n   if(active){powerTime+=reduced.matches?0:dt;if(powerPulse<=0)powerPulse=1;if(reduced.matches)powerPulse=0;else powerPulse=Math.max(0,powerPulse-dt*2.5);\r\n    const feet=[{index:27,x:.46,y:.835,visible:true},{index:28,x:.54,y:.835,visible:true}],state=powerModule.avatarPowerState(powerRecord,160,168,reduced.matches?0:powerTime,reduced.matches?0:powerPulse,false,feet);\r\n    if(state&&powerArtCtx){powerArtCtx.clearRect(0,0,160,168);if(state.blizzard)blizzardModule.renderBlizzard(powerArtCtx,state);else powerRenderer.renderPower(powerArtCtx,state);ctx.save();ctx.globalAlpha=.55;ctx.translate(-6+pulse*4,1-pulse*5);ctx.drawImage(powerArt,0,0);ctx.restore();}\r\n   }else{powerPulse=0;if(!canShow)powerTime=0;}\r\n  }\r\n  const effects=fx.getContext('2d');effects.clearRect(0,0,fx.width,fx.height);\r\n  effects.save();effects.translate(anchor.x,anchor.y);effects.rotate(-Math.PI/2-.13);\r\n  drawAnimatedWeapon(effects,equipped,{weapons:W,x:0,y:0,scale:anchor.scale,now,action,reducedMotion:reduced.matches});\r\n  effects.restore();\r\n }\r\n function tick(now){frame=0;if(!running||document.hidden)return;if(now-last>(reduced.matches?100:32)){draw(now);last=now;}frame=requestAnimationFrame(tick);}\r\n function start(){running=true;load();if(!frame)frame=requestAnimationFrame(tick);}\r\n function stop(){running=false;action=null;queued=false;if(frame)cancelAnimationFrame(frame);frame=0;resetPower();scene.classList.remove('team-strike');draw(performance.now());}\r\n const visibility=()=>{if(document.hidden){if(frame)cancelAnimationFrame(frame);frame=0;resetPower();draw(performance.now());}else if(running&&!frame){measure();last=0;frame=requestAnimationFrame(tick);}};\r\n const pageshow=()=>{requestPowerSnapshot();if(running&&!document.hidden&&!frame)frame=requestAnimationFrame(tick);};\r\n const account=event=>{progress=equipmentProgress(event.detail);load();};\r\n const resize=new ResizeObserver(measure);resize.observe(scene);\r\n window.addEventListener('myr5:account-progress',account);window.addEventListener('mominc-avatar-change',load);window.addEventListener('myr5:retained-character-power',retainedPower);window.addEventListener('pageshow',pageshow);document.addEventListener('visibilitychange',visibility);load();requestPowerSnapshot();\r\n return {load,start,stop,get weapon(){return {...equipped};},get progress(){return {...progress};},attack(hit){\r\n   const now=performance.now();\r\n   // A special finishes even if the user continues tapping. One queued basic\r\n   // preserves follow-through instead of restarting every windup.\r\n   if(!hit.special&&action&&now-action.startedAt<duration()){\r\n    if(action.special||now-action.startedAt<duration()*.6){queued=true;return;}\r\n   }\r\n   action={startedAt:now,special:!!hit.special};queued=false;\r\n   scene.classList.toggle('team-strike',!!hit.assisted);draw(now);\r\n  },dispose(){stop();powerLoadToken++;powerSchedule?.dispose();powerArt=null;powerArtCtx=null;resize.disconnect();window.removeEventListener('myr5:account-progress',account);window.removeEventListener('mominc-avatar-change',load);window.removeEventListener('myr5:retained-character-power',retainedPower);window.removeEventListener('pageshow',pageshow);document.removeEventListener('visibilitychange',visibility);type.removeEventListener('change',equip);tier.removeEventListener('change',equip);}};\r\n}\r\n","pod/rest-coach-sprite.mjs":"// Rest scene coach while the 3D coach is off (coach3d-flag.mjs): the 64-bit coach sprite, front-facing, standing\r\n// still in front of the user, beside the MOM gun lane. Same sprite source as the Fruit Ninja coach (camera-slice.mjs). Drawn once per call.\r\n// The MOM gun (arcade/slice/rest-game.mjs) hangs top-centre and fires down the middle at the player, so the coach stands\r\n// right of that lane, facing us. No seated frame exists in war-room/coach-sprites.js, so he stands still.\r\nexport const REST_SPRITE={heightPct:46,bottomPct:2,centerPct:70}; // tuning: size/feet/horizontal centre within the rest mount\r\nexport function mountRestCoachSprite(mount){\r\n if(!mount)return null;\r\n let canvas=mount.querySelector('canvas.rest-coach-sprite');\r\n if(!canvas){\r\n  canvas=document.createElement('canvas');canvas.className='rest-coach-sprite';canvas.width=64;canvas.height=96;canvas.setAttribute('aria-hidden','true');\r\n  const aspect=64/96;\r\n  canvas.style.cssText=`position:absolute;left:${REST_SPRITE.centerPct}%;bottom:${REST_SPRITE.bottomPct}%;height:${REST_SPRITE.heightPct}%;aspect-ratio:${aspect};transform:translateX(-50%);image-rendering:pixelated;pointer-events:none;`;\r\n  mount.append(canvas);\r\n }\r\n (async()=>{\r\n  try{const A=window.GalaAvatar,{loadGala}=await import('/pod/identity.mjs'),m=await import('/war-room/coach-sprites.js'),c=await m.loadWarRoomCoaches(document);\r\n   c.draw(A.draw,canvas,loadGala(localStorage,A).look,{});}\r\n  catch{const x=canvas.getContext('2d');x.fillStyle='#bc89ff';x.fillRect(20,30,24,50);x.fillStyle='#fff';x.fillRect(24,36,6,6);x.fillRect(34,36,6,6);}\r\n })();\r\n return canvas;\r\n}\r\n","pod/retro-rooms.css":"/* The original pixel world, with the current coach, Gala equipment and Helping Hand. */\r\n:root{--room-world:url('/pod/worlds/great-wall.webp');--room-pixel:ui-monospace,SFMono-Regular,Consolas,monospace}\r\nbody[data-screen=rest]{overflow:hidden;max-width:none}\r\nbody[data-screen=rest]>.ship-header,body[data-screen=rest]>.sync-badge{display:none}\r\n#restScreen{position:fixed;inset:var(--shell-top,14px) var(--shell-right,14px) var(--shell-bottom,var(--myr5-bar,88px)) var(--shell-left,14px);z-index:30;min-height:0;padding:0;overflow:hidden;display:block;background:#211b32;font-family:var(--room-pixel)}\r\n#restScreen .encounter{position:absolute;inset:0;margin:0;width:100%;height:100%;min-height:0;max-height:none;border:0;border-radius:0;isolation:isolate;background:#413251}\r\n.rest-world-backdrop,.rest-world-floor{position:absolute;inset:0;background:var(--room-world) center/100% 100% no-repeat;image-rendering:pixelated;pointer-events:none}\r\n.rest-world-backdrop{z-index:0}\r\n.rest-world-floor{z-index:4;clip-path:inset(79% 0 0)}\r\n#restScreen .encounter-grid,#restScreen .encounter-orbit{display:none}\r\n#restScreen .rest-coach-mount{position:absolute;inset:4% -6% auto;width:112%;height:76%;z-index:3;pointer-events:none}\r\n#restScreen .rest-coach-mount canvas{image-rendering:pixelated}\r\n#restScreen .coach-battle-platform{left:12%;right:12%;bottom:21%;height:34px;z-index:2;border-radius:0;border:0;clip-path:polygon(0 0,100% 0,96% 75%,72% 100%,28% 100%,4% 75%);background:var(--room-world) center bottom/100% auto;box-shadow:0 5px #171327}\r\n#restScreen .attack-target{inset:18% 0 23%;width:100%;z-index:8;border-radius:0;background:none!important;box-shadow:none!important}\r\n#restScreen .attack-target span{bottom:18%;font:700 14px/1.4 var(--room-pixel);color:#fff4c9;text-shadow:2px 2px #201329,-2px -2px #201329}\r\n#restScreen .player-platform{left:50%;bottom:15%;width:clamp(220px,60vw,580px);height:28px;z-index:6;transform:translateX(-50%)}\r\n#restScreen .platform-top{border-radius:0;clip-path:polygon(3% 0,97% 0,100% 100%,0 100%);border:3px solid #cad1ab;background:var(--room-world) center bottom/100% auto;box-shadow:0 -4px #e2d7a6}\r\n#restScreen .platform-edge{inset:12px 0 -18px;border-radius:0;clip-path:polygon(0 0,100% 0,88% 85%,65% 100%,35% 100%,12% 85%);border-color:#b0a69c;background:linear-gradient(#5a5263,#211c30);box-shadow:none}\r\n#restScreen .platform-thruster{display:none}\r\n#restScreen #restAvatar{left:calc(50% - 58px);bottom:8px;width:116px;height:122px;image-rendering:pixelated;filter:drop-shadow(3px 3px #211b31aa)}\r\n#restScreen .rest-hand{left:auto;right:4%;bottom:17%;width:clamp(110px,24vw,210px);height:clamp(125px,29vw,230px);z-index:7;pointer-events:none}\r\n#restScreen .player-name{bottom:10.5%;z-index:9;left:0;right:0;font:14px/1.3 var(--room-pixel);color:#fff5dc;text-shadow:2px 2px #211329;background:#1a152a88;padding:5px}\r\n#restScreen .rest-weapon-name{left:12px;bottom:24%;z-index:9;font:14px/1.3 var(--room-pixel);text-shadow:2px 2px #211329}\r\n#restScreen .rest-heading{position:absolute;z-index:12;top:0;left:0;right:0;padding:calc(10px + env(safe-area-inset-top)) 16px 12px;background:linear-gradient(#171023ee,#17102390,transparent)}\r\n#restScreen .rest-heading h1{font:700 22px/1.2 var(--room-pixel);letter-spacing:0;color:#fff1cf}\r\n#restScreen .eyebrow{font:12px/1.4 var(--room-pixel)}\r\n#restScreen button{font-family:var(--room-pixel);border-radius:2px;box-shadow:3px 3px #110d1c;min-height:44px}\r\n#restScreen .boss-status{top:calc(88px + env(safe-area-inset-top));left:16px;right:16px;z-index:10;background:#201832b3;border:2px solid #cab7ce;border-radius:0;font:14px/1.3 var(--room-pixel)}\r\n#restScreen .boss-health{border-radius:0;height:10px}\r\n#restScreen .rest-receipt{position:absolute;z-index:12;top:calc(146px + env(safe-area-inset-top));left:16px;right:16px;padding:6px 10px;background:#211830aa;font:14px/1.3 var(--room-pixel);color:#fff0dc}\r\n#restScreen .rest-bottom{position:absolute;bottom:0;left:0;right:0;z-index:12;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px calc(10px + env(safe-area-inset-bottom));border-top:2px solid #b19bb8;background:#1a152af2}\r\n#restScreen .rest-bottom>div:last-child{display:flex;align-items:center;gap:10px}\r\n#restScreen .rest-bottom button{font-size:14px;white-space:nowrap}\r\n#restScreen .rest-clock small{font-size:12px}\r\n#restScreen .rest-clock strong{font:700 26px/1.1 var(--room-pixel)}\r\n#restScreen .hand-team{position:absolute;z-index:11;left:16px;bottom:calc(96px + env(safe-area-inset-bottom));margin:0;max-width:60%;background:#211830dd;padding:8px;border:1px solid #b2d2b3;border-radius:0;font:12px/1.3 var(--room-pixel)}\r\nbody[data-screen=rest] .reward-pack-launch{bottom:calc(156px + env(safe-area-inset-bottom,0px))}\r\n#restScreen .hand-team #handTeamStatus{display:none}\r\n#restScreen .hand-team small{font-size:12px}\r\n#restScreen .rest-feedback{position:absolute;left:16px;right:16px;top:calc(185px + env(safe-area-inset-top));z-index:11;margin:0;min-height:0;text-align:center;font:14px/1.4 var(--room-pixel);text-shadow:2px 2px #211329;color:#fff5da;pointer-events:none}\r\n#restScreen .shield-note{position:absolute;right:16px;bottom:calc(97px + env(safe-area-inset-bottom));z-index:11;max-width:35%;margin:0;text-align:right;font:12px/1.3 var(--room-pixel);text-shadow:2px 2px #211329;color:#fff5da}\r\n#restScreen .damage-float{z-index:11;top:40%;font:900 clamp(26px,7vw,48px)/1 var(--room-pixel);text-shadow:3px 3px #341630}\r\n#restScreen .attack-flash{z-index:7;top:36%;left:47%}\r\n\r\n.meditation-panel{width:min(900px,100vw)!important;max-width:100vw;max-height:100dvh;padding:16px!important;border:3px solid #b2a0bb!important;border-radius:0!important;background:#201b32!important;font-family:var(--room-pixel)}\r\n.meditation-panel header h2,.meditation-entry strong{font:700 20px/1.3 var(--room-pixel)}\r\n.meditation-panel header button,.meditation-panel [data-breath-pause],.meditation-entry{border-radius:2px!important;font-family:var(--room-pixel);box-shadow:3px 3px #110d1c}\r\n.meditation-stage{height:clamp(320px,58dvh,620px);margin-top:14px;overflow:hidden;border:3px solid #99889f;background:var(--room-world) center/100% 100% no-repeat;image-rendering:pixelated}\r\n.meditation-stage::after{content:'';position:absolute;inset:0;background:#1b164122;z-index:0;pointer-events:none}\r\n.meditation-character{position:absolute!important;bottom:15%;left:calc(50% - 64px);width:128px;height:192px;z-index:3;padding:0!important;image-rendering:pixelated}\r\n.meditation-character canvas{width:128px;height:192px;max-width:none;image-rendering:pixelated}\r\n.meditation-platform{position:absolute;left:20%;right:20%;bottom:14%;height:24px;z-index:2;border-top:5px solid #e1d3ab;border-bottom:8px solid #352b43;background:var(--room-world) center bottom/100% auto;clip-path:polygon(3% 0,97% 0,100% 55%,85% 100%,15% 100%,0 55%)}\r\n.meditation-panel .breathing-ring{bottom:12%;left:calc(50% - 120px);width:240px;height:240px;z-index:1;border:3px solid #c2f0c5;box-shadow:0 0 0 4px #26344e,0 0 0 7px #b8e8c455;animation-timing-function:steps(24)}\r\n.meditation-speech{min-height:28px;margin:14px 0;font:16px/1.5 var(--room-pixel);color:#fff0cf}\r\n.meditation-panel [data-breath-pause]{display:block;margin:auto;min-width:110px;min-height:44px;font-size:14px}\r\n.meditation-panel .meditation-can{top:auto;bottom:25%;border-radius:0;box-shadow:3px 3px #221a36}\r\n@media(min-width:760px){#restScreen .rest-coach-mount{left:12%;width:76%;height:82%;top:-2%}#restScreen .rest-heading,#restScreen .rest-bottom{padding-left:max(24px,calc((100vw - 1000px)/2));padding-right:max(24px,calc((100vw - 1000px)/2))}.meditation-stage{background-size:100% 130%;background-position:center bottom}}\r\n@media(max-height:600px){#restScreen .rest-heading{padding-top:8px}#restScreen .eyebrow,#restScreen .rest-receipt,#restScreen .rest-feedback{display:none}#restScreen .boss-status{top:64px}#restScreen .rest-coach-mount{height:83%;top:-5%}#restScreen .player-platform{bottom:24%}#restScreen #restAvatar{width:90px;height:95px;left:calc(50% - 45px)}#restScreen .player-name{bottom:18%}#restScreen .hand-team{bottom:84px}#restScreen .shield-note{bottom:86px}#restScreen .rest-hand{bottom:23%;height:140px;width:130px}}\r\n@media(max-width:370px){#restScreen .rest-bottom{padding-left:10px;padding-right:10px;gap:7px}#restScreen .rest-bottom>div:last-child{gap:6px}#restScreen .rest-bottom button{font-size:13px;padding:8px}#restScreen .hand-team{max-width:56%}}\r\n#restScreen .slice-layer{position:absolute;inset:0;width:100%;height:100%;z-index:7;pointer-events:none;image-rendering:pixelated}\r\n","pod/rooms/cage/draco_wasm_wrapper.js":"var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n=\"undefined\"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;\r\n$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=[\"object\"==typeof globalThis&&globalThis,h,\"object\"==typeof window&&window,\"object\"==typeof self&&self,\"object\"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error(\"Cannot find global object\");};$jscomp.global=$jscomp.getGlobal(this);\r\n$jscomp.defineProperty=$jscomp.ASSUME_ES5||\"function\"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE=\"function\"===typeof Symbol&&\"symbol\"===typeof Symbol(\"x\");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX=\"$jscp$\";\r\nvar $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};\r\n$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(\".\");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};\r\n$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(\".\");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&\"es6\"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?\r\n$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+\"$\"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};\r\n$jscomp.polyfill(\"Promise\",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&\"undefined\"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf(\"[native code]\")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};\r\nvar p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();\r\ntry{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError(\"A Promise cannot resolve to itself\"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case \"object\":var q=null!=f;break a;case \"function\":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):\r\nthis.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}\"function\"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error(\"Cannot settle(\"+f+\", \"+q+\"): Promise already settled in state\"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();\r\nthis.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;\"undefined\"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if(\"undefined\"===typeof u)return!0;\"function\"===typeof f?f=new f(\"unhandledrejection\",{cancelable:!0}):\r\n\"function\"===typeof q?f=new q(\"unhandledrejection\",{cancelable:!0}):(f=$jscomp.global.document.createEvent(\"CustomEvent\"),f.initCustomEvent(\"unhandledrejection\",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();\r\nf.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return\"function\"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);\r\nbreak;case 2:q(A.result_);break;default:throw Error(\"Unexpected state: \"+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,\r\nF){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},\"es6\",\"es3\");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&\"function\"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};\r\n$jscomp.polyfill(\"Object.assign\",function(h){return h||$jscomp.assign},\"es6\",\"es3\");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError(\"The 'this' value for String.prototype.\"+k+\" must not be null or undefined\");if(n instanceof RegExp)throw new TypeError(\"First argument to String.prototype.\"+k+\" must not be a regular expression\");return h+\"\"};\r\n$jscomp.polyfill(\"String.prototype.startsWith\",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,\"startsWith\");n+=\"\";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},\"es6\",\"es3\");\r\n$jscomp.polyfill(\"Array.prototype.copyWithin\",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},\"es6\",\"es3\");\r\n$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill(\"Int8Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Uint8Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Uint8ClampedArray.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Int16Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");\r\n$jscomp.polyfill(\"Uint16Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Int32Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Uint32Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Float32Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");$jscomp.polyfill(\"Float64Array.prototype.copyWithin\",$jscomp.typedArrayCopyWithin,\"es6\",\"es5\");\r\nvar DracoDecoderModule=function(){var h=\"undefined\"!==typeof document&&document.currentScript?document.currentScript.src:void 0;\"undefined\"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d=\"\";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&\r\n63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c=\"\";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);\r\ne=\"Aborted(\"+e+\")\";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+\". Build with -sASSERTIONS for more info.\");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw\"both async and sync fetching of the wasm failed\";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if(\"function\"==typeof fetch&&!P.startsWith(\"file://\"))return fetch(P,{credentials:\"same-origin\"}).then(function(e){if(!e.ok)throw\"failed to load wasm binary file at '\"+P+\"'\";return e.arrayBuffer()}).catch(function(){return f(P)});\r\nif(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+\r\n12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+\r\n16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for(\"function\"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for(\"function\"==\r\ntypeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus(\"Running...\"),setTimeout(function(){setTimeout(function(){a.setStatus(\"\")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if(\"string\"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=\r\nd?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if(\"object\"===\r\ntypeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw\"cannot construct a VoidPtr, no constructor in IDL\";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=\r\nthis}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw\"cannot construct a Status, no constructor in IDL\";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=\r\nQa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a=\"undefined\"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&\"function\"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&\"function\"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if(\"string\"!==typeof e)return!1;e=e.split(\".\");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<\r\ne[1]?!1:!0};var Ta=Object.assign({},a),ta=\"object\"==typeof window,fa=\"function\"==typeof importScripts,Ua=\"object\"==typeof process&&\"object\"==typeof process.versions&&\"string\"==typeof process.versions.node,U=\"\";if(Ua){var Va=require(\"fs\"),pa=require(\"path\");U=fa?pa.dirname(U)+\"/\":__dirname+\"/\";var Wa=function(e,b){e=e.startsWith(\"file://\")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:\"utf8\")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,\r\nb,c){e=e.startsWith(\"file://\")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\\\/g,\"/\");process.argv.slice(2);a.inspect=function(){return\"[Emscripten Module object]\"}}else if(ta||fa)fa?U=self.location.href:\"undefined\"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf(\"blob:\")?U.substr(0,U.replace(/[?#].*/,\"\").lastIndexOf(\"/\")+1):\"\",Wa=function(e){var b=new XMLHttpRequest;b.open(\"GET\",\r\ne,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open(\"GET\",e,!1);b.responseType=\"arraybuffer\";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open(\"GET\",e,!0);d.responseType=\"arraybuffer\";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&\r\n(ea=a.wasmBinary);\"object\"!=typeof WebAssembly&&y(\"no native wasm support detected\");var ja,sa=!1,ra=\"undefined\"!=typeof TextDecoder?new TextDecoder(\"utf8\"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P=\"draco_decoder_gltf.wasm\";P.startsWith(\"data:application/octet-stream;base64,\")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y(\"\")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;\r\nfor(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}\r\nfunction c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da(\"failed to asynchronously prepare wasm: \"+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da(\"Module.instantiateWasm callback failed with error: \"+g),ka(g)}(function(){return ea||\"function\"!=typeof WebAssembly.instantiateStreaming||P.startsWith(\"data:application/octet-stream;base64,\")||\r\nP.startsWith(\"file://\")||Ua||\"function\"!=typeof fetch?c(b):fetch(P,{credentials:\"same-origin\"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da(\"wasm streaming compile failed: \"+t);da(\"falling back to ArrayBuffer instantiation\");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=\r\nfunction(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=\r\na.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=\r\na.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=\r\nfunction(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=\r\na.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=\r\nfunction(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=\r\nfunction(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=\r\na._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=\r\na.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=\r\na._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,\r\narguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=\r\na.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=\r\na.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=\r\na.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=\r\na._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=\r\nfunction(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=\r\na.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=\r\nfunction(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=\r\na.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=\r\nfunction(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=\r\na.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=\r\nfunction(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=\r\na.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,\r\narguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=\r\nfunction(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=\r\na.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},\r\noc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=\r\nfunction(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=\r\na._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=\r\na.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=\r\na.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=\r\na.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=\r\na.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=\r\nfunction(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=\r\na.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=\r\na.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=\r\na.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=\r\na.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=\r\na._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=\r\na.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=\r\nfunction(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=\r\na.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=\r\nfunction(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};\r\na._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for(\"function\"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,\r\nc){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw\"Error: Cannot destroy object. (Did you create it yourself?)\";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=\r\nr.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=\r\nX;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();\"object\"==typeof b&&(b=Z(b));c&&\"object\"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);\r\nQ.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=\r\nx;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=\r\nx.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=\r\nD;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=\r\nObject.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=\r\nH;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=\r\nfunction(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=\r\nC.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Gb(c,\r\nb)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};\r\nK.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=\r\nL;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Sb(c,b)};\r\nM.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};\r\nO.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=\r\nz;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===\r\ntypeof c?c.ptr:R(c);d&&\"object\"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;\r\nb&&\"object\"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=\r\nthis.ptr;r.prepare();\"object\"==typeof b&&(b=Z(b));c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();\"object\"==typeof b&&(b=Z(b));c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&\r\n(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&\"object\"===typeof b&&(b=b.ptr);c=c&&\"object\"===typeof c?c.ptr:R(c);d=d&&\"object\"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=\r\nm.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=\r\nfunction(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);\r\nreturn sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=\r\nfunction(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;\r\nb&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=\r\nb.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&\r\n(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===\r\ntypeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);d&&\"object\"===typeof d&&(d=d.ptr);g&&\"object\"===typeof g&&(g=g.ptr);t&&\"object\"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);Fc(c,\r\nb)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&\"object\"===typeof b&&(b=b.ptr);c&&\"object\"===\r\ntypeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=\r\n$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if(\"function\"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);\r\nif(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();\"object\"===typeof exports&&\"object\"===typeof module?module.exports=DracoDecoderModule:\"function\"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):\"object\"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);\r\n","pod/set-flow.mjs":"// Workout progress is earned by completed tracked sets. Rest taps never earn XP.\r\nexport const XP_PER_SET=25,XP_PER_LEVEL=100;\r\nexport const REST_IDLE_MS=3000;\r\nimport {weaponDamage,dayAt} from '../combat.mjs';\r\nimport {tapDamage,kitPetDps,bossHp,dailyCap,specialBudget,SPECIAL_LEVEL,BOSS_ATTACK_EVERY_HITS,REST_SECONDS} from '../combat-config.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nimport {PerformanceWorkout} from './performance-workout.mjs';\r\nimport {cosmeticLevel} from '../progression-rules.mjs';\r\nimport {AbilityCooldown} from './weapon-evolution.mjs';\r\nexport {bossHp as bossHealthMax} from '../combat-config.mjs';\r\nexport const shouldStartRestTargets=(phase,preparationRest)=>phase==='rest'||preparationRest===true;\r\nexport const DEFAULT_GOALS={...Object.fromEntries(Object.values(EXERCISES).map(m=>[m.id,m.defaultGoal])),squat:3,pushup:3,tree:9,warrior:9,horse:9,boxing:9,jogging:3,jumping:3};\r\nexport const valueOf=m=>m.kind==='hold'?m.totalHold:m.kind==='pace'?(m.active??0):m.count;\r\nexport function readProgress(raw){\r\n // circuit is a cached mirror of the server-computed daily-circuit payload (circuit.mjs),\r\n // kept alongside completedSets so the circuit meter can render on reopen before the next\r\n // myr5:account-progress event lands (Rank 6: \"progress survives app close/reopen mid-circuit\").\r\n try{const p=typeof raw==='string'?JSON.parse(raw):raw;if(p?.version===1&&Number.isSafeInteger(p.completedSets)&&p.completedSets>=0)return {version:1,completedSets:Math.min(p.completedSets,1000000),circuit:p.circuit&&typeof p.circuit==='object'?p.circuit:null};}catch{}\r\n return {version:1,completedSets:0,circuit:null};\r\n}\r\nexport class SetFlow {\r\n constructor(progress=null,{cooldown=null,now=Date.now()}={}){this.progress=readProgress(progress);this.phase='pod';this.sequence=0;this.active=null;this.preview=false;this.restUntil=0;this.hits=0;this.damage=0;this.lastTap=-Infinity;this.lastRestInteraction=-Infinity;this.abilities=new AbilityCooldown(cooldown,now);\r\n  // Battle-pass kit level (D8/D22), 1-5: pod.mjs sets it from battle-pass.mjs (combatLevel)\r\n  // for the track of the set just finished; 1 (the weakest kit) until then.\r\n  this.kitLevel=1;\r\n  // Boss HP and the daily tap-damage cap are per calendar day, not per rest\r\n  // bout, so one boss fight can span all 3 rests of a workout (D20).\r\n  this.damageDay=dayAt(now);this.tapDamageToday=0;this.specialDamageToday=0;this.lastPetTick=null;\r\n }\r\n get xp(){return this.performanceXp??this.progress.completedSets*XP_PER_SET;}\r\n get level(){return this.performanceXp!==undefined?cosmeticLevel(this.xp).level:1+Math.floor(this.xp/XP_PER_LEVEL);}\r\n get coachHealth(){return Math.max(0,bossHp(this.kitLevel??1)-this.damage);}\r\n resetIfNewDay(now){const day=dayAt(now);if(this.damageDay!==day){this.damageDay=day;this.damage=0;this.tapDamageToday=0;this.specialDamageToday=0;}}\r\n // Pet DPS (D22) accrues for the time since the last tap/rest-start, capped\r\n // at 30s so a long idle gap can't award a lump of pet damage.\r\n // ponytail: only ticks on tap(), so an idle pet (no taps) deals no damage —\r\n // add a rest-phase timer if a truly continuous idle tick matters later.\r\n petTick(now){const dps=kitPetDps(this.kitLevel??1);const last=this.lastPetTick??now;this.lastPetTick=now;return dps>0?dps*Math.max(0,Math.min(30,(now-last)/1000)):0;}\r\n touchRest(now=Date.now()){if(this.phase==='rest')this.lastRestInteraction=Math.max(this.lastRestInteraction,now);}\r\n shouldEndRest(now=Date.now()){return this.phase==='rest'&&now>=Math.max(this.restUntil,this.lastRestInteraction)+REST_IDLE_MS;}\r\n get attackDamage(){return weaponDamage(this.combat,this.weapon);}\r\n start(mode,goal=DEFAULT_GOALS[mode],restSeconds=REST_SECONDS,options={}){\r\n  if(!Object.hasOwn(DEFAULT_GOALS,mode))throw new Error('Unknown exercise');\r\n  this.active={id:++this.sequence,mode,goal:Math.max(1,Number(goal)||DEFAULT_GOALS[mode]),restSeconds:Math.max(15,Math.min(180,Number(restSeconds)||REST_SECONDS))};this.phase='set';this.preview=false;this.workout=options.performance?new PerformanceWorkout({mode,kind:EXERCISES[mode]?.kind??(['tree','warrior','horse'].includes(mode)?'hold':'reps'),...options}):null;if(this.workout)this.active.restSeconds=options.target?this.active.restSeconds:30;return this.active;\r\n }\r\n consume(m,now){\r\n  if(this.phase!=='set'||!this.active||m.mode!==this.active.mode)return null;\r\n  if(this.workout){const result=this.workout.update(m,now);return result?this.completePerformance(result,now,m.name):null;}\r\n  const reached=valueOf(m)>=this.active.goal;\r\n  if(!reached)return null;\r\n  // Timed boxing needs observed hand movement, not simply an unattended timer.\r\n  const earned=m.kind!=='pace'||m.active>0;\r\n  if(earned)this.progress.completedSets++;\r\n  this.resetIfNewDay(now);\r\n  this.phase='rest';this.preview=false;this.restUntil=now+this.active.restSeconds*1000;this.hits=0;this.lastTap=-Infinity;this.lastRestInteraction=-Infinity;this.lastPetTick=now;\r\n  return {mode:m.mode,name:m.name,value:valueOf(m),goal:this.active.goal,earned,xp:earned?XP_PER_SET:0,level:this.level,set:this.progress.completedSets};\r\n }\r\n completePerformance(result,now,name){\r\n  this.resetIfNewDay(now);this.phase='rest';this.preview=false;this.restUntil=now+this.active.restSeconds*1000;this.hits=0;this.lastTap=-Infinity;this.lastRestInteraction=-Infinity;this.lastPetTick=now;\r\n  return {...result,name:name??EXERCISES[result.mode]?.name??result.mode,goal:this.active.goal,xp:result.xpBase,level:this.level,set:this.progress.completedSets+1};\r\n }\r\n finishWorking(m,now=Date.now()){\r\n  if(this.phase!=='set'||!this.workout)return null;const result=this.workout.update(m,now)??this.workout.finish();return result?this.completePerformance(result,now,m.name):null;\r\n }\r\n previewRest(now,seconds=REST_SECONDS){this.resetIfNewDay(now);this.phase='rest';this.preview=true;this.restUntil=now+seconds*1000;this.hits=0;this.lastTap=-Infinity;this.lastRestInteraction=-Infinity;this.lastPetTick=now;}\r\n remaining(now){return Math.max(0,Math.ceil((this.restUntil-now)/1000));}\r\n extend(seconds=30,now=Date.now()){if(this.phase==='rest')this.restUntil=Math.max(this.restUntil,now)+seconds*1000;}\r\n tap(now,withHand=false){\r\n  this.touchRest(now);\r\n  if(this.phase!=='rest'||now-this.lastTap<180)return null;\r\n  this.resetIfNewDay(now);this.beforeRestAttack?.(now);if(this.restBossId&&this.coachHealth<=0)return null;\r\n  const level=this.kitLevel??1;\r\n  if(this.tapDamageToday>=dailyCap(level))return null; // daily cap reached: no further damage events\r\n  this.lastTap=now;this.hits++;\r\n  const assisted=withHand&&this.hits%3===0,warmth=this.workout?(this.workout.stage==='preparation-rest'?(this.workout.nextStage==='working'?2:1):3):1,damage=tapDamage(level,this.combat,now)*warmth+this.petTick(now);\r\n  this.tapDamageToday+=Math.min(damage,this.restBossId?this.coachHealth:damage);this.damage+=damage;this.onRestDamage?.(now);\r\n  return {hits:this.hits,damage,totalDamage:this.damage,blocked:damage===0,assisted,charge:withHand?this.hits%3:0,bossAttack:this.hits%BOSS_ATTACK_EVERY_HITS===0};\r\n }\r\n preparationTap(now=Date.now(),withHand=false){if(this.phase!=='set'||this.workout?.stage!=='preparation-rest')return null;this.phase='rest';try{return this.tap(now,withHand);}finally{this.phase='set';}}\r\n special(weapon,{now=Date.now(),progress,catalog}={}){this.touchRest(now);this.resetIfNewDay(now);const level=this.kitLevel??1;if(this.phase==='rest'){this.beforeRestAttack?.(now);if(this.restBossId&&this.coachHealth<=0)return {ok:false,reason:'boss-defeated'};}\r\n if(level<SPECIAL_LEVEL)return {ok:false,reason:'level',unlockLevel:SPECIAL_LEVEL}; // D17: specials unlock at L3\r\n if(this.tapDamageToday>=dailyCap(level))return {ok:false,reason:'daily-cap'}; // both checked before activate() so a refused special never burns its cooldown\r\n const result=this.abilities.activate(weapon,{now,progress,catalog,inRest:this.phase==='rest'});if(!result.ok)return result;\r\n // Legacy weapon-tier damage, capped by the day's special budget (combat-config SPECIAL_DAMAGE_FRACTION).\r\n const damage=Math.min(weaponDamage(this.combat,weapon,now),Math.max(0,specialBudget(level)-this.specialDamageToday));this.specialDamageToday+=damage;this.damage+=damage;this.onRestDamage?.(now);return {...result,special:true,assisted:false,damage,totalDamage:this.damage,blocked:damage===0,hits:this.hits};}\r\n leave(){this.phase='pod';this.active=null;}\r\n}\r\n","pod/set-transition.mjs":"// #150 (Ian, D46): a finished set goes into rest through the wormhole and comes back out through it, slow enough to\r\n// load under it and to put the phone down or pick it up. Each trip is one pair: 'in' covers the screen, swap() changes\r\n// what is under it, 'out' opens it again (playWormhole('in') leaves its cover up until 'out' removes it).\r\n// Never over the live camera (D24); without the portal or with reduced motion, a plain fade.\r\nexport const TRANSITION_MIN_MS=2600; // the one knob: how long each trip keeps the screen moving/covered\r\nexport const FADE_MS=600;\r\nexport const LINES={rest:'Set complete — put the phone down, rest starts in a moment',practice:'Rest starts in a moment',next:'Next set — pick up your phone',home:'Leaving rest'};\r\nconst reducedMotion=()=>!!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\r\nfunction cover(text,fade){\r\n const el=document.createElement('div');el.className='set-transition';el.setAttribute('role','status');el.textContent=text;\r\n el.style.cssText=`position:fixed;inset:0;margin:0;padding:0 24px 22vh;border:0;width:auto;height:auto;max-width:none;max-height:none;display:flex;align-items:flex-end;justify-content:center;text-align:center;box-sizing:border-box;pointer-events:none;z-index:10001;color:#fff;font:600 17px/1.35 system-ui,sans-serif;text-shadow:0 1px 6px #000,0 0 14px #000;background:${fade?'#000':'transparent'}`;\r\n document.body.append(el);if(el.showPopover){el.setAttribute('popover','manual');el.showPopover();}\r\n return el;\r\n}\r\nconst fadeTo=(el,to)=>el.animate([{opacity:1-to},{opacity:to}],{duration:FADE_MS/2,easing:'ease',fill:'forwards'}).finished.catch(()=>{});\r\n// Resolves once the new screen is showing; swap()'s error (if any) is rethrown after the cover is gone.\r\nexport async function throughWormhole(text,swap){\r\n const body=document.body;\r\n if(body.dataset.tracking==='true'||body.dataset.cameraWorkout==='true')return swap();\r\n const portal=globalThis.myr5Portal,play=!reducedMotion()&&!portal?.disposed&&typeof portal?.playWormhole==='function'?portal.playWormhole:null;\r\n const half=TRANSITION_MIN_MS/2,wasInert=body.inert;body.inert=true; // the tunnel lets taps through; nothing under it is live\r\n let el=null;\r\n try{\r\n  if(play){const going=play({direction:'in',minMs:half});el=cover(text,false);await going;}\r\n  else{el=cover(text,true);await fadeTo(el,1);}\r\n  await swap();\r\n }finally{\r\n  try{if(play)await play({direction:'out',minMs:half});else if(el)await fadeTo(el,0);}catch{}\r\n  el?.remove();body.inert=wasInert;\r\n }\r\n}\r\n","pod/ship-backdrop.mjs":"import {requestPhoneMotion,createTiltLook} from '../character-phone-sensor.mjs';\r\n/** Keep the decorative pod scene moving only while the pod page is the visible workspace. */\r\nexport function mountShipBackdropMotion({root=document.getElementById('podShipBackdrop'),body=document.body,doc=document}={}){\r\n if(!root||!body||!doc)return ()=>{};\r\n const reduce=doc.defaultView?.matchMedia?.('(prefers-reduced-motion: reduce)');\r\n const view=doc.defaultView,tiltKey='myr5.tiltPermission';\r\n let tiltFrame=0,nx=0,ny=0,bx=0,by=0,cx=0,cy=0,bt=0,ct=0,lastFrame=0,sensorActive=false;\r\n const look=createTiltLook(view);\r\n const clearTilt=()=>{nx=ny=bx=by=cx=cy=bt=ct=0;body.style.setProperty('--pod-bg-x','0px');body.style.setProperty('--pod-bg-y','0px');body.style.setProperty('--pod-bg-tilt','0deg');body.style.setProperty('--pod-controls-x','0px');body.style.setProperty('--pod-controls-y','0px');body.style.setProperty('--pod-controls-tilt','0deg');};\r\n const tickTilt=now=>{\r\n  tiltFrame=0;if(root.dataset.motionRunning!=='true'){lastFrame=0;clearTilt();return;}\r\n  const elapsed=lastFrame?Math.min(100,Math.max(0,now-lastFrame)):16;lastFrame=now;\r\n  const bg=1-Math.exp(-elapsed/120),controls=1-Math.exp(-elapsed/360);\r\n  bx+=(nx*24-bx)*bg;by+=(ny*24-by)*bg;bt+=(nx*12-bt)*bg;\r\n  cx+=(nx*4-cx)*controls;cy+=(ny*4-cy)*controls;ct+=(nx*1.5-ct)*controls;\r\n  body.style.setProperty('--pod-bg-x',bx.toFixed(2)+'px');body.style.setProperty('--pod-bg-y',by.toFixed(2)+'px');body.style.setProperty('--pod-bg-tilt',bt.toFixed(2)+'deg');\r\n  body.style.setProperty('--pod-controls-x',cx.toFixed(2)+'px');body.style.setProperty('--pod-controls-y',cy.toFixed(2)+'px');body.style.setProperty('--pod-controls-tilt',ct.toFixed(2)+'deg');\r\n  if(Math.abs(nx*24-bx)+Math.abs(ny*24-by)+Math.abs(nx*4-cx)+Math.abs(ny*4-cy)>.08)tiltFrame=requestAnimationFrame(tickTilt);\r\n };\r\n const kickTilt=()=>{if(!tiltFrame&&root.dataset.motionRunning==='true')tiltFrame=requestAnimationFrame(tickTilt);};\r\n const pointerMove=event=>{if(root.dataset.motionRunning!=='true')return;const w=Math.max(1,doc.documentElement.clientWidth),h=Math.max(1,doc.documentElement.clientHeight);nx=Math.max(-1,Math.min(1,(event.clientX/w-.5)*2));ny=Math.max(-1,Math.min(1,(event.clientY/h-.5)*2));kickTilt();};\r\n const pointerLeave=()=>{nx=ny=0;kickTilt();};\r\n const orientation=event=>{\r\n  if(root.dataset.motionRunning!=='true')return;\r\n  const t=look.read(event,view.performance.now());if(!t)return;\r\n  nx=t.x;ny=t.y;kickTilt();\r\n };\r\n const startSensor=()=>{\r\n  if(sensorActive||!view?.DeviceOrientationEvent)return;\r\n  view.addEventListener('deviceorientation',orientation);sensorActive=true;look.reset();\r\n };\r\n const stopSensor=()=>{if(sensorActive)view?.removeEventListener('deviceorientation',orientation);sensorActive=false;look.reset();};\r\n const requestSensor=async()=>{\r\n  const api=view?.DeviceOrientationEvent;\r\n  if(!api?.requestPermission)return;\r\n  let answer='denied';try{answer=await requestPhoneMotion()?'granted':'denied';}catch{}\r\n  try{view.localStorage.setItem(tiltKey,answer==='granted'?'granted':'denied');}catch{}\r\n  if(answer==='granted'){startSensor();sensorButton?.remove();}\r\n };\r\n const sensorButton=view?.DeviceOrientationEvent?.requestPermission?doc.createElement('button'):null;\r\n if(sensorButton){sensorButton.type='button';sensorButton.className='pod-tilt-request';sensorButton.textContent='Enable tilt';sensorButton.setAttribute('aria-label','Enable device tilt to look around');sensorButton.addEventListener('click',requestSensor);doc.querySelector('.ship-header')?.append(sensorButton);}\r\n const active=()=>{\r\n  const workoutHome=doc.getElementById('portalWorkoutHome');\r\n  const portalHome=doc.getElementById('portalHome');\r\n  const portalShowing=!!portalHome&&!portalHome.hidden&&!workoutHome?.open;\r\n  const covered=portalShowing||[...doc.querySelectorAll('dialog[open]')].some(dialog=>dialog.id!=='portalWorkoutHome');\r\n  const onPod=body.dataset.screen==='pod'&&body.dataset.tracking!=='true'&&!covered;\r\n  const running=onPod&&!doc.hidden&&!reduce?.matches;\r\n  root.dataset.motionRunning=String(running);\r\n  root.dataset.visible=String(onPod);\r\n  if(running){\r\n   let stored='';try{stored=view.localStorage.getItem(tiltKey)||'';}catch{}\r\n   if(!view?.DeviceOrientationEvent?.requestPermission||stored==='granted')startSensor();\r\n  }else{stopSensor();nx=ny=0;lastFrame=0;cancelAnimationFrame(tiltFrame);tiltFrame=0;clearTilt();}\r\n };\r\n const routeObserver=new MutationObserver(active);\r\n routeObserver.observe(body,{attributes:true,attributeFilter:['data-screen','data-tracking','open','hidden'],subtree:true});\r\n // Runtime portal dialogs are attached directly to body; this small observer detects their insertion.\r\n const modalObserver=new MutationObserver(active);\r\n modalObserver.observe(body,{childList:true});\r\n doc.addEventListener('visibilitychange',active);\r\n doc.addEventListener('pointermove',pointerMove,{passive:true});doc.defaultView?.addEventListener('blur',pointerLeave);\r\n reduce?.addEventListener?.('change',active);\r\n  const onPageHide=event=>{if(!event.persisted)cleanup();else root.dataset.motionRunning='false';};\r\n const onPageShow=()=>active();\r\n view?.addEventListener('pagehide',onPageHide);\r\n view?.addEventListener('pageshow',onPageShow);\r\n active();\r\n function cleanup(){\r\n  routeObserver.disconnect();modalObserver.disconnect();doc.removeEventListener('visibilitychange',active);\r\n  doc.removeEventListener('pointermove',pointerMove);view?.removeEventListener('blur',pointerLeave);stopSensor();sensorButton?.remove();cancelAnimationFrame(tiltFrame);tiltFrame=0;lastFrame=0;clearTilt();\r\n  reduce?.removeEventListener?.('change',active);\r\n  view?.removeEventListener('pagehide',onPageHide);view?.removeEventListener('pageshow',onPageShow);\r\n  root.dataset.motionRunning='false';root.dataset.visible='false';\r\n }\r\n return cleanup;\r\n}\r\n","pod/ship-interior.css":"/* R16: decorative ship interior behind the existing pod controls and character. */\r\nbody[data-screen=\"pod\"]{background:transparent!important}\r\nbody[data-screen=\"pod\"]>.ship-header,\r\nbody[data-screen=\"pod\"]>#homeScreen,\r\n#portalWorkoutHome>.ship-header,\r\n#portalWorkoutHome>#homeScreen{position:relative;z-index:1}\r\nbody[data-screen=\"pod\"]>.ship-header,#portalWorkoutHome>.ship-header,\r\nbody[data-screen=\"pod\"]>#homeScreen,#portalWorkoutHome>#homeScreen{translate:var(--pod-controls-x,0px) var(--pod-controls-y,0px);rotate:var(--pod-controls-tilt,0deg)}\r\n\r\n.pod-ship-backdrop{\r\n position:fixed;z-index:0;inset:0 auto auto 50%;transform:translateX(-50%);\r\n width:min(100vw,640px);height:100dvh;overflow:hidden;isolation:isolate;\r\n background:#100b18;pointer-events:none;user-select:none;-webkit-user-select:none;\r\n visibility:hidden;\r\n}\r\nbody[data-screen=\"pod\"] .pod-ship-backdrop[data-visible=\"true\"]{visibility:visible}\r\nbody .pod-ship-backdrop[data-visible=\"false\"]{visibility:hidden}\r\n#portalWorkoutHome.portal-framed.portal-fullscreen{\r\n display:flex!important;flex-direction:column;overflow:hidden;background:transparent!important;\r\n padding:0!important; /* R20: the dock sits below the housing now, so the room reserves no band for it */\r\n}\r\n#portalWorkoutHome>.pod-ship-backdrop{position:absolute;inset:0;width:auto;height:auto;left:0;transform:none;z-index:0;border-radius:inherit}\r\n#portalWorkoutHome>#homeScreen{flex:1 1 auto;min-height:0;height:auto;overflow:hidden}\r\n#portalWorkoutHome>.ship-header{flex:none}\r\n.pod-ship-backdrop img,.pod-ship-lights{position:absolute;display:block;inset:0;width:100%;height:100%;pointer-events:none}\r\n.pod-ship-nebula{\r\n inset:auto!important;left:50%!important;top:50%!important;\r\n width:max(160%,160dvh)!important;height:max(160%,160dvh)!important;max-width:none;\r\n object-fit:cover;translate:calc(-50% + var(--pod-bg-x,0px)) calc(-50% + var(--pod-bg-y,0px));rotate:var(--pod-bg-tilt,0deg);transform-origin:center;\r\n animation:pod-nebula-drift 300s linear infinite;will-change:transform;z-index:0;\r\n}\r\n.pod-ship-cutout{z-index:2;object-fit:fill}\r\n/* Reveal the retained nebula's warm filaments in narrow portrait gutters. */\r\n@media(max-width:430px) and (orientation:portrait){\r\n .pod-ship-nebula{width:max(110%,110dvh)!important;height:max(110%,110dvh)!important}\r\n}\r\n.pod-ship-lights{\r\n z-index:3;opacity:.46;mix-blend-mode:screen;\r\n background:\r\n  radial-gradient(ellipse 8% 2.1% at 50% 7%,#ffe2a033 0,transparent 75%),\r\n  radial-gradient(ellipse 7% 3% at 5% 18%,#ffdfa01a 0,transparent 78%),\r\n  radial-gradient(ellipse 7% 3% at 95% 18%,#ffdfa01a 0,transparent 78%),\r\n  radial-gradient(ellipse 8% 2.5% at 50% 91%,#e5c9ff20 0,transparent 78%);\r\n}\r\n.pod-ship-lights:before,.pod-ship-lights:after{content:\"\";position:absolute;inset:0;pointer-events:none}\r\n.pod-ship-lights:before{\r\n background:\r\n  radial-gradient(ellipse 5.4% 2.2% at 36% 7.7%,#171022 0 35%,transparent 76%),\r\n  radial-gradient(ellipse 5.4% 2.2% at 64% 7.7%,#171022 0 35%,transparent 76%),\r\n  radial-gradient(ellipse 4.5% 3.2% at 5% 16%,#171022 0 32%,transparent 78%),\r\n  radial-gradient(ellipse 4.5% 3.2% at 95% 16%,#171022 0 32%,transparent 78%),\r\n  radial-gradient(ellipse 4.3% 2.6% at 5% 80%,#171022 0 32%,transparent 78%),\r\n  radial-gradient(ellipse 4.3% 2.6% at 95% 80%,#171022 0 32%,transparent 78%);\r\n mix-blend-mode:multiply;opacity:.05;animation:pod-lamp-dim 11.8s ease-in-out infinite;\r\n}\r\n.pod-ship-lights:after{\r\n background:\r\n  radial-gradient(ellipse 5.4% 2.2% at 36% 7.7%,#ffe6a3d9 0,transparent 75%),\r\n  radial-gradient(ellipse 5.4% 2.2% at 64% 7.7%,#ffe6a3d9 0,transparent 75%),\r\n  radial-gradient(ellipse 4.5% 3.2% at 5% 16%,#ffe5aad0 0,transparent 76%),\r\n  radial-gradient(ellipse 4.5% 3.2% at 95% 16%,#ffe5aad0 0,transparent 76%),\r\n  radial-gradient(ellipse 4.3% 2.6% at 5% 80%,#ffe6b9bd 0,transparent 76%),\r\n  radial-gradient(ellipse 4.3% 2.6% at 95% 80%,#ffe6b9bd 0,transparent 76%);\r\n mix-blend-mode:screen;opacity:.02;animation:pod-lamp-glow 13.4s ease-in-out infinite;\r\n}\r\n\r\n/* The hardware artwork is a transparent hologram plate, so the live interface remains the foreground. */\r\nbody[data-screen=\"pod\"]>.ship-header,\r\n#portalWorkoutHome>.ship-header{\r\n --pod-header-gap:clamp(12px,2dvh,20px);\r\n height:calc(72px - var(--pod-header-gap));margin:var(--pod-header-gap) 10px 0;padding:4px 16px;\r\n border:1px solid #c5a4db88;border-radius:13px;\r\n background:repeating-linear-gradient(0deg,#f5e4ff05 0 1px,transparent 1px 4px),linear-gradient(115deg,#39234a66,#291b3f59 55%,#4a2d5c5c);\r\n box-shadow:inset 0 1px #f5e3ff30,inset 0 -1px #a777ce48,0 0 0 1px #24152d99,0 0 22px #b36cdb32;\r\n backdrop-filter:blur(4px) saturate(1.14);-webkit-backdrop-filter:blur(4px) saturate(1.14);\r\n}\r\nbody[data-screen=\"pod\"]>.ship-header:before,\r\n#portalWorkoutHome>.ship-header:before{\r\n content:\"\";position:absolute;inset:3px;pointer-events:none;opacity:.8;filter:drop-shadow(0 0 2px #b877ed);\r\n background:\r\n  linear-gradient(#edd4ff,#edd4ff) left top/14px 1px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) left top/1px 10px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) right top/14px 1px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) right top/1px 10px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) left bottom/14px 1px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) left bottom/1px 10px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) right bottom/14px 1px no-repeat,\r\n  linear-gradient(#edd4ff,#edd4ff) right bottom/1px 10px no-repeat;\r\n}\r\nbody[data-screen=\"pod\"]>.ship-header>*,\r\n#portalWorkoutHome>.ship-header>*{position:relative;z-index:1}\r\nbody[data-screen=\"pod\"]>.ship-header .icon-button,\r\n#portalWorkoutHome>.ship-header .icon-button{\r\n border-color:#bd96d377;background:linear-gradient(145deg,#513561aa,#251a34cc);\r\n box-shadow:inset 0 1px #f6e7ff35,0 0 12px #ba7cdb30;color:#f5e6ff;\r\n}\r\n.pod-tilt-request{min-height:40px;padding:0 9px;border:1px solid #bd96d377;border-radius:9px;background:#251a34cc;color:#f5e6ff;font:600 11px system-ui,sans-serif;touch-action:manipulation}\r\n.pod-tilt-request:focus-visible{outline:3px solid #ffb24d;outline-offset:2px}\r\n\r\n@keyframes pod-nebula-drift{\r\n from{transform:rotate(0deg)}\r\n to{transform:rotate(360deg)}\r\n}\r\n@keyframes pod-lamp-dim{\r\n 0%,62%,100%{opacity:.04}\r\n 64%{opacity:.44}\r\n 66%{opacity:.38}\r\n 68%{opacity:.06}\r\n 70%{opacity:.3}\r\n 72%{opacity:.04}\r\n}\r\n@keyframes pod-lamp-glow{\r\n 0%,66%,100%{opacity:.02}\r\n 68%{opacity:.24}\r\n 70%{opacity:.5}\r\n 72%{opacity:.14}\r\n 74%{opacity:.02}\r\n}\r\n@media(prefers-reduced-motion:reduce){\r\n .pod-ship-nebula,.pod-ship-lights{animation:none!important;will-change:auto}\r\n .pod-ship-lights:before,.pod-ship-lights:after{animation:none!important;opacity:0!important}\r\n}\r\n@media(max-height:700px) and (max-width:600px) and (orientation:portrait){\r\n #homeScreen .pod-header-actions{padding-top:2px;padding-bottom:0}\r\n #homeScreen .deck-strip{height:32px}\r\n #homeScreen #view{min-height:100px}\r\n #homeScreen #view .coach-line{top:2px;padding:1px 5px}\r\n #homeScreen #view .coach-line p{font-size:11px;line-height:1.1;-webkit-line-clamp:1;white-space:nowrap}\r\n #homeScreen #view .home-character{inset:24px 8px 4px}\r\n #homeScreen .workout-console{padding-top:8px;padding-bottom:6px}\r\n}\r\n#podShipBackdrop[data-motion-running=\"false\"] .pod-ship-nebula,\r\n#podShipBackdrop[data-motion-running=\"false\"] .pod-ship-lights,\r\n#podShipBackdrop[data-motion-running=\"false\"] .pod-ship-lights:before,\r\n#podShipBackdrop[data-motion-running=\"false\"] .pod-ship-lights:after{animation-play-state:paused}\r\n","pod/weapon-abilities.css":"#restWeaponFx{position:absolute;inset:0;width:100%;height:100%;z-index:7;pointer-events:none;image-rendering:pixelated}\r\n#restScreen .weapon-evolution .projectile,#restScreen .weapon-evolution .attack-flash{display:none}\r\n#restScreen .weapon-ability{position:absolute;z-index:13;left:12px;top:46%;bottom:auto;transform:translateY(-50%);width:clamp(80px,18vw,104px);aspect-ratio:1;padding:0;background:#181425ed;border:2px solid #87c9c1;box-shadow:4px 4px 0 #090c17}\r\n#restScreen .weapon-ability button{display:block;width:100%;height:100%;min-height:44px;margin:0;border:0;border-radius:0;color:#c2fff1;background:#233e44;box-shadow:none;font:700 .875rem/1.25 var(--room-pixel);padding:8px 6px 14px;overflow-wrap:anywhere}\r\n#restScreen .weapon-ability button:disabled{opacity:1;background:#232c3c;color:#98aebf;cursor:default}\r\n#restScreen .weapon-ability button:focus-visible{outline:3px solid #fff1c8;outline-offset:3px}\r\n#restScreen .weapon-ability progress{position:absolute;left:0;bottom:0;width:100%;height:6px;display:block;border:0;background:#203044;accent-color:#9af5df;pointer-events:none}\r\n#restScreen .weapon-ability progress::-webkit-progress-bar{background:#203044}\r\n#restScreen .weapon-ability progress::-webkit-progress-value{background:#9af5df}\r\n@media(max-height:600px){#restScreen .weapon-ability{left:8px;top:48%;width:80px}}\r\n","pod/weapon-animator.mjs":"import {evolution} from './weapon-evolution.mjs';\r\n\r\nconst TAU = Math.PI * 2;\r\nconst clamp = value => Math.max(0, Math.min(1, value));\r\nconst point = (x, y) => [Math.round(x), Math.round(y)];\r\n\r\nfunction polygon(ctx, vertices, color, alpha = 1) {\r\n  ctx.save(); ctx.globalAlpha *= alpha; ctx.fillStyle = color; ctx.beginPath();\r\n  vertices.forEach(([x, y], i) => i ? ctx.lineTo(Math.round(x), Math.round(y)) : ctx.moveTo(Math.round(x), Math.round(y)));\r\n  ctx.closePath(); ctx.fill(); ctx.restore();\r\n}\r\nfunction line(ctx, vertices, color, width = 2, alpha = 1) {\r\n  ctx.save(); ctx.globalAlpha *= alpha; ctx.strokeStyle = color; ctx.lineWidth = width; ctx.lineJoin = 'miter'; ctx.beginPath();\r\n  vertices.forEach(([x, y], i) => i ? ctx.lineTo(Math.round(x), Math.round(y)) : ctx.moveTo(Math.round(x), Math.round(y)));\r\n  ctx.stroke(); ctx.restore();\r\n}\r\nfunction arc(ctx, x, y, radius, from, to, color, width = 2, alpha = 1) {\r\n  const count = Math.max(5, Math.ceil(Math.abs(to - from) * 5));\r\n  line(ctx, Array.from({length: count + 1}, (_, i) => point(x + Math.cos(from + (to - from) * i / count) * radius, y + Math.sin(from + (to - from) * i / count) * radius)), color, width, alpha);\r\n}\r\nfunction diamond(ctx, x, y, size, color, alpha = 1) {\r\n  polygon(ctx, [[x, y-size], [x+size, y], [x, y+size], [x-size, y]], color, alpha);\r\n}\r\n\r\n// Renders within the caller's canvas/transform. All timing is caller supplied;\r\n// no timers, event listeners, storage, or workout state live in the renderer.\r\nexport function drawAnimatedWeapon(ctx, value, {\r\n  weapons = globalThis.GalaWeapons,\r\n  x = 90, y = 90, scale = 1, now = 0, action = null, reducedMotion = false,\r\n} = {}) {\r\n  const p = evolution(value);\r\n  const duration = action?.special && p.ability ? p.ability.animationMs : p.attackMs;\r\n  const age = action ? (now - action.startedAt) / duration : -1;\r\n  const active = age >= 0 && age < 1;\r\n  const a = clamp(age), special = active && action.special && !!p.ability;\r\n  const pulse = active ? Math.sin(a * Math.PI) : 0;\r\n  const windup = active ? Math.sin(clamp(a / .23) * Math.PI / 2) : 0;\r\n  const power = (special ? 1.5 + p.stage * .11 : 1) * pulse;\r\n  const motion = p.family.motion;\r\n  const t = reducedMotion ? 0 : now / 1000;\r\n  const float = reducedMotion ? 0 : Math.sin(t * 1.7) * p.idleLift;\r\n  const reach = p.reach * (special ? 1.25 : 1);\r\n  const extent = special ? p.stage + 2 : 1;\r\n  const ranged = ['rail','lance','fork','rockets','arrow','gauss','burst'].includes(motion);\r\n  const horizontal = ['arc','plasma','sonic'].includes(motion);\r\n  let dx = 0, dy = float, rotation = ranged ? Math.PI / 2 : horizontal ? 0 : -.14;\r\n  if (active && !reducedMotion) {\r\n    if (motion === 'cut' || motion === 'reap') rotation += -1.15 + a * 2.9;\r\n    else if (motion === 'cleave') { rotation += -1.25 + a * 2.7; dy -= windup * 12 * (1 - a); }\r\n    else if (motion === 'blink') { dx = Math.sin(a * Math.PI) * reach; rotation += Math.sin(a * TAU) * .65; }\r\n    else if (motion === 'return') { dx = Math.sin(a * Math.PI) * reach; dy -= Math.sin(a * TAU) * 14; rotation += a * TAU * (2 + p.stage); }\r\n    else if (motion === 'rush') { dx = Math.sin(a * Math.PI) * 16; rotation = Math.sin(a * TAU * extent) * .45; }\r\n    else if (ranged || horizontal) { dx -= power * 7; rotation -= pulse * .09; }\r\n    else { dy -= power * 12; rotation += Math.sin(a * TAU) * .2; }\r\n  }\r\n\r\n  ctx.save(); ctx.translate(Math.round(x), Math.round(y)); ctx.scale(scale, scale); ctx.imageSmoothingEnabled = false;\r\n  // Slow orbiting hardware remains legible in idle and reduced-motion mode.\r\n  for (let k = 0; k < p.orbitCount; k++) {\r\n    const theta = t * .7 + k * TAU / p.orbitCount;\r\n    diamond(ctx, Math.cos(theta) * (24 + p.tier), Math.sin(theta) * (23 + p.tier * .4), 2 + p.stage * .35, p.energy, .5);\r\n  }\r\n  if (p.tier >= 12) arc(ctx, 0, 0, 24 + p.tier, -.7 + t * .12, 2.9 + t * .12, p.accent, 1, .22);\r\n\r\n  if (active && !reducedMotion) {\r\n    // Charge converges into the weapon before release. Advanced tiers add\r\n    // stepped afterimages, without large white flashes or camera shake.\r\n    if (a < .26) {\r\n      for (let k = 0; k < p.trailCount; k++) {\r\n        const theta = k * TAU / p.trailCount + t;\r\n        const r = (1 - a / .26) * (reach * .6) + 8;\r\n        diamond(ctx, Math.cos(theta) * r, Math.sin(theta) * r, 2, p.accent, .65);\r\n      }\r\n    }\r\n    for (let k = p.trailCount; k > 0; k--) {\r\n      if (motion === 'blink' || motion === 'rush' || motion === 'return') {\r\n        ctx.save(); ctx.globalAlpha = .06 + .08 * (1 - k / (p.trailCount + 1));\r\n        ctx.translate(dx - k * (special ? 9 : 5), dy); ctx.rotate(rotation);\r\n        weapons.draw(ctx, value, {x: -20, y: -40, palette: p}); ctx.restore();\r\n      }\r\n    }\r\n  }\r\n\r\n  // Additional geometry follows the defining family rather than applying a\r\n  // single generic decoration to every weapon. Proportions grow each tier.\r\n  ctx.save(); ctx.translate(Math.round(dx), Math.round(dy)); ctx.rotate(rotation);\r\n  ctx.scale(p.width * .8, p.length * .8);\r\n  weapons.draw(ctx, value, {x: -20, y: -40, palette: p});\r\n  if (p.tier > 0) {\r\n    const fin = p.finLength, count = 1 + p.stage;\r\n    if (['cut','cleave','blink','lance','fork','reap'].includes(motion)) {\r\n      for (const side of [-1, 1]) polygon(ctx, [[side*5,-8],[side*(7+fin*.32),-24-fin],[side*3,-29-fin],[side*3,-8]], p.accent, .65);\r\n    } else if (ranged || horizontal) {\r\n      for (let k = 0; k < count; k++) {\r\n        const yy = horizontal ? -15 : -25 - k * 3;\r\n        polygon(ctx, [[-13,yy],[-13-fin*.45,yy-4],[-13-fin*.45,yy+6],[-11,yy+4]], p.energy, .75);\r\n      }\r\n    } else if (motion === 'return') {\r\n      for (let k = 0; k < 3 + p.stage; k++) {\r\n        const theta = k * TAU / (3 + p.stage);\r\n        polygon(ctx, [[Math.cos(theta)*15,Math.sin(theta)*18-8],[Math.cos(theta+.16)*(20+fin*.3),Math.sin(theta+.16)*(23+fin*.3)-8],[Math.cos(theta+.3)*15,Math.sin(theta+.3)*18-8]], p.accent);\r\n      }\r\n    } else if (motion === 'rush') {\r\n      for (const side of [-1,1]) polygon(ctx, [[side*9,-18],[side*(9+fin*.5),-25-fin*.3],[side*14,-11]], p.accent);\r\n    } else {\r\n      for (let k = 0; k < count; k++) {\r\n        const theta = k * TAU / count - Math.PI / 2;\r\n        diamond(ctx, Math.cos(theta)*(22+fin*.2), Math.sin(theta)*(28+fin*.2)-4, 1.5+p.stage*.3, p.accent);\r\n      }\r\n    }\r\n  }\r\n  ctx.restore();\r\n\r\n  if (active && reducedMotion) {\r\n    // A contained, stationary emblem conveys an attack with no shake, rush,\r\n    // repeated blinking, or moving background.\r\n    diamond(ctx, 32, -16, special ? 11 : 6, p.energy, .75);\r\n    arc(ctx, 32, -16, special ? 16 : 10, 0, TAU, p.accent, 2, .65);\r\n  } else if (active && a > .18) {\r\n    const travel = clamp((a - .18) / .62), fade = Math.min(1, (1 - a) * 3.5);\r\n    const target = 32 + travel * reach, burst = Math.sin(travel * Math.PI);\r\n    const beam = (vertices, width = 2, alpha = 1, color = p.energy) => line(ctx, vertices, color, width, fade * alpha);\r\n    const ring = (xx, yy, radius, width = 2, alpha = 1) => arc(ctx, xx, yy, radius, 0, TAU, p.energy, width, fade * alpha);\r\n    const slash = (yy, reverse = false) => {\r\n      const radius = reach * (.58 + burst * .35);\r\n      for (let k = 0; k < p.trailCount; k++) arc(ctx, 8, yy, radius-k*3, (reverse?-.1:-1.4)+travel*.45, (reverse?1.5:.5)+travel*.55, k===0?p.core:p.energy, k===0?2:3, fade/(1+k*.35));\r\n    };\r\n    switch (motion) {\r\n      case 'cut': slash(-6); if (special) slash(14, true); break;\r\n      case 'cleave':\r\n        slash(6); beam([[24,-45],[target,32],[target+reach*.35,32]], 4+power*5);\r\n        if (special) for(let k=0;k<extent;k++) beam([[target,32],[target+(k-extent/2)*9,16-k*3],[target+(k-extent/2)*14,28]],2);\r\n        break;\r\n      case 'blink':\r\n        for(let k=0;k<extent;k++) beam([[target-k*9-9,-16+k*5],[target-k*9+10,6+k*5]],2+k%2);\r\n        break;\r\n      case 'arc':\r\n        for(let k=0;k<extent;k++) beam([[16,-10],[target*.4,-18+k*7],[target*.6,-4+k*4],[target,-14+k*8]],2);\r\n        break;\r\n      case 'burst':\r\n        for(let k=0;k<2+extent;k++) { const xx=24+((travel+k*.17)%1)*reach; beam([[xx-10,-12+k*5],[xx+5,-12+k*5]],2+special); }\r\n        break;\r\n      case 'rail':\r\n        beam([[5,-6],[32+reach,-6]],3+power*5); beam([[5,-6],[32+reach,-6]],2,1,p.core);\r\n        for(let k=0;k<extent;k++) ring(25+k*reach/extent,-6,5+burst*(6+p.stage),1,.7);\r\n        break;\r\n      case 'chain':\r\n        for(let k=0;k<extent+1;k++) { const yy=(k-extent/2)*12; beam([[0,-20],[25,-8],[48,yy-10],[65,yy+2],[target,yy-5]],2); diamond(ctx,target,yy-5,3,p.core,fade); }\r\n        break;\r\n      case 'drone':\r\n        for(let k=0;k<extent;k++) { const yy=Math.sin(travel*TAU+k)* (14+p.stage*3); beam([[target-20,yy-8],[target-9,yy],[target,yy]],1,.7); diamond(ctx,target,yy,4+p.stage*.5,p.energy,fade); }\r\n        break;\r\n      case 'lance':\r\n        polygon(ctx, [[12,-8],[target+30,-13-power*2],[target+43, -5],[target+30,3+power*2],[12,-2]],p.energy,fade*.8);\r\n        beam([[16,-5],[target+36,-5]],2,1,p.core); break;\r\n      case 'fork':\r\n        for(let k=-1;k<=1;k++) { const yy=k*(12+travel*16); beam([[0,k*7],[target,yy]],2+power*2); diamond(ctx,target,yy,3+p.stage,p.accent,fade); if(special)ring(target,yy,7+burst*10,1); }\r\n        break;\r\n      case 'rockets':\r\n        for(let k=0;k<2+extent;k++) { const yy=-Math.sin(travel*Math.PI)*(18+k*6)+k*5; beam([[target-13,yy+7],[target-6,yy+1],[target,yy]],2,.7); diamond(ctx,target,yy,3,p.accent,fade); if(travel>.8)ring(target,yy,4+burst*12,2); } break;\r\n      case 'reap': slash(-2); ring(target*.6,-6,10+burst*15,3,.7); if(special)slash(15,true); break;\r\n      case 'arrow':\r\n        for(let k=0;k<extent;k++) { const yy=(k-(extent-1)/2)*9; beam([[target-26,yy],[target+7,yy]],2); polygon(ctx,[[target+14,yy],[target+2,yy-5],[target+2,yy+5]],p.core,fade); } break;\r\n      case 'gauss':\r\n        for(let k=0;k<extent;k++) { const xx=target-k*11; diamond(ctx,xx,-6,7+p.stage,p.energy,fade); beam([[xx-23,-6],[xx,-6]],2,.8,p.accent); } break;\r\n      case 'return':\r\n        for(let k=0;k<extent;k++) arc(ctx,dx,dy,18+k*5,travel*TAU+k,travel*TAU+k+4,p.energy,2,fade*.8); break;\r\n      case 'rush':\r\n        for(let k=0;k<3+extent;k++) { const xx=20+((travel+k*.13)%1)*reach*.8,yy=Math.sin(k*2.1)*14; beam([[xx-19,yy],[xx+2,yy]],3); diamond(ctx,xx+5,yy,4+p.stage,p.accent,fade); } break;\r\n      case 'gravity':\r\n        polygon(ctx,Array.from({length:12},(_,k)=>{const theta=k*TAU/12;return [target*.7+Math.cos(theta)*(8+burst*18),-6+Math.sin(theta)*(8+burst*18)];}),'#080a1b',fade);\r\n        for(let k=0;k<extent+1;k++) arc(ctx,target*.7,-6,12+burst*19+k*4,k+t,-k+t+4,p.energy,2,fade*.8);\r\n        break;\r\n      case 'sonic':\r\n        for(let k=0;k<extent+1;k++) arc(ctx,8+k*15+travel*reach*.7,-5,8+travel*(18+p.tier)-k*2,-1.15,1.15,p.energy,2+special,fade*.8); break;\r\n      case 'swarm':\r\n        for(let k=0;k<4+extent*2;k++) { const theta=k*2.399+t*3; const xx=target*.8+Math.cos(theta)*(8+burst*16),yy=Math.sin(theta)*(12+burst*14); beam([[xx-7,yy+5],[xx,yy]],1,.6); diamond(ctx,xx,yy,2+k%2,p.energy,fade); } break;\r\n      case 'plasma':\r\n        beam([[10,-6],[target,-6]],6+power*8,.7);\r\n        diamond(ctx,target,-6,9+power*(7+p.stage),p.energy,fade);\r\n        diamond(ctx,target,-6,4+power*4,p.core,fade);\r\n        if(special)for(let k=0;k<extent;k++)ring(target,-6,12+k*5+burst*10,1,.55);\r\n        break;\r\n    }\r\n    // Deterministic shards use tier-driven counts; no random allocations or\r\n    // particle objects accumulate between frames.\r\n    if (special && travel > .5) {\r\n      for(let k=0;k<p.particleCount;k++) {\r\n        const theta=k*2.399, radius=(travel-.5)*(20+(k%7)*8+p.tier);\r\n        diamond(ctx,32+reach*.7+Math.cos(theta)*radius,Math.sin(theta)*radius*.65,1+k%2,k%3?p.energy:p.accent,fade*.7);\r\n      }\r\n    }\r\n  }\r\n  ctx.restore();\r\n  return {active, progress: a, profile: p};\r\n}\r\n","pod/weapon-evolution.mjs":"// Presentation and ability proposals. Saved weapon IDs and earned requirements\r\n// remain owned by GalaWeapons; this module does not award workout progress.\r\nconst definitions = [\r\n  ['rapier', 'cut', 186, 8, ['Crescent cut', 'Twin crescent', 'Sky sever', 'Rift ballet', 'Horizon split']],\r\n  ['greatsword', 'cleave', 215, 12, ['Heavy arc', 'Fault line', 'Meteor cleave', 'World breaker', 'Heaven fall']],\r\n  ['dagger', 'blink', 284, 6, ['Phase step', 'Double take', 'Ghost rush', 'Afterimage storm', 'Zero moment']],\r\n  ['sabre', 'arc', 191, 7, ['Arc shot', 'Ricochet', 'Chain flash', 'Lightning fan', 'Thunder crown']],\r\n  ['axe', 'burst', 153, 9, ['Pulse burst', 'Split volley', 'Crossfire', 'Aurora barrage', 'Infinite salvo']],\r\n  ['hammer', 'rail', 210, 14, ['Rail shot', 'Twin rail', 'Ion tunnel', 'Orbital piercer', 'Skyline erase']],\r\n  ['mace', 'chain', 271, 10, ['Coil lash', 'Forked current', 'Tesla web', 'Storm cage', 'Living lightning']],\r\n  ['flail', 'drone', 164, 10, ['Drone dive', 'Twin dive', 'Hunter spiral', 'Satellite rush', 'Constellation fall']],\r\n  ['spear', 'lance', 182, 9, ['Lance thrust', 'Triple pierce', 'Comet lance', 'Starfall spear', 'Event horizon']],\r\n  ['trident', 'fork', 233, 11, ['Tri-beam', 'Prism fork', 'Ninefold light', 'Sky lattice', 'Prism cathedral']],\r\n  ['halberd', 'rockets', 22, 14, ['Rocket salvo', 'Cluster bloom', 'Meteor rain', 'Orbital garden', 'Supernova parade']],\r\n  ['scythe', 'reap', 292, 12, ['Gravity sweep', 'Twin moon', 'Orbit harvest', 'Rift scythe', 'Eclipse reaper']],\r\n  ['bow', 'arrow', 168, 10, ['Photon arrow', 'Split star', 'Comet rain', 'Heaven string', 'Constellation arrow']],\r\n  ['crossbow', 'gauss', 216, 11, ['Gauss bolt', 'Capacitor burst', 'Prism bolt', 'Warp volley', 'Luminous spearhead']],\r\n  ['chakram', 'return', 308, 8, ['Orbit throw', 'Twin orbit', 'Solar wheel', 'Rift carousel', 'Galaxy return']],\r\n  ['gauntlets', 'rush', 25, 7, ['Power rush', 'Twin impact', 'Meteor fists', 'Dragon engine', 'Thousand suns']],\r\n  ['staff', 'gravity', 259, 14, ['Gravity well', 'Twin wells', 'Orbit crush', 'Singularity', 'Pocket universe']],\r\n  ['wand', 'sonic', 179, 9, ['Sonic ring', 'Triple echo', 'Resonance wall', 'Aurora wave', 'Universe echo']],\r\n  ['tome', 'swarm', 147, 12, ['Nanite rush', 'Split hive', 'Prism swarm', 'Astral flock', 'Living constellation']],\r\n  ['cannon', 'plasma', 320, 16, ['Plasma bloom', 'Twin reactor', 'Solar lance', 'Supernova', 'Impossible sun']],\r\n];\r\n\r\nexport const ABILITY_TIERS = Object.freeze([4, 8, 12, 16, 20]);\r\nexport const WEAPON_FAMILIES = Object.freeze(Object.fromEntries(definitions.map(\r\n  ([id, motion, hue, cooldown, moves]) => [id, Object.freeze({id, motion, hue, cooldown, moves: Object.freeze(moves)})],\r\n)));\r\nexport const MAX_COOLDOWN_MS = 40_000;\r\n\r\nexport function normalizeWeapon(value) {\r\n  if (!value || !Object.hasOwn(WEAPON_FAMILIES, value.type) ||\r\n      !Number.isInteger(value.tier) || value.tier < 0 || value.tier > 20) {\r\n    throw new Error('Unknown weapon or tier.');\r\n  }\r\n  return {type: value.type, tier: value.tier};\r\n}\r\n\r\nexport function evolution(value) {\r\n  const weapon = normalizeWeapon(value), family = WEAPON_FAMILIES[weapon.type];\r\n  const t = weapon.tier, stage = Math.floor(t / 4), fraction = t / 20;\r\n  // Every adjacent tier changes hue, proportions, and footprint. Milestones\r\n  // add silhouettes/choreography without resetting the smaller tier gains.\r\n  const hue = (family.hue + t * 4.7) % 360;\r\n  return {\r\n    ...weapon, family, stage, fraction,\r\n    energy: `hsl(${hue.toFixed(1)} 96% ${62 + fraction * 17}%)`,\r\n    accent: `hsl(${(hue + 55) % 360} 100% ${70 + fraction * 12}%)`,\r\n    shell: `hsl(${(family.hue + t * 2) % 360} ${18 + fraction * 24}% ${35 + fraction * 42}%)`,\r\n    core: '#f4fbff',\r\n    width: 1 + t * .023,\r\n    length: 1 + t * .031,\r\n    reach: 35 + t * 2.8,\r\n    trailCount: 1 + Math.floor(t / 3),\r\n    particleCount: 3 + t * 2,\r\n    orbitCount: stage,\r\n    finLength: 2 + t * .65,\r\n    idleLift: 1.5 + t * .12,\r\n    attackMs: Math.max(350, 680 - t * 11),\r\n    ability: abilityFor(weapon),\r\n  };\r\n}\r\n\r\nexport function abilityFor(value) {\r\n  const weapon = normalizeWeapon(value), family = WEAPON_FAMILIES[weapon.type];\r\n  if (weapon.tier < ABILITY_TIERS[0]) return null;\r\n  const rank = ABILITY_TIERS.filter(tier => weapon.tier >= tier).length;\r\n  return Object.freeze({\r\n    id: `${weapon.type}:${rank}`,\r\n    family: weapon.type,\r\n    name: family.moves[rank - 1],\r\n    rank,\r\n    unlockTier: ABILITY_TIERS[rank - 1],\r\n    cooldownMs: (family.cooldown + (rank - 1) * 2) * 1000,\r\n    animationMs: 1000 + rank * 180,\r\n    damageMultiplier: 2 + rank,\r\n    motion: family.motion,\r\n  });\r\n}\r\n\r\n// One shared special gauge: changing the family/tier or re-entering rest cannot\r\n// reset its timer. The arena owns persistence, and the gallery uses a separate\r\n// instance. Clamping time prevents a clock rollback from shortening a cooldown.\r\nexport class AbilityCooldown {\r\n  constructor(saved = null, now = Date.now()) {\r\n    if (!Number.isFinite(now) || now < 0) throw new Error('Invalid cooldown clock.');\r\n    if (typeof saved === 'string') { try { saved = JSON.parse(saved); } catch { saved = null; } }\r\n    this.lastNow = now;\r\n    this.readyAt = now;\r\n    this.durationMs = 0;\r\n    if (saved?.version === 1 && Number.isFinite(saved.readyAt)) {\r\n      this.readyAt = Math.max(now, Math.min(now + MAX_COOLDOWN_MS, saved.readyAt));\r\n      this.durationMs = Math.max(this.readyAt - now, Math.min(MAX_COOLDOWN_MS, Number(saved.durationMs) || 0));\r\n    }\r\n  }\r\n\r\n  clock(now) {\r\n    if (!Number.isFinite(now) || now < 0) throw new Error('Invalid cooldown clock.');\r\n    this.lastNow = Math.max(this.lastNow, now);\r\n    return this.lastNow;\r\n  }\r\n\r\n  remaining(now = Date.now()) {\r\n    return Math.max(0, this.readyAt - this.clock(now));\r\n  }\r\n\r\n  activate(value, {now = Date.now(), progress, inRest = false, catalog = globalThis.GalaWeapons} = {}) {\r\n    const weapon = normalizeWeapon(value), ability = abilityFor(weapon), at = this.clock(now);\r\n    if (!inRest) return {ok: false, reason: 'not-rest'};\r\n    if (!ability) return {ok: false, reason: 'tier', unlockTier: ABILITY_TIERS[0]};\r\n    if (!progress || !catalog?.unlocked(weapon, progress)) return {ok: false, reason: 'locked'};\r\n    const remainingMs = this.remaining(at);\r\n    if (remainingMs > 0) return {ok: false, reason: 'cooldown', remainingMs};\r\n    this.readyAt = at + ability.cooldownMs;\r\n    this.durationMs = ability.cooldownMs;\r\n    return {ok: true, ability, readyAt: this.readyAt};\r\n  }\r\n\r\n  snapshot() { return {version: 1, readyAt: this.readyAt, durationMs: this.durationMs}; }\r\n  merge(saved,now=Date.now()) { const incoming=new AbilityCooldown(saved,this.clock(now));if(incoming.readyAt>this.readyAt){this.readyAt=incoming.readyAt;this.durationMs=incoming.durationMs;}return this.remaining(now); }\r\n}\r\n","pod/weapon-review.css":":root{color-scheme:dark;--bg:#080c18;--panel:#101726;--line:#263347;--ink:#eaf4ff;--muted:#94a6bb;--accent:#90f6dc;font:15px/1.4 ui-sans-serif,system-ui,sans-serif;background:var(--bg);color:var(--ink)}\r\n*{box-sizing:border-box}body{margin:0}button,a,input{-webkit-tap-highlight-color:transparent}button{font:inherit;color:inherit;cursor:pointer;border:1px solid var(--line);border-radius:7px;background:#182334;min-height:44px;padding:9px 15px}button:hover{background:#24344c}button:disabled{cursor:default;opacity:.5}button:focus-visible,a:focus-visible,input:focus-visible{outline:3px solid var(--accent);outline-offset:3px}button[aria-pressed=true]{border-color:var(--accent);color:var(--accent);background:#173333}main{max-width:1350px;margin:auto;padding:24px 32px 36px}header{display:flex;align-items:center;gap:22px;margin:0 0 22px}header a{font-weight:900;letter-spacing:.1em;color:var(--accent);text-decoration:none}h1{font-size:22px;margin:0;font-weight:650}header>span{margin-left:auto;color:var(--muted);font-size:12px}.workspace{display:grid;grid-template-columns:195px 1fr;gap:25px}nav{display:grid;align-content:start;gap:5px}nav button{text-align:left;min-height:36px;padding:7px 11px;border-color:transparent;background:transparent;font-size:13px}nav button[aria-pressed=true]{border-color:var(--line)}.preview{min-width:0}.stage{position:relative;overflow:hidden;border:1px solid #34475b;border-radius:13px;background:#172541 url('worlds/great-wall.webp') center/cover}.stage:before{content:\"\";position:absolute;inset:0;background:linear-gradient(#111a357a,#1017299c);pointer-events:none}.stage-title{position:absolute;top:22px;left:25px;right:25px;display:flex;justify-content:space-between;gap:20px;z-index:1;pointer-events:none}.stage-title span{font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:#b8d3dd}h2{font-size:25px;letter-spacing:-.03em;margin:3px 0;font-weight:650}#tierNumber{border:1px solid #60838377;height:32px;min-width:55px;text-align:center;line-height:30px;border-radius:5px}.stage canvas{display:block;position:relative;width:100%;height:auto;image-rendering:pixelated}.preview-badge{position:absolute;bottom:15px;left:23px;font-size:11px;color:#b6c8db}.controls{padding:18px 2px 20px}.tier-control{display:flex;gap:14px;align-items:center;margin-bottom:15px}.tier-control label{font-size:13px;color:var(--muted)}.tier-control input{width:100%;accent-color:var(--accent);cursor:pointer;min-height:24px}.tier-control output{font-variant-numeric:tabular-nums;min-width:23px}.action-controls{display:flex;gap:8px}#special{flex:1;background:var(--accent);border-color:var(--accent);color:#102628;font-weight:650}#special:disabled{opacity:1;background:#193437;border-color:#315252;color:#a6cfca}#cooldown{display:block;width:100%;height:4px;border:0;margin:13px 0 10px;accent-color:var(--accent);background:var(--line);border-radius:4px;overflow:hidden}#cooldown::-webkit-progress-bar{background:var(--line)}#cooldown::-webkit-progress-value{background:var(--accent)}.details{display:flex;justify-content:space-between;gap:15px;color:var(--muted);font-size:11px}.comparison{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}figure{margin:0;border:1px solid var(--line);border-radius:9px;background:var(--panel);overflow:hidden}figure:last-child{border-color:#578d89}figure canvas{width:100%;height:auto;display:block;image-rendering:pixelated}figcaption{padding:8px 12px 12px;font-size:11px;color:var(--muted)}figure:last-child figcaption{color:var(--accent)}.motion-note{color:var(--muted);font-size:12px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\r\n@media(max-width:850px){main{padding:15px}.workspace{grid-template-columns:1fr;gap:14px}nav{display:flex;overflow-x:auto;gap:4px;padding-bottom:7px}nav button{flex-shrink:0}header{margin-bottom:15px;gap:15px}header>span{font-size:10px}.stage-title{top:15px;left:17px;right:17px}h2{font-size:20px}.preview-badge{font-size:10px;left:15px;bottom:10px}}\r\n@media(max-width:470px){main{padding:10px}h1{font-size:18px}header a{font-size:13px}.stage-title{top:10px;left:12px;right:12px}.stage-title span{font-size:9px}h2{font-size:17px}#tierNumber{height:25px;line-height:23px;min-width:40px}.preview-badge{font-size:9px;bottom:7px}.action-controls button{font-size:12px;padding:8px 10px}.comparison{gap:5px}figcaption{padding:4px 6px 9px;font-size:9px}.details{font-size:10px}.stage canvas{min-height:245px;object-fit:contain}.controls{padding-top:12px}}\r\n","pod/weapon-review.html":"<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\r\n  <meta name=\"robots\" content=\"noindex\"><title>Weapon lab · MYR5</title>\r\n  <link rel=\"stylesheet\" href=\"weapon-review.css\">\r\n</head>\r\n<body>\r\n  <main>\r\n    <header><a href=\"../pose.html\">MYR5</a><h1>Weapon lab</h1><span>Animation preview</span></header>\r\n    <div class=\"workspace\">\r\n      <nav aria-label=\"Weapons\" id=\"families\"></nav>\r\n      <section class=\"preview\" aria-label=\"Weapon preview\">\r\n        <div class=\"stage\">\r\n          <div class=\"stage-title\"><div><span id=\"tierLabel\"></span><h2 id=\"weaponName\"></h2></div><span id=\"tierNumber\"></span></div>\r\n          <canvas id=\"scene\" width=\"640\" height=\"360\" aria-label=\"Animated weapon demonstration\"></canvas>\r\n          <span class=\"preview-badge\">All tiers available here for review</span>\r\n        </div>\r\n        <div class=\"controls\">\r\n          <div class=\"tier-control\"><label for=\"tier\">Tier</label><input id=\"tier\" type=\"range\" min=\"0\" max=\"20\" value=\"4\"><output id=\"tierOutput\" for=\"tier\">4</output></div>\r\n          <div class=\"action-controls\"><button id=\"attack\">Attack</button><button id=\"special\" class=\"special\">Special</button><button id=\"autoplay\" aria-pressed=\"false\">Autoplay</button></div>\r\n          <progress id=\"cooldown\" max=\"1\" value=\"1\" aria-label=\"Special ability ready\"></progress>\r\n          <div class=\"details\"><span id=\"unlock\"></span><span id=\"abilityInfo\"></span></div>\r\n        </div>\r\n        <section class=\"comparison\" aria-label=\"Tier comparison\">\r\n          <figure><canvas width=\"320\" height=\"180\"></canvas><figcaption>Field · tier 0</figcaption></figure>\r\n          <figure><canvas width=\"320\" height=\"180\"></canvas><figcaption id=\"previousLabel\"></figcaption></figure>\r\n          <figure><canvas width=\"320\" height=\"180\"></canvas><figcaption id=\"currentLabel\"></figcaption></figure>\r\n        </section>\r\n        <p class=\"motion-note\" id=\"motionNote\" hidden>Reduced motion is on.</p>\r\n        <div id=\"status\" role=\"status\" aria-live=\"polite\" class=\"sr-only\"></div>\r\n      </section>\r\n    </div>\r\n  </main>\r\n  <script src=\"/workout-tracks.js\"></script><script src=\"gala-weapons.js\"></script>\r\n  <script type=\"module\" src=\"weapon-review.mjs\"></script>\r\n</body>\r\n</html>\r\n","pod/weapon-review.mjs":"import {AbilityCooldown, abilityFor, evolution} from './weapon-evolution.mjs';\r\nimport {drawAnimatedWeapon} from './weapon-animator.mjs';\r\n\r\nconst $ = id => document.getElementById(id), weapons = window.GalaWeapons;\r\nconst progress = {trainingVersion:1,training:Object.fromEntries(Object.keys(globalThis.MYR5Training.TRAINING_TRACKS).map(id=>[id,{activeDays:365,completedSets:365}]))};\r\n// Review-only progress and cooldown: never read or write account/localStorage.\r\nconst cooldown = new AbilityCooldown();\r\nconst reduced = matchMedia('(prefers-reduced-motion: reduce)');\r\nconst canvas = $('scene'), ctx = canvas.getContext('2d');\r\nconst comparison = [...document.querySelectorAll('.comparison canvas')];\r\nlet selected = {type: 'rapier', tier: 4}, action = null, autoplay = false;\r\nlet frame = 0, lastFrame = 0, lastAuto = 0, cooling = false;\r\n\r\nfor (const weapon of weapons.types) {\r\n  const button = document.createElement('button');\r\n  button.textContent = weapon.name; button.dataset.type = weapon.id;\r\n  button.setAttribute('aria-pressed', String(weapon.id === selected.type));\r\n  button.addEventListener('click', () => { selected = {...selected, type: weapon.id}; action = null; update(); });\r\n  $('families').append(button);\r\n}\r\n\r\nfunction update() {\r\n  const profile = evolution(selected), ability = profile.ability, required = weapons.requirements(selected);\r\n  document.documentElement.style.setProperty('--accent', profile.energy);\r\n  $('weaponName').textContent = weapons.types.find(w => w.id === selected.type).name;\r\n  $('tierLabel').textContent = weapons.tiers[selected.tier];\r\n  $('tierNumber').textContent = `${selected.tier} / 20`;\r\n  $('tierOutput').value = selected.tier;\r\n  $('previousLabel').textContent = `${weapons.tiers[Math.max(0, selected.tier - 1)]} · tier ${Math.max(0, selected.tier - 1)}`;\r\n  $('currentLabel').textContent = `${weapons.tiers[selected.tier]} · tier ${selected.tier}`;\r\n  $('unlock').textContent = selected.tier ? `${required.label} · ${required.xp} XP` : 'Available from the start';\r\n  $('abilityInfo').textContent = ability ? `Rank ${ability.rank} · ${ability.cooldownMs / 1000}s cooldown` : 'Special unlocks at tier 4';\r\n  for (const button of $('families').children) button.setAttribute('aria-pressed', String(button.dataset.type === selected.type));\r\n  updateCooldown();\r\n}\r\n\r\nfunction attack(special = false) {\r\n  if(!special&&action?.special&&performance.now()-action.startedAt<(abilityFor(selected)?.animationMs||0))return;\r\n  if (special) {\r\n    const result = cooldown.activate(selected, {progress, inRest: true, catalog: weapons});\r\n    if (!result.ok) return;\r\n    $('status').textContent = result.ability.name;\r\n    cooling = true;\r\n  }\r\n  action = {startedAt: performance.now(), special};\r\n  lastAuto = performance.now();\r\n}\r\n\r\nfunction updateCooldown() {\r\n  const ability = abilityFor(selected), remaining = cooldown.remaining();\r\n  $('attack').disabled=!!(action?.special&&performance.now()-action.startedAt<(ability?.animationMs||0));\r\n  $('special').disabled = !ability || remaining > 0;\r\n  $('special').textContent = !ability ? 'Unlocks at tier 4' : remaining ? `${ability.name} · ${(remaining / 1000).toFixed(1)}s` : `${ability.name} · Ready`;\r\n  // A shared clock may belong to another weapon, so its progress is measured\r\n  // against the last activated duration rather than the selected tier's timer.\r\n  const duration = cooldown.durationMs;\r\n  $('cooldown').value = remaining ? Math.max(0, 1 - remaining / Math.max(1, duration)) : 1;\r\n  $('cooldown').setAttribute('aria-label', remaining ? 'Special ability cooling down' : 'Special ability ready');\r\n  if (cooling && remaining === 0) { cooling = false; $('status').textContent = 'Special ready'; }\r\n}\r\n$('attack').addEventListener('click', () => attack());\r\n$('special').addEventListener('click', () => attack(true));\r\n$('tier').addEventListener('input', event => { selected = {...selected, tier: Number(event.target.value)}; action = null; update(); });\r\n$('autoplay').addEventListener('click', () => { autoplay = !autoplay; $('autoplay').setAttribute('aria-pressed', String(autoplay)); if (autoplay) attack(); });\r\n\r\nfunction platform(context, width, height) {\r\n  context.clearRect(0, 0, width, height); context.imageSmoothingEnabled = false;\r\n  const x = width * .29, y = height * .79, half = width * .23;\r\n  context.fillStyle = '#0b152c'; context.beginPath(); context.moveTo(x-half,y); context.lineTo(x+half,y); context.lineTo(x+half*.67,y+height*.08); context.lineTo(x-half*.55,y+height*.09); context.fill();\r\n  context.fillStyle = '#384a69'; context.fillRect(x-half,y-4,half*2,5);\r\n  context.fillStyle = '#78dbc4'; context.fillRect(x-half*.6,y+4,half*1.2,2);\r\n  context.fillStyle = '#a7d6e1'; context.globalAlpha = .35;\r\n  for(let k=0;k<13;k++) context.fillRect((k*59+23)%width,(k*31+19)%(height*.7),1,1);\r\n  context.globalAlpha = 1;\r\n}\r\n\r\nfunction render(now) {\r\n  frame = 0;\r\n  if (document.hidden) return;\r\n  if (now - lastFrame > (reduced.matches ? 100 : 32)) {\r\n    lastFrame = now;\r\n    if (autoplay && now - lastAuto > 2300) attack();\r\n    platform(ctx, canvas.width, canvas.height);\r\n    drawAnimatedWeapon(ctx, selected, {weapons, x: 185, y: 190, scale: 1.45, now, action, reducedMotion: reduced.matches});\r\n    const tiers = [0, Math.max(0, selected.tier - 1), selected.tier];\r\n    comparison.forEach((card, index) => {\r\n      const context = card.getContext('2d'); platform(context, card.width, card.height);\r\n      drawAnimatedWeapon(context, {...selected, tier: tiers[index]}, {weapons, x: 92, y: 88, scale: .65, now, action, reducedMotion: reduced.matches});\r\n    });\r\n    updateCooldown();\r\n  }\r\n  frame = requestAnimationFrame(render);\r\n}\r\n\r\nfunction motionPreference() { $('motionNote').hidden = !reduced.matches; }\r\nconst visibility = () => { if (document.hidden) { cancelAnimationFrame(frame); frame = 0; } else if (!frame) frame = requestAnimationFrame(render); };\r\ndocument.addEventListener('visibilitychange', visibility);\r\nreduced.addEventListener('change', motionPreference);\r\nwindow.addEventListener('pagehide', () => { cancelAnimationFrame(frame); frame = 0; document.removeEventListener('visibilitychange', visibility); reduced.removeEventListener('change', motionPreference); });\r\nupdate(); motionPreference(); frame = requestAnimationFrame(render);\r\n","pod/whiteboard.css":"@font-face{font-family:BoardHand0;font-style:normal;font-weight:400;font-display:swap;src:url('/pod/fonts/hand-0.woff2') format('woff2')}\r\n@font-face{font-family:BoardHand2;font-style:normal;font-weight:400;font-display:swap;src:url('/pod/fonts/hand-2.woff2') format('woff2')}\r\n@font-face{font-family:BoardHand6;font-style:normal;font-weight:400;font-display:swap;src:url('/pod/fonts/hand-6.woff2') format('woff2')}\r\n#accountPanel{color-scheme:light;--marker:#204f99;width:min(880px,calc(100vw - 22px));max-width:calc(100vw - 22px);max-height:calc(100dvh - 28px);padding:26px clamp(13px,4vw,38px) 35px;border:11px solid #bac0bf;border-top-color:#e3e6e4;border-left-color:#d5d9d7;border-right-color:#8c9698;border-bottom-width:17px;border-radius:8px;background:radial-gradient(ellipse at 8% 25%,#778e9809,transparent 33%),radial-gradient(ellipse at 83% 74%,#8b659c07,transparent 25%),linear-gradient(110deg,#fffefa,#f5f7ef 50%,#fffffb);box-shadow:inset 0 0 0 2px #818d9155,inset 3px 4px 14px #a4adac33,0 0 0 2px #475057,5px 12px 0 #333b42,0 25px 80px #000b;color:#204f99;font:22px/1.3 BoardHand0,'Comic Sans MS',cursive;overflow-x:hidden}\r\n#accountPanel::backdrop{background:#0c0b15cf;backdrop-filter:blur(7px)}#accountPanel header{align-items:flex-start;margin-bottom:16px;border-bottom:3px solid #243b6977;padding-bottom:13px;box-shadow:none;transform:rotate(-.25deg)}#accountPanel .progress-eyebrow{font:16px BoardHand2,cursive;color:#695979;letter-spacing:.8px}#accountPanel h2{font:clamp(29px,5vw,42px)/1.15 BoardHand6,cursive;color:#173c78;letter-spacing:-1px;margin-top:4px}\r\n#accountPanel button,#accountPanel .action-link{color:#263747;min-height:44px;padding:8px 14px;border:2px solid #66757b;border-radius:5px;background:linear-gradient(#f6f7eb,#d8dfd6);box-shadow:inset 0 2px #fff,0 4px #718088,0 6px #33434b55;font:19px/1.15 BoardHand0,cursive}#accountPanel button:active:not(:disabled){transform:translateY(3px);box-shadow:inset 1px 2px 4px #71808877,0 1px #718088}#accountPanel button:focus-visible,#accountPanel input:focus-visible,#accountPanel a:focus-visible,#accountPanel summary:focus-visible{outline:3px solid #174b92;outline-offset:3px}\r\n#accountPanel header>[data-close]{background:repeating-linear-gradient(0deg,#455d73 0 2px,#354d63 2px 4px);border:4px solid #bec8c7;color:#faf9f0;box-shadow:inset 0 2px #9daeb7,0 5px #33434b;min-width:65px;transform:rotate(3deg);font:18px BoardHand0,cursive}\r\n#accountPanel .progress-summary{border-bottom:1px solid #8fa8bb77;padding-bottom:16px;gap:10px}#accountPanel #accountLevel{font:30px BoardHand2,cursive;color:#883645}#accountPanel #accountSets{font:21px BoardHand0,cursive;color:#4b5461}#accountPanel p,#accountPanel .hint{font:19px/1.4 BoardHand0,cursive;color:#56606c}#accountPanel #recordsStatus{font-size:18px}#accountPanel .record-intro{margin-top:18px}\r\n#accountPanel .record-board{--ink:#204f99;margin-top:23px;color:var(--ink)}#accountPanel .record-board[data-kind=hold]{--ink:#773c86}#accountPanel .record-board[data-kind=steps]{--ink:#2e6d4c}#accountPanel .record-board[data-kind=jumps]{--ink:#a44144}#accountPanel .record-board[data-kind=pace]{--ink:#875b23}\r\n#accountPanel .record-heading{border-bottom:2px solid var(--ink);padding-bottom:6px;transform:rotate(-.25deg)}#accountPanel .record-heading h3{margin:0;font:29px BoardHand2,cursive;color:var(--ink)}#accountPanel .record-heading>span{font:18px BoardHand0,cursive;color:var(--ink)}#accountPanel table{font:23px BoardHand0,cursive;font-variant-numeric:tabular-nums}#accountPanel thead th{font:16px BoardHand0,cursive;color:#526575;letter-spacing:0;text-transform:none;padding-top:9px}#accountPanel thead th:first-child{font-size:0;width:28px}#accountPanel thead th:nth-child(2){width:41%}#accountPanel thead th:last-child{width:37px}\r\n#accountPanel tbody th,#accountPanel td{border-bottom:1px solid #8fa8bb55;padding:10px 4px;font-weight:400;color:var(--ink)}#accountPanel .record-rank{font:18px BoardHand0,cursive;color:var(--ink)}#accountPanel .record-best{font:30px BoardHand2,cursive;color:var(--ink);letter-spacing:-1px}#accountPanel tbody td:last-child{font:21px BoardHand0,cursive;color:var(--ink)}#accountPanel tr[data-record=false]{opacity:.6}#accountPanel tr[data-record=false] .record-best{color:var(--ink)}\r\n#accountPanel .unlock-board h3{font:28px BoardHand2,cursive;color:#6b3b80;border-bottom:2px solid #6b3b8077;padding-bottom:6px}#accountPanel .unlock-row{font:23px BoardHand0,cursive;border-color:#8fa8bb55;color:#6b3b80}#accountPanel .unlock-row>span:last-child{font:20px BoardHand0,cursive;color:#56606c}#accountPanel .unlock-row[data-unlocked=true]{background:linear-gradient(transparent 70%,#e9dc8140 71%,transparent 95%)}#accountPanel .unlock-row[data-unlocked=true]>span:last-child{color:#2e6d4c}\r\n#accountPanel .account-settings{border-color:#8fa8bb88}#accountPanel summary{font:22px BoardHand0,cursive;color:#485e78;cursor:pointer}#accountPanel .account-settings h3{font:25px BoardHand2,cursive;color:#6b3b80}#accountPanel input{background:#e7edf344;border:0;border-bottom:2px solid #667c99;color:#244873;font:22px BoardHand0,cursive;box-shadow:none}#accountPanel label{color:#485461;font:20px BoardHand0,cursive}#accountPanel a{color:#174b92}\r\n.marker-tray{position:sticky;bottom:-35px;height:25px;margin:23px -6px -35px;display:flex;gap:13px;align-items:flex-end;justify-content:flex-end;padding:0 16px 1px;background:linear-gradient(#97a7ad,#e2e7e4 45%,#929fa4 48%,#576771);border-radius:2px;border-top:1px solid #8b999f;box-shadow:0 -3px 8px #27343a22;pointer-events:none}.marker-tray i{height:11px;width:65px;background:linear-gradient(#fff,#d7dddd);border:1px solid #637076;border-right:18px solid var(--ink);border-left:7px solid var(--ink);border-radius:3px;box-shadow:0 2px 2px #1c293866;transform:rotate(-2deg)}.marker-tray i:nth-child(2){transform:rotate(2deg)}\r\n@media(min-width:650px){#workoutList{display:grid;grid-template-columns:1fr 1fr;column-gap:30px;align-items:start}.record-board:last-child{grid-column:1/-1}}\r\n@media(max-width:420px){#accountPanel{border-width:7px;border-bottom-width:13px;padding-top:20px}#accountPanel header{position:relative;display:block;min-height:99px;transform:none}#accountPanel header>[data-close]{position:absolute;top:0;right:0;min-width:55px;padding:7px}#accountPanel .progress-eyebrow{font-size:14px}#accountPanel h2{padding-top:24px;font-size:29px}#accountPanel .record-heading>span{font-size:16px}#accountPanel table{font-size:21px}#accountPanel thead th{font-size:14px}#accountPanel .record-best{font-size:26px}#accountPanel .progress-summary button{padding:8px;font-size:17px}}\r\n","pod/workout-session-owner.mjs":"const KEY = 'myr5-workout-session-owner-v1';\r\n\r\n// The pod is the workout owner.  This intentionally persists only a local\r\n// acknowledgement: account workout sync remains owned by launch.mjs.\r\nexport class WorkoutSessionOwner {\r\n  constructor({ storage = globalThis.localStorage, saveProgress } = {}) {\r\n    this.storage = storage;\r\n    this.saveProgress = saveProgress;\r\n    const prior = this.read();\r\n    // A camera/tracker cannot survive a reload, so never revive an active run.\r\n    this.phase = 'idle';\r\n    this.revision = Math.max(0, Number(prior?.revision) || 0) + (prior?.phase === 'active' ? 1 : 0);\r\n    this.lease = null;\r\n    this.persist();\r\n  }\r\n  read() { try { return JSON.parse(this.storage?.getItem(KEY) || 'null'); } catch { return null; } }\r\n  persist() { try { this.storage?.setItem(KEY, JSON.stringify({ version: 1, phase: this.phase, revision: this.revision })); return true; } catch { return false; } }\r\n  snapshot() { return { phase: this.phase, revision: this.revision, transitioning: !!this.lease }; }\r\n  canStart() { return this.phase === 'idle' && !this.lease; }\r\n  start() { if (!this.canStart()) return false; this.phase = 'active'; this.revision++; this.persist(); return true; }\r\n  // Idempotent release for every stop path (also drops a stale idle lease).\r\n  stop() { this.lease = null; if (this.phase !== 'idle') { this.phase = 'idle'; this.revision++; this.persist(); } }\r\n  async complete(payload) {\r\n    if (this.phase !== 'active') return { saved: false, reason: 'no active workout' };\r\n    let result;\r\n    try { result = await this.saveProgress?.(payload); } catch (error) { return { saved: false, reason: error?.message || 'workout progress was not saved' }; }\r\n    if (!result?.local) return { saved: false, reason: result?.reason || 'workout progress was not saved' };\r\n    this.phase = 'idle'; this.revision++;\r\n    const acknowledged=this.persist();\r\n    // The workout database is the completion authority. A failed UI cache write\r\n    // must not turn an already committed completion into an impossible retry.\r\n    return { saved: true, acknowledged, ...result };\r\n  }\r\n  acquireIdleLease() {\r\n    if (!this.canStart()) return null;\r\n    const lease = { revision: this.revision, released: false };\r\n    this.lease = lease;\r\n    return lease;\r\n  }\r\n  releaseIdleLease(lease) { if (this.lease === lease) { lease.released = true; this.lease = null; } }\r\n  async save() { return this.persist(); }\r\n}\r\n","pod/worlds/boards/cogs/door-layout.json":"{\"version\":1,\"image\":[1024,1792],\"frame\":{\"x0\":0.0244,\"y0\":0.0268,\"x1\":0.9756,\"y1\":0.8008},\"parts\":[{\"node\":\"m19\",\"kind\":\"gear\",\"u\":0.2834,\"v\":0.8087,\"r\":0.0493,\"rot\":77,\"layer\":0},{\"node\":\"g41\",\"kind\":\"gear\",\"u\":0.3648,\"v\":0.8356,\"r\":0.0447,\"rot\":24,\"layer\":1,\"drives\":0},{\"node\":\"m47\",\"kind\":\"gear\",\"u\":0.2834,\"v\":0.8621,\"r\":0.0442,\"rot\":274,\"layer\":2,\"drives\":0},{\"node\":\"m40\",\"kind\":\"gear\",\"u\":0.2033,\"v\":0.8352,\"r\":0.0432,\"rot\":298,\"layer\":3,\"drives\":0},{\"node\":\"m05\",\"kind\":\"gear\",\"u\":0.2043,\"v\":0.7826,\"r\":0.042,\"rot\":259,\"layer\":4,\"drives\":0},{\"node\":\"g53\",\"kind\":\"gear\",\"u\":0.4397,\"v\":0.8603,\"r\":0.0418,\"rot\":44,\"layer\":5,\"drives\":1},{\"node\":\"g07\",\"kind\":\"gear\",\"u\":0.3648,\"v\":0.8848,\"r\":0.0415,\"rot\":35,\"layer\":6,\"drives\":1},{\"node\":\"g54\",\"kind\":\"gear\",\"u\":0.2834,\"v\":0.911,\"r\":0.0414,\"rot\":282,\"layer\":7,\"drives\":2},{\"node\":\"g19\",\"kind\":\"gear\",\"u\":0.2094,\"v\":0.8866,\"r\":0.0413,\"rot\":289,\"layer\":8,\"drives\":2},{\"node\":\"g22\",\"kind\":\"gear\",\"u\":0.1301,\"v\":0.8593,\"r\":0.0413,\"rot\":114,\"layer\":9,\"drives\":3},{\"node\":\"g33\",\"kind\":\"gear\",\"u\":0.1322,\"v\":0.8064,\"r\":0.0438,\"rot\":298,\"layer\":10,\"drives\":3},{\"node\":\"g61\",\"kind\":\"gear\",\"u\":0.5112,\"v\":0.8839,\"r\":0.0407,\"rot\":295,\"layer\":11,\"drives\":5},{\"node\":\"g10\",\"kind\":\"gear\",\"u\":0.4397,\"v\":0.9073,\"r\":0.0404,\"rot\":25,\"layer\":12,\"drives\":5},{\"node\":\"g40\",\"kind\":\"gear\",\"u\":0.3648,\"v\":0.9311,\"r\":0.0394,\"rot\":23,\"layer\":13,\"drives\":6},{\"node\":\"g26\",\"kind\":\"gear\",\"u\":0.2834,\"v\":0.9565,\"r\":0.0382,\"rot\":68,\"layer\":14,\"drives\":7},{\"node\":\"g09\",\"kind\":\"gear\",\"u\":0.2158,\"v\":0.9333,\"r\":0.0367,\"rot\":73,\"layer\":15,\"drives\":7},{\"node\":\"g57\",\"kind\":\"light\",\"u\":0.7285,\"v\":0.8232,\"r\":0.0619,\"rot\":180,\"layer\":16},{\"node\":\"g51\",\"kind\":\"pipe\",\"u\":0.8094,\"v\":0.8797,\"r\":0.0504,\"rot\":180,\"layer\":17},{\"node\":\"m14\",\"kind\":\"screw\",\"u\":0.7202,\"v\":0.9125,\"r\":0.05,\"rot\":180,\"layer\":18},{\"node\":\"m21\",\"kind\":\"pipe\",\"u\":0.6333,\"v\":0.9436,\"r\":0.0475,\"rot\":180,\"layer\":19},{\"node\":\"m26\",\"kind\":\"light\",\"u\":0.88,\"v\":0.9217,\"r\":0.0473,\"rot\":292,\"layer\":20},{\"node\":\"g44\",\"kind\":\"pipe\",\"u\":0.1393,\"v\":0.9413,\"r\":0.0467,\"rot\":180,\"layer\":21},{\"node\":\"m31\",\"kind\":\"valve\",\"u\":0.3289,\"v\":0.6806,\"r\":0.0458,\"rot\":280,\"layer\":22},{\"node\":\"m27\",\"kind\":\"pipe\",\"u\":0.7249,\"v\":0.7036,\"r\":0.0441,\"rot\":180,\"layer\":23},{\"node\":\"m25\",\"kind\":\"pipe\",\"u\":0.1922,\"v\":0.0578,\"r\":0.044,\"rot\":180,\"layer\":24},{\"node\":\"m41\",\"kind\":\"plate\",\"u\":0.8594,\"v\":0.0592,\"r\":0.0436,\"rot\":254,\"layer\":25},{\"node\":\"g39\",\"kind\":\"plate\",\"u\":0.7331,\"v\":0.131,\"r\":0.0425,\"rot\":180,\"layer\":26},{\"node\":\"g06\",\"kind\":\"plate\",\"u\":0.2521,\"v\":0.1382,\"r\":0.0421,\"rot\":0,\"layer\":27},{\"node\":\"m09\",\"kind\":\"plate\",\"u\":0.8936,\"v\":0.8414,\"r\":0.0416,\"rot\":0,\"layer\":28},{\"node\":\"g12\",\"kind\":\"light\",\"u\":0.3184,\"v\":0.384,\"r\":0.0416,\"rot\":180,\"layer\":29},{\"node\":\"g21\",\"kind\":\"pipe\",\"u\":0.6797,\"v\":0.3881,\"r\":0.0416,\"rot\":0,\"layer\":30},{\"node\":\"m18\",\"kind\":\"valve\",\"u\":0.5469,\"v\":0.9576,\"r\":0.0415,\"rot\":92,\"layer\":31},{\"node\":\"m34\",\"kind\":\"pipe\",\"u\":0.6719,\"v\":0.4499,\"r\":0.0413,\"rot\":180,\"layer\":32},{\"node\":\"m42\",\"kind\":\"plate\",\"u\":0.3555,\"v\":0.4807,\"r\":0.0413,\"rot\":294,\"layer\":33},{\"node\":\"m48\",\"kind\":\"plate\",\"u\":0.6395,\"v\":0.8493,\"r\":0.0413,\"rot\":253,\"layer\":34},{\"node\":\"g16\",\"kind\":\"valve\",\"u\":0.1758,\"v\":0.2775,\"r\":0.0409,\"rot\":229,\"layer\":35},{\"node\":\"g62\",\"kind\":\"light\",\"u\":0.8223,\"v\":0.2775,\"r\":0.0409,\"rot\":37,\"layer\":36},{\"node\":\"m06\",\"kind\":\"plate\",\"u\":0.1758,\"v\":0.5356,\"r\":0.0407,\"rot\":0,\"layer\":37},{\"node\":\"m49\",\"kind\":\"screw\",\"u\":0.8223,\"v\":0.539,\"r\":0.0405,\"rot\":175,\"layer\":38},{\"node\":\"m46\",\"kind\":\"plate\",\"u\":0.0819,\"v\":0.6765,\"r\":0.0404,\"rot\":250,\"layer\":39},{\"node\":\"m50\",\"kind\":\"plate\",\"u\":0.0814,\"v\":0.1439,\"r\":0.0403,\"rot\":0,\"layer\":40},{\"node\":\"m13\",\"kind\":\"pipe\",\"u\":0.9252,\"v\":0.1462,\"r\":0.0399,\"rot\":180,\"layer\":41},{\"node\":\"m15\",\"kind\":\"plate\",\"u\":0.9186,\"v\":0.6848,\"r\":0.0399,\"rot\":285,\"layer\":42},{\"node\":\"m03\",\"kind\":\"plate\",\"u\":0.855,\"v\":0.7779,\"r\":0.0398,\"rot\":180,\"layer\":43},{\"node\":\"m29\",\"kind\":\"valve\",\"u\":0.3164,\"v\":0.106,\"r\":0.0398,\"rot\":160,\"layer\":44},{\"node\":\"m08\",\"kind\":\"light\",\"u\":0.4699,\"v\":0.9629,\"r\":0.0397,\"rot\":179,\"layer\":45},{\"node\":\"m38\",\"kind\":\"plate\",\"u\":0.8221,\"v\":0.9543,\"r\":0.0396,\"rot\":296,\"layer\":46},{\"node\":\"m12\",\"kind\":\"plate\",\"u\":0.3428,\"v\":0.7495,\"r\":0.0395,\"rot\":35,\"layer\":47},{\"node\":\"m01\",\"kind\":\"plate\",\"u\":0.7582,\"v\":0.0366,\"r\":0.0391,\"rot\":180,\"layer\":48},{\"node\":\"g59\",\"kind\":\"plate\",\"u\":0.1616,\"v\":0.6157,\"r\":0.038,\"rot\":180,\"layer\":49},{\"node\":\"g15\",\"kind\":\"light\",\"u\":0.5501,\"v\":0.6116,\"r\":0.038,\"rot\":0,\"layer\":50},{\"node\":\"g38\",\"kind\":\"plate\",\"u\":0.8242,\"v\":0.6015,\"r\":0.0378,\"rot\":180,\"layer\":51},{\"node\":\"g60\",\"kind\":\"valve\",\"u\":0.9289,\"v\":0.958,\"r\":0.0376,\"rot\":359,\"layer\":52},{\"node\":\"g46\",\"kind\":\"plate\",\"u\":0.6114,\"v\":0.7377,\"r\":0.0365,\"rot\":295,\"layer\":53},{\"node\":\"g63\",\"kind\":\"plate\",\"u\":0.6212,\"v\":0.0961,\"r\":0.0365,\"rot\":228,\"layer\":54},{\"node\":\"g03\",\"kind\":\"plate\",\"u\":0.4025,\"v\":0.6138,\"r\":0.0342,\"rot\":197,\"layer\":55},{\"node\":\"m07\",\"kind\":\"plate\",\"u\":0.0778,\"v\":0.9048,\"r\":0.0322,\"rot\":177,\"layer\":56},{\"node\":\"g02\",\"kind\":\"plate\",\"u\":0.597,\"v\":0.1989,\"r\":0.0322,\"rot\":236,\"layer\":57},{\"node\":\"g32\",\"kind\":\"screw\",\"u\":0.4258,\"v\":0.2184,\"r\":0.0318,\"rot\":312,\"layer\":58},{\"node\":\"g14\",\"kind\":\"screw\",\"u\":0.6653,\"v\":0.3307,\"r\":0.0316,\"rot\":30,\"layer\":59},{\"node\":\"g04\",\"kind\":\"screw\",\"u\":0.5908,\"v\":0.293,\"r\":0.031,\"rot\":147,\"layer\":60},{\"node\":\"m45\",\"kind\":\"screw\",\"u\":0.4286,\"v\":0.2726,\"r\":0.0307,\"rot\":126,\"layer\":61},{\"node\":\"g13\",\"kind\":\"screw\",\"u\":0.0703,\"v\":0.8248,\"r\":0.0306,\"rot\":254,\"layer\":62},{\"node\":\"m20\",\"kind\":\"pipe\",\"u\":0.7305,\"v\":0.9567,\"r\":0.03,\"rot\":0,\"layer\":63},{\"node\":\"g17\",\"kind\":\"light\",\"u\":0.6758,\"v\":0.4853,\"r\":0.0295,\"rot\":281,\"layer\":64},{\"node\":\"g36\",\"kind\":\"pipe\",\"u\":0.1524,\"v\":0.8913,\"r\":0.0483,\"rot\":60,\"layer\":65},{\"node\":\"g31\",\"kind\":\"plate\",\"u\":0.3672,\"v\":0.973,\"r\":0.0661,\"rot\":270,\"layer\":66},{\"node\":\"g34\",\"kind\":\"light\",\"u\":0.3086,\"v\":0.7157,\"r\":0.0549,\"rot\":210,\"layer\":67},{\"node\":\"g11\",\"kind\":\"pipe\",\"u\":0.4371,\"v\":0.318,\"r\":0.0462,\"rot\":123,\"layer\":68},{\"node\":\"g49\",\"kind\":\"pipe\",\"u\":0.9473,\"v\":0.7502,\"r\":0.0508,\"rot\":213,\"layer\":69},{\"node\":\"g58\",\"kind\":\"pipe\",\"u\":0.9374,\"v\":0.6478,\"r\":0.0447,\"rot\":153,\"layer\":70},{\"node\":\"m17\",\"kind\":\"screw\",\"u\":0.9199,\"v\":0.8025,\"r\":0.0437,\"rot\":0,\"layer\":71},{\"node\":\"m35\",\"kind\":\"light\",\"u\":0.4468,\"v\":0.6467,\"r\":0.0568,\"rot\":90,\"layer\":72},{\"node\":\"m22\",\"kind\":\"pipe\",\"u\":0.9227,\"v\":0.8807,\"r\":0.0647,\"rot\":327,\"layer\":73},{\"node\":\"m28\",\"kind\":\"pipe\",\"u\":0.6874,\"v\":0.7387,\"r\":0.0473,\"rot\":219,\"layer\":74},{\"node\":\"g29\",\"kind\":\"light\",\"u\":0.0997,\"v\":0.9818,\"r\":0.0966,\"rot\":270,\"layer\":75},{\"node\":\"g47\",\"kind\":\"light\",\"u\":0.2602,\"v\":0.983,\"r\":0.0639,\"rot\":180,\"layer\":76},{\"node\":\"g08\",\"kind\":\"plate\",\"u\":0.5651,\"v\":0.3223,\"r\":0.0352,\"rot\":57,\"layer\":77},{\"node\":\"m43\",\"kind\":\"pipe\",\"u\":0.7328,\"v\":0.6649,\"r\":0.0449,\"rot\":123,\"layer\":78},{\"node\":\"g01\",\"kind\":\"light\",\"u\":0.6817,\"v\":0.1606,\"r\":0.0516,\"rot\":204,\"layer\":79},{\"node\":\"g05\",\"kind\":\"light\",\"u\":0.6098,\"v\":0.0626,\"r\":0.0445,\"rot\":342,\"layer\":80},{\"node\":\"g43\",\"kind\":\"screw\",\"u\":0.0467,\"v\":0.7962,\"r\":0.0311,\"rot\":240,\"layer\":81},{\"node\":\"m44\",\"kind\":\"screw\",\"u\":0.9636,\"v\":0.0851,\"r\":0.0455,\"rot\":252,\"layer\":82},{\"node\":\"g24\",\"kind\":\"light\",\"u\":0.3949,\"v\":0.3749,\"r\":0.0457,\"rot\":210,\"layer\":83},{\"node\":\"g45\",\"kind\":\"light\",\"u\":0.2885,\"v\":0.4745,\"r\":0.0601,\"rot\":27,\"layer\":84},{\"node\":\"m11\",\"kind\":\"light\",\"u\":0.5698,\"v\":0.8782,\"r\":0.0477,\"rot\":93,\"layer\":85},{\"node\":\"m33\",\"kind\":\"light\",\"u\":0.8055,\"v\":0.3216,\"r\":0.046,\"rot\":114,\"layer\":86},{\"node\":\"m02\",\"kind\":\"screw\",\"u\":0.8534,\"v\":0.5129,\"r\":0.0437,\"rot\":117,\"layer\":87},{\"node\":\"g50\",\"kind\":\"screw\",\"u\":0.3909,\"v\":0.7386,\"r\":0.0387,\"rot\":201,\"layer\":88},{\"node\":\"g18\",\"kind\":\"screw\",\"u\":0.1773,\"v\":0.1955,\"r\":0.0352,\"rot\":36,\"layer\":89},{\"node\":\"m04\",\"kind\":\"light\",\"u\":0.6644,\"v\":0.6783,\"r\":0.0548,\"rot\":66,\"layer\":90},{\"node\":\"g37\",\"kind\":\"screw\",\"u\":0.755,\"v\":0.1615,\"r\":0.0301,\"rot\":327,\"layer\":91},{\"node\":\"g25\",\"kind\":\"screw\",\"u\":0.7443,\"v\":0.983,\"r\":0.0477,\"rot\":90,\"layer\":92},{\"node\":\"m16\",\"kind\":\"light\",\"u\":0.8438,\"v\":0.9849,\"r\":0.0518,\"rot\":270,\"layer\":93},{\"node\":\"m24\",\"kind\":\"light\",\"u\":0.9414,\"v\":0.9843,\"r\":0.0459,\"rot\":0,\"layer\":94},{\"node\":\"g27\",\"kind\":\"screw\",\"u\":0.8423,\"v\":0.2219,\"r\":0.0393,\"rot\":270,\"layer\":95},{\"node\":\"m30\",\"kind\":\"pipe\",\"u\":0.6451,\"v\":0.887,\"r\":0.0436,\"rot\":87,\"layer\":96},{\"node\":\"m23\",\"kind\":\"pipe\",\"u\":0.256,\"v\":0.04,\"r\":0.0424,\"rot\":216,\"layer\":97},{\"node\":\"g28\",\"kind\":\"screw\",\"u\":0.5398,\"v\":0.2797,\"r\":0.0388,\"rot\":0,\"layer\":98},{\"node\":\"m36\",\"kind\":\"pipe\",\"u\":0.1937,\"v\":0.4984,\"r\":0.0502,\"rot\":117,\"layer\":99},{\"node\":\"m32\",\"kind\":\"screw\",\"u\":0.2366,\"v\":0.5383,\"r\":0.0334,\"rot\":117,\"layer\":100},{\"node\":\"m37\",\"kind\":\"pipe\",\"u\":0.5677,\"v\":0.1682,\"r\":0.0429,\"rot\":294,\"layer\":101},{\"node\":\"g42\",\"kind\":\"screw\",\"u\":0.4304,\"v\":0.176,\"r\":0.0512,\"rot\":333,\"layer\":102},{\"node\":\"g30\",\"kind\":\"pipe\",\"u\":0.3811,\"v\":0.2147,\"r\":0.0331,\"rot\":153,\"layer\":103},{\"node\":\"g56\",\"kind\":\"screw\",\"u\":0.6164,\"v\":0.3953,\"r\":0.0314,\"rot\":90,\"layer\":104},{\"node\":\"g23\",\"kind\":\"light\",\"u\":0.0491,\"v\":0.6305,\"r\":0.0455,\"rot\":24,\"layer\":105},{\"node\":\"g48\",\"kind\":\"screw\",\"u\":0.9235,\"v\":0.0265,\"r\":0.0343,\"rot\":321,\"layer\":106},{\"node\":\"g20\",\"kind\":\"screw\",\"u\":0.6326,\"v\":0.7675,\"r\":0.0323,\"rot\":111,\"layer\":107},{\"node\":\"g35\",\"kind\":\"screw\",\"u\":0.3065,\"v\":0.6483,\"r\":0.0402,\"rot\":288,\"layer\":108},{\"node\":\"g05\",\"kind\":\"light\",\"u\":0.8177,\"v\":0.084,\"r\":0.0445,\"rot\":321,\"layer\":109},{\"node\":\"g55\",\"kind\":\"pipe\",\"u\":0.9649,\"v\":0.8429,\"r\":0.0572,\"rot\":270,\"layer\":110},{\"node\":\"g28\",\"kind\":\"screw\",\"u\":0.6194,\"v\":0.9827,\"r\":0.0457,\"rot\":270,\"layer\":111},{\"node\":\"g25\",\"kind\":\"screw\",\"u\":0.6295,\"v\":0.4841,\"r\":0.0343,\"rot\":216,\"layer\":112},{\"node\":\"g35\",\"kind\":\"screw\",\"u\":0.324,\"v\":0.1522,\"r\":0.0568,\"rot\":153,\"layer\":113},{\"node\":\"g31\",\"kind\":\"plate\",\"u\":0.6088,\"v\":0.8239,\"r\":0.0396,\"rot\":285,\"layer\":114},{\"node\":\"g24\",\"kind\":\"light\",\"u\":0.2036,\"v\":0.3182,\"r\":0.0457,\"rot\":327,\"layer\":115},{\"node\":\"m35\",\"kind\":\"light\",\"u\":0.7301,\"v\":0.3678,\"r\":0.0568,\"rot\":297,\"layer\":116},{\"node\":\"g11\",\"kind\":\"pipe\",\"u\":0.3844,\"v\":0.0867,\"r\":0.0544,\"rot\":246,\"layer\":117},{\"node\":\"g29\",\"kind\":\"light\",\"u\":0.5385,\"v\":0.6702,\"r\":0.0684,\"rot\":180,\"layer\":118},{\"node\":\"g18\",\"kind\":\"screw\",\"u\":0.5207,\"v\":0.8515,\"r\":0.0352,\"rot\":84,\"layer\":119},{\"node\":\"m44\",\"kind\":\"screw\",\"u\":0.9711,\"v\":0.712,\"r\":0.0537,\"rot\":90,\"layer\":120},{\"node\":\"g34\",\"kind\":\"light\",\"u\":0.4519,\"v\":0.5268,\"r\":0.0549,\"rot\":270,\"layer\":121},{\"node\":\"g52\",\"kind\":\"plate\",\"u\":0.6905,\"v\":0.0956,\"r\":0.0331,\"rot\":270,\"layer\":122},{\"node\":\"g56\",\"kind\":\"screw\",\"u\":0.5638,\"v\":0.9168,\"r\":0.0314,\"rot\":354,\"layer\":123},{\"node\":\"g48\",\"kind\":\"screw\",\"u\":0.6615,\"v\":0.5119,\"r\":0.0343,\"rot\":213,\"layer\":124},{\"node\":\"m24\",\"kind\":\"light\",\"u\":0.7051,\"v\":0.8683,\"r\":0.0459,\"rot\":180,\"layer\":125},{\"node\":\"g49\",\"kind\":\"pipe\",\"u\":0.221,\"v\":0.6085,\"r\":0.0508,\"rot\":147,\"layer\":126},{\"node\":\"g42\",\"kind\":\"screw\",\"u\":0.7335,\"v\":0.4579,\"r\":0.0307,\"rot\":180,\"layer\":127},{\"node\":\"g50\",\"kind\":\"screw\",\"u\":0.3901,\"v\":0.2785,\"r\":0.0328,\"rot\":207,\"layer\":128},{\"node\":\"g47\",\"kind\":\"light\",\"u\":0.0306,\"v\":0.7408,\"r\":0.0541,\"rot\":264,\"layer\":129},{\"node\":\"m17\",\"kind\":\"screw\",\"u\":0.0469,\"v\":0.0894,\"r\":0.0437,\"rot\":303,\"layer\":130},{\"node\":\"g36\",\"kind\":\"pipe\",\"u\":0.8719,\"v\":0.2987,\"r\":0.0483,\"rot\":339,\"layer\":131},{\"node\":\"m43\",\"kind\":\"pipe\",\"u\":0.3398,\"v\":0.4479,\"r\":0.0449,\"rot\":180,\"layer\":132},{\"node\":\"m04\",\"kind\":\"light\",\"u\":0.7884,\"v\":0.7835,\"r\":0.0464,\"rot\":90,\"layer\":133},{\"node\":\"m10\",\"kind\":\"pipe\",\"u\":0.8126,\"v\":0.8292,\"r\":0.0518,\"rot\":270,\"layer\":134},{\"node\":\"g37\",\"kind\":\"screw\",\"u\":0.6884,\"v\":0.0248,\"r\":0.0355,\"rot\":69,\"layer\":135},{\"node\":\"g45\",\"kind\":\"light\",\"u\":0.1146,\"v\":0.0413,\"r\":0.0601,\"rot\":210,\"layer\":136},{\"node\":\"m02\",\"kind\":\"screw\",\"u\":0.9712,\"v\":0.9375,\"r\":0.0437,\"rot\":90,\"layer\":137},{\"node\":\"g27\",\"kind\":\"screw\",\"u\":0.7159,\"v\":0.4876,\"r\":0.0333,\"rot\":333,\"layer\":138},{\"node\":\"g43\",\"kind\":\"screw\",\"u\":0.4201,\"v\":0.9385,\"r\":0.0311,\"rot\":138,\"layer\":139},{\"node\":\"g08\",\"kind\":\"plate\",\"u\":0.7815,\"v\":0.9163,\"r\":0.0352,\"rot\":270,\"layer\":140},{\"node\":\"g23\",\"kind\":\"light\",\"u\":0.1507,\"v\":0.3107,\"r\":0.0455,\"rot\":210,\"layer\":141},{\"node\":\"m36\",\"kind\":\"pipe\",\"u\":0.6026,\"v\":0.615,\"r\":0.0425,\"rot\":63,\"layer\":142},{\"node\":\"m32\",\"kind\":\"screw\",\"u\":0.5533,\"v\":0.1332,\"r\":0.0334,\"rot\":117,\"layer\":143},{\"node\":\"g58\",\"kind\":\"pipe\",\"u\":0.9433,\"v\":0.195,\"r\":0.0379,\"rot\":207,\"layer\":144},{\"node\":\"g20\",\"kind\":\"screw\",\"u\":0.0707,\"v\":0.9502,\"r\":0.0382,\"rot\":183,\"layer\":145},{\"node\":\"m33\",\"kind\":\"light\",\"u\":0.2566,\"v\":0.3799,\"r\":0.046,\"rot\":243,\"layer\":146},{\"node\":\"m37\",\"kind\":\"pipe\",\"u\":0.7698,\"v\":0.2851,\"r\":0.0364,\"rot\":114,\"layer\":147},{\"node\":\"g01\",\"kind\":\"light\",\"u\":0.4918,\"v\":0.928,\"r\":0.0437,\"rot\":138,\"layer\":148},{\"node\":\"m16\",\"kind\":\"light\",\"u\":0.1816,\"v\":0.0189,\"r\":0.0622,\"rot\":90,\"layer\":149},{\"node\":\"m30\",\"kind\":\"pipe\",\"u\":0.33,\"v\":0.3368,\"r\":0.0515,\"rot\":303,\"layer\":150},{\"node\":\"m39\",\"kind\":\"pipe\",\"u\":0.0519,\"v\":0.8586,\"r\":0.0425,\"rot\":165,\"layer\":151},{\"node\":\"m22\",\"kind\":\"pipe\",\"u\":0.806,\"v\":0.4967,\"r\":0.0548,\"rot\":63,\"layer\":152},{\"node\":\"m28\",\"kind\":\"pipe\",\"u\":0.0514,\"v\":0.19,\"r\":0.0473,\"rot\":240,\"layer\":153},{\"node\":\"m10\",\"kind\":\"pipe\",\"u\":0.6295,\"v\":0.3626,\"r\":0.0439,\"rot\":237,\"layer\":154},{\"node\":\"g28\",\"kind\":\"screw\",\"u\":0.5608,\"v\":0.543,\"r\":0.0457,\"rot\":90,\"layer\":155},{\"node\":\"g56\",\"kind\":\"screw\",\"u\":0.2562,\"v\":0.6812,\"r\":0.0371,\"rot\":183,\"layer\":156},{\"node\":\"m32\",\"kind\":\"screw\",\"u\":0.5373,\"v\":0.5134,\"r\":0.0334,\"rot\":270,\"layer\":157},{\"node\":\"m11\",\"kind\":\"light\",\"u\":0.5466,\"v\":0.2054,\"r\":0.0477,\"rot\":270,\"layer\":158},{\"node\":\"g27\",\"kind\":\"screw\",\"u\":0.177,\"v\":0.2216,\"r\":0.0393,\"rot\":90,\"layer\":159},{\"node\":\"g49\",\"kind\":\"pipe\",\"u\":0.787,\"v\":0.6175,\"r\":0.0508,\"rot\":33,\"layer\":160},{\"node\":\"m37\",\"kind\":\"pipe\",\"u\":0.8188,\"v\":0.0247,\"r\":0.0364,\"rot\":36,\"layer\":161},{\"node\":\"g42\",\"kind\":\"screw\",\"u\":0.5775,\"v\":0.649,\"r\":0.0363,\"rot\":333,\"layer\":162},{\"node\":\"g20\",\"kind\":\"screw\",\"u\":0.4111,\"v\":0.4507,\"r\":0.0323,\"rot\":315,\"layer\":163},{\"node\":\"m16\",\"kind\":\"light\",\"u\":0.4191,\"v\":0.5903,\"r\":0.0373,\"rot\":90,\"layer\":164},{\"node\":\"g47\",\"kind\":\"light\",\"u\":0.5336,\"v\":0.9855,\"r\":0.0541,\"rot\":180,\"layer\":165},{\"node\":\"g01\",\"kind\":\"light\",\"u\":0.1545,\"v\":0.7558,\"r\":0.0437,\"rot\":327,\"layer\":166},{\"node\":\"g08\",\"kind\":\"plate\",\"u\":0.9745,\"v\":0.1685,\"r\":0.0416,\"rot\":270,\"layer\":167},{\"node\":\"m24\",\"kind\":\"light\",\"u\":0.9726,\"v\":0.1239,\"r\":0.0459,\"rot\":90,\"layer\":168},{\"node\":\"g43\",\"kind\":\"screw\",\"u\":0.9748,\"v\":0.0263,\"r\":0.0311,\"rot\":0,\"layer\":169},{\"node\":\"g29\",\"kind\":\"light\",\"u\":0.1356,\"v\":0.5155,\"r\":0.0684,\"rot\":336,\"layer\":170},{\"node\":\"g36\",\"kind\":\"pipe\",\"u\":0.8051,\"v\":0.2012,\"r\":0.0483,\"rot\":147,\"layer\":171},{\"node\":\"g18\",\"kind\":\"screw\",\"u\":0.4515,\"v\":0.1307,\"r\":0.0298,\"rot\":333,\"layer\":172},{\"node\":\"m44\",\"kind\":\"screw\",\"u\":0.0584,\"v\":0.7108,\"r\":0.0455,\"rot\":339,\"layer\":173},{\"node\":\"g13\",\"kind\":\"screw\",\"u\":0.4519,\"v\":0.692,\"r\":0.0276,\"rot\":132,\"layer\":174},{\"node\":\"g04\",\"kind\":\"screw\",\"u\":0.6068,\"v\":0.4548,\"r\":0.0329,\"rot\":235,\"layer\":175},{\"node\":\"g22\",\"kind\":\"gear\",\"u\":0.0429,\"v\":0.0235,\"r\":0.0372,\"rot\":170,\"layer\":176},{\"node\":\"g02\",\"kind\":\"plate\",\"u\":0.8257,\"v\":0.6466,\"r\":0.029,\"rot\":310,\"layer\":177},{\"node\":\"m18\",\"kind\":\"valve\",\"u\":0.8285,\"v\":0.7468,\"r\":0.0373,\"rot\":126,\"layer\":178},{\"node\":\"g17\",\"kind\":\"light\",\"u\":0.666,\"v\":0.1148,\"r\":0.0266,\"rot\":11,\"layer\":179},{\"node\":\"g54\",\"kind\":\"gear\",\"u\":0.0363,\"v\":0.8873,\"r\":0.0372,\"rot\":113,\"layer\":180},{\"node\":\"m45\",\"kind\":\"screw\",\"u\":0.1632,\"v\":0.0951,\"r\":0.0276,\"rot\":114,\"layer\":181},{\"node\":\"g33\",\"kind\":\"gear\",\"u\":0.875,\"v\":0.0207,\"r\":0.0371,\"rot\":249,\"layer\":182},{\"node\":\"g07\",\"kind\":\"gear\",\"u\":0.0363,\"v\":0.927,\"r\":0.0373,\"rot\":120,\"layer\":183},{\"node\":\"g14\",\"kind\":\"screw\",\"u\":0.4222,\"v\":0.8274,\"r\":0.0285,\"rot\":155,\"layer\":184},{\"node\":\"g32\",\"kind\":\"screw\",\"u\":0.4019,\"v\":0.5456,\"r\":0.0286,\"rot\":76,\"layer\":185},{\"node\":\"m07\",\"kind\":\"plate\",\"u\":0.3164,\"v\":0.019,\"r\":0.0342,\"rot\":295,\"layer\":186},{\"node\":\"g26\",\"kind\":\"gear\",\"u\":0.3209,\"v\":0.5172,\"r\":0.0343,\"rot\":238,\"layer\":187},{\"node\":\"g40\",\"kind\":\"gear\",\"u\":0.9626,\"v\":0.6153,\"r\":0.0355,\"rot\":92,\"layer\":188},{\"node\":\"g46\",\"kind\":\"plate\",\"u\":0.9198,\"v\":0.7154,\"r\":0.0329,\"rot\":42,\"layer\":189},{\"node\":\"m03\",\"kind\":\"plate\",\"u\":0.9295,\"v\":0.1079,\"r\":0.0358,\"rot\":147,\"layer\":190},{\"node\":\"m20\",\"kind\":\"pipe\",\"u\":0.395,\"v\":0.7688,\"r\":0.0319,\"rot\":345,\"layer\":191},{\"node\":\"g53\",\"kind\":\"gear\",\"u\":0.0366,\"v\":0.1205,\"r\":0.0376,\"rot\":118,\"layer\":192},{\"node\":\"g03\",\"kind\":\"plate\",\"u\":0.58,\"v\":0.7718,\"r\":0.0308,\"rot\":325,\"layer\":193},{\"node\":\"g15\",\"kind\":\"light\",\"u\":0.082,\"v\":0.3599,\"r\":0.0285,\"rot\":180,\"layer\":194},{\"node\":\"m15\",\"kind\":\"plate\",\"u\":0.916,\"v\":0.3594,\"r\":0.0299,\"rot\":293,\"layer\":195},{\"node\":\"g25\",\"kind\":\"screw\",\"u\":0.9797,\"v\":0.783,\"r\":0.0286,\"rot\":0,\"layer\":196},{\"node\":\"g37\",\"kind\":\"screw\",\"u\":0.8516,\"v\":0.1986,\"r\":0.0251,\"rot\":15,\"layer\":197},{\"node\":\"m20\",\"kind\":\"pipe\",\"u\":0.3683,\"v\":0.3973,\"r\":0.0225,\"rot\":0,\"layer\":198},{\"node\":\"g50\",\"kind\":\"screw\",\"u\":0.5723,\"v\":0.5167,\"r\":0.0273,\"rot\":30,\"layer\":199},{\"node\":\"g52\",\"kind\":\"plate\",\"u\":0.6299,\"v\":0.1286,\"r\":0.0276,\"rot\":30,\"layer\":200},{\"node\":\"m36\",\"kind\":\"pipe\",\"u\":0.824,\"v\":0.9141,\"r\":0.0354,\"rot\":115,\"layer\":201},{\"node\":\"g35\",\"kind\":\"screw\",\"u\":0.6582,\"v\":0.8069,\"r\":0.0284,\"rot\":115,\"layer\":202},{\"node\":\"g11\",\"kind\":\"pipe\",\"u\":0.1266,\"v\":0.9024,\"r\":0.0326,\"rot\":125,\"layer\":203},{\"node\":\"g23\",\"kind\":\"light\",\"u\":0.8713,\"v\":0.9596,\"r\":0.0321,\"rot\":15,\"layer\":204},{\"node\":\"g17\",\"kind\":\"light\",\"u\":0.5918,\"v\":0.2254,\"r\":0.0221,\"rot\":40,\"layer\":205},{\"node\":\"m35\",\"kind\":\"light\",\"u\":0.6758,\"v\":0.9754,\"r\":0.0401,\"rot\":270,\"layer\":206},{\"node\":\"g42\",\"kind\":\"screw\",\"u\":0.3751,\"v\":0.704,\"r\":0.0256,\"rot\":210,\"layer\":207},{\"node\":\"g24\",\"kind\":\"light\",\"u\":0.6484,\"v\":0.7176,\"r\":0.0381,\"rot\":255,\"layer\":208},{\"node\":\"g20\",\"kind\":\"screw\",\"u\":0.877,\"v\":0.6049,\"r\":0.0269,\"rot\":165,\"layer\":209},{\"node\":\"g08\",\"kind\":\"plate\",\"u\":0.4473,\"v\":0.6038,\"r\":0.0294,\"rot\":30,\"layer\":210},{\"node\":\"m32\",\"kind\":\"screw\",\"u\":0.5977,\"v\":0.9096,\"r\":0.0278,\"rot\":115,\"layer\":211},{\"node\":\"g08\",\"kind\":\"plate\",\"u\":0.7686,\"v\":0.9431,\"r\":0.0294,\"rot\":125,\"layer\":212},{\"node\":\"g25\",\"kind\":\"screw\",\"u\":0.0323,\"v\":0.9542,\"r\":0.0286,\"rot\":115,\"layer\":213},{\"node\":\"g49\",\"kind\":\"pipe\",\"u\":0.2867,\"v\":0.3532,\"r\":0.0358,\"rot\":45,\"layer\":214},{\"node\":\"g49\",\"kind\":\"pipe\",\"u\":0.2543,\"v\":0.7081,\"r\":0.0358,\"rot\":285,\"layer\":215}]}","pod/worlds/boards/mache/provenance.json":"{\r\n    \"sourceGame\":  \"D:/MBS-corgi-refresh/games/corgi/index.html\",\r\n    \"usageEvidence\":  \"tv/channels/corgi.js lines 449 and 1538 load school-drawing-1..3.png as collectible pages. games/corgi/index.html line 40 displays corgi-game-still.png.\",\r\n    \"note\":  \"Byte-for-byte copies of public game assets supplied by Ian; no private character memory used.\",\r\n    \"assets\":  [\r\n                   {\r\n                       \"file\":  \"corgi-game-still.png\",\r\n                       \"source\":  \"D:/MBS-corgi-refresh/tv/assets/corgi-game-still.png\",\r\n                       \"sha256\":  \"f8fad5d6e527bc05d738b469db345454dfd9e7b58cca916a119a224a30aa85c9\"\r\n                   },\r\n                   {\r\n                       \"file\":  \"school-drawing-1.png\",\r\n                       \"source\":  \"D:/MBS-corgi-refresh/tv/assets/school-drawing-1.png\",\r\n                       \"sha256\":  \"e710ba872f8085e1d9f76920eaad6f643ae317c93f6a2e3b99c703f841431540\"\r\n                   },\r\n                   {\r\n                       \"file\":  \"school-drawing-2.png\",\r\n                       \"source\":  \"D:/MBS-corgi-refresh/tv/assets/school-drawing-2.png\",\r\n                       \"sha256\":  \"52e7828ef668b368eabc27a78911d621ad9e2f24de4bf4c9d06c9a2fc606dc8c\"\r\n                   },\r\n                   {\r\n                       \"file\":  \"school-drawing-3.png\",\r\n                       \"source\":  \"D:/MBS-corgi-refresh/tv/assets/school-drawing-3.png\",\r\n                       \"sha256\":  \"e9e73d2264544d09cafe03bcd9024b317d4de8599a4b0c9c877ebbcf7279d06c\"\r\n                   }\r\n               ]\r\n}\r\n","pod/worlds/boards/mache/shipping-derivatives.json":"[\r\n  {\r\n    \"source\": \"school-drawing-1.png\",\r\n    \"sourceSha256\": \"e710ba872f8085e1d9f76920eaad6f643ae317c93f6a2e3b99c703f841431540\",\r\n    \"sourceBytes\": 3066544,\r\n    \"file\": \"school-drawing-1.webp\",\r\n    \"bytes\": 257804,\r\n    \"sha256\": \"5e863a5be259159fa3426b0049b820c87f2a4c7cf469b91a54cb561cc3f58bd9\"\r\n  },\r\n  {\r\n    \"source\": \"school-drawing-2.png\",\r\n    \"sourceSha256\": \"52e7828ef668b368eabc27a78911d621ad9e2f24de4bf4c9d06c9a2fc606dc8c\",\r\n    \"sourceBytes\": 3051869,\r\n    \"file\": \"school-drawing-2.webp\",\r\n    \"bytes\": 250600,\r\n    \"sha256\": \"59a2b19573eb2c5536b351faa6ae4966fab8a4460a502c830cf05d8a54131996\"\r\n  },\r\n  {\r\n    \"source\": \"school-drawing-3.png\",\r\n    \"sourceSha256\": \"e9e73d2264544d09cafe03bcd9024b317d4de8599a4b0c9c877ebbcf7279d06c\",\r\n    \"sourceBytes\": 3428927,\r\n    \"file\": \"school-drawing-3.webp\",\r\n    \"bytes\": 214366,\r\n    \"sha256\": \"87b228ea81764215c7a8b971b6bdae453fe7ddfb7d28b6745e4deb3dfad231c0\"\r\n  },\r\n  {\r\n    \"source\": \"corgi-game-still.png\",\r\n    \"sourceSha256\": \"f8fad5d6e527bc05d738b469db345454dfd9e7b58cca916a119a224a30aa85c9\",\r\n    \"sourceBytes\": 121666,\r\n    \"file\": \"corgi-game-still.webp\",\r\n    \"bytes\": 34210,\r\n    \"sha256\": \"95e88782d615c85dacf40ded79ceb93ea9ce325f5498d1f7f31099494ae21d85\"\r\n  }\r\n]","pose-classifier.mjs":"// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// k-NN pose classifier: a plain-JS reimplementation of the pose classification method documented by Google\r\n// MediaPipe (the \"Pose classification\" colab for MediaPipe Pose, Apache-2.0): normalise landmarks on the hip centre\r\n// and torso size, embed pairwise joint distances, keep the top 30 samples by max distance, then the top 10 by mean\r\n// distance, and vote. A mirrored pose (x negated, as in the original) also matches, so one side's recording\r\n// serves both. Differences: 2D (the engine keeps no z), feet and ankles dropped (they never enter counting).\r\n// Counting on these votes (EMA, enter/exit thresholds) lives in movement-engine.mjs.\r\nexport const EMBED_LANDMARKS=[11,12,13,14,15,16,23,24,25,26];\r\nconst PAIRS=[[11,13],[12,14],[13,15],[14,16],[23,25],[24,26],[11,15],[12,16],[23,15],[24,16],[13,14],[25,26],[15,16]];\r\n// p: aspect-corrected points (features().p). Returns 2 numbers per pair, torso first.\r\nexport function embed(p){\r\n const hx=(p[23].x+p[24].x)/2,hy=(p[23].y+p[24].y)/2,sx=(p[11].x+p[12].x)/2,sy=(p[11].y+p[12].y)/2;\r\n const size=Math.max(Math.hypot(sx-hx,sy-hy)*2.5,...EMBED_LANDMARKS.map(i=>Math.hypot(p[i].x-hx,p[i].y-hy)))||1;\r\n const e=[(sx-hx)/size,(sy-hy)/size];\r\n for(const [a,b] of PAIRS)e.push((p[b].x-p[a].x)/size,(p[b].y-p[a].y)/size);\r\n return e;\r\n}\r\n// samples file: {exercise, need:[landmark ids], samples:[[label,[embedding]],…]} from scripts/pose-samples.mjs.\r\nexport class PoseClassifier{\r\n constructor({need=[11,12,23,24],samples}){this.need=need;this.samples=samples.map(([label,e])=>({label,e:Float64Array.from(e)}));}\r\n classify(p){\r\n  const e=embed(p),m=e.map((v,i)=>i%2?v:-v),n=e.length;\r\n  const dist=(s,q)=>{let max=0,sum=0;for(let i=0;i<n;i++){const d=Math.abs(s[i]-q[i]);sum+=d;if(d>max)max=d;}return [max,sum/n];};\r\n  const scored=this.samples.map(({label,e:s})=>{const a=dist(s,e),b=dist(s,m);return {label,max:Math.min(a[0],b[0]),mean:Math.min(a[1],b[1])};});\r\n  const top=scored.sort((a,b)=>a.max-b.max).slice(0,30).sort((a,b)=>a.mean-b.mean).slice(0,10);\r\n  const votes={};for(const {label} of top)votes[label]=(votes[label]??0)+1/top.length;\r\n  return votes;\r\n }\r\n}\r\n","pose-frame.mjs":"// One pending camera callback, cancelled when the set stops. Prefer fresh decoded frames.\r\nexport class PoseFrameScheduler {\r\n constructor(video,{request=requestAnimationFrame,cancel=cancelAnimationFrame}={}){this.video=video;this.request=request;this.cancel=cancel;this.id=null;this.videoFrame=false;this.frame=null;}\r\n schedule(callback){\r\n  this.stop();this.videoFrame=typeof this.video.requestVideoFrameCallback==='function';\r\n  const run=(time,metadata)=>{this.id=null;this.frame=metadata??null;callback();};\r\n  this.id=this.videoFrame?this.video.requestVideoFrameCallback(run):this.request(run);\r\n }\r\n stop(){if(this.id!==null){if(this.videoFrame)this.video.cancelVideoFrameCallback(this.id);else this.cancel(this.id);this.id=null;}}\r\n}\r\n","pose-recorder.mjs":"// Dev-only, loaded by ?recordPose=1: records the camera workout's landmark stream plus tap-labelled \"down\"/\"up\"\r\n// frames as a JSON download for scripts/pose-samples.mjs. Tap Down or Up, get into that position during the\r\n// 3 s countdown and hold it; the next 1.5 s of frames carry the label. Switching exercise starts a new recording.\r\nexport function mountPoseRecorder(stage){\r\n let recording=null;\r\n const bar=document.createElement('div');bar.setAttribute('role','group');bar.setAttribute('aria-label','Pose recorder');\r\n bar.style.cssText='position:absolute;left:8px;right:8px;bottom:max(16px,env(safe-area-inset-bottom));z-index:2;display:flex;gap:8px;align-items:stretch;font:600 15px system-ui,sans-serif;color:#fff';\r\n const status=document.createElement('output');status.setAttribute('aria-live','polite');status.style.cssText='flex:1;min-width:0;padding:8px 10px;border-radius:12px;background:#000a';status.textContent='Recorder waiting for tracking';\r\n const button=(text,onClick)=>{const b=document.createElement('button');b.type='button';b.textContent=text;b.style.cssText='min-width:64px;min-height:48px;border:0;border-radius:12px;background:#fff;color:#000;font:inherit';b.addEventListener('click',onClick);bar.append(b);};\r\n const show=()=>{\r\n  if(!recording)return;const now=performance.now(),last=recording.labels.at(-1);\r\n  status.textContent=last&&now<last.from?`${last.label.toUpperCase()} in ${Math.ceil((last.from-now)/1000)}…`:last&&now<=last.to?`Hold ${last.label}…`:`${recording.exercise} · ${recording.frames.length} frames · ${recording.labels.length} labels · ${(window.myr5TestState?.rate??0).toFixed(1)}/s ${window.myr5TestState?.delegate||''}`;\r\n };\r\n const label=name=>{if(!recording)return;const from=Math.round(performance.now()+3000);recording.labels.push({label:name,from,to:from+1500});show();};\r\n bar.append(status);button('Down',()=>label('down'));button('Up',()=>label('up'));\r\n button('Save',()=>{\r\n  if(!recording?.frames.length)return;\r\n  const link=document.createElement('a');link.href=URL.createObjectURL(new Blob([JSON.stringify(recording)],{type:'application/json'}));\r\n  link.download=`recordPose-${recording.exercise}-${new Date().toISOString().slice(0,19).replace(/[:T]/g,'-')}.json`;\r\n  bar.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(link.href),10000);status.textContent='Saved '+link.download;\r\n });\r\n stage.append(bar);\r\n addEventListener('myr5:pose',({detail})=>{\r\n  const exercise=window.myr5TestState?.motion?.mode;if(stage.hidden||!detail.points||!exercise)return;\r\n  if(recording?.exercise!==exercise)recording={version:1,exercise,aspect:detail.width/detail.height,frames:[],labels:[]};\r\n  recording.frames.push([Math.round(detail.now),detail.points.slice(0,27).map(v=>[+v.x.toFixed(4),+v.y.toFixed(4),+(v.visibility??0).toFixed(3)]),...detail.world?[detail.world.slice(0,27).map(v=>[+v.x.toFixed(4),+v.y.toFixed(4),+v.z.toFixed(4)])]:[]]);\r\n  show();\r\n });\r\n}\r\n","pose-samples.mjs":"// Generated by scripts/pose-samples.mjs from pose-samples/*.samples.json. Do not edit by hand.\r\n// Exercises listed here count reps with the k-NN pose classifier; every other exercise uses the rules.\r\nexport default {};\r\n","pose.html":"<!doctype html>\r\n<!-- Pixel test adapted from Ian's MYR5 pose.html in the Barehands project.\r\n     Barehands copyright (c) 2026 Jared Rhodenizer; AGPL-3.0-or-later.\r\n     Source and license: /pose.html and /LICENSE. Camera frames stay in this browser. -->\r\n<html lang=\"en\">\r\n<meta charset=\"utf-8\">\r\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\r\n<link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script><title>MYR5 · AR alien workout coach</title>\r\n<meta name=\"description\" content=\"MYR5: free AR workout coach in your browser. A 3D alien coach counts your reps with your phone camera, on-device. No equipment, no headset.\">\r\n<link rel=\"canonical\" href=\"https://myr5.mominc.online/\">\r\n<meta property=\"og:type\" content=\"website\"><meta property=\"og:site_name\" content=\"MYR5 Coach\"><meta property=\"og:url\" content=\"https://myr5.mominc.online/\">\r\n<meta property=\"og:title\" content=\"MYR5 · AR alien workout coach\">\r\n<meta property=\"og:description\" content=\"Your phone camera counts reps on-device; finish sets to level up your alien coach. Free, no download.\">\r\n<meta property=\"og:image\" content=\"https://myr5.mominc.online/icons/myr5-alien-512.png\">\r\n<meta name=\"twitter:card\" content=\"summary\"><meta name=\"twitter:title\" content=\"MYR5 · AR alien workout coach\"><meta name=\"twitter:description\" content=\"Your phone camera counts reps on-device; finish sets to level up your alien coach. Free, no download.\"><meta name=\"twitter:image\" content=\"https://myr5.mominc.online/icons/myr5-alien-512.png\">\r\n<script type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://myr5.mominc.online/#operator\",\"name\":\"MYR5\",\"description\":\"Independent app maker of MYR5 Coach\",\"url\":\"https://myr5.mominc.online/about\",\"email\":\"ianmyersrocks97@gmail.com\"},{\"@type\":\"SoftwareApplication\",\"@id\":\"https://myr5.mominc.online/#app\",\"name\":\"MYR5 Coach\",\"alternateName\":\"MYR5\",\"url\":\"https://myr5.mominc.online/\",\"description\":\"Free browser workout app with a 3D alien coach. The phone camera counts reps and holds on the device; video frames are not uploaded. Installs as a web app; no equipment or headset. Intended for ages 13 and up.\",\"applicationCategory\":\"HealthApplication\",\"applicationSubCategory\":\"Fitness\",\"operatingSystem\":\"Any modern browser (Android, iOS, desktop)\",\"browserRequirements\":\"Requires JavaScript, WebGL and camera access for rep counting\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"typicalAgeRange\":\"13-\",\"author\":{\"@id\":\"https://myr5.mominc.online/#operator\"},\"publisher\":{\"@id\":\"https://myr5.mominc.online/#operator\"},\"mainEntityOfPage\":\"https://myr5.mominc.online/about\"}]}</script>\r\n<meta name=\"theme-color\" content=\"#21172f\">\r\n<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\r\n<meta name=\"apple-mobile-web-app-title\" content=\"MYR5 Coach\">\r\n<link rel=\"manifest\" href=\"/manifest.webmanifest?v=myr5-alien-v1\">\r\n<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script>\r\n<link rel=\"modulepreload\" href=\"/launch-runtime.mjs?v=startup-recovery-1\">\r\n<link rel=\"modulepreload\" href=\"/app-runtime.mjs?v=rest-flow-1\">\r\n<link rel=\"modulepreload\" href=\"/local-coach-runtime.mjs?v=rest-flow-1\">\r\n<link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\" crossorigin>\r\n<link rel=\"preconnect\" href=\"https://storage.googleapis.com\" crossorigin>\r\n<link rel=\"icon\" type=\"image/webp\" href=\"/pod/mom-inc-mark-96.webp\"><link rel=\"apple-touch-icon\" href=\"/pod/mom-inc-mark-96.webp\">\r\n<link rel=\"stylesheet\" href=\"/launch.css\">\r\n<link rel=\"stylesheet\" href=\"/menu.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/pod.css\">\r\n<link rel=\"stylesheet\" href=\"/flip-display.css\">\r\n<link rel=\"stylesheet\" href=\"/food-live.css\"><link rel=\"stylesheet\" href=\"/pod/holo.css\">\r\n<link rel=\"stylesheet\" href=\"/battle-pass-page.css\">\r\n<link rel=\"stylesheet\" href=\"/spotify-terminal.css\">\r\n<link rel=\"stylesheet\" href=\"/actual-dj.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/whiteboard.css\">\r\n<link rel=\"stylesheet\" href=\"/focus-library.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/control-board.css\">\r\n<link rel=\"stylesheet\" href=\"/arcade/tub-flight/style.css\">\r\n<link rel=\"stylesheet\" href=\"/meditation.css\"><link rel=\"stylesheet\" href=\"/pod/rest-arena.css\">\r\n<link rel=\"stylesheet\" href=\"/personal-tracker.css\"><link rel=\"stylesheet\" href=\"/scoreboard.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/retro-rooms.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/weapon-abilities.css\">\r\n<link rel=\"stylesheet\" href=\"/coach-hub.css\"><link rel=\"stylesheet\" href=\"/settings-frame.css\"><link rel=\"stylesheet\" href=\"/settings-crt.css\"><link rel=\"stylesheet\" href=\"/achievements-board.css\"><link rel=\"stylesheet\" href=\"/pod/home-character.css\">\r\n<link rel=\"stylesheet\" href=\"/camera-workout.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/ship-interior.css\">\r\n<link rel=\"stylesheet\" href=\"/pod/persistent-chrome.css\">\r\n<link rel=\"stylesheet\" href=\"/shader-effects.css\">\r\n<div class=\"device-bezel\" aria-hidden=\"true\"><i class=\"case-screw case-tl\"></i><i class=\"case-screw case-tr\"></i><i class=\"case-screw case-bl\"></i><i class=\"case-screw case-br\"></i></div>\r\n<noscript><h1>MYR5 Coach: AR alien workout coach</h1><p>MYR5 is a free web app for bodyweight workouts with a 3D alien coach. Your phone's camera counts reps and holds on the device; video is not uploaded. It installs from the browser, with no store, headset or equipment. Intended for ages 13 and up. Made by MYR5, an independent app. The app itself needs JavaScript. <a href=\"/about\">About MYR5</a> | <a href=\"/privacy\">Privacy</a></p></noscript>\r\n<div id=\"podShipBackdrop\" class=\"pod-ship-backdrop\" aria-hidden=\"true\" data-motion-running=\"false\">\r\n <img class=\"pod-ship-nebula\" src=\"/pod/assets/nebula.webp\" alt=\"\" draggable=\"false\">\r\n <img class=\"pod-ship-cutout\" src=\"/pod/assets/ship-interior-cutout.png\" alt=\"\" draggable=\"false\">\r\n <span class=\"pod-ship-lights\" aria-hidden=\"true\"></span>\r\n</div>\r\n<header class=\"ship-header\"><a class=\"mom-brand\" href=\"#pod\" aria-label=\"Return to the pod\"><img src=\"/pod/mom-inc-mark-96.webp\" width=\"36\" height=\"36\" alt=\"MOM Inc\"></a><button id=\"openSettings\" class=\"icon-button ship-settings-toggle\" aria-label=\"Open settings terminal\" aria-haspopup=\"dialog\" aria-controls=\"settings\"><span class=\"ship-settings-handle\" aria-hidden=\"true\"></span><span class=\"ship-settings-screen\" aria-hidden=\"true\"><strong>SETTINGS</strong></span></button></header>\r\n<main id=\"homeScreen\" class=\"pod-page\">\r\n <nav class=\"pod-header-actions\" aria-label=\"Pod character\"><button id=\"openIdentity\" type=\"button\"><canvas id=\"miniAvatar\" width=\"64\" height=\"96\" aria-hidden=\"true\"></canvas><span id=\"guestLabel\">Anonymous guest</span></button><a data-access-route=\"/creature/index.html\" href=\"/creature/index.html\">Customize coach</a></nav>\r\n <div class=\"deck-strip\"><span><i class=\"status-light\"></i> POD 005</span><details id=\"podGoals\" class=\"pod-goals\"><summary>GOALS <span data-goals-status></span></summary><div class=\"pod-goals-menu\"></div></details><span id=\"levelBadge\">WORKOUT LV. 01</span></div>\r\n <div id=\"trainingView\" class=\"training-view\">\r\n <div class=\"containment-bay\">\r\n <section id=\"view\" class=\"containment-pod\" aria-label=\"Your character's viewing port\">\r\n  <i class=\"frame-bolt tl\" aria-hidden=\"true\"></i><i class=\"frame-bolt tr\" aria-hidden=\"true\"></i><i class=\"frame-bolt bl\" aria-hidden=\"true\"></i><i class=\"frame-bolt br\" aria-hidden=\"true\"></i><div class=\"pod-top-vent\" aria-hidden=\"true\"></div>\r\n  <div class=\"pod-rim rim-left\" aria-hidden=\"true\"></div><div class=\"pod-rim rim-right\" aria-hidden=\"true\"></div>\r\n  <div class=\"pod-inscription\" aria-hidden=\"true\">M Y R 5</div>\r\n  <div class=\"coach-line\"><span class=\"voice-led\" aria-hidden=\"true\"></span><p id=\"coachCaption\" aria-live=\"polite\">Ready when you are.</p></div>\r\n  <div id=\"coachMount\" class=\"coach-mount\"><p id=\"coachLoading\">Loading…</p></div>\r\n  <section id=\"homeCharacter\" class=\"home-character\" aria-label=\"Your Gala character\" hidden><button type=\"button\" aria-label=\"Play your equipped weapon attack\"><canvas width=\"160\" height=\"168\" role=\"img\" aria-label=\"Your Gala character\"></canvas><span data-weapon></span></button></section>\r\n  <div class=\"pod-floor\" aria-hidden=\"true\"></div>\r\n  <div id=\"cameraPreview\" class=\"camera-preview\"><video id=\"v\" playsinline muted></video><canvas id=\"c\"></canvas><span id=\"previewLabel\">CAMERA OFF</span></div>\r\n </section></div>\r\n  <div id=\"hud\" class=\"simple-counter\"><div class=\"counter-readout\"><small id=\"setNumber\">SET 01</small><span id=\"primary\">00</span><div class=\"counter-target\"><span id=\"primaryLabel\">reps</span><span class=\"target-divider\" aria-hidden=\"true\"></span><span>Goal <span id=\"goalValue\">3</span></span></div></div></div>\r\n </div>\r\n <section id=\"controls\" class=\"workout-console\" aria-label=\"Your set\">\r\n\r\n  <div class=\"control-board difficulty-control\" role=\"group\" aria-label=\"Workout selector\">\r\n   <div class=\"cb-arcade-keys\">\r\n    <button type=\"button\" id=\"coolCardio\" class=\"cb-cardio\" aria-label=\"Start endless Subway Burpees runner\"><span class=\"cb-cardio-led\" aria-hidden=\"true\"></span><strong>SUBWAY BURPEES</strong><small aria-hidden=\"true\">⚡ 🦵</small></button>\r\n    <button type=\"button\" id=\"plankFlight\" class=\"cb-cardio\" aria-label=\"Start Jet Plank Joyride\"><span class=\"cb-cardio-led\" aria-hidden=\"true\"></span><strong>JET PLANK JOYRIDE</strong><small aria-hidden=\"true\">🎯 ⚖️</small></button>\r\n    <button type=\"button\" id=\"coverCrisis\" class=\"cb-cardio\" aria-label=\"Start endless Crouching Crisis shooter\"><span class=\"cb-cardio-led\" aria-hidden=\"true\"></span><strong>CROUCHING CRISIS</strong><small aria-hidden=\"true\">🦵 🥊</small></button>\r\n    <button type=\"button\" id=\"openDance\" class=\"cb-cardio\" aria-label=\"Open Dance, Dance, and Obey\"><span class=\"cb-cardio-led\" aria-hidden=\"true\"></span><strong>DANCE, DANCE, AND OBEY</strong><small aria-hidden=\"true\">⚡ 🥋</small></button>\r\n   </div>\r\n   <div class=\"cb-top\"><div class=\"cb-readout\"><div class=\"cb-line\"><strong id=\"exerciseName\">Chest</strong><output id=\"difficultySetting\" for=\"difficultySlider\">LV 1/8</output></div><button type=\"button\" id=\"variationName\" aria-live=\"polite\" aria-haspopup=\"dialog\" aria-controls=\"library\" title=\"See this movement\">Knee push-up</button></div><button id=\"openLibrary\" class=\"cb-key\" aria-label=\"Open movement library\">MOVE&shy;MENTS ↗</button></div>\r\n   <select id=\"movement\" aria-label=\"Movement\" hidden></select>\r\n   <div class=\"cb-deck\">\r\n    <div class=\"cb-part\"><span class=\"cb-label\" id=\"exerciseDialLabel\">FOCUS</span><div id=\"exerciseDial\" class=\"cb-rotary cb-dial\" role=\"slider\" tabindex=\"0\" aria-labelledby=\"exerciseDialLabel\" aria-valuemin=\"0\" aria-valuemax=\"9\" aria-valuenow=\"0\" aria-valuetext=\"Chest\"><span class=\"cb-ticks\" aria-hidden=\"true\"></span><i aria-hidden=\"true\"></i></div></div>\r\n    <div class=\"cb-part\"><span class=\"cb-label\" id=\"difficultyLabel\">LEVEL</span><div id=\"difficultySlider\" class=\"cb-rotary cb-knob\" role=\"slider\" tabindex=\"0\" aria-labelledby=\"difficultyLabel\" aria-valuemin=\"1\" aria-valuemax=\"8\" aria-valuenow=\"1\" aria-valuetext=\"Knee push-up, level 1 of 8\"><span class=\"cb-ticks\" aria-hidden=\"true\"></span><i aria-hidden=\"true\"></i></div><span class=\"cb-scale\" aria-hidden=\"true\"><span>1</span><span id=\"difficultyMax\">8</span></span></div>\r\n    <div class=\"cb-part cb-lever\" role=\"group\" aria-label=\"Difficulty lever\"><button type=\"button\" id=\"harder\">HARDER</button><i aria-hidden=\"true\"></i><button type=\"button\" id=\"easier\">EASIER</button></div>\r\n    <div class=\"cb-part\"><span class=\"cb-label\" aria-hidden=\"true\">SOUND</span><button id=\"toggleVoice\" class=\"cb-switch\" role=\"switch\" aria-label=\"Coach sound\" aria-checked=\"true\" data-on=\"true\">ON</button></div>\r\n   </div>\r\n   <details class=\"movement-scope\"><summary>Tracking details</summary><p id=\"variationHint\"></p><p id=\"trackingScope\"></p></details>\r\n  </div>\r\n  <p>During camera workouts, tap the counter to stop.</p>\r\n  <div class=\"set-actions\"><button id=\"start\" aria-label=\"Begin set\" class=\"primary-action\"><strong>BEGIN</strong></button><button id=\"stop\" disabled hidden>STOP</button></div>\r\n  <div id=\"bar\" class=\"set-track\" aria-hidden=\"true\"><div id=\"activity\"></div></div>\r\n  <p id=\"status\" role=\"status\" hidden></p>\r\n  <span id=\"movementName\" hidden>Squats</span><span id=\"secondary\" hidden></span><span id=\"countState\" hidden></span>\r\n </section>\r\n <!-- W2-2M #117: the pod keeps only the viewing port, the control board and BEGIN. The extras that hang off\r\n      .crew-footer (personal tracker, optional expansion, Meditation entry) ride in #podExtras, which coach-hub.mjs\r\n      moves into Records (bar); Meditation stays reachable as the #meditate route. -->\r\n <div id=\"podExtras\" class=\"pod-extras\"><footer class=\"crew-footer\"></footer></div>\r\n</main>\r\n<main id=\"restScreen\" class=\"rest-page\" hidden aria-labelledby=\"restHeading\">\r\n <div class=\"rest-heading\"><div><p class=\"eyebrow\" id=\"restEyebrow\">SET COMPLETE</p><h1 id=\"restHeading\" tabindex=\"-1\">Rest</h1></div><button id=\"leaveRest\" class=\"icon-button\" aria-label=\"Return to the pod\">↗</button></div>\r\n <div class=\"rest-receipt\"><span id=\"setReceipt\">Set saved</span><span id=\"earnedXp\">+25 XP</span></div>\r\n <section class=\"encounter\" aria-label=\"Rest encounter with your custom coach\">\r\n  <div class=\"rest-world-backdrop\" aria-hidden=\"true\"></div><div class=\"rest-world-floor\" aria-hidden=\"true\"></div>\r\n  <div class=\"encounter-orbit\" aria-hidden=\"true\"></div><div class=\"encounter-grid\" aria-hidden=\"true\"></div>\r\n  <div class=\"boss-status\"><span id=\"powerName\">SHIELD ACTIVE</span><span id=\"damageTotal\">0 DAMAGE</span><span id=\"coachHealth\">1,000,000,000 HP</span><div class=\"boss-health\" role=\"progressbar\" aria-label=\"Coach health\" aria-valuemin=\"0\" aria-valuemax=\"1000000000\" aria-valuenow=\"1000000000\"><i id=\"bossHealth\"></i></div></div>\r\n  <div class=\"coach-battle-platform\" aria-hidden=\"true\"></div><span id=\"restWeaponName\" class=\"rest-weapon-name\"></span>\r\n  <div id=\"restCoachMount\" class=\"rest-coach-mount\"><p id=\"restCoachLoading\">Loading…</p></div>\r\n  <button id=\"attackCoach\" class=\"attack-target\" aria-label=\"Attack the coach with Helping Hand\"><span>TAP TO STRIKE</span></button>\r\n  <div id=\"attackFlash\" class=\"attack-flash\" aria-hidden=\"true\"></div><div id=\"projectile\" class=\"projectile\" aria-hidden=\"true\"></div>\r\n  <div id=\"damageFloat\" class=\"damage-float\" aria-hidden=\"true\"></div>\r\n  <div class=\"player-platform\"><div class=\"platform-top\"></div><div class=\"platform-edge\"></div><div class=\"platform-thruster\"></div><canvas id=\"restAvatar\" width=\"64\" height=\"96\" role=\"img\" aria-label=\"Your Gala character on the floating platform\"></canvas></div>\r\n  <canvas id=\"restWeaponFx\" width=\"640\" height=\"360\" aria-hidden=\"true\"></canvas>\r\n  <div class=\"weapon-ability\"><button id=\"weaponSpecial\" disabled>Special · tier 4</button><progress id=\"weaponCooldown\" max=\"1\" value=\"1\" aria-label=\"Special ability cooldown\"></progress></div>\r\n  <div class=\"rest-hand\"><div id=\"restHandMount\"></div><img id=\"restHandFallback\" data-optional-src=\"/handborne/previews/family-11.png?v=5\" alt=\"Your Helping Hand\" hidden></div>\r\n  <div class=\"player-name\"><span id=\"restGuest\">Gala guest</span><span id=\"restLevel\">LV. 01</span></div>\r\n </section>\r\n <div class=\"hand-team\"><span id=\"handTeamStatus\" role=\"status\">Helping Hand ready</span><button id=\"retryHand\" hidden>Retry hand</button><progress id=\"handCharge\" value=\"0\" max=\"3\" aria-label=\"Helping Hand team strike charge\"></progress><small id=\"handChargeLabel\">Team strike in 3 taps</small></div>\r\n <p id=\"restFeedback\" class=\"rest-feedback\" role=\"status\"></p>\r\n <div class=\"rest-bottom\"><div class=\"rest-clock\"><small>RECOVERY</small><strong id=\"restTime\">1:00</strong></div><div><button id=\"nextSet\" class=\"primary-action\" disabled>Next set →</button><button id=\"moreRest\" class=\"text-button\">+30 seconds</button></div></div>\r\n <p id=\"shieldNote\" class=\"shield-note\">Sign in to power your weapon.</p>\r\n</main>\r\n<dialog id=\"settings\" class=\"pod-dialog\" aria-labelledby=\"settingsTitle\"><header><h2 id=\"settingsTitle\">Settings</h2><button id=\"closeSettings\" aria-label=\"Close pod settings\">Close</button></header>\r\n  <details class=\"settings-group\"><summary>Workout</summary><div class=\"settings-fields\"><label>Set goal<select id=\"goal\" aria-label=\"Set goal\"></select></label><label>Camera<select id=\"camera\" aria-label=\"Camera\"><option value=\"user\">Front camera</option><option value=\"environment\">Back camera</option><option value=\"manual\">Manual / timer</option></select></label><label>Coach ability<select id=\"coachPower\"><option value=\"shield\">Shield</option><option value=\"ember\">Ember</option><option value=\"arc\">Arc</option><option value=\"frost\">Frost</option></select></label></div>\r\n   <div class=\"goal-control\"><div><label for=\"goalSlider\" id=\"goalLabel\">GOAL</label><output id=\"goalSetting\" for=\"goalSlider\">3 REPS</output></div><input id=\"goalSlider\" type=\"range\" min=\"0\" max=\"3\" step=\"1\" value=\"1\" aria-label=\"Set goal\"><div class=\"slider-scale\" aria-hidden=\"true\"><span id=\"goalMin\">1</span><span id=\"goalMax\">20</span></div></div></details>\r\n <details class=\"settings-group\"><summary>Voice</summary><div class=\"voice-row\"><button id=\"testVoice\">Test voice</button><span id=\"voiceType\">Robot voice</span></div></details>\r\n <details class=\"settings-group\"><summary>Materials</summary><div class=\"settings-fields\"><button id=\"manageMaterials\" type=\"button\">Manage materials</button><p id=\"materialsStatus\" role=\"status\">Sign in to download materials you own.</p><section id=\"materialControls\" hidden aria-label=\"Downloaded materials\"><button data-material-download type=\"button\">Download material</button><button data-material-cancel type=\"button\" hidden>Cancel</button><button data-material-equip type=\"button\" disabled>Equip after restart</button><p data-material-status role=\"status\"></p><div id=\"materialRuntime\" aria-label=\"Active material preview\"></div></section></div></details>\r\n <details class=\"settings-group\"><summary>Pod</summary><div class=\"settings-actions\"><button id=\"reset\">Reset count</button><button id=\"visitRest\">Rest room</button></div></details>\r\n <details><summary>About &amp; licenses</summary><p><a href=\"/LICENSE\">Coach source license</a> · <a href=\"/source.json\" download>Source code</a></p><div id=\"packLicenses\">Optional pack licenses load when Settings opens.</div></details>\r\n <details id=\"trackingDiag\"><summary>Camera & tracking</summary><p id=\"lensInfo\">The camera opens when you begin a set.</p><button id=\"widest\" disabled>Widest view</button><p id=\"hint\"></p><p id=\"paceNote\" hidden></p><p id=\"detail\">Camera off · Tracker closed</p><p id=\"measurement\"></p><p id=\"jointReadings\"></p><pre id=\"cameraDetails\"></pre><p id=\"note\">Keep the joints requested for your selected exercise in view. Movement estimates, not technique grades.</p></details>\r\n <div id=\"roundControl\" hidden><select id=\"duration\" aria-label=\"Legacy round duration\"><option value=\"0\">Open timer</option><option value=\"30\">30 seconds</option><option value=\"60\">1 minute</option><option value=\"120\">2 minutes</option><option value=\"180\">3 minutes</option></select></div>\r\n</dialog>\r\n<dialog id=\"identity\" class=\"pod-dialog avatar-dialog\" aria-labelledby=\"identityTitle\">\r\n <header><h2 id=\"identityTitle\">Your Gala avatar</h2><button id=\"closeIdentity\">Close</button></header>\r\n <div id=\"identitySummary\">\r\n  <canvas id=\"identityAvatar\" width=\"64\" height=\"96\" role=\"img\" aria-label=\"Your saved Gala appearance\"></canvas><p id=\"identityName\">Gala guest</p>\r\n  <details class=\"gala-import\"><summary>Change your Gala look</summary><label class=\"primary-action file-label\">Import Gala look<input id=\"importGala\" type=\"file\" accept=\"application/json,.json\"></label><a href=\"https://mominc.online/gala/\" target=\"_blank\" rel=\"noopener noreferrer\">Open Join Gala ↗</a></details>\r\n  <p id=\"identityStatus\" role=\"status\">Saved on this device</p>\r\n  <section class=\"weapon-subsection\" aria-labelledby=\"weaponHeading\"><h3 id=\"weaponHeading\">Weapon</h3><label>Weapon<select id=\"weaponType\"></select></label><label>Upgrade<select id=\"weaponTier\"></select></label><progress id=\"weaponLevelBar\" class=\"level-bar\" max=\"1\" value=\"0\" aria-label=\"XP to next level\"></progress><p id=\"weaponStatus\" role=\"status\"></p><details><summary>Upgrades</summary><p>Every workout earns XP toward your level; each level unlocks the next upgrade. Rest attacks earn no XP.</p></details></section>\r\n  <section class=\"hand-subsection\" aria-labelledby=\"handTitle\"><img id=\"identityHandPreview\" data-optional-src=\"/handborne/previews/family-11.png?v=5\" alt=\"Helping Hand creature family preview\"><div><h3 id=\"handTitle\">Helping Hand</h3><button id=\"customizeHand\" data-access-route=\"/handborne/index.html\" class=\"primary-action\">Customize hand →</button></div></section>\r\n </div>\r\n <section id=\"handEditorPanel\" hidden aria-label=\"Customize your companion\"><div class=\"hand-editor-bar\"><button id=\"backToAvatar\">← Your Gala avatar</button><span>Autosaved</span></div><iframe id=\"handEditorFrame\" title=\"Helping Hand customizer with live preview\" allow=\"clipboard-write\"></iframe></section>\r\n</dialog>\r\n\r\n<dialog id=\"library\" aria-labelledby=\"libraryTitle\">\r\n  <video id=\"handVideo\" playsinline muted hidden></video>\r\n  <div class=\"glass-shell\">\r\n    <header class=\"library-header\"><div><h1 id=\"libraryTitle\">Movements</h1></div><button id=\"closeLibrary\" data-hand aria-label=\"Close library\">Close</button></header>\r\n    <label class=\"library-filter\" for=\"libraryFocus\">Focus<select id=\"libraryFocus\"></select></label>\r\n    <section id=\"movementCards\" class=\"movement-cards\" aria-label=\"Movement library\"></section>\r\n    <nav id=\"libraryPages\" class=\"library-pages\" aria-label=\"Movement pages\"><button id=\"previousPage\" data-hand>Previous</button><span id=\"pageNumber\">1 / 2</span><button id=\"nextPage\" data-hand>Next</button></nav>\r\n    <section id=\"hologramPanel\" hidden>\r\n      <div class=\"holo-heading\"><button id=\"backLibrary\" data-hand>Back</button><h2 id=\"holoName\">Squat hologram</h2></div>\r\n      <div id=\"holoStage\" role=\"img\" aria-label=\"Interactive exercise hologram. Drag to rotate.\"><p id=\"holoStatus\" role=\"status\">Loading hologram…</p></div>\r\n      <aside class=\"holo-setup\" aria-label=\"Camera setup for this exercise\">\r\n        <div class=\"holo-setup-block\"><h3>Camera</h3><p id=\"holoCameraPosition\" class=\"holo-position\"></p><p id=\"holoCameraPlacement\"></p></div>\r\n        <div class=\"holo-setup-block\"><h3>Keep visible</h3><ul id=\"holoVisibleJoints\" class=\"holo-joints\"></ul><p id=\"holoFrameNote\"></p></div>\r\n        <p class=\"holo-no-feet\">No foot or ankle tracking.</p>\r\n      </aside>\r\n      <div class=\"holo-tools\"><button id=\"holoPlay\" data-hand>Pause animation</button><button id=\"holoReset\" data-hand>Reset view</button></div>\r\n\r\n      <p id=\"introCue\" class=\"intro-cue\" hidden></p>\r\n      <button id=\"useHologram\" data-hand class=\"use-movement\">Begin</button>\r\n    </section>\r\n    <footer class=\"library-footer\">\r\n      <button id=\"startFromLibrary\" data-hand class=\"use-movement\">Start Squats</button>\r\n      <details id=\"gestureArea\"><summary>Gestures</summary><div class=\"hand-row\"><button id=\"toggleHands\" aria-pressed=\"false\">Enable exercise gestures</button><span id=\"handState\" role=\"status\">Touch controls ready</span></div>\r\n      <progress id=\"confirmProgress\" max=\"1\" value=\"0\" aria-label=\"Thumbs-up confirmation\"></progress>\r\n      <details id=\"handHelp\"><summary>Gesture guide</summary><p>Push-ups: raise one arm and point at its upper arm with your other index finger. Squats: hands on hips. Tree: hands together overhead. Warrior: arms out in a T. Horse: hands together at your chest. Boxing: two fists near your shoulders. Jogging: pump your arms alternately four times. Jumping: hands raised apart.</p><p>Hold thumbs up to confirm.</p></details></details>\r\n    </footer>\r\n  </div>\r\n  <div id=\"handCursor\" hidden aria-hidden=\"true\"></div>\r\n</dialog>\r\n<script src=\"/workout-tracks.js\"></script><script src=\"/pod/gala-weapons.js\"></script><script src=\"/pod/gala-avatar.js\"></script><script src=\"/pod/gala-performer.js\"></script><script src=\"/pod/dj-identity.js\"></script>\r\n<script type=\"module\" src=\"/app-runtime.mjs?v=rest-flow-1\"></script>\r\n<script type=\"module\" src=\"/launch-bootstrap.mjs?v=startup-recovery-1\"></script>\r\n<script type=\"module\" src=\"/shader-entry.mjs?v=rest-flow-1\"></script>\r\n</html>\r\n","post-download.mjs":"// D34 post-download package, picked in a Downloads menu. scripts/offline-assets.mjs tags every package\r\n// file with a group; the service worker owns this release's package cache and, for the groups this\r\n// device chose, says what is still missing and how big each group is (PACKAGE_PLAN). This page fetches\r\n// those whole files (Sites has no byte ranges), each checked against its build sha256, so an interrupted\r\n// download resumes file by file, across reloads. The menu is its own full-screen page, shown once after\r\n// the first open and never over the quilt portal (the portal waits, hidden, until it closes); Settings\r\n// and Install reopen it (window.myr5Packs.open). A fresh installed-app launch saves the full public\r\n// package automatically; ordinary web visits keep a deliberate Download action.\r\nimport {ROWS,TRACKS,LEVELS_PER_BOSS} from './battle-pass-rewards.mjs';\r\nimport {RELEASE} from './release-info.mjs';\r\n\r\nconst STATE='myr5-full-download',CHOSEN='myr5-download-groups',SEEN='myr5-downloads-seen',INSTALL_PENDING='myr5-full-package-install-pending',INSTALLED_SEEN='myr5-full-package-installed-app-seen';\r\nconst BUSY=['camera','model','tracking','manual'];\r\nconst mb=bytes=>bytes>0&&bytes<104858?'<0.1 MB':(bytes/1048576).toFixed(bytes<10*1048576?1:0)+' MB';\r\nconst read=(store,key)=>{try{return store.getItem(key);}catch{return null;}};\r\nconst write=(store,key,value)=>{try{value==null?store.removeItem(key):store.setItem(key,value);}catch{}};\r\nconst say=text=>Object.assign(Error(text),{shown:true});\r\n// Group ids come from scripts/offline-assets.mjs; a row shows only when this build has that group.\r\nconst GROUPS=[\r\n // W2-2O (#136): the portal experience's art, offered first (and picked) on the first open.\r\n ['starter','Starter: portal, pyramid, ship and worlds','The offline workout tracker, the quilt portal, the Food pyramid, the starter ship and its worlds, and the achievements art.'],\r\n ['grimoire-ice','Crystal grimoire portal','Crystal board art and its reflective color-matched wormhole.'],\r\n ['grimoire-grass','Grass grimoire portal','Grass and flower art with its dirt, roots and bugs wormhole.'],\r\n ['grimoire-cogs','Cogs grimoire portal','Clockwork board parts, door art and its mechanical wormhole.'],\r\n ['grimoire-jelly','Jelly grimoire portal','Jelly board art and its bumpy color-matched wormhole.'],\r\n ['grimoire-wood','Wood grimoire portal','Wood board art and its ember, bark and charcoal wormhole.'],\r\n ['grimoire-pond','Pond grimoire portal','Pond poster art and its murky water, lily and koi wormhole.'],\r\n ['grimoire-clay','Little Boyfriend clay grimoire','Sculpted clay, purple trails and the dry-clay secret.'],\r\n ['grimoire-paper','Dr. Girlfriend paper grimoire','Measured graph paper, glue and decorative scissors.'],\r\n ['grimoire-mache','Cortisol Corgi grimoire','Scary paper drawings, paint and the papier-mâché visitor.'],\r\n ['room-scoreboard','Scoreboard classroom','The 3D classroom wall and desks, with friends at their seats and a whiteboard you can enter.'],\r\n ['room-reminders','Reminders computer','The purple space computer around your reminder controls.'],\r\n ['room-cage','3D customizer cage','Your coach on a pedestal, with pet cages, a weapon wall and a mirror you tap to open each part of the customizer.'],\r\n ['coach','Your coach','The regular coach models, the customizer and exercise demos.'],\r\n ['bodies','Extra coach bodies','More body shapes for the customizer, by workout section. A body you pick also downloads by itself.'],\r\n ['hand','Helping Hand','Your hand companion and all its looks.'],\r\n ['music','Page music','Six looping songs for the pod, grimoire, achievements, customizer, food pyramid and classroom.'],\r\n ['drop-pods','Reward drop pods','The 3D drop pods that land when you open a reward pack.'],\r\n ['voices','Voices','Your coach’s spoken lines.'],\r\n ['food','Food scanner','The food scanner model and the food list.'],\r\n ['meditation','Meditation & backgrounds','Meditation, rest and board backgrounds.'],\r\n ['games','Games & War Room','Arcade games and the War Room.'],\r\n ['vault','Achievement Vault','Vault hall, secret worlds, boards, models and art.'],\r\n];\r\n// Body sections follow the board rows (#102): a section's bodies unlock when every boss in its row\r\n// is beaten. Mirrors track-placements.ts sectionComplete(), which can't be imported here (its catalog\r\n// pulls three.js into the launch bundle).\r\nconst SECTIONS=[{id:'bodies-starter',name:'Starter',row:null},...ROWS.filter(row=>row.track).map(row=>({id:'bodies-'+TRACKS[row.track].catalog,name:TRACKS[row.track].name,row}))];\r\nconst sectionLocked=(row,progress)=>!!row&&!Array.from({length:row.bosses},(_,i)=>progress[`${row.id}-${i+1}`]??0).every(levels=>levels>=LEVELS_PER_BOSS);\r\nconst row=(id,title,text,extra='')=>`<label class=\"download-row\"><input type=\"checkbox\" ${id.startsWith('*')?`data-all=\"${id.slice(1)}\"`:`data-group=\"${id}\"`}><span><strong>${title}</strong>${extra}<small>${text}</small></span><b data-size></b></label>`;\r\n\r\nexport function mountPostDownload({host}){\r\n const worker=navigator.serviceWorker;\r\n if(!worker||!globalThis.caches||!host)return null;\r\n const menu=document.createElement('dialog'),bar=document.createElement('section'),settings=document.createElement('section'),note=document.createElement('p');\r\n menu.id='downloadsMenu';menu.className='downloads-menu';menu.setAttribute('aria-labelledby','downloadsTitle');menu.setAttribute('aria-describedby','downloadsText');\r\nconst status='<p role=\"status\" data-text></p><progress max=\"1\" value=\"0\" aria-label=\"Download progress\"></progress><span data-bytes></span><button type=\"button\" data-toggle></button><button type=\"button\" data-update hidden>Update Coach to continue downloads</button>';\r\n menu.innerHTML='<div class=\"downloads-page\"><h2 id=\"downloadsTitle\">Downloads</h2><p id=\"downloadsText\">Save the complete public content library for offline use in one resumable download. Groups are selected by default; clear any you do not want on this device. You can change this any time in Settings.</p><p class=\"hint\" data-cellular hidden>You’re on mobile data. Wi‑Fi is best for big downloads.</p><p class=\"hint\" data-network hidden>You’re offline. The complete package is selected and will start when you reconnect.</p><p class=\"hint\" data-save-data hidden>Data Saver is on. The complete package is selected; tap Download selected when you want to start.</p><fieldset data-groups><legend>Complete content package</legend></fieldset><section data-packs></section></div>'\r\n  +'<footer class=\"downloads-footer\"><div class=\"downloads-status\">'+status+'</div><p data-total></p><div class=\"actions\"><button type=\"button\" class=\"main-action\" data-download>Download selected</button><button type=\"button\" data-later>Not now</button></div></footer>';\r\n bar.className='full-download-bar';bar.setAttribute('aria-label','MyR5 downloads');bar.hidden=true;\r\n settings.className='full-download-settings';settings.setAttribute('aria-labelledby','fullDownloadSettingsTitle');\r\n bar.innerHTML=status+'<button type=\"button\" data-hide aria-label=\"Hide download status\">Hide</button>';\r\n settings.innerHTML='<h3 id=\"fullDownloadSettingsTitle\">Downloads</h3><button type=\"button\" data-open>Choose downloads</button>'+status;\r\n note.className='body-download-note';note.setAttribute('role','status');note.setAttribute('popover','manual');note.hidden=true;\r\n host.append(settings);document.body.append(menu,bar,note);\r\n const list=menu.querySelector('[data-groups]'),packsHost=menu.querySelector('[data-packs]'),menuStatus=menu.querySelector('.downloads-status');\r\n // Settings reaches the same menu (the pod Settings dialog, when this page has one).\r\n const podSettings=document.getElementById('settings'),entry=document.createElement('div');\r\n if(podSettings){entry.className='settings-fields downloads-entry';entry.innerHTML='<button type=\"button\" data-open>Downloads</button>';const before=podSettings.querySelector('.settings-group,.settings-actions');before?before.before(entry):podSettings.append(entry);}\r\n // Account-bound, signed optional packets live beside (not inside) the anonymous package groups.\r\n // Do not even add their controls to the anonymous page: mount them only after the same-origin\r\n // account bridge publishes a stable owner, and remove them again as soon as that owner clears.\r\n let sectionControls=null,sectionEpoch=0,mountedOwner=null;\r\n const sectionOwner=value=>typeof value?.user?.id==='string'?value.user.id:null;\r\n const clearSections=()=>{++sectionEpoch;sectionControls?.dispose();sectionControls=null;mountedOwner=null;};\r\n const mountSections=async account=>{\r\n  const owner=sectionOwner(account),run=++sectionEpoch;if(!owner)return;mountedOwner=owner;\r\n  try{\r\n   const {mountPostDownloadSections}=await import('./modules/materials/post-download-ui.mjs');\r\n   if(run!==sectionEpoch||sectionOwner(window.myr5AuthenticatedAccount)!==owner)return;\r\n   sectionControls=mountPostDownloadSections({host:packsHost,account:window.myr5AuthenticatedAccount});\r\n   if(!sectionControls)mountedOwner=null;\r\n  }catch(error){if(run===sectionEpoch)mountedOwner=null;console.warn('Extra offline packs unavailable',error);}\r\n };\r\n // The section UI receives account updates itself; polling the same owner must not dispose it.\r\n const sectionsReady=event=>{if(mountedOwner&&sectionOwner(event.detail)===mountedOwner)return;clearSections();void mountSections(event.detail);};\r\n const sectionsCleared=()=>clearSections();\r\n window.addEventListener('myr5:account-ready',sectionsReady);window.addEventListener('myr5:account-cleared',sectionsCleared);\r\n void mountSections(window.myr5AuthenticatedAccount);\r\n // Any part of the app (e.g. the ship scene, when its pack isn't installed yet) can open the menu,\r\n // optionally pointing at one group (a scene without its Starter art: 'starter', picked for them) or one\r\n // signed section's button.\r\n // due(): the first-open Downloads menu is open or about to open, so other first-screen popups wait their turn.\r\n window.myr5Packs=Object.freeze({due:()=>wantMenu||menu.open,open(sectionId){\r\n  openMenu(false);\r\n  const id=typeof sectionId==='string'&&/^[a-z0-9-]+$/.test(sectionId)?sectionId:null,group=id&&list.querySelector(`[data-group=\"${id}\"]`);\r\n  if(group&&!group.disabled){group.checked=true;sync();}\r\n  const target=group||(id&&sectionControls?.panel?.querySelector(`[data-actions] [data-sections~=\"${id}\"]`));\r\n  if(target){target.scrollIntoView({block:'center',behavior:'smooth'});target.focus({preventScroll:true});}\r\n  return !!target;\r\n }});\r\n\r\n let plan=null,phase='checking',message='',got=0,controller=null,wantMenu=false,autoMenu=false,firstPick=false,restorePortal=false,doneTimer=0,progress=null;\r\n const busy=()=>document.body.dataset.cameraWorkout==='true'||document.body.dataset.tracking==='true'||document.body.dataset.screen==='rest'||BUSY.includes(window.myr5TestState?.phase);\r\n const installedApp=()=>navigator.standalone===true||globalThis.matchMedia?.('(display-mode: standalone)').matches===true;\r\n const saveDataRequested=()=>navigator.connection?.saveData===true;\r\n if(installedApp()&&!read(localStorage,INSTALLED_SEEN)){write(localStorage,INSTALL_PENDING,'1');write(localStorage,INSTALLED_SEEN,'1');}\r\n // A ?panel= or #route deep link came for one thing; nothing auto-opens over it (the quilt portal waits the same way).\r\n const deepLinked=()=>new URLSearchParams(location.search).has('panel')||!!window.myr5Routes?.hashRoute?.();\r\n const idle=()=>!busy()&&!document.hidden&&!document.querySelector('dialog[open]');\r\n const cellular=()=>navigator.connection?.type==='cellular'||navigator.connection?.saveData===true;\r\n // null: the whole package (a download chosen before the menu existed, or Everything).\r\n function chosen(){if(read(localStorage,INSTALL_PENDING)==='1')return null;try{const list=JSON.parse(read(localStorage,CHOSEN));if(Array.isArray(list))return list;}catch{}return read(localStorage,STATE)?null:[];}\r\n const boxes=()=>[...list.querySelectorAll('[data-group]')];\r\n function paint(){\r\n  const total=plan?.total||0,left=Math.max(0,(plan?.remaining||0)-got),done=total-left;\r\n  const text=phase==='done'?(total?'Ready offline. Your downloads work without a connection.':'MyR5 works with what’s on this phone. Choose downloads for more offline.'):phase==='downloading'?'Downloading your picks…':phase==='paused'?'Download paused.':phase==='error'||phase==='stale'?message:phase==='checking'?'Checking this device…':`${mb(left)} of your picks still to download.`;\r\n  const toggle=phase==='downloading'?'Pause':phase==='paused'||phase==='error'?'Resume':phase==='ready'?`Download ${mb(left)}`:'';\r\n  for(const box of [bar,settings,menuStatus]){\r\n   const active=['downloading','paused','error','stale'].includes(phase)&&total>0;\r\n   box.querySelector('[data-text]').textContent=text;box.querySelector('progress').value=total?done/total:0;box.querySelector('progress').hidden=!active;\r\n   box.querySelector('[data-bytes]').textContent=active?`${mb(done)} of ${mb(total)}`:'';\r\n   const button=box.querySelector('[data-toggle]');button.hidden=!toggle;button.textContent=toggle;box.querySelector('[data-update]').hidden=phase!=='stale';\r\n  }\r\n  menuStatus.hidden=!['checking','downloading','paused','error','stale'].includes(phase);\r\n  if(menu.open)sync();\r\n }\r\n // The menu: one toggle and size per group; Extra coach bodies splits by workout section.\r\n function renderMenu(){\r\n  const groups=plan?.groups||{},sizes=id=>groups[id];\r\n  const bodies=SECTIONS.filter(section=>sizes(section.id));\r\n  const current=new Map(boxes().map(box=>[box.dataset.group,box.checked])),expanded=menu.open&&!!list.querySelector('details')?.open;\r\n  const rendered=new Set();\r\n  // A re-render keeps keyboard focus on the same row (e.g. the Starter row a scene asked for).\r\n  const focused=list.contains(document.activeElement)&&document.activeElement.dataset,refocus=focused&&(focused.group?`[data-group=\"${focused.group}\"]`:focused.all?`[data-all=\"${focused.all}\"]`:null);\r\n  list.querySelectorAll('.download-row,.download-bodies').forEach(node=>node.remove());\r\n  for(const [id,title,text] of GROUPS){\r\n   if(id==='bodies'){\r\n    if(!bodies.length)continue;\r\n    const wrap=document.createElement('div');wrap.className='download-bodies';\r\n    wrap.innerHTML=row('*bodies',title,text)+'<details><summary>By workout section</summary>'+bodies.map(section=>row(section.id,section.name,section.row?`${sizes(section.id).files} bodies`:`${sizes(section.id).files} bodies, open to everyone`,'<em data-lock hidden>Locked · preview</em>')).join('')+'</details>';\r\n    wrap.querySelector('details').open=expanded;list.append(wrap);for(const section of bodies)rendered.add(section.id);continue;\r\n   }\r\n   if(sizes(id)){list.insertAdjacentHTML('beforeend',row(id,title,text,id==='coach'||id==='starter'?'<em>Recommended</em>':''));rendered.add(id);}\r\n  }\r\n  // New release groups still appear under the full-package toggle even before this copy gains curated labels.\r\n  for(const id of Object.keys(groups))if(!rendered.has(id)){\r\n   const title=id.split('-').map(part=>part.charAt(0).toUpperCase()+part.slice(1)).join(' ');\r\n   list.insertAdjacentHTML('beforeend',row(id,title,'Additional public content included in the complete offline package.'));\r\n   rendered.add(id);\r\n  }\r\n  if(Object.keys(groups).length>1)list.insertAdjacentHTML('afterbegin',row('*everything','Complete public content package','Every public group, including Vault worlds and their assets.','<em>Recommended</em>'));\r\n  // A re-render while the menu is up (a fresher plan) keeps what the user has toggled so far.\r\n  const picked=chosen(),kept=menu.open?current:null;\r\n  for(const box of boxes()){\r\n   const info=sizes(box.dataset.group),label=box.closest('label');label.dataset.saved=String(!info.remaining);\r\n   box.checked=!info.remaining||(kept?.has(box.dataset.group)?kept.get(box.dataset.group):firstPick?true:!picked||picked.includes(box.dataset.group));\r\n   label.querySelector('[data-size]').textContent=!info.remaining?'Saved':info.remaining<info.total?`${mb(info.remaining)} left`:mb(info.total);\r\n  }\r\n  paintLocks();sync();\r\n  if(refocus)list.querySelector(refocus)?.focus({preventScroll:true});\r\n }\r\n function paintLocks(){\r\n  if(progress)for(const section of SECTIONS){const badge=list.querySelector(`[data-group=\"${section.id}\"]`)?.closest('label').querySelector('[data-lock]');if(badge)badge.hidden=!sectionLocked(section.row,progress);}\r\n }\r\n const saved=box=>box.closest('label').dataset.saved==='true',left=box=>plan?.groups?.[box.dataset.group]?.remaining||0;\r\n function sync(){\r\n  const all=boxes(),busyNow=!!controller;\r\n  for(const box of all)box.disabled=busyNow||saved(box);\r\n  for(const [name,members] of [['bodies',all.filter(box=>box.dataset.group.startsWith('bodies-'))],['everything',all]]){\r\n   const parent=list.querySelector(`[data-all=\"${name}\"]`);if(!parent)continue;\r\n   const on=members.filter(box=>box.checked).length,size=members.reduce((sum,box)=>sum+left(box),0);\r\n   parent.checked=on===members.length;parent.indeterminate=on>0&&on<members.length;parent.disabled=busyNow||!size;\r\n   parent.closest('label').querySelector('[data-size]').textContent=size?mb(size):'Saved';\r\n  }\r\n  const selected=all.filter(box=>box.checked&&!saved(box)).reduce((sum,box)=>sum+left(box),0);\r\n  menu.querySelector('[data-total]').textContent=busyNow?'':selected?`Selected: ${mb(selected)}`:'Nothing new selected.';\r\n  const download=menu.querySelector('[data-download]');download.disabled=busyNow||phase==='stale'||!selected||!navigator.onLine;download.hidden=busyNow||phase==='stale';\r\n }\r\n list.addEventListener('change',event=>{\r\n  const all=event.target.dataset.all;\r\n  if(all)for(const box of boxes())if(!box.disabled&&(all==='everything'||box.dataset.group.startsWith('bodies-')))box.checked=event.target.checked;\r\n  sync();\r\n });\r\n const portalUp=()=>!!window.myr5Portal&&!window.myr5Portal.disposed&&document.getElementById('portalHome')?.hidden===false;\r\n function openMenu(auto){\r\n  if(menu.open)return;\r\n  // Shown once: the first open marks it seen (and picks the recommended group); Settings reopens it.\r\n  const installPending=read(localStorage,INSTALL_PENDING)==='1';\r\n  autoMenu=auto;firstPick=auto&&(firstPick||installPending||!read(localStorage,SEEN)&&!chosen()?.length);\r\n  if(installPending)write(localStorage,CHOSEN,null);\r\n  const autoPackage=auto&&firstPick&&(installedApp()||installPending)&&navigator.onLine&&!saveDataRequested();\r\n  menu.returnValue='';if(auto)write(localStorage,SEEN,'1');\r\n  // Its own screen, never a modal over the quilt: the portal steps aside and comes back after.\r\n  restorePortal=portalUp();if(restorePortal)window.myr5Portal.hide();\r\n  menu.querySelector('[data-cellular]').hidden=!cellular();menu.querySelector('[data-network]').hidden=navigator.onLine;menu.querySelector('[data-save-data]').hidden=!saveDataRequested();menu.querySelector('[data-later]').textContent=auto?'Not now':'Close';\r\n  renderMenu();paint();menu.showModal();menu.querySelector('.downloads-page').scrollTop=0;\r\n  menu.querySelector(auto&&!menu.querySelector('[data-download]').disabled?'[data-download]':'[data-later]').focus();\r\n  // A fresh installed-app launch or appinstalled event starts the complete public package online.\r\n  // Browser visitors and Data Saver users keep the selected package but wait for the user's action;\r\n  // signed owner-scoped packs are mounted separately below.\r\n   if(autoPackage)queueMicrotask(()=>{if(menu.open&&!controller)void start();});\r\n  import('./battle-pass.mjs').then(({loadProgress})=>{progress=loadProgress();paintLocks();},()=>{});\r\n  // Sizes change as bodies download on demand; show the worker's current count.\r\n  if(plan&&!controller)ask().then(fresh=>{if(controller)return;plan=fresh;got=0;paint();if(menu.open)renderMenu();},()=>{});\r\n }\r\n menu.querySelector('[data-download]').onclick=()=>{\r\n  const all=boxes(),picked=all.filter(box=>box.checked).map(box=>box.dataset.group);\r\n  write(localStorage,INSTALL_PENDING,null);\r\n  write(localStorage,CHOSEN,picked.length===all.length?null:JSON.stringify(picked));\r\n  menu.close('download');void start();\r\n };\r\n // \"Not now\" (the first-open label) really pauses the auto-started package: the bar keeps Resume. Plain \"Close\" (menu opened from Settings) leaves a running download alone.\r\n menu.querySelector('[data-later]').onclick=()=>{if(autoMenu&&controller)controller.abort();menu.close('later');};\r\n menu.addEventListener('close',()=>{\r\n  if(menu.returnValue==='busy'){wantMenu=autoMenu;restorePortal=false;return;}\r\n  // First open: the quilt waited for this menu (app.mjs skips it while a dialog is open).\r\n  if(restorePortal)window.myr5Portal?.show?.();\r\n  // Not just \"not mounted yet\": the mount can finish (window.myr5Portal set, still hidden) while this\r\n  // menu was open, since shouldShow() only gets one check, at mount time, and this menu wasn't open\r\n  // yet then. Always route through the shared opener; it no-ops fast when already mounted (#risk 1).\r\n  else if(autoMenu&&window.coachPlan&&!deepLinked())void window.myr5Menus?.portal?.({shouldShow:idle});\r\n  restorePortal=false;autoMenu=false;\r\n });\r\n for(const button of [settings,entry].map(node=>node.querySelector('[data-open]')).filter(Boolean))button.onclick=()=>openMenu(false);\r\n\r\n function ask(adopt=false){\r\n  return new Promise((resolve,reject)=>{\r\n   const channel=new MessageChannel(),timer=setTimeout(()=>reject(say('Coach is busy. Try again.')),60000);\r\n   channel.port1.onmessage=({data})=>{clearTimeout(timer);data?.error?reject(say('Could not check the download. Try again.')):resolve(data);};\r\n   if(!worker.controller){clearTimeout(timer);reject(say('Reload MyR5 to continue the download.'));return;}\r\n   worker.controller.postMessage({type:'PACKAGE_PLAN',adopt,groups:chosen()??undefined},[channel.port2]);\r\n  });\r\n }\r\n async function save(asset,signal){\r\n  const cache=await caches.open(asset.cache||plan.cache);\r\n  for(let attempt=1;;attempt++){\r\n   let responseAccepted=false;\r\n   try{\r\n    // HTML may be rewritten in transit by edge security products; the worker allows that too.\r\n    const response=await fetch(asset.url,{cache:'no-cache',headers:{'x-myr5-package':'1'},signal,...(asset.url.endsWith('.html')?{}:{integrity:asset.integrity})});\r\n    if(!response.ok)throw Error('HTTP '+response.status);\r\n    responseAccepted=true;\r\n    await cache.put(asset.key||asset.url,response);return;\r\n   }catch(error){\r\n    if(signal.aborted||error.name==='QuotaExceededError')throw error;\r\n    if(attempt===3){\r\n     if(!responseAccepted&&await newerReleaseAvailable(signal)&&!signal.aborted)throw Object.assign(Error('A newer Coach release is live. Update Coach to continue downloads; saved files and choices are kept.'),{code:'stale_release'});\r\n     throw error;\r\n    }\r\n    await new Promise(resolve=>setTimeout(resolve,1500*attempt));\r\n   }\r\n  }\r\n }\r\n async function newerReleaseAvailable(signal){\r\n  if(signal.aborted)return false;\r\n  const probe=new AbortController(),abort=()=>probe.abort();\r\n  const timer=setTimeout(abort,5000);\r\n  if(signal.aborted)abort();else signal.addEventListener('abort',abort,{once:true});\r\n  try{\r\n   const response=await fetch('/api/releases/current',{cache:'no-store',credentials:'omit',signal:probe.signal});\r\n   if(signal.aborted||probe.signal.aborted)return false;\r\n   if(!response.ok)return false;\r\n   const current=await response.json();\r\n   if(signal.aborted||probe.signal.aborted)return false;\r\n   return typeof current?.id==='string'&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(current.id)&&current.id!==RELEASE.id;\r\n  }catch{return false;}finally{clearTimeout(timer);signal.removeEventListener('abort',abort);}\r\n }\r\n async function start(){\r\n  if(controller)return;\r\n  controller=new AbortController();const {signal}=controller;\r\n  write(localStorage,STATE,'on');phase='downloading';message='';bar.hidden=false;paint();\r\n  navigator.storage?.persist?.().catch(()=>{});\r\n  try{\r\n   // Pass 1 saves the picked groups and the voice manifest; pass 2 the voice clips that manifest names.\r\n   for(let pass=0;;pass++){\r\n    plan=await ask(true);got=0;paint();\r\n    if(!plan.missing.length)break;\r\n    if(pass===3)throw say('Download stopped. It continues where it left off.');\r\n    const {quota=0,usage=0}=await navigator.storage?.estimate?.().catch(()=>({}))||{};\r\n    if(quota&&quota-usage<plan.remaining)throw say(`Not enough free space. MyR5 needs ${mb(plan.remaining)}.`);\r\n    for(const asset of plan.missing){await save(asset,signal);got+=asset.bytes;paint();}\r\n   }\r\n   phase='done';write(localStorage,INSTALL_PENDING,null);bar.hidden=false;clearTimeout(doneTimer);doneTimer=0;\r\n  }catch(error){\r\n   if(signal.aborted){phase='paused';write(localStorage,STATE,'paused');}\r\n   else if(error.code==='stale_release'){phase='stale';message=error.message;}\r\n   else{phase='error';message=error.shown?error.message:error.name==='QuotaExceededError'?'Not enough free space. Free some up, then resume.':'Download stopped. Check your connection; it continues where it left off.';}\r\n  // Keep saved bytes against this plan until the next worker plan replaces both plan and got.\r\n  }finally{controller=null;paint();if(menu.open)renderMenu();else if(phase==='error'&&idle())openMenu(false);}\r\n }\r\n for(const box of [bar,settings,menuStatus])box.querySelector('[data-toggle]').onclick=()=>controller?controller.abort():void start();\r\n for(const box of [bar,settings,menuStatus])box.querySelector('[data-update]').onclick=()=>{\r\n  // Recovery is a user action: stop the old release's request, leave its verified cache alone,\r\n  // and let the normal recovery page coordinate activation with any active workout clients.\r\n  controller?.abort();\r\n  if(menu.open)menu.close('stale-update');\r\n  window.open('/repair-coach','_blank','noopener,noreferrer');\r\n };\r\n bar.querySelector('[data-hide]').onclick=()=>{bar.hidden=true;};\r\n // A roster body, or a portal scene's Starter art, that isn't on this phone downloads by itself when it's\r\n // needed (sw.js). The note is a popover so it also shows over a scene's dialog; never over a workout.\r\n // Offline, a scene without its Starter art shows its own placeholder, so only a body says it's missing.\r\n const pending=new Map();let noteTimer=0;\r\n const showNote=text=>{note.textContent=text;note.hidden=!text||busy();try{note.hidden?note.hidePopover():note.showPopover();}catch{}};\r\n worker.addEventListener?.('message',({data})=>{\r\n  const starter=data?.type==='STARTER_DOWNLOAD';\r\n  if(data?.type!=='BODY_DOWNLOAD'&&!starter)return;\r\n  clearTimeout(noteTimer);\r\n  if(data.state==='start')pending.set(data.url,starter);else pending.delete(data.url);\r\n  const missing=data.state==='unavailable'&&!starter;\r\n  showNote(missing?'This body isn’t on this phone yet. Connect to the internet to download it.':!pending.size?'':[...pending.values()].every(Boolean)?'Downloading…':'Downloading this body…');\r\n  if(missing)noteTimer=setTimeout(()=>showNote(''),6000);\r\n });\r\n // Nothing appears over a workout or camera-only mode; the menu waits until the app is idle.\r\n const ticker=setInterval(()=>{\r\n  if(menu.open&&busy())menu.close('busy');\r\n   if(note.textContent)showNote(note.textContent);\r\n  if(wantMenu&&!deepLinked()&&idle()){wantMenu=false;openMenu(true);}\r\n  if(phase==='done'&&!bar.hidden&&!doneTimer&&idle())doneTimer=setTimeout(()=>{bar.hidden=true;},6000);\r\n },1000);\r\n window.addEventListener('online',()=>{if(read(localStorage,STATE)==='on'&&['ready','error'].includes(phase)&&!saveDataRequested())void start();});\r\n window.addEventListener('online',()=>{\r\n  if(menu.open){menu.querySelector('[data-network]').hidden=true;sync();}\r\n  if(read(localStorage,INSTALL_PENDING)==='1'){\r\n   write(localStorage,CHOSEN,null);\r\n   if(menu.open&&!saveDataRequested()){firstPick=true;renderMenu();void start();}\r\n   else if(!menu.open)wantMenu=true;\r\n  }\r\n });\r\n window.addEventListener('offline',()=>{if(menu.open){menu.querySelector('[data-network]').hidden=false;sync();}});\r\n const installedEvent=()=>{write(localStorage,INSTALL_PENDING,'1');write(localStorage,SEEN,null);wantMenu=true;if(menu.open){firstPick=true;write(localStorage,CHOSEN,null);renderMenu();if(navigator.onLine&&!saveDataRequested())void start();}};\r\n window.addEventListener('appinstalled',installedEvent);\r\n window.addEventListener('pagehide',event=>{if(!event.persisted){clearInterval(ticker);window.removeEventListener('myr5:account-ready',sectionsReady);window.removeEventListener('myr5:account-cleared',sectionsCleared);window.removeEventListener('appinstalled',installedEvent);clearSections();}});\r\n (async()=>{\r\n  // A first visit is controlled once the core install activates; its menu can open then.\r\n  if(!worker.controller)await new Promise(resolve=>worker.addEventListener?.('controllerchange',resolve,{once:true}));\r\n  if(read(localStorage,INSTALL_PENDING)==='1')write(localStorage,CHOSEN,null);\r\n  try{plan=await ask();}catch(error){phase='error';message=error.message;paint();return;}\r\n  phase=plan.remaining?'ready':'done';paint();if(menu.open)renderMenu();\r\n  const state=read(localStorage,STATE);\r\n  if(state)write(localStorage,SEEN,'1');\r\n  // Resume an already-started package after a reload, or fetch an update's changed files.\r\n  if(plan.remaining&&state==='on'&&navigator.onLine&&!saveDataRequested())return void start();\r\n  if(plan.remaining&&state==='paused'){phase='paused';bar.hidden=false;paint();return;}\r\n  // A deep link (?panel=) came for one thing; like the quilt, the menu waits for a plain open.\r\n  if(!plan.remaining)write(localStorage,INSTALL_PENDING,null);\r\n  if((!read(localStorage,SEEN)||read(localStorage,INSTALL_PENDING)==='1')&&!deepLinked()&&Object.values(plan.groups||{}).some(group=>group.remaining))wantMenu=true;\r\n })();\r\n}\r\n","privacy.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>MYR5 Coach privacy</title><link rel=\"icon\" href=\"/icons/myr5-alien-192.png\"><link rel=\"stylesheet\" href=\"/signin.css\"><style>main{width:min(100% - 40px,740px)}h2{margin-top:32px;font-size:22px}a{color:#dfb7ff}li{margin:8px 0;line-height:1.5}</style><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main><a class=\"brand\" href=\"/install\"><img src=\"/icons/myr5-alien-192.png\" alt=\"MYR5\"><span>MYR5</span></a><h1>MYR5 Coach privacy</h1><p>Updated October 6, 2026. This page describes the MYR5 Coach web app at myr5.mominc.online, operated by MYR5 (independent app). MOM Inc. is a fictional character from <a href=\"https://mominc.online\">mominc.online</a>, not the operator.</p><h2>Age</h2>\r\n<p>MYR5 Coach is intended for people aged 13 and over. It is not directed to children under 13, and we do not knowingly collect personal information from them. If you believe a child under 13 has an account, contact us below and we will delete it.</p>\r\n<h2>Signing in</h2><p>Clerk handles sign-in, email verification codes and login sessions. If you choose Google, the requested information is your basic profile, email address and account identity. This Coach login does not request access to your Gmail messages, Drive files or YouTube account. Coach uses a verified account identifier to keep your saved records separate from other users.</p><p>If you choose to connect an existing Coach to a new login, the app records the connection after you sign in to both accounts.</p><h2>What Coach saves</h2><p>Information you submit may include your goal, session length, movement limits, optional setup answers, character appearance, workout records, meals and nutrition entries, and reminder preferences. These records are stored online so they can be restored on another device. If you enable notifications, the app also stores the device's push subscription and your schedule and time zone.</p><p>Local coaching and its workout history can stay on this device. Signing in does not automatically upload guest workout history. When account import is enabled, importing that history requires an explicit choice; the original device history remains local. Imported history does not earn competitive ranking or workout rewards.</p><h2>Gala run records</h2><p>A ranked run saves its start time, completed game milestones, chosen DJ name and three-character moniker, and app-receipt status. A temporary installation cookie carries your private run invitation for up to seven days. Your DJ name and completion time appear on the shared Gala board only after you choose Join the rankings. Run records are separate from your Coach account; contact MYR5 below to remove a published run. A hashed network identifier limits repeated run creation.</p><h2>Your training crew</h2><p>Connecting with an invite code shares your name, saved Gala character appearance and best completed set for each exercise with that friend. Each account can connect two friends. This does not share email addresses, meals, goals, setup answers or camera images. Invite codes work once and expire after seven days; you can cancel an unused invite. Either person can remove a connection to stop future access. Deleting Coach data removes your crew connections and invites.</p><h2>Your camera and photos</h2><p>All camera and pose processing stays on your phone; nothing from the camera is uploaded. Workout camera analysis and food-photo recognition run in your browser. The Coach app does not upload the camera video or selected food photo to its account database. Workout results and the meal details you confirm are saved when you submit them. Recognition software and models are downloaded from external hosting services, which receive ordinary network request information.</p><h2>Storage and service providers</h2><p>Coach uses browser storage and cookies for sign-in, settings, offline files and setup transfers. Its online app and data storage use OpenAI Sites and Cloudflare. Clerk and its email providers deliver authentication; Google handles Google sign-in. Device push services deliver notifications when enabled. These services receive information needed to provide their functions and may process technical and security logs.</p><p>A temporary installation transfer expires after one hour. Your saved Coach account, appearance and workout history are separate from that temporary transfer and remain available until you clear the saved data.</p><h2>Your choices</h2><p>In Coach's account settings, use <strong>Download my data</strong> to export your saved records or <strong>Delete Coach data</strong> to remove the app's saved Coach records and reminders. Clearing Coach data does not delete your Google, ChatGPT or Clerk login account; the login connection can remain. You can turn off notifications and camera access in your device or browser settings. Clearing browser storage removes local copies.</p><p>Account deletion and device-local deletion are separate. Deleting online Coach data leaves your local guest workout history on this device. Coach retains a minimal account identifier, data-generation number and deletion receipts to prevent delayed requests from restoring deleted data. Reminder-service deletion can remain pending while that service is unavailable; already-dispatched notifications or email cannot be recalled. After deletion is confirmed, you can make a new explicit import choice when importing is enabled. Retrying an older deletion does not erase newer account data.</p><p>For privacy questions or help removing a login account and associated connection records, contact <a href=\"mailto:ianmyersrocks97@gmail.com\">ianmyersrocks97@gmail.com</a>.</p><h2>Provider information</h2><p><a href=\"https://clerk.com/legal/privacy\">Clerk privacy policy</a> · <a href=\"https://policies.google.com/privacy\">Google privacy policy</a> · <a href=\"https://openai.com/policies/privacy-policy/\">OpenAI privacy policy</a> · <a href=\"https://www.cloudflare.com/privacypolicy/\">Cloudflare privacy policy</a></p><a class=\"back\" href=\"/install\">Back to Coach</a></main></body></html>\r\n","progression-guide.mjs":"﻿// User-facing rules come from the same constants as workout progression.\r\nimport {HOLD_RECOVERY_SECONDS,WORKOUT_REST_SECONDS,PREPARATION_REPS,SECOND_PREPARATION_TEMPO_SECONDS,SPRINT_ROUNDS,SPRINT_SECONDS,SPRINT_REST_SECONDS,COSMETIC_LEVEL_COUNT,DAILY_TRIO_BONUS} from './progression-rules.mjs';\r\nexport const GUIDE=[\r\n ['train','Train','BUILD YOUR PERFORMANCE. EARN YOUR COACH.',[\r\n  ['Holds',`Active holding earns XP in completed 15-second blocks. Pause or break the pose to recover for ${HOLD_RECOVERY_SECONDS} seconds. Rest earns no exercise XP. A break resets the uninterrupted attempt; earned XP stays. Hold 5 uninterrupted minutes to unlock the coach at that difficulty, and 10 uninterrupted minutes for gold. Change difficulty to start a new uninterrupted attempt.`],\r\n  ['Working sets',`Follow the easy ${PREPARATION_REPS}-rep preparation set, rest ${WORKOUT_REST_SECONDS} seconds, then do ${PREPARATION_REPS} harder preparation reps at ${SECOND_PREPARATION_TEMPO_SECONDS} seconds per full rep. Rest ${WORKOUT_REST_SECONDS} seconds before the working set. Only the working set earns XP and appears in history. Aim for a controlled challenging set; finish when you need to stop. 8 and 12 reps unlock weapon milestones; 15 reps unlock the coach. XP stops after 30 reps.`],\r\n  ['Recovery','One rep working set per muscle group per day. Holds and reps may train the same group on the same day. After two consecutive days training a muscle group, take a day off that group while its boss recovers.'],\r\n  ['Cardio',`Warm up before sprinting. Complete ${SPRINT_ROUNDS} rounds of ${SPRINT_SECONDS}-second sprints and ${SPRINT_REST_SECONDS}-second rests. Gentle cardio earns slower XP. Five sprint rounds or five active minutes of gentle cardio unlock the matching difficulty’s cardio coach.`],\r\n  ['Choose your level','Exercises show Easy, Medium, Hard or Expert. You can select any level; rewards reflect what you perform. Inspect Achievements for each real coach, weapon family and ship requirement.']]],\r\n ['weapons','Weapons','TEN GROUPS. TWENTY WEAPONS.',[\r\n  ['Difficulty blocks','Every weapon has 20 earned tiers: Easy 1–5, Medium 6–10, Hard 11–15 and Expert 16–20. Higher difficulty performance can jump directly into its block.'],\r\n  ['Holds','Hold 1 uninterrupted minute for the first tier in the block, 3 minutes for the second and 5 minutes for a coach clear. Repeat a 5-minute clear to add another tier. A 10-minute uninterrupted hold completes that block and earns the golden coach.'],\r\n  ['Reps','Complete 8 reps for the first tier in the difficulty block, 12 for the second and 15 for a coach clear. Repeat the working-set clear on another eligible day to add a tier.'],\r\n  ['Ships','Complete Expert performance in the ship’s specified group and exercise type: 5 uninterrupted hold minutes or 15 working reps. Exact requirements appear under Achievements → Ships.']]],\r\n ['rewards','XP & packs','250 COSMETIC LEVELS.',[\r\n  ['Cosmetic progression',`Workout XP advances ${COSMETIC_LEVEL_COUNT} levels of packs, colors, palettes, textures and finishes. Exercise performance unlocks coaches, weapons and ships. Each earned coach adds 25% to workout XP; those bonuses add together.`],\r\n  ['Meditation',`Complete meditation to double all workout XP earned that local day, including XP earned before the meditation session. Workout + meditation + food grants a separate ${DAILY_TRIO_BONUS} XP once that day. The ${DAILY_TRIO_BONUS} XP bonus is not doubled.`],\r\n  ['Rest victories','Defeat the first boss during rest timers each local day to earn one uncommon pack. Defeat five that day to earn one legendary pack as well. Each reward is granted once per day. Rest defeats do not unlock coaches.'],\r\n  ['Coach characters','Every coach unlocks its matching 64-bit character at the same time. Customize its colors in the War Room mirror. Coach characters do not wear clothing.']]],\r\n ['combat','Damage','YOUR STREAK IS YOUR POWER.',[\r\n  ['Every hit','10 × consecutive login days × weapon damage level. Starter damage level is 1; earned weapon tiers reach 20, giving damage level 21.'],\r\n  ['Daily damage boost','Finish the full breathing session for ×100 damage that combat day. Five login days with damage level 3 gives 150 damage, or 15,000 with breathing. Combat streaks and this damage boost use UTC days. Cosmetic daily XP uses local days.'],\r\n  ['Specials','Earn specials at tiers 4, 8, 12, 16 and 20. One shared cooldown prevents switching weapons to skip the wait. Team strikes change the animation; the damage formula stays the same.']]],\r\n ['breathing','Meditation','THREE MINUTES. DAILY XP DOUBLED.',[\r\n  ['Start in the still room','Open Meditation and start the three-minute session. Follow a comfortable breathing pace.'],\r\n  ['Finish the session','Paused time, closed rooms and hidden tabs do not count. Completion doubles today’s workout XP once; repeating meditation does not stack the multiplier. Meditation coaches unlock after 3, 6, 9 and 12 separate completed days.']]],\r\n ['rooms','Side rooms','OFF DUTY. STILL IN THE POD.',[\r\n  ['Rest arena','After the timer reaches zero, three seconds without a tap ends rest. Keep tapping to stay. Rest attacks earn no workout XP, but the first and fifth boss defeats of the day earn cosmetic packs.'],\r\n  ['Helping Hand','Customize your hand from your avatar. Every third hand-assisted hit triggers a team strike.'],\r\n  ['Meditation arcade','Keep tapping the meditating character to discover Tub Flight. Its minigame rewards do not replace a meditation session.'],\r\n  ['Gala & War Room','Your verified account keeps performance ownership in sync. War Room access still requires the completed Gala run and app installation.']]],\r\n ['reminders','Reminders','MOM WILL FOLLOW UP.',[\r\n  ['Choose the pressure','Set one, two or three messages per day. Missed scheduled training days make the next message firmer. Gentle tone, pause, frequency and quiet hours remain under your control.'],\r\n  ['Connect this device','Allow notifications on each device. On iPhone, add Coach to your home screen first. The live sender works while Coach is closed; local previews do not send notifications.'],\r\n  ['App updates','Updates download while connected and install when the app is idle. Older installs may need Update now once. A What’s new notice appears after releases.']]]\r\n];\r\n","progression-rules.mjs":"// Confirmed workout philosophy. Shared pure rules for client, local history and server.\r\n// Counts are active exercise time/repetitions, never rest or camera wall-clock time.\r\nexport const PROGRESSION_VERSION=2;\r\n// Every XP number below is the original (x1) design value times XP_SCALE. Level NUMBERS are unchanged:\r\n// unlocks key on level, seconds, reps and rounds, never on absolute XP.\r\nexport const XP_SCALE=10;\r\n// Arcade bonus XP may add at most this share of a session's exercise base XP (before multipliers).\r\nexport const BONUS_XP_SHARE=0.1;\r\n// The one bonus clamp, shared by the client (finish) and the server (importedPerformanceXp).\r\n// Absent, garbage or negative -> 0; anything else is floored and capped at the share of the base.\r\nexport function clampBonusXp(raw,base){const n=typeof raw==='number'&&Number.isFinite(raw)&&raw>0?Math.floor(raw):0;return Math.min(n,Math.floor(Math.max(0,Number(base)||0)*BONUS_XP_SHARE));}\r\nexport const DIFFICULTIES=Object.freeze(['easy','medium','hard','expert']);\r\nconst scaled=rows=>Object.freeze(rows.map(r=>Object.freeze(r.map(n=>n*XP_SCALE))));\r\nexport const HOLD_XP_PER_MINUTE=scaled([[4,8,16],[8,12,20],[12,16,24],[16,20,28]]);\r\nexport const REP_XP=scaled([[1,2,4],[2,3,5],[3,4,6],[4,5,7]]);\r\nexport const HOLD_BLOCK_SECONDS=15,HOLD_TARGET_SECONDS=600,HOLD_CAP_SECONDS=1800;\r\nexport const HOLD_RECOVERY_SECONDS=15,WORKOUT_REST_SECONDS=30;\r\nexport const GOLD_HOLD_SECONDS=600,REP_CAP=30;\r\nexport const PREPARATION_REPS=3,SECOND_PREPARATION_TEMPO_SECONDS=5;\r\nexport const SPRINT_ROUNDS=5,SPRINT_SECONDS=15,SPRINT_REST_SECONDS=45;\r\nexport const MAX_CONSECUTIVE_TRAINING_DAYS=2;\r\nexport const PACK_SIZES=Object.freeze({uncommon:1,rare:2,legendary:3,secret:3});\r\nexport const COSMETIC_PACK_ODDS=Object.freeze({\r\n uncommon:Object.freeze({color:90,'64-bit':7,texture:3}),\r\n rare:Object.freeze({color:80,'64-bit':15,texture:5}),\r\n legendary:Object.freeze({color:70,'64-bit':20,texture:10}),\r\n // Secret packs come only from secret achievements: legendary's size and 64-bit chance, double its texture chance (10 -> 20).\r\n secret:Object.freeze({color:60,'64-bit':20,texture:20}),\r\n});\r\nexport const DAILY_MEDITATION_MULTIPLIER=2,DAILY_TRIO_BONUS=500*XP_SCALE;\r\nexport const BENCHMARK=Object.freeze({sessions:120,activeMinutes:30,expertXpPerMinute:28*XP_SCALE,meditationMultiplier:DAILY_MEDITATION_MULTIPLIER,trioBonus:DAILY_TRIO_BONUS});\r\nexport const COSMETIC_XP_TARGET=BENCHMARK.sessions*(BENCHMARK.activeMinutes*BENCHMARK.expertXpPerMinute*BENCHMARK.meditationMultiplier+BENCHMARK.trioBonus);\r\nexport const SPRINT_ROUND_XP=BENCHMARK.expertXpPerMinute*SPRINT_SECONDS/60,GENTLE_BLOCK_XP=XP_SCALE;\r\nexport const COSMETIC_LEVEL_COUNT=250;\r\nconst transitions=COSMETIC_LEVEL_COUNT-1,firstCost=40,baseTarget=COSMETIC_XP_TARGET/XP_SCALE;\r\n// Increasing per-level costs, exact 261,600 XP endpoint with meditation and all-three\r\n// completion on each benchmark training day. Additive coach bonuses\r\n// deliberately accelerate this conservative benchmark; achievements never require XP.\r\n// Built at the original scale then multiplied, so every threshold is exactly XP_SCALE x the old one.\r\nexport const COSMETIC_LEVEL_XP=Object.freeze(Array.from({length:COSMETIC_LEVEL_COUNT},(_,n)=>\r\n XP_SCALE*(firstCost*n+Math.floor((baseTarget-firstCost*transitions)*n*(n-1)/(transitions*(transitions-1))))\r\n));\r\n\r\nfunction nonnegative(value,name){if(!Number.isFinite(value)||value<0)throw RangeError(`Invalid ${name}.`);return value;}\r\nfunction difficultyIndex(value){\r\n const index=typeof value==='string'?DIFFICULTIES.indexOf(value):value;\r\n if(!Number.isInteger(index)||index<0||index>=DIFFICULTIES.length)throw RangeError('Invalid difficulty.');\r\n return index;\r\n}\r\nexport function coachXpMultiplier(unlockedCoaches=0){\r\n if(!Number.isSafeInteger(unlockedCoaches)||unlockedCoaches<0)throw RangeError('Invalid unlocked coach count.');\r\n return 1+unlockedCoaches*.25;\r\n}\r\nexport function holdRate(difficulty,activeSeconds){\r\n nonnegative(activeSeconds,'active time');\r\n return HOLD_XP_PER_MINUTE[difficultyIndex(difficulty)][activeSeconds<60?0:activeSeconds<180?1:2];\r\n}\r\nexport function extendedHoldMultiplier(continuousSeconds){\r\n nonnegative(continuousSeconds,'continuous time');\r\n return continuousSeconds>=1200?2:continuousSeconds>=900?1.5:continuousSeconds>=600?1.25:1;\r\n}\r\n/** Completed blocks only. `from` is already credited active time: no rounding on\r\n * pause/restart. The owner carries unfinished blocks across breaks in the session.\r\n * Continuous time is a separate attempt clock, supplied by the session controller. */\r\nexport function holdXp({difficulty,from=0,to,continuousAtFrom=0,unlockedCoaches=0}={}){\r\n nonnegative(from,'credited active time');nonnegative(to,'active time');nonnegative(continuousAtFrom,'continuous time');\r\n const index=difficultyIndex(difficulty),multiplier=coachXpMultiplier(unlockedCoaches);\r\n const end=Math.min(HOLD_CAP_SECONDS,to),start=Math.min(HOLD_CAP_SECONDS,from);\r\n if(end<start)throw RangeError('Active time cannot move backwards.');\r\n let xp=0;\r\n for(let block=Math.floor(start/HOLD_BLOCK_SECONDS);block<Math.floor(end/HOLD_BLOCK_SECONDS);block++){\r\n  const activeStart=block*HOLD_BLOCK_SECONDS;\r\n  // The block began before `from` when a controller samples a partial block.\r\n  const continuousStart=Math.max(0,continuousAtFrom+activeStart-from);\r\n  xp+=holdRate(index,activeStart)/4*extendedHoldMultiplier(continuousStart)*multiplier;\r\n }\r\n return xp;\r\n}\r\nexport function repRate(difficulty,rep){\r\n if(!Number.isInteger(rep)||rep<1)throw RangeError('Invalid repetition.');\r\n const rates=REP_XP[difficultyIndex(difficulty)];\r\n if(rep>REP_CAP)return 0;\r\n const base=rates[rep<8?0:rep<12?1:2];\r\n return base*(rep>25?2:rep>20?1.5:rep>15?1.25:1);\r\n}\r\nexport function repXp({difficulty,from=0,to,unlockedCoaches=0,preparation=false}={}){\r\n difficultyIndex(difficulty);\r\n if(!Number.isSafeInteger(from)||!Number.isSafeInteger(to)||from<0||to<from)throw RangeError('Invalid repetition range.');\r\n const multiplier=coachXpMultiplier(unlockedCoaches);if(preparation)return 0;\r\n let xp=0;for(let rep=from+1;rep<=Math.min(REP_CAP,to);rep++)xp+=repRate(difficulty,rep)*multiplier;\r\n return xp;\r\n}\r\nexport function performanceMilestones(kind,value){\r\n nonnegative(value,'performance');\r\n if(kind==='hold')return {weapon1:value>=60,weapon2:value>=180,coach:value>=300,golden:value>=GOLD_HOLD_SECONDS,special:value>=GOLD_HOLD_SECONDS};\r\n if(kind==='reps')return {weapon1:value>=8,weapon2:value>=12,coach:value>=15,golden:false,special:false};\r\n throw RangeError('Unknown performance kind.');\r\n}\r\nexport function performanceWeaponTier(difficulty,repeatedCompletions=0){\r\n const index=difficultyIndex(difficulty);\r\n if(!Number.isSafeInteger(repeatedCompletions)||repeatedCompletions<0)throw RangeError('Invalid repeat count.');\r\n return Math.min(20,index*5+Math.min(5,repeatedCompletions));\r\n}\r\nexport function cosmeticLevel(xp){\r\n nonnegative(xp,'XP');let index=0;\r\n while(index<COSMETIC_LEVEL_XP.length-1&&xp>=COSMETIC_LEVEL_XP[index+1])index++;\r\n const max=index===COSMETIC_LEVEL_XP.length-1;\r\n return {level:index+1,xp,floor:COSMETIC_LEVEL_XP[index],next:max?null:COSMETIC_LEVEL_XP[index+1],\r\n  into:max?0:xp-COSMETIC_LEVEL_XP[index],need:max?0:COSMETIC_LEVEL_XP[index+1]-COSMETIC_LEVEL_XP[index],max};\r\n}\r\n","public-access.mjs":"// Public shell download policy. Anyone with the installed app can enter every\r\n// optional room (Ian, v61 correction: no second Coach Army gate); optional\r\n// editor/pocket assets are only kept out of the first install precache.\r\nexport const OPTIONAL_PRECACHE_PREFIXES = Object.freeze(['/handborne/','/war-room','/warroom','/character-editor/','/editor/','/pocket-hardware.css','/hardware-launch.css','/pod/hardware.css','/pod/hardware.mjs','/pod/whiteboard.css']);\r\n// Still read by the War Room lane (war-room/war-room.mjs); nothing outside it gates on this.\r\nexport function coachArmyComplete(account){\r\n const entitlement=account?.entitlements?.coachArmy;\r\n return entitlement?.status==='completed'&&Number.isSafeInteger(entitlement.completedAt)&&entitlement.completedAt>0;\r\n}\r\nexport function filterInitialPrecache(files){return files.filter(file=>!OPTIONAL_PRECACHE_PREFIXES.some(prefix=>file===prefix||file.startsWith(prefix)));}\r\n","public-entry.mjs":"// Deferred bootstrap for optional editor documents. Open to guests and every\r\n// account (no Coach Army gate); account-owned saves still go through the APIs.\r\n(function () {\r\n  // Vite's dev client can evaluate an external script after currentScript has\r\n  // cleared; the tag remains the unambiguous contract for this document.\r\n  const script = document.currentScript || document.querySelector('script[data-entry][data-module]');\r\n  if (!script?.dataset.entry || !script.dataset.module) return;\r\n  // Keep this file usable as a classic external script; Vite otherwise\r\n  // rewrites import() into module syntax before the browser executes it.\r\n  (new Function('path', 'return import(path)'))(script.dataset.module);\r\n})();\r\n","push-subscribe.mjs":"// Shared by every pushManager subscribe caller (launch.mjs, armie-letters-client.mjs).\r\n// R7-VAPID-ROTATE: release 7 moves reminders to a Cloudflare Worker with a NEW VAPID\r\n// keypair; the old one can't be read back. A subscription made under the old key\r\n// looks \"existing\" to the browser, so callers used to just reuse it -- and every push\r\n// to that phone then failed silently. Compare the subscribed key's bytes to the\r\n// server's current public key and resubscribe when they differ.\r\nexport const fromBase64 = text => Uint8Array.from(atob(text.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(text.length / 4) * 4, '=')), c => c.charCodeAt(0));\r\n\r\nfunction sameKey(existingKey, key) {\r\n if (!existingKey) return false;\r\n const x = new Uint8Array(existingKey);\r\n return x.length === key.length && x.every((v, i) => v === key[i]);\r\n}\r\n\r\n// True when this device's subscription was made under a different VAPID key (before the Cloudflare\r\n// move), so pushes to it fail. An unknown key (browser doesn't expose it) is not treated as stale.\r\nexport const staleSubscription = (subscription, publicKeyBase64) => {\r\n const existingKey = subscription?.options?.applicationServerKey;\r\n return !!existingKey && !!publicKeyBase64 && !sameKey(existingKey, fromBase64(publicKeyBase64));\r\n};\r\n\r\n// Returns {subscription, fresh}. fresh is true when a new browser subscription was\r\n// just created (no prior subscription, or the prior one was for a stale key and got\r\n// unsubscribed) -- callers use it to decide whether to roll back on a failed server\r\n// registration, same as the old `!existing` check.\r\nexport async function subscribePush(registration, publicKeyBase64) {\r\n const key = fromBase64(publicKeyBase64);\r\n const existing = await registration.pushManager.getSubscription();\r\n if (existing) {\r\n  if (sameKey(existing.options?.applicationServerKey, key)) return {subscription: existing, fresh: false};\r\n  await existing.unsubscribe();\r\n }\r\n const subscription = await registration.pushManager.subscribe({userVisibleOnly: true, applicationServerKey: key});\r\n return {subscription, fresh: true};\r\n}\r\n","quick-setup-form.mjs":"import {FIELDS,missingFields} from './onboarding-domain.mjs?v=quick-install-v1';\r\nimport {withQuickDefaults} from './quick-setup.mjs';\r\nconst node=(tag,text)=>{const el=document.createElement(tag);if(text)el.textContent=text;return el;};\r\nexport function mountQuickSetup(host,initial,{save,label='Start my coach',changed=()=>{}}){\r\n const data=withQuickDefaults(initial),form=node('form');form.className='setup-form quick-setup';\r\n const controls={};\r\n for(const key of ['goal','sessionMinutes','limits']){\r\n  const field=FIELDS.find(f=>f.key===key),label=node('label',key==='goal'?'Goal':key==='sessionMinutes'?'Minutes per session':'Any injuries or movement limits?');let input;\r\n  if(key==='goal'){input=node('select');input.append(new Option('Choose your goal',''));for(const value of field.options)input.append(new Option(value,value));}\r\n  else if(key==='sessionMinutes'){input=node('input');input.type='number';input.min=1;input.max=180;input.step=1;input.inputMode='numeric';}\r\n  else{input=node('textarea');input.rows=2;input.maxLength=2000;input.placeholder='None, or describe your limits.';}\r\n  input.name=key;input.required=key!=='limits';input.value=key==='limits'?data.answers.armie?.q3||'':data.profile[key]??'';controls[key]=input;label.append(input);form.append(label);\r\n  if(key==='limits'){\r\n    const noLimitsBtn=node('button','No limits');noLimitsBtn.type='button';noLimitsBtn.className='no-limits-btn';\r\n    const sync=()=>{const on=/^none\\.?$/i.test(input.value.trim());noLimitsBtn.setAttribute('aria-pressed',String(on));noLimitsBtn.textContent=on?'✓ No limits':'No limits';noLimitsBtn.style.outline=on?'2px solid #b8e9cf':'';};\r\n    noLimitsBtn.addEventListener('click',()=>{input.value=/^none\\.?$/i.test(input.value.trim())?'':'None';paint();});input.addEventListener('input',sync);form.addEventListener('input',sync);noLimitsBtn.addEventListener('click',sync);sync();\r\n    label.append(noLimitsBtn);\r\n  }\r\n }\r\n const status=node('p');status.role='status';const button=node('button',label);button.type='submit';button.className='setup-submit';form.append(status,button);host.replaceChildren(form);\r\n function collect(){data.profile.goal=controls.goal.value;data.profile.sessionMinutes=controls.sessionMinutes.value===''?null:Number(controls.sessionMinutes.value);data.answers.armie??={};data.answers.armie.q3=controls.limits.value.trim();return data;}\r\n function paint(){collect();const missing=missingFields(data);button.disabled=missing.length>0;status.textContent=missing.length?'': 'You’re ready.';changed(structuredClone(data));}\r\n form.addEventListener('input',paint);form.addEventListener('change',paint);\r\n form.onsubmit=async event=>{event.preventDefault();collect();if(missingFields(data).length||!form.reportValidity())return;button.disabled=true;status.textContent='Starting your coach…';try{await save(structuredClone(data));}catch(error){status.textContent=error.message;button.disabled=false;}};\r\n paint();return {form,collect};\r\n}\r\n","quick-setup.mjs":"import {withOfficeDefaults} from './office-domain.mjs';\r\nexport const QUICK_REQUIRED_FIELDS=['goal','sessionMinutes'];\r\nexport function withQuickDefaults(initial){\r\n const data=initial.entryRoute==='office'?withOfficeDefaults(initial):structuredClone(initial);\r\n data.setupMode='quick';data.profile??={};data.answers??={};\r\n const defaults={name:'You',coach:data.appearance?.['myr5-recipe-v1']?.coach||'supportive',trainingStyle:'Gradual progression',guidance:'Balanced',restSeconds:'60',trainingTime:'17:00',timezone:'UTC',reminderTone:'gentle',reminderDays:'3'};\r\n for(const [key,value] of Object.entries(defaults))if(data.profile[key]==null||data.profile[key]==='')data.profile[key]=value;\r\n if(data.profile.exercises==null||Array.isArray(data.profile.exercises)&&!data.profile.exercises.length)data.profile.exercises=['squat','pushup','tree','warrior','horse','boxing','jogging','jumping'];\r\n return data;\r\n}\r\n","receive-coach.mjs":"import {WEBSITE} from './onboarding-domain.mjs';\r\nexport function receiveCoach(store){\r\n const source=window.opener;if(!source)return Promise.reject(Error('Return to the final website setup and tap Unlock again. Keep that page open while Coach loads.'));\r\n return new Promise((resolve,reject)=>{let interval,timer;\r\n  const cleanup=()=>{clearInterval(interval);clearTimeout(timer);window.removeEventListener('message',message);};\r\n  function message(event){if(event.origin!==WEBSITE||event.source!==source||event.data?.type!=='myr5:coach-transfer')return;try{const v=event.data.profile,raw=JSON.stringify(v);if(!v||new TextEncoder().encode(raw).length>55000)throw Error('This transfer is too large.');store(raw);source.postMessage({type:'myr5:coach-received'},WEBSITE);cleanup();resolve(v);}catch(e){cleanup();reject(e);}}\r\n  window.addEventListener('message',message);const ready=()=>source.postMessage({type:'myr5:ready-for-coach'},WEBSITE);interval=setInterval(ready,700);timer=setTimeout(()=>{cleanup();reject(Error('Return to the website and tap Unlock again. Your saved answers are still there.'));},120000);ready();\r\n });\r\n}\r\n","recover.html":"<!doctype html>\r\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta http-equiv=\"refresh\" content=\"0;url=/repair-coach\"><title>Repair Coach</title><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><p><a href=\"/repair-coach\">Repair and update Coach</a></p></body></html>\r\n","recovery-page.mjs":"// This page has no module, sign-in, or cached-page dependency.\r\nexport const RECOVERY_HTML=String.raw`<!doctype html>\r\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Repair Coach</title>\r\n<style>html{background:#152126}body{border:12px ridge #728880;box-sizing:border-box;min-height:calc(100dvh - 16px);box-shadow:inset 0 0 20px #0008}button,a{box-shadow:0 3px 4px #0008}button:active{transform:translateY(3px)}body{margin:0;min-height:100svh;display:grid;place-items:center;background:#241c35;color:#fff7de;font:17px/1.6 system-ui,sans-serif}main{max-width:420px;padding:32px;text-align:center}img{width:160px;height:160px;object-fit:contain}progress{display:block;width:100%;height:14px;accent-color:#edc866}button,a{font:inherit}button{padding:12px 24px;min-height:48px;background:#edc866;border:0;border-radius:8px;color:#241c35}a{color:#fff7de}button[hidden]{display:none}</style></head>\r\n<body><main><img src=\"/icons/myr5-alien-192.png\" alt=\"MYR5 Coach\"><h1>Repairing Coach</h1><progress id=\"progress\" max=\"100\" aria-label=\"Coach download\"></progress><p id=\"status\" role=\"status\">Connecting to the update…</p><p id=\"instructions\">Keep this page open while Coach downloads. Your saved progress stays here.</p><button id=\"retry\" hidden>Try again</button><noscript><p>Open this link in Chrome or Safari with JavaScript enabled.</p></noscript></main>\r\n<script>\r\n(()=>{'use strict';\r\nconst status=document.getElementById('status'),retry=document.getElementById('retry'),meter=document.getElementById('progress'),sw=navigator.serviceWorker;\r\nlet currentWorker;\r\nconst waitingMessage='Update downloaded. Waiting for your Coach session to finish safely. No browser tabs need to be closed.';\r\nlet preparedUpdate=null;\r\nsw?.addEventListener('message',event=>{\r\n if(event.data?.type==='UPDATE_SAFETY_CHECK'){preparedUpdate=event.data.id;event.ports[0]?.postMessage({safe:true,protocol:2,id:preparedUpdate});return;}\r\n if(event.data?.type==='UPDATE_CONFIRM'){event.ports[0]?.postMessage({safe:preparedUpdate===event.data.id,protocol:2,id:preparedUpdate});return;}\r\n if(event.data?.type==='UPDATE_ABORT'){if(preparedUpdate===event.data.id)preparedUpdate=null;return;}\r\n if(event.data?.type!=='OFFLINE_PROGRESS'||event.source!==currentWorker)return;\r\n const p=event.data;if(!p.totalBytes)return;\r\n const percent=Math.min(99,Math.floor(100*p.bytes/p.totalBytes));meter.value=percent;\r\n status.textContent='Saving Coach on this device: '+percent+'% · '+p.files+' of '+p.totalFiles+' files. Large models can take a few minutes.';\r\n});\r\nfunction state(worker,allowed){return new Promise((resolve,reject)=>{\r\n const finish=error=>{clearTimeout(timer);worker.removeEventListener('statechange',check);error?reject(error):resolve();};\r\n const check=()=>{if(allowed.includes(worker.state))finish();else if(worker.state==='redundant')finish(Error('The download did not finish. Check your connection and free space, then try again.'));};\r\n const timer=setTimeout(()=>finish(Error('The download is taking longer than expected. Check your connection, then try again.')),20*60*1000);\r\n worker.addEventListener('statechange',check);check();\r\n});}\r\nfunction activate(worker){return new Promise((resolve,reject)=>{\r\n const channel=new MessageChannel();\r\n const finish=(error,result)=>{clearTimeout(timer);channel.port1.close();error?reject(error):resolve(result);};\r\n const timer=setTimeout(()=>finish(Error(waitingMessage)),20000);\r\n channel.port1.onmessage=event=>{\r\n  if(event.data?.activated)return finish(null,true);\r\n  if(['close_clients','busy'].includes(event.data?.reason))return finish(null,false);\r\n  finish(Error(waitingMessage));\r\n };\r\n try{worker.postMessage({type:'PREPARE_UPDATE'},[channel.port2]);}catch(error){finish(error);}\r\n});}\r\nasync function update(){\r\n retry.hidden=true;meter.removeAttribute('value');status.textContent='Connecting to the update…';\r\n try{\r\n  if(!sw)throw Error('Open this repair link in Chrome, Edge, or Safari.');\r\n  const reg=await sw.register('/sw.js',{updateViaCache:'none'});await reg.update();\r\n  const worker=reg.installing||reg.waiting||reg.active;if(!worker)throw Error('Coach could not start downloading. Try again while online.');\r\n  currentWorker=worker;worker.postMessage({type:'OFFLINE_STATUS'});\r\n  status.textContent='Downloading the complete Coach app. Large models can take a few minutes.';\r\n  await state(worker,['installed','activating','activated']);\r\n  if(reg.waiting){\r\n   status.textContent='Preparing the update…';\r\n   if(!await activate(reg.waiting)){\r\n    currentWorker=null;meter.value=100;status.textContent=waitingMessage;\r\n    document.getElementById('instructions').textContent='Finish your workout first. If your installed Coach is an older version, swipe Coach away once from Android Recent apps. Leave this page open; it will finish automatically. Your saved progress stays here.';\r\n    setTimeout(update,5000);return;\r\n   }\r\n  }\r\n  await state(worker,['activated']);\r\n  meter.value=100;status.textContent='Coach is ready. Opening your app…';\r\n  location.replace('/pose.html?panel=install&update=repaired');\r\n }catch(error){status.textContent=error.message;retry.hidden=false;}\r\n}\r\nretry.onclick=update;update();\r\n})();\r\n</script></body></html>`;\r\n","release-build.mjs":"// Generated by the production build from app content.\nexport const BUILD_ID='207aa55d1c21dc94e7c9';\n","release-info.mjs":"import {BUILD_ID} from './release-build.mjs';\r\nexport const RELEASE=Object.freeze({id:'2026-10-20-release-102-'+BUILD_ID,title:'Release 102: Full-screen material grimoires',date:'2026-10-20',url:'https://myr5.mominc.online/pose',notes:[\r\n 'Every grimoire fills the usable screen inside the frame, in portrait and landscape, with wormholes made from its own material.',\r\n 'Pixel starter: connect lights with colored wires along the rafters above a distant dance floor. Power every fixture in any order to start the party, floor collapse, sparks, blackout and secret descent.',\r\n 'Little Boyfriend clay: sculpted Sakura tablet and purple clay trails. Cover 80 percent of the board to dry, crack and crumble it.',\r\n 'Dr. Girlfriend paper: measured graph paper, white glue and falling construction paper. Decorative left-handed scissors cut a visible trail with the opposing blade hidden beneath the sheet.',\r\n 'Cortisol Corgi papier mache: original scary drawings and warnings, paint splatters, and a stick-figure scare leading to the Achievement Hall through a newspaper wormhole.',\r\n 'Coach World gains the completed RTS command console: a 3?3 command card, animated selection portraits, multi-unit tiles, minimap markers and controls fitted inside the cockpit in portrait and landscape.',\r\n 'Raids use owned and trained crews, hall upgrades and a built hangar; defenses and real expedition rewards now reach the game rules.',\r\n 'The character editor has collapsible part menus, fewer duplicate designs, a larger character and automatic close-up views of the part you edit.',\r\n 'World distance fades in dithered bands; optional ?retro=1 enables the original-art color palette. Existing cockpit, games, sky, terrain and saved progress are preserved.',\r\n 'A single metal frame and engraved navigation keys surround the cockpit, menus and setup screens. Active games fill the screen; pausing returns the frame.',\r\n 'The cockpit separates your viewing port, workout controls and game cartridges. Tap a panel to work with it, then release it to explore again.',\r\n 'Standing and floor games have separate hanging visors, illustrated cartridges and retro title screens that wait for their game files to load.',\r\n 'Reminders sit behind the modeled computer glass, with the same working controls available when its 3D room is unavailable. The Vault door has deeper metalwork and keeps its existing wheel and earned secrets.',\r\n 'Plank Flight scenery has layered structures, consistent surface scale and distant skies. Dance now includes its required game files in the published app.',\r\n 'Dance becomes an alien club: your larger saved 3D coach stands on the floor with beat-driven crowd shadows, lights and solid named-move demonstrations.',\r\n '24 named dances include Sprinkler, Shopping Cart, Whip, Nae Nae, Dougie and Gangnam Style. Move timing follows the tempo; exact poses and footwork are not scored.',\r\n 'Subway Burpees, Crouching Crisis and Jet Plank Joyride add angled light, depth and grounded shadows, with quieter clocks and clearer level, bonus XP and recent-power feedback.',\r\n 'Coach World is one map: pinch from your base out to your habitat, then the Las Vegas MOM city with moving ship traffic and live weather. The Base/Sectors/Camera tabs are gone; Camera is a header button.',\r\n 'Raids add a cannon with seven abilities. Your base and troops keep working as before; ?world=0, ?rts3d=0 and ?rts=0 still switch the new layers off.',\r\n 'The Vault door and hall use lighter graphics settings on phones to reduce graphics-memory pressure.',\r\n 'If the door loses its graphics context, it rebuilds once at lower resolution while keeping your selected clue and open door. Repeated losses still offer Retry; earned secrets stay saved.',\r\n 'Crouching Crisis: stopping mid level-change never switches the level afterwards, and a graphics hiccup can no longer freeze the next-level reveal.',\r\n 'Grimoire secrets finish their current animation before handing graphics over to the Achievement Vault, avoiding a crash during the door transition.',\r\n 'Coach previews scan dense limb meshes without oversized function calls that can exceed a phone browser’s call-stack limit.',\r\n 'Plank Flight keeps moving forward during visible flat-stomach rest even when arms are hidden or folded, without a new calibration countdown.',\r\n 'Returning to your hands keeps the learned controls. Floor rest remains uncredited; missing-body tracking loss still pauses flight.',\r\n 'The Achievement Vault keeps its new 3D renderer when a previous close event arrives during reopening.',\r\n 'A stalled Vault startup now offers Retry instead of staying on Opening indefinitely; existing progress stays saved.',\r\n 'Fixes missing game files: Crouching Crisis Halo, Kitchen and PS2 levels and Plank Flight cockpit load again.',\r\n 'Crouching Crisis: walk through a multi-floor building between waves (turns, stairs, elevator, outside), cover waits at each stop, richer textures, atmosphere, robot Terminators and new sounds.',\r\n 'Coach World: pinch to zoom out now works (default view a little closer, pull back to see the whole base), and the base edge fades into a soft round horizon instead of a square.',\r\n 'Coach World: tap a building to select it, then tap MOVE and drag or tap where it should go. Holding a building still picks it up. Tap empty ground to open the build tray for that spot; tap a troop to open its commands.',\r\n 'Dance is part of Coach: use any music app with four beat taps and a selected style, or play the three original Coach tracks directly. No developer account required.',\r\n 'Your two-legged 3D coach mirrors your real arm movement, framed to the visible part of your body, with a holographic live outline and a separate large choreography cue. Four-legged coaches use the outline instead.',\r\n 'Purple and blue party lights bring in red for a miss, green for good and gold for great, with matching feedback around the phone edge.',\r\n 'Choose among 24 named dance moves and nine styles. Each 120-beat round has five star goals: 2,400, 4,800, 7,200, 9,000 and 10,800 points. Movement is forgiving; no exact pose required.',\r\n 'Song changes include an unscored celebration and shakeout, then fresh rhythm preparation and a count-in. Android native timing remains available through the companion.',\r\n 'Plank Flight can keep flying during a steady chest-down rest when your arms are partly hidden. Floor rest earns no exercise credit; normal controls return when your arms reappear.',\r\n 'Flight starts at one-third of the former speed and accelerates smoothly. Timed posture cues, pickups and traps keep their rhythm.',\r\n 'Corner thrusters warm toward red during continuous lift, cool on the floor and send their exhaust below the screen. Tropical and rainbow routes add a motorcycle cockpit with push-up wheelies.',\r\n 'Straight routes add ramps and a gold flip chime in the motorcycle worlds while the camera stays upright. Walls have deeper structure, and the tropical promenade has curved palms and layered shopfronts.',\r\n 'Keeps the current Dance floor, Crouching Crisis zones and levels, Cool Cardio, and Coach World.'\r\n]});\r\n","release-trust/materials/coach-ships-biomes/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 9912720,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"f2765764fd81bb0ee68dcecbb8f24b3fb3d7215fa4602568fcbbdf75af525af8\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"b4affce9fb7cf2ea06e9adaa695953b25a9b03fadaa0ffc6492c9da0862d68b3\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"3e958e6d00361b1c63fe31befa1182b23723b4fd0787820777368f6f8066c1ed\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"2eae71fda821496c23ae0a8d39b06b18b67aba93d6e2b31e1bcc511833402e8b\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"6680544f0f81efb648448e647ef59e10eb1197c3ef59df7ee190ba3b87015cb7\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"e6fc194be9547f028642ab1ce14eb774b0699d70727183124475b482a7af96c8\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"701ce69f570b6285c27b56d6c7214c5df80965a8308ccf1f27e2fde48c6ea721\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"158ca3547a802f1c24a036d320f4b8d8e7db1040215feb322214281c545275ed\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"8a43506ead05071e5002aded3891f544f5ab026e49e094cbbfcb2800f0defd01\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 475536,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"e6dbedf1a406342412dff2be6780f1f9e3bab889e3e25de059e50c3516b2af53\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/supportive.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/supportive.glb\",\r\n      \"sha256\": \"2770e0aa51d2cf262db2762700f4f3567cd34e44d0619f0628eae3bffdf06f09\"\r\n    },\r\n    {\r\n      \"bytes\": 10461352,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"23402097030897ba7bb7bf107333aa36c5b73ff63db64c6a942511b9aa221bfc\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"436d8a9b042e45d07a40efb65376a415a62537f1c3e9c9e422c24ca86e565ab4\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"1a9df385796255e38dbb2b64d7d7f755bfe97b5b1087610aa1e3cf71cb940bff\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"8e69ea4b69f2bb16ec7c6bd06ea667ed50c44317fbb5c9ec90ea719ec3cf1cfc\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"4af9dc6357edce143f179b64efb03f6af94cf606bebedde214a0aab3340a0389\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"628483d904c68b3745eb90409fb3e303e5424688f75c83354a150be01d347166\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"e376840ce2bd710cd1ba257ae5511ddfc1c546e54e8316c658ca1cc8f86ece60\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"9103062f012725e0dc02b74755385d9b5d6b4a29bb7f77d6b0254f1a8c7da895\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"e64648862e68b8e694585d8ec7567d500984d18522dc8bc48e318e7ba8478fb8\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1024168,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"cc579137cb1a17c074f4ede489f1dc5b01dafcdefed09e2c987d6164aa6c0268\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/direct.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/direct.glb\",\r\n      \"sha256\": \"d3757906634a3b2d056147270fbb95f37516853a6ac3f668bc1fd181dc1d36db\"\r\n    },\r\n    {\r\n      \"bytes\": 10085068,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"ab377a2aad7f469a5c3afd842a48ea865b1e1bcfcd39dae1bd21a325b394afc1\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"7cdd1c509d9e8d7f277f0e908376e14ffa4c37c8092a3e826738e4b0aac786d0\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"5189b144571bb13e7217de84a93e4c73cec45deb597404691f42f766410a5f24\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"43e598b8f7de4a741cd488a9dd1478e527974682d442e5bacb519f1a068759f0\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"3e25e7d1dc59e3de576e57ed694726676d4ff8b2afdfec1312c3c32caedc272a\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"cd87eae6a9046c8256f786c7a81f938e5d82953cce1b46f6aef16db9c5a96f51\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"f9676b44b771f8ca43c3c3a6412ac1044baac7e644091409b48a9f7006e06936\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"f864c5d0715bce027271713a3dcb0f6de59027db712ace5a0a730614f34d6a45\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"69ce1b9e4924a1afe26990aa059ad5c54b68d0656aac387a84540f41b0fcfe73\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 647884,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"1aaf24e13307eeb56bbf4a25ae1d7144ecd1bafb653fbe67a6235c898ae3f59b\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/analytical.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/analytical.glb\",\r\n      \"sha256\": \"0bed9d9a447f75dd126cb315c449d763048034902422ad6ac7629b3803dc241f\"\r\n    },\r\n    {\r\n      \"bytes\": 9944576,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"9d74ff2aa3b7c6ca4d3ca2783c549eb656148d7adf5bb73a6675172dbf1abffb\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"523a462a353592c0c7b6a873eccf2797a86434854c629fb17f397ae59ca94d60\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"792768fc8a5a6c195574c9bbe2d8a1f090adf3179e5c763ecadd4e49dc25952d\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"d04a2f1f31c2689559b0453598f85e8d7689bdc803a146f4ba68345866eafa39\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"d7a4bdee82b8090a50ed30e1c1a3ab7fbe2e674154c6bb88ea0e15e455793f81\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"02c48c3047662315d7055ef0117fb0e6ebd4be71ecda45e5b6dbd58669c06e96\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"c02451b26910974d90899e3736a9c3a1252936caa4fc6b525559324f39092d3d\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"e1b6db796df886a5a1528bbea35ba6ee1eee00a78522d05fe99d8b3a05e220ac\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"1eff2bf1af9eb6323702464eb7cd7c4d5f39191ae1c04307427d59844b98913f\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 507392,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"f9cca59d2cf2cc70adc847966253c615051e72617a429963ad1734c84dbdd927\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/playful.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/playful.glb\",\r\n      \"sha256\": \"c53d74da8e12b7676cf6d06d5526938d26da05d07dd4b38af74fae845137e4df\"\r\n    },\r\n    {\r\n      \"bytes\": 10341060,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"edd56ada4921456d02d13140cabcf4b25226a11804c775c94bd1605f5fe34015\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"447b925eb3541f4e4001c0078c3727451fd0f34e4a2934f9317c328ecf5512b8\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"fc36e092cf490f839661bff0b173654298648fd15b7754328d820e666386c5cd\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"046ccf0a8443348a653ac8c57ee7c0872feb1a8222313c26d362c27196d5e14e\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"9eacf012e5d03d0162a9c5cc3238d2c34788ab771c1f52df412eda05c4794a3f\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"f34a37a4c104db9f6796edd5d557f349fa58fd6d95d5fdc7549d7c3793c5f621\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"44f3eca0cc0ae08f9d81a60ff7bc1fc3ba2a19e54a84caa8a91ecffeda1b5cc8\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"c70c3cd2cf3169050114cb79cdc1710f9757b8575022de3febf841d28ef4f040\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"e0fc194e241215d4bd1f934afacd27c1d40ce8d52e8f34cec22160b57b4c32a6\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 903876,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"2bd20a3d8f1503bd67769f1165b1784418ebeb687581ae97d244ba420089595d\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/calm.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/calm.glb\",\r\n      \"sha256\": \"2288a2f10b1a0affc007870f70bf25c1223c00a3d21dcbd28a7262677b78b404\"\r\n    },\r\n    {\r\n      \"bytes\": 10315084,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"ca9d75894c972695ded6bc30603a44d1ab36a31f7b3ceb10da33e723373f7d60\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"72eab6b8bb19667b1cc03812b5eefa6292bde2b0932ea8d59d8f17ad46ab1618\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"9cf1c6820e32c2ac6d6ef328b613d5ea1e681ead2a11daeb4d65c60033682c8d\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 3,\r\n          \"offset\": 3145728,\r\n          \"sha256\": \"9df28c76b385a2a370037b771dc74de41a7531af2a5dea471dcca37166e17782\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 4,\r\n          \"offset\": 4194304,\r\n          \"sha256\": \"25b2914bf6fbd69a75794549d0ebf5431e493ba7c01ce7beccbb47d3224d4be2\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 5,\r\n          \"offset\": 5242880,\r\n          \"sha256\": \"0031f0120d939a91453bd523959d8af96b99b2ea873fb9e174e0c7a1afee12f3\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 6,\r\n          \"offset\": 6291456,\r\n          \"sha256\": \"f866f71f191290d1b74d9eecec3d35984e1b737daa3b1ff0aa9f57780a91799c\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 7,\r\n          \"offset\": 7340032,\r\n          \"sha256\": \"1ef9efa4fa4f36d7abc5cdf495c697120c0c8602a89ae965bddfc49af5cb2f2b\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 8,\r\n          \"offset\": 8388608,\r\n          \"sha256\": \"e5a7c0f9f04959c3b01454e987ed90f0bef4e6b250a6363cd9534c560b90c072\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        },\r\n        {\r\n          \"bytes\": 877900,\r\n          \"index\": 9,\r\n          \"offset\": 9437184,\r\n          \"sha256\": \"b62bdd8d7ca73c5782879c040797ff523a64bb21ecce083f44b7747fb5836f68\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/mom.glb\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/ships/mom.glb\",\r\n      \"sha256\": \"1deb2c0afd574bed329a401b723eeefcb5025b7a9eaf13d5dbaf6c46930ed4d6\"\r\n    },\r\n    {\r\n      \"bytes\": 277137,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 277137,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"53e26dfbfbe97a5ddd78f12b846f00ec191fd547aef7a5763953beb18bd79c44\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/coach-ships-biomes/1.0.0/biomes.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/biomes.m5bundle\",\r\n      \"sha256\": \"53e26dfbfbe97a5ddd78f12b846f00ec191fd547aef7a5763953beb18bd79c44\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"coach-ships-biomes\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"l1EbETMJXruyyKCTrGYBr6rlr3mAS2YUmFXlnzDcqTMhMV+SXhsizYHEF6RdWTEwIE8/4pn2kkXzDuj5EssABA==\"\r\n}\r\n","release-trust/materials/track-arms/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 1122666,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"c92098d11ba3535c4765abdce87f4d2cf0e3deba5f5c5664e9e3983b2ead58de\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-arms/1.0.0/arms.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 74090,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"bf7e003b9e3bd59411cf16a8442e7054e8c0e7648e1425a1439f0998a053b9d5\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-arms/1.0.0/arms.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/arms.m5bundle\",\r\n      \"sha256\": \"ff0cf7e43a688e74a6a32a9d6fc73e6f9557dea3ed85ce0cce7910716d1476b4\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-arms\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"mcmu2PmtFbyozhlFOQFCmdXUQlYIlomts1hJIOctyUwiYEB/MOXacIGZa6ynr71/QgOsFjvlhctd/QbsjbmhCg==\"\r\n}\r\n","release-trust/materials/track-cardio/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 1537805,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"eb01e9f223b96013cad792777daef229c731973b7d3b61c48cb1257ffd73c1c0\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-cardio/1.0.0/cardio.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 489229,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"4086ce291e2c25c07662e9d66d00fe013ac874d85cd3a63c39756fe74f1ad284\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-cardio/1.0.0/cardio.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/cardio.m5bundle\",\r\n      \"sha256\": \"9c8c59d696df833daa99cfccf29a5e0975d1953228d322ac0439b60770feea3b\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-cardio\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"WNRFA+XchdvXXYRykgF0QK3CTXIU0kFdSjt/ZBibt3piWo1H9jmN7rGdL7+na6bveJjYdR4eDzRFVKi4qdMKAA==\"\r\n}\r\n","release-trust/materials/track-chest/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 843781,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 843781,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"c76d08541a42ccf4f47cb52beea0396665ec7d77c36c61a61905f59ba9bf0c25\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-chest/1.0.0/chest.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/chest.m5bundle\",\r\n      \"sha256\": \"c76d08541a42ccf4f47cb52beea0396665ec7d77c36c61a61905f59ba9bf0c25\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-chest\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"6uZXXkvQaUcNHbK80w1xds9KVQh0yXK39u+sFcrcoUbsJY+wmcOGWki0hiE5lCW39PXoBNn+jCdillGEQYfFAA==\"\r\n}\r\n","release-trust/materials/track-glutes/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 2546727,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"340512fd3d3e666ddc5ea5f30ff0a483cd32becab2edd486eb78dd9586c54506\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-glutes/1.0.0/glutes.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"34d360b648b5dc563bd2dcd3f5c3f44f1b1ad2b3e89b8ec35f9d3f4b4ee65842\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-glutes/1.0.0/glutes.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 449575,\r\n          \"index\": 2,\r\n          \"offset\": 2097152,\r\n          \"sha256\": \"887e931765526a6d1f5ce47a6f85c313a96888255090d39698ac9d09130e76e5\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-glutes/1.0.0/glutes.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/glutes.m5bundle\",\r\n      \"sha256\": \"6728e3c20116b189ad8c486cc48ef861bf28d02270fc9e8dda6960dc82bf97a7\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-glutes\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"6fqedheEUjwSQdkVE17DU31DKSX0H9S0xhZ9Tpb/L7JmJFcyFLgF8TBrq7gmbizRw2O+WftrHwyHGO4w94YRDw==\"\r\n}\r\n","release-trust/materials/track-martial-arts/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 1350733,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"8395e377d42d370e53f9e9082841aa53bb25a4f7f43b73771863c1a0515e4fd7\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-martial-arts/1.0.0/martial-arts.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 302157,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"f0721d49614af71174d30d2da1122eb5820d1fdf09fbdfe2ce3786f1c8307265\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-martial-arts/1.0.0/martial-arts.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/martial-arts.m5bundle\",\r\n      \"sha256\": \"654966fa10024710dc47e3618f68003b5ee0c07ee23c870f2d93adc0ed31854e\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-martial-arts\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"pVrwyRilcqn0LefqKa10WvIAym7Eb5pNy9NxRywZcnHNiM0I6LnnOAb6X4PJbL+xek6OM5IfYy6jm099xG7cCg==\"\r\n}\r\n","release-trust/materials/track-meditation/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 1584210,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"eb2dd5f0aa842b621e58d3e03fa61b42c195b8257f9f31c08c8989998712f2e0\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-meditation/1.0.0/meditation.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 535634,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"23dd1c83aaff02d52bc1d7edf962110635bff6fc49e6a5ca8f168c8f33b2b3e0\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-meditation/1.0.0/meditation.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/meditation.m5bundle\",\r\n      \"sha256\": \"cbffbd9b31a524cafd879ae27dcad5f3aace3bd9b7f99fa7ea44cb644015b7bb\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-meditation\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"oq8iPBwBYr3VMV1/Ufa03kwtM4Aanh4GgSqjk54joFOfmtxfpPmK8Pm7m29BCRIj0izSuYGWhE0zF2ftAzY/Dw==\"\r\n}\r\n","release-trust/materials/track-quads/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 1227707,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 1048576,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"96732979bd4e29d4a5a620a2cb7507c0e568d30c6c963db3cd231b554b59be79\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-quads/1.0.0/quads.m5bundle\"\r\n        },\r\n        {\r\n          \"bytes\": 179131,\r\n          \"index\": 1,\r\n          \"offset\": 1048576,\r\n          \"sha256\": \"0bce54b9701d11ff7e2ad975506719f8c6cd3ce39af09fff2c87f97783c13b96\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-quads/1.0.0/quads.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/quads.m5bundle\",\r\n      \"sha256\": \"928026875a70613d3ddf7d727d6a83e08ca6be869cc2959c78c341d14eef7a9e\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-quads\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"j/NP4pr2/Mehe3oEZ1KWgz5PEBJei0W5TDptODUCuitsCckGSVb9y/tBXDaJSdbe00q3NRG48EENeU9vURe3Aw==\"\r\n}\r\n","release-trust/materials/track-yoga/chunk-manifest.json":"{\r\n  \"assets\": [\r\n    {\r\n      \"bytes\": 950844,\r\n      \"chunks\": [\r\n        {\r\n          \"bytes\": 950844,\r\n          \"index\": 0,\r\n          \"offset\": 0,\r\n          \"sha256\": \"e9452dbc7d6425a687945fcacd173ba6f958fee183aab01e7596d4a6aa679b8e\",\r\n          \"url\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials/track-yoga/1.0.0/yoga.m5bundle\"\r\n        }\r\n      ],\r\n      \"path\": \"assets/yoga.m5bundle\",\r\n      \"sha256\": \"e9452dbc7d6425a687945fcacd173ba6f958fee183aab01e7596d4a6aa679b8e\"\r\n    }\r\n  ],\r\n  \"keyId\": \"mom-material-production-v1\",\r\n  \"packId\": \"track-yoga\",\r\n  \"schema\": \"mom-material-chunks-v1\",\r\n  \"version\": \"1.0.0\",\r\n  \"signature\": \"Y6ZQRTG3JiJE2mKIvtwjp370WC/eScHXaNUxKAARpIKOkPN4XVYEQf4mg0VI04XKI5ZfTMW5EAzPTWvzqvewAA==\"\r\n}\r\n","release-trust/public-build.json":"{\r\n  \"$comment\": [\r\n    \"Public build-time trust for release builds (scripts/build.mjs). Public verification values only:\",\r\n    \"the private signing key never goes here or in the build env (build.mjs throws if it is set).\",\r\n    \"An env var with the same name overrides a value here; null leaves it unset.\",\r\n    \"MYR5_MATERIAL_*: Ed25519 public key + commit-pinned raw host for the creature-skin and ship/biome\",\r\n    \"post-download sections. The nine signed manifests are in materials/<section>/chunk-manifest.json;\",\r\n    \"the build verifies each against this key before copying it to /materials/. Recovered 2026-09-26\",\r\n    \"from Codex's release 2 build (site-b0dea68, 22 Sep); later Sites builds, incl. live v65, dropped them.\",\r\n    \"PUBLIC_EXPANSION_SIGNING_JWK: no expansion key has ever been published (null in every release).\"\r\n  ],\r\n  \"MYR5_MATERIAL_PUBLIC_SIGNING_JWK\": {\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"x\":\"nfofxCZW0Pz9o2_6d2AGnwL7EA3CsA52NlF1vMcFnDY\"},\r\n  \"MYR5_MATERIALS_BASE_URL\": \"https://raw.githubusercontent.com/MomInc-MBS/coach-app/1c49652e0e16d0b89818d32a0748bee98b8f1418/materials\",\r\n  \"MYR5_MATERIAL_SITE_MANIFESTS\": \"release-trust\",\r\n  \"PUBLIC_EXPANSION_SIGNING_JWK\": null\r\n}\r\n","reminder-controls.mjs":"import {TONES,CADENCES,CADENCE_LABELS,reminderMessage,quietPosToTime,quietTimeToPos} from './reminder-settings.mjs';\r\n// R3-12: four controls on the TV glass (power = #notificationSwitch in launch.mjs, times-per-day dial, quiet-hours slider,\r\n// three tone faces) drive the ordinary reminder form; everything else lives behind the wrench hatch.\r\nexport function mountReminderControls(){\r\n const $=id=>document.getElementById(id),form=$('reminderForm'),slider=$('consistencySlider'),dial=$('timesDial'),from=$('quietFrom'),to=$('quietTo'),faces=[...document.querySelectorAll('#remindersPanel .tv-tones [data-tone]')];\r\n let tone=1,times=1,drag=null;\r\n const label=n=>n===0?'Off':n+' a day';\r\n function render(){\r\n  const t=TONES[tone],days=CADENCES[Number(slider.value)];\r\n  form.elements.tone.value=t;form.elements.daysPerWeek.value=days;form.elements.timesPerDay.value=times;\r\n  faces.forEach((f,i)=>f.setAttribute('aria-checked',String(i===tone)));\r\n  $('reminderPreview').textContent=reminderMessage(form.elements.kind.value,t);\r\n  $('consistencyValue').textContent=CADENCE_LABELS[days];slider.setAttribute('aria-valuetext',CADENCE_LABELS[days]);slider.style.setProperty('--fill',`${Number(slider.value)/3*100}%`);\r\n  dial.setAttribute('aria-disabled',String(!!form.dataset.editing));dial.setAttribute('aria-valuenow',times);dial.setAttribute('aria-valuetext',label(times));dial.style.setProperty('--turn',`${(times-2.5)*54}deg`);\r\n  dial.querySelectorAll('.tv-ticks s').forEach((s,i)=>{s.style.setProperty('--a',`${(i-2.5)*54}deg`);i<times?s.setAttribute('data-on',''):s.removeAttribute('data-on');});\r\n  $('timesValue').textContent=times===0?'OFF':times+'/day';\r\n }\r\n // Quiet slider <-> the two time inputs in the hatch (those stay the source of truth for saving).\r\n function quietFromInputs(){\r\n  from.value=quietTimeToPos(form.elements.quietStart.value);to.value=quietTimeToPos(form.elements.quietEnd.value);quietPaint();\r\n }\r\n function quietPaint(){\r\n  const a=Number(from.value),b=Number(to.value);\r\n  const track=from.parentElement;track.style.setProperty('--a',`${a/47*100}%`);track.style.setProperty('--b',`${b/47*100}%`);\r\n  $('quietValue').textContent=a===b?'ANY':quietPosToTime(a)+'-'+quietPosToTime(b);\r\n }\r\n function quietToInputs(){form.elements.quietStart.value=quietPosToTime(from.value);form.elements.quietEnd.value=quietPosToTime(to.value);quietPaint();}\r\n const changeTimes=v=>{if(form.dataset.editing)return;times=Math.max(0,Math.min(5,v));render();};\r\n faces.forEach((f,i)=>f.onclick=()=>{tone=i;render();});\r\n dial.onkeydown=e=>{const a={ArrowLeft:times-1,ArrowDown:times-1,ArrowRight:times+1,ArrowUp:times+1,Home:0,End:5};if(Object.hasOwn(a,e.key)){e.preventDefault();changeTimes(a[e.key]);}};\r\n dial.onpointerdown=e=>{if(e.button!==0)return;drag={x:e.clientX,y:e.clientY,times,id:e.pointerId};dial.setPointerCapture(e.pointerId);dial.focus();};\r\n dial.onpointermove=e=>{if(drag?.id===e.pointerId)changeTimes(drag.times+Math.round(((e.clientX-drag.x)-(e.clientY-drag.y))/24));};\r\n dial.onpointerup=dial.onpointercancel=dial.onlostpointercapture=()=>{drag=null;};\r\n from.oninput=to.oninput=quietToInputs;\r\n for(const name of ['quietStart','quietEnd'])form.elements[name].addEventListener('input',quietFromInputs);\r\n slider.oninput=render;form.elements.kind.addEventListener('change',render);\r\n render();quietFromInputs();\r\n return {load(toneName='direct',days=7,perDay=1){tone=Math.max(0,TONES.indexOf(toneName));times=perDay;slider.value=Math.max(0,CADENCES.indexOf(days));render();quietFromInputs();}};\r\n}\r\n","reminder-live.mjs":"import {scheduledDay} from './reminder-settings.mjs';\r\nconst formatters=new Map();\r\nfunction localParts(now,timezone){if(!formatters.has(timezone))formatters.set(timezone,new Intl.DateTimeFormat('en-CA',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hourCycle:'h23'}));const parts=Object.fromEntries(formatters.get(timezone).formatToParts(now).map(p=>[p.type,p.value]));return {day:`${parts.year}-${parts.month}-${parts.day}`,time:`${parts.hour}:${parts.minute}`};}\r\nexport function quietReminder(r){const a=r.quiet_start,b=r.quiet_end,t=r.time;return a!==b&&(a<b?t>=a&&t<b:t>=a||t<b);}\r\nexport function nextReminderAt(r,now=Date.now()){\r\n if(!r.enabled||quietReminder(r))return null;\r\n const today=localParts(now,r.timezone).day;\r\n for(let day=0;day<9;day++){\r\n  const date=new Date(Date.parse(today+'T12:00:00Z')+day*86400000).toISOString().slice(0,10);if(!scheduledDay(date,r.days_per_week??7))continue;\r\n  const target=Date.parse(date+'T'+r.time+':00Z');let guess=target;\r\n  for(let i=0;i<3;i++){const local=localParts(guess,r.timezone);guess+=target-Date.parse(local.day+'T'+local.time+':00Z');}\r\n  // Validate the wall time; a spring-forward gap must not become a different hour.\r\n  const candidates=[guess-3600000,guess,guess+3600000].filter(t=>{const p=localParts(t,r.timezone);return p.day===date&&p.time===r.time&&t>now-600000;}).sort((a,b)=>a-b);\r\n  if(candidates.length)return candidates[0];\r\n }\r\n return null;\r\n}\r\nexport function reminderCountdown(time,now=Date.now()){if(time==null)return '';const seconds=Math.ceil((time-now)/1000);if(seconds<=0)return 'Scheduled now';if(seconds<60)return `In ${seconds}s`;if(seconds<3600)return `In ${Math.ceil(seconds/60)} min`;if(seconds<86400)return `In ${Math.floor(seconds/3600)}h ${Math.ceil((seconds%3600)/60)}m`;return `In ${Math.ceil(seconds/86400)} days`;}\r\nexport function mountLiveReminders({refresh,read,getAccount,deviceReady=()=>true}){\r\n const status=document.getElementById('reminderLiveStatus'),panel=document.getElementById('remindersPanel');let rows=[],schedule=[],busy=false,stopped=false,lastRead=0;\r\n function update(items){rows=items;schedule=items.map(r=>({r,next:nextReminderAt(r)}));render();}\r\n function render(){\r\n  if(!panel.open||document.hidden)return;\r\n  const now=Date.now();for(const entry of schedule)if(entry.next!=null&&entry.next<=now-600000)entry.next=nextReminderAt(entry.r,now);\r\n  for(const {r,next}of schedule){const label=document.getElementById('reminder-next-'+r.id);if(label)label.textContent=!r.enabled?'Paused':quietReminder(r)?'This time is inside quiet hours':reminderCountdown(next,now);}\r\n  const account=getAccount(),on=document.getElementById('notificationSwitch').getAttribute('aria-checked')==='true';\r\n  const fresh=account&&Date.now()-account.syncedAt<90000;\r\n  const connected=navigator.onLine&&fresh&&account?.push?.configured&&account?.push?.schedulerActive&&on&&deviceReady();\r\n  status.dataset.state=connected?'live':'waiting';\r\n  const next=schedule.filter(x=>x.next!=null).sort((a,b)=>a.next-b.next)[0];\r\n  status.textContent=!navigator.onLine?'Offline · reminders resume when connected':!account?'Sign in to receive reminders':account.push.environment==='preview'?'Local preview · notifications run in the live app':!fresh?'Checking the reminder connection…':!account.push.configured?'Notification service is unavailable':!account.push.schedulerActive?'Reminder sender is reconnecting':!on?'Turn on notifications for this device':!deviceReady()?'Reconnecting this device…':!rows.some(r=>r.enabled)?'Connected · save or enable a reminder':next?`Live · ${next.r.kind} · ${reminderCountdown(next.next,now)}`:'Connected · choose a time outside quiet hours';\r\n }\r\n async function sync(){if(stopped||busy||document.hidden||!panel.open)return;busy=true;try{await refresh();await read();lastRead=Date.now();}finally{busy=false;render();}}\r\n const timer=setInterval(()=>{render();if(Date.now()-lastRead>=15000)sync();},1000);\r\n window.addEventListener('online',sync);window.addEventListener('offline',render);document.addEventListener('visibilitychange',sync);\r\n window.addEventListener('pagehide',()=>{stopped=true;clearInterval(timer);});\r\n return {update,sync,render};\r\n}\r\n","reminder-plan.mjs":"import {scheduledDay} from './reminder-settings.mjs';\r\nexport const MAX_DAILY_MESSAGES=3;\r\nexport function missedTrainingDays(status,now,daysPerWeek=7){\r\n if(!status||!Number.isSafeInteger(status.startedDay))return 0;\r\n const today=Math.floor(now/86400000),last=Number.isSafeInteger(status.lastCompletedDay)?status.lastCompletedDay:status.startedDay-1;\r\n let missed=0;for(let day=Math.max(last+1,status.startedDay,today-30);day<today;day++)if(scheduledDay(new Date(day*86400000).toISOString().slice(0,10),daysPerWeek))missed++;\r\n return missed;\r\n}\r\nexport function coachReminder(missed,tone='cheeky'){\r\n if(tone==='gentle')return missed?'Your pod is still here. Return with one comfortable set.':'A small session counts. Visit your pod when you’re ready.';\r\n if(missed>=7)return 'MOM is done waiting. Your arsenal is collecting dust. One steady set. Make your comeback.';\r\n if(missed>=3)return 'MOM is disappointed in that empty training log. Report to the pod. One set gets you moving again.';\r\n if(missed>=1)return 'You missed training. MOM noticed. Your next weapon upgrade is still waiting. Report to the pod.';\r\n return 'POD CHECK. Your weapon will not upgrade itself. One steady set. Log your category day.';\r\n}\r\n","reminder-settings.mjs":"export const TONES=['gentle','direct','cheeky'];\r\nexport const CADENCES=[1,3,5,7];\r\nexport const DAYS={1:[1],3:[1,3,5],5:[1,2,3,4,5],7:[0,1,2,3,4,5,6]};\r\nexport const CADENCE_LABELS={1:'Once a week · Monday',3:'3 days a week · Mon, Wed, Fri',5:'Weekdays · Monday–Friday',7:'Every day'};\r\nconst messages={\r\n gentle:{workout:'A little movement, at your pace. MOM is here when you are ready.',meal:'Take a moment to log your meal. One small check-in is enough.',water:'A gentle nudge to take a water break.',motivation:'You do not have to do everything today. One small step counts.'},\r\n direct:{workout:'Time for your workout. Your coach is ready.',meal:'Pause and log your meal.',water:'Time for a water break.',motivation:'A small step counts. Move at your own pace.'},\r\n cheeky:{workout:'MOM has warmed up her encouragement. Your turn to move, darling.',meal:'Your meal has a story. MOM would like the short version. Log it, darling.',water:'Your water is waiting. Very patient. Unlike MOM.',motivation:'MOM believes in you. Embarrassingly much. Time for one small step.'}\r\n};\r\nexport function reminderMessage(kind,tone='direct'){return messages[TONES.includes(tone)?tone:'direct'][kind]||messages.direct.motivation;}\r\nexport function scheduledDay(day,daysPerWeek=7){const weekday=new Date(day+'T12:00:00Z').getUTCDay();return (DAYS[daysPerWeek]||DAYS[7]).includes(weekday);}\r\n// R3-12 times-per-day dial: n evenly spaced reminder times across the awake window (quiet end to quiet start).\r\n// ponytail: 5-minute grid, one daily plan per Save; each time is an ordinary reminder row, so CRUD is unchanged.\r\nconst toMin=t=>{const [h,m]=String(t).split(':').map(Number);return h*60+m;};\r\nconst toTime=m=>{m=((m%1440)+1440)%1440;return String(Math.floor(m/60)).padStart(2,'0')+':'+String(m%60).padStart(2,'0');};\r\nexport function spreadTimes(n,quietStart='22:00',quietEnd='07:00',first='17:00'){\r\n if(!(n>1))return [first];\r\n const a=toMin(quietEnd);let span=toMin(quietStart)-a;if(span<=0)span+=1440;\r\n return Array.from({length:n},(_,i)=>toTime(Math.round((a+(i+.5)*span/n)/5)*5));\r\n}\r\n// Quiet-hours slider position (30 min steps from 12:00 to 12:00) <-> \"HH:MM\".\r\nexport const quietPosToTime=pos=>toTime(720+Number(pos)*30);\r\nexport const quietTimeToPos=t=>Math.min(47,Math.max(0,Math.round((((toMin(t)-720)%1440+1440)%1440)/30)));\r\n// Idempotent multi-row save: one run at a time (double tap), ids minted once per pending plan (a retry re-sends the same ids,\r\n// the server's POST is ON CONFLICT DO NOTHING), and the 16-row cap checked before anything is sent.\r\nexport function createReminderSaver(post){\r\n let busy=false,ids=null;\r\n return {reset(){ids=null;},async save({times,editingId=null,data,existing=0}){\r\n  if(busy)return false;busy=true;\r\n  try{\r\n   if(!editingId&&existing+times.length>16)throw Error('Keep up to 16 reminders. Remove some first.');\r\n   if(!editingId&&ids?.length!==times.length)ids=times.map(()=>crypto.randomUUID());\r\n   for(const [k,time] of times.entries())await post(editingId,{...data,time,id:editingId||ids[k]});\r\n   ids=null;return true;\r\n  }finally{busy=false;}\r\n }};\r\n}\r\n","rest-boss-rewards.mjs":"﻿import {performanceOwner,localDay} from './performance-progress.mjs';\r\nimport {grantUnlock,isGranted} from './unlock-ledger.mjs';\r\nexport const REST_BOSS_REWARDS_KEY='myr5-rest-boss-rewards-v1';\r\nexport const REST_BOSS_PACKS=Object.freeze([{count:1,tier:'uncommon'},{count:5,tier:'legendary'}]);\r\nexport function captureRestBossContext({storage=globalThis.localStorage,account=globalThis.myr5AuthenticatedAccount??null,day=localDay()}={}){\r\n const captured=account?structuredClone(account):null;return {storage,account:captured,owner:performanceOwner(storage,captured),day};\r\n}\r\nfunction context(options={}){return options.owner?options:captureRestBossContext(options);}\r\nfunction key(c){return `${REST_BOSS_REWARDS_KEY}/${encodeURIComponent(c.owner)}`;}\r\nfunction read(c){try{const value=JSON.parse(c.storage.getItem(key(c))||'{}');return value?.version===1&&value.days&&typeof value.days==='object'?value:{version:1,days:{}};}catch{return {version:1,days:{}};}}\r\nfunction dayIds(data,day){const values=data.days[day];return Array.isArray(values)?[...new Set(values.filter(x=>typeof x==='string'&&x.length<=256))]:[];}\r\nexport function restBossPackId(day,tier){return `reward-pack:${tier}:rest-boss-v1:${day}`;}\r\nexport function hasRestBossDefeat(id,options={}){const c=context(options),data=read(c);return Object.keys(data.days).some(day=>dayIds(data,day).includes(id));}\r\nexport function readRestBossRewards(options={}){const c=context(options),ids=dayIds(read(c),c.day);return {owner:c.owner,day:c.day,count:ids.length,defeatIds:ids,packs:REST_BOSS_PACKS.map(p=>({...p,id:restBossPackId(c.day,p.tier),earned:ids.length>=p.count,granted:isGranted('reward-pack',restBossPackId(c.day,p.tier),{account:c.account})}))};}\r\n/** Captured owner and day are passed by the actual rest encounter. Replaying an\r\n * encounter repairs a missing grant, but never counts the defeat again. */\r\nexport function recordRestBossDefeat(defeatId,options={}){\r\n if(typeof defeatId!=='string'||!defeatId||defeatId.length>256)throw RangeError('Invalid rest boss encounter.');\r\n const c=context(options);if(!/^\\d{4}-\\d{2}-\\d{2}$/.test(c.day))throw RangeError('Invalid rest boss day.');\r\n const data=read(c),ids=dayIds(data,c.day),already=Object.keys(data.days).some(day=>dayIds(data,day).includes(defeatId));if(!already){ids.push(defeatId);data.days[c.day]=ids;try{c.storage.setItem(key(c),JSON.stringify(data));}catch{return {recorded:false,granted:[],...readRestBossRewards(c)};}}\r\n const granted=[];for(const p of REST_BOSS_PACKS)if(ids.length>=p.count){const id=restBossPackId(c.day,p.tier);if(grantUnlock('reward-pack',id,{account:c.account}))granted.push({kind:'reward-pack',id,tier:p.tier});}\r\n const result={...readRestBossRewards(c),recorded:!already,granted};if((!already||granted.length)&&typeof window!=='undefined')window.dispatchEvent(new CustomEvent('myr5:rest-boss-rewards',{detail:result}));return result;\r\n}\r\n\r\nexport function recordRestBossDefeatExclusive(id,options={}){const c=context(options),locks=globalThis.navigator?.locks;return locks?.request?locks.request(key(c),()=>recordRestBossDefeat(id,c)):Promise.resolve(recordRestBossDefeat(id,c));}\r\n","reward-pack-ui.mjs":"import {COACHES} from './performance-catalog.mjs';\r\nimport PALETTES from './creature/source/creator/palettes.json' with {type:'json'};\r\nimport {openRewardPackExclusive,unopenedPacks,PACK_SIZES,packSize} from './reward-packs.mjs';\r\n\r\n// The pack is just a tile: a 64x64 pixel tile in its tier colour, then the item it awarded. Both popups are\r\n// modal <dialog> (top layer) so the portal's background inerting never swallows it, and it presents itself\r\n// once per pack per page load when one is waiting; the small launcher button reopens packs left for Later.\r\nconst CSS=`\r\n.reward-pack-launch[hidden]{display:none}\r\n.reward-pack-launch{position:fixed;right:16px;bottom:calc(var(--myr5-bar,0px) + 18px + env(safe-area-inset-bottom,0px));z-index:9000;display:flex;align-items:center;gap:8px;border:2px solid var(--pack);border-radius:12px;background:#21182f;color:#fff4bd;padding:6px 12px 6px 6px;font:800 14px system-ui;box-shadow:0 8px 30px #0009;cursor:pointer}\r\n.reward-pack-launch canvas{width:32px;height:32px;image-rendering:pixelated}\r\n.reward-pack-dialog{width:min(calc(100vw - 32px),380px);max-height:calc(100dvh - 32px);margin:auto;padding:0;border:0;background:none;color:#fff7df;font:500 16px/1.4 system-ui;overflow:visible}\r\n.reward-pack-dialog::backdrop{background:#090717e8}\r\n.reward-pack-card{overflow:auto;max-height:calc(100dvh - 32px);box-sizing:border-box;text-align:center;border:2px solid var(--pack);border-radius:20px;background:radial-gradient(circle at 50% 30%,color-mix(in srgb,var(--pack) 26%,#21152e),#13101e 73%);box-shadow:0 0 55px color-mix(in srgb,var(--pack) 35%,transparent);padding:18px 18px 20px}\r\n.reward-pack-card h2{margin:4px 0 0;font:900 24px/1.1 system-ui;text-transform:uppercase;letter-spacing:.04em}.reward-pack-card .eyebrow{margin:0;color:var(--pack);font:800 12px system-ui;letter-spacing:.2em;text-transform:uppercase}\r\n.reward-pack-tile{display:block;margin:14px auto 10px;width:min(56vw,192px);height:min(56vw,192px);image-rendering:pixelated;filter:drop-shadow(0 10px 8px #000a)}\r\n.reward-pack-dialog.opened .reward-pack-tile{animation:pack-flip .45s ease-out}\r\n.reward-pack-result{min-height:64px;margin:0 0 14px}.reward-pack-result strong{display:block;color:var(--pack);font-size:22px;line-height:1.2}.reward-pack-result small{display:block;opacity:.85;text-transform:uppercase;letter-spacing:.12em;font-size:12px;margin-top:4px}\r\n.reward-pack-swatches{display:flex;justify-content:center;gap:6px;margin-top:8px}.reward-pack-swatches i{width:26px;height:26px;border:2px solid #fff7df;border-radius:4px}\r\n.reward-pack-actions{display:flex;gap:9px;justify-content:center}.reward-pack-actions button{min-height:44px;border:2px solid var(--pack);border-radius:12px;background:var(--pack);color:#15101c;font:800 15px system-ui;padding:8px 18px;cursor:pointer}.reward-pack-actions button.secondary{background:transparent;color:#fff7df}\r\n.reward-pack-actions button:focus-visible,.reward-pack-launch:focus-visible{outline:3px solid white;outline-offset:3px}\r\n@keyframes pack-flip{0%{transform:scale(1)}40%{transform:scale(1.12) rotateY(90deg);filter:brightness(2)}100%{transform:scale(1)}}\r\n.reward-pack-launch[data-tier=secret],.reward-pack-dialog[data-tier=secret] .reward-pack-card{border-color:transparent;background:linear-gradient(#21182f,#21182f) padding-box,linear-gradient(120deg,#3ff5ff,#b388ff,#ff4bd8,#ffd36a,#3ff5ff) border-box;background-size:auto,300% 100%;animation:pack-iri 5s linear infinite}\r\n.reward-pack-dialog[data-tier=secret] .reward-pack-card{background:radial-gradient(circle at 50% 30%,#3a2160,#13101e 73%) padding-box,linear-gradient(120deg,#3ff5ff,#b388ff,#ff4bd8,#ffd36a,#3ff5ff) border-box;background-size:auto,300% 100%}\r\n.reward-pack-launch[data-tier=secret] canvas,.reward-pack-dialog[data-tier=secret] .reward-pack-tile{animation:pack-hue 6s linear infinite}\r\n@keyframes pack-iri{to{background-position:0 0,-300% 0}}@keyframes pack-hue{50%{filter:hue-rotate(60deg) drop-shadow(0 10px 8px #000a)}}\r\n@media(prefers-reduced-motion:reduce){.reward-pack-dialog.opened .reward-pack-tile,.reward-pack-launch[data-tier=secret],.reward-pack-dialog[data-tier=secret] .reward-pack-card,.reward-pack-launch[data-tier=secret] canvas,.reward-pack-dialog[data-tier=secret] .reward-pack-tile{animation:none}}\r\n`;\r\nexport const TIER_COLORS=Object.freeze({uncommon:'#76e356',rare:'#4bafff',legendary:'#ff9c36',secret:'#b388ff'});\r\nexport const tierOf=id=>{const tier=String(id).startsWith('reward-pack:')?String(id).split(':')[1]:'uncommon';return Object.hasOwn(TIER_COLORS,tier)?tier:'uncommon';};\r\nconst CATEGORY={color:'Colour palette','64-bit':'64-bit pixel finish',texture:'Texture'};\r\n// What the tile says about an opened pack: the actual item, what kind it is, and its colours when it is a palette.\r\nexport function rewardSummary(opened){\r\n const rewards=opened?.rewards|| (opened?.reward?[opened.reward]:[]);\r\n const reward=rewards[0]||{};\r\n // A single colour (pool items carry `hex`; a hex id is itself one) shows one swatch the way a palette shows its bands.\r\n const single=reward.kind==='color'?[/^#[0-9a-f]{6}$/i.test(reward.id)?reward.id:reward.hex].filter(Boolean):[];\r\n const colors=reward.kind==='palette'?PALETTES.find(item=>item.id===reward.id)?.colors||[]:single;\r\n const detail=reward.kind==='boss-skin'?'64-bit boss skin':reward.kind==='color'?'Colour':reward.kind==='palette'?'Colour palette':CATEGORY[reward.category||opened?.category]||'Cosmetic';\r\n return {title:reward.name||'Collection complete',detail,colors,rewards};\r\n}\r\n// Determine the reveal treatment from the persisted rewards. Secret packs keep\r\n// their established holographic treatment; boss skins with no cosmetic colour\r\n// keep the original pack-tier fallback.\r\nexport function rewardRevealType(opened,fallback='uncommon'){\r\n if(opened?.tier==='secret'||fallback==='secret')return 'secret';\r\n const rewards=opened?.rewards||(opened?.reward?[opened.reward]:[]);\r\n if(rewards.some(reward=>reward.kind==='texture'||reward.category==='texture'))return 'texture';\r\n const colors=rewards.reduce((n,reward)=>n+(reward.kind==='palette'?(PALETTES.find(item=>item.id===reward.id)?.colors?.length||2):reward.kind==='color'?1:0),0);\r\n if(colors>1)return 'multiple-colors';\r\n if(colors===1)return 'single-color';\r\n return 'cosmetic';\r\n}\r\nexport const rewardRevealTier=(opened,fallback='uncommon')=>({texture:'legendary','multiple-colors':'rare','single-color':'uncommon',secret:'secret'})[rewardRevealType(opened,fallback)]||(TIER_COLORS[fallback]?fallback:'uncommon');\r\nconst REWARD_COLORS=Object.freeze({texture:TIER_COLORS.legendary,'multiple-colors':TIER_COLORS.rare,'single-color':TIER_COLORS.uncommon});\r\nconst shade=(hex,amount)=>{const n=parseInt(hex.slice(1),16),c=[n>>16,n>>8&255,n&255].map(v=>Math.max(0,Math.min(255,Math.round(v+amount))));return '#'+c.map(v=>v.toString(16).padStart(2,'0')).join('');};\r\n// A 64x64 bevelled pixel tile in the tier colour. Unopened: a pixel \"?\" glyph. Opened: the awarded palette's\r\n// colours as bands (or a pixel star for skins/textures), so the tile itself shows what came out.\r\nexport function drawTierTile(ctx,tier,{colors=[],opened=false}={}){\r\n const base=TIER_COLORS[tier]||TIER_COLORS.uncommon,rect=(x,y,w,h,color)=>{ctx.fillStyle=color;ctx.fillRect(x,y,w,h);};\r\n ctx.clearRect(0,0,64,64);\r\n rect(0,0,64,64,shade(base,-120));rect(2,2,60,60,shade(base,70));rect(4,4,58,58,shade(base,-70));rect(4,4,56,56,base);\r\n rect(8,8,48,48,shade(base,-40));rect(10,10,44,44,shade(base,-95));\r\n if(opened&&colors.length){const band=Math.floor(44/colors.length);colors.forEach((color,i)=>rect(10,10+i*band,44,i===colors.length-1?44-i*band:band,color));}\r\n else{\r\n  const ink=opened?'#fff7df':shade(base,90),px=(x,y)=>rect(12+x*4,12+y*4,4,4,ink);\r\n  const glyph=opened?['....#.....','....#.....','...###....','#########.','.#######..','..#####...','..##.##...','.##...##..','.#.....#..','..........']:['..#####...','.##...##..','.##...##..','.....##...','....##....','....##....','..........','....##....','....##....','..........'];\r\n  glyph.forEach((row,y)=>[...row].forEach((cell,x)=>{if(cell==='#')px(x,y);}));\r\n }\r\n rect(4,4,56,2,shade(base,110));rect(4,4,2,56,shade(base,110));\r\n // Secret: an iridescent bevel, cyan/violet/magenta/gold pixels round the frame (CSS hue-rotates it on screen).\r\n if(tier==='secret'){const hues=['#3ff5ff','#b388ff','#ff4bd8','#ffd36a'];for(let i=0;i<14;i++){const c=hues[i%4],o=2+i*4;rect(o,2,4,2,c);rect(60,o,2,4,c);rect(62-o-4,60,4,2,hues[(i+2)%4]);rect(2,62-o-4,2,4,hues[(i+2)%4]);}}\r\n}\r\n// What an opened pack unlocked, as the plain items the reveal card (and the dialog's own result area) list.\r\nexport const unlocked=opened=>rewardSummary(opened).rewards.map(reward=>{const item=rewardSummary({reward,category:reward.category}),coach=COACHES.find(entry=>entry.id===reward.coachId)?.label||'your coach',detail=['color','palette','texture'].includes(reward.kind)?`${item.detail} for Original MYR5 and ${coach}`:`${item.detail} for ${coach}`;return {...reward,title:item.title,detail,colors:item.colors};});\r\nexport function mountRewardPacks(){\r\n const style=document.createElement('style');style.textContent=CSS;document.head.append(style);\r\n const launch=document.createElement('button');launch.type='button';launch.className='reward-pack-launch';launch.hidden=true;launch.innerHTML='<canvas width=\"64\" height=\"64\" aria-hidden=\"true\"></canvas><span></span>';document.body.append(launch);\r\n const overlay=document.createElement('dialog');overlay.className='reward-pack-dialog';overlay.setAttribute('aria-labelledby','pack-title');overlay.innerHTML='<section class=\"reward-pack-card\"><p class=\"eyebrow\">MYR5 • REWARD DROP</p><h2 id=\"pack-title\"></h2><canvas class=\"reward-pack-tile\" width=\"64\" height=\"64\" role=\"img\"></canvas><div class=\"reward-pack-result\" role=\"status\" aria-live=\"polite\"></div><div class=\"reward-pack-actions\"><button type=\"button\" data-open>Open pack</button><button type=\"button\" class=\"secondary\" data-close>Later</button></div></section>';document.body.append(overlay);\r\n const tile=overlay.querySelector('.reward-pack-tile'),result=overlay.querySelector('.reward-pack-result'),open=overlay.querySelector('[data-open]'),close=overlay.querySelector('[data-close]');let current=null,presentTimer=0;const presented=new Set();\r\n const available=()=>unopenedPacks();\r\n function update(){\r\n  const ids=available(),count=ids.length;\r\n  launch.hidden=!count||overlay.open;if(launch.hidden)return;\r\n  const tier=tierOf(ids[0]);launch.style.setProperty('--pack',TIER_COLORS[tier]);launch.dataset.tier=tier;drawTierTile(launch.querySelector('canvas').getContext('2d'),tier);\r\n  launch.querySelector('span').textContent=String(count);launch.setAttribute('aria-label',`${count} unopened reward pack${count===1?'':'s'}`);launch.title=launch.getAttribute('aria-label');\r\n  const bar=document.getElementById('coachDock')?.getBoundingClientRect();launch.style.bottom=bar?.height?Math.max(18,innerHeight-bar.top+12)+'px':'';\r\n }\r\n // Present a waiting pack by itself, once per pack per page load, but never over a camera set.\r\n function present(){\r\n  clearTimeout(presentTimer);const id=available().find(id=>!presented.has(id));if(!id||overlay.open)return;\r\n  if(document.body.dataset.tracking==='true'||document.body.dataset.cameraWorkout==='true'){presentTimer=setTimeout(present,1500);return;}\r\n  if(document.querySelector('dialog[open]:not(.reward-pack-dialog)')){presentTimer=setTimeout(present,500);return;}\r\n  // The starter quilt only appears when no dialog is open: let it come up first (up to 5 s after launch).\r\n  if(document.getElementById('portalHome')?.hidden!==false&&performance.now()<5000){presentTimer=setTimeout(present,250);return;}\r\n  presented.add(id);show(id);\r\n }\r\n function hide(){if(overlay.open)overlay.close();}\r\n function select(id){\r\n  current={kind:'reward-pack',id,tier:tierOf(id)};overlay.classList.remove('opened');overlay.style.setProperty('--pack',TIER_COLORS[current.tier]);overlay.dataset.tier=current.tier;\r\n  overlay.querySelector('h2').textContent=`${current.tier} pack`;tile.setAttribute('aria-label',`${current.tier} pack tile`);drawTierTile(tile.getContext('2d'),current.tier);\r\n  {const n=packSize(current);result.textContent=`${n} coach cosmetic${n===1?'':'s'} inside. Tap to open.`;};open.hidden=false;close.textContent='Later';\r\n }\r\n let pods=null;const preload=()=>pods??=import('./drop-pod-opening.mjs').catch(()=>null);\r\n function show(id){select(id);preload();launch.hidden=true;overlay.showModal();open.focus();}\r\n launch.onclick=()=>{const id=available()[0];if(id)show(id);};close.onclick=hide;overlay.addEventListener('close',()=>{current=null;update();});\r\n\r\n function render(opened){\r\n  const summary=rewardSummary(opened),revealType=rewardRevealType(opened,current.tier),revealTier=rewardRevealTier(opened,current.tier),revealColor=REWARD_COLORS[revealType]||TIER_COLORS[current.tier];overlay.dataset.tier=current.tier;overlay.dataset.rewardType=revealType;overlay.style.setProperty('--pack',revealColor);drawTierTile(tile.getContext('2d'),revealTier,{colors:summary.colors,opened:true});tile.setAttribute('aria-label',`${summary.title}, ${summary.detail}`);\r\n  overlay.classList.remove('opened');void tile.offsetWidth;overlay.classList.add('opened');\r\n  result.replaceChildren();\r\n  if(!summary.rewards.length)result.textContent='All cosmetics for your unlocked coaches are collected.';\r\n  for(const item of unlocked(opened)){\r\n   const title=document.createElement('strong'),kind=document.createElement('small');title.textContent=`${item.title} unlocked!`;kind.textContent=item.detail;result.append(title,kind);\r\n   if(item.colors.length){const swatches=document.createElement('div');swatches.className='reward-pack-swatches';for(const color of item.colors){const chip=document.createElement('i');chip.style.background=color;swatches.append(chip);}result.append(swatches);}\r\n  }\r\n  open.hidden=true;close.textContent='Done';close.focus();\r\n }\r\n // Open pack starts the full-screen drop-pod sequence; the pack is opened when the pod is tapped, and Open another moves to the next waiting pack.\r\n const sequence=pending=>{let openedResult=null;return {tier:pending.tier,hasNext:()=>available().some(id=>id!==pending.id),onExit:hide,revealType:()=>rewardRevealType(openedResult,pending.tier),revealTier:()=>rewardRevealTier(openedResult,pending.tier),revealColor:()=>REWARD_COLORS[rewardRevealType(openedResult,pending.tier)]||TIER_COLORS[pending.tier],\r\n  open:async()=>{const opened=await openRewardPackExclusive(pending);if(!opened)return null;openedResult=opened;if(current===pending)render(opened);return unlocked(opened);},\r\n  onNext:()=>{const id=available().find(id=>id!==pending.id);if(!id)return null;select(id);return sequence(current);}};\r\n };\r\n open.onclick=async()=>{\r\n  if(!current)return;const pending=current,module=await preload();if(current!==pending)return;\r\n  if(module){module.playDropPod(sequence(pending));return;}\r\n  // The sequence module could not load (offline without the update): fall back to the plain tile.\r\n  open.disabled=true;let opened;try{opened=await openRewardPackExclusive(pending);}finally{open.disabled=false;}if(current!==pending)return;if(!opened){result.textContent='Could not save this pack. Try again.';return;}\r\n  render(opened);\r\n };\r\n const onGrant=event=>{if(event.detail?.granted?.some(item=>item.kind==='reward-pack')){update();present();}};\r\n const onReady=event=>{update();present();};\r\n const onCleared=()=>{clearTimeout(presentTimer);hide();update();};\r\n window.addEventListener('myr5:performance-progress',update);window.addEventListener('myr5:battle-pass',onGrant);window.addEventListener('myr5:account-ready',onReady);window.addEventListener('myr5:account-cleared',onCleared);update();\r\n return ()=>{clearTimeout(presentTimer);hide();window.removeEventListener('myr5:performance-progress',update);window.removeEventListener('myr5:battle-pass',onGrant);window.removeEventListener('myr5:account-ready',onReady);window.removeEventListener('myr5:account-cleared',onCleared);launch.remove();overlay.remove();style.remove();};\r\n}\r\n","reward-packs.mjs":"// Packs contain coach-specific cosmetics. Rolls are committed before grants so a\r\n// retry repairs interrupted storage without rerolling or duplicating rewards.\r\nimport {colourRewardPool,textureRewardPool} from './battle-pass-rewards.mjs';\r\nimport * as ledger from './unlock-ledger.mjs';\r\nimport * as store from './creature/source/creator/unlock-store.ts';\r\nimport {unlockedCoachIds,performanceOwner,coachAccess} from './performance-progress.mjs';\r\nimport {PACK_SIZES,COSMETIC_PACK_ODDS} from './progression-rules.mjs';\r\nexport const PACK_ODDS=COSMETIC_PACK_ODDS;\r\nexport {PACK_SIZES};\r\n// Achievement Vault packs (id `reward-pack:secret:vault-<goal>`) are holographic secret packs with no 64-bit items (colour 75 / texture 25).\r\n// Size follows the goal's tier (legendary 3, rare 2; its first slot is then a texture). vault-store notes the goal tier at grant time so the\r\n// core never imports the vault goal list.\r\nexport const VAULT_PACK=Object.freeze({rare:2,legendary:3,odds:Object.freeze({secret:Object.freeze({color:75,'64-bit':0})})});\r\nexport const isVaultPack=item=>String(item?.id).includes(':vault-');\r\nconst VT_KEY='myr5-vault-pack-tier-v1';\r\nexport const noteVaultPack=(id,tier)=>{try{const d=JSON.parse(localStorage.getItem(VT_KEY)||'{}');if(d[id]!==tier){d[id]=tier;localStorage.setItem(VT_KEY,JSON.stringify(d));}}catch{/* private mode */}};\r\nconst vaultSize=id=>{try{return VAULT_PACK[JSON.parse(localStorage.getItem(VT_KEY)||'{}')[id]]||VAULT_PACK.rare;}catch{return VAULT_PACK.rare;}};\r\nexport const packSize=item=>isVaultPack(item)?vaultSize(item.id):PACK_SIZES[item.tier];\r\nconst KEY='myr5-opened-reward-packs-v2';\r\nconst ownerKey=()=>`${KEY}/${encodeURIComponent(performanceOwner())}`;\r\nconst safeRead=()=>{try{const data=JSON.parse(localStorage.getItem(ownerKey())||'{}');return data&&typeof data==='object'&&!Array.isArray(data)?data:{};}catch{return {};}};\r\nconst save=data=>{try{localStorage.setItem(ownerKey(),JSON.stringify(data));return true;}catch{return false;}};\r\nexport const packItem=(tier,id)=>({kind:'reward-pack',id,name:`${tier[0].toUpperCase()+tier.slice(1)} Pack`,tier,line:`Open for ${packSize({id,tier})} coach cosmetic${packSize({id,tier})===1?'':'s'}.`});\r\nconst unit=random=>{const value=random();if(!Number.isFinite(value))throw RangeError('Invalid random value.');return Math.min(1-Number.EPSILON,Math.max(0,value));};\r\nexport function rollCategory(tier,random=Math.random,table=PACK_ODDS){\r\n const odds=table[tier];if(!odds)throw Error('Unknown pack tier');\r\n const roll=unit(random)*100;\r\n return roll<odds.color?'color':roll<odds.color+odds['64-bit']?'64-bit':'texture';\r\n}\r\nconst pools={color:colourRewardPool(),texture:textureRewardPool()};\r\nexport function setTexturePool(items){pools.texture=items.filter(item=>item.kind==='texture').map(({kind,id,name})=>({kind,id,name}));}\r\nconst identity=reward=>store.cosmeticId(reward.coachId,reward.id);\r\nexport const hasCosmetic=reward=>reward.kind==='boss-skin'?ledger.isGranted('boss-skin',identity(reward)):store.isGranted(reward.kind,reward.id,reward.coachId);\r\nconst categoryPool=(category,coaches)=>coaches.flatMap(coachId=>category==='64-bit'?[{kind:'boss-skin',id:`${coachId}-skin`,name:'64-bit Pixel Finish',coachId}]:pools[category].map(item=>({...item,coachId})));\r\nexport function remainingCosmetics({coaches=unlockedCoachIds()}={}){\r\n const owned=[...new Set(coaches)].filter(coach=>coachAccess(coach));\r\n const grants=Object.fromEntries(['color','palette','texture'].map(kind=>[kind,new Set(store.ownerGrantedIds(kind))]));grants['boss-skin']=new Set(ledger.grantedIds('boss-skin'));\r\n return ['color','64-bit','texture'].flatMap(category=>categoryPool(category,owned).map(reward=>({...reward,category}))).filter(reward=>!grants[reward.kind].has(identity(reward)));\r\n}\r\nconst rewardsOf=opened=>opened?.rewards|| (opened?.reward?[{...opened.reward,category:opened.category}]:[]);\r\nexport const openedPack=id=>{\r\n const current=safeRead();if(current[id])return current[id];\r\n // Earlier one-item packs keep their saved roll; migrate it onto the saved coach.\r\n try{const owner=globalThis.myr5AuthenticatedAccount?.user?.id;if(!owner)return null;const legacy=JSON.parse(localStorage.getItem(`myr5-opened-reward-packs-v1/${owner}`)||'{}')[id];if(!legacy?.reward)return null;const preferred=store.currentCosmeticCoach(),coaches=unlockedCoachIds(),coachId=coaches.includes(preferred)?preferred:coaches[0];if(!coachId)return null;const reward={...legacy.reward,...(legacy.reward.kind==='boss-skin'?{id:`${coachId}-skin`}:{}),coachId,category:legacy.category};const result={...legacy,reward,rewards:[reward],legacy:true};return save({...current,[id]:result})?result:null;}catch{return null;}\r\n};\r\nexport const unopenedPacks=()=>ledger.grantedIds('reward-pack').filter(id=>{const opened=openedPack(id);return !opened||rewardsOf(opened).some(reward=>!hasCosmetic(reward));});\r\nfunction grantReward(reward){\r\n if(!coachAccess(reward.coachId))return false;\r\n if(reward.kind==='boss-skin')ledger.grantUnlock(reward.kind,identity(reward));\r\n else store.grantUnlock(reward.kind,reward.id,reward.coachId);\r\n return hasCosmetic(reward);\r\n}\r\nexport function openRewardPack(item,{random=Math.random}={}){\r\n if(item?.kind!=='reward-pack'||!PACK_ODDS[item.tier]||!ledger.isGranted('reward-pack',item.id))return null;\r\n const previous=openedPack(item.id);\r\n if(previous)return rewardsOf(previous).every(grantReward)?previous:null;\r\n const coaches=unlockedCoachIds().filter(coach=>coachAccess(coach));if(!coaches.length)return null;\r\n const rewards=[];const selected=new Set(),unowned=remainingCosmetics({coaches});\r\n const vault=isVaultPack(item);\r\n for(let slot=0;slot<packSize(item);slot++){\r\n  const rolled=vault&&vaultSize(item.id)===VAULT_PACK.legendary&&slot===0?'texture':rollCategory(item.tier,random,vault?VAULT_PACK.odds:PACK_ODDS);\r\n  // Preserve category odds until that category is exhausted; then award another\r\n  // unowned category rather than a duplicate while the collection is incomplete.\r\n  const order=[rolled,...['color','64-bit','texture'].filter(category=>category!==rolled&&!(vault&&category==='64-bit'))];\r\n  let category=rolled,choices=[];\r\n  for(const candidate of order){const available=unowned.filter(reward=>reward.category===candidate&&!selected.has(identity(reward)+':'+reward.kind));if(available.length){category=candidate;choices=available;break;}}\r\n  if(!choices.length)break; // Complete collection: do not manufacture duplicates.\r\n  const reward={...choices[Math.floor(unit(random)*choices.length)],category};\r\n  selected.add(identity(reward)+':'+reward.kind);rewards.push(reward);\r\n  // A non-original coach's material reward also unlocks that material on MYR5.\r\n  // Reserve the inherited MYR5 slot for this pack so it cannot roll twice.\r\n  if(reward.coachId!=='myr5'&&reward.kind!=='boss-skin')selected.add(store.cosmeticId('myr5',reward.id)+':'+reward.kind);\r\n }\r\n const result={tier:item.tier,rewards,category:rewards[0]?.category||'complete',reward:rewards[0]||null,complete:rewards.length===0};\r\n const current=safeRead();if(current[item.id])return current[item.id];\r\n if(!save({...current,[item.id]:result}))return null;\r\n return rewards.every(grantReward)?result:null;\r\n}\r\n// Cross-tab UI openings serialize the saved roll and grant repair when Web Locks\r\n// are available. The synchronous primitive also remains useful offline/in tests.\r\nexport async function openRewardPackExclusive(item,options={}){\r\n try{const owner=performanceOwner(),run=()=>performanceOwner()===owner?openRewardPack(item,options):null;\r\n return await (globalThis.navigator?.locks?.request?globalThis.navigator.locks.request(`${KEY}/${owner}/${item?.id}`,run):run());}catch{return null;}\r\n}\r\n// The final cosmetic level is a collection completion reward. Only currently\r\n// unlocked coaches qualify; coaches earned later remain collectible afterwards.\r\nexport function completeCosmeticCollection(){\r\n const remaining=remainingCosmetics();\r\n const materials=remaining.filter(reward=>reward.kind!=='boss-skin'),skins=remaining.filter(reward=>reward.kind==='boss-skin');\r\n store.grantUnlocks(materials);ledger.grantUnlocks('boss-skin',skins.map(identity));\r\n const ungranted=remainingCosmetics().length;return {granted:remaining.length-ungranted,remaining:ungranted};\r\n}\r\nexport function grantDailyPack({date=new Date(),account=globalThis.myr5AuthenticatedAccount}={}){\r\n // Legacy callers stay harmless. Existing earned packs remain owned.\r\n return null;\r\n}\r\n","robot-audio.mjs":"// Every spoken cue uses the approved voice pack, including compound sentences.\r\nimport {BUILD_ID} from './release-build.mjs';\r\nimport {coachFetch,coachOnline} from './coach-net.mjs';\r\nexport const VOICE_MANIFEST='/voice/manifest.json?v='+BUILD_ID;\r\nexport const VOICE_CACHE='myr5-voice-approved-v2';\r\n// Local playback treatments of approved clips; no OS voice or network speech service.\r\nexport const COACH_VOICE_VARIANTS=Object.freeze({\r\n robot:{label:'Robot',rate:1,detune:0,filter:null},\r\n clear:{label:'Clear',rate:1.04,detune:0,filter:'clear'},\r\n warm:{label:'Warm',rate:.96,detune:-250,filter:'warm'},\r\n light:{label:'Light',rate:1.10,detune:250,filter:'light'}\r\n});\r\nexport const DEFAULT_COACH_VOICE='robot';\r\nexport function voicePhrases(text,phrases){\r\n text=text.trim();if(phrases[text])return [text];\r\n const parts=(text.match(/[^.!?]+[.!?]+(?:\\s|$)|[^.!?]+$/g)||[]).map(s=>s.trim()).filter(Boolean);\r\n return parts.length&&parts.every(s=>phrases[s])?parts:[];\r\n}\r\nexport class RobotAudio {\r\n constructor(onMode=()=>{}){this.onMode=onMode;this.context=null;this.cache=new Map();this.cacheBytes=0;this.current=null;this.generation=0;this.variant=DEFAULT_COACH_VOICE;}\r\n setVariant(id){if(!COACH_VOICE_VARIANTS[id])return false;this.variant=id;return true;}\r\n get mode(){return COACH_VOICE_VARIANTS[this.variant]?.label??COACH_VOICE_VARIANTS[DEFAULT_COACH_VOICE].label;}\r\n unlock(){const Context=globalThis.AudioContext||globalThis.webkitAudioContext;if(!Context)return false;try{this.context??=new Context();if(this.context.state==='suspended')this.context.resume().catch(()=>{});return true;}catch{return false;}}\r\n stop(){this.generation++;const current=this.current;this.current=null;if(current){current.abort?.abort();try{current.source?.stop();}catch{}current.finish?.();}this.onMode('Voice: '+this.mode);}\r\n async play(text){\r\n  if(!this.unlock())return false;\r\n  const run=++this.generation,abort=new AbortController();this.current={abort};\r\n  try{\r\n   const got=this.manifest?null:await coachFetch(VOICE_MANIFEST,{signal:abort.signal},{timeoutMs:5000,local:true});\r\n   if(got){if(!got.ok)throw Error('Voice pack unavailable');this.manifest=got.data;}\r\n   const manifest=await this.manifest,parts=voicePhrases(text,manifest.phrases);\r\n   if(!parts.length)throw Error('No approved clip');\r\n   for(const phrase of parts){\r\n    if(run!==this.generation)return true;\r\n    let buffer=this.cache.get(phrase);\r\n    if(!buffer){\r\n     const clip=await coachFetch(manifest.phrases[phrase],{signal:abort.signal},{timeoutMs:5000,parse:'arrayBuffer',local:true});\r\n     if(!clip.ok)throw Error('Voice unavailable');const data=clip.data;if(run!==this.generation)return true;\r\n     buffer=await this.context.decodeAudioData(data);if(run!==this.generation)return true;\r\n     const bytes=buffer.length*buffer.numberOfChannels*4;\r\n     while(this.cache.size&&this.cacheBytes+bytes>12*1024*1024){const key=this.cache.keys().next().value,old=this.cache.get(key);this.cacheBytes-=old.length*old.numberOfChannels*4;this.cache.delete(key);}\r\n     this.cache.set(phrase,buffer);this.cacheBytes+=bytes;\r\n    }\r\n    if(this.context.state!=='running'){this.onMode('Tap Test voice to enable sound');this.current=null;return false;}\r\n     const profile=COACH_VOICE_VARIANTS[this.variant]??COACH_VOICE_VARIANTS[DEFAULT_COACH_VOICE];\r\n     const source=this.context.createBufferSource();source.buffer=buffer;if(source.playbackRate)source.playbackRate.value=profile.rate;if(source.detune)source.detune.value=profile.detune;\r\n     const nodes=this.makeVoiceChain(profile),chain=[source,...nodes,this.context.destination];\r\n     for(let i=0;i<chain.length-1;i++)chain[i].connect(chain[i+1]);\r\n     this.onMode('Voice: '+profile.label);\r\n     await new Promise(resolve=>{let done=false;const finish=()=>{if(done)return;done=true;source.disconnect();nodes.forEach(node=>node.disconnect());if(run===this.generation)this.current={abort};resolve();};this.current={source,abort,finish};source.onended=finish;source.start();});\r\n   }\r\n   if(run===this.generation)this.current=null;return true;\r\n  }catch{if(run===this.generation){this.current=null;this.onMode(coachOnline()?'Voice unavailable · captions on':'Offline · captions on, voice returns when you reconnect');return false;}return true;}\r\n }\r\n makeVoiceChain(profile){\r\n  const ctx=this.context,chain=[];\r\n  if(!profile.filter)return chain;\r\n  if(typeof ctx.createBiquadFilter!=='function')return chain;\r\n  const filter=(type,frequency,gain=0,q=0.7)=>{const node=ctx.createBiquadFilter();node.type=type;node.frequency.value=frequency;node.Q.value=q;if(node.gain)node.gain.value=gain;chain.push(node);};\r\n  if(profile.filter==='robot'){\r\n   filter('bandpass',1450,0,0.75);\r\n   if(ctx.createWaveShaper){const shaper=ctx.createWaveShaper();shaper.curve=Float32Array.from({length:257},(_,i)=>{const x=i/128-1;return Math.tanh(2.2*x);});shaper.oversample='2x';chain.push(shaper);}\r\n  }else if(profile.filter==='warm'){\r\n   filter('lowshelf',220,3.5);filter('highshelf',3600,-2.5);\r\n  }else if(profile.filter==='light'){\r\n   filter('highpass',115);filter('highshelf',2400,3);\r\n  }else{\r\n   filter('highpass',75);filter('peaking',2800,1.5,0.8);\r\n  }\r\n  return chain;\r\n }\r\n}\r\n","robot-speech.cjs":"// Local formant speech. No model download, cloud service, microphone, or GPU.\r\nconst {execFile}=require('node:child_process');\r\nconst fs=require('node:fs');\r\nconst ENGINE='C:/Program Files/eSpeak NG/espeak-ng.exe';\r\n// Keep the synthetic timbre, but retain sentence stress and avoid choppy word gaps.\r\nconst PROFILE={voice:'en-us',wordsPerMinute:125,pitch:52,pitchRange:38,wordGap:1};\r\nconst cache=new Map(),pending=new Map();let cacheBytes=0;\r\nconst MAX_CACHE=12*1024*1024;\r\nfunction synthesize(text){\r\n if(typeof text!=='string'||!text.trim()||text.length>600||/[\\x00-\\x08\\x0b-\\x1f]/.test(text))return Promise.reject(new Error('Invalid speech text'));\r\n text=text.trim();if(cache.has(text))return Promise.resolve(cache.get(text));if(pending.has(text))return pending.get(text);\r\n if(pending.size>=4)return Promise.reject(new Error('Speech busy'));\r\n const task=new Promise((resolve,reject)=>{\r\n  // Arguments are passed without a shell. Text is never interpreted as markup.\r\n  const args=['--stdout','-b','1','-v',PROFILE.voice,'-s',String(PROFILE.wordsPerMinute),'-p',String(PROFILE.pitch),'-P',String(PROFILE.pitchRange),'-g',String(PROFILE.wordGap),'--stdin'];\r\n  const child=execFile(ENGINE,args,{encoding:'buffer',windowsHide:true,timeout:5000,maxBuffer:5*1024*1024},(error,wav)=>{\r\n   if(error)return reject(new Error('Robot voice unavailable'));\r\n   if(wav.length<44||wav.toString('ascii',0,4)!=='RIFF')return reject(new Error('Invalid speech audio'));\r\n   // eSpeak's stdout uses streaming placeholder sizes; give phone decoders exact sizes.\r\n   wav.writeUInt32LE(wav.length-8,4);let found=false;\r\n   for(let offset=12;offset+8<=wav.length;){const size=wav.readUInt32LE(offset+4);if(wav.toString('ascii',offset,offset+4)==='data'){wav.writeUInt32LE(wav.length-offset-8,offset+4);found=true;break;}offset+=8+size+(size%2);}\r\n   if(!found)return reject(new Error('Missing speech samples'));\r\n   while(cache.size&&cacheBytes+wav.length>MAX_CACHE){const key=cache.keys().next().value;cacheBytes-=cache.get(key).length;cache.delete(key);}\r\n   cache.set(text,wav);cacheBytes+=wav.length;resolve(wav);\r\n  });\r\n  child.stdin.on('error',()=>{});child.stdin.end(text,'utf8');\r\n }).finally(()=>pending.delete(text));\r\n pending.set(text,task);return task;\r\n}\r\nmodule.exports={synthesize,PROFILE,available:()=>fs.existsSync(ENGINE)};\r\n","roguelite-run.html":"<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><meta name=\"theme-color\" content=\"#101b20\"><title>MYR5 — Field Run</title>\n<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script>\n<link rel=\"stylesheet\" href=\"/modules/roguelite/run-view.css\"><link rel=\"stylesheet\" href=\"/modules/roguelite/combat-view.css\"></head>\n<body><main id=\"run-stage\">\n <canvas id=\"world\" aria-label=\"Coach training biome\"></canvas>\n <div class=\"vignette\"></div>\n <header><span class=\"brand\">MOM INC. <b>FIELD TRIAL</b></span><span id=\"room-label\">LOCAL RUN PREVIEW</span><button id=\"pause\" aria-label=\"Pause run\">Ⅱ</button><button id=\"tutorial-toggle\" aria-pressed=\"true\">Tutorials on</button></header>\n <video id=\"camera-preview\" autoplay playsinline muted hidden aria-label=\"Your movement camera\"></video>\n <section id=\"opening\"><span class=\"eyebrow\">YOUR BODY IS THE CONTROLLER</span><h1>ONE MORE<br><em>ROOM.</em></h1><p>Train with the coaches. Build your run.<br>Break MOM’s hold.</p><label class=\"combat-choice\" for=\"combat-class\">WEAPON STYLE <select id=\"combat-class\"><option value=\"knight\">Lance and wall shield</option><option value=\"monk\" selected>Unarmed - speed and movement</option><option value=\"dual\">Paired blades - parry and counter</option><option value=\"mage\">Magic focus - shapes and spells</option></select></label><p id=\"class-help\">Attack behind your shield at half speed. Move aside for red attacks.</p><div class=\"start-actions\"><button id=\"begin\">START CAMERA + RUN →</button><button id=\"resume\" hidden>RESUME RUN ↗</button></div><small id=\"opening-note\">Local build. Rewards stay in this preview</small><a id=\"signin\" href=\"/signin.html?return_to=%2Froguelite-run.html%3FworldRun%3D1\" hidden>Sign in to start an authenticated run.</a></section>\n <section id=\"hud\" hidden><div class=\"run-progress\"><span id=\"level\">LEVEL 1</span><progress id=\"xp\" max=\"100\" value=\"0\" aria-label=\"Run XP\"></progress><span id=\"xp-label\">0 XP</span><span id=\"world-energy\" hidden>SAVED STAMINA: 0s</span></div><div id=\"stats\">♥ 100　✊ 10　✦ 0</div><div class=\"coach-tag\"><span id=\"biome-label\"></span><strong id=\"coach-name\"></strong></div><div id=\"objective\"><span id=\"verb\"></span><strong id=\"goal\"></strong><span id=\"cue\"></span></div><div id=\"motion-arrow\" aria-hidden=\"true\">↕</div><div id=\"upgrade\" hidden><h2>MAKE YOUR NEXT ROOM EASIER</h2><div class=\"upgrade-options\"><div data-choice=\"left\"><span>←</span><b>♥</b><strong>HEALTH</strong><small>Stand left</small></div><div data-choice=\"crouch\"><span>↓</span><b>✦</b><strong>LUCK</strong><small>Crouch</small></div><div data-choice=\"right\"><span>→</span><b>✊</b><strong>DAMAGE</strong><small>Stand right</small></div></div></div><canvas id=\"held-weapon\" hidden aria-label=\"Equipped weapon\"></canvas><canvas id=\"held-shield\" hidden aria-label=\"Equipped shield\"></canvas><div id=\"run-message\" role=\"status\" aria-live=\"polite\">Step into view.</div></section><button id=\"end-run\" style=\"position:absolute;right:18px;bottom:18px;z-index:45\" hidden>END RUN</button>\n <section id=\"summary\" hidden><span class=\"eyebrow\">FIELD TRIAL COMPLETE</span><h1>STILL<br><em>STANDING.</em></h1><p id=\"summary-text\"></p><button id=\"another\">BACK TO START</button><a id=\"return-base\" href=\"/pose\" hidden>RETURN TO BASE</a></section>\n <button id=\"room-look\" hidden>ENABLE PHONE LOOK</button><button id=\"camera-retry\" hidden>RETRY CAMERA</button>\n <aside id=\"error\" role=\"alert\" hidden></aside>\n</main><script type=\"module\" src=\"/modules/roguelite/run-view.mjs\"></script></body></html>\n","rope.mjs":"// Tiny verlet rope for the FULL workout power cables. Pure maths, no DOM.\r\n// TUNING KNOBS -- real phones need tuning; change only here.\r\nexport const ROPE={\r\n segments:14,        // points per cable\r\n gravityScale:2600,  // px/s^2 at a full 1.0 gravity vector\r\n damping:.97,        // velocity kept per step (lower = heavier air)\r\n iterations:8,       // constraint passes per step\r\n stiffness:.9,       // 0..1 how hard each pass pulls segments back to length\r\n sway:.07,           // idle sway (fraction of gravity) when no tilt reading exists\r\n swayHz:.45,\r\n fanDeg:8,           // loose cables fan out in two rings, this many degrees apart, from straight down toward the left\r\n looseLength:100,     // px of a loose cable\r\n looseStagger:70,    // extra px on every other cable so plugs do not stack\r\n plugSlack:1.06,     // plugged cable length = distance * this (it sags under gravity)\r\n slackSpread:.025,   // extra slack per cable (i%5) so the bundle in the gutter fans out a little\r\n gutter:64,          // px of the right-hand cord channel plugged cables live in\r\n settleSteps:90,     // steps run at once for the static (reduced motion) pose\r\n};\r\n\r\nexport function makeRope(ax,ay,length,n=ROPE.segments){\r\n return {length,pts:Array.from({length:n},(_,i)=>{const y=ay+length*i/(n-1);return {x:ax,y,px:ax,py:y};})};\r\n}\r\n\r\n/** One step. g = {gx,gy} (unit-ish, +y down). anchor pinned; end pinned when given (plugged / dragged). */\r\nexport function stepRope(rope,dt,g,{anchor,end=null,length=rope.length}={},knobs=ROPE){\r\n const p=rope.pts,n=p.length;rope.length=length;const seg=length/(n-1),ax=g.gx*knobs.gravityScale*dt*dt,ay=g.gy*knobs.gravityScale*dt*dt;\r\n for(let i=1;i<n;i++){const q=p[i],vx=(q.x-q.px)*knobs.damping,vy=(q.y-q.py)*knobs.damping;q.px=q.x;q.py=q.y;q.x+=vx+ax;q.y+=vy+ay;}\r\n const pin=()=>{Object.assign(p[0],{x:anchor.x,y:anchor.y,px:anchor.x,py:anchor.y});if(end)Object.assign(p[n-1],{x:end.x,y:end.y,px:end.x,py:end.y});};\r\n pin();\r\n for(let it=0;it<knobs.iterations;it++){\r\n  for(let i=0;i<n-1;i++){\r\n   const a=p[i],b=p[i+1],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1e-6,k=(d-seg)/d*knobs.stiffness;\r\n   const wa=i===0?0:1,wb=i===n-2&&end?0:1,w=wa+wb;if(!w)continue;\r\n   a.x+=dx*k*wa/w;a.y+=dy*k*wa/w;b.x-=dx*k*wb/w;b.y-=dy*k*wb/w;\r\n  }\r\n  pin();\r\n }\r\n return p;\r\n}\r\n\r\n/** Rotate a gravity vector by deg degrees (per-cable fan). */\r\nexport const rotate=(g,deg)=>{const a=deg*Math.PI/180,c=Math.cos(a),s=Math.sin(a);return {gx:g.gx*c-g.gy*s,gy:g.gx*s+g.gy*c};};\r\n\r\n/** Smooth SVG path through the points (quadratic midpoints). */\r\nexport function ropePath(pts){\r\n let d=`M${pts[0].x.toFixed(1)} ${pts[0].y.toFixed(1)}`;\r\n for(let i=1;i<pts.length-1;i++){const m={x:(pts[i].x+pts[i+1].x)/2,y:(pts[i].y+pts[i+1].y)/2};d+=`Q${pts[i].x.toFixed(1)} ${pts[i].y.toFixed(1)} ${m.x.toFixed(1)} ${m.y.toFixed(1)}`;}\r\n const l=pts[pts.length-1];return d+`L${l.x.toFixed(1)} ${l.y.toFixed(1)}`;\r\n}\r\n","scheduler/service.mjs":"import {runReminders,subscriptionInput} from '../server/push.mjs';\r\nimport {reminderInput,fail} from '../server/domain.mjs';\r\nimport {emailSubscription,emailLinkAction,runReleaseEmails} from '../server/release-email.mjs';\r\nimport {syncTrainingStatus} from '../server/reminder-plan.mjs';\r\nimport {runReleasePush} from '../server/release-push.mjs';\r\nimport {epochFencedBatch,reconcileRemoteEpoch} from '../server/remote-epochs.mjs';\r\nimport {remoteAccountDeletionStatements} from '../server/remote-deletion-data.mjs';\r\nimport {remoteAccountOperation} from '../server/remote-account-operations.mjs';\r\nimport {validatedReminderProof} from '../server/remote-reminders.mjs';\r\n\r\nconst json=(data,status=200)=>new Response(JSON.stringify(data),{status,headers:{'Content-Type':'application/json','Cache-Control':'no-store'}});\r\nasync function authorized(request,env){\r\n const token=request.headers.get('Authorization')||'';if(!env.REMINDER_SERVICE_TOKEN||token.length>200)return false;\r\n const hash=value=>crypto.subtle.digest('SHA-256',new TextEncoder().encode(value));\r\n const [a,b]=await Promise.all([hash(token),hash(`Bearer ${env.REMINDER_SERVICE_TOKEN}`)]);\r\n let diff=0;const left=new Uint8Array(a),right=new Uint8Array(b);for(let i=0;i<left.length;i++)diff|=left[i]^right[i];return diff===0;\r\n}\r\nasync function body(request){const text=await request.text();if(text.length>60000)fail('Too much reminder data.',413);try{return JSON.parse(text);}catch{fail('Invalid reminder data.');}}\r\nfunction proofFromHeaders(request,user){\r\n const epoch=request.headers.get('X-Coach-Data-Epoch'),through=request.headers.get('X-Coach-Deleted-Through');\r\n if(epoch===null||through===null)throw Object.assign(Error('Account generation is required.'),{status:428,code:'epoch_required'});\r\n if(!/^[1-9][0-9]*$/.test(epoch)||!/^(0|[1-9][0-9]*)$/.test(through))fail('Invalid account generation.',422);\r\n return validatedReminderProof(user,{ownerId:user,currentDataEpoch:Number(epoch),deletedThroughEpoch:Number(through)});\r\n}\r\nasync function importExisting(request,env,user,proof){\r\n const input=await body(request);if(!Array.isArray(input.reminders)||input.reminders.length>16||!Array.isArray(input.subscriptions)||input.subscriptions.length>32)fail('Invalid reminder import.');\r\n const database=env.DB,key='import:'+JSON.stringify([user,proof.currentDataEpoch]);\r\n const priorKey=proof.currentDataEpoch===1?'import:'+user:key;\r\n if(await database.prepare('SELECT value FROM system WHERE key=? OR key=?').bind(key,priorKey).first())return {imported:true};\r\n const statements=[];\r\n for(const row of input.reminders){\r\n  const r=reminderInput({id:row.id,kind:row.kind,time:row.time,timezone:row.timezone,enabled:!!row.enabled,quietStart:row.quiet_start,quietEnd:row.quiet_end,tone:row.tone,daysPerWeek:row.days_per_week});\r\n  statements.push(database.prepare('INSERT INTO reminders(id,user_id,kind,time,timezone,enabled,quiet_start,quiet_end,tone,days_per_week) SELECT ?,?,?,?,?,?,?,?,?,? WHERE NOT EXISTS(SELECT 1 FROM system WHERE key=? OR key=?) ON CONFLICT(id) DO NOTHING').bind(r.id,user,r.kind,r.time,r.timezone,r.enabled,r.quietStart,r.quietEnd,r.tone,r.daysPerWeek,key,priorKey));\r\n }\r\n for(const item of input.subscriptions){const s=subscriptionInput(item);statements.push(database.prepare('INSERT INTO subscriptions(endpoint,user_id,data,created_at) SELECT ?,?,?,? WHERE NOT EXISTS(SELECT 1 FROM system WHERE key=? OR key=?) ON CONFLICT(endpoint) DO NOTHING').bind(s.endpoint,user,JSON.stringify(s),Date.now(),key,priorKey));}\r\n statements.push(database.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO NOTHING').bind(key,String(Date.now())));\r\n await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:proof.currentDataEpoch,now:Date.now(),statements});return {imported:true};\r\n}\r\nexport default {\r\n async fetch(request,env){\r\n  try{\r\n   const url=new URL(request.url),path=url.pathname,method=request.method;\r\n   if(path==='/health'&&method==='GET'){const tick=await env.DB.prepare(\"SELECT value FROM system WHERE key='scheduler_tick'\").first();return json({ok:true,service:'MYR5 reminders',configured:!!env.VAPID_PRIVATE_KEY&&!!env.VAPID_PUBLIC_KEY,schedulerActive:!!tick&&Date.now()-Number(tick.value)<300000});}\r\n   if(!await authorized(request,env))return json({error:'Unauthorized.'},401);\r\n   if(path==='/internal/status'&&method==='GET'){const tick=await env.DB.prepare(\"SELECT value FROM system WHERE key='scheduler_tick'\").first();return json({configured:!!env.VAPID_PRIVATE_KEY&&!!env.VAPID_PUBLIC_KEY,publicKey:env.VAPID_PUBLIC_KEY||null,schedulerActive:!!tick&&Date.now()-Number(tick.value)<300000,lastRun:tick?Number(tick.value):null});}\r\n   // Token actions atomically match the currently stored unique token. No pseudo-owner epoch.\r\n   if(/^\\/internal\\/release-email\\/(confirm|unsubscribe)$/.test(path)&&method==='POST'){const input=await body(request);return json(await emailLinkAction(env,path.split('/').at(-1),input.token));}\r\n   const user=request.headers.get('X-Coach-User');if(!user||user.length>512||user.trim()!==user||/[\\r\\n]/.test(user))return json({error:'Missing account.'},401);\r\n   const allowed=(path==='/internal/account-reconcile'&&method==='POST')||(path==='/internal/training-status'&&method==='POST')||(path==='/internal/import'&&method==='POST')||(path==='/api/updates/subscription'&&['GET','POST','DELETE'].includes(method))||(path==='/api/reminders'&&['GET','POST'].includes(method))||(path==='/api/reminders/plan'&&['GET','PUT'].includes(method))||(/^\\/api\\/reminders\\/[a-f0-9-]{36}$/.test(path)&&['PUT','PATCH','DELETE'].includes(method))||(/^\\/api\\/push\\/(subscribe|unsubscribe|test)$/.test(path)&&method==='POST')||(path==='/api/export'&&method==='GET');\r\n   if(!allowed)return json({error:'Not found.'},404);\r\n   const proof=proofFromHeaders(request,user),ack=await reconcileRemoteEpoch(env.DB,{...proof,now:Date.now(),deletions:remoteAccountDeletionStatements(env.DB,user)});\r\n   if(path==='/internal/account-reconcile')return json({ownerId:ack.ownerId,currentDataEpoch:ack.currentDataEpoch,deletedThroughEpoch:ack.deletedThroughEpoch});\r\n   if(ack.currentDataEpoch!==proof.currentDataEpoch)throw Object.assign(Error('Account generation changed.'),{status:409,code:'target_epoch_mismatch'});\r\n   if(path==='/internal/training-status')return json(await syncTrainingStatus(env.DB,user,await body(request),Date.now(),{dataEpoch:proof.currentDataEpoch}));\r\n   if(path==='/internal/import')return json(await importExisting(request,env,user,proof));\r\n   if(path==='/api/updates/subscription')return json(await emailSubscription(env,user,method,method==='POST'?await body(request):{},Date.now(),{dataEpoch:proof.currentDataEpoch}));\r\n   return json(await remoteAccountOperation(request,env,user,proof.currentDataEpoch));\r\n  }catch(error){\r\n   const status=error.status||(error.code==='target_epoch_mismatch'?409:error.code==='remote_epoch_busy'?503:500);\r\n   return json({error:status===500?'Could not complete the reminder request.':error.message,...(typeof error.code==='string'?{code:error.code}:{})},status);\r\n  }\r\n },\r\n // Scheduled senders retain their captured source epoch through claims and callbacks.\r\n async scheduled(event,env,ctx){\r\n  ctx.waitUntil((async()=>{const result=await runReminders(env);if(result.failed)throw Error(`${result.failed} reminder deliveries failed`);})());\r\n  ctx.waitUntil((async()=>{const result=await runReleaseEmails(env);if(result.failed)throw Error(`${result.failed} release email deliveries failed`);})());\r\n  ctx.waitUntil((async()=>{const result=await runReleasePush(env);if(result.failed)throw Error(`${result.failed} update notification deliveries failed`);})());\r\n }\r\n};\r\n","scheduler/worker.mjs":"// Kept as the stable scheduler entry point for older setup instructions.\r\nexport {default} from './service.mjs';\r\n","scheduler/wrangler.jsonc":"{\r\n\t\"name\": \"myr5-coach-reminders\",\r\n\t\"main\": \"service.mjs\",\r\n\t\"compatibility_date\": \"2026-05-15\",\r\n\t\"triggers\": {\r\n\t\t\"crons\": []\r\n\t},\r\n\t\"vars\": {\r\n\t\t\"VAPID_SUBJECT\": \"https://myr5-coach.ianmyersrocks97.chatgpt.site\",\r\n\t\t\"RELEASE_ORIGIN\": \"https://myr5.mominc.online\",\r\n\t\t\"RELEASE_EMAIL_FROM\": \"dispatch@mominc.online\"\r\n\t},\r\n\t\"observability\": {\r\n\t\t\"enabled\": true\r\n\t},\r\n\t\"account_id\": \"f35c131b47d223ae301a8b577d6e2d59\",\r\n\t\"workers_dev\": true,\r\n\t\"preview_urls\": false,\r\n\t\"d1_databases\": [\r\n\t\t{\r\n\t\t\t\"binding\": \"DB\",\r\n\t\t\t\"database_name\": \"myr5-coach-reminders\",\r\n\t\t\t\"database_id\": \"3edf94f0-2175-41d1-9b4f-98e9cfe95945\"\r\n\t\t}\r\n\t]\r\n}\r\n","scoreboard-domain.mjs":"import {GROUP_EXERCISES} from './exercise-library.mjs';\r\nexport const GROUP_INKS={chest:'#a32b49',legs:'#2356a0',hips:'#754094',core:'#21715c',shoulders:'#95460e',balance:'#326d30',yoga:'#76458b',stances:'#374a7d',boxing:'#a33125',cardio:'#086e7c'};\r\nexport const scoreUnit=exercise=>exercise.kind==='hold'?'sec held':exercise.kind==='pace'?'sec active':exercise.kind==='steps'?'knee lifts':'reps';\r\nexport const displayScore=value=>Number.isFinite(value)?String(Math.floor(value)):'—';\r\nexport function comparisonRows(group,members=[]){\r\n return (GROUP_EXERCISES[group]||[]).slice().reverse().map(exercise=>{\r\n  const values=Array.from({length:3},(_,slot)=>{const record=members[slot]?.items?.find(item=>item.mode===exercise.id);return record&&record.sets>0&&Number.isFinite(Number(record.best))?Math.floor(Number(record.best)):null;});\r\n  const maximum=Math.max(1,...values.filter(v=>v!==null));\r\n  return {exercise,unit:scoreUnit(exercise),bars:values.map((value,slot)=>{const rank=value===null?null:1+values.filter(v=>v!==null&&v>value).length;return {slot,value,rank,medal:rank===1?'gold':rank===2?'silver':rank===3?'bronze':null,height:value===null?0:Math.max(0,value)/maximum*100};})};\r\n });\r\n}\r\n","scoreboard.css":"/* Extend the existing dry-erase board and its marker controls. */\r\n#accountPanel{width:min(1060px,calc(100vw - 22px));--marker:#a32b49;padding-bottom:22px;scroll-padding-bottom:120px}\r\n#accountPanel [hidden]{display:none!important}#accountPanel h2{font-size:clamp(30px,5vw,43px)}\r\n#accountPanel .crew-invites{padding:0 0 18px;margin:0 0 18px;border-bottom:2px solid #82919f70}#accountPanel .crew-invites h3{font:28px/1.2 BoardHand2,cursive;color:#304663;margin:0 0 12px}\r\n.scoreboard-crew{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:8px 0 12px}.crew-place{display:flex;align-items:center;flex-direction:column;min-width:0;gap:5px;padding:10px 5px;background:#e5e9eb66;border:1px dashed #7e8f9e;border-radius:5px}\r\n.crew-head{height:72px;width:72px;display:grid;place-items:center;border:2px solid #778d9c;border-radius:5px;background:#e7deed;color:#61768a;font:32px/1 Arial,sans-serif;flex:none}.crew-head canvas{width:64px;height:64px;image-rendering:pixelated}.crew-place strong{font:700 16px/1.3 Arial,sans-serif;color:#2e3e56;overflow-wrap:anywhere;text-align:center}.crew-place-label{font:13px/1.3 Arial,sans-serif;color:#536373}\r\n#accountPanel .crew-remove{font:14px/1.2 Arial,sans-serif;min-height:44px;border:0;box-shadow:none;background:transparent;text-decoration:underline;padding:5px;color:#80503d}\r\n#accountPanel #crewInviteControls summary{font:21px/1.4 BoardHand0,cursive;min-height:44px;padding:5px 0}#accountPanel #crewInviteControls p{font:16px/1.5 Arial,sans-serif}#accountPanel #crewInviteControls button{font:16px/1.3 Arial,sans-serif;margin:6px 7px 8px 0}#accountPanel #crewInviteControls label{font:16px/1.4 Arial,sans-serif}#accountPanel #crewInviteControls input{font:16px/1.5 monospace;width:100%;padding:12px 8px;min-height:44px}#crewJoinForm{margin:15px 0 8px;border-top:1px solid #9ba7b6;padding-top:14px}#accountPanel #crewStatus{font:15px/1.5 Arial,sans-serif;margin:8px 0 0}#crewStatus:empty{display:none}\r\n#accountPanel .progress-summary{padding-bottom:12px}#accountPanel #accountLevel{font-size:25px}#accountPanel #accountSets{font-size:18px}\r\n.scoreboard-heading{display:flex;align-items:center;justify-content:space-between;gap:15px;flex-wrap:wrap;border-bottom:3px solid var(--marker);margin-top:22px;padding-bottom:12px;color:var(--marker)}#accountPanel .scoreboard-heading h3{margin:0;font:36px/1.1 BoardHand2,cursive;color:var(--marker)}#accountPanel #scoreboardSubtitle{margin:4px 0 0;font:16px/1.4 Arial,sans-serif;color:var(--marker)}.medal-key{display:flex;gap:7px;flex-wrap:wrap}.medal-key span{font:700 12px/1.3 Arial,sans-serif;padding:5px 7px;border:1px solid #87919b;border-radius:3px;color:#253240}.medal-key [data-medal=gold]{background:#e7c666}.medal-key [data-medal=silver]{background:#d2dde3}.medal-key [data-medal=bronze]{background:#cd9b78}\r\n#boardCanvas{position:relative}#accountPanel #workoutList{display:block;position:relative;color:var(--marker);min-height:220px}#accountPanel #recordsStatus{font:14px/1.5 Arial,sans-serif;margin:10px 0}\r\n.score-axis{font:700 14px/1.5 Arial,sans-serif;color:var(--marker);text-transform:uppercase;letter-spacing:.6px}#accountPanel .score-caption{font:13px/1.5 Arial,sans-serif;color:#596878;margin:4px 0 12px}\r\n.score-row{display:grid;grid-template-columns:minmax(100px,1.1fr) minmax(0,2fr);gap:15px;align-items:center;padding:15px 0 14px;border-bottom:2px solid color-mix(in srgb,var(--marker) 25%,transparent)}.score-exercise{border-left:3px solid var(--marker);padding-left:10px}.score-level{font:700 12px/1.4 Arial,sans-serif;letter-spacing:.8px}.score-exercise h4{font:24px/1.1 BoardHand2,cursive;color:var(--marker);margin:6px 0}.score-unit{font:13px/1.4 Arial,sans-serif;color:#536373}\r\n.score-pillars{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;align-self:stretch}.score-pillar{display:flex;flex-direction:column;text-align:center;min-width:0;color:#344352}.score-value{font:700 24px/1.2 BoardHand2,cursive;font-variant-numeric:tabular-nums;color:var(--marker)}.pillar-track{height:72px;display:flex;align-items:flex-end;justify-content:center;background:repeating-linear-gradient(to top,transparent 0 23px,#9eabb92a 23px 24px);border-bottom:2px solid var(--marker);margin-top:5px}.pillar-bar{display:block;width:min(45px,90%);height:var(--bar-height);border:2px solid #795923;border-bottom:0;border-radius:5px 3px 0 0;background:repeating-linear-gradient(-15deg,#e0b74f 0 4px,#efd282 4px 7px);box-shadow:inset 3px 0 #fff4bd7a,3px 0 #674c3126}\r\n[data-medal=silver] .pillar-bar{background:repeating-linear-gradient(-15deg,#a9bbc8 0 4px,#d5e0e5 4px 7px);border-color:#536b7d;box-shadow:inset 3px 0 #f8ffff7a,3px 0 #41586b26}[data-medal=bronze] .pillar-bar{background:repeating-linear-gradient(-15deg,#b67848 0 4px,#d6a780 4px 7px);border-color:#704328;box-shadow:inset 3px 0 #ffe0b87a,3px 0 #63392126}[data-medal=empty] .pillar-bar{height:6px;width:70%;background:transparent;border:1px dashed #8a9daa;border-bottom:0;box-shadow:none}\r\n.pillar-rank{font:700 11px/1.4 Arial,sans-serif;text-transform:capitalize;margin-top:6px;color:#52606d}.pillar-name{font:13px/1.35 Arial,sans-serif;overflow-wrap:anywhere;margin-top:3px}#accountPanel .score-tie-note{font:13px/1.5 Arial,sans-serif;margin:12px 0;color:#596878}\r\n#accountPanel .marker-tray{height:auto;min-height:98px;position:sticky;bottom:-22px;margin:25px -10px -22px;padding:12px 10px 8px;display:flex;align-items:stretch;justify-content:flex-start;gap:9px;overflow-x:auto;overscroll-behavior-x:contain;pointer-events:auto;z-index:4;scrollbar-width:thin;background:linear-gradient(#c4cece,#f0f2ea 32%,#c4cece 35%,#929fa4);border-top:3px solid #74868e;box-shadow:0 -5px 14px #27343a25;scroll-padding:10px}\r\n#accountPanel .board-marker{flex:0 0 82px;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:10px;background:transparent;border:2px solid transparent;box-shadow:none;border-radius:5px;padding:7px 4px;min-height:78px;color:#243a49;touch-action:manipulation}#accountPanel .board-marker[aria-pressed=true]{border-color:var(--pen-ink);background:#ffffffb5}.marker-label{font:700 13px/1.25 Arial,sans-serif;max-width:84px;text-align:center}.marker-body{height:15px;width:68px;border:1px solid #66737c;border-left:12px solid var(--pen-ink);border-right:10px solid var(--pen-ink);border-radius:4px;background:linear-gradient(#fff,#d1d8d8);box-shadow:0 3px 3px #243b4866;position:relative;transform:rotate(-5deg);display:block}.marker-eraser{position:absolute;left:-12px;top:-2px;height:17px;width:9px;border:1px solid #415164;border-radius:3px;background:repeating-linear-gradient(#49555d 0 2px,#7e8b8c 2px 3px)}.marker-tip{position:absolute;right:-16px;top:4px;width:7px;height:5px;background:var(--pen-ink);border-radius:0 3px 3px 0}\r\n#accountPanel .marker-tray .marker-body i{transform:none;box-shadow:none}.marker-tray .marker-eraser{display:block}.marker-tray .marker-tip{display:block}\r\n#scoreboardMarkers i.marker-eraser{position:absolute;left:-12px;right:auto;top:-2px;height:17px;width:9px;border:1px solid #415164;border-radius:3px;background:repeating-linear-gradient(#49555d 0 2px,#7e8b8c 2px 3px)}#scoreboardMarkers i.marker-tip{position:absolute;right:-16px;left:auto;top:4px;width:7px;height:5px;border:0;background:var(--pen-ink);border-radius:0 3px 3px 0}\r\n#boardMovingPen{position:absolute;z-index:3;top:0;left:0;width:85px;height:19px;background:linear-gradient(#fff,#d1d8d8);border:1px solid #536674;border-left:12px solid var(--pen-ink);border-right:10px solid var(--pen-ink);border-radius:4px;pointer-events:none;filter:drop-shadow(2px 4px 2px #21374944);transform-origin:50% 50%}#boardMovingPen .marker-eraser{height:21px;left:-13px;top:-2px}\r\n.board-erasing #workoutList{animation:board-wipe .38s ease-in both}.board-erasing #boardMovingPen{animation:pen-erase .38s ease-in both}.board-drawing #workoutList{animation:board-reveal .44s steps(8,end) both}.board-drawing #boardMovingPen{animation:pen-scribble .44s linear both}\r\n@keyframes board-wipe{from{clip-path:inset(0)}to{clip-path:inset(0 0 0 100%)}}\r\n@keyframes board-reveal{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}\r\n@keyframes pen-erase{0%{transform:translate(var(--pen-from-x),var(--pen-from-y)) rotate(175deg)}25%{left:0;top:8%;transform:rotate(175deg)}60%{left:45%;top:40%;transform:rotate(190deg)}100%{left:85%;top:75%;transform:rotate(175deg)}}\r\n@keyframes pen-scribble{0%{left:0;top:5%;transform:rotate(-25deg)}20%{left:18%;top:78%;transform:rotate(20deg)}40%{left:35%;top:12%;transform:rotate(-25deg)}60%{left:50%;top:80%;transform:rotate(20deg)}80%{left:66%;top:15%;transform:rotate(-25deg)}100%{left:85%;top:75%;transform:rotate(20deg)}}\r\n@media(min-width:800px){.score-row{grid-template-columns:240px minmax(0,1fr)}.pillar-track{height:90px}.score-pillars{gap:28px}.pillar-bar{width:62px}.score-exercise h4{font-size:27px}#accountPanel .board-marker{flex:1 0 73px}.scoreboard-crew{gap:22px}.crew-place{flex-flow:row wrap;justify-content:center;column-gap:12px}.crew-place-label{flex-basis:100%;text-align:center}}\r\n@media(max-width:420px){#accountPanel header{min-height:100px}#accountPanel .progress-eyebrow{font-size:12px}#accountPanel h2{font-size:30px}.score-row{grid-template-columns:94px minmax(0,1fr);gap:8px}.score-exercise{padding-left:6px}.score-exercise h4{font-size:21px}.score-pillars{gap:5px}.pillar-name{font-size:12px}.scoreboard-crew{gap:6px}.crew-head{width:62px;height:62px}.crew-head canvas{width:58px;height:58px}.crew-place strong{font-size:14px}.crew-place-label{font-size:12px}.medal-key span{font-size:12px}.scoreboard-heading{gap:10px}}\r\n@media(prefers-reduced-motion:reduce){#accountPanel .board-erasing,#accountPanel .board-drawing,#accountPanel #workoutList,#boardMovingPen{animation:none!important}#boardMovingPen{display:none!important}}\r\n","scoreboard.mjs":"import {FOCUS_GROUPS} from './exercise-library.mjs';\r\nimport {GROUP_INKS,comparisonRows,displayScore} from './scoreboard-domain.mjs';\r\nimport {paintStyleText} from './workout-symbols.mjs';\r\nconst $=id=>document.getElementById(id);\r\nconst element=(tag,className,text)=>{const node=document.createElement(tag);if(className)node.className=className;if(text!=null)node.textContent=text;return node;};\r\nexport function mountScoreboard({api,getAccount}){\r\n const container=$('workoutList'),panel=$('accountPanel'),tray=$('scoreboardMarkers');\r\n panel.classList.add('classroom-panel');\r\n const classroomHost=element('div','classroom-host');classroomHost.dataset.roomHost='';panel.querySelector('header')?.after(classroomHost);\r\n const classroomStyle=element('link');classroomStyle.rel='stylesheet';classroomStyle.href='/modules/rooms/classroom.css';document.head.append(classroomStyle);\r\n let room=null,roomEpoch=0;\r\n async function openClassroom(){if(!panel.open||room)return;const epoch=++roomEpoch;panel.dataset.room='loading';try{const {mountClassroom}=await import('./modules/rooms/classroom.mjs');if(epoch!==roomEpoch||!panel.open)return;room=mountClassroom(panel,{host:classroomHost});room.setDesks(deskNodes());}catch(error){if(epoch!==roomEpoch)return;delete panel.dataset.room;classroomHost.replaceChildren();const offer=element('button','','Download classroom');offer.type='button';offer.onclick=()=>window.myr5Packs?.open?.('room-scoreboard');classroomHost.append(offer);panel.dispatchEvent(new CustomEvent('myr5:classroom-board',{bubbles:true}));console.warn('Classroom unavailable',error);}}\r\n const classroomObserver=new MutationObserver(()=>{if(panel.open)void openClassroom();});classroomObserver.observe(panel,{attributes:true,attributeFilter:['open']});\r\n let selected='chest',members=[],loaded=false,requestNumber=0,animationNumber=0,animationTimers=[],pendingInvite=null,crewSignature='';\r\n function clearAnimation(){animationTimers.forEach(clearTimeout);animationTimers=[];panel.classList.remove('board-erasing','board-drawing');$('boardMovingPen').hidden=true;}\r\n function renderGraph(){\r\n  container.replaceChildren();const group=FOCUS_GROUPS.find(g=>g.id===selected);panel.style.setProperty('--marker',GROUP_INKS[selected]);\r\n  paintStyleText($('scoreboardGroup'),group.name);$('scoreboardSubtitle').textContent=group.description;\r\n  for(const pen of tray.children){pen.setAttribute('aria-pressed',String(pen.dataset.group===selected));}\r\n  const axis=element('div','score-axis','↑ Exercise level');const caption=element('p','score-caption','Best completed set · bar heights compare people within each row');container.append(axis,caption);\r\n  for(const row of comparisonRows(selected,members)){\r\n   const section=element('section','score-row');section.setAttribute('aria-label',`Level ${row.exercise.difficulty}: ${row.exercise.name}`);\r\n   const label=element('div','score-exercise');label.append(element('span','score-level',`LV ${String(row.exercise.difficulty).padStart(2,'0')}`),element('h4','',row.exercise.name),element('span','score-unit',row.unit));\r\n   const pillars=element('div','score-pillars');\r\n   for(const bar of row.bars){const person=members[bar.slot],name=bar.slot===0?'You':person?.name||`Friend ${bar.slot}`;const pillar=element('div','score-pillar');pillar.dataset.medal=bar.medal||'empty';pillar.setAttribute('aria-label',`${name}: ${bar.value===null?(person?'no completed set':'not connected'):`${displayScore(bar.value)} ${row.unit}, ${bar.medal}`}`);\r\n    const value=element('strong','score-value',displayScore(bar.value)),track=element('div','pillar-track'),ink=element('i','pillar-bar');ink.style.setProperty('--bar-height',bar.height+'%');ink.setAttribute('aria-hidden','true');track.append(ink);\r\n    pillar.append(value,track,element('span','pillar-rank',bar.medal?`${bar.rank} · ${bar.medal}`:person?'No set':'Open place'),element('span','pillar-name',name));pillars.append(pillar);\r\n   }\r\n   section.append(label,pillars);container.append(section);\r\n  }\r\n }\r\n function select(group,button){\r\n  if(group===selected&&loaded)return;selected=group;const sequence=++animationNumber;clearAnimation();\r\n  window.dispatchEvent(new Event('myr5:whiteboard-redraw'));\r\n  if(matchMedia('(prefers-reduced-motion: reduce)').matches){renderGraph();return;}\r\n  const pen=$('boardMovingPen');pen.hidden=false;pen.style.setProperty('--pen-ink',GROUP_INKS[group]);\r\n  const from=button.getBoundingClientRect(),to=$('boardCanvas').getBoundingClientRect();pen.style.setProperty('--pen-from-x',`${from.left+from.width/2-to.left}px`);pen.style.setProperty('--pen-from-y',`${from.top-to.top}px`);\r\n  panel.classList.add('board-erasing');\r\n  animationTimers.push(setTimeout(()=>{if(sequence!==animationNumber)return;renderGraph();panel.classList.remove('board-erasing');panel.classList.add('board-drawing');},380));\r\n  animationTimers.push(setTimeout(()=>{if(sequence===animationNumber)clearAnimation();},820));\r\n }\r\n for(const group of FOCUS_GROUPS){const pen=element('button','board-marker');pen.type='button';pen.dataset.group=group.id;pen.style.setProperty('--pen-ink',GROUP_INKS[group.id]);pen.setAttribute('aria-label',`${group.name} scoreboard`);pen.setAttribute('aria-controls','boardCanvas');pen.setAttribute('aria-pressed',String(group.id===selected));const body=element('span','marker-body');body.setAttribute('aria-hidden','true');body.append(element('i','marker-eraser'),element('i','marker-tip'));pen.append(body,paintStyleText(element('span','marker-label'),group.name));pen.onclick=()=>select(group.id,pen);tray.append(pen);}\r\n function localGala(){try{return JSON.parse(localStorage.getItem('mominc-avatar-v1'));}catch{return null;}}\r\n function drawHead(slot,person){\r\n  const frame=element('div','crew-head');if(!person){frame.textContent='+';frame.setAttribute('aria-hidden','true');return frame;}\r\n  const saved=person.avatar||(slot===0?localGala():null);\r\n  if(!saved||!window.GalaAvatar){frame.textContent='?';frame.setAttribute('aria-label','No saved Gala look');return frame;}\r\n  try{const look=window.GalaAvatar.normalize(saved),source=document.createElement('canvas'),head=document.createElement('canvas');head.width=64;head.height=64;head.setAttribute('role','img');head.setAttribute('aria-label',`${slot===0?'Your':person.name+'’s'} Gala character head`);\r\n   const paint=blink=>{window.GalaAvatar.draw(source,look,{base:false,weapon:false,companion:false,prop:false,blink});const ctx=head.getContext('2d');ctx.imageSmoothingEnabled=false;ctx.clearRect(0,0,64,64);ctx.drawImage(source,8,0,48,40,0,5,64,54);};\r\n   paint(false);frame.append(head);\r\n   // Blink every 3-6 s per head; the chain ends itself once the head is no longer in the page.\r\n   const later=()=>setTimeout(()=>{if(!head.isConnected)return;paint(true);setTimeout(()=>{paint(false);later();},120);},3000+Math.random()*3000);later();\r\n  }catch{frame.textContent='?';frame.setAttribute('aria-label','No saved Gala look');}\r\n  return frame;\r\n }\r\n function renderCrew(){const signature=JSON.stringify(members.map(({name,link,avatar})=>({name,link,avatar})))+(localStorage.getItem('mominc-avatar-v1')||'');if(signature===crewSignature)return;crewSignature=signature;$('scoreboardCrew').replaceChildren();for(let slot=0;slot<3;slot++){const person=members[slot],card=element('div','crew-place');card.append(drawHead(slot,person),element('strong','',slot===0?(person?.name||'You'):person?.name||`Friend ${slot}`),element('span','crew-place-label',slot===0?'Your place':person?'Connected':'Invite a friend'));if(person?.link){const remove=element('button','crew-remove','Remove');remove.type='button';remove.setAttribute('aria-label',`Stop sharing with ${person.name}`);remove.onclick=()=>action(remove,async()=>{await api('/api/scoreboard/friends/'+encodeURIComponent(person.link),'DELETE');await refresh();$('crewStatus').textContent='Friend removed. Sharing has stopped.';});card.append(remove);}$('scoreboardCrew').append(card);}$('createCrewInvite').disabled=members.length>=3;$('crewJoin').disabled=members.length>=3;room?.setDesks(deskNodes());}\r\n function deskNodes(){return [1,2].map(slot=>{const person=members[slot];if(person){const figure=element('figure');figure.append(drawHead(slot,person),element('figcaption','',person.name||`Friend ${slot}`));return figure;}const pick=element('button');pick.type='button';pick.setAttribute('aria-label',`Pick a friend for desk ${slot}`);pick.append(element('b','','+'),element('span','','Pick friends'));pick.onclick=pickFriends;return pick;});}\r\n function pickFriends(){room?.showBoard();if($('accountContent').hidden){$('signIn').focus();return;}const controls=$('crewInviteControls');controls.open=true;controls.scrollIntoView({block:'center'});controls.querySelector('summary').focus({preventScroll:true});}\r\n async function action(button,fn){button.disabled=true;try{await fn();}catch(error){$('crewStatus').textContent=error.message;}finally{button.disabled=members.length>=3&&['createCrewInvite','crewJoin'].includes(button.id);}}\r\n $('createCrewInvite').onclick=()=>action($('createCrewInvite'),async()=>{const result=await api('/api/scoreboard/invite','POST',{consent:true});pendingInvite=result.code;$('crewInviteCode').value=result.code;$('crewInviteOutput').hidden=false;$('revokeCrewInvite').hidden=false;$('crewStatus').textContent='Code ready. Share it with one friend. It expires in seven days.';});\r\n $('copyCrewInvite').onclick=async()=>{try{await navigator.clipboard.writeText(pendingInvite||$('crewInviteCode').value);$('crewStatus').textContent='Invite code copied. Your friend can enter it in their scoreboard.';}catch{$('crewInviteCode').focus();$('crewInviteCode').select();$('crewStatus').textContent='Code selected. Use Copy to share it.';}};\r\n $('revokeCrewInvite').onclick=()=>action($('revokeCrewInvite'),async()=>{await api('/api/scoreboard/invite','DELETE');pendingInvite=null;$('crewInviteOutput').hidden=true;$('revokeCrewInvite').hidden=true;$('crewStatus').textContent='Invite cancelled.';});\r\n $('crewJoinForm').onsubmit=event=>{event.preventDefault();action($('crewJoin'),async()=>{await api('/api/scoreboard/join','POST',{code:$('crewJoinCode').value,consent:true});void import('./modules/vault/vault-store.mjs').then(m=>m.bump('scoreboard-link')).catch(()=>{});$('crewJoinCode').value='';await refresh();$('crewStatus').textContent='Connected. Your names, Gala looks and best sets are shared.';});};\r\n async function refresh(){if(!getAccount())return;const user=getAccount().user.id,epoch=getAccount().dataEpoch,number=++requestNumber;container.setAttribute('aria-busy','true');$('recordsStatus').textContent='Updating the board…';try{const result=await api('/api/scoreboard');if(number!==requestNumber||getAccount()?.user.id!==user||getAccount()?.dataEpoch!==epoch||result.targetAccountId!==user||result.dataEpoch!==epoch)return;members=result.members;loaded=true;renderCrew();renderGraph();$('revokeCrewInvite').hidden=!result.inviteExpiresAt;if(!result.inviteExpiresAt){pendingInvite=null;$('crewInviteOutput').hidden=true;}$('recordsStatus').textContent=members[0]?.items.length?`Updated ${new Date(result.updatedAt).toLocaleTimeString([],{hour:'numeric',minute:'2-digit'})}`:'Finish a set to put your first record on the board.';}catch(error){if(number===requestNumber)$('recordsStatus').textContent=`Board could not refresh. ${error.message}`;}finally{if(number===requestNumber)container.removeAttribute('aria-busy');}}\r\n function clear(){++requestNumber;++animationNumber;clearAnimation();members=[];loaded=false;crewSignature='';pendingInvite=null;$('crewInviteOutput').hidden=true;$('crewInviteCode').value='';renderCrew();renderGraph();}\r\n panel.addEventListener('close',()=>{++animationNumber;clearAnimation();++roomEpoch;room?.dispose();room=null;delete panel.dataset.room;delete panel.dataset.roomView;});\r\n const timer=setInterval(()=>{if(panel.open&&!document.hidden&&loaded)refresh();},30000);window.addEventListener('pagehide',()=>{clearInterval(timer);clearAnimation();});\r\n window.addEventListener('myr5:account-progress',()=>{if(panel.open&&loaded)refresh();});\r\n renderCrew();renderGraph();return {refresh,clear};\r\n}\r\n","scripts/actual-dj-preview.mjs":"import {createServer} from 'node:http';\r\nimport {readFile} from 'node:fs/promises';\r\nimport {resolve,extname,sep} from 'node:path';\r\nimport {fileURLToPath} from 'node:url';\r\nconst project=fileURLToPath(new URL('../',import.meta.url));\r\nconst html='<!doctype html><html lang=\"en\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>actual.dj / MYR5 preview</title><link rel=\"stylesheet\" href=\"/spotify-terminal.css\"><link rel=\"stylesheet\" href=\"/actual-dj.css\"><body style=\"background:#100b18\"><button id=\"open\" style=\"padding:20px;margin:30px\">Open actual.dj</button><script type=\"module\">import{initSpotifyTerminal}from\"/spotify-terminal.mjs\";const dj=window.djPreview=initSpotifyTerminal({getAccount:()=>null});document.getElementById(\"open\").addEventListener(\"click\",()=>dj.open());dj.open();</script></body></html>';\r\nconst types={'.html':'text/html','.mjs':'text/javascript','.js':'text/javascript','.css':'text/css','.json':'application/json','.webmanifest':'application/manifest+json','.m4a':'audio/mp4','.wav':'audio/wav','.mp3':'audio/mpeg','.webp':'image/webp','.png':'image/png','.jpg':'image/jpeg','.svg':'image/svg+xml','.glb':'model/gltf-binary','.bin':'application/octet-stream','.wasm':'application/wasm','.woff2':'font/woff2','.ttf':'font/ttf'};\r\nexport function sendFile(req,res,data,type){\r\n res.setHeader('Content-Type',type);res.setHeader('Accept-Ranges','bytes');\r\n // HTTP ranges apply to GET; HEAD reports the same full representation without a body.\r\n const range=req.method==='GET'?req.headers.range:null;\r\n if(range){\r\n  const match=range.match(/^bytes=(\\d*)-(\\d*)$/i);let start,end;\r\n  if(match&&(match[1]||match[2])){\r\n   if(match[1]){start=Number(match[1]);end=match[2]?Number(match[2]):data.length-1;}\r\n   else{const suffix=Number(match[2]);start=suffix>0?Math.max(0,data.length-suffix):data.length;end=data.length-1;}\r\n  }\r\n  if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||start>=data.length||end<start){res.writeHead(416,{'Content-Range':`bytes */${data.length}`,'Content-Length':'0'});res.end();return;}\r\n  end=Math.min(end,data.length-1);\r\n  res.writeHead(206,{'Content-Range':`bytes ${start}-${end}/${data.length}`,'Content-Length':String(end-start+1)});res.end(data.subarray(start,end+1));return;\r\n }\r\n res.setHeader('Content-Length',String(data.length));res.end(req.method==='HEAD'?undefined:data);\r\n}\r\nexport function createDJPreviewServer({root=project}={}){\r\n return createServer(async(req,res)=>{res.setHeader('Cache-Control','no-store');\r\n  const path=new URL(req.url,'http://preview').pathname;\r\n  if(path==='/'||path==='/actual-dj-preview'){res.setHeader('Content-Type','text/html');res.end(html);return;}\r\n  if(path.startsWith('/api/')){res.writeHead(401,{'Content-Type':'application/json'});res.end('{\"error\":\"Sign in on the live MYR5 app to use account controls.\"}');return;}\r\n  try{const decoded=decodeURIComponent(path),file=resolve(root,'.'+decoded);if(!file.startsWith(resolve(root)+sep)||decoded.split('/').some(p=>p.startsWith('.'))||/^\\/(server|scripts|tests|node_modules|release-trust)\\//.test(decoded)||!types[extname(file)])throw Error();const data=await readFile(file);sendFile(req,res,data,types[extname(file)]);}catch{res.writeHead(404);res.end('Not found');}\r\n });\r\n}\r\nif(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){\r\n const server=createDJPreviewServer({root:process.env.ACTUAL_DJ_PREVIEW_ROOT||project}),port=Number(process.env.ACTUAL_DJ_PREVIEW_PORT)||18905;\r\n server.listen(port,'127.0.0.1',()=>console.log(`actual.dj preview: http://127.0.0.1:${port}/actual-dj-preview`));\r\n}\r\n","scripts/assets.mjs":"import {readFile,writeFile,mkdir} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nimport {build} from 'esbuild';\r\n// Keep Three.js out of the eager launch bundle; the Food panel loads it on demand.\r\nexport async function ensureThreeVendor(){\r\n await mkdir('vendor/three',{recursive:true});\r\n await build({entryPoints:['node_modules/three/build/three.module.js'],bundle:false,minify:true,format:'esm',outfile:'vendor/three/three.module.js',logLevel:'silent'});\r\n await build({entryPoints:['node_modules/three/examples/jsm/loaders/GLTFLoader.js'],bundle:true,minify:true,format:'esm',external:['three'],outfile:'vendor/three/GLTFLoader.js',logLevel:'silent'});\r\n await build({entryPoints:['node_modules/three/examples/jsm/libs/meshopt_decoder.module.js'],bundle:false,minify:true,format:'esm',outfile:'vendor/three/meshopt_decoder.module.js',logLevel:'silent'});\r\n}\r\nconst revision='40911d5761c2e8167664c3416fb37580246c74b9';\r\nconst files=[['myr5.glb','1593467f0aa641119d56f28b2a98af3877fe08ec'],['anatomy.glb','1035a26eb090e8a0436495a3c62839ad67a718b6'],['hands-v2.glb','83713fc5689b8e78ced779fcb5435d14b04d4c7a']];\r\nconst blobId=bytes=>createHash('sha1').update(`blob ${bytes.length}\\0`).update(bytes).digest('hex');\r\nexport async function ensureHandAssets(){\r\n const path='handborne/models/family-20.glb',expected='e77adfca6536a7221d9006048717a8f4fce0552e';\r\n await mkdir('handborne/models',{recursive:true});\r\n let bytes;try{bytes=await readFile(path);}catch{\r\n  const url='https://raw.githubusercontent.com/MomInc-MBS/mominc-mbs.github.io/b625d640418dddf988ce5a028a0a0a10da87fd32/handborne/models/family-20.glb';\r\n  const response=await fetch(url);if(!response.ok)throw Error(`Could not download the Fluffy hand model: ${response.status}`);bytes=Buffer.from(await response.arrayBuffer());\r\n }\r\n if(blobId(bytes)!==expected)throw Error('Fluffy hand model content check failed.');\r\n await writeFile(path,bytes);\r\n}\r\nexport async function ensureAssets(){await mkdir('creature/models',{recursive:true});for(const [name,expected]of files){const path=`creature/models/${name}`;let bytes;try{bytes=await readFile(path);}catch{if(name==='hands-v2.glb')throw Error('Restore the authored hands-v2.glb from the project source.');const url=`https://raw.githubusercontent.com/MomInc-MBS/mominc-mbs.github.io/${revision}/tv/assets/armie-intro/myr5/models/${name}`;const response=await fetch(url);if(!response.ok)throw Error(`Could not download ${name}: ${response.status}`);bytes=Buffer.from(await response.arrayBuffer());}if(blobId(bytes)!==expected)throw Error(`Model content check failed: ${name}`);await writeFile(path,bytes);}}\r\n","scripts/audit-rts-roster.mjs":"import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport {fileURLToPath} from 'node:url';\r\nimport {UNIT_CATALOG,COACH_UNITS,SECTION_COMPANIONS,BASIC_WORKER} from '../modules/coach-world/unit-catalog.mjs';\r\nimport {BUILDINGS,MAX_LEVEL} from '../modules/coach-world/catalog.mjs';\r\nimport {SHIP_ROSTER,SHIP_TIERS,SHIP_VARIANTS,SHIP_DIRECTIONS,SHIP_STATES,shipPixels,shipFrameCount,SHIP_CLIP_MS} from '../modules/coach-world-rts/ship-roster.mjs';\r\n\r\nconst root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');\r\nconst atlasDir=path.join(root,'modules/coach-world-rts/atlas');\r\nconst actions=['idle','walk','work','attack','hit','death'];\r\nconst directions=['N','NE','E','SE','S','SW','W','NW'];\r\nconst errors=[],incompleteStates=[],atlases={};\r\nfor(const file of fs.readdirSync(atlasDir).filter(f=>/^(bodies-.*|myr5)\\.json$/.test(f))){\r\n const atlas=JSON.parse(fs.readFileSync(path.join(atlasDir,file),'utf8'));\r\n const image=atlas.image||`${atlas.unit}.png`,png=path.join(atlasDir,image);\r\n let bounds=null;\r\n if(!fs.existsSync(png))errors.push(`${file}: missing image ${atlas.image}`);\r\n else {const b=fs.readFileSync(png);if(b.toString('hex',0,8)!=='89504e470d0a1a0a')errors.push(`${file}: invalid PNG ${atlas.image}`);else bounds={width:b.readUInt32BE(16),height:b.readUInt32BE(20)};}\r\n atlases[atlas.unit]={atlas,bounds,file,image};\r\n}\r\nlet stateRects=0;\r\nfor(const u of UNIT_CATALOG){\r\n const name=u.rtsSheet||(u===BASIC_WORKER?'myr5':null),entry=atlases[name];\r\n if(!entry){errors.push(`${u.id}: no sheet ${name}`);continue;}\r\n const {atlas,bounds}=entry,id=u.sourceCoachId;\r\n if(id&&id!=='myr5'&&!atlas.units?.[id]){errors.push(`${u.id}: source ${id} absent from ${entry.file}`);continue;}\r\n const clips=id&&id!=='myr5'?atlas.units[id].actions:atlas.actions;\r\n for(const action of actions){\r\n  const clip=clips?.[action];if(!clip){incompleteStates.push({unitId:u.id,sheet:name,action,reason:'state absent from source atlas'});continue;}\r\n  if(!Array.isArray(clip.rects)||clip.rects.length!==5){errors.push(`${u.id}: ${action} must have five stored direction rows`);continue;}\r\n  for(const dir of directions){const d=atlas.dirs?.[dir];if(!d){errors.push(`${u.id}: missing facing ${dir}`);continue;}const [stored]=d,rects=clip.rects[stored];if(!rects?.length){errors.push(`${u.id}: ${action}/${dir} has no frames`);continue;}\r\n   for(const [x,y,w,h] of rects){stateRects++;if(![x,y,w,h].every(Number.isFinite)||x<0||y<0||w<=0||h<=0||!bounds||x+w>bounds.width||y+h>bounds.height)errors.push(`${u.id}: ${action}/${dir} rect [${x},${y},${w},${h}] outside ${name} PNG ${bounds?`${bounds.width}x${bounds.height}`:'(missing)'}`);}\r\n  }\r\n }\r\n}\r\nif(MAX_LEVEL!==5)errors.push(`Building tier contract changed: expected 5, found ${MAX_LEVEL}`);\r\n// Ships are a separate air-unit lane: they use procedural pixel art and GLB assets,\r\n// not the ground-unit atlas/state contract above.\r\nconst shipSource=path.join(root,'modules/coach-world/pixel-art.mjs');\r\nconst starterPath=path.join(root,'pod/worlds/starter/supportive.glb');\r\nconst shipSources={proceduralPixelArt:{path:path.relative(root,shipSource),exists:fs.existsSync(shipSource),evidence:fs.existsSync(shipSource)?fs.readFileSync(shipSource,'utf8').includes(\"'ship'\"):false},starterGlb:{path:path.relative(root,starterPath),exists:fs.existsSync(starterPath),bytes:fs.existsSync(starterPath)?fs.statSync(starterPath).size:0,glbMagic:fs.existsSync(starterPath)?fs.readFileSync(starterPath).toString('ascii',0,4):null},sharedArtDisclosure:'supportive flies the starter GLB; the other five hulls fly native painted sprites (ship-roster.mjs shipPixels)'};\r\nif(!shipSources.proceduralPixelArt.exists||!shipSources.proceduralPixelArt.evidence)errors.push('ship procedural pixel-art source missing its ship path');\r\nif(!shipSources.starterGlb.exists||shipSources.starterGlb.glbMagic!=='glTF')errors.push('shipped starter ship GLB missing or invalid');\r\nconst ships=SHIP_ROSTER.map(ship=>({id:ship.id,label:ship.label,frame:ship.frame,role:ship.role,ownedAssetIdentity:ship.id,source:shipSources,tiers:SHIP_TIERS.map(tier=>({tier,variants:SHIP_VARIANTS.map(variant=>({variant,directions:SHIP_DIRECTIONS,states:SHIP_STATES,sharedArt:variant!=='standard'||ship.id!=='supportive'}))}))}));\r\nlet shipFrames=0;\r\nfor(const ship of SHIP_ROSTER)for(const tier of SHIP_TIERS)for(const variant of SHIP_VARIANTS)for(const direction of SHIP_DIRECTIONS)for(const state of SHIP_STATES)for(let f=0;f<shipFrameCount(state);f++){\r\n const {data}=shipPixels({id:ship.id,tier,variant,direction,state,time:f*(SHIP_CLIP_MS[state]?SHIP_CLIP_MS[state]/Math.max(1,shipFrameCount(state)-1):120)});shipFrames++;\r\n let lit=0;for(let i=3;i<data.length;i+=4)if(data[i])lit++;\r\n if(!lit&&!(state==='death'&&f===shipFrameCount(state)-1))errors.push(`ship ${ship.id} t${tier} ${variant} ${direction} ${state}#${f}: empty frame`);\r\n}\r\nif(ships.length!==6||ships.some(s=>s.tiers.length!==4||s.tiers.some(t=>t.variants.length!==4)))errors.push('ship lane must contain all six identities and all supported tier/variant combinations');\r\nconst buildings=BUILDINGS.map(b=>({id:b.id,kind:b.kind,prerequisites:b.prerequisites||[],tiers:[1,2,3,4,5].map(level=>({level,buildingId:b.id,levelPath:Array.from({length:level},(_,i)=>`${b.id}:level-${i+1}`)}))}));\r\nconst report={generatedAt:new Date().toISOString(),summary:{variants:UNIT_CATALOG.length,groundVariants:UNIT_CATALOG.length,shipIdentities:ships.length,shipFramesChecked:shipFrames,shipTierVariantRecords:ships.reduce((n,s)=>n+s.tiers.reduce((m,t)=>m+t.variants.length,0),0),totalInventory:UNIT_CATALOG.length+ships.length,coaches:COACH_UNITS.length,companions:Object.values(SECTION_COMPANIONS).flat().length,workers:1,requestedStatesPerVariant:actions.length,directionsPerState:directions.length,checkedStateRects:stateRects,buildings:BUILDINGS.length,buildingTierPaths:buildings.length*5,missingStates:incompleteStates.length,shipMissingStates:0},actions,directions,atlases:Object.fromEntries(Object.entries(atlases).map(([name,v])=>[name,{file:v.file,png:v.image,bounds:v.bounds,sourceCount:Object.keys(v.atlas.units||{}).length}])),ships,buildings,incompleteStates,errors};\r\nconst out=path.join(root,'artifacts/RTS-ROSTER-AUDIT.json');fs.mkdirSync(path.dirname(out),{recursive:true});fs.writeFileSync(out,JSON.stringify(report,null,2)+'\\n');\r\nconsole.log(`RTS roster audit: ${report.summary.variants} variants, ${report.summary.shipIdentities} ship hulls (${report.summary.shipFramesChecked} frames), ${report.summary.checkedStateRects} rectangles, ${report.summary.buildingTierPaths} building tier paths; ${errors.length} invalid records, ${incompleteStates.length} missing states.`);\r\nconsole.log(`Report: ${path.relative(root,out)}`);\r\nif(errors.length||incompleteStates.length){for(const e of errors)console.error(`ERROR ${e}`);for(const e of incompleteStates)console.error(`MISSING ${e.unitId}: ${e.action} on ${e.sheet}`);process.exitCode=1;}\r\n","scripts/build-creature-skin-manifests.mjs":"// Builds the additive creature-skin runtime catalog and optional-pack asset manifest\r\n// from the two immutable handoff manifests. Texture bytes remain in plan/assets-inbox.\r\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\nconst PACKETS = [\r\n  { dir: 'forged-realms', packetId: 'coach-creature-forged-realms-v1' },\r\n  { dir: 'celestial-rift', packetId: 'coach-creature-celestial-rift-v1' },\r\n];\r\nconst TRACKS = new Set(['chest','quads','glutes','arms','yoga','martial-arts','cardio','meditation']);\r\nconst HEX = /^[a-f0-9]{64}$/;\r\n\r\nexport async function buildCreatureSkinManifests({ root = process.cwd(), outputDir = root, catalogPath = join(outputDir, 'creature-skins.json') } = {}) {\r\n  const base = join(root, 'plan/assets-inbox/creature-skins');\r\n  const collections = [], ids = new Set();\r\n  for (const packet of PACKETS) {\r\n    const sourcePath = join(base, packet.dir, 'source-manifest.json');\r\n    const source = JSON.parse(await readFile(sourcePath, 'utf8'));\r\n    if (source.packetId !== packet.packetId || !Array.isArray(source.items)) throw new Error(`Invalid packet manifest: ${packet.packetId}`);\r\n    collections.push({ id: source.items[0]?.collection, packetId: source.packetId, displayName: source.displayName, description: source.description, runtimeNotes: source.runtimeNotes });\r\n    for (const item of source.items) {\r\n      if (!item.id?.startsWith('creature-') || ids.has(item.id)) throw new Error(`Duplicate or non-namespaced creature skin id: ${item.id}`);\r\n      if (!TRACKS.has(item.track) || ![1,2,3].includes(item.collectionSlot) || item.collection !== collections.at(-1).id) throw new Error(`Invalid track/collection for ${item.id}`);\r\n      ids.add(item.id);\r\n      for (const [map, info] of Object.entries(item.maps)) {\r\n        if (!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(info.file||'') || info.file.includes('..') || !Number.isSafeInteger(info.bytes) || info.bytes<1 || !HEX.test(info.sha256)) throw new Error(`Invalid ${map} metadata for ${item.id}`);\r\n      }\r\n    }\r\n  }\r\n  if (ids.size !== 48) throw new Error(`Expected 48 unique creature skins; found ${ids.size}`);\r\n  const catalog = { schemaVersion: 1, additive: true, colorSpace: { basecolor: 'srgb', tintMask: 'srgb', normal: 'linear', roughness: 'linear', height: 'linear', ao: 'linear', metalness: 'linear', opacity: 'linear', emissive: 'srgb' }, normalConvention: 'OpenGL +Y', tinting: 'tint-mask multiplied by selected creature palette', collections };\r\n  // Preserve complete source metadata, including source URLs, CC0 attribution, and original checksums.\r\n  catalog.skins = [];\r\n  for (const packet of PACKETS) {\r\n    const source = JSON.parse(await readFile(join(base, packet.dir, 'source-manifest.json'), 'utf8'));\r\n    catalog.skins.push(...source.items.map(item => ({ ...item, runtime: { unlock: { level: [1, 3, 5][item.collectionSlot - 1], track: item.track }, pack: `track-${item.track}`, bundle: `assets/${item.track}.m5bundle`, maps: Object.fromEntries(Object.entries(item.maps).map(([name, map]) => [name, { entry: `${item.id}/${name}`, bytes: map.bytes, sha256: map.sha256 }])) } })));\r\n  }\r\n  await mkdir(outputDir, { recursive: true });\r\n  await mkdir(dirname(catalogPath), { recursive: true });\r\n  await writeFile(catalogPath, JSON.stringify(catalog, null, 2) + '\\n');\r\n  return { skins: ids.size, outputDir: resolve(outputDir) };\r\n}\r\n\r\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\r\n  const outArg = process.argv.find(x => x.startsWith('--out='));\r\n  console.log(JSON.stringify(await buildCreatureSkinManifests({ outputDir: outArg ? resolve(outArg.slice(6)) : process.cwd() }), null, 2));\r\n}\r\n","scripts/build-dance-music-lab.mjs":"import {mkdir,cp,readFile,writeFile,stat,readdir} from 'node:fs/promises';\r\nimport {resolve} from 'node:path';\r\nimport {build} from 'esbuild';\r\nimport {sourceOffer} from './source-offer.mjs';\r\nconst root=resolve(import.meta.dirname,'..'),out=resolve(root,'dist/dance-music-lab');\r\nawait mkdir(out,{recursive:true});\r\nconst files=['modules/dance/music-lab/index.html','modules/dance/music-lab/lab.css','modules/dance/music-lab/lab.mjs','modules/dance/music-lab/capture.mjs','modules/dance/music-lab/pcm-worklet.mjs','modules/dance/music/beat-probe.mjs','modules/dance/content/index.mjs','modules/dance/content/catalogue.mjs','modules/dance/content/features.mjs','modules/dance/ui/skeleton.mjs','modules/dance/assets/audio/neon-step-pop.wav','LICENSE'];\r\n// Copy the authored catalogue's local dependencies as well; no game backend, account or Spotify endpoints.\r\nawait cp(resolve(root,'modules/dance/content'),resolve(out,'modules/dance/content'),{recursive:true});\r\nawait cp(resolve(root,'modules/dance/assets/audio'),resolve(out,'modules/dance/assets/audio'),{recursive:true});\r\nawait cp(resolve(root,'modules/dance'),resolve(out,'modules/dance'),{recursive:true,filter:p=>!/[\\\\/]tests(?:[\\\\/]|$)/.test(p)});\r\n// The Android floor reuses the production coach/model runtime and pose tracker.\r\nawait cp(resolve(root,'modules/dance/android-game'),resolve(out,'modules/dance/android-game'),{recursive:true});\r\nawait mkdir(resolve(out,'arcade'),{recursive:true});\r\nawait cp(resolve(root,'arcade/visual-language.mjs'),resolve(out,'arcade/visual-language.mjs'));\r\nawait cp(resolve(root,'vendor'),resolve(out,'vendor'),{recursive:true});\r\nawait cp(resolve(root,'creature'),resolve(out,'creature'),{recursive:true,filter:p=>!/[\\\\/]source(?:[\\\\/]|$)/.test(p)&&!p.endsWith('.map')});\r\n// Optional roster parts use the same public packs as the existing coach creator.\r\nawait cp(resolve(root,'packs'),resolve(out,'packs'),{recursive:true});\r\nawait mkdir(resolve(out,'creature/assets'),{recursive:true});\r\nawait build({entryPoints:[resolve(root,'creature/source/phone.ts')],bundle:true,format:'esm',target:'es2022',minify:true,outfile:resolve(out,'creature/assets/phone.js'),plugins:[{name:'vendored-three',setup(b){b.onResolve({filter:/^three$/},()=>({path:'three',external:true}));}}]});\r\nawait cp(resolve(out,'creature/assets/phone.js'),resolve(out,'modules/dance/coach-phone.js'));\r\nconst sources=await sourceOffer(root);\r\nfor(const path of files){await mkdir(resolve(out,path,'..'),{recursive:true});await cp(resolve(root,path),resolve(out,path));if(!path.endsWith('.wav'))sources[path]=await readFile(resolve(root,path),'utf8');}\r\nfor(const folder of ['modules/dance/android-game','creature/source']){\r\n const walk=async dir=>{for(const e of await readdir(resolve(root,dir),{withFileTypes:true})){if(e.isSymbolicLink()||e.name.startsWith('.')||e.name==='node_modules')continue;const p=dir+'/'+e.name;if(e.isDirectory())await walk(p);else if(/\\.(mjs|ts|html|css)$/.test(p))sources[p]=await readFile(resolve(root,p),'utf8');}};\r\n await walk(folder);\r\n}\r\nfor(const path of ['creature/dance-pose.mjs','creature/power-anchors.mjs','coach-preview-space.mjs','character-phone-physics.mjs'])sources[path]=await readFile(resolve(root,path),'utf8');\r\nsources['scripts/build-dance-music-lab.mjs']=await readFile(new URL(import.meta.url),'utf8');\r\nsources['wrangler.dance-music.jsonc']=await readFile(resolve(root,'wrangler.dance-music.jsonc'),'utf8');\r\nasync function offerAndroid(folder){\r\n  for(const entry of await readdir(resolve(root,folder),{withFileTypes:true})){\r\n    if(entry.isSymbolicLink()||entry.name.startsWith('.')||entry.name==='build')continue;\r\n    const path=folder+'/'+entry.name;\r\n    if(entry.isDirectory())await offerAndroid(path);\r\n    else if(/\\.(java|xml|md|ps1)$/.test(path))sources[path]=await readFile(resolve(root,path),'utf8');\r\n  }\r\n}\r\ntry{await offerAndroid('android/dance-companion');}catch(e){if(e.code!=='ENOENT')throw e;}\r\nconst apk=resolve(root,'android/dance-companion/build/myr5-dance-game-v2-debug.apk');\r\nawait mkdir(resolve(out,'downloads'),{recursive:true});\r\nlet hasApk=false;try{if((await stat(apk)).size>0){await cp(apk,resolve(out,'downloads/myr5-dance-companion.apk'));hasApk=true;}}catch{}\r\nlet html=await readFile(resolve(out,'modules/dance/music-lab/index.html'),'utf8');\r\nif(!hasApk)html=html.replace(/<a[^>]*id=\"apk\"[^>]*>[^<]*<\\/a>/,'<p id=\"apk\">Android companion build is being prepared. The browser preview is available below.</p>');\r\nawait writeFile(resolve(out,'modules/dance/music-lab/index.html'),html);\r\nawait writeFile(resolve(out,'index.html'),'<!doctype html><meta http-equiv=\"refresh\" content=\"0;url=/modules/dance/music-lab/\"><a href=\"/modules/dance/music-lab/\">Open MYR5 music timing test</a>');\r\nawait writeFile(resolve(out,'source.json'),JSON.stringify(sources));\r\nawait writeFile(resolve(out,'modules/dance/source.json'),JSON.stringify(sources));\r\nawait writeFile(resolve(out,'_headers'),'/downloads/*.apk\\n  Content-Type: application/vnd.android.package-archive\\n  Content-Disposition: attachment; filename=\"myr5-dance-companion.apk\"\\n/*\\n  Permissions-Policy: microphone=(self), camera=(self)\\n  X-Content-Type-Options: nosniff\\n');\r\nconsole.log(JSON.stringify({out,hasApk}));\r\n","scripts/build-flight-coverage.mjs":"import {readdir,readFile,writeFile} from 'node:fs/promises';\r\nimport {resolve} from 'node:path';\r\nimport {pathToFileURL} from 'node:url';\r\n// Compact, auditable residency index from the exact shipped bake. An absent file is\r\n// unsupported coverage, rather than an empty desert tile inferred from HTTP 404.\r\nexport async function buildFlightCoverage(root){\r\n const manifest=JSON.parse(await readFile(resolve(root,'manifest.json'),'utf8')),rows={};\r\n for(const dir of await readdir(resolve(root,'vec/16'),{withFileTypes:true}))if(dir.isDirectory()&&/^\\d+$/.test(dir.name)){\r\n  const ys=(await readdir(resolve(root,'vec/16',dir.name))).filter(f=>/^\\d+\\.mvt(?:\\.gz)?$/.test(f)).map(f=>Number(f.split('.')[0])).sort((a,b)=>a-b),ranges=[];\r\n  for(const y of ys){const last=ranges.at(-1);if(last&&y<=last[1]+1)last[1]=y;else ranges.push([y,y]);}rows[dir.name]=ranges;\r\n }\r\n const code=`// Generated by scripts/build-flight-coverage.mjs from world/v1; regenerate when the bake changes.\\nexport const bakeVersion=${JSON.stringify(manifest.bakeVersion)};\\nconst rows=${JSON.stringify(rows)};\\nexport const coveredCell=c=>c.z===16&&!!rows[c.x]?.some(([a,b])=>c.y>=a&&c.y<=b);\\n`;\r\n await writeFile(new URL('../modules/coach-flight/coverage.mjs',import.meta.url),code);return Object.keys(rows).length;\r\n}\r\nif(process.argv[1]&&import.meta.url===pathToFileURL(resolve(process.argv[1])).href){if(!process.argv[2])throw new Error('Pass the world/v1 bake directory.');console.log('Flight coverage columns:',await buildFlightCoverage(process.argv[2]));}\r\n","scripts/build-food-fdc.mjs":"// One-off: maps each of the 101 Food-101 scanner labels to a USDA FoodData Central food and writes\r\n// food/nutrition-fdc.json ({label: {fdcId,description,servingName,servingG,kcal,proteinG,fatG,carbsG,\r\n// vitamins}}). Run with: node scripts/build-food-fdc.mjs\r\n//\r\n// Source data: FDC's bulk CSV downloads (Survey/FNDDS, SR Legacy, Foundation) instead of the rate-limited\r\n// (10 req/window) DEMO_KEY search API -- one download covers all 101 labels. Cached in FDC_CACHE_DIR\r\n// (default: an OS temp folder) so re-runs are instant.\r\n//\r\n// ponytail: hand-rolled `\",\"`-boundary CSV split (unescaping \"\" -> \") instead of a csv-parser dependency.\r\n// These bulk files never nest a literal `\",\"` inside a field (checked on 2026 vintage files). Swap in a\r\n// real parser if a future USDA release breaks that assumption.\r\nimport {existsSync} from 'node:fs';\r\nimport {mkdir, readFile, writeFile, readdir} from 'node:fs/promises';\r\nimport {tmpdir} from 'node:os';\r\nimport {join} from 'node:path';\r\nimport {spawnSync} from 'node:child_process';\r\n\r\nconst CACHE_DIR = process.env.FDC_CACHE_DIR || join(tmpdir(), 'myr5-fdc-cache');\r\nconst DATASETS = [\r\n // priority order: FNDDS (\"as eaten\" dishes) first, per the brief -- suits mixed dishes like pad thai.\r\n {key: 'survey', zip: 'FoodData_Central_survey_food_csv_2024-10-31.zip'},\r\n {key: 'sr_legacy', zip: 'FoodData_Central_sr_legacy_food_csv_2018-04.zip'},\r\n {key: 'foundation', zip: 'FoodData_Central_foundation_food_csv_2026-04-30.zip'},\r\n];\r\nconst FDC_BASE = 'https://fdc.nal.usda.gov/fdc-datasets/';\r\n\r\n// The 101 Food-101 labels, in the model's own order (confirmed against the ethz/food101 HF dataset\r\n// metadata; also what food-worker.mjs emits after replaceAll('_',' ')).\r\nconst LABELS = ['apple_pie', 'baby_back_ribs', 'baklava', 'beef_carpaccio', 'beef_tartare', 'beet_salad', 'beignets', 'bibimbap', 'bread_pudding', 'breakfast_burrito', 'bruschetta', 'caesar_salad', 'cannoli', 'caprese_salad', 'carrot_cake', 'ceviche', 'cheesecake', 'cheese_plate', 'chicken_curry', 'chicken_quesadilla', 'chicken_wings', 'chocolate_cake', 'chocolate_mousse', 'churros', 'clam_chowder', 'club_sandwich', 'crab_cakes', 'creme_brulee', 'croque_madame', 'cup_cakes', 'deviled_eggs', 'donuts', 'dumplings', 'edamame', 'eggs_benedict', 'escargots', 'falafel', 'filet_mignon', 'fish_and_chips', 'foie_gras', 'french_fries', 'french_onion_soup', 'french_toast', 'fried_calamari', 'fried_rice', 'frozen_yogurt', 'garlic_bread', 'gnocchi', 'greek_salad', 'grilled_cheese_sandwich', 'grilled_salmon', 'guacamole', 'gyoza', 'hamburger', 'hot_and_sour_soup', 'hot_dog', 'huevos_rancheros', 'hummus', 'ice_cream', 'lasagna', 'lobster_bisque', 'lobster_roll_sandwich', 'macaroni_and_cheese', 'macarons', 'miso_soup', 'mussels', 'nachos', 'omelette', 'onion_rings', 'oysters', 'pad_thai', 'paella', 'pancakes', 'panna_cotta', 'peking_duck', 'pho', 'pizza', 'pork_chop', 'poutine', 'prime_rib', 'pulled_pork_sandwich', 'ramen', 'ravioli', 'red_velvet_cake', 'risotto', 'samosa', 'sashimi', 'scallops', 'seaweed_salad', 'shrimp_and_grits', 'spaghetti_bolognese', 'spaghetti_carbonara', 'spring_rolls', 'steak', 'strawberry_shortcake', 'sushi', 'tacos', 'takoyaki', 'tiramisu', 'tuna_tartare', 'waffles'].map(s => s.replaceAll('_', ' '));\r\n\r\n// Query overrides for labels where a plain word match picks a bad FDC entry. The first 12 are the\r\n// existing aliases already hand-picked for the SR28 fuzzy search in nutrition.mjs -- reused here so\r\n// both lookup paths agree on what these dishes mean.\r\nconst QUERY = {\r\n 'french fries': 'potato french fries', 'ice cream': 'ice creams', 'hamburger': 'hamburger',\r\n 'hot dog': 'frankfurter', 'grilled salmon': 'salmon cooked', 'grilled cheese sandwich': 'sandwich cheese',\r\n 'macaroni and cheese': 'macaroni cheese', 'steak': 'beef steak cooked', 'french toast': 'french toast',\r\n 'fried rice': 'rice fried', 'sashimi': 'fish raw', 'omelette': 'egg omelet',\r\n // additional overrides found by inspecting two automated passes against the bulk CSVs (see the report\r\n // for which of these are close substitutes rather than an exact FDC match -- cannoli, macarons, poutine,\r\n // risotto and takoyaki have no FDC entry at all in Survey/SR Legacy/Foundation).\r\n 'escargots': 'snails cooked', 'foie gras': 'goose liver pate', 'cheese plate': 'cheese cheddar',\r\n 'spaghetti bolognese': 'spaghetti with meat sauce', 'spaghetti carbonara': 'pasta cream sauce meat restaurant',\r\n 'huevos rancheros': 'huevos rancheros', 'peking duck': 'duck peking style', 'prime rib': 'beef prime rib roasted',\r\n 'club sandwich': 'club sandwich', 'croque madame': 'sandwich egg cheese ham', 'cup cakes': 'cupcake',\r\n 'red velvet cake': 'red velvet cake', 'panna cotta': 'custard', 'shrimp and grits': 'shrimp and grits',\r\n 'beef carpaccio': 'beef carpaccio raw', 'tuna tartare': 'tuna raw', 'beef tartare': 'tartare',\r\n 'baby back ribs': 'pork ribs barbecued', 'filet mignon': 'beef steak lean cooked', 'pork chop': 'pork chop fat eaten',\r\n 'beet salad': 'beets fresh cooked', 'seaweed salad': 'seaweed pickled',\r\n 'fried calamari': 'squid fried', 'lobster roll sandwich': 'lobster salad', 'pulled pork sandwich': 'pulled pork barbecue sauce',\r\n 'caprese salad': 'mozzarella tomato basil oil vinegar dressing', 'lobster bisque': 'soup bisque',\r\n 'spring rolls': 'egg roll meatless', 'gyoza': 'wonton dumpling pot sticker fried', 'donuts': 'doughnut nfs',\r\n 'pizza': 'pizza cheese restaurant fast food', 'fish and chips': 'fish cod fried',\r\n // no FDC entry anywhere in these three datasets for these five -- nearest defensible single-food proxy:\r\n 'risotto': 'rice white cooked', 'poutine': 'potato french fries nfs', 'takoyaki': 'octopus',\r\n 'cannoli': 'cream puff eclair custard filled', 'macarons': 'cookie meringue',\r\n};\r\n// Labels with no real FDC entry for the dish itself -- QUERY above points them at the closest single-food\r\n// stand-in; flag that plainly regardless of the word-coverage score (which will read 1.0 since the query\r\n// was written to match the stand-in, not the dish).\r\nconst KNOWN_PROXY = new Set(['risotto', 'poutine', 'takoyaki', 'cannoli', 'macarons', 'beef carpaccio', 'shrimp and grits', 'fish and chips']);\r\n\r\nconst STOPWORDS = new Set(['and', 'with', 'the', 'a', 'of', 'in', 'style', 'ns', 'as', 'to', 'or', 'raw', 'cooked']);\r\nconst norm = s => s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\r\nconst words = text => norm(text).replace(/[^a-z0-9 ]/g, ' ').split(/\\s+/).filter(w => w.length > 1 && !STOPWORDS.has(w)).map(w => w.replace(/(ies)$/, 'y').replace(/s$/, ''));\r\n\r\n// --- tiny CSV reader: fields are always fully quoted and never contain the 4-char sequence `\",\"`. ---\r\n// A handful of Foundation lab-sample rows have a stray embedded newline inside the description (verified\r\n// against the 2026-04-30 dump, e.g. fdc_id 321829); a line-based split turns one such row into two ragged\r\n// ones. We drop rows that don't match the header's column count rather than reconstructing them -- they're\r\n// obscure lab-sample records no Food-101 label would ever match anyway.\r\nfunction parseCsv(text) {\r\n const lines = text.split(/\\r?\\n/);\r\n if (lines.at(-1) === '') lines.pop();\r\n const rows = lines.map(line => line.slice(1, -1).split('\",\"').map(f => f.replace(/\"\"/g, '\"')));\r\n const cols = rows[0].length;\r\n return rows.filter(r => r.length === cols);\r\n}\r\nasync function readCsv(path) { return parseCsv(await readFile(path, 'utf8')); }\r\n\r\nasync function ensureDataset({key, zip}) {\r\n const zipPath = join(CACHE_DIR, zip);\r\n const extractDir = join(CACHE_DIR, key);\r\n if (existsSync(extractDir)) return findCsvRoot(extractDir);\r\n await mkdir(CACHE_DIR, {recursive: true});\r\n if (!existsSync(zipPath)) {\r\n  console.log(`Downloading ${zip}...`);\r\n  const res = await fetch(FDC_BASE + zip);\r\n  if (!res.ok) throw new Error(`${zip}: HTTP ${res.status}`);\r\n  await writeFile(zipPath, Buffer.from(await res.arrayBuffer()));\r\n }\r\n await mkdir(extractDir, {recursive: true});\r\n const unzip = spawnSync('unzip', ['-oq', zipPath, '-d', extractDir]);\r\n if (unzip.status !== 0) throw new Error(`unzip ${zip} failed: ${unzip.stderr}`);\r\n return findCsvRoot(extractDir);\r\n}\r\nasync function findCsvRoot(dir) {\r\n const entries = await readdir(dir, {withFileTypes: true});\r\n const sub = entries.find(e => e.isDirectory());\r\n return sub ? join(dir, sub.name) : dir;\r\n}\r\n\r\nasync function loadDataset(spec) {\r\n const root = await ensureDataset(spec);\r\n const [foodRows, nutrientRows, portionRows, nutrientDictRows, measureUnitRows] = await Promise.all(\r\n  ['food.csv', 'food_nutrient.csv', 'food_portion.csv', 'nutrient.csv', 'measure_unit.csv'].map(f => readCsv(join(root, f)))\r\n );\r\n const foods = new Map(); // fdcId -> {description, dataType}\r\n for (const [fdcId, dataType, description] of foodRows.slice(1)) foods.set(fdcId, {description, dataType});\r\n const measureUnits = new Map();\r\n for (const [id, name] of measureUnitRows.slice(1)) measureUnits.set(id, name);\r\n // nutrient.csv: id,name,unit_name,nutrient_nbr,rank -- we key by nutrient_nbr (the stable USDA number).\r\n const nutrientIdByNumber = new Map();\r\n for (const [id, , , nbr] of nutrientDictRows.slice(1)) nutrientIdByNumber.set(nbr, id);\r\n return {foods, measureUnits, nutrientRows: nutrientRows.slice(1), portionRows: portionRows.slice(1), nutrientIdByNumber};\r\n}\r\n\r\n// USDA nutrient numbers we need, and (for vitamins) the %DV table -- mirrors food/pyramid-tiles.mjs's\r\n// VITAMIN_DV so the top-2-by-%DV pick here matches what that screen would compute at runtime.\r\nconst MACRO_NBR = {kcal: '208', proteinG: '203', fatG: '204', carbsG: '205'};\r\nconst VITAMIN_NBR = {vitaminA: '320', vitaminC: '401', vitaminD: '328', vitaminE: '323', vitaminB12: '418', folate: '417'};\r\nconst VITAMIN_DV = {vitaminA: 900, vitaminC: 90, vitaminD: 20, vitaminE: 15, vitaminB12: 2.4, folate: 400};\r\nconst VITAMIN_NAME = {vitaminA: 'Vitamin A', vitaminC: 'Vitamin C', vitaminD: 'Vitamin D', vitaminE: 'Vitamin E', vitaminB12: 'Vitamin B12', folate: 'Folate'};\r\nconst VITAMIN_UNIT = {vitaminA: 'µg RAE', vitaminC: 'mg', vitaminD: 'µg', vitaminE: 'mg', vitaminB12: 'µg', folate: 'µg'};\r\n\r\nfunction scoreCandidate(queryWords, description) {\r\n const descWords = words(description);\r\n const matched = queryWords.filter(w => descWords.includes(w)).length;\r\n const coverage = matched / queryWords.length;\r\n return {coverage, length: descWords.length};\r\n}\r\n\r\nfunction bestMatch(dataset, queryWords) {\r\n let best = null;\r\n for (const [fdcId, {description}] of dataset.foods) {\r\n  const {coverage, length} = scoreCandidate(queryWords, description);\r\n  if (coverage === 0) continue;\r\n  if (!best || coverage > best.coverage || (coverage === best.coverage && length < best.length)) {\r\n   best = {fdcId, description, coverage, length};\r\n  }\r\n }\r\n return best;\r\n}\r\n\r\n// Every usable (nonzero gram, nameable) FDC portion row for a food, in the dataset's own seq_num order.\r\nfunction rawPortions(dataset, fdcId) {\r\n const rows = dataset.portionRows.filter(r => r[1] === fdcId).sort((a, b) => Number(a[2] || 0) - Number(b[2] || 0));\r\n const out = [];\r\n for (const row of rows) {\r\n  const [, , , amount, measureUnitId, portionDescription, modifier, gramWeight] = row;\r\n  const grams = Number(gramWeight);\r\n  if (!Number.isFinite(grams) || grams <= 0) continue;\r\n  let name = portionDescription && portionDescription !== 'Quantity not specified' ? portionDescription : '';\r\n  // `modifier` is usually free text (\"serving\") but sometimes carries a bare measure_unit_id reference\r\n  // code (e.g. \"90000\") instead, typically paired with an empty portion_description -- skip those as\r\n  // unlabeled rather than showing the raw code.\r\n  if (!name && modifier && !/^\\d+$/.test(modifier)) name = amount ? `${amount} ${modifier}` : modifier;\r\n  if (!name) { const unit = dataset.measureUnits.get(measureUnitId); if (unit && unit !== 'undetermined') name = amount ? `${amount} ${unit}` : unit; }\r\n  if (name) out.push({name: name.trim(), servingG: grams});\r\n }\r\n return out;\r\n}\r\n\r\n// A photo is of a plated meal, not a lab reference amount -- FDC's *first* portion is often a garnish-size\r\n// or whole-recipe outlier (french fries \"1 fry\" 5 g, chocolate mousse \"1 recipe yield\" 808 g). Rule (per\r\n// conductor review): drop portions that read as a crumb/garnish/whole-batch unit unless that leaves\r\n// nothing; from what's left, take whichever is closest to a 150 g plate by log distance (log distance\r\n// treats \"half of 150\" and \"double 150\" as equally far, which a plain gram difference would not); if\r\n// everything left is still under 60 g (sushi, wings, dumplings, mussels...), report N of that single unit\r\n// instead (N = round(150/unit), capped at 12) rather than one unrealistic bite.\r\nconst EXCLUDE_PORTION_RE = /recipe|yield|miniature|bite|tiny|fry\\b|chip\\b|pod\\b|tbsp|tablespoon|teaspoon|cracker-size/i;\r\nconst DIP_LABELS = new Set(['guacamole', 'hummus']); // a dip is eaten a couple tablespoons at a time, not by the cup\r\nfunction closestByLogDistance(list, targetG) {\r\n return list.reduce((best, p) => {\r\n  const d = Math.abs(Math.log(p.servingG) - Math.log(targetG));\r\n  return !best || d < best.d ? {...p, d} : best;\r\n }, null);\r\n}\r\nfunction pluralizeUnit(name) {\r\n const m = /^1\\s+([a-zA-Z][a-zA-Z ]*)$/.exec(name); // \"1 cookie\" -> \"cookie\"; anything fancier stays \"pieces\"\r\n if (!m) return null;\r\n const noun = m[1];\r\n if (/^oz$/i.test(noun)) return noun; // abbreviated units (FDC's \"1 oz\") don't take an 's'\r\n if (/[^aeiou]y$/i.test(noun)) return noun.slice(0, -1) + 'ies'; // patty -> patties, not pattys\r\n return /[sxz]$|[cs]h$/i.test(noun) ? noun + 'es' : noun + 's';\r\n}\r\nfunction pickPortion(dataset, fdcId, label) {\r\n const all = rawPortions(dataset, fdcId);\r\n if (!all.length) return null;\r\n if (DIP_LABELS.has(label)) {\r\n  const tbsp = all.find(p => /tablespoon|tbsp/i.test(p.name));\r\n  if (tbsp) return {servingName: `2 ${tbsp.name.replace(/^1\\s+/, '')}`, servingG: round(tbsp.servingG * 2, 0), rule: 'dip: 2x tablespoon portion'};\r\n  const closest = closestByLogDistance(all, 30);\r\n  return {servingName: closest.name, servingG: closest.servingG, rule: 'dip: closest to 30 g'};\r\n }\r\n const kept = all.filter(p => !EXCLUDE_PORTION_RE.test(p.name));\r\n const candidates = kept.length ? kept : all;\r\n const prefix = kept.length ? '' : 'no non-excluded portion; ';\r\n if (candidates.every(p => p.servingG < 60)) {\r\n  const unit = candidates[0];\r\n  const n = Math.min(12, Math.max(1, Math.round(150 / unit.servingG)));\r\n  const name = n === 1 ? unit.name : `${n} ${pluralizeUnit(unit.name) || 'pieces'}`;\r\n  return {servingName: name, servingG: round(n * unit.servingG, 0), rule: `${prefix}small units only, x${n} (${unit.servingG} g each)`};\r\n }\r\n const closest = closestByLogDistance(candidates, 150);\r\n return {servingName: closest.name, servingG: closest.servingG, rule: `${prefix}closest to 150 g`};\r\n}\r\n\r\nfunction nutrientAmountPer100g(dataset, fdcId, nutrientNbr) {\r\n // Foundation/SR Legacy food_nutrient.csv key nutrient_id by nutrient.csv's internal id (e.g. 1008 for\r\n // Energy); Survey/FNDDS's food_nutrient.csv keys it by the raw USDA nutrient number instead (208). No\r\n // id/nbr collide (checked), so matching either is safe and dataset-agnostic.\r\n const nutrientId = dataset.nutrientIdByNumber.get(nutrientNbr);\r\n const row = dataset.nutrientRows.find(r => r[1] === fdcId && (r[2] === nutrientId || r[2] === nutrientNbr));\r\n return row ? Number(row[3]) : null;\r\n}\r\n\r\nfunction round(v, d = 1) { const m = 10 ** d; return Math.round(v * m) / m; }\r\n\r\nasync function buildEntry(label, datasets) {\r\n const overrideQuery = QUERY[label] || label;\r\n const queryWords = words(overrideQuery);\r\n let picked = null, dataset = null;\r\n for (const ds of datasets) {\r\n  const candidate = bestMatch(ds, queryWords);\r\n  if (candidate && candidate.coverage === 1) { picked = candidate; dataset = ds; break; }\r\n  if (candidate && (!picked || candidate.coverage > picked.coverage)) { picked = candidate; dataset = ds; }\r\n }\r\n if (!picked) return {label, entry: null, note: 'no FDC match at all'};\r\n const {fdcId, description, coverage} = picked;\r\n const portion = pickPortion(dataset, fdcId, label);\r\n const servingG = portion?.servingG ?? 100;\r\n const servingName = portion?.servingName ?? '100 g';\r\n const scale = servingG / 100;\r\n const macros = {};\r\n for (const [key, nbr] of Object.entries(MACRO_NBR)) {\r\n  const per100 = nutrientAmountPer100g(dataset, fdcId, nbr);\r\n  macros[key] = per100 == null ? null : round(per100 * scale, key === 'kcal' ? 0 : 1);\r\n }\r\n const vitaminPcts = [];\r\n for (const [key, nbr] of Object.entries(VITAMIN_NBR)) {\r\n  const per100 = nutrientAmountPer100g(dataset, fdcId, nbr);\r\n  if (per100 == null) continue;\r\n  const amount = round(per100 * scale, 2);\r\n  const pct = amount / VITAMIN_DV[key];\r\n  if (pct > 0) vitaminPcts.push({key, amount, pct});\r\n }\r\n vitaminPcts.sort((a, b) => b.pct - a.pct);\r\n const vitamins = vitaminPcts.slice(0, 2).map(v => ({name: VITAMIN_NAME[v.key], amount: v.amount, unit: VITAMIN_UNIT[v.key]}));\r\n const notes = [];\r\n if (!portion) notes.push('fell back to 100 g (no usable FDC portion)');\r\n if (coverage < 1 || KNOWN_PROXY.has(label)) notes.push(`approximate match only (word coverage ${coverage.toFixed(2)}) -- no exact FDC entry for this dish`);\r\n return {\r\n  label,\r\n  entry: {fdcId: Number(fdcId), description, servingName, servingG, ...macros, vitamins},\r\n  note: notes.join('; ') || null,\r\n  coverage, dataset: dataset.key, portionRule: portion?.rule ?? 'no FDC portion at all',\r\n };\r\n}\r\n\r\nconst datasets = [];\r\nfor (const spec of DATASETS) { console.log(`Loading ${spec.key}...`); datasets.push({key: spec.key, ...await loadDataset(spec)}); }\r\n\r\nconst result = {};\r\nconst fallbacks = [];\r\nconst report = [];\r\nfor (const label of LABELS) {\r\n const {entry, note, coverage, dataset, portionRule} = await buildEntry(label, datasets);\r\n if (!entry) { fallbacks.push(`${label}: NO MATCH`); continue; }\r\n result[label] = entry;\r\n report.push(`${label.padEnd(28)} -> [${dataset}] fdc:${entry.fdcId} \"${entry.description}\" (coverage ${coverage.toFixed(2)}, ${entry.servingName}, ${entry.servingG} g, ${entry.kcal} kcal) [${portionRule}]`);\r\n if (note) fallbacks.push(`${label}: ${note}`);\r\n}\r\n\r\nconsole.log(report.join('\\n'));\r\nconsole.log('\\n--- fallbacks / notes ---');\r\nconsole.log(fallbacks.length ? fallbacks.join('\\n') : '(none)');\r\nconsole.log(`\\n${Object.keys(result).length} / ${LABELS.length} labels resolved.`);\r\n\r\nawait mkdir('food', {recursive: true});\r\nawait writeFile('food/nutrition-fdc.json', JSON.stringify(result, null, 1) + '\\n');\r\nconsole.log('Wrote food/nutrition-fdc.json');\r\n","scripts/build-gala-avatar.mjs":"// Bundles the Gala DFO rig (pod/gala-rig) into the classic script pod/gala-avatar.js. The site's\r\n// gala/avatar.js is a byte copy of this file.\r\nimport { build } from 'esbuild';\r\nconst banner = `/*! MOM Inc Gala avatar: a procedural character rig drawn as a 64x96 pixel sprite.\r\n Rig, pieces, animation and pixelize law ported from DFO Online (github.com/Lattymoy/daggerfall-js-source),\r\n MIT (c) 2026 Lattymoy; includes Daggerfall Unity-derived code, MIT (c) 2009-2023 Daggerfall Workshop.\r\n Full notices: LICENSE-DFO.txt beside this file */`;\r\nawait build({ entryPoints: ['./pod/gala-rig/entry.mjs'], bundle: true, format: 'iife', target: 'es2020', minify: true, legalComments: 'none', banner: { js: banner }, outfile: 'pod/gala-avatar.js' });\r\n","scripts/build-hand.mjs":"import {spawnSync} from 'node:child_process';\r\nimport {readFile,writeFile,cp} from 'node:fs/promises';\r\nconst source=new URL('../handborne/source/',import.meta.url);\r\n// One authoritative implementation for both editors. This also catches accidental drift.\r\nfor(const name of ['material-language.ts','material-patterns.ts','material-refinement.ts','palette-finishes.ts','palettes.json']){\r\n await cp(new URL('../creature/source/creator/'+name,import.meta.url),new URL('app/'+name,source));\r\n}\r\nconst result=spawnSync(process.execPath,['scripts/build-embedded.mjs'],{cwd:source,stdio:'inherit'});\r\nif(result.status!==0)throw Error('Hand customizer build failed.');\r\nconst out=new URL('dist/embedded/',source),target=new URL('../handborne/',import.meta.url);\r\nawait cp(new URL('assets/',out),new URL('assets/',target),{recursive:true});\r\nawait cp(new URL('companion.mjs',out),new URL('companion.mjs',target));\r\nawait cp(new URL('unlock-bridge.mjs',out),new URL('unlock-bridge.mjs',target));\r\nconst built=await readFile(new URL('index.html',out),'utf8');\r\nconst js=built.match(/src=\"([^\"]+\\.js)\"/)?.[1],css=built.match(/href=\"([^\"]+\\.css)\"/)?.[1];\r\nif(!js||!css)throw Error('Hand build entrypoints missing.');\r\nlet index=await readFile(new URL('index.html',target),'utf8');\r\nindex=index.replace(/data-module=\"[^\"]+\"/,`data-module=\"${js}\"`).replace(/href=\"\\/handborne\\/assets\\/[^\\\"]+\\.css\"/,`href=\"${css}\"`);\r\nawait writeFile(new URL('index.html',target),index);\r\nconsole.log('Hand customizer shares current Coach materials.');\r\n","scripts/build-npc-catalog.mjs":"import {writeFile} from 'node:fs/promises';\r\nimport {textureRewardPool,colourRewardPool} from '../battle-pass-rewards.mjs';\r\n// World modules are served directly, unlike app-runtime. Keep the TS creator\r\n// catalog behind this generated data boundary so mobile browsers never parse it.\r\nconst content={textures:textureRewardPool().filter(i=>i.kind==='texture'),colours:colourRewardPool().filter(i=>i.kind==='color')};\r\nawait writeFile('modules/coach-world-rts/npc-cosmetic-catalog.mjs','// Generated from the real reward pools by scripts/build-npc-catalog.mjs.\\nexport const NPC_COSMETICS='+JSON.stringify(content)+';\\n');\r\n","scripts/build-optional-packs.mjs":"// Rank 2b: builds optional-art packs from plan/assets-inbox/ + asset-manifest.json\r\n// (paths are given as arguments; this script never assumes the plan repo's\r\n// location) into the /packs/<pack-id>/<version>/... + /packs/index.json layout,\r\n// signed with the EXISTING pack-catalog mechanism (packs/catalog-trust.mjs,\r\n// packs/catalog-config.mjs — the same Ed25519 catalog scheme\r\n// scripts/provision-pack-catalog-keys.mjs provisions as a GitHub secret).\r\n//\r\n// Never reads/prints/copies the real signing key: a key file path only ever\r\n// flows into crypto.createPrivateKey/sign, never into a log line, and this\r\n// script never writes one to disk. With no key, it produces an unsigned dry\r\n// run and says so plainly — the output cannot pass verifyPackCatalog().\r\nimport { createHash, createPrivateKey, createPublicKey, sign as edSign } from 'node:crypto';\r\nimport { existsSync } from 'node:fs';\r\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\r\nimport { basename, extname, join } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport {\r\n  PACK_CATALOG_PROTOCOL, PACK_CATALOG_SCHEMA, PACK_ASSET_MAX_BYTES, PACK_TOTAL_MAX_BYTES,\r\n  canonicalCatalogPayload, verifyPackCatalog,\r\n} from '../packs/catalog-trust.mjs';\r\nimport { productionCatalogKeyring } from '../packs/catalog-config.mjs';\r\nimport { PACK_HOST_BASE_URL } from '../packs/pack-host-config.mjs';\r\n\r\nconst DEFAULT_VERSION = '1.0.0';\r\nconst DEFAULT_MIN_APP_VERSION = '1.0.0';\r\nconst SPDX_ORIGINAL = 'LicenseRef-MomInc-Original';\r\n\r\nexport class PackSizeError extends Error {}\r\n\r\nexport const sha256Hex = bytes => createHash('sha256').update(bytes).digest('hex');\r\n\r\nfunction concatBytes(chunks) {\r\n  const out = new Uint8Array(chunks.reduce((n, c) => n + c.byteLength, 0));\r\n  let offset = 0;\r\n  for (const chunk of chunks) { out.set(chunk, offset); offset += chunk.byteLength; }\r\n  return out;\r\n}\r\n\r\nexport async function loadAssetManifest(manifestPath) {\r\n  const parsed = JSON.parse(await readFile(manifestPath, 'utf8'));\r\n  if (!Array.isArray(parsed.assets)) throw new Error(`${manifestPath} is missing an \"assets\" array`);\r\n  return parsed;\r\n}\r\n\r\n/**\r\n * Splits manifest entries into ones whose file exists in assets-inbox\r\n * (`found`, with bytes/sha256 computed) and ones that don't yet (`pending`,\r\n * e.g. the \"status\": \"template\" placeholder that ships with an empty inbox).\r\n */\r\nexport async function resolveAssets(manifest, inboxDir) {\r\n  const found = [], pending = [];\r\n  for (const entry of manifest.assets) {\r\n    const filePath = join(inboxDir, entry.filename);\r\n    if (!existsSync(filePath)) { pending.push(entry); continue; }\r\n    const data = await readFile(filePath);\r\n    found.push({ ...entry, bytes: data.byteLength, sha256: sha256Hex(data), data });\r\n  }\r\n  return { found, pending };\r\n}\r\n\r\nfunction groupByPack(assets) {\r\n  const packs = new Map();\r\n  for (const asset of assets) {\r\n    if (!asset.pack) throw new Error(`asset \"${asset.filename}\" has no \"pack\" id in asset-manifest.json`);\r\n    if (!packs.has(asset.pack)) packs.set(asset.pack, []);\r\n    packs.get(asset.pack).push(asset);\r\n  }\r\n  return packs;\r\n}\r\n\r\nconst spdxFor = license => (!license || license === 'original') ? SPDX_ORIGINAL : license;\r\n\r\n/**\r\n * Builds one pack: content-addressed asset files, a pack-lifecycle-shaped\r\n * manifest.json (the shape packs/pack-lifecycle.mjs downloads and verifies\r\n * against), and the catalog entry pack-license-surface.mjs / catalog-state.mjs\r\n * expect inside /packs/index.json. Throws PackSizeError if any asset or the\r\n * pack total is over the existing catalog's own caps (packs/catalog-trust.mjs\r\n * PACK_ASSET_MAX_BYTES / PACK_TOTAL_MAX_BYTES) rather than emit an\r\n * unverifiable catalog silently.\r\n */\r\nexport function buildPack({ packId, version = DEFAULT_VERSION, assets, minAppVersion = DEFAULT_MIN_APP_VERSION, fallbackId = 'builtin:none', baseUrl = PACK_HOST_BASE_URL }) {\r\n  const oversized = assets.filter(a => a.bytes > PACK_ASSET_MAX_BYTES);\r\n  if (oversized.length) throw new PackSizeError(`pack \"${packId}\": ${oversized.length} asset(s) exceed the ${PACK_ASSET_MAX_BYTES}-byte catalog cap: ${oversized.map(a => `${a.filename} (${a.bytes}B)`).join(', ')}`);\r\n\r\n  const manifestAssets = assets.map(asset => ({ path: `packs/${packId}/${version}/assets/${asset.sha256}${extname(asset.filename)}`, bytes: asset.bytes, sha256: asset.sha256 }));\r\n  const packManifest = { packId, version, minAppVersion, sha256: sha256Hex(concatBytes(assets.map(a => a.data))), assets: manifestAssets };\r\n  const manifestBytes = new TextEncoder().encode(JSON.stringify(packManifest));\r\n  const manifestHash = sha256Hex(manifestBytes);\r\n  const manifestPath = `/packs/${packId}/${version}/manifest.${manifestHash}.json`;\r\n\r\n  const dependencies = assets.map((asset, i) => ({\r\n    name: basename(asset.filename, extname(asset.filename)),\r\n    version,\r\n    path: `/${manifestAssets[i].path}`,\r\n    bytes: asset.bytes,\r\n    sha256: asset.sha256,\r\n    spdx: spdxFor(asset.license),\r\n    sourceUrl: /^https:\\/\\//.test(asset.source || '') ? asset.source : `${baseUrl}/${manifestAssets[i].path}`,\r\n    attribution: asset.owner || 'MOM INC',\r\n  }));\r\n  const totalBytes = manifestBytes.byteLength + dependencies.reduce((sum, d) => sum + d.bytes, 0);\r\n  if (totalBytes > PACK_TOTAL_MAX_BYTES) throw new PackSizeError(`pack \"${packId}\": total ${totalBytes}B exceeds the ${PACK_TOTAL_MAX_BYTES}-byte catalog cap`);\r\n\r\n  return {\r\n    catalogEntry: { packId, version, fallbackId, manifest: { path: manifestPath, bytes: manifestBytes.byteLength, sha256: manifestHash }, dependencies, totalBytes },\r\n    files: [...assets.map((a, i) => ({ path: manifestAssets[i].path, data: a.data })), { path: `packs/${packId}/${version}/manifest.${manifestHash}.json`, data: manifestBytes }],\r\n  };\r\n}\r\n\r\nexport function deriveKeyId(privateKeyJwk, keyring) {\r\n  const publicJwk = createPublicKey(createPrivateKey({ key: privateKeyJwk, format: 'jwk' })).export({ format: 'jwk' });\r\n  const match = keyring.find(entry => entry.publicKey.x === publicJwk.x);\r\n  if (!match) throw new Error('signing key does not match any entry in the production keyring (packs/catalog-keys.generated.mjs) — wrong key file?');\r\n  return match.keyId;\r\n}\r\n\r\nexport function signCatalog(unsignedCatalog, privateKeyJwk, keyId) {\r\n  const candidate = { ...unsignedCatalog, keyId, alg: 'Ed25519', signature: '' };\r\n  const signature = edSign(null, Buffer.from(canonicalCatalogPayload(candidate)), createPrivateKey({ key: privateKeyJwk, format: 'jwk' })).toString('base64');\r\n  return { ...candidate, signature };\r\n}\r\n\r\n/**\r\n * Full build: reads the manifest + inbox, builds every declared pack,\r\n * writes /packs/<id>/<version>/... plus /packs/index.json under outDir, and\r\n * signs the catalog only if keyPath is given (env var or CLI argument) —\r\n * otherwise returns an explicit unsigned dry run. The private key file's\r\n * *contents* are read only to sign in-memory; they are never logged, never\r\n * copied into outDir, and never returned in the result.\r\n */\r\nexport async function buildOptionalPacks({ manifestPath, inboxDir, outDir, keyPath, generation = 1, writeManifest = false, keyring = productionCatalogKeyring() }) {\r\n  const manifest = await loadAssetManifest(manifestPath);\r\n  const { found, pending } = await resolveAssets(manifest, inboxDir);\r\n  const grouped = groupByPack(found);\r\n\r\n  const packs = [], errors = [];\r\n  for (const [packId, assets] of grouped) {\r\n    try { packs.push(buildPack({ packId, version: manifest.packVersions?.[packId] ?? DEFAULT_VERSION, assets })); }\r\n    catch (error) { errors.push({ packId, message: error.message }); }\r\n  }\r\n\r\n  for (const pack of packs) for (const file of pack.files) {\r\n    const target = join(outDir, file.path);\r\n    await mkdir(join(target, '..'), { recursive: true });\r\n    await writeFile(target, file.data);\r\n  }\r\n\r\n  let catalog = { schema: PACK_CATALOG_SCHEMA, protocol: PACK_CATALOG_PROTOCOL, generation, publishedAt: Date.now(), packs: packs.map(p => p.catalogEntry) };\r\n  let signed = false, keyId = null, verified = null;\r\n  if (keyPath) {\r\n    const jwk = JSON.parse(await readFile(keyPath, 'utf8')); // never logged, never persisted\r\n    keyId = deriveKeyId(jwk, keyring);\r\n    catalog = signCatalog(catalog, jwk, keyId);\r\n    signed = true;\r\n    verified = await verifyPackCatalog(catalog, keyring);\r\n  } else {\r\n    catalog = { ...catalog, keyId: null, alg: 'Ed25519', signature: null, dryRun: true };\r\n  }\r\n  await mkdir(join(outDir, 'packs'), { recursive: true });\r\n  await writeFile(join(outDir, 'packs', 'index.json'), JSON.stringify(catalog, null, 2) + '\\n');\r\n\r\n  if (writeManifest && found.length) {\r\n    const byFilename = new Map(found.map(a => [a.filename, a]));\r\n    manifest.assets = manifest.assets.map(entry => byFilename.has(entry.filename) ? { ...entry, bytes: byFilename.get(entry.filename).bytes, sha256: byFilename.get(entry.filename).sha256 } : entry);\r\n    await writeFile(manifestPath, JSON.stringify(manifest, null, 2));\r\n  }\r\n\r\n  const sizes = packs.map(p => ({ packId: p.catalogEntry.packId, version: p.catalogEntry.version, assets: p.catalogEntry.dependencies.length, totalBytes: p.catalogEntry.totalBytes }));\r\n  return { signed, keyId, verified, packs: sizes, pendingAssets: pending.map(p => p.filename), errors, outDir };\r\n}\r\n\r\nasync function main() {\r\n  const args = Object.fromEntries(process.argv.slice(2).map(arg => { const [k, ...v] = arg.replace(/^--/, '').split('='); return [k, v.join('=') || true]; }));\r\n  const manifestPath = args.manifest || process.env.MYR5_PACK_ASSET_MANIFEST;\r\n  const inboxDir = args.inbox || process.env.MYR5_PACK_ASSETS_INBOX;\r\n  const outDir = args.out || process.env.MYR5_PACK_OUT_DIR;\r\n  const keyPath = args.key || process.env.MYR5_PACK_CATALOG_SIGNING_KEY_FILE || null;\r\n  if (!manifestPath || !inboxDir || !outDir) throw new Error('usage: node build-optional-packs.mjs --manifest=<asset-manifest.json> --inbox=<assets-inbox dir> --out=<output dir> [--key=<private jwk file>] [--generation=N] [--write-manifest]');\r\n  const result = await buildOptionalPacks({ manifestPath, inboxDir, outDir, keyPath, generation: args.generation ? Number(args.generation) : 1, writeManifest: Boolean(args['write-manifest']) });\r\n  console.log(JSON.stringify(result, null, 2));\r\n  if (!result.signed) console.log('\\nUNSIGNED DRY RUN — no key file was given, so /packs/index.json has signature:null and will NOT pass verifyPackCatalog(). Pass --key=<path-to-private-jwk> (never commit that file) to produce a real signed catalog.');\r\n  if (result.pendingAssets.length) console.log(`\\n${result.pendingAssets.length} manifest entr${result.pendingAssets.length === 1 ? 'y' : 'ies'} not yet in assets-inbox, skipped: ${result.pendingAssets.join(', ')}`);\r\n  if (result.errors.length) { console.log(`\\n${result.errors.length} pack(s) failed to build:`); for (const e of result.errors) console.log(`  - ${e.packId}: ${e.message}`); }\r\n}\r\n\r\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) await main();\r\n","scripts/build-post-download-sections.mjs":"// Deterministic builder for per-track resumable post-download sections.\r\n// It writes content bundles and unsigned chunk-manifest templates outside core/Sites.\r\nimport { createHash } from 'node:crypto';\r\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport { parseMaterialsBaseUrl } from './material-release.mjs';\r\n\r\nconst MAP_ORDER = ['basecolor','normal','roughness','height','metalness','tintMask','ao','opacity','emissive','preview'];\r\nconst hash = bytes => createHash('sha256').update(bytes).digest('hex');\r\n\r\nfunction makeBundle(entries) {\r\n  const index = Object.create(null);\r\n  let offset = 0;\r\n  for (const [name, bytes] of entries) { if(!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(name)||name.includes('..')||Object.hasOwn(index,name)||!bytes.byteLength)throw new Error('unsafe or duplicate bundle entry');index[name] = { offset, bytes: bytes.byteLength, sha256: hash(bytes) }; offset += bytes.byteLength; }\r\n  const header = Buffer.from(JSON.stringify(index));\r\n  const prefix = Buffer.alloc(4); prefix.writeUInt32BE(header.byteLength);\r\n  return Buffer.concat([prefix, header, ...entries.map(([, bytes]) => bytes)]);\r\n}\r\n\r\nasync function chunkRecord(bytes, { path, url, chunkBytes }) {\r\n  const chunks = [];\r\n  for (let offset = 0, index = 0; offset < bytes.byteLength; offset += chunkBytes, index++) {\r\n    const data = bytes.subarray(offset, Math.min(offset + chunkBytes, bytes.byteLength));\r\n    chunks.push({ index, offset, bytes: data.byteLength, sha256: hash(data), url });\r\n  }\r\n  return { path, bytes: bytes.byteLength, sha256: hash(bytes), chunks };\r\n}\r\n\r\nexport async function buildPostDownloadSections({ root = process.cwd(), outputDir = join(root, 'plan/assets-inbox/post-download-sections'), baseUrl = process.env.MYR5_MATERIALS_BASE_URL, chunkBytes = 1024 * 1024, materialize = true } = {}) {\r\n  if (!Number.isInteger(chunkBytes) || chunkBytes < 1 || chunkBytes > 1024 * 1024) throw new Error('chunk size must obey the 1 MiB chunk-delivery policy');\r\n  baseUrl=parseMaterialsBaseUrl(baseUrl).baseUrl;\r\n  const output = resolve(outputDir), generated = [];\r\n  const sections = [];\r\n  const sourcePackets = await Promise.all(['forged-realms','celestial-rift'].map(async packet => ({\r\n    packet,\r\n    root: join(root, `plan/assets-inbox/creature-skins/${packet}`),\r\n    items: JSON.parse(await readFile(join(root, `plan/assets-inbox/creature-skins/${packet}/source-manifest.json`), 'utf8')).items,\r\n  })));\r\n  const trackNames = [...new Set(sourcePackets.flatMap(packet => packet.items.map(item => item.track)))].sort();\r\n  const expectedTracks=['arms','cardio','chest','glutes','martial-arts','meditation','quads','yoga'];\r\n  if(JSON.stringify(trackNames)!==JSON.stringify(expectedTracks))throw new Error('expected exactly eight known track packets');\r\n  const ids=new Set();for(const source of sourcePackets)for(const item of source.items){if(!/^creature-[a-z0-9-]+$/.test(item.id)||ids.has(item.id)||![1,2,3].includes(item.collectionSlot))throw new Error('invalid source skin identity');ids.add(item.id);}\r\n  if(ids.size!==48)throw new Error('expected exactly 48 source skins');\r\n  for (const track of trackNames) {\r\n    const packId = `track-${track}`, packDir = join(output, packId), entries = [];\r\n    for (const source of sourcePackets) {\r\n      for (const item of source.items.filter(row => row.track === track).sort((a,b) => a.collectionSlot - b.collectionSlot)) {\r\n        for (const map of MAP_ORDER) {\r\n          const meta = item.maps[map];\r\n          if (!meta) continue;\r\n          if(!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(meta.file||'')||meta.file.includes('..'))throw new Error('unsafe source texture path');\r\n          const bytes = await readFile(join(source.root, meta.file));\r\n          if (bytes.byteLength !== meta.bytes || hash(bytes) !== meta.sha256) throw new Error(`source packet checksum mismatch: ${item.id}/${map}`);\r\n          entries.push([`${item.id}/${map}`, bytes]);\r\n        }\r\n      }\r\n    }\r\n    const bundle = makeBundle(entries), rel = `assets/${track}.m5bundle`;\r\n    if(materialize){await mkdir(dirname(join(packDir, rel)), { recursive: true });await writeFile(join(packDir, rel), bundle);}\r\n    const url = `${baseUrl}/${packId}/1.0.0/${track}.m5bundle`;\r\n    const assets = [await chunkRecord(bundle, { path: rel, url, chunkBytes })];\r\n    sections.push(unsignedSection({ packId, assets }));\r\n    generated.push({ id: packId, assets: assets.length, bytes: bundle.byteLength, directory: packDir });\r\n  }\r\n\r\n  // Stage the six scene ships and fold all 24 tiny biome plates into one indexed asset.\r\n  const shipRoot = join(root, 'plan/assets-inbox/ships'), shipPackDir = join(output, 'coach-ships-biomes');\r\n  const shipAssets = [];\r\n  for (const ship of ['supportive','direct','analytical','playful','calm','mom']) {\r\n    const bytes = await readFile(join(shipRoot, `${ship}.glb`));\r\n    const rel = `assets/ships/${ship}.glb`;\r\n    if(materialize){await mkdir(dirname(join(shipPackDir, rel)), { recursive: true });await writeFile(join(shipPackDir, rel), bytes);}\r\n    shipAssets.push(await chunkRecord(bytes, { path: rel, url: `${baseUrl}/coach-ships-biomes/1.0.0/${ship}.glb`, chunkBytes }));\r\n  }\r\n  const biomeRoot = join(root, 'plan/assets-inbox/biomes'), biomeFiles = ['original','patch','fold','lattice','wax','fungi','glass','bristle','cork','coral','metal','feather','moss','stormcloud','frost','ember','shadow','dragon','spring','vine','chitin','puff','rock','shag'].map(name => `${name}.webp`);\r\n  const biomeEntries = [];\r\n  for (const filename of biomeFiles) biomeEntries.push([filename, await readFile(join(biomeRoot, filename))]);\r\n  const biomeBundle = makeBundle(biomeEntries), biomePath = 'assets/biomes.m5bundle';\r\n  if(materialize){await mkdir(dirname(join(shipPackDir, biomePath)), { recursive: true });await writeFile(join(shipPackDir, biomePath), biomeBundle);}\r\n  shipAssets.push(await chunkRecord(biomeBundle, { path: biomePath, url: `${baseUrl}/coach-ships-biomes/1.0.0/biomes.m5bundle`, chunkBytes }));\r\n  sections.push(unsignedSection({ packId: 'coach-ships-biomes', assets: shipAssets }));\r\n  generated.push({ id: 'coach-ships-biomes', assets: shipAssets.length, bytes: shipAssets.reduce((n,a)=>n+a.bytes,0), directory: shipPackDir });\r\n\r\n  for (const section of sections) {\r\n    const dir = join(output, section.packId);\r\n    await mkdir(dir, { recursive: true });\r\n    await writeFile(join(dir, 'chunk-manifest.unsigned.json'), JSON.stringify(section, null, 2) + '\\n');\r\n    await writeFile(join(dir, 'section.json'), JSON.stringify({ id: section.packId, delivery: 'resumable-verified-chunks', signed: false, activation: 'blocked-until-signed-and-hosted', bytes: section.assets.reduce((n,a)=>n+a.bytes,0), assets: section.assets.map(a=>a.path) }, null, 2) + '\\n');\r\n  }\r\n  return { signed: false, activation: 'blocked-until-signed-and-hosted', sections: generated };\r\n}\r\n\r\nfunction unsignedSection({ packId, assets }) {\r\n  return { schema: 'mom-material-chunks-v1', packId, version: '1.0.0', keyId: 'mom-material-production-v1', assets, signature: '' };\r\n}\r\n\r\nexport function unpackBundle(value) {\r\n  const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);\r\n  if (bytes.byteLength < 4) throw new Error('truncated bundle header');\r\n  const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);\r\n  if (length < 2 || length > bytes.byteLength - 4) throw new Error('invalid bundle index length');\r\n  const index = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + length)));\r\n  const contentStart = 4 + length, output = new Map();\r\n  for (const [name, entry] of Object.entries(index)) {\r\n    if (!Number.isSafeInteger(entry.offset) || !Number.isSafeInteger(entry.bytes) || entry.offset < 0 || entry.bytes < 0 || contentStart + entry.offset + entry.bytes > bytes.byteLength) throw new Error(`invalid bundle range: ${name}`);\r\n    const item = bytes.slice(contentStart + entry.offset, contentStart + entry.offset + entry.bytes);\r\n    if (hash(item) !== entry.sha256) throw new Error(`bundle entry digest mismatch: ${name}`);\r\n    output.set(name, item);\r\n  }\r\n  return output;\r\n}\r\n\r\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\r\n  const arg = name => process.argv.find(x => x.startsWith(`--${name}=`))?.slice(name.length + 3);\r\n  console.log(JSON.stringify(await buildPostDownloadSections({ outputDir: arg('out') || undefined, baseUrl: arg('base-url') || undefined, materialize: !process.argv.includes('--metadata-only') }), null, 2));\r\n}\r\n","scripts/build-pyramid-scanner.py":"# Pyramid food scanner GLB: preserve the authored mesh (ratio 1.0), split dials, add screens + lens.\r\n# Run: blender -b -P scripts/build-pyramid-scanner.py -- food/pyramid-scanner.glb\r\n# Do not decimate or quantize this scanner; its controls and sculpted body stay at authored resolution.\r\nimport bpy, bmesh, math, json, sys, hashlib\r\nfrom mathutils import Vector\r\nfrom mathutils.geometry import intersect_line_plane\r\nSRC=r\"C:/Users/ianmy/Downloads/pyramid+nutrition+robot+3d+model.glb\"\r\nOUT=sys.argv[sys.argv.index('--')+1]\r\nRATIO=float(sys.argv[sys.argv.index('--')+2]) if len(sys.argv)>sys.argv.index('--')+2 else 1.0\r\nif RATIO != 1.0:\r\n    raise ValueError('Quality-preserving pyramid build requires decimation ratio 1.0')\r\nwith open(SRC,'rb') as source_file:\r\n    print('PYRAMID_SOURCE_SHA256',hashlib.sha256(source_file.read()).hexdigest())\r\nKNOB_R=0.046\r\nS=1.15/800  # ortho_scale / px, matches render.py\r\nSCREENS={  # name box nudged right to cover the bezel\r\n  # view-0 pixel boxes (x0,y0,x1,y1) of the baked screens\r\n 'screen_name':(350,240,461,318),'screen_calories':(320,339,395,400),'screen_protein':(403,339,480,400),\r\n 'screen_fat':(286,418,352,490),'screen_carbs':(366,418,435,490),'screen_vitamins':(446,418,514,490)}\r\nLENS=(0,400,128)\r\n# dial cap centres + axes (Blender coords), measured head-on in dial.py/prof.py\r\nDIALS=[((0.2539,-0.1033,0.4261),(0.845,-0.34,0.414)),((-0.2497,-0.0937,0.4257),(-0.877,-0.289,0.385)),((0.1157,0.2132,0.4222),(0.133,0.895,0.426))]\r\n\r\nbpy.ops.wm.read_factory_settings(use_empty=True)\r\nbpy.ops.import_scene.gltf(filepath=SRC)\r\nsc=bpy.context.scene\r\nbody=[o for o in sc.objects if o.type=='MESH'][0]; body.name='body'\r\nbpy.context.view_layer.objects.active=body; body.select_set(True)\r\nbpy.ops.object.transform_apply(location=True,rotation=True,scale=True)\r\nm=body.modifiers.new('d','DECIMATE'); m.ratio=RATIO; bpy.ops.object.modifier_apply(modifier='d')\r\ndg=bpy.context.evaluated_depsgraph_get()\r\n\r\ndef ray(view,px,py):\r\n    r=math.radians(view); loc=Vector((2*math.sin(r),-2*math.cos(r),0.55))\r\n    fwd=(Vector((0,0,0.49))-loc).normalized(); right=fwd.cross(Vector((0,0,1))).normalized(); up=right.cross(fwd)\r\n    return loc+right*(px-400)*S+up*(400-py)*S, fwd\r\ndef hit(view,px,py):\r\n    o,d=ray(view,px,py); h,p,n,*_=sc.ray_cast(dg,o,d)\r\n    assert h,(view,px,py); return p,n\r\ngl=lambda v:[round(v.x,5),round(v.z,5),round(-v.y,5)]  # Blender Z-up -> glTF Y-up\r\n\r\n# screens: plane from interior hits, corners = corner rays hitting that plane, lifted 2mm\r\nfor name,(x0,y0,x1,y1) in SCREENS.items():\r\n    x0,y0,x1,y1=x0-3,y0-3,x1+3,y1+3\r\n    pts=[hit(0,x0+(x1-x0)*fx,y0+(y1-y0)*fy) for fx in (.25,.5,.75) for fy in (.25,.5,.75)]\r\n    c=sum((p for p,_ in pts),Vector())/len(pts); n=sum((q for _,q in pts),Vector()).normalized()\r\n    c=c+n*0.005\r\n    corners=[intersect_line_plane(o,o+d,c,n) for o,d in (ray(0,x,y) for x,y in ((x0,y1),(x1,y1),(x1,y0),(x0,y0)))]\r\n    # push any body geometry in front of the screen (raised apple, bezel lips) 1mm behind it\r\n    e1=corners[1]-corners[0]; e2=corners[3]-corners[0]\r\n    for v in body.data.vertices:\r\n        d=v.co-corners[0]; u=d.dot(e1)/e1.length_squared; w=d.dot(e2)/e2.length_squared; h=(v.co-c).dot(n)\r\n        if 0.03<u<0.97 and 0.03<w<0.97 and -0.001<h<0.03: v.co-=n*(h+0.001)\r\n    me=bpy.data.meshes.new(name); me.from_pydata([v-c for v in corners],[],[(0,1,2,3)])\r\n    uv=me.uv_layers.new(); [setattr(uv.data[i],'uv',t) for i,t in enumerate(((0,0),(1,0),(1,1),(0,1)))]\r\n    mat=bpy.data.materials.get('screen') or bpy.data.materials.new('screen'); me.materials.append(mat)\r\n    ob=bpy.data.objects.new(name,me); ob.location=c; sc.collection.objects.link(ob)\r\n    ob['normal']=gl(n)\r\n\r\np,n=hit(*LENS); lens=bpy.data.objects.new('lens',None); lens.location=p; lens['normal']=gl(n); lens['radius']=0.03\r\nsc.collection.objects.link(lens)\r\n\r\n# dials: split faces inside a cylinder around each dial normal\r\nfound=[(Vector(c),Vector(n).normalized()) for c,n in DIALS]\r\nbpy.context.view_layer.objects.active=body\r\nfor i,(c,n) in enumerate(found):\r\n    bpy.ops.object.mode_set(mode='EDIT'); bm=bmesh.from_edit_mesh(body.data)\r\n    for f in bm.faces: f.select=False\r\n    def inside(v):\r\n        d=v.co-c; a=d.dot(n); return -0.016<a<0.012 and (d-n*a).length<KNOB_R\r\n    for f in bm.faces:\r\n        if all(inside(v) for v in f.verts): f.select=True\r\n    bmesh.update_edit_mesh(body.data); bpy.ops.mesh.separate(type='SELECTED'); bpy.ops.object.mode_set(mode='OBJECT')\r\n    k=[o for o in bpy.context.selected_objects if o is not body][0]; k.name=f'knob_{i}'; k.select_set(False)\r\n    k.data.transform(__import__('mathutils').Matrix.Translation(-c)); k.location=c\r\n    k['axis']=gl(n); body.select_set(True)\r\nprint('KNOBS',len(found),[gl(c) for c,_ in found])\r\nbpy.ops.object.select_all(action='SELECT')\r\nbpy.ops.export_scene.gltf(filepath=OUT,export_format='GLB',export_extras=True,export_yup=True,use_selection=False)\r\n","scripts/build-rescue-model.mjs":"import {build} from 'esbuild';\n// Same Three instance as run-scene; bundle only the existing creator/rig leaves.\nawait build({entryPoints:['creature/source/rescue-model.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'creature/assets/rescue-model.js',plugins:[{name:'vendored-three',setup(b){b.onResolve({filter:/^three$/},()=>({path:'three',external:true}));}}]});\n","scripts/build-shader-effects.mjs":"// Rebuild the locally served MIT runtime; no account or runtime CDN is required.\r\nimport {build} from 'esbuild';\r\nimport {mkdir,readFile,writeFile} from 'node:fs/promises';\r\nimport {createRequire} from 'node:module';\r\nimport {dirname,resolve} from 'node:path';\r\nimport {createHash} from 'node:crypto';\r\nconst require=createRequire(import.meta.url);\r\nconst search=process.env.SHADER_BUILD_NODE_PATH ? [process.env.SHADER_BUILD_NODE_PATH] : [];\r\nconst core=require.resolve('shaders/core',{paths:[resolve('.'),...search]});\r\nconst root=resolve(dirname(core),'../..');\r\nconst entry=resolve(root,'dist/js/index.js');\r\nconst pkg=JSON.parse(await readFile(resolve(root,'package.json'),'utf8'));\r\nif(pkg.version!=='4.0.4')throw Error('Expected the reviewed shaders4.0.4 runtime.');\r\nconst {outputFiles:[output]}=await build({stdin:{contents:`export {createShader} from ${JSON.stringify(entry)};`,resolveDir:root},bundle:true,format:'esm',target:'es2022',minify:true,write:false,legalComments:'eof'});\r\nawait mkdir('vendor/shaders',{recursive:true});\r\nawait writeFile('vendor/shaders/runtime.mjs',output.contents);\r\nawait writeFile('vendor/shaders/LICENSE',await readFile(resolve(root,'LICENSE')));\r\nawait writeFile('vendor/shaders/version.json',JSON.stringify({package:'shaders',version:pkg.version,license:pkg.license,source:'https://github.com/shader-effects-inc/shaders',sha256:createHash('sha256').update(output.contents).digest('hex')}));\r\n","scripts/build-starter-ship.py":"# Starter ship GLB for Original MYR5: initialScene(fresh recipe) picks 'supportive' (coach 'supportive').\r\n# run: blender -b -P scripts/build-starter-ship.py -- D:/coach-merge/creature/models/ships/supportive.glb pod/worlds/starter/supportive.glb 0.04 512\r\n# Decimates the Tripo mesh, shrinks textures (colour TEX px, normal/metal-rough TEX/2) and exports WebP textures\r\n# (EXT_texture_webp, which GLTFLoader reads natively). Node, mesh and material names are kept as imported.\r\n# The other starter files are plain encodes/copies (PIL): pod/worlds/great-wall.webp = great-wall.png at WebP q90 m6;\r\n# pod/worlds/starter/<wonder>.webp = plan/assets-inbox/backgrounds/clean/<wonder>.webp unchanged.\r\nimport bpy, bmesh, sys\r\nSRC, OUT, RATIO, TEX = (lambda a: (a[0], a[1], float(a[2]), int(a[3])))(sys.argv[sys.argv.index('--') + 1:])\r\n\r\nbpy.ops.wm.read_factory_settings(use_empty=True)\r\nbpy.ops.import_scene.gltf(filepath=SRC)\r\nfor ob in [o for o in bpy.context.scene.objects if o.type == 'MESH']:\r\n    bpy.context.view_layer.objects.active = ob\r\n    # weld the split seams first: open seam boundaries stop decimate collapsing below ~9k tris\r\n    bm = bmesh.new(); bm.from_mesh(ob.data); bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=1e-6); bm.to_mesh(ob.data); bm.free()\r\n    m = ob.modifiers.new('decimate', 'DECIMATE'); m.ratio = RATIO; m.use_collapse_triangulate = True\r\n    bpy.ops.object.modifier_apply(modifier=m.name)\r\n    print('TRIS', ob.name, len(ob.data.polygons))\r\ncolour = {n.image.name for mat in bpy.data.materials if mat.node_tree for n in mat.node_tree.nodes\r\n          if n.type == 'TEX_IMAGE' and n.image and any(l.to_socket.name == 'Base Color' for l in n.outputs[0].links)}\r\nfor img in bpy.data.images:\r\n    size = TEX if img.name in colour else TEX // 2\r\n    if img.size[0] > size: img.scale(size, size)\r\n    print('IMAGE', img.name, tuple(img.size))\r\nbpy.ops.export_scene.gltf(filepath=OUT, export_format='GLB', export_yup=True, export_image_format='WEBP', export_image_quality=80)\r\n","scripts/build.mjs":"import './build-hand.mjs';\r\nimport './build-shader-effects.mjs';\r\nimport {build} from 'vite';\r\nimport {resolve,sep} from 'node:path';\r\nimport {optimizeModels} from './optimize-glb.mjs';\r\nimport {sites} from '@openai/sites-vite-plugin';\r\nimport {mkdir,cp,readdir,readFile,writeFile,unlink,rm} from 'node:fs/promises';\r\nimport {deploymentSize,WORKERS_FILE_LIMIT,WORKERS_FILE_BYTES} from './deployment-size.mjs';\r\nimport {omitDuplicateCoachIcon} from './icon-stage.mjs';\r\nimport {ensureAssets,ensureHandAssets,ensureThreeVendor} from './assets.mjs';\r\nimport {ensureMediapipe} from './mediapipe.mjs';\r\nimport {build as bundleEditor} from 'esbuild';\r\nimport {gzipSync} from 'node:zlib';\r\n// All app pages share the vendored Three core; addons remain bundled by esbuild.\r\nconst vendoredThree={name:'vendored-three',setup(build){build.onResolve({filter:/^three$/},()=>({path:'three',external:true}));}};\r\nimport {prepareReleaseBuild} from './release-build.mjs';\r\nimport {writeOfflineWorker} from './offline-assets.mjs';\r\nimport {sourceOffer} from './source-offer.mjs';\r\nimport {packNutrition} from './pack-nutrition.mjs';\r\nimport {buildFlightCoverage} from './build-flight-coverage.mjs';\r\nimport {assertMaterialTrustShipped,copySignedMaterialManifests,loadMaterialPublicBuildConfig} from './material-release.mjs';\r\n// Public release trust is committed in release-trust/public-build.json; an env var of the same name overrides it.\r\nfor(const [key,value] of Object.entries(JSON.parse(await readFile('release-trust/public-build.json','utf8'))))if(!key.startsWith('$')&&value!==null)process.env[key]??=typeof value==='string'?value:JSON.stringify(value);\r\nconst publicExpansionKey=process.env.PUBLIC_EXPANSION_SIGNING_JWK ? JSON.parse(process.env.PUBLIC_EXPANSION_SIGNING_JWK) : null;\r\nif(publicExpansionKey && !(publicExpansionKey.kty==='OKP' && publicExpansionKey.crv==='Ed25519' && typeof publicExpansionKey.x==='string' && /^[A-Za-z0-9_-]{43}$/.test(publicExpansionKey.x) && !/^A+$/.test(publicExpansionKey.x))) throw new Error('PUBLIC_EXPANSION_SIGNING_JWK must be a non-placeholder Ed25519 public JWK');\r\nif(process.env.MYR5_MATERIAL_SIGNING_PRIVATE_JWK)throw new Error('Do not expose a private material signing key to the Sites build.');\r\nconst materialRelease=loadMaterialPublicBuildConfig(),materialManifestSource=process.env.MYR5_MATERIAL_SITE_MANIFESTS;\r\nif(materialRelease.configured&&!materialManifestSource)throw new Error('MYR5_MATERIAL_SITE_MANIFESTS is required when material release trust is configured.');\r\nif(!materialRelease.configured&&materialManifestSource)throw new Error('Signed material manifests cannot be copied without matching public trust and immutable host configuration.');\r\nawait ensureAssets();\r\nif(process.env.WORLD_TILES_DIR)await buildFlightCoverage(process.env.WORLD_TILES_DIR);\r\nawait ensureHandAssets();\r\nawait ensureThreeVendor();\r\nawait ensureMediapipe();\r\nawait import('./build-npc-catalog.mjs');\r\nawait bundleEditor({entryPoints:['./modules/coach-world-rts/npc-achievement-rules.mjs'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'modules/coach-world-rts/npc-achievement-rules.js'});\r\nawait bundleEditor({entryPoints:['./modules/coach-world-rts/npc-cosmetic-grants.mjs'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'modules/coach-world-rts/npc-cosmetic-grants.js'});\r\nawait bundleEditor({entryPoints:['./creature/source/editor.ts'],bundle:true,format:'esm',target:'es2022',minify:true,sourcemap:true,outfile:'creature/assets/editor.js',plugins:[vendoredThree],define:materialRelease.defines});\r\n// The app viewer must use the same recipe catalog and materials as the editor.\r\nawait bundleEditor({entryPoints:['./creature/source/phone.ts'],bundle:true,format:'esm',target:'es2022',minify:true,sourcemap:true,outfile:'creature/assets/phone.js',plugins:[vendoredThree],define:materialRelease.defines});\r\nawait bundleEditor({entryPoints:['./creature/source/reward-preview.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'creature/assets/reward-preview.js',plugins:[vendoredThree],define:materialRelease.defines});\nawait import('./build-rescue-model.mjs');\n// The War Room's Gala character bay reuses the cage room and its Draco loader, so it is bundled like the editor.\r\nawait bundleEditor({entryPoints:['./creature/source/war-room-gala.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'war-room/gala-bay.js',plugins:[vendoredThree]});\r\nawait bundleEditor({entryPoints:['./creature/source/war-room-coaches.ts'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'war-room/coach-sprites.js'}); // R26: the pod's home character (lazy)\r\nawait import('./build-gala-avatar.mjs'); // Gala DFO rig -> pod/gala-avatar.js\r\nawait bundleEditor({entryPoints:['./weapon-training.mjs'],bundle:true,format:'iife',globalName:'MYR5Training',target:'es2022',minify:true,outfile:'workout-tracks.js'});\r\nawait bundleEditor({entryPoints:['./local-coach/browser-runtime.mjs'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'local-coach-runtime.mjs'});\r\nconst releaseBuild=await prepareReleaseBuild();\r\nawait bundleEditor({entryPoints:['./app.mjs'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'app-runtime.mjs',external:['https://*','./modules/coach-world/view.mjs','/modules/coach-world/hub-window.mjs','./local-coach-runtime.mjs','./arcade/slice/camera-slice.mjs','./arcade/runner/runner.mjs','./arcade/cover/cover.mjs','./arcade/plank-flight/flight.mjs','./arcade/workout-effects.mjs','./arcade/hold/camera-hold.mjs','./arcade/hold/blizzard.mjs','./arcade/power/camera-power.mjs','./arcade/power/power.mjs','./arcade/power/retained-power.mjs','./arcade/power/avatar-power.mjs','./arcade/quest-progress-reporter.mjs','./creature/assets/phone.js','./modules/portal/portal-entry.mjs','./modules/ships/ship-view.mjs','./modules/ships/ship-intro.mjs','./modules/vault/vault-store.mjs','./modules/vault/vault-banner.mjs','./modules/vault/vault-door.mjs','./modules/vault/vault-hall.mjs','./modules/vault/vault-goals.mjs','./modules/vault/quest-progress.mjs','../modules/vault/quest-progress.mjs'],plugins:[vendoredThree],define:materialRelease.defines});\r\nawait bundleEditor({entryPoints:['./launch.mjs'],bundle:true,format:'esm',target:'es2022',minify:true,outfile:'launch-runtime.mjs',external:['three','three/addons/loaders/GLTFLoader.js','/handborne/companion.mjs','./nutrition-data.mjs','./local-coach-runtime.mjs','./food/pyramid-scanner.mjs','./drop-pod-opening.mjs','./modules/rooms/classroom.mjs','./modules/vault/vault-store.mjs'],define:materialRelease.defines});\r\nawait build({configFile:false,plugins:[sites()],build:{outDir:'dist/server',ssr:'server/cloudflare.mjs',target:'es2022',minify:true,rollupOptions:{output:{entryFileNames:'index.js',inlineDynamicImports:true}},ssrEmitAssets:false},ssr:{noExternal:true}});\r\nawait mkdir('dist/client',{recursive:true});\r\nawait rm('dist/client/materials',{recursive:true,force:true});\r\nawait rm('dist/client/modules/coach-world-rts/dev',{recursive:true,force:true});\r\nawait rm('dist/client/modules/coach-flight/dev.html',{force:true});\r\n// Root scratch captures (shot-tmp.mjs, tmp-*.mjs) are local tooling, never shipped.\r\nconst scratch=/^(shot-tmp|tmp-).*\\.mjs$/;\r\nfor(const entry of await readdir('.',{withFileTypes:true})){if(entry.isFile()&&/\\.(html|css|mjs|webmanifest)$/.test(entry.name)&&!scratch.test(entry.name))await cp(entry.name,`dist/client/${entry.name}`);}\r\nawait cp('workout-tracks.js','dist/client/workout-tracks.js');\r\n// Crawler files (robots/sitemap/llms) are served as static assets; never precached.\r\nfor(const file of ['robots.txt','sitemap.xml','llms.txt'])await cp(file,`dist/client/${file}`);\r\nconsole.log('Packed nutrition bytes removed:',await packNutrition());\r\nfor(const folder of ['pod','creature','models','icons','handborne','arcade','modules','packs','war-room','food','vendor','reward-assets','audio','pods'])await cp(folder,`dist/client/${folder}`,{recursive:true,filter:(p)=>!/[\\\\/]source[\\\\/].*node_modules(?:[\\\\/]|$)/.test(p)&&!/(?:^|[\\\\/])modules[\\\\/]coach-world-rts[\\\\/]dev(?:[\\\\/]|$)/.test(p)&&!/(?:^|[\\\\/])modules[\\\\/]coach-flight[\\\\/]dev\\.html$/.test(p)&&!/(?:^|[\\\\/])tests[\\\\/]|modules[\\\\/]dance[\\\\/]music-lab(?![\\\\/]capture\\.mjs$)|\\.map$|\\.LEGAL\\.txt$/.test(p)});\r\n{const t=process.env.WORLD_TILES_DIR;if(t)await cp(t,'dist/client/world/v1',{recursive:true});}\r\n// V6 far ring DEM (z9 flight, z10 base view; baked by D:/myr5-work/world-ring/bake-ring.py, outside git like world/v1): WORLD_RING_DIR=D:/myr5-work/world-ring/out\r\n{const r=process.env.WORLD_RING_DIR;if(r)for(const z of ['9','10'])await cp(`${r}/dem/${z}`,`dist/client/world/v1/dem/${z}`,{recursive:true});} // world/v1 static tiles are baked outside git (161 MB); set WORLD_TILES_DIR to ship them\r\nawait mkdir('dist/client/modules/dance/music-lab',{recursive:true});\r\nfor(const file of ['capture.mjs','pcm-worklet.mjs'])await cp(`modules/dance/music-lab/${file}`,`dist/client/modules/dance/music-lab/${file}`);\r\nawait rm('dist/client/pod/weapon-review.html',{force:true});\r\nawait cp('creature/assets/phone.js','dist/client/modules/dance/coach-phone.js');\r\n// Legacy Coach 512 URL is aliased by the Worker and service worker.\r\nawait omitDuplicateCoachIcon('dist/client');\r\nif(materialRelease.configured){\r\n await copySignedMaterialManifests({sourceDir:materialManifestSource,siteRoot:'dist/client',baseUrl:process.env.MYR5_MATERIALS_BASE_URL,publicJwk:materialRelease.publicJwk});\r\n const configPath='dist/client/modules/materials/material-config.mjs';let config=await readFile(configPath,'utf8');\r\n config=config.replace(/export const BUILT_PUBLIC_MATERIAL_SIGNING_JWK = [^;]+;/,`export const BUILT_PUBLIC_MATERIAL_SIGNING_JWK = ${JSON.stringify(materialRelease.publicJwk)};`).replace(/export const BUILT_MATERIAL_RESOURCE_POLICY = [^;]+;/,`export const BUILT_MATERIAL_RESOURCE_POLICY = ${JSON.stringify(materialRelease.policy)};`);\r\n await writeFile(configPath,config);\r\n}\r\nawait assertMaterialTrustShipped('dist/client');\r\n// Achievement Vault: its raw modules reach .ts sources (unlock-store) that production strips, so bundle them in place (lazy, never core:\r\n// offline-assets VAULT). Splitting shares one store between door/hall; app-runtime/launch-runtime keep the store external so it is not inlined.\r\nawait bundleEditor({entryPoints:['modules/vault/vault-store.mjs','modules/vault/vault-banner.mjs','modules/vault/vault-door.mjs','modules/vault/vault-hall.mjs','modules/vault/quest-progress.mjs'],bundle:true,splitting:true,format:'esm',target:'es2022',minify:true,outdir:'dist/client/modules/vault',allowOverwrite:true,outExtension:{'.js':'.mjs'},external:['three','three/*','../portal/*','../../drop-pod-opening.mjs'],chunkNames:'chunk-[hash]'});\r\n// Authoring projects remain in the published source repository, not the app bundle.\r\nfor(const folder of ['creature/source','handborne/source']){const target=resolve('dist/client',folder);if(!target.startsWith(resolve('dist/client')+sep))throw Error('Invalid staging path');await rm(target,{recursive:true,force:true});}\r\n// Keep debugger-only maps in the open-source repository,\r\n// but not in the deployable static archive. Runtime code does not request them.\r\nfor(const path of [\r\n 'dist/client/handborne/assets/hand-entry-B7K7J180.js',\r\n 'dist/client/handborne/assets/hand-entry-BySRDxUO.css',\r\n 'dist/client/handborne/assets/GLTFExporter-CL_WC7Dc.js',\r\n 'dist/client/handborne/assets/OBJExporter-J3SdsgWZ.js',\r\n 'dist/client/creature/assets/chunk-2X4UOJKI.js',\r\n 'dist/client/creature/assets/chunk-FY3X2IKC.js',\r\n 'dist/client/creature/assets/chunk-QUSSDQTX.js',\r\n 'dist/client/creature/assets/chunk-2X4UOJKI.js.map',\r\n 'dist/client/creature/assets/chunk-FY3X2IKC.js.map',\r\n 'dist/client/creature/assets/chunk-QUSSDQTX.js.map',\r\n 'dist/client/creature/assets/editor.js.map',\r\n 'dist/client/creature/assets/phone.js.map',\r\n])await rm(path,{force:true});\r\nconsole.log('Model bytes saved:',await optimizeModels('dist/client/creature/models')+await optimizeModels('dist/client/handborne/models'));\r\n// The source configuration is intentionally empty. Only a validated public\r\n// verification JWK may be embedded in a release; signing material is never read.\r\nconst expansionConfigPath='dist/client/modules/new/expansion-config.mjs';\r\nawait writeFile(expansionConfigPath,(await readFile('modules/new/expansion-config.mjs','utf8')).replace('export const BUILT_PUBLIC_EXPANSION_SIGNING_JWK = null;',`export const BUILT_PUBLIC_EXPANSION_SIGNING_JWK = ${JSON.stringify(publicExpansionKey)};`));\r\n// Publish only the current voice pack, excluding obsolete clips and source masters.\r\nconst voiceManifest=JSON.parse(await readFile('voice/manifest.json','utf8'));\r\nawait mkdir('dist/client/voice',{recursive:true});\r\nfor(const entry of await readdir('dist/client/voice',{withFileTypes:true}))if(entry.isFile()&&/\\.(wav|mp3|json)$/.test(entry.name))await unlink('dist/client/voice/'+entry.name);\r\nawait cp('voice/manifest.json','dist/client/voice/manifest.json');\r\nfor(const url of new Set(Object.values(voiceManifest.phrases)))await cp('.'+url,'dist/client'+url);\r\nlet pose=(await readFile('pose.html','utf8')).replaceAll('startup-recovery-1',releaseBuild).replaceAll('rest-flow-1',releaseBuild);\r\n// One stylesheet in production: concatenate the <link rel=stylesheet> files in document order (every url() in them is absolute).\r\nconst cssLinks=[...pose.matchAll(/<link[^>]*rel=\"stylesheet\"[^>]*href=\"([^\"?]+)[^\"]*\"[^>]*>/g)];\r\nconst cssHrefs=cssLinks.map(m=>m[1]);\r\nawait writeFile('dist/client/app.css',(await Promise.all(cssHrefs.map(h=>readFile('.'+h,'utf8')))).join('\\n'));\r\nfor(const m of cssLinks)pose=pose.replace(m[0],'');\r\npose=pose.replace('<link rel=\"manifest\"',`<link rel=\"stylesheet\" href=\"/app.css?v=${releaseBuild}\"><link rel=\"manifest\"`);\r\nawait writeFile('dist/client/pose.html',pose);await writeFile('dist/client/index.html',pose);await cp('LICENSE','dist/client/LICENSE');\r\nawait writeOfflineWorker('dist/client',releaseBuild);\r\n// The AGPL source offer travels with the app, with no runtime secrets or user records (scripts/source-offer.mjs).\r\n// ponytail: two copies; /source.json is the Settings link, the Account panel (launch-shell.mjs) still links the .gz.\r\n// Developer test pages are not part of the released app or the source needed to rebuild it, so they are left out of the offer too.\r\nconst offer=await sourceOffer();for(const key of Object.keys(offer))if(/^modules\\/coach-world-rts\\/dev\\//.test(key)||scratch.test(key))delete offer[key];\r\nconst sources=JSON.stringify(offer);\r\nawait writeFile('dist/client/source.json',sources);await writeFile('dist/client/source.json.gz',gzipSync(sources,{level:9}));\r\nconst {files,payloadBytes,largest}=await deploymentSize('dist');\r\nconsole.log(`Workers limits: ${files} / ${WORKERS_FILE_LIMIT} files; largest ${largest.bytes} / ${WORKERS_FILE_BYTES} bytes (${largest.path}); ${payloadBytes} total bytes.`);\r\nconsole.log('Coach build ready.');\r\n","scripts/check-food.mjs":"import {pipeline,env} from '@huggingface/transformers';\r\nimport {mkdir,writeFile} from 'node:fs/promises';\r\nenv.cacheDir='../food-model-cache';\r\nconst model=await pipeline('image-classification','onnx-community/swin-finetuned-food101-ONNX',{device:'cpu',dtype:'q8',revision:'e5e50bfc6425aa546f3b4421ca8bd79d0dd610b8'});\r\nconst datasetUrl='https://datasets-server.huggingface.co/first-rows?dataset=ethz%2Ffood101&config=default&split=validation';\r\nconst response=await fetch(datasetUrl);if(!response.ok)throw Error(`Food fixture service: ${response.status}`);const data=await response.json();\r\nconst fixtures=data.rows.slice(0,2).map(x=>({source:datasetUrl,image:x.row.image.src,expected:data.features.find(f=>f.name==='label').type.names[x.row.label]}));\r\nfor(const offset of [5000,12500,20000]){const source=`https://datasets-server.huggingface.co/rows?dataset=ethz%2Ffood101&config=default&split=validation&offset=${offset}&length=1`;const d=await(await fetch(source)).json();fixtures.push({source,image:d.rows[0].row.image.src,expected:d.features.find(f=>f.name==='label').type.names[d.rows[0].row.label]});}\r\nfixtures.push({source:'https://huggingface.co/Xenova/clip-vit-base-patch32',image:'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/tiger.jpg',expected:'non-food'});\r\nconst results=[];for(const f of fixtures){const result=await model(f.image,{top_k:4});results.push({expected:f.expected,top:result.slice(0,4),source:f.source});console.log(JSON.stringify(results.at(-1)));}\r\nawait writeFile('../food-recognition-check.json',JSON.stringify(results,null,2));\r\n","scripts/clay-board-assets.mjs":"// Builds the Clay board GLB: simplifies a supplied ~1.2M-tri Tripo clay tablet (real relief, UVs, normals and embedded\r\n// textures kept, same approach as pixel-dance-assets.mjs) to <=60k tris. Both candidates are simplified and measured;\r\n// PICK (argv[3], default geometric-clay-tablet) is written to pod/worlds/boards/clay/clay-tablet.glb, the other to\r\n// artifacts/clay/. Usage: node scripts/clay-board-assets.mjs [srcDir] [pick]\r\nimport {readFile,writeFile,mkdir} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nimport {MeshoptSimplifier} from 'meshoptimizer';\r\nconst SRC=process.argv[2]||'D:/outputs/grimoire-plan-2026-10-20/assets',PICK=process.argv[3]||'geometric-clay-tablet',OUT='pod/worlds/boards/clay',ALT='artifacts/clay',MAXTRIS=60000;\r\nconst CANDIDATES=['geometric-clay-tablet','ritual-sigil-tablet'];\r\nconst sha=b=>createHash('sha256').update(b).digest('hex');\r\nconst align=n=>n+3&~3;\r\n\r\nfunction parse(glb){\r\n const jl=glb.readUInt32LE(12),doc=JSON.parse(glb.subarray(20,20+jl)),bin=glb.subarray(28+jl,28+jl+glb.readUInt32LE(20+jl));\r\n return {doc,bin};\r\n}\r\nfunction acc(doc,bin,i,T){\r\n const a=doc.accessors[i],v=doc.bufferViews[a.bufferView],o=bin.byteOffset+(v.byteOffset||0)+(a.byteOffset||0);\r\n return new T(bin.buffer.slice(o,o+a.count*{SCALAR:1,VEC2:2,VEC3:3}[a.type]*T.BYTES_PER_ELEMENT));\r\n}\r\nfunction bounds(p){\r\n const mn=[1/0,1/0,1/0],mx=[-1/0,-1/0,-1/0];\r\n for(let i=0;i<p.length;i+=3)for(let k=0;k<3;k++){mn[k]=Math.min(mn[k],p[i+k]);mx[k]=Math.max(mx[k],p[i+k]);}\r\n return {min:mn,max:mx,size:mx.map((v,k)=>v-mn[k])};\r\n}\r\n// Relief of each broad face: per-cell outermost z over a G x G grid of the xy footprint (thin axis = z), coarse enough that\r\n// both the 1.2M-tri source and the 60k-tri output put dozens of vertices in every cell.\r\n// mean = how far the face stands off the centre plane, sd = how much relief it carries.\r\nconst gridZ=(P,b,G=16)=>{\r\n const hi=new Float32Array(G*G).fill(-1/0),lo=new Float32Array(G*G).fill(1/0);\r\n for(let i=0;i<P.length;i+=3){\r\n  const gx=Math.min(G-1,Math.floor((P[i]-b.min[0])/b.size[0]*G)),gy=Math.min(G-1,Math.floor((P[i+1]-b.min[1])/b.size[1]*G)),k=gy*G+gx;\r\n  hi[k]=Math.max(hi[k],P[i+2]);lo[k]=Math.min(lo[k],P[i+2]);\r\n }\r\n return {hi,lo};\r\n};\r\nconst stat=a=>{const v=[...a].filter(Number.isFinite),m=v.reduce((s,x)=>s+x,0)/v.length;return {mean:+m.toFixed(4),sd:+Math.sqrt(v.reduce((s,x)=>s+(x-m)**2,0)/v.length).toFixed(4)};};\r\nexport function faceRelief(P,b){const {hi,lo}=gridZ(P,b);return {'+Z':stat(hi),'-Z':stat(lo)};}\r\n// How far the simplified surface's per-cell height strays from the source's (model units; the tablet is ~0.98 tall).\r\nexport function reliefDelta(src,srcB,out,outB){\r\n const a=gridZ(src,srcB),c=gridZ(out,outB),d=(x,y)=>{const e=[...x].map((v,i)=>Math.abs(v-y[i])).filter(Number.isFinite);return {max:+Math.max(...e).toFixed(4),mean:+(e.reduce((s,v)=>s+v,0)/e.length).toFixed(5)};};\r\n return {'+Z':d(a.hi,c.hi),'-Z':d(a.lo,c.lo)};\r\n}\r\nfunction glb(doc,chunks){ // chunks: [{bytes,view?}] appended into one BIN\r\n let off=0;const parts=[];\r\n doc.bufferViews=chunks.map(c=>{const v={buffer:0,byteOffset:off,byteLength:c.bytes.length,...c.view};parts.push(c.bytes,Buffer.alloc(align(c.bytes.length)-c.bytes.length));off+=align(c.bytes.length);return v;});\r\n doc.buffers=[{byteLength:off}];\r\n const bin=Buffer.concat(parts);let j=Buffer.from(JSON.stringify(doc));j=Buffer.concat([j,Buffer.alloc(align(j.length)-j.length,0x20)]);\r\n const h=Buffer.alloc(12),jh=Buffer.alloc(8),bh=Buffer.alloc(8);\r\n h.writeUInt32LE(0x46546c67,0);h.writeUInt32LE(2,4);h.writeUInt32LE(28+j.length+bin.length,8);\r\n jh.writeUInt32LE(j.length,0);jh.write('JSON',4);bh.writeUInt32LE(bin.length,0);bh.write('BIN\\0',4);\r\n return Buffer.concat([h,jh,j,bh,bin]);\r\n}\r\n\r\nasync function build(name){\r\n const srcPath=`${SRC}/${name}.glb`,src=await readFile(srcPath),{doc,bin}=parse(src);\r\n const prim=doc.meshes[0].primitives[0],A=prim.attributes;\r\n const pos=acc(doc,bin,A.POSITION,Float32Array),nrm=acc(doc,bin,A.NORMAL,Float32Array),uv=acc(doc,bin,A.TEXCOORD_0,Float32Array),idx=acc(doc,bin,prim.indices,Uint32Array);\r\n const inB=bounds(pos),inTris=idx.length/3;\r\n // Weld UV/normal seams by position, then simplify positions only; surviving triangles keep their original normals/UVs\r\n // (the normal map carries the fine relief). Raise the error budget until under MAXTRIS.\r\n const remap=MeshoptSimplifier.generatePositionRemap(pos,3),widx=idx.map(v=>remap[v]);\r\n let simp,err;\r\n for(let e=0.01;;e*=2){[simp,err]=MeshoptSimplifier.simplify(widx,pos,3,MAXTRIS*3,e,['Permissive']);if(simp.length/3<=MAXTRIS||e>1)break;}\r\n const map=new Int32Array(pos.length/3).fill(-1),used=[],outIdx=new Uint32Array(simp.length);\r\n for(let i=0;i<simp.length;i++){const v=simp[i];if(map[v]<0){map[v]=used.length;used.push(v);}outIdx[i]=map[v];}\r\n const n=used.length,P=new Float32Array(n*3),N=new Float32Array(n*3),U=new Float32Array(n*2);\r\n used.forEach((v,i)=>{P.set(pos.subarray(v*3,v*3+3),i*3);N.set(nrm.subarray(v*3,v*3+3),i*3);U.set(uv.subarray(v*2,v*2+2),i*2);});\r\n const outB=bounds(P),tris=outIdx.length/3;\r\n if(tris>MAXTRIS)throw Error(`${name}: simplified to ${tris} tris > ${MAXTRIS}`);\r\n const view=i=>doc.bufferViews[i],img=i=>Buffer.from(bin.buffer,bin.byteOffset+view(i).byteOffset,view(i).byteLength),f=a=>Buffer.from(a.buffer);\r\n const nd={asset:doc.asset,scene:0,scenes:doc.scenes,nodes:doc.nodes,materials:doc.materials,textures:doc.textures,samplers:doc.samplers,\r\n  images:doc.images.map((im,k)=>({...im,bufferView:4+k})),\r\n  meshes:[{name:doc.meshes[0].name,primitives:[{attributes:{POSITION:0,NORMAL:1,TEXCOORD_0:2},indices:3,material:0}]}],\r\n  accessors:[\r\n   {bufferView:0,componentType:5126,count:n,type:'VEC3',min:outB.min,max:outB.max},\r\n   {bufferView:1,componentType:5126,count:n,type:'VEC3'},\r\n   {bufferView:2,componentType:5126,count:n,type:'VEC2'},\r\n   {bufferView:3,componentType:5125,count:outIdx.length,type:'SCALAR'}]};\r\n const out=glb(nd,[{bytes:f(P),view:{target:34962}},{bytes:f(N),view:{target:34962}},{bytes:f(U),view:{target:34962}},{bytes:f(outIdx),view:{target:34963}},\r\n  ...doc.images.map((_,k)=>({bytes:img(doc.images[k].bufferView)}))]);\r\n return {name,out,report:{source:{path:srcPath,bytes:src.length,sha256:sha(src),verts:pos.length/3,tris:inTris,bounds:inB,relief:faceRelief(pos,inB)},\r\n  out:{bytes:out.length,sha256:sha(out),verts:n,tris,bounds:outB,simplifyError:err,relief:faceRelief(P,outB),reliefDelta:reliefDelta(pos,inB,P,outB),textureBytes:doc.images.map(i=>view(i.bufferView).byteLength)}}};\r\n}\r\n\r\nif(process.argv[1]?.endsWith('clay-board-assets.mjs')){\r\n if(!CANDIDATES.includes(PICK))throw Error(`pick must be one of ${CANDIDATES}`);\r\n await MeshoptSimplifier.ready;await mkdir(OUT,{recursive:true});await mkdir(ALT,{recursive:true});\r\n const built=[];for(const c of CANDIDATES)built.push(await build(c)); // one at a time: each holds a ~36 MB source\r\n const report={pick:PICK,candidates:{},generator:'scripts/clay-board-assets.mjs',meshoptimizer:'1.3.0',\r\n  frontFaceAxis:'+Z/-Z (thinnest extent); upright slab: width X, height +Y from y=0, depth Z; board orients it with orientMatrix'};\r\n for(const b of built){\r\n  const path=b.name===PICK?`${OUT}/clay-tablet.glb`:`${ALT}/${b.name}.glb`;\r\n  await writeFile(path,b.out);report.candidates[b.name]={...b.report,out:{path,...b.report.out}};\r\n }\r\n await writeFile(`${ALT}/assets.json`,JSON.stringify(report,null,1));\r\n console.log(JSON.stringify(report,null,1));\r\n}\r\n","scripts/colour-census.mjs":"// R18 G2: count every hex across palettes.json + SIMPLE_COLORS + LEGACY_COLORS; the top 15 are the free colours. Ties keep first appearance (SIMPLE, LEGACY, then palettes).\r\nimport {build} from 'esbuild';\r\nglobalThis.localStorage={getItem:()=>null,setItem(){}};\r\nconst out=await build({stdin:{contents:\"export {COLORS,PALETTES} from './creature/source/creator/materials-registry';\",resolveDir:process.cwd(),loader:'ts'},bundle:true,format:'esm',platform:'neutral',mainFields:['module','main'],write:false,target:'es2022'});\r\nconst {COLORS,PALETTES}=await import('data:text/javascript;base64,'+Buffer.from(out.outputFiles[0].text).toString('base64'));\r\nfunction census(){\r\n const n=new Map();\r\n const add=h=>n.set(h.toLowerCase(),(n.get(h.toLowerCase())||0)+1);\r\n for(const c of COLORS)[c.primary,c.secondary,c.accent].forEach(add);\r\n for(const p of PALETTES)p.colors.forEach(add);\r\n return [...n].sort((a,b)=>b[1]-a[1]);\r\n}\r\n\r\nfor(const [h,c] of census().slice(0,15))console.log(h,c);\r\n","scripts/deployment-size.mjs":"import {readdir,stat} from 'node:fs/promises';\r\nimport {join} from 'node:path';\r\n\r\n// Cloudflare Workers static assets: 20,000 files per version (Free plan; Paid is 100,000)\r\n// and 25 MiB per file. There is no total-size cap, unlike the old 256 MiB Sites archive.\r\nexport const WORKERS_FILE_LIMIT=20000,WORKERS_FILE_BYTES=25*1024*1024;\r\n\r\n// Walk the whole dist tree (client assets plus the server bundle) after the build.\r\nexport async function deploymentSize(directory='dist',{fileLimit=WORKERS_FILE_LIMIT,fileBytes=WORKERS_FILE_BYTES}={}){\r\n let files=0,payloadBytes=0,largest={path:null,bytes:0};\r\n async function walk(path){for(const entry of await readdir(path,{withFileTypes:true})){const child=join(path,entry.name);if(entry.isDirectory())await walk(child);else if(entry.isFile()){const {size}=await stat(child);files++;payloadBytes+=size;if(size>largest.bytes)largest={path:child,bytes:size};}else throw new Error(`Non-regular deployment entry: ${child}`);}}\r\n await walk(directory);\r\n if(files>fileLimit)throw new Error(`Too many files for Workers: ${files} / ${fileLimit}.`);\r\n if(largest.bytes>fileBytes)throw new Error(`File too large for Workers: ${largest.path} is ${largest.bytes} / ${fileBytes} bytes.`);\r\n return {files,payloadBytes,largest};\r\n}\r\n","scripts/dev.mjs":"import {createServer} from 'vite';\r\nimport {build as bundleStartup} from 'esbuild';\r\nawait bundleStartup({entryPoints:['./creature/source/phone.ts','./creature/source/editor.ts','./creature/source/reward-preview.ts'],bundle:true,format:'esm',target:'es2022',outdir:'creature/assets'});\r\nawait bundleStartup({entryPoints:['./app.mjs'],bundle:true,format:'esm',target:'es2022',outfile:'app-runtime.mjs',external:['https://*','./modules/coach-world/view.mjs']});\r\nawait bundleStartup({entryPoints:['./launch.mjs'],bundle:true,format:'esm',target:'es2022',outfile:'launch-runtime.mjs',external:['./food/pyramid-scanner.mjs','./drop-pod-opening.mjs','/handborne/companion.mjs']});\r\nimport {sites} from '@openai/sites-vite-plugin';\r\nimport {Miniflare} from 'miniflare';\r\nimport {readFile,readdir} from 'node:fs/promises';\r\nimport {ensureHandAssets,ensureThreeVendor} from './assets.mjs';\r\nimport {ensureMediapipe} from './mediapipe.mjs';\r\nawait ensureHandAssets();\r\nawait ensureThreeVendor();\r\nawait ensureMediapipe();\r\nawait import('./build-npc-catalog.mjs');\r\nawait bundleStartup({entryPoints:['./modules/coach-world-rts/npc-achievement-rules.mjs'],bundle:true,format:'esm',target:'es2022',outfile:'modules/coach-world-rts/npc-achievement-rules.js'});\r\nawait bundleStartup({entryPoints:['./modules/coach-world-rts/npc-cosmetic-grants.mjs'],bundle:true,format:'esm',target:'es2022',outfile:'modules/coach-world-rts/npc-cosmetic-grants.js'});\r\nconst mf=new Miniflare({modules:true,script:'export default {fetch(){return new Response(\"ok\")}}',d1Databases:['DB'],d1Persist:'.wrangler/local-db'});\r\nconst DB=await mf.getD1Database('DB');\r\nawait DB.prepare('CREATE TABLE IF NOT EXISTS _local_migrations (name TEXT PRIMARY KEY)').run();\r\nfor(const name of (await readdir('drizzle')).filter(n=>n.endsWith('.sql')).sort()){if(await DB.prepare('SELECT name FROM _local_migrations WHERE name=?').bind(name).first())continue;const statements=(await readFile(`drizzle/${name}`,'utf8')).split('--> statement-breakpoint').map(s=>s.trim()).filter(Boolean);await DB.batch(statements.map(s=>DB.prepare(s)));await DB.prepare('INSERT INTO _local_migrations(name) VALUES(?)').bind(name).run();}\r\nconst server=await createServer({configFile:false,plugins:[sites(),{name:'coach-api',configureServer(server){return()=>server.middlewares.use(async(req,res,next)=>{if(!req.url.startsWith('/api/')&&req.url!=='/health')return next();try{const chunks=[];for await(const chunk of req)chunks.push(chunk);const request=new Request(`http://${req.headers.host}${req.url}`,{method:req.method,headers:req.headers,body:['GET','HEAD'].includes(req.method)?undefined:Buffer.concat(chunks)});const {default:worker}=await server.ssrLoadModule('/server/worker.mjs');const result=await worker.fetch(request,{DB,LOCAL_PREVIEW:true});res.writeHead(result.status,Object.fromEntries(result.headers));res.end(Buffer.from(await result.arrayBuffer()));}catch{res.writeHead(500);res.end('Local request failed');}})}}],server:{host:'127.0.0.1',port:Number(process.env.COACH_DEV_PORT)||5195,strictPort:true},appType:'mpa'});\r\nawait server.listen();server.printUrls();\r\nprocess.on('SIGINT',async()=>{await server.close();await mf.dispose();process.exit();});\r\n","scripts/encode-voice.mjs":"// Phone-friendly copies of the approved masters; no pitch or tempo changes.\r\nimport {readFile,writeFile,stat} from 'node:fs/promises';\r\nimport {execFile} from 'node:child_process';\r\nimport {promisify} from 'node:util';\r\nconst exec=promisify(execFile),engine=process.env.MYR5_FFMPEG||'ffmpeg';\r\nconst manifest=JSON.parse(await readFile('voice/manifest.json','utf8'));\r\nlet bytes=0,done=0;const entries=Object.entries(manifest.phrases);\r\nfor(const [text,url]of entries){\r\n const output=url.replace(/\\.wav$/,'.mp3');\r\n if(url!==output)await exec(engine,['-hide_banner','-loglevel','error','-y','-threads','1','-i','.'+url,'-c:a','libmp3lame','-q:a','3','-ac','1','.'+output],{windowsHide:true});\r\n manifest.phrases[text]=output;bytes+=(await stat('.'+output)).size;if(++done%300===0)console.log(`${done}/${entries.length} compact voice clips ready.`);\r\n}\r\nmanifest.bytes=bytes;manifest.format='mp3';await writeFile('voice/manifest.json',JSON.stringify(manifest));console.log(`Voice download: ${Math.round(bytes/1024/1024)} MB.`);\r\n","scripts/execute-game-generation-d1.mjs":"#!/usr/bin/env node\r\n// Parent-operated native Wrangler D1 binding transport. Importing this file has no effects.\r\n// --execute is opt-in. Never run this against remote resources during implementation/tests.\r\nimport { DatabaseSync } from 'node:sqlite';\r\nimport { createHash } from 'node:crypto';\r\nimport { readFileSync,writeFileSync,mkdtempSync,realpathSync,existsSync,unlinkSync,rmdirSync } from 'node:fs';\r\nimport { tmpdir } from 'node:os';\r\nimport { join,resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport { parseArgs } from 'node:util';\r\nimport { parseJsonConfig,validateResetTarget,PRODUCTION_TARGET } from './reset-game-generation.mjs';\r\nimport { resetD1GameGeneration,resetTableQuery,snapshotSql,snapshotEvidence } from './reset-game-generation-d1.mjs';\r\nconst check=(ok,message)=>{if(!ok)throw Object.assign(Error(message),{safeResetMessage:message});};\r\nconst sha=bytes=>createHash('sha256').update(bytes).digest('hex');\r\nexport function backupEvidenceFromSql(bytes){\r\n const db=new DatabaseSync(':memory:');\r\n try{\r\n  db.exec(bytes.toString('utf8'));\r\n  return Object.fromEntries(db.prepare(resetTableQuery).all().map(({name})=>{\r\n   const columns=db.prepare(`PRAGMA table_info(\"${name.replaceAll('\"','\"\"')}\")`).all().map(r=>r.name);\r\n   return [name,snapshotEvidence(db.prepare(snapshotSql(name,columns)).get().snapshot)];\r\n  }));\r\n }finally{db.close();}\r\n}\r\nexport async function main(args=process.argv.slice(2)){\r\n const {values:v}=parseArgs({args,options:Object.fromEntries(['config','worker','origin','binding','database-name','database-id','account-id','reset-id','expected-generation','cutoff-ms','backup','backup-sha256','evidence'].map(k=>[k,{type:'string'}]).concat([['execute',{type:'boolean',default:false}],['integration-ready',{type:'boolean',default:false}],['remote',{type:'boolean',default:false}]]))});\r\n for(const k of ['config','worker','origin','binding','database-name','database-id','account-id','reset-id','expected-generation','cutoff-ms','backup','backup-sha256','evidence'])check(v[k],`Missing --${k}`);\r\n check(v.remote===true,'Explicit --remote is required to connect to production');\r\n check(/^[a-f0-9]{32}$/i.test(v['account-id']),'Explicit Cloudflare account ID required');\r\n check(/^[1-9][0-9]*$/.test(v['expected-generation'])&&/^[1-9][0-9]*$/.test(v['cutoff-ms']),'Invalid generation/cutoff');\r\n const configPath=realpathSync(v.config),configBytes=readFileSync(configPath),config=parseJsonConfig(configBytes.toString('utf8'));\r\n const target=validateResetTarget(config,{worker:v.worker,origin:v.origin,binding:v.binding,databaseName:v['database-name'],databaseId:v['database-id']});\r\n check(Object.keys(PRODUCTION_TARGET).every(k=>target[k]===PRODUCTION_TARGET[k]),'Production identity mismatch');\r\n check(!config.account_id||config.account_id===v['account-id'],'Cloudflare account/config mismatch');\r\n const backupPath=realpathSync(v.backup),bytes=readFileSync(backupPath),evidencePath=resolve(v.evidence);\r\n check(/^[a-f0-9]{64}$/.test(v['backup-sha256'])&&sha(bytes)===v['backup-sha256'],'Backup SHA-256 mismatch');\r\n check(!existsSync(evidencePath)&&evidencePath!==backupPath&&evidencePath!==configPath,'Evidence must be a NEW separate path');\r\n check(!v.execute||v['integration-ready']===true,'--execute requires --integration-ready');\r\n check(JSON.parse(readFileSync(new URL('../node_modules/wrangler/package.json',import.meta.url),'utf8')).version==='4.92.0','Reverify native transport before changing Wrangler 4.92.0');\r\n const backupEvidence=backupEvidenceFromSql(bytes);\r\n const dir=mkdtempSync(join(tmpdir(),'myr5-reset-d1-')),proxyConfig=join(dir,'wrangler.json');\r\n // Use ONLY the explicitly verified binding. No production routes/assets/secrets/cron.\r\n writeFileSync(proxyConfig,JSON.stringify({name:'myr5-reset-binding',account_id:v['account-id'],compatibility_date:config.compatibility_date,d1_databases:[{binding:'DB',database_name:target.databaseName,database_id:target.databaseId,remote:true}]}));\r\n let proxy;\r\n try{\r\n  const {getPlatformProxy}=await import('wrangler');\r\n  proxy=await getPlatformProxy({configPath:proxyConfig,envFiles:[],persist:false,remoteBindings:true});\r\n  // This is the native D1 binding, not a loop of REST/Wrangler DELETE commands.\r\n  const result=await resetD1GameGeneration(proxy.env.DB,{target,resetId:v['reset-id'],expectedGeneration:Number(v['expected-generation']),resetAt:Number(v['cutoff-ms']),execute:v.execute,integrationReady:v['integration-ready'],backup:{path:backupPath,sha256:v['backup-sha256']},backupEvidence});\r\n  writeFileSync(evidencePath,JSON.stringify({...result,configPath,configSha256:sha(configBytes),accountId:v['account-id'],transport:'Wrangler 4.92 getPlatformProxy remote D1 / one DB.batch'},null,2)+'\\n',{flag:'wx'});\r\n  console.log(JSON.stringify({mode:result.mode,alreadyReset:result.alreadyReset,gameGeneration:result.gameGeneration,evidence:evidencePath}));\r\n }finally{await proxy?.dispose();unlinkSync(proxyConfig);rmdirSync(dir);}\r\n}\r\nif(process.argv[1]&&import.meta.url===pathToFileURL(resolve(process.argv[1])).href)main().catch(error=>{console.error(error.safeResetMessage||'D1 reset stopped; no record data logged. Inspect configuration, backup and generation; retry the SAME reset ID to recover a committed receipt.');process.exitCode=1;});\r\n","scripts/export-world-sprites.mjs":"// Export ORIGINAL MYR5 art into the existing Piskel editor format; no new editor.\r\nimport {mkdir,writeFile} from 'node:fs/promises';\r\nimport {deflateSync} from 'node:zlib';\r\nimport {pathToFileURL} from 'node:url';\r\nimport {rasterSprite,ART_KINDS,ART_DIRECTION,spriteSizeFor} from '../modules/coach-world/pixel-art.mjs';\r\n// Every frame gets one square cell of the largest sprite size (128); each sprite sits top-left in its cell at its own size (64/96/128). Frame rects in the manifest give the real size.\r\nconst CELL=Math.max(...ART_KINDS.map(spriteSizeFor));\r\n// MYR5_ART_OUT=<dir> redirects output (used for verification without touching the committed art/).\r\nconst out=process.env.MYR5_ART_OUT?pathToFileURL(process.env.MYR5_ART_OUT.replace(/[\\\\/]?$/,'/')):new URL('../modules/coach-world/art/',import.meta.url);await mkdir(out,{recursive:true});\r\nfunction crc32(bytes){let crc=-1;for(const b of bytes){crc^=b;for(let i=0;i<8;i++)crc=(crc>>>1)^((crc&1)?0xedb88320:0);}return (crc^-1)>>>0;}\r\nfunction chunk(type,data){const header=Buffer.alloc(4);header.writeUInt32BE(data.length);const body=Buffer.concat([Buffer.from(type),data]),crc=Buffer.alloc(4);crc.writeUInt32BE(crc32(body));return Buffer.concat([header,body,crc]);}\r\nfunction png(width,height,rgba){const ihdr=Buffer.alloc(13);ihdr.writeUInt32BE(width);ihdr.writeUInt32BE(height,4);ihdr[8]=8;ihdr[9]=6;const rows=Buffer.alloc(height*(width*4+1));for(let y=0;y<height;y++)rgba.copy(rows,y*(width*4+1)+1,y*width*4,(y+1)*width*4);return Buffer.concat([Buffer.from([137,80,78,71,13,10,26,10]),chunk('IHDR',ihdr),chunk('IDAT',deflateSync(rows)),chunk('IEND',Buffer.alloc(0))]);}\r\nconst manifest={version:1,size:CELL,kinds:ART_KINDS,variants:5,factions:{}};\r\nfor(const faction of Object.keys(ART_DIRECTION)){\r\n const frames=[];for(let variant=0;variant<5;variant++)for(const kind of ART_KINDS)frames.push(rasterSprite(faction,kind,variant));\r\n const rgba=Buffer.alloc(frames.length*CELL*CELL*4),width=frames.length*CELL;\r\n for(let f=0;f<frames.length;f++){const sprite=frames[f],N=sprite.width;for(let y=0;y<N;y++)for(let x=0;x<N;x++){const n=sprite.pixels[y*N+x],c=sprite.palette[n],offset=(y*width+f*CELL+x)*4;if(n){rgba[offset]=parseInt(c.slice(1,3),16);rgba[offset+1]=parseInt(c.slice(3,5),16);rgba[offset+2]=parseInt(c.slice(5,7),16);rgba[offset+3]=255;}}}\r\n const image=png(width,CELL,rgba);await writeFile(new URL(`${faction}.png`,out),image);\r\n const file={modelVersion:2,piskel:{name:`MYR5 ${faction}`,description:'Original sprites, 64/96/128 px by footprint, top-left of each 128 px cell (see manifest frame rects). Frames: 18 kinds per tier, five tiers. Upper-left light; one-cell outline. Export a selected frame as PNG and import with scripts/import-world-sprite.py.',fps:1,height:CELL,width:CELL,layers:[JSON.stringify({name:'Architecture and props',opacity:1,frameCount:frames.length,chunks:[{layout:frames.map((_,i)=>[i]),base64PNG:'data:image/png;base64,'+image.toString('base64')}]})],hiddenFrames:[]}};\r\n await writeFile(new URL(`${faction}.piskel`,out),JSON.stringify(file));manifest.factions[faction]={...ART_DIRECTION[faction],png:`${faction}.png`,source:`${faction}.piskel`,frames:frames.map((s,i)=>({kind:s.kind,variant:s.variant,size:s.width,frame:[i*CELL,0,s.width,s.width]}))};\r\n}\r\nawait writeFile(new URL('manifest.json',out),JSON.stringify(manifest,null,2)+'\\n');console.log(`Exported ${Object.keys(ART_DIRECTION).length} editable Piskel sources and PNG sheets (${Object.keys(ART_DIRECTION).length*5*ART_KINDS.length} sprites).`);\r\n","scripts/grimoire-preview.mjs":"import {createServer} from 'node:http';\r\nimport {readFile} from 'node:fs/promises';\r\nimport {resolve,extname,sep} from 'node:path';\r\nimport {transform} from 'esbuild';\r\nimport {ensureThreeVendor} from './assets.mjs';\r\nawait ensureThreeVendor();\r\nconst root=resolve('.');const port=Number(process.env.GRIMOIRE_PORT)||5198;\r\nconst server=createServer(async(req,res)=>{try{const url=new URL(req.url,'http://localhost'),p=url.pathname==='/'?'/standalone/grimoire-lab.html':decodeURIComponent(url.pathname),file=resolve(root,'.'+p);if(!file.startsWith(root+sep))throw Error();const ext=extname(file);res.setHeader('Content-Type',({'.html':'text/html','.mjs':'text/javascript','.js':'text/javascript','.ts':'text/javascript','.css':'text/css','.json':'application/json','.webp':'image/webp','.png':'image/png','.jpg':'image/jpeg','.glb':'model/gltf-binary'})[ext]||'application/octet-stream');res.setHeader('Cache-Control','no-store');const data=await readFile(file);res.end(ext==='.ts'?(await transform(data.toString(),{loader:'ts'})).code:data);}catch{res.writeHead(404);res.end('Not found');}});\r\nserver.listen(port,'127.0.0.1',()=>console.log(`Grimoire workshop http://127.0.0.1:${port}/`));\r\n","scripts/icon-stage.mjs":"import {unlink} from 'node:fs/promises';\r\nimport {join} from 'node:path';\r\nexport async function omitDuplicateCoachIcon(root){try{await unlink(join(root,'icons','coach-512.png'));}catch(error){if(error?.code!=='ENOENT')throw error;}}\r\n","scripts/icons.mjs":"import sharp from 'sharp';\r\nimport {mkdir,copyFile} from 'node:fs/promises';\r\nawait mkdir('icons',{recursive:true});\r\n// Resize the original MYR5 artwork without redrawing or cropping it.\r\nconst source='icons/myr5-original.png',background='#21172f';\r\nasync function icon(size,name,ratio=.9){\r\n const edge=Math.round(size*ratio),pad=Math.floor((size-edge)/2);\r\n const art=await sharp(source).resize(edge,edge,{fit:'contain',background}).png().toBuffer();\r\n await sharp({create:{width:size,height:size,channels:3,background}}).composite([{input:art,left:pad,top:pad}]).removeAlpha().png().toFile('icons/'+name);\r\n}\r\nfor(const size of [192,512]){await icon(size,`myr5-alien-${size}.png`);await copyFile(`icons/myr5-alien-${size}.png`,`icons/coach-${size}.png`);}\r\nawait icon(180,'myr5-alien-apple.png');\r\nawait icon(512,'myr5-alien-maskable.png',.76);\r\n","scripts/import-world-sprite.py":"\"\"\"Import a selected square PNG frame (64, 96 or 128 px; the kind's own size is best) from Piskel/Pixelorama into live sprite data.\r\nUsage: python scripts/import-world-sprite.py chest hq 0 edited.png\r\nRequires Pillow (already available in the studio Python environment).\r\n\"\"\"\r\nimport json, sys\r\nfrom pathlib import Path\r\nfrom PIL import Image\r\nroot=Path(__file__).resolve().parents[1]\r\nfaction,kind,tier,path=sys.argv[1:]\r\nassert faction in ('chest','quads','glutes','arms-shoulders','yoga','martial-arts','cardio')\r\nassert kind in ('hq','hall','building','ship','resource','shop','node','rubble','crew','production','expedition','foundation','drop','landing','care','observatory','habitat','hangar')\r\nassert tier in ('0','1','2','3','4')\r\nim=Image.open(path).convert('RGBA')\r\nassert im.size in ((64,64),(96,96),(128,128)), 'Export a selected 64x64, 96x96 or 128x128 frame, without resizing.'\r\ncolors=['#00000000']; pixels=[]\r\nfor r,g,b,a in im.getdata():\r\n    assert a in (0,255), 'Use hard alpha and disable antialiasing.'\r\n    c=f'#{r:02x}{g:02x}{b:02x}' if a else '#00000000'\r\n    if c not in colors: colors.append(c)\r\n    pixels.append(colors.index(c))\r\nassert len(colors)<=256, 'Use at most 255 visible colors.'\r\ntarget=root/'modules/coach-world/pixel-overrides.mjs'\r\ntext=target.read_text(encoding='utf-8')\r\ndata=json.loads(text.split('Object.freeze(',1)[1].rsplit(');',1)[0])\r\ndata[f'{faction}/{kind}/{tier}']={'palette':colors,'pixels':pixels}\r\ntarget.write_text('// Pixel-editor imports; regenerate sheets with scripts/export-world-sprites.mjs.\\nexport const SPRITE_OVERRIDES=Object.freeze('+json.dumps(data,separators=(',',':'))+');\\n',encoding='utf-8')\r\nprint('Imported',faction,kind,tier,'; reload the app to see the edited sprite.')\r\n","scripts/material-release.mjs":"import { createHash, createPrivateKey, createPublicKey, sign } from 'node:crypto';\r\nimport { mkdir, readFile, writeFile, readdir } from 'node:fs/promises';\r\nimport { dirname, join, resolve, sep } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport { isDeepStrictEqual } from 'node:util';\r\nimport { canonicalChunkPayload, DEFAULT_LOCAL_RESOURCE_POLICY, validateChunkManifest, verifyChunkManifest } from '../modules/materials/chunk-delivery.mjs';\r\n\r\nexport const MATERIAL_SECTION_IDS=Object.freeze(['track-arms','track-cardio','track-chest','track-glutes','track-martial-arts','track-meditation','track-quads','track-yoga','coach-ships-biomes']);\r\nconst SHA=/^[a-f0-9]{40}$/;\r\nconst safeSegment=value=>typeof value==='string'&&/^[A-Za-z0-9_.-]{1,100}$/.test(value)&&value!=='.'&&value!=='..';\r\n\r\n/** Only a full commit object ID is accepted: branch and movable tag names are not immutable pins. */\r\nexport function parseMaterialsBaseUrl(value){\r\n let url;try{url=new URL(value);}catch{throw new Error('MYR5_MATERIALS_BASE_URL must be a commit-pinned raw.githubusercontent.com URL.');}\r\n const match=url.pathname.match(/^\\/([^/]+)\\/([^/]+)\\/([a-f0-9]{40})\\/materials$/);\r\n if(url.origin!=='https://raw.githubusercontent.com'||!match||!safeSegment(match[1])||!safeSegment(match[2])||!SHA.test(match[3])||url.username||url.password||url.search||url.hash)throw new Error('MYR5_MATERIALS_BASE_URL must be https://raw.githubusercontent.com/<owner>/<repo>/<40-char-commit>/materials.');\r\n return Object.freeze({baseUrl:url.href.replace(/\\/$/,''),trustedOrigins:Object.freeze([url.origin]),allowedProtocols:Object.freeze(['https:']),pathPrefix:`/${match[1]}/${match[2]}/${match[3]}/materials/`});\r\n}\r\n\r\nexport function publicJwkFromPrivate(input){\r\n let value;try{value=typeof input==='string'?JSON.parse(input):input;}catch{throw new Error('Material signing key must be an Ed25519 private JWK.');}\r\n if(!value||value.kty!=='OKP'||value.crv!=='Ed25519'||typeof value.d!=='string'||!/^[A-Za-z0-9_-]{43}$/.test(value.d)||typeof value.x!=='string'||!/^[A-Za-z0-9_-]{43}$/.test(value.x))throw new Error('Material signing key must be an Ed25519 private JWK.');\r\n try{const privateKey=createPrivateKey({key:value,format:'jwk'}),derived=createPublicKey(privateKey).export({format:'jwk'});if(derived.x!==value.x)throw new Error();return Object.freeze({kty:'OKP',crv:'Ed25519',x:derived.x});}catch{throw new Error('Material signing key is invalid.');}\r\n}\r\n\r\nexport function loadMaterialPublicBuildConfig(env=process.env){\r\n const rawKey=env.MYR5_MATERIAL_PUBLIC_SIGNING_JWK,base=env.MYR5_MATERIALS_BASE_URL;\r\n if(!rawKey&&!base)return Object.freeze({configured:false,defines:Object.freeze({}),policy:null,publicJwk:null});\r\n if(!rawKey||!base)throw new Error('Set both MYR5_MATERIAL_PUBLIC_SIGNING_JWK and MYR5_MATERIALS_BASE_URL, or neither.');\r\n const host=parseMaterialsBaseUrl(base);let key;try{key=JSON.parse(rawKey);}catch{throw new Error('MYR5_MATERIAL_PUBLIC_SIGNING_JWK must be a public Ed25519 JWK.');}\r\n if(!key||key.kty!=='OKP'||key.crv!=='Ed25519'||typeof key.x!=='string'||!/^[A-Za-z0-9_-]{43}$/.test(key.x)||'d'in key||Object.keys(key).some(field=>!['kty','crv','x','alg','use','key_ops','ext'].includes(field))||(key.key_ops&&(!Array.isArray(key.key_ops)||key.key_ops.some(op=>op!=='verify'))))throw new Error('MYR5_MATERIAL_PUBLIC_SIGNING_JWK must be a public Ed25519 JWK without private fields.');\r\n const publicJwk=Object.freeze({kty:'OKP',crv:'Ed25519',x:key.x}),policy=Object.freeze({trustedOrigins:host.trustedOrigins,allowedProtocols:host.allowedProtocols,pathPrefix:host.pathPrefix});\r\n return Object.freeze({configured:true,publicJwk,policy,defines:Object.freeze({__MYR5_MATERIAL_PUBLIC_JWK__:JSON.stringify(publicJwk),__MYR5_MATERIAL_RESOURCE_POLICY__:JSON.stringify(policy)})});\r\n}\r\n\r\nconst safeOutput=(root,path)=>{const full=resolve(path),base=resolve(root);if(full===base||!full.startsWith(base+sep))throw new Error('Output path must be a dedicated child directory.');return full;};\r\nconst digest=bytes=>createHash('sha256').update(bytes).digest('hex');\r\nconst expectedPaths=id=>id==='coach-ships-biomes'?['supportive','direct','analytical','playful','calm','mom'].map(ship=>`assets/ships/${ship}.glb`).concat('assets/biomes.m5bundle'):[`assets/${id.slice(6)}.m5bundle`];\r\nfunction validateSectionInventory(manifest,host){\r\n const expected=expectedPaths(manifest.packId).sort(),actual=manifest.assets.map(asset=>asset.path).sort();\r\n if(JSON.stringify(actual)!==JSON.stringify(expected))throw new Error(`Section asset inventory mismatch: ${manifest.packId}.`);\r\n for(const asset of manifest.assets){const filename=asset.path.split('/').at(-1),expectedUrl=`${host.baseUrl}/${manifest.packId}/1.0.0/${filename}`;if(asset.chunks.some(chunk=>chunk.url!==expectedUrl))throw new Error(`Section asset URL mismatch: ${manifest.packId}/${asset.path}.`);}\r\n}\r\n\r\n/** Signs all nine manifests, stages host bytes without overwriting conflicts, and stages only manifests for Sites. */\r\nexport async function signAndStageMaterialSections({inputDir,hostRoot,siteRoot,baseUrl,privateJwk,keyId='mom-material-production-v1'}={}){\r\n if(!inputDir||!hostRoot||!siteRoot||!privateJwk)throw new Error('Input, host stage, Site manifest stage, and signing key are required.');\r\n const host=parseMaterialsBaseUrl(baseUrl),input=resolve(inputDir),hostOut=resolve(hostRoot),siteOut=resolve(siteRoot);\r\n const inside=(parent,child)=>child===parent||child.startsWith(parent+sep);\r\n if(inside(input,hostOut)||inside(input,siteOut)||inside(hostOut,siteOut)||inside(siteOut,hostOut))throw new Error('Input, host stage, and Site manifest stage must be separate directory trees.');\r\n if(hostOut.split(sep).some(part=>['dist','client'].includes(part.toLowerCase())))throw new Error('Raw-host asset staging must remain outside deployable Sites directories.');\r\n await mkdir(hostOut,{recursive:true});await mkdir(siteOut,{recursive:true});if((await readdir(siteOut)).length)throw new Error('Site manifest staging must be an empty dedicated directory.');\r\n if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/.test(keyId))throw new Error('Material key ID is invalid.');\r\n const publicJwk=publicJwkFromPrivate(privateJwk),privateKey=createPrivateKey({key:typeof privateJwk==='string'?JSON.parse(privateJwk):privateJwk,format:'jwk'}),policy={...DEFAULT_LOCAL_RESOURCE_POLICY,...host};\r\n const sectionSummaries=[];\r\n for(const id of MATERIAL_SECTION_IDS){\r\n  const dir=join(input,id);let manifest;try{manifest=JSON.parse(await readFile(join(dir,'chunk-manifest.unsigned.json'),'utf8'));}catch{throw new Error(`Missing unsigned manifest for ${id}.`);}\r\n  if(manifest.schema!=='mom-material-chunks-v1'||manifest.packId!==id||manifest.version!=='1.0.0'||!Array.isArray(manifest.assets)||!manifest.assets.length)throw new Error(`Unsigned manifest identity mismatch for ${id}.`);\r\n  manifest.keyId=keyId;manifest.signature='';validateChunkManifest(manifest,policy);validateSectionInventory(manifest,host);\r\n  const hostedPaths=new Set();for(const asset of manifest.assets){\r\n   const urls=new Set(asset.chunks.map(chunk=>chunk.url));if(urls.size!==1)throw new Error(`Asset URLs must resolve to one immutable file: ${id}/${asset.path}`);\r\n   const url=new URL([...urls][0]),expectedPrefix=`${host.baseUrl}/${id}/1.0.0/`,filename=decodeURIComponent(url.pathname.split('/').at(-1)||'');\r\n   if(url.origin!==host.trustedOrigins[0]||!url.href.startsWith(expectedPrefix)||url.search||url.hash)throw new Error(`Chunk URL escapes the configured immutable host: ${id}/${asset.path}`);\r\n   const bytes=await readFile(join(dir,asset.path));if(bytes.byteLength!==asset.bytes||await digest(bytes)!==asset.sha256)throw new Error(`Asset bytes do not match manifest: ${id}/${asset.path}`);\r\n   for(const chunk of asset.chunks)if(await digest(bytes.subarray(chunk.offset,chunk.offset+chunk.bytes))!==chunk.sha256)throw new Error(`Asset chunk hash mismatch: ${id}/${asset.path}/${chunk.index}`);\r\n   if(!safeSegment(filename)||url.pathname!==`${host.pathPrefix}${id}/1.0.0/${filename}`)throw new Error(`Chunk URL has an unsafe asset path: ${id}/${asset.path}`);\r\n   if(hostedPaths.has(url.pathname))throw new Error(`Two assets map to one hosted path: ${id}/${url.pathname}`);hostedPaths.add(url.pathname);\r\n   const destination=safeOutput(hostOut,join(hostOut,'materials',id,'1.0.0',filename));await mkdir(dirname(destination),{recursive:true});\r\n   try{const hosted=await readFile(destination);if(!hosted.equals(bytes))throw new Error(`Existing raw-host file differs from the signed source: ${id}/${filename}`);}catch(error){if(error?.code!=='ENOENT')throw error;await writeFile(destination,bytes,{flag:'wx'});}\r\n  }\r\n  // Publish only authenticated wire-format fields, never arbitrary input metadata.\r\n  manifest=JSON.parse(canonicalChunkPayload(manifest));manifest.signature=sign(null,Buffer.from(canonicalChunkPayload(manifest)),privateKey).toString('base64');\r\n  await verifyChunkManifest(manifest,publicJwk,policy);validateSectionInventory(manifest,host);\r\n  const siteManifest=safeOutput(siteOut,join(siteOut,'materials',id,'chunk-manifest.json'));await mkdir(dirname(siteManifest),{recursive:true});await writeFile(siteManifest,JSON.stringify(manifest,null,2)+'\\n');\r\n  sectionSummaries.push({id,assets:manifest.assets.length,bytes:manifest.assets.reduce((n,a)=>n+a.bytes,0)});\r\n }\r\n return Object.freeze({signed:true,publicJwk,policy,sections:Object.freeze(sectionSummaries)});\r\n}\r\n\r\nexport async function copySignedMaterialManifests({sourceDir,siteRoot,baseUrl,publicJwk}={}){\r\n if(!sourceDir||!siteRoot||!publicJwk)throw new Error('Signed manifest source, Site root, and public trust key are required.');\r\n if(publicJwk.kty!=='OKP'||publicJwk.crv!=='Ed25519'||typeof publicJwk.x!=='string'||!/^[A-Za-z0-9_-]{43}$/.test(publicJwk.x)||'d'in publicJwk)throw new Error('Signed manifest copy requires a public Ed25519 verification key.');\r\n const host=parseMaterialsBaseUrl(baseUrl),source=resolve(sourceDir),site=resolve(siteRoot),inside=(parent,child)=>child===parent||child.startsWith(parent+sep);\r\n if(inside(site,source))throw new Error('Signed manifests must be staged outside the Sites archive.');\r\n const policy={...DEFAULT_LOCAL_RESOURCE_POLICY,...host},copied=[];\r\n for(const id of MATERIAL_SECTION_IDS){\r\n  const path=join(source,'materials',id,'chunk-manifest.json');let manifest,bytes;try{bytes=await readFile(path);manifest=JSON.parse(bytes.toString('utf8'));}catch{throw new Error(`Missing signed Site manifest for ${id}.`);}\r\n  if(manifest.packId!==id||manifest.version!=='1.0.0'||!manifest.signature)throw new Error(`Signed manifest identity mismatch for ${id}.`);\r\n  await verifyChunkManifest(manifest,publicJwk,policy);validateSectionInventory(manifest,host);\r\n  if(!isDeepStrictEqual(manifest,{...JSON.parse(canonicalChunkPayload(manifest)),signature:manifest.signature}))throw new Error(`Signed manifest contains unauthenticated metadata: ${id}.`);\r\n  const destination=safeOutput(site,join(site,'materials',id,'chunk-manifest.json'));await mkdir(dirname(destination),{recursive:true});await writeFile(destination,bytes);copied.push(id);\r\n }\r\n return Object.freeze(copied);\r\n}\r\n\r\n/** Live shipped a bundle whose trust define was never set, so skins failed silently. Refuse that build. */\r\nexport async function assertMaterialTrustShipped(siteRoot){\r\n const manifests=await readdir(join(siteRoot,'materials')).catch(error=>{if(error?.code==='ENOENT')return [];throw error;});\r\n if(!manifests.length)return;\r\n for(const entry of await readdir(siteRoot,{recursive:true,withFileTypes:true}))if(entry.isFile()&&/\\.m?js$/.test(entry.name)){\r\n  const path=join(entry.parentPath,entry.name);\r\n  if(/(?:^|[\\\\/])(?:source|node_modules)[\\\\/]/.test(path))continue;\r\n  if((await readFile(path,'utf8')).includes('__MYR5_MATERIAL_PUBLIC_JWK__'))throw new Error(`Signed material manifests ship in ${siteRoot}/materials, but ${path} has no material public key (__MYR5_MATERIAL_PUBLIC_JWK__ is undefined), so skins and textures would not load. Keep release-trust/public-build.json (or set MYR5_MATERIAL_PUBLIC_SIGNING_JWK) and pass materialRelease.defines to every bundle that imports modules/materials.`);\r\n }\r\n}\r\n\r\nasync function stdinSecret(){if(process.stdin.isTTY)throw new Error('Provide the signing JWK through MYR5_MATERIAL_SIGNING_PRIVATE_JWK or stdin.');let text='';for await(const chunk of process.stdin)text+=chunk;if(!text.trim())throw new Error('Signing JWK input is empty.');return text.trim();}\r\nif(process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href){\r\n try{const inputDir=process.env.MYR5_POST_DOWNLOAD_INPUT_DIR,hostRoot=process.env.MYR5_MATERIAL_HOST_STAGE_DIR,siteRoot=process.env.MYR5_MATERIAL_SITE_MANIFESTS,baseUrl=process.env.MYR5_MATERIALS_BASE_URL,secret=process.env.MYR5_MATERIAL_SIGNING_PRIVATE_JWK??await stdinSecret();const result=await signAndStageMaterialSections({inputDir,hostRoot,siteRoot,baseUrl,privateJwk:secret,keyId:process.env.MYR5_MATERIAL_SIGNING_KEY_ID||undefined});process.stdout.write(JSON.stringify(result)+'\\n');}\r\n catch(error){process.stderr.write(`${error.message}\\n`);process.exitCode=1;}\r\n}\r\n","scripts/mechanical-grimoire-poster.cjs":"#!/usr/bin/env node\r\n/**\r\n * Re-render the 2D fallback poster pod/worlds/boards/cogs-poster.webp\r\n * from the final 3D board at rest.\r\n */\r\n\r\nconst path = require('node:path');\r\nconst fs = require('node:fs').promises;\r\nconst sharp = require('sharp');\r\n\r\nconst PORT = '5197';\r\nconst TARGET_WIDTH = 1024;\r\nconst VIEWPORT = { width: 1400, height: 1900, deviceScaleFactor: 1 };\r\nconst URL = `http://127.0.0.1:${PORT}`;\r\nconst OUTPUT_PATH = path.join(__dirname, '..', 'pod', 'worlds', 'boards', 'cogs-poster.webp');\r\n\r\nasync function main() {\r\n  // Set environment and start preview server in-process\r\n  process.env.MYR5_GRIMOIRE_PORT = PORT;\r\n  const server = require('./mechanical-grimoire-preview.cjs');\r\n\r\n  // Wait briefly for server to start\r\n  await new Promise(r => setTimeout(r, 100));\r\n\r\n  let browser, page;\r\n  try {\r\n    // Launch playwright\r\n    const { chromium } = require('playwright');\r\n    browser = await chromium.launch();\r\n    page = await browser.newPage();\r\n\r\n    // Set viewport\r\n    await page.setViewportSize(VIEWPORT);\r\n\r\n    // Navigate\r\n    await page.goto(URL, { waitUntil: 'networkidle' });\r\n\r\n    // Wait until window.board exists, window.errors.length === 0, plus 1500 ms\r\n    await page.waitForFunction(() => {\r\n      return window.board && window.errors.length === 0;\r\n    }, { timeout: 10000 });\r\n\r\n    await new Promise(r => setTimeout(r, 1500));\r\n\r\n    // Screenshot the #board canvas element\r\n    const boardElement = await page.$('#board');\r\n    if (!boardElement) {\r\n      throw new Error('#board element not found');\r\n    }\r\n\r\n    const screenshot = await boardElement.screenshot({ type: 'png' });\r\n\r\n    // Convert to webp with sharp, resize to width 1024 keeping aspect\r\n    const sharpImage = sharp(screenshot);\r\n    const metadata = await sharpImage.metadata();\r\n    const originalWidth = metadata.width;\r\n    const originalHeight = metadata.height;\r\n    const newHeight = Math.round((TARGET_WIDTH / originalWidth) * originalHeight);\r\n\r\n    const webpBuffer = await sharpImage\r\n      .resize(TARGET_WIDTH, newHeight, {\r\n        fit: 'fill',\r\n        withoutEnlargement: false,\r\n      })\r\n      .webp({ quality: 82 })\r\n      .toBuffer();\r\n\r\n    // Write output\r\n    await fs.writeFile(OUTPUT_PATH, webpBuffer);\r\n\r\n    // Report\r\n    const stats = await fs.stat(OUTPUT_PATH);\r\n    console.log(`Poster regenerated: ${TARGET_WIDTH}x${newHeight} (${stats.size} bytes)`);\r\n\r\n  } finally {\r\n    if (page) await page.close();\r\n    if (browser) await browser.close();\r\n    process.exit(0);\r\n  }\r\n}\r\n\r\nmain().catch(err => {\r\n  console.error('Error:', err);\r\n  process.exit(1);\r\n});\r\n","scripts/mechanical-grimoire-preview.cjs":"// Local review harness: node scripts/mechanical-grimoire-preview.cjs\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..');\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Mechanical grimoire review</title><style>\r\n*{box-sizing:border-box}body{margin:0;background:#090d11;color:#dce5eb;font:14px system-ui}main{display:flex;align-items:center;justify-content:center;gap:32px;min-height:100vh;padding:24px}#board{width:min(66vw,570px);height:calc(100vh - 48px);max-height:880px;position:relative;background:radial-gradient(ellipse,#284050,#090d11 65%)}aside{width:240px}h1{font-size:24px}p{line-height:1.6;color:#94a6b4}label{display:flex;align-items:center;justify-content:space-between;margin:18px 0}button,select{background:#263943;border:1px solid #3f5665;color:#dce5eb;border-radius:6px;padding:10px;margin:4px 0;width:100%}#status{font-size:12px}@media(max-width:650px){main{padding:8px;gap:8px;flex-direction:column}#board{width:100%;height:70vh}aside{width:100%}h1,p{display:none}}\r\n</style><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><main><div id=\"board\"></div><aside><h1>Mechanical grimoire</h1><p>Drag along a shape to turn its gear train. Complete it to release the metal assembly.</p><label>Mechanism <input aria-label=\"Mechanism colour\" id=\"mechanism\" type=\"color\" value=\"#c59a56\"></label><label>Back wall <input aria-label=\"Back wall colour\" id=\"backplate\" type=\"color\" value=\"#263943\"></label><select id=\"shape\" aria-label=\"Shape\"></select><label>Progress <input aria-label=\"Progress\" id=\"progress\" type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"0\"></label><button id=\"trace\">Trace selected shape</button><button id=\"release\">Release selected shape</button><button id=\"reset\">Reset assemblies</button><p id=\"status\">Loading existing models…</p></aside></main><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';import {cogs} from '/modules/portal/portal-board-cogs.mjs';import {mechanicalPaths} from '/modules/portal/portal-mechanical-layout.mjs';import {SHAPES,fromFrame} from '/modules/portal/portal-shapes.mjs';import {portalWindow} from '/modules/portal/portal-apertures.mjs';\r\nconst host=document.querySelector('#board'),status=document.querySelector('#status');window.errors=[];window.addEventListener('error',e=>errors.push(e.message));\r\ntry{window.board=await createGlbBoard(host,{effect:cogs});board.setTint('#c59a56');board.setBackplateTint('#263943');status.textContent='Ready — drag the metal contours';\r\ndocument.querySelector('#mechanism').oninput=e=>board.setTint(e.target.value);document.querySelector('#backplate').oninput=e=>board.setBackplateTint(e.target.value);\r\nconst select=document.querySelector('#shape');[...Object.keys(SHAPES).filter(id=>id!=='line'),'line-lr','line-rl','line-down'].forEach(id=>select.add(new Option(id,id)));const polys=()=>{const id=select.value,templates=id.startsWith('line-')?[SHAPES.line[id==='line-down'?0:1]]:SHAPES[id];return templates.map(p=>p.points.map(([u,v])=>fromFrame(u,v,cogs.frame)));};const aperture=()=>{const f=board.faceRect(),F=cogs.frame,rect={left:F.x0*f.width,top:F.y0*f.height,width:(F.x1-F.x0)*f.width,height:(F.y1-F.y0)*f.height},id=select.value==='cross'?'rect':select.value==='hdiamond'?'vdiamond':select.value;return portalWindow(id,rect).pts.map(([x,y])=>[x/f.width,y/f.height]);};\r\nwindow.traceShape=async()=>{board.heal();const f=board.faceRect();for(const poly of polys()){board.press(99,f.left+poly[0][0]*f.width,f.top+poly[0][1]*f.height);for(let i=1;i<poly.length;i++){const a=poly[i-1],b=poly[i];for(let j=1;j<=8;j++){board.press(99,f.left+(a[0]+(b[0]-a[0])*j/8)*f.width,f.top+(a[1]+(b[1]-a[1])*j/8)*f.height);await new Promise(r=>setTimeout(r,12));}}board.release(99);}};\r\nwindow.cogsProgress=(i,p)=>cogs.progress(i,p);const prog=document.querySelector('#progress');prog.oninput=()=>mechanicalPaths().forEach((p,i)=>{if(p.id===select.value)cogs.progress(i,+prog.value);});\r\ndocument.querySelector('#trace').onclick=traceShape;document.querySelector('#release').onclick=async()=>{await board.cut(aperture(),'#c59a56',1100);status.textContent='Released '+select.value;};document.querySelector('#reset').onclick=()=>{board.heal();status.textContent='Assemblies reset';};\r\nhost.style.touchAction='none';host.onpointerdown=e=>{host.setPointerCapture(e.pointerId);board.press(e.pointerId,e.clientX,e.clientY);};host.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))board.press(e.pointerId,e.clientX,e.clientY);};host.onpointerup=host.onpointercancel=e=>board.release(e.pointerId);\r\n}catch(e){status.textContent=e.stack;errors.push(e.stack);}\r\n</script></body></html>`;\r\nhttp.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');const data=await fs.readFile(file);res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css','.html':'text/html; charset=utf-8'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(Number(process.env.MYR5_GRIMOIRE_PORT)||5198,'127.0.0.1',function(){console.log('Mechanical preview http://127.0.0.1:'+this.address().port);});\r\n","scripts/mediapipe.mjs":"// R9-OFFLINE: the pose tracker is served same-origin from /vendor/mediapipe/<version>/ so a camera\r\n// workout opened once online also runs offline. These are the exact files live used from the CDNs,\r\n// each checked by sha256, downloaded once into vendor/mediapipe/ (gitignored). Same file in both apps.\r\nimport {createHash} from 'node:crypto';\r\nimport {mkdir,readFile,writeFile} from 'node:fs/promises';\r\nimport {dirname} from 'node:path';\r\nexport const MEDIAPIPE_VERSION='0.10.14';\r\nconst npm=`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${MEDIAPIPE_VERSION}/`,model='https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/';\r\nconst FILES={\r\n 'vision_bundle.mjs':[npm,'e77f281f9619150d937023c355bae170e9120e3b9e43f1e23a2a7bee07197669'],\r\n 'wasm/vision_wasm_internal.js':[npm,'9440cf0cc0cea21800e31581ec32aeedcc5fbf9df4509796bbc7d3f99e52ab9c'],\r\n 'wasm/vision_wasm_internal.wasm':[npm,'f82a8e6c05e08a44cc9f9e7ec5f845935bcbb1b1500ebe8c2f4812fb4e2917dc'],\r\n // Browsers without WebAssembly SIMD (Safari before 16.4) fetch these instead.\r\n 'wasm/vision_wasm_nosimd_internal.js':[npm,'abe9b6fbeaf86fcb53a5edce3926c82ccb0619e18fed4d9d9ce561ee7f55e054'],\r\n 'wasm/vision_wasm_nosimd_internal.wasm':[npm,'38b61feab2fd7934e05cbe9f68baa308978a5e3b7f85c1913bb8ae89b8ef8b97'],\r\n 'pose_landmarker_lite.task':[model,'59929e1d1ee95287735ddd833b19cf4ac46d29bc7afddbbf6753c459690d574a'],\r\n};\r\nconst sha256=bytes=>createHash('sha256').update(bytes).digest('hex');\r\nexport async function ensureMediapipe(root=`vendor/mediapipe/${MEDIAPIPE_VERSION}`){\r\n for(const [name,[base,expected]] of Object.entries(FILES)){\r\n  const path=`${root}/${name}`;let bytes=await readFile(path).catch(()=>null);\r\n  if(bytes&&sha256(bytes)===expected)continue;\r\n  const response=await fetch(base+name);if(!response.ok)throw Error(`Could not download MediaPipe ${name}: ${response.status}`);\r\n  bytes=Buffer.from(await response.arrayBuffer());if(sha256(bytes)!==expected)throw Error(`MediaPipe content check failed: ${name}`);\r\n  await mkdir(dirname(path),{recursive:true});await writeFile(path,bytes);\r\n }\r\n}\r\n","scripts/music-loops.py":"\"\"\"Offline tool: cut Ian's six songs into seamless 8-bar loops -> audio/music/*.m4a + manifest.json + report.\r\nRun: python scripts/music-loops.py   (needs numpy, scipy, ffmpeg; sources in .vault/music-src)\r\nMethod: tempo/bar grid from beat analysis (hand-checked start s0, bpm0 per song), then a small search around (start, length)\r\nfor the quietest seam (log-mel jump across the wrap, ranked against every interior point), with the reverb tail folded onto the head.\r\nEncodes AAC-LC .m4a (iOS-safe) and verifies the decode length.\"\"\"\r\nimport numpy as np, scipy.io.wavfile as wav, subprocess, json, os, sys, pathlib\r\nfrom scipy.signal import stft\r\nROOT = pathlib.Path(__file__).resolve().parent.parent\r\nFF = os.environ.get('FFMPEG', r'C:/Users/ianmy/AppData/Local/Microsoft/WinGet/Packages/Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe/ffmpeg-9.0-full_build/bin/ffmpeg')\r\nSR = 44100\r\n# name: (start s0 [s], bpm0, tempo tolerance, note). 8 bars of 4/4 = 32 beats.\r\nSONGS = {\r\n 'hey-man-idk': (0.62, 104.3, .012, 'music begins 0.6 s; level drops at 19.0 s'),\r\n 'guarded-gate': (0.04, 120.0, .0, '120 bpm grid fits cleanly for 8 bars (16 s); music keeps playing after bar 8'),\r\n 'daemon-time': (0.62, 90.7, .012, 'grid phase 0.6 s, ~91 bpm throughout the body'),\r\n 'sick-with-science': (0.04, 140.5, .012, 'drop at 13.7 s'),\r\n 'laboratory-violence': (0.0, 120.0, .0, 'drop at 15.98 s = 8 bars at 120'),\r\n 'main-theme-one': (2.0, 120.0, .0, '2 s count-in clicks then music; self-similar at +16 s'),\r\n}\r\ndef decode(path):\r\n    p = subprocess.run([FF, '-v', 'error', '-i', str(path), '-f', 'f32le', '-ac', '2', '-ar', str(SR), '-'], capture_output=True, check=True)\r\n    return np.frombuffer(p.stdout, np.float32).reshape(-1, 2).copy()\r\ndef mel(x, n=1024):\r\n    f, t, S = stft(x.mean(1), SR, nperseg=n, noverlap=n - 256, boundary=None, padded=False)\r\n    e = np.geomspace(60, 9000, 33); M = np.abs(S)\r\n    return np.log1p(np.array([M[(f >= a) & (f < b)].sum(0) for a, b in zip(e[:-1], e[1:])]) * 20)\r\ndef rms_db(x): return 20 * np.log10(np.sqrt((x.astype(np.float64) ** 2).mean()) + 1e-9)\r\ndef fold(x, s, L):\r\n    \"\"\"loop = x[s:s+L] with the decaying tail x[s+L:s+L+T] added onto its head.\"\"\"\r\n    body = x[s:s + L].copy(); tail = x[s + L:]\r\n    ref = np.sqrt((body.mean(1) ** 2).mean()); T = 0\r\n    if len(tail):\r\n        env = np.sqrt(np.convolve(tail.mean(1) ** 2, np.ones(441) / 441, 'same'))\r\n        below = np.where(env < ref * .08)[0]\r\n        T = int(below[0]) if len(below) else len(tail)\r\n    T = int(min(T, 3 * SR, len(tail), L // 3))\r\n    if len(tail) > 4410 and (np.sqrt((tail[:4410].mean(1) ** 2).mean()) > ref * .6 or T > int(1.5 * SR)):\r\n        # the music keeps playing past bar 8 (no decaying tail): continue the waveform and crossfade into the head\r\n        k = min(int(.06 * SR), len(tail)); t = np.linspace(0, np.pi / 2, k, dtype=np.float32)[:, None]\r\n        body[:k] = body[:k] * np.sin(t) + tail[:k] * np.cos(t); return body, k\r\n    if T > 0:\r\n        w = np.ones(T, np.float32); k = min(T, int(.25 * SR)) if T < 2 * SR else int(.6 * SR)\r\n        w[-k:] = np.cos(np.linspace(0, np.pi / 2, k)) ** 2\r\n        body[:T] += tail[:T] * w[:, None]\r\n    return body, T\r\ndef seam_stats(y):\r\n    \"\"\"Jump across the wrap vs the same measure at every interior hop. Lower percentile = smoother seam.\"\"\"\r\n    z = np.concatenate([y, y]); n = len(y); h = 256; W = 4\r\n    M = mel(z)\r\n    def jump(c):\r\n        i = c // h; a = M[:, i - W - 3:i - 3].mean(1); b = M[:, i + 1:i + 1 + W].mean(1); return np.linalg.norm(a - b)\r\n    cuts = np.array([int(round(n * k / 32)) for k in range(4, 32, 4)])  # the 7 interior bar lines (the wrap is a bar line too)\r\n    inner = np.array([jump(c) for c in cuts]); seam = jump(n)\r\n    pct = float((inner < seam).mean() * 100)\r\n    k = int(.005 * SR); rj = abs(rms_db(y[-k:]) - rms_db(y[:k]))\r\n    step = float(np.abs(y[0] - y[-1]).max()); typ = float(np.abs(np.diff(y, axis=0)).mean() * 6)\r\n    return {'melJumpPercentile': round(pct, 1), 'melJump': round(float(seam), 3), 'barLineMedianJump': round(float(np.median(inner)), 3), 'rmsJump5msDb': round(float(rj), 2), 'sampleStep': round(step, 4), 'typicalStep6x': round(typ, 4)}\r\ndef search(x, s0, bpm0, tol):\r\n    L0 = 32 * 60 / bpm0; best = None\r\n    for ds in np.arange(-0.02, 0.0201, 0.01):\r\n        for dl in (np.linspace(-tol, tol, 9) if tol else [0]):\r\n            s = int(round((s0 + ds) * SR)); L = int(round(L0 * (1 + dl) * SR))\r\n            if s < 0 or s + L > len(x): continue\r\n            y, T = fold(x, s, L); st = seam_stats(y)\r\n            cost = st['melJumpPercentile'] + st['rmsJump5msDb'] * 4 + abs(dl) * 1500 + abs(ds) * 200\r\n            if best is None or cost < best[0]: best = (cost, s, L, T, st, 32 * 60 * SR / L)\r\n    return best\r\ndef main():\r\n    only = set(sys.argv[1:]); out = ROOT / 'audio' / 'music'; out.mkdir(parents=True, exist_ok=True)\r\n    chk = ROOT / '.vault' / 'audio-check'; chk.mkdir(parents=True, exist_ok=True); (ROOT / '.vault' / 'tmp').mkdir(exist_ok=True)\r\n    manifest = {'version': 1, 'sampleRate': SR, 'bars': 8, 'tracks': {}}; rows = []\r\n    for name, (s0, bpm0, tol, note) in SONGS.items():\r\n        if only and name not in only: continue\r\n        x = decode(ROOT / '.vault' / 'music-src' / f'{name}.mp3')\r\n        cost, s, L, T, st, bpm = search(x, s0, bpm0, tol)\r\n        y, _ = fold(x, s, L)\r\n        peak = np.abs(y).max(); rms = np.sqrt((y ** 2).mean()); g = min(.10 / rms, .89 / peak); y = (y * g).astype(np.float32)\r\n        tmp = ROOT / '.vault' / 'tmp' / f'{name}.loop.wav'; wav.write(tmp, SR, (np.clip(y, -1, 1) * 32767).astype(np.int16))\r\n        m4a = out / f'{name}.m4a'\r\n        subprocess.run([FF, '-v', 'error', '-y', '-i', str(tmp), '-c:a', 'aac', '-b:a', '112k', '-movflags', '+faststart', str(m4a)], check=True)\r\n        d = subprocess.run([FF, '-v', 'error', '-i', str(m4a), '-f', 'f32le', '-ac', '2', '-ar', str(SR), '-'], capture_output=True, check=True).stdout\r\n        dec = np.frombuffer(d, np.float32).reshape(-1, 2); frames = len(y)\r\n        three = np.concatenate([dec[:frames] if len(dec) >= frames else y] * 3)\r\n        wav.write(chk / f'{name}-x3.wav', SR, (np.clip(three, -1, 1) * 32767).astype(np.int16))\r\n        manifest['tracks'][name] = {'url': f'/audio/music/{name}.m4a', 'bpm': round(bpm, 2), 'bars': 8, 'beats': 32, 'frames': frames, 'loopStart': 0, 'loopEnd': round(frames / SR, 6), 'duration': round(frames / SR, 6), 'sourceStart': round(s / SR, 4), 'tailFolded': round(T / SR, 3), 'bytes': m4a.stat().st_size, 'decodedFrames': int(len(dec))}\r\n        rows.append((name, round(bpm, 2), round(frames / SR, 3), round(s / SR, 3), round(T / SR, 2), st, m4a.stat().st_size, int(len(dec)) - frames, note))\r\n        print(rows[-1], flush=True)\r\n    if not only:\r\n        (out / 'manifest.json').write_text(json.dumps(manifest, indent=1))\r\n        r = ['# Music loop report', '', 'Tempo/bars come from onset-grid fits plus level drops (see scripts/music-loops.py). Seam pct = share of the 7 interior bar lines whose spectral jump is SMALLER than the wrap (the wrap is a bar line, so a bar-line-sized jump is expected: under ~70 = as smooth as a normal bar line, 100 = rougher than every one). Nobody could listen: judge by ear with `.vault/audio-check/*-x3.wav`.', '',\r\n             '| song | bpm | bars | loop s | source start s | tail folded s | seam pct | mel jump (seam / typical) | rms jump 5ms dB | m4a bytes | decode len - frames | note |', '|---|---|---|---|---|---|---|---|---|---|---|---|']\r\n        for n, b, d, s, t, st, by, dl, note in rows:\r\n            r.append(f\"| {n} | {b} | 8 | {d} | {s} | {t} | {st['melJumpPercentile']} | {st['melJump']} / {st['barLineMedianJump']} | {st['rmsJump5msDb']} | {by} | {dl} | {note} |\")\r\n        (ROOT / '.vault' / 'audio-report.md').write_text('\\n'.join(r) + '\\n')\r\nmain()\r\n","scripts/music-reloop.py":"\"\"\"Direct seam search for songs whose tempo grid is unreliable (hey-man-idk, guarded-gate).\r\nFor each start s the 0.2 s of audio around s (pre 0.1 s + post 0.1 s) is cross-correlated with the whole song: a peak at lag L means\r\nthe music at s+L looks (waveform-wise, so beat phase included) like the music at s, i.e. x[s+L] can follow x[s+L-1]... Loop = x[s:s+L]\r\nwith a 40 ms equal-power crossfade of the continuation x[s+L:s+L+k] into the head. Usage: python scripts/music-reloop.py name [lo hi]\"\"\"\r\nimport sys, numpy as np, scipy.io.wavfile as wav, subprocess, json, pathlib\r\nfrom scipy.signal import fftconvolve\r\nsys.argv_save = sys.argv; src = open(pathlib.Path(__file__).with_name('music-loops.py'), encoding='utf-8').read().replace(\"\\nmain()\\n\", \"\\n\")\r\nns = {'__file__': str(pathlib.Path(__file__).with_name('music-loops.py'))}; exec(src, ns)\r\nSR = 44100; ROOT = ns['ROOT']; FF = ns['FF']\r\ndef search(x, s_lo, s_hi, L_lo, L_hi, pre=.1, post=.1):\r\n    m = x.mean(1).astype(np.float64); n = len(m); res = []\r\n    csq = np.concatenate([[0], np.cumsum(m ** 2)])\r\n    for s in np.arange(s_lo, s_hi, .005):\r\n        a = int(s * SR); lo, hi = a - int(pre * SR), a + int(post * SR)\r\n        if lo < 0: continue\r\n        t = m[lo:hi]; te = (t ** 2).sum()\r\n        c = fftconvolve(m, t[::-1], 'valid')            # c[i] = sum m[i:i+len]*t\r\n        i = np.arange(len(c)); en = csq[i + len(t)] - csq[i]\r\n        ncc = c / np.sqrt(en * te + 1e-12)\r\n        for L in range(int(L_lo * SR), min(int(L_hi * SR), len(c) - 1 - a + lo)):\r\n            pass\r\n        idx = np.arange(lo + int(L_lo * SR), min(lo + int(L_hi * SR), len(ncc)))\r\n        if len(idx) == 0: continue\r\n        j = idx[np.argmax(ncc[idx])]; res.append((float(ncc[j]), s, (j - lo) / SR))\r\n    res.sort(reverse=True); return res\r\ndef build(x, s, L, k=int(.04 * SR)):\r\n    a = int(round(s * SR)); n = int(round(L * SR)); body = x[a:a + n].copy(); tail = x[a + n:a + n + k]\r\n    k = len(tail); t = np.linspace(0, np.pi / 2, k, dtype=np.float32)[:, None]\r\n    body[:k] = body[:k] * np.sin(t) + tail * np.cos(t); return body\r\nCUTS = {'hey-man-idk': (0.585, 10.1893, '8 bars at ~188.4 bpm; flux-pattern repeat found by direct search (the old 105 bpm grid was wrong)'),\r\n        'guarded-gate': (0.075, 15.8284, 'direct waveform seam search near 8 bars; music continues past the loop, so the head is crossfaded with the continuation')}\r\ndef main():\r\n    name = sys.argv[1]; s, L, note = CUTS[name]; scripts = ns\r\n    x = scripts['decode'](ROOT / '.vault' / 'music-src' / f'{name}.mp3')\r\n    y = build(x, s, L); st = scripts['seam_stats'](y)\r\n    peak = np.abs(y).max(); rms = np.sqrt((y ** 2).mean()); y = (y * min(.10 / rms, .89 / peak)).astype(np.float32)\r\n    tmp = ROOT / '.vault' / 'tmp' / f'{name}.loop.wav'; wav.write(tmp, SR, (np.clip(y, -1, 1) * 32767).astype(np.int16))\r\n    m4a = ROOT / 'audio' / 'music' / f'{name}.m4a'\r\n    subprocess.run([FF, '-v', 'error', '-y', '-i', str(tmp), '-c:a', 'aac', '-b:a', '112k', '-movflags', '+faststart', str(m4a)], check=True)\r\n    dec = np.frombuffer(subprocess.run([FF, '-v', 'error', '-i', str(m4a), '-f', 'f32le', '-ac', '2', '-ar', str(SR), '-'], capture_output=True, check=True).stdout, np.float32).reshape(-1, 2)\r\n    frames = len(y); three = np.concatenate([dec[:frames]] * 3)\r\n    chk = ROOT / '.vault' / 'audio-check'; w3 = chk / f'{name}-x3.wav'; wav.write(w3, SR, (np.clip(three, -1, 1) * 32767).astype(np.int16))\r\n    mp = ROOT / 'audio' / 'music' / 'manifest.json'; man = json.loads(mp.read_text())\r\n    man['tracks'][name].update({'bpm': round(32 * 60 / L, 2), 'frames': frames, 'loopEnd': round(frames / SR, 6), 'duration': round(frames / SR, 6), 'sourceStart': round(s, 4), 'tailFolded': 0.04, 'bytes': m4a.stat().st_size, 'decodedFrames': int(len(dec))})\r\n    mp.write_text(json.dumps(man, indent=1))\r\n    print(name, 'bpm', round(32 * 60 / L, 2), 'loop', round(L, 4), 'bytes', m4a.stat().st_size, 'dec-frames', len(dec) - frames, st)\r\nmain()\r\n","scripts/octave.cjs":"const {execFile}=require('node:child_process');\r\nconst fs=require('node:fs');\r\nconst PROCESSOR=process.env.MYR5_FFMPEG||'ffmpeg';\r\nfunction repairWave(wav){\r\n if(wav.length<44||wav.toString('ascii',0,4)!=='RIFF')throw Error('Invalid speech audio');\r\n wav.writeUInt32LE(wav.length-8,4);\r\n for(let offset=12;offset+8<=wav.length;){const size=wav.readUInt32LE(offset+4);if(wav.toString('ascii',offset,offset+4)==='data'){wav.writeUInt32LE(wav.length-offset-8,offset+4);return wav;}offset+=8+size+(size%2);}\r\n throw Error('Missing speech samples');\r\n}\r\nfunction lowerOctave(input){return new Promise((resolve,reject)=>{\r\n const child=execFile(PROCESSOR,['-hide_banner','-loglevel','error','-threads','1','-f','wav','-i','pipe:0','-af','rubberband=pitch=0.8908987181:tempo=1:formant=preserved:pitchq=quality,highpass=f=50,equalizer=f=2600:t=q:w=1.2:g=-2,lowpass=f=7200,alimiter=limit=0.95','-ac','1','-ar','22050','-c:a','pcm_s16le','-f','wav','pipe:1'],{encoding:'buffer',windowsHide:true,timeout:7000,maxBuffer:8*1024*1024},(error,output,stderr)=>{if(error){reject(Error('Deep robot voice unavailable: '+error.message+' '+String(stderr)));return;}try{resolve(repairWave(output));}catch(e){reject(e);}});\r\n child.stdin.on('error',()=>{});child.stdin.end(input);\r\n});}\r\nmodule.exports={lowerOctave,repairWave,available:()=>fs.existsSync(PROCESSOR)};\r\n","scripts/offline-assets.mjs":"import {createHash} from 'node:crypto';\r\nimport {createReadStream} from 'node:fs';\r\nimport {readdir,readFile,writeFile,stat} from 'node:fs/promises';\r\nimport {join,posix} from 'node:path';\r\nimport {fileURLToPath} from 'node:url';\r\nimport {build} from 'esbuild';\r\n\r\nconst folders=['pod','creature','models','icons','handborne','arcade','war-room','food','vendor','modules','audio','pods','reward-assets'];\r\n// D34: core precache holds only what a first run needs (sign-in/onboarding, home, a camera or manual\r\n// workout with its counter, updates/recovery, the offline shell). Core is every root, /icons/ or /pod/\r\n// file that the first-run pages name, directly or through other core files. The scan is conservative:\r\n// any path string counts. Bundled sources that no page requests, and everything else, ship in the\r\n// post-download package (sw.js OPTIONAL_ASSETS): fetched on demand online, offline once downloaded.\r\nconst CORE_ENTRIES=['/pose.html','/index.html','/onboarding.html','/signin.html','/install.html','/privacy.html','/manifest.webmanifest'];\r\n// W2-2O (#136): the portal experience's heavy art is ONE optional \"Starter\" download, never core: the quilt\r\n// texture, the pyramid model, the starter ship + six wonders, the still-room backdrop and the achievements\r\n// art (everything in pod/worlds/). Their small code (modules/portal, the ship view, the pyramid scanner,\r\n// the GLTF loader) stays core, so each scene opens offline and shows its own placeholder.\r\n// R9-OFFLINE: the pinned pose tracker (vendor/mediapipe, scripts/mediapipe.mjs) is Starter too, so camera workouts run offline.\r\nexport const STARTER=/^\\/(?:pod\\/worlds\\/|food\\/pyramid-scanner\\.glb$|vendor\\/mediapipe\\/)/;\r\nexport const BOARDS=/^\\/pod\\/worlds\\/boards\\//;\r\n// Achievement Vault: lazy code and art, never core (the hall's roster GLBs are fetched on demand).\r\nexport const VAULT=/^\\/(?:modules\\/vault\\/|pod\\/worlds\\/vault\\/)/;\r\nconst TUNNELS=/^\\/modules\\/portal\\/portal-tunnel-(?:ice|grass|cogs|jelly|wood|pond|clay|paper|mache)\\.mjs$/;\r\nexport const SCOREBOARD_ROOM=/^\\/(?:pod\\/rooms\\/classroom-(?:wall|desks)\\.glb|modules\\/rooms\\/classroom\\.(?:mjs|css))$/;\r\nexport const REMINDERS_ROOM=/^\\/(?:pod\\/rooms\\/console\\.(?:glb|webp)|modules\\/rooms\\/reminders-computer\\.css)$/;\r\n// W4-4E (D47): the customizer cage and the Draco decoder only it needs. Never core; the customizer stays 2D without it.\r\nexport const CAGE_ROOM=/^\\/pod\\/rooms\\/cage\\//;\r\n// Page music (six AAC loops + manifest) is optional Music. These raw DJ authoring\r\n// modules are also optional: production Coach statically bundles them in core\r\n// launch-runtime.mjs. Keep their standalone source route without duplicating\r\n// first-run bytes; genuine runtime CSS, visualizer vendors and Hand assets retain\r\n// their existing groups.\r\nexport const MUSIC=/^\\/(?:audio\\/music\\/|actual-dj(?:-providers|-hand|-playlist)?\\.mjs$)/;\r\n// Each grimoire's art: its GLB board (cogs: its folder), its flat poster (portal.mjs's base layer) and its tunnel effect.\r\nconst GRIMOIRE_ART={ice:'ice\\\\.glb',grass:'(?:grass|flower)\\\\.glb',cogs:'cogs/.+',jelly:'jelly\\\\.glb',wood:'wood\\\\.glb',pond:'(?!)',clay:'clay/.+',paper:'paper/.+',mache:'mache/.+'}; // pixel is the default Starter; pond is procedural\r\nconst GRIMOIRE_GROUPS=Object.entries(GRIMOIRE_ART).map(([id,art])=>['grimoire-'+id,new RegExp(`^/(?:pod/worlds/boards/(?:${art}|${id}-poster\\\\.webp)|modules/portal/portal-tunnel-${id}\\\\.mjs)$`)]);\r\n// Named by first-run code, but used only by deferrable features that already cope without them:\r\n// food reference search (2.6 MB) and Records handwriting fonts (swap).\r\n// The production pages use the generated runtime bundles; source entries remain published for dev/source tools,\r\n// but are not downloaded in addition to those bundles. The conservative URL scanner also sees their names in comments.\r\nconst DEFERRED=/^\\/(?:app\\.mjs$|launch\\.mjs$|nutrition-data\\.mjs$|pod\\/fonts\\/|food\\/drgf-paper-character\\.png$)/;\r\nconst coreFolder=url=>!url.slice(1).includes('/')||url==='/modules/visual-flags.mjs'||/^\\/(?:audio|icons|modules\\/portal|modules\\/ships|modules\\/shell|food|vendor\\/three)\\//.test(url)||url.startsWith('/pod/')&&!/\\.(?:glb|gltf|bin)$/i.test(url);\r\nconst reference=/(?:\\.{1,2}\\/|\\/)?[\\w@][\\w\\-./@]*\\.(?:html|css|mjs|js|webmanifest|json|mp3|ogg|wav|png|jpe?g|webp|avif|gif|svg|ico|glb|gltf|bin|woff2?|ttf|otf)\\b/g;\r\nconst runtime=/\\.(?:html|css|mjs|js|webmanifest|json|mp3|m4a|ogg|wav|png|jpe?g|webp|avif|gif|svg|ico|glb|gltf|bin|wasm|task|woff2?|ttf|otf)$/i;\r\nconst excluded=new Set(['sw.js','source.json','source.json.gz','package.json','package-lock.json','recover.html','recovery-page.mjs','about.html']);\r\n\r\nasync function identify(root,path){\r\n const hash=createHash('sha256');\r\n for await(const chunk of createReadStream(join(root,path)))hash.update(chunk);\r\n return {url:'/'+path,integrity:'sha256-'+hash.digest('base64'),bytes:(await stat(join(root,path))).size};\r\n}\r\nasync function coreClosure(root,urls,template){\r\n const core=new Set(),queue=[...CORE_ENTRIES];\r\n try{for(const [ref] of (await readFile(template,'utf8')).matchAll(reference))queue.push(ref);}catch(error){if(error.code!=='ENOENT')throw error;}\r\n while(queue.length){\r\n  const url=queue.shift();\r\n if(core.has(url)||!urls.has(url)||!coreFolder(url)||DEFERRED.test(url)||STARTER.test(url)||BOARDS.test(url)||VAULT.test(url)||TUNNELS.test(url)||SCOREBOARD_ROOM.test(url)||REMINDERS_ROOM.test(url)||CAGE_ROOM.test(url)||MUSIC.test(url))continue;\r\n  core.add(url);\r\n  if(/\\.(?:html|css|mjs|js|webmanifest|json)$/.test(url))for(const [ref] of (await readFile(join(root,url),'utf8')).matchAll(reference))\r\n   queue.push(ref.startsWith('/')?ref:posix.join(posix.dirname(url),ref),'/'+ref.replace(/^\\.\\//,''));\r\n }\r\n return core;\r\n}\r\n// The voice clips (not core) are listed with their identities inside the voice manifest, whose own\r\n// identity is in the package list; `contains` is the byte total of the clips it names.\r\nexport async function identifyVoice(root){\r\n let manifest;try{manifest=JSON.parse(await readFile(join(root,'voice/manifest.json'),'utf8'));}catch(error){if(error.code==='ENOENT')return;throw error;}\r\n manifest.files=[];for(const url of new Set(Object.values(manifest.phrases)))manifest.files.push(await identify(root,url.slice(1)));\r\n await writeFile(join(root,'voice/manifest.json'),JSON.stringify(manifest));\r\n}\r\n\r\n// The group manifest: the Downloads menu picks the post-download package by group; every optional file\r\n// carries one. First match wins; whatever is left (regular coach models, customizer, exercise demos, app\r\n// screens) is \"Your coach\". Roster bodies never join it: each goes to its workout section from\r\n// track-placements.ts (#102), or the bodies' Starter section when it has no placement.\r\n// \"starter\" (W2-2O) is the portal experience's art. Vault code and art remain optional public assets,\r\n// grouped into the same integrity-checked post-download package as the rest of the library.\r\n// Each grimoire device downloads its own board art and tunnel effect together. Quilt stays Starter.\r\nconst GROUPS=[\r\n ...GRIMOIRE_GROUPS,\r\n ['room-scoreboard',SCOREBOARD_ROOM],\r\n ['room-reminders',REMINDERS_ROOM],\r\n ['room-cage',CAGE_ROOM],\r\n ['music',MUSIC],\r\n ['vault',VAULT],\r\n ['starter',/^\\/modules\\/portal\\/portal-tunnel-(?:pixel|quilt)\\.mjs$/],\r\n ['starter',STARTER],\r\n ['drop-pods',/^\\/(?:pods|reward-assets)\\//],\r\n ['voices',/^\\/voice\\//],\r\n ['hand',/^\\/handborne\\//],\r\n // The meshopt decoder is the pyramid's alone (release 5): core here through the pyramid scanner (W2-2O keeps the scenes'\r\n // code core), and with Food should it ever leave core. GLTFLoader is shared (hologram, ships, pyramid): core (coreFolder).\r\n ['food',/^\\/(?:food\\/|vendor\\/three\\/meshopt_decoder\\.module\\.js$|nutrition-data\\.mjs$|food-live\\.css$|meal-)/],\r\n ['meditation',/^\\/(?:meditation|breathing)/],\r\n ['games',/^\\/(?:arcade|war-room)\\//],\r\n];\r\nconst ROSTER_BODY=/^\\/creature\\/models\\/roster\\/[^/]+\\.glb$/;\r\nlet placements;\r\nexport function bodySections(){\r\n placements??=build({entryPoints:[fileURLToPath(new URL('../creature/source/creator/track-placements.ts',import.meta.url))],bundle:true,write:false,format:'esm',platform:'neutral',logLevel:'silent'})\r\n  .then(({outputFiles:[out]})=>import('data:text/javascript;base64,'+Buffer.from(out.text).toString('base64')))\r\n  .then(({TRACK_PLACEMENTS})=>new Map(TRACK_PLACEMENTS.map(p=>['/'+p.sourceAsset,p.tracks[0]])));\r\n return placements;\r\n}\r\nexport const groupOf=(url,sections)=>ROSTER_BODY.test(url)?'bodies-'+(sections.get(url)??'starter'):GROUPS.find(([,test])=>test.test(url))?.[0]??'coach';\r\n\r\n// PackLifecycle bytes are outside both inventories.\r\nexport async function offlineInventory(root,template='sw.js'){\r\n const assets=[];\r\n async function walk(path){\r\n  for(const entry of await readdir(join(root,path),{withFileTypes:true})){\r\n   if(entry.name.startsWith('.')||entry.name==='source')continue;\r\n   const name=path+'/'+entry.name;\r\n   if(entry.isDirectory())await walk(name);\r\n   else if(entry.isFile()&&runtime.test(entry.name))assets.push(await identify(root,name));\r\n  }\r\n }\r\n for(const entry of await readdir(root,{withFileTypes:true}))if(entry.isFile()&&!excluded.has(entry.name)&&runtime.test(entry.name))assets.push(await identify(root,entry.name));\r\n for(const folder of folders)try{await walk(folder);}catch(error){if(error.code!=='ENOENT')throw error;}\r\n const core=await coreClosure(root,new Set(assets.map(a=>a.url)),template);\r\n try{const {files}=JSON.parse(await readFile(join(root,'voice/manifest.json'),'utf8'));if(Array.isArray(files))assets.push({...await identify(root,'voice/manifest.json'),contains:files.reduce((sum,file)=>sum+file.bytes,0)});}catch(error){if(error.code!=='ENOENT')throw error;}\r\n assets.sort((a,b)=>a.url.localeCompare(b.url));\r\n const sections=await bodySections();\r\n return {core:assets.filter(a=>core.has(a.url)),optional:assets.filter(a=>!core.has(a.url)).map(a=>({...a,group:groupOf(a.url,sections)}))};\r\n}\r\n\r\nexport async function offlineAssets(root){return (await offlineInventory(root)).core;}\r\n\r\nexport async function writeOfflineWorker(root,buildId,template='sw.js'){\r\n await identifyVoice(root);\r\n const {core:assets,optional}=await offlineInventory(root,template);\r\n for(const required of ['/shell-entry.mjs','/modules/shell/owner.mjs','/modules/shell/game-phase.mjs','/modules/shell/shell.css','/modules/visual-flags.mjs','/pod/instrument-controls.mjs','/pod/instrument-controls.css'])if(!assets.some(asset=>asset.url===required&&asset.bytes>0))throw Error('Missing offline cockpit asset: '+required);\r\n for(const game of ['runner','cover','dance','plank-flight'])if(!assets.some(asset=>asset.url===`/pod/entry/${game}.webp`&&asset.bytes>0&&asset.bytes<=80000))throw Error('Missing or oversized offline game poster: '+game);\r\n for(const required of ['/pose.html','/app-runtime.mjs','/launch-bootstrap.mjs','/launch-runtime.mjs','/local-coach-runtime.mjs','/onboarding.html','/onboarding.mjs','/workout-tracks.js','/pod/gala-weapons.js','/pod/gala-avatar.js','/pod/gala-performer.js','/pod/dj-identity.js']){\r\n  if(!assets.some(asset=>asset.url===required&&asset.bytes>0))throw Error('Missing offline Coach asset: '+required);\r\n }\r\n const source=await readFile(template,'utf8');\r\n if(!source.includes('/* OFFLINE_ASSETS */ []'))throw Error('Offline worker template is missing its asset marker.');\r\n await writeFile(join(root,'sw.js'),source.replace(/^const SHELL='[^']*'/,`const SHELL='myr5-shell-${buildId}'`).replace('/* OFFLINE_ASSETS */ []',JSON.stringify(assets)).replace('/* OPTIONAL_ASSETS */ []',JSON.stringify(optional)));\r\n const mib=list=>(list.reduce((sum,asset)=>sum+asset.bytes+(asset.contains||0),0)/1048576).toFixed(1);\r\n const groups=Object.entries(Object.groupBy(optional,a=>a.group)).map(([id,list])=>`${id} ${list.length}/${mib(list)}`).join(', ');\r\n console.log(`Offline Coach core: ${assets.length} files, ${mib(assets)} MiB. Complete public content package: ${optional.length} entries, ${mib(optional)} MiB (groups, files/MiB: ${groups}).`);\r\n return assets;\r\n}\r\n","scripts/optimize-glb.mjs":"import {readFile,writeFile,readdir} from 'node:fs/promises';\r\nimport {join} from 'node:path';\r\nimport {createHash} from 'node:crypto';\r\nimport {MeshoptEncoder,MeshoptDecoder} from 'meshoptimizer';\r\nimport sharp from 'sharp';\r\n// Shrinks a GLB without changing what a loader gets back, so code that reads vertices, samples\r\n// triangles or finds nodes by name behaves exactly as with the authored file. Vertex, index, skin and\r\n// animation data move into EXT_meshopt_compression with no quantization, filters or reordering, and\r\n// index buffers use INDICES mode (TRIANGLES mode rotates each triangle's corners): every accessor\r\n// decodes to its original bytes, checked here before anything is written. The glTF JSON is kept\r\n// (names, transforms, materials); only buffer views move, and identical ones share bytes (this\r\n// replaces the old compaction pass). PNG textures become lossless WebP only when the decoded pixels\r\n// match and it is smaller; JPEGs stay JPEG (re-encoding them loses detail).\r\n// ponytail: lossless only. Rounding normals to 16 bits (exponential filter) saves ~15 MiB more but moved\r\n// sub-pixel detail on sculpted styles (iris fibres) in the before/after frames; quantizing positions or\r\n// normals breaks the customizer (sculpting, computeVertexNormals, eye placement).\r\n// Loaders of these files need setMeshoptDecoder (creature/source/creator/assemble.ts, handborne).\r\nconst SIZE={5120:1,5121:1,5122:2,5123:2,5125:4,5126:4},COUNT={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16};\r\nconst align=n=>n+3&~3;\r\n\r\nexport async function optimizeGLB(input){\r\n if(input.readUInt32LE(0)!==0x46546c67||input.readUInt32LE(4)!==2)throw Error('Expected GLB 2');\r\n const jsonLength=input.readUInt32LE(12),doc=JSON.parse(input.subarray(20,20+jsonLength));\r\n if(doc.extensionsUsed?.includes('EXT_meshopt_compression')||doc.buffers?.length!==1||doc.buffers[0].uri)return input;\r\n const bin=input.subarray(28+jsonLength,28+jsonLength+input.readUInt32LE(20+jsonLength));\r\n await MeshoptEncoder.ready;await MeshoptDecoder.ready;\r\n // How each buffer view is read: by element size (vertices, skins, animation) or as indices.\r\n const use=new Map(),skip=new Set();\r\n for(const a of doc.accessors||[]){\r\n  if(a.sparse){skip.add(a.sparse.indices.bufferView);skip.add(a.sparse.values.bufferView);}\r\n  if(a.bufferView==null)continue;const u=use.get(a.bufferView)??{sizes:new Set()};u.sizes.add(SIZE[a.componentType]*COUNT[a.type]);u.component=SIZE[a.componentType];use.set(a.bufferView,u);\r\n }\r\n for(const mesh of doc.meshes||[])for(const p of mesh.primitives)if(p.indices!=null&&doc.accessors[p.indices].bufferView!=null)use.get(doc.accessors[p.indices].bufferView).index=true;\r\n const images=new Map((doc.images||[]).map((image,i)=>[image.bufferView,i]));\r\n const chunks=[],seen=new Map();let size=0,fallback=0,packed=0,webps=0;\r\n const put=bytes=>{const key=createHash('sha256').update(bytes).digest('base64');if(!seen.has(key)){seen.set(key,size);chunks.push(bytes,Buffer.alloc(align(bytes.length)-bytes.length));size+=align(bytes.length);}return seen.get(key);};\r\n for(const [i,view] of (doc.bufferViews||[]).entries()){\r\n  let bytes=bin.subarray(view.byteOffset||0,(view.byteOffset||0)+view.byteLength);\r\n  const u=use.get(i),image=images.get(i);\r\n  if(u&&!skip.has(i)&&!view.extensions&&!(u.index&&u.sizes.size>1)){\r\n   const mode=u.index?'INDICES':'ATTRIBUTES',stride=u.index?u.component:view.byteStride??(u.sizes.size===1?[...u.sizes][0]:4);\r\n   if((u.index?stride===2||stride===4:stride%4===0&&stride<=256)&&view.byteLength%stride===0){\r\n    const count=view.byteLength/stride,source=new Uint8Array(bytes),out=MeshoptEncoder.encodeGltfBuffer(source,count,stride,mode);\r\n    const back=new Uint8Array(count*stride);MeshoptDecoder.decodeGltfBuffer(back,count,stride,out,mode,'NONE');\r\n    if(!Buffer.from(back).equals(source))throw Error(`Buffer view ${i} did not decode to its original bytes.`);\r\n    if(out.length<view.byteLength){\r\n     view.extensions={EXT_meshopt_compression:{buffer:0,byteOffset:put(Buffer.from(out)),byteLength:out.length,byteStride:stride,mode,count}};\r\n     view.buffer=1;view.byteOffset=fallback;fallback=align(fallback+view.byteLength);packed++;continue;\r\n    }\r\n   }\r\n  }\r\n  if(image!=null&&doc.images[image].mimeType==='image/png'){\r\n   const out=await sharp(bytes).webp({lossless:true,exact:true,effort:6}).toBuffer();\r\n   const [a,b]=await Promise.all([bytes,out].map(x=>sharp(x).ensureAlpha().raw().toBuffer()));\r\n   if(out.length<bytes.length&&a.equals(b)){\r\n    bytes=out;doc.images[image].mimeType='image/webp';webps++;\r\n    for(const t of doc.textures||[])if(t.source===image){delete t.source;t.extensions={...t.extensions,EXT_texture_webp:{source:image}};}\r\n   }\r\n  }\r\n  view.byteOffset=put(bytes);\r\n }\r\n const used=new Set(doc.extensionsUsed),required=new Set(doc.extensionsRequired);\r\n doc.buffers=[{byteLength:size}];\r\n if(packed){doc.buffers.push({byteLength:fallback,extensions:{EXT_meshopt_compression:{fallback:true}}});used.add('EXT_meshopt_compression');required.add('EXT_meshopt_compression');}\r\n if(webps){used.add('EXT_texture_webp');required.add('EXT_texture_webp');}\r\n if(used.size)doc.extensionsUsed=[...used];if(required.size)doc.extensionsRequired=[...required];\r\n let json=Buffer.from(JSON.stringify(doc));json=Buffer.concat([json,Buffer.alloc(align(json.length)-json.length,32)]);\r\n const body=Buffer.concat(chunks),head=Buffer.alloc(28);\r\n head.writeUInt32LE(0x46546c67,0);head.writeUInt32LE(2,4);head.writeUInt32LE(28+json.length+body.length,8);head.writeUInt32LE(json.length,12);head.writeUInt32LE(0x4e4f534a,16);\r\n head.writeUInt32LE(body.length,20);head.writeUInt32LE(0x004e4942,24);\r\n return Buffer.concat([head.subarray(0,20),json,head.subarray(20),body]);\r\n}\r\n\r\n// Rewrites every GLB under root in place (the build runs this on its dist copy); returns bytes saved.\r\nexport async function optimizeModels(root){let saved=0;for(const entry of await readdir(root,{withFileTypes:true})){const path=join(root,entry.name);if(entry.isDirectory())saved+=await optimizeModels(path);else if(entry.name.endsWith('.glb')){const input=await readFile(path),out=await optimizeGLB(input);if(out.length<input.length){await writeFile(path,out);saved+=input.length-out.length;}}}return saved;}\r\n","scripts/optimize-mache-art.mjs":"// Deterministic shipping derivatives of the original game references; source art stays untouched.\r\nimport sharp from 'sharp';\r\nimport {readFile,writeFile,unlink} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nconst dir='pod/worlds/boards/mache/',files=['school-drawing-1','school-drawing-2','school-drawing-3','corgi-game-still'],rows=[];\r\nconst provenance=JSON.parse(await readFile(dir+'provenance.json','utf8'));\r\nfor(const stem of files){const src=await readFile(dir+stem+'.png').catch(()=>readFile(provenance.assets.find(a=>a.file===stem+'.png').source));const out=await sharp(src).resize({width:1024,height:1024,fit:'inside',withoutEnlargement:true}).webp({quality:88}).toBuffer();await writeFile(dir+stem+'.webp',out);rows.push({source:stem+'.png',sourceSha256:createHash('sha256').update(src).digest('hex'),sourceBytes:src.length,file:stem+'.webp',bytes:out.length,sha256:createHash('sha256').update(out).digest('hex')});}\r\nawait writeFile(dir+'shipping-derivatives.json',JSON.stringify(rows,null,2));\r\nfor(const stem of files)await unlink(dir+stem+'.png').catch(e=>{if(e.code!=='ENOENT')throw e;});\r\nconsole.log(JSON.stringify(rows.map(({sourceBytes,file,bytes})=>({file,sourceBytes,bytes})),null,2));\r\n","scripts/pack-nutrition.mjs":"import {readFile,writeFile} from 'node:fs/promises';\r\nimport {resolve} from 'node:path';\r\nimport {pathToFileURL} from 'node:url';\r\n\r\n// The USDA source remains readable in the repository and the AGPL source offer.\r\n// Only the deployed copy stores its repeated object keys once, then restores\r\n// the exact object array when the optional Food list is opened.\r\nexport async function packNutrition(source='nutrition-data.mjs',target='dist/client/nutrition-data.mjs'){\r\n const records=(await import(pathToFileURL(resolve(source)).href)).default;\r\n if(!Array.isArray(records)||!records.length)throw Error('Nutrition source has no rows.');\r\n const keys=Object.keys(records[0]),signature=keys.join('\\0');\r\n for(const row of records)if(Object.keys(row).join('\\0')!==signature)throw Error('Nutrition source columns differ between rows.');\r\n const rows=records.map(row=>keys.map(key=>row[key]));\r\n const header=(await readFile(source,'utf8')).split(/\\r?\\n/).slice(0,3).join('\\n');\r\n const module=`${header}\\n// Packed deploy copy; scripts/pack-nutrition.mjs verifies a uniform source schema.\\nexport default (()=>{const keys=${JSON.stringify(keys)},rows=${JSON.stringify(rows)},out=new Array(rows.length);for(let j=0;j<rows.length;j++){const item={};for(let i=0;i<keys.length;i++)item[keys[i]]=rows[j][i];out[j]=item;}return out;})();\\n`;\r\n await writeFile(target,module);\r\n return (await readFile(source)).length-Buffer.byteLength(module);\r\n}\r\n","scripts/package-weapon-review.mjs":"import {readFile,writeFile,mkdir} from 'node:fs/promises';\r\nimport {resolve,dirname} from 'node:path';\r\nimport {build} from 'esbuild';\r\nconst output=resolve(process.argv[2]||'../../outputs/Weapon-Lab.html');\r\nconst js=await build({entryPoints:['pod/weapon-review.mjs'],bundle:true,format:'iife',target:'es2022',write:false,minify:true});\r\nconst weapons=await readFile('pod/gala-weapons.js','utf8');\r\nconst training=await readFile('workout-tracks.js','utf8');\r\nconst world=(await readFile('pod/worlds/great-wall.webp')).toString('base64');\r\nconst css=(await readFile('pod/weapon-review.css','utf8')).replace(\"url('worlds/great-wall.webp')\",`url('data:image/webp;base64,${world}')`);\r\nconst html=(await readFile('pod/weapon-review.html','utf8'))\r\n .replace('<link rel=\"stylesheet\" href=\"weapon-review.css\">',`<style>${css}</style>`)\r\n .replace('href=\"../pose.html\"','href=\"https://myr5.mominc.online/pose.html\"')\r\n .replace('<script src=\"/workout-tracks.js\"></script>',`<script>${training}</script>`)\r\n .replace('<script src=\"gala-weapons.js\"></script>',`<script>${weapons.replaceAll('</script','<\\\\/script')}</script>`)\r\n .replace('<script type=\"module\" src=\"weapon-review.mjs\"></script>',`<script>${js.outputFiles[0].text.replaceAll('</script','<\\\\/script')}</script>`);\r\nawait mkdir(dirname(output),{recursive:true});await writeFile(output,html);console.log('Self-contained weapon review saved.');\r\n","scripts/pixel-dance-assets.mjs":"// Builds the Pixel Dance GLBs: simplifies the supplied 1.2M-tri dance floor (real geometry, UVs, normals,\r\n// embedded textures kept) to <=60k tris and copies the projector. Usage: node scripts/pixel-dance-assets.mjs [srcDir]\r\nimport {readFile,writeFile,mkdir} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nimport {MeshoptSimplifier} from 'meshoptimizer';\r\nconst SRC=process.argv[2]||'D:/outputs/grimoire-plan-2026-10-20/assets',OUT='pod/worlds/boards/pixel',MAXTRIS=60000;\r\nconst sha=b=>createHash('sha256').update(b).digest('hex');\r\nconst align=n=>n+3&~3;\r\n\r\nfunction parse(glb){\r\n const jl=glb.readUInt32LE(12),doc=JSON.parse(glb.subarray(20,20+jl)),bin=glb.subarray(28+jl,28+jl+glb.readUInt32LE(20+jl));\r\n return {doc,bin};\r\n}\r\nfunction acc(doc,bin,i,T){\r\n const a=doc.accessors[i],v=doc.bufferViews[a.bufferView],o=bin.byteOffset+(v.byteOffset||0)+(a.byteOffset||0);\r\n return new T(bin.buffer.slice(o,o+a.count*{SCALAR:1,VEC2:2,VEC3:3}[a.type]*T.BYTES_PER_ELEMENT));\r\n}\r\nfunction bounds(p){\r\n const mn=[1/0,1/0,1/0],mx=[-1/0,-1/0,-1/0];\r\n for(let i=0;i<p.length;i+=3)for(let k=0;k<3;k++){mn[k]=Math.min(mn[k],p[i+k]);mx[k]=Math.max(mx[k],p[i+k]);}\r\n return {min:mn,max:mx,size:mx.map((v,k)=>v-mn[k])};\r\n}\r\nfunction glb(doc,chunks){ // chunks: [{bytes,view?}] appended into one BIN\r\n let off=0;const parts=[];\r\n doc.bufferViews=chunks.map(c=>{const v={buffer:0,byteOffset:off,byteLength:c.bytes.length,...c.view};parts.push(c.bytes,Buffer.alloc(align(c.bytes.length)-c.bytes.length));off+=align(c.bytes.length);return v;});\r\n doc.buffers=[{byteLength:off}];\r\n const bin=Buffer.concat(parts);let j=Buffer.from(JSON.stringify(doc));j=Buffer.concat([j,Buffer.alloc(align(j.length)-j.length,0x20)]);\r\n const h=Buffer.alloc(12),jh=Buffer.alloc(8),bh=Buffer.alloc(8);\r\n h.writeUInt32LE(0x46546c67,0);h.writeUInt32LE(2,4);h.writeUInt32LE(28+j.length+bin.length,8);\r\n jh.writeUInt32LE(j.length,0);jh.write('JSON',4);bh.writeUInt32LE(bin.length,0);bh.write('BIN\\0',4);\r\n return Buffer.concat([h,jh,j,bh,bin]);\r\n}\r\n\r\nawait MeshoptSimplifier.ready;await mkdir(OUT,{recursive:true});\r\nconst srcPath=`${SRC}/geometric-art-dance-floor.glb`,src=await readFile(srcPath),{doc,bin}=parse(src);\r\nconst prim=doc.meshes[0].primitives[0],A=prim.attributes;\r\nconst pos=acc(doc,bin,A.POSITION,Float32Array),nrm=acc(doc,bin,A.NORMAL,Float32Array),uv=acc(doc,bin,A.TEXCOORD_0,Float32Array),idx=acc(doc,bin,prim.indices,Uint32Array);\r\nconst inB=bounds(pos),inTris=idx.length/3;\r\n// Weld UV/normal seams by position, then simplify positions only (seams otherwise pin ~350k tris);\r\n// surviving triangles keep their original vertex normals/UVs. Raise the error budget until under MAXTRIS.\r\nconst remap=MeshoptSimplifier.generatePositionRemap(pos,3),widx=idx.map(v=>remap[v]);\r\nlet simp,err;\r\nfor(let e=0.01;;e*=2){[simp,err]=MeshoptSimplifier.simplify(widx,pos,3,MAXTRIS*3,e,['Permissive']);if(simp.length/3<=MAXTRIS||e>1)break;}\r\n// Compact to used vertices.\r\nconst map=new Int32Array(pos.length/3).fill(-1),used=[];\r\nconst outIdx=new Uint32Array(simp.length);\r\nfor(let i=0;i<simp.length;i++){const v=simp[i];if(map[v]<0){map[v]=used.length;used.push(v);}outIdx[i]=map[v];}\r\nconst n=used.length,P=new Float32Array(n*3),N=new Float32Array(n*3),U=new Float32Array(n*2);\r\nused.forEach((v,i)=>{P.set(pos.subarray(v*3,v*3+3),i*3);N.set(nrm.subarray(v*3,v*3+3),i*3);U.set(uv.subarray(v*2,v*2+2),i*2);});\r\nconst outB=bounds(P),tris=outIdx.length/3;\r\nif(tris>MAXTRIS)throw Error(`simplified to ${tris} tris > ${MAXTRIS}`);\r\nconst view=i=>doc.bufferViews[i],img=i=>Buffer.from(bin.buffer,bin.byteOffset+view(i).byteOffset,view(i).byteLength);\r\nconst f=a=>Buffer.from(a.buffer);\r\nconst nd={asset:doc.asset,scene:0,scenes:doc.scenes,nodes:doc.nodes,materials:doc.materials,textures:doc.textures,samplers:doc.samplers,\r\n images:doc.images.map((im,k)=>({...im,bufferView:4+k})),\r\n meshes:[{name:doc.meshes[0].name,primitives:[{attributes:{POSITION:0,NORMAL:1,TEXCOORD_0:2},indices:3,material:0}]}],\r\n accessors:[\r\n  {bufferView:0,componentType:5126,count:n,type:'VEC3',min:outB.min,max:outB.max},\r\n  {bufferView:1,componentType:5126,count:n,type:'VEC3'},\r\n  {bufferView:2,componentType:5126,count:n,type:'VEC2'},\r\n  {bufferView:3,componentType:5125,count:outIdx.length,type:'SCALAR'}]};\r\nconst out=glb(nd,[{bytes:f(P),view:{target:34962}},{bytes:f(N),view:{target:34962}},{bytes:f(U),view:{target:34962}},{bytes:f(outIdx),view:{target:34963}},\r\n ...doc.images.map((_,k)=>({bytes:img(doc.images[k].bufferView)}))]);\r\nawait writeFile(`${OUT}/dance-floor.glb`,out);\r\nconst projSrc=await readFile(`${SRC}/neon-cube-projector.glb`);await writeFile(`${OUT}/projector.glb`,projSrc);\r\nconst pj=parse(projSrc),pp=pj.doc.meshes[0].primitives[0],pb=bounds(acc(pj.doc,pj.bin,pp.attributes.POSITION,Float32Array));\r\nconst report={\r\n danceFloor:{source:{path:srcPath,bytes:src.length,sha256:sha(src),verts:pos.length/3,tris:inTris,bounds:inB},\r\n  out:{path:`${OUT}/dance-floor.glb`,bytes:out.length,sha256:sha(out),verts:n,tris,bounds:outB,simplifyError:err,textureBytes:doc.images.map(i=>view(i.bufferView).byteLength)},\r\n  frontFaceAxis:'+Z/-Z (thinnest extent); upright slab: width X, height +Y from y=0, depth Z',\r\n  note:'real simplified geometry; recolor gold/grout/purple in renderer via material/uv, source colors not final'},\r\n projector:{source:{bytes:projSrc.length,sha256:sha(projSrc)},out:{path:`${OUT}/projector.glb`,tris:acc(pj.doc,pj.bin,pp.indices,Uint32Array).length/3,bounds:pb}},\r\n generator:'scripts/pixel-dance-assets.mjs',meshoptimizer:'1.3.0'};\r\nawait mkdir('artifacts/pixel-dance',{recursive:true});\r\nawait writeFile('artifacts/pixel-dance/assets.json',JSON.stringify(report,null,1));\r\nconsole.log(JSON.stringify(report,null,1));\r\n","scripts/pose-samples.mjs":"// Turns a ?recordPose=1 recording into a k-NN samples file and a replay fixture.\r\n//   node scripts/pose-samples.mjs <recording.json> [--reps N] [--use]\r\n//     Writes tests/fixtures/pose-replay/<name>.replay.json (+ <name>.samples.json beside it) and prints how the\r\n//     rules and the k-NN counted. --use moves the samples to pose-samples/<exercise>.samples.json and registers\r\n//     them in pose-samples.mjs, so the app counts that exercise with the k-NN.\r\n//   node scripts/pose-samples.mjs --example\r\n//     Rebuilds the synthetic squat example (tests/pose-fixtures.mjs) in tests/fixtures/pose-replay.\r\nimport {readFile,writeFile,readdir,mkdir,rm} from 'node:fs/promises';\r\nimport {basename} from 'node:path';\r\nimport {fileURLToPath} from 'node:url';\r\nimport {MovementSession,MOVEMENTS,features} from '../movement-engine.mjs';\r\nimport {embed,EMBED_LANDMARKS} from '../pose-classifier.mjs';\r\nconst root=new URL('../',import.meta.url),fixtures=new URL('tests/fixtures/pose-replay/',root),registry=new URL('pose-samples/',root);\r\nconst MAX_BYTES=50*1024,PER_CLASS=100;\r\nconst round=(v,d=3)=>Math.round(v*10**d)/10**d;\r\nconst evenly=(list,n)=>list.length<=n?list:Array.from({length:n},(_,i)=>list[Math.floor(i*list.length/n)]);\r\n// Replay frames keep only the landmarks counting uses; the rest come back invisible.\r\nconst points=frame=>{const p=Array.from({length:27},()=>({x:0,y:0,visibility:0}));frame.forEach(([x,y,visibility],i)=>{p[EMBED_LANDMARKS[i]]={x,y,visibility};});return p;};\r\n// World landmarks (metres, recorded since R9) feed the rules' toward-camera angles; older recordings replay 2D.\r\nconst worldPoints=w=>{if(!w)return null;const p=Array(27).fill(null);w.forEach(([x,y,z],i)=>{p[EMBED_LANDMARKS[i]]={x,y,z};});return p;};\r\nexport function replay(fixture,options={}){\r\n const session=new MovementSession(fixture.exercise,options);\r\n for(const [t,frame,world] of fixture.frames)session.update(points(frame),t,fixture.aspect,worldPoints(world));\r\n return session.count;\r\n}\r\nexport function convert(recording,{reps=null}={}){\r\n const {exercise,aspect=1,frames,labels=[]}=recording;\r\n if(MOVEMENTS[exercise]?.kind!=='reps')throw new Error(`${exercise} is not a rep exercise; the k-NN only counts reps.`);\r\n const t0=frames[0][0],compact=frames.map(([t,p,w])=>[Math.round(t-t0),EMBED_LANDMARKS.map(i=>[round(p[i][0]),round(p[i][1]),round(p[i][2],2)]),...w?[EMBED_LANDMARKS.map(i=>w[i].map(v=>round(v,4)))]:[]]);\r\n const labelled=compact.flatMap(([t,frame])=>{const label=labels.find(l=>t+t0>=l.from&&t+t0<=l.to)?.label;return label?[{label,f:features(points(frame),aspect)}]:[];});\r\n // Landmarks clear in 90% of the labelled frames are required; a side view only needs its near side.\r\n const need=EMBED_LANDMARKS.filter(i=>labelled.filter(({f})=>f.visible([i])&&f.p[i].visibility>=.6).length>=.9*labelled.length);\r\n if(![11,23].every(i=>need.includes(i))&&![12,24].every(i=>need.includes(i)))throw new Error('No clear shoulder and hip in the labelled frames. Record again with the body in view.');\r\n const byLabel={};for(const {label,f} of labelled)if(f.visible(need))(byLabel[label]??=[]).push(embed(f.p).map(v=>round(v)));\r\n if(!byLabel.up?.length||!byLabel.down?.length)throw new Error('The recording needs both \"up\" and \"down\" labels.');\r\n const samples={exercise,need,samples:Object.entries(byLabel).flatMap(([label,list])=>evenly(list,PER_CLASS).map(e=>[label,e]))};\r\n if(JSON.stringify(samples).length>MAX_BYTES)throw new Error('Samples file over 50 KB.');\r\n const fixture={exercise,aspect,reps,rules:0,knn:0,frames:compact};\r\n fixture.rules=replay(fixture,{samples:null});fixture.knn=replay(fixture,{samples});\r\n return {samples,fixture};\r\n}\r\nasync function register(){\r\n const files=(await readdir(registry).catch(()=>[])).filter(n=>n.endsWith('.samples.json')).sort();\r\n await writeFile(new URL('pose-samples.mjs',root),['// Generated by scripts/pose-samples.mjs from pose-samples/*.samples.json. Do not edit by hand.','// Exercises listed here count reps with the k-NN pose classifier; every other exercise uses the rules.',\r\n  ...files.map((n,i)=>`import s${i} from './pose-samples/${n}' with {type:'json'};`),`export default {${files.map((n,i)=>`${JSON.stringify(n.replace('.samples.json',''))}:s${i}`).join(',')}};`,''].join('\\n'));\r\n}\r\nasync function main(args){\r\n const example=args.includes('--example'),use=args.includes('--use'),reps=args.includes('--reps')?Number(args[args.indexOf('--reps')+1]):null;\r\n const file=args.find(a=>!a.startsWith('--')&&a!==String(reps));\r\n const recording=example?(await import('../tests/pose-fixtures.mjs')).syntheticRecording():JSON.parse(await readFile(file,'utf8'));\r\n const {samples,fixture}=convert(recording,{reps:example?6:reps});\r\n const name=example?'squat-synthetic':basename(file,'.json').replace(/^recordPose-/,'');\r\n await mkdir(fixtures,{recursive:true});\r\n await writeFile(new URL(name+'.replay.json',fixtures),JSON.stringify(fixture)+'\\n');\r\n if(use){\r\n  await mkdir(registry,{recursive:true});await writeFile(new URL(samples.exercise+'.samples.json',registry),JSON.stringify(samples)+'\\n');\r\n  await rm(new URL(name+'.samples.json',fixtures),{force:true});await register();\r\n  // Other replays of this exercise count with the registered samples: re-lock their k-NN counts.\r\n  for(const other of (await readdir(fixtures)).filter(n=>n.endsWith('.replay.json')&&n!==name+'.replay.json')){\r\n   const url=new URL(other,fixtures),old=JSON.parse(await readFile(url,'utf8'));\r\n   if(old.exercise!==samples.exercise||(await readdir(fixtures)).includes(other.replace('.replay.json','.samples.json')))continue;\r\n   old.knn=replay(old,{samples});await writeFile(url,JSON.stringify(old)+'\\n');\r\n  }\r\n }else await writeFile(new URL(name+'.samples.json',fixtures),JSON.stringify(samples)+'\\n');\r\n console.log(`${samples.exercise}: ${samples.samples.length} samples, needs landmarks ${samples.need.join(' ')}. Rules counted ${fixture.rules}, k-NN counted ${fixture.knn}${reps===null&&!example?'':`, you did ${fixture.reps}`}.`);\r\n console.log(use?`k-NN now counts ${samples.exercise} in the app. Run npm test, then commit pose-samples/ and the replay.`:`Replay saved as tests/fixtures/pose-replay/${name}.replay.json. Add --use to count ${samples.exercise} with the k-NN in the app.`);\r\n}\r\nif(process.argv[1]===fileURLToPath(import.meta.url))main(process.argv.slice(2)).catch(error=>{console.error(error.message);process.exit(1);});\r\n","scripts/prepare-sound-assets.py":"\"\"\"Rebuild the CC0 sound pack from the attributed masters in plan/assets-inbox/audio.\r\n\r\nRequires Python 3 and ffmpeg/ffprobe. The two original generated waveforms are CC0.\r\nAll paths and edit recipes are retained in audio/sfx/manifest.json for reproducibility.\r\n\"\"\"\r\nimport array\r\nimport hashlib\r\nimport html\r\nimport json\r\nimport math\r\nimport pathlib\r\nimport random\r\nimport struct\r\nimport subprocess\r\nimport wave\r\n\r\nROOT = pathlib.Path(__file__).resolve().parents[1]\r\nMASTERS = ROOT / 'plan/assets-inbox/audio'\r\nOUT = ROOT / 'audio/sfx'\r\nOUT.mkdir(parents=True, exist_ok=True)\r\nSOURCES = {\r\n    'interface': ('Kenney', 'https://kenney.nl/assets/interface-sounds'),\r\n    'impact': ('Kenney', 'https://kenney.nl/assets/impact-sounds'),\r\n    'mechanical': ('qubodup', 'https://opengameart.org/content/7-mechanical-clicks-and-buzzes'),\r\n    'ice': ('bart', 'https://opengameart.org/content/ice-spells'),\r\n    'jelly': ('Independent.nu', 'https://opengameart.org/content/8-wet-squish-slurp-impacts'),\r\n    'water': ('ezwa', 'https://opengameart.org/content/6-short-water-splashes'),\r\n    'waves': ('transitking', 'https://opengameart.org/content/water-waves'),\r\n    'grass': ('qubodup', 'https://opengameart.org/content/20-rustles-dry-leaves'),\r\n    'cloth': ('Iochi Glaucus', 'https://opengameart.org/content/fabric-rustling'),\r\n    'wind': ('Luke.RUSTLTD', 'https://opengameart.org/content/wind1'),\r\n    'hum': ('LEGIT Audio', 'https://opengameart.org/content/the-shop'),\r\n    'portal': ('Ogrebane', 'https://opengameart.org/content/teleport-spell'),\r\n    'original': ('MOM Inc.', '/source.json'),\r\n}\r\n\r\n\r\ndef generated(name, duration, sample):\r\n    path = MASTERS / (name + '.wav')\r\n    with wave.open(str(path), 'wb') as f:\r\n        f.setnchannels(1)\r\n        f.setsampwidth(2)\r\n        f.setframerate(24000)\r\n        frames = bytearray()\r\n        for n in range(int(duration * 24000)):\r\n            t = n / 24000\r\n            envelope = min(1, t / .012, (duration - t) / .04)\r\n            frames.extend(struct.pack('<h', int(max(-1, min(1, sample(t) * envelope)) * 24000)))\r\n        f.writeframes(frames)\r\n\r\n\r\nrng = random.Random(21)\r\ngenerated('dialup-original', 1.8, lambda t:\r\n          .19 * math.sin(2 * math.pi * (350 if t < .35 else 1050 if t < .7 else 2100) * t)\r\n          + .1 * math.sin(2 * math.pi * (440 if t < .35 else 1650) * t)\r\n          + (rng.uniform(-.05, .05) if t > .7 else 0))\r\ngenerated('crt-original', .12, lambda t:\r\n          rng.uniform(-.2, .2) * math.exp(-t * 55)\r\n          + .08 * math.sin(2 * math.pi * 180 * t) * math.exp(-t * 40))\r\n\r\n# Original UI foley and synthesis, dedicated to CC0 by MOM Inc.\r\ngenerated('bloop-original', .22, lambda t: .3*math.sin(2*math.pi*(260*t-340*t*t))*math.exp(-t*16))\r\ngenerated('signal-original', .35, lambda t: .18*math.sin(2*math.pi*(660 if t<.14 else 880)*t) if t<.08 or .18<t<.26 else 0)\r\ngenerated('electric-original', .25, lambda t: rng.uniform(-.22,.22)+.018*math.sin(2*math.pi*110*t))\r\ngenerated('whiteboard-original', .9, lambda t: rng.uniform(-.3,.3)*(1 if t<.34 else .05 if t<.4 else .5 if int((t-.4)/.06)%2==0 else .12))\r\nSOURCES['waterfall'] = ('kurt', 'https://opengameart.org/content/stream-sounds')\r\n\r\n# cue, filename, source/master, provenance key, maximum duration, extra filter, seek\r\nEDITS = [\r\n    ('mechanical', 'mechanical-click-1', 'mechanical/mechanical/mechanical_button-01.flac', 'mechanical', .4, '', 0),\r\n    ('mechanical', 'mechanical-click-2', 'mechanical/mechanical/mechanical_clicks-05.flac', 'mechanical', .42, '', 0),\r\n    ('switch', 'metal-switch-1', 'kenney-interface/Audio/switch_001.ogg', 'interface', .4, '', 0),\r\n    ('switch', 'metal-switch-2', 'kenney-interface/Audio/toggle_001.ogg', 'interface', .4, '', 0),\r\n    ('ice', 'ice-crack-1', 'ice/ice.wav', 'ice', .85, '', 0),\r\n    ('ice', 'ice-crack-2', 'ice/coldsnap.wav', 'ice', .85, '', .1),\r\n    ('jelly', 'jelly-squish-1', 'squish/impsplat/impactsplat07.mp3.flac', 'jelly', .55, '', 0),\r\n    ('jelly', 'jelly-squish-2', 'squish/impsplat/impactsplat08.mp3.flac', 'jelly', .66, '', 0),\r\n    ('water', 'water-splash-1', 'splash/ezwa-water_splash/water_splash-02.flac', 'water', .42, '', 0),\r\n    ('water', 'water-splash-2', 'splash/ezwa-water_splash/water_splash-03.flac', 'water', .72, '', 0),\r\n    ('water-slosh', 'water-slosh', 'water-slosh.flac', 'waves', 2.5, 'lowpass=f=2500,', 0),\r\n    ('grass', 'grass-rustle-1', 'leaves/rustle/rustle01.flac', 'grass', .7, 'lowpass=f=4000,', 0),\r\n    ('grass', 'grass-rustle-2', 'leaves/rustle/rustle02.flac', 'grass', .7, 'lowpass=f=4000,', 0),\r\n    ('grass-tinkle', 'grass-tinkle', 'kenney-interface/Audio/glass_001.ogg', 'interface', .6, '', 0),\r\n    ('quilt', 'cloth-rustle-1', 'cloth.ogg', 'cloth', .65, 'lowpass=f=4000,', .5),\r\n    ('quilt', 'cloth-rustle-2', 'cloth.ogg', 'cloth', .65, 'lowpass=f=4000,', 2),\r\n    ('wood', 'wood-tap-1', 'kenney-impact/Audio/impactWood_light_000.ogg', 'impact', .45, '', 0),\r\n    ('wood', 'wood-tap-2', 'kenney-impact/Audio/impactWood_light_001.ogg', 'impact', .45, '', 0),\r\n    ('wood-scrape', 'wood-scrape', 'wood/wood_impact/crack01.mp3.flac', 'wood', .65, 'lowpass=f=3000,', 0),\r\n    ('cogs', 'cogs-ratchet-1', 'kenney-impact/Audio/impactMetal_light_000.ogg', 'impact', .4, '', 0),\r\n    ('cogs', 'cogs-ratchet-2', 'kenney-impact/Audio/impactMetal_light_001.ogg', 'impact', .4, '', 0),\r\n    ('crt', 'crt-tap', 'crt-original.wav', 'original', .12, '', 0),\r\n    ('transit', 'portal-transit', 'teleport.wav', 'portal', 2, '', 0),\r\n    ('dialup', 'dialup', 'dialup-original.wav', 'original', 1.8, '', 0),\r\n    ('pod-hum', 'pod-hum', 'shop/TheShopCollection_convenience_store_drinks_fridge_drone.wav', 'hum', 12, 'highpass=f=50,lowpass=f=1100,', 1),\r\n    ('whiteboard', 'whiteboard-erase-marker', 'whiteboard-original.wav', 'original', .9, 'highpass=f=300,lowpass=f=4200,', 0),\r\n    ('electric', 'electric-static', 'electric-original.wav', 'original', .25, 'highpass=f=1400,lowpass=f=4200,', 0),\r\n    ('bloop', 'menu-bloop', 'bloop-original.wav', 'original', .22, '', 0),\r\n    ('signal', 'signal-beeps', 'signal-original.wav', 'original', .35, '', 0),\r\n    ('waterfall', 'meditation-waterfall', 'stream-waterfall/stream-waterfall/waterfall1.ogg', 'waterfall', 12, 'highpass=f=80,lowpass=f=5500,', 1),\r\n    ('breeze', 'gentle-breeze', 'wind.wav', 'wind', 12, 'lowpass=f=2300,', 2),\r\n]\r\nSOURCES['wood'] = ('Independent.nu', 'https://opengameart.org/content/35-wooden-crackshitsdestructions')\r\nmanifest = {'version': 1, 'license': 'CC0-1.0 AND CC-BY-3.0', 'cues': {}, 'assets': [], 'sources': []}\r\nfor cue, name, master, provenance, duration, filter_prefix, seek in EDITS:\r\n    source = MASTERS / master\r\n    target = OUT / (name + '.mp3')\r\n    # Normalize masters once; category/interaction gains are controlled by the mixer.\r\n    filters = filter_prefix + 'loudnorm=I=-22:TP=-3:LRA=7'\r\n    if duration < 5:\r\n        filters += ',afade=t=in:st=0:d=0.004'\r\n        filters += f',afade=t=out:st={max(0,duration-.035)}:d=0.035'\r\n    decode = ['ffmpeg', '-hide_banner', '-loglevel', 'error', '-ss', str(seek),\r\n              '-i', str(source), '-t', str(duration), '-af', filters, '-ar', '24000', '-ac', '1']\r\n    if duration >= 5:\r\n        # Make a cyclic overlap: the output starts after the head, then its tail\r\n        # blends into that head. The next loop continues at the adjacent sample,\r\n        # without a recurring fade-to-silence or a discontinuous hard cut.\r\n        samples = array.array('f')\r\n        samples.frombytes(subprocess.check_output(decode + ['-f', 'f32le', '-']))\r\n        overlap = 12000\r\n        blend = array.array('f', (samples[-overlap+i] * (1-i/overlap)\r\n                                 + samples[i] * (i/overlap) for i in range(overlap)))\r\n        cyclic = samples[overlap:-overlap] + blend\r\n        subprocess.run(['ffmpeg', '-hide_banner', '-loglevel', 'error', '-y',\r\n                        '-f', 'f32le', '-ar', '24000', '-ac', '1', '-i', 'pipe:0',\r\n                        '-codec:a', 'libmp3lame', '-b:a', '64k', str(target)],\r\n                       input=cyclic.tobytes(), check=True)\r\n    else:\r\n        subprocess.run(decode + ['-y', '-codec:a', 'libmp3lame', '-b:a', '64k', str(target)], check=True)\r\n    url = '/audio/sfx/' + target.name\r\n    manifest['cues'].setdefault(cue, []).append(url)\r\n    manifest['assets'].append({'url': url, 'source': provenance, 'master': master,\r\n                              'seek': seek, 'maxSeconds': duration, 'filters': filters,\r\n                              'loopCrossfadeSeconds': .5 if duration >= 5 else None,\r\n                              'sha256': hashlib.sha256(target.read_bytes()).hexdigest(),\r\n                              'masterSha256': hashlib.sha256(source.read_bytes()).hexdigest(),\r\n                              'bytes': target.stat().st_size})\r\nmanifest['cues']['dial'] = manifest['cues']['mechanical']\r\nmanifest['cues']['lever'] = manifest['cues']['switch']\r\nfor key, (author, url) in SOURCES.items():\r\n    if any(a['source'] == key for a in manifest['assets']):\r\n        manifest['sources'].append({'id': key, 'author': author, 'url': url, 'license': 'CC-BY-3.0' if key == 'waterfall' else 'CC0-1.0'})\r\n(OUT / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\\n', encoding='utf-8')\r\nitems = ''.join(f'<li>{html.escape(s[\"author\"])} — <a href=\"{html.escape(s[\"url\"])}\">{html.escape(s[\"id\"])}</a> ({html.escape(s[\"license\"])})</li>' for s in manifest['sources'])\r\n(OUT / 'credits.html').write_text('<!doctype html><html lang=\"en\"><meta charset=\"utf-8\">'\r\n    '<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Sound credits</title>'\r\n    '<style>body{font:18px system-ui;max-width:48rem;margin:3rem auto;padding:1rem;background:#101821;color:#e9f0f7}a{color:#9ee4ff}li{margin:1rem 0}</style>'\r\n    '<h1>Sound credits</h1><p>Effects are released under '\r\n    '<a href=\"https://creativecommons.org/publicdomain/zero/1.0/\">CC0 1.0</a>. '\r\n    'The meditation waterfall is adapted from Stream Sounds by kurt, licensed under <a href=\"https://creativecommons.org/licenses/by/3.0/\">CC BY 3.0</a>, trimmed, filtered, normalized and crossfaded into a loop. Other effects are CC0. Material recordings were edited into short cues; wind, teleport, dial-up and CRT effects include sound design. '\r\n    'Coach voices are local variants of the existing eSpeak NG generated clips. '\r\n    'The app code remains under its existing AGPL license.</p><ul>' + items + '</ul>'\r\n    '<p><a href=\"manifest.json\">Asset provenance and checksums</a> · <a href=\"/source.json\">App source</a> · <a href=\"/pose\">Back to Coach</a></p></html>', encoding='utf-8')\r\nprint(f'Prepared {len(manifest[\"assets\"])} openly licensed clips, {sum(a[\"bytes\"] for a in manifest[\"assets\"]):,} bytes.')\r\n","scripts/provision-pack-catalog-keys.mjs":"import {generateKeyPairSync,createHash} from 'node:crypto';\r\nimport {existsSync,mkdirSync,readFileSync,renameSync,rmSync,writeFileSync} from 'node:fs';\r\nimport {dirname,join} from 'node:path';\r\nimport {spawnSync} from 'node:child_process';\r\n\r\nconst repository=process.env.MYR5_GITHUB_REPOSITORY||'MomInc-MBS/coach-app';\r\nconst activeSecret='PACK_CATALOG_SIGNING_PRIVATE_JWK';\r\nconst output=new URL('../packs/catalog-keys.generated.mjs',import.meta.url);\r\nconst current=readFileSync(output,'utf8');\r\nif(!current.includes('PUBLIC_PACK_CATALOG_KEYS=Object.freeze([])'))throw Error('Catalog keys are already provisioned. Use the documented dual-key rotation procedure instead of replacing them.');\r\n\r\nfunction keyPair(){\r\n return generateKeyPairSync('ed25519',{publicKeyEncoding:{format:'jwk'},privateKeyEncoding:{format:'jwk'}});\r\n}\r\nfunction fingerprint(key){return createHash('sha256').update(Buffer.from(key.x,'base64url')).digest('hex');}\r\nfunction run(command,args,options={}){\r\n const result=spawnSync(command,args,{encoding:'utf8',windowsHide:true,...options});\r\n if(result.status!==0)throw Error((result.stderr||result.stdout||command+' failed').trim());\r\n return result;\r\n}\r\n\r\nconst active=keyPair(),recovery=keyPair();\r\nconst activeFingerprint=fingerprint(active.publicKey),recoveryFingerprint=fingerprint(recovery.publicKey);\r\nconst stamp=new Date().toISOString().slice(0,10);\r\nconst activeId=`myr5-pack-release-${stamp}-${activeFingerprint.slice(0,12)}`;\r\nconst recoveryId=`myr5-pack-recovery-${stamp}-${recoveryFingerprint.slice(0,12)}`;\r\n\r\nrun('gh',['secret','set',activeSecret,'--repo',repository],{\r\n input:JSON.stringify(active.privateKey),\r\n env:{...process.env,GH_PROMPT_DISABLED:'1'}\r\n});\r\n\r\nconst recoveryRoot=join(process.env.LOCALAPPDATA||dirname(process.env.USERPROFILE||process.cwd()),'MYR5','catalog-keys');\r\nconst recoveryPath=join(recoveryRoot,`${recoveryId}.jwk.dpapi`);\r\nif(existsSync(recoveryPath))throw Error('The recovery-key destination already exists.');\r\nmkdirSync(recoveryRoot,{recursive:true});\r\nconst powershell=join(process.env.SystemRoot||'C:\\\\Windows','System32','WindowsPowerShell','v1.0','powershell.exe');\r\nconst protect=[\r\n '$destination=$env:MYR5_RECOVERY_PATH',\r\n '$plaintext=[Console]::In.ReadToEnd()',\r\n \"Add-Type -AssemblyName 'System.Security'\",\r\n '$bytes=[Text.Encoding]::UTF8.GetBytes($plaintext)',\r\n \"$entropy=[Text.Encoding]::UTF8.GetBytes('MYR5 pack catalog recovery v1')\",\r\n '$cipher=[Security.Cryptography.ProtectedData]::Protect($bytes,$entropy,[Security.Cryptography.DataProtectionScope]::CurrentUser)',\r\n '[IO.File]::WriteAllBytes($destination,$cipher)'\r\n].join(';');\r\ntry{\r\n run(powershell,['-NoLogo','-NoProfile','-NonInteractive','-Command',protect],{\r\n  input:JSON.stringify(recovery.privateKey),\r\n  env:{...process.env,MYR5_RECOVERY_PATH:recoveryPath}\r\n });\r\n}catch(error){\r\n try{rmSync(recoveryPath,{force:true});}catch{}\r\n throw error;\r\n}\r\n\r\nconst entry=(keyId,role,publicKey)=>({keyId,role,protocols:[1],publicKey:{kty:'OKP',crv:'Ed25519',x:publicKey.x,ext:true}});\r\nconst keys=[entry(activeId,'release',active.publicKey),entry(recoveryId,'recovery',recovery.publicKey)];\r\nconst source=`// Generated by scripts/provision-pack-catalog-keys.mjs. Private keys never belong in this repository.\\nexport const PUBLIC_PACK_CATALOG_KEYS=Object.freeze(${JSON.stringify(keys,null,2)}.map(entry=>Object.freeze({...entry,protocols:Object.freeze(entry.protocols),publicKey:Object.freeze(entry.publicKey)})));\\n`;\r\nconst temporary=new URL('../packs/.catalog-keys.generated.tmp',import.meta.url);\r\nwriteFileSync(temporary,source,{encoding:'utf8',flag:'wx'});\r\nrenameSync(temporary,output);\r\n\r\nconsole.log(JSON.stringify({\r\n repository,\r\n active:{keyId:activeId,fingerprint:`sha256:${activeFingerprint}`,secret:activeSecret},\r\n recovery:{keyId:recoveryId,fingerprint:`sha256:${recoveryFingerprint}`,encryptedPath:recoveryPath},\r\n publicKeyring:new URL('../packs/catalog-keys.generated.mjs',import.meta.url).pathname\r\n},null,2));\r\n","scripts/r21-preview.cjs":"// Local Release 21 review; read-only API fixtures, no account or production writes.\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..');\r\nconst types={'.html':'text/html; charset=utf-8','.css':'text/css','.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.wasm':'application/wasm','.webp':'image/webp','.png':'image/png'};\r\nhttp.createServer(async(req,res)=>{\r\n const pathname=new URL(req.url,'http://local').pathname;\r\n if(pathname==='/__blank__'){res.setHeader('Content-Type','text/html');res.end('<!doctype html><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Release 21 review</title>');return;}\r\n if(pathname.startsWith('/api/')){res.writeHead(pathname==='/api/auth/config'||pathname==='/api/gala/leaderboard'?200:401,{'Content-Type':'application/json'});res.end(JSON.stringify(pathname==='/api/auth/config'?{enabled:false}:pathname==='/api/gala/leaderboard'?{items:[]}:{error:'Local guest preview'}));return;}\r\n try{const file=path.resolve(root,'.'+decodeURIComponent(pathname.endsWith('/')?pathname+'index.html':pathname));if(!file.startsWith(root+path.sep))throw Error('outside preview');res.setHeader('Content-Type',types[path.extname(file)]||'application/octet-stream');res.end(await fs.readFile(file));}catch{res.writeHead(404);res.end();}\r\n}).listen(Number(process.env.MYR5_R21_PORT)||5203,'127.0.0.1',function(){console.log('Release 21 review http://127.0.0.1:'+this.address().port);});\r\n","scripts/release-build.mjs":"import {createHash} from 'node:crypto';\r\nimport {createReadStream} from 'node:fs';\r\nimport {readdir,writeFile} from 'node:fs/promises';\r\nimport {join} from 'node:path';\r\n\r\n// Stable content identity makes every changed deployment discoverable, even\r\n// when a release-note edit or manual service-worker version bump is forgotten.\r\nexport async function releaseBuildId(root='.'){\r\n const hash=createHash('sha256');\r\n async function add(path){hash.update(path+'\\0');for await(const chunk of createReadStream(join(root,path),{highWaterMark:4*1024*1024}))hash.update(chunk);hash.update('\\0');}\r\n async function walk(path){for(const entry of (await readdir(join(root,path),{withFileTypes:true})).sort((a,b)=>a.name.localeCompare(b.name))){if(entry.isSymbolicLink()||entry.name.startsWith('.')||['node_modules','dist'].includes(entry.name))continue;const name=path+'/'+entry.name;if(entry.isDirectory())await walk(name);else if(entry.isFile())await add(name);}}\r\n for(const entry of (await readdir(root,{withFileTypes:true})).sort((a,b)=>a.name.localeCompare(b.name)))if(entry.isFile()&&!['release-build.mjs','launch-runtime.mjs','app-runtime.mjs'].includes(entry.name)&&/\\.(html|css|mjs|js|webmanifest|json)$/.test(entry.name))await add(entry.name);\r\n for(const folder of ['server','db','scripts','scheduler','pod','creature','models','icons','handborne','arcade','voice','local-coach','modules','packs','war-room','food','vendor','audio'])try{await walk(folder);}catch(error){if(error.code!=='ENOENT')throw error;}\r\n return hash.digest('hex').slice(0,20);\r\n}\r\nexport async function prepareReleaseBuild(root='.'){\r\n const id=await releaseBuildId(root);\r\n await writeFile(join(root,'release-build.mjs'),`// Generated by the production build from app content.\\nexport const BUILD_ID='${id}';\\n`);\r\n return id;\r\n}\r\n","scripts/render-route-voice.mjs":"import {readFile,writeFile,mkdir,stat} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nimport {createRequire} from 'node:module';\r\nimport {execFile} from 'node:child_process';\r\nimport {promisify} from 'node:util';\r\nimport {ROUTE_LINES} from '../workout-route.mjs';\r\nconst {synthesize,PROFILE}=createRequire(import.meta.url)('./robot-speech.cjs'),exec=promisify(execFile);\r\nconst manifest=JSON.parse(await readFile('voice/manifest.json','utf8')),profile={...PROFILE,filter:'rubberband-preserved-formants-eq-v1'};\r\nconst phrases=[...Object.values(ROUTE_LINES),...[ROUTE_LINES.advance,ROUTE_LINES.max,ROUTE_LINES.limit].map(line=>line+' '+ROUTE_LINES.rest)];\r\nawait mkdir('../voice-work',{recursive:true});\r\nfor(const text of phrases){\r\n const hash=createHash('sha256').update(JSON.stringify(profile)+'\\n'+text).digest('hex').slice(0,24),output=`voice/${hash}.mp3`;\r\n try{await stat(output);}catch{const input=`../voice-work/${hash}.wav`;await writeFile(input,await synthesize(text));await exec(process.env.MYR5_FFMPEG||'ffmpeg',['-hide_banner','-loglevel','error','-y','-threads','1','-i',input,'-c:a','libmp3lame','-q:a','3','-ac','1',output],{windowsHide:true});}\r\n manifest.phrases[text]='/'+output;\r\n}\r\nmanifest.bytes=(await Promise.all(Object.values(manifest.phrases).map(async url=>(await stat('.'+url)).size))).reduce((a,b)=>a+b,0);\r\nawait writeFile('voice/manifest.json',JSON.stringify(manifest));console.log(`${phrases.length} workout challenge clips ready in the existing robot voice.`);\r\n","scripts/render-voice.mjs":"// Rebuild the approved Coach voice; filenames include its complete sound profile.\r\nimport {readFile,mkdir,writeFile} from 'node:fs/promises';\r\nimport {createHash} from 'node:crypto';\r\nimport {createRequire} from 'node:module';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nconst require=createRequire(import.meta.url),{synthesize,PROFILE}=require('./robot-speech.cjs');\r\nconst previous=JSON.parse(await readFile('voice/manifest.json','utf8'));\r\nconst phrases=new Set(Object.keys(previous.phrases));\r\nfor(const file of ['coach.mjs','menu.mjs','app.mjs','movement-engine.mjs','movement-rules.mjs','pod/pod.mjs','pod/encouragement.mjs','coach-profile.mjs','office-domain.mjs']){\r\n const code=await readFile(file,'utf8');for(const m of code.matchAll(/'([^'\\n]{3,600})'/g)){\r\n  const text=m[1];if(/^[A-Za-z]/.test(text)&&/[ .!?]/.test(text)&&!/[<>{}=\\[\\]\\/]/.test(text)&&!text.includes(' · '))phrases.add(text);\r\n }\r\n}\r\nfor(const m of Object.values(EXERCISES))for(const text of [m.name,m.name+' selected.',m.name+' example.',m.name+'? Hold thumbs up to confirm.',m.hint])phrases.add(text);\r\nfor(let n=1;n<=200;n++)phrases.add(String(n));\r\nfor(let n=10;n<=600;n+=10){phrases.add(`${n} seconds.`);phrases.add(`${n} seconds left.`);phrases.add(`${n} seconds. Keep your own pace.`);}\r\nphrases.add('MOM believes in you. One. Two. Three.');\r\nfor(const text of [...phrases])for(const sentence of text.match(/[^.!?]+[.!?]+(?:\\s|$)|[^.!?]+$/g)||[])if(sentence.trim())phrases.add(sentence.trim());\r\nconst soundProfile={...PROFILE,filter:'rubberband-preserved-formants-eq-v1'};\r\nawait mkdir('voice',{recursive:true});\r\nconst manifest={version:2,profile:soundProfile,engine:'eSpeak NG + Rubber Band',license:previous.license,phrases:{}};\r\nlet total=0,completed=0;const queue=[...phrases];\r\nasync function render(){while(queue.length){const text=queue.shift(),filename=createHash('sha256').update(JSON.stringify(soundProfile)+'\\n'+text).digest('hex').slice(0,24)+'.wav';let wav;\r\n try{wav=await readFile('voice/'+filename);}catch{wav=await synthesize(text);await writeFile('voice/'+filename,wav);}\r\n manifest.phrases[text]='/voice/'+filename;total+=wav.length;if(++completed%100===0)console.log(`${completed}/${phrases.size} approved voice clips ready.`);\r\n}}\r\nawait render();\r\nmanifest.bytes=total;await writeFile('voice/manifest.json',JSON.stringify(manifest));console.log(`${completed} approved clips; ${Math.round(total/1024/1024)} MB.`);\r\n","scripts/reset-game-generation-d1.mjs":"// One D1 batch owns the reset, assertions, preservation checks and immutable receipt.\r\n// No statement from this batch may ever be executed individually.\r\nimport { createHash, randomUUID } from 'node:crypto';\r\nimport { createFreshGameBase, gameGenerationGuard, inspectGameGeneration } from '../server/game-generation.mjs';\r\nimport { CLEAR_GAME_TABLES, isEarnedGamePack, PRODUCTION_TARGET } from './reset-game-generation.mjs';\r\nimport { WORLD_SECTORS } from '../modules/coach-world/sites.mjs';\r\nconst check=(ok,message)=>{if(!ok)throw Object.assign(Error(message),{safeResetMessage:message});};\r\nconst q=name=>'\"'+name.replaceAll('\"','\"\"')+'\"';\r\nconst sha=s=>createHash('sha256').update(s).digest('hex');\r\nconst stable=o=>JSON.stringify(o,(_,v)=>v&&typeof v==='object'&&!Array.isArray(v)?Object.fromEntries(Object.keys(v).sort().map(k=>[k,v[k]])):v);\r\nconst preservedWorld=['world_drop_locations','world_friends','world_friend_codes','world_neutral_zones'];\r\nconst special=['world_bases','world_sectors','account_pack_entitlements','war_room_arsenals','game_generation','game_generation_assertions','game_generation_resets'];\r\nconst schemaSql=\"SELECT json_group_array(json_array(type,name,tbl_name,sql)) AS snapshot FROM (SELECT type,name,tbl_name,sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%' ORDER BY type,name)\";\r\nexport const resetTableQuery=\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%' ORDER BY name\";\r\n// Typed cells preserve NULL, integer/text and blobs; order by all columns is deterministic.\r\nexport function snapshotSql(table,columns,where='1=1'){\r\n const cells=columns.map(c=>`json_array(typeof(${q(c)}),CASE WHEN typeof(${q(c)})='blob' THEN hex(${q(c)}) ELSE ${q(c)} END)`);\r\n return `SELECT json_group_array(json_array(${cells.join(',')})) AS snapshot FROM (SELECT ${columns.map(q).join(',')} FROM ${q(table)} WHERE ${where} ORDER BY ${columns.map(q).join(',')})`;\r\n}\r\nexport const snapshotEvidence=s=>({count:JSON.parse(s).length,sha256:sha(stable(JSON.parse(s)))});\r\nexport async function inspectD1Reset(db){\r\n const tables=(await db.prepare(resetTableQuery).all()).results.map(r=>r.name);\r\n const snapshots=[];\r\n for(const table of tables){\r\n  check(!table.startsWith('world_')||[...CLEAR_GAME_TABLES,...preservedWorld,...special].includes(table),`Unclassified game table: ${table}`);\r\n  const columns=(await db.prepare(`PRAGMA table_info(${q(table)})`).all()).results.map(r=>r.name);\r\n  const sql=snapshotSql(table,columns),snapshot=(await db.prepare(sql).first()).snapshot;\r\n  snapshots.push({table,columns,sql,snapshot});\r\n }\r\n return {tables,snapshots,schema:(await db.prepare(schemaSql).first()).snapshot,before:Object.fromEntries(snapshots.map(s=>[s.table,snapshotEvidence(s.snapshot)]))};\r\n}\r\nconst decoded=s=>JSON.parse(s.snapshot).map(row=>Object.fromEntries(s.columns.map((c,i)=>[c,row[i][1]])));\r\nconst assertion=(db,condition,...params)=>db.prepare(`INSERT INTO game_generation_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`).bind(randomUUID(),...params);\r\nconst requestFor=o=>stable({version:1,target:o.target,resetId:o.resetId,expectedGeneration:o.expectedGeneration,resetAt:o.resetAt});\r\nasync function receipt(db,o){\r\n const row=await db.prepare('SELECT request,evidence FROM game_generation_resets WHERE reset_id=?').bind(o.resetId).first();\r\n if(!row)return null;check(row.request===requestFor(o),'Reset ID reused with different target/generation/cutoff');return {...JSON.parse(row.evidence),alreadyReset:true};\r\n}\r\nexport async function resetD1GameGeneration(db,o){\r\n check(/^[A-Za-z0-9:_-]{1,120}$/.test(o.resetId||''),'Invalid reset ID');\r\n check(Number.isSafeInteger(o.expectedGeneration)&&o.expectedGeneration>=1&&o.expectedGeneration<Number.MAX_SAFE_INTEGER,'Invalid expected generation');\r\n check(Number.isSafeInteger(o.resetAt)&&o.resetAt>0,'Explicit cutoff required');\r\n check(stable(o.target)===stable(PRODUCTION_TARGET),'Production target mismatch');\r\n const prior=await receipt(db,o);if(prior)return prior;\r\n const current=await inspectGameGeneration(db);check(current.gameGeneration===o.expectedGeneration,'Game generation mismatch');check(o.resetAt>current.gameResetAt,'Cutoff must increase');\r\n const plan=await inspectD1Reset(db),byName=new Map(plan.snapshots.map(s=>[s.table,s]));\r\n const rows=table=>byName.has(table)?decoded(byName.get(table)):[];\r\n const authored=new Map(WORLD_SECTORS.map(s=>[s.id,s]));\r\n for(const row of rows('world_sectors'))check(row.kind==='territory'||authored.has(row.id),'Unknown authored sector');\r\n const evidence={...JSON.parse(requestFor(o)),mode:o.execute?'executed-d1':'dry-run-d1',gameGeneration:o.expectedGeneration+1,gameResetAt:o.resetAt,before:plan.before,alreadyReset:false,\r\n  affected:{cleared:Object.fromEntries(CLEAR_GAME_TABLES.filter(t=>byName.has(t)).map(t=>[t,plan.before[t].count])),freshBases:rows('world_bases').length,earnedPacks:rows('account_pack_entitlements').filter(p=>isEarnedGamePack(p.pack_id)).length}};\r\n if(!o.execute)return evidence;\r\n check(o.integrationReady===true,'Integration-ready attestation required');\r\n check(o.backup?.path&&/^[a-f0-9]{64}$/.test(o.backup.sha256),'Verified backup required');\r\n check(stable(o.backupEvidence)===stable(plan.before),'Backup differs from current database; take a fresh backup');\r\n evidence.backup=o.backup;\r\n const statements=[gameGenerationGuard(db,o.expectedGeneration)];\r\n // Schema AND whole table snapshots are guarded inside this transaction. This covers\r\n // new/deleted owners, complete base JSON, revisions, rewards and protected writes.\r\n statements.push(assertion(db,`(${schemaSql})=?`,plan.schema));\r\n statements.push(assertion(db,`(SELECT json_group_array(name) FROM (${resetTableQuery}))=?`,JSON.stringify(plan.tables)));\r\n for(const s of plan.snapshots.filter(s=>s.table!=='game_generation_assertions'))statements.push(assertion(db,`(${s.sql})=?`,s.snapshot));\r\n const protectedSnapshots=plan.snapshots.filter(s=>!CLEAR_GAME_TABLES.includes(s.table)&&!special.includes(s.table));\r\n const protectedChecks=protectedSnapshots.map(s=>({name:s.table,sql:s.sql,snapshot:s.snapshot}));\r\n // Only proven server-earned packs are removed; ambiguous and paid ownership survives.\r\n const earned=rows('account_pack_entitlements').filter(p=>isEarnedGamePack(p.pack_id));\r\n if(byName.has('account_pack_entitlements')){\r\n  const ids=earned.map(p=>p.pack_id),where=ids.length?`pack_id NOT IN (${ids.map(id=>\"'\"+id.replaceAll(\"'\",\"''\")+\"'\").join(',')})`:'1=1';\r\n  const s=byName.get('account_pack_entitlements'),sql=snapshotSql(s.table,s.columns,where);\r\n  protectedChecks.push({name:'retained_pack_ownership',sql,snapshot:(await db.prepare(sql).first()).snapshot});\r\n }\r\n if(byName.has('war_room_arsenals')){const sql=snapshotSql('war_room_arsenals',['user_id','recipes']);protectedChecks.push({name:'war_room_recipes',sql,snapshot:(await db.prepare(sql).first()).snapshot});}\r\n if(byName.has('world_sectors')){const sql=snapshotSql('world_sectors',['id','site_id','kind','latitude','longitude','enabled'],\"kind<>'territory'\");protectedChecks.push({name:'authored_geography',sql,snapshot:(await db.prepare(sql).first()).snapshot});}\r\n evidence.protectedBefore=Object.fromEntries(protectedChecks.map(s=>[s.name,snapshotEvidence(s.snapshot)]));\r\n // The same equality assertions after mutations prove these exact hashes inside the batch.\r\n evidence.protectedAfter=evidence.protectedBefore;\r\n statements.push(db.prepare('UPDATE game_generation SET generation=?,reset_at=? WHERE id=1').bind(evidence.gameGeneration,o.resetAt));\r\n for(const p of earned)statements.push(db.prepare('DELETE FROM account_pack_entitlements WHERE user_id=? AND pack_id=?').bind(p.user_id,p.pack_id));\r\n for(const r of rows('world_bases')){\r\n  const fresh=createFreshGameBase(r.owner,o.resetAt,evidence,JSON.parse(r.base));\r\n  statements.push(db.prepare('UPDATE world_bases SET base=?,charge=0,revision=revision+1,created_at=? WHERE owner=? AND epoch=? AND revision=? AND base=?').bind(JSON.stringify(fresh),o.resetAt,r.owner,r.epoch,r.revision,r.base));\r\n  statements.push(assertion(db,'EXISTS(SELECT 1 FROM world_bases WHERE owner=? AND epoch=? AND public_id=? AND revision=? AND base=? AND charge=0 AND created_at=?)',r.owner,r.epoch,r.public_id,r.revision+1,JSON.stringify(fresh),o.resetAt));\r\n }\r\n if(byName.has('war_room_arsenals'))statements.push(db.prepare('UPDATE war_room_arsenals SET loadout=?,revision=revision+1,updated_at=?').bind('{\"type\":\"rapier\",\"tier\":0}',o.resetAt));\r\n for(const table of CLEAR_GAME_TABLES)if(byName.has(table))statements.push(db.prepare(`DELETE FROM ${q(table)}`));\r\n if(byName.has('world_sectors')){\r\n  statements.push(db.prepare(\"DELETE FROM world_sectors WHERE kind='territory'\"));\r\n  for(const row of rows('world_sectors').filter(r=>r.kind!=='territory'))statements.push(db.prepare(\"UPDATE world_sectors SET controller=?,revision=revision+1,generation=generation+1,closes_at=NULL,pause_until=NULL,grace_until=0,phase='stable',changed_at=?,conflict_at=? WHERE id=?\").bind(authored.get(row.id).initialFaction,o.resetAt,o.resetAt,row.id));\r\n }\r\n for(const s of protectedChecks)statements.push(assertion(db,`(${s.sql})=?`,s.snapshot));\r\n statements.push(db.prepare('DELETE FROM game_generation_assertions'));\r\n statements.push(db.prepare('INSERT INTO game_generation_resets(reset_id,previous_generation,generation,reset_at,request,evidence) VALUES(?,?,?,?,?,?)').bind(o.resetId,o.expectedGeneration,evidence.gameGeneration,o.resetAt,requestFor(o),JSON.stringify(evidence)));\r\n // ponytail: bounded one-shot release operation, not a resumable per-owner job.\r\n check(statements.length<=900,'Reset exceeds one-batch release budget; do not split');\r\n await o.beforeBatch?.(); // fixture race injection only; CLI never supplies it\r\n try{await db.batch(statements);}catch(error){const done=await receipt(db,o);if(done)return done;throw error;}\r\n return evidence;\r\n}\r\n","scripts/reset-game-generation.mjs":"#!/usr/bin/env node\r\n// Deliberately LOCAL SQLite only. No Wrangler, HTTP, credentials, remote mutation or deployment.\r\n// A production executor must use equivalent ONE-transaction D1 batch semantics after integration.\r\nimport { DatabaseSync } from 'node:sqlite';\r\nimport { createHash } from 'node:crypto';\r\nimport { readFileSync, writeFileSync, existsSync, statSync, realpathSync } from 'node:fs';\r\nimport { resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport { parseArgs } from 'node:util';\r\nimport { createFreshGameBase } from '../server/game-generation.mjs';\r\nimport { WORLD_SECTORS } from '../modules/coach-world/sites.mjs';\r\n\r\nexport const PRODUCTION_TARGET = Object.freeze({ worker: 'myr5-coach', origin: 'https://myr5.mominc.online',\r\n  binding: 'DB', databaseName: 'myr5-coach', databaseId: '5731afde-1437-4fcf-9b25-1ad868b24fe3' });\r\nexport const CLEAR_GAME_TABLES = Object.freeze([\r\n  'world_requests', 'world_sources', 'world_grants', 'world_sequence', 'world_runs', 'world_run_events',\r\n  'world_run_visits', 'world_run_receipts', 'world_run_writer_claims', 'world_run_campaign',\r\n  'world_raid_tickets', 'world_coach_unlocks', 'world_boss_progression', 'world_boss_run_pins',\r\n  'world_coach_rescues', 'world_run_rescue_manifests', 'world_run_rescue_level_receipts',\r\n  'world_boss_clear_receipts', 'world_boss_run_manifests', 'world_boss_level_clear_receipts',\r\n  'world_raids', 'world_raid_pack_claims', 'world_drop_challenges',\r\n  'world_drop_claims', 'world_wild_encounters', 'world_wild_daily', 'world_wild_seen', 'world_wild_lockout',\r\n  'world_walks', 'world_presence', 'world_influence', 'world_visits', 'world_encounters',\r\n  'world_last_rewarded', 'world_zones', 'world_zone_npc', 'world_assertions',\r\n]);\r\nconst PRESERVED_WORLD_TABLES = ['world_drop_locations', 'world_friends', 'world_friend_codes', 'world_neutral_zones'];\r\nconst SPECIAL_TABLES = ['world_bases', 'world_sectors', 'account_pack_entitlements', 'war_room_arsenals',\r\n  'game_generation', 'game_generation_assertions', 'game_generation_resets'];\r\n// These exact namespaces are issued by server/run-room-rewards.mjs, world-raid-api.mjs and\r\n// world-drops.mjs. Unknown/legacy/catalog pack IDs are NEVER treated as earned by default.\r\nexport const isEarnedGamePack = id => typeof id === 'string'\r\n  && /^reward-pack:(?:uncommon|rare|legendary|secret):(?:run-|raid-|world-).+$/u.test(id);\r\nconst sha = data => createHash('sha256').update(data).digest('hex');\r\nconst quote = name => `\"${name.replaceAll('\"', '\"\"')}\"`;\r\nconst assert = (condition, message) => { if (!condition) throw Error(message); };\r\nconst stable = value => JSON.stringify(value, (_, v) => typeof v === 'bigint' ? { bigint: String(v) }\r\n  : v instanceof Uint8Array ? { hex: Buffer.from(v).toString('hex') }\r\n    : v && !Array.isArray(v) && typeof v === 'object' ? Object.fromEntries(Object.keys(v).sort().map(k => [k, v[k]])) : v);\r\nconst hashRows = rows => ({ count: rows.length, sha256: sha(rows.map(stable).sort().join('\\n')) });\r\nconst tableNames = db => db.prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\").all().map(r => r.name);\r\nconst rows = (db, table) => db.prepare(`SELECT * FROM ${quote(table)}`).all();\r\nexport function parseJsonConfig(text) {\r\n  // Preserve strings (URLs, escaped quotes) while removing JSONC comments/trailing commas.\r\n  return JSON.parse(text.replace(/^\\uFEFF/u, '').replace(/(\"(?:\\\\.|[^\"\\\\])*\")|\\/\\/[^\\r\\n]*|\\/\\*[\\s\\S]*?\\*\\//gu, (match, string) => string ?? '')\r\n    .replace(/(\"(?:\\\\.|[^\"\\\\])*\")|,(?=\\s*[}\\]])/gu, (match, string) => string ?? ''));\r\n}\r\nexport function validateResetTarget(config, expected) {\r\n  assert(expected && Object.keys(PRODUCTION_TARGET).every(k => typeof expected[k] === 'string' && expected[k]), 'Explicit worker/origin/binding/database name/UUID required');\r\n  const bindings = config.d1_databases?.filter(b => b.binding === expected.binding) ?? [];\r\n  assert(bindings.length === 1, 'D1 binding must resolve exactly once in explicit root config (no implicit env)');\r\n  assert(config.name === expected.worker && config.vars?.RELEASE_ORIGIN === expected.origin\r\n    && config.routes?.some(r => r.custom_domain === true && `https://${r.pattern}` === expected.origin)\r\n    && bindings[0].database_name === expected.databaseName && bindings[0].database_id === expected.databaseId, 'Reset target/config mismatch');\r\n  return { ...expected };\r\n}\r\nfunction protectedEvidence(db, tables) {\r\n  const result = {};\r\n  for (const table of tables) {\r\n    if (CLEAR_GAME_TABLES.includes(table) || SPECIAL_TABLES.includes(table)) continue;\r\n    result[table] = hashRows(rows(db, table));\r\n  }\r\n  if (tables.includes('account_pack_entitlements')) result.account_pack_entitlements = hashRows(rows(db, 'account_pack_entitlements').filter(r => !isEarnedGamePack(r.pack_id)));\r\n  if (tables.includes('war_room_arsenals')) result.war_room_recipes = hashRows(rows(db, 'war_room_arsenals').map(r => ({ user_id: r.user_id, recipes: r.recipes })));\r\n  if (tables.includes('world_bases')) result.private_base_preferences = hashRows(rows(db, 'world_bases').map(r => {\r\n    const b = JSON.parse(r.base), fresh = createFreshGameBase(r.owner, 0, { gameGeneration: 1, gameResetAt: 0 }, b);\r\n    return { owner: r.owner, epoch: r.epoch, public_id: r.public_id, home: fresh.home, addressPreferences: fresh.addressPreferences };\r\n  }));\r\n  if (tables.includes('world_sectors')) result.authored_geography = hashRows(rows(db, 'world_sectors').filter(r => r.kind !== 'territory')\r\n    .map(({ id, site_id, kind, latitude, longitude, enabled }) => ({ id, site_id, kind, latitude, longitude, enabled })));\r\n  return result;\r\n}\r\nfunction inspectReset(db, options) {\r\n  const tables = tableNames(db), knownWorld = new Set([...CLEAR_GAME_TABLES, ...PRESERVED_WORLD_TABLES, ...SPECIAL_TABLES]);\r\n  assert(!tables.some(t => t.startsWith('world_') && !knownWorld.has(t)), `Unclassified game tables: ${tables.filter(t => t.startsWith('world_') && !knownWorld.has(t)).join(',')}`);\r\n  assert(['game_generation', 'game_generation_resets', 'game_generation_assertions'].every(t => tables.includes(t)), 'Apply additive 0031 migration before reset');\r\n  const current = db.prepare('SELECT generation,reset_at FROM game_generation WHERE id=1').get();\r\n  assert(current && Number.isSafeInteger(current.generation) && current.generation >= 1 && Number.isSafeInteger(current.reset_at) && current.reset_at >= 0, 'Invalid game generation metadata');\r\n  const before = Object.fromEntries(tables.map(t => [t, hashRows(rows(db, t))]));\r\n  const packs = tables.includes('account_pack_entitlements') ? rows(db, 'account_pack_entitlements') : [];\r\n  return { tables, current, before, protectedBefore: protectedEvidence(db, tables),\r\n    affected: { cleared: Object.fromEntries(CLEAR_GAME_TABLES.filter(t => tables.includes(t)).map(t => [t, before[t].count])),\r\n      freshBases: before.world_bases?.count ?? 0, resetArsenals: before.war_room_arsenals?.count ?? 0,\r\n      resetSectors: before.world_sectors?.count ?? 0, earnedPacks: packs.filter(r => isEarnedGamePack(r.pack_id)).length,\r\n      preservedPacks: packs.filter(r => !isEarnedGamePack(r.pack_id)).length },\r\n    request: { version: 1, target: options.target, resetId: options.resetId, expectedGeneration: options.expectedGeneration, resetAt: options.resetAt } };\r\n}\r\n\r\n/** Whole reset INCLUDING pre/post evidence and journal is one SQLite transaction. Dry-run rolls\r\n * back a read transaction and changes nothing. This is also the executable local export rehearsal.\r\n * ponytail: scans all rows for protected hashes; for a large database use a streaming evidence\r\n * exporter before extending this to production. Never silently split the reset transaction.\r\n */\r\nexport function resetLocalGameGeneration(db, options) {\r\n  assert(typeof options.resetId === 'string' && /^[A-Za-z0-9:_-]{1,120}$/u.test(options.resetId), 'Invalid reset ID');\r\n  assert(Number.isSafeInteger(options.expectedGeneration) && options.expectedGeneration >= 1 && options.expectedGeneration < Number.MAX_SAFE_INTEGER, 'Invalid expected generation');\r\n  assert(Number.isSafeInteger(options.resetAt) && options.resetAt > 0, 'Explicit positive reset cutoff required');\r\n  assert(options.target && Object.keys(PRODUCTION_TARGET).every(k => options.target[k]), 'Validated explicit target required');\r\n  db.exec(options.execute ? 'BEGIN IMMEDIATE' : 'BEGIN');\r\n  try {\r\n    const plan = inspectReset(db, options), request = stable(plan.request);\r\n    const prior = db.prepare('SELECT request,evidence FROM game_generation_resets WHERE reset_id=?').get(options.resetId);\r\n    if (prior) {\r\n      assert(prior.request === request, 'Reset ID reused with different target/generation/cutoff');\r\n      db.exec('ROLLBACK');\r\n      return { ...JSON.parse(prior.evidence), alreadyReset: true };\r\n    }\r\n    assert(plan.current.generation === options.expectedGeneration, 'Game generation mismatch');\r\n    assert(options.resetAt > plan.current.reset_at, 'Reset cutoff must increase');\r\n    // Every stored hub must have an authored initial faction. Never guess a controller.\r\n    const sectors = new Map(WORLD_SECTORS.map(s => [s.id, s]));\r\n    if (plan.tables.includes('world_sectors')) for (const s of rows(db, 'world_sectors')) {\r\n      assert(s.kind === 'territory' || sectors.has(s.id), `Unknown authored sector ${s.id}: cannot restore initial control`);\r\n    }\r\n    const evidence = { ...plan.request, mode: options.execute ? 'executed-local' : 'dry-run',\r\n      databaseScope: 'local SQLite fixture/export; config is not proof of remote database identity',\r\n      before: plan.before, affected: plan.affected, protectedBefore: plan.protectedBefore,\r\n      gameGeneration: options.expectedGeneration + 1, gameResetAt: options.resetAt, alreadyReset: false };\r\n    if (!options.execute) { db.exec('ROLLBACK'); return evidence; }\r\n    assert(options.integrationReady === true, 'Integration-ready attestation required');\r\n    assert(options.backup?.path && /^[a-f0-9]{64}$/u.test(options.backup.sha256), 'Verified backup path/SHA-256 required');\r\n    assert(typeof options.verifyBackup === 'function', 'Backup verification callback required');\r\n    options.verifyBackup(); // CLI checks real export bytes under the write lock; fixture adapters supply evidence.\r\n    evidence.backup = options.backup;\r\n    db.prepare('UPDATE game_generation SET generation=?,reset_at=? WHERE id=1').run(evidence.gameGeneration, options.resetAt);\r\n    if (plan.tables.includes('account_pack_entitlements')) {\r\n      const remove = db.prepare('DELETE FROM account_pack_entitlements WHERE user_id=? AND pack_id=?');\r\n      for (const pack of rows(db, 'account_pack_entitlements')) if (isEarnedGamePack(pack.pack_id)) remove.run(pack.user_id, pack.pack_id);\r\n    }\r\n    // Rebuild bases rather than preserving an allowlist of their ever-growing game fields.\r\n    // Only validated address preferences, row identity and account epoch survive.\r\n    if (plan.tables.includes('world_bases')) for (const row of rows(db, 'world_bases')) {\r\n      const base = createFreshGameBase(row.owner, options.resetAt, evidence, JSON.parse(row.base));\r\n      db.prepare('UPDATE world_bases SET base=?,charge=0,revision=revision+1,created_at=? WHERE owner=?').run(JSON.stringify(base), options.resetAt, row.owner);\r\n    }\r\n    if (plan.tables.includes('war_room_arsenals')) db.prepare('UPDATE war_room_arsenals SET loadout=?,revision=revision+1,updated_at=?').run('{\"type\":\"rapier\",\"tier\":0}', options.resetAt);\r\n    for (const table of CLEAR_GAME_TABLES) if (plan.tables.includes(table)) db.exec(`DELETE FROM ${quote(table)}`);\r\n    if (plan.tables.includes('world_sectors')) {\r\n      db.exec(\"DELETE FROM world_sectors WHERE kind='territory'\");\r\n      const update = db.prepare(\"UPDATE world_sectors SET controller=?,revision=revision+1,generation=generation+1,closes_at=NULL,pause_until=NULL,grace_until=0,phase='stable',changed_at=?,conflict_at=? WHERE id=?\");\r\n      for (const row of rows(db, 'world_sectors')) update.run(sectors.get(row.id).initialFaction, options.resetAt, options.resetAt, row.id);\r\n    }\r\n    db.exec('DELETE FROM game_generation_assertions');\r\n    evidence.protectedAfter = protectedEvidence(db, plan.tables);\r\n    assert(stable(evidence.protectedBefore) === stable(evidence.protectedAfter), 'Protected-table evidence changed; rolling back reset');\r\n    evidence.after = Object.fromEntries(plan.tables.filter(t => t !== 'game_generation_resets').map(t => [t, hashRows(rows(db, t))]));\r\n    db.prepare('INSERT INTO game_generation_resets(reset_id,previous_generation,generation,reset_at,request,evidence) VALUES(?,?,?,?,?,?)')\r\n      .run(options.resetId, options.expectedGeneration, evidence.gameGeneration, options.resetAt, request, JSON.stringify(evidence));\r\n    db.exec('COMMIT');\r\n    return evidence;\r\n  } catch (error) { if (db.isTransaction) db.exec('ROLLBACK'); throw error; }\r\n}\r\n\r\nasync function main() {\r\n  const { values: v } = parseArgs({ options: Object.fromEntries([\r\n    'config', 'sqlite', 'worker', 'origin', 'binding', 'database-name', 'database-id', 'reset-id', 'expected-generation',\r\n    'cutoff-ms', 'backup', 'backup-sha256', 'evidence',\r\n  ].map(k => [k, { type: 'string' }]).concat([['execute', { type: 'boolean', default: false }], ['integration-ready', { type: 'boolean', default: false }]])) });\r\n  for (const key of ['config', 'sqlite', 'worker', 'origin', 'binding', 'database-name', 'database-id', 'reset-id', 'expected-generation', 'cutoff-ms', 'evidence']) assert(v[key], `Missing --${key}`);\r\n  const configPath = realpathSync(v.config), sqlitePath = realpathSync(v.sqlite), evidencePath = resolve(v.evidence);\r\n  assert(evidencePath !== sqlitePath && evidencePath !== configPath && !existsSync(evidencePath), 'Evidence destination must be a NEW separate file');\r\n  const target = validateResetTarget(parseJsonConfig(readFileSync(configPath, 'utf8')), { worker: v.worker, origin: v.origin,\r\n    binding: v.binding, databaseName: v['database-name'], databaseId: v['database-id'] });\r\n  for (const key of ['expected-generation', 'cutoff-ms']) assert(/^[1-9]\\d*$/u.test(v[key]), `Invalid --${key}`);\r\n  let backup;\r\n  if (v.execute) {\r\n    assert(v.backup && v['backup-sha256'], '--execute requires --backup and --backup-sha256');\r\n    backup = { path: realpathSync(v.backup), sha256: v['backup-sha256'] };\r\n    assert(backup.path !== sqlitePath && backup.path !== evidencePath, 'Backup must be a separate file');\r\n    const a = statSync(backup.path), b = statSync(sqlitePath);\r\n    assert(a.dev !== b.dev || a.ino !== b.ino, 'Backup must not be a hard link to the source');\r\n  }\r\n  const db = new DatabaseSync(sqlitePath, { readOnly: !v.execute });\r\n  try {\r\n    const result = resetLocalGameGeneration(db, { target, resetId: v['reset-id'], expectedGeneration: Number(v['expected-generation']),\r\n      resetAt: Number(v['cutoff-ms']), execute: v.execute, integrationReady: v['integration-ready'], backup,\r\n      verifyBackup: () => {\r\n        assert(!existsSync(`${sqlitePath}-wal`) || statSync(`${sqlitePath}-wal`).size === 0, 'Use a checkpointed SQLite export; active WAL is unsupported');\r\n        assert(sha(readFileSync(backup.path)) === backup.sha256 && sha(readFileSync(sqlitePath)) === backup.sha256, 'Backup is not an exact SHA-256 match for the locked source export');\r\n      } });\r\n    writeFileSync(evidencePath, `${JSON.stringify({ ...result, configPath, sqlitePath }, null, 2)}\\n`, { flag: 'wx' });\r\n    console.log(JSON.stringify({ mode: result.mode, alreadyReset: result.alreadyReset, gameGeneration: result.gameGeneration, affected: result.affected, evidence: evidencePath }));\r\n  } finally { db.close(); }\r\n}\r\nif (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) main().catch(error => { console.error(error.message); process.exitCode = 1; });\r\n","scripts/robot-speech.cjs":"// Local formant speech. No model download, cloud service, microphone, or GPU.\r\nconst {execFile}=require('node:child_process');\r\nconst fs=require('node:fs');\r\nconst octave=require('./octave.cjs');\r\nconst ENGINE='C:/Program Files/eSpeak NG/espeak-ng.exe';\r\n// Keep the synthetic timbre, but retain sentence stress and avoid choppy word gaps.\r\nconst PROFILE={voice:'en-us',wordsPerMinute:125,pitch:52,pitchRange:45,wordGap:0,pitchRatio:2**(-2/12)};\r\nconst cache=new Map(),pending=new Map();let cacheBytes=0;\r\nconst MAX_CACHE=12*1024*1024;\r\nfunction synthesize(text){\r\n if(typeof text!=='string'||!text.trim()||text.length>600||/[\\x00-\\x08\\x0b-\\x1f]/.test(text))return Promise.reject(new Error('Invalid speech text'));\r\n text=text.trim();if(cache.has(text))return Promise.resolve(cache.get(text));if(pending.has(text))return pending.get(text);\r\n if(pending.size>=4)return Promise.reject(new Error('Speech busy'));\r\n const task=new Promise((resolve,reject)=>{\r\n  // Arguments are passed without a shell. Text is never interpreted as markup.\r\n  const args=['--stdout','-b','1','-v',PROFILE.voice,'-s',String(PROFILE.wordsPerMinute),'-p',String(PROFILE.pitch),'-P',String(PROFILE.pitchRange),'-g',String(PROFILE.wordGap),'--stdin'];\r\n  const child=execFile(ENGINE,args,{encoding:'buffer',windowsHide:true,timeout:5000,maxBuffer:5*1024*1024},async(error,wav)=>{\r\n   if(error)return reject(new Error('Robot voice unavailable'));\r\n   if(wav.length<44||wav.toString('ascii',0,4)!=='RIFF')return reject(new Error('Invalid speech audio'));\r\n   // eSpeak's stdout uses streaming placeholder sizes; give phone decoders exact sizes.\r\n   wav.writeUInt32LE(wav.length-8,4);let found=false;\r\n   for(let offset=12;offset+8<=wav.length;){const size=wav.readUInt32LE(offset+4);if(wav.toString('ascii',offset,offset+4)==='data'){wav.writeUInt32LE(wav.length-offset-8,offset+4);found=true;break;}offset+=8+size+(size%2);}\r\n   if(!found)return reject(new Error('Missing speech samples'));\r\n   try{wav=await octave.lowerOctave(wav);}catch(error){reject(error);return;}\r\n   while(cache.size&&cacheBytes+wav.length>MAX_CACHE){const key=cache.keys().next().value;cacheBytes-=cache.get(key).length;cache.delete(key);}\r\n   cache.set(text,wav);cacheBytes+=wav.length;resolve(wav);\r\n  });\r\n  child.stdin.on('error',()=>{});child.stdin.end(text+'\\n','utf8');\r\n }).finally(()=>pending.delete(text));\r\n pending.set(text,task);return task;\r\n}\r\nmodule.exports={synthesize,PROFILE,available:()=>fs.existsSync(ENGINE)&&octave.available()};\r\n","scripts/serve-flight.mjs":"// Local source preview, including ignored geographic assets. No production writes.\r\nimport {createServer} from 'node:http';\r\nimport {readFile,stat} from 'node:fs/promises';\r\nimport {resolve,extname,sep} from 'node:path';\r\nconst root=resolve('.'),tiles=process.env.WORLD_TILES_DIR||'C:/myr5-work/world-bake/out/static-gz/world/v1';\r\nconst mime={'.html':'text/html','.mjs':'text/javascript','.js':'text/javascript','.css':'text/css','.json':'application/json','.png':'image/png','.webp':'image/webp','.svg':'image/svg+xml','.woff2':'font/woff2','.glb':'model/gltf-binary'};\r\ncreateServer(async(req,res)=>{try{const u=new URL(req.url,'http://localhost'),p=decodeURIComponent(u.pathname);if(p==='/api/flight/address'){const {flightAddress}=await import('../server/flight-address.mjs');const parts=[];for await(const c of req)parts.push(c);const r=await flightAddress(new Request('http://'+req.headers.host+p,{method:req.method,headers:req.headers,body:Buffer.concat(parts)}));res.writeHead(r.status,Object.fromEntries(r.headers));res.end(await r.text());return;}\r\n const path=p.startsWith('/world/v1/')?resolve(tiles,p.slice(10)):resolve(root,'.'+(p==='/'?'/pose.html':p==='/pose'?'/pose.html':p));const allowed=p.startsWith('/world/v1/')?resolve(tiles):root;if(path!==allowed&&!path.startsWith(allowed+sep))throw Error('path');const data=await readFile(path);res.writeHead(200,{'Content-Type':mime[extname(path)]||'application/octet-stream','Cache-Control':'no-store'});res.end(data);\r\n }catch{res.writeHead(404);res.end('Not found');}}).listen(Number(process.env.FLIGHT_PORT)||5198,'127.0.0.1',()=>console.log('Flight preview http://127.0.0.1:5198/pose#world'));\r\n","scripts/shader-preview.mjs":"import {createServer} from 'node:http';\r\nimport {readFile,stat} from 'node:fs/promises';\r\nimport {resolve,extname,sep} from 'node:path';\r\nimport {fileURLToPath} from 'node:url';\r\nconst types={'.html':'text/html','.js':'text/javascript','.mjs':'text/javascript','.css':'text/css','.json':'application/json','.webp':'image/webp','.png':'image/png','.jpg':'image/jpeg','.svg':'image/svg+xml','.glb':'model/gltf-binary','.bin':'application/octet-stream','.wasm':'application/wasm','.m4a':'audio/mp4','.mp3':'audio/mpeg','.woff2':'font/woff2','.ttf':'font/ttf','.webmanifest':'application/manifest+json'};\r\nexport function previewServer(root){\r\n root=resolve(root);\r\n return createServer(async(req,res)=>{\r\n  res.setHeader('Cache-Control','no-store');\r\n  const path=decodeURIComponent(new URL(req.url,'http://preview').pathname);\r\n  if(path.startsWith('/api/')){const config=path==='/api/auth/config';res.writeHead(config?200:401,{'Content-Type':'application/json'});res.end(JSON.stringify(config?{enabled:false}:{error:'Sign in on the live app'}));return;}\r\n  try{\r\n   let file=resolve(root,'.'+path);if(!file.startsWith(root+sep)&&file!==root)throw Error();\r\n   if(path.split('/').some(part=>part.startsWith('.'))||/^\\/(?:scripts|server|tests|node_modules|release-trust)(?:\\/|$)/.test(path))throw Error();\r\n   if((await stat(file)).isDirectory())file=resolve(file,'index.html');\r\n   const body=await readFile(file);res.writeHead(200,{'Content-Type':types[extname(file)]||'application/octet-stream'});res.end(req.method==='HEAD'?undefined:body);\r\n  }catch{res.writeHead(404);res.end('Not found');}\r\n });\r\n}\r\nif(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){\r\n const root=process.argv[2]||'dist/client',port=Number(process.argv[3])||18941;\r\n previewServer(root).listen(port,'127.0.0.1',()=>console.log(`Shader preview http://127.0.0.1:${port}`));\r\n}\r\n","scripts/sim-world-interactions.mjs":"import {writeFile,mkdir} from 'node:fs/promises';\r\nimport {createFishing,CONFIG} from '../modules/coach-world-rts/capture-minigame.mjs';\r\nimport {freshBase,applyBaseAction,advanceBase,constructionQuote} from '../modules/coach-world/state.mjs';\r\nimport {npcInventory,awardRunCurrency,applyNpcPurchase,readProgression} from '../modules/coach-world-rts/npc-progression.mjs';\r\nconst output={capture:{},onboarding:[],wallet:{}};\r\nfor(const tier of Object.keys(CONFIG)){\r\n const result={trackingWins:0,idleLosses:0,heldLosses:0,maxSeconds:0};\r\n for(let seed=0;seed<100;seed++)for(const policy of ['tracking','idle','held']){\r\n  const game=createFishing({tier,seed:String(seed)});let holding=false;\r\n  for(let frame=0;frame<3000&&game.state.status==='playing';frame++){\r\n   const s=game.state;if(policy==='tracking'){if(s.pos>s.zoneC+.025)holding=true;else if(s.pos<s.zoneC-.025)holding=false;}else holding=policy==='held';\r\n   game.step(20,holding);\r\n  }\r\n  if(policy==='tracking'&&game.state.status==='won'){result.trackingWins++;result.maxSeconds=Math.max(result.maxSeconds,game.state.t);}\r\n  if(policy==='idle'&&game.state.status==='lost')result.idleLosses++;\r\n  if(policy==='held'&&game.state.status==='lost')result.heldLosses++;\r\n }\r\n output.capture[tier]=result;\r\n}\r\nlet clock=Date.UTC(2026,9,21),base=freshBase('simulation',clock);\r\nfor(const buildingId of ['landing','hall-chest','production-chest','habitat','hangar','hq']){\r\n const quote=constructionQuote(base,buildingId);output.onboarding.push({buildingId,allowed:quote.allowed,cost:quote.cost,reason:quote.reason||null,materials:base.materials.chest});\r\n if(quote.allowed){base=applyBaseAction(base,{type:base.buildings[buildingId].level?'upgrade':'build',buildingId},{now:clock});clock=Math.max(clock,...Object.values(base.buildings).map(b=>b.job?.dueAt||0));base=advanceBase(base,clock);}\r\n}\r\nlet wallet=freshBase('wallet-simulation',clock);\r\nfor(let i=0;i<250;i++)wallet=awardRunCurrency(wallet,{runId:'workout',eventId:'event-'+i,amount:10},{now:clock+i}).base;\r\nconst before=readProgression(wallet).balance;wallet=awardRunCurrency(wallet,{runId:'workout',eventId:'event-0',amount:10},{now:clock+500}).base;\r\noutput.wallet={awards:250,balance:readProgression(wallet).balance,oldestReceiptRepaid:readProgression(wallet).balance!==before};\r\nfor(let i=0;i<10;i++){\r\n const npcId='npc-'+i,item=npcInventory(npcId,readProgression(wallet)).find(i=>i.kind==='stat');\r\n const purchase={npcId,itemId:item.id,receiptId:'purchase-'+i};\r\n const once=applyNpcPurchase(wallet,purchase,{now:clock+600+i});\r\n const twice=applyNpcPurchase(once.base,purchase,{now:clock+700+i});\r\n if(once.result.granted&&readProgression(twice.base).balance!==readProgression(once.base).balance)throw Error('Purchase replay spent twice');wallet=twice.base;\r\n}\r\noutput.wallet.afterPurchases=readProgression(wallet).balance;\r\nconst path=process.argv[2]||'artifacts/world1021-simulation.json';await mkdir('artifacts',{recursive:true});await writeFile(path,JSON.stringify(output,null,2));console.log(JSON.stringify(output,null,2));\r\nif(output.wallet.oldestReceiptRepaid||Object.values(output.capture).some(r=>r.trackingWins!==100||r.idleLosses!==100||r.heldLosses!==100))process.exitCode=1;\r\n","scripts/source-offer.mjs":"import {readdir,readFile} from 'node:fs/promises';\r\nimport {join} from 'node:path';\r\n\r\n// The AGPL source offer: the authored source needed to rebuild and run this release.\r\n// Never secrets or private material (plan/, env files, *.dpapi, private keys), tests,\r\n// build outputs or media (the app already serves its media).\r\nconst ROOTS=['server','db','scripts','scheduler','pod','local-coach','modules','creature','handborne','war-room','food','packs','arcade','drizzle','release-trust','audio'];\r\nconst GENERATED=new Set(['app-runtime.mjs','launch-runtime.mjs','local-coach-runtime.mjs','nutrition-data.mjs','workout-tracks.js','war-room/gala-bay.js','handborne/companion.mjs']);\r\nconst offered=path=>(path==='LICENSE'||/\\.(mjs|js|cjs|ts|tsx|py|html|css|json|jsonc|sql|webmanifest)$/.test(path))&&!GENERATED.has(path)&&!/(^|\\/)(assets|node_modules|dist|plan|tests)\\/|(^|\\/)\\.|\\.dpapi$|secret|\\.env|\\.dev\\.vars/i.test(path);\r\nconst PRIVATE_KEY=/-----BEGIN [A-Z ]*PRIVATE KEY-----|\"d\"\\s*:\\s*\"[A-Za-z0-9_-]{40,}\"/;\r\nconst SKIP_DIRECTORIES=new Set(['assets','node_modules','dist','plan','tests']);\r\nconst REUSED_SOURCES=['vendor/bitchord/LICENSE','vendor/bitchord/NOTICE','vendor/butterchurn/butterchurn-2.6.7.min.js','vendor/butterchurn/presets-2.4.7.min.js','vendor/butterchurn/LICENSE.engine','vendor/butterchurn/LICENSE.presets','vendor/butterchurn/README.md'];\r\n\r\nexport async function sourceOffer(root='.'){\r\n const paths=(await readdir(root)).filter(offered);\r\n async function walk(folder){\r\n  for(const entry of await readdir(join(root,folder),{withFileTypes:true})){\r\n   if(entry.isSymbolicLink())continue;\r\n   const path=`${folder}/${entry.name}`;\r\n   if(entry.isDirectory()){if(!entry.name.startsWith('.')&&!SKIP_DIRECTORIES.has(entry.name))await walk(path);}\r\n   else if(entry.isFile()&&offered(path))paths.push(path);\r\n  }\r\n }\r\n for(const folder of ROOTS)await walk(folder);\r\n paths.push(...REUSED_SOURCES);\r\n paths.push('vendor/shaders/runtime.mjs','vendor/shaders/LICENSE');\r\n const sources={};\r\n for(const path of paths.sort()){sources[path]=await readFile(join(root,path),'utf8');if(PRIVATE_KEY.test(sources[path]))throw new Error(`The source offer would publish a private key: ${path}`);}\r\n return sources;\r\n}\r\n","scripts/stage-grimoire-review.mjs":"// A separate static review site: no production routes, backend bindings or cron.\r\nimport {cp,mkdir,writeFile} from 'node:fs/promises';\r\nconst target='artifacts/grimoire-review';\r\nawait mkdir(target,{recursive:true});\r\nawait cp('dist/client',target,{recursive:true,filter:p=>!/[\\\\/]world[\\\\/]v1(?:[\\\\/]|$)/.test(p)});\r\nawait cp('standalone/grimoire-lab.html',`${target}/index.html`);\r\nawait writeFile(`${target}/robots.txt`,'User-agent: *\\nDisallow: /\\n');\r\nawait writeFile(`${target}/_headers`,'/*\\n  X-Robots-Tag: noindex, nofollow\\n  Cache-Control: no-store\\n');\r\nconsole.log('Separate grimoire review staged:',target);\r\n","scripts/vault-audio-preview.cjs":"// Browser check for page music: node scripts/vault-audio-preview.cjs  (serves the worktree on :8910, drives Chromium)\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=8910;\r\nconst types={'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.m4a':'audio/mp4','.mp3':'audio/mpeg','.html':'text/html'};\r\nconst html=`<!doctype html><html><body><div id=\"portalHome\" hidden></div><button id=\"b\">tap</button><script type=\"module\">\r\nimport {mountPageMusic} from '/audio/page-music.mjs';import {physicalSound} from '/audio/physical-sound.mjs';\r\nphysicalSound.mount();window.physicalSound=physicalSound;window.route='';window.myr5Routes={current:()=>window.route};window.m=mountPageMusic();window.ready=true;\r\n</script></body></html>`;\r\nconst server=http.createServer(async(req,res)=>{const u=new URL(req.url,'http://l');if(u.pathname==='/'){res.setHeader('Content-Type','text/html');return res.end(html);}\r\n try{const f=path.join(root,decodeURIComponent(u.pathname));if(!f.startsWith(root))throw 0;const b=await fs.readFile(f);res.setHeader('Content-Type',types[path.extname(f)]||'application/octet-stream');res.end(b);}catch{res.statusCode=404;res.end('no');}});\r\nasync function main(){\r\n await new Promise(r=>server.listen(PORT,r));\r\n const {chromium}=require('playwright');const browser=await chromium.launch({args:['--autoplay-policy=no-user-gesture-required']});\r\n const page=await browser.newPage();const errors=[];page.on('pageerror',e=>errors.push(e.message));page.on('console',m=>{if(m.type()==='error'||m.type()==='warning')errors.push(m.text());});\r\n await page.goto(`http://localhost:${PORT}/`);await page.waitForFunction(()=>window.ready);\r\n const out={};const wait=ms=>page.waitForTimeout(ms);\r\n out.beforeGesture=await page.evaluate(()=>m.state());\r\n await page.click('#b');await wait(2500);\r\n out.afterGesture=await page.evaluate(()=>m.state());\r\n out.fadeLog=await page.evaluate(()=>m.log.filter(l=>l.what==='fade-in').map(l=>({to:l.v1,dur:+(l.t1-l.t0).toFixed(3)})));\r\n out.bedLog=await page.evaluate(()=>m.log.filter(l=>l.what==='bed').map(l=>({dur:+(l.t1-l.t0).toFixed(3)})));\r\n // decoded length must equal the manifest frames (priming/padding trimmed by the decoder)\r\n out.frames=await page.evaluate(async()=>{const man=await (await fetch('/audio/music/manifest.json')).json();const ctx=physicalSound.context,r={};for(const [n,t] of Object.entries(man.tracks)){const b=await ctx.decodeAudioData(await (await fetch(t.url)).arrayBuffer());r[n]={decoded:b.length,expected:t.frames,sr:b.sampleRate};}return r;});\r\n // grimoire theme: 35 % over 5 s\r\n await page.evaluate(()=>{document.getElementById('portalHome').hidden=false;window.dispatchEvent(new CustomEvent('myr5:route'));});await wait(1500);\r\n out.portalFade=await page.evaluate(()=>({now:m.current(),fades:m.log.filter(l=>l.what==='fade-in').map(l=>({to:l.v1,dur:+(l.t1-l.t0).toFixed(3)})).pop()}));\r\n // same track across routes does not restart\r\n const before=await page.evaluate(()=>m.log.length);await page.evaluate(()=>{window.route='settings';window.dispatchEvent(new CustomEvent('myr5:route'));});await wait(300);\r\n out.noRestart=await page.evaluate(n=>m.log.length===n,before);\r\n // classroom: cut and resume\r\n await page.evaluate(()=>{window.route='scoreboard';document.getElementById('portalHome').hidden=true;window.dispatchEvent(new CustomEvent('myr5:route'));});await wait(1500);\r\n const cut=await page.evaluate(()=>{window.dispatchEvent(new CustomEvent('myr5:music-cut'));return m.state();});\r\n await wait(1200);\r\n const res=await page.evaluate(()=>{const off=m.state().cut.offset;window.dispatchEvent(new CustomEvent('myr5:music-resume'));return {off,playing:m.current()};});\r\n out.cutResume={track:cut.cut&&cut.cut.name,cutOffset:cut.cut&&+cut.cut.offset.toFixed(3),resumed:res.playing,stateAfterCut:cut.playing};\r\n // mute and hidden\r\n await page.evaluate(()=>physicalSound.setMuted(true));await wait(300);\r\n out.mutedMasterGain=await page.evaluate(()=>m.state().muted);\r\n out.errors=errors;console.log(JSON.stringify(out,null,1));await browser.close();server.close();\r\n}\r\nmain().catch(e=>{console.error(e);process.exit(1);});\r\n","scripts/vault-int-e2e.cjs":"// Integration e2e on the BUILT app: node scripts/vault-int-e2e.cjs  (serves dist/client, 375x812 touch). Shots -> .vault/shots/int-*.png\r\nconst {chromium}=require('../node_modules/playwright'),http=require('node:http'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');\r\nconst root=path.resolve(__dirname,'../dist/client'),shots=path.resolve(__dirname,'../.vault/shots/');\r\nconst T={'.html':'text/html','.js':'text/javascript','.mjs':'text/javascript','.css':'text/css','.json':'application/json','.webmanifest':'application/manifest+json','.png':'image/png','.webp':'image/webp','.m4a':'audio/mp4','.glb':'model/gltf-binary'};\r\nconst srv=http.createServer((req,res)=>{const p=new URL(req.url,'http://l').pathname;if(p.startsWith('/api/')){res.writeHead(p==='/api/auth/config'?200:401,{'Content-Type':'application/json'});return res.end(JSON.stringify(p==='/api/auth/config'?{enabled:false}:{error:'x'}));}\r\n const f=path.resolve(root,'.'+(p==='/'?'/pose.html':p));if(!f.startsWith(root)||!fs.existsSync(f)||fs.statSync(f).isDirectory()){res.writeHead(404);return res.end();}res.writeHead(200,{'Content-Type':T[path.extname(f)]||'application/octet-stream'});fs.createReadStream(f).pipe(res);});\r\n(async()=>{await new Promise(r=>srv.listen(0,'127.0.0.1',r));const base='http://127.0.0.1:'+srv.address().port;\r\n const b=await chromium.launch({args:['--use-gl=angle','--use-angle=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']});\r\n const ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:2,serviceWorkers:'block',reducedMotion:'reduce'});\r\n await ctx.addInitScript(()=>{Object.defineProperty(navigator,'standalone',{configurable:true,value:true});localStorage.myr5Debug='1';});\r\n await ctx.addInitScript(()=>{const get=Storage.prototype.getItem,d=new Date(),day=`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;Storage.prototype.getItem=function(k){return String(k).startsWith('myr5-how-to-play-day-v1/')?day:get.call(this,k);};});\r\n const seedP=await ctx.newPage();await seedP.goto(base+'/onboarding.html');\r\n const {completeCoach}=await import('../tests/onboarding-fixture.mjs');\r\n await seedP.evaluate(async intake=>{const {openLocalCoach}=await import('/local-coach-runtime.mjs');const repo=await openLocalCoach();await repo.forOwner(repo.guestOwnerId).saveSetup(intake,{startDay:'2026-09-21'});repo.close();},completeCoach());await seedP.close();\r\n const p=await ctx.newPage(),errs=[];p.on('console',m=>{if(m.type()==='error')errs.push(m.text());});p.on('pageerror',e=>errs.push('pageerror '+e.message));\r\n const shot=async n=>{await p.waitForTimeout(900);await p.screenshot({path:shots+'/int-'+n+'.png'});console.log('shot',n);};\r\n const step=async(n,f)=>{try{await f();console.log('OK  ',n);}catch(e){console.log('FAIL',n,e.message.split('\\n')[0]);}};\r\n await p.goto(base+'/pose.html');await p.waitForFunction(()=>window.myr5TestState?.phase==='idle'&&!!document.querySelector('.coach-dock'),null,{timeout:60000});\r\n await p.evaluate(()=>window.myr5Menus.portal());await p.waitForFunction(()=>document.getElementById('portalHome')?.hidden===false);await shot('01-portal');\r\n await step('music armed on first tap',async()=>{await p.mouse.click(5,5);await p.waitForTimeout(1500);console.log('music',JSON.stringify(await p.evaluate(()=>window.myr5Music?.state())));});\r\n await step('ice: real 10 taps',async()=>{await p.evaluate(()=>myr5Portal.board('ice'));await p.waitForTimeout(4000);await shot('02a-ice-board');\r\n  await p.evaluate(()=>[...document.querySelectorAll('button')].find(b=>/Got it/.test(b.textContent))?.click());await p.waitForTimeout(500);for(let i=0;i<10;i++){await p.touchscreen.tap(187,300);await p.waitForTimeout(450);}\r\n  await p.waitForSelector('#portalVaultDoor',{timeout:8000});await shot('02b-ice-door-poster');\r\n  console.log('ice secret',await p.evaluate(()=>!!JSON.parse(localStorage.getItem(Object.keys(localStorage).find(k=>k.startsWith('myr5-vault-v1/')))||'{}').secrets?.ice));});\r\n await step('other five secrets',async()=>{for(const id of ['pond','jelly','grass','wood','quilt'])await p.evaluate(id=>myr5Portal.secret(id),id);await p.waitForTimeout(500);\r\n  console.log('secrets',await p.evaluate(()=>Object.keys(JSON.parse(localStorage.getItem(Object.keys(localStorage).find(k=>k.startsWith('myr5-vault-v1/'))))?.secrets||{})));await shot('02c-door-poster');});\r\n await step('tap poster -> vault route',async()=>{await p.click('#portalVaultDoor');await p.waitForFunction(()=>document.getElementById('vaultPanel')?.open&&document.getElementById('vaultPanel')._vault,null,{timeout:30000});await p.waitForTimeout(1500);await shot('03-vault-door');\r\n  console.log(JSON.stringify(await p.evaluate(()=>{const i=document.getElementById('vaultPanel')._vault.info();return {head:i.view.head,lines:i.view.lines,foot:i.view.foot};})));});\r\n const info=()=>p.evaluate(()=>document.getElementById('vaultPanel')._vault.info()),P=(u,v)=>p.evaluate(([u,v])=>document.getElementById('vaultPanel')._vault.project(u,v),[u,v]);\r\n const drag=async(pts,hold)=>{const a=await P(...pts[0]);await p.mouse.move(a.x,a.y);await p.mouse.down();for(const q of pts.slice(1)){const c=await P(...q);await p.mouse.move(c.x,c.y,{steps:3});}if(!hold)await p.mouse.up();};\r\n const ring=(c,R,a0,a1,n)=>Array.from({length:n+1},(_,i)=>{const a=a0+(a1-a0)*i/n;return [c[0]+Math.sin(a)*R,c[1]-Math.cos(a)*R];});\r\n await step('knob detent',async()=>{const a=(await info()).view.id;await drag(ring([.5,.455],.07,0,Math.PI/6+.12,4));const c=(await info()).view.id;assert.notEqual(a,c);console.log(a,'->',c);await shot('04-knob');});\r\n await step('flywheel opens',async()=>{await drag(ring([.5,.72],.12,0,Math.PI*2.05,40));await p.waitForTimeout(2500);assert.equal((await info()).open,1);await shot('05-open');});\r\n await step('usb port -> hall',async()=>{const sc=await p.evaluate(()=>document.getElementById('vaultPanel')._vault.projectWorld(0,0,-.2));await p.mouse.click(sc.x,sc.y);await p.waitForFunction(()=>window.myr5Hall,null,{timeout:60000});await p.waitForTimeout(4000);await shot('06-hall');});\r\n await step('statue 1 card',async()=>{await p.evaluate(()=>myr5Hall.go(6*1-4.8));await p.waitForTimeout(6000);console.log(JSON.stringify(await p.evaluate(()=>myr5Hall.debug())));await shot('07-statue1-card');});\r\n await step('earn + sweep',async()=>{const id=await p.evaluate(async()=>{const g=await import('/modules/vault/vault-goals.mjs');return g.GOALS[0].id;});await p.evaluate(id=>myr5Vault.earn(id),id);await p.waitForTimeout(300);await shot('08a-sweep');await p.waitForTimeout(2000);await shot('08-earned');});\r\n await step('open pack -> holographic drop pod',async()=>{await p.waitForSelector('[data-pack]',{timeout:5000});await p.click('[data-pack]');await p.waitForSelector('dialog.drop-pod[data-tier=secret]',{timeout:8000});await shot('09a-pod-sky');await p.waitForTimeout(7000);await shot('09b-pod-landed');\r\n  await p.click('[data-hit]');await p.waitForTimeout(4000);await shot('09c-pod-reveal');console.log('reveal',(await p.textContent('.drop-pod-card')).replace(/\\s+/g,' ').slice(0,200));await p.click('[data-exit]');await p.waitForTimeout(800);await shot('09d-card-after');});\r\n await step('hall end + secrets',async()=>{await p.evaluate(()=>myr5Hall.jump(6*50));await p.waitForTimeout(2500);await shot('10-hall-end');for(const id of ['wood','quilt','ice'])await p.evaluate(id=>myr5Portal.secret(id),id);await p.waitForTimeout(500);});\r\n console.log('hall-end counter',await p.evaluate(()=>JSON.stringify(myr5Vault.state?.())));\r\n console.log(JSON.stringify(errs));console.log('console errors',JSON.stringify(errs.filter(e=>!/Failed to load resource/.test(e))),'(raw',errs.length,')');\r\n await b.close();srv.close();})();\r\n","scripts/vault-l1-preview.cjs":"// Vault L1 (pond secret) review harness: node scripts/vault-l1-preview.cjs  (serves :8902) then drives it with Playwright\r\n// when run with --shots (writes .vault/shots/l1-*.png). Debug hooks: pond.forceBig(), pond.debug().\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),port=Number(process.env.VAULT_L1_PORT)||8902;\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><style>html,body{margin:0;height:100%;background:#06110f;overflow:hidden}#board{position:absolute;inset:0 15px 100px 15px;touch-action:none}</style><script>\r\n// virtual clock: rAF callbacks only run inside advance(ms) so slow software-GL screenshots cannot skew the hold timing\r\n(()=>{let V=performance.now(),q=[];performance.now=()=>V;window.requestAnimationFrame=cb=>{q.push(cb);return q.length;};window.cancelAnimationFrame=()=>{};\r\nwindow.advance=ms=>{const end=V+ms;while(V<end){V+=1000/30;const c=q;q=[];for(const f of c)f(V);}};})();\r\n</script><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><div id=\"board\"></div><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';import {pond} from '/modules/portal/portal-board-pond.mjs';\r\nwindow.errors=[];window.secrets=0;window.addEventListener('error',e=>errors.push(e.message));window.addEventListener('myr5:portal-secret',e=>{secrets++;window.secretBoard=e.detail.board;});\r\nconst host=document.querySelector('#board');window.pond=pond;\r\ntry{window.board=await createGlbBoard(host,{effect:pond});\r\nconst rel=e=>{const r=host.getBoundingClientRect();return [e.clientX-r.left,e.clientY-r.top];};\r\nhost.onpointerdown=e=>{host.setPointerCapture(e.pointerId);board.press(e.pointerId,...rel(e));};host.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))board.press(e.pointerId,...rel(e));};host.onpointerup=host.onpointercancel=e=>board.release(e.pointerId);\r\nwindow.bigXY=()=>{const f=board.faceRect(),b=pond.debug().big;const r=host.getBoundingClientRect();return [r.left+f.left+b.x*f.width,r.top+f.top+b.y*f.width];};\r\nwindow.ready=true;}catch(e){errors.push(e.stack);}\r\n</script></body></html>`;\r\nconst server=http.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');const data=await fs.readFile(file);res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(port,'127.0.0.1',()=>console.log('Vault L1 preview http://127.0.0.1:'+port));\r\nif(process.argv.includes('--shots'))(async()=>{\r\n const {chromium}=require('playwright'),sleep=ms=>new Promise(r=>setTimeout(r,ms));await fs.mkdir(path.join(root,'.vault/shots'),{recursive:true});\r\n const b=await chromium.launch({channel:'msedge',headless:true,args:['--enable-webgl','--ignore-gpu-blocklist','--use-gl=angle','--use-angle=swiftshader']}),ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:1}),pg=await ctx.newPage();\r\n const logs=[];pg.on('console',m=>logs.push(m.type()+': '+m.text()));pg.on('pageerror',e=>logs.push('pageerror: '+e.message));\r\n await pg.goto('http://127.0.0.1:'+port+'/');await pg.waitForFunction(()=>window.ready||window.errors.length,null,{timeout:60000});\r\n const shot=n=>pg.screenshot({path:path.join(root,'.vault/shots/l1-'+n+'.png')}),dbg=()=>pg.evaluate(()=>{const d=pond.debug();return {sec:d.sec,phase:d.phase,e:d.an&&+d.an.e.toFixed(2),pitch:d.an&&+d.an.pitch.toFixed(2)};});\r\n const adv=async ms=>{for(;ms>0;ms-=200)await pg.evaluate(m=>advance(m),Math.min(200,ms));},start=async()=>{await pg.evaluate(()=>pond.forceBig());await adv(1800);};\r\n // 1) full hold: shots at 0 / .7 / 1.4 / 2.0 s of hold, then the splash\r\n await start();await shot('0-big');let [x,y]=await pg.evaluate(()=>bigXY());await pg.mouse.move(x,y);await pg.mouse.down();await adv(34);\r\n for(const [t,n] of [[0,'0'],[700,'0.7'],[700,'1.4'],[400,'1.8']]){await adv(t);await shot('hold-'+n);console.log('hold',n,await dbg());}\r\n for(const [t,n] of [[100,'splash-a'],[250,'splash-b'],[250,'splash-c'],[400,'splash-d'],[500,'splash-e']]){await adv(t);await shot(n);console.log(n,await dbg());}\r\n await pg.mouse.up();await adv(1500);console.log('after splash',await dbg(),'secrets',await pg.evaluate(()=>[secrets,secretBoard]));await shot('after');\r\n // 2) early lift -> bored\r\n await start();[x,y]=await pg.evaluate(()=>bigXY());await pg.mouse.move(x,y);await pg.mouse.down();await adv(1100);await pg.mouse.up();\r\n for(const [t,n] of [[150,'a'],[600,'b'],[700,'c'],[900,'d'],[1500,'e']]){await adv(t);await shot('bored-'+n);console.log('bored',n,await dbg());}\r\n await adv(3000);console.log('bored end',await dbg(),'secrets',await pg.evaluate(()=>secrets));\r\n // 3) press elsewhere while big: no secret\r\n await start();await pg.mouse.move(40,60);await pg.mouse.down();await adv(2300);console.log('miss',await dbg(),'secrets',await pg.evaluate(()=>secrets));await pg.mouse.up();\r\n console.log('errors',await pg.evaluate(()=>errors),logs.filter(l=>!/^log|^info|^debug/.test(l)));await b.close();server.close();\r\n})().catch(e=>{console.error(e);process.exit(1);});\r\n","scripts/vault-l2-preview.cjs":"// Lane L2 (wood secret) harness: node scripts/vault-l2-preview.cjs [shots]\r\n// Serves a page mounting the wood board on :8903; with `shots` it drives it with Playwright (375x812 touch) and\r\n// writes .vault/shots/l2-*.png, then exits.\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=Number(process.env.VAULT_L2_PORT)||8903;\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><style>\r\n*{box-sizing:border-box}html,body{margin:0;height:100%;background:#140c06;overflow:hidden}#board{position:absolute;left:15px;right:15px;top:0;bottom:103px;touch-action:none}\r\n</style><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><div id=\"board\"></div><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';import {wood} from '/modules/portal/portal-board-wood.mjs';\r\nwindow.errors=[];window.secrets=[];window.addEventListener('error',e=>errors.push(e.message));window.addEventListener('myr5:portal-secret',e=>secrets.push(e.detail));\r\nconst host=document.querySelector('#board');\r\ntry{window.wood=wood;window.board=await createGlbBoard(host,{effect:wood});board.setTint('#d9a15a');window.ready=true;}catch(e){errors.push(e.stack);}\r\nhost.onpointerdown=e=>{host.setPointerCapture(e.pointerId);board.press(e.pointerId,e.clientX,e.clientY);};\r\nhost.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))board.press(e.pointerId,e.clientX,e.clientY);};\r\nhost.onpointerup=host.onpointercancel=e=>{window.claimed=(board.claims||wood.claims)(e.pointerId);board.release(e.pointerId);};\r\n</script></body></html>`;\r\nconst types={'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css'};\r\nconst server=http.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}\r\n try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');const data=await fs.readFile(file);res.setHeader('Content-Type',types[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}});\r\nserver.listen(PORT,'127.0.0.1',()=>{console.log('Wood secret preview http://127.0.0.1:'+PORT);if(process.argv[2]==='shots')shots().then(()=>process.exit(0),e=>{console.error(e);process.exit(1);});});\r\n\r\nasync function shots(){\r\n const {chromium}=require('playwright'),browser=await chromium.launch({args:['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']});\r\n const page=await (await browser.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:1})).newPage(),logs=[];\r\n page.on('console',m=>{if(['error','warning'].includes(m.type()))logs.push(m.type()+': '+m.text());});\r\n await page.goto('http://127.0.0.1:'+PORT+'/');await page.waitForFunction('window.ready||window.errors.length',null,{timeout:30000});\r\n console.log('errors',await page.evaluate('errors'));\r\n const snap=n=>page.screenshot({path:path.join(root,'.vault/shots/l2-'+n+'.png')}),wait=ms=>page.waitForTimeout(ms);\r\n // face quadrant: top-left area; scrub in x. Mouse = pointer events, same path as touch.\r\n // Scrub through the board API exactly as portal.mjs does (board.press with client coords): legs of 26 px, ~330 ms apart.\r\n await page.evaluate(()=>{window.scrubTo=async(legs,{x0=60,y0=170,leg=26,gap=200}={})=>{const sleep=ms=>new Promise(r=>setTimeout(r,ms));\r\n  board.press(1,x0,y0);for(let k=0;k<legs;k++){for(let j=1;j<=2;j++){board.press(1,x0+(k%2?leg-leg/2*j:leg/2*j),y0+(k%3)*4);await sleep(gap/2);}if(window.stopWhenFire&&myr5Wood.state().stage==='fire')break;}};});\r\n await page.evaluate('scrubTo(6)');await snap('1-heat-a');console.log('rs',JSON.stringify(await page.evaluate('myr5Wood.state().rs.claimId')));await page.evaluate(()=>{window.stopWhenFire=true;return scrubTo(10);});await snap('1-heat-b');await page.evaluate('myr5Wood.stage||0');if((await page.evaluate('myr5Wood.state().stage'))!=='fire'){console.log('software GL too slow for a real 8-reversal scrub; using the debug hook for fire');await page.evaluate('myr5Wood.fire()');}await wait(300);\r\n console.log('heat',JSON.stringify(await page.evaluate('myr5Wood.state().heat')));\r\n console.log('claimed during scrub',await page.evaluate('myr5Wood.state().rs.claimId'));\r\n await page.evaluate('board.release(1)');console.log('claims after lift',await page.evaluate('(board.claims||wood.claims)(1)'),await page.evaluate('myr5Wood.state().stage'));\r\n for(const [n,p] of [['2-fire-start',.25],['3-fire',.6],['4-fire-full',1]]){await page.evaluate(`myr5Wood.pose('fire',${p})`);await wait(n==='4-fire-full'?3500:2500);await snap(n);}\r\n await page.mouse.click(260,520);await wait(500);await page.evaluate('myr5Wood.adv(200)');await wait(1500);await snap('5-steam');\r\n // Software GL runs ~3 fps, so freeze time (debug hook) to photograph each stage.\r\n for(const [n,st,p] of [['6-ash','ash',1],['7-crumble-a','crumble',.3],['8-crumble-b','crumble',.55],['9-crumble-c','crumble',.8],['10-gone','crumble',1]]){await page.evaluate(`myr5Wood.pose('${st}',${p})`);await wait(2200);await snap(n);}\r\n console.log('secrets',JSON.stringify(await page.evaluate('secrets')),'errors',JSON.stringify(await page.evaluate('errors')),'console',JSON.stringify(logs.slice(0,8)));\r\n await browser.close();\r\n}\r\n","scripts/vault-l3-preview.cjs":"// L3 Crystal ice secret preview: node scripts/vault-l3-preview.cjs\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..');\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>L3 Ice secret</title><style>\r\n*{box-sizing:border-box}body{margin:0;background:#000}#board{width:375px;height:812px;position:relative;touch-action:none}\r\n</style></head><body><div id=\"board\"></div><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';\r\nimport {ice} from '/modules/portal/portal-board-ice.mjs';\r\nconst host=document.querySelector('#board');\r\n// manual clock (software GL frames take ~300 ms, which would break a 600 ms tap chain): time only moves when skip(ms) says so; rAF timestamps follow it\r\nlet vt=1000;performance.now=()=>vt;window.skip=ms=>{vt+=ms;};const raf=window.requestAnimationFrame.bind(window);window.requestAnimationFrame=cb=>raf(()=>cb(vt));\r\nwindow.errors=[];window.secrets=0;window.addEventListener('error',e=>errors.push(e.message));\r\nwindow.addEventListener('myr5:portal-secret',()=>window.secrets++);\r\ntry{\r\n const board=window.board=await createGlbBoard(host,{effect:ice});\r\n host.style.background=board.background; // the real portal host paints the board background; a black host is just the harness\r\n host.onpointerdown=e=>{board.press(e.pointerId,e.clientX,e.clientY);window.downClaim=ice.claims(e.pointerId);};\r\n host.onpointermove=e=>{if(e.buttons)board.press(e.pointerId,e.clientX,e.clientY);};\r\n host.onpointerup=host.onpointercancel=e=>{window.lastClaim=ice.claims(e.pointerId);board.release(e.pointerId);};\r\n window.tap=async(x,y,hold=60)=>{const o={pointerId:7,clientX:x,clientY:y,bubbles:true,buttons:1};host.dispatchEvent(new PointerEvent('pointerdown',o));skip(hold);await new Promise(r=>setTimeout(r,350));host.dispatchEvent(new PointerEvent('pointerup',{...o,buttons:0}));};\r\n const pts=[[190,560],[110,430],[270,480],[150,650],[250,650],[120,540],[280,600],[190,450],[200,700],[190,580]];\r\n window.chain=async n=>{window.claimLog=[];for(let i=0;i<n;i++){await tap(...pts[i]);claimLog.push(window.lastClaim);skip(100);await new Promise(r=>setTimeout(r,350));}}; // software GL: each frame blocks ~300 ms, so taps are naturally ~400 ms apart\r\n window.ready=true;\r\n}catch(e){errors.push(e.stack);}\r\n</script></body></html>`;\r\nhttp.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');const data=await fs.readFile(file);res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css','.html':'text/html; charset=utf-8'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(Number(process.env.MYR5_L3_PORT)||8904,'127.0.0.1',function(){console.log('L3 Ice preview: http://127.0.0.1:'+this.address().port);});\r\n","scripts/vault-l3-shots.cjs":"// node scripts/vault-l3-shots.cjs  (preview must be running on 8904): taps the crystal 10 times and screenshots each stage.\r\nconst {chromium}=require('playwright');\r\n(async()=>{\r\n const b=await chromium.launch(),ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true}),pg=await ctx.newPage();\r\n const logs=[];pg.on('console',m=>/error|warn/i.test(m.type())&&logs.push(m.text()));pg.on('pageerror',e=>logs.push(String(e)));\r\n await pg.goto('http://127.0.0.1:'+(process.env.MYR5_L3_PORT||8904)+'/');await pg.waitForFunction('window.ready||window.errors.length',null,{timeout:30000});\r\n console.log('errors',await pg.evaluate('errors'));\r\n const shot=n=>pg.screenshot({path:`.vault/shots/l3-${n}.png`});\r\n for(let i=0;i<14;i++){await pg.evaluate('tap(190,560)');await pg.evaluate('skip(900)');} // warm the lazy crack-frame cache so the real run is not jank-bound\r\n await shot('01-initial');\r\n await pg.evaluate('chain(3)');await pg.evaluate('skip(400)');await pg.waitForTimeout(500);await shot('02-after-3-taps');await pg.evaluate('skip(2500)').then(()=>pg.waitForTimeout(700));\r\n await pg.evaluate('chain(9)');await pg.evaluate('skip(400)');await pg.waitForTimeout(500);await shot('03-after-9-taps');console.log('claims 1-9',await pg.evaluate('claimLog'));await pg.evaluate('skip(2500)').then(()=>pg.waitForTimeout(700));\r\n await pg.evaluate('chain(10)');\r\n for(const n of ['04a-shatter-0','04b-shatter-1','04c-shatter-2']){await pg.evaluate('skip(110)');await pg.waitForTimeout(700);await shot(n);}\r\n await pg.evaluate('skip(1500)');await pg.waitForTimeout(900);await shot('05-empty-face');\r\n console.log('secrets',await pg.evaluate('secrets'));\r\n await pg.evaluate('board.heal()');await pg.waitForTimeout(300);await shot('06-healed');\r\n console.log('console problems',logs);await b.close();\r\n})();\r\n","scripts/vault-l4-preview.cjs":"// Quilt vault-secret review harness: node scripts/vault-l4-preview.cjs [--shoot]  (port 8904; / = bare board, /portal = full portal)\r\n// --shoot drives real two-finger touches with Playwright and saves .vault/shots/l4-*.png\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),port=Number(process.env.VAULT_L4_PORT)||8904;\r\nconst map='<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script>';\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><style>*{box-sizing:border-box}body{margin:0;background:#17111e}#board{width:100vw;height:100vh;position:relative;touch-action:none}</style>${map}</head><body><div id=\"board\"></div><script type=\"module\">\r\nimport {createQuiltBoardGL,QUILT_SECRET} from '/modules/portal/portal-board.mjs';\r\nconst raf=requestAnimationFrame.bind(window);window.requestAnimationFrame=cb=>window.__slow?setTimeout(()=>raf(cb),120):raf(cb);QUILT_SECRET.ms2=120000;QUILT_SECRET.ms1=60000;window.errors=[];window.secrets=0;addEventListener('error',e=>errors.push(e.message));addEventListener('myr5:portal-secret',e=>secrets++);\r\nconst host=document.querySelector('#board');\r\ntry{const b=window.board=await createQuiltBoardGL(host);window.claimed={};\r\nhost.onpointerdown=e=>{host.setPointerCapture(e.pointerId);b.press(e.pointerId,e.clientX,e.clientY);};host.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))b.press(e.pointerId,e.clientX,e.clientY);};host.onpointerup=host.onpointercancel=e=>{b.release(e.pointerId);claimed[e.pointerId]=b.claims(e.pointerId);};\r\nwindow.ready=true;}catch(e){errors.push(e.stack);window.ready=true;}\r\n</script></body></html>`;\r\nconst portalHtml=`<!doctype html><style>body{margin:0}</style><button id=\"background\">Coach</button><nav class=\"coach-dock\"></nav>${map}`;\r\nconst srv=http.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'||url.pathname==='/portal'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(url.pathname==='/'?html:portalHtml);return;}try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');let data=await fs.readFile(file);if(file.endsWith('.ts'))data=(await require('esbuild').transform(data.toString(),{loader:'ts'})).code;res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.png':'image/png','.ts':'text/javascript','.css':'text/css'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(port,'127.0.0.1',async()=>{\r\n console.log('Quilt preview http://127.0.0.1:'+port);\r\n if(!process.argv.includes('--shoot'))return;\r\n const {chromium}=require('playwright'),b=await chromium.launch({args:['--use-gl=swiftshader','--enable-unsafe-swiftshader']});\r\n await fs.mkdir(path.join(root,'.vault/shots'),{recursive:true});\r\n const open=async(url,wait)=>{const ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:1}),p=await ctx.newPage();p.on('console',m=>{if(/error|warn/i.test(m.type()))console.log('console:',m.text());});p.on('pageerror',e=>console.log('pageerror',e.message));await p.goto(url);await p.waitForFunction(wait,null,{timeout:60000});return [ctx,p];};\r\n // ---- bare board, real two-finger touches\r\n let [ctx,p]=await open('http://127.0.0.1:'+port+'/',()=>window.ready);\r\n const shot=n=>p.screenshot({path:path.join(root,'.vault/shots/l4-'+n+'.png')}),sleep=ms=>p.waitForTimeout(ms),cdp=await ctx.newCDPSession(p);\r\n const touch=(type,pts)=>cdp.send('Input.dispatchTouchEvent',{type,touchPoints:pts});\r\n const face=await p.evaluate(()=>board.quiltRect()),at=(fx,fy)=>[Math.round(face.left+face.width*fx),Math.round(face.top+face.height*fy)];\r\n const state=()=>p.evaluate(()=>({phase:board.secretDebug.state().phase,p:+board.secretDebug.state().p.toFixed(2),vis:board.secretDebug.vis().map(v=>+v.toFixed(2)),secrets,errors,claimed}));\r\n const pts=(a,dx,dy)=>a.map(([fx,fy],i)=>({x:at(fx,fy)[0]+dx,y:at(fx,fy)[1]+dy,id:i+1}));\r\n const drag=async(a,dx,dy,steps=8,hold=false)=>{await touch('touchStart',pts(a,0,0));for(let i=1;i<=steps;i++){await touch('touchMove',pts(a,dx*i/steps,dy*i/steps));await sleep(30);}if(!hold)await touch('touchEnd',[]);};\r\n const S1=[[.25,.85],[.75,.85]],S2=[[.3,.1],[.3,.35]];\r\n console.log('flat',JSON.stringify(await state()));await shot('0-flat');\r\n await drag(S1,0,-face.height*.28,8,true);await sleep(600);await shot('1a-step1-early');console.log('early1',JSON.stringify(await state()));\r\n await touch('touchEnd',[]);await sleep(900);console.log('fell back',JSON.stringify(await state()));\r\n await drag(S1,0,-face.height*.455,10,true);await sleep(600);await shot('1-step1-middrag');console.log('mid1',JSON.stringify(await state()));\r\n await touch('touchEnd',[]);await sleep(900);await shot('2-step1-done');console.log('done1',JSON.stringify(await state()));\r\n await drag(S2,face.width*.3,0,8,true);await sleep(600);await shot('3a-step2-early');\r\n await touch('touchEnd',[]);await sleep(900);console.log('fell back 2',JSON.stringify(await state()));\r\n await drag(S2,face.width*.52,0,10,true);await sleep(600);await shot('3-step2-middrag');console.log('mid2',JSON.stringify(await state()));\r\n await p.evaluate(()=>{window.__slow=true;});await touch('touchEnd',[]);await sleep(500);await shot('4-step2-done');console.log('done2',JSON.stringify(await state()));\r\n await shot('5-slide-off-a');await shot('5-slide-off-b');await p.evaluate(()=>{window.__slow=false;});await sleep(1200);await shot('5-slide-off-end');console.log('slid',JSON.stringify(await state()));\r\n await p.evaluate(()=>board.heal());await sleep(300);await shot('6-healed');console.log('healed',JSON.stringify(await state()));\r\n await ctx.close();\r\n\r\n // ---- fold angles via the debug hook (real reducer): step 1 and 2 held at 30, 90, 150 degrees\r\n [ctx,p]=await open('http://127.0.0.1:'+port+'/',()=>window.ready);\r\n await p.evaluate(()=>{});const hold=async(step,deg,name)=>{await p.evaluate(([s,d])=>board.secretDebug.fold(s,d/180,false),[step,deg]);await sleep(900);await p.screenshot({path:path.join(root,'.vault/shots/l4-'+name+'.png')});await p.evaluate(()=>board.secretDebug.cancel());};\r\n for(const d of [30,90,120,150]){await hold(1,d,'a1-step1-'+d);await sleep(700);}\r\n await p.evaluate(()=>board.secretDebug.fold(1,1,true));await sleep(1200);\r\n for(const d of [30,90,120,150]){await hold(2,d,'a2-step2-'+d);await sleep(700);}\r\n await p.evaluate(()=>{window.__slow=true;board.secretDebug.fold(2,1,true);});for(let k=0;k<6;k++){await sleep(350);console.log('bundle',k,JSON.stringify(await p.evaluate(()=>board.secretDebug.vis())));await p.screenshot({path:path.join(root,'.vault/shots/l4-a3-bundle'+k+'.png')});}\r\n await ctx.close();\r\n // ---- full portal: secret -> door poster\r\n [ctx,p]=await open('http://127.0.0.1:'+port+'/portal',()=>true);\r\n await p.evaluate(async()=>{window.__went=[];window.myr5Routes={go:id=>window.__went.push(id)};const {openQuiltPortal}=await import('/modules/portal/portal-entry.mjs');window.portal=await openQuiltPortal();window.portal.show();});\r\n await p.waitForFunction(()=>document.getElementById('portalHome')?.hidden===false,null,{timeout:30000});\r\n await p.waitForFunction(()=>window.portal.current().secretDebug,null,{timeout:60000});await sleep(1500);\r\n const cdp2=await ctx.newCDPSession(p),t2=(type,pts)=>cdp2.send('Input.dispatchTouchEvent',{type,touchPoints:pts});\r\n const f2=await p.evaluate(()=>window.portal.current().quiltRect()),a2=(fx,fy)=>[Math.round(f2.left+f2.width*fx),Math.round(f2.top+f2.height*fy)];\r\n const mv=async(a,dx,dy)=>{const P=k=>a.map(([fx,fy],i)=>({x:a2(fx,fy)[0]+dx*k,y:a2(fx,fy)[1]+dy*k,id:i+1}));await t2('touchStart',P(0));for(let k=1;k<=8;k++){await t2('touchMove',P(k/8));await sleep(30);}await t2('touchEnd',[]);};\r\n await mv(S1,0,-f2.height*.4);await sleep(1200);await mv(S2,f2.width*.5,0);\r\n await p.waitForSelector('#portalVaultDoor',{timeout:15000});await sleep(900);await p.screenshot({path:path.join(root,'.vault/shots/l4-7-door-poster.png')});\r\n console.log('portal',JSON.stringify(await p.evaluate(()=>({door:!!document.getElementById('portalVaultDoor'),store:Object.keys(localStorage).filter(k=>k.startsWith('myr5-vault-v1/')).map(k=>JSON.parse(localStorage[k]).secrets)}))));\r\n await b.close();srv.close();\r\n});\r\n","scripts/vault-l5-preview.cjs":"// Vault L5 (grass alien/UFO secret) review harness.\r\n//   node scripts/vault-l5-preview.cjs          serve http://127.0.0.1:8905 (open it, drag from the purple cap to the ship)\r\n//   node scripts/vault-l5-preview.cjs shoot    serve + drive it with Playwright, shots -> .vault/shots/l5-*.png\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=Number(process.env.VAULT_L5_PORT)||8905;\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Grass secret</title><style>\r\n*{box-sizing:border-box}html,body{margin:0;height:100%;background:#0b150a;overflow:hidden}#host{position:fixed;left:15px;right:15px;top:70px;bottom:103px;touch-action:none}#host canvas{width:100%;height:100%}</style>\r\n<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><div id=\"host\"></div><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';import {grass,SECRET} from '/modules/portal/portal-board-grass.mjs';\r\nwindow.SECRET=SECRET;window.errors=[];window.secretFired=0;window.addEventListener('error',e=>errors.push(e.message));window.addEventListener('myr5:portal-secret',e=>{secretFired++;window.secretDetail=e.detail;});\r\nconst host=document.querySelector('#host');\r\ntry{window.board=await createGlbBoard(host,{effect:grass});window.ready=true;\r\nhost.onpointerdown=e=>{host.setPointerCapture(e.pointerId);board.press(e.pointerId,e.clientX,e.clientY);};\r\nhost.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))board.press(e.pointerId,e.clientX,e.clientY);};\r\nhost.onpointerup=host.onpointercancel=e=>{window.lastClaim=window.myr5GrassSecret.claims(e.pointerId);board.release(e.pointerId);};\r\n}catch(e){errors.push(e.stack);window.ready=true;}\r\n</script></body></html>`;\r\nconst mime={'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css','.html':'text/html; charset=utf-8'};\r\nconst server=http.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}\r\n try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');res.setHeader('Content-Type',mime[path.extname(file)]||'application/octet-stream');res.end(await fs.readFile(file));}catch{res.writeHead(404);res.end();}});\r\nserver.listen(PORT,'127.0.0.1',()=>{console.log('Grass secret preview http://127.0.0.1:'+PORT);if(process.argv[2]==='shoot')shoot().then(()=>process.exit(0),e=>{console.error(e);process.exit(1);});});\r\n\r\nasync function shoot(){\r\n const {chromium}=require('playwright'),out=path.join(root,'.vault','shots');await fs.mkdir(out,{recursive:true});\r\n const browser=await chromium.launch({args:['--use-angle=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']});\r\n const ctx=await browser.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:2}),page=await ctx.newPage();\r\n const logs=[];page.on('console',m=>{if(/error|warn/i.test(m.type()))logs.push(m.text());});page.on('pageerror',e=>logs.push(String(e)));\r\n await page.goto(`http://127.0.0.1:${PORT}/`);await page.waitForFunction('window.ready',null,{timeout:60000});\r\n const wait=ms=>page.waitForTimeout(ms),shot=async(n,clip)=>page.screenshot({path:path.join(out,`l5-${n}.png`),...(clip?{clip}:{})}),state=()=>page.evaluate('window.myr5GrassSecret&&window.myr5GrassSecret.state()');\r\n const f=await page.evaluate('(()=>{const r=board.faceRect();return {l:r.left,t:r.top,w:r.width,h:r.height}})()');\r\n const P=(u,v)=>[f.l+u*f.w,f.t+v*f.h],start=P(.1,.085),ship=P(.9,.915);\r\n await wait(800);await shot('01-idle');\r\n await shot('02-idle-alien-zoom',{x:start[0]-45,y:start[1]-45,width:90,height:90});await shot('03-idle-ufo-zoom',{x:ship[0]-60,y:ship[1]-60,width:110,height:110});\r\n // drag along the straight line in ~14 px strokes (a real finger), pausing at fractions\r\n const lerp=k=>[start[0]+(ship[0]-start[0])*k,start[1]+(ship[1]-start[1])*k],len=Math.hypot(ship[0]-start[0],ship[1]-start[1]);\r\n async function dragTo(k0,k1,ms=16){const n=Math.ceil(len*(k1-k0)/14);for(let i=1;i<=n;i++){const p=lerp(k0+(k1-k0)*i/n);await page.mouse.move(p[0],p[1]);await wait(ms);}}\r\n await page.mouse.move(...start);await page.mouse.down();await dragTo(0,.02);await page.evaluate('SECRET.gapMs=30000');await dragTo(.02,.4,45);await wait(150);await shot('04-following-line');\r\n await dragTo(.4,.55,45);await page.evaluate('SECRET.gapMs=3000');await wait(100);\r\n await page.mouse.up();const claim1=await page.evaluate('window.lastClaim');await wait(1500);const st1=await state();await shot('05-waiting-at-gap');\r\n await wait(2300);const st2=await state();await shot('06-after-3s-reset');\r\n console.log('claim at lift:',claim1,'| gap state phase/pos/len:',st1.phase,st1.pos.toFixed(2),st1.len.toFixed(2),'| after 3s:',st2.phase,'resets',st2.resets);\r\n // full line\r\n await page.mouse.move(...start);await page.mouse.down();await dragTo(0,1,40);await wait(120);\r\n await page.mouse.up();\r\n const tt=Date.now();await page.waitForFunction(\"window.myr5GrassSecret.state().phase==='done'\",null,{timeout:40000});console.log('real run reached done in',Date.now()-tt,'ms after lift; fired',await page.evaluate('window.secretFired'));\r\n const poses=[['board',.5],['fly',.3],['fly',.7],['crack',.45],['crack',1],['split',.3],['split',.6],['done',0]];\r\n for(const [ph,k] of poses){await page.evaluate(`myr5GrassSecret.freeze('${ph}',${k})`);await wait(500);await shot(`07-${ph}-${k}`);}\r\n await wait(400);const fired=await page.evaluate('[window.secretFired,window.secretDetail]'),errs=await page.evaluate('window.errors');\r\n console.log('portal-secret fired:',JSON.stringify(fired),'| page errors:',JSON.stringify(errs),'| console:',JSON.stringify(logs.slice(0,5)));\r\n await browser.close();server.close();\r\n}\r\n","scripts/vault-l6-preview.cjs":"// Jelly vault-secret review harness: node scripts/vault-l6-preview.cjs [--shoot]  (port 8906)\r\n// --shoot drives a held finger with Playwright and saves .vault/shots/l6-*.png\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),port=Number(process.env.VAULT_L6_PORT)||8906;\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><style>*{box-sizing:border-box}body{margin:0;background:#0d160a}#board{width:100vw;height:100vh;position:relative;touch-action:none}</style><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><div id=\"board\"></div><script type=\"module\">\r\nimport {createGlbBoard} from '/modules/portal/portal-board-glb.mjs';import {jelly} from '/modules/portal/portal-board-jelly.mjs';\r\nwindow.errors=[];window.secrets=0;addEventListener('error',e=>errors.push(e.message));addEventListener('myr5:portal-secret',e=>secrets++);\r\nconst host=document.querySelector('#board');\r\ntry{window.board=await createGlbBoard(host,{effect:jelly});window.jelly=jelly;\r\nhost.onpointerdown=e=>{host.setPointerCapture(e.pointerId);board.press(e.pointerId,e.clientX,e.clientY);};host.onpointermove=e=>{if(host.hasPointerCapture(e.pointerId))board.press(e.pointerId,e.clientX,e.clientY);};host.onpointerup=host.onpointercancel=e=>{window.lastClaim=jelly.claims(e.pointerId);board.release(e.pointerId);};\r\nwindow.ready=true;}catch(e){errors.push(e.stack);window.ready=true;}\r\n</script></body></html>`;\r\nconst srv=http.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}try{const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');const data=await fs.readFile(file);res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(port,'127.0.0.1',async()=>{\r\n console.log('Jelly preview http://127.0.0.1:'+port);\r\n if(!process.argv.includes('--shoot'))return;\r\n const {chromium}=require('playwright'),b=await chromium.launch({args:['--use-gl=swiftshader','--enable-unsafe-swiftshader']}),ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:1}),p=await ctx.newPage();\r\n p.on('console',m=>{if(/error|warn/i.test(m.type()))console.log('console:',m.text());});\r\n await p.addInitScript(()=>{const r=performance.now.bind(performance);performance.now=()=>window.__pin??r();const raf=requestAnimationFrame.bind(window);window.requestAnimationFrame=cb=>raf(()=>cb(performance.now()));});\r\n await p.goto('http://127.0.0.1:'+port+'/');await p.waitForFunction(()=>window.ready,null,{timeout:60000});\r\n const shot=n=>p.screenshot({path:path.join(root,'.vault/shots/l6-'+n+'.png')}),sleep=ms=>p.waitForTimeout(ms);\r\n await fs.mkdir(path.join(root,'.vault/shots'),{recursive:true});\r\n const cdp=await ctx.newCDPSession(p);\r\n const touch=(type,pts)=>cdp.send('Input.dispatchTouchEvent',{type,touchPoints:pts});\r\n const at={x:80,y:650,id:1};\r\n // fake clock: the effect reads performance.now(), so pin it to press+target and let swiftshader render at its own pace\r\n const pin=async(ms)=>p.evaluate(m=>{window.__pin=window.__t0+m;board.resume();},ms);\r\n await touch('touchStart',[at]);await p.evaluate(()=>{window.__t0=performance.now();});\r\n const at_=async(sec,n)=>{await pin(sec*1000);await sleep(1500);await shot(n);};\r\n await at_(2,'2s');await at_(4,'4s');await at_(5,'5s');await at_(6,'6s');await at_(6.4,'boil');await at_(6.7,'door-rising');await at_(7.1,'door-up');await at_(9,'door-calm');\r\n console.log('state',JSON.stringify(await p.evaluate(()=>({secrets,errors}))));\r\n await touch('touchEnd',[]);\r\n // lift early: hold 3 s, lift, look 0.3 s later and 1.5 s later\r\n await p.evaluate(()=>{window.secrets=0;});\r\n await p.reload();await p.waitForFunction(()=>window.ready);\r\n await touch('touchStart',[at]);await p.evaluate(()=>{window.__t0=performance.now();});\r\n await pin(6600);await sleep(1500);await shot('lift-before');\r\n await touch('touchEnd',[]);await p.evaluate(()=>{window.__t0=window.__pin;});await pin(450);await sleep(1500);await shot('lift-subsiding');await pin(900);await sleep(1500);await shot('lift-after');\r\n console.log('early-lift',JSON.stringify(await p.evaluate(()=>({secrets,errors}))));\r\n await b.close();srv.close();\r\n});\r\n","scripts/vault-l7-poster.cjs":"// Renders pod/worlds/vault/door-poster.webp (a still of the closed door, <=120 KB): node scripts/vault-l7-poster.cjs\r\nconst {chromium}=require('../node_modules/playwright'),{spawn}=require('node:child_process'),fs=require('node:fs'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=process.env.MYR5_VAULT_PORT||8907;\r\n(async()=>{\r\n const srv=spawn(process.execPath,[path.join(__dirname,'vault-l7-preview.cjs')],{env:{...process.env,MYR5_VAULT_PORT:PORT},stdio:'ignore'});await new Promise(r=>setTimeout(r,800));\r\n const b=await chromium.launch({args:['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']}),page=await(await b.newContext({viewport:{width:375,height:812},deviceScaleFactor:2})).newPage();\r\n try{\r\n  await page.goto('http://127.0.0.1:'+PORT+'/');await page.waitForFunction(()=>window.ready,null,{timeout:90000});await page.waitForTimeout(2500);\r\n  const tl=await page.evaluate(()=>vault.projectWorld(-.67,.67,.03)),br=await page.evaluate(()=>vault.projectWorld(.67,-.67,.03));\r\n  const png=await page.screenshot({clip:{x:tl.x,y:tl.y,width:br.x-tl.x,height:br.y-tl.y}});\r\n  const b64=await page.evaluate(async d=>{const bm=await createImageBitmap(await (await fetch('data:image/png;base64,'+d)).blob()),c=document.createElement('canvas');c.width=bm.width;c.height=bm.height;c.getContext('2d').drawImage(bm,0,0);\r\n   for(const q of [.82,.7,.55]){const blob=await new Promise(r=>c.toBlob(r,'image/webp',q));if(blob.size<=115000||q===.55){const u=new Uint8Array(await blob.arrayBuffer());let s='';for(let i=0;i<u.length;i+=8192)s+=String.fromCharCode(...u.subarray(i,i+8192));return btoa(s);}}},png.toString('base64'));\r\n  const out=path.join(root,'pod/worlds/vault/door-poster.webp');fs.mkdirSync(path.dirname(out),{recursive:true});fs.writeFileSync(out,Buffer.from(b64,'base64'));console.log(out,fs.statSync(out).size,'bytes');\r\n }finally{await b.close();srv.kill();}\r\n})().catch(e=>{console.error(e);process.exit(1)});\r\n","scripts/vault-l7-preview.cjs":"// Local review harness for the vault door: node scripts/vault-l7-preview.cjs  (MYR5_VAULT_PORT, default 8907)\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..');\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Vault door</title><style>html,body{margin:0;height:100%;background:#171020;overflow:hidden}#host{position:fixed;inset:0}</style>\r\n<script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script></head><body><div id=\"host\"></div><script type=\"module\">\r\nimport {mountVaultDoor} from '/modules/vault/vault-door.mjs';\r\nwindow.errors=[];addEventListener('error',e=>errors.push(e.message));addEventListener('unhandledrejection',e=>errors.push(String(e.reason)));\r\nconst GOALS=[\r\n {id:'still-pond',tier:'rare',title:'Still Water',clue:'The koi that fills the pond is not afraid of you.',test:s=>!!s.earned?.['still-pond'],progress:()=>null},\r\n {id:'fire-starter',tier:'rare',title:'Tinder',clue:'Where the wood is dry, rub it the wrong way until it sulks.',test:s=>!!s.earned?.['fire-starter']},\r\n {id:'shares',tier:'rare',title:'Show And Tell',clue:'Pass the secret to five friends on five different days.',test:()=>false,progress:()=>({have:3,need:5})},\r\n {id:'door-open',tier:'rare',title:'Open Sesame',clue:'Turn the great wheel once, and the wall will answer.',test:()=>true},\r\n {id:'all-six',tier:'legendary',title:'Six Of Six',clue:'Every board keeps a secret. Learn them all.',test:()=>false,progress:()=>({have:2,need:6})},\r\n];\r\nconst state={earned:{}};\r\nif(location.search.includes('real')){await (await import('/modules/vault/vault-door.mjs')).openVault();window.ready=true;}else{\r\nwindow.vault=await mountVaultDoor(document.getElementById('host'),{goals:GOALS,read:()=>state,onEnter:()=>{window.entered=(window.entered||0)+1}});\r\nwindow.ready=true;}\r\n</script></body></html>`;\r\nhttp.createServer(async(req,res)=>{const url=new URL(req.url,'http://local');if(url.pathname==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(html);return;}try{if(url.pathname==='/modules/vault/vault-hall.mjs'&&!require('node:fs').existsSync(path.join(root,'modules/vault/vault-hall.mjs'))){res.setHeader('Content-Type','text/javascript');res.end('export const enterHall=()=>{window.hallEntered=1};');return;}const file=path.resolve(root,'.'+decodeURIComponent(url.pathname));if(!file.startsWith(root+path.sep))throw Error('outside root');let data=await fs.readFile(file);if(file.endsWith('.ts')){data=(await require('esbuild').transform(data.toString(),{loader:'ts',format:'esm'})).code;res.setHeader('Content-Type','text/javascript');res.end(data);return;}res.setHeader('Content-Type',({'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.png':'image/png','.css':'text/css'})[path.extname(file)]||'application/octet-stream');res.end(data);}catch{res.writeHead(404);res.end();}}).listen(Number(process.env.MYR5_VAULT_PORT)||8907,'127.0.0.1',function(){console.log('Vault door preview http://127.0.0.1:'+this.address().port);});\r\n","scripts/vault-l7-shots.cjs":"// Drives the vault-door preview at 375x812 touch and asserts the L7 behaviours: node scripts/vault-l7-shots.cjs\r\n// Starts scripts/vault-l7-preview.cjs itself. Shots -> .vault/shots/L7-*.png, poster -> pod/worlds/vault/door-poster.webp\r\nconst {chromium}=require('../node_modules/playwright'),{spawn}=require('node:child_process'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');\r\nconst root=path.resolve(__dirname,'..'),PORT=process.env.MYR5_VAULT_PORT||8907;\r\n(async()=>{\r\n const srv=spawn(process.execPath,[path.join(__dirname,'vault-l7-preview.cjs')],{env:{...process.env,MYR5_VAULT_PORT:PORT},stdio:'ignore'});await new Promise(r=>setTimeout(r,800));\r\n fs.mkdirSync(path.join(root,'.vault/shots'),{recursive:true});\r\n const b=await chromium.launch({args:['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']}),ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:2}),page=await ctx.newPage();\r\n const logs=[];page.on('console',m=>{if(m.type()==='error'&&!/404/.test(m.text()))logs.push(m.text())});page.on('pageerror',e=>logs.push('pageerror '+e.message));\r\n try{\r\n  await page.goto('http://127.0.0.1:'+PORT+'/');await page.waitForFunction(()=>window.ready||window.errors.length,null,{timeout:90000});\r\n  assert.deepEqual(await page.evaluate(()=>window.errors),[]);\r\n  const info=()=>page.evaluate(()=>vault.info()),P=(u,v)=>page.evaluate(([u,v])=>vault.project(u,v),[u,v]);\r\n  const shot=async n=>{await page.waitForTimeout(1800);await page.screenshot({path:path.join(root,'.vault/shots/L7-'+n+'.png')});};\r\n  const drag=async(pts,hold=false)=>{const a=await P(...pts[0]);await page.mouse.move(a.x,a.y);await page.mouse.down();for(const q of pts.slice(1)){const p=await P(...q);await page.mouse.move(p.x,p.y,{steps:3});}if(!hold)await page.mouse.up();};\r\n  const ring=(c,R,a0,a1,n)=>Array.from({length:n+1},(_,i)=>{const a=a0+(a1-a0)*i/n;return [c[0]+Math.sin(a)*R,c[1]-Math.cos(a)*R];});\r\n  await shot('01-idle');\r\n  let i0=await info();assert.equal(i0.shadows,false,'shadows off when idle');assert.equal(i0.awake,false,'no free-running RAF');\r\n  // finger drives gears / pipes / lights / weld\r\n  const wp=Array.from({length:15},(_,i)=>[.12+i*.025,.82+Math.sin(i/2)*.05]);\r\n  const a=await P(...wp[0]);await page.mouse.move(a.x,a.y);await page.mouse.down();for(const q of wp.slice(1)){const p=await P(...q);await page.mouse.move(p.x,p.y,{steps:2});}\r\n  await page.waitForTimeout(150);await page.screenshot({path:path.join(root,'.vault/shots/L7-02-finger.png')});assert.equal((await info()).shadows,true,'shadows on while touched');await page.mouse.up();\r\n  // knob: +30deg steps goals forward in order, wraps; -30deg steps back\r\n  const K=[.5,.455],ids=[];const idx=async()=>(await info()).view.id;\r\n  ids.push(await idx());\r\n  await drag(ring(K,.07,0,Math.PI/6+.12,4));ids.push(await idx());await shot('03-crt-a');\r\n  await drag(ring(K,.07,0,Math.PI/6+.12,4));ids.push(await idx());await shot('04-crt-b');\r\n  await drag(ring(K,.07,0,-(Math.PI/6+.12),4));ids.push(await idx());\r\n  console.log('goal order',ids.join(' > '));\r\n  assert.deepEqual(ids,['still-pond','fire-starter','shares','fire-starter']);\r\n  await drag(ring(K,.07,0,-(Math.PI/6+.12),4));await drag(ring(K,.07,0,-(Math.PI/6+.12),4));assert.equal(await idx(),'all-six','wraps backwards');await shot('05-crt-c');\r\n  // flywheel: half turn held, then released (springs back), then a full turn opens\r\n  const F=[.5,.72];\r\n  await drag(ring(F,.12,0,Math.PI,18),true);await page.waitForTimeout(300);await page.screenshot({path:path.join(root,'.vault/shots/L7-06-fly-half.png')});\r\n  const half=await info();assert.ok(half.progress>.45&&half.progress<.55,'half turn '+half.progress);assert.equal(half.open,0);await page.mouse.up();\r\n  await page.waitForTimeout(2500);assert.ok((await info()).progress<.05,'unfinished turn springs back');\r\n  await drag(ring(F,.12,0,Math.PI*2.05,40));await page.waitForTimeout(1500);await shot('07-open');\r\n  const op=await info();assert.equal(op.open,1,'door fully open');assert.equal(op.awake,true,'hint ring pulses for a few seconds');await page.waitForTimeout(7000);const op2=await info();assert.equal(op2.shadows,false,'asleep after the pulse');assert.equal(op2.awake,false);\r\n  const sc=await page.evaluate(()=>vault.projectWorld(0,0,-.2));await page.mouse.click(sc.x,sc.y);await page.waitForTimeout(450);\r\n  await page.screenshot({path:path.join(root,'.vault/shots/L7-08-mid-dolly.png')});await page.waitForTimeout(1800);\r\n  await page.screenshot({path:path.join(root,'.vault/shots/L7-09-purple-fill.png')});\r\n  assert.equal(await page.evaluate(()=>window.entered),1,'port tap calls enter');\r\n  assert.equal((await info()).awake,false,'dolly ends asleep');\r\n  // the real route: openVault() with the real vault-goals / vault-store\r\n  await page.goto('http://127.0.0.1:'+PORT+'/?real=1');await page.waitForFunction(()=>window.ready||window.errors.length,null,{timeout:90000});\r\n  assert.deepEqual(await page.evaluate(()=>window.errors),[]);const rv=await page.evaluate(()=>document.getElementById('vaultPanel')._vault.info().view);console.log('real route CRT',JSON.stringify(rv));assert.ok(/^GOAL 01\\//.test(rv.head));\r\n  assert.deepEqual(logs,[]);console.log('L7 browser checks passed');\r\n }finally{await b.close();srv.kill();}\r\n})().catch(e=>{console.error(e);process.exit(1)});\r\n","scripts/vault-l8-preview.cjs":"// Hall review harness: node scripts/vault-l8-preview.cjs (port 8908). Uses the real vault modules; open /?debug for myr5Hall hooks.\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=Number(process.env.VAULT_L8_PORT)||8908;\r\nconst html=`<!doctype html><meta name=viewport content=\"width=device-width,initial-scale=1\"><body style=\"margin:0;background:#000\"><script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\",\"three/addons/loaders/GLTFLoader.js\":\"/vendor/three/GLTFLoader.js\",\"three/addons/libs/meshopt_decoder.module.js\":\"/vendor/three/meshopt_decoder.module.js\"}}</script><script type=module>window.errors=[];addEventListener('error',e=>errors.push(e.message));\r\nawait import('/modules/vault/vault-store.mjs');const m=await import('/modules/vault/vault-hall.mjs');window.hall=m;window.go=()=>m.enterHall();document.body.insertAdjacentHTML('beforeend','<button id=go style=\"position:fixed;top:40%;left:30%;z-index:99\">enter</button>');go.onclick=()=>m.enterHall();</script>`;\r\nconst mime={'.mjs':'text/javascript','.js':'text/javascript','.json':'application/json','.glb':'model/gltf-binary','.webp':'image/webp','.css':'text/css','.ts':'text/javascript'};\r\nconst server=http.createServer(async(req,res)=>{const u=new URL(req.url,'http://l');if(u.pathname==='/'){res.setHeader('Content-Type','text/html');return res.end(html);}\r\n try{const f=path.resolve(root,'.'+decodeURIComponent(u.pathname));if(!f.startsWith(root+path.sep))throw 0;let d=await fs.readFile(f);if(f.endsWith('.ts'))d=require('node:module').stripTypeScriptTypes(d.toString());res.setHeader('Content-Type',mime[path.extname(f)]||'application/octet-stream');res.end(d);}\r\n catch{res.writeHead(404);res.end();}});\r\nmodule.exports={server,PORT};\r\nif(require.main===module)server.listen(PORT,'127.0.0.1',()=>console.log('hall preview http://127.0.0.1:'+PORT));\r\n","scripts/vault-l8-shots.cjs":"const {server,PORT}=require('./vault-l8-preview.cjs'),{chromium}=require('playwright'),out=require('node:path').resolve(__dirname,'../.vault/shots/');\r\n(async()=>{await new Promise(r=>server.listen(PORT,'127.0.0.1',r));const b=await chromium.launch({args:['--use-gl=angle','--use-angle=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist']});\r\nconst p=await (await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:2})).newPage();const errs=[];p.on('console',m=>m.type()==='error'&&errs.push(m.text()));p.on('pageerror',e=>errs.push(String(e)));\r\nconst shot=n=>p.screenshot({path:out+'/l8-'+n+'.png'}),dbg=()=>p.evaluate(()=>myr5Hall.debug()),wait=ms=>p.waitForTimeout(ms),S=k=>6*(k+1)-4.8;\r\nawait p.goto(`http://127.0.0.1:${PORT}/?debug`);await p.waitForFunction(()=>window.go,null,{timeout:15000}).catch(()=>console.log(errs));\r\nconst ids=await p.evaluate(async()=>{const g=await import('/modules/vault/vault-goals.mjs');return g.GOALS.map(x=>x.id);});console.log('goals',ids.length,ids.at(-1));\r\nconsole.log('pre-errs',JSON.stringify(errs));await p.evaluate(()=>{myr5Vault.reset();window.go();});\r\nawait p.waitForFunction(()=>window.myr5Hall,null,{timeout:30000}).catch(()=>console.log('nohall',JSON.stringify(errs)));for(const [n,t] of [['25',.8],['50',1.6],['75',2.4]]){await p.evaluate(t=>myr5Hall.at(t),t);await wait(500);await shot('1-transition-'+n);}await p.evaluate(()=>myr5Hall.at(-1));await wait(3500);await shot('2-start');\r\nawait p.evaluate(s=>myr5Hall.go(s),S(0));await wait(5000);console.log(JSON.stringify(await dbg()));await shot('3-locked-card');\r\nawait p.evaluate(id=>myr5Vault.earn(id),ids[0]);await wait(250);await shot('4a-earn-sweep');await wait(1800);await shot('4-earned-card');\r\nconsole.log('pack btn',await p.evaluate(()=>!!document.querySelector('[data-pack]')));\r\nif(await p.$('[data-pack]')){await p.click('[data-pack]');await wait(1200);await shot('4b-pack-opened');}\r\nfor(const k of [1,2,3]){await p.evaluate(s=>myr5Hall.jump(s),S(k));await wait(1800);await shot('5-statue'+(k+1));}\r\nawait p.evaluate(()=>myr5Hall.jump(6*40));await wait(2500);console.log(JSON.stringify(await dbg()));await shot('6-far');\r\nconsole.log('errors',JSON.stringify(errs));await b.close();server.close();})();\r\n","scripts/vault-tub-preview.cjs":"// Tub flight endless review harness. node scripts/vault-tub-preview.cjs shoot  -> .vault/shots/tub-*.png\r\nconst http=require('node:http'),fs=require('node:fs/promises'),path=require('node:path');\r\nconst root=path.resolve(__dirname,'..'),PORT=Number(process.env.VAULT_TUB_PORT)||8921;\r\nconst html=`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><link rel=\"stylesheet\" href=\"/arcade/tub-flight/style.css\"><style>body{margin:0;padding:10px;background:#164743}</style></head><body><main id=\"g\"></main><script type=\"module\">\r\nimport {mountTubFlight} from '/arcade/tub-flight/game.mjs';window.errors=[];addEventListener('error',e=>errors.push(e.message));window.fuelCalls=0;\r\nwindow.h=mountTubFlight(document.getElementById('g'),{autoFuel:true,onComplete:()=>fuelCalls++});\r\nwindow.bot=true;const g=h.game;setInterval(()=>{if(!bot||g.phase!=='running')return;const p=g.pipes.find(p=>p.x+44>69);const t=p?p.gap+8:210;if(g.y>t&&g.velocity>0)g.flap();},8);\r\n</script></body></html>`;\r\nconst mime={'.mjs':'text/javascript','.css':'text/css'};\r\nconst server=http.createServer(async(req,res)=>{const u=new URL(req.url,'http://l');if(u.pathname==='/'){res.setHeader('Content-Type','text/html');res.end(html);return;}\r\n try{const f=path.resolve(root,'.'+u.pathname);if(!f.startsWith(root+path.sep))throw 0;res.setHeader('Content-Type',mime[path.extname(f)]||'application/octet-stream');res.end(await fs.readFile(f));}catch{res.writeHead(404);res.end();}});\r\nserver.listen(PORT,'127.0.0.1',()=>{if(process.argv[2]==='shoot')shoot().then(()=>process.exit(0),e=>{console.error(e);process.exit(1);});else console.log('http://127.0.0.1:'+PORT);});\r\nasync function shoot(){\r\n const {chromium}=require('playwright'),out=path.join(root,'.vault','shots');await fs.mkdir(out,{recursive:true});\r\n const b=await chromium.launch(),ctx=await b.newContext({viewport:{width:375,height:812},hasTouch:true,deviceScaleFactor:2}),page=await ctx.newPage();\r\n const logs=[];page.on('console',m=>{if(/error|warn/i.test(m.type()))logs.push(m.text());});page.on('pageerror',e=>logs.push(String(e)));\r\n await page.goto(`http://127.0.0.1:${PORT}/`);await page.waitForFunction('window.h');\r\n const shot=n=>page.screenshot({path:path.join(out,`tub-${n}.png`)}),sc=()=>page.evaluate('h.game.score');\r\n const until=n=>page.waitForFunction(`h.game.score>=${n}||h.game.phase==='crashed'`,null,{timeout:120000,polling:20});\r\n await page.evaluate('localStorage.clear()');await page.reload();await page.waitForFunction('window.h');\r\n await shot('1-start');\r\n await page.evaluate('h.game.flap();document.querySelector(\"canvas\").dispatchEvent(new Event(\"pointerdown\"))');\r\n await until(5);await page.evaluate('h.pause()');console.log('mid',await sc());await shot('2-mid5');await page.click('[data-pause]');\r\n await until(8);await page.waitForTimeout(300);console.log('fuel',await sc(),await page.evaluate('[fuelCalls,localStorage.getItem(\"mbs-fuel-flight-v1\"),h.game.phase]'));await shot('3-fuel-toast');\r\n await until(16);await page.evaluate('h.pause()');console.log('mid16',await sc(),await page.evaluate('h.game.pipes[0].half'));await shot('4-mid16');\r\n await page.click('[data-pause]');await page.evaluate('bot=false');await page.waitForFunction(\"h.game.phase==='crashed'\",null,{timeout:20000});await page.waitForTimeout(150);\r\n console.log('crash',await page.evaluate('[h.game.score,localStorage.getItem(\"mbs-tub-best-v1\"),localStorage.getItem(\"myr5-vault-pending\")]'));await shot('5-crash');\r\n await page.click('[data-retry]');await shot('6-retry');console.log('errors',logs,await page.evaluate('errors'));await b.close();}\r\n","scripts/worker-sprite-bake/bake_worker.py":"\"\"\"Append original GLB worker combat frames without changing the existing atlas cells.\r\n\r\nRun with Python 3.11 (Pillow, NumPy):\r\n  python scripts/worker-sprite-bake/bake_worker.py\r\nThe source pipeline is read only. Blender runs on a temporary copy of its baker.\r\n\"\"\"\r\nfrom __future__ import annotations\r\n\r\nimport importlib.util\r\nimport json\r\nimport os\r\nfrom pathlib import Path\r\nimport shutil\r\nimport subprocess\r\nimport tempfile\r\n\r\nimport numpy as np\r\nfrom PIL import Image\r\n\r\n\r\nROOT = Path(__file__).resolve().parents[2]\r\nATLAS = ROOT / \"modules/coach-world-rts/atlas\"\r\nSOURCE = Path(os.environ.get(\"MYR5_SPRITE_PIPELINE\", \"D:/myr5-work/sprite-pipeline\"))\r\nBLENDER = Path(os.environ.get(\"MYR5_BLENDER\", \"C:/Program Files/Blender Foundation/Blender 4.5/blender.exe\"))\r\nNEW = (\"attack\", \"hit\", \"death\")\r\nTICK = 42\r\nOUTLINE = (12, 8, 18)\r\nSPEC = (255, 250, 255)\r\n\r\n\r\ndef clips():\r\n    spec = importlib.util.spec_from_file_location(\"worker_source_clips\", SOURCE / \"clips_procedural.py\")\r\n    module = importlib.util.module_from_spec(spec)\r\n    spec.loader.exec_module(module)\r\n    return module.make_clips(\"biped\", 4.6)\r\n\r\n\r\ndef luminance(a):\r\n    return a[..., :3] @ np.array([.299, .587, .114])\r\n\r\n\r\ndef small(a, cell):\r\n    out = np.asarray(Image.fromarray(a.astype(np.uint8), \"RGBA\").resize(cell, Image.Resampling.BOX)).copy()\r\n    out[..., 3] = np.where(out[..., 3] >= 128, 255, 0)\r\n    return out\r\n\r\n\r\ndef outline(a):\r\n    opaque = a[..., 3] > 0\r\n    padded = np.pad(opaque, 1)\r\n    edge = np.zeros_like(opaque)\r\n    for dy in range(3):\r\n        for dx in range(3):\r\n            edge |= padded[dy:dy + opaque.shape[0], dx:dx + opaque.shape[1]]\r\n    a[edge & ~opaque] = (*OUTLINE, 255)\r\n    return a\r\n\r\n\r\ndef main():\r\n    base = json.loads((ATLAS / \"myr5.json\").read_text())\r\n    assert list(base[\"actions\"])[:3] == [\"idle\", \"walk\", \"work\"]\r\n    assert base[\"cell\"] == [28, 40] and base[\"maskDiv\"] == 4 and base[\"tickMs\"] == TICK\r\n    assert not any(name in base[\"actions\"] for name in NEW), \"Refusing to append twice\"\r\n    assert BLENDER.is_file() and (SOURCE / \"input/myr5.glb\").is_file()\r\n    definitions = clips()\r\n    cell = tuple(base[\"cell\"])\r\n    with tempfile.TemporaryDirectory(prefix=\"myr5-worker-bake-\") as directory:\r\n        stage = Path(directory)\r\n        shutil.copy2(SOURCE / \"clips_procedural.py\", stage / \"clips_procedural.py\")\r\n        baker = (SOURCE / \"bake_sprites.py\").read_text()\r\n        old = 'CL = make_clips(kind, U.get(\"H\", 3.0)) if ROSTER else CLIPS'\r\n        assert baker.count(old) == 1, \"Source baker changed; review the isolated patch\"\r\n        (stage / \"bake_sprites.py\").write_text(baker.replace(old, 'CL = make_clips(\"biped\", 4.6)'))\r\n        config = json.loads((SOURCE / \"units.json\").read_text())[\"myr5\"]\r\n        config.update(glb=str((SOURCE / \"input/myr5.glb\").resolve()), rawDir=str(stage), clipsOrder=list(NEW))\r\n        (stage / \"units.json\").write_text(json.dumps({\"myr5\": config}))\r\n        cmd = [str(BLENDER), \"-b\", \"-t\", \"2\", \"-P\", str(stage / \"bake_sprites.py\"), \"--\", str(stage / \"units.json\"), \"myr5\"]\r\n        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\r\n        if result.returncode:\r\n            print(result.stdout[-8000:])\r\n            raise RuntimeError(f\"Blender bake failed ({result.returncode})\")\r\n        raw = stage / \"raw-myr5\"\r\n        frames = [(name, i, direction) for name in NEW for i in range(len(definitions[name])) for direction in range(5)]\r\n        def read(tag):\r\n            return np.asarray(Image.open(raw / f\"{tag}.png\").convert(\"RGBA\"), dtype=np.float32)\r\n        source = {key: (read(f\"{key[0]}_{key[1]}_{key[2]}b\"), read(f\"{key[0]}_{key[1]}_{key[2]}f\")) for key in frames}\r\n        ratios = np.concatenate([(luminance(beauty) / np.maximum(luminance(flat), 1))[(beauty[..., 3] > 127) & (luminance(flat) > 14)] for beauty, flat in source.values()])\r\n        edges = np.percentile(ratios, [30, 60, 88, 97.5])\r\n        multipliers = np.array([.5, .75, 1, 1.25])\r\n        rendered = {}\r\n        for key, (beauty, flat) in source.items():\r\n            light = luminance(flat)\r\n            ratio = luminance(beauty) / np.maximum(light, 1)\r\n            step = np.where(light <= 14, 1, (ratio > edges[0]).astype(int) + (ratio > edges[1]) + (ratio > edges[2]))\r\n            step = np.where(light > 200, 3, step)\r\n            rgb = np.clip(255 * (flat[..., :3] / 255) ** .65 * multipliers[step][..., None], 0, 255)\r\n            rgb = np.where(((ratio > edges[3]) & (light > 14))[..., None], rgb * .25 + np.array(SPEC) * .75, rgb)\r\n            rendered[key] = outline(small(np.dstack([rgb, beauty[..., 3]]), cell))\r\n        pixels = np.concatenate([a[a[..., 3] > 0][:, :3] for a in rendered.values()])\r\n        palette = Image.fromarray(pixels[None]).quantize(33, Image.Quantize.MEDIANCUT, dither=Image.Dither.NONE)\r\n        colors = np.vstack([np.array(palette.getpalette()[:99], np.uint8).reshape(33, 3), SPEC, OUTLINE]).astype(np.int32)\r\n\r\n        original = Image.open(ATLAS / \"myr5.png\").convert(\"LA\")\r\n        original_mask = Image.open(ATLAS / \"myr5-mask.png\").convert(\"L\")\r\n        assert original.size == tuple(base[\"sheet\"])\r\n        assert original_mask.size == (original.width // 4, original.height // 4)\r\n        width = original.width + sum(len(definitions[name]) for name in NEW) * cell[0]\r\n        sheet = Image.new(\"LA\", (width, original.height))\r\n        mask = Image.new(\"L\", (width // 4, original.height // 4))\r\n        sheet.paste(original, (0, 0))\r\n        mask.paste(original_mask, (0, 0))\r\n        offset = original.width\r\n        for name in NEW:\r\n            sequence = definitions[name]\r\n            rects = []\r\n            for direction in range(5):\r\n                row = []\r\n                for i in range(len(sequence)):\r\n                    key = (name, i, direction)\r\n                    a = rendered[key]\r\n                    delta = a[..., :3].astype(np.int32)[..., None, :] - colors\r\n                    nearest = colors[(delta * delta).sum(-1).argmin(-1)]\r\n                    gray = np.rint(luminance(nearest)).astype(np.uint8)\r\n                    gray[a[..., 3] == 0] = 0\r\n                    x, y = offset + i * cell[0], direction * cell[1]\r\n                    sheet.paste(Image.fromarray(np.dstack([gray, a[..., 3]]), \"LA\"), (x, y))\r\n                    m = read(f\"{name}_{i}_{direction}m\")\r\n                    opaque = (m[..., 3] > 127).astype(np.float32)\r\n                    team = ((m[..., 0] > 127) & (m[..., 3] > 127)).astype(np.float32)\r\n                    down = lambda a: np.asarray(Image.fromarray(a).resize((cell[0] // 4, cell[1] // 4), Image.Resampling.BOX))\r\n                    coverage = down(opaque)\r\n                    share = np.where(coverage > 1e-3, down(team) / np.maximum(coverage, 1e-3), 0)\r\n                    mask.paste(Image.fromarray(np.rint(share * 255).astype(np.uint8), \"L\"), (x // 4, y // 4))\r\n                    row.append([x, y, *cell])\r\n                rects.append(row)\r\n            base[\"actions\"][name] = {\"frames\": len(sequence), \"ms\": [ticks * TICK for _, ticks in sequence], \"rects\": rects}\r\n            if name == \"attack\":\r\n                base[\"actions\"][name][\"strike\"] = 1\r\n            offset += len(sequence) * cell[0]\r\n        base[\"sheet\"] = [width, original.height]\r\n        sheet.save(ATLAS / \"myr5.png\")\r\n        mask.save(ATLAS / \"myr5-mask.png\")\r\n        (ATLAS / \"myr5.json\").write_text(json.dumps(base, separators=(\",\", \":\")) + \"\\n\")\r\n        print(f\"Appended {', '.join(NEW)} from original MYR5 GLB: {sheet.size}, {mask.size}\")\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n","scripts/workout-tracking-research.mjs":"// Read-only synthetic research probes. Does not change production modules.\r\nimport {MovementSession,features,MOVEMENTS} from '../movement-engine.mjs';\r\nimport {evaluateMovement} from '../movement-rules.mjs';\r\nimport {ENGINE,LIBRARY} from '../tests/pose-fixtures.mjs';\r\nimport {PoseClassifier} from '../pose-classifier.mjs';\r\nimport {convert} from './pose-samples.mjs';\r\nimport {syntheticRecording} from '../tests/pose-fixtures.mjs';\r\nimport {execFileSync} from 'node:child_process';\r\n\r\nconst clone=p=>p.map(v=>({...v}));\r\nfunction tree(){const p=ENGINE.standing();Object.assign(p[25],{x:.25,y:.60});return p;}\r\nfunction hold(mode,pose,{missing=null,from=2000,to=2400}={}){\r\n const session=new MovementSession(mode,{samples:null});\r\n for(let t=0;t<=5000;t+=50){let p=clone(pose());if(missing&&t>=from&&t<to)p=missing(p);session.update(p,t);}\r\n return {seconds:session.totalHold,continuous:session.hold,message:session.message};\r\n}\r\nconst obscure=p=>p.map((v,i)=>({...v,visibility:[11,12,13,14,15,16,23,24,25,26].includes(i)?0:v.visibility}));\r\nconst results={source:execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(),syntheticOnly:true,hold:[],cadence:[]};\r\nfor(const [mode,pose] of [['tree',tree],['high-plank',()=>LIBRARY.floor()]]){\r\n for(const [label,missing] of [['continuous',null],['missing-joints',obscure],['no-pose',()=>null]]){\r\n  results.hold.push({mode,scenario:label,...hold(mode,pose,{missing})});\r\n }\r\n}\r\nconst floor=LIBRARY.floor(),handsKnees=clone(floor);\r\nfor(const i of [25,26])Object.assign(handsKnees[i],{x:.5,y:.8});\r\nconst cropped=p=>p.map((v,i)=>({...v,...([25,26].includes(i)?{y:1.2,visibility:0}:{})}));\r\nresults.plankAmbiguity={visible:evaluateMovement(features(handsKnees),MOVEMENTS['high-plank']),cropped:evaluateMovement(features(cropped(handsKnees)),MOVEMENTS['high-plank'])};\r\nfunction count(fps,period,offset){\r\n const r=new MovementSession('squat',{samples:null}),a=ENGINE.standing(),b=ENGINE.squat(),n=10,end=3+n*period;\r\n for(let t=offset/fps;t<end+2;t+=1/fps){const k=t<3||t>=end?0:(1-Math.cos(2*Math.PI*(t-3)/period))/2;\r\n  r.update(a.map((v,i)=>({...v,x:v.x+(b[i].x-v.x)*k,y:v.y+(b[i].y-v.y)*k})),t*1000);\r\n }\r\n return r.count;\r\n}\r\nfor(const fps of [1.4,1.5,1.6,2,2.5,4,8,15,30])for(const period of [1,1.5,2,3,4]){\r\n const counts=Array.from({length:40},(_,i)=>count(fps,period,i/40));\r\n results.cadence.push({fps,secondsPerRep:period,expected:10,min:Math.min(...counts),max:Math.max(...counts),mean:counts.reduce((a,b)=>a+b,0)/counts.length,exact:counts.filter(n=>n===10).length,phases:40});\r\n}\r\nconst {samples}=convert(syntheticRecording()),knn=new PoseClassifier(samples),p=ENGINE.standing();\r\nconst gate=points=>features(points).visible(knn.need);\r\nconst crop=p.map((v,i)=>({...v,...([25,26].includes(i)?{y:1.2,visibility:0}:{})}));\r\nresults.knn={required:knn.need,standing:{gate:gate(p),votes:knn.classify(features(p).p)},kneesCropped:{gate:gate(crop),votes:knn.classify(features(crop).p)}};\r\nconsole.log(JSON.stringify(results,null,2));\r\n","scripts/world-interactions-preview.mjs":"import {createServer} from 'node:http';\r\nimport {readFile} from 'node:fs/promises';\r\nimport {resolve,extname,sep} from 'node:path';\r\nconst root=resolve('.'),port=Number(process.env.WORLD_PREVIEW_PORT)||5206;\r\nconst types={'.html':'text/html','.mjs':'text/javascript','.js':'text/javascript','.css':'text/css','.json':'application/json','.png':'image/png','.webp':'image/webp','.jpg':'image/jpeg','.glb':'model/gltf-binary','.gz':'application/gzip'};\r\nconst page='<!doctype html><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>MYR5 World interaction check</title><script type=\"module\">import {openWorld} from \"/modules/coach-world/view.mjs\";openWorld({onEnterWarRoom(){}});</script>';\r\ncreateServer(async(req,res)=>{\r\n try{\r\n  const url=new URL(req.url,'http://localhost');res.setHeader('Cache-Control','no-store');\r\n  if(url.pathname==='/'){res.setHeader('Content-Type','text/html');res.end(page);return;}\r\n  if(url.pathname.startsWith('/api/')){res.setHeader('Content-Type','application/json');res.end(JSON.stringify({enabled:false}));return;}\r\n  const file=resolve(root,'.'+decodeURIComponent(url.pathname==='/pose'?'/pose.html':url.pathname));\r\n  if(!file.startsWith(root+sep))throw Error('Outside preview root');\r\n  res.setHeader('Content-Type',types[extname(file)]||'application/octet-stream');res.end(await readFile(file));\r\n }catch{res.writeHead(404);res.end('Not found');}\r\n}).listen(port,'127.0.0.1',()=>console.log(`World interaction check: http://127.0.0.1:${port}`));\r\n","server/account-data-epochs.mjs":"// Primary-D1 foundation only. A route must supply an authenticated canonical\r\n// owner and its own owner-bound statements; this module performs no remote I/O.\r\nexport class AccountDataEpochError extends Error {\r\n constructor(code, message=code, options){super(message,options);this.name='AccountDataEpochError';this.code=code;}\r\n}\r\nconst fail=code=>{throw new AccountDataEpochError(code);};\r\nconst owner=value=>{if(typeof value!=='string'||!value||value.length>512||value.trim()!==value)fail('invalid-owner');return value;};\r\nconst safeEpoch=value=>{if(!Number.isSafeInteger(value)||value<1)fail('invalid-epoch');return value;};\r\nconst safeTime=value=>{if(!Number.isSafeInteger(value)||value<0)fail('invalid-time');return value;};\r\nconst provision=(database,ownerId,now)=>database.prepare('INSERT INTO account_data_epochs(owner_id,epoch,updated_at) VALUES(?,1,?) ON CONFLICT(owner_id) DO NOTHING').bind(ownerId,now);\r\n\r\n// One SQL statement gives a coherent snapshot when another deletion commits.\r\n// Missing rows mean untouched legacy epoch 1, never a deletion confirmation.\r\nexport async function inspectAccountDataEpoch(database,ownerId){\r\n ownerId=owner(ownerId);\r\n const row=await database.prepare('SELECT e.epoch, (SELECT MAX(d.deleted_epoch) FROM account_data_deletions d WHERE d.owner_id=e.owner_id) AS deleted_through_epoch FROM account_data_epochs e WHERE e.owner_id=?').bind(ownerId).first();\r\n if(!row)return {ownerId,currentDataEpoch:1,deletedThroughEpoch:0};\r\n const currentDataEpoch=safeEpoch(row.epoch),deletedThroughEpoch=row.deleted_through_epoch??0;\r\n if(!Number.isSafeInteger(deletedThroughEpoch)||deletedThroughEpoch<0||deletedThroughEpoch!==currentDataEpoch-1)fail('epoch-history-corrupt');\r\n return {ownerId,currentDataEpoch,deletedThroughEpoch};\r\n}\r\nexport async function readAccountDataEpoch(database,ownerId,now){\r\n ownerId=owner(ownerId);now=safeTime(now);\r\n await provision(database,ownerId,now).run();\r\n return inspectAccountDataEpoch(database,ownerId);\r\n}\r\n\r\n// Keep every primary data deletion in this same batch. A zero-row SELECT is\r\n// NOT a guard: the receipt's NOT NULL constraint aborts the entire transaction.\r\n// Receipts/epoch tombstones must never be included in the supplied deletions.\r\nexport async function deleteAccountDataAtEpoch(database,{ownerId,expectedDataEpoch,now,deletions}){\r\n ownerId=owner(ownerId);expectedDataEpoch=safeEpoch(expectedDataEpoch);now=safeTime(now);\r\n if(expectedDataEpoch===Number.MAX_SAFE_INTEGER)fail('epoch-overflow');\r\n if(!Array.isArray(deletions))fail('invalid-deletions');\r\n const observed=await inspectAccountDataEpoch(database,ownerId);\r\n if(expectedDataEpoch>observed.currentDataEpoch)fail('epoch-mismatch');\r\n if(expectedDataEpoch<observed.currentDataEpoch)return {...observed,alreadyDeleted:true};\r\n try{\r\n  await database.batch([\r\n   provision(database,ownerId,now),\r\n   database.prepare('INSERT INTO account_data_deletions(owner_id,deleted_epoch,deleted_at) VALUES(?,(SELECT epoch FROM account_data_epochs WHERE owner_id=? AND epoch=?),?)').bind(ownerId,ownerId,expectedDataEpoch,now),\r\n   database.prepare('UPDATE account_data_epochs SET epoch=epoch+1,updated_at=? WHERE owner_id=? AND epoch=?').bind(now,ownerId,expectedDataEpoch),\r\n   ...deletions,\r\n  ]);\r\n  return {ownerId,currentDataEpoch:expectedDataEpoch+1,deletedThroughEpoch:expectedDataEpoch,alreadyDeleted:false};\r\n }catch(error){\r\n  const current=await inspectAccountDataEpoch(database,ownerId);\r\n  if(current.currentDataEpoch>expectedDataEpoch)return {...current,alreadyDeleted:true};\r\n  if(current.currentDataEpoch<expectedDataEpoch)fail('epoch-mismatch');\r\n  throw error;\r\n }\r\n}\r\n","server/account-readiness.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {remoteReminders,reminderService} from './remote-reminders.mjs';\r\nimport {syncTrainingStatus} from './reminder-plan.mjs';\r\nconst healthKey='readiness:reminders';\r\nexport const trainingReadinessKey=owner=>'readiness:training:'+JSON.stringify(owner);\r\nconst valid=value=>value&&Number.isSafeInteger(value.startedAt)&&value.startedAt>=0&&typeof value.observationId==='string'&&['ready','unavailable'].includes(value.state);\r\nconst validPush=push=>push&&typeof push.configured==='boolean'&&typeof push.schedulerActive==='boolean'&&(push.publicKey===null||typeof push.publicKey==='string');\r\nasync function read(database,key){\r\n const row=await database.prepare('SELECT value FROM system WHERE key=?').bind(key).first();\r\n try{const value=JSON.parse(row?.value??'null');return valid(value)?value:null;}catch{return null;}\r\n}\r\n// Cache access is optional; only these cache keys are allowed to fail open.\r\nconst optionalRead=async(database,key)=>{try{return await read(database,key);}catch{return null;}};\r\nfunction write(database,key,value){\r\n return database.prepare(\"INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value WHERE CASE WHEN json_valid(system.value)=0 THEN 1 WHEN json_type(system.value,'$.startedAt')!='integer' OR json_type(system.value,'$.startedAt') IS NULL OR json_type(system.value,'$.observationId')!='text' OR json_type(system.value,'$.observationId') IS NULL OR json_extract(system.value,'$.startedAt')<0 OR coalesce(json_extract(system.value,'$.state') IN ('ready','unavailable'),0)=0 THEN 1 ELSE json_extract(system.value,'$.startedAt')<json_extract(excluded.value,'$.startedAt') OR (json_extract(system.value,'$.startedAt')=json_extract(excluded.value,'$.startedAt') AND json_extract(system.value,'$.observationId')<json_extract(excluded.value,'$.observationId')) END\").bind(key,JSON.stringify(value));\r\n}\r\nconst view=(value,now)=>value?{...value,state:now-value.startedAt>300000?'stale':value.state}:{state:'unknown',checkedAt:null};\r\nexport async function readAccountReadiness(database,owner,epoch,now=Date.now()){\r\n const [health,training]=await Promise.all([optionalRead(database,healthKey),optionalRead(database,trainingReadinessKey(owner))]);\r\n const safeHealth=health?{...health}:null;if(safeHealth&&!validPush(safeHealth.push))delete safeHealth.push;\r\n return {reminders:view(safeHealth,now),training:view(training?.dataEpoch===epoch?training:null,now)};\r\n}\r\nexport async function refreshReminderHealth(database,env,owner,{now=Date.now(),observationId=crypto.randomUUID()}={}){\r\n let value={startedAt:now,observationId,checkedAt:null,state:'unavailable'};\r\n try{\r\n  const push=await reminderService(env,owner,'/internal/status');if(!validPush(push))throw Error('Invalid reminder health.');\r\n  value={...value,state:'ready',checkedAt:Date.now(),push:{configured:push.configured,publicKey:push.publicKey,schedulerActive:push.schedulerActive}};\r\n }catch{\r\n  const previous=await optionalRead(database,healthKey);\r\n  if(validPush(previous?.push))value={...value,push:previous.push,lastSuccessAt:previous.state==='ready'?previous.checkedAt:previous.lastSuccessAt??null};\r\n }\r\n try{await write(database,healthKey,value).run();}catch{/* Health result remains usable even when its optional cache is unavailable. */}\r\n return value;\r\n}\r\nexport async function refreshTrainingReadiness(database,env,owner,proof,training,{now=Date.now(),observationId=crypto.randomUUID()}={}){\r\n const value={startedAt:now,observationId,checkedAt:null,dataEpoch:proof.currentDataEpoch,state:'unavailable'};\r\n try{\r\n  if(remoteReminders(env))await reminderService(env,owner,'/internal/training-status','POST',training,proof);\r\n  else await syncTrainingStatus(database,owner,training,now,{dataEpoch:proof.currentDataEpoch});\r\n  value.state='ready';value.checkedAt=Date.now();\r\n }catch{\r\n  const previous=await optionalRead(database,trainingReadinessKey(owner));\r\n  if(previous?.dataEpoch===proof.currentDataEpoch)value.lastSuccessAt=previous.state==='ready'?previous.checkedAt:previous.lastSuccessAt??null;\r\n }\r\n // Even a failure observation may never recreate a deleted user's cache.\r\n await epochFencedBatch(database,{ownerId:owner,expectedDataEpoch:proof.currentDataEpoch,now,statements:[write(database,trainingReadinessKey(owner),value)]});\r\n return value;\r\n}\r\nexport function scheduleAccountReadiness(ctx,database,env,owner,proof,training){\r\n if(typeof ctx?.waitUntil!=='function')return;\r\n const now=Date.now();\r\n if(remoteReminders(env))ctx.waitUntil(Promise.resolve().then(()=>refreshReminderHealth(database,env,owner,{now})).catch(()=>{}));\r\n ctx.waitUntil(Promise.resolve().then(()=>refreshTrainingReadiness(database,env,owner,proof,training,{now})).catch(()=>{}));\r\n}\r\n","server/armie-push.mjs":"// Turns a fired D12 Armie letter into a Web Push send, reusing the EXISTING\r\n// push infrastructure (sendPush in server/push.mjs) -- no changes to\r\n// push.mjs. The in-app inbox is the source of truth (D23); this call is only\r\n// the \"you have mail\" nudge, and its failure must never lose the letter (the\r\n// letter is already written to the inbox independently of whether this\r\n// succeeds -- see armie-letters-client.mjs).\r\nimport {sendPush} from './push.mjs';\r\nimport {pickArmieLetter} from '../armie-letters.mjs';\r\n\r\nexport function armiePushPayload(date, letterId) {\r\n const letter = pickArmieLetter(letterId, date);\r\n if (!letter) return null;\r\n return {title: letter.header, body: letter.lines.join(' '), tag: `myr5-armie-${date}-${letterId}`, url: '/pose.html?panel=armie', kind: 'myr5-armie-letter'};\r\n}\r\n\r\nexport async function sendArmieLetterPush(env, sub, date, letterId, options) {\r\n const payload = armiePushPayload(date, letterId);\r\n if (!payload) return null;\r\n return sendPush(env, sub, payload, options);\r\n}\r\n","server/armie-scheduler.mjs":"// D12/D23 catch-up sender: pushes any fired Armie letter to users who have a\r\n// push subscription but haven't opened the app (so nothing called\r\n// combatProgress for them today). Mirrors the shape of runReminders()\r\n// (server/push.mjs).\r\n//\r\n// Runs from server/worker.mjs scheduled() through runArmieLetterPushes (R8),\r\n// which dedups on drizzle/0021 armie_letter_deliveries(user_id, day, letter_id).\r\n// `alreadySent`/`markSent`/`send` stay injectable for tests/armie-scheduler.test.mjs.\r\nimport {dayAt} from '../combat.mjs';\r\nimport {evaluateStreakHistory} from '../streak-forgiveness.mjs';\r\nimport {sendArmieLetterPush} from './armie-push.mjs';\r\nimport {db} from './db.mjs';\r\n\r\nconst staleError = error => ['stale_delivery', 'target_epoch_mismatch'].includes(error?.code);\r\n\r\nexport async function runArmieLetterScheduler(env, database, {now = Date.now(), alreadySent, markSent, send = sendArmieLetterPush} = {}) {\r\n const day = dayAt(now);\r\n // evaluateStreakHistory fires nothing unless today is kept, so only today's users matter (this runs every minute).\r\n const users = (await database.prepare('SELECT DISTINCT user_id FROM login_days WHERE day=?').bind(day).all()).results;\r\n let sent = 0, failed = 0;\r\n for (const {user_id: user} of users) {\r\n  const subs = (await database.prepare('SELECT * FROM subscriptions WHERE user_id=?').bind(user).all()).results;\r\n  if (!subs.length) continue;\r\n  const epochRow = await database.prepare('SELECT epoch FROM account_data_epochs WHERE owner_id=?').bind(user).first();\r\n  const dataEpoch = epochRow?.epoch ?? 1;\r\n  const days = (await database.prepare('SELECT day FROM login_days WHERE user_id=? AND day<=?').bind(user, day).all()).results;\r\n  const history = evaluateStreakHistory(days.map(row => row.day), day);\r\n  for (const dayResult of history) {\r\n   for (const letterId of dayResult.lettersFired) {\r\n    if (await alreadySent(user, dayResult.date, letterId)) continue;\r\n    let delivered = false;\r\n    for (const s of subs) {\r\n     try {\r\n      await send(env, JSON.parse(s.data), dayResult.date, letterId, {ownerId: user, dataEpoch, subscriptionData: s.data, subscriptionCreatedAt: s.created_at});\r\n      delivered = true; sent++;\r\n     } catch (error) { if (!staleError(error)) failed++; }\r\n    }\r\n    if (delivered) await markSent(user, dayResult.date, letterId);\r\n   }\r\n  }\r\n }\r\n return {sent, failed};\r\n}\r\n\r\n// Claims (user, day, letter) before sending, so each letter pushes at most once per user, even across\r\n// overlapping cron runs. ponytail: a failed push is not retried; the in-app inbox still has the letter (D23).\r\nexport async function runArmieLetterPushes(env, {now = Date.now(), send} = {}) {\r\n if (!env.VAPID_PRIVATE_KEY) return {sent: 0, failed: 0, configured: false};\r\n const database = db(env);\r\n const claimedBefore = async (user, day, letterId) => !(await database.prepare('INSERT INTO armie_letter_deliveries(user_id,day,letter_id,sent_at) VALUES(?,?,?,?) ON CONFLICT DO NOTHING').bind(user, day, letterId, now).run()).meta.changes;\r\n return runArmieLetterScheduler(env, database, {now, alreadySent: claimedBefore, markSent: async () => {}, ...(send ? {send} : {})});\r\n}\r\n","server/auth.mjs":"import {verifyToken} from '@clerk/backend';\r\nimport {fail} from './domain.mjs';\r\nexport function authConfig(env){return {enabled:!!(env.CLERK_PUBLISHABLE_KEY&&env.CLERK_JWT_KEY&&env.CLERK_FRONTEND_URL&&env.CLERK_AUTHORIZED_PARTIES),publishableKey:env.CLERK_PUBLISHABLE_KEY||null,frontend:env.CLERK_FRONTEND_URL||null};}\r\nexport async function identity(request,env){\r\n  // A custom-domain Worker receives caller-controlled forwarded headers. When\r\n  // Clerk is configured, legacy identity requires an explicitly trusted proxy.\r\n  const trustLegacy=env.LOCAL_PREVIEW===true||env.TRUST_LEGACY_AUTH_HEADERS==='true'||!authConfig(env).enabled;\r\n  const legacy=trustLegacy?request.headers.get('oai-authenticated-user-id'):null;\r\n  if(!request.headers.has('Authorization'))return {legacy,clerk:null,email:request.headers.get('oai-authenticated-user-email')||'Your account'};\r\n  if(!authConfig(env).enabled||!/^Bearer \\S+$/.test(request.headers.get('Authorization')))fail('Sign in to Coach again.',401);\r\n  try{\r\n    // Only explicit bearer sessions are accepted here. Browser cookies remain\r\n    // Clerk's responsibility; the app never guesses an identity from an email.\r\n    const parties=env.CLERK_AUTHORIZED_PARTIES.split(',').filter(Boolean);\r\n    const claims=await verifyToken(request.headers.get('Authorization').slice(7),{jwtKey:env.CLERK_JWT_KEY,authorizedParties:parties});\r\n    if(typeof claims.sub!=='string'||!claims.sub.startsWith('user_')||typeof claims.sid!=='string'||!claims.sid.startsWith('sess_')||claims.iss!==env.CLERK_FRONTEND_URL||!parties.includes(claims.azp)||claims.sts==='pending')fail('Sign in to Coach again.',401);\r\n    return {legacy,clerk:claims.sub,email:'Your Mom Inc account'};\r\n  }catch{fail('Sign in to Coach again.',401);}\r\n}\r\nexport async function accountOwner(database,id){\r\n  if(id.clerk){await database.prepare('INSERT INTO account_identities(clerk_id,owner_id,created_at) VALUES(?,?,?) ON CONFLICT(clerk_id) DO NOTHING').bind(id.clerk,'clerk:'+id.clerk,Date.now()).run();return (await database.prepare('SELECT owner_id FROM account_identities WHERE clerk_id=?').bind(id.clerk).first()).owner_id;}\r\n  if(!id.legacy)fail('Sign in to save your progress.',401);return id.legacy;\r\n}\r\nexport async function linkLegacy(database,id){\r\n  if(!id.clerk||!id.legacy)fail('Sign in to both accounts to reconnect your saved Coach.',401);\r\n  // Unique keys make concurrent linking deterministic and prevent two logins\r\n  // from claiming the same Coach. Existing separate accounts are not merged.\r\n  try{await database.prepare('INSERT INTO account_identities(clerk_id,owner_id,created_at) VALUES(?,?,?) ON CONFLICT(clerk_id) DO NOTHING').bind(id.clerk,id.legacy,Date.now()).run();}catch{fail('This Coach is already connected to another login. Continue with ChatGPT to access it.',409);}\r\n  const row=await database.prepare('SELECT owner_id FROM account_identities WHERE clerk_id=?').bind(id.clerk).first();\r\n  if(row?.owner_id!==id.legacy)fail('This login already has a separate Coach. Continue with ChatGPT to keep using your original Coach.',409);\r\n  return {linked:true};\r\n}\r\n","server/cloudflare.mjs":"import worker from './worker.mjs';\r\nimport music from '../audio/music/manifest.json' with {type:'json'};\r\n// Only the published, bounded music files need byte serving; other assets keep their existing route.\r\nconst musicBytes=new Map(Object.values(music.tracks).map(track=>[track.url,track.bytes]));\r\nfunction matchingRangeValidator(value,headers){\r\n if(!value)return true;\r\n if(value.startsWith('\"')||value.startsWith('W/'))return !value.startsWith('W/')&&value===headers.get('etag');\r\n const date=Date.parse(value),modified=Date.parse(headers.get('last-modified'));\r\n return Number.isFinite(date)&&Number.isFinite(modified)&&value===headers.get('last-modified');\r\n}\r\n// Single-range parsing follows scripts/actual-dj-preview.mjs, including suffix and end clamping.\r\nexport async function musicRangeResponse(request,response,size){\r\n if(response.status!==200)return response;\r\n if(!Number.isSafeInteger(size)||size<=0||size>1024*1024)return new Response('Music asset exceeds byte-serving bounds',{status:502});\r\n const reader=response.body?.getReader(),chunks=[];let length=0;\r\n if(reader)try{while(true){const {done,value}=await reader.read();if(done)break;length+=value.byteLength;if(length>size){await reader.cancel();return new Response('Music asset size mismatch',{status:502});}chunks.push(value);}}finally{reader.releaseLock();}\r\n if(length!==size)return new Response('Music asset size mismatch',{status:502});\r\n const bytes=new Uint8Array(size);let offset=0;for(const chunk of chunks){bytes.set(chunk,offset);offset+=chunk.byteLength;}\r\n const headers=new Headers(response.headers);headers.delete('content-encoding');headers.delete('content-range');headers.set('accept-ranges','bytes');headers.set('content-length',String(size));\r\n if(request.method==='HEAD')return new Response(null,{status:200,headers});\r\n const range=request.headers.get('range');\r\n if(!range||!matchingRangeValidator(request.headers.get('if-range'),headers))return new Response(bytes,{headers});\r\n const match=range.match(/^bytes=(\\d*)-(\\d*)$/i);let start,end;\r\n if(match&&(match[1]||match[2])){\r\n  if(match[1]){start=Number(match[1]);end=match[2]?Number(match[2]):size-1;}\r\n  else{const suffix=Number(match[2]);start=suffix>0?Math.max(0,size-suffix):size;end=size-1;}\r\n }\r\n if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||start>=size||end<start){headers.set('content-range',`bytes */${size}`);headers.set('content-length','0');return new Response(null,{status:416,headers});}\r\n end=Math.min(end,size-1);headers.set('content-range',`bytes ${start}-${end}/${size}`);headers.set('content-length',String(end-start+1));\r\n return new Response(bytes.subarray(start,end+1),{status:206,headers});\r\n}\r\n// Public Cloudflare Workers entry (the build bundles this, not worker.mjs).\r\n// OpenAI Sites injected oai-authenticated-user-* after its own ChatGPT sign-in; on a\r\n// public Worker any caller can send them, so trusting them is account takeover.\r\n// They are dropped here, leaving identity to a verified Clerk bearer token (auth.mjs).\r\n// Tests and the local dev server (Sites mock sign-in) still call worker.mjs directly.\r\nexport default {\r\n ...worker,\r\n async fetch(request,env,ctx){\r\n  const headers=new Headers(request.headers);\r\n  for(const name of [...headers.keys()])if(name.startsWith('oai-authenticated-user-'))headers.delete(name);\r\n  const clean=new Request(request,{headers}),size=musicBytes.get(new URL(request.url).pathname);\r\n  if(size&&env.ASSETS&&['GET','HEAD'].includes(request.method)){\r\n   // ASSETS is an in-process binding, so this does not re-enter the public Worker route.\r\n   // Fetch the complete representation for slicing, never a conditional or partial response.\r\n   for(const name of ['range','if-range','if-none-match','if-modified-since','if-match','if-unmodified-since'])headers.delete(name);\r\n   headers.set('accept-encoding','identity');\r\n   return musicRangeResponse(clean,await env.ASSETS.fetch(new Request(request,{method:'GET',headers})),size);\r\n  }\r\n  return worker.fetch(clean,env,ctx);\r\n },\r\n};\r\n","server/coach-army-epochs.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nexport function coachEpoch(value=1){\r\n if(!Number.isSafeInteger(value)||value<1)throw Object.assign(Error('Invalid Coach Army generation.'),{status:422,code:'invalid_account_epoch'});\r\n return value;\r\n}\r\nexport function coachConflict(){throw Object.assign(Error('Coach Army state changed. Refresh before retrying.'),{status:409,code:'coach_army_conflict'});}\r\n// Each predicate is fixed SQL supplied by the owning helper, never request SQL.\r\nexport const requireCoachRow=(db,owner,select,values)=>db.prepare(`UPDATE account_data_epochs SET epoch=CASE WHEN EXISTS(${select}) THEN epoch ELSE NULL END WHERE owner_id=?`).bind(...values,owner);\r\nexport async function coachBatch(db,owner,epoch,now,statements){\r\n try{return await epochFencedBatch(db,{ownerId:owner,expectedDataEpoch:coachEpoch(epoch),now,statements});}\r\n catch(error){if(error.code==='target_epoch_mismatch')throw Object.assign(error,{status:409});throw error;}\r\n}\r\n","server/coach-army-runs.mjs":"import {fail} from './domain.mjs';\r\nimport {coachEpoch,coachBatch,coachConflict,requireCoachRow} from './coach-army-epochs.mjs';\r\nexport const COACH_ARMY_STAGES=['djscratch','goon','lilboyfriend','corgi','hand','armie'];\r\nconst eventId=value=>typeof value==='string'&&/^[A-Za-z0-9._:-]{1,200}$/.test(value);\r\nasync function safe(db,run){const events=(await db.prepare('SELECT stage FROM coach_army_run_events WHERE run_id=? ORDER BY sequence').bind(run.id).all()).results;return {id:run.id,targetAccountId:run.account_id,dataEpoch:run.data_epoch,startedAt:run.started_at,completedAt:run.completed_at,completed:run.completed_at!=null,stages:events.map(e=>e.stage)};}\r\nasync function row(db,id,owner){const run=await db.prepare('SELECT * FROM coach_army_runs WHERE id=? AND account_id=?').bind(id,owner).first();if(!run)fail('Coach Army run not found.',404);return run;}\r\nexport async function startCoachArmyRun(db,owner,now=Date.now(),{dataEpoch=1}={}){\r\n const epoch=coachEpoch(dataEpoch),id=crypto.randomUUID();await coachBatch(db,owner,epoch,now,[db.prepare('INSERT INTO coach_army_runs(id,account_id,started_at,updated_at,data_epoch) VALUES(?,?,?,?,?)').bind(id,owner,now,now,epoch)]);return safe(db,await row(db,id,owner));\r\n}\r\nexport async function readCoachArmyRun(db,id,owner){return safe(db,await row(db,id,owner));}\r\nfunction assertOrigin(run,dataEpoch){if(dataEpoch!==undefined&&coachEpoch(dataEpoch)!==run.data_epoch)coachConflict();}\r\nfunction eventStatements(db,run,event,now){\r\n const {eventId:id,sequence,stage,source}=event,owner=run.account_id,epoch=run.data_epoch;\r\n const statements=[\r\n  requireCoachRow(db,owner,'SELECT 1 FROM coach_army_runs WHERE id=? AND account_id=? AND data_epoch=? AND completed_at IS NULL',[run.id,owner,epoch]),\r\n  requireCoachRow(db,owner,'SELECT 1 WHERE (SELECT COALESCE(MAX(sequence),0) FROM coach_army_run_events WHERE run_id=?)=?',[run.id,sequence-1]),\r\n  db.prepare('INSERT INTO coach_army_run_events(event_id,run_id,account_id,sequence,stage,source,received_at,data_epoch) VALUES(?,?,?,?,?,?,?,?)').bind(id,run.id,owner,sequence,stage,source,now,epoch),\r\n  db.prepare('UPDATE coach_army_runs SET updated_at=? WHERE id=?').bind(now,run.id)\r\n ];\r\n if(source==='verifier'&&stage==='armie'){\r\n  statements.push(requireCoachRow(db,owner,\"SELECT 1 WHERE (SELECT count(*) FROM coach_army_run_events WHERE run_id=? AND source='verifier' AND data_epoch=?)=6\",[run.id,epoch]));\r\n  const completionId=`coach-army-run:${run.id}`;\r\n  statements.push(db.prepare('UPDATE coach_army_runs SET completed_at=?,completion_event_id=?,updated_at=? WHERE id=?').bind(now,completionId,now,run.id));\r\n  statements.push(db.prepare(\"INSERT INTO coach_army_completion_outbox(event_id,run_id,account_id,completed_at,status,updated_at,data_epoch) VALUES(?,?,?,?,'pending',?,?)\").bind(completionId,run.id,owner,now,now,epoch));\r\n }\r\n return statements;\r\n}\r\nasync function writeEvent(db,event,now){\r\n const {runId,accountId,eventId:id,sequence,stage,source,dataEpoch}=event;\r\n if(!eventId(id)||!Number.isSafeInteger(sequence)||sequence<1||stage!==COACH_ARMY_STAGES[sequence-1]||!['browser','verifier'].includes(source))fail('Invalid Coach Army progress event.',409);\r\n const run=await row(db,runId,accountId);assertOrigin(run,dataEpoch);\r\n async function replay(){\r\n  const old=await db.prepare('SELECT * FROM coach_army_run_events WHERE event_id=?').bind(id).first();\r\n  if(!old)return false;\r\n  if(old.run_id!==runId||old.account_id!==accountId||old.sequence!==sequence||old.stage!==stage||old.source!==source||old.data_epoch!==run.data_epoch)coachConflict();\r\n  await coachBatch(db,accountId,run.data_epoch,now,[]);return true;\r\n }\r\n if(await replay())return {run:await safe(db,run),replay:true};\r\n if(run.completed_at!=null)coachConflict();\r\n const previous=await db.prepare('SELECT COALESCE(MAX(sequence),0) AS n FROM coach_army_run_events WHERE run_id=?').bind(runId).first();\r\n if(sequence!==previous.n+1)fail('Coach Army progress is out of order.',409);\r\n try{await coachBatch(db,accountId,run.data_epoch,now,eventStatements(db,run,event,now));}\r\n catch(error){if(error.code==='target_epoch_mismatch')throw error;if(await replay())return {run:await readCoachArmyRun(db,runId,accountId),replay:true};coachConflict();}\r\n return {run:await readCoachArmyRun(db,runId,accountId),replay:false,...(source==='browser'?{evidence:'pending_verifier'}:stage==='armie'?{outbox:'pending'}:{})};\r\n}\r\nexport const recordBrowserCoachArmyEvent=(db,input,owner,now)=>writeEvent(db,{...input,accountId:owner,source:'browser'},now);\r\n// An omitted external producer epoch denotes legacy epoch 1, never the run's new generation.\r\nexport const recordVerifiedCoachArmyEvent=(db,input,now)=>writeEvent(db,{...input,dataEpoch:coachEpoch(input.dataEpoch),source:'verifier'},now);\r\n\r\nconst DJ_SEQUENCE=Object.freeze([['bass',8],['treble',3],['volume',7],['tempo',9]]);\r\nconst challengeView=(c,run)=>({id:c.id,expiresAt:c.expires_at,nextStep:c.next_step,targetAccountId:run.account_id,dataEpoch:run.data_epoch,steps:DJ_SEQUENCE.map(([key,value])=>({key,value}))});\r\nexport async function startDjscratchChallenge(db,runId,owner,now=Date.now(),{dataEpoch}={}){\r\n const run=await row(db,runId,owner);assertOrigin(run,dataEpoch);if(run.completed_at!=null)coachConflict();\r\n const active=await db.prepare(\"SELECT * FROM coach_army_djscratch_challenges WHERE run_id=? AND account_id=? AND status='active' AND expires_at>? ORDER BY created_at DESC LIMIT 1\").bind(runId,owner,now).first();\r\n if(active){await coachBatch(db,owner,run.data_epoch,now,[]);return challengeView(active,run);}\r\n const id=crypto.randomUUID(),expiresAt=now+600000;\r\n await coachBatch(db,owner,run.data_epoch,now,[\r\n  requireCoachRow(db,owner,'SELECT 1 FROM coach_army_runs WHERE id=? AND account_id=? AND data_epoch=? AND completed_at IS NULL',[runId,owner,run.data_epoch]),\r\n  db.prepare(\"UPDATE coach_army_djscratch_challenges SET status='superseded',updated_at=? WHERE run_id=? AND account_id=? AND status='active'\").bind(now,runId,owner),\r\n  db.prepare(\"INSERT INTO coach_army_djscratch_challenges(id,run_id,account_id,expires_at,next_step,status,created_at,updated_at,data_epoch) VALUES(?,?,?,?,0,'active',?,?,?)\").bind(id,runId,owner,expiresAt,now,now,run.data_epoch)\r\n ]);\r\n return challengeView({id,expires_at:expiresAt,next_step:0},run);\r\n}\r\nexport async function recordDjscratchControl(db,{runId,challengeId,key,value,dataEpoch},owner,now=Date.now()){\r\n if(!eventId(challengeId)||!DJ_SEQUENCE.some(([k])=>k===key)||!Number.isSafeInteger(value)||value<0||value>10)fail('Invalid DJ Scratch control evidence.');\r\n const run=await row(db,runId,owner);assertOrigin(run,dataEpoch);\r\n const c=await db.prepare('SELECT * FROM coach_army_djscratch_challenges WHERE id=? AND run_id=? AND account_id=?').bind(challengeId,runId,owner).first();if(!c)fail('DJ Scratch challenge not found.',404);\r\n if(c.data_epoch!==run.data_epoch)coachConflict();\r\n await coachBatch(db,owner,c.data_epoch,now,[]);\r\n if(c.expires_at<=now)fail('DJ Scratch challenge expired.',409);\r\n const answer=(extra)=>readCoachArmyRun(db,runId,owner).then(run=>({run,...extra}));\r\n if(c.status==='completed')return answer({replay:true,accepted:true,complete:true,nextStep:4});\r\n if(c.status!=='active')coachConflict();\r\n const step=c.next_step,[expectedKey,expectedValue]=DJ_SEQUENCE[step]||[];\r\n if(key!==expectedKey||value!==expectedValue){const replay=DJ_SEQUENCE.slice(0,step).some(([k,v])=>k===key&&v===value);return answer({accepted:replay,replay,complete:false,nextStep:step});}\r\n const next=step+1;\r\n const statements=[\r\n  requireCoachRow(db,owner,\"SELECT 1 FROM coach_army_djscratch_challenges WHERE id=? AND account_id=? AND status='active' AND next_step=? AND data_epoch=? AND expires_at>?\",[challengeId,owner,step,c.data_epoch,now]),\r\n  db.prepare(\"UPDATE coach_army_djscratch_challenges SET next_step=?,status=?,completed_at=?,updated_at=? WHERE id=?\").bind(next,next===4?'completed':'active',next===4?now:null,now,challengeId)\r\n ];\r\n if(next===4)statements.push(...eventStatements(db,run,{eventId:`djscratch:${challengeId}`,sequence:1,stage:'djscratch',source:'verifier'},now));\r\n try{await coachBatch(db,owner,c.data_epoch,now,statements);}\r\n catch(error){if(error.code==='target_epoch_mismatch')throw error;const latest=await db.prepare('SELECT next_step,status FROM coach_army_djscratch_challenges WHERE id=?').bind(challengeId).first();if(latest?.next_step>step)return answer({accepted:true,replay:true,complete:latest.status==='completed',nextStep:latest.next_step});coachConflict();}\r\n return answer({accepted:true,replay:false,complete:next===4,nextStep:next});\r\n}\r\nexport async function deliverCoachArmyOutbox(db,env,now=Date.now()){\r\n if(!env.COACH_ARMY_ENTITLEMENT_URL||!env.COACH_ARMY_ENTITLEMENT_TOKEN)return {attempted:0,delivered:0,configured:false};\r\n const rows=(await db.prepare(\"SELECT * FROM coach_army_completion_outbox WHERE status='pending' ORDER BY updated_at LIMIT 25\").all()).results;\r\n let delivered=0;\r\n for(const item of rows){\r\n  try{\r\n   await coachBatch(db,item.account_id,item.data_epoch,now,[]);\r\n   const response=await fetch(env.COACH_ARMY_ENTITLEMENT_URL,{method:'POST',redirect:'error',signal:AbortSignal.timeout(5000),headers:{'Content-Type':'application/json','Authorization':`Bearer ${env.COACH_ARMY_ENTITLEMENT_TOKEN}`},body:JSON.stringify({eventType:'coach_army.completed',eventId:item.event_id,accountId:item.account_id,completedAt:item.completed_at,dataEpoch:item.data_epoch})});\r\n   if(!response.ok)throw Error(`Entitlement receiver returned ${response.status}.`);\r\n   const ack=(await coachBatch(db,item.account_id,item.data_epoch,now,[db.prepare(\"UPDATE coach_army_completion_outbox SET status='delivered',attempts=attempts+1,last_error=NULL,delivered_at=?,updated_at=? WHERE event_id=? AND data_epoch=? AND status='pending'\").bind(now,now,item.event_id,item.data_epoch)]))[0];\r\n   if(ack.meta.changes===1)delivered++;\r\n  }catch(error){\r\n   if(error.code==='target_epoch_mismatch')continue;\r\n   try{await coachBatch(db,item.account_id,item.data_epoch,now,[db.prepare('UPDATE coach_army_completion_outbox SET attempts=attempts+1,last_error=?,updated_at=? WHERE event_id=? AND data_epoch=?').bind('Delivery failed; retry pending.',now,item.event_id,item.data_epoch)]);}\r\n   catch(ackError){if(ackError.code!=='target_epoch_mismatch')throw ackError;}\r\n  }\r\n }\r\n return {attempted:rows.length,delivered,configured:true};\r\n}\r\n","server/combat.mjs":"import {inspectGameGeneration,withGameGeneration} from './game-generation.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {dayAt,DAY_MS,BREATHING_MS} from '../combat.mjs';\r\nimport {evaluateStreakHistory} from '../streak-forgiveness.mjs';\r\nimport {fail,uuid} from './domain.mjs';\r\n\r\nexport async function combatProgress(database,user,now=Date.now(),{recordLogin=true,dataEpoch=1,gameResetAt=0}={}){\r\n const day=dayAt(now);\r\n if(recordLogin)await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('INSERT INTO login_days(user_id,day,logged_at) VALUES(?,?,?) ON CONFLICT(user_id,day) DO NOTHING').bind(user,day,now)]});\r\n // Earlier completed workouts are evidence of a signed-in day. No other\r\n // historical logins are invented when introducing streak tracking.\r\n const days=(await database.prepare(`SELECT day FROM login_days WHERE user_id=? AND day<=? AND (?=0 OR logged_at>?) UNION SELECT CAST(completed_at/86400000 AS INTEGER) AS day FROM workouts WHERE user_id=? AND source='server' AND completed_at IS NOT NULL AND completed_at<? AND (?=0 OR (started_at>? AND completed_at>=started_at)) ORDER BY day DESC`).bind(user,day,gameResetAt,gameResetAt,user,(day+1)*DAY_MS,gameResetAt,gameResetAt).all()).results;\r\n const breathing=await database.prepare('SELECT id FROM breathing_sessions WHERE user_id=? AND completed_at>=? AND completed_at<? AND (?=0 OR started_at>?) LIMIT 1').bind(user,day*DAY_MS,(day+1)*DAY_MS,gameResetAt,gameResetAt).first();\r\n // D12 forgiveness detail (forgivenWeeksInARow, milestones, fired Armie letter\r\n // ids) comes from the same evaluator combat.mjs loginStreak() uses.\r\n const history=evaluateStreakHistory(days.map(row=>row.day),day);\r\n const latest=history.at(-1)||{streak:0,forgivenWeeksInARow:0,forgivenessAvailableThisWeek:true,auraMilestone:null,lettersFired:[]};\r\n // A letter can fire on a day nothing else called in on (e.g. the day of a\r\n // forgiven miss). pendingLetters carries any fired in the last 35 days so a\r\n // reopen after a gap still delivers them; the inbox dedupes by (date, letter\r\n // id), so re-seeing an already-delivered one here is a no-op.\r\n // ponytail: bounded recompute each call, not a persisted \"already sent\"\r\n // cursor -- fine at this data scale, revisit if per-user history gets huge.\r\n const pendingLetters=history.filter(d=>d.lettersFired.length&&dayAt(Date.parse(d.date))>=day-35).flatMap(d=>d.lettersFired.map(id=>({date:d.date,id})));\r\n return {day,loginStreak:latest.streak,forgivenWeeksInARow:latest.forgivenWeeksInARow,forgivenessAvailableThisWeek:latest.forgivenessAvailableThisWeek,auraMilestone:latest.auraMilestone,lettersFired:latest.lettersFired,pendingLetters,breathingCompleted:!!breathing};\r\n}\r\nexport async function startBreathing(database,user,now=Date.now(),{dataEpoch=1}={}){\r\n const recent=await database.prepare('SELECT id,started_at FROM breathing_sessions WHERE user_id=? AND completed_at IS NULL AND started_at>? ORDER BY started_at DESC LIMIT 1').bind(user,now-5000).first();\r\n if(recent){await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[]});return {id:recent.id,startedAt:recent.started_at,durationMs:BREATHING_MS};}\r\n const id=crypto.randomUUID();\r\n await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('INSERT INTO breathing_sessions(id,user_id,started_at) VALUES(?,?,?)').bind(id,user,now)]});\r\n return {id,startedAt:now,durationMs:BREATHING_MS};\r\n}\r\nexport async function completeBreathing(database,user,value,now=Date.now(),{dataEpoch=1}={}){\r\n const id=uuid(value.id),session=await database.prepare('SELECT * FROM breathing_sessions WHERE id=? AND user_id=?').bind(id,user).first();\r\n if(!session)fail('Breathing session not found.',404);\r\n if(session.completed_at==null){\r\n  const elapsed=now-session.started_at;\r\n  if(elapsed<BREATHING_MS||elapsed>2*60*60*1000||!Number.isFinite(value.activeMs)||value.activeMs<BREATHING_MS||value.activeMs>elapsed+1000)fail('Finish the full three-minute breathing session first.');\r\n\r\n }\r\n await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[\r\n  database.prepare('UPDATE breathing_sessions SET completed_at=? WHERE id=? AND user_id=? AND completed_at IS NULL').bind(now,id,user),\r\n  database.prepare('INSERT INTO login_days(user_id,day,logged_at) VALUES(?,?,?) ON CONFLICT(user_id,day) DO NOTHING').bind(user,dayAt(now),now),\r\n ]});\r\n const game=await inspectGameGeneration(database);return {...await combatProgress(withGameGeneration(database,game.gameGeneration),user,now,{recordLogin:false,gameResetAt:game.gameResetAt}),...game};\r\n}\r\n","server/db.mjs":"export const db=env=>{if(!env.DB)throw Object.assign(new Error('Account storage is not connected yet.'),{status:503});return env.DB;};\r\n","server/domain.mjs":"import {TONES,CADENCES,scheduledDay} from '../reminder-settings.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nexport const MODES={...Object.fromEntries(Object.values(EXERCISES).map(m=>[m.id,m.kind==='hold'||m.kind==='pace'?[15,20,30,45,60]:[5,10,15,20]])),squat:[5,10,15,20],pushup:[5,10,15,20],tree:[15,20,30,45,60],warrior:[15,20,30,45,60],horse:[15,20,30,45,60],boxing:[30,60,90,120,180],jogging:[20,40,60,100],jumping:[5,10,15,20]};\r\nexport const KINDS={workout:'Time for your workout. Your coach is ready.',meal:'Pause and log your meal.',water:'Time for a water break.',motivation:'A small step counts. Move at your own pace.'};\r\nexport function fail(message,status=400){throw Object.assign(new Error(message),{status});}\r\nexport const cleanText=(v,max=160)=>typeof v==='string'&&v.trim().length&&v.trim().length<=max?v.trim():fail('Please check the highlighted information.');\r\nexport const uuid=v=>typeof v==='string'&&/^[a-f0-9-]{36}$/.test(v)?v:fail('Invalid record.');\r\nexport function nutrition(v,max=20000){if(v==null||v==='')return null;return typeof v==='number'&&Number.isFinite(v)&&v>=0&&v<=max?v:fail('Check the nutrition amounts.');}\r\nexport function mealInput(v){const eatenAt=cleanText(v.eatenAt,40);if(!/^\\d{4}-\\d\\d-\\d\\dT/.test(eatenAt)||!Number.isFinite(Date.parse(eatenAt)))fail('Choose a valid meal time.');if(v.micros!=null&&(typeof v.micros!=='object'||Array.isArray(v.micros)))fail('Invalid micronutrients.');const micros=Object.fromEntries(['calcium','iron','magnesium','potassium','sodium','zinc','vitaminA','vitaminC','vitaminD','vitaminE','vitaminB12','folate'].map(k=>[k,nutrition(v.micros?.[k],2000000)]));return {id:uuid(v.id),name:cleanText(v.name),portion:cleanText(v.portion,120),calories:nutrition(v.calories),protein:nutrition(v.protein),carbs:nutrition(v.carbs),fat:nutrition(v.fat),micros,nutritionSource:v.nutritionSource==null?null:cleanText(v.nutritionSource,120),eatenAt:new Date(eatenAt).toISOString()};}\r\nexport function reminderInput(v){const tone=v.tone??'direct',daysPerWeek=v.daysPerWeek??7;if(!TONES.includes(tone))fail('Choose gentle, direct or cheeky.');if(!CADENCES.includes(daysPerWeek))fail('Choose 1, 3, 5 or 7 reminder days.');if(!Object.hasOwn(KINDS,v.kind))fail('Choose a reminder type.');for(const k of ['time','quietStart','quietEnd'])if(typeof v[k]!=='string'||!/^([01]\\d|2[0-3]):[0-5]\\d$/.test(v[k]))fail('Choose a valid reminder time.');try{new Intl.DateTimeFormat('en',{timeZone:v.timezone}).format();}catch{fail('Choose a valid time zone.');}return {id:uuid(v.id),tone,daysPerWeek,kind:v.kind,time:v.time,timezone:v.timezone,enabled:v.enabled===true?1:0,quietStart:v.quietStart,quietEnd:v.quietEnd};}\r\nexport function localTime(now,timezone){const p=Object.fromEntries(new Intl.DateTimeFormat('en-CA',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hourCycle:'h23'}).formatToParts(new Date(now)).map(x=>[x.type,x.value]));return {day:`${p.year}-${p.month}-${p.day}`,time:`${p.hour}:${p.minute}`};}\r\nconst minutes=s=>Number(s.slice(0,2))*60+Number(s.slice(3));\r\nexport function isDue(r,now){if(!r.enabled)return false;const l=localTime(now,r.timezone),a=minutes(r.quiet_start),b=minutes(r.quiet_end),t=minutes(l.time);if(!scheduledDay(l.day,r.days_per_week??7))return false;if(a!==b&&(a<b?t>=a&&t<b:t>=a||t<b))return false;const delta=t-minutes(r.time);return delta>=0&&delta<10?l.day:false;}\r\nexport function progress(count){return {completedSets:count,xp:count*25,level:1+Math.floor(count/4),unlocks:{shieldBreak:count>=196,ember:count>=4,arc:count>=16,frost:count>=36}};}\r\nexport function validateCompletion(w,input,now){const elapsed=(now-w.started_at)/1000,kind=EXERCISES[w.mode]?.kind,min=['hold','pace'].includes(kind)?w.goal:Math.max(2,w.goal*.3);if(elapsed<min||elapsed>7200)fail('This set expired or finished too quickly. Begin a new set.');if(!Number.isFinite(input.value)||input.value<w.goal||input.value>100000)fail('Complete the selected goal first.');if(kind==='pace'&&(!Number.isFinite(input.active)||input.active<w.goal))fail('Hand movement is needed to complete this round.');return true;}\r\n","server/entitlements.mjs":"import {fail} from './domain.mjs';\r\nimport {coachEpoch,coachBatch,coachConflict,requireCoachRow} from './coach-army-epochs.mjs';\r\nconst validId=v=>typeof v==='string'&&v.length>0&&v.length<=200&&v.trim()===v;\r\nfunction validate(event){\r\n if(!event||event.eventType!=='coach_army.completed'||!validId(event.eventId)||!validId(event.accountId)||!Number.isSafeInteger(event.completedAt)||event.completedAt<1)fail('Invalid Coach Army completion event.');\r\n return coachEpoch(event.dataEpoch);\r\n}\r\nfunction completionStatements(db,event,epoch,now){\r\n return [\r\n  db.prepare(`INSERT INTO coach_army_completions(event_id,user_id,completed_at,received_at,data_epoch) VALUES(?,?,?,?,?) ON CONFLICT(event_id) DO UPDATE SET completed_at=CASE WHEN coach_army_completions.user_id=excluded.user_id AND coach_army_completions.completed_at=excluded.completed_at AND coach_army_completions.data_epoch=excluded.data_epoch THEN coach_army_completions.completed_at ELSE NULL END`).bind(event.eventId,event.accountId,event.completedAt,now,epoch),\r\n  db.prepare(`INSERT INTO account_entitlements(user_id,coach_army_status,coach_army_completed_at,coach_army_event_id,updated_at,data_epoch) VALUES(?,'completed',?,?,?,?) ON CONFLICT(user_id) DO UPDATE SET coach_army_status='completed',coach_army_completed_at=MIN(COALESCE(account_entitlements.coach_army_completed_at,excluded.coach_army_completed_at),excluded.coach_army_completed_at),coach_army_event_id=CASE WHEN account_entitlements.coach_army_status='completed' THEN account_entitlements.coach_army_event_id ELSE excluded.coach_army_event_id END,updated_at=excluded.updated_at,data_epoch=excluded.data_epoch`).bind(event.accountId,event.completedAt,event.eventId,now,epoch)\r\n ];\r\n}\r\nasync function completionIdentity(db,event,epoch){\r\n const old=await db.prepare('SELECT * FROM coach_army_completions WHERE event_id=?').bind(event.eventId).first();\r\n if(old&&(old.user_id!==event.accountId||old.completed_at!==event.completedAt||old.data_epoch!==epoch))coachConflict();\r\n}\r\nconst result=(db,owner)=>db.prepare('SELECT coach_army_status AS status,coach_army_completed_at AS completedAt,coach_army_event_id AS eventId,data_epoch AS dataEpoch,user_id AS targetAccountId FROM account_entitlements WHERE user_id=?').bind(owner).first();\r\n// Missing producer epoch is the fixed legacy generation 1, never current-on-arrival.\r\nexport async function recordCoachArmyCompletion(db,event,now=Date.now()){\r\n const epoch=validate(event);await completionIdentity(db,event,epoch);\r\n try{await coachBatch(db,event.accountId,epoch,now,completionStatements(db,event,epoch,now));}\r\n catch(error){await completionIdentity(db,event,epoch);throw error;}\r\n return result(db,event.accountId);\r\n}\r\n\r\nexport async function readEntitlements(database,user){\r\n  const row=await database.prepare('SELECT coach_army_status AS status,coach_army_completed_at AS completedAt,coach_army_event_id AS eventId FROM account_entitlements WHERE user_id=?').bind(user).first();\r\n  const packs=await database.prepare(\"SELECT pack_id AS packId, granted_at AS grantedAt FROM account_pack_entitlements WHERE user_id=? AND status='owned' ORDER BY pack_id\").bind(user).all();\r\n  return {coachArmy:row?.status==='completed'&&Number.isSafeInteger(Number(row.completedAt))?{status:'completed',completedAt:Number(row.completedAt)}:null,ownedPacks:(packs.results||[]).filter(pack=>typeof pack.packId==='string'&&Number.isSafeInteger(Number(pack.grantedAt))).map(pack=>({packId:pack.packId,status:'owned',grantedAt:Number(pack.grantedAt)}))};\r\n}\r\n\r\n\r\nconst randomToken=()=>Array.from(crypto.getRandomValues(new Uint8Array(32)),b=>b.toString(16).padStart(2,'0')).join('');\r\nconst sha256=async value=>Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(value))),b=>b.toString(16).padStart(2,'0')).join('');\r\nexport async function createCoachArmyBinding(db,{runId,accountId,eventId,completedAt,dataEpoch},now=Date.now()){\r\n const epoch=validate({eventType:'coach_army.completed',accountId,eventId,completedAt,dataEpoch});if(!validId(runId))fail('Invalid Coach Army binding.');\r\n const run=await db.prepare('SELECT completed_at FROM gala_runs WHERE id=?').bind(runId).first();\r\n if(!run?.completed_at||run.completed_at>completedAt)fail('Coach Army run is not authoritatively complete.',409);\r\n const existing=await db.prepare('SELECT * FROM coach_army_bindings WHERE event_id=?').bind(eventId).first();\r\n if(existing){\r\n  if(existing.run_id!==runId||existing.account_id!==accountId||existing.completed_at!==completedAt||existing.data_epoch!==epoch)coachConflict();\r\n  await coachBatch(db,accountId,epoch,now,[]);\r\n  return {bindingId:existing.binding_id,claimToken:null,expiresAt:existing.expires_at,claimed:existing.claimed_at!=null,targetAccountId:accountId,dataEpoch:epoch};\r\n }\r\n const bindingId=crypto.randomUUID(),claimToken=randomToken(),expiresAt=now+300000;\r\n await coachBatch(db,accountId,epoch,now,[\r\n  requireCoachRow(db,accountId,'SELECT 1 FROM gala_runs WHERE id=? AND completed_at IS NOT NULL AND completed_at<=?',[runId,completedAt]),\r\n  db.prepare('INSERT INTO coach_army_bindings(binding_id,run_id,account_id,event_id,claim_hash,completed_at,expires_at,created_at,data_epoch) VALUES(?,?,?,?,?,?,?,?,?)').bind(bindingId,runId,accountId,eventId,await sha256(claimToken),completedAt,expiresAt,now,epoch)\r\n ]);\r\n return {bindingId,claimToken,expiresAt,claimed:false,targetAccountId:accountId,dataEpoch:epoch};\r\n}\r\nexport async function claimCoachArmyBinding(db,{bindingId,claimToken,accountId,dataEpoch},now=Date.now()){\r\n if(!validId(bindingId)||typeof claimToken!=='string'||!validId(accountId))fail('Invalid Coach Army claim.');\r\n const known=await db.prepare('SELECT * FROM coach_army_bindings WHERE binding_id=?').bind(bindingId).first();\r\n if(!known||known.account_id!==accountId)fail('Coach Army binding does not belong to this account.',403);\r\n if(known.claimed_at!=null)fail('Coach Army binding was already claimed.',409);\r\n if(known.expires_at<=now)fail('Coach Army binding expired.',410);\r\n if(known.claim_hash!==await sha256(claimToken))fail('Invalid Coach Army claim.',401);\r\n const epoch=coachEpoch(known.data_epoch);if(dataEpoch!==undefined&&coachEpoch(dataEpoch)!==epoch)coachConflict();\r\n const event={eventType:'coach_army.completed',eventId:known.event_id,accountId,completedAt:known.completed_at,dataEpoch:epoch};\r\n await completionIdentity(db,event,epoch);\r\n await coachBatch(db,accountId,epoch,now,[\r\n  requireCoachRow(db,accountId,'SELECT 1 FROM coach_army_bindings WHERE binding_id=? AND account_id=? AND claim_hash=? AND claimed_at IS NULL AND expires_at>? AND data_epoch=?',[bindingId,accountId,known.claim_hash,now,epoch]),\r\n  db.prepare('UPDATE coach_army_bindings SET claimed_at=? WHERE binding_id=?').bind(now,bindingId),\r\n  ...completionStatements(db,event,epoch,now)\r\n ]);\r\n return result(db,accountId);\r\n}\r\n","server/flight-address.mjs":"// User-requested US street-address approximation. No address logging or shared cache.\r\nconst replies=(v,status=200)=>new Response(JSON.stringify(v),{status,headers:{'Content-Type':'application/json','Cache-Control':'no-store','X-Content-Type-Options':'nosniff'}});\r\nconst limits=new Map();\r\nexport async function flightAddress(request,{fetchImpl=fetch,now=Date.now()}={}){\r\n const url=new URL(request.url);if(request.method!=='POST')return replies({error:'Method not allowed.'},405);\r\n if(request.headers.get('Origin')!==url.origin)return replies({error:'Open address search from MYR5.'},403);\r\n if(!request.headers.get('Content-Type')?.startsWith('application/json'))return replies({error:'Send JSON.'},415);\r\n const key=request.headers.get('CF-Connecting-IP')||'local',prev=limits.get(key);if(prev&&now-prev.at<1200)return replies({error:'Please wait before searching again.'},429);\r\n if(limits.size>1000)limits.clear();limits.set(key,{at:now});\r\n try{\r\n  const reader=request.body?.getReader();if(!reader)return replies({error:'Send an address.'},400);const chunks=[];let size=0;for(;;){const {done,value}=await reader.read();if(done)break;size+=value.byteLength;if(size>1024){await reader.cancel();return replies({error:'Address is too long.'},413);}chunks.push(value);}const bytes=new Uint8Array(size);let offset=0;for(const c of chunks){bytes.set(c,offset);offset+=c.byteLength;}\r\n  const {address}=JSON.parse(new TextDecoder().decode(bytes));if(typeof address!=='string'||address.trim().length<3||address.length>100)return replies({error:'Enter a US street address under 100 characters.'},400);\r\n  const u=new URL('https://geocoding.geo.census.gov/geocoder/locations/onelineaddress');u.search=new URLSearchParams({address:address.trim(),benchmark:'Public_AR_Current',format:'json'});\r\n  const res=await fetchImpl(u,{headers:{Accept:'application/json'},signal:AbortSignal.timeout(8000)});if(!res.ok)return replies({error:'Address lookup unavailable.'},502);\r\n  const data=await res.json(),places=(data.result?.addressMatches||[]).slice(0,5).map(p=>({source:'address',id:String(p.tigerLine?.tigerLineId||p.matchedAddress),label:String(p.matchedAddress).slice(0,120),lat:p.coordinates?.y,lng:p.coordinates?.x})).filter(p=>Number.isFinite(p.lat)&&Number.isFinite(p.lng)&&p.lat>=35.96&&p.lat<=36.34&&p.lng>=-115.44&&p.lng<=-114.96);\r\n  return replies({places,approximate:true});\r\n }catch(e){return replies({error:e instanceof SyntaxError?'Invalid request.':'Address lookup unavailable.'},e instanceof SyntaxError?400:502);}\r\n}\r\n","server/gala.mjs":"import {fail} from './domain.mjs';\r\nconst checkpoints={djscratch:'dj_at',goon:'gala_at',lilboyfriend:'lil_at',corgi:'corgi_at',hand:'hand_at',armie:'armie_at'};\r\nconst hash=async text=>Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(text))),b=>b.toString(16).padStart(2,'0')).join('');\r\nconst safe=row=>({id:row.id,startedAt:row.started_at,completedAt:row.completed_at,durationMs:row.duration_ms,installedAt:row.installed_at,joinedAt:row.joined_at,djName:row.dj_name,moniker:row.moniker,completed:Object.entries(checkpoints).filter(([,column])=>row[column]!=null).map(([name])=>name)});\r\nexport function galaName(value){const base=String(value?.base||'').trim(),moniker=String(value?.moniker||'').trim().toUpperCase();if(!/^[A-Z0-9 ]{2,48}$/i.test(base)||!/^[A-Z0-9]{3}$/.test(moniker))fail('Choose a DJ name and a three-character moniker.');return {name:`${base} · ${moniker}`,moniker};}\r\nexport async function galaApi(request,database,readBody){\r\n const url=new URL(request.url),origin=request.headers.get('Origin'),self=url.origin,allowed=origin===self||origin==='https://mominc.online'||origin==='https://www.mominc.online'||(!origin&&request.method==='GET');\r\n const response=(data,status=200)=>new Response(JSON.stringify(data),{status,headers:{'Content-Type':'application/json','Cache-Control':'no-store','X-Content-Type-Options':'nosniff','Vary':'Origin',...(allowed&&origin?{'Access-Control-Allow-Origin':origin,'Access-Control-Allow-Methods':'GET, POST, OPTIONS','Access-Control-Allow-Headers':'Content-Type, Authorization'}:{})}});\r\n try{\r\n  if(request.method==='OPTIONS')return allowed?new Response(null,{status:204,headers:{'Access-Control-Allow-Origin':origin,'Access-Control-Allow-Methods':'GET, POST, OPTIONS','Access-Control-Allow-Headers':'Content-Type, Authorization','Vary':'Origin'}}):response({error:'Open this from MOM Inc.'},403);\r\n  const path=url.pathname,method=request.method,now=Date.now();\r\n  if(path==='/api/gala/install-draft'){\r\n   if((method==='POST'&&origin!==self)||(origin&&origin!==self))fail('Open this from Coach.',403);\r\n   let id,token;\r\n   if(method==='POST'){const data=await readBody(request);id=data.id;token=data.token;}\r\n   else if(method==='GET'){const saved=request.headers.get('Cookie')?.split(';').map(x=>x.trim()).find(x=>x.startsWith('__Host-gala-run='))?.split('=')[1];[id,token]=(saved||'').split('.');if(!saved)return response({data:null});}\r\n   else fail('Action unavailable.',405);\r\n   if(!/^[0-9a-f-]{36}$/.test(id||'')||!/^[a-f0-9]{64}$/.test(token||''))fail('Invalid Gala invitation.');\r\n   const row=await database.prepare('SELECT * FROM gala_runs WHERE id=? AND token_hash=?').bind(id,await hash(token)).first();if(!row)fail('Run not found.',404);\r\n   const result=response({data:{version:1,id,token}});if(method==='POST')result.headers.set('Set-Cookie',`__Host-gala-run=${id}.${token}; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=604800`);return result;\r\n  }\r\n  if(path==='/api/gala/leaderboard'&&method==='GET'){\r\n   const rows=await database.prepare('SELECT dj_name AS djName,moniker,duration_ms AS durationMs,joined_at AS joinedAt FROM gala_runs WHERE joined_at IS NOT NULL ORDER BY duration_ms ASC,joined_at ASC LIMIT 100').all();\r\n   return response({items:rows.results.map((row,i)=>({...row,rank:i+1}))});\r\n  }\r\n  if(!allowed)fail('Open this action from MOM Inc or Coach.',403);\r\n  if(path==='/api/gala/runs'&&method==='POST'){\r\n   const creator=await hash(request.headers.get('CF-Connecting-IP')||'local');\r\n   const recent=await database.prepare('SELECT COUNT(*) AS total FROM gala_runs WHERE creator=? AND started_at>?').bind(creator,now-3600000).first();if(recent.total>=12)fail('Please wait before starting another ranked run.',429);\r\n   const id=crypto.randomUUID(),token=Array.from(crypto.getRandomValues(new Uint8Array(32)),b=>b.toString(16).padStart(2,'0')).join('');\r\n   await database.prepare('INSERT INTO gala_runs(id,token_hash,creator,started_at) VALUES(?,?,?,?)').bind(id,await hash(token),creator,now).run();\r\n   return response({id,token,startedAt:now},201);\r\n  }\r\n  const match=path.match(/^\\/api\\/gala\\/runs\\/([0-9a-f-]{36})(?:\\/(checkpoint|install|join|moniker))?$/i);if(!match)fail('Run not found.',404);\r\n  const token=request.headers.get('Authorization')?.replace(/^Bearer /,'');if(!token||!/^[a-f0-9]{64}$/.test(token))fail('Open the run on the device where you started it.',401);\r\n  const row=await database.prepare('SELECT * FROM gala_runs WHERE id=? AND token_hash=?').bind(match[1],await hash(token)).first();if(!row)fail('Run not found.',404);\r\n  if(!match[2]&&method==='GET')return response(safe(row));\r\n  if(method!=='POST')fail('Action unavailable.',405);\r\n  const data=await readBody(request);\r\n  if(match[2]==='checkpoint'){\r\n   if(row.joined_at)return response(safe(row));\r\n   const column=checkpoints[data.stage];if(!column)fail('Unknown run milestone.');\r\n   if(data.stage==='djscratch'){\r\n    const name=galaName(data);if(row.dj_at&&row.dj_name!==name.name)fail('Your DJ identity is already attached to this run.',409);\r\n    await database.prepare('UPDATE gala_runs SET dj_at=COALESCE(dj_at,?),dj_name=?,moniker=? WHERE id=?').bind(now,name.name,name.moniker,row.id).run();\r\n   }else{\r\n    if(data.stage==='armie'&&(!row.dj_at||!row.lil_at||!row.corgi_at||!row.gala_at||!row.hand_at))fail('Finish all four character pages, including the Gala, and your hand before Coach Armie.',409);\r\n    await database.prepare(`UPDATE gala_runs SET ${column}=COALESCE(${column},?) WHERE id=?`).bind(now,row.id).run();\r\n   }\r\n   await database.prepare('UPDATE gala_runs SET completed_at=?,duration_ms=?-started_at WHERE id=? AND completed_at IS NULL AND dj_at IS NOT NULL AND gala_at IS NOT NULL AND lil_at IS NOT NULL AND corgi_at IS NOT NULL AND hand_at IS NOT NULL AND armie_at IS NOT NULL').bind(now,now,row.id).run();\r\n  }else if(match[2]==='moniker'){\r\n   if(!row.dj_at||row.joined_at)fail('Change your moniker before joining the rankings.',409);\r\n   const next=galaName({base:row.dj_name.split(' · ')[0],moniker:data.moniker}),duplicate=await database.prepare('SELECT id FROM gala_runs WHERE public_name=? AND id<>?').bind(next.name.toUpperCase(),row.id).first();\r\n   if(duplicate)fail('That name and moniker are taken. Try another three characters.',409);\r\n   await database.prepare('UPDATE gala_runs SET dj_name=?,moniker=? WHERE id=? AND joined_at IS NULL').bind(next.name,next.moniker,row.id).run();\r\n  }else if(match[2]==='install'){\r\n   if(origin!==self||data.installed!==true)fail('Open the installed MYR5 app to save your run.',403);\r\n   await database.prepare('UPDATE gala_runs SET installed_at=COALESCE(installed_at,?) WHERE id=?').bind(now,row.id).run();\r\n  }else if(match[2]==='join'){\r\n   if(data.confirm!==true)fail('Choose Join the rankings to show your DJ name and time.');\r\n   if(!row.completed_at||!row.installed_at)fail('Complete DJ Scratch, the Gala and Armie, then get the app.',409);\r\n   if(!row.joined_at){\r\n    const nameKey=row.dj_name.toUpperCase(),duplicate=await database.prepare('SELECT id FROM gala_runs WHERE public_name=? AND id<>?').bind(nameKey,row.id).first();\r\n    if(duplicate)fail('That DJ name and moniker are already on the board. This run stays saved on your device.',409);\r\n    try{await database.prepare('UPDATE gala_runs SET joined_at=?,public_name=? WHERE id=? AND joined_at IS NULL').bind(now,nameKey,row.id).run();}catch(error){if(/UNIQUE constraint/i.test(error.message))fail('That DJ name and moniker are already on the board.',409);throw error;}\r\n   }\r\n  }\r\n  return response(safe(await database.prepare('SELECT * FROM gala_runs WHERE id=?').bind(row.id).first()));\r\n }catch(error){if(!error.status)console.error('Gala request failed',error.message);return response({error:error.status?error.message:'The Gala terminal could not connect. Try again.'},error.status||500);}\r\n}\r\n","server/game-generation.mjs":"// Game progress lifetime only. Never substitute this for the account-data epoch.\r\nimport { freshBase } from '../modules/coach-world/state.mjs';\r\nimport { BLUEPRINT_POLICY_VERSION } from '../modules/coach-world/blueprint-policy.mjs';\r\nexport class GameGenerationError extends Error {\r\n  constructor(code, details) { super(code); this.name = 'GameGenerationError'; this.code = code; this.status = 409; this.details = details; }\r\n}\r\nconst fail = (code, details) => { throw new GameGenerationError(code, details); };\r\nconst validGeneration = n => Number.isSafeInteger(n) && n >= 1;\r\nconst validTime = n => Number.isSafeInteger(n) && n >= 0;\r\nconst generation = n => validGeneration(n) ? n : fail('game_generation_required');\r\nconst metadata = value => {\r\n  if (!value || !validGeneration(value.gameGeneration) || !validTime(value.gameResetAt)) fail('game_generation_corrupt');\r\n  return value;\r\n};\r\n\r\nexport async function inspectGameGeneration(db) {\r\n  const row = await db.prepare('SELECT generation,reset_at FROM game_generation WHERE id=1').first();\r\n  return metadata({ gameGeneration: row?.generation, gameResetAt: row?.reset_at });\r\n}\r\nexport function requireGameGeneration(expected, current) {\r\n  metadata(current);\r\n  if (!validGeneration(expected) || expected !== current.gameGeneration) fail('game_generation_mismatch', current);\r\n  return current;\r\n}\r\nexport function gameGenerationGuard(db, expected) {\r\n  return db.prepare('INSERT INTO game_generation_assertions(id,ok) VALUES(?,CASE WHEN EXISTS(SELECT 1 FROM game_generation WHERE id=1 AND generation=?) THEN 1 ELSE NULL END)').bind(crypto.randomUUID(), generation(expected));\r\n}\r\nexport const clearGameGenerationGuards = db => db.prepare('DELETE FROM game_generation_assertions');\r\n\r\n/** Pin ONE request/cron to a generation. Guards and caller statements commit or roll back together.\r\n * Also fences reads: replay/provision/error-recovery cannot return old-generation rows after reset.\r\n * Existing owner/epoch/CAS guards still apply. No exec/raw/session escape hatch is exposed.\r\n * Single SELECT reads validate metadata in the same snapshot without taking an assertion write lock.\n */\r\nconst guardedGenerations = new WeakMap();\r\nexport function withGameGeneration(db, expected) {\r\n  generation(expected);\r\n  if (guardedGenerations.get(db) === expected) return db;\r\n  const statements = new WeakMap();\r\n  async function batch(items) {\r\n    const raw = items.map(item => statements.get(item) ?? fail('game_generation_foreign_statement'));\r\n    try {\r\n      return (await db.batch([gameGenerationGuard(db, expected), ...raw, clearGameGenerationGuards(db)])).slice(1, -1);\r\n    } catch (error) {\r\n      // Preserve unrelated constraints; remap stale generation even when the underlying API\r\n      // wraps its constraint error without exposing the assertion table name.\r\n      requireGameGeneration(expected, await inspectGameGeneration(db));\r\n      throw error;\r\n    }\r\n  }\r\n  async function read(raw) {\n    const [meta, result] = await db.batch([db.prepare('SELECT generation,reset_at FROM game_generation WHERE id=1'), raw]);\n    const row = meta.results?.[0];\n    requireGameGeneration(expected, { gameGeneration: row?.generation, gameResetAt: row?.reset_at });\n    return result;\n  }\n  function wrap(raw, singleSelect) {\n    const statement = {\n      bind: (...args) => wrap(raw.bind(...args), singleSelect),\n      run: async () => (await batch([statement]))[0],\n      all: async () => singleSelect ? read(raw) : (await batch([statement]))[0],\n      first: async column => {\n        const row = (singleSelect ? await read(raw) : (await batch([statement]))[0]).results?.[0] ?? null;\n        if (row && column !== undefined && !Object.hasOwn(row, column)) throw Error(`D1_COLUMN_NOTFOUND: ${column}`);\r\n        return row === null ? null : column === undefined ? row : row[column];\r\n      },\r\n    };\r\n    statements.set(statement, raw);\r\n    return statement;\r\n  }\r\n  // Trusted application SQL only. Unknown/stacked/commented SQL retains the write guard.\n  const guarded = Object.freeze({ prepare: sql => wrap(db.prepare(sql), /^\\s*SELECT\\b/i.test(sql) && !/;|--|\\/\\*/.test(sql)), batch });\n  guardedGenerations.set(guarded, expected);\r\n  return guarded;\r\n}\r\n\r\n/** Use authoritative activity times, never delivery time. Cross-cutoff sessions earn no game\r\n * rewards even if completed/imported later. Existing fitness validators still run separately. */\r\nexport function isGameRewardEligible({ startedAt, completedAt } = {}, current) {\r\n  metadata(current);\r\n  if (!validTime(completedAt)) return false;\r\n  if (current.gameResetAt === 0) return true; // untouched legacy generation\r\n  return validTime(startedAt) && startedAt > current.gameResetAt && completedAt >= startedAt;\r\n}\r\nexport function gameGenerationKey(key, expected) {\r\n  if (typeof key !== 'string' || !key) fail('game_generation_key_invalid');\r\n  return `${key}/game/${generation(expected)}`;\r\n}\r\n\r\n/** Keep private address preferences, never copy an old progression object into a fresh base. */\r\nexport function createFreshGameBase(owner, now, current, previous = {}) {\r\n  metadata(current);\r\n  if (typeof owner !== 'string' || !owner || !validTime(now)) fail('game_generation_base_invalid');\r\n  const place = p => p && p.consent === true && Number.isFinite(p.latitude) && Math.abs(p.latitude) <= 90\r\n    && Number.isFinite(p.longitude) && Math.abs(p.longitude) <= 180\r\n    ? { latitude: p.latitude, longitude: p.longitude, consent: true } : null;\r\n  const outpostPlaces = {};\r\n  for (const slot of ['gym', 'work']) {\r\n    const saved = place(previous.outposts?.[slot]?.place ?? previous.addressPreferences?.outpostPlaces?.[slot]);\r\n    if (saved) outpostPlaces[slot] = saved;\r\n  }\r\n  const home = place(previous.home);\r\n  return { ...freshBase(owner, now), blueprintPolicyVersion: BLUEPRINT_POLICY_VERSION,\r\n    gameGeneration: current.gameGeneration, gameResetAt: current.gameResetAt,\r\n    home, raidMappedAt: home ? now : null, addressPreferences: { outpostPlaces } };\r\n}\r\n","server/install-draft.mjs":"import {validIncomingCoach} from '../pending-coach.mjs';\r\nimport {fail} from './domain.mjs';\r\nconst COOKIE='__Host-coach-install',LIFETIME=3600000;\r\nconst hash=async value=>Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(value))),b=>b.toString(16).padStart(2,'0')).join('');\r\nconst tokenFrom=request=>request.headers.get('Cookie')?.split(';').map(v=>v.trim()).find(v=>v.startsWith(COOKIE+'='))?.slice(COOKIE.length+1)||'';\r\nconst cookie=(token,age=3600)=>`${COOKIE}=${token}; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=${age}`;\r\nconst json=(data,header)=>new Response(JSON.stringify(data),{headers:{'Content-Type':'application/json','Cache-Control':'no-store','X-Content-Type-Options':'nosniff',...(header?{'Set-Cookie':header}:{})}});\r\nexport async function installDraft(request,database,readBody,now=Date.now()){\r\n const token=tokenFrom(request),key=/^[a-f0-9]{64}$/.test(token)?await hash(token):null;\r\n if(request.method==='GET'){\r\n  const row=key?await database.prepare('SELECT data FROM install_drafts WHERE token_hash=? AND expires_at>?').bind(key,now).first():null;\r\n  return json({data:row?JSON.parse(row.data):null});\r\n }\r\n if(request.method==='DELETE'){\r\n  if(key)await database.prepare('DELETE FROM install_drafts WHERE token_hash=?').bind(key).run();\r\n  return json({cleared:true},cookie('',0));\r\n }\r\n if(request.method!=='POST')fail('Method not allowed.',405);\r\n const {data}=await readBody(request),raw=JSON.stringify(data);\r\n if(!validIncomingCoach(data))fail('Return to the creature studio and choose your coach again.');\r\n if(new TextEncoder().encode(raw).length>55000)fail('Your saved coach is too large.',413);\r\n const creator=await hash(request.headers.get('CF-Connecting-IP')||'local');\r\n const next=Array.from(crypto.getRandomValues(new Uint8Array(32)),b=>b.toString(16).padStart(2,'0')).join('');\r\n const results=await database.batch([\r\n  database.prepare('DELETE FROM install_drafts WHERE expires_at<=? OR token_hash=?').bind(now,key||''),\r\n  database.prepare('INSERT INTO install_drafts(token_hash,data,creator,expires_at) SELECT ?,?,?,? WHERE (SELECT COUNT(*) FROM install_drafts WHERE creator=?)<20 RETURNING token_hash').bind(await hash(next),raw,creator,now+LIFETIME,creator)\r\n ]);\r\n if(!results[1].results?.length)fail('Please wait before preparing another coach.',429);\r\n // Safari copies first-party cookies into newly installed apps; personal answers stay out of URLs.\r\n return json({ready:true},cookie(next));\r\n}\r\n","server/npc-rewards.mjs":"import {inspectGameGeneration,isGameRewardEligible} from './game-generation.mjs';\r\n// Award amounts are studio tuning. Clients supply a durable source ID, never money.\r\nimport {awardRunCurrency,readProgression,currencyReceiptKey} from '../modules/coach-world-rts/npc-progression.mjs';\r\nimport {deriveAccountPerformance} from './performance-progress.mjs';\r\nexport const RUN_CURRENCY_RULES=Object.freeze({version:1,completedWorkoutCredits:10});\r\n// World entry reconciles ordinary workouts, then claims their wallet rewards.\r\n// This is the real earning path until short roguelite rooms have server events.\r\nexport async function claimVerifiedWorkoutCurrency(db,base,{accountId,dataEpoch,now}){\r\n const game=await inspectGameGeneration(db);\r\n const rows=(await db.prepare(\"SELECT w.id,w.mode,w.source,w.client_workout_id,w.value,w.active,w.started_at,w.completed_at,w.performance_snapshot FROM world_sources s JOIN workouts w ON w.id=s.source_id AND w.user_id=s.owner WHERE s.owner=? AND s.epoch=? AND s.decision='accepted' AND w.source='server' AND w.completed_at>=? AND w.completed_at<=? ORDER BY w.completed_at,w.id LIMIT 5000\").bind(accountId,dataEpoch,now-30*86400000,now).all()).results;\r\n const receipts=readProgression(base).currencyReceipts,eligible=rows.filter(w=>isGameRewardEligible({startedAt:w.started_at,completedAt:w.completed_at},game)&&!Object.hasOwn(receipts,currencyReceiptKey('workout',w.id))&&deriveAccountPerformance({workouts:[w]}).totalXp>0);\r\n let next=base,amount=0;const creditedWorkoutIds=[];\r\n for(const w of eligible.slice(0,100)){const credit=awardRunCurrency(next,{runId:'workout',eventId:w.id,amount:RUN_CURRENCY_RULES.completedWorkoutCredits,accountId},{now});if(!credit.result.ok)throw RangeError(credit.result.reason);next=credit.base;if(credit.result.awarded){amount+=credit.result.amount;creditedWorkoutIds.push(w.id);}}\r\n return {base:next,result:{ok:true,awarded:creditedWorkoutIds.length,amount,balance:readProgression(next).balance,creditedWorkoutIds,pending:Math.max(0,eligible.length-100)}};\r\n}\r\nconst validId=v=>typeof v==='string'&&/^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$/.test(v);\r\nexport async function creditVerifiedRunReward(db,base,input,{accountId,now}){\r\n if(!validId(input.runId)||!validId(input.eventId)||!validId(input.workoutId))throw RangeError('npc_reward_receipt_required');\r\n if(Object.hasOwn(input,'amount'))throw RangeError('npc_reward_amount_is_server_owned');\r\n const workout=await db.prepare('SELECT id,mode,source,client_workout_id,value,active,started_at,completed_at,performance_snapshot FROM workouts WHERE user_id=? AND id=? AND completed_at IS NOT NULL').bind(accountId,input.workoutId).first();\r\n if(!workout||!Number.isSafeInteger(workout.completed_at)||workout.completed_at>now)throw RangeError('npc_reward_workout_not_found');\r\n if(!isGameRewardEligible({startedAt:workout.started_at,completedAt:workout.completed_at},await inspectGameGeneration(db)))throw RangeError('npc_reward_workout_before_reset');\r\n if(workout.source!=='server')throw RangeError('npc_reward_workout_ineligible');\r\n const source=await db.prepare('SELECT decision FROM world_sources WHERE owner=? AND epoch=? AND source_id=?').bind(accountId,input.dataEpoch,input.workoutId).first();\r\n if(!source)throw RangeError('npc_reward_reconcile_required');\r\n if(source.decision!=='accepted')throw RangeError('npc_reward_workout_ineligible');\r\n // Reuse the account XP validator: unknown modes, fabricated imports, and empty\r\n // records do not become currency simply by existing in the workouts table.\r\n const progress=deriveAccountPerformance({workouts:[workout]});\r\n if(!(progress.totalXp>0))throw RangeError('npc_reward_workout_ineligible');\r\n // Source identity is global within this owner/epoch, regardless of caller run or\r\n // event labels. Failing/restarting the run cannot repay the same workout.\r\n const credited=awardRunCurrency(base,{runId:'workout',eventId:workout.id,amount:RUN_CURRENCY_RULES.completedWorkoutCredits,accountId},{now});\r\n if(!credited.result.ok)throw RangeError(credited.result.reason);\r\n return {...credited,result:{...credited.result,runId:input.runId,eventId:input.eventId,workoutId:workout.id,balance:credited.base.npcProgression.balance}};\r\n}\r\n","server/onboarding.mjs":"import {inspectAccountDataEpoch} from './account-data-epochs.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {validateOnboarding,dailyTargets,calendarDay} from '../onboarding-domain.mjs';\r\nexport async function readOnboarding(database,user,now=Date.now()){\r\n const row=await database.prepare('SELECT data,start_day,completed_at,revision FROM onboarding WHERE user_id=?').bind(user).first();\r\n if(!row)return null;const data=JSON.parse(row.data);return {data,startDay:row.start_day,completedAt:row.completed_at,revision:row.revision,targets:dailyTargets(data.profile,row.start_day,now)};\r\n}\r\nexport async function saveOnboarding(database,user,input,now=Date.now(),{dataEpoch=1}={}){\r\n if(!Number.isSafeInteger(input.revision)||input.revision<0)throw Object.assign(Error('Refresh your coach settings before saving.'),{status:400});\r\n const data=validateOnboarding(input.data),appearance=Object.fromEntries(Object.entries(data.appearance).map(([k,v])=>[k,JSON.stringify(v)])),token=crypto.randomUUID();\r\n const writes=await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[\r\n  database.prepare('INSERT INTO onboarding(user_id,data,start_day,completed_at,updated_at,revision,write_token) SELECT ?,?,?,?,?,1,? WHERE ?=0 OR EXISTS(SELECT 1 FROM onboarding WHERE user_id=?) ON CONFLICT(user_id) DO UPDATE SET data=excluded.data,updated_at=excluded.updated_at,revision=onboarding.revision+1,write_token=excluded.write_token WHERE onboarding.revision=? RETURNING revision').bind(user,JSON.stringify(data),calendarDay(now,data.profile.timezone),now,now,token,input.revision,user,input.revision),\r\n  database.prepare(\"INSERT INTO profiles(user_id,data,revision,updated_at) SELECT ?,?,1,? WHERE EXISTS(SELECT 1 FROM onboarding WHERE user_id=? AND write_token=?) ON CONFLICT(user_id) DO UPDATE SET data=json_patch(profiles.data,excluded.data),revision=profiles.revision+1,updated_at=excluded.updated_at\").bind(user,JSON.stringify(appearance),now,user,token)\r\n ]});\r\n // A stale request writes neither the onboarding row nor its appearance snapshot.\r\n if(!writes[0].results?.length)throw Object.assign(Error('Coach settings changed on another device. Refresh before saving again.'),{status:409});\r\n const onboarding=await readOnboarding(database,user,now);\r\n if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==dataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});\r\n return {onboarding,appearance};\r\n}\r\n","server/performance-progress.mjs":"import {inspectGameGeneration,isGameRewardEligible} from './game-generation.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nimport {exerciseDifficulty} from '../performance-catalog.mjs';\r\nimport {holdXp,repXp,GENTLE_BLOCK_XP} from '../progression-rules.mjs';\r\nimport {importedPerformanceXp} from '../performance-import-codec.mjs';\r\nimport {validateImportSnapshot} from '../workout-import-codec.mjs';\r\nimport {readPerformanceProgress,recordPerformanceSession,recordDailyActivity,workoutEligibility,choosePerformancePaths} from '../performance-progress.mjs';\r\nimport {CHOOSABLE_TRACKS} from '../performance-catalog.mjs';\r\nimport {inspectAccountDataEpoch} from './account-data-epochs.mjs';\r\n\r\nexport function performanceDay(timestamp,timezone='UTC'){\r\n const parts=new Intl.DateTimeFormat('en-US',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date(timestamp));\r\n return ['year','month','day'].map(type=>parts.find(p=>p.type===type).value).join('-');\r\n}\r\n/** Request-local derivation from authenticated durable records. Client XP,\r\n * claimed ownership, cosmetic level and coach bonuses never authorize rewards. */\r\nexport function deriveAccountPerformance({workouts=[],breathing=[],meals=[],timezone='UTC',paths=[]}={}){\r\n const cache=new Map(),options={owner:'server-derivation',storage:{getItem:k=>cache.get(k)??null,setItem:(k,v)=>cache.set(k,v)}};\r\n if(paths.length)choosePerformancePaths(paths,options);\r\n const activities=[...breathing.filter(b=>Number.isSafeInteger(b.completed_at)).map(b=>({time:b.completed_at,kind:'meditation',id:b.id})),...meals.map(m=>({time:Date.parse(m.eaten_at),kind:'food',id:m.id})).filter(m=>Number.isFinite(m.time)),...workouts.filter(w=>Number.isSafeInteger(w.completed_at)).map(w=>({time:w.completed_at,kind:'workout',row:w}))].sort((a,b)=>a.time-b.time||String(a.id??a.row.id).localeCompare(String(b.id??b.row.id)));\r\n for(const activity of activities){const day=performanceDay(activity.time,timezone);if(activity.kind!=='workout'){recordDailyActivity(activity.kind,{day,id:activity.id},options);continue;}\r\n  const w=activity.row,e=EXERCISES[w.mode];if(!e)continue;\r\n  let record;\r\n  if(w.performance_snapshot){try{const snapshot=validateImportSnapshot(JSON.parse(w.performance_snapshot));if(!snapshot.performance||snapshot.mode!==w.mode||snapshot.clientWorkoutId!==(w.client_workout_id??w.id)||snapshot.value!==w.value||snapshot.activeSeconds!==w.active||snapshot.completedAt!==w.completed_at||(w.started_at!=null&&snapshot.startedAt!==w.started_at))continue;const p=snapshot.performance;record={...p,id:w.client_workout_id??w.id,mode:w.mode,value:snapshot.value,activeSeconds:snapshot.activeSeconds,xpBase:importedPerformanceXp(snapshot),day};}catch{continue;}}\r\n  else if(w.source==='server'){\r\n   const difficulty=exerciseDifficulty(w.mode),kind=e.kind==='hold'?'hold':e.kind==='reps'&&e.group!=='cardio'?'reps':'gentle',active=Math.max(0,Math.min(1800,Number(w.active)||0)),value=Math.max(0,Number(w.value)||0);\r\n   let xpBase=kind==='reps'?repXp({difficulty,to:Math.floor(value)}):kind==='gentle'?Math.floor(active/15)*GENTLE_BLOCK_XP:0;\r\n   if(kind==='hold')for(let start=0;start+15<=active;start+=15)xpBase+=holdXp({difficulty,from:start,to:start+15,continuousAtFrom:0});\r\n   record={id:w.id,mode:w.mode,kind,difficulty,value,activeSeconds:active,maxContinuousSeconds:0,perDifficultyContinuous:{},xpBase,day};\r\n  }else continue;\r\n  if(record.xpBase<=0||!workoutEligibility(record.mode,{...options,day,kind:record.kind}).allowed)continue;\r\n  recordPerformanceSession(record,options);\r\n }\r\n return readPerformanceProgress(options);\r\n}\r\nexport async function readAccountPerformance(database,user,timezone){\r\n if(!timezone){const row=await database.prepare('SELECT data FROM onboarding WHERE user_id=?').bind(user).first();try{timezone=JSON.parse(row?.data||'{}').profile?.timezone||'UTC';}catch{timezone='UTC';}}\r\n const epoch=(await inspectAccountDataEpoch(database,user)).currentDataEpoch,game=await inspectGameGeneration(database);\r\n const [workouts,breathing,meals,choice]=await Promise.all([\r\n  database.prepare('SELECT id,mode,source,client_workout_id,value,active,started_at,completed_at,performance_snapshot FROM workouts WHERE user_id=? AND completed_at IS NOT NULL ORDER BY completed_at,id').bind(user).all(),\r\n  database.prepare('SELECT id,started_at,completed_at FROM breathing_sessions WHERE user_id=? AND completed_at IS NOT NULL').bind(user).all(),\r\n  database.prepare('SELECT id,eaten_at FROM meals WHERE user_id=?').bind(user).all(),\r\n  database.prepare('SELECT paths FROM workout_path_choices WHERE user_id=? AND data_epoch=?').bind(user,epoch).first(),\r\n ]);\r\n let paths=[];if(choice){try{paths=JSON.parse(choice.paths);}catch{}if(!Array.isArray(paths)||paths.length!==2||new Set(paths).size!==2||!paths.every(id=>CHOOSABLE_TRACKS.includes(id)))throw Error('Stored workout path choice is invalid.');}\r\n const performance=deriveAccountPerformance({workouts:workouts.results.filter(w=>isGameRewardEligible({startedAt:w.started_at,completedAt:w.completed_at},game)),breathing:breathing.results.filter(w=>isGameRewardEligible({startedAt:w.started_at,completedAt:w.completed_at},game)),meals:meals.results.filter(w=>isGameRewardEligible({startedAt:Date.parse(w.eaten_at),completedAt:Date.parse(w.eaten_at)},game)),timezone,paths});\r\n const bonus=await database.prepare('SELECT COALESCE(SUM(xp),0) AS xp FROM world_drop_claims WHERE owner=? AND epoch=?').bind(user,epoch).first();\r\n performance.worldBonusXp=Number(bonus?.xp)||0;performance.totalXp+=performance.worldBonusXp;return performance;\r\n}\r\n","server/push.mjs":"import {buildPushPayload} from '@block65/webcrypto-web-push';\r\nimport {db} from './db.mjs';\r\nimport {reminderMessage} from '../reminder-settings.mjs';\r\nimport {isDue,fail} from './domain.mjs';\r\nimport {claimNotificationSlot} from './reminder-plan.mjs';\r\nimport {missedTrainingDays,coachReminder} from '../reminder-plan.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nexport function subscriptionInput(v){let url;try{url=new URL(v.endpoint);}catch{fail('Invalid notification subscription.');}const host=url.hostname;if(url.protocol!=='https:'||url.port||url.username||url.password||!(host==='fcm.googleapis.com'||host==='updates.push.services.mozilla.com'||host.endsWith('.push.services.mozilla.com')||host==='web.push.apple.com'||host.endsWith('.notify.windows.com')))fail('This notification provider is not supported.');for(const [k,size] of [['p256dh',87],['auth',22]])if(typeof v.keys?.[k]!=='string'||!new RegExp(`^[A-Za-z0-9_-]{${size}}={0,2}$`).test(v.keys[k]))fail('Invalid notification keys.');return {endpoint:url.href,keys:v.keys,expirationTime:null};}\r\nconst stale=()=>Object.assign(Error('Notification generation changed.'),{code:'stale_delivery',status:409});\r\nconst staleError=error=>['stale_delivery','target_epoch_mismatch'].includes(error?.code);\r\nexport async function sendPush(env,sub,message,{ttl=600,ownerId,dataEpoch,subscriptionData,subscriptionCreatedAt}={}){\r\n if(typeof ownerId!=='string'||!ownerId||!Number.isSafeInteger(dataEpoch)||dataEpoch<1||typeof subscriptionData!=='string'||!Number.isSafeInteger(subscriptionCreatedAt)||subscriptionCreatedAt<0)throw stale();\r\n const database=db(env),payload=await buildPushPayload({data:JSON.stringify(message),options:{ttl}},sub,{subject:env.VAPID_SUBJECT,publicKey:env.VAPID_PUBLIC_KEY,privateKey:env.VAPID_PRIVATE_KEY});\r\n const current=await database.prepare('SELECT s.endpoint FROM subscriptions s LEFT JOIN account_data_epochs e ON e.owner_id=s.user_id WHERE s.endpoint=? AND s.user_id=? AND s.data=? AND s.created_at=? AND COALESCE(e.epoch,1)=?').bind(sub.endpoint,ownerId,subscriptionData,subscriptionCreatedAt,dataEpoch).first();\r\n if(!current)throw stale();\r\n const result=await fetch(sub.endpoint,{...payload,redirect:'manual',signal:AbortSignal.timeout(15000)});\r\n if(result.status===404||result.status===410){\r\n  try{await epochFencedBatch(database,{ownerId,expectedDataEpoch:dataEpoch,now:Date.now(),statements:[database.prepare('DELETE FROM subscriptions WHERE endpoint=? AND user_id=? AND data=? AND created_at=?').bind(sub.endpoint,ownerId,subscriptionData,subscriptionCreatedAt)]});}\r\n  catch(error){if(!staleError(error))throw error;}\r\n }\r\n if(!result.ok)throw Error('Notification service did not accept delivery.');return result.status;\r\n}\r\nexport async function runReminders(env,now=Date.now()){\r\n const database=db(env);await database.prepare(\"INSERT INTO system(key,value) VALUES('scheduler_tick',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value\").bind(String(now)).run();\r\n if(!env.VAPID_PRIVATE_KEY)return {sent:0,configured:false};\r\n const rows=(await database.prepare('SELECT r.*,COALESCE(e.epoch,1) AS data_epoch FROM reminders r LEFT JOIN account_data_epochs e ON e.owner_id=r.user_id WHERE r.enabled=1').all()).results;let sent=0,failed=0;\r\n for(const r of rows){\r\n  const day=isDue(r,now);if(!day)continue;\r\n  const subs=(await database.prepare('SELECT * FROM subscriptions WHERE user_id=?').bind(r.user_id).all()).results;\r\n  if(!subs.length)continue;\r\n  try{if(!await claimNotificationSlot(database,r.user_id,day,r.id,{dataEpoch:r.data_epoch,now,requireReminder:true}))continue;}catch(error){if(staleError(error))continue;throw error;}\r\n  let training=null;try{const status=await database.prepare('SELECT value FROM system WHERE key=?').bind('training:'+r.user_id).first();training=status?JSON.parse(status.value):null;}catch{}\r\n  const message=r.kind==='workout'?coachReminder(missedTrainingDays(training,now,r.days_per_week),r.tone):reminderMessage(r.kind,r.tone);\r\n  const commit=statements=>epochFencedBatch(database,{ownerId:r.user_id,expectedDataEpoch:r.data_epoch,now,statements});\r\n  for(const s of subs){\r\n   let claimed;\r\n   try{\r\n    const results=await commit([database.prepare(\"INSERT INTO deliveries(reminder_id,day,endpoint,status,updated_at) SELECT ?,?,?,'sending',? WHERE EXISTS(SELECT 1 FROM reminders WHERE id=? AND user_id=? AND enabled=1) AND EXISTS(SELECT 1 FROM subscriptions WHERE endpoint=? AND user_id=? AND data=? AND created_at=?) ON CONFLICT(reminder_id,day,endpoint) DO UPDATE SET status='sending',updated_at=excluded.updated_at WHERE deliveries.status!='sent' AND deliveries.updated_at<? RETURNING reminder_id\").bind(r.id,day,s.endpoint,now,r.id,r.user_id,s.endpoint,r.user_id,s.data,s.created_at,now-120000)]);\r\n    claimed=results[0].results?.length>0;\r\n   }catch(error){if(staleError(error))continue;throw error;}\r\n   if(!claimed)continue;\r\n   let deliveryStatus='sent';\r\n   try{await sendPush(env,JSON.parse(s.data),{title:'MOM // POD CHECK',body:message,tag:`${r.id}-${day}`,url:`/pose.html?panel=${r.kind==='meal'?'meals':'reminders'}`,kind:'reminder-'+r.kind},{ownerId:r.user_id,dataEpoch:r.data_epoch,subscriptionData:s.data,subscriptionCreatedAt:s.created_at});sent++;}\r\n   catch(error){if(staleError(error))continue;failed++;deliveryStatus='failed';}\r\n   try{await commit([database.prepare('UPDATE deliveries SET status=?,updated_at=? WHERE reminder_id=? AND day=? AND endpoint=? AND updated_at=? AND EXISTS(SELECT 1 FROM subscriptions WHERE endpoint=? AND user_id=? AND data=? AND created_at=?)').bind(deliveryStatus,now,r.id,day,s.endpoint,now,s.endpoint,r.user_id,s.data,s.created_at)]);}catch(error){if(!staleError(error))throw error;}\r\n  }\r\n }\r\n await database.prepare('DELETE FROM deliveries WHERE updated_at<?').bind(now-35*86400000).run();await database.prepare(\"DELETE FROM system WHERE key LIKE 'notify-budget:%' AND substr(key,-10)<?\").bind(new Date(now-35*86400000).toISOString().slice(0,10)).run();return {sent,failed,configured:true};\r\n}\r\n","server/release-email.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {RELEASE} from '../release-info.mjs';\r\nimport {fail} from './domain.mjs';\r\n\r\nconst DAY=86400000;\r\nconst hash=async value=>Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(value))),x=>x.toString(16).padStart(2,'0')).join('');\r\nconst token=()=>crypto.randomUUID().replaceAll('-','')+crypto.randomUUID().replaceAll('-','');\r\nconst escape=value=>String(value).replace(/[&<>\"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\r\nexport const emailConfigured=env=>(!!env.EMAIL?.send||!!env.RESEND_API_KEY)&&/^[^\\s<>@]+@[^\\s<>@]+\\.[^\\s<>@]+$/.test(env.RELEASE_EMAIL_FROM||'')&&/^https:\\/\\//.test(env.RELEASE_ORIGIN||'');\r\nexport function emailInput(value){const email=typeof value==='string'?value.trim().toLowerCase():'';if(email.length>254||!/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?\\.[a-z]{2,63}$/.test(email))fail('Enter a valid email address.');return email;}\r\nfunction link(env,action,value){return new URL(`/api/updates/${action}?token=${value}`,env.RELEASE_ORIGIN).href;}\r\nasync function mail(env,email,subject,text,html,unsubscribe){\r\n const content={subject,text,html,...(unsubscribe?{headers:{'List-Unsubscribe':`<${unsubscribe}>`,'List-Unsubscribe-Post':'List-Unsubscribe=One-Click'}}:{})};\r\n if(env.EMAIL?.send)return env.EMAIL.send({from:{email:env.RELEASE_EMAIL_FROM,name:'MOM Dispatch'},to:email,...content});\r\n const response=await fetch('https://api.resend.com/emails',{method:'POST',redirect:'manual',signal:AbortSignal.timeout(15000),headers:{'Authorization':`Bearer ${env.RESEND_API_KEY}`,'Content-Type':'application/json','Idempotency-Key':`coach-${await hash(email+subject+text)}`},body:JSON.stringify({from:`MOM Dispatch <${env.RELEASE_EMAIL_FROM}>`,to:[email],...content})});\r\n if(!response.ok)throw new Error('Email provider did not accept the message.');\r\n const result=await response.json();if(!result.id)throw new Error('Email provider returned no message ID.');return {messageId:result.id};\r\n}\r\nexport async function emailSubscription(env,user,method,input={},now=Date.now(),{dataEpoch=1}={}){\r\n const db=env.DB;\r\n const commit=statements=>epochFencedBatch(db,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements});\r\n const first=async statement=>(await commit([statement]))[0].results?.[0]??null;\r\n if(method==='GET'){const row=await db.prepare('SELECT email,enabled,confirmed_at FROM release_subscribers WHERE user_id=?').bind(user).first();return {configured:emailConfigured(env),email:row?.email||null,enabled:!!row?.enabled,confirmedAt:row?.confirmed_at||null};}\r\n if(method==='DELETE'){await commit([db.prepare('DELETE FROM release_subscribers WHERE user_id=?').bind(user),db.prepare('DELETE FROM release_deliveries WHERE user_id=?').bind(user)]);return {enabled:false};}\r\n if(method!=='POST')fail('Method not allowed.',405);\r\n if(!emailConfigured(env))fail('Email delivery is not connected yet. In-app updates are available now.',503);\r\n const email=emailInput(input.email),existing=await db.prepare('SELECT * FROM release_subscribers WHERE user_id=?').bind(user).first();\r\n if(existing?.email===email&&existing.enabled)return {message:'Release emails are already on.',enabled:true};\r\n // Atomic limits survive concurrent requests and apply to both account and destination.\r\n for(const key of [`release-email-user:${user}`,`release-email-address:${await hash(email)}`]){\r\n  const claim=await first(db.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value WHERE CAST(system.value AS INTEGER)<? RETURNING key').bind(key,String(now),now-3600000));\r\n  if(!claim)fail('Please wait an hour before requesting another confirmation email.',429);\r\n }\r\n const hour=Math.floor(now/3600000),global=await first(db.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=CAST(system.value AS INTEGER)+1 WHERE CAST(system.value AS INTEGER)<30 RETURNING value').bind(`release-email-hour:${hour}`,'1'));\r\n if(!global)fail('Email confirmation is busy. Please try again later.',429);\r\n const confirm=token(),unsubscribe=token(),confirmHash=await hash(confirm);\r\n await commit([db.prepare('INSERT INTO release_subscribers(user_id,email,enabled,confirm_hash,expires_at,unsubscribe_token,updated_at,confirmed_at,last_release) VALUES(?,?,0,?,?,?,?,NULL,?) ON CONFLICT(user_id) DO UPDATE SET email=excluded.email,enabled=0,confirm_hash=excluded.confirm_hash,expires_at=excluded.expires_at,unsubscribe_token=excluded.unsubscribe_token,updated_at=excluded.updated_at,confirmed_at=NULL,last_release=excluded.last_release').bind(user,email,confirmHash,now+DAY,unsubscribe,now,RELEASE.id)]);\r\n const current=await db.prepare('SELECT s.user_id FROM release_subscribers s LEFT JOIN account_data_epochs e ON e.owner_id=s.user_id WHERE s.user_id=? AND s.confirm_hash=? AND COALESCE(e.epoch,1)=?').bind(user,confirmHash,dataEpoch).first();if(!current)throw Object.assign(Error('Account generation changed.'),{status:409,code:'target_epoch_mismatch'});\r\n const confirmUrl=link(env,'confirm',confirm),stopUrl=link(env,'unsubscribe',unsubscribe);\r\n try {await mail(env,email,'MOM Dispatch · Confirm your transmission channel',`MOM here. Your training pod is ready to receive transmissions. Confirm your email to hear when a Coach app update is ready:\\n${confirmUrl}\\n\\nThis link expires in 24 hours. If you did not request this, ignore this email.\\nCancel: ${stopUrl}`,`<p>MOM here. Your training pod is ready to receive transmissions. Confirm your email to hear when a Coach app update is ready.</p><p><a href=\"${escape(confirmUrl)}\">Confirm update emails</a></p><p>This link expires in 24 hours. If you did not request this, ignore this email.</p><p><a href=\"${escape(stopUrl)}\">Cancel</a></p>`,stopUrl);}\r\n catch{try{await commit([db.prepare('DELETE FROM release_subscribers WHERE user_id=? AND confirm_hash=?').bind(user,confirmHash)]);}catch(error){if(error.code!=='target_epoch_mismatch')throw error;}fail('The confirmation email could not be sent. Please try again later.',503);}\r\n return {message:'Check your inbox and confirm your email. Release emails stay off until you confirm.',enabled:false};\r\n}\r\nexport async function emailLinkAction(env,action,value,now=Date.now()){\r\n if(!/^[a-f0-9]{64}$/.test(value||''))fail('This email link is invalid.',400);\r\n if(action==='unsubscribe'){await env.DB.prepare('DELETE FROM release_subscribers WHERE unsubscribe_token=?').bind(value).run();return {message:'Update emails are off. You can close this page.'};}\r\n if(action!=='confirm')fail('Not found.',404);\r\n const row=await env.DB.prepare('UPDATE release_subscribers SET enabled=1,confirmed_at=?,confirm_hash=NULL,expires_at=NULL,updated_at=?,last_release=? WHERE confirm_hash=? AND expires_at>? RETURNING user_id').bind(now,now,RELEASE.id,await hash(value),now).first();\r\n if(!row)fail('This confirmation link has expired or was already used. Open Coach to request another.',410);\r\n return {message:'Your email is confirmed. We’ll email you when a new app update is ready.'};\r\n}\r\nexport function emailLinkPage(action,value,message){\r\n const valid=/^[a-f0-9]{64}$/.test(value||''),title=action==='confirm'?'Confirm app update emails':'Stop app update emails';\r\n const form=valid&&!message?`<form method=\"post\"><button>${action==='confirm'?'Confirm email':'Stop update emails'}</button></form>`:'';\r\n return new Response(`<!doctype html><html lang=\"en\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title} · MYR5 Coach</title><style>body{font:18px system-ui;max-width:36rem;margin:12vh auto;padding:24px;background:#f5f3eb;color:#252820}button,a{font:inherit}button{padding:14px 22px;border:0;border-radius:12px;background:#304c32;color:white;cursor:pointer}a{color:#304c32}</style><h1>${title}</h1><p>${escape(message||(valid?'Choose below to save your email preference.':'This email link is invalid.'))}</p>${form}<p><a href=\"/pose.html?panel=install\">Open Coach</a></p></html>`,{status:valid?200:400,headers:{'Content-Type':'text/html; charset=utf-8','Cache-Control':'no-store','Referrer-Policy':'no-referrer','X-Content-Type-Options':'nosniff','Content-Security-Policy':\"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'\"}});\r\n}\r\nexport async function runReleaseEmails(env,now=Date.now()){\r\n if(!emailConfigured(env))return {configured:false,sent:0,failed:0};\r\n // Deploying the sender ahead of the app must never announce an unpublished build.\r\n const live=await fetch(new URL('/api/releases/current',env.RELEASE_ORIGIN),{redirect:'manual',signal:AbortSignal.timeout(10000),headers:{'Cache-Control':'no-cache'}});\r\n if(!live.ok||(await live.json()).id!==RELEASE.id)return {sent:0,failed:0,waitingForPublication:true};\r\n const rows=(await env.DB.prepare('SELECT s.*,COALESCE(e.epoch,1) AS data_epoch FROM release_subscribers s LEFT JOIN account_data_epochs e ON e.owner_id=s.user_id WHERE enabled=1 AND confirmed_at IS NOT NULL AND (last_release IS NULL OR last_release!=?) AND NOT EXISTS (SELECT 1 FROM release_deliveries d WHERE d.user_id=s.user_id AND d.release_id=?) LIMIT 10').bind(RELEASE.id,RELEASE.id).all()).results;\r\n let sent=0,failed=0;\r\n for(const row of rows){\r\n  const commit=statements=>epochFencedBatch(env.DB,{ownerId:row.user_id,expectedDataEpoch:row.data_epoch,now,statements});\r\n  let claim;\r\n  try{claim=(await commit([env.DB.prepare(\"INSERT INTO release_deliveries(user_id,release_id,status,updated_at) SELECT user_id,?,'claimed',? FROM release_subscribers WHERE user_id=? AND enabled=1 AND unsubscribe_token=? ON CONFLICT(user_id,release_id) DO NOTHING RETURNING user_id\").bind(RELEASE.id,now,row.user_id,row.unsubscribe_token)]))[0].results?.[0];}\r\n  catch(error){if(error.code==='target_epoch_mismatch')continue;throw error;}\r\n  if(!claim)continue;\r\n  const current=await env.DB.prepare('SELECT s.email FROM release_subscribers s LEFT JOIN account_data_epochs e ON e.owner_id=s.user_id WHERE s.user_id=? AND s.enabled=1 AND s.unsubscribe_token=? AND COALESCE(e.epoch,1)=?').bind(row.user_id,row.unsubscribe_token,row.data_epoch).first();\r\n  if(!current)continue;\r\n  const stopUrl=link(env,'unsubscribe',row.unsubscribe_token),appUrl=new URL('/pose.html?panel=install',env.RELEASE_ORIGIN).href;\r\n  let deliveryStatus='sent';\r\n  try {\r\n   await mail(env,row.email,`MOM Dispatch · Your training pod has an upgrade`,`TRANSMISSION FROM MOM\\n\\nYour training pod has an upgrade.\\n${RELEASE.title}\\n\\n${RELEASE.notes.join('\\n')}\\n\\nOpen Coach to update: ${appUrl}\\n\\nYou opened this channel for Coach app updates. Unsubscribe: ${stopUrl}`,`<p>MOM DISPATCH</p><h1>Your training pod has an upgrade.</h1><h2>${escape(RELEASE.title)}</h2><ul>${RELEASE.notes.map(n=>`<li>${escape(n)}</li>`).join('')}</ul><p><a href=\"${escape(appUrl)}\">Open Coach to update</a></p><p>You opened this channel for Coach app updates. <a href=\"${escape(stopUrl)}\">Unsubscribe</a>.</p>`,stopUrl);\r\n   sent++;\r\n  }catch{failed++;deliveryStatus='failed';}\r\n  const updates=[env.DB.prepare('UPDATE release_deliveries SET status=?,updated_at=? WHERE user_id=? AND release_id=? AND updated_at=? AND EXISTS(SELECT 1 FROM release_subscribers WHERE user_id=? AND unsubscribe_token=?)').bind(deliveryStatus,now,row.user_id,RELEASE.id,now,row.user_id,row.unsubscribe_token)];\r\n  if(deliveryStatus==='sent')updates.push(env.DB.prepare('UPDATE release_subscribers SET last_release=? WHERE user_id=? AND unsubscribe_token=?').bind(RELEASE.id,row.user_id,row.unsubscribe_token));\r\n  try{await commit(updates);}catch(error){if(error.code!=='target_epoch_mismatch')throw error;}\r\n }\r\n // Delivery claims are retained after uncertain sends so the next tick cannot duplicate mail.\r\n await env.DB.batch([env.DB.prepare('DELETE FROM release_subscribers WHERE enabled=0 AND updated_at<?').bind(now-7*DAY),env.DB.prepare(\"DELETE FROM system WHERE key LIKE 'release-email-%' AND ((key LIKE 'release-email-hour:%' AND CAST(substr(key,20) AS INTEGER)<?) OR (key NOT LIKE 'release-email-hour:%' AND CAST(value AS INTEGER)<?))\").bind(Math.floor((now-2*DAY)/3600000),now-2*DAY)]);\r\n return {configured:true,sent,failed};\r\n}\r\n","server/release-push.mjs":"import {db} from './db.mjs';\r\nimport {sendPush} from './push.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nconst stale=error=>['target_epoch_mismatch','stale_delivery'].includes(error?.code);\r\nexport async function runReleasePush(env,now=Date.now(),published=null){ // published: the main Worker passes RELEASE (a Worker can't fetch its own custom domain)\r\n if(!env.RELEASE_ORIGIN||!env.VAPID_PUBLIC_KEY||!env.VAPID_PRIVATE_KEY)return {sent:0,failed:0,configured:false};\r\n const startedAt=Date.now(),origin=new URL(env.RELEASE_ORIGIN);\r\n if(origin.protocol!=='https:'||origin.username||origin.password)throw Error('Invalid release origin.');\r\n let release=published;\r\n if(!release){const response=await fetch(new URL('/api/releases/current',origin).href,{redirect:'manual',cache:'no-store',signal:AbortSignal.timeout(15000)});\r\n if(!response.ok)throw Error('Could not check the published Coach release.');\r\n release=await response.json();}if(typeof release?.id!=='string'||!/^[-a-zA-Z0-9_.]{1,200}$/.test(release.id)||typeof release.title!=='string'||!release.title.trim())throw Error('Invalid published Coach release.');\r\n const database=db(env),retryBefore=now-120000;\r\n const rows=(await database.prepare(\"SELECT s.endpoint,s.user_id,s.data,s.created_at,COALESCE(e.epoch,1) AS data_epoch FROM subscriptions s LEFT JOIN account_data_epochs e ON e.owner_id=s.user_id LEFT JOIN release_push_deliveries d ON d.endpoint=s.endpoint AND d.release_id=? WHERE d.endpoint IS NULL OR (d.status!='sent' AND d.updated_at<?) ORDER BY s.created_at,s.endpoint LIMIT 50\").bind(release.id,retryBefore).all()).results;\r\n let sent=0,failed=0;\r\n for(const row of rows){\r\n  const claimAt=now+Date.now()-startedAt,commit=statements=>epochFencedBatch(database,{ownerId:row.user_id,expectedDataEpoch:row.data_epoch,now:claimAt,statements});\r\n  let claim;\r\n  try{claim=(await commit([database.prepare(\"INSERT INTO release_push_deliveries(endpoint,release_id,status,updated_at) SELECT endpoint,?,'sending',? FROM subscriptions WHERE endpoint=? AND user_id=? AND data=? AND created_at=? ON CONFLICT(endpoint,release_id) DO UPDATE SET status='sending',updated_at=excluded.updated_at WHERE release_push_deliveries.status!='sent' AND release_push_deliveries.updated_at<? RETURNING endpoint\").bind(release.id,claimAt,row.endpoint,row.user_id,row.data,row.created_at,claimAt-120000)]))[0].results?.[0];}\r\n  catch(error){if(stale(error))continue;throw error;}\r\n  if(!claim)continue;\r\n  let deliveryStatus='sent';\r\n  try{\r\n   await sendPush(env,JSON.parse(row.data),{kind:'app-update',title:'Coach update ready to download',body:'A new version of MYR5 Coach is ready. Tap to download the update.',tag:'myr5-update-'+release.id,url:'/pose.html?panel=install&update='+encodeURIComponent(release.id)},{ttl:86400,ownerId:row.user_id,dataEpoch:row.data_epoch,subscriptionData:row.data,subscriptionCreatedAt:row.created_at});sent++;\r\n  }catch(error){if(stale(error))continue;failed++;deliveryStatus='failed';}\r\n  try{await commit([database.prepare('UPDATE release_push_deliveries SET status=?,updated_at=? WHERE endpoint=? AND release_id=? AND updated_at=? AND EXISTS(SELECT 1 FROM subscriptions WHERE endpoint=? AND user_id=? AND data=? AND created_at=?)').bind(deliveryStatus,claimAt,row.endpoint,release.id,claimAt,row.endpoint,row.user_id,row.data,row.created_at)]);}\r\n  catch(error){if(!stale(error))throw error;}\r\n }\r\n await database.prepare('DELETE FROM release_push_deliveries WHERE release_id!=? AND updated_at<?').bind(release.id,now-90*86400000).run();\r\n const result={sent,failed,configured:true,releaseId:release.id};await database.prepare(\"INSERT INTO system(key,value) VALUES('release_push_tick',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value\").bind(JSON.stringify({...result,checkedAt:now})).run();return result;\r\n}\r\n","server/reminder-plan.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {reminderInput,fail} from './domain.mjs';\r\nexport async function readCoachPlan(database,user){const row=await database.prepare('SELECT value FROM system WHERE key=?').bind('coach-plan:'+user).first();if(!row)return {enabled:false,count:3,times:['09:00','14:00','19:00'],tone:'cheeky',ids:[]};try{return JSON.parse(row.value);}catch{return {enabled:false,count:3,times:['09:00','14:00','19:00'],tone:'cheeky',ids:[]};}}\r\nexport async function saveCoachPlan(database,user,input,{dataEpoch=1}={}){\r\n if(!Number.isInteger(input.count)||input.count<1||input.count>3||!Array.isArray(input.times)||input.times.length!==input.count||new Set(input.times).size!==input.count)fail('Choose one to three different reminder times.');\r\n const prior=await readCoachPlan(database,user),ids=Array.from({length:input.count},(_,i)=>prior.ids?.[i]||crypto.randomUUID());\r\n const rows=input.times.map((time,index)=>reminderInput({id:ids[index],kind:'workout',time,timezone:input.timezone,enabled:input.enabled===true,quietStart:input.quietStart||'22:00',quietEnd:input.quietEnd||'07:00',tone:input.tone||'cheeky',daysPerWeek:input.daysPerWeek??7}));\r\n const plan={enabled:input.enabled===true,count:rows.length,times:rows.map(r=>r.time),timezone:rows[0].timezone,quietStart:rows[0].quietStart,quietEnd:rows[0].quietEnd,tone:rows[0].tone,daysPerWeek:rows[0].daysPerWeek,ids};\r\n const statements=(prior.ids||[]).filter(id=>!ids.includes(id)).map(id=>database.prepare('DELETE FROM reminders WHERE id=? AND user_id=?').bind(id,user));\r\n for(const r of rows)statements.push(database.prepare('INSERT INTO reminders(id,user_id,kind,time,timezone,enabled,quiet_start,quiet_end,tone,days_per_week) VALUES(?,?,?,?,?,?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET time=excluded.time,timezone=excluded.timezone,enabled=excluded.enabled,quiet_start=excluded.quiet_start,quiet_end=excluded.quiet_end,tone=excluded.tone,days_per_week=excluded.days_per_week WHERE reminders.user_id=excluded.user_id').bind(r.id,user,r.kind,r.time,r.timezone,r.enabled,r.quietStart,r.quietEnd,r.tone,r.daysPerWeek));\r\n statements.push(database.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value').bind('coach-plan:'+user,JSON.stringify(plan)));await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now:Date.now(),statements});return plan;\r\n}\r\nexport async function syncTrainingStatus(database,user,input,now=Date.now(),{dataEpoch=1}={}){\r\n const today=Math.floor(now/86400000);if(!Number.isSafeInteger(input.startedDay)||input.startedDay<0||input.startedDay>today||(input.lastCompletedDay!=null&&(!Number.isSafeInteger(input.lastCompletedDay)||input.lastCompletedDay<input.startedDay||input.lastCompletedDay>today)))fail('Invalid training status.');\r\n const statement=database.prepare(\"INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=json_object('startedDay',min(json_extract(system.value,'$.startedDay'),json_extract(excluded.value,'$.startedDay')),'lastCompletedDay',nullif(max(coalesce(json_extract(system.value,'$.lastCompletedDay'),-1),coalesce(json_extract(excluded.value,'$.lastCompletedDay'),-1)),-1),'syncedAt',max(json_extract(system.value,'$.syncedAt'),json_extract(excluded.value,'$.syncedAt')))\").bind('training:'+user,JSON.stringify({startedDay:input.startedDay,lastCompletedDay:input.lastCompletedDay??null,syncedAt:now}));await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[statement]});return {synced:true};\r\n}\r\nexport async function claimNotificationSlot(database,user,day,reminder,{dataEpoch=1,now=Date.now(),requireReminder=false}={}){\r\n const key=`notify-budget:${user}:${day}`;\r\n const statement=database.prepare(\"INSERT INTO system(key,value) SELECT ?,json_array(?) WHERE ?=0 OR EXISTS(SELECT 1 FROM reminders WHERE id=? AND user_id=? AND enabled=1) ON CONFLICT(key) DO UPDATE SET value=CASE WHEN EXISTS(SELECT 1 FROM json_each(system.value) WHERE value=?) THEN system.value ELSE json_insert(system.value,'$[#]',?) END WHERE json_array_length(system.value)<3 OR EXISTS(SELECT 1 FROM json_each(system.value) WHERE value=?) RETURNING key\").bind(key,reminder,requireReminder?1:0,reminder,user,reminder,reminder,reminder);\r\n return (await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[statement]}))[0].results.length>0;\r\n}\r\n","server/remote-account-operations.mjs":"import {reminderInput,uuid,cleanText,fail} from './domain.mjs';\r\nimport {readCoachPlan,saveCoachPlan} from './reminder-plan.mjs';\r\nimport {subscriptionInput,sendPush} from './push.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\n\r\n// Explicit request write sets. Scheduler authenticates and reconciles proof first.\r\nexport async function remoteAccountOperation(request,env,user,dataEpoch,now=Date.now()){\r\n const database=env.DB,path=new URL(request.url).pathname,method=request.method;\r\n const commit=statements=>epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements});\r\n const first=async statement=>(await commit([statement]))[0].results?.[0]??null;\r\n const body=async()=>{const text=await request.text();if(text.length>60000)fail('Too much reminder data.',413);try{return JSON.parse(text);}catch{fail('Invalid reminder data.');}};\r\n if(path==='/api/reminders/plan'&&method==='GET')return readCoachPlan(database,user);\r\n if(path==='/api/reminders/plan'&&method==='PUT')return saveCoachPlan(database,user,await body(),{dataEpoch});\r\n if(path==='/api/reminders'&&method==='GET')return {items:(await database.prepare('SELECT * FROM reminders WHERE user_id=? ORDER BY time').bind(user).all()).results};\r\n if(path==='/api/reminders'&&method==='POST'){\r\n  const v=reminderInput(await body());\r\n  const n=await database.prepare('SELECT count(*) AS n FROM reminders WHERE user_id=?').bind(user).first();if(n.n>=16)fail('Keep up to 16 reminders. Remove one before adding another.');\r\n  await commit([database.prepare('INSERT INTO reminders(id,user_id,kind,time,timezone,enabled,quiet_start,quiet_end,tone,days_per_week) VALUES(?,?,?,?,?,?,?,?,?,?) ON CONFLICT(id) DO NOTHING').bind(v.id,user,v.kind,v.time,v.timezone,v.enabled,v.quietStart,v.quietEnd,v.tone,v.daysPerWeek)]);return {saved:true};\r\n }\r\n if(/^\\/api\\/reminders\\/[a-f0-9-]{36}$/.test(path)){\r\n  const id=uuid(path.split('/').at(-1));\r\n  if(method==='DELETE'){await commit([database.prepare('DELETE FROM reminders WHERE id=? AND user_id=?').bind(id,user)]);return {deleted:true};}\r\n  if(method==='PUT'){\r\n   const v=reminderInput({...await body(),id});\r\n   const saved=await first(database.prepare('UPDATE reminders SET kind=?,time=?,timezone=?,enabled=?,quiet_start=?,quiet_end=?,tone=?,days_per_week=? WHERE id=? AND user_id=? RETURNING id').bind(v.kind,v.time,v.timezone,v.enabled,v.quietStart,v.quietEnd,v.tone,v.daysPerWeek,id,user));\r\n   if(!saved)fail('Reminder not found.',404);return {saved:true};\r\n  }\r\n  if(method==='PATCH'){\r\n   const v=await body();if(typeof v.enabled!=='boolean')fail('Choose on or off.');\r\n   const saved=await first(database.prepare('UPDATE reminders SET enabled=? WHERE id=? AND user_id=? RETURNING id').bind(v.enabled?1:0,id,user));\r\n   if(!saved)fail('Reminder not found.',404);return {enabled:v.enabled};\r\n  }\r\n }\r\n if(path==='/api/push/subscribe'&&method==='POST'){\r\n  if(!env.VAPID_PRIVATE_KEY)fail('Notification sending is not connected yet.',503);\r\n  const s=subscriptionInput(await body()),data=JSON.stringify(s);\r\n  const prior=await database.prepare('SELECT user_id,data FROM subscriptions WHERE endpoint=?').bind(s.endpoint).first();\r\n  // Atomic compare-and-set preserves prior endpoint reassignment semantics without\r\n  // letting a delayed read overwrite a different intervening subscription.\r\n  const saved=await first(database.prepare('INSERT INTO subscriptions(endpoint,user_id,data,created_at) VALUES(?,?,?,?) ON CONFLICT(endpoint) DO UPDATE SET user_id=excluded.user_id,data=excluded.data,created_at=excluded.created_at WHERE subscriptions.user_id=? AND subscriptions.data=? RETURNING endpoint').bind(s.endpoint,user,data,now,prior?.user_id??'',prior?.data??''));\r\n  if(!saved)throw Object.assign(Error('Subscription changed. Try again.'),{status:409,code:'subscription_changed'});return {subscribed:true};\r\n }\r\n if(path==='/api/push/unsubscribe'&&method==='POST'){\r\n  const v=await body();await commit([database.prepare('DELETE FROM subscriptions WHERE endpoint=? AND user_id=?').bind(cleanText(v.endpoint,2048),user)]);return {unsubscribed:true};\r\n }\r\n if(path==='/api/push/test'&&method==='POST'){\r\n  const v=await body(),s=await database.prepare('SELECT data,created_at FROM subscriptions WHERE endpoint=? AND user_id=?').bind(cleanText(v.endpoint,2048),user).first();\r\n  if(!s)fail('Enable notifications on this device first.');const key='test:'+user;\r\n  const claimed=await first(database.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value WHERE CAST(system.value AS INTEGER)<=? RETURNING key').bind(key,String(now),now-30000));\r\n  if(!claimed)fail('Wait 30 seconds between test reminders.',429);\r\n  await sendPush(env,JSON.parse(s.data),{title:'MYR5 Coach',body:'Your phone is connected to Coach reminders.',url:'/pose.html?panel=reminders',tag:'myr5-test'},{ownerId:user,dataEpoch,subscriptionData:s.data,subscriptionCreatedAt:s.created_at});return {accepted:true};\r\n }\r\n if(path==='/api/export'&&method==='GET'){\r\n  const data={};for(const table of ['reminders','subscriptions'])data[table]=(await database.prepare(`SELECT * FROM ${table} WHERE user_id=?`).bind(user).all()).results;return data;\r\n }\r\n fail('Not found.',404);\r\n}\r\n","server/remote-deletion-data.mjs":"// Trusted owner-bound statements; run only inside reconcileRemoteEpoch's batch.\r\nexport function remoteAccountDeletionStatements(db,user){\r\n return [\r\n  db.prepare('DELETE FROM deliveries WHERE endpoint IN (SELECT endpoint FROM subscriptions WHERE user_id=?) OR reminder_id IN (SELECT id FROM reminders WHERE user_id=?)').bind(user,user),\r\n  ...['reminders','subscriptions','release_subscribers','release_deliveries'].map(table=>db.prepare(`DELETE FROM ${table} WHERE user_id=?`).bind(user)),\r\n  ...['test:','training:','coach-plan:','release-email-user:'].map(prefix=>db.prepare('DELETE FROM system WHERE key=?').bind(prefix+user)),\r\n  // Explicit legacy markers are retained to prevent old bootstrap imports.\r\n  db.prepare(\"DELETE FROM system WHERE substr(key,1,14)='notify-budget:' AND substr(key,-11,1)=':' AND substr(key,15,length(key)-25)=?\").bind(user),\r\n ];\r\n}\r\n","server/remote-epochs.mjs":"// PROPOSAL: transaction proof primitives only, not HTTP integration or auth.\r\n// All statements supplied by callers MUST be owner-bound trusted SQL.\r\nexport class RemoteEpochError extends Error {\r\n constructor(code){super(code);this.code=code;this.name='RemoteEpochError';}\r\n}\r\nconst fail=code=>{throw new RemoteEpochError(code);};\r\nfunction validate(owner,epoch,now){\r\n if(typeof owner!=='string'||!owner||owner.length>512||owner.trim()!==owner)fail('invalid_owner');\r\n if(!Number.isSafeInteger(epoch)||epoch<1)fail('invalid_epoch');\r\n if(!Number.isSafeInteger(now)||now<0)fail('invalid_time');\r\n}\r\nconst provision=(db,owner,now)=>db.prepare('INSERT INTO account_data_epochs(owner_id,epoch,updated_at) VALUES(?,1,?) ON CONFLICT(owner_id) DO NOTHING').bind(owner,now);\r\n// NOT NULL violation fails the *whole batch*, unlike an ineffective zero-row guard.\r\nconst guard=(db,owner,epoch)=>db.prepare('UPDATE account_data_epochs SET epoch=CASE WHEN epoch=? THEN epoch ELSE NULL END WHERE owner_id=?').bind(epoch,owner);\r\nexport async function epochFencedBatch(db,{ownerId,expectedDataEpoch,now,statements}){\r\n validate(ownerId,expectedDataEpoch,now);if(!Array.isArray(statements))fail('invalid_statements');\r\n try{\r\n  const result=await db.batch([provision(db,ownerId,now),guard(db,ownerId,expectedDataEpoch),...statements]);\r\n  return result.slice(2);\r\n }catch(error){\r\n  const row=await db.prepare('SELECT epoch FROM account_data_epochs WHERE owner_id=?').bind(ownerId).first();\r\n  if((row?.epoch??1)!==expectedDataEpoch)fail('target_epoch_mismatch');\r\n  throw error;\r\n }\r\n}\r\n// A caller must authenticate the primary service and derive this proof from its\r\n// retained D1 receipts. Client headers/body are NOT acceptable proof authority.\r\nexport async function reconcileRemoteEpoch(db,{ownerId,currentDataEpoch,deletedThroughEpoch,now,deletions}){\r\n validate(ownerId,currentDataEpoch,now);\r\n if(deletedThroughEpoch!==currentDataEpoch-1||!Array.isArray(deletions))fail('invalid_proof');\r\n for(let attempt=0;attempt<4;attempt++){\r\n  const row=await db.prepare('SELECT epoch FROM account_data_epochs WHERE owner_id=?').bind(ownerId).first();\r\n  const observed=row?.epoch??1;\r\n  if(observed>=currentDataEpoch)return {ownerId,currentDataEpoch:observed,deletedThroughEpoch:observed-1,reconciled:false};\r\n  try{\r\n   await db.batch([\r\n    provision(db,ownerId,now),guard(db,ownerId,observed),\r\n    ...deletions,\r\n    db.prepare('UPDATE account_data_epochs SET epoch=?,updated_at=? WHERE owner_id=?').bind(currentDataEpoch,now,ownerId),\r\n    db.prepare('INSERT INTO account_data_deletions(owner_id,deleted_epoch,deleted_at) VALUES(?,?,?)').bind(ownerId,deletedThroughEpoch,now),\r\n   ]);\r\n   return {ownerId,currentDataEpoch,deletedThroughEpoch,reconciled:true};\r\n  }catch(error){\r\n   const latest=await db.prepare('SELECT epoch FROM account_data_epochs WHERE owner_id=?').bind(ownerId).first();\r\n   if((latest?.epoch??1)===observed)throw error; // Actual data-deletion error, not a CAS race.\r\n  }\r\n }\r\n fail('remote_epoch_busy');\r\n}\r\n","server/remote-reminders.mjs":"import {db} from './db.mjs';\r\nimport {fail} from './domain.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nexport const remoteReminders=env=>!!env.REMINDER_SERVICE_ORIGIN;\r\nexport function validatedReminderProof(user,proof){\r\n if(!proof||proof.ownerId!==user||typeof user!=='string'||!user||user.length>512||user.trim()!==user||/[\\r\\n]/.test(user)||!Number.isSafeInteger(proof.currentDataEpoch)||proof.currentDataEpoch<1||proof.deletedThroughEpoch!==proof.currentDataEpoch-1)fail('Invalid account generation.',422);\r\n return {ownerId:user,currentDataEpoch:proof.currentDataEpoch,deletedThroughEpoch:proof.deletedThroughEpoch};\r\n}\r\nexport async function reminderService(env,user,path,method='GET',data,proof){\r\n if(!env.REMINDER_SERVICE_TOKEN)fail('The reminder service is not connected.',503);\r\n const origin=new URL(env.REMINDER_SERVICE_ORIGIN);\r\n if(origin.protocol!=='https:'||origin.pathname!=='/'||origin.username||origin.password)fail('Invalid reminder service configuration.',503);\r\n // Global status and unique-token actions do not carry an account proof.\r\n const unscoped=path==='/internal/status'||path.startsWith('/internal/release-email/');\r\n const captured=unscoped?null:validatedReminderProof(user,proof);\r\n const headers={'Authorization':`Bearer ${env.REMINDER_SERVICE_TOKEN}`,'X-Coach-User':user,'Content-Type':'application/json',...(captured?{'X-Coach-Data-Epoch':String(captured.currentDataEpoch),'X-Coach-Deleted-Through':String(captured.deletedThroughEpoch)}:{})};\r\n let response;\r\n try{response=await fetch(new URL(path,origin),{method,headers,body:data===undefined?undefined:JSON.stringify(data),redirect:'manual',signal:AbortSignal.timeout(5000)});}\r\n catch{fail('The reminder service is temporarily unavailable. Please try again.',503);}\r\n if(response.status>=300&&response.status<400)fail('The reminder service returned an unexpected redirect.',503);\r\n if(!response.headers.get('content-type')?.includes('application/json'))fail('The reminder service is temporarily unavailable.',503);\r\n let result;try{result=await response.json();}catch{fail('The reminder service is temporarily unavailable.',503);}\r\n if(!response.ok)throw Object.assign(Error(result.error||'Could not save your reminder. Please try again.'),{status:response.status,...(typeof result.code==='string'?{code:result.code}:{})});\r\n return result;\r\n}\r\nexport async function reconcileReminderAccount(env,user,proof){\r\n const captured=validatedReminderProof(user,proof);\r\n const result=await reminderService(env,user,'/internal/account-reconcile','POST',undefined,captured);\r\n const acknowledged=validatedReminderProof(user,result);\r\n if(acknowledged.currentDataEpoch<captured.currentDataEpoch)fail('Reminder deletion is still pending.',503);\r\n return acknowledged;\r\n}\r\nexport function queueReminderReconciliation(database,user,currentDataEpoch,now){\r\n const proof=validatedReminderProof(user,{ownerId:user,currentDataEpoch,deletedThroughEpoch:currentDataEpoch-1});\r\n return database.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value').bind('remote-deletion:'+user,JSON.stringify(proof));\r\n}\r\nexport async function attemptReminderReconciliation(env,user,proof){\r\n if(!remoteReminders(env))return {status:'not_configured'};\r\n try{\r\n  const ack=await reconcileReminderAccount(env,user,proof);\r\n  await db(env).prepare(\"DELETE FROM system WHERE key=? AND json_extract(value,'$.currentDataEpoch')<=?\").bind('remote-deletion:'+user,ack.currentDataEpoch).run();\r\n  return {status:'reconciled'};\r\n }catch{return {status:'pending'};}\r\n}\r\nexport async function ensureReminderMigration(env,user,proof){\r\n const captured=validatedReminderProof(user,proof),database=db(env),key='reminders_remote:'+JSON.stringify([user,captured.currentDataEpoch]);\r\n if(await database.prepare('SELECT value FROM system WHERE key=?').bind(key).first())return;\r\n const reminders=(await database.prepare('SELECT * FROM reminders WHERE user_id=?').bind(user).all()).results;\r\n const subscriptions=(await database.prepare('SELECT data FROM subscriptions WHERE user_id=?').bind(user).all()).results.map(s=>JSON.parse(s.data));\r\n await reminderService(env,user,'/internal/import','POST',{reminders,subscriptions},captured);\r\n await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:captured.currentDataEpoch,now:Date.now(),statements:[database.prepare('INSERT INTO system(key,value) VALUES(?,?) ON CONFLICT(key) DO NOTHING').bind(key,String(Date.now()))]});\r\n}\r\n","server/run-chest-rewards.mjs":"// Pure server helpers for the treasure side room's one blueprint chest. No SQL, DOM or clock: callers own persistence.\r\n// A blueprint is an ENTITLEMENT in the scoped ledger (modules/coach-world/blueprint-entitlements.mjs); it never builds anything\r\n// and never changes what construction already allows.\r\nimport { BUILDING_BY_ID } from '../modules/coach-world/catalog.mjs';\r\nimport { BLUEPRINT_DROP_BUILDING_IDS, BLUEPRINT_POLICY_VERSION } from '../modules/coach-world/blueprint-policy.mjs';\r\nimport { createBlueprintEntitlements, restoreBlueprintEntitlements, grantBlueprintEntitlement } from '../modules/coach-world/blueprint-entitlements.mjs';\r\n\r\nexport const TREASURE_CHEST_ID = 'chest-1';\r\n// The root-owned drop list (blueprint-policy.mjs) is the ONLY pool: core buildings are never chest drops. The pick prefers a blueprint\r\n// the account does not own yet and is stable per (run seed, run, visit); with everything owned the chest still opens and the\r\n// ledger records an honest already-owned receipt, never a second grant.\r\nexport const TREASURE_BLUEPRINT_POLICY = Object.freeze({ version: 1, tier: 1, dropPolicyVersion: BLUEPRINT_POLICY_VERSION });\r\n\r\nconst hash = text => { let h = 0x811c9dc5; for (const char of text) h = Math.imul(h ^ char.codePointAt(0), 0x01000193) >>> 0; h ^= h >>> 16; h = Math.imul(h, 0x85ebca6b) >>> 0; h ^= h >>> 13; return h >>> 0; };\r\nconst refuse = (reason, base, extra = {}) => ({ ok: false, reason, base, ...extra });\r\n\r\n// The ledger exactly as stored, or a fresh one — always restored against the ACTUAL owner and data epoch, never the saved scope.\r\nfunction ledgerFor(base, accountId, dataEpoch) {\r\n  const saved = base.blueprintEntitlements;\r\n  return saved === undefined ? createBlueprintEntitlements({ accountId, dataEpoch }) : restoreBlueprintEntitlements(saved, { accountId, dataEpoch });\r\n}\r\n\r\n/** Stable building for this run's treasure from the root drop list, preferring blueprints the account does not own. Null only if the account scope is invalid. */\r\nexport function pickTreasureBlueprint({ base, accountId, dataEpoch, runSeed, runId, visitId } = {}) {\r\n  if (!base || typeof base !== 'object' || base.seed !== accountId) return null;\r\n  let ledger;\r\n  try { ledger = ledgerFor(base, accountId, dataEpoch); } catch { return null; }\r\n  const owned = new Set(ledger.owned), pool = [...BLUEPRINT_DROP_BUILDING_IDS].sort();\r\n  const unowned = pool.filter(id => !owned.has(`blueprint:${id}`)), candidates = unowned.length ? unowned : pool;\r\n  if (!candidates.length) return null;\r\n  return candidates[hash(`treasure-blueprint-v${TREASURE_BLUEPRINT_POLICY.version}|${runSeed}|${runId}|${visitId}`) % candidates.length];\r\n}\r\n\r\n/** The one server-issued chest manifest the pure treasure engine is created with. Clients never supply or see a choice. */\r\nexport function treasureManifestFor(args) {\r\n  const buildingId = pickTreasureBlueprint(args);\r\n  return buildingId === null ? null : [{ id: TREASURE_CHEST_ID, rewards: [{ type: 'blueprint', buildingId, tier: TREASURE_BLUEPRINT_POLICY.tier }] }];\r\n}\r\n\r\n/** True only for the exact issued shape: one chest, one blueprint reward, tier 1, on a real building. */\r\nexport function validTreasureManifest(manifest, buildingId) {\r\n  if (!Array.isArray(manifest) || manifest.length !== 1) return false;\r\n  const [chest] = manifest;\r\n  if (!chest || chest.id !== TREASURE_CHEST_ID || !Array.isArray(chest.rewards) || chest.rewards.length !== 1) return false;\r\n  const [reward] = chest.rewards;\r\n  return reward?.type === 'blueprint' && reward.tier === TREASURE_BLUEPRINT_POLICY.tier && Object.hasOwn(BUILDING_BY_ID, reward.buildingId) && BLUEPRINT_DROP_BUILDING_IDS.includes(reward.buildingId)\r\n    && (buildingId === undefined || reward.buildingId === buildingId);\r\n}\r\n\r\n/**\r\n * Project an ACCEPTED opened chest into the account's blueprint ledger. `rewards` must be the opened chest's rewards from the\r\n * persisted manifest. Returns the new base (all other fields untouched) and the receipt; replays are duplicates and grant nothing.\r\n */\r\nexport function grantTreasureBlueprint({ base, accountId, dataEpoch, runId, visitId, chestId, rewards, now } = {}) {\r\n  if (!base || typeof base !== 'object' || typeof accountId !== 'string' || base.seed !== accountId) return refuse('owner_mismatch', base);\r\n  if (!validTreasureManifest([{ id: chestId, rewards }])) return refuse('bad_reward', base);\r\n  const buildingId = rewards[0].buildingId;\r\n  let ledger;\r\n  try { ledger = ledgerFor(base, accountId, dataEpoch); } catch (error) { return refuse('ledger_invalid', base, { detail: error.message }); }\r\n  const granted = grantBlueprintEntitlement(ledger, { accountId, dataEpoch, runId, visitId, chestId, buildingId, now });\r\n  if (!granted.ok) return refuse(granted.reason, base, { detail: granted.error });\r\n  return { ok: true, reason: granted.reason, base: { ...base, blueprintEntitlements: granted.state }, granted: granted.granted, receipt: granted.receipt, buildingId,\r\n    blueprintId: `blueprint:${buildingId}`, owned: [...granted.state.owned] };\r\n}\r\n","server/run-room-rewards.mjs":"// Pure server helper: turns a validated run-XP delta into permanent Gym Credits and level-up pack grants.\r\n// Callers own persistence (one atomic write of the returned base + pack rows); this module has no SQL, DOM or clock.\r\n// Credits reuse the single npcProgression wallet and its idempotent receipts; packs reuse the run XP pack entitlements.\r\nimport { restoreRunXp, createRunXp, XP_TUNING_PROPOSAL } from '../modules/roguelite/run-xp.mjs';\r\nimport { awardRunCurrency, readProgression, CURRENCY } from '../modules/coach-world-rts/npc-progression.mjs';\r\nimport { RUN_CURRENCY_RULES } from './npc-rewards.mjs';\r\nimport { PACK_SIZES } from '../progression-rules.mjs';\r\n\r\n// ponytail: provisional studio tuning, not Ian-specified. Retune here only.\r\nexport const ROOM_REWARD_TUNING = Object.freeze({\r\n  version: 1,\r\n  baseCredits: RUN_CURRENCY_RULES.completedWorkoutCredits,\r\n  kindMultiplier: Object.freeze({ basic: 1, 'full-game': 1, treasure: 1, secret: 1, miniboss: 3, mainboss: 6 }),\r\n  // Pack rarity odds (percent) slide linearly from the early to the late value between these run levels.\r\n  packLevels: Object.freeze({ early: 2, late: 30 }),\r\n  packWeights: Object.freeze({\r\n    early: Object.freeze({ uncommon: 90, rare: 9, legendary: 1 }),\r\n    late: Object.freeze({ uncommon: 45, rare: 40, legendary: 15 }),\r\n  }),\r\n});\r\n\r\nconst RUN_ID = /^[A-Za-z0-9:_-]{1,96}$/;\r\nconst canon = value => Array.isArray(value) ? `[${value.map(canon).join(',')}]`\r\n  : value && typeof value === 'object' ? `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canon(value[key])}`).join(',')}}`\r\n    : JSON.stringify(value);\r\nconst lerp = (from, to, t) => Math.round(from + (to - from) * t);\r\n\r\n/** Gym Credits one new room award is worth. Unknown kinds are never rewarded. */\r\nexport function roomRewardCredits(kind) {\r\n  const multiplier = ROOM_REWARD_TUNING.kindMultiplier[kind];\r\n  return Object.hasOwn(ROOM_REWARD_TUNING.kindMultiplier, kind) ? ROOM_REWARD_TUNING.baseCredits * multiplier : 0;\r\n}\r\n\r\n/** Percent odds per pack tier at a run level; always integers summing to 100. */\r\nexport function packWeightsForLevel(level) {\r\n  const { early, late } = ROOM_REWARD_TUNING.packLevels, { packWeights } = ROOM_REWARD_TUNING;\r\n  const t = Math.min(1, Math.max(0, (level - early) / (late - early)));\r\n  // The rarer tiers are interpolated and the common tier is the remainder, so every weight moves monotonically.\r\n  const rare = lerp(packWeights.early.rare, packWeights.late.rare, t), legendary = lerp(packWeights.early.legendary, packWeights.late.legendary, t);\r\n  return { uncommon: 100 - rare - legendary, rare, legendary };\r\n}\r\n\r\nfunction roll100(text) {\r\n  let h = 0x811c9dc5;\r\n  for (const char of text) h = Math.imul(h ^ char.codePointAt(0), 0x01000193) >>> 0;\r\n  h ^= h >>> 16; h = Math.imul(h, 0x85ebca6b) >>> 0; h ^= h >>> 13; h = Math.imul(h, 0xc2b2ae35) >>> 0; h ^= h >>> 16;\r\n  return (h >>> 0) % 100;\r\n}\r\n\r\n/** Deterministic tier from the server-owned run seed + level; the client never picks or rerolls it. */\r\nexport function packTierFor({ runSeed, runId, level }) {\r\n  const weights = packWeightsForLevel(level), roll = roll100(`reward-pack-v${ROOM_REWARD_TUNING.version}|${runSeed}|${runId}|${level}`);\r\n  return roll < weights.uncommon ? 'uncommon' : roll < weights.uncommon + weights.rare ? 'rare' : 'legendary';\r\n}\r\n\r\n/** Stable account_pack_entitlements id, compatible with `reward-pack:<tier>:...` (raid packs use `raid-...`). */\r\nexport const runPackId = (tier, runId, level) => `reward-pack:${tier}:run-${runId}-${level}`;\r\n\r\nconst refuse = (base, reason, extra = {}) => ({ ok: false, reason, base, credits: { awarded: 0, awards: [] }, packs: [], ...extra });\r\n\r\n/**\r\n * Apply the rewards for the XP gained between two server-owned snapshots of the SAME run.\r\n *   base      server-owned world base (its npcProgression is the one wallet); never mutated\r\n *   accountId owner; must be the base's owner (server bases are seeded with the owner id)\r\n *   runSeed   server-owned run seed (state.seed) used for the pack rarity roll\r\n *   beforeXp  last accepted run XP (null/undefined = from the start of the run); afterXp  the new XP\r\n * Returns { ok, base, credits:{awarded,awards[]}, packs[], balance } or { ok:false, reason, base } with the base untouched.\r\n * Replaying the same snapshots against the returned base credits nothing twice; the pack descriptors are stable so the\r\n * caller's INSERT ... ON CONFLICT DO NOTHING on (user_id, pack_id) stays idempotent. Ended runs keep earned credits.\r\n */\r\nexport function applyRunRoomRewards({ base, accountId, runId, runSeed, beforeXp, afterXp, now } = {}) {\r\n  if (!base || typeof base !== 'object' || typeof accountId !== 'string' || !accountId || accountId.length > 160) return refuse(base, 'bad_owner');\r\n  if (base.seed !== accountId) return refuse(base, 'owner_mismatch');\n  if (typeof runId !== 'string' || !RUN_ID.test(runId) || !Number.isSafeInteger(now) || now < 0) return refuse(base, 'bad_run');\r\n  if (!(typeof runSeed === 'string' && runSeed) && !Number.isSafeInteger(runSeed)) return refuse(base, 'bad_seed');\r\n  let before, after;\r\n  try { before = restoreRunXp(beforeXp ?? createRunXp(runId)); after = restoreRunXp(afterXp); } catch { return refuse(base, 'xp_state_invalid'); }\r\n  if (before.runId !== runId || after.runId !== runId) return refuse(base, 'xp_run_mismatch');\r\n  // Append-only: the earlier awards, boss victories and packs must be an exact prefix of the later state.\r\n  const prefix = key => before[key].length <= after[key].length && before[key].every((item, index) => canon(item) === canon(after[key][index]));\r\n  if (!prefix('awards') || !prefix('bossVictories') || !prefix('packEntitlements') || after.xp < before.xp) return refuse(base, 'xp_not_append_only');\r\n\r\n  let next = base;\r\n  const awards = [], packs = [];\r\n  let awarded = 0;\r\n  for (const award of after.awards.slice(before.awards.length)) {\r\n    const amount = roomRewardCredits(award.kind);\r\n    if (amount <= 0 || amount > CURRENCY.maxAward) return refuse(base, 'bad_amount');\r\n    const credit = awardRunCurrency(next, { runId, eventId: `room:${award.roomId}`, amount, accountId }, { now });\r\n    if (!credit.result.ok) return refuse(base, 'wallet_receipt_conflict', { detail: credit.result.reason });\r\n    next = credit.base;\r\n    if (credit.result.awarded) awarded += amount;\r\n    awards.push({ roomId: award.roomId, receiptId: award.receiptId, kind: award.kind, xp: award.xp, amount, awarded: credit.result.awarded });\r\n  }\r\n  for (const entitlement of after.packEntitlements.slice(before.packEntitlements.length)) {\r\n    const tier = packTierFor({ runSeed: String(runSeed), runId, level: entitlement.level }), packId = runPackId(tier, runId, entitlement.level);\r\n    packs.push({ packId, tier, size: PACK_SIZES[tier], level: entitlement.level, entitlementId: entitlement.id,\r\n      entitlement: { userId: accountId, packId, status: 'owned', grantedAt: now } });\r\n  }\r\n  return { ok: true, reason: null, base: next, credits: { awarded, awards }, packs,\r\n    balance: readProgression(next).balance, xp: { before: before.xp, after: after.xp, perLevel: XP_TUNING_PROPOSAL.xpPerLevel } };\r\n}\r\n","server/scoreboard.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {fail,cleanText} from './domain.mjs';\r\nconst hash=async value=>Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(value))),b=>b.toString(16).padStart(2,'0')).join('');\r\nconst codeValue=value=>typeof value==='string'?value.replace(/[\\s-]/g,'').toLowerCase():'';\r\nconst connections=(database,user)=>database.prepare('SELECT id,CASE WHEN user_a=? THEN user_b ELSE user_a END AS friend FROM scoreboard_links WHERE user_a=? OR user_b=? ORDER BY created_at,id').bind(user,user,user).all();\r\nasync function scores(database,user){return (await database.prepare(`SELECT mode,COUNT(*) AS sets,MAX(value) AS best FROM workouts WHERE user_id=? AND source='server' AND competitive_status='accepted' AND completed_at IS NOT NULL GROUP BY mode`).bind(user).all()).results;}\r\nexport function sharedGala(profile){try{const raw=JSON.parse(JSON.parse(profile||'{}')['mominc-avatar-v1']||'null');if(raw?.schema!=='mominc-avatar'||(raw.version!==1&&raw.version!==2)||!Number.isInteger(raw.dye)||raw.dye<0||raw.dye>9)return null;const trim=raw.version===2?raw.trim??raw.dye:raw.dye;if(!Number.isInteger(trim)||trim<0||trim>9)return null;const parts={};for(const key of ['body','skin','face','hair','facial','headwear','neck','torso','shoulders','arms','hands','legs','feet','held','back','base','pet','build','head','crown','jaw','tail','armor','alloy','shield']){const value=raw.parts?.[key]??0;if(!Number.isInteger(value)||value<0||value>39)return null;parts[key]=value;}return {schema:'mominc-avatar',version:2,name:'',dye:raw.dye,trim,parts};}catch{return null;}}\r\nasync function member(database,user,link=null){const row=await database.prepare('SELECT data FROM onboarding WHERE user_id=?').bind(user).first(),profile=await database.prepare('SELECT data FROM profiles WHERE user_id=?').bind(user).first();let name='Crew member';try{name=JSON.parse(row?.data||'{}').profile?.name||name;}catch{}return {name:String(name).slice(0,60),link,avatar:sharedGala(profile?.data),items:await scores(database,user)};}\r\nexport async function scoreboardApi(database,user,path,method,input={},now=Date.now(),{dataEpoch=1}={}){\r\n if(path==='/api/scoreboard'&&method==='GET'){\r\n  const links=(await connections(database,user)).results;\r\n  const members=await Promise.all([member(database,user),...links.map(l=>member(database,l.friend,l.id))]);\r\n  const invite=await database.prepare('SELECT expires_at FROM scoreboard_invites WHERE owner_id=? AND expires_at>?').bind(user,now).first();\r\n  return {members,inviteExpiresAt:invite?.expires_at??null,updatedAt:now};\r\n }\r\n if(path==='/api/scoreboard/invite'&&method==='POST'){\r\n  if(input.consent!==true)fail('Choose to share your name and best sets with a friend.');\r\n  if((await connections(database,user)).results.length>=2)fail('Your two friend places are full. Remove a friend to invite someone else.',409);\r\n  const code=Array.from(crypto.getRandomValues(new Uint8Array(12)),b=>b.toString(16).padStart(2,'0')).join('');\r\n  const saved=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('INSERT INTO scoreboard_invites(owner_id,token_hash,created_at,expires_at) VALUES(?,?,?,?) ON CONFLICT(owner_id) DO UPDATE SET token_hash=excluded.token_hash,created_at=excluded.created_at,expires_at=excluded.expires_at WHERE scoreboard_invites.created_at<=? RETURNING owner_id').bind(user,await hash(code),now,now+7*86400000,now-60000)]}))[0].results[0];\r\n  if(!saved)fail('Wait a minute before replacing your invite code.',429);\r\n  return {code:code.match(/.{6}/g).join('-').toUpperCase(),expiresAt:now+7*86400000};\r\n }\r\n if(path==='/api/scoreboard/invite'&&method==='DELETE'){\r\n  await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('DELETE FROM scoreboard_invites WHERE owner_id=?').bind(user)]});return {revoked:true};\r\n }\r\n if(path==='/api/scoreboard/join'&&method==='POST'){\r\n  if(input.consent!==true)fail('Choose to share your name and best sets with this friend.');\r\n  const code=codeValue(input.code);if(!/^[a-f0-9]{24}$/.test(code))fail('Enter the full invite code from your friend.');\r\n  const rate=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare(\"INSERT INTO system(key,value) VALUES(?,json_object('since',?,'count',1)) ON CONFLICT(key) DO UPDATE SET value=CASE WHEN json_extract(system.value,'$.since')<? THEN excluded.value ELSE json_set(system.value,'$.count',json_extract(system.value,'$.count')+1) END WHERE json_extract(system.value,'$.since')<? OR json_extract(system.value,'$.count')<20 RETURNING key\").bind(`scoreboard-join:${user}`,now,now-3600000,now-3600000)]}))[0].results[0];\r\n  if(!rate)fail('Too many attempts. Try again in an hour.',429);\r\n  const tokenHash=await hash(code),invite=await database.prepare('SELECT owner_id FROM scoreboard_invites WHERE token_hash=? AND expires_at>?').bind(tokenHash,now).first();\r\n  if(!invite)fail('That invite expired or was used. Ask your friend for a new code.',404);\r\n  if(invite.owner_id===user)fail('That is your own code. Enter a friend’s code.');\r\n  const [a,b]=[user,invite.owner_id].sort(),id=crypto.randomUUID();\r\n  // Capacity, token consumption and membership are one transaction, including concurrent joins.\r\n  const [insert]=await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[\r\n   database.prepare('INSERT INTO scoreboard_links(id,user_a,user_b,created_at) SELECT ?,?,?,? WHERE EXISTS(SELECT 1 FROM scoreboard_invites WHERE token_hash=? AND expires_at>?) AND (SELECT COUNT(*) FROM scoreboard_links WHERE user_a=? OR user_b=?)<2 AND (SELECT COUNT(*) FROM scoreboard_links WHERE user_a=? OR user_b=?)<2 ON CONFLICT(user_a,user_b) DO NOTHING RETURNING id').bind(id,a,b,now,tokenHash,now,a,a,b,b),\r\n   database.prepare('DELETE FROM scoreboard_invites WHERE token_hash=? AND EXISTS(SELECT 1 FROM scoreboard_links WHERE id=?)').bind(tokenHash,id)\r\n  ]});\r\n  if(!insert.results.length)fail('Already connected, or one of your boards is full. Refresh your board.',409);\r\n  return {connected:true};\r\n }\r\n if(path.startsWith('/api/scoreboard/friends/')&&method==='DELETE'){\r\n  const id=cleanText(path.split('/').at(-1),50);const removed=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('DELETE FROM scoreboard_links WHERE id=? AND (user_a=? OR user_b=?) RETURNING id').bind(id,user,user)]}))[0].results[0];\r\n  if(!removed)fail('Friend connection not found.',404);return {removed:true};\r\n }\r\n fail('Not found.',404);\r\n}\r\nexport async function exportScoreboard(database,user){return {connections:(await connections(database,user)).results.map(({id})=>({id})),invite:(await database.prepare('SELECT created_at,expires_at FROM scoreboard_invites WHERE owner_id=?').bind(user).first())??null};}\r\nexport function deleteScoreboard(database,user){return [database.prepare('DELETE FROM scoreboard_links WHERE user_a=? OR user_b=?').bind(user,user),database.prepare('DELETE FROM scoreboard_invites WHERE owner_id=?').bind(user),database.prepare('DELETE FROM system WHERE key=?').bind(`scoreboard-join:${user}`)];}\r\n","server/spotify.mjs":"import {SPOTIFY_SCOPES,spotifyNowPlaying,spotifyPlaylist,spotifyTrack,spotifyDevice} from '../spotify-domain.mjs';\r\nimport {inspectAccountDataEpoch} from './account-data-epochs.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\n\r\nconst API='https://api.spotify.com/v1';\r\nconst ACCOUNTS='https://accounts.spotify.com';\r\nconst json=(value,status=200,extra={})=>new Response(JSON.stringify(value),{status,headers:{'Content-Type':'application/json','Cache-Control':'no-store','X-Content-Type-Options':'nosniff',...extra}});\r\nconst error=(message,status=400,code='spotify_error')=>Object.assign(new Error(message),{status,code});\r\nconst bytes=n=>crypto.getRandomValues(new Uint8Array(n));\r\nconst b64=b=>btoa(String.fromCharCode(...b)).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');\r\nconst from64=s=>Uint8Array.from(atob(s.replace(/-/g,'+').replace(/_/g,'/')+'='.repeat((4-s.length%4)%4)),c=>c.charCodeAt(0));\r\nconst hash=async s=>b64(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(s))));\r\nconst secret=async env=>{\r\n  if(!env.SPOTIFY_TOKEN_KEY||env.SPOTIFY_TOKEN_KEY.length<32)throw error('Spotify token encryption is not configured.',503,'spotify_unconfigured');\r\n  return crypto.subtle.importKey('raw',await crypto.subtle.digest('SHA-256',new TextEncoder().encode(env.SPOTIFY_TOKEN_KEY)),'AES-GCM',false,['encrypt','decrypt']);\r\n};\r\nconst encrypt=async(env,value)=>{const iv=bytes(12),cipher=await crypto.subtle.encrypt({name:'AES-GCM',iv},await secret(env),new TextEncoder().encode(value));return `${b64(iv)}.${b64(new Uint8Array(cipher))}`;};\r\nconst decrypt=async(env,value)=>{const [iv,cipher]=String(value).split('.');return new TextDecoder().decode(await crypto.subtle.decrypt({name:'AES-GCM',iv:from64(iv)},await secret(env),from64(cipher)));};\r\nconst config=env=>{\r\n  if(!env.SPOTIFY_CLIENT_ID||!env.SPOTIFY_REDIRECT_URI)throw error('Spotify connection is not configured.',503,'spotify_unconfigured');\r\n  const uri=new URL(env.SPOTIFY_REDIRECT_URI);\r\n  if(uri.pathname!=='/api/spotify/callback'||!['https:','http:'].includes(uri.protocol))throw error('Spotify redirect URI is invalid.',503,'spotify_unconfigured');\r\n  return uri;\r\n};\r\nconst cookie=(value,request)=>`spotify_oauth=${value}; Path=/api/spotify/callback; HttpOnly; SameSite=Lax; Max-Age=${value?600:0}${new URL(request.url).protocol==='https:'?'; Secure':''}`;\r\nconst cookieValue=request=>request.headers.get('Cookie')?.match(/(?:^|;\\s*)spotify_oauth=([A-Za-z0-9_-]{32,128})(?:;|$)/)?.[1]||null;\r\nconst noStoreRedirect=(to,request,clear=false)=>new Response(null,{status:303,headers:{Location:to,'Cache-Control':'no-store',...(clear?{'Set-Cookie':cookie('',request)}:{})}});\r\nconst safeResult=(kind,request)=>noStoreRedirect(`/pose.html?panel=spotify&spotify=${kind}`,request,true);\r\nconst postToken=async(env,form)=>{\r\n  const response=await fetch(`${ACCOUNTS}/api/token`,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({...form,client_id:env.SPOTIFY_CLIENT_ID}),signal:AbortSignal.timeout(15000)});\r\n  const data=await response.json().catch(()=>({}));\r\n  if(!response.ok)throw error(data.error==='invalid_grant'?'Spotify authorization expired. Connect again.':'Spotify authorization is unavailable.',response.status===429?429:502,data.error==='invalid_grant'?'spotify_reconnect':'spotify_upstream');\r\n  if(!data.access_token||!data.expires_in)throw error('Spotify authorization returned incomplete data.',502,'spotify_upstream');\r\n  return data;\r\n};\r\n\r\n// This callback is the sole route that runs before the app's normal account identity check.\r\n// State is consumed atomically; the separate HttpOnly cookie binds the redirect to its starter.\r\nexport async function spotifyCallback(request,env,database) {\r\n  try {\r\n    config(env);await secret(env);\r\n    if(request.method!=='GET')return json({error:'Method not allowed.'},405);\r\n    const u=new URL(request.url),state=u.searchParams.get('state'),proof=cookieValue(request);\r\n    if(!state||!proof||!await validState(database,state,proof))return safeResult(state&&!proof&&await stateExists(database,state)?'browser':'expired',request);\r\n    const row=await database.prepare('DELETE FROM spotify_oauth_states WHERE state_hash=? AND cookie_hash=? AND expires_at>? RETURNING user_id,verifier_enc,data_epoch').bind(await hash(state),await hash(proof),Date.now()).first();\r\n    if(!row)return safeResult('expired',request);\r\n    if(u.searchParams.has('error'))return safeResult('denied',request);\r\n    const code=u.searchParams.get('code');if(!code)return safeResult('expired',request);\r\n    const token=await postToken(env,{grant_type:'authorization_code',code,redirect_uri:env.SPOTIFY_REDIRECT_URI,code_verifier:await decrypt(env,row.verifier_enc)});\r\n    if(!token.refresh_token)return safeResult('failed',request);\r\n    const me=await fetch(`${API}/me`,{headers:{Authorization:`Bearer ${token.access_token}`},signal:AbortSignal.timeout(15000)});\r\n    // Development-mode apps answer 403 for any Spotify account not on the dashboard user allowlist.\r\n    if(me.status===403)return safeResult('notlisted',request);\r\n    if(!me.ok)return safeResult('failed',request);\r\n    const profile=await me.json();if(!profile.id)return safeResult('failed',request);\r\n    await epochFencedBatch(database,{ownerId:row.user_id,expectedDataEpoch:row.data_epoch,now:Date.now(),statements:[database.prepare(`INSERT INTO spotify_connections(user_id,spotify_user_id,display_name,access_token_enc,refresh_token_enc,expires_at,scopes,data_epoch,updated_at) VALUES(?,?,?,?,?,?,?,?,?)\r\n      ON CONFLICT(user_id) DO UPDATE SET spotify_user_id=excluded.spotify_user_id,display_name=excluded.display_name,access_token_enc=excluded.access_token_enc,refresh_token_enc=excluded.refresh_token_enc,expires_at=excluded.expires_at,scopes=excluded.scopes,data_epoch=excluded.data_epoch,updated_at=excluded.updated_at`)\r\n      .bind(row.user_id,profile.id,profile.display_name||profile.id,await encrypt(env,token.access_token),await encrypt(env,token.refresh_token),Date.now()+token.expires_in*1000,token.scope||SPOTIFY_SCOPES,row.data_epoch,Date.now())]});\r\n    return safeResult('connected',request);\r\n  } catch {return safeResult('failed',request);}\r\n}\r\nasync function validState(database,state,proof){\r\n  if(!/^[A-Za-z0-9_-]{32,128}$/.test(state))return false;\r\n  const row=await database.prepare('SELECT 1 AS ok FROM spotify_oauth_states WHERE state_hash=? AND cookie_hash=? AND expires_at>?').bind(await hash(state),await hash(proof),Date.now()).first();\r\n  return !!row;\r\n}\r\n// A real state with no cookie means the return landed in a different browser jar (iOS Home Screen app vs Safari).\r\nasync function stateExists(database,state){\r\n  if(!/^[A-Za-z0-9_-]{32,128}$/.test(state))return false;\r\n  return !!await database.prepare('SELECT 1 AS ok FROM spotify_oauth_states WHERE state_hash=? AND expires_at>?').bind(await hash(state),Date.now()).first();\r\n}\r\nasync function connection(database,user){const proof=await inspectAccountDataEpoch(database,user);return database.prepare('SELECT * FROM spotify_connections WHERE user_id=? AND data_epoch=?').bind(user,proof.currentDataEpoch).first();}\r\nasync function access(env,database,user){\r\n  let row=await connection(database,user);if(!row)throw error('Connect Spotify first.',409,'spotify_disconnected');\r\n  if(row.expires_at>Date.now()+60000)return decrypt(env,row.access_token_enc);\r\n  let token;\r\n  try{token=await postToken(env,{grant_type:'refresh_token',refresh_token:await decrypt(env,row.refresh_token_enc)});}\r\n  catch(e){if(e.code==='spotify_reconnect')try{await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:row.data_epoch,now:Date.now(),statements:[database.prepare('DELETE FROM spotify_connections WHERE user_id=? AND data_epoch=? AND refresh_token_enc=?').bind(user,row.data_epoch,row.refresh_token_enc)]});}catch{}throw e;}\r\n  await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:row.data_epoch,now:Date.now(),statements:[database.prepare('UPDATE spotify_connections SET access_token_enc=?,refresh_token_enc=?,expires_at=?,updated_at=? WHERE user_id=? AND data_epoch=?')\r\n    .bind(await encrypt(env,token.access_token),token.refresh_token?await encrypt(env,token.refresh_token):row.refresh_token_enc,Date.now()+token.expires_in*1000,Date.now(),user,row.data_epoch)]});\r\n  return token.access_token;\r\n}\r\nasync function spotifyFetch(env,database,user,path,{method='GET',body}={}){\r\n  const readEpoch=(await inspectAccountDataEpoch(database,user)).currentDataEpoch;\r\n  const response=await fetch(API+path,{method,headers:{Authorization:`Bearer ${await access(env,database,user)}`,...(body?{'Content-Type':'application/json'}:{})},...(body?{body:JSON.stringify(body)}:{}),signal:AbortSignal.timeout(15000)});\r\n  if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==readEpoch)throw error('Account data changed.',409,'target_epoch_mismatch');\r\n  if(response.status===204||response.status===202)return null;\r\n  if(response.status===429)throw error('Spotify is busy. Try again shortly.',429,'spotify_rate_limited');\r\n  if(response.status===403)throw error('Spotify Premium or app access is needed for this control.',403,'spotify_restricted');\r\n  if(response.status===404)throw error('Open Spotify on a device, then try this control.',409,'spotify_no_device');\r\n  if(response.status===401)throw error('Spotify session expired. Reconnect Spotify.',401,'spotify_reconnect');\r\n  if(!response.ok)throw error('Spotify is temporarily unavailable.',502,'spotify_upstream');\r\n  return response.json().catch(()=>null);\r\n}\r\nasync function readBody(request){\r\n  if(!request.headers.get('Content-Type')?.startsWith('application/json'))throw error('Send JSON.',415);\r\n  const length=Number(request.headers.get('Content-Length')||0);if(length>2048)throw error('Request too large.',413);\r\n  const text=await request.text();if(text.length>2048)throw error('Request too large.',413);\r\n  try{return JSON.parse(text);}catch{throw error('Invalid request.');}\r\n}\r\nfunction pageOffset(u,allowed){\r\n  for(const key of u.searchParams.keys())if(!allowed.includes(key))throw error('Invalid query.');\r\n  const raw=u.searchParams.get('offset');if(raw===null)return 0;\r\n  if(!/^(0|[1-9][0-9]{0,5})$/.test(raw))throw error('Choose a valid offset.');\r\n  const offset=Number(raw);if(!Number.isSafeInteger(offset)||offset>100000)throw error('Choose a valid offset.');\r\n  return offset;\r\n}\r\nconst onlyKeys=(input,keys)=>input&&typeof input==='object'&&!Array.isArray(input)&&Object.keys(input).every(key=>keys.includes(key))&&keys.every(key=>Object.hasOwn(input,key));\r\nconst spotifyId=value=>typeof value==='string'&&/^[A-Za-z0-9]{10,32}$/.test(value);\r\nexport async function spotifyApi(request,env,database,user){\r\n  try{\r\n    config(env);await secret(env);\r\n    const u=new URL(request.url),p=u.pathname,m=request.method;\r\n    if(!user)throw error('Sign in to Coach.',401,'unauthorized');\r\n    if(!['GET','HEAD'].includes(m)&&request.headers.get('Origin')!==u.origin)throw error('Open this action from Coach.',403,'origin_required');\r\n    let writeEpoch=null;\r\n    if(!['GET','HEAD'].includes(m)){\r\n      const expected=request.headers.get('X-Expected-Data-Epoch');\r\n      if(request.headers.get('X-Target-Account')!==user||!expected||!/^[1-9][0-9]*$/.test(expected))throw error('Refresh account details before connecting Spotify.',428,'account_assertion_required');\r\n      writeEpoch=Number(expected);\r\n      if(!Number.isSafeInteger(writeEpoch)||(await inspectAccountDataEpoch(database,user)).currentDataEpoch!==writeEpoch)throw error('Account data changed.',409,'target_epoch_mismatch');\r\n    }\r\n    if(p==='/api/spotify/status'&&m==='GET'){\r\n      const row=await connection(database,user);\r\n      return json({configured:true,connected:!!row,account:row?{name:row.display_name,id:row.spotify_user_id}:null});\r\n    }\r\n    if(p==='/api/spotify/connect'&&m==='POST'){\r\n      const state=b64(bytes(32)),proof=b64(bytes(32)),verifier=b64(bytes(64)),challenge=await hash(verifier);\r\n      await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:writeEpoch,now:Date.now(),statements:[\r\n        database.prepare('DELETE FROM spotify_oauth_states WHERE user_id=? OR expires_at<?').bind(user,Date.now()),\r\n        database.prepare('INSERT INTO spotify_oauth_states(state_hash,cookie_hash,user_id,verifier_enc,data_epoch,expires_at) VALUES(?,?,?,?,?,?)')\r\n          .bind(await hash(state),await hash(proof),user,await encrypt(env,verifier),writeEpoch,Date.now()+600000),\r\n      ]});\r\n      const auth=new URL(`${ACCOUNTS}/authorize`);\r\n      auth.search=new URLSearchParams({client_id:env.SPOTIFY_CLIENT_ID,response_type:'code',redirect_uri:env.SPOTIFY_REDIRECT_URI,scope:SPOTIFY_SCOPES,state,code_challenge_method:'S256',code_challenge:challenge}).toString();\r\n      return json({url:auth.href},200,{'Set-Cookie':cookie(proof,request)});\r\n    }\r\n    if(p==='/api/spotify/disconnect'&&m==='POST'){\r\n      await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:writeEpoch,now:Date.now(),statements:[database.prepare('DELETE FROM spotify_connections WHERE user_id=?').bind(user),database.prepare('DELETE FROM spotify_oauth_states WHERE user_id=?').bind(user)]});\r\n      return json({connected:false});\r\n    }\r\n    if(p==='/api/spotify/now-playing'&&m==='GET')return json({nowPlaying:spotifyNowPlaying(await spotifyFetch(env,database,user,'/me/player'))});\r\n    if(p==='/api/spotify/search'&&m==='GET'){\r\n      for(const key of u.searchParams.keys())if(!['q','offset'].includes(key))throw error('Invalid query.');\r\n      const q=u.searchParams.get('q');if(typeof q!=='string'||!q.trim()||q.length>200)throw error('Enter a valid search.');\r\n      const offset=pageOffset(u,['q','offset']),data=await spotifyFetch(env,database,user,`/search?${new URLSearchParams({q:q.trim(),type:'track',limit:'10',offset:String(offset)})}`);\r\n      return json({items:(data?.tracks?.items||[]).map(spotifyTrack).filter(Boolean),total:Number(data?.tracks?.total)||0,offset});\r\n    }\r\n    if(p==='/api/spotify/queue'&&m==='GET'){\r\n      if(u.search)return json({error:'Invalid query.'},400);\r\n      const data=await spotifyFetch(env,database,user,'/me/player/queue');\r\n      return json({items:(data?.queue||[]).map(spotifyTrack).filter(Boolean)});\r\n    }\r\n    if(p==='/api/spotify/devices'&&m==='GET'){\r\n      if(u.search)return json({error:'Invalid query.'},400);\r\n      const data=await spotifyFetch(env,database,user,'/me/player/devices');\r\n      return json({items:(data?.devices||[]).map(spotifyDevice)});\r\n    }\r\n    if(p==='/api/spotify/playlist-tracks'&&m==='GET'){\r\n      for(const key of u.searchParams.keys())if(!['playlistId','offset'].includes(key))throw error('Invalid query.');\r\n      const playlistId=u.searchParams.get('playlistId');if(!spotifyId(playlistId))throw error('Choose a valid playlist.',422);\r\n      const offset=pageOffset(u,['playlistId','offset']),data=await spotifyFetch(env,database,user,`/playlists/${encodeURIComponent(playlistId)}/items?limit=50&offset=${offset}`);\r\n      return json({items:(data?.items||[]).map(entry=>spotifyTrack(entry?.item??entry?.track)).filter(Boolean),total:Number(data?.total)||0,offset});\r\n    }\r\n    if(p==='/api/spotify/playlists'&&m==='GET'){\r\n      const offset=Math.max(0,Math.min(1000,Number.parseInt(u.searchParams.get('offset')||'0',10)||0));\r\n      const data=await spotifyFetch(env,database,user,`/me/playlists?limit=20&offset=${offset}`);\r\n      return json({items:(data?.items||[]).map(spotifyPlaylist),total:Number(data?.total)||0,offset});\r\n    }\r\n    if(p==='/api/spotify/playback'&&m==='POST'){\r\n      const input=await readBody(request),action=input?.action;\r\n      if(!['pause','resume','next','previous','playlist','seek','volume','shuffle','repeat','track','queue','transfer'].includes(action))throw error('Unknown playback control.',422);\r\n      let path='/me/player/',method='POST',body;\r\n      if(action==='pause'){path+='pause';method='PUT';}\r\n      if(action==='resume'){path+='play';method='PUT';}\r\n      if(action==='next')path+='next';\r\n      if(action==='previous')path+='previous';\r\n      if(action==='playlist'){\r\n        if(!onlyKeys(input,['action','playlistId'])||!spotifyId(input.playlistId))throw error('Choose a valid playlist.',422);\r\n        path+='play';method='PUT';body={context_uri:`spotify:playlist:${input.playlistId}`};\r\n      }\r\n      if(action==='seek'){\r\n        if(!onlyKeys(input,['action','positionMs'])||!Number.isSafeInteger(input.positionMs)||input.positionMs<0||input.positionMs>86400000)throw error('Choose a valid playback position.',422);\r\n        path+=`seek?position_ms=${input.positionMs}`;method='PUT';\r\n      }\r\n      if(action==='volume'){\r\n        if(!onlyKeys(input,['action','volumePercent'])||!Number.isInteger(input.volumePercent)||input.volumePercent<0||input.volumePercent>100)throw error('Choose a valid volume.',422);\r\n        path+=`volume?volume_percent=${input.volumePercent}`;method='PUT';\r\n      }\r\n      if(action==='shuffle'){\r\n        if(!onlyKeys(input,['action','enabled'])||typeof input.enabled!=='boolean')throw error('Choose a valid shuffle setting.',422);\r\n        path+=`shuffle?state=${input.enabled}`;method='PUT';\r\n      }\r\n      if(action==='repeat'){\r\n        if(!onlyKeys(input,['action','mode'])||!['off','context','track'].includes(input.mode))throw error('Choose a valid repeat mode.',422);\r\n        path+=`repeat?state=${input.mode}`;method='PUT';\r\n      }\r\n      if(action==='track'||action==='queue'){\r\n        if(!onlyKeys(input,['action','trackId'])||!spotifyId(input.trackId))throw error('Choose a valid track.',422);\r\n        const uri=`spotify:track:${input.trackId}`;\r\n        if(action==='track'){path+='play';method='PUT';body={uris:[uri]};}\r\n        else{path+=`queue?uri=${encodeURIComponent(uri)}`;method='POST';}\r\n      }\r\n      if(action==='transfer'){\r\n        if(!onlyKeys(input,['action','deviceId'])||typeof input.deviceId!=='string'||!/^[A-Za-z0-9_-]{1,128}$/.test(input.deviceId))throw error('Choose a valid device.',422);\r\n        path='/me/player';method='PUT';body={device_ids:[input.deviceId],play:true};\r\n      }\r\n      if(['pause','resume','next','previous'].includes(action)&&!onlyKeys(input,['action']))throw error('Invalid playback control.',422);\r\n      await spotifyFetch(env,database,user,path,{method,body});return json({ok:true});\r\n    }\r\n    return json({error:'Not found.'},404);\r\n  }catch(e){if(e.code==='target_epoch_mismatch')return json({error:'Account data changed.',code:e.code},409);return json({error:e.status?e.message:'Spotify is unavailable.',code:e.code||'spotify_error'},e.status||500);}\r\n}\r\n","server/war-room.mjs":"import {readAccountPerformance} from './performance-progress.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {fail} from './domain.mjs';\r\n\r\n// These are the existing Gala family ids.  A selection is presentation data,\r\n// not an entitlement: this service never creates XP, tiers, or rewards.\r\nconst WEAPONS=new Set(['rapier','greatsword','dagger','sabre','axe','hammer','mace','flail','spear','trident','halberd','scythe','bow','crossbow','chakram','gauntlets','staff','wand','tome','cannon']);\r\nconst id=value=>typeof value==='string'&&/^[A-Za-z0-9_-]{1,80}$/.test(value);\r\nconst json=value=>JSON.stringify(value);\r\nconst parse=(value,fallback)=>{try{const parsed=JSON.parse(value);return parsed&&typeof parsed==='object'?parsed:fallback;}catch{return fallback;}};\r\nconst weapon=value=>{\r\n if(!value||typeof value!=='object'||!WEAPONS.has(value.type)||!Number.isInteger(value.tier)||value.tier<0||value.tier>20)fail('Choose a valid Gala weapon.');\r\n return {type:value.type,tier:value.tier};\r\n};\r\nconst recipe=value=>{\r\n if(!value||typeof value!=='object'||!id(value.id)||typeof value.name!=='string'||value.name.trim().length<1||value.name.trim().length>80||typeof value.data!=='string'||value.data.length<1||value.data.length>30000)fail('Choose a valid saved recipe.');\r\n return {id:value.id,name:value.name.trim(),data:value.data};\r\n};\r\n// Persisted rows are untrusted. Ignore malformed entries so an old or\r\n// interrupted save cannot prevent the owner from opening the War Room.\r\nconst storedWeapon=value=>{try{return weapon(value);}catch{return {type:'rapier',tier:0};}};\r\nconst storedRecipes=value=>{\r\n const parsed=parse(value,[]),items=Array.isArray(parsed)?parsed:[],seen=new Set(),valid=[];\r\n for(const item of items){try{const safe=recipe(item);if(!seen.has(safe.id)){seen.add(safe.id);valid.push(safe);}}catch{}if(valid.length===20)break;}\r\n return valid;\r\n};\r\nasync function current(database,user){\r\n return (await database.prepare('SELECT loadout,recipes,revision,updated_at FROM war_room_arsenals WHERE user_id=?').bind(user).first())||{loadout:json({type:'rapier',tier:0}),recipes:'[]',revision:0,updated_at:null};\r\n}\r\nconst safe=row=>({loadout:storedWeapon(parse(row.loadout,{type:'rapier',tier:0})),recipes:storedRecipes(row.recipes),revision:Number.isSafeInteger(Number(row.revision))&&Number(row.revision)>=0?Number(row.revision):0,updatedAt:row.updated_at==null?null:Number.isFinite(Number(row.updated_at))?Number(row.updated_at):null});\r\nasync function save(database,user,previous,next,now,dataEpoch){\r\n const result=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[database.prepare('INSERT INTO war_room_arsenals(user_id,loadout,recipes,revision,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(user_id) DO UPDATE SET loadout=excluded.loadout,recipes=excluded.recipes,revision=war_room_arsenals.revision+1,updated_at=excluded.updated_at WHERE war_room_arsenals.revision=? RETURNING revision,updated_at').bind(user,json(next.loadout),json(next.recipes),1,now,previous.revision)]}))[0].results[0];\r\n if(!result)fail('War Room changed on another device. Refresh before saving.',409);\r\n return {...next,revision:Number(result.revision),updatedAt:Number(result.updated_at)};\r\n}\r\nexport async function warRoomApi(database,user,path,method,input,now=Date.now(),{dataEpoch=1}={}){\r\n const row=await current(database,user),state=safe(row);\r\n if(path==='/api/war-room'&&method==='GET')return {state};\r\n if(path==='/api/war-room/loadout'&&method==='PUT'){\r\n  if(!Number.isSafeInteger(input?.revision)||input.revision!==state.revision)fail('War Room changed on another device. Refresh before saving.',409);\r\n  const selected=weapon(input.loadout),performance=await readAccountPerformance(database,user);if(selected.tier>(performance.weapons[selected.type]??0)&&!(selected.type===state.loadout.type&&selected.tier<=state.loadout.tier))fail('Earn this weapon tier through its workout milestones.',403);\r\n  return {state:await save(database,user,state,{...state,loadout:selected},now,dataEpoch)};\r\n }\r\n if(path==='/api/war-room/recipes'&&method==='POST'){\r\n  if(!Number.isSafeInteger(input?.revision)||input.revision!==state.revision)fail('War Room changed on another device. Refresh before saving.',409);\r\n  const item=recipe(input.recipe);if(state.recipes.some(existing=>existing.id===item.id))fail('That recipe id already exists.',409);if(state.recipes.length>=20)fail('Keep up to 20 saved recipes.');\r\n  return {state:await save(database,user,state,{...state,recipes:[...state.recipes,item]},now,dataEpoch)};\r\n }\r\n if(path==='/api/war-room/import/profile'&&method==='POST'){\r\n  if(!Number.isSafeInteger(input?.revision)||input.revision!==state.revision)fail('War Room changed on another device. Refresh before importing.',409);\r\n  // This is deliberately opt-in and reads only the authenticated owner's\r\n  // established profile. Cookies, IPs, and an arbitrary supplied owner id can\r\n  // never claim a legacy Gala save.\r\n  const profile=await database.prepare('SELECT data FROM profiles WHERE user_id=?').bind(user).first();\r\n  const saved=parse(profile?.data,{}),avatar=parse(saved['mominc-avatar-v1'],{}),next={...state};\r\n  if(avatar.weapon){try{const legacy=weapon(avatar.weapon);const performance=await readAccountPerformance(database,user);if(legacy.tier<=(performance.weapons[legacy.type]??0)||legacy.type===state.loadout.type&&legacy.tier<=state.loadout.tier)next.loadout=legacy;}catch{/* malformed legacy appearance is ignored */}}\r\n  const legacyRecipe=saved['myr5-recipe-v1'];\r\n  if(typeof legacyRecipe==='string'&&legacyRecipe.length>0&&legacyRecipe.length<=30000&&!next.recipes.some(item=>item.id==='legacy-myr5'))next.recipes=[...next.recipes,{id:'legacy-myr5',name:'Imported MYR5 recipe',data:legacyRecipe}];\r\n  if(JSON.stringify(next.loadout)===JSON.stringify(state.loadout)&&JSON.stringify(next.recipes)===JSON.stringify(state.recipes))fail('No validated legacy War Room data is available for this account.',404);\r\n  return {state:await save(database,user,state,next,now,dataEpoch),imported:true};\r\n }\r\n const match=path.match(/^\\/api\\/war-room\\/recipes\\/([A-Za-z0-9_-]{1,80})$/);\r\n if(match&&method==='DELETE'){\r\n  if(!Number.isSafeInteger(input?.revision)||input.revision!==state.revision)fail('War Room changed on another device. Refresh before saving.',409);\r\n  const recipes=state.recipes.filter(item=>item?.id!==match[1]);if(recipes.length===state.recipes.length)fail('Recipe not found.',404);\r\n  return {state:await save(database,user,state,{...state,recipes},now,dataEpoch)};\r\n }\r\n fail('War Room action unavailable.',405);\r\n}\r\n","server/worker.mjs":"import {inspectGameGeneration,requireGameGeneration,withGameGeneration} from './game-generation.mjs';\r\nimport {spotifyApi,spotifyCallback} from './spotify.mjs';\r\nimport {saveWorkoutPaths} from './workout-paths.mjs';\r\nimport {readAccountPerformance} from './performance-progress.mjs';\r\nimport {readAccountReadiness,scheduleAccountReadiness,refreshReminderHealth,trainingReadinessKey} from './account-readiness.mjs';\r\nimport {remoteAccountOperation} from './remote-account-operations.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {AccountDataEpochError,inspectAccountDataEpoch,deleteAccountDataAtEpoch} from './account-data-epochs.mjs';\r\nimport {importWorkout,WorkoutImportError} from './workout-import.mjs';\r\nimport {readExerciseRoute,assertRoundAvailable,completeRound,maximumRoundGoal} from './workout-route.mjs';\r\nimport {scoreboardApi,exportScoreboard,deleteScoreboard} from './scoreboard.mjs';\r\nimport {authConfig,identity,accountOwner,linkLegacy} from './auth.mjs';\r\nimport {galaApi} from './gala.mjs';\r\nimport {installDraft} from './install-draft.mjs';\r\nimport {readOnboarding,saveOnboarding} from './onboarding.mjs';\r\nimport {db} from './db.mjs';\r\nimport {remoteReminders,reminderService,ensureReminderMigration,queueReminderReconciliation,attemptReminderReconciliation} from './remote-reminders.mjs';\r\nimport {MODES,uuid,cleanText,fail,mealInput,progress,} from './domain.mjs';\r\nimport {runReminders} from './push.mjs';\r\nimport {runArmieLetterPushes} from './armie-scheduler.mjs';\r\nimport {runReleasePush} from './release-push.mjs';\r\nimport {RELEASE} from '../release-info.mjs';\r\nimport {trainingFromDaily} from '../weapon-training.mjs';\r\nimport {combatProgress,startBreathing,completeBreathing} from './combat.mjs';\r\nimport {circuitProgress} from '../circuit.mjs';\r\nimport {syncTrainingStatus} from './reminder-plan.mjs';\r\nimport {emailSubscription,emailLinkAction,emailLinkPage} from './release-email.mjs';\r\nimport {readEntitlements,recordCoachArmyCompletion,createCoachArmyBinding,claimCoachArmyBinding} from './entitlements.mjs';\r\nimport {startCoachArmyRun,readCoachArmyRun,recordBrowserCoachArmyEvent,recordVerifiedCoachArmyEvent,startDjscratchChallenge,recordDjscratchControl,deliverCoachArmyOutbox} from './coach-army-runs.mjs';\r\nimport {warRoomApi} from './war-room.mjs';\r\nimport {worldApi,worldDeletionStatements,settleDueWorldSectors,exportWorldData,worldZonesEnabled} from './world-api.mjs';\r\nimport {worldWild} from './world-wild.mjs';\r\nimport {handleWorldWeather} from './world-weather.mjs';\r\nimport {flightAddress} from './flight-address.mjs';\r\nimport {exportWorldDrops} from './world-drops.mjs';\r\nimport {RECOVERY_HTML} from '../recovery-page.mjs';\r\nconst json=(data,status=200)=>new Response(JSON.stringify(data),{status,headers:{'Content-Type':'application/json','Cache-Control':'no-store','X-Content-Type-Options':'nosniff'}});\r\nasync function body(request){if(!request.headers.get('content-type')?.startsWith('application/json'))fail('Send JSON.',415);const reader=request.body?.getReader();if(!reader)fail('Empty request.');const chunks=[];let size=0;for(;;){const {done,value}=await reader.read();if(done)break;size+=value.byteLength;if(size>60000){await reader.cancel();fail('Request too large.',413);}chunks.push(value);}const bytes=new Uint8Array(size);let offset=0;for(const c of chunks){bytes.set(c,offset);offset+=c.byteLength;}let v;try{v=JSON.parse(new TextDecoder().decode(bytes));}catch{fail('Invalid request.');}if(!v||typeof v!=='object'||Array.isArray(v))fail('Invalid request.');return v;}\r\nasync function count(database,user){return Number((await database.prepare(`SELECT count(*) AS total FROM workouts WHERE user_id=? AND source='server' AND completed_at IS NOT NULL`).bind(user).first()).total);}\r\nasync function workoutProgress(database,user,now=Date.now(),includeCombat=true,onboarding=null,{recordLogin=true}={}){\r\n const game=await inspectGameGeneration(database),gameDb=withGameGeneration(database,game.gameGeneration);\r\n const rows=(await database.prepare(`SELECT mode,CAST(completed_at/86400000 AS INTEGER) AS day,COUNT(*) AS sets FROM workouts WHERE user_id=? AND source='server' AND completed_at IS NOT NULL GROUP BY mode,day`).bind(user).all()).results;\r\n const gameRows=(await gameDb.prepare(`SELECT mode,CAST(completed_at/86400000 AS INTEGER) AS day,COUNT(*) AS sets FROM workouts WHERE user_id=? AND source='server' AND completed_at IS NOT NULL AND (?=0 OR (started_at>? AND completed_at>=started_at)) GROUP BY mode,day`).bind(user,game.gameResetAt,game.gameResetAt).all()).results;\r\n const legacy=progress(rows.reduce((sum,row)=>sum+Number(row.sets),0)),earned=progress(gameRows.reduce((sum,row)=>sum+Number(row.sets),0)),performance=await readAccountPerformance(gameDb,user,onboarding?.data?.profile?.timezone||'UTC');\r\n return {...game,performance,exerciseRoute:await readExerciseRoute(database,user,onboarding,now),...earned,fitnessCompletedSets:legacy.completedSets,unlocks:progress(Math.max(earned.completedSets,Object.keys(performance.sessions).length)).unlocks,activeDays:new Set(rows.map(row=>row.day)).size,trainingVersion:1,training:trainingFromDaily(gameRows),trainingStatus:{startedDay:rows.length?rows.reduce((value,r)=>Math.min(value,r.day),Infinity):Math.floor(now/86400000),lastCompletedDay:rows.length?rows.reduce((value,r)=>Math.max(value,r.day),-Infinity):null},...(includeCombat?{combat:await combatProgress(gameDb,user,now,{recordLogin,gameResetAt:game.gameResetAt}),circuit:await dailyCircuitProgress(gameDb,user,gameRows,now,game.gameResetAt)}:{})};\r\n}\r\n// Daily circuit (Rank 6, D15): reuses the same mode+day rows already fetched above, plus two\r\n// small distinct-day scans over breathing_sessions/meals (mirrors combatProgress's breathing\r\n// query -- UTC day, same as the rest of this file's daily-cap logic).\r\nasync function dailyCircuitProgress(database,user,rows,now,cutoff=0){\r\n const [breathingDays,mealDays]=await Promise.all([\r\n  database.prepare(`SELECT DISTINCT CAST(completed_at/86400000 AS INTEGER) AS day FROM breathing_sessions WHERE user_id=? AND completed_at IS NOT NULL AND (?=0 OR (started_at>? AND completed_at>=started_at))`).bind(user,cutoff,cutoff).all(),\r\n  database.prepare(`SELECT DISTINCT CAST(strftime('%s',eaten_at) AS INTEGER)/86400 AS day FROM meals WHERE user_id=? AND (?=0 OR CAST(strftime('%s',eaten_at) AS INTEGER)*1000>?)`).bind(user,cutoff,cutoff).all(),\r\n ]);\r\n return circuitProgress(rows,breathingDays.results.map(r=>r.day),mealDays.results.map(r=>r.day),Math.floor(now/86400000));\r\n}\r\nasync function gameApi(request,env,ctx){\r\n const path=new URL(request.url).pathname;\r\n const scoped=path==='/api/account'||path==='/api/world'||path.startsWith('/api/world/')||path==='/api/war-room'||path.startsWith('/api/war-room/');\r\n if(!scoped||env.REMINDER_INTERNAL===true)return api(request,env,ctx);\r\n const raw=db(env),metadata=await inspectGameGeneration(raw),header=request.headers.get('X-MYR5-Game-Generation');\r\n // Account GET bootstraps the signed-in shell. It cannot provision a base.\r\n const expected=header===null&&path==='/api/account'&&request.method==='GET'?metadata.gameGeneration:/^[1-9][0-9]*$/.test(header||'')?Number(header):null;\r\n requireGameGeneration(expected,metadata);\r\n const response=await api(request,{...env,DB:withGameGeneration(raw,expected)},ctx);\r\n requireGameGeneration(expected,await inspectGameGeneration(raw));\r\n const value=await response.json();\r\n if(metadata.gameResetAt>0&&path==='/api/account'&&value.profile?.['mominc-avatar-v1']){\r\n  try{const avatar=JSON.parse(value.profile['mominc-avatar-v1']);delete avatar.weapon;value.profile['mominc-avatar-v1']=JSON.stringify(avatar);}catch{delete value.profile['mominc-avatar-v1'];}\r\n }\r\n return json({...value,...metadata},response.status);\r\n}\r\nasync function api(request,env,ctx){const u=new URL(request.url),p=u.pathname,method=request.method;\r\n if(p==='/api/workouts/import'&&(method!=='POST'||env.WORKOUT_IMPORTS_ENABLED!=='true'))return json({error:'Not found.'},404);\r\n const database=db(env);\r\n if(p==='/api/spotify/callback'&&method==='GET')return spotifyCallback(request,env,database);\r\n if(p.startsWith('/api/gala/'))return galaApi(request,database,body);\r\n if(p==='/api/integrations/coach-army/completion'&&method==='POST'){\r\n  if(!env.COACH_ARMY_COMPLETION_SECRET||request.headers.get('Authorization')!==`Bearer ${env.COACH_ARMY_COMPLETION_SECRET}`)fail('Unauthorized.',401);\r\n  return json({entitlements:await recordCoachArmyCompletion(database,await body(request))});\r\n }\r\n if(p==='/api/integrations/coach-army/binding'&&method==='POST'){\r\n  if(!env.COACH_ARMY_COMPLETION_SECRET||request.headers.get('Authorization')!==`Bearer ${env.COACH_ARMY_COMPLETION_SECRET}`)fail('Unauthorized.',401);\r\n  return json({binding:await createCoachArmyBinding(database,await body(request))});\r\n }\r\n if(p==='/api/integrations/coach-army/run-events'&&method==='POST'){\r\n  if(!env.COACH_ARMY_COMPLETION_SECRET||request.headers.get('Authorization')!==`Bearer ${env.COACH_ARMY_COMPLETION_SECRET}`)fail('Unauthorized.',401);\r\n  const result=await recordVerifiedCoachArmyEvent(database,await body(request),Date.now());\r\n  return json({run:result.run,accepted:true,outbox:result.outbox||null});\r\n }\r\n if(p==='/api/weather'&&method==='GET')return handleWorldWeather(request,env,ctx);\r\n if(p==='/api/flight/address')return flightAddress(request);\r\n if(p==='/api/releases/current'&&method==='GET')return json(RELEASE);\r\n if(['/api/updates/confirm','/api/updates/unsubscribe'].includes(p)){\r\n  const action=p.split('/').at(-1),token=u.searchParams.get('token');\r\n  if(method==='GET')return emailLinkPage(action,token);\r\n  if(method!=='POST')fail('Method not allowed.',405);\r\n  if(action==='confirm'&&request.headers.get('Origin')!==u.origin)fail('Confirm from the email link page.',403);\r\n  if(!/^[a-f0-9]{64}$/.test(token||''))fail('This email link is invalid.');\r\n  const result=remoteReminders(env)?await reminderService(env,'email-link',`/internal/release-email/${action}`,'POST',{token}):await emailLinkAction(env,action,token);\r\n  return emailLinkPage(action,token,result.message);\r\n }\r\n if(p==='/api/cron'&&method==='POST'){if(!env.CRON_SECRET||request.headers.get('Authorization')!==`Bearer ${env.CRON_SECRET}`)fail('Unauthorized.',401);if(remoteReminders(env))fail('The independent reminder service owns scheduling.',409);return json(await runReminders(env));}\r\n if(!['GET','HEAD'].includes(method)&&request.headers.get('Origin')!==u.origin)fail('Open this action from Coach.',403);\r\n if(p==='/api/auth/config'&&method==='GET')return json(authConfig(env));\r\n if(p==='/api/auth/link'&&method==='POST'){const v=await body(request);if(v.confirm!==true)fail('Confirm the account connection.');return json(await linkLegacy(database,await identity(request,env)));}\r\n if(p==='/api/install-draft')return installDraft(request,database,body);\r\n const id=await identity(request,env),user=await accountOwner(database,id),now=Date.now();\r\n if(p==='/api/game-generation'&&method==='GET')return json({...await inspectGameGeneration(database),targetAccountId:user});\r\n // World homes and social state require a verified bearer identity on the public\r\n // Worker. Legacy forwarded headers are permitted only in our local test host.\r\n if((p==='/api/world'||p.startsWith('/api/world/'))&&!id.clerk&&env.LOCAL_PREVIEW!==true)fail('Sign in to your Mom Inc account to use the shared world.',401);\r\n const worldRunPath=p==='/api/world/runs'||p==='/api/world/runs/current'||p.startsWith('/api/world/runs/');\r\n const worldRaidPath=p==='/api/world/raids'||p==='/api/world/raids/current'||p==='/api/world/raids/start'||p==='/api/world/raids/complete';\r\n const worldRunRuntimePath=/^\\/api\\/world\\/runs\\/[^/]+\\/(?:claim|events|outbox-status)$/.test(p);\r\n if(worldRunPath&&!((p==='/api/world/runs'&&method==='POST')||(p==='/api/world/runs/current'&&method==='GET')||(worldRunRuntimePath&&method==='POST')))fail('world_run_route_not_found',404);\r\n if(worldRaidPath&&!((['/api/world/raids','/api/world/raids/current'].includes(p)&&method==='GET')||(['/api/world/raids/start','/api/world/raids/complete'].includes(p)&&method==='POST')))fail('world_raid_route_not_found',404);\r\n if(p.startsWith('/api/spotify/'))return spotifyApi(request,env,database,user);\r\n if(p==='/api/performance/paths'&&method==='POST')return json(await saveWorkoutPaths(request,database,user,body));\r\n const accountReadProof=p==='/api/account'&&method==='GET'?await inspectAccountDataEpoch(database,user):null;\r\n const reminderRequest=p==='/api/updates/subscription'||p==='/api/reminders'||p.startsWith('/api/reminders/')||p.startsWith('/api/push/');\r\n const primaryWrite=(p==='/api/profile'&&method==='PUT')||(p==='/api/onboarding'&&method==='PUT')||(p==='/api/goals'&&method==='POST')||(p.startsWith('/api/goals/')&&method==='PATCH')||(p==='/api/meals'&&method==='POST')||(p.startsWith('/api/meals/')&&method==='DELETE')||(['/api/breathing/start','/api/breathing/complete'].includes(p)&&method==='POST');\r\n const socialWrite=!['GET','HEAD'].includes(method)&&(p.startsWith('/api/war-room/')||p.startsWith('/api/scoreboard/')||p==='/api/world'||p.startsWith('/api/world/'));\r\n let workoutWriteProof=null;\r\n if((method==='POST'&&p.startsWith('/api/coach-army/'))||socialWrite||primaryWrite||(method==='POST'&&['/api/workouts/start','/api/workouts/complete'].includes(p))||(reminderRequest&&!['GET','HEAD'].includes(method))){\r\n  const target=request.headers.get('X-Target-Account'),rawEpoch=request.headers.get('X-Expected-Data-Epoch');\r\n  if(target===null||rawEpoch===null)throw Object.assign(Error('Refresh account details before saving.'),{status:428,code:'account_assertion_required'});\r\n  if(target!==user)throw Object.assign(Error('Account changed.'),{status:409,code:'target_mismatch'});\r\n  if(!/^[1-9][0-9]*$/.test(rawEpoch)||!Number.isSafeInteger(Number(rawEpoch)))throw Object.assign(Error('Invalid account generation.'),{status:422,code:'invalid_account_epoch'});\r\n  workoutWriteProof=await inspectAccountDataEpoch(database,user);\r\n  if(workoutWriteProof.currentDataEpoch!==Number(rawEpoch))throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});\r\n }\r\n const reminderProof=reminderRequest?(workoutWriteProof||await inspectAccountDataEpoch(database,user)):null;\r\n if(p==='/api/world'||p.startsWith('/api/world/')){\r\n  if(env.REMINDER_INTERNAL===true)fail('Not found.',404);\r\n  const proof=workoutWriteProof||await inspectAccountDataEpoch(database,user);\r\n  const runInput=(worldRunPath||worldRaidPath)&&method==='POST'?await body(request):{};\r\n  const result=await worldApi(request,database,user,p,method,(worldRunPath||worldRaidPath)?runInput:['GET','HEAD'].includes(method)?{}:await body(request),now,{dataEpoch:proof.currentDataEpoch,zones:worldZonesEnabled(env),wild:worldWild(env),runs:env.WORLD_RUNS==='1',raids:env.WORLD_RAIDS==='1'});\r\n  if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==proof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});\r\n  return json(result);\r\n }\r\n const exportProof=p==='/api/export'?await inspectAccountDataEpoch(database,user):null;\r\n const onboarding=env.REMINDER_INTERNAL===true?null:await readOnboarding(database,user,now);\r\n if(p==='/api/workouts/import'&&method==='POST'){\r\n  if(request.headers.get('X-Target-Account')!==user)throw Object.assign(Error('target_mismatch'),{status:409,code:'target_mismatch'});\r\n  const input=await body(request);\r\n  try{\r\n   const result=await importWorkout(database,{ownerId:user,targetAccountId:request.headers.get('X-Target-Account'),idempotencyKey:request.headers.get('Idempotency-Key'),body:input,now});\r\n   return json({workout:result.workout,receipt:result.receipt},result.status);\r\n  }catch(error){\r\n   if(error instanceof WorkoutImportError){if(error.status===400)error.status=422;throw error;}\r\n   throw Object.assign(Error('Import temporarily unavailable.'),{status:503,code:'import_unavailable'});\r\n  }\r\n }\r\n if(p==='/api/war-room'&&method==='GET'){const proof=await inspectAccountDataEpoch(database,user),room=await warRoomApi(database,user,p,method,null,now);if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==proof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});return json({...room,targetAccountId:user,dataEpoch:proof.currentDataEpoch});}\r\n if(p==='/api/war-room/loadout'&&method==='PUT')return json(await warRoomApi(database,user,p,method,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch}));\r\n if(p==='/api/war-room/recipes'&&method==='POST')return json(await warRoomApi(database,user,p,method,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch}));\r\n if(p==='/api/war-room/import/profile'&&method==='POST')return json(await warRoomApi(database,user,p,method,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch}));\r\n if(/^\\/api\\/war-room\\/recipes\\/[A-Za-z0-9_-]{1,80}$/.test(p)&&method==='DELETE')return json(await warRoomApi(database,user,p,method,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch}));\r\n if(p==='/api/coach-army/runs'&&method==='POST')return json({run:await startCoachArmyRun(database,user,now,{dataEpoch:workoutWriteProof.currentDataEpoch})},201);\r\n const coachArmyRun=p.match(/^\\/api\\/coach-army\\/runs\\/([0-9a-f-]{36})(?:\\/events)?$/i);\r\n if(coachArmyRun&&method==='GET'&&!p.endsWith('/events'))return json({run:await readCoachArmyRun(database,coachArmyRun[1],user)});\r\n if(coachArmyRun&&method==='POST'&&p.endsWith('/events')){const result=await recordBrowserCoachArmyEvent(database,{...(await body(request)),runId:coachArmyRun[1],dataEpoch:workoutWriteProof.currentDataEpoch},user,now);return json({run:result.run,accepted:true,evidence:result.evidence||null,replay:result.replay});}\r\n const djscratch=p.match(/^\\/api\\/coach-army\\/runs\\/([0-9a-f-]{36})\\/djscratch(?:\\/controls)?$/i);\r\n if(djscratch&&method==='POST'&&!p.endsWith('/controls'))return json({challenge:await startDjscratchChallenge(database,djscratch[1],user,now,{dataEpoch:workoutWriteProof.currentDataEpoch})});\r\n if(djscratch&&method==='POST'&&p.endsWith('/controls'))return json(await recordDjscratchControl(database,{...(await body(request)),runId:djscratch[1],dataEpoch:workoutWriteProof.currentDataEpoch},user,now));\r\n if(p==='/api/coach-army/claim'&&method==='POST')return json({entitlements:await claimCoachArmyBinding(database,{...(await body(request)),accountId:user,dataEpoch:workoutWriteProof.currentDataEpoch},now)});\r\n if(p==='/api/scoreboard'||p.startsWith('/api/scoreboard/')){const proof=workoutWriteProof||await inspectAccountDataEpoch(database,user),result=await scoreboardApi(database,user,p,method,method==='POST'?await body(request):{},now,{dataEpoch:proof.currentDataEpoch});if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==proof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});return json({...result,targetAccountId:user,dataEpoch:proof.currentDataEpoch});}\r\n if(p==='/api/updates/subscription'){const input=method==='POST'?await body(request):{};return json(remoteReminders(env)?await reminderService(env,user,p,method,method==='POST'?input:undefined,reminderProof):await emailSubscription(env,user,method,input,now,{dataEpoch:reminderProof.currentDataEpoch}));}\r\n if(p==='/api/onboarding'&&method==='PUT')return json(await saveOnboarding(database,user,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch}));\r\n if(env.REMINDER_INTERNAL!==true&&!onboarding&&method!=='GET'&&(p==='/api/workouts/start'||p.startsWith('/api/breathing/')||p==='/api/meals'||p.startsWith('/api/reminders')||p==='/api/push/subscribe'))fail('Answer the three setup questions to start Coach.',403);\r\n if(p==='/api/reminders'||p.startsWith('/api/reminders/')||p.startsWith('/api/push/')){if(remoteReminders(env)){await ensureReminderMigration(env,user,reminderProof);return json(await reminderService(env,user,p,method,['POST','PUT','PATCH'].includes(method)?await body(request):undefined,reminderProof));}return json(await remoteAccountOperation(request,env,user,reminderProof.currentDataEpoch,now));}\r\n if(p==='/api/account/readiness'&&method==='GET'){\r\n  // Optional observations only; this response is never account authority.\r\n  const proof=await inspectAccountDataEpoch(database,user);\r\n  let push,health=null;\r\n  if(remoteReminders(env)){health=await refreshReminderHealth(database,env,user);push={configured:false,publicKey:null,schedulerActive:false,...health.push,status:health.state};}\r\n  else {const tick=await database.prepare(\"SELECT value FROM system WHERE key='scheduler_tick'\").first();push={configured:!!env.VAPID_PUBLIC_KEY,publicKey:env.VAPID_PUBLIC_KEY||null,schedulerActive:!!tick&&now-Number(tick.value)<300000,status:'ready'};}\r\n  const readiness=await readAccountReadiness(database,user,proof.currentDataEpoch);\r\n  if(health&&(!Number.isSafeInteger(readiness.reminders.startedAt)||health.startedAt>readiness.reminders.startedAt||(health.startedAt===readiness.reminders.startedAt&&health.observationId>=readiness.reminders.observationId)))readiness.reminders=health;\r\n  if(readiness.reminders.push)push={...readiness.reminders.push,status:readiness.reminders.state};\r\n  if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==proof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});\r\n  return json({targetAccountId:user,dataEpoch:proof.currentDataEpoch,readiness,push:{...push,environment:env.LOCAL_PREVIEW?'preview':'live'},checkedAt:Date.now()});\r\n }\r\n if(p==='/api/account'&&method==='GET'){\r\n  const [profile,total,tick,entitlements]=await Promise.all([database.prepare('SELECT * FROM profiles WHERE user_id=?').bind(user).first(),workoutProgress(database,user,now,env.REMINDER_INTERNAL!==true,onboarding,{recordLogin:false}),database.prepare(\"SELECT value FROM system WHERE key='scheduler_tick'\").first(),readEntitlements(database,user)]);\r\n  let push={configured:!!env.VAPID_PUBLIC_KEY,publicKey:env.VAPID_PUBLIC_KEY||null,schedulerActive:!!tick&&now-Number(tick.value)<300000};\r\n  if(remoteReminders(env)){\r\n   push={configured:false,publicKey:null,schedulerActive:false,status:'pending'};\r\n\r\n   if(typeof ctx?.waitUntil==='function'){\r\n    const queued=await database.prepare('SELECT value FROM system WHERE key=?').bind(`remote-deletion:${user}`).first();\r\n    if(queued)ctx.waitUntil(Promise.resolve().then(()=>attemptReminderReconciliation(env,user,JSON.parse(queued.value))).catch(()=>{}));\r\n   }\r\n  }\r\n  // Epoch was captured before onboarding/profile/progress reads; a concurrent\r\n  // deletion makes this mixed read unsuitable for a fresh account snapshot.\r\n  const current=await inspectAccountDataEpoch(database,user);\r\n  if(current.currentDataEpoch!==accountReadProof.currentDataEpoch)throw Object.assign(Error('Account data changed. Refresh to continue.'),{status:409,code:'account_epoch_changed'});\r\n  const readiness=await readAccountReadiness(database,user,accountReadProof.currentDataEpoch,now);\r\n  if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==accountReadProof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'account_epoch_changed'});\r\n  if(remoteReminders(env)&&readiness.reminders.push)push={...readiness.reminders.push,status:readiness.reminders.state};\r\n  scheduleAccountReadiness(ctx,database,env,user,accountReadProof,total.trainingStatus);\r\n  return json({user:{id:user,email:id.email,provider:id.clerk?'clerk':'chatgpt'},dataEpoch:accountReadProof.currentDataEpoch,deletionEvidence:accountReadProof,profile:profile?JSON.parse(profile.data):{},revision:profile?.revision||0,onboarding,progress:total,entitlements,readiness,push:{...push,environment:env.LOCAL_PREVIEW?'preview':'live'},syncedAt:now});\r\n }\r\n if(p==='/api/goals'&&method==='GET')return json({items:(await database.prepare('SELECT id,title,note,status,created_at,updated_at FROM goals WHERE user_id=? ORDER BY updated_at DESC').bind(user).all()).results});\r\n if(p==='/api/goals'&&method==='POST'){const v=await body(request),title=cleanText(v.title,160),note=v.note==null||v.note===''?'':cleanText(v.note,1000);if(!title)fail('Add a title for this goal.');const id=uuid(v.id||crypto.randomUUID());await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare('INSERT INTO goals(id,user_id,title,note,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?)').bind(id,user,title,note,'active',now,now)]});return json({saved:true,id});}\r\n if(p.startsWith('/api/goals/')&&method==='PATCH'){const id=uuid(p.split('/').at(-1)),v=await body(request),changes=[];if(v.title!==undefined){const title=cleanText(v.title,160);if(!title)fail('Add a title for this goal.');changes.push(['title',title]);}if(v.note!==undefined)changes.push(['note',v.note===''?'':cleanText(v.note,1000)]);if(v.status!==undefined){if(!['active','completed','archived'].includes(v.status))fail('Choose active, completed, or archived.');changes.push(['status',v.status]);}if(!changes.length)fail('Nothing to save.');const sets=changes.map(([key])=>`${key}=?`).join(','),saved=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare(`UPDATE goals SET ${sets},updated_at=? WHERE id=? AND user_id=? RETURNING id`).bind(...changes.map(([,value])=>value),now,id,user)]}))[0].results[0];if(!saved)fail('Goal not found.',404);return json({saved:true});}\r\n if(p==='/api/profile'&&method==='PUT'){const v=await body(request);if(!Number.isSafeInteger(v.revision)||!v.data||typeof v.data!=='object'||Array.isArray(v.data))fail('Invalid profile.');const allowed=['myr5-recipe-v1','myr5-motion-v1','mominc-avatar-v1','myr5-pod-power-v1','handborne-recipe-v4','mbs-dj-identity-v1'];const safe={};for(const k of allowed){if(v.data[k]!=null){if(typeof v.data[k]!=='string'||v.data[k].length>30000)fail('Appearance is too large.');safe[k]=v.data[k];}}const result=(await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare('INSERT INTO profiles(user_id,data,revision,updated_at) VALUES(?,?,1,?) ON CONFLICT(user_id) DO UPDATE SET data=excluded.data,revision=profiles.revision+1,updated_at=excluded.updated_at WHERE profiles.revision=? RETURNING revision').bind(user,JSON.stringify(safe),now,v.revision)]}))[0].results[0];if(!result)fail('Appearance changed on another device. Restore it before saving again.',409);return json(result);}\r\n if(env.REMINDER_INTERNAL!==true&&p==='/api/breathing/start'&&method==='POST'){const ticket=await startBreathing(database,user,now,{dataEpoch:workoutWriteProof.currentDataEpoch});if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==workoutWriteProof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});return json({...ticket,targetAccountId:user,dataEpoch:workoutWriteProof.currentDataEpoch});}\r\n if(env.REMINDER_INTERNAL!==true&&p==='/api/breathing/complete'&&method==='POST'){const combat=await completeBreathing(database,user,await body(request),now,{dataEpoch:workoutWriteProof.currentDataEpoch});if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==workoutWriteProof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});return json({combat,gameGeneration:combat.gameGeneration,gameResetAt:combat.gameResetAt,targetAccountId:user,dataEpoch:workoutWriteProof.currentDataEpoch});}\r\n if(p==='/api/workouts/start'&&method==='POST'){const v=await body(request);if(!Object.hasOwn(MODES,v.mode)||(!Number.isSafeInteger(v.goal)||v.goal<1||v.goal>await maximumRoundGoal(database,user,v.mode,onboarding.targets.goals[v.mode]??0)))fail('Choose a valid exercise and goal.');await assertRoundAvailable(database,user,v.mode,onboarding?.data?.profile?.timezone||'UTC',now);const recent=await database.prepare(`SELECT count(*) AS n FROM workouts WHERE user_id=? AND source='server' AND started_at>?`).bind(user,now-60000).first();if(recent.n>=12)fail('Please wait a moment before starting again.',429);const id=crypto.randomUUID();await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare(`INSERT INTO workouts(id,user_id,mode,goal,started_at) VALUES(?,?,?,?,?)`).bind(id,user,v.mode,v.goal,now)]});return json({id,startedAt:now,targetAccountId:user,dataEpoch:workoutWriteProof.currentDataEpoch});}\r\n if(p==='/api/workouts/complete'&&method==='POST'){const v=await body(request),id=uuid(v.id),w=await database.prepare(`SELECT * FROM workouts WHERE id=? AND user_id=? AND source='server'`).bind(id,user).first();if(!w)fail('Workout not found.',404);await completeRound(database,user,w,v,onboarding?.data?.profile?.timezone||'UTC',now,{dataEpoch:workoutWriteProof.currentDataEpoch});const progress=await workoutProgress(database,user,now,env.REMINDER_INTERNAL!==true,onboarding,{recordLogin:false});if(remoteReminders(env))try{await reminderService(env,user,'/internal/training-status','POST',progress.trainingStatus,workoutWriteProof);}catch{}else await syncTrainingStatus(database,user,progress.trainingStatus,now,{dataEpoch:workoutWriteProof.currentDataEpoch});if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==workoutWriteProof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'target_epoch_mismatch'});return json({progress,gameGeneration:progress.gameGeneration,gameResetAt:progress.gameResetAt,targetAccountId:user,dataEpoch:workoutWriteProof.currentDataEpoch});}\r\n if(p==='/api/workouts'&&method==='GET')return json({items:(await database.prepare('SELECT id,mode,goal,value,completed_at FROM workouts WHERE user_id=? AND completed_at IS NOT NULL ORDER BY completed_at DESC LIMIT 100').bind(user).all()).results});\r\n if(p==='/api/achievements'&&method==='GET')return json({items:(await database.prepare('SELECT mode,COUNT(*) AS sets,MAX(value) AS best,SUM(value) AS total FROM workouts WHERE user_id=? AND completed_at IS NOT NULL GROUP BY mode').bind(user).all()).results});\r\n if(p==='/api/meals'&&method==='GET')return json({items:(await database.prepare('SELECT * FROM meals WHERE user_id=? ORDER BY eaten_at DESC LIMIT 100').bind(user).all()).results});\r\n if(p==='/api/meals'&&method==='POST'){const v=mealInput(await body(request));await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare('INSERT INTO meals(id,user_id,name,portion,calories,protein,carbs,fat,micros,nutrition_source,eaten_at,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(id) DO NOTHING').bind(v.id,user,v.name,v.portion,v.calories,v.protein,v.carbs,v.fat,JSON.stringify(v.micros),v.nutritionSource,v.eatenAt,now)]});return json({saved:true});}\r\n if(p.startsWith('/api/meals/')&&method==='DELETE'){await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:workoutWriteProof.currentDataEpoch,now,statements:[database.prepare('DELETE FROM meals WHERE id=? AND user_id=?').bind(uuid(p.split('/').at(-1)),user)]});return json({deleted:true});}\r\n  if(p==='/api/export'&&method==='GET'){const tables=['workout_path_choices','profiles','onboarding','workouts','meals','reminders','goals','war_room_arsenals'].filter(t=>t!=='onboarding'||env.REMINDER_INTERNAL!==true);const data={spotify_connections:(await database.prepare('SELECT spotify_user_id,display_name,scopes,data_epoch,updated_at FROM spotify_connections WHERE user_id=?').bind(user).all()).results};for(const table of tables)data[table]=(await database.prepare(`SELECT * FROM ${table} WHERE user_id=?`).bind(user).all()).results;if(remoteReminders(env)){await ensureReminderMigration(env,user,exportProof);data.reminders=(await reminderService(env,user,'/api/reminders','GET',undefined,exportProof)).items;data.releaseEmails=await reminderService(env,user,'/api/updates/subscription','GET',undefined,exportProof);}if(env.REMINDER_INTERNAL!==true){\r\n for(const table of ['account_entitlements','account_pack_entitlements','coach_army_completions'])data[table]=(await database.prepare(`SELECT * FROM ${table} WHERE user_id=?`).bind(user).all()).results;\r\n for(const table of ['coach_army_runs','coach_army_run_events','coach_army_completion_outbox','coach_army_djscratch_challenges'])data[table]=(await database.prepare(`SELECT * FROM ${table} WHERE account_id=?`).bind(user).all()).results;\r\n data.coach_army_bindings=(await database.prepare('SELECT binding_id,run_id,account_id,event_id,completed_at,expires_at,claimed_at,created_at,data_epoch FROM coach_army_bindings WHERE account_id=?').bind(user).all()).results;\r\n data.world=await exportWorldData(database,user,exportProof.currentDataEpoch);\r\n data.worldDrops=await exportWorldDrops(database,user);data.scoreboard=await exportScoreboard(database,user);data.combat=await combatProgress(database,user,now,{recordLogin:false});for(const table of ['login_days','breathing_sessions'])data[table]=(await database.prepare(`SELECT * FROM ${table} WHERE user_id=?`).bind(user).all()).results;}if(!remoteReminders(env))data.releaseEmails=await emailSubscription(env,user,'GET');data.deletionEvidence=exportProof;data.deletionReceipts=(await database.prepare('SELECT owner_id,deleted_epoch,deleted_at FROM account_data_deletions WHERE owner_id=? ORDER BY deleted_epoch').bind(user).all()).results;data.workoutImports=(await database.prepare('SELECT i.* FROM workout_imports i JOIN workouts w ON w.id=i.workout_id WHERE w.user_id=?').bind(user).all()).results;data.competitiveDecisions=(await database.prepare('SELECT d.* FROM competitive_decisions d JOIN workouts w ON w.id=d.workout_id WHERE w.user_id=?').bind(user).all()).results;if((await inspectAccountDataEpoch(database,user)).currentDataEpoch!==exportProof.currentDataEpoch)throw Object.assign(Error('Account data changed.'),{status:409,code:'account_epoch_changed'});return json(data);}\r\n  if(p==='/api/account'&&method==='DELETE'){\r\n   const target=request.headers.get('X-Target-Account');\r\n   if(target===null)throw Object.assign(Error('Refresh account details before deleting.'),{status:428,code:'account_target_required'});\r\n   if(target!==user)throw Object.assign(Error('Account changed. Refresh before deleting.'),{status:409,code:'target_mismatch'});\r\n   const v=await body(request);\r\n   if(!Object.hasOwn(v,'expectedDataEpoch'))throw Object.assign(Error('Refresh account details before deleting.'),{status:428,code:'account_epoch_required'});\r\n   if(!Number.isSafeInteger(v.expectedDataEpoch)||v.expectedDataEpoch<1||v.expectedDataEpoch>=Number.MAX_SAFE_INTEGER||Object.keys(v).some(key=>!['confirm','expectedDataEpoch'].includes(key)))throw Object.assign(Error('Invalid account deletion request.'),{status:422,code:'invalid_account_epoch'});\r\n   if(v.confirm!=='DELETE')fail('Type DELETE to clear your Coach data.');\r\n   let proof;\r\n   try{const raidTablesAvailable=!!(await database.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name='world_raids'\").first());proof=await deleteAccountDataAtEpoch(database,{ownerId:user,expectedDataEpoch:v.expectedDataEpoch,now,deletions:[...(env.REMINDER_INTERNAL===true?[]:worldDeletionStatements(database,user,raidTablesAvailable)),database.prepare('DELETE FROM system WHERE key=?').bind(trainingReadinessKey(user)),...(env.REMINDER_INTERNAL===true?[]:deleteScoreboard(database,user)),database.prepare('DELETE FROM deliveries WHERE endpoint IN (SELECT endpoint FROM subscriptions WHERE user_id=?) OR reminder_id IN (SELECT id FROM reminders WHERE user_id=?)').bind(user,user),database.prepare('DELETE FROM coach_army_djscratch_challenges WHERE account_id=?').bind(user),database.prepare('DELETE FROM coach_army_bindings WHERE account_id=?').bind(user),database.prepare('DELETE FROM coach_army_completion_outbox WHERE account_id=?').bind(user),database.prepare('DELETE FROM coach_army_run_events WHERE account_id=?').bind(user),database.prepare('DELETE FROM coach_army_runs WHERE account_id=?').bind(user),...['test:','training:','coach-plan:'].map(prefix=>database.prepare('DELETE FROM system WHERE key=?').bind(prefix+user)),database.prepare('DELETE FROM system WHERE substr(key,1,length(key)-10)=?').bind('notify-budget:'+user+':'),...['spotify_connections','spotify_oauth_states','workout_path_choices','profiles','onboarding','workouts','meals','reminders','goals','war_room_arsenals','subscriptions','release_subscribers','release_deliveries','login_days','armie_letter_deliveries','breathing_sessions','account_entitlements','account_pack_entitlements','coach_army_completions'].filter(t=>!['onboarding','login_days','breathing_sessions'].includes(t)||env.REMINDER_INTERNAL!==true).map(t=>database.prepare(`DELETE FROM ${t} WHERE user_id=?`).bind(user)),...(remoteReminders(env)?[queueReminderReconciliation(database,user,v.expectedDataEpoch+1,now)]:[])]});}\r\n   catch(error){if(error instanceof AccountDataEpochError&&error.code==='epoch-mismatch')throw Object.assign(Error('Account data changed. Refresh before deleting.'),{status:409,code:'target_epoch_mismatch'});throw error;}\r\n   const remote=remoteReminders(env)?await attemptReminderReconciliation(env,user,proof):{status:'not_configured'};\r\n   return json({deleted:!proof.alreadyDeleted,deletedEpoch:v.expectedDataEpoch,...proof,dataEpoch:proof.currentDataEpoch,deletionEvidence:{ownerId:proof.ownerId,currentDataEpoch:proof.currentDataEpoch,deletedThroughEpoch:proof.deletedThroughEpoch},remote});\r\n  }\r\n return json({error:'Not found.'},404);\r\n}\r\nexport default {\r\n async fetch(request,env,ctx){try{const url=new URL(request.url);if(url.pathname==='/repair-coach')return new Response(RECOVERY_HTML,{headers:{'Content-Type':'text/html; charset=utf-8','Cache-Control':'no-store','X-Content-Type-Options':'nosniff'}});if(url.pathname.startsWith('/api/'))return await gameApi(request,env,ctx);if(url.pathname==='/health'){let reminders=null;if(remoteReminders(env)){try{const status=await reminderService(env,'health','/internal/status');reminders={available:true,configured:status.configured,schedulerActive:status.schedulerActive};}catch{reminders={available:false,configured:false,schedulerActive:false};}}else{const tick=await db(env).prepare(\"SELECT value FROM system WHERE key='scheduler_tick'\").first().catch(()=>null),age=tick?Math.floor((Date.now()-Number(tick.value))/1000):null;reminders={mode:'worker-cron',lastTickAgeS:age,active:age!==null&&age<300};}return json({ok:true,app:'MYR5 Coach',reminders});}if(url.pathname==='/')return Response.redirect(new URL('/pose.html',url),302);if(url.pathname==='/icons/coach-512.png'&&env.ASSETS){const canonical=new URL(url);canonical.pathname='/icons/myr5-alien-512.png';return env.ASSETS.fetch(new Request(canonical,request));}if(env.ASSETS){const response=await env.ASSETS.fetch(request);if(response.status===404&&request.method==='GET'&&(request.headers.get('accept')||'').includes('text/html')&&!/\\.[a-z0-9]+$/i.test(url.pathname))return new Response('<!doctype html><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Return to Coach</title><link rel=stylesheet href=/modules/shell/shell.css><script type=module src=/shell-entry.mjs></script><main style=\"margin:40px 26px;padding:24px;background:#17272b;color:#e4efe7;border:6px ridge #728a84;font:18px/1.6 system-ui\"><h1>This station is unavailable</h1><p>Your saved coach is still here.</p><a href=/pose style=\"color:#f4d98e;display:inline-block;padding:14px\">Return to Coach</a></main>',{status:404,headers:{'Content-Type':'text/html; charset=utf-8','X-Content-Type-Options':'nosniff'}});return response;}return new Response('Not found',{status:404});}catch(e){if(e.code==='target_epoch_mismatch'&&!e.status)e.status=409;if(!e.status)console.error('Coach request failed',e.message);return json({error:e.status?e.message:'Could not complete this request. Please try again.',...(e.status&&e.code?{code:e.code}:{}),...(e.code==='game_generation_mismatch'?e.details:{})},e.status||500);}},\r\n async scheduled(event,env,ctx){if(!remoteReminders(env)){ctx.waitUntil(runReminders(env));ctx.waitUntil(runArmieLetterPushes(env));ctx.waitUntil(runReleasePush(env,Date.now(),RELEASE).catch(e=>console.error('Release push failed',e.message)));}if(env.REMINDER_INTERNAL!==true)ctx.waitUntil((async()=>{const raw=db(env),metadata=await inspectGameGeneration(raw);return settleDueWorldSectors(withGameGeneration(raw,metadata.gameGeneration));})().catch(e=>console.error('World maintenance failed',e.message)));ctx.waitUntil(deliverCoachArmyOutbox(db(env),env));}\r\n};\r\n","server/workout-import.mjs":"import {WorkoutImportCodecError,validateImportSnapshot,fingerprintImportSnapshot,importIdempotencyKey,canonicalImportKey} from '../workout-import-codec.mjs';\r\nimport {readAccountDataEpoch,inspectAccountDataEpoch} from './account-data-epochs.mjs';\r\n\r\nexport class WorkoutImportError extends Error {\r\n constructor(code,status=400){super(code);this.name='WorkoutImportError';this.code=code;this.status=status;}\r\n}\r\nconst fail=(code,status)=>{throw new WorkoutImportError(code,status);};\r\nfunction requestBody(raw){\r\n try{\r\n  if(!raw||typeof raw!=='object'||Array.isArray(raw)||![Object.prototype,null].includes(Object.getPrototypeOf(raw)))fail('invalid_workout_import');\r\n  const descriptors=Object.getOwnPropertyDescriptors(raw),keys=['digestVersion','targetDataEpoch','snapshot','fingerprint'];\r\n  if(Reflect.ownKeys(descriptors).length!==keys.length||keys.some(key=>!descriptors[key]||!Object.hasOwn(descriptors[key],'value')||!descriptors[key].enumerable))fail('invalid_workout_import');\r\n  return Object.fromEntries(keys.map(key=>[key,descriptors[key].value]));\r\n }catch{fail('invalid_workout_import');}\r\n}\r\nfunction response(row,status){\r\n const {id,user_id,mode,goal,started_at,completed_at,value,active,source,client_workout_id,competitive_status}=row;\r\n return {status,workout:{id,user_id,mode,goal,started_at,completed_at,value,active,source,client_workout_id,competitive_status},\r\n  receipt:{targetAccountId:user_id,targetDataEpoch:row.account_data_epoch,digestVersion:row.digest_version,fingerprint:row.fingerprint,idempotencyKey:row.idempotency_key}};\r\n}\r\nconst lookup=(database,ownerId,clientWorkoutId)=>database.prepare(`\r\n SELECT w.*,i.idempotency_key,i.fingerprint,i.digest_version,i.account_data_epoch,COALESCE(e.epoch,1) AS current_data_epoch\r\n FROM workouts w LEFT JOIN workout_imports i ON i.workout_id=w.id\r\n LEFT JOIN account_data_epochs e ON e.owner_id=w.user_id\r\n WHERE w.user_id=? AND w.client_workout_id=?`).bind(ownerId,clientWorkoutId).first();\r\nfunction compare(row,request){\r\n if(row.current_data_epoch!==request.targetDataEpoch)fail('target_epoch_mismatch',409);\r\n if(row.source!=='guest_import'||row.competitive_status!=='not_eligible'||row.fingerprint!==request.fingerprint||row.idempotency_key!==request.idempotencyKey||row.digest_version!==request.digestVersion||row.account_data_epoch!==request.targetDataEpoch)fail('fingerprint_conflict',409);\r\n}\r\n\r\n// This helper does not authenticate requests. The caller must gate the route,\r\n// validate origin/auth, and supply its canonical authenticated owner. The target\r\n// assertion is only a consistency check and cannot select another owner.\r\nexport async function importWorkout(database,{ownerId,targetAccountId,idempotencyKey,body,now=Date.now()}){\r\n if(typeof ownerId!=='string'||!ownerId||ownerId.length>512||ownerId.trim()!==ownerId||/[\\u0000-\\u001f\\u007f]/.test(ownerId))fail('invalid_authenticated_owner',500);\r\n if(targetAccountId!==ownerId)fail('target_account_mismatch',409);\r\n if(!Number.isSafeInteger(now)||now<0)fail('invalid_server_time',500);\r\n let request;\r\n try{\r\n  const input=requestBody(body),snapshot=validateImportSnapshot(input.snapshot);\r\n  const keyInput={digestVersion:input.digestVersion,targetAccountId:ownerId,targetDataEpoch:input.targetDataEpoch,clientWorkoutId:snapshot.clientWorkoutId,fingerprint:input.fingerprint};\r\n  canonicalImportKey(keyInput);\r\n  if(typeof idempotencyKey!=='string'||!(/^[0-9a-f]{64}$/).test(idempotencyKey))fail('invalid_idempotency_key');\r\n  const fingerprint=await fingerprintImportSnapshot(snapshot,input.digestVersion);\r\n  if(fingerprint!==input.fingerprint)fail('invalid_fingerprint');\r\n  if(await importIdempotencyKey(keyInput)!==idempotencyKey)fail('invalid_idempotency_key');\r\n  request={snapshot,digestVersion:input.digestVersion,targetDataEpoch:input.targetDataEpoch,fingerprint,idempotencyKey};\r\n }catch(error){if(error instanceof WorkoutImportCodecError)fail(error.code,error.code==='crypto_unavailable'?500:400);throw error;}\r\n const {snapshot}=request;\r\n const prior=await lookup(database,ownerId,snapshot.clientWorkoutId);\r\n if(prior){compare(prior,request);return response(prior,200);}\r\n\r\n // Version 1 adds no age/freshness or goal-completion policy beyond the pinned\r\n // codec. Partial personal history remains valid and never becomes authoritative.\r\n await readAccountDataEpoch(database,ownerId,now);\r\n const id=globalThis.crypto.randomUUID();\r\n await database.batch([\r\n  database.prepare(`INSERT INTO workouts(id,user_id,mode,goal,started_at,completed_at,value,active,source,client_workout_id,competitive_status,performance_snapshot)\r\n   SELECT ?,?,?,?,?,?,?,?,'guest_import',?,NULL,? FROM account_data_epochs WHERE owner_id=? AND epoch=?\r\n   ON CONFLICT(user_id,client_workout_id) WHERE client_workout_id IS NOT NULL DO NOTHING`).bind(id,ownerId,snapshot.mode,snapshot.goal,snapshot.startedAt,snapshot.completedAt,snapshot.value,snapshot.activeSeconds,snapshot.clientWorkoutId,snapshot.performance?JSON.stringify(snapshot):null,ownerId,request.targetDataEpoch),\r\n  database.prepare(`INSERT INTO workout_imports(workout_id,idempotency_key,fingerprint,digest_version,account_data_epoch,created_at)\r\n   SELECT id,?,?,?,?,? FROM workouts WHERE id=? AND user_id=?`).bind(request.idempotencyKey,request.fingerprint,request.digestVersion,request.targetDataEpoch,now,id,ownerId),\r\n\r\n ]);\r\n const persisted=await lookup(database,ownerId,snapshot.clientWorkoutId);\r\n if(!persisted){\r\n  const epoch=await inspectAccountDataEpoch(database,ownerId);\r\n  if(epoch.currentDataEpoch!==request.targetDataEpoch)fail('target_epoch_mismatch',409);\r\n  fail('import_not_persisted',500);\r\n }\r\n compare(persisted,request);\r\n return response(persisted,persisted.id===id?201:200);\r\n}\r\n","server/workout-paths.mjs":"import {CHOOSABLE_TRACKS} from '../performance-catalog.mjs';\r\nimport {inspectAccountDataEpoch} from './account-data-epochs.mjs';\r\nimport {epochFencedBatch} from './remote-epochs.mjs';\r\n\r\nconst fail=(message,status,code)=>{throw Object.assign(Error(message),{status,code});};\r\nconst samePair=(a,b)=>a.length===2&&b.length===2&&a.every(id=>b.includes(id));\r\n\r\n/** An authenticated account's one-time path choice. The server stores only path IDs;\r\n * all coach access is reconstructed from the canonical catalogue and earned history. */\r\nexport async function saveWorkoutPaths(request,database,user,readBody){\r\n if(request.method!=='POST')fail('Method not allowed.',405,'method_not_allowed');\r\n if(request.headers.get('Origin')!==new URL(request.url).origin)fail('Open this action from Coach.',403,'origin_mismatch');\r\n const target=request.headers.get('X-Target-Account'),rawEpoch=request.headers.get('X-Expected-Data-Epoch');\r\n if(!target||!rawEpoch)fail('Refresh account details before saving.',428,'account_assertion_required');\r\n if(target!==user)fail('Account changed.',409,'target_mismatch');\r\n if(!/^[1-9][0-9]*$/.test(rawEpoch)||!Number.isSafeInteger(Number(rawEpoch)))fail('Invalid account generation.',422,'invalid_account_epoch');\r\n const epoch=Number(rawEpoch),proof=await inspectAccountDataEpoch(database,user);\r\n if(proof.currentDataEpoch!==epoch)fail('Account data changed.',409,'target_epoch_mismatch');\r\n const value=await readBody(request),paths=value.paths;\r\n if(!Array.isArray(paths)||paths.length!==2||new Set(paths).size!==2||!paths.every(id=>CHOOSABLE_TRACKS.includes(id)))fail('Choose exactly two workout paths.',422,'invalid_paths');\r\n const now=Date.now();\r\n try{\r\n  await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:epoch,now,statements:[database.prepare(`INSERT INTO workout_path_choices(user_id,data_epoch,paths,created_at) VALUES(?,?,?,?)\r\n   ON CONFLICT(user_id) DO UPDATE SET data_epoch=excluded.data_epoch,paths=excluded.paths,created_at=excluded.created_at\r\n   WHERE workout_path_choices.data_epoch<>excluded.data_epoch`).bind(user,epoch,JSON.stringify(paths),now)]});\r\n }catch(error){if(error.code==='target_epoch_mismatch')fail('Account data changed.',409,'target_epoch_mismatch');throw error;}\r\n const row=await database.prepare('SELECT paths FROM workout_path_choices WHERE user_id=? AND data_epoch=?').bind(user,epoch).first();\r\n let saved;try{saved=JSON.parse(row?.paths);}catch{}\r\n if(!Array.isArray(saved)||!samePair(paths,saved))fail('Workout paths were already chosen.',409,'paths_already_chosen');\r\n return {paths:saved};\r\n}\r\n","server/workout-route.mjs":"import {epochFencedBatch} from './remote-epochs.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nimport {DAILY_ROUND_LIMIT,exerciseFamily,makeExerciseRoute,routeDay} from '../workout-route.mjs';\r\nimport {fail,validateCompletion} from './domain.mjs';\r\n\r\nexport function workoutDayBounds(now,timezone='UTC'){\r\n const day=routeDay(now,timezone);\r\n // Find local midnight by calendar date; DST days need not be 24 hours long.\r\n function boundary(low,high,after){while(high-low>1){const mid=Math.floor((low+high)/2);if(after(routeDay(mid,timezone)))high=mid;else low=mid;}return high;}\r\n return {day,start:boundary(now-36*3600000,now,d=>d>=day),end:boundary(now,now+36*3600000,d=>d>day)};\r\n}\r\nconst familyModes=mode=>[...Object.values(EXERCISES).filter(m=>m.group===exerciseFamily(mode)).map(m=>m.id),...(exerciseFamily(mode)==='cardio'?['jumping']:[])];\r\nexport async function readExerciseRoute(database,user,onboarding,now=Date.now()){\r\n const timezone=onboarding?.data?.profile?.timezone||'UTC',{day,start,end}=workoutDayBounds(now,timezone);\r\n const [history,today]=await Promise.all([\r\n  database.prepare(`SELECT mode,sets,value FROM (SELECT mode,value,COUNT(*) OVER (PARTITION BY mode) AS sets,ROW_NUMBER() OVER (PARTITION BY mode ORDER BY completed_at DESC,id DESC) AS recent FROM workouts WHERE user_id=? AND source='server' AND completed_at IS NOT NULL) WHERE recent=1`).bind(user).all(),\r\n  database.prepare(`SELECT mode,COUNT(*) AS sets FROM workouts WHERE user_id=? AND source='server' AND completed_at>=? AND completed_at<? GROUP BY mode`).bind(user,start,end).all()\r\n ]);\r\n return makeExerciseRoute(history.results,today.results,{day,timezone,goals:onboarding?.targets?.goals});\r\n}\r\nexport async function assertRoundAvailable(database,user,mode,timezone,now){\r\n const {start,end}=workoutDayBounds(now,timezone),modes=familyModes(mode);\r\n const count=await database.prepare(`SELECT COUNT(*) AS n FROM workouts WHERE user_id=? AND source='server' AND mode IN (${modes.map(()=>'?').join(',')}) AND completed_at>=? AND completed_at<?`).bind(user,...modes,start,end).first();\r\n if(count.n>=DAILY_ROUND_LIMIT)fail('Five rounds completed for this exercise family today. Choose another family or come back tomorrow.',409);\r\n}\r\nexport async function maximumRoundGoal(database,user,mode,personalGoal=0){\r\n const previous=await database.prepare(`SELECT value FROM workouts WHERE user_id=? AND source='server' AND mode=? AND completed_at IS NOT NULL ORDER BY completed_at DESC,id DESC LIMIT 1`).bind(user,mode).first();\r\n return Math.max(600,personalGoal,previous?Math.floor(Number(previous.value))+1:0);\r\n}\r\nexport async function completeRound(database,user,workout,input,timezone,now,{dataEpoch=1}={}){\r\n if(workout.completed_at==null)validateCompletion(workout,input,now);\r\n const {start,end}=workoutDayBounds(now,timezone),modes=familyModes(workout.mode);\r\n // The limit and completion are one atomic write, including competing devices.\r\n const statement=database.prepare(`UPDATE workouts SET completed_at=?,value=?,active=? WHERE id=? AND user_id=? AND source='server' AND completed_at IS NULL AND (SELECT COUNT(*) FROM workouts WHERE user_id=? AND source='server' AND mode IN (${modes.map(()=>'?').join(',')}) AND completed_at>=? AND completed_at<?)<? RETURNING id`).bind(now,input.value,Number.isFinite(input.active)?input.active:0,workout.id,user,user,...modes,start,end,DAILY_ROUND_LIMIT);\r\n const results=await epochFencedBatch(database,{ownerId:user,expectedDataEpoch:dataEpoch,now,statements:[statement,\r\n  database.prepare(\"INSERT INTO login_days(user_id,day,logged_at) SELECT user_id,?,? FROM workouts WHERE id=? AND user_id=? AND source='server' AND completed_at IS NOT NULL ON CONFLICT(user_id,day) DO NOTHING\").bind(Math.floor(now/86400000),now,workout.id,user),\r\n ]});\r\n const saved=results[0].results?.[0];\r\n if(!saved){const existing=await database.prepare(`SELECT completed_at FROM workouts WHERE id=? AND user_id=? AND source='server'`).bind(workout.id,user).first();if(existing?.completed_at==null)throw Object.assign(Error('Five rounds completed for this exercise family today. This extra round was not added.'),{status:409,code:'daily_round_limit'});}\r\n}\r\n","server/world-api.mjs":"import {inspectGameGeneration,isGameRewardEligible,createFreshGameBase,GameGenerationError} from './game-generation.mjs';\r\nimport {freshBase,advanceBase,applyBaseAction,normalizeBase} from '../modules/coach-world/state.mjs';\r\nimport {GROUP_TO_FACTION,FACTIONS,NPC_ITEMS,SHIP_IDS,hangarShipTier} from '../modules/coach-world/catalog.mjs';\r\nimport * as geography from '../modules/coach-world/sites.mjs';\r\nimport {readAccountPerformance} from './performance-progress.mjs';\r\nimport {sectorAt,neighbors,cellToLatLng,cellToBoundary,containsPoint} from '../modules/coach-world/geo.mjs';\r\nimport {isValidCell,getResolution,gridDisk} from '../modules/coach-world/vendor/h3.mjs';\r\nimport {LEVEL_XP} from '../modules/coach-world/levels.mjs';\r\nimport {zoneOf,zoneSectors,zoneCenter,claimAllowed,npcSpots,npcCandidates} from '../modules/coach-world/zones.mjs';\r\nimport {EXERCISES} from '../exercise-library.mjs';\r\nimport {worldDropsApi,worldDropDeletionStatements} from './world-drops.mjs';\r\nimport {wildNearby,wildCommand,wildDeletionStatements} from './world-wild.mjs';\r\nimport {applyNpcPurchase,getRunStartBenefits,readProgression,CURRENCY} from '../modules/coach-world-rts/npc-progression.mjs';\r\nimport {creditVerifiedRunReward,claimVerifiedWorkoutCurrency} from './npc-rewards.mjs';\r\nimport {arCatchEntry} from '../modules/coach-world-rts/ar-catch.mjs';\r\nimport {defenseDrill} from '../modules/coach-world/rts-defense.mjs';\r\nimport {worldRunApi} from './world-run-api.mjs';\r\nimport {worldRunRuntimeApi} from './world-run-runtime.mjs';\r\nimport {worldRaidApi} from './world-raid-api.mjs';\r\nimport {restoreBlueprintEntitlements} from '../modules/coach-world/blueprint-entitlements.mjs';\r\nimport {BLUEPRINT_POLICY_VERSION} from '../modules/coach-world/blueprint-policy.mjs';\r\nexport const WORLD_RULES=Object.freeze({version:1,windowMs:1800000,graceMs:21600000,conversionMs:21600000,healingMs:86400000,influenceMs:259200000,walkDwellMs:90000});\r\n// R58 zones. Default OFF: no route and no command until env WORLD_ZONES==='1' (worker passes zones:worldZonesEnabled(env)).\r\nexport const worldZonesEnabled=env=>env?.WORLD_ZONES==='1';\r\nconst ZONE_BAKE_VERSION='r58-1';\r\nconst zoneLevel=b=>{const xp=Object.values(b.buildings).reduce((s,x)=>s+Math.max(0,Number(x.level)||0),0);let l=1;while(l<LEVEL_XP.length&&xp>=LEVEL_XP[l])l++;return l;};\r\nexport class WorldError extends Error{constructor(code,status=409){super(code);this.code=code;this.status=status;}}\r\nconst fail=(code,status)=>{throw new WorldError(code,status);};\r\nconst uuid=()=>crypto.randomUUID();\r\nconst stmt=(db,sql,...args)=>db.prepare(sql).bind(...args);\r\nconst first=(db,sql,...args)=>stmt(db,sql,...args).first();\r\nconst all=async(db,sql,...args)=>(await stmt(db,sql,...args).all()).results;\r\nconst day=now=>new Date(now).toISOString().slice(0,10);\r\nconst json=JSON.stringify;\r\nconst sites=()=>geography.WORLD_SITES;\r\nconst sectors=()=>geography.WORLD_SECTORS;\r\nconst siteById=id=>sites().find(s=>s.id===id);\r\nconst sectorById=id=>{const known=geography.SECTOR_BY_ID[id];if(known)return known;if(!isValidCell(id)||getResolution(id)!==10)return null;const [latitude,longitude]=cellToLatLng(id),ring=cellToBoundary(id,true);ring.push([...ring[0]]);return {id,siteId:null,kind:'territory',latitude,longitude,neighbors:neighbors(id),outpostFactions:FACTIONS.map(f=>f.id),polygon:ring,polygonGeoJSON:{type:'Polygon',coordinates:[ring]}};};\r\nconst enabledSite=(id,now)=>{const s=siteById(id);if(!s||s.enabled===false||!(geography.isSiteOpen?.(s,now)??true))fail('public_site_closed');return s;};\r\nconst assert=(db,condition,args=[])=>stmt(db,`INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`,uuid(),...args);\r\nconst epochGuard=(db,owner,epoch)=>assert(db,'EXISTS(SELECT 1 FROM account_data_epochs WHERE owner_id=? AND epoch=?)',[owner,epoch]);\r\nconst baseGuard=(db,row)=>assert(db,'EXISTS(SELECT 1 FROM world_bases WHERE owner=? AND epoch=? AND revision=?)',[row.owner,row.epoch,row.revision]);\r\nconst sectorGuard=(db,row)=>assert(db,'EXISTS(SELECT 1 FROM world_sectors WHERE id=? AND revision=? AND generation=? AND enabled=1)',[row.id,row.revision,row.generation]);\r\nasync function atomic(db,guards,writes){await db.batch([...guards,...writes,stmt(db,'DELETE FROM world_assertions')]);}\r\nasync function fingerprint(v){return [...new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(json(v))))].map(v=>v.toString(16).padStart(2,'0')).join('');}\r\nfunction snapshot(row,now){const base=advanceBase(JSON.parse(row.base),now);base.launchCharge=row.charge;return {targetAccountId:row.owner,dataEpoch:row.epoch,revision:row.revision,serverTime:now,base,publicId:row.public_id};}\r\nasync function ownSnapshot(db,row,now){\r\n const own=snapshot(row,now),home=own.base.home;\r\n if(own.base.blueprintEntitlements!==undefined)own.blueprints=restoreBlueprintEntitlements(own.base.blueprintEntitlements,{accountId:row.owner,dataEpoch:row.epoch});\r\n const progression=readProgression(own.base);own.wallet={balance:progression.balance,currency:CURRENCY.name,revision:progression.revision};\r\n const territory=home?await first(db,'SELECT controller,phase FROM world_sectors WHERE id=? AND enabled=1',sectorAt(home.latitude,home.longitude)):null;\r\n own.environment={faction:territory?.controller||own.base.faction,phase:territory?.phase||'stable',source:territory?.controller?'territory':'civilization'};\r\n return own;\r\n}\r\nasync function provision(db,user,epoch,now){\r\n if(typeof user!=='string'||!user||!Number.isSafeInteger(epoch)||epoch<1)fail('world_account_required',401);\r\n const current=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',user);if((current?.epoch??1)!==epoch)fail('world_epoch_mismatch');\r\n const base=createFreshGameBase(user,now,await inspectGameGeneration(db));\r\n base.blueprintPolicyVersion=BLUEPRINT_POLICY_VERSION;\r\n await atomic(db,[assert(db,'COALESCE((SELECT epoch FROM account_data_epochs WHERE owner_id=?),1)=?',[user,epoch])],[stmt(db,'INSERT INTO account_data_epochs(owner_id,epoch,updated_at) VALUES(?,1,?) ON CONFLICT DO NOTHING',user,now),stmt(db,'INSERT INTO world_bases(owner,epoch,public_id,revision,base,charge,created_at) VALUES(?,?,?,1,?,0,?) ON CONFLICT(owner) DO NOTHING',user,epoch,uuid(),json(base),now)]);\r\n const row=await first(db,'SELECT * FROM world_bases WHERE owner=? AND epoch=?',user,epoch);if(!row)fail('world_epoch_mismatch');return row;\r\n}\r\nfunction deploymentGuard(db,sector,faction){if(sector.kind==='territory')return assert(db,'1=1');if(sector.controller===faction)return assert(db,'1=1');const def=sectorById(sector.id);if(def?.outpostFactions?.includes(faction))return assert(db,'1=1');const ns=def?.neighbors??[];return assert(db,ns.length?`EXISTS(SELECT 1 FROM world_sectors WHERE id IN (${ns.map(()=>'?').join(',')}) AND controller=? AND enabled=1)`:'0=1',ns.length?[...ns,faction]:[]);}\r\nfunction saveBase(db,row,base,charge=row.charge){return stmt(db,'UPDATE world_bases SET base=?,charge=?,revision=revision+1 WHERE owner=? AND epoch=? AND revision=?',json(base),charge,row.owner,row.epoch,row.revision);}\r\nconst seeded=new WeakMap();\r\nasync function seed(db,now){if(seeded.has(db))return seeded.get(db);const rows=sectors();if(!rows.length)return;const pending=db.batch(rows.map(s=>stmt(db,'INSERT INTO world_sectors(id,site_id,latitude,longitude,controller,changed_at,conflict_at,enabled) VALUES(?,?,?,?,?,?,?,?) ON CONFLICT DO NOTHING',s.id,s.siteId,s.latitude,s.longitude,s.faction??s.controller??'chest',now,now,s.enabled===false?0:1)));seeded.set(db,pending);try{await pending;}catch(e){seeded.delete(db);throw e;}}\r\nconst distance=(a,b)=>{const r=Math.PI/180,p=(b.latitude-a.latitude)*r,q=(b.longitude-a.longitude)*r;return 12742000*Math.asin(Math.min(1,Math.sqrt(Math.sin(p/2)**2+Math.cos(a.latitude*r)*Math.cos(b.latitude*r)*Math.sin(q/2)**2)));};\r\nfunction validFix(input,now){if(![input.latitude,input.longitude,input.accuracy,input.sampledAt].every(Number.isFinite)||Math.abs(input.latitude)>90||Math.abs(input.longitude)>180||input.accuracy<0||input.accuracy>25||now-input.sampledAt>15000||input.sampledAt>now+1000)fail('world_gps_fix_rejected',400);}\r\nasync function locate(input,now){const found=await geography.locatePublicSector?.(input.latitude,input.longitude,input.accuracy,now);return found??{status:'outside'};}\r\n// units: {trainedUnitId:1} for units at home only (away on an expedition are left out); ids/counts, no location. Raids use it for the defender's garrison.\r\nconst appearance=base=>{const away=new Set(['launched','returning'].includes(base.expedition?.status)?base.expedition.unitIds:[]);return {version:base.version,faction:base.faction,buildings:base.buildings,ships:base.ships,selectedShip:base.selectedShip,units:Object.fromEntries(Object.entries(base.units??{}).map(([id,n])=>[id,n-(away.has(id)?1:0)]).filter(([,n])=>n>0)),rtsTech:base.rtsTech?{version:1,levels:base.rtsTech.levels,addons:base.rtsTech.addons,health:base.rtsTech.health,jobs:{},repairs:{}}:undefined};};\r\nasync function canDeploy(db,sector,faction){if(sector.kind==='territory')return true;if(sector.controller===faction)return true;const definition=sectorById(sector.id);if(definition?.outpostFactions?.includes(faction))return true;const neighbors=definition?.neighbors??[];if(!neighbors.length)return false;const found=await first(db,`SELECT 1 AS ok FROM world_sectors WHERE id IN (${neighbors.map(()=>'?').join(',')}) AND controller=? AND enabled=1`,...neighbors,faction);return !!found;}\r\nasync function influenceState(db,sector,cutoff){const rows=await all(db,'SELECT faction,SUM(points) AS points FROM world_influence WHERE sector_id=? AND COALESCE(effective_at,accepted_at)<=? AND expires_at>? GROUP BY faction',sector.id,cutoff,cutoff);return Object.fromEntries(rows.map(r=>[r.faction,r.points]));}\r\nasync function maybeWindow(db,sector,now,extraFaction,extraPoints=1){const scores=await influenceState(db,sector,now);scores[extraFaction]=(scores[extraFaction]??0)+extraPoints;if(sector.closes_at||sector.grace_until>now)return stmt(db,\"UPDATE world_sectors SET revision=revision+1,phase=CASE WHEN ? IS NOT controller THEN 'warzone' ELSE phase END,conflict_at=CASE WHEN ? IS NOT controller THEN ? ELSE conflict_at END WHERE id=?\",extraFaction,extraFaction,now,sector.id);const eligible=Object.entries(scores).some(([f,p])=>f!==sector.controller&&p>=3&&p-(scores[sector.controller]??0)>=2);return eligible?stmt(db,\"UPDATE world_sectors SET closes_at=?,pause_until=NULL,phase='warzone',conflict_at=?,revision=revision+1 WHERE id=?\",now+WORLD_RULES.windowMs,now,sector.id):stmt(db,\"UPDATE world_sectors SET phase=CASE WHEN ? IS NOT controller THEN 'warzone' ELSE phase END,conflict_at=CASE WHEN ? IS NOT controller THEN ? ELSE conflict_at END,revision=revision+1 WHERE id=?\",extraFaction,extraFaction,now,sector.id);}\r\nexport async function settleDueWorldSectors(db,now=Date.now()){\r\n await seed(db,now);let settled=0;\r\n await db.batch([stmt(db,'DELETE FROM world_walks WHERE updated_at<?',now-86400000),stmt(db,'DELETE FROM world_presence WHERE updated_at<?',now-86400000),stmt(db,'DELETE FROM world_friend_codes WHERE expires_at<?',now)]);\r\n for(const e of await all(db,\"SELECT * FROM world_encounters WHERE (status='pending' AND expires_at<=?) OR (status='admitted' AND due_at<=?) LIMIT 40\",now,now))try{await settleEncounter(db,e,now);}catch(error){if(error instanceof GameGenerationError)throw error;}\r\n const due=await all(db,'SELECT * FROM world_sectors WHERE closes_at IS NOT NULL AND closes_at<=? AND COALESCE(pause_until,closes_at)<=? LIMIT 40',now,now);\r\n for(const s of due){const scores=await influenceState(db,s,s.closes_at),rank=Object.entries(scores).sort((a,b)=>b[1]-a[1]);let winner=s.controller;if(rank[0]&&rank[0][1]>=3&&rank[0][1]-(rank[1]?.[1]??0)>=2&&await canDeploy(db,s,rank[0][0]))winner=rank[0][0];\r\n if(s.kind==='hub'&&(!siteById(s.site_id)?.enabled||!geography.isSiteOpen(siteById(s.site_id),now)))winner=s.controller;\r\n const changed=winner!==s.controller;\r\n try{await atomic(db,[sectorGuard(db,s),...(changed?[deploymentGuard(db,s,winner)]:[])],[stmt(db,\"UPDATE world_sectors SET controller=?,closes_at=NULL,pause_until=NULL,generation=generation+1,revision=revision+1,grace_until=?,phase=?,changed_at=?,conflict_at=? WHERE id=?\",winner,changed?now+WORLD_RULES.graceMs:s.grace_until,changed?'converting':'warzone',changed?now:s.changed_at,now,s.id)]);settled++;}catch(error){if(error instanceof GameGenerationError)throw error;/* another worker settled the generation */}}\r\n // Only quiet areas heal. A rival receipt updates conflict_at and resets the clock.\r\n await stmt(db,\"UPDATE world_sectors SET phase=CASE WHEN ?-MAX(changed_at,conflict_at)>=? THEN 'stable' WHEN ?-MAX(changed_at,conflict_at)>=? THEN 'healing' ELSE phase END,revision=revision+1 WHERE controller IS NOT NULL AND closes_at IS NULL AND phase<>'stable' AND ?-MAX(changed_at,conflict_at)>=?\",now,WORLD_RULES.conversionMs+WORLD_RULES.healingMs,now,WORLD_RULES.conversionMs,now,WORLD_RULES.conversionMs).run();return {settled};\r\n}\r\nasync function reconcile(db,row,now){\r\n const deletion=await first(db,'SELECT MAX(deleted_at) AS at FROM account_data_deletions WHERE owner_id=?',row.owner);\r\n const records=await all(db,'SELECT id,mode,source,competitive_status,goal,started_at,completed_at,value,active FROM workouts WHERE user_id=? AND completed_at IS NOT NULL AND completed_at>=? AND NOT EXISTS(SELECT 1 FROM world_sources ws WHERE ws.owner=? AND ws.epoch=? AND ws.source_id=workouts.id) ORDER BY completed_at,id LIMIT 500',row.owner,Math.max(now-30*86400000,(deletion?.at??-1)+1),row.owner,row.epoch);\r\n const game=await inspectGameGeneration(db);\r\n const existing=await all(db,'SELECT * FROM world_sources WHERE owner=? AND epoch=?',row.owner,row.epoch),known=new Set(existing.map(r=>r.source_id));\r\n const grants=await all(db,'SELECT day,track FROM world_grants WHERE owner=? AND epoch=?',row.owner,row.epoch),grantKeys=new Set(grants.map(g=>g.day+'/'+g.track));\r\n const reservation=await first(db,\"SELECT COALESCE(SUM(CASE WHEN a=? THEN a_reserved ELSE b_reserved END),0) AS n FROM world_encounters WHERE (a=? OR b=?) AND status IN ('pending','admitted')\",row.owner,row.owner,row.owner);\r\n const capacity=Math.max(0,10-reservation.n);\r\n const base=advanceBase(JSON.parse(row.base),now),writes=[];let charge=row.charge,accepted=0;\r\n const prior=existing.filter(s=>s.decision==='accepted');\r\n for(const w of records){if(known.has(w.id))continue;const exercise=EXERCISES[w.mode],track=GROUP_TO_FACTION[exercise?.group],d=day(w.completed_at),elapsed=(w.completed_at-w.started_at)/1000;\r\n let reason='accepted';if(!isGameRewardEligible({startedAt:w.started_at,completedAt:w.completed_at},game))reason='before_game_reset';else if(w.source!=='server')reason='guest_import';else if(w.competitive_status==='not_eligible')reason='source_not_eligible';else if(!exercise||!track)reason='unknown_mode';else if(!Number.isSafeInteger(w.completed_at)||!Number.isSafeInteger(w.started_at)||w.completed_at>now||elapsed<2||elapsed>7200||!Number.isFinite(w.active)||w.active<0||w.active>elapsed+1||!Number.isFinite(w.value)||w.value<=0||w.value<w.goal||w.value>100000)reason='invalid_completion';else if(['hold','pace'].includes(exercise.kind)&&(w.active<60||w.value>elapsed+1))reason='insufficient_active';else if(exercise.kind==='reps'&&w.value<5)reason='insufficient_reps';\r\n const prev=n=>day(w.completed_at-n*86400000);if(reason==='accepted'&&prior.some(g=>g.track===track&&g.day===prev(1))&&prior.some(g=>g.track===track&&g.day===prev(2)))reason='recovery';\r\n if(reason==='accepted'&&exercise.kind==='reps'&&prior.some(g=>g.track===track&&g.day===d))reason='daily_working_set';\r\n writes.push(stmt(db,'INSERT INTO world_sources(owner,epoch,source_id,decision,day,track) VALUES(?,?,?,?,?,?)',row.owner,row.epoch,w.id,reason,d,track??null));\r\n if(reason!=='accepted')continue;prior.push({track,day:d});accepted++;\r\n const key=d+'/'+track;if(grantKeys.has(key))continue;const daily=[...grantKeys].filter(k=>k.startsWith(d+'/')).length;if(daily>=3)continue;grantKeys.add(key);\r\n writes.push(stmt(db,'INSERT INTO world_grants(owner,epoch,day,track) VALUES(?,?,?,?)',row.owner,row.epoch,d,track));base.materials[track]=(base.materials[track]??0)+10;base.rewardDays[d]??=[];base.rewardDays[d].push(track);if(daily===0&&w.completed_at>=now-86400000)charge=Math.min(capacity,charge+1);\r\n }\r\n const meals=await all(db,'SELECT id,eaten_at,created_at FROM meals WHERE user_id=? AND created_at>=? ORDER BY created_at LIMIT 100',row.owner,Math.max(now-30*86400000,(deletion?.at??-1)+1));\r\n const breathing=await all(db,'SELECT id,started_at,completed_at FROM breathing_sessions WHERE user_id=? AND completed_at>=? ORDER BY completed_at LIMIT 100',row.owner,Math.max(now-30*86400000,(deletion?.at??-1)+1));\r\n for(const activity of [...meals.map(v=>({id:'food:'+v.id,kind:'food',startedAt:Date.parse(v.eaten_at),at:Date.parse(v.eaten_at)})),...breathing.map(v=>({id:'meditation:'+v.id,kind:'meditation',startedAt:v.started_at,at:v.completed_at}))]){if(!isGameRewardEligible({startedAt:activity.startedAt,completedAt:activity.at},game)||known.has(activity.id)||!Number.isSafeInteger(activity.at)||activity.at>now||activity.at<now-30*86400000)continue;const d=day(activity.at);writes.push(stmt(db,'INSERT INTO world_sources(owner,epoch,source_id,decision,day,track) VALUES(?,?,?,?,?,NULL)',row.owner,row.epoch,activity.id,'care',d));const flags=base.careDays[d]??={};if(!flags[activity.kind]){flags[activity.kind]=true;const key=activity.kind==='food'?'rations':'calmTokens';base[key]++;}}\r\n const personal=await readAccountPerformance(db,row.owner);base.coachAccess=[...new Set([...base.coachAccess,...personal.coaches])];let hash=0;for(const c of base.seed)hash=(Math.imul(hash,31)+c.charCodeAt(0))>>>0;for(const id of personal.ships??[])if(SHIP_IDS.includes(id)&&id!=='supportive'){const tier=hangarShipTier(base.buildings.hangar.level);if(Object.hasOwn(base.ships,id))base.ships[id].tier=Math.max(base.ships[id].tier,tier);else base.ships[id]={tier,variant:(hash+SHIP_IDS.indexOf(id))%4};}\r\n return {base,charge,writes,result:{accepted,policyVersion:WORLD_RULES.version}};\r\n}\r\nfunction presenceGuard(db,p,now){return assert(db,'EXISTS(SELECT 1 FROM world_presence WHERE owner=? AND epoch=? AND site_id=? AND seq=? AND updated_at=? AND updated_at>=?)',[p.owner,p.epoch,p.site_id,p.seq,p.updated_at,now-15000]);}\r\nfunction encounterGuard(db,e){return assert(db,'EXISTS(SELECT 1 FROM world_encounters WHERE id=? AND status=? AND revision=? AND a_reserved=? AND b_reserved=?)',[e.id,e.status,e.revision,e.a_reserved,e.b_reserved]);}\r\nasync function partyRows(db,e){return Promise.all([first(db,'SELECT * FROM world_bases WHERE owner=? AND epoch=?',e.a,e.ae),first(db,'SELECT * FROM world_bases WHERE owner=? AND epoch=?',e.b,e.be)]);}\r\nfunction refundStatements(db,e){return [stmt(db,'UPDATE world_bases SET charge=MIN(10,charge+?),revision=revision+1 WHERE owner=? AND epoch=?',e.a_reserved,e.a,e.ae),stmt(db,'UPDATE world_bases SET charge=MIN(10,charge+?),revision=revision+1 WHERE owner=? AND epoch=?',e.b_reserved,e.b,e.be),stmt(db,\"UPDATE world_encounters SET status='cancelled',a_reserved=0,b_reserved=0,revision=revision+1 WHERE id=?\",e.id)];}\r\nconst STRENGTH_W={hq:4,tower:3,wall:2,press:2,mortar:2,hall:1,fence:.5,trap:.5};\r\n/** Deterministic encounter strength from base composition (R1-C8): defenses and trained units beat level-1 fence spam. */\r\nexport function encounterStrength(base){let n=0;for(const v of Object.values(base.buildings)){const kind=v.id==='hq'?'hq':v.id.replace(/-.*$/,'');n+=(STRENGTH_W[kind]??0)*Math.max(0,Number(v.level)||0);}return n+2*Object.values(base.units??{}).reduce((sum,count)=>sum+(Number.isSafeInteger(count)?count:0),0)+2*(base.coachAccess?.length??0);}\r\nasync function settleEncounter(db,e,now){\r\n if(!['pending','admitted'].includes(e.status))return e;\r\n const expired=e.status==='pending'&&e.expires_at<=now;\r\n if(!expired&&!(e.status==='admitted'&&e.due_at<=now))return e;\r\n const [a,b]=await partyRows(db,e);if(!a||!b)return e;\r\n const guards=[epochGuard(db,e.a,e.ae),epochGuard(db,e.b,e.be),baseGuard(db,a),baseGuard(db,b),encounterGuard(db,e)];\r\n if(expired){await atomic(db,guards,refundStatements(db,e));return first(db,'SELECT * FROM world_encounters WHERE id=?',e.id);}\r\n const sector=await first(db,'SELECT * FROM world_sectors WHERE id=?',e.sector_id),open=siteById(e.site_id);\r\n if(!sector||sector.generation!==e.generation||!sector.enabled||!open||open.enabled===false){await atomic(db,guards,refundStatements(db,e));return first(db,'SELECT * FROM world_encounters WHERE id=?',e.id);}\r\n // Deterministic simulation from persisted earned buildings, pinned factions, and encounter seed.\r\n const sa=encounterStrength(normalizeBase(JSON.parse(a.base))),sb=encounterStrength(normalizeBase(JSON.parse(b.base))),aWins=sa===sb?(parseInt((await fingerprint(e.id)).slice(0,8),16)%2===0):sa>sb,winner=aWins?a:b,faction=aWins?e.faction_a:e.faction_b;\r\n if(!Number.isSafeInteger(e.admitted_at)||e.admitted_at>e.due_at||e.admitted_at>now){await atomic(db,guards,refundStatements(db,e));return first(db,'SELECT * FROM world_encounters WHERE id=?',e.id);}\r\n const effectiveAt=e.admitted_at;\r\n const receiptId='battle:'+e.id,already=await first(db,\"SELECT id FROM world_influence WHERE owner=? AND epoch=? AND day=? AND sector_id=? AND kind='battle'\",winner.owner,winner.epoch,day(effectiveAt),sector.id),writes=[];\r\n guards.push(sectorGuard(db,sector));\r\n if(!already&&sector.grace_until<=now&&await canDeploy(db,sector,faction)){\r\n guards.push(deploymentGuard(db,sector,faction));\r\n writes.push(stmt(db,\"INSERT INTO world_influence(id,owner,epoch,day,sector_id,faction,kind,points,accepted_at,effective_at,expires_at) VALUES(?,?,?,?,?,?,'battle',3,?,?,?)\",receiptId,winner.owner,winner.epoch,day(effectiveAt),sector.id,faction,now,effectiveAt,effectiveAt+WORLD_RULES.influenceMs));\r\n const window=await maybeWindow(db,sector,now,faction,3);if(window)writes.push(window);\r\n }\r\n writes.push(stmt(db,\"UPDATE world_encounters SET status='settled',winner=?,a_reserved=0,b_reserved=0,revision=revision+1 WHERE id=?\",winner.public_id,e.id));\r\n await atomic(db,guards,writes);return first(db,'SELECT * FROM world_encounters WHERE id=?',e.id);\r\n}\r\nfunction encounterDTO(e,user){if(!e||![e.a,e.b].includes(user))fail('world_encounter_not_found',404);return {id:e.id,status:e.status,revision:e.revision,siteId:e.site_id,sectorId:e.sector_id,meetingAnchor:JSON.parse(e.anchor),expiresAt:e.expires_at,dueAt:e.due_at,admittedAt:e.admitted_at,winner:e.winner,role:e.a===user?'inviter':'invitee'};}\r\nasync function settleOwnEncounters(db,user,now){for(const e of await all(db,\"SELECT * FROM world_encounters WHERE (a=? OR b=?) AND status IN ('pending','admitted') LIMIT 20\",user,user))try{await settleEncounter(db,e,now);}catch(error){if(error instanceof WorldError||error instanceof GameGenerationError)throw error;/* contested CAS: next tick retries */}}\r\n// R5-C8: a rival = non-friend whose current public presence is in the SAME sector as the viewer's fresh presence. Decided here; coordinates never leave the server.\r\nconst rivalAppearance=base=>{const a=appearance(base);return {version:a.version,faction:a.faction,buildings:a.buildings,units:a.units,rtsTech:a.rtsTech};};\r\n// R5-C9: live presence only. The saved home never decides rival eligibility (it is private).\r\nasync function presentSector(db,owner,now,maxAge){const b=await first(db,'SELECT epoch FROM world_bases WHERE owner=?',owner);if(!b)return null;const p=await first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=? AND updated_at>=?',owner,b.epoch,now-maxAge);if(!p)return null;const l=await locate({...p,sampledAt:p.updated_at},now);return l.status==='inside'?l.sectorId:null;}\r\nconst RIVAL_VIEWER_MS=60000,RIVAL_TARGET_MS=3600000;\r\nasync function privateBase(db,user,publicId,now){\r\n const target=await first(db,'SELECT * FROM world_bases WHERE public_id=?',publicId);if(!target)return {fog:true};if(target.owner===user)return snapshot(target,now);\r\n const permission=await first(db,'SELECT location FROM world_friends WHERE a=? AND b=?',target.owner,user);\r\n const battle=permission?null:await first(db,\"SELECT id FROM world_encounters WHERE ((a=? AND b=?) OR (a=? AND b=?)) AND (status='admitted' OR (status='settled' AND due_at>=?)) ORDER BY due_at DESC LIMIT 1\",target.owner,user,user,target.owner,now-3600000);\r\n if(!permission&&!battle){const mine=await presentSector(db,user,now,RIVAL_VIEWER_MS);if(!mine||await presentSector(db,target.owner,now,RIVAL_TARGET_MS)!==mine)return {fog:true};return {publicId:target.public_id,revision:target.revision,base:rivalAppearance(normalizeBase(JSON.parse(target.base))),scope:'rival'};}\r\n const base=normalizeBase(JSON.parse(target.base)),response={publicId:target.public_id,revision:target.revision,base:appearance(base),scope:permission?'friend':'encounter'};\r\n if(permission?.location===1&&base.home)response.home=base.home;return response;\r\n}\r\nasync function mapDTO(db,user,now,request){await seed(db,now);await settleDueWorldSectors(db,now);const params=new URL(request.url).searchParams;const hasLat=params.has('latitude'),hasLng=params.has('longitude');if(hasLat!==hasLng)fail('world_map_center_required',400);const latitude=hasLat?Number(params.get('latitude')):36.1699,longitude=hasLng?Number(params.get('longitude')):-115.1398,radius=params.has('radius')?Number(params.get('radius')):5000,cursor=params.get('cursor')??'';\r\n if(!Number.isFinite(latitude)||Math.abs(latitude)>90||!Number.isFinite(longitude)||Math.abs(longitude)>180||!Number.isFinite(radius)||radius<100||radius>5000||cursor&&(!isValidCell(cursor)||getResolution(cursor)!==10))fail('world_map_query_invalid',400);\r\n const latDelta=radius/111000,lngDelta=Math.min(180,latDelta/Math.max(.001,Math.cos(latitude*Math.PI/180))),west=longitude-lngDelta,east=longitude+lngDelta;let longitudeSql='longitude BETWEEN ? AND ?',longitudeArgs=[west,east];if(west < -180){longitudeSql='(longitude>=? OR longitude<=?)';longitudeArgs=[west+360,east];}else if(east>180){longitudeSql='(longitude>=? OR longitude<=?)';longitudeArgs=[west,east-360];}\r\n const rows=await all(db,`SELECT * FROM world_sectors WHERE enabled=1 AND latitude BETWEEN ? AND ? AND ${longitudeSql} AND id>? ORDER BY id LIMIT 513`,Math.max(-90,latitude-latDelta),Math.min(90,latitude+latDelta),...longitudeArgs,cursor);const publicSectors=rows.slice(0,512),query={latitude,longitude,radius,limit:512,truncated:rows.length>512,nextCursor:rows.length>512?publicSectors.at(-1).id:null};\r\n const friends=[];for(const p of await all(db,'SELECT b.public_id,b.base,f.location FROM world_friends f JOIN world_bases b ON b.owner=f.a WHERE f.b=?',user)){const base=normalizeBase(JSON.parse(p.base));const friend={publicId:p.public_id,base:appearance(base)};if(p.location===1&&base.home)friend.home=base.home;friends.push(friend);}\r\n const rivals=[],mine=await presentSector(db,user,now,RIVAL_VIEWER_MS);\r\n if(mine){const fr=new Set(friends.map(f=>f.publicId));for(const p of await all(db,'SELECT b.owner,b.public_id,b.base,b.epoch,p.latitude,p.longitude,p.accuracy,p.updated_at,p.epoch AS pe FROM world_presence p JOIN world_bases b ON b.owner=p.owner WHERE p.owner<>? AND p.updated_at>=? ORDER BY p.updated_at DESC LIMIT 64',user,now-RIVAL_TARGET_MS)){if(rivals.length>=8)break;if(fr.has(p.public_id)||p.pe!==p.epoch)continue;const l=await locate({latitude:p.latitude,longitude:p.longitude,accuracy:p.accuracy,sampledAt:p.updated_at},now);if(l.status==='inside'&&l.sectorId===mine)rivals.push({publicId:p.public_id,base:rivalAppearance(normalizeBase(JSON.parse(p.base)))});}}\r\n return {serverTime:now,rivals,sites:sites().filter(s=>s.enabled!==false).map(s=>({...s})),sectors:publicSectors.map(s=>({id:s.id,siteId:s.site_id,kind:s.kind,latitude:s.latitude,longitude:s.longitude,polygonGeoJSON:sectorById(s.id)?.polygonGeoJSON,polygon:sectorById(s.id)?.polygon,initialPatchId:sectorById(s.id)?.initialPatchId,outpostFactions:sectorById(s.id)?.outpostFactions,controller:s.controller,revision:s.revision,generation:s.generation,closesAt:s.closes_at,graceUntil:s.grace_until,phase:s.phase,changedAt:s.changed_at,conflictAt:s.conflict_at,conversionProgress:Math.min(1,Math.max(0,(now-Math.max(s.changed_at,s.conflict_at))/WORLD_RULES.conversionMs))})),friends,query,geometryEpoch:2,rules:WORLD_RULES};\r\n}\r\n// Mirrors the credit condition in walkCommand, checking in the same order, so the first failing rule is named.\r\nasync function creditBlocker(db,sector,faction,{exists,next,counts,now}){\r\n if(exists)return 'already_walked_here_today';\r\n if(now-next.firstAt<WORLD_RULES.walkDwellMs)return 'dwell';\r\n if(next.reports<6)return 'samples';\r\n if(next.movement<40)return 'movement';\r\n if(counts.daily>=12)return 'daily_cap';\r\n if((counts.hourly??0)>=4)return 'hourly_cap';\r\n if(!(sector.controller===faction||sector.grace_until<=now))return 'sector_in_grace';\r\n return 'no_adjacent_controlled_sector';\r\n}\r\nasync function walkCommand(db,row,input,now){\r\n const base=advanceBase(JSON.parse(row.base),now),guards=[],writes=[];let result={};\r\n if(input.type==='walk-start'){\r\n if(input.confirmLawfulWalking!==true)fail('world_lawful_walking_confirmation_required',400);\r\n const old=await first(db,'SELECT * FROM world_walks WHERE owner=?',row.owner);if(old&&now-old.updated_at<30000)fail('world_walk_already_active');\r\n const sessionId=uuid(),nonce=uuid();writes.push(stmt(db,'INSERT INTO world_walks(owner,epoch,id,nonce,seq,state,updated_at) VALUES(?,?,?,?,0,?,?) ON CONFLICT(owner) DO UPDATE SET epoch=excluded.epoch,id=excluded.id,nonce=excluded.nonce,seq=0,state=excluded.state,updated_at=excluded.updated_at',row.owner,row.epoch,sessionId,nonce,json({issuedAt:now,lawfulWalking:true}),now));result={sessionId,nonce};\r\n }else{\r\n const session=await first(db,'SELECT * FROM world_walks WHERE owner=? AND epoch=? AND id=?',row.owner,row.epoch,input.sessionId);if(!session||input.nonce!==session.nonce)fail('world_walk_session_mismatch');if(now-session.updated_at>60000&&input.type!=='walk-stop')fail('world_walk_session_expired');\r\n guards.push(assert(db,'EXISTS(SELECT 1 FROM world_walks WHERE owner=? AND epoch=? AND id=? AND seq=? AND nonce=?)',[row.owner,row.epoch,session.id,session.seq,session.nonce]));\r\n if(input.type==='walk-stop'){writes.push(stmt(db,'DELETE FROM world_walks WHERE owner=?',row.owner),stmt(db,'DELETE FROM world_presence WHERE owner=?',row.owner));result={stopped:true};}\r\n else{\r\n validFix(input,now);if(!Number.isSafeInteger(input.seq)||input.seq!==session.seq+1||input.sampledAt<session.updated_at-15000)fail('world_walk_sequence');\r\n const prior=JSON.parse(session.state);if(input.sampledAt<(prior.issuedAt??session.updated_at)||input.sampledAt<=(prior.sampledAt??-1))fail('world_walk_fix_before_challenge');const fix={latitude:input.latitude,longitude:input.longitude,accuracy:input.accuracy};\r\n const dt=prior.lastAt?(now-prior.lastAt)/1000:0,travel=prior.last?distance(prior.last,fix):0;\r\n if(dt>0&&Math.max(0,travel-input.accuracy-(prior.last?.accuracy??0))/dt>3.5)fail('world_walk_impossible_speed');\r\n const publicLocation=await locate(input,now),containingPark=sites().find(site=>containsPoint(site.geometry,input.latitude,input.longitude));const location=containingPark&&!geography.isSiteOpen(containingPark,now)?{status:'closed',sectorId:sectorAt(input.latitude,input.longitude),siteId:containingPark.id}:publicLocation.status==='inside'||publicLocation.status==='ambiguous'?publicLocation:{status:'inside',sectorId:sectorAt(input.latitude,input.longitude),siteId:null};const inside=location.status==='inside',same=inside&&prior.sectorId===location.sectorId&&dt>0&&dt<=25;\r\n if(prior.lawfulWalking!==true)fail('world_lawful_walking_confirmation_required',400);\r\n const next={lawfulWalking:true,issuedAt:prior.issuedAt??session.updated_at,sampledAt:input.sampledAt,sectorId:inside?location.sectorId:null,firstAt:same?prior.firstAt:now,reports:same?prior.reports+1:1,movement:same?(prior.movement??0):0,last:fix,lastAt:now,anchor:same?(prior.anchor??prior.last):fix};\r\n // Distance is measured from a stable anchor, never a sum of GPS jitter.\r\n if(same&&distance(next.anchor,fix)>Math.max(12,fix.accuracy+next.anchor.accuracy)){next.movement+=Math.max(0,distance(next.anchor,fix)-Math.max(fix.accuracy,next.anchor.accuracy));next.anchor=fix;}\r\n writes.push(stmt(db,'UPDATE world_walks SET seq=?,state=?,updated_at=? WHERE owner=?',input.seq,json(next),now,row.owner));\r\n result={status:inside?'tracking':location.status,sectorId:inside?location.sectorId:null,dwellMs:now-next.firstAt,movement:next.movement,credited:false};\r\n if(inside){\r\n const isHub=publicLocation.status==='inside';if(isHub)enabledSite(publicLocation.siteId,now);\r\n let sector=await first(db,'SELECT * FROM world_sectors WHERE id=?',location.sectorId);if(sector&&!sector.enabled)fail('world_sector_unavailable');\r\n const definition=sectorById(location.sectorId);if(!definition)fail('world_sector_unavailable');\r\n const provisional=!sector;sector??={id:location.sectorId,site_id:null,kind:'territory',controller:null,revision:1,generation:1,closes_at:null,pause_until:null,grace_until:0,phase:'warzone',changed_at:now,conflict_at:now,enabled:1,latitude:definition.latitude,longitude:definition.longitude};\r\n if(!provisional)guards.push(sectorGuard(db,sector));\r\n if(isHub)writes.push(stmt(db,'INSERT INTO world_presence(owner,epoch,site_id,seq,latitude,longitude,accuracy,updated_at) VALUES(?,?,?,?,?,?,?,?) ON CONFLICT(owner) DO UPDATE SET epoch=excluded.epoch,site_id=excluded.site_id,seq=excluded.seq,latitude=excluded.latitude,longitude=excluded.longitude,accuracy=excluded.accuracy,updated_at=excluded.updated_at',row.owner,row.epoch,publicLocation.siteId,input.seq,input.latitude,input.longitude,input.accuracy,now));else writes.push(stmt(db,'DELETE FROM world_presence WHERE owner=?',row.owner));\r\n result.siteId=isHub?publicLocation.siteId:null;result.kind=sector.kind;result.territoryOnly=!isHub;\r\n const exists=await first(db,\"SELECT 1 AS ok FROM world_influence WHERE owner=? AND epoch=? AND day=? AND sector_id=? AND kind='walk'\",row.owner,row.epoch,day(now),sector.id);\r\n const counts=await first(db,\"SELECT COUNT(*) AS daily,SUM(CASE WHEN accepted_at>? THEN 1 ELSE 0 END) AS hourly FROM world_influence WHERE owner=? AND epoch=? AND day=? AND kind='walk'\",now-3600000,row.owner,row.epoch,day(now));\r\n if(!exists&&now-next.firstAt>=WORLD_RULES.walkDwellMs&&next.reports>=6&&next.movement>=40&&counts.daily<12&&(counts.hourly??0)<4&&(sector.controller===base.faction||sector.grace_until<=now)&&await canDeploy(db,sector,base.faction)){\r\n if(provisional){guards.push(assert(db,'NOT EXISTS(SELECT 1 FROM world_sectors WHERE id=?)',[sector.id]));writes.push(stmt(db,\"INSERT INTO world_sectors(id,site_id,kind,latitude,longitude,controller,revision,generation,phase,changed_at,conflict_at) VALUES(?,NULL,'territory',?,?,NULL,1,1,'warzone',?,?)\",sector.id,definition.latitude,definition.longitude,now,now));}\r\n guards.push(deploymentGuard(db,sector,base.faction));\r\n guards.push(assert(db,\"(SELECT COUNT(*) FROM world_influence WHERE owner=? AND epoch=? AND day=? AND kind='walk')<12 AND (SELECT COUNT(*) FROM world_influence WHERE owner=? AND epoch=? AND accepted_at>? AND kind='walk')<4\",[row.owner,row.epoch,day(now),row.owner,row.epoch,now-3600000]));\r\n writes.push(stmt(db,\"INSERT INTO world_influence(id,owner,epoch,day,sector_id,faction,kind,points,accepted_at,expires_at) VALUES(?,?,?,?,?,?,'walk',1,?,?)\",uuid(),row.owner,row.epoch,day(now),sector.id,base.faction,now,now+WORLD_RULES.influenceMs));const window=await maybeWindow(db,sector,now,base.faction);if(window)writes.push(window);result.credited=true;\r\n }else result.reason=await creditBlocker(db,sector,base.faction,{exists,next,counts,now});\r\n }else writes.push(stmt(db,'DELETE FROM world_presence WHERE owner=?',row.owner));\r\n }\r\n }\r\n return {base,charge:row.charge,guards,writes,result};\r\n}\r\nasync function socialCommand(db,row,input,now){\r\n const base=advanceBase(JSON.parse(row.base),now),guards=[],writes=[];let charge=row.charge,result={};\r\n if(input.type==='friend-invite'){const code=uuid();writes.push(stmt(db,'INSERT INTO world_friend_codes(code,owner,epoch,expires_at) VALUES(?,?,?,?)',code,row.owner,row.epoch,now+86400000));result={code,expiresAt:now+86400000};}\r\n if(input.type==='friend-accept'){\r\n const invitation=await first(db,'SELECT * FROM world_friend_codes WHERE code=? AND expires_at>?',input.code,now);if(!invitation||invitation.owner===row.owner)fail('world_friend_code_invalid');\r\n guards.push(epochGuard(db,invitation.owner,invitation.epoch),assert(db,'EXISTS(SELECT 1 FROM world_friend_codes WHERE code=? AND owner=? AND epoch=? AND expires_at>?)',[input.code,invitation.owner,invitation.epoch,now]));\r\n writes.push(stmt(db,'INSERT INTO world_friends(a,b,location) VALUES(?,?,0) ON CONFLICT DO NOTHING',row.owner,invitation.owner),stmt(db,'INSERT INTO world_friends(a,b,location) VALUES(?,?,0) ON CONFLICT DO NOTHING',invitation.owner,row.owner),stmt(db,'DELETE FROM world_friend_codes WHERE code=?',input.code));result={connected:true};\r\n }\r\n if(['friend-revoke','friend-location'].includes(input.type)){\r\n const target=await first(db,'SELECT owner FROM world_bases WHERE public_id=?',input.publicId);if(!target)fail('world_friend_not_found',404);\r\n if(input.type==='friend-revoke'){writes.push(stmt(db,'DELETE FROM world_friends WHERE (a=? AND b=?) OR (a=? AND b=?)',row.owner,target.owner,target.owner,row.owner));result={revoked:true};}\r\n else{writes.push(stmt(db,'UPDATE world_friends SET location=? WHERE a=? AND b=?',input.shareLocation===true?1:0,row.owner,target.owner));result={shareLocation:input.shareLocation===true};}\r\n }\r\n if(input.type==='encounter-invite'){\r\n enabledSite(input.siteId,now);const target=await first(db,'SELECT * FROM world_bases WHERE public_id=?',input.targetPublicId);if(!target||target.owner===row.owner)fail('world_opponent_unavailable');\r\n const [pa,pb]=await Promise.all([first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=? AND site_id=?',row.owner,row.epoch,input.siteId),first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=? AND site_id=?',target.owner,target.epoch,input.siteId)]);\r\n if(!pa||!pb||pa.updated_at<now-15000||pb.updated_at<now-15000||distance(pa,pb)+pa.accuracy+pb.accuracy>80)fail('world_both_players_must_be_nearby');\r\n const la=await locate({...pa,sampledAt:pa.updated_at},now),lb=await locate({...pb,sampledAt:pb.updated_at},now);if(la.status!=='inside'||lb.status!=='inside'||la.sectorId!==lb.sectorId)fail('world_same_public_sector_required');\r\n const sector=await first(db,'SELECT * FROM world_sectors WHERE id=? AND enabled=1',la.sectorId);if(!sector)fail('world_sector_unavailable');if(sector.closes_at&&sector.closes_at<=now)fail('world_capture_window_closed');if(charge<1)fail('world_launch_charge_required');\r\n const busy=await first(db,\"SELECT id FROM world_encounters WHERE (a IN (?,?) OR b IN (?,?)) AND status IN ('pending','admitted')\",row.owner,target.owner,row.owner,target.owner);if(busy)fail('world_encounter_already_active');\r\n guards.push(epochGuard(db,target.owner,target.epoch),baseGuard(db,target),presenceGuard(db,pa,now),presenceGuard(db,pb,now),sectorGuard(db,sector),assert(db,\"NOT EXISTS(SELECT 1 FROM world_encounters WHERE (a IN (?,?) OR b IN (?,?)) AND status IN ('pending','admitted'))\",[row.owner,target.owner,row.owner,target.owner]));\r\n if(input.confirmPublicPath!==true||!input.meetingAnchor)fail('world_public_path_confirmation_required',400);const anchor=input.meetingAnchor;if(!Number.isFinite(anchor.latitude)||!Number.isFinite(anchor.longitude)||Math.abs(anchor.latitude)>90||Math.abs(anchor.longitude)>180)fail('world_meeting_anchor_invalid',400);const anchorLocation=await locate({...anchor,accuracy:Math.max(pa.accuracy,pb.accuracy)},now);if(anchorLocation.status!=='inside'||anchorLocation.sectorId!==sector.id||distance(pa,anchor)+pa.accuracy>60||distance(pb,anchor)+pb.accuracy>60)fail('world_meeting_anchor_not_nearby');\r\n const id=uuid();writes.push(stmt(db,\"INSERT INTO world_encounters(id,a,ae,b,be,site_id,anchor,sector_id,generation,status,revision,a_reserved,b_reserved,expires_at,faction_a,faction_b) VALUES(?,?,?,?,?,?,?,?,?,'pending',1,1,0,?,?,?)\",id,row.owner,row.epoch,target.owner,target.epoch,input.siteId,json(anchor),sector.id,sector.generation,now+120000,base.faction,JSON.parse(target.base).faction));charge--;result={encounter:{id,status:'pending',expiresAt:now+120000}};\r\n }\r\n if(['encounter-accept','encounter-cancel'].includes(input.type)){\r\n const e=await first(db,'SELECT * FROM world_encounters WHERE id=?',input.encounterId);if(!e||![e.a,e.b].includes(row.owner))fail('world_encounter_not_found',404);if(!['pending','admitted'].includes(e.status))fail('world_encounter_terminal');\r\n const [a,b]=await partyRows(db,e);if(!a||!b)fail('world_opponent_unavailable');const other=row.owner===e.a?b:a;\r\n guards.push(epochGuard(db,other.owner,other.epoch),baseGuard(db,other),encounterGuard(db,e));\r\n if(input.type==='encounter-cancel'){\r\n const ownRefund=row.owner===e.a?e.a_reserved:e.b_reserved,otherRefund=row.owner===e.a?e.b_reserved:e.a_reserved;charge=Math.min(10,charge+ownRefund);\r\n writes.push(stmt(db,'UPDATE world_bases SET charge=MIN(10,charge+?),revision=revision+1 WHERE owner=? AND epoch=?',otherRefund,other.owner,other.epoch),stmt(db,\"UPDATE world_encounters SET status='cancelled',a_reserved=0,b_reserved=0,revision=revision+1 WHERE id=?\",e.id));result={encounter:{id:e.id,status:'cancelled'}};\r\n }else{\r\n if(e.b!==row.owner||e.status!=='pending'||e.expires_at<=now)fail('world_encounter_not_accepting');if(charge<1)fail('world_launch_charge_required');enabledSite(e.site_id,now);\r\n const pa=await first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=?',a.owner,a.epoch),pb=await first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=?',b.owner,b.epoch);\r\n if(!pa||!pb||pa.site_id!==e.site_id||pb.site_id!==e.site_id||pa.updated_at<now-15000||pb.updated_at<now-15000||distance(pa,pb)+pa.accuracy+pb.accuracy>80)fail('world_both_players_must_be_nearby');\r\n const la=await locate({...pa,sampledAt:pa.updated_at},now),lb=await locate({...pb,sampledAt:pb.updated_at},now);if(la.status!=='inside'||lb.status!=='inside'||la.sectorId!==e.sector_id||lb.sectorId!==e.sector_id)fail('world_same_public_sector_required');\r\n const anchor=JSON.parse(e.anchor);if(distance(pa,anchor)+pa.accuracy>60||distance(pb,anchor)+pb.accuracy>60)fail('world_meeting_anchor_not_nearby');\r\n const sector=await first(db,'SELECT * FROM world_sectors WHERE id=? AND enabled=1',e.sector_id);if(sector?.closes_at&&sector.closes_at<=now)fail('world_capture_window_closed');if(!sector||sector.generation!==e.generation||JSON.parse(a.base).faction!==e.faction_a||base.faction!==e.faction_b)fail('world_encounter_state_changed');\r\n guards.push(presenceGuard(db,pa,now),presenceGuard(db,pb,now),sectorGuard(db,sector),assert(db,\"EXISTS(SELECT 1 FROM world_encounters WHERE id=? AND status='pending' AND expires_at>?)\",[e.id,now]),assert(db,'EXISTS(SELECT 1 FROM world_sectors WHERE id=? AND (closes_at IS NULL OR closes_at>?))',[sector.id,now]));charge--;\r\n writes.push(stmt(db,\"UPDATE world_encounters SET status='admitted',b_reserved=1,due_at=?,admitted_at=?,revision=revision+1 WHERE id=?\",now+30000,now,e.id));\r\n if(sector.closes_at&&!sector.pause_until)writes.push(stmt(db,'UPDATE world_sectors SET pause_until=closes_at+120000,revision=revision+1 WHERE id=?',sector.id));\r\n result={encounter:{id:e.id,status:'admitted',dueAt:now+30000}};\r\n }\r\n }\r\n return {base,charge,guards,writes,result};\r\n}\r\nasync function shopCommand(db,row,input,now){\r\n const base=advanceBase(JSON.parse(row.base),now),p=await first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=? AND site_id=?',row.owner,row.epoch,input.siteId);\r\n enabledSite(input.siteId,now);if(!p||p.updated_at<now-15000)fail('world_visit_nearby_required');const guards=[presenceGuard(db,p,now)],writes=[];const item=input.type==='visit'?'visit':input.itemId;\r\n const cosmetic=NPC_ITEMS.find(i=>i.id===item);if(!['visit','ration','calm-token'].includes(item)&&!cosmetic)fail('world_shop_item_unknown',400);if(cosmetic&&base.inventory[item])fail('world_shop_already_owned');\r\n const already=await first(db,'SELECT 1 AS ok FROM world_visits WHERE owner=? AND epoch=? AND day=? AND site_id=? AND item_id=?',row.owner,row.epoch,day(now),input.siteId,item);if(already)fail('world_daily_shop_limit');\r\n if(item!=='visit'){const track=base.faction,cost=cosmetic?.cost??5;if((base.materials[track]??0)<cost)fail('world_shop_materials_required');base.materials[track]-=cost;if(cosmetic)base.inventory[item]=true;else{const key=item==='ration'?'rations':'calmTokens';base[key]=(base[key]??0)+1;}}\r\n writes.push(stmt(db,'INSERT INTO world_visits(owner,epoch,day,site_id,item_id) VALUES(?,?,?,?,?)',row.owner,row.epoch,day(now),input.siteId,item));return {base,charge:row.charge,guards,writes,result:{visited:true,itemId:item}};\r\n}\r\n// R58 zones. The server trusts only its own walk state (last validated fix, dwell start), never a client fix.\r\nconst zoneNpcCandidates=zoneId=>npcCandidates(zoneId,sites());\r\nfunction zoneSpots(zoneId){return npcSpots(zoneId,zoneNpcCandidates(zoneId),ZONE_BAKE_VERSION);}\r\nasync function zoneWalk(db,row,input,now){\r\n const session=await first(db,'SELECT * FROM world_walks WHERE owner=? AND epoch=? AND id=?',row.owner,row.epoch,input.sessionId);\r\n if(!session||input.nonce!==session.nonce)fail('world_walk_session_mismatch');\r\n if(now-session.updated_at>15000)fail('world_walk_session_expired');\r\n const prior=JSON.parse(session.state);if(prior.lawfulWalking!==true||!prior.last)fail('world_zone_walk_required');\r\n return {session,prior,guard:assert(db,'EXISTS(SELECT 1 FROM world_walks WHERE owner=? AND epoch=? AND id=? AND seq=? AND nonce=?)',[row.owner,row.epoch,session.id,session.seq,session.nonce])};\r\n}\r\nasync function zonesDTO(db,user,now,request){\r\n const params=new URL(request.url).searchParams,latitude=Number(params.get('latitude')),longitude=Number(params.get('longitude'));\r\n const center=zoneOf(latitude,longitude);if(!center||!params.has('latitude')||!params.has('longitude'))fail('world_zones_query_invalid',400);\r\n const ring=gridDisk(center,1),zones=[];\r\n const neutral=new Set((await all(db,`SELECT id FROM world_neutral_zones WHERE id IN (${ring.map(()=>'?').join(',')})`,...ring)).map(r=>r.id));\r\n for(const id of ring){\r\n  const z=await first(db,'SELECT claimed_by,controller,claimed_at FROM world_zones WHERE id=?',id);\r\n  const defeated=new Set((await all(db,'SELECT idx FROM world_zone_npc WHERE zone_id=?',id)).map(r=>r.idx));\r\n  zones.push({id,neutral:neutral.has(id),controller:z?.controller??null,claimedBy:z?.claimed_by??null,phase:!z?'unclaimed':now-z.claimed_at<WORLD_RULES.conversionMs?'converting':'stable',npc:zoneSpots(id).map(s=>({idx:s.idx,tier:null,faction:null,defeated:defeated.has(s.idx)}))});\r\n }\r\n return {serverTime:now,center,zones};\r\n}\r\nasync function zoneCommand(db,row,input,now){\r\n const base=advanceBase(JSON.parse(row.base),now),level=zoneLevel(base),{session,prior,guard}=await zoneWalk(db,row,input,now),fix=prior.last;\r\n if(input.type==='claim-zone'){\r\n  const zoneId=zoneOf(fix.latitude,fix.longitude);if(!zoneId||input.zoneId!==zoneId)fail('world_zone_not_here',409);\r\n  if(await first(db,'SELECT id FROM world_neutral_zones WHERE id=?',zoneId))fail('world_zone_neutral-nature',409);\r\n  const last=await first(db,'SELECT lat,lng,at FROM world_last_rewarded WHERE owner=?',row.owner);\r\n  const verdict=claimAllowed({fix,center:zoneCenter(zoneId),level,dwellMs:now-(prior.firstAt??Infinity),lastRewarded:last?{latitude:last.lat,longitude:last.lng,at:last.at}:null,now});\r\n  if(!verdict.ok)fail('world_zone_'+verdict.reason,409);\r\n  const held=await first(db,'SELECT controller FROM world_zones WHERE id=?',zoneId);// R5-C4: an enemy-held zone needs this player's own NPC defeat\r\n  if(held?.controller&&held.controller!==base.faction&&!await first(db,'SELECT 1 AS ok FROM world_zone_npc WHERE zone_id=? AND defeated_by=?',zoneId,row.public_id))fail('world_zone_npc_required',409);\r\n  const writes=[stmt(db,'DELETE FROM world_zone_npc WHERE zone_id=?',zoneId),stmt(db,'INSERT INTO world_zones(id,controller,claimed_by,claimed_at,revision) VALUES(?,?,?,?,1) ON CONFLICT(id) DO UPDATE SET controller=excluded.controller,claimed_by=excluded.claimed_by,claimed_at=excluded.claimed_at,revision=world_zones.revision+1',zoneId,base.faction,row.public_id,now)];\r\n  for(const sid of zoneSectors(zoneId)){const [lat,lng]=cellToLatLng(sid);writes.push(stmt(db,\"INSERT INTO world_sectors(id,site_id,kind,latitude,longitude,controller,revision,generation,phase,changed_at,conflict_at,enabled) VALUES(?,NULL,'territory',?,?,?,1,1,'converting',?,?,1) ON CONFLICT(id) DO UPDATE SET controller=excluded.controller,generation=world_sectors.generation+1,revision=world_sectors.revision+1,phase='converting',changed_at=excluded.changed_at,conflict_at=excluded.conflict_at WHERE world_sectors.kind='territory'\",sid,lat,lng,base.faction,now,now));}\r\n  writes.push(stmt(db,'INSERT INTO world_last_rewarded(owner,epoch,lat,lng,at) VALUES(?,?,?,?,?) ON CONFLICT(owner) DO UPDATE SET epoch=excluded.epoch,lat=excluded.lat,lng=excluded.lng,at=excluded.at',row.owner,row.epoch,fix.latitude,fix.longitude,now));\r\n  return {base,charge:row.charge,guards:[guard],writes,result:{zoneId,claimedBy:row.public_id}};\r\n }\r\n // npc-defeated\r\n if(level<2)fail('world_zone_level_required',409);// same gate as claim-zone: HQ plus one upgrade, not account level 10\r\n const ring=gridDisk(zoneOf(fix.latitude,fix.longitude),1);if(!ring.includes(input.zoneId))fail('world_zone_out_of_ring',409);\r\n const spot=zoneSpots(input.zoneId).find(s=>s.idx===input.idx);if(!spot)fail('world_zone_npc_unknown',409);\r\n return {base,charge:row.charge,guards:[guard,assert(db,'NOT EXISTS(SELECT 1 FROM world_zone_npc WHERE zone_id=? AND idx=?)',[input.zoneId,input.idx])],writes:[stmt(db,'INSERT INTO world_zone_npc(zone_id,idx,defeated_by,at) VALUES(?,?,?,?)',input.zoneId,input.idx,row.public_id,now)],result:{zoneId:input.zoneId,idx:input.idx}};\r\n}\r\nexport async function worldApi(request,db,user,path,method,input={},now=Date.now(),{dataEpoch=1,zones=false,wild=null,runs=false,raids=false}={}){\r\n if(!Number.isSafeInteger(now)||now<0)fail('world_invalid_time',400);\r\n if(path==='/api/world/raids'||path==='/api/world/raids/current'||path==='/api/world/raids/start'||path==='/api/world/raids/complete')return worldRaidApi(request,db,user,path,method,input,now,{dataEpoch,enabled:raids});\r\n if(path==='/api/world/runs'||path==='/api/world/runs/current')return worldRunApi(request,db,user,path,method,input,now,{dataEpoch,enabled:runs});\r\n if(path.startsWith('/api/world/runs/')){if(!runs)fail('world_route_not_found',404);return worldRunRuntimeApi(request,db,user,dataEpoch,path,method,input,now,{raidsEnabled:raids});}\r\n let row=await provision(db,user,dataEpoch,now);\r\n if(path==='/api/world/drops')return worldDropsApi(request,db,row,method,input,now);\r\n if(method==='GET'){\r\n if(path==='/api/world/run-benefits')return {targetAccountId:user,dataEpoch:row.epoch,revision:row.revision,serverTime:now,balance:readProgression(JSON.parse(row.base)).balance,benefits:getRunStartBenefits(readProgression(JSON.parse(row.base)),{accountId:user})};\r\n if(path==='/api/world/map')return mapDTO(db,user,now,request);\r\n if(path==='/api/world/zones'){if(!zones)fail('world_route_not_found',404);return zonesDTO(db,user,now,request);}\r\n if(path==='/api/world/wild/nearby'){if(!wild||!zones)fail('world_route_not_found',404);return wildNearby(db,row,now,request,wild);}\r\n if(path.startsWith('/api/world/bases/'))return privateBase(db,user,decodeURIComponent(path.slice('/api/world/bases/'.length)),now);\r\n if(path.startsWith('/api/world/encounters/')){const e=await first(db,'SELECT * FROM world_encounters WHERE id=?',decodeURIComponent(path.slice('/api/world/encounters/'.length)));if(!e||![e.a,e.b].includes(user))fail('world_encounter_not_found',404);return {serverTime:now,encounter:encounterDTO(await settleEncounter(db,e,now),user)};}\r\n if(path!=='/api/world')fail('world_route_not_found',404);\r\n return ownSnapshot(db,row,now);\r\n }\r\n if(method!=='POST'||path!=='/api/world')fail('world_route_not_found',404);\r\n if(typeof input.type!=='string'||input.type.length>40)fail('world_command_required',400);\r\n if(typeof input.id!=='string'||!/^[A-Za-z0-9:_-]{1,100}$/.test(input.id)||request.headers.get('Idempotency-Key')!==input.id)fail('world_idempotency_key_required',400);\r\n const hash=await fingerprint(input),readReplay=()=>first(db,'SELECT * FROM world_requests WHERE owner=? AND epoch=? AND id=?',user,dataEpoch,input.id);\r\n let receipt=await readReplay();if(receipt){if(receipt.hash!==hash)fail('world_idempotency_payload_conflict');return JSON.parse(receipt.response);}\r\n if(!Number.isSafeInteger(input.revision)||input.revision!==row.revision)fail('world_revision_conflict');\r\n await seed(db,now);\r\n let command;\r\n if(input.type==='reconcile')command=await reconcile(db,row,now);\r\n else if(input.type==='npc-claim-rewards'){\r\n  if(Object.hasOwn(input,'amount'))fail('npc_reward_amount_is_server_owned',400);\r\n  try{command={...await claimVerifiedWorkoutCurrency(db,advanceBase(JSON.parse(row.base),now),{accountId:user,dataEpoch:row.epoch,now}),charge:row.charge,writes:[]};}\r\n  catch(e){if(e instanceof RangeError||e instanceof TypeError)fail(e.message,400);throw e;}\r\n }\r\n else if(input.type==='rts-defense-drill'){\r\n  try{command={base:defenseDrill(advanceBase(JSON.parse(row.base),now),input,{now}),charge:row.charge,writes:[],result:{type:input.type}};}\r\n  catch(e){if(e instanceof RangeError)fail(e.message,400);throw e;}\r\n }\r\n else if(input.type==='ar-catch'){\r\n  try{const {entry}=arCatchEntry(input,now);command={base:applyBaseAction(advanceBase(JSON.parse(row.base),now),{type:'wild-catch',entry},{now,wildServer:true}),charge:row.charge,writes:[],result:{caught:true,entry}};}\r\n  catch(e){if(e instanceof RangeError)fail(e.message,400);throw e;}\r\n }\r\n else if(input.type==='npc-purchase'||input.type==='npc-run-reward'){\r\n  try{\r\n   const current=advanceBase(JSON.parse(row.base),now);\r\n   const next=input.type==='npc-purchase'?applyNpcPurchase(current,{itemId:input.itemId,npcId:input.npcId,receiptId:input.receiptId,accountId:user},{now}):await creditVerifiedRunReward(db,current,{...input,dataEpoch:row.epoch},{accountId:user,now});\r\n   if(!next.result.ok)fail(next.result.reason,409);\r\n   command={...next,charge:row.charge,writes:[]};\r\n  }catch(e){if(e instanceof RangeError||e instanceof TypeError)fail(e.message,400);throw e;}\r\n }\r\n else if(input.type.startsWith('walk-'))command=await walkCommand(db,row,input,now);\r\n else if(input.type.startsWith('friend-')||input.type.startsWith('encounter-'))command=await socialCommand(db,row,input,now);\r\n else if(['visit','purchase'].includes(input.type))command=await shopCommand(db,row,input,now);\r\n else if(['claim-zone','npc-defeated'].includes(input.type)){if(!zones)fail('world_route_not_found',404);command=await zoneCommand(db,row,input,now);}\r\n else if(input.type.startsWith('wild-')){if(!wild||!zones)fail('world_route_not_found',404);command=await wildCommand(db,row,input,now,{...wild,zoneWalk});}\r\n else if(input.type==='tick')command={base:advanceBase(JSON.parse(row.base),now),charge:row.charge,writes:[],result:{tick:true}};\r\n else{\r\n if(input.type==='faction'){\r\n const old=JSON.parse(row.base);if(now-(old.factionChangedAt??0)<7*86400000&&old.factionChangedAt)fail('world_faction_cooldown');\r\n const active=await first(db,\"SELECT id FROM world_encounters WHERE (a=? OR b=?) AND status IN ('pending','admitted')\",user,user);if(active)fail('world_encounter_active');\r\n }\r\n const actionBase=advanceBase(JSON.parse(row.base),now),guards=[];let sharedMissionAuthorized=false;\r\n if(input.type==='launch-expedition'&&actionBase.expedition?.shared){const mission=actionBase.expedition;enabledSite(mission.siteId,now);const p=await first(db,'SELECT * FROM world_presence WHERE owner=? AND epoch=? AND site_id=?',user,dataEpoch,mission.siteId);if(!p||p.updated_at<now-15000)fail('world_shared_expedition_requires_fresh_hub');guards.push(presenceGuard(db,p,now));sharedMissionAuthorized=true;}\r\n try{command={base:applyBaseAction({...actionBase,launchCharge:row.charge},input,{now,sharedMissionAuthorized}),charge:row.charge,guards,writes:[],result:{type:input.type}};}catch(e){if(e instanceof RangeError)fail(e.message,400);throw e;}\r\n }\r\n const projected={...row,base:json(command.base),charge:command.charge,revision:row.revision+1},response={...await ownSnapshot(db,projected,now),result:command.result};\r\n try{await atomic(db,[epochGuard(db,user,dataEpoch),baseGuard(db,row),...(command.guards??[])],[...(command.writes??[]),saveBase(db,row,command.base,command.charge),stmt(db,'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)',user,dataEpoch,input.id,hash,json(response))]);}\r\n catch(error){if(error instanceof GameGenerationError)throw error;receipt=await readReplay();if(receipt){if(receipt.hash!==hash)fail('world_idempotency_payload_conflict');return JSON.parse(receipt.response);}const epoch=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',user);if(epoch?.epoch!==dataEpoch)fail('world_epoch_mismatch');const latest=await first(db,'SELECT revision FROM world_bases WHERE owner=?',user);if(latest?.revision!==row.revision)fail('world_revision_conflict');if(String(error.message).includes('world_assertions'))fail('world_state_conflict');throw error;}\r\n if(input.type==='tick'){await settleOwnEncounters(db,user,now);await settleDueWorldSectors(db,now);}\r\n return response;\r\n}\r\n// Called inside the primary epoch-increment/deletion batch. Refund surviving\r\n// reservations first, then erase personal rows. Earned anonymous control persists.\r\nexport function worldDeletionStatements(db,user,raidTablesAvailable=false){return [\r\n ...(raidTablesAvailable?[stmt(db,'DELETE FROM world_raid_pack_claims WHERE owner=?',user),stmt(db,'DELETE FROM world_raids WHERE owner=?',user)]:[]),\r\n ...worldDropDeletionStatements(db,user),\r\n ...wildDeletionStatements(db,user),\r\n stmt(db,`UPDATE world_bases SET charge=MIN(10,charge+COALESCE((SELECT SUM(CASE WHEN e.a=world_bases.owner THEN e.a_reserved ELSE e.b_reserved END) FROM world_encounters e WHERE (e.a=? OR e.b=?) AND e.status IN ('pending','admitted') AND ((e.a=world_bases.owner AND e.ae=world_bases.epoch) OR (e.b=world_bases.owner AND e.be=world_bases.epoch))),0)),revision=revision+1 WHERE owner<>? AND owner IN (SELECT a FROM world_encounters WHERE b=? UNION SELECT b FROM world_encounters WHERE a=?)`,user,user,user,user,user),\r\n stmt(db,\"UPDATE world_encounters SET status='cancelled',a_reserved=0,b_reserved=0,revision=revision+1 WHERE (a=? OR b=?) AND status IN ('pending','admitted')\",user,user),\r\n ...['world_requests','world_sources','world_grants','world_friend_codes','world_walks','world_presence','world_influence','world_visits'].map(t=>stmt(db,`DELETE FROM ${t} WHERE owner=?`,user)),\r\n stmt(db,'DELETE FROM world_friends WHERE a=? OR b=?',user,user),stmt(db,'DELETE FROM world_encounters WHERE a=? OR b=?',user,user),stmt(db,'DELETE FROM world_bases WHERE owner=?',user),\r\n ];}\r\n// Owner-only account export. Receipts/challenges are deliberately excluded:\r\n// they contain replay responses, private location copies and live nonces.\r\nexport async function exportWorldData(db,user,epoch){\r\n const base=await first(db,'SELECT epoch,public_id,revision,base,charge,created_at FROM world_bases WHERE owner=? AND epoch=?',user,epoch);\r\n const progression={};\r\n for(const table of ['world_sources','world_grants','world_visits','world_influence']){\r\n  progression[table]=(await all(db,`SELECT * FROM ${table} WHERE owner=? AND epoch=?`,user,epoch)).map(({owner,...row})=>row);\r\n }\r\n const raidTables=await first(db,\"SELECT 1 AS ready FROM sqlite_master WHERE type='table' AND name='world_raids'\");\r\n progression.worldRaids=raidTables?await all(db,'SELECT raid_id,status,revision,started_seq,started_at,target_id,tier,result,pack_id,completed_at FROM world_raids WHERE owner=? AND epoch=? ORDER BY started_seq',user,epoch):[];\r\n progression.worldRaidPackClaims=raidTables?await all(db,'SELECT raid_id,pack_id,tier,result,granted_at FROM world_raid_pack_claims WHERE owner=? AND epoch=? ORDER BY granted_at,raid_id',user,epoch):[];\r\n const friends=base?await all(db,'SELECT b.public_id AS publicId,f.location AS shareLocation FROM world_friends f JOIN world_bases b ON b.owner=f.b WHERE f.a=?',user):[];\r\n const encounters=await all(db,'SELECT id,site_id,sector_id,status,due_at,admitted_at,expires_at FROM world_encounters WHERE (a=? AND ae=?) OR (b=? AND be=?)',user,epoch,user,epoch);\r\n return {dataEpoch:epoch,station:base?{publicId:base.public_id,revision:base.revision,base:normalizeBase(JSON.parse(base.base)),charge:base.charge,createdAt:base.created_at}:null,progression,friends,encounters};\r\n}\r\n","server/world-boss-progression-schema.sql":"CREATE TABLE world_boss_progression(\n owner TEXT NOT NULL,\n epoch INTEGER NOT NULL CHECK(typeof(epoch)='integer' AND epoch BETWEEN 1 AND 9007199254740991),\n clear_count INTEGER NOT NULL CHECK(typeof(clear_count)='integer' AND clear_count BETWEEN 0 AND 9007199254740991),\n revision INTEGER NOT NULL CHECK(typeof(revision)='integer' AND revision BETWEEN 0 AND 9007199254740991),\n updated_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch)\n);\n--> statement-breakpoint\nCREATE TABLE world_boss_run_pins(\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, run_id TEXT NOT NULL,\n started_seq INTEGER NOT NULL CHECK(typeof(started_seq)='integer' AND started_seq>=1),\n issued_clear_ordinal INTEGER NOT NULL CHECK(typeof(issued_clear_ordinal)='integer' AND issued_clear_ordinal BETWEEN 0 AND 9007199254740991),\n ledger_revision INTEGER NOT NULL CHECK(typeof(ledger_revision)='integer' AND ledger_revision BETWEEN 0 AND 9007199254740991),\n schedule_version INTEGER NOT NULL CHECK(schedule_version=2),\n policy_id TEXT NOT NULL CHECK(policy_id='authored-triples-clear-v1'),\n groups_hash TEXT NOT NULL CHECK(length(groups_hash)=64),\n rows_hash TEXT NOT NULL CHECK(length(rows_hash)=64),\n group_id TEXT NOT NULL,\n assignment_id TEXT NOT NULL CHECK(length(assignment_id)=64),\n assignment TEXT NOT NULL,\n issued_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,run_id), UNIQUE(owner,epoch,started_seq)\n);\n--> statement-breakpoint\nCREATE TABLE world_boss_clear_receipts(\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, run_id TEXT NOT NULL,\n level INTEGER NOT NULL CHECK(level=1),\n clear_ordinal INTEGER NOT NULL CHECK(typeof(clear_ordinal)='integer' AND clear_ordinal BETWEEN 0 AND 9007199254740991),\n level_receipt_id TEXT NOT NULL,\n fingerprint TEXT NOT NULL CHECK(length(fingerprint)=64),\n result TEXT NOT NULL,\n run_revision INTEGER NOT NULL, ledger_revision INTEGER NOT NULL, accepted_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,run_id,level), UNIQUE(owner,epoch,clear_ordinal), UNIQUE(owner,epoch,level_receipt_id)\n);\n--> statement-breakpoint\nCREATE TRIGGER world_boss_run_pins_immutable BEFORE UPDATE ON world_boss_run_pins\nBEGIN SELECT RAISE(ABORT,'boss_pin_immutable'); END;\n--> statement-breakpoint\nCREATE TRIGGER world_boss_clear_receipts_immutable BEFORE UPDATE ON world_boss_clear_receipts\nBEGIN SELECT RAISE(ABORT,'boss_receipt_immutable'); END;\n","server/world-boss-progression.mjs":"import {validateRunManifest} from '../modules/roguelite/run-schedule-v3.mjs';\nimport {validateRescueRunManifest} from '../modules/roguelite/run-schedule-v4.mjs';\nimport { RunStorageError, canonicalJson, fingerprint, epochGuard, readCurrentEpoch } from './world-run-storage.mjs';\r\nimport { bossAssignmentForClearCount, SCHEDULE_VERSION_V2, BOSS_POLICY_ID } from '../modules/roguelite/run-schedule-v2.mjs';\r\n\r\nexport const BOSS_PROGRESSION_VERSION = 1;\r\nexport const BOSS_PROGRESSION_TABLES = Object.freeze(['world_boss_progression','world_boss_run_pins','world_boss_clear_receipts','world_boss_run_manifests','world_boss_level_clear_receipts','world_coach_rescues','world_run_rescue_manifests','world_run_rescue_level_receipts']);\nconst bad = (v, field) => { if (v) throw new RunStorageError('run_storage_invalid',400,field); };\r\nconst safe = (v,min=0) => Number.isSafeInteger(v)&&v>=min;\r\nconst nonempty = v => typeof v==='string'&&v.length>0;\r\nfunction scope(owner,epoch){ bad(!nonempty(owner),'owner'); bad(!safe(epoch,1),'epoch'); }\r\nfunction freeze(v){ if(v&&typeof v==='object'&&!Object.isFrozen(v)){ for(const x of Object.values(v)) freeze(x); Object.freeze(v); } return v; }\r\nconst stmt=(db,sql,...args)=>db.prepare(sql).bind(...args);\r\nconst assertion=(db,condition,args=[])=>stmt(db,`INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`,crypto.randomUUID(),...args);\r\nexport async function readBossProgression(db,owner,epoch){ scope(owner,epoch); const r=await db.prepare('SELECT clear_count,revision FROM world_boss_progression WHERE owner=? AND epoch=?').bind(owner,epoch).first(); return Object.freeze({clearCount:r?.clear_count??0,revision:r?.revision??0,exists:!!r}); }\r\nasync function readRootBossRunPin(db,owner,epoch,runId){ scope(owner,epoch); bad(!nonempty(runId),'runId'); const r=await db.prepare('SELECT * FROM world_boss_run_pins WHERE owner=? AND epoch=? AND run_id=?').bind(owner,epoch,runId).first(); if(!r)return null; return freeze({runId:r.run_id,startedSeq:r.started_seq,issuedClearOrdinal:r.issued_clear_ordinal,ledgerRevision:r.ledger_revision,groupId:r.group_id,assignmentId:r.assignment_id,tableHashes:{groups:r.groups_hash,eligibilityRows:r.rows_hash},assignment:JSON.parse(r.assignment)}); }\r\nexport async function readBossClearContext(db,{owner,epoch,runId,levelIndex}){ scope(owner,epoch); bad(!nonempty(runId),'runId'); const [ledger,pin,rows]=await Promise.all([readBossProgression(db,owner,epoch),readBossRunPin(db,owner,epoch,runId,levelIndex),db.prepare('SELECT coach_id FROM world_coach_unlocks WHERE owner=? AND epoch=? ORDER BY coach_id').bind(owner,epoch).all()]); const context={ledger,pin,ownedCoachIds:rows.results.map(x=>x.coach_id)}; Object.defineProperties(context,{owner:{value:owner,enumerable:false},epoch:{value:epoch,enumerable:false}}); return freeze(context); }\r\nfunction evidenceShape(e){ bad(!e||typeof e!=='object'||Array.isArray(e)||Object.keys(e).some(k=>!['levelReceiptId','bosses','levelIndex'].includes(k))||!nonempty(e.levelReceiptId)||!Array.isArray(e.bosses)||e.bosses.length<1||e.bosses.length>3,'evidence'); bad(e.levelIndex!==undefined&&(!safe(e.levelIndex,1)||e.levelIndex>5||e.bosses.length!==3),'level evidence'); const slots=new Set(),ids=new Set(); for(const b of e.bosses){ bad(!b||Object.keys(b).some(k=>!['slot','bossReceiptId'].includes(k))||!['mini1','mini2','main'].includes(b.slot)||!nonempty(b.bossReceiptId)||slots.has(b.slot)||ids.has(b.bossReceiptId),'boss evidence'); slots.add(b.slot); ids.add(b.bossReceiptId); } bad(!slots.has('main'),'main boss required'); bad(ids.has(e.levelReceiptId),'receipt alias'); return {...(e.levelIndex===undefined?{}:{levelIndex:e.levelIndex}),levelReceiptId:e.levelReceiptId,bosses:['mini1','mini2','main'].filter(s=>slots.has(s)).map(slot=>({slot,bossReceiptId:e.bosses.find(b=>b.slot===slot).bossReceiptId}))}; }\r\nexport async function bossClearFingerprint({owner,epoch,runId,issuedClearOrdinal,assignmentId,evidence}){ scope(owner,epoch); bad(!nonempty(runId)||!safe(issuedClearOrdinal)||!nonempty(assignmentId),'fingerprint input'); return fingerprint({v:1,owner,epoch,runId,issuedClearOrdinal,assignmentId,evidence:evidenceShape(evidence)}); }\r\nexport async function replayBossClear(db,{owner,epoch,runId,fingerprint:fp,levelIndex}){ scope(owner,epoch); bad(!nonempty(runId)||typeof fp!=='string'||!/^[0-9a-f]{64}$/.test(fp),'replay key'); if(await readCurrentEpoch(db,owner)!==epoch)throw new RunStorageError('run_epoch_mismatch',409); bad(levelIndex!==undefined&&(!safe(levelIndex,1)||levelIndex>5),'level'); const table=levelIndex===undefined?'world_boss_clear_receipts':'world_boss_level_clear_receipts'; const r=await db.prepare(`SELECT fingerprint,result FROM ${table} WHERE owner=? AND epoch=? AND run_id=? AND level=?`).bind(owner,epoch,runId,levelIndex??1).first(); if(!r)return null; if(r.fingerprint!==fp)throw new RunStorageError('boss_clear_conflict',409); return freeze({replayed:true,result:JSON.parse(r.result)}); }\r\nexport function issueBossRunStatements(db,{owner,epoch,runId,startedSeq,now,ledger}){ scope(owner,epoch); bad(!nonempty(runId)||!safe(startedSeq,1)||!safe(now)||!ledger||!safe(ledger.clearCount)||!safe(ledger.revision),'issue input'); const a=bossAssignmentForClearCount(ledger.clearCount); return Object.freeze({issuedClearOrdinal:ledger.clearCount,assignment:a,guards:Object.freeze([]),writes:Object.freeze([stmt(db,'INSERT INTO world_boss_progression(owner,epoch,clear_count,revision,updated_at) VALUES(?,?,0,0,?) ON CONFLICT(owner,epoch) DO NOTHING',owner,epoch,now),assertion(db,'EXISTS(SELECT 1 FROM world_boss_progression WHERE owner=? AND epoch=? AND clear_count=? AND revision=?)',[owner,epoch,ledger.clearCount,ledger.revision]),assertion(db,\"EXISTS(SELECT 1 FROM world_runs WHERE owner=? AND epoch=? AND run_id=? AND status='active' AND revision=0 AND started_seq=?)\",[owner,epoch,runId,startedSeq]),stmt(db,'INSERT INTO world_boss_run_pins(owner,epoch,run_id,started_seq,issued_clear_ordinal,ledger_revision,schedule_version,policy_id,groups_hash,rows_hash,group_id,assignment_id,assignment,issued_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)',owner,epoch,runId,startedSeq,ledger.clearCount,ledger.revision,SCHEDULE_VERSION_V2,BOSS_POLICY_ID,a.tableHashes.groups,a.tableHashes.eligibilityRows,a.groupId,a.assignmentId,canonicalJson(a),now)])}); }\r\n/** The trusted adapter must compute fp with bossClearFingerprint from this same normalized evidence; this builder cannot prove digest/evidence equality. */\r\nexport function bossClearStatements(db,{owner,epoch,runId,runRevision,now,context,evidence,fingerprint:fp}){ scope(owner,epoch); bad(!nonempty(runId)||!safe(runRevision)||!safe(now)||!context?.ledger,'clear input'); bad(context.owner!==owner||context.epoch!==epoch,'clear context scope'); const e=evidenceShape(evidence); bad(typeof fp!=='string'||!/^[0-9a-f]{64}$/.test(fp),'fingerprint'); const {ledger,pin}=context; if(!pin)throw new RunStorageError('boss_run_not_v2',409); bad(e.levelIndex!==pin.levelIndex,'level evidence pin mismatch'); const root=pin.rootPin??pin; const expected=bossAssignmentForClearCount(pin.issuedClearOrdinal); if(canonicalJson(pin.assignment)!==canonicalJson(expected)||pin.assignmentId!==expected.assignmentId||pin.groupId!==expected.groupId||pin.tableHashes.groups!==expected.tableHashes.groups||pin.tableHashes.eligibilityRows!==expected.tableHashes.eligibilityRows||pin.startedSeq<1)throw new RunStorageError('run_storage_invalid',400,'pin integrity'); if(pin.issuedClearOrdinal!==ledger.clearCount||pin.ledgerRevision!==ledger.revision)throw new RunStorageError('boss_clear_stale_ordinal',409); if(!safe(ledger.clearCount+1)||!safe(ledger.revision+1))throw new RunStorageError('boss_progression_overflow',409); const owned=new Set(context.ownedCoachIds??[]), promoted=[],suppressed=[],unlocks=[]; for(const b of e.bosses){ const canonicalCoachId=expected.slots[b.slot].canonicalCoachId; const x={canonicalCoachId,slot:b.slot,bossReceiptId:b.bossReceiptId}; if(owned.has(canonicalCoachId))suppressed.push(canonicalCoachId); else {promoted.push(canonicalCoachId);unlocks.push({coachId:canonicalCoachId,encounterId:(pin.levelIndex?`level-${pin.levelIndex}-`:'')+(b.slot==='main'?'intro-mainboss':b.slot==='mini1'?'intro-miniboss':'intro-miniboss-2'),bossReceiptId:b.bossReceiptId,levelReceiptId:e.levelReceiptId,revealOrder:unlocks.length});} } const result={runId,...(pin.levelIndex?{levelIndex:pin.levelIndex}:{}),clearOrdinal:pin.issuedClearOrdinal,clearCount:pin.issuedClearOrdinal+1,ledgerRevision:ledger.revision+1,groupId:expected.groupId,cycle:expected.cycle,assignmentId:expected.assignmentId,promotedCoachIds:promoted,suppressedCoachIds:suppressed}; const guards=[assertion(db,\"EXISTS(SELECT 1 FROM world_runs WHERE owner=? AND epoch=? AND run_id=? AND status='active' AND revision=?)\",[owner,epoch,runId,runRevision]),assertion(db,'EXISTS(SELECT 1 FROM world_boss_run_pins WHERE owner=? AND epoch=? AND run_id=? AND issued_clear_ordinal=? AND ledger_revision=? AND assignment_id=? AND groups_hash=? AND rows_hash=? AND group_id=? AND assignment=? AND started_seq=? AND schedule_version=2 AND policy_id=?)',[owner,epoch,runId,root.issuedClearOrdinal,root.ledgerRevision,root.assignmentId,root.tableHashes.groups,root.tableHashes.eligibilityRows,root.groupId,canonicalJson(root.assignment),root.startedSeq,BOSS_POLICY_ID]),assertion(db,'EXISTS(SELECT 1 FROM world_boss_progression WHERE owner=? AND epoch=? AND clear_count=? AND revision=? AND clear_count<9007199254740991)',[owner,epoch,pin.issuedClearOrdinal,pin.ledgerRevision]),...(pin.levelIndex?[assertion(db,'EXISTS(SELECT 1 FROM world_boss_run_manifests WHERE owner=? AND epoch=? AND run_id=? AND manifest=?)',[owner,epoch,runId,canonicalJson(pin.manifest)])]:[]),...suppressed.map(id=>assertion(db,'EXISTS(SELECT 1 FROM world_coach_unlocks WHERE owner=? AND epoch=? AND coach_id=?)',[owner,epoch,id]))]; const writes=[assertion(db,\"EXISTS(SELECT 1 FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND receipt_id=? AND type='level_cleared')\",[owner,epoch,runId,e.levelReceiptId]),...e.bosses.map(b=>assertion(db,\"EXISTS(SELECT 1 FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND receipt_id=? AND type='boss_defeated')\",[owner,epoch,runId,b.bossReceiptId])),stmt(db,`INSERT INTO ${pin.levelIndex?'world_boss_level_clear_receipts':'world_boss_clear_receipts'}(owner,epoch,run_id,level,clear_ordinal,level_receipt_id,fingerprint,result,run_revision,ledger_revision,accepted_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)`,owner,epoch,runId,pin.levelIndex??1,pin.issuedClearOrdinal,e.levelReceiptId,fp,canonicalJson(result),runRevision,ledger.revision,now),stmt(db,'UPDATE world_boss_progression SET clear_count=clear_count+1,revision=revision+1,updated_at=? WHERE owner=? AND epoch=? AND clear_count=? AND revision=?',now,owner,epoch,pin.issuedClearOrdinal,pin.ledgerRevision)]; return Object.freeze({result:freeze(result),unlocks:freeze(unlocks),guards:Object.freeze(guards),writes:Object.freeze(writes)}); }\r\nexport function bossProgressionDeletionStatements(db,owner){ bad(!nonempty(owner),'owner'); return BOSS_PROGRESSION_TABLES.map(t=>stmt(db,`DELETE FROM ${t} WHERE owner=?`,owner)); }\n\nexport async function readRescuedCoachIds(db,owner,epoch){\n scope(owner,epoch);\n return (await stmt(db,'SELECT canonical_coach_id FROM world_coach_rescues WHERE owner=? AND epoch=? ORDER BY canonical_coach_id',owner,epoch).all()).results.map(row=>row.canonical_coach_id);\n}\nexport async function readRescueManifest(db,owner,epoch,runId){\n scope(owner,epoch);\n const row=await stmt(db,'SELECT manifest,manifest_hash FROM world_run_rescue_manifests WHERE owner=? AND epoch=? AND run_id=?',owner,epoch,runId).first();\n if(!row)throw new RunStorageError('world_run_manifest_missing',409);\n const manifest=validateRescueRunManifest(JSON.parse(row.manifest));\n if(await fingerprint(manifest)!==row.manifest_hash)throw new RunStorageError('world_run_manifest_invalid',409);\n return {manifest,manifestHash:row.manifest_hash};\n}\n\r\nexport async function readBossRunPin(db,owner,epoch,runId,levelIndex){\r\n const root=await readRootBossRunPin(db,owner,epoch,runId);\r\n if(levelIndex===undefined||!root)return root;\r\n bad(!safe(levelIndex,1)||levelIndex>5,'level pin');\r\n const row=await stmt(db,'SELECT manifest FROM world_boss_run_manifests WHERE owner=? AND epoch=? AND run_id=?',owner,epoch,runId).first();\r\n if(!row)throw new RunStorageError('world_run_manifest_missing',409);\r\n let manifest;try{manifest=validateRunManifest(JSON.parse(row.manifest));}catch{throw new RunStorageError('world_run_manifest_invalid',409);}\r\n bad(canonicalJson(manifest.levels[0].assignment)!==canonicalJson(root.assignment),'manifest root pin');\r\n const assignment=manifest.levels[levelIndex-1].assignment;\r\n return freeze({...root,rootPin:root,manifest,levelIndex,issuedClearOrdinal:root.issuedClearOrdinal+levelIndex-1,ledgerRevision:root.ledgerRevision+levelIndex-1,assignment,assignmentId:assignment.assignmentId,groupId:assignment.groupId,tableHashes:assignment.tableHashes});\r\n}\r\n","server/world-boss-run-owner.mjs":"import {validateRunManifest} from '../modules/roguelite/run-schedule-v3.mjs';\nimport {validateRescueRunManifest} from '../modules/roguelite/run-schedule-v4.mjs';\nimport {fingerprint as hashManifest} from './world-run-storage.mjs';\nimport {canonicalJson} from './world-run-storage.mjs';\r\n/** Production composition at the existing world-run transaction owner boundary.\r\n * Trusted callbacks are server-side adapters. They do not accept client SQL or fingerprints.\r\n */\r\nimport {\r\n  RunStorageError, startRunAtomic, appendRunEventAtomic, readCurrentEpoch, readRun,\r\n  readEventReplay,\r\n} from './world-run-storage.mjs';\r\nimport {\r\n  readBossProgression, readBossRunPin, readBossClearContext, bossClearFingerprint,\r\n  issueBossRunStatements, bossClearStatements, replayBossClear,\r\n} from './world-boss-progression.mjs';\r\n\r\nconst invalid = (condition, field) => { if (condition) throw new RunStorageError('run_storage_invalid', 400, field); };\r\nconst safe = n => Number.isSafeInteger(n) && n >= 0;\r\nconst text = s => typeof s === 'string' && s.length > 0;\r\nconst parse = s => JSON.parse(s);\r\nconst withSnapshot = (value, snapshot) => ({ ...value, bossProgression: snapshot });\r\n\r\nfunction validateStart(input) {\r\n  invalid(!input || !text(input.owner) || !safe(input.epoch) || input.epoch < 1 || !text(input.requestId) || !text(input.hash) || !safe(input.now), 'start request');\r\n  invalid(typeof input.create !== 'function' || typeof input.resumeResponse !== 'function', 'start callbacks');\r\n}\r\n\r\n/** Starts/resumes through C's request-id and transaction owner; P3 pin and response share its batch. */\r\nexport async function startBossRunAtomic(db, input) {\r\n  validateStart(input);\r\n  const { create, resumeResponse, ...request } = input;\r\n  return startRunAtomic(db, {\r\n    ...request,\r\n    create: async seq => {\r\n      const ledger = await readBossProgression(db, request.owner, request.epoch);\r\n      const created = await create({ ...seq, issuedClearOrdinal: ledger.clearCount });\r\n      invalid(!created || !text(created.runId) || !created.response || typeof created.response !== 'object', 'new run result');\r\n      const issue = issueBossRunStatements(db, { owner: request.owner, epoch: request.epoch, runId: created.runId,\r\n        startedSeq: seq.startedSeq, now: request.now, ledger });\r\n      if(created.state?.schedule?.scheduleVersion===3){\n        const manifest=validateRunManifest(created.state.schedule.manifest);\r\n        invalid(canonicalJson(manifest.levels[0].assignment)!==canonicalJson(issue.assignment),'manifest root');\r\n        created.writes=[...(created.writes??[]),db.prepare('INSERT INTO world_boss_run_manifests(owner,epoch,run_id,manifest) VALUES(?,?,?,?)').bind(request.owner,request.epoch,created.runId,canonicalJson(manifest))];\r\n      }\n      if(created.state?.schedule?.scheduleVersion===4){\n        const manifest=validateRescueRunManifest(created.state.schedule.manifest);\n        created.writes=[...(created.writes??[]),db.prepare('INSERT INTO world_run_rescue_manifests(owner,epoch,run_id,manifest,manifest_hash) VALUES(?,?,?,?,?)').bind(request.owner,request.epoch,created.runId,canonicalJson(manifest),await hashManifest(manifest))];\n      }\n      const snapshot = Object.freeze({ issuedClearOrdinal: issue.issuedClearOrdinal, assignment: issue.assignment });\r\n      return { ...created, state: withSnapshot(created.state, snapshot), response: withSnapshot(created.response, snapshot),\r\n        guards: issue.guards, writes: [...issue.writes,...(created.writes??[])] };\r\n    },\r\n    resumeResponse: async active => {\r\n      const response = await resumeResponse(active);\r\n      const pin = await readBossRunPin(db, request.owner, request.epoch, active.run_id);\r\n      return pin ? withSnapshot(response, { issuedClearOrdinal: pin.issuedClearOrdinal, assignment: pin.assignment }) : response;\r\n    },\r\n  });\r\n}\r\n\r\nfunction validateEvent(input) {\r\n  invalid(!input || !text(input.owner) || !safe(input.epoch) || input.epoch < 1 || !text(input.runId) ||\r\n    !Number.isSafeInteger(input.clientSeq) || input.clientSeq < 1 || !text(input.requestId) || !text(input.type) ||\r\n    !text(input.hash) || !safe(input.now) || typeof input.apply !== 'function', 'run event');\r\n}\r\n\r\n/**\r\n * `verifyAcceptedClear` is a trusted host adapter. It returns normalized accepted evidence or null.\r\n * It must derive the fingerprint from that same evidence through this composition; callers cannot\r\n * submit a fingerprint. Gameplay verification and HTTP request parsing belong to a future host adapter.\r\n */\r\nexport async function appendBossRunEventAtomic(db, input) {\r\n  validateEvent(input); // All key validation precedes database reads or adapter calls.\r\n  const { verifyAcceptedClear, apply, levelIndex, ...request } = input;\r\n  invalid(verifyAcceptedClear !== undefined && typeof verifyAcceptedClear !== 'function', 'trusted clear adapter');\r\n\r\n  if (await readCurrentEpoch(db, request.owner) !== request.epoch) throw new RunStorageError('run_epoch_mismatch', 409);\r\n  // C retains event replay/hash-conflict ownership and answers exact sequence replays before verification.\r\n  const exact = await readEventReplay(db, request);\r\n  if (exact) return appendRunEventAtomic(db, { ...request, apply });\r\n  const run = await readRun(db, request.owner, request.epoch, request.runId);\r\n  if (!run) throw new RunStorageError('run_not_found', 409);\r\n  const pin = await readBossRunPin(db, request.owner, request.epoch, request.runId,levelIndex);\r\n  let accepted = null, fingerprint = null;\r\n  if (verifyAcceptedClear && pin) {\r\n    accepted = await verifyAcceptedClear(Object.freeze({ run: structuredClone(run), pin, event: Object.freeze({\r\n      clientSeq: request.clientSeq, requestId: request.requestId, type: request.type, hash: request.hash,\r\n    }) }));\r\n    if (accepted !== null && accepted !== undefined) {\r\n      fingerprint = await bossClearFingerprint({ owner: request.owner, epoch: request.epoch, runId: request.runId,\r\n        issuedClearOrdinal: pin.issuedClearOrdinal, assignmentId: pin.assignmentId, evidence: accepted });\r\n      const prior = await replayBossClear(db, { owner: request.owner, epoch: request.epoch, runId: request.runId, fingerprint,levelIndex });\r\n      if (prior) {\r\n        // Preserve C's journal behavior for active runs; a terminal run still returns the immutable P3 response.\r\n        if (run.status === 'active') return appendRunEventAtomic(db, { ...request, apply: async () => ({\r\n          outcome: 'noop', state: parse(run.state), response: prior.result,\r\n        }) });\r\n        return { replayed: true, outcome: 'noop', response: prior.result };\r\n      }\r\n    }\r\n  }\r\n\r\n  try {\r\n    return await appendRunEventAtomic(db, { ...request, apply: async ctx => {\r\n      const outcome = await apply(ctx);\r\n      if (accepted === null || accepted === undefined) return outcome;\r\n      invalid(!pin || outcome?.outcome !== 'applied', 'accepted clear requires applied C outcome');\r\n      const receipts = outcome.receipts ?? [];\r\n      invalid(!receipts.some(r => r?.type === 'level_cleared' && r.receiptId === accepted.levelReceiptId), 'accepted level receipt missing');\r\n      for (const boss of accepted.bosses ?? []) {\r\n        if (receipts.some(r => r?.type === 'boss_defeated' && r.receiptId === boss.bossReceiptId)) continue;\r\n        // Earlier minibosses were accepted on their own visit events. Bind those\r\n        // already committed receipts here; the same-batch SQL assertions below\r\n        // recheck their presence with the level-clear write.\r\n        const prior = await db.prepare(\"SELECT data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND receipt_id=? AND type='boss_defeated'\")\r\n          .bind(request.owner, request.epoch, request.runId, boss.bossReceiptId).first();\r\n        let data; try { data = prior ? JSON.parse(prior.data) : null; } catch { data = null; }\r\n        invalid(!prior || data?.slot !== boss.slot || data?.assignmentId !== pin.assignmentId ||\r\n          data?.issuedClearOrdinal !== pin.issuedClearOrdinal || data?.canonicalCoachId !== pin.assignment.slots[boss.slot]?.canonicalCoachId,\r\n        'accepted boss receipt missing');\r\n      }\r\n      const context = await readBossClearContext(db, { owner: request.owner, epoch: request.epoch, runId: request.runId,levelIndex });\r\n      const fp = await bossClearFingerprint({ owner: request.owner, epoch: request.epoch, runId: request.runId,\r\n        issuedClearOrdinal: context.pin?.issuedClearOrdinal ?? 0, assignmentId: context.pin?.assignmentId ?? '', evidence: accepted });\r\n      invalid(fp !== fingerprint, 'accepted clear facts changed during apply');\r\n      const clear = bossClearStatements(db, { owner: request.owner, epoch: request.epoch, runId: request.runId,\r\n        runRevision: ctx.run.revision, now: request.now, context, evidence: accepted, fingerprint: fp });\r\n      const response = outcome.response?.envelope && outcome.response && typeof outcome.response === 'object' && !Array.isArray(outcome.response)\r\n        ? { ...outcome.response, ...clear.result, bossProgression: clear.result } : clear.result;\r\n      if (outcome.response?.detail && typeof outcome.response.detail === 'object')\r\n        response.detail = { ...outcome.response.detail, bossProgression: clear.result };\r\n      return { ...outcome, clear: true, unlocks: [...(outcome.unlocks ?? []), ...clear.unlocks], response,\r\n        guards: [...(outcome.guards ?? []), ...clear.guards], writes: [...(outcome.writes ?? []), ...clear.writes] };\r\n    } });\r\n  } catch (error) {\n    if(error?.code?.startsWith('game_generation_'))throw error;\n    // Lost acknowledgements and races resolve through the P3 epoch-first replay lookup.\r\n    if (fingerprint) {\r\n      const prior = await replayBossClear(db, { owner: request.owner, epoch: request.epoch, runId: request.runId, fingerprint,levelIndex });\r\n      if (prior) return { replayed: true, outcome: 'applied', response: prior.result };\r\n    }\r\n    throw error;\r\n  }\r\n}\r\n","server/world-drops.mjs":"import {DROP_KINDS,DROP_RULES,gymCircuitReward,dropDistance} from '../modules/coach-world/location-drop-rules.mjs';\r\nimport {readAccountPerformance} from './performance-progress.mjs';\r\nconst stmt=(db,sql,...args)=>db.prepare(sql).bind(...args),first=(db,sql,...args)=>stmt(db,sql,...args).first(),all=async(db,sql,...args)=>(await stmt(db,sql,...args).all()).results;\r\nconst fail=(code,status=409)=>{throw Object.assign(Error(code),{code,status});},uuid=()=>crypto.randomUUID();\r\nconst assert=(db,condition,args=[])=>stmt(db,`INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`,uuid(),...args);\r\nasync function hash(value){return [...new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(JSON.stringify(value))))].map(v=>v.toString(16).padStart(2,'0')).join('');}\r\nconst ownRows=(db,table,row)=>all(db,`SELECT * FROM ${table} WHERE owner=? AND epoch=?`,row.owner,row.epoch);\r\nfunction validFix(fix,now,issuedAt=0){if(![fix.latitude,fix.longitude,fix.accuracy,fix.sampledAt].every(Number.isFinite)||Math.abs(fix.latitude)>90||Math.abs(fix.longitude)>180||fix.accuracy<0||fix.accuracy>DROP_RULES.maxAccuracy||now-fix.sampledAt>15000||fix.sampledAt>now+1000||fix.sampledAt<issuedAt)fail('drop_fresh_accurate_gps_required',400);}\r\nfunction near(fix,location){if(dropDistance(fix,location)+fix.accuracy>DROP_RULES.radiusMeters)fail('drop_move_closer');}\r\nasync function snapshot(db,row,now){\r\n const [locations,claims,performance]=await Promise.all([ownRows(db,'world_drop_locations',row),all(db,'SELECT id,kind,claimed_at,xp,reward FROM world_drop_claims WHERE owner=? AND epoch=? ORDER BY claimed_at,id',row.owner,row.epoch),readAccountPerformance(db,row.owner)]);\r\n const last=Object.fromEntries(DROP_KINDS.map(kind=>[kind,Math.max(0,...claims.filter(c=>c.kind===kind).map(c=>c.claimed_at))]));\r\n return {targetAccountId:row.owner,dataEpoch:row.epoch,serverTime:now,locations:locations.map(l=>({kind:l.kind,latitude:l.latitude,longitude:l.longitude,version:l.version,availableAt:last[l.kind]?last[l.kind]+DROP_RULES.cooldownMs:now})),claims:claims.map(c=>({id:c.id,kind:c.kind,claimedAt:c.claimed_at,...JSON.parse(c.reward)})),gymReward:gymCircuitReward(performance)};\r\n}\r\nexport async function worldDropsApi(request,db,row,method,input,now){\r\n if(method==='GET')return snapshot(db,row,now);if(method!=='POST')fail('drop_method_not_allowed',405);\r\n if(typeof input.id!=='string'||!/^[A-Za-z0-9:_-]{1,100}$/.test(input.id)||request.headers.get('Idempotency-Key')!==input.id)fail('drop_idempotency_key_required',400);\r\n if(!DROP_KINDS.includes(input.kind)||!['set-location','remove-location','start-claim','sample-claim'].includes(input.type))fail('drop_command_invalid',400);\r\n const fingerprint=await hash({path:'world/drops',input}),replay=()=>first(db,'SELECT * FROM world_requests WHERE owner=? AND epoch=? AND id=?',row.owner,row.epoch,input.id),receipt=await replay();if(receipt){if(receipt.hash!==fingerprint)fail('drop_idempotency_payload_conflict');return JSON.parse(receipt.response);}\r\n const guards=[assert(db,'EXISTS(SELECT 1 FROM account_data_epochs WHERE owner_id=? AND epoch=?)',[row.owner,row.epoch])],writes=[];let result={};\r\n const location=await first(db,'SELECT * FROM world_drop_locations WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind);\r\n const locationGuard=()=>location?assert(db,'EXISTS(SELECT 1 FROM world_drop_locations WHERE owner=? AND epoch=? AND kind=? AND version=?)',[row.owner,row.epoch,input.kind,location.version]):assert(db,'NOT EXISTS(SELECT 1 FROM world_drop_locations WHERE owner=? AND epoch=? AND kind=?)',[row.owner,row.epoch,input.kind]);\r\n guards.push(locationGuard());\r\n if(input.type==='set-location'){\r\n  if(input.confirmPrivateLocation!==true||input.confirmAccessible!==true)fail('drop_location_consent_required',400);validFix(input,now);\r\n  if(location&&now-location.updated_at<3600000)fail('drop_location_change_cooldown');\r\n  writes.push(stmt(db,'INSERT INTO world_drop_locations(owner,epoch,kind,latitude,longitude,version,updated_at) VALUES(?,?,?,?,?,?,?) ON CONFLICT(owner,epoch,kind) DO UPDATE SET latitude=excluded.latitude,longitude=excluded.longitude,version=excluded.version,updated_at=excluded.updated_at',row.owner,row.epoch,input.kind,input.latitude,input.longitude,uuid(),now),stmt(db,'DELETE FROM world_drop_challenges WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind));result={saved:true,kind:input.kind};\r\n }else if(input.type==='remove-location'){\r\n  writes.push(stmt(db,'DELETE FROM world_drop_locations WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind),stmt(db,'DELETE FROM world_drop_challenges WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind));result={removed:true,kind:input.kind};\r\n }else{\r\n  if(!location)fail('drop_location_required');\r\n  guards.push(assert(db,'NOT EXISTS(SELECT 1 FROM world_drop_claims WHERE owner=? AND epoch=? AND kind=? AND claimed_at>?)',[row.owner,row.epoch,input.kind,now-DROP_RULES.cooldownMs]));\r\n  const priorClaim=await first(db,'SELECT claimed_at FROM world_drop_claims WHERE owner=? AND epoch=? AND kind=? ORDER BY claimed_at DESC LIMIT 1',row.owner,row.epoch,input.kind);if(priorClaim&&now-priorClaim.claimed_at<DROP_RULES.cooldownMs)fail('drop_cache_recharging');\r\n  if(input.type==='start-claim'){\r\n   validFix(input,now);near(input,location);const id=uuid(),nonce=uuid();writes.push(stmt(db,'INSERT INTO world_drop_challenges(owner,epoch,kind,id,nonce,location_version,issued_at,expires_at,first_at,first_sample,last_sample,seq) VALUES(?,?,?,?,?,?,?,?,NULL,NULL,NULL,0) ON CONFLICT(owner,epoch,kind) DO UPDATE SET id=excluded.id,nonce=excluded.nonce,location_version=excluded.location_version,issued_at=excluded.issued_at,expires_at=excluded.expires_at,first_at=NULL,first_sample=NULL,last_sample=NULL,seq=0',row.owner,row.epoch,input.kind,id,nonce,location.version,now,now+DROP_RULES.challengeMs));result={challenge:{id,nonce,expiresAt:now+DROP_RULES.challengeMs},kind:input.kind};\r\n  }else{\r\n   const challenge=await first(db,'SELECT * FROM world_drop_challenges WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind);if(!challenge||challenge.id!==input.challengeId||challenge.nonce!==input.nonce||challenge.location_version!==location.version||challenge.expires_at<now)fail('drop_challenge_expired');\r\n   validFix(input,now,challenge.issued_at);near(input,location);if(!Number.isSafeInteger(input.seq)||input.seq!==challenge.seq+1||input.sampledAt<=(challenge.last_sample??-1))fail('drop_sequence_rejected');\r\n   guards.push(assert(db,'EXISTS(SELECT 1 FROM world_drop_challenges WHERE owner=? AND epoch=? AND kind=? AND id=? AND nonce=? AND seq=? AND location_version=? AND expires_at>=?)',[row.owner,row.epoch,input.kind,challenge.id,challenge.nonce,challenge.seq,location.version,now]));\r\n   if(challenge.first_at!=null&&now-challenge.first_at>=DROP_RULES.dwellMs&&input.sampledAt-challenge.first_sample>=DROP_RULES.dwellMs){\r\n    const quote=input.kind==='gym'?gymCircuitReward(await readAccountPerformance(db,row.owner)):null;if(quote&&!quote.eligible)fail('drop_complete_a_phone_workout_first');\r\n    const id=uuid(),tier=input.kind==='gym'?'secret':'uncommon',packs=Array.from({length:quote?.secretPacks??1},(_,i)=>`reward-pack:${tier}:world-${input.kind}:${id}:${i+1}`),reward={xp:quote?.xp??0,packs,...(quote?{circuit:quote}:{})};\r\n    writes.push(stmt(db,'INSERT INTO world_drop_claims(owner,epoch,id,kind,claimed_at,xp,reward) VALUES(?,?,?,?,?,?,?)',row.owner,row.epoch,id,input.kind,now,reward.xp,JSON.stringify(reward)),stmt(db,'DELETE FROM world_drop_challenges WHERE owner=? AND epoch=? AND kind=?',row.owner,row.epoch,input.kind));result={claimed:true,kind:input.kind,claim:{id,claimedAt:now,...reward}};\r\n   }else{\r\n    writes.push(stmt(db,'UPDATE world_drop_challenges SET first_at=COALESCE(first_at,?),first_sample=COALESCE(first_sample,?),last_sample=?,seq=? WHERE owner=? AND epoch=? AND kind=?',now,input.sampledAt,input.sampledAt,input.seq,row.owner,row.epoch,input.kind));result={claimed:false,kind:input.kind,dwellMs:challenge.first_at==null?0:now-challenge.first_at};\r\n   }\r\n  }\r\n }\r\n const response={targetAccountId:row.owner,dataEpoch:row.epoch,serverTime:now,result};\r\n try{await db.batch([...guards,...writes,stmt(db,'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)',row.owner,row.epoch,input.id,fingerprint,JSON.stringify(response)),stmt(db,'DELETE FROM world_assertions')]);}\r\n catch(error){const saved=await replay();if(saved){if(saved.hash!==fingerprint)fail('drop_idempotency_payload_conflict');return JSON.parse(saved.response);}const epoch=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',row.owner);if(epoch?.epoch!==row.epoch)fail('world_epoch_mismatch');if(String(error.message).includes('world_assertions')||String(error.message).includes('UNIQUE'))fail('drop_state_changed');throw error;}\r\n return response;\r\n}\r\nexport function worldDropDeletionStatements(db,owner){return ['world_drop_locations','world_drop_challenges','world_drop_claims'].map(table=>stmt(db,`DELETE FROM ${table} WHERE owner=?`,owner));}\r\nexport async function exportWorldDrops(db,owner){return {locations:(await all(db,'SELECT kind,latitude,longitude,updated_at FROM world_drop_locations WHERE owner=?',owner)),claims:(await all(db,'SELECT kind,claimed_at,xp,reward FROM world_drop_claims WHERE owner=?',owner))};}\r\n","server/world-raid-api.mjs":"import { canonicalJson,fingerprint,epochGuard,baseGuard,sequenceStatements,consumeTicketStatements,clearAssertions } from './world-run-storage.mjs';\r\nimport { npcTargets } from '../modules/coach-world-rts/raid-targets.mjs';\r\nimport { buildRaid,validDrop,rosterFromUnits,attackerUpgrades,raidShips } from '../modules/coach-world-rts/raid-arena.mjs';\r\nimport { unitInstances } from '../modules/coach-world/production.mjs';\r\nimport { busyHarvestWorkers } from '../modules/coach-world/harvest-adapter.mjs';\r\nimport { simulate,MAX_UNITS,MAX_TICKS,ABILITIES,W,H,ABILITY_WARMUP,ABILITY_COOLDOWN } from '../modules/coach-world-rts/battle-sim.mjs';\r\n\r\nexport const RAID_POLICY=Object.freeze({enabledValue:'1',playbackSpeed:4,tickMs:100,abilityLimit:2});\r\nexport class WorldRaidError extends Error{constructor(code,status=409){super(code);this.code=code;this.status=status;}}\r\nconst fail=(code,status=409)=>{throw new WorldRaidError(code,status);};\r\nconst stmt=(db,sql,...args)=>db.prepare(sql).bind(...args);\r\nconst first=(db,sql,...args)=>stmt(db,sql,...args).first();\r\nconst all=(db,sql,...args)=>stmt(db,sql,...args).all().then(x=>x.results);\r\nconst assert=(db,condition,args=[])=>stmt(db,`INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`,crypto.randomUUID(),...args);\r\nconst parse=(s)=>{try{return JSON.parse(s);}catch{fail('world_raid_storage_invalid',500);}};\r\nconst obj=v=>v&&typeof v==='object'&&!Array.isArray(v);\r\nconst idOk=v=>typeof v==='string'&&/^[A-Za-z0-9:_-]{1,80}$/.test(v);\r\nconst answer=(row)=>{const simInput=parse(row.sim_input);return {raid:{id:row.raid_id,status:row.status,revision:row.revision,startedSeq:row.started_seq,startedAt:row.started_at,targetId:row.target_id,tier:row.tier,target:simInput.npcTarget??null,playerFaction:row.player_faction,selectedUnitIDs:parse(row.selected_unit_ids),dropTile:parse(row.drop_tile),simInput,packId:row.pack_id,result:row.result?parse(row.result):null}};};\r\nconst responseSaved=async(db,owner,epoch,key,hash)=>{const r=await first(db,'SELECT hash,response FROM world_requests WHERE owner=? AND epoch=? AND id=?',owner,epoch,key);if(!r)return null;if(r.hash!==hash)fail('world_idempotency_payload_conflict');return parse(r.response);};\r\nfunction closed(input,allowed){if(!obj(input)||Object.keys(input).some(k=>!allowed.includes(k)))fail('world_raid_invalid_request',400);}\r\nfunction ticketNotReady(){fail('world_raid_ticket_required',409);}\r\nfunction randomSeed(){const b=crypto.getRandomValues(new Uint32Array(1));return (b[0]%2147483646)+1;}\r\nfunction accountTargets(row,now){const base=row?parse(row.base):null;return npcTargets({mappedAt:base?.raidMappedAt??(base?.home?row.created_at:null),now});}\r\nfunction accountUnits(base,selected){\r\n if(!Array.isArray(selected)||selected.length<1||selected.length>MAX_UNITS||selected.some(x=>typeof x!=='string')||new Set(selected).size!==selected.length)fail('world_raid_roster_invalid',400);\r\n const owned=new Map(unitInstances(base,{cap:200}).filter(x=>!x.away).map(x=>[x.instanceId,x])),busy=busyHarvestWorkers(base),actual=[];\r\n const units=selected.map(id=>{\r\n  const worker=/^worker#([1-9][0-9]*)$/.exec(id);\r\n  if(worker){const n=Number(worker[1]),crewId=`crew-${n}`;if(n>(base.workers??0)||busy.has(crewId))fail('world_raid_unit_not_owned',409);actual.push(crewId);return {instanceId:crewId,role:'worker',faction:base.faction};}\r\n  const u=owned.get(id);if(!u)fail('world_raid_unit_not_owned',409);actual.push(u.instanceId);return u;\r\n });\r\n return {units,selectedUnitIDs:actual,roster:rosterFromUnits(units,'')};\r\n}\r\nfunction abilitiesFor(input,faction){\r\n if(input===undefined)return [];\r\n if(!Array.isArray(input)||input.length>RAID_POLICY.abilityLimit)fail('world_raid_ability_invalid',400);\r\n let prev=-1;\r\n return input.map((a,i)=>{if(!obj(a)||Object.keys(a).some(k=>!['tick','x','y'].includes(k))||!Number.isSafeInteger(a.tick)||a.tick<ABILITY_WARMUP||a.tick>=MAX_TICKS||a.tick<prev||!Number.isSafeInteger(a.x)||a.x<0||a.x>W||!Number.isSafeInteger(a.y)||a.y<0||a.y>H)fail('world_raid_ability_invalid',400);if(i&&a.tick-prev<ABILITY_COOLDOWN)fail('world_raid_ability_invalid',400);prev=a.tick;if(!ABILITIES[faction])fail('world_raid_ability_invalid',400);return{faction,tick:a.tick,x:a.x,y:a.y,side:0};});\r\n}\r\nexport async function worldRaidApi(request,db,owner,path,method,input,now,{dataEpoch,enabled=false}={}){\r\n if(!enabled)fail('world_route_not_found',404);\r\n if(!Number.isSafeInteger(now)||now<0)fail('world_invalid_time',400);\r\n if(path==='/api/world/raids/current'||path==='/api/world/raids'){\r\n  if(method!=='GET')fail('world_raid_method_not_allowed',405);\r\n  const active=await first(db,\"SELECT * FROM world_raids WHERE owner=? AND epoch=? AND status='active' ORDER BY started_seq DESC LIMIT 1\",owner,dataEpoch),ticket=await first(db,\"SELECT ticket_id FROM world_raid_tickets WHERE owner=? AND epoch=? AND status='available' LIMIT 1\",owner,dataEpoch);\r\n  const rows=path==='/api/world/raids'?await all(db,'SELECT * FROM world_raids WHERE owner=? AND epoch=? ORDER BY started_seq DESC LIMIT 50',owner,dataEpoch):[];\r\n  const baseRow=await first(db,'SELECT base,created_at FROM world_bases WHERE owner=? AND epoch=?',owner,dataEpoch);\r\n  return {targetAccountId:owner,dataEpoch,serverTime:now,targets:accountTargets(baseRow,now),ticketAvailable:!!ticket,active:active?answer(active).raid:null,raids:rows.map(r=>answer(r).raid)};\r\n }\r\n if(path!=='/api/world/raids/start'&&path!=='/api/world/raids/complete')fail('world_route_not_found',404);\r\n if(method!=='POST')fail('world_raid_method_not_allowed',405);\r\n const start=path.endsWith('/start'),abandon=!start&&input?.action==='abandon';closed(input,start?['id','targetId','selectedUnitIDs','dropTile']:abandon?['id','action']:['id','abilityInputs']);\r\n if(!idOk(input.id)||!idOk(input.targetId)&&start)fail('world_raid_invalid_request',400);\r\n if(request.headers.get('Idempotency-Key')!==input.id)fail('world_raid_idempotency_key_required',400);\r\n const key=`raid:${start?'start':abandon?'abandon':'complete'}:${owner}:${input.id}`;\r\n const hash=await fingerprint({operation:start?'start':abandon?'abandon':'complete',owner,dataEpoch,input});\r\n const replay=await responseSaved(db,owner,dataEpoch,key,hash);if(replay)return replay;\r\n if(start){\r\n  if(!obj(input.dropTile)||!Number.isSafeInteger(input.dropTile.x)||!Number.isSafeInteger(input.dropTile.y))fail('world_raid_drop_invalid',400);\r\n  const baseRow=await first(db,'SELECT revision,base,created_at FROM world_bases WHERE owner=? AND epoch=?',owner,dataEpoch);if(!baseRow)fail('world_not_provisioned',409);\r\n  const target=accountTargets(baseRow,now).find(t=>t.id===input.targetId);if(!target)fail('world_raid_target_unknown',400);\r\n  const base=parse(baseRow.base);const {units,selectedUnitIDs,roster}=accountUnits(base,input.selectedUnitIDs);\r\n  if(!validDrop(target.base,input.dropTile).ok)fail('world_raid_drop_invalid',400);\r\n  const ticket=await first(db,\"SELECT ticket_id FROM world_raid_tickets WHERE owner=? AND epoch=? AND status='available'\",owner,dataEpoch);if(!ticket)ticketNotReady();\r\n  const sim=buildRaid({drop:input.dropTile,base:target.base,tier:target.tier,attackers:roster,ships:raidShips(base),seed:randomSeed(),upgrades:attackerUpgrades(base)}).input;\r\n  sim.npcTarget=target;sim.rulesVersion=6; if(!sim.systems)sim.systems=[[],[]];\r\n  const seqRow=await first(db,'SELECT next FROM world_sequence WHERE owner=? AND epoch=?',owner,dataEpoch),seq=seqRow?.next??1,raidId=`raid-${(await fingerprint([owner,dataEpoch,input.id])).slice(0,40)}`;\r\n  const row={owner,epoch:dataEpoch,raid_id:raidId,status:'active',revision:0,started_seq:seq,started_at:now,target_id:target.id,tier:target.tier,ticket_id:ticket.ticket_id,base_revision:baseRow.revision,player_faction:base.faction,selected_unit_ids:canonicalJson(selectedUnitIDs),drop_tile:canonicalJson(input.dropTile),sim_input:canonicalJson(sim),rules_version:6,result:null,pack_id:null,completed_at:null};\r\n  const response=answer(row);\r\n  const writes=[...consumeTicketStatements(db,{owner,epoch:dataEpoch,ticketId:ticket.ticket_id,raidId,consumedSeq:seq,now}),\r\n   stmt(db,'INSERT INTO world_raids(owner,epoch,raid_id,status,revision,started_seq,started_at,target_id,tier,ticket_id,base_revision,player_faction,selected_unit_ids,drop_tile,sim_input,rules_version,result,pack_id,completed_at) VALUES(?,?,?,\\'active\\',0,?,?,?,?,?,?,?,?,?,?,?,NULL,NULL,NULL)',owner,dataEpoch,raidId,seq,now,target.id,target.tier,ticket.ticket_id,baseRow.revision,base.faction,row.selected_unit_ids,row.drop_tile,row.sim_input,6),\r\n   stmt(db,'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)',owner,dataEpoch,key,hash,canonicalJson(response))];\r\n  try{await db.batch([epochGuard(db,owner,dataEpoch),baseGuard(db,{owner,epoch:dataEpoch,revision:baseRow.revision}),assert(db,\"NOT EXISTS(SELECT 1 FROM world_raids WHERE owner=? AND epoch=? AND status='active')\",[owner,dataEpoch]),...sequenceStatements(db,{owner,epoch:dataEpoch,expectedNext:seq,count:1}),...writes,clearAssertions(db)]);}\r\n  catch(e){const again=await responseSaved(db,owner,dataEpoch,key,hash);if(again)return again;const current=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',owner);if(current?.epoch!==dataEpoch)fail('world_epoch_mismatch');if(String(e.message).includes('world_assertions'))fail('world_raid_state_conflict');throw e;}\r\n  return response;\r\n }\r\n if(!idOk(input.id))fail('world_raid_invalid_request',400);\r\n const raid=await first(db,'SELECT * FROM world_raids WHERE owner=? AND epoch=? AND raid_id=?',owner,dataEpoch,input.id);if(!raid)fail('world_raid_not_found',404);\r\n if(abandon){\r\n  if(raid.status!=='active')return answer(raid);\r\n  const next={...raid,status:'abandoned',revision:raid.revision+1,completed_at:now},response=answer(next);\r\n  try{await db.batch([epochGuard(db,owner,dataEpoch),assert(db,\"EXISTS(SELECT 1 FROM world_raids WHERE owner=? AND epoch=? AND raid_id=? AND status='active' AND revision=?)\",[owner,dataEpoch,raid.raid_id,raid.revision]),stmt(db,\"UPDATE world_raids SET status='abandoned',revision=revision+1,completed_at=? WHERE owner=? AND epoch=? AND raid_id=? AND status='active' AND revision=?\",now,owner,dataEpoch,raid.raid_id,raid.revision),stmt(db,'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)',owner,dataEpoch,key,hash,canonicalJson(response)),clearAssertions(db)]);}\r\n  catch(e){const current=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',owner);if(current?.epoch!==dataEpoch)fail('world_epoch_mismatch');const again=await responseSaved(db,owner,dataEpoch,key,hash);if(again)return again;if(String(e.message).includes('world_assertions')||String(e.message).includes('UNIQUE'))fail('world_raid_state_conflict');throw e;}\r\n  return response;\r\n }\r\n if(raid.status!=='active')return answer(raid);\r\n const abilities=abilitiesFor(input.abilityInputs,raid.player_faction);\r\n const simInput=parse(raid.sim_input);if(raid.rules_version!==6||simInput.rulesVersion!==6)fail('world_raid_rules_invalid',500);\r\n simInput.abilities=abilities;let sim;try{sim=simulate(simInput);}catch{fail('world_raid_simulation_invalid',400);}\r\n const minimum=Math.ceil(sim.result.ticks*RAID_POLICY.tickMs/RAID_POLICY.playbackSpeed);if(now-raid.started_at<minimum)fail('world_raid_timeline_too_fast',409);\r\n const won=sim.result.winner==='a',status=won?'won':'lost',tier=won?({easy:'uncommon',normal:'rare',hard:'legendary'}[raid.tier]):null;\r\n const packId=won?`reward-pack:${tier}:raid-${raid.raid_id}`:null,result=canonicalJson({winner:sim.result.winner,ticks:sim.result.ticks,a:sim.result.a,b:sim.result.b,rulesVersion:6});\r\n const next={...raid,status,revision:raid.revision+1,result,pack_id:packId,completed_at:now};const response=answer(next),seqRow=await first(db,'SELECT next FROM world_sequence WHERE owner=? AND epoch=?',owner,dataEpoch),seq=seqRow?.next??1;\r\n const writes=[stmt(db,'UPDATE world_raids SET status=?,revision=revision+1,result=?,pack_id=?,completed_at=? WHERE owner=? AND epoch=? AND raid_id=? AND status=\\'active\\' AND revision=?',status,result,packId,now,owner,dataEpoch,raid.raid_id,raid.revision),\r\n  stmt(db,'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)',owner,dataEpoch,key,hash,canonicalJson(response))];\r\n if(won)writes.push(stmt(db,'INSERT INTO world_raid_pack_claims(owner,epoch,raid_id,pack_id,tier,result,granted_at) VALUES(?,?,?,?,?,?,?)',owner,dataEpoch,raid.raid_id,packId,tier,result,now),stmt(db,\"INSERT INTO account_pack_entitlements(user_id,pack_id,status,granted_at) VALUES(?,?,'owned',?)\",owner,packId,now));\r\n try{await db.batch([epochGuard(db,owner,dataEpoch),...sequenceStatements(db,{owner,epoch:dataEpoch,expectedNext:seq,count:1}),assert(db,'EXISTS(SELECT 1 FROM world_raids WHERE owner=? AND epoch=? AND raid_id=? AND status=\\'active\\' AND revision=?)',[owner,dataEpoch,raid.raid_id,raid.revision]),...writes,clearAssertions(db)]);}\r\n catch(e){const current=await first(db,'SELECT epoch FROM account_data_epochs WHERE owner_id=?',owner);if(current?.epoch!==dataEpoch)fail('world_epoch_mismatch');const again=await responseSaved(db,owner,dataEpoch,key,hash);if(again)return again;if(String(e.message).includes('world_assertions')||String(e.message).includes('UNIQUE'))fail('world_raid_state_conflict');throw e;}\r\n return response;\r\n}\r\n","server/world-raid-schema.sql":"CREATE TABLE world_raids(owner TEXT NOT NULL,epoch INTEGER NOT NULL,raid_id TEXT NOT NULL,status TEXT NOT NULL CHECK(status IN('active','won','lost','abandoned')),revision INTEGER NOT NULL CHECK(revision>=0),started_seq INTEGER NOT NULL,started_at INTEGER NOT NULL,target_id TEXT NOT NULL,tier TEXT NOT NULL CHECK(tier IN('easy','normal','hard')),ticket_id TEXT NOT NULL,base_revision INTEGER NOT NULL,player_faction TEXT NOT NULL,selected_unit_ids TEXT NOT NULL,drop_tile TEXT NOT NULL,sim_input TEXT NOT NULL,rules_version INTEGER NOT NULL CHECK(rules_version BETWEEN 2 AND 6),result TEXT,pack_id TEXT,completed_at INTEGER,PRIMARY KEY(owner,epoch,raid_id),UNIQUE(owner,epoch,started_seq));\n--> statement-breakpoint\nCREATE UNIQUE INDEX world_raids_one_active ON world_raids(owner,epoch) WHERE status='active';\n--> statement-breakpoint\nCREATE TABLE world_raid_pack_claims(owner TEXT NOT NULL,epoch INTEGER NOT NULL,raid_id TEXT NOT NULL,pack_id TEXT NOT NULL UNIQUE,tier TEXT NOT NULL CHECK(tier IN('uncommon','rare','legendary')),result TEXT NOT NULL,granted_at INTEGER NOT NULL,PRIMARY KEY(owner,epoch,raid_id));\n","server/world-rts-settle.mjs":"// W4 P1 settlement (reserved file, UNWIRED: nothing imports this until Codex lands S5). Gated by env.WORLD_RTS==='1'.\r\n// read-versioned -> compute (plain JS, outside any batch) -> conditional apply; at most 3 retries on version conflict.\r\n// io is supplied by the Codex adapter (D1): read(id) -> {version, input:{rulesVersion,seed,rosters,pads}, settled?}  (input comes from AUTHENTICATED server rows only, never the request)\r\n//                                          apply({encounterId,expectedVersion,result,log}) -> {ok:true} | {duplicate:true,stored} | {conflict:true}  (one batch, each write WHERE revision=?)\r\nimport {simulate,makeLog} from '../modules/coach-world-rts/battle-sim.mjs';\r\nexport const rtsEnabled=env=>env?.WORLD_RTS==='1';\r\nconst reply=(status,body)=>({status,body});\r\nexport async function settleRtsEncounter(env,encounterId,io,{retries=3}={}){\r\n if(!rtsEnabled(env))return reply(404,{error:'not_found'});\r\n for(let i=0;i<retries;i++){\r\n  const snap=await io.read(encounterId);\r\n  if(!snap)return reply(404,{error:'world_encounter_not_found'});\r\n  if(snap.settled)return reply(200,{...snap.settled,duplicate:true});\r\n  let out;try{out=simulate(snap.input);}catch{return reply(422,{error:'world_battle_input_invalid'});}\r\n  const log=makeLog(snap.input,out),r=await io.apply({encounterId,expectedVersion:snap.version,result:out.result,log});\r\n  if(r.ok)return reply(200,{result:out.result,log});\r\n  if(r.duplicate)return reply(200,{...r.stored,duplicate:true});\r\n }\r\n return reply(409,{error:'world_settle_conflict'});\r\n}\r\n","server/world-run-api.mjs":"import {readAccountPerformance} from './performance-progress.mjs';\r\nimport {createRunManifest} from '../modules/roguelite/run-schedule-v3.mjs';\nimport {createRescueRunManifest} from '../modules/roguelite/run-schedule-v4.mjs';\nimport {projectCoachHearts} from '../modules/roguelite/coach-hearts.mjs';\nimport {validateRunLoadout} from '../modules/roguelite/run-loadout.mjs';\r\nimport { EXERCISES } from '../exercise-library.mjs';\r\nimport { createRunSession } from '../modules/roguelite/run-controller.mjs';\r\nimport { createRunStats } from '../modules/roguelite/run-stats.mjs';\r\nimport { createBoonInventory, grantBoon } from '../modules/roguelite/boons.mjs';\r\nimport { bossAssignmentForClearCount } from '../modules/roguelite/run-schedule-v2.mjs';\r\nimport { getRunStartBenefits, readProgression, startBoonReceipts } from '../modules/coach-world-rts/npc-progression.mjs';\r\nimport { startBossRunAtomic } from './world-boss-run-owner.mjs';\r\nimport { readActiveRun, readBase, readCampaign, readCurrentEpoch } from './world-run-storage.mjs';\r\nimport { readBossRunPin,readRescuedCoachIds } from './world-boss-progression.mjs';\nimport {createBlueprintEntitlements,restoreBlueprintEntitlements} from '../modules/coach-world/blueprint-entitlements.mjs';\r\n\r\nexport const WORLD_RUN_EXERCISES = Object.freeze(['shallow-squat', 'boxing', 'overhead-reach', 'mountain', 'march']);\r\nexport class WorldRunApiError extends Error {\r\n  constructor(code, status = 409) { super(code); this.name = 'WorldRunApiError'; this.code = code; this.status = status; }\r\n}\r\nconst fail = (code, status) => { throw new WorldRunApiError(code, status); };\r\nconst sha256 = async value => [...new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value)))].map(byte => byte.toString(16).padStart(2, '0')).join('');\r\nconst uuid = value => typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\r\nconst json = value => JSON.stringify(value);\r\nconst safeEpoch = value => Number.isSafeInteger(value) && value > 0;\r\nconst exerciseIds = WORLD_RUN_EXERCISES;\r\nif (exerciseIds.some(id => !Object.hasOwn(EXERCISES, id))) throw new Error('Configured world run exercise is missing');\r\n\r\nfunction checkRequest(path, method, input, request) {\r\n  if (path === '/api/world/runs/current') {\r\n    if (method !== 'GET') fail('world_run_route_not_found', 404);\r\n    return null;\r\n  }\r\n  if (path !== '/api/world/runs' || method !== 'POST') fail('world_run_route_not_found', 404);\r\n  if (!input || typeof input !== 'object' || Array.isArray(input) ||\r\n      Object.keys(input).some(key => !['id', 'tutorialsEnabled', 'loadout','heartId'].includes(key)) || !uuid(input.id) ||\n      (input.heartId!==undefined&&input.heartId!==null&&(typeof input.heartId!=='string'||input.heartId.length>200)) ||\n      (input.tutorialsEnabled !== undefined && typeof input.tutorialsEnabled !== 'boolean')) fail('world_run_request_invalid', 400);\r\n  if (request.headers.get('Idempotency-Key') !== input.id) fail('world_run_idempotency_key_required', 400);\r\n  return { id: input.id, tutorialsEnabled: input.tutorialsEnabled ?? true, exerciseIds, loadout: input.loadout,heartId:input.heartId??null };\n}\r\n\r\nasync function assertEpoch(db, owner, epoch) {\r\n  if ((await readCurrentEpoch(db, owner) ?? 1) !== epoch) fail('world_epoch_mismatch', 409);\r\n}\r\nfunction parseRun(run) {\r\n  try { return JSON.parse(run.state); } catch { fail('world_run_state_invalid', 409); }\r\n}\r\nfunction scopedBlueprints(base,owner,epoch){\r\n  try{return base?.blueprintEntitlements===undefined?createBlueprintEntitlements({accountId:owner,dataEpoch:epoch})\r\n    :restoreBlueprintEntitlements(base.blueprintEntitlements,{accountId:owner,dataEpoch:epoch});}\r\n  catch{fail('world_run_blueprint_ledger_invalid',409);}\r\n}\r\nfunction currentDTO(owner, epoch, run, state, progression, base=null,hearts=null,serverPerformance=null) {\n  const currentVisit = state.schedule?.nodes?.[state.cursor] ?? null;\r\n  const visitId = run.visit_id ?? `visit-${run.run_id}-${run.visit_index ?? 0}`;\r\n  let runtime = null, binding = null;\r\n  try { runtime = run.runtime == null ? null : JSON.parse(run.runtime); } catch { fail('world_run_state_invalid', 409); }\r\n  try { binding = run.entered_visit_binding == null ? null : JSON.parse(run.entered_visit_binding); } catch { fail('world_run_state_invalid', 409); }\r\n  const common = { targetAccountId: owner, dataEpoch: epoch, runId: run.run_id, runStatus: run.status,\r\n    runRevision: run.revision, nextClientSeq: run.next_client_seq, writerGeneration: run.writer_generation ?? 0,\r\n    clockEpoch: run.clock_epoch ?? 0, currentVisitId: currentVisit ? visitId : null,\r\n    visit: currentVisit ? { visitIndex: run.visit_index ?? 0, visitId, state: run.visit_state ?? 'pending', binding } : null,\r\n    visitState: run.visit_state ?? 'pending', lastFrameSeq: run.last_frame_seq ?? -1,\r\n    lastFrameNow: run.last_frame_now ?? null, currentVisit, runState: state,\r\n    runtime: (run.visit_state ?? 'pending') === 'entered' ? runtime : null,blueprints:scopedBlueprints(base,owner,epoch),hearts,serverPerformance };\n  return {\n    targetAccountId: owner,\n    dataEpoch: epoch,\n    hearts,serverPerformance,\n    run: { runId: run.run_id, status: run.status, revision: run.revision, nextClientSeq: run.next_client_seq,\r\n      startedSeq: run.started_seq, currentVisit },\r\n    currentVisit,\r\n    runState: state,\r\n    pending: run.status === 'active' ? { status: common.visitState === 'entered' ? 'entered' : 'awaiting_runtime', visitId: currentVisit ? visitId : null } : null,\r\n    runtime: common.runtime,\r\n    envelope: common,\r\n    bossProgression: progression,\r\n  };\r\n}\r\n\r\nfunction startingRun({ runId, owner, epoch, seed, tutorialsEnabled, assignment, campaign, benefits, worldRevision, loadout, galaWeaponTiers,scheduleVersion,manifest,heartId,rescuedCoachIds }) {\n  const initialRun = {\r\n    id: runId, accountId: owner, dataEpoch: epoch,\r\n    stats: createRunStats({ startingLevels: benefits.statBonuses, rulesVersion:2 }),\r\n    boons: createBoonInventory(), weaponIds:benefits.weaponUpgrades, galaWeaponTiers, relicIds:benefits.relics.map(r=>r.id),\r\n  };\r\n  for (const receipt of startBoonReceipts({...benefits,relics:benefits.relics.filter(r=>loadout.relicIds.includes(r.id))})) {\r\n    initialRun.boons = grantBoon(initialRun.boons, receipt.boonId, receipt.receiptId).inventory;\r\n  }\r\n  const state = createRunSession({ runId, accountId: owner, dataEpoch: epoch, seed, exerciseIds,\n    initialRun, campaign, tutorialsEnabled, scheduleVersion, rulesVersion:2, manifest:manifest??createRunManifest(assignment.clearOrdinal), loadout,heartId,rescuedCoachIds });\n  if(scheduleVersion===4)state.hearts=projectCoachHearts(rescuedCoachIds,heartId);\n  state.startingBenefits = {\r\n    progressionRevision: benefits.revision,\r\n    galaWeaponTiers,\r\n    worldRevision,\r\n    statBonuses: { ...benefits.statBonuses },\r\n    relics: structuredClone(benefits.relics),\r\n    weaponUpgrades: [...benefits.weaponUpgrades],\r\n    weaponTextures: [...benefits.weaponTextures],\r\n    weaponColors: [...benefits.weaponColors],\r\n  };\r\n  return state;\r\n}\r\n\r\n/** Feature-gated HTTP handler. It creates only a pinned run session; no runtime or completion route exists here. */\r\nexport async function worldRunApi(request, db, owner, path, method, input = {}, now = Date.now(), { dataEpoch, enabled = false,scheduleVersion=4 } = {}) {\n  if (!enabled) fail('world_run_route_not_found', 404);\r\n  if (!Number.isSafeInteger(now) || now < 0 || !safeEpoch(dataEpoch) || typeof owner !== 'string' || owner.length === 0) fail('world_run_context_invalid', 400);\r\n  const normalized = checkRequest(path, method, input, request);\r\n  await assertEpoch(db, owner, dataEpoch);\n  const rescuedCoachIds=await readRescuedCoachIds(db,owner,dataEpoch);\n  const serverPerformance={weapons:(await readAccountPerformance(db,owner)).weapons};\n\r\n  if (!normalized) {\r\n    const active = await readActiveRun(db, owner, dataEpoch);\r\n    const base=await readBase(db,owner,dataEpoch);\r\n    if (!active) {\r\n      await assertEpoch(db, owner, dataEpoch);\r\n      return { targetAccountId: owner, dataEpoch, run: null, currentVisit: null, pending: null, runState: null, runtime: null,hearts:projectCoachHearts(rescuedCoachIds),serverPerformance,\n        envelope: { targetAccountId: owner, dataEpoch, runId: null, runStatus: null, runRevision: null,\r\n          nextClientSeq: null, writerGeneration: null, clockEpoch: null, currentVisitId: null, visit: null,\r\n          visitState: null, lastFrameSeq: null, lastFrameNow: null, currentVisit: null, runState: null, runtime: null,blueprints:scopedBlueprints(base?.base,owner,dataEpoch),hearts:projectCoachHearts(rescuedCoachIds),serverPerformance } };\n    }\r\n    const pin = await readBossRunPin(db, owner, dataEpoch, active.run_id);\r\n    if (!pin) fail('world_run_schedule_unsupported', 409);\r\n    const state = parseRun(active);\r\n    await assertEpoch(db, owner, dataEpoch);\r\n    return currentDTO(owner, dataEpoch, active, state, { issuedClearOrdinal: pin.issuedClearOrdinal, assignment: pin.assignment },base?.base,projectCoachHearts(rescuedCoachIds,state.heartModifiers?.heartId??null),serverPerformance);\n  }\r\n\r\n  const base = await readBase(db, owner, dataEpoch);\r\n  if (!base) fail('world_base_required', 409);\r\n  const progression = readProgression(base.base);\r\n  const benefits = getRunStartBenefits(progression, { accountId: owner });\r\n  const campaign = await readCampaign(db, owner, dataEpoch);\r\n  const galaWeaponTiers=serverPerformance.weapons;\n  let loadout;try{loadout=validateRunLoadout(normalized.loadout,{weaponIds:benefits.weaponUpgrades,galaWeaponTiers,relicIds:benefits.relics.map(r=>r.id)});}catch{fail('world_run_loadout_invalid',400);}\r\n  if(scheduleVersion===4&&loadout.relicIds.length)fail('world_run_legacy_relic_rejected',400);\n  try{projectCoachHearts(rescuedCoachIds,normalized.heartId);}catch{fail('world_run_heart_not_owned',400);}\n  const normalizedRequest = { tutorialsEnabled: normalized.tutorialsEnabled, exerciseIds: normalized.exerciseIds, loadout,...(Object.hasOwn(input,'heartId')?{heartId:normalized.heartId}:{}) };\n  const idScope = await sha256(json(['world-run-start-v1', owner, dataEpoch, normalized.id]));\r\n  const requestId = `wrstart:${idScope}`;\r\n  const requestHash = await sha256(json(['world-run-start-v1', owner, dataEpoch, normalizedRequest]));\r\n  const runId = `wr-${idScope}`; // stable, server-created, and within the run owner's 96-byte ID limit.\r\n  const seedBytes = crypto.getRandomValues(new Uint32Array(1));\r\n  const seed = seedBytes[0]; // generated once outside C's bounded transaction retries.\n  const manifest=scheduleVersion===4?createRescueRunManifest({rescuedCoachIds,random:()=>crypto.getRandomValues(new Uint32Array(1))[0]/4294967296}):null;\n  const result = await startBossRunAtomic(db, {\r\n    owner, epoch: dataEpoch, requestId, hash: requestHash, now,\r\n    create: async ({ issuedClearOrdinal }) => {\r\n      const assignment = bossAssignmentForClearCount(issuedClearOrdinal);\r\n      const state = startingRun({ runId, owner, epoch: dataEpoch, seed, tutorialsEnabled: normalized.tutorialsEnabled,\r\n        assignment, campaign: campaign?.progress ?? null, benefits, worldRevision: base.revision, loadout, galaWeaponTiers,scheduleVersion,manifest,heartId:normalized.heartId,rescuedCoachIds });\n      return { runId, state, response: { runId, scheduleVersion } };\n    },\r\n    resumeResponse: async active => ({ runId: active.run_id }),\r\n  });\r\n  const storedState = result.run ? parseRun(result.run) : null;\r\n  await assertEpoch(db, owner, dataEpoch);\r\n  if (!storedState) fail('world_run_state_unavailable', 409);\r\n  const pin = await readBossRunPin(db, owner, dataEpoch, result.run.run_id);\r\n  if (!pin) fail('world_run_schedule_unsupported', 409);\r\n  await assertEpoch(db, owner, dataEpoch);\r\n  return { ...currentDTO(owner, dataEpoch, result.run, storedState,\r\n    { issuedClearOrdinal: pin.issuedClearOrdinal, assignment: pin.assignment },(await readBase(db,owner,dataEpoch))?.base,projectCoachHearts(rescuedCoachIds,storedState.heartModifiers?.heartId??null),serverPerformance),\n    replayed: result.replayed, resumed: result.resumed, startResponse: result.response };\r\n}\r\n","server/world-run-runtime.mjs":"import {awardRunProficiency} from '../modules/roguelite/run-proficiency.mjs';\r\nimport {\r\n  RunStorageError, canonicalJson, fingerprint, epochGuard, runGuard, updateRunStatements,\r\n  startRequestStatement, clearAssertions, readStartReplay, readEventReplay, readRun,\r\n  readCurrentEpoch, sequenceStatements, receiptStatements, visitStatements, readAvailableTicket,\r\n  baseGuard, saveBaseStatement, appendRunEventAtomic, readBase,\r\n} from './world-run-storage.mjs';\r\nimport { readBossRunPin,readRescuedCoachIds,readRescueManifest,readBossProgression } from './world-boss-progression.mjs';\nimport {createRescue,restoreRescue,validateRescueInput} from '../modules/roguelite/rescue-state.mjs';\nimport {heartForCoach,projectCoachHearts,expireRunWearables,grantChestWearable,CHEST_WEARABLES} from '../modules/roguelite/coach-hearts.mjs';\nimport {authoredRoomKind,normalizeAuthoredSample,restoreAuthoredRoom} from '../modules/roguelite/run-authored-rooms.mjs';\nimport {rescueLevelRequirements} from '../modules/roguelite/run-schedule-v4.mjs';\nimport { appendBossRunEventAtomic } from './world-boss-run-owner.mjs';\r\nimport {\r\n  currentRunRoom, finishRunTutorial, setRunPaused, advanceRunRoom, roomTarget, chooseRunRoute, interactRunShop,\r\n} from '../modules/roguelite/run-controller.mjs';\r\nimport { createRoomUnlockProgress, recordBossVictory, discoverRoom, recordCompletedRun } from '../modules/roguelite/room-unlocks.mjs';\r\nimport { isRunShopItemId } from '../modules/roguelite/run-shop.mjs';\nimport { applyRunRoomRewards } from './run-room-rewards.mjs';\r\nimport { enterRoom, resumePage, resumePending, stepFrame } from '../modules/roguelite/run-step.mjs';\r\nimport { createRunScheduleForVersion, coachForRoomNodeForVersion } from '../modules/roguelite/run-schedule-v2-builder.mjs';\r\nimport { bossAssignmentForClearCount } from '../modules/roguelite/run-schedule-v2.mjs';\r\nimport { finishBossChip } from '../modules/roguelite/boss-finisher.mjs';\r\nimport { ticketDecision, energyGrantMs } from '../modules/coach-world/run-grant-rules.mjs';\r\nimport { restoreTreasureChase, openTreasureChest } from '../modules/roguelite/treasure-room.mjs';\r\nimport { resolveRunTreasure, exitCaughtRunTreasure } from '../modules/roguelite/run-controller.mjs';\r\nimport { TREASURE_CHEST_ID, treasureManifestFor, validTreasureManifest, grantTreasureBlueprint } from './run-chest-rewards.mjs';\r\nimport { createBlueprintEntitlements, restoreBlueprintEntitlements } from '../modules/coach-world/blueprint-entitlements.mjs';\r\nimport { CLASS_IDS, CLASS_INPUT_BOOLS, SPELL_IDS } from '../modules/roguelite/class-combat.mjs';\r\n\r\nexport class WorldRunRuntimeError extends RunStorageError {}\r\nconst fail = (code, status = 409, details) => { throw new WorldRunRuntimeError(code, status, details); };\r\nconst invalid = (condition, detail) => { if (condition) fail('run_storage_invalid', 400, detail); };\r\nconst safe = n => Number.isSafeInteger(n) && n >= 0;\r\nconst finite = n => typeof n === 'number' && Number.isFinite(n) && n >= 0;\r\nconst text = value => typeof value === 'string' && value.length > 0;\r\nconst CLOCK_ROUNDTRIP_TOLERANCE_MS = 0.001;\r\nconst stmt = (db, sql, ...args) => db.prepare(sql).bind(...args);\r\nconst first = (db, sql, ...args) => stmt(db, sql, ...args).first();\r\nconst all = async (db, sql, ...args) => (await stmt(db, sql, ...args).all()).results;\r\nconst parse = (value, field) => { try { return value == null ? null : JSON.parse(value); } catch { fail('run_internal_fault', 500, field); } };\r\nconst clone = value => structuredClone(value);\r\nconst receiptId = async (owner, epoch, runId, visitId, roomId) => `wu-${(await fingerprint([owner, epoch, runId, visitId, roomId])).slice(0, 64)}`;\r\nconst sha = value => fingerprint(value);\r\nconst assertion = (db, condition, args = []) => stmt(db, `INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`, crypto.randomUUID(), ...args);\r\nconst uuid = value => typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\r\n\r\nexport const RUN_RUNTIME_POLICY = Object.freeze({ clockSlackMs: 1000, maxAcceptedWallGapMs: 2000,\r\n  minRepIntervalMs: 250, holdDeltaFloatToleranceMs: 0.001, maxFrames: 32, bossFinishAckWindowMs: 2000,\r\n  energyPerUnitMs: 30000, energyPerRunCapMs: 300000,\r\n  energyBankCapMs: 600000, ticketMinUnits: 3, ticketCapacity: 1 });\r\n\r\n// Optional full-game rooms this host can referee, keyed by roomType -> route id.\r\n// Jelly (crisis_combat), Clay (shrink_burpees_maze) and Dance (pixel_laser) are the arcade doors hosted.\r\nconst OPTIONAL_ROOM_TYPES = Object.freeze({ corgi_shop: 'papier_mache', healing_waters: 'pond', coin_chase: 'grass', crisis_combat: 'jelly', shrink_burpees_maze: 'clay', pixel_laser: 'dance',nature_yoga:'wood',secret_room:'quilt',two_altars:'flat_paper' });\nconst SUPPORTED_OPTIONAL_ROOMS = Object.freeze(Object.values(OPTIONAL_ROOM_TYPES));\r\nconst KNOWN_OPTIONAL_ROOMS = Object.freeze(['pond', 'grass', 'papier_mache', 'jelly', 'clay', 'dance', 'treasure','wood','quilt','flat_paper']);\nconst CLAY_ACTIONS = Object.freeze(['jump', 'slide', 'left', 'right']);\r\n// Rooms whose accepted movement can qualify as one unit (>= energyPerUnitMs of accepted credit); pond/shop never do.\r\nconst MOVEMENT_ROOM_TYPES = Object.freeze(['coin_chase', 'crisis_combat', 'shrink_burpees_maze', 'pixel_laser']);\r\n// Which arcade door an exact sample.arcade shape belongs to: {actions} clay, {energy,coverage} dance, anything else is judged as jelly's five controls.\r\nconst arcadeShapeOf = arcade => arcade && typeof arcade === 'object' && !Array.isArray(arcade)\r\n  ? (Object.hasOwn(arcade, 'actions') ? 'clay' : Object.hasOwn(arcade, 'energy') || Object.hasOwn(arcade, 'coverage') ? 'dance' : 'jelly') : 'jelly';\r\nconst SHOP_ACTIONS = Object.freeze(['open-door', 'grab', 'return', 'checkout', 'exit', 'attack']);\r\nconst nodeSupported = node => Boolean(node) && (['warmup', 'miniboss', 'mainboss', 'recovery'].includes(node.type) ||\r\n  node.type === 'bonus' && node.doorId === 'treasure' && node.roomType === 'treasure_side_room' ||\r\n  node.type === 'full-game' && Object.hasOwn(OPTIONAL_ROOM_TYPES, node.roomType) && node.doorId === OPTIONAL_ROOM_TYPES[node.roomType]);\r\n\r\nexport function visitIdFor(runId, visitIndex) { return `visit-${runId}-${visitIndex}`; }\r\nfunction validateAction(path, method, body, request) {\r\n  const claim = path.match(/^\\/api\\/world\\/runs\\/([^/]+)\\/claim$/);\r\n  const events = path.match(/^\\/api\\/world\\/runs\\/([^/]+)\\/events$/);\r\n  const status = path.match(/^\\/api\\/world\\/runs\\/([^/]+)\\/outbox-status$/);\r\n  const decodeRunId = match => {\r\n    let runId; try { runId = decodeURIComponent(match[1]); } catch { fail('run_storage_invalid', 400, 'runId'); }\r\n    invalid(!/^[A-Za-z0-9_-]{1,96}$/.test(runId), 'runId'); return runId;\r\n  };\r\n  if (claim && method === 'POST') {\r\n    const parsed = validateClaim(body); if (request.headers.get('Idempotency-Key') !== parsed.id) fail('run_idempotency_key_required', 400);\r\n    return { action: 'claim', runId: decodeRunId(claim), body: parsed };\r\n  }\r\n  if (events && method === 'POST') {\r\n    const parsed = validateEvent(body); if (request.headers.get('Idempotency-Key') !== parsed.id) fail('run_idempotency_key_required', 400);\r\n    return { action: 'event', runId: decodeRunId(events), body: parsed };\r\n  }\r\n  if (status && method === 'POST') return { action: 'status', runId: decodeRunId(status), body: validateStatus(body) };\r\n  if (!claim && !events && !status) fail('world_run_route_not_found', 404);\r\n  fail('world_run_route_not_found', 404);\r\n}\r\nfunction exactKeys(value, keys, field) {\r\n  invalid(!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(key => !keys.includes(key)), field);\r\n}\r\nfunction validateClaim(body) {\r\n  exactKeys(body, ['id', 'type', 'expectedWriterGeneration', 'expectedRunRevision', 'now'], 'claim fields');\r\n  invalid(!uuid(body.id) || body.type !== 'writer-claim' || !safe(body.expectedWriterGeneration) || !safe(body.expectedRunRevision) || !finite(body.now), 'claim');\r\n  return body;\r\n}\r\nfunction validateEvent(body) {\r\n  exactKeys(body, ['id', 'clientSeq', 'writerGeneration', 'clockEpoch', 'type', 'payload'], 'event fields');\r\n  invalid(!uuid(body.id) || !safe(body.clientSeq) || body.clientSeq < 1 || body.clientSeq >= Number.MAX_SAFE_INTEGER || !safe(body.writerGeneration) || !safe(body.clockEpoch) ||\r\n    typeof body.type !== 'string' || body.type.length < 1 || body.type.length > 40, 'event');\r\n  if (body.writerGeneration === 0) fail('writer_claim_required', 409);\r\n  if (!['tutorial', 'room_enter', 'run_pause', 'run_frame', 'run_advance', 'run_end', 'boss_finish','rescue_begin', 'route_chosen', 'shop_action', 'treasure_open'].includes(body.type)) fail('run_action_unsupported', 409);\n  if (!body.payload || typeof body.payload !== 'object' || Array.isArray(body.payload)) invalid(true, 'event payload');\r\n  const p = body.payload;\r\n  const allowed = {\r\n    tutorial: ['visitId'], room_enter: ['visitId', 'now', 'classId'], run_pause: ['visitId', 'reason', 'paused'],\r\n    run_frame: ['visitId', 'clockEpoch', 'frames'], run_advance: ['visitId'], run_end: [], boss_finish: ['visitId'],rescue_begin:['visitId'],\n    route_chosen: ['visitId', 'roomId'], shop_action: ['visitId', 'action'], treasure_open: ['visitId', 'chestId'],\r\n  }[body.type];\r\n  exactKeys(p, allowed, 'event payload fields');\n  if(body.type==='rescue_begin')invalid(!text(p.visitId),'visitId');\n  if (body.type === 'tutorial' || body.type === 'room_enter' || body.type === 'run_advance' || body.type === 'run_frame' || body.type === 'run_pause' || body.type === 'boss_finish' ||\r\n    body.type === 'route_chosen' || body.type === 'shop_action' || body.type === 'treasure_open') invalid(!text(p.visitId), 'visitId');\r\n  // roomId null is training; any other id must be a known optional room (eligibility is the reducer's call).\r\n  if (body.type === 'route_chosen') invalid(!Object.hasOwn(p, 'roomId') || !(p.roomId === null || KNOWN_OPTIONAL_ROOMS.includes(p.roomId)), 'route roomId');\r\n  if (body.type === 'shop_action') {\r\n    exactKeys(p.action, ['type', 'itemId'], 'shop action fields');\r\n    const stocked = ['grab', 'return'].includes(p.action.type);\r\n    invalid(!SHOP_ACTIONS.includes(p.action.type) || (stocked ? !isRunShopItemId(p.action.itemId) : p.action.itemId !== undefined), 'shop action');\n  }\r\n  if (body.type === 'treasure_open') invalid(p.chestId !== TREASURE_CHEST_ID, 'treasure chestId');\r\n  if (body.type === 'room_enter') invalid(!finite(p.now), 'room enter clock');\r\n  if (body.type === 'room_enter' && p.classId !== undefined) invalid(!CLASS_IDS.includes(p.classId), 'combat class');\r\n  if (body.type === 'run_pause') invalid(!['tracking', 'tutorial', 'background', 'camera-handoff', 'user', 'loading'].includes(p.reason) || typeof p.paused !== 'boolean', 'pause');\r\n  if (body.type === 'run_frame') {\r\n    invalid(p.clockEpoch !== body.clockEpoch || !Array.isArray(p.frames) || p.frames.length < 1 || p.frames.length > RUN_RUNTIME_POLICY.maxFrames, 'frames');\r\n    for (const frame of p.frames) {\r\n      exactKeys(frame, ['op', 'frameSeq', 'now', 'tracked', 'value', 'choice', 'boss', 'pond', 'grass', 'arcade', 'treasure', 'sourceTs', 'quality', 'cueAck','yoga','secret','altar','rescue'], 'frame fields');\n      for(const kind of ['yoga','secret','altar','rescue'])if(frame[kind]!==undefined){\n        invalid(frame.op!=='sample','room input requires sample');\n        try{if(kind==='rescue')validateRescueInput(frame[kind]);else normalizeAuthoredSample(kind,frame[kind]);}catch{invalid(true,kind+' frame');}\n      }\n      invalid(!['track', 'sample'].includes(frame.op) || !safe(frame.frameSeq) || !finite(frame.now) || typeof frame.tracked !== 'boolean' ||\r\n        (frame.op === 'sample' && (!Number.isFinite(frame.value) || frame.value < 0 || ![undefined, null, 'left', 'right', 'crouch'].includes(frame.choice))) ||\r\n        (frame.op === 'track' && (frame.value !== undefined || frame.choice !== undefined || frame.boss !== undefined || frame.pond !== undefined || frame.grass !== undefined || frame.arcade !== undefined || frame.treasure !== undefined)), 'frame');\r\n      if(frame.sourceTs!==undefined)invalid(!finite(frame.sourceTs)||frame.sourceTs>frame.now,'source timestamp');\r\n      if(frame.quality!==undefined){exactKeys(frame.quality,['body','hands'],'quality');invalid(Object.keys(frame.quality).length!==2||typeof frame.quality.body!=='boolean'||typeof frame.quality.hands!=='boolean','quality');}\r\n      if(frame.cueAck!==undefined&&frame.cueAck!==null){exactKeys(frame.cueAck,['phase','attackNo','generation'],'cue ack');invalid(Object.keys(frame.cueAck).length!==3||!safe(frame.cueAck.generation)||![1,2].includes(frame.cueAck.phase)||!safe(frame.cueAck.attackNo),'cue ack');}\r\n      if (frame.arcade !== undefined) {\r\n        // Three exact shapes, never mixed: Clay {actions}, Dance {energy,coverage}, Jelly the five cover-crisis controls.\r\n        const shape = arcadeShapeOf(frame.arcade);\r\n        if (shape === 'clay') validateClayInput(frame.arcade);\r\n        else if (shape === 'dance') validateDanceInput(frame.arcade);\r\n        else validateJellyInput(frame.arcade);\r\n      }\r\n      if (frame.pond !== undefined) {\r\n        exactKeys(frame.pond, ['balanced', 'handOut'], 'pond frame fields');\r\n        invalid(Object.keys(frame.pond).length !== 2 || typeof frame.pond.balanced !== 'boolean' || typeof frame.pond.handOut !== 'boolean', 'pond frame');\r\n      }\r\n      if (frame.grass !== undefined) {\r\n        exactKeys(frame.grass, ['controlValid', 'height01', 'steer', 'exerciseEligible'], 'grass frame fields');\r\n        const { controlValid, height01, steer } = frame.grass;\r\n        invalid(![3,4].includes(Object.keys(frame.grass).length) || frame.grass.exerciseEligible!==undefined&&typeof frame.grass.exerciseEligible!=='boolean' || typeof controlValid !== 'boolean' ||\r\n          typeof height01 !== 'number' || !Number.isFinite(height01) || height01 < 0 || height01 > 1 ||\r\n          typeof steer !== 'number' || !Number.isFinite(steer) || steer < -1 || steer > 1, 'grass frame');\r\n      }\r\n      if (frame.treasure !== undefined) {\r\n        exactKeys(frame.treasure, ['jogging'], 'treasure frame fields');\r\n        invalid(Object.keys(frame.treasure).length !== 1 || typeof frame.treasure.jogging !== 'boolean', 'treasure frame');\r\n      }\r\n      if (frame.boss !== undefined) {\r\n        const base = ['left', 'punchLeft', 'punchRight', 'crouch', 'jump', 'jog'];\r\n        exactKeys(frame.boss, [...base, ...CLASS_INPUT_BOOLS, 'spell'], 'boss frame fields');\r\n        invalid(base.some(key => typeof frame.boss[key] !== 'boolean') ||\r\n          CLASS_INPUT_BOOLS.some(key => frame.boss[key] !== undefined && typeof frame.boss[key] !== 'boolean') ||\r\n          (frame.boss.spell !== undefined && frame.boss.spell !== null && !SPELL_IDS.includes(frame.boss.spell)), 'boss frame');\r\n      }\r\n    }\r\n  }\r\n  return body;\r\n}\r\nconst unit = (value, low = 0, high = 1) => typeof value === 'number' && Number.isFinite(value) && value >= low && value <= high;\r\n// Exact Dance sample: the two observed movement scalars only. Score, beats, time and rewards are the engine's, never input.\r\nfunction validateDanceInput(input) {\r\n  exactKeys(input, ['energy', 'coverage'], 'dance arcade fields');\r\n  invalid(Object.keys(input).length !== 2 || !unit(input.energy) || !unit(input.coverage), 'dance arcade');\r\n}\r\n// Exact Clay (authored world run) sample: an ordered, duplicate-free list of at most four runner controls.\r\nfunction validateClayInput(input) {\r\n  exactKeys(input, ['actions'], 'clay arcade fields');\r\n  invalid(Object.keys(input).length !== 1 || !Array.isArray(input.actions) || input.actions.length > CLAY_ACTIONS.length ||\r\n    input.actions.some(action => !CLAY_ACTIONS.includes(action)) || new Set(input.actions).size !== input.actions.length, 'clay actions');\r\n}\r\n// Exact Jelly (cover crisis) sample: every key present, every range closed, nothing else.\r\nfunction validateJellyInput(input) {\r\n  exactKeys(input, ['hidden', 'side', 'beam', 'zones', 'fires'], 'arcade fields');\r\n  invalid(Object.keys(input).length !== 5 || typeof input.hidden !== 'boolean' || ![-1, 0, 1].includes(input.side), 'arcade hidden/side');\r\n  exactKeys(input.beam, ['on', 'x'], 'arcade beam fields');\r\n  invalid(Object.keys(input.beam).length !== 2 || typeof input.beam.on !== 'boolean' || !unit(input.beam.x, -1, 1), 'arcade beam');\r\n  exactKeys(input.zones, ['left', 'middle', 'right', 'up'], 'arcade zone fields');\r\n  invalid(Object.keys(input.zones).length !== 4 || !['left', 'middle', 'right', 'up'].every(key => unit(input.zones[key])), 'arcade zones');\r\n  invalid(!Array.isArray(input.fires) || input.fires.length > 2, 'arcade fires');\r\n  for (const fire of input.fires) {\r\n    exactKeys(fire, ['aimX', 'power'], 'arcade fire fields');\r\n    invalid(Object.keys(fire).length !== 2 || !(fire.aimX === null || unit(fire.aimX, -1, 1)) || !unit(fire.power, 0, 100), 'arcade fire');\r\n  }\r\n}\r\nfunction validateStatus(body) {\r\n  exactKeys(body, ['entries'], 'status fields');\r\n  invalid(!Array.isArray(body.entries) || body.entries.length < 1 || body.entries.length > 64, 'status entries');\r\n  for (const item of body.entries) {\r\n    exactKeys(item, ['writerGeneration', 'clientSeq', 'eventHash'], 'status entry');\r\n    invalid(!safe(item.writerGeneration) || !safe(item.clientSeq) || item.clientSeq < 1 || typeof item.eventHash !== 'string' || !/^[a-f0-9]{64}$/.test(item.eventHash), 'status entry');\r\n  }\r\n  return body;\r\n}\r\nfunction stateFor(run) { const value = parse(run?.state, 'run state'); if (!value || ![2,3,4].includes(value.schedule?.scheduleVersion)) fail('world_run_schedule_unsupported', 409); return value; }\nfunction normalizeRestorableState(state) {\r\n  const version = state.schedule?.scheduleVersion ?? 1;\r\n  let expected;\r\n  try {\r\n    expected = createRunScheduleForVersion(version, { seed: state.seed, exerciseIds: state.exerciseIds,\r\n      assignment: version === 2 ? state.schedule.assignment : undefined,\r\n      manifest: version>=3?state.schedule.manifest:undefined, pendingBoss: version>=3?state.escapeHistory:state.escapeDetour?.boss ?? null, routes: state.routes ?? [] });\n  } catch { fail('run_state_invalid', 409); }\r\n  if (canonicalJson(state.schedule) !== canonicalJson(expected)) fail('run_state_invalid', 409);\r\n  return { ...state, schedule: expected };\r\n}\r\nfunction runtimeFor(run) { return parse(run.runtime, 'runtime'); }\r\nfunction timingFor(run) { return { segmentAdmittedServerMs: run.started_at, visitEnteredServerMs: null, pausedMs: 0,\r\n  pauseSinceServerMs: null, visitClaimedMs: run.started_at, unpausedAcceptedMs: 0, visitAcceptedMs: 0,\r\n  ...(parse(run.run_timing, 'run timing') ?? {}) }; }\r\nfunction blueprintLedger(base, owner, epoch) {\r\n  try { return base?.blueprintEntitlements === undefined ? createBlueprintEntitlements({ accountId: owner, dataEpoch: epoch })\r\n    : restoreBlueprintEntitlements(base.blueprintEntitlements, { accountId: owner, dataEpoch: epoch }); }\r\n  catch { fail('run_blueprint_ledger_invalid', 409); }\r\n}\r\nfunction envelope(owner, epoch, run, runState = stateFor(run), runtime = runtimeFor(run), base = null) {\r\n  const current = currentRunRoom(runState);\r\n  const visit = current ? { visitIndex: run.visit_index, visitId: run.visit_id ?? visitIdFor(run.run_id, run.visit_index),\r\n    state: run.visit_state, binding: parse(run.entered_visit_binding, 'visit binding') } : null;\r\n  return { targetAccountId: owner, dataEpoch: epoch, runId: run.run_id, runStatus: run.status,\r\n    runRevision: run.revision, nextClientSeq: run.next_client_seq, clockEpoch: run.clock_epoch,\r\n    writerGeneration: run.writer_generation, visit, lastFrameSeq: run.last_frame_seq,\r\n    lastFrameNow: run.last_frame_now, runState, runtime, currentVisit: current,\r\n    blueprints: blueprintLedger(base?.base ?? base, owner, epoch),hearts:runState.hearts??null,\n    serverPerformance:{weapons:runState.startingBenefits?.galaWeaponTiers??{}} };\n}\r\nfunction response(owner, epoch, run, runState, runtime, now, outcome, detail, receipts = [], base = null) {\r\n  return { targetAccountId: owner, dataEpoch: epoch, serverTime: now, outcome, ...(detail === undefined ? {} : { detail }),\r\n    ...(receipts.length ? { receipts } : {}), envelope: envelope(owner, epoch, run, runState, runtime, base) };\r\n}\r\nfunction eventHash(event) { return sha({ id: event.id, type: event.type, payload: event.payload,\r\n  writerGeneration: event.writerGeneration, clockEpoch: event.clockEpoch }); }\r\nfunction parseFingerprint(row) { return JSON.parse(row.response); }\r\nasync function assertEpoch(db, owner, epoch) {\r\n  if ((await readCurrentEpoch(db, owner) ?? 1) !== epoch) fail('run_epoch_mismatch', 409);\r\n}\r\nfunction verifyWriterGeneration(run, generation) {\r\n  if (run.writer_generation !== generation) fail('writer_fenced', 409, { currentGeneration: run.writer_generation, runRevision: run.revision });\r\n}\r\nfunction visitMatches(run, payload) { if (payload.visitId !== (run.visit_id ?? visitIdFor(run.run_id, run.visit_index))) fail('run_visit_conflict', 409); }\r\nfunction supportedNode(state) {\r\n  const node = currentRunRoom(state);\r\n  if (!nodeSupported(node)) fail('run_action_unsupported', 409);\r\n  return node;\r\n}\r\nfunction serverClock(run, clientNow) { return clientNow + run.clock_offset_ms; }\r\nfunction validBossPin(state, pin) {\r\n  const assignment=state.schedule.scheduleVersion===3?state.schedule.manifest?.levels.find(l=>l.levelIndex===currentRunRoom(state)?.levelIndex)?.assignment:state.schedule.assignment;\r\n  if (!pin || pin.assignmentId !== assignment?.assignmentId || pin.issuedClearOrdinal !== assignment?.clearOrdinal) return false;\r\n  try {\r\n    const expected = bossAssignmentForClearCount(pin.issuedClearOrdinal);\r\n    return canonicalJson(pin.assignment) === canonicalJson(expected) && canonicalJson(assignment) === canonicalJson(expected);\r\n  } catch { return false; }\r\n}\r\nfunction bossPinGuard(db, owner, epoch, runId, pin) {\r\n  pin=pin.rootPin??pin;\r\n  return assertion(db, \"EXISTS(SELECT 1 FROM world_boss_run_pins WHERE owner=? AND epoch=? AND run_id=? AND issued_clear_ordinal=? AND assignment_id=? AND group_id=? AND groups_hash=? AND rows_hash=? AND assignment=? AND schedule_version=2 AND policy_id='authored-triples-clear-v1')\",\r\n    [owner, epoch, runId, pin.issuedClearOrdinal, pin.assignmentId, pin.groupId, pin.tableHashes.groups, pin.tableHashes.eligibilityRows, canonicalJson(pin.assignment)]);\r\n}\r\nasync function applySimpleAction(db, type, payload, context, owner, epoch, serverNow, lastRaidStartSeq = null) {\r\n  const run = context.run, state = stateFor(run), runtime = runtimeFor(run), timing = timingFor(run);\r\n  let nextState = clone(state), nextRuntime = runtime, nextRun = { ...run }, receipts = [], visits = [], ticket = null, end = null, base = undefined, campaign = undefined;\r\n  const visit = run.visit_id ?? visitIdFor(run.run_id, run.visit_index);\r\n  if (type !== 'run_end') visitMatches(run, payload);\r\n  if (type === 'tutorial') {\r\n    supportedNode(state);\r\n    if (state.phase === 'tutorial') nextState = finishRunTutorial(state);\r\n    else return noop(context, 'tutorial_already_done');\r\n  } else if (type === 'room_enter') {\r\n    const node = supportedNode(state);\r\n    if (state.phase === 'tutorial') fail('run_tutorial_required', 409);\r\n    if (run.visit_state === 'entered') return noop(context, 'already_entered');\r\n    if (payload.classId !== undefined) {\r\n      if (state.combatClass && state.combatClass !== payload.classId) fail('run_class_locked', 409);\r\n      // Class selection is pinned on the first entered room, before combat starts.\r\n      if (!state.combatClass && state.cursor !== 0) fail('run_class_locked', 409);\r\n      state.combatClass = payload.classId;\r\n    }\r\n    const now = serverClock(run, payload.now);\r\n    if (!finite(now) || now > serverNow + RUN_RUNTIME_POLICY.clockSlackMs) fail('run_clock_ahead', 409);\r\n    let bossVisualId = null, canonicalCoachId = null;\r\n    if (node.type === 'miniboss' || node.type === 'mainboss') {\r\n      let row;\r\n      try { row = coachForRoomNodeForVersion(state.schedule.scheduleVersion, node); } catch { fail('run_state_invalid', 409); }\r\n      if(state.schedule.scheduleVersion===4)await checkedRescueManifest(db,owner,epoch,run,state);\n      else {const pin = await readBossRunPin(db, owner, epoch, run.run_id,state.schedule.scheduleVersion===3?node.levelIndex:undefined);\n       if (!validBossPin(state, pin)) fail('world_run_schedule_unsupported', 409);}\n      bossVisualId = row.sourceVisualId; canonicalCoachId = row.canonicalCoachId;\r\n    }\r\n    let treasureManifest;\r\n    if (node.type === 'bonus' && node.doorId === 'treasure') {\r\n      if (!context.base?.base) fail('run_base_missing', 409);\r\n      treasureManifest = treasureManifestFor({ base: context.base.base, accountId: owner, dataEpoch: epoch,\r\n        runSeed: state.seed, runId: run.run_id, visitId: visit });\r\n      if (!validTreasureManifest(treasureManifest)) fail('run_treasure_manifest_rejected', 409);\r\n    }\r\n    let entered;\r\n    try { entered = enterRoom({ runState: state, now, bossVisualId, ...(treasureManifest ? { treasureManifest } : {}) }); } catch { fail('run_room_enter_rejected', 409); }\r\n    nextState = entered.runState; nextRuntime = { ...entered.runtime, repLastNow: now };\n    if(state.schedule.scheduleVersion===4&&canonicalCoachId){\n      const rescued=await readRescuedCoachIds(db,owner,epoch),first=!!heartForCoach(canonicalCoachId)&&!rescued.includes(canonicalCoachId);\n      nextRuntime.rescuePresentation={canonicalCoachId,presentationCoachId:first?canonicalCoachId:'myr5',rescueKind:first?'first':'repeat'};\n    }\n    nextRun.visit_state = 'entered'; nextRun.entered_visit_binding = JSON.stringify({ visitId: visit, nodeId: node.id, manifestRevision: 1, bossVisualId, canonicalCoachId,\r\n      ...(treasureManifest ? { owner, epoch, treasureBuildingId: treasureManifest[0].rewards[0].buildingId } : {}) });\r\n    nextRun.visit_id = visit;\n    if(treasureManifest&&state.schedule.scheduleVersion===4){\n      const wearableOffer={version:1,wearableId:CHEST_WEARABLES[run.visit_index%CHEST_WEARABLES.length].id};\n      nextRuntime.treasureWearableOffer=wearableOffer;\n      nextRun.entered_visit_binding=JSON.stringify({...JSON.parse(nextRun.entered_visit_binding),wearableOffer});\n    }\n    nextRun.last_frame_now = now;\r\n    timing.visitEnteredServerMs = serverNow;\r\n    // pausedMs is a RUN aggregate; the credit budget only subtracts the pause that happened inside THIS visit.\r\n    // A row entered before this field existed has no baseline and conservatively subtracts the run's whole paused total.\r\n    timing.visitPausedBaseMs = timing.pausedMs;\r\n    timing.visitAcceptedMs = 0;\r\n    timing.pauseSinceServerMs = nextState.pauseReasons.length ? serverNow : null;\r\n  } else if(type==='rescue_begin'){\n    const node=supportedNode(state);\n    if(state.schedule.scheduleVersion!==4||run.visit_state!=='entered'||!runtime?.boss||runtime.boss.status!=='finisher'||state.phase!=='active'||state.stats.health<=0||state.pauseReasons.length||runtime.boss.kind==='mainboss'&&(runtime.boss.phase!==2||runtime.boss.phase2Pending))fail('run_rescue_not_ready',409);\n    await checkedRescueManifest(db,owner,epoch,run,state);\n    if(runtime.rescue)return noop(context,'rescue_already_started');\n    const presentation=runtime.rescuePresentation;\n    if(!presentation||presentation.canonicalCoachId!==node.canonicalCoachId)fail('run_visit_binding_invalid',409);\n    nextRuntime={...runtime,goodSamples:0,rescue:createRescue({encounterId:node.id,challengeId:crypto.randomUUID(),generation:0,...presentation})};\n  } else if (type === 'run_pause') {\n    supportedNode(state);\r\n    const prior = nextState.pauseReasons.includes(payload.reason);\r\n    if (prior === payload.paused) return noop(context, 'pause_unchanged');\r\n    nextState = setRunPaused(state, payload.reason, payload.paused);\n    if(nextRuntime?.rescue&&nextRuntime.rescue.status!=='accepted')nextRuntime={...nextRuntime,goodSamples:0,rescue:restoreRescue(nextRuntime.rescue,{challengeId:crypto.randomUUID(),generation:nextRuntime.rescue.generation+1})};\n    if(nextRuntime?.authored)nextRuntime={...nextRuntime,authored:restoreAuthoredRoom(nextRuntime.authored,currentRunRoom(state))};\n    if (payload.paused && !state.pauseReasons.length) timing.pauseSinceServerMs = serverNow;\r\n    if (!payload.paused && state.pauseReasons.length === 1 && timing.pauseSinceServerMs !== null) {\r\n      timing.pausedMs += Math.max(0, serverNow - timing.pauseSinceServerMs); timing.pauseSinceServerMs = null;\r\n    }\r\n  } else if (type === 'run_advance') {\r\n    const priorNode = supportedNode(state);\r\n    if (run.visit_state !== 'entered' || state.phase !== 'walk' || state.pauseReasons.length) fail('run_advance_not_ready', 409);\r\n    if (state.completion?.escaped && priorNode.type !== 'mainboss'&&priorNode.roomType!=='treasure_side_room') fail('run_advance_not_ready', 409);\n    try { nextState = advanceRunRoom(state); } catch { fail('run_advance_not_ready', 409); }\r\n    const node = currentRunRoom(nextState);\r\n    if (node && !nodeSupported(node)) fail('run_action_unsupported', 409);\r\n    // The reducer discovers/records into the run-local copy; mirror the same idempotent steps into the\r\n    // canonical campaign row so the next run carries them (same atomic batch, CAS on its revision).\r\n    const priorProgress = context.campaign?.progress ?? createRoomUnlockProgress();\r\n    let progress = priorProgress;\r\n    if (node?.type === 'full-game'&&node.doorId!=='quilt') progress = discoverRoom(progress, { roomId: node.doorId, receiptId: `discover:${run.run_id}:${node.doorId}` }).progress;\n    if (!node) progress = recordCompletedRun(progress, { runId: run.run_id, receiptId: `run:${run.run_id}` }).progress;\r\n    if (canonicalJson(progress) !== canonicalJson(priorProgress)) campaign = { progress };\r\n    nextRun.visit_index++;\r\n    nextRun.visit_id = node ? visitIdFor(run.run_id, nextRun.visit_index) : null;\r\n    nextRun.visit_state = 'pending'; nextRun.entered_visit_binding = null; nextRuntime = null;\r\n    nextRun.clock_epoch++; nextRun.last_frame_seq = -1; nextRun.last_frame_now = null;\r\n    // The page's performance clock is still anchored by this writer claim. A room\r\n    // handoff changes the logical clock epoch, not the performance-to-wall mapping.\r\n    nextRun.last_writer_server_ms = serverNow;\r\n    timing.segmentAdmittedServerMs = serverNow; timing.visitEnteredServerMs = null; timing.visitClaimedMs = serverNow;\r\n    timing.visitAcceptedMs = 0;\r\n    timing.pauseSinceServerMs = null;\r\n    if (!node) {\r\n      const receiptRows = await all(db, \"SELECT type,data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=?\", owner, epoch, run.run_id);\r\n      const cleared = state.rulesVersion===2?new Set(receiptRows.filter(row=>row.type==='level_cleared').map(row=>parse(row.data,'level receipt').levelIndex)).size===5:receiptRows.some(row => row.type === 'level_cleared');\r\n      if (nextState.phase !== 'complete' || (state.rulesVersion===2?priorNode.type!=='recovery'||priorNode.levelIndex!==5:priorNode.type !== 'mainboss' || Boolean(priorNode.returning) !== Boolean(state.escapeDetour)) ||\r\n          state.completion?.roomId !== priorNode.id || state.completion?.escaped || state.stats.health <= 0 || state.pauseReasons.length || !cleared)\r\n        fail('run_completion_not_earned', 409);\r\n      const acceptedQualifyingUnits = run.accepted_qualifying_units;\r\n      const hasTicket = Boolean(context.availableTicket);\r\n      const decision = ticketDecision({ run: { runId: run.run_id, status: 'completed', startedSeq: run.started_seq, acceptedQualifyingUnits },\r\n        lastRaidStartSeq, hasAvailableTicket: hasTicket }, { ticketMinUnits: RUN_RUNTIME_POLICY.ticketMinUnits, ticketCapacity: RUN_RUNTIME_POLICY.ticketCapacity });\r\n      const endSeq = context.nextSeq;\r\n      const ticketId = decision.eligible ? `ticket-${run.run_id}` : null;\r\n      const record = { version: 1, runId: run.run_id, eligible: decision.eligible, reason: decision.reason,\r\n        qualifyingUnits: acceptedQualifyingUnits, startedSeq: run.started_seq, lastRaidStartSeqSnapshot: lastRaidStartSeq,\r\n        hasAvailableTicketSnapshot: hasTicket, endSeq, ticketId };\r\n      nextState.ticketDecision = record; nextRun.ticket_decision = JSON.stringify(record);\r\n      receipts = [{ receiptId: `run-ended-${run.run_id}`, type: 'run_ended', visitId: visit, data: record }];\r\n      end = { status: 'completed' }; if (decision.eligible) ticket = { ticketId };\r\n    }\r\n  } else if (type === 'route_chosen') {\r\n    const anchor = supportedNode(state);\r\n    if (run.visit_state !== 'entered' || state.phase !== 'walk' || state.pauseReasons.length) fail('run_route_not_ready', 409);\r\n    const prior = (state.routes ?? []).find(route => route.afterId === anchor.id);\r\n    if (prior) {\r\n      if (prior.roomId !== payload.roomId) fail('run_route_conflict', 409);\r\n      return noop(context, 'route_unchanged');\r\n    }\r\n    if (payload.roomId !== null && !SUPPORTED_OPTIONAL_ROOMS.includes(payload.roomId) && payload.roomId !== 'treasure') fail('run_action_unsupported', 409);\r\n    // chooseRunRoute owns eligibility (walk phase, intro anchor, unlock policy) and rebuilds the schedule.\r\n    try { nextState = chooseRunRoute(state, payload.roomId); } catch { fail('run_route_rejected', 409); }\r\n  } else if (type === 'shop_action') {\r\n    const node = supportedNode(state);\r\n    if (node.roomType !== 'corgi_shop') fail('run_action_unsupported', 409);\r\n    if (run.visit_state !== 'entered') fail('run_visit_pending', 409);\r\n    if (state.phase !== 'active' || state.pauseReasons.length) fail('run_shop_not_ready', 409);\r\n    // Shop time only ever advances through run_frame; shopId is the current node, never the client's.\r\n    try { nextState = interactRunShop(state, payload.action); } catch { fail('run_reducer_rejected', 409); }\r\n    if (canonicalJson(nextState) === canonicalJson(state)) return noop(context, 'shop_action_ignored');\r\n    const done = await optionalRoomCompletion(owner, epoch, run, node, state, nextState);\r\n    if (done) { receipts = done.receipts; visits = done.visits; }\r\n  } else if (type === 'treasure_open') {\r\n    const node = supportedNode(state), binding = parse(run.entered_visit_binding, 'visit binding');\r\n    if (node.type !== 'bonus' || node.doorId !== 'treasure' || node.roomType !== 'treasure_side_room') fail('run_action_unsupported', 409);\r\n    if (run.visit_state !== 'entered' || !runtime?.treasure) fail('run_visit_pending', 409);\r\n    const chestAlreadyOpened = runtime.treasure.openedChests?.some(item => item.chestId === payload.chestId);\r\n    if ((!chestAlreadyOpened && state.phase !== 'active') || state.pauseReasons.length || state.stats.health <= 0) fail('run_treasure_not_ready', 409);\r\n    if (!binding || binding.visitId !== visit || binding.nodeId !== node.id || binding.owner !== owner || binding.epoch !== epoch ||\r\n        binding.manifestRevision !== 1 || !validTreasureManifest(runtime.treasure.chestManifest, binding.treasureBuildingId) ||\r\n        runtime.treasure.encounterId !== node.id) fail('run_visit_binding_invalid', 409);\r\n    const opened = openTreasureChest(runtime.treasure, { chestId: payload.chestId,\r\n      receiptId: `treasure-${(await fingerprint(['world-treasure-chest-v1', owner, epoch, run.run_id, visit, payload.chestId])).slice(0, 56)}` });\r\n    if (!opened.opened) {\r\n      if (opened.duplicate) return { ...noop(context, 'treasure_chest_already_opened'), response: { outcome: 'noop', reason: 'treasure_chest_already_opened' } };\r\n      fail('run_treasure_not_ready', 409);\r\n    }\r\n    nextRuntime = { ...runtime, treasure: opened.run };\r\n    try { nextState = resolveRunTreasure(state, nextRuntime.treasure); } catch { fail('run_treasure_not_ready', 409); }\n    if(nextState.heartModifiers){\n      const offer=binding.wearableOffer;\n      if(offer?.version!==1||!CHEST_WEARABLES.some(item=>item.id===offer.wearableId)||canonicalJson(offer)!==canonicalJson(runtime.treasureWearableOffer))fail('run_visit_binding_invalid',409);\n      nextState.heartModifiers=grantChestWearable(nextState.heartModifiers,{wearableId:offer.wearableId,receiptId:opened.chest.receiptId,boundary:'room-complete'}).state;\n    }\n    if (!context.base?.base) fail('run_base_missing', 409);\r\n    const grant = grantTreasureBlueprint({ base: context.base.base, accountId: owner, dataEpoch: epoch, runId: run.run_id,\r\n      visitId: visit, chestId: payload.chestId, rewards: opened.chest.rewards, now: serverNow });\r\n    if (!grant.ok) fail('run_treasure_grant_rejected', 409, grant.reason);\r\n    base = { base: grant.base, charge: context.base.charge };\r\n    const roomReceiptId = await receiptId(owner, epoch, run.run_id, visit, node.id);\r\n    receipts = [{ receiptId: roomReceiptId, type: 'room_completed', visitId: visit,\r\n      data: { roomId: node.id, xp: nextState.completion.xp, energyGrantedMs: 0,\r\n        acceptedQualifyingUnits: run.accepted_qualifying_units, outcome: 'treasure-opened', chestId: payload.chestId,\r\n        chestReceiptId: opened.chest.receiptId,\r\n        blueprintId: grant.blueprintId, buildingId: grant.buildingId, granted: grant.granted, rewardReason: grant.reason } }];\r\n    visits = [{ visitIndex: run.visit_index, visitId: visit, disposition: 'completed', receiptId: roomReceiptId }];\r\n  } else if (type === 'run_end') {\r\n    if (run.status !== 'active') fail('run_terminal');\r\n    const acceptedQualifyingUnits = run.accepted_qualifying_units;\r\n    const hasTicket = Boolean(context.availableTicket);\r\n    if (state.phase === 'complete') fail('run_terminal');\r\n    if (state.rulesVersion!==2&&await first(db, \"SELECT 1 AS ok FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND type='level_cleared' LIMIT 1\", owner, epoch, run.run_id))\r\n      fail('run_completion_pending', 409);\r\n    const endStatus = ['defeated','captured'].includes(state.phase) ? 'defeated' : 'abandoned';\n    if(nextState.heartModifiers){nextState.heartModifiers=expireRunWearables(nextState.heartModifiers);nextState.runCoins=0;if(nextState.shopLedger)nextState.shopLedger.coins=0;}\n    const decision = ticketDecision({ run: { runId: run.run_id, status: endStatus, startedSeq: run.started_seq, acceptedQualifyingUnits },\r\n      lastRaidStartSeq, hasAvailableTicket: hasTicket }, { ticketMinUnits: RUN_RUNTIME_POLICY.ticketMinUnits, ticketCapacity: RUN_RUNTIME_POLICY.ticketCapacity });\r\n    const endSeq = context.nextSeq;\r\n    const ticketId = decision.eligible ? `ticket-${run.run_id}` : null;\r\n    const record = { version: 1, runId: run.run_id, eligible: decision.eligible, reason: decision.reason,\r\n      qualifyingUnits: acceptedQualifyingUnits, startedSeq: run.started_seq, lastRaidStartSeqSnapshot: lastRaidStartSeq,\r\n      hasAvailableTicketSnapshot: hasTicket, endSeq, ticketId };\r\n    nextState.ticketDecision = record;\r\n    nextRun.ticket_decision = JSON.stringify(record);\r\n    receipts = [{ receiptId: `run-ended-${run.run_id}`, type: 'run_ended', visitId: visit, data: record }];\r\n    end = { status: endStatus };\r\n    if (decision.eligible) ticket = { ticketId };\r\n  }\r\n  return { outcome: 'applied', state: nextState, runtime: nextRuntime, runMeta: nextRun, timing, receipts, visits, ticket, end, base, campaign };\r\n}\r\n// Pond/shop completion: XP/heal are the reducer's; no energy and no qualifying unit (no workout happened here).\r\nasync function optionalRoomCompletion(owner, epoch, run, node, before, after) {\r\n  if (node.type !== 'full-game' || !after.completion || after.completion.roomId !== node.id || before.completion?.roomId === node.id) return null;\r\n  const visit = run.visit_id ?? visitIdFor(run.run_id, run.visit_index), receipt = await receiptId(owner, epoch, run.run_id, visit, node.id);\r\n  return {\r\n    receipts: [{ receiptId: receipt, type: 'room_completed', visitId: visit, data: { roomId: node.id, xp: after.completion.xp,\r\n      energyGrantedMs: 0, acceptedQualifyingUnits: run.accepted_qualifying_units, outcome: after.completion.outcome ?? null,\r\n      ...(after.completion.coins === undefined ? {} : { coins: after.completion.coins }), runCoins: after.runCoins } }],\r\n    visits: [{ visitIndex: run.visit_index, visitId: visit, disposition: 'completed', receiptId: receipt }],\r\n  };\r\n}\r\nfunction noop(context, reason) {\r\n  return { outcome: 'noop', state: stateFor(context.run), runtime: runtimeFor(context.run), runMeta: context.run,\r\n    timing: timingFor(context.run), receipts: [], visits: [], response: { outcome: 'noop', reason } };\r\n}\r\n\r\nasync function applyFrames(db, input, context, owner, epoch, now) {\n  const run = context.run, state = stateFor(run), runtime = runtimeFor(run);\r\n  const node = supportedNode(state); visitMatches(run, input.payload);\r\n  const bossNode = node.type === 'miniboss' || node.type === 'mainboss';\n  for(const kind of ['yoga','secret','altar'])if(input.payload.frames.some(frame=>frame[kind]!==undefined)&&authoredRoomKind(node)!==kind)fail('run_action_unsupported',409);\n  if(input.payload.frames.some(frame=>frame.rescue!==undefined)&&(!bossNode||state.schedule.scheduleVersion!==4||!runtime?.rescue))fail('run_action_unsupported',409);\n  if (!bossNode && input.payload.frames.some(frame => frame.boss !== undefined)) fail('run_action_unsupported', 409);\r\n  if (bossNode && input.payload.frames.some(frame => frame.boss !== undefined) && !runtime?.boss) fail('run_visit_pending', 409);\r\n  if (node.roomType !== 'healing_waters' && input.payload.frames.some(frame => frame.pond !== undefined)) fail('run_action_unsupported', 409);\r\n  if (node.roomType !== 'coin_chase' && input.payload.frames.some(frame => frame.grass !== undefined)) fail('run_action_unsupported', 409);\r\n  // Arcade controls belong to the room they were shaped for (clay, dance or jelly): a frame's shape must name this node's door.\r\n  if (input.payload.frames.some(frame => frame.arcade !== undefined && node.doorId !== arcadeShapeOf(frame.arcade))) fail('run_action_unsupported', 409);\r\n  if (node.doorId !== 'treasure' && input.payload.frames.some(frame => frame.treasure !== undefined)) fail('run_action_unsupported', 409);\r\n  if (run.visit_state !== 'entered' || !runtime) fail('run_visit_pending', 409);\r\n  if (node.doorId === 'treasure') {\r\n    const binding = parse(run.entered_visit_binding, 'visit binding');\r\n    if (!binding || binding.visitId !== (run.visit_id ?? visitIdFor(run.run_id, run.visit_index)) || binding.nodeId !== node.id ||\r\n        binding.owner !== owner || binding.epoch !== epoch || binding.manifestRevision !== 1 ||\r\n        !validTreasureManifest(runtime.treasure?.chestManifest, binding.treasureBuildingId) || runtime.treasure?.encounterId !== node.id)\r\n      fail('run_visit_binding_invalid', 409);\r\n    try { restoreTreasureChase(runtime.treasure); } catch { fail('run_runtime_invalid', 409); }\r\n  }\r\n  if (input.payload.clockEpoch !== run.clock_epoch) fail('run_clock_epoch_conflict', 409);\r\n  verifyWriterGeneration(run, input.writerGeneration);\r\n  let nextState = clone(state), nextRuntime = clone(runtime), lastSeq = run.last_frame_seq, lastNow = run.last_frame_now;\r\n  const timing = timingFor(run);\r\n  // Continuity credit is qualified per frame: each tracked, eligible frame earns the time since the previous accepted frame\r\n  // (bounded by the existing per-gap limit), so 32 valid 100 ms frames earn their ~3.2 s, not a 2 s batch ceiling.\r\n  const baselineNow = lastNow ?? serverClock(run, input.payload.frames[0].now);\r\n  let creditedFrameMs = 0, chip = null, treasureCaught = false;\r\n  for (const frame of input.payload.frames) {\r\n    if (frame.frameSeq <= lastSeq) fail('run_invalid_frame', 400);\r\n    const serverTime = serverClock(run, frame.now);\r\n    if (!finite(serverTime) || serverTime > now + RUN_RUNTIME_POLICY.clockSlackMs || (lastNow !== null && serverTime < lastNow)) fail('run_invalid_frame', 400, 'frame_clock');\r\n    if (frame.op === 'sample') {\r\n      const node = currentRunRoom(nextState), delta = Math.max(0, frame.value - nextState.lastValue);\r\n      if (delta > 0 && node.reps) {\r\n        const previousRep = nextRuntime.repLastNow ?? serverTime;\r\n        const permitted = Math.floor(Math.max(0, serverTime - previousRep) / RUN_RUNTIME_POLICY.minRepIntervalMs);\r\n        if (delta > permitted) fail('run_invalid_frame', 400, 'rep_delta');\r\n        if (delta) nextRuntime.repLastNow = previousRep + delta * RUN_RUNTIME_POLICY.minRepIntervalMs;\r\n      } else if (delta > 0) {\r\n        const counted = lastNow === null ? 0 : Math.max(0, serverTime - lastNow);\r\n        // Adding the epoch offset can quantize performance.now() deltas by ~0.0001ms.\r\n        // Allow 1µs of arithmetic noise only; exercise duration remains unchanged.\r\n        const deltaMs = delta * 1000;\r\n        if (deltaMs > counted + RUN_RUNTIME_POLICY.holdDeltaFloatToleranceMs)\r\n          fail('run_invalid_frame', 400, { reason: 'hold_delta', deltaMs, countedMs: counted,\r\n            toleranceMs: RUN_RUNTIME_POLICY.holdDeltaFloatToleranceMs });\r\n      }\r\n      if (frame.tracked && nextState.phase === 'active' && !nextState.pauseReasons.length) {\r\n        const paused = nextState.pauseReasons.length > 0 || !frame.tracked;\r\n        if (!paused && nextRuntime.lastQualifiedFrameNow !== undefined) {\r\n          const gap = serverTime - nextRuntime.lastQualifiedFrameNow;\r\n          if (gap > RUN_RUNTIME_POLICY.maxAcceptedWallGapMs) nextRuntime.lastQualifiedFrameNow = serverTime - RUN_RUNTIME_POLICY.maxAcceptedWallGapMs;\r\n        }\r\n      }\r\n    }\r\n    // A fresh physical punch while the chip is ALREADY open: finisher status, a released baseline (prev is never null/undefined),\r\n    // a false->true punch edge, and a tracked, active, alive, unpaused run. The blow that creates the finisher, a held punch,\r\n    // a punch right after a pause/reload (prev null) or an untracked/paused frame can never break it.\r\n    const preBoss = nextRuntime.boss;\r\n    const preEdge = state.schedule.scheduleVersion!==4&&bossNode && !chip && frame.op === 'sample' && frame.tracked === true && frame.boss !== undefined &&\n      preBoss?.status === 'finisher' && preBoss.prev !== null && typeof preBoss.prev === 'object' && !preBoss.pausedBy?.length &&\r\n      (preBoss.kind !== 'mainboss' || (preBoss.phase === 2 && preBoss.phase2Pending !== true)) &&\r\n      nextState.phase === 'active' && nextState.stats.health > 0 && nextState.pauseReasons.length === 0 &&\r\n      ((frame.boss.punchLeft === true && preBoss.prev.punchLeft === false) || (frame.boss.punchRight === true && preBoss.prev.punchRight === false));\r\n    const pauseForNonTrackingReason = nextState.pauseReasons.some(reason => reason !== 'tracking');\r\n    const sourceTs=frame.sourceTs===undefined?undefined:serverClock(run,frame.sourceTs);\r\n    const serverFresh=state.rulesVersion!==2||(sourceTs!==undefined&&now-sourceTs>=0&&now-sourceTs<=300);\r\n    const pureFrame = { ...frame, now: serverTime,...(sourceTs!==undefined?{sourceTs}:{}),...(!serverFresh?{tracked:false}:{}), ...(pauseForNonTrackingReason ? { tracked: false } : {}) };\r\n    const priorPaused = nextState.pauseReasons.length > 0;\r\n    const creditEligibleBefore = nextState.phase === 'active' && nextState.pauseReasons.length === 0;\r\n    const observedDelta = lastNow === null ? 0 : Math.max(0, serverTime - lastNow);\r\n    const treasureWasChasing=nextRuntime.treasure?.status==='chasing';\n    let stepped;\n    try { stepped = stepFrame({ runState: nextState, runtime: nextRuntime, frame: pureFrame }); }\r\n    catch { fail('run_reducer_rejected', 409); }\r\n    nextState = stepped.runState; nextRuntime = stepped.runtime;\n    if(treasureWasChasing&&nextRuntime.treasure?.status==='caught'&&nextState.phase==='walk')treasureCaught=true;\n    if (nextRuntime.treasure?.status === 'caught' && nextState.phase === 'active' && nextState.stats.health > 0 && !nextState.pauseReasons.length) {\r\n      try { nextState = exitCaughtRunTreasure(nextState, nextRuntime.treasure); } catch { fail('run_treasure_exit_rejected', 409); }\r\n      treasureCaught = true;\r\n    }\r\n    {\r\n      const reducerCredit = stepped.creditMs ?? 0;\r\n      const gapCredit = (node.roomType!=='coin_chase'||frame.grass?.controlValid===true&&frame.grass?.exerciseEligible===true) && state.rulesVersion!==2 && frame.op === 'sample' && frame.tracked && creditEligibleBefore ? Math.max(0, observedDelta - reducerCredit) : 0;\r\n      creditedFrameMs += Math.min(RUN_RUNTIME_POLICY.maxAcceptedWallGapMs, reducerCredit + gapCredit);\r\n    }\r\n    const paused = nextState.pauseReasons.length > 0;\r\n    if (!priorPaused && paused) timing.pauseSinceServerMs = serverTime;\r\n    else if (priorPaused && !paused && timing.pauseSinceServerMs !== null) {\r\n      timing.pausedMs += Math.max(0, serverTime - timing.pauseSinceServerMs);\r\n      timing.pauseSinceServerMs = null;\r\n    }\r\n    lastSeq = frame.frameSeq; lastNow = serverTime;\n    if(state.schedule.scheduleVersion===4&&!chip&&stepped.transitions.some(t=>t.type==='rescue-accepted')&&nextState.phase==='active'&&!nextState.reaperStarted){\n      chip=await prepareRescueFinish(db,input,context,owner,epoch,now,{state:nextState,runtime:nextRuntime,timing});\n      nextState=chip.state;nextRuntime=chip.runtime;\n    }\n    if (preEdge && nextState.phase === 'active' && nextState.stats.health > 0 && nextState.pauseReasons.length === 0 &&\r\n        nextRuntime.boss?.status === 'finisher' && !nextRuntime.boss.pausedBy?.length) {\r\n      // The remaining frames of the batch still run (and are still validated) on the finished state.\r\n      chip = await chipFinishAtFrame(db, input, context, owner, epoch, now, { frame, serverTime, state: nextState, runtime: nextRuntime, timing });\r\n      if (chip) { nextState = chip.state; nextRuntime = chip.runtime; }\r\n    }\r\n  }\r\n  const nextRun = { ...run, last_frame_seq: lastSeq, last_frame_now: lastNow, last_writer_server_ms: now };\r\n  const eligibleWall = input.payload.frames.some(frame => frame.op === 'sample' && frame.tracked) &&\r\n    input.payload.frames.every(frame => frame.tracked&&(node.roomType!=='coin_chase'||frame.grass?.controlValid===true&&frame.grass?.exerciseEligible===true)) && state.phase === 'active' && state.pauseReasons.length === 0;\r\n  // Accepted movement is CUMULATIVE and can never outrun the unpaused wall of this visit at request entry, however late or\r\n  // bunched the requests that carry the frames are: budget = (now - visit entry - paused time) - already accepted. A lagging\r\n  // ACK that later drains a valid continuous backlog is therefore still paid in full, a batch cannot claim time the server saw\r\n  // pass while paused, and nothing before the visit was entered is ever credited. The batch is also bounded by its own frame span.\r\n  const pausedNowMs = nextState.pauseReasons.length && timing.pauseSinceServerMs !== null ? Math.max(0, now - timing.pauseSinceServerMs) : 0;\r\n  const visitWallBudgetMs = timing.visitEnteredServerMs === null ? now - run.last_writer_server_ms\r\n    : Math.max(0, now - timing.visitEnteredServerMs - (timing.pausedMs - (timing.visitPausedBaseMs ?? 0)) - pausedNowMs - timing.visitAcceptedMs);\r\n  const acceptedWallMs = eligibleWall ? Math.max(0, Math.min(creditedFrameMs, lastNow - baselineNow, visitWallBudgetMs)) : 0;\r\n  if (eligibleWall) { timing.unpausedAcceptedMs += acceptedWallMs; timing.visitAcceptedMs += acceptedWallMs; }\r\n  const receipts = [], visits = [];\r\n  let base, campaign;\r\n  if (chip) {\r\n    receipts.push(...chip.receipts); visits.push(...chip.visits);\r\n    base = chip.base; campaign = chip.campaign;\r\n    nextRun.accepted_qualifying_units = chip.runMeta.accepted_qualifying_units;\r\n    nextRun.energy_granted_ms = chip.runMeta.energy_granted_ms;\r\n    nextRun.run_granted_ms = chip.runMeta.run_granted_ms;\r\n  }\r\n  const completed = nextState.phase === 'upgrade' && state.phase !== 'upgrade' && nextState.completion?.roomId;\r\n  if (nextState.completion?.escaped && !state.completion?.escaped) {\r\n    visits.push({ visitIndex: run.visit_index, visitId: run.visit_id ?? visitIdFor(run.run_id, run.visit_index), disposition: 'escaped' });\r\n  }\r\n  if (treasureCaught) visits.push({ visitIndex: run.visit_index, visitId: run.visit_id ?? visitIdFor(run.run_id, run.visit_index), disposition: 'escaped' });\r\n  const priorCompletion = state.completion?.roomId === completed;\r\n  if (completed && !priorCompletion) {\r\n    const node = currentRunRoom(state);\r\n    const goalMs = node.reps ? Math.max(0, (node.reps - 1) * RUN_RUNTIME_POLICY.minRepIntervalMs) : roomTarget(node) * 1000;\r\n    if (timing.visitAcceptedMs < goalMs) fail('run_elapsed_too_short', 409, { requiredMs: goalMs, acceptedMs: timing.visitAcceptedMs });\r\n    const visit = run.visit_id ?? visitIdFor(run.run_id, run.visit_index), roomReceipt = await receiptId(owner, epoch, run.run_id, visit, completed);\r\n    const award = await acceptRoomEnergy(context, run, now, roomReceipt), amount = award.amount;\r\n    base = award.base;\r\n    nextRun.accepted_qualifying_units++;\r\n    nextRun.energy_granted_ms += amount;\r\n    nextRun.run_granted_ms += amount;\r\n    nextState.acceptedQualifyingUnits = nextRun.accepted_qualifying_units;\r\n    nextState.energyGrantedMs = nextRun.energy_granted_ms;\r\n    receipts.push({ receiptId: roomReceipt, type: 'room_completed', visitId: visit,\r\n      data: { roomId: completed, xp: nextState.completion.xp, energyGrantedMs: amount, acceptedQualifyingUnits: nextRun.accepted_qualifying_units } });\r\n    visits.push({ visitIndex: run.visit_index, visitId: visit, disposition: 'completed', receiptId: roomReceipt });\r\n  }\r\n  const optional = await optionalRoomCompletion(owner, epoch, run, node, state, nextState);\r\n  if (optional) {\r\n    // Grass and Jelly are movement: they qualify like a warmup once the server has accepted a full unit of\r\n    // credit, once, at the terminal completion. Pond/shop (and a short clear) complete with their XP/coins but no\r\n    // energy and no ticket unit; reload, pause and death never repeat or create a unit.\r\n    const data = optional.receipts[0].data;\r\n    if (MOVEMENT_ROOM_TYPES.includes(node.roomType) && timing.visitAcceptedMs >= RUN_RUNTIME_POLICY.energyPerUnitMs) {\r\n      const award = await acceptRoomEnergy(context, run, now, optional.receipts[0].receiptId);\r\n      base = award.base;\r\n      nextRun.accepted_qualifying_units++;\r\n      nextRun.energy_granted_ms += award.amount;\r\n      nextRun.run_granted_ms += award.amount;\r\n      nextState.acceptedQualifyingUnits = nextRun.accepted_qualifying_units;\r\n      nextState.energyGrantedMs = nextRun.energy_granted_ms;\r\n      data.energyGrantedMs = award.amount;\r\n      data.acceptedQualifyingUnits = nextRun.accepted_qualifying_units;\r\n    }\r\n    receipts.push(...optional.receipts); visits.push(...optional.visits);\r\n  }\r\n  const responseValue = { outcome: 'applied', completedRoomId: completed || optional?.receipts[0].data.roomId || null,\r\n    energyGrantedMs: receipts.find(receipt => receipt.type === 'room_completed')?.data?.energyGrantedMs ?? 0,\r\n    acceptedQualifyingUnits: nextRun.accepted_qualifying_units, ...(chip ? { bossFinish: chip.response } : {}) };\r\n  let end;\r\n  if(nextState.reaperStarted){end={status:'completed'};receipts.push({receiptId:`rest-reaper-${run.run_id}`,type:'run_ended',visitId:run.visit_id,data:{reason:'rest-reaper',activeRunMs:nextState.activeRunMs,death:false}});}\r\n  return { outcome: 'applied', state: nextState, runtime: nextRuntime, runMeta: nextRun, timing, receipts, visits, ...(end?{end}:{}),\r\n    base, campaign, response: responseValue, ...(chip ? { evidence: chip.evidence, guards: chip.guards,writes:chip.writes } : {}) };\n}\r\n// The atomic finisher: the very same boss_finish pipeline (freshness window, pin, rewards, campaign, level-clear evidence),\r\n// run on the state this accepted frame just produced and at the request-entry server time. Only an edge frame that is not\r\n// fresh against that time (older than the unchanged boss_finish ACK window, or ahead of the server clock) is skipped: the\r\n// frames still commit and legacy boss_finish stays available. Everything prepareBossFinish then rejects (binding, pin,\r\n// schedule, incomplete clear path, elapsed time, CAS) propagates and fails the whole event closed, exactly like boss_finish.\r\nasync function chipFinishAtFrame(db, input, context, owner, epoch, now, { frame, serverTime, state, runtime, timing }) {\r\n  const ageMs = now - serverTime;\r\n  if (ageMs < 0 || ageMs > RUN_RUNTIME_POLICY.bossFinishAckWindowMs) return null;\r\n  const projected = { ...context.run, state: canonicalJson(state), runtime: canonicalJson(runtime), last_frame_seq: frame.frameSeq,\r\n    last_frame_now: serverTime, last_writer_server_ms: now, run_timing: canonicalJson(timing) };\r\n  return prepareBossFinish(db, { payload: { visitId: input.payload.visitId } }, { run: projected, base: context.base, campaign: context.campaign },\r\n    owner, epoch, now);\r\n}\r\n\r\n// One accepted qualifying unit's energy: settle the trip bank to `now`, size the grant, journal it by receipt.\r\nasync function acceptRoomEnergy(context, run, now, receipt) {\r\n  const energyModule = await import('../modules/coach-world/harvest-adapter.mjs');\r\n  if (typeof energyModule.advanceRtsEconomy !== 'function' || typeof energyModule.awardAcceptedRunEnergy !== 'function' || typeof energyModule.harvestEnergySnapshot !== 'function') fail('run_energy_helper_unavailable', 500);\r\n  const baseState = context.base?.base;\r\n  if (!baseState) fail('run_base_missing', 409);\r\n  // Settle the canonical trip bank through the exact award time before sizing\r\n  // the grant: workers may have consumed banked time since the stored snapshot.\r\n  const settledBase = energyModule.advanceRtsEconomy(baseState, now);\r\n  const energy = energyModule.harvestEnergySnapshot(settledBase);\r\n  const amount = energyGrantMs({ runGrantedMs: run.run_granted_ms, bankAvailableMs: energy.bankOperatingMs }, RUN_RUNTIME_POLICY);\r\n  const awardedBase = amount === 0 ? settledBase : energyModule.awardAcceptedRunEnergy(settledBase, { receiptId: receipt, ms: amount, now });\r\n  return { amount, base: { base: awardedBase, charge: context.base.charge } };\r\n}\r\n\r\nfunction bossSlot(node) {\n  if(node.bossSlot)return node.bossSlot;\r\n  if (node.id === 'intro-miniboss') return 'mini1';\r\n  if (node.id === 'intro-miniboss-2') return 'mini2';\r\n  if (node.id === 'intro-mainboss') return 'main';\r\n  fail('run_action_unsupported', 409);\r\n}\nasync function checkedRescueManifest(db,owner,epoch,run,state){\n const pin=await readRescueManifest(db,owner,epoch,run.run_id);\n if(canonicalJson(pin.manifest)!==canonicalJson(state.schedule.manifest))fail('run_visit_binding_invalid',409);\n return pin;\n}\n// Only the accepted ordered referee edge calls this. It shares the existing event/base CAS batch.\nasync function prepareRescueFinish(db,input,context,owner,epoch,now,{state,runtime,timing}){\n const run=context.run,node=currentRunRoom(state),visit=run.visit_id,binding=parse(run.entered_visit_binding,'visit binding');\n const pin=await checkedRescueManifest(db,owner,epoch,run,state),level=pin.manifest.levels[node.levelIndex-1],encounter=level.encounters.find(e=>e.nodeId===node.id);\n const rescue=runtime.rescue;\n if(!encounter||!rescue||rescue.status!=='accepted'||rescue.receipt||rescue.encounterId!==node.id||rescue.canonicalCoachId!==encounter.canonicalCoachId||binding?.nodeId!==node.id||binding?.visitId!==visit||binding?.canonicalCoachId!==encounter.canonicalCoachId||binding?.bossVisualId!==encounter.sourceVisualId||runtime.boss.coachId!==encounter.sourceVisualId||runtime.boss.seed!==encounter.patternSeed)fail('run_rescue_binding_invalid',409);\n const finished=finishBossChip(state,runtime.boss);\n if(!finished.applied)fail('run_boss_finisher_rejected',409,finished.reason);\n const bossReceipt=`boss-${await fingerprint(['rescue-six-v1',owner,epoch,run.run_id,pin.manifestHash,node.levelIndex,node.roleId,visit,encounter.canonicalCoachId,rescue.challengeId,rescue.generation,rescue.lastObservationId])}`;\n const rescued=await readRescuedCoachIds(db,owner,epoch),heart=heartForCoach(encounter.canonicalCoachId);\n const firstRescue=rescue.rescueKind==='first'&&!!heart&&!rescued.includes(encounter.canonicalCoachId);\n const guards=[assertion(db,'EXISTS(SELECT 1 FROM world_run_rescue_manifests WHERE owner=? AND epoch=? AND run_id=? AND manifest_hash=? AND manifest=?)',[owner,epoch,run.run_id,pin.manifestHash,canonicalJson(pin.manifest)])],writes=[];\n if(firstRescue){\n  guards.push(assertion(db,'NOT EXISTS(SELECT 1 FROM world_coach_rescues WHERE owner=? AND epoch=? AND canonical_coach_id=?)',[owner,epoch,encounter.canonicalCoachId]));\n  writes.push(stmt(db,'INSERT INTO world_coach_rescues(owner,epoch,canonical_coach_id,boss_receipt_id,run_id,encounter_id,policy_id,rescued_at) VALUES(?,?,?,?,?,?,?,?)',owner,epoch,encounter.canonicalCoachId,bossReceipt,run.run_id,node.id,'rescue-six-v1',now));\n  // Legacy unlock projection requires a non-null source column; immediate rescues bind it to this boss receipt.\n  writes.push(stmt(db,\"INSERT INTO world_coach_unlocks(owner,epoch,coach_id,run_id,encounter_id,boss_receipt_id,level_receipt_id,status,granted_seq,reveal_order,granted_at,revealed_at) VALUES(?,?,?,?,?,?,?,'pending_reveal',?,?,?,NULL) ON CONFLICT(owner,epoch,coach_id) DO NOTHING\",owner,epoch,encounter.canonicalCoachId,run.run_id,node.id,bossReceipt,bossReceipt,run.started_seq,node.levelIndex*6+level.encounters.indexOf(encounter),now));\n  rescued.push(encounter.canonicalCoachId);\n }\n const result={bossReceiptId:bossReceipt,coachId:encounter.canonicalCoachId,heartId:firstRescue?heart.heartId:null,status:firstRescue?'first':'repeat'};\n finished.state.hearts=projectCoachHearts(rescued,finished.state.heartModifiers?.heartId??null);\n const data={policyId:'rescue-six-v1',manifestHash:pin.manifestHash,levelIndex:node.levelIndex,slot:node.roleId,bossId:node.id,canonicalCoachId:encounter.canonicalCoachId,sourceVisualId:encounter.sourceVisualId,phase:finished.boss.phase,returning:node.returning===true,unlockReceipt:finished.boss.unlockReceipt,rescue:result};\n const receipts=[{receiptId:bossReceipt,type:'boss_defeated',visitId:visit,data}];\n if(node.roleId==='pair-3-main'){\n  const prior=await all(db,\"SELECT receipt_id,data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND type='boss_defeated'\",owner,epoch,run.run_id);\n  const facts=prior.map(r=>({receiptId:r.receipt_id,data:parse(r.data,'boss receipt')})).concat([{receiptId:bossReceipt,data}]);\n  const bosses=level.encounters.map(e=>facts.find(f=>f.data.policyId==='rescue-six-v1'&&f.data.manifestHash===pin.manifestHash&&f.data.levelIndex===node.levelIndex&&f.data.slot===e.roleId&&f.data.bossId===e.nodeId&&f.data.canonicalCoachId===e.canonicalCoachId&&f.data.sourceVisualId===e.sourceVisualId&&f.data.phase===(e.kind==='mainboss'?2:1)));\n  const required=rescueLevelRequirements(pin.manifest,node.levelIndex).requiredRoomIds;\n  const rooms=await all(db,\"SELECT receipt_id,data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND type='room_completed'\",owner,epoch,run.run_id);\n  const roomFacts=rooms.map(row=>({receiptId:row.receipt_id,data:parse(row.data,'room receipt')}));\n  if(bosses.some(b=>!b)||new Set(bosses.map(b=>b.receiptId)).size!==6||required.some(id=>!roomFacts.some(r=>r.data.roomId===id)))fail('run_completion_not_earned',409);\n  const ledger=await readBossProgression(db,owner,epoch),root=await readBossRunPin(db,owner,epoch,run.run_id);\n  if(ledger.clearCount!==root.issuedClearOrdinal+node.levelIndex-1||ledger.revision!==root.ledgerRevision+node.levelIndex-1)fail('boss_clear_stale_ordinal',409);\n  const evidence={policyId:'rescue-six-v1',manifestHash:pin.manifestHash,levelIndex:node.levelIndex,bossReceiptIds:bosses.map(b=>b.receiptId),roomReceiptIds:required.map(id=>roomFacts.find(r=>r.data.roomId===id).receiptId)};\n  const fp=await fingerprint([owner,epoch,run.run_id,evidence]),levelReceipt=`level-${fp}`;\n  receipts.push({receiptId:levelReceipt,type:'level_cleared',visitId:visit,data:{...evidence,clearCount:ledger.clearCount+1}});\n  guards.push(assertion(db,'EXISTS(SELECT 1 FROM world_boss_progression WHERE owner=? AND epoch=? AND clear_count=? AND revision=?)',[owner,epoch,ledger.clearCount,ledger.revision]));\n  for(const id of [...evidence.bossReceiptIds.filter(id=>id!==bossReceipt),...evidence.roomReceiptIds])guards.push(assertion(db,'EXISTS(SELECT 1 FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND receipt_id=?)',[owner,epoch,run.run_id,id]));\n  writes.push(stmt(db,'INSERT INTO world_run_rescue_level_receipts(owner,epoch,run_id,level,level_receipt_id,fingerprint,result,accepted_at) VALUES(?,?,?,?,?,?,?,?)',owner,epoch,run.run_id,node.levelIndex,levelReceipt,fp,canonicalJson(evidence),now),stmt(db,'UPDATE world_boss_progression SET clear_count=clear_count+1,revision=revision+1,updated_at=? WHERE owner=? AND epoch=? AND revision=?',now,owner,epoch,ledger.revision));\n }\n const win={kind:node.type,encounterId:`${run.run_id}:${node.id}`,receiptId:bossReceipt};\n const campaign={progress:recordBossVictory(context.campaign?.progress??createRoomUnlockProgress(),win).progress};\n finished.state.campaign=recordBossVictory(finished.state.campaign,win).progress;\n const roomReceipt=await receiptId(owner,epoch,run.run_id,visit,node.id),energy=await acceptRoomEnergy(context,run,now,roomReceipt);\n const runMeta={...run,accepted_qualifying_units:run.accepted_qualifying_units+1,energy_granted_ms:run.energy_granted_ms+energy.amount,run_granted_ms:run.run_granted_ms+energy.amount};\n finished.state.acceptedQualifyingUnits=runMeta.accepted_qualifying_units;finished.state.energyGrantedMs=runMeta.energy_granted_ms;\n receipts.push({receiptId:roomReceipt,type:'room_completed',visitId:visit,data:{roomId:node.id,xp:finished.state.completion.xp,energyGrantedMs:energy.amount,acceptedQualifyingUnits:runMeta.accepted_qualifying_units}});\n return {state:finished.state,runtime:{...runtime,boss:finished.boss,rescue:{...rescue,receipt:result}},runMeta,timing,receipts,visits:[{visitIndex:run.visit_index,visitId:visit,disposition:'completed',receiptId:roomReceipt}],base:energy.base,campaign,guards,writes,response:{type:'rescue',...result}};\n}\nasync function stableBossReceiptId(owner, epoch, runId, pin, slot, node) {\n  return `boss-${(await fingerprint(['world-boss-receipt-v1', owner, epoch, runId, pin.assignmentId, slot, node.id])).slice(0, 64)}`;\r\n}\r\nasync function stableLevelReceiptId(owner, epoch, runId, pin) {\r\n  return `level-${(await fingerprint(['world-level-clear-v1', owner, epoch, runId, pin.assignmentId])).slice(0, 64)}`;\r\n}\r\nasync function priorBossFacts(db, owner, epoch, runId, pin, state) {\r\n  const rows = await all(db, \"SELECT receipt_id,data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND type='boss_defeated'\", owner, epoch, runId);\r\n  const assignment = pin.assignment;\r\n  const found = {};\r\n  for (const row of rows) {\r\n    const data = parse(row.data, 'boss receipt');\r\n    if (data.assignmentId !== pin.assignmentId || data.issuedClearOrdinal !== pin.issuedClearOrdinal) continue;\r\n    if (['mini1', 'mini2'].includes(data.slot) && data.bossId === `${pin.levelIndex?`level-${pin.levelIndex}-`:''}${data.slot === 'mini1' ? 'intro-miniboss' : 'intro-miniboss-2'}` &&\r\n        data.phase === 1 && data.returning === false && data.canonicalCoachId === assignment.slots[data.slot].canonicalCoachId &&\r\n        data.sourceVisualId === assignment.slots[data.slot].sourceVisualId &&\r\n        state.warmups.introBossReceipts.some(fact => fact.bossId === data.bossId && fact.receiptId === data.unlockReceipt)) found[data.slot] = row.receipt_id;\r\n  }\r\n  return found;\r\n}\r\nasync function prepareBossFinish(db, input, context, owner, epoch, now) {\n  const run = context.run, state = stateFor(run), runtime = runtimeFor(run), payload = input.payload;\n  if(state.schedule.scheduleVersion===4)fail('run_rescue_frames_required',409);\n  const node = supportedNode(state); visitMatches(run, payload);\r\n  if (!['miniboss', 'mainboss'].includes(node.type)) fail('run_action_unsupported', 409);\r\n  if (run.visit_state !== 'entered' || !runtime?.boss) fail('run_visit_pending', 409);\r\n  const visit = run.visit_id ?? visitIdFor(run.run_id, run.visit_index);\r\n  const binding = parse(run.entered_visit_binding, 'visit binding');\r\n  if (!binding || binding.visitId !== visit || binding.nodeId !== node.id || binding.bossVisualId !== node.sourceVisualId || binding.canonicalCoachId !== node.canonicalCoachId)\r\n    fail('run_visit_binding_invalid', 409);\r\n  const nowMapped = serverClock(run, now - run.clock_offset_ms);\r\n  const mappingErrorMs = Math.abs(nowMapped - now);\r\n  let finished;\r\n  try { finished = finishBossChip(state, runtime.boss); } catch { fail('run_boss_finisher_rejected', 409); }\r\n  if (!finished?.applied) fail('run_boss_finisher_required', 409, finished?.reason);\r\n  const pin = await readBossRunPin(db, owner, epoch, run.run_id,state.schedule.scheduleVersion===3?node.levelIndex:undefined);\r\n  if (!validBossPin(state, pin))\r\n    fail('world_run_schedule_unsupported', 409);\r\n  const slot = bossSlot(node), expected = pin.assignment.slots[slot];\r\n  if (node.canonicalCoachId !== expected.canonicalCoachId || node.sourceVisualId !== expected.sourceVisualId || runtime.boss.coachId !== expected.sourceVisualId)\r\n    fail('run_visit_binding_invalid', 409);\r\n  // `nowMapped` is a round trip through the fractional browser/server clock offset.\r\n  // At epoch scale it can differ from the integer request time by sub-microseconds.\r\n  // Freshness still comes from the committed frame and server write timestamps.\r\n  const frameAgeMs = now - run.last_frame_now, writerAgeMs = now - run.last_writer_server_ms, bossAgeMs = now - runtime.boss.now;\r\n  if (state.stats.health <= 0 || state.pauseReasons.length || state.phase !== 'active' || !Number.isFinite(nowMapped) ||\r\n      mappingErrorMs > CLOCK_ROUNDTRIP_TOLERANCE_MS || !finite(run.last_frame_now) || !Number.isSafeInteger(run.last_writer_server_ms) ||\r\n      frameAgeMs < 0 || writerAgeMs < 0 || bossAgeMs < 0 ||\r\n      Math.max(frameAgeMs, writerAgeMs, bossAgeMs) > RUN_RUNTIME_POLICY.bossFinishAckWindowMs || runtime.boss.pausedBy?.length)\r\n    fail('run_boss_finisher_rejected', 409);\r\n  const bossReceipt = await stableBossReceiptId(owner, epoch, run.run_id, pin, slot, node);\r\n  const data = { slot, bossId: node.id, canonicalCoachId: expected.canonicalCoachId, sourceVisualId: expected.sourceVisualId,\r\n    assignmentId: pin.assignmentId, issuedClearOrdinal: pin.issuedClearOrdinal, phase: finished.boss.phase,\r\n    returning: node.returning === true, unlockReceipt: finished.boss.unlockReceipt };\r\n  const receipts = [{ receiptId: bossReceipt, type: 'boss_defeated', visitId: visit, data }];\r\n  let evidence = null;\r\n  const recoveryPath = Boolean(state.escapeDetour);\r\n  const levelPrefix=state.rulesVersion===2?`level-${node.levelIndex}-`:'';\r\n  const clearPath = node.id === levelPrefix+'intro-mainboss' && Boolean(node.returning) === recoveryPath && finished.boss.phase === 2 &&\r\n    finished.boss.levelPassed === true && finished.boss.phase2Pending !== true &&\r\n    finished.state.phase === 'walk' && finished.state.completion?.roomId === node.id && !finished.state.completion?.escaped &&\r\n    Array.from({ length: 15 }, (_, i) => `${levelPrefix}intro-${i + 1}`).every(id => finished.state.warmups.resolvedReceipts.includes(id)) &&\r\n    (!recoveryPath || Array.from({ length: 5 }, (_, i) => `${levelPrefix}recovery-${i + 1}`).every(id => finished.state.warmups.resolvedReceipts.includes(id)));\r\n  if (clearPath) {\r\n    const prior = await priorBossFacts(db, owner, epoch, run.run_id, pin, finished.state);\r\n    if (!prior.mini1 || !prior.mini2) fail('run_completion_not_earned', 409);\r\n    const roomRows = await all(db, \"SELECT data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND type='room_completed'\", owner, epoch, run.run_id);\r\n    const acceptedRooms = new Set(roomRows.map(row => parse(row.data, 'room receipt').roomId));\r\n    const requiredRooms = [...Array.from({ length: 15 }, (_, i) => `${levelPrefix}intro-${i + 1}`),\r\n      ...(recoveryPath ? Array.from({ length: 5 }, (_, i) => `${levelPrefix}recovery-${i + 1}`) : [])];\r\n    if (requiredRooms.some(roomId => !acceptedRooms.has(roomId))) fail('run_completion_not_earned', 409);\r\n    const mainReceipt = bossReceipt;\r\n    const levelReceipt = await stableLevelReceiptId(owner, epoch, run.run_id, pin);\r\n    receipts.push({ receiptId: levelReceipt, type: 'level_cleared', visitId: visit, data: { assignmentId: pin.assignmentId,\r\n      issuedClearOrdinal: pin.issuedClearOrdinal, mainBossReceiptId: mainReceipt, ...(state.rulesVersion===2?{levelIndex:node.levelIndex}:{}) } });\r\n    evidence = { ...(state.rulesVersion===2?{levelIndex:node.levelIndex}:{}),levelReceiptId: levelReceipt, bosses: [{ slot: 'mini1', bossReceiptId: prior.mini1 },\r\n      { slot: 'mini2', bossReceiptId: prior.mini2 }, { slot: 'main', bossReceiptId: mainReceipt }] };\r\n  }\r\n  // A verified miniboss kill (or the real phase-two main clear) is a campaign milestone that unlocks optional\r\n  // rooms; a phase-one main escape never reaches here as a victory. Coach unlocks stay with the level clear.\r\n  let campaign;\r\n  if (node.type === 'miniboss' || clearPath) {\r\n    const win = { kind: node.type, encounterId: `${run.run_id}:${node.id}`, receiptId: bossReceipt };\r\n    const priorProgress = context.campaign?.progress ?? createRoomUnlockProgress();\r\n    const progress = recordBossVictory(priorProgress, win).progress;\r\n    if (canonicalJson(progress) !== canonicalJson(priorProgress)) campaign = { progress };\r\n    finished.state.campaign = recordBossVictory(finished.state.campaign, win).progress;\r\n  }\r\n  const timing = timingFor(run);\r\n  if (timing.visitAcceptedMs <= 0) fail('run_elapsed_too_short', 409);\r\n  const nextRuntime = { ...runtime, boss: finished.boss };\r\n  const nextRun = { ...run };\r\n  const roomReceiptId = await receiptId(owner, epoch, run.run_id, visit, node.id);\r\n  const energyModule = await import('../modules/coach-world/harvest-adapter.mjs');\r\n  if (typeof energyModule.advanceRtsEconomy !== 'function' || typeof energyModule.awardAcceptedRunEnergy !== 'function' || typeof energyModule.harvestEnergySnapshot !== 'function') fail('run_energy_helper_unavailable', 500);\r\n  const baseState = context.base?.base;\r\n  if (!baseState) fail('run_base_missing', 409);\r\n  const settledBase = energyModule.advanceRtsEconomy(baseState, now);\r\n  const available = energyModule.harvestEnergySnapshot(settledBase);\r\n  const amount = energyGrantMs({ runGrantedMs: run.run_granted_ms, bankAvailableMs: available.bankOperatingMs }, RUN_RUNTIME_POLICY);\r\n  const awardedBase = amount === 0 ? settledBase\r\n    : energyModule.awardAcceptedRunEnergy(settledBase, { receiptId: roomReceiptId, ms: amount, now });\r\n  const base = { base: awardedBase, charge: context.base.charge };\r\n  nextRun.accepted_qualifying_units++;\r\n  nextRun.energy_granted_ms += amount;\r\n  nextRun.run_granted_ms += amount;\r\n  finished.state.acceptedQualifyingUnits = nextRun.accepted_qualifying_units;\r\n  finished.state.energyGrantedMs = nextRun.energy_granted_ms;\r\n  receipts.push({ receiptId: roomReceiptId, type: 'room_completed', visitId: visit,\r\n    data: { roomId: node.id, xp: finished.state.completion.xp, energyGrantedMs: amount, acceptedQualifyingUnits: nextRun.accepted_qualifying_units } });\r\n  const visits = [{ visitIndex: run.visit_index, visitId: visit, disposition: 'completed', receiptId: roomReceiptId }];\r\n  return { outcome: 'applied', state: finished.state, runtime: nextRuntime, runMeta: nextRun, timing, receipts, visits, base, campaign,\r\n    response: { type: 'boss_finish', bossId: node.id, phase: finished.boss.phase, status: finished.boss.status, clear: Boolean(evidence) },\r\n    evidence, guards: [bossPinGuard(db, owner, epoch, run.run_id, pin)] };\r\n}\r\n\r\n// Permanent room rewards. An accepted event that appended XP awards derives Gym Credits + level packs from the\r\n// validated XP delta (helper checks same run, append-only). The wallet rides the same base CAS as the energy award;\r\n// pack rows ride the same atomic batch. Any helper failure fails closed before anything is written.\r\nfunction roomRewards(db, ctx, applied, owner, now) {\r\n  if (applied.outcome !== 'applied') return null;\r\n  const before = stateFor(ctx.run).xp, after = applied.state.xp;\r\n  if (!before || !after || after.awards.length === before.awards.length) return null;\r\n  const baseState = applied.base?.base ?? ctx.base?.base;\r\n  if (!baseState) fail('run_base_missing', 409);\r\n  const charge = applied.base?.charge ?? ctx.base.charge;\r\n  const result = applyRunRoomRewards({ base: baseState, accountId: owner, runId: ctx.run.run_id, runSeed: applied.state.seed,\r\n    beforeXp: before, afterXp: after, now });\r\n  if (!result.ok) fail('run_reward_failed', 500, result.reason);\r\n  const known = new Map((applied.state.confirmedRewardPacks ?? []).map(pack => [pack.id, pack]));\r\n  for (const pack of result.packs) known.set(pack.packId, { id: pack.packId, tier: pack.tier, level: pack.level });\r\n  // Audit each room's receipt with what it paid; a pack belongs to the award whose XP crossed its level.\r\n  let xp = before.xp;\r\n  const perLevel = result.xp.perLevel;\r\n  for (const award of result.credits.awards) {\r\n    const crossed = result.packs.filter(pack => (pack.level - 1) * perLevel > xp && (pack.level - 1) * perLevel <= xp + award.xp).map(pack => pack.packId);\r\n    xp += award.xp;\r\n    const receipt = applied.receipts.find(item => item.type === 'room_completed' && item.data?.roomId === award.roomId);\r\n    if (receipt) receipt.data.rewards = { credits: award.amount, awarded: award.awarded, packs: crossed };\r\n  }\r\n  const packs = result.packs.map(pack => ({ id: pack.packId, tier: pack.tier, level: pack.level }));\r\n  return {\r\n    state: { ...applied.state, confirmedRewardPacks: [...known.values()] },\r\n    base: { base: result.base, charge },\r\n    summary: { creditsAwarded: result.credits.awarded, balance: result.balance, packs },\r\n    writes: result.packs.map(pack => stmt(db, \"INSERT INTO account_pack_entitlements(user_id,pack_id,status,granted_at) VALUES(?,?,'owned',?) ON CONFLICT(user_id,pack_id) DO NOTHING\",\r\n      pack.entitlement.userId, pack.entitlement.packId, pack.entitlement.grantedAt)),\r\n  };\r\n}\r\n\r\nfunction extraRuntimeWrite(db, previousRun, nextRun, state, runtime, timing, owner, epoch, revision) {\r\n  return stmt(db, `UPDATE world_runs SET writer_generation=?,clock_epoch=?,visit_index=?,visit_id=?,visit_state=?,entered_visit_binding=?,runtime=?,last_frame_seq=?,last_frame_now=?,clock_offset_ms=?,last_writer_server_ms=?,run_timing=?,accepted_qualifying_units=?,energy_granted_ms=?,run_granted_ms=?,ticket_decision=? WHERE owner=? AND epoch=? AND run_id=? AND revision=?`,\r\n    nextRun.writer_generation, nextRun.clock_epoch, nextRun.visit_index, nextRun.visit_id,\r\n    nextRun.visit_state, nextRun.entered_visit_binding, runtime == null ? null : canonicalJson(runtime),\r\n    nextRun.last_frame_seq, nextRun.last_frame_now, nextRun.clock_offset_ms, nextRun.last_writer_server_ms,\r\n    canonicalJson(timing), nextRun.accepted_qualifying_units, nextRun.energy_granted_ms, nextRun.run_granted_ms,\r\n    nextRun.ticket_decision, owner, epoch, previousRun.run_id, revision);\r\n}\r\nfunction projectedRun(run, state, runtime, meta, clientSeq, revision) {\r\n  return { ...run, ...meta, revision, next_client_seq: clientSeq + 1, state: canonicalJson(state), runtime: runtime == null ? null : canonicalJson(runtime) };\r\n}\r\nfunction eventResponseFor(owner, epoch, run, state, runtime, now, outcome, detail, receipts = [], base = null) {\r\n  return response(owner, epoch, run, state, runtime, now, outcome, detail, receipts, base);\r\n}\r\n\r\nasync function eventApply(db, owner, epoch, input, now, raidsEnabled=false) {\r\n  const apply = async ctx => {\r\n      if (ctx.run.writer_generation === 0) fail('writer_claim_required', 409);\r\n      verifyWriterGeneration(ctx.run, input.writerGeneration);\r\n      if (ctx.run.clock_epoch !== input.clockEpoch) fail('run_clock_epoch_conflict', 409);\r\n      let lastRaidStartSeq = null, raidSequenceGuard = [];\r\n      if ((input.type === 'run_end' || input.type === 'run_advance') && raidsEnabled) {\r\n        const latest = await first(db,'SELECT MAX(started_seq) AS seq FROM world_raids WHERE owner=? AND epoch=?',owner,epoch);\r\n        lastRaidStartSeq = latest?.seq ?? null;\r\n        raidSequenceGuard = [assertion(db,'EXISTS(SELECT 1 FROM world_sequence WHERE owner=? AND epoch=? AND next=?)',[owner,epoch,ctx.nextSeq])];\r\n      }\r\n      const applied = input.type === 'run_frame' ? await applyFrames(db, input, ctx, owner, epoch, now)\r\n        : input.type === 'boss_finish' ? await prepareBossFinish(db, input, ctx, owner, epoch, now)\r\n          : await applySimpleAction(db, input.type, input.payload, ctx, owner, epoch, now,lastRaidStartSeq);\r\n      if(input.type==='run_frame'&&applied.end&&applied.state.reaperStarted){\r\n        if(raidsEnabled){\r\n          const latest=await first(db,'SELECT MAX(started_seq) AS seq FROM world_raids WHERE owner=? AND epoch=?',owner,epoch);\r\n          lastRaidStartSeq=latest?.seq??null;\r\n          raidSequenceGuard=[assertion(db,'EXISTS(SELECT 1 FROM world_sequence WHERE owner=? AND epoch=? AND next=?)',[owner,epoch,ctx.nextSeq])];\r\n        }\r\n        const acceptedQualifyingUnits=applied.runMeta.accepted_qualifying_units,hasTicket=Boolean(ctx.availableTicket);\r\n        const decision=ticketDecision({run:{runId:ctx.run.run_id,status:'completed',startedSeq:ctx.run.started_seq,acceptedQualifyingUnits},lastRaidStartSeq,hasAvailableTicket:hasTicket},\r\n          {ticketMinUnits:RUN_RUNTIME_POLICY.ticketMinUnits,ticketCapacity:RUN_RUNTIME_POLICY.ticketCapacity});\r\n        const ticketId=decision.eligible?`ticket-${ctx.run.run_id}`:null;\r\n        const record={version:1,runId:ctx.run.run_id,eligible:decision.eligible,reason:decision.reason,qualifyingUnits:acceptedQualifyingUnits,\r\n          startedSeq:ctx.run.started_seq,lastRaidStartSeqSnapshot:lastRaidStartSeq,hasAvailableTicketSnapshot:hasTicket,endSeq:ctx.nextSeq,ticketId};\r\n        applied.state.ticketDecision=record;applied.runMeta.ticket_decision=JSON.stringify(record);\r\n        Object.assign(applied.receipts.find(receipt=>receipt.type==='run_ended').data,{ticketDecision:record});\r\n        if(ticketId)applied.ticket={ticketId};\r\n      }\r\n      const reward = roomRewards(db, ctx, applied, owner, now);\r\n      if (reward) {\r\n        applied.state = reward.state; applied.base = reward.base;\r\n        applied.response = { ...(applied.response ?? { type: input.type }), rewards: reward.summary };\r\n      }\r\n      if(applied.outcome==='applied'&&applied.state.rulesVersion===2){\r\n        const baseState=applied.base?.base??ctx.base?.base;\r\n        if(baseState){const earned=awardRunProficiency(baseState,applied.state,applied.receipts);if(earned.awarded){applied.base={base:earned.base,charge:applied.base?.charge??ctx.base.charge};applied.state.proficiency=earned.units;}}\r\n      }\r\n      const state = applied.state, runtime = applied.runtime;\r\n      const clearState = { ...state };\r\n      const meta = { ...applied.runMeta, last_writer_server_ms: now, ...(applied.end ? { status: applied.end.status } : {}) };\r\n      const projected = projectedRun(ctx.run, clearState, runtime, meta, input.clientSeq, ctx.run.revision + 1);\r\n      const responseBase = applied.base?.base ?? ctx.base?.base ?? null;\r\n      const envelopeResponse = eventResponseFor(owner, epoch, projected, clearState, runtime, now, applied.outcome,\r\n        applied.response ?? { type: input.type }, applied.receipts, responseBase);\r\n      const writes = applied.outcome === 'noop' ? [] : [extraRuntimeWrite(db, ctx.run, meta, clearState, runtime, applied.timing, owner, epoch, projected.revision), ...(reward?.writes ?? []),...(applied.writes??[])];\n      return { outcome: applied.outcome, state: clearState, response: envelopeResponse,\r\n        receipts: applied.receipts, visits: applied.visits, ...(applied.end ? { end: applied.end } : {}),\r\n        ...(applied.ticket ? { ticket: applied.ticket } : {}), ...(applied.base ? { base: applied.base } : {}),\r\n        ...(applied.evidence ? { clear: true } : {}), ...(applied.campaign ? { campaign: applied.campaign } : {}), guards:[...raidSequenceGuard, ...(applied.guards ?? [])], writes };\r\n  };\r\n  let result;\r\n  // boss gestures can now break the chip inside the accepted frame, so those frames share boss_finish's clear owner.\r\n  const bossFrames = input.type === 'run_frame' && input.payload.frames.some(frame => frame.boss !== undefined);\r\n  const savedForPolicy=await readRun(db,owner,epoch,input.runId);\n  if(!savedForPolicy)fail('run_not_found',409);\n  const bossOwned = stateFor(savedForPolicy).schedule.scheduleVersion!==4&&(input.type === 'boss_finish' || bossFrames);\n  if (bossOwned) {\r\n    const savedRun=await readRun(db,owner,epoch,input.runId),savedState=stateFor(savedRun);\r\n    const levelIndex=savedState.rulesVersion===2?currentRunRoom(savedState)?.levelIndex:undefined;\r\n    result = await appendBossRunEventAtomic(db, { owner, epoch, runId: input.runId, clientSeq: input.clientSeq,\r\n      requestId: input.id, type: input.type, hash: input.hash, now, writerGeneration: input.writerGeneration,levelIndex,\r\n      verifyAcceptedClear: async ({ run, pin }) => {\r\n        if (!bossOwned || !pin) return null;\r\n        // Only a batch with a punch while the boss can still be (or become) breakable needs the clear preview.\r\n        if (bossFrames && (!['fighting', 'finisher'].includes(runtimeFor(run)?.boss?.status) || !input.payload.frames.some(frame => frame.boss?.punchLeft || frame.boss?.punchRight))) return null;\r\n        const parsed = { ...input, runId: input.runId };\r\n        const previewContext = { run, base: await readBase(db, owner, epoch) };\r\n        try {\r\n          const preview = bossFrames ? await applyFrames(db, parsed, previewContext, owner, epoch, now) : await prepareBossFinish(db, parsed, previewContext, owner, epoch, now);\r\n          return preview.evidence ?? null;\r\n        } catch (error) { if(error?.code?.startsWith('game_generation_'))throw error;return null; }\n      }, apply });\r\n  } else {\r\n    result = await appendRunEventAtomic(db, { owner, epoch, runId: input.runId, clientSeq: input.clientSeq,\r\n      requestId: input.id, type: input.type, hash: input.hash, now, writerGeneration: input.writerGeneration, apply });\r\n  }\r\n  if (bossOwned && !result.response?.envelope) {\r\n    // P3's immutable clear result is separately attached, while the client ACK always\r\n    // receives the committed C runtime/state envelope from the transaction just run.\r\n    await assertEpoch(db, owner, epoch);\r\n    const journal = await readEventReplay(db, { owner, epoch, runId: input.runId, clientSeq: input.clientSeq });\r\n    if (journal?.hash === input.hash) {\r\n      const saved = parse(journal.response, 'boss finish journal response');\r\n      if (saved?.envelope) return { ...result, response: saved };\r\n    }\r\n    const committed = await readRun(db, owner, epoch, input.runId);\r\n    if (!committed) fail('run_not_found', 404);\r\n    const committedReceipts = await all(db, 'SELECT receipt_id,type,visit_id,data FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? AND client_seq=? ORDER BY receipt_id',\r\n      owner, epoch, input.runId, input.clientSeq);\r\n      result = { ...result, response: eventResponseFor(owner, epoch, committed, stateFor(committed), runtimeFor(committed), now,\r\n      result.outcome, { type: input.type, bossProgression: result.response }, committedReceipts.map(row => ({ receiptId: row.receipt_id,\r\n        type: row.type, visitId: row.visit_id, data: parse(row.data, 'receipt data') })), (await readBase(db, owner, epoch))?.base ?? null) };\r\n  }\r\n  return result;\r\n}\r\n\r\nasync function claim(db, owner, epoch, runId, request, now) {\r\n  await assertEpoch(db, owner, epoch);\r\n  const scopedId = `wrclaim:${await sha([owner, epoch, runId, request.id])}`;\r\n  const requestHash = await sha(['writer-claim-v1', owner, epoch, runId, request.expectedWriterGeneration, request.expectedRunRevision, request.now]);\r\n  for (let attempt = 0; attempt < 3; attempt++) {\r\n    await assertEpoch(db, owner, epoch);\r\n    const replay = await readStartReplay(db, { owner, epoch, requestId: scopedId });\r\n    if (replay) {\r\n      if (replay.hash !== requestHash) fail('run_idempotency_conflict', 409);\r\n      return { replayed: true, response: parseFingerprint(replay) };\r\n    }\r\n    const run = await readRun(db, owner, epoch, runId);\r\n    if (!run) fail('run_not_found', 404);\r\n    if (run.status !== 'active') return { replayed: false, response: response(owner, epoch, run, stateFor(run), runtimeFor(run), now, 'terminal', undefined, [], (await readBase(db, owner, epoch))?.base ?? null) };\r\n    if (run.writer_generation !== request.expectedWriterGeneration) fail('writer_claim_superseded', 409, { currentGeneration: run.writer_generation, runRevision: run.revision });\r\n    if (run.revision !== request.expectedRunRevision) fail('writer_claim_revision_mismatch', 409, { runRevision: run.revision });\r\n    const state = normalizeRestorableState(stateFor(run)), oldRuntime = runtimeFor(run), timing = timingFor(run);\r\n    const clientNow = request.now, offset = now - clientNow, nextGen = run.writer_generation + 1, nextClock = run.clock_epoch + 1;\r\n    let resumed;\r\n    if (run.visit_state === 'entered') {\r\n      if (!oldRuntime) fail('run_internal_fault', 500, 'entered visit missing runtime');\r\n      resumed = resumePage({ runState: state, runtime: oldRuntime, now, accountId: owner, dataEpoch: epoch });\r\n    } else resumed = resumePending({ runState: state, accountId: owner, dataEpoch: epoch });\r\n    const nextState = resumed.runState, nextRuntime = resumed.runtime;\r\n    if (run.writer_generation >= Number.MAX_SAFE_INTEGER || run.clock_epoch >= Number.MAX_SAFE_INTEGER || run.revision >= Number.MAX_SAFE_INTEGER) fail('run_revision_overflow', 409);\r\n    if (nextRuntime) nextRuntime.repLastNow = now;\r\n    const next = { ...run, writer_generation: nextGen, clock_epoch: nextClock, last_frame_seq: -1,\r\n      last_frame_now: null, clock_offset_ms: offset, last_writer_server_ms: now };\r\n    const projected = { ...run, ...next, revision: run.revision + 1, state: canonicalJson(nextState), runtime: nextRuntime == null ? null : canonicalJson(nextRuntime) };\r\n    const responseValue = response(owner, epoch, projected, nextState, nextRuntime, now, 'claimed',\r\n      { kind: 'writer-claim', claimedGeneration: nextGen, previousGeneration: run.writer_generation }, [], (await readBase(db, owner, epoch))?.base ?? null);\r\n    // A reload clears transient pauses (tracking/background/...): close their interval into pausedMs so it is never budgeted as unpaused wall.\r\n    const claimTiming = { ...timing, visitClaimedMs: now };\r\n    if (claimTiming.pauseSinceServerMs !== null && !nextState.pauseReasons.length) {\r\n      claimTiming.pausedMs += Math.max(0, now - claimTiming.pauseSinceServerMs); claimTiming.pauseSinceServerMs = null;\r\n    }\r\n    const claimRow = stmt(db, 'INSERT INTO world_run_writer_claims(owner,epoch,run_id,generation,request_id,request_hash,at_revision,next_client_seq_at_claim,claimed_at) VALUES(?,?,?,?,?,?,?,?,?)',\r\n      owner, epoch, runId, nextGen, scopedId, requestHash, projected.revision, run.next_client_seq, now);\r\n    const projection = stmt(db, 'UPDATE world_runs SET writer_generation=?,clock_epoch=?,last_frame_seq=-1,last_frame_now=NULL,clock_offset_ms=?,last_writer_server_ms=?,run_timing=?,runtime=?,visit_state=?,entered_visit_binding=? WHERE owner=? AND epoch=? AND run_id=? AND revision=?',\r\n      nextGen, nextClock, offset, now, canonicalJson(claimTiming), nextRuntime == null ? null : canonicalJson(nextRuntime),\r\n      run.visit_state, run.entered_visit_binding, owner, epoch, runId, projected.revision);\r\n    try {\r\n      await db.batch([epochGuard(db, owner, epoch), runGuard(db, { owner, epoch, runId, revision: run.revision, nextClientSeq: run.next_client_seq }),\r\n        assertion(db, \"EXISTS(SELECT 1 FROM world_runs WHERE owner=? AND epoch=? AND run_id=? AND status='active' AND writer_generation=? AND revision=?)\", [owner, epoch, runId, request.expectedWriterGeneration, run.revision]),\r\n        ...updateRunStatements(db, { owner, epoch, runId, fromRevision: run.revision, state: nextState, nextClientSeq: run.next_client_seq, now }),\r\n        projection, claimRow, startRequestStatement(db, { owner, epoch, requestId: scopedId, hash: requestHash, response: responseValue }), clearAssertions(db)]);\r\n      return { replayed: false, response: responseValue };\r\n    } catch (error) {\n      if(error?.code?.startsWith('game_generation_'))throw error;\n      await assertEpoch(db, owner, epoch);\r\n      const winner = await readStartReplay(db, { owner, epoch, requestId: scopedId });\r\n      if (winner) { if (winner.hash !== requestHash) fail('run_idempotency_conflict'); return { replayed: true, response: parseFingerprint(winner) }; }\r\n      const current = await readRun(db, owner, epoch, runId);\r\n      if (!current || current.writer_generation !== request.expectedWriterGeneration) fail('writer_claim_superseded', 409, { currentGeneration: current?.writer_generation, runRevision: current?.revision });\r\n      if (attempt === 2) fail('run_retry', 409);\r\n    }\r\n  }\r\n}\r\n\r\nfunction classifyStatusRow(row, owner, epoch, runId) {\r\n  const entry = { writerGeneration: row.query_generation, clientSeq: row.query_seq, eventHash: row.query_hash };\r\n  const echo = { targetAccountId: owner, dataEpoch: epoch, runId, queriedGeneration: entry.writerGeneration,\r\n    clientSeq: entry.clientSeq, eventHash: entry.eventHash, currentGeneration: row.current_generation ?? null };\r\n  if (row.current_epoch !== epoch) return { ...echo, outcome: 'run_epoch_mismatch' };\r\n  if (row.run_status === null) return { ...echo, outcome: 'run_not_found' };\r\n  if (entry.writerGeneration > row.current_generation) fail('run_generation_future', 400);\r\n  if (row.row_generation === entry.writerGeneration) {\r\n    if (row.row_hash === entry.eventHash) return { ...echo, outcome: 'ACCEPTED_MATCHING', response: parse(row.row_response, 'journal response') };\r\n    return { ...echo, outcome: 'CONFLICT_OCCUPIED' };\r\n  }\r\n  if (row.fence_generation !== null && row.fence_generation !== undefined) {\r\n    if (entry.clientSeq >= row.fence_watermark) return { ...echo, outcome: 'FINAL_FENCED_UNACCEPTED', proof: {\r\n      generation: row.fence_generation, at_revision: row.fence_at_revision, next_client_seq_at_claim: row.fence_watermark, claimed_at: row.fence_claimed_at } };\r\n    return { ...echo, outcome: 'CONFLICT_OCCUPIED' };\r\n  }\r\n  if (entry.writerGeneration === row.current_generation && row.run_status !== 'active') {\r\n    if (entry.clientSeq >= row.run_next_seq) return { ...echo, outcome: 'FINAL_TERMINAL_UNACCEPTED', proof: {\r\n      runStatus: row.run_status, nextClientSeqAtEnd: row.run_next_seq, runRevision: row.run_revision } };\r\n    return { ...echo, outcome: 'CONFLICT_OCCUPIED' };\r\n  }\r\n  if (entry.writerGeneration === row.current_generation && row.run_status === 'active') {\r\n    if (entry.clientSeq >= row.run_next_seq) return { ...echo, outcome: 'PROVISIONAL_CURRENT_UNACCEPTED' };\r\n    return { ...echo, outcome: 'CONFLICT_OCCUPIED' };\r\n  }\r\n  fail('run_internal_fault', 500, 'generation advanced without matching claim');\r\n}\r\nasync function joinedStatus(db, owner, epoch, runId, entries) {\r\n  const rows = await all(db, `WITH wanted AS (\r\n    SELECT CAST(key AS INTEGER) AS idx,\r\n      CAST(json_extract(value,'$.writerGeneration') AS INTEGER) AS query_generation,\r\n      CAST(json_extract(value,'$.clientSeq') AS INTEGER) AS query_seq,\r\n      json_extract(value,'$.eventHash') AS query_hash\r\n    FROM json_each(?)\r\n  )\r\n  SELECT wanted.idx,wanted.query_generation,wanted.query_seq,wanted.query_hash,a.epoch AS current_epoch,\r\n    r.status AS run_status,r.writer_generation AS current_generation,r.next_client_seq AS run_next_seq,r.revision AS run_revision,\r\n    c.generation AS fence_generation,c.next_client_seq_at_claim AS fence_watermark,c.at_revision AS fence_at_revision,c.claimed_at AS fence_claimed_at,\r\n    j.writer_generation AS row_generation,j.hash AS row_hash,j.response AS row_response\r\n  FROM wanted LEFT JOIN account_data_epochs a ON a.owner_id=?\r\n  LEFT JOIN world_runs r ON r.owner=a.owner_id AND r.epoch=? AND r.run_id=?\r\n  LEFT JOIN world_run_writer_claims c ON c.owner=r.owner AND c.epoch=r.epoch AND c.run_id=r.run_id AND c.generation=wanted.query_generation+1\r\n  LEFT JOIN world_run_events j ON j.owner=r.owner AND j.epoch=r.epoch AND j.run_id=r.run_id AND j.client_seq=wanted.query_seq\r\n  ORDER BY wanted.idx`, JSON.stringify(entries), owner, epoch, runId);\r\n  if (rows.length !== entries.length) fail('run_internal_fault', 500, 'status query omitted entry');\r\n  return rows.map(row => classifyStatusRow(row, owner, epoch, runId));\r\n}\r\n\r\nexport async function worldRunRuntimeApi(request, db, owner, epoch, path, method, body, now, {raidsEnabled=false}={}) {\r\n  const parsed = validateAction(path, method, body, request);\r\n  if (!safe(epoch) || epoch < 1 || !text(owner) || !safe(now)) fail('run_storage_invalid', 400, 'runtime context');\r\n  const runId = parsed.runId;\r\n  if (parsed.action === 'claim') return claim(db, owner, epoch, runId, parsed.body, now);\r\n  if (parsed.action === 'status') {\r\n    await assertEpoch(db, owner, epoch);\r\n    const entries = await joinedStatus(db, owner, epoch, runId, parsed.body.entries);\r\n    await assertEpoch(db, owner, epoch);\r\n    return { targetAccountId: owner, dataEpoch: epoch, runId, entries };\r\n  }\r\n  const bodyValue = parsed.body;\r\n  await assertEpoch(db, owner, epoch);\r\n  const hash = await eventHash(bodyValue);\r\n  const replay = await readEventReplay(db, { owner, epoch, runId, clientSeq: bodyValue.clientSeq });\r\n  if (replay && replay.hash !== hash) {\r\n    await assertEpoch(db, owner, epoch);\r\n    fail('run_event_conflict', 409);\r\n  }\r\n  await assertEpoch(db, owner, epoch);\r\n  const result = await eventApply(db, owner, epoch, { ...bodyValue, runId, hash }, now,raidsEnabled);\r\n  await assertEpoch(db, owner, epoch);\r\n  return result;\r\n}\r\n","server/world-run-storage-schema.sql":"﻿CREATE TABLE world_sequence(owner TEXT NOT NULL,epoch INTEGER NOT NULL,next INTEGER NOT NULL CHECK(next>=1),PRIMARY KEY(owner,epoch));\r\n--> statement-breakpoint\r\nCREATE TABLE world_runs(owner TEXT NOT NULL,epoch INTEGER NOT NULL,run_id TEXT NOT NULL,status TEXT NOT NULL CHECK(status IN('active','completed','defeated','abandoned')),revision INTEGER NOT NULL CHECK(revision>=0),started_seq INTEGER NOT NULL,end_seq INTEGER,next_client_seq INTEGER NOT NULL CHECK(next_client_seq>=1),state TEXT NOT NULL,started_at INTEGER NOT NULL,last_event_at INTEGER NOT NULL,ended_at INTEGER,PRIMARY KEY(owner,epoch,run_id),UNIQUE(owner,epoch,started_seq),CHECK((status='active' AND end_seq IS NULL AND ended_at IS NULL) OR (status<>'active' AND end_seq IS NOT NULL AND ended_at IS NOT NULL)));\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX world_runs_one_active ON world_runs(owner,epoch) WHERE status='active';\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX world_runs_end_seq ON world_runs(owner,epoch,end_seq) WHERE end_seq IS NOT NULL;\r\n--> statement-breakpoint\r\nCREATE TABLE world_run_events(owner TEXT NOT NULL,epoch INTEGER NOT NULL,run_id TEXT NOT NULL,client_seq INTEGER NOT NULL,request_id TEXT NOT NULL,type TEXT NOT NULL,hash TEXT NOT NULL,outcome TEXT NOT NULL CHECK(outcome IN('applied','noop')),response TEXT NOT NULL,accepted_at INTEGER NOT NULL,PRIMARY KEY(owner,epoch,run_id,client_seq));\r\n--> statement-breakpoint\r\nCREATE TABLE world_run_visits(owner TEXT NOT NULL,epoch INTEGER NOT NULL,run_id TEXT NOT NULL,visit_index INTEGER NOT NULL,visit_id TEXT NOT NULL,disposition TEXT NOT NULL CHECK(disposition IN('completed','escaped')),receipt_id TEXT,client_seq INTEGER NOT NULL,accepted_at INTEGER NOT NULL,PRIMARY KEY(owner,epoch,run_id,visit_index),UNIQUE(owner,epoch,run_id,visit_id));\r\n--> statement-breakpoint\r\nCREATE TABLE world_run_receipts(owner TEXT NOT NULL,epoch INTEGER NOT NULL,receipt_id TEXT NOT NULL,run_id TEXT NOT NULL,type TEXT NOT NULL CHECK(type IN('room_completed','boss_defeated','level_cleared','run_ended')),visit_id TEXT NOT NULL,data TEXT NOT NULL,client_seq INTEGER NOT NULL,accepted_at INTEGER NOT NULL,verification TEXT NOT NULL CHECK(verification='game-policy-v1'),PRIMARY KEY(owner,epoch,receipt_id),UNIQUE(owner,epoch,run_id,visit_id,type));\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX world_run_receipts_one_clear ON world_run_receipts(owner,epoch,run_id,COALESCE(json_extract(data,'$.levelIndex'),1)) WHERE type='level_cleared';\r\n--> statement-breakpoint\r\nCREATE TABLE world_raid_tickets(owner TEXT NOT NULL,epoch INTEGER NOT NULL,ticket_id TEXT NOT NULL,source_run_id TEXT NOT NULL,issued_seq INTEGER NOT NULL,status TEXT NOT NULL CHECK(status IN('available','consumed')),consumed_raid_id TEXT,consumed_seq INTEGER,issued_at INTEGER NOT NULL,consumed_at INTEGER,PRIMARY KEY(owner,epoch,ticket_id),UNIQUE(owner,epoch,source_run_id));\r\n--> statement-breakpoint\r\nCREATE UNIQUE INDEX world_raid_tickets_capacity1 ON world_raid_tickets(owner,epoch) WHERE status='available';\r\n--> statement-breakpoint\r\nCREATE TABLE world_coach_unlocks(owner TEXT NOT NULL,epoch INTEGER NOT NULL,coach_id TEXT NOT NULL,run_id TEXT NOT NULL,encounter_id TEXT NOT NULL,boss_receipt_id TEXT NOT NULL,level_receipt_id TEXT NOT NULL,status TEXT NOT NULL CHECK(status IN('pending_reveal','revealed')),granted_seq INTEGER NOT NULL,reveal_order INTEGER NOT NULL,granted_at INTEGER NOT NULL,revealed_at INTEGER,PRIMARY KEY(owner,epoch,coach_id));\r\n--> statement-breakpoint\r\nCREATE TABLE world_run_campaign(owner TEXT NOT NULL,epoch INTEGER NOT NULL,progress TEXT NOT NULL,revision INTEGER NOT NULL CHECK(revision>=0),PRIMARY KEY(owner,epoch));\r\n","server/world-run-storage.mjs":"﻿export const RUN_STORAGE_VERSION = 1;\nexport const RUN_STORAGE_TABLES = Object.freeze(['world_sequence','world_runs','world_run_events','world_run_visits','world_run_receipts','world_raid_tickets','world_coach_unlocks','world_run_campaign','world_run_writer_claims']);\nconst uuid = () => crypto.randomUUID();\nconst stmt = (db, sql, ...args) => db.prepare(sql).bind(...args);\nconst first = (db, sql, ...args) => stmt(db, sql, ...args).first();\nconst all = async (db, sql, ...args) => (await stmt(db, sql, ...args).all()).results;\nconst fail = (code, status = code === 'run_storage_invalid' ? 400 : 409, details) => { throw new RunStorageError(code, status, details); };\nconst safeInt = (v, min = 0) => Number.isSafeInteger(v) && v >= min;\nconst nonempty = v => typeof v === 'string' && v.length > 0;\n\nexport class RunStorageError extends Error {\n  constructor(code, status = 409, details) { super(code); this.name = 'RunStorageError'; this.code = code; this.status = status; if (details !== undefined) this.details = details; }\n}\nfunction invalid(condition, detail) { if (condition) fail('run_storage_invalid', 400, detail); }\nfunction validateOwnerEpoch(owner, epoch) { invalid(!nonempty(owner), 'owner'); invalid(!safeInt(epoch, 1), 'epoch'); }\nfunction validateNow(now) { invalid(!safeInt(now), 'now'); }\nfunction validateAttempts(maxAttempts) { invalid(!safeInt(maxAttempts, 1) || maxAttempts > 3, 'maxAttempts'); }\nfunction parseJson(text, field) { try { return JSON.parse(text); } catch { fail('run_storage_invalid', 400, field); } }\n\n/** Stable JSON: recursively sorted object keys, insertion order preserved for arrays. */\nexport function canonicalJson(value) {\n  const seen = new Set();\n  function visit(input, path) {\n    if (input === null || typeof input === 'string' || typeof input === 'boolean') return input;\n    if (typeof input === 'number') { if (!Number.isFinite(input)) fail('run_storage_invalid', 400, `non-finite:${path}`); return input; }\n    if (typeof input !== 'object') fail('run_storage_invalid', 400, `unsupported:${path}`);\n    if (seen.has(input)) fail('run_storage_invalid', 400, `cycle:${path}`);\n    seen.add(input);\n    let result;\n    if (Array.isArray(input)) result = Array.from(input, (v, i) => visit(v, `${path}[${i}]`));\n    else {\n      const proto = Object.getPrototypeOf(input);\n      if (proto !== Object.prototype && proto !== null) fail('run_storage_invalid', 400, `object-type:${path}`);\n      if (Object.getOwnPropertySymbols(input).length) fail('run_storage_invalid', 400, `symbol-key:${path}`);\n      // A null prototype keeps JSON keys such as \"__proto__\" as ordinary own data.\n      result = Object.create(null);\n      for (const key of Object.keys(input).sort()) {\n        const child = input[key];\n        if (child === undefined || typeof child === 'function' || typeof child === 'symbol') fail('run_storage_invalid', 400, `unsupported:${path}.${key}`);\n        result[key] = visit(child, `${path}.${key}`);\n      }\n    }\n    seen.delete(input);\n    return result;\n  }\n  try { return JSON.stringify(visit(value, '$')); }\n  catch (error) { if (error instanceof RunStorageError) throw error; fail('run_storage_invalid', 400, 'canonical_json'); }\n}\nexport async function fingerprint(value) {\n  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonicalJson(value)));\n  return [...new Uint8Array(digest)].map(x => x.toString(16).padStart(2, '0')).join('');\n}\n\nconst assertion = (db, condition, args = []) => stmt(db, `INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`, uuid(), ...args);\nexport const clearAssertions = db => stmt(db, 'DELETE FROM world_assertions');\nexport function epochGuard(db, owner, epoch) { validateOwnerEpoch(owner, epoch); return assertion(db, 'EXISTS(SELECT 1 FROM account_data_epochs WHERE owner_id=? AND epoch=?)', [owner, epoch]); }\nexport function baseGuard(db, { owner, epoch, revision }) { validateOwnerEpoch(owner, epoch); invalid(!safeInt(revision), 'base revision'); return assertion(db, 'EXISTS(SELECT 1 FROM world_bases WHERE owner=? AND epoch=? AND revision=?)', [owner, epoch, revision]); }\nexport function runGuard(db, { owner, epoch, runId, revision, nextClientSeq, writerGeneration }) {\n  validateOwnerEpoch(owner, epoch); invalid(!nonempty(runId) || !safeInt(revision) || !safeInt(nextClientSeq, 1), 'run guard');\n  if (writerGeneration !== undefined) invalid(!safeInt(writerGeneration), 'writer generation');\n  const writerClause = writerGeneration === undefined ? '' : ' AND writer_generation=?';\n  const writerArgs = writerGeneration === undefined ? [] : [writerGeneration];\n  return assertion(db, `EXISTS(SELECT 1 FROM world_runs WHERE owner=? AND epoch=? AND run_id=? AND status='active' AND revision=? AND next_client_seq=?${writerClause})`, [owner, epoch, runId, revision, nextClientSeq, ...writerArgs]);\n}\nexport function campaignGuard(db, { owner, epoch, revision }) {\n  validateOwnerEpoch(owner, epoch);\n  if (revision === null) return assertion(db, 'NOT EXISTS(SELECT 1 FROM world_run_campaign WHERE owner=? AND epoch=?)', [owner, epoch]);\n  invalid(!safeInt(revision), 'campaign revision');\n  return assertion(db, 'EXISTS(SELECT 1 FROM world_run_campaign WHERE owner=? AND epoch=? AND revision=?)', [owner, epoch, revision]);\n}\nexport function sequenceStatements(db, { owner, epoch, expectedNext, count }) {\n  validateOwnerEpoch(owner, epoch); invalid(!safeInt(expectedNext, 1) || !safeInt(count, 1) || count > 2 || expectedNext + count > Number.MAX_SAFE_INTEGER, 'sequence allocation');\n  return [stmt(db, 'INSERT INTO world_sequence(owner,epoch,next) VALUES(?,?,1) ON CONFLICT(owner,epoch) DO NOTHING', owner, epoch),\n    assertion(db, 'EXISTS(SELECT 1 FROM world_sequence WHERE owner=? AND epoch=? AND next=?)', [owner, epoch, expectedNext]),\n    stmt(db, 'UPDATE world_sequence SET next=? WHERE owner=? AND epoch=? AND next=?', expectedNext + count, owner, epoch, expectedNext)];\n}\n\nexport async function readCurrentEpoch(db, owner) { if (!nonempty(owner)) fail('run_storage_invalid', 400, 'owner'); return (await first(db, 'SELECT epoch FROM account_data_epochs WHERE owner_id=?', owner))?.epoch ?? null; }\nexport async function readSequence(db, owner, epoch) { validateOwnerEpoch(owner, epoch); return (await first(db, 'SELECT next FROM world_sequence WHERE owner=? AND epoch=?', owner, epoch))?.next ?? 1; }\nexport async function readRun(db, owner, epoch, runId) { validateOwnerEpoch(owner, epoch); if (!nonempty(runId)) fail('run_storage_invalid', 400, 'runId'); return await first(db, 'SELECT * FROM world_runs WHERE owner=? AND epoch=? AND run_id=?', owner, epoch, runId) ?? null; }\nexport async function readActiveRun(db, owner, epoch) { validateOwnerEpoch(owner, epoch); return await first(db, \"SELECT * FROM world_runs WHERE owner=? AND epoch=? AND status='active'\", owner, epoch) ?? null; }\nexport async function readBase(db, owner, epoch) { validateOwnerEpoch(owner, epoch); const row = await first(db, 'SELECT revision,base,charge FROM world_bases WHERE owner=? AND epoch=?', owner, epoch); return row ? { ...row, base: parseJson(row.base, 'base') } : null; }\nexport async function readCampaign(db, owner, epoch) { validateOwnerEpoch(owner, epoch); const row = await first(db, 'SELECT revision,progress FROM world_run_campaign WHERE owner=? AND epoch=?', owner, epoch); return row ? { revision: row.revision, progress: parseJson(row.progress, 'campaign progress') } : null; }\nexport async function readAvailableTicket(db, owner, epoch) { validateOwnerEpoch(owner, epoch); return await first(db, \"SELECT * FROM world_raid_tickets WHERE owner=? AND epoch=? AND status='available'\", owner, epoch) ?? null; }\nexport async function readEventReplay(db, { owner, epoch, runId, clientSeq }) { validateOwnerEpoch(owner, epoch); invalid(!nonempty(runId) || !safeInt(clientSeq, 1), 'event replay key'); return await first(db, 'SELECT * FROM world_run_events WHERE owner=? AND epoch=? AND run_id=? AND client_seq=?', owner, epoch, runId, clientSeq) ?? null; }\nexport async function readStartReplay(db, { owner, epoch, requestId }) { validateOwnerEpoch(owner, epoch); invalid(!nonempty(requestId), 'start replay key'); return await first(db, 'SELECT * FROM world_requests WHERE owner=? AND epoch=? AND id=?', owner, epoch, requestId) ?? null; }\nexport async function readRunReceipts(db, owner, epoch, runId) { validateOwnerEpoch(owner, epoch); return await all(db, 'SELECT * FROM world_run_receipts WHERE owner=? AND epoch=? AND run_id=? ORDER BY client_seq,receipt_id', owner, epoch, runId); }\nexport async function readRunVisits(db, owner, epoch, runId) { validateOwnerEpoch(owner, epoch); return await all(db, 'SELECT * FROM world_run_visits WHERE owner=? AND epoch=? AND run_id=? ORDER BY visit_index', owner, epoch, runId); }\r\nfunction jsonText(value, field) { return canonicalJson(value); }\nexport function insertRunStatements(db, { owner, epoch, runId, startedSeq, state, now }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(runId) || !safeInt(startedSeq, 1), 'new run');\n  return [stmt(db, `INSERT INTO world_runs(owner,epoch,run_id,status,revision,started_seq,end_seq,next_client_seq,state,started_at,last_event_at,ended_at) VALUES(?,?,?,'active',0,?,NULL,1,?,?,?,NULL)`, owner, epoch, runId, startedSeq, jsonText(state, 'run state'), now, now)];\n}\nexport function updateRunStatements(db, { owner, epoch, runId, fromRevision, state, nextClientSeq, now, end }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(runId) || !safeInt(fromRevision) || !safeInt(nextClientSeq, 1), 'run update');\n  if (end !== undefined) invalid(!end || !['completed','defeated','abandoned'].includes(end.status) || !safeInt(end.endSeq, 1), 'run end');\n  const tail = end ? ',status=?,end_seq=?,ended_at=?' : '';\n  const args = [jsonText(state, 'run state'), nextClientSeq, now, ...(end ? [end.status, end.endSeq, now] : []), owner, epoch, runId, fromRevision];\n  return [stmt(db, `UPDATE world_runs SET revision=revision+1,state=?,next_client_seq=?,last_event_at=?${tail} WHERE owner=? AND epoch=? AND run_id=? AND revision=?`, ...args)];\n}\nexport function journalEventStatement(db, { owner, epoch, runId, clientSeq, requestId, type, hash, outcome, response, now, writerGeneration }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now);\n  invalid(!nonempty(runId) || !safeInt(clientSeq, 1) || !nonempty(requestId) || !nonempty(type) || !nonempty(hash) || !['applied','noop'].includes(outcome), 'event journal');\n  if (writerGeneration !== undefined) {\n    invalid(!safeInt(writerGeneration), 'writer generation');\n    return stmt(db, 'INSERT INTO world_run_events(owner,epoch,run_id,client_seq,request_id,type,hash,outcome,response,accepted_at,writer_generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)', owner, epoch, runId, clientSeq, requestId, type, hash, outcome, jsonText(response, 'event response'), now, writerGeneration);\n  }\n  return stmt(db, 'INSERT INTO world_run_events(owner,epoch,run_id,client_seq,request_id,type,hash,outcome,response,accepted_at) VALUES(?,?,?,?,?,?,?,?,?,?)', owner, epoch, runId, clientSeq, requestId, type, hash, outcome, jsonText(response, 'event response'), now);\n}\nexport function startRequestStatement(db, { owner, epoch, requestId, hash, response }) {\n  validateOwnerEpoch(owner, epoch); invalid(!nonempty(requestId) || !nonempty(hash), 'start request');\n  return stmt(db, 'INSERT INTO world_requests(owner,epoch,id,hash,response) VALUES(?,?,?,?,?)', owner, epoch, requestId, hash, jsonText(response, 'start response'));\n}\nexport function visitStatements(db, { owner, epoch, runId, clientSeq, now, visits = [] }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(runId) || !safeInt(clientSeq, 1) || !Array.isArray(visits), 'visits');\n  return visits.map((v, i) => {\n    invalid(!v || !safeInt(v.visitIndex) || !nonempty(v.visitId) || !['completed','escaped'].includes(v.disposition) || (v.receiptId !== undefined && !nonempty(v.receiptId)), `visit:${i}`);\n    return stmt(db, 'INSERT INTO world_run_visits(owner,epoch,run_id,visit_index,visit_id,disposition,receipt_id,client_seq,accepted_at) VALUES(?,?,?,?,?,?,?,?,?)', owner, epoch, runId, v.visitIndex, v.visitId, v.disposition, v.receiptId ?? null, clientSeq, now);\n  });\n}\nexport function receiptStatements(db, { owner, epoch, runId, clientSeq, now, receipts = [] }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(runId) || !safeInt(clientSeq, 1) || !Array.isArray(receipts), 'receipts');\n  return receipts.map((r, i) => {\n    invalid(!r || !nonempty(r.receiptId) || !['room_completed','boss_defeated','level_cleared','run_ended'].includes(r.type) || !nonempty(r.visitId), `receipt:${i}`);\n    return stmt(db, \"INSERT INTO world_run_receipts(owner,epoch,receipt_id,run_id,type,visit_id,data,client_seq,accepted_at,verification) VALUES(?,?,?,?,?,?,?,?,?,'game-policy-v1')\", owner, epoch, r.receiptId, runId, r.type, r.visitId, jsonText(r.data ?? {}, `receipt:${i}`), clientSeq, now);\n  });\n}\nexport function issueTicketStatement(db, { owner, epoch, ticketId, sourceRunId, issuedSeq, now }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(ticketId) || !nonempty(sourceRunId) || !safeInt(issuedSeq, 1), 'ticket');\n  return stmt(db, \"INSERT INTO world_raid_tickets(owner,epoch,ticket_id,source_run_id,issued_seq,status,consumed_raid_id,consumed_seq,issued_at,consumed_at) VALUES(?,?,?,?,?,'available',NULL,NULL,?,NULL)\", owner, epoch, ticketId, sourceRunId, issuedSeq, now);\n}\n/** Builder alone guarantees nothing: compose with epochGuard, sequence CAS, and the raid snapshot insert in one batch; consumedSeq must be that batch's N. */\nexport function consumeTicketStatements(db, { owner, epoch, ticketId, raidId, consumedSeq, now }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(ticketId) || !nonempty(raidId) || !safeInt(consumedSeq, 1), 'ticket consumption');\n  return [assertion(db, \"EXISTS(SELECT 1 FROM world_raid_tickets WHERE owner=? AND epoch=? AND ticket_id=? AND status='available')\", [owner, epoch, ticketId]),\n    stmt(db, \"UPDATE world_raid_tickets SET status='consumed',consumed_raid_id=?,consumed_seq=?,consumed_at=? WHERE owner=? AND epoch=? AND ticket_id=? AND status='available'\", raidId, consumedSeq, now, owner, epoch, ticketId)];\n}\nexport function unlockStatements(db, { owner, epoch, runId, now, grantedSeq, unlocks = [] }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!nonempty(runId) || !safeInt(grantedSeq, 1) || !Array.isArray(unlocks), 'unlocks');\n  return unlocks.map((u, i) => {\n    invalid(!u || !nonempty(u.coachId) || !nonempty(u.encounterId) || !nonempty(u.bossReceiptId) || !nonempty(u.levelReceiptId) || !safeInt(u.revealOrder), `unlock:${i}`);\n    return stmt(db, \"INSERT INTO world_coach_unlocks(owner,epoch,coach_id,run_id,encounter_id,boss_receipt_id,level_receipt_id,status,granted_seq,reveal_order,granted_at,revealed_at) VALUES(?,?,?,?,?,?,?,'pending_reveal',?,?,?,NULL)\", owner, epoch, u.coachId, runId, u.encounterId, u.bossReceiptId, u.levelReceiptId, grantedSeq, u.revealOrder, now);\n  });\n}\nexport function revealAckStatements(db, { owner, epoch, coachIds, now }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!Array.isArray(coachIds), 'coachIds');\n  const ids = coachIds.map((id, i) => { invalid(!nonempty(id), `coachId:${i}`); return id; });\n  return [...new Set(ids)].sort().map(id => stmt(db, \"UPDATE world_coach_unlocks SET status='revealed',revealed_at=? WHERE owner=? AND epoch=? AND coach_id=? AND status='pending_reveal'\", now, owner, epoch, id));\n}\nexport function campaignStatement(db, { owner, epoch, fromRevision, progress }) {\n  validateOwnerEpoch(owner, epoch); const text = jsonText(progress, 'campaign progress');\n  if (fromRevision === null) return stmt(db, 'INSERT INTO world_run_campaign(owner,epoch,progress,revision) VALUES(?,?,?,0)', owner, epoch, text);\n  invalid(!safeInt(fromRevision), 'campaign revision');\n  return stmt(db, 'UPDATE world_run_campaign SET progress=?,revision=revision+1 WHERE owner=? AND epoch=? AND revision=?', text, owner, epoch, fromRevision);\n}\nexport function saveBaseStatement(db, { owner, epoch, fromRevision, base, charge }) {\n  validateOwnerEpoch(owner, epoch); invalid(!safeInt(fromRevision) || !safeInt(charge), 'base save');\n  return stmt(db, 'UPDATE world_bases SET base=?,charge=?,revision=revision+1 WHERE owner=? AND epoch=? AND revision=?', jsonText(base, 'base'), charge, owner, epoch, fromRevision);\n}\nexport function deletionStatements(db, owner) {\n  invalid(!nonempty(owner), 'owner'); return RUN_STORAGE_TABLES.map(table => stmt(db, `DELETE FROM ${table} WHERE owner=?`, owner));\n}\nexport async function exportRows(db, owner, epoch) {\n  validateOwnerEpoch(owner, epoch);\n  const runs = await all(db, 'SELECT owner,epoch,run_id,status,revision,started_seq,end_seq,next_client_seq,started_at,last_event_at,ended_at FROM world_runs WHERE owner=? AND epoch=? ORDER BY started_seq', owner, epoch);\n  return { runs, events: [], visits: await all(db, 'SELECT * FROM world_run_visits WHERE owner=? AND epoch=? ORDER BY run_id,visit_index', owner, epoch),\n    receipts: await all(db, 'SELECT * FROM world_run_receipts WHERE owner=? AND epoch=? ORDER BY run_id,client_seq,receipt_id', owner, epoch),\n    tickets: await all(db, 'SELECT * FROM world_raid_tickets WHERE owner=? AND epoch=? ORDER BY issued_seq,ticket_id', owner, epoch),\n    unlocks: await all(db, 'SELECT * FROM world_coach_unlocks WHERE owner=? AND epoch=? ORDER BY granted_seq,reveal_order,coach_id', owner, epoch),\n    campaign: await readCampaign(db, owner, epoch) };\n}\r\nfunction parseResponse(row) { return parseJson(row.response, 'stored response'); }\nasync function replayStart(db, owner, epoch, requestId, hash) {\n  const row = await readStartReplay(db, { owner, epoch, requestId });\n  if (!row) return null;\n  if (row.hash !== hash) fail('run_idempotency_conflict');\n  const response = parseResponse(row);\n  const run = response?.runId ? await readRun(db, owner, epoch, response.runId) : null;\n  return { replayed: true, resumed: false, run, response };\n}\nfunction frozenClone(value) {\n  const copy = structuredClone(value);\n  const freeze = item => { if (item && typeof item === 'object' && !Object.isFrozen(item)) { Object.freeze(item); for (const child of Object.values(item)) freeze(child); } return item; };\n  return freeze(copy);\n}\nasync function assertCurrentEpoch(db, owner, epoch) { if (await readCurrentEpoch(db, owner) !== epoch) fail('run_epoch_mismatch'); }\n\nexport async function startRunAtomic(db, { owner, epoch, requestId, hash, now, replaceActive = false, maxAttempts = 3, create, resumeResponse, endOld }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); validateAttempts(maxAttempts);\n  invalid(!nonempty(requestId) || !nonempty(hash) || typeof create !== 'function' || typeof resumeResponse !== 'function' || typeof replaceActive !== 'boolean', 'start request');\n  for (let attempt = 0; attempt < maxAttempts; attempt++) {\n    await assertCurrentEpoch(db, owner, epoch);\n    const replay = await replayStart(db, owner, epoch, requestId, hash);\n    if (replay) return replay;\n    const active = await readActiveRun(db, owner, epoch);\n    if (active && !replaceActive) {\n      const response = await resumeResponse(frozenClone(active));\n      invalid(!response || typeof response !== 'object' || Array.isArray(response) || response.runId !== active.run_id, 'resume response runId');\n      try {\n        await db.batch([epochGuard(db, owner, epoch), assertion(db, \"EXISTS(SELECT 1 FROM world_runs WHERE owner=? AND epoch=? AND run_id=? AND status='active' AND revision=?)\", [owner, epoch, active.run_id, active.revision]),\n          startRequestStatement(db, { owner, epoch, requestId, hash, response }), clearAssertions(db)]);\n        return { replayed: false, resumed: true, run: active, response };\n      } catch (error) {\n        if(error?.code?.startsWith('game_generation_'))throw error;\n        // Retry from epoch then idempotency lookup; never allocate a replacement on resume.\n      }\n      continue;\n    }\n\n    const next = await readSequence(db, owner, epoch);\n    const replacing = Boolean(active && replaceActive);\n    const count = replacing ? 2 : 1;\n    let oldEnd;\n    if (replacing) {\n      if (typeof endOld !== 'function') fail('run_storage_invalid', 400, 'endOld callback required');\n      oldEnd = await endOld({ active: frozenClone(active), endSeq: next });\n      invalid(!oldEnd || typeof oldEnd !== 'object' || Array.isArray(oldEnd) || oldEnd.status !== 'abandoned' || !Object.hasOwn(oldEnd, 'state'), 'old run end');\n      invalid(oldEnd.receipts !== undefined && !Array.isArray(oldEnd.receipts), 'old run receipts');\n      const oldClearCount = (oldEnd.receipts ?? []).filter(r => r?.type === 'level_cleared').length;\n      invalid(oldClearCount > 0, 'replacement cannot allocate a clear sequence');\n      invalid(oldEnd.ticket && Object.hasOwn(oldEnd.ticket, 'issuedSeq'), 'caller old-ticket sequence');\n      if (oldEnd.ticket !== undefined) invalid(!oldEnd.ticket || typeof oldEnd.ticket !== 'object' || Array.isArray(oldEnd.ticket) || !nonempty(oldEnd.ticket.ticketId), 'old ticket id');\n    }\n    const startedSeq = replacing ? next + 1 : next;\n    const created = await create({ startedSeq, nextSeq: next + count });\n    invalid(!created || !nonempty(created.runId) || (active && created.runId === active.run_id) || !Object.hasOwn(created, 'state') || !created.response || created.response.runId !== created.runId, 'new run result or response identity');\n    const extra = validateStatementBundles(created, 'create result');\n    if (await readRun(db, owner, epoch, created.runId)) {\n      // create() may have yielded to an identical request that committed this stable run ID.\n      // Check epoch first, then resolve the winning request fingerprint before treating it as ID reuse.\n      await assertCurrentEpoch(db, owner, epoch);\n      const winningReplay = await replayStart(db, owner, epoch, requestId, hash);\n      if (winningReplay) return winningReplay;\n      fail('run_storage_invalid', 400, 'runId already allocated in this epoch');\n    }\n    const writes = [\n      ...(replacing ? [runGuard(db, { owner, epoch, runId: active.run_id, revision: active.revision, nextClientSeq: active.next_client_seq }),\n        ...updateRunStatements(db, { owner, epoch, runId: active.run_id, fromRevision: active.revision, state: oldEnd.state, nextClientSeq: active.next_client_seq, now, end: { status: 'abandoned', endSeq: next } }),\n        ...receiptStatements(db, { owner, epoch, runId: active.run_id, clientSeq: active.next_client_seq, now, receipts: oldEnd.receipts ?? [] }),\n        ...(oldEnd.ticket ? [issueTicketStatement(db, { owner, epoch, ticketId: oldEnd.ticket.ticketId, sourceRunId: active.run_id, issuedSeq: next, now })] : [])] : []),\n      ...insertRunStatements(db, { owner, epoch, runId: created.runId, startedSeq, state: created.state, now }),\n      startRequestStatement(db, { owner, epoch, requestId, hash, response: created.response }),\n    ];\n    try {\n      await db.batch([epochGuard(db, owner, epoch), ...extra.guards, ...sequenceStatements(db, { owner, epoch, expectedNext: next, count }), ...writes, ...extra.writes, clearAssertions(db)]);\n      return { replayed: false, resumed: false, run: await readRun(db, owner, epoch, created.runId), response: created.response };\n    } catch (error) {\n      if(error?.code?.startsWith('game_generation_'))throw error;\n      // Loop begins with the epoch check, then checks winning replay before current run state.\n    }\n  }\n  // Bounded last-look: observe a winner after the last CAS failure without recomputing caller state.\n  await assertCurrentEpoch(db, owner, epoch);\n  const winningReplay = await replayStart(db, owner, epoch, requestId, hash);\n  if (winningReplay) return winningReplay;\n  fail('run_retry');\n}\n\nfunction validateOutcome(out) {\n  invalid(!out || !['applied','noop'].includes(out.outcome) || !Object.hasOwn(out, 'state') || !Object.hasOwn(out, 'response'), 'event outcome');\n  const receipts = out.receipts ?? [], visits = out.visits ?? [], unlocks = out.unlocks ?? [];\n  invalid(!Array.isArray(receipts) || !Array.isArray(visits) || !Array.isArray(unlocks), 'event projections');\n  const clearReceipts = receipts.filter(r => r?.type === 'level_cleared');\n  invalid(clearReceipts.length > 1, 'multiple level clear receipts');\n  const clear = out.clear === true || clearReceipts.length === 1;\n  invalid(out.clear === true && clearReceipts.length !== 1, 'clear flag without exactly one clear receipt');\n  invalid(out.clear !== undefined && typeof out.clear !== 'boolean', 'clear flag');\n  const end = out.end !== undefined && out.end !== null;\n  if (end) invalid(typeof out.end !== 'object' || Array.isArray(out.end) || !['completed','defeated','abandoned'].includes(out.end.status), 'terminal status');\n  if (out.ticket !== undefined) invalid(!out.ticket || typeof out.ticket !== 'object' || Array.isArray(out.ticket) || !nonempty(out.ticket.ticketId), 'ticket');\n  invalid(out.ticket !== undefined && !end, 'ticket without terminal end');\n  invalid(unlocks.length > 0 && !clear, 'unlocks without clear');\n  const extra = validateStatementBundles(out, 'event outcome');\n  invalid(out.outcome === 'noop' && (clear || end || out.ticket || unlocks.length || receipts.length || visits.length || out.campaign || out.base || extra.writes.length), 'noop has projections');\n  const derivedCount = Number(clear) + Number(end);\n  if (out.seqCount !== undefined) invalid(!safeInt(out.seqCount) || out.seqCount !== derivedCount, 'seqCount mismatch');\n  invalid(out.ticket && Object.hasOwn(out.ticket, 'issuedSeq'), 'caller ticket sequence');\n  for (const unlock of unlocks) invalid(unlock && Object.hasOwn(unlock, 'grantedSeq'), 'caller unlock sequence');\n  if (out.campaign !== undefined) invalid(!out.campaign || typeof out.campaign !== 'object' || Array.isArray(out.campaign) || !Object.hasOwn(out.campaign, 'progress'), 'campaign projection');\n  if (out.base !== undefined) invalid(!out.base || typeof out.base !== 'object' || Array.isArray(out.base) || !Object.hasOwn(out.base, 'base') || !safeInt(out.base.charge), 'base projection');\n  return { clear, end: end ? out.end : null, derivedCount, receipts, visits, unlocks, guards: extra.guards, writes: extra.writes };\n}\nfunction validateStatementBundles(value, field) {\n  const guards = value.guards ?? [], writes = value.writes ?? [];\n  invalid(!Array.isArray(guards) || !Array.isArray(writes) || [...guards, ...writes].some(s => !s || typeof s.run !== 'function'), `${field} statement bundles`);\n  return { guards, writes };\n}\nasync function contextForApply(db, owner, epoch, run, nextSeq) {\n  return { run, base: await readBase(db, owner, epoch), campaign: await readCampaign(db, owner, epoch), availableTicket: await readAvailableTicket(db, owner, epoch), nextSeq };\n}\n\nexport async function appendRunEventAtomic(db, { owner, epoch, runId, clientSeq, requestId, type, hash, now, writerGeneration, maxAttempts = 3, apply }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); validateAttempts(maxAttempts);\n  if (writerGeneration !== undefined) invalid(!safeInt(writerGeneration), 'writer generation');\n  invalid(!nonempty(runId) || !safeInt(clientSeq, 1) || !nonempty(requestId) || !nonempty(type) || !nonempty(hash) || typeof apply !== 'function', 'run event');\n  for (let attempt = 0; attempt < maxAttempts; attempt++) {\n    await assertCurrentEpoch(db, owner, epoch);\n    const replay = await readEventReplay(db, { owner, epoch, runId, clientSeq });\n    if (replay) {\n      if (replay.hash !== hash) fail('run_event_conflict');\n      return { replayed: true, outcome: replay.outcome, response: parseJson(replay.response, 'event replay response') };\n    }\n    const run = await readRun(db, owner, epoch, runId);\n    if (!run) fail('run_not_found');\n    if (run.status !== 'active') fail('run_terminal');\n    if (clientSeq !== run.next_client_seq) fail('run_event_gap', 409, { nextClientSeq: run.next_client_seq });\n    const ctx = await contextForApply(db, owner, epoch, run, await readSequence(db, owner, epoch));\n    const out = await apply(ctx);\n    const shape = validateOutcome(out);\n    if (out.base) invalid(!ctx.base, 'base projection without existing base');\n    const statements = [\n      ...(shape.derivedCount ? sequenceStatements(db, { owner, epoch, expectedNext: ctx.nextSeq, count: shape.derivedCount }) : []),\n      ...updateRunStatements(db, { owner, epoch, runId, fromRevision: run.revision, state: out.state, nextClientSeq: clientSeq + 1, now, ...(shape.end ? { end: { status: shape.end.status, endSeq: ctx.nextSeq + Number(shape.clear) } } : {}) }),\n      journalEventStatement(db, { owner, epoch, runId, clientSeq, requestId, type, hash, outcome: out.outcome, response: out.response, now, ...(writerGeneration === undefined ? {} : { writerGeneration }) }),\n      ...visitStatements(db, { owner, epoch, runId, clientSeq, now, visits: shape.visits }),\n      ...receiptStatements(db, { owner, epoch, runId, clientSeq, now, receipts: shape.receipts }),\n      ...(out.ticket ? [issueTicketStatement(db, { owner, epoch, ticketId: out.ticket.ticketId, sourceRunId: runId, issuedSeq: ctx.nextSeq + Number(shape.clear), now })] : []),\n      ...unlockStatements(db, { owner, epoch, runId, now, grantedSeq: ctx.nextSeq, unlocks: shape.unlocks }),\n      ...(out.campaign ? [campaignStatement(db, { owner, epoch, fromRevision: ctx.campaign?.revision ?? null, progress: out.campaign.progress })] : []),\n      ...(out.base ? [saveBaseStatement(db, { owner, epoch, fromRevision: ctx.base.revision, base: out.base.base, charge: out.base.charge })] : []),\n    ];\n    try {\n      await db.batch([epochGuard(db, owner, epoch), runGuard(db, { owner, epoch, runId, revision: run.revision, nextClientSeq: clientSeq, ...(writerGeneration === undefined ? {} : { writerGeneration }) }),\n        ...(out.base ? [baseGuard(db, { owner, epoch, revision: ctx.base.revision })] : []),\n        ...(out.campaign ? [campaignGuard(db, { owner, epoch, revision: ctx.campaign?.revision ?? null })] : []),\n        ...shape.guards, ...statements, ...shape.writes, clearAssertions(db)]);\n      return { replayed: false, outcome: out.outcome, response: out.response };\n    } catch (error) {\n      if(error?.code?.startsWith('game_generation_'))throw error;\n      // Next loop checks epoch first, then exact replay, before terminal/gap checks.\n    }\n  }\n  await assertCurrentEpoch(db, owner, epoch);\n  const winningReplay = await readEventReplay(db, { owner, epoch, runId, clientSeq });\n  if (winningReplay) {\n    if (winningReplay.hash !== hash) fail('run_event_conflict');\n    return { replayed: true, outcome: winningReplay.outcome, response: parseJson(winningReplay.response, 'event replay response') };\n  }\n  fail('run_retry');\n}\nexport async function revealAckAtomic(db, { owner, epoch, coachIds, now }) {\n  validateOwnerEpoch(owner, epoch); validateNow(now); invalid(!Array.isArray(coachIds), 'coachIds');\n  await assertCurrentEpoch(db, owner, epoch);\n  const unique = [...new Set(coachIds)];\n  await db.batch([epochGuard(db, owner, epoch), ...revealAckStatements(db, { owner, epoch, coachIds: unique, now }), clearAssertions(db)]);\n  return { revealed: unique.sort() };\n}\r\n","server/world-run-v3-migration.sql":"-- Apply to the existing world-run schema before enabling new starts.\n-- Fresh installations use world-run-storage-schema.sql and drizzle/0032.\nDROP INDEX IF EXISTS world_run_receipts_one_clear;\n--> statement-breakpoint\nCREATE UNIQUE INDEX world_run_receipts_one_clear ON world_run_receipts(owner,epoch,run_id,COALESCE(json_extract(data,'$.levelIndex'),1)) WHERE type='level_cleared';\n","server/world-run-writer-schema.sql":"ALTER TABLE world_runs ADD COLUMN writer_generation INTEGER NOT NULL DEFAULT 0 CHECK(writer_generation>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN clock_epoch INTEGER NOT NULL DEFAULT 0 CHECK(clock_epoch>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN visit_index INTEGER NOT NULL DEFAULT 0 CHECK(visit_index>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN visit_id TEXT;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN visit_state TEXT NOT NULL DEFAULT 'pending' CHECK(visit_state IN('pending','entered'));\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN entered_visit_binding TEXT;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN runtime TEXT;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN last_frame_seq INTEGER NOT NULL DEFAULT -1;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN last_frame_now REAL;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN clock_offset_ms REAL NOT NULL DEFAULT 0;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN last_writer_server_ms INTEGER NOT NULL DEFAULT 0;\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN run_timing TEXT NOT NULL DEFAULT '{}';\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN accepted_qualifying_units INTEGER NOT NULL DEFAULT 0 CHECK(accepted_qualifying_units>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN energy_granted_ms INTEGER NOT NULL DEFAULT 0 CHECK(energy_granted_ms>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN run_granted_ms INTEGER NOT NULL DEFAULT 0 CHECK(run_granted_ms>=0);\n--> statement-breakpoint\nALTER TABLE world_runs ADD COLUMN ticket_decision TEXT;\n--> statement-breakpoint\nALTER TABLE world_run_events ADD COLUMN writer_generation INTEGER NOT NULL DEFAULT 0 CHECK(writer_generation>=0);\n--> statement-breakpoint\nCREATE TABLE world_run_writer_claims(\n owner TEXT NOT NULL, epoch INTEGER NOT NULL, run_id TEXT NOT NULL, generation INTEGER NOT NULL CHECK(generation>=1),\n request_id TEXT NOT NULL, request_hash TEXT NOT NULL CHECK(length(request_hash)=64), at_revision INTEGER NOT NULL,\n next_client_seq_at_claim INTEGER NOT NULL, claimed_at INTEGER NOT NULL,\n PRIMARY KEY(owner,epoch,run_id,generation), UNIQUE(owner,epoch,request_id)\n);\n--> statement-breakpoint\nCREATE TRIGGER world_run_writer_claims_immutable BEFORE UPDATE ON world_run_writer_claims\nBEGIN SELECT RAISE(ABORT,'writer_claim_immutable'); END;\n","server/world-weather.mjs":"// R58 GET /api/weather?lat=&lng= : US National Weather Service (api.weather.gov, US government data, public domain, no key) fetched server-side so\r\n// users' IPs never reach a third party. lat/lng are rounded to 0.1 deg here too. Returns {code (WMO-style), wind (m/s), temp (degC), source:'nws'} for the\r\n// client's alienFor(); non-US / failure -> 404 {error} and the client falls back to last known / clear. Cached per 0.1 deg cell for 30 min (Cache API).\r\n// Wiring in server/worker.mjs (one line, with the other /api routes):  if(p==='/api/weather'&&method==='GET')return handleWorldWeather(request,env,ctx);   (import {handleWorldWeather} from './world-weather.mjs')\r\nexport const NWS_UA = 'MYR5 (myr5.mominc.online)', TTL_S = 1800;\r\nexport const round1 = v => Math.round(+v * 10) / 10 + 0;\r\n\r\n// NWS shortForecast text -> nearest WMO code the client table understands.\r\nexport function codeFromText(t = '') {\r\n  const s = String(t).toLowerCase();\r\n  if (/thunder|t-storm/.test(s)) return /severe|heavy|damaging/.test(s) ? 99 : 95;\r\n  if (/snow|flurr|sleet|ice|freezing|blizzard|wintry/.test(s)) return /heavy|blizzard/.test(s) ? 75 : /light|flurr/.test(s) ? 71 : 73;\r\n  if (/drizzle/.test(s)) return 51;\r\n  if (/rain|shower/.test(s)) return /heavy/.test(s) ? 65 : /slight|light|chance|isolated|scattered/.test(s) ? 61 : 63;\r\n  if (/fog|haze|mist|smoke/.test(s)) return 45;\r\n  return 0;\r\n}\r\n// \"10 mph\" / \"5 to 15 mph\" / \"10 mph with gusts to 25 mph\" -> m/s (uses the highest number: gusts count as wind for twisters)\r\nexport function windMs(s = '') { const n = String(s).match(/\\d+(\\.\\d+)?/g); return n ? Math.round(Math.max(...n.map(Number)) * .44704 * 10) / 10 : 0; }\r\nexport const tempC = (v, unit = 'F') => Math.round((unit === 'C' ? +v : (+v - 32) * 5 / 9) * 10) / 10;\r\nexport function fromPeriod(p) { return { code: codeFromText(p?.shortForecast), wind: windMs(p?.windSpeed), temp: tempC(p?.temperature, p?.temperatureUnit), source: 'nws' }; }\r\n\r\nconst json = (o, status = 200, extra = {}) => new Response(JSON.stringify(o), { status, headers: { 'content-type': 'application/json', 'cache-control': `public, max-age=${TTL_S}`, ...extra } });\r\n\r\nexport async function handleWorldWeather(request, env, ctx, deps = {}) {\r\n  const url = new URL(request.url), lat = round1(url.searchParams.get('lat')), lng = round1(url.searchParams.get('lng'));\r\n  if (!url.searchParams.get('lat') || !url.searchParams.get('lng') || !Number.isFinite(lat) || !Number.isFinite(lng) || Math.abs(lat) > 90 || Math.abs(lng) > 180) return json({ error: 'bad coordinates' }, 400, { 'cache-control': 'no-store' });\r\n  const f = deps.fetch || fetch, cache = deps.cache === undefined ? globalThis.caches?.default : deps.cache, key = new Request(`https://weather.cache.invalid/nws?c=${lat},${lng}`);\r\n  const hit = await cache?.match(key).catch(() => null); if (hit) return hit;\r\n  const get = async u => { const r = await f(u, { headers: { 'user-agent': NWS_UA, accept: 'application/geo+json' } }); if (!r.ok) throw Object.assign(new Error('nws ' + r.status), { status: r.status }); return r.json(); };\r\n  try {\r\n    const pt = await get(`https://api.weather.gov/points/${lat},${lng}`), hourly = pt?.properties?.forecastHourly;\r\n    if (!hourly) throw new Error('no forecast');\r\n    const p = (await get(hourly))?.properties?.periods?.[0]; if (!p) throw new Error('no period');\r\n    const res = json({ ...fromPeriod(p), cell: `${lat},${lng}` });\r\n    if (cache) { const put = cache.put(key, res.clone()); ctx?.waitUntil ? ctx.waitUntil(put) : await put; }\r\n    return res;\r\n  } catch (e) { return json({ error: e.status === 404 ? 'outside US coverage' : 'weather unavailable' }, e.status === 404 ? 404 : 502, { 'cache-control': 'no-store' }); }\r\n}\r\n","server/world-wild.mjs":"// Wild coach hunt, server lane (PLAN-WILD WS-B). OFF unless env WORLD_WILD==='1' AND WILD_SECRET is set AND WORLD_ZONES is on.\r\n// Spawns are a pure function of (res-8 budget cell, 30-min UTC bucket, WILD_SECRET): zero storage until a player starts an encounter.\r\n// Wiring (see portal-audit-20261017/WAR-ROOM-COORDINATION.md): worker.mjs passes {wild:worldWild(env)} to worldApi; world-api.mjs routes\r\n// GET /api/world/wild/nearby -> wildNearby and POST type 'wild-*' -> wildCommand, and adds wildDeletionStatements to worldDeletionStatements.\r\nimport {zoneOf} from '../modules/coach-world/zones.mjs';\r\nimport {gridDisk,cellToParent,cellToLatLng} from '../modules/coach-world/vendor/h3.mjs';\r\nimport {distanceMeters} from '../modules/coach-world/geo.mjs';\r\nimport {WILD_SPOTS_BY_RES8,WILD_HA_BY_RES8} from '../modules/coach-world/wild-spots-data.mjs';\r\nimport {UNIT_CATALOG} from '../modules/coach-world/unit-catalog.mjs';\r\nimport {advanceBase,applyBaseAction,WILD_LEDGER_CAP} from '../modules/coach-world/state.mjs';\r\nimport {rollTier,SEDATION_PER_HIT,DARTS,DAILY_CAPS,CATCH_ODDS} from '../modules/coach-world-rts/wild-rarity.mjs';\r\nimport {createSpook,stepSpook} from '../modules/coach-world-rts/wild-spook.mjs';\r\n\r\nexport const worldWildEnabled=env=>env?.WORLD_WILD==='1';\r\n// null = off (flag off or no secret: fail closed). Otherwise the config object worldApi takes as options.wild.\r\nexport const worldWild=env=>worldWildEnabled(env)&&typeof env.WILD_SECRET==='string'&&env.WILD_SECRET.length>=16?Object.freeze({secret:env.WILD_SECRET}):null;\r\n\r\nexport const WILD_RULES=Object.freeze({\r\n bucketMs:1800000,radarM:400,revealM:150,farM:250,startAccuracyM:40,tokenMs:360000,reencounterMs:1800000,fledLongMs:86400000,\r\n lockMs:300000,lockMps:4,hardMps:3.5,jumpM:40,minApproachMs:20000,maxPoints:120,maxGapMs:60000,staleMs:8000,windowMs:5000,\r\n inRangeM:15,closeEnoughM:30,quiet:Object.freeze([22,5]),perCellBucket:6,haPerSlot:14,maxSlots:6,napPct:Object.freeze({white:.3,green:.3,blue:.2,orange:.2,gold:.05,iridescent:.05}),\r\n cleanSpook:60,stagger:10,missSpook:15,sedationDecay:4,qualityBonus:.02,qualityCap:.1,\r\n});\r\n// A14 3.2 \"with approach bonus (clean)\"; the base column is CATCH_ODDS in wild-rarity.mjs.\r\nexport const CLEAN_ODDS=Object.freeze({white:.98,green:.92,blue:.8,orange:.62,gold:.45,iridescent:.3});\r\nexport const WILD_UNIT_IDS=Object.freeze(UNIT_CATALOG.filter(u=>u.family==='Four-legged').map(u=>u.id));\r\n\r\nclass WildError extends Error{constructor(code,status=409){super(code);this.code=code;this.status=status;}}\r\nconst fail=(code,status)=>{throw new WildError(code,status);};\r\nconst stmt=(db,sql,...args)=>db.prepare(sql).bind(...args);\r\nconst first=(db,sql,...args)=>stmt(db,sql,...args).first();\r\nconst all=async(db,sql,...args)=>(await stmt(db,sql,...args).all()).results;\r\nconst assert=(db,condition,args=[])=>stmt(db,`INSERT INTO world_assertions(id,ok) VALUES(?,CASE WHEN ${condition} THEN 1 ELSE NULL END)`,crypto.randomUUID(),...args);\r\nconst clamp=(v,lo,hi)=>Math.min(hi,Math.max(lo,v));\r\nconst num=(v,lo,hi,dflt=0)=>Number.isFinite(v)?clamp(v,lo,hi):dflt;\r\nconst json=JSON.stringify;\r\n\r\n// ---- deterministic spawns -------------------------------------------------------------------------------------------------------\r\nconst keys=new Map();\r\nasync function hmac(secret,message){\r\n let key=keys.get(secret);if(!key){key=crypto.subtle.importKey('raw',new TextEncoder().encode(secret),{name:'HMAC',hash:'SHA-256'},false,['sign']);keys.set(secret,key);}\r\n return new Uint8Array(await crypto.subtle.sign('HMAC',await key,new TextEncoder().encode(message)));\r\n}\r\nconst hex=bytes=>[...bytes].map(b=>b.toString(16).padStart(2,'0')).join('');\r\nconst u32=(b,i)=>((b[i]<<24)|(b[i+1]<<16)|(b[i+2]<<8)|b[i+3])>>>0;\r\nexport const bucketOf=now=>Math.floor(now/WILD_RULES.bucketMs);\r\nexport const slotCount=res8=>clamp(Math.round((WILD_HA_BY_RES8[res8]??0)/WILD_RULES.haPerSlot),0,WILD_RULES.maxSlots);\r\n// Every spawn of one res-8 cell that is BORN in `bucket` (it may live 1 or 2 buckets).\r\nexport async function spawnsFor(res8,bucket,secret){\r\n const spots=WILD_SPOTS_BY_RES8[res8],out=[];if(!spots?.length)return out;\r\n for(let slot=0,n=slotCount(res8);slot<n;slot++){\r\n  const h=await hmac(secret,`${res8}|${bucket}|${slot}`),[lat,lng,land]=spots[u32(h,4)%spots.length],id=hex(h).slice(0,24),tier=rollTier(hex(h).slice(8,24),land,false);\r\n  out.push({spawnId:id,res8,bornBucket:bucket,lifeBuckets:h[16]&1?2:1,latitude:lat,longitude:lng,land,tier,facing:Math.round(h[24]/256*360),napping:h[25]/256<WILD_RULES.napPct[tier],unitId:WILD_UNIT_IDS[h[26]%WILD_UNIT_IDS.length],zone:zoneOf(lat,lng),\r\n   expiresAt:(bucket+(h[16]&1?2:1))*WILD_RULES.bucketMs});\r\n }\r\n return out;\r\n}\r\nconst ringRes8=(lat,lng)=>{const z=zoneOf(lat,lng);return z?[...new Set(gridDisk(z,2).map(c=>cellToParent(c,8)))]:[];};\r\n// Live spawns within `radiusM` of the point, nearest first, each with its exact distance (server-side only).\r\nexport async function liveSpawnsNear(lat,lng,now,secret,radiusM=WILD_RULES.radarM){\r\n const b=bucketOf(now),out=[];\r\n for(const res8 of ringRes8(lat,lng))for(const born of [b-1,b])for(const s of await spawnsFor(res8,born,secret)){\r\n  if(s.expiresAt<=now)continue;const d=distanceMeters(lat,lng,s.latitude,s.longitude);if(d<=radiusM)out.push({...s,distance:d});\r\n }\r\n return out.sort((a,c)=>a.distance-c.distance||(a.spawnId<c.spawnId?-1:1));\r\n}\r\n\r\n// ---- radar -----------------------------------------------------------------------------------------------------------------------\r\n// Reveal ladder: nothing beyond 400 m; 400..150 m the res-9 zone centre only; exact position is released ONLY by wild-start.\r\nexport async function wildNearby(db,row,now,request,{secret}){\r\n const q=new URL(request.url).searchParams,lat=Number(q.get('latitude')),lng=Number(q.get('longitude'));\r\n if(!q.has('latitude')||!q.has('longitude')||!zoneOf(lat,lng))fail('wild_query_invalid',400);\r\n const met=new Set((await all(db,'SELECT spawn_id FROM world_wild_seen WHERE owner=? AND epoch=? AND until_at>?',row.owner,row.epoch,now)).map(r=>r.spawn_id));\r\n const spawns=(await liveSpawnsNear(lat,lng,now,secret)).map(s=>{\r\n  const z=zoneOf(s.latitude,s.longitude),[cl,cg]=cellToLatLng(z);\r\n  return {spawnId:s.spawnId,tier:s.tier,band:s.distance<=WILD_RULES.revealM?'tracking':s.distance<=WILD_RULES.farM?'near':'far',canStart:s.distance<=WILD_RULES.revealM&&!met.has(s.spawnId),met:met.has(s.spawnId),\r\n   cellCenter:{latitude:cl,longitude:cg},leavingSoon:s.expiresAt-now<=300000,expiresAt:s.expiresAt};\r\n });\r\n return {serverTime:now,bucket:bucketOf(now),radiusM:WILD_RULES.radarM,spawns};\r\n}\r\n\r\n// ---- local time, quiet hours, caps -------------------------------------------------------------------------------------------------\r\n// tzOffset = JS getTimezoneOffset() minutes (west positive). A client cannot move its clock further than ~1 DST hour from the\r\n// solar offset of the player's longitude, so quiet hours/daily resets cannot be dodged by lying about the timezone.\r\nexport function localParts(now,longitude,tzOffset){\r\n const solar=Math.round(longitude/15)*60,east=clamp(Number.isFinite(tzOffset)?-tzOffset:solar,solar-30,solar+90),t=new Date(now+east*60000);\r\n return {hour:t.getUTCHours(),day:t.toISOString().slice(0,10)};\r\n}\r\nexport const inQuietHours=hour=>hour>=WILD_RULES.quiet[0]||hour<WILD_RULES.quiet[1];\r\n\r\n// ---- speed / replay ---------------------------------------------------------------------------------------------------------------\r\nconst bearing=(a,b)=>{const k=Math.PI/180,dy=(b.latitude-a.latitude),dx=(b.longitude-a.longitude)*Math.cos(a.latitude*k);return (Math.atan2(dx,dy)/k+360)%360;};\r\nconst angleDiff=(a,b)=>{const d=Math.abs(a-b)%360;return d>180?360-d:d;};\r\nconst ptDist=(a,b)=>distanceMeters(a.latitude,a.longitude,b.latitude,b.longitude);\r\nconst cleanPoint=p=>({latitude:p.latitude,longitude:p.longitude,accuracy:num(p.accuracy,0,1000,1000),sampledAt:p.sampledAt,cadenceHz:num(p.cadenceHz,0,6),peakG:num(p.peakG,0,8),gyroRms:num(p.gyroRms,0,20)});\r\nconst validPoint=p=>p&&typeof p==='object'&&[p.latitude,p.longitude,p.accuracy,p.sampledAt].every(Number.isFinite)&&Math.abs(p.latitude)<=90&&Math.abs(p.longitude)<=180&&p.accuracy>=0;\r\n\r\n// Pure: advance a spook replay over validated track points. Same stepSpook the client runs, so the numbers agree.\r\n// state = {spook,trail,peak,lastAt}; returns {state,flee,lock,implausible}. `spawn` needs latitude/longitude/facing/tier/napping.\r\nexport function replaySpook(state,points,spawn){\r\n let {spook,trail,peak}=structuredClone(state);let lock=0,implausible=null,flee=false;\r\n for(const raw of points){\r\n  const p=cleanPoint(raw),prev=trail[trail.length-1],dtMs=clamp(p.sampledAt-prev.sampledAt,1,WILD_RULES.maxGapMs);\r\n  const seg=Math.max(0,ptDist(prev,p)-prev.accuracy-p.accuracy)/(dtMs/1000);\r\n  if(seg>WILD_RULES.hardMps||(dtMs<=1000&&ptDist(prev,p)>WILD_RULES.jumpM)){implausible='speed';break;}\r\n  const win=trail.find(t=>p.sampledAt-t.sampledAt<=WILD_RULES.windowMs+1000&&p.sampledAt-t.sampledAt>=WILD_RULES.windowMs)??trail[0],wdt=Math.max(1,(p.sampledAt-win.sampledAt)/1000);\r\n  const speed=Math.max(0,ptDist(win,p)-Math.min(6,.25*(win.accuracy+p.accuracy)))/wdt;\r\n  if(speed>WILD_RULES.lockMps)lock++;else lock=0;\r\n  const dist=ptDist(p,spawn),dEff=Math.max(0,dist-.5*p.accuracy),approach=angleDiff(bearing(spawn,p),spawn.facing),n=Math.ceil(dtMs/1000);\r\n  for(let i=0;i<n;i++){ // sub-step so sparse reporting cannot skip gain\r\n   const r=stepSpook(spook,{napping:spawn.napping,dtMs:dtMs/n,distanceM:dEff,accuracyM:p.accuracy,speedMps:Math.max(speed,Math.min(p.cadenceHz*.7,WILD_RULES.hardMps)),tier:spawn.tier,approachDeg:approach,peakG:i===0?p.peakG:0,gyroRms:p.gyroRms,cadenceHz:p.cadenceHz});\r\n   spook=r.state;\r\n   peak=Math.max(peak,spook.meter);if(spook.fled||spook.meter>=100){flee=true;break;}\r\n  }\r\n  trail=[...trail,p].slice(-8);if(flee)break;\r\n }\r\n return {state:{spook,trail,peak,lastAt:trail[trail.length-1].sampledAt},flee,lock:lock>=2,implausible};\r\n}\r\nexport const rollU=seed=>u32(Uint8Array.from(seed.match(/../g).map(h=>parseInt(h,16))),0)/4294967296;\r\n// Final server roll. quality bonus capped at +-10 points; clean approach uses the A14 clean column.\r\nexport function catchOdds(tier,clean,qualityBonus){return clamp((clean?CLEAN_ODDS[tier]:CATCH_ODDS[tier])+clamp(qualityBonus,-WILD_RULES.qualityCap,WILD_RULES.qualityCap),0,1);}\r\n\r\n// ---- commands ---------------------------------------------------------------------------------------------------------------------\r\nconst dartsFor=tier=>tier==='iridescent'?DARTS.iridescent:DARTS.default;\r\nconst dailyRow=(db,row,day)=>first(db,'SELECT * FROM world_wild_daily WHERE owner=? AND epoch=? AND day=?',row.owner,row.epoch,day);\r\n// ctx: {secret, zoneWalk(db,row,input,now)} injected by world-api so this file never imports it back.\r\nexport async function wildCommand(db,row,input,now,ctx){\r\n if(input.type==='wild-start')return startEncounter(db,row,input,now,ctx);\r\n if(input.type==='wild-shot')return shootEncounter(db,row,input,now,ctx);\r\n if(input.type==='wild-end')return endEncounter(db,row,input,now);\r\n return fail('wild_command_unknown',400);\r\n}\r\nasync function startEncounter(db,row,input,now,ctx){\r\n if(typeof input.spawnId!=='string'||!/^[0-9a-f]{24}$/.test(input.spawnId))fail('wild_spawn_invalid',400);\r\n const base=advanceBase(JSON.parse(row.base),now),{prior,guard}=await ctx.zoneWalk(db,row,input,now),fix=prior.last;\r\n if(!(fix.accuracy<=WILD_RULES.startAccuracyM))fail('wild_gps_weak',409);\r\n const {hour,day}=localParts(now,fix.longitude,input.tzOffset);if(inQuietHours(hour))fail('wild_quiet_hours',409);\r\n const lock=await first(db,'SELECT until_at FROM world_wild_lockout WHERE owner=?',row.owner);if(lock&&lock.until_at>now)fail('wild_locked',409);\r\n if(base.wild.length>=WILD_LEDGER_CAP)fail('wild_roster_full',409);\r\n const spawn=(await liveSpawnsNear(fix.latitude,fix.longitude,now,ctx.secret,WILD_RULES.revealM)).find(s=>s.spawnId===input.spawnId);\r\n if(!spawn)fail('wild_spawn_gone',409);\r\n const d=spawn.distance;if(d>WILD_RULES.revealM)fail('wild_too_far',409);\r\n const active=await first(db,\"SELECT id,spawn_id,expires_at FROM world_wild_encounters WHERE owner=? AND epoch=? AND status='active' AND expires_at>?\",row.owner,row.epoch,now);\r\n if(active)fail('wild_encounter_active',409);\r\n const seen=await first(db,'SELECT until_at FROM world_wild_seen WHERE owner=? AND epoch=? AND spawn_id=?',row.owner,row.epoch,spawn.spawnId);if(seen&&seen.until_at>now)fail('wild_recently_met',409);\r\n const daily=await dailyRow(db,row,day)??{encounters:0,blue:0,orange:0,gold:0,iridescent:0};\r\n if(daily.encounters>=DAILY_CAPS.encounters)fail('wild_daily_cap',409);\r\n if(DAILY_CAPS[spawn.tier]!=null&&daily[spawn.tier]>=DAILY_CAPS[spawn.tier])fail('wild_daily_cap_tier',409);\r\n const bucket=bucketOf(now),cell=await first(db,'SELECT COUNT(*) AS n FROM world_wild_encounters WHERE owner=? AND epoch=? AND res8=? AND bucket=?',row.owner,row.epoch,spawn.res8,bucket);\r\n if(cell.n>=WILD_RULES.perCellBucket)fail('wild_cell_cap',409);\r\n const id=crypto.randomUUID(),seed=hex(await hmac(ctx.secret,'catch|'+id)),spook0=createSpook(),\r\n  trail=[cleanPoint({...fix,sampledAt:now})],expiresAt=now+WILD_RULES.tokenMs;\r\n const writes=[\r\n  stmt(db,'INSERT INTO world_wild_encounters(id,owner,epoch,spawn_id,res8,bucket,tier,unit_id,zone_id,lat,lng,facing,napping,spawn_expires,seed,started_at,expires_at,status,darts_used,sedation,sedation_at,spook,peak_spook,trail,last_sampled_at,quality_bonus) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,0,?,?,0,?,?,0)',\r\n   id,row.owner,row.epoch,spawn.spawnId,spawn.res8,bucket,spawn.tier,spawn.unitId,spawn.zone,spawn.latitude,spawn.longitude,spawn.facing,spawn.napping?1:0,spawn.expiresAt,seed,now,expiresAt,'active',now,json(spook0),json(trail),now),\r\n  stmt(db,'INSERT INTO world_wild_daily(owner,epoch,day,encounters,blue,orange,gold,iridescent) VALUES(?,?,?,1,0,0,0,0) ON CONFLICT(owner,epoch,day) DO UPDATE SET encounters=encounters+1',row.owner,row.epoch,day),\r\n  stmt(db,'INSERT INTO world_wild_seen(owner,epoch,spawn_id,until_at) VALUES(?,?,?,?) ON CONFLICT(owner,epoch,spawn_id) DO UPDATE SET until_at=excluded.until_at',row.owner,row.epoch,spawn.spawnId,now+WILD_RULES.reencounterMs),\r\n ];\r\n const guards=[guard,assert(db,\"NOT EXISTS(SELECT 1 FROM world_wild_encounters WHERE owner=? AND epoch=? AND status='active' AND expires_at>?)\",[row.owner,row.epoch,now])];\r\n return {base,charge:row.charge,guards,writes,result:{encounterId:id,spawn:{spawnId:spawn.spawnId,tier:spawn.tier,unitId:spawn.unitId,latitude:spawn.latitude,longitude:spawn.longitude,facing:spawn.facing,napping:spawn.napping,land:spawn.land,exact:true},expiresAt,sedation:0,darts:dartsFor(spawn.tier),\r\n  safety:'Look up. Don\\'t run. Stay off roads.'}};\r\n}\r\nasync function loadEncounter(db,row,input,now){\r\n if(typeof input.encounterId!=='string'||!/^[A-Za-z0-9-]{8,64}$/.test(input.encounterId))fail('wild_encounter_invalid',400);\r\n const e=await first(db,'SELECT * FROM world_wild_encounters WHERE id=? AND owner=? AND epoch=?',input.encounterId,row.owner,row.epoch);if(!e)fail('wild_encounter_not_found',404);\r\n return e;\r\n}\r\nconst closeWrite=(db,e,status,now,flag=null)=>stmt(db,\"UPDATE world_wild_encounters SET status=?,ended_at=?,flag=COALESCE(?,flag) WHERE id=? AND status='active'\",status,now,flag,e.id);\r\nasync function shootEncounter(db,row,input,now,ctx){\r\n const e=await loadEncounter(db,row,input,now);if(e.status!=='active')fail('wild_encounter_closed',409);\r\n const base=advanceBase(JSON.parse(row.base),now);\r\n if(!['perfect','hit','miss'].includes(input.quality))fail('wild_shot_invalid',400);\r\n if(!Array.isArray(input.track)||!input.track.length||input.track.length>WILD_RULES.maxPoints||!input.track.every(validPoint))fail('wild_track_invalid',400);\r\n const {prior,guard}=await ctx.zoneWalk(db,row,input,now),fix=prior.last;\r\n const guards=[guard,assert(db,\"EXISTS(SELECT 1 FROM world_wild_encounters WHERE id=? AND status='active' AND darts_used=? AND last_sampled_at=?)\",[e.id,e.darts_used,e.last_sampled_at])];\r\n const spawn={latitude:e.lat,longitude:e.lng,facing:e.facing,napping:e.napping===1,tier:e.tier,expiresAt:e.spawn_expires},writes=[];\r\n if(now>=e.expires_at){writes.push(closeWrite(db,e,'expired',now));return {base,charge:row.charge,guards,writes,result:{outcome:'fled',sedation:e.sedation,dartsLeft:0}};}\r\n const fled=(why,lock=false)=>{writes.push(closeWrite(db,e,lock?'locked':'fled',now,why));if(lock)writes.push(stmt(db,'INSERT INTO world_wild_lockout(owner,until_at) VALUES(?,?) ON CONFLICT(owner) DO UPDATE SET until_at=excluded.until_at',row.owner,now+WILD_RULES.lockMs));\r\n  const longMs=['gold','iridescent'].includes(e.tier)?WILD_RULES.fledLongMs:WILD_RULES.reencounterMs;writes.push(stmt(db,'UPDATE world_wild_seen SET until_at=? WHERE owner=? AND epoch=? AND spawn_id=?',now+longMs,row.owner,row.epoch,e.spawn_id));\r\n  return {base,charge:row.charge,guards,writes,result:{outcome:'fled',sedation:e.sedation,dartsLeft:0,...(lock?{locked:true,lockMs:WILD_RULES.lockMs}:{}),...(why==='spooked'?{reason:'spooked',hint:{bearing:Math.round((bearing({latitude:spawn.latitude,longitude:spawn.longitude},fix)+180)%360),meters:120}}:{})}};};\r\n // 1) track: new points only, monotonic, fresh, ends where the server's own walk session says the player is.\r\n const fresh=input.track.filter(p=>p.sampledAt>e.last_sampled_at&&p.sampledAt>=e.started_at);\r\n if(!fresh.length||fresh.some((p,i)=>p.sampledAt<=(fresh[i-1]?.sampledAt??0)||p.sampledAt>now+1000))fail('wild_track_invalid',400);\r\n if(now-fresh[fresh.length-1].sampledAt>WILD_RULES.staleMs)fail('wild_track_stale',400);\r\n const lastPt=fresh[fresh.length-1];if(ptDist(lastPt,fix)>lastPt.accuracy+fix.accuracy+25)return fled('implausible');\r\n if(now-e.started_at<WILD_RULES.minApproachMs)return fled('too_fast');\r\n const r=replaySpook({spook:JSON.parse(e.spook),trail:JSON.parse(e.trail),peak:e.peak_spook,lastAt:e.last_sampled_at},fresh,spawn);\r\n if(r.implausible)return fled('implausible');\r\n if(r.lock)return fled('speed',true);\r\n let {spook,trail,peak}=r.state;\r\n if(r.flee)return fled('spooked');\r\n // 2) in range?\r\n const dEff=Math.max(0,ptDist(fix,spawn)-.5*fix.accuracy);\r\n if(dEff>(fix.accuracy<=15?WILD_RULES.inRangeM+3:WILD_RULES.closeEnoughM))fail('wild_not_in_range',409);\r\n // 3) dart\r\n const dt=Math.max(0,(now-e.sedation_at)/1000);let sedation=Math.max(0,e.sedation-WILD_RULES.sedationDecay*dt),bonus=e.quality_bonus,darts=e.darts_used+1;\r\n const clean=peak<=WILD_RULES.cleanSpook;\r\n if(input.quality==='miss'){spook={...spook,meter:Math.min(100,spook.meter+WILD_RULES.missSpook)};bonus-=WILD_RULES.qualityBonus;}\r\n else{const per=SEDATION_PER_HIT[e.tier]*(input.quality==='perfect'?1.5:1)*(e.darts_used===0&&clean?2:1);sedation=Math.min(100,sedation+per);bonus+=input.quality==='perfect'?WILD_RULES.qualityBonus:0;spook={...spook,meter:Math.min(100,spook.meter+WILD_RULES.stagger)};}\r\n peak=Math.max(peak,spook.meter);\r\n const update=(status='active')=>stmt(db,'UPDATE world_wild_encounters SET darts_used=?,sedation=?,sedation_at=?,spook=?,peak_spook=?,trail=?,last_sampled_at=?,quality_bonus=?,status=?,ended_at=? WHERE id=? AND status=\\'active\\' AND darts_used=?',\r\n  darts,sedation,now,json({...spook,fled:spook.fled||spook.meter>=100}),peak,json(trail),r.state.lastAt,bonus,status,status==='active'?null:now,e.id,e.darts_used);\r\n if(spook.meter>=100){writes.push(update('fled'));return {base,charge:row.charge,guards,writes,result:{outcome:'fled',reason:'spooked',sedation,dartsLeft:0}};}\r\n const dartsLeft=dartsFor(e.tier)-darts;\r\n if(sedation<100){\r\n  if(dartsLeft<=0){writes.push(update('fled'));return {base,charge:row.charge,guards,writes,result:{outcome:'fled',sedation,dartsLeft:0}};}\r\n  writes.push(update());return {base,charge:row.charge,guards,writes,result:{outcome:'continue',sedation,dartsLeft,spook:Math.round(spook.meter),...(input.quality==='miss'?{spookBump:WILD_RULES.missSpook}:{})}};\r\n }\r\n // 4) server roll with the stored seed\r\n const p=catchOdds(e.tier,clean,bonus);\r\n if(rollU(e.seed)>=p){writes.push(update('fled'));return {base,charge:row.charge,guards,writes,result:{outcome:'fled',sedation:100,dartsLeft}};}\r\n const entry={id:e.id,unitId:e.unit_id,tier:e.tier,seed:e.seed,caughtAt:now,zoneId:e.zone_id};\r\n let next;try{next=applyBaseAction({...base,launchCharge:row.charge},{type:'wild-catch',entry},{now,wildServer:true});}catch(err){if(err instanceof RangeError)fail('wild_roster_full',409);throw err;}\r\n const {day}=localParts(now,fix.longitude,input.tzOffset),col=DAILY_CAPS[e.tier]!=null?e.tier:null;\r\n if(col)writes.push(stmt(db,`INSERT INTO world_wild_daily(owner,epoch,day,encounters,blue,orange,gold,iridescent) VALUES(?,?,?,0,0,0,0,0) ON CONFLICT(owner,epoch,day) DO UPDATE SET ${col}=${col}+1`,row.owner,row.epoch,day));\r\n writes.push(update('caught'),stmt(db,'UPDATE world_wild_seen SET until_at=? WHERE owner=? AND epoch=? AND spawn_id=?',e.spawn_expires,row.owner,row.epoch,e.spawn_id));\r\n return {base:next,charge:row.charge,guards,writes,result:{outcome:'caught',sedation:100,dartsLeft,catch:{id:e.id,tier:e.tier,unitId:e.unit_id,zoneId:e.zone_id}}};\r\n}\r\nasync function endEncounter(db,row,input,now){\r\n const e=await loadEncounter(db,row,input,now),base=advanceBase(JSON.parse(row.base),now);\r\n return {base,charge:row.charge,guards:[],writes:e.status==='active'?[closeWrite(db,e,'ended',now)]:[],result:{ok:true,outcome:e.status==='active'?'ended':e.status}};\r\n}\r\nexport function wildDeletionStatements(db,user){return ['world_wild_encounters','world_wild_daily','world_wild_seen','world_wild_lockout'].map(t=>stmt(db,`DELETE FROM ${t} WHERE owner=?`,user));}\r\nexport {WildError};\r\n","settings-crt.css":"/* W2-2P (#137): TV site's CRT tube glass, ported to the Settings terminal screen only — never the\r\n   bezel (border/box-shadow/corner bolts) or the SATCOM top / BUILD bottom strips that\r\n   settings-frame.css (#107, lane 2L) owns. Geometry (position/size) is written inline by\r\n   settings-crt.mjs from the live screen rect; this file only styles what's drawn inside it.\r\n   Curve = a bigger tube corner radius + a stronger four-edge vignette + a soft convex highlight\r\n   band near the top + a thin inner shadow so the glass reads as recessed into the bezel, ported\r\n   from site-hint tv/tv.css .glass + .phosphor + .glare (border-radius, inset shadow,\r\n   scanline+vignette gradient, glare gradient). Conductor pass (2026-09-23) on 0a450a2: the first\r\n   cut was too faint and left a seam where the overlay stopped short of the real screen edge —\r\n   settings-crt.mjs now insets only by the border width so this sits flush; the corner bolts read\r\n   fine under the vignette's own corner darkening. Both layers are pointer-events:none overlays;\r\n   see settings-crt.mjs for why. */\r\n.crt-screen{position:fixed;pointer-events:none;z-index:6;overflow:hidden;border-radius:26px/20px;\r\n box-shadow:inset 0 0 0 1px #00000080,inset 0 0 10px 2px #00000070}\r\n.crt-screen>div{position:absolute;inset:0}\r\n.crt-vignette{\r\n background:\r\n  /* soft convex highlight band near the top — the tube's bulge catching light */\r\n  linear-gradient(180deg,#ffffff2e 0%,#ffffff12 12%,transparent 30%),\r\n  radial-gradient(ellipse 60% 40% at 24% 10%,#ffffff26,transparent 55%),\r\n  /* four-edge falloff: corners and edges read darker than the centre */\r\n  radial-gradient(ellipse 75% 70% at 50% 50%,transparent 42%,#000000b3 100%);\r\n}\r\n.crt-scanlines{\r\n /* ~3px period, ~1.5px dark line: visible at 1x per the conductor's crop check */\r\n background-image:repeating-linear-gradient(#000 0 1.5px,transparent 1.5px 3px);\r\n opacity:.22;animation:crt-roll 9s linear infinite;\r\n}\r\n@keyframes crt-roll{to{background-position-y:12px}}\r\n@media(prefers-reduced-motion:reduce){.crt-scanlines{animation:none}}\r\n/* optional: one soft band rolling slowly down the tube, on top of the fine scanlines */\r\n.crt-scan-band{\r\n background-image:linear-gradient(180deg,transparent,#9dffb01a 45%,transparent 90%);\r\n background-size:100% 45%;background-repeat:no-repeat;\r\n animation:crt-band-roll 7s linear infinite;\r\n}\r\n@keyframes crt-band-roll{0%{background-position-y:-45%}100%{background-position-y:145%}}\r\n@media(prefers-reduced-motion:reduce){.crt-scan-band{animation:none;opacity:0}}\r\n\r\n.crt-touch-layer{position:fixed;pointer-events:none;z-index:7;overflow:hidden;border-radius:26px/20px}\r\n/* the touch spot: ported from site-hint tv/tv.css .press (a growing radial spot at the touch\r\n   point, springy release) — recoloured for green phosphor instead of the site's purple/gold glass:\r\n   a darker soft centre (low opacity, not black) inside a brighter green bulge ring, alpha-blended\r\n   (no mix-blend-mode:screen — that can only lighten, so it read as a black hole instead of a lens). */\r\n.settings-touch-mark{\r\n position:absolute;width:88px;height:88px;margin:-44px 0 0 -44px;border-radius:50%;\r\n background:radial-gradient(circle,#03150d66 0 16%,#9dffb01f 26%,#9dffb080 38%,#9dffb01f 52%,transparent 68%);\r\n animation:settings-touch-pulse .5s ease-out forwards;\r\n}\r\n@keyframes settings-touch-pulse{0%{transform:scale(.25);opacity:0}30%{opacity:.9}100%{transform:scale(1);opacity:0}}\r\n@media(prefers-reduced-motion:reduce){.settings-touch-mark{animation:none;opacity:0}}\r\n","settings-crt.mjs":"// W2-2P (#137): TV touch effect + CRT curve + scan lines on the Settings terminal screen only —\r\n// never the bezel (border/box-shadow/corner bolts) or the SATCOM top / BUILD bottom strips that\r\n// settings-frame.css (#107, lane 2L) owns. Two pointer-events:none overlays are appended as the\r\n// LAST children of #settings: a dialog shown via showModal() renders in the browser's \"top layer\",\r\n// and everything painted there — including these position:fixed descendants — stays layered above\r\n// the rest of the page and above the dialog's own earlier children, so this doesn't need its own\r\n// z-index arms race with the app.\r\n//\r\n// \"Screen rect\" = the dialog's own box, inset from its border (clears the bezel) and from the top\r\n// strip's bottom edge / bottom strip's top edge (clears the labels + BUILD strip). #settings scrolls\r\n// (.terminal-menu; overflow:auto), with the top strip scrolling away and the bottom strip pinned via\r\n// position:sticky, so the rect is recomputed on open, resize and scroll rather than measured once.\r\n//\r\n// Touch effect: ported from site-hint tv/tv.js:173-207 \"press into the glass\" (tv.css .press /\r\n// .press-layer) — the growing radial glass-touch spot only. The site's screen-wide scale/sink\r\n// (tv.js grow(), scaling #screen toward the touch point) is skipped: #settings is full of real\r\n// controls, and the brief calls for a pure overlay that never blocks or delays the tap underneath,\r\n// so nothing about the dialog itself moves.\r\n// ponytail: single CSS keyframe instead of the site's rAF hold-tracking loop — good enough for a\r\n// tap; add hold-based growth (mirroring tv.js grow()) if Ian wants press-and-hold sizing later.\r\n//\r\n// Conductor pass on 0a450a2: the first cut inset the overlay by the border width PLUS 8px to clear\r\n// the corner bolts, which left a visible seam — a strip of plain dialog background between the real\r\n// screen edge and the vignette's own (darker) edge. Insetting by the border width only makes the\r\n// overlay meet the screen edge cleanly, at the cost of the vignette's corner darkening sitting over\r\n// the bolts near the very corner — an acceptable trade since that's what buys the flush edge.\r\n\r\nexport function mountSettingsCrt(settings){\r\n const screen=document.createElement('div');screen.className='crt-screen';screen.setAttribute('aria-hidden','true');\r\n screen.innerHTML='<div class=\"crt-vignette\"></div><div class=\"crt-scanlines\"></div><div class=\"crt-scan-band\"></div>';\r\n const touch=document.createElement('div');touch.className='crt-touch-layer';touch.setAttribute('aria-hidden','true');\r\n settings.append(screen,touch);\r\n\r\n function place(){\r\n  const r=settings.getBoundingClientRect();\r\n  if(!r.width){screen.style.width=touch.style.width='0px';return;}\r\n  const inset=parseFloat(getComputedStyle(settings).borderTopWidth)||0;\r\n  const top=settings.querySelector('.satcom-top')?.getBoundingClientRect()??{bottom:r.top+inset};\r\n  const bottom=settings.querySelector('.satcom-bottom')?.getBoundingClientRect()??{top:r.bottom-inset};\r\n  const left=r.left+inset,right=r.right-inset;\r\n  const y0=Math.max(top.bottom,r.top+inset),y1=Math.min(bottom.top,r.bottom-inset);\r\n  for(const el of [screen,touch]){\r\n   el.style.left=left+'px';el.style.top=y0+'px';\r\n   el.style.width=Math.max(0,right-left)+'px';el.style.height=Math.max(0,y1-y0)+'px';\r\n  }\r\n }\r\n place();\r\n // Scroll/resize fire many times a frame: one place() (3 rect reads + 4 writes) per frame at most.\r\n let queued=0;const queue=()=>{queued||=requestAnimationFrame(()=>{queued=0;place();});};\r\n document.getElementById('openSettings')?.addEventListener('click',queue);\r\n settings.addEventListener('scroll',queue,{passive:true});\r\n addEventListener('resize',queue);\r\n\r\n settings.addEventListener('pointerdown',event=>{\r\n  if(event.target.closest('.satcom-strip'))return;\r\n  const r=touch.getBoundingClientRect();\r\n  if(!r.width||event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)return;\r\n  const mark=document.createElement('span');mark.className='settings-touch-mark';\r\n  mark.style.left=(event.clientX-r.left)+'px';mark.style.top=(event.clientY-r.top)+'px';\r\n  touch.append(mark);\r\n  const remove=()=>mark.remove();\r\n  mark.addEventListener('animationend',remove);\r\n  setTimeout(remove,700); // reduced motion fires no animationend; this still cleans it up\r\n },{passive:true});\r\n}\r\n","settings-frame.css":"/* #107/#120: a military-satcom frame usable on any dialog (mountSatcomFrame in coach-hub.mjs) --\r\n   Settings (#settings), the Install panel (#installPanel) and the full-screen Downloads menu\r\n   (#downloadsMenu). Decoration only — corner bezel, a SATCOM status strip on top and a BUILD/clock\r\n   strip on the bottom. The dialog's own items, their order and their behaviour are untouched. */\r\n.satcom-frame{\r\n border:3px solid #47523f!important;\r\n box-shadow:7px 7px 0 #030806,inset 0 0 0 3px #10160e,inset 0 0 0 4px #6b9e6c66!important;\r\n}\r\n.satcom-frame h2{text-shadow:0 0 6px #6fae6f99}\r\n/* Faint CRT scanlines. A ::before is never matched by the locked theme's `html[...] *` kill switch,\r\n   so this keeps animating there without any JS — see satcom-radar below for the same trick. */\r\n.satcom-frame::before{\r\n content:'';position:absolute;inset:0;z-index:5;pointer-events:none;border-radius:inherit;\r\n background-image:repeating-linear-gradient(#000 0 1px,transparent 1px 3px);\r\n opacity:.08;animation:satcom-scan 7s linear infinite;\r\n}\r\n@media(prefers-reduced-motion:reduce){.satcom-frame::before{animation:none}}\r\n\r\n.satcom-strip{display:flex;align-items:center;gap:8px;padding:7px 0;font:700 .7rem/1.3 ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;text-transform:uppercase;color:#9be8a3;text-shadow:0 0 4px #4f9b57}\r\n.satcom-top{border-bottom:1px solid #3e5b41;margin-bottom:10px}\r\n/* #settings has the UA dialog:modal default overflow:auto, so it's the scrolling container the long\r\n   settings list scrolls inside; stick the BUILD/clock strip to its bottom so it's never below the\r\n   fold (risk 5) — sticky needs its own background or the scrolled content shows through underneath. */\r\n/* pointer-events:none: sticky, so it sits over whatever scrolls underneath it (risk: a longer\r\n   dialog's own controls, e.g. Install's Copy link, land under it) -- nothing inside is interactive. */\r\n.satcom-bottom{border-top:1px solid #3e5b41;margin-top:14px;justify-content:space-between;position:sticky;bottom:0;background:linear-gradient(140deg,#392640,#1c1426 65%);pointer-events:none}\r\n.satcom-sat{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.4;stroke-linecap:round}\r\n.satcom-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\r\n.satcom-label em{font-style:normal;color:#ffbe5c}\r\n.satcom-top.locked .satcom-label em{color:#9dffb0}\r\n.satcom-bars{display:flex;align-items:flex-end;gap:2px;flex:none}\r\n.satcom-bars i{display:block;width:3px;background:#3e5b41;opacity:.35}\r\n.satcom-bars i:nth-child(1){height:5px}.satcom-bars i:nth-child(2){height:8px}.satcom-bars i:nth-child(3){height:11px}.satcom-bars i:nth-child(4){height:14px}\r\n.satcom-top.locked .satcom-bars i{background:#9dffb0;opacity:1}\r\n@keyframes satcom-blink{0%,100%{opacity:.2}50%{opacity:1}}\r\n@keyframes satcom-flash{0%{box-shadow:inset 0 0 0 999px #9dffb033}100%{box-shadow:inset 0 0 0 999px transparent}}\r\n@keyframes satcom-scan{0%{background-position-y:0}100%{background-position-y:12px}}\r\n@keyframes satcom-spin{to{transform:rotate(1turn)}}\r\n\r\n.satcom-clock{font-variant-numeric:tabular-nums;flex:none}\r\n.satcom-build{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right}\r\n.satcom-radar{position:relative;flex:none;width:15px;height:15px;border-radius:50%;border:1px solid #3e5b41;overflow:hidden}\r\n.satcom-radar::before{content:'';position:absolute;inset:2px;border:1px solid #2b4230;border-radius:50%}\r\n.satcom-radar::after{content:'';position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 0deg,#9dffb0cc,transparent 30%);animation:satcom-spin 4s linear infinite}\r\n@media(prefers-reduced-motion:reduce){.satcom-radar::after{animation:none}}\r\n\r\n.satcom-bolt{position:absolute;width:14px;height:14px;border-color:#6b9e6c;pointer-events:none}\r\n.satcom-bolt::after{content:'';position:absolute;width:5px;height:5px;border-radius:50%;background:radial-gradient(circle at 35% 35%,#cfead0,#5b7a5c 70%);box-shadow:0 0 0 1px #253524}\r\n.satcom-bolt-tl{top:6px;left:6px;border-top:2px solid;border-left:2px solid}\r\n.satcom-bolt-tl::after{top:-1px;left:-1px}\r\n.satcom-bolt-tr{top:6px;right:6px;border-top:2px solid;border-right:2px solid}\r\n.satcom-bolt-tr::after{top:-1px;right:-1px}\r\n.satcom-bolt-bl{bottom:6px;left:6px;border-bottom:2px solid;border-left:2px solid}\r\n.satcom-bolt-bl::after{bottom:-1px;left:-1px}\r\n.satcom-bolt-br{bottom:6px;right:6px;border-bottom:2px solid;border-right:2px solid}\r\n.satcom-bolt-br::after{bottom:-1px;right:-1px}\r\n\r\n@media(max-width:380px){.satcom-strip{font-size:.62rem}}\r\n/* The Downloads menu is full screen with no padding of its own, so the frame would sit on the glass's\r\n   edges and rounded corners (the clock's first digit ran under the bottom-left bolt): pull the bolts in\r\n   from the corners and pad both strips clear of them. */\r\n.downloads-menu .satcom-strip{padding-left:max(32px,env(safe-area-inset-left));padding-right:max(32px,env(safe-area-inset-right))}\r\n.downloads-menu .satcom-top{margin-bottom:0;padding-top:max(9px,env(safe-area-inset-top))}\r\n.downloads-menu .satcom-top+.downloads-page{padding-top:16px} /* the strip already clears the notch */\r\n.downloads-menu .satcom-bottom{margin-top:0;padding-bottom:max(9px,env(safe-area-inset-bottom))}\r\n.downloads-menu .satcom-bolt-tl,.downloads-menu .satcom-bolt-tr{top:max(10px,env(safe-area-inset-top))}\r\n.downloads-menu .satcom-bolt-bl,.downloads-menu .satcom-bolt-br{bottom:max(10px,env(safe-area-inset-bottom))}\r\n.downloads-menu .satcom-bolt-tl,.downloads-menu .satcom-bolt-bl{left:max(10px,env(safe-area-inset-left))}\r\n.downloads-menu .satcom-bolt-tr,.downloads-menu .satcom-bolt-br{right:max(10px,env(safe-area-inset-right))}\r\n\r\n/* R3-03 one text colour: menu text is the strip tint (lightened to 4.5:1 on the plum plate, portal-look.mjs dimTint); headings too (small text on the plate needs the tint).\r\n   The SATCOM readout strips are inset CRT displays and keep their phosphor green. */\r\n#settings :is(h1,h2,h3,p,li,label,legend,summary,small,a,button,span,strong,em,output,input,select,textarea,div):not(.satcom-strip,.satcom-strip *){color:var(--strip-dim)}\r\n/* R3-05 one bottom reservation owner: inside the shell the well (--shell-top / --shell-bottom) already clears the notch and the dock, so the full-screen Downloads page must not count the safe areas a second time. */\r\nhtml.shell-ready .downloads-menu .satcom-bottom{padding-bottom:9px}\r\nhtml.shell-ready .downloads-menu .satcom-top{padding-top:9px}\r\nhtml.shell-ready .downloads-menu .satcom-strip{padding-left:12px;padding-right:12px}\r\nhtml.shell-ready .downloads-menu .satcom-bolt-tl,html.shell-ready .downloads-menu .satcom-bolt-tr{top:10px}\r\nhtml.shell-ready .downloads-menu .satcom-bolt-bl,html.shell-ready .downloads-menu .satcom-bolt-br{bottom:10px}\r\nhtml.shell-ready .downloads-menu .satcom-bolt-tl,html.shell-ready .downloads-menu .satcom-bolt-bl{left:10px}\r\nhtml.shell-ready .downloads-menu .satcom-bolt-tr,html.shell-ready .downloads-menu .satcom-bolt-br{right:10px}\r\n","shader-aurora.mjs":"// Pure placement recipes. The shared manager owns canvases, GPU support and lifecycle.\r\n// Props checked against shaders' Aurora definition; two curtains stay fixed because\r\n// curtainCount is a compile-time prop. Existing room/portrait art remains underneath.\r\nconst canvasStyle = {\r\n  position: 'absolute',\r\n  top: '0',\r\n  left: '0',\r\n  width: '100%',\r\n  height: '42%',\r\n  zIndex: '0',\r\n  pointerEvents: 'none',\r\n  imageRendering: 'auto',\r\n  transform: 'none',\r\n  objectFit: 'fill',\r\n  maxWidth: 'none',\r\n};\r\n\r\nfunction meditationActive(host) {\r\n  const dialog = host?.closest?.('.meditation-panel');\r\n  const scene = host?.closest?.('[data-meditation-scene]');\r\n  if (!dialog?.open || scene?.hidden || host?.isConnected === false) return false;\r\n  if (['breathing-paused', 'snorting', 'blacking-out'].some(name => dialog.classList.contains(name))) return false;\r\n  // The released scene uses a waterfall backdrop for every day's wonder.\r\n  // Add only ambient sky light; the backdrop and grayscale reveal remain intact.\r\n  return true;\r\n}\r\n\r\nfunction meditationComponents() {\r\n  return [{\r\n    type: 'Aurora',\r\n    props: {\r\n      colorA: '#a533f8', colorB: '#76dfb0', colorC: '#479bc4',\r\n      intensity: 35, curtainCount: 2, speed: 1.2,\r\n      waviness: 35, rayDensity: 16, height: 95,\r\n      center: { x: .5, y: .05 }, seed: 5,\r\n    },\r\n  }];\r\n}\r\n\r\nfunction portraitActive(host) {\r\n  return !!host?.querySelector?.('#avatar') && host.isConnected !== false && !host.closest?.('[hidden]');\r\n}\r\n\r\nfunction portraitComponents() {\r\n  return [{\r\n    type: 'Aurora',\r\n    props: {\r\n      colorA: '#713150', colorB: '#cbb4e7', colorC: '#d7b56c',\r\n      intensity: 40, curtainCount: 2, speed: .8,\r\n      waviness: 28, rayDensity: 12, height: 85,\r\n      center: { x: .5, y: .03 }, seed: 5,\r\n    },\r\n  }];\r\n}\r\n\r\nexport const auroraTargets = [\r\n  {\r\n    selector: '.meditation-panel .meditation-far',\r\n    name: 'aurora', priority: 10, opacity: .25,\r\n    style: { ...canvasStyle },\r\n    active: meditationActive,\r\n    components: meditationComponents,\r\n  },\r\n  {\r\n    selector: '.atelier .portrait',\r\n    name: 'aurora', priority: 10, opacity: .25,\r\n    style: { ...canvasStyle, height: '78%' },\r\n    active: portraitActive,\r\n    components: portraitComponents,\r\n  },\r\n];\r\n","shader-effects.css":"/* The shader is decorative; native artwork, text and touch targets stay in front. */\r\ncanvas.shaders-layer{position:absolute!important;pointer-events:none!important;image-rendering:auto!important;object-fit:fill!important;max-width:none!important;filter:none;display:block}\r\ncanvas.shaders-layer[data-shader-effect=\"hologram\"]{mix-blend-mode:screen}\r\n.meditation-far>.shaders-layer{filter:grayscale(1)!important;mix-blend-mode:screen;transition:filter 2.5s ease}\r\n.meditation-colour .meditation-far>.shaders-layer{filter:none!important}\r\n.ship-header>.shaders-layer{border-radius:inherit}\r\n.ship-header>a,.ship-header>button{position:relative;z-index:1}\r\n#holoStage{isolation:isolate}\r\n#holoStage>canvas:not(.shaders-layer){z-index:1}\r\n.portrait>.shaders-layer{z-index:0!important;filter:none!important}\r\n.dj .logobar,.reward-pack-card{position:relative}\r\n.dj .logobar>.shaders-layer{mix-blend-mode:screen}\r\n.drop-pod-card>.shaders-layer,.reward-pack-card>.shaders-layer,.creature-skin-reward>.shaders-layer{mix-blend-mode:screen}\r\n.cage-bays{position:relative;isolation:isolate}\r\n.cage-bays>button{position:relative;z-index:1}\r\n.gala-bay-stage{isolation:isolate}\r\n.gala-bay-stage>canvas:not(.shaders-layer){z-index:1}\r\n.design-studio .hand-canvas>.shaders-layer{mix-blend-mode:screen}\r\n.design-studio .hand-canvas>canvas:not(.shaders-layer){position:relative;z-index:1}\r\n@media(prefers-reduced-motion:reduce){canvas.shaders-layer{display:none!important}}\r\n","shader-effects.mjs":"// Decorative WebGPU effects. The existing artwork remains the immediate fallback.\r\nexport function startShaderEffects({targets, loadRuntime = () => import('/vendor/shaders/runtime.mjs')} = {}) {\r\n  const reduced = matchMedia('(prefers-reduced-motion: reduce)');\r\n  let spent = new WeakMap();\r\n  const failed = new WeakMap(), exhausted = new Set();\r\n  let current = null, timer = 0, stopped = false, epoch = 0, runtime, owner=null;\r\n  const canRun = () => !stopped && !document.hidden && !reduced.matches && !!navigator.gpu && !document.getElementById('mbs-game-loading') &&\r\n    document.body.dataset.cameraWorkout !== 'true' && document.body.dataset.tracking !== 'true';\r\n  function visible(host) {\r\n    if (!host.isConnected || host.closest('[hidden], [inert]')) return false;\r\n    const dialog = host.closest('dialog');\r\n    if (dialog && !dialog.open) return false;\r\n    const modal = [...document.querySelectorAll('dialog[open]')].at(-1);\r\n    if (modal && !modal.contains(host)) return false;\r\n    const tv = host.closest('#tv');\r\n    if(tv && tv.dataset.state!=='on')return false;\r\n    const style = getComputedStyle(host), rect = host.getBoundingClientRect();\r\n    return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0 &&\r\n      rect.width > 1 && rect.height > 1 && rect.bottom > 0 && rect.right > 0 && rect.top < innerHeight && rect.left < innerWidth;\r\n  }\r\n  function mark(map, host, target) {\r\n    const names = map.get(host) || new Set(); names.add(target); map.set(host, names);\r\n  }\r\n  function destroy() {\r\n    epoch++;\r\n    if (!current) return;\r\n    clearTimeout(current.timeout);\r\n    current.resize?.disconnect();\r\n    current.shader?.destroy();\r\n    current.canvas.remove();\r\n    current = null;\r\n  }\r\n  function candidates() {\r\n    for(const entry of exhausted) if(!entry.host.isConnected || !entry.host.matches(entry.target.selector) || (entry.target.active && !entry.target.active(entry.host))) {\r\n      spent.get(entry.host)?.delete(entry.target); exhausted.delete(entry);\r\n    }\r\n    const list = [];\r\n    for (const target of targets || []) for (const host of document.querySelectorAll(target.selector)) {\r\n      const active = !target.active || target.active(host);\r\n      if (!active) { spent.get(host)?.delete(target); failed.get(host)?.delete(target); continue; }\r\n      if (!spent.get(host)?.has(target) && !failed.get(host)?.has(target) && visible(host)) list.push({target, host});\r\n    }\r\n    return list.sort((a,b) => (b.target.priority || 0) - (a.target.priority || 0));\r\n  }\r\n  function resize(entry) {\r\n    if (!entry.shader || !entry.canvas.isConnected) return;\r\n    const {width,height} = entry.canvas.getBoundingClientRect();\r\n    // Explicit bounded dimensions keep decorative surfaces inexpensive on high-DPR phones.\r\n    const scale = Math.min(1, 600 / Math.max(1,width), 400 / Math.max(1,height)) / Math.max(1,devicePixelRatio || 1);\r\n    const cssWidth=entry.canvas.style.width, cssHeight=entry.canvas.style.height;\r\n    entry.shader.resize(Math.max(1,Math.round(width*scale)), Math.max(1,Math.round(height*scale)));\r\n    entry.canvas.style.width=cssWidth; entry.canvas.style.height=cssHeight;\r\n  }\r\n  async function mount({target, host}) {\r\n    const canvas = document.createElement('canvas');\r\n    canvas.className = 'shaders-layer'; canvas.dataset.shader = target.name; canvas.dataset.shaderEffect=target.name;\r\n    canvas.setAttribute('aria-hidden','true');\r\n    Object.assign(canvas.style, {position:'absolute',inset:'0',width:'100%',height:'100%',pointerEvents:'none',opacity:String(target.opacity ?? .35),zIndex:'0',...target.style});\r\n    host.append(canvas);\r\n    const entry = current = {host,target,canvas,shader:null};\r\n    const request = ++epoch;\r\n    try {\r\n      runtime ||= loadRuntime();\r\n      const {createShader} = await runtime;\r\n      if (request !== epoch || !canRun() || !visible(host) || (target.active && !target.active(host))) {\r\n        if (current === entry) destroy(); return;\r\n      }\r\n      const shader = await createShader(canvas,{components:target.components(host)},{disableTelemetry:true,observeElement:false,onError:()=>{\r\n        setTimeout(()=>{if(current===entry && entry.shader?.getFailureReason?.()){mark(failed,host,target);destroy();schedule();}},0);\r\n      }});\r\n      if (request !== epoch || !canRun() || !visible(host) || (target.active && !target.active(host))) {\r\n        shader.destroy(); if (current === entry) destroy(); return;\r\n      }\r\n      if (shader.getFailureReason?.()) { shader.destroy(); mark(failed,host,target); destroy(); return; }\r\n      entry.shader = shader; resize(entry);\r\n      entry.resize = new ResizeObserver(() => resize(entry)); entry.resize.observe(host);\r\n      if (target.durationMs) entry.timeout = setTimeout(() => { mark(spent,host,target); exhausted.add({host,target}); if (current === entry) destroy(); schedule(); },target.durationMs);\r\n    } catch {\r\n      mark(failed,host,target);\r\n      if (current === entry) destroy();\r\n    }\r\n  }\r\n  function sync() {\r\n    timer = 0;\r\n    if (!canRun()) { destroy(); return; }\r\n    const next = candidates()[0];\r\n    if (current && next?.host === current.host && next.target === current.target) return;\r\n    destroy(); if (next) void mount(next);\r\n  }\r\n  function schedule() {\r\n    if (!stopped && !timer) timer = setTimeout(sync,80);\r\n  }\r\n  const observer = new MutationObserver(records=>{\r\n    for(const entry of exhausted) for(const record of records) if(record.target.contains?.(entry.host) &&\r\n      (record.attributeName==='data-phase' || record.attributeName==='open' ||\r\n        (record.attributeName==='class' && record.target.matches('.reward-pack-dialog') && !String(record.oldValue).split(/\\s+/).includes('opened')))) {\r\n      spent.get(entry.host)?.delete(entry.target);exhausted.delete(entry);break;\r\n    }\r\n    schedule();\r\n  });\r\n  observer.observe(document.body,{subtree:true,childList:true,attributes:true,attributeOldValue:true,attributeFilter:['open','hidden','inert','class','data-state','data-phase','data-screen','data-camera-workout','data-tracking','data-wonder']});\r\n  const events = ['scroll','resize','visibilitychange','pageshow'];\r\n  for (const type of events) window.addEventListener(type,schedule,{passive:true,capture:true});\r\n  reduced.addEventListener('change',schedule);\r\n  const clear = () => { spent=new WeakMap(); exhausted.clear(); destroy(); schedule(); };\r\n  const account = event => {const next=`${event.detail?.user?.id || ''}/${event.detail?.dataEpoch ?? ''}`;if(next!==owner){owner=next;clear();}};\r\n  window.addEventListener('myr5:account-cleared',clear);\r\n  window.addEventListener('myr5:account-ready',account);\r\n  function dispose() {\r\n    stopped = true; clearTimeout(timer); timer = 0; destroy(); observer.disconnect();\r\n    for (const type of events) window.removeEventListener(type,schedule,true);\r\n    reduced.removeEventListener('change',schedule);\r\n    window.removeEventListener('myr5:account-cleared',clear);\r\n    window.removeEventListener('myr5:account-ready',account);\r\n    window.removeEventListener('pagehide',hide);\r\n  }\r\n  const hide = () => { clearTimeout(timer); timer=0; destroy(); };\r\n  window.addEventListener('pagehide',hide); schedule();\r\n  return {dispose,refresh:schedule};\r\n}\r\n","shader-entry.mjs":"import {startShaderEffects} from './shader-effects.mjs';\r\nimport {auroraTargets} from './shader-aurora.mjs';\r\nimport {hologramTargets} from './shader-hologram.mjs';\r\nimport {sparkleTargets} from './shader-sparkle.mjs';\r\nstartShaderEffects({targets:[...auroraTargets,...hologramTargets,...sparkleTargets]});\r\n","shader-hologram.mjs":"// Decorative recipes for shaders 4.0.4 Hologram. The shared manager owns canvases\r\n// and lifecycle; exercise/hand Three.js models and controls stay in their hosts.\r\n// SDF names/fields checked against the npm package's analytic shape resolver:\r\n// ellipseSDF / roundedRectSDF use radius (half-width), height and rounding.\r\nconst available = host => !!host?.isConnected && !host.closest('[hidden], [aria-hidden=\"true\"]') &&\r\n  (!host.closest('dialog') || host.closest('dialog').open);\r\n\r\nconst projection = (color, shape) => [{\r\n  type: 'Hologram',\r\n  props: {\r\n    color, shape: JSON.stringify(shape), scale: 3,\r\n    center: {x: .5, y: .5}, brightness: .8, fill: .12,\r\n    edgeGlow: .6, edgeSoftness: .03, depthLines: .12, depthScale: 12,\r\n    scanlines: .18, scanlineScale: 60, scanlineSpeed: .2,\r\n    sweep: .025, flicker: 0, distortion: 0, grain: .05, speed: .25,\r\n  },\r\n}];\r\nconst plate = {type: 'roundedRectSDF', radius: .44, height: .1, rounding: .04};\r\nconst pedestal = {type: 'ellipseSDF', radius: .44, height: .07};\r\n\r\nexport const hologramTargets = [\r\n  {\r\n    selector: 'body[data-screen=\"pod\"] > .ship-header, #portalWorkoutHome > .ship-header',\r\n    name: 'hologram', priority: 20, opacity: .24,\r\n    // A small plate accent in the open centre, clear of the brand and Settings.\r\n    style: {top: '5px', left: 'calc(50% - 56px)', width: '112px', height: '30px', zIndex: '0'},\r\n    active: host => available(host) &&\r\n      (!!host.closest('#portalWorkoutHome') || host.ownerDocument?.body?.dataset.screen === 'pod'),\r\n    components: () => projection('#b877ed', plate),\r\n  },\r\n  {\r\n    selector: '#hologramPanel #holoStage',\r\n    name: 'hologram', priority: 20, opacity: .3,\r\n    // Small projection light in the stage margin, clear of the figure and setup controls.\r\n    style: {bottom: 'auto', top: '12px', left: '12px', width: '64px', height: '32px', zIndex: '0'},\r\n    active: host => available(host) && !!host.closest('#hologramPanel') &&\r\n      !!host.closest('dialog')?.open,\r\n    components: () => projection('#9afae4', pedestal),\r\n  },\r\n  {\r\n    selector: '.war-room .gala-bay-stage',\r\n    name: 'hologram', priority: 20, opacity: .24,\r\n    style: {bottom: 'auto', top: '80px', left: '12px', width: '64px', height: '32px', zIndex: '2'},\r\n    active: host => available(host) && !!host.closest('.war-room'),\r\n    components: () => projection('#ff9a1f', plate),\r\n  },\r\n  {\r\n    // The current hand customizer owns the model and floor. Keep a projection\r\n    // light in its empty upper corner, clear of the hand and bottom view tools.\r\n    selector: '.design-studio .viewport-panel .hand-canvas',\r\n    name: 'hologram', priority: 20, opacity: .28,\r\n    style: {inset: 'auto', top: '12px', right: '12px', width: '64px', height: '32px', zIndex: '2'},\r\n    active: host => available(host) && !!host.closest('.design-studio') &&\r\n      !!host.querySelector('canvas:not([data-shader-effect])') &&\r\n      !host.closest('.hand-viewer-wrap')?.querySelector('.model-status'),\r\n    components: () => projection('#78ffdc', pedestal),\r\n  },\r\n];\r\n","shader-sparkle.mjs":"// Sparkle filters its shader children, not the DOM beneath its canvas. These small\r\n// ornaments supply their own bright Star art; rewards, tiles and text stay intact.\r\n// Verified against shader-effects-inc/shaders packages/core/src/shaders/{Sparkle,Star}.\r\nconst TIER_COLORS = Object.freeze({\r\n  uncommon: '#76e356', rare: '#4bafff', legendary: '#ff9c36', secret: '#b388ff',\r\n});\r\n\r\nconst tierColor = host => TIER_COLORS[host?.closest?.('[data-tier]')?.dataset?.tier] || '#ffe39a';\r\nconst hasUnlock = host => [...(host?.querySelectorAll?.('strong') || [])]\r\n  .some(item => / unlocked!$/.test(item.textContent || ''));\r\nconst openedDialog = (host, selector) => host?.closest?.(selector)?.open === true;\r\n\r\nfunction ornament(color, seed = 0) {\r\n  return [{\r\n    type: 'Sparkle',\r\n    id: 'reward-sparkle',\r\n    props: {\r\n      size: 16, intensity: 2.2, threshold: .45, expand: 0,\r\n      rayLength: 2, colorize: .65, speed: .65, seed,\r\n    },\r\n    children: [{\r\n      type: 'Star', id: 'reward-sparkle-art',\r\n      props: {color, center: {x: .5, y: .5}, radius: .42, sides: 4, innerRatio: .18},\r\n    }],\r\n  }];\r\n}\r\n\r\nconst cornerStyle = Object.freeze({\r\n  position: 'absolute', inset: 'auto', top: '12px', right: '12px',\r\n  width: '56px', height: '56px', pointerEvents: 'none', zIndex: '1',\r\n});\r\n\r\n// The shared manager owns visibility, camera/reduced-motion guards, GPU setup,\r\n// timeouts and cleanup. These descriptors never grant, clear or persist rewards.\r\nexport const sparkleTargets = [\r\n  {\r\n    selector: '.drop-pod[data-phase=\"reveal\"] .drop-pod-card',\r\n    name: 'sparkle', priority: 50, opacity: .6, durationMs: 2200,\r\n    style: {...cornerStyle, top: '4px', right: '4px',width:'40px',height:'40px'},\r\n    active: host => openedDialog(host, '.drop-pod') &&\r\n      host.closest('.drop-pod').dataset?.phase === 'reveal' && hasUnlock(host),\r\n    components: host => ornament(tierColor(host)),\r\n  },\r\n  {\r\n    // A canvas cannot visibly host another canvas. Mount in the surrounding card\r\n    // and put the ornament beside the existing 2D reward tile instead.\r\n    selector: '.reward-pack-dialog.opened .reward-pack-card',\r\n    name: 'sparkle', priority: 50, opacity: .6, durationMs: 2200,\r\n    style: {...cornerStyle, top: '84px'},\r\n    active: host => openedDialog(host, '.reward-pack-dialog.opened') &&\r\n      !host.ownerDocument?.querySelector?.('.drop-pod[open]') &&\r\n      hasUnlock(host.querySelector?.('.reward-pack-result')),\r\n    components: host => ornament(tierColor(host), 1),\r\n  },\r\n  {\r\n    selector: '.creature-skin-reward',\r\n    name: 'sparkle', priority: 50, opacity: .6, durationMs: 2200,\r\n    style: {...cornerStyle, top: '-16px', right: '-8px', width: '44px', height: '44px'},\r\n    active: host => !!host?.querySelector?.('li'),\r\n    components: () => ornament('#ffe39a', 2),\r\n  },\r\n  {\r\n    selector: '.dj .logobar',\r\n    name: 'sparkle', priority: 50, opacity: .6, durationMs: 2200,\r\n    style: {...cornerStyle, top: '15%', left: '4%', right: 'auto', width: '80px', height: '80px'},\r\n    active: host => !!host?.querySelector?.('img'),\r\n    components: () => ornament('#fff4bd', 3),\r\n  },\r\n];\r\n\r\n// Existing .lcd-card.done nodes do not identify a newly banked unlock. A future\r\n// transition signal can opt them in without replaying old saved achievements.\r\n","shell-entry.mjs":"import {initShell} from './modules/shell/owner.mjs';\r\nimport {mountInstrumentControls} from './pod/instrument-controls.mjs';\r\nconst shell=initShell();\r\nif(shell){const style=document.createElement('link');style.rel='stylesheet';style.href='/pod/instrument-controls.css';document.head.append(style);mountInstrumentControls();}\r\n","signin.css":":root{font-family:system-ui,sans-serif;color:#f7efff;background:#160e20;color-scheme:dark}*{box-sizing:border-box}body{margin:0;min-height:100svh;background:radial-gradient(ellipse at 50% 0,#48235b,transparent 65%),#160e20}main{width:min(100% - 32px,440px);margin:0 auto;padding:32px 0 48px}.brand{display:flex;align-items:center;gap:12px;font-weight:750;font-size:20px;color:inherit;text-decoration:none}.brand img{width:56px;height:56px;border-radius:15px}h1{font-size:32px;line-height:1.15;margin:28px 0 12px}p{color:#d5c5df;line-height:1.5}button,.secondary{display:block;width:100%;font:inherit;font-weight:650;text-align:center;padding:14px 18px;border:1px solid #83579b;border-radius:12px;background:#ba82ec;color:#1c1026;cursor:pointer;text-decoration:none;margin:10px 0}.secondary{background:#291b35;color:#f7efff}button:disabled{opacity:.6;cursor:wait}details{margin-top:24px;border-top:1px solid #654576;padding-top:18px}summary{cursor:pointer;font-size:14px}.back{display:inline-block;color:#d6b4ee;margin-top:26px;font-size:14px}#loginStatus:empty{display:none}#login{margin-top:22px}#login .cl-rootBox,#login .cl-cardBox{width:100%}[hidden]{display:none!important}\r\n","signin.html":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\"><meta name=\"theme-color\" content=\"#241231\"><title>Sign in · MYR5 Coach</title><link rel=\"icon\" href=\"/icons/myr5-alien-192.png\"><link rel=\"apple-touch-icon\" href=\"/icons/myr5-alien-apple.png\"><link rel=\"stylesheet\" href=\"/signin.css\"><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head><body><main><a class=\"brand\" href=\"/install\"><img src=\"/icons/myr5-alien-192.png\" alt=\"MYR5\"><span>Mom Inc</span></a><h1>Sign in</h1><p id=\"intro\">Save your progress.</p><p id=\"loginStatus\" role=\"status\">Connecting…</p><a id=\"continueGuest\" class=\"secondary\" href=\"/pose#world\">Continue without signing in</a><p>Your personal base saves on this device. Sign in for shared battles and location rewards.</p><div id=\"login\"></div><section id=\"connected\" hidden><p id=\"who\"></p><button id=\"continue\">Continue to Coach</button><button id=\"switchAccount\" class=\"secondary\">Use another account</button></section><button id=\"retry\" hidden>Retry login</button><p><a class=\"back\" href=\"/privacy.html\">Privacy</a></p><a class=\"back\" href=\"/install\">Back to install</a></main><script type=\"module\" src=\"/signin.mjs\"></script></body></html>\r\n","signin.mjs":"import {authTransitions} from './auth-transition.mjs';\r\nimport {loadLogin, authSettings} from './auth-client.mjs';\r\nimport {safeReturn} from './auth-paths.mjs';\r\n\r\nconst $ = id => document.getElementById(id);\r\nconst params = new URLSearchParams(location.search);\r\nconst returnTo = safeReturn(params.get('return_to'));\r\n// This link stays available even when the identity provider cannot connect.\r\n$('continueGuest').addEventListener('click',()=>{try{sessionStorage.setItem('myr5-browse-without-setup','1');}catch{}});\r\nconst here = '/signin.html?return_to=' + encodeURIComponent(returnTo);\r\n\r\nlet currentClerk = null;\r\nlet currentListener = null;\r\nlet pendingLogin = false;\r\n\r\nfunction error(e) {\r\n  $('loginStatus').textContent = e.message || 'Login could not connect. Please retry.';\r\n  $('retry').hidden = false;\r\n}\r\n\r\nasync function startLogin({ fresh = false } = {}) {\r\n  if (pendingLogin) return;\r\n  pendingLogin = true;\r\n  $('retry').hidden = true;\r\n  $('loginStatus').textContent = 'Connecting...';\r\n  try {\r\n    const config = await authSettings({ force: fresh });\r\n    if (!config.enabled) {\r\n      authTransitions().invalidate();\r\n      localStorage.removeItem('myr5-login-provider');\r\n      error(Error('Sign-in is not available here yet.'));\r\n      return;\r\n    }\r\n    if (!config.frontend || !config.publishableKey) {\r\n      error(Error('Invalid Clerk configuration: frontend or publishableKey missing.'));\r\n      return;\r\n    }\r\n    if (fresh) {\r\n      if (currentClerk && typeof currentClerk.unmountSignIn === 'function') {\r\n        try { currentClerk.unmountSignIn($('login')); } catch (_) {}\r\n      }\r\n      if (currentListener) {\r\n        try { currentListener(); } catch (_) {}\r\n        currentListener = null;\r\n      }\r\n      currentClerk = null;\r\n    }\r\n    if (!currentClerk) {\r\n      currentClerk = await loadLogin();\r\n    }\r\n\r\n    const show = () => {\r\n      $('loginStatus').textContent = '';\r\n      if (currentClerk.user) {\r\n        $('login').hidden = true;\r\n        $('connected').hidden = false;\r\n        $('who').textContent =\r\n          'Signed in as ' +\r\n          (currentClerk.user.primaryEmailAddress?.emailAddress || 'your Mom Inc account');\r\n      } else {\r\n        $('login').hidden = false;\r\n        $('connected').hidden = true;\r\n      }\r\n    };\r\n\r\n    if (!currentClerk.user) {\r\n      $('login').hidden = false;\r\n      $('connected').hidden = true;\r\n      currentClerk.mountSignIn($('login'), {\r\n        routing: 'hash',\r\n        forceRedirectUrl: here,\r\n        signUpForceRedirectUrl: here,\r\n        appearance: { variables: { colorPrimary: '#9561c5', borderRadius: '12px' } }\r\n      });\r\n    }\r\n\r\n    if (typeof currentClerk.addListener === 'function') {\r\n      if (currentListener) {\r\n        try { currentListener(); } catch (_) {}\r\n      }\r\n      currentListener = currentClerk.addListener(show);\r\n    }\r\n\r\n    $('continue').onclick = async () => {\r\n      const button = $('continue');\r\n      button.disabled = true;\r\n      try {\r\n        const transitions = authTransitions();\r\n        transitions.invalidate();\r\n        const ticket = transitions.capture();\r\n        transitions.assertCurrent(ticket);\r\n        localStorage.setItem('myr5-login-provider', 'clerk');\r\n        transitions.invalidate();\r\n        location.assign(returnTo);\r\n      } catch (e) {\r\n        error(e);\r\n        button.disabled = false;\r\n      }\r\n    };\r\n\r\n    $('switchAccount').onclick = async () => {\r\n      try {\r\n        authTransitions().invalidate();\r\n        if (currentClerk && typeof currentClerk.signOut === 'function') {\r\n          await currentClerk.signOut();\r\n        }\r\n        localStorage.removeItem('myr5-login-provider');\r\n        location.replace(here);\r\n      } catch (e) {\r\n        error(e);\r\n      }\r\n    };\r\n\r\n  } catch (e) {\r\n    error(e);\r\n  } finally {\r\n    pendingLogin = false;\r\n\r\n  }\r\n}\r\n\r\n$('retry').onclick = () => startLogin({ fresh: true });\r\nstartLogin({ fresh: true });\r\n","space-rocks.mjs":"// The constellation's dark lit-rim asteroids, shared by the board's drift layers and the flight game's targets.\r\nconst rand=(a,b)=>a+Math.random()*(b-a);\r\nexport const asteroid=(r0,r1)=>({r:rand(r0,r1),rot:rand(0,7),vr:rand(-.3,.3),verts:Array.from({length:8},()=>rand(.55,1))});\r\nfunction poly(ctx,cx,cy,r,rot,verts){\r\n ctx.beginPath();\r\n verts.forEach((v,i)=>{const a=rot+i/verts.length*Math.PI*2,x=cx+Math.cos(a)*r*v,y=cy+Math.sin(a)*r*v;i?ctx.lineTo(x,y):ctx.moveTo(x,y);});\r\n ctx.closePath();\r\n}\r\n// Irregular dark polygon with a lit rim (a directional gradient stroke) — cheap stand-in for a lit asteroid.\r\nexport function rock(ctx,cx,cy,r,rot,verts,alpha){\r\n const g=ctx.createLinearGradient(cx-r,cy-r,cx+r*.2,cy+r*.2);\r\n g.addColorStop(0,'rgba(255,255,255,.6)');g.addColorStop(1,'rgba(255,255,255,0)');\r\n ctx.globalAlpha=alpha;ctx.strokeStyle=g;ctx.lineWidth=1.2;poly(ctx,cx,cy,r,rot,verts);ctx.fillStyle='#0c0818';ctx.fill();ctx.stroke();ctx.globalAlpha=1;\r\n}\r\n","spotify-domain.mjs":"// Spotify-facing values are deliberately reduced to fields the terminal displays.\r\nexport const SPOTIFY_SCOPES = [\r\n  'user-read-private', 'user-read-currently-playing', 'user-read-playback-state',\r\n  'user-modify-playback-state', 'playlist-read-private', 'playlist-read-collaborative',\r\n].join(' ');\r\n\r\nexport function spotifyJamLink(value) {\r\n  try {\r\n    const u = new URL(String(value).trim());\r\n    if (u.protocol !== 'https:' || !['spotify.link', 'open.spotify.com'].includes(u.hostname)) return null;\r\n    if (u.username || u.password || u.port || u.hash) return null;\r\n    if (u.hostname === 'open.spotify.com' && !/^\\/socialsession\\/[A-Za-z0-9]+\\/?$/.test(u.pathname)) return null;\r\n    if (u.hostname === 'spotify.link' && !/^\\/[A-Za-z0-9]+\\/?$/.test(u.pathname)) return null;\r\n    return u.href;\r\n  } catch { return null; }\r\n}\r\n\r\nexport function spotifyPlaylist(item) {\r\n  return {id:item?.id||'',name:item?.name||'Untitled playlist',tracks:Number(item?.items?.total??item?.tracks?.total??0),image:item?.images?.[0]?.url||null,url:item?.external_urls?.spotify||null};\r\n}\r\n\r\nexport function spotifyTrack(item) {\r\n  if(!item||typeof item!=='object')return null;\r\n  return {id:typeof item.id==='string'?item.id:null,name:item.name||'Unknown track',artists:(item.artists||[]).map(a=>a?.name).filter(Boolean),album:item.album?.name||null,image:item.album?.images?.[0]?.url||null,url:item.external_urls?.spotify||null,durationMs:Number(item.duration_ms)||0,uri:item.uri||(item.id?`spotify:track:${item.id}`:null)};\r\n}\r\n\r\nexport function spotifyDevice(item) {\r\n  return {id:item?.id||null,name:item?.name||'Unknown device',active:!!item?.is_active,restricted:!!item?.is_restricted,volumePercent:Number.isInteger(item?.volume_percent)?item.volume_percent:null,supportsVolume:!!item?.supports_volume};\r\n}\r\n\r\nexport function spotifyNowPlaying(data) {\r\n  const item=data?.item,base={playing:!!data?.is_playing,progressMs:Number(data?.progress_ms)||0,durationMs:Number(item?.duration_ms)||0,device:data?.device?.name||null,deviceId:data?.device?.id||null,volumePercent:Number.isInteger(data?.device?.volume_percent)?data.device.volume_percent:null,shuffle:!!data?.shuffle_state,repeat:['off','context','track'].includes(data?.repeat_state)?data.repeat_state:'off'};\r\n  return {...base,track:item?spotifyTrack(item):null,bpm:null};\r\n}\r\n\r\n// Tempo is only active when a provider actually supplies a measured value.\r\nexport function handPulse(tempo,progressMs=0) {\r\n  if (!Number.isFinite(tempo)||tempo<=0||tempo>400) return null;\r\n  let movement=tempo;\r\n  if(movement>=30) movement/=Math.max(1,Math.floor(movement/30));\r\n  while(movement<30) movement*=2;\r\n  const period=60000/movement;\r\n  return {movementBpm:movement,periodMs:period,phase:((progressMs%period)+period)%period/period};\r\n}\r\n","spotify-return.mjs":"export function spotifyReturnMessage(result){\r\n  return ({denied:'Spotify connection was cancelled. Tap Connect Spotify to try again.',expired:'Spotify sign in expired. Tap Connect Spotify to start again.',failed:'Spotify could not finish connecting. Tap Connect Spotify to try again.',\r\n    browser:'Spotify finished in a different browser than Coach. On iPhone, open Coach in Safari (not the Home Screen app), then tap Connect Spotify again.',\r\n    notlisted:'Spotify accepted your sign in, but this Spotify account is not on the Coach test list yet. Ask Ian to add your Spotify email, then tap Connect Spotify.',\r\n    incomplete:'Spotify sign in did not finish. If it stopped at the terms page, tap Agree there, or open Coach in Safari and tap Connect Spotify again.'})[result]||null;\r\n}\r\n","spotify-terminal.css":".spotify-terminal{width:min(760px,calc(100vw - 20px));max-width:760px;max-height:calc(100dvh - 20px);padding:0;border:2px solid #e96920;border-radius:18px;background:#120c09;color:#ffcf92;box-shadow:0 0 0 5px #2c160c,0 22px 80px #000d, inset 0 0 35px #f672221c;font:14px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace}\r\n.spotify-terminal::backdrop{background:#030201df;backdrop-filter:blur(6px)}\r\n.sp-deck-hardware{position:relative;display:flex;align-items:center;justify-content:space-between;height:154px;margin:12px 0 18px;padding:12px 28px;border:2px solid #87817c;border-bottom:6px solid #281e1b;border-radius:14px;background:linear-gradient(130deg,#c8b4a921,transparent 38%,#ffc47e12 72%,transparent),repeating-linear-gradient(0deg,#ffffff05 0 1px,transparent 1px 3px),linear-gradient(150deg,#40352f,#16130f 65%,#33231c);box-shadow:inset 0 2px #fff7,inset 0 -4px #0009,0 9px 15px #0009}.sp-platter{position:relative;width:124px;height:124px;border:7px solid #262020;border-radius:50%;background:repeating-radial-gradient(circle,#121012 0 2px,#34303a 2px 3px);box-shadow:0 0 0 3px #8e8581,0 6px 9px #000b,inset 0 0 18px #000}.sp-platter:after{content:'';position:absolute;inset:34%;border:2px solid #c48943;border-radius:50%;background:#f2a24f;box-shadow:0 0 8px #f18931}.sp-platter i{position:absolute;inset:49%;border-radius:50%;background:#f4e0c3;z-index:1}.sp-tonearm{position:absolute;left:137px;top:20px;width:min(26%,150px);height:10px;border:2px solid #9a9699;border-radius:8px;background:linear-gradient(#e6d9ca,#6d6264);transform:rotate(22deg);transform-origin:right center;box-shadow:1px 3px 5px #000}.sp-tonearm:after{content:'';position:absolute;left:-8px;top:-5px;width:18px;height:18px;border-radius:4px;background:#d18c45;box-shadow:0 1px 4px #000}.sp-deck-leds{display:flex;flex-direction:column;gap:10px;margin-left:auto;padding:10px 12px;border:1px solid #6b513b;border-radius:6px;background:#0b0909}.sp-deck-leds b{display:flex;align-items:center;gap:8px;color:#bd9f83;font-size:9px;letter-spacing:.1em}.sp-deck-leds b:before{content:'';width:9px;height:9px;border-radius:50%;background:#e98933;box-shadow:0 0 7px #e98933}\r\n.spotify-terminal *{box-sizing:border-box}.spotify-terminal button,.spotify-terminal a{font:inherit}.spotify-terminal button{min-height:42px;border:1px solid #f68539;border-radius:6px;background:#31170e;color:#ffbd79;cursor:pointer;box-shadow:inset 0 1px #ffd29833,0 3px #100906}.spotify-terminal button:hover,.spotify-terminal a:hover{background:#50210e;color:#fff2d8}.spotify-terminal button:active{transform:translateY(2px);box-shadow:inset 0 2px #100906}.spotify-terminal :focus-visible{outline:2px solid #fff0b8;outline-offset:3px}.spotify-terminal [hidden]{display:none!important}\r\n.sp-shell{display:grid;grid-template-rows:auto minmax(0,1fr) auto;min-height:min(780px,calc(100dvh - 20px))}.sp-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 22px 14px;border-bottom:2px solid #80330f;background:linear-gradient(115deg,#3f1709,#160e0b 75%)}.sp-label{display:block;font-size:11px;font-weight:700;letter-spacing:.15em;color:#f1853e}.sp-top h2{margin:0;font-size:clamp(23px,5vw,36px);line-height:1;letter-spacing:.09em;color:#ffb45c;text-shadow:0 0 14px #f77d2a88}.sp-top .sp-close{min-width:44px;font-size:24px}.sp-screen{overflow-y:auto;padding:18px 22px 42px;background:repeating-linear-gradient(0deg,#0000 0 3px,#0003 3px 4px),radial-gradient(ellipse at 50% 0,#5b250e66,transparent 65%)}.sp-screen>section{margin:0 0 20px;padding:16px;border:1px solid #77401e;border-radius:10px;background:#170d09d9;box-shadow:inset 0 0 20px #0005,0 0 15px #c65a1b13}.sp-status{min-height:20px;margin:0 0 16px;color:#ffd096;letter-spacing:.08em}.sp-account{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.sp-account-name{margin-right:auto;color:#ffe3ba}.sp-account-actions{display:flex;gap:8px}.sp-account-actions button{padding:6px 12px}.sp-track{display:grid;grid-template-columns:76px minmax(0,1fr);gap:14px;align-items:center;margin:14px 0}.sp-art,.sp-playlist-art{display:grid;place-items:center;overflow:hidden;border:1px solid #a75827;border-radius:6px;background:#29130c;color:#e9883e;font-size:30px}.sp-art{width:76px;height:76px}.sp-art img,.sp-playlist-art img{width:100%;height:100%;object-fit:cover}.sp-track strong,.sp-track span{display:block;overflow-wrap:anywhere}.sp-title{font-size:19px;color:#ffe1ad}.sp-artist{color:#eeb17f}.sp-progress{display:flex;align-items:center;gap:10px}.sp-progress-bar{flex:1;min-width:0;height:9px;accent-color:#ff982f}.sp-time{white-space:nowrap;font-size:11px}.sp-controls{display:flex;justify-content:center;gap:8px;margin:16px 0}.sp-controls button{min-width:64px;padding:6px 12px}.sp-device{margin:8px 0 0;color:#ba8b69;font-size:11px}.sp-hand{display:grid;grid-template-columns:1fr minmax(120px,200px);align-items:center;gap:4px 16px}.sp-hand .sp-label,.sp-hand-status{grid-column:1}.sp-hand-mount{grid-column:2;grid-row:1/3;width:100%;height:150px;display:grid;place-items:center}.sp-hand-mount img{max-width:100%;max-height:100%;object-fit:contain;filter:drop-shadow(0 0 15px #ff8a3180)}.sp-hand-mount canvas{max-width:100%;max-height:100%}.sp-hand-mount.sp-beating{animation:sp-hand-beat var(--sp-beat-ms) ease-in-out var(--sp-phase-ms) infinite}.sp-hand-status{margin:0;color:#d9a174;font-size:11px}.sp-jam p{margin:8px 0 12px}.sp-open-spotify{display:inline-block;padding:9px 12px;border:1px solid #e57b2a;border-radius:6px;color:#ffd5a4;text-decoration:none}.sp-jam-form{margin-top:15px}.sp-jam-form label{display:block;margin:0 0 5px;font-size:11px}.sp-jam-form>div{display:flex;gap:8px}.sp-jam-form input{min-width:0;flex:1;min-height:42px;padding:8px;border:1px solid #a75827;border-radius:6px;background:#0b0706;color:#ffe2bd;font:inherit}.sp-jam-form button{padding:7px 12px}.sp-jam-status{min-height:16px;color:#ffae73;font-size:11px}.sp-playlist-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.sp-count{color:#c58c63;font-size:11px}.sp-playlist-list{list-style:none;padding:0;margin:0}.sp-playlist{display:grid;grid-template-columns:52px minmax(0,1fr) auto;gap:10px;align-items:center;padding:8px 0;border-top:1px solid #5a2b15}.sp-playlist-art{width:52px;height:52px;font-size:20px}.sp-playlist-info{min-width:0}.sp-playlist-info strong,.sp-playlist-info small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sp-playlist-info strong{color:#ffe1ad}.sp-playlist-info small{color:#b98b68}.sp-playlist-play{padding:5px 10px}.sp-more{display:block;width:100%;margin-top:12px}.sp-shell footer{padding:9px 20px;border-top:1px solid #743211;color:#a87553;text-align:center;font-size:10px;letter-spacing:.07em}\r\n@keyframes sp-hand-beat{0%,100%{transform:translateY(0) rotate(-2deg)}20%{transform:translateY(-9px) rotate(2deg)}50%{transform:translateY(0) rotate(-2deg)}}\r\n@media(max-width:540px){.sp-shell{min-height:calc(100dvh - 20px)}.sp-top{padding:12px}.sp-screen{padding:12px 12px 36px}.sp-screen>section{padding:12px}.sp-deck-hardware{height:120px;padding:8px}.sp-platter{width:95px;height:95px}.sp-tonearm{left:91px;top:12px;width:75px}.sp-deck-leds{gap:5px;padding:6px}.sp-hand{grid-template-columns:1fr 115px}.sp-hand-mount{height:120px}.sp-controls button{min-width:50px;padding:5px}.sp-jam-form>div{flex-wrap:wrap}.sp-jam-form button{width:100%}.sp-account-actions{width:100%}.sp-account-actions button{flex:1}}\r\n@media(prefers-reduced-motion:reduce){.sp-hand-mount.sp-beating{animation:none}.spotify-terminal button{transition:none}}\r\n/* The transparent hand hovers over the small physical controller at the end of the records. */\r\n.sp-screen>section.sp-hand{position:relative;display:block;min-height:255px;padding:0;border:0;background:none;box-shadow:none}\r\n.sp-hand>.sp-label,.sp-hand>.sp-hand-status{position:relative;z-index:2;max-width:45%;font-size:10px}\r\n.sp-hand .sp-hand-mount{position:absolute;z-index:2;right:5%;top:-12px;width:65%;height:190px;pointer-events:none;background:none}\r\n.sp-hand .sp-deck-hardware{position:absolute;left:4%;right:4%;bottom:0;height:105px;margin:0;padding:12px 18px;transform:perspective(500px) rotateX(14deg);background:linear-gradient(145deg,#5b5551,#19181b);border-radius:18px}\r\n.sp-hand .sp-platter{box-sizing:border-box;width:90px;height:90px;margin:0;padding:0;min-width:0;min-height:0;cursor:pointer}\r\n.sp-hand .sp-tonearm{left:98px;top:16px;width:55px}\r\n.sp-mixer{display:flex;align-items:center;gap:14px;margin-left:auto}\r\n.sp-hand .sp-knob{position:relative;width:26px;height:26px;min-width:0;min-height:0;padding:0;border:3px ridge #918e86;border-radius:50%;background:radial-gradient(circle at 35% 30%,#737778,#27272b);box-shadow:0 3px 5px #0009}\r\n.sp-hand .sp-knob:after{content:'';position:absolute;top:1px;left:9px;width:2px;height:8px;background:#ffb568;transform:rotate(-20deg)}\r\n.sp-hand .sp-fader{position:relative;width:18px;height:64px;min-width:0;min-height:0;padding:0;border:2px inset #696562;border-radius:4px;background:#09090a}\r\n.sp-hand .sp-fader:after{content:'';position:absolute;left:-5px;right:-5px;top:25px;height:11px;border:1px solid #efcf9f;background:linear-gradient(#d7b482,#786650);border-radius:2px}\r\n.sp-hand .sp-deck-leds{margin-left:10px;padding:5px;font-size:7px}\r\n@media(max-width:400px){.sp-mixer{gap:8px}.sp-hand .sp-deck-leds{display:none}}\r\n\r\n/* The DJ hand stays above the lower-right corner while the records scroll. */\r\n.sp-shell{position:relative;height:min(780px,calc(100dvh - 20px));min-height:0;overflow:hidden}\r\n.sp-shell>.sp-hand-mount{position:absolute;right:10px;bottom:calc(30px + env(safe-area-inset-bottom));top:auto;z-index:4;width:clamp(130px,25vw,190px);height:190px;overflow:hidden;pointer-events:none;background:none}\r\n.sp-shell>.sp-hand-mount canvas{width:100%;height:100%;display:block}\r\n.sp-shell>.sp-hand-mount img{width:100%;height:125%;max-height:none;object-fit:cover;object-position:center top;align-self:start}\r\n.sp-screen>section.sp-hand{min-height:150px}\r\n@media(max-width:540px){.sp-shell{height:calc(100dvh - 20px);min-height:0}.sp-shell>.sp-hand-mount{width:135px;height:170px;right:6px;bottom:calc(32px + env(safe-area-inset-bottom))}}\r\n","spotify-terminal.mjs":"import {spotifyJamLink} from './spotify-domain.mjs';\r\nimport {signInPath} from './auth-paths.mjs';\r\nimport {spotifyReturnMessage} from './spotify-return.mjs';\r\nimport {mountActualDJ} from './actual-dj.mjs';\r\n\r\nconst make=(tag,className,text)=>{const el=document.createElement(tag);if(className)el.className=className;if(text!=null)el.textContent=text;return el;};\r\nconst fmt=ms=>`${Math.floor((ms||0)/60000)}:${String(Math.floor(((ms||0)%60000)/1000)).padStart(2,'0')}`;\r\n\r\n// request(path, options) must be the app's authenticated fetch bridge and return a Response.\r\n// The bridge owns Clerk/ChatGPT session headers; this module never sees OAuth tokens.\r\nexport function initSpotifyTerminal({request=fetch,getAccount=()=>window.myr5AuthenticatedAccount}={}) {\r\n  let dialog=document.getElementById('spotifyPanel'),abort=null,poll=null,playing=false,disposed=false,connected=false,playlistOffset=0,playlistTotal=0;\r\n  if(!dialog){dialog=make('dialog','spotify-terminal');dialog.id='spotifyPanel';document.body.append(dialog);}\r\n  dialog.innerHTML=`<div class=\"sp-shell\"><header class=\"sp-top\"><div><h2>DJ TERMINAL</h2></div><button class=\"sp-close\" type=\"button\" aria-label=\"Close DJ terminal\">×</button></header><div class=\"sp-screen\"><p class=\"sp-status\" role=\"status\" aria-live=\"polite\">Checking Spotify connection…</p><section class=\"sp-account\"><span class=\"sp-label\">ACCOUNT LINK</span><strong class=\"sp-account-name\">OFFLINE</strong><div class=\"sp-account-actions\"><button class=\"sp-connect\" type=\"button\">Connect Spotify</button><button class=\"sp-disconnect\" type=\"button\" hidden>Disconnect</button></div></section><section class=\"sp-now\"><span class=\"sp-label\">ON THE DECK</span><div class=\"sp-track\"><div class=\"sp-art\" aria-hidden=\"true\">♫</div><div><strong class=\"sp-title\">No track playing</strong><span class=\"sp-artist\">Open Spotify on a device to begin.</span></div></div><div class=\"sp-progress\"><progress class=\"sp-progress-bar\" max=\"1\" value=\"0\"></progress><span class=\"sp-time\">0:00 / 0:00</span></div><div class=\"sp-controls\"><button data-action=\"previous\" aria-label=\"Previous track\">◂◂</button><button data-action=\"pause\" class=\"sp-pause\">PAUSE</button><button data-action=\"resume\" class=\"sp-resume\">PLAY</button><button data-action=\"next\" aria-label=\"Next track\">▸▸</button></div><p class=\"sp-device\">Spotify Connect controls require Premium and an active device.</p></section><section class=\"sp-hand\"><span class=\"sp-label\">PLAYBACK CONTROLS</span></section><section class=\"sp-jam\"><span class=\"sp-label\">LISTEN WITH FRIENDS</span><p>Start a Jam in Spotify, then share its invitation link here.</p><a class=\"sp-open-spotify\" href=\"https://open.spotify.com/\" target=\"_blank\" rel=\"noopener noreferrer\">OPEN SPOTIFY ↗</a><form class=\"sp-jam-form\"><label for=\"spJamLink\">Jam invitation link</label><div><input id=\"spJamLink\" type=\"url\" inputmode=\"url\" placeholder=\"https://spotify.link/…\" autocomplete=\"off\"><button type=\"submit\">OPEN JAM</button></div></form><p class=\"sp-jam-status\" aria-live=\"polite\"></p></section><section class=\"sp-playlists\"><div class=\"sp-playlist-heading\"><span class=\"sp-label\">YOUR PLAYLISTS</span><span class=\"sp-count\"></span></div><ol class=\"sp-playlist-list\"></ol><button class=\"sp-more\" type=\"button\" hidden>LOAD MORE ↓</button></section></div><footer>SPOTIFY PLAYBACK STAYS ON YOUR DEVICE // MYR5 DJ BOOTH</footer></div>`;\r\n  const $=s=>dialog.querySelector(s),status=message=>{$('.sp-status').textContent=message;};\r\n  const seekLabel=make('label','dj-spotify-seek','SEEK '),seekControl=make('input');seekControl.type='range';seekControl.min='0';seekControl.max='1';seekControl.step='1000';seekControl.value='0';seekControl.setAttribute('aria-label','Spotify track position');seekLabel.append(seekControl);$('.sp-progress').after(seekLabel);\r\n  const volumeLabel=make('label','dj-spotify-volume','VOLUME '),volumeControl=make('input');volumeControl.type='range';volumeControl.min='0';volumeControl.max='100';volumeControl.step='1';volumeControl.value='50';volumeControl.setAttribute('aria-label','Spotify device volume');volumeLabel.append(volumeControl);$('.sp-controls').after(volumeLabel);\r\n  const signIn=make('a','sp-sign-in','SIGN IN TO COACH');signIn.href=signInPath(location.pathname+location.search+location.hash);signIn.hidden=true;$('.sp-account-actions').append(signIn);\r\n  const PENDING='myr5.spotifyPending',store=(fn)=>{try{return fn(localStorage);}catch{return null;}};\r\n  const pendingFresh=()=>Date.now()-Number(store(l=>l.getItem(PENDING))||0)<15*60000;\r\n  let returnResult=new URLSearchParams(location.search).get('spotify'),connectBusy=false,loadRevision=0;\r\n  const accountAvailable=()=>!!getAccount()?.user?.id;\r\n\r\n  // Keep the physical playback controls alongside the Spotify records.\r\n  $('.sp-playlists').after($('.sp-hand'));\r\n  const deck=make('div','sp-deck-hardware');deck.innerHTML='<button type=\"button\" class=\"sp-platter\" data-action=\"resume\" aria-label=\"Play\"><i></i></button><div class=\"sp-tonearm\" aria-hidden=\"true\"></div><div class=\"sp-mixer\"><button type=\"button\" class=\"sp-knob\" data-action=\"previous\" aria-label=\"Previous track\"></button><button type=\"button\" class=\"sp-knob\" data-action=\"next\" aria-label=\"Next track\"></button><button type=\"button\" class=\"sp-fader\" data-action=\"pause\" aria-label=\"Pause\"></button></div><div class=\"sp-deck-leds\" aria-hidden=\"true\"><b>POWER</b><b>STEREO</b><b>ON AIR</b></div>';\r\n  $('.sp-hand').append(deck);\r\n  const fetchJSON=async(path,options={})=>{const headers=new Headers(options.headers),account=getAccount(),owner=account?.user?.id,epoch=account?.dataEpoch,signal=abort?.signal;if(!['GET','HEAD'].includes((options.method||'GET').toUpperCase())){if(!owner||!Number.isSafeInteger(epoch))throw Error('Sign in to Coach before connecting Spotify.');headers.set('X-Target-Account',owner);headers.set('X-Expected-Data-Epoch',String(epoch));}const response=await request(path,{credentials:'same-origin',cache:'no-store',...options,headers,signal});const data=await response.json();if(signal?.aborted||getAccount()?.user?.id!==owner||getAccount()?.dataEpoch!==epoch)throw new DOMException('Spotify account changed.','AbortError');if(!response.ok)throw Object.assign(Error(data.error||'Spotify is unavailable.'),{code:data.code,status:response.status});return data;};\r\n  function stop(){loadRevision++;clearTimeout(poll);poll=null;abort?.abort();abort=null;}\r\n  function showNow(now){\r\n    playing=!!now.playing;\r\n    actualDJ.spotifyPlaying(playing);\r\n    const track=now.track;$('.sp-title').textContent=track?.name||'No track playing';$('.sp-artist').textContent=track?track.artists.join(', ')||'Unknown artist':'Open Spotify on a device to begin.';\r\n    $('.sp-art').replaceChildren();if(track?.image){const img=make('img');img.src=track.image;img.alt='';img.loading='lazy';$('.sp-art').append(img);}else $('.sp-art').textContent='♫';\r\n    const duration=Math.max(1,now.durationMs||1);$('.sp-progress-bar').max=duration;$('.sp-progress-bar').value=Math.min(duration,now.progressMs||0);$('.sp-time').textContent=`${fmt(now.progressMs)} / ${fmt(now.durationMs)}`;\r\n    seekControl.max=duration;if(document.activeElement!==seekControl)seekControl.value=Math.min(duration,now.progressMs||0);seekControl.disabled=!now.track;\r\n    volumeControl.disabled=!Number.isFinite(now.volumePercent);if(Number.isFinite(now.volumePercent)&&document.activeElement!==volumeControl)volumeControl.value=now.volumePercent;\r\n    $('.sp-pause').hidden=!now.playing;$('.sp-resume').hidden=!!now.playing;\r\n    $('.sp-device').textContent=now.device?`Playing on ${now.device}. Spotify Premium is required for remote control.`:'Spotify Connect controls require Premium and an active device.';\r\n    const label=document.getElementById('spotifyNowPlayingLabel');if(label)label.textContent=track?`${track.name} — ${track.artists.join(', ')}`:'actual.dj';\r\n  }\r\n  async function refreshNow(){if(!dialog.open||!connected||document.hidden)return;const revision=loadRevision;try{const data=await fetchJSON('/api/spotify/now-playing');if(revision!==loadRevision||!dialog.open)return;showNow(data.nowPlaying);status(data.nowPlaying.playing?'LIVE FROM SPOTIFY':'CONNECTED // WAITING FOR PLAYBACK');}catch(e){if(revision!==loadRevision||!dialog.open||e.name==='AbortError')return;status(e.message);if(e.code==='spotify_reconnect'||e.code==='spotify_disconnected'){connected=false;actualDJ.spotifyState(false);}}finally{if(revision===loadRevision&&dialog.open&&!disposed&&connected)poll=setTimeout(refreshNow,10000);}}\r\n  async function loadPlaylists(reset=false){const revision=loadRevision;if(reset){playlistOffset=0;playlistTotal=0;$('.sp-playlist-list').replaceChildren();}const data=await fetchJSON(`/api/spotify/playlists?offset=${playlistOffset}`);if(revision!==loadRevision||!dialog.open)return;playlistTotal=data.total;for(const item of data.items){const li=make('li','sp-playlist');const art=make('span','sp-playlist-art');if(item.image){const img=make('img');img.src=item.image;img.alt='';img.loading='lazy';art.append(img);}else art.textContent='♫';const label=make('span','sp-playlist-info');label.append(make('strong','',item.name),make('small','',`${item.tracks} tracks`));const play=make('button','sp-playlist-play','PLAY ▸');play.type='button';play.dataset.playlist=item.id;play.setAttribute('aria-label',`Play ${item.name}`);li.append(art,label,play);$('.sp-playlist-list').append(li);}playlistOffset+=data.items.length;$('.sp-count').textContent=`${Math.min(playlistOffset,playlistTotal)} / ${playlistTotal}`;$('.sp-more').hidden=playlistOffset>=playlistTotal||!data.items.length;}\r\n  async function load(){const result=returnResult,revision=++loadRevision;signIn.hidden=accountAvailable();if(!accountAvailable()){connected=false;actualDJ.spotifyState(false);$('.sp-connect').hidden=true;$('.sp-disconnect').hidden=true;$('.sp-now').hidden=true;$('.sp-playlists').hidden=true;status('Sign in to Coach to connect your Spotify account.');return;}try{status('CHECKING ACCOUNT LINK…');const data=await fetchJSON('/api/spotify/status');if(revision!==loadRevision||!dialog.open)return;connected=data.connected;actualDJ.spotifyState(connected);if(connected)store(l=>l.removeItem(PENDING));$('.sp-account-name').textContent=data.account?.name||'NOT CONNECTED';$('.sp-connect').hidden=connected;$('.sp-disconnect').hidden=!connected;$('.sp-now').hidden=!connected;$('.sp-playlists').hidden=!connected;if(!connected){const pending=!result&&pendingFresh();store(l=>l.removeItem(PENDING));status(spotifyReturnMessage(result||(pending?'incomplete':null))||'CONNECT YOUR SPOTIFY ACCOUNT');showNow({track:null,progressMs:0,durationMs:0});return;}status('CONNECTED TO SPOTIFY');await Promise.all([refreshNow(),loadPlaylists(true)]);}catch(e){if(revision===loadRevision&&dialog.open&&e.name!=='AbortError')status(e.message);}}\r\n  async function action(name,playlistId,payload={}){const revision=loadRevision;try{if(['resume','track','playlist','transfer'].includes(name))actualDJ.spotifyStarting();status('SENDING TO SPOTIFY…');await fetchJSON('/api/spotify/playback',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:name,...payload,...(playlistId?{playlistId}:{})})});if(revision!==loadRevision||!dialog.open)return false;if(name==='pause'){playing=false;actualDJ.spotifyPlaying(false);}else if(['resume','track','playlist','transfer'].includes(name)){playing=true;actualDJ.spotifyPlaying(true);}void import('./modules/vault/vault-store.mjs').then(m=>m.bump('dj-session')).catch(()=>{});clearTimeout(poll);poll=setTimeout(refreshNow,1000);return true;}catch(e){if(revision===loadRevision&&dialog.open&&e.name!=='AbortError')status(e.message);return false;}}\r\n  const actualDJ=mountActualDJ({dialog,spotifyJSON:fetchJSON,spotifyAction:(name,payload)=>action(name,null,payload)});\r\n  seekControl.addEventListener('change',()=>void action('seek',null,{positionMs:Math.round(Number(seekControl.value))}));\r\n  volumeControl.addEventListener('change',()=>void action('volume',null,{volumePercent:Number(volumeControl.value)}));\r\n  const open=()=>{if(disposed)return null;if(!dialog.open){abort=new AbortController();dialog.showModal();void load();}return dialog;};\r\n  $('.sp-close').addEventListener('click',()=>dialog.close());dialog.addEventListener('close',()=>{if(!dialog.open)stop();});\r\n  $('.sp-connect').addEventListener('click',async()=>{if(connectBusy)return;connectBusy=true;$('.sp-connect').disabled=true;status('OPENING SPOTIFY SIGN IN...');try{const data=await fetchJSON('/api/spotify/connect',{method:'POST'});if(!dialog.open)return;store(l=>l.setItem(PENDING,String(Date.now())));location.assign(data.url);}catch(e){if(dialog.open)status(e.message);}finally{connectBusy=false;$('.sp-connect').disabled=false;}});\r\n  $('.sp-disconnect').addEventListener('click',async()=>{try{await fetchJSON('/api/spotify/disconnect',{method:'POST'});connected=false;stop();abort=new AbortController();await load();}catch(e){status(e.message);}});\r\n  $('.sp-controls').addEventListener('click',e=>{const button=e.target.closest('[data-action]');if(button)void action(button.dataset.action);});\r\n  deck.addEventListener('click',e=>{const button=e.target.closest('[data-action]');if(button)void action(button.dataset.action);});\r\n  $('.sp-playlist-list').addEventListener('click',e=>{const button=e.target.closest('[data-playlist]');if(button)void action('playlist',button.dataset.playlist);});\r\n  $('.sp-more').addEventListener('click',()=>{void loadPlaylists().catch(e=>status(e.message));});\r\n  $('.sp-jam-form').addEventListener('submit',e=>{e.preventDefault();const link=spotifyJamLink($('#spJamLink').value);if(!link){$('.sp-jam-status').textContent='Enter a Spotify Jam invitation link.';return;}$('.sp-jam-status').textContent='Opening Jam in Spotify…';window.open(link,'_blank','noopener,noreferrer');});\r\n  const onVisible=()=>{if(document.hidden&&dialog.open)dialog.close();else if(!document.hidden&&!dialog.open&&!returnResult&&pendingFresh()&&accountAvailable())open();};document.addEventListener('visibilitychange',onVisible);\r\n  const onPageHide=()=>{if(dialog.open)dialog.close();};window.addEventListener('pagehide',onPageHide);\r\n  const onAccountCleared=()=>{stop();actualDJ.spotifyState(false);$('.sp-playlist-list').replaceChildren();showNow({track:null});$('.sp-account-name').textContent='NOT CONNECTED';if(dialog.open)dialog.close();connected=false;const label=document.getElementById('spotifyNowPlayingLabel');if(label)label.textContent='actual.dj';};\r\n  window.addEventListener('myr5:account-cleared',onAccountCleared);\r\n  const onHeader=event=>{if(event.target.closest?.('#spotifyNowPlayingOpen'))open();};document.addEventListener('click',onHeader);\r\n  window.myr5Menus={...window.myr5Menus,spotify:open};\r\n  // Account initialization may clear an anonymous shell before restoring the signed-in account.\r\n  // Keep the OAuth result until that restoration has completed, then reopen the terminal.\r\n  const onAccountReady=()=>{queueMicrotask(()=>{\r\n    if(disposed||!accountAvailable())return;\r\n    if(returnResult){const params=new URLSearchParams(location.search);params.delete('spotify');history.replaceState(history.state,'',location.pathname+(params.size?`?${params}`:'')+location.hash);actualDJ.showSpotify();if(!dialog.open)open();else void load();returnResult=null;}\r\n    else if(dialog.open)void load();\r\n    else if(pendingFresh())open();\r\n  });};\r\n  window.addEventListener('myr5:account-ready',onAccountReady);\r\n  if(returnResult)queueMicrotask(()=>{if(accountAvailable())onAccountReady();else open();});\r\n  return {open,dispose(){actualDJ.dispose();disposed=true;stop();window.removeEventListener('myr5:account-ready',onAccountReady);document.removeEventListener('visibilitychange',onVisible);window.removeEventListener('pagehide',onPageHide);window.removeEventListener('myr5:account-cleared',onAccountCleared);document.removeEventListener('click',onHeader);if(window.myr5Menus?.spotify===open)delete window.myr5Menus.spotify;dialog.remove();}};\r\n}\r\n","standalone-account-context.mjs":"// Standalone pages obtain ownership only from a verified account read. They do\r\n// not treat device appearance, a cached ID or a login-provider flag as identity.\r\nimport {authFetch} from './auth-client.mjs';\r\nimport {mergeVerifiedPerformance} from './performance-progress.mjs';\r\nimport {authTransitions} from './auth-transition.mjs';\r\nexport function createStandaloneAccountContext({request=authFetch,transitions=authTransitions(),target=globalThis.window??globalThis,timeoutMs=8000}={}){\r\n let active=null,generation=0,disposed=false;\r\n const emit=(name,detail)=>{const event=new Event(name);if(detail!==undefined)Object.defineProperty(event,'detail',{value:detail});target.dispatchEvent?.(event);};\r\n function clear(){generation++;active=null;target.myr5AuthenticatedAccount=null;target.coachEntitlements=null;target.coachProgress=null;emit('myr5:account-cleared');}\r\n clear();const unsubscribe=transitions.subscribe(clear);\r\n async function read({ticket=transitions.capture()}={}){\r\n  const current=++generation,controller=new AbortController();let timer;\r\n  const deadline=new Promise((_,reject)=>{timer=setTimeout(()=>{controller.abort();reject(Object.assign(Error('Account service timed out.'),{code:'account_timeout'}));},timeoutMs);});\r\n  try{\r\n   const response=await Promise.race([request('/api/account?core=1',{signal:AbortSignal.any([ticket.signal,controller.signal]),credentials:'same-origin',cache:'no-store'}),deadline]);\r\n   transitions.assertCurrent(ticket);if(disposed||current!==generation)return null;\r\n   if(!response.ok||response.redirected||!response.headers?.get('content-type')?.includes('application/json'))throw Error('Verified account unavailable.');\r\n   const account=await Promise.race([response.json(),deadline]);transitions.assertCurrent(ticket);\r\n   if(disposed||current!==generation)return null;\r\n   if(typeof account?.user?.id!=='string'||!account.user.id||account.user.id.length>128||!Number.isSafeInteger(account.dataEpoch)||account.dataEpoch<1)throw Error('Invalid verified account.');\r\n   if(active&&(active.user.id!==account.user.id||active.dataEpoch!==account.dataEpoch)){transitions.invalidate();return null;}\r\n   mergeVerifiedPerformance(account);active=account;target.myr5AuthenticatedAccount=account;target.coachEntitlements=account.entitlements??null;target.coachProgress=account.progress??null;emit('myr5:account-ready',account);return account;\r\n  }catch{\r\n   if(!disposed&&current===generation&&transitions.isCurrent(ticket))clear();return null;\r\n  }finally{clearTimeout(timer);controller.abort();}\r\n }\r\n async function refresh(){try{return await read({ticket:transitions.beginRefresh()});}catch{if(!disposed)clear();return null;}}\r\n return {read,refresh,clear,get account(){return active;},dispose(){disposed=true;generation++;unsubscribe?.();}};\r\n}\r\n","streak-forgiveness.mjs":"// D12 streak/forgiveness rules (see DECISIONS.md D12, \"D12 detailed rules\" R1-R7,\r\n// and \"D12 letters\" table). This is the ONE place that implements those rules —\r\n// combat.mjs's loginStreak() delegates here instead of re-deriving them; do not\r\n// add a second streak calculation anywhere else.\r\n//\r\n// Input: an ordered array of day records `{date, status, timezone?}` where\r\n// `date` is that day's LOCAL calendar date as 'YYYY-MM-DD' (R1: already resolved\r\n// by the caller using the timezone recorded for that day — this module does no\r\n// timezone math of its own, it just groups by calendar date) and `status` is\r\n// 'kept' or 'missed'. `timezone` is accepted and ignored by the date math (it\r\n// exists so callers can carry it through for their own records); a repeated\r\n// `date` (R2, e.g. a day that repeats after crossing back over the date line)\r\n// is folded into a single day, keeping the first entry seen for that date.\r\n//\r\n// Output: one result per unique day, in input order:\r\n//   { date, streak, forgivenWeeksInARow, forgivenessAvailableThisWeek,\r\n//     auraMilestone, lettersFired }\r\n// matching plan/muse/streak-test-vectors.json's expectedAfterEachDay shape.\r\n\r\nconst FORGIVE_BY_START_COUNT = ['forgive-1', 'forgive-2', 'forgive-3-limit'];\r\n\r\nconst toUTCDate = ds => { const [y, m, d] = ds.split('-').map(Number); return new Date(Date.UTC(y, m - 1, d)); };\r\nconst isoDate = dt => dt.toISOString().slice(0, 10);\r\n// Monday-start week key for a local calendar date (R2).\r\nconst weekOf = ds => { const dt = toUTCDate(ds); dt.setUTCDate(dt.getUTCDate() - (dt.getUTCDay() + 6) % 7); return isoDate(dt); };\r\nconst isSunday = ds => toUTCDate(ds).getUTCDay() === 0;\r\n\r\nexport function evaluateStreakDays(days) {\r\n let streak = 0, cnt = 0, forgivenThisWeek = false, brokeThisWeek = false, cntStartWeek = 0, curWeek = null;\r\n const seen = new Set(), out = [];\r\n for (const day of days) {\r\n  const ds = day.date;\r\n  if (seen.has(ds)) continue;\r\n  seen.add(ds);\r\n  const week = weekOf(ds);\r\n  if (week !== curWeek) { curWeek = week; forgivenThisWeek = false; brokeThisWeek = false; cntStartWeek = cnt; }\r\n  const letters = [];\r\n  let keptOrForgiven = false;\r\n  if (day.status === 'kept') {\r\n   streak += 1; keptOrForgiven = true;\r\n  } else {\r\n   // R4: forgiven only if nothing forgiven yet this week, the 3-week limit\r\n   // hasn't been hit, and the streak hasn't already broken this week (R4c).\r\n   const forgivable = !forgivenThisWeek && cnt < 3 && !brokeThisWeek;\r\n   if (forgivable) {\r\n    streak += 1; forgivenThisWeek = true; keptOrForgiven = true;\r\n    const letter = FORGIVE_BY_START_COUNT[cntStartWeek];\r\n    if (letter) letters.push(letter);\r\n   } else {\r\n    letters.push(cnt === 3 && !forgivenThisWeek && !brokeThisWeek ? 'streak-broken-after-limit' : 'streak-broken');\r\n    streak = 0; cnt = 0; brokeThisWeek = true;\r\n   }\r\n  }\r\n  // R7: aura milestone on a kept-or-forgiven day whose streak is a positive multiple of 5.\r\n  const auraMilestone = (keptOrForgiven && streak > 0 && streak % 5 === 0) ? streak : null;\r\n  if (auraMilestone === 5 || auraMilestone === 10 || auraMilestone === 15) letters.push(`milestone-${auraMilestone}`);\r\n  if (isSunday(ds)) {\r\n   // R5: forgivenWeeksInARow only changes at week end.\r\n   if (brokeThisWeek) cnt = 0;\r\n   else if (forgivenThisWeek) cnt += 1;\r\n   else { const hadCount = cnt > 0; cnt = 0; if (hadCount) letters.push('forgive-restored'); }\r\n   if (cnt === 3) letters.push('forgive-off-warning');\r\n  }\r\n  // R6, reported after every day (its Sunday value already reflects the week-end update above).\r\n  const forgivenessAvailableThisWeek = !forgivenThisWeek && cnt < 3 && !brokeThisWeek;\r\n  out.push({ date: ds, streak, forgivenWeeksInARow: cnt, forgivenessAvailableThisWeek, auraMilestone, lettersFired: letters });\r\n }\r\n return out;\r\n}\r\n\r\n// Adapter for the account server's login_days rows (integer day numbers, UTC,\r\n// same as combat.mjs dayAt). Returns one result per day from the account's\r\n// first known day through `today` -- the full history, because a letter (e.g.\r\n// a forgiven miss) fires on the day it happens even if nothing calls in again\r\n// until days later. Empty when `today` itself is not a kept day.\r\n// ponytail: login_days has no timezone column yet, so each UTC day number is\r\n// treated as the local date (R1/R2 not yet per-user). Upgrade path: record the\r\n// device timezone with each login and pass real local dates to\r\n// evaluateStreakDays instead of calling this.\r\nexport function evaluateStreakHistory(dayNumbers, today) {\r\n const kept = new Set(dayNumbers.map(Number));\r\n if (!kept.has(today)) return [];\r\n const records = [];\r\n for (let d = Math.min(today, ...kept); d <= today; d++) records.push({ date: isoDate(new Date(d * 86400000)), status: kept.has(d) ? 'kept' : 'missed' });\r\n return evaluateStreakDays(records);\r\n}\r\n","sw.js":"const SHELL='myr5-shell-22d1026e5a1d6f2441dd',VOICE='myr5-voice-approved-v2';\r\n// Filled from the complete production asset inventory, with content identities.\r\nconst ASSETS=/* OFFLINE_ASSETS */ [];\r\n// D34 post-download package: every other deployed runtime file, each tagged with its Downloads-menu\r\n// group (scripts/offline-assets.mjs). Fetched on demand into a small LRU until the user downloads its\r\n// group into this release's own package cache. A roster body fetched on demand is kept there too.\r\nconst OPTIONAL_ASSETS=/* OPTIONAL_ASSETS */ [];\r\nconst BUILD=SHELL.slice('myr5-shell-'.length);\r\nconst OPTIONAL='myr5-optional-'+BUILD,OPTIONAL_LIMIT=64*1024*1024;\r\nconst PACKAGE='myr5-package-'+BUILD,PACKAGE_INDEX='/__myr5_package__';\r\nconst OPTIONAL_BY_URL=new Map(OPTIONAL_ASSETS.map(asset=>[asset.url,asset]));\r\nlet optionalWrite=Promise.resolve();\r\nasync function saveOptional(asset,response){\r\n if(asset.bytes>OPTIONAL_LIMIT)return;\r\n const copy=response.clone();\r\n const write=optionalWrite.then(async()=>{\r\n  const cache=await caches.open(OPTIONAL),keys=await cache.keys();\r\n  let used=keys.reduce((sum,key)=>sum+(OPTIONAL_BY_URL.get(new URL(key.url).pathname)?.bytes??0),0);\r\n  if(await cache.match(asset.url))return;\r\n  for(const key of keys){if(used+asset.bytes<=OPTIONAL_LIMIT)break;used-=OPTIONAL_BY_URL.get(new URL(key.url).pathname)?.bytes??0;await cache.delete(key);}\r\n  await cache.put(asset.url,copy);\r\n });\r\n optionalWrite=write.catch(()=>{});return optionalWrite;\r\n}\r\nconst INDEX='/__myr5_offline_assets__';\r\n// Every shell and package cache records what it holds (url and integrity). A file is reused across\r\n// releases only when its integrity is unchanged, so a package is never half old and half new.\r\nconst indexes=new Map();\r\nfunction cacheIndex(name){\r\n if(!indexes.has(name))indexes.set(name,caches.open(name).then(cache=>cache.match(name.startsWith('myr5-shell-')?INDEX:PACKAGE_INDEX)).then(saved=>saved.json()).then(list=>new Map(list.map(asset=>[asset.url,asset.integrity])),()=>new Map()));\r\n return indexes.get(name);\r\n}\r\nasync function localCopy(asset,lru=true){\r\n for(const name of await caches.keys())if(name!==PACKAGE&&/^myr5-(?:shell|package)-/.test(name)&&(await cacheIndex(name)).get(asset.url)===asset.integrity){\r\n  const hit=await caches.match(asset.url,{cacheName:name});if(hit)return {name,hit};\r\n }\r\n const hit=lru&&await caches.match(asset.url,{cacheName:OPTIONAL});return hit&&{name:OPTIONAL,hit};\r\n}\r\nconst sri=async body=>'sha256-'+btoa(String.fromCharCode(...new Uint8Array(await crypto.subtle.digest('SHA-256',body))));\r\nasync function packageCache(){\r\n const cache=await caches.open(PACKAGE);\r\n if(!await cache.match(PACKAGE_INDEX))await cache.put(PACKAGE_INDEX,new Response(JSON.stringify(OPTIONAL_ASSETS),{headers:{'Content-Type':'application/json'}}));\r\n return cache;\r\n}\r\nconst size=list=>list.reduce((sum,asset)=>sum+asset.bytes+(asset.contains||0),0);\r\n// What the chosen groups still need from the network, plus every group's size and what it still\r\n// misses (the Downloads menu). No group list (older pages) means the whole package. Unchanged files\r\n// already on the device (older package, older shell) are moved in first, whatever their group; the\r\n// on-demand LRU (64 MiB, kept for people who never asked for a group) is adopted only for a chosen\r\n// group, once the user has chosen to download.\r\nasync function packagePlan(adopt,groups){\r\n const cache=await packageCache(),chosen=Array.isArray(groups)?new Set(groups):null,wanted=asset=>!chosen||chosen.has(asset.group);\r\n const have=new Set((await cache.keys()).map(key=>new URL(key.url).pathname)),missing=[],sizes={};let total=0;\r\n for(const asset of OPTIONAL_ASSETS){\r\n  const group=sizes[asset.group]??={files:0,total:0,remaining:0};let need=[];\r\n  if(asset.contains!==undefined)need=await voiceMissing(asset);\r\n  else if(!have.has(asset.url)){\r\n   const copy=await localCopy(asset,adopt&&wanted(asset));\r\n   if(!copy)need=[asset];\r\n   else{await cache.put(asset.url,copy.hit);if(!copy.name.startsWith('myr5-shell-'))await(await caches.open(copy.name)).delete(asset.url);}\r\n  }\r\n  group.files++;group.total+=size([asset]);group.remaining+=size(need);\r\n  if(wanted(asset)){total+=size([asset]);missing.push(...need);}\r\n }\r\n // Older packages are spent once the chosen groups are complete; this release's LRU still serves\r\n // groups nobody chose, until every group is here.\r\n const complete=Object.values(sizes).every(group=>!group.remaining);\r\n if(!missing.length)for(const name of await caches.keys())if(name!==PACKAGE&&(name.startsWith('myr5-package-')||name.startsWith('myr5-optional-')&&(name!==OPTIONAL||complete)))await caches.delete(name);\r\n return {type:'PACKAGE_PLAN',cache:PACKAGE,total,missing,remaining:size(missing),groups:sizes};\r\n}\r\n// Voice clips live in the update-stable voice cache. Their identities come from this release's\r\n// verified voice manifest; until that is saved, the manifest itself is what is missing.\r\nasync function voiceMissing(entry){\r\n const voice=await caches.open(VOICE),key=entry.url+'?v='+BUILD,saved=await voice.match(key);\r\n let files;try{const body=await saved.arrayBuffer();if(await sri(body)===entry.integrity)files=JSON.parse(new TextDecoder().decode(body)).files;}catch{}\r\n if(!Array.isArray(files))return [{...entry,cache:VOICE,key}];\r\n const have=new Set((await voice.keys()).map(request=>request.url));\r\n return files.filter(file=>!have.has(new URL(file.url,self.location.origin).href)).map(file=>({...file,cache:VOICE}));\r\n}\r\nlet planning=Promise.resolve();\r\nconst ASSET_BY_URL=new Map(ASSETS.map(asset=>[asset.url,asset]));\r\nconst downloadProgress={type:'OFFLINE_PROGRESS',files:0,totalFiles:ASSETS.length,bytes:0,totalBytes:ASSETS.reduce((n,a)=>n+a.bytes,0)};\r\nasync function reportDownload(){\r\n for(const client of await self.clients.matchAll?.({type:'window',includeUncontrolled:true})||[])try{client.postMessage(downloadProgress);}catch{}\r\n}\r\n\r\nfunction assetPath(path){\r\n if(path==='/'||path==='/index.html'||path==='/index')return '/pose.html';\r\n if(path==='/icons/coach-512.png')return '/icons/myr5-alien-512.png';\r\n if(ASSET_BY_URL.has(path)||OPTIONAL_BY_URL.has(path))return path;\r\n const html=path.endsWith('/')?path+'index.html':path+'.html';\r\n if(ASSET_BY_URL.has(html)||OPTIONAL_BY_URL.has(html))return html;\r\n return path+'/index.html';\r\n}\r\nfunction offlineResponse(response){\r\n // Sites serves /pose.html through its clean /pose URL. A fetched response\r\n // retains redirected=true even after CacheStorage saves it. Chrome rejects\r\n // that response for a navigation whose redirect mode is manual (ERR_FAILED).\r\n // Keep the verified bytes, but give the offline page its own response.\r\n const headers=new Headers(response.headers);\r\n headers.delete('content-encoding');headers.delete('content-length');\r\n return new Response(response.body,{status:response.status,statusText:response.statusText,headers});\r\n}\r\n// Music stays cached as a verified complete file. Slice only the response sent to the media element.\r\n// Same single-range semantics as scripts/actual-dj-preview.mjs; no provider audio is intercepted.\r\nasync function musicResponse(request,response,asset){\r\n if(!/^\\/audio\\/music\\/[^/]+\\.m4a$/.test(asset.url)||response.status!==200)return response;\r\n const size=asset.bytes;if(!Number.isSafeInteger(size)||size<=0||size>1024*1024)return new Response('Music asset exceeds byte-serving bounds',{status:502});\r\n const reader=response.body?.getReader(),chunks=[];let length=0;\r\n if(reader)try{while(true){const {done,value}=await reader.read();if(done)break;length+=value.byteLength;if(length>size){await reader.cancel();return new Response('Music asset size mismatch',{status:502});}chunks.push(value);}}finally{reader.releaseLock();}\r\n if(length!==size)return new Response('Music asset size mismatch',{status:502});\r\n const bytes=new Uint8Array(size);let offset=0;for(const chunk of chunks){bytes.set(chunk,offset);offset+=chunk.byteLength;}\r\n const headers=new Headers(response.headers);headers.delete('content-encoding');headers.delete('content-range');headers.set('accept-ranges','bytes');headers.set('content-length',String(size));\r\n if(request.method==='HEAD')return new Response(null,{headers});\r\n const range=request.headers.get('range'),validator=request.headers.get('if-range');\r\n const date=Date.parse(validator),modified=Date.parse(headers.get('last-modified'));\r\n const matches=!validator||(validator.startsWith('\"')?validator===headers.get('etag'):!validator.startsWith('W/')&&Number.isFinite(date)&&Number.isFinite(modified)&&validator===headers.get('last-modified'));\r\n if(!range||!matches)return new Response(bytes,{headers});\r\n const match=range.match(/^bytes=(\\d*)-(\\d*)$/i);let start,end;\r\n if(match&&(match[1]||match[2])){\r\n  if(match[1]){start=Number(match[1]);end=match[2]?Number(match[2]):size-1;}\r\n  else{const suffix=Number(match[2]);start=suffix>0?Math.max(0,size-suffix):size;end=size-1;}\r\n }\r\n if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||start>=size||end<start){headers.set('content-range',`bytes */${size}`);headers.set('content-length','0');return new Response(null,{status:416,headers});}\r\n end=Math.min(end,size-1);headers.set('content-range',`bytes ${start}-${end}/${size}`);headers.set('content-length',String(end-start+1));\r\n return new Response(bytes.subarray(start,end+1),{status:206,headers});\r\n}\r\nasync function downloadAsset(asset){\r\n // Edge security products can rewrite HTML in transit. Keep integrity strict for\r\n // executable and binary assets, but allow those harmless HTML transformations.\r\n const html=new URL(asset.url,self.location.origin).pathname.endsWith('.html');\r\n const response=await fetch(new Request(new URL(asset.url,self.location.origin),{cache:'reload',...(html?{}:{integrity:asset.integrity})}));\r\n const destination=response.url?new URL(response.url):null;\r\n const redirectedElsewhere=response.redirected&&(!destination||destination.origin!==self.location.origin||assetPath(destination.pathname)!==assetPath(asset.url));\r\n if(!response.ok||redirectedElsewhere||/^\\/audio\\/music\\/[^/]+\\.m4a$/.test(asset.url)&&response.status!==200)throw Error('Could not save Coach asset: '+asset.url);\r\n return offlineResponse(response);\r\n}\r\nasync function installAssets(){\r\n if(!ASSETS.length)throw Error('Build Coach before installing its offline assets.');\r\n const cache=await caches.open(SHELL),previous=[];\r\n for(const name of (await caches.keys()).reverse()){\r\n  if(!name.startsWith('myr5-shell-')||name===SHELL)continue;\r\n  const old=await caches.open(name),saved=await old.match(INDEX);\r\n  if(saved)try{previous.push({cache:old,files:new Map((await saved.json()).map(asset=>[asset.url,asset.integrity]))});}catch{}\r\n }\r\n let next=0,failed=false;\r\n const results=await Promise.allSettled(Array.from({length:4},async()=>{\r\n  while(!failed&&next<ASSETS.length){\r\n   const asset=ASSETS[next++];\r\n   try{\r\n    let response;\r\n    for(const old of previous)if(old.files.get(asset.url)===asset.integrity){response=await old.cache.match(asset.url);if(response?.ok)break;response=null;}\r\n    await cache.put(asset.url,response?offlineResponse(response):await downloadAsset(asset));\r\n    downloadProgress.files++;downloadProgress.bytes+=asset.bytes;await reportDownload();\r\n   }catch(error){failed=true;throw error;}\r\n  }\r\n }));\r\n const failure=results.find(result=>result.status==='rejected');\r\n try{\r\n  if(failure)throw failure.reason;\r\n  await cache.put(INDEX,new Response(JSON.stringify(ASSETS),{headers:{'Content-Type':'application/json'}}));\r\n }catch(error){await caches.delete(SHELL);throw error;}\r\n}\r\nself.addEventListener('install',event=>event.waitUntil(installAssets()));\r\n// skipWaiting() only activates once the old worker has finished its in-flight fetches, and a coach model\r\n// downloading on a phone can hold it for minutes. Pages stay frozen for the update only this long; after\r\n// that they are released (UPDATE_ABORT) and the update lands by itself once the old worker is idle.\r\nconst ACTIVATION_WAIT=8000;let markActivated;const activation=new Promise(resolve=>markActivated=resolve);\r\nself.addEventListener('activate',event=>{markActivated(true);event.waitUntil((async()=>{\r\n const names=await caches.keys();let previous=null;\r\n for(const name of [...names].reverse())if(name.startsWith('myr5-shell-')&&name!==SHELL&&await(await caches.open(name)).match(INDEX)){previous=name;break;}\r\n for(const name of names){\r\n  if(name.startsWith('myr5-shell-')&&name!==SHELL&&name!==previous)await caches.delete(name);\r\n  if(name.startsWith('myr5-optional-')&&name!==OPTIONAL&&name!=='myr5-optional-'+previous?.slice('myr5-shell-'.length))await caches.delete(name);\r\n }\r\n // Keep the newest older package until this release's package is complete (its files move over).\r\n for(const name of names.filter(name=>name.startsWith('myr5-package-')&&name!==PACKAGE).slice(0,-1))await caches.delete(name);\r\n const voice=await caches.open(VOICE);\r\n for(const key of await voice.keys()){const url=new URL(key.url);if(url.pathname==='/voice/manifest.json'&&url.searchParams.get('v')!==BUILD)await voice.delete(key);}\r\n await self.clients.claim();\r\n})());});\r\nlet updateAttempt=null;\r\nfunction updateReply(client,type,id){return new Promise(resolve=>{\r\n const channel=new MessageChannel();let done=false;\r\n const finish=value=>{if(done)return;done=true;clearTimeout(timer);channel.port1.close();resolve(value?.safe===true&&value.protocol===2&&value.id===id);};\r\n const timer=setTimeout(()=>finish(null),4000);\r\n channel.port1.onmessage=event=>finish(event.data);\r\n try{client.postMessage({type,id},[channel.port2]);}catch{finish(null);}\r\n});}\r\nasync function prepareUpdate(source){\r\n const clients=await self.clients.matchAll({type:'window',includeUncontrolled:true});\r\n if(!source?.id||!clients.some(client=>client.id===source.id))return {activated:false,reason:'busy'};\r\n const id=crypto.randomUUID();let activated=false;\r\n try{\r\n  if(!(await Promise.all(clients.map(client=>updateReply(client,'UPDATE_SAFETY_CHECK',id)))).every(Boolean))return {activated:false,reason:'busy'};\r\n  if(!(await Promise.all(clients.map(client=>updateReply(client,'UPDATE_CONFIRM',id)))).every(Boolean))return {activated:false,reason:'busy'};\r\n  // New or navigated windows must take part in their own fresh preparation.\r\n  const current=await self.clients.matchAll({type:'window',includeUncontrolled:true});\r\n  if(current.length!==clients.length||current.some(client=>!clients.some(old=>old.id===client.id&&old.url===client.url)))return {activated:false,reason:'busy'};\r\n  // Not awaited: Chromium resolves skipWaiting() only when activation starts, WebKit at once.\r\n  self.skipWaiting().catch(()=>{});\r\n  if(!await Promise.race([activation,new Promise(resolve=>setTimeout(()=>resolve(!self.registration.waiting),ACTIVATION_WAIT))]))return {activated:false,reason:'busy'};\r\n  activated=true;return {activated:true};\r\n }finally{if(!activated)for(const client of clients)try{client.postMessage({type:'UPDATE_ABORT',id});}catch{}}\r\n}\r\nself.addEventListener('message',event=>{\r\n if(event.data?.type==='OFFLINE_STATUS'){event.source?.postMessage(downloadProgress);return;}\r\n if(event.data?.type==='PACKAGE_PLAN'){\r\n  const plan=planning.then(()=>packagePlan(event.data.adopt===true,event.data.groups));planning=plan.catch(()=>{});\r\n  event.waitUntil(plan.then(value=>event.ports[0]?.postMessage(value),()=>event.ports[0]?.postMessage({type:'PACKAGE_PLAN',error:true})));return;\r\n }\r\n if(event.data?.type!=='PREPARE_UPDATE')return;\r\n if(updateAttempt){event.ports[0]?.postMessage({activated:false,reason:'busy'});return;}\r\n updateAttempt=prepareUpdate(event.source).catch(()=>({activated:false,reason:'busy'}));\r\n event.waitUntil(updateAttempt.then(result=>event.ports[0]?.postMessage(result)).finally(()=>{updateAttempt=null;}));\r\n});\r\nself.addEventListener('fetch',event=>{\r\n const request=event.request,url=new URL(request.url);\r\n if((request.method!=='GET'&&!(request.method==='HEAD'&&/^\\/audio\\/music\\/[^/]+\\.m4a$/.test(url.pathname)))||url.origin!==self.location.origin||url.pathname.startsWith('/api/')||url.pathname.includes('with-chatgpt')||url.pathname==='/callback'||url.pathname.startsWith('/signin')||url.pathname.startsWith('/source.json')||url.pathname==='/repair-coach'||url.pathname==='/recover'||url.pathname==='/recover.html'||request.headers.has('x-myr5-package'))return;\r\n if(url.pathname.startsWith('/voice/')){\r\n  event.respondWith((async()=>{const cache=await caches.open(VOICE),cached=await cache.match(request);if(cached)return cached;const response=await fetch(request);if(response.ok&&!response.redirected)await cache.put(request,response.clone());return response;})());return;\r\n }\r\n // Canonical paths also cover release query strings and customizer directory\r\n // links. Cached art is returned immediately, without a network revalidation.\r\n const path=assetPath(url.pathname);\r\n const asset=ASSET_BY_URL.get(path)||OPTIONAL_BY_URL.get(path);\r\n if(!asset)return;\r\n const optional=OPTIONAL_BY_URL.has(path),body=asset.group?.startsWith('bodies-'),kept=body||asset.group==='starter';\r\n // A roster body, or a portal scene's Starter art (W2-2O), that isn't on the device yet: fetched alone and\r\n // kept in the package; its page shows \"Downloading…\", or says it's missing when offline (post-download.mjs).\r\n const tell=state=>kept&&event.clientId&&event.waitUntil(self.clients.get(event.clientId).then(client=>client?.postMessage({type:body?'BODY_DOWNLOAD':'STARTER_DOWNLOAD',url:path,state}),()=>{}));\r\n event.respondWith((async()=>{\r\n  // Opening a cache creates it, so the package cache is only read by name here.\r\n  const cached=optional?await caches.match(path,{cacheName:PACKAGE})||(await localCopy(asset))?.hit:await(await caches.open(SHELL)).match(path);\r\n  if(cached)return musicResponse(request,offlineResponse(cached),asset);\r\n  tell('start');\r\n  let response;\r\n  try{response=await downloadAsset(asset);}catch{\r\n   tell('unavailable');\r\n   // Never mix an unverified new executable into an older installed shell.\r\n   return new Response('This resource is unavailable. Reconnect or update Coach.',{status:503});\r\n  }\r\n  if(kept){const copy=response.clone();event.waitUntil(packageCache().then(cache=>cache.put(path,copy)).catch(()=>{}).then(()=>tell('done')));}\r\n  else if(optional)event.waitUntil(saveOptional(asset,response));else try{await(await caches.open(SHELL)).put(path,response.clone());}catch{}\r\n  return musicResponse(request,response,asset);\r\n })());\r\n});\r\nself.addEventListener('push',event=>{\r\n let data={title:'MYR5 Coach',body:'Your coach has a reminder.',url:'/pose.html'};try{data={...data,...event.data.json()};}catch{}\r\n event.waitUntil((async()=>{\r\n  // D23: an Armie letter push is never shown during camera-only mode or\r\n  // mid-set. The busy window holds it and shows it when the set ends\r\n  // (armie-letters-client.mjs); the letter itself already lives in the inbox.\r\n  if(data.kind==='myr5-armie-letter'){\r\n   for(const client of await self.clients.matchAll({type:'window',includeUncontrolled:true}))\r\n    if(await clientAnswers(client,'ARMIE_NOTIFY_CHECK',reply=>reply?.blocked===true)){client.postMessage({type:'ARMIE_LETTER_HELD',letter:data});return;}\r\n  }\r\n  return self.registration.showNotification(String(data.title).slice(0,80),{body:String(data.body).slice(0,200),icon:'/icons/myr5-alien-192.png',badge:'/icons/myr5-alien-192.png',tag:data.tag||'myr5-reminder',data:{url:data.url,kind:data.kind},...(data.kind==='app-update'?{actions:[{action:'download',title:'Download update'}]}:{}),renotify:false});\r\n })());\r\n});\r\nfunction clientAnswers(client,type,accept){\r\n return new Promise(resolve=>{\r\n  const channel=new MessageChannel();\r\n  const finish=handled=>{clearTimeout(timer);channel.port1.close();resolve(handled);};\r\n  const timer=setTimeout(()=>finish(false),1000);\r\n  channel.port1.onmessage=event=>finish(accept(event.data));\r\n  try{client.postMessage({type},[channel.port2]);}catch{finish(false);}\r\n });\r\n}\r\nasync function offerUpdate(client){\r\n if(await clientAnswers(client,'APP_UPDATE_AVAILABLE',data=>data?.handled===true))return true;\r\n // Older working pages do not acknowledge APP_UPDATE_AVAILABLE. Their safety\r\n // reply still proves they are alive, including a busy workout answering false.\r\n return clientAnswers(client,'UPDATE_SAFETY_CHECK',data=>typeof data?.safe==='boolean');\r\n}\r\n// W2-2A (#18): a reminder opens its route in the app: meal -> #food, workout -> #workout. Older ?panel= links\r\n// (other reminders, the test push) map to the same routes; anything else opens unchanged.\r\nconst NOTIFY_ROUTES={'reminder-meal':'food','reminder-workout':'workout'},PANEL_ROUTES={meals:'food',reminders:'reminders',account:'scoreboard',history:'history',install:'install'};\r\nfunction routeUrl(url,kind){\r\n if(!['/pose.html','/pose','/'].includes(url.pathname))return url;\r\n const route=NOTIFY_ROUTES[kind]||PANEL_ROUTES[url.searchParams.get('panel')];if(!route)return url;\r\n const routed=new URL('/pose.html',url);url.searchParams.forEach((value,key)=>{if(key!=='panel')routed.searchParams.set(key,value);});routed.hash=route;return routed;\r\n}\r\nself.addEventListener('notificationclick',event=>{event.notification.close();event.waitUntil((async()=>{\r\n let url;try{url=new URL(event.notification.data?.url||'/pose.html',self.location.origin);}catch{return;}\r\n if(url.origin!==self.location.origin)return;\r\n const update=event.notification.data?.kind==='app-update',all=await self.clients.matchAll({type:'window',includeUncontrolled:true});\r\n if(update){\r\n  // An old or failed page can accept postMessage without displaying anything.\r\n  // Open independent recovery if it cannot acknowledge the update. Leave the\r\n  // existing window and any unsaved workout or form intact.\r\n  for(const client of all)if(new URL(client.url).origin===url.origin&&['/pose.html','/pose','/'].includes(new URL(client.url).pathname)){\r\n   await client.focus();if(await offerUpdate(client))return;\r\n  }\r\n  return self.clients.openWindow(new URL('/repair-coach',self.location.origin).href);\r\n }\r\n url=routeUrl(url,event.notification.data?.kind);\r\n for(const client of all)if(new URL(client.url).origin===url.origin){await client.navigate(url.href);return client.focus();}\r\n return self.clients.openWindow(url.href);\r\n})());});\r\n","unlock-gate.mjs":"// \"Why is this locked\" sheet + the one-time celebration. The rule itself is unlockStatus() in performance-progress.mjs.\r\nimport {readPerformanceProgress,unlockStatus} from './performance-progress.mjs';\r\nimport {paintStyleText} from './workout-symbols.mjs';\r\nexport const UNLOCK_SEEN_KEY='myr5-all-unlocked-seen-v1';\r\nexport const UNLOCK_MESSAGE='Finish a workout, a meditation and a food log to unlock every coach and workout style.';\r\nconst STYLE='position:fixed;inset:auto 12px 12px 12px;z-index:99999;margin:0;max-width:none;border:1px solid #6cf;border-radius:16px;background:#101828;color:#fff;padding:16px;font:15px system-ui;box-shadow:0 8px 40px #000a';\r\nconst ITEMS=[['workout','Workout (Subway Burpees counts)'],['meditation','Meditation'],['food','Food log']];\r\nlet sheet;\r\nexport function showUnlockGate({onCardio}={}){\r\n const status=unlockStatus(readPerformanceProgress());\r\n sheet?.remove();sheet=document.createElement('dialog');sheet.id='unlockGate';sheet.setAttribute('style',STYLE);\r\n const p=document.createElement('p');p.textContent=UNLOCK_MESSAGE;p.setAttribute('role','status');\r\n const ul=document.createElement('ul');ul.style.cssText='list-style:none;padding:0;margin:8px 0';\r\n for(const [key,label] of ITEMS){const li=document.createElement('li');li.dataset.item=key;li.dataset.done=String(status[key]);paintStyleText(li,`${status[key]?'\\u2713':'\\u25CB'} ${label}`);ul.append(li);}\r\n const row=document.createElement('div');row.style.cssText='display:flex;gap:8px';\r\n const cardio=document.createElement('button');cardio.type='button';cardio.dataset.gate='cardio';cardio.textContent='Play Subway Burpees';\r\n const close=document.createElement('button');close.type='button';close.textContent='Close';\r\n cardio.onclick=()=>{sheet.close();onCardio?.();};close.onclick=()=>sheet.close();\r\n row.append(cardio,close);sheet.append(p,ul,row);document.body.append(sheet);sheet.showModal();\r\n}\r\n/** Celebrate once, the first time progress flips to fully unlocked (silent for already-unlocked saves). */\r\nexport function watchUnlock(storage=globalThis.localStorage){\r\n const check=()=>{try{if(!unlockStatus(readPerformanceProgress()).done||storage.getItem(UNLOCK_SEEN_KEY))return;storage.setItem(UNLOCK_SEEN_KEY,'1');celebrate();}catch{}};\r\n addEventListener('myr5:performance-progress',check);\r\n}\r\nfunction celebrate(){\r\n const t=document.createElement('div');t.id='unlockToast';t.setAttribute('role','status');t.setAttribute('style',STYLE.replace('border-radius:16px','border-radius:16px;text-align:center'));\r\n t.textContent='\\u2728 Everything unlocked: all coaches, workout styles and movements!';document.body.append(t);setTimeout(()=>t.remove(),5000);\r\n}\r\n","unlock-ledger.mjs":"import {gameCacheKey,isCurrentGameAccount} from './modules/coach-world/game-generation-client.mjs';\r\n// Global battle-pass ledger (rank 6b) for the items unlock-store.ts doesn't hold: weapons, pets,\r\n// boss looks, specials, auras, Food bonuses (D32). Textures/colours/palettes stay in unlock-store.ts. Same API\r\n// shape as unlock-store.ts, one localStorage key, not per-coach (unlocks are global).\r\nimport {performanceOwner} from './performance-progress.mjs';\r\nimport {markPending,markPendingMany,ACCOUNT_SCOPED_LEDGER_KINDS} from './unlock-pending.mjs';\r\nexport {ACCOUNT_SCOPED_LEDGER_KINDS};\r\nexport const LEDGER_KEY='myr5-battle-pass-ledger-v1';\r\nexport const LEDGER_KINDS=Object.freeze(['weapon','pet','boss-texture','boss-skin','boss-unlock','special','aura','bonus','creature-skin','ship','reward-pack']);\r\n\r\nfunction read(){\r\n try{\r\n  const d=JSON.parse(localStorage.getItem(gameCacheKey(LEDGER_KEY))||'{}');\r\n  return Object.fromEntries(LEDGER_KINDS.map(k=>[k,Array.isArray(d[k])?d[k]:[]]));\r\n }catch{return Object.fromEntries(LEDGER_KINDS.map(k=>[k,[]]));}\r\n}\r\n\r\nconst accountKinds=new Set([...ACCOUNT_SCOPED_LEDGER_KINDS,'boss-skin']);\r\n// `create:false` (reads) never mints a guest id: a guest with no id has no overlay yet, so reads stay side-effect free.\r\nconst ownerKey=({account=globalThis.myr5AuthenticatedAccount}={},create=true)=>{\r\n try{\r\n const id=typeof account==='string'?account:account?.user?.id;\r\n if(typeof id==='string'&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(id))return `${LEDGER_KEY}/account/${id}${typeof account==='object'&&account?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(account.dataEpoch))}`:''}`;\r\n const guest=create?performanceOwner(globalThis.localStorage,account):localStorage.getItem('myr5-local-guest-owner-v1');\r\n return guest?`${LEDGER_KEY}/guest/${encodeURIComponent(create?guest:`guest:${guest}`)}`:null;\r\n }catch{return null;}\r\n};\r\nfunction accountRead(options){try{const raw=ownerKey(options,false),key=raw?gameCacheKey(raw):null;if(!key)return {};const data=JSON.parse(localStorage.getItem(key)||'{}');return data&&typeof data==='object'&&!Array.isArray(data)?data:{};}catch{return {};}}\r\n// Legacy global kinds read the device ledger plus the current owner's overlay (grants made with `accountScoped:true`,\r\n// e.g. NPC weapon purchases); other owners' and other epochs' overlays never leak in. Account kinds stay owner-only.\r\nexport const grantedIds=(kind,options)=>{\r\n if(!LEDGER_KINDS.includes(kind))return [];\r\n const overlay=accountRead(options)[kind],own=Array.isArray(overlay)?overlay:[];\r\n if(accountKinds.has(kind))return own;\r\n return own.length?[...new Set([...read()[kind],...own])]:read()[kind];\r\n};\r\nexport const isGranted=(kind,id,options)=>grantedIds(kind,options).includes(id);\r\nconst scoped=(kind,options)=>accountKinds.has(kind)||options?.accountScoped===true;\r\n/** Idempotent: true only the first time `id` is granted for `kind`. `{accountScoped:true}` writes a legacy global kind\r\n * to the current owner's overlay instead of the shared device ledger (the global ledger is never touched). */\r\nexport function grantUnlock(kind,id,options){\r\n if(!isCurrentGameAccount(options?.account))return false;\r\n if(!LEDGER_KINDS.includes(kind)||typeof id!=='string'||!id)return false;\r\n if(scoped(kind,options)){\r\n  if(isGranted(kind,id,options))return false; // already in the owner overlay, or already global for everyone\r\n  const raw=ownerKey(options),key=raw?gameCacheKey(raw):null;if(!key)return false;\r\n  const data=accountRead(options),ids=Array.isArray(data[kind])?data[kind]:[];\r\n  data[kind]=[...ids,id];\r\n  try{localStorage.setItem(key,JSON.stringify(data));}catch{return false;}\r\n  markPending(kind,id,{...options,accountScoped:true});return true;\r\n }\r\n const store=read(); // global grants only consult the global store: an owner's paid overlay never blocks an achievement grant\r\n if(store[kind].includes(id))return false;\r\n store[kind].push(id);\r\n try{localStorage.setItem(gameCacheKey(LEDGER_KEY),JSON.stringify(store));}catch{return false;}\r\n markPending(kind,id);\r\n return true;\r\n}\r\n\r\n/** Batch grants are used for collection completion; one storage write per kind. */\r\nexport function grantUnlocks(kind,ids,options){\r\n if(!isCurrentGameAccount(options?.account))return 0;\r\n if(!LEDGER_KINDS.includes(kind)||!Array.isArray(ids))return 0;\r\n const toOwner=scoped(kind,options),key=toOwner?(ownerKey(options)?gameCacheKey(ownerKey(options)):null):gameCacheKey(LEDGER_KEY);if(!key)return 0;\r\n const data=toOwner?accountRead(options):read(),known=new Set(toOwner?grantedIds(kind,options):data[kind]||[]),own=new Set(data[kind]||[]),added=[];\r\n for(const id of ids)if(typeof id==='string'&&id&&!known.has(id)){known.add(id);own.add(id);added.push(id);}\r\n if(!added.length)return 0;data[kind]=[...own];\r\n try{localStorage.setItem(key,JSON.stringify(data));}catch{return 0;}\r\n markPendingMany(added.map(id=>({kind,id})),toOwner?{...options,accountScoped:true}:options);return added.length;\r\n}\r\n","unlock-pending.mjs":"// Grant-time \"new\" markers behind the unlock sparkle (unlock-seen.mjs). A grant that just succeeded marks its one item\r\n// pending; viewing clears it. Grants that predate this record have no marker, so they stay quiet. No imports, so the\r\n// ledger and unlock-store can both use it without a cycle. Scope mirrors the ledger: account kinds per account, rest per device.\r\nimport {performanceOwner} from './performance-progress.mjs';\r\nexport const PENDING_KEY='myr5-unlock-pending-v1';\r\nexport const ACCOUNT_SCOPED_LEDGER_KINDS=Object.freeze(['creature-skin','ship','reward-pack']);\r\nconst accountKinds=new Set([...ACCOUNT_SCOPED_LEDGER_KINDS,'texture','color','palette','boss-skin']);\r\n// `create:false` (reads) never mints a guest id: a guest with no id has no owner record yet, so reads stay side-effect free.\r\nconst ownerKeyOf=({account=globalThis.myr5AuthenticatedAccount}={},create=true)=>{\r\n try{\r\n const id=typeof account==='string'?account:account?.user?.id;\r\n if(typeof id==='string'&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(id))return `${PENDING_KEY}/account/${id}${typeof account==='object'&&account?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(account.dataEpoch))}`:''}`;\r\n const guest=create?performanceOwner(globalThis.localStorage,account):localStorage.getItem('myr5-local-guest-owner-v1');\r\n return guest?`${PENDING_KEY}/guest/${encodeURIComponent(create?guest:`guest:${guest}`)}`:null;\r\n }catch{return null;}\r\n};\r\n// Write key: account kinds (or `accountScoped:true` grants of a device kind) go to the owner record, the rest to the device record.\r\nconst keyOf=(kind,options={})=>accountKinds.has(kind)||options.accountScoped===true?ownerKeyOf(options):PENDING_KEY;\r\n// Read keys: a device kind may carry legacy device markers and current-owner markers; both count, other owners never do.\r\nconst keysOf=(kind,options)=>accountKinds.has(kind)?[ownerKeyOf(options,false)]:[PENDING_KEY,ownerKeyOf(options,false)];\r\nconst read=key=>{try{const d=JSON.parse(localStorage.getItem(key)||'{}');return d&&typeof d==='object'&&!Array.isArray(d)?d:{};}catch{return {};}};\r\nconst idsOf=(data,kind)=>Array.isArray(data[kind])?data[kind]:[];\r\n\r\nexport const isPending=(kind,id,options)=>keysOf(kind,options).some(key=>!!key&&idsOf(read(key),kind).includes(id));\r\nfunction update(key,kind,change){\r\n if(!key)return;\r\n const data=read(key),ids=idsOf(data,kind),next=change(ids);\r\n if(next===ids)return;\r\n try{localStorage.setItem(key,JSON.stringify({...data,[kind]:next}));}catch{/* storage unavailable: the item just isn't sparkled */}\r\n}\r\nexport function markPendingMany(entries,options){\r\n const records=new Map();\r\n for(const {kind,id} of entries){const key=keyOf(kind,options);if(!key)continue;\r\n  let record=records.get(key);if(!record){record={data:read(key),known:new Map()};records.set(key,record);}\r\n  if(!record.known.has(kind)){record.data[kind]=[...idsOf(record.data,kind)];record.known.set(kind,new Set(record.data[kind]));}\r\n  const known=record.known.get(kind);if(!known.has(id)){known.add(id);record.data[kind].push(id);}\r\n }\r\n for(const [key,record] of records)try{localStorage.setItem(key,JSON.stringify(record.data));}catch{}\r\n}\r\nexport const markPending=(kind,id,options)=>update(keyOf(kind,options),kind,ids=>ids.includes(id)?ids:[...ids,id]);\r\nexport const clearPending=(kind,id,options)=>{for(const key of keysOf(kind,options))update(key,kind,ids=>ids.includes(id)?ids.filter(x=>x!==id):ids);};\r\n\r\n// Items unlocked by derived state (no grant call, e.g. body Species from section completion): the first call per kind\r\n// only snapshots what is already unlocked (no historical flood); later calls mark just the ids added since. Device-scoped.\r\nexport const BASELINE_KEY='myr5-unlock-baseline-v1';\r\nexport function noteUnlocked(kind,ids){\r\n const data=read(BASELINE_KEY),known=Array.isArray(data[kind])?data[kind]:null,fresh=known?ids.filter(id=>!known.includes(id)):ids;\r\n if(known&&!fresh.length)return;\r\n try{localStorage.setItem(BASELINE_KEY,JSON.stringify({...data,[kind]:[...(known||[]),...fresh]}));}catch{return;}\r\n if(known)for(const id of fresh)markPending(kind,id);\r\n}\r\n","unlock-seen.mjs":"// Sparkle for newly unlocked items: an item is \"new\" only while its grant-time pending marker (unlock-pending.mjs)\r\n// is set, so grants that existed before this shipped never sparkle. Viewing clears the marker. It never grants or\r\n// gates anything: callers decide an item is unlocked, this only remembers that it was looked at.\r\nimport {isPending,clearPending} from './unlock-pending.mjs';\r\n\r\n/** True when this item was granted since the feature shipped and has not been viewed. */\r\nexport const isUnseen=isPending;\r\n\r\nconst STYLE='[data-sparkle]::after{content:\"\\\\2726\" / \"New\";color:#ffd23a;font-size:.85em;margin-left:.3em;text-shadow:0 0 4px #ffd23a;animation:myr5-sparkle 1.4s ease-in-out infinite alternate}'+\r\n 'button[data-sparkle]{position:relative}button[data-color][data-sparkle]::after{position:absolute;top:1px;right:3px;margin:0}'+\r\n '@keyframes myr5-sparkle{to{opacity:.45}}@media (prefers-reduced-motion:reduce){[data-sparkle]::after{animation:none}}';\r\nfunction ensureStyle(doc){\r\n if(doc.getElementById('myr5-sparkle-style'))return;\r\n const style=doc.createElement('style');style.id='myr5-sparkle-style';style.textContent=STYLE;doc.head.append(style);\r\n}\r\n\r\n/** Records the item as viewed and clears every sparkle showing it. */\r\nexport function markSeen(kind,id,options){\r\n clearPending(kind,id,options);\r\n for(const el of globalThis.document?.querySelectorAll('[data-sparkle]')||[])if(el.dataset.sparkle===`${kind}:${id}`){\r\n  delete el.dataset.sparkle;\r\n  if(el.tagName==='OPTION'&&el.textContent.startsWith('✦ '))el.textContent=el.textContent.slice(2);\r\n }\r\n}\r\n\r\n// Keep each badge visible long enough to notice. Time outside the viewport or in a hidden tab never counts.\r\nconst DWELL_MS=1500;\r\nfunction afterVisibleDwell(el,seen,active=()=>!!el.dataset.sparkle){\r\n if(typeof IntersectionObserver!=='function')return;\r\n const doc=el.ownerDocument;let inView=false,timer=null;\r\n const cancel=()=>{if(timer!==null){clearTimeout(timer);timer=null;}};\r\n const schedule=()=>{if(timer!==null||!inView||doc.visibilityState!=='visible'||!active())return;\r\n  timer=setTimeout(()=>{timer=null;if(inView&&el.isConnected&&doc.visibilityState==='visible'&&active()){io.disconnect();doc.removeEventListener('visibilitychange',onVisibility);seen();}},DWELL_MS);\r\n };\r\n const onVisibility=()=>{if(doc.visibilityState!=='visible')cancel();else schedule();};\r\n const io=new IntersectionObserver(entries=>{\r\n  if(!el.isConnected){cancel();io.disconnect();doc.removeEventListener('visibilitychange',onVisibility);return;}\r\n  inView=entries.some(e=>e.isIntersecting&&e.intersectionRatio>=.6);\r\n  if(inView)schedule();else cancel();\r\n },{threshold:[.6]});\r\n doc.addEventListener('visibilitychange',onVisibility);io.observe(el);\r\n}\r\n\r\n/** Sparkles `el` if unseen. It clears after 1.5s on screen, or immediately on click/focus. */\r\nexport function sparkle(el,kind,id,options){\r\n if(!isUnseen(kind,id,options))return false;\r\n ensureStyle(el.ownerDocument);\r\n el.dataset.sparkle=`${kind}:${id}`;\r\n const seen=()=>markSeen(kind,id,options);\r\n el.addEventListener('click',seen,{once:true});el.addEventListener('focus',seen,{once:true});\r\n afterVisibleDwell(el,seen);\r\n return true;\r\n}\r\n\r\n/** <option> variant: a closed dropdown shows nothing, so the item counts as viewed once it is picked (watchSelect). */\r\nexport function sparkleOption(option,kind,id,options){\r\n if(!isUnseen(kind,id,options))return false;\r\n ensureStyle(option.ownerDocument);\r\n option.dataset.sparkle=`${kind}:${id}`;option.textContent='✦ '+option.textContent;\r\n return true;\r\n}\r\nconst markSelected=(select,options)=>{\r\n const tag=select.selectedOptions[0]?.dataset.sparkle,at=tag?.indexOf(':');\r\n if(tag)markSeen(tag.slice(0,at),tag.slice(at+1),options);\r\n};\r\n/** Picking an option views it; so does the select showing it on screen (a lone ship or skin can't be \"picked\"). */\r\nexport function watchSelect(select,options){\r\n select.addEventListener('change',()=>markSelected(select,options));\r\n afterVisibleDwell(select,()=>markSelected(select,options),()=>!!select.selectedOptions[0]?.dataset.sparkle);\r\n}\r\n","update-policy.mjs":"export const UPDATE_IDLE_MS=15000;\r\nexport const RELEASE_SEEN_KEY='myr5-release-seen-v1';\r\n\r\nexport function safeToUpdate({tracking=false,rest=false,dialog=false,editing=false,hidden=false,online=true,lastInteraction=0,now=Date.now(),automatic=true}={}){\r\n return !tracking&&!rest&&!dialog&&!editing&&!hidden&&online&&(!automatic||now-lastInteraction>=UPDATE_IDLE_MS);\r\n}\r\n\r\nexport function releaseNotice(storage,id){\r\n let seen=false;\r\n try{seen=storage.getItem(RELEASE_SEEN_KEY)===id;}catch{}\r\n return {get visible(){return !seen;},dismiss(){seen=true;try{storage.setItem(RELEASE_SEEN_KEY,id);}catch{}}};\r\n}\r\n\r\nexport function requestActivation(worker,{timeout=10000}={}){\r\n return new Promise((resolve,reject)=>{\r\n  const channel=new MessageChannel();\r\n  const finish=(error)=>{clearTimeout(timer);channel.port1.close();error?reject(error):resolve();};\r\n  const timer=setTimeout(()=>finish(Error('Coach is still preparing the update. It will retry automatically.')),timeout);\r\n  channel.port1.onmessage=event=>finish(event.data?.activated?null:Error('Coach is waiting for another session to finish saving. It will retry automatically.'));\r\n  try{worker.postMessage({type:'PREPARE_UPDATE'},[channel.port2]);}catch(error){finish(error);}\r\n });\r\n}\r\n","update-session.mjs":"// A prepared page keeps its workout lease until activation or an explicit abort.\r\n// Only an explicit abort releases it early (app-updates.mjs aborts when its barrier times out).\r\nexport function createUpdateParticipant({canPrepare,acquire,save,freeze,reload}){\r\n let held=null,preparing=null;\r\n function abort(id){if(held?.id!==id)return;const item=held;held=null;item.release();item.thaw?.();}\r\n async function message(data){\r\n  const id=data?.id;if(typeof id!=='string'||!id)return {safe:false};\r\n  if(data.type==='UPDATE_ABORT'){abort(id);return {safe:false};}\r\n  if(data.type==='UPDATE_CONFIRM')return {safe:held?.id===id&&held.ready===true,protocol:2,id};\r\n  if(data.type!=='UPDATE_SAFETY_CHECK')return {safe:false};\r\n  if(held)return {safe:held.id===id&&held.ready===true,protocol:2,id};\r\n  if(preparing||!canPrepare())return {safe:false};\r\n  const release=acquire();if(!release)return {safe:false};\r\n  const item={id,release,ready:false,thaw:null};held=item;\r\n  try{\r\n   item.thaw=freeze(id);preparing=Promise.resolve().then(save);await preparing;\r\n   if(held!==item)return {safe:false};item.ready=true;\r\n   return {safe:true,protocol:2,id};\r\n  }catch{abort(id);return {safe:false};}finally{preparing=null;}\r\n }\r\n return {message,get prepared(){return !!held?.ready;},activated(){if(held?.ready){reload();return true;}return false;}};\r\n}\r\n","vendor/bitchord/LICENSE":"                    GNU GENERAL PUBLIC LICENSE\r\n                       Version 3, 29 June 2007\r\n\r\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\r\n Everyone is permitted to copy and distribute verbatim copies\r\n of this license document, but changing it is not allowed.\r\n\r\n                            Preamble\r\n\r\n  The GNU General Public License is a free, copyleft license for\r\nsoftware and other kinds of works.\r\n\r\n  The licenses for most software and other practical works are designed\r\nto take away your freedom to share and change the works.  By contrast,\r\nthe GNU General Public License is intended to guarantee your freedom to\r\nshare and change all versions of a program--to make sure it remains free\r\nsoftware for all its users.  We, the Free Software Foundation, use the\r\nGNU General Public License for most of our software; it applies also to\r\nany other work released this way by its authors.  You can apply it to\r\nyour programs, too.\r\n\r\n  When we speak of free software, we are referring to freedom, not\r\nprice.  Our General Public Licenses are designed to make sure that you\r\nhave the freedom to distribute copies of free software (and charge for\r\nthem if you wish), that you receive source code or can get it if you\r\nwant it, that you can change the software or use pieces of it in new\r\nfree programs, and that you know you can do these things.\r\n\r\n  To protect your rights, we need to prevent others from denying you\r\nthese rights or asking you to surrender the rights.  Therefore, you have\r\ncertain responsibilities if you distribute copies of the software, or if\r\nyou modify it: responsibilities to respect the freedom of others.\r\n\r\n  For example, if you distribute copies of such a program, whether\r\ngratis or for a fee, you must pass on to the recipients the same\r\nfreedoms that you received.  You must make sure that they, too, receive\r\nor can get the source code.  And you must show them these terms so they\r\nknow their rights.\r\n\r\n  Developers that use the GNU GPL protect your rights with two steps:\r\n(1) assert copyright on the software, and (2) offer you this License\r\ngiving you legal permission to copy, distribute and/or modify it.\r\n\r\n  For the developers' and authors' protection, the GPL clearly explains\r\nthat there is no warranty for this free software.  For both users' and\r\nauthors' sake, the GPL requires that modified versions be marked as\r\nchanged, so that their problems will not be attributed erroneously to\r\nauthors of previous versions.\r\n\r\n  Some devices are designed to deny users access to install or run\r\nmodified versions of the software inside them, although the manufacturer\r\ncan do so.  This is fundamentally incompatible with the aim of\r\nprotecting users' freedom to change the software.  The systematic\r\npattern of such abuse occurs in the area of products for individuals to\r\nuse, which is precisely where it is most unacceptable.  Therefore, we\r\nhave designed this version of the GPL to prohibit the practice for those\r\nproducts.  If such problems arise substantially in other domains, we\r\nstand ready to extend this provision to those domains in future versions\r\nof the GPL, as needed to protect the freedom of users.\r\n\r\n  Finally, every program is threatened constantly by software patents.\r\nStates should not allow patents to restrict development and use of\r\nsoftware on general-purpose computers, but in those that do, we wish to\r\navoid the special danger that patents applied to a free program could\r\nmake it effectively proprietary.  To prevent this, the GPL assures that\r\npatents cannot be used to render the program non-free.\r\n\r\n  The precise terms and conditions for copying, distribution and\r\nmodification follow.\r\n\r\n                       TERMS AND CONDITIONS\r\n\r\n  0. Definitions.\r\n\r\n  \"This License\" refers to version 3 of the GNU General Public License.\r\n\r\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\r\nworks, such as semiconductor masks.\r\n\r\n  \"The Program\" refers to any copyrightable work licensed under this\r\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\r\n\"recipients\" may be individuals or organizations.\r\n\r\n  To \"modify\" a work means to copy from or adapt all or part of the work\r\nin a fashion requiring copyright permission, other than the making of an\r\nexact copy.  The resulting work is called a \"modified version\" of the\r\nearlier work or a work \"based on\" the earlier work.\r\n\r\n  A \"covered work\" means either the unmodified Program or a work based\r\non the Program.\r\n\r\n  To \"propagate\" a work means to do anything with it that, without\r\npermission, would make you directly or secondarily liable for\r\ninfringement under applicable copyright law, except executing it on a\r\ncomputer or modifying a private copy.  Propagation includes copying,\r\ndistribution (with or without modification), making available to the\r\npublic, and in some countries other activities as well.\r\n\r\n  To \"convey\" a work means any kind of propagation that enables other\r\nparties to make or receive copies.  Mere interaction with a user through\r\na computer network, with no transfer of a copy, is not conveying.\r\n\r\n  An interactive user interface displays \"Appropriate Legal Notices\"\r\nto the extent that it includes a convenient and prominently visible\r\nfeature that (1) displays an appropriate copyright notice, and (2)\r\ntells the user that there is no warranty for the work (except to the\r\nextent that warranties are provided), that licensees may convey the\r\nwork under this License, and how to view a copy of this License.  If\r\nthe interface presents a list of user commands or options, such as a\r\nmenu, a prominent item in the list meets this criterion.\r\n\r\n  1. Source Code.\r\n\r\n  The \"source code\" for a work means the preferred form of the work\r\nfor making modifications to it.  \"Object code\" means any non-source\r\nform of a work.\r\n\r\n  A \"Standard Interface\" means an interface that either is an official\r\nstandard defined by a recognized standards body, or, in the case of\r\ninterfaces specified for a particular programming language, one that\r\nis widely used among developers working in that language.\r\n\r\n  The \"System Libraries\" of an executable work include anything, other\r\nthan the work as a whole, that (a) is included in the normal form of\r\npackaging a Major Component, but which is not part of that Major\r\nComponent, and (b) serves only to enable use of the work with that\r\nMajor Component, or to implement a Standard Interface for which an\r\nimplementation is available to the public in source code form.  A\r\n\"Major Component\", in this context, means a major essential component\r\n(kernel, window system, and so on) of the specific operating system\r\n(if any) on which the executable work runs, or a compiler used to\r\nproduce the work, or an object code interpreter used to run it.\r\n\r\n  The \"Corresponding Source\" for a work in object code form means all\r\nthe source code needed to generate, install, and (for an executable\r\nwork) run the object code and to modify the work, including scripts to\r\ncontrol those activities.  However, it does not include the work's\r\nSystem Libraries, or general-purpose tools or generally available free\r\nprograms which are used unmodified in performing those activities but\r\nwhich are not part of the work.  For example, Corresponding Source\r\nincludes interface definition files associated with source files for\r\nthe work, and the source code for shared libraries and dynamically\r\nlinked subprograms that the work is specifically designed to require,\r\nsuch as by intimate data communication or control flow between those\r\nsubprograms and other parts of the work.\r\n\r\n  The Corresponding Source need not include anything that users\r\ncan regenerate automatically from other parts of the Corresponding\r\nSource.\r\n\r\n  The Corresponding Source for a work in source code form is that\r\nsame work.\r\n\r\n  2. Basic Permissions.\r\n\r\n  All rights granted under this License are granted for the term of\r\ncopyright on the Program, and are irrevocable provided the stated\r\nconditions are met.  This License explicitly affirms your unlimited\r\npermission to run the unmodified Program.  The output from running a\r\ncovered work is covered by this License only if the output, given its\r\ncontent, constitutes a covered work.  This License acknowledges your\r\nrights of fair use or other equivalent, as provided by copyright law.\r\n\r\n  You may make, run and propagate covered works that you do not\r\nconvey, without conditions so long as your license otherwise remains\r\nin force.  You may convey covered works to others for the sole purpose\r\nof having them make modifications exclusively for you, or provide you\r\nwith facilities for running those works, provided that you comply with\r\nthe terms of this License in conveying all material for which you do\r\nnot control copyright.  Those thus making or running the covered works\r\nfor you must do so exclusively on your behalf, under your direction\r\nand control, on terms that prohibit them from making any copies of\r\nyour copyrighted material outside their relationship with you.\r\n\r\n  Conveying under any other circumstances is permitted solely under\r\nthe conditions stated below.  Sublicensing is not allowed; section 10\r\nmakes it unnecessary.\r\n\r\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\r\n\r\n  No covered work shall be deemed part of an effective technological\r\nmeasure under any applicable law fulfilling obligations under article\r\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\r\nsimilar laws prohibiting or restricting circumvention of such\r\nmeasures.\r\n\r\n  When you convey a covered work, you waive any legal power to forbid\r\ncircumvention of technological measures to the extent such circumvention\r\nis effected by exercising rights under this License with respect to\r\nthe covered work, and you disclaim any intention to limit operation or\r\nmodification of the work as a means of enforcing, against the work's\r\nusers, your or third parties' legal rights to forbid circumvention of\r\ntechnological measures.\r\n\r\n  4. Conveying Verbatim Copies.\r\n\r\n  You may convey verbatim copies of the Program's source code as you\r\nreceive it, in any medium, provided that you conspicuously and\r\nappropriately publish on each copy an appropriate copyright notice;\r\nkeep intact all notices stating that this License and any\r\nnon-permissive terms added in accord with section 7 apply to the code;\r\nkeep intact all notices of the absence of any warranty; and give all\r\nrecipients a copy of this License along with the Program.\r\n\r\n  You may charge any price or no price for each copy that you convey,\r\nand you may offer support or warranty protection for a fee.\r\n\r\n  5. Conveying Modified Source Versions.\r\n\r\n  You may convey a work based on the Program, or the modifications to\r\nproduce it from the Program, in the form of source code under the\r\nterms of section 4, provided that you also meet all of these conditions:\r\n\r\n    a) The work must carry prominent notices stating that you modified\r\n    it, and giving a relevant date.\r\n\r\n    b) The work must carry prominent notices stating that it is\r\n    released under this License and any conditions added under section\r\n    7.  This requirement modifies the requirement in section 4 to\r\n    \"keep intact all notices\".\r\n\r\n    c) You must license the entire work, as a whole, under this\r\n    License to anyone who comes into possession of a copy.  This\r\n    License will therefore apply, along with any applicable section 7\r\n    additional terms, to the whole of the work, and all its parts,\r\n    regardless of how they are packaged.  This License gives no\r\n    permission to license the work in any other way, but it does not\r\n    invalidate such permission if you have separately received it.\r\n\r\n    d) If the work has interactive user interfaces, each must display\r\n    Appropriate Legal Notices; however, if the Program has interactive\r\n    interfaces that do not display Appropriate Legal Notices, your\r\n    work need not make them do so.\r\n\r\n  A compilation of a covered work with other separate and independent\r\nworks, which are not by their nature extensions of the covered work,\r\nand which are not combined with it such as to form a larger program,\r\nin or on a volume of a storage or distribution medium, is called an\r\n\"aggregate\" if the compilation and its resulting copyright are not\r\nused to limit the access or legal rights of the compilation's users\r\nbeyond what the individual works permit.  Inclusion of a covered work\r\nin an aggregate does not cause this License to apply to the other\r\nparts of the aggregate.\r\n\r\n  6. Conveying Non-Source Forms.\r\n\r\n  You may convey a covered work in object code form under the terms\r\nof sections 4 and 5, provided that you also convey the\r\nmachine-readable Corresponding Source under the terms of this License,\r\nin one of these ways:\r\n\r\n    a) Convey the object code in, or embodied in, a physical product\r\n    (including a physical distribution medium), accompanied by the\r\n    Corresponding Source fixed on a durable physical medium\r\n    customarily used for software interchange.\r\n\r\n    b) Convey the object code in, or embodied in, a physical product\r\n    (including a physical distribution medium), accompanied by a\r\n    written offer, valid for at least three years and valid for as\r\n    long as you offer spare parts or customer support for that product\r\n    model, to give anyone who possesses the object code either (1) a\r\n    copy of the Corresponding Source for all the software in the\r\n    product that is covered by this License, on a durable physical\r\n    medium customarily used for software interchange, for a price no\r\n    more than your reasonable cost of physically performing this\r\n    conveying of source, or (2) access to copy the\r\n    Corresponding Source from a network server at no charge.\r\n\r\n    c) Convey individual copies of the object code with a copy of the\r\n    written offer to provide the Corresponding Source.  This\r\n    alternative is allowed only occasionally and noncommercially, and\r\n    only if you received the object code with such an offer, in accord\r\n    with subsection 6b.\r\n\r\n    d) Convey the object code by offering access from a designated\r\n    place (gratis or for a charge), and offer equivalent access to the\r\n    Corresponding Source in the same way through the same place at no\r\n    further charge.  You need not require recipients to copy the\r\n    Corresponding Source along with the object code.  If the place to\r\n    copy the object code is a network server, the Corresponding Source\r\n    may be on a different server (operated by you or a third party)\r\n    that supports equivalent copying facilities, provided you maintain\r\n    clear directions next to the object code saying where to find the\r\n    Corresponding Source.  Regardless of what server hosts the\r\n    Corresponding Source, you remain obligated to ensure that it is\r\n    available for as long as needed to satisfy these requirements.\r\n\r\n    e) Convey the object code using peer-to-peer transmission, provided\r\n    you inform other peers where the object code and Corresponding\r\n    Source of the work are being offered to the general public at no\r\n    charge under subsection 6d.\r\n\r\n  A separable portion of the object code, whose source code is excluded\r\nfrom the Corresponding Source as a System Library, need not be\r\nincluded in conveying the object code work.\r\n\r\n  A \"User Product\" is either (1) a \"consumer product\", which means any\r\ntangible personal property which is normally used for personal, family,\r\nor household purposes, or (2) anything designed or sold for incorporation\r\ninto a dwelling.  In determining whether a product is a consumer product,\r\ndoubtful cases shall be resolved in favor of coverage.  For a particular\r\nproduct received by a particular user, \"normally used\" refers to a\r\ntypical or common use of that class of product, regardless of the status\r\nof the particular user or of the way in which the particular user\r\nactually uses, or expects or is expected to use, the product.  A product\r\nis a consumer product regardless of whether the product has substantial\r\ncommercial, industrial or non-consumer uses, unless such uses represent\r\nthe only significant mode of use of the product.\r\n\r\n  \"Installation Information\" for a User Product means any methods,\r\nprocedures, authorization keys, or other information required to install\r\nand execute modified versions of a covered work in that User Product from\r\na modified version of its Corresponding Source.  The information must\r\nsuffice to ensure that the continued functioning of the modified object\r\ncode is in no case prevented or interfered with solely because\r\nmodification has been made.\r\n\r\n  If you convey an object code work under this section in, or with, or\r\nspecifically for use in, a User Product, and the conveying occurs as\r\npart of a transaction in which the right of possession and use of the\r\nUser Product is transferred to the recipient in perpetuity or for a\r\nfixed term (regardless of how the transaction is characterized), the\r\nCorresponding Source conveyed under this section must be accompanied\r\nby the Installation Information.  But this requirement does not apply\r\nif neither you nor any third party retains the ability to install\r\nmodified object code on the User Product (for example, the work has\r\nbeen installed in ROM).\r\n\r\n  The requirement to provide Installation Information does not include a\r\nrequirement to continue to provide support service, warranty, or updates\r\nfor a work that has been modified or installed by the recipient, or for\r\nthe User Product in which it has been modified or installed.  Access to a\r\nnetwork may be denied when the modification itself materially and\r\nadversely affects the operation of the network or violates the rules and\r\nprotocols for communication across the network.\r\n\r\n  Corresponding Source conveyed, and Installation Information provided,\r\nin accord with this section must be in a format that is publicly\r\ndocumented (and with an implementation available to the public in\r\nsource code form), and must require no special password or key for\r\nunpacking, reading or copying.\r\n\r\n  7. Additional Terms.\r\n\r\n  \"Additional permissions\" are terms that supplement the terms of this\r\nLicense by making exceptions from one or more of its conditions.\r\nAdditional permissions that are applicable to the entire Program shall\r\nbe treated as though they were included in this License, to the extent\r\nthat they are valid under applicable law.  If additional permissions\r\napply only to part of the Program, that part may be used separately\r\nunder those permissions, but the entire Program remains governed by\r\nthis License without regard to the additional permissions.\r\n\r\n  When you convey a copy of a covered work, you may at your option\r\nremove any additional permissions from that copy, or from any part of\r\nit.  (Additional permissions may be written to require their own\r\nremoval in certain cases when you modify the work.)  You may place\r\nadditional permissions on material, added by you to a covered work,\r\nfor which you have or can give appropriate copyright permission.\r\n\r\n  Notwithstanding any other provision of this License, for material you\r\nadd to a covered work, you may (if authorized by the copyright holders of\r\nthat material) supplement the terms of this License with terms:\r\n\r\n    a) Disclaiming warranty or limiting liability differently from the\r\n    terms of sections 15 and 16 of this License; or\r\n\r\n    b) Requiring preservation of specified reasonable legal notices or\r\n    author attributions in that material or in the Appropriate Legal\r\n    Notices displayed by works containing it; or\r\n\r\n    c) Prohibiting misrepresentation of the origin of that material, or\r\n    requiring that modified versions of such material be marked in\r\n    reasonable ways as different from the original version; or\r\n\r\n    d) Limiting the use for publicity purposes of names of licensors or\r\n    authors of the material; or\r\n\r\n    e) Declining to grant rights under trademark law for use of some\r\n    trade names, trademarks, or service marks; or\r\n\r\n    f) Requiring indemnification of licensors and authors of that\r\n    material by anyone who conveys the material (or modified versions of\r\n    it) with contractual assumptions of liability to the recipient, for\r\n    any liability that these contractual assumptions directly impose on\r\n    those licensors and authors.\r\n\r\n  All other non-permissive additional terms are considered \"further\r\nrestrictions\" within the meaning of section 10.  If the Program as you\r\nreceived it, or any part of it, contains a notice stating that it is\r\ngoverned by this License along with a term that is a further\r\nrestriction, you may remove that term.  If a license document contains\r\na further restriction but permits relicensing or conveying under this\r\nLicense, you may add to a covered work material governed by the terms\r\nof that license document, provided that the further restriction does\r\nnot survive such relicensing or conveying.\r\n\r\n  If you add terms to a covered work in accord with this section, you\r\nmust place, in the relevant source files, a statement of the\r\nadditional terms that apply to those files, or a notice indicating\r\nwhere to find the applicable terms.\r\n\r\n  Additional terms, permissive or non-permissive, may be stated in the\r\nform of a separately written license, or stated as exceptions;\r\nthe above requirements apply either way.\r\n\r\n  8. Termination.\r\n\r\n  You may not propagate or modify a covered work except as expressly\r\nprovided under this License.  Any attempt otherwise to propagate or\r\nmodify it is void, and will automatically terminate your rights under\r\nthis License (including any patent licenses granted under the third\r\nparagraph of section 11).\r\n\r\n  However, if you cease all violation of this License, then your\r\nlicense from a particular copyright holder is reinstated (a)\r\nprovisionally, unless and until the copyright holder explicitly and\r\nfinally terminates your license, and (b) permanently, if the copyright\r\nholder fails to notify you of the violation by some reasonable means\r\nprior to 60 days after the cessation.\r\n\r\n  Moreover, your license from a particular copyright holder is\r\nreinstated permanently if the copyright holder notifies you of the\r\nviolation by some reasonable means, this is the first time you have\r\nreceived notice of violation of this License (for any work) from that\r\ncopyright holder, and you cure the violation prior to 30 days after\r\nyour receipt of the notice.\r\n\r\n  Termination of your rights under this section does not terminate the\r\nlicenses of parties who have received copies or rights from you under\r\nthis License.  If your rights have been terminated and not permanently\r\nreinstated, you do not qualify to receive new licenses for the same\r\nmaterial under section 10.\r\n\r\n  9. Acceptance Not Required for Having Copies.\r\n\r\n  You are not required to accept this License in order to receive or\r\nrun a copy of the Program.  Ancillary propagation of a covered work\r\noccurring solely as a consequence of using peer-to-peer transmission\r\nto receive a copy likewise does not require acceptance.  However,\r\nnothing other than this License grants you permission to propagate or\r\nmodify any covered work.  These actions infringe copyright if you do\r\nnot accept this License.  Therefore, by modifying or propagating a\r\ncovered work, you indicate your acceptance of this License to do so.\r\n\r\n  10. Automatic Licensing of Downstream Recipients.\r\n\r\n  Each time you convey a covered work, the recipient automatically\r\nreceives a license from the original licensors, to run, modify and\r\npropagate that work, subject to this License.  You are not responsible\r\nfor enforcing compliance by third parties with this License.\r\n\r\n  An \"entity transaction\" is a transaction transferring control of an\r\norganization, or substantially all assets of one, or subdividing an\r\norganization, or merging organizations.  If propagation of a covered\r\nwork results from an entity transaction, each party to that\r\ntransaction who receives a copy of the work also receives whatever\r\nlicenses to the work the party's predecessor in interest had or could\r\ngive under the previous paragraph, plus a right to possession of the\r\nCorresponding Source of the work from the predecessor in interest, if\r\nthe predecessor has it or can get it with reasonable efforts.\r\n\r\n  You may not impose any further restrictions on the exercise of the\r\nrights granted or affirmed under this License.  For example, you may\r\nnot impose a license fee, royalty, or other charge for exercise of\r\nrights granted under this License, and you may not initiate litigation\r\n(including a cross-claim or counterclaim in a lawsuit) alleging that\r\nany patent claim is infringed by making, using, selling, offering for\r\nsale, or importing the Program or any portion of it.\r\n\r\n  11. Patents.\r\n\r\n  A \"contributor\" is a copyright holder who authorizes use under this\r\nLicense of the Program or a work on which the Program is based.  The\r\nwork thus licensed is called the contributor's \"contributor version\".\r\n\r\n  A contributor's \"essential patent claims\" are all patent claims\r\nowned or controlled by the contributor, whether already acquired or\r\nhereafter acquired, that would be infringed by some manner, permitted\r\nby this License, of making, using, or selling its contributor version,\r\nbut do not include claims that would be infringed only as a\r\nconsequence of further modification of the contributor version.  For\r\npurposes of this definition, \"control\" includes the right to grant\r\npatent sublicenses in a manner consistent with the requirements of\r\nthis License.\r\n\r\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\r\npatent license under the contributor's essential patent claims, to\r\nmake, use, sell, offer for sale, import and otherwise run, modify and\r\npropagate the contents of its contributor version.\r\n\r\n  In the following three paragraphs, a \"patent license\" is any express\r\nagreement or commitment, however denominated, not to enforce a patent\r\n(such as an express permission to practice a patent or covenant not to\r\nsue for patent infringement).  To \"grant\" such a patent license to a\r\nparty means to make such an agreement or commitment not to enforce a\r\npatent against the party.\r\n\r\n  If you convey a covered work, knowingly relying on a patent license,\r\nand the Corresponding Source of the work is not available for anyone\r\nto copy, free of charge and under the terms of this License, through a\r\npublicly available network server or other readily accessible means,\r\nthen you must either (1) cause the Corresponding Source to be so\r\navailable, or (2) arrange to deprive yourself of the benefit of the\r\npatent license for this particular work, or (3) arrange, in a manner\r\nconsistent with the requirements of this License, to extend the patent\r\nlicense to downstream recipients.  \"Knowingly relying\" means you have\r\nactual knowledge that, but for the patent license, your conveying the\r\ncovered work in a country, or your recipient's use of the covered work\r\nin a country, would infringe one or more identifiable patents in that\r\ncountry that you have reason to believe are valid.\r\n\r\n  If, pursuant to or in connection with a single transaction or\r\narrangement, you convey, or propagate by procuring conveyance of, a\r\ncovered work, and grant a patent license to some of the parties\r\nreceiving the covered work authorizing them to use, propagate, modify\r\nor convey a specific copy of the covered work, then the patent license\r\nyou grant is automatically extended to all recipients of the covered\r\nwork and works based on it.\r\n\r\n  A patent license is \"discriminatory\" if it does not include within\r\nthe scope of its coverage, prohibits the exercise of, or is\r\nconditioned on the non-exercise of one or more of the rights that are\r\nspecifically granted under this License.  You may not convey a covered\r\nwork if you are a party to an arrangement with a third party that is\r\nin the business of distributing software, under which you make payment\r\nto the third party based on the extent of your activity of conveying\r\nthe work, and under which the third party grants, to any of the\r\nparties who would receive the covered work from you, a discriminatory\r\npatent license (a) in connection with copies of the covered work\r\nconveyed by you (or copies made from those copies), or (b) primarily\r\nfor and in connection with specific products or compilations that\r\ncontain the covered work, unless you entered into that arrangement,\r\nor that patent license was granted, prior to 28 March 2007.\r\n\r\n  Nothing in this License shall be construed as excluding or limiting\r\nany implied license or other defenses to infringement that may\r\notherwise be available to you under applicable patent law.\r\n\r\n  12. No Surrender of Others' Freedom.\r\n\r\n  If conditions are imposed on you (whether by court order, agreement or\r\notherwise) that contradict the conditions of this License, they do not\r\nexcuse you from the conditions of this License.  If you cannot convey a\r\ncovered work so as to satisfy simultaneously your obligations under this\r\nLicense and any other pertinent obligations, then as a consequence you may\r\nnot convey it at all.  For example, if you agree to terms that obligate you\r\nto collect a royalty for further conveying from those to whom you convey\r\nthe Program, the only way you could satisfy both those terms and this\r\nLicense would be to refrain entirely from conveying the Program.\r\n\r\n  13. Use with the GNU Affero General Public License.\r\n\r\n  Notwithstanding any other provision of this License, you have\r\npermission to link or combine any covered work with a work licensed\r\nunder version 3 of the GNU Affero General Public License into a single\r\ncombined work, and to convey the resulting work.  The terms of this\r\nLicense will continue to apply to the part which is the covered work,\r\nbut the special requirements of the GNU Affero General Public License,\r\nsection 13, concerning interaction through a network will apply to the\r\ncombination as such.\r\n\r\n  14. Revised Versions of this License.\r\n\r\n  The Free Software Foundation may publish revised and/or new versions of\r\nthe GNU General Public License from time to time.  Such new versions will\r\nbe similar in spirit to the present version, but may differ in detail to\r\naddress new problems or concerns.\r\n\r\n  Each version is given a distinguishing version number.  If the\r\nProgram specifies that a certain numbered version of the GNU General\r\nPublic License \"or any later version\" applies to it, you have the\r\noption of following the terms and conditions either of that numbered\r\nversion or of any later version published by the Free Software\r\nFoundation.  If the Program does not specify a version number of the\r\nGNU General Public License, you may choose any version ever published\r\nby the Free Software Foundation.\r\n\r\n  If the Program specifies that a proxy can decide which future\r\nversions of the GNU General Public License can be used, that proxy's\r\npublic statement of acceptance of a version permanently authorizes you\r\nto choose that version for the Program.\r\n\r\n  Later license versions may give you additional or different\r\npermissions.  However, no additional obligations are imposed on any\r\nauthor or copyright holder as a result of your choosing to follow a\r\nlater version.\r\n\r\n  15. Disclaimer of Warranty.\r\n\r\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\r\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\r\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\r\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\r\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\r\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\r\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r\n\r\n  16. Limitation of Liability.\r\n\r\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\r\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\r\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\r\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\r\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\r\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\r\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\r\nSUCH DAMAGES.\r\n\r\n  17. Interpretation of Sections 15 and 16.\r\n\r\n  If the disclaimer of warranty and limitation of liability provided\r\nabove cannot be given local legal effect according to their terms,\r\nreviewing courts shall apply local law that most closely approximates\r\nan absolute waiver of all civil liability in connection with the\r\nProgram, unless a warranty or assumption of liability accompanies a\r\ncopy of the Program in return for a fee.\r\n\r\n                     END OF TERMS AND CONDITIONS\r\n\r\n            How to Apply These Terms to Your New Programs\r\n\r\n  If you develop a new program, and you want it to be of the greatest\r\npossible use to the public, the best way to achieve this is to make it\r\nfree software which everyone can redistribute and change under these terms.\r\n\r\n  To do so, attach the following notices to the program.  It is safest\r\nto attach them to the start of each source file to most effectively\r\nstate the exclusion of warranty; and each file should have at least\r\nthe \"copyright\" line and a pointer to where the full notice is found.\r\n\r\n    <one line to give the program's name and a brief idea of what it does.>\r\n    Copyright (C) <year>  <name of author>\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\r\n\r\nAlso add information on how to contact you by electronic and paper mail.\r\n\r\n  If the program does terminal interaction, make it output a short\r\nnotice like this when it starts in an interactive mode:\r\n\r\n    <program>  Copyright (C) <year>  <name of author>\r\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r\n    This is free software, and you are welcome to redistribute it\r\n    under certain conditions; type `show c' for details.\r\n\r\nThe hypothetical commands `show w' and `show c' should show the appropriate\r\nparts of the General Public License.  Of course, your program's commands\r\nmight be different; for a GUI interface, you would use an \"about box\".\r\n\r\n  You should also get your employer (if you work as a programmer) or school,\r\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\r\nFor more information on this, and how to apply and follow the GNU GPL, see\r\n<https://www.gnu.org/licenses/>.\r\n\r\n  The GNU General Public License does not permit incorporating your program\r\ninto proprietary programs.  If your program is a subroutine library, you\r\nmay consider it more useful to permit linking proprietary applications with\r\nthe library.  If this is what you want to do, use the GNU Lesser General\r\nPublic License instead of this License.  But first, please read\r\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\r\n","vendor/bitchord/NOTICE":"BitChord playback gain helpers\r\nSource: https://github.com/kushagrasinghx/BitChord\r\nRevision: 2c599a6cd7a195227a11dab5769f88da8fd08194\r\nOrigin: app/src/main/java/com/music/bitchord/playback/CrossfadeController.kt\r\nMYR5 adaptation: actual-dj-engine.mjs riseGain / fallGain (JavaScript port).\r\nCopyright and license: upstream contributors, GPL-3.0. See LICENSE.\r\nThe remaining native Android playback and analysis stack is not bundled here.\r\n","vendor/butterchurn/LICENSE.engine":"MIT License\n\nCopyright (c) 2013-2018 Jordan Berg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","vendor/butterchurn/LICENSE.presets":"MIT License\n\nCopyright (c) 2013-2018 Jordan Berg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","vendor/butterchurn/README.md":"# Vendored Butterchurn\r\n\r\nUnmodified official npm distributables, downloaded 2026-10-09 using `npm pack --ignore-scripts`. No package.json or lockfile changes.\r\n\r\n- Engine: butterchurn **2.6.7**, `lib/butterchurn.min.js` → `butterchurn-2.6.7.min.js`.\r\n  Source: https://github.com/jberg/butterchurn\r\n  Archive: https://registry.npmjs.org/butterchurn/-/butterchurn-2.6.7.tgz\r\n  npm integrity: `sha512-BJiRA8L0L2+84uoG2SSfkp0kclBuN+vQKf217pK7pMlwEO2ZEg3MtO2/o+l8Qpr8Nbejg8tmL1ZHD1jmhiaaqg==`\r\n  Vendored SHA256: `4e67421bc18d48fac4a6ff4e69e2778f770737fbdd4e323438da154085b4818d`.\r\n- Presets: butterchurn-presets **2.4.7**, `lib/butterchurnPresets.min.js` → `presets-2.4.7.min.js`.\r\n  Source: https://github.com/jberg/butterchurn-presets\r\n  Archive: https://registry.npmjs.org/butterchurn-presets/-/butterchurn-presets-2.4.7.tgz\r\n  npm integrity: `sha512-4MdM8ripz/VfH1BCldrIKdAc/1ryJFBDvqlyow6Ivo1frwj0H3duzvSMFC7/wIjAjxb1QpwVHVqGqS9uAFKhpg==`\r\n  Vendored SHA256: `136c746836aef6dff8a1f6a93ede48c7bbe4da27d2f44de6b59ebc56ffefca10`.\r\n\r\nBoth upstream packages use MIT. Their complete original notices travel in LICENSE.engine and LICENSE.presets. Preset author names remain in the preset names and runtime status. No upstream rendering or equation code was modified.\r\n\r\nThe wrapper lazy-loads both UMD scripts. WebGL2 is required; it reports an error if unavailable. It connects actual audio only, via Butterchurn's native AudioNode input or its supported render(audioLevels) waveform input when switching AudioContexts. Input gain controls waveform sensitivity and does not change audible mix volume. Source labels preserve MYR5 mix versus microphone.\r\n\r\nThirty FPS, 1.5 maximum pixel ratio and 32×24 internal mesh limit mobile GPU cost. Reduced motion disables preset morphing and lowers rendering to twelve FPS; preset animations and audio-reactive shape changes remain. This is reduced animation, not a claim of flash-safety certification.\r\n\r\nScene selectors are original preset families, not literal standalone spectrum/scope meters. Each family has three real alternate looks. The palette control selects another supplied preset rather than claiming an unsupported recoloring API. Label it **Alternate look** in UI.\r\n\r\nPrimary presets: orbit = Flexi - mindblob mix; tunnel = martin - glass corridor; spectrum = Geiss - Thumb Drum; scope = Eo.S. + Phat - cubetrace - v2; trails = Zylot - Paint Spill (Music Reactive Paint Mix). All fifteen preset names are declared in the wrapper and selected unchanged from the supplied bank.\r\n\r\nButterchurn 2.6.7 compiles preset equations using new Function. A strict production CSP requires an explicit compatible policy; this wrapper does not weaken the site's policy. Official source documentation: https://github.com/jberg/butterchurn#usage\r\n","vendor/butterchurn/butterchurn-2.6.7.min.js":"!function(t,e){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(\"butterchurn\",[],e):\"object\"==typeof exports?exports.butterchurn=e():t.butterchurn=e()}(window,function(){return function(t){var e={};function i(s){if(e[s])return e[s].exports;var r=e[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=e,i.d=function(t,e,s){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},i.r=function(t){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&\"object\"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(i.r(s),Object.defineProperty(s,\"default\",{enumerable:!0,value:t}),2&e&&\"string\"!=typeof t)for(var r in t)i.d(s,r,function(e){return t[e]}.bind(null,r));return s},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,\"a\",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p=\"\",i(i.s=5)}([function(t,e,i){var s;void 0===(s=function(){return{baseVals:{gammaadj:1.25,wave_g:.5,mv_x:12,warpscale:1,brighten:0,mv_y:9,wave_scale:1,echo_alpha:0,additivewave:0,sx:1,sy:1,warp:.01,red_blue:0,wave_mode:0,wave_brighten:0,wrap:0,zoomexp:1,fshader:0,wave_r:.5,echo_zoom:1,wave_smoothing:.75,warpanimspeed:1,wave_dots:0,wave_x:.5,wave_y:.5,zoom:1,solarize:0,modwavealphabyvolume:0,dx:0,cx:.5,dy:0,darken_center:0,cy:.5,invert:0,bmotionvectorson:0,rot:0,modwavealphaend:.95,wave_mystery:-.2,decay:.9,wave_a:1,wave_b:.5,rating:5,modwavealphastart:.75,darken:0,echo_orient:0,ib_r:.5,ib_g:.5,ib_b:.5,ib_a:0,ib_size:0,ob_r:.5,ob_g:.5,ob_b:.5,ob_a:0,ob_size:0,mv_dx:0,mv_dy:0,mv_a:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_l:0},init_eqs:function(){return{}},frame_eqs:function(t){return t.rkeys=[\"warp\"],t.zoom=1.01+.02*t.treb_att,t.warp=.15+.25*t.bass_att,t},pixel_eqs:function(t){return t.warp=t.warp+.15*t.rad,t},waves:[{baseVals:{a:1,enabled:0,b:1,g:1,scaling:1,samples:512,additive:0,usedots:0,spectrum:0,r:1,smoothing:.5,thick:0,sep:0},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t},point_eqs:\"\"},{baseVals:{a:1,enabled:0,b:1,g:1,scaling:1,samples:512,additive:0,usedots:0,spectrum:0,r:1,smoothing:.5,thick:0,sep:0},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t},point_eqs:\"\"},{baseVals:{a:1,enabled:0,b:1,g:1,scaling:1,samples:512,additive:0,usedots:0,spectrum:0,r:1,smoothing:.5,thick:0,sep:0},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t},point_eqs:\"\"},{baseVals:{a:1,enabled:0,b:1,g:1,scaling:1,samples:512,additive:0,usedots:0,spectrum:0,r:1,smoothing:.5,thick:0,sep:0},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t},point_eqs:\"\"}],shapes:[{baseVals:{r2:0,a:1,enabled:0,b:0,tex_ang:0,thickoutline:0,g:0,textured:0,g2:1,tex_zoom:1,additive:0,border_a:.1,border_b:1,b2:0,a2:0,r:1,border_g:1,rad:.1,x:.5,y:.5,ang:0,sides:4,border_r:1},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t}},{baseVals:{r2:0,a:1,enabled:0,b:0,tex_ang:0,thickoutline:0,g:0,textured:0,g2:1,tex_zoom:1,additive:0,border_a:.1,border_b:1,b2:0,a2:0,r:1,border_g:1,rad:.1,x:.5,y:.5,ang:0,sides:4,border_r:1},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t}},{baseVals:{r2:0,a:1,enabled:0,b:0,tex_ang:0,thickoutline:0,g:0,textured:0,g2:1,tex_zoom:1,additive:0,border_a:.1,border_b:1,b2:0,a2:0,r:1,border_g:1,rad:.1,x:.5,y:.5,ang:0,sides:4,border_r:1},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t}},{baseVals:{r2:0,a:1,enabled:0,b:0,tex_ang:0,thickoutline:0,g:0,textured:0,g2:1,tex_zoom:1,additive:0,border_a:.1,border_b:1,b2:0,a2:0,r:1,border_g:1,rad:.1,x:.5,y:.5,ang:0,sides:4,border_r:1},init_eqs:function(t){return t.rkeys=[],t},frame_eqs:function(t){return t}}],warp:\"shader_body {\\nret = texture2D(sampler_main, uv).rgb;\\nret -= 0.004;\\n}\\n\",comp:\"shader_body {\\nret = texture2D(sampler_main, uv).rgb;\\nret *= hue_shader;\\n}\\n\"}}.apply(e,[]))||(t.exports=s)},function(t,e,i){\"use strict\";{const t=(t,e)=>{var i=\"function\"==typeof e,s=\"function\"==typeof e,r=\"function\"==typeof e;Object.defineProperty(Math,t,{configurable:i,enumerable:r,writable:s,value:e})};t(\"DEG_PER_RAD\",Math.PI/180),t(\"RAD_PER_DEG\",180/Math.PI);const e=new Float32Array(1);t(\"scale\",function(t,e,i,s,r){return 0===arguments.length?NaN:Number.isNaN(t)||Number.isNaN(e)||Number.isNaN(i)||Number.isNaN(s)||Number.isNaN(r)?NaN:t===1/0||t===-1/0?t:(t-e)*(r-s)/(i-e)+s}),t(\"fscale\",function(t,i,s,r,a){return e[0]=Math.scale(t,i,s,r,a),e[0]}),t(\"clamp\",function(t,e,i){return Math.min(i,Math.max(e,t))}),t(\"radians\",function(t){return t*Math.DEG_PER_RAD}),t(\"degrees\",function(t){return t*Math.RAD_PER_DEG})}},function(t,e){window.sqr=function(t){return t*t},window.sqrt=function(t){return Math.sqrt(Math.abs(t))},window.log10=function(t){return Math.log(t)*Math.LOG10E},window.sign=function(t){return t>0?1:t<0?-1:0},window.rand=function(t){var e=Math.floor(t);return e<1?Math.random():Math.random()*e},window.randint=function(t){return Math.floor(rand(t))},window.bnot=function(t){return Math.abs(t)<1e-5?1:0},window.pow=function(t,e){var i,s=Math.pow(t,e);return i=s,!isFinite(i)||isNaN(i)?0:s},window.div=function(t,e){return 0===e?0:t/e},window.mod=function(t,e){return 0===e?0:Math.floor(t)%Math.floor(e)},window.bitor=function(t,e){return Math.floor(t)|Math.floor(e)},window.bitand=function(t,e){return Math.floor(t)&Math.floor(e)},window.sigmoid=function(t,e){var i=1+Math.exp(-t*e);return Math.abs(i)>1e-5?1/i:0},window.bor=function(t,e){return Math.abs(t)>1e-5||Math.abs(e)>1e-5?1:0},window.band=function(t,e){return Math.abs(t)>1e-5&&Math.abs(e)>1e-5?1:0},window.equal=function(t,e){return Math.abs(t-e)<1e-5?1:0},window.above=function(t,e){return t>e?1:0},window.below=function(t,e){return t<e?1:0},window.ifcond=function(t,e,i){return Math.abs(t)>1e-5?e:i},window.memcpy=function(t,e,i,s){var r=e,a=i,h=s;return a<0&&(h+=a,r-=a,a=0),r<0&&(h+=r,a-=r,r=0),h>0&&t.copyWithin(r,a,h),e}},,,function(t,e,i){\"use strict\";i.r(e);i(1),i(2);function s(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var r=function(){function t(e,i){var s=arguments.length>2&&void 0!==arguments[2]&&arguments[2];!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.samplesIn=e,this.samplesOut=i,this.equalize=s,this.NFREQ=2*i,this.equalize&&this.initEqualizeTable(),this.initBitRevTable(),this.initCosSinTable()}var e,i,r;return e=t,(i=[{key:\"initEqualizeTable\",value:function(){this.equalizeArr=new Float32Array(this.samplesOut);for(var t=1/this.samplesOut,e=0;e<this.samplesOut;e++)this.equalizeArr[e]=-.02*Math.log((this.samplesOut-e)*t)}},{key:\"initBitRevTable\",value:function(){this.bitrevtable=new Uint16Array(this.NFREQ);for(var t=0;t<this.NFREQ;t++)this.bitrevtable[t]=t;for(var e=0,i=0;i<this.NFREQ;i++){if(e>i){var s=this.bitrevtable[i];this.bitrevtable[i]=this.bitrevtable[e],this.bitrevtable[e]=s}for(var r=this.NFREQ>>1;r>=1&&e>=r;)e-=r,r>>=1;e+=r}}},{key:\"initCosSinTable\",value:function(){for(var t=2,e=0;t<=this.NFREQ;)e+=1,t<<=1;this.cossintable=[new Float32Array(e),new Float32Array(e)],t=2;for(var i=0;t<=this.NFREQ;){var s=-2*Math.PI/t;this.cossintable[0][i]=Math.cos(s),this.cossintable[1][i]=Math.sin(s),i+=1,t<<=1}}},{key:\"timeToFrequencyDomain\",value:function(t){for(var e=new Float32Array(this.NFREQ),i=new Float32Array(this.NFREQ),s=0;s<this.NFREQ;s++){var r=this.bitrevtable[s];r<this.samplesIn?e[s]=t[r]:e[s]=0,i[s]=0}for(var a=2,h=0;a<=this.NFREQ;){for(var o=this.cossintable[0][h],n=this.cossintable[1][h],l=1,m=0,u=a>>1,g=0;g<u;g++){for(var c=g;c<this.NFREQ;c+=a){var A=c+u,f=l*e[A]-m*i[A],d=l*i[A]+m*e[A];e[A]=e[c]-f,i[A]=i[c]-d,e[c]+=f,i[c]+=d}var v=l;l=v*o-m*n,m=m*o+v*n}a<<=1,h+=1}var p=new Float32Array(this.samplesOut);if(this.equalize)for(var _=0;_<this.samplesOut;_++)p[_]=this.equalizeArr[_]*Math.sqrt(e[_]*e[_]+i[_]*i[_]);else for(var x=0;x<this.samplesOut;x++)p[x]=Math.sqrt(e[x]*e[x]+i[x]*i[x]);return p}}])&&s(e.prototype,i),r&&s(e,r),t}();function a(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var h=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.numSamps=512,this.fftSize=2*this.numSamps,this.fft=new r(this.fftSize,512,!0),e&&(this.audioContext=e,this.audible=e.createDelay(),this.analyser=e.createAnalyser(),this.analyser.smoothingTimeConstant=0,this.analyser.fftSize=this.fftSize,this.audible.connect(this.analyser),this.analyserL=e.createAnalyser(),this.analyserL.smoothingTimeConstant=0,this.analyserL.fftSize=this.fftSize,this.analyserR=e.createAnalyser(),this.analyserR.smoothingTimeConstant=0,this.analyserR.fftSize=this.fftSize,this.splitter=e.createChannelSplitter(2),this.audible.connect(this.splitter),this.splitter.connect(this.analyserL,0),this.splitter.connect(this.analyserR,1)),this.timeByteArray=new Uint8Array(this.fftSize),this.timeByteArrayL=new Uint8Array(this.fftSize),this.timeByteArrayR=new Uint8Array(this.fftSize),this.timeArray=new Int8Array(this.fftSize),this.timeByteArraySignedL=new Int8Array(this.fftSize),this.timeByteArraySignedR=new Int8Array(this.fftSize),this.tempTimeArrayL=new Int8Array(this.fftSize),this.tempTimeArrayR=new Int8Array(this.fftSize),this.timeArrayL=new Int8Array(this.numSamps),this.timeArrayR=new Int8Array(this.numSamps)}var e,i,s;return e=t,(i=[{key:\"sampleAudio\",value:function(){this.analyser.getByteTimeDomainData(this.timeByteArray),this.analyserL.getByteTimeDomainData(this.timeByteArrayL),this.analyserR.getByteTimeDomainData(this.timeByteArrayR),this.processAudio()}},{key:\"updateAudio\",value:function(t,e,i){this.timeByteArray.set(t),this.timeByteArrayL.set(e),this.timeByteArrayR.set(i),this.processAudio()}},{key:\"processAudio\",value:function(){for(var t=0,e=0,i=0;t<this.fftSize;t++)this.timeArray[t]=this.timeByteArray[t]-128,this.timeByteArraySignedL[t]=this.timeByteArrayL[t]-128,this.timeByteArraySignedR[t]=this.timeByteArrayR[t]-128,this.tempTimeArrayL[t]=.5*(this.timeByteArraySignedL[t]+this.timeByteArraySignedL[i]),this.tempTimeArrayR[t]=.5*(this.timeByteArraySignedR[t]+this.timeByteArraySignedR[i]),t%2==0&&(this.timeArrayL[e]=this.tempTimeArrayL[t],this.timeArrayR[e]=this.tempTimeArrayR[t],e+=1),i=t;this.freqArray=this.fft.timeToFrequencyDomain(this.timeArray),this.freqArrayL=this.fft.timeToFrequencyDomain(this.timeByteArraySignedL),this.freqArrayR=this.fft.timeToFrequencyDomain(this.timeByteArraySignedR)}},{key:\"connectAudio\",value:function(t){t.connect(this.audible)}},{key:\"disconnectAudio\",value:function(t){t.disconnect(this.audible)}}])&&a(e.prototype,i),s&&a(e,s),t}();function o(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var n=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.audio=e;var i=(this.audio.audioContext?this.audio.audioContext.sampleRate:44100)/this.audio.fftSize,s=Math.clamp(Math.round(20/i)-1,0,this.audio.numSamps-1),r=Math.clamp(Math.round(320/i)-1,0,this.audio.numSamps-1),a=Math.clamp(Math.round(2800/i)-1,0,this.audio.numSamps-1),h=Math.clamp(Math.round(11025/i)-1,0,this.audio.numSamps-1);this.starts=[s,r,a],this.stops=[r,a,h],this.val=new Float32Array(3),this.imm=new Float32Array(3),this.att=new Float32Array(3),this.avg=new Float32Array(3),this.longAvg=new Float32Array(3),this.att.fill(1),this.avg.fill(1),this.longAvg.fill(1)}var e,i,s;return e=t,s=[{key:\"isFiniteNumber\",value:function(t){return Number.isFinite(t)&&!Number.isNaN(t)}},{key:\"adjustRateToFPS\",value:function(t,e,i){return Math.pow(t,e/i)}}],(i=[{key:\"updateAudioLevels\",value:function(e,i){if(this.audio.freqArray.length>0){var s=e;!t.isFiniteNumber(s)||s<15?s=15:s>144&&(s=144),this.imm.fill(0);for(var r=0;r<3;r++)for(var a=this.starts[r];a<this.stops[r];a++)this.imm[r]+=this.audio.freqArray[a];for(var h=0;h<3;h++){var o=void 0;o=this.imm[h]>this.avg[h]?.2:.5,o=t.adjustRateToFPS(o,30,s),this.avg[h]=this.avg[h]*o+this.imm[h]*(1-o),o=i<50?.9:.992,o=t.adjustRateToFPS(o,30,s),this.longAvg[h]=this.longAvg[h]*o+this.imm[h]*(1-o),this.longAvg[h]<.001?(this.val[h]=1,this.att[h]=1):(this.val[h]=this.imm[h]/this.longAvg[h],this.att[h]=this.avg[h]/this.longAvg[h])}}}},{key:\"bass\",get:function(){return this.val[0]}},{key:\"bass_att\",get:function(){return this.att[0]}},{key:\"mid\",get:function(){return this.val[1]}},{key:\"mid_att\",get:function(){return this.att[1]}},{key:\"treb\",get:function(){return this.val[2]}},{key:\"treb_att\",get:function(){return this.att[2]}}])&&o(e.prototype,i),s&&o(e,s),t}(),l=i(0),m=i.n(l);function u(t){return function(t){if(Array.isArray(t)){for(var e=0,i=new Array(t.length);e<t.length;e++)i[e]=t[e];return i}}(t)||function(t){if(Symbol.iterator in Object(t)||\"[object Arguments]\"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance\")}()}function g(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var c=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i,s;return e=t,s=[{key:\"atan2\",value:function(t,e){var i=Math.atan2(t,e);return i<0&&(i+=2*Math.PI),i}},{key:\"cloneVars\",value:function(t){return Object.assign({},t)}},{key:\"range\",value:function(t,e){return void 0===e?u(Array(t).keys()):Array.from({length:e-t},function(e,i){return i+t})}},{key:\"pick\",value:function(t,e){for(var i={},s=0;s<e.length;s++){var r=e[s];i[r]=t[r]}return i}},{key:\"omit\",value:function(t,e){for(var i=Object.assign({},t),s=0;s<e.length;s++){delete i[e[s]]}return i}}],(i=null)&&g(e.prototype,i),s&&g(e,s),t}();function A(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var f=function(){function t(e,i,s){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.preset=e,this.texsizeX=s.texsizeX,this.texsizeY=s.texsizeY,this.mesh_width=s.mesh_width,this.mesh_height=s.mesh_height,this.aspectx=s.aspectx,this.aspecty=s.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.qs=c.range(1,33).map(function(t){return\"q\".concat(t)}),this.ts=c.range(1,9).map(function(t){return\"t\".concat(t)}),this.regs=c.range(100).map(function(t){return t<10?\"reg0\".concat(t):\"reg\".concat(t)}),this.initializeEquations(i)}var e,i,s;return e=t,(i=[{key:\"initializeEquations\",value:function(t){this.runVertEQs=\"\"!==this.preset.pixel_eqs,this.mdVSQInit=null,this.mdVSRegs=null,this.mdVSFrame=null,this.mdVSUserKeys=null,this.mdVSFrameMap=null,this.mdVSShapes=null,this.mdVSUserKeysShapes=null,this.mdVSFrameMapShapes=null,this.mdVSWaves=null,this.mdVSUserKeysWaves=null,this.mdVSFrameMapWaves=null,this.mdVSQAfterFrame=null,this.gmegabuf=new Array(1048576).fill(0);var e={frame:t.frame,time:t.time,fps:t.fps,bass:t.bass,bass_att:t.bass_att,mid:t.mid,mid_att:t.mid_att,treb:t.treb,treb_att:t.treb_att,meshx:this.mesh_width,meshy:this.mesh_height,aspectx:this.invAspectx,aspecty:this.invAspecty,pixelsx:this.texsizeX,pixelsy:this.texsizeY,gmegabuf:this.gmegabuf};this.mdVS=Object.assign({},this.preset.baseVals,e),this.mdVS.megabuf=new Array(1048576).fill(0),this.mdVS.rand_start=new Float32Array([Math.random(),Math.random(),Math.random(),Math.random()]),this.mdVS.rand_preset=new Float32Array([Math.random(),Math.random(),Math.random(),Math.random()]);var i=this.qs.concat(this.regs,Object.keys(this.mdVS)),s=this.preset.init_eqs(c.cloneVars(this.mdVS));this.mdVSQInit=c.pick(s,this.qs),this.mdVSRegs=c.pick(s,this.regs);var r=c.pick(s,Object.keys(c.omit(s,i)));if(r.megabuf=s.megabuf,r.gmegabuf=s.gmegabuf,this.mdVSFrame=this.preset.frame_eqs(Object.assign({},this.mdVS,this.mdVSQInit,this.mdVSRegs,r)),this.mdVSUserKeys=Object.keys(c.omit(this.mdVSFrame,i)),this.mdVSFrameMap=c.pick(this.mdVSFrame,this.mdVSUserKeys),this.mdVSQAfterFrame=c.pick(this.mdVSFrame,this.qs),this.mdVSRegs=c.pick(this.mdVSFrame,this.regs),this.mdVSWaves=[],this.mdVSTWaveInits=[],this.mdVSUserKeysWaves=[],this.mdVSFrameMapWaves=[],this.preset.waves&&this.preset.waves.length>0)for(var a=0;a<this.preset.waves.length;a++){var h=this.preset.waves[a],o=h.baseVals;if(0!==o.enabled){var n=Object.assign({},o,e),l=this.qs.concat(this.ts,this.regs,Object.keys(n));Object.assign(n,this.mdVSQAfterFrame,this.mdVSRegs),n.megabuf=new Array(1048576).fill(0),h.init_eqs&&(n=h.init_eqs(n),this.mdVSRegs=c.pick(n,this.regs),Object.assign(n,o)),this.mdVSWaves.push(n),this.mdVSTWaveInits.push(c.pick(n,this.ts)),this.mdVSUserKeysWaves.push(Object.keys(c.omit(n,l))),this.mdVSFrameMapWaves.push(c.pick(n,this.mdVSUserKeysWaves[a]))}else this.mdVSWaves.push({}),this.mdVSTWaveInits.push({}),this.mdVSUserKeysWaves.push([]),this.mdVSFrameMapWaves.push({})}if(this.mdVSShapes=[],this.mdVSTShapeInits=[],this.mdVSUserKeysShapes=[],this.mdVSFrameMapShapes=[],this.preset.shapes&&this.preset.shapes.length>0)for(var m=0;m<this.preset.shapes.length;m++){var u=this.preset.shapes[m],g=u.baseVals;if(0!==g.enabled){var A=Object.assign({},g,e),f=this.qs.concat(this.ts,this.regs,Object.keys(A));Object.assign(A,this.mdVSQAfterFrame,this.mdVSRegs),A.megabuf=new Array(1048576).fill(0),u.init_eqs&&(A=u.init_eqs(A),this.mdVSRegs=c.pick(A,this.regs),Object.assign(A,g)),this.mdVSShapes.push(A),this.mdVSTShapeInits.push(c.pick(A,this.ts)),this.mdVSUserKeysShapes.push(Object.keys(c.omit(A,f))),this.mdVSFrameMapShapes.push(c.pick(A,this.mdVSUserKeysShapes[m]))}else this.mdVSShapes.push({}),this.mdVSTShapeInits.push({}),this.mdVSUserKeysShapes.push([]),this.mdVSFrameMapShapes.push({})}}},{key:\"updatePreset\",value:function(t,e){this.preset=t,this.initializeEquations(e)}},{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty}},{key:\"runFrameEquations\",value:function(t){this.mdVSFrame=Object.assign({},this.mdVS,this.mdVSQInit,this.mdVSFrameMap,t),this.mdVSFrame=this.preset.frame_eqs(this.mdVSFrame),this.mdVSFrameMap=c.pick(this.mdVSFrame,this.mdVSUserKeys),this.mdVSQAfterFrame=c.pick(this.mdVSFrame,this.qs)}}])&&A(e.prototype,i),s&&A(e,s),t}();function d(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var v=/uniform sampler2D sampler_(?:.+?);/g,p=/uniform sampler2D sampler_(.+?);/,_=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i,s;return e=t,s=[{key:\"getShaderParts\",value:function(t){var e=t.indexOf(\"shader_body\");if(t&&e>-1){var i=t.substring(0,e),s=t.substring(e),r=s.indexOf(\"{\"),a=s.lastIndexOf(\"}\");return[i,s.substring(r+1,a)]}return[\"\",t]}},{key:\"getFragmentFloatPrecision\",value:function(t){return t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.HIGH_FLOAT).precision>0?\"highp\":t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.MEDIUM_FLOAT).precision>0?\"mediump\":\"lowp\"}},{key:\"getUserSamplers\",value:function(t){var e=[],i=t.match(v);if(i&&i.length>0)for(var s=0;s<i.length;s++){var r=i[s].match(p);if(r&&r.length>0){var a=r[1];e.push({sampler:a})}}return e}}],(i=null)&&d(e.prototype,i),s&&d(e,s),t}();function x(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var b=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i,s;return e=t,s=[{key:\"smoothWave\",value:function(t,e,i){for(var s,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=0,h=0,o=1,n=0;n<i-1;n++){s=o,o=Math.min(i-1,n+2);for(var l=0;l<3;l++)e[3*a+l]=t[3*n+l];if(r)for(var m=0;m<3;m++)e[3*(a+1)+m]=.5*(-.15*t[3*h+m]+1.15*t[3*n+m]+1.15*t[3*s+m]+-.15*t[3*o+m]);else{for(var u=0;u<2;u++)e[3*(a+1)+u]=.5*(-.15*t[3*h+u]+1.15*t[3*n+u]+1.15*t[3*s+u]+-.15*t[3*o+u]);e[3*(a+1)+2]=0}h=n,a+=2}for(var g=0;g<3;g++)e[3*a+g]=t[3*(i-1)+g]}},{key:\"smoothWaveAndColor\",value:function(t,e,i,s,r){for(var a,h=arguments.length>5&&void 0!==arguments[5]&&arguments[5],o=0,n=0,l=1,m=0;m<r-1;m++){a=l,l=Math.min(r-1,m+2);for(var u=0;u<3;u++)i[3*o+u]=t[3*m+u];if(h)for(var g=0;g<3;g++)i[3*(o+1)+g]=.5*(-.15*t[3*n+g]+1.15*t[3*m+g]+1.15*t[3*a+g]+-.15*t[3*l+g]);else{for(var c=0;c<2;c++)i[3*(o+1)+c]=.5*(-.15*t[3*n+c]+1.15*t[3*m+c]+1.15*t[3*a+c]+-.15*t[3*l+c]);i[3*(o+1)+2]=0}for(var A=0;A<4;A++)s[4*o+A]=e[4*m+A],s[4*(o+1)+A]=e[4*m+A];n=m,o+=2}for(var f=0;f<3;f++)i[3*o+f]=t[3*(r-1)+f];for(var d=0;d<4;d++)s[4*o+d]=e[4*(r-1)+d]}}],(i=null)&&x(e.prototype,i),s&&x(e,s),t}();function T(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var E=function(){function t(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e;this.positions=new Float32Array(1536),this.positions2=new Float32Array(1536),this.oldPositions=new Float32Array(1536),this.oldPositions2=new Float32Array(1536),this.smoothedPositions=new Float32Array(3069),this.smoothedPositions2=new Float32Array(3069),this.color=[0,0,0,1],this.texsizeX=i.texsizeX,this.texsizeY=i.texsizeY,this.aspectx=i.aspectx,this.aspecty=i.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader(),this.vertexBuf=this.gl.createBuffer()}var e,i,s;return e=t,s=[{key:\"processWaveform\",value:function(t,e){var i=[],s=e.wave_scale/128,r=e.wave_smoothing,a=s*(1-r);i.push(t[0]*s);for(var h=1;h<t.length;h++)i.push(t[h]*a+i[h-1]*r);return i}}],(i=[{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aPos;\\n                                      uniform vec2 thickOffset;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos + vec3(thickOffset, 0.0), 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      out vec4 fragColor;\\n                                      uniform vec4 u_color;\\n                                      void main(void) {\\n                                        fragColor = u_color;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLoc=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.colorLoc=this.gl.getUniformLocation(this.shaderProgram,\"u_color\"),this.thickOffsetLoc=this.gl.getUniformLocation(this.shaderProgram,\"thickOffset\")}},{key:\"generateWaveform\",value:function(e,i,s,r,a){var h=a.wave_a,o=(a.bass+a.mid+a.treb)/3;if(o>-.01&&h>.001&&s.length>0){var n=t.processWaveform(s,a),l=t.processWaveform(r,a),m=Math.floor(a.wave_mode)%8,u=Math.floor(a.old_wave_mode)%8,g=2*a.wave_x-1,c=2*a.wave_y-1;this.numVert=0,this.oldNumVert=0;for(var A=e&&m!==u?2:1,f=0;f<A;f++){var d=0===f?m:u,v=a.wave_mystery;0!==d&&1!==d&&4!==d||!(v<-1||v>1)||(v=.5*v+.5,v-=Math.floor(v),v=2*(v=Math.abs(v))-1);var p=void 0,_=void 0,x=void 0;if(0===f?(_=this.positions,x=this.positions2):(_=this.oldPositions,x=this.oldPositions2),h=a.wave_a,0===d){if(a.modwavealphabyvolume>0){var T=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/T}h=Math.clamp(h,0,1);for(var E=1/((p=Math.floor(n.length/2)+1)-1),P=Math.floor((n.length-p)/2),R=0;R<p-1;R++){var L=.5+.4*l[R+P]+v,S=R*E*2*Math.PI+.2*a.time;if(R<p/10){var y=R/(.1*p);L=(1-(y=.5-.5*Math.cos(y*Math.PI)))*(.5+.4*l[R+p+P]+v)+L*y}_[3*R+0]=L*Math.cos(S)*this.aspecty+g,_[3*R+1]=L*Math.sin(S)*this.aspectx+c,_[3*R+2]=0}_[3*(p-1)+0]=_[0],_[3*(p-1)+1]=_[1],_[3*(p-1)+2]=0}else if(1===d){if(h*=1.25,a.modwavealphabyvolume>0){var w=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/w}h=Math.clamp(h,0,1),p=Math.floor(n.length/2);for(var U=0;U<p;U++){var M=.53+.43*l[U]+v,F=.5*n[U+32]*Math.PI+2.3*a.time;_[3*U+0]=M*Math.cos(F)*this.aspecty+g,_[3*U+1]=M*Math.sin(F)*this.aspectx+c,_[3*U+2]=0}}else if(2===d){if(this.texsizeX<1024?h*=.09:this.texsizeX>=1024&&this.texsizeX<2048?h*=.11:h*=.13,a.modwavealphabyvolume>0){var q=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/q}h=Math.clamp(h,0,1),p=n.length;for(var z=0;z<n.length;z++)_[3*z+0]=l[z]*this.aspecty+g,_[3*z+1]=n[(z+32)%n.length]*this.aspectx+c,_[3*z+2]=0}else if(3===d){if(this.texsizeX<1024?h*=.15:this.texsizeX>=1024&&this.texsizeX<2048?h*=.22:h*=.33,h*=1.3,h*=a.treb*a.treb,a.modwavealphabyvolume>0){var B=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/B}h=Math.clamp(h,0,1),p=n.length;for(var C=0;C<n.length;C++)_[3*C+0]=l[C]*this.aspecty+g,_[3*C+1]=n[(C+32)%n.length]*this.aspectx+c,_[3*C+2]=0}else if(4===d){if(a.modwavealphabyvolume>0){var D=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/D}h=Math.clamp(h,0,1),(p=n.length)>this.texsizeX/3&&(p=Math.floor(this.texsizeX/3));for(var V=1/p,I=Math.floor((n.length-p)/2),X=.45+.5*(.5*v+.5),k=1-X,N=0;N<p;N++){var O=2*N*V+(g-1)+.44*l[(N+25+I)%n.length],W=.47*n[N+I]+c;N>1&&(O=O*k+X*(2*_[3*(N-1)+0]-_[3*(N-2)+0]),W=W*k+X*(2*_[3*(N-1)+1]-_[3*(N-2)+1])),_[3*N+0]=O,_[3*N+1]=W,_[3*N+2]=0}}else if(5===d){if(this.texsizeX<1024?h*=.09:this.texsizeX>=1024&&this.texsizeX<2048?h*=.11:h*=.13,a.modwavealphabyvolume>0){var Q=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/Q}h=Math.clamp(h,0,1);var Y=Math.cos(.3*a.time),G=Math.sin(.3*a.time);p=n.length;for(var H=0;H<n.length;H++){var j=(H+32)%n.length,K=l[H]*n[j]+n[H]*l[j],J=l[H]*l[H]-n[j]*n[j];_[3*H+0]=(K*Y-J*G)*(this.aspecty+g),_[3*H+1]=(K*G+J*Y)*(this.aspectx+c),_[3*H+2]=0}}else if(6===d||7===d){if(a.modwavealphabyvolume>0){var Z=a.modwavealphaend-a.modwavealphastart;h*=(o-a.modwavealphastart)/Z}h=Math.clamp(h,0,1),(p=Math.floor(n.length/2))>this.texsizeX/3&&(p=Math.floor(this.texsizeX/3));for(var $=Math.floor((n.length-p)/2),tt=.5*Math.PI*v,et=Math.cos(tt),it=Math.sin(tt),st=[g*Math.cos(tt+.5*Math.PI)-3*et,g*Math.cos(tt+.5*Math.PI)+3*et],rt=[g*Math.sin(tt+.5*Math.PI)-3*it,g*Math.sin(tt+.5*Math.PI)+3*it],at=0;at<2;at++)for(var ht=0;ht<4;ht++){var ot=void 0,nt=!1;switch(ht){case 0:st[at]>1.1&&(ot=(1.1-st[1-at])/(st[at]-st[1-at]),nt=!0);break;case 1:st[at]<-1.1&&(ot=(-1.1-st[1-at])/(st[at]-st[1-at]),nt=!0);break;case 2:rt[at]>1.1&&(ot=(1.1-rt[1-at])/(rt[at]-rt[1-at]),nt=!0);break;case 3:rt[at]<-1.1&&(ot=(-1.1-rt[1-at])/(rt[at]-rt[1-at]),nt=!0)}if(nt){var lt=st[at]-st[1-at],mt=rt[at]-rt[1-at];st[at]=st[1-at]+lt*ot,rt[at]=rt[1-at]+mt*ot}}et=(st[1]-st[0])/p,it=(rt[1]-rt[0])/p;var ut=Math.atan2(it,et),gt=Math.cos(ut+.5*Math.PI),ct=Math.sin(ut+.5*Math.PI);if(6===d)for(var At=0;At<p;At++){var ft=n[At+$];_[3*At+0]=st[0]+et*At+.25*gt*ft,_[3*At+1]=rt[0]+it*At+.25*ct*ft,_[3*At+2]=0}else if(7===d){for(var dt=Math.pow(.5*c+.5,2),vt=0;vt<p;vt++){var pt=n[vt+$];_[3*vt+0]=st[0]+et*vt+gt*(.25*pt+dt),_[3*vt+1]=rt[0]+it*vt+ct*(.25*pt+dt),_[3*vt+2]=0}for(var _t=0;_t<p;_t++){var xt=l[_t+$];x[3*_t+0]=st[0]+et*_t+gt*(.25*xt-dt),x[3*_t+1]=rt[0]+it*_t+ct*(.25*xt-dt),x[3*_t+2]=0}}}0===f?(this.positions=_,this.positions2=x,this.numVert=p,this.alpha=h):(this.oldPositions=_,this.oldPositions2=x,this.oldNumVert=p,this.oldAlpha=h)}var bt=.5-.5*Math.cos(i*Math.PI),Tt=1-bt;this.oldNumVert>0&&(h=bt*this.alpha+Tt*this.oldAlpha);var Et=Math.clamp(a.wave_r,0,1),Pt=Math.clamp(a.wave_g,0,1),Rt=Math.clamp(a.wave_b,0,1);if(0!==a.wave_brighten){var Lt=Math.max(Et,Pt,Rt);Lt>.01&&(Et/=Lt,Pt/=Lt,Rt/=Lt)}if(this.color=[Et,Pt,Rt,h],this.oldNumVert>0)if(7===m){for(var St=(this.oldNumVert-1)/(2*this.numVert),yt=0;yt<this.numVert;yt++){var wt=yt*St,Ut=Math.floor(wt),Mt=wt-Ut,Ft=this.oldPositions[3*Ut+0]*(1-Mt)+this.oldPositions[3*(Ut+1)+0]*Mt,qt=this.oldPositions[3*Ut+1]*(1-Mt)+this.oldPositions[3*(Ut+1)+1]*Mt;this.positions[3*yt+0]=this.positions[3*yt+0]*bt+Ft*Tt,this.positions[3*yt+1]=this.positions[3*yt+1]*bt+qt*Tt,this.positions[3*yt+2]=0}for(var zt=0;zt<this.numVert;zt++){var Bt=(zt+this.numVert)*St,Ct=Math.floor(Bt),Dt=Bt-Ct,Vt=this.oldPositions[3*Ct+0]*(1-Dt)+this.oldPositions[3*(Ct+1)+0]*Dt,It=this.oldPositions[3*Ct+1]*(1-Dt)+this.oldPositions[3*(Ct+1)+1]*Dt;this.positions2[3*zt+0]=this.positions2[3*zt+0]*bt+Vt*Tt,this.positions2[3*zt+1]=this.positions2[3*zt+1]*bt+It*Tt,this.positions2[3*zt+2]=0}}else if(7===u){for(var Xt=this.numVert/2,kt=(this.oldNumVert-1)/Xt,Nt=0;Nt<Xt;Nt++){var Ot=Nt*kt,Wt=Math.floor(Ot),Qt=Ot-Wt,Yt=this.oldPositions[3*Wt+0]*(1-Qt)+this.oldPositions[3*(Wt+1)+0]*Qt,Gt=this.oldPositions[3*Wt+1]*(1-Qt)+this.oldPositions[3*(Wt+1)+1]*Qt;this.positions[3*Nt+0]=this.positions[3*Nt+0]*bt+Yt*Tt,this.positions[3*Nt+1]=this.positions[3*Nt+1]*bt+Gt*Tt,this.positions[3*Nt+2]=0}for(var Ht=0;Ht<Xt;Ht++){var jt=Ht*kt,Kt=Math.floor(jt),Jt=jt-Kt,Zt=this.oldPositions2[3*Kt+0]*(1-Jt)+this.oldPositions2[3*(Kt+1)+0]*Jt,$t=this.oldPositions2[3*Kt+1]*(1-Jt)+this.oldPositions2[3*(Kt+1)+1]*Jt;this.positions2[3*Ht+0]=this.positions[3*(Ht+Xt)+0]*bt+Zt*Tt,this.positions2[3*Ht+1]=this.positions[3*(Ht+Xt)+1]*bt+$t*Tt,this.positions2[3*Ht+2]=0}}else for(var te=(this.oldNumVert-1)/this.numVert,ee=0;ee<this.numVert;ee++){var ie=ee*te,se=Math.floor(ie),re=ie-se,ae=this.oldPositions[3*se+0]*(1-re)+this.oldPositions[3*(se+1)+0]*re,he=this.oldPositions[3*se+1]*(1-re)+this.oldPositions[3*(se+1)+1]*re;this.positions[3*ee+0]=this.positions[3*ee+0]*bt+ae*Tt,this.positions[3*ee+1]=this.positions[3*ee+1]*bt+he*Tt,this.positions[3*ee+2]=0}for(var oe=0;oe<this.numVert;oe++)this.positions[3*oe+1]=-this.positions[3*oe+1];if(this.smoothedNumVert=2*this.numVert-1,b.smoothWave(this.positions,this.smoothedPositions,this.numVert),7===m||7===u){for(var ne=0;ne<this.numVert;ne++)this.positions2[3*ne+1]=-this.positions2[3*ne+1];b.smoothWave(this.positions2,this.smoothedPositions2,this.numVert)}return!0}return!1}},{key:\"drawBasicWaveform\",value:function(t,e,i,s,r){if(this.generateWaveform(t,e,i,s,r)){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.smoothedPositions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLoc),this.gl.uniform4fv(this.colorLoc,this.color);var a=1;0===r.wave_thick&&0===r.wave_dots||(a=4),0!==r.additivewave?this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE):this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA);for(var h=0!==r.wave_dots?this.gl.POINTS:this.gl.LINE_STRIP,o=0;o<a;o++){0===o?this.gl.uniform2fv(this.thickOffsetLoc,[0,0]):1===o?this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,0]):2===o?this.gl.uniform2fv(this.thickOffsetLoc,[0,2/this.texsizeY]):3===o&&this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,2/this.texsizeY]),this.gl.drawArrays(h,0,this.smoothedNumVert)}if(7===Math.floor(r.wave_mode)%8){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.smoothedPositions2,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLoc);for(var n=0;n<a;n++){0===n?this.gl.uniform2fv(this.thickOffsetLoc,[0,0]):1===n?this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,0]):2===n?this.gl.uniform2fv(this.thickOffsetLoc,[0,2/this.texsizeY]):3===n&&this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,2/this.texsizeY]),this.gl.drawArrays(h,0,this.smoothedNumVert)}}}}}])&&T(e.prototype,i),s&&T(e,s),t}();function P(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var R=function(){function t(e,i,s){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.index=e,this.gl=i;this.pointsData=[new Float32Array(512),new Float32Array(512)],this.positions=new Float32Array(1536),this.colors=new Float32Array(2048),this.smoothedPositions=new Float32Array(3069),this.smoothedColors=new Float32Array(4092),this.texsizeX=s.texsizeX,this.texsizeY=s.texsizeY,this.mesh_width=s.mesh_width,this.mesh_height=s.mesh_height,this.aspectx=s.aspectx,this.aspecty=s.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.positionVertexBuf=this.gl.createBuffer(),this.colorVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      uniform float uSize;\\n                                      uniform vec2 thickOffset;\\n                                      in vec3 aPos;\\n                                      in vec4 aColor;\\n                                      out vec4 vColor;\\n                                      void main(void) {\\n                                        vColor = aColor;\\n                                        gl_PointSize = uSize;\\n                                        gl_Position = vec4(aPos + vec3(thickOffset, 0.0), 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      in vec4 vColor;\\n                                      out vec4 fragColor;\\n                                      void main(void) {\\n                                        fragColor = vColor;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.aColorLocation=this.gl.getAttribLocation(this.shaderProgram,\"aColor\"),this.sizeLoc=this.gl.getUniformLocation(this.shaderProgram,\"uSize\"),this.thickOffsetLoc=this.gl.getUniformLocation(this.shaderProgram,\"thickOffset\")}},{key:\"generateWaveform\",value:function(t,e,i,s,r,a,h,o){if(0!==h.baseVals.enabled&&t.length>0){var n=Object.assign({},a.mdVSWaves[this.index],a.mdVSFrameMapWaves[this.index],a.mdVSQAfterFrame,a.mdVSTWaveInits[this.index],r),l=h.frame_eqs(n);Object.prototype.hasOwnProperty.call(l,\"samples\")?this.samples=l.samples:this.samples=512,this.samples>512&&(this.samples=512),this.samples=Math.floor(this.samples);var m=Math.floor(l.sep),u=l.scaling,g=l.spectrum,A=l.smoothing,f=l.usedots,d=l.r,v=l.g,p=l.b,_=l.a,x=a.mdVS.wave_scale;if(this.samples-=m,this.samples>=2||0!==f&&this.samples>=1){var T=0!==g,E=(T?.15:.004)*u*x,P=T?i:t,R=T?s:e,L=T?0:Math.floor((512-this.samples)/2-m/2),S=T?0:Math.floor((512-this.samples)/2+m/2),y=T?(512-m)/this.samples:1,w=Math.pow(.98*A,.5),U=1-w;this.pointsData[0][0]=P[L],this.pointsData[1][0]=R[S];for(var M=1;M<this.samples;M++){var F=P[Math.floor(M*y+L)],q=R[Math.floor(M*y+S)];this.pointsData[0][M]=F*U+this.pointsData[0][M-1]*w,this.pointsData[1][M]=q*U+this.pointsData[1][M-1]*w}for(var z=this.samples-2;z>=0;z--)this.pointsData[0][z]=this.pointsData[0][z]*U+this.pointsData[0][z+1]*w,this.pointsData[1][z]=this.pointsData[1][z]*U+this.pointsData[1][z+1]*w;for(var B=0;B<this.samples;B++)this.pointsData[0][B]*=E,this.pointsData[1][B]*=E;for(var C=0;C<this.samples;C++){var D=this.pointsData[0][C],V=this.pointsData[1][C];l.sample=C/(this.samples-1),l.value1=D,l.value2=V,l.x=.5+D,l.y=.5+V,l.r=d,l.g=v,l.b=p,l.a=_,\"\"!==h.point_eqs&&(l=h.point_eqs(l));var I=(2*l.x-1)*this.invAspectx,X=(-2*l.y+1)*this.invAspecty,k=l.r,N=l.g,O=l.b,W=l.a;this.positions[3*C+0]=I,this.positions[3*C+1]=X,this.positions[3*C+2]=0,this.colors[4*C+0]=k,this.colors[4*C+1]=N,this.colors[4*C+2]=O,this.colors[4*C+3]=W*o}var Q=a.mdVSUserKeysWaves[this.index],Y=c.pick(l,Q);return a.mdVSFrameMapWaves[this.index]=Y,this.mdVSWaveFrame=l,0===f&&b.smoothWaveAndColor(this.positions,this.colors,this.smoothedPositions,this.smoothedColors,this.samples),!0}}return!1}},{key:\"drawCustomWaveform\",value:function(t,e,i,s,r,a,h,o){if(o&&this.generateWaveform(e,i,s,r,a,h,o,t)){this.gl.useProgram(this.shaderProgram);var n,l,m,u=0!==this.mdVSWaveFrame.usedots,g=0!==this.mdVSWaveFrame.thick,c=0!==this.mdVSWaveFrame.additive;u?(n=this.positions,l=this.colors,m=this.samples):(n=this.smoothedPositions,l=this.smoothedColors,m=2*this.samples-1),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,n,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.colorVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,l,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aColorLocation);var A=1;u?g?this.gl.uniform1f(this.sizeLoc,2+(this.texsizeX>=1024?1:0)):this.gl.uniform1f(this.sizeLoc,1+(this.texsizeX>=1024?1:0)):(this.gl.uniform1f(this.sizeLoc,1),g&&(A=4)),c?this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE):this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA);for(var f=u?this.gl.POINTS:this.gl.LINE_STRIP,d=0;d<A;d++){0===d?this.gl.uniform2fv(this.thickOffsetLoc,[0,0]):1===d?this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,0]):2===d?this.gl.uniform2fv(this.thickOffsetLoc,[0,2/this.texsizeY]):3===d&&this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,2/this.texsizeY]),this.gl.drawArrays(f,0,m)}}}}])&&P(e.prototype,i),s&&P(e,s),t}();function L(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var S=function(){function t(e,i,s){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.index=e,this.gl=i;this.positions=new Float32Array(309),this.colors=new Float32Array(412),this.uvs=new Float32Array(206),this.borderPositions=new Float32Array(306),this.texsizeX=s.texsizeX,this.texsizeY=s.texsizeY,this.mesh_width=s.mesh_width,this.mesh_height=s.mesh_height,this.aspectx=s.aspectx,this.aspecty=s.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.positionVertexBuf=this.gl.createBuffer(),this.colorVertexBuf=this.gl.createBuffer(),this.uvVertexBuf=this.gl.createBuffer(),this.borderPositionVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader(),this.createBorderShader(),this.mainSampler=this.gl.createSampler(),i.samplerParameteri(this.mainSampler,i.TEXTURE_MIN_FILTER,i.LINEAR_MIPMAP_LINEAR),i.samplerParameteri(this.mainSampler,i.TEXTURE_MAG_FILTER,i.LINEAR),i.samplerParameteri(this.mainSampler,i.TEXTURE_WRAP_S,i.REPEAT),i.samplerParameteri(this.mainSampler,i.TEXTURE_WRAP_T,i.REPEAT)}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aPos;\\n                                      in vec4 aColor;\\n                                      in vec2 aUv;\\n                                      out vec4 vColor;\\n                                      out vec2 vUv;\\n                                      void main(void) {\\n                                        vColor = aColor;\\n                                        vUv = aUv;\\n                                        gl_Position = vec4(aPos, 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      uniform sampler2D uTexture;\\n                                      uniform float uTextured;\\n                                      in vec4 vColor;\\n                                      in vec2 vUv;\\n                                      out vec4 fragColor;\\n                                      void main(void) {\\n                                        if (uTextured != 0.0) {\\n                                          fragColor = texture(uTexture, vUv) * vColor;\\n                                        } else {\\n                                          fragColor = vColor;\\n                                        }\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.aColorLocation=this.gl.getAttribLocation(this.shaderProgram,\"aColor\"),this.aUvLocation=this.gl.getAttribLocation(this.shaderProgram,\"aUv\"),this.texturedLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTextured\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\")}},{key:\"createBorderShader\",value:function(){this.borderShaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aBorderPos;\\n                                      uniform vec2 thickOffset;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aBorderPos +\\n                                                           vec3(thickOffset, 0.0), 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      out vec4 fragColor;\\n                                      uniform vec4 uBorderColor;\\n                                      void main(void) {\\n                                        fragColor = uBorderColor;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.borderShaderProgram,t),this.gl.attachShader(this.borderShaderProgram,e),this.gl.linkProgram(this.borderShaderProgram),this.aBorderPosLoc=this.gl.getAttribLocation(this.borderShaderProgram,\"aBorderPos\"),this.uBorderColorLoc=this.gl.getUniformLocation(this.borderShaderProgram,\"uBorderColor\"),this.thickOffsetLoc=this.gl.getUniformLocation(this.shaderProgram,\"thickOffset\")}},{key:\"drawCustomShape\",value:function(t,e,i,s,r){if(0!==s.baseVals.enabled){this.setupShapeBuffers(i.mdVSFrame);for(var a=Object.assign({},i.mdVSShapes[this.index],i.mdVSFrameMapShapes[this.index],i.mdVSQAfterFrame,i.mdVSTShapeInits[this.index],e),h=c.cloneVars(a),o=Math.clamp(a.num_inst,1,1024),n=0;n<o;n++){a.instance=n,a.x=h.x,a.y=h.y,a.rad=h.rad,a.ang=h.ang,a.r=h.r,a.g=h.g,a.b=h.b,a.a=h.a,a.r2=h.r2,a.g2=h.g2,a.b2=h.b2,a.a2=h.a2,a.border_r=h.border_r,a.border_g=h.border_g,a.border_b=h.border_b,a.border_a=h.border_a,a.thickoutline=h.thickoutline,a.textured=h.textured,a.tex_zoom=h.tex_zoom,a.tex_ang=h.tex_ang,a.additive=h.additive;var l=s.frame_eqs(a),m=l.sides;m=Math.clamp(m,3,100),m=Math.floor(m);var u=l.rad,g=l.ang,A=2*l.x-1,f=-2*l.y+1,d=l.r,v=l.g,p=l.b,_=l.a,x=l.r2,b=l.g2,T=l.b2,E=l.a2,P=l.border_r,R=l.border_g,L=l.border_b,S=l.border_a;this.borderColor=[P,R,L,S*t];var y=l.thickoutline,w=l.textured,U=l.tex_zoom,M=l.tex_ang,F=l.additive,q=this.borderColor[3]>0,z=Math.abs(w)>=1,B=Math.abs(y)>=1,C=Math.abs(F)>=1;this.positions[0]=A,this.positions[1]=f,this.positions[2]=0,this.colors[0]=d,this.colors[1]=v,this.colors[2]=p,this.colors[3]=_*t,z&&(this.uvs[0]=.5,this.uvs[1]=.5);for(var D=.25*Math.PI,V=1;V<=m+1;V++){var I=2*((V-1)/m)*Math.PI,X=I+g+D;if(this.positions[3*V+0]=A+u*Math.cos(X)*this.aspecty,this.positions[3*V+1]=f+u*Math.sin(X),this.positions[3*V+2]=0,this.colors[4*V+0]=x,this.colors[4*V+1]=b,this.colors[4*V+2]=T,this.colors[4*V+3]=E*t,z){var k=I+M+D;this.uvs[2*V+0]=.5+.5*Math.cos(k)/U*this.aspecty,this.uvs[2*V+1]=.5+.5*Math.sin(k)/U}q&&(this.borderPositions[3*(V-1)+0]=this.positions[3*V+0],this.borderPositions[3*(V-1)+1]=this.positions[3*V+1],this.borderPositions[3*(V-1)+2]=this.positions[3*V+2])}this.mdVSShapeFrame=l,this.drawCustomShapeInstance(r,m,z,q,B,C)}var N=i.mdVSUserKeysShapes[this.index],O=c.pick(this.mdVSShapeFrame,N);i.mdVSFrameMapShapes[this.index]=O}}},{key:\"setupShapeBuffers\",value:function(t){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.DYNAMIC_DRAW),this.gl.vertexAttribPointer(this.aPosLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.colorVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.colors,this.gl.DYNAMIC_DRAW),this.gl.vertexAttribPointer(this.aColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aColorLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.uvVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.uvs,this.gl.DYNAMIC_DRAW),this.gl.vertexAttribPointer(this.aUvLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aUvLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.borderPositionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.borderPositions,this.gl.DYNAMIC_DRAW),this.gl.vertexAttribPointer(this.aBorderPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aBorderPosLoc);var e=0!==t.wrap?this.gl.REPEAT:this.gl.CLAMP_TO_EDGE;this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_S,e),this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_T,e)}},{key:\"drawCustomShapeInstance\",value:function(t,e,i,s,r,a){this.gl.useProgram(this.shaderProgram);var h=new Float32Array(this.positions.buffer,0,3*(e+2));this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,h),this.gl.vertexAttribPointer(this.aPosLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLocation);var o=new Float32Array(this.colors.buffer,0,4*(e+2));if(this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.colorVertexBuf),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,o),this.gl.vertexAttribPointer(this.aColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aColorLocation),i){var n=new Float32Array(this.uvs.buffer,0,2*(e+2));this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.uvVertexBuf),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,n),this.gl.vertexAttribPointer(this.aUvLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aUvLocation)}if(this.gl.uniform1f(this.texturedLoc,i?1:0),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.bindSampler(0,this.mainSampler),this.gl.uniform1i(this.textureLoc,0),a?this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE):this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_FAN,0,e+2),s){this.gl.useProgram(this.borderShaderProgram);var l=new Float32Array(this.borderPositions.buffer,0,3*(e+1));this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.borderPositionVertexBuf),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,l),this.gl.vertexAttribPointer(this.aBorderPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aBorderPosLoc),this.gl.uniform4fv(this.uBorderColorLoc,this.borderColor);for(var m=r?4:1,u=0;u<m;u++){0===u?this.gl.uniform2fv(this.thickOffsetLoc,[0,0]):1===u?this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,0]):2===u?this.gl.uniform2fv(this.thickOffsetLoc,[0,2/this.texsizeY]):3===u&&this.gl.uniform2fv(this.thickOffsetLoc,[2/this.texsizeX,2/this.texsizeY]),this.gl.drawArrays(this.gl.LINE_STRIP,0,e+1)}}}}])&&L(e.prototype,i),s&&L(e,s),t}();function y(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var w=function(){function t(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.positions=new Float32Array(72),this.aspectx=i.aspectx,this.aspecty=i.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader(),this.vertexBuf=this.gl.createBuffer()}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aPos;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      out vec4 fragColor;\\n                                      uniform vec4 u_color;\\n                                      void main(void) {\\n                                        fragColor = u_color;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLoc=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.colorLoc=this.gl.getUniformLocation(this.shaderProgram,\"u_color\")}},{key:\"addTriangle\",value:function(t,e,i,s){this.positions[t+0]=e[0],this.positions[t+1]=e[1],this.positions[t+2]=e[2],this.positions[t+3]=i[0],this.positions[t+4]=i[1],this.positions[t+5]=i[2],this.positions[t+6]=s[0],this.positions[t+7]=s[1],this.positions[t+8]=s[2]}},{key:\"generateBorder\",value:function(t,e,i){if(e>0&&t[3]>0){var s=i/2,r=e/2+s,a=2*s,h=2*s,o=2*r,n=2*r,l=[-1+a,-1+n,0],m=[-1+a,1-n,0],u=[-1+o,1-n,0],g=[-1+o,-1+n,0];return this.addTriangle(0,g,m,l),this.addTriangle(9,g,u,m),l=[1-a,-1+n,0],m=[1-a,1-n,0],u=[1-o,1-n,0],g=[1-o,-1+n,0],this.addTriangle(18,l,m,g),this.addTriangle(27,m,u,g),l=[-1+a,-1+h,0],m=[-1+a,n-1,0],u=[1-a,n-1,0],g=[1-a,-1+h,0],this.addTriangle(36,g,m,l),this.addTriangle(45,g,u,m),l=[-1+a,1-h,0],m=[-1+a,1-n,0],u=[1-a,1-n,0],g=[1-a,1-h,0],this.addTriangle(54,l,m,g),this.addTriangle(63,m,u,g),!0}return!1}},{key:\"drawBorder\",value:function(t,e,i){this.generateBorder(t,e,i)&&(this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLoc),this.gl.uniform4fv(this.colorLoc,t),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLES,0,this.positions.length/3))}}])&&y(e.prototype,i),s&&y(e,s),t}();function U(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var M=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.aspectx=i.aspectx,this.aspecty=i.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.generatePositions(),this.colors=new Float32Array([0,0,0,3/32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),this.positionVertexBuf=this.gl.createBuffer(),this.colorVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.generatePositions()}},{key:\"generatePositions\",value:function(){this.positions=new Float32Array([0,0,0,-.05*this.aspecty,0,0,0,-.05,0,.05*this.aspecty,0,0,0,.05,0,-.05*this.aspecty,0,0])}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aPos;\\n                                      in vec4 aColor;\\n                                      out vec4 vColor;\\n                                      void main(void) {\\n                                        vColor = aColor;\\n                                        gl_Position = vec4(aPos, 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      in vec4 vColor;\\n                                      out vec4 fragColor;\\n                                      void main(void) {\\n                                        fragColor = vColor;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.aColorLocation=this.gl.getAttribLocation(this.shaderProgram,\"aColor\")}},{key:\"drawDarkenCenter\",value:function(t){0!==t.darken_center&&(this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.colorVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.colors,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aColorLocation),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_FAN,0,this.positions.length/3))}}])&&U(e.prototype,i),s&&U(e,s),t}();function F(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var q=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.maxX=64,this.maxY=48,this.positions=new Float32Array(this.maxX*this.maxY*2*3),this.texsizeX=i.texsizeX,this.texsizeY=i.texsizeY,this.mesh_width=i.mesh_width,this.mesh_height=i.mesh_height,this.positionVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      in vec3 aPos;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 1.0);\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      out vec4 fragColor;\\n                                      uniform vec4 u_color;\\n                                      void main(void) {\\n                                        fragColor = u_color;\\n                                      }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.aPosLoc=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.colorLoc=this.gl.getUniformLocation(this.shaderProgram,\"u_color\")}},{key:\"getMotionDir\",value:function(t,e,i){var s,r,a=Math.floor(i*this.mesh_height),h=i*this.mesh_height-a,o=Math.floor(e*this.mesh_width),n=e*this.mesh_width-o,l=o+1,m=a+1,u=this.mesh_width+1;return s=t[2*(a*u+o)+0]*(1-n)*(1-h),r=t[2*(a*u+o)+1]*(1-n)*(1-h),s+=t[2*(a*u+l)+0]*n*(1-h),r+=t[2*(a*u+l)+1]*n*(1-h),s+=t[2*(m*u+o)+0]*(1-n)*h,r+=t[2*(m*u+o)+1]*(1-n)*h,[s+=t[2*(m*u+l)+0]*n*h,1-(r+=t[2*(m*u+l)+1]*n*h)]}},{key:\"generateMotionVectors\",value:function(t,e){var i=t.mv_a,s=Math.floor(t.mv_x),r=Math.floor(t.mv_y);if(i>.001&&s>0&&r>0){var a=t.mv_x-s,h=t.mv_y-r;s>this.maxX&&(s=this.maxX,a=0),r>this.maxY&&(r=this.maxY,h=0);var o=t.mv_dx,n=t.mv_dy,l=t.mv_l,m=1/this.texsizeX;this.numVecVerts=0;for(var u=0;u<r;u++){var g=(u+.25)/(r+h+.25-1);if((g-=n)>1e-4&&g<.9999)for(var c=0;c<s;c++){var A=(c+.25)/(s+a+.25-1);if((A+=o)>1e-4&&A<.9999){var f=this.getMotionDir(e,A,g),d=f[0],v=f[1],p=d-A,_=v-g;p*=l,_*=l;var x=Math.sqrt(p*p+_*_);x<m&&x>1e-8?(p*=x=m/x,_*=x):(p=m,p=m);var b=2*A-1,T=2*g-1,E=2*(d=A+p)-1,P=2*(v=g+_)-1;this.positions[3*this.numVecVerts+0]=b,this.positions[3*this.numVecVerts+1]=T,this.positions[3*this.numVecVerts+2]=0,this.positions[3*(this.numVecVerts+1)+0]=E,this.positions[3*(this.numVecVerts+1)+1]=P,this.positions[3*(this.numVecVerts+1)+2]=0,this.numVecVerts+=2}}}if(this.numVecVerts>0)return this.color=[t.mv_r,t.mv_g,t.mv_b,i],!0}return!1}},{key:\"drawMotionVectors\",value:function(t,e){this.generateMotionVectors(t,e)&&(this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.aPosLoc,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.aPosLoc),this.gl.uniform4fv(this.colorLoc,this.color),this.gl.lineWidth(1),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.LINES,0,this.numVecVerts))}}])&&F(e.prototype,i),s&&F(e,s),t}();function z(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var B=function(){function t(e,i,s){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.noise=i,this.image=s,this.texsizeX=r.texsizeX,this.texsizeY=r.texsizeY,this.mesh_width=r.mesh_width,this.mesh_height=r.mesh_height,this.aspectx=r.aspectx,this.aspecty=r.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.buildPositions(),this.indexBuf=e.createBuffer(),this.positionVertexBuf=this.gl.createBuffer(),this.warpUvVertexBuf=this.gl.createBuffer(),this.warpColorVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader(),this.mainSampler=this.gl.createSampler(),this.mainSamplerFW=this.gl.createSampler(),this.mainSamplerFC=this.gl.createSampler(),this.mainSamplerPW=this.gl.createSampler(),this.mainSamplerPC=this.gl.createSampler(),e.samplerParameteri(this.mainSampler,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSampler,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSampler,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSampler,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_MIN_FILTER,e.NEAREST_MIPMAP_NEAREST),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_MAG_FILTER,e.NEAREST),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_MIN_FILTER,e.NEAREST_MIPMAP_NEAREST),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_MAG_FILTER,e.NEAREST),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE)}var e,i,s;return e=t,(i=[{key:\"buildPositions\",value:function(){for(var t=this.mesh_width,e=this.mesh_height,i=t+1,s=e+1,r=2/t,a=2/e,h=[],o=0;o<s;o++)for(var n=o*a-1,l=0;l<i;l++){var m=l*r-1;h.push(m,-n,0)}for(var u=[],g=0;g<e;g++)for(var c=0;c<t;c++){var A=c+i*g,f=c+i*(g+1),d=c+1+i*(g+1),v=c+1+i*g;u.push(A,f,v),u.push(f,d,v)}this.vertices=new Float32Array(h),this.indices=new Uint16Array(u)}},{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.buildPositions()}},{key:\"createShader\",value:function(){var t,e,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\";if(0===i.length)t=\"ret = texture(sampler_main, uv).rgb * decay;\",e=\"\";else{var s=_.getShaderParts(i);e=s[0],t=s[1]}t=(t=t.replace(/texture2D/g,\"texture\")).replace(/texture3D/g,\"texture\"),this.userTextures=_.getUserSamplers(e),this.shaderProgram=this.gl.createProgram();var r=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(r,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      const vec2 halfmad = vec2(0.5);\\n                                      in vec2 aPos;\\n                                      in vec2 aWarpUv;\\n                                      in vec4 aWarpColor;\\n                                      out vec2 uv;\\n                                      out vec2 uv_orig;\\n                                      out vec4 vColor;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 0.0, 1.0);\\n                                        uv_orig = aPos * halfmad + halfmad;\\n                                        uv = aWarpUv;\\n                                        vColor = aWarpColor;\\n                                      }\")),this.gl.compileShader(r);var a=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(a,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      precision mediump sampler3D;\\n\\n                                      in vec2 uv;\\n                                      in vec2 uv_orig;\\n                                      in vec4 vColor;\\n                                      out vec4 fragColor;\\n                                      uniform sampler2D sampler_main;\\n                                      uniform sampler2D sampler_fw_main;\\n                                      uniform sampler2D sampler_fc_main;\\n                                      uniform sampler2D sampler_pw_main;\\n                                      uniform sampler2D sampler_pc_main;\\n                                      uniform sampler2D sampler_blur1;\\n                                      uniform sampler2D sampler_blur2;\\n                                      uniform sampler2D sampler_blur3;\\n                                      uniform sampler2D sampler_noise_lq;\\n                                      uniform sampler2D sampler_noise_lq_lite;\\n                                      uniform sampler2D sampler_noise_mq;\\n                                      uniform sampler2D sampler_noise_hq;\\n                                      uniform sampler2D sampler_pw_noise_lq;\\n                                      uniform sampler3D sampler_noisevol_lq;\\n                                      uniform sampler3D sampler_noisevol_hq;\\n                                      uniform float time;\\n                                      uniform float decay;\\n                                      uniform vec2 resolution;\\n                                      uniform vec4 aspect;\\n                                      uniform vec4 texsize;\\n                                      uniform vec4 texsize_noise_lq;\\n                                      uniform vec4 texsize_noise_mq;\\n                                      uniform vec4 texsize_noise_hq;\\n                                      uniform vec4 texsize_noise_lq_lite;\\n                                      uniform vec4 texsize_noisevol_lq;\\n                                      uniform vec4 texsize_noisevol_hq;\\n\\n                                      uniform float bass;\\n                                      uniform float mid;\\n                                      uniform float treb;\\n                                      uniform float vol;\\n                                      uniform float bass_att;\\n                                      uniform float mid_att;\\n                                      uniform float treb_att;\\n                                      uniform float vol_att;\\n\\n                                      uniform float frame;\\n                                      uniform float fps;\\n\\n                                      uniform vec4 _qa;\\n                                      uniform vec4 _qb;\\n                                      uniform vec4 _qc;\\n                                      uniform vec4 _qd;\\n                                      uniform vec4 _qe;\\n                                      uniform vec4 _qf;\\n                                      uniform vec4 _qg;\\n                                      uniform vec4 _qh;\\n\\n                                      #define q1 _qa.x\\n                                      #define q2 _qa.y\\n                                      #define q3 _qa.z\\n                                      #define q4 _qa.w\\n                                      #define q5 _qb.x\\n                                      #define q6 _qb.y\\n                                      #define q7 _qb.z\\n                                      #define q8 _qb.w\\n                                      #define q9 _qc.x\\n                                      #define q10 _qc.y\\n                                      #define q11 _qc.z\\n                                      #define q12 _qc.w\\n                                      #define q13 _qd.x\\n                                      #define q14 _qd.y\\n                                      #define q15 _qd.z\\n                                      #define q16 _qd.w\\n                                      #define q17 _qe.x\\n                                      #define q18 _qe.y\\n                                      #define q19 _qe.z\\n                                      #define q20 _qe.w\\n                                      #define q21 _qf.x\\n                                      #define q22 _qf.y\\n                                      #define q23 _qf.z\\n                                      #define q24 _qf.w\\n                                      #define q25 _qg.x\\n                                      #define q26 _qg.y\\n                                      #define q27 _qg.z\\n                                      #define q28 _qg.w\\n                                      #define q29 _qh.x\\n                                      #define q30 _qh.y\\n                                      #define q31 _qh.z\\n                                      #define q32 _qh.w\\n\\n                                      uniform vec4 slow_roam_cos;\\n                                      uniform vec4 roam_cos;\\n                                      uniform vec4 slow_roam_sin;\\n                                      uniform vec4 roam_sin;\\n\\n                                      uniform float blur1_min;\\n                                      uniform float blur1_max;\\n                                      uniform float blur2_min;\\n                                      uniform float blur2_max;\\n                                      uniform float blur3_min;\\n                                      uniform float blur3_max;\\n\\n                                      uniform float scale1;\\n                                      uniform float scale2;\\n                                      uniform float scale3;\\n                                      uniform float bias1;\\n                                      uniform float bias2;\\n                                      uniform float bias3;\\n\\n                                      uniform vec4 rand_frame;\\n                                      uniform vec4 rand_preset;\\n\\n                                      float PI = \").concat(Math.PI,\";\\n\\n                                      \").concat(e,\"\\n\\n                                      void main(void) {\\n                                        vec3 ret;\\n                                        float rad = length(uv_orig - 0.5);\\n                                        float ang = atan(uv_orig.x - 0.5, uv_orig.y - 0.5);\\n\\n                                        \").concat(t,\"\\n\\n                                        fragColor = vec4(ret, 1.0) * vColor;\\n                                      }\")),this.gl.compileShader(a),this.gl.attachShader(this.shaderProgram,r),this.gl.attachShader(this.shaderProgram,a),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.warpUvLocation=this.gl.getAttribLocation(this.shaderProgram,\"aWarpUv\"),this.warpColorLocation=this.gl.getAttribLocation(this.shaderProgram,\"aWarpColor\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_main\"),this.textureFWLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_fw_main\"),this.textureFCLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_fc_main\"),this.texturePWLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pw_main\"),this.texturePCLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pc_main\"),this.blurTexture1Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur1\"),this.blurTexture2Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur2\"),this.blurTexture3Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur3\"),this.noiseLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_lq\"),this.noiseMQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_mq\"),this.noiseHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_hq\"),this.noiseLQLiteLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_lq_lite\"),this.noisePointLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pw_noise_lq\"),this.noiseVolLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noisevol_lq\"),this.noiseVolHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noisevol_hq\"),this.decayLoc=this.gl.getUniformLocation(this.shaderProgram,\"decay\"),this.texsizeLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize\"),this.texsizeNoiseLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_lq\"),this.texsizeNoiseMQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_mq\"),this.texsizeNoiseHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_hq\"),this.texsizeNoiseLQLiteLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_lq_lite\"),this.texsizeNoiseVolLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noisevol_lq\"),this.texsizeNoiseVolHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noisevol_hq\"),this.resolutionLoc=this.gl.getUniformLocation(this.shaderProgram,\"resolution\"),this.aspectLoc=this.gl.getUniformLocation(this.shaderProgram,\"aspect\"),this.bassLoc=this.gl.getUniformLocation(this.shaderProgram,\"bass\"),this.midLoc=this.gl.getUniformLocation(this.shaderProgram,\"mid\"),this.trebLoc=this.gl.getUniformLocation(this.shaderProgram,\"treb\"),this.volLoc=this.gl.getUniformLocation(this.shaderProgram,\"vol\"),this.bassAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"bass_att\"),this.midAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"mid_att\"),this.trebAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"treb_att\"),this.volAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"vol_att\"),this.timeLoc=this.gl.getUniformLocation(this.shaderProgram,\"time\"),this.frameLoc=this.gl.getUniformLocation(this.shaderProgram,\"frame\"),this.fpsLoc=this.gl.getUniformLocation(this.shaderProgram,\"fps\"),this.blur1MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur1_min\"),this.blur1MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur1_max\"),this.blur2MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur2_min\"),this.blur2MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur2_max\"),this.blur3MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur3_min\"),this.blur3MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur3_max\"),this.scale1Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale1\"),this.scale2Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale2\"),this.scale3Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale3\"),this.bias1Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias1\"),this.bias2Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias2\"),this.bias3Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias3\"),this.randPresetLoc=this.gl.getUniformLocation(this.shaderProgram,\"rand_preset\"),this.randFrameLoc=this.gl.getUniformLocation(this.shaderProgram,\"rand_frame\"),this.qaLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qa\"),this.qbLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qb\"),this.qcLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qc\"),this.qdLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qd\"),this.qeLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qe\"),this.qfLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qf\"),this.qgLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qg\"),this.qhLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qh\"),this.slowRoamCosLoc=this.gl.getUniformLocation(this.shaderProgram,\"slow_roam_cos\"),this.roamCosLoc=this.gl.getUniformLocation(this.shaderProgram,\"roam_cos\"),this.slowRoamSinLoc=this.gl.getUniformLocation(this.shaderProgram,\"slow_roam_sin\"),this.roamSinLoc=this.gl.getUniformLocation(this.shaderProgram,\"roam_sin\");for(var h=0;h<this.userTextures.length;h++){var o=this.userTextures[h];o.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_\".concat(o.sampler))}}},{key:\"updateShader\",value:function(t){this.createShader(t)}},{key:\"bindBlurVals\",value:function(t,e){var i=t[0],s=t[1],r=t[2],a=e[0],h=e[1],o=e[2],n=a-i,l=i,m=h-s,u=s,g=o-r,c=r;this.gl.uniform1f(this.blur1MinLoc,i),this.gl.uniform1f(this.blur1MaxLoc,a),this.gl.uniform1f(this.blur2MinLoc,s),this.gl.uniform1f(this.blur2MaxLoc,h),this.gl.uniform1f(this.blur3MinLoc,r),this.gl.uniform1f(this.blur3MaxLoc,o),this.gl.uniform1f(this.scale1Loc,n),this.gl.uniform1f(this.scale2Loc,m),this.gl.uniform1f(this.scale3Loc,g),this.gl.uniform1f(this.bias1Loc,l),this.gl.uniform1f(this.bias2Loc,u),this.gl.uniform1f(this.bias3Loc,c)}},{key:\"renderQuadTexture\",value:function(t,e,i,s,r,a,h,o,n,l){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.indexBuf),this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,this.indices,this.gl.STATIC_DRAW),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.vertices,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.warpUvVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,n,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.warpUvLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.warpUvLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.warpColorVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,l,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.warpColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.warpColorLocation);var m=0!==o.wrap?this.gl.REPEAT:this.gl.CLAMP_TO_EDGE;this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_S,m),this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_T,m),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(0,this.mainSampler),this.gl.uniform1i(this.textureLoc,0),this.gl.activeTexture(this.gl.TEXTURE1),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(1,this.mainSamplerFW),this.gl.uniform1i(this.textureFWLoc,1),this.gl.activeTexture(this.gl.TEXTURE2),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(2,this.mainSamplerFC),this.gl.uniform1i(this.textureFCLoc,2),this.gl.activeTexture(this.gl.TEXTURE3),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(3,this.mainSamplerPW),this.gl.uniform1i(this.texturePWLoc,3),this.gl.activeTexture(this.gl.TEXTURE4),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(4,this.mainSamplerPC),this.gl.uniform1i(this.texturePCLoc,4),this.gl.activeTexture(this.gl.TEXTURE5),this.gl.bindTexture(this.gl.TEXTURE_2D,i),this.gl.uniform1i(this.blurTexture1Loc,5),this.gl.activeTexture(this.gl.TEXTURE6),this.gl.bindTexture(this.gl.TEXTURE_2D,s),this.gl.uniform1i(this.blurTexture2Loc,6),this.gl.activeTexture(this.gl.TEXTURE7),this.gl.bindTexture(this.gl.TEXTURE_2D,r),this.gl.uniform1i(this.blurTexture3Loc,7),this.gl.activeTexture(this.gl.TEXTURE8),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQ),this.gl.uniform1i(this.noiseLQLoc,8),this.gl.activeTexture(this.gl.TEXTURE9),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexMQ),this.gl.uniform1i(this.noiseMQLoc,9),this.gl.activeTexture(this.gl.TEXTURE10),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexHQ),this.gl.uniform1i(this.noiseHQLoc,10),this.gl.activeTexture(this.gl.TEXTURE11),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQLite),this.gl.uniform1i(this.noiseLQLiteLoc,11),this.gl.activeTexture(this.gl.TEXTURE12),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQ),this.gl.bindSampler(12,this.noise.noiseTexPointLQ),this.gl.uniform1i(this.noisePointLQLoc,12),this.gl.activeTexture(this.gl.TEXTURE13),this.gl.bindTexture(this.gl.TEXTURE_3D,this.noise.noiseTexVolLQ),this.gl.uniform1i(this.noiseVolLQLoc,13),this.gl.activeTexture(this.gl.TEXTURE14),this.gl.bindTexture(this.gl.TEXTURE_3D,this.noise.noiseTexVolHQ),this.gl.uniform1i(this.noiseVolHQLoc,14);for(var u=0;u<this.userTextures.length;u++){var g=this.userTextures[u];this.gl.activeTexture(this.gl.TEXTURE15+u),this.gl.bindTexture(this.gl.TEXTURE_2D,this.image.getTexture(g.sampler)),this.gl.uniform1i(g.textureLoc,15+u)}this.gl.uniform1f(this.decayLoc,o.decay),this.gl.uniform2fv(this.resolutionLoc,[this.texsizeX,this.texsizeY]),this.gl.uniform4fv(this.aspectLoc,[this.aspectx,this.aspecty,this.invAspectx,this.invAspecty]),this.gl.uniform4fv(this.texsizeLoc,[this.texsizeX,this.texsizeY,1/this.texsizeX,1/this.texsizeY]),this.gl.uniform4fv(this.texsizeNoiseLQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseMQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseHQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseLQLiteLoc,[32,32,1/32,1/32]),this.gl.uniform4fv(this.texsizeNoiseVolLQLoc,[32,32,1/32,1/32]),this.gl.uniform4fv(this.texsizeNoiseVolHQLoc,[32,32,1/32,1/32]),this.gl.uniform1f(this.bassLoc,o.bass),this.gl.uniform1f(this.midLoc,o.mid),this.gl.uniform1f(this.trebLoc,o.treb),this.gl.uniform1f(this.volLoc,(o.bass+o.mid+o.treb)/3),this.gl.uniform1f(this.bassAttLoc,o.bass_att),this.gl.uniform1f(this.midAttLoc,o.mid_att),this.gl.uniform1f(this.trebAttLoc,o.treb_att),this.gl.uniform1f(this.volAttLoc,(o.bass_att+o.mid_att+o.treb_att)/3),this.gl.uniform1f(this.timeLoc,o.time),this.gl.uniform1f(this.frameLoc,o.frame),this.gl.uniform1f(this.fpsLoc,o.fps),this.gl.uniform4fv(this.randPresetLoc,o.rand_preset),this.gl.uniform4fv(this.randFrameLoc,new Float32Array([Math.random(),Math.random(),Math.random(),Math.random()])),this.gl.uniform4fv(this.qaLoc,new Float32Array([o.q1||0,o.q2||0,o.q3||0,o.q4||0])),this.gl.uniform4fv(this.qbLoc,new Float32Array([o.q5||0,o.q6||0,o.q7||0,o.q8||0])),this.gl.uniform4fv(this.qcLoc,new Float32Array([o.q9||0,o.q10||0,o.q11||0,o.q12||0])),this.gl.uniform4fv(this.qdLoc,new Float32Array([o.q13||0,o.q14||0,o.q15||0,o.q16||0])),this.gl.uniform4fv(this.qeLoc,new Float32Array([o.q17||0,o.q18||0,o.q19||0,o.q20||0])),this.gl.uniform4fv(this.qfLoc,new Float32Array([o.q21||0,o.q22||0,o.q23||0,o.q24||0])),this.gl.uniform4fv(this.qgLoc,new Float32Array([o.q25||0,o.q26||0,o.q27||0,o.q28||0])),this.gl.uniform4fv(this.qhLoc,new Float32Array([o.q29||0,o.q30||0,o.q31||0,o.q32||0])),this.gl.uniform4fv(this.slowRoamCosLoc,[.5+.5*Math.cos(.005*o.time),.5+.5*Math.cos(.008*o.time),.5+.5*Math.cos(.013*o.time),.5+.5*Math.cos(.022*o.time)]),this.gl.uniform4fv(this.roamCosLoc,[.5+.5*Math.cos(.3*o.time),.5+.5*Math.cos(1.3*o.time),.5+.5*Math.cos(5*o.time),.5+.5*Math.cos(20*o.time)]),this.gl.uniform4fv(this.slowRoamSinLoc,[.5+.5*Math.sin(.005*o.time),.5+.5*Math.sin(.008*o.time),.5+.5*Math.sin(.013*o.time),.5+.5*Math.sin(.022*o.time)]),this.gl.uniform4fv(this.roamSinLoc,[.5+.5*Math.sin(.3*o.time),.5+.5*Math.sin(1.3*o.time),.5+.5*Math.sin(5*o.time),.5+.5*Math.sin(20*o.time)]),this.bindBlurVals(a,h),t?this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA):this.gl.disable(this.gl.BLEND),this.gl.drawElements(this.gl.TRIANGLES,this.indices.length,this.gl.UNSIGNED_SHORT,0),t||this.gl.enable(this.gl.BLEND)}}])&&z(e.prototype,i),s&&z(e,s),t}();function C(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var D=function(){function t(e,i,s){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.noise=i,this.image=s,this.mesh_width=r.mesh_width,this.mesh_height=r.mesh_height,this.texsizeX=r.texsizeX,this.texsizeY=r.texsizeY,this.aspectx=r.aspectx,this.aspecty=r.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.compWidth=32,this.compHeight=24,this.buildPositions(),this.indexBuf=e.createBuffer(),this.positionVertexBuf=this.gl.createBuffer(),this.compColorVertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader(),this.mainSampler=this.gl.createSampler(),this.mainSamplerFW=this.gl.createSampler(),this.mainSamplerFC=this.gl.createSampler(),this.mainSamplerPW=this.gl.createSampler(),this.mainSamplerPC=this.gl.createSampler(),e.samplerParameteri(this.mainSampler,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSampler,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSampler,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSampler,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSamplerFW,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_MAG_FILTER,e.LINEAR),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerFC,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_MIN_FILTER,e.NEAREST_MIPMAP_NEAREST),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_MAG_FILTER,e.NEAREST),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.mainSamplerPW,e.TEXTURE_WRAP_T,e.REPEAT),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_MIN_FILTER,e.NEAREST_MIPMAP_NEAREST),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_MAG_FILTER,e.NEAREST),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.samplerParameteri(this.mainSamplerPC,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE)}var e,i,s;return e=t,s=[{key:\"generateHueBase\",value:function(t){for(var e=new Float32Array([1,1,1,1,1,1,1,1,1,1,1,1]),i=0;i<4;i++){e[3*i+0]=.6+.3*Math.sin(30*t.time*.0143+3+21*i+t.rand_start[3]),e[3*i+1]=.6+.3*Math.sin(30*t.time*.0107+1+13*i+t.rand_start[1]),e[3*i+2]=.6+.3*Math.sin(30*t.time*.0129+6+9*i+t.rand_start[2]);for(var s=Math.max(e[3*i],e[3*i+1],e[3*i+2]),r=0;r<3;r++)e[3*i+r]=e[3*i+r]/s,e[3*i+r]=.5+.5*e[3*i+r]}return e}}],(i=[{key:\"buildPositions\",value:function(){for(var t=this.compWidth,e=this.compHeight,i=t+1,s=e+1,r=2/t,a=2/e,h=[],o=0;o<s;o++)for(var n=o*a-1,l=0;l<i;l++){var m=l*r-1;h.push(m,-n,0)}for(var u=[],g=0;g<e;g++)for(var c=0;c<t;c++){var A=c+i*g,f=c+i*(g+1),d=c+1+i*(g+1),v=c+1+i*g;u.push(A,f,v),u.push(f,d,v)}this.vertices=new Float32Array(h),this.indices=new Uint16Array(u)}},{key:\"updateGlobals\",value:function(t){this.mesh_width=t.mesh_width,this.mesh_height=t.mesh_height,this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.buildPositions()}},{key:\"createShader\",value:function(){var t,e,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\";if(0===i.length)t=\"float orient_horiz = mod(echo_orientation, 2.0);\\n                        float orient_x = (orient_horiz != 0.0) ? -1.0 : 1.0;\\n                        float orient_y = (echo_orientation >= 2.0) ? -1.0 : 1.0;\\n                        vec2 uv_echo = ((uv - 0.5) *\\n                                        (1.0 / echo_zoom) *\\n                                        vec2(orient_x, orient_y)) + 0.5;\\n\\n                        ret = mix(texture(sampler_main, uv).rgb,\\n                                  texture(sampler_main, uv_echo).rgb,\\n                                  echo_alpha);\\n\\n                        ret *= gammaAdj;\\n\\n                        if(fShader >= 1.0) {\\n                          ret *= hue_shader;\\n                        } else if(fShader > 0.001) {\\n                          ret *= (1.0 - fShader) + (fShader * hue_shader);\\n                        }\\n\\n                        if(brighten != 0) ret = sqrt(ret);\\n                        if(darken != 0) ret = ret*ret;\\n                        if(solarize != 0) ret = ret * (1.0 - ret) * 4.0;\\n                        if(invert != 0) ret = 1.0 - ret;\",e=\"\";else{var s=_.getShaderParts(i);e=s[0],t=s[1]}t=(t=t.replace(/texture2D/g,\"texture\")).replace(/texture3D/g,\"texture\"),this.userTextures=_.getUserSamplers(e),this.shaderProgram=this.gl.createProgram();var r=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(r,\"#version 300 es\\n                                      const vec2 halfmad = vec2(0.5);\\n                                      in vec2 aPos;\\n                                      in vec4 aCompColor;\\n                                      out vec2 vUv;\\n                                      out vec4 vColor;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 0.0, 1.0);\\n                                        vUv = aPos * halfmad + halfmad;\\n                                        vColor = aCompColor;\\n                                      }\"),this.gl.compileShader(r);var a=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(a,\"#version 300 es\\n                                      precision \".concat(this.floatPrecision,\" float;\\n                                      precision highp int;\\n                                      precision mediump sampler2D;\\n                                      precision mediump sampler3D;\\n\\n                                      vec3 lum(vec3 v){\\n                                          return vec3(dot(v, vec3(0.32,0.49,0.29)));\\n                                      }\\n\\n                                      in vec2 vUv;\\n                                      in vec4 vColor;\\n                                      out vec4 fragColor;\\n                                      uniform sampler2D sampler_main;\\n                                      uniform sampler2D sampler_fw_main;\\n                                      uniform sampler2D sampler_fc_main;\\n                                      uniform sampler2D sampler_pw_main;\\n                                      uniform sampler2D sampler_pc_main;\\n                                      uniform sampler2D sampler_blur1;\\n                                      uniform sampler2D sampler_blur2;\\n                                      uniform sampler2D sampler_blur3;\\n                                      uniform sampler2D sampler_noise_lq;\\n                                      uniform sampler2D sampler_noise_lq_lite;\\n                                      uniform sampler2D sampler_noise_mq;\\n                                      uniform sampler2D sampler_noise_hq;\\n                                      uniform sampler2D sampler_pw_noise_lq;\\n                                      uniform sampler3D sampler_noisevol_lq;\\n                                      uniform sampler3D sampler_noisevol_hq;\\n\\n                                      uniform float time;\\n                                      uniform float gammaAdj;\\n                                      uniform float echo_zoom;\\n                                      uniform float echo_alpha;\\n                                      uniform float echo_orientation;\\n                                      uniform int invert;\\n                                      uniform int brighten;\\n                                      uniform int darken;\\n                                      uniform int solarize;\\n                                      uniform vec2 resolution;\\n                                      uniform vec4 aspect;\\n                                      uniform vec4 texsize;\\n                                      uniform vec4 texsize_noise_lq;\\n                                      uniform vec4 texsize_noise_mq;\\n                                      uniform vec4 texsize_noise_hq;\\n                                      uniform vec4 texsize_noise_lq_lite;\\n                                      uniform vec4 texsize_noisevol_lq;\\n                                      uniform vec4 texsize_noisevol_hq;\\n\\n                                      uniform float bass;\\n                                      uniform float mid;\\n                                      uniform float treb;\\n                                      uniform float vol;\\n                                      uniform float bass_att;\\n                                      uniform float mid_att;\\n                                      uniform float treb_att;\\n                                      uniform float vol_att;\\n\\n                                      uniform float frame;\\n                                      uniform float fps;\\n\\n                                      uniform vec4 _qa;\\n                                      uniform vec4 _qb;\\n                                      uniform vec4 _qc;\\n                                      uniform vec4 _qd;\\n                                      uniform vec4 _qe;\\n                                      uniform vec4 _qf;\\n                                      uniform vec4 _qg;\\n                                      uniform vec4 _qh;\\n\\n                                      #define q1 _qa.x\\n                                      #define q2 _qa.y\\n                                      #define q3 _qa.z\\n                                      #define q4 _qa.w\\n                                      #define q5 _qb.x\\n                                      #define q6 _qb.y\\n                                      #define q7 _qb.z\\n                                      #define q8 _qb.w\\n                                      #define q9 _qc.x\\n                                      #define q10 _qc.y\\n                                      #define q11 _qc.z\\n                                      #define q12 _qc.w\\n                                      #define q13 _qd.x\\n                                      #define q14 _qd.y\\n                                      #define q15 _qd.z\\n                                      #define q16 _qd.w\\n                                      #define q17 _qe.x\\n                                      #define q18 _qe.y\\n                                      #define q19 _qe.z\\n                                      #define q20 _qe.w\\n                                      #define q21 _qf.x\\n                                      #define q22 _qf.y\\n                                      #define q23 _qf.z\\n                                      #define q24 _qf.w\\n                                      #define q25 _qg.x\\n                                      #define q26 _qg.y\\n                                      #define q27 _qg.z\\n                                      #define q28 _qg.w\\n                                      #define q29 _qh.x\\n                                      #define q30 _qh.y\\n                                      #define q31 _qh.z\\n                                      #define q32 _qh.w\\n\\n                                      uniform vec4 slow_roam_cos;\\n                                      uniform vec4 roam_cos;\\n                                      uniform vec4 slow_roam_sin;\\n                                      uniform vec4 roam_sin;\\n\\n                                      uniform float blur1_min;\\n                                      uniform float blur1_max;\\n                                      uniform float blur2_min;\\n                                      uniform float blur2_max;\\n                                      uniform float blur3_min;\\n                                      uniform float blur3_max;\\n\\n                                      uniform float scale1;\\n                                      uniform float scale2;\\n                                      uniform float scale3;\\n                                      uniform float bias1;\\n                                      uniform float bias2;\\n                                      uniform float bias3;\\n\\n                                      uniform vec4 rand_frame;\\n                                      uniform vec4 rand_preset;\\n\\n                                      uniform float fShader;\\n\\n                                      float PI = \").concat(Math.PI,\";\\n\\n                                      \").concat(e,\"\\n\\n                                      void main(void) {\\n                                        vec3 ret;\\n                                        vec2 uv = vUv;\\n                                        vec2 uv_orig = vUv;\\n                                        uv.y = 1.0 - uv.y;\\n                                        uv_orig.y = 1.0 - uv_orig.y;\\n                                        float rad = length(uv - 0.5);\\n                                        float ang = atan(uv.x - 0.5, uv.y - 0.5);\\n                                        vec3 hue_shader = vColor.rgb;\\n\\n                                        \").concat(t,\"\\n\\n                                        fragColor = vec4(ret, vColor.a);\\n                                      }\")),this.gl.compileShader(a),this.gl.attachShader(this.shaderProgram,r),this.gl.attachShader(this.shaderProgram,a),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.compColorLocation=this.gl.getAttribLocation(this.shaderProgram,\"aCompColor\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_main\"),this.textureFWLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_fw_main\"),this.textureFCLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_fc_main\"),this.texturePWLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pw_main\"),this.texturePCLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pc_main\"),this.blurTexture1Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur1\"),this.blurTexture2Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur2\"),this.blurTexture3Loc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_blur3\"),this.noiseLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_lq\"),this.noiseMQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_mq\"),this.noiseHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_hq\"),this.noiseLQLiteLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noise_lq_lite\"),this.noisePointLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_pw_noise_lq\"),this.noiseVolLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noisevol_lq\"),this.noiseVolHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_noisevol_hq\"),this.timeLoc=this.gl.getUniformLocation(this.shaderProgram,\"time\"),this.gammaAdjLoc=this.gl.getUniformLocation(this.shaderProgram,\"gammaAdj\"),this.echoZoomLoc=this.gl.getUniformLocation(this.shaderProgram,\"echo_zoom\"),this.echoAlphaLoc=this.gl.getUniformLocation(this.shaderProgram,\"echo_alpha\"),this.echoOrientationLoc=this.gl.getUniformLocation(this.shaderProgram,\"echo_orientation\"),this.invertLoc=this.gl.getUniformLocation(this.shaderProgram,\"invert\"),this.brightenLoc=this.gl.getUniformLocation(this.shaderProgram,\"brighten\"),this.darkenLoc=this.gl.getUniformLocation(this.shaderProgram,\"darken\"),this.solarizeLoc=this.gl.getUniformLocation(this.shaderProgram,\"solarize\"),this.texsizeLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize\"),this.texsizeNoiseLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_lq\"),this.texsizeNoiseMQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_mq\"),this.texsizeNoiseHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_hq\"),this.texsizeNoiseLQLiteLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noise_lq_lite\"),this.texsizeNoiseVolLQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noisevol_lq\"),this.texsizeNoiseVolHQLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize_noisevol_hq\"),this.resolutionLoc=this.gl.getUniformLocation(this.shaderProgram,\"resolution\"),this.aspectLoc=this.gl.getUniformLocation(this.shaderProgram,\"aspect\"),this.bassLoc=this.gl.getUniformLocation(this.shaderProgram,\"bass\"),this.midLoc=this.gl.getUniformLocation(this.shaderProgram,\"mid\"),this.trebLoc=this.gl.getUniformLocation(this.shaderProgram,\"treb\"),this.volLoc=this.gl.getUniformLocation(this.shaderProgram,\"vol\"),this.bassAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"bass_att\"),this.midAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"mid_att\"),this.trebAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"treb_att\"),this.volAttLoc=this.gl.getUniformLocation(this.shaderProgram,\"vol_att\"),this.frameLoc=this.gl.getUniformLocation(this.shaderProgram,\"frame\"),this.fpsLoc=this.gl.getUniformLocation(this.shaderProgram,\"fps\"),this.blur1MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur1_min\"),this.blur1MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur1_max\"),this.blur2MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur2_min\"),this.blur2MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur2_max\"),this.blur3MinLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur3_min\"),this.blur3MaxLoc=this.gl.getUniformLocation(this.shaderProgram,\"blur3_max\"),this.scale1Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale1\"),this.scale2Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale2\"),this.scale3Loc=this.gl.getUniformLocation(this.shaderProgram,\"scale3\"),this.bias1Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias1\"),this.bias2Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias2\"),this.bias3Loc=this.gl.getUniformLocation(this.shaderProgram,\"bias3\"),this.randPresetLoc=this.gl.getUniformLocation(this.shaderProgram,\"rand_preset\"),this.randFrameLoc=this.gl.getUniformLocation(this.shaderProgram,\"rand_frame\"),this.fShaderLoc=this.gl.getUniformLocation(this.shaderProgram,\"fShader\"),this.qaLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qa\"),this.qbLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qb\"),this.qcLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qc\"),this.qdLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qd\"),this.qeLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qe\"),this.qfLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qf\"),this.qgLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qg\"),this.qhLoc=this.gl.getUniformLocation(this.shaderProgram,\"_qh\"),this.slowRoamCosLoc=this.gl.getUniformLocation(this.shaderProgram,\"slow_roam_cos\"),this.roamCosLoc=this.gl.getUniformLocation(this.shaderProgram,\"roam_cos\"),this.slowRoamSinLoc=this.gl.getUniformLocation(this.shaderProgram,\"slow_roam_sin\"),this.roamSinLoc=this.gl.getUniformLocation(this.shaderProgram,\"roam_sin\");for(var h=0;h<this.userTextures.length;h++){var o=this.userTextures[h];o.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"sampler_\".concat(o.sampler))}}},{key:\"updateShader\",value:function(t){this.createShader(t)}},{key:\"bindBlurVals\",value:function(t,e){var i=t[0],s=t[1],r=t[2],a=e[0],h=e[1],o=e[2],n=a-i,l=i,m=h-s,u=s,g=o-r,c=r;this.gl.uniform1f(this.blur1MinLoc,i),this.gl.uniform1f(this.blur1MaxLoc,a),this.gl.uniform1f(this.blur2MinLoc,s),this.gl.uniform1f(this.blur2MaxLoc,h),this.gl.uniform1f(this.blur3MinLoc,r),this.gl.uniform1f(this.blur3MaxLoc,o),this.gl.uniform1f(this.scale1Loc,n),this.gl.uniform1f(this.scale2Loc,m),this.gl.uniform1f(this.scale3Loc,g),this.gl.uniform1f(this.bias1Loc,l),this.gl.uniform1f(this.bias2Loc,u),this.gl.uniform1f(this.bias3Loc,c)}},{key:\"generateCompColors\",value:function(e,i,s){for(var r=t.generateHueBase(i),a=this.compWidth+1,h=this.compHeight+1,o=new Float32Array(a*h*4),n=0,l=0;l<h;l++)for(var m=0;m<a;m++){for(var u=m/this.compWidth,g=l/this.compHeight,c=[1,1,1],A=0;A<3;A++)c[A]=r[0+A]*u*g+r[3+A]*(1-u)*g+r[6+A]*u*(1-g)+r[9+A]*(1-u)*(1-g);var f=1;if(e){u*=this.mesh_width+1,g*=this.mesh_height+1,u=Math.clamp(u,0,this.mesh_width-1),g=Math.clamp(g,0,this.mesh_height-1);var d=Math.floor(u),v=Math.floor(g),p=u-d,_=g-v;f=s[4*(v*(this.mesh_width+1)+d)+3]*(1-p)*(1-_)+s[4*(v*(this.mesh_width+1)+(d+1))+3]*p*(1-_)+s[4*((v+1)*(this.mesh_width+1)+d)+3]*(1-p)*_+s[4*((v+1)*(this.mesh_width+1)+(d+1))+3]*p*_}o[n+0]=c[0],o[n+1]=c[1],o[n+2]=c[2],o[n+3]=f,n+=4}return o}},{key:\"renderQuadTexture\",value:function(t,e,i,s,r,a,h,o,n){var l=this.generateCompColors(t,o,n);this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.indexBuf),this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,this.indices,this.gl.STATIC_DRAW),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.vertices,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.compColorVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,l,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.compColorLocation,4,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.compColorLocation);var m=0!==o.wrap?this.gl.REPEAT:this.gl.CLAMP_TO_EDGE;this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_S,m),this.gl.samplerParameteri(this.mainSampler,this.gl.TEXTURE_WRAP_T,m),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(0,this.mainSampler),this.gl.uniform1i(this.textureLoc,0),this.gl.activeTexture(this.gl.TEXTURE1),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(1,this.mainSamplerFW),this.gl.uniform1i(this.textureFWLoc,1),this.gl.activeTexture(this.gl.TEXTURE2),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(2,this.mainSamplerFC),this.gl.uniform1i(this.textureFCLoc,2),this.gl.activeTexture(this.gl.TEXTURE3),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(3,this.mainSamplerPW),this.gl.uniform1i(this.texturePWLoc,3),this.gl.activeTexture(this.gl.TEXTURE4),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.bindSampler(4,this.mainSamplerPC),this.gl.uniform1i(this.texturePCLoc,4),this.gl.activeTexture(this.gl.TEXTURE5),this.gl.bindTexture(this.gl.TEXTURE_2D,i),this.gl.uniform1i(this.blurTexture1Loc,5),this.gl.activeTexture(this.gl.TEXTURE6),this.gl.bindTexture(this.gl.TEXTURE_2D,s),this.gl.uniform1i(this.blurTexture2Loc,6),this.gl.activeTexture(this.gl.TEXTURE7),this.gl.bindTexture(this.gl.TEXTURE_2D,r),this.gl.uniform1i(this.blurTexture3Loc,7),this.gl.activeTexture(this.gl.TEXTURE8),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQ),this.gl.uniform1i(this.noiseLQLoc,8),this.gl.activeTexture(this.gl.TEXTURE9),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexMQ),this.gl.uniform1i(this.noiseMQLoc,9),this.gl.activeTexture(this.gl.TEXTURE10),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexHQ),this.gl.uniform1i(this.noiseHQLoc,10),this.gl.activeTexture(this.gl.TEXTURE11),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQLite),this.gl.uniform1i(this.noiseLQLiteLoc,11),this.gl.activeTexture(this.gl.TEXTURE12),this.gl.bindTexture(this.gl.TEXTURE_2D,this.noise.noiseTexLQ),this.gl.bindSampler(12,this.noise.noiseTexPointLQ),this.gl.uniform1i(this.noisePointLQLoc,12),this.gl.activeTexture(this.gl.TEXTURE13),this.gl.bindTexture(this.gl.TEXTURE_3D,this.noise.noiseTexVolLQ),this.gl.uniform1i(this.noiseVolLQLoc,13),this.gl.activeTexture(this.gl.TEXTURE14),this.gl.bindTexture(this.gl.TEXTURE_3D,this.noise.noiseTexVolHQ),this.gl.uniform1i(this.noiseVolHQLoc,14);for(var u=0;u<this.userTextures.length;u++){var g=this.userTextures[u];this.gl.activeTexture(this.gl.TEXTURE15+u),this.gl.bindTexture(this.gl.TEXTURE_2D,this.image.getTexture(g.sampler)),this.gl.uniform1i(g.textureLoc,15+u)}this.gl.uniform1f(this.timeLoc,o.time),this.gl.uniform1f(this.gammaAdjLoc,o.gammaadj),this.gl.uniform1f(this.echoZoomLoc,o.echo_zoom),this.gl.uniform1f(this.echoAlphaLoc,o.echo_alpha),this.gl.uniform1f(this.echoOrientationLoc,o.echo_orient),this.gl.uniform1i(this.invertLoc,o.invert),this.gl.uniform1i(this.brightenLoc,o.brighten),this.gl.uniform1i(this.darkenLoc,o.darken),this.gl.uniform1i(this.solarizeLoc,o.solarize),this.gl.uniform2fv(this.resolutionLoc,[this.texsizeX,this.texsizeY]),this.gl.uniform4fv(this.aspectLoc,[this.aspectx,this.aspecty,this.invAspectx,this.invAspecty]),this.gl.uniform4fv(this.texsizeLoc,new Float32Array([this.texsizeX,this.texsizeY,1/this.texsizeX,1/this.texsizeY])),this.gl.uniform4fv(this.texsizeNoiseLQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseMQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseHQLoc,[256,256,1/256,1/256]),this.gl.uniform4fv(this.texsizeNoiseLQLiteLoc,[32,32,1/32,1/32]),this.gl.uniform4fv(this.texsizeNoiseVolLQLoc,[32,32,1/32,1/32]),this.gl.uniform4fv(this.texsizeNoiseVolHQLoc,[32,32,1/32,1/32]),this.gl.uniform1f(this.bassLoc,o.bass),this.gl.uniform1f(this.midLoc,o.mid),this.gl.uniform1f(this.trebLoc,o.treb),this.gl.uniform1f(this.volLoc,(o.bass+o.mid+o.treb)/3),this.gl.uniform1f(this.bassAttLoc,o.bass_att),this.gl.uniform1f(this.midAttLoc,o.mid_att),this.gl.uniform1f(this.trebAttLoc,o.treb_att),this.gl.uniform1f(this.volAttLoc,(o.bass_att+o.mid_att+o.treb_att)/3),this.gl.uniform1f(this.frameLoc,o.frame),this.gl.uniform1f(this.fpsLoc,o.fps),this.gl.uniform4fv(this.randPresetLoc,o.rand_preset),this.gl.uniform4fv(this.randFrameLoc,new Float32Array([Math.random(),Math.random(),Math.random(),Math.random()])),this.gl.uniform1f(this.fShaderLoc,o.fshader),this.gl.uniform4fv(this.qaLoc,new Float32Array([o.q1||0,o.q2||0,o.q3||0,o.q4||0])),this.gl.uniform4fv(this.qbLoc,new Float32Array([o.q5||0,o.q6||0,o.q7||0,o.q8||0])),this.gl.uniform4fv(this.qcLoc,new Float32Array([o.q9||0,o.q10||0,o.q11||0,o.q12||0])),this.gl.uniform4fv(this.qdLoc,new Float32Array([o.q13||0,o.q14||0,o.q15||0,o.q16||0])),this.gl.uniform4fv(this.qeLoc,new Float32Array([o.q17||0,o.q18||0,o.q19||0,o.q20||0])),this.gl.uniform4fv(this.qfLoc,new Float32Array([o.q21||0,o.q22||0,o.q23||0,o.q24||0])),this.gl.uniform4fv(this.qgLoc,new Float32Array([o.q25||0,o.q26||0,o.q27||0,o.q28||0])),this.gl.uniform4fv(this.qhLoc,new Float32Array([o.q29||0,o.q30||0,o.q31||0,o.q32||0])),this.gl.uniform4fv(this.slowRoamCosLoc,[.5+.5*Math.cos(.005*o.time),.5+.5*Math.cos(.008*o.time),.5+.5*Math.cos(.013*o.time),.5+.5*Math.cos(.022*o.time)]),this.gl.uniform4fv(this.roamCosLoc,[.5+.5*Math.cos(.3*o.time),.5+.5*Math.cos(1.3*o.time),.5+.5*Math.cos(5*o.time),.5+.5*Math.cos(20*o.time)]),this.gl.uniform4fv(this.slowRoamSinLoc,[.5+.5*Math.sin(.005*o.time),.5+.5*Math.sin(.008*o.time),.5+.5*Math.sin(.013*o.time),.5+.5*Math.sin(.022*o.time)]),this.gl.uniform4fv(this.roamSinLoc,[.5+.5*Math.sin(.3*o.time),.5+.5*Math.sin(1.3*o.time),.5+.5*Math.sin(5*o.time),.5+.5*Math.sin(20*o.time)]),this.bindBlurVals(a,h),t?this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA):this.gl.disable(this.gl.BLEND),this.gl.drawElements(this.gl.TRIANGLES,this.indices.length,this.gl.UNSIGNED_SHORT,0),t||this.gl.enable(this.gl.BLEND)}}])&&C(e.prototype,i),s&&C(e,s),t}();function V(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var I=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.textureRatio=i.textureRatio,this.texsizeX=i.texsizeX,this.texsizeY=i.texsizeY,this.positions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.vertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.useFXAA()?this.createFXAAShader():this.createShader()}var e,i,s;return e=t,(i=[{key:\"useFXAA\",value:function(){return this.textureRatio<=1}},{key:\"updateGlobals\",value:function(t){this.textureRatio=t.textureRatio,this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.gl.deleteProgram(this.shaderProgram),this.useFXAA()?this.createFXAAShader():this.createShader()}},{key:\"createFXAAShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n       const vec2 halfmad = vec2(0.5);\\n       in vec2 aPos;\\n       out vec2 v_rgbM;\\n       out vec2 v_rgbNW;\\n       out vec2 v_rgbNE;\\n       out vec2 v_rgbSW;\\n       out vec2 v_rgbSE;\\n       uniform vec4 texsize;\\n       void main(void) {\\n         gl_Position = vec4(aPos, 0.0, 1.0);\\n\\n         v_rgbM = aPos * halfmad + halfmad;\\n         v_rgbNW = v_rgbM + (vec2(-1.0, -1.0) * texsize.zx);\\n         v_rgbNE = v_rgbM + (vec2(1.0, -1.0) * texsize.zx);\\n         v_rgbSW = v_rgbM + (vec2(-1.0, 1.0) * texsize.zx);\\n         v_rgbSE = v_rgbM + (vec2(1.0, 1.0) * texsize.zx);\\n       }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 v_rgbM;\\n       in vec2 v_rgbNW;\\n       in vec2 v_rgbNE;\\n       in vec2 v_rgbSW;\\n       in vec2 v_rgbSE;\\n       out vec4 fragColor;\\n       uniform vec4 texsize;\\n       uniform sampler2D uTexture;\\n\\n       #ifndef FXAA_REDUCE_MIN\\n         #define FXAA_REDUCE_MIN   (1.0/ 128.0)\\n       #endif\\n       #ifndef FXAA_REDUCE_MUL\\n         #define FXAA_REDUCE_MUL   (1.0 / 8.0)\\n       #endif\\n       #ifndef FXAA_SPAN_MAX\\n         #define FXAA_SPAN_MAX     8.0\\n       #endif\\n\\n       void main(void) {\\n         vec4 color;\\n         vec3 rgbNW = textureLod(uTexture, v_rgbNW, 0.0).xyz;\\n         vec3 rgbNE = textureLod(uTexture, v_rgbNE, 0.0).xyz;\\n         vec3 rgbSW = textureLod(uTexture, v_rgbSW, 0.0).xyz;\\n         vec3 rgbSE = textureLod(uTexture, v_rgbSE, 0.0).xyz;\\n         vec3 rgbM  = textureLod(uTexture, v_rgbM, 0.0).xyz;\\n         vec3 luma = vec3(0.299, 0.587, 0.114);\\n         float lumaNW = dot(rgbNW, luma);\\n         float lumaNE = dot(rgbNE, luma);\\n         float lumaSW = dot(rgbSW, luma);\\n         float lumaSE = dot(rgbSE, luma);\\n         float lumaM  = dot(rgbM,  luma);\\n         float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\\n         float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\\n\\n         mediump vec2 dir;\\n         dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\n         dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\n\\n         float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *\\n                               (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\\n\\n         float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\\n         dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),\\n                   max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\\n                   dir * rcpDirMin)) * texsize.zw;\\n\\n         vec3 rgbA = 0.5 * (\\n             textureLod(uTexture, v_rgbM + dir * (1.0 / 3.0 - 0.5), 0.0).xyz +\\n             textureLod(uTexture, v_rgbM + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);\\n         vec3 rgbB = rgbA * 0.5 + 0.25 * (\\n             textureLod(uTexture, v_rgbM + dir * -0.5, 0.0).xyz +\\n             textureLod(uTexture, v_rgbM + dir * 0.5, 0.0).xyz);\\n\\n         float lumaB = dot(rgbB, luma);\\n         if ((lumaB < lumaMin) || (lumaB > lumaMax))\\n           color = vec4(rgbA, 1.0);\\n         else\\n           color = vec4(rgbB, 1.0);\\n\\n         fragColor = color;\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\"),this.texsizeLoc=this.gl.getUniformLocation(this.shaderProgram,\"texsize\")}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n       const vec2 halfmad = vec2(0.5);\\n       in vec2 aPos;\\n       out vec2 uv;\\n       void main(void) {\\n         gl_Position = vec4(aPos, 0.0, 1.0);\\n         uv = aPos * halfmad + halfmad;\\n       }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 uv;\\n       out vec4 fragColor;\\n       uniform sampler2D uTexture;\\n\\n       void main(void) {\\n         fragColor = vec4(texture(uTexture, uv).rgb, 1.0);\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\")}},{key:\"renderQuadTexture\",value:function(t){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.uniform1i(this.textureLoc,0),this.useFXAA()&&this.gl.uniform4fv(this.texsizeLoc,new Float32Array([this.texsizeX,this.texsizeY,1/this.texsizeX,1/this.texsizeY])),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}])&&V(e.prototype,i),s&&V(e,s),t}();function X(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var k=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.positions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.vertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n       const vec2 halfmad = vec2(0.5);\\n       in vec2 aPos;\\n       out vec2 uv;\\n       void main(void) {\\n         gl_Position = vec4(aPos, 0.0, 1.0);\\n         uv = aPos * halfmad + halfmad;\\n       }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 uv;\\n       out vec4 fragColor;\\n       uniform sampler2D uTexture;\\n\\n       void main(void) {\\n         fragColor = vec4(texture(uTexture, uv).rgb, 1.0);\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\")}},{key:\"renderQuadTexture\",value:function(t){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.uniform1i(this.textureLoc,0),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}])&&X(e.prototype,i),s&&X(e,s),t}();function N(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var O=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.blurLevel=i;var s=[4,3.8,3.5,2.9,1.9,1.2,.7,.3],r=s[0]+s[1]+s[2]+s[3],a=s[4]+s[5]+s[6]+s[7],h=0+(s[2]+s[3])/r*2,o=2+(s[6]+s[7])/a*2;this.wds=new Float32Array([r,a,h,o]),this.wDiv=1/(2*(r+a)),this.positions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.vertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      const vec2 halfmad = vec2(0.5);\\n                                      in vec2 aPos;\\n                                      out vec2 uv;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 0.0, 1.0);\\n                                        uv = aPos * halfmad + halfmad;\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 uv;\\n       out vec4 fragColor;\\n       uniform sampler2D uTexture;\\n       uniform vec4 texsize;\\n       uniform float ed1;\\n       uniform float ed2;\\n       uniform float ed3;\\n       uniform vec4 wds;\\n       uniform float wdiv;\\n\\n       void main(void) {\\n         float w1 = wds[0];\\n         float w2 = wds[1];\\n         float d1 = wds[2];\\n         float d2 = wds[3];\\n\\n         vec2 uv2 = uv.xy;\\n\\n         vec3 blur =\\n           ( texture(uTexture, uv2 + vec2(0.0, d1 * texsize.w) ).xyz\\n           + texture(uTexture, uv2 + vec2(0.0,-d1 * texsize.w) ).xyz) * w1 +\\n           ( texture(uTexture, uv2 + vec2(0.0, d2 * texsize.w) ).xyz\\n           + texture(uTexture, uv2 + vec2(0.0,-d2 * texsize.w) ).xyz) * w2;\\n\\n         blur.xyz *= wdiv;\\n\\n         float t = min(min(uv.x, uv.y), 1.0 - max(uv.x, uv.y));\\n         t = sqrt(t);\\n         t = ed1 + ed2 * clamp(t * ed3, 0.0, 1.0);\\n         blur.xyz *= t;\\n\\n         fragColor = vec4(blur, 1.0);\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\"),this.texsizeLocation=this.gl.getUniformLocation(this.shaderProgram,\"texsize\"),this.ed1Loc=this.gl.getUniformLocation(this.shaderProgram,\"ed1\"),this.ed2Loc=this.gl.getUniformLocation(this.shaderProgram,\"ed2\"),this.ed3Loc=this.gl.getUniformLocation(this.shaderProgram,\"ed3\"),this.wdsLocation=this.gl.getUniformLocation(this.shaderProgram,\"wds\"),this.wdivLoc=this.gl.getUniformLocation(this.shaderProgram,\"wdiv\")}},{key:\"renderQuadTexture\",value:function(t,e,i){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.uniform1i(this.textureLoc,0);var s=0===this.blurLevel?e.b1ed:0;this.gl.uniform4fv(this.texsizeLocation,[i[0],i[1],1/i[0],1/i[1]]),this.gl.uniform1f(this.ed1Loc,1-s),this.gl.uniform1f(this.ed2Loc,s),this.gl.uniform1f(this.ed3Loc,5),this.gl.uniform4fv(this.wdsLocation,this.wds),this.gl.uniform1f(this.wdivLoc,this.wDiv),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}])&&N(e.prototype,i),s&&N(e,s),t}();function W(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var Q=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.blurLevel=i;var s=[4,3.8,3.5,2.9,1.9,1.2,.7,.3],r=s[0]+s[1],a=s[2]+s[3],h=s[4]+s[5],o=s[6]+s[7],n=0+2*s[1]/r,l=2+2*s[3]/a,m=4+2*s[5]/h,u=6+2*s[7]/o;this.ws=new Float32Array([r,a,h,o]),this.ds=new Float32Array([n,l,m,u]),this.wDiv=.5/(r+a+h+o),this.positions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.vertexBuf=this.gl.createBuffer(),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n                                      const vec2 halfmad = vec2(0.5);\\n                                      in vec2 aPos;\\n                                      out vec2 uv;\\n                                      void main(void) {\\n                                        gl_Position = vec4(aPos, 0.0, 1.0);\\n                                        uv = aPos * halfmad + halfmad;\\n                                      }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 uv;\\n       out vec4 fragColor;\\n       uniform sampler2D uTexture;\\n       uniform vec4 texsize;\\n       uniform float scale;\\n       uniform float bias;\\n       uniform vec4 ws;\\n       uniform vec4 ds;\\n       uniform float wdiv;\\n\\n       void main(void) {\\n         float w1 = ws[0];\\n         float w2 = ws[1];\\n         float w3 = ws[2];\\n         float w4 = ws[3];\\n         float d1 = ds[0];\\n         float d2 = ds[1];\\n         float d3 = ds[2];\\n         float d4 = ds[3];\\n\\n         vec2 uv2 = uv.xy;\\n\\n         vec3 blur =\\n           ( texture(uTexture, uv2 + vec2( d1 * texsize.z,0.0) ).xyz\\n           + texture(uTexture, uv2 + vec2(-d1 * texsize.z,0.0) ).xyz) * w1 +\\n           ( texture(uTexture, uv2 + vec2( d2 * texsize.z,0.0) ).xyz\\n           + texture(uTexture, uv2 + vec2(-d2 * texsize.z,0.0) ).xyz) * w2 +\\n           ( texture(uTexture, uv2 + vec2( d3 * texsize.z,0.0) ).xyz\\n           + texture(uTexture, uv2 + vec2(-d3 * texsize.z,0.0) ).xyz) * w3 +\\n           ( texture(uTexture, uv2 + vec2( d4 * texsize.z,0.0) ).xyz\\n           + texture(uTexture, uv2 + vec2(-d4 * texsize.z,0.0) ).xyz) * w4;\\n\\n         blur.xyz *= wdiv;\\n         blur.xyz = blur.xyz * scale + bias;\\n\\n         fragColor = vec4(blur, 1.0);\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\"),this.texsizeLocation=this.gl.getUniformLocation(this.shaderProgram,\"texsize\"),this.scaleLoc=this.gl.getUniformLocation(this.shaderProgram,\"scale\"),this.biasLoc=this.gl.getUniformLocation(this.shaderProgram,\"bias\"),this.wsLoc=this.gl.getUniformLocation(this.shaderProgram,\"ws\"),this.dsLocation=this.gl.getUniformLocation(this.shaderProgram,\"ds\"),this.wdivLoc=this.gl.getUniformLocation(this.shaderProgram,\"wdiv\")}},{key:\"getScaleAndBias\",value:function(t,e){var i,s,r=[1,1,1],a=[0,0,0];return r[0]=1/(e[0]-t[0]),a[0]=-t[0]*r[0],i=(t[1]-t[0])/(e[0]-t[0]),s=(e[1]-t[0])/(e[0]-t[0]),r[1]=1/(s-i),a[1]=-i*r[1],i=(t[2]-t[1])/(e[1]-t[1]),s=(e[2]-t[1])/(e[1]-t[1]),r[2]=1/(s-i),a[2]=-i*r[2],{scale:r[this.blurLevel],bias:a[this.blurLevel]}}},{key:\"renderQuadTexture\",value:function(t,e,i,s,r){this.gl.useProgram(this.shaderProgram),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.uniform1i(this.textureLoc,0);var a=this.getScaleAndBias(i,s),h=a.scale,o=a.bias;this.gl.uniform4fv(this.texsizeLocation,[r[0],r[1],1/r[0],1/r[1]]),this.gl.uniform1f(this.scaleLoc,h),this.gl.uniform1f(this.biasLoc,o),this.gl.uniform4fv(this.wsLoc,this.ws),this.gl.uniform4fv(this.dsLocation,this.ds),this.gl.uniform1f(this.wdivLoc,this.wDiv),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}])&&W(e.prototype,i),s&&W(e,s),t}();function Y(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var G=function(){function t(e,i,s){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.blurLevel=e,this.blurRatios=i,this.gl=s,this.texsizeX=r.texsizeX,this.texsizeY=r.texsizeY,this.anisoExt=this.gl.getExtension(\"EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"MOZ_EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"WEBKIT_EXT_texture_filter_anisotropic\"),this.blurHorizontalFrameBuffer=this.gl.createFramebuffer(),this.blurVerticalFrameBuffer=this.gl.createFramebuffer(),this.blurHorizontalTexture=this.gl.createTexture(),this.blurVerticalTexture=this.gl.createTexture(),this.setupFrameBufferTextures(),this.blurHorizontal=new Q(s,this.blurLevel,r),this.blurVertical=new O(s,this.blurLevel,r)}var e,i,s;return e=t,(i=[{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.setupFrameBufferTextures()}},{key:\"getTextureSize\",value:function(t){var e=Math.max(this.texsizeX*t,16);e=16*Math.floor((e+3)/16);var i=Math.max(this.texsizeY*t,16);return[e,i=4*Math.floor((i+3)/4)]}},{key:\"setupFrameBufferTextures\",value:function(){var t=this.blurLevel>0?this.blurRatios[this.blurLevel-1]:[1,1],e=this.blurRatios[this.blurLevel],i=this.getTextureSize(t[1]),s=this.getTextureSize(e[0]);this.bindFrameBufferTexture(this.blurHorizontalFrameBuffer,this.blurHorizontalTexture,s);var r=s,a=this.getTextureSize(e[1]);this.bindFrameBufferTexture(this.blurVerticalFrameBuffer,this.blurVerticalTexture,a),this.horizontalTexsizes=[i,s],this.verticalTexsizes=[r,a]}},{key:\"bindFrambufferAndSetViewport\",value:function(t,e){this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,t),this.gl.viewport(0,0,e[0],e[1])}},{key:\"bindFrameBufferTexture\",value:function(t,e,i){if(this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,i[0],i[1],0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,new Uint8Array(i[0]*i[1]*4)),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.anisoExt){var s=this.gl.getParameter(this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this.gl.texParameterf(this.gl.TEXTURE_2D,this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,s)}this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,t),this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,this.gl.TEXTURE_2D,e,0)}},{key:\"renderBlurTexture\",value:function(t,e,i,s){this.bindFrambufferAndSetViewport(this.blurHorizontalFrameBuffer,this.horizontalTexsizes[1]),this.blurHorizontal.renderQuadTexture(t,e,i,s,this.horizontalTexsizes[0]),this.gl.bindTexture(this.gl.TEXTURE_2D,this.blurHorizontalTexture),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.bindFrambufferAndSetViewport(this.blurVerticalFrameBuffer,this.verticalTexsizes[1]),this.blurVertical.renderQuadTexture(this.blurHorizontalTexture,e,this.verticalTexsizes[0]),this.gl.bindTexture(this.gl.TEXTURE_2D,this.blurVerticalTexture),this.gl.generateMipmap(this.gl.TEXTURE_2D)}}])&&Y(e.prototype,i),s&&Y(e,s),t}();function H(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var j=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.anisoExt=this.gl.getExtension(\"EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"MOZ_EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"WEBKIT_EXT_texture_filter_anisotropic\"),this.noiseTexLQ=this.gl.createTexture(),this.noiseTexLQLite=this.gl.createTexture(),this.noiseTexMQ=this.gl.createTexture(),this.noiseTexHQ=this.gl.createTexture(),this.noiseTexVolLQ=this.gl.createTexture(),this.noiseTexVolHQ=this.gl.createTexture(),this.nTexArrLQ=t.createNoiseTex(256,1),this.nTexArrLQLite=t.createNoiseTex(32,1),this.nTexArrMQ=t.createNoiseTex(256,4),this.nTexArrHQ=t.createNoiseTex(256,8),this.nTexArrVolLQ=t.createNoiseVolTex(32,1),this.nTexArrVolHQ=t.createNoiseVolTex(32,4),this.bindTexture(this.noiseTexLQ,this.nTexArrLQ,256,256),this.bindTexture(this.noiseTexLQLite,this.nTexArrLQLite,32,32),this.bindTexture(this.noiseTexMQ,this.nTexArrMQ,256,256),this.bindTexture(this.noiseTexHQ,this.nTexArrHQ,256,256),this.bindTexture3D(this.noiseTexVolLQ,this.nTexArrVolLQ,32,32,32),this.bindTexture3D(this.noiseTexVolHQ,this.nTexArrVolHQ,32,32,32),this.noiseTexPointLQ=this.gl.createSampler(),e.samplerParameteri(this.noiseTexPointLQ,e.TEXTURE_MIN_FILTER,e.NEAREST_MIPMAP_NEAREST),e.samplerParameteri(this.noiseTexPointLQ,e.TEXTURE_MAG_FILTER,e.NEAREST),e.samplerParameteri(this.noiseTexPointLQ,e.TEXTURE_WRAP_S,e.REPEAT),e.samplerParameteri(this.noiseTexPointLQ,e.TEXTURE_WRAP_T,e.REPEAT)}var e,i,s;return e=t,s=[{key:\"fCubicInterpolate\",value:function(t,e,i,s,r){var a=r*r,h=s-i-t+e;return h*(r*a)+(t-e-h)*a+(i-t)*r+e}},{key:\"dwCubicInterpolate\",value:function(e,i,s,r,a){for(var h=[],o=0;o<4;o++){var n=t.fCubicInterpolate(e[o]/255,i[o]/255,s[o]/255,r[o]/255,a);n=Math.clamp(n,0,1),h[o]=255*n}return h}},{key:\"createNoiseVolTex\",value:function(e,i){for(var s=e*e*e,r=new Uint8Array(4*s),a=i>1?216:256,h=.5*a,o=0;o<s;o++)r[4*o+0]=Math.floor(Math.random()*a+h),r[4*o+1]=Math.floor(Math.random()*a+h),r[4*o+2]=Math.floor(Math.random()*a+h),r[4*o+3]=Math.floor(Math.random()*a+h);var n=e*e,l=e;if(i>1){for(var m=0;m<e;m+=i)for(var u=0;u<e;u+=i)for(var g=0;g<e;g++)if(g%i!=0){for(var c=Math.floor(g/i)*i+e,A=m*n+u*l,f=[],d=[],v=[],p=[],_=0;_<4;_++)f[_]=r[4*A+(c-i)%e*4+_],d[_]=r[4*A+c%e*4+_],v[_]=r[4*A+(c+i)%e*4+_],p[_]=r[4*A+(c+2*i)%e*4+_];for(var x=g%i/i,b=t.dwCubicInterpolate(f,d,v,p,x),T=0;T<4;T++){r[m*n*4+u*l*4+(4*g+T)]=b[T]}}for(var E=0;E<e;E+=i)for(var P=0;P<e;P++)for(var R=0;R<e;R++)if(R%i!=0){for(var L=Math.floor(R/i)*i+e,S=E*n,y=[],w=[],U=[],M=[],F=0;F<4;F++){var q=4*P+4*S+F;y[F]=r[(L-i)%e*l*4+q],w[F]=r[L%e*l*4+q],U[F]=r[(L+i)%e*l*4+q],M[F]=r[(L+2*i)%e*l*4+q]}for(var z=R%i/i,B=t.dwCubicInterpolate(y,w,U,M,z),C=0;C<4;C++){r[R*l*4+(4*P+4*S+C)]=B[C]}}for(var D=0;D<e;D++)for(var V=0;V<e;V++)for(var I=0;I<e;I++)if(I%i!=0){for(var X=V*l,k=Math.floor(I/i)*i+e,N=[],O=[],W=[],Q=[],Y=0;Y<4;Y++){var G=4*D+4*X+Y;N[Y]=r[(k-i)%e*n*4+G],O[Y]=r[k%e*n*4+G],W[Y]=r[(k+i)%e*n*4+G],Q[Y]=r[(k+2*i)%e*n*4+G]}for(var H=V%i/i,j=t.dwCubicInterpolate(N,O,W,Q,H),K=0;K<4;K++){r[I*n*4+(4*D+4*X+K)]=j[K]}}}return r}},{key:\"createNoiseTex\",value:function(e,i){for(var s=e*e,r=new Uint8Array(4*s),a=i>1?216:256,h=.5*a,o=0;o<s;o++)r[4*o+0]=Math.floor(Math.random()*a+h),r[4*o+1]=Math.floor(Math.random()*a+h),r[4*o+2]=Math.floor(Math.random()*a+h),r[4*o+3]=Math.floor(Math.random()*a+h);if(i>1){for(var n=0;n<e;n+=i)for(var l=0;l<e;l++)if(l%i!=0){for(var m=Math.floor(l/i)*i+e,u=n*e,g=[],c=[],A=[],f=[],d=0;d<4;d++)g[d]=r[4*u+(m-i)%e*4+d],c[d]=r[4*u+m%e*4+d],A[d]=r[4*u+(m+i)%e*4+d],f[d]=r[4*u+(m+2*i)%e*4+d];for(var v=l%i/i,p=t.dwCubicInterpolate(g,c,A,f,v),_=0;_<4;_++)r[n*e*4+4*l+_]=p[_]}for(var x=0;x<e;x++)for(var b=0;b<e;b++)if(b%i!=0){for(var T=Math.floor(b/i)*i+e,E=[],P=[],R=[],L=[],S=0;S<4;S++)E[S]=r[(T-i)%e*e*4+4*x+S],P[S]=r[T%e*e*4+4*x+S],R[S]=r[(T+i)%e*e*4+4*x+S],L[S]=r[(T+2*i)%e*e*4+4*x+S];for(var y=b%i/i,w=t.dwCubicInterpolate(E,P,R,L,y),U=0;U<4;U++)r[b*e*4+4*x+U]=w[U]}}return r}}],(i=[{key:\"bindTexture\",value:function(t,e,i,s){if(this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,i,s,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.anisoExt){var r=this.gl.getParameter(this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this.gl.texParameterf(this.gl.TEXTURE_2D,this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,r)}}},{key:\"bindTexture3D\",value:function(t,e,i,s,r){if(this.gl.bindTexture(this.gl.TEXTURE_3D,t),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texImage3D(this.gl.TEXTURE_3D,0,this.gl.RGBA,i,s,r,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e),this.gl.generateMipmap(this.gl.TEXTURE_3D),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.anisoExt){var a=this.gl.getParameter(this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this.gl.texParameterf(this.gl.TEXTURE_3D,this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,a)}}}])&&H(e.prototype,i),s&&H(e,s),t}();function K(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var J=function(){function t(e){var i=this;!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.anisoExt=this.gl.getExtension(\"EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"MOZ_EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"WEBKIT_EXT_texture_filter_anisotropic\"),this.samplers={},this.clouds2Image=new Image,this.clouds2Image.onload=function(){i.samplers.clouds2=i.gl.createTexture(),i.bindTexture(i.samplers.clouds2,i.clouds2Image,128,128)},this.clouds2Image.src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4RP+RXhpZgAASUkqAAgAAAAJAA8BAgAGAAAAegAAABABAgAVAAAAgAAAABIBAwABAAAAAQAAABoBBQABAAAAoAAAABsBBQABAAAAqAAAACgBAwABAAAAAgAAADIBAgAUAAAAsAAAABMCAwABAAAAAQAAAGmHBAABAAAAxAAAAGYFAABDYW5vbgBDYW5vbiBQb3dlclNob3QgUzExMAAAAAAAAAAAAAAAAEgAAAABAAAASAAAAAEAAAAyMDAyOjAxOjE5IDE3OjMzOjIwABsAmoIFAAEAAABWAwAAnYIFAAEAAABeAwAAAJAHAAQAAAAwMjEwA5ACABQAAAAOAgAABJACABQAAAAiAgAAAZEHAAQAAAABAgMAApEFAAEAAAA+AwAAAZIKAAEAAABGAwAAApIFAAEAAABOAwAABJIKAAEAAABmAwAABZIFAAEAAABuAwAABpIFAAEAAAB2AwAAB5IDAAEAAAAFAAAACZIDAAEAAAAAAAAACpIFAAEAAAB+AwAAfJIHAJoBAACGAwAAhpIHAAgBAAA2AgAAAKAHAAQAAAAwMTAwAaADAAEAAAABAAAAAqAEAAEAAACAAAAAA6AEAAEAAACAAAAABaAEAAEAAAAwBQAADqIFAAEAAAAgBQAAD6IFAAEAAAAoBQAAEKIDAAEAAAACAAAAF6IDAAEAAAACAAAAAKMHAAEAAAADAAAAAAAAADIwMDI6MDE6MTkgMTc6MzM6MjAAMjAwMjowMToxOSAxNzozMzoyMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQAAACoBAAAgAAAAuAAAACAAAAABAAAAgAIAAEgAAAAKAAAA/////wMAAACK+AIAAAABAL8BAADoAwAArQAAACAAAAAMAAEAAwAmAAAAHAQAAAIAAwAEAAAAaAQAAAMAAwAEAAAAcAQAAAQAAwAaAAAAeAQAAAAAAwAGAAAArAQAAAAAAwAEAAAAuAQAAAYAAgAgAAAAwAQAAAcAAgAYAAAA4AQAAAgABAABAAAAkc4UAAkAAgAgAAAA+AQAABAABAABAAAAAAAJAQ0AAwAEAAAAGAUAAAAAAABMAAIAAAAFAAAAAAAAAAQAAAABAAAAAQAAAAAAAAAAAAAAAwABAAEwAAD/////WgGtACAAYgC4AP//AAAAAAAAAAAAAP//SABABkAGAgCtANMAngAAAAAAAAAAADQAAACPAEYBtQAqAfT/AgABAAEAAAAAAAAAAAAEMAAAAAAAAAAAvwEAALgAJwEAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAElNRzpQb3dlclNob3QgUzExMCBKUEVHAAAAAAAAAAAARmlybXdhcmUgVmVyc2lvbiAxLjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAMgAuQC5AABqGADOAAAAgE8SAJsAAAAEAAEAAgAEAAAAUjk4AAIABwAEAAAAMDEwMAEQAwABAAAAQAYAAAIQAwABAAAAsAQAAAAAAAAGAAMBAwABAAAABgAAABoBBQABAAAAtAUAABsBBQABAAAAvAUAACgBAwABAAAAAgAAAAECBAABAAAA9AUAAAICBAABAAAAuA0AAAAAAAC0AAAAAQAAALQAAAABAAAAaM5qp6ps7vXbS52etpVdo/tuYZ2wtrDFXnrx1HK+braKpineV1+3VFWVteo72Poc/9j/2wCEAAkGBggGBQkIBwgKCQkLDRYPDQwMDRwTFRAWIR0jIiEcIB8kKTQsJCcxJx4fLT0tMTY3Ojo6Iio/RD44QjM3OTYBCQkJDAoMFAwMFA8KCgoPGhoKChoaTxoaGhoaT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT//AABEIAHgAoAMBIQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOdCcU4R11HMSLHTxFTAXy6PLxQIUJTglIDo9KtbWzjScNvnK/gtao1FkycjaO1ebWvOWvyR307RjZfM5zXoraacTW3DtkyD1PrWathui39q66cmoK+60OacU5O2xA8ZQlT2qBkrdfmYsiZMUwpxVCImXNRMntTERlaaRg0CN5Y8iniOszUlWOniOgQhj5o2UwDZS7KBFmAuoCnIAq69wUjIHPHWuaok5HTBtIqrbzXCMyAEDqCarPvGV6Yqlbb+Xch337kBTOd1RNHxgCrc+xKgNWAPxyD2qCWMAY7g81UJ83yJlGxCy4qJlzWqMyMpTClAjoxCUbDCniP2rK5qOVKkEdMA8ummPmgA2Vd0m1S4vMTIXjUEtjtUzdotrdLQcFeSXQfcQqJ2y/GaZL5fkhE5Y9TXPFt2Zu7K6IUinVWVW+XvjvSNCsceScsa0k1067kRT69NisY8mnC2YoWA4qL2KtcglyjcVVdd78daqnK3zImr/IheFgTkdKiZK6ou6MJKxGyUwrTJOxmjaS2WYqwjLHbnp9KBaeeB5MbZxzXLGVlfotzpcdbdXsQiKniOtSBfLppjoTE0NMdPiYxElSRmiSurAnZiSMTzmmKSDmpUdCpS1NvT0TUoHEjpGQcYC8n3qM6MJdxgYuF46VyyfI2ui6nQlzJPq+hDPo0qcKNz/wB0U54Es7co/wAzkcgdAamU01ZbtjUWnrsjDn+dzxiqpjYHK1aZDHJGQmM9ahe2zk+lbU5WZlOOhWZKjKV1nOddYTPLpptjztbcB2NTBXibaSUOOma4IWt+h2y3/Uj8rmlEdbJmLQpTjpTNlNCYnl00x1RI0x00x4oARd6tmPIPtW1o+uf2fGd+GORlcdffNZVaaqRt1NKc+R36HQxWsWoqbmGQ/MMkg4rL1bSdi5UV5fM4ys9LHfZNXXU599Lkd+FNMbSzGPmHNb85lyFaS32HgUx8pGcqK2g72M5aGY8fPSomSvRRwndafZfYtRCzL8rHFaPiPTTHKlxHGEjKhTj1ryKU/wB4uzR6dSPuPujF2YIzTxHxXamtuxyNPfuIY+KYY6okDHg4pHQIMsQKLhYhV0dtq8mr6aQ8loZRy390DNZVKqgr92aQpczKcd8+nXefLHAwVI6028nt7mTzIY/KJ5IB4qI3UuZO6fxIuSTjy21WzLmjXs9rKFidgM/dzxXTJeRECC5ZN5XPWscVTTlePxM0oS0s9kUriaIEiIKAPzrFup/3uBzmopU3fUqc0isTEQWftVWZ0dPlWuqNNr0RhKafqzOlh6mq7x12RZytHqssMcwSfy0wwyDuxRq2oCew8gxjdx1HT3rx6Uby9GenUdkc/wCSpPzdaV4WVeFJru226nLv8iFVc/eXFKYsCqi7omSIjHzS3EKSRZBJbHNOWwRMp4WjO/O0Z4NWUubuGParnafSsXFS0ZonYRo/Pwzcmk8gL0FbQgkjOUncfFK9sSU4JpkkzO+7Jz9atRV7mbk7WHpczAcOT9aUqzgu3Ud6lxSd1oylJvRkMgDZJJzVSTK9KqKJbIGJqJlzWiViG7nfW1/ZK8XJUDqT0q9q08V2sRiL5HAG35SD3Bryaalzps9KduWyKt1pjWoXzG2uRnkcCs+8ee2YKJUbIzx0Iq/bXemiRPs7IY15Ey7m+TA5BrPuNUDIyCMDnhs81rz3SsZ8tmXbFDe2DTKVzHwyk8n6Vl3944Zo04A7jvT9pp5oOTX1Mp5GVsnmtG21aEQKkikFRj604SFKJOmpWrHAYr9RUjMGXKcg9xW0WmYyTREwNN281qZkqphQRwacCMYPHvUPUpCPGhXORmqU0fNEXqEkV2j9qjKVoQa+GAALE47VPDezRYUOdo7V5CkelY0pb+eayOJt4PG1uSKxpEkQkkmp0T9StX8hnm5GCM1GUBzVXsIj+deFYge1NMTueuapyJURr2jMvTmqclq4PK4ohMJRIhGwNadgLolUjDMvcVtz217GfLc2PsuSQQdw7Uw2pU/MCK6FU6eWhg4afmWLeKFkZJcg9mFRzac8MSyMRhumKnns7PZvQOS6utLblaRMLyR9KhkhVVBDZzV21TFeysVXWoiK1MjttV8O/YWyXVgegFZRsTu4FeHdp2e63PWSvqupZtrbadpHFPnst4xgVDlqUkUX03ax7VEbNd3ByapSbFYDYKw4PPpTv7LdT0wRVq703J0XkBtlU7Sy7qje1yMMtJpoaaZWbTCZOB+FdVo+n/ZrRXaEh/pwacptxEo2ZZfRBLmQNskY8g1lXmm3VsS4IZaaxDvZ9NifZK35mUZbp7odD6jGK3jcotogmgUrWsp3tZ2sTGO+nqZr3Flco6JEEdc7eetLDoElxEH81Vz0FbQrOEby9530MZUlJ+7ppqOOgRxDMrqcdumaqz6Xa55YJnphqaxE5PRadgdGKWr17nd+cl4VFzGHAq0NEspRuRNp9K5vYxm3e6b2ZvzuK027CroNsPvLz6iql7oICFkOQO1RPCuMbp3a3Q41ruzWj2MG604xZJrInQoSVHPrXPB3NZEYlm6bM0gup0+SQttPXmt42W25DuRTW7ht6qXX1qxZSSSttZcqPWrjJPfXuiWrbGgFiADHBxW9p1z5dv8AvW3J2B7VbUeXuQnK/kM+0SyTt5GSg/ic8VUv7xpodrDn26Gs5wj0+LqXGT67dDFWLEhfkGo5nklyrE4qlC9vwJcrFRbJVl3GtO1njhTqQR61u4StYyU1civ7sSLtAJ981kSLnPJrelHlRhVlzM7yLTdTtJuu9Qe3NdBbGUorMFJxz2NcFPnUrWO2XK4lsdKCARg13bmBSurCGU4aMtn0qjJ4Xt3YnP0GK4pYbmk+X3bGyq2WvvFKTw5IpIRAR61Fc+Gttvvfn1GOlYeynHVq1uprzxfzKcCW1mdroXU8YIqQR2KA7AxPUgDGKiz3TKutjPnjic74jtB9TzT4p58Bc7yOm6tItrfoQ0mWEubtZf367l7DtUqq1w24gKg6kDpW0FFrm7Gc207dynKqqzAoOehFVmhLdFJ/CumKtuYN9gGnzuPlibmoXs5VJBXkH1qlVjtdEezlvYimtJEXLow/CqErIDWkZp7WZEotbnrsTkjrmphz1rGDutdToloxaK0EMkU9VGSKRDIQd4A9MVm+ZS0+F7selvPoNDuHw3T2oJWUlWH50r3Vn1HtqjG1LSmVS6DdzxxWQ+nTSTcghjXBKPs3Z/I6IvmV/vK7aWYptsp2jua0LG3tllLQZkK8dO9C95227g9FfcmuFnnUrtyF9BUthHhfLkjO0n14zXToo2WhiruV2JqFtFGNyxoSPUVztzrdzBJhdoVewFZJ8zs3dLY0a5dVu9yCTxLKUPyDd2NZE+tXDyF84J74rSMEiJSbKFxqFxMpDyuQe2azpN3dj+dbRlbYzkr7nvCJkYxsP95eDUqxyA584t7EVnTi+j5fLoaSa66+ZOM45orqMgooAYwqNhis5DQ0yMBio2Zm7ZrNu+5VrDNizPsdFI9CKjNrDCuEiCZ6kcVlKEd7fMtSe34DY2jV8YKknvzTLqUQcs+PwqJuyuVHU5TWtVeaX5coq/dGaxpLxpUw4zjvRFKwSepAF85SUGcdRVeaJh/DiqvZ2JsZ86sDz0qBo2xu/hq0yLHvy9KeK2pkvcdRWogpM0AIaYwqJAhNq1FcPKoHlIHHesZNqPu6vsWtXrou5HuK5YLzjjNZ1/c3YiIUZX+8vauec36LqbRivV9DNivriYlWOdo6HmrxleWIBgDx3HSpaugvZmDqFuWYgwKSPQVlsjxIym3BUgjmoXa+xT7lSOzd3PkAq3YZpby8vVASeNendBzWukt+nUz22Jo7S2v4A3lFGxzg1Rm0l4m+UMVPqKlSa03Q2k9T/9n4qqwQ2C6FUcJKhVwpbQ1vCsihOUlK0km1lS0VoSE2qiF4TrpDJE0aZJK5EgBF7pQGeoyWHrHyLxlrwklpeaZbWWmyFkkIa43/2P/bAEMAAgEBAQEBAgEBAQICAgICBAMCAgICBQQEAwQGBQYGBgUGBgYHCQgGBwkHBgYICwgJCgoKCgoGCAsMCwoMCQoKCv/bAEMBAgICAgICBQMDBQoHBgcKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCv/AABEIAIAAgAMBIgACEQEDEQH/xAAeAAACAwEAAwEBAAAAAAAAAAAGBwQFCAMBAgkACv/EADcQAAEDAwMDAgUDAgYCAwAAAAECAwQFBhEAEiEHMUETUQgiMmFxFIGRFaEjQlKxwdEW8ReCov/EABsBAAICAwEAAAAAAAAAAAAAAAUGAwQBAgcA/8QAMREAAgEDAwMCBQMDBQAAAAAAAQIDAAQRBRIhMUFRE3EiYYGRwaHR4QYU8BUjMnKx/9oADAMBAAIRAxEAPwDNEamJCR8v9tT4dJ3Zwn+2rSHStzaVBvOrSDShnBTpvDYpbIBqsi0QKRn0+QO2uwpJQQCjRFEpR8D+2uj1LIXjb/bWwfmtNvFDqaWE/LsHfXZFNB/y6uVU75uUjj7a6NwMfMEfjWd3Fa0f/DB0mtK7KpIum8KgUxqQ+0pmE2EqMlzOQFA/5MgZ/J1q2L1glUxsPtIbbitNpW80EgbwSO+PGsWWjUqhRZy/0Tqkh1OFgH78aaKLzm0i28SnlLddYwk+wGdJH9QafJd3QLtkdh4802aNeRwWxCjBHU+aA/iosex//ktysdPnN8SpAOymM/M1IUo7/wD6k8jS8uTpxPthCJL3yuJSFKGOwPY50wavS7gnU3+vro7i4QXkyA3naoc86FrhnVGqpQl1SvTI5QVZzycHR6zkmiiSMvkLwSevtQe7WJ5HcLyeRS/q0BHqLc9NIKjyB50Pz6cEkkj+2j2qUlDRWfrJSQEgdjqqRbKKkVMJe2uBO5KSngn20SW9t1OC1DjaTsMhaBKhBCWt23A841QVGnBaiQ3n86O67TGWigR1bsg7hjkHPnVFNiJSgpIyc8DRBDxVRhjigmVAAP041CcaW2rcgYI9tE82n5PCedVkqAUkgJ1uQDUXfFaZplIUMsqb2kHke2rGNSylf0g8+2j2rWvRZtbjvxXY7EV14tuymdxzknCiD9hnge+oU+110+WtoLS4hKylDiBwoe/+2gkVysgB80akhZCQao4lMCk528jXRykKJ3bfxq8jUopABT31KXSRn6NS7sVFjihNVM+Y5T24zr1FPIVt26I3aUoEkA9+2uCqaUuDKdShs1oQM0bVvpPAtizaDUKLKVIVUYaZcxTrQSpl4jBQPOE/7k6rK1QUU213PUmJVLeWG4zTSgoff8Ht/Op1239WbjjNqqMgKDLKW0hCQkAJAHYceNC8aprVNbW+nKErG7nxnnGlyG3vJcvIckHP8f4KNyz20QCxjqP4rlFq98KoZs5ptxmKuQQ4kZBK/PPtjx21U3NbopREMhKlgfOQex9taAhdK3uofT7/AMo6eUh2PBElXqOyn0bFKT9XJOQRuHccg6BKn0RvByUUyqI+pxbZWnCchSQcZyOMZxzqs97E5IwFweR3z86nS0dFByWyOD2x8qULduuOOfIwVcZOBquqaEUV9t1EMBQz3HjTz6c9OpUibLl1aKGIsMelIekfKncoHAB8nj9tK/qfDpiqu9Hp3KWyQCR3++q7XStcel4FSiAiLf5pTVmEhcl1aOQok8e+h2bTVBZJGD99HAYnQZKxCYSXHRt3LQFAZ+x17XBbjT0VpLURKNqcFwJ5Ufvpms9VUuEfvQC609gpZaWMqAcnjzxqslQwBx+2jGr0ZyI6WHmsKx/OqaXTu4KfxjxpgBDDNBDuU1t2HUKReHSW0yqB6D9NEhh+Q0jIWvcFBC/bgkhX3I8al1mQ5ULdj0gUeKw2zIW6hbKDuJICeSSf9I0c/Bn0Pi3xcL1o1iSmP6chKz6qcjaPlPB78Ej99D9etp63K1OtySfUMSU4zuAwCUqIz++Nc70q8huB6SHLJz9yaeNQt3hbe3Rhj7AUJMUc8fJru5S0+n9HI99EcOkFxO5ScY9hr2k0hIbPy+PbTCX3UEA2mg1ym7gfl51Hk0rCdwbOilVLUkkFGvC6SVEkI/IOrAkAqBlNBbkJQQQnODxqK7TFIPKNGTtFZS4d+AAMnOvU2dPqEN6bAhuuMxwPWdbbJSjPbJ8aw9xFEMk4FeSOSQ4UZqNY/V26LLpj1qR5CjT5K8uhP1oJKclJJ4+ka2DZLVgdROlbVDtKII9wohsKeDxG8Mn/AD4BI2naPPdWsxdOennSm511K27kulcCqlgKpUpxQ9FSwPpV7A++ovTq+Lw6IdUGJcSWmQuG56DjbUrc082T9IUONvn/AI0rana2msB1tjtlX4vG79x2/wDaYLO4udM2mcZjbjzinj1f6PXNEtfDtIYjts8+nETj1FEY3qz3JwNZJvGw566u4n0FbiTu419Ird6o2r18oaWnIiYr8mKlT0dXdteSCArGCMAY/wCNKq8ehtl2tMcl1LY8+SpSGkjsOcE/9aRrbULm0maKZfiHamiW1huI1dDxWGHOmU9tkPyIpSM5STqGKHBTIEea2VJB5GtFXzCob812AkIbUjgADHGgWo9OY7Sf1jrjYDhJQpRxxpktbidjlxig08MSjC81nbqPSKe3Wj/Twop9IbwrsFew0HzaeE8lPfTav+22WqissELUSd2DxjQRVKQGx8qPyddMsJA1qgz2pDvEK3LH519dunnRiPZfXiDc8OoxUU1x8IdUy6NqwrIBx3wSM6B/jNsG2aZ1fdlW5LbWJ0Rtx5pAyW1425J7HIAOmjYxrN8yqTb9UoEanKXT0h+ey8lTrxGcKScZRn2PnzpWdXKVKYvqo0559+U7EfLSJMiOW3HAnspSTnx57Ec65F/TyYuid3IGDjx710nV2zAo28Z/X2pVU+2JMJrZIVk9xrg6xDkLWww8lS0n5kA8jRo7NtiAwpF0SVNEK+YIQdwGq9u16ImOzWqO8l1qWne24MHI/wCD9jpvhugGEakEDrzS/Lb7gXYYJ+VCS6c5HUHkJ+dJyCR2OudJpEya86zGirce27m/TTnGOSSPbV7dM2FRkw0uOMqEuQWfkeSVIUMd0jkdxqM4HqK8qR6oZ9MEOlRxgeQdXBcJIp2HmqZt3jcFhxShvufX6ZWQuS84SlZJaSOMZ9tMzpz8RVmUmy5do120UuNPJBSyklG5eACSR3yB2++ll1F6rW69WZKItHTIUUFDD7rpGxefqwO478atrNtyFeVoR6o84gPeotC1NEDJB4PbQie3W/X02PGc9aKRTf2R3gVUXJRH59xuVSgRzGZcXuQ2CcIB8DXWHClMOIdlLKlA5yfHPfRk1bbkOElp9e5aBtzjwO2qmpNMxspTjPuPGjVnZpGB5FCLq7eQkY4o+HXyRYtowaBY4ALMlt5ySpeVhSQNwPH0nAI9hka6TPiakXWt2Rcqn23HUkrDaApJXwMjz7/zpRyWSpzcPOplOghLaHZLSi2VYCgNYk0PT2G5kyx79+awurXoOA3HjtVjWqgqq1FdVUVqbWCGyDhQOPOhK6KnV3VoVJdWG0AhAHkaNJUQrpbcVLSAVnd6iOVHuMaFrnp0tpKv1BJUgYIOpLeKFTtA6cVFNNKRknrzQLV5sV1agWjz/mPfQjVYSFLUWxx4zorqsBwun5cA6qJEEkH7edGIY1iHw0NkdpDzWvLB+KW9rXr0OpN1x55tbXpTQtsbkoOAQkqBwQBweccadHTfrT0wrFz1K5ruuWfOcl00x4s2SylTsde0JCl+OEgpBHP2GsvVG0ajCfUw7CIKDjKRqw6eyKjb9cbdMcPNKc2vMujhSc9jri6Tw+myrhdwwSPFdSaNyyk84OaPut/WO1oTkuzG6PFmul8LYrDBO5SMHIVu5UVcfg9u+l1Gvup0+lLRb0v/AA8ENtvEkNk8naNEd4dNl1J1+tNx0oU4srS0Owz4GfGltMo1VgTDGfWpKEqzwO+orW8WIARtgit5oC+d65BoaqIqqpSprkle71crKlHg50fdVevFq31ZdPt+NbyoU+PT249RloUNstaCT6pAAwo55P2Gh1+lSnt7CmS5nJScarUWstThbciFWOT8vYaIJqWcFjyPzVVrME4A4oErdLE1tamV5JOQfY6pqZeN22Sp1mkVd5lLowtKF8HTjh2HBfaSEIBJByPbQ/cnRhLzS5cTJOSSlQ7a2ttYEUmCaxNp5kTIFD1rfEHekScluoTjKaUseo2/yQnzg+NNinTqPdba36FN9cJA9RJGFJJ5wRpNW/02nTa81SGYpLrrwQkbfJONao6f/C3UunPTxd5Sn1LefdQlUb0+R3IP8aY7bW0jnRC3/LigdxpfqRMwHSl2/RH23Ni2SD7EauaRa1RlUaRLjxS4iMAp7YeQCcZx5AP8Z0aVyg0RgNvSZxafWfodSBzjjj+PxrzRK43aFX/Rwq9CccqLKmlNMvhRJIKcKT7j799GG1ZJIvhI3ePahY0x1k+LO3zS+juvtOBpvCcqHJAONV931CVP+R2GhWVY3oRjb/Gn51R6ET0Uin1i0LUHomIgyW2RvWF4PJH1DPck+4xxxpS3ZR61Zlddi16gNtnaU+m4nKT9xrW3vYL0BoSN3jIzxWJbSazOJQdv1xSlrFLbSokg5OqWRBSXDuIH50dVKmVCrOLMOEpz8J7aoa9Z1w0Vaf6tRZLBcA9NLjJG7PI/9aPRyDAVjzQhkJOQOK+lfxU/DzTVXM2enFkf4D6C4+7FbKxu85OcD8AaTUH4erjaeLrNGcSsKwpBbP8AbWtOiV5zKnVG00SptyUrOFpS8FA/YjPGnW3QrdrITOcpLaXQQTubwQR7++uKLok12zehIBz0I4x8iD+mK6h/qKQKokQnjrnmsCu9MJ8ajpZqNLWktpwoKTpe3TZtDZlrUI+1e3JCm+M6+md1dN7VuuCqPPpTW8NkNrQkAg447ayz1t6Ff0FMh5qlrKjnZhPnGhGqaZe6RIDL8St0I/Pir9nfW98pAGCOx/FZFbpkB2oKQ5BbbU2rAUrhK/tqxj2pa8qQp+tPMw1hISyMEpd57HGcHnPtgak3h0/uKbP/AEkeI6CFH6UEYOqef0lvNcb1XZDoWk7kJUrnOtreSHgsRXnVyOBXpd67Jst8xKdHMtfqAKLY+VQ8lKh3/OuUe2oVxRjPpAzv5LDn1t/Y++ulF6e1y9YZtp9paKgw5hlwpJ9XOePznU/p70tvqgXO8K3EfZEMFBTggLXgkDH7dtEi9hM2w4WqoFzGu5cmudk9B4NWvmImcoRGluBTkoJ4SnI5/OtnMdO2rdZgVKt1mNJgtsJERQQPTkYCRtxyO2SSeTu1nqk3TETV4dKVFTGUtwpkGQsJSnHPCjxp41S9alWbWVY1syI7UVhLf6mXJeAbYHOTvP8AqHAAz286llsrV1TEmfwKhW5uFZspj8mqjq58PfTe6KC7Vo8KNGU2hS1ORlggr5OMDkcax3UulMFfUVuO5MUhppe5DxPbHOONa2u2NVKBSlMUCVNkMuR0plPvpAaWvn6M4OPzpL1C3pcOovOymwXSFbVBOdufI/71pY288UpEDllPT81m5nieMGVQDUTqj1OrNm2221bF3PrdRGLLxaePJ5899DvTLqJROq9VpznVGC++mG2WnGwCQ8rOAT5z7/jXpUbcW+46mpI3kqyk9+NelvvtWe4h2nx0ZQ4CpJT3HnTFp2n3CpvHXnnoaDXt/AW2k8ccdRTerNsdGbepiq7SbPZSQz6qmxFUSkHt4IHP99KK7OtdlxnltsUKS4VEpfadOAMdsfcHVldvVKtVOkriQ3VRy4r/ABdijhQHYY8aUldil1TinkBSl87jotpmj78tdkk/9iaGX+rCMhbYAD2FfTe1PgzqHT+7UXJatwF6M1IC22ivDm0HI5Hn99Puh0+RTssKqLzzeMpTJBKk/bJ1CtaWzMbJizUOBBIWE5BB/BAP76vmySnn++hul6faxH14iefnkfT5e+aLXl1O/wDtv2+VedVdx04TlMtoajFS1FCvXZ3ZSe+PY41aaj1GK7LjlEd703ByheOx0VuohNAVxmqcTbJAaD698P3TisQZDDVDbZfeOQ+ngpP/AFoJY+Du3xUkzKrLalsDOWcFOD+f402Y9MqzVLdaqNS9V8kltxJIIGOBqPGl1OBGcDzO9RPClL57HQKXR9JkZXaDZx24+4HFEEvrxAVWTPv+M1k7qf03c6UXG5Kt+2W3S0slmSpsgd+/PfA/31VT+rw5XV7Tgxqi9HLzsh5IWXMA4wk8Jz/61qfqf0ypfUSkqnMtgzWo69iSTySOBrOVT+Fy8H6k2xVqTIbS4fmf2ZShOlG+0xrOUqyZU9CBnj+KN214J1BBwR1FI+5axbN0SRL9L0pTqgXGkNYQhWPA0QWv0pvrqJRAqgz5amow/wAJv1fkGMnsfHJ0Vv8ASGj9La+5Vbzt+XLisglpLUc7XecABXj8nTHoTFTdsaIbcguUlh0BSWW1J3ZcAyFecD/nWbRTI/pxnbjz+1YuJPTTe4z7UtbWoF2XPOYtepy1L/TIUpwOOhKUJQMq559j/Oqu+qXW4tYcRS6bMQzKQENMrQcqTjgcDkeR9tN+2enl4Wncypj8OO+AMu5SpaCnIzyPOrvrrU6bS7f/AFKKm1FfWgpSoqSTvxnA9iNMM+orZlSoDADH17mg8Nm90DklST+nYVmdfQq/6q4hX9CDKXRu3PvISEjPcjOf7Z1X3T0BlW/SHKtU7jhD0nQhxDIUoJ9yTjxnwNBV/dYep9r3K8+xXpYCuEoWtQBTnjH2Ol31P+IPqddDCI8utO7UIx6bR2p/cDv++rKanqbspVlA9v3qBtPsVBDBif8APFMWtWPSqdTnahIuultpwfSbmv8ApKUARhQye2Of20lbs6o2bDkriqrsJWxW0rbVuSr99ANzXLXZ29dSlur+XlS3CdLyvRW1rWsOg55I76MWupyoT6jbvpihtxp8LD4Bj61/RJHoRq8ZmNWFvJWyrcxIjultxP7juNXdEoJouRFqT7rSvqTJXuOffOvaIT6YBJOBxnU9ogpwBjVbTrSDAkxyMc9/5q7NcSOSvbxXtr9r9r920ZqrXhYBSQdQJjQIJx+dTVup7ajSNqknPtqCcAx1lTg5qllPvxcltwj8agSnqpIQSEuqB7nB51dqYjlRLo75BP2xquu+ZckWnoNqw0StqgH2lOYUUeQPzoHM/pRM7E4HYDJ+1EEw7hRxnueB96rabFcqrkmPJa9UNoBLK+x+bng9+NU9woj0+Utb1vtObAMteiR6ae+5I8du+plWqFah0t5VKbEV1xW4uuIO5IA4Bz986z71mvbqpRbmTUaqX429sNhyO4r03BnIWOfIxn8aA3N9CsigDnyen3olFayFDk0665W4Eq1v69HlyC00raWmlBSkKzwSPtwceQdYw+L3rDWLhqggJQ41FiI2RcnBWc/MtQAABJ8eO2tAWXcl2/p3WX3S4pwpVuWySl3I/wD1pQ9erfrM2c+0i3I8sFBcQtMTkI7c7e3PvoZNcPHcCQjj371aiCPGUB5rLNfviqyKYiTU2VrbQdiXHBnIz21CqNq1WpwUzaPDMhtxsLCmkZwD747aOLwgXNHt522avZjQiLWHEEp+dsDcBt9uSM/jVFRLZ6vWBSZF2dNHZSIzzKm5jbRStSRzwUkHgZznHfVxLkyLxgH9DVdo1j6nIpK31QaoylfqMEEDCgBoHl0OU7HVUm2VpS3wpvGc8d9ak6WVGL1IdnW51Ht6NMmuO+ozMGGHMEYKSBhJAPIOO5OfGqC//h1doNVcnUOnThGUopKS0HAoc9iO/wDHjUqak0bGNxz+lQtbK3xrX//Z\",this.emptyImage=new Image,this.emptyImage.onload=function(){i.samplers.empty=i.gl.createTexture(),i.bindTexture(i.samplers.empty,i.emptyImage,1,1)},this.emptyImage.src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=\"}var e,i,s;return e=t,(i=[{key:\"bindTexture\",value:function(t,e,i,s){if(this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,i,s,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.REPEAT),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.anisoExt){var r=this.gl.getParameter(this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this.gl.texParameterf(this.gl.TEXTURE_2D,this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,r)}}},{key:\"loadExtraImages\",value:function(t){var e=this;Object.keys(t).forEach(function(i){var s=t[i],r=s.data,a=s.width,h=s.height;if(!e.samplers[i]){var o=new Image;o.onload=function(){e.samplers[i]=e.gl.createTexture(),e.bindTexture(e.samplers[i],o,a,h)},o.src=r}})}},{key:\"getTexture\",value:function(t){var e=this.samplers[t];return e||this.samplers.clouds2}}])&&K(e.prototype,i),s&&K(e,s),t}();function Z(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var $=function(){function t(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.texsizeX=i.texsizeX,this.texsizeY=i.texsizeY,this.aspectx=i.aspectx,this.aspecty=i.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.buildPositions(),this.textTexture=this.gl.createTexture(),this.indexBuf=e.createBuffer(),this.positionVertexBuf=this.gl.createBuffer(),this.vertexBuf=this.gl.createBuffer(),this.canvas=document.createElement(\"canvas\"),this.canvas.width=this.texsizeX,this.canvas.height=this.texsizeY,this.context2D=this.canvas.getContext(\"2d\"),this.floatPrecision=_.getFragmentFloatPrecision(this.gl),this.createShader()}var e,i,s;return e=t,(i=[{key:\"generateTitleTexture\",value:function(t){this.context2D.clearRect(0,0,this.texsizeX,this.texsizeY),this.fontSize=Math.floor(this.texsizeX/256*16),this.fontSize=Math.max(this.fontSize,6),this.context2D.font=\"italic \".concat(this.fontSize,\"px Times New Roman\");var e=t,i=this.context2D.measureText(e).width;if(i>this.texsizeX){var s=this.texsizeX/i*.91;e=\"\".concat(e.substring(0,Math.floor(e.length*s)),\"...\"),i=this.context2D.measureText(e).width}this.context2D.fillStyle=\"#FFFFFF\",this.context2D.fillText(e,(this.texsizeX-i)/2,this.texsizeY/2);var r=new Uint8Array(this.context2D.getImageData(0,0,this.texsizeX,this.texsizeY).data.buffer);this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,!0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.textTexture),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.texsizeX,this.texsizeY,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,r),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.bindTexture(this.gl.TEXTURE_2D,null)}},{key:\"updateGlobals\",value:function(t){this.texsizeX=t.texsizeX,this.texsizeY=t.texsizeY,this.aspectx=t.aspectx,this.aspecty=t.aspecty,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.canvas.width=this.texsizeX,this.canvas.height=this.texsizeY}},{key:\"buildPositions\",value:function(){for(var t=[],e=0;e<8;e++)for(var i=e*(2/7)-1,s=0;s<16;s++){var r=s*(2/15)-1;t.push(r,-i,0)}for(var a=[],h=0;h<7;h++)for(var o=0;o<15;o++){var n=o+16*h,l=o+16*(h+1),m=o+1+16*(h+1),u=o+1+16*h;a.push(n,l,u),a.push(l,m,u)}this.vertices=new Float32Array(t),this.indices=new Uint16Array(a)}},{key:\"createShader\",value:function(){this.shaderProgram=this.gl.createProgram();var t=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(t,\"#version 300 es\\n       const vec2 halfmad = vec2(0.5);\\n       in vec2 aPos;\\n       in vec2 aUv;\\n       out vec2 uv_orig;\\n       out vec2 uv;\\n       void main(void) {\\n         gl_Position = vec4(aPos, 0.0, 1.0);\\n         uv_orig = aPos * halfmad + halfmad;\\n         uv = aUv;\\n       }\"),this.gl.compileShader(t);var e=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(e,\"#version 300 es\\n       precision \".concat(this.floatPrecision,\" float;\\n       precision highp int;\\n       precision mediump sampler2D;\\n\\n       in vec2 uv_orig;\\n       in vec2 uv;\\n       out vec4 fragColor;\\n       uniform sampler2D uTexture;\\n       uniform float textColor;\\n\\n       void main(void) {\\n         fragColor = texture(uTexture, uv) * vec4(textColor);\\n       }\")),this.gl.compileShader(e),this.gl.attachShader(this.shaderProgram,t),this.gl.attachShader(this.shaderProgram,e),this.gl.linkProgram(this.shaderProgram),this.positionLocation=this.gl.getAttribLocation(this.shaderProgram,\"aPos\"),this.uvLocation=this.gl.getAttribLocation(this.shaderProgram,\"aUv\"),this.textureLoc=this.gl.getUniformLocation(this.shaderProgram,\"uTexture\"),this.textColorLoc=this.gl.getUniformLocation(this.shaderProgram,\"textColor\")}},{key:\"generateUvs\",value:function(t,e,i){for(var s=[],r=0;r<8;r++)for(var a=0;a<16;a++){var h=2*(a/15)-1,o=2*(.75*(r/7-.5)+.5)-1;t>=1&&(o+=1/this.texsizeY),s.push(h,e?o:-o)}for(var n=Math.max(0,1-1.5*t),l=1.3*Math.pow(n,1.8),m=0;m<8;m++)for(var u=0;u<16;u++){var g=16*m+u;s[g]+=.07*l*Math.sin(.31*i.time+.39*s[g]-1.94*s[g+1]),s[g]+=.044*l*Math.sin(.81*i.time-1.91*s[g]+.27*s[g+1]),s[g]+=.061*l*Math.sin(1.31*i.time+.61*s[g]+.74*s[g+1]),s[g+1]+=.061*l*Math.sin(.37*i.time+1.83*s[g]+.69*s[g+1]),s[g+1]+=.07*l*Math.sin(.67*i.time+.42*s[g]-1.39*s[g+1]),s[g+1]+=.087*l*Math.sin(1.07*i.time+3.55*s[g]+.89*s[g+1])}for(var c=1.01/(Math.pow(t,.21)+.01),A=0;A<s.length/2;A++)s[2*A]*=c,s[2*A+1]*=c*this.invAspecty,s[2*A]=(s[2*A]+1)/2,s[2*A+1]=(s[2*A+1]+1)/2;return new Float32Array(s)}},{key:\"renderTitle\",value:function(t,e,i){this.gl.useProgram(this.shaderProgram);var s=this.generateUvs(t,e,i);this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.indexBuf),this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,this.indices,this.gl.STATIC_DRAW),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionVertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.vertices,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.positionLocation,3,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.positionLocation),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuf),this.gl.bufferData(this.gl.ARRAY_BUFFER,s,this.gl.STATIC_DRAW),this.gl.vertexAttribPointer(this.uvLocation,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.uvLocation),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.textTexture),this.gl.uniform1i(this.textureLoc,0),this.gl.uniform1f(this.textColorLoc,Math.pow(t,.3)),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.gl.drawElements(this.gl.TRIANGLES,this.indices.length,this.gl.UNSIGNED_SHORT,0)}}])&&Z(e.prototype,i),s&&Z(e,s),t}();function tt(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var et=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.mesh_width=e.mesh_width,this.mesh_height=e.mesh_height,this.aspectx=e.aspectx,this.aspecty=e.aspecty,this.vertInfoA=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)),this.vertInfoC=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)),this.createBlendPattern()}var e,i,s;return e=t,s=[{key:\"resizeMatrixValues\",value:function(t,e,i,s,r){for(var a=new Float32Array((s+1)*(r+1)),h=0,o=0;o<r+1;o++)for(var n=0;n<s+1;n++){var l=n/r,m=o/s;l*=e+1,m*=i+1,l=Math.clamp(l,0,e-1),m=Math.clamp(m,0,i-1);var u=Math.floor(l),g=Math.floor(m),c=l-u,A=m-g,f=t[g*(e+1)+u],d=t[g*(e+1)+(u+1)],v=t[(g+1)*(e+1)+u],p=t[(g+1)*(e+1)+(u+1)];a[h]=f*(1-c)*(1-A)+d*c*(1-A)+v*(1-c)*A+p*c*A,h+=1}return a}}],(i=[{key:\"updateGlobals\",value:function(e){var i=this.mesh_width,s=this.mesh_height;this.mesh_width=e.mesh_width,this.mesh_height=e.mesh_height,this.aspectx=e.aspectx,this.aspecty=e.aspecty,this.mesh_width===i&&this.mesh_height===s||(this.vertInfoA=t.resizeMatrixValues(this.vertInfoA,i,s,this.mesh_width,this.mesh_height),this.vertInfoC=t.resizeMatrixValues(this.vertInfoC,i,s,this.mesh_width,this.mesh_height))}},{key:\"genPlasma\",value:function(t,e,i,s,r){var a=Math.floor((t+e)/2),h=Math.floor((i+s)/2),o=this.vertInfoC[i*(this.mesh_width+1)+t],n=this.vertInfoC[i*(this.mesh_width+1)+e],l=this.vertInfoC[s*(this.mesh_width+1)+t],m=this.vertInfoC[s*(this.mesh_width+1)+e];s-i>=2&&(0===t&&(this.vertInfoC[h*(this.mesh_width+1)+t]=.5*(o+l)+(2*Math.random()-1)*r*this.aspecty),this.vertInfoC[h*(this.mesh_width+1)+e]=.5*(n+m)+(2*Math.random()-1)*r*this.aspecty),e-t>=2&&(0===i&&(this.vertInfoC[i*(this.mesh_width+1)+a]=.5*(o+n)+(2*Math.random()-1)*r*this.aspectx),this.vertInfoC[s*(this.mesh_width+1)+a]=.5*(l+m)+(2*Math.random()-1)*r*this.aspectx),s-i>=2&&e-t>=2&&(o=this.vertInfoC[h*(this.mesh_width+1)+t],n=this.vertInfoC[h*(this.mesh_width+1)+e],l=this.vertInfoC[i*(this.mesh_width+1)+a],m=this.vertInfoC[s*(this.mesh_width+1)+a],this.vertInfoC[h*(this.mesh_width+1)+a]=.25*(l+m+o+n)+(2*Math.random()-1)*r,this.genPlasma(t,a,i,h,.5*r),this.genPlasma(a,e,i,h,.5*r),this.genPlasma(t,a,h,s,.5*r),this.genPlasma(a,e,h,s,.5*r))}},{key:\"createBlendPattern\",value:function(){var t=1+Math.floor(3*Math.random());if(0===t)for(var e=0,i=0;i<=this.mesh_height;i++)for(var s=0;s<=this.mesh_width;s++)this.vertInfoA[e]=1,this.vertInfoC[e]=0,e+=1;else if(1===t)for(var r=6.28*Math.random(),a=Math.cos(r),h=Math.sin(r),o=.1+.2*Math.random(),n=1/o,l=0,m=0;m<=this.mesh_height;m++)for(var u=m/this.mesh_height*this.aspecty,g=0;g<=this.mesh_width;g++){var c=(g/this.mesh_width*this.aspectx-.5)*a+(u-.5)*h+.5;c=(c-.5)/Math.sqrt(2)+.5,this.vertInfoA[l]=n*(1+o),this.vertInfoC[l]=n*c-n,l+=1}else if(2===t){var A=.12+.13*Math.random(),f=1/A;this.vertInfoC[0]=Math.random(),this.vertInfoC[this.mesh_width]=Math.random(),this.vertInfoC[this.mesh_height*(this.mesh_width+1)]=Math.random(),this.vertInfoC[this.mesh_height*(this.mesh_width+1)+this.mesh_width]=Math.random(),this.genPlasma(0,this.mesh_width,0,this.mesh_height,.25);for(var d=this.vertInfoC[0],v=this.vertInfoC[0],p=0,_=0;_<=this.mesh_height;_++)for(var x=0;x<=this.mesh_width;x++)d>this.vertInfoC[p]&&(d=this.vertInfoC[p]),v<this.vertInfoC[p]&&(v=this.vertInfoC[p]),p+=1;var b=1/(v-d);p=0;for(var T=0;T<=this.mesh_height;T++)for(var E=0;E<=this.mesh_width;E++){var P=(this.vertInfoC[p]-d)*b;this.vertInfoA[p]=f*(1+A),this.vertInfoC[p]=f*P-f,p+=1}}else if(3===t)for(var R=.02+.14*Math.random()+.34*Math.random(),L=1/R,S=2*Math.floor(2*Math.random())-1,y=0,w=0;w<=this.mesh_height;w++)for(var U=(w/this.mesh_height-.5)*this.aspecty,M=0;M<=this.mesh_width;M++){var F=(M/this.mesh_width-.5)*this.aspectx,q=1.41421*Math.sqrt(F*F+U*U);-1===S&&(q=1-q),this.vertInfoA[y]=L*(1+R),this.vertInfoC[y]=L*q-L,y+=1}}}])&&tt(e.prototype,i),s&&tt(e,s),t}();function it(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var st=function(){function t(e,i,s){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.gl=e,this.audio=i,this.frameNum=0,this.fps=30,this.time=0,this.presetTime=0,this.lastTime=performance.now(),this.timeHist=[0],this.timeHistMax=120,this.blending=!1,this.blendStartTime=0,this.blendProgress=0,this.blendDuration=0,this.width=s.width||1200,this.height=s.height||900,this.mesh_width=s.meshWidth||48,this.mesh_height=s.meshHeight||36,this.pixelRatio=s.pixelRatio||window.devicePixelRatio||1,this.textureRatio=s.textureRatio||1,this.outputFXAA=s.outputFXAA||!1,this.texsizeX=this.width*this.pixelRatio*this.textureRatio,this.texsizeY=this.height*this.pixelRatio*this.textureRatio,this.aspectx=this.texsizeY>this.texsizeX?this.texsizeX/this.texsizeY:1,this.aspecty=this.texsizeX>this.texsizeY?this.texsizeY/this.texsizeX:1,this.invAspectx=1/this.aspectx,this.invAspecty=1/this.aspecty,this.qs=c.range(1,33).map(function(t){return\"q\".concat(t)}),this.ts=c.range(1,9).map(function(t){return\"t\".concat(t)}),this.regs=c.range(0,100).map(function(t){return t<10?\"reg0\".concat(t):\"reg\".concat(t)}),this.blurRatios=[[.5,.25],[.125,.125],[.0625,.0625]],this.audioLevels=new n(this.audio),this.prevFrameBuffer=this.gl.createFramebuffer(),this.targetFrameBuffer=this.gl.createFramebuffer(),this.prevTexture=this.gl.createTexture(),this.targetTexture=this.gl.createTexture(),this.compFrameBuffer=this.gl.createFramebuffer(),this.compTexture=this.gl.createTexture(),this.anisoExt=this.gl.getExtension(\"EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"MOZ_EXT_texture_filter_anisotropic\")||this.gl.getExtension(\"WEBKIT_EXT_texture_filter_anisotropic\"),this.bindFrameBufferTexture(this.prevFrameBuffer,this.prevTexture),this.bindFrameBufferTexture(this.targetFrameBuffer,this.targetTexture),this.bindFrameBufferTexture(this.compFrameBuffer,this.compTexture);var r={pixelRatio:this.pixelRatio,textureRatio:this.textureRatio,texsizeX:this.texsizeX,texsizeY:this.texsizeY,mesh_width:this.mesh_width,mesh_height:this.mesh_height,aspectx:this.aspectx,aspecty:this.aspecty};this.noise=new j(e),this.image=new J(e),this.warpShader=new B(e,this.noise,this.image,r),this.compShader=new D(e,this.noise,this.image,r),this.outputShader=new I(e,r),this.prevWarpShader=new B(e,this.noise,this.image,r),this.prevCompShader=new D(e,this.noise,this.image,r),this.numBlurPasses=0,this.blurShader1=new G(0,this.blurRatios,e,r),this.blurShader2=new G(1,this.blurRatios,e,r),this.blurShader3=new G(2,this.blurRatios,e,r),this.blurTexture1=this.blurShader1.blurVerticalTexture,this.blurTexture2=this.blurShader2.blurVerticalTexture,this.blurTexture3=this.blurShader3.blurVerticalTexture,this.basicWaveform=new E(e,r),this.customWaveforms=c.range(4).map(function(t){return new R(t,e,r)}),this.customShapes=c.range(4).map(function(t){return new S(t,e,r)}),this.prevCustomWaveforms=c.range(4).map(function(t){return new R(t,e,r)}),this.prevCustomShapes=c.range(4).map(function(t){return new S(t,e,r)}),this.darkenCenter=new M(e,r),this.innerBorder=new w(e,r),this.outerBorder=new w(e,r),this.motionVectors=new q(e,r),this.titleText=new $(e,r),this.blendPattern=new et(r),this.resampleShader=new k(e),this.supertext={startTime:-1},this.warpUVs=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)*2),this.warpColor=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)*4),this.gl.clearColor(0,0,0,1),this.blankPreset=m.a;var a={frame:0,time:0,fps:45,bass:1,bass_att:1,mid:1,mid_att:1,treb:1,treb_att:1};this.preset=m.a,this.prevPreset=this.preset,this.presetEquationRunner=new f(this.preset,a,r),this.prevPresetEquationRunner=new f(this.prevPreset,a,r),this.regVars=this.presetEquationRunner.mdVSRegs}var e,i,s;return e=t,s=[{key:\"getHighestBlur\",value:function(t){return/sampler_blur3/.test(t)?3:/sampler_blur2/.test(t)?2:/sampler_blur1/.test(t)?1:0}},{key:\"mixFrameEquations\",value:function(t,e,i){var s=.5-.5*Math.cos(t*Math.PI),r=1-s,a=c.cloneVars(e);return a.decay=s*e.decay+r*i.decay,a.wave_a=s*e.wave_a+r*i.wave_a,a.wave_r=s*e.wave_r+r*i.wave_r,a.wave_g=s*e.wave_g+r*i.wave_g,a.wave_b=s*e.wave_b+r*i.wave_b,a.wave_x=s*e.wave_x+r*i.wave_x,a.wave_y=s*e.wave_y+r*i.wave_y,a.wave_mystery=s*e.wave_mystery+r*i.wave_mystery,a.ob_size=s*e.ob_size+r*i.ob_size,a.ob_r=s*e.ob_r+r*i.ob_r,a.ob_g=s*e.ob_g+r*i.ob_g,a.ob_b=s*e.ob_b+r*i.ob_b,a.ob_a=s*e.ob_a+r*i.ob_a,a.ib_size=s*e.ib_size+r*i.ib_size,a.ib_r=s*e.ib_r+r*i.ib_r,a.ib_g=s*e.ib_g+r*i.ib_g,a.ib_b=s*e.ib_b+r*i.ib_b,a.ib_a=s*e.ib_a+r*i.ib_a,a.mv_x=s*e.mv_x+r*i.mv_x,a.mv_y=s*e.mv_y+r*i.mv_y,a.mv_dx=s*e.mv_dx+r*i.mv_dx,a.mv_dy=s*e.mv_dy+r*i.mv_dy,a.mv_l=s*e.mv_l+r*i.mv_l,a.mv_r=s*e.mv_r+r*i.mv_r,a.mv_g=s*e.mv_g+r*i.mv_g,a.mv_b=s*e.mv_b+r*i.mv_b,a.mv_a=s*e.mv_a+r*i.mv_a,a.echo_zoom=s*e.echo_zoom+r*i.echo_zoom,a.echo_alpha=s*e.echo_alpha+r*i.echo_alpha,a.echo_orient=s*e.echo_orient+r*i.echo_orient,a.wave_dots=s<.5?i.wave_dots:e.wave_dots,a.wave_thick=s<.5?i.wave_thick:e.wave_thick,a.additivewave=s<.5?i.additivewave:e.additivewave,a.wave_brighten=s<.5?i.wave_brighten:e.wave_brighten,a.darken_center=s<.5?i.darken_center:e.darken_center,a.gammaadj=s<.5?i.gammaadj:e.gammaadj,a.wrap=s<.5?i.wrap:e.wrap,a.invert=s<.5?i.invert:e.invert,a.brighten=s<.5?i.brighten:e.brighten,a.darken=s<.5?i.darken:e.darken,a.solarize=s<.5?i.brighten:e.solarize,a.b1n=s*e.b1n+r*i.b1n,a.b2n=s*e.b2n+r*i.b2n,a.b3n=s*e.b3n+r*i.b3n,a.b1x=s*e.b1x+r*i.b1x,a.b2x=s*e.b2x+r*i.b2x,a.b3x=s*e.b3x+r*i.b3x,a.b1ed=s*e.b1ed+r*i.b1ed,a}},{key:\"getBlurValues\",value:function(t){var e=t.b1n,i=t.b2n,s=t.b3n,r=t.b1x,a=t.b2x,h=t.b3x;if(r-e<.1){var o=.5*(e+r);e=o-.05,r=o-.05}if((a=Math.min(r,a))-(i=Math.max(e,i))<.1){var n=.5*(i+a);i=n-.05,a=n-.05}if((h=Math.min(a,h))-(s=Math.max(i,s))<.1){var l=.5*(s+h);s=l-.05,h=l-.05}return{blurMins:[e,i,s],blurMaxs:[r,a,h]}}}],(i=[{key:\"loadPreset\",value:function(e,i){this.blendPattern.createBlendPattern(),this.blending=!0,this.blendStartTime=this.time,this.blendDuration=i,this.blendProgress=0,this.prevPresetEquationRunner=this.presetEquationRunner,this.prevPreset=this.preset,this.preset=e,this.preset.baseVals.old_wave_mode=this.prevPreset.baseVals.wave_mode,this.presetTime=this.time;var s={frame:this.frameNum,time:this.time,fps:this.fps,bass:this.audioLevels.bass,bass_att:this.audioLevels.bass_att,mid:this.audioLevels.mid,mid_att:this.audioLevels.mid_att,treb:this.audioLevels.treb,treb_att:this.audioLevels.treb_att},r={pixelRatio:this.pixelRatio,textureRatio:this.textureRatio,texsizeX:this.texsizeX,texsizeY:this.texsizeY,mesh_width:this.mesh_width,mesh_height:this.mesh_height,aspectx:this.aspectx,aspecty:this.aspecty};this.presetEquationRunner=new f(this.preset,s,r),this.regVars=this.presetEquationRunner.mdVSRegs;var a=this.prevWarpShader;this.prevWarpShader=this.warpShader,this.warpShader=a;var h=this.prevCompShader;this.prevCompShader=this.compShader,this.compShader=h;var o=this.preset.warp.trim(),n=this.preset.comp.trim();this.warpShader.updateShader(o),this.compShader.updateShader(n),0===o.length?this.numBlurPasses=0:this.numBlurPasses=t.getHighestBlur(o),0!==n.length&&(this.numBlurPasses=Math.max(this.numBlurPasses,t.getHighestBlur(n)))}},{key:\"loadExtraImages\",value:function(t){this.image.loadExtraImages(t)}},{key:\"setRendererSize\",value:function(t,e,i){var s=this.texsizeX,r=this.texsizeY;if(this.width=t,this.height=e,this.mesh_width=i.meshWidth||this.mesh_width,this.mesh_height=i.meshHeight||this.mesh_height,this.pixelRatio=i.pixelRatio||this.pixelRatio,this.textureRatio=i.textureRatio||this.textureRatio,this.texsizeX=t*this.pixelRatio*this.textureRatio,this.texsizeY=e*this.pixelRatio*this.textureRatio,this.aspectx=this.texsizeY>this.texsizeX?this.texsizeX/this.texsizeY:1,this.aspecty=this.texsizeX>this.texsizeY?this.texsizeY/this.texsizeX:1,this.texsizeX!==s||this.texsizeY!==r){var a=this.gl.createTexture();this.bindFrameBufferTexture(this.targetFrameBuffer,a),this.bindFrambufferAndSetViewport(this.targetFrameBuffer,this.texsizeX,this.texsizeY),this.resampleShader.renderQuadTexture(this.targetTexture),this.targetTexture=a,this.bindFrameBufferTexture(this.prevFrameBuffer,this.prevTexture),this.bindFrameBufferTexture(this.compFrameBuffer,this.compTexture)}this.updateGlobals(),this.frameNum>0&&this.renderToScreen()}},{key:\"setInternalMeshSize\",value:function(t,e){this.mesh_width=t,this.mesh_height=e,this.updateGlobals()}},{key:\"setOutputAA\",value:function(t){this.outputFXAA=t}},{key:\"updateGlobals\",value:function(){var t={pixelRatio:this.pixelRatio,textureRatio:this.textureRatio,texsizeX:this.texsizeX,texsizeY:this.texsizeY,mesh_width:this.mesh_width,mesh_height:this.mesh_height,aspectx:this.aspectx,aspecty:this.aspecty};this.presetEquationRunner.updateGlobals(t),this.prevPresetEquationRunner.updateGlobals(t),this.warpShader.updateGlobals(t),this.prevWarpShader.updateGlobals(t),this.compShader.updateGlobals(t),this.prevCompShader.updateGlobals(t),this.outputShader.updateGlobals(t),this.blurShader1.updateGlobals(t),this.blurShader2.updateGlobals(t),this.blurShader3.updateGlobals(t),this.basicWaveform.updateGlobals(t),this.customWaveforms.forEach(function(e){return e.updateGlobals(t)}),this.customShapes.forEach(function(e){return e.updateGlobals(t)}),this.prevCustomWaveforms.forEach(function(e){return e.updateGlobals(t)}),this.prevCustomShapes.forEach(function(e){return e.updateGlobals(t)}),this.darkenCenter.updateGlobals(t),this.innerBorder.updateGlobals(t),this.outerBorder.updateGlobals(t),this.motionVectors.updateGlobals(t),this.titleText.updateGlobals(t),this.blendPattern.updateGlobals(t),this.warpUVs=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)*2),this.warpColor=new Float32Array((this.mesh_width+1)*(this.mesh_height+1)*4)}},{key:\"calcTimeAndFPS\",value:function(t){var e;if(t)e=t;else{var i=performance.now();((e=(i-this.lastTime)/1e3)>1||e<0||this.frame<2)&&(e=1/30),this.lastTime=i}this.time+=1/this.fps,this.blending&&(this.blendProgress=(this.time-this.blendStartTime)/this.blendDuration,this.blendProgress>1&&(this.blending=!1));var s=this.timeHist[this.timeHist.length-1]+e;this.timeHist.push(s),this.timeHist.length>this.timeHistMax&&this.timeHist.shift();var r=this.timeHist.length/(s-this.timeHist[0]);if(Math.abs(r-this.fps)>3&&this.frame>this.timeHistMax)this.fps=r;else{this.fps=.93*this.fps+(1-.93)*r}}},{key:\"runPixelEquations\",value:function(t,e,i,s){for(var r=this.mesh_width,a=this.mesh_height,h=r+1,o=a+1,n=this.time*e.warpanimspeed,l=1/e.warpscale,m=11.68+4*Math.cos(1.413*n+10),u=8.77+3*Math.cos(1.113*n+7),g=10.54+3*Math.cos(1.233*n+3),A=11.49+4*Math.cos(.933*n+5),f=0/this.texsizeX,d=0/this.texsizeY,v=this.aspectx,p=this.aspecty,_=c.cloneVars(e),x=0,b=0,T=0;T<o;T++)for(var E=0;E<h;E++){var P=E/r*2-1,R=T/a*2-1,L=Math.sqrt(P*P*v*v+R*R*p*p);if(i){var S=void 0;S=T===a/2&&E===r/2?0:c.atan2(R*p,P*v),_.x=.5*P*v+.5,_.y=-.5*R*p+.5,_.rad=L,_.ang=S,_.zoom=e.zoom,_.zoomexp=e.zoomexp,_.rot=e.rot,_.warp=e.warp,_.cx=e.cx,_.cy=e.cy,_.dx=e.dx,_.dy=e.dy,_.sx=e.sx,_.sy=e.sy,_=t.pixel_eqs(_)}var y=_.warp,w=_.zoom,U=_.zoomexp,M=_.cx,F=_.cy,q=_.sx,z=_.sy,B=_.dx,C=_.dy,D=_.rot,V=1/Math.pow(w,Math.pow(U,2*L-1)),I=.5*P*v*V+.5,X=.5*-R*p*V+.5;I=(I-M)/q+M,X=(X-F)/z+F,0!==y&&(I+=.0035*y*Math.sin(.333*n+l*(P*m-R*A)),X+=.0035*y*Math.cos(.375*n-l*(P*g+R*u)),I+=.0035*y*Math.cos(.753*n-l*(P*u-R*g)),X+=.0035*y*Math.sin(.825*n+l*(P*m+R*A)));var k=I-M,N=X-F,O=Math.cos(D),W=Math.sin(D);if(I=k*O-N*W+M,X=k*W+N*O+F,I=((I-=B)-.5)/v+.5,X=((X-=C)-.5)/p+.5,I+=f,X+=d,s){var Q=this.blendPattern.vertInfoA[x/2]*this.blendProgress+this.blendPattern.vertInfoC[x/2];Q=Math.clamp(Q,0,1),this.warpUVs[x]=this.warpUVs[x]*Q+I*(1-Q),this.warpUVs[x+1]=this.warpUVs[x+1]*Q+X*(1-Q),this.warpColor[b+0]=1,this.warpColor[b+1]=1,this.warpColor[b+2]=1,this.warpColor[b+3]=Q}else this.warpUVs[x]=I,this.warpUVs[x+1]=X,this.warpColor[b+0]=1,this.warpColor[b+1]=1,this.warpColor[b+2]=1,this.warpColor[b+3]=1;x+=2,b+=4}this.mdVSVertex=_}},{key:\"bindFrambufferAndSetViewport\",value:function(t,e,i){this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,t),this.gl.viewport(0,0,e,i)}},{key:\"bindFrameBufferTexture\",value:function(t,e){if(this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.texsizeX,this.texsizeY,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,new Uint8Array(this.texsizeX*this.texsizeY*4)),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR_MIPMAP_LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.anisoExt){var i=this.gl.getParameter(this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this.gl.texParameterf(this.gl.TEXTURE_2D,this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,i)}this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,t),this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,this.gl.TEXTURE_2D,e,0)}},{key:\"render\",value:function(){var e=this,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},s=i.audioLevels,r=i.elapsedTime;this.calcTimeAndFPS(r),this.frameNum+=1,s?this.audio.updateAudio(s.timeByteArray,s.timeByteArrayL,s.timeByteArrayR):this.audio.sampleAudio(),this.audioLevels.updateAudioLevels(this.fps,this.frameNum);var a={frame:this.frameNum,time:this.time,fps:this.fps,bass:this.audioLevels.bass,bass_att:this.audioLevels.bass_att,mid:this.audioLevels.mid,mid_att:this.audioLevels.mid_att,treb:this.audioLevels.treb,treb_att:this.audioLevels.treb_att,meshx:this.mesh_width,meshy:this.mesh_height,aspectx:this.invAspectx,aspecty:this.invAspecty,pixelsx:this.texsizeX,pixelsy:this.texsizeY},h=Object.assign({},a);h.gmegabuf=this.prevPresetEquationRunner.gmegabuf,a.gmegabuf=this.presetEquationRunner.gmegabuf,Object.assign(a,this.regVars),this.presetEquationRunner.runFrameEquations(a);var o,n=this.presetEquationRunner.mdVSFrame;this.runPixelEquations(this.presetEquationRunner.preset,n,this.presetEquationRunner.runVertEQs,!1),Object.assign(this.regVars,c.pick(this.mdVSVertex,this.regs)),Object.assign(a,this.regVars),this.blending?(this.prevPresetEquationRunner.runFrameEquations(h),this.runPixelEquations(this.prevPresetEquationRunner.preset,this.prevPresetEquationRunner.mdVSFrame,this.prevPresetEquationRunner.runVertEQs,!0),o=t.mixFrameEquations(this.blendProgress,n,this.prevPresetEquationRunner.mdVSFrame)):o=n;var l=this.targetTexture;this.targetTexture=this.prevTexture,this.prevTexture=l;var m=this.targetFrameBuffer;this.targetFrameBuffer=this.prevFrameBuffer,this.prevFrameBuffer=m,this.gl.bindTexture(this.gl.TEXTURE_2D,this.prevTexture),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.bindFrambufferAndSetViewport(this.targetFrameBuffer,this.texsizeX,this.texsizeY),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.enable(this.gl.BLEND),this.gl.blendEquation(this.gl.FUNC_ADD),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA);var u=t.getBlurValues(o),g=u.blurMins,A=u.blurMaxs;this.blending?(this.prevWarpShader.renderQuadTexture(!1,this.prevTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,g,A,this.prevPresetEquationRunner.mdVSFrame,this.warpUVs,this.warpColor),this.warpShader.renderQuadTexture(!0,this.prevTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,g,A,o,this.warpUVs,this.warpColor)):this.warpShader.renderQuadTexture(!1,this.prevTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,g,A,n,this.warpUVs,this.warpColor),this.numBlurPasses>0&&(this.blurShader1.renderBlurTexture(this.targetTexture,n,g,A),this.numBlurPasses>1&&(this.blurShader2.renderBlurTexture(this.blurTexture1,n,g,A),this.numBlurPasses>2&&this.blurShader3.renderBlurTexture(this.blurTexture2,n,g,A)),this.bindFrambufferAndSetViewport(this.targetFrameBuffer,this.texsizeX,this.texsizeY)),this.motionVectors.drawMotionVectors(o,this.warpUVs),this.preset.shapes&&this.preset.shapes.length>0&&this.customShapes.forEach(function(t,i){t.drawCustomShape(e.blending?e.blendProgress:1,a,e.presetEquationRunner,e.preset.shapes[i],e.prevTexture)}),this.preset.waves&&this.preset.waves.length>0&&this.customWaveforms.forEach(function(t,i){t.drawCustomWaveform(e.blending?e.blendProgress:1,e.audio.timeArrayL,e.audio.timeArrayR,e.audio.freqArrayL,e.audio.freqArrayR,a,e.presetEquationRunner,e.preset.waves[i])}),this.blending&&(this.prevPreset.shapes&&this.prevPreset.shapes.length>0&&this.prevCustomShapes.forEach(function(t,i){t.drawCustomShape(1-e.blendProgress,h,e.prevPresetEquationRunner,e.prevPreset.shapes[i],e.prevTexture)}),this.prevPreset.waves&&this.prevPreset.waves.length>0&&this.prevCustomWaveforms.forEach(function(t,i){t.drawCustomWaveform(1-e.blendProgress,e.audio.timeArrayL,e.audio.timeArrayR,e.audio.freqArrayL,e.audio.freqArrayR,h,e.prevPresetEquationRunner,e.prevPreset.waves[i])})),this.basicWaveform.drawBasicWaveform(this.blending,this.blendProgress,this.audio.timeArrayL,this.audio.timeArrayR,o),this.darkenCenter.drawDarkenCenter(o);var f=[o.ob_r,o.ob_g,o.ob_b,o.ob_a];this.outerBorder.drawBorder(f,o.ob_size,0);var d=[o.ib_r,o.ib_g,o.ib_b,o.ib_a];if(this.innerBorder.drawBorder(d,o.ib_size,o.ob_size),this.supertext.startTime>=0){var v=(this.time-this.supertext.startTime)/this.supertext.duration;v>=1&&this.titleText.renderTitle(v,!0,a)}this.globalVars=a,this.mdVSFrame=n,this.mdVSFrameMixed=o,this.renderToScreen()}},{key:\"renderToScreen\",value:function(){this.outputFXAA?this.bindFrambufferAndSetViewport(this.compFrameBuffer,this.texsizeX,this.texsizeY):this.bindFrambufferAndSetViewport(null,this.width,this.height),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.enable(this.gl.BLEND),this.gl.blendEquation(this.gl.FUNC_ADD),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA);var e=t.getBlurValues(this.mdVSFrameMixed),i=e.blurMins,s=e.blurMaxs;if(this.blending?(this.prevCompShader.renderQuadTexture(!1,this.targetTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,i,s,this.prevPresetEquationRunner.mdVSFrame,this.warpColor),this.compShader.renderQuadTexture(!0,this.targetTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,i,s,this.mdVSFrameMixed,this.warpColor)):this.compShader.renderQuadTexture(!1,this.targetTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,i,s,this.mdVSFrame,this.warpColor),this.supertext.startTime>=0){var r=(this.time-this.supertext.startTime)/this.supertext.duration;this.titleText.renderTitle(r,!1,this.globalVars),r>=1&&(this.supertext.startTime=-1)}this.outputFXAA&&(this.gl.bindTexture(this.gl.TEXTURE_2D,this.compTexture),this.gl.generateMipmap(this.gl.TEXTURE_2D),this.bindFrambufferAndSetViewport(null,this.width,this.height),this.outputShader.renderQuadTexture(this.compTexture))}},{key:\"launchSongTitleAnim\",value:function(t){this.supertext={startTime:this.time,duration:1.7},this.titleText.generateTitleTexture(t)}},{key:\"toDataURL\",value:function(){var e=this,i=new Uint8Array(this.texsizeX*this.texsizeY*4),s=this.gl.createFramebuffer(),r=this.gl.createTexture();this.bindFrameBufferTexture(s,r);var a=t.getBlurValues(this.mdVSFrameMixed),h=a.blurMins,o=a.blurMaxs;this.compShader.renderQuadTexture(!1,this.targetTexture,this.blurTexture1,this.blurTexture2,this.blurTexture3,h,o,this.mdVSFrame,this.warpColor),this.gl.readPixels(0,0,this.texsizeX,this.texsizeY,this.gl.RGBA,this.gl.UNSIGNED_BYTE,i),Array.from({length:this.texsizeY},function(t,s){return i.slice(s*e.texsizeX*4,(s+1)*e.texsizeX*4)}).forEach(function(t,s){return i.set(t,(e.texsizeY-s-1)*e.texsizeX*4)});var n=document.createElement(\"canvas\");n.width=this.texsizeX,n.height=this.texsizeY;var l=n.getContext(\"2d\"),m=l.createImageData(this.texsizeX,this.texsizeY);return m.data.set(i),l.putImageData(m,0,0),this.gl.deleteTexture(r),this.gl.deleteFramebuffer(s),n.toDataURL()}},{key:\"warpBufferToDataURL\",value:function(){var t=new Uint8Array(this.texsizeX*this.texsizeY*4);this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,this.targetFrameBuffer),this.gl.readPixels(0,0,this.texsizeX,this.texsizeY,this.gl.RGBA,this.gl.UNSIGNED_BYTE,t);var e=document.createElement(\"canvas\");e.width=this.texsizeX,e.height=this.texsizeY;var i=e.getContext(\"2d\"),s=i.createImageData(this.texsizeX,this.texsizeY);return s.data.set(t),i.putImageData(s,0,0),e.toDataURL()}}])&&it(e.prototype,i),s&&it(e,s),t}();function rt(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}var at=function(){function t(e,i,s){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.audio=new h(e);var r=i.getContext(\"webgl2\",{alpha:!1,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!1});this.baseValsDefaults={decay:.98,gammaadj:2,echo_zoom:2,echo_alpha:0,echo_orient:0,red_blue:0,brighten:0,darken:0,wrap:1,darken_center:0,solarize:0,invert:0,fshader:0,b1n:0,b2n:0,b3n:0,b1x:1,b2x:1,b3x:1,b1ed:.25,wave_mode:0,additivewave:0,wave_dots:0,wave_thick:0,wave_a:.8,wave_scale:1,wave_smoothing:.75,wave_mystery:0,modwavealphabyvolume:0,modwavealphastart:.75,modwavealphaend:.95,wave_r:1,wave_g:1,wave_b:1,wave_x:.5,wave_y:.5,wave_brighten:1,mv_x:12,mv_y:9,mv_dx:0,mv_dy:0,mv_l:.9,mv_r:1,mv_g:1,mv_b:1,mv_a:1,warpanimspeed:1,warpscale:1,zoomexp:1,zoom:1,rot:0,cx:.5,cy:.5,dx:0,dy:0,warp:1,sx:1,sy:1,ob_size:.01,ob_r:0,ob_g:0,ob_b:0,ob_a:0,ib_size:.01,ib_r:.25,ib_g:.25,ib_b:.25,ib_a:0},this.shapeBaseValsDefaults={enabled:0,sides:4,additive:0,thickoutline:0,textured:0,num_inst:1,tex_zoom:1,tex_ang:0,x:.5,y:.5,rad:.1,ang:0,r:1,g:0,b:0,a:1,r2:0,g2:1,b2:0,a2:0,border_r:1,border_g:1,border_b:1,border_a:.1},this.waveBaseValsDefaults={enabled:0,samples:512,sep:0,scaling:1,smoothing:.5,r:1,g:1,b:1,a:1,spectrum:0,usedots:0,thick:0,additive:0},this.renderer=new st(r,this.audio,s)}var e,i,s;return e=t,(i=[{key:\"connectAudio\",value:function(t){this.audioNode=t,this.audio.connectAudio(t)}},{key:\"disconnectAudio\",value:function(t){this.audio.disconnectAudio(t)}},{key:\"loadPreset\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=Object.assign({},t);i.baseVals=Object.assign({},this.baseValsDefaults,i.baseVals);for(var s=0;s<i.shapes.length;s++)i.shapes[s].baseVals=Object.assign({},this.shapeBaseValsDefaults,i.shapes[s].baseVals);for(var r=0;r<i.waves.length;r++)i.waves[r].baseVals=Object.assign({},this.waveBaseValsDefaults,i.waves[r].baseVals);if(\"function\"!=typeof i.init_eqs){i.init_eqs=new Function(\"a\",\"\".concat(i.init_eqs_str,\" return a;\")),i.frame_eqs=new Function(\"a\",\"\".concat(i.frame_eqs_str,\" return a;\")),i.pixel_eqs_str&&\"\"!==i.pixel_eqs_str?i.pixel_eqs=new Function(\"a\",\"\".concat(i.pixel_eqs_str,\" return a;\")):i.pixel_eqs=\"\";for(var a=0;a<i.shapes.length;a++)0!==i.shapes[a].baseVals.enabled&&(i.shapes[a]=Object.assign({},i.shapes[a],{init_eqs:new Function(\"a\",\"\".concat(i.shapes[a].init_eqs_str,\" return a;\")),frame_eqs:new Function(\"a\",\"\".concat(i.shapes[a].frame_eqs_str,\" return a;\"))}));for(var h=0;h<i.waves.length;h++)if(0!==i.waves[h].baseVals.enabled){var o={init_eqs:new Function(\"a\",\"\".concat(i.waves[h].init_eqs_str,\" return a;\")),frame_eqs:new Function(\"a\",\"\".concat(i.waves[h].frame_eqs_str,\" return a;\"))};i.waves[h].point_eqs_str&&\"\"!==i.waves[h].point_eqs_str?o.point_eqs=new Function(\"a\",\"\".concat(i.waves[h].point_eqs_str,\" return a;\")):o.point_eqs=\"\",i.waves[h]=Object.assign({},i.waves[h],o)}}this.renderer.loadPreset(i,e)}},{key:\"loadExtraImages\",value:function(t){this.renderer.loadExtraImages(t)}},{key:\"setRendererSize\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};this.renderer.setRendererSize(t,e,i)}},{key:\"setInternalMeshSize\",value:function(t,e){this.renderer.setInternalMeshSize(t,e)}},{key:\"setOutputAA\",value:function(t){this.renderer.setOutputAA(t)}},{key:\"render\",value:function(t){this.renderer.render(t)}},{key:\"launchSongTitleAnim\",value:function(t){this.renderer.launchSongTitleAnim(t)}},{key:\"toDataURL\",value:function(){return this.renderer.toDataURL()}},{key:\"warpBufferToDataURL\",value:function(){return this.renderer.warpBufferToDataURL()}}])&&rt(e.prototype,i),s&&rt(e,s),t}();function ht(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,\"value\"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}i.d(e,\"default\",function(){return ot});var ot=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i,s;return e=t,s=[{key:\"createVisualizer\",value:function(t,e,i){return new at(t,e,i)}}],(i=null)&&ht(e.prototype,i),s&&ht(e,s),t}()}])});\n//# sourceMappingURL=butterchurn.min.js.map","vendor/butterchurn/presets-2.4.7.min.js":"!function(a,e){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(\"butterchurnPresets\",[],e):\"object\"==typeof exports?exports.butterchurnPresets=e():a.butterchurnPresets=e()}(\"undefined\"!=typeof self?self:this,function(){return function(a){var e={};function t(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return a[r].call(n.exports,n,n.exports,t),n.l=!0,n.exports}return t.m=a,t.c=e,t.d=function(a,e,r){t.o(a,e)||Object.defineProperty(a,e,{configurable:!1,enumerable:!0,get:r})},t.n=function(a){var e=a&&a.__esModule?function(){return a.default}:function(){return a};return t.d(e,\"a\",e),e},t.o=function(a,e){return Object.prototype.hasOwnProperty.call(a,e)},t.p=\"\",t(t.s=165)}([function(a,e,t){a.exports=!t(5)(function(){return 7!=Object.defineProperty({},\"a\",{get:function(){return 7}}).a})},function(a,e){a.exports=function(a){return\"object\"==typeof a?null!==a:\"function\"==typeof a}},function(a,e){var t=a.exports=\"undefined\"!=typeof window&&window.Math==Math?window:\"undefined\"!=typeof self&&self.Math==Math?self:Function(\"return this\")();\"number\"==typeof __g&&(__g=t)},function(a,e){var t=a.exports={version:\"2.5.3\"};\"number\"==typeof __e&&(__e=t)},function(a,e,t){var r=t(15),n=t(16),_=t(18),s=Object.defineProperty;e.f=t(0)?Object.defineProperty:function(a,e,t){if(r(a),e=_(e,!0),r(t),n)try{return s(a,e,t)}catch(a){}if(\"get\"in t||\"set\"in t)throw TypeError(\"Accessors not supported!\");return\"value\"in t&&(a[e]=t.value),a}},function(a,e){a.exports=function(a){try{return!!a()}catch(a){return!0}}},function(a,e,t){\"use strict\";e.__esModule=!0,e.default=function(a,e){if(!(a instanceof e))throw new TypeError(\"Cannot call a class as a function\")}},function(a,e,t){\"use strict\";e.__esModule=!0;var r,n=t(8),_=(r=n)&&r.__esModule?r:{default:r};e.default=function(){function a(a,e){for(var t=0;t<e.length;t++){var r=e[t];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),(0,_.default)(a,r.key,r)}}return function(e,t,r){return t&&a(e.prototype,t),r&&a(e,r),e}}()},function(a,e,t){a.exports={default:t(9),__esModule:!0}},function(a,e,t){t(10);var r=t(3).Object;a.exports=function(a,e,t){return r.defineProperty(a,e,t)}},function(a,e,t){var r=t(11);r(r.S+r.F*!t(0),\"Object\",{defineProperty:t(4).f})},function(a,e,t){var r=t(2),n=t(3),_=t(12),s=t(14),v=function(a,e,t){var m,i,p,b=a&v.F,l=a&v.G,x=a&v.S,o=a&v.P,d=a&v.B,u=a&v.W,c=l?n:n[e]||(n[e]={}),y=c.prototype,h=l?r:x?r[e]:(r[e]||{}).prototype;for(m in l&&(t=e),t)(i=!b&&h&&void 0!==h[m])&&m in c||(p=i?h[m]:t[m],c[m]=l&&\"function\"!=typeof h[m]?t[m]:d&&i?_(p,r):u&&h[m]==p?function(a){var e=function(e,t,r){if(this instanceof a){switch(arguments.length){case 0:return new a;case 1:return new a(e);case 2:return new a(e,t)}return new a(e,t,r)}return a.apply(this,arguments)};return e.prototype=a.prototype,e}(p):o&&\"function\"==typeof p?_(Function.call,p):p,o&&((c.virtual||(c.virtual={}))[m]=p,a&v.R&&y&&!y[m]&&s(y,m,p)))};v.F=1,v.G=2,v.S=4,v.P=8,v.B=16,v.W=32,v.U=64,v.R=128,a.exports=v},function(a,e,t){var r=t(13);a.exports=function(a,e,t){if(r(a),void 0===e)return a;switch(t){case 1:return function(t){return a.call(e,t)};case 2:return function(t,r){return a.call(e,t,r)};case 3:return function(t,r,n){return a.call(e,t,r,n)}}return function(){return a.apply(e,arguments)}}},function(a,e){a.exports=function(a){if(\"function\"!=typeof a)throw TypeError(a+\" is not a function!\");return a}},function(a,e,t){var r=t(4),n=t(19);a.exports=t(0)?function(a,e,t){return r.f(a,e,n(1,t))}:function(a,e,t){return a[e]=t,a}},function(a,e,t){var r=t(1);a.exports=function(a){if(!r(a))throw TypeError(a+\" is not an object!\");return a}},function(a,e,t){a.exports=!t(0)&&!t(5)(function(){return 7!=Object.defineProperty(t(17)(\"div\"),\"a\",{get:function(){return 7}}).a})},function(a,e,t){var r=t(1),n=t(2).document,_=r(n)&&r(n.createElement);a.exports=function(a){return _?n.createElement(a):{}}},function(a,e,t){var r=t(1);a.exports=function(a,e){if(!r(a))return a;var t,n;if(e&&\"function\"==typeof(t=a.toString)&&!r(n=t.call(a)))return n;if(\"function\"==typeof(t=a.valueOf)&&!r(n=t.call(a)))return n;if(!e&&\"function\"==typeof(t=a.toString)&&!r(n=t.call(a)))return n;throw TypeError(\"Can't convert object to primitive value\")}},function(a,e){a.exports=function(a,e){return{enumerable:!(1&a),configurable:!(2&a),writable:!(4&a),value:e}}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.42,decay:1,echo_zoom:.999823,echo_alpha:.5,echo_orient:1,wave_mode:5,wave_thick:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001185,wave_scale:.325446,wave_smoothing:.9,modwavealphastart:.5,modwavealphaend:1,warpanimspeed:2.630064,warpscale:3.209168,zoomexp:1.000158,dx:1e-5,dy:1e-5,warp:.01,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.005,ob_a:.5,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:.1,mv_x:6.4,mv_y:4.8,mv_l:5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.thresh=0;a.dx_r=0;a.dy_r=0;a.tg1=0;a.tg2=0;a.tg3=0;a.six=0;\",frame_eqs_str:\"a.wave_r=.5+.5*Math.sin(1.6*a.time);a.wave_g=.5+.5*Math.sin(4.1*a.time);a.wave_b=-1+(1-a.wave_r+1-a.wave_g);a.warp=2;a.ob_r+=a.wave_b*above(Math.sin(.1*a.time),0);a.ob_b+=a.wave_g*above(Math.sin(.1*a.time),0);a.ob_g+=a.wave_r*above(Math.sin(.1*a.time),0);a.ob_r+=a.wave_g*below(Math.sin(.1*a.time),0);a.ob_b+=a.wave_r*below(Math.sin(.1*a.time),0);a.ob_g+=a.wave_b*below(Math.sin(.1*a.time),0);\",pixel_eqs_str:\"a.thresh=2*above(a.bass_att,a.thresh)+(1-above(a.bass_att,a.thresh))*(.96*(a.thresh-1.3)+1.3);a.dx_r=.05*equal(a.thresh,2)*Math.sin(5*a.time)+(1-equal(a.thresh,2))*a.dx_r;a.dy_r=.056*equal(a.thresh,2)*Math.sin(6*a.time)+(1-equal(a.thresh,2))*a.dy_r;a.tg1=Math.abs(Math.sin(a.time));a.tg2=22*above(a.tg1,.75)+12*below(a.tg1,.25)+18*above(a.tg1,.25)*below(a.tg1,.5)+12*above(a.tg1,.5)*below(a.tg1,.75);a.tg3=.00001<Math.abs(equal(a.thresh,2))?a.tg2:a.tg3;a.six=Math.sin(a.x);a.dx+=\\na.dx_r*Math.sin(Math.abs(a.tg3*a.y))*Math.sin(a.time);a.dy+=a.dy_r*Math.sin(Math.abs(a.tg3*a.x))*Math.cos(a.time);a.dx+=a.dx_r*pow(a.rad,2*a.y)*Math.sin(a.time);a.dy+=a.dy_r*pow(a.rad,2*a.x)*Math.sin(a.time);a.zoom-=.0825*pow(a.rad,6*a.x)*Math.cos(6*a.ang);a.rot-=.039375*Math.sin(1.25*a.time)*pow(a.rad,a.x)*Math.sin(1.45*a.time)*Math.sin(a.time);\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:1,echo_orient:1,wave_mode:5,wave_brighten:0,wave_a:.001,wave_scale:1.447722,wave_smoothing:.5,modwavealphastart:.5,modwavealphaend:1,fshader:1,dx:1e-5,dy:1e-5,warp:.01,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.015,ob_a:1,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:0,mv_y:0,mv_l:1,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.pfdx_r=0;a.pfthresh=0;a.thresh=0;a.ladder=0;a.q1=0;a.leafset=0;a.dy_r=0;a.pfdy_r=0;a.leaf=0;a.dx_r=0;a.q2=0;a.q3=0;a.leafset=3;\",frame_eqs_str:\"a.wave_r=.5+.5*Math.sin(1.6*a.time);a.wave_g=.5+.5*Math.sin(4.1*a.time);a.wave_b=-1+(1-a.wave_r+1-a.wave_g);a.warp=0;a.pfthresh=3*above(a.bass_att,a.pfthresh)+(1-above(a.bass_att,a.pfthresh))*(.96*(a.pfthresh-1.3)+1.3);a.pfdx_r=.015*equal(a.pfthresh,3)*Math.sin(5*a.time)+(1-equal(a.pfthresh,3))*a.pfdx_r;a.pfdy_r=.015*equal(a.pfthresh,3)*Math.sin(6*a.time)+(1-equal(a.pfthresh,3))*a.pfdy_r;a.q1=a.wave_r;a.q2=a.wave_g;a.q3=a.wave_b;a.ob_r=1-.75*Math.abs(a.q1);a.ob_g=1-.75*Math.abs(a.q2);\\na.ob_b=1-.75*Math.abs(a.q3);a.echo_zoom+=Math.min(Math.max(.75,50*a.pfdx_r),1);a.echo_orient+=16*a.pfdy_r;a.dx+=15.1*a.pfdx_r;a.dy+=15.1*a.pfdy_r;a.ob_size+=.005*a.bass_att;a.ib_a-=Math.min(.5,.5-Math.abs(a.dx+a.dy));\",pixel_eqs_str:\"a.thresh=2*above(a.bass_att,a.thresh)+(1-above(a.bass_att,a.thresh))*(.96*(a.thresh-1.3)+1.3);a.dx_r=.015*equal(a.thresh,2)*Math.sin(5*a.time)+(1-equal(a.thresh,2))*a.dx_r;a.dy_r=.015*equal(a.thresh,2)*Math.sin(6*a.time)+(1-equal(a.thresh,2))*a.dy_r;a.ladder=Math.abs(Math.sin(1*a.time));a.leaf=5*below(a.ladder,.2)+8*above(a.ladder,.2)*below(a.ladder,.4)+12*above(a.ladder,.4)*below(a.ladder,.6)+18*above(a.ladder,.6)*below(a.ladder,.8)+24*above(a.ladder,.8);a.leafset=.00001<Math.abs(equal(a.thresh,\\n2))?a.leaf:a.leafset;a.zoom+=.05*(.75-Math.cos(a.leafset*a.rad))*(1-a.rad);a.zoom-=Math.abs(.05*(.75-Math.cos(4*a.rad)));\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:.96,echo_zoom:.99663,echo_orient:1,wave_mode:2,wave_dots:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.011726,wave_smoothing:.9,warpanimspeed:.010284,warpscale:.01,warp:.01,wave_r:.5,wave_g:.4,wave_b:.3,ob_size:0,ob_r:.11,ob_b:.1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:64,mv_y:48,mv_l:5,mv_r:0,mv_g:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:32,additive:1,rad:.194774,r:0,b:1,a:1e-6,r2:.63,g2:.7,b2:1,a2:.07,border_a:0},init_eqs_str:\"a.lens_scale=0;a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.pos_scale=0;a.cubesize=0;a.xq=0;a.sang=0;a.flip=0;a.zq=0;a.tm=0;a.zp=0;a.q2=0;a.sample=0;a.q8=0;a.flip=1;\",frame_eqs_str:\"a.flip=-a.flip;a.lens_scale=.5*a.flip+.5;a.lens_scale=1+2.4*a.lens_scale;a.pos_scale=.00001<Math.abs(equal(a.flip,-1))?.5:a.lens_scale;a.t1=.25*a.q1;a.sample=1;a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.tm=4*a.q1+4*a.sample;a.xp=Math.sin(a.tm)*Math.cos(3*a.tm)*.5+.5;a.yp=Math.sin(1.1*a.tm)*Math.sin(4.1*a.tm)*.5+.5;a.zp=Math.sin(2.9*a.tm)*Math.cos(1.77*a.tm)*.5+.5;a.xp=div(Math.floor(a.xp*a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(Math.floor(a.yp*a.cubesize),\\na.cubesize)-.5+a.fix;a.zp=div(Math.floor(a.zp*a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp*a.sang+a.zp*a.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(-a.xp,a.zp)*a.pos_scale+.5;a.y=div(-a.yp,a.zp)*a.pos_scale*1.333+.5;a.rad=a.rad*(1+div(a.q8,\\n3))*a.lens_scale;\"},{baseVals:{enabled:1,sides:6,rad:.043785,r:.3,g:.6,b:1,g2:0,b2:1,border_a:0},init_eqs_str:\"a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.cubesize=0;a.xq=0;a.sang=0;a.zq=0;a.tm=0;a.zp=0;a.q2=0;a.sample=0;a.q8=0;\",frame_eqs_str:\"a.t1=.25*a.q1;a.sample=1;a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.tm=4*a.q1+4*a.sample;a.xp=Math.sin(a.tm)*Math.cos(3*a.tm)*.5+.5;a.yp=Math.sin(1.1*a.tm)*Math.sin(4.1*a.tm)*.5+.5;a.zp=Math.sin(2.9*a.tm)*Math.cos(1.77*a.tm)*.5+.5;a.xp=div(Math.floor(a.xp*a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(Math.floor(a.yp*a.cubesize),a.cubesize)-.5+a.fix;a.zp=div(Math.floor(a.zp*a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);\\na.xq=a.xp*a.sang+a.zp*a.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(a.xp,a.zp)+.5;a.y=1.333*div(a.yp,a.zp)+.5;a.a=Math.min(a.a+div(a.q8,2),1);a.r=Math.min(a.r*(1+a.q8),1);a.g=Math.min(a.g*(1+a.q8),1);a.r2=Math.min(div(a.q8,2),1);a.g2=Math.min(div(a.q8,4),1);a.rad*=1+div(a.q8,7);\"},{baseVals:{enabled:1,sides:36,rad:.284278,r:0,a:0,r2:.23,g2:.54,b2:1,a2:.05,border_g:.8,border_b:.4,border_a:.45},init_eqs_str:\"a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.cubesize=0;a.xq=0;a.sang=0;a.zq=0;a.tm=0;a.zp=0;a.q2=0;a.sample=0;a.q8=0;\",frame_eqs_str:\"a.t1=.25*a.q1;a.sample=1;a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.tm=4*a.q1+4*a.sample;a.xp=Math.sin(a.tm)*Math.cos(3*a.tm)*.5+.5;a.yp=Math.sin(1.1*a.tm)*Math.sin(4.1*a.tm)*.5+.5;a.zp=Math.sin(2.9*a.tm)*Math.cos(1.77*a.tm)*.5+.5;a.xp=div(Math.floor(a.xp*a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(Math.floor(a.yp*a.cubesize),a.cubesize)-.5+a.fix;a.zp=div(Math.floor(a.zp*a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);\\na.xq=a.xp*a.sang+a.zp*a.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(a.xp,a.zp)+.5;a.y=1.333*div(a.yp,a.zp)+.5;a.a2=Math.min(a.a2*(1+div(a.q8,2)),1);a.r2=Math.min(a.r2*(1+div(a.q8,4)),1);a.g2=Math.min(a.g2*(1+div(a.q8,3)),1);a.border_a=Math.min(a.border_a*(1+a.q8),1);\"},{baseVals:{enabled:1,sides:6,additive:1,rad:.158045,r:.3,g:.6,b:1,a:.140001,r2:.4,g2:.5,b2:1,border_a:0},init_eqs_str:\"a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.cubesize=0;a.xq=0;a.sang=0;a.zq=0;a.tm=0;a.zp=0;a.q2=0;a.sample=0;a.q8=0;\",frame_eqs_str:\"a.t1=.25*a.q1;a.sample=1;a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.tm=4*a.q1+4*a.sample;a.xp=Math.sin(a.tm)*Math.cos(3*a.tm)*.5+.5;a.yp=Math.sin(1.1*a.tm)*Math.sin(4.1*a.tm)*.5+.5;a.zp=Math.sin(2.9*a.tm)*Math.cos(1.77*a.tm)*.5+.5;a.xp=div(Math.floor(a.xp*a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(Math.floor(a.yp*a.cubesize),a.cubesize)-.5+a.fix;a.zp=div(Math.floor(a.zp*a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);\\na.xq=a.xp*a.sang+a.zp*a.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(a.xp,a.zp)+.5;a.y=1.333*div(a.yp,a.zp)+.5;a.a=Math.min(a.a*a.q8,1);a.rad*=1+a.q8;\"}],waves:[{baseVals:{enabled:1,thick:1,additive:1,r:.05,g:.09},init_eqs_str:\"a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.cubesize=0;a.xq=0;a.sang=0;a.zq=0;a.ang=0;a.tm=0;a.zp=0;a.q2=0;\",frame_eqs_str:\"a.t1=.25*a.q1;\",point_eqs_str:\"a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.tm=4*a.q1+4*a.sample;a.xp=Math.sin(a.tm)*Math.cos(3*a.tm)*.5+.5;a.yp=Math.sin(1.1*a.tm)*Math.sin(4.1*a.tm)*.5+.5;a.zp=Math.sin(2.9*a.tm)*Math.cos(1.77*a.tm)*.5+.5;a.xp=div(Math.floor(a.xp*a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(Math.floor(a.yp*a.cubesize),a.cubesize)-.5+a.fix;a.zp=div(Math.floor(a.zp*a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp*a.sang+a.zp*\\na.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(a.xp,a.zp)+.5;a.y=1.333*div(a.yp,a.zp)+.5;\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,usedots:1,thick:1},init_eqs_str:\"a.cang=0;a.n=0;a.yq=0;a.xp=0;a.fix=0;a.yp=0;a.t1=0;a.q1=0;a.cubesize=0;a.xq=0;a.sang=0;a.zq=0;a.ang=0;a.zp=0;a.q2=0;\",frame_eqs_str:\"a.t1=.25*a.q1;\",point_eqs_str:\"a.n=6.283*a.sample;a.cubesize=a.q2;a.fix=.5*div(1,a.cubesize);a.xp=div(randint(a.cubesize),a.cubesize)-.5+a.fix;a.yp=div(randint(a.cubesize),a.cubesize)-.5+a.fix;a.zp=div(randint(a.cubesize),a.cubesize)-.5+a.fix;a.ang=a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp*a.sang+a.zp*a.cang;a.yq=a.yp;a.zq=a.xp*a.cang-a.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.ang=.75*a.t1;a.sang=Math.sin(a.ang);a.cang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sang+a.zp*a.cang;a.zq=a.yp*a.cang-\\na.zp*a.sang;a.xp=a.xq;a.yp=a.yq;a.zp=a.zq;a.zp+=2;a.x=div(a.xp,a.zp)+.5;a.y=1.333*div(a.yp,a.zp)+.5;\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.q1=0;a.beat=0;a.vol=0;a.bc=0;a.size=0;a.q2=0;a.trigger=0;a.q7=0;a.mtime=0;a.q8=0;a.mv_x=64;a.mv_y=48;a.nut=0;a.stp=0;a.stq=0;a.rtp=0;a.rtq=0;a.wvr=0;a.decay=0;a.dcsp=0;a.size=4;a.bc=0;\",frame_eqs_str:\"a.decay=.95;a.zoom=1.005;a.vol=.25*(a.bass+a.mid+a.treb);a.vol*=a.vol;a.q8=a.vol;a.mtime+=.01*a.vol*div(75,a.fps);a.q7=a.mtime;a.monitor=div(512,8);a.warp=0;a.q1=.9*a.mtime;a.beat=above(a.vol,1);a.bc=Math.max(a.bc,0);a.bc=.00001<Math.abs(equal(a.bc,0))?a.bc+a.beat:a.bc-div(div(1,a.fps),4);a.trigger=equal(a.bc,1);a.monitor=a.size;a.size+=a.trigger;a.size=.00001<Math.abs(above(a.size,10))?4:a.size;a.q2=Math.floor(a.size);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:5,decay:.95,echo_zoom:1.006596,echo_orient:1,wave_mode:2,wave_dots:1,wave_brighten:0,wave_a:.019788,wave_scale:.011726,wave_smoothing:.9,warpanimspeed:.010284,warpscale:.01,fshader:1,warp:.01,wave_r:.5,wave_g:.4,wave_b:.3,ob_size:0,ob_r:.11,ob_b:.1,ib_size:.005,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:31.999994,mv_y:24.000004,mv_dx:.02,mv_dy:-.02,mv_l:1,mv_r:.49,mv_g:.48,mv_b:.300001,mv_a:0},shapes:[{baseVals:{enabled:1,textured:1,rad:1.670888,tex_ang:.942478,tex_zoom:.534261,g:1,b:1,r2:1,b2:1,a2:1,border_a:.17},init_eqs_str:\"a.flux=0;a.q5=0;a.fluxs=0;a.advflux=0;a.q3=0;a.adv=0;a.advs=0;\",frame_eqs_str:\"a.flux=9*a.q5;a.fluxs=Math.max(a.flux,0);a.fluxs=Math.min(a.fluxs,1);a.advflux=a.q3*a.fluxs+-a.q3*(1-a.fluxs);a.adv+=a.advflux;a.advs=div(a.adv,256);a.ang=a.advs;a.rad=1.671+div(a.q3,25);\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:3,x:.59,rad:.444842,r:0,r2:.1,g2:.05,border_a:0},init_eqs_str:\"a.rotator=0;a.dir=0;a.mover=0;a.dir=3;a.mover=0;a.rotator=.255;\",frame_eqs_str:\"a.ang=a.rotator;a.x=.00001<Math.abs(equal(a.dir,1))?1-a.mover:.00001<Math.abs(equal(a.dir,1.5))?.15:.00001<Math.abs(equal(a.dir,2))?0:.00001<Math.abs(equal(a.dir,2.5))?0:.00001<Math.abs(equal(a.dir,3))?0+a.mover:.00001<Math.abs(equal(a.dir,3.5))?1:(equal(a.dir,4),1);a.y=.00001<Math.abs(equal(a.dir,1))?1:.00001<Math.abs(equal(a.dir,1.5))?1:.00001<Math.abs(equal(a.dir,2))?1-a.mover:.00001<Math.abs(equal(a.dir,2.5))?0:.00001<Math.abs(equal(a.dir,3))?0:.00001<Math.abs(equal(a.dir,\\n3.5))?0:.00001<Math.abs(equal(a.dir,4))?0+a.mover:1;a.mover=.00001<Math.abs(equal(a.dir,1))?a.mover+.005:.00001<Math.abs(equal(a.dir,1.5))?0:.00001<Math.abs(equal(a.dir,2))?a.mover+.005:.00001<Math.abs(equal(a.dir,2.5))?0:.00001<Math.abs(equal(a.dir,3))?a.mover+.005:.00001<Math.abs(equal(a.dir,3.5))?0:.00001<Math.abs(equal(a.dir,4))?a.mover+.005:0;a.dir=.00001<Math.abs(equal(a.dir,1))?.00001<Math.abs(above(a.mover,.995))?1.5:a.dir:.00001<Math.abs(equal(a.dir,1.5))?.00001<Math.abs(below(a.rotator,\\n-1.29))?2:a.dir:.00001<Math.abs(equal(a.dir,2))?.00001<Math.abs(above(a.mover,.995))?2.5:a.dir:.00001<Math.abs(equal(a.dir,2.5))?.00001<Math.abs(below(a.rotator,-2.85))?3:a.dir:.00001<Math.abs(equal(a.dir,3))?.00001<Math.abs(above(a.mover,.995))?3.5:a.dir:.00001<Math.abs(equal(a.dir,3.5))?.00001<Math.abs(below(a.rotator,-4.44))?4:a.dir:.00001<Math.abs(equal(a.dir,4))?.00001<Math.abs(above(a.mover,.995))?4.5:a.dir:.00001<Math.abs(equal(a.dir,4.5))?.00001<Math.abs(below(a.rotator,-5.94))?1:a.dir:a.dir;\\na.rotator=.00001<Math.abs(equal(a.dir,1.5))?.00001<Math.abs(above(a.rotator,-1.31))?a.rotator-.05:a.rotator:.00001<Math.abs(equal(a.dir,2))?-1.3:.00001<Math.abs(equal(a.dir,2.5))?.00001<Math.abs(above(a.rotator,-2.87))?a.rotator-.05:a.rotator:.00001<Math.abs(equal(a.dir,3))?-2.86:.00001<Math.abs(equal(a.dir,3.5))?.00001<Math.abs(above(a.rotator,-4.46))?a.rotator-.05:a.rotator:.00001<Math.abs(equal(a.dir,4))?-4.45:.00001<Math.abs(equal(a.dir,4.5))?.00001<Math.abs(above(a.rotator,-5.97))?a.rotator-\\n.05:a.rotator:.00001<Math.abs(equal(a.dir,4))?-5.96:.26;a.b=above(a.mid,1.5);a.r2=above(a.mid,1.5);a.g2=above(a.mid,1.5);a.b2=above(a.mid,1.5);\"},{baseVals:{enabled:1,sides:3,additive:1,x:.84,r:.98,g:1,b:.98,a:.3,g2:.09,border_a:0},init_eqs_str:\"a.q1=0;\",frame_eqs_str:\"a.x=.4*Math.sin(div(a.time,2))+.5;a.y=.4*Math.sin(a.time)+.5;a.rad=div(a.q1*a.q1,2);a.ang=4*a.q1;a.r=.7+.5*Math.sin(div(a.time,2));a.g=.7+.5*Math.sin(div(a.time,2)+2);a.b=.7+.5*Math.sin(div(a.time,2)+4);\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.speedinv=0;a.q1=0;a.q5=0;a.flux=0;a.q4=0;a.q2=0;a.qb=0;a.speed=0;a.qc=0;a.q3=0;a.qa=0;a.mv_x=64;a.mv_y=48;a.nut=0;a.stp=0;a.stq=0;a.rtp=0;a.rtq=0;a.wvr=0;a.decay=0;a.dcsp=0;a.q1=0;a.q2=0;a.q3=0;\",frame_eqs_str:\"a.decay=1;a.zoom=1;a.speed=.8;a.speedinv=1-a.speed;a.q1=a.qa*a.speed+a.bass*a.speedinv;a.q2=a.qb*a.speed+a.mid*a.speedinv;a.q3=a.qc*a.speed+a.treb*a.speedinv;a.qa=a.q1;a.qb=a.q2;a.qc=a.q3;a.flux=Math.sin(div(a.time,2));a.q4=.5*a.flux+.5;a.q5=a.flux;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.21,decay:.96,echo_zoom:.99663,echo_orient:1,wave_mode:2,wave_dots:1,wave_brighten:0,brighten:1,invert:1,wave_a:.001,wave_scale:.011726,wave_smoothing:.9,warpanimspeed:.037492,warpscale:.014889,warp:.033004,wave_r:.5,wave_g:.4,wave_b:.3,ob_size:.055,ob_r:1,ob_g:1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:0,mv_y:43.199997,mv_l:1,mv_g:.91,mv_b:.71,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,r:.1,b:.7},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.r1=0;a.g2=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.r2=0;a.ang=0;a.sinang=0;a.tm=0;a.b2=0;a.t2=0;a.zp=0;a.g1=0;a.t4=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5);a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.xq=a.xp;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.3;a.ang=3.14+1.5*\\nMath.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*Math.cos(a.tm)-.5;\\na.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=a.a;a.b+=.3*pow(1-a.sample,2);a.r1=a.t1;a.g1=a.t2;a.b1=a.t3;a.r2=a.t4;a.g2=a.t5;a.b2=a.t6;a.r=a.r1*a.flip+a.r2*(1-a.flip);a.g=a.g1*a.flip+a.g2*(1-a.flip);a.b=a.b1*a.flip+a.b2*(1-a.flip);\"},{baseVals:{enabled:1,r:.2,b:.6},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.r1=0;a.g2=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.r2=0;a.ang=0;a.sinang=0;a.tm=0;a.b2=0;a.t2=0;a.zp=0;a.g1=0;a.t4=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5)+.1;a.yp=-a.yp;a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.3;\\na.ang=3.14+1.5*Math.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*Math.cos(a.tm)-\\n.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=a.a;a.b+=.3*pow(1-a.sample,2);a.r1=a.t1;a.g1=a.t2;a.b1=a.t3;a.r2=a.t4;a.g2=a.t5;a.b2=a.t6;a.r=a.r1*a.flip+a.r2*(1-a.flip);a.g=a.g1*a.flip+a.g2*(1-a.flip);a.b=a.b1*a.flip+a.b2*(1-a.flip);\"},{baseVals:{enabled:1,usedots:1,thick:1,additive:1,g:.6,b:.1},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.t8=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.ang=0;a.sinang=0;a.tm=0;a.t2=0;a.zp=0;a.t4=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;a.t8=Math.floor(2*Math.sin(2*a.time)+3);\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5);a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.xq=a.xp;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm+1;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.3;a.ang=3.14+\\n1.5*Math.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*Math.cos(a.tm)-\\n.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=above(Math.sin(128*a.tm*a.t8),0);a.r=a.t4;a.g=a.t5;a.b=a.t6;\"},{baseVals:{enabled:1,usedots:1,thick:1,additive:1,g:.3,b:.1},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.t8=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.ang=0;a.sinang=0;a.tm=0;a.t2=0;a.zp=0;a.t4=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;a.t8=Math.floor(2*Math.sin(2*a.time)+3);\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5)+.1;a.yp=-a.yp;a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm+1;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=\\n.3;a.ang=3.14+1.5*Math.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*\\nMath.cos(a.tm)-.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=a.a;a.a*=above(Math.sin(128*a.tm*a.t8),0);a.r=a.t4;a.g=a.t5;a.b=a.t6;\"}],init_eqs_str:'a.meanbass_att=0;a.q1=0;a[\"var\"]=0;a.flip=0;a.lastbeat=0;a.beatrate=0;a.mode=0;a.peakbass_att=0;a.beatcounter=0;a.att=0;a.volume=0;a.beat=0;a.vol=0;a.mtime=0;a.q8=0;a.mv_x=64;a.mv_y=48;a.nut=0;a.stp=0;a.stq=0;a.rtp=0;a.rtq=0;a.wvr=0;a.decay=0;a.dcsp=0;a.warp=0;',frame_eqs_str:\"a.volume=.3*(a.bass+a.mid+a.att);a.beatrate=equal(a.beatrate,0)+(1-equal(a.beatrate,0))*(below(a.volume,.01)+(1-below(a.volume,.01))*a.beatrate);a.lastbeat+=equal(a.lastbeat,0)*a.time;a.meanbass_att=.1*(9*a.meanbass_att+a.bass_att);a.peakbass_att=Math.max(a.bass_att,a.peakbass_att);a.beatrate=Math.max(.00001<Math.abs(a.beat)?.00001<Math.abs(below(a.time-a.lastbeat,2*a.beatrate))?.1*(9*a.beatrate+a.time-a.lastbeat):a.beatrate:a.beatrate,.1);a.peakbass_att=a.beat*a.bass_att+(1-\\na.beat)*a.peakbass_att*(.95*above(a.time-a.lastbeat,2*a.beatrate)+.995*(1-above(a.time-a.lastbeat,2*a.beatrate)));a.lastbeat=a.beat*a.time+(1-a.beat)*a.lastbeat;a.peakbass_att=Math.max(a.peakbass_att,1.1*a.meanbass_att);a.beat=above(a.volume,.8)*below(a.peakbass_att-a.bass_att,.05*a.peakbass_att)*above(a.time-a.lastbeat,.1+.5*(a.beatrate-.1));a.beatcounter+=a.beat;a.mode=.00001<Math.abs(a.beat*equal(mod(a.beatcounter,2),0))?1-a.mode:a.mode;a.flip=2*a.mode-1;a.monitor=a.flip;a.q8=a.flip;a.decay=1;\\na.zoom=1.002;a.vol=.25*(a.bass_att+a.mid_att+a.treb_att);a.vol*=a.vol;a.mtime+=.1*a.vol*a.flip*div(55,a.fps);a.q1=.4*a.mtime;a.warp=0;\",pixel_eqs_str:'a[\"var\"]=Math.tan(a.time)*a.treb*a.treb;a.zoom=1+div(a.rad,40)+div(a[\"var\"],40);',warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.999,echo_zoom:1.006596,echo_alpha:.5,echo_orient:3,wave_mode:2,additivewave:1,wave_dots:1,wave_thick:1,wave_a:4.099998,wave_scale:1.18647,wave_smoothing:.63,wave_mystery:-.2,modwavealphastart:.71,modwavealphaend:1.28,warpscale:1.331,zoom:.999514,warp:.01,wave_r:.3,wave_g:.6,ob_size:.005,ob_r:.4999,ob_b:1,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:.2,mv_r:0,mv_g:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.new_mid=0;a.q6=0;a.q1=0;a.q5=0;a.treb_c=0;a.vol_att=0;a.zoome=0;a.mid_c=0;a.new_treb=0;a.q4=0;a.rad_cycle=0;a.vol_c=0;a.bass_c=0;a.rote=0;a.vol=0;a.new_bass=0;a.q2=0;a.q3=0;a.q7=0;a.q8=0;\",frame_eqs_str:\"a.vol_att=.25*a.treb_att+.25*a.mid_att+div(.25*a.bass_att+.5*Math.sin(a.vol),a.vol);a.vol=a.bass+a.treb+a.mid;a.new_bass=.25*a.bass+.5*Math.sin(.25*a.bass_att);a.new_treb=.25*a.treb+.5*Math.sin(.25*a.treb_att);a.new_mid=.25*a.mid+.4*Math.sin(.25*a.mid_att);a.bass_c=a.q1-Math.sin(bitand(a.bass_att,.54*a.time));a.treb_c=a.q2-Math.sin(bitand(a.treb_att,.44*a.time));a.mid_c=a.q3-Math.sin(bitand(a.mid_att,.24*a.time));a.vol_c=a.q4-Math.sin(bitand(a.vol_att,.64*a.time));a.q1=Math.sin(bitand(a.bass-\\na.new_bass,.63*a.time));a.q2=Math.sin(bitand(a.treb-a.new_treb,.43*a.time));a.q3=Math.sin(bitand(a.mid-a.new_mid,.23*a.time));a.q4=Math.sin(bitand(a.vol,.65*a.time));a.q5=a.bass_c;a.q6=a.treb_c;a.q7=a.mid_c;a.q8=a.vol_c;a.wave_mystery=a.bass_att-1;a.wave_r+=.2*Math.sin(.43*a.time);a.wave_b-=.2*Math.sin(.54*a.time);a.wave_g-=.4*Math.sin(.34*a.time);a.ob_a=0;\",pixel_eqs_str:\"a.rad_cycle=a.rad*a.rad*a.x*60*a.rad*Math.sin(a.q6);a.rote=a.rot+.1*Math.sin(a.rad_cycle*Math.sin(3.14*a.rad))+.01*Math.sin(a.q1)*Math.tan(a.rad);a.zoome=a.zoom+.1*Math.sin(3.14*a.rad*Math.sin(3.14*a.ang)*Math.sin(a.q2)-a.rote);a.zoom=a.zoome+.05*Math.sin(3.14*a.rad*a.q2)*Math.sin(a.q4);a.rot=a.rote*a.rad+div(div(a.ang,2)*Math.sin(a.q3)*Math.sin(3.14*a.ang*Math.sin(a.q3)*Math.sin(a.q4)),2)+.1*Math.sin(3.14*a.ang)*Math.sin(a.q1);\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,decay:1,echo_zoom:1,wave_mode:1,wave_brighten:0,wave_a:1,wave_scale:.504218,wave_mystery:.24,warpanimspeed:9.8608,warpscale:16.2174,zoomexp:1.503744,zoom:1.0201,warp:.819544,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.005,ob_a:.2,ib_size:.005,ib_r:0,ib_g:0,ib_b:0,ib_a:.06,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.xpos=0;a.yamptarg=0;a.ydir=0;a.zoom_exp=0;a.yaccel=0;a.xamptarg=0;a.xamp=0;a.xspeed=0;a.ypos=0;a.xaccel=0;a.att=0;a.vol=0;a.yamp=0;a.xdir=0;a.yspeed=0;\",frame_eqs_str:\"a.dx=-.0005;a.dy=-.0005;a.vol=div(a.bass+a.mid+a.att,6);a.xamptarg=.00001<Math.abs(equal(mod(a.frame,15),0))?Math.min(.5*a.vol*a.bass_att,.5):a.xamptarg;a.xamp+=.5*(a.xamptarg-a.xamp);a.xdir=.00001<Math.abs(above(Math.abs(a.xpos),a.xamp))?-sign(a.xpos):.00001<Math.abs(below(Math.abs(a.xspeed),.1))?2*above(a.xpos,0)-1:a.xdir;a.xaccel=a.xdir*a.xamp-a.xpos-.055*a.xspeed*below(Math.abs(a.xpos),a.xamp);a.xspeed=a.xspeed+a.xdir*a.xamp-a.xpos-.055*a.xspeed*below(Math.abs(a.xpos),a.xamp);\\na.xpos+=.001*a.xspeed;a.wave_x=a.xpos+.5;a.yamptarg=.00001<Math.abs(equal(mod(a.frame,15),0))?Math.min(.3*a.vol*a.treb_att,.5):a.yamptarg;a.yamp+=.5*(a.yamptarg-a.yamp);a.ydir=.00001<Math.abs(above(Math.abs(a.ypos),a.yamp))?-sign(a.ypos):.00001<Math.abs(below(Math.abs(a.yspeed),.1))?2*above(a.ypos,0)-1:a.ydir;a.yaccel=a.ydir*a.yamp-a.ypos-.055*a.yspeed*below(Math.abs(a.ypos),a.yamp);a.yspeed=a.yspeed+a.ydir*a.yamp-a.ypos-.055*a.yspeed*below(Math.abs(a.ypos),a.yamp);a.ypos+=.001*a.yspeed;a.wave_y=\\na.ypos+.5;a.wave_r+=.35*(.6*Math.sin(.98*a.time)+.4*Math.sin(1.047*a.time));a.wave_g+=.35*(.6*Math.sin(.835*a.time)+.4*Math.sin(1.081*a.time));a.wave_b+=.35*(.6*Math.sin(.814*a.time)+.4*Math.sin(1.011*a.time));a.rot+=.03*(.6*Math.sin(.381*a.time)+.4*Math.sin(.479*a.time));a.cx+=.41*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.41*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.wave_mystery+=.15*(.6*Math.sin(.629*a.time)+.4*Math.sin(1.826*a.time));a.warp*=a.vol;a.zoom-=.02*a.zoom*\\na.bass_att;a.zoom_exp=1.5*(.6*Math.sin(.381*a.time)+.4*Math.sin(.479*a.time));a.ob_a=1-2*a.vol;a.monitor=a.zoom_exp;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_brighten:0,darken:1,wave_a:.002,wave_scale:.14,wave_smoothing:0,wave_mystery:-.84,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_g:0,wave_b:.6,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:23,textured:1,rad:.72143,tex_ang:.62832,tex_zoom:.0402,g:1,b:1,a:.9,r2:.9,b2:1,a2:.3,border_a:0},init_eqs_str:\"a.vis=0;a.q27=0;a.q22=0;\",frame_eqs_str:\"a.vis=div(bnot(mod(a.q27,8))*a.q22,2);a.vis=Math.min(a.vis,1);a.a=div(a.vis,2);a.a2=div(a.vis,2);a.tex_zoom=1;a.rad=.3*a.vis;\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:5,additive:1,num_inst:4,x:.9,rad:.24732,tex_zoom:.40839,g:.6,a:.4,g2:0,b2:.6,border_a:0},init_eqs_str:\"a.blue=0;a.ampl=0;a.steps=0;a.k1=0;a.q24=0;a.green=0;a.red=0;a.trig=0;a.q27=0;a.q28=0;\",frame_eqs_str:\"a.steps=a.q28+1;a.steps=4;a.ampl=.2;a.x=.5+a.ampl*Math.cos(6.28*(div(a.q27,a.steps)+div(a.instance,4)));a.y=.5+a.ampl*Math.sin(6.28*(div(a.q27,a.steps)+div(a.instance,4)));a.a=.4;a.a2=0;a.trig=a.q24*bnot(mod(a.q27,4));a.red=bnot(a.trig)*a.red+div(randint(100),100)*a.trig;a.green=bnot(a.trig)*a.green+div(randint(100),100)*a.trig;a.blue=bnot(a.trig)*a.blue+div(randint(100),100)*a.trig;a.k1=div(a.instance,a.num_inst);a.r=a.red*a.k1;a.g=a.green*(1-a.k1);a.b=4*a.blue*a.k1*(1-a.k1);\\na.r=a.red;a.g=a.green;a.b=a.blue;a.r2=a.r;a.b2=a.b;a.g2=a.g;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.px=0;a.index2=0;a.speed_=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q13=0;a.q15=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.py=0;a.movz=0;a.trel=0;a.q9=0;a.rott=0;a.vol__=0;a.is_beat=0;a.k1=0;a.q24=0;a.vx=0;a.dec_slow=0;a.q11=0;a.q10=0;a.vy=0;a.vz=0;a.q4=0;a.dir=0;a.q16=0;a.p2=0;a.avg=0;a.trig=0;a.beat=0;a.q17=0;a.vol=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.speed=0;a.q3=0;a.t0=0;a.vol_=0;a.q7=0;a.q28=0;a.q20=0;a.q8=0;a.ang0=0;a.speed_=1;a.vx=0;a.vy=0;a.vz=0;a.trel=0;a.p1=0;a.dir=0;a.ang0=\\n0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.96,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.5);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,16);a.index2=mod(a.index2+a.is_beat*bnot(a.index),8);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q24=a.is_beat;a.vol=a.bass_att+a.mid_att+\\na.treb_att;a.vol_=a.dec_med*a.vol_+(1-a.dec_med)*a.vol;a.vol__=a.dec_med*a.vol__+(1-a.dec_med)*a.vol_;a.q27=a.index+1;a.q28=a.index2+1;a.k1=a.is_beat*equal(mod(a.index,4),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.trig=a.q24*bnot(mod(a.index,8));a.vx=a.vx*bnot(a.trig)+a.trig*(div(randint(100),100)-.5);a.vy=a.vy*bnot(a.trig)+a.trig*(div(randint(100),100)-.5);a.vz=a.vz*bnot(a.trig)+\\na.trig*(div(randint(100),100)-.5);a.q10=.2+a.vy*a.vy*4;a.q11=.5*Math.cos(div(a.time,9))+.3;a.speed=Math.min(a.vol_-a.vol__+.5,1);a.speed=Math.max(a.speed,-.5);a.speed_=bnot(a.trig)*a.speed_+a.trig*a.speed;a.movz+=div(1,a.fps)*a.speed_;a.q9=a.movz;a.q12=2*Math.min(a.q22,6);a.trig=bnot(mod(a.index,4))*a.q24;a.dir=bnot(a.trig)*a.dir+a.trig*(randint(10)-5);a.trel+=div(.2,a.fps)*a.dir;a.q5=Math.cos(a.trel);a.q6=Math.sin(a.trel+0*a.q27*mod(a.q28,2));a.q7=-a.q6;a.q8=a.q5;a.px=a.px*a.dec_med+(1-a.dec_med)*\\na.vx;a.py=a.py*a.dec_med+(1-a.dec_med)*a.vy;a.q15=div(a.px,3);a.q16=div(a.py,3);a.q13=Math.min(2,1+Math.abs(8*a.px*a.py));a.ang0=a.ang0*a.dec_med+a.vz*(1-a.dec_med);a.q17=2*a.ang0;a.zoom=1.1;a.rot=.2*Math.sin(bnot(mod(a.q28,2))*a.q28);a.dx=0;a.monitor=a.q11;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 uv6_1;\\n  vec2 uv1_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (uv - 0.5);\\n  float x_4;\\n  x_4 = (abs(tmpvar_3.x) - abs(tmpvar_3.y));\\n  float tmpvar_5;\\n  tmpvar_5 = (18.0 * sqrt((x_4 * x_4)));\\n  uv1_2 = (tmpvar_3 - (clamp (\\n    ((sin(tmpvar_5) / cos(tmpvar_5)) * normalize(tmpvar_3))\\n  , vec2(-3.0, -3.0), vec2(3.0, 3.0)) / 60.0));\\n  uv6_1 = (0.4 * cos((\\n    (uv1_2 * 4.0)\\n   * q28)));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (((texture (sampler_main, \\n    ((uv1_2 * (q11 + (q10 * tmpvar_5))) + 0.5)\\n  ).xyz + \\n    (vec3((0.006 / sqrt(dot (uv6_1, uv6_1)))) * (1.0 + roam_cos).xyz)\\n  ) * 0.99) - 0.025);\\n  ret = tmpvar_6.xyz;\\n }\",comp:\"float xlat_mutableang2;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutableuv2;\\n shader_body { \\n  vec2 uv_1;\\n  vec3 ret1_3;\\n  uv_1 = ((uv - 0.5) * aspect.xy);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q15;\\n  tmpvar_4.y = q16;\\n  uv_1 = (uv_1 + tmpvar_4);\\n  mat2 tmpvar_5;\\n  tmpvar_5[uint(0)] = _qb.xy;\\n  tmpvar_5[1u] = _qb.zw;\\n  uv_1 = (uv_1 * tmpvar_5);\\n  float tmpvar_6;\\n  float tmpvar_7;\\n  tmpvar_7 = (min (abs(\\n    (uv_1.y / uv_1.x)\\n  ), 1.0) / max (abs(\\n    (uv_1.y / uv_1.x)\\n  ), 1.0));\\n  float tmpvar_8;\\n  tmpvar_8 = (tmpvar_7 * tmpvar_7);\\n  tmpvar_8 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_8) + 0.05368138) * tmpvar_8) - 0.1173503)\\n     * tmpvar_8) + 0.1938925) * tmpvar_8) - 0.3326756)\\n   * tmpvar_8) + 0.9999793) * tmpvar_7);\\n  tmpvar_8 = (tmpvar_8 + (float(\\n    (abs((uv_1.y / uv_1.x)) > 1.0)\\n  ) * (\\n    (tmpvar_8 * -2.0)\\n   + 1.570796)));\\n  tmpvar_6 = (tmpvar_8 * sign((uv_1.y / uv_1.x)));\\n  if ((abs(uv_1.x) > (1e-08 * abs(uv_1.y)))) {\\n    if ((uv_1.x < 0.0)) {\\n      if ((uv_1.y >= 0.0)) {\\n        tmpvar_6 += 3.141593;\\n      } else {\\n        tmpvar_6 = (tmpvar_6 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_6 = (sign(uv_1.y) * 1.570796);\\n  };\\n  xlat_mutablers0.x = ((tmpvar_6 / 3.1416) * 2.0);\\n  xlat_mutablers0.y = (0.03 / sqrt(dot (uv_1, uv_1)));\\n  ret1_3 = vec3(0.0, 0.0, 0.0);\\n  for (int n_2 = 0; n_2 <= 10; n_2++) {\\n    float tmpvar_9;\\n    tmpvar_9 = fract((-(q9) + (\\n      float(n_2)\\n     / 10.0)));\\n    xlat_mutableang2 = (((q1 * 3.14) * float(n_2)) / 10.0);\\n    float tmpvar_10;\\n    tmpvar_10 = cos(xlat_mutableang2);\\n    float tmpvar_11;\\n    tmpvar_11 = sin(xlat_mutableang2);\\n    mat2 tmpvar_12;\\n    tmpvar_12[uint(0)].x = tmpvar_10;\\n    tmpvar_12[uint(0)].y = -(tmpvar_11);\\n    tmpvar_12[1u].x = tmpvar_11;\\n    tmpvar_12[1u].y = tmpvar_10;\\n    xlat_mutableuv2 = (uv_1 * ((q13 * tmpvar_9) * tmpvar_12));\\n    ret1_3 = max (ret1_3, (texture (sampler_main, (xlat_mutableuv2 + 0.5)).xyz * (1.0 - tmpvar_9)));\\n  };\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 1.0;\\n  tmpvar_13.xyz = ((ret1_3 * 2.0) + ((\\n    (bass_att * xlat_mutablers0.y)\\n   * texture (sampler_main, \\n    ((uv_1 * q12) + vec2(0.5, 0.0))\\n  ).yzx) * clamp (\\n    (1.0 - (ret1_3 * 32.0))\\n  , 0.0, 1.0)));\\n  ret = tmpvar_13.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.965,echo_zoom:1.483827,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,wave_brighten:0,wrap:0,darken_center:1,darken:1,wave_a:.001,wave_scale:1.285751,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:.01,warpscale:1.470245,zoomexp:4.778023,zoom:.998162,warp:.01,sx:1.001828,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.005,ob_r:1,ob_g:.5,ob_b:.5,ob_a:1,ib_size:.5,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:4.800001,mv_dx:.4,mv_l:1,mv_r:0,mv_g:.5,mv_a:.1},shapes:[{baseVals:{enabled:1,sides:3,additive:1,thickoutline:1,textured:1,x:1,y:.59,rad:.559231,ang:3.39292,tex_zoom:100,r:0,g:1,b:1,g2:0,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"\",frame_eqs_str:\"a.x=.1*Math.sin(div(a.time,10))+.5+.1*a.treb_att;\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:6,textured:1,x:.3,y:.7,rad:1.089252,ang:.816814,tex_ang:3.141592,tex_zoom:.504215,g:1,b:1,r2:1,b2:1,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:1,sides:3,textured:1,rad:.284278,ang:3.141593,tex_ang:4.900885,tex_zoom:2.987755,g:1,b:1,r2:.95,b2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:'a[\"var\"]=0;',frame_eqs_str:'a.ang=div(a.time,10);a.tex_zoom=3.4+.03*a.bass;a[\"var\"]=above(a.bass_att,.7);a.a=a[\"var\"];a.a2=a[\"var\"];a.border_a=a[\"var\"];'}],waves:[{baseVals:{enabled:1,usedots:1,thick:1,additive:1,r:0,a:.06},init_eqs_str:\"a.px=0;a.xoffset2=0;a.py=0;a.xoffset1=0;a.pheight=0;a.pphase=0;a.yspout=0;a.pphase2=0;a.xspout=0;a.lrorient=0;a.yheight=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.xspout=.5;a.yspout=-.01;a.pphase=9999*a.sample*a.sample*.0001;a.pphase2=.1+.01*mod(3349*a.sample*a.sample,100);a.pheight=.002*mod(9893*a.sample,100);a.yheight=.01*mod(1231*a.sample*a.sample,100);a.r=.01*mod(5454*a.sample,100)*Math.abs(Math.sin(.25*a.time));a.g=.01*mod(9954*a.sample,100);a.xoffset1=Math.cos(a.time*a.pphase2+a.pphase)*a.pheight;a.xoffset2=-1*Math.cos(a.time*a.pphase2+a.pphase)*a.pheight;a.lrorient=.00001<Math.abs(below(Math.cos(a.time*a.pphase2+a.pphase),Math.cos((a.time-\\n.1)*a.pphase2+a.pphase)))?0:1;a.px=.00001<Math.abs(equal(a.lrorient,0))?a.xspout-a.pheight+a.xoffset1:a.xspout+a.pheight-a.xoffset2;a.py=a.yspout+Math.abs(Math.sin(a.time*a.pphase2+a.pphase))*a.yheight;a.x=a.px;a.y=a.py;\"},{baseVals:{enabled:1,usedots:1,thick:1,additive:1,r:0},init_eqs_str:\"a.pphase=0;a.pheight=0;a.xspout=0;a.yspout=0;a.px=0;a.py=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.pphase=5671*a.sample*Math.cos(.0001*a.time);a.pheight=.005*mod(7654*a.sample,100)*Math.sin(.2*a.time);a.xspout=.00001<Math.abs(below(Math.abs(Math.sin(.2*a.time)),.005))?.3+.01*randint(40):a.xspout;a.yspout=.00001<Math.abs(below(Math.abs(Math.sin(.2*a.time)),.005))?.3+.01*randint(40):a.yspout;a.px=a.xspout+Math.cos(a.time+a.pphase)*a.pheight;a.py=a.yspout+Math.sin(a.time+a.pphase)*a.pheight;a.x=a.px;a.y=a.py;a.a=Math.abs(.3*Math.sin(.2*a.time)+.1*a.treb_att);a.r=2*a.treb;\\n\"},{baseVals:{enabled:1,thick:1,additive:1,b:0},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.yr=0;a.xs=0;a.yp=0;a.xr=0;a.q1=0;a.xq=0;a.flip=0;a.ca=0;a.ys=0;a.sa=0;a.zq=0;a.phs=0;a.ang=0;a.tm=0;a.zp=0;a.zr=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+4*a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=(.1*a.flip-.05)*a.sample;a.zp=0;a.ang=20*a.tm+.4*Math.sin(76*a.tm+4*a.time);a.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.xr=a.xp*a.sa+a.yp*a.ca;a.yr=a.xp*a.ca-a.yp*a.sa;a.zr=a.zp;a.xp=a.xr;a.yp=a.yr+.05+.2*(.5*Math.sin(a.tm)+.5)+.05;a.zp=a.zr;a.ang=Math.sin(2*a.tm);a.xq=a.xp;a.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.yq=a.yp*a.sa+a.zp*a.ca;a.zq=a.yp*a.ca-a.zp*a.sa;a.ang=8*a.tm;\\na.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.xp=a.xq*a.sa+a.yq*a.ca;a.yp=a.xq*a.ca-a.yq*a.sa;a.zp=a.zq;a.zp-=.3;a.ang=3.14+2.5*Math.sin(2*a.tm-.5);a.xq=a.xp;a.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.yq=a.yp*a.sa+a.zp*a.ca;a.zq=a.yp*a.ca-a.zp*a.sa;a.ang=-1+Math.cos(3*a.tm+.5);a.xp=a.xq*a.sa+a.yq*a.ca;a.yp=a.xq*a.ca-a.yq*a.sa;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(1*a.tm)-1.05;a.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.xq=a.xp*a.sa+a.zp*a.ca;a.yq=a.yp;a.zq=a.xp*a.ca-a.zp*a.sa;a.ang=Math.cos(a.tm);\\na.xp=a.xq;a.sa=Math.sin(a.ang);a.ca=Math.cos(a.ang);a.yp=a.yq*a.ca-a.zq*a.sa;a.zp=a.yq*a.sa+a.zq*a.ca;a.zp+=1.5;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=1-a.sample;\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.vol=0;a.mtime=0;a.q1=0;\",frame_eqs_str:\"a.decay=.999;a.wrap=Math.sin(10*a.time);a.mv_dx=a.bass;a.ib_a=.005;a.vol=.25*(a.bass+a.mid+a.treb);a.vol*=a.vol;a.mtime+=.01*a.vol*div(55,a.fps);a.q1=.5*a.time;\",pixel_eqs_str:\"a.zoom=1.005-div(a.rad,100);a.rot=div(a.rad,600);a.sy=-1;\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1.006596,echo_alpha:.5,additivewave:1,wave_thick:1,wave_brighten:0,wrap:0,wave_a:4.099998,wave_scale:.01,wave_smoothing:.63,wave_mystery:-.4,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.999513,warp:.01,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.005,ob_r:.01,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:.5,mv_r:.35,mv_g:.35,mv_b:.35,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.q8=0;a.oldq8=0;\",frame_eqs_str:\"a.ob_r=.5+.5*Math.sin(2*a.time);a.ob_g=.5+.5*Math.sin(1.23*a.time);a.ob_b=.5+.5*Math.sin(1.321*a.time);a.wave_a=0;a.q8=a.oldq8+.003*div(pow(1.2*a.bass+.4*a.bass_att+.1*a.treb+.1*a.treb_att+.1*a.mid+.1*a.mid_att,6),a.fps);a.oldq8=a.q8;a.warp=0;\",pixel_eqs_str:\"a.rot=.1*(a.rad+Math.cos(5+5*Math.sin(1.211*a.q8)*a.x-.5)-Math.sin((5+5*Math.sin(.973*a.q8))*a.y-.5));a.dx=.005*(Math.cos(5+5*Math.sin(1.311*a.q8)*a.x-.5)-Math.sin((5+5*Math.sin(.9431*a.q8))*a.y-.5));a.dy=.005*(Math.cos(5+5*Math.sin(1.021*a.q8)*a.x-.5)-Math.sin((5+5*Math.sin(.987*a.q8))*a.y-.5));a.zoom=1-.005*(a.rad+Math.cos(5+5*Math.sin(.943*a.q8)*a.x-.5)-Math.sin((5+5*Math.sin(1.0961*a.q8))*a.y-.5));\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:1,echo_zoom:1,wave_mode:7,additivewave:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:100,wave_scale:.438649,wave_smoothing:.5,modwavealphastart:.5,modwavealphaend:1,zoomexp:.999996,fshader:1,dx:1e-5,dy:1e-5,warp:.01,wave_y:.976,ob_size:.005,ob_r:.4,ob_g:.3,ob_a:1,ib_r:1,ib_g:.6,ib_b:0,ib_a:1,mv_x:24.959999,mv_y:19.199999,mv_l:.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.q1=0;a.thresh=0;a.dx_r=0;a.dy_r=0;\",frame_eqs_str:\"a.warp=0;a.wave_r=.5+.5*Math.sin(.894*a.time);a.wave_g=.5+.5*Math.sin(1.14*a.time);a.wave_b=.5+.5*Math.sin(3-a.bass_att);a.thresh=2*above(a.bass_att,a.thresh)+(1-above(a.bass_att,a.thresh))*((a.thresh-1.3)*(.9+.1*Math.sin(2.8*a.time))+1.3);a.dx_r=.004*equal(a.thresh,2)*Math.sin(5*a.time)+(1-equal(a.thresh,2))*a.dx_r;a.dy_r=.004*equal(a.thresh,2)*Math.sin(6*a.time)+(1-equal(a.thresh,2))*a.dy_r;a.q1=a.thresh;a.dx=1.1*a.dx_r;a.dy=1.1*a.dy_r;a.dx+=.00001<Math.abs(above(a.bass,1.35))?\\n31*a.dx_r:0;a.dy=.00001<Math.abs(above(a.bass,1.3))?0:a.dy;a.decay=.995+.004*Math.sin(.369*a.time)+.001*Math.sin(1.54*a.time);\",pixel_eqs_str:\"a.zoom-=.01*a.q1*a.rad;a.zoomexp=1+.2*(a.rad-.2*a.q1);a.sx-=Math.cos(a.y*(6.28+3.14*Math.sin(a.time)))*(.009+.003*Math.sin(2.18*a.time))*Math.sin(.3*a.time);a.rot=.001*Math.sin(3.14*a.x)*Math.sin(.67*a.time);\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.94,echo_zoom:.9998,echo_alpha:.4999,echo_orient:3,wave_mode:1,additivewave:1,wave_dots:1,wave_brighten:0,wave_a:1.254574,wave_scale:.45029,wave_smoothing:0,zoomexp:1.008151,zoom:.659411,warp:.01,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.005,ob_r:1,ob_a:1,ib_size:.005,ib_r:0,ib_g:0,ib_b:0,ib_a:.9,mv_x:64,mv_y:48,mv_l:0,mv_r:0,mv_g:.7,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.bblock=0;a.grid=0;a.q6=0;a.q1=0;a.q5=0;a.tth=0;a.tblock=0;a.bpulse=0;a.pulse=0;a.mblock=0;a.q4=0;a.mpulse=0;a.mod_state=0;a.bth=0;a.mres=0;a.tpulse=0;a.tres=0;a.le=0;a.ccl=0;a.q2=0;a.bres=0;a.q3=0;a.mth=0;a.q7=0;a.q8=0;\",frame_eqs_str:\"a.warp=0;a.le=1.5+2*Math.sin(a.bass_att);a.bpulse=band(above(a.le,a.bth),above(a.le-a.bth,a.bblock));a.bblock=a.le-a.bth;a.bth=.00001<Math.abs(above(a.le,a.bth))?a.le+div(114,a.le+10)-7.407:a.bth+div(.07*a.bth,a.bth-12)+.1*below(a.bth,2.7)*(2.7-a.bth);a.bth=.00001<Math.abs(above(a.bth,6))?6:a.bth;a.bres=a.bpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.bpulse)*a.bres;a.le=1.5+2*Math.sin(a.treb_att);a.tpulse=band(above(a.le,a.tth),above(a.le-a.tth,a.tblock));a.tblock=a.le-a.tth;a.tth=\\n.00001<Math.abs(above(a.le,a.tth))?a.le+div(114,a.le+10)-7.407:a.tth+div(.07*a.tth,a.tth-12)+.1*below(a.tth,2.7)*(2.7-a.tth);a.tth=.00001<Math.abs(above(a.tth,6))?6:a.tth;a.tres=a.tpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.tpulse)*a.tres;a.le=1.5+2*Math.sin(a.mid_att);a.mpulse=band(above(a.le,a.mth),above(a.le-a.mth,a.mblock));a.mblock=a.le-a.mth;a.mth=.00001<Math.abs(above(a.le,a.mth))?a.le+div(114,a.le+10)-7.407:a.mth+div(.07*a.mth,a.mth-12)+.1*below(a.mth,2.7)*(2.7-a.mth);a.mth=.00001<Math.abs(above(a.mth,\\n6))?6:a.mth;a.mres=a.mpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.mpulse)*a.mres;a.pulse=.00001<Math.abs(above(Math.abs(a.pulse),3.14))?-3.14:a.pulse+.003*(a.bth+a.mth+a.tth);a.q1=a.bres;a.q2=a.tres;a.q3=a.mres;a.q4=Math.sin(a.pulse);a.mod_state=(above(a.q1,0)+above(a.q2,0)+above(a.q3,0))*(1+above(a.q4,0));a.ccl=a.ccl+a.tpulse+a.mpulse-a.bpulse;a.q5=Math.cos(a.pulse*(.5+.1*a.mod_state));a.q6=Math.sin(a.pulse*(.5+pow(.25,a.mod_state)));a.q7=a.mod_state;a.q8=a.ccl;a.ob_r=.5+.5*Math.cos(a.q1+a.q7);a.ob_g=\\n.5+.5*Math.cos(3.14*a.q2+a.q7);a.ob_b=.5+.5*Math.cos(2*a.q3+Math.sin(.0816*a.time));a.ib_size=.025+.02*a.q2;a.ob_size=.03+.02*a.q3-.002*a.q7;a.wave_r=.5+.5*Math.sin(a.q1*a.q7+2.183*a.time);a.wave_g=.5+.5*Math.sin(3*a.q2+1.211*a.time);a.wave_b=.5+.5*Math.sin(a.q3+1.541*a.time);a.ob_a=.8+.2*a.q2;a.zoom+=.01*a.q4;\",pixel_eqs_str:\"a.grid=mod(pow(2*Math.sin(a.rad*a.q6*a.q2+a.x*a.y*a.q6*a.q3),1+mod(a.q7,5)),2);a.rot=bnot(a.grid)+a.grid*a.q4;a.sx+=.003*Math.sin((a.q2+.5)*a.x);a.sy+=.003*Math.sin((a.q1+3.4)*a.y);a.zoom+=.11*Math.cos(3.14*a.rad)*a.q4;\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:.96,echo_zoom:2.216266,echo_alpha:.78,wave_mode:5,additivewave:1,wave_dots:1,wave_brighten:0,brighten:1,wave_a:1.254574,wave_scale:.931011,wave_smoothing:0,zoomexp:1.0081,fshader:.4,zoom:.820774,cx:.4999,warp:.01,sx:.999998,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.005,ob_r:1,ob_a:1,ib_size:.005,ib_r:0,ib_g:0,ib_b:0,ib_a:.9,mv_x:0,mv_y:0,mv_l:.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.bblock=0;a.grid=0;a.q6=0;a.snee=0;a.q1=0;a.q5=0;a.tth=0;a.tblock=0;a.bpulse=0;a.pulse=0;a.mblock=0;a.q4=0;a.snur=0;a.mpulse=0;a.mod_state=0;a.bth=0;a.mres=0;a.tpulse=0;a.tres=0;a.le=0;a.ccl=0;a.q2=0;a.bres=0;a.q3=0;a.mth=0;a.q7=0;a.q8=0;\",frame_eqs_str:\"a.warp=0;a.le=1.5+2*Math.sin(a.bass_att);a.bpulse=band(above(a.le,a.bth),above(a.le-a.bth,a.bblock));a.bblock=a.le-a.bth;a.bth=.00001<Math.abs(above(a.le,a.bth))?a.le+div(114,a.le+10)-7.407:a.bth+div(.07*a.bth,a.bth-12)+.1*below(a.bth,2.7)*(2.7-a.bth);a.bth=.00001<Math.abs(above(a.bth,6))?6:a.bth;a.bres=a.bpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.bpulse)*a.bres;a.le=1.5+2*Math.sin(a.treb_att);a.tpulse=band(above(a.le,a.tth),above(a.le-a.tth,a.tblock));a.tblock=a.le-a.tth;a.tth=\\n.00001<Math.abs(above(a.le,a.tth))?a.le+div(114,a.le+10)-7.407:a.tth+div(.07*a.tth,a.tth-12)+.1*below(a.tth,2.7)*(2.7-a.tth);a.tth=.00001<Math.abs(above(a.tth,6))?6:a.tth;a.tres=a.tpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.tpulse)*a.tres;a.le=1.5+2*Math.sin(a.mid_att);a.mpulse=band(above(a.le,a.mth),above(a.le-a.mth,a.mblock));a.mblock=a.le-a.mth;a.mth=.00001<Math.abs(above(a.le,a.mth))?a.le+div(114,a.le+10)-7.407:a.mth+div(.07*a.mth,a.mth-12)+.1*below(a.mth,2.7)*(2.7-a.mth);a.mth=.00001<Math.abs(above(a.mth,\\n6))?6:a.mth;a.mres=a.mpulse*Math.sin(a.pulse+.5*a.le)+bnot(a.mpulse)*a.mres;a.pulse=.00001<Math.abs(above(Math.abs(a.pulse),3.14))?-3.14:a.pulse+.003*(a.bth+a.mth+a.tth);a.q1=a.bres;a.q2=a.tres;a.q3=a.mres;a.q4=Math.sin(a.pulse);a.mod_state=(above(a.q1,0)+above(a.q2,0)+above(a.q3,0))*(1+above(a.q4,0));a.ccl=a.ccl+a.tpulse+a.mpulse-a.bpulse;a.q5=Math.cos(a.pulse*(.5+.1*a.mod_state));a.q6=Math.sin(a.pulse*(.5+pow(.25,a.mod_state)));a.q7=a.mod_state;a.q8=a.ccl;a.ob_r=.5+.5*Math.cos(a.q1+a.q7);a.ob_g=\\n.5+.5*Math.cos(3.14*a.q2+a.q7);a.ob_b=.5+.5*Math.cos(2*a.q3+Math.sin(.0816*a.time));a.ib_size=.025+.02*a.q2;a.ob_size=.03+.02*a.q3-.002*a.q7;a.wave_r=.5+.5*Math.sin(a.q1*a.q7+2.183*a.time);a.wave_g=.5+.5*Math.sin(3*a.q2+1.211*a.time);a.wave_b=.5+.5*Math.sin(a.q3+1.541*a.time);a.ob_a=.8+.2*a.q2;a.rot=1;a.cx+=.05*a.q4;a.cy+=.05*a.q5;a.zoom=.95+.05*a.q6;\",pixel_eqs_str:\"a.snee=bnot(above(Math.sin(a.ang)-a.x,.5)*above(a.q2,0)+above(a.y-Math.cos(a.ang),.5)*above(a.q1,0));a.snur=bnot(below(a.x,.5)*above(a.q3,0)+below(a.y,.5)*below(a.q7,4));a.grid=Math.sin(sigmoid(Math.sin(6.28*a.y*a.q2),Math.sin(6.28*a.x*a.q6))*(10+a.q7));a.zoom=a.zoom+.02*Math.cos(2*a.rad+a.rad*a.q2)*sign(a.snee)-.04*Math.sin(3.14*a.rad*a.q3-3.14*Math.cos(3.14*a.rad*a.snur-3.14*a.q6));a.sx=.00001<Math.abs(below(a.x,.5)*below(a.y,.5))?a.sx+.2*a.q4*a.snur:1+.1*a.q2*a.grid;a.sy=\\n.00001<Math.abs(below(a.x,.5)*below(a.y,.5))?a.sy+.2*a.q5*a.snee:1+.1*a.q2*a.grid;\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:4.990001,decay:1,echo_zoom:10.784553,wave_mode:7,additivewave:1,wave_dots:1,wave_brighten:0,wrap:0,wave_a:.997938,wave_scale:1.990516,wave_smoothing:0,wave_mystery:-1,modwavealphastart:.5,modwavealphaend:1,warpscale:.999998,zoomexp:.999985,fshader:1,zoom:.9999,dy:1e-5,warp:.01,wave_r:.400001,wave_g:.4,wave_y:1,ob_size:0,ob_r:.300001,ob_g:1,ob_b:.3,ob_a:.100001,ib_size:.005,ib_r:0,ib_g:0,ib_b:0,ib_a:.5,mv_x:8.960042,mv_y:12.960033,mv_dx:-.26,mv_dy:.44,mv_l:5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"},{baseVals:{},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"\"}],init_eqs_str:\"a.maxdbass=0;a.f=0;a.ttan1=0;a.avgbass=0;a.dbass=0;a.q1=0;a.prevavgbass=0;a.amt=0;a.cheat=0;a.totx=0;a.flip=0;a.lastbeat=0;a.ttan2=0;a.bpm=0;a.pctg=0;a.prevrot=0;a.interval=0;a.q4=0;a.pbass=0;a.toty=0;a.beat=0;a.vol=0;a.q2=0;a.dist=0;a.lastflip=0;a.prevvol=0;a.q3=0;a.isleftytonosy=0;a.sure=0;a.flip=-1;\",frame_eqs_str:\"a.warp=0;a.decay=1;a.vol=div(.75*(a.bass_att+a.mid_att+a.treb_att),3)+.25*a.prevvol;a.prevavgbass=a.avgbass;a.avgbass+=.01*(a.bass-a.avgbass);a.q4=Math.max(1.001*a.avgbass-.999*a.prevavgbass,0);a.q4=Math.min(a.q4,.006);a.sure=.00001<Math.abs(equal(a.sure,0))?.6:a.sure;a.interval=.00001<Math.abs(equal(a.interval,0))?40:a.interval;a.lastbeat=.00001<Math.abs(equal(a.lastbeat,0))?a.frame-a.fps:a.lastbeat;a.dbass=div(a.bass-a.pbass,a.fps);a.beat=above(a.dbass,.6*a.maxdbass)*above(a.frame-\\na.lastbeat,div(a.fps,3));a.sure=.00001<Math.abs(a.beat*below(Math.abs(a.frame-(a.interval+a.lastbeat)),div(a.fps,5)))?Math.min(.095+a.sure,1):a.beat*(a.sure-.095)+(1-a.beat)*a.sure*.9996;a.sure=Math.max(.5,a.sure);a.cheat=.00001<Math.abs(above(a.frame,a.lastbeat+a.interval+Math.floor(div(a.fps,10)))*above(a.sure,.91))?1:a.cheat;a.beat=.00001<Math.abs(a.cheat)?1:a.beat;a.sure=.00001<Math.abs(a.cheat)?.95*a.sure:a.sure;a.maxdbass=Math.max(.999*a.maxdbass,a.dbass);a.maxdbass=Math.max(.012,a.maxdbass);\\na.maxdbass=Math.min(.02,a.maxdbass);a.interval=.00001<Math.abs(a.beat)?a.frame-a.lastbeat:a.interval;a.lastbeat=.00001<Math.abs(a.beat)?a.frame-a.cheat*Math.floor(div(a.fps,10)):a.lastbeat;a.cheat=0;a.pbass=a.bass;a.lastflip=.00001<Math.abs(above(div(a.bass,a.avgbass),2)*above(a.frame-a.lastflip,100)*a.beat)?a.frame:a.lastflip;a.flip=.00001<Math.abs(equal(a.frame,a.lastflip))?Math.abs(a.flip)-1:a.flip;a.wave_mystery=a.flip;a.ob_size=div(.08*below(a.frame-a.lastbeat,div(a.fps,8))*(a.frame-a.lastbeat),\\na.fps);a.f=Math.abs(Math.cos(div(a.time,8)+.54+Math.sin(div(a.time,3)+1.075)));a.ob_r=1*a.f+(1-a.f);a.ob_g=.3*a.f+(1-a.f);a.ob_b=.3*a.f+.3*(1-a.f);a.f=div(a.frame-a.lastbeat,a.interval);a.f*=above(a.f,.8)*below(a.f,1);a.f=Math.max(0,a.f);a.f=Math.min(a.f,1);a.wave_g=.4+.6*a.f;a.wave_b=.4+.6*(1-a.f);a.q1=div(3.1416*(a.wave_mystery+1),2);a.q2=.25*Math.cos(a.time+Math.abs(2*Math.sin(2*a.time+2.311)*(a.vol-a.amt))*Math.sin(7.45*a.time+.876));a.q3=-a.q2;a.amt+=.05*(a.vol-a.amt);a.prevvol=a.vol;a.bpm+=\\n.01*(div(60*a.fps,a.interval)-a.bpm);a.monitor=0*a.pctg+1*a.bpm;\",pixel_eqs_str:\"a.x-=.5;a.y=-(a.y-.5);a.ttan1=Math.tan(a.q1+1.5708)*(a.x-a.q3)-a.y+a.q2;a.ttan2=Math.tan(a.q1+1.5708);a.isleftytonosy=above(a.ttan1*sign(3.1416-a.q1),0);a.dist=div(Math.abs(a.ttan1),sqrt(a.ttan2*a.ttan2+1));a.totx=div(.5*Math.cos(a.q1)*sign(a.isleftytonosy-.5)*sqr(a.dist),.5-a.q2);a.toty=div(-.5*Math.sin(a.q1)*sign(a.isleftytonosy-.5)*sqr(a.dist),.5-a.q2);a.dx+=a.totx;a.dy+=a.toty;a.prevrot=a.q1;a.zoom=1-div(25*a.q4*sqrt(pow(.5-a.dist,3)),Math.abs(.5-a.q2));\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:.997,echo_zoom:.996629,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001185,wave_scale:.01,wave_smoothing:.27,wave_mystery:-.38,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.999514,warp:.01,ob_size:0,ob_r:.01,ob_a:1,ib_size:0,ib_r:1,ib_g:1,ib_b:1,ib_a:1,mv_x:64,mv_y:48,mv_l:.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0},shapes:[{baseVals:{enabled:1,sides:3,rad:.34,ang:.53,r:.56,g:.36,r2:.9,a2:.5,border_a:0},init_eqs_str:\"a.angle=0;a.bassspin=0;a.q1=0;a.q2=0;a.bassspin=0;a.angle=0;\",frame_eqs_str:\"a.ang=a.angle;a.bassspin=.00001<Math.abs(above(.05*a.bass,a.bassspin))?a.bassspin+.001:a.bassspin-.001;a.bassspin*=above(a.bassspin,0);a.angle+=a.bassspin;a.r=.3*a.bass;a.g=.3*a.treb;a.b=.3*a.mid;a.r2=.8+.2*Math.sin(1.2*a.time);a.g2=.8+.2*Math.sin(.9777*a.time);a.b2=.8+.2*Math.sin(.7005*a.time);a.border_a=1*above(a.bass+a.treb+a.mid,5);a.x=.5+a.q1;a.y=.5+a.q2;\"},{baseVals:{enabled:1,sides:100,additive:1,textured:1,rad:.108073,g:1,b:1,r2:1,b2:1,a2:1,border_a:0},init_eqs_str:\"a.bassspin=0;a.xpos=0;a.ypos=0;a.q1=0;a.q2=0;a.bassspin=0;a.xpos=.25;a.ypos=.25;\",frame_eqs_str:\"a.bassspin=.00001<Math.abs(above(.05*a.bass,a.bassspin))?a.bassspin+.001:a.bassspin-.001;a.bassspin*=above(a.bassspin,0);a.xpos+=a.bassspin;a.ypos+=a.bassspin;a.x=.5+a.q1+.13*Math.sin(a.xpos);a.y=.5+a.q2+.18*Math.cos(a.ypos);\"},{baseVals:{enabled:1,sides:29,additive:1,textured:1,rad:.105693,g:1,b:1,r2:1,b2:1,a2:1,border_a:0},init_eqs_str:\"a.bassspin=0;a.xpos=0;a.ypos=0;a.q1=0;a.q2=0;a.bassspin=0;a.xpos=2.3;a.ypos=2.3;\",frame_eqs_str:\"a.bassspin=.00001<Math.abs(above(.05*a.bass,a.bassspin))?a.bassspin+.001:a.bassspin-.001;a.bassspin*=above(a.bassspin,0);a.xpos+=a.bassspin;a.ypos+=a.bassspin;a.x=.5+a.q1+.13*Math.sin(a.xpos);a.y=.5+a.q2+.18*Math.cos(a.ypos);\"},{baseVals:{enabled:1,sides:100,additive:1,textured:1,rad:.091434,g:1,b:1,r2:1,b2:1,a2:1,border_a:0},init_eqs_str:\"a.bassspin=0;a.xpos=0;a.ypos=0;a.q1=0;a.q2=0;a.bassspin=0;a.xpos=4.5;a.ypos=4.5;\",frame_eqs_str:\"a.bassspin=.00001<Math.abs(above(.05*a.bass,a.bassspin))?a.bassspin+.001:a.bassspin-.001;a.bassspin*=above(a.bassspin,0);a.xpos+=a.bassspin;a.ypos+=a.bassspin;a.x=.5+a.q1+.13*Math.sin(a.xpos);a.y=.5+a.q2+.18*Math.cos(a.ypos);\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.xpos=0;a.yamptarg=0;a.ydir=0;a.q1=0;a.yaccel=0;a.xamptarg=0;a.xamp=0;a.xspeed=0;a.ypos=0;a.xaccel=0;a.att=0;a.vol=0;a.q2=0;a.yamp=0;a.xdir=0;a.yspeed=0;\",frame_eqs_str:\"a.warp=0;a.decay=.92;a.vol=div(a.bass+a.mid+a.att,6);a.xamptarg=.00001<Math.abs(equal(mod(a.frame,15),0))?Math.min(.5*a.vol*a.bass_att,.5):a.xamptarg;a.xamp+=.5*(a.xamptarg-a.xamp);a.xdir=.00001<Math.abs(above(Math.abs(a.xpos),a.xamp))?-sign(a.xpos):.00001<Math.abs(below(Math.abs(a.xspeed),.1))?2*above(a.xpos,0)-1:a.xdir;a.xaccel=a.xdir*a.xamp-a.xpos-.055*a.xspeed*below(Math.abs(a.xpos),a.xamp);a.xspeed=a.xspeed+a.xdir*a.xamp-a.xpos-.055*a.xspeed*below(Math.abs(a.xpos),a.xamp);\\na.xpos+=.001*a.xspeed;a.yamptarg=.00001<Math.abs(equal(mod(a.frame,15),0))?Math.min(.3*a.vol*a.treb_att,.5):a.yamptarg;a.yamp+=.5*(a.yamptarg-a.yamp);a.ydir=.00001<Math.abs(above(Math.abs(a.ypos),a.yamp))?-sign(a.ypos):.00001<Math.abs(below(Math.abs(a.yspeed),.1))?2*above(a.ypos,0)-1:a.ydir;a.yaccel=a.ydir*a.yamp-a.ypos-.055*a.yspeed*below(Math.abs(a.ypos),a.yamp);a.yspeed=a.yspeed+a.ydir*a.yamp-a.ypos-.055*a.yspeed*below(Math.abs(a.ypos),a.yamp);a.ypos+=.001*a.yspeed;a.q1=a.ypos;a.q2=a.xpos;\",pixel_eqs_str:\"a.zoom+=a.q1*a.q2*2;a.rot=10+a.rad*a.treb*.1;\",warp:\"\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:3,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wrap:0,darken_center:1,solarize:1,wave_a:.001,zoom:.97,rot:-6.27999,warp:52e-5,wave_r:0,wave_g:0,wave_b:0,ob_r:1,ob_g:1,ob_b:1,mv_r:.8,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,textured:1,x:.25,y:.75,rad:4.44708,tex_zoom:.22746,r:0,a:.1,g2:0,a2:.2,border_r:0,border_g:0,border_a:0},init_eqs_str:\"a.q1=0;a.tex_capture=0;a.q3=0;a.tex_saw=.4;\",frame_eqs_str:\"a.ang=.2*a.q1;a.tex_capture=above(a.q3,1);a.tex_zoom=.6;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,smoothing:0},init_eqs_str:\"a.q1=0;a.speed=0;a.v=0;a.xs=0;a.ys=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.q1=0;a.speed=.8*a.bass_att;a.v=1E6*a.sample+a.value2*a.bass*.1;a.xs+=Math.sin(a.v)*a.speed*Math.atan(1.51*a.v);a.ys+=Math.sin(a.v)*a.speed*Math.atan(10*a.v);a.x=.5+.5*Math.sin(.1*a.xs)*Math.cos(.2*a.time+a.xs);a.y=.5+.5*Math.sin(.12*a.ys)*Math.cos(.1*a.time+a.xs);a.x=.8*a.x+.1;a.y=.8*a.y+.1;a.r=.5*Math.sin(1.22*a.time)+.6;a.g=.4+.4*Math.sin(1.307*a.time+2*a.y);a.b=.4+.4*Math.sin(1.959*a.time+2*a.x);a.xs=.00001<Math.abs(above(a.xs,1E3))?0:a.xs;a.ys=.00001<Math.abs(above(a.ys,\\n1E3))?0:a.ys;\"},{baseVals:{enabled:1,thick:1,smoothing:0},init_eqs_str:\"a.q1=0;a.speed=0;a.v=0;a.xs=0;a.ys=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.q1=0;a.speed=.8*a.bass_att;a.v=1E6*a.sample+a.value2*a.bass*.1;a.xs+=Math.sin(a.v)*a.speed*Math.atan(1.51*a.v);a.ys+=Math.sin(a.v)*a.speed*Math.atan(10*a.v);a.x=.5+.5*Math.sin(.1*a.xs)*Math.cos(.2*a.time+a.xs);a.y=.5+.5*Math.sin(.14*a.ys)*Math.cos(.1*a.time+a.xs);a.x=.8*a.x+.1;a.y=.8*a.y+.1;a.x=.6*a.x+.2;a.y=.6*a.y+.2;a.r=.5*Math.sin(1.322*a.time)+.6;a.g=.4+.4*Math.sin(1.5407*a.time+2*a.y);a.b=.4+.4*Math.sin(1.759*a.time+2*a.x);a.xs=.00001<Math.abs(above(a.xs,1E3))?0:a.xs;\\na.ys=.00001<Math.abs(above(a.ys,1E3))?0:a.ys;\"},{baseVals:{enabled:1,thick:1,smoothing:0},init_eqs_str:\"a.q1=0;a.speed=0;a.v=0;a.xs=0;a.ys=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.q1=0;a.speed=.8*a.bass_att;a.v=1E6*a.sample+a.value2*a.bass*.1;a.xs+=Math.sin(a.v)*a.speed*Math.atan(1.51*a.v);a.ys+=Math.sin(a.v)*a.speed*Math.atan(10*a.v);a.x=.5+.5*Math.sin(.1*a.xs)*Math.cos(.2*a.time+a.xs);a.y=.5+.5*Math.sin(.14*a.ys)*Math.cos(.1*a.time+a.xs);a.x=.8*a.x+.1;a.y=.8*a.y+.1;a.x=.25*a.x+.375;a.y=.25*a.y+.375;a.r=.5*Math.sin(1.622*a.time)+.6;a.g=.4+.4*Math.sin(1.2407*a.time+2*a.y);a.b=.4+.4*Math.sin(1.359*a.time+2*a.x);a.xs=.00001<Math.abs(above(a.xs,1E3))?\\n0:a.xs;a.ys=.00001<Math.abs(above(a.ys,1E3))?0:a.ys;\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.basstime=0;a.stickybit=0;a.volavg2=0;a.q1=0;a.decay_r=0;a.sample1=0;a.difftime=0;a.diff=0;a.decay_b=0;a.edge=0;a.volavg=0;a.bit2=0;a.vol=0;a.q2=0;a.q3=0;a.basssum=0;a.decay_g=0;a.sample2=0;\",frame_eqs_str:\"a.basstime+=.03*a.bass;a.q1=4*a.basstime;a.basstime=.00001<Math.abs(below(a.basstime,1E3))?1E3:a.basstime;a.basstime+=.03*a.bass_att;a.vol=pow(a.bass+a.mid+a.treb,2);a.basssum=a.vol;a.stickybit=mod(a.time,2);a.volavg+=a.vol*equal(a.stickybit,1);a.sample1+=equal(a.stickybit,1);a.volavg2+=a.vol*equal(a.stickybit,0);a.sample2+=equal(a.stickybit,0);a.edge=bnot(equal(a.bit2,a.stickybit));a.volavg-=a.volavg*a.edge*a.stickybit;a.volavg2-=a.volavg2*a.edge*equal(a.stickybit,0);a.sample1-=\\na.sample1*a.edge*a.stickybit;a.sample2-=a.sample2*a.edge*equal(a.stickybit,0);a.diff=.00001<Math.abs(equal(a.stickybit,1))?div(a.basssum,div(a.volavg2,a.sample2)):0;a.diff=.00001<Math.abs(equal(a.stickybit,0))?div(a.basssum,div(a.volavg,a.sample1)):a.diff;a.q3=a.diff;a.bit2=mod(a.time,2);a.difftime+=.03*a.diff;a.q2=a.difftime;a.difftime=.00001<Math.abs(above(a.difftime,2E3))?0:a.difftime;a.monitor=3.14*Math.abs(Math.cos(a.time));a.mv_a=above(a.diff,10);\",pixel_eqs_str:\"a.zoom=1+.05*a.q3*a.rad;a.decay_r=.2*a.rad*Math.sin(.35*a.q2)+.85+.1*Math.sin(a.q2);a.decay_g=.2*a.rad*Math.sin(.5*a.q2)+.85+.1*Math.sin(.7*a.q2);a.decay_b=.2*a.rad*Math.sin(.4*a.q2)+.85+.1*Math.sin(.8*a.q2);a.rot=0;\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (vec2(1.0, 0.0) * texsize.z);\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (vec2(0.0, 1.0) * texsize.z);\\n  ret_1 = (((\\n    (texture (sampler_main, (uv + tmpvar_2)).xyz + texture (sampler_main, (uv + tmpvar_2)).xyz)\\n   * 0.5) + (\\n    (texture (sampler_main, (uv + tmpvar_3)).xyz + texture (sampler_main, (uv + tmpvar_3)).xyz)\\n   * 0.5)) - texture (sampler_main, ((\\n    (uv - 0.5)\\n   * 0.9) + 0.5)).xyz);\\n  ret_1 = (ret_1 - 0.4);\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = ret_1;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = ((0.5 - uv) + 0.5);\\n  ret_1 = (mix (texture (sampler_main, uv).xyz, texture (sampler_main, tmpvar_2).xyz, vec3(0.5, 0.5, 0.5)) * 2.0);\\n  ret_1 = (((\\n    ((texture (sampler_blur3, uv).xyz * scale3) + bias3)\\n   * 2.0) + (\\n    ((texture (sampler_blur3, tmpvar_2).xyz * scale3) + bias3)\\n   * 2.0)) + ret_1);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_1;\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.07,decay:.95,echo_zoom:1,echo_alpha:.5,echo_orient:1,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,brighten:1,darken:1,invert:1,wave_a:4.1,wave_scale:1.286,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.99951,warp:.01,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.5,ob_r:.01,ib_size:.26,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,spectrum:1,thick:1},init_eqs_str:\"a.xang=0;a.fov=0;a.yang=0;a.my=0;a.oz=0;a.ayang=0;a.mz=0;a.oy=0;a.mod=0;a.azang=0;a.mx=0;a.vol=0;a.sp=0;a.zang=0;a.axang=0;a.ox=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.sp=1607.68*a.sample;a.vol=.33*(a.bass_att+a.mid_att+a.treb_att);a.vol=.2+.5*(a.value1+a.value2);a.vol=.2;a.mod=.00001<Math.abs(below(a.mid_att,1.8))?a.mid_att+.2:2;a.ox=.5*Math.sin(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.oy=(a.sample-0)*a.mod;a.oz=.5*Math.cos(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.xang=.672*a.time;a.axang=0;a.yang=-1.351*a.time;a.ayang=0;a.zang=-.401*a.time;a.azang=0;a.fov=.6+.2*Math.sin(a.time);a.fov=.5;a.mx=a.ox*Math.cos(a.zang)-a.oy*Math.sin(a.zang);a.my=\\na.ox*Math.sin(a.zang)+a.oy*Math.cos(a.zang);a.ox=a.mx;a.oy=a.my;a.mx=a.ox*Math.cos(a.yang)+a.oz*Math.sin(a.yang);a.mz=-a.ox*Math.sin(a.yang)+a.oz*Math.cos(a.yang);a.ox=a.mx;a.oz=a.mz;a.my=a.oy*Math.cos(a.xang)-a.oz*Math.sin(a.xang);a.mz=a.oy*Math.sin(a.xang)+a.oz*Math.cos(a.xang);a.oy=a.my;a.oz=a.mz;a.oz=Math.abs(a.oz)-2;a.x=div(a.ox*a.fov,a.oz)+.5;a.x=.75*(a.x-.5)+.5;a.y=div(a.oy*a.fov,a.oz)+.5;a.r=1+Math.sin(a.sp);a.b=.5+.5*Math.sin(1.57*a.sample);a.g=.5+.5*Math.cos(1.57*a.sample);a.a=.5+.25*(a.oz+\\n2);\"},{baseVals:{enabled:1,spectrum:1,thick:1},init_eqs_str:\"a.xang=0;a.fov=0;a.yang=0;a.my=0;a.oz=0;a.ayang=0;a.mz=0;a.oy=0;a.mod=0;a.azang=0;a.mx=0;a.vol=0;a.sp=0;a.zang=0;a.axang=0;a.ox=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.sp=1607.68*a.sample;a.vol=.33*(a.bass_att+a.mid_att+a.treb_att);a.vol=.2+.5*(a.value1+a.value2);a.vol=.2;a.mod=.00001<Math.abs(below(a.bass_att,1.8))?a.bass_att+.2:2;a.ox=.5*Math.sin(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.oy=(a.sample-0)*a.mod;a.oz=.5*Math.cos(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.xang=-.321*a.time;a.axang=0;a.yang=1.531*a.time;a.ayang=0;a.zang=-.101*a.time;a.azang=0;a.fov=.6+.2*Math.sin(a.time);a.fov=.5;a.mx=a.ox*Math.cos(a.zang)-a.oy*Math.sin(a.zang);a.my=\\na.ox*Math.sin(a.zang)+a.oy*Math.cos(a.zang);a.ox=a.mx;a.oy=a.my;a.mx=a.ox*Math.cos(a.yang)+a.oz*Math.sin(a.yang);a.mz=-a.ox*Math.sin(a.yang)+a.oz*Math.cos(a.yang);a.ox=a.mx;a.oz=a.mz;a.my=a.oy*Math.cos(a.xang)-a.oz*Math.sin(a.xang);a.mz=a.oy*Math.sin(a.xang)+a.oz*Math.cos(a.xang);a.oy=a.my;a.oz=a.mz;a.oz=Math.abs(a.oz)-2;a.x=div(a.ox*a.fov,a.oz)+.5;a.x=.75*(a.x-.5)+.5;a.y=div(a.oy*a.fov,a.oz)+.5;a.g=1+Math.sin(a.sp);a.r=.5+.5*Math.sin(1.57*a.sample);a.b=.5+.5*Math.cos(1.57*a.sample);a.a=.5+.25*(a.oz+\\n2);\"},{baseVals:{enabled:1,spectrum:1,thick:1},init_eqs_str:\"a.xang=0;a.fov=0;a.yang=0;a.my=0;a.oz=0;a.ayang=0;a.mz=0;a.oy=0;a.mod=0;a.azang=0;a.mx=0;a.vol=0;a.sp=0;a.zang=0;a.axang=0;a.ox=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.sp=1607.68*a.sample;a.vol=.33*(a.bass_att+a.mid_att+a.treb_att);a.vol=.2+.5*(a.value1+a.value2);a.vol=.2;a.mod=.00001<Math.abs(below(a.treb_att,1.8))?a.treb_att+.2:2;a.ox=.5*Math.sin(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.oy=(a.sample-0)*a.mod;a.oz=.5*Math.cos(a.sp)*Math.sin(3.14*a.sample)*a.vol;a.xang=.221*a.time;a.axang=0;a.yang=-.411*a.time;a.ayang=0;a.zang=1.201*a.time;a.azang=0;a.fov=.6+.2*Math.sin(a.time);a.fov=.5;a.mx=a.ox*Math.cos(a.zang)-a.oy*Math.sin(a.zang);a.my=\\na.ox*Math.sin(a.zang)+a.oy*Math.cos(a.zang);a.ox=a.mx;a.oy=a.my;a.mx=a.ox*Math.cos(a.yang)+a.oz*Math.sin(a.yang);a.mz=-a.ox*Math.sin(a.yang)+a.oz*Math.cos(a.yang);a.ox=a.mx;a.oz=a.mz;a.my=a.oy*Math.cos(a.xang)-a.oz*Math.sin(a.xang);a.mz=a.oy*Math.sin(a.xang)+a.oz*Math.cos(a.xang);a.oy=a.my;a.oz=a.mz;a.oz=Math.abs(a.oz)-2;a.x=div(a.ox*a.fov,a.oz)+.5;a.x=.75*(a.x-.5)+.5;a.y=div(a.oy*a.fov,a.oz)+.5;a.b=1+Math.sin(a.sp);a.g=.5+.5*Math.sin(1.57*a.sample);a.r=.5+.5*Math.cos(1.57*a.sample);a.a=.5+.25*(a.oz+\\n2);\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.it=0;a.radm=0;a.mod=0;\",frame_eqs_str:\"a.wave_a=0;\",pixel_eqs_str:\"a.it=.3*Math.sin(.2*a.time);a.radm=.5*a.rad;a.rot=.02*Math.sin(20*(a.radm+a.it));a.mod=Math.sin(5*a.ang);a.mod*=a.mod*a.mod*a.mod*a.mod;a.zoom=1+Math.abs(.01*a.mod);a.zoom*=Math.min(1.05,Math.max(1,Math.max(a.bass,a.treb)));\",warp:\" shader_body { \\n  vec3 xfer_1;\\n  vec3 ret_2;\\n  vec4 tmpvar_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv - 0.5);\\n  tmpvar_3 = texture (sampler_main, (tmpvar_4 + 0.5));\\n  ret_2 = (tmpvar_3.xyz / ((tmpvar_3.x + tmpvar_3.y) + tmpvar_3.z));\\n  ret_2 = texture (sampler_main, ((tmpvar_4 * dot (ret_2, vec3(1.0, 0.975, 0.95))) + 0.5)).xyz;\\n  vec3 tmpvar_5;\\n  tmpvar_5 = clamp (((ret_2 - 0.05) * 99.0), 0.0, 1.0);\\n  xfer_1.x = tmpvar_5.x;\\n  xfer_1.yz = (tmpvar_5.yz * clamp ((\\n    (0.1 - ret_2.xy)\\n   * 99.0), 0.0, 1.0));\\n  ret_2 = (ret_2 + (vec3(-0.014, 0.014, 0.0) * tmpvar_5.xxx));\\n  ret_2 = (ret_2 + (vec3(0.0, -0.08, 0.08) * xfer_1.yyy));\\n  ret_2 = (ret_2 + (vec3(0.0, 0.0, -0.02) * xfer_1.zzz));\\n  ret_2 = (ret_2 + (vec3(5.0, 15.0, 40.0) * (\\n    (texture (sampler_noise_lq, (((uv_orig * texsize.xy) * texsize_noise_lq.zw) + rand_frame.xy)).xyz - 0.5)\\n   / 256.0)));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_2;\\n  ret = tmpvar_6.xyz;\\n }\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:2,gammaadj:1,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wave_a:.001,wave_scale:.958,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:30.965,warpscale:2.572,zoom:1.00901,warp:54e-5,wave_r:0,wave_g:0,wave_b:0,mv_x:25.6,mv_y:9.6,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dx_residual=0;a.dy_residual=0;a.bass_thresh=0;a.rg=0;a.q9=0;\",frame_eqs_str:\"a.wave_r=.85+.25*Math.sin(.437*a.time+1);a.wave_g=.85+.25*Math.sin(.544*a.time+2);a.wave_b=.85+.25*Math.sin(.751*a.time+3);a.rot+=.01*(.6*Math.sin(.381*a.time)+.4*Math.sin(.579*a.time));a.cx+=.21*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.21*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.003*(.6*Math.sin(.234*a.time)+.4*Math.sin(.277*a.time));a.dy+=.003*(.6*Math.sin(.284*a.time)+.4*Math.sin(.247*a.time));a.decay-=.01*equal(mod(a.frame,6),0);a.dx+=\\na.dx_residual;a.dy+=a.dy_residual;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.96*(a.bass_thresh-1.3)+1.3);a.dx_residual=.016*equal(a.bass_thresh,2.13)*Math.sin(7*a.time)+(1-equal(a.bass_thresh,2.13))*a.dx_residual;a.dy_residual=.012*equal(a.bass_thresh,2.13)*Math.sin(9*a.time)+(1-equal(a.bass_thresh,2.13))*a.dy_residual;a.wave_x-=7*a.dx_residual;a.wave_y-=7*a.dy_residual;a.wave_mystery=.03*a.time;a.rg=Math.max(.77*a.rg,.02+.5*Math.min(2,1.3*Math.max(0,a.mid_att-\\n1)));a.q9=a.rg;a.zoom+=.1*a.q9;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = texture (sampler_main, uv).xyz;\\n  ret_1 = (ret_1 + ((ret_1 - \\n    ((((\\n      (texture (sampler_blur1, uv).xyz * scale1)\\n     + bias1) * 0.3) + ((\\n      (texture (sampler_blur2, uv).xyz * scale2)\\n     + bias2) * 0.4)) + (((texture (sampler_blur3, uv).xyz * scale3) + bias3) * 0.3))\\n  ) * 0.3));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 122.0) * (\\n    (clamp ((treb_att - 1.0), 0.0, 1.0) * 0.4)\\n   + 0.3)));\\n  ret_1 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.2, 0.2, 0.2));\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\",comp:\"vec2 xlat_mutabledz;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutableuv3;\\n shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.y = 0.0;\\n  tmpvar_1.x = texsize.z;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = 0.0;\\n  tmpvar_2.y = texsize.w;\\n  vec2 uv_3;\\n  float inten_4;\\n  float dist_5;\\n  vec2 uv1_6;\\n  vec3 ret_7;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_9;\\n  tmpvar_9 = (time / 18.0);\\n  dist_5 = (1.0 - fract((0.25 + tmpvar_9)));\\n  inten_4 = ((sqrt(dist_5) * (1.0 - dist_5)) * 4.0);\\n  uv_3 = (tmpvar_8 * aspect.yx);\\n  xlat_mutableuv3 = (vec2(0.51, 0.55) + (uv_3 * dist_5));\\n  xlat_mutabledz.x = (inten_4 * ((2.0 * \\n    dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29))\\n  ) - (2.0 * \\n    dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29))\\n  )));\\n  xlat_mutabledz.y = (inten_4 * ((2.0 * \\n    dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29))\\n  ) - (2.0 * \\n    dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29))\\n  )));\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (texture (sampler_main, xlat_mutableuv3).xyz * inten_4));\\n  dist_5 = (1.0 - fract((0.5 + tmpvar_9)));\\n  inten_4 = ((sqrt(dist_5) * (1.0 - dist_5)) * 4.0);\\n  uv_3 = (tmpvar_8 * aspect.yx);\\n  xlat_mutableuv3 = (vec2(0.49, 0.55) + (uv_3 * dist_5));\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_4));\\n  dist_5 = (1.0 - fract((0.75 + tmpvar_9)));\\n  inten_4 = ((sqrt(dist_5) * (1.0 - dist_5)) * 4.0);\\n  uv_3 = (tmpvar_8 * aspect.yx);\\n  xlat_mutableuv3 = (vec2(0.51, 0.55) + (uv_3 * dist_5));\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_4));\\n  dist_5 = (1.0 - fract((1.0 + tmpvar_9)));\\n  inten_4 = ((sqrt(dist_5) * (1.0 - dist_5)) * 4.0);\\n  uv_3 = (tmpvar_8 * aspect.yx);\\n  xlat_mutableuv3 = (vec2(0.49, 0.55) + (uv_3 * dist_5));\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_1)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_4 * (\\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 + tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n   - \\n    (2.0 * dot (texture (sampler_main, (xlat_mutableuv3 - tmpvar_2)).xyz, vec3(0.32, 0.49, 0.29)))\\n  )));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_4));\\n  xlat_mutabledz = (xlat_mutabledz * (0.5 + rand_preset.z));\\n  vec2 tmpvar_10;\\n  tmpvar_10 = (2.0 * (rand_preset.xy - 0.5));\\n  uv1_6 = (4.0 * tmpvar_8);\\n  vec2 tmpvar_11;\\n  tmpvar_11 = sin(((uv1_6 + xlat_mutabledz) + tmpvar_10));\\n  vec2 tmpvar_12;\\n  tmpvar_12 = sin(((uv1_6 + \\n    (xlat_mutabledz * 1.4)\\n  ) + tmpvar_10));\\n  vec2 tmpvar_13;\\n  tmpvar_13 = sin(((uv1_6 + \\n    (xlat_mutabledz * 1.8)\\n  ) + tmpvar_10));\\n  vec3 tmpvar_14;\\n  tmpvar_14.x = inversesqrt(dot (tmpvar_11, tmpvar_11));\\n  tmpvar_14.y = inversesqrt(dot (tmpvar_12, tmpvar_12));\\n  tmpvar_14.z = inversesqrt(dot (tmpvar_13, tmpvar_13));\\n  ret_7 = (((\\n    (tmpvar_14 * ((vec3(0.01, 0.01, 0.01) * (1.0 + \\n      (rand_preset.xyz / 2.0)\\n    )) * (0.5 + rand_preset.y)))\\n   * \\n    ((((rand_preset.x - 0.5) * 4.0) * xlat_mutableret1) + (8.0 * (1.0 + rand_preset)).xyz)\\n  ) - (xlat_mutableret1.x * 0.5)) + ((xlat_mutableret1.y + xlat_mutableret1.z) / 3.0));\\n  ret_7 = (ret_7 - ((slow_roam_sin.wzy * roam_cos.zxy) * 0.4));\\n  ret_7 = (ret_7 * (1.0 + ret_7));\\n  vec4 tmpvar_15;\\n  tmpvar_15.w = 1.0;\\n  tmpvar_15.xyz = ret_7;\\n  ret = tmpvar_15.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.995,echo_zoom:1,echo_orient:1,wave_mode:5,wave_brighten:0,wrap:0,wave_a:100,wave_scale:.721,wave_smoothing:.5,modwavealphastart:.5,modwavealphaend:1,fshader:1,dx:1e-5,dy:1e-5,warp:.01,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:0,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:0,mv_y:0,mv_l:1,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.thresh=0;a.dx_r=0;a.dy_r=0;a.xs=0;\",frame_eqs_str:\"a.wave_r=.5+.5*Math.sin(6*a.time);a.wave_g=.5+.5*Math.sin(4.1*a.time);a.wave_b=-1+(1-a.wave_r+1-a.wave_g);a.warp=0;\",pixel_eqs_str:\"a.thresh=2*above(a.bass_att,a.thresh)+(1-above(a.bass_att,a.thresh))*(.96*(a.thresh-1.3)+1.3);a.dx_r=.015*equal(a.thresh,2)*Math.sin(5*a.time)+(1-equal(a.thresh,2))*a.dx_r;a.dy_r=.015*equal(a.thresh,2)*Math.sin(6*a.time)+(1-equal(a.thresh,2))*a.dy_r;a.xs=above(Math.sin(12*a.dx_r*a.bass),0)*a.dy_r*Math.sin(2*a.rad)+below(Math.sin(12*a.dx_r*a.bass),0)*Math.cos(a.time)*a.dx_r*Math.sin(.6*a.rad);a.zoom+=Math.abs(12*a.xs);a.rot+=5*a.xs*Math.cos(1-a.xs*a.rad*12*a.dx_r);a.dx+=a.dx_r;\\na.dy+=a.dy_r;\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (vec2(1280.0, 1024.0) * texsize.zw);\\n  float tmpvar_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv + vec2(0.005, 0.0));\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - vec2(0.005, 0.0));\\n  tmpvar_4 = (((\\n    (texture (sampler_blur2, tmpvar_5).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_6).xyz * scale2)\\n   + bias2)).x * tmpvar_3.x);\\n  float tmpvar_7;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (uv + vec2(0.0, 0.005));\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (uv - vec2(0.0, 0.005));\\n  tmpvar_7 = (((\\n    (texture (sampler_blur2, tmpvar_8).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_9).xyz * scale2)\\n   + bias2)).x * tmpvar_3.y);\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4;\\n  tmpvar_10.y = tmpvar_7;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = (((\\n    (texture (sampler_blur2, tmpvar_5).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_6).xyz * scale2)\\n   + bias2)).x * tmpvar_3.x);\\n  tmpvar_11.y = (((\\n    (texture (sampler_blur2, tmpvar_8).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_9).xyz * scale2)\\n   + bias2)).x * tmpvar_3.y);\\n  ret_2.x = texture (sampler_fw_main, ((uv - (tmpvar_10 * 0.01)) + (tmpvar_11 * 0.003))).x;\\n  vec4 tmpvar_12;\\n  tmpvar_12 = texture (sampler_blur3, uv);\\n  ret_2.x = (ret_2.x + ((ret_2.x - \\n    ((tmpvar_12.xyz * scale3) + bias3)\\n  .x) * 0.1));\\n  ret_2.x = (ret_2.x + 0.004);\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = tmpvar_7;\\n  tmpvar_13.y = -(tmpvar_4);\\n  my_uv_1 = (uv + ((tmpvar_13 * 0.05) * (1.2 - \\n    ((tmpvar_12.xyz * scale3) + bias3)\\n  .y)));\\n  ret_2.z = texture (sampler_fw_main, my_uv_1).z;\\n  vec2 x_14;\\n  x_14 = (my_uv_1 - uv);\\n  ret_2.z = (ret_2.z + ((\\n    ((ret_2.z - ((texture (sampler_blur1, uv).xyz * scale1) + bias1).z) * sqrt(dot (x_14, x_14)))\\n   * 180.0) / sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  )));\\n  ret_2.z = (ret_2.z * 0.8);\\n  ret_2.z = (ret_2.z + 0.004);\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = -(tmpvar_7);\\n  tmpvar_15.y = tmpvar_4;\\n  my_uv_1 = (tmpvar_15 * 0.045);\\n  vec2 tmpvar_16;\\n  tmpvar_16.x = (((\\n    (texture (sampler_blur2, (uv + vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (uv - vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2)).y * tmpvar_3.x);\\n  tmpvar_16.y = (((\\n    (texture (sampler_blur2, (uv + vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (uv - vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2)).y * tmpvar_3.y);\\n  my_uv_1 = (my_uv_1 + (uv - (tmpvar_16 * 0.03)));\\n  ret_2.y = texture (sampler_fw_main, my_uv_1).y;\\n  ret_2.y = (ret_2.y + ((\\n    (ret_2.y - ((texture (sampler_blur3, my_uv_1).xyz * scale3) + bias3).y)\\n   * 0.1) + 0.01));\\n  vec4 tmpvar_17;\\n  tmpvar_17.w = 1.0;\\n  tmpvar_17.xyz = ret_2;\\n  ret = tmpvar_17.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = texture (sampler_main, uv).xyz;\\n  ret_1 = (ret_1 * hue_shader);\\n  vec3 tmpvar_2;\\n  tmpvar_2 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.5, 1.0, 0.1));\\n  ret_1 = tmpvar_2;\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = tmpvar_2;\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.998,echo_zoom:1.421,wave_mode:7,additivewave:1,modwavealphabyvolume:1,darken_center:1,wave_a:1.193,wave_scale:1.489,wave_smoothing:0,modwavealphastart:.87,modwavealphaend:1.09,warpscale:3.138,zoom:1.003,warp:.09218,wave_r:.5,wave_g:.5,wave_b:.5,wave_x:.6,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.t2=0;\",frame_eqs_str:\"a.wave_r+=.365*(.6*Math.sin(4.437*a.time)+.4*Math.sin(3.97*a.time));a.wave_g+=.365*(.6*Math.sin(4.344*a.time)+.4*Math.sin(3.841*a.time));a.wave_b+=.365*(.6*Math.sin(4.251*a.time)+.4*Math.sin(3.055*a.time));a.rot+=.02*(.6*Math.sin(.181*a.time)+.09*Math.sin(-.279*a.time));a.zoom+=.025*(.6*Math.sin(.3131*a.time+2)+.4*Math.sin(-.479*a.time+4));a.decay-=.01*equal(mod(a.frame,6),0);a.t2=6*a.time;a.wave_x=.5+.2*(.6*Math.sin(.374*a.t2)+.4*Math.sin(.294*a.t2));a.wave_y=.5+.2*(.6*Math.sin(.393*\\na.t2)+.4*Math.sin(.223*a.t2));\",pixel_eqs_str:\"a.dx=0;a.dy=0;a.dx+=2*div(Math.cos(29.37*a.y-1.9*a.time),a.pixelsx);a.dy+=2*div(Math.cos(33.21*a.x-1.7*a.time),a.pixelsy);a.dx+=1.5*div(Math.cos(77.55*a.y-2.1*a.time),a.pixelsx);a.dy+=1.5*div(Math.cos(78.32*a.x-2.4*a.time),a.pixelsy);\",warp:\" shader_body { \\n  vec2 dxy_1;\\n  dxy_1.x = cos(((154.56 * uv_orig.y) - time));\\n  dxy_1.y = cos(((154.56 * uv_orig.x) - time));\\n  float tmpvar_2;\\n  tmpvar_2 = (time * 5.0);\\n  dxy_1.x = (dxy_1.x + cos((\\n    (412.16 * uv_orig.y)\\n   - tmpvar_2)));\\n  dxy_1.y = (dxy_1.y + cos((\\n    (412.16 * uv_orig.x)\\n   - tmpvar_2)));\\n  dxy_1.y = (dxy_1.y + 0.15);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = (max (texture (sampler_fw_main, (uv + \\n    (dxy_1 * texsize.zw)\\n  )).xyz, (texture (sampler_main, uv_orig).xyz * 0.8)) - 0.004);\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = ((texture (sampler_main, uv).xyz * 0.8) + ((\\n    (texture (sampler_blur1, uv).xyz * scale1)\\n   + bias1) * 0.7));\\n  ret = tmpvar_1.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.9,echo_zoom:1.169,wave_mode:2,additivewave:1,wave_dots:1,wave_thick:1,wave_a:.274,wave_scale:2.827,wave_smoothing:.09,modwavealphastart:.83,modwavealphaend:1.31,warpscale:3.138,zoom:.993,warp:54e-5,wave_r:.5,wave_g:.5,wave_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dx_residual=0;a.dy_residual=0;a.bass_thresh=0;\",frame_eqs_str:\"a.wave_r=.85+.25*Math.sin(.437*a.time+1);a.wave_g=.85+.25*Math.sin(.544*a.time+2);a.wave_b=.85+.25*Math.sin(.751*a.time+3);a.rot+=.01*(.6*Math.sin(.381*a.time)+.4*Math.sin(.579*a.time));a.cx+=.21*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.21*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.003*(.6*Math.sin(.234*a.time)+.4*Math.sin(.277*a.time));a.dy+=.003*(.6*Math.sin(.284*a.time)+.4*Math.sin(.247*a.time));a.decay-=.01*equal(mod(a.frame,6),0);a.dx+=\\na.dx_residual;a.dy+=a.dy_residual;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.96*(a.bass_thresh-1.3)+1.3);a.dx_residual=.016*equal(a.bass_thresh,2.13)*Math.sin(7*a.time)+(1-equal(a.bass_thresh,2.13))*a.dx_residual;a.dy_residual=.012*equal(a.bass_thresh,2.13)*Math.sin(9*a.time)+(1-equal(a.bass_thresh,2.13))*a.dy_residual;a.wave_x-=7*a.dx_residual;a.wave_y-=7*a.dy_residual;a.wave_mystery=.03*a.time;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = (tmpvar_2.xyz + ((tmpvar_2.xyz - \\n    ((texture (sampler_blur1, uv).xyz * scale1) + bias1)\\n  ) * 0.2));\\n  ret_1 = (ret_1 - 0.02);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_1;\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_main, uv).xyz * 1.15);\\n  ret = tmpvar_1.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,additivewave:1,wave_brighten:0,wave_a:.001,wave_scale:.01,wave_smoothing:.63,wave_mystery:-1,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:30.965,warpscale:2.572,zoom:1.00901,warp:54e-5,wave_r:.65,wave_g:.65,wave_b:.65,mv_x:12.8,mv_y:9.6,mv_l:1,mv_g:.91,mv_b:.71,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,additive:1,rad:.49138,r:0,g:1,b:1,border_a:0},init_eqs_str:\"a.q4=0;a.q5=0;\",frame_eqs_str:\"a.x=.5+a.q4;a.y=.5+a.q5;a.a=a.bass_att+a.mid_att+a.treb_att;a.a*=.25;a.a=a.a*a.a*1.5;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dx_residual=0;a.dy_residual=0;a.bass_thresh=0;\",frame_eqs_str:\"a.wave_r=.85+.25*Math.sin(.437*a.time+1);a.wave_g=.85+.25*Math.sin(.544*a.time+2);a.wave_b=.85+.25*Math.sin(.751*a.time+3);a.rot+=.01*(.6*Math.sin(.381*a.time)+.4*Math.sin(.579*a.time));a.cx+=.21*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.21*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.003*(.6*Math.sin(.234*a.time)+.4*Math.sin(.277*a.time));a.dy+=.003*(.6*Math.sin(.284*a.time)+.4*Math.sin(.247*a.time));a.decay-=.01*equal(mod(a.frame,6),0);a.dx+=\\na.dx_residual;a.dy+=a.dy_residual;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.96*(a.bass_thresh-1.3)+1.3);a.dx_residual=.016*equal(a.bass_thresh,2.13)*Math.sin(7*a.time)+(1-equal(a.bass_thresh,2.13))*a.dx_residual;a.dy_residual=.012*equal(a.bass_thresh,2.13)*Math.sin(9*a.time)+(1-equal(a.bass_thresh,2.13))*a.dy_residual;a.wave_x-=7*a.dx_residual;a.wave_y-=7*a.dy_residual;a.wave_mystery=.03*a.time;a.zoom+=.005*(.6*Math.sin(.1934*a.time+3)+.4*Math.sin(.307*\\na.time+9));a.zoom+=.2*Math.max(0,a.bass_att-1.1);a.warp+=.5*Math.max(0,a.treb-1.1);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = (tmpvar_2.xyz + ((tmpvar_2.xyz - \\n    ((texture (sampler_blur2, uv).xyz * scale2) + bias2)\\n  ) * 0.6));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 12.0) * clamp (\\n    (treb_att - 1.0)\\n  , 0.0, 1.0)));\\n  vec3 tmpvar_3;\\n  tmpvar_3 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.04, 0.04, 0.04));\\n  ret_1 = tmpvar_3;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = tmpvar_3;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec3 N_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (vec2(1.0, 0.0) * texsize.zw);\\n  N_1.x = ((texture (sampler_main, (uv_orig + tmpvar_3)).xyz - texture (sampler_main, (uv_orig - tmpvar_3)).xyz).x * 0.8);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (vec2(0.0, 1.0) * texsize.zw);\\n  N_1.y = ((texture (sampler_main, (uv_orig + tmpvar_4)).xyz - texture (sampler_main, (uv_orig - tmpvar_4)).xyz).x * 0.8);\\n  N_1.x = (N_1.x + ((\\n    ((texture (sampler_blur1, (uv_orig + tmpvar_3)).xyz * scale1) + bias1)\\n   - \\n    ((texture (sampler_blur1, (uv_orig - tmpvar_3)).xyz * scale1) + bias1)\\n  ).x * 0.2));\\n  N_1.y = (N_1.y + ((\\n    ((texture (sampler_blur1, (uv_orig + tmpvar_4)).xyz * scale1) + bias1)\\n   - \\n    ((texture (sampler_blur1, (uv_orig - tmpvar_4)).xyz * scale1) + bias1)\\n  ).x * 0.2));\\n  N_1.z = -0.077;\\n  vec3 tmpvar_5;\\n  tmpvar_5 = normalize(N_1);\\n  N_1 = tmpvar_5;\\n  vec3 tmpvar_6;\\n  tmpvar_6.z = -0.8;\\n  tmpvar_6.x = q9;\\n  tmpvar_6.y = q10;\\n  vec3 tmpvar_7;\\n  tmpvar_7.z = 0.0;\\n  tmpvar_7.xy = ((uv_orig * 2.0) - 1.0);\\n  vec3 tmpvar_8;\\n  tmpvar_8 = normalize((tmpvar_6 - tmpvar_7));\\n  vec3 tmpvar_9;\\n  tmpvar_9 = normalize((tmpvar_7 - vec3(0.0, 0.0, 1.0)));\\n  ret_2 = (vec3(clamp (dot (tmpvar_5, tmpvar_8), 0.0, 1.0)) * vec3(1.2, 0.9, 0.7));\\n  ret_2 = (ret_2 * normalize((\\n    (texture (sampler_blur3, uv_orig).xyz * scale3)\\n   + bias3).yzx));\\n  ret_2 = (ret_2 + (pow (\\n    clamp (dot (normalize((tmpvar_9 + \\n      ((2.0 * tmpvar_5) * dot (tmpvar_9, tmpvar_5))\\n    )), tmpvar_8), 0.0, 1.0)\\n  , 32.0) * 0.5));\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = ret_2;\\n  ret = tmpvar_10.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:1,echo_zoom:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.535239,wave_smoothing:0,wave_mystery:-.48,modwavealphastart:1,modwavealphaend:1.1,warpscale:3.138,fshader:1,zoom:1.023,warp:.122616,wave_g:.65,wave_b:0,mv_x:6.4,mv_y:48,mv_dx:.5,mv_dy:.5,mv_l:5,mv_r:.013716,mv_g:.872347,mv_b:.522466,mv_a:0},shapes:[{baseVals:{enabled:1,thickoutline:1,textured:1,rad:.746302,tex_zoom:.942039,g:1,b:1,r2:1,b2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.val=0;\",frame_eqs_str:\"a.ang=.5*Math.sin(a.time)+.5;a.val=3;a.a2=.33*a.val;a.a=.33*a.val;a.rad=.4*Math.cos(.3*a.time)+.65;a.x=.25*Math.sin(.25*a.time)+.5;a.y=.25*Math.cos(.45*a.time)+.5;\"},{baseVals:{enabled:1,thickoutline:1,textured:1,rad:.746302,tex_zoom:.942039,g:1,b:1,r2:1,b2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.val=0;\",frame_eqs_str:\"a.ang=.5*Math.cos(.3*a.time)+.5;a.val=3;a.a2=.33*a.val;a.a=.33*a.val;a.rad=.4*Math.sin(.3*a.time)+.65;a.x=.25*Math.cos(.25*a.time)+.5;a.y=.25*Math.sin(.45*a.time)+.5;\"},{baseVals:{enabled:1,thickoutline:1,rad:.364564,r:0,g2:0,a2:1,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.h2=0;a.vr=0;a.vg=0;a.vb=0;a.dist=0;a.maat=0;\",frame_eqs_str:\"a.h2+=.8*above(a.treb,1.32)*.9;a.vr=.5*Math.sin(.8*a.h2)+.5;a.vg=.5*Math.sin(.5*a.h2)+.5;a.vb=.5*Math.sin(.1*a.h2)+.5;a.g=a.vg;a.r=a.vr;a.b=a.vb;a.g2=a.g;a.r2=a.r;a.b2=a.b;a.dist=.01*mod(a.frame,100);a.maat+=above(a.bass_att,1.5);a.maat*=below(a.maat,16);a.x=.00001<Math.abs(below(a.maat,8))?.00001<Math.abs(below(a.maat,4))?a.dist:1-a.dist:a.x;a.y=.00001<Math.abs(above(a.maat,8))?.00001<Math.abs(above(a.maat,4))?a.dist:1-a.dist:a.y;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dx_residual=0;a.dy_residual=0;a.bass_thresh=0;\",frame_eqs_str:\"a.wave_r=1;a.wave_g=0;a.wave_b=0;a.wave_r=sqrt(a.wave_r);a.wave_g=sqrt(a.wave_g);a.wave_b=sqrt(a.wave_b);a.rot+=.01*(.6*Math.sin(.381*a.time)+.4*Math.sin(.579*a.time));a.cx+=.21*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.21*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.003*(.6*Math.sin(.234*a.time)+.4*Math.sin(.277*a.time));a.dy+=.003*(.6*Math.sin(.284*a.time)+.4*Math.sin(.247*a.time));a.decay-=.01*equal(mod(a.frame,6),0);a.dx+=a.dx_residual;a.dy+=\\na.dy_residual;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.96*(a.bass_thresh-1.3)+1.3);a.dx_residual=.016*equal(a.bass_thresh,2.13)*Math.sin(7*a.time)+(1-equal(a.bass_thresh,2.13))*a.dx_residual;a.dy_residual=.012*equal(a.bass_thresh,2.13)*Math.sin(9*a.time)+(1-equal(a.bass_thresh,2.13))*a.dy_residual;a.wave_x-=7*a.dx_residual;a.wave_y-=7*a.dy_residual;a.wave_mystery=.03*a.time;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_main, (uv + (\\n    (texture (sampler_main, (uv + texsize.zw)).xy - 0.37)\\n   * 0.03))).xyz - 0.004);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.05 + (0.9 * uv));\\n  ret_2 = (abs((\\n    ((texture (sampler_blur1, uv_1).xyz * scale1) + bias1)\\n   - texture (sampler_main, uv_1).xyz)) * 6.0);\\n  ret_2 = (ret_2 * 1.333);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_2;\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.7,echo_zoom:1.16936,wave_mode:2,wave_dots:1,wave_brighten:0,wave_a:.001,wave_scale:.011726,wave_smoothing:.9,zoom:.999902,warp:.01,wave_r:.5,wave_g:.4,wave_b:.3,ob_size:.0065,ib_size:.26,mv_x:0,mv_y:43.199997,mv_l:1,mv_g:.91,mv_b:.71,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,r:.1,b:.7},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.r1=0;a.g2=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.r2=0;a.ang=0;a.sinang=0;a.tm=0;a.b2=0;a.t2=0;a.zp=0;a.g1=0;a.t4=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5);a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.xq=a.xp;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.3;a.ang=3.14+1.5*\\nMath.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*Math.cos(a.tm)-.5;\\na.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=a.a;a.b+=.3*pow(1-a.sample,2);a.r1=a.t1;a.g1=a.t2;a.b1=a.t3;a.r2=a.t4;a.g2=a.t5;a.b2=a.t6;a.r=a.r1*a.flip+a.r2*(1-a.flip);a.g=a.g1*a.flip+a.g2*(1-a.flip);a.b=a.b1*a.flip+a.b2*(1-a.flip);\"},{baseVals:{enabled:1,r:.2,b:.6},init_eqs_str:\"a.n=0;a.yq=0;a.xp=0;a.t5=0;a.xs=0;a.yp=0;a.t1=0;a.q1=0;a.cosang=0;a.r1=0;a.g2=0;a.xq=0;a.t3=0;a.flip=0;a.t6=0;a.ys=0;a.zq=0;a.phs=0;a.r2=0;a.ang=0;a.sinang=0;a.tm=0;a.b2=0;a.t2=0;a.zp=0;a.g1=0;a.t4=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.t1=.5*Math.sin(a.time)+.5;a.t2=.5*Math.sin(a.time+2.1)+.5;a.t3=.5*Math.sin(a.time+4.2)+.5;a.t4=.5*Math.sin(a.time+1.1)+.5;a.t5=.5*Math.sin(a.time+3.1)+.5;a.t6=.5*Math.sin(a.time+5.2)+.5;\",point_eqs_str:\"a.n=6.283*a.sample;a.phs=.2*-a.sample;a.tm=a.q1+a.phs;a.flip+=1;a.flip*=below(a.flip,2);a.xp=0;a.yp=.1*a.flip+.2*(.5*Math.sin(a.tm)+.5)+.1;a.yp=-a.yp;a.zp=0;a.ang=.5*Math.sin(2*a.tm)+.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.yq=a.yp;a.zq=a.zp;a.ang=8*a.tm;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.3;\\na.ang=3.14+1.5*Math.sin(2*a.tm-.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp;a.yq=a.yp*a.sinang+a.zp*a.cosang;a.zq=a.yp*a.cosang-a.zp*a.sinang;a.ang=-1+Math.cos(3.1*a.tm+.5);a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq*a.sinang+a.yq*a.cosang;a.yp=a.xq*a.cosang-a.yq*a.sinang;a.zp=a.zq;a.zp-=.35;a.ang=1.75*Math.cos(2.3*a.tm)-1.05;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xq=a.xp*a.sinang+a.zp*a.cosang;a.yq=a.yp;a.zq=a.xp*a.cosang-a.zp*a.sinang;a.ang=.5*Math.cos(a.tm)-\\n.5;a.sinang=Math.sin(a.ang);a.cosang=Math.cos(a.ang);a.xp=a.xq;a.yp=a.yq*a.cosang-a.zq*a.sinang;a.zp=a.yq*a.sinang+a.zq*a.cosang;a.zp+=2;a.xs=div(a.xp,a.zp);a.ys=div(a.yp,a.zp);a.x=a.xs+.5;a.y=1.3*a.ys+.5;a.a=.00001<Math.abs(equal(a.q8,1))?1-a.sample:a.sample;a.a*=a.a;a.b+=.3*pow(1-a.sample,2);a.r1=a.t1;a.g1=a.t2;a.b1=a.t3;a.r2=a.t4;a.g2=a.t5;a.b2=a.t6;a.r=a.r1*a.flip+a.r2*(1-a.flip);a.g=a.g1*a.flip+a.g2*(1-a.flip);a.b=a.b1*a.flip+a.b2*(1-a.flip);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.movement=0;a.q1=0;\",frame_eqs_str:\"a.movement=a.movement+.01*(a.bass+a.bass_att)+.001*pow(a.bass+1,3);a.q1=a.movement;a.monitor=a.q1;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = texture (sampler_main, uv).xyz;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (normalize((uv - uv_orig)) * texsize.zw);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 0.0;\\n  tmpvar_3.xyz = ret_1;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 0.0;\\n  tmpvar_4.xyz = max (tmpvar_3, (texture (sampler_main, (uv - tmpvar_2)) * 0.9)).xyz;\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 0.0;\\n  tmpvar_5.xyz = max (tmpvar_4, (texture (sampler_main, (uv + tmpvar_2)) * 0.97)).xyz;\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 0.0;\\n  tmpvar_6.xyz = max (tmpvar_5, (texture (sampler_main, (uv + \\n    (tmpvar_2 * 2.0)\\n  )) * 0.97)).xyz;\\n  ret_1 = (max (tmpvar_6, (texture (sampler_main, \\n    (uv + (tmpvar_2 * 3.0))\\n  ) * 0.9)).xyz * 0.92);\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = ret_1;\\n  ret = tmpvar_7.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.05 + (0.9 * uv));\\n  ret_2 = (abs((\\n    ((texture (sampler_blur1, uv_1).xyz * scale1) + bias1)\\n   - texture (sampler_main, uv_1).xyz)) * 6.0);\\n  ret_2 = (ret_2 * 1.333);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_2;\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1,echo_orient:3,wave_mode:1,wave_thick:1,wrap:0,darken_center:1,wave_a:.3,wave_scale:.881,wave_smoothing:.5,wave_mystery:-1,warpscale:2.853,zoomexp:3.6,zoom:1.02109,rot:-.16,warp:.309,wave_r:.6,wave_g:.6,wave_b:.6,wave_y:.47,mv_l:1.75},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q8=0;a.oldq8=0;\",frame_eqs_str:\"a.wave_r+=.4*(.6*Math.sin(.933*a.time)+.4*Math.sin(1.045*a.time));a.wave_g+=.4*(.6*Math.sin(.9*a.time)+.4*Math.sin(.956*a.time));a.wave_b+=.4*(.6*Math.sin(.91*a.time)+.4*Math.sin(.92*a.time));a.q8=a.oldq8+(.00001<Math.abs(above(a.bass+a.bass_att,1.8))?a.q8+.0005*pow(a.bass+a.bass_att-1,9):0);a.oldq8=a.q8;a.monitor=a.q8;a.zoom+=.023*(.6*Math.sin(.339*a.q8)+.4*Math.sin(.276*a.q8));a.rot+=.03*(.6*Math.sin(.381*a.q8)+.4*Math.sin(.579*a.q8));a.mv_r=a.wave_r;a.mv_b=a.wave_b;a.mv_g=\\na.wave_g;a.mv_x=1.25;a.mv_y=1.25;a.mv_dx=.1*Math.sin(1.1*a.time);a.mv_dy=.1*Math.cos(1.112*a.time);\",pixel_eqs_str:\"a.rot+=div(1,10*(a.rad+.2+.1*Math.sin(a.q8)));\",warp:\"\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1 = texture (sampler_main, uv);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = (tmpvar_1.xyz * (1.0 + (\\n    (abs((fract(\\n      (tmpvar_1.xyz * 8.0)\\n    ) - 0.5)) - 0.25)\\n   * 0.4)));\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:2.001,decay:.95,echo_zoom:.9996,wave_mode:1,wrap:0,wave_a:3.072644,wave_scale:1.285746,wave_smoothing:0,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.999513,warp:.0101,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.005,ob_a:.8,ib_size:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"\",frame_eqs_str:\"a.wave_r+=.5*Math.sin(1.13*a.time);a.wave_g+=.5*Math.sin(1.23*a.time);a.wave_b+=.5*Math.sin(1.33*a.time);\",pixel_eqs_str:\"a.zoom+=.05*(Math.sin(6*a.ang)+.3*Math.sin(Math.sin(2*a.time*Math.sin(a.time)*a.rad))-.1*Math.cos(a.rad));a.rot+=.5*Math.sin(.5-a.rad)*Math.cos(.02*(.5-a.rad)+a.time);a.sx+=.01*(.99-a.rad)*Math.sin(.733*a.time)*below(Math.sin(a.time),0);a.sy+=.01*(.99-a.rad)*Math.cos(.953*a.time)*above(Math.sin(a.time),0);a.zoom-=.05*(1-a.rad)*below(a.rad,.5);\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_main, (uv + (\\n    (texture (sampler_main, (uv + texsize.zw)).xy - 0.37)\\n   * 0.03))).xyz - 0.004);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.03 + (0.94 * uv));\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 0.7);\\n  ret_2 = (texture (sampler_main, (uv_1 + (tmpvar_3 * vec2(3.5, 0.0)))).xyz * 3.0);\\n  ret_2 = (ret_2 + (texture (sampler_main, (uv_1 + \\n    (tmpvar_3 * vec2(0.0, 3.5))\\n  )).xyz * 3.0));\\n  ret_2 = (ret_2 + (texture (sampler_main, (uv_1 + \\n    (tmpvar_3 * vec2(0.0, -3.5))\\n  )).xyz * -2.0));\\n  ret_2 = (ret_2 + (texture (sampler_main, (uv_1 + \\n    (tmpvar_3 * vec2(-3.5, 0.0))\\n  )).xyz * -2.0));\\n  ret_2 = (ret_2 / 2.0);\\n  ret_2 = (ret_2 * (1.0 + (0.15 * \\n    ((texture (sampler_noise_lq, (rand_frame.xy + (\\n      (uv_1 * texsize.xy)\\n     * texsize_noise_lq.zw))).xxx * 2.0) - 1.0)\\n  )));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = pow (ret_2, vec3(0.5, 0.8, 1.0));\\n  ret_2 = tmpvar_4;\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = tmpvar_4;\\n  ret = tmpvar_5.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:.97,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:2,wave_brighten:0,wrap:0,wave_a:100,wave_scale:.797,wave_smoothing:.5,modwavealphastart:.5,modwavealphaend:1,dx:1e-5,dy:1e-5,warp:.01,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.005,ob_a:1,ib_r:0,ib_g:0,ib_b:0,ib_a:.1,mv_x:0,mv_y:0,mv_l:1,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.thresh=0;a.dx_r=0;a.dy_r=0;\",frame_eqs_str:\"a.warp=0;a.ob_r=a.ob_r+.8*Math.sin(1.1*a.time)+1.1*Math.cos(2*a.time);a.ob_b=a.ob_b+.9*Math.sin(1.2*a.time)+Math.cos(1.5*a.time);a.ob_g=a.ob_g+.7*Math.sin(a.time)+Math.sin(.4*a.time);a.wave_r=Math.abs(a.ob_r+.2);a.wave_g=Math.abs(a.ob_g+.2);a.wave_b=Math.abs(a.ob_b+.2);a.ib_b=a.ib_b+.44*Math.cos(1.5*a.time)+.7*Math.tan(a.time);a.ib_g+=2*Math.sin(a.time*a.bass_att);a.ib_r=a.ib_r+.8*Math.sin(1.2*a.time)+1.3*Math.cos(1.8*a.time);\",pixel_eqs_str:\"a.thresh=2*above(a.bass_att,a.thresh)+(1-above(a.bass_att,a.thresh))*(.96*(a.thresh-1.3)+1.3);a.dx_r=.015*equal(a.thresh,2)*Math.sin(5*a.time)+(1-equal(a.thresh,2))*a.dx_r;a.dy_r=.015*equal(a.thresh,2)*Math.sin(6*a.time)+(1-equal(a.thresh,2))*a.dy_r;a.zoom+=.07*a.treb*Math.abs(Math.sin(a.time*a.rad))*(.3-a.rad);a.dy+=a.dy_r*below(a.rad,.5-.2*a.time);a.dx+=a.dx_r*below(a.rad,.5-.2*a.time);a.rot+=.7*a.bass_att*Math.sin(Math.abs(a.dx_r*(1-a.rad)*2*a.time+Math.cos(12*a.rad)))*.2;\\n\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = (tmpvar_2.xyz + ((tmpvar_2.xyz - \\n    ((texture (sampler_blur1, uv).xyz * scale1) + bias1)\\n  ) * 0.3));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    (texture (sampler_noise_lq, ((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 0.1))).xyz - 0.5)\\n   / 256.0) * 28.0));\\n  vec3 tmpvar_3;\\n  tmpvar_3 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.04, 0.04, 0.04));\\n  ret_1 = tmpvar_3;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = tmpvar_3;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\"\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,additivewave:1,modwavealphabyvolume:1,wave_a:.009,wave_scale:2.713,wave_smoothing:0,modwavealphastart:1.2,modwavealphaend:1.2,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.44,wave_g:.4,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:3,additive:1,num_inst:175,rad:.20065,ang:.75398,tex_ang:3.14159,tex_zoom:.99979,r:0,g2:0,border_r:.01,border_g:0,border_a:1},init_eqs_str:\"a.my_z=0;a.d=0;a.y3=0;a.z2=0;a.y1=0;a.w=0;a.q12=0;a.w2=0;a.x1=0;a.q13=0;a.q15=0;a.dy1=0;a.zoom=0;a.p=0;a.q5=0;a.dz1=0;a.z3=0;a.w3=0;a.my_x=0;a.x3=0;a.my_y=0;a.q11=0;a.dd=0;a.q4=0;a.yy1=0;a.q16=0;a.w1=0;a.x2=0;a.q17=0;a.l=0;a.y2=0;a.dx1=0;a.zz1=0;a.q14=0;a.z1=0;a.q3=0;a.q32=0;a.xx1=0;\",frame_eqs_str:\"a.xx1=.00001<Math.abs(equal(a.instance,0))?a.q11:a.xx1;a.yy1=.00001<Math.abs(equal(a.instance,0))?a.q12:a.yy1;a.zz1=.00001<Math.abs(equal(a.instance,0))?a.q13:a.zz1;a.dx1=a.q14*(a.yy1-a.xx1);a.dy1=a.xx1*(a.q15-a.zz1)-a.yy1;a.dz1=a.xx1*a.yy1-a.q16*a.zz1;a.dd=sqrt(a.dx1*a.dx1+a.dy1*a.dy1+a.dz1*a.dz1);a.xx1+=div(a.q17*a.dx1,a.dd);a.yy1+=div(a.q17*a.dy1,a.dd);a.zz1+=div(a.q17*a.dz1,a.dd);a.my_x=.1*a.xx1;a.my_y=.1*a.yy1;a.my_z=.1*a.zz1-3;a.d=4.75;a.zoom=.55+.25*Math.sin(.5*a.q32);\\na.w1=a.q3;a.w2=a.q4;a.w3=a.q5;a.x1=Math.cos(a.w1)*a.my_x+Math.sin(a.w1)*a.my_y;a.y1=-Math.sin(a.w1)*a.my_x+Math.cos(a.w1)*a.my_y;a.z1=a.my_z;a.x2=Math.cos(a.w2)*a.x1+Math.sin(a.w2)*a.z1;a.z2=-Math.sin(a.w2)*a.x1+Math.cos(a.w2)*a.z1;a.y2=a.y1;a.y3=Math.cos(a.w3)*a.y2+Math.sin(a.w3)*a.z2;a.z3=-Math.sin(a.w3)*a.y2+Math.cos(a.w3)*a.z2;a.x3=a.x2;a.l=sqrt(a.x3*a.x3+a.y3*a.y3);a.w=Math.atan2(a.x3,a.y3);a.p=Math.tan(Math.asin(1)+Math.atan2(a.d+a.z3,a.l));a.d=sqrt(a.x3*a.x3+a.y3*a.y3+(a.z3+a.d)*(a.z3+a.d));\\na.my_x=a.zoom*Math.sin(a.w)*a.p;a.my_y=a.zoom*Math.cos(a.w)*a.p;a.x=.5+a.my_x;a.y=.5+a.my_y;a.rad=div(a.rad,a.d);a.ang-=div(a.instance,a.num_inst)*Math.asin(1)*8;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q13=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.movz=0;a.trel=0;a.q9=0;a.rott=0;a.vol__=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.trel_=0;a.vx=0;a.dec_slow=0;a.q11=0;a.q10=0;a.vy=0;a.q4=0;a.dir=0;a.dir_=0;a.p2=0;a.avg=0;a.trig=0;a.beat=0;a.vol=0;a.p1=0;a.peak=0;a.dec_fast=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.vol_=0;a.q7=0;a.q28=0;a.q20=0;a.q8=0;a.step=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.96,div(30,a.fps));a.dec_fast=pow(.6,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,0+a.avg+a.peak)*above(a.time,a.t0+.1);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,16);a.index2=mod(a.index2+a.is_beat*bnot(a.index),8);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q24=a.is_beat;\\na.vol=a.bass_att+a.mid_att+a.treb_att;a.vol_=a.dec_med*a.vol_+(1-a.dec_med)*a.vol;a.vol__=a.dec_med*a.vol__+(1-a.dec_med)*a.vol_;a.q27=a.index+1;a.q28=a.index2+1;a.q23=a.q22-div(.1,a.q22);a.q23=Math.max(a.q23,1);a.k1=a.is_beat*equal(mod(a.index,8),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_fast*a.p2+(1-a.dec_fast)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.trig=a.q24*bnot(mod(a.index,2));a.vx=a.vx*bnot(a.trig)+a.trig*(div(Math.floor(randint(100)),\\n100)-.5);a.vy=a.vy*bnot(a.trig)+a.trig*(div(Math.floor(randint(100)),100)-.5);a.q10=.2+a.vy*a.vy*2;a.q11=div(Math.sin(div(a.time,9)),2)+.4;a.movz-=div(1,a.fps)*(.3+a.vx);a.q9=a.movz;a.q12=2*Math.min(a.q22,6);a.q13=Math.min(2,1+Math.abs(8*a.vy*a.vx));a.dir_=a.bass-1;a.trig=bnot(mod(a.index,4))*a.q24;a.dir=bnot(a.trig)*a.dir+a.trig*(Math.floor(randint(10))-5);a.trel+=div(.1,a.fps)*a.dir;a.trel_=a.dec_med*a.trel_+(1-a.dec_med)*a.trel;a.q5=Math.cos(a.trel_);a.q6=Math.sin(a.trel_+0*a.q27*mod(a.q28,2));\\na.q7=-a.q6;a.q8=a.q5;a.zoom=1.02;a.rot=.5*Math.sin(bnot(mod(a.q28,2))*a.q28);a.rot=0*Math.sin(div(a.time,3));a.dx=0;a.monitor=a.q11;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = ((uv * texsize.xy) * 0.08);\\n  vec3 tmpvar_2;\\n  tmpvar_2 = (texture (sampler_main, (uv - (\\n    ((sin(tmpvar_1) / cos(tmpvar_1)) * texsize.zw)\\n   * 3.0))).xyz + (vec3(dot (texture (sampler_noise_lq, \\n    ((((texsize.xy * texsize_noise_lq.zw).x * uv) * 0.02) + (0.1 * rand_frame).xy)\\n  ), vec4(0.32, 0.49, 0.29, 0.0))) / 30.0));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ((mix (tmpvar_2, \\n    (1.0 - tmpvar_2.zyx)\\n  , vec3(0.01, 0.01, 0.01)) - 0.03) - (0.2 * pow (\\n    (1.0 - rad)\\n  , 18.0)));\\n  ret = tmpvar_3.xyz;\\n }\",comp:\"vec3 xlat_mutableneu;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutablerss;\\nvec2 xlat_mutableuv2;\\n shader_body { \\n  vec2 uv_1;\\n  vec2 ofs_2;\\n  vec3 ret1_3;\\n  uv_1 = ((uv - 0.5) * aspect.xy);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q5;\\n  tmpvar_4.y = q6;\\n  uv_1 = (uv_1 + (tmpvar_4 / 4.0));\\n  mat2 tmpvar_5;\\n  tmpvar_5[uint(0)] = _qb.xy;\\n  tmpvar_5[1u] = _qb.zw;\\n  uv_1 = (uv_1 * tmpvar_5);\\n  float tmpvar_6;\\n  float tmpvar_7;\\n  tmpvar_7 = (min (abs(\\n    (uv_1.y / uv_1.x)\\n  ), 1.0) / max (abs(\\n    (uv_1.y / uv_1.x)\\n  ), 1.0));\\n  float tmpvar_8;\\n  tmpvar_8 = (tmpvar_7 * tmpvar_7);\\n  tmpvar_8 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_8) + 0.05368138) * tmpvar_8) - 0.1173503)\\n     * tmpvar_8) + 0.1938925) * tmpvar_8) - 0.3326756)\\n   * tmpvar_8) + 0.9999793) * tmpvar_7);\\n  tmpvar_8 = (tmpvar_8 + (float(\\n    (abs((uv_1.y / uv_1.x)) > 1.0)\\n  ) * (\\n    (tmpvar_8 * -2.0)\\n   + 1.570796)));\\n  tmpvar_6 = (tmpvar_8 * sign((uv_1.y / uv_1.x)));\\n  if ((abs(uv_1.x) > (1e-08 * abs(uv_1.y)))) {\\n    if ((uv_1.x < 0.0)) {\\n      if ((uv_1.y >= 0.0)) {\\n        tmpvar_6 += 3.141593;\\n      } else {\\n        tmpvar_6 = (tmpvar_6 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_6 = (sign(uv_1.y) * 1.570796);\\n  };\\n  xlat_mutablers0.x = (((tmpvar_6 / 3.1416) * 6.0) * q28);\\n  xlat_mutablers0.y = inversesqrt(dot (uv_1, uv_1));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = (xlat_mutablers0.x + (q9 * 8.0));\\n  tmpvar_9.y = (xlat_mutablers0.y + ((q9 * q28) * 4.0));\\n  xlat_mutablerss = (tmpvar_9 / 12.0);\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = q5;\\n  tmpvar_10.y = q6;\\n  ofs_2 = (0.1 * tmpvar_10.yx);\\n  float tmpvar_11;\\n  float tmpvar_12;\\n  tmpvar_12 = -(q9);\\n  tmpvar_11 = fract(tmpvar_12);\\n  mat2 tmpvar_13;\\n  tmpvar_13[uint(0)].x = 1.0;\\n  tmpvar_13[uint(0)].y = -0.0;\\n  tmpvar_13[1u].x = 0.0;\\n  tmpvar_13[1u].y = 1.0;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_11)\\n   * tmpvar_13)) * aspect.yx);\\n  vec2 tmpvar_14;\\n  tmpvar_14 = fract(((xlat_mutableuv2 + 0.5) + ofs_2));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_14).xyz + ((texture (sampler_blur1, tmpvar_14).xyz * scale1) + bias1));\\n  ret1_3 = max (vec3(0.0, 0.0, 0.0), ((xlat_mutableneu * \\n    (1.0 - (tmpvar_11 * tmpvar_11))\\n  ) * 2.0));\\n  float tmpvar_15;\\n  tmpvar_15 = fract((tmpvar_12 + 0.3333333));\\n  mat2 tmpvar_16;\\n  tmpvar_16[uint(0)].x = -0.4990803;\\n  tmpvar_16[uint(0)].y = -0.8665558;\\n  tmpvar_16[1u].x = 0.8665558;\\n  tmpvar_16[1u].y = -0.4990803;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_15)\\n   * tmpvar_16)) * aspect.yx);\\n  vec2 tmpvar_17;\\n  tmpvar_17 = fract(((xlat_mutableuv2 + 0.5) + ofs_2));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_17).xyz + ((texture (sampler_blur1, tmpvar_17).xyz * scale1) + bias1));\\n  ret1_3 = max (ret1_3, ((xlat_mutableneu * \\n    (1.0 - (tmpvar_15 * tmpvar_15))\\n  ) * 2.0));\\n  float tmpvar_18;\\n  tmpvar_18 = fract((tmpvar_12 + 0.6666667));\\n  mat2 tmpvar_19;\\n  tmpvar_19[uint(0)].x = -0.5018377;\\n  tmpvar_19[uint(0)].y = 0.8649619;\\n  tmpvar_19[1u].x = -0.8649619;\\n  tmpvar_19[1u].y = -0.5018377;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_18)\\n   * tmpvar_19)) * aspect.yx);\\n  vec2 tmpvar_20;\\n  tmpvar_20 = fract(((xlat_mutableuv2 + 0.5) + ofs_2));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_20).xyz + ((texture (sampler_blur1, tmpvar_20).xyz * scale1) + bias1));\\n  ret1_3 = max (ret1_3, ((xlat_mutableneu * \\n    (1.0 - (tmpvar_18 * tmpvar_18))\\n  ) * 2.0));\\n  float tmpvar_21;\\n  tmpvar_21 = fract((tmpvar_12 + 1.0));\\n  mat2 tmpvar_22;\\n  tmpvar_22[uint(0)].x = 0.9999949;\\n  tmpvar_22[uint(0)].y = 0.003185092;\\n  tmpvar_22[1u].x = -0.003185092;\\n  tmpvar_22[1u].y = 0.9999949;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_21)\\n   * tmpvar_22)) * aspect.yx);\\n  vec2 tmpvar_23;\\n  tmpvar_23 = fract(((xlat_mutableuv2 + 0.5) + ofs_2));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_23).xyz + ((texture (sampler_blur1, tmpvar_23).xyz * scale1) + bias1));\\n  ret1_3 = max (ret1_3, ((xlat_mutableneu * \\n    (1.0 - (tmpvar_21 * tmpvar_21))\\n  ) * 2.0));\\n  vec2 tmpvar_24;\\n  tmpvar_24.x = (ret1_3.x + ret1_3.z);\\n  tmpvar_24.y = (ret1_3.x - ret1_3.y);\\n  vec4 tmpvar_25;\\n  tmpvar_25.w = 1.0;\\n  tmpvar_25.xyz = ((ret1_3 + (\\n    ((bass_att * 0.004) / sqrt(dot (uv_1, uv_1)))\\n   * roam_sin).xyz) + ((2.0 * \\n    (bass_att * ((texture (sampler_blur1, fract(\\n      (xlat_mutablerss + (tmpvar_24 / 2.0))\\n    )).xyz * scale1) + bias1).zxy)\\n  ) * clamp (\\n    (1.0 - (ret1_3 * 4.0))\\n  , 0.0, 1.0)));\\n  ret = tmpvar_25.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.14,decay:1,echo_zoom:1,wave_mode:6,wave_thick:1,wave_brighten:0,wrap:0,darken:1,wave_a:1.17,wave_scale:.797,wave_smoothing:0,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoomexp:.9995,zoom:.9998,rot:.02,dy:-.008,warp:.01,sx:1.0098,wave_r:.5,wave_g:.5,wave_b:.5,wave_x:.9,ob_size:.005,ob_a:.8,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:44.8,mv_y:38.4,mv_l:5,mv_g:.91,mv_b:.71,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q12=0;a.q18=0;a.q6=0;a.bass_thresh=0;a.wg=0;a.q11=0;a.q10=0;a.wb=0;a.q17=0;a.vol=0;a.q2=0;a.q3=0;a.wr=0;a.q7=0;a.q8=0;\",frame_eqs_str:\"a.wave_r+=.3*Math.sin(50*a.vol);a.wave_b+=.3*Math.sin(20*a.vol);a.wave_g+=.5*Math.sin(35*a.vol);a.q8=a.wave_r;a.q7=a.wave_b;a.q6=a.wave_g;a.wr=.5+.4*(.6*Math.sin(1.1*a.time)+.4*Math.sin(.8*a.time));a.wb=.5+.4*(.6*Math.sin(1.6*a.time)+.4*Math.sin(.5*a.time));a.wg=.5+.4*(.6*Math.sin(1.34*a.time)+.4*Math.sin(.4*a.time));a.monitor=a.wg;a.q10=a.wr;a.q11=a.wb;a.q12=a.wg;a.q18=.007*Math.sin(.1*a.time);a.q17=-.007*Math.sin(.254*a.time);a.q2=a.bass_thresh;a.vol=.25*(a.bass+a.mid+a.treb);\\na.vol*=a.vol;a.q3=a.vol;a.warp=0;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 noise3_1;\\n  vec3 tmpvar_2;\\n  tmpvar_2 = (texture (sampler_main, uv).xyz + ((texture (sampler_blur1, uv).xyz * scale1) + bias1));\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (0.5 + ((\\n    (uv - vec2(0.0, 1.0))\\n   - 0.5) * (1.0 + \\n    (tmpvar_2.y * 0.03)\\n  )));\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (tmpvar_3.x + pow (tmpvar_2.x, 0.0));\\n  tmpvar_4.y = (tmpvar_3.y + pow (tmpvar_2.x, 0.005));\\n  noise3_1 = (texture (sampler_noise_lq, ((\\n    (uv_orig * texsize.xy)\\n   * texsize_noise_lq.zw) + rand_frame.xy)).xyz * fract(q15));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (noise3_1 * (vec3(1.0, 1.0, 1.0) - vec3(fract(\\n    (q3 * 0.5)\\n  ))));\\n  noise3_1 = tmpvar_5;\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_main, fract(tmpvar_4));\\n  vec3 tmpvar_7;\\n  tmpvar_7.x = q10;\\n  tmpvar_7.y = q11;\\n  tmpvar_7.z = q12;\\n  vec3 tmpvar_8;\\n  tmpvar_8 = mix (tmpvar_5, tmpvar_7, tmpvar_6.xxx);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = (tmpvar_6.xyz + clamp ((\\n    (tmpvar_6.yzx * tmpvar_8.zxy)\\n   - \\n    (tmpvar_6.zxy * tmpvar_8.yzx)\\n  ), 0.0, 1.0));\\n  ret = tmpvar_9.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (texture (sampler_main, (uv - tmpvar_2)).xyz - texture (sampler_main, (uv + tmpvar_2)).xyz).x;\\n  tmpvar_4.y = (texture (sampler_main, (uv - tmpvar_3)).xyz - texture (sampler_main, (uv + tmpvar_3)).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    ((uv - 0.5) * 2.0)\\n  )) - tmpvar_4);\\n  float tmpvar_5;\\n  tmpvar_5 = clamp ((0.04 / sqrt(\\n    dot (uv1_1, uv1_1)\\n  )), 0.0, 1.0);\\n  uv1_1 = ((0.3 * cos(\\n    (uv1_1 * 12.0)\\n  )) - (9.0 * tmpvar_4));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (tmpvar_5 + ((texture (sampler_main, uv).xyz * 12.0) * vec3(clamp (\\n    (0.04 / sqrt(dot (uv1_1, uv1_1)))\\n  , 0.0, 1.0))));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.14,decay:1,echo_zoom:1,wave_mode:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.179,wave_smoothing:0,wave_mystery:.3,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoomexp:.8195,zoom:1.0697,dy:.006,warp:.01,sx:.9996,wave_g:0,wave_b:0,ob_a:.8,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:0,mv_y:0,mv_l:1,mv_g:.91,mv_b:.71,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q12=0;a.q18=0;a.q6=0;a.q5=0;a.bass_thresh=0;a.wg=0;a.q11=0;a.q10=0;a.wb=0;a.q17=0;a.vol=0;a.q2=0;a.q3=0;a.wr=0;a.q7=0;a.mtime=0;a.q8=0;\",frame_eqs_str:\"a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.91*(a.bass_thresh-1.3)+1.3);a.wave_r=.5+.5*(.6*Math.sin(1.3*a.time)+.4*Math.sin(.98*a.time));a.wave_b=.5+.5*(.6*Math.sin(1.1*a.time)+.4*Math.sin(.78*a.time));a.wave_g=.5+.5*(.6*Math.sin(1.2*a.time)+.4*Math.sin(.6*a.time));a.q8=a.wave_r;a.q7=a.wave_b;a.q6=a.wave_g;a.wr=.5+.4*(.6*Math.sin(.2*a.time)+.4*Math.sin(.8*a.time));a.wb=.5+.4*(.6*Math.sin(.377*a.time)+.4*Math.sin(.5*a.time));a.wg=.5+\\n.4*(.6*Math.sin(.7*a.time)+.4*Math.sin(.4*a.time));a.q10=a.wr;a.q11=a.wb;a.q12=a.wg;a.q10=.8;a.q11=.2;a.q12=.1;a.q18=.01*Math.sin(.1*a.mtime);a.q17=-.01*Math.sin(.254*a.mtime);a.q2=a.bass_thresh;a.vol=.25*(a.bass+a.mid+a.treb);a.vol*=a.vol;a.q3=a.vol;a.q5=.5*a.vol;a.mtime+=.01*a.vol;a.q2=.25*a.mtime;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 noise2_1;\\n  vec3 ret_2;\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, uv).xyz * scale1) + bias1) + texture (sampler_main, uv).xyz);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (0.5 + ((uv - 0.5) * (1.0 + \\n    (tmpvar_3.y * 0.05)\\n  )));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (tmpvar_4.x + pow (tmpvar_3.x, q17));\\n  tmpvar_5.y = (tmpvar_4.y + pow (tmpvar_3.x, q18));\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_fc_main, fract(tmpvar_5));\\n  vec3 tmpvar_7;\\n  tmpvar_7.x = q10;\\n  tmpvar_7.y = q11;\\n  tmpvar_7.z = q12;\\n  noise2_1 = (texture (sampler_noise_lq, ((\\n    (uv_orig * texsize.xy)\\n   * texsize_noise_lq.zw) + rand_frame.xy)).xyz + ((tmpvar_7 * vec3(rad)) * vol));\\n  vec3 a_8;\\n  a_8 = (1.0 - tmpvar_6.xyz);\\n  ret_2 = (tmpvar_6.xyz + (0.3 * clamp (\\n    ((a_8.yzx * noise2_1.zxy) - (a_8.zxy * noise2_1.yzx))\\n  , 0.0, 1.0)));\\n  ret_2 = (ret_2 * 0.97);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = ret_2;\\n  ret = tmpvar_9.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = (texture (sampler_main, uv).xyz * vec3(0.9, 0.3, 0.5));\\n  ret_1 = (ret_1 * 1.34);\\n  ret_1 = (ret_1 * ret_1);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.28,decay:.8,echo_zoom:1,echo_orient:3,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,brighten:1,wave_a:.001,wave_scale:1.286,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,zoomexp:3.04777,zoom:1.0173,warp:.01605,wave_g:.65,wave_b:.65,ob_size:0,ob_a:1,mv_x:64,mv_y:48,mv_l:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,additive:1,g:0,b:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.8);\"},{baseVals:{enabled:1,thick:1,additive:1,r:0,g:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.05*a.bass;a.ma-=3.1415*above(a.mid,1)*.05*a.mid;a.mx+=.0001*Math.cos(a.ma);a.my+=.0001*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.1);\"},{baseVals:{enabled:1,thick:1,additive:1,r:0,b:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.mid,1)*.01*a.mid;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0004*Math.cos(a.ma);a.my+=.0004*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.3);\"},{baseVals:{enabled:1,thick:1,additive:1,g:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,.5)*.02*a.bass;a.ma-=3.1415*above(a.treb,.5)*.02*a.treb;a.mx+=.0008*Math.cos(a.ma);a.my+=.0008*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.2);\"}],init_eqs_str:\"a.z=0;a.w=0;a.d_x=0;a.q25=0;a.index=0;a.q12=0;a.q18=0;a.q13=0;a.q15=0;a.q29=0;a.dt=0;a.q1=0;a.dec_med=0;a.dm=0;a.mm=0;a.tt=0;a.is_beat=0;a.q31=0;a.q23=0;a.q24=0;a.dec_slow=0;a.q11=0;a.q16=0;a.q26=0;a.avg=0;a.r=0;a.db=0;a.beat=0;a.q17=0;a.d_y=0;a.peak=0;a.bb=0;a.q27=0;a.q14=0;a.t0=0;a.q32=0;a.q28=0;a.q30=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,2);a.d_x=.00001<Math.abs(a.is_beat)?.001*(randint(2E3)-1E3):a.d_x;a.d_y=.00001<Math.abs(a.is_beat)?.001*(randint(2E3)-1E3):a.d_y;a.r=\\n.00001<Math.abs(a.is_beat)?.001*(randint(2E3)-1E3):a.r;a.z=.00001<Math.abs(a.is_beat)?.001*randint(1E3):a.z;a.zoom=1.03+.06*a.z;a.rot=.06*a.r;a.dx=.01*a.d_x;a.dy=.01*a.d_y;a.q1=1;a.q23=div(randint(1E3),1E3);a.q24=div(randint(1E3),1E3);a.q25=6.28*div(randint(1E3),1E3);a.q26=a.q25-3.14;a.q27=div(randint(1E3),12E3)+.04;a.q28=div(randint(1E3),1E3);a.q29=div(randint(1E3),1E3);a.q30=6.28*div(randint(1E3),1E3);a.q31=a.q30-3.14;a.q32=div(randint(1E3),14E3)+.04;a.db=.98*a.db+.2*a.bass;a.bb+=.1*a.db;a.dt=.98*\\na.dt+.2*a.treb;a.tt+=.1*a.dt;a.dm=.98*a.dm+.2*a.mid;a.mm+=.1*a.dm;a.w=.1*(a.bb-a.tt);a.q11=Math.sin(a.w);a.q12=Math.cos(a.w);a.q13=.5+.25*Math.sin(.1*(a.bb-a.mm));a.q14=.5+.25*Math.sin(.1*(a.tt-a.mm));a.q15=4;a.q16=.06;a.q17=Math.sin(-a.w);a.q18=Math.cos(-a.w);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.x;\\n  tmpvar_5.y = tmpvar_4.x;\\n  vec2 tmpvar_6;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = mix (uv_orig, uv, vec2(0.1, 0.1));\\n  tmpvar_6 = (tmpvar_7 + ((tmpvar_5 * texsize.zw) * 2.0));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_3.y;\\n  tmpvar_8.y = tmpvar_4.y;\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (mix (uv_orig, uv, vec2(0.3, 0.3)) + ((tmpvar_8 * texsize.zw) * 2.0));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_3.z;\\n  tmpvar_10.y = tmpvar_4.z;\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (tmpvar_7 + ((tmpvar_10 * texsize.zw) * 2.0));\\n  ret_1.x = (texture (sampler_main, tmpvar_6).x - ((texture (sampler_main, tmpvar_6).xyz - \\n    ((texture (sampler_blur3, tmpvar_6).xyz * scale3) + bias3)\\n  ).x * 0.01));\\n  ret_1.y = (texture (sampler_main, tmpvar_9).y - ((texture (sampler_main, tmpvar_9).xyz - \\n    ((texture (sampler_blur3, tmpvar_9).xyz * scale3) + bias3)\\n  ).y * 0.01));\\n  ret_1.z = (texture (sampler_main, tmpvar_11).z - ((texture (sampler_main, tmpvar_11).xyz - \\n    ((texture (sampler_blur3, tmpvar_11).xyz * scale3) + bias3)\\n  ).z * 0.01));\\n  ret_1 = (ret_1 + (-0.004 + (\\n    (texture (sampler_noise_lq, (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 1.2)) + rand_frame.xy)).xyz - 0.5)\\n   * 0.1)));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ret_1;\\n  ret = tmpvar_12.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_3.y;\\n  tmpvar_6.y = tmpvar_4.y;\\n  ret_1 = (mix (vec3(0.2, 0.0, 0.1), vec3(0.8, 0.6, 0.0), texture (sampler_main, (uv - \\n    (tmpvar_5 * 0.2)\\n  )).yyy) * ((\\n    (-(tmpvar_3.y) + tmpvar_4.y)\\n   + 1.0) + (\\n    (-(tmpvar_3.z) + tmpvar_4.z)\\n   * 2.0)));\\n  vec3 tmpvar_7;\\n  tmpvar_7 = mix (mix (ret_1, vec3(0.3, 0.0, 0.8), vec3((\\n    ((texture (sampler_blur1, (uv - (tmpvar_6 * 0.04))).xyz * scale1) + bias1)\\n  .x * 1.4))), vec3(1.0, 0.9, 0.4), texture (sampler_main, uv).zzz);\\n  ret_1 = tmpvar_7;\\n  vec4 tmpvar_8;\\n  tmpvar_8.w = 1.0;\\n  tmpvar_8.xyz = tmpvar_7;\\n  ret = tmpvar_8.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:1,gammaadj:1,decay:1,echo_zoom:1,wrap:0,darken_center:1,brighten:1,darken:1,wave_a:.001,warpanimspeed:.141,warpscale:14.497,zoom:.99951,warp:.75055,wave_r:0,wave_g:0,wave_b:0,ob_size:.5,ob_r:.01,ib_size:.26,mv_a:0},shapes:[{baseVals:{enabled:1,sides:6,rad:.17809,ang:.26,tex_zoom:.39711,g:.7,b:.6,r2:.4,g2:0,a2:1,border_a:0},init_eqs_str:\"a.q3=0;\",frame_eqs_str:\"a.x=.11+.22*randint(5);a.y=.1+.16*mod(2*a.time,6);a.b=.6+.3*a.q3;a.g=.7+.2*a.q3;a.r=.7;a.g2=.3*a.g;a.r2=.3*a.r;a.b2=.3*a.b;\"},{baseVals:{enabled:1,sides:6,additive:1,rad:.17809,ang:.26,tex_zoom:.99999,g:1,b:.4,a:.5,r2:.2,g2:0,b2:.3,a2:.6,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.q3=0;\",frame_eqs_str:\"a.x=.11+.22*randint(5);a.y=.1+.16*mod(2*a.time,6);a.b=.6+.3*a.q3;a.g=.7+.2*a.q3;a.r=.7;a.g2=.3*a.g;a.r2=.3*a.r;a.b2=.3*a.b;\"},{baseVals:{enabled:1,sides:6,textured:1,rad:.17633,ang:.26,tex_zoom:1.19615,r2:1,b2:1,a2:.2,border_r:0,border_g:0,border_b:0,border_a:.2},init_eqs_str:\"a.q3=0;a.q4=0;a.q5=0;a.q1=0;\",frame_eqs_str:\"a.x=.22*randint(6);a.y=.18+.16*mod(2*a.time,5);a.g=1.5*a.q3;a.r=1.3*a.q4;a.b=1.7*a.q5;a.tex_ang=-a.q1;a.tex_zoom=2+Math.sin(a.q1);\"},{baseVals:{enabled:1,sides:6,textured:1,rad:.16284,ang:.26,r2:.8,g2:.8,b2:.8,a2:.2,border_r:0,border_g:0,border_b:0,border_a:.2},init_eqs_str:\"a.q3=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.x=.11+.22*randint(5);a.y=.1+.16*mod(2*a.time,6);a.r=.3*a.q3;a.b=.2*a.q2;a.g=.1*a.q1;a.tex_ang=a.q1;a.tex_zoom=2+1.2*Math.sin(1.5*a.q1);\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.q12=0;a.w2=0;a.ref_ang=0;a.x1=0;a.vx3=0;a.q13=0;a.q6=0;a.q1=0;a.q5=0;a.q9=0;a.vx1=0;a.vx4=0;a.x3=0;a.q11=0;a.q10=0;a.q4=0;a.vy4=0;a.bounce=0;a.x4=0;a.w1=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.vy1=0;a.q2=0;a.q14=0;a.v1=0;a.vx2=0;a.q3=0;a.y4=0;a.q7=0;a.vy3=0;a.v2=0;a.q8=0;a.x1=.5;a.x2=.51;a.y2=1;a.y1=.7;a.x3=.8;a.y3=.5;a.x4=.2;a.y4=.5;a.ax1=0;a.ay1=0;a.ax2=0;a.ay2=0;a.ax3=0;a.ay3=0;a.vx1=0;a.vx2=0;\",frame_eqs_str:\"a.sx=1+.01*mod(8*a.bass,8)*equal(mod(a.time,Math.floor(24-2*a.bass)),0);a.sy=1+.01*mod(8*a.mid,8)*equal(mod(a.time,12+Math.floor(24-2*a.bass)),0);a.zoom=.99;a.wave_a=0;a.r=.12+.004*Math.max(a.bass_att,a.treb_att);a.bounce=below(a.y1,a.r);a.y1+=a.vy1;a.vy1=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy1)+.1*(a.r-a.y1):a.vy1-div(.0036,a.fps);a.bounce=below(a.x1,a.r);a.x1+=a.vx1;a.vx1=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx1)+.1*(a.r-a.x1):a.vx1;a.bounce=above(a.x1,1-a.r);a.vx1=\\n.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx1)+.04*(1-a.r-a.x1):a.vx1;a.bounce=below(a.y2,a.r);a.y2+=a.vy2;a.vy2=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy2)+.1*(a.r-a.y2):a.vy2-div(.0036,a.fps);a.bounce=below(a.x2,a.r);a.x2+=a.vx2;a.vx2=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx2)+.1*(a.r-a.x2):a.vx2;a.bounce=above(a.x2,1-a.r);a.vx2=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx2)+.1*(1-a.r-a.x2):a.vx2;a.bounce=below(a.y3,a.r);a.y3+=a.vy3;a.vy3=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy3)+.1*(a.r-\\na.y3):a.vy3-div(.0036,a.fps);a.bounce=below(a.x3,a.r);a.x3+=a.vx3;a.vx3=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx3)+.1*(a.r-a.x3):a.vx3;a.bounce=above(a.x3,1-a.r);a.vx3=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx3)+.1*(1-a.r-a.x3):a.vx3;a.bounce=below(a.y4,a.r);a.y4+=a.vy4;a.vy4=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy4)+.1*(a.r-a.y4):a.vy4-div(.0036,a.fps);a.bounce=below(a.x4,a.r);a.x4+=a.vx4;a.vx4=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx4)+.1*(a.r-a.x4):a.vx4;a.bounce=above(a.x4,1-a.r);\\na.vx4=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx4)+.1*(1-a.r-a.x4):a.vx4;a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x2-a.vx2)+sqr(a.y1+a.vy1-a.y2-a.vy2)),2*a.r);a.ref_ang=Math.atan2(a.x2-a.x1,a.y2-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx2,a.vy2);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;\\na.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy2;a.bounce=\\nbelow(sqrt(sqr(a.x1+a.vx1-a.x3-a.vx3)+sqr(a.y1+a.vy1-a.y3-a.vy3)),2*a.r);a.ref_ang=Math.atan2(a.x3-a.x1,a.y3-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx3,a.vy3);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+\\nMath.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy3;a.bounce=below(sqrt(sqr(a.x2+a.vx2-a.x3-a.vx3)+sqr(a.y2+a.vy2-a.y3-a.vy3)),2*a.r);a.ref_ang=\\nMath.atan2(a.x3-a.x2,a.y3-a.y2)+Math.asin(1);a.v1=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.v2=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.w1=Math.atan2(a.vx2,a.vy2);a.w2=Math.atan2(a.vx3,a.vy3);a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy2;\\na.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy3;a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x4-a.vx4)+sqr(a.y1+a.vy1-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x1,a.y4-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=\\nsqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx4,a.vy4);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+\\nMath.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy4;a.bounce=below(sqrt(sqr(a.x2+a.vx2-a.x4-a.vx4)+sqr(a.y2+a.vy2-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x2,a.y4-a.y2)+Math.asin(1);a.v1=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.v2=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx2,a.vy2);a.w2=Math.atan2(a.vx4,a.vy4);a.vx2=.00001<\\nMath.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy2;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?\\nMath.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy4;a.bounce=below(sqrt(sqr(a.x3+a.vx3-a.x4-a.vx4)+sqr(a.y3+a.vy3-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x3,a.y4-a.y3)+Math.asin(1);a.v1=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.v2=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx3,a.vy3);a.w2=Math.atan2(a.vx4,a.vy4);a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*\\na.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy3;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-\\na.ref_ang-Math.asin(1)):a.vy4;a.q1=a.aspectx;a.q2=a.aspecty;a.q3=a.x1;a.q4=a.y1;a.q5=a.r;a.q6=a.x2;a.q7=a.y2;a.q8=a.r;a.q9=a.x3;a.q10=a.y3;a.q11=a.r;a.q12=a.x4;a.q13=a.y4;a.q14=a.r;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1.z = (texture (sampler_main, uv).z * 0.5);\\n  vec2 tmpvar_2;\\n  tmpvar_2 = ((uv_orig * texsize.xy) * texsize_noise_lq.zw);\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 4.0);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  tmpvar_4.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  ret_1.y = texture (sampler_fw_main, clamp ((uv_orig + (\\n    (tmpvar_4 * texsize.zw)\\n   * 4.0)), 0.0, 1.0)).y;\\n  ret_1.y = (ret_1.y + ((\\n    (ret_1 - ((texture (sampler_blur1, uv_orig).xyz * scale1) + bias1))\\n  .y * 0.025) + -0.014));\\n  ret_1.y = (ret_1.y + ((texture (sampler_noise_lq, tmpvar_2).y - 0.5) * 0.02));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  tmpvar_5.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  ret_1.x = ((texture (sampler_main, (uv - \\n    ((tmpvar_5 * texsize.zw) * 4.0)\\n  )).x - (ret_1.y * 0.01)) + 0.004);\\n  ret_1.x = (ret_1.x + ((\\n    (texture (sampler_noise_lq, tmpvar_2).x - 0.5)\\n   * 0.01) + (ret_1.z * 0.14)));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_1;\\n  ret = tmpvar_6.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.x = q3;\\n  tmpvar_1.y = q4;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = q6;\\n  tmpvar_2.y = q7;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q9;\\n  tmpvar_3.y = q10;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q12;\\n  tmpvar_4.y = q13;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (texsize.zw * 1.2);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (texture (sampler_main, (uv + (vec2(1.0, 0.0) * tmpvar_5))).xyz - texture (sampler_main, (uv - (vec2(1.0, 0.0) * tmpvar_5))).xyz);\\n  vec3 tmpvar_7;\\n  tmpvar_7 = (texture (sampler_main, (uv + (vec2(0.0, 1.0) * tmpvar_5))).xyz - texture (sampler_main, (uv - (vec2(0.0, 1.0) * tmpvar_5))).xyz);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_8.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_9;\\n  x_9 = ((uv - (tmpvar_8 * 8.0)) - (1.0 - tmpvar_1));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_10.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_11;\\n  x_11 = ((uv - (tmpvar_10 * 8.0)) - (1.0 - tmpvar_2));\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_12.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_13;\\n  x_13 = ((uv - (tmpvar_12 * 8.0)) - (1.0 - tmpvar_3));\\n  vec2 tmpvar_14;\\n  tmpvar_14.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_14.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_15;\\n  x_15 = ((uv - (tmpvar_14 * 8.0)) - (1.0 - tmpvar_4));\\n  vec4 tmpvar_16;\\n  tmpvar_16.w = 1.0;\\n  tmpvar_16.xyz = (mix (texture (sampler_main, uv).xyz, max (\\n    max ((vec3((1.0 - pow (\\n      sqrt(dot (x_9, x_9))\\n    , 0.2))) * vec3(2.0, 1.0, -1.0)), (vec3((1.0 - pow (\\n      sqrt(dot (x_11, x_11))\\n    , 0.2))) * vec3(2.0, -1.0, 1.0)))\\n  , \\n    max ((vec3((1.0 - pow (\\n      sqrt(dot (x_13, x_13))\\n    , 0.2))) * vec3(-1.0, 1.0, 2.0)), (vec3((1.0 - pow (\\n      sqrt(dot (x_15, x_15))\\n    , 0.2))) * vec3(1.0, -1.0, 2.0)))\\n  ), vec3(0.5, 0.5, 0.5)) * 1.25);\\n  ret = tmpvar_16.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.995,echo_zoom:1.007,echo_orient:3,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,wave_a:.001,wave_scale:.958,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:.05,ob_g:.1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:25.6,mv_y:9.6,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.look=0;a.n=0;a.reg26=0;a.w=0;a.uvx0=0;a.reg34=0;a.reg28=0;a.reg23=0;a.q25=0;a.angchg=0;a.reg20=0;a.reg15=0;a.reg10=0;a.h3=0;a.q12=0;a.v3=0;a.q18=0;a.q22=0;a.q21=0;a.diry=0;a.q13=0;a.q6=0;a.posx=0;a.fps_=0;a.reg25=0;a.bt=0;a.uvx=0;a.q1=0;a.travel=0;a.posz=0;a.q5=0;a.q9=0;a.dirz=0;a.dec_s=0;a.reg16=0;a.v=0;a.slow=0;a.reg36=0;a.mm=0;a.h2=0;a.reg22=0;a.tt=0;a.uvy=0;a.rotz=0;a.bm=0;a.ly=0;a.dist_=0;a.q23=0;a.q24=0;a.reg24=0;a.cran0=0;a.ran2=0;a.q11=0;a.q10=0;a.reg14=0;a.posy=0;\\na.reg31=0;a.dirx=0;a.q4=0;a.start=0;a.reg12=0;a.reg13=0;a.c2=0;a.reg37=0;a.s3=0;a.yslope=0;a.lampy=0;a.q16=0;a.xslope=0;a.q26=0;a.reg38=0;a.reg35=0;a.reg11=0;a.mt=0;a.tx=0;a.avg=0;a.uvz=0;a.c3=0;a.uvy0=0;a.mx=0;a.reg27=0;a.mn=0;a.q19=0;a.beat=0;a.q17=0;a.reg32=0;a.lx=0;a.reg21=0;a.uvz0=0;a.len=0;a.reg18=0;a.reg30=0;a.bb=0;a.q2=0;a.q27=0;a.slen=0;a.q14=0;a.dist=0;a.h1=0;a.reg17=0;a.v1=0;a.speed=0;a.q3=0;a.s1=0;a.t0=0;a.s2=0;a.ran1=0;a.reg33=0;a.q7=0;a.ds=0;a.q28=0;a.lampx=0;a.ty=0;a.c1=0;a.v2=0;a.q20=\\n0;a.q8=0;a.avg=.01;a.q7=.25;a.q8=randint(2)-1;a.q16=1+randint(2);a.q18=randint(.8)+.1;a.q30=1;a.q31=128;a.start=1;a.travel=0;a.rotz=0;a.look=0;a.slow=0;a.t0=a.time+3;a.lampx=.5;a.lampy=.5;a.cran0=randint(1);for(var b=a.n=0;1E4>b;b++)a.gmegabuf[Math.floor(a.n)]=0,a.n+=1;for(b=a.n=0;1E4>b;b++)a.megabuf[Math.floor(a.n)]=0,a.n+=1;a.trelx=0;a.trely=0;a.trelz=0;a.reg20=1;a.reg21=0;a.reg22=0;a.reg23=0;a.reg24=1;a.reg25=0;a.reg26=0;a.reg27=0;a.reg28=1;b=0;do{b+=1;var c;a.ran1=div(randint(800),100);a.ran2=\\ndiv(randint(800),100);a.ran3=div(randint(800),100);a.posx=randint(5)-2;a.posy=randint(5)-2;a.posz=randint(5)-2;a.c1=Math.cos(a.ran1);a.c2=Math.cos(a.ran2);a.c3=Math.cos(a.ran3);a.s1=Math.sin(a.ran1);a.s2=Math.sin(a.ran2);a.s3=Math.sin(a.ran3);a.reg20=a.c2*a.c1;a.reg21=a.c2*a.s1;a.reg22=-a.s2;a.reg23=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg24=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg25=a.s3*a.c2;a.reg26=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg27=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg28=a.c3*a.c2;a.dist=.001;var d=0;do{d+=1;a.uvx=div(a.reg26*\\na.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:\\n.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):\\na.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.05;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(c)&&1048576>d);d=.06>a.dist?1:0}while(.00001<Math.abs(d)&&1048576>b);\",frame_eqs_str:\"a.fps_=0*a.fps_+1*(.00001<Math.abs(25>=a.fps?1:0)?a.fps:25+.5*(a.fps-25));a.dec_s=1-div(.06*30,a.fps_);a.beat=a.time>a.t0+3?1:0;a.t0=.00001<Math.abs(a.beat)?a.time:a.t0;a.speed=div(Math.min(.2,a.dist_-.02)*(1+2*a.avg)*(1-0*a.slow)*.7,a.q7);a.ds=a.ds*a.dec_s+div((1-a.dec_s)*a.speed*.25,a.fps_);a.rotz=.00001<Math.abs(.00001>Math.abs(a.rotz-0)?1:0)?a.beat*(randint(100)<20*a.travel?1:0)*(div(randint(10),10)-.3):bnot(a.beat*(30>randint(100)?1:0))*a.rotz;a.slow=.00001<Math.abs(bnot(a.slow))?\\na.beat*(6>randint(1E3*a.avg)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.look=.00001<Math.abs(bnot(a.look))?a.beat*(12>randint(1E3*a.speed)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.lx=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.lx;a.ly=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.ly;a.lampx=a.lampx*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.lx:.5);a.lampy=a.lampy*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.ly:.5);a.q1=a.lampx;a.q2=a.lampy;a.dirx=a.reg26;a.diry=a.reg27;a.dirz=\\na.reg28;a.posx+=a.ds*a.dirx;a.posy+=a.ds*a.diry;a.posz+=a.ds*a.dirz;a.q4=a.posx;a.q5=a.posy;a.q6=a.posz;a.angchg=(.2-a.dist_)*(.2>a.dist_?1:0)*2;a.travel=.00001<Math.abs(0<a.angchg?1:0)?0:a.travel+a.ds;a.v1=a.v1*a.dec_s+(1-a.dec_s)*a.rotz*a.ds;a.v2=a.v2*a.dec_s+div((1-a.dec_s)*a.angchg*a.xslope,a.fps_);a.v3=a.v3*a.dec_s+(1-a.dec_s)*(div(a.angchg*a.yslope,a.fps_)+2*a.v1*Math.sin(.1*a.time));a.reg30=a.reg20;a.reg31=a.reg21;a.reg32=a.reg22;a.reg33=a.reg23;a.reg34=a.reg24;a.reg35=a.reg25;a.reg36=a.reg26;\\na.reg37=a.reg27;a.reg38=a.reg28;a.n=0;for(var b=a.avg=0;5>b;b++){a.n+=1;a.ran1=div(randint(100),100);a.ran2=div(randint(100),200)-.25;a.tx=Math.cos(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.ty=Math.sin(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.c1=Math.cos(a.v1);a.c2=Math.cos(a.v2+a.ty);a.c3=Math.cos(a.v3+a.tx);a.s1=Math.sin(a.v1);a.s2=Math.sin(a.v2+a.ty);a.s3=Math.sin(a.v3+a.tx);a.reg10=a.c2*a.c1;a.reg11=a.c2*a.s1;a.reg12=-a.s2;a.reg13=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg14=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg15=a.s3*\\na.c2;a.reg16=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg17=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg18=a.c3*a.c2;a.reg20=a.reg30;a.reg21=a.reg31;a.reg22=a.reg32;a.reg23=a.reg33;a.reg24=a.reg34;a.reg25=a.reg35;a.reg26=a.reg36;a.reg27=a.reg37;a.reg28=a.reg38;a.q20=a.reg10*a.reg20+a.reg11*a.reg23+a.reg12*a.reg26;a.q21=a.reg10*a.reg21+a.reg11*a.reg24+a.reg12*a.reg27;a.q22=a.reg10*a.reg22+a.reg11*a.reg25+a.reg12*a.reg28;a.q23=a.reg13*a.reg20+a.reg14*a.reg23+a.reg15*a.reg26;a.q24=a.reg13*a.reg21+a.reg14*a.reg24+a.reg15*a.reg27;\\na.q25=a.reg13*a.reg22+a.reg14*a.reg25+a.reg15*a.reg28;a.q26=a.reg16*a.reg20+a.reg17*a.reg23+a.reg18*a.reg26;a.q27=a.reg16*a.reg21+a.reg17*a.reg24+a.reg18*a.reg27;a.q28=a.reg16*a.reg22+a.reg17*a.reg25+a.reg18*a.reg28;a.reg20=a.q20;a.reg21=a.q21;a.reg22=a.q22;a.reg23=a.q23;a.reg24=a.q24;a.reg25=a.q25;a.reg26=a.q26;a.reg27=a.q27;a.reg28=a.q28;a.dist=.002;var c,d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;\\na.uvx=8*(div(a.uvx,8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?\\n-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.1;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<\\nMath.abs(c)&&1048576>d);a.megabuf[Math.floor(a.n)]=a.megabuf[Math.floor(a.n)]*a.dec_s+(1-a.dec_s)*a.dist;a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5))}a.n=0;for(b=a.avg=0;5>b;b++)a.n+=1,a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5));a.xslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[1]-a.megabuf[3]),-3),3);a.yslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[4]-a.megabuf[2]),-3),3);a.monitor=a.avg;a.dist_=a.dist_*a.dec_s+(1-a.dec_s)*a.dist;a.q10=a.ds*a.q7;a.q14=Math.abs(a.ds)+2*(Math.abs(a.v1)+\\nMath.abs(a.v2)+Math.abs(a.v3))+div(1,255)+.05*a.start;a.q19=.6+.4*Math.sin(.02*a.time+6*a.cran0);a.start*=.9;a.q11=a.v1;a.q12=a.v2;a.q13=a.v3;a.monitor=a.q16;a.bb=.99*a.bb+.02*a.bass;a.mm=.99*a.mm+.02*a.mid;a.tt=.99*a.tt+.02*a.treb;a.mx=Math.max(Math.max(a.bb,a.mm),a.tt);a.mn=Math.min(Math.min(a.bb,a.mm),a.tt);a.h1=div(a.bb-a.mn,a.mx-a.mn);a.h2=div(a.mm-a.mn,a.mx-a.mn);a.h3=div(a.tt-a.mn,a.mx-a.mn);a.v=div(.1333,a.fps);a.bm+=(a.h1-a.h2)*a.v;a.mt+=(a.h2-a.h3)*a.v;a.bt+=(a.h1-a.h3)*a.v;a.w=2*a.bm;a.q3=\\nMath.sin(a.w);a.q9=Math.cos(a.w);a.q17=a.bm;a.q18=a.mt;a.q19=a.bt;\",pixel_eqs_str:\"a.warp=0;a.zoom=1;a.dx=div(-a.q12,a.q16)*(1+0*pow(a.x-.5,2));a.dy=div(a.q13,a.q16)*(1+0*pow(a.y-.5,2));a.rot=a.q11;\",warp:\"float sustain;\\nfloat xlat_mutabledist;\\nfloat xlat_mutablestruc;\\nvec2 xlat_mutableuv1;\\nvec3 xlat_mutableuv2;\\n shader_body { \\n  mat3 tmpvar_1;\\n  tmpvar_1[uint(0)].x = q20;\\n  tmpvar_1[uint(0)].y = q23;\\n  tmpvar_1[uint(0)].z = q26;\\n  tmpvar_1[1u].x = q21;\\n  tmpvar_1[1u].y = q24;\\n  tmpvar_1[1u].z = q27;\\n  tmpvar_1[2u].x = q22;\\n  tmpvar_1[2u].y = q25;\\n  tmpvar_1[2u].z = q28;\\n  vec3 tmpvar_2;\\n  tmpvar_2.x = q4;\\n  tmpvar_2.y = q5;\\n  tmpvar_2.z = q6;\\n  sustain = (1.0123 - q14);\\n  vec2 uv_3;\\n  vec3 ret_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv - 0.5);\\n  xlat_mutableuv1 = ((tmpvar_5 * aspect.xy) * q16);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_pc_main, uv);\\n  uv_3 = ((tmpvar_5 * (1.0 - \\n    (q10 / (1.0 - ((tmpvar_6.z + \\n      (0.003921569 * tmpvar_6.y)\\n    ) + (q10 * 0.7))))\\n  )) + 0.5);\\n  vec4 tmpvar_7;\\n  tmpvar_7 = fract((8.0 * texture (sampler_noise_lq, (uv_3 + rand_frame.yz))));\\n  xlat_mutabledist = tmpvar_7.x;\\n  if ((tmpvar_7.y > 0.2)) {\\n    vec3 tmpvar_8;\\n    tmpvar_8 = (tmpvar_7.xyz - vec3(0.4, 0.5, 0.5));\\n    vec2 uvi_9;\\n    uvi_9 = ((tmpvar_8.zy * 0.003) + uv_3);\\n    vec2 pix_10;\\n    vec4 nb2_11;\\n    vec4 nb_12;\\n    vec2 x_13;\\n    x_13 = (uvi_9 - 0.5);\\n    pix_10 = (texsize.zw * (1.0 + (\\n      sqrt(dot (x_13, x_13))\\n     * 8.0)));\\n    float tmpvar_14;\\n    tmpvar_14 = (q10 * 0.7);\\n    vec4 tmpvar_15;\\n    tmpvar_15 = texture (sampler_pc_main, (uvi_9 - pix_10));\\n    nb_12.x = (1.0 - ((tmpvar_15.z + \\n      (0.003921569 * tmpvar_15.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_16;\\n    tmpvar_16 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(1.0, -1.0))));\\n    nb_12.y = (1.0 - ((tmpvar_16.z + \\n      (0.003921569 * tmpvar_16.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_17;\\n    tmpvar_17 = texture (sampler_pc_main, (uvi_9 + pix_10));\\n    nb_12.z = (1.0 - ((tmpvar_17.z + \\n      (0.003921569 * tmpvar_17.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_18;\\n    tmpvar_18 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(-1.0, 1.0))));\\n    nb_12.w = (1.0 - ((tmpvar_18.z + \\n      (0.003921569 * tmpvar_18.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_19;\\n    tmpvar_19 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(0.0, -1.0))));\\n    nb2_11.x = (1.0 - ((tmpvar_19.z + \\n      (0.003921569 * tmpvar_19.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_20;\\n    tmpvar_20 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(1.0, 0.0))));\\n    nb2_11.y = (1.0 - ((tmpvar_20.z + \\n      (0.003921569 * tmpvar_20.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_21;\\n    tmpvar_21 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(0.0, 1.0))));\\n    nb2_11.z = (1.0 - ((tmpvar_21.z + \\n      (0.003921569 * tmpvar_21.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_22;\\n    tmpvar_22 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(-1.0, 0.0))));\\n    nb2_11.w = (1.0 - ((tmpvar_22.z + \\n      (0.003921569 * tmpvar_22.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_23;\\n    tmpvar_23 = min (nb_12, nb2_11);\\n    nb_12.zw = tmpvar_23.zw;\\n    nb_12.xy = min (tmpvar_23.xy, tmpvar_23.zw);\\n    xlat_mutabledist = (min (nb_12.x, nb_12.y) + ((0.008 * tmpvar_8.x) * abs(tmpvar_8.y)));\\n  };\\n  vec4 tmpvar_24;\\n  tmpvar_24 = texture (sampler_pc_main, uv_3);\\n  float tmpvar_25;\\n  tmpvar_25 = min (xlat_mutabledist, (1.0 - (\\n    (tmpvar_24.z + (0.003921569 * tmpvar_24.y))\\n   + \\n    (q10 * 0.7)\\n  )));\\n  xlat_mutabledist = tmpvar_25;\\n  float tmpvar_26;\\n  tmpvar_26 = (tmpvar_25 + pow (tmpvar_25, 3.0));\\n  vec3 tmpvar_27;\\n  tmpvar_27.xy = (xlat_mutableuv1 * tmpvar_26);\\n  tmpvar_27.z = tmpvar_26;\\n  xlat_mutableuv2 = (((tmpvar_27 / q7) * tmpvar_1) + tmpvar_2);\\n  xlat_mutableuv2 = ((fract(\\n    ((xlat_mutableuv2 / 8.0) + 0.5)\\n  ) - 0.5) * 8.0);\\n  float li_28;\\n  vec3 zz0_29;\\n  vec3 zz_30;\\n  zz0_29 = (xlat_mutableuv2 + q8);\\n  li_28 = 0.0;\\n  zz_30 = ((2.0 * clamp (xlat_mutableuv2, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - xlat_mutableuv2);\\n  float tmpvar_31;\\n  tmpvar_31 = dot (zz_30, zz_30);\\n  if ((tmpvar_31 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_31 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_31);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_32;\\n  tmpvar_32 = dot (zz_30, zz_30);\\n  if ((tmpvar_32 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_32 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_32);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_33;\\n  tmpvar_33 = dot (zz_30, zz_30);\\n  if ((tmpvar_33 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_33 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_33);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_34;\\n  tmpvar_34 = dot (zz_30, zz_30);\\n  if ((tmpvar_34 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_34 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_34);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_35;\\n  tmpvar_35 = dot (zz_30, zz_30);\\n  if ((tmpvar_35 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_35 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_35);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_36;\\n  tmpvar_36 = dot (zz_30, zz_30);\\n  if ((tmpvar_36 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_36 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_36);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_37;\\n  tmpvar_37 = dot (zz_30, zz_30);\\n  if ((tmpvar_37 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_37 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_37);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_38;\\n  tmpvar_38 = dot (zz_30, zz_30);\\n  if ((tmpvar_38 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_38 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_38);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  vec4 tmpvar_39;\\n  tmpvar_39.xyz = zz_30;\\n  tmpvar_39.w = li_28;\\n  float tmpvar_40;\\n  tmpvar_40 = sqrt(dot (zz_30, zz_30));\\n  xlat_mutablestruc = (sqrt(dot (tmpvar_39.xyw, tmpvar_39.xyw)) / 24.0);\\n  vec4 tmpvar_41;\\n  tmpvar_41 = texture (sampler_pc_main, uv_3);\\n  float tmpvar_42;\\n  float tmpvar_43;\\n  tmpvar_43 = (q10 * 0.7);\\n  tmpvar_42 = ((log(\\n    (1.0 + (tmpvar_40 / 24.0))\\n  ) * 0.02) * (1.0 - (1.0 - \\n    ((tmpvar_41.z + (0.003921569 * tmpvar_41.y)) + tmpvar_43)\\n  )));\\n  float tmpvar_44;\\n  vec4 tmpvar_45;\\n  tmpvar_45 = texture (sampler_pc_main, uv_3);\\n  tmpvar_44 = (1.0 - ((tmpvar_45.z + \\n    (0.003921569 * tmpvar_45.y)\\n  ) + tmpvar_43));\\n  if ((((tmpvar_25 <= tmpvar_44) && (tmpvar_40 < 24.0)) && (tmpvar_25 > 0.005))) {\\n    ret_4.x = (((1.0 - sustain) * xlat_mutablestruc) + (sustain * mix (texture (sampler_main, uv_3).xyz, \\n      ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1)\\n    , vec3(\\n      (q14 * 4.0)\\n    )).x));\\n    float x_46;\\n    x_46 = ((1.0 - tmpvar_25) * 255.0);\\n    float ip_47;\\n    ip_47 = float(int(x_46));\\n    vec2 tmpvar_48;\\n    tmpvar_48.x = (x_46 - ip_47);\\n    tmpvar_48.y = (ip_47 / 255.0);\\n    ret_4.yz = tmpvar_48;\\n  } else {\\n    vec3 tmpvar_49;\\n    tmpvar_49.y = 0.0;\\n    tmpvar_49.x = sustain;\\n    tmpvar_49.z = (1.0 - tmpvar_42);\\n    vec3 tmpvar_50;\\n    tmpvar_50.xy = vec2(0.003921569, 0.0);\\n    tmpvar_50.z = (q14 / 6.0);\\n    ret_4 = ((texture (sampler_fc_main, uv_3).xyz * tmpvar_49) - tmpvar_50);\\n  };\\n  vec4 tmpvar_51;\\n  tmpvar_51.w = 1.0;\\n  tmpvar_51.xyz = ret_4;\\n  ret = tmpvar_51.xyz;\\n }\",comp:\"vec2 xlat_mutabled;\\nvec3 xlat_mutabledx;\\nvec3 xlat_mutabledy;\\n shader_body { \\n  vec3 ret_1;\\n  xlat_mutabled = (texsize.zw * 1.5);\\n  xlat_mutabledx = (texture (sampler_main, (uv_orig + (vec2(1.0, 0.0) * xlat_mutabled))).xyz - texture (sampler_main, (uv_orig - (vec2(1.0, 0.0) * xlat_mutabled))).xyz);\\n  xlat_mutabledy = (texture (sampler_main, (uv_orig + (vec2(0.0, 1.0) * xlat_mutabled))).xyz - texture (sampler_main, (uv_orig - (vec2(0.0, 1.0) * xlat_mutabled))).xyz);\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = xlat_mutabledx.y;\\n  tmpvar_2.y = xlat_mutabledy.y;\\n  vec2 x_3;\\n  x_3 = (tmpvar_2 * 8.0);\\n  ret_1 = (((texture (sampler_main, uv).x * \\n    (1.0 - sqrt(dot (x_3, x_3)))\\n  ) * pow (hue_shader, vec3(6.0, 6.0, 6.0))) * 1.4);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = xlat_mutabledx.z;\\n  tmpvar_4.y = xlat_mutabledy.z;\\n  vec2 x_5;\\n  x_5 = (tmpvar_4 * 4.0);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = mix (ret_1, vec3(1.0, 1.0, 1.0), vec3(sqrt(dot (x_5, x_5))));\\n  ret_1 = tmpvar_6;\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = tmpvar_6;\\n  ret = tmpvar_7.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.9,echo_zoom:1.169,echo_orient:1,wave_mode:5,additivewave:1,wave_a:0,wave_scale:.9,wave_smoothing:.63,wave_mystery:1,modwavealphastart:2,modwavealphaend:2,warpscale:2.853,rot:.006,warp:0,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.005,mv_x:0,mv_y:48,mv_dx:-.941,mv_dy:.426,mv_l:5,mv_r:.316,mv_g:.078,mv_b:.942,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:100,textured:1,rad:.78903,ang:.62832,tex_zoom:1.02009,r:0,g:1,b:1,r2:.7,b2:1,border_a:0},init_eqs_str:\"a.vx=0;a.vy=0;\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.y3=0;a.y1=0;a.xx=0;a.res=0;a.q12=0;a.x1=0;a.vx3=0;a.q13=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.c_x=0;a.c_y=0;a.q9=0;a.d1=0;a.v=0;a.si1=0;a.vx4=0;a.diff=0;a.x3=0;a.q23=0;a.q24=0;a.d2=0;a.q11=0;a.q10=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.dir=0;a.x4=0;a.r=0;a.x2=0;a.beat=0;a.vol=0;a.vy2=0;a.y2=0;a.size=0;a.q2=0;a.q14=0;a.si2=0;a.vx2=0;a.q3=0;a.yy=0;a.y4=0;a.q7=0;a.vy3=0;a.strength=0;a.xx1=0;a.velocity=0;a.q8=0;a.c_x=.5;a.c_y=.5;\",frame_eqs_str:\"a.sx=1+.01*mod(8*a.bass,8)*equal(mod(a.time,Math.floor(24-2*a.bass)),0);a.sy=1+.01*mod(8*a.mid,8)*equal(mod(a.time,12+Math.floor(24-2*a.bass)),0);a.q1=a.aspectx;a.q2=a.aspecty;a.rot=0;a.zoom=1;a.warp=0;a.vol=8*a.bass+4*a.mid+2*a.treb;a.vol*=above(a.vol,17);a.monitor=a.vol;a.beat=above(a.vol,a.res);a.diff=(1-a.beat)*a.diff+a.beat*(a.vol-a.res);a.res=a.beat*(a.vol+2*a.diff)+(1-a.beat)*(a.res-div(60*(.04*a.diff+.12),a.fps));a.res=Math.max(0,a.res);a.monitor=a.res;a.r=.00001<Math.abs(a.beat)?\\n.0001*(randint(200)-100):a.r;a.rot=a.r;a.c_x=.00001<Math.abs(a.beat)?.5+.005*(randint(200)-100):a.c_x;a.c_y=.00001<Math.abs(a.beat)?.5+.005*(randint(200)-100):a.c_y;a.q23=a.c_x;a.q24=a.c_y;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+1.5*a.yy1;a.dt=div(.03,a.fps);a.vx2=a.vx2*(1-2*a.dt)+10*a.dt*(a.x1+a.x3-2*a.x2);a.vy2=a.vy2*(1-2*a.dt)+a.dt*(10*(a.y1+a.y3-2*a.y2)-.5);a.vx3=a.vx3*(1-2*a.dt)+10*a.dt*(a.x2+a.x4-2*a.x3);a.vy3=\\na.vy3*(1-2*a.dt)+a.dt*(10*(a.y2+a.y4-2*a.y3)-.5);a.vx4=a.vx4*(1-2*a.dt)+10*a.dt*(a.x3-a.x4);a.vy4=a.vy4*(1-2*a.dt)+a.dt*(10*(a.y3-a.y4)-.5);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:.5*Math.abs(a.vx2);a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:.5*-Math.abs(a.vx2);a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:.5*Math.abs(a.vx3);a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:.5*-Math.abs(a.vx3);a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:\\n.5*Math.abs(a.vx4);a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:.5*-Math.abs(a.vx4);a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:.5*Math.abs(a.vy2);a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:.5*-Math.abs(a.vy2);a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:.5*Math.abs(a.vy3);a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:.5*-Math.abs(a.vy3);a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:.5*Math.abs(a.vy4);a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:.5*-Math.abs(a.vy4);a.q1=a.x1;a.q2=a.x2;a.q3=a.x3;a.q4=a.x4;a.q5=\\na.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.q9=div(1,a.aspectx);a.q10=div(1,a.aspecty);a.q11=a.aspectx;a.q12=a.aspecty;a.q13=.8*sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.q14=Math.atan2(a.vx4,a.vy4);\",pixel_eqs_str:\"a.d=pow(sqrt(sqr(a.x-a.q3)+sqr(a.y-a.q4)),2)-0;a.v=.03;a.dx=a.v*(a.x-a.q23)*a.d;a.dy=a.v*(a.y-a.q24)*a.d;a.x=.5+(a.x-.5)*a.q11;a.y=.5+(a.y-.5)*a.q12;a.dir=-a.q14+Math.asin(1);a.velocity=a.q13;a.strength=100;a.size=.07;a.xx=a.q4;a.yy=1-a.q8;a.x1=a.xx+Math.cos(a.dir+1.5708)*a.size;a.y1=a.yy-Math.sin(a.dir+1.5708)*a.size;a.x2=a.xx-Math.cos(a.dir+1.5708)*a.size;a.y2=a.yy+Math.sin(a.dir+1.5708)*a.size;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.size;a.si1=1-div(1,\\n1+pow(2,100*-a.d1));a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.size;a.si2=1-div(1,1+pow(2,100*-a.d2));a.dx+=(a.si1*Math.sin(a.y1-a.y)*a.d1-a.si2*Math.sin(a.y2-a.y)*a.d2)*a.strength*a.velocity;a.dy+=(-a.si1*Math.sin(a.x1-a.x)*a.d1+a.si2*Math.sin(a.x2-a.x)*a.d2)*a.strength*a.velocity;\",warp:\"vec2 xlat_mutabled;\\nvec3 xlat_mutabledx;\\nvec3 xlat_mutabledy;\\n shader_body { \\n  vec3 ret_1;\\n  vec3 tmpvar_2;\\n  tmpvar_2 = (texture (sampler_noise_lq, ((\\n    (uv_orig * texsize.xy)\\n   * texsize_noise_lq.zw) + rand_frame.xy)).xyz - 0.5);\\n  xlat_mutabled = (texsize.zw * 4.0);\\n  xlat_mutabledx = (((texture (sampler_blur1, \\n    (uv_orig + (vec2(1.0, 0.0) * xlat_mutabled))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_orig - (vec2(1.0, 0.0) * xlat_mutabled))\\n  ).xyz * scale1) + bias1));\\n  xlat_mutabledy = (((texture (sampler_blur1, \\n    (uv_orig + (vec2(0.0, 1.0) * xlat_mutabled))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_orig - (vec2(0.0, 1.0) * xlat_mutabled))\\n  ).xyz * scale1) + bias1));\\n  xlat_mutabled = uv;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = xlat_mutabledx.y;\\n  tmpvar_3.y = xlat_mutabledy.y;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = xlat_mutabledx.z;\\n  tmpvar_4.y = xlat_mutabledy.z;\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_main, uv_orig);\\n  ret_1.y = ((texture (sampler_main, (uv - \\n    ((tmpvar_3 * texsize.zw) * 6.0)\\n  )).y - 0.008) + ((1.0 - tmpvar_5.x) * 0.018));\\n  ret_1.z = ((texture (sampler_main, (uv - \\n    ((tmpvar_4 * texsize.zw) * 6.0)\\n  )).z - 0.008) + (tmpvar_5.x * 0.018));\\n  ret_1.x = texture (sampler_fc_main, (uv + ((tmpvar_2.xy * texsize.zw) * 0.5))).x;\\n  ret_1.x = (ret_1.x + ((\\n    (ret_1.x - ((texture (sampler_blur3, uv).xyz * scale3) + bias3).x)\\n   * 0.15) + (tmpvar_2 * 0.0042)).x);\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_1;\\n  ret = tmpvar_6.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.x = q3;\\n  tmpvar_1.y = q4;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = q6;\\n  tmpvar_2.y = q7;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q9;\\n  tmpvar_3.y = q10;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q12;\\n  tmpvar_4.y = q13;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (texsize.zw * 1.25);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (texture (sampler_main, (uv + (vec2(1.0, 0.0) * tmpvar_5))).xyz - texture (sampler_main, (uv - (vec2(1.0, 0.0) * tmpvar_5))).xyz);\\n  vec3 tmpvar_7;\\n  tmpvar_7 = (texture (sampler_main, (uv + (vec2(0.0, 1.0) * tmpvar_5))).xyz - texture (sampler_main, (uv - (vec2(0.0, 1.0) * tmpvar_5))).xyz);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_8.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_9;\\n  x_9 = ((uv - (tmpvar_8 * 8.0)) - tmpvar_1);\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_10.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_11;\\n  x_11 = ((uv - (tmpvar_10 * 8.0)) - tmpvar_2);\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_12.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_13;\\n  x_13 = ((uv - (tmpvar_12 * 8.0)) - tmpvar_3);\\n  vec2 tmpvar_14;\\n  tmpvar_14.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_14.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec2 x_15;\\n  x_15 = ((uv - (tmpvar_14 * 8.0)) - tmpvar_4);\\n  vec4 tmpvar_16;\\n  tmpvar_16.w = 1.0;\\n  tmpvar_16.xyz = mix (texture (sampler_main, uv).xyz, max (max (\\n    (vec3((1.0 - pow (sqrt(\\n      dot (x_9, x_9)\\n    ), 0.2))) * vec3(2.0, 1.0, -1.0))\\n  , \\n    (vec3((1.0 - pow (sqrt(\\n      dot (x_11, x_11)\\n    ), 0.2))) * vec3(2.0, -1.0, 1.0))\\n  ), max (\\n    (vec3((1.0 - pow (sqrt(\\n      dot (x_13, x_13)\\n    ), 0.2))) * vec3(-1.0, 1.0, 2.0))\\n  , \\n    (vec3((1.0 - pow (sqrt(\\n      dot (x_15, x_15)\\n    ), 0.2))) * vec3(1.0, -1.0, 2.0))\\n  )), vec3(0.5, 0.5, 0.5));\\n  ret = tmpvar_16.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.955,echo_zoom:.997,echo_alpha:.5,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wave_a:.001,wave_scale:.49,wave_smoothing:0,modwavealphastart:.71,modwavealphaend:1.3,warpscale:16.016,zoomexp:11.56276,fshader:1,zoom:1.05971,warp:.13126,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:0,ob_a:1,ib_size:0,mv_x:64,mv_y:48,mv_l:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:14,additive:1,num_inst:126,rad:.0303,tex_ang:.62832,r:0,b:1,g2:0,b2:1,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.8);\"},{baseVals:{enabled:1,sides:14,additive:1,num_inst:128,rad:.03,tex_ang:.62832,r:0,b:1,g2:0,b2:1,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.05*a.bass;a.ma-=3.1415*above(a.mid,1)*.05*a.mid;a.mx+=.0001*Math.cos(a.ma);a.my+=.0001*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.1);\"},{baseVals:{enabled:1,sides:14,additive:1,num_inst:32,rad:.0297,tex_ang:.62832,r:0,b:1,g2:0,b2:1,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.mid,1)*.01*a.mid;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0004*Math.cos(a.ma);a.my+=.0004*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.3);\"},{baseVals:{enabled:1,sides:14,additive:1,num_inst:16,rad:.0303,tex_ang:.62832,r:0,b:1,g2:0,b2:1,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,.5)*.02*a.bass;a.ma-=3.1415*above(a.treb,.5)*.02*a.treb;a.mx+=.0008*Math.cos(a.ma);a.my+=.0008*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.2);\"}],waves:[{baseVals:{enabled:1,thick:1,additive:1,scaling:2.0231,smoothing:0,r:0,b:0},init_eqs_str:\"a.d=0;a.tt2=0;a.res=0;a.tt1=0;a.diff=0;a.tt3=0;a.beat=0;a.vol=0;a.m=0;a.monitor=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.vol=8*a.bass+5*a.mid+3*a.treb;a.m=.97*a.m+.08*a.vol;a.monitor=a.vol;a.beat=above(a.vol,a.res)*above(a.vol,a.m)*above(a.vol,16);a.diff=(1-a.beat)*a.diff+a.beat*(a.vol-a.res);a.res=a.beat*(a.vol+.04*a.m)+(1-a.beat)*(a.res-div(60*(.1+.02*a.diff),a.fps));a.res=Math.max(0,a.res);a.a=a.beat;\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.5+a.d*a.sample*(1-a.sample)*4;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.xx=0;a.si3=0;a.q12=0;a.t1=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.q9=0;a.d1=0;a.si1=0;a.vx4=0;a.grav=0;a.x3=0;a.d2=0;a.q11=0;a.q10=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.m1=0;a.spring=0;a.si2=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=10;a.grav=.5;a.resist=1;a.bounce=.75;a.dt=.0005*div(60,a.fps);a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-\\na.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*\\na.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<\\nMath.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q1=a.x1;a.q2=a.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.q9=div(1,a.aspectx);a.q10=div(1,a.aspecty);a.zoom=1;a.r=.96*a.r+(a.x1-.5);a.rot=.1*a.r;a.q12=Math.atan2(a.vx4,a.vy4);a.q11=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.zoom=1.001;a.rot=0;a.warp=.2;a.wave_a=0;\",pixel_eqs_str:\"a.dir=-a.q12+Math.asin(1);a.b1=.1;a.m1=25*a.q11;a.t1=.05;a.xx=a.q4;a.yy=1-a.q8;a.x1=a.xx+Math.cos(a.dir+1.5708)*a.b1;a.y1=a.yy-Math.sin(a.dir+1.5708)*a.b1;a.x2=a.xx-Math.cos(a.dir+1.5708)*a.b1;a.y2=a.yy+Math.sin(a.dir+1.5708)*a.b1;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.b1;a.si1=1-div(1,1+pow(2,1E3*-a.d1));a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.b1;a.si2=1-div(1,1+pow(2,1E3*-a.d2));a.si3=0*-pow(a.q5,3);a.dx=2*(a.si1*Math.sin(a.y1-a.y)*a.m1*\\na.d1-a.si2*Math.sin(a.y2-a.y)*a.m1*a.d2+a.si3*Math.cos(a.dir)*a.t1);a.dy=2*(-a.si1*Math.sin(a.x1-a.x)*a.m1*a.d1+a.si2*Math.sin(a.x2-a.x)*a.m1*a.d2-a.si3*Math.sin(a.dir)*a.t1);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv + ((tmpvar_5 * texsize.zw) * 4.0));\\n  ret_1.z = (((texture (sampler_main, tmpvar_6).z - \\n    ((texture (sampler_main, tmpvar_6).z - ((texture (sampler_blur3, tmpvar_6).xyz * scale3) + bias3).z) * 0.02)\\n  ) - 0.008) + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * texsize_noise_lq.zw) + rand_frame.xy)\\n  ).xyz - 0.5) * 0.1)).x;\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_3.x;\\n  tmpvar_7.y = tmpvar_4.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = ((0.5 + (uv - 0.5)) - (tmpvar_7 * texsize.zw));\\n  ret_1.x = texture (sampler_main, tmpvar_8).x;\\n  ret_1.x = (ret_1.x + ((\\n    (ret_1.x - ((texture (sampler_blur3, tmpvar_8).xyz * scale3) + bias3))\\n  .x * 0.4) + 0.006));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_3.x;\\n  tmpvar_9.y = tmpvar_4.x;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_3.y;\\n  tmpvar_10.y = tmpvar_4.y;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = tmpvar_3.z;\\n  tmpvar_11.y = tmpvar_4.z;\\n  ret_1.y = texture (sampler_fc_main, (((uv - \\n    ((tmpvar_9 * texsize.zw) * 8.0)\\n  ) + (\\n    (tmpvar_10 * texsize.zw)\\n   * 4.0)) + ((tmpvar_11 * texsize.zw) * 8.0))).y;\\n  ret_1.y = (ret_1.y * (1.0 + (ret_1.x * 0.1)));\\n  ret_1.y = (ret_1.y - (0.004 + (\\n    clamp (ret_1.z, 0.0, 1.0)\\n   * 0.012)));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ret_1;\\n  ret = tmpvar_12.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 4.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_3.y;\\n  tmpvar_6.y = tmpvar_4.y;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = ((uv - (tmpvar_5 * 0.1)) + (tmpvar_6 * 0.06));\\n  vec4 tmpvar_8;\\n  tmpvar_8 = texture (sampler_main, uv);\\n  ret_1 = (((\\n    ((texture (sampler_blur2, tmpvar_7).xyz * scale2) + bias2)\\n  .x * \\n    clamp ((1.0 - tmpvar_8.z), 0.0, 1.0)\\n  ) * pow (hue_shader.yxz, vec3(8.0, 8.0, 8.0))) * 3.0);\\n  ret_1 = (mix (ret_1, (\\n    pow (hue_shader.yzx, vec3(8.0, 8.0, 8.0))\\n   * 1.4), vec3((\\n    (texture (sampler_main, tmpvar_7).x * 0.8)\\n   + \\n    ((texture (sampler_blur1, tmpvar_7).xyz * scale1) + bias1)\\n  .x))) * clamp ((1.0 - \\n    (((texture (sampler_blur1, uv).xyz * scale1) + bias1).y * 4.0)\\n  ), 0.0, 1.0));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_3.y;\\n  tmpvar_9.y = tmpvar_4.y;\\n  vec3 tmpvar_10;\\n  vec3 tmpvar_11;\\n  tmpvar_11 = pow (hue_shader, vec3(8.0, 8.0, 8.0));\\n  tmpvar_10 = mix (mix (ret_1, vec3(1.0, 1.0, 1.0), (\\n    (tmpvar_11 * texture (sampler_main, clamp ((uv - (tmpvar_9 * 2.0)), 0.0, 1.0)).z)\\n   * 1.2)), (tmpvar_11.zxy * 1.8), tmpvar_8.yyy);\\n  ret_1 = tmpvar_10;\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = tmpvar_10;\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.9,echo_zoom:1.169,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,wave_a:1.413,wave_scale:.418,wave_smoothing:0,wave_mystery:-.66,modwavealphastart:2,modwavealphaend:2,warpanimspeed:.626,warpscale:1.331,zoomexp:1.00001,zoom:.99951,warp:.08925,wave_r:0,wave_g:0,wave_x:.24,wave_y:.44,ob_size:.5,ob_r:.01,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_a:0},shapes:[{baseVals:{enabled:1,additive:1,rad:.0277,ang:6.03186,tex_ang:6.03186,tex_zoom:.6839,g:1,r2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.an=0;a.q6=0;a.q4=0;a.q5=0;a.q3=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.an+=a.q6;a.ang=.5*a.an;a.x=a.q4;a.y=a.q5;a.rad=a.q3*sqrt(2);a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:1,additive:1,rad:.0277,ang:6.03186,tex_ang:6.03186,tex_zoom:.6839,g:1,r2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.an=0;a.q9=0;a.q7=0;a.q8=0;a.q3=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.an+=a.q9;a.ang=.5*a.an;a.x=a.q7;a.y=a.q8;a.rad=a.q3*sqrt(2);a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:1,additive:1,rad:.0277,ang:6.03186,tex_ang:6.03186,tex_zoom:.6839,b:1,r2:1,g2:0,b2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.an=0;a.q12=0;a.q10=0;a.q11=0;a.q3=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.an+=a.q12;a.ang=.5*a.an;a.x=a.q10;a.y=a.q11;a.rad=a.q3*sqrt(2);a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,additive:1,scaling:2.0231,smoothing:0,r:0},init_eqs_str:\"a.d=0;a.tt2=0;a.res=0;a.tt1=0;a.diff=0;a.tt3=0;a.beat=0;a.vol=0;a.m=0;a.monitor=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.vol=8*a.bass+5*a.mid+3*a.treb;a.m=.97*a.m+.08*a.vol;a.monitor=a.vol;a.beat=above(a.vol,a.res)*above(a.vol,a.m)*above(a.vol,16);a.diff=(1-a.beat)*a.diff+a.beat*(a.vol-a.res);a.res=a.beat*(a.vol+.04*a.m)+(1-a.beat)*(a.res-div(60*(.1+.02*a.diff),a.fps));a.res=Math.max(0,a.res);a.a=a.beat;\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.5+a.d*a.sample*(1-a.sample)*2;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.q12=0;a.w2=0;a.ref_ang=0;a.du=0;a.x1=0;a.vx3=0;a.q13=0;a.q6=0;a.q1=0;a.q5=0;a.q9=0;a.vx1=0;a.mult=0;a.x3=0;a.q11=0;a.q10=0;a.ang2=0;a.dv=0;a.vr3=0;a.vr2=0;a.q4=0;a.bounce=0;a.vr=0;a.v2r=0;a.w1=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.vy1=0;a.q2=0;a.q14=0;a.dist=0;a.vr1=0;a.v1=0;a.vx2=0;a.q3=0;a.q7=0;a.vy3=0;a.v2=0;a.q8=0;a.x1=.5;a.y1=.6;a.x2=.5;a.y2=.4;a.x3=.5;a.y3=.2;a.vr1=.0001;a.vr2=0;a.vr3=0;a.vx1=0;a.vx2=0;a.vx3=0;\",frame_eqs_str:\"a.zoom=1;a.warp=0;a.wave_a=0;a.r=.03+.004*(a.bass_att+a.treb_att);a.monitor=a.aspecty;a.vr=Math.sin(a.vr1)*a.r;a.bounce=below(a.y1,a.r-.5*(a.aspectx-1));a.y1+=a.vy1;a.vy1=.00001<Math.abs(a.bounce)?.96*Math.abs(a.vy1)+.1*(a.r-a.y1-.5*(a.aspectx-1)):a.vy1-div(.018,a.fps);a.vx1=.00001<Math.abs(a.bounce)?a.vx1+.15*(a.vr-a.vx1):a.vx1;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vx1-a.vr):a.vr;a.vr1=Math.asin(div(a.vr,a.r));a.bounce=above(a.x1,1-a.r+.5*(a.aspecty-1));a.vx1=.00001<Math.abs(a.bounce)?\\n.96*-Math.abs(a.vx1)+.1*(1-a.r-a.x1+.5*(a.aspecty-1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?a.vy1+.15*(a.vr-a.vy1):a.vy1;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vy1-a.vr):a.vr;a.vr1=Math.asin(div(a.vr,a.r));a.bounce=below(a.x1,a.r-.5*(a.aspecty-1));a.x1+=a.vx1;a.vx1=.00001<Math.abs(a.bounce)?.96*Math.abs(a.vx1)+.1*(a.r-a.x1-.5*(a.aspecty-1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?a.vy1+.15*(-a.vr-a.vy1):a.vy1;a.vr=.00001<Math.abs(a.bounce)?a.vr-.85*(a.vy1+a.vr):a.vr;a.vr1=Math.asin(div(a.vr,a.r));\\na.vr=Math.sin(a.vr2)*a.r;a.bounce=below(a.y2,a.r-.5*(a.aspectx-1));a.y2+=a.vy2;a.vy2=.00001<Math.abs(a.bounce)?.96*Math.abs(a.vy2)+.1*(a.r-a.y2-.5*(a.aspectx-1)):a.vy2-div(.018,a.fps);a.vx2=.00001<Math.abs(a.bounce)?a.vx2+.15*(a.vr-a.vx2):a.vx2;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vx2-a.vr):a.vr;a.vr2=Math.asin(div(a.vr,a.r));a.bounce=above(a.x2,1-a.r+.5*(a.aspecty-1));a.vx2=.00001<Math.abs(a.bounce)?.96*-Math.abs(a.vx2)+.1*(1-a.r-a.x2+.5*(a.aspecty-1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?\\na.vy2+.15*(a.vr-a.vy2):a.vy2;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vy2-a.vr):a.vr;a.vr2=Math.asin(div(a.vr,a.r));a.bounce=below(a.x2,a.r-.5*(a.aspecty-1));a.x2+=a.vx2;a.vx2=.00001<Math.abs(a.bounce)?.96*Math.abs(a.vx2)+.1*(a.r-a.x2-.5*(a.aspecty-1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?a.vy2+.15*(-a.vr-a.vy2):a.vy2;a.vr=.00001<Math.abs(a.bounce)?a.vr-.85*(a.vy2+a.vr):a.vr;a.vr2=Math.asin(div(a.vr,a.r));a.vr=Math.sin(a.vr3)*a.r;a.bounce=below(a.y3,a.r-.5*(a.aspectx-1));a.y3+=a.vy3;a.vy3=.00001<\\nMath.abs(a.bounce)?.96*Math.abs(a.vy3)+.1*(a.r-a.y3-.5*(a.aspectx-1)):a.vy3-div(.018,a.fps);a.vx3=.00001<Math.abs(a.bounce)?a.vx3+.15*(a.vr-a.vx3):a.vx3;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vx3-a.vr):a.vr;a.vr3=Math.asin(div(a.vr,a.r));a.bounce=above(a.x3,1-a.r+.5*(a.aspecty-1));a.vx3=.00001<Math.abs(a.bounce)?.96*-Math.abs(a.vx3)+.1*(1-a.r-a.x3+.5*(a.aspecty-1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?a.vy3+.15*(a.vr-a.vy3):a.vy3;a.vr=.00001<Math.abs(a.bounce)?a.vr+.85*(a.vy3-a.vr):a.vr;a.vr3=\\nMath.asin(div(a.vr,a.r));a.bounce=below(a.x3,a.r-.5*(a.aspecty-1));a.x3+=a.vx3;a.vx3=.00001<Math.abs(a.bounce)?.96*Math.abs(a.vx3)+.1*(a.r-a.x3-.5*(a.aspecty-1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?a.vy3+.15*(-a.vr-a.vy3):a.vy3;a.vr=.00001<Math.abs(a.bounce)?a.vr-.85*(a.vy3+a.vr):a.vr;a.vr3=Math.asin(div(a.vr,a.r));a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x2-a.vx2)+sqr(a.y1+a.vy1-a.y2-a.vy2)),2*a.r);a.bounce*=below(sqrt(sqr(a.x1+a.vx1-a.x2-a.vx2)+sqr(a.y1+a.vy1-a.y2-a.vy2)),sqrt(sqr(a.x1-a.x2)+sqr(a.y1-\\na.y2)));a.ref_ang=Math.atan2(a.x2-a.x1,a.y2-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx2,a.vy2);a.vr=Math.sin(a.vr1)*a.r;a.v2r=Math.sin(a.vr2)*a.r;a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?\\nMath.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.v2r-a.vr-Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+\\n.1*(a.v2r-a.vr-Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy2;a.vr=.00001<Math.abs(a.bounce)?a.vr+.9*(Math.cos(a.w1-a.ref_ang)*(a.v1-a.v2)-a.vr):a.vr;a.vr1=Math.asin(div(a.vr,a.r));a.v2r=.00001<Math.abs(a.bounce)?a.v2r+.9*(Math.cos(a.w2-a.ref_ang)*(a.v2-a.v1)-a.v2r):a.v2r;a.vr2=Math.asin(div(a.v2r,a.r));a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x3-a.vx3)+sqr(a.y1+a.vy1-a.y3-a.vy3)),2*a.r);a.bounce*=below(sqrt(sqr(a.x1+\\na.vx1-a.x3-a.vx3)+sqr(a.y1+a.vy1-a.y3-a.vy3)),sqrt(sqr(a.x1-a.x3)+sqr(a.y1-a.y3)));a.ref_ang=Math.atan2(a.x3-a.x1,a.y3-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx3,a.vy3);a.vr=Math.sin(a.vr1)*a.r;a.v2r=Math.sin(a.vr3)*a.r;a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.sin(a.ref_ang+Math.asin(1))*a.v2*\\nMath.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.v2r-a.vr-Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx3;\\na.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.v2r-a.vr-Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy3;a.vr=.00001<Math.abs(a.bounce)?a.vr+.9*(Math.cos(a.w1-a.ref_ang)*(a.v1-a.v2)-a.vr):a.vr;a.vr1=Math.asin(div(a.vr,a.r));a.v2r=.00001<Math.abs(a.bounce)?a.v2r+.9*(Math.cos(a.w2-a.ref_ang)*(a.v2-a.v1)-a.v2r):a.v2r;a.vr3=Math.asin(div(a.v2r,a.r));a.bounce=below(sqrt(sqr(a.x3+\\na.vx3-a.x2-a.vx2)+sqr(a.y3+a.vy3-a.y2-a.vy2)),2*a.r);a.bounce*=below(sqrt(sqr(a.x2+a.vx2-a.x3-a.vx3)+sqr(a.y2+a.vy2-a.y3-a.vy3)),sqrt(sqr(a.x2-a.x3)+sqr(a.y2-a.y3)));a.ref_ang=Math.atan2(a.x2-a.x3,a.y2-a.y3)+Math.asin(1);a.v1=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.v2=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.w1=Math.atan2(a.vx3,a.vy3);a.w2=Math.atan2(a.vx2,a.vy2);a.vr=Math.sin(a.vr3)*a.r;a.v2r=Math.sin(a.vr2)*a.r;a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.sin(a.ref_ang)*\\na.v1*Math.cos(a.w1-a.ref_ang))+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.v2r-Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy3;a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.v2r-a.vr-Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.sin(a.ref_ang+\\nMath.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.v2r-a.vr-Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy2;a.vr=.00001<Math.abs(a.bounce)?a.vr+.9*(Math.cos(a.w1-a.ref_ang)*(a.v1-a.v2)-a.vr):a.vr;a.vr3=Math.asin(div(a.vr,a.r));a.v2r=.00001<Math.abs(a.bounce)?a.v2r+.9*(Math.cos(a.w2-a.ref_ang)*(a.v2-a.v1)-a.v2r):a.v2r;\\na.vr2=Math.asin(div(a.v2r,a.r));a.q1=a.aspectx;a.q2=a.aspecty;a.q3=2*a.r;a.q4=a.x1;a.q5=a.y1;a.q6=a.vr1;a.q7=a.x2;a.q8=a.y2;a.q9=a.vr2;a.q10=a.x3;a.q11=a.y3;a.q12=a.vr3;a.q13=Math.atan2(div(a.x1+a.x2+a.x3,3)-.5,div(a.y1+a.y2+a.y3,3)-.5);a.q14=.2*sigmoid(sqrt(sqr(div(a.x1+a.x2+a.x3,3)-.5)+sqr(div(a.y1+a.y2+a.y3,3)-.5)),2);\",pixel_eqs_str:\"a.du=2*a.x-1-div(2*(a.q4+a.q7+a.q10-1.5),3);a.dv=2*a.y-1+div(2*(a.q5+a.q8+a.q11-1.5),3);a.dist=sqrt(a.du*a.du+a.dv*a.dv);a.ang2=Math.atan2(a.du,a.dv);a.mult=.4*Math.sin(.05*a.dist)*a.q14;a.dx=a.mult*Math.sin(2*a.ang2+a.q13)*a.aspectx;a.dy=a.mult*Math.cos(2*a.ang2+a.q13)*a.aspecty;\",warp:\" shader_body { \\n  float n_1;\\n  vec3 ret_2;\\n  vec4 tmpvar_3;\\n  tmpvar_3 = texture (sampler_pw_main, uv);\\n  ret_2 = tmpvar_3.xyz;\\n  n_1 = (clamp ((texture (sampler_pw_main, \\n    (uv + texsize.zw)\\n  ).x * 999.0), 0.0, 1.0) + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(1.0, 0.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(1.0, -1.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(0.0, 1.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(0.0, -1.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(-1.0, 1.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv + (texsize.zw * vec2(-1.0, 0.0)))\\n  ).x * 999.0), 0.0, 1.0));\\n  n_1 = (n_1 + clamp ((texture (sampler_pw_main, \\n    (uv - texsize.zw)\\n  ).x * 999.0), 0.0, 1.0));\\n  if ((n_1 < 1.9)) {\\n    ret_2 = (tmpvar_3.xyz - 0.4);\\n  };\\n  if ((n_1 > 3.1)) {\\n    ret_2 = (ret_2 - 0.4);\\n  };\\n  bool tmpvar_4;\\n  if ((ret_2.x < 0.1)) {\\n    tmpvar_4 = (abs((n_1 - 3.0)) < 0.5);\\n  } else {\\n    tmpvar_4 = bool(0);\\n  };\\n  if (tmpvar_4) {\\n    ret_2 = (ret_2 + 0.4);\\n  };\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ret_2;\\n  ret = tmpvar_5.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret2_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 8.0);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1)).y;\\n  tmpvar_4.y = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1)).y;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv + (tmpvar_4 * 0.55));\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_blur3, uv);\\n  ret_2 = (vec3((0.25 * dot (\\n    clamp ((2.0 * ((tmpvar_6.xyz * scale3) + bias3)), 0.0, 1.0)\\n  , vec3(0.32, 0.49, 0.29)))) - (0.8 * dot (\\n    clamp (((20.0 * (\\n      (0.6 * ((texture (sampler_blur2, uv).xyz * scale2) + bias2))\\n     - 0.01)) - 2.0), 0.0, 1.0)\\n  , vec3(0.32, 0.49, 0.29))));\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_blur1, uv);\\n  ret_2 = (ret_2 + dot (clamp (\\n    ((30.0 * ((texture (sampler_main, uv).xyz + \\n      (((tmpvar_7.xyz * scale1) + bias1) * 0.15)\\n    ) - 0.01)) - 2.0)\\n  , 0.0, 1.0), vec3(0.32, 0.49, 0.29)));\\n  ret_2 = (ret_2 + 1.0);\\n  vec3 tmpvar_8;\\n  tmpvar_8 = mix (ret_2, (ret_2 * (\\n    ((texture (sampler_blur3, tmpvar_5).xyz * scale3) + bias3)\\n   - \\n    ((texture (sampler_blur1, tmpvar_5).xyz * scale1) + bias1)\\n  )), pow (hue_shader, ret_2));\\n  ret2_1 = (vec3((-0.5 * dot (\\n    ((texture (sampler_blur3, tmpvar_5).xyz * scale3) + bias3)\\n  , vec3(0.32, 0.49, 0.29)))) + (0.8 * (\\n    (texture (sampler_blur1, tmpvar_5).xyz * scale1)\\n   + bias1)));\\n  ret2_1 = (ret2_1 - (0.9 * texture (sampler_main, tmpvar_5).xyz));\\n  ret2_1 = (ret2_1 - 1.5);\\n  vec3 tmpvar_9;\\n  tmpvar_9 = mix (ret2_1, (ret2_1 * (\\n    ((tmpvar_6.xyz * scale3) + bias3)\\n   - \\n    ((tmpvar_7.xyz * scale1) + bias1)\\n  )), pow (hue_shader.zxy, tmpvar_8));\\n  ret2_1 = tmpvar_9;\\n  vec3 tmpvar_10;\\n  tmpvar_10 = abs((tmpvar_8 - tmpvar_9));\\n  ret_2 = (tmpvar_10 * tmpvar_10);\\n  ret_2 = (ret_2 * 1.15);\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = ret_2;\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1,decay:.9,echo_zoom:1,wave_brighten:0,wrap:0,wave_a:.001,wave_scale:5.715,wave_smoothing:.9,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.162,warpscale:5.582,zoomexp:.32104,zoom:.9901,warp:.11563,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,textured:1,rad:.05343,tex_zoom:12.77228,g:1,b:1,a:0,r2:1,b2:1,a2:1,border_g:0,border_a:0},init_eqs_str:\"a.vx=0;a.vy=0;\",frame_eqs_str:\"\"},{baseVals:{enabled:1,sides:100,textured:1,y:.75,rad:.46753,g:1,b:1,r2:1,b2:1,border_a:0},init_eqs_str:\"a.w=0;a.q1=0;\",frame_eqs_str:\"a.w=4*-Math.atan2(.5,a.q1)+4*Math.asin(1);a.ang=a.w;a.x=.5+.19*Math.sin(a.w);a.y=.5+.26*Math.cos(a.w);\"},{baseVals:{enabled:1,sides:100,textured:1,y:.75,rad:.46753,g:1,b:1,r2:1,b2:1,border_a:0},init_eqs_str:\"a.w=0;a.q1=0;\",frame_eqs_str:\"a.w=4*-Math.atan2(.5,a.q1)+4*Math.asin(1)+div(2*Math.asin(1),3);a.ang=a.w;a.x=.5+.19*Math.sin(a.w);a.y=.5+.26*Math.cos(a.w);\"},{baseVals:{enabled:1,sides:100,textured:1,y:.75,rad:.46753,g:1,b:1,r2:1,b2:1,border_a:0},init_eqs_str:\"a.w=0;a.q1=0;\",frame_eqs_str:\"a.w=4*-Math.atan2(.5,a.q1)+4*Math.asin(1)-div(2*Math.asin(1),3);a.ang=a.w;a.x=.5+.19*Math.sin(a.w);a.y=.5+.26*Math.cos(a.w);\"}],waves:[{baseVals:{enabled:1,usedots:1,thick:1,additive:1,scaling:2.44415,smoothing:0},init_eqs_str:\"a.d=0;a.n=0;a.y1=0;a.xx=0;a.z=0;a.w=0;a.t5=0;a.t1=0;a.x1=0;a.cl3=0;a.j3=0;a.cl2=0;a.zoom=0;a.j=0;a.cl1=0;a.t8=0;a.v=0;a.t3=0;a.t6=0;a.pi3=0;a.t7=0;a.c2=0;a.j2=0;a.s3=0;a.t=0;a.k=0;a.zz=0;a.c3=0;a.t2=0;a.bb=0;a.s1=0;a.s2=0;a.t4=0;a.yy=0;a.c=0;a.c1=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.t1=0;a.v=.01;a.j+=.01*a.bass;a.j2+=.01*a.mid_att;a.j3+=.01*a.treb_att;a.t2=a.j;a.t3=a.j2;a.t4=a.j3;a.k=.99*a.k+div(10*a.mid,a.fps);a.t5=-a.k;a.cl1=a.cl1-.0005-.003*a.bass;a.cl1=.00001<Math.abs(above(a.cl1,1))?0:a.cl1;a.cl1=.00001<Math.abs(below(a.cl1,0))?1:a.cl1;a.t8=a.cl1;a.cl2=a.cl2+.0001+.001*a.mid-.0005*a.bass-.0005*a.treb;a.cl2=.00001<Math.abs(above(a.cl2,1))?0:a.cl2;a.cl2=.00001<Math.abs(below(a.cl2,0))?1:a.cl2;a.t7=a.cl2;a.cl3=a.cl3+.0001+.001*a.treb-.0005*a.bass-.0005*\\na.mid;a.cl3=.00001<Math.abs(above(a.cl3,1))?0:a.cl3;a.cl3=.00001<Math.abs(below(a.cl3,0))?1:a.cl3;a.t6=a.cl3;\",point_eqs_str:\"a.xx=div(mod(983624912364*a.sample,1E7)+100,1E7);a.yy=div(mod(1896575575*a.xx,1E7)+100,1E7);a.zz=div(mod(58652340875*a.yy,1E7)+100,1E7);a.d=sqrt(sqr(a.xx)+sqr(a.yy)+sqr(a.zz));a.zz=a.zz+a.t8-(.00001<Math.abs(above(a.zz+a.t8,1))?1:0)-.5;a.xx=a.xx+a.t7-(.00001<Math.abs(above(a.xx+a.t7,1))?1:0)-.5;a.yy=a.yy+a.t6-(.00001<Math.abs(above(a.yy+a.t6,1))?1:0)-.5;a.v=.001;a.w=1;a.bb=a.d*a.d*.5;a.n=.3;a.s1=Math.sin(Math.sin(a.t2*a.w+a.bb)*a.n);a.s2=Math.sin(Math.sin(a.t3*a.w+a.bb)*a.n);\\na.s3=Math.sin(Math.sin(a.t4*a.w+a.bb)*a.n);a.c1=Math.cos(Math.sin(a.t2*a.w+a.bb)*a.n);a.c2=Math.cos(Math.sin(a.t3*a.w+a.bb)*a.n);a.c3=Math.cos(Math.sin(a.t4*a.w+a.bb)*a.n);a.z=(a.c3*a.s1*a.c2+a.s3*a.s2)*a.xx-(a.c3*a.s1*a.s2-a.s3*a.c2)*a.yy+a.c3*a.c1*a.zz;a.x1=a.c1*a.c2*a.xx+a.c1*a.s2*a.yy-a.s1*a.zz;a.y1=(a.s3*a.s1*a.c2-a.c3*a.s2)*a.xx+(a.s3*a.s1*a.s2+a.c3*a.c2)*a.yy+a.s3*a.c1*a.zz;a.zoom=.5*div(1,a.z+.5);a.x=.5+a.zoom*a.x1+0*Math.sin(.1*a.time);a.y=.5+a.zoom*a.y1+0*Math.cos(.16801*a.time);a.pi3=2.0941239;\\na.t=2*a.z+1*a.t2;a.c=3;a.r=Math.sin(a.t)*a.c;a.g=Math.sin(a.t+a.pi3)*a.c;a.b=Math.sin(a.t-a.pi3)*a.c;a.r=.00001<Math.abs(above(a.r,1))?1:a.r;a.r=.00001<Math.abs(below(a.r,0))?0:a.r;a.g=.00001<Math.abs(above(a.g,1))?1:a.g;a.g=.00001<Math.abs(below(a.g,0))?0:a.g;a.b=.00001<Math.abs(above(a.b,1))?1:a.b;a.b=.00001<Math.abs(below(a.b,0))?0:a.b;a.a=-a.z+.7;\"},{baseVals:{enabled:1,thick:1,scaling:2.44415,smoothing:0,a:.05},init_eqs_str:\"a.t8=0;a.q1=0;a.pi3=0;a.t=0;a.q4=0;a.q6=0;a.c=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.t8=1;\",point_eqs_str:\"a.t8=-a.t8;a.y=.48*a.sample;a.x=.5+.04*a.t8-a.t8*a.sample*.02+(sqr(2*a.sample-1)-1)*a.q1*.5;a.pi3=2.0941239;a.t=10*-(a.q4-a.q6)+a.sample*Math.asin(1)*4.01;a.c=9;a.r=Math.sin(a.t)*a.c;a.g=Math.sin(a.t+a.pi3)*a.c;a.b=Math.sin(a.t-a.pi3)*a.c;a.r=.00001<Math.abs(above(a.r,1))?1:a.r;a.r=.00001<Math.abs(below(a.r,0))?0:a.r;a.g=.00001<Math.abs(above(a.g,1))?1:a.g;a.g=.00001<Math.abs(below(a.g,0))?0:a.g;a.b=.00001<Math.abs(above(a.b,1))?1:a.b;a.b=.00001<Math.abs(below(a.b,0))?0:a.b;\\n\"},{baseVals:{enabled:1,samples:49,scaling:2.44415,smoothing:0,r:0,g:0,b:0},init_eqs_str:\"a.t8=0;a.q1=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.t8=1;\",point_eqs_str:\"a.t8=-1;a.y=.48*a.sample;a.x=.5+.04*a.t8-a.t8*a.sample*.02+(sqr(2*a.sample-1)-1)*a.q1*.5;\"},{baseVals:{enabled:1,samples:49,scaling:2.44415,smoothing:0,r:0,g:0,b:0},init_eqs_str:\"a.t8=0;a.q1=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.t8=1;\",point_eqs_str:\"a.t8=1;a.y=.48*a.sample;a.x=.5+.04*a.t8-a.t8*a.sample*.02+(sqr(2*a.sample-1)-1)*a.q1*.5;\"}],init_eqs_str:\"a.d=0;a.vt=0;a.q6=0;a.q1=0;a.q5=0;a.vb=0;a.q9=0;a.v=0;a.mm=0;a.tt=0;a.q4=0;a.bb=0;a.q2=0;a.q3=0;a.vvm=0;a.vvb=0;a.vm=0;a.vvt=0;a.x1=0;a.y1=0;\",frame_eqs_str:\"a.zoom=1;a.q1=.1*(a.bass-a.treb);a.vvb=.00001<Math.abs(below(a.vvb,0))?0:a.vvb;a.vvm=.00001<Math.abs(below(a.vvm,0))?0:a.vvm;a.vvt=.00001<Math.abs(below(a.vvt,0))?0:a.vvt;a.vb=.85*a.vb+(1-a.vb)*pow(a.bass,2)*.001;a.vvb=.95*a.vvb+(1-a.vvb)*a.vb*.2;a.vm=.85*a.vm+(1-a.vm)*pow(a.mid,2)*.01;a.vvm=.95*a.vvm+(1-a.vvm)*a.vm*.2;a.vt=.85*a.vt+(1-a.vt)*pow(a.treb,2)*.001;a.vvt=.95*a.vvt+(1-a.vvt)*a.vt*.2;a.q1=(a.vvb-a.vvt)*a.vvm;a.q2=a.vvm;a.q3=a.vvt;a.v=.2;a.d=0;a.bb=a.bb+a.vvb*a.v-a.d;\\na.mm=a.mm+a.vvm*a.v-a.d;a.tt=a.tt+a.vvt*a.v-a.d;a.q4=a.bb;a.q5=a.mm;a.q6=a.tt;a.q4=.00001<Math.abs(above(Math.abs(a.q1),.02))?.99:1;a.q9=.5+.5*Math.sin(.14*a.time);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_fc_main, uv) * q4).xyz;\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.x = 0.5;\\n  tmpvar_1.y = (1.0 - q9);\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (uv_orig - tmpvar_1);\\n  float tmpvar_3;\\n  tmpvar_3 = (3.0 / tmpvar_2.y);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = ((tmpvar_2.x * tmpvar_3) * q9);\\n  tmpvar_4.y = (tmpvar_3 * q9);\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_main, fract(((1.0 - \\n    abs(((fract(\\n      ((tmpvar_2 + vec2(0.5, 1.0)) * 0.5)\\n    ) * 2.0) - 1.0))\\n  ) - (\\n    ((texture (sampler_noise_hq, ((tmpvar_4 * 0.05) + (vec2(0.1, -0.05) * time))) - 0.5) * float(int((tmpvar_2.y > 0.0))))\\n  .xy * 0.025))));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (((tmpvar_5.xyz * tmpvar_5.xyz) * 1.4) - 0.04);\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.56,decay:1,echo_zoom:.362,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.599,wave_smoothing:0,wave_mystery:-.5,modwavealphastart:2,modwavealphaend:2,warpscale:.107,zoomexp:.1584,fshader:1,warp:.01,wave_r:.51,wave_g:.5,ob_size:0,ob_a:1,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:.5,mv_r:0,mv_g:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.y3=0;a.y1=0;a.xx=0;a.s=0;a.t1=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.v=0;a.vx4=0;a.mm=0;a.tt=0;a.grav=0;a.x3=0;a.xx2=0;a.q4=0;a.a=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.r=0;a.x2=0;a.mx=0;a.mn=0;a.vy2=0;a.y2=0;a.bb=0;a.q2=0;a.m1=0;a.spring=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.vy3=0;a.xx1=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.ib_r=.3*Math.sin(5*a.time)+.7;a.ib_g=.3*Math.sin(4*a.time)+.3;a.ib_b=.5*Math.sin(4*div(a.time,3))+.5;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=10;a.grav=.5;a.resist=1;a.bounce=.75;a.dt=.0002*div(60,a.fps);a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-\\n2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-a.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<\\nMath.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*a.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?\\na.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q4=a.x4;a.q8=a.y4;a.q1=a.aspectx;a.q2=a.aspecty;a.zoom=1;a.warp=0;a.bb=.99*a.bb+.02*a.bass;a.mm=.99*a.mm+.02*a.mid;a.tt=.99*a.tt+.02*a.treb;a.mx=Math.max(Math.max(a.bb,a.mm),a.tt);a.mn=Math.min(Math.min(a.bb,a.mm),a.tt);a.ob_r=div(a.bb-a.mn,a.mx-a.mn);a.ob_b=div(a.mm-a.mn,a.mx-a.mn);a.ob_g=div(a.tt-a.mn,a.mx-a.mn);a.q6=Math.atan2(a.vx4,\\na.vy4);a.q5=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.a=.95*a.a+a.q5;a.s=.9*a.s+a.a;a.q3=.1*a.s;a.monitor=a.s;a.wave_a=0;\",pixel_eqs_str:\"a.x=.5+(a.x-.5)*a.q1;a.y=.5+(a.y-.5)*a.q2;a.dir=-a.q6+0*Math.asin(1);a.b1=.1;a.m1=25*a.q5;a.t1=.05;a.xx=a.q4;a.yy=1-a.q8;a.dx=0;a.dy=0;a.d=sqrt((a.x-a.xx)*(a.x-a.xx)+(a.y-a.yy)*(a.y-a.yy));a.r=.05;a.v=-35*a.q5;a.dx=a.v*Math.sin(a.dir)*(a.r-a.d)*(1-sigmoid(a.d-a.r,200))*a.q2;a.dy=a.v*Math.cos(a.dir)*(a.r-a.d)*(1-sigmoid(a.d-a.r,200))*a.q1;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  tmpvar_1.xyz = ((texture (sampler_main, clamp (\\n    (uv - (((vec2(0.0, 32.0) * texsize.zw) * dot (\\n      (tmpvar_2.xyz - 0.35)\\n    , vec3(0.32, 0.49, 0.29))) * (dot (tmpvar_2.xyz, vec3(0.32, 0.49, 0.29)) - 0.4)))\\n  , 0.0, 1.0)).xyz - 0.0011) + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 1.5)) + rand_frame.xy)\\n  ) - 0.5) * 0.0038).xyz);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (texture (sampler_main, (uv - tmpvar_2)).xyz - texture (sampler_main, (uv + tmpvar_2)).xyz).x;\\n  tmpvar_4.y = (texture (sampler_main, (uv - tmpvar_3)).xyz - texture (sampler_main, (uv + tmpvar_3)).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    ((uv - 0.5) * 2.0)\\n  )) - tmpvar_4);\\n  float tmpvar_5;\\n  tmpvar_5 = clamp ((0.04 / sqrt(\\n    dot (uv1_1, uv1_1)\\n  )), 0.0, 1.0);\\n  uv1_1 = ((0.3 * cos(\\n    (uv1_1 * 12.0)\\n  )) - (9.0 * tmpvar_4));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (tmpvar_5 + ((texture (sampler_main, uv).xyz * 12.0) * vec3(clamp (\\n    (0.04 / sqrt(dot (uv1_1, uv1_1)))\\n  , 0.0, 1.0))));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,modwavealphabyvolume:1,wave_a:.207,wave_scale:.397,wave_smoothing:0,wave_mystery:.24,modwavealphaend:1.17,warpscale:16.016,zoomexp:11.56276,zoom:1.05971,warp:.13126,ob_size:.005,ob_a:1,ib_size:0,mv_x:64,mv_y:48,mv_l:0,mv_r:.35,mv_g:.35,mv_b:.35,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.cx1=0;a.y3=0;a.y1=0;a.cy1=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.v=0;a.vx4=0;a.grav=0;a.x3=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.spring=0;a.vx2=0;a.q3=0;a.resist=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.q8=0;\",frame_eqs_str:\"a.decay=1;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+1.5*(a.xx1-a.xx2);a.y1=.5+a.yy1;a.spring=18;a.grav=1;a.resist=5;a.bounce=.9;a.dt=.0003;a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-a.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-\\na.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*a.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*\\na.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q1=a.x1;a.q2=\\na.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.q6=Math.atan2(a.vx4,a.vy4);a.q5=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.zoom=1.001;a.rot=0;a.warp=.2;a.wave_a=0;\",pixel_eqs_str:\"a.r=.2;a.cx1=a.q4;a.cy1=a.q5;a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.dir=-(a.r*a.r-a.d*a.d)*a.q3;a.x1=.00001<Math.abs(above(a.d,a.r))?0:Math.sin(a.y-a.cy1)*a.dir;a.y1=.00001<Math.abs(above(a.d,a.r))?0:-Math.sin(a.x-a.cx1)*a.dir;a.v=1;a.dx=a.x1*a.v;a.dy=a.y1*a.v;\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.5 + ((uv - 0.5) * 1.002));\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 8.0);\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_4.x;\\n  tmpvar_6.y = tmpvar_5.x;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = fract(((0.5 + \\n    (uv_1 - 0.5)\\n  ) - (tmpvar_6 * texsize.zw)));\\n  ret_2.x = texture (sampler_main, tmpvar_7).x;\\n  ret_2.x = (ret_2.x + ((\\n    (ret_2.x - ((texture (sampler_blur2, tmpvar_7).xyz * scale2) + bias2))\\n  .x * 0.4) + 0.006));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_5.x;\\n  tmpvar_8.y = -(tmpvar_4.x);\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_4.z;\\n  tmpvar_9.y = tmpvar_5.z;\\n  ret_2.z = max ((clamp (\\n    (((texture (sampler_blur1, uv_1).xyz * scale1) + bias1).x - 0.3)\\n  , 0.0, 1.0) * 2.0), (texture (sampler_fc_main, (\\n    (uv_1 - ((tmpvar_8 * texsize.zw) * 8.0))\\n   + \\n    ((tmpvar_9 * texsize.zw) * 4.0)\\n  )).z - 0.008));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4.y;\\n  tmpvar_10.y = tmpvar_5.y;\\n  ret_2.y = max (ret_2.x, (texture (sampler_fc_main, (uv_1 + \\n    (tmpvar_10 * texsize.zw)\\n  )).y - 0.016));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = ret_2;\\n  ret = tmpvar_11.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 6.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = dot (tmpvar_3, vec3(0.32, 0.49, 0.29));\\n  tmpvar_5.y = dot (tmpvar_4, vec3(0.32, 0.49, 0.29));\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - (0.25 * tmpvar_5));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = dot (tmpvar_3, vec3(0.32, 0.49, 0.29));\\n  tmpvar_7.y = dot (tmpvar_4, vec3(0.32, 0.49, 0.29));\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (uv + (0.25 * tmpvar_7));\\n  ret_1 = ((0.8 * (\\n    (texture (sampler_blur3, tmpvar_6).xyz * scale3)\\n   + bias3)) - ((texture (sampler_blur1, tmpvar_6).xyz * scale1) + bias1));\\n  ret_1 = (ret_1 + (0.6 * (\\n    (texture (sampler_blur1, uv).xyz * scale1)\\n   + bias1)));\\n  ret_1 = (ret_1 - ((\\n    (texture (sampler_blur2, tmpvar_8).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur1, tmpvar_8).xyz * scale1)\\n   + bias1)));\\n  ret_1 = (ret_1 + ((1.2 * texture (sampler_main, tmpvar_8).xyz) + (0.15 * \\n    ((texture (sampler_blur1, tmpvar_8).xyz * scale1) + bias1)\\n  )));\\n  ret_1 = (ret_1 + 1.0);\\n  float tmpvar_9;\\n  tmpvar_9 = dot (ret_1, vec3(0.32, 0.49, 0.29));\\n  ret_1 = (mix (vec3(tmpvar_9), (\\n    (0.75 * vec3(tmpvar_9))\\n   * \\n    dot ((((0.6 * \\n      ((texture (sampler_blur3, tmpvar_6).xyz * scale3) + bias3)\\n    ) - (0.7 * texture (sampler_main, uv).xyz)) - (0.3 * (\\n      (texture (sampler_blur1, tmpvar_8).xyz * scale1)\\n     + bias1))), vec3(0.32, 0.49, 0.29))\\n  ), pow (hue_shader, vec3(tmpvar_9))) * 0.9);\\n  ret_1 = (ret_1 * ret_1);\\n  vec3 tmpvar_10;\\n  tmpvar_10 = sqrt(ret_1);\\n  ret_1 = tmpvar_10;\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = tmpvar_10;\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1,wave_thick:1,wrap:0,wave_a:.004,wave_scale:9.731,wave_smoothing:0,wave_mystery:1,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.442,warpscale:7.315,zoomexp:1.50374,warp:.08563,wave_y:.04,ob_size:0,ob_g:1,ob_a:1,ib_size:0,ib_r:1,ib_g:0,ib_b:.75,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:3,additive:1,num_inst:400,rad:.16283,tex_zoom:.73458,g:1,b:1,a:.5,g2:0,border_a:0},init_eqs_str:\"a.bending=0;a.ppo=0;a.index=0;a.i3=0;a.dir=0;a.i4=0;a.size=0;a.q2=0;a.q32=0;a.sample=0;a.i3=0;a.i4=0;\",frame_eqs_str:\"a.i3=.00001<Math.abs(equal(a.instance,0))?0:a.i3;a.i4=.00001<Math.abs(equal(a.instance,0))?0:a.i4;a.ppo=8;a.index=a.i4*a.q32;a.sample=mod(a.i3,a.ppo);a.size=40*a.gmegabuf[Math.floor(a.index+4)];a.x=a.gmegabuf[Math.floor(a.index)];a.y=a.gmegabuf[Math.floor(a.index+1)];a.ang=a.gmegabuf[Math.floor(a.index+9)];a.rad=.05*a.size;a.r=a.gmegabuf[Math.floor(a.index+5)];a.g=a.gmegabuf[Math.floor(a.index+6)];a.b=a.gmegabuf[Math.floor(a.index+7)];a.sample=div(a.sample,a.ppo);a.bending=\\n5*a.gmegabuf[Math.floor(a.index+12)]+5*a.gmegabuf[Math.floor(a.index+10)];a.dir=-a.ang+(a.sample-.4)*a.bending;a.ang=1.5*Math.asin(1)-a.dir+.05*a.bending;a.x+=.06*(a.sample-.3)*Math.cos(a.dir)*a.size+Math.sin(a.dir)*a.size*a.bending*.01;a.y+=.06*(a.sample-.3)*Math.sin(a.dir)*a.size-Math.cos(a.dir)*a.size*a.bending*.01;a.x=.5+div(a.x-.5,a.q2);a.rad=1.5*a.rad-.05*a.sample*a.size;a.i3+=1;a.i4=.00001<Math.abs(equal(mod(a.i3,a.ppo),0))?a.i4+1:a.i4;\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:1,samples:65,spectrum:1,usedots:1,thick:1,additive:1,scaling:.33408,smoothing:0,a:0},init_eqs_str:\"a.d=0;a.q32=0;a.t8=0;a.t1=0;a.t2=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.t8=1;a.t1=.5;a.t2=.9;\",point_eqs_str:\"a.d=0;a.y=.2+a.value1+a.value2;a.x=.9-.8*a.sample;a.gmegabuf[Math.floor((64*a.sample-1)*a.q32+14)]=a.value1+a.value2;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.fric=0;a.d=0;a.nliststart=0;a.w=0;a.random=0;a.index2=0;a.index=0;a.w2=0;a.rotatefactor=0;a.ref_ang=0;a.smaller=0;a.dt=0;a.q1=0;a.j=0;a.v=0;a.count=0;a.shock=0;a.nn=0;a.vrr=0;a.check=0;a.gravity=0;a.vr2=0;a.direction=0;a.bouncefactor=0;a.smallestfind=0;a.distance=0;a.bounce=0;a.vr=0;a.vv1=0;a.vv2=0;a.vrr2=0;a.w1=0;a.attributes=0;a.i=0;a.m2=0;a.hit=0;a.q2=0;a.m1=0;a.h=0;a.v1=0;a.acceleration=0;a.findindex=0;a.friction=0;a.pi2=0;a.sample=0;a.v2=0;a.dampening=0;for(var b=a.i=\\n0;1048576>b;b++)a.gmegabuf[Math.floor(a.i)]=0,a.i+=1;a.count=50;a.attributes=32;a.nliststart=24;a.minradius=.004;a.maxradius=.04;a.v=0;for(b=a.index=0;b<a.count;b++)a.gmegabuf[Math.floor(a.index*a.attributes)]=div(randint(1E3),1E3),a.gmegabuf[Math.floor(a.index*a.attributes+1)]=div(randint(1E3),1E3),a.gmegabuf[Math.floor(a.index*a.attributes+2)]=a.v*(div(randint(1E3),1E3)-.5),a.gmegabuf[Math.floor(a.index*a.attributes+3)]=a.v*(div(randint(1E3),1E3)-.5),a.gmegabuf[Math.floor(a.index*a.attributes+4)]=\\na.minradius+div((a.maxradius-a.minradius)*(a.index+1),a.count),a.gmegabuf[Math.floor(a.index*a.attributes+5)]=0,a.gmegabuf[Math.floor(a.index*a.attributes+6)]=1,a.gmegabuf[Math.floor(a.index*a.attributes+7)]=1,a.gmegabuf[Math.floor(a.index*a.attributes+8)]=pow(a.gmegabuf[Math.floor(a.index*a.attributes+4)],3),a.gmegabuf[Math.floor(a.index*a.attributes+9)]=div(4*Math.asin(1)*randint(1E3),1E3),a.gmegabuf[Math.floor(a.index*a.attributes+10)]=0,a.gmegabuf[Math.floor(a.index*a.attributes+13)]=div(a.index,\\na.count-1),a.gmegabuf[Math.floor(a.index*a.attributes+14)]=0,a.index+=1;a.q30=a.nliststart;a.q31=a.count;a.q32=a.attributes;\",frame_eqs_str:\"a.dt=div(1,a.fps);a.warp=0;a.zoom=1;a.wave_a=0;a.gravity=0*a.dt;a.dampening=0;a.friction=2048*a.dt;a.shock=.002;a.pi2=Math.asin(1);a.nn=3;a.check=2;a.bouncefactor=.1;a.rotatefactor=0;a.h=.5*(a.aspecty-1);a.w=.5*(a.aspectx-1);a.index=0;for(var c=a.index2=0;c<a.count;c++){a.random=div(randint(1E3),1E3);a.sample=a.gmegabuf[Math.floor(a.index+13)];a.gmegabuf[Math.floor(a.index+11)]=.92*a.gmegabuf[Math.floor(a.index+11)]-4*a.gmegabuf[Math.floor(a.index+12)]*a.dt+2*(a.random-.5)*\\na.dt*a.gmegabuf[Math.floor(a.index+14)];a.gmegabuf[Math.floor(a.index+12)]+=60*a.gmegabuf[Math.floor(a.index+11)]*a.dt;a.v=sqrt(sqr(a.gmegabuf[Math.floor(a.index+2)])+sqr(a.gmegabuf[Math.floor(a.index+3)]));a.fric=Math.max(0,1-sqr(a.v*a.friction)-2*a.v);a.gmegabuf[Math.floor(a.index+2)]*=a.fric;a.gmegabuf[Math.floor(a.index+3)]*=a.fric;a.gmegabuf[Math.floor(a.index+10)]=a.gmegabuf[Math.floor(a.index+10)]*a.fric+a.v*a.gmegabuf[Math.floor(a.index+12)]*0;a.acceleration=0*a.random+.003*Math.abs(a.gmegabuf[Math.floor(a.index+\\n11)]);a.direction=a.gmegabuf[Math.floor(a.index+9)]-a.pi2;a.gmegabuf[Math.floor(a.index+2)]+=Math.sin(a.direction)*a.acceleration;a.gmegabuf[Math.floor(a.index+3)]+=Math.cos(a.direction)*a.acceleration;a.gmegabuf[Math.floor(a.index)]+=60*a.gmegabuf[Math.floor(a.index+2)]*a.dt;a.gmegabuf[Math.floor(a.index+1)]+=60*a.gmegabuf[Math.floor(a.index+3)]*a.dt;a.gmegabuf[Math.floor(a.index+9)]+=.5*a.gmegabuf[Math.floor(a.index+10)];a.gmegabuf[Math.floor(a.index+3)]-=a.gravity;a.vr=Math.sin(a.gmegabuf[Math.floor(a.index+\\n10)])*a.gmegabuf[Math.floor(a.index+4)];a.bounce=above(a.gmegabuf[Math.floor(a.index+1)],1-a.gmegabuf[Math.floor(a.index+4)]+a.w);a.gmegabuf[Math.floor(a.index+2)]=.00001<Math.abs(a.bounce)?a.gmegabuf[Math.floor(a.index+2)]+(a.vr+a.gmegabuf[Math.floor(a.index+2)])*a.rotatefactor:a.gmegabuf[Math.floor(a.index+2)];a.gmegabuf[Math.floor(a.index+3)]=.00001<Math.abs(a.bounce)?-Math.abs(a.gmegabuf[Math.floor(a.index+3)])*a.dampening-a.shock:a.gmegabuf[Math.floor(a.index+3)];a.vr=.00001<Math.abs(a.bounce)?\\na.vr-(a.gmegabuf[Math.floor(a.index+2)]+a.vr)*(1-a.rotatefactor):a.vr;a.bounce=below(a.gmegabuf[Math.floor(a.index+1)],a.gmegabuf[Math.floor(a.index+4)]-a.w);a.gmegabuf[Math.floor(a.index+2)]=.00001<Math.abs(a.bounce)?a.gmegabuf[Math.floor(a.index+2)]+(a.vr-a.gmegabuf[Math.floor(a.index+2)])*a.rotatefactor:a.gmegabuf[Math.floor(a.index+2)];a.gmegabuf[Math.floor(a.index+3)]=.00001<Math.abs(a.bounce)?Math.abs(a.gmegabuf[Math.floor(a.index+3)])*a.dampening+a.shock:a.gmegabuf[Math.floor(a.index+3)];a.vr=\\n.00001<Math.abs(a.bounce)?a.vr+(a.gmegabuf[Math.floor(a.index+2)]-a.vr)*(1-a.rotatefactor):a.vr;a.bounce=above(a.gmegabuf[Math.floor(a.index)],1-a.gmegabuf[Math.floor(a.index+4)]+a.h);a.gmegabuf[Math.floor(a.index+2)]=.00001<Math.abs(a.bounce)?-Math.abs(a.gmegabuf[Math.floor(a.index+2)])*a.dampening-a.shock:a.gmegabuf[Math.floor(a.index+2)];a.gmegabuf[Math.floor(a.index+3)]=.00001<Math.abs(a.bounce)?a.gmegabuf[Math.floor(a.index+3)]+(a.vr-a.gmegabuf[Math.floor(a.index+3)])*a.rotatefactor:a.gmegabuf[Math.floor(a.index+\\n3)];a.vr=.00001<Math.abs(a.bounce)?a.vr+(a.gmegabuf[Math.floor(a.index+3)]-a.vr)*(1-a.rotatefactor):a.vr;a.bounce=below(a.gmegabuf[Math.floor(a.index)],a.gmegabuf[Math.floor(a.index+4)]-a.h);a.gmegabuf[Math.floor(a.index+2)]=.00001<Math.abs(a.bounce)?Math.abs(a.gmegabuf[Math.floor(a.index+2)])*a.dampening+a.shock:a.gmegabuf[Math.floor(a.index+2)];a.gmegabuf[Math.floor(a.index+3)]=.00001<Math.abs(a.bounce)?a.gmegabuf[Math.floor(a.index+3)]-(a.vr+a.gmegabuf[Math.floor(a.index+3)])*a.rotatefactor:a.gmegabuf[Math.floor(a.index+\\n3)];a.vr=.00001<Math.abs(a.bounce)?a.vr-(a.gmegabuf[Math.floor(a.index+3)]+a.vr)*(1-a.rotatefactor):a.vr;a.gmegabuf[Math.floor(a.index+10)]=Math.asin(div(a.vr,a.gmegabuf[Math.floor(a.index+4)]));a.i=0;for(var b=a.j=0;b<a.count;b++)a.d=sqrt(sqr(a.gmegabuf[Math.floor(a.index)]-a.gmegabuf[Math.floor(a.i)])+sqr(a.gmegabuf[Math.floor(a.index+1)]-a.gmegabuf[Math.floor(a.i+1)])),a.d=.00001<Math.abs(equal(a.d,0))?10:a.d,a.d-=.5*(a.gmegabuf[Math.floor(a.index+4)]+a.gmegabuf[Math.floor(a.i+4)]),a.megabuf[Math.floor(a.j)]=\\na.i,a.megabuf[Math.floor(a.j+1)]=a.d,a.j+=2,a.i+=a.attributes;for(b=a.i=0;b<a.nn;b++){a.j=a.i;a.smallestfind=10;a.findindex=-1;for(var d=0;d<a.count-a.j;d++)a.distance=a.megabuf[Math.floor(2*a.j+1)],a.smaller=above(a.smallestfind,a.distance),a.smallestfind=.00001<Math.abs(a.smaller)?a.distance:a.smallestfind,a.findindex=.00001<Math.abs(a.smaller)?2*a.j:a.findindex,a.j+=1;a.j=a.megabuf[Math.floor(2*a.i)];a.d=a.megabuf[Math.floor(2*a.i+1)];a.megabuf[Math.floor(2*a.i)]=a.megabuf[Math.floor(a.findindex)];\\na.megabuf[Math.floor(2*a.i+1)]=a.megabuf[Math.floor(a.findindex+1)];a.megabuf[Math.floor(a.findindex)]=a.j;a.megabuf[Math.floor(a.findindex+1)]=a.d;a.i+=1}for(b=a.i=0;b<a.nn;b++)a.gmegabuf[Math.floor(a.index+a.nliststart+a.i)]=a.megabuf[Math.floor(2*a.i)],a.i+=1;for(b=a.i=0;b<a.check;b++)a.index2=a.megabuf[Math.floor(a.i)],a.hit=below(sqrt(sqr(a.gmegabuf[Math.floor(a.index)]-a.gmegabuf[Math.floor(a.index2)])+sqr(a.gmegabuf[Math.floor(a.index+1)]-a.gmegabuf[Math.floor(a.index2+1)])),a.gmegabuf[Math.floor(a.index+\\n4)]+a.gmegabuf[Math.floor(a.index2+4)])*above(sqrt(sqr(a.gmegabuf[Math.floor(a.index)]-a.gmegabuf[Math.floor(a.index2)])+sqr(a.gmegabuf[Math.floor(a.index+1)]-a.gmegabuf[Math.floor(a.index2+1)])),sqrt(sqr(a.gmegabuf[Math.floor(a.index+0)]-a.gmegabuf[Math.floor(a.index2+0)]+a.gmegabuf[Math.floor(a.index+2)]-a.gmegabuf[Math.floor(a.index2+2)])+sqr(a.gmegabuf[Math.floor(a.index+1)]-a.gmegabuf[Math.floor(a.index2+1)]+a.gmegabuf[Math.floor(a.index+3)]-a.gmegabuf[Math.floor(a.index2+3)]))),a.ref_ang=Math.atan2(a.gmegabuf[Math.floor(a.index2)]-\\na.gmegabuf[Math.floor(a.index)],a.gmegabuf[Math.floor(a.index2+1)]-a.gmegabuf[Math.floor(a.index+1)])+a.pi2,a.v1=sqrt(sqr(a.gmegabuf[Math.floor(a.index+2)])+sqr(a.gmegabuf[Math.floor(a.index+3)])),a.v2=sqrt(sqr(a.gmegabuf[Math.floor(a.index2+2)])+sqr(a.gmegabuf[Math.floor(a.index2+3)])),a.w1=Math.atan2(a.gmegabuf[Math.floor(a.index+2)],a.gmegabuf[Math.floor(a.index+3)]),a.w2=Math.atan2(a.gmegabuf[Math.floor(a.index2+2)],a.gmegabuf[Math.floor(a.index2+3)]),a.vr2=Math.sin(a.gmegabuf[Math.floor(a.index2+\\n10)])*a.gmegabuf[Math.floor(a.index2+4)],a.m1=a.gmegabuf[Math.floor(a.index+8)],a.m2=a.gmegabuf[Math.floor(a.index2+8)],a.vv1=div((a.m1-a.m2)*a.v1+2*a.m2*a.v2,a.m1+a.m2),a.vv2=div((a.m2-a.m1)*a.v2+2*a.m1*a.v1,a.m1+a.m2),a.vrr=div((a.m1-a.m2)*a.vr+2*a.m2*a.vr2,a.m1+a.m2),a.vrr2=div((a.m2-a.m1)*a.vr2+2*a.m1*a.vr,a.m1+a.m2),a.gmegabuf[Math.floor(a.index+2)]=.00001<Math.abs(a.hit)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.vr2-Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))+Math.cos(a.ref_ang)*\\na.vv1*Math.sin(a.w2-a.ref_ang):a.gmegabuf[Math.floor(a.index+2)],a.gmegabuf[Math.floor(a.index+3)]=.00001<Math.abs(a.hit)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+.1*(a.vr-a.vr2-Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang))-Math.sin(a.ref_ang)*a.vv1*Math.sin(a.w2-a.ref_ang):a.gmegabuf[Math.floor(a.index+3)],a.gmegabuf[Math.floor(a.index2+2)]=.00001<Math.abs(a.hit)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.vr2-a.vr-Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))+Math.cos(a.ref_ang)*\\na.vv2*Math.sin(a.w1-a.ref_ang):a.gmegabuf[Math.floor(a.index2+2)],a.gmegabuf[Math.floor(a.index2+3)]=.00001<Math.abs(a.hit)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+.1*(a.vr2-a.vr-Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang))-Math.sin(a.ref_ang)*a.vv2*Math.sin(a.w1-a.ref_ang):a.gmegabuf[Math.floor(a.index2+3)],a.vr=.00001<Math.abs(a.hit)?a.vr+(Math.cos(a.w1-a.ref_ang)*(a.v1-a.v2)-a.vr):a.vr,a.gmegabuf[Math.floor(a.index+10)]=Math.asin(div(a.vr,a.gmegabuf[Math.floor(a.index+4)])),a.vr2=\\n.00001<Math.abs(a.hit)?a.vr2+(Math.cos(a.w2-a.ref_ang)*(a.v2-a.v1)-a.vr2):a.vr2,a.gmegabuf[Math.floor(a.index2+10)]=Math.asin(div(a.vr2,a.gmegabuf[Math.floor(a.index2+4)])),a.i+=2;a.index+=a.attributes}a.q1=a.aspectx;a.q2=a.aspecty;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1.z = (texture (sampler_main, uv).z * 0.5);\\n  vec2 tmpvar_2;\\n  tmpvar_2 = ((uv_orig * texsize.xy) * texsize_noise_lq.zw);\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 4.0);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  tmpvar_4.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  ret_1.y = texture (sampler_fw_main, clamp ((uv_orig + (\\n    (tmpvar_4 * texsize.zw)\\n   * 4.0)), 0.0, 1.0)).y;\\n  ret_1.y = (ret_1.y + ((\\n    (ret_1 - ((texture (sampler_blur1, uv_orig).xyz * scale1) + bias1))\\n  .y * 0.025) + -0.014));\\n  ret_1.y = (ret_1.y + ((texture (sampler_noise_lq, tmpvar_2).y - 0.5) * 0.02));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(1.0, 0.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  tmpvar_5.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv_orig + (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_orig - (vec2(0.0, 1.0) * tmpvar_3))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  ret_1.x = ((texture (sampler_main, (uv - \\n    ((tmpvar_5 * texsize.zw) * 4.0)\\n  )).x - (ret_1.y * 0.01)) + 0.004);\\n  ret_1.x = (ret_1.x + ((\\n    (texture (sampler_noise_lq, tmpvar_2).x - 0.5)\\n   * 0.01) + (ret_1.z * 0.14)));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_1;\\n  ret = tmpvar_6.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 4.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1) + bias1)\\n  )) * 0.5).y;\\n  tmpvar_3.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1) + bias1)\\n  )) * 0.5).y;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv - ((tmpvar_3 * texsize.zw) * 128.0));\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_main, uv);\\n  ret_1 = (((\\n    ((texture (sampler_blur2, tmpvar_4).xyz * scale2) + bias2)\\n  .x * \\n    clamp ((1.0 - tmpvar_5.y), 0.0, 1.0)\\n  ) * pow (hue_shader.yxz, vec3(8.0, 8.0, 8.0))) * 3.0);\\n  ret_1 = (mix (mix (ret_1, \\n    (pow (hue_shader.yzx, vec3(8.0, 8.0, 8.0)) * 1.4)\\n  , vec3(\\n    ((texture (sampler_main, tmpvar_4).x * 0.8) + ((texture (sampler_blur1, tmpvar_4).xyz * scale1) + bias1).x)\\n  )), vec3(1.0, 1.0, 1.0), (\\n    (pow (hue_shader, vec3(8.0, 8.0, 8.0)) * texture (sampler_main, clamp (uv, 0.0, 1.0)).y)\\n   * 1.2)) * clamp ((1.0 - tmpvar_5.z), 0.0, 1.0));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_1;\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.995,echo_zoom:1.007,echo_orient:3,wave_thick:1,modwavealphabyvolume:1,wave_a:.9,wave_scale:2.997,wave_smoothing:0,wave_mystery:-.5,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.274,warpscale:7.98,zoom:.99951,warp:.20929,wave_r:.02,wave_g:.02,wave_b:0,ob_size:.5,ob_r:1,ob_g:.12,ob_b:1,ob_a:.37,ib_size:0,ib_a:1,mv_x:0,mv_y:0,mv_l:5,mv_a:0,b1ed:.2},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.w=0;a.h3=0;a.q12=0;a.q6=0;a.bt=0;a.q1=0;a.q5=0;a.startx=0;a.v=0;a.mm=0;a.h2=0;a.tt=0;a.bm=0;a.q11=0;a.q10=0;a.q4=0;a.a=0;a.mt=0;a.b=0;a.mx=0;a.mn=0;a.bb=0;a.q2=0;a.starty=0;a.h1=0;a.q3=0;a.q7=0;a.x1=0;a.y1=.001;a.z1=0;\",frame_eqs_str:\"a.zoom=1;a.wave_a=0;a.startx=.3;a.starty=.3;a.a=.55;a.b=.05;a.d=.09;a.q1=a.aspectx;a.q2=a.aspecty;a.q5=a.a;a.q6=a.b;a.q7=a.d;a.bb=.99*a.bb+.02*a.bass;a.mm=.99*a.mm+.02*a.mid;a.tt=.99*a.tt+.02*a.treb;a.mx=Math.max(Math.max(a.bb,a.mm),a.tt);a.mn=Math.min(Math.min(a.bb,a.mm),a.tt);a.h1=div(a.bb-a.mn,a.mx-a.mn);a.h2=div(a.mm-a.mn,a.mx-a.mn);a.h3=div(a.tt-a.mn,a.mx-a.mn);a.v=div(.1333,a.fps);a.bm+=(a.h1-a.h2)*a.v;a.mt+=(a.h2-a.h3)*a.v;a.bt+=(a.h1-a.h3)*a.v;a.w=2*a.bm;a.q3=Math.sin(a.w);\\na.q4=Math.cos(a.w);a.q10=a.bm;a.q11=a.mt;a.q12=a.bt;\",pixel_eqs_str:\"a.x=.5+(a.x-.51)*a.q1;a.y=.5-(a.y-.5)*a.q2;a.dx=a.x*(1-a.x)-div(a.q5*a.x*a.y,a.x+a.q6);a.dy=-a.q7*a.y*(1-div(a.y,a.x));a.dx=div(.02*-a.dx,a.q1);a.dy=div(.02*-a.dy,a.q2);\",warp:\" shader_body { \\n  vec2 my_uv2_1;\\n  vec2 dz_2;\\n  vec3 ret_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (texsize.zw * 4.0);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = ((2.0 * (\\n    (texture (sampler_blur1, (uv + (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (uv - (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)));\\n  vec3 tmpvar_6;\\n  tmpvar_6 = ((2.0 * (\\n    (texture (sampler_blur1, (uv + (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (uv - (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_5.x;\\n  tmpvar_7.y = tmpvar_6.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (tmpvar_7 * texsize.zw);\\n  ret_3.x = (texture (sampler_fw_main, (uv - tmpvar_8)).x - ((\\n    (texture (sampler_blur3, uv).xyz * scale3)\\n   + bias3).x - (\\n    (texture (sampler_blur1, (uv + tmpvar_8)).xyz * scale1)\\n   + bias1).x));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_5.y;\\n  tmpvar_9.y = tmpvar_6.y;\\n  dz_2 = (-(tmpvar_9) * texsize.zw);\\n  ret_3.y = ((texture (sampler_fw_main, (uv - dz_2)).y - 0.06) - ((\\n    ((texture (sampler_blur2, uv).xyz * scale2) + bias2)\\n  .y - \\n    ((texture (sampler_blur1, (uv + dz_2)).xyz * scale1) + bias1)\\n  .y) * 1.3));\\n  vec2 tmpvar_10;\\n  tmpvar_10 = ((uv_orig - 0.5) * (1.8 - (\\n    (bass_att - treb_att)\\n   * 0.015)));\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = ((tmpvar_10.x * tmpvar_10.x) - (tmpvar_10.y * tmpvar_10.y));\\n  tmpvar_11.y = ((2.0 * tmpvar_10.x) * tmpvar_10.y);\\n  my_uv2_1 = (tmpvar_11 + vec2(0.28, 0.4));\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = ((2.0 * (\\n    (texture (sampler_blur1, (my_uv2_1 + (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (my_uv2_1 - (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale1)\\n   + bias1))).z;\\n  tmpvar_12.y = ((2.0 * (\\n    (texture (sampler_blur1, (my_uv2_1 + (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (my_uv2_1 - (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale1)\\n   + bias1))).z;\\n  dz_2 = (tmpvar_12 * texsize.zw);\\n  ret_3.z = (texture (sampler_main, clamp ((my_uv2_1 + dz_2), 0.0, 1.0)).z - 0.014);\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 1.0;\\n  tmpvar_13.xyz = ret_3;\\n  ret = tmpvar_13.xyz;\\n }\",comp:\" shader_body { \\n  vec3 dy_1;\\n  vec3 dx_2;\\n  vec2 mirror_uv_3;\\n  vec3 ret_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = ((uv - 0.5) * aspect.xy);\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = ((q4 * tmpvar_5.x) - (q3 * tmpvar_5.y));\\n  tmpvar_6.y = ((q3 * tmpvar_5.x) + (q4 * tmpvar_5.y));\\n  vec2 tmpvar_7;\\n  tmpvar_7 = (0.5 - (tmpvar_6 * 2.0));\\n  vec2 numerator_8;\\n  numerator_8 = (tmpvar_7 - vec2(0.0, 0.5));\\n  vec2 denominator_9;\\n  denominator_9 = (tmpvar_7 - vec2(1.0, 0.5));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = ((numerator_8.x * denominator_9.x) + (numerator_8.y * denominator_9.y));\\n  tmpvar_10.y = ((numerator_8.y * denominator_9.x) - (numerator_8.x * denominator_9.y));\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (((tmpvar_10 / \\n    ((denominator_9.x * denominator_9.x) + (denominator_9.y * denominator_9.y))\\n  ) + 0.5) - vec2(0.5, 0.5));\\n  float tmpvar_12;\\n  tmpvar_12 = sqrt(dot (tmpvar_11, tmpvar_11));\\n  float tmpvar_13;\\n  float tmpvar_14;\\n  tmpvar_14 = (min (abs(\\n    (tmpvar_11.x / tmpvar_11.y)\\n  ), 1.0) / max (abs(\\n    (tmpvar_11.x / tmpvar_11.y)\\n  ), 1.0));\\n  float tmpvar_15;\\n  tmpvar_15 = (tmpvar_14 * tmpvar_14);\\n  tmpvar_15 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_15) + 0.05368138) * tmpvar_15) - 0.1173503)\\n     * tmpvar_15) + 0.1938925) * tmpvar_15) - 0.3326756)\\n   * tmpvar_15) + 0.9999793) * tmpvar_14);\\n  tmpvar_15 = (tmpvar_15 + (float(\\n    (abs((tmpvar_11.x / tmpvar_11.y)) > 1.0)\\n  ) * (\\n    (tmpvar_15 * -2.0)\\n   + 1.570796)));\\n  tmpvar_13 = (tmpvar_15 * sign((tmpvar_11.x / tmpvar_11.y)));\\n  if ((abs(tmpvar_11.y) > (1e-08 * abs(tmpvar_11.x)))) {\\n    if ((tmpvar_11.y < 0.0)) {\\n      if ((tmpvar_11.x >= 0.0)) {\\n        tmpvar_13 += 3.141593;\\n      } else {\\n        tmpvar_13 = (tmpvar_13 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_13 = (sign(tmpvar_11.x) * 1.570796);\\n  };\\n  vec2 tmpvar_16;\\n  tmpvar_16.x = (tmpvar_13 * 0.1591549);\\n  tmpvar_16.y = tmpvar_12;\\n  vec2 tmpvar_17;\\n  tmpvar_17.x = ((tmpvar_16.x * 2.0) + q11);\\n  tmpvar_17.y = ((0.3 * log(tmpvar_12)) + q12);\\n  vec2 tmpvar_18;\\n  tmpvar_18 = (0.5 + (0.5 - abs(\\n    ((fract((tmpvar_17 * 0.5)) * 2.0) - 1.0)\\n  )));\\n  vec2 tmpvar_19;\\n  tmpvar_19 = (texsize.zw * 3.0);\\n  vec3 tmpvar_20;\\n  tmpvar_20 = ((2.0 * (\\n    (texture (sampler_blur1, (tmpvar_18 + (vec2(1.0, 0.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (tmpvar_18 - (vec2(1.0, 0.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)));\\n  vec3 tmpvar_21;\\n  tmpvar_21 = ((2.0 * (\\n    (texture (sampler_blur1, (tmpvar_18 + (vec2(0.0, 1.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (tmpvar_18 - (vec2(0.0, 1.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)));\\n  vec2 tmpvar_22;\\n  tmpvar_22.x = tmpvar_20.x;\\n  tmpvar_22.y = tmpvar_21.x;\\n  mirror_uv_3 = (tmpvar_18 + ((tmpvar_22 * texsize.zw) * 4.0));\\n  ret_4 = ((mix (ret_4, vec3(1.0, 1.0, 1.0), \\n    ((((texture (sampler_blur1, mirror_uv_3).xyz * scale1) + bias1).x * (1.0 - (\\n      (texture (sampler_blur2, mirror_uv_3).xyz * scale2)\\n     + bias2).x)) * (pow (hue_shader, vec3(4.0, 4.0, 4.0)) * 1.4))\\n  ) * texture (sampler_main, mirror_uv_3).xxx) + ((\\n    (1.0 - texture (sampler_main, mirror_uv_3).x)\\n   * \\n    ((texture (sampler_blur1, mirror_uv_3).xyz * scale1) + bias1)\\n  .x) * vec3(3.0, 3.0, 3.0)));\\n  vec2 tmpvar_23;\\n  tmpvar_23.x = tmpvar_20.x;\\n  tmpvar_23.y = tmpvar_21.x;\\n  mirror_uv_3 = (mirror_uv_3 - ((tmpvar_23 * texsize.zw) * 24.0));\\n  dx_2 = ((2.0 * (\\n    (texture (sampler_blur1, (mirror_uv_3 + (vec2(1.0, 0.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (mirror_uv_3 - (vec2(1.0, 0.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)));\\n  dy_1 = ((2.0 * (\\n    (texture (sampler_blur1, (mirror_uv_3 + (vec2(0.0, 1.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)) - (2.0 * (\\n    (texture (sampler_blur1, (mirror_uv_3 - (vec2(0.0, 1.0) * tmpvar_19))).xyz * scale1)\\n   + bias1)));\\n  vec2 tmpvar_24;\\n  tmpvar_24.x = -(dx_2.y);\\n  tmpvar_24.y = dy_1.y;\\n  vec2 tmpvar_25;\\n  tmpvar_25 = (tmpvar_24 * 0.25);\\n  vec3 tmpvar_26;\\n  tmpvar_26 = mix (ret_4, vec3(1.0, 1.0, 1.0), (mix (vec3(1.0, 0.7, 0.2), vec3(0.15, 0.0, 0.5), vec3(\\n    ((((\\n      ((mirror_uv_3.x * 0.8) - mirror_uv_3.y)\\n     + 0.75) + tmpvar_25.x) + tmpvar_25.y) - 0.1)\\n  )) * texture (sampler_main, mirror_uv_3).y));\\n  vec2 tmpvar_27;\\n  vec2 tmpvar_28;\\n  tmpvar_28 = ((0.5 + (\\n    (uv - 0.5)\\n   * aspect.wz)) - vec2(0.5, 0.5));\\n  vec2 tmpvar_29;\\n  tmpvar_29.x = ((q4 * tmpvar_28.x) - (q3 * tmpvar_28.y));\\n  tmpvar_29.y = ((q3 * tmpvar_28.x) + (q4 * tmpvar_28.y));\\n  tmpvar_27 = (vec2(0.5, 0.5) + tmpvar_29);\\n  mirror_uv_3 = tmpvar_27.yx;\\n  vec2 tmpvar_30;\\n  tmpvar_30 = (vec2(1.0, 0.0) * texsize.zw);\\n  dx_2 = ((2.0 * texture (sampler_main, (tmpvar_27.yx + tmpvar_30)).xyz) - (2.0 * texture (sampler_main, (tmpvar_27.yx - tmpvar_30)).xyz));\\n  vec2 tmpvar_31;\\n  tmpvar_31 = (vec2(0.0, 1.0) * texsize.zw);\\n  dy_1 = ((2.0 * texture (sampler_main, (tmpvar_27.yx + tmpvar_31)).xyz) - (2.0 * texture (sampler_main, (tmpvar_27.yx - tmpvar_31)).xyz));\\n  vec2 tmpvar_32;\\n  tmpvar_32.x = dx_2.z;\\n  tmpvar_32.y = dy_1.z;\\n  vec3 tmpvar_33;\\n  tmpvar_33 = mix (tmpvar_26, vec3(0.9, 0.9, 1.0), vec3((1.0 - texture (sampler_main, (tmpvar_27.yx - tmpvar_32)).z)));\\n  ret_4 = tmpvar_33;\\n  vec4 tmpvar_34;\\n  tmpvar_34.w = 1.0;\\n  tmpvar_34.xyz = tmpvar_33;\\n  ret = tmpvar_34.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.995,echo_zoom:1.007,echo_orient:3,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,wave_a:1.413,wave_scale:.418,wave_smoothing:0,wave_mystery:-.66,modwavealphastart:2,modwavealphaend:2,warpanimspeed:.626,warpscale:8.642,zoomexp:7.10084,zoom:.99951,warp:.09014,wave_r:0,wave_g:0,wave_x:.24,wave_y:.44,ob_size:0,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:48,additive:1,rad:.0277,ang:6.03186,tex_ang:6.03186,tex_zoom:.6839,r:0,g:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.q3=0;a.q4=0;a.q5=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.x=a.q3;a.y=a.q4;a.rad=a.q5;a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:1,sides:48,additive:1,rad:.0277,ang:6.03186,tex_ang:6.03186,tex_zoom:.6839,r:0,b:1,g2:0,b2:1,a2:1,border_r:0,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.q6=0;a.q7=0;a.q8=0;a.q2=0;a.q1=0;\",frame_eqs_str:\"a.x=a.q6;a.y=a.q7;a.rad=a.q8;a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,additive:1,scaling:2.0231,smoothing:0,g:0,b:0},init_eqs_str:\"a.d=0;a.tt2=0;a.res=0;a.tt1=0;a.diff=0;a.tt3=0;a.beat=0;a.vol=0;a.m=0;a.monitor=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.vol=8*a.bass+5*a.mid+3*a.treb;a.m=.97*a.m+.08*a.vol;a.monitor=a.vol;a.beat=above(a.vol,a.res)*above(a.vol,a.m)*above(a.vol,16);a.diff=(1-a.beat)*a.diff+a.beat*(a.vol-a.res);a.res=a.beat*(a.vol+.04*a.m)+(1-a.beat)*(a.res-div(60*(.1+.02*a.diff),a.fps));a.res=Math.max(0,a.res);a.a=a.beat;\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.5+a.d*a.sample*(1-a.sample)*2;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.xx=0;a.q12=0;a.w2=0;a.ref_ang=0;a.q18=0;a.x1=0;a.vx3=0;a.q13=0;a.q15=0;a.q6=0;a.q1=0;a.q5=0;a.q9=0;a.d1=0;a.si1=0;a.vx1=0;a.vx4=0;a.x3=0;a.d2=0;a.q11=0;a.q10=0;a.q4=0;a.vy4=0;a.dir=0;a.bounce=0;a.q16=0;a.x4=0;a.w1=0;a.r=0;a.x2=0;a.q17=0;a.vy2=0;a.y2=0;a.vy1=0;a.q2=0;a.m1=0;a.q14=0;a.si2=0;a.v1=0;a.vx2=0;a.q3=0;a.yy=0;a.y4=0;a.q7=0;a.vy3=0;a.v2=0;a.b1=0;a.q8=0;a.x1=.5;a.x2=.51;a.y2=.9;a.y1=.7;a.x3=.8;a.y3=.5;a.x4=.2;a.y4=.5;a.ax1=0;a.ay1=0;a.ax2=0;a.ay2=0;a.ax3=\\n0;a.ay3=0;a.vx1=0;a.vx2=0;\",frame_eqs_str:\"a.zoom=1.002;a.warp=.2;a.wave_a=0;a.r=.04+.008*Math.max(a.bass_att,a.treb_att);a.bounce=below(a.y1,a.r);a.y1+=a.vy1;a.vy1=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy1)+.1*(a.r-a.y1):a.vy1-div(.018,a.fps);a.bounce=below(a.x1,a.r);a.x1+=a.vx1;a.vx1=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx1)+.1*(a.r-a.x1):a.vx1;a.bounce=above(a.x1,1-a.r);a.vx1=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx1)+.04*(1-a.r-a.x1):a.vx1;a.bounce=below(a.y2,a.r);a.y2+=a.vy2;a.vy2=.00001<Math.abs(a.bounce)?\\n.94*Math.abs(a.vy2)+.1*(a.r-a.y2):a.vy2-div(.018,a.fps);a.bounce=below(a.x2,a.r);a.x2+=a.vx2;a.vx2=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx2)+.1*(a.r-a.x2):a.vx2;a.bounce=above(a.x2,1-a.r);a.vx2=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx2)+.1*(1-a.r-a.x2):a.vx2;a.bounce=below(a.y3,a.r);a.y3+=a.vy3;a.vy3=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy3)+.1*(a.r-a.y3):a.vy3-div(.018,a.fps);a.bounce=below(a.x3,a.r);a.x3+=a.vx3;a.vx3=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx3)+.1*(a.r-a.x3):a.vx3;\\na.bounce=above(a.x3,1-a.r);a.vx3=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx3)+.1*(1-a.r-a.x3):a.vx3;a.bounce=below(a.y4,a.r);a.y4+=a.vy4;a.vy4=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vy4)+.1*(a.r-a.y4):a.vy4-div(.018,a.fps);a.bounce=below(a.x4,a.r);a.x4+=a.vx4;a.vx4=.00001<Math.abs(a.bounce)?.94*Math.abs(a.vx4)+.1*(a.r-a.x4):a.vx4;a.bounce=above(a.x4,1-a.r);a.vx4=.00001<Math.abs(a.bounce)?.94*-Math.abs(a.vx4)+.1*(1-a.r-a.x4):a.vx4;a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x2-a.vx2)+sqr(a.y1+a.vy1-a.y2-\\na.vy2)),2*a.r);a.ref_ang=Math.atan2(a.x2-a.x1,a.y2-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx2,a.vy2);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-\\nMath.asin(1)):a.vy1;a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy2;a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x3-a.vx3)+sqr(a.y1+a.vy1-a.y3-a.vy3)),2*a.r);a.ref_ang=Math.atan2(a.x3-a.x1,a.y3-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*\\na.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx3,a.vy3);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-\\na.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy3;a.bounce=below(sqrt(sqr(a.x2+a.vx2-a.x3-a.vx3)+sqr(a.y2+a.vy2-a.y3-a.vy3)),2*a.r);a.ref_ang=Math.atan2(a.x3-a.x2,a.y3-a.y2)+Math.asin(1);a.v1=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.v2=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.w1=Math.atan2(a.vx2,a.vy2);a.w2=Math.atan2(a.vx3,\\na.vy3);a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy2;a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=\\n.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy3;a.bounce=below(sqrt(sqr(a.x1+a.vx1-a.x4-a.vx4)+sqr(a.y1+a.vy1-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x1,a.y4-a.y1)+Math.asin(1);a.v1=sqrt(a.vx1*a.vx1+a.vy1*a.vy1);a.v2=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx1,a.vy1);a.w2=Math.atan2(a.vx4,a.vy4);a.vx1=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+\\nMath.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx1;a.vy1=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy1;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+\\nMath.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy4;a.bounce=below(sqrt(sqr(a.x2+a.vx2-a.x4-a.vx4)+sqr(a.y2+a.vy2-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x2,a.y4-a.y2)+Math.asin(1);a.v1=sqrt(a.vx2*a.vx2+a.vy2*a.vy2);a.v2=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx2,a.vy2);a.w2=Math.atan2(a.vx4,a.vy4);a.vx2=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx2;a.vy2=.00001<\\nMath.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy2;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy4;a.bounce=below(sqrt(sqr(a.x3+\\na.vx3-a.x4-a.vx4)+sqr(a.y3+a.vy3-a.y4-a.vy4)),2*a.r);a.ref_ang=Math.atan2(a.x4-a.x3,a.y4-a.y3)+Math.asin(1);a.v1=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);a.v2=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.w1=Math.atan2(a.vx3,a.vy3);a.w2=Math.atan2(a.vx4,a.vy4);a.vx3=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vx3;a.vy3=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v1*Math.cos(a.w1-a.ref_ang)+Math.cos(a.ref_ang+\\nMath.asin(1))*a.v2*Math.cos(a.w2-a.ref_ang-Math.asin(1)):a.vy3;a.vx4=.00001<Math.abs(a.bounce)?Math.sin(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.sin(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vx4;a.vy4=.00001<Math.abs(a.bounce)?Math.cos(a.ref_ang)*a.v2*Math.cos(a.w2-a.ref_ang)+Math.cos(a.ref_ang+Math.asin(1))*a.v1*Math.cos(a.w1-a.ref_ang-Math.asin(1)):a.vy4;a.q1=a.aspectx;a.q2=a.aspecty;a.q3=a.x1;a.q4=a.y1;a.q5=2*a.r;a.q6=a.x2;a.q7=a.y2;a.q8=2*a.r;a.q9=a.x3;a.q10=a.y3;\\na.q11=2*a.r;a.q12=a.x4;a.q13=a.y4;a.q14=2*a.r;a.q15=Math.atan2(a.vx4,a.vy4);a.q16=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.q17=Math.atan2(a.vx3,a.vy3);a.q18=sqrt(a.vx3*a.vx3+a.vy3*a.vy3);\",pixel_eqs_str:\"a.x=.5+(a.x-.5)*a.q2;a.y=.5+(a.y-.5)*a.q1;a.dir=-a.q15+Math.asin(1);a.b1=.075;a.m1=25*a.q16;a.xx=a.q12;a.yy=1-a.q13;a.x1=a.xx-Math.sin(a.dir)*a.b1;a.y1=a.yy-Math.cos(a.dir)*a.b1;a.x2=a.xx+Math.sin(a.dir)*a.b1;a.y2=a.yy+Math.cos(a.dir)*a.b1;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.b1;a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.b1;a.si1=sigmoid(-a.d1,1E3);a.si2=sigmoid(-a.d2,1E3);a.dx=3*(a.si1*Math.sin(a.y1-a.y)*a.m1*a.d1-a.si2*Math.sin(a.y2-a.y)*\\na.m1*a.d2)*a.q1;a.dy=3*(-a.si1*Math.sin(a.x1-a.x)*a.m1*a.d1+a.si2*Math.sin(a.x2-a.x)*a.m1*a.d2)*a.q2;a.dir=-a.q17+Math.asin(1);a.m1=25*a.q18;a.xx=a.q9;a.yy=1-a.q10;a.x1=a.xx-Math.sin(a.dir)*a.b1;a.y1=a.yy-Math.cos(a.dir)*a.b1;a.x2=a.xx+Math.sin(a.dir)*a.b1;a.y2=a.yy+Math.cos(a.dir)*a.b1;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.b1;a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.b1;a.si1=sigmoid(-a.d1,1E3);a.si2=sigmoid(-a.d2,1E3);a.dx+=3*(a.si1*Math.sin(a.y1-a.y)*a.m1*\\na.d1-a.si2*Math.sin(a.y2-a.y)*a.m1*a.d2)*a.q1;a.dy+=3*(-a.si1*Math.sin(a.x1-a.x)*a.m1*a.d1+a.si2*Math.sin(a.x2-a.x)*a.m1*a.d2)*a.q2;\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv + ((tmpvar_5 * texsize.zw) * 4.0));\\n  ret_1.z = (((texture (sampler_main, tmpvar_6).z - \\n    ((texture (sampler_main, tmpvar_6).z - ((texture (sampler_blur3, tmpvar_6).xyz * scale3) + bias3).z) * 0.02)\\n  ) - 0.008) + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * texsize_noise_lq.zw) + rand_frame.xy)\\n  ).xyz - 0.5) * 0.1)).x;\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_3.x;\\n  tmpvar_7.y = tmpvar_4.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = ((0.5 + (uv - 0.5)) - (tmpvar_7 * texsize.zw));\\n  ret_1.x = texture (sampler_main, tmpvar_8).x;\\n  ret_1.x = (ret_1.x + ((\\n    (ret_1.x - ((texture (sampler_blur3, tmpvar_8).xyz * scale3) + bias3))\\n  .x * 0.4) + 0.006));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_3.x;\\n  tmpvar_9.y = tmpvar_4.x;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_3.y;\\n  tmpvar_10.y = tmpvar_4.y;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = tmpvar_3.z;\\n  tmpvar_11.y = tmpvar_4.z;\\n  ret_1.y = texture (sampler_fc_main, (((uv - \\n    ((tmpvar_9 * texsize.zw) * 8.0)\\n  ) + (\\n    (tmpvar_10 * texsize.zw)\\n   * 4.0)) + ((tmpvar_11 * texsize.zw) * 8.0))).y;\\n  ret_1.y = (ret_1.y * (1.0 + (ret_1.x * 0.1)));\\n  ret_1.y = (ret_1.y - (0.004 + (\\n    clamp (ret_1.z, 0.0, 1.0)\\n   * 0.04)));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ret_1;\\n  ret = tmpvar_12.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 4.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_3.y;\\n  tmpvar_6.y = tmpvar_4.y;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = ((uv - (tmpvar_5 * 0.1)) + (tmpvar_6 * 0.06));\\n  vec4 tmpvar_8;\\n  tmpvar_8 = texture (sampler_main, uv);\\n  ret_1 = (((\\n    ((texture (sampler_blur2, tmpvar_7).xyz * scale2) + bias2)\\n  .x * \\n    clamp ((1.0 - tmpvar_8.z), 0.0, 1.0)\\n  ) * pow (hue_shader.yxz, vec3(8.0, 8.0, 8.0))) * 3.0);\\n  ret_1 = (mix (ret_1, (\\n    pow (hue_shader.yzx, vec3(8.0, 8.0, 8.0))\\n   * 1.4), vec3((\\n    (texture (sampler_main, tmpvar_7).x * 0.8)\\n   + \\n    ((texture (sampler_blur1, tmpvar_7).xyz * scale1) + bias1)\\n  .x))) * clamp ((1.0 - \\n    (((texture (sampler_blur1, uv).xyz * scale1) + bias1).y * 4.0)\\n  ), 0.0, 1.0));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_3.y;\\n  tmpvar_9.y = tmpvar_4.y;\\n  vec3 tmpvar_10;\\n  vec3 tmpvar_11;\\n  tmpvar_11 = pow (hue_shader, vec3(8.0, 8.0, 8.0));\\n  tmpvar_10 = mix (mix (ret_1, vec3(1.0, 1.0, 1.0), (\\n    (tmpvar_11 * texture (sampler_main, clamp ((uv - (tmpvar_9 * 2.0)), 0.0, 1.0)).z)\\n   * 1.2)), (tmpvar_11.zxy * 1.8), tmpvar_8.yyy);\\n  ret_1 = tmpvar_10;\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = tmpvar_10;\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.5,echo_zoom:1.03,wave_brighten:0,wrap:0,wave_a:.001,wave_scale:.01,wave_smoothing:0,modwavealphastart:1,modwavealphaend:1,warpscale:.107,zoomexp:4.28632,fshader:1,warp:.01743,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ob_g:1,ob_a:1,ib_size:0,ib_r:1,ib_g:1,ib_b:1,ib_a:1,mv_x:64,mv_y:48,mv_l:5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:100,textured:1,rad:.789,ang:.6283,tex_zoom:1.17257,r:0,g:1,border_a:0},init_eqs_str:\"a.an=0;a.vx=0;a.vy=0;\",frame_eqs_str:\"a.rad=.65+.1*a.bass;a.an=.99*a.an+.1*(a.bass-a.treb);a.ang=.1*a.an+.6;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:495,sep:4,spectrum:1,thick:1,additive:1,scaling:100,smoothing:1,r:0,g:.04,b:0,a:.99},init_eqs_str:\"a.my_z=0;a.d=0;a.n=0;a.y3=0;a.z2=0;a.y1=0;a.w=0;a.t5=0;a.w2=0;a.t1=0;a.x1=0;a.q6=0;a.zoom=0;a.p=0;a.q1=0;a.q5=0;a.t8=0;a.z3=0;a.w3=0;a.t3=0;a.my_x=0;a.x3=0;a.t6=0;a.my_y=0;a.pi=0;a.q4=0;a.t7=0;a.rd=0;a.w1=0;a.x2=0;a.t2=0;a.l=0;a.y2=0;a.q2=0;a.z1=0;a.q3=0;a.t4=0;a.t2=0;a.t3=0;a.t4=0;a.ab=1;\",frame_eqs_str:\"a.t1=a.q1;a.t2=a.q2;a.t3=a.q3;a.t4=a.q4;a.t5=a.q5;a.t6=a.q6;a.t8=.07;a.t7=1;\",point_eqs_str:\"a.t7=-a.t7;a.pi=Math.asin(1);a.n=180;a.rd=.075;a.my_x=.5*Math.sin(a.sample*a.pi*4+(a.t7+1)*a.t8)+Math.cos(a.sample*a.pi*a.n)*a.rd*Math.sin(a.sample*a.pi*4+(a.t7+1)*a.t8);a.my_y=.5*Math.cos(a.sample*a.pi*4+(a.t7+1)*a.t8)+Math.cos(a.sample*a.pi*a.n)*a.rd*Math.cos(a.sample*a.pi*4+(a.t7+1)*a.t8);a.my_z=Math.sin(a.sample*a.pi*a.n)*a.rd;a.d=1.4;a.zoom=.65;a.w1=a.q2;a.w2=a.q3;a.w3=a.q4;a.x1=Math.cos(a.w1)*a.my_x+Math.sin(a.w1)*a.my_y;a.y1=-Math.sin(a.w1)*a.my_x+Math.cos(a.w1)*a.my_y;\\na.z1=a.my_z;a.x2=Math.cos(a.w2)*a.x1+Math.sin(a.w2)*a.z1;a.z2=-Math.sin(a.w2)*a.x1+Math.cos(a.w2)*a.z1;a.y2=a.y1;a.y3=Math.cos(a.w3)*a.y2+Math.sin(a.w3)*a.z2;a.z3=-Math.sin(a.w3)*a.y2+Math.cos(a.w3)*a.z2;a.x3=a.x2;a.l=sqrt(a.x3*a.x3+a.y3*a.y3);a.w=Math.atan2(a.x3,a.y3);a.p=Math.tan(Math.asin(1)+Math.atan2(a.d+a.z3,a.l));a.d=sqrt(a.x3*a.x3+a.y3*a.y3+(a.z3+a.d)*(a.z3+a.d));a.my_x=a.zoom*Math.sin(a.w)*a.p;a.my_y=a.zoom*Math.cos(a.w)*a.p;a.x=.5+a.my_x;a.y=.5+a.my_y;a.b=-a.z3+.5;a.b=.5*Math.min(1,Math.max(0,\\na.b));a.r=1-2*a.b;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.n=0;a.n2=0;a.j3=0;a.q1=0;a.q5=0;a.n1=0;a.q9=0;a.v=0;a.q10=0;a.q4=0;a.j2=0;a.k=0;a.j1=0;a.q2=0;a.q3=0;a.x1=0;a.y1=0;\",frame_eqs_str:\"a.q1=0;a.v=.4;a.j1=.95*a.j1+sqr(4*a.bass)*a.v;a.j2=.95*a.j2+sqr(4*a.mid)*a.v;a.j3=.95*a.j3+sqr(4*a.treb)*a.v;a.n+=.0052*a.j1;a.n1+=.0052*a.j2;a.n2+=.0052*a.j3;a.q2=.01*a.n;a.q3=.01*a.n1;a.q4=.01*a.n2;a.k=.99*a.k+sqr(2*a.mid_att);a.q5=0*a.k;a.zoom=1.0016;a.warp=0;a.rot=-0;a.q9=a.aspectx;a.q10=a.aspecty;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 4.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (((texture (sampler_blur2, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale2) + bias2)).z;\\n  tmpvar_3.y = -(((\\n    (texture (sampler_blur2, (uv + (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (uv - (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale2)\\n   + bias2)).z);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv + ((tmpvar_3 * texsize.zw) * 60.0));\\n  vec2 x_5;\\n  x_5 = (tmpvar_4 - uv);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_main, uv);\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_blur1, uv);\\n  ret_1.z = (((texture (sampler_main, tmpvar_4).z + \\n    (((tmpvar_6.z - (\\n      (tmpvar_7.xyz * scale1)\\n     + bias1).z) * 200.0) * sqrt(dot (x_5, x_5)))\\n  ) * 0.96) - 0.02);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1)).y;\\n  tmpvar_8.y = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1)).y;\\n  ret_1.y = ((texture (sampler_main, (uv_orig + \\n    ((tmpvar_8 * texsize.zw) * 6.0)\\n  )).y + (\\n    (tmpvar_6.y - ((tmpvar_7.xyz * scale1) + bias1).y)\\n   * 0.1)) - 0.016);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = ret_1;\\n  ret = tmpvar_9.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.z;\\n  tmpvar_5.y = tmpvar_4.z;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_3.y;\\n  tmpvar_6.y = tmpvar_4.y;\\n  ret_1 = (mix (vec3(0.2, 0.0, 0.1), vec3(0.8, 0.6, 0.0), texture (sampler_main, (uv - \\n    (tmpvar_5 * 0.2)\\n  )).yyy) * ((\\n    (-(tmpvar_3.y) + tmpvar_4.y)\\n   + 1.0) + (\\n    (-(tmpvar_3.z) + tmpvar_4.z)\\n   * 2.0)));\\n  vec3 tmpvar_7;\\n  tmpvar_7 = mix (mix (ret_1, vec3(0.2, 0.0, 0.4), vec3((\\n    ((texture (sampler_blur1, (uv - (tmpvar_6 * 0.04))).xyz * scale1) + bias1)\\n  .x * 1.4))), vec3(1.0, 0.9, 0.8), texture (sampler_main, uv).zzz);\\n  ret_1 = tmpvar_7;\\n  vec4 tmpvar_8;\\n  tmpvar_8.w = 1.0;\\n  tmpvar_8.xyz = tmpvar_7;\\n  ret = tmpvar_8.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:1,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wave_a:2.789,wave_scale:.292,wave_smoothing:0,wave_mystery:.12,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.01,warpscale:100,zoomexp:.92178,zoom:.9901,warp:.01,wave_g:0,ob_size:0,ob_r:.2,ob_a:.1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:100,additive:1,thickoutline:1,x:.43,y:.42,rad:.15799,ang:.1885,tex_zoom:.87865,r:0,g:1,g2:0,border_a:0},init_eqs_str:\"a.q4=0;a.q8=0;\",frame_eqs_str:\"a.x=a.q4;a.y=a.q8;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.xx=0;a.si3=0;a.t1=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.d1=0;a.si1=0;a.vx4=0;a.grav=0;a.x3=0;a.d2=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.m1=0;a.spring=0;a.si2=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.b1=0;a.q8=0;a.x1=.9;a.y1=.5;a.x2=.5;a.y2=.5;a.x3=.5;a.y3=.5;a.x4=.5;a.y4=.5;\",frame_eqs_str:\"a.decay=1;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+a.xx1-a.xx2;a.y1=.5+a.yy1;a.spring=18;a.grav=1;a.resist=.2;a.bounce=.9;a.dt=.0003;a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-a.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*\\na.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*a.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*\\na.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q1=a.x1;a.q2=\\na.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.zoom=1.004;a.q6=Math.atan2(a.vx4,a.vy4);a.q5=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);\",pixel_eqs_str:\"a.dir=-a.q6+Math.asin(1);a.b1=.08;a.m1=45*a.q5;a.t1=.5;a.xx=.5+div(a.q4-.5,a.aspectx);a.yy=1-(.5+div(a.q8-.5,a.aspecty));a.x1=a.xx+Math.cos(a.dir+1.5708)*a.b1;a.y1=a.yy-Math.sin(a.dir+1.5708)*a.b1;a.x2=a.xx-Math.cos(a.dir+1.5708)*a.b1;a.y2=a.yy+Math.sin(a.dir+1.5708)*a.b1;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.b1;a.si1=1-div(1,1+pow(2,100*-a.d1));a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.b1;a.si2=1-div(1,1+pow(2,100*-a.d2));a.si3=0*-pow(a.q5,\\n3);a.dx=div(2*(a.si1*Math.sin(a.y1-a.y)*a.m1*a.d1-a.si2*Math.sin(a.y2-a.y)*a.m1*a.d2+a.si3*Math.cos(a.dir)*a.t1),a.aspectx);a.dy=div(2*(-a.si1*Math.sin(a.x1-a.x)*a.m1*a.d1+a.si2*Math.sin(a.x2-a.x)*a.m1*a.d2-a.si3*Math.sin(a.dir)*a.t1),a.aspecty);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1)).x;\\n  tmpvar_3.y = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1)).x;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv - ((tmpvar_3 * texsize.zw) * 0.5));\\n  ret_1.x = texture (sampler_fw_main, tmpvar_4).x;\\n  ret_1.x = (ret_1.x + ((\\n    (ret_1.x - ((texture (sampler_blur3, tmpvar_4).xyz * scale3) + bias3).x)\\n   * 0.2) - 0.004));\\n  ret_1.y = ((texture (sampler_fw_main, uv_orig).y * 0.98) - 0.004);\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ret_1;\\n  ret = tmpvar_5.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = (texsize.zw * 4.0);\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1)).x;\\n  tmpvar_2.y = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1)).x;\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = mix ((vec3(0.0, 0.0, 1.0) * texture (sampler_main, uv).x), vec3(1.0, 0.0, 0.0), texture (sampler_main, (uv - tmpvar_2)).yyy);\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:1,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,wave_thick:1,wave_brighten:0,wrap:0,wave_a:.004,wave_scale:.01,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.137,warpscale:13.125,zoom:.99951,warp:.99213,wave_y:.04,ob_size:.015,ob_r:.1,ob_g:.25,ob_a:1,ib_size:0,ib_b:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,rad:.02015,tex_ang:.12566,tex_zoom:1.51878,r:0,a:.1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.q12=0;a.x1=0;a.vx3=0;a.q13=0;a.q15=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.q9=0;a.vx4=0;a.x3=0;a.q11=0;a.q10=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.q16=0;a.x4=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.q14=0;a.vx2=0;a.q3=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.q8=0;\",frame_eqs_str:\"a.warp=0;a.wave_a=0;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+1.5*a.yy1;a.dt=div(.03,a.fps);a.vx2=a.vx2*(1-2*a.dt)+10*a.dt*(a.x1+a.x3-2*a.x2);a.vy2=a.vy2*(1-2*a.dt)+a.dt*(10*(a.y1+a.y3-2*a.y2)-.5);a.vx3=a.vx3*(1-2*a.dt)+10*a.dt*(a.x2+a.x4-2*a.x3);a.vy3=a.vy3*(1-2*a.dt)+a.dt*(10*(a.y2+a.y4-2*a.y3)-.5);a.vx4=a.vx4*(1-2*a.dt)+10*a.dt*(a.x3-a.x4);a.vy4=a.vy4*(1-2*a.dt)+a.dt*(10*(a.y3-a.y4)-.5);a.x2+=\\na.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:.5*Math.abs(a.vx2);a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:.5*-Math.abs(a.vx2);a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:.5*Math.abs(a.vx3);a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:.5*-Math.abs(a.vx3);a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:.5*Math.abs(a.vx4);a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:.5*-Math.abs(a.vx4);a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:.5*Math.abs(a.vy2);\\na.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:.5*-Math.abs(a.vy2);a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:.5*Math.abs(a.vy3);a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:.5*-Math.abs(a.vy3);a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:.5*Math.abs(a.vy4);a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:.5*-Math.abs(a.vy4);a.q1=a.x1;a.q2=a.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.q9=div(1,a.aspectx);a.q10=div(1,a.aspecty);a.q11=a.aspectx;a.q12=a.aspecty;a.q13=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);\\na.q14=Math.atan2(a.vx4,a.vy4);a.q15=Math.sin(a.q14);a.q16=Math.cos(a.q14);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 my_uv_2;\\n  vec3 ret_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q4;\\n  tmpvar_4.y = (1.0 - q8);\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = -(q15);\\n  tmpvar_5.y = q16;\\n  vec2 domain_6;\\n  vec2 uv_rot_7;\\n  domain_6 = (uv - tmpvar_4);\\n  domain_6 = (domain_6 * aspect.xy);\\n  float tmpvar_8;\\n  tmpvar_8 = (1.0/((1.0 + exp(\\n    ((sqrt(dot (domain_6, domain_6)) - 0.07) * 50.0)\\n  ))));\\n  domain_6 = (domain_6 + ((tmpvar_5 * q13) * (aspect.wz * tmpvar_8)));\\n  float tmpvar_9;\\n  tmpvar_9 = sin(tmpvar_8);\\n  float tmpvar_10;\\n  tmpvar_10 = cos(tmpvar_8);\\n  uv_rot_7.x = ((tmpvar_10 * domain_6.x) - (tmpvar_9 * domain_6.y));\\n  uv_rot_7.y = ((tmpvar_9 * domain_6.x) + (tmpvar_10 * domain_6.y));\\n  uv_rot_7 = (uv_rot_7 * aspect.zw);\\n  uv_rot_7 = (uv_rot_7 + tmpvar_4);\\n  uv_1 = (clamp ((tmpvar_4 + \\n    ((uv_rot_7 - tmpvar_4) * mix (1.0, 4.0, tmpvar_8))\\n  ), 0.0, 1.0) + (texsize.zw * vec2(0.0, 0.15)));\\n  vec2 tmpvar_11;\\n  tmpvar_11 = mix (uv_orig, uv_1, vec2(0.2, 0.2));\\n  uv_1 = tmpvar_11;\\n  vec2 tmpvar_12;\\n  tmpvar_12 = (vec2(1280.0, 1024.0) * texsize.zw);\\n  float tmpvar_13;\\n  tmpvar_13 = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.005, 0.0))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.005, 0.0))).xyz * scale2)\\n   + bias2)).x * tmpvar_12.x);\\n  float tmpvar_14;\\n  tmpvar_14 = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.0, 0.005))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.0, 0.005))).xyz * scale2)\\n   + bias2)).x * tmpvar_12.y);\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = tmpvar_13;\\n  tmpvar_15.y = tmpvar_14;\\n  vec2 tmpvar_16;\\n  tmpvar_16.x = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.005, 0.0))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.005, 0.0))).xyz * scale2)\\n   + bias2)).x * tmpvar_12.x);\\n  tmpvar_16.y = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.0, 0.005))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.0, 0.005))).xyz * scale2)\\n   + bias2)).x * tmpvar_12.y);\\n  ret_3.x = texture (sampler_fw_main, ((tmpvar_11 - (tmpvar_15 * 0.006)) + (tmpvar_16 * 0.003))).x;\\n  ret_3.x = (ret_3.x + ((ret_3.x - \\n    ((texture (sampler_blur3, tmpvar_11).xyz * scale3) + bias3)\\n  .x) * 0.1));\\n  ret_3.x = (ret_3.x + 0.004);\\n  vec2 tmpvar_17;\\n  tmpvar_17.x = tmpvar_14;\\n  tmpvar_17.y = -(tmpvar_13);\\n  my_uv_2 = (uv_orig + ((tmpvar_17 * 0.05) * (1.2 - \\n    ((texture (sampler_blur3, uv_orig).xyz * scale3) + bias3)\\n  .y)));\\n  ret_3.z = texture (sampler_fw_main, my_uv_2).z;\\n  vec2 x_18;\\n  x_18 = (my_uv_2 - uv_orig);\\n  ret_3.z = (ret_3.z + ((\\n    ((ret_3.z - ((texture (sampler_blur1, uv_orig).xyz * scale1) + bias1).z) * sqrt(dot (x_18, x_18)))\\n   * 180.0) / sqrt(\\n    dot (tmpvar_12, tmpvar_12)\\n  )));\\n  ret_3.z = (ret_3.z * 0.85);\\n  ret_3.z = (ret_3.z + 0.008);\\n  vec2 tmpvar_19;\\n  tmpvar_19.x = -(tmpvar_14);\\n  tmpvar_19.y = tmpvar_13;\\n  my_uv_2 = (tmpvar_19 * 0.045);\\n  vec2 tmpvar_20;\\n  tmpvar_20.x = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2)).y * tmpvar_12.x);\\n  tmpvar_20.y = (((\\n    (texture (sampler_blur2, (tmpvar_11 + vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (tmpvar_11 - vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2)).y * tmpvar_12.y);\\n  my_uv_2 = (my_uv_2 + (tmpvar_11 - (tmpvar_20 * 0.03)));\\n  ret_3.y = texture (sampler_fw_main, my_uv_2).y;\\n  ret_3.y = (ret_3.y + ((\\n    (ret_3.y - ((texture (sampler_blur3, my_uv_2).xyz * scale3) + bias3).y)\\n   * 0.1) + 0.03));\\n  vec4 tmpvar_21;\\n  tmpvar_21.w = 1.0;\\n  tmpvar_21.xyz = ret_3;\\n  ret = tmpvar_21.xyz;\\n }\",comp:\" shader_body { \\n  vec2 dz_1;\\n  vec3 dy_2;\\n  vec3 dx_3;\\n  vec2 d_4;\\n  vec3 ret_5;\\n  vec2 tmpvar_6;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = (vec2(1.0, 0.0) * texsize.zw);\\n  tmpvar_6.x = (texture (sampler_main, (uv + tmpvar_7)).xyz - texture (sampler_main, (uv - tmpvar_7)).xyz).y;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (vec2(0.0, 1.0) * texsize.zw);\\n  tmpvar_6.y = (texture (sampler_main, (uv + tmpvar_8)).xyz - texture (sampler_main, (uv - tmpvar_8)).xyz).y;\\n  d_4 = (texsize.zw * 2.0);\\n  dx_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * d_4))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * d_4))\\n  ).xyz * scale1) + bias1));\\n  dy_2 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * d_4))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * d_4))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = dx_3.y;\\n  tmpvar_9.y = dy_2.y;\\n  dz_1 = ((tmpvar_6 * 3.0) + tmpvar_9);\\n  ret_5 = (vec3(((\\n    pow ((sqrt(dot (dz_1, dz_1)) * 0.8), 0.7)\\n   + \\n    (((texture (sampler_blur2, uv).xyz * scale2) + bias2).y * 0.4)\\n  ) - 0.1)) * vec3(0.3, 0.5, 0.7));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = dx_3.x;\\n  tmpvar_10.y = dy_2.x;\\n  vec3 tmpvar_11;\\n  tmpvar_11 = mix (mix (ret_5, vec3(0.2, 0.1, 0.0), vec3((texture (sampler_main, \\n    (uv + ((tmpvar_10 * texsize.zw) * 18.0))\\n  ).x * 6.0))), vec3(1.0, 1.0, 1.0), texture (sampler_main, uv).zzz);\\n  ret_5 = tmpvar_11;\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = tmpvar_11;\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,wave_thick:1,wave_brighten:0,wave_a:.004,wave_scale:.267,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpscale:16.016,zoomexp:11.56276,zoom:1.05971,warp:.13126,wave_y:.04,ob_size:0,ob_a:1,ib_size:0,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,rad:.01,tex_ang:.12566,tex_zoom:1.51878,g:.99,b:1,a:.1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.v=0;a.t=0;a.q12=0;a.q13=0;a.q11=div(.5,Math.asin(1));\",frame_eqs_str:\"a.zoom=1;a.v=.9*a.v+.04*(a.bass-a.treb);a.t+=.01*a.v;a.q12=.1*a.time+a.t;a.q13=.25+.01*(a.bass_att-a.treb_att);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec2 d_2;\\n  vec3 ret_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (texsize.zw * 4.0);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur1, \\n    fract((uv + (vec2(1.0, 0.0) * tmpvar_4)))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    fract((uv - (vec2(1.0, 0.0) * tmpvar_4)))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (((texture (sampler_blur1, \\n    fract((uv + (vec2(0.0, 1.0) * tmpvar_4)))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    fract((uv - (vec2(0.0, 1.0) * tmpvar_4)))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_7;\\n  tmpvar_7 = ((texture (sampler_blur1, uv).xyz * scale1) + bias1);\\n  d_2 = -(tmpvar_4);\\n  float tmpvar_8;\\n  tmpvar_8 = (d_2 * 4.0).x;\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_5.y;\\n  tmpvar_9.y = tmpvar_6.y;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_5.x;\\n  tmpvar_10.y = tmpvar_6.x;\\n  vec2 tmpvar_11;\\n  tmpvar_11 = ((uv - (tmpvar_9 * d_2)) - (tmpvar_10 * tmpvar_8));\\n  ret_3.y = texture (sampler_fc_main, (tmpvar_11 - floor(tmpvar_11))).y;\\n  ret_3.y = (ret_3.y + ((\\n    (ret_3.y - tmpvar_7.y)\\n   * 0.042) + -0.0075));\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = tmpvar_5.x;\\n  tmpvar_12.y = tmpvar_6.x;\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = tmpvar_5.z;\\n  tmpvar_13.y = tmpvar_6.z;\\n  my_uv_1 = ((uv - (tmpvar_12 * d_2)) - (tmpvar_13 * tmpvar_8));\\n  ret_3.x = texture (sampler_fc_main, (my_uv_1 - floor(my_uv_1))).x;\\n  ret_3.x = (ret_3.x + ((\\n    (ret_3.x - tmpvar_7.x)\\n   * 0.042) + -0.0075));\\n  vec2 tmpvar_14;\\n  tmpvar_14.x = tmpvar_5.z;\\n  tmpvar_14.y = tmpvar_6.z;\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = tmpvar_5.y;\\n  tmpvar_15.y = tmpvar_6.y;\\n  my_uv_1 = ((uv - (tmpvar_14 * d_2)) - (tmpvar_15 * tmpvar_8));\\n  ret_3.z = texture (sampler_fc_main, (my_uv_1 - floor(my_uv_1))).z;\\n  ret_3.z = (ret_3.z + ((\\n    (ret_3.z - tmpvar_7.z)\\n   * 0.042) + -0.0075));\\n  vec4 tmpvar_16;\\n  tmpvar_16.w = 1.0;\\n  tmpvar_16.xyz = ret_3;\\n  ret = tmpvar_16.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  uv_1 = uv;\\n  vec3 ret_2;\\n  float tmpvar_3;\\n  tmpvar_3 = -(q12);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = ((uv - 0.5) * aspect.wz);\\n  float tmpvar_5;\\n  float tmpvar_6;\\n  tmpvar_6 = (min (abs(\\n    (tmpvar_4.x / tmpvar_4.y)\\n  ), 1.0) / max (abs(\\n    (tmpvar_4.x / tmpvar_4.y)\\n  ), 1.0));\\n  float tmpvar_7;\\n  tmpvar_7 = (tmpvar_6 * tmpvar_6);\\n  tmpvar_7 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_7) + 0.05368138) * tmpvar_7) - 0.1173503)\\n     * tmpvar_7) + 0.1938925) * tmpvar_7) - 0.3326756)\\n   * tmpvar_7) + 0.9999793) * tmpvar_6);\\n  tmpvar_7 = (tmpvar_7 + (float(\\n    (abs((tmpvar_4.x / tmpvar_4.y)) > 1.0)\\n  ) * (\\n    (tmpvar_7 * -2.0)\\n   + 1.570796)));\\n  tmpvar_5 = (tmpvar_7 * sign((tmpvar_4.x / tmpvar_4.y)));\\n  if ((abs(tmpvar_4.y) > (1e-08 * abs(tmpvar_4.x)))) {\\n    if ((tmpvar_4.y < 0.0)) {\\n      if ((tmpvar_4.x >= 0.0)) {\\n        tmpvar_5 += 3.141593;\\n      } else {\\n        tmpvar_5 = (tmpvar_5 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_5 = (sign(tmpvar_4.x) * 1.570796);\\n  };\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = ((tmpvar_5 * q11) - tmpvar_3);\\n  tmpvar_8.y = (((q13 * \\n    log(sqrt(dot (tmpvar_4, tmpvar_4)))\\n  ) + (tmpvar_5 * q11)) + tmpvar_3);\\n  uv_1 = (0.5 + (0.5 - abs(\\n    ((fract((tmpvar_8 * 0.5)) * 2.0) - 1.0)\\n  )));\\n  vec4 tmpvar_9;\\n  tmpvar_9 = texture (sampler_main, uv_1);\\n  ret_2 = (vec3(dot (tmpvar_9.xyz, vec3(0.32, 0.49, 0.29))) * mix (vec3(1.0, 1.0, 1.0), vec3(0.2, 0.5, 1.0), tmpvar_9.xxx));\\n  ret_2 = (ret_2 * 2.0);\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = ret_2;\\n  ret = tmpvar_10.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,wave_thick:1,wave_brighten:0,wrap:0,wave_a:.004,wave_scale:.242,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.88,warpscale:9.181,zoomexp:.65309,zoom:.87866,warp:.04914,wave_y:.04,ob_size:.05,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,y:.04,rad:.01,tex_ang:.12566,tex_zoom:1.51878,r:0,a:0,g2:0,b2:.01,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,additive:1,scaling:2.0231,smoothing:0,r:0,b:0},init_eqs_str:\"a.tt3=0;a.tt2=0;a.tt1=0;a.d=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.r=1;a.g=0;a.b=1;\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.6+a.d*a.sample*(1-a.sample)*2;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.c_inv_i=0;a.translation_x=0;a.q12=0;a.a_i=0;a.a_r=0;a.q18=0;a.q13=0;a.scale=0;a.q15=0;a.c_inv_r=0;a.angle=0;a.q11=0;a.bcad_r=0;a.b_r=0;a.q16=0;a.bcad_i=0;a.q17=0;a.q14=0;a.translation_y=0;a.translation_v=0;a.b_i=0;a.translation_u=0;a.x1=.9;a.y1=.5;a.x2=.5;a.y2=.5;a.x3=.5;a.y3=.5;a.x4=.5;a.y4=.5;\",frame_eqs_str:\"a.zoom=1;a.scale=1;a.angle=.1*a.time;a.translation_x=0;a.translation_y=.12;a.a_r=Math.cos(a.angle)*a.scale;a.a_i=Math.sin(a.angle)*a.scale;a.b_r=a.translation_x;a.b_i=a.translation_y;a.scale=1.6;a.angle=0;a.translation_u=0;a.translation_v=0;a.q15=Math.cos(a.angle)*a.scale;a.q16=Math.sin(a.angle)*a.scale;a.q17=a.translation_u;a.q18=a.translation_v;a.c_inv_r=div(a.q15,a.q15*a.q15+a.q16*a.q16);a.c_inv_i=div(a.q16,a.q15*a.q15+a.q16*a.q16);a.q11=a.a_r*a.c_inv_r-a.a_i*a.c_inv_i;a.q12=\\na.a_r*a.c_inv_i-a.a_i*a.c_inv_r;a.bcad_r=a.b_r*a.q15-a.b_i*a.q16-(a.a_r*a.q17-a.a_i*a.q18);a.bcad_i=a.b_r*a.q16-a.b_i*a.q15-(a.a_r*a.q18-a.a_i*a.q17);a.q13=a.bcad_r*a.c_inv_r-a.bcad_i*a.c_inv_i;a.q14=a.bcad_r*a.c_inv_i-a.bcad_i*a.c_inv_r;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  float conway_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (((\\n    ((texture (sampler_blur2, (uv + vec2(0.02, 0.0))).xyz * scale2) + bias2)\\n   - \\n    ((texture (sampler_blur2, (uv - vec2(0.02, 0.0))).xyz * scale2) + bias2)\\n  ).y * 1280.0) * texsize.z);\\n  tmpvar_3.y = (((\\n    ((texture (sampler_blur2, (uv + vec2(0.0, 0.02))).xyz * scale2) + bias2)\\n   - \\n    ((texture (sampler_blur2, (uv - vec2(0.0, 0.02))).xyz * scale2) + bias2)\\n  ).y * 1024.0) * texsize.w);\\n  ret_2.y = texture (sampler_pc_main, (uv - (tmpvar_3 * 0.004))).y;\\n  ret_2.y = (ret_2.y + ((\\n    ((ret_2.y - ((texture (sampler_blur1, uv).xyz * scale1) + bias1).y) - 0.1)\\n   * 0.1) + 0.02));\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (vec2(0.0, 1.0) * texsize.zw);\\n  ret_2.z = (texture (sampler_fc_main, (uv - tmpvar_4)).z - 0.004);\\n  conway_1 = (texture (sampler_pw_main, (uv_orig - texsize.zw)).x + texture (sampler_pw_main, (uv_orig + (vec2(0.0, -1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(1.0, -1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(-1.0, 0.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(1.0, 0.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(-1.0, 1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + tmpvar_4)).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + texsize.zw)).x);\\n  conway_1 = (conway_1 - fract(conway_1));\\n  float tmpvar_5;\\n  tmpvar_5 = clamp (texture (sampler_pc_main, uv_orig).x, 0.0, 1.0);\\n  ret_2.x = (clamp ((1.0 - \\n    abs((conway_1 - 3.0))\\n  ), 0.0, 1.0) * (1.0 - tmpvar_5));\\n  ret_2.x = (ret_2.x + (clamp (\\n    max ((1.0 - abs((conway_1 - 2.0))), (1.0 - abs((conway_1 - 3.0))))\\n  , 0.0, 1.0) * tmpvar_5));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret_2;\\n  ret = tmpvar_6.xyz;\\n }\",comp:\" shader_body { \\n  vec2 moebius_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q11;\\n  tmpvar_3.y = q12;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q17;\\n  tmpvar_4.y = q18;\\n  vec2 tmpvar_5;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - 0.5);\\n  tmpvar_5 = (tmpvar_6 * aspect.xy);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = ((tmpvar_5.x * q15) - (tmpvar_5.y * q16));\\n  tmpvar_7.y = ((tmpvar_5.x * q16) - (tmpvar_5.y * q15));\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (tmpvar_7 + tmpvar_4);\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = ((q13 * tmpvar_8.x) + (q14 * tmpvar_8.y));\\n  tmpvar_9.y = ((q14 * tmpvar_8.x) - (q13 * tmpvar_8.y));\\n  moebius_1 = (((tmpvar_9 / \\n    ((tmpvar_8.x * tmpvar_8.x) + (tmpvar_8.y * tmpvar_8.y))\\n  ) + tmpvar_3) * 0.5);\\n  float tmpvar_10;\\n  tmpvar_10 = sqrt(dot (moebius_1, moebius_1));\\n  moebius_1 = (0.5 + ((\\n    (1.0 - abs(((\\n      fract((moebius_1 * 0.5))\\n     * 2.0) - 1.0)))\\n   - 0.5) * 0.95));\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (0.5 + (tmpvar_6 * 0.2));\\n  ret_2 = (texture (sampler_main, tmpvar_11).z * vec3(0.4, 0.0, 0.7));\\n  ret_2 = (mix (ret_2, vec3(0.0, 1.0, 1.0), vec3(clamp (texture (sampler_fc_main, moebius_1).y, 0.0, 1.0))) * (1.4 - pow (\\n    (tmpvar_10 * 0.8)\\n  , 0.3)));\\n  vec3 tmpvar_12;\\n  tmpvar_12 = mix (mix (mix (ret_2, vec3(4.0, 1.0, 0.0), vec3(\\n    ((clamp ((texture (sampler_fc_main, tmpvar_11).y - texture (sampler_pc_main, tmpvar_11).y), 0.0, 1.0) * 4.0) * (tmpvar_10 * tmpvar_10))\\n  )), vec3(-4.0, -4.0, -4.0), texture (sampler_main, tmpvar_11).xxx), vec3(2.0, 2.0, 2.0), vec3((texture (sampler_pc_main, tmpvar_11).x * 0.75)));\\n  ret_2 = tmpvar_12;\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 1.0;\\n  tmpvar_13.xyz = tmpvar_12;\\n  ret = tmpvar_13.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:1,gammaadj:1.28,decay:.8,echo_zoom:1,echo_orient:3,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,brighten:1,wave_a:.001,wave_scale:1.286,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:.01,warpscale:100,zoomexp:.92178,zoom:.9901,warp:.01,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.005,ob_g:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.y3=0;a.y1=0;a.xx=0;a.s=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.v=0;a.vx4=0;a.grav=0;a.x3=0;a.q11=0;a.q10=0;a.xx2=0;a.q4=0;a.a=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.spring=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.vy3=0;a.xx1=0;a.v2=0;a.q8=0;a.x1=.9;a.y1=.5;a.x2=.5;a.y2=.5;a.x3=.5;a.y3=.5;a.x4=.5;a.y4=.5;\",frame_eqs_str:\"a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=10;a.grav=.5;a.resist=1;a.bounce=.75;a.dt=.0001*div(60,a.fps);a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-\\na.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*\\na.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<\\nMath.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q4=a.x4;a.q8=a.y4;a.q1=a.aspectx;a.q2=a.aspecty;a.q10=a.x1;a.q11=a.y1;a.zoom=1;a.warp=0;a.q6=Math.atan2(a.vx4,a.vy4);a.q5=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.a=.95*a.a+a.q5;a.s=.9*a.s+a.a;a.q3=.1*a.s;a.monitor=a.s;a.wave_a=0;\",pixel_eqs_str:\"a.x=.5+(a.x-.5)*a.q1;a.y=.5+(a.y-.5)*a.q2;a.xx=a.q4;a.yy=1-a.q8;a.dx=0;a.dy=0;a.d=sqrt((a.x-a.xx)*(a.x-a.xx)+(a.y-a.yy)*(a.y-a.yy));a.r=.1;a.v=20;a.v2=a.q5;a.dx=(a.v*(Math.sin(a.y-a.yy)*(a.d-a.r)-(a.x-a.xx)*(a.d-div(a.r,2)))+Math.cos(a.dir)*a.v2)*(1-sigmoid(a.d-a.r,120));a.dy=(-a.v*(Math.sin(a.x-a.xx)*(a.d-a.r)+(a.y-a.yy)*(a.d-div(a.r,2)))-Math.sin(a.dir)*a.v2)*(1-sigmoid(a.d-a.r,120));\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = mix (uv_orig, uv, vec2(2.0, 2.0));\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 2.0);\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (tmpvar_2 + (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (tmpvar_2 - (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur1, \\n    (tmpvar_2 + (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (tmpvar_2 - (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_4.x;\\n  tmpvar_6.y = tmpvar_5.x;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = (tmpvar_2 + ((tmpvar_6 * texsize.zw) * 8.0));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_4.y;\\n  tmpvar_8.y = tmpvar_5.y;\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (tmpvar_2 + ((tmpvar_8 * texsize.zw) * 8.0));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4.z;\\n  tmpvar_10.y = tmpvar_5.z;\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (tmpvar_2 + ((tmpvar_10 * texsize.zw) * 8.0));\\n  ret_1.x = (texture (sampler_main, tmpvar_7).x - ((texture (sampler_main, tmpvar_7).xyz - \\n    ((texture (sampler_blur3, tmpvar_7).xyz * scale3) + bias3)\\n  ).x * 0.02));\\n  ret_1.y = (texture (sampler_main, tmpvar_9).y - ((texture (sampler_main, tmpvar_9).xyz - \\n    ((texture (sampler_blur3, tmpvar_9).xyz * scale3) + bias3)\\n  ).y * 0.02));\\n  ret_1.z = (texture (sampler_main, tmpvar_11).z - ((texture (sampler_main, tmpvar_11).xyz - \\n    ((texture (sampler_blur3, tmpvar_11).xyz * scale3) + bias3)\\n  ).z * 0.02));\\n  ret_1 = (ret_1 + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 1.2)) + rand_frame.xy)\\n  ).xyz - 0.5) * 0.04));\\n  ret_1 = (ret_1 - ((ret_1.yzx * 0.1) - 0.02));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ret_1;\\n  ret = tmpvar_12.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = (texsize.zw * 8.0);\\n  vec3 tmpvar_2;\\n  tmpvar_2 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_1))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q4;\\n  tmpvar_4.y = (1.0 - q8);\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_2.y;\\n  tmpvar_5.y = tmpvar_3.y;\\n  vec2 x_6;\\n  x_6 = ((uv - (tmpvar_5 * 2.0)) - tmpvar_4);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_2.x;\\n  tmpvar_7.y = tmpvar_3.x;\\n  vec2 x_8;\\n  x_8 = ((uv - (tmpvar_7 * 2.0)) - tmpvar_4);\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_2.z;\\n  tmpvar_9.y = tmpvar_3.z;\\n  vec2 x_10;\\n  x_10 = ((uv - (tmpvar_9 * 2.0)) - tmpvar_4);\\n  vec3 tmpvar_11;\\n  tmpvar_11.x = (1.1 - pow (sqrt(\\n    dot (x_8, x_8)\\n  ), 0.2));\\n  tmpvar_11.y = (1.1 - pow (sqrt(\\n    dot (x_6, x_6)\\n  ), 0.2));\\n  tmpvar_11.z = (1.1 - pow (sqrt(\\n    dot (x_10, x_10)\\n  ), 0.2));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ((tmpvar_11 * texture (sampler_main, uv).xyz) * 2.4);\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:1,echo_zoom:1,echo_orient:2,wave_mode:1,wave_thick:1,wave_brighten:0,wrap:0,wave_a:.004,wave_scale:.01,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpanimspeed:1.311,warpscale:8.311,zoomexp:5.20652,warp:.13291,wave_y:.04,ob_size:.005,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:0,mv_y:0,mv_l:1,mv_g:.91,mv_b:.71,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:100,additive:1,thickoutline:1,y:.75,rad:.07493,tex_zoom:.73458,g:1,g2:0,border_a:0},init_eqs_str:\"a.q4=0;a.q8=0;a.vx=0;\",frame_eqs_str:\"a.x=a.q4;a.y=a.q8;\"},{baseVals:{enabled:1,sides:100,additive:1,thickoutline:1,textured:1,x:.7,y:.3,rad:.6623,ang:2.45044,tex_zoom:.74934,g:.1,b:.1,r2:1,g2:.1,b2:.1,a2:1,border_a:0},init_eqs_str:\"a.d=0;a.xx=0;a.q4=0;a.yy=0;a.q8=0;a.aang=0;a.xx=.5;a.yy=.4;\",frame_eqs_str:\"a.d=sqrt(sqr(a.xx-a.q4)+sqr(a.yy-a.q8));a.xx=.00001<Math.abs(below(a.d,.15))?.4+div(randint(200),1E3):a.xx;a.yy=.00001<Math.abs(below(a.d,.15))?.3+div(randint(400),1E3):a.yy;a.aang=.00001<Math.abs(below(a.d,.12))?div(randint(1E3),1E3):a.aang;a.ang=4*a.aang*Math.asin(1);a.x=a.xx;a.y=a.yy;\"},{baseVals:{enabled:1,sides:100,additive:1,thickoutline:1,textured:1,x:.43,y:.6,rad:1.16781,ang:5.96903,tex_zoom:.67165,r2:1,g2:0,a2:1,border_a:0},init_eqs_str:\"a.d=0;a.xx=0;a.q4=0;a.yy=0;a.q8=0;a.aang=0;a.xx=.5;a.yy=.4;\",frame_eqs_str:\"a.d=sqrt(sqr(a.xx-a.q4)+sqr(a.yy-a.q8));a.xx=.00001<Math.abs(below(a.d,.15))?.4+div(randint(200),1E3):a.xx;a.yy=.00001<Math.abs(below(a.d,.15))?.3+div(randint(400),1E3):a.yy;a.aang=.00001<Math.abs(below(a.d,.12))?div(randint(1E3),1E3):a.aang;a.ang=4*a.aang*Math.asin(1);a.x=a.xx;a.y=a.yy;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.spx3=0;a.x1=0;a.vx3=0;a.q6=0;a.spy2=0;a.dt=0;a.spy3=0;a.q1=0;a.q5=0;a.spx2=0;a.vx4=0;a.spx4=0;a.grav=0;a.x3=0;a.ry4=0;a.xx2=0;a.q4=0;a.spy4=0;a.yy1=0;a.vy4=0;a.bounce=0;a.x4=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.spring=0;a.vx2=0;a.q3=0;a.resist=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.q8=0;a.x1=0;a.y1=0;\",frame_eqs_str:\"a.decay=.25;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=50;a.grav=2;a.resist=5;a.bounce=.75;a.dt=div(.009,a.fps);a.spx2=(a.x1+a.x3-2*a.x2)*a.spring;a.spy2=(a.y1+a.y3-2*a.y2)*a.spring;a.spx3=(a.x2+a.x4-2*a.x3)*a.spring;a.spy3=(a.y2+a.y4-2*a.y3)*a.spring;a.spx4=(a.x3-a.x4)*a.spring;a.spy4=(a.y3-a.y4)*a.spring;a.vx2=a.vx2*\\n(1-a.resist*a.dt)+a.dt*a.spx2;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*(a.spy2-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*a.spx3;a.vy3=a.vy3*(1-a.resist*a.dt)+a.dt*(a.spy3-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*a.spx4;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*(a.spy4-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,.1))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,.9))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,\\n.1))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,.9))?a.vx3:-Math.abs(a.vx3)*a.bounce;a.vx4=.00001<Math.abs(above(a.x4,.1))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,.9))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*\\na.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q1=a.x1;a.q2=a.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.monitor=a.ry4;a.zoom=1.0004;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec3 ret_2;\\n  float tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur2, \\n    (uv + vec2(0.005, 0.0))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    (uv - vec2(0.005, 0.0))\\n  ).xyz * scale2) + bias2)).y;\\n  float tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur2, \\n    (uv + vec2(0.0, 0.005))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    (uv - vec2(0.0, 0.005))\\n  ).xyz * scale2) + bias2)).y;\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3;\\n  tmpvar_5.y = tmpvar_4;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - (tmpvar_5 * 0.01));\\n  ret_2.y = texture (sampler_fc_main, (tmpvar_6 - floor(tmpvar_6))).y;\\n  ret_2.y = (ret_2.y + ((ret_2.y - \\n    ((texture (sampler_blur3, (tmpvar_6 - floor(tmpvar_6))).xyz * scale3) + bias3)\\n  .y) * 0.1));\\n  vec2 tmpvar_7;\\n  tmpvar_7 = floor(uv);\\n  ret_2.y = (ret_2.y + (0.006 - (\\n    ((texture (sampler_blur3, (uv - tmpvar_7)).xyz * scale3) + bias3)\\n  .x * 5.0)));\\n  ret_2.y = ret_2.y;\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = -(tmpvar_4);\\n  tmpvar_8.y = tmpvar_3;\\n  my_uv_1 = (tmpvar_8 * 0.05);\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = (((texture (sampler_blur2, \\n    (uv + vec2(0.01, 0.0))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    (uv - vec2(0.01, 0.0))\\n  ).xyz * scale2) + bias2)).z;\\n  tmpvar_9.y = (((texture (sampler_blur2, \\n    (uv + vec2(0.0, 0.01))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    (uv - vec2(0.0, 0.01))\\n  ).xyz * scale2) + bias2)).z;\\n  my_uv_1 = (my_uv_1 + (uv - (tmpvar_9 * 0.005)));\\n  ret_2.z = texture (sampler_fw_main, (my_uv_1 - floor(my_uv_1))).z;\\n  ret_2.z = (ret_2.z + ((ret_2.z - \\n    ((texture (sampler_blur3, (my_uv_1 - floor(my_uv_1))).xyz * scale3) + bias3)\\n  .z) * 0.13));\\n  ret_2.z = (ret_2.z * 0.95);\\n  ret_2.z = (ret_2.z + ((0.03 - \\n    ((texture (sampler_blur3, (uv - tmpvar_7)).xyz * scale3) + bias3)\\n  .x) - (texture (sampler_main, \\n    (my_uv_1 - floor(my_uv_1))\\n  ).y * 0.05)));\\n  ret_2.x = (texture (sampler_main, uv_orig).x - 0.3);\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = ret_2;\\n  ret = tmpvar_10.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 8.0);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_blur1, \\n    (uv + (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(1.0, 0.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv + (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv - (vec2(0.0, 1.0) * tmpvar_2))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.y;\\n  tmpvar_5.y = tmpvar_4.y;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv + (tmpvar_5 * 0.1));\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_main, uv);\\n  ret_1 = (((\\n    ((texture (sampler_blur2, tmpvar_6).xyz * scale2) + bias2)\\n  .x * \\n    clamp ((1.0 - tmpvar_7.z), 0.0, 1.0)\\n  ) * pow (hue_shader.yxz, vec3(8.0, 8.0, 8.0))) * 3.0);\\n  ret_1 = (mix (ret_1, (\\n    pow (hue_shader.yzx, vec3(8.0, 8.0, 8.0))\\n   * 1.4), vec3((\\n    (texture (sampler_main, tmpvar_6).x * 0.8)\\n   + \\n    ((texture (sampler_blur1, tmpvar_6).xyz * scale1) + bias1)\\n  .x))) * clamp ((1.0 - \\n    (((texture (sampler_blur1, uv).xyz * scale1) + bias1).y * 4.0)\\n  ), 0.0, 1.0));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_3.y;\\n  tmpvar_8.y = tmpvar_4.y;\\n  vec3 tmpvar_9;\\n  vec3 tmpvar_10;\\n  tmpvar_10 = pow (hue_shader, vec3(8.0, 8.0, 8.0));\\n  tmpvar_9 = mix (mix (ret_1, vec3(1.0, 1.0, 1.0), (\\n    (tmpvar_10 * texture (sampler_main, clamp ((uv - (tmpvar_8 * 0.04)), 0.0, 1.0)).z)\\n   * 1.2)), (tmpvar_10.zxy * 1.8), tmpvar_7.yyy);\\n  ret_1 = tmpvar_9;\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = tmpvar_9;\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.56,decay:1,echo_zoom:.362,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.599,wave_smoothing:0,wave_mystery:-.5,modwavealphastart:2,modwavealphaend:2,warpscale:.107,zoomexp:.1584,fshader:1,warp:.01,wave_r:.51,wave_g:.5,ob_size:0,ob_a:1,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:.5,mv_r:0,mv_g:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.y3=0;a.y1=0;a.xx=0;a.s=0;a.q12=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.v=0;a.vx4=0;a.mm=0;a.tt=0;a.grav=0;a.x3=0;a.xx2=0;a.q4=0;a.a=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.t=0;a.r=0;a.x2=0;a.mx=0;a.mn=0;a.vy2=0;a.y2=0;a.bb=0;a.q2=0;a.spring=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.vy3=0;a.xx1=0;a.q8=0;a.q11=div(.5,Math.asin(1));\",frame_eqs_str:\"a.ib_r=.3*Math.sin(5*a.time)+.7;a.ib_g=.3*Math.sin(4*a.time)+.3;a.ib_b=.5*Math.sin(4*div(a.time,3))+.5;a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+3*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=25;a.grav=1.1;a.resist=1;a.bounce=.85;a.dt=.0002*div(60,a.fps);a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-\\n2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-a.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<\\nMath.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*a.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?\\na.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<Math.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q4=a.x4;a.q8=a.y4;a.q1=a.aspectx;a.q2=a.aspecty;a.zoom=1;a.warp=0;a.bb=.99*a.bb+.02*a.bass;a.mm=.99*a.mm+.02*a.mid;a.tt=.99*a.tt+.02*a.treb;a.mx=Math.max(Math.max(a.bb,a.mm),a.tt);a.mn=Math.min(Math.min(a.bb,a.mm),a.tt);a.ob_r=div(a.bb-a.mn,a.mx-a.mn);a.ob_b=div(a.mm-a.mn,a.mx-a.mn);a.ob_g=div(a.tt-a.mn,a.mx-a.mn);a.q6=Math.atan2(a.vx4,\\na.vy4);a.q5=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.a=.95*a.a+a.q5;a.s=.9*a.s+a.a;a.q3=.1*a.s;a.t+=div(.1,a.fps);a.t=.00001<Math.abs(above(a.t,2))?a.t-2:a.t;a.q12=-a.t;a.monitor=a.t;a.wave_a=0;\",pixel_eqs_str:\"a.x=.5+(a.x-.5)*a.q1;a.y=.5+(a.y-.5)*a.q2;a.dir=-a.q6+0*Math.asin(1);a.xx=a.q4;a.yy=1-a.q8;a.dx=0;a.dy=0;a.d=sqrt((a.x-a.xx)*(a.x-a.xx)+(a.y-a.yy)*(a.y-a.yy));a.r=.08;a.v=-15*a.q5;a.dx=a.v*Math.sin(a.dir)*(a.r-a.d)*(1-sigmoid(a.d-a.r,200))*a.q2;a.dy=a.v*Math.cos(a.dir)*(a.r-a.d)*(1-sigmoid(a.d-a.r,200))*a.q1;\",warp:\" shader_body { \\n  vec3 tmpvar_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  tmpvar_1 = vec3((((tmpvar_2.x + tmpvar_2.y) + tmpvar_2.z) * 0.33333));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ((texture (sampler_main, clamp (\\n    (uv - ((vec2(0.0, 0.0168) * (tmpvar_1 - 0.35).xy) * (tmpvar_1 - 0.4).xy))\\n  , 0.0, 1.0)).xyz - 0.001) + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 2.0)) + rand_frame.xy)\\n  ) - 0.5) * 0.04).xyz);\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = ((uv - 0.5).yx * aspect.zw);\\n  float tmpvar_2;\\n  float tmpvar_3;\\n  tmpvar_3 = (min (abs(\\n    (tmpvar_1.x / tmpvar_1.y)\\n  ), 1.0) / max (abs(\\n    (tmpvar_1.x / tmpvar_1.y)\\n  ), 1.0));\\n  float tmpvar_4;\\n  tmpvar_4 = (tmpvar_3 * tmpvar_3);\\n  tmpvar_4 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_4) + 0.05368138) * tmpvar_4) - 0.1173503)\\n     * tmpvar_4) + 0.1938925) * tmpvar_4) - 0.3326756)\\n   * tmpvar_4) + 0.9999793) * tmpvar_3);\\n  tmpvar_4 = (tmpvar_4 + (float(\\n    (abs((tmpvar_1.x / tmpvar_1.y)) > 1.0)\\n  ) * (\\n    (tmpvar_4 * -2.0)\\n   + 1.570796)));\\n  tmpvar_2 = (tmpvar_4 * sign((tmpvar_1.x / tmpvar_1.y)));\\n  if ((abs(tmpvar_1.y) > (1e-08 * abs(tmpvar_1.x)))) {\\n    if ((tmpvar_1.y < 0.0)) {\\n      if ((tmpvar_1.x >= 0.0)) {\\n        tmpvar_2 += 3.141593;\\n      } else {\\n        tmpvar_2 = (tmpvar_2 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_2 = (sign(tmpvar_1.x) * 1.570796);\\n  };\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (tmpvar_2 * q11);\\n  tmpvar_5.y = (((0.5 * \\n    log(sqrt(dot (tmpvar_1, tmpvar_1)))\\n  ) - (tmpvar_2 * q11)) + q12);\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = pow (texture (sampler_main, (0.5 + (\\n    (0.5 - abs(((\\n      fract((tmpvar_5 * 0.5))\\n     * 2.0) - 1.0)))\\n   * vec2(0.96, 1.0)))).xyz, vec3(0.618034, 0.618034, 0.618034));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,wave_thick:1,wave_brighten:0,wave_a:.004,wave_scale:.01,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.01,warpscale:100,zoomexp:.24298,zoom:.9901,warp:.01,wave_y:.04,ob_size:0,ob_g:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,rad:.01,tex_ang:.12566,tex_zoom:1.51878,r:.05,a:.1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.x1=.9;a.y1=.5;a.x2=.5;a.y2=.5;a.x3=.5;a.y3=.5;a.x4=.5;a.y4=.5;\",frame_eqs_str:\"a.zoom=1;a.warp=0;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (0.02 * aspect.zw);\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur2, \\n    ((uv + (vec2(1.0, 0.0) * tmpvar_3)) - floor((uv + (vec2(1.0, 0.0) * tmpvar_3))))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    ((uv - (vec2(1.0, 0.0) * tmpvar_3)) - floor((uv - (vec2(1.0, 0.0) * tmpvar_3))))\\n  ).xyz * scale2) + bias2));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur2, \\n    ((uv + (vec2(0.0, 1.0) * tmpvar_3)) - floor((uv + (vec2(0.0, 1.0) * tmpvar_3))))\\n  ).xyz * scale2) + bias2) - ((texture (sampler_blur2, \\n    ((uv - (vec2(0.0, 1.0) * tmpvar_3)) - floor((uv - (vec2(0.0, 1.0) * tmpvar_3))))\\n  ).xyz * scale2) + bias2));\\n  vec3 tmpvar_6;\\n  tmpvar_6 = ((texture (sampler_blur1, uv).xyz * scale1) + bias1);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_4.y;\\n  tmpvar_7.y = tmpvar_5.y;\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_5.x;\\n  tmpvar_8.y = -(tmpvar_4.x);\\n  vec2 tmpvar_9;\\n  tmpvar_9 = ((uv - (tmpvar_7 * vec2(0.01, 0.01))) - (tmpvar_8 * -0.02));\\n  ret_2.y = texture (sampler_fc_main, (tmpvar_9 - floor(tmpvar_9))).y;\\n  ret_2.y = (ret_2.y + ((\\n    (ret_2.y - tmpvar_6.y)\\n   * 0.02) + 0.005));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4.x;\\n  tmpvar_10.y = tmpvar_5.x;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = tmpvar_5.z;\\n  tmpvar_11.y = -(tmpvar_4.z);\\n  my_uv_1 = ((uv - (tmpvar_10 * vec2(0.01, 0.01))) - (tmpvar_11 * -0.02));\\n  ret_2.x = texture (sampler_fc_main, (my_uv_1 - floor(my_uv_1))).x;\\n  ret_2.x = (ret_2.x + ((\\n    (ret_2.x - tmpvar_6.x)\\n   * 0.02) + 0.005));\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = tmpvar_4.z;\\n  tmpvar_12.y = tmpvar_5.z;\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = tmpvar_5.y;\\n  tmpvar_13.y = -(tmpvar_4.y);\\n  my_uv_1 = ((uv - (tmpvar_12 * vec2(0.01, 0.01))) - (tmpvar_13 * -0.02));\\n  ret_2.z = texture (sampler_fc_main, (my_uv_1 - floor(my_uv_1))).z;\\n  ret_2.z = (ret_2.z + ((\\n    (ret_2.z - tmpvar_6.z)\\n   * 0.02) + 0.005));\\n  vec4 tmpvar_14;\\n  tmpvar_14.w = 1.0;\\n  tmpvar_14.xyz = ret_2;\\n  ret = tmpvar_14.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv2_1;\\n  vec3 ret_2;\\n  uv2_1 = (uv + (vec2(1.0, 0.0) * texsize.zw));\\n  float tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(-1.0, 0.0) * texsize.zw));\\n  float tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(0.0, 1.0) * texsize.zw));\\n  float tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(0.0, -1.0) * texsize.zw));\\n  vec3 tmpvar_6;\\n  tmpvar_6.z = 0.14;\\n  tmpvar_6.x = (tmpvar_3 - tmpvar_4);\\n  tmpvar_6.y = (tmpvar_5 - ((\\n    (texture (sampler_main, uv2_1).xyz + (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4))\\n   + \\n    (((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2) * 0.15)\\n  ) + (\\n    ((texture (sampler_blur3, uv2_1).xyz * scale3) + bias3)\\n   * 0.1)).x);\\n  ret_2 = (0.5 + (0.5 * normalize(tmpvar_6)));\\n  vec2 x_7;\\n  x_7 = (ret_2.xy - 0.5);\\n  ret_2 = (ret_2 * clamp ((\\n    sqrt(dot (x_7, x_7))\\n   * 5.0), 0.0, 1.0));\\n  ret_2 = ret_2.xxy;\\n  ret_2 = (ret_2 + 1.15);\\n  ret_2 = (ret_2 * mix (ret_2, (ret_2 * \\n    (((texture (sampler_blur3, uv).xyz * scale3) + bias3) - ((texture (sampler_blur1, uv).xyz * scale1) + bias1))\\n  ), pow (hue_shader.zxy, ret_2)));\\n  ret_2 = (ret_2 * ret_2);\\n  vec4 tmpvar_8;\\n  tmpvar_8.w = 1.0;\\n  tmpvar_8.xyz = ret_2;\\n  ret = tmpvar_8.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1,echo_alpha:.5,wave_thick:1,wave_brighten:0,wrap:0,wave_a:.004,wave_scale:.242,wave_smoothing:0,wave_mystery:-.44,modwavealphastart:1,modwavealphaend:1,warpanimspeed:.397,warpscale:15.099,zoomexp:.65309,zoom:.87866,warp:.04027,wave_y:.04,ob_size:0,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,thickoutline:1,y:.04,rad:.01,tex_ang:.12566,tex_zoom:1.51878,r:0,a:0,g2:0,b2:.01,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,additive:1,scaling:2.0231,smoothing:0,r:0,b:0},init_eqs_str:\"a.tt3=0;a.tt2=0;a.tt1=0;a.d=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.r=1;a.g=0;a.b=1;\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.6+a.d*a.sample*(1-a.sample)*2;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.c_inv_i=0;a.translation_x=0;a.q12=0;a.a_i=0;a.a_r=0;a.q18=0;a.q13=0;a.scale=0;a.q15=0;a.c_inv_r=0;a.angle=0;a.q11=0;a.bcad_r=0;a.b_r=0;a.q16=0;a.bcad_i=0;a.q17=0;a.q14=0;a.translation_y=0;a.translation_v=0;a.b_i=0;a.translation_u=0;a.x1=.9;a.y1=.5;a.x2=.5;a.y2=.5;a.x3=.5;a.y3=.5;a.x4=.5;a.y4=.5;\",frame_eqs_str:\"a.zoom=.998;a.scale=1;a.angle=.02*a.time;a.translation_x=0;a.translation_y=.12;a.a_r=Math.cos(a.angle)*a.scale;a.a_i=Math.sin(a.angle)*a.scale;a.b_r=a.translation_x;a.b_i=a.translation_y;a.scale=1;a.angle=0*Math.sin(.1337*a.time);a.translation_u=0;a.translation_v=-.2;a.q15=Math.cos(a.angle)*a.scale;a.q16=Math.sin(a.angle)*a.scale;a.q17=a.translation_u;a.q18=a.translation_v;a.c_inv_r=div(a.q15,a.q15*a.q15+a.q16*a.q16);a.c_inv_i=div(a.q16,a.q15*a.q15+a.q16*a.q16);a.q11=a.a_r*\\na.c_inv_r-a.a_i*a.c_inv_i;a.q12=a.a_r*a.c_inv_i-a.a_i*a.c_inv_r;a.bcad_r=a.b_r*a.q15-a.b_i*a.q16-(a.a_r*a.q17-a.a_i*a.q18);a.bcad_i=a.b_r*a.q16-a.b_i*a.q15-(a.a_r*a.q18-a.a_i*a.q17);a.q13=a.bcad_r*a.c_inv_r-a.bcad_i*a.c_inv_i;a.q14=a.bcad_r*a.c_inv_i-a.bcad_i*a.c_inv_r;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  float conway_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (((\\n    ((texture (sampler_blur2, (uv + vec2(0.02, 0.0))).xyz * scale2) + bias2)\\n   - \\n    ((texture (sampler_blur2, (uv - vec2(0.02, 0.0))).xyz * scale2) + bias2)\\n  ).y * 1280.0) * texsize.z);\\n  tmpvar_3.y = (((\\n    ((texture (sampler_blur2, (uv + vec2(0.0, 0.02))).xyz * scale2) + bias2)\\n   - \\n    ((texture (sampler_blur2, (uv - vec2(0.0, 0.02))).xyz * scale2) + bias2)\\n  ).y * 1024.0) * texsize.w);\\n  ret_2.y = texture (sampler_pc_main, (uv - (tmpvar_3 * 0.004))).y;\\n  ret_2.y = (ret_2.y + ((\\n    ((ret_2.y - ((texture (sampler_blur1, uv).xyz * scale1) + bias1).y) - 0.1)\\n   * 0.1) + 0.02));\\n  ret_2.z = (texture (sampler_fc_main, (0.5 + (\\n    (uv - 0.5)\\n   * 0.992))).z - 0.004);\\n  conway_1 = (texture (sampler_pw_main, (uv_orig - texsize.zw)).x + texture (sampler_pw_main, (uv_orig + (vec2(0.0, -1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(1.0, -1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(-1.0, 0.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(1.0, 0.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(-1.0, 1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + (vec2(0.0, 1.0) * texsize.zw))).x);\\n  conway_1 = (conway_1 + texture (sampler_pw_main, (uv_orig + texsize.zw)).x);\\n  conway_1 = (conway_1 - fract(conway_1));\\n  float tmpvar_4;\\n  tmpvar_4 = clamp (texture (sampler_pc_main, uv_orig).x, 0.0, 1.0);\\n  ret_2.x = (clamp ((1.0 - \\n    abs((conway_1 - 3.0))\\n  ), 0.0, 1.0) * (1.0 - tmpvar_4));\\n  ret_2.x = (ret_2.x + (clamp (\\n    max ((1.0 - abs((conway_1 - 2.0))), (1.0 - abs((conway_1 - 3.0))))\\n  , 0.0, 1.0) * tmpvar_4));\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ret_2;\\n  ret = tmpvar_5.xyz;\\n }\",comp:\" shader_body { \\n  vec2 moebius_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q11;\\n  tmpvar_3.y = q12;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q17;\\n  tmpvar_4.y = q18;\\n  vec2 tmpvar_5;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - 0.5);\\n  tmpvar_5 = (tmpvar_6 * aspect.xy);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = ((tmpvar_5.x * q15) - (tmpvar_5.y * q16));\\n  tmpvar_7.y = ((tmpvar_5.x * q16) - (tmpvar_5.y * q15));\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (tmpvar_7 + tmpvar_4);\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = ((q13 * tmpvar_8.x) + (q14 * tmpvar_8.y));\\n  tmpvar_9.y = ((q14 * tmpvar_8.x) - (q13 * tmpvar_8.y));\\n  moebius_1 = (((tmpvar_9 / \\n    ((tmpvar_8.x * tmpvar_8.x) + (tmpvar_8.y * tmpvar_8.y))\\n  ) + tmpvar_3) * 0.5);\\n  float tmpvar_10;\\n  tmpvar_10 = sqrt(dot (moebius_1, moebius_1));\\n  moebius_1 = (0.5 + ((\\n    (1.0 - abs(((\\n      fract((moebius_1 * 0.5))\\n     * 2.0) - 1.0)))\\n   - 0.5) * 0.99));\\n  vec3 tmpvar_11;\\n  tmpvar_11 = mix (mix (mix (\\n    mix (mix ((mix (ret_2, vec3(0.2, 0.6, 1.0), vec3(\\n      (texture (sampler_pc_main, moebius_1).y * 2.0)\\n    )) * (vec3(1.0, 1.0, 1.0) - vec3(\\n      ((((texture (sampler_blur1, \\n        (0.5 + (tmpvar_6 * 0.5))\\n      ).xyz * scale1) + bias1).y * 2.0) * tmpvar_10)\\n    ))), vec3(1.0, 1.0, 1.0), texture (sampler_pc_main, moebius_1).xxx), vec3(4.0, 1.0, 0.0), vec3(clamp (((texture (sampler_pc_main, \\n      (0.5 + (tmpvar_6 * 0.2))\\n    ).y * 2.0) * (\\n      (tmpvar_10 * tmpvar_10)\\n     * tmpvar_10)), 0.0, 1.0)))\\n  , vec3(0.1, 0.0, 0.0), vec3(\\n    clamp ((((texture (sampler_blur1, \\n      (0.5 + (tmpvar_6 * 0.2))\\n    ).xyz * scale1) + bias1).x * 12.0), 0.0, 1.0)\\n  )), (vec3(0.5, 0.8, 1.0) * texture (sampler_pc_main, uv).z), vec3((\\n    clamp ((((texture (sampler_blur1, \\n      (0.5 + (tmpvar_6 * 0.2))\\n    ).xyz * scale1) + bias1).x * 4.0), 0.0, 1.0)\\n   * 1.4))), vec3(1.0, 1.0, 1.0), texture (sampler_pc_main, (0.5 + (tmpvar_6 * 0.2))).xxx);\\n  ret_2 = tmpvar_11;\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = tmpvar_11;\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.56,decay:1,echo_zoom:.362,echo_orient:1,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.286,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,fshader:1,zoom:1.004,warp:.19788,sx:.99967,sy:.9999,wave_g:.65,wave_b:.65,ob_size:0,ob_a:1,mv_x:64,mv_y:48,mv_l:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1,r:0,g:.3,b:.75},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.8);\"},{baseVals:{enabled:1,thick:1,r:0,b:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.05*a.bass;a.ma-=3.1415*above(a.mid,1)*.05*a.mid;a.mx+=.0001*Math.cos(a.ma);a.my+=.0001*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.1);\"},{baseVals:{enabled:1,thick:1,g:.5,b:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.mid,1)*.01*a.mid;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0004*Math.cos(a.ma);a.my+=.0004*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.3);\"},{baseVals:{enabled:1,thick:1,r:.4,g:0,b:.6},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,.5)*.02*a.bass;a.ma-=3.1415*above(a.treb,.5)*.02*a.treb;a.mx+=.0008*Math.cos(a.ma);a.my+=.0008*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.2);\"}],init_eqs_str:\"a.du=0;a.q1=0;a.mm=0;a.tt=0;a.mult=0;a.ang2=0;a.dv=0;a.mx=0;a.mn=0;a.bb=0;a.q2=0;a.dist=0;\",frame_eqs_str:\"a.wave_r+=.2*(.6*Math.sin(.98*a.time)+.4*Math.sin(1.047*a.time));a.wave_g+=.2*(.6*Math.sin(.835*a.time)+.4*Math.sin(1.081*a.time));a.wave_b+=.2*(.6*Math.sin(.814*a.time)+.4*Math.sin(1.011*a.time));a.q1=2*a.cx-1+.6*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.q2=2*a.cy-1+.6*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.warp=0;a.zoom=1;a.bb=.99*a.bb+.02*a.bass;a.mm=.99*a.mm+.02*a.mid;a.tt=.99*a.tt+.02*a.treb;a.mx=Math.max(Math.max(a.bb,a.mm),a.tt);a.mn=Math.min(Math.min(a.bb,\\na.mm),a.tt);a.ob_r=div(a.bb-a.mn,a.mx-a.mn);a.ob_b=div(a.mm-a.mn,a.mx-a.mn);a.ob_g=div(a.tt-a.mn,a.mx-a.mn);\",pixel_eqs_str:\"a.du=2*a.x-1-a.q1;a.dv=2*a.y-1-a.q2;a.dist=sqrt(a.du*a.du+a.dv*a.dv);a.ang2=Math.atan2(a.du,a.dv)+.15*a.time;a.mult=.05*Math.sin(.05*a.dist);a.dx=a.mult*Math.sin(2*a.ang2-1.5);a.dy=a.mult*Math.cos(2*a.ang2-1.5);\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  tmpvar_1.xyz = ((texture (sampler_main, clamp (\\n    (uv - (((vec2(0.0, 64.0) * texsize.zw) * dot (\\n      (tmpvar_2.xyz - 0.35)\\n    , vec3(0.32, 0.49, 0.29))) * (dot (tmpvar_2.xyz, vec3(0.32, 0.49, 0.29)) - 0.4)))\\n  , 0.0, 1.0)).xyz - 0.0011) + ((texture (sampler_noise_lq, \\n    (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 1.5)) + rand_frame.xy)\\n  ) - 0.5) * 0.0038).xyz);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (texture (sampler_main, (uv - tmpvar_2)).xyz - texture (sampler_main, (uv + tmpvar_2)).xyz).x;\\n  tmpvar_4.y = (texture (sampler_main, (uv - tmpvar_3)).xyz - texture (sampler_main, (uv + tmpvar_3)).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    ((uv - 0.5) * 2.0)\\n  )) - tmpvar_4);\\n  float tmpvar_5;\\n  tmpvar_5 = clamp ((0.04 / sqrt(\\n    dot (uv1_1, uv1_1)\\n  )), 0.0, 1.0);\\n  uv1_1 = ((0.3 * cos(\\n    (uv1_1 * 12.0)\\n  )) - (9.0 * tmpvar_4));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (tmpvar_5 + ((texture (sampler_main, uv).xyz * 12.0) * vec3(clamp (\\n    (0.04 / sqrt(dot (uv1_1, uv1_1)))\\n  , 0.0, 1.0))));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.56,decay:1,echo_zoom:.362,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.599,wave_smoothing:0,wave_mystery:-.5,modwavealphastart:2,modwavealphaend:2,warpscale:.107,zoomexp:.1584,fshader:1,warp:.01,wave_r:.51,wave_g:.5,ob_size:0,ob_a:1,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:.5,mv_r:0,mv_g:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.cx1=0;a.y3=0;a.y1=0;a.cy1=0;a.x1=0;a.x3=0;a.dir=0;a.r=0;a.x2=0;a.y2=0;\",frame_eqs_str:\"a.ib_r=.3*Math.sin(5*a.time)+.7;a.ib_g=.3*Math.sin(4*a.time)+.3;a.ib_b=.5*Math.sin(4*div(a.time,3))+.5;a.wave_r=1-a.ib_r;a.wave_g=1-a.ib_g;a.wave_b=1-a.ib_b;a.wave_x=.5+.3*Math.sin(3*a.time);a.wave_y=.5+.3*Math.cos(2.187*a.time);\",pixel_eqs_str:\"a.r=div(a.bass,4);a.cx1=.5+.2*Math.sin(.618*a.time);a.cy1=.5+.2*Math.cos(1.618*a.time);a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.dir=a.bass*(a.r*a.r-a.d*a.d)*.3;a.x1=.00001<Math.abs(above(a.d,a.r))?0:Math.sin(a.y-a.cy1)*a.dir;a.y1=.00001<Math.abs(above(a.d,a.r))?0:-Math.sin(a.x-a.cx1)*a.dir;a.cx1=.5+.3*Math.sin(2.618*a.time);a.cy1=.5+.3*Math.cos(3.14*a.time);a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.dir=-a.mid*(a.r*a.r-a.d*a.d)*.3;a.x2=.00001<\\nMath.abs(above(a.d,a.r))?0:Math.sin(a.y-a.cy1)*a.dir;a.y2=.00001<Math.abs(above(a.d,a.r))?0:-Math.sin(a.x-a.cx1)*a.dir;a.cx1=.5+.4*Math.sin(2.618*-a.time);a.cy1=.5+.4*Math.cos(1.14*-a.time);a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.dir=-a.treb*(a.r*a.r-a.d*a.d)*.3;a.x3=.00001<Math.abs(above(a.d,a.r))?0:Math.sin(a.y-a.cy1)*a.dir;a.y3=.00001<Math.abs(above(a.d,a.r))?0:-Math.sin(a.x-a.cx1)*a.dir;a.dx=a.x1+a.x2+a.x3;a.dy=a.y1+a.y2+a.y3;\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 2.0)) + rand_frame.xy);\\n  uv_1 = (uv + ((texture (sampler_noise_lq, tmpvar_2).xy - 0.5) * texsize.zw));\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = bass;\\n  tmpvar_3.y = treb;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = (texture (sampler_main, (uv_1 + (\\n    (texture (sampler_main, (mix (uv_1, uv_orig, vec2(-1.0, -1.0)) + texsize.zw)).xy - 0.4)\\n   * \\n    (-0.004 + (0.04 * clamp ((tmpvar_3 - 1.0), 0.0, 1.0)))\\n  ))).xyz - (0.0008 + (\\n    (texture (sampler_noise_lq, tmpvar_2) - 0.5)\\n   * 0.02)).xyz);\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (texture (sampler_main, (uv - tmpvar_2)).xyz - texture (sampler_main, (uv + tmpvar_2)).xyz).x;\\n  tmpvar_4.y = (texture (sampler_main, (uv - tmpvar_3)).xyz - texture (sampler_main, (uv + tmpvar_3)).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    ((uv - 0.5) * 2.0)\\n  )) - tmpvar_4);\\n  float tmpvar_5;\\n  tmpvar_5 = clamp ((0.04 / sqrt(\\n    dot (uv1_1, uv1_1)\\n  )), 0.0, 1.0);\\n  uv1_1 = ((0.3 * cos(\\n    (uv1_1 * 12.0)\\n  )) - (9.0 * tmpvar_4));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = (tmpvar_5 + ((texture (sampler_main, uv).xyz * 12.0) * vec3(clamp (\\n    (0.04 / sqrt(dot (uv1_1, uv1_1)))\\n  , 0.0, 1.0))));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,decay:1,echo_zoom:1,echo_alpha:.5,echo_orient:3,invert:1,wave_a:.001,warpanimspeed:100,warpscale:.01,zoomexp:1.14947,warp:0,sx:.9901,sy:.9901,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ob_r:1,ob_g:1,ob_b:1,ib_r:1,ib_g:1,ib_b:1,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:100,g:1,b:1,r2:1,b2:1,border_a:0},init_eqs_str:\"a.q4=0;a.q3=0;a.xx=0;a.yy=0;a.radi=0;a.q1=0;\",frame_eqs_str:\"a.a=above(a.q4,1E3-div(9E3,a.fps))&&above(a.q3,1.5)?1:0;a.a2=above(a.q4,1E3)&&above(a.q3,1.5)?1:0;a.xx=(above(a.q4,1E3-div(3,a.fps)),.01*randint(100));a.yy=.00001<Math.abs(above(a.q4,1E3-div(3,a.fps)))?.01*randint(100):.01*randint(70)+.15;a.radi=.00001<Math.abs(above(a.q4,1E3-div(3,a.fps)))?.333*sqrt(a.q3)+.15:.4*sqrt(a.q3);a.rad=a.radi;a.x=a.xx;a.y=a.yy;a.r=.5*Math.sin(1.22*a.q1)+.6;a.g=.45+.45*Math.sin(1.307*a.q1);a.b=.45+.45*Math.sin(1.959*a.q1);a.r2=.5*Math.sin(1.622*a.q1)+\\n.6;a.g2=.45+.45*Math.sin(1.507*a.q1);a.b2=.45+.45*Math.sin(1.6559*a.q1);\"}],waves:[{baseVals:{enabled:1,spectrum:1,thick:1,a:.42},init_eqs_str:\"a.zs=0;a.speed=0;a.q3=0;a.q29=0;a.q30=0;a.q4=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.zs=.00001<Math.abs(below(a.zs,-9.42478))?0:a.zs;a.zs=.00001<Math.abs(above(a.zs,9.42478))?0:a.zs;a.speed=.0025*Math.floor(Math.tan(pow(a.q3,.667)))*(1.5708*Math.atan(a.bass)+1.5708*Math.atan(a.mid)+1.5708*Math.atan(a.treb));a.zs+=a.speed;a.x=.5+(.18+.005*pow(a.q3,1.25))*Math.cos(a.zs*Math.asin(1)*100);a.y=.5+(.18+.005*pow(a.q3,1.25))*Math.sin(a.zs*Math.asin(1)*100);.00001<Math.abs(above(a.mid+a.treb,2*a.bass))?a.r=.3-Math.abs(.33*Math.sin(1-a.q29*a.bass*a.bass)):a.r=.7+Math.abs(.33*\\nMath.sin(1-a.q29*a.bass*a.bass));.00001<Math.abs(above(a.bass+a.treb,2*a.mid))?a.g=.3-Math.abs(.33*Math.sin(1-a.q30*a.mid*a.mid)):a.g=.7+Math.abs(.33*Math.sin(1-a.q30*a.mid*a.mid));.00001<Math.abs(below(a.mid+a.bass,2*a.treb))?a.b=.3-Math.abs(.33*Math.sin(1-a.q3*a.treb*a.treb)):a.b=.7+Math.abs(.33*Math.sin(1-a.q3*a.treb*a.treb));a.a=.175+.0007*a.q4;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.foo_r=0;a.maxs=0;a.n=0;a.ampl=0;a.bd_minbpm=0;a.im=0;a.basstime=0;a.bd_mean=0;a.timediff=0;a.bd_src=0;a.bass_residual=0;a.bd_slot=0;a.bd_b=0;a.bd_oct=0;a.tcos=0;a.stickybit=0;a.bd_arg=0;a.old_treb_flop=0;a.quali=0;a.q12=0;a.foo_b=0;a.bass_flop=0;a.q22=0;a.q21=0;a.volavg2=0;a.q13=0;a.q15=0;a.q29=0;a.dt=0;a.bd_pk=0;a.maxo=0;a.bd_m=0;a.q1=0;a.bd_cos=0;a.q5=0;a.foo_g=0;a.treb_flop=0;a.sample1=0;a.bass_thresh=0;a.difftime=0;a.dec_s=0;a.old_bass_flop=0;a.bd_exc=0;a.dec_xs=0;a.kraft=\\n0;a.diff=0;a.treb_thresh=0;a.bd_ampl=0;a.bd_mid_=0;a.maxind=0;a.edge=0;a.tsin=0;a.re0=0;a.testi=0;a.ec_steps=0;a.creep=0;a.q31=0;a.bpm=0;a.q23=0;a.q24=0;a.pulse=0;a.q11=0;a.bd_bass_=0;a.bd_tab0=0;a.pv_beat=0;a.bass_changed=0;a.show=0;a.mid_thresh=0;a.dec_m=0;a.q4=0;a.im0=0;a.bd_spo=0;a.mid_changed=0;a.entropy=0;a.test0=0;a.bd_nres=0;a.bd_bass=0;a.maxa=0;a.volavg=0;a.feder=0;a.q16=0;a.bd_recsz=0;a.old_mid_flop=0;a.mid_residual=0;a.bit2=0;a.tstart=0;a.bd_omega=0;a.bd_treb_=0;a.beatsin=0;a.bd_tabptr=\\n0;a.bd_treb=0;a.treb_residual=0;a.q19=0;a.beat=0;a.vol=0;a.bd_mid=0;a.chaos=0;a.bd_dat0=0;a.prog=0;a.mid_flop=0;a.re=0;a.bd_t=0;a.beatcos=0;a.q2=0;a.time_st=0;a.q14=0;a.treb_changed=0;a.bd_maxbpm=0;a.bd_qual=0;a.vol2=0;a.q3=0;a.t0=0;a.vol_=0;a.m=0;a.oct=0;a.quad=0;a.q32=0;a.basssum=0;a.creepo=0;a.q28=0;a.bd_sin=0;a.sample2=0;a.q30=0;a.change=0;a.beatct=0;a.q20=0;a.q8=0;a.bd_finc=0;for(var b=a.index=0;2E5>b;b++)a.megabuf[Math.floor(a.index)]=0,a.gmegabuf[Math.floor(a.index)]=0,a.index+=1;a.tstart=\\na.time;a.bd_recsz=8;a.bd_oct=4;a.bd_spo=30;a.bd_finc=pow(2,div(1,a.bd_spo));a.bd_nres=a.bd_oct*a.bd_spo;a.bd_minbpm=20;a.bd_maxbpm=pow(2,a.bd_oct)*a.bd_minbpm;a.bd_dat0=1E5;a.bd_tab0=a.bd_dat0+a.bd_nres*a.bd_recsz*3;a.reg00=a.bd_minbpm;a.reg01=a.bd_maxbpm;a.reg02=a.bd_recsz;a.reg03=a.bd_nres;a.reg05=a.bd_finc;a.reg06=a.bd_dat0;a.reg07=a.bd_mp0;a.time_st=0;a.timediff=1;\",frame_eqs_str:\"a.dt=Math.min(div(1,a.fps),.1);a.dec_m=1-4*a.dt;a.dec_s=1-a.dt;a.dec_xs=1-div(a.dt,6);a.t0=a.time-a.tstart;a.bd_b=a.bass;a.bd_bass_=a.bd_bass_*a.dec_m+(1-a.dec_m)*a.bd_b;a.bd_bass=a.bd_b-a.bd_bass_;a.bd_m=a.mid;a.bd_mid_=a.bd_mid_*a.dec_m+(1-a.dec_m)*a.bd_m;a.bd_mid=a.bd_m-a.bd_mid_;a.bd_t=a.treb;a.bd_treb_=a.bd_treb_*a.dec_m+(1-a.dec_m)*a.bd_t;a.bd_treb=a.bd_t-a.bd_treb_;a.n=0;a.bpm=a.bd_minbpm;for(var b=0;b<a.bd_nres;b++)a.gmegabuf[Math.floor(5E4+a.n*a.bd_recsz)]=0,a.bd_arg=\\ndiv(a.bpm,60)*a.t0*6.283,a.gmegabuf[Math.floor(a.bd_tab0+a.n*a.bd_recsz)]=a.bpm,a.bpm*=a.bd_finc,a.n+=1;for(b=a.bd_src=0;3>b;b++){a.bd_exc=a.bd_bass*(.00001>Math.abs(a.bd_src-0)?1:0)+a.bd_mid*(.00001>Math.abs(a.bd_src-1)?1:0)+a.bd_treb*(.00001>Math.abs(a.bd_src-2)?1:0);for(var c=a.oct=0;c<a.bd_oct;c++){a.bd_slot=0;a.bd_pk=0;for(var d=a.bd_mean=0;d<a.bd_spo;d++){a.bd_tabptr=a.bd_tab0+(a.oct*a.bd_spo+a.bd_slot)*a.bd_recsz;a.n=a.bd_dat0+(a.bd_nres*a.bd_src+a.oct*a.bd_spo+a.bd_slot)*a.bd_recsz;a.bpm=\\na.gmegabuf[Math.floor(a.bd_tabptr)];a.bd_omega=6.283*div(a.bpm,60);a.feder=sqr(a.bd_omega);a.gmegabuf[Math.floor(a.n+4)]=a.gmegabuf[Math.floor(a.n+2)];a.gmegabuf[Math.floor(a.n+1)]*=1-.5*a.dt*sqrt(div(a.bpm,300));a.ec_steps=2*Math.floor(1+div(a.bpm,50));for(var e=0;e<a.ec_steps;e++)a.kraft=a.bd_exc-a.feder*a.gmegabuf[Math.floor(a.n+2)],a.gmegabuf[Math.floor(a.n+1)]+=div(a.kraft*a.dt,a.ec_steps),a.gmegabuf[Math.floor(a.n+2)]+=div(a.gmegabuf[Math.floor(a.n+1)]*a.dt,a.ec_steps);a.beatcos=a.gmegabuf[Math.floor(a.n+\\n1)];a.beatsin=a.gmegabuf[Math.floor(a.n+2)]*a.bd_omega;a.quad=sqrt(pow(a.beatsin,2)+pow(a.beatcos,2));a.gmegabuf[Math.floor(a.n)]=.8*a.gmegabuf[Math.floor(a.n)]+.2*a.quad;a.bd_ampl=a.gmegabuf[Math.floor(a.n)];a.bd_mean+=a.bd_ampl;.00001<Math.abs(a.bd_ampl>a.bd_pk?1:0)?a.bd_pk=a.bd_ampl:0;a.bd_slot+=1}a.bd_slot=0;a.bd_qual=pow(div(a.bd_pk,a.bd_mean)*a.bd_spo-1,1);a.gmegabuf[Math.floor(2*(a.bd_src*a.bd_oct+a.oct))]=a.bd_qual;for(d=0;d<a.bd_spo;d++)a.m=5E4+(a.bd_slot+0*a.oct*a.bd_spo)*a.bd_recsz,a.n=\\na.bd_dat0+(a.bd_nres*a.bd_src+a.oct*a.bd_spo+a.bd_slot)*a.bd_recsz,a.gmegabuf[Math.floor(a.m)]+=div(a.bd_qual*a.gmegabuf[Math.floor(a.n)],12),a.bd_slot+=1;a.oct+=1}a.bd_src+=1}a.bd_slot=0;a.maxind=0;a.bd_pk=0;for(b=a.bd_mean=0;b<a.bd_spo;b++)a.m=5E4+a.bd_slot*a.bd_recsz,a.gmegabuf[Math.floor(a.m)]=pow(a.gmegabuf[Math.floor(a.m)],1),a.bd_mean+=a.gmegabuf[Math.floor(a.m)],.00001<Math.abs(a.gmegabuf[Math.floor(a.m)]>a.bd_pk?1:0)?(a.bd_pk=a.gmegabuf[Math.floor(a.m)],a.maxind=a.bd_slot):0,a.bd_slot+=1;\\na.quali=div(a.bd_pk,a.bd_mean)*a.bd_spo-1;for(b=a.n=0;150>b;b++)a.gmegabuf[Math.floor(50+a.n)]*=0,a.n+=1;a.bd_src=0;a.tsin=0;for(b=a.tcos=0;3>b;b++){for(c=a.oct=0;c<a.bd_oct;c++)a.n=a.bd_dat0+(a.bd_nres*a.bd_src+a.oct*a.bd_spo+a.maxind)*a.bd_recsz,a.bd_tabptr=a.bd_tab0+(a.oct*a.bd_spo+a.maxind)*a.bd_recsz,a.bpm=a.gmegabuf[Math.floor(a.bd_tabptr)],a.im=a.gmegabuf[Math.floor(a.n+1)],a.re=6.283*div(a.gmegabuf[Math.floor(a.n+2)]*a.bpm,60),a.bd_cos=Math.cos(6.28*div(a.bpm,60)*a.time),a.bd_sin=-Math.sin(6.28*\\ndiv(a.bpm,60)*a.time),a.re0=a.re*a.bd_cos+a.im*a.bd_sin,a.im0=-a.re*a.bd_sin+a.im*a.bd_cos,a.bd_qual=a.gmegabuf[Math.floor(2*(a.bd_src*a.bd_oct+a.oct))],a.gmegabuf[Math.floor(50+4*a.oct)]+=a.bd_qual*a.im0,a.gmegabuf[Math.floor(4*a.oct+51)]+=a.bd_qual*a.re0,a.gmegabuf[Math.floor(4*a.oct+52)]+=a.bd_qual,a.gmegabuf[100]+=a.bd_qual*a.im0,a.gmegabuf[101]+=a.bd_qual*a.re0,a.re=a.bd_cos*a.re0+a.bd_sin*a.im0,a.im=-a.bd_cos*a.im0+a.bd_sin*a.re0,a.tsin+=5*a.im,a.oct+=1;a.bd_src+=1}a.q30=a.tsin;a.vol=a.bass_att+\\na.mid_att+a.treb_att;a.vol_=.95*a.vol_+.05*a.vol;a.q29=a.vol-a.vol_;a.bd_src=0;a.maxo=0;a.maxs=0;for(b=a.maxa=0;3>b;b++){a.oct=1;for(c=0;c<a.bd_oct-1;c++)a.n=a.bd_dat0+(a.bd_nres*a.bd_src+a.oct*a.bd_spo+a.maxind)*a.bd_recsz,a.bd_qual=a.gmegabuf[Math.floor(2*(a.bd_src*a.bd_oct+a.oct))],a.ampl=div(a.bd_qual,1+a.oct*div(a.maxind,a.bd_oct)*0),.00001<Math.abs(a.ampl>a.maxa?1:0)?(a.maxa=a.ampl,a.maxs=a.bd_src,a.maxo=a.oct):0,a.oct+=1;a.bd_src+=1}a.q28=pow(4*a.bd_qual,1.5);a.q31=a.q28;.00001<Math.abs(above(a.q28,\\n50))?a.q28=pow(5*Math.sin(.5*a.bd_qual),2):0;a.testi=a.bd_nres*a.maxs+a.bd_spo*a.maxo+a.maxind;.00001<Math.abs(bor(a.beat&&.00001>Math.abs(a.prog-0)?1:0,2>Math.abs(a.testi-a.test0)?1:0))?a.test0=a.testi:0;a.n=a.bd_dat0+a.test0*a.bd_recsz;a.creep=a.gmegabuf[Math.floor(a.n+1)];a.beat=(0<a.creep?1:0)*(0>a.creepo?1:0);a.beatct=.00001<Math.abs(a.change)?0:a.beatct+a.beat;a.prog=mod(a.prog+a.beat,8);a.creepo=a.creep;a.show=.00001<Math.abs(a.beat)?1:.5*a.show;a.q4=1E3*div(a.gmegabuf[Math.floor(a.n+1)],a.gmegabuf[Math.floor(a.n)]);\\na.q2=a.prog;.00001<Math.abs(a.beat)?(a.timediff=a.time-a.time_st,a.time_st=a.time):a.timediff=a.timediff;.00001<Math.abs(below(a.timediff,.0625))?a.timediff=.0625:a.timediff=a.timediff;a.q8=a.timediff;a.monitor=a.q8;a.q24=a.bd_maxbpm;a.old_bass_flop=a.bass_flop;a.old_treb_flop=a.treb_flop;a.old_mid_flop=a.mid_flop;a.chaos=.9+.1*Math.sin(a.pulse);a.entropy=.00001<Math.abs(equal(a.pulse,-20))?1+a.bass_flop+a.treb_flop+a.mid_flop+.002*a.q4:a.entropy;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-\\nabove(a.bass_att,a.bass_thresh))*((a.bass_thresh-1.6)*a.chaos+1.6);a.bass_flop=Math.abs(a.bass_flop-equal(a.bass_thresh,2));a.treb_thresh=2*above(a.treb_att,a.treb_thresh)+(1-above(a.treb_att,a.treb_thresh))*((a.treb_thresh-1.6)*a.chaos+1.6);a.treb_flop=Math.abs(a.treb_flop-equal(a.treb_thresh,2));a.mid_thresh=2*above(a.mid_att,a.mid_thresh)+(1-above(a.mid_att,a.mid_thresh))*((a.mid_thresh-1.6)*a.chaos+1.6);a.mid_flop=Math.abs(a.mid_flop-equal(a.mid_thresh,2));a.bass_changed=bnot(equal(a.old_bass_flop,\\na.bass_flop));a.mid_changed=bnot(equal(a.old_mid_flop,a.mid_flop));a.treb_changed=bnot(equal(a.old_treb_flop,a.treb_flop));a.bass_residual=a.bass_changed*Math.sin(1.5*a.pulse)+bnot(a.bass_changed)*a.bass_residual;a.treb_residual=a.treb_changed*Math.sin(1.5*a.pulse)+bnot(a.treb_changed)*a.treb_residual;a.mid_residual=a.mid_changed*Math.sin(1.5*a.pulse)+bnot(a.mid_changed)*a.mid_residual;a.pulse=.00001<Math.abs(above(Math.abs(a.pulse),20))?-20:a.pulse+.018*(a.bass_thresh+a.mid_thresh+a.treb_thresh);\\na.q11=a.mid_residual;a.q12=a.bass_residual;a.q13=a.treb_residual;a.q14=Math.sin(a.pulse);a.q15=Math.cos(a.pulse*(.5+.1*a.entropy));a.q16=Math.sin(a.pulse*(.5+pow(.25,a.entropy)));a.ob_r=Math.sin(1.0785*a.time+a.q16);a.ob_b=Math.sin(.8445*a.time+a.q15);a.ob_g=Math.sin(.413*a.time+a.q14);a.ib_r=Math.cos(.6*a.time+.1*a.q11);a.ib_b=Math.cos(1.4055*a.time+.1*a.q12);a.ib_g=Math.cos(.833*a.time+.1*a.q13);a.ib_size=.05+.03*a.q12;a.ob_size=.03+.02*Math.sin(2.321*a.time+.2*a.q12);a.ob_a=.6+.4*a.q13;a.ib_a=\\n.9+.1*Math.sin(.3*a.q12+a.q14+.5*a.q11);a.rot=0;a.basstime+=div(3*a.q29,a.fps);a.q1=a.basstime;a.basstime=.00001<Math.abs(below(a.basstime,-1E4))?0:a.basstime;a.basstime=.00001<Math.abs(above(a.basstime,1E4))?0:a.basstime;a.basstime+=div(.75*a.bass_att,a.fps);a.vol2=pow(a.bass+a.mid+a.treb,2);a.basssum=a.vol2;a.stickybit=mod(a.time,2);a.volavg+=a.vol2*equal(a.stickybit,1);a.sample1+=equal(a.stickybit,1);a.volavg2+=a.vol2*equal(a.stickybit,0);a.sample2+=equal(a.stickybit,0);a.edge=bnot(equal(a.bit2,\\na.stickybit));a.volavg-=a.volavg*a.edge*a.stickybit;a.volavg2-=a.volavg2*a.edge*equal(a.stickybit,0);a.sample1-=a.sample1*a.edge*a.stickybit;a.sample2-=a.sample2*a.edge*equal(a.stickybit,0);a.diff=.00001<Math.abs(equal(a.stickybit,1))?div(a.basssum,div(a.volavg2,a.sample2)):0;a.diff=.00001<Math.abs(equal(a.stickybit,0))?div(a.basssum,div(a.volavg,a.sample1)):a.diff;a.q3=a.diff;a.pv_beat=.2*a.pv_beat*sqrt(div(a.fps,30))+div(.0001*(a.bass_att*a.bass_att+a.mid_att*a.mid_att+a.treb_att*a.treb_att)*a.q4,\\nsqrt(a.q8));a.q32=a.pv_beat;a.monitor=a.q32;a.bit2=mod(a.time,2);a.difftime+=.03*a.diff;a.q2=a.difftime;a.warp=.005*(a.bass*a.bass+a.mid*a.mid+a.treb*a.treb);a.foo_r=Math.sin(.34313*a.q1);a.foo_g=Math.sin(.3675*a.q1);a.foo_b=Math.sin(.55095*a.q1);.00001<Math.abs(above(a.foo_r,a.foo_g)&&above(a.foo_r,a.foo_b)?1:0)?a.q23=1:a.q23=0;.00001<Math.abs(above(a.foo_g,a.foo_r)&&above(a.foo_g,a.foo_b)?1:0)?a.q21=1:a.q21=0;.00001<Math.abs(above(a.foo_b,a.foo_r)&&above(a.foo_b,a.foo_g)?1:0)?a.q22=1:a.q22=0;.00001<\\nMath.abs(equal(a.q11+a.q12+a.q13,0))?a.q22=1:0;a.q5=div(30,a.fps);a.q19=50;a.q20=1;.00001<Math.abs(above(a.q31,14))?a.q19=100:a.q19=a.q19;.00001<Math.abs(above(a.q31,50))?a.q19=500:a.q19=a.q19;.00001<Math.abs(above(a.q3,2))?a.q19=.5+10*a.q8:a.q19=a.q19;\",pixel_eqs_str:\".00001<Math.abs(a.q32>1.2*sqrt(a.bass_att)?1:0)?(a.rot=.00001<Math.abs(above(a.q14,0))?.2*a.rad*a.q15:Math.tan(.001*a.rad*a.q4),a.zoom=.00001<Math.abs(above(a.q12,1.5))?a.zoom+(1-a.zoom)*a.rot*Math.cos(3.14*a.rad*a.q12):.00001<Math.abs(above(a.q13,2)*above(a.x,.5+.5*a.q15))?a.zoom+(1-a.zoom)*Math.sin(a.q11*a.rot*3.14):a.zoom+(1-a.zoom)*Math.cos(3*a.rad*a.q16),a.dx=above(a.q11,0)*Math.sin(.5*a.rad*a.q12),a.dy=above(a.q13,0)*Math.sin(.5*a.rad*a.q13)):(a.zoom=a.q19,a.zoomexp=a.q20,\\na.rot=0);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_pw_main, uv_orig);\\n  ret_1 = ((texture (sampler_blur1, tmpvar_2.xy).xyz * scale1) + bias1);\\n  vec3 tmpvar_3;\\n  tmpvar_3 = vec3((((1.0 - ret_1.x) * (1.0 - ret_1.y)) * (1.0 - ret_1.z)));\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (((texsize.zw * q5) * -3.0) * ((\\n    pow (tmpvar_3, vec3(0.333, 0.333, 0.333))\\n   + \\n    ((((0.5 * tmpvar_3) * (\\n      (texture (sampler_blur2, ret_1.xx).xyz * scale2)\\n     + bias2)) * ((texture (sampler_blur2, ret_1.yy).xyz * scale2) + bias2)) * ((texture (sampler_blur2, ret_1.zz).xyz * scale2) + bias2))\\n  ) + (\\n    ((0.25 * ((texture (sampler_blur3, ret_1.xx).xyz * scale3) + bias3)) * ((texture (sampler_blur3, ret_1.yy).xyz * scale3) + bias3))\\n   * \\n    ((texture (sampler_blur3, ret_1.xx).xyz * scale3) + bias3)\\n  )).xy);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur1, \\n    (uv_orig + (vec2(1.0, 0.0) * tmpvar_4))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_orig - (vec2(1.0, 0.0) * tmpvar_4))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (((texture (sampler_blur1, \\n    (uv_orig + (vec2(0.0, 1.0) * tmpvar_4))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_orig - (vec2(0.0, 1.0) * tmpvar_4))\\n  ).xyz * scale1) + bias1));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_5.x;\\n  tmpvar_7.y = tmpvar_6.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_5.y;\\n  tmpvar_8.y = tmpvar_6.y;\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_5.z;\\n  tmpvar_9.y = tmpvar_6.z;\\n  vec2 tmpvar_10;\\n  tmpvar_10 = (uv_orig + ((\\n    (((tmpvar_7 * q21) + (tmpvar_8 * q22)) + (tmpvar_9 * q23))\\n   * texsize.zw) * 6.0));\\n  ret_1 = ((texture (sampler_main, tmpvar_10).xyz + (\\n    (texture (sampler_main, uv).xyz - ((texture (sampler_blur1, uv).xyz * scale1) + bias1))\\n   * 0.1)) - ((0.00015 * q28) * (\\n    ((((\\n      (texture (sampler_main, tmpvar_10).x * texture (sampler_main, tmpvar_10).y)\\n     * texture (sampler_main, tmpvar_10).z) / (2.0 * q8)) + ((\\n      sqrt(treb)\\n     * texture (sampler_main, tmpvar_10).x) * texture (sampler_main, tmpvar_10).y)) + ((sqrt(mid) * texture (sampler_main, tmpvar_10).x) * texture (sampler_main, tmpvar_10).z))\\n   + \\n    ((sqrt(bass) * texture (sampler_main, tmpvar_10).y) * texture (sampler_main, tmpvar_10).z)\\n  )));\\n  ret_1.x = mix (ret_1.x, (1.01 * texture (sampler_pc_main, tmpvar_10).x), (bass_att * 0.05));\\n  ret_1.y = mix (ret_1.y, (1.01 * texture (sampler_pc_main, tmpvar_10).y), (mid_att * 0.05));\\n  ret_1.z = mix (ret_1.z, (1.01 * texture (sampler_pc_main, tmpvar_10).z), (treb_att * 0.05));\\n  ret_1 = (ret_1 + ((\\n    (pow (q3, 1.2) - 0.25)\\n   * 0.00667) * tmpvar_2).xyz);\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 0.0;\\n  tmpvar_11.xyz = ret_1;\\n  vec3 tmpvar_12;\\n  tmpvar_12 = mix (tmpvar_11, texture (sampler_pc_main, uv), vec4(float((\\n    (q32 * sqrt(((\\n      ((1.0 - ret_1.x) * (1.0 - ret_1.y))\\n     + \\n      ((1.0 - ret_1.x) * (1.0 - ret_1.z))\\n    ) + (\\n      (1.0 - ret_1.y)\\n     * \\n      (1.0 - ret_1.z)\\n    ))))\\n   > 1.333)))).xyz;\\n  ret_1 = tmpvar_12;\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 1.0;\\n  tmpvar_13.xyz = tmpvar_12;\\n  ret = tmpvar_13.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = mix (texture (sampler_main, uv).xyz, texture (sampler_main, ((0.5 - uv) + 0.5)).xyz, vec3(0.5, 0.5, 0.5));\\n  ret_1 = (1.0 - ((ret_1 * \\n    (1.0 - ret_1)\\n  ) * 4.0));\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:.925,echo_zoom:1.007,echo_orient:3,wave_brighten:0,brighten:1,darken:1,solarize:1,wave_a:.001,wave_scale:.01,wave_smoothing:0,modwavealphastart:1,modwavealphaend:1,warpanimspeed:1.459,warpscale:2.007,fshader:1,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:5,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,textured:1,rad:.789,ang:.6283,tex_zoom:1.17257,r:0,g:1,border_a:0},init_eqs_str:\"a.an=0;a.vx=0;a.vy=0;\",frame_eqs_str:\"a.rad=.65+.1*a.bass;a.an=.99*a.an+.1*(a.bass-a.treb);a.ang=.1*a.an+.6;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:495,sep:4,spectrum:1,thick:1,additive:1,scaling:100,smoothing:1,r:0,g:.04,b:0,a:.99},init_eqs_str:\"a.my_z=0;a.d=0;a.n=0;a.y3=0;a.z2=0;a.y1=0;a.w=0;a.t5=0;a.w2=0;a.t1=0;a.x1=0;a.q6=0;a.zoom=0;a.p=0;a.q1=0;a.q5=0;a.t8=0;a.z3=0;a.w3=0;a.t3=0;a.my_x=0;a.x3=0;a.t6=0;a.my_y=0;a.pi=0;a.q4=0;a.t7=0;a.rd=0;a.w1=0;a.x2=0;a.t2=0;a.l=0;a.y2=0;a.q2=0;a.z1=0;a.q3=0;a.t4=0;a.t2=0;a.t3=0;a.t4=0;a.ab=1;\",frame_eqs_str:\"a.t1=a.q1;a.t2=a.q2;a.t3=a.q3;a.t4=a.q4;a.t5=a.q5;a.t6=a.q6;a.t8=.07;a.t7=1;\",point_eqs_str:\"a.t7=-a.t7;a.pi=Math.asin(1);a.n=180;a.rd=.075;a.my_x=.5*Math.sin(a.sample*a.pi*4+(a.t7+1)*a.t8)+Math.cos(a.sample*a.pi*a.n)*a.rd*Math.sin(a.sample*a.pi*4+(a.t7+1)*a.t8);a.my_y=.5*Math.cos(a.sample*a.pi*4+(a.t7+1)*a.t8)+Math.cos(a.sample*a.pi*a.n)*a.rd*Math.cos(a.sample*a.pi*4+(a.t7+1)*a.t8);a.my_z=Math.sin(a.sample*a.pi*a.n)*a.rd;a.d=1.4;a.zoom=.65;a.w1=a.q2;a.w2=a.q3;a.w3=a.q4;a.x1=Math.cos(a.w1)*a.my_x+Math.sin(a.w1)*a.my_y;a.y1=-Math.sin(a.w1)*a.my_x+Math.cos(a.w1)*a.my_y;\\na.z1=a.my_z;a.x2=Math.cos(a.w2)*a.x1+Math.sin(a.w2)*a.z1;a.z2=-Math.sin(a.w2)*a.x1+Math.cos(a.w2)*a.z1;a.y2=a.y1;a.y3=Math.cos(a.w3)*a.y2+Math.sin(a.w3)*a.z2;a.z3=-Math.sin(a.w3)*a.y2+Math.cos(a.w3)*a.z2;a.x3=a.x2;a.l=sqrt(a.x3*a.x3+a.y3*a.y3);a.w=Math.atan2(a.x3,a.y3);a.p=Math.tan(Math.asin(1)+Math.atan2(a.d+a.z3,a.l));a.d=sqrt(a.x3*a.x3+a.y3*a.y3+(a.z3+a.d)*(a.z3+a.d));a.my_x=a.zoom*Math.sin(a.w)*a.p;a.my_y=a.zoom*Math.cos(a.w)*a.p;a.x=.5+a.my_x;a.y=.5+a.my_y;a.b=-a.z3+.5;a.b=.5*Math.min(1,Math.max(0,\\na.b));a.r=1-2*a.b;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q29=0;a.q1=0;a.dec_med=0;a.rott=0;a.is_beat=0;a.q31=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.p3=0;a.q3=0;a.t0=0;a.q28=0;a.q30=0;a.q20=0;a.p4=0;a.step=0;a.step=0;\",frame_eqs_str:\"a.dec_med=pow(.7,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,-2+a.avg+a.peak)*above(a.time,a.t0+.1);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,16);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass_att+\\na.mid_att+a.treb_att;a.q27=a.index+1;a.q28=a.index2;a.q29=2*(mod(a.index,2)-.5);a.k1=a.is_beat*equal(mod(a.index,2),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,8);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.step+=a.q24;a.p3=a.p3*a.dec_slow+(1-a.dec_slow)*a.step;a.q30=a.step;a.p4=a.dec_slow*a.p4+(1-a.dec_slow)*a.q27;a.q31=a.p4;a.q12=a.time-a.t0;a.monitor=a.q12;a.zoom=1;a.rot=-0;a.dx=0;\",pixel_eqs_str:\"a.zoom=1.3;\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 uv6_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_4;\\n  tmpvar_4 = (((q29 * 2.0) * sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  )) + (rand_frame * 64.0)).x;\\n  uv_1 = (uv + (clamp (\\n    ((sin(tmpvar_4) / cos(tmpvar_4)) * normalize(tmpvar_3))\\n  , vec2(-2.0, -2.0), vec2(2.0, 2.0)) / 20.0));\\n  uv6_2 = (0.4 * sin((tmpvar_3 * 22.0)));\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = (((q24 * \\n    (((texture (sampler_main, uv_1).xyz - (\\n      ((texture (sampler_blur1, fract(uv_1)).xyz * scale1) + bias1)\\n     * 0.04)) + (0.15 * (vec3(\\n      (0.1 / sqrt(dot (uv6_2, uv6_2)))\\n    ) * roam_cos.xyz))) - 0.02)\\n  ) * 0.98) + ((1.0 - q24) * texture (sampler_main, uv_orig).xyz));\\n  ret = tmpvar_5.xyz;\\n }\",comp:\"uniform sampler2D sampler_rand00;\\n shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1 = texture (sampler_main, uv);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ((texture (sampler_rand00, (0.4 + \\n    (0.6 * tmpvar_1.xy)\\n  )) * tmpvar_1.z) * 3.0).xyz;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:2,gammaadj:2.7,wave_mode:1,modwavealphabyvolume:1,wave_a:2.707,wave_scale:1.025,wave_smoothing:.1,modwavealphastart:.77,modwavealphaend:1.01,warpscale:1.331,zoom:1.014,warp:.21786,wave_r:.65,wave_g:.65,wave_b:.65,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"\",frame_eqs_str:\"a.wave_r+=.35*(.6*Math.sin(3.98*a.time)+.4*Math.sin(11.047*a.time));a.wave_g+=.35*(.6*Math.sin(.835*a.time)+.4*Math.sin(1.081*a.time));a.wave_b+=.35*(.6*Math.sin(.814*a.time)+.4*Math.sin(1.011*a.time));a.cx+=.11*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.11*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.005*(.6*Math.sin(.173*a.time)+.4*Math.sin(.223*a.time));a.decay-=.01*equal(mod(a.frame,20),0);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_main, (uv + (\\n    (texture (sampler_main, (mix (uv, uv_orig, vec2(-1.0, -1.0)) + texsize.zw)).xy - 0.37)\\n   * 0.01))).xyz - 0.004);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1 = texture (sampler_main, uv);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = mix (vec3(dot (tmpvar_1.xyz, vec3(0.3333, 0.3333, 0.3333))), tmpvar_1.xyz, vec3(3.0, 3.0, 3.0));\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.9,echo_zoom:1.16936,wave_mode:7,modwavealphabyvolume:1,wave_a:0,wave_scale:1.015009,wave_smoothing:.522,modwavealphastart:.83,modwavealphaend:1.31,warpscale:3.138,zoom:1.009006,warp:536e-6,wave_r:.5,wave_g:.5,wave_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dx_residual=0;a.dy_residual=0;a.bass_thresh=0;\",frame_eqs_str:\"a.wave_r=.85+.25*Math.sin(.437*a.time+1);a.wave_g=.85+.25*Math.sin(.544*a.time+2);a.wave_b=.85+.25*Math.sin(.751*a.time+3);a.rot+=.01*(.6*Math.sin(.381*a.time)+.4*Math.sin(.579*a.time));a.cx+=.21*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.cy+=.21*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.dx+=.003*(.6*Math.sin(.234*a.time)+.4*Math.sin(.277*a.time));a.dy+=.003*(.6*Math.sin(.284*a.time)+.4*Math.sin(.247*a.time));a.decay-=.01*equal(mod(a.frame,6),0);a.dx+=\\na.dx_residual;a.dy+=a.dy_residual;a.bass_thresh=2*above(a.bass_att,a.bass_thresh)+(1-above(a.bass_att,a.bass_thresh))*(.96*(a.bass_thresh-1.3)+1.3);a.dx_residual=.016*equal(a.bass_thresh,2.13)*Math.sin(7*a.time)+(1-equal(a.bass_thresh,2.13))*a.dx_residual;a.dy_residual=.012*equal(a.bass_thresh,2.13)*Math.sin(9*a.time)+(1-equal(a.bass_thresh,2.13))*a.dy_residual;a.wave_x-=7*a.dx_residual;a.wave_y-=7*a.dy_residual;a.wave_mystery=.03*a.time;a.zoom+=.005*(.6*Math.sin(.1934*a.time+3)+.4*Math.sin(.307*\\na.time+9));a.zoom+=.4*Math.max(0,a.bass_att-1.1);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = texture (sampler_main, uv).xyz;\\n  ret_1 = (ret_1 + ((ret_1 - \\n    ((texture (sampler_blur2, uv).xyz * scale2) + bias2)\\n  ) * 0.3));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 122.0) * clamp (\\n    (treb_att - 1.0)\\n  , 0.0, 1.0)));\\n  ret_1 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.2, 0.2, 0.2));\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = (texture (sampler_main, uv).xyz + ((\\n    (texture (sampler_blur1, uv).xyz * scale1)\\n   + bias1) * 0.4));\\n  vec3 tmpvar_2;\\n  tmpvar_2 = pow (ret_1, vec3(0.5, 0.8, 1.7));\\n  ret_1 = tmpvar_2;\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = tmpvar_2;\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,wave_mode:6,additivewave:1,wave_dots:1,wave_thick:1,wave_brighten:0,wave_a:100,wave_scale:3.63,wave_smoothing:.9,wave_mystery:-.3,modwavealphastart:1.15,modwavealphaend:1.55,warpscale:1.331,zoom:.96971,warp:0,wave_r:.6,wave_g:.6,wave_b:.6,mv_x:64,mv_y:48,mv_l:0,mv_r:.35,mv_g:.35,mv_b:.35,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.decay_rate=0;a.q6=0;a.rot_sum=0;a.q1=0;a.q5=0;a.prev_beat=0;a.is_beat=0;a.min_att=0;a.beat=0;a.decay_to=0;a.rot_sim=0;a.q2=0;a.q3=0;a.beat_level=0;a.rot_sum=0;a.q2=.07+.00004*randint(1E3)+.00003*randint(1E3);a.q3=1.035+.06*(randint(1E3)+randint(1E3)+randint(1E3))*.000333;\",frame_eqs_str:\"a.wave_r+=.5*(.6*Math.sin(1.98*a.time)+.4*Math.sin(3.047*a.time));a.wave_g+=.5*(.6*Math.sin(2.835*a.time)+.4*Math.sin(2.081*a.time));a.wave_b+=.5*(.6*Math.sin(3.814*a.time)+.4*Math.sin(1.011*a.time));a.cx=.5;a.cy=.5;a.rot=a.q2;a.zoom=a.zoom-1+a.q3;a.rot_sum+=a.rot;a.q1=-a.rot_sum;a.q5=Math.cos(a.rot_sum);a.q6=Math.sin(a.rot_sim);a.monitor=a.q2;a.min_att=2.5;a.decay_to=.8;a.decay_rate=pow(.999,a.fps);a.beat=div(a.bass,Math.max(a.min_att,a.bass_att));a.beat=Math.max(a.beat,div(a.mid,\\nMath.max(a.min_att,a.mid_att)));a.beat=Math.max(a.beat,div(a.treb,Math.max(a.min_att,a.treb_att)));a.beat=Math.max(a.beat,(a.prev_beat-a.decay_to)*a.decay_rate+a.decay_to);a.beat_level=24*(a.beat-a.prev_beat-.02);a.is_beat=above(a.beat_level,.5);a.prev_beat=a.beat;a.wave_a=a.beat_level;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_pw_main, uv).xyz - 0.004);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 uv2_2;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * (min (aspect.x, aspect.y) * 0.8));\\n  uv_1 = (uv_1 * aspect.xy);\\n  float tmpvar_3;\\n  tmpvar_3 = sin(q1);\\n  float tmpvar_4;\\n  tmpvar_4 = cos(q1);\\n  uv2_2.x = ((uv_1.x * tmpvar_4) - (uv_1.y * tmpvar_3));\\n  uv2_2.y = ((uv_1.x * tmpvar_3) + (uv_1.y * tmpvar_4));\\n  uv2_2 = (uv2_2 * aspect.zw);\\n  uv2_2 = (uv2_2 + 0.5);\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = (abs((\\n    (texture (sampler_main, uv2_2).xyz * 2.65)\\n   + \\n    (((texture (sampler_blur1, uv2_2).xyz * scale1) + bias1) * -2.0)\\n  )) * 1.5);\\n  ret = tmpvar_5.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.9,echo_zoom:1.16936,wave_mode:5,additivewave:1,wave_a:0,wave_scale:.899719,wave_smoothing:.63,wave_mystery:1,modwavealphastart:2,modwavealphaend:2,warpscale:2.593743,zoom:1.00496,warp:.278033,sx:.999666,sy:.9999,wave_r:.65,wave_g:.65,wave_b:.65,mv_x:0,mv_y:48,mv_dx:-.941273,mv_dy:.426319,mv_l:5,mv_r:.315997,mv_g:.078173,mv_b:.941976,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.du=0;a.q1=0;a.rg=0;a.q9=0;a.mult=0;a.ang2=0;a.dv=0;a.q4=0;a.q2=0;a.dist=0;a.q3=0;a.rg=0;\",frame_eqs_str:\"a.wave_r+=.35*(.6*Math.sin(.98*a.time)+.4*Math.sin(1.047*a.time));a.wave_g+=.35*(.6*Math.sin(.835*a.time)+.4*Math.sin(1.081*a.time));a.wave_b+=.35*(.6*Math.sin(.814*a.time)+.4*Math.sin(1.011*a.time));a.q1=2*a.cx-1+.52*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.q2=2*a.cy-1+.52*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));a.q3=2*a.cx-1+.52*(.6*Math.sin(.174*-a.time)+.4*Math.sin(.364*a.time));a.q4=2*a.cy-1+.52*(.6*Math.sin(.234*a.time)+.4*Math.sin(.271*-a.time));\\na.decay-=.01*equal(mod(a.frame,5),0);a.rg=Math.max(.95*a.rg,.3+.5*Math.min(2,1.3*Math.max(0,a.mid_att-1)));a.q9=a.rg;\",pixel_eqs_str:\"a.du=2*a.x-1-a.q1;a.dv=2*a.y-1-a.q2;a.dist=sqrt(a.du*a.du+a.dv*a.dv);a.ang2=Math.atan2(a.du,a.dv);a.mult=div(.008,a.dist+.4);a.dx=a.mult*Math.sin(a.ang2-1.5);a.dy=a.mult*Math.cos(a.ang2-1.5);a.du=2*a.x-1-a.q3;a.dv=2*a.y-1-a.q4;a.dist=sqrt(a.du*a.du+a.dv*a.dv);a.ang2=Math.atan2(a.du,a.dv);a.mult=div(.008,a.dist+.4);a.dx+=a.mult*Math.sin(a.ang2+1.5);a.dy+=a.mult*Math.cos(a.ang2+1.5);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = mix (uv_orig, uv, vec2(q9));\\n  ret_1 = texture (sampler_main, tmpvar_2).xyz;\\n  ret_1 = (ret_1 + ((ret_1 - \\n    ((texture (sampler_blur1, tmpvar_2).xyz * scale1) + bias1)\\n  ) * 0.3));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 122.0) * clamp (\\n    (treb_att - 1.0)\\n  , 0.0, 1.0)));\\n  ret_1 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.2, 0.2, 0.2));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_1;\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (0.3 + (0.4 * vec3(dot (texture (sampler_main, uv).xyz, vec3(0.32, 0.49, 0.29)))));\\n  ret = tmpvar_1.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.9,echo_zoom:1.169,echo_orient:1,wave_mode:5,additivewave:1,wave_a:0,wave_scale:.9,wave_smoothing:.63,wave_mystery:1,modwavealphastart:2,modwavealphaend:2,warpscale:1.331,zoom:1.004,warp:.19788,sx:.99967,sy:.9999,wave_r:.65,wave_g:.65,wave_b:.65,mv_x:0,mv_y:48,mv_dx:-.941,mv_dy:.426,mv_l:5,mv_r:.316,mv_g:.078,mv_b:.942,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.du=0;a.q1=0;a.dv=0;a.q2=0;a.dist=0;a.ang2=0;a.mult=0;\",frame_eqs_str:\"a.wave_r+=.2*(.6*Math.sin(.98*a.time)+.4*Math.sin(1.047*a.time));a.wave_g+=.2*(.6*Math.sin(.835*a.time)+.4*Math.sin(1.081*a.time));a.wave_b+=.2*(.6*Math.sin(.814*a.time)+.4*Math.sin(1.011*a.time));a.q1=2*a.cx-1+.6*(.6*Math.sin(.374*a.time)+.4*Math.sin(.294*a.time));a.q2=2*a.cy-1+.6*(.6*Math.sin(.393*a.time)+.4*Math.sin(.223*a.time));\",pixel_eqs_str:\"a.du=2*a.x-1-a.q1;a.dv=2*a.y-1-a.q2;a.dist=sqrt(a.du*a.du+a.dv*a.dv);a.ang2=Math.atan2(a.du,a.dv)+.15*a.time;a.mult=.65*Math.sin(.05*a.dist);a.dx=a.mult*Math.sin(2*a.ang2-1.5);a.dy=a.mult*Math.cos(2*a.ang2-1.5);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = (tmpvar_2.xyz + ((tmpvar_2.xyz - \\n    ((texture (sampler_blur2, uv).xyz * scale2) + bias2)\\n  ) * 0.3));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 122.0) * clamp (\\n    (treb_att - 1.0)\\n  , 0.0, 1.0)));\\n  vec3 tmpvar_3;\\n  tmpvar_3 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.2, 0.2, 0.2));\\n  ret_1 = tmpvar_3;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = tmpvar_3;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv2_1;\\n  vec3 ret_2;\\n  uv2_1 = (uv + (vec2(1.0, 0.0) * texsize.zw));\\n  float tmpvar_3;\\n  tmpvar_3 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(-1.0, 0.0) * texsize.zw));\\n  float tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(0.0, 1.0) * texsize.zw));\\n  float tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_main, uv2_1).xyz + \\n    (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4)\\n  ) + (\\n    ((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2)\\n   * 0.15)) + ((\\n    (texture (sampler_blur3, uv2_1).xyz * scale3)\\n   + bias3) * 0.1)).x;\\n  uv2_1 = (uv + (vec2(0.0, -1.0) * texsize.zw));\\n  vec3 tmpvar_6;\\n  tmpvar_6.z = 0.14;\\n  tmpvar_6.x = (tmpvar_3 - tmpvar_4);\\n  tmpvar_6.y = (tmpvar_5 - ((\\n    (texture (sampler_main, uv2_1).xyz + (((texture (sampler_blur1, uv2_1).xyz * scale1) + bias1) * 0.4))\\n   + \\n    (((texture (sampler_blur2, uv2_1).xyz * scale2) + bias2) * 0.15)\\n  ) + (\\n    ((texture (sampler_blur3, uv2_1).xyz * scale3) + bias3)\\n   * 0.1)).x);\\n  ret_2 = (0.5 + (0.5 * normalize(tmpvar_6)));\\n  vec2 x_7;\\n  x_7 = (ret_2.xy - 0.5);\\n  ret_2 = (ret_2 * clamp ((\\n    sqrt(dot (x_7, x_7))\\n   * 5.0), 0.0, 1.0));\\n  ret_2 = ret_2.xxy;\\n  ret_2 = (ret_2 + 1.15);\\n  ret_2 = (ret_2 * mix (ret_2, (ret_2 * \\n    (((texture (sampler_blur3, uv).xyz * scale3) + bias3) - ((texture (sampler_blur1, uv).xyz * scale1) + bias1))\\n  ), pow (hue_shader.yxz, ret_2)));\\n  ret_2 = (ret_2 * ret_2);\\n  vec4 tmpvar_8;\\n  tmpvar_8.w = 1.0;\\n  tmpvar_8.xyz = ret_2;\\n  ret = tmpvar_8.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:2.4,decay:1,echo_zoom:.997,echo_alpha:.5,echo_orient:3,wave_mode:2,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.28,wave_smoothing:.9,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:2.599,warpscale:.01,zoomexp:.99817,zoom:.86978,warp:.01,sy:1.0017,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ob_r:1,ob_g:1,ob_b:1,ib_size:.04,ib_r:0,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:.5,mv_r:.35,mv_g:.35,mv_b:.35,mv_a:.2},shapes:[{baseVals:{enabled:1,sides:20,additive:1,textured:1,rad:1.99867,tex_zoom:.49486,g:1,b:1,a:.75,r2:1,b2:1,a2:1,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.ang2=0;a.redsine=0;a.greensine=0;a.bluesine=0;a.redif=0;a.greenif=0;a.blueif=0;a.decay=.1;\",frame_eqs_str:\"a.sx=1;a.sy=1;a.redsine=.5+.15*a.bass*Math.sin(3*a.time);a.greensine=.5+.15*a.mid*Math.sin(2*a.time);a.bluesine=.5+.15*a.treb*Math.sin(a.time);a.redif=.9*(.00001<Math.abs(above(a.bass,1.2))?a.redsine:.00001<Math.abs(above(a.redif,.95))?0:.98*a.redif);a.greenif=.8*(.00001<Math.abs(above(a.mid,1.2))?a.greensine:.00001<Math.abs(above(a.greenif,.95))?0:.8*a.greenif);a.blueif=.8*(.00001<Math.abs(above(a.treb,1.2))?a.bluesine:.00001<Math.abs(above(a.blueif,.95))?0:.8*a.blueif);a.ib_r=\\na.redif;a.ib_g=a.greenif;a.ib_b=a.blueif;\",pixel_eqs_str:\"a.ang2=Math.sin(3*a.ang+a.time+.05*Math.sin(a.time*above(a.mid_att,1.2)));a.rot=1.2*a.ang2;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (((texture (sampler_blur1, uv).xyz * scale1) + bias1) - texture (sampler_main, uv).xyz);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = (mix (texture (sampler_main, uv).xyz, texture (sampler_main, (\\n    (vec2(-1.003, 1.003) * (uv - 0.5))\\n   + 0.5)).xyz, vec3(0.5, 0.5, 0.5)) * 2.4);\\n  ret_1 = (ret_1 * ret_1);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1,decay:.995,echo_zoom:1,echo_alpha:.5,echo_orient:1,wave_mode:6,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,wave_a:.001,wave_scale:2.103,wave_smoothing:.54,wave_mystery:.38,modwavealphastart:.81,modwavealphaend:1.4,warpscale:1.331,warp:.01,wave_r:0,wave_g:0,wave_b:0,ob_size:.005,ob_a:1,ib_size:.005,ib_a:1,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:14,num_inst:78,x:.6,rad:.02217,tex_zoom:60.80383,g:1,b:1,a:.9,g2:0,a2:1,border_g:.59,border_a:1},init_eqs_str:\"a.fov=0;a.arg=0;a.t1=0;a.posx=0;a.trel=0;a.yl=0;a.k1=0;a.posy=0;a.xl=0;a.time2=0;a.q19=0;a.t2=0;a.yw=0;a.dist=0;a.xw=0;a.speed=0;a.t1=div(Math.floor(randint(10)),10);a.t2=.2+div(Math.floor(randint(8)),10);\",frame_eqs_str:\"a.speed=.02;a.trel=Math.floor(a.time*a.speed)-a.time*a.speed+1;a.k1=div(a.instance,a.num_inst);a.k1-=Math.floor(a.k1);a.dist=1.4-a.k1;a.fov=.1;a.arg=a.dist-.02*a.time;a.posx=div(a.fov,a.dist)*(Math.sin(13*a.arg)+.2*Math.sin(332*a.dist));a.posy=div(a.fov,a.dist)*(Math.cos(36*a.arg)+.2*Math.sin(332*a.dist));a.xl=.5+a.posx;a.yl=.5+a.posy;a.time2=div(a.time,4)+Math.cos(a.instance);a.xw=.5+.5*(Math.sin(14*a.arg)+.02*Math.cos(1131*a.arg));a.yw=a.time2-Math.floor(a.time2);a.x=.00001<\\nMath.abs(bnot(a.q19))?a.xl:a.xw;a.y=.00001<Math.abs(bnot(a.q19))?a.yl:a.yw;a.ang=a.time*Math.sin(44*a.k1)*12;a.a=a.t1;a.border_a=0;a.a2=a.t2;a.rad=div(.01,a.dist);a.r=.5+.3*Math.sin(a.instance+a.arg);a.b=.5+.3*Math.sin(a.instance-2+a.arg);a.g=.5+.3*Math.sin(2*a.instance+a.arg);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.mq29=0;a.cthr=0;a.mq1=0;a.q25=0;a.q12=0;a.q22=0;a.q21=0;a.q13=0;a.q29=0;a.mq31=0;a.mq21=0;a.mq24=0;a.oldq8=0;a.chng=0;a.mq28=0;a.mq23=0;a.q31=0;a.q23=0;a.q24=0;a.q11=0;a.atime=0;a.q26=0;a.mq27=0;a.mq25=0;a.mq22=0;a.vol=0;a.mq26=0;a.q27=0;a.q28=0;a.q8=0;a.q8=0;\",frame_eqs_str:\"a.chng=Math.sin(.5*a.time);a.cthr=.9999;a.mq21=.00001<Math.abs(above(a.chng,a.cthr))?randint(3):a.mq21;a.mq22=.00001<Math.abs(above(a.chng,a.cthr))?randint(3):a.mq22;a.mq23=.00001<Math.abs(above(a.chng,a.cthr))?randint(3):a.mq23;a.mq24=.00001<Math.abs(above(a.chng,a.cthr))?randint(2):a.mq24;a.mq25=.00001<Math.abs(above(a.chng,a.cthr))?randint(2):a.mq25;a.mq26=.00001<Math.abs(above(a.chng,a.cthr))?randint(2):a.mq26;a.mq27=.00001<Math.abs(above(a.chng,a.cthr))?randint(1):a.mq27;\\na.mq28=.00001<Math.abs(above(a.chng,a.cthr))?randint(1):a.mq28;a.mq29=.00001<Math.abs(above(a.chng,a.cthr))?.3*randint(1):a.mq29;a.mq31=.00001<Math.abs(above(a.chng,a.cthr))?.3*randint(1):a.mq31;a.monitor=a.chng;a.q21=a.mq21;a.q22=a.mq22;a.q23=a.mq23;a.q24=a.mq24;a.q25=a.mq25;a.q26=a.mq26;a.q27=a.mq27;a.q28=a.mq28;a.q29=a.mq29;a.q31=a.mq31;a.monitor=a.mq1;a.vol=a.bass+a.treb+a.mid;a.atime+=a.vol;a.q11=.4+.4*Math.sin(.006*a.atime);a.q12=.4+.4*Math.cos(.00613828348*a.atime);a.q13=.4+.4*Math.sin(.00598593455*\\na.atime);a.monitor=a.q13;a.warp=0;a.q8=a.oldq8+.0003*div(pow(1+1.2*a.bass+.4*a.bass_att+.1*a.treb+.1*a.treb_att+.1*a.mid+.1*a.mid_att,6),a.fps);a.oldq8=a.q8;a.wave_r=.5+.5*Math.sin(1.123*a.q8);a.wave_g=.5+.5*Math.sin(1.576*a.q8);a.wave_b=.5+.5*Math.cos(1.465*a.q8);a.ib_a=.2*a.bass;\",pixel_eqs_str:\"a.dx=a.dx+.008*Math.sin((2*a.y-1)*a.meshx)+.008*Math.sin((2*a.y-1)*a.meshx*1.3333);a.dy=a.dy+.008*Math.cos((2*a.x-1)*a.meshx*1.3333)+.008*Math.cos((2*a.x-1)*a.meshx);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = tmpvar_2.xyz;\\n  vec4 tmpvar_3;\\n  tmpvar_3 = texture (sampler_noisevol_hq, (((vec3(0.05, 0.05, 0.0) * uv.xyy) * (\\n    (q27 * texsize.xyy)\\n   * texsize_noisevol_hq.zww)) + ((time * vec3(0.0, 0.0, 1.0)) * q29)));\\n  if (((tmpvar_2.x > (q21 * q13)) && (tmpvar_2.x <= (q24 * q11)))) {\\n    ret_1.y = (tmpvar_2.y - (tmpvar_3.x * 0.5));\\n  };\\n  if (((ret_1.y > (q22 * q11)) && (ret_1.y <= (q25 * q12)))) {\\n    ret_1.z = (tmpvar_2.z - (tmpvar_3.y * 0.5));\\n  };\\n  if (((ret_1.z > (q23 * q12)) && (ret_1.z <= (q26 * q13)))) {\\n    ret_1.x = (tmpvar_2.x - (tmpvar_3.z * 0.5));\\n  };\\n  ret_1 = (ret_1 + ((ret_1 - \\n    ((texture (sampler_blur1, uv).xyz * scale1) + bias1)\\n  ) * 0.2));\\n  ret_1 = (ret_1 - 0.02);\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = ret_1;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.05 + (0.9 * uv));\\n  vec4 tmpvar_3;\\n  tmpvar_3 = texture (sampler_main, uv_1);\\n  ret_2 = tmpvar_3.xyz;\\n  vec4 tmpvar_4;\\n  tmpvar_4 = texture (sampler_noisevol_hq, (((vec3(0.05, 0.05, 0.0) * uv_1.xyy) * (\\n    (q28 * texsize.xyy)\\n   * texsize_noisevol_hq.zww)) + ((time * vec3(0.0, 0.0, 1.0)) * q31)));\\n  if (((tmpvar_3.x > (q26 * q13)) && (tmpvar_3.x <= (q23 * q11)))) {\\n    ret_2.z = (tmpvar_3.z - (tmpvar_4.x * 0.5));\\n  };\\n  if (((tmpvar_3.y > (q25 * q11)) && (tmpvar_3.y <= (q22 * q12)))) {\\n    ret_2.x = (tmpvar_3.x - (tmpvar_4.y * 0.5));\\n  };\\n  if (((ret_2.z > (q24 * q12)) && (ret_2.z <= (q21 * q13)))) {\\n    ret_2.y = (tmpvar_3.y - (tmpvar_4.z * 0.5));\\n  };\\n  ret_2 = (abs((\\n    ((texture (sampler_blur1, uv_1).xyz * scale1) + bias1)\\n   - ret_2)) * 6.0);\\n  ret_2 = (ret_2 * 1.333);\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ret_2;\\n  ret = tmpvar_5.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.157,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.9995,warp:.009,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:.5,ob_r:.01,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_a:0,b1x:.7,b1ed:0},shapes:[{baseVals:{enabled:1,sides:14,num_inst:512,rad:.1026,tex_ang:.62832,r2:1,g2:0,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;a.r_border=0;a.g_border=0;a.b_border=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.rad=div(a.bass+a.treb,100);a.a=above(a.bass+a.mid+a.treb,.8);a.r=div(Math.floor(randint(100)),100);a.g=div(Math.floor(randint(100)),\\n100);a.b=div(Math.floor(randint(100)),100);a.r2=div(Math.floor(randint(100)),100);a.g2=div(Math.floor(randint(100)),100);a.b2=div(Math.floor(randint(100)),100);a.r_border=div(Math.floor(randint(100)),100);a.g_border=div(Math.floor(randint(100)),100);a.b_border=div(Math.floor(randint(100)),100);\"},{baseVals:{enabled:1,sides:23,num_inst:1024,rad:.10262,ang:.43982,tex_ang:.62832,a:.5,r2:1,g2:0,a2:.5,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;a.r_border=0;a.g_border=0;a.b_border=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.05*a.bass;a.ma-=3.1415*above(a.mid,1)*.05*a.mid;a.mx+=.0001*Math.cos(a.ma);a.my+=.0001*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.rad=div(a.bass+a.treb,75);a.a=above(a.bass+a.mid+a.treb,.1);a.r=div(Math.floor(randint(100)),100);a.g=div(Math.floor(randint(100)),\\n100);a.b=div(Math.floor(randint(100)),100);a.r2=div(Math.floor(randint(100)),100);a.g2=div(Math.floor(randint(100)),100);a.b2=div(Math.floor(randint(100)),100);a.r_border=div(Math.floor(randint(100)),100);a.g_border=div(Math.floor(randint(100)),100);a.b_border=div(Math.floor(randint(100)),100);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.n=0;a.z=0;a.n2=0;a.q25=0;a.w2=0;a.q22=0;a.q21=0;a.q29=0;a.j3=0;a.q1=0;a.n1=0;a.v=0;a.w3=0;a.q31=0;a.q23=0;a.q24=0;a.pi=0;a.pi2inv=0;a.j2=0;a.w1=0;a.zz=0;a.vol=0;a.j1=0;a.q27=0;a.maskangle=0;a.q3=0;a.q32=0;a.q28=0;a.q30=0;a.q20=0;a.mtime=0;a.x1=2;a.y1=2;a.z1=2;\",frame_eqs_str:\"a.d=20;a.z=4;a.zz=1+0*a.bass_att;a.pi=2*Math.asin(1);a.pi2inv=div(.5,a.pi);a.maskangle=Math.asin(div(1,a.d));a.q32=a.maskangle;a.q31=a.d;a.q30=a.z;a.q29=a.pi2inv;a.q28=a.pi;a.q27=div(1,a.zz);a.v=.4;a.j1=.95*a.j1+sqr(4*a.bass)*a.v;a.j2=.95*a.j2+sqr(4*a.mid)*a.v;a.j3=.95*a.j3+sqr(4*a.treb)*a.v;a.n+=.0152*a.j1;a.n1+=.0152*a.j2;a.n2+=.0152*a.j3;a.vol=.25*(a.bass+a.mid+a.treb);a.vol=a.vol;a.q3=a.vol;a.mtime+=.01*a.vol;a.q1=a.mtime;a.zoom+=.015*a.vol;a.w1=.01*a.n;a.w2=.01*a.n1;a.w3=\\n.01*a.n2;a.q20=Math.cos(a.w1);a.q21=Math.sin(a.w1);a.q22=Math.cos(a.w2);a.q23=Math.sin(a.w2);a.q24=Math.cos(a.w3);a.q25=Math.sin(a.w3);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = (((uv_orig * texsize.xy) * (texsize_noise_lq.zw * 1.5)) + rand_frame.xy);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ((texture (sampler_main, (\\n    mix (uv_orig, uv, vec2(((dot (texture (sampler_main, \\n      mix (uv_orig, uv, vec2(4.0, 4.0))\\n    ).xyz, vec3(0.32, 0.49, 0.29)) - dot (\\n      ((texture (sampler_blur2, mix (uv_orig, uv, vec2(-12.0, -12.0))).xyz * scale2) + bias2)\\n    , vec3(0.32, 0.49, 0.29))) * 12.0)))\\n   + \\n    (((texture (sampler_noise_lq, tmpvar_1) - 0.5).xy * texsize.zw) * 0.5)\\n  )).xyz + (\\n    (texture (sampler_noise_lq, tmpvar_1) - 0.5)\\n   * 0.006).xyz) + -0.0006);\\n  ret = tmpvar_2.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret2_2;\\n  vec2 uv2_3;\\n  vec3 ret_4;\\n  uv2_3.y = uv.y;\\n  uv2_3.x = (1.0 - uv.x);\\n  uv2_3 = (0.5 + ((uv2_3 - 0.5) * 0.98));\\n  uv_1 = (0.5 + ((uv - 0.5) * 0.98));\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (texsize.zw * 4.0);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(1.0, 0.0) * tmpvar_5))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(1.0, 0.0) * tmpvar_5))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_7;\\n  tmpvar_7 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(0.0, 1.0) * tmpvar_5))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(0.0, 1.0) * tmpvar_5))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_8;\\n  tmpvar_8.z = 0.0;\\n  tmpvar_8.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_8.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  vec4 tmpvar_9;\\n  tmpvar_9 = texture (sampler_noisevol_hq, ((vec3(0.05, 0.05, 0.0) * (\\n    ((uv_1.xyy + (tmpvar_8 * 0.5)) * texsize.xyy)\\n   * texsize_noisevol_hq.zww)) + (vec3(0.0, 0.0, 0.2) * time)));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = dot (tmpvar_6, vec3(0.32, 0.49, 0.29));\\n  tmpvar_10.y = dot (tmpvar_7, vec3(0.32, 0.49, 0.29));\\n  uv_1 = (uv_1 - (tmpvar_10 * 0.04));\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = dot (((\\n    (texture (sampler_blur1, (uv2_3 + (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (uv2_3 - (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  tmpvar_11.y = dot (((\\n    (texture (sampler_blur1, (uv2_3 + (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (uv2_3 - (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  uv2_3 = (uv2_3 - (tmpvar_11 * 0.04));\\n  ret_4 = (abs((\\n    ((texture (sampler_main, uv_1).xyz + ((texture (sampler_blur1, uv_1).xyz * scale1) + bias1)) - ((texture (sampler_blur2, uv_1).xyz * scale2) + bias2))\\n   - \\n    ((texture (sampler_blur3, uv_1).xyz * scale3) + bias3)\\n  )) * (dot (tmpvar_9, vec4(0.32, 0.49, 0.29, 0.0)) * 3.0));\\n  ret2_2 = (abs((\\n    ((texture (sampler_main, uv2_3).xyz + ((texture (sampler_blur1, uv2_3).xyz * scale1) + bias1)) - ((texture (sampler_blur2, uv2_3).xyz * scale2) + bias2))\\n   - \\n    ((texture (sampler_blur3, uv2_3).xyz * scale3) + bias3)\\n  )) * (dot (tmpvar_9, vec4(0.32, 0.49, 0.29, 0.0)) * 3.0));\\n  ret2_2 = (0.5 - pow (ret2_2, vec3(0.5, 0.5, 0.5)));\\n  ret_4 = (pow (ret_4, vec3(0.5, 0.5, 0.5)) - (0.3 - ret2_2));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = ret_4;\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:4,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken_center:1,darken:1,wave_a:.001,wave_scale:.527429,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:.442,warpscale:.498,zoom:.9999,warp:.01,sx:.9999,wave_r:.8,wave_g:.49,ob_size:0,ob_r:1,ob_g:1,ob_b:1,ob_a:.05,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,thickoutline:1,textured:1,x:.26,y:.2,rad:.393173,tex_zoom:1.392831,r:0,g:.55,b:.5,a:.9,g2:.4,b2:.4,border_r:.3,border_g:.7,border_b:.8,border_a:.2},init_eqs_str:\"a.g0=0;a.y0=0;a.q1=0;a.x0=0;a.q24=0;a.q26=0;a.r0=0;a.trig=0;a.q2=0;a.b0=0;a.rad0=0;\",frame_eqs_str:\"a.trig=a.q24;a.textured=1;a.x0=a.x0*bnot(a.trig)+a.trig*(.2+div(randint(100),200));a.y0=a.y0*bnot(a.trig)+a.trig*(.2+div(randint(100),200));a.x0+=div(.03*a.q1*(3+a.q26),a.fps);a.y0+=div(.03*a.q2*(3+a.q26),a.fps);a.x0-=Math.floor(a.x0);a.y0-=Math.floor(a.y0);a.tex_ang=a.time;a.ang=a.time*a.q2;a.x=a.x0;a.y=a.y0;a.rad0=a.rad0*bnot(a.trig)+div(a.trig*randint(100),200);a.rad=a.rad0;a.r0=a.r0*bnot(a.trig)+div(a.trig*randint(10),10);a.b0=a.b0*bnot(a.trig)+div(a.trig*randint(10),10);\\na.g0=a.g0*bnot(a.trig)+div(a.trig*randint(10),10);a.border_r=a.r0;a.border_g=a.g0;a.border_b=a.b0;a.r=a.r0;a.b=a.b0;a.g=a.g0;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.rm=0;a.ampl=0;a.index2=0;a.index=0;a.q18=0;a.q22=0;a.q21=0;a.movex=0;a.q1=0;a.dec_med=0;a.index3=0;a.trel=0;a.rott=0;a.ava=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.mov=0;a.avg=0;a.movez=0;a.q19=0;a.beat=0;a.q17=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.movey=0;a.q3=0;a.t0=0;a.q28=0;a.q30=0;a.q20=0;a.dirx=1;\",frame_eqs_str:\"a.dec_med=pow(.6,div(30,a.fps));a.dec_slow=pow(.9,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),4);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.q20=a.avg;a.q21=\\na.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.ava=a.ava*a.dec_slow+a.q26*(1-a.dec_slow);a.k1=a.is_beat*equal(mod(a.index,2),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,2);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.q27=8-a.index;a.q28=.5+div(Math.sin(div(a.time,7)),10);a.rm=Math.min(a.q26-1.5*a.ava,2);a.mov=a.is_beat*a.rm+(1-a.is_beat)*a.mov;a.movez+=div(.4,a.fps)*a.mov;a.q30=a.movez;a.ampl=\\ndiv(a.q26,8);a.movex+=div(.2,a.fps)*Math.sin(a.rott);a.movey+=div(.2,a.fps)*Math.cos(a.rott);a.q18=a.movex;a.q19=a.movey;a.trel=a.trel+div(1,a.fps)+a.q24;a.q17=2*Math.sin(div(a.trel,4));\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 uv6_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_4;\\n  tmpvar_4 = (((q28 * 2.0) * sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  )) + (rand_frame * 64.0)).x;\\n  uv_1 = (uv + (clamp (\\n    ((sin(tmpvar_4) / cos(tmpvar_4)) * normalize(tmpvar_3))\\n  , vec2(-16.0, -16.0), vec2(16.0, 16.0)) / 20.0));\\n  uv6_2 = (0.4 * sin((tmpvar_3 * 12.0)));\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = (((q24 * \\n    (((texture (sampler_main, uv_1).xyz - (\\n      ((texture (sampler_blur1, fract(uv_1)).xyz * scale1) + bias1)\\n     * 0.04)) + (0.15 * (vec3(\\n      (0.1 / sqrt(dot (uv6_2, uv6_2)))\\n    ) * roam_cos.xyz))) - 0.04)\\n  ) * 0.98) + ((1.0 - q24) * texture (sampler_main, uv_orig).xyz));\\n  ret = tmpvar_5.xyz;\\n }\",comp:\"vec3 xlat_mutableneu;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutablers2;\\n shader_body { \\n  vec2 uv_1;\\n  float inten_3;\\n  float dist_4;\\n  float ang2_5;\\n  vec2 uv2_6;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * aspect.xy);\\n  dist_4 = 1.0;\\n  inten_3 = 1.0;\\n  xlat_mutableret1 = vec3(0.0, 0.0, 0.0);\\n  for (float n_2 = 0.0; n_2 <= 4.0; n_2 += 1.0) {\\n    vec2 uv3_7;\\n    ang2_5 = ((6.28 * n_2) / 4.0);\\n    float tmpvar_8;\\n    tmpvar_8 = cos(ang2_5);\\n    float tmpvar_9;\\n    tmpvar_9 = sin(ang2_5);\\n    uv2_6.x = ((uv_1.x * tmpvar_8) - (uv_1.y * tmpvar_9));\\n    uv2_6.y = ((uv_1.x * tmpvar_9) + (uv_1.y * tmpvar_8));\\n    uv2_6 = (uv2_6 * aspect.yx);\\n    dist_4 = (1.0 - fract((\\n      (0.25 * n_2)\\n     + q30)));\\n    inten_3 = ((sqrt(dist_4) * (1.0 - dist_4)) * 4.0);\\n    vec2 tmpvar_10;\\n    tmpvar_10.x = tmpvar_8;\\n    tmpvar_10.y = tmpvar_9;\\n    uv3_7 = (fract((\\n      ((3.0 * uv2_6) * dist_4)\\n     + \\n      (0.3 * q27)\\n    )) + (q17 * tmpvar_10));\\n    xlat_mutableneu = (texture (sampler_main, uv3_7).xyz - ((texture (sampler_blur2, \\n      ((uv3_7 * 1.02) * q1)\\n    ).xyz * scale2) + bias2));\\n    xlat_mutableneu = (xlat_mutableneu * vec3(greaterThanEqual (xlat_mutableneu, vec3(0.0, 0.0, 0.0))));\\n    xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_3));\\n  };\\n  xlat_mutablers2 = ((0.4 * cos(\\n    ((uv_1 * 13.0) + time)\\n  )) - dot (xlat_mutableret1, vec3(0.32, 0.49, 0.29)));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = (xlat_mutableret1 + ((\\n    ((0.05 / sqrt(dot (xlat_mutablers2, xlat_mutablers2))) * q26)\\n   / 4.0) * hue_shader));\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:7,wave_dots:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.39158,wave_scale:.01,wave_smoothing:.504,wave_mystery:-1,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:.7,wave_g:0,ob_r:1,ob_g:1,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:5,additive:1,x:.9,rad:.22613,g:.6,g2:0,b2:.4,a2:.3,border_a:0},init_eqs_str:\"a.dx=0;a.dy=0;a.q22=0;a.q24=0;a.t_rel=5*a.time;a.t1=Math.floor(a.t_rel);\",frame_eqs_str:\"a.dx=div(randint(1E3),1E3)-.5;a.dy=div(randint(1E3),1E3)-.5;a.x=.5+a.dx*(1-div(a.q22,4));a.y=.5+a.dy*(1-div(a.q22,4));a.ang=a.time;a.a=1;a.rad=.1*a.q24+.08+div(a.q22,40);\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:256,spectrum:1,usedots:1,scaling:.5033,g:.6,b:.4},init_eqs_str:\"a.xang=0;a.ampl=0;a.fov=0;a.t1=0;a.yang=0;a.my=0;a.oz=0;a.t_rel=0;a.mz=0;a.q26=0;a.oy=0;a.mx=0;a.zang=0;a.t_abs=0;a.ox=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.t_abs=a.sample;a.t_rel=a.sample-div(a.time,1);a.ampl=a.time;a.ox=10*a.ampl*Math.sin(68*a.t_abs);a.oy=10*a.ampl*Math.cos(28*a.t_abs);a.oz=10*a.ampl*Math.cos(128*a.t_abs);a.r=1;a.g=.5;a.b=1;a.a=div(a.q26,18);a.xang=a.t1;a.yang=2*a.t1;a.zang=a.t1;a.fov=.12;a.mx=a.ox*Math.cos(a.zang)-a.oy*Math.sin(a.zang);a.my=a.ox*Math.sin(a.zang)+a.oy*Math.cos(a.zang);a.ox=a.mx;a.oy=a.my;a.mx=a.ox*Math.cos(a.yang)+a.oz*Math.sin(a.yang);a.mz=-a.ox*Math.sin(a.yang)+a.oz*Math.cos(a.yang);a.ox=\\na.mx;a.oz=a.mz;a.my=a.oy*Math.cos(a.xang)-a.oz*Math.sin(a.xang);a.mz=a.oy*Math.sin(a.xang)+a.oz*Math.cos(a.xang);a.oy=a.my;a.oz=a.mz;a.oz-=6;a.x=div(a.ox*a.fov,a.oz)+.5;a.y=div(a.oy*a.fov,a.oz)+.5;\"},{baseVals:{enabled:1},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"a.a=Math.max(Math.sin(div(a.time,8))-.5,0);a.r=.4;a.g=.2;a.b=.7;a.x=a.sample+.01*Math.cos(243*a.sample+a.time);a.y=.5+.5*Math.sin(div(a.time,13))+.02*Math.sin(143*a.sample);a.a=.7;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index4=0;a.index=0;a.q22=0;a.q21=0;a.q29=0;a.q1=0;a.dec_med=0;a.index3=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.t_rel=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.movez=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q28=0;a.q20=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),4);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.q20=a.avg;a.q21=\\na.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_slow*a.p2+(1-a.dec_slow)*a.p1;a.rott=div(3.14159265359*a.p2,2);a.q27=8-a.index;a.q28=.5+div(Math.sin(div(a.time,7)),10);a.q29=a.index4;a.mv_a=.2;a.t_rel=div(a.time,3);a.wave_a=Math.max(Math.sin(div(a.time,8))-.5,0);a.wave_a=0;a.wave_x=.5+.5*Math.sin(div(a.time,13));a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.movez+=div(.3,a.fps);\\na.q29=a.movez;a.zoom=1;a.rot=0;a.dx=.003;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = (((uv - vec2(0.5, 0.5)) * texsize.xy) * 0.03);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ((0.99 * texture (sampler_main, (uv + \\n    (((clamp (\\n      (sin(tmpvar_1) / cos(tmpvar_1))\\n    , vec2(-12.0, -12.0), vec2(12.0, 12.0)) * cos(\\n      (tmpvar_1.yx * tmpvar_1.yx)\\n    )) * texsize.zw) * 12.0)\\n  )).xyz) - 0.01);\\n  ret = tmpvar_2.xyz;\\n }\",comp:\"vec3 xlat_mutableret1;\\nvec2 xlat_mutableuv2;\\n shader_body { \\n  vec2 uv_1;\\n  float inten_2;\\n  float dist_3;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * aspect.xy);\\n  dist_3 = (1.0 - fract((0.25 + q29)));\\n  inten_2 = (sqrt(dist_3) * (1.0 - (dist_3 * dist_3)));\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (texture (sampler_main, fract(\\n    (((2.0 * uv_1) * dist_3) + 0.5)\\n  )).xyz * inten_2));\\n  xlat_mutableuv2.x = -(uv_1.y);\\n  xlat_mutableuv2.y = uv_1.x;\\n  dist_3 = (1.0 - fract((0.5 + q29)));\\n  inten_2 = (sqrt(dist_3) * (1.0 - (dist_3 * dist_3)));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, fract(\\n    (((2.0 * xlat_mutableuv2) * dist_3) + 0.5)\\n  )).xyz * inten_2));\\n  dist_3 = (1.0 - fract((0.75 + q29)));\\n  inten_2 = (sqrt(dist_3) * (1.0 - (dist_3 * dist_3)));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, fract(\\n    (((2.0 * uv_1) * dist_3) + 0.5)\\n  )).xyz * inten_2));\\n  xlat_mutableuv2.x = -(uv_1.y);\\n  xlat_mutableuv2.y = uv_1.x;\\n  dist_3 = (1.0 - fract((1.0 + q29)));\\n  inten_2 = (sqrt(dist_3) * (1.0 - (dist_3 * dist_3)));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, fract(\\n    (((2.0 * xlat_mutableuv2) * dist_3) + 0.5)\\n  )).xyz * inten_2));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = (xlat_mutableret1 * 3.0);\\n  ret = tmpvar_4.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.980001,decay:.5,echo_zoom:.952379,echo_alpha:.5,echo_orient:3,wave_mode:4,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.391579,wave_scale:1.575857,wave_smoothing:.45,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:.5,wave_g:.79,ob_size:0,ob_a:.8,ib_size:.26,mv_x:31.999998,mv_y:24.000004,mv_l:.05,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,textured:1,rad:.545996,ang:2.199115,tex_ang:.502655,tex_zoom:2.02311,g:1,b:.9,a:.6,r2:.8,g2:.8,b2:.8,a2:.6,border_b:0,border_a:.7},init_eqs_str:\"a.q29=0;a.q22=0;a.q23=0;a.q24=0;\",frame_eqs_str:\"a.tex_ang=1+Math.sin(div(a.time,9));a.rad=a.q29;a.tex_zoom=2+Math.sin(div(a.time,17));a.additive=div(a.q22,6);a.textured=1-bnot(a.q23)*a.q24;\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:100,x:.503,rad:.01,tex_zoom:.609857,g:.1,a:.9,r2:1,b2:1,border_r:.5,border_g:.5,border_b:.5,border_a:0},init_eqs_str:\"a.q21=0;\",frame_eqs_str:\"a.x=.5+div(randint(10),25);a.y=.5+div(randint(10),25);a.ang=randint(6);a.r=div(randint(4),3);a.g=div(randint(4),3);a.b=div(randint(4),3);a.r2=a.b;a.g2=a.r;a.b2=a.g;a.a=Math.min(div(a.q21,2),.9);a.rad=div(a.a,9);a.a=.5;a.a2=.8;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index=0;a.copy=0;a.q22=0;a.q21=0;a.grid=0;a.q29=0;a.q1=0;a.dec_med=0;a.str=0;a.index3=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.go=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.movez=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.p3=0;a.q3=0;a.t0=0;a.rot1=0;a.q28=0;a.q30=0;a.q20=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,4);a.index2=mod(a.index2+a.is_beat*bnot(a.index),4);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.q20=a.avg;a.q21=\\na.beat;a.q22=Math.max(a.peak,4);a.q23=a.index;a.q24=a.is_beat;a.q26=Math.max(a.bass+a.mid+a.treb,3);a.go=a.go*a.dec_slow+(1-a.dec_slow)*equal(a.index2,2);a.movez+=div(3*(1+.3*a.q26),a.fps)*a.go;a.q30=a.movez;a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.p3=a.dec_med*a.p3+(1-a.dec_med)*a.p2;a.rott=div(3.1416*a.p3,2);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.str=a.str*a.dec_slow+(1-a.dec_slow)*a.index3;a.q27=4-a.str;\\na.grid=a.grid*a.dec_med+(1-a.dec_med)*equal(a.index2,2)*bnot(a.index);a.mv_a=.6*a.grid;a.rot1=a.dec_med*a.rot1+(1-a.dec_med)*bnot(a.index2);a.q28=a.rot1;a.copy=a.copy*a.dec_slow+(1-a.dec_slow)*a.index3;a.q29=.3*a.copy;\",pixel_eqs_str:\"a.dx=.02*a.q28;a.rot=.04*a.q2;a.zoom=.96+.1*Math.sin(div(a.time,7));\",warp:\" shader_body { \\n  vec2 zz_1;\\n  mat2 tmpvar_2;\\n  tmpvar_2[uint(0)] = _qa.xy;\\n  tmpvar_2[1u] = _qa.zw;\\n  zz_1 = (((\\n    (uv - vec2(0.5, 0.5))\\n   * texsize.xy) * 0.01) * tmpvar_2);\\n  zz_1 = -(zz_1.yx);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ((texture (sampler_main, (uv + \\n    (((clamp (\\n      (sin(zz_1.yx) / cos(zz_1.yx))\\n    , vec2(-8.0, -8.0), vec2(8.0, 8.0)) * cos(\\n      (4.0 * zz_1)\\n    )) * texsize.zw) * 16.0)\\n  )).xyz * 0.99) - 0.01);\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv3_1;\\n  vec2 uv2_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_4;\\n  tmpvar_4 = (0.1 / (sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  ) + 0.1));\\n  vec2 tmpvar_5;\\n  float tmpvar_6;\\n  tmpvar_6 = (ang / 3.14);\\n  tmpvar_5.x = tmpvar_6;\\n  tmpvar_5.y = (q27 * tmpvar_4);\\n  uv2_2.y = (tmpvar_5.y + (0.1 * q30));\\n  uv2_2.x = (tmpvar_6 + (0.2 * time));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_6;\\n  tmpvar_7.y = (4.0 * tmpvar_4);\\n  uv3_1.y = (tmpvar_7.y - (0.2 * q30));\\n  uv3_1.x = tmpvar_7.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = fract(uv3_1);\\n  uv3_1 = tmpvar_8;\\n  float tmpvar_9;\\n  tmpvar_9 = clamp ((1.0 - (4.0 * rad)), 0.0, 1.0);\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = (((\\n    (3.0 * max (((2.0 * texture (sampler_main, uv2_2).xyz) + texture (sampler_main, tmpvar_8).xyz), ((2.0 * \\n      ((texture (sampler_blur2, fract(uv2_2)).xyz * scale2) + bias2)\\n    ) + (\\n      (texture (sampler_blur2, fract(tmpvar_8)).xyz * scale2)\\n     + bias2))))\\n   * rad) + (\\n    ((uv.y * pow ((1.0 - rad), 8.0)) * roam_cos)\\n  .xyz * tmpvar_9)) + ((tmpvar_9 * 2.0) * (\\n    (texture (sampler_blur1, fract(uv)).xyz * scale1)\\n   + bias1)));\\n  ret = tmpvar_10.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:6,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,darken_center:1,darken:1,wave_a:.001,wave_scale:.236,wave_smoothing:0,wave_mystery:-.64,modwavealphastart:.81,modwavealphaend:1.4,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_b:.6,ob_size:.005,ob_a:.8,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:18,num_inst:4,x:.83,rad:.14349,tex_ang:3.76991,tex_zoom:5.27784,r:0,g:1,b:1,a:.5,r2:1,g2:.2,b2:1,border_b:0,border_a:0},init_eqs_str:\"a.d=0;a.k1=0;a.q22=0;a.q27=0;a.r0=0;a.q24=0;a.g0=0;a.b0=0;\",frame_eqs_str:\"a.d=.3+.2*div(mod(a.frame,8),8);a.k1=6.28*div(a.instance,a.num_inst);a.d=.3+div(a.q22,8);a.x=.5+a.d*Math.cos(a.k1);a.y=.5+a.d*Math.sin(a.k1);a.a=Math.min(1,.2+div(.8*a.q22,2)*mod(a.q27,2));a.a2=0;a.r=.6+.4*Math.sin(div(a.time,4)+1.56);a.b=.6+.4*Math.sin(div(a.time,4)+3.14);a.g=.6+.4*Math.sin(div(a.time,4));a.r0=a.r0*bnot(a.q24)+(.5+.5*Math.sin(a.time))*a.q24;a.g0=a.g0*bnot(a.q24)+(.5+.5*Math.sin(1.7*a.time))*a.q24;a.b0=div(2-a.r0-a.g0,2);a.r=a.r0;a.b=a.b0;a.g=a.g0;a.r2=1-a.g;\\na.g2=1-a.b;a.b2=1-a.r;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:128,scaling:.89152,smoothing:0},init_eqs_str:\"a.q22=0;a.q27=0;a.q24=0;a.t1=0;a.t2=0;a.r0=0;a.g0=0;\",frame_eqs_str:\"a.t1=a.t1*bnot(a.q24)+(.5+.5*Math.sin(a.time))*a.q24;a.t2=a.t2*bnot(a.q24)+(.5+.5*Math.sin(1.7*a.time))*a.q24;\",point_eqs_str:\"a.x=a.sample;a.y=.5;a.a=a.q22*mod(a.q27+1,2);a.a=div(a.q24*mod(a.q27+1,2),2)+0;a.r=a.t1;a.b=a.t2;a.g=div(2-a.r0-a.g0,2);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.speed_=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q13=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.movz=0;a.trel=0;a.q9=0;a.rott=0;a.spg=0;a.vol__=0;a.is_beat=0;a.q31=0;a.k1=0;a.q24=0;a.vx=0;a.dec_slow=0;a.vy=0;a.vz=0;a.q4=0;a.dir=0;a.p2=0;a.avg=0;a.trig=0;a.beat=0;a.q17=0;a.vol=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.speed=0;a.q3=0;a.t0=0;a.vol_=0;a.q7=0;a.q28=0;a.q30=0;a.q20=0;a.q8=0;a.ang0=0;a.speed_=1;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.96,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.1);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,12);a.index2=mod(a.index2+a.is_beat*bnot(a.index),8);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q24=a.is_beat;a.vol=div(a.bass_att+a.mid_att+\\na.treb_att,2);a.vol_=a.dec_med*a.vol_+(1-a.dec_med)*a.vol;a.vol__=a.dec_med*a.vol__+(1-a.dec_med)*a.vol_;a.q27=a.index+1;a.q28=a.index2+2;a.k1=a.is_beat*equal(mod(a.index,4),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.trig=a.q24*bnot(mod(a.index,8));a.vx=a.vx*bnot(a.trig)+a.trig*(div(randint(100),100)-.5);a.vy=a.vy*bnot(a.trig)+a.trig*(div(randint(100),100)-.5);a.vz=a.vz*bnot(a.trig)+\\na.trig*(div(randint(100),100)-.5);a.speed=Math.min(a.vol_-a.vol__+.5,1);a.speed=Math.max(a.speed,-.5);a.speed_=bnot(a.trig)*a.speed_+a.trig*a.speed;a.spg=a.dec_med*a.spg+(1-a.dec_med)*a.speed_;a.movz+=div(1,a.fps)*(a.speed_+div(0*a.vol_,6));a.q9=a.movz;a.q12=2*Math.min(a.q22,6);a.q13=2*Math.sin(div(a.movz,3)-div(a.time,11));a.trig=bnot(mod(a.index,4))*a.q24;a.dir=bnot(a.trig)*a.dir+a.trig*(randint(10)-5);a.trel+=div(.1,a.fps)*a.dir;a.q5=Math.cos(a.trel-a.movz);a.q6=Math.sin(div(a.trel,2)-a.movz);\\na.q7=-a.q6;a.q8=a.q5;a.ang0=a.ang0*a.dec_med+a.vz*(1-a.dec_med);a.q17=2*a.ang0;a.q30=.2*Math.sin(div(a.time,8));a.q31=.5+.5*Math.sin(div(a.time,3.3));a.zoom=1;a.rot=0;a.dx=0;a.monitor=a.q28;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"vec3 xlat_mutablemus;\\n shader_body { \\n  vec2 d_1;\\n  vec2 uv6_2;\\n  vec2 uv1_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv - 0.5);\\n  uv1_3 = tmpvar_4;\\n  uv6_2 = tmpvar_4;\\n  float x_5;\\n  x_5 = (abs(tmpvar_4.x) - abs(tmpvar_4.y));\\n  float tmpvar_6;\\n  tmpvar_6 = (12.0 * sqrt((x_5 * x_5)));\\n  vec2 tmpvar_7;\\n  tmpvar_7 = normalize(tmpvar_4);\\n  d_1 = tmpvar_7;\\n  float tmpvar_8;\\n  tmpvar_8 = (float(mod (q28, 2.0)));\\n  if ((tmpvar_8 == 0.0)) {\\n    d_1 = ((q1 * tmpvar_7) - (q2 / tmpvar_7));\\n  };\\n  uv1_3 = (tmpvar_4 + ((-0.06 * \\n    dot (((texture (sampler_blur1, uv).xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29))\\n  ) * normalize(tmpvar_4)));\\n  uv1_3 = (uv1_3 - (clamp (\\n    ((sin(tmpvar_6) / cos(tmpvar_6)) * d_1)\\n  , vec2(-4.0, -4.0), vec2(4.0, 4.0)) / 60.0));\\n  uv6_2 = (0.4 * cos((\\n    (uv1_3 * 2.0)\\n   * q28)));\\n  xlat_mutablemus = (clamp (vec3((0.0006 / \\n    (sqrt(dot (uv6_2, uv6_2)) - (0.002 * (q27 + (8.0 * q28))))\\n  )), 0.0, 1.0) * q31);\\n  xlat_mutablemus = (xlat_mutablemus * (1.0 + roam_cos).xyz);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = ((0.4 * (\\n    ((texture (sampler_main, ((uv1_3 * \\n      (0.8 + (0.2 * tmpvar_6))\\n    ) + 0.5)).xyz + xlat_mutablemus) * 0.95)\\n   - 0.025)) + (0.6 * texture (sampler_main, uv_orig)).xyz);\\n  ret = tmpvar_9.xyz;\\n }\",comp:\"vec3 xlat_mutableneu;\\nvec3 xlat_mutableret2;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutablerss;\\nvec2 xlat_mutableuv2;\\n shader_body { \\n  vec2 uv_1;\\n  vec2 ofs_2;\\n  vec3 ret1_3;\\n  vec2 uv5_4;\\n  uv_1 = ((uv - 0.5) * aspect.xy);\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = q5;\\n  tmpvar_5.y = q6;\\n  uv_1 = (uv_1 + (tmpvar_5 / 4.0));\\n  uv5_4 = uv_1;\\n  mat2 tmpvar_6;\\n  tmpvar_6[uint(0)] = _qb.xy;\\n  tmpvar_6[1u] = _qb.zw;\\n  uv_1 = (uv_1 * tmpvar_6);\\n  float tmpvar_7;\\n  float tmpvar_8;\\n  tmpvar_8 = (min (abs(\\n    (uv5_4.y / uv5_4.x)\\n  ), 1.0) / max (abs(\\n    (uv5_4.y / uv5_4.x)\\n  ), 1.0));\\n  float tmpvar_9;\\n  tmpvar_9 = (tmpvar_8 * tmpvar_8);\\n  tmpvar_9 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_9) + 0.05368138) * tmpvar_9) - 0.1173503)\\n     * tmpvar_9) + 0.1938925) * tmpvar_9) - 0.3326756)\\n   * tmpvar_9) + 0.9999793) * tmpvar_8);\\n  tmpvar_9 = (tmpvar_9 + (float(\\n    (abs((uv5_4.y / uv5_4.x)) > 1.0)\\n  ) * (\\n    (tmpvar_9 * -2.0)\\n   + 1.570796)));\\n  tmpvar_7 = (tmpvar_9 * sign((uv5_4.y / uv5_4.x)));\\n  if ((abs(uv5_4.x) > (1e-08 * abs(uv5_4.y)))) {\\n    if ((uv5_4.x < 0.0)) {\\n      if ((uv5_4.y >= 0.0)) {\\n        tmpvar_7 += 3.141593;\\n      } else {\\n        tmpvar_7 = (tmpvar_7 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_7 = (sign(uv5_4.y) * 1.570796);\\n  };\\n  xlat_mutablers0.x = (((tmpvar_7 / 3.1416) * 6.0) * q28);\\n  float tmpvar_10;\\n  tmpvar_10 = (1.5 / sqrt(dot (uv_1, uv_1)));\\n  xlat_mutablers0.y = tmpvar_10;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = (xlat_mutablers0.x + (q9 * 4.0));\\n  tmpvar_11.y = (tmpvar_10 + ((q9 * q28) * 4.0));\\n  xlat_mutablerss = (tmpvar_11 / 12.0);\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = q5;\\n  tmpvar_12.y = q6;\\n  ofs_2 = (0.1 * tmpvar_12.yx);\\n  float tmpvar_13;\\n  float tmpvar_14;\\n  tmpvar_14 = -(q9);\\n  tmpvar_13 = fract(tmpvar_14);\\n  mat2 tmpvar_15;\\n  tmpvar_15[uint(0)].x = 1.0;\\n  tmpvar_15[uint(0)].y = -0.0;\\n  tmpvar_15[1u].x = 0.0;\\n  tmpvar_15[1u].y = 1.0;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_13)\\n   * tmpvar_15)) * aspect.yx);\\n  xlat_mutableneu = (3.0 * texture (sampler_main, fract((\\n    (xlat_mutableuv2 + 0.5)\\n   + ofs_2)))).xyz;\\n  ret1_3 = max (vec3(0.0, 0.0, 0.0), (xlat_mutableneu * (1.0 - \\n    (tmpvar_13 * tmpvar_13)\\n  )));\\n  float tmpvar_16;\\n  tmpvar_16 = fract((tmpvar_14 + 0.3333333));\\n  mat2 tmpvar_17;\\n  tmpvar_17[uint(0)].x = -0.4990803;\\n  tmpvar_17[uint(0)].y = -0.8665558;\\n  tmpvar_17[1u].x = 0.8665558;\\n  tmpvar_17[1u].y = -0.4990803;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_16)\\n   * tmpvar_17)) * aspect.yx);\\n  xlat_mutableneu = (3.0 * texture (sampler_main, fract((\\n    (xlat_mutableuv2 + 0.5)\\n   + ofs_2)))).xyz;\\n  ret1_3 = max (ret1_3, (xlat_mutableneu * (1.0 - \\n    (tmpvar_16 * tmpvar_16)\\n  )));\\n  float tmpvar_18;\\n  tmpvar_18 = fract((tmpvar_14 + 0.6666667));\\n  mat2 tmpvar_19;\\n  tmpvar_19[uint(0)].x = -0.5018377;\\n  tmpvar_19[uint(0)].y = 0.8649619;\\n  tmpvar_19[1u].x = -0.8649619;\\n  tmpvar_19[1u].y = -0.5018377;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_18)\\n   * tmpvar_19)) * aspect.yx);\\n  xlat_mutableneu = (3.0 * texture (sampler_main, fract((\\n    (xlat_mutableuv2 + 0.5)\\n   + ofs_2)))).xyz;\\n  ret1_3 = max (ret1_3, (xlat_mutableneu * (1.0 - \\n    (tmpvar_18 * tmpvar_18)\\n  )));\\n  float tmpvar_20;\\n  tmpvar_20 = fract((tmpvar_14 + 1.0));\\n  mat2 tmpvar_21;\\n  tmpvar_21[uint(0)].x = 0.9999949;\\n  tmpvar_21[uint(0)].y = 0.003185092;\\n  tmpvar_21[1u].x = -0.003185092;\\n  tmpvar_21[1u].y = 0.9999949;\\n  xlat_mutableuv2 = ((uv_1 * (\\n    (q13 * tmpvar_20)\\n   * tmpvar_21)) * aspect.yx);\\n  xlat_mutableneu = (3.0 * texture (sampler_main, fract((\\n    (xlat_mutableuv2 + 0.5)\\n   + ofs_2)))).xyz;\\n  ret1_3 = max (ret1_3, (xlat_mutableneu * (1.0 - \\n    (tmpvar_20 * tmpvar_20)\\n  )));\\n  vec2 tmpvar_22;\\n  tmpvar_22.x = (ret1_3.x + ret1_3.z);\\n  tmpvar_22.y = (ret1_3.x - ret1_3.y);\\n  xlat_mutableret2 = (((\\n    (texture (sampler_blur1, fract((xlat_mutablerss + (tmpvar_22 / 2.0)))).xyz * scale1)\\n   + bias1) / tmpvar_10) * 12.0);\\n  vec4 tmpvar_23;\\n  tmpvar_23.w = 1.0;\\n  tmpvar_23.xyz = ((ret1_3 + (\\n    ((bass_att * 0.004) / sqrt(dot (uv_1, uv_1)))\\n   * roam_sin).xyz) + (sqrt(xlat_mutableret2.zxy) * clamp (\\n    (1.0 - (ret1_3 * 4.0))\\n  , 0.0, 1.0)));\\n  ret = tmpvar_23.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:2,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.958,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ob_g:.1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:25.6,mv_y:9.6,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.n=0;a.reg26=0;a.uvx0=0;a.reg34=0;a.reg28=0;a.reg23=0;a.q25=0;a.angchg=0;a.reg20=0;a.reg15=0;a.reg10=0;a.q12=0;a.v3=0;a.q22=0;a.q21=0;a.diry=0;a.q13=0;a.q6=0;a.posx=0;a.fps_=0;a.reg25=0;a.uvx=0;a.q1=0;a.travel=0;a.posz=0;a.q5=0;a.movz=0;a.dirz=0;a.dec_s=0;a.reg16=0;a.slow=0;a.reg36=0;a.reg22=0;a.uvy=0;a.rotz=0;a.dist_=0;a.q23=0;a.q24=0;a.reg24=0;a.cran0=0;a.vx=0;a.ran2=0;a.q11=0;a.q10=0;a.reg14=0;a.posy=0;a.vy=0;a.vz=0;a.reg31=0;a.dirx=0;a.dec_m=0;a.q4=0;a.start=0;a.reg12=\\n0;a.reg13=0;a.c2=0;a.reg37=0;a.s3=0;a.yslope=0;a.q16=0;a.xslope=0;a.q26=0;a.reg38=0;a.reg35=0;a.reg11=0;a.tx=0;a.avg=0;a.uvz=0;a.c3=0;a.uvy0=0;a.reg27=0;a.q19=0;a.beat=0;a.q17=0;a.vol=0;a.reg32=0;a.reg21=0;a.uvz0=0;a.len=0;a.reg18=0;a.reg30=0;a.q27=0;a.slen=0;a.q14=0;a.dist=0;a.reg17=0;a.v1=0;a.speed=0;a.s1=0;a.t0=0;a.s2=0;a.ran1=0;a.reg33=0;a.q7=0;a.ds=0;a.q28=0;a.ty=0;a.c1=0;a.v2=0;a.q20=0;a.q8=0;a.avg=.01;a.q7=.2;a.q8=div(randint(200),100)-1;a.q16=1.2;a.q18=randint(.8)+.1;a.q17=2.6;a.start=1;a.travel=\\n0;a.rotz=0;a.look=0;a.slow=0;a.t0=a.time+3;a.lampx=.5;a.lampy=.5;a.cran0=randint(1);for(var b=a.n=0;1E4>b;b++)a.gmegabuf[Math.floor(a.n)]=0,a.n+=1;for(b=a.n=0;1E4>b;b++)a.megabuf[Math.floor(a.n)]=0,a.n+=1;a.trelx=0;a.trely=0;a.trelz=0;a.reg20=1;a.reg21=0;a.reg22=0;a.reg23=0;a.reg24=1;a.reg25=0;a.reg26=0;a.reg27=0;a.reg28=1;b=0;do{b+=1;var c;a.ran1=div(randint(800),100);a.ran2=div(randint(800),100);a.ran3=div(randint(800),100);a.posx=randint(10)-5;a.posy=randint(10)-5;a.posz=randint(10)-5;a.c1=Math.cos(a.ran1);\\na.c2=Math.cos(a.ran2);a.c3=Math.cos(a.ran3);a.s1=Math.sin(a.ran1);a.s2=Math.sin(a.ran2);a.s3=Math.sin(a.ran3);a.reg20=a.c2*a.c1;a.reg21=a.c2*a.s1;a.reg22=-a.s2;a.reg23=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg24=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg25=a.s3*a.c2;a.reg26=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg27=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg28=a.c3*a.c2;a.dist=.001;var d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,\\n8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=\\na.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)*a.q17+a.uvx0,a.uvy=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)*a.q17+a.uvy0,a.uvz=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)*a.q17+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.05;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(c)&&\\n1048576>d);d=.06>a.dist?1:0}while(.00001<Math.abs(d)&&1048576>b);\",frame_eqs_str:\"a.fps_=0*a.fps_+1*(.00001<Math.abs(25>=a.fps?1:0)?a.fps:25+.5*(a.fps-25));a.dec_s=1-div(.03*30,a.fps_);a.dec_m=1-div(3,a.fps_);a.beat=a.time>a.t0+3?1:0;a.t0=.00001<Math.abs(a.beat)?a.time:a.t0;a.speed=div(Math.min(.2,a.dist_-.02)*(1+2*a.avg)*(1-0*a.slow)*.1,a.q7);a.ds=a.ds*a.dec_s+div((1-a.dec_s)*a.speed*.4,a.fps_);a.rotz=.00001<Math.abs(2<a.travel?1:0)?.5:0;a.vol=a.vol*a.dec_m+div((1-a.dec_m)*(a.bass_att+a.mid_att+a.treb_att),4);a.q1=div(a.vol,2)+.3;a.dirx=a.reg26;a.diry=a.reg27;\\na.dirz=a.reg28;a.posx+=a.ds*a.dirx;a.posy+=a.ds*a.diry;a.posz+=a.ds*a.dirz;a.q4=a.posx;a.q5=a.posy;a.q6=a.posz;a.angchg=(.1-a.dist_)*(.1>a.dist_?1:0);a.travel=.00001<Math.abs(0<a.angchg?1:0)?0:a.travel+a.ds;a.v1=a.v1*a.dec_s+div((1-a.dec_s)*a.rotz*a.ds,2);a.v2=a.v2*a.dec_s+div((1-a.dec_s)*a.angchg*a.xslope,a.fps_);a.v3=a.v3*a.dec_s+(1-a.dec_s)*(div(a.angchg*a.yslope,a.fps_)+2*a.v1*Math.sin(.1*a.time));a.reg30=a.reg20;a.reg31=a.reg21;a.reg32=a.reg22;a.reg33=a.reg23;a.reg34=a.reg24;a.reg35=a.reg25;\\na.reg36=a.reg26;a.reg37=a.reg27;a.reg38=a.reg28;a.n=0;for(var b=a.avg=0;5>b;b++){a.n+=1;a.ran1=div(randint(100),100);a.ran2=div(randint(100),200)-.25;a.tx=Math.cos(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.ty=Math.sin(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.c1=Math.cos(a.v1);a.c2=Math.cos(a.v2+a.ty);a.c3=Math.cos(a.v3+a.tx);a.s1=Math.sin(a.v1);a.s2=Math.sin(a.v2+a.ty);a.s3=Math.sin(a.v3+a.tx);a.reg10=a.c2*a.c1;a.reg11=a.c2*a.s1;a.reg12=-a.s2;a.reg13=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg14=a.s3*a.s2*a.s1+a.c3*\\na.c1;a.reg15=a.s3*a.c2;a.reg16=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg17=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg18=a.c3*a.c2;a.reg20=a.reg30;a.reg21=a.reg31;a.reg22=a.reg32;a.reg23=a.reg33;a.reg24=a.reg34;a.reg25=a.reg35;a.reg26=a.reg36;a.reg27=a.reg37;a.reg28=a.reg38;a.q20=a.reg10*a.reg20+a.reg11*a.reg23+a.reg12*a.reg26;a.q21=a.reg10*a.reg21+a.reg11*a.reg24+a.reg12*a.reg27;a.q22=a.reg10*a.reg22+a.reg11*a.reg25+a.reg12*a.reg28;a.q23=a.reg13*a.reg20+a.reg14*a.reg23+a.reg15*a.reg26;a.q24=a.reg13*a.reg21+a.reg14*a.reg24+\\na.reg15*a.reg27;a.q25=a.reg13*a.reg22+a.reg14*a.reg25+a.reg15*a.reg28;a.q26=a.reg16*a.reg20+a.reg17*a.reg23+a.reg18*a.reg26;a.q27=a.reg16*a.reg21+a.reg17*a.reg24+a.reg18*a.reg27;a.q28=a.reg16*a.reg22+a.reg17*a.reg25+a.reg18*a.reg28;a.reg20=a.q20;a.reg21=a.q21;a.reg22=a.q22;a.reg23=a.q23;a.reg24=a.q24;a.reg25=a.q25;a.reg26=a.q26;a.reg27=a.q27;a.reg28=a.q28;a.dist=.002;var c,d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=\\na.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>\\na.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)*a.q17+a.uvx0,a.uvy=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)*a.q17+a.uvy0,a.uvz=(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)*a.q17+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.1;c=(.6>a.dist?1:0)*(30<a.len?\\n1:0)}while(.00001<Math.abs(c)&&1048576>d);a.megabuf[Math.floor(a.n)]=a.megabuf[Math.floor(a.n)]*a.dec_s+(1-a.dec_s)*a.dist;a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5))}a.n=0;for(b=a.avg=0;5>b;b++)a.n+=1,a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5));a.xslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[1]-a.megabuf[3]),-3),3);a.yslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[4]-a.megabuf[2]),-3),3);a.monitor=a.avg;a.dist_=a.dist_*a.dec_s+(1-a.dec_s)*a.dist;a.q10=a.ds*a.q7;a.q14=Math.abs(a.ds)+\\n2*(Math.abs(a.v1)+Math.abs(a.v2)+Math.abs(a.v3))+.05*a.start;a.q19=.6+.4*Math.sin(.02*a.time+6*a.cran0);a.start*=.9;a.warp=0;a.zoom=1;a.dx=div(-a.v2,a.q16);a.dy=div(a.v3,a.q16);a.rot=a.v1;a.vx-=div(a.v2,a.q16);a.vy+=div(a.v3,a.q16);a.vz+=a.v1;a.q11=a.vx;a.q12=a.vy;a.movz+=a.ds;a.q13=a.movz;a.monitor=a.q13;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"float sustain;\\nfloat ffac;\\nfloat xlat_mutabledist;\\nfloat xlat_mutablelimit;\\nfloat xlat_mutablestruc;\\nvec2 xlat_mutableuv1;\\nvec3 xlat_mutableuv2;\\n shader_body { \\n  mat3 tmpvar_1;\\n  tmpvar_1[uint(0)].x = q20;\\n  tmpvar_1[uint(0)].y = q23;\\n  tmpvar_1[uint(0)].z = q26;\\n  tmpvar_1[1u].x = q21;\\n  tmpvar_1[1u].y = q24;\\n  tmpvar_1[1u].z = q27;\\n  tmpvar_1[2u].x = q22;\\n  tmpvar_1[2u].y = q25;\\n  tmpvar_1[2u].z = q28;\\n  vec3 tmpvar_2;\\n  tmpvar_2.x = q4;\\n  tmpvar_2.y = q5;\\n  tmpvar_2.z = q6;\\n  sustain = (0.98 - q14);\\n  ffac = q17;\\n  vec2 uv_3;\\n  uv_3 = (((uv - 0.5) * (1.0 - \\n    (q10 / (1.0 - ((texture (sampler_blur1, uv).xyz * scale1) + bias1).z))\\n  )) + 0.5);\\n  xlat_mutableuv1 = (((uv_orig - 0.5) * aspect.xy) * q16);\\n  vec4 tmpvar_4;\\n  tmpvar_4 = fract((8.0 * texture (sampler_noise_lq, (uv_3 + rand_frame.yz))));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = tmpvar_4.xyz;\\n  if ((tmpvar_4.y > (0.4 * rad))) {\\n    vec3 tmpvar_6;\\n    tmpvar_6 = (tmpvar_4.xyz - vec3(0.5, 0.5, 0.5));\\n    vec4 nb2_7;\\n    vec4 nb_8;\\n    vec2 tmpvar_9;\\n    tmpvar_9 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 - texsize.zw)).yz)\\n     + vec2(0.5, 0.5))));\\n    nb_8.x = (1.0 - (tmpvar_9.y + (0.015625 * \\n      (tmpvar_9.x - 0.5)\\n    )));\\n    vec2 tmpvar_10;\\n    tmpvar_10 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(1.0, -1.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb_8.y = (1.0 - (tmpvar_10.y + (0.015625 * \\n      (tmpvar_10.x - 0.5)\\n    )));\\n    vec2 tmpvar_11;\\n    tmpvar_11 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + texsize.zw)).yz)\\n     + vec2(0.5, 0.5))));\\n    nb_8.z = (1.0 - (tmpvar_11.y + (0.015625 * \\n      (tmpvar_11.x - 0.5)\\n    )));\\n    vec2 tmpvar_12;\\n    tmpvar_12 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(-1.0, 1.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb_8.w = (1.0 - (tmpvar_12.y + (0.015625 * \\n      (tmpvar_12.x - 0.5)\\n    )));\\n    vec2 tmpvar_13;\\n    tmpvar_13 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(0.0, -1.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb2_7.x = (1.0 - (tmpvar_13.y + (0.015625 * \\n      (tmpvar_13.x - 0.5)\\n    )));\\n    vec2 tmpvar_14;\\n    tmpvar_14 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(1.0, 0.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb2_7.y = (1.0 - (tmpvar_14.y + (0.015625 * \\n      (tmpvar_14.x - 0.5)\\n    )));\\n    vec2 tmpvar_15;\\n    tmpvar_15 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(0.0, 1.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb2_7.z = (1.0 - (tmpvar_15.y + (0.015625 * \\n      (tmpvar_15.x - 0.5)\\n    )));\\n    vec2 tmpvar_16;\\n    tmpvar_16 = (0.0078125 * floor((\\n      (128.0 * texture (sampler_pc_main, (uv_3 + (texsize.zw * vec2(-1.0, 0.0)))).yz)\\n     + vec2(0.5, 0.5))));\\n    nb2_7.w = (1.0 - (tmpvar_16.y + (0.015625 * \\n      (tmpvar_16.x - 0.5)\\n    )));\\n    vec4 tmpvar_17;\\n    tmpvar_17 = min (nb_8, nb2_7);\\n    nb_8.zw = tmpvar_17.zw;\\n    nb_8.xy = min (tmpvar_17.xy, tmpvar_17.zw);\\n    xlat_mutabledist = (min (nb_8.x, nb_8.y) + ((0.006 * tmpvar_6.x) * abs(tmpvar_6.y)));\\n  } else {\\n    xlat_mutabledist = tmpvar_5.x;\\n  };\\n  vec2 tmpvar_18;\\n  tmpvar_18 = (0.0078125 * floor((\\n    (128.0 * texture (sampler_pc_main, uv_3).yz)\\n   + vec2(0.5, 0.5))));\\n  xlat_mutabledist = (min (xlat_mutabledist, (1.0 - \\n    (tmpvar_18.y + (0.015625 * (tmpvar_18.x - 0.5)))\\n  )) - (q10 * 0.8));\\n  xlat_mutablelimit = (15.0 + (10.0 * xlat_mutabledist));\\n  vec3 tmpvar_19;\\n  tmpvar_19.xy = (xlat_mutableuv1 * xlat_mutabledist);\\n  tmpvar_19.z = xlat_mutabledist;\\n  xlat_mutableuv2 = (((tmpvar_19 / q7) * tmpvar_1) + tmpvar_2);\\n  xlat_mutableuv2 = ((fract(\\n    ((xlat_mutableuv2 / 8.0) + 0.5)\\n  ) - 0.5) * 8.0);\\n  int iterations_21;\\n  vec3 zz0_22;\\n  vec3 zz_23;\\n  zz_23 = xlat_mutableuv2;\\n  zz0_22 = (xlat_mutableuv2 + q8);\\n  iterations_21 = int((8.0 - float(\\n    (xlat_mutabledist > 0.8)\\n  )));\\n  for (int n_20 = 0; n_20 <= iterations_21; n_20++) {\\n    zz_23 = ((2.0 * clamp (zz_23, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_23);\\n    zz_23 = (zz_23 * (clamp (\\n      max ((0.25 / dot (zz_23, zz_23)), 0.25)\\n    , 0.0, 1.0) * 4.0));\\n    zz_23 = ((ffac * zz_23) + zz0_22);\\n  };\\n  xlat_mutablestruc = (sqrt(dot (zz_23.xz, zz_23.xz)) / xlat_mutablelimit);\\n  vec2 tmpvar_24;\\n  tmpvar_24 = (0.0078125 * floor((\\n    (128.0 * vec2((1.0 - xlat_mutabledist)))\\n   + vec2(0.5, 0.5))));\\n  vec2 tmpvar_25;\\n  tmpvar_25.x = ((64.0 * (\\n    (1.0 - xlat_mutabledist)\\n   - tmpvar_24.x)) + 0.5);\\n  tmpvar_25.y = tmpvar_24.x;\\n  vec3 tmpvar_26;\\n  float tmpvar_27;\\n  tmpvar_27 = (q14 * 2.0);\\n  tmpvar_26.x = (((1.0 - sustain) * xlat_mutablestruc) + (sustain * mix (texture (sampler_main, uv_3).xyz, \\n    ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1)\\n  , vec3(tmpvar_27)).x));\\n  tmpvar_26.yz = tmpvar_25;\\n  vec3 tmpvar_28;\\n  tmpvar_28.y = 1.0;\\n  tmpvar_28.x = sustain;\\n  tmpvar_28.z = 1.0;\\n  vec3 tmpvar_29;\\n  tmpvar_29.y = 0.0;\\n  tmpvar_29.x = 0.003921569;\\n  tmpvar_29.z = (0.01568628 * (0.2 + rad));\\n  vec4 tmpvar_30;\\n  tmpvar_30.w = 1.0;\\n  tmpvar_30.xyz = mix (tmpvar_26, ((\\n    mix (texture (sampler_main, uv_3).xyz, ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1), vec3(tmpvar_27))\\n   * tmpvar_28) - tmpvar_29), vec3(clamp ((\\n    sqrt(dot (zz_23, zz_23))\\n   - xlat_mutablelimit), 0.0, 1.0)));\\n  ret = tmpvar_30.xyz;\\n }\",comp:\"float xlat_mutableinten;\\nfloat xlat_mutabletmp;\\nvec2 xlat_mutableuv1;\\nvec2 xlat_mutableuv2;\\nfloat xlat_mutablez;\\n shader_body { \\n  float t_rel_2;\\n  vec3 ret1_3;\\n  float struc_4;\\n  xlat_mutableuv1 = ((uv * aspect.xy) - vec2(0.5, 0.5));\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_main, uv);\\n  float tmpvar_6;\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_blur1, uv);\\n  tmpvar_6 = mix (min ((1.0 - tmpvar_5.z), (1.0 - \\n    ((texture (sampler_blur2, uv).xyz * scale2) + bias2)\\n  .z)), (1.0 - (\\n    (tmpvar_7.xyz * scale1)\\n   + bias1).z), 0.5);\\n  struc_4 = ((mix (tmpvar_5.xyz, \\n    ((tmpvar_7.xyz * scale1) + bias1)\\n  , vec3(tmpvar_6)).x * (1.0 - tmpvar_6)) * tmpvar_6);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = q11;\\n  tmpvar_8.y = q12;\\n  xlat_mutableuv2 = ((xlat_mutableuv1 * 0.2) - (tmpvar_8 * 0.2));\\n  vec2 uvi_9;\\n  uvi_9 = (xlat_mutableuv2 * 4.0);\\n  float zv_10;\\n  zv_10 = (0.008 * time);\\n  xlat_mutabletmp = clamp (dot ((texture (sampler_noise_hq, \\n    (xlat_mutableuv2 + (0.03 * ((\\n      (dot (texture (sampler_noise_hq, uvi_9), vec4(0.32, 0.49, 0.29, 0.0)) + (dot (texture (sampler_noise_hq, (\\n        (uvi_9 * 2.0)\\n       + zv_10)), vec4(0.32, 0.49, 0.29, 0.0)) / 2.0))\\n     + \\n      (dot (texture (sampler_noise_hq, ((uvi_9 * 4.0) + (2.0 * zv_10))), vec4(0.32, 0.49, 0.29, 0.0)) / 4.0)\\n    ) + (\\n      dot (texture (sampler_noise_hq, ((uvi_9 * 8.0) + (4.0 * zv_10))), vec4(0.32, 0.49, 0.29, 0.0))\\n     / 8.0))))\\n  ) - 0.4), vec4(0.32, 0.49, 0.29, 0.0)), 0.0, 1.0);\\n  float tmpvar_11;\\n  tmpvar_11 = clamp ((pow (xlat_mutabletmp, 1.2) * sign(xlat_mutabletmp)), 0.0, 1.0);\\n  xlat_mutabletmp = tmpvar_11;\\n  vec3 tmpvar_12;\\n  tmpvar_12.xy = vec2(0.1, 0.1);\\n  tmpvar_12.z = (1.2 - uv.y);\\n  ret1_3 = ((tmpvar_12 + 0.07) + ((\\n    clamp (((tmpvar_6 * 2.0) - 1.5), 0.0, 1.0)\\n   * tmpvar_11) * 4.0));\\n  t_rel_2 = (q13 * 6.0);\\n  for (int n_1 = 1; n_1 <= 3; n_1++) {\\n    xlat_mutablez = (1.0 - fract((\\n      (float(n_1) / 3.0)\\n     - \\n      (fract(-(t_rel_2)) / 3.0)\\n    )));\\n    xlat_mutableinten = (((1.0 - xlat_mutablez) * xlat_mutablez) * 2.0);\\n    vec2 tmpvar_13;\\n    tmpvar_13.x = q11;\\n    tmpvar_13.y = q12;\\n    xlat_mutableuv2 = (((xlat_mutablez * xlat_mutableuv1) / 4.0) - (tmpvar_13 / 6.0));\\n    vec2 uvi_14;\\n    uvi_14 = (xlat_mutableuv2 * 4.0);\\n    float zv_15;\\n    zv_15 = (0.008 * time);\\n    xlat_mutabletmp = clamp (dot ((\\n      (texture (sampler_noise_hq, (xlat_mutableuv2 + (0.03 * (\\n        ((dot (texture (sampler_noise_hq, uvi_14), vec4(0.32, 0.49, 0.29, 0.0)) + (dot (texture (sampler_noise_hq, \\n          ((uvi_14 * 2.0) + zv_15)\\n        ), vec4(0.32, 0.49, 0.29, 0.0)) / 2.0)) + (dot (texture (sampler_noise_hq, (\\n          (uvi_14 * 4.0)\\n         + \\n          (2.0 * zv_15)\\n        )), vec4(0.32, 0.49, 0.29, 0.0)) / 4.0))\\n       + \\n        (dot (texture (sampler_noise_hq, ((uvi_14 * 8.0) + (4.0 * zv_15))), vec4(0.32, 0.49, 0.29, 0.0)) / 8.0)\\n      )))) - (xlat_mutablez * 0.5))\\n     - 0.3), vec4(0.32, 0.49, 0.29, 0.0)), 0.0, 1.0);\\n    xlat_mutabletmp = (((\\n      clamp ((xlat_mutabletmp * sign(xlat_mutabletmp)), 0.0, 1.0)\\n     * xlat_mutableinten) * q1) * 2.0);\\n    ret1_3 = (((ret1_3 + \\n      ((vec3(4.0, 3.0, 0.8) * q1) * struc_4)\\n    ) * clamp (\\n      (1.0 - xlat_mutabletmp)\\n    , 0.0, 1.0)) + xlat_mutabletmp);\\n  };\\n  vec4 tmpvar_16;\\n  tmpvar_16.w = 1.0;\\n  tmpvar_16.xyz = (1.0 - exp((-1.6 * ret1_3)));\\n  ret = tmpvar_16.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,additivewave:1,wave_dots:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.527,wave_smoothing:0,wave_mystery:.6,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_g:.49,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b2x:.6,b3x:.4,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:5,additive:1,num_inst:256,x:.26,y:.2,rad:.39317,tex_zoom:.9355,g2:0,a2:.2,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.n=0;a.w=0;a.high=0;a.q12=0;a.arg=0;a.q13=0;a.y0=0;a.flen=0;a.x0=0;a.k1=0;a.q11=0;a.z0=0;a.exc=0;a.p2=0;a.p1=0;a.a0=0;a.q2=0;a.slen=0;a.q14=0;a.q3=0;a.q32=0;a.rad0=0;\",frame_eqs_str:\"a.n=a.instance;a.flen=a.reg00;a.slen=div(a.reg00,2);a.z0=10;a.y0=div(a.gmegabuf[Math.floor(2E3+a.n+a.flen)],a.z0);a.x0=div(a.gmegabuf[Math.floor(2E3+a.n)],a.z0);a.a0=a.gmegabuf[Math.floor(a.n+1E4)];a.k1=div(a.instance,a.num_inst)-.5;a.x=.5+a.x0+Math.sin(8*a.k1*Math.sin(.07*a.q12))*Math.sin(.13*a.q11)*a.q3*.7;a.y=.5+a.q32*(a.y0+Math.sin(8*a.k1*Math.sin(.1*a.q14))*Math.sin(.2*a.q13)*a.q3*.7);a.arg=div(a.q2,8);a.high=Math.exp(-500*pow(a.arg+.5-div(a.instance,a.num_inst),2));a.high+=\\nMath.exp(-500*pow(-a.arg+.5-div(a.instance,a.num_inst),2));a.exc=sqrt(pow(a.x-.5,2)+pow(a.y-.5,2));a.rad0=above(a.z0,0)*Math.min(.1,div(a.a0,60))+.005;a.rad0=a.rad0*(1+2*a.exc)*(1+a.high);a.p1=.5+div(Math.sin(a.q12),2);a.p2=.5+div(Math.sin(1.4*a.q13),2);a.exc=pow(a.x-a.p1,2)+pow(a.y-a.p2,2);a.rad=Math.min(a.rad0*(1+div(.004*a.q3,Math.abs(a.exc))),1);a.a=Math.min(8*a.a0+.4,1);a.k1=5*div(a.instance,a.num_inst)+a.high;a.w=1-Math.exp(div(-a.treb_att,2)-.5);a.g=a.w+(1-a.w)*Math.sin(a.k1);a.r=a.w+(1-a.w)*\\nMath.sin(a.k1-div(6.28,3));a.b=a.w+(1-a.w)*Math.sin(a.k1-div(12.56,3));a.a2=div(a.a,4);a.g2=0*a.g;a.b2=0*a.b;a.r2=0*a.r;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,sep:120,spectrum:1,additive:1,scaling:7.52386,smoothing:0,r:0,a:.7},init_eqs_str:\"a.flen=0;a.n=0;a.vol=0;a.chg=0;a.dec=0;a.q1=0;a.q2=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.flen=a.reg00;a.n=Math.floor(a.sample*a.flen);a.vol=(a.value1+a.value2)*(1+div(.1,a.sample+.03));a.chg=Math.min(Math.max(a.vol-a.gmegabuf[Math.floor(a.n)],-1),1);a.dec=.00001<Math.abs(0<a.chg?1:0)?1-.3*a.chg:1+.2*a.chg;a.chg=a.q1-a.q2;a.dec=.94-Math.abs(a.chg)*(.00001<Math.abs(0<a.chg?1:0)?.2:.1);a.dec=Math.min(Math.max(a.dec,0),1);a.gmegabuf[Math.floor(a.n)]=a.gmegabuf[Math.floor(a.n)]*a.dec+a.vol*(1-a.dec);a.dec=div(a.q2,4);a.dec=Math.max(Math.min(a.dec,1),.1);a.gmegabuf[Math.floor(a.n)]=\\na.gmegabuf[Math.floor(a.n)]*a.dec+a.gmegabuf[Math.floor(a.n+(.8>a.q2?1:0))]*(1-a.dec);a.gmegabuf[Math.floor(a.n+1E4)]=.2*a.gmegabuf[Math.floor(a.n+1E4)]+div(.8*a.vol,3);a.a=0;a.x=a.sample;a.y=.2+.23*a.gmegabuf[Math.floor(a.n+0)];\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.n=0;a.sw2=0;a.in1=0;a.q12=0;a.q13=0;a.sw3=0;a.dif=0;a.q1=0;a.lev3=0;a.flen=0;a.lev4=0;a.in2=0;a.lev1=0;a.k1=0;a.q11=0;a.ofs=0;a.dec_m=0;a.i=0;a.k=0;a.m2=0;a.vol=0;a.q2=0;a.slen=0;a.m1=0;a.q14=0;a.sw1=0;a.lev2=0;a.q3=0;a.reg00=0;a.vol_=0;a.dec=0;a.q32=0;a.sw4=0;for(var b=a.n=0;5E4>b;b++)a.gmegabuf[Math.floor(a.n)]=0,a.megabuf[Math.floor(a.n)]=0,a.n+=1;a.sw1=randint(8);a.sw2=randint(8);a.sw3=randint(8);a.sw4=randint(8);\",frame_eqs_str:\"a.flen=512;a.reg00=a.flen;a.slen=div(a.flen,2);a.dec_m=pow(.94,div(30,a.fps));a.n=0;for(var b=a.vol=0;b<a.slen;b++)a.vol+=div(pow(a.gmegabuf[Math.floor(a.n)],2),a.flen),a.n+=1;a.vol=div(sqrt(a.vol),2);a.vol_=a.vol_*a.dec_m+(1-a.dec_m)*a.vol;a.lev1=a.lev1*a.dec_m+(1-a.dec_m)*a.gmegabuf[1];a.lev2=a.lev2*a.dec_m+(1-a.dec_m)*a.gmegabuf[20];a.lev3=a.lev3*a.dec_m+(1-a.dec_m)*a.gmegabuf[50];a.lev4=a.lev4*a.dec_m+(1-a.dec_m)*a.gmegabuf[100];a.sw1+=div(0<a.lev1-a.gmegabuf[1]?1:0,a.fps);\\na.sw2+=div(0<a.lev2-a.gmegabuf[20]?1:0,a.fps);a.sw3+=div(0<a.lev3-a.gmegabuf[50]?1:0,a.fps);a.sw4+=div(0<a.lev4-a.gmegabuf[100]?1:0,a.fps);a.dif=16*(1+Math.sin(div(a.sw3,4)))+2;a.ofs=8*Math.sin(div(a.sw2,3));a.n=0;a.k1=0*a.frame;for(b=0;b<a.slen;b++)a.m1=mod(a.n,a.slen),a.m2=mod(a.n+a.ofs,a.slen),a.k=mod(a.m1+a.dif,a.slen),a.i=mod(a.slen+a.m2-a.dif,a.slen),a.in1=1.2*div(a.gmegabuf[Math.floor(a.m1)]-a.gmegabuf[Math.floor(a.k)],pow(a.vol_+.03,.8)),a.in2=1.2*div(a.gmegabuf[Math.floor(a.m2)]-a.gmegabuf[Math.floor(a.i)],\\npow(a.vol_+.03,.8)),a.dec=.00001<Math.abs(pow(a.gmegabuf[Math.floor(2E3+a.n)],2)+pow(a.gmegabuf[Math.floor(2E3+a.flen+a.n)],2)>a.in1*a.in1+a.in2*a.in2?1:0)?.8:.94,a.dec=pow(a.dec,div(30,a.fps)),a.gmegabuf[Math.floor(2E3+a.n)]=a.gmegabuf[Math.floor(2E3+a.n)]*a.dec+(1-a.dec)*a.in1,a.gmegabuf[Math.floor(2E3+a.flen+a.n)]=a.gmegabuf[Math.floor(2E3+a.flen+a.n)]*a.dec+(1-a.dec)*a.in2,a.n+=1;a.q1=div(a.bass+a.treb+a.mid,3);a.q2=div(a.bass_att+a.treb_att+a.mid_att,3);a.q3=a.vol_;a.q11=a.sw1;a.q12=a.sw2;a.q13=\\na.sw3;a.q14=a.sw4;a.rot=0;a.zoom=.98;a.warp=.3;a.rot=0;a.q32=a.aspecty;a.monitor=a.dif;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = ((texture (sampler_main, uv).xyz * clamp (\\n    (q1 - 0.8)\\n  , 0.0, 1.0)) * 0.92);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = (texture (sampler_main, uv).xyz + ((texture (sampler_blur2, uv).xyz * scale2) + bias2));\\n  ret_1 = (ret_1 + ((0.8 * \\n    (hue_shader - 0.8)\\n  ) * (1.0 - uv.y)));\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:4,additivewave:1,wave_dots:1,modwavealphabyvolume:1,darken:1,wave_a:.33064,wave_scale:.897961,wave_smoothing:.108,wave_mystery:.1,modwavealphastart:.72,modwavealphaend:1.28,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:.5,wave_b:.5,wave_y:.54,ob_r:1,ob_g:1,ob_b:1,ib_size:.26,mv_x:24.799994,mv_dy:.16,mv_l:1.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:352,usedots:1,additive:1,scaling:.038558,smoothing:.2,g:0},init_eqs_str:\"a.t02=0;a.q1=0;a.ratio=0;a.ampl=0;a.x1=0;a.y1=0;\",frame_eqs_str:\"a.q1=a.bass_att;\",point_eqs_str:\"a.r=Math.abs(Math.sin(div(a.frame,38)));a.g=.5*Math.abs(Math.cos(div(a.frame,45)));a.b=.5*Math.abs(Math.sin(div(a.frame,133)));a.a=.3;a.t02+=div(a.q1,10);a.ratio=Math.sin(div(a.frame,49));a.ampl=.01+.4*sqr(Math.sin(div(a.frame,18))*Math.cos(div(a.frame,123)));a.x1=div(a.r-.5,15)+.5+a.ampl*Math.sin(6.28*a.sample);a.y1=div(a.b-.5,15)+.5+a.ampl*Math.cos(6.28*a.sample);a.x=a.x1+.2*(a.ampl+a.ratio)*Math.sin(6.28*a.sample*a.ratio*7.3);a.y=a.y1+.2*(a.ampl+a.ratio)*Math.cos(37.68*a.sample);\\n\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.q25=0;a.index=0;a.q18=0;a.q22=0;a.q21=0;a.q29=0;a.q6=0;a.movex=0;a.q1=0;a.dec_med=0;a.q5=0;a.index3=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.mov=0;a.avg=0;a.beat=0;a.p1=0;a.dx1=0;a.peak=0;a.q2=0;a.q27=0;a.clear=0;a.p3=0;a.q3=0;a.t0=0;a.rot1=0;a.q32=0;a.q28=0;a.q20=0;a.clear=.5;\",frame_eqs_str:\"a.dec_med=pow(.96,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,4);a.index2=mod(a.index2+a.is_beat*bnot(a.index),8);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.q20=a.avg;a.q21=\\na.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.p3=a.dec_med*a.p3+(1-a.dec_med)*a.p2;a.rott=div(3.1416*a.p3,4);a.rot1+=a.q26;a.q25=.01*a.rot1;a.q27=8-a.index;a.q28=a.index3;a.dx1=a.dec_med*a.dx1+(1-a.dec_med)*bnot(a.index2);a.q29=a.dx1;a.monitor=a.q29;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.q5=1.5*Math.cos(div(a.time,9));a.q6=.5*Math.sin(div(a.time,\\n7));a.clear=a.clear*a.dec_med+1-a.dec_med;a.q32=a.clear;a.mov=bnot(a.q24)*a.movex+(div(randint(100),100)-50)*a.q2;a.movex+=div(.2,a.fps)*a.q2;a.q18=a.movex;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 uv6_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_3;\\n  tmpvar_3 = (rand_frame * 64.0).x;\\n  uv6_1 = (0.5 * cos((\\n    ((tmpvar_2 * mat2(0.7, -0.7, 0.7, 0.7)) * 17.0)\\n   + \\n    (rand_frame * 6.0)\\n  .xy)));\\n  float x_4;\\n  x_4 = (uv6_1.x + uv6_1.y);\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ((0.147 * (\\n    (texture (sampler_main, (uv + clamp ((\\n      (sin(tmpvar_3) / cos(tmpvar_3))\\n     * \\n      normalize(tmpvar_2)\\n    ), vec2(-8.0, -8.0), vec2(8.0, 8.0)))).xyz + (0.15 * (vec3((0.01 / \\n      sqrt((x_4 * x_4))\\n    )) * roam_cos.xyz)))\\n   - 0.02)) + ((0.85 * \\n    (texture (sampler_main, uv_orig).xyz - 0.002)\\n  ) * q32));\\n  ret = tmpvar_5.xyz;\\n }\",comp:\"vec2 xlat_mutabledz;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutableuv3;\\nvec2 xlat_mutableuv4;\\n shader_body { \\n  vec2 uv_1;\\n  float inten_2;\\n  float dist_3;\\n  vec3 ret_4;\\n  uv_1 = (uv - 0.5);\\n  float tmpvar_5;\\n  tmpvar_5 = (time / 2.0);\\n  dist_3 = (1.0 - fract(tmpvar_5));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 2.0);\\n  vec2 tmpvar_6;\\n  tmpvar_6.y = 0.4;\\n  tmpvar_6.x = q18;\\n  xlat_mutableuv3 = (((2.0 * uv_1) * dist_3) + tmpvar_6);\\n  vec2 tmpvar_7;\\n  tmpvar_7.y = 0.0;\\n  tmpvar_7.x = texsize.z;\\n  vec2 tmpvar_8;\\n  tmpvar_8.y = 0.0;\\n  tmpvar_8.x = texsize.z;\\n  xlat_mutabledz.x = (inten_2 * dot ((texture (sampler_main, \\n    (xlat_mutableuv3 + tmpvar_7)\\n  ).xyz - texture (sampler_main, \\n    (xlat_mutableuv3 - tmpvar_8)\\n  ).xyz), vec3(0.32, 0.49, 0.29)));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = 0.0;\\n  tmpvar_9.y = texsize.w;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = 0.0;\\n  tmpvar_10.y = texsize.w;\\n  xlat_mutabledz.y = (inten_2 * dot ((texture (sampler_main, \\n    (xlat_mutableuv3 + tmpvar_9)\\n  ).xyz - texture (sampler_main, \\n    (xlat_mutableuv3 - tmpvar_10)\\n  ).xyz), vec3(0.32, 0.49, 0.29)));\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (texture (sampler_main, xlat_mutableuv3).xyz * inten_2));\\n  dist_3 = (1.0 - fract((0.3333333 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 2.0);\\n  vec2 tmpvar_11;\\n  tmpvar_11.y = 0.4;\\n  tmpvar_11.x = q18;\\n  xlat_mutableuv3 = (((2.0 * uv_1) * dist_3) + tmpvar_11);\\n  vec2 tmpvar_12;\\n  tmpvar_12.y = 0.0;\\n  tmpvar_12.x = texsize.z;\\n  vec2 tmpvar_13;\\n  tmpvar_13.y = 0.0;\\n  tmpvar_13.x = texsize.z;\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_12)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_13)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  vec2 tmpvar_14;\\n  tmpvar_14.x = 0.0;\\n  tmpvar_14.y = texsize.w;\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = 0.0;\\n  tmpvar_15.y = texsize.w;\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_14)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_15)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_2));\\n  dist_3 = (1.0 - fract((0.6666667 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 2.0);\\n  vec2 tmpvar_16;\\n  tmpvar_16.y = 0.4;\\n  tmpvar_16.x = q18;\\n  xlat_mutableuv3 = (((2.0 * uv_1) * dist_3) + tmpvar_16);\\n  vec2 tmpvar_17;\\n  tmpvar_17.y = 0.0;\\n  tmpvar_17.x = texsize.z;\\n  vec2 tmpvar_18;\\n  tmpvar_18.y = 0.0;\\n  tmpvar_18.x = texsize.z;\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_17)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_18)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  vec2 tmpvar_19;\\n  tmpvar_19.x = 0.0;\\n  tmpvar_19.y = texsize.w;\\n  vec2 tmpvar_20;\\n  tmpvar_20.x = 0.0;\\n  tmpvar_20.y = texsize.w;\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_19)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_20)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_2));\\n  dist_3 = (1.0 - fract((1.0 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 2.0);\\n  vec2 tmpvar_21;\\n  tmpvar_21.y = 0.4;\\n  tmpvar_21.x = q18;\\n  xlat_mutableuv3 = (((2.0 * uv_1) * dist_3) + tmpvar_21);\\n  vec2 tmpvar_22;\\n  tmpvar_22.y = 0.0;\\n  tmpvar_22.x = texsize.z;\\n  vec2 tmpvar_23;\\n  tmpvar_23.y = 0.0;\\n  tmpvar_23.x = texsize.z;\\n  xlat_mutabledz.x = (xlat_mutabledz.x + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_22)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_23)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  vec2 tmpvar_24;\\n  tmpvar_24.x = 0.0;\\n  tmpvar_24.y = texsize.w;\\n  vec2 tmpvar_25;\\n  tmpvar_25.x = 0.0;\\n  tmpvar_25.y = texsize.w;\\n  xlat_mutabledz.y = (xlat_mutabledz.y + (inten_2 * dot (\\n    (texture (sampler_main, (xlat_mutableuv3 + tmpvar_24)).xyz - texture (sampler_main, (xlat_mutableuv3 - tmpvar_25)).xyz)\\n  , vec3(0.32, 0.49, 0.29))));\\n  xlat_mutableret1 = max (xlat_mutableret1, (texture (sampler_main, xlat_mutableuv3).xyz * inten_2));\\n  uv_1 = (uv_1 + xlat_mutabledz);\\n  vec2 tmpvar_26;\\n  tmpvar_26.x = q5;\\n  tmpvar_26.y = q6;\\n  xlat_mutableuv4 = (uv_1 - (0.4 * tmpvar_26));\\n  float tmpvar_27;\\n  tmpvar_27 = (0.1 * clamp ((1.0/(\\n    (abs(uv_1.y) + 0.1)\\n  )), 0.0, 12.0));\\n  vec2 tmpvar_28;\\n  tmpvar_28.x = (uv_1.x * tmpvar_27);\\n  tmpvar_28.y = tmpvar_27;\\n  vec3 tmpvar_29;\\n  tmpvar_29.xy = vec2(0.0, 0.0);\\n  tmpvar_29.z = clamp ((1.0 - (3.0 * uv_1.y)), 0.0, 1.0);\\n  ret_4 = (vec3(0.0, 0.1, 0.1) + (0.1 * tmpvar_29));\\n  ret_4 = (ret_4 + (vec3(dot (texture (sampler_noise_hq, \\n    (tmpvar_28 + (0.1 * time))\\n  ), vec4(0.32, 0.49, 0.29, 0.0))) * (\\n    (clamp ((1.0 - (12.0 * uv_1.y)), 0.0, 1.0) * 0.1)\\n   / \\n    (0.05 + sqrt(dot (xlat_mutableuv4, xlat_mutableuv4)))\\n  )));\\n  ret_4 = (ret_4 + ((0.4 * xlat_mutableret1) + (xlat_mutableret1 * q22)));\\n  vec4 tmpvar_30;\\n  tmpvar_30.w = 1.0;\\n  tmpvar_30.xyz = ret_4;\\n  ret = tmpvar_30.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:2,modwavealphabyvolume:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:20.944651,wave_smoothing:0,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:.99,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,textured:1,y:.47,rad:.24057,tex_zoom:.14057,g:.3,b:.5,g2:0,b2:.3,a2:1,border_b:0,border_a:0},init_eqs_str:\"a.q24=0;\",frame_eqs_str:\"a.x=.5;a.y=.5;a.a=a.q24;a.a2=a.q24;\"},{baseVals:{enabled:1,sides:100,additive:1,thickoutline:1,rad:.05134,tex_zoom:.12288,r:.3,g:.2,b:.2,a:.7,g2:0,border_r:0,border_g:.5,border_b:.5,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.r=.5+.3*Math.sin(a.time);a.g=.5+.3*Math.sin(div(a.time,1.5));a.b=.5+.3*Math.sin(div(a.time,3.7));a.r2=0;a.b2=0;a.g2=0;a.a=.05;a.a2=0;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:100,thick:1,scaling:.045052,smoothing:.1,a:.6},init_eqs_str:\"a.k1=0;a.dy=0;a.dx=0;a.t2=0;a.q24=0;a.q27=0;\",frame_eqs_str:\"a.t2=.45+div(randint(10),100);\",point_eqs_str:\"a.k1=bnot(mod(100*a.sample+a.time,2));a.dy=.015*above(Math.sin(11*a.sample),0)+.008*above(Math.sin(a.time+74*a.sample),0)+.008*above(Math.sin(a.time+128*a.sample),0);a.dx=.01*above(Math.sin(27*a.sample),0)+.01*above(Math.sin(a.time+134*a.sample),0);a.x=.2*(a.sample-.5)+.5+a.dx;a.y=a.t2+a.dy;a.a=a.q24*a.k1*bnot(mod(a.q27,4));a.r=.6;a.g=0;a.b=.6;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.dec_xlow=0;a.index2=0;a.speed_=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.q9=0;a.movx=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q10=0;a.tilt_=0;a.q4=0;a.peakx=0;a.dir__=0;a.dir=0;a.dir_=0;a.movy=0;a.q26=0;a.maxp=0;a.p2=0;a.avg=0;a.trig=0;a.beat=0;a.p1=0;a.peak=0;a.tilt=0;a.q2=0;a.q27=0;a.speed=0;a.q3=0;a.t0=0;a.q7=0;a.q20=0;a.q8=0;a.xk=0;\",frame_eqs_str:\"a.dec_med=pow(.8,div(30,a.fps));a.dec_slow=pow(.95,div(30,a.fps));a.dec_xlow=pow(.995,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,16);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=\\na.index;a.q24=a.is_beat;a.q26=a.bass_att+a.mid_att+a.treb_att;a.q27=a.index+1;a.peakx=above(a.q22,1.5*a.maxp);a.maxp=Math.max(a.maxp,a.q22);a.maxp*=a.dec_xlow;a.k1=a.is_beat*equal(mod(a.index,3),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.trig=a.q24*bnot(mod(a.index,4));a.dir=a.dir+div(a.trig*a.q26*(div(randint(100),100)-.5),2)+a.peakx;a.dir_=a.dir_*a.dec_slow+a.dir*(1-a.dec_slow);\\na.dir__=a.dir__*a.dec_slow+a.dir_*(1-a.dec_slow);a.q5=Math.cos(a.dir__);a.q6=Math.sin(a.dir__);a.speed=a.speed*bnot(a.trig)+div(a.trig*a.q26*5,a.fps);a.speed_=a.speed_*a.dec_slow+a.speed*(1-a.dec_slow);a.movx+=a.speed_*a.q6;a.movy+=a.speed_*a.q5;a.q7=a.movx;a.q8=a.movy;a.tilt=a.dir-a.dir__;a.tilt_=a.dec_slow*a.tilt_+(1-a.dec_slow)*a.tilt;a.monitor=a.maxp;a.q9=Math.cos(a.tilt_*a.speed_);a.q10=Math.sin(a.tilt_*a.speed_);a.q12=a.time;\",pixel_eqs_str:\"a.zoom=1.3;\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 uv6_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_4;\\n  tmpvar_4 = ((2.0 * sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  )) + (rand_frame * 64.0)).x;\\n  uv_1 = (uv + (clamp (\\n    ((sin(tmpvar_4) / cos(tmpvar_4)) * normalize(tmpvar_3))\\n  , vec2(-2.0, -2.0), vec2(2.0, 2.0)) / 20.0));\\n  uv6_2 = (0.4 * sin((tmpvar_3 * 22.0)));\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = (((q24 * \\n    (((texture (sampler_main, uv_1).xyz - (\\n      ((texture (sampler_blur1, fract(uv_1)).xyz * scale1) + bias1)\\n     * 0.04)) + (0.15 * (vec3(\\n      (0.1 / sqrt(dot (uv6_2, uv6_2)))\\n    ) * roam_cos.xyz))) - 0.02)\\n  ) * 0.98) + ((1.0 - q24) * texture (sampler_main, uv_orig).xyz));\\n  ret = tmpvar_5.xyz;\\n }\",comp:\"vec3 xlat_mutableret1;\\nvec2 xlat_mutablers;\\n shader_body { \\n  vec2 uv1_1;\\n  mat2 tmpvar_2;\\n  tmpvar_2[uint(0)].x = q9;\\n  tmpvar_2[uint(0)].y = -(q10);\\n  tmpvar_2[1u].x = q10;\\n  tmpvar_2[1u].y = q9;\\n  uv1_1 = (((uv_orig - 0.5) * aspect.xy) * tmpvar_2);\\n  uv1_1 = (uv1_1 * aspect.yx);\\n  float tmpvar_3;\\n  tmpvar_3 = (3.0 / abs(uv1_1.y));\\n  xlat_mutablers.x = (uv1_1.x * tmpvar_3);\\n  xlat_mutablers.y = (tmpvar_3 / 2.0);\\n  mat2 tmpvar_4;\\n  tmpvar_4[uint(0)].x = q5;\\n  tmpvar_4[uint(0)].y = -(q6);\\n  tmpvar_4[1u].x = q6;\\n  tmpvar_4[1u].y = q5;\\n  xlat_mutablers = (tmpvar_4 * xlat_mutablers);\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = q7;\\n  tmpvar_5.y = q8;\\n  xlat_mutablers = (xlat_mutablers + tmpvar_5);\\n  xlat_mutableret1 = ((texture (sampler_blur1, fract(\\n    (xlat_mutablers / 12.0)\\n  )).xyz * scale1) + bias1);\\n  vec2 tmpvar_6;\\n  tmpvar_6.y = 0.0;\\n  tmpvar_6.x = q5;\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = (((xlat_mutableret1 * 32.0) / tmpvar_3) + ((\\n    ((q22 * sqrt(tmpvar_3)) / 4.0)\\n   * \\n    sin(((uv1_1 - q12) * q27))\\n  .x) * texture (sampler_main, (\\n    (((4.0 * xlat_mutableret1) / tmpvar_3).xy + ((uv1_1 / 2.0) / (0.5 + abs(uv1_1.y))))\\n   + tmpvar_6)).xyz));\\n  ret = tmpvar_7.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.98,decay:.5,echo_zoom:.952,echo_alpha:.5,echo_orient:3,wave_mode:5,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.474,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.8,wave_g:.49,ob_size:0,ob_a:.3,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1x:.8,b1ed:0},shapes:[{baseVals:{enabled:1,sides:7,additive:1,num_inst:1024,rad:.04896,tex_ang:1.00531,tex_zoom:1.53117,r:0,g:1,b:1,a:0,g2:0,border_b:0,border_a:0},init_eqs_str:\"a.max_age=0;a.n=0;a.x0=0;a.y0=0;a.z0=0;a.rad0=0;\",frame_eqs_str:\"a.max_age=a.reg00;a.n=12*a.instance;a.x0=a.gmegabuf[Math.floor(a.n)];a.y0=a.gmegabuf[Math.floor(a.n+1)];a.z0=a.gmegabuf[Math.floor(a.n+2)];.00001<Math.abs(-100>a.z0?1:0)?(a.rad=0,a.gmegabuf[Math.floor(a.n+8)]=a.max_age):(a.rad0=div(pow(1-div(a.gmegabuf[Math.floor(a.n+8)],a.max_age),.2),a.z0)*a.gmegabuf[Math.floor(a.n+7)]+.01,a.rad=.032*Math.abs(a.rad0),a.x=.5+div(a.x0,a.z0),a.y=.5+div(a.y0,a.z0));a.a=1;a.a2=.2;a.g=.8;a.g2=0;a.b=.2*(3<randint(10)?1:0)+.2*(0>a.z0?1:0);a.b2=0;\\n\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:160,sep:20,usedots:1,additive:1,scaling:7.858,smoothing:.1,r:.2,g:.3,a:.6},init_eqs_str:\"a.q32=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.x=div(randint(100),100);a.y=.5-div(1-.7,a.q32)-.15*(div(randint(100),100)-.5);a.a=.15;a.r=0;a.b=1;a.g=0;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:'a.max_age=0;a.ind2=0;a.n=0;a.free=0;a.high=0;a.index=0;a.tht=0;a.v00=0;a.yobf=0;a.dt=0;a.y0=0;a.q1=0;a.dec_med=0;a.q5=0;a.ran1_=0;a[\"new\"]=0;a.push=0;a.new1=0;a.is_beat=0;a.q24=0;a.vol_alt=0;a.dec_slow=0;a.ran2=0;a.ind1=0;a.q10=0;a.v0=0;a.med=0;a.beat=0;a.vol=0;a.peak=0;a.dec_fast=0;a.q27=0;a.bass1=0;a.q3=0;a.t0=0;a.vol_=0;a.dec=0;a.m=0;a.ran1=0;a.q32=0;a.phi=0;a.air=0;a.max_age=4.5;a.reg00=a.max_age;for(var b=a.n=0;12288>b;b++)a.gmegabuf[Math.floor(a.n)]=a.max_age-1+randint(2),\\na.n+=1;a.q9=2*(randint(25)-10);a.q3=div(randint(100),100);a.q4=div(randint(100),100);',frame_eqs_str:'a.dec_fast=1-div(8.8,a.fps);a.dec_med=1-div(6,a.fps);a.dec_slow=1-div(1.6,a.fps);a.vol=div(a.bass+a.med+a.treb,3);a.vol_=a.vol_*a.dec_slow+(1-a.dec_slow)*a.vol;a.beat=a.vol;a.is_beat=above(a.beat,1+4*a.peak)*above(a.time,a.t0+.2);a.t0=.00001<Math.abs(a.is_beat)?a.time:a.t0;a.peak=.00001<Math.abs(a.is_beat)?a.beat:a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,1024);a.ind1=a.ind1*a.dec_med+(1-a.dec_fast)*a.index;a.ind2=a.ind2*a.dec_med+(1-a.dec_fast)*a.ind1;a.q27=a.ind2;a.q24=\\na.is_beat;a.ran1=.00001<Math.abs(a.is_beat)?div(randint(100),100)-.5:a.ran1;a.ran2=.00001<Math.abs(a.is_beat)?div(randint(100),50)+1:a.ran2;a.ran1_=a.dec_med*a.ran1_+(1-a.dec_med)*a.ran1;a.high=2.9<a.ran2?1:0;a.q5=a.ran1_;a.n=0;a.push=Math.max(0,a.vol-a.vol_alt)*a.vol_;a.air=.1*a.dt;a.dt=div(1,a.fps);a.v00=pow(a.push,.25)+.4;a.free=512;a[\"new\"]=0;a.y0=-.68;a.yobf=a.y0+.1;for(var b=0;512>b;b++)a.megabuf[Math.floor(a.n)]=a.gmegabuf[Math.floor(a.n)],a.megabuf[Math.floor(a.n+1)]=a.gmegabuf[Math.floor(a.n+\\n1)],a.megabuf[Math.floor(a.n+2)]=a.gmegabuf[Math.floor(a.n+2)],.00001<Math.abs(a.gmegabuf[Math.floor(a.n+8)]>a.max_age?1:0)?.00001<Math.abs(band(a.push>=10*a.dt*bnot(a.high)?1:0,200>a[\"new\"]?1:0))?(--a.free,a[\"new\"]+=1,a.tht=div(randint(100),500)*(1+1.5*a.high),a.phi=randint(12),a.v0=a.v00*(1+div(randint(10),40)),a.gmegabuf[Math.floor(a.n)]=1.7*a.ran1,a.gmegabuf[Math.floor(a.n+1)]=a.y0+2.2*a.high,a.gmegabuf[Math.floor(a.n+2)]=2,a.gmegabuf[Math.floor(a.n+3)]=a.v0*Math.sin(a.tht)*Math.cos(a.phi),a.gmegabuf[Math.floor(a.n+\\n4)]=a.v0*Math.cos(4*a.tht)*(1-1.5*a.high),a.gmegabuf[Math.floor(a.n+5)]=a.v0*Math.sin(a.tht)*Math.sin(a.phi)*1.5*a.vol,a.gmegabuf[Math.floor(a.n+7)]=0,a.gmegabuf[Math.floor(a.n+8)]=div(randint(100),100)):0:(--a.free,a.gmegabuf[Math.floor(a.n)]+=a.gmegabuf[Math.floor(a.n+3)]*a.dt,a.gmegabuf[Math.floor(a.n+1)]+=a.gmegabuf[Math.floor(a.n+4)]*a.dt,a.gmegabuf[Math.floor(a.n+2)]+=a.gmegabuf[Math.floor(a.n+5)]*a.dt,a.gmegabuf[Math.floor(a.n+3)]*=1-a.air,a.gmegabuf[Math.floor(a.n+4)]*=1-a.air,a.gmegabuf[Math.floor(a.n+\\n4)]-=.8*a.dt,a.gmegabuf[Math.floor(a.n+5)]*=1-a.air,a.gmegabuf[Math.floor(a.n+7)]=(a.gmegabuf[Math.floor(a.n+1)]>a.yobf?1:0)*Math.min(1,18*(a.gmegabuf[Math.floor(a.n+1)]-a.yobf)),.00001<Math.abs(band(a.gmegabuf[Math.floor(a.n+1)]<a.yobf?1:0,0>a.gmegabuf[Math.floor(a.n+4)]?1:0))?a.gmegabuf[8]=a.max_age:0,a.gmegabuf[Math.floor(a.n+8)]+=a.dt),a.n+=12;for(b=a.n=0;512>b;b++)a.m=6132+a.n,a.gmegabuf[Math.floor(a.m)]=div(a.gmegabuf[Math.floor(a.n)]+a.megabuf[Math.floor(a.n)],2),a.gmegabuf[Math.floor(a.m+\\n1)]=div(a.gmegabuf[Math.floor(a.n+1)]+a.megabuf[Math.floor(a.n+1)],2),a.gmegabuf[Math.floor(a.m+2)]=div(a.gmegabuf[Math.floor(a.n+2)]+a.megabuf[Math.floor(a.n+2)],2),a.gmegabuf[Math.floor(a.m+7)]=a.gmegabuf[Math.floor(a.n+7)],a.gmegabuf[Math.floor(a.m+8)]=a.gmegabuf[Math.floor(a.n+8)],a.n+=12;a.dec=.00001<Math.abs(a.bass>a.bass1?1:0)?.5:.9;a.bass1=a.bass1*a.dec+a.bass*(1-a.dec);a.q1=Math.min(1,Math.max(0,a.bass1-1.5)*Math.abs(a.q3-.5)*3);a.q10=Math.max(a.vol_-.1,.1);a.vol_alt=a.vol;a.q32=a.aspecty;\\na.new1=.00001<Math.abs(0<a[\"new\"]?1:0)?a[\"new\"]:a.new1;a.monitor=a.new1;',pixel_eqs_str:\"a.dy=-.007*a.y;a.warp=0;a.rot=div(.025*Math.sin(a.q27+7*a.x+0*a.y)*.2,Math.abs(a.y-.7)+.1)*a.q10;a.zoom=1.003+.2*Math.max(0,a.y-.7);\",warp:\"vec2 ver;\\nfloat xlat_mutablecloud;\\nvec2 xlat_mutabledz;\\nvec2 xlat_mutableuv1;\\nvec2 xlat_mutableuv2;\\nfloat xlat_mutablez;\\n shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.y = 0.0;\\n  tmpvar_1.x = texsize.z;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = 0.0;\\n  tmpvar_2.y = texsize.w;\\n  ver = (tmpvar_2 * 2.0);\\n  vec3 ret_3;\\n  xlat_mutabledz.x = (2.0 * dot (vec3((texture (sampler_main, \\n    (uv + tmpvar_1)\\n  ).z - texture (sampler_main, \\n    (uv - tmpvar_1)\\n  ).z)), vec3(0.32, 0.49, 0.29)));\\n  xlat_mutabledz.y = (2.0 * dot (vec3((texture (sampler_main, \\n    (uv + ver)\\n  ).z - texture (sampler_main, \\n    (uv - ver)\\n  ).z)), vec3(0.32, 0.49, 0.29)));\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = q3;\\n  tmpvar_4.y = (0.4 + (q4 * 0.4));\\n  xlat_mutableuv1 = (uv_orig - tmpvar_4);\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_main, uv_orig);\\n  xlat_mutablez = ((0.8 * (xlat_mutableuv1.y - 0.4)) - (mix (tmpvar_5.x, \\n    ((texture (sampler_blur1, uv_orig).xyz * scale1) + bias1)\\n  .x, 0.5) * 0.03));\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = (xlat_mutableuv1.x * xlat_mutablez);\\n  tmpvar_6.y = xlat_mutablez;\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = 0.0;\\n  tmpvar_7.y = (-(time) * 0.014);\\n  xlat_mutableuv2 = (tmpvar_6 + tmpvar_7);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = 0.0;\\n  tmpvar_8.y = (time * 0.004);\\n  vec2 uvi_9;\\n  uvi_9 = ((xlat_mutableuv2 * 1.5) + tmpvar_8);\\n  float zv_10;\\n  zv_10 = (0.002 * time);\\n  xlat_mutablecloud = (1.0 - (1.5 * abs(\\n    (texture (sampler_noise_hq, ((xlat_mutableuv2 + (0.07 * \\n      abs((((\\n        (dot (texture (sampler_noise_hq, uvi_9), vec4(0.32, 0.49, 0.29, 0.0)) + (dot (texture (sampler_noise_hq, (\\n          (uvi_9 * 2.0)\\n         + zv_10)), vec4(0.32, 0.49, 0.29, 0.0)) / 2.0))\\n       + \\n        (dot (texture (sampler_noise_hq, ((uvi_9 * 4.0) + zv_10)), vec4(0.32, 0.49, 0.29, 0.0)) / 4.0)\\n      ) + (\\n        dot (texture (sampler_noise_hq, ((uvi_9 * 8.0) + zv_10)), vec4(0.32, 0.49, 0.29, 0.0))\\n       / -8.0)) - 1.0))\\n    )) / 2.0)) - 0.5)\\n  ))).x;\\n  xlat_mutablecloud = (xlat_mutablecloud * clamp ((\\n    (texture (sampler_noise_hq, (xlat_mutableuv2 * vec2(2.0, 0.5))).x + 0.5)\\n   - \\n    (3.0 * abs(xlat_mutableuv2.x))\\n  ), 0.0, 1.0));\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = 0.0;\\n  tmpvar_11.y = ((-0.01 * time) * (0.125 * float(\\n    int((8.0 * pow (xlat_mutablecloud, 4.0)))\\n  )));\\n  float tmpvar_12;\\n  tmpvar_12 = (texture (sampler_noise_hq, (18.0 * (\\n    (xlat_mutableuv2 + (0.05 * xlat_mutablecloud))\\n   + tmpvar_11))) - 0.75).x;\\n  xlat_mutablecloud = (xlat_mutablecloud * (1.0 + (\\n    (0.2 * tmpvar_12)\\n   * \\n    (xlat_mutablecloud * xlat_mutablecloud)\\n  )));\\n  ret_3.x = ((0.3 * xlat_mutablecloud) + (0.7 * tmpvar_5.xyz)).x;\\n  xlat_mutableuv1 = (uv - vec2(0.0, 0.71));\\n  vec4 tmpvar_13;\\n  tmpvar_13 = texture (sampler_blur1, uv);\\n  float tmpvar_14;\\n  tmpvar_14 = clamp (((12.0 * \\n    (((10.0 * xlat_mutableuv1.y) + ((\\n      (tmpvar_13.xyz * scale1)\\n     + bias1).x / 2.0)) - 1.0)\\n  ) + tmpvar_12), 0.0, 1.0);\\n  xlat_mutableuv1 = (uv - 0.7);\\n  ret_3.z = (((\\n    ((((4.0 * tmpvar_14) * (1.0 - tmpvar_14)) * clamp ((tmpvar_5.x - 0.2), 0.0, 1.0)) + texture (sampler_main, (uv + (xlat_mutabledz * 0.003))).z)\\n   - \\n    (0.03 * sqrt(dot (xlat_mutabledz, xlat_mutabledz)))\\n  ) - (\\n    pow (((tmpvar_13.xyz * scale1) + bias1).z, 8.0)\\n   * 0.2)) - 0.01);\\n  ret_3.y = (texture (sampler_main, mix (uv, uv_orig, vec2(0.5, 0.5))).y * (0.85 - (0.2 * \\n    ((tmpvar_13.xyz * scale1) + bias1)\\n  .y)));\\n  ret_3.x = (ret_3.x + ((texture (sampler_noise_lq, \\n    (2.0 * xlat_mutableuv2)\\n  ) * 0.08) * clamp (\\n    (1.0 - (3.0 * ret_3.x))\\n  , 0.0, 1.0)).x);\\n  vec4 tmpvar_15;\\n  tmpvar_15.w = 1.0;\\n  tmpvar_15.xyz = ret_3;\\n  ret = tmpvar_15.xyz;\\n }\",comp:\"float dunk;\\nfloat xlat_mutablenoise;\\nvec2 xlat_mutablers;\\nvec2 xlat_mutablers0;\\nfloat xlat_mutablesmask;\\nfloat xlat_mutablesmoke;\\nvec2 xlat_mutableuv1;\\nfloat xlat_mutablez;\\n shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.y = 0.0;\\n  tmpvar_1.x = texsize.z;\\n  dunk = ((rand_preset.x * 0.3) + 0.1);\\n  vec2 uv_2;\\n  vec3 ret_3;\\n  uv_2 = (uv + (texsize.zw / 2.0));\\n  xlat_mutableuv1 = (uv_2 - vec2(0.0, 0.7));\\n  xlat_mutablesmask = (((10.0 * xlat_mutableuv1.y) + (\\n    ((texture (sampler_blur1, uv_2).xyz * scale1) + bias1)\\n  .x / 2.0)) - 1.0);\\n  float tmpvar_4;\\n  tmpvar_4 = clamp (((2.0 * xlat_mutablesmask) + 0.3), 0.0, 1.0);\\n  xlat_mutablenoise = ((3.0 * (1.0 - tmpvar_4)) * texture (sampler_noise_lq, ((xlat_mutableuv1 * 1.5) + (rand_frame * 0.2).xy)).x);\\n  float tmpvar_5;\\n  tmpvar_5 = clamp (((12.0 * xlat_mutablesmask) + (xlat_mutablenoise / 2.0)), 0.0, 1.0);\\n  xlat_mutablez = ((0.35 / xlat_mutableuv1.y) + (xlat_mutablenoise / 2.0));\\n  xlat_mutablers0.x = (xlat_mutableuv1.x * xlat_mutablez);\\n  xlat_mutablers0.y = xlat_mutablez;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = (xlat_mutablers0.x + (time / 4.0));\\n  tmpvar_6.y = (xlat_mutablez + (time * 0.21));\\n  xlat_mutablers = (tmpvar_6 * tmpvar_5);\\n  float t_7;\\n  t_7 = ((texture (sampler_main, uv_2).x - dunk) * (1.0 - (tmpvar_5 * \\n    pow (uv_2.y, 4.0)\\n  )));\\n  float tmpvar_8;\\n  tmpvar_8 = clamp (((1.2 * t_7) - 0.2), 0.0, 1.0);\\n  t_7 = tmpvar_8;\\n  vec3 tmpvar_9;\\n  tmpvar_9.x = tmpvar_8;\\n  tmpvar_9.y = (tmpvar_8 * tmpvar_8);\\n  tmpvar_9.z = pow (tmpvar_8, 8.0);\\n  vec3 tmpvar_10;\\n  tmpvar_10 = clamp ((tmpvar_9 / vec3(0.8, 0.8, 0.8)), 0.0, 1.0);\\n  float t_11;\\n  t_11 = ((texture (sampler_blur2, (uv_2 + 0.03)).xyz * scale2) + bias2).x;\\n  float tmpvar_12;\\n  tmpvar_12 = clamp (((1.2 * t_11) - 0.2), 0.0, 1.0);\\n  t_11 = tmpvar_12;\\n  vec3 tmpvar_13;\\n  tmpvar_13.x = tmpvar_12;\\n  tmpvar_13.y = (tmpvar_12 * tmpvar_12);\\n  tmpvar_13.z = pow (tmpvar_12, 8.0);\\n  vec3 tmpvar_14;\\n  tmpvar_14 = clamp ((tmpvar_13 / vec3(0.8, 0.8, 0.8)), 0.0, 1.0);\\n  ret_3 = ((tmpvar_10 * (tmpvar_10 * \\n    (3.0 - (2.0 * tmpvar_10))\\n  )) + ((\\n    (((1.0 - texture (sampler_main, uv_2).x) * (1.0 - tmpvar_5)) * abs((texture (sampler_main, (uv_2 - tmpvar_1)).x - texture (sampler_main, (uv_2 + tmpvar_1)).x)))\\n   * \\n    (tmpvar_14 * (tmpvar_14 * (3.0 - (2.0 * tmpvar_14))))\\n  ) * q9));\\n  xlat_mutablesmoke = ((texture (sampler_blur1, uv_2).xyz * scale1) + bias1).z;\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = uv_2.x;\\n  tmpvar_15.y = ((0.85 - xlat_mutableuv1.y) + ((\\n    ((texture (sampler_noise_hq, xlat_mutablers) + texture (sampler_noise_hq, ((xlat_mutablers / 4.0) - (time / 8.0)))) - 1.0)\\n  .x * 0.2) * q10));\\n  float t_16;\\n  t_16 = ((texture (sampler_main, tmpvar_15).x + texture (sampler_main, tmpvar_15).y) - dunk);\\n  float tmpvar_17;\\n  tmpvar_17 = clamp (((1.2 * t_16) - 0.2), 0.0, 1.0);\\n  t_16 = tmpvar_17;\\n  vec3 tmpvar_18;\\n  tmpvar_18.x = tmpvar_17;\\n  tmpvar_18.y = (tmpvar_17 * tmpvar_17);\\n  tmpvar_18.z = pow (tmpvar_17, 8.0);\\n  vec3 tmpvar_19;\\n  tmpvar_19 = clamp ((tmpvar_18 / vec3(0.8, 0.8, 0.8)), 0.0, 1.0);\\n  ret_3 = (ret_3 + ((\\n    (tmpvar_19 * (tmpvar_19 * (3.0 - (2.0 * tmpvar_19))))\\n   * tmpvar_5) * 0.85));\\n  float tmpvar_20;\\n  tmpvar_20 = ((3.6 * tmpvar_4) * (1.0 - tmpvar_4));\\n  ret_3 = (ret_3 * (clamp (\\n    (1.0 - (xlat_mutablesmoke * (1.0 - tmpvar_20)))\\n  , 0.0, 1.0) * clamp (\\n    (1.0 - (tmpvar_20 * float((rand_preset.x > 0.5))))\\n  , 0.0, 1.0)));\\n  vec3 tmpvar_21;\\n  tmpvar_21.z = 0.0;\\n  tmpvar_21.xy = (((texture (sampler_blur3, uv_2).xyz * scale3) + bias3).xy * vec2(3.0, 5.0));\\n  ret_3 = (ret_3 + ((xlat_mutablesmoke * \\n    dot (tmpvar_21, vec3(0.32, 0.49, 0.29))\\n  ) * vec3(1.0, 0.84, 0.62)));\\n  float tmpvar_22;\\n  tmpvar_22 = clamp (((1.2 * \\n    clamp (texture (sampler_main, uv_2).y, 0.0, 1.0)\\n  ) - 0.2), 0.0, 1.0);\\n  vec3 tmpvar_23;\\n  tmpvar_23.x = tmpvar_22;\\n  tmpvar_23.y = (tmpvar_22 * tmpvar_22);\\n  tmpvar_23.z = pow (tmpvar_22, 8.0);\\n  vec3 tmpvar_24;\\n  tmpvar_24 = clamp ((tmpvar_23 / vec3(0.8, 0.8, 0.8)), 0.0, 1.0);\\n  ret_3 = (ret_3 + (tmpvar_24 * (tmpvar_24 * \\n    (3.0 - (2.0 * tmpvar_24))\\n  )));\\n  float tmpvar_25;\\n  tmpvar_25 = clamp (0.52, 0.0, 1.0);\\n  vec3 tmpvar_26;\\n  tmpvar_26.x = tmpvar_25;\\n  tmpvar_26.y = (tmpvar_25 * tmpvar_25);\\n  tmpvar_26.z = pow (tmpvar_25, 8.0);\\n  vec3 tmpvar_27;\\n  tmpvar_27 = clamp ((tmpvar_26 / vec3(0.8, 0.8, 0.8)), 0.0, 1.0);\\n  vec3 tmpvar_28;\\n  tmpvar_28 = mix (clamp (ret_3, 0.0, 1.0), (tmpvar_27 * (tmpvar_27 * \\n    (3.0 - (2.0 * tmpvar_27))\\n  )), vec3((pow (\\n    ((1.0 - uv_2.y) - ((uv_2.x - 0.5) * (q3 - 0.5)))\\n  , 4.0) * q1)));\\n  ret_3 = tmpvar_28;\\n  vec4 tmpvar_29;\\n  tmpvar_29.w = 1.0;\\n  tmpvar_29.xyz = tmpvar_28;\\n  ret = tmpvar_29.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.698,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:0,ob_g:.1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:25.6,mv_y:9.6,mv_l:5,mv_g:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.n=0;a.maxbpm=0;a.reg26=0;a.maxind1=0;a.uvx0=0;a.cosb_=0;a.reg34=0;a.num_res=0;a.reg28=0;a.reg23=0;a.q25=0;a.angchg=0;a.reg20=0;a.reg15=0;a.reg10=0;a.sinb_=0;a.index=0;a.quali=0;a.v3=0;a.q18=0;a.q22=0;a.q21=0;a.diry=0;a.q6=0;a.posx=0;a.dt=0;a.reg25=0;a.mean=0;a.uvx=0;a.q1=0;a.posz=0;a.q5=0;a.reg52=0;a.dt1=0;a.dec_f=0;a.dirz=0;a.dec_s=0;a.reg16=0;a.bt2=0;a.reg36=0;a.minbpm=0;a.reg22=0;a.uvy=0;a.rotz=0;a.cosb=0;a.omega=0;a.dist_=0;a.ec_steps=0;a.bpm=0;a.q23=0;a.q24=0;a.reg24=\\n0;a.ran2=0;a.pi=0;a.q10=0;a.reg14=0;a.sinb=0;a.reg53=0;a.posy=0;a.reg31=0;a.dirx=0;a.dec_m=0;a.q4=0;a.start=0;a.reg12=0;a.reg13=0;a.v2p=0;a.c2=0;a.reg37=0;a.ex=0;a.s3=0;a.yslope=0;a.q16=0;a.xslope=0;a.q26=0;a.reg38=0;a.v3p=0;a.reg35=0;a.p2=0;a.mov=0;a.reg11=0;a.tx=0;a.avg=0;a.bt1=0;a.beatsin=0;a.uvz=0;a.c3=0;a.uvy0=0;a.reg27=0;a.q19=0;a.q17=0;a.vol=0;a.reg32=0;a.reg21=0;a.uvz0=0;a.len=0;a.reg18=0;a.beatcos=0;a.reg30=0;a.q2=0;a.b1y=0;a.q27=0;a.bri=0;a.slen=0;a.q14=0;a.dist=0;a.trel1=0;a.p3=0;a.reg17=\\n0;a.v1=0;a.speed=0;a.vol2=0;a.q3=0;a.s1=0;a.vol_=0;a.dec=0;a.s2=0;a.quad=0;a.b2y=0;a.ran1=0;a.q32=0;a.reg33=0;a.q7=0;a.ds=0;a.q28=0;a.ty=0;a.excite=0;a.c1=0;a.v2=0;a.q20=0;a.p4=0;a.q8=0;a.maxvol=0;for(var b=a.n=0;2E4>b;b++)a.megabuf[Math.floor(a.n)]=0,a.gmegabuf[Math.floor(a.n)]=0,a.n+=1;a.minbpm=30;a.maxbpm=230;a.num_res=100;for(b=a.index=0;b<a.num_res;b++)a.n=12*a.index,a.bpm=a.minbpm*pow(div(a.maxbpm,a.minbpm),div(a.index,a.num_res)),a.gmegabuf[Math.floor(a.n)]=a.bpm,a.index+=1;a.pi=3.1416;a.octave=\\ndiv(a.num_res*Math.log(2),Math.log(div(a.maxbpm,a.minbpm)));a.q7=.15;a.q8=.2+randint(1);a.q16=1.6;a.start=1;a.travel=0;a.rotz=0;a.t0=a.time+3;a.cran0=randint(1);a.trelx=0;a.trely=0;a.trelz=0;a.reg20=1;a.reg21=0;a.reg22=0;a.reg23=0;a.reg24=1;a.reg25=0;a.reg26=0;a.reg27=0;a.reg28=1;b=0;do{b+=1;var c;a.ran1=randint(8);a.ran2=randint(8);a.ran3=randint(8);a.posx=randint(8);a.posy=randint(8);a.posz=randint(8);a.c1=Math.cos(a.ran1);a.c2=Math.cos(a.ran2);a.c3=Math.cos(a.ran3);a.s1=Math.sin(a.ran1);a.s2=Math.sin(a.ran2);\\na.s3=Math.sin(a.ran3);a.reg20=a.c2*a.c1;a.reg21=a.c2*a.s1;a.reg22=-a.s2;a.reg23=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg24=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg25=a.s3*a.c2;a.reg26=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg27=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg28=a.c3*a.c2;a.dist=.001;var d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=1+2*(div(a.uvx,4)+30.5-Math.floor(div(a.uvx,4)+30.5)-.5);a.uvy=2+2*(div(a.uvy,4)+30.5-Math.floor(div(a.uvy,\\n4)+30.5)-.5);a.uvz=3+2*(div(a.uvz,4)+30.5-Math.floor(div(a.uvz,4)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:\\n.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.05;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(c)&&1048576>d);d=.06>a.dist?1:0}while(.00001<Math.abs(d)&&1048576>b);\",frame_eqs_str:\"a.dt=Math.min(div(1,a.fps),.1);a.dec_f=.8;a.dec_m=1-4*a.dt;a.dec_s=1-a.dt;a.vol=div(a.bass+div(a.mid,2)+a.treb,3);a.vol_=a.vol_*a.dec_m+(1-a.dec_m)*a.vol;a.dec=a.dec_s;a.vol2=a.vol2*a.dec+(1-a.dec)*Math.min(3,a.vol);a.maxvol=.00001<Math.abs(a.vol>a.maxvol?1:0)?a.vol:a.maxvol*a.dec_s;a.excite=a.vol-a.vol_;a.index=0;a.maxind1=0;for(var b=a.mean=0;b<a.num_res;b++){a.n=12*a.index;a.omega=div(2*a.gmegabuf[Math.floor(a.n)]*a.pi,60);a.gmegabuf[Math.floor(a.n+1)]*=1-.25*a.dt;a.ec_steps=\\nMath.floor(10*a.dt*a.omega)+1;a.dt1=div(a.dt,a.ec_steps);a.ex=a.excite;for(var c=0;c<a.ec_steps;c++)a.gmegabuf[Math.floor(a.n+1)]+=(a.ex-sqr(a.omega)*a.gmegabuf[Math.floor(a.n+2)])*a.dt1,a.gmegabuf[Math.floor(a.n+2)]+=a.gmegabuf[Math.floor(a.n+1)]*a.dt1;a.beatcos=a.gmegabuf[Math.floor(a.n+1)];a.beatsin=a.gmegabuf[Math.floor(a.n+2)]*a.omega;a.quad=sqrt(pow(a.beatsin,2)+pow(a.beatcos,2));a.mean+=div(a.quad,a.num_res);a.gmegabuf[Math.floor(a.n+3)]=a.gmegabuf[Math.floor(a.n+3)]*a.dec_m+a.quad*(1-a.dec_m);\\n.00001<Math.abs(a.gmegabuf[Math.floor(a.n+3)]>a.gmegabuf[Math.floor(12*a.maxind1+3)]?1:0)?a.maxind1=a.index:0;a.index+=1}a.quali=div(a.gmegabuf[Math.floor(12*a.maxind1+3)],a.mean);a.bpm=a.minbpm*pow(div(a.maxbpm,a.minbpm),div(a.maxind1,a.num_res));.00001<Math.abs(160<a.bpm?1:0)?a.bpm/=2:0;a.trel1+=(div(a.bpm,60)*a.pi+a.excite*a.cosb)*(.7<a.maxvol?1:0)*a.dt;a.q32=a.trel1;a.speed=div((.15+.25*a.dist_)*(1+a.avg),a.q7)*(.34+.34*a.vol_)*(a.q8+.8);a.ds=a.ds*a.dec_s+(1-a.dec_s)*a.speed*.25*a.dt;a.rotz=.8*\\npow(Math.sin(div(a.time,12)),5);a.dirx=a.reg26;a.diry=a.reg27;a.dirz=a.reg28;a.posx+=a.ds*a.dirx;a.posy+=a.ds*a.diry;a.posz+=a.ds*a.dirz;a.q4=a.posx;a.q5=a.posy;a.q6=a.posz;a.angchg=Math.max(0,.4-a.dist_);a.v1=a.v1*a.dec_m+(1-a.dec_m)*a.rotz*a.ds;a.dec=1-3*a.dt;a.v2p=a.v2p*a.dec+(1-a.dec)*(a.angchg*a.xslope+.02*a.p2)*a.dt;a.v3p=a.v3p*a.dec+(1-a.dec)*(a.angchg*a.yslope+.02*a.p3)*a.dt;a.v2=a.v2*a.dec+(1-a.dec)*a.v2p;a.v3=a.v3*a.dec+(1-a.dec)*a.v3p;a.p3=sign(a.v3);a.p4=sign(a.v2);a.reg30=a.reg20;a.reg31=\\na.reg21;a.reg32=a.reg22;a.reg33=a.reg23;a.reg34=a.reg24;a.reg35=a.reg25;a.reg36=a.reg26;a.reg37=a.reg27;a.reg38=a.reg28;a.n=0;for(b=a.avg=0;5>b;b++){a.n+=1;a.ran1=div(randint(100),100);a.ran2=div(randint(100),200)-.25;a.tx=Math.cos(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.ty=Math.sin(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.c1=Math.cos(a.v1);a.c2=Math.cos(a.v2+a.ty);a.c3=Math.cos(a.v3+a.tx);a.s1=Math.sin(a.v1);a.s2=Math.sin(a.v2+a.ty);a.s3=Math.sin(a.v3+a.tx);a.reg10=a.c2*a.c1;a.reg11=a.c2*a.s1;a.reg12=\\n-a.s2;a.reg13=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg14=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg15=a.s3*a.c2;a.reg16=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg17=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg18=a.c3*a.c2;a.reg20=a.reg30;a.reg21=a.reg31;a.reg22=a.reg32;a.reg23=a.reg33;a.reg24=a.reg34;a.reg25=a.reg35;a.reg26=a.reg36;a.reg27=a.reg37;a.reg28=a.reg38;a.q20=a.reg10*a.reg20+a.reg11*a.reg23+a.reg12*a.reg26;a.q21=a.reg10*a.reg21+a.reg11*a.reg24+a.reg12*a.reg27;a.q22=a.reg10*a.reg22+a.reg11*a.reg25+a.reg12*a.reg28;a.q23=a.reg13*a.reg20+\\na.reg14*a.reg23+a.reg15*a.reg26;a.q24=a.reg13*a.reg21+a.reg14*a.reg24+a.reg15*a.reg27;a.q25=a.reg13*a.reg22+a.reg14*a.reg25+a.reg15*a.reg28;a.q26=a.reg16*a.reg20+a.reg17*a.reg23+a.reg18*a.reg26;a.q27=a.reg16*a.reg21+a.reg17*a.reg24+a.reg18*a.reg27;a.q28=a.reg16*a.reg22+a.reg17*a.reg25+a.reg18*a.reg28;a.reg20=a.q20;a.reg21=a.q21;a.reg22=a.q22;a.reg23=a.q23;a.reg24=a.q24;a.reg25=a.q25;a.reg26=a.q26;a.reg27=a.q27;a.reg28=a.q28;a.dist=.002;var d;c=0;do{c+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*\\na.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=1+2*(div(a.uvx,4)+30.5-Math.floor(div(a.uvx,4)+30.5)-.5);a.uvy=2+2*(div(a.uvy,4)+30.5-Math.floor(div(a.uvy,4)+30.5)-.5);a.uvz=3+2*(div(a.uvz,4)+30.5-Math.floor(div(a.uvz,4)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(d=0;8>d;d++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?\\n1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*\\na.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.1;d=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(d)&&1048576>c);a.megabuf[Math.floor(a.n)]=a.megabuf[Math.floor(a.n)]*a.dec_s+(1-a.dec_s)*a.dist;a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5))}a.n=0;for(b=a.avg=0;5>b;b++)a.n+=1,a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5));a.xslope=Math.min(Math.max(8*(a.megabuf[1]-a.megabuf[3]),-2),2);a.yslope=Math.min(Math.max(8*(a.megabuf[4]-a.megabuf[2]),-2),2);a.dist_=a.dist_*a.dec_m+(1-a.dec_m)*a.dist;\\na.q10=a.ds*a.q7;a.q14=Math.abs(a.ds)+2*(Math.abs(a.v1)+Math.abs(a.v2)+Math.abs(a.v3))+div(1,255)+0*a.start;a.start*=.9;a.reg52=-a.v2;a.reg53=a.v3;a.warp=0;a.zoom=1;a.rot=a.v1;a.sinb=Math.sin(a.trel1);a.cosb=Math.cos(a.trel1);a.bt1=0>a.sinb*a.sinb_?1:0;a.bt2=0>a.cosb*a.cosb_?1:0;.00001<Math.abs(a.bt1)?(a.b1x=randint(2)-1,a.b1y=randint(2)-1):0;.00001<Math.abs(a.bt2)?(a.b2x=randint(2)-1,a.b2y=randint(2)-1):0;a.sinb_=a.sinb;a.cosb_=a.cosb;a.bri=.012*a.vol2*sqrt(div(100,a.bpm))+.01*a.excite;a.mov=div(sqrt(a.v3*\\na.v3+a.v2*a.v2),a.dt);a.q1=a.b1x*Math.abs(a.b1x);a.q2=a.b1y*Math.abs(a.b1y);a.q17=.4*a.b2x;a.q18=.4*a.b2y;a.len=pow(a.q1,2)+pow(a.q2,2);a.q3=pow(a.sinb,2)*a.bri*(1+4*a.len)*Math.max(0,1-5*a.mov);a.q19=pow(a.cosb,2)*a.bri*1.2;a.monitor=a.q8;\",pixel_eqs_str:\"a.warp=0;a.zoom=1;a.tx=Math.tan(.9*(2*a.x-1));a.ty=Math.tan(.9*(2*a.y-1));a.dx=div(a.reg52,a.q16)*(1+.5*Math.abs(a.tx));a.dy=div(a.reg53,a.q16)*(1+.5*Math.abs(a.ty));a.dx+=a.reg53*a.ty*(a.x-.5);a.dy+=a.reg52*a.tx*(a.y-.5);\",warp:\"float sustain;\\nfloat pix;\\nvec3 mod1;\\nfloat xlat_mutabledist;\\nfloat xlat_mutablestruc;\\nvec2 xlat_mutableuv1;\\nvec3 xlat_mutableuv2;\\n shader_body { \\n  mat3 tmpvar_1;\\n  tmpvar_1[uint(0)].x = q20;\\n  tmpvar_1[uint(0)].y = q23;\\n  tmpvar_1[uint(0)].z = q26;\\n  tmpvar_1[1u].x = q21;\\n  tmpvar_1[1u].y = q24;\\n  tmpvar_1[1u].z = q27;\\n  tmpvar_1[2u].x = q22;\\n  tmpvar_1[2u].y = q25;\\n  tmpvar_1[2u].z = q28;\\n  vec3 tmpvar_2;\\n  tmpvar_2.x = q4;\\n  tmpvar_2.y = q5;\\n  tmpvar_2.z = q6;\\n  sustain = (0.97 - q14);\\n  pix = (texsize.z * 2.0);\\n  mod1 = (rand_preset.xyz - 0.5);\\n  vec2 uv_3;\\n  vec3 ret_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv - 0.5);\\n  xlat_mutableuv1 = ((tmpvar_5 * aspect.xy) * q16);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_pw_main, uv);\\n  uv_3 = ((tmpvar_5 * (1.0 - \\n    (q10 / (1.0 - (tmpvar_6.z + (0.003921569 * tmpvar_6.y))))\\n  )) + 0.5);\\n  vec4 tmpvar_7;\\n  tmpvar_7 = fract((8.0 * texture (sampler_noise_lq, (uv_3 + rand_frame.yz))));\\n  xlat_mutabledist = tmpvar_7.x;\\n  if ((tmpvar_7.y > 0.16)) {\\n    vec4 nb2_8;\\n    vec4 nb_9;\\n    vec4 tmpvar_10;\\n    tmpvar_10 = texture (sampler_pw_main, (uv_3 + vec2(-(pix))));\\n    nb_9.x = (1.0 - (tmpvar_10.z + (0.003921569 * tmpvar_10.y)));\\n    vec4 tmpvar_11;\\n    tmpvar_11 = texture (sampler_pw_main, (uv_3 + (pix * vec2(1.0, -1.0))));\\n    nb_9.y = (1.0 - (tmpvar_11.z + (0.003921569 * tmpvar_11.y)));\\n    vec4 tmpvar_12;\\n    tmpvar_12 = texture (sampler_pw_main, (uv_3 + vec2(pix)));\\n    nb_9.z = (1.0 - (tmpvar_12.z + (0.003921569 * tmpvar_12.y)));\\n    vec4 tmpvar_13;\\n    tmpvar_13 = texture (sampler_pw_main, (uv_3 + (pix * vec2(-1.0, 1.0))));\\n    nb_9.w = (1.0 - (tmpvar_13.z + (0.003921569 * tmpvar_13.y)));\\n    vec4 tmpvar_14;\\n    tmpvar_14 = texture (sampler_pw_main, (uv_3 + (pix * vec2(0.0, -1.0))));\\n    nb2_8.x = (1.0 - (tmpvar_14.z + (0.003921569 * tmpvar_14.y)));\\n    vec4 tmpvar_15;\\n    tmpvar_15 = texture (sampler_pw_main, (uv_3 + (pix * vec2(1.0, 0.0))));\\n    nb2_8.y = (1.0 - (tmpvar_15.z + (0.003921569 * tmpvar_15.y)));\\n    vec4 tmpvar_16;\\n    tmpvar_16 = texture (sampler_pw_main, (uv_3 + (pix * vec2(0.0, 1.0))));\\n    nb2_8.z = (1.0 - (tmpvar_16.z + (0.003921569 * tmpvar_16.y)));\\n    vec4 tmpvar_17;\\n    tmpvar_17 = texture (sampler_pw_main, (uv_3 + (pix * vec2(-1.0, 0.0))));\\n    nb2_8.w = (1.0 - (tmpvar_17.z + (0.003921569 * tmpvar_17.y)));\\n    vec4 tmpvar_18;\\n    tmpvar_18 = min (nb_9, nb2_8);\\n    nb_9.zw = tmpvar_18.zw;\\n    nb_9.xy = min (tmpvar_18.xy, tmpvar_18.zw);\\n    xlat_mutabledist = ((min (nb_9.x, nb_9.y) + (\\n      (0.006 * (tmpvar_7.xyz - 0.5).x)\\n     * tmpvar_7.y)) - (q10 * 0.5));\\n  };\\n  float theta_19;\\n  theta_19 = (xlat_mutabledist * 1.35);\\n  float theta_20;\\n  theta_20 = (xlat_mutabledist * 1.35);\\n  vec3 tmpvar_21;\\n  tmpvar_21.xy = (xlat_mutableuv1 * ((\\n    sin(theta_19)\\n   / \\n    cos(theta_19)\\n  ) / 1.35));\\n  tmpvar_21.z = ((sin(theta_20) / cos(theta_20)) / 1.35);\\n  xlat_mutableuv2 = (((tmpvar_21 / q7) * tmpvar_1) + tmpvar_2);\\n  xlat_mutableuv2 = (((\\n    fract(((xlat_mutableuv2 / 4.0) + 0.5))\\n   - 0.5) * 2.0) + vec3(1.0, 2.0, 3.0));\\n  vec3 zz0_23;\\n  vec3 zz_24;\\n  zz_24 = xlat_mutableuv2;\\n  zz0_23 = (xlat_mutableuv2 + q8);\\n  for (int n_22 = 0; n_22 <= 8; n_22++) {\\n    zz_24 = ((2.0 * clamp (zz_24, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_24);\\n    zz_24 = (zz_24 * max ((1.0/(\\n      dot (zz_24, zz_24)\\n    )), 1.0));\\n    zz_24 = ((2.6 * zz_24) + zz0_23);\\n  };\\n  vec4 tmpvar_25;\\n  tmpvar_25.w = 0.0;\\n  tmpvar_25.xyz = zz_24;\\n  float tmpvar_26;\\n  tmpvar_26 = sqrt(dot (zz_24, zz_24));\\n  vec4 tmpvar_27;\\n  tmpvar_27.w = 0.0;\\n  tmpvar_27.xyz = mod1;\\n  xlat_mutablestruc = ((1.0 - (\\n    (1.4 * dot (tmpvar_25, tmpvar_27))\\n   / 40.0)) - (xlat_mutabledist * 0.3));\\n  if (((tmpvar_26 < 40.0) && (xlat_mutabledist > (0.04 * \\n    (1.0 - rad)\\n  )))) {\\n    ret_4.x = mix (xlat_mutablestruc, texture (sampler_main, uv_3).x, sustain);\\n    float x_28;\\n    x_28 = ((1.0 - xlat_mutabledist) * 255.0);\\n    float ip_29;\\n    ip_29 = float(int(x_28));\\n    vec2 tmpvar_30;\\n    tmpvar_30.x = (x_28 - ip_29);\\n    tmpvar_30.y = (ip_29 / 255.0);\\n    ret_4.yz = tmpvar_30;\\n  } else {\\n    ret_4 = ((texture (sampler_fc_main, uv_3) * sustain) - 0.003921569).xyz;\\n  };\\n  vec4 tmpvar_31;\\n  tmpvar_31.w = 1.0;\\n  tmpvar_31.xyz = ret_4;\\n  ret = tmpvar_31.xyz;\\n }\",comp:\"float xlat_mutablecross1;\\nvec2 xlat_mutabledz1;\\nfloat xlat_mutablerdist;\\nvec2 xlat_mutableuv0;\\nvec2 xlat_mutableuv4;\\nvec2 xlat_mutableuva;\\nvec2 xlat_mutableuvb;\\n shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1.x = q1;\\n  tmpvar_1.y = q2;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = q17;\\n  tmpvar_2.y = q18;\\n  vec2 uv_3;\\n  vec3 ret_4;\\n  uv_3 = (uv + (texsize.zw / 2.0));\\n  xlat_mutableuv0 = uv_3;\\n  vec2 uvi_5;\\n  uvi_5 = (uv_3 + vec2(0.002, 0.0));\\n  float tmpvar_6;\\n  tmpvar_6 = mix (((texture (sampler_blur1, uvi_5).xyz * scale1) + bias1).x, texture (sampler_main, uvi_5).x, 0.1);\\n  vec2 uvi_7;\\n  uvi_7 = (uv_3 - vec2(0.002, 0.0));\\n  float tmpvar_8;\\n  tmpvar_8 = mix (((texture (sampler_blur1, uvi_7).xyz * scale1) + bias1).x, texture (sampler_main, uvi_7).x, 0.1);\\n  xlat_mutabledz1.x = ((tmpvar_6 * float(\\n    (tmpvar_6 > 0.02)\\n  )) - (tmpvar_8 * float(\\n    (tmpvar_8 > 0.02)\\n  )));\\n  vec2 uvi_9;\\n  uvi_9 = (uv_3 + vec2(0.0, 0.002));\\n  float tmpvar_10;\\n  tmpvar_10 = mix (((texture (sampler_blur1, uvi_9).xyz * scale1) + bias1).x, texture (sampler_main, uvi_9).x, 0.1);\\n  vec2 uvi_11;\\n  uvi_11 = (uv_3 - vec2(0.0, 0.002));\\n  float tmpvar_12;\\n  tmpvar_12 = mix (((texture (sampler_blur1, uvi_11).xyz * scale1) + bias1).x, texture (sampler_main, uvi_11).x, 0.1);\\n  xlat_mutabledz1.y = ((tmpvar_10 * float(\\n    (tmpvar_10 > 0.02)\\n  )) - (tmpvar_12 * float(\\n    (tmpvar_12 > 0.02)\\n  )));\\n  uv_3 = (uv_3 + (xlat_mutabledz1 * 0.26));\\n  xlat_mutablerdist = ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1).z;\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 0.0;\\n  tmpvar_13.xyz = texture (sampler_noise_hq, vec2(((xlat_mutablerdist * 0.05) + (time * 0.02)))).xyz;\\n  vec4 tmpvar_14;\\n  tmpvar_14 = mix (tmpvar_13, roam_sin, vec4(0.5, 0.5, 0.5, 0.5));\\n  xlat_mutableuv4 = (uv_3 - 0.4);\\n  xlat_mutableuva = ((xlat_mutableuv4 - tmpvar_1) - ((\\n    ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1)\\n  .x * 0.4) * float(\\n    (xlat_mutablerdist > 0.2)\\n  )));\\n  xlat_mutableuvb = ((xlat_mutableuv4 - tmpvar_2) - ((\\n    (texture (sampler_blur1, uv_3).xyz * scale1)\\n   + bias1).x * 0.4));\\n  float angle_15;\\n  float tmpvar_16;\\n  tmpvar_16 = abs(xlat_mutableuva.x);\\n  if ((xlat_mutableuva.y >= 0.0)) {\\n    angle_15 = (1.0 - ((xlat_mutableuva.y - tmpvar_16) / (xlat_mutableuva.y + tmpvar_16)));\\n  } else {\\n    angle_15 = (3.0 - ((xlat_mutableuva.y + tmpvar_16) / (tmpvar_16 - xlat_mutableuva.y)));\\n  };\\n  float tmpvar_17;\\n  if ((xlat_mutableuva.x < 0.0)) {\\n    tmpvar_17 = -(angle_15);\\n  } else {\\n    tmpvar_17 = angle_15;\\n  };\\n  xlat_mutablecross1 = ((pow (\\n    sin((3.141593 * (tmpvar_17 + (5.1 * q20))))\\n  , 1.5) + (xlat_mutablerdist / 2.0)) + 0.1);\\n  vec3 tmpvar_18;\\n  tmpvar_18 = vec3((((1.0/(\\n    dot (xlat_mutableuva, xlat_mutableuva)\\n  )) / xlat_mutablecross1) * q3));\\n  vec3 tmpvar_19;\\n  tmpvar_19 = vec3(((1.0/(dot (xlat_mutableuvb, xlat_mutableuvb))) * q19));\\n  ret_4 = (clamp ((1.0 - \\n    (2.0 * xlat_mutablerdist)\\n  ), 0.0, 1.0) * ((\\n    (tmpvar_18 * tmpvar_14.xyz)\\n   + \\n    (tmpvar_19 * (1.0 - tmpvar_14.xyz))\\n  ) + 0.1));\\n  ret_4 = (ret_4 + (clamp (\\n    (2.0 * xlat_mutablerdist)\\n  , 0.0, 1.0) * (\\n    (tmpvar_18 * tmpvar_14.zyx)\\n   + \\n    (tmpvar_19 * (1.0 - tmpvar_14.zyx))\\n  )));\\n  ret_4 = (ret_4 + ((\\n    (texture (sampler_blur1, xlat_mutableuv0).xyz * scale1)\\n   + bias1).x * 0.05));\\n  ret_4 = (1.0 - exp(-(ret_4)));\\n  vec4 tmpvar_20;\\n  tmpvar_20.w = 1.0;\\n  tmpvar_20.xyz = ret_4;\\n  ret = tmpvar_20.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:2,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:5,wave_dots:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:1.169162,wave_smoothing:0,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:.99,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:0,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b2x:.3,b1ed:0},shapes:[{baseVals:{enabled:1,sides:53,rad:.05408,tex_ang:1.00531,tex_zoom:1.531168,r:.2,g:.7,g2:0,a2:1,border_b:0,border_a:0},init_eqs_str:\"a.q24=0;a.rad0=0;\",frame_eqs_str:\"a.x=.5;a.y=.5;a.a=a.q24;a.a2=0;a.a=0;a.rad0=bnot(a.q24)*a.rad0+div(randint(10),50)*a.q24;a.rad=a.rad0;a.b=0;a.r=div(randint(10),10);a.g=1;a.a2=a.q24;\"},{baseVals:{enabled:1,sides:44,textured:1,x:.7,rad:.2173,tex_zoom:3.277448,g:1,b:.5,r2:1,b2:1,a2:1,border_r:.5,border_g:.5,border_b:0},init_eqs_str:\"a.rot0=0;a.q1=0;a.posx=0;a.q24=0;a.posy=0;a.rad0=0;\",frame_eqs_str:\"a.textured=1;a.rot0+=div(1,a.fps)*a.q1;a.posx=(1-a.q24)*a.posx+a.q24*(.3+div(randint(100),200));a.posy=(1-a.q24)*a.posy+a.q24*(.3+div(randint(100),200));a.rad0=(1-a.q24)*a.rad0+a.q24*(.05+div(randint(100),300));a.rad=a.rad0;a.x=a.posx;a.y=a.posy;a.ang=a.rot0;\"},{baseVals:{enabled:1,sides:63,x:.503,rad:.038857,tex_zoom:2.2233,g:.1,r2:1,b2:1,a2:.7,border_a:0},init_eqs_str:\"a.rot0=0;a.q2=0;a.posx=0;a.q24=0;a.posy=0;a.rad0=0;a.q26=0;\",frame_eqs_str:\"a.textured=1;a.rot0+=div(.1,a.fps)*a.q2;a.posx=(1-a.q24)*a.posx+a.q24*(.3+div(randint(100),200));a.posy=(1-a.q24)*a.posy+a.q24*(.3+div(randint(100),200));a.rad0=(1-a.q24)*a.rad0+a.q24*(.05+div(randint(100),400));a.rad=a.rad0;a.x=a.posx+a.q26;a.y=a.posy;a.ang=a.rot0;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.q25=0;a.index=0;a.q22=0;a.q21=0;a.q29=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.rott=0;a.is_beat=0;a.q31=0;a.q23=0;a.k1=0;a.q24=0;a.t_rel=0;a.dec_slow=0;a.q10=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.p3=0;a.q3=0;a.t0=0;a.q7=0;a.ds=0;a.q28=0;a.q30=0;a.q20=0;a.p4=0;a.q8=0;\",frame_eqs_str:\"a.dec_med=pow(.7,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.beat=a.bass+a.mid+a.treb;a.beat*=a.beat;a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2*a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;\\na.q23=a.index;a.q24=a.is_beat;a.q26=a.bass_att+a.mid_att+a.treb_att+3;a.q27=a.index+1;a.q28=a.index2;a.q29=a.index2;a.monitor=a.q29;a.k1=a.is_beat*equal(mod(a.index,2),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.q5=Math.cos(div(a.time,17));a.q6=Math.sin(div(a.time,17));a.q7=-a.q2;a.q8=a.q1;a.ds=a.ds*a.dec_med+a.q24*(1-a.dec_med);a.q25=a.q24;a.q10=a.q22+3;a.t_rel=8*a.time;a.p3=\\na.p3*a.dec_med+(1-a.dec_med)*(100*a.index+0*a.q26);a.q30=a.p3;a.p4=a.dec_med*a.p4+(1-a.dec_med)*a.q27;a.q31=a.p4;a.zoom=1.1+.2*a.q1;a.warp=0;a.ob_size=.01;a.ob_r=.9;a.ob_g=1;a.ob_b=0;a.ob_a=.2*Math.sin(div(a.time,7));\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_3;\\n  tmpvar_3 = (sqrt(dot (tmpvar_2, tmpvar_2)) + (rand_frame * 13.0)).x;\\n  uv_1 = (uv + (clamp (\\n    ((sin(tmpvar_3) / cos(tmpvar_3)) / normalize(tmpvar_2))\\n  , vec2(-2.0, -2.0), vec2(2.0, 2.0)) / 16.0));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = ((q24 * (\\n    ((texture (sampler_main, uv_1).xyz + ((0.1 * vec3(\\n      ((q1 * cos((uv_1.x * 44.0))) - cos((uv_1.y * 82.0)))\\n    )) * (1.0 + texture (sampler_noise_lq, \\n      ((uv_1 / 16.0) + (time / 100.0))\\n    )).xyz)) * 0.98)\\n   - 0.025)) + ((1.0 - q24) * texture (sampler_main, uv_orig).xyz));\\n  ret = tmpvar_4.xyz;\\n }\",comp:\"vec2 xlat_mutabledz;\\nvec3 xlat_mutableneu;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutableuv3;\\n shader_body { \\n  vec2 uv2_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  xlat_mutabledz.x = dot ((texture (sampler_main, (uv + tmpvar_2)).xyz - texture (sampler_main, (uv - tmpvar_2)).xyz), vec3(0.32, 0.49, 0.29));\\n  xlat_mutabledz.y = dot ((texture (sampler_main, (uv + tmpvar_3)).xyz - texture (sampler_main, (uv - tmpvar_3)).xyz), vec3(0.32, 0.49, 0.29));\\n  uv2_1 = (uv - 0.5);\\n  vec2 tmpvar_4;\\n  tmpvar_4.y = 0.0;\\n  float tmpvar_5;\\n  tmpvar_5 = (time / 8.0);\\n  tmpvar_4.x = tmpvar_5;\\n  float tmpvar_6;\\n  tmpvar_6 = (q27 * 2.0);\\n  xlat_mutableuv3 = (((tmpvar_6 * uv2_1) * 0.1) + tmpvar_4);\\n  xlat_mutableuv3 = (fract(xlat_mutableuv3) * aspect.yx);\\n  xlat_mutableuv3 = ((0.1 * cos(\\n    (22.0 * xlat_mutableuv3)\\n  )) + (18.0 * xlat_mutabledz));\\n  float tmpvar_7;\\n  tmpvar_7 = clamp ((0.02 / sqrt(\\n    dot (xlat_mutableuv3, xlat_mutableuv3)\\n  )), 0.0, 1.0);\\n  vec4 tmpvar_8;\\n  tmpvar_8 = (1.0 + roam_cos);\\n  xlat_mutableneu = ((0.1 * vec3(tmpvar_7)) + ((0.45 * \\n    dot (vec3(tmpvar_7), vec3(0.32, 0.49, 0.29))\\n  ) * tmpvar_8).xyz);\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (xlat_mutableneu * 1.252262));\\n  vec2 tmpvar_9;\\n  tmpvar_9.y = 0.0;\\n  tmpvar_9.x = tmpvar_5;\\n  xlat_mutableuv3 = (((tmpvar_6 * uv2_1) * 0.1) + tmpvar_9);\\n  xlat_mutableuv3 = (fract(xlat_mutableuv3) * aspect.yx);\\n  xlat_mutableuv3 = ((0.1 * cos(\\n    (22.0 * xlat_mutableuv3)\\n  )) + (18.0 * xlat_mutabledz));\\n  float tmpvar_10;\\n  tmpvar_10 = clamp ((0.02 / sqrt(\\n    dot (xlat_mutableuv3, xlat_mutableuv3)\\n  )), 0.0, 1.0);\\n  xlat_mutableneu = ((0.1 * vec3(tmpvar_10)) + ((0.45 * \\n    dot (vec3(tmpvar_10), vec3(0.32, 0.49, 0.29))\\n  ) * tmpvar_8).xyz);\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * 1.252262));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = (xlat_mutableret1 + clamp ((\\n    (texture (sampler_main, uv).xyz * 4.0)\\n   * \\n    (0.2 + xlat_mutableret1)\\n  ), 0.0, 1.0));\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:6,wave_thick:1,modwavealphabyvolume:1,darken:1,wave_a:.001,wave_scale:.159809,wave_smoothing:.45,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,wave_y:.9,ob_r:1,ob_g:1,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1x:.6999,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index=0;a.q22=0;a.q21=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.movez=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q32=0;a.q7=0;a.q20=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.k1=a.is_beat*equal(a.index,0);a.p1=\\na.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_slow*a.p2+(1-a.dec_slow)*a.p1;a.rott=div(3.1416*a.p2,4);a.q27=a.index+1;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.zoom=1;a.rot=-0*a.index;a.q32=pow(.996,div(30,a.fps));a.q5=div(Math.sin(div(a.time,11))+1,5);a.movez+=div(.06,a.fps)*(1.1+a.q1);a.q6=a.movez;a.q7=.005*Math.sin(div(a.time,15));\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"vec3 xlat_mutablenoise;\\nvec2 xlat_mutablers;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutableuv1;\\n shader_body { \\n  vec3 copy_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = 0.5;\\n  tmpvar_2.y = q5;\\n  xlat_mutableuv1 = (uv - tmpvar_2);\\n  float tmpvar_3;\\n  tmpvar_3 = (1.0/(xlat_mutableuv1.y));\\n  xlat_mutablers0.x = (xlat_mutableuv1.x * tmpvar_3);\\n  xlat_mutablers0.y = tmpvar_3;\\n  xlat_mutablers.x = xlat_mutablers0.x;\\n  xlat_mutablers.y = (tmpvar_3 + (time * 0.3));\\n  xlat_mutablenoise = (vec3(dot (texture (sampler_pw_noise_lq, (xlat_mutablers / 63.0)), vec4(0.32, 0.49, 0.29, 0.0))) * (dot (texture (sampler_pw_noise_lq, \\n    (xlat_mutablers / 12.0)\\n  ), vec4(0.32, 0.49, 0.29, 0.0)) + 0.5));\\n  xlat_mutablenoise = (xlat_mutablenoise * (clamp (\\n    ((10.0 * xlat_mutablenoise) - 8.0)\\n  , 0.0, 1.0) * clamp (\\n    (2.0 / tmpvar_3)\\n  , 0.0, 1.0)));\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = uv.x;\\n  tmpvar_4.y = (uv.y - (0.024 / tmpvar_3));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = uv.x;\\n  tmpvar_5.y = (uv.y - (0.012 / tmpvar_3));\\n  copy_1 = (texture (sampler_main, tmpvar_4).xyz + texture (sampler_main, tmpvar_5).xyz);\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = uv.x;\\n  tmpvar_6.y = (uv.y - (0.006 / tmpvar_3));\\n  copy_1 = (copy_1 + texture (sampler_main, tmpvar_6).xyz);\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = ((xlat_mutablenoise + (\\n    (((copy_1 / 3.0) * (1.0 + slow_roam_cos).xyz) / 2.0)\\n   * 0.99)) - 0.005);\\n  ret = tmpvar_7.xyz;\\n }\",comp:\"vec3 xlat_mutablecont;\\nvec3 xlat_mutableneu;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutablers2;\\n shader_body { \\n  vec2 uv_1;\\n  float inten_2;\\n  float dist_3;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * aspect.xy);\\n  dist_3 = (1.0 - fract(q6));\\n  inten_2 = ((dist_3 * (1.0 - dist_3)) * 6.0);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = fract(((uv_1 * dist_3) + 0.55));\\n  xlat_mutableneu = texture (sampler_main, tmpvar_4).xyz;\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (xlat_mutableneu * inten_2));\\n  xlat_mutablecont = max (vec3(0.0, 0.0, 0.0), ((\\n    -(texture (sampler_main, tmpvar_4).xyz)\\n   + \\n    ((texture (sampler_blur1, (tmpvar_4 + q7)).xyz * scale1) + bias1)\\n  ) * inten_2));\\n  dist_3 = (1.0 - fract((0.5 + q6)));\\n  inten_2 = ((dist_3 * (1.0 - dist_3)) * 6.0);\\n  vec2 tmpvar_5;\\n  tmpvar_5 = fract(((uv_1 * dist_3) + 0.55));\\n  xlat_mutableneu = texture (sampler_main, tmpvar_5).xyz;\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_2));\\n  xlat_mutablecont = max (xlat_mutablecont, ((\\n    -(texture (sampler_main, tmpvar_5).xyz)\\n   + \\n    ((texture (sampler_blur1, (tmpvar_5 + q7)).xyz * scale1) + bias1)\\n  ) * inten_2));\\n  dist_3 = (1.0 - fract((1.0 + q6)));\\n  inten_2 = ((dist_3 * (1.0 - dist_3)) * 6.0);\\n  vec2 tmpvar_6;\\n  tmpvar_6 = fract(((uv_1 * dist_3) + 0.55));\\n  xlat_mutableneu = texture (sampler_main, tmpvar_6).xyz;\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_2));\\n  xlat_mutablecont = max (xlat_mutablecont, ((\\n    -(texture (sampler_main, tmpvar_6).xyz)\\n   + \\n    ((texture (sampler_blur1, (tmpvar_6 + q7)).xyz * scale1) + bias1)\\n  ) * inten_2));\\n  vec2 tmpvar_7;\\n  tmpvar_7.y = 5.0;\\n  tmpvar_7.x = (time / 12.0);\\n  xlat_mutablers2 = ((0.1 * cos(\\n    ((uv_1 * 3.0) + tmpvar_7)\\n  )) + (0.1 * xlat_mutableret1).xy);\\n  vec4 tmpvar_8;\\n  tmpvar_8.w = 1.0;\\n  tmpvar_8.xyz = (((-0.1 * xlat_mutableret1) + (\\n    (clamp ((0.005 / sqrt(\\n      dot (xlat_mutablers2, xlat_mutablers2)\\n    )), 0.0, 1.0) * vec3(18.0, 16.2, 10.8))\\n   * \\n    (0.2 + (0.3 * xlat_mutableret1))\\n  )) + ((\\n    dot (xlat_mutablecont, vec3(0.32, 0.49, 0.29))\\n   * \\n    (1.0 + slow_roam_cos)\\n  ) / 2.0).xyz);\\n  ret = tmpvar_8.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:6,wave_dots:1,modwavealphabyvolume:1,darken:1,wave_a:.001,wave_scale:1.740724,wave_smoothing:.45,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_g:.99,ob_size:0,ob_r:1,ob_b:1,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index4=0;a.index=0;a.q22=0;a.q21=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.index3=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.t_rel=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.movez=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.p3=0;a.q3=0;a.t0=0;a.q7=0;a.q28=0;a.q30=0;a.q20=0;a.q8=0;a.p3=0;a.t_rel=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),4);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.monitor=a.index4;\\na.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass_att+a.mid_att+a.treb_att+1;a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.p3=a.dec_med*a.p3+(1-a.dec_med)*a.p2;a.rott=div(3.14159265359*a.p3,2);a.q27=8-a.index;a.q28=a.index2+1;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.t_rel+=div(.6,a.fps)*a.q1*a.q1;a.q5=Math.cos(a.t_rel);a.q6=Math.sin(a.t_rel);a.q7=-a.q6;a.q8=a.q5;a.movez+=div(div(.6,\\na.fps),(1.4-a.q5)*(1.4-a.q5));a.q30=a.movez;a.zoom=1;\",pixel_eqs_str:\"a.dx=div(0*a.q28,8);\",warp:\"vec3 xlat_mutablenoise;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutablers;\\n shader_body { \\n  float z_1;\\n  mat2 tmpvar_2;\\n  tmpvar_2[uint(0)] = _qb.xy;\\n  tmpvar_2[1u] = _qb.zw;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = ((uv * tmpvar_2) - 0.5);\\n  float tmpvar_4;\\n  tmpvar_4 = (0.4 / abs(tmpvar_3.y));\\n  xlat_mutablers.x = (tmpvar_3.x * tmpvar_4);\\n  xlat_mutablers.y = (tmpvar_4 + q30);\\n  vec4 tmpvar_5;\\n  tmpvar_5 = texture (sampler_pw_noise_lq, (xlat_mutablers / 32.0));\\n  xlat_mutablenoise = (tmpvar_5.xyz * vec3(greaterThanEqual (tmpvar_5.xyz, vec3(0.9, 0.9, 0.9))));\\n  xlat_mutablenoise = (xlat_mutablenoise * (1.0 + (0.5 * \\n    (dot (texture (sampler_noise_hq, (16.0 * xlat_mutablers)), vec4(0.32, 0.49, 0.29, 0.0)) - 0.5)\\n  )));\\n  xlat_mutableret1 = xlat_mutablenoise;\\n  z_1 = (1.2 / abs(tmpvar_3.y));\\n  xlat_mutablers.x = (tmpvar_3.x * z_1);\\n  xlat_mutablers.y = (z_1 + q30);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_pw_noise_lq, (xlat_mutablers / 32.0));\\n  xlat_mutablenoise = (tmpvar_6.xyz * vec3(greaterThanEqual (tmpvar_6.xyz, vec3(0.9, 0.9, 0.9))));\\n  xlat_mutableret1 = (xlat_mutableret1 + xlat_mutablenoise);\\n  z_1 = (0.4 / abs(tmpvar_3.x));\\n  xlat_mutablers.y = (tmpvar_3.y * z_1);\\n  xlat_mutablers.x = (z_1 + q30);\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_pw_noise_lq, (xlat_mutablers / 32.0));\\n  xlat_mutablenoise = (tmpvar_7.xyz * vec3(greaterThanEqual (tmpvar_7.xyz, vec3(0.9, 0.9, 0.9))));\\n  xlat_mutablenoise = (xlat_mutablenoise * (1.0 + (0.5 * \\n    (dot (texture (sampler_noise_hq, (16.0 * xlat_mutablers)), vec4(0.32, 0.49, 0.29, 0.0)) - 0.5)\\n  )));\\n  xlat_mutableret1 = (xlat_mutableret1 + xlat_mutablenoise);\\n  z_1 = (1.2 / abs(tmpvar_3.x));\\n  xlat_mutablers.y = (tmpvar_3.y * z_1);\\n  xlat_mutablers.x = (z_1 + q30);\\n  vec4 tmpvar_8;\\n  tmpvar_8 = texture (sampler_pw_noise_lq, (xlat_mutablers / 32.0));\\n  xlat_mutablenoise = (tmpvar_8.xyz * vec3(greaterThanEqual (tmpvar_8.xyz, vec3(0.9, 0.9, 0.9))));\\n  xlat_mutableret1 = (xlat_mutableret1 + xlat_mutablenoise);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = xlat_mutableret1;\\n  ret = tmpvar_9.xyz;\\n }\",comp:\"uniform sampler2D sampler_clouds2;\\n shader_body { \\n  vec2 uv_1;\\n  vec2 uv1_2;\\n  uv_1 = (uv * aspect.xy);\\n  vec2 tmpvar_3;\\n  tmpvar_3.y = 0.0;\\n  tmpvar_3.x = texsize.z;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = 0.0;\\n  tmpvar_4.y = texsize.w;\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (dot (texture (sampler_main, (uv_1 - tmpvar_3)).xyz, vec3(0.32, 0.49, 0.29)) - dot (texture (sampler_main, (uv_1 + tmpvar_3)).xyz, vec3(0.32, 0.49, 0.29)));\\n  tmpvar_5.y = (dot (texture (sampler_main, (uv_1 - tmpvar_4)).xyz, vec3(0.32, 0.49, 0.29)) - dot (texture (sampler_main, (uv_1 + tmpvar_4)).xyz, vec3(0.32, 0.49, 0.29)));\\n  uv1_2 = ((0.3 * sin(\\n    ((uv_1 + (0.02 * time)) * 6.0)\\n  )) + (0.2 * tmpvar_5));\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_clouds2, (uv_orig + tmpvar_5));\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = (((-2.0 * \\n    (0.1 * texture (sampler_main, uv_1))\\n  .xyz) + (\\n    clamp (((0.004 * q26) / sqrt(dot (uv1_2, uv1_2))), 0.0, 1.0)\\n   * vec3(1.0, 0.8, 0.4))) + (0.4 * (tmpvar_6.xyz + \\n    (dot (tmpvar_6.xyz, vec3(0.32, 0.49, 0.29)) - 0.7)\\n  )));\\n  ret = tmpvar_7.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.780001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:5,wave_dots:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:5.552,wave_smoothing:.504,wave_mystery:-1,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_g:0,wave_b:0,ob_size:.06,ob_r:1,ob_g:1,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1x:.6999,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:5,additive:1,x:.9,rad:.22613,g:.6,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.x=.05+div(randint(900),1E3);a.y=.05+div(randint(900),1E3);a.ang=div(randint(320),100);\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.ready=0;a.index2=0;a.index4=0;a.index=0;a.q22=0;a.q21=0;a.sp0=0;a.q29=0;a.q1=0;a.dec_med=0;a.q5=0;a.index3=0;a.rott=0;a.is_beat=0;a.q31=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.go=0;a.q4=0;a.is_beat2=0;a.q26=0;a.p2=0;a.avg=0;a.movez=0;a.q19=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q32=0;a.q28=0;a.q30=0;a.q20=0;a.index4=randint(2);a.index3=randint(4);\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,4);a.index2=mod(a.index2+a.is_beat*bnot(a.index),4);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),4);a.index4=mod(a.index4+\\na.is_beat*bnot(a.index)*bnot(a.index2)*bnot(a.index3),2);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.ready=a.is_beat*bnot(a.ready)+bnot(a.is_beat2)*a.ready;a.is_beat2=a.ready*above(a.time,a.t0+.2);a.q19=a.is_beat2;a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.14159265359*a.p2,2);a.q27=a.index+1;a.q28=a.index2+1;a.q29=4*a.index3+1;a.q30=a.index4;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);\\na.q3=-a.q2;a.q4=a.q1;a.sp0=a.dec_slow*a.sp0+(a.q24+.05)*(1-a.dec_slow);a.go=a.go*a.dec_med+(1-a.dec_med)*(1-bnot(a.index2+a.index3));a.movez+=div(.015*30,a.fps)*a.go;a.q31=a.movez;a.q32=.5+.02*Math.sin(div(a.time,5));a.q5=mod(a.index4,2);a.zoom=1;a.rot=0;a.dx=.05*Math.max(Math.sin(div(a.time,9.7))-.95,0);a.dy=.002*(1-a.go);a.rot=50*(a.dx-a.dy);\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = ((uv * texsize.xy) * 0.03);\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = (cos((tmpvar_1.y * q1)) * sin(-(tmpvar_1.y)));\\n  tmpvar_2.y = (sin(tmpvar_1.x) * cos((tmpvar_1.y * q2)));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ((texture (sampler_main, (uv - \\n    ((tmpvar_2 * texsize.zw) * 18.0)\\n  )).xyz * 0.99) - 0.01);\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec3 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv).xyz;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = (texture (sampler_main, (uv - vec2(0.001, 0.0))).xyz - texture (sampler_main, (uv + vec2(0.001, 0.0))).xyz).x;\\n  tmpvar_3.y = (texture (sampler_main, (uv - vec2(0.0, 0.001))).xyz - texture (sampler_main, (uv + vec2(0.0, 0.001))).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    ((uv - 0.5) + 1.8)\\n  )) - (2.0 * tmpvar_3));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = (0.8 * ((0.3 * \\n    dot (tmpvar_2, vec3(0.32, 0.49, 0.29))\\n  ) + (\\n    (22.0 * clamp ((0.01 / sqrt(\\n      dot (uv1_1, uv1_1)\\n    )), 0.0, 1.0))\\n   * \\n    (tmpvar_2 + 0.1)\\n  )));\\n  ret = tmpvar_4.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,wrap:0,darken:1,wave_a:.001,wave_scale:.958,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:.05,ob_g:.1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:25.6,mv_y:9.6,mv_l:0,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.look=0;a.n=0;a.reg26=0;a.uvx0=0;a.reg34=0;a.reg28=0;a.reg23=0;a.q25=0;a.angchg=0;a.reg20=0;a.reg15=0;a.reg10=0;a.q12=0;a.v3=0;a.q22=0;a.q21=0;a.diry=0;a.q13=0;a.q6=0;a.posx=0;a.fps_=0;a.reg25=0;a.uvx=0;a.q1=0;a.travel=0;a.posz=0;a.q5=0;a.dirz=0;a.dec_s=0;a.reg16=0;a.slow=0;a.reg36=0;a.reg22=0;a.uvy=0;a.rotz=0;a.ly=0;a.dist_=0;a.q23=0;a.q24=0;a.reg24=0;a.cran0=0;a.ran2=0;a.q11=0;a.q10=0;a.reg14=0;a.posy=0;a.reg31=0;a.dirx=0;a.q4=0;a.start=0;a.reg12=0;a.reg13=0;a.c2=0;a.reg37=\\n0;a.s3=0;a.yslope=0;a.lampy=0;a.q16=0;a.xslope=0;a.q26=0;a.reg38=0;a.reg35=0;a.reg11=0;a.tx=0;a.avg=0;a.uvz=0;a.c3=0;a.uvy0=0;a.reg27=0;a.q19=0;a.beat=0;a.reg32=0;a.lx=0;a.reg21=0;a.uvz0=0;a.len=0;a.reg18=0;a.reg30=0;a.q2=0;a.q27=0;a.slen=0;a.q14=0;a.dist=0;a.reg17=0;a.v1=0;a.speed=0;a.s1=0;a.t0=0;a.s2=0;a.ran1=0;a.reg33=0;a.q7=0;a.ds=0;a.q28=0;a.lampx=0;a.ty=0;a.c1=0;a.v2=0;a.q20=0;a.q8=0;a.avg=.01;a.q7=.25;a.q8=randint(2)-1;a.q16=1+randint(2);a.q18=randint(.8)+.1;a.q30=1;a.q31=128;a.start=1;a.travel=\\n0;a.rotz=0;a.look=0;a.slow=0;a.t0=a.time+3;a.lampx=.5;a.lampy=.5;a.cran0=randint(1);for(var b=a.n=0;1E4>b;b++)a.gmegabuf[Math.floor(a.n)]=0,a.n+=1;for(b=a.n=0;1E4>b;b++)a.megabuf[Math.floor(a.n)]=0,a.n+=1;a.trelx=0;a.trely=0;a.trelz=0;a.reg20=1;a.reg21=0;a.reg22=0;a.reg23=0;a.reg24=1;a.reg25=0;a.reg26=0;a.reg27=0;a.reg28=1;b=0;do{b+=1;var c;a.ran1=div(randint(800),100);a.ran2=div(randint(800),100);a.ran3=div(randint(800),100);a.posx=randint(5)-2;a.posy=randint(5)-2;a.posz=randint(5)-2;a.c1=Math.cos(a.ran1);\\na.c2=Math.cos(a.ran2);a.c3=Math.cos(a.ran3);a.s1=Math.sin(a.ran1);a.s2=Math.sin(a.ran2);a.s3=Math.sin(a.ran3);a.reg20=a.c2*a.c1;a.reg21=a.c2*a.s1;a.reg22=-a.s2;a.reg23=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg24=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg25=a.s3*a.c2;a.reg26=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg27=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg28=a.c3*a.c2;a.dist=.001;var d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,\\n8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=\\na.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.05;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(c)&&1048576>\\nd);d=.06>a.dist?1:0}while(.00001<Math.abs(d)&&1048576>b);\",frame_eqs_str:\"a.fps_=0*a.fps_+1*(.00001<Math.abs(25>=a.fps?1:0)?a.fps:25+.5*(a.fps-25));a.dec_s=1-div(.06*30,a.fps_);a.beat=a.time>a.t0+3?1:0;a.t0=.00001<Math.abs(a.beat)?a.time:a.t0;a.speed=div(Math.min(.2,a.dist_-.02)*(1+2*a.avg)*(1-0*a.slow)*.7,a.q7);a.ds=a.ds*a.dec_s+div((1-a.dec_s)*a.speed*.25,a.fps_);a.rotz=.00001<Math.abs(.00001>Math.abs(a.rotz-0)?1:0)?a.beat*(randint(100)<20*a.travel?1:0)*(div(randint(10),10)-.3):bnot(a.beat*(30>randint(100)?1:0))*a.rotz;a.slow=.00001<Math.abs(bnot(a.slow))?\\na.beat*(6>randint(1E3*a.avg)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.look=.00001<Math.abs(bnot(a.look))?a.beat*(12>randint(1E3*a.speed)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.lx=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.lx;a.ly=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.ly;a.lampx=a.lampx*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.lx:.5);a.lampy=a.lampy*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.ly:.5);a.q1=a.lampx;a.q2=a.lampy;a.dirx=a.reg26;a.diry=a.reg27;a.dirz=\\na.reg28;a.posx+=a.ds*a.dirx;a.posy+=a.ds*a.diry;a.posz+=a.ds*a.dirz;a.q4=a.posx;a.q5=a.posy;a.q6=a.posz;a.angchg=(.2-a.dist_)*(.2>a.dist_?1:0)*2;a.travel=.00001<Math.abs(0<a.angchg?1:0)?0:a.travel+a.ds;a.v1=a.v1*a.dec_s+(1-a.dec_s)*a.rotz*a.ds;a.v2=a.v2*a.dec_s+div((1-a.dec_s)*a.angchg*a.xslope,a.fps_);a.v3=a.v3*a.dec_s+(1-a.dec_s)*(div(a.angchg*a.yslope,a.fps_)+2*a.v1*Math.sin(.1*a.time));a.reg30=a.reg20;a.reg31=a.reg21;a.reg32=a.reg22;a.reg33=a.reg23;a.reg34=a.reg24;a.reg35=a.reg25;a.reg36=a.reg26;\\na.reg37=a.reg27;a.reg38=a.reg28;a.n=0;for(var b=a.avg=0;5>b;b++){a.n+=1;a.ran1=div(randint(100),100);a.ran2=div(randint(100),200)-.25;a.tx=Math.cos(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.ty=Math.sin(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.c1=Math.cos(a.v1);a.c2=Math.cos(a.v2+a.ty);a.c3=Math.cos(a.v3+a.tx);a.s1=Math.sin(a.v1);a.s2=Math.sin(a.v2+a.ty);a.s3=Math.sin(a.v3+a.tx);a.reg10=a.c2*a.c1;a.reg11=a.c2*a.s1;a.reg12=-a.s2;a.reg13=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg14=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg15=a.s3*\\na.c2;a.reg16=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg17=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg18=a.c3*a.c2;a.reg20=a.reg30;a.reg21=a.reg31;a.reg22=a.reg32;a.reg23=a.reg33;a.reg24=a.reg34;a.reg25=a.reg35;a.reg26=a.reg36;a.reg27=a.reg37;a.reg28=a.reg38;a.q20=a.reg10*a.reg20+a.reg11*a.reg23+a.reg12*a.reg26;a.q21=a.reg10*a.reg21+a.reg11*a.reg24+a.reg12*a.reg27;a.q22=a.reg10*a.reg22+a.reg11*a.reg25+a.reg12*a.reg28;a.q23=a.reg13*a.reg20+a.reg14*a.reg23+a.reg15*a.reg26;a.q24=a.reg13*a.reg21+a.reg14*a.reg24+a.reg15*a.reg27;\\na.q25=a.reg13*a.reg22+a.reg14*a.reg25+a.reg15*a.reg28;a.q26=a.reg16*a.reg20+a.reg17*a.reg23+a.reg18*a.reg26;a.q27=a.reg16*a.reg21+a.reg17*a.reg24+a.reg18*a.reg27;a.q28=a.reg16*a.reg22+a.reg17*a.reg25+a.reg18*a.reg28;a.reg20=a.q20;a.reg21=a.q21;a.reg22=a.q22;a.reg23=a.q23;a.reg24=a.q24;a.reg25=a.q25;a.reg26=a.q26;a.reg27=a.q27;a.reg28=a.q28;a.dist=.002;var c,d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;\\na.uvx=8*(div(a.uvx,8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?\\n-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.1;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<\\nMath.abs(c)&&1048576>d);a.megabuf[Math.floor(a.n)]=a.megabuf[Math.floor(a.n)]*a.dec_s+(1-a.dec_s)*a.dist;a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5))}a.n=0;for(b=a.avg=0;5>b;b++)a.n+=1,a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5));a.xslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[1]-a.megabuf[3]),-3),3);a.yslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[4]-a.megabuf[2]),-3),3);a.monitor=a.avg;a.dist_=a.dist_*a.dec_s+(1-a.dec_s)*a.dist;a.q10=a.ds*a.q7;a.q14=Math.abs(a.ds)+2*(Math.abs(a.v1)+\\nMath.abs(a.v2)+Math.abs(a.v3))+div(1,255)+.05*a.start;a.q19=.6+.4*Math.sin(.02*a.time+6*a.cran0);a.start*=.9;a.q11=a.v1;a.q12=a.v2;a.q13=a.v3;a.monitor=a.q16;\",pixel_eqs_str:\"a.warp=0;a.zoom=1;a.dx=div(-a.q12,a.q16)*(1+0*pow(a.x-.5,2));a.dy=div(a.q13,a.q16)*(1+0*pow(a.y-.5,2));a.rot=a.q11;\",warp:\"float sustain;\\nfloat xlat_mutabledist;\\nfloat xlat_mutablestruc;\\nvec2 xlat_mutableuv1;\\nvec3 xlat_mutableuv2;\\n shader_body { \\n  mat3 tmpvar_1;\\n  tmpvar_1[uint(0)].x = q20;\\n  tmpvar_1[uint(0)].y = q23;\\n  tmpvar_1[uint(0)].z = q26;\\n  tmpvar_1[1u].x = q21;\\n  tmpvar_1[1u].y = q24;\\n  tmpvar_1[1u].z = q27;\\n  tmpvar_1[2u].x = q22;\\n  tmpvar_1[2u].y = q25;\\n  tmpvar_1[2u].z = q28;\\n  vec3 tmpvar_2;\\n  tmpvar_2.x = q4;\\n  tmpvar_2.y = q5;\\n  tmpvar_2.z = q6;\\n  sustain = (0.98 - q14);\\n  vec2 uv_3;\\n  vec3 ret_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv - 0.5);\\n  xlat_mutableuv1 = ((tmpvar_5 * aspect.xy) * q16);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_pc_main, uv);\\n  uv_3 = ((tmpvar_5 * (1.0 - \\n    (q10 / (1.0 - ((tmpvar_6.z + \\n      (0.003921569 * tmpvar_6.y)\\n    ) + (q10 * 0.7))))\\n  )) + 0.5);\\n  vec4 tmpvar_7;\\n  tmpvar_7 = fract((8.0 * texture (sampler_noise_lq, (uv_3 + rand_frame.yz))));\\n  xlat_mutabledist = tmpvar_7.x;\\n  if ((tmpvar_7.y > 0.2)) {\\n    vec3 tmpvar_8;\\n    tmpvar_8 = (tmpvar_7.xyz - vec3(0.4, 0.5, 0.5));\\n    vec2 uvi_9;\\n    uvi_9 = ((tmpvar_8.zy * 0.003) + uv_3);\\n    vec2 pix_10;\\n    vec4 nb2_11;\\n    vec4 nb_12;\\n    vec2 x_13;\\n    x_13 = (uvi_9 - 0.5);\\n    pix_10 = (texsize.zw * (1.0 + (\\n      sqrt(dot (x_13, x_13))\\n     * 8.0)));\\n    float tmpvar_14;\\n    tmpvar_14 = (q10 * 0.7);\\n    vec4 tmpvar_15;\\n    tmpvar_15 = texture (sampler_pc_main, (uvi_9 - pix_10));\\n    nb_12.x = (1.0 - ((tmpvar_15.z + \\n      (0.003921569 * tmpvar_15.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_16;\\n    tmpvar_16 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(1.0, -1.0))));\\n    nb_12.y = (1.0 - ((tmpvar_16.z + \\n      (0.003921569 * tmpvar_16.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_17;\\n    tmpvar_17 = texture (sampler_pc_main, (uvi_9 + pix_10));\\n    nb_12.z = (1.0 - ((tmpvar_17.z + \\n      (0.003921569 * tmpvar_17.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_18;\\n    tmpvar_18 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(-1.0, 1.0))));\\n    nb_12.w = (1.0 - ((tmpvar_18.z + \\n      (0.003921569 * tmpvar_18.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_19;\\n    tmpvar_19 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(0.0, -1.0))));\\n    nb2_11.x = (1.0 - ((tmpvar_19.z + \\n      (0.003921569 * tmpvar_19.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_20;\\n    tmpvar_20 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(1.0, 0.0))));\\n    nb2_11.y = (1.0 - ((tmpvar_20.z + \\n      (0.003921569 * tmpvar_20.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_21;\\n    tmpvar_21 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(0.0, 1.0))));\\n    nb2_11.z = (1.0 - ((tmpvar_21.z + \\n      (0.003921569 * tmpvar_21.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_22;\\n    tmpvar_22 = texture (sampler_pc_main, (uvi_9 + (pix_10 * vec2(-1.0, 0.0))));\\n    nb2_11.w = (1.0 - ((tmpvar_22.z + \\n      (0.003921569 * tmpvar_22.y)\\n    ) + tmpvar_14));\\n    vec4 tmpvar_23;\\n    tmpvar_23 = min (nb_12, nb2_11);\\n    nb_12.zw = tmpvar_23.zw;\\n    nb_12.xy = min (tmpvar_23.xy, tmpvar_23.zw);\\n    xlat_mutabledist = (min (nb_12.x, nb_12.y) + ((0.008 * tmpvar_8.x) * abs(tmpvar_8.y)));\\n  };\\n  vec4 tmpvar_24;\\n  tmpvar_24 = texture (sampler_pc_main, uv_3);\\n  float tmpvar_25;\\n  tmpvar_25 = min (xlat_mutabledist, (1.0 - (\\n    (tmpvar_24.z + (0.003921569 * tmpvar_24.y))\\n   + \\n    (q10 * 0.7)\\n  )));\\n  xlat_mutabledist = tmpvar_25;\\n  float tmpvar_26;\\n  tmpvar_26 = (tmpvar_25 + pow (tmpvar_25, 3.0));\\n  vec3 tmpvar_27;\\n  tmpvar_27.xy = (xlat_mutableuv1 * tmpvar_26);\\n  tmpvar_27.z = tmpvar_26;\\n  xlat_mutableuv2 = (((tmpvar_27 / q7) * tmpvar_1) + tmpvar_2);\\n  xlat_mutableuv2 = ((fract(\\n    ((xlat_mutableuv2 / 8.0) + 0.5)\\n  ) - 0.5) * 8.0);\\n  float li_28;\\n  vec3 zz0_29;\\n  vec3 zz_30;\\n  zz0_29 = (xlat_mutableuv2 + q8);\\n  li_28 = 0.0;\\n  zz_30 = ((2.0 * clamp (xlat_mutableuv2, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - xlat_mutableuv2);\\n  float tmpvar_31;\\n  tmpvar_31 = dot (zz_30, zz_30);\\n  if ((tmpvar_31 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_31 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_31);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_32;\\n  tmpvar_32 = dot (zz_30, zz_30);\\n  if ((tmpvar_32 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_32 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_32);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_33;\\n  tmpvar_33 = dot (zz_30, zz_30);\\n  if ((tmpvar_33 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_33 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_33);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_34;\\n  tmpvar_34 = dot (zz_30, zz_30);\\n  if ((tmpvar_34 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_34 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_34);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_35;\\n  tmpvar_35 = dot (zz_30, zz_30);\\n  if ((tmpvar_35 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_35 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_35);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_36;\\n  tmpvar_36 = dot (zz_30, zz_30);\\n  if ((tmpvar_36 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_36 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_36);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_37;\\n  tmpvar_37 = dot (zz_30, zz_30);\\n  if ((tmpvar_37 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_37 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_37);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  zz_30 = ((2.0 * clamp (zz_30, vec3(-1.0, -1.0, -1.0), vec3(1.0, 1.0, 1.0))) - zz_30);\\n  float tmpvar_38;\\n  tmpvar_38 = dot (zz_30, zz_30);\\n  if ((tmpvar_38 <= 0.25)) {\\n    zz_30 = (zz_30 * 4.0);\\n    li_28 = 24.0;\\n  } else {\\n    if ((tmpvar_38 <= 1.0)) {\\n      zz_30 = (zz_30 / tmpvar_38);\\n    };\\n  };\\n  zz_30 = ((2.6 * zz_30) + zz0_29);\\n  vec4 tmpvar_39;\\n  tmpvar_39.xyz = zz_30;\\n  tmpvar_39.w = li_28;\\n  float tmpvar_40;\\n  tmpvar_40 = sqrt(dot (zz_30, zz_30));\\n  xlat_mutablestruc = (sqrt(dot (tmpvar_39.xyw, tmpvar_39.xyw)) / 24.0);\\n  vec4 tmpvar_41;\\n  tmpvar_41 = texture (sampler_pc_main, uv_3);\\n  float tmpvar_42;\\n  float tmpvar_43;\\n  tmpvar_43 = (q10 * 0.7);\\n  tmpvar_42 = ((log(\\n    (1.0 + (tmpvar_40 / 24.0))\\n  ) * 0.02) * (1.0 - (1.0 - \\n    ((tmpvar_41.z + (0.003921569 * tmpvar_41.y)) + tmpvar_43)\\n  )));\\n  float tmpvar_44;\\n  vec4 tmpvar_45;\\n  tmpvar_45 = texture (sampler_pc_main, uv_3);\\n  tmpvar_44 = (1.0 - ((tmpvar_45.z + \\n    (0.003921569 * tmpvar_45.y)\\n  ) + tmpvar_43));\\n  if ((((tmpvar_25 <= tmpvar_44) && (tmpvar_40 < 24.0)) && (tmpvar_25 > 0.005))) {\\n    ret_4.x = (((1.0 - sustain) * xlat_mutablestruc) + (sustain * mix (texture (sampler_main, uv_3).xyz, \\n      ((texture (sampler_blur1, uv_3).xyz * scale1) + bias1)\\n    , vec3(\\n      (q14 * 4.0)\\n    )).x));\\n    float x_46;\\n    x_46 = ((1.0 - tmpvar_25) * 255.0);\\n    float ip_47;\\n    ip_47 = float(int(x_46));\\n    vec2 tmpvar_48;\\n    tmpvar_48.x = (x_46 - ip_47);\\n    tmpvar_48.y = (ip_47 / 255.0);\\n    ret_4.yz = tmpvar_48;\\n  } else {\\n    vec3 tmpvar_49;\\n    tmpvar_49.y = 0.0;\\n    tmpvar_49.x = sustain;\\n    tmpvar_49.z = (1.0 - tmpvar_42);\\n    vec3 tmpvar_50;\\n    tmpvar_50.xy = vec2(0.003921569, 0.0);\\n    tmpvar_50.z = (q14 / 6.0);\\n    ret_4 = ((texture (sampler_fc_main, uv_3).xyz * tmpvar_49) - tmpvar_50);\\n  };\\n  vec4 tmpvar_51;\\n  tmpvar_51.w = 1.0;\\n  tmpvar_51.xyz = ret_4;\\n  ret = tmpvar_51.xyz;\\n }\",comp:\" shader_body { \\n  vec3 tmpvar_1;\\n  tmpvar_1.x = q4;\\n  tmpvar_1.y = q5;\\n  tmpvar_1.z = q6;\\n  mat3 tmpvar_2;\\n  tmpvar_2[uint(0)].x = q20;\\n  tmpvar_2[uint(0)].y = q23;\\n  tmpvar_2[uint(0)].z = q26;\\n  tmpvar_2[1u].x = q21;\\n  tmpvar_2[1u].y = q24;\\n  tmpvar_2[1u].z = q27;\\n  tmpvar_2[2u].x = q22;\\n  tmpvar_2[2u].y = q25;\\n  tmpvar_2[2u].z = q28;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q1;\\n  tmpvar_3.y = q2;\\n  vec2 uv_4;\\n  vec3 ret_5;\\n  uv_4 = (((uv - 0.5) * 0.9) + 0.5);\\n  vec3 tmpvar_6;\\n  tmpvar_6.xy = ((uv_4 - 0.5) * min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - \\n    ((texture (sampler_blur2, uv_4).xyz * scale2) + bias2)\\n  .z)));\\n  tmpvar_6.z = min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - (\\n    (texture (sampler_blur2, uv_4).xyz * scale2)\\n   + bias2).z));\\n  float tmpvar_7;\\n  tmpvar_7 = clamp ((abs(\\n    ((1.0 - ((texture (sampler_blur2, uv_4).xyz * scale2) + bias2).z) - clamp ((1.0 - (\\n      (texture (sampler_blur2, tmpvar_3).xyz * scale2)\\n     + bias2).z), 0.1, 0.4))\\n  ) + 0.2), 0.0, 1.0);\\n  float tmpvar_8;\\n  tmpvar_8 = clamp (((1.0 - \\n    exp(-(((texture (sampler_blur1, uv_4).xyz * scale1) + bias1).x))\\n  ) - 0.2), 0.0, 1.0);\\n  ret_5 = ((mix (texture (sampler_main, uv_4).xyz, \\n    ((texture (sampler_blur1, uv_4).xyz * scale1) + bias1)\\n  , vec3(tmpvar_7)).x * (0.2 + \\n    ((1.0 - tmpvar_7) * (1.0 - min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - \\n      ((texture (sampler_blur2, uv_4).xyz * scale2) + bias2)\\n    .z))))\\n  )) * (1.0 + (0.5 * \\n    sin((((tmpvar_6 / q7) * tmpvar_2) + tmpvar_1))\\n  )));\\n  vec3 tmpvar_9;\\n  tmpvar_9.xy = vec2(0.0, 1.0);\\n  tmpvar_9.z = (tmpvar_8 * 3.0);\\n  ret_5 = (mix (ret_5, tmpvar_9, vec3(tmpvar_8)) + ((\\n    pow ((1.0 - mix (texture (sampler_main, uv_4).xyz, (\\n      (texture (sampler_blur1, uv_4).xyz * scale1)\\n     + bias1), vec3(0.8, 0.8, 0.8)).z), 3.0)\\n   * \\n    (0.5 + (0.5 * slow_roam_cos))\\n  ) * q19).xyz);\\n  ret_5 = (1.0 - exp((-2.0 * ret_5)));\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = ret_5;\\n  ret = tmpvar_10.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,wave_mode:7,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.958178,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,ob_r:.3999,ob_a:.2,ib_size:0,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:100,additive:1,x:.26,y:.2,rad:.393173,tex_zoom:.9355,r:0,g:.55,b:.5,g2:.4,b2:.4,a2:.07,border_r:.3,border_g:.7,border_b:.8,border_a:0},init_eqs_str:\"a.g0=0;a.y0=0;a.q1=0;a.x0=0;a.q24=0;a.q26=0;a.r0=0;a.trig=0;a.q2=0;a.b0=0;a.rad0=0;\",frame_eqs_str:\"a.trig=a.q24;a.x0=a.x0*bnot(a.trig)+a.trig*(.5+div(randint(100),200));a.y0=a.y0*bnot(a.trig)+a.trig*(.5+div(randint(100),200));a.x0+=div(.1*a.q1*(3+a.q26),a.fps);a.y0+=div(.1*a.q2*(3+a.q26),a.fps);a.x0-=Math.floor(a.x0);a.y0-=Math.floor(a.y0);a.tex_ang=a.time;a.tex_zoom=a.q1;a.ang=div(a.time,100)*a.q2;a.x=a.x0;a.y=a.y0;a.rad0=a.rad0*bnot(a.trig)+a.trig*(.04+div(randint(100),1E3));a.rad=a.rad0;a.r0=bnot(a.trig)*a.r0+div(a.trig*randint(10),10);a.g0=bnot(a.trig)*a.g0+div(a.trig*\\nrandint(10),10);a.b0=bnot(a.trig)*a.b0+div(a.trig*randint(10),10);a.r=a.r0;a.b=a.b0;a.g=a.g0;a.r2=0;a.b2=0;a.g2=0;a.a=1;a.a2=.3;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index4=0;a.index=0;a.q22=0;a.q21=0;a.q1=0;a.dec_med=0;a.index3=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q11=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q28=0;a.q20=0;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),3);a.monitor=a.index4;\\na.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.q11=Math.min(a.q22,3);a.k1=a.is_beat*equal(a.index,0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.14159265359*a.p2,2);a.q27=8-a.index;a.q28=a.index2;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.zoom=1+.02*a.q1;a.rot=.01*a.q2;a.dx=0*a.index;a.wave_a=0;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\"float xlat_mutabledx;\\nfloat xlat_mutabledy;\\nvec2 xlat_mutableuv2;\\nvec2 xlat_mutablezz;\\n shader_body { \\n  vec2 uv_1;\\n  vec3 crisp_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3.y = 0.0;\\n  tmpvar_3.x = texsize.w;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = 0.0;\\n  tmpvar_4.y = texsize.z;\\n  xlat_mutablezz = ((uv * texsize.xy) * 0.01);\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (cos((xlat_mutablezz.y * q1)) * sin(-(xlat_mutablezz.y)));\\n  tmpvar_5.y = (sin(xlat_mutablezz.x) * cos((xlat_mutablezz.y * q2)));\\n  uv_1 = (uv - ((tmpvar_5 * texsize.zw) * (8.0 + \\n    (6.0 * q11)\\n  )));\\n  xlat_mutableuv2 = (((uv_1 / 2.0) * q27) / 4.0);\\n  xlat_mutabledx = dot ((texture (sampler_main, (xlat_mutableuv2 + tmpvar_3)).xyz - texture (sampler_main, (xlat_mutableuv2 - tmpvar_3)).xyz), vec3(0.32, 0.49, 0.29));\\n  xlat_mutabledy = dot ((texture (sampler_main, (xlat_mutableuv2 + tmpvar_4)).xyz - texture (sampler_main, (xlat_mutableuv2 - tmpvar_4)).xyz), vec3(0.32, 0.49, 0.29));\\n  float tmpvar_6;\\n  tmpvar_6 = (0.15 + (0.1 * q28));\\n  vec2 tmpvar_7;\\n  tmpvar_7 = (xlat_mutableuv2 + (time / 100.0));\\n  xlat_mutabledx = (xlat_mutabledx + (tmpvar_6 * (texture (sampler_noise_hq, tmpvar_7).x - 0.5)));\\n  xlat_mutabledy = (xlat_mutabledy + (tmpvar_6 * (texture (sampler_noise_hq, tmpvar_7).y - 0.5)));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = xlat_mutabledx;\\n  tmpvar_8.y = xlat_mutabledy;\\n  xlat_mutablezz = tmpvar_8;\\n  crisp_2 = (texture (sampler_main, (uv_1 + (tmpvar_8 * 0.04))).xyz + texture (sampler_main, uv_1).xyz);\\n  crisp_2 = (crisp_2 * 0.5);\\n  crisp_2 = (crisp_2 + ((0.05 * \\n    (0.9 + (0.1 * roam_cos))\\n  .xyz) - (\\n    sqrt(dot (tmpvar_8, tmpvar_8))\\n   * 0.3)));\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = ((crisp_2 * 0.97) - 0.015);\\n  ret = tmpvar_9.xyz;\\n }\",comp:\"vec2 xlat_mutabledz;\\nvec3 xlat_mutableneu;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutableuv3;\\n shader_body { \\n  vec2 uv2_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = texsize.z;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = 0.0;\\n  tmpvar_3.y = texsize.w;\\n  xlat_mutabledz.x = dot ((texture (sampler_main, (uv + tmpvar_2)).xyz - texture (sampler_main, (uv - tmpvar_2)).xyz), vec3(0.32, 0.49, 0.29));\\n  xlat_mutabledz.y = dot ((texture (sampler_main, (uv + tmpvar_3)).xyz - texture (sampler_main, (uv - tmpvar_3)).xyz), vec3(0.32, 0.49, 0.29));\\n  uv2_1 = (uv - 0.5);\\n  xlat_mutableuv3 = ((0.2 * uv2_1) + 0.5);\\n  float tmpvar_4;\\n  tmpvar_4 = (time / 2.0);\\n  xlat_mutableuv3 = ((0.2 * cos(\\n    ((42.0 * fract(xlat_mutableuv3)) + tmpvar_4)\\n  )) + (99.0 * xlat_mutabledz));\\n  float tmpvar_5;\\n  tmpvar_5 = clamp ((0.01 / sqrt(\\n    dot (xlat_mutableuv3, xlat_mutableuv3)\\n  )), 0.0, 1.0);\\n  xlat_mutableneu = ((0.1 * vec3(tmpvar_5)) + (0.9 * dot (vec3(tmpvar_5), vec3(0.32, 0.49, 0.29))));\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (xlat_mutableneu * 1.252262));\\n  xlat_mutableuv3 = ((0.2 * uv2_1) + 0.5);\\n  xlat_mutableuv3 = ((0.2 * cos(\\n    ((42.0 * fract(xlat_mutableuv3)) + tmpvar_4)\\n  )) + (99.0 * xlat_mutabledz));\\n  float tmpvar_6;\\n  tmpvar_6 = clamp ((0.01 / sqrt(\\n    dot (xlat_mutableuv3, xlat_mutableuv3)\\n  )), 0.0, 1.0);\\n  xlat_mutableneu = ((0.1 * vec3(tmpvar_6)) + (0.9 * dot (vec3(tmpvar_6), vec3(0.32, 0.49, 0.29))));\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * 1.252262));\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = (xlat_mutableret1 + clamp ((\\n    (16.0 * ((0.5 * texture (sampler_main, (uv + \\n      (0.1 * xlat_mutabledz)\\n    )).xyz) + 0.01))\\n   * \\n    (0.1 + xlat_mutableret1)\\n  ), 0.0, 1.0));\\n  ret = tmpvar_7.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:4,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.527,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.8,wave_g:.49,ob_size:.015,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b2x:.3,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:506,sep:116,spectrum:1,thick:1,scaling:1.07408,smoothing:0,a:.7},init_eqs_str:\"a.n=0;a.m=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.n=Math.floor((a.reg00+.5)*a.sample);a.m=30001+div(a.n,div(a.reg00,a.reg01));a.gmegabuf[Math.floor(a.m)]=a.value1+a.value2;a.x=a.gmegabuf[Math.floor(1E4+a.n)];a.y=a.gmegabuf[Math.floor(15E3+a.n)];a.a=a.gmegabuf[Math.floor(2E4+a.n)];a.b=Math.min(Math.max(a.gmegabuf[Math.floor(25E3+a.n)],0),1);a.r=1-a.b;a.g=.5;\"},{baseVals:{enabled:1,samples:506,thick:1,scaling:.89152,smoothing:.82},init_eqs_str:\"a.n=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.n=Math.floor((a.reg00+.5)*a.sample)+a.reg00;a.x=a.gmegabuf[Math.floor(1E4+a.n)];a.y=a.gmegabuf[Math.floor(15E3+a.n)];a.a=a.gmegabuf[Math.floor(2E4+a.n)];a.b=Math.min(Math.max(a.gmegabuf[Math.floor(25E3+a.n)],0),1);a.r=1-a.b;a.g=.5;\"},{baseVals:{enabled:1,samples:506,thick:1,scaling:.89152,smoothing:.82},init_eqs_str:\"a.n=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.n=Math.floor((a.reg00+.5)*a.sample)+2*a.reg00;a.x=a.gmegabuf[Math.floor(1E4+a.n)];a.y=a.gmegabuf[Math.floor(15E3+a.n)];a.a=a.gmegabuf[Math.floor(2E4+a.n)];a.b=Math.min(Math.max(a.gmegabuf[Math.floor(25E3+a.n)],0),1);a.r=1-a.b;a.g=.5;\"},{baseVals:{enabled:1,samples:506,spectrum:1,thick:1},init_eqs_str:\"a.n=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.n=Math.floor((a.reg00-.5)*a.sample)+3*a.reg00;a.x=a.gmegabuf[Math.floor(1E4+a.n)];a.y=a.gmegabuf[Math.floor(15E3+a.n)];a.a=a.gmegabuf[Math.floor(2E4+a.n)];a.b=Math.min(Math.max(a.gmegabuf[Math.floor(25E3+a.n)],0),1);a.r=1-a.b;a.g=.5;\"}],init_eqs_str:\"a.xang=0;a.fov=0;a.hell=0;a.cbeat=0;a.index2=0;a.bindex=0;a.ran4=0;a.index=0;a.dec_v=0;a.yang=0;a.q29=0;a.q6=0;a.amp_=0;a.xlen=0;a.smooth=0;a.q1=0;a.dec_med=0;a.sum=0;a.q5=0;a.dec_f=0;a.trely=0;a.flen=0;a.reg01=0;a.my=0;a.oz=0;a.imag=0;a.is_beat=0;a.yind=0;a.oy0a=0;a.dec_slow=0;a.ran2=0;a.ind=0;a.z0=0;a.ylen=0;a.real=0;a.ran4_=0;a.ran3=0;a.q4=0;a.mz=0;a.oy0=0;a.amp=0;a.tc0=0;a.oy=0;a.avg=0;a.mx=0;a.vol=0;a.ran2_=0;a.peak=0;a.decc=0;a.q2=0;a.bd_bt=0;a.zang=0;a.q3=0;a.reg00=0;\\na.trelz=0;a.q32=0;a.ran3_=0;a.q28=0;a.trelx=0;a.q30=0;a.ox=0;a.xind=0;for(var b=a.index=0;7E4>b;b++)a.megabuf[Math.floor(a.index)]=0,a.gmegabuf[Math.floor(a.index)]=0,a.index+=1;a.zang=1;a.yang=0;a.zang=2;\",frame_eqs_str:\"a.xlen=45;a.ylen=45;a.flen=30;a.reg00=div(a.xlen*a.ylen,4);a.reg01=div(a.reg00,4);a.dec_med=1-div(.06*30,a.fps);a.dec_slow=1-div(.6,a.fps);a.dec_f=pow(.8,div(30,a.fps));a.q30=a.dec_slow;a.smooth=Math.max(1,pow(6,div(a.fps,30))-2);a.cbeat=a.bass+a.mid+a.treb;a.decc=.00001<Math.abs(a.vol>a.cbeat?1:0)?.8:a.dec_med;a.vol=a.vol*a.decc+(1-a.decc)*a.cbeat;a.avg=a.avg*a.dec_slow+a.cbeat*(1-a.dec_slow);a.is_beat=above(a.cbeat,1.5*a.avg)*above(a.time,a.tc0+.2);a.tc0=.00001<Math.abs(bor(a.is_beat,\\na.bd_bt))?a.time:a.tc0;a.peak=.00001<Math.abs(a.is_beat)?a.cbeat:a.peak*a.dec_med;a.ind=0;a.sum=0;a.amp=.01;for(var b=0;b<a.reg01;b++)a.sum+=div(a.gmegabuf[Math.floor(a.ind+3E4)],a.reg01),a.amp+=pow(a.gmegabuf[Math.floor(a.ind+3E4)],2),a.ind+=1;a.ind=0;a.amp_=a.amp_*a.dec_med+600*div((1-a.dec_med)*sqrt(a.amp),a.reg01);for(b=0;b<a.reg01;b++)a.megabuf[Math.floor(a.ind+3E4)]=div(a.gmegabuf[Math.floor(a.ind+3E4)]-a.sum,a.amp_),a.ind+=1;for(b=a.index2=0;b<a.flen;b++){a.index=0;a.real=0;for(var c=a.imag=\\n0;c<a.flen;c++)a.real+=Math.cos(6.28*div(a.index,a.flen)*a.index2)*a.megabuf[Math.floor(div(a.index*a.reg01,8)+30002)],a.imag+=Math.sin(6.28*div(a.index,a.flen)*a.index2)*a.megabuf[Math.floor(div(a.index*a.reg01,8)+30002)],a.index+=1;a.megabuf[Math.floor(1E4+a.index2)]=a.megabuf[Math.floor(1E4+a.index2)]*a.dec_f+a.real;a.megabuf[Math.floor(15E3+a.index2)]=a.megabuf[Math.floor(15E3+a.index2)]*a.dec_f+a.imag;a.index2+=1}a.ind=1;for(b=0;b<div(a.flen,2);b++){a.cx=a.megabuf[Math.floor(1E4+a.ind)];a.cy=\\na.megabuf[Math.floor(15E3+a.ind)];a.yind=-1;for(c=0;3>c;c++){a.xind=-1;for(var d=0;3>d;d++)a.ox=mod((a.cx+.5)*a.xlen+a.xind,a.xlen),a.oy=mod((a.cy+.5)*a.ylen+a.yind,a.ylen),a.amp=3*(a.cx*a.cx+a.cy*a.cy),a.megabuf[Math.floor(a.oy*a.ylen+a.ox)]-=div(div(60,a.fps)*sqrt(a.amp)*above(a.amp,.02),1+a.xind*a.xind+a.yind*a.yind),a.xind+=1;a.yind+=1}a.ind+=1}for(b=a.yind=0;b<a.ylen;b++){for(c=a.xind=0;c<a.xlen;c++)a.megabuf[Math.floor(a.yind*a.ylen+a.xind+5E3)]=a.dec_med*(div(a.gmegabuf[Math.floor(a.yind*a.ylen+\\nmod(a.xind+1,a.xlen))]+a.gmegabuf[Math.floor(a.yind*a.ylen+mod(a.xlen+a.xind-1,a.xlen))]+a.gmegabuf[Math.floor(mod(a.yind+1,a.ylen)*a.ylen+a.xind)]+a.gmegabuf[Math.floor(mod(a.yind+a.ylen-1,a.ylen)*a.ylen+a.xind)]+a.gmegabuf[Math.floor(a.yind*a.ylen+a.xind)]*a.smooth*4,2+2*a.smooth)-a.megabuf[Math.floor(a.yind*a.ylen+a.xind)]),a.xind+=1;a.yind+=1}a.bindex+=a.is_beat;.00001<Math.abs(a.is_beat&&.00001>Math.abs(mod(a.bindex,4)-0)?1:0)?a.ran2=div(randint(100)-30,60):0;.00001<Math.abs(a.is_beat&&.00001>\\nMath.abs(mod(a.bindex,4)-2)?1:0)?a.ran3=div(randint(100)-30,60):0;.00001<Math.abs(a.is_beat&&.00001>Math.abs(mod(a.bindex,6)-2)?1:0)?a.ran4=div(randint(100)-30,60):0;a.dec_v=Math.min(Math.max(0,1-div(8*a.vol,a.fps)),a.dec_slow);a.ran2_=a.ran2_*a.dec_v+(1-a.dec_v)*a.ran2;a.ran3_=a.ran3_*a.dec_v+(1-a.dec_v)*a.ran3;a.ran4_=a.ran4_*a.dec_v+(1-a.dec_v)*a.ran4;a.trelx+=div(div(a.ran2_,a.fps),7);a.trely+=div(div(a.ran3_,a.fps),2);a.trelz+=div(div(a.ran4_,a.fps),6);a.zang=6*Math.sin(a.trelz);a.xang=6*Math.sin(div(a.zang,\\n5)+a.trelx);a.yang=6*Math.sin(0*div(a.zang,3)+a.trely);a.q1=Math.cos(a.xang);a.q2=Math.sin(a.xang);a.q3=Math.cos(a.yang);a.q4=Math.sin(a.yang);a.q5=Math.cos(a.zang);a.q6=Math.sin(a.zang);a.fov=1;for(b=a.yind=0;b<a.ylen;b++){for(c=a.xind=0;c<a.xlen;c++)a.ind=a.yind*a.ylen+a.xind,a.megabuf[Math.floor(a.ind)]=a.gmegabuf[Math.floor(a.ind)],a.gmegabuf[Math.floor(a.ind)]=a.megabuf[Math.floor(a.ind+5E3)],a.oz=a.yind-div(a.ylen,2),a.ox=.00001<Math.abs(bnot(mod(a.yind,2)))?a.xind:a.xlen-a.xind-1,a.oy0a=div(a.oy0+\\na.oy0a,2.5),a.oy=a.gmegabuf[Math.floor(a.yind*a.ylen+a.ox)],a.oy0=a.oy,a.ox-=div(a.xlen,2),a.mx=a.ox*a.q5-a.oy*a.q6,a.my=a.ox*a.q6+a.oy*a.q5,a.ox=a.mx,a.oy=a.my,a.mx=a.ox*a.q3+a.oz*a.q4,a.mz=-a.ox*a.q4+a.oz*a.q3,a.ox=a.mx,a.oz=a.mz,a.my=a.oy*a.q1-a.oz*a.q2,a.mz=a.oy*a.q2+a.oz*a.q1,a.z0=90+40*Math.sin(14*a.trelz),a.oy=a.my,a.oz=a.mz+a.z0,a.gmegabuf[Math.floor(1E4+a.ind)]=div(a.fov*a.ox,a.oz)+.5,a.gmegabuf[Math.floor(15E3+a.ind)]=div(a.fov*a.oy,a.oz)+.5,a.hell=Math.max(Math.min(.5+div(a.oy0,4),1),.1),\\na.gmegabuf[Math.floor(2E4+a.ind)]=Math.max(Math.min(a.hell*(.5+.1*(a.oy0a-a.oy0)),1),.1),a.gmegabuf[Math.floor(25E3+a.ind)]=div(a.oy0,16)+.5,a.xind+=1;a.yind+=1}a.q29=div(50*a.fov,a.z0);a.q32=a.aspecty;a.q28=Math.min(div(a.vol,3)-.3,1);a.monitor=a.is_beat;\",pixel_eqs_str:\"a.rot=0;a.zoom=1.1;a.warp=0;a.dy=.02;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = vec3(0.0, 0.0, 0.0);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\"vec3 xlat_mutablecol;\\n shader_body { \\n  vec2 uv_1;\\n  uv_1 = uv;\\n  int iter_3;\\n  vec3 ret_4;\\n  ret_4 = texture (sampler_main, uv).xyz;\\n  iter_3 = int((clamp (q29, 0.0, 1.0) * 8.0));\\n  for (int n_2 = 1; n_2 < iter_3; n_2++) {\\n    vec2 tmpvar_5;\\n    tmpvar_5.y = 0.0;\\n    tmpvar_5.x = float(n_2);\\n    vec2 tmpvar_6;\\n    tmpvar_6.y = 0.0;\\n    tmpvar_6.x = float(n_2);\\n    ret_4 = max (max (ret_4, texture (sampler_main, (uv_1 - \\n      (texsize.zw * tmpvar_5)\\n    )).xyz), texture (sampler_main, (uv_1 + (texsize.zw * tmpvar_6))).xyz);\\n  };\\n  float tmpvar_7;\\n  tmpvar_7 = clamp ((1.0 - (2.0 * \\n    dot (ret_4, vec3(0.32, 0.49, 0.29))\\n  )), 0.0, 1.0);\\n  float tmpvar_8;\\n  float tmpvar_9;\\n  tmpvar_9 = (uv.x - 0.5);\\n  tmpvar_8 = (0.5 / ((\\n    (uv.y + ((tmpvar_9 * 1.4) * (q5 * q3)))\\n   - 0.4) + (0.3 * q4)));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = (tmpvar_8 * tmpvar_9);\\n  tmpvar_10.y = tmpvar_8;\\n  vec3 tmpvar_11;\\n  tmpvar_11.x = q2;\\n  tmpvar_11.y = q4;\\n  tmpvar_11.z = q6;\\n  xlat_mutablecol = (0.5 + (0.5 * tmpvar_11));\\n  float x_12;\\n  x_12 = ((uv.x - (\\n    dot (ret_4, vec3(0.32, 0.49, 0.29))\\n   * 5.0)) + 0.5);\\n  ret_4 = (ret_4 * (q28 + (\\n    ((6.0 * (0.05 / sqrt(\\n      (x_12 * x_12)\\n    ))) * (1.0 + xlat_mutablecol))\\n   / 2.0)));\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = q1;\\n  tmpvar_13.y = (q3 + time);\\n  float tmpvar_14;\\n  tmpvar_14 = (((\\n    (texture (sampler_noise_lq, (tmpvar_10 + tmpvar_13)).x * tmpvar_7)\\n   * \\n    float((tmpvar_8 > 0.0))\\n  ) * 0.2) * min (1.0, (1.0/(tmpvar_8))));\\n  ret_4 = (ret_4 + tmpvar_14);\\n  ret_4 = (ret_4 + ((\\n    (sin((12.0 * q2)) * tmpvar_7)\\n   * tmpvar_14) * dot (\\n    (12.0 * ((texture (sampler_blur1, (tmpvar_10 - vec2(-0.5, 0.3))).xyz * scale1) + bias1))\\n  , vec3(0.32, 0.49, 0.29))));\\n  ret_4 = (ret_4 + ((\\n    ((0.5 / abs(tmpvar_8)) * normalize(xlat_mutablecol))\\n   * \\n    float((tmpvar_8 < 0.0))\\n  ) * tmpvar_7));\\n  vec4 tmpvar_15;\\n  tmpvar_15.w = 1.0;\\n  tmpvar_15.xyz = ret_4;\\n  ret = tmpvar_15.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1.980001,decay:.5,echo_zoom:.999998,echo_alpha:.5,echo_orient:3,modwavealphabyvolume:1,darken:1,wave_a:.001,wave_scale:10.437056,wave_smoothing:.45,wave_mystery:.08,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.4595,warpscale:2.0067,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:.99,ob_size:0,ob_r:1,ob_g:1,ob_b:1,ib_size:0,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.4999,mv_g:.4999,mv_b:.4999,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,rad:.048958,tex_ang:1.00531,tex_zoom:1.531168,r:.5,g:1,b:.9,r2:.83,g2:.93,b2:.8,a2:1,border_b:0,border_a:0},init_eqs_str:\"a.trel=0;a.q20=0;a.q28=0;a.q26=0;\",frame_eqs_str:\"a.trel=div(a.time,2)+a.q20;a.x=.5+Math.sin(2*a.trel);a.y=.5+Math.cos(1.3*a.trel+div(a.q28,3));a.a=div(a.q26,4)+.2;\"},{baseVals:{enabled:0}},{baseVals:{enabled:1,x:.503,rad:.038857,tex_zoom:.609857,g:.1,a:.9,r2:1,b2:1,border_r:.5,border_g:.5,border_b:.5,border_a:0},init_eqs_str:\"a.is_beat=0;a.t0=0;a.q21=0;\",frame_eqs_str:\"a.x=div(randint(10),10);a.y=div(randint(10),10);a.r=div(randint(4),3);a.g=div(randint(4),3);a.b=div(randint(4),3);a.is_beat=above(a.time,a.t0+.03);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.a=Math.min(div(a.q21,2),.9)*a.is_beat;a.rad=div(a.a*a.a,3);\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,sep:120,additive:1,scaling:.891519,smoothing:.82,a:.6},init_eqs_str:\"a.k1=0;a.k2=0;a.xi=0;a.yi=0;a.dx=0;a.dy=0;a.q22=0;a.t2=0;a.t1=1+.3*(.01*randint(101)-.01*randint(101));a.t2=1+.3*(.01*randint(101)-.01*randint(101));a.t3=1+.3*(.01*randint(101)-.01*randint(101));a.t4=1+.3*(.01*randint(101)-.01*randint(101));a.t5=1+.3*(.01*randint(101)-.01*randint(101));a.t6=1+.3*(.01*randint(101)-.01*randint(101));a.t7=1+.3*(.01*randint(101)-.01*randint(101));a.t8=1+.3*(.01*randint(101)-.01*randint(101));\",frame_eqs_str:\"a.t2+=a.bass_att;\",point_eqs_str:\"a.k1=mod(100*a.sample,8);a.k2=bnot(a.k1);a.xi=a.value1*a.k2+a.xi*(1-a.k2);a.yi=a.value2*(1-a.k2)+a.yi*a.k2;a.dx=.99*a.dx+a.xi;a.dy=.99*a.dy+a.yi;a.x=.5+div(a.xi,2);a.y=.5+div(a.yi,2);a.a=div(a.q22,8);a.a=Math.min(a.a,.2);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index=0;a.q22=0;a.q21=0;a.fade=0;a.q1=0;a.dec_med=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q32=0;a.q20=0;a.fade=.5;\",frame_eqs_str:\"a.dec_med=pow(.9,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.q26=a.bass+a.mid+a.treb;a.k1=a.is_beat*equal(a.index,0);a.p1=\\na.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.14159265358*a.p2,2);a.q27=a.index+1;a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.zoom=1;a.rot=-0*a.index;a.fade=a.fade*a.dec_med+pow(.996,div(30,a.fps))*(1-a.dec_med);a.q32=a.fade;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 zz_1;\\n  mat2 tmpvar_2;\\n  tmpvar_2[uint(0)] = _qa.xy;\\n  tmpvar_2[1u] = _qa.zw;\\n  zz_1 = (((\\n    (uv - vec2(0.5, 0.5))\\n   * texsize.xy) * (0.015 * q27)) * tmpvar_2);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = (((q32 * texture (sampler_main, \\n    (uv + ((clamp (\\n      (sin(zz_1) / cos(zz_1))\\n    , vec2(-20.0, -20.0), vec2(20.0, 20.0)) * texsize.zw) * 8.0))\\n  ).xyz) + (\\n    (0.03 * texture (sampler_noise_lq, ((uv * 0.3) + (0.01 * rand_frame).xy)))\\n  .xyz * \\n    (1.0 - ((texture (sampler_blur1, uv).xyz * scale1) + bias1))\\n  )) - 0.02);\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1 = texture (sampler_main, uv);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ((tmpvar_1.xyz + clamp (\\n    (3.0 * (((texture (sampler_blur1, \\n      (uv - (0.01 * tmpvar_1.xyz).xy)\\n    ).xyz * scale1) + bias1) - vec3(0.1, 0.1, 0.2)))\\n  , 0.0, 1.0)) * 1.3);\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:.952,echo_alpha:.5,echo_orient:3,wave_mode:6,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:2.103,wave_smoothing:.54,wave_mystery:.38,modwavealphastart:.81,modwavealphaend:1.4,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:0,wave_g:0,wave_b:0,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:1,sides:16,thickoutline:1,textured:1,num_inst:3,x:.73,rad:.29466,tex_zoom:1.87511,r:.7,g:.7,b:1,g2:0,border_b:0,border_a:0},init_eqs_str:\"a.trig=0;a.q25=0;a.x0=0;a.y0=0;\",frame_eqs_str:\"a.trig=a.q25;a.a=.8*a.trig;a.a2=0;a.x0=a.x0*bnot(a.trig)+div(a.trig*randint(100),100);a.y0=a.y0*bnot(a.trig)+div(a.trig*randint(100),100);a.tex_ang=randint(20);a.rad=.1+div(randint(10),8);a.x=a.x0;a.y=a.y0;a.r=.7+.3*Math.sin(div(a.time,12));a.b=.7+.3*Math.sin(div(a.time,15));a.g=.7+.3*Math.sin(div(a.time,8));a.r2=a.r;a.b2=a.b;a.g2=a.g;\"},{baseVals:{enabled:1,sides:36,thickoutline:1,textured:1,num_inst:4,x:.3,rad:.05429,ang:1.25664,tex_ang:.37699,tex_zoom:1.02841,g:.7,b:.5,r2:1,g2:0,border_g:.59,border_b:0,border_a:0},init_eqs_str:\"a.q31=0;a.q32=0;a.q30=0;\",frame_eqs_str:\"a.x=a.q31;a.y=a.q32;a.rad=.06;a.tex_ang=a.time;a.a=a.q30;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q25=0;a.index=0;a.q12=0;a.q22=0;a.q21=0;a.q15=0;a.q29=0;a.q6=0;a.dec_med=0;a.q5=0;a.mindev=0;a.trel=0;a.t0a=0;a.mov1=0;a.vis=0;a.is_beat=0;a.q31=0;a.q23=0;a.q24=0;a.dec_slow=0;a.q11=0;a.hpeak=0;a.q10=0;a.sdev=0;a.med=0;a.spb=0;a.dir=0;a.spb_=0;a.q16=0;a.rota=0;a.q19=0;a.vol=0;a.peak=0;a.trig1=0;a.wamp=0;a.speed=0;a.t0=0;a.vol_=0;a.q32=0;a.q7=0;a.wamp_=0;a.q30=0;a.q20=0;a.q8=0;a.t0a=a.time;a.t0=a.time+.5;a.spb_=.5;a.volb=.5;a.volx=.5;a.vol_=1;a.index=0;a.mov1=0;a.sdev=.1;a.wamp_=\\n.1;\",frame_eqs_str:\"a.dec_med=pow(.8,div(30,a.fps));a.dec_slow=pow(.95,div(30,a.fps));a.vol=div(a.bass+a.med+a.treb,1.5);a.vol_=a.vol_*a.dec_slow+(1-a.dec_slow)*a.vol;a.is_beat=above(a.vol,a.vol_+2*a.peak)*above(a.time,a.t0+.45*a.spb_);a.t0a=.00001<Math.abs(a.is_beat)?a.t0:a.t0a;a.t0=.00001<Math.abs(a.is_beat)?a.time:a.t0;a.peak=.00001<Math.abs(a.is_beat)?a.vol:a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,128);a.spb=Math.min(a.t0-a.t0a+.01,2);a.spb_=.00001<Math.abs(a.is_beat)?.9*a.spb_+.1*a.spb:\\na.spb_;a.q20=a.vol_;a.q21=above(a.vol,2.7*a.vol_);a.q22=Math.max(0,a.peak-0)+.01;a.hpeak=a.hpeak*a.dec_med+(1-a.dec_med)*a.q21;a.q23=a.hpeak*(1+randint(8));a.q24=a.is_beat;a.q25=div(a.q22*(1+randint(8)),8);a.mindev=Math.min(Math.min(Math.abs(a.spb-a.spb_),Math.abs(2*a.spb-a.spb_)),Math.abs(a.spb-2*a.spb_));a.sdev=.00001<Math.abs(a.is_beat)?.8*a.sdev+.2*pow(a.mindev+.02,2):a.sdev;a.speed=div(div(1,a.fps),a.spb_);a.trel+=a.speed;a.q10=a.trel;a.wamp=Math.min(div(.001,a.sdev),.15);a.wamp_=a.wamp_*a.dec_slow+\\n(1-a.dec_slow)*a.wamp;a.q11=Math.sin(div(a.trel,2))*a.wamp_;a.q12=div(a.wamp_,2);a.rota=2*a.wamp_*Math.sin(div(a.trel,3));a.q5=Math.cos(a.rota);a.q6=Math.sin(a.rota);a.q7=-a.q6;a.q8=a.q5;a.q15=.05*-Math.sin(.5*a.trel);a.q16=div(Math.cos(a.trel)*a.wamp_,2);a.q19=.1*Math.sin(div(a.trel,12));a.trig1=bnot(a.vis)*bnot(randint(100*a.fps));a.vis=.00001<Math.abs(a.trig1)?1:a.vis*below(a.mov1,1);a.q30=a.vis;a.mov1=.00001<Math.abs(a.vis)?a.mov1+div(2*a.speed,a.fps):-1;a.dir=.00001<Math.abs(a.trig1)?randint(100):\\na.dir;a.q31=.5+.5*Math.cos(a.dir)*a.mov1;a.q32=.5+.5*Math.sin(a.dir)*a.mov1;a.q29=2+Math.sin(div(a.time,17));a.monitor=a.wamp;\",pixel_eqs_str:\"a.zoom=1+.02*a.rad*a.q20;a.dy=-0;a.warp=.1;a.dx=0;\",warp:\"mat2 ofs;\\nvec2 xlat_mutableuv2;\\nfloat xlat_mutablezv;\\n shader_body { \\n  mat2 tmpvar_1;\\n  tmpvar_1[uint(0)].x = texsize.z;\\n  tmpvar_1[uint(0)].y = 0.0;\\n  tmpvar_1[1u].x = 0.0;\\n  tmpvar_1[1u].y = texsize.w;\\n  ofs = (tmpvar_1 * 4.0);\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = ((uv.x - 0.5) + q19);\\n  tmpvar_2.y = (uv.y - 0.5);\\n  vec2 v_3;\\n  v_3.x = ofs[0].x;\\n  v_3.y = ofs[1].x;\\n  vec2 v_4;\\n  v_4.x = ofs[0].x;\\n  v_4.y = ofs[1].x;\\n  vec2 v_5;\\n  v_5.x = ofs[0].y;\\n  v_5.y = ofs[1].y;\\n  vec2 v_6;\\n  v_6.x = ofs[0].y;\\n  v_6.y = ofs[1].y;\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = (texture (sampler_main, (uv + v_3)) - texture (sampler_main, (uv - v_4))).z;\\n  tmpvar_7.y = (texture (sampler_main, (uv + v_5)) - texture (sampler_main, (uv - v_6))).z;\\n  xlat_mutablezv = (time * 0.08);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = (0.02 * time);\\n  tmpvar_8.y = (-0.05 * time);\\n  xlat_mutableuv2 = ((3.0 * tmpvar_2) + tmpvar_8);\\n  vec3 tmpvar_9;\\n  tmpvar_9.xy = xlat_mutableuv2;\\n  tmpvar_9.z = xlat_mutablezv;\\n  vec3 tmpvar_10;\\n  tmpvar_10.xy = (xlat_mutableuv2 * vec2(2.0, 2.0));\\n  tmpvar_10.z = (xlat_mutablezv * 2.0);\\n  vec3 tmpvar_11;\\n  tmpvar_11.xy = (xlat_mutableuv2 * vec2(4.0, 4.0));\\n  tmpvar_11.z = (xlat_mutablezv * 3.0);\\n  vec3 tmpvar_12;\\n  tmpvar_12.xy = (xlat_mutableuv2 * vec2(8.0, 8.0));\\n  tmpvar_12.z = (xlat_mutablezv * 7.0);\\n  xlat_mutableuv2 = (tmpvar_2 + ((\\n    (((texture (sampler_noisevol_hq, tmpvar_9).z + (texture (sampler_noisevol_hq, tmpvar_10).z / 2.0)) + (texture (sampler_noisevol_hq, tmpvar_11).z / 4.0)) + (texture (sampler_noisevol_hq, tmpvar_12).z / 8.0))\\n   - 1.0) * 0.1));\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 1.0;\\n  tmpvar_13.xyz = (((\\n    (((0.2 + (\\n      (((1.0 + bass_att) * 0.01) / sqrt(dot (xlat_mutableuv2, xlat_mutableuv2)))\\n     * \\n      (1.0 + normalize(slow_roam_cos))\\n    .xyz)) + ((rand_preset.z - 0.5) * xlat_mutableuv2.y)) * 0.1)\\n   / 2.0) + (texture (sampler_main, \\n    (uv - (0.02 * tmpvar_7))\\n  ).xyz * 0.9)) - 0.01);\\n  ret = tmpvar_13.xyz;\\n }\",comp:\"float xlat_mutablerain;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutablers;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutableuv1;\\nfloat xlat_mutablewater;\\nfloat xlat_mutablez;\\n shader_body { \\n  vec2 uv_1;\\n  mat2 tmpvar_2;\\n  tmpvar_2[uint(0)] = _qb.xy;\\n  tmpvar_2[1u] = _qb.zw;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q15;\\n  tmpvar_3.y = q16;\\n  uv_1 = (((\\n    (((uv - 0.5) * tmpvar_2) * 0.7)\\n   * aspect.xy) + 0.5) + tmpvar_3);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = 0.5;\\n  tmpvar_4.y = ((q11 + 0.6) + (q12 * (\\n    sin(((uv_1.x * 6.0) + q10))\\n   * \\n    cos(((uv_1.y * 8.0) + (time * 0.6)))\\n  )));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (uv_1.x * 4.0);\\n  tmpvar_5.y = uv_1.y;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = (uv_1.x * 4.0);\\n  tmpvar_6.y = uv_1.y;\\n  xlat_mutablerain = (texture (sampler_noise_lq, ((tmpvar_5 * 0.2) - time)).x + texture (sampler_noise_lq, ((tmpvar_6 * 0.5) - time)).x);\\n  float tmpvar_7;\\n  tmpvar_7 = (time / 4.0);\\n  xlat_mutablerain = (xlat_mutablerain * clamp ((\\n    (q20 / 2.0)\\n   - \\n    abs(sin(((uv.x / 4.0) - tmpvar_7)))\\n  ), 0.0, 1.0));\\n  xlat_mutableuv1 = ((uv_1 - tmpvar_4) + (xlat_mutablerain * 0.01));\\n  vec3 tmpvar_8;\\n  tmpvar_8.x = xlat_mutableuv1.y;\\n  tmpvar_8.y = (xlat_mutableuv1.x * 2.0);\\n  tmpvar_8.z = (time * 0.2);\\n  xlat_mutableuv1 = (xlat_mutableuv1 + ((texture (sampler_noisevol_hq, tmpvar_8) * \\n    (0.5 - abs(xlat_mutableuv1.y))\\n  ).x * 0.05));\\n  float tmpvar_9;\\n  tmpvar_9 = clamp ((128.0 * xlat_mutableuv1.y), 0.0, 1.0);\\n  xlat_mutablez = (0.4 / xlat_mutableuv1.y);\\n  xlat_mutablers0.x = (xlat_mutableuv1.x * xlat_mutablez);\\n  xlat_mutablers0.y = xlat_mutablez;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = (xlat_mutablers0.x + tmpvar_7);\\n  tmpvar_10.y = (xlat_mutablez + (time * 4.0));\\n  xlat_mutablers = (tmpvar_10 * tmpvar_9);\\n  xlat_mutablewater = (texture (sampler_noise_hq, xlat_mutablers) + texture (sampler_noise_hq, ((xlat_mutablers / 4.0) - (time / 8.0)))).x;\\n  xlat_mutablewater = (xlat_mutablewater - 1.0);\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = (xlat_mutableuv1.x + 0.5);\\n  tmpvar_11.y = abs((0.5 - xlat_mutableuv1.y));\\n  xlat_mutableret1 = (((texture (sampler_main, uv_1).xyz * \\n    (1.0 - tmpvar_9)\\n  ) + (\\n    ((texture (sampler_main, (tmpvar_11 - (0.2 * xlat_mutablewater))).xyz * tmpvar_9) * pow ((1.0/(xlat_mutablez)), 0.2))\\n   * \\n    (2.0 - rand_preset.y)\\n  )) + ((\\n    (((0.25 + rand_preset.y) * (1.0 + (rand_preset.x * xlat_mutablewater))) * tmpvar_9)\\n   / xlat_mutablez) * vec3(0.0, 0.1, 0.1)));\\n  xlat_mutableret1 = (xlat_mutableret1 + (0.06 * xlat_mutablerain));\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 1.0;\\n  tmpvar_12.xyz = (1.0 - exp((\\n    (-(pow (xlat_mutableret1, vec3(1.5, 1.5, 1.5))) * 8.0)\\n   * \\n    (1.0 + (q23 * 4.0))\\n  )));\\n  ret = tmpvar_12.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:.952,echo_alpha:.5,echo_orient:3,wave_mode:4,additivewave:1,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.527,wave_smoothing:.45,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.8,wave_g:.49,ob_size:.015,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:12,additive:1,num_inst:571,x:.26,y:.2,rad:.39317,tex_zoom:.9355,g2:0,a2:.2,border_g:0,border_b:0,border_a:0},init_eqs_str:\"a.px=0;a.fov=0;a.q12=0;a.copy=0;a.q18=0;a.tht=0;a.q15=0;a.flim=0;a.q1=0;a.q5=0;a.py=0;a.mov1=0;a.vis=0;a.oz=0;a.k1=0;a.q11=0;a.z0=0;a.q10=0;a.copies=0;a.v0=0;a.q4=0;a.burst=0;a.oy=0;a.rota=0;a.q19=0;a.q17=0;a.spur=0;a.q2=0;a.q14=0;a.rotmode=0;a.q3=0;a.t0=0;a.phi=0;a.ox=0;\",frame_eqs_str:\"a.mov1=a.q1;a.rotmode=a.q3;a.burst=a.q10;a.copies=a.q14;a.copy=div(mod(a.instance,a.copies),a.copies);a.k1=div(Math.floor(div(a.instance,a.copies))*a.copies,a.num_inst);a.k1=div(Math.floor(a.k1*a.burst),a.burst)+div(a.k1,a.burst);a.mov1=a.mov1+a.k1-a.copy*a.q2;a.mov1-=Math.floor(a.mov1);a.rotmode-=a.copy*a.q4;a.rota=a.mov1*a.rotmode*a.q5*2;a.mov1=a.mov1*(1-a.rotmode)+a.rotmode*(.5+.025*a.k1);a.mov1=pow(a.mov1,.9);a.tht=Math.sin(a.k1*a.k1*123.2)*(.2+0*a.rotmode);a.phi=1613.3*\\na.k1+a.rota;a.v0=(1+.3*Math.cos(13234.7*a.k1))*a.q5;a.z0=div(a.q18,a.q5);a.t0=.4;a.spur=(a.mov1-a.t0)*above(a.mov1-a.t0,0)*a.q15;a.py=a.spur*Math.sin(6.28*a.copy);a.px=a.spur*Math.cos(6.28*a.copy);a.fov=2*a.z0;a.ox=(a.mov1-a.q19)*a.v0*Math.sin(a.tht)*Math.cos(a.phi);a.oy=a.mov1*a.v0*(1-a.mov1)*Math.cos(2*a.tht)-.25;a.oz=a.mov1*a.v0*Math.sin(a.tht)*Math.sin(a.phi);a.ox=a.ox+(a.k1-.5)*a.q17+a.px;a.oy-=a.py;a.oz+=3*a.py;a.rad=div(.025*a.z0,a.oz+a.z0)*(1-.5*a.mov1);a.x=.5+div(a.fov*a.ox,a.oz+a.z0);a.y=\\n.5+div(a.fov*a.oy,a.oz+a.z0);a.vis=above(a.mov1,a.q11)*below(a.mov1,a.q12)*above(a.oz,-a.z0);a.a=div(.7*a.vis,sqrt(a.copies))*(1+above(a.spur,0));a.a2=0;a.g=a.flim;\"},{baseVals:{enabled:1,sides:13,additive:1,num_inst:212,x:.65,y:.38,rad:1.26467,tex_zoom:.73678,g:1,a:.3,r2:1,g2:0,border_r:.5,border_g:.5,border_b:0,border_a:0},init_eqs_str:\"a.mov2=0;a.q25=0;a.copy=0;a.tht=0;a.q29=0;a.trel=0;a.mov1=0;a.oz=0;a.k1=0;a.z0=0;a.copies=0;a.k2=0;a.oy=0;a.stray=0;a.q27=0;a.size0=0;a.phase=0;a.t0=0;a.phi=0;a.ox=0;\",frame_eqs_str:\"a.trel=a.q25;a.copies=1;a.copy=div(mod(a.instance,a.copies),a.copies);a.k1=div(Math.floor(div(a.instance,a.copies))*a.copies,a.num_inst);a.k2=a.k1+.5;a.trel=a.trel;a.t0=.5;a.phase=above(a.trel,a.t0);a.mov1=bnot(a.phase)*(a.t0-a.trel+.01*a.k1);a.mov2=a.phase*(a.trel-a.t0);a.stray=1.2-a.phase;a.tht=3.14*Math.sin(1234.2*a.k1+a.time*bnot(a.phase))*a.stray;a.phi=3245.1*a.k1;a.size0=.02;a.ox=a.mov1*a.q27+Math.sin(a.tht)*Math.cos(a.phi)*(a.size0+a.mov2*(2+a.k1));a.oy=12*a.mov1+div(Math.cos(a.tht)*\\n(a.size0+4*a.mov2*a.k2)*(1-a.mov2*a.k2),2);a.oz=8*a.mov1+Math.sin(a.tht)*Math.sin(a.phi)*(a.size0+2*a.mov2*(2+a.k1));a.z0=1;a.rad=div(div(.03,a.oz+a.z0),1+div(a.trel*randint(100),50));a.x=.5+div(a.ox,a.oz+a.z0);a.y=a.q29+.2+div(a.oy,a.oz+a.z0);a.a=.1*bnot(a.phase)+a.phase*Math.max(0,1-0*a.mov2)*above(a.oz+a.z0,0)*.8;\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.stop=0;a.q25=0;a.index=0;a.q12=0;a.q18=0;a.q22=0;a.q21=0;a.q13=0;a.q15=0;a.q29=0;a.q6=0;a.q1=0;a.dec_med=0;a.q5=0;a.vshift=0;a.lo=0;a.xtime=0;a.t0a=0;a.mov1=0;a.is_beat=0;a.q31=0;a.q23=0;a.q24=0;a.dec_slow=0;a.q11=0;a.hpeak=0;a.q10=0;a.copies=0;a.q4=0;a.med=0;a.hi=0;a.vs0=0;a.bursts=0;a.spb_=0;a.volb=0;a.q16=0;a.q26=0;a.q19=0;a.q17=0;a.vol=0;a.peak=0;a.spur=0;a.q2=0;a.q27=0;a.q14=0;a.rotmode=0;a.wind=0;a.tmp=0;a.xbase=0;a.speed=0;a.q3=0;a.t0=0;a.vol_=0;a.q32=0;a.spread=0;\\na.q28=0;a.q30=0;a.q20=0;a.volx=0;a.t0a=a.time;a.t0=a.time+.5;a.spb_=.5;a.volb=.5;a.volx=.5;a.vol_=1;a.index=0;a.mov1=0;\",frame_eqs_str:\"a.dec_med=pow(.85,div(30,a.fps));a.dec_slow=pow(.95,div(30,a.fps));a.vol=div(a.bass+a.med+a.treb,1.5);a.vol_=a.vol_*a.dec_slow+(1-a.dec_slow)*a.vol;a.is_beat=above(a.vol,.1+a.vol_+2*a.peak)*above(a.time,a.t0+.45*a.spb_);a.t0a=.00001<Math.abs(a.is_beat)?a.t0:a.t0a;a.t0=.00001<Math.abs(a.is_beat)?a.time:a.t0;a.peak=.00001<Math.abs(a.is_beat)?a.vol:a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,128);a.spb_=.00001<Math.abs(a.is_beat)?.9*a.spb_+.1*Math.min(a.t0-a.t0a+.01,2):a.spb_;\\na.q20=a.vol_;a.q21=above(a.vol,2.7*a.vol_);a.q22=Math.max(0,a.peak-0)+.01;a.hpeak=a.hpeak*a.dec_med+(1-a.dec_med)*a.q21;a.q23=a.hpeak*(1+randint(8));a.q24=a.is_beat;a.volb=bnot(a.q24)*a.volb+a.q24*a.vol_;a.volx+=div(.2,a.fps)*Math.max(Math.min(a.volb-a.volx,1),-1)*bnot(a.rotmode);a.rotmode=Math.max(Math.min(4*Math.sin(div(a.mov1,2.4)-1.5)-3,1),0);a.stop=(.00001<Math.abs(bnot(a.stop))?bnot(mod(a.index,24)):below(a.lo,1))*bnot(a.rotmode);a.spread=.00001<Math.abs(below(a.lo,1))?a.spread:div(randint(2)*\\nrandint(12),20);a.copies=.00001<Math.abs(below(a.lo,1))?a.copies:3+randint(2)*randint(8);a.spur=.00001<Math.abs(below(a.lo,1))?a.spur:.1*above(a.copies,5);a.bursts=.00001<Math.abs(below(a.lo,1))?a.bursts:1+randint(2)*randint(6);a.xbase=.00001<Math.abs(below(a.lo,1))?a.xbase:randint(2)*(div(randint(10),10)-.5);a.vs0=.00001<Math.abs(a.is_beat*bnot(mod(a.index,77)))?div(randint(5),4)-.7:a.vs0;a.vshift+=div(3,a.fps)*Math.max(Math.min(a.vs0-a.vshift,.04),-.04);a.speed=div(div(.15,a.fps),a.spb_);a.tmp=\\na.mov1;a.mov1+=a.speed;a.q2=a.mov1-a.tmp;a.q1=a.mov1;a.tmp=a.rotmode;a.q3=a.rotmode;a.q4=a.rotmode-a.tmp;a.tmp=a.volx;a.q5=a.volx;a.q6=a.q5-a.tmp;a.q10=a.bursts;a.lo=.00001<Math.abs(a.stop)?a.lo+a.speed:0;a.hi=.00001<Math.abs(above(a.lo,1))?0:a.hi+a.speed;a.q11=a.lo;a.q12=a.hi;a.q13=0*sqrt(a.q22);a.q14=a.copies;a.q15=a.spur*(1-a.rotmode);a.q16=0;a.q17=a.spread*(1-a.rotmode);a.q18=div(1,Math.min(Math.max(10*Math.sin(div(a.mov1,2)),1),4));a.q19=a.xbase;a.xtime=.00001<Math.abs(a.stop*a.q21*above(a.xtime,\\n2))?0:a.xtime+div(.2,a.fps);a.q25=a.xtime;a.q26=.00001<Math.abs(below(a.xtime,.55))?pow(32,512*-sqr(a.xtime-.55)):pow(2,3*-sqr(a.xtime-.55));a.q27=4*Math.sin(div(a.time,29));a.wind+=div(.5,a.fps)*(a.volx-1.5);a.q28=a.wind;a.q29=a.vshift;a.q30=Math.max(Math.sin(div(a.mov1,2.2))*Math.sin(div(a.time,7)),0)+a.q26;a.q31=2*a.q5-2+Math.sin(div(a.time,17));a.q32=3+2*Math.sin(div(a.time,27));a.monitor=a.vshift;\",pixel_eqs_str:\"a.zoom=1;a.dy=-0;a.warp=0;\",warp:\"mat2 ofs;\\nfloat xlat_mutablegreen;\\nfloat xlat_mutablek1;\\nfloat xlat_mutablered;\\nvec2 xlat_mutableuv2;\\nfloat xlat_mutablewave;\\nfloat xlat_mutablezv;\\n shader_body { \\n  mat2 tmpvar_1;\\n  tmpvar_1[uint(0)].x = texsize.z;\\n  tmpvar_1[uint(0)].y = 0.0;\\n  tmpvar_1[1u].x = 0.0;\\n  tmpvar_1[1u].y = texsize.w;\\n  ofs = (tmpvar_1 * 4.0);\\n  float delta_2;\\n  float ky_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (uv.x - 0.5);\\n  tmpvar_4.y = ((uv.y - 0.5) + q29);\\n  vec2 v_5;\\n  v_5.x = ofs[0].x;\\n  v_5.y = ofs[1].x;\\n  vec2 v_6;\\n  v_6.x = ofs[0].x;\\n  v_6.y = ofs[1].x;\\n  vec2 v_7;\\n  v_7.x = ofs[0].y;\\n  v_7.y = ofs[1].y;\\n  vec2 v_8;\\n  v_8.x = ofs[0].y;\\n  v_8.y = ofs[1].y;\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = (texture (sampler_main, (uv + v_5)) - texture (sampler_main, (uv - v_6))).x;\\n  tmpvar_9.y = (texture (sampler_main, (uv + v_7)) - texture (sampler_main, (uv - v_8))).x;\\n  xlat_mutablered = (0.5 * texture (sampler_main, (uv - (0.004 * tmpvar_9))).x);\\n  xlat_mutablered = (xlat_mutablered + (0.5 * texture (sampler_main, uv).x));\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4.x;\\n  tmpvar_10.y = (0.0078125 * (float(\\n    int((tmpvar_4.y * 128.0))\\n  ) + abs(\\n    (fract((tmpvar_4.y * 128.0)) - 0.5)\\n  )));\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (tmpvar_10 * 4.0);\\n  ky_3 = clamp ((-0.5 - tmpvar_11.y), 0.0, 1.0);\\n  xlat_mutablek1 = (abs((\\n    fract((tmpvar_11.x * 2.0))\\n   - 0.5)) * 8.0);\\n  xlat_mutablek1 = (xlat_mutablek1 - sign(xlat_mutablek1));\\n  delta_2 = (0.1 + (rand_preset * 0.05)).x;\\n  xlat_mutablek1 = (xlat_mutablek1 - ((\\n    (4.0 * sign(xlat_mutablek1))\\n   * ky_3) * ky_3));\\n  ky_3 = clamp ((ky_3 - delta_2), 0.0, 1.0);\\n  xlat_mutablek1 = (xlat_mutablek1 - ((\\n    (8.0 * sign(xlat_mutablek1))\\n   * ky_3) * ky_3));\\n  ky_3 = clamp ((ky_3 - delta_2), 0.0, 1.0);\\n  xlat_mutablek1 = (xlat_mutablek1 - ((\\n    (12.0 * sign(xlat_mutablek1))\\n   * ky_3) * ky_3));\\n  ky_3 = clamp ((ky_3 - delta_2), 0.0, 1.0);\\n  xlat_mutablek1 = (xlat_mutablek1 - ((\\n    (16.0 * sign(xlat_mutablek1))\\n   * ky_3) * ky_3));\\n  ky_3 = clamp ((ky_3 - delta_2), 0.0, 1.0);\\n  float tmpvar_12;\\n  tmpvar_12 = clamp (xlat_mutablek1, -1.6, 1.6);\\n  xlat_mutablek1 = tmpvar_12;\\n  xlat_mutablegreen = (cos((tmpvar_12 * 2.0)) - 0.1);\\n  xlat_mutablewave = ((sin(\\n    ((q1 * 8.0) - (uv.x * 6.0))\\n  ) / 2.0) * q3);\\n  xlat_mutablewave = ((xlat_mutablewave * float(\\n    (xlat_mutablewave >= 0.0)\\n  )) + (q30 * 0.02));\\n  xlat_mutablered = (xlat_mutablered + ((\\n    (xlat_mutablewave / abs(sin((\\n      (1.0/(tmpvar_12))\\n     + \\n      (q1 / 8.0)\\n    ))))\\n   * xlat_mutablegreen) * ky_3));\\n  xlat_mutablezv = ((time * 0.08) + (0.2 * sin(\\n    ((uv * 16.0) + time)\\n  ))).x;\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = q28;\\n  tmpvar_13.y = (0.2 * time);\\n  xlat_mutableuv2 = ((tmpvar_4 * 2.0) + tmpvar_13);\\n  vec3 tmpvar_14;\\n  tmpvar_14.xy = xlat_mutableuv2;\\n  tmpvar_14.z = xlat_mutablezv;\\n  vec3 tmpvar_15;\\n  tmpvar_15.xy = (xlat_mutableuv2 * vec2(2.0, 2.0));\\n  tmpvar_15.z = (xlat_mutablezv * 2.0);\\n  vec3 tmpvar_16;\\n  tmpvar_16.xy = (xlat_mutableuv2 * vec2(4.0, 4.0));\\n  tmpvar_16.z = (xlat_mutablezv * 3.0);\\n  vec3 tmpvar_17;\\n  tmpvar_17.xy = (xlat_mutableuv2 * vec2(8.0, 8.0));\\n  tmpvar_17.z = (xlat_mutablezv * 7.0);\\n  xlat_mutableuv2 = (tmpvar_4 + ((\\n    ((texture (sampler_noisevol_hq, tmpvar_14).z + (texture (sampler_noisevol_hq, tmpvar_15).z / 2.0)) + (texture (sampler_noisevol_hq, tmpvar_16).z / 4.0))\\n   + \\n    (texture (sampler_noisevol_hq, tmpvar_17).z / 8.0)\\n  ) * 0.1));\\n  vec3 tmpvar_18;\\n  tmpvar_18.x = xlat_mutablered;\\n  tmpvar_18.y = xlat_mutablegreen;\\n  tmpvar_18.z = ((1.0 + xlat_mutableuv2.y) * sqrt(dot (xlat_mutableuv2, xlat_mutableuv2)));\\n  vec4 tmpvar_19;\\n  tmpvar_19.w = 1.0;\\n  tmpvar_19.xyz = ((tmpvar_18 * 0.8) - 0.005);\\n  ret = tmpvar_19.xyz;\\n }\",comp:\"float trel;\\nfloat vshift;\\nvec3 xlat_mutableret1;\\nvec2 xlat_mutablers;\\nvec2 xlat_mutablers0;\\nfloat xlat_mutablesmoke;\\nfloat xlat_mutablesmoke2;\\nvec2 xlat_mutableuv_l;\\nfloat xlat_mutablew;\\nfloat xlat_mutablez;\\n shader_body { \\n  trel = (time / 2.0);\\n  vshift = (0.5 - q29);\\n  vec2 tmpvar_1;\\n  tmpvar_1.x = rand_preset.x;\\n  tmpvar_1.y = ((-0.1 - (rand_preset.y / 2.0)) + vshift);\\n  vec2 tmpvar_2;\\n  tmpvar_2.y = 0.0;\\n  tmpvar_2.x = (0.005 * time);\\n  xlat_mutableuv_l = (uv + tmpvar_2);\\n  vec2 tmpvar_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (uv - 0.5);\\n  tmpvar_3 = sin(((\\n    (tmpvar_4 * aspect.xy)\\n   + 0.5) - (tmpvar_1 * aspect.xy)));\\n  vec2 tmpvar_5;\\n  tmpvar_5 = fract(uv);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = vec3((texture (sampler_fc_main, tmpvar_5).x + (2.0 * (\\n    (texture (sampler_blur2, tmpvar_5).xyz * scale2)\\n   + bias2).x)));\\n  vec3 tmpvar_7;\\n  tmpvar_7.x = tmpvar_6.x;\\n  tmpvar_7.y = pow (tmpvar_6.x, 2.1);\\n  tmpvar_7.z = pow (tmpvar_6.x, 4.0);\\n  xlat_mutablew = ((uv.y - (\\n    ((dot (texture (sampler_noise_hq, vec2((xlat_mutableuv_l.x / 6.0))), vec4(0.32, 0.49, 0.29, 0.0)) + uv.x) - 0.5)\\n   / 4.0)) - vshift);\\n  xlat_mutablez = (0.2 / xlat_mutablew);\\n  float tmpvar_8;\\n  tmpvar_8 = clamp ((-0.2 - (\\n    (8.0 * q5)\\n   * xlat_mutablew)), 0.0, 1.0);\\n  xlat_mutablers0.x = (((uv.x - 0.5) * (1.0 + \\n    abs(xlat_mutablez)\\n  )) * 2.0);\\n  xlat_mutablers0.y = xlat_mutablez;\\n  xlat_mutablers0 = (xlat_mutablers0 * ((4.0 * \\n    float((xlat_mutablez <= 0.0))\\n  ) + 1.0));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = 0.0;\\n  tmpvar_9.y = (1.5 + xlat_mutablew);\\n  vec2 x_10;\\n  x_10 = (xlat_mutablers0 - tmpvar_9);\\n  float tmpvar_11;\\n  tmpvar_11 = ((q26 / sqrt(\\n    dot (x_10, x_10)\\n  )) / 4.0);\\n  xlat_mutablers0 = (xlat_mutablers0 * (1.0 + (tmpvar_11 * tmpvar_11)));\\n  xlat_mutablers.x = (xlat_mutablers0 * 2.0).x;\\n  xlat_mutablers.y = (xlat_mutablers0.y + (trel * 0.5));\\n  float tmpvar_12;\\n  tmpvar_12 = (texture (sampler_main, uv).y * tmpvar_8);\\n  vec3 tmpvar_13;\\n  tmpvar_13.xy = (xlat_mutablers * 2.0);\\n  tmpvar_13.z = (0.2 * trel);\\n  float tmpvar_14;\\n  tmpvar_14 = dot (((\\n    ((2.0 * clamp ((texture (sampler_noisevol_hq, \\n      (tmpvar_13 * float((xlat_mutablez >= 0.0)))\\n    ) - 0.2), 0.0, 1.0)).xyz * clamp ((texture (sampler_noise_hq, (xlat_mutablers / 16.0)) - 0.5), 0.0, 1.0).x)\\n   / \\n    sqrt(abs(xlat_mutablez))\\n  ) + tmpvar_11), vec3(0.32, 0.49, 0.29));\\n  vec3 tmpvar_15;\\n  tmpvar_15.x = tmpvar_14;\\n  tmpvar_15.y = pow (tmpvar_14, 2.1);\\n  tmpvar_15.z = pow (tmpvar_14, 6.0);\\n  xlat_mutableret1 = (((\\n    ((uv.y * (1.0 + q23)) + (0.1 / sqrt(dot (tmpvar_3, tmpvar_3))))\\n   * vec3(0.1, 0.1, 0.2)) * tmpvar_8) + (clamp (tmpvar_15, 0.0, 1.0) * (1.0 - \\n    clamp ((-1.0 - (12.0 * xlat_mutablew)), 0.0, 1.0)\\n  )));\\n  xlat_mutableret1 = (xlat_mutableret1 * clamp ((1.0 - \\n    (tmpvar_12 * 8.0)\\n  ), 0.0, 1.0));\\n  vec4 tmpvar_16;\\n  tmpvar_16 = texture (sampler_main, fract((tmpvar_4 + 0.5)));\\n  xlat_mutablesmoke = (tmpvar_16.z * (tmpvar_16.z * q5));\\n  xlat_mutableret1 = (xlat_mutableret1 * clamp ((1.0 - \\n    ((xlat_mutablesmoke * q32) * tmpvar_8)\\n  ), 0.0, 1.0));\\n  xlat_mutablesmoke2 = (texture (sampler_main, ((tmpvar_4 / 2.0) + 0.5)).z * q30);\\n  xlat_mutableret1 = (xlat_mutableret1 + (clamp (tmpvar_7, 0.0, 1.0) - (\\n    ((xlat_mutablez * clamp ((xlat_mutablesmoke + \\n      (xlat_mutablew * q31)\\n    ), 0.0, 1.0)) * vec3(1.0, 0.4, 0.1))\\n   * tmpvar_8)));\\n  float tmpvar_17;\\n  tmpvar_17 = clamp (((\\n    (tmpvar_12 * xlat_mutablez)\\n   * xlat_mutablez) / 16.0), 0.0, 1.0);\\n  vec3 tmpvar_18;\\n  tmpvar_18.x = tmpvar_17;\\n  tmpvar_18.y = pow (tmpvar_17, 2.1);\\n  tmpvar_18.z = pow (tmpvar_17, 6.0);\\n  xlat_mutableret1 = (xlat_mutableret1 + clamp (tmpvar_18, 0.0, 1.0));\\n  xlat_mutableret1 = (xlat_mutableret1 * clamp ((\\n    (((dot (texture (sampler_noise_mq, vec2(\\n      ((xlat_mutableuv_l.x / 4.0) + (0.02 * trel))\\n    )), vec4(0.32, 0.49, 0.29, 0.0)) / 6.0) - uv.y) + 0.8)\\n   * 32.0), 0.0, 1.0));\\n  vec3 tmpvar_19;\\n  tmpvar_19.x = xlat_mutablesmoke2;\\n  tmpvar_19.y = pow (xlat_mutablesmoke2, 2.1);\\n  tmpvar_19.z = pow (xlat_mutablesmoke2, 6.0);\\n  xlat_mutableret1 = ((xlat_mutableret1 * clamp (\\n    (1.0 - xlat_mutablesmoke2)\\n  , 0.0, 1.0)) + clamp (tmpvar_19, 0.0, 1.0));\\n  vec4 tmpvar_20;\\n  tmpvar_20.w = 1.0;\\n  tmpvar_20.xyz = (1.0 - exp((\\n    -(xlat_mutableret1)\\n   * 2.0)));\\n  ret = tmpvar_20.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:6,wave_thick:1,modwavealphabyvolume:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.527,wave_smoothing:.09,modwavealphastart:0,modwavealphaend:1.32,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.8,wave_g:.49,ob_a:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:1.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0,b2x:.7,b1ed:0},shapes:[{baseVals:{enabled:1,sides:12,num_inst:1024,rad:.03632,tex_ang:1.00531,tex_zoom:1.53117,b:1,a:0,g2:0,border_b:0,border_a:0},init_eqs_str:\"a.fov=0;a.n=0;a.x0=0;a.y0=0;a.z0=0;a.q32=0;a.t1=0;\",frame_eqs_str:\"a.fov=a.reg03;a.n=a.instance*a.reg00;a.x0=a.gmegabuf[Math.floor(a.n)];a.y0=a.gmegabuf[Math.floor(a.n+1)];a.z0=a.gmegabuf[Math.floor(a.n+2)]+a.reg02;a.x=div(a.x0,a.z0)*a.fov+.5;a.y=div(a.y0,a.z0)*a.q32*a.fov+.5;a.r=a.gmegabuf[Math.floor(a.n+3)];a.g=a.gmegabuf[Math.floor(a.n+4)];a.b=a.gmegabuf[Math.floor(a.n+5)];a.r2=div(a.r,2);a.g2=div(a.g,2);a.b2=div(a.b2,2);a.a=div(a.instance,1024);a.a2=.5*a.a;a.rad=Math.min(div(.02,a.z0),.5)*(0<a.z0?1:0)*2.5*sqrt(a.a);a.rad*=a.gmegabuf[Math.floor(a.n+\\n6)];--a.t1;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,scaling:2.15542,smoothing:.2,r:0,a:.7},init_eqs_str:\"a.fov=0;a.n=0;a.t1=0;a.x0=0;a.y0=0;a.z0=0;a.k1=0;a.t2=0;\",frame_eqs_str:\"a.t1=1023;a.t2=0;\",point_eqs_str:\"a.fov=a.reg03;a.n=a.t1*a.reg00;a.x0=a.gmegabuf[Math.floor(a.n)];a.y0=a.gmegabuf[Math.floor(a.n+1)];a.z0=a.gmegabuf[Math.floor(a.n+2)]+a.reg02;a.x=div(a.x0,a.z0)*a.fov+.5;a.y=div(a.y0,a.z0)*a.fov+.5;a.a=Math.max(0,div(a.t1,1024));a.k1=a.reg01+a.t1;a.r=a.gmegabuf[Math.floor(a.n+3)];a.g=a.gmegabuf[Math.floor(a.n+4)];a.b=a.gmegabuf[Math.floor(a.n+5)];a.a=div(div(a.t1,1024)*(.5<=a.z0?1:0),2);--a.t1;a.gmegabuf[Math.floor(1E4+a.t2)]=Math.abs(a.value1+a.value2);a.t2+=1;\"},{baseVals:{enabled:1,scaling:.89152,smoothing:.82,r:0,a:.1},init_eqs_str:\"a.fov=0;a.n=0;a.t1=0;a.x0=0;a.y0=0;a.z0=0;\",frame_eqs_str:\"a.t1=512;\",point_eqs_str:\"a.fov=a.reg03;a.n=a.t1*a.reg00;a.x0=a.gmegabuf[Math.floor(a.n)];a.y0=a.gmegabuf[Math.floor(a.n+1)];a.z0=a.gmegabuf[Math.floor(a.n+2)]+a.reg02;a.x=div(a.x0,a.z0)*a.fov+.5;a.y=div(a.y0,a.z0)*a.fov+.5;a.r=a.gmegabuf[Math.floor(a.n+3)];a.g=a.gmegabuf[Math.floor(a.n+4)];a.b=a.gmegabuf[Math.floor(a.n+5)];a.a=div(div(a.t1,1024)*(.5<=a.z0?1:0),2);--a.t1;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:'a.xang=0;a.n=0;a.fov=0;a.index2=0;a.ran9=0;a.ran5_=0;a.right=0;a.ran4=0;a.index=0;a.q12=0;a.yang=0;a.q6=0;a.y0=0;a.ran8=0;a.q1=0;a.r1=0;a.q5=0;a.ran1_=0;a.points=0;a.dt1=0;a.dec_f=0;a.x0=0;a[\"new\"]=0;a.dec_s=0;a.k3=0;a.reg01=0;a.my=0;a.oz=0;a.is_beat=0;a.k1=0;a.ran2=0;a.q11=0;a.z0=0;a.q10=0;a.ran4_=0;a.ran9_=0;a.ran3=0;a.ran5=0;a.dec_m=0;a.ran7=0;a.k2=0;a.mz=0;a.ran8_=0;a.ddy=0;a.ran6=0;a.dec2=0;a.ran6_=0;a.oy=0;a.rsum=0;a.mx=0;a.beat=0;a.vol=0;a.ran2_=0;a.peak=0;a.ddx=0;a.q2=\\n0;a.zang=0;a.trel1=0;a.ran7_=0;a.t0=0;a.dec=0;a.m=0;a.ran1=0;a.q32=0;a.ran3_=0;a.left=0;a.pk=0;a.recsize=0;a.ox=0;a.zofs=0;for(var b=a.index=0;1E5>b;b++)a.megabuf[Math.floor(a.index)]=0,a.gmegabuf[Math.floor(a.index)]=0,a.index+=1;a.recsize=8;a.reg00=a.recsize;a.points=1024;a.reg01=0;a.zofs=1+2*div(randint(100),100);a.reg02=a.zofs;a.fov=.3;a.reg03=a.fov;',frame_eqs_str:'a.dec_f=pow(.3,div(30,a.fps));a.dec_m=pow(.85,div(30,a.fps));a.dec_s=pow(.95,div(30,a.fps));a.beat=a.bass+a.mid+a.treb-(a.bass_att+a.mid_att+a.treb_att)+(a.bass+a.mid+a.treb);a.beat/=3;a.peak=a.peak*a.dec_m+(1-a.dec_m)*pow(a.beat-1,1)*(1<a.beat?1:0)*4;a.is_beat=above(a.beat,1)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),8);.00001<Math.abs(a.is_beat)?(a.ran1=div(randint(100),50)-\\n1,a.ran2=div(randint(100),50)-1,a.ran3=div(randint(100),50)-1):0;a.ran1_=a.dec_m*a.ran1_+(1-a.dec_m)*a.ran1;a.ran2_=a.dec_m*a.ran2_+(1-a.dec_m)*a.ran2;a.ran3_=a.dec_m*a.ran3_+(1-a.dec_m)*a.ran3;a.rsum=sqrt(a.ran1_*a.ran1_+a.ran2_*a.ran2_+a.ran3_*a.ran3_);.00001<Math.abs(a.is_beat*(.00001>Math.abs(a.index-2)?1:0))?(a.ran4=div(randint(100),50)-1,a.ran5=div(randint(100),50)-1,a.ran6=div(randint(100),50)-1):0;a.ran4_=a.dec_m*a.ran4_+(1-a.dec_m)*a.ran4;a.ran5_=a.dec_m*a.ran5_+(1-a.dec_m)*a.ran5;a.ran6_=\\na.dec_m*a.ran6_+(1-a.dec_m)*a.ran6;.00001<Math.abs(a.is_beat*(.00001>Math.abs(a.index-6)?1:0))?(a.ran7=div(randint(100),50)-1,a.ran8=div(randint(100),50)-1,a.ran9=div(randint(100),50)-1):0;a.ran7_=a.dec_m*a.ran7_+(1-a.dec_m)*a.ran7;a.ran8_=a.dec_m*a.ran8_+(1-a.dec_m)*a.ran8;a.ran9_=a.dec_m*a.ran9_+(1-a.dec_m)*a.ran9;a.pk=sqrt(a.peak+.1);a[\"new\"]=Math.floor(12*(a.ran4-a.ran5)*a.pk-div(12*(a.ran3-a.ran1),a.pk));a[\"new\"]=Math.max(Math.min(a[\"new\"],20),2);a.reg01+=a[\"new\"];a.dec=a.dec_m;a.n=a.recsize*\\na.points;a.m=0;a.dt1=div(div(.00001<Math.abs(0<a.ran1?1:0)?4*a.pk:div(4,a.pk),a.fps)*a[\"new\"],6);a.vol=Math.max(a.ran1+a.ran2,.2)*a.pk*2;for(var b=0;b<a[\"new\"];b++)a.trel1+=a.dt1,a.x0=a.x0*a.dec+(1-a.dec)*(Math.sin(a.trel1+6*a.ran3)*a.vol+a.ran1),a.y0=a.y0*a.dec+(1-a.dec)*(Math.sin(a.trel1+6*a.ran2)*a.vol+a.ran2),a.z0=a.z0*a.dec+(1-a.dec)*(Math.sin(a.trel1+6*a.ran1)*a.vol+a.ran3),a.gmegabuf[Math.floor(a.n)]=a.x0,a.gmegabuf[Math.floor(a.n+1)]=a.y0,a.gmegabuf[Math.floor(a.n+2)]=a.z0,a.gmegabuf[Math.floor(a.n+\\n3)]=div(div(a.ran1_,a.rsum),3)+.5,a.gmegabuf[Math.floor(a.n+4)]=div(div(a.ran2_,a.rsum),3)+.5,a.gmegabuf[Math.floor(a.n+5)]=div(div(a.ran3_,a.rsum),3)+.5,a.gmegabuf[Math.floor(a.n+6)]=0*a.gmegabuf[Math.floor(1E4+a.m)]+1,a.n+=a.recsize,a.m+=1;for(b=a.n=0;b<a.recsize*a.points;b++)a.gmegabuf[Math.floor(a.n)]=a.gmegabuf[Math.floor(a.n+a[\"new\"]*a.recsize)],a.n+=1;a.xang=div(a.ran4_,a.fps);a.yang=div(a.ran5_,a.fps);a.zang=div(a.ran6_,a.fps);a.ddx=Math.min(Math.max(a.ddx+div(a.yang,a.fps),-1),1);a.ddy=Math.min(Math.max(a.ddy+\\ndiv(a.xang,a.fps),-1),1);a.q1=2*a.ddx;a.q2=2*a.ddy;for(b=a.n=0;b<a.points;b++)a.ox=a.gmegabuf[Math.floor(a.n)],a.oy=a.gmegabuf[Math.floor(a.n+1)],a.oz=a.gmegabuf[Math.floor(a.n+2)],a.mx=a.ox*Math.cos(a.zang)-a.oy*Math.sin(a.zang),a.my=a.ox*Math.sin(a.zang)+a.oy*Math.cos(a.zang),a.ox=a.mx,a.oy=a.my,a.mx=a.ox*Math.cos(a.yang)+a.oz*Math.sin(a.yang),a.mz=-a.ox*Math.sin(a.yang)+a.oz*Math.cos(a.yang),a.ox=a.mx,a.oz=a.mz,a.my=a.oy*Math.cos(a.xang)-a.oz*Math.sin(a.xang),a.mz=a.oy*Math.sin(a.xang)+a.oz*Math.cos(a.xang),\\na.oy=a.my,a.oz=a.mz,a.gmegabuf[Math.floor(a.n)]=a.ox,a.gmegabuf[Math.floor(a.n+1)]=a.oy,a.gmegabuf[Math.floor(a.n+2)]=a.oz,a.n+=a.recsize;a.k1=div(div(a.ran7_,a.fps),2);a.k2=div(div(a.ran8_,a.fps),2);a.k3=div(Math.abs(a.ran9_),a.fps);a.dec=.4+.6*a.ran5_;a.dec2=1-a.dec;a.r1=.5+.3*a.ran4_;for(b=a.n=0;b<a.points-1;b++)a.m=a.n*a.recsize,a.left=mod(a.n-1+a.points,a.points)*a.recsize,a.right=mod(a.n+1+a.points,a.points)*a.recsize,a.gmegabuf[Math.floor(a.m)]=a.dec*a.gmegabuf[Math.floor(a.m)]+a.dec2*(a.gmegabuf[Math.floor(a.left)]*\\na.r1+a.gmegabuf[Math.floor(a.right)]*(1-a.r1)+a.k1),a.gmegabuf[Math.floor(a.m+1)]=a.dec*a.gmegabuf[Math.floor(a.m+1)]+a.dec2*(a.gmegabuf[Math.floor(a.left+1)]*a.r1+a.gmegabuf[Math.floor(a.right+1)]*(1-a.r1)+a.k2),a.n+=1;a.m=mod(a.frame,a.points);a.m=Math.max(0,Math.floor(1024+200*(a.t0-a.time)));a.n=a.m*a.recsize;a.q5=div(a.gmegabuf[Math.floor(a.n)],a.gmegabuf[Math.floor(a.n+2)]+a.zofs)*a.fov;a.q6=div(-a.gmegabuf[Math.floor(a.n+1)],a.gmegabuf[Math.floor(a.n+2)]+a.zofs)*a.fov;a.q10=sqrt(a.gmegabuf[Math.floor(a.n+\\n3)]);a.q11=sqrt(a.gmegabuf[Math.floor(a.n+4)]);a.q12=sqrt(a.gmegabuf[Math.floor(a.n+5)]);a.monitor=a.m;a.q32=a.aspecty;',pixel_eqs_str:\"a.rot=0;a.zoom=1;a.warp=0;a.dy=0;a.dx=-0;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = vec3(0.0, 0.0, 0.0);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\"float xlat_mutablesmask;\\nvec2 xlat_mutableuv1;\\nvec2 xlat_mutableuv2;\\nvec2 xlat_mutableuv3;\\nvec2 xlat_mutableuv4;\\n shader_body { \\n  float flash_1;\\n  float dist_2;\\n  float stars_3;\\n  vec3 ret_4;\\n  xlat_mutableuv1 = ((uv - 0.5) * aspect.xy);\\n  float tmpvar_5;\\n  tmpvar_5 = (0.1 * time);\\n  float tmpvar_6;\\n  tmpvar_6 = (0.2 * rad);\\n  dist_2 = ((1.0 - fract(\\n    (0.3333333 + tmpvar_5)\\n  )) * (1.0 - tmpvar_6));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = q1;\\n  tmpvar_7.y = q2;\\n  xlat_mutableuv3 = (xlat_mutableuv1 + tmpvar_7);\\n  xlat_mutableuv4 = ((32.0 * dist_2) * xlat_mutableuv3);\\n  xlat_mutablesmask = (texture (sampler_pw_noise_lq, (xlat_mutableuv4 / 256.0)).y - 0.9);\\n  vec2 tmpvar_8;\\n  tmpvar_8 = abs((fract(xlat_mutableuv4) - 0.5));\\n  stars_3 = max (0.0, clamp ((\\n    (1.0 - dist_2)\\n   * \\n    ((0.1 / sqrt(dot (tmpvar_8, tmpvar_8))) * xlat_mutablesmask)\\n  ), 0.0, 1.0));\\n  dist_2 = ((1.0 - fract(\\n    (0.6666667 + tmpvar_5)\\n  )) * (1.0 - tmpvar_6));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = q1;\\n  tmpvar_9.y = q2;\\n  xlat_mutableuv3 = (xlat_mutableuv1 + tmpvar_9);\\n  xlat_mutableuv4 = ((32.0 * dist_2) * xlat_mutableuv3);\\n  xlat_mutablesmask = (texture (sampler_pw_noise_lq, (xlat_mutableuv4 / 256.0)).y - 0.9);\\n  vec2 tmpvar_10;\\n  tmpvar_10 = abs((fract(xlat_mutableuv4) - 0.5));\\n  stars_3 = (stars_3 + max (stars_3, clamp (\\n    ((1.0 - dist_2) * ((0.1 / sqrt(\\n      dot (tmpvar_10, tmpvar_10)\\n    )) * xlat_mutablesmask))\\n  , 0.0, 1.0)));\\n  dist_2 = ((1.0 - fract(\\n    (1.0 + tmpvar_5)\\n  )) * (1.0 - tmpvar_6));\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = q1;\\n  tmpvar_11.y = q2;\\n  xlat_mutableuv3 = (xlat_mutableuv1 + tmpvar_11);\\n  xlat_mutableuv4 = ((32.0 * dist_2) * xlat_mutableuv3);\\n  xlat_mutablesmask = (texture (sampler_pw_noise_lq, (xlat_mutableuv4 / 256.0)).y - 0.9);\\n  vec2 tmpvar_12;\\n  tmpvar_12 = abs((fract(xlat_mutableuv4) - 0.5));\\n  stars_3 = (stars_3 + max (stars_3, clamp (\\n    ((1.0 - dist_2) * ((0.1 / sqrt(\\n      dot (tmpvar_12, tmpvar_12)\\n    )) * xlat_mutablesmask))\\n  , 0.0, 1.0)));\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = q5;\\n  tmpvar_13.y = q6;\\n  xlat_mutableuv2 = (xlat_mutableuv1 - tmpvar_13);\\n  float tmpvar_14;\\n  tmpvar_14 = ((0.01 / sqrt(\\n    dot (xlat_mutableuv2, xlat_mutableuv2)\\n  )) * min (3.0, (\\n    ((mid - 0.5) * float((mid > 0.5)))\\n   * 2.0)));\\n  flash_1 = tmpvar_14;\\n  float angle_15;\\n  float tmpvar_16;\\n  tmpvar_16 = abs(xlat_mutableuv2.x);\\n  if ((xlat_mutableuv2.y >= 0.0)) {\\n    angle_15 = (1.0 - ((xlat_mutableuv2.y - tmpvar_16) / (xlat_mutableuv2.y + tmpvar_16)));\\n  } else {\\n    angle_15 = (3.0 - ((xlat_mutableuv2.y + tmpvar_16) / (tmpvar_16 - xlat_mutableuv2.y)));\\n  };\\n  angle_15 = (angle_15 * 0.25);\\n  float tmpvar_17;\\n  if ((xlat_mutableuv2.x < 0.0)) {\\n    tmpvar_17 = -(angle_15);\\n  } else {\\n    tmpvar_17 = angle_15;\\n  };\\n  flash_1 = (tmpvar_14 * (tmpvar_14 / (\\n    abs((fract((\\n      (3.0 * tmpvar_17)\\n     + \\n      (time * 2.0)\\n    )) - 0.5))\\n   + 0.18)));\\n  vec3 tmpvar_18;\\n  tmpvar_18 = max ((texture (sampler_main, uv).xyz * 2.0), ((\\n    (texture (sampler_blur2, uv).xyz * scale2)\\n   + bias2) * 2.0));\\n  vec2 tmpvar_19;\\n  tmpvar_19 = sin(xlat_mutableuv3);\\n  ret_4 = (clamp ((0.025 / \\n    sqrt(dot (tmpvar_19, tmpvar_19))\\n  ), 0.0, 1.0) * vec3(0.4, 0.1, 1.0));\\n  ret_4 = (ret_4 + clamp ((stars_3 * stars_3), 0.0, 1.0));\\n  ret_4 = (ret_4 * clamp ((1.0 - \\n    (2.0 * dot (tmpvar_18, vec3(0.32, 0.49, 0.29)))\\n  ), 0.0, 1.0));\\n  ret_4 = (ret_4 + tmpvar_18);\\n  vec3 tmpvar_20;\\n  tmpvar_20.x = q10;\\n  tmpvar_20.y = q11;\\n  tmpvar_20.z = q12;\\n  ret_4 = (ret_4 + ((2.0 * \\n    clamp (flash_1, 0.0, 1.0)\\n  ) * tmpvar_20));\\n  vec4 tmpvar_21;\\n  tmpvar_21.w = 1.0;\\n  tmpvar_21.xyz = ret_4;\\n  ret = tmpvar_21.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,additivewave:1,wave_thick:1,modwavealphabyvolume:1,darken:1,wave_a:.001,wave_scale:.133,wave_smoothing:0,wave_mystery:-1,modwavealphastart:1,modwavealphaend:1.3,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.015,ob_b:1,ib_size:.26,mv_a:0,b2x:.3,b1ed:0},shapes:[{baseVals:{enabled:1,sides:40,thickoutline:1,rad:.06623,tex_zoom:1.79845,r:0,a:.1,g2:0,border_b:0,border_a:0},init_eqs_str:\"a.vol=0;a.bob=0;a.border_1=0;a.ro=0;a.sp=0;a.red=0;a.spi=0;a.tm=0;a.bob=1.5;a.ro=0;a.red=randint(20);\",frame_eqs_str:\"a.vol=1+.2*div(a.bass_att+a.treb_att+a.mid_att,3);a.bob=a.bob*above(a.bob,.01)-.01+(1-above(a.bob,.01));a.bob=.4+.4*Math.sin(.8*a.time);a.bob*=a.vol;a.border_1=.4;a.sides=30;a.ro+=.02;a.ang=a.ro;a.sp=.025*a.red;a.spi=.5-a.sp;a.tm=.1*a.time;a.border_r=.5+a.sp*Math.sin(.6*a.tm)+a.spi*Math.cos(1.46*a.tm);a.border_g=.5+a.sp*Math.sin(1.294*a.tm)+a.spi*Math.cos(.87*a.tm);a.border_b=.5+a.sp*Math.sin(1.418*a.tm)+a.spi*Math.cos(.76*a.tm);\"},{baseVals:{enabled:1,sides:40,additive:1,num_inst:4,g:1,b:1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.x=.5+.225*Math.sin(.7*div(a.time,a.instance));a.y=.5+.3*Math.cos(.7*div(a.time,a.instance));a.x-=.4*a.x*Math.sin(a.time);a.y-=.4*a.y*Math.cos(a.time);a.rad*=a.mid_att;a.r=.5+.5*Math.sin(.5*a.frame);a.b=.5+.5*Math.sin(.5*a.frame+2.094);a.g=.5+.5*Math.sin(.5*a.frame+4.188);\"},{baseVals:{enabled:1,sides:40,additive:1,g:1,b:1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.x=.5+.5*(.3*Math.sin(1.1*a.time)+.7*Math.sin(.5*a.time));a.x=.5+.225*Math.sin(a.time+2.09);a.y=.5+.3*Math.cos(a.time+2.09);a.rad*=a.bass_att;a.r=.5+.5*Math.sin(.5*a.frame);a.b=.5+.5*Math.sin(.5*a.frame+2.094);a.g=.5+.5*Math.sin(.5*a.frame+4.188);\"},{baseVals:{enabled:1,sides:40,additive:1,num_inst:5,rad:.07419,g:1,b:1,g2:0,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.x=.5+.225*Math.sin(div(a.time,a.instance));a.y=.5+.3*Math.cos(div(a.time,a.instance));a.x+=.4*a.x*Math.sin(a.time);a.y+=.4*a.y*Math.cos(a.time);a.rad*=a.treb_att;a.r=.5+.5*Math.sin(.5*a.frame);a.b=.5+.5*Math.sin(.5*a.frame+2.094);a.g=.5+.5*Math.sin(.5*a.frame+4.188);\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.index=0;a.q22=0;a.q21=0;a.q1=0;a.dec_med=0;a.ps=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.dec_slow=0;a.q4=0;a.q26=0;a.p2=0;a.avg=0;a.beat=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.q28=0;a.q20=0;\",frame_eqs_str:\"a.dec_med=pow(.7,div(30,a.fps));a.dec_slow=pow(.99,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.2+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.q20=a.avg;a.q21=a.beat;a.q22=a.peak;a.ps=.9*a.ps+.1*a.q22;a.q23=a.ps;a.q24=a.is_beat;\\na.q26=a.bass_att+a.mid_att+a.treb_att;a.q27=a.index+1;a.q28=a.index2;a.k1=a.is_beat*equal(mod(a.index,2),0);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,4);a.q1=Math.cos(a.rott);a.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;\",pixel_eqs_str:\"a.zoom=1.05;\",warp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (uv - vec2(0.5, 0.5));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 0.0;\\n  vec4 tmpvar_4;\\n  tmpvar_4 = texture (sampler_blur1, uv);\\n  tmpvar_3.xyz = ((tmpvar_4.xyz * scale1) + bias1);\\n  float tmpvar_5;\\n  tmpvar_5 = (dot (tmpvar_3, roam_sin) * 16.0);\\n  mat2 tmpvar_6;\\n  tmpvar_6[uint(0)].x = cos(tmpvar_5);\\n  tmpvar_6[uint(0)].y = -(sin(tmpvar_5));\\n  tmpvar_6[1u].x = sin(tmpvar_5);\\n  tmpvar_6[1u].y = cos(tmpvar_5);\\n  uv_1 = ((tmpvar_2 + (\\n    (0.2 * dot (((tmpvar_4.xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29)))\\n   * \\n    (tmpvar_2 * tmpvar_6)\\n  )) - 0.5);\\n  vec2 tmpvar_7;\\n  tmpvar_7 = ((uv_1 * texsize.xy) * 0.02);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = (cos((tmpvar_7.y * q1)) * sin(-(tmpvar_7.y)));\\n  tmpvar_8.y = (sin(tmpvar_7.x) * cos((tmpvar_7.y * q2)));\\n  uv_1 = (uv_1 - ((tmpvar_8 * texsize.zw) * 12.0));\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = ((texture (sampler_main, uv_1).xyz * 0.98) - 0.02);\\n  ret = tmpvar_9.xyz;\\n }\",comp:\"vec3 xlat_mutableret1;\\nvec2 xlat_mutablers;\\nvec2 xlat_mutableuv1;\\nfloat xlat_mutablez;\\n shader_body { \\n  xlat_mutableuv1 = (uv - 0.5);\\n  xlat_mutablez = (0.2 / abs(xlat_mutableuv1.y));\\n  xlat_mutablers.x = (xlat_mutableuv1.x * xlat_mutablez);\\n  xlat_mutablers.y = ((xlat_mutablez / 2.0) + (time * 4.0));\\n  vec4 tmpvar_1;\\n  tmpvar_1 = texture (sampler_noise_hq, xlat_mutablers);\\n  xlat_mutableret1 = ((tmpvar_1.xyz * vec3(\\n    greaterThanEqual (tmpvar_1.xyz, vec3(0.0, 0.0, 0.0))\\n  )) - 0.6);\\n  float tmpvar_2;\\n  tmpvar_2 = clamp ((128.0 * xlat_mutableuv1.y), 0.0, 1.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3 = fract(((\\n    (xlat_mutableuv1 * (1.0 - abs(xlat_mutableuv1.x)))\\n   - 0.5) - (\\n    (xlat_mutableret1 * 0.05)\\n   * tmpvar_2).xy));\\n  float x_4;\\n  x_4 = (tmpvar_3.y - 0.52);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (texture (sampler_main, tmpvar_3) + ((0.02 / \\n    (0.02 + sqrt((x_4 * x_4)))\\n  ) * slow_roam_sin)).xyz;\\n  xlat_mutableret1 = tmpvar_5;\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (32.0 * ((\\n    (uv * mat2(0.6, -0.8, 0.8, 0.6))\\n   + \\n    (tmpvar_5 * 0.1)\\n  .xy) + (time / 64.0)));\\n  vec2 tmpvar_7;\\n  tmpvar_7 = abs((fract(tmpvar_6) - 0.5));\\n  vec3 tmpvar_8;\\n  tmpvar_8 = clamp (((0.25 / \\n    sqrt(dot (tmpvar_7, tmpvar_7))\\n  ) * vec3((texture (sampler_pw_noise_lq, \\n    (tmpvar_6 / 256.0)\\n  ).y - 0.9))), 0.0, 1.0);\\n  vec4 tmpvar_9;\\n  tmpvar_9.w = 1.0;\\n  tmpvar_9.xyz = (tmpvar_5 + ((\\n    (tmpvar_8.x * tmpvar_8.x)\\n   + \\n    ((rand_preset * (0.5 - uv.y)).xyz * vec3(0.0, 0.0, 1.0))\\n  ) * (1.0 - tmpvar_2)));\\n  ret = tmpvar_9.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:2,gammaadj:1.98,decay:.5,echo_zoom:1,echo_alpha:.5,echo_orient:3,wave_mode:3,wave_thick:1,wrap:0,darken:1,wave_a:100,wave_scale:.282,wave_smoothing:.9,wave_mystery:1,warpanimspeed:1.459,warpscale:2.007,zoom:.9999,warp:.01,sx:.9999,wave_r:.5,wave_g:.5,wave_b:.5,ob_size:.05,ob_g:.1,ob_b:1,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_a:0,b1ed:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,spectrum:1,thick:1,scaling:.2248},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.r=a.bass;a.g=a.treb;a.b=.5;\",point_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.8);\"},{baseVals:{enabled:1,spectrum:1,thick:1,scaling:.2743},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;\",frame_eqs_str:\"a.r=a.bass;a.g=a.treb;a.b=.5;\",point_eqs_str:\"a.ma+=3.1415*above(a.mid,1)*.01*a.mid;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0004*Math.cos(a.ma);a.my+=.0004*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.a=above(a.bass+a.mid+a.treb,.3);\"},{baseVals:{enabled:1,thick:1,scaling:2.0231,smoothing:0},init_eqs_str:\"a.d=0;a.tt2=0;a.res=0;a.tt1=0;a.diff=0;a.tt3=0;a.beat=0;a.vol=0;a.m=0;a.monitor=0;a.t2=0;a.t3=0;a.t4=0;a.cl=0;\",frame_eqs_str:\"a.vol=8*a.bass+5*a.mid+3*a.treb;a.m=.97*a.m+.08*a.vol;a.monitor=a.vol;a.beat=above(a.vol,a.res)*above(a.vol,a.m)*above(a.vol,16);a.diff=(1-a.beat)*a.diff+a.beat*(a.vol-a.res);a.res=a.beat*(a.vol+.04*a.m)+(1-a.beat)*(a.res-div(60*(.1+.02*a.diff),a.fps));a.res=Math.max(0,a.res);\",point_eqs_str:\"a.tt3=.6*a.tt3+1*a.value1;a.tt2=.7*a.tt2+.2*a.tt3;a.tt1=.8*a.tt1+.1*a.tt2;a.d=.9*a.d+.2*a.tt1;a.y=.5+a.d*a.sample*(1-a.sample)*2;a.x=-.05+1.1*a.sample;\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.look=0;a.n=0;a.reg26=0;a.uvx0=0;a.reg34=0;a.reg28=0;a.reg23=0;a.q25=0;a.angchg=0;a.reg20=0;a.reg15=0;a.reg10=0;a.q12=0;a.v3=0;a.q22=0;a.q21=0;a.diry=0;a.q13=0;a.q6=0;a.posx=0;a.fps_=0;a.reg25=0;a.uvx=0;a.q1=0;a.travel=0;a.posz=0;a.q5=0;a.dirz=0;a.dec_s=0;a.reg16=0;a.slow=0;a.reg36=0;a.reg22=0;a.uvy=0;a.rotz=0;a.ly=0;a.dist_=0;a.q23=0;a.q24=0;a.reg24=0;a.cran0=0;a.ran2=0;a.q11=0;a.q10=0;a.reg14=0;a.posy=0;a.reg31=0;a.dirx=0;a.q4=0;a.start=0;a.reg12=0;a.reg13=0;a.c2=0;a.reg37=\\n0;a.s3=0;a.yslope=0;a.lampy=0;a.q16=0;a.xslope=0;a.q26=0;a.reg38=0;a.reg35=0;a.reg11=0;a.tx=0;a.avg=0;a.uvz=0;a.c3=0;a.uvy0=0;a.reg27=0;a.q19=0;a.beat=0;a.reg32=0;a.lx=0;a.reg21=0;a.uvz0=0;a.len=0;a.reg18=0;a.reg30=0;a.q2=0;a.q27=0;a.slen=0;a.q14=0;a.dist=0;a.reg17=0;a.v1=0;a.speed=0;a.s1=0;a.t0=0;a.s2=0;a.ran1=0;a.reg33=0;a.q7=0;a.ds=0;a.q28=0;a.lampx=0;a.ty=0;a.c1=0;a.v2=0;a.q20=0;a.q8=0;a.avg=.01;a.q7=.25;a.q8=randint(2)-1;a.q16=1+randint(2);a.q18=randint(.8)+.1;a.q30=1;a.q31=128;a.start=1;a.travel=\\n0;a.rotz=0;a.look=0;a.slow=0;a.t0=a.time+3;a.lampx=.5;a.lampy=.5;a.cran0=randint(1);for(var b=a.n=0;1E4>b;b++)a.gmegabuf[Math.floor(a.n)]=0,a.n+=1;for(b=a.n=0;1E4>b;b++)a.megabuf[Math.floor(a.n)]=0,a.n+=1;a.trelx=0;a.trely=0;a.trelz=0;a.reg20=1;a.reg21=0;a.reg22=0;a.reg23=0;a.reg24=1;a.reg25=0;a.reg26=0;a.reg27=0;a.reg28=1;b=0;do{b+=1;var c;a.ran1=div(randint(800),100);a.ran2=div(randint(800),100);a.ran3=div(randint(800),100);a.posx=randint(5)-2;a.posy=randint(5)-2;a.posz=randint(5)-2;a.c1=Math.cos(a.ran1);\\na.c2=Math.cos(a.ran2);a.c3=Math.cos(a.ran3);a.s1=Math.sin(a.ran1);a.s2=Math.sin(a.ran2);a.s3=Math.sin(a.ran3);a.reg20=a.c2*a.c1;a.reg21=a.c2*a.s1;a.reg22=-a.s2;a.reg23=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg24=a.s3*a.s2*a.s1+a.c3*a.c1;a.reg25=a.s3*a.c2;a.reg26=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg27=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg28=a.c3*a.c2;a.dist=.001;var d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;a.uvy+=a.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,\\n8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<Math.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=\\na.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.05;c=(.6>a.dist?1:0)*(30<a.len?1:0)}while(.00001<Math.abs(c)&&1048576>\\nd);d=.06>a.dist?1:0}while(.00001<Math.abs(d)&&1048576>b);\",frame_eqs_str:\"a.wave_a=0;a.fps_=0*a.fps_+1*(.00001<Math.abs(25>=a.fps?1:0)?a.fps:25+.5*(a.fps-25));a.dec_s=1-div(.06*30,a.fps_);a.beat=a.time>a.t0+3?1:0;a.t0=.00001<Math.abs(a.beat)?a.time:a.t0;a.speed=div(Math.min(.2,a.dist_-.02)*(1+2*a.avg)*(1-0*a.slow)*.7,a.q7);a.ds=a.ds*a.dec_s+div((1-a.dec_s)*a.speed*.25,a.fps_);a.rotz=.00001<Math.abs(.00001>Math.abs(a.rotz-0)?1:0)?a.beat*(randint(100)<20*a.travel?1:0)*(div(randint(10),10)-.3):bnot(a.beat*(30>randint(100)?1:0))*a.rotz;a.slow=.00001<\\nMath.abs(bnot(a.slow))?a.beat*(6>randint(1E3*a.avg)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.look=.00001<Math.abs(bnot(a.look))?a.beat*(12>randint(1E3*a.speed)?1:0):bnot(a.beat*(50>randint(100)?1:0));a.lx=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.lx;a.ly=.00001<Math.abs(a.beat)?div(randint(150),200)+.15:a.ly;a.lampx=a.lampx*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.lx:.5);a.lampy=a.lampy*a.dec_s+(1-a.dec_s)*(.00001<Math.abs(a.look)?a.ly:.5);a.q1=a.lampx;a.q2=a.lampy;a.dirx=a.reg26;\\na.diry=a.reg27;a.dirz=a.reg28;a.posx+=a.ds*a.dirx;a.posy+=a.ds*a.diry;a.posz+=a.ds*a.dirz;a.q4=a.posx;a.q5=a.posy;a.q6=a.posz;a.angchg=(.2-a.dist_)*(.2>a.dist_?1:0)*2;a.travel=.00001<Math.abs(0<a.angchg?1:0)?0:a.travel+a.ds;a.v1=a.v1*a.dec_s+(1-a.dec_s)*a.rotz*a.ds;a.v2=a.v2*a.dec_s+div((1-a.dec_s)*a.angchg*a.xslope,a.fps_);a.v3=a.v3*a.dec_s+(1-a.dec_s)*(div(a.angchg*a.yslope,a.fps_)+2*a.v1*Math.sin(.1*a.time));a.reg30=a.reg20;a.reg31=a.reg21;a.reg32=a.reg22;a.reg33=a.reg23;a.reg34=a.reg24;a.reg35=\\na.reg25;a.reg36=a.reg26;a.reg37=a.reg27;a.reg38=a.reg28;a.n=0;for(var b=a.avg=0;5>b;b++){a.n+=1;a.ran1=div(randint(100),100);a.ran2=div(randint(100),200)-.25;a.tx=Math.cos(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.ty=Math.sin(1.57*a.n+a.ran2)*(4>=a.n?1:0)*a.ran1;a.c1=Math.cos(a.v1);a.c2=Math.cos(a.v2+a.ty);a.c3=Math.cos(a.v3+a.tx);a.s1=Math.sin(a.v1);a.s2=Math.sin(a.v2+a.ty);a.s3=Math.sin(a.v3+a.tx);a.reg10=a.c2*a.c1;a.reg11=a.c2*a.s1;a.reg12=-a.s2;a.reg13=a.s3*a.s2*a.c1-a.c3*a.s1;a.reg14=a.s3*a.s2*\\na.s1+a.c3*a.c1;a.reg15=a.s3*a.c2;a.reg16=a.c3*a.s2*a.c1+a.s3*a.s1;a.reg17=a.c3*a.s2*a.s1-a.s3*a.c1;a.reg18=a.c3*a.c2;a.reg20=a.reg30;a.reg21=a.reg31;a.reg22=a.reg32;a.reg23=a.reg33;a.reg24=a.reg34;a.reg25=a.reg35;a.reg26=a.reg36;a.reg27=a.reg37;a.reg28=a.reg38;a.q20=a.reg10*a.reg20+a.reg11*a.reg23+a.reg12*a.reg26;a.q21=a.reg10*a.reg21+a.reg11*a.reg24+a.reg12*a.reg27;a.q22=a.reg10*a.reg22+a.reg11*a.reg25+a.reg12*a.reg28;a.q23=a.reg13*a.reg20+a.reg14*a.reg23+a.reg15*a.reg26;a.q24=a.reg13*a.reg21+a.reg14*\\na.reg24+a.reg15*a.reg27;a.q25=a.reg13*a.reg22+a.reg14*a.reg25+a.reg15*a.reg28;a.q26=a.reg16*a.reg20+a.reg17*a.reg23+a.reg18*a.reg26;a.q27=a.reg16*a.reg21+a.reg17*a.reg24+a.reg18*a.reg27;a.q28=a.reg16*a.reg22+a.reg17*a.reg25+a.reg18*a.reg28;a.reg20=a.q20;a.reg21=a.q21;a.reg22=a.q22;a.reg23=a.q23;a.reg24=a.q24;a.reg25=a.q25;a.reg26=a.q26;a.reg27=a.q27;a.reg28=a.q28;a.dist=.002;var c,d=0;do{d+=1;a.uvx=div(a.reg26*a.dist,a.q7);a.uvy=div(a.reg27*a.dist,a.q7);a.uvz=div(a.reg28*a.dist,a.q7);a.uvx+=a.posx;\\na.uvy+=a.posy;a.uvz+=a.posz;a.uvx=8*(div(a.uvx,8)+30.5-Math.floor(div(a.uvx,8)+30.5)-.5);a.uvy=8*(div(a.uvy,8)+30.5-Math.floor(div(a.uvy,8)+30.5)-.5);a.uvz=8*(div(a.uvz,8)+30.5-Math.floor(div(a.uvz,8)+30.5)-.5);a.uvx0=a.uvx+a.q8;a.uvy0=a.uvy+a.q8;a.uvz0=a.uvz+a.q8;for(c=0;8>c;c++)a.uvx=.00001<Math.abs(1<a.uvx?1:0)?2-a.uvx:.00001<Math.abs(-1>a.uvx?1:0)?-2-a.uvx:a.uvx,a.uvy=.00001<Math.abs(1<a.uvy?1:0)?2-a.uvy:.00001<Math.abs(-1>a.uvy?1:0)?-2-a.uvy:a.uvy,a.uvz=.00001<Math.abs(1<a.uvz?1:0)?2-a.uvz:.00001<\\nMath.abs(-1>a.uvz?1:0)?-2-a.uvz:a.uvz,a.slen=a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz,a.uvx=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvx:.00001<Math.abs(1>a.slen?1:0)?div(a.uvx,a.slen):a.uvx)+a.uvx0,a.uvy=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvy:.00001<Math.abs(1>a.slen?1:0)?div(a.uvy,a.slen):a.uvy)+a.uvy0,a.uvz=2.6*(.00001<Math.abs(.25>a.slen?1:0)?4*a.uvz:.00001<Math.abs(1>a.slen?1:0)?div(a.uvz,a.slen):a.uvz)+a.uvz0;a.len=sqrt(a.uvx*a.uvx+a.uvy*a.uvy+a.uvz*a.uvz);a.dist*=1.1;c=(.6>a.dist?1:0)*(30<\\na.len?1:0)}while(.00001<Math.abs(c)&&1048576>d);a.megabuf[Math.floor(a.n)]=a.megabuf[Math.floor(a.n)]*a.dec_s+(1-a.dec_s)*a.dist;a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5))}a.n=0;for(b=a.avg=0;5>b;b++)a.n+=1,a.avg+=Math.abs(div(a.megabuf[Math.floor(a.n)],5));a.xslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[1]-a.megabuf[3]),-3),3);a.yslope=Math.min(Math.max(div(2,a.avg)*(a.megabuf[4]-a.megabuf[2]),-3),3);a.monitor=a.avg;a.dist_=a.dist_*a.dec_s+(1-a.dec_s)*a.dist;a.q10=a.ds*a.q7;a.q14=Math.abs(a.ds)+\\n2*(Math.abs(a.v1)+Math.abs(a.v2)+Math.abs(a.v3))+div(1,255)+.05*a.start;a.q19=.6+.4*Math.sin(.02*a.time+6*a.cran0);a.start*=.9;a.q11=a.v1;a.q12=a.v2;a.q13=a.v3;a.monitor=a.q16;\",pixel_eqs_str:\"a.warp=0;a.zoom=1;a.dx=div(-a.q12,a.q16)*(1+0*pow(a.x-.5,2));a.dy=div(a.q13,a.q16)*(1+0*pow(a.y-.5,2));a.rot=a.q11;\",warp:\" shader_body { \\n  float dy_1;\\n  float dx_2;\\n  vec3 ret_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = ((uv * texsize.xy) * texsize_noise_lq.zw);\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (texsize.zw * 4.0);\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  tmpvar_6.y = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  )).y * 0.5);\\n  ret_3.y = texture (sampler_fw_main, clamp ((uv + (\\n    (tmpvar_6 * texsize.zw)\\n   * 4.0)), 0.0, 1.0)).y;\\n  ret_3.y = (ret_3.y + ((\\n    (ret_3 - ((texture (sampler_blur1, uv).xyz * scale1) + bias1))\\n  .y * 0.025) + -0.01));\\n  ret_3.y = (ret_3.y + ((texture (sampler_noise_lq, tmpvar_4).y - 0.5) * 0.02));\\n  dx_2 = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(1.0, 0.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  )).z * 0.5);\\n  dy_1 = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv - (vec2(0.0, 1.0) * tmpvar_5))).xyz * scale1) + bias1)\\n  )).z * 0.5);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = dx_2;\\n  tmpvar_7.y = dy_1;\\n  ret_3.z = ((texture (sampler_main, (uv - \\n    ((tmpvar_7 * texsize.zw) * 4.0)\\n  )).z - (ret_3.y * 0.01)) + 0.004);\\n  ret_3.z = (ret_3.z + ((texture (sampler_noise_lq, tmpvar_4).y - 0.5) * 0.01));\\n  dx_2 = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (tmpvar_5 * vec2(1.0, 0.0)))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv + (tmpvar_5 * vec2(-1.0, 0.0)))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  dy_1 = (((2.0 * \\n    ((texture (sampler_blur1, (uv + (tmpvar_5 * vec2(0.0, 1.0)))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv + (tmpvar_5 * vec2(0.0, -1.0)))).xyz * scale1) + bias1)\\n  )).x * 0.5);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = dx_2;\\n  tmpvar_8.y = dy_1;\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (tmpvar_8 * texsize.zw);\\n  vec2 domain_10;\\n  domain_10 = (uv - (tmpvar_9 * 2.5));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 0.0;\\n  tmpvar_11.xyz = max (vec4(0.0, 0.0, 0.0, 0.0), texture (sampler_fc_main, (domain_10 + (texsize.zw * vec2(-1.0, 0.0))))).xyz;\\n  vec4 tmpvar_12;\\n  tmpvar_12.w = 0.0;\\n  tmpvar_12.xyz = max (tmpvar_11, texture (sampler_fc_main, (domain_10 + (texsize.zw * vec2(0.0, -1.0))))).xyz;\\n  vec4 tmpvar_13;\\n  tmpvar_13.w = 0.0;\\n  tmpvar_13.xyz = max (tmpvar_12, texture (sampler_fc_main, domain_10)).xyz;\\n  vec4 tmpvar_14;\\n  tmpvar_14.w = 0.0;\\n  tmpvar_14.xyz = max (tmpvar_13, texture (sampler_fc_main, (domain_10 + (texsize.zw * vec2(0.0, 1.0))))).xyz;\\n  ret_3.x = ((max (tmpvar_14, texture (sampler_fc_main, \\n    (domain_10 + (texsize.zw * vec2(1.0, 0.0)))\\n  )).x + (\\n    (texture (sampler_main, (uv + (tmpvar_9 * 4.0))).x - ((texture (sampler_blur1, (uv + \\n      (tmpvar_9 * 4.0)\\n    )).xyz * scale1) + bias1).x)\\n   * 0.206)) - 0.09);\\n  vec4 tmpvar_15;\\n  tmpvar_15.w = 1.0;\\n  tmpvar_15.xyz = ret_3;\\n  ret = tmpvar_15.xyz;\\n }\",comp:\"float xlat_mutablelamp;\\nvec2 xlat_mutablers0;\\nvec2 xlat_mutablerss;\\nvec2 xlat_mutableuv1;\\n shader_body { \\n  vec3 tmpvar_1;\\n  tmpvar_1.x = q4;\\n  tmpvar_1.y = q5;\\n  tmpvar_1.z = q6;\\n  mat3 tmpvar_2;\\n  tmpvar_2[uint(0)].x = q20;\\n  tmpvar_2[uint(0)].y = q23;\\n  tmpvar_2[uint(0)].z = q26;\\n  tmpvar_2[1u].x = q21;\\n  tmpvar_2[1u].y = q24;\\n  tmpvar_2[1u].z = q27;\\n  tmpvar_2[2u].x = q22;\\n  tmpvar_2[2u].y = q25;\\n  tmpvar_2[2u].z = q28;\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = q1;\\n  tmpvar_3.y = q2;\\n  vec2 uv_4;\\n  vec3 dots_5;\\n  vec3 ret_6;\\n  vec2 tmpvar_7;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (uv - 0.5);\\n  tmpvar_7 = (0.5 + (tmpvar_8 * vec2(1.1, 0.81)));\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (uv - vec2(0.5, 0.5));\\n  uv_4 = (tmpvar_8 * aspect.xy);\\n  float tmpvar_10;\\n  float tmpvar_11;\\n  tmpvar_11 = (min (abs(\\n    (uv_4.y / uv_4.x)\\n  ), 1.0) / max (abs(\\n    (uv_4.y / uv_4.x)\\n  ), 1.0));\\n  float tmpvar_12;\\n  tmpvar_12 = (tmpvar_11 * tmpvar_11);\\n  tmpvar_12 = (((\\n    ((((\\n      ((((-0.01213232 * tmpvar_12) + 0.05368138) * tmpvar_12) - 0.1173503)\\n     * tmpvar_12) + 0.1938925) * tmpvar_12) - 0.3326756)\\n   * tmpvar_12) + 0.9999793) * tmpvar_11);\\n  tmpvar_12 = (tmpvar_12 + (float(\\n    (abs((uv_4.y / uv_4.x)) > 1.0)\\n  ) * (\\n    (tmpvar_12 * -2.0)\\n   + 1.570796)));\\n  tmpvar_10 = (tmpvar_12 * sign((uv_4.y / uv_4.x)));\\n  if ((abs(uv_4.x) > (1e-08 * abs(uv_4.y)))) {\\n    if ((uv_4.x < 0.0)) {\\n      if ((uv_4.y >= 0.0)) {\\n        tmpvar_10 += 3.141593;\\n      } else {\\n        tmpvar_10 = (tmpvar_10 - 3.141593);\\n      };\\n    };\\n  } else {\\n    tmpvar_10 = (sign(uv_4.y) * 1.570796);\\n  };\\n  xlat_mutablers0.x = ((tmpvar_10 / 3.1416) * 2.0);\\n  xlat_mutablers0.y = (0.02 / sqrt(dot (uv_4, uv_4)));\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = xlat_mutablers0.x;\\n  tmpvar_13.y = (xlat_mutablers0.y + time);\\n  xlat_mutablerss = (tmpvar_13 * mat2(0.7, -0.7, 0.7, 0.7));\\n  vec4 tmpvar_14;\\n  tmpvar_14 = vec4(greaterThanEqual ((texture (sampler_pw_noise_lq, \\n    (xlat_mutablerss / 32.0)\\n  ) - 0.7), vec4(0.0, 0.0, 0.0, 0.0)));\\n  vec2 tmpvar_15;\\n  tmpvar_15 = abs((fract(\\n    (xlat_mutablerss * 8.0)\\n  ) - 0.5));\\n  vec2 tmpvar_16;\\n  tmpvar_16.x = (xlat_mutablers0.x * 2.0);\\n  tmpvar_16.y = (xlat_mutablers0.y + (time / 2.0));\\n  xlat_mutablerss = (tmpvar_16 * mat2(0.7, -0.7, 0.7, 0.7));\\n  vec4 tmpvar_17;\\n  tmpvar_17 = vec4(greaterThanEqual ((texture (sampler_pw_noise_lq, \\n    (xlat_mutablerss / 32.0)\\n  ) - 0.7), vec4(0.0, 0.0, 0.0, 0.0)));\\n  vec2 tmpvar_18;\\n  tmpvar_18 = abs((fract(\\n    (xlat_mutablerss * 8.0)\\n  ) - 0.5));\\n  xlat_mutablerss = tmpvar_18;\\n  dots_5 = (vec3((clamp (\\n    (0.04 / sqrt(dot (tmpvar_15, tmpvar_15)))\\n  , 0.0, 1.0) * tmpvar_14.x)) + (clamp (\\n    (0.04 / sqrt(dot (tmpvar_18, tmpvar_18)))\\n  , 0.0, 1.0) * tmpvar_17.x));\\n  dots_5 = (dots_5 * clamp ((0.04 / \\n    abs((0.01 / xlat_mutablers0.y))\\n  ), 0.0, 1.0));\\n  dots_5 = (dots_5 * (dots_5 * 2.0));\\n  vec2 tmpvar_19;\\n  tmpvar_19.x = -((tmpvar_9.y * -1024.0));\\n  tmpvar_19.y = (tmpvar_9.x * -1024.0);\\n  vec2 tmpvar_20;\\n  tmpvar_20.x = tmpvar_19.x;\\n  tmpvar_20.y = -(tmpvar_19.y);\\n  uv_4 = (vec2(-100.0, 100.0) * (tmpvar_20 / (\\n    (tmpvar_19.x * tmpvar_19.x)\\n   + \\n    (tmpvar_19.y * tmpvar_19.y)\\n  )).yx);\\n  uv_4 = (0.5 + ((\\n    (1.0 - abs(((\\n      fract((mix ((0.5 + \\n        ((tmpvar_7 - 0.5) * 2.0)\\n      ), (uv_4 + 0.5), vec2(0.5, 0.5)) * 0.5))\\n     * 2.0) - 1.0)))\\n   - 0.5) * 0.98));\\n  uv_4 = (((uv_4 - 0.5) * 0.9) + 0.5);\\n  xlat_mutableuv1 = ((uv_4 - tmpvar_3) * aspect.xy);\\n  float tmpvar_21;\\n  tmpvar_21 = min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - (\\n    (texture (sampler_blur2, uv_4).xyz * scale2)\\n   + bias2).z));\\n  vec3 tmpvar_22;\\n  tmpvar_22.xy = ((uv_4 - 0.5) * min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - \\n    ((texture (sampler_blur2, uv_4).xyz * scale2) + bias2)\\n  .z)));\\n  tmpvar_22.z = min ((1.0 - texture (sampler_main, uv_4).z), (1.0 - (\\n    (texture (sampler_blur2, uv_4).xyz * scale2)\\n   + bias2).z));\\n  float tmpvar_23;\\n  tmpvar_23 = clamp ((abs(\\n    ((1.0 - ((texture (sampler_blur2, uv_4).xyz * scale2) + bias2).z) - clamp ((1.0 - (\\n      (texture (sampler_blur2, tmpvar_3).xyz * scale2)\\n     + bias2).z), 0.1, 0.4))\\n  ) + 0.2), 0.0, 1.0);\\n  vec3 tmpvar_24;\\n  tmpvar_24 = mix (texture (sampler_main, uv_4).xyz, ((texture (sampler_blur1, uv_4).xyz * scale1) + bias1), vec3(tmpvar_23));\\n  float tmpvar_25;\\n  tmpvar_25 = clamp (((1.0 - \\n    exp(-(((texture (sampler_blur1, uv_4).xyz * scale1) + bias1).x))\\n  ) - 0.2), 0.0, 1.0);\\n  ret_6 = ((tmpvar_24.x * (0.2 + \\n    ((1.0 - tmpvar_23) * (1.0 - tmpvar_21))\\n  )) * (1.0 + (0.5 * \\n    sin((((tmpvar_22 / q7) * tmpvar_2) + tmpvar_1))\\n  )));\\n  vec3 tmpvar_26;\\n  tmpvar_26.xy = vec2(0.0, 1.0);\\n  tmpvar_26.z = (tmpvar_25 * 3.0);\\n  vec3 tmpvar_27;\\n  tmpvar_27 = mix (ret_6, tmpvar_26, vec3(tmpvar_25));\\n  xlat_mutablelamp = (((\\n    clamp ((1.0 - (4.0 * sqrt(\\n      dot (xlat_mutableuv1, xlat_mutableuv1)\\n    ))), 0.0, 1.0)\\n   * tmpvar_24.x) * clamp (\\n    (1.0 - (2.0 * mix (tmpvar_21, (1.0 - \\n      ((texture (sampler_blur1, uv_4).xyz * scale1) + bias1)\\n    .z), 0.2)))\\n  , 0.0, 1.0)) * 1.8);\\n  ret_6 = (tmpvar_27 + ((1.0 - \\n    dot (tmpvar_27, vec3(0.32, 0.49, 0.29))\\n  ) * xlat_mutablelamp));\\n  ret_6 = (1.0 - exp((-2.0 * ret_6)));\\n  ret_6 = (ret_6 + (dots_5 * (1.0 + ret_6)));\\n  vec4 tmpvar_28;\\n  tmpvar_28.w = 1.0;\\n  tmpvar_28.xyz = ret_6;\\n  ret = tmpvar_28.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,wave_mode:4,additivewave:1,wave_dots:1,modwavealphabyvolume:1,wave_a:.331,wave_scale:.898,wave_smoothing:.108,wave_mystery:.1,modwavealphastart:.72,modwavealphaend:1.28,zoom:1.3345,wave_r:0,wave_g:.5,wave_b:.5,wave_y:.54,mv_x:24.8,mv_dy:.16,mv_l:1.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,samples:352,usedots:1,additive:1,scaling:.03856,smoothing:.2,g:0},init_eqs_str:\"a.t02=0;a.q1=0;a.ratio=0;a.ampl=0;a.x1=0;a.y1=0;\",frame_eqs_str:\"a.q1=a.bass_att;\",point_eqs_str:\"a.r=Math.abs(Math.sin(div(a.frame,38)));a.g=.5*Math.abs(Math.cos(div(a.frame,45)));a.b=.5*Math.abs(Math.sin(div(a.frame,133)));a.a=.3;a.t02+=div(a.q1,10);a.ratio=Math.sin(div(a.frame,49));a.ampl=.01+.4*sqr(Math.sin(div(a.frame,18))*Math.cos(div(a.frame,123)));a.x1=div(a.r-.5,15)+.5+a.ampl*Math.sin(6.28*a.sample);a.y1=div(a.b-.5,15)+.5+a.ampl*Math.cos(6.28*a.sample);a.x=a.x1+.2*(a.ampl+a.ratio)*Math.sin(6.28*a.sample*a.ratio*7.3);a.y=a.y1+.2*(a.ampl+a.ratio)*Math.cos(37.68*a.sample);\\n\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.oldshift=0;a.shift=0;a.normalframez=0;a.crash=0;a.nex=0;a.rshift=0;a.q1=0;a.zoom1=0;\",frame_eqs_str:\"a.dx=0;a.oldshift=a.shift;a.normalframez+=1;a.shift=above(a.bass_att,1)*above(a.treb_att,.9);a.crash=Math.abs(a.oldshift-a.shift);a.nex=1*equal(a.rshift,0)+2*equal(a.rshift,1);a.rshift=.00001<Math.abs(a.crash)?a.nex:a.rshift;a.monitor=a.rshift;a.wave_r=div(Math.floor(randint(200)),200);a.wave_g=div(Math.floor(randint(200)),200);a.wave_b=div(Math.floor(randint(200)),200);a.warp=0;a.q1=above(a.bass_att,1.3);a.zoom1=a.zoom+.15-.3*mod(a.normalframez,2);a.zoom=.00001<Math.abs(a.shift)?\\na.zoom1:1;a.rot=a.rot-.1+.1*a.rshift;\",pixel_eqs_str:\"a.dy=.007*-below(a.y,.4)+.007*above(a.y,.6);\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = ((texture (sampler_main, (uv_orig + \\n    ((uv - uv_orig) * dot (texture (sampler_main, uv).xyz, vec3(0.32, 0.49, 0.29)))\\n  )).xyz * 0.99) - 0.002);\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = (texture (sampler_main, uv).xyz * 2.0);\\n  ret_1 = (ret_1 * (1.0 - ret_1));\\n  ret_1 = (((1.0 - \\n    pow (clamp (ret_1, 0.0, 1.0), vec3(0.5, 0.5, 0.5))\\n  ) * 1.5) - 0.75);\\n  vec4 tmpvar_2;\\n  tmpvar_2.w = 1.0;\\n  tmpvar_2.xyz = ret_1;\\n  ret = tmpvar_2.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,decay:1,echo_zoom:1,echo_orient:3,wave_mode:1,wrap:0,darken_center:1,wave_a:.001,zoom:.97,rot:-6.27999,warp:52e-5,wave_r:0,wave_g:0,wave_b:0,ob_r:1,ob_g:1,ob_b:1,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,textured:1,rad:.49849,g:1,b:1,a:.7,r2:1,g2:0,b2:1,a2:.7,border_a:0},init_eqs_str:\"a.q2=0;a.tex_capture=0;a.q3=0;\",frame_eqs_str:\"a.r2=.5+.5*Math.sin(.35*a.q2);a.g2=.5+.5*Math.sin(.578*a.q2);a.b2=.5+.5*Math.sin(.689*a.q2);a.ang=a.q2;a.tex_capture=above(a.q3,2);\"},{baseVals:{enabled:1,sides:100,textured:1,rad:.74218,g:1,b:1,a:.6,r2:1,g2:0,b2:1,a2:.6,border_a:0},init_eqs_str:\"a.q2=0;a.tex_capture=0;a.q3=0;\",frame_eqs_str:\"a.r2=.5+.5*Math.sin(.45*a.q2);a.g2=.5+.5*Math.sin(.678*a.q2);a.b2=.5+.5*Math.sin(.689*a.q2);a.ang=1.05*-a.q2;a.tex_capture=above(a.q3,2);\"},{baseVals:{enabled:1,sides:100,textured:1,rad:1.00035,g:1,b:1,a:.5,r2:1,g2:0,b2:1,a2:.5,border_a:0},init_eqs_str:\"a.q2=0;a.tex_capture=0;a.q3=0;\",frame_eqs_str:\"a.r2=.5+.5*Math.sin(.45*a.q2);a.g2=.5+.5*Math.sin(.578*a.q2);a.b2=.5+.5*Math.sin(.789*a.q2);a.ang=.899*a.q2;a.tex_capture=above(a.q3,2);\"},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,sep:1,spectrum:1,thick:1,additive:1},init_eqs_str:\"a.xx=0;a.s=0;a.xs=0;a.ss=0;a.q1=0;a.xd=0;a.zd=0;a.v=0;a.yx=0;a.angle2=0;a.zs=0;a.angle=0;a.ys=0;a.xn=0;a.yd=0;a.angle3=0;a.yn=0;a.u=0;a.zx=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.u=3.14159*Math.cos(.1*a.q1);a.v=6.28318*Math.cos(.015*a.q1);a.s=314*a.sample;a.ss=6280*a.sample;a.xs=(.3+.1*Math.cos(a.s))*Math.cos(a.ss)*.2*Math.cos(a.v);a.ys=(.3+.1*Math.cos(a.s))*Math.sin(a.ss)*6*a.u;a.zs=.1*Math.sin(a.s)*Math.sin(a.v);a.angle=.1*a.q1;a.yx=a.ys*Math.cos(a.angle)-a.zs*Math.sin(a.angle);a.zx=a.ys*Math.sin(a.angle)+a.zs*Math.cos(a.angle);a.xx=a.xs;a.angle2=.11*a.q1;a.xd=a.xx*Math.cos(a.angle2)-a.zx*Math.sin(a.angle2);a.zd=a.xx*Math.sin(a.angle2)+a.zx*Math.cos(a.angle2);\\na.yd=a.yx;a.angle3=.15*a.q1;a.xn=a.xd*Math.cos(a.angle3)-a.yd*Math.sin(a.angle3);a.yn=a.xd*Math.sin(a.angle3)+a.yd*Math.cos(a.angle3);a.zd=a.zd;a.x=a.xn*a.zd*.3+.5;a.y=a.yn*a.zd*.36+.5;a.r=.5+.5*Math.sin(1.2*a.q1+a.x+a.x);a.g=.5+.5*Math.sin(1.5*a.q1+a.x+a.y);a.b=.5+.5*Math.sin(1.36*a.q1+a.y+a.y);\"},{baseVals:{enabled:1,sep:1,spectrum:1,thick:1,additive:1},init_eqs_str:\"a.xx=0;a.s=0;a.xs=0;a.ss=0;a.q1=0;a.xd=0;a.zd=0;a.v=0;a.yx=0;a.angle2=0;a.zs=0;a.angle=0;a.ys=0;a.xn=0;a.yd=0;a.angle3=0;a.yn=0;a.u=0;a.zx=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.u=3.14159*Math.cos(.1*a.q1);a.v=6.28318*Math.cos(.015*a.q1);a.s=314*a.sample;a.ss=6280*a.sample;a.xs=(.3+.1*Math.cos(a.s))*Math.cos(a.ss)*.2*Math.cos(a.v);a.ys=(.3+.1*Math.cos(a.s))*Math.sin(a.ss)*6*a.u;a.zs=.1*Math.sin(a.s)*Math.sin(a.v);a.angle=.1*a.q1;a.yx=a.ys*Math.cos(a.angle)-a.zs*Math.sin(a.angle);a.zx=a.ys*Math.sin(a.angle)+a.zs*Math.cos(a.angle);a.xx=a.xs;a.angle2=.13*a.q1;a.xd=a.xx*Math.cos(a.angle2)-a.zx*Math.sin(a.angle2);a.zd=a.xx*Math.sin(a.angle2)+a.zx*Math.cos(a.angle2);\\na.yd=a.yx;a.angle3=.16*a.q1;a.xn=a.xd*Math.cos(a.angle3)-a.yd*Math.sin(a.angle3);a.yn=a.xd*Math.sin(a.angle3)+a.yd*Math.cos(a.angle3);a.zd=a.zd;a.x=a.xn*a.zd*.3+.5;a.y=a.yn*a.zd*.36+.5;a.r=.5+.5*Math.sin(1.2*a.q1+a.x+a.x);a.g=.5+.5*Math.sin(1.5*a.q1+a.x+a.y);a.b=.5+.5*Math.sin(1.36*a.q1+a.y+a.y);\"},{baseVals:{enabled:1,sep:1,spectrum:1,thick:1,additive:1},init_eqs_str:\"a.xx=0;a.s=0;a.xs=0;a.ss=0;a.q1=0;a.xd=0;a.zd=0;a.v=0;a.yx=0;a.angle2=0;a.zs=0;a.angle=0;a.ys=0;a.xn=0;a.yd=0;a.angle3=0;a.yn=0;a.u=0;a.zx=0;\",frame_eqs_str:\"\",point_eqs_str:\"a.u=3.14159*Math.cos(.1*a.q1);a.v=6.28318*Math.cos(.015*a.q1);a.s=314*a.sample;a.ss=6280*a.sample;a.xs=(.3+.1*Math.cos(a.s))*Math.cos(a.ss)*.2*Math.cos(a.v);a.ys=(.3+.1*Math.cos(a.s))*Math.sin(a.ss)*6*a.u;a.zs=.1*Math.sin(a.s)*Math.sin(a.v);a.angle=.1*a.q1;a.yx=a.ys*Math.cos(a.angle)-a.zs*Math.sin(a.angle);a.zx=a.ys*Math.sin(a.angle)+a.zs*Math.cos(a.angle);a.xx=a.xs;a.angle2=.16*a.q1;a.xd=a.xx*Math.cos(a.angle2)-a.zx*Math.sin(a.angle2);a.zd=a.xx*Math.sin(a.angle2)+a.zx*Math.cos(a.angle2);\\na.yd=a.yx;a.angle3=.16*a.q1;a.xn=a.xd*Math.cos(a.angle3)-a.yd*Math.sin(a.angle3);a.yn=a.xd*Math.sin(a.angle3)+a.yd*Math.cos(a.angle3);a.zd=a.zd;a.x=a.xn*a.zd*.3+.5;a.y=a.yn*a.zd*.36+.5;a.r=.5+.5*Math.sin(1.2*a.q1+a.x+a.x);a.g=.5+.5*Math.sin(1.5*a.q1+a.x+a.y);a.b=.5+.5*Math.sin(1.36*a.q1+a.y+a.y);\"},{baseVals:{enabled:0}}],init_eqs_str:\"a.basstime=0;a.stickybit=0;a.volavg2=0;a.q1=0;a.decay_r=0;a.sample1=0;a.difftime=0;a.diff=0;a.decay_b=0;a.edge=0;a.volavg=0;a.bit2=0;a.vol=0;a.q2=0;a.q3=0;a.basssum=0;a.decay_g=0;a.sample2=0;\",frame_eqs_str:\"a.basstime+=.03*a.bass;a.q1=4*a.basstime;a.basstime=.00001<Math.abs(below(a.basstime,1E3))?1E3:a.basstime;a.basstime+=.03*a.bass_att;a.vol=pow(a.bass+a.mid+a.treb,2);a.basssum=a.vol;a.stickybit=mod(a.time,2);a.volavg+=a.vol*equal(a.stickybit,1);a.sample1+=equal(a.stickybit,1);a.volavg2+=a.vol*equal(a.stickybit,0);a.sample2+=equal(a.stickybit,0);a.edge=bnot(equal(a.bit2,a.stickybit));a.volavg-=a.volavg*a.edge*a.stickybit;a.volavg2-=a.volavg2*a.edge*equal(a.stickybit,0);a.sample1-=\\na.sample1*a.edge*a.stickybit;a.sample2-=a.sample2*a.edge*equal(a.stickybit,0);a.diff=.00001<Math.abs(equal(a.stickybit,1))?div(a.basssum,div(a.volavg2,a.sample2)):0;a.diff=.00001<Math.abs(equal(a.stickybit,0))?div(a.basssum,div(a.volavg,a.sample1)):a.diff;a.q3=a.diff;a.bit2=mod(a.time,2);a.difftime+=.03*a.diff;a.q2=a.difftime;a.difftime=.00001<Math.abs(above(a.difftime,2E3))?0:a.difftime;a.monitor=3.14*Math.abs(Math.cos(a.time));a.mv_a=above(a.diff,10);\",pixel_eqs_str:\"a.zoom=1+.05*a.q3*a.rad;a.decay_r=.2*a.rad*Math.sin(.35*a.q2)+.85+.1*Math.sin(a.q2);a.decay_g=.2*a.rad*Math.sin(.5*a.q2)+.85+.1*Math.sin(.7*a.q2);a.decay_b=.2*a.rad*Math.sin(.4*a.q2)+.85+.1*Math.sin(.8*a.q2);a.rot=0;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_fc_main, uv).xyz * (0.8 + (q3 * 0.1)));\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = (texture (sampler_main, uv).xyz * 2.0);\\n  ret = tmpvar_1.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1,decay:1,echo_zoom:1.007,echo_alpha:.5,echo_orient:1,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_a:.1,wave_scale:3.815,wave_smoothing:.9,modwavealphastart:0,modwavealphaend:1.1,zoom:1.0099,warp:.04177,wave_r:.65,wave_g:.65,ob_size:.007,ib_size:.26,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0},shapes:[{baseVals:{enabled:1,sides:3,x:.37,rad:6.81129,ang:3.64425,r:0,a:.5,g2:0,a2:.5,border_a:1},init_eqs_str:\"a.q1=0;a.t1=0;a.t1=.01*randint(100);a.t2=.01*randint(100);\",frame_eqs_str:\"a.ang=a.q1*(.303+.01*a.t1);a.r=Math.min(1,Math.max(0,.1*Math.sin(.417*a.time+1)));a.g=Math.min(1,Math.max(0,.1*Math.sin(.391*a.time+2)));a.b=Math.min(1,Math.max(0,.1*Math.sin(.432*a.time+4)));a.r2=Math.min(1,Math.max(0,a.r2+.02*Math.sin(.657*a.time+3)));a.g2=Math.min(1,Math.max(0,a.g2+.02*Math.sin(.737*a.time+5)));a.b2=Math.min(1,Math.max(0,a.b2+.02*Math.sin(.884*a.time+6)));a.additive=.5+.15*(a.bass+a.bass_att);\"},{baseVals:{enabled:1,sides:100,additive:1,textured:1,x:.37,rad:.89796,ang:3.64425,g:1,b:1,a:.5,g2:0,a2:1,border_a:0},init_eqs_str:\"a.q1=0;a.t1=0;a.t1=.01*randint(100);a.t2=.01*randint(100);\",frame_eqs_str:\"a.x+=.07*Math.sin(.15*a.q1+3);a.y+=.03*Math.sin(.19*a.q1+1);a.tex_ang=a.q1*(.01+.0001*a.t1);a.r=Math.min(1,Math.max(0,a.r+.01*Math.sin(.0417*a.time+1)));a.g=Math.min(1,Math.max(0,a.g+.01*Math.sin(.391*a.time+2)));a.b=Math.min(1,Math.max(0,a.b+.01*Math.sin(.432*a.time+4)));a.r2=Math.min(1,Math.max(0,a.r2+.01*Math.sin(.457*a.time+3)));a.g2=Math.min(1,Math.max(0,a.g2+.01*Math.sin(.0437*a.time+5)));a.b2=Math.min(1,Math.max(0,a.b2+.01*Math.sin(.484*a.time+6)));\"},{baseVals:{enabled:1,sides:100,additive:1,textured:1,x:.67,y:.43,rad:.51386,ang:4.20974,g:1,b:1,g2:0,a2:1,border_a:0},init_eqs_str:\"a.q1=0;a.t1=0;a.t2=0;a.t1=.01*randint(100);a.t2=.01*randint(100);\",frame_eqs_str:\"a.x+=.05*Math.sin(.017*a.q1);a.y+=.09*Math.sin(.013*a.q1);a.tex_ang=a.q1*(.02+.0001*a.t1);a.rad*=.9+.2*a.t2;a.r=Math.min(1,Math.max(0,a.r+.01*Math.sin(.417*a.time+1)));a.g=Math.min(1,Math.max(0,a.g+.01*Math.sin(.391*a.time+2)));a.b=Math.min(1,Math.max(0,a.b+.01*Math.sin(.432*a.time+4)));a.r2=Math.min(1,Math.max(0,a.r2+.01*Math.sin(.457*a.time+3)));a.g2=Math.min(1,Math.max(0,a.g2+.01*Math.sin(.437*a.time+5)));a.b2=Math.min(1,Math.max(0,a.b2+.01*Math.sin(.484*a.time+6)));\"},{baseVals:{enabled:1,sides:100,additive:1,textured:1,rad:.22298,r:.6,g:.8,b:1,g2:0,a2:1,border_a:0},init_eqs_str:\"a.q1=0;\",frame_eqs_str:\"a.x+=.08*Math.sin(.25*a.q1);a.y+=.1*Math.sin(.5*a.q1+2);a.ang=a.time;\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.movement=0;a.q1=0;\",frame_eqs_str:\"a.movement=a.movement+.1*Math.max(0,a.bass+a.bass_att-2)+.15*pow(a.bass,3)+.005;a.q1=a.movement;a.monitor=a.q1;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv);\\n  ret_1 = (tmpvar_2.xyz + ((tmpvar_2.xyz - \\n    ((texture (sampler_blur1, uv).xyz * scale1) + bias1)\\n  ) * 0.5));\\n  ret_1 = (ret_1 * 0.9);\\n  ret_1 = (ret_1 + ((\\n    ((texture (sampler_noise_lq, ((\\n      (uv_orig * texsize.xy)\\n     * \\n      (texsize_noise_lq.zw * 0.4)\\n    ) + rand_frame.xy)).xyz - 0.5) / 256.0)\\n   * 12.0) * clamp (\\n    (treb_att - 1.0)\\n  , 0.0, 1.0)));\\n  vec3 tmpvar_3;\\n  tmpvar_3 = mix (ret_1, vec3(dot (ret_1, vec3(0.32, 0.49, 0.29))), vec3(0.08, 0.08, 0.08));\\n  ret_1 = tmpvar_3;\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = tmpvar_3;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 uv3_2;\\n  vec2 uv2_3;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * aspect.xy);\\n  uv2_3.x = ((uv_1.x * -0.497265) - (uv_1.y * 0.8675987));\\n  uv2_3.y = ((uv_1.x * 0.8675987) + (uv_1.y * -0.497265));\\n  uv3_2.x = ((uv_1.x * -0.5000263) - (uv_1.y * -0.8660102));\\n  uv3_2.y = ((uv_1.x * -0.8660102) + (uv_1.y * -0.5000263));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = max (max (texture (sampler_main, (uv_1 + 0.5)).xyz, texture (sampler_main, (uv2_3 + 0.5)).xyz), texture (sampler_main, (uv3_2 + 0.5)).xyz);\\n  ret = tmpvar_4.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.49,decay:.5,echo_zoom:1.002,echo_alpha:.5,echo_orient:1,wave_mode:7,additivewave:1,modwavealphabyvolume:1,wave_brighten:0,wave_a:.001,wave_scale:1.157,wave_smoothing:.63,modwavealphastart:.71,modwavealphaend:1.3,warpscale:16.016,zoomexp:11.56276,fshader:1,zoom:1.05971,warp:.13126,wave_r:.65,wave_g:.65,wave_b:.65,ob_size:0,ob_a:1,ib_size:0,mv_x:64,mv_y:48,mv_l:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:14,num_inst:512,rad:.1026,tex_ang:.62832,r2:1,g2:0,a2:1,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;a.r_border=0;a.g_border=0;a.b_border=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.01*a.bass;a.ma-=3.1415*above(a.treb,1)*.01*a.treb;a.mx+=.0002*Math.cos(a.ma);a.my+=.0002*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.rad=div(a.bass+a.treb,30);a.a=above(a.bass+a.mid+a.treb,.8);a.r=div(Math.floor(randint(100)),100);a.g=div(Math.floor(randint(100)),\\n100);a.b=div(Math.floor(randint(100)),100);a.r2=div(Math.floor(randint(100)),100);a.g2=div(Math.floor(randint(100)),100);a.b2=div(Math.floor(randint(100)),100);a.r_border=div(Math.floor(randint(100)),100);a.g_border=div(Math.floor(randint(100)),100);a.b_border=div(Math.floor(randint(100)),100);\"},{baseVals:{enabled:1,sides:23,num_inst:1024,rad:.10262,ang:.43982,tex_ang:.62832,a:.5,r2:1,g2:0,a2:.5,border_a:0},init_eqs_str:\"a.ma=0;a.mx=0;a.my=0;a.r_border=0;a.g_border=0;a.b_border=0;\",frame_eqs_str:\"a.ma+=3.1415*above(a.bass,1)*.05*a.bass;a.ma-=3.1415*above(a.mid,1)*.05*a.mid;a.mx+=.0001*Math.cos(a.ma);a.my+=.0001*Math.sin(a.ma);a.mx=.00001<Math.abs(above(a.mx,.9))?.9-a.mx:a.mx;a.my=.00001<Math.abs(above(a.my,.9))?.9-a.my:a.my;a.mx=.00001<Math.abs(below(a.mx,.1))?.9+a.mx:a.mx;a.my=.00001<Math.abs(below(a.my,.1))?.9+a.my:a.my;a.x=a.mx;a.y=a.my;a.rad=div(a.bass+a.treb,25);a.a=above(a.bass+a.mid+a.treb,.1);a.r=div(Math.floor(randint(100)),100);a.g=div(Math.floor(randint(100)),\\n100);a.b=div(Math.floor(randint(100)),100);a.r2=div(Math.floor(randint(100)),100);a.g2=div(Math.floor(randint(100)),100);a.b2=div(Math.floor(randint(100)),100);a.r_border=div(Math.floor(randint(100)),100);a.g_border=div(Math.floor(randint(100)),100);a.b_border=div(Math.floor(randint(100)),100);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.y3=0;a.y1=0;a.xx=0;a.q12=0;a.x1=0;a.vx3=0;a.q6=0;a.dt=0;a.q1=0;a.q5=0;a.q9=0;a.d1=0;a.si1=0;a.vx4=0;a.grav=0;a.x3=0;a.d2=0;a.q11=0;a.q10=0;a.xx2=0;a.q4=0;a.yy1=0;a.vy4=0;a.dir=0;a.bounce=0;a.x4=0;a.r=0;a.x2=0;a.vy2=0;a.y2=0;a.q2=0;a.m1=0;a.spring=0;a.si2=0;a.vx2=0;a.q3=0;a.resist=0;a.yy=0;a.y4=0;a.q7=0;a.vy3=0;a.xx1=0;a.b1=0;a.q8=0;\",frame_eqs_str:\"a.xx1=.9*a.xx1+.01*a.bass;a.xx2=.9*a.xx2+.01*a.treb;a.yy1=.94*a.yy1+.0075*(a.treb+a.bass);a.x1=.5+2*(a.xx1-a.xx2);a.y1=.4+a.yy1;a.x1=Math.max(0,Math.min(1,a.x1));a.y1=Math.max(0,Math.min(1,a.y1));a.spring=10;a.grav=.5;a.resist=1;a.bounce=.75;a.dt=.0005*div(60,a.fps);a.vx2=a.vx2*(1-a.resist*a.dt)+a.dt*(a.x1+a.x3-2*a.x2)*a.spring;a.vy2=a.vy2*(1-a.resist*a.dt)+a.dt*((a.y1+a.y3-2*a.y2)*a.spring-a.grav);a.vx3=a.vx3*(1-a.resist*a.dt)+a.dt*(a.x2+a.x4-2*a.x3)*a.spring;a.vy3=a.vy3*(1-\\na.resist*a.dt)+a.dt*((a.y2+a.y4-2*a.y3)*a.spring-a.grav);a.vx4=a.vx4*(1-a.resist*a.dt)+a.dt*(a.x3-a.x4)*a.spring;a.vy4=a.vy4*(1-a.resist*a.dt)+a.dt*((a.y3-a.y4)*a.spring-a.grav);a.x2+=a.vx2;a.y2+=a.vy2;a.x3+=a.vx3;a.y3+=a.vy3;a.x4+=a.vx4;a.y4+=a.vy4;a.vx2=.00001<Math.abs(above(a.x2,0))?a.vx2:Math.abs(a.vx2)*a.bounce;a.vx2=.00001<Math.abs(below(a.x2,1))?a.vx2:-Math.abs(a.vx2)*a.bounce;a.vx3=.00001<Math.abs(above(a.x3,0))?a.vx3:Math.abs(a.vx3)*a.bounce;a.vx3=.00001<Math.abs(below(a.x3,1))?a.vx3:-Math.abs(a.vx3)*\\na.bounce;a.vx4=.00001<Math.abs(above(a.x4,0))?a.vx4:Math.abs(a.vx4)*a.bounce;a.vx4=.00001<Math.abs(below(a.x4,1))?a.vx4:-Math.abs(a.vx4)*a.bounce;a.vy2=.00001<Math.abs(above(a.y2,0))?a.vy2:Math.abs(a.vy2)*a.bounce;a.vy2=.00001<Math.abs(below(a.y2,1))?a.vy2:-Math.abs(a.vy2)*a.bounce;a.vy3=.00001<Math.abs(above(a.y3,0))?a.vy3:Math.abs(a.vy3)*a.bounce;a.vy3=.00001<Math.abs(below(a.y3,1))?a.vy3:-Math.abs(a.vy3)*a.bounce;a.vy4=.00001<Math.abs(above(a.y4,0))?a.vy4:Math.abs(a.vy4)*a.bounce;a.vy4=.00001<\\nMath.abs(below(a.y4,1))?a.vy4:-Math.abs(a.vy4)*a.bounce;a.q1=a.x1;a.q2=a.x2;a.q3=a.x3;a.q4=a.x4;a.q5=a.y1;a.q6=a.y2;a.q7=a.y3;a.q8=a.y4;a.q9=div(1,a.aspectx);a.q10=div(1,a.aspecty);a.zoom=1;a.r=.96*a.r+(a.x1-.5);a.rot=.1*a.r;a.q12=Math.atan2(a.vx4,a.vy4);a.q11=sqrt(a.vx4*a.vx4+a.vy4*a.vy4);a.zoom=1.001;a.rot=0;a.warp=.2;a.wave_a=0;\",pixel_eqs_str:\"a.dir=-a.q12+Math.asin(1);a.b1=.1;a.m1=25*a.q11;a.xx=a.q4;a.yy=1-a.q8;a.x1=a.xx-Math.sin(a.dir)*a.b1;a.y1=a.yy-Math.cos(a.dir)*a.b1;a.x2=a.xx+Math.sin(a.dir)*a.b1;a.y2=a.yy+Math.cos(a.dir)*a.b1;a.d1=sqrt((a.x1-a.x)*(a.x1-a.x)+(a.y1-a.y)*(a.y1-a.y))-2*a.b1;a.d2=sqrt((a.x2-a.x)*(a.x2-a.x)+(a.y2-a.y)*(a.y2-a.y))-2*a.b1;a.si1=sigmoid(-a.d1,1E3);a.si2=sigmoid(-a.d2,1E3);a.dx=2*(a.si1*Math.sin(a.y1-a.y)*a.m1*a.d1-a.si2*Math.sin(a.y2-a.y)*a.m1*a.d2);a.dy=2*(-a.si1*Math.sin(a.x1-a.x)*\\na.m1*a.d1+a.si2*Math.sin(a.x2-a.x)*a.m1*a.d2);\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = mix (uv_orig, uv, vec2(0.3, 0.3));\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 3.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = dot (((\\n    (texture (sampler_blur1, (tmpvar_1 + (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (tmpvar_1 - (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  tmpvar_3.y = dot (((\\n    (texture (sampler_blur1, (tmpvar_1 + (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (tmpvar_1 - (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = (texture (sampler_fc_main, (tmpvar_1 + (\\n    (tmpvar_3 * texsize.zw)\\n   * 9.0))) - 0.01).xyz;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret3_2;\\n  vec3 ret_3;\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (((uv - 0.5) * vec2(-1.0, 1.0)) + 0.5);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = abs(((texture (sampler_main, uv).xyz * \\n    clamp ((((\\n      (texture (sampler_blur2, uv).xyz * scale2)\\n     + bias2) * 2.8) - 0.13), 0.0, 1.0)\\n  ) - (texture (sampler_main, tmpvar_4).xyz * \\n    clamp ((((\\n      (texture (sampler_blur2, tmpvar_4).xyz * scale2)\\n     + bias2) * 2.8) - 0.13), 0.0, 1.0)\\n  )));\\n  ret3_2 = (tmpvar_5 * sqrt(tmpvar_5));\\n  ret3_2 = (ret3_2 * vec3(0.9, 1.6, 2.3));\\n  ret3_2 = (ret3_2 * 3.0);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = pow (ret3_2, (1.0 - ret3_2));\\n  ret3_2 = tmpvar_6;\\n  uv_1 = (uv * 2.0);\\n  vec2 tmpvar_7;\\n  tmpvar_7 = floor((fract(\\n    (uv_1 * 0.5)\\n  ) * 2.0));\\n  uv_1 = ((fract(uv_1) * (1.0 - tmpvar_7)) + (tmpvar_7 * fract(\\n    (1.0 - uv_1)\\n  )));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = rad;\\n  tmpvar_8.y = uv_1.y;\\n  uv_1 = tmpvar_8;\\n  vec2 tmpvar_9;\\n  tmpvar_9 = ((0.5 - tmpvar_8.yx) + 0.5);\\n  ret_3 = (texture (sampler_main, tmpvar_8.yx).xyz * clamp ((\\n    (((texture (sampler_blur2, tmpvar_8.yx).xyz * scale2) + bias2) * 2.8)\\n   - 0.13), 0.0, 1.0));\\n  vec3 tmpvar_10;\\n  tmpvar_10 = mix (ret_3, (texture (sampler_main, tmpvar_9).xyz * clamp (\\n    ((((texture (sampler_blur2, tmpvar_9).xyz * scale2) + bias2) * 2.8) - 0.13)\\n  , 0.0, 1.0)), vec3(0.5, 0.5, 0.5));\\n  ret_3 = (tmpvar_10 * tmpvar_10);\\n  ret_3 = (ret_3 * vec3(0.9, 1.6, 2.3));\\n  ret_3 = (ret_3 * 3.0);\\n  ret_3 = (max (tmpvar_6, (0.8 * vec3(\\n    (0.5 * dot (ret_3, vec3(0.32, 0.49, 0.29)))\\n  ))) - (roam_sin.xyz * roam_cos.zxy));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = ret_3;\\n  ret = tmpvar_11.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:0,gammaadj:1.35,decay:1,echo_zoom:2.144269,echo_alpha:.31,echo_orient:3,wave_mode:7,wave_brighten:0,wrap:0,wave_a:4.1,wave_scale:5.552,wave_smoothing:.504,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.999514,cx:-1,warp:.72142,sy:.980296,wave_g:0,wave_b:0,ob_size:.5,ob_r:1,ib_size:.5,ib_r:1,ib_g:0,ib_b:0,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:1,sides:5,textured:1,rad:.98608,tex_ang:3.14159,tex_zoom:.999794,g:1,b:1,r2:1,b2:1,a2:1,border_a:0},init_eqs_str:\"\",frame_eqs_str:\"a.ang=Math.sin(div(a.time,65));\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.bl=0;a.tic=0;a.swi=0;a.q6=0;a.it=0;a.q1=0;a.q5=0;a.bvb=0;a.itar=0;a.mid_avg=0;a.tin=0;a.bass_avg=0;a.vav=0;a.rate=0;a.bd=0;a.q4=0;a.cha=0;a.ra=0;a.treb_avg=0;a.ul=0;a.db=0;a.gv=0;a.q2=0;a.iter=0;a.q3=0;a.vm=0;a.cma=0;a.cm=0;a.rb=0;\",frame_eqs_str:\"a.bl=3.8;a.ul=7.9;a.rate=11.9;a.cha=.01;a.gv=.00001<Math.abs(above(a.gv,a.bl))?.00001<Math.abs(below(a.gv,a.ul))?.00001<Math.abs(above(a.fps,a.rate))?a.gv+a.cha:a.gv-a.cha:a.ul-.1:a.bl+.1;a.monitor=a.gv;a.wave_a=0;a.tic=Math.min(a.time-a.tin,1);a.tin=a.time;a.ra=10;a.treb_avg=a.tic*(a.treb_avg*(div(1,a.tic)-a.ra)+a.ra*a.treb);a.mid_avg=a.tic*(a.mid_avg*(div(1,a.tic)-a.ra)+a.ra*a.mid);a.bass_avg=a.tic*(a.bass_avg*(div(1,a.tic)-a.ra)+a.ra*a.bass);a.rb=1;a.vav=a.tic*(a.vav*(div(1,\\na.tic)-a.rb)+a.rb*(a.bass+a.treb+a.mid)*.33333);a.q1=a.treb_avg;a.q2=a.mid_avg;a.q3=a.bass_avg;a.db=a.bass-a.bass_avg;a.it=(a.it+a.tic)*below(a.it,1);a.rb=.5*div(1,a.tic);a.bvb=a.tic*(a.bass*a.rb+(div(1,a.tic)-a.rb)*a.bvb);a.bd=a.bass-a.bvb;a.vm=a.vm-a.tic+a.swi;a.swi=above(a.bd-a.vm,0);a.q4=1-a.swi;a.cm=.00001<Math.abs(above(a.iter,30)+equal(a.time,0))?randint(3)+1:a.cm;a.iter=(a.iter+a.tic)*(1-above(a.iter,30));a.q5=.00001<Math.abs(equal(a.cm,0))?3:a.cm;a.cma=.00001<Math.abs(above(a.itar,5)+equal(a.time,\\n0))?Math.floor(5*a.vav):a.cma;a.itar=(a.itar+a.tic)*(1-above(a.itar,5));a.q6=Math.floor(5*a.vav);a.decay=.97;a.zoom=1.01;a.sx=1;a.sy=1;\",pixel_eqs_str:\"a.warp=a.bass;\",warp:\" shader_body { \\n  vec3 ret_1;\\n  ret_1 = ((texture (sampler_main, uv_orig).xyz * vec3(0.97, 1.0, 0.97)) - vec3(0.03, 0.0, 0.03));\\n  vec4 tmpvar_2;\\n  tmpvar_2 = texture (sampler_blur1, uv_orig);\\n  ret_1.z = (ret_1.z + ((\\n    (tmpvar_2.xyz * scale1)\\n   + bias1).z * 0.45));\\n  ret_1.x = (ret_1.x + ((\\n    ((tmpvar_2.xyz * scale1) + bias1)\\n  .z * 0.05) + (\\n    ((texture (sampler_blur3, uv_orig).xyz * scale3) + bias3)\\n  .z * 0.05)));\\n  ret_1.z = (ret_1.z - ret_1.x);\\n  ret_1.y = clamp (texture (sampler_pw_main, uv_orig).y, 0.0, 1.0);\\n  ret_1 = (ret_1 * 0.98);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ret_1;\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret_1;\\n  vec3 tmpvar_2;\\n  tmpvar_2.z = 0.0;\\n  tmpvar_2.xy = texsize.zw;\\n  vec3 tmpvar_3;\\n  tmpvar_3 = (tmpvar_2 * 2.5);\\n  ret_1 = (texture (sampler_main, uv).xyz * 0.5);\\n  ret_1 = (ret_1 + (vec3(3.4, 2.38, 1.02) * (\\n    dot (((texture (sampler_blur1, (uv + tmpvar_3.xz)).xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29))\\n   - \\n    dot (((texture (sampler_blur1, (uv - tmpvar_3.xz)).xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29))\\n  )));\\n  ret_1 = (ret_1 + (vec3(0.68, 1.7, 2.38) * (\\n    dot (((texture (sampler_blur1, (uv + tmpvar_3.zy)).xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29))\\n   - \\n    dot (((texture (sampler_blur1, (uv - tmpvar_3.zy)).xyz * scale1) + bias1), vec3(0.32, 0.49, 0.29))\\n  )));\\n  ret_1 = (ret_1 * 1.5);\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = ret_1;\\n  ret = tmpvar_4.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:2.05,decay:.99,echo_zoom:1.008,echo_alpha:.5,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.625,wave_scale:1.187,wave_smoothing:0,modwavealphastart:.71,modwavealphaend:1.3,warpanimspeed:1.459,warpscale:2.007,fshader:.19,zoom:.9999,warp:.01,sx:.9999,wave_r:.5,wave_g:.5,wave_b:.5,ob_r:1,ob_g:1,ob_b:1,ib_size:.26,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:.5},shapes:[{baseVals:{enabled:1,sides:9,thickoutline:1,textured:1,rad:.12566,ang:3.39292,tex_ang:.3768,tex_zoom:.32473,g:1,b:1,r2:1,b2:1,a2:1,border_r:.03,border_g:.67,border_b:.79,border_a:1},init_eqs_str:\"a.q1=0;a.q2=0;a.q3=0;\",frame_eqs_str:\"a.x+=.13*Math.sin(.9*a.time);a.y+=.16*Math.sin(.5*a.time);a.border_r=a.q1;a.border_b=a.q2;a.border_g=a.q3;\"},{baseVals:{enabled:1,sides:3,thickoutline:1,rad:.12572,a2:1,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.q1=0;a.q2=0;a.q3=0;\",frame_eqs_str:\"a.rad=a.rad-.2+.2*a.bass_att;a.r=a.r+.25*Math.sin(1.1*a.time)+.2*Math.sin(.25*a.time);a.g=a.g+.25*Math.cos(1.5*a.time)+.22*Math.cos(.24*a.time);a.b=a.b+.25*Math.sin(.6*a.time)+.27*Math.cos(.7*a.time);a.r2=1-Math.abs(a.r);a.g2=1-Math.abs(a.g);a.b2=1-Math.abs(a.b);a.ang+=3*Math.abs(Math.tan(1*a.time));a.x=a.x+.14*Math.cos(a.time)+.2*Math.sin(.42*a.time);a.y=a.y+.16*Math.sin(1.2*a.time)+.15*Math.sin(.8*a.time)+.21*Math.cos(.45*a.time);a.border_r=a.q1;a.border_b=a.q2;a.border_g=\\na.q3;\"},{baseVals:{enabled:1,ang:.03,r:.21,g:.44,b:.23,r2:.18,b2:.11,a2:1,border_r:.15,border_g:.4,border_b:.48,border_a:1},init_eqs_str:\"a.q1=0;a.q2=0;a.q3=0;\",frame_eqs_str:\"a.ang=2.4*a.time;a.x=.5+.22*Math.cos(3.3*a.time)+.14*Math.cos(1.2*a.time);a.y=.5+.26*Math.sin(3.1*a.time)+.13*Math.sin(1.7*a.time);a.r=.5+.5*Math.sin(1.013*a.time+5);a.g=.5+.5*Math.sin(1.063*a.time+2);a.b=.5+.5*Math.sin(1.054*a.time+1);a.r2=.5+.5*Math.sin(1.085*a.time+3);a.g2=.5+.5*Math.sin(1.056*a.time+1);a.b2=.5+.5*Math.sin(1.038*a.time+4);a.border_r=a.q1;a.border_b=a.q2;a.border_g=a.q3;\"},{baseVals:{enabled:1,sides:5,x:.51,y:.49,rad:.09986,ang:.06283,a2:1,border_r:.46,border_g:.28,border_b:.54,border_a:1},init_eqs_str:\"a.q4=0;a.q1=0;a.q2=0;a.q3=0;\",frame_eqs_str:\"a.x=.5+.07*Math.cos(.5*a.q4)+.31*Math.sin(3.5*a.time);a.y=.5+.07*Math.sin(.5*a.q4)+.31*Math.cos(3.5*a.time);a.r=.5+.5*Math.sin(1.013*a.time+2);a.g=.5+.5*Math.sin(.863*a.time+3);a.b=.5+.5*Math.sin(1.054*a.time+1);a.r2=.5+.5*Math.sin(1.185*a.time+3);a.g2=.5+.5*Math.sin(1.356*a.time+2);a.b2=.5+.5*Math.sin(.738*a.time+4);a.border_r=a.q1;a.border_b=a.q2;a.border_g=a.q3;\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.index2=0;a.xk=0;a.kiss=0;a.sw=0;a.yk=0;a.index=0;a.q22=0;a.q21=0;a.q29=0;a.q6=0;a.dt=0;a.y0=0;a.q1=0;a.dec_med=0;a.q5=0;a.index3=0;a.q9=0;a.x0=0;a.ax=0;a.rott=0;a.is_beat=0;a.q23=0;a.k1=0;a.q24=0;a.vx=0;a.dec_slow=0;a.q10=0;a.vy=0;a.q4=0;a.k2=0;a.q26=0;a.ay=0;a.p2=0;a.avg=0;a.beat=0;a.vol=0;a.p1=0;a.peak=0;a.q2=0;a.q27=0;a.q3=0;a.t0=0;a.m=0;a.q7=0;a.q28=0;a.v2=0;a.q20=0;a.q8=0;a.vol=0;a.p1=0;a.vx=.2;a.vy=-.1;a.kx=0;a.ky=0;\",frame_eqs_str:\"a.dec_med=pow(.96,div(30,a.fps));a.dec_slow=pow(.999,div(30,a.fps));a.beat=Math.max(Math.max(a.bass,a.mid),a.treb);a.avg=a.avg*a.dec_slow+a.beat*(1-a.dec_slow);a.is_beat=above(a.beat,.5+a.avg+a.peak)*above(a.time,a.t0+.2);a.t0=a.is_beat*a.time+(1-a.is_beat)*a.t0;a.peak=a.is_beat*a.beat+(1-a.is_beat)*a.peak*a.dec_med;a.index=mod(a.index+a.is_beat,8);a.index2=mod(a.index2+a.is_beat*bnot(a.index),2);a.index3=mod(a.index3+a.is_beat*bnot(a.index)*bnot(a.index2),2);a.q20=a.avg;a.q21=\\na.beat;a.q22=a.peak;a.q23=a.index;a.q24=a.is_beat;a.vol=a.bass_att+a.treb_att;a.v2=a.v2*a.dec_med+a.vol*(1-a.dec_med);a.q26=Math.max(Math.atan(a.vol-a.v2),0);a.q27=a.index+1;a.sw=a.sw*a.dec_med+(1-a.dec_med)*mod(a.index2,2);a.q28=a.sw;a.kiss=a.kiss*a.dec_med+(1-a.dec_med)*bnot(a.index2)*below(a.index,4);a.q29=a.kiss;a.k1=a.is_beat*bnot(a.index);a.k2=a.is_beat*bnot(a.index);a.p1=a.k1*(a.p1+1)+(1-a.k1)*a.p1;a.p2=a.dec_med*a.p2+(1-a.dec_med)*a.p1;a.rott=div(3.1416*a.p2,2);a.monitor=a.k1;a.q1=Math.cos(a.rott);\\na.q2=Math.sin(a.rott);a.q3=-a.q2;a.q4=a.q1;a.q5=Math.cos(div(a.time,4));a.q6=-Math.sin(div(a.time,4));a.q7=-a.q6;a.q8=a.q5;a.zoom=1;a.rot=-0;a.dt=div(3,a.fps);a.m=1;a.x0=0;a.y0=0;a.ax=div(-(a.xk-a.x0),a.m)+div(sign(a.vx+.001)*a.dt*a.bass,40);a.ay=div(-(a.yk-a.y0),a.m)+div(sign(a.vy+.001)*a.dt*a.treb,40);a.monitor=a.index3;a.vx=a.vx*a.dec_slow+a.ax*a.dt;a.vy=a.vy*a.dec_slow+a.ay*a.dt;a.xk+=a.vx*a.dt;a.yk+=a.vy*a.dt;a.q9=a.xk;a.q10=a.yk;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = ((uv * texsize.xy) * 0.08);\\n  vec3 tmpvar_2;\\n  tmpvar_2 = (texture (sampler_main, (uv - (\\n    ((sin(tmpvar_1) / cos(tmpvar_1)) * texsize.zw)\\n   * 3.0))).xyz + (vec3(dot (texture (sampler_noise_lq, \\n    ((((texsize.xy * texsize_noise_lq.zw).x * uv) * 0.02) + (0.1 * rand_frame).xy)\\n  ), vec4(0.32, 0.49, 0.29, 0.0))) / 30.0));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = ((mix (tmpvar_2, \\n    (1.0 - tmpvar_2.zyx)\\n  , vec3(0.01, 0.01, 0.01)) - 0.03) - (0.2 * pow (\\n    (1.0 - rad)\\n  , 18.0)));\\n  ret = tmpvar_3.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = cos(((uv.y * texsize.y) / 2.0));\\n  tmpvar_2.y = sin(((uv.x * texsize.x) / 2.0));\\n  uv_1 = (uv + ((2.0 * texsize.zw) * tmpvar_2));\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = (1.4 * pow ((\\n    clamp (((texture (sampler_blur1, uv_1).xyz * scale1) + bias1), 0.0, 1.0)\\n   * \\n    (texture (sampler_pc_main, uv_1).xyz - ((texture (sampler_blur3, uv_1).xyz * scale3) + bias3))\\n  ), vec3(0.5, 0.5, 0.5)));\\n  ret = tmpvar_3.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:4,gammaadj:1.98,echo_zoom:2.448,echo_alpha:.5,wave_thick:1,wrap:0,wave_a:3.645,wave_scale:1.951,wave_smoothing:.5,wave_mystery:-.5,warpscale:2.853,zoomexp:2.1,zoom:1.025,warp:1.29077,wave_g:0,wave_b:0,ob_size:0,ob_a:1,ib_size:0,ib_r:0,ib_g:0,ib_b:0,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:100,rad:.13518,g2:0,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.q8=0;\",frame_eqs_str:\"a.x=.3+.05*Math.sin(.89*a.q8);a.y=.4-.05*Math.cos(.77*a.q8);a.r=.25+.25*Math.sin(.7679*a.time);a.g=.25+.25*Math.sin(.8079*a.time);a.b=.25+.25*Math.sin(.7339*a.time);a.r2=.25+.25*Math.sin(.6979*a.time);a.g2=.25+.25*Math.sin(.849*a.time);a.b2=.25+.25*Math.sin(.8079*a.time);\"},{baseVals:{enabled:1,sides:100,rad:.06623,g2:0,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.q8=0;\",frame_eqs_str:\"a.x=.3-.05*Math.sin(.7089*a.q8);a.y=.4+.05*Math.cos(.5077*a.q8);a.r=.25+.25*Math.sin(.6479*a.time);a.g=.25+.25*Math.sin(.5079*a.time);a.b=.25+.25*Math.sin(.9339*a.time);a.r2=.25+.25*Math.sin(.779*a.time);a.g2=.25+.25*Math.sin(.707*a.time);a.b2=.25+.25*Math.sin(.747*a.time);\"},{baseVals:{enabled:1,sides:100,rad:.03646,g2:0,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.q8=0;\",frame_eqs_str:\"a.x=.3+.05*Math.sin(.679*a.q8);a.y=.4-.05*Math.cos(.877*a.q8);a.r=.25+.25*Math.sin(.5679*a.time);a.g=.25+.25*Math.sin(.4079*a.time);a.b=.25+.25*Math.sin(1.1339*a.time);a.r2=.25+.25*Math.sin(.9979*a.time);a.g2=.25+.25*Math.sin(.891*a.time);a.b2=.25+.25*Math.sin(.713*a.time);\"},{baseVals:{enabled:1,sides:100,rad:.0122,g2:0,a2:1,border_r:0,border_g:0,border_b:0,border_a:1},init_eqs_str:\"a.q8=0;\",frame_eqs_str:\"a.x=.3+.05*Math.sin(.916*a.q8);a.y=.4-.05*Math.cos(.977*a.q8);a.r=.25+.25*Math.sin(1.1679*a.time);a.g=.25+.25*Math.sin(1.18079*a.time);a.b=.25+.25*Math.sin(1.17339*a.time);a.r2=.25+.25*Math.sin(1.16979*a.time);a.g2=.25+.25*Math.sin(1.1849*a.time);a.b2=.25+.25*Math.sin(1.81079*a.time);\"}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.myrad=0;a.q25=0;a.q12=0;a.q18=0;a.q22=0;a.q21=0;a.vt=0;a.q13=0;a.q15=0;a.q29=0;a.q6=0;a.q1=0;a.myx=0;a.q5=0;a.vb=0;a.q9=0;a.myy=0;a.oldq8=0;a.q31=0;a.q23=0;a.q24=0;a.q11=0;a.q10=0;a.q4=0;a.q16=0;a.mybass=0;a.q26=0;a.rd=0;a.q19=0;a.q17=0;a.q2=0;a.q27=0;a.q14=0;a.q3=0;a.vvm=0;a.q32=0;a.vvb=0;a.q7=0;a.q28=0;a.vm=0;a.q30=0;a.q20=0;a.vvt=0;a.q8=0;\",frame_eqs_str:\"a.vb=.95*a.vb+(1-a.vb)*pow(a.bass_att,2)*.02;a.vvb=.95*a.vvb+(1-a.vvb)*a.vb*.01;a.vm=.95*a.vm+(1-a.vm)*pow(a.mid_att,2)*.02;a.vvm=.95*a.vvm+(1-a.vvm)*a.vm*.01;a.vt=.95*a.vt+(1-a.vt)*pow(a.treb_att,2)*.02;a.vvt=.95*a.vvt+(1-a.vvt)*a.vt*.01;a.vvb=Math.min(1,Math.max(0,a.vvb));a.vvm=Math.min(1,Math.max(0,a.vvm));a.vvt=Math.min(1,Math.max(0,a.vvt));a.q1=2*a.vvb;a.q2=2*a.vvm;a.q3=2*a.vvt;a.q4=5*(a.q1+a.q2+a.q3);a.q5=5*(a.q1+a.q2+a.q3);a.q6=5*(a.q1+a.q2+a.q3);a.q7=5*(a.q1+a.q2+a.q3);\\na.q8=5*(a.q1+a.q2+a.q3);a.q9=5*(a.q1+a.q2+a.q3);a.q10=5*(a.q1+a.q2+a.q3);a.q11=5*(a.q1+a.q2+a.q3);a.q12=5*(a.q1+a.q2+a.q3);a.q13=5*(a.q1+a.q2+a.q3);a.q14=5*(a.q1+a.q2+a.q3);a.q15=5*(a.q1+a.q2+a.q3);a.q16=5*(a.q1+a.q2+a.q3);a.q17=5*(a.q1+a.q2+a.q3);a.q18=5*(a.q1+a.q2+a.q3);a.q19=5*(a.q1+a.q2+a.q3);a.q20=5*(a.q1+a.q2+a.q3);a.q21=5*(a.q1+a.q2+a.q3);a.q22=5*(a.q1+a.q2+a.q3);a.q23=5*(a.q1+a.q2+a.q3);a.q24=5*(a.q1+a.q2+a.q3);a.q25=5*(a.q1+a.q2+a.q3);a.q26=5*(a.q1+a.q2+a.q3);a.q27=5*(a.q1+a.q2+a.q3);a.q28=\\n5*(a.q1+a.q2+a.q3);a.q29=5*(a.q1+a.q2+a.q3);a.q30=5*(a.q1+a.q2+a.q3);a.q31=5*(a.q1+a.q2+a.q3);a.q32=5*(a.q1+a.q2+a.q3);a.warp=0;a.rot=0;a.cx=.5;a.cy=.5;a.q1=.5+.1*Math.sin(a.time);a.q2=.5-.1*Math.cos(a.time);a.wave_a=0;a.decay=.95;a.q1=.5+.1*Math.sin(a.q8);a.q2=.5;a.q8=a.oldq8+.003*div(pow(1+1.2*a.bass+.4*a.bass_att+.1*a.treb+.1*a.treb_att+.1*a.mid+.1*a.mid_att,6),a.fps);a.oldq8=a.q8;a.mybass+=.01*(a.bass+a.bass_att);a.zoom=1.09;a.q1=.5+0*Math.sin(.12*a.q8);a.q2=.5-0*Math.cos(.177*a.q8);\",pixel_eqs_str:\"a.myx=1.1*(a.x-a.q1);a.myy=1.1*(a.y-a.q2);a.myrad=a.myx*a.myx+a.myy*a.myy;a.dx=(.5+.02*Math.sin(a.q8))*div(a.myy,a.myrad+1);a.dy=-(.5+.02*Math.sin(.897*a.q8))*div(a.myx,a.myrad+1);a.rd=a.bass*a.rad;a.rot=div(a.rd,10);a.sy=1.02+div(a.rad,10);a.sx=a.sy-a.myrad;\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = ((uv_orig - 0.5) * vec2(1.81, 1.81));\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = ((tmpvar_1.x * tmpvar_1.x) - (tmpvar_1.y * tmpvar_1.y));\\n  tmpvar_2.y = ((2.0 * tmpvar_1.x) * tmpvar_1.y);\\n  vec4 tmpvar_3;\\n  tmpvar_3.w = 1.0;\\n  tmpvar_3.xyz = (texture (sampler_fc_main, (tmpvar_2 + vec2(0.448, 0.701))) - 0.004).xyz;\\n  ret = tmpvar_3.xyz;\\n }\",comp:\"vec3 xlat_mutableneu;\\nvec3 xlat_mutableret1;\\n shader_body { \\n  vec2 uv_1;\\n  float inten_2;\\n  float dist_3;\\n  vec2 uv2_4;\\n  uv_1 = (uv - 0.5);\\n  uv_1 = (uv_1 * aspect.xy);\\n  float tmpvar_5;\\n  tmpvar_5 = (time / 2.0);\\n  dist_3 = (1.0 - fract(tmpvar_5));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 8.0);\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = q1;\\n  tmpvar_6.y = q2;\\n  vec2 tmpvar_7;\\n  tmpvar_7 = fract(((\\n    ((3.0 * uv_1) * dist_3)\\n   + 0.5) + (tmpvar_6 * 0.05)));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_7).xyz - ((texture (sampler_blur1, \\n    (tmpvar_7 + 0.003)\\n  ).xyz * scale1) + bias1));\\n  xlat_mutableret1 = max (vec3(0.0, 0.0, 0.0), (xlat_mutableneu * inten_2));\\n  uv2_4.x = ((uv_1.x * -0.4990803) - (uv_1.y * 0.8665558));\\n  uv2_4.y = ((uv_1.x * 0.8665558) + (uv_1.y * -0.4990803));\\n  dist_3 = (1.0 - fract((0.3333333 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 8.0);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = q1;\\n  tmpvar_8.y = q2;\\n  vec2 tmpvar_9;\\n  tmpvar_9 = fract(((\\n    ((3.0 * uv2_4) * dist_3)\\n   + 0.5) + (tmpvar_8 * 0.05)));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_9).xyz - ((texture (sampler_blur1, \\n    (tmpvar_9 + 0.003)\\n  ).xyz * scale1) + bias1));\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_2));\\n  uv2_4.x = ((uv_1.x * -0.5018377) - (uv_1.y * -0.8649619));\\n  uv2_4.y = ((uv_1.x * -0.8649619) + (uv_1.y * -0.5018377));\\n  dist_3 = (1.0 - fract((0.6666667 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 8.0);\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = q1;\\n  tmpvar_10.y = q2;\\n  vec2 tmpvar_11;\\n  tmpvar_11 = fract(((\\n    ((3.0 * uv2_4) * dist_3)\\n   + 0.5) + (tmpvar_10 * 0.05)));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_11).xyz - ((texture (sampler_blur1, \\n    (tmpvar_11 + 0.003)\\n  ).xyz * scale1) + bias1));\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_2));\\n  uv2_4.x = ((uv_1.x * 0.9999949) - (uv_1.y * -0.003185092));\\n  uv2_4.y = ((uv_1.x * -0.003185092) + (uv_1.y * 0.9999949));\\n  dist_3 = (1.0 - fract((1.0 + tmpvar_5)));\\n  inten_2 = ((sqrt(dist_3) * (1.0 - dist_3)) * 8.0);\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = q1;\\n  tmpvar_12.y = q2;\\n  vec2 tmpvar_13;\\n  tmpvar_13 = fract(((\\n    ((3.0 * uv2_4) * dist_3)\\n   + 0.5) + (tmpvar_12 * 0.05)));\\n  xlat_mutableneu = (texture (sampler_main, tmpvar_13).xyz - ((texture (sampler_blur1, \\n    (tmpvar_13 + 0.003)\\n  ).xyz * scale1) + bias1));\\n  xlat_mutableret1 = max (xlat_mutableret1, (xlat_mutableneu * inten_2));\\n  vec4 tmpvar_14;\\n  tmpvar_14.w = 1.0;\\n  tmpvar_14.xyz = (xlat_mutableret1 * 4.0);\\n  ret = tmpvar_14.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:5,gammaadj:1,decay:1,echo_zoom:1,echo_orient:1,wave_thick:1,wave_brighten:0,wrap:0,brighten:1,wave_a:.401,wave_scale:3.177,wave_smoothing:0,wave_mystery:-.4,modwavealphastart:1,modwavealphaend:1,warpanimspeed:2.289,warpscale:5.472,zoomexp:.01,zoom:.9901,warp:1.8566,ob_size:.005,ob_a:1,ib_size:0,ib_r:1,ib_g:0,ib_b:0,ib_a:1,mv_x:64,mv_y:48,mv_l:0,mv_b:0,mv_a:0},shapes:[{baseVals:{enabled:1,sides:12,additive:1,num_inst:512,y:.55,rad:.034,tex_zoom:.7874,g:.45,g2:0,border_a:0},init_eqs_str:\"a.my_z=0;a.d=0;a.y3=0;a.z2=0;a.y1=0;a.w=0;a.w2=0;a.t1=0;a.x1=0;a.rnd2=0;a.zoom=0;a.p=0;a.q1=0;a.q5=0;a.z3=0;a.w3=0;a.t3=0;a.my_x=0;a.x3=0;a.wv=0;a.my_y=0;a.q4=0;a.t=0;a.w1=0;a.i=0;a.x2=0;a.t2=0;a.l=0;a.y2=0;a.rnd4=0;a.wh=0;a.q2=0;a.z1=0;a.rnd3=0;a.rnd1=0;a.q3=0;a.t4=0;a.started=0;a.t1=.412;a.t2=.4563;a.t3=.6452;a.t4=.2565;\",frame_eqs_str:\"a.rnd1=.00001<Math.abs(equal(a.instance,0))?a.t1:a.rnd1;a.rnd2=.00001<Math.abs(equal(a.instance,0))?a.t2:a.rnd2;a.rnd3=.00001<Math.abs(equal(a.instance,0))?a.t3:a.rnd3;a.rnd4=.00001<Math.abs(equal(a.instance,0))?a.t4:a.rnd4;a.rnd1=4*a.rnd1*(1-a.rnd1);a.rnd2=4*a.rnd2*(1-a.rnd2);a.rnd3=4*a.rnd3*(1-a.rnd3);a.rnd4=4*a.rnd4*(1-a.rnd4);a.t=.6;a.t=a.rnd1+a.time*a.t-Math.floor(a.rnd1+a.time*a.t);a.t+=.1*a.rnd2;a.wh=a.rnd4*Math.asin(1)*4;a.wv=.25+.1*a.rnd3;a.d=1.4;a.zoom=1;a.l=1;a.w1=\\na.q3;a.w2=a.q4;a.w3=a.q5;a.i=a.instance;a.my_x=a.t*Math.cos(a.wh)*Math.sin(a.wv)*a.l;a.my_y=(-.5+(a.t-.75)*(a.t-.75))*Math.cos(a.wv)*a.l;a.my_z=a.t*Math.sin(a.wh)*Math.sin(a.wv)*a.l;a.x1=Math.cos(a.w1)*a.my_x+Math.sin(a.w1)*a.my_y;a.y1=-Math.sin(a.w1)*a.my_x+Math.cos(a.w1)*a.my_y;a.z1=a.my_z;a.x2=Math.cos(a.w2)*a.x1+Math.sin(a.w2)*a.z1;a.z2=-Math.sin(a.w2)*a.x1+Math.cos(a.w2)*a.z1;a.y2=a.y1;a.y3=Math.cos(a.w3)*a.y2+Math.sin(a.w3)*a.z2;a.z3=-Math.sin(a.w3)*a.y2+Math.cos(a.w3)*a.z2;a.x3=a.x2;a.l=sqrt(a.x3*\\na.x3+a.y3*a.y3);a.w=Math.atan2(a.x3,a.y3);a.p=Math.tan(Math.asin(1)+Math.atan2(a.d+a.z3,a.l));a.d=sqrt(a.x3*a.x3+a.y3*a.y3+(a.z3+a.d)*(a.z3+a.d));a.rad=div(a.rad,a.d);a.my_x=a.zoom*Math.sin(a.w)*a.p;a.my_y=a.zoom*Math.cos(a.w)*a.p;a.x=.5+a.my_x;a.y=.5+a.my_y;a.x=.5+div(a.x-.5,a.q2);a.y=.5+div(a.y-.5,a.q1);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q1=0;a.q2=0;a.b=0;a.m=0;a.t=0;a.q3=0;a.q4=0;a.q5=0;\",frame_eqs_str:\"a.wave_a=0;a.q1=a.aspectx;a.q2=a.aspecty;a.b+=a.bass*a.bass*.85;a.m+=a.mid*a.mid*.85;a.t+=a.treb*a.treb*.85;a.q3=.012*a.b;a.q4=.012*a.m;a.q5=.012*a.t;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec3 ret_2;\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (vec2(1280.0, 1024.0) * texsize.zw);\\n  float tmpvar_4;\\n  vec2 tmpvar_5;\\n  tmpvar_5 = (uv + vec2(0.005, 0.0));\\n  vec2 tmpvar_6;\\n  tmpvar_6 = (uv - vec2(0.005, 0.0));\\n  tmpvar_4 = (((\\n    (texture (sampler_blur2, tmpvar_5).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_6).xyz * scale2)\\n   + bias2)).x * tmpvar_3.x);\\n  float tmpvar_7;\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (uv + vec2(0.0, 0.005));\\n  vec2 tmpvar_9;\\n  tmpvar_9 = (uv - vec2(0.0, 0.005));\\n  tmpvar_7 = (((\\n    (texture (sampler_blur2, tmpvar_8).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_9).xyz * scale2)\\n   + bias2)).x * tmpvar_3.y);\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4;\\n  tmpvar_10.y = tmpvar_7;\\n  vec2 tmpvar_11;\\n  tmpvar_11.x = (((\\n    (texture (sampler_blur2, tmpvar_5).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_6).xyz * scale2)\\n   + bias2)).x * tmpvar_3.x);\\n  tmpvar_11.y = (((\\n    (texture (sampler_blur2, tmpvar_8).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, tmpvar_9).xyz * scale2)\\n   + bias2)).x * tmpvar_3.y);\\n  ret_2.x = texture (sampler_fw_main, ((uv - (tmpvar_10 * 0.01)) + (tmpvar_11 * 0.003))).x;\\n  vec4 tmpvar_12;\\n  tmpvar_12 = texture (sampler_blur3, uv);\\n  ret_2.x = (ret_2.x + ((ret_2.x - \\n    ((tmpvar_12.xyz * scale3) + bias3)\\n  .x) * 0.1));\\n  ret_2.x = (ret_2.x + 0.004);\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = tmpvar_7;\\n  tmpvar_13.y = -(tmpvar_4);\\n  my_uv_1 = (uv + ((tmpvar_13 * 0.05) * (1.2 - \\n    ((tmpvar_12.xyz * scale3) + bias3)\\n  .y)));\\n  ret_2.z = texture (sampler_fw_main, my_uv_1).z;\\n  vec2 x_14;\\n  x_14 = (my_uv_1 - uv);\\n  ret_2.z = (ret_2.z + ((\\n    ((ret_2.z - ((texture (sampler_blur1, uv).xyz * scale1) + bias1).z) * sqrt(dot (x_14, x_14)))\\n   * 180.0) / sqrt(\\n    dot (tmpvar_3, tmpvar_3)\\n  )));\\n  ret_2.z = (ret_2.z * 0.8);\\n  ret_2.z = (ret_2.z + 0.004);\\n  vec2 tmpvar_15;\\n  tmpvar_15.x = -(tmpvar_7);\\n  tmpvar_15.y = tmpvar_4;\\n  my_uv_1 = (tmpvar_15 * 0.045);\\n  vec2 tmpvar_16;\\n  tmpvar_16.x = (((\\n    (texture (sampler_blur2, (uv + vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (uv - vec2(0.01, 0.0))).xyz * scale2)\\n   + bias2)).y * tmpvar_3.x);\\n  tmpvar_16.y = (((\\n    (texture (sampler_blur2, (uv + vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2) - (\\n    (texture (sampler_blur2, (uv - vec2(0.0, 0.01))).xyz * scale2)\\n   + bias2)).y * tmpvar_3.y);\\n  my_uv_1 = (my_uv_1 + (uv - (tmpvar_16 * 0.03)));\\n  ret_2.y = texture (sampler_fw_main, my_uv_1).y;\\n  ret_2.y = (ret_2.y + ((\\n    (ret_2.y - ((texture (sampler_blur3, my_uv_1).xyz * scale3) + bias3).y)\\n   * 0.1) + 0.01));\\n  vec4 tmpvar_17;\\n  tmpvar_17.w = 1.0;\\n  tmpvar_17.xyz = ret_2;\\n  ret = tmpvar_17.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec3 tmpvar_2;\\n  tmpvar_2 = texture (sampler_main, uv).xyz;\\n  vec2 tmpvar_3;\\n  tmpvar_3.y = 0.0;\\n  tmpvar_3.x = texsize.z;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = 0.0;\\n  tmpvar_4.y = texsize.w;\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (texture (sampler_main, (uv - tmpvar_3)).xyz - texture (sampler_main, (uv + tmpvar_3)).xyz).x;\\n  tmpvar_5.y = (texture (sampler_main, (uv - tmpvar_4)).xyz - texture (sampler_main, (uv + tmpvar_4)).xyz).x;\\n  uv1_1 = ((0.3 * cos(\\n    (((uv - 0.5) * 2.0) + 1.7)\\n  )) - (2.0 * tmpvar_5));\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ((-(tmpvar_2) / 4.0) + ((6.0 * vec3(\\n    clamp ((0.03 / sqrt(dot (uv1_1, uv1_1))), 0.0, 1.0)\\n  )) * (-0.08 + tmpvar_2)));\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:2,decay:.99,wave_brighten:0,wave_a:1,wave_scale:.01,wave_mystery:-.2,warpanimspeed:3.434,warpscale:1.295,zoom:1.025,warp:.11284,wave_r:0,wave_g:0,wave_b:0,ob_size:.005,ob_b:1,ob_a:1,mv_x:64,mv_y:48,mv_l:5,mv_g:0,mv_b:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.d=0;a.cx1=0;a.y3=0;a.y1=0;a.cy1=0;a.q25=0;a.q12=0;a.q18=0;a.x1=0;a.q22=0;a.q21=0;a.vt=0;a.q13=0;a.q15=0;a.q29=0;a.q6=0;a.q1=0;a.q5=0;a.vb=0;a.q9=0;a.x3=0;a.q31=0;a.q23=0;a.q24=0;a.q11=0;a.q10=0;a.q4=0;a.dir=0;a.q16=0;a.q26=0;a.x2=0;a.q19=0;a.q17=0;a.y2=0;a.q2=0;a.q27=0;a.q14=0;a.q3=0;a.vvm=0;a.q32=0;a.vvb=0;a.q7=0;a.q28=0;a.vm=0;a.q30=0;a.q20=0;a.vvt=0;a.q8=0;\",frame_eqs_str:\"a.vb=.95*a.vb+(1-a.vb)*pow(a.bass_att,2)*.02;a.vvb=.95*a.vvb+(1-a.vvb)*a.vb*.01;a.vm=.95*a.vm+(1-a.vm)*pow(a.mid_att,2)*.02;a.vvm=.95*a.vvm+(1-a.vvm)*a.vm*.01;a.vt=.95*a.vt+(1-a.vt)*pow(a.treb_att,2)*.02;a.vvt=.95*a.vvt+(1-a.vvt)*a.vt*.01;a.vvb=Math.min(1,Math.max(0,a.vvb));a.vvm=Math.min(1,Math.max(0,a.vvm));a.vvt=Math.min(1,Math.max(0,a.vvt));a.q1=10*(a.vvb+a.vvm+a.vvt);a.q2=10*(a.vvb+a.vvm+a.vvt);a.q3=10*(a.vvb+a.vvm+a.vvt);a.q4=10*(a.vvb+a.vvm+a.vvt);a.q5=10*(a.vvb+a.vvm+\\na.vvt);a.q6=10*(a.vvb+a.vvm+a.vvt);a.q7=10*(a.vvb+a.vvm+a.vvt);a.q8=10*(a.vvb+a.vvm+a.vvt);a.q9=10*(a.vvb+a.vvm+a.vvt);a.q10=10*(a.vvb+a.vvm+a.vvt);a.q11=10*(a.vvb+a.vvm+a.vvt);a.q12=10*(a.vvb+a.vvm+a.vvt);a.q13=10*(a.vvb+a.vvm+a.vvt);a.q14=10*(a.vvb+a.vvm+a.vvt);a.q15=10*(a.vvb+a.vvm+a.vvt);a.q16=10*(a.vvb+a.vvm+a.vvt);a.q17=10*(a.vvb+a.vvm+a.vvt);a.q18=10*(a.vvb+a.vvm+a.vvt);a.q19=10*(a.vvb+a.vvm+a.vvt);a.q20=10*(a.vvb+a.vvm+a.vvt);a.q21=10*(a.vvb+a.vvm+a.vvt);a.q22=10*(a.vvb+a.vvm+a.vvt);a.q23=\\n10*(a.vvb+a.vvm+a.vvt);a.q24=10*(a.vvb+a.vvm+a.vvt);a.q25=10*(a.vvb+a.vvm+a.vvt);a.q26=10*(a.vvb+a.vvm+a.vvt);a.q27=10*(a.vvb+a.vvm+a.vvt);a.q28=10*(a.vvb+a.vvm+a.vvt);a.q29=10*(a.vvb+a.vvm+a.vvt);a.q30=10*(a.vvb+a.vvm+a.vvt);a.q31=10*(a.vvb+a.vvm+a.vvt);a.q32=10*(a.vvb+a.vvm+a.vvt);a.zoom=.995;a.wave_a=0;a.mv_g=.45+.3*Math.sin(1.33*a.time)+.25*Math.sin(a.treb);a.mv_r=.45+.3*Math.sin(1.13*a.time)+.0825*(a.bass+a.treb+a.mid);a.mv_b=.45+.3*Math.sin(1.23*a.time)+.25*Math.sin(a.bass);a.mv_dx=.1*Math.sin(.1*\\na.time);a.mv_dy=.13*Math.cos(.084*a.time);a.mv_x=64-Math.floor(32*(Math.sin(1.5*a.time)+1));a.mv_y=48-Math.floor(22*(Math.cos(1.5*a.time)+1));a.monitor=a.mv_y;\",pixel_eqs_str:\"a.cx1=.5+.2*Math.sin(.618*a.time);a.cy1=.5+.2*Math.cos(1.618*a.time);a.dir=a.bass;a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.x1=.00001<Math.abs(above(a.d,.3))?0:.05*Math.sin(a.y-a.cy1)*a.dir;a.y1=.00001<Math.abs(above(a.d,.3))?0:.05*-Math.sin(a.x-a.cx1)*a.dir;a.cx1=.5+.3*Math.sin(2.618*a.time);a.cy1=.5+.3*Math.cos(3.14*a.time);a.dir=2*-a.mid;a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.x2=.00001<Math.abs(above(a.d,.2))?0:.05*Math.sin(a.y-a.cy1)*\\na.dir;a.y2=.00001<Math.abs(above(a.d,.2))?0:.05*-Math.sin(a.x-a.cx1)*a.dir;a.cx1=.5+.4*Math.sin(2.618*-a.time);a.cy1=.5+.4*Math.cos(1.14*-a.time);a.dir=3*a.treb;a.d=sqrt((a.x-a.cx1)*(a.x-a.cx1)+(a.y-a.cy1)*(a.y-a.cy1));a.x3=.00001<Math.abs(above(a.d,.1))?0:.05*Math.sin(a.y-a.cy1)*a.dir;a.y3=.00001<Math.abs(above(a.d,.1))?0:.05*-Math.sin(a.x-a.cx1)*a.dir;a.dx=a.dx+a.x1+a.x2+a.x3;a.dy=a.dy+a.y1+a.y2+a.y3;\",warp:\" shader_body { \\n  vec2 tmpvar_1;\\n  tmpvar_1 = mix (uv_orig, uv, vec2(0.3, 0.3));\\n  vec2 tmpvar_2;\\n  tmpvar_2 = (texsize.zw * 3.0);\\n  vec2 tmpvar_3;\\n  tmpvar_3.x = dot (((\\n    (texture (sampler_blur1, (tmpvar_1 + (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (tmpvar_1 - (vec2(1.0, 0.0) * tmpvar_2))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  tmpvar_3.y = dot (((\\n    (texture (sampler_blur1, (tmpvar_1 + (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1)\\n   + bias1) - (\\n    (texture (sampler_blur1, (tmpvar_1 - (vec2(0.0, 1.0) * tmpvar_2))).xyz * scale1)\\n   + bias1)), vec3(0.32, 0.49, 0.29));\\n  vec4 tmpvar_4;\\n  tmpvar_4.w = 1.0;\\n  tmpvar_4.xyz = (texture (sampler_fc_main, (tmpvar_1 + (\\n    (tmpvar_3 * texsize.zw)\\n   * 9.0))) - 0.01).xyz;\\n  ret = tmpvar_4.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv_1;\\n  vec3 ret_2;\\n  uv_1 = (0.5 + ((uv - 0.5) * 0.98));\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (texsize.zw * 4.0);\\n  vec3 tmpvar_4;\\n  tmpvar_4 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(1.0, 0.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((texture (sampler_blur1, \\n    (uv_1 + (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1) - ((texture (sampler_blur1, \\n    (uv_1 - (vec2(0.0, 1.0) * tmpvar_3))\\n  ).xyz * scale1) + bias1));\\n  vec3 tmpvar_6;\\n  tmpvar_6.z = 0.0;\\n  tmpvar_6.x = dot (tmpvar_4, vec3(0.32, 0.49, 0.29));\\n  tmpvar_6.y = dot (tmpvar_5, vec3(0.32, 0.49, 0.29));\\n  vec4 tmpvar_7;\\n  tmpvar_7 = texture (sampler_noisevol_hq, ((vec3(0.05, 0.05, 0.0) * (\\n    ((uv_1.xyy + (tmpvar_6 * 0.5)) * texsize.xyy)\\n   * texsize_noisevol_hq.zww)) + (vec3(0.0, 0.0, 0.2) * time)));\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = dot (tmpvar_4, vec3(0.32, 0.49, 0.29));\\n  tmpvar_8.y = dot (tmpvar_5, vec3(0.32, 0.49, 0.29));\\n  uv_1 = (uv_1 - (tmpvar_8 * 0.04));\\n  ret_2 = (abs((\\n    ((texture (sampler_main, uv_1).xyz + ((texture (sampler_blur1, uv_1).xyz * scale1) + bias1)) - ((texture (sampler_blur2, uv_1).xyz * scale2) + bias2))\\n   - \\n    ((texture (sampler_blur3, uv_1).xyz * scale3) + bias3)\\n  )) * (dot (tmpvar_7, vec4(0.32, 0.49, 0.29, 0.0)) * 3.0));\\n  vec3 tmpvar_9;\\n  tmpvar_9 = pow (ret_2, vec3(0.5, 0.5, 0.5));\\n  ret_2 = tmpvar_9;\\n  vec4 tmpvar_10;\\n  tmpvar_10.w = 1.0;\\n  tmpvar_10.xyz = tmpvar_9;\\n  ret = tmpvar_10.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,echo_zoom:1,wave_mode:1,wave_thick:1,wrap:0,invert:1,wave_scale:.5,wave_smoothing:.45,warpanimspeed:2.217,warpscale:100,zoomexp:100,zoom:1.01,warp:1.25946,wave_r:0,wave_g:0,wave_b:0,ob_r:1,ob_g:1,ob_b:1,ob_a:1,ib_r:1,ib_g:1,ib_b:1,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q1=0;\",frame_eqs_str:\"a.q1=.0003*pow(1.2+1*a.bass+.4*a.bass_att+.1*a.treb+.1*a.treb_att+.1*a.mid+.1*a.mid_att,6);a.monitor=a.q1;a.wave_r+=.5*Math.sin(1.13*a.time);a.wave_g+=.5*Math.sin(1.23*a.time);a.wave_b+=.5*Math.sin(1.33*a.time);a.ob_r+=Math.sin(2.26*a.time);a.ob_g+=Math.sin(2.46*a.time);a.ob_b+=Math.sin(2.66*a.time);\",pixel_eqs_str:\"a.zoom+=.125*Math.sin(a.q1);a.rot+=div(.25*Math.sin(100*a.fps)*a.q1,2);a.zoom-=.05*Math.sin(a.rad);a.rot+=.05*Math.sin(5*a.fps);\",warp:\" shader_body { \\n  vec3 ret_1;\\n  vec2 tmpvar_2;\\n  tmpvar_2 = fract(uv);\\n  vec2 tmpvar_3;\\n  tmpvar_3 = (((uv_orig * texsize.xy) * texsize_noise_lq.zw) + rand_frame.xy);\\n  vec2 tmpvar_4;\\n  tmpvar_4 = (texsize.zw * 12.0);\\n  vec3 tmpvar_5;\\n  tmpvar_5 = (((2.0 * \\n    ((texture (sampler_blur3, (uv_orig + (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale3) + bias3)\\n  ) - (2.0 * \\n    ((texture (sampler_blur3, (uv_orig - (vec2(1.0, 0.0) * tmpvar_4))).xyz * scale3) + bias3)\\n  )) * 0.5);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = (((2.0 * \\n    ((texture (sampler_blur3, (uv_orig + (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale3) + bias3)\\n  ) - (2.0 * \\n    ((texture (sampler_blur3, (uv_orig - (vec2(0.0, 1.0) * tmpvar_4))).xyz * scale3) + bias3)\\n  )) * 0.5);\\n  ret_1.x = ((texture (sampler_fc_main, tmpvar_2).x - (\\n    (((texture (sampler_blur3, tmpvar_2).xyz * scale3) + bias3) - texture (sampler_fc_main, tmpvar_2).xyz)\\n  .x * 0.1)) - 0.035);\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_5.x;\\n  tmpvar_7.y = tmpvar_6.x;\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_5.y;\\n  tmpvar_8.y = tmpvar_6.y;\\n  ret_1.y = (((texture (sampler_fc_main, \\n    (((uv_orig + (\\n      ((texture (sampler_noise_lq, tmpvar_3).xy - 0.5) * texsize.zw)\\n     * 4.0)) - ((tmpvar_7 * texsize.zw) * 4.0)) + ((tmpvar_8 * texsize.zw) * 8.0))\\n  ).y + \\n    ((texture (sampler_noise_lq, tmpvar_3).xyz - 0.5) * 0.1)\\n  ) - 0.012) + (texture (sampler_pc_main, uv_orig).z * 0.2)).x;\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_6.x;\\n  tmpvar_9.y = -(tmpvar_5.x);\\n  vec2 tmpvar_10;\\n  tmpvar_10 = ((uv_orig + (\\n    ((texture (sampler_noise_lq, tmpvar_3).xy - 0.5) * texsize.zw)\\n   * 2.0)) + ((tmpvar_9 * texsize.zw) * 64.0));\\n  ret_1.z = (((\\n    (texture (sampler_fc_main, tmpvar_10).z + ((texture (sampler_fc_main, tmpvar_10).z - (\\n      (texture (sampler_blur2, tmpvar_10).xyz * scale2)\\n     + bias2).z) * 0.2))\\n   + \\n    (texture (sampler_noise_lq, tmpvar_3).xyz * 0.1)\\n  ) - (\\n    (texture (sampler_blur3, uv_orig).xyz * scale3)\\n   + bias3).x) - (clamp (\\n    (1.0 - ((texture (sampler_blur2, uv_orig).xyz * scale2) + bias2).x)\\n  , 0.0, 1.0) * 0.04)).x;\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = ret_1;\\n  ret = tmpvar_11.xyz;\\n }\",comp:\"vec2 xlat_mutabled;\\nvec3 xlat_mutabledx;\\nvec3 xlat_mutabledy;\\n shader_body { \\n  vec3 ret_1;\\n  xlat_mutabled = (texsize.zw * 1.5);\\n  xlat_mutabledx = (texture (sampler_main, (uv_orig + (vec2(1.0, 0.0) * xlat_mutabled))).xyz - texture (sampler_main, (uv_orig - (vec2(1.0, 0.0) * xlat_mutabled))).xyz);\\n  xlat_mutabledy = (texture (sampler_main, (uv_orig + (vec2(0.0, 1.0) * xlat_mutabled))).xyz - texture (sampler_main, (uv_orig - (vec2(0.0, 1.0) * xlat_mutabled))).xyz);\\n  vec2 tmpvar_2;\\n  tmpvar_2.x = xlat_mutabledx.y;\\n  tmpvar_2.y = xlat_mutabledy.y;\\n  vec2 x_3;\\n  x_3 = (tmpvar_2 * 8.0);\\n  ret_1 = (((texture (sampler_main, uv).x * \\n    (1.0 - sqrt(dot (x_3, x_3)))\\n  ) * pow (hue_shader, vec3(6.0, 6.0, 6.0))) * 1.4);\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = xlat_mutabledx.z;\\n  tmpvar_4.y = xlat_mutabledy.z;\\n  vec2 x_5;\\n  x_5 = (tmpvar_4 * 4.0);\\n  vec3 tmpvar_6;\\n  tmpvar_6 = mix (ret_1, vec3(1.0, 1.0, 1.0), vec3(sqrt(dot (x_5, x_5))));\\n  ret_1 = tmpvar_6;\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = tmpvar_6;\\n  ret = tmpvar_7.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1.3,decay:1,echo_zoom:1.75,echo_alpha:.15,echo_orient:3,wave_mode:1,wave_dots:1,darken:1,wave_scale:.5,wave_smoothing:.9,warp:.01,mv_l:5},shapes:[{baseVals:{enabled:1,sides:100,border_a:0},init_eqs_str:\"a.q1=0;\",frame_eqs_str:\"a.r+=Math.sin(.339*a.time);a.g+=Math.sin(.369*a.time);a.b+=Math.sin(.399*a.time);a.r2+=Math.sin(.113*a.time);a.g2+=Math.sin(.123*a.time);a.b2+=Math.sin(.133*a.time);a.rad=div(a.q1,3);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"a.q1=0;\",frame_eqs_str:\"a.q1=.0003*pow(1+1.2*a.bass+.4*a.bass_att+.1*a.treb+.1*a.treb_att+.1*a.mid+.1*a.mid_att,6);a.mv_a=a.q1;a.mv_x+=Math.sin(a.time);a.mv_y+=Math.cos(a.time);a.mv_dx+=1.25*Math.sin(8*a.fps);a.mv_dy+=1.35*Math.sin(8*a.fps);a.mv_r+=Math.sin(.565*a.time);a.mv_g+=Math.sin(.615*a.time);a.mv_b+=Math.sin(.665*a.time);a.wave_r+=.5*Math.sin(1.13*a.time);a.wave_g+=.5*Math.sin(1.23*a.time);a.wave_b+=.5*Math.sin(1.33*a.time);\",pixel_eqs_str:\"a.zoom+=.0125*a.q1;a.rot+=.025*Math.sin(10*a.fps)*a.q1;a.warp=0;\",warp:\" shader_body { \\n  vec2 my_uv_1;\\n  vec3 ret_2;\\n  vec3 tmpvar_3;\\n  tmpvar_3 = ((640.0 * texsize.z) * ((2.0 * \\n    ((texture (sampler_blur2, (uv + vec2(0.01, 0.0))).xyz * scale2) + bias2)\\n  ) - (2.0 * \\n    ((texture (sampler_blur2, (uv - vec2(0.01, 0.0))).xyz * scale2) + bias2)\\n  )));\\n  vec3 tmpvar_4;\\n  tmpvar_4 = ((512.0 * texsize.w) * ((2.0 * \\n    ((texture (sampler_blur2, (uv + vec2(0.0, 0.01))).xyz * scale2) + bias2)\\n  ) - (2.0 * \\n    ((texture (sampler_blur2, (uv - vec2(0.0, 0.01))).xyz * scale2) + bias2)\\n  )));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = tmpvar_3.y;\\n  tmpvar_5.y = tmpvar_4.y;\\n  vec2 tmpvar_6;\\n  tmpvar_6.x = tmpvar_3.x;\\n  tmpvar_6.y = tmpvar_4.x;\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = tmpvar_4.y;\\n  tmpvar_7.y = -(tmpvar_3.y);\\n  vec2 tmpvar_8;\\n  tmpvar_8 = (uv - ((\\n    ((tmpvar_5 * 0.3) + (tmpvar_6 * 0.1))\\n   + \\n    (tmpvar_7 * 0.01)\\n  ) * 0.01));\\n  ret_2.x = texture (sampler_fw_main, (tmpvar_8 - floor(tmpvar_8))).x;\\n  ret_2.x = (ret_2.x + ((\\n    (2.0 * ret_2.x)\\n   - \\n    (2.0 * ((texture (sampler_blur1, tmpvar_8).xyz * scale1) + bias1).x)\\n  ) * 0.25));\\n  vec2 tmpvar_9;\\n  tmpvar_9.x = tmpvar_3.y;\\n  tmpvar_9.y = tmpvar_4.y;\\n  vec2 tmpvar_10;\\n  tmpvar_10.x = tmpvar_4.x;\\n  tmpvar_10.y = -(tmpvar_3.y);\\n  my_uv_1 = ((uv - (tmpvar_9 * 0.01)) + (tmpvar_10 * 0.001));\\n  ret_2.y = texture (sampler_fw_main, (my_uv_1 - floor(my_uv_1))).y;\\n  ret_2.y = (ret_2.y + ((\\n    ((2.0 * ret_2.y) - (2.0 * ((texture (sampler_blur3, my_uv_1).xyz * scale3) + bias3).y))\\n   * 0.025) + 0.01));\\n  vec4 tmpvar_11;\\n  tmpvar_11.w = 1.0;\\n  tmpvar_11.xyz = ret_2;\\n  ret = tmpvar_11.xyz;\\n }\",comp:\"vec2 xlat_mutablefactorA;\\n shader_body { \\n  vec2 uv_1;\\n  vec2 dz_2;\\n  vec3 dy_3;\\n  vec3 dx_4;\\n  vec2 d_5;\\n  vec3 ret_6;\\n  xlat_mutablefactorA = (uv - vec2(0.5, 0.5));\\n  vec2 tmpvar_7;\\n  tmpvar_7.x = -((xlat_mutablefactorA.y * -1024.0));\\n  tmpvar_7.y = (xlat_mutablefactorA.x * -1024.0);\\n  vec2 tmpvar_8;\\n  tmpvar_8.x = tmpvar_7.x;\\n  tmpvar_8.y = -(tmpvar_7.y);\\n  uv_1 = (vec2(-100.0, 100.0) * (tmpvar_8 / (\\n    (tmpvar_7.x * tmpvar_7.x)\\n   + \\n    (tmpvar_7.y * tmpvar_7.y)\\n  )).yx);\\n  uv_1 = (0.5 + ((\\n    (1.0 - abs(((\\n      fract((mix ((0.5 + \\n        (((0.5 + (\\n          (uv - 0.5)\\n         * vec2(1.1, 0.81))) - 0.5) * 2.0)\\n      ), (uv_1 + 0.5), vec2(0.5, 0.5)) * 0.5))\\n     * 2.0) - 1.0)))\\n   - 0.5) * 0.98));\\n  vec2 tmpvar_9;\\n  vec2 tmpvar_10;\\n  tmpvar_10 = (vec2(1.0, 0.0) * texsize.zw);\\n  tmpvar_9.x = (texture (sampler_main, (uv_1 + tmpvar_10)).xyz - texture (sampler_main, (uv_1 - tmpvar_10)).xyz).y;\\n  vec2 tmpvar_11;\\n  tmpvar_11 = (vec2(0.0, 1.0) * texsize.zw);\\n  tmpvar_9.y = (texture (sampler_main, (uv_1 + tmpvar_11)).xyz - texture (sampler_main, (uv_1 - tmpvar_11)).xyz).y;\\n  d_5 = (texsize.zw * 2.0);\\n  dx_4 = (((2.0 * \\n    ((texture (sampler_blur1, (uv_1 + (vec2(1.0, 0.0) * d_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_1 - (vec2(1.0, 0.0) * d_5))).xyz * scale1) + bias1)\\n  )) * 0.5);\\n  dy_3 = (((2.0 * \\n    ((texture (sampler_blur1, (uv_1 + (vec2(0.0, 1.0) * d_5))).xyz * scale1) + bias1)\\n  ) - (2.0 * \\n    ((texture (sampler_blur1, (uv_1 - (vec2(0.0, 1.0) * d_5))).xyz * scale1) + bias1)\\n  )) * 0.5);\\n  vec2 tmpvar_12;\\n  tmpvar_12.x = dx_4.y;\\n  tmpvar_12.y = dy_3.y;\\n  dz_2 = ((tmpvar_9 * 3.0) + tmpvar_12);\\n  ret_6 = (vec3(((\\n    pow ((sqrt(dot (dz_2, dz_2)) * 0.8), 0.7)\\n   + \\n    (((texture (sampler_blur2, uv_1).xyz * scale2) + bias2).y * 0.4)\\n  ) - 0.1)) * vec3(0.3, 0.5, 0.7));\\n  vec2 tmpvar_13;\\n  tmpvar_13.x = dx_4.x;\\n  tmpvar_13.y = dy_3.x;\\n  vec3 tmpvar_14;\\n  tmpvar_14 = mix (mix (ret_6, vec3(0.2, 0.1, 0.0), vec3((texture (sampler_main, \\n    (uv_1 + ((tmpvar_13 * texsize.zw) * 18.0))\\n  ).x * 6.0))), vec3(1.0, 1.0, 1.0), texture (sampler_main, uv_1).zzz);\\n  ret_6 = tmpvar_14;\\n  vec4 tmpvar_15;\\n  tmpvar_15.w = 1.0;\\n  tmpvar_15.xyz = tmpvar_14;\\n  ret = tmpvar_15.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,wave_mode:2,wrap:0,wave_a:.001,wave_scale:5.819,wave_mystery:-.38,wave_r:.5,wave_g:.5,wave_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1,thick:1},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"a.y=a.sample;a.x=.003*(a.value1+a.value2);a.a=.1*(a.value1+a.value2);a.r=.5+.3*Math.sin(10*a.sample+a.time);a.g=.5+.3*Math.cos(10*a.sample-1.334*a.time);a.b=.5+.3*Math.sin(10*a.sample+.998*a.time);\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"\",frame_eqs_str:\"a.warp=0;a.wave_r=a.wave_r+.4*Math.sin(.333*a.time)+.2*a.bass_att;a.wave_g=a.wave_g+.4*Math.sin(.555*a.time)+.2*a.treb_att;a.wave_b=a.wave_b+.4*Math.sin(.444*a.time)+.2*a.mid_att;\",pixel_eqs_str:\"\",pixel_eqs:\"\",warp:\" shader_body { \\n  vec2 muv_1;\\n  vec3 ret_2;\\n  float tmpvar_3;\\n  tmpvar_3 = (2.0 * bass_att);\\n  muv_1.x = (uv.x - ((\\n    ((0.01 * cos((\\n      (uv.x * (5.0 + tmpvar_3))\\n     + time))) + (0.01 * cos((\\n      (uv.y * (5.0 + tmpvar_3))\\n     + time))))\\n   * 0.5) * treb_att));\\n  float tmpvar_4;\\n  tmpvar_4 = (2.0 * mid_att);\\n  muv_1.y = (uv.y + ((\\n    ((0.01 * sin((\\n      (uv.x * (5.0 + tmpvar_4))\\n     + time))) - (0.01 * cos((\\n      (uv.y * (5.0 + tmpvar_4))\\n     + time))))\\n   * 0.5) * bass_att));\\n  vec3 tmpvar_5;\\n  tmpvar_5 = ((texture (sampler_blur2, muv_1).xyz * scale2) + bias2);\\n  vec4 tmpvar_6;\\n  tmpvar_6 = texture (sampler_pw_main, muv_1);\\n  ret_2.x = (tmpvar_6.x + (tmpvar_5.x * (bass_att * 0.05)));\\n  ret_2.y = (tmpvar_6.y + (tmpvar_5.y * (treb_att * 0.05)));\\n  ret_2.z = (tmpvar_6.z + (tmpvar_5.z * (mid_att * 0.05)));\\n  if ((ret_2.x > 0.9)) {\\n    ret_2.x = 0.0;\\n  };\\n  if ((ret_2.y > 0.9)) {\\n    ret_2.y = 0.0;\\n  };\\n  if ((ret_2.z > 0.9)) {\\n    ret_2.z = 0.0;\\n  };\\n  vec4 tmpvar_7;\\n  tmpvar_7.w = 1.0;\\n  tmpvar_7.xyz = ret_2;\\n  ret = tmpvar_7.xyz;\\n }\",comp:\" shader_body { \\n  vec3 ret1_1;\\n  vec2 uv1_2;\\n  vec3 ret_3;\\n  vec4 tmpvar_4;\\n  tmpvar_4 = texture (sampler_main, uv);\\n  ret_3 = (tmpvar_4.xyz * (0.6 + (0.3 * \\n    sin(((uv.x * 10.0) + time))\\n  )));\\n  vec2 tmpvar_5;\\n  tmpvar_5.x = (texture (sampler_main, (uv - vec2(0.001, 0.0))).xyz - texture (sampler_main, (uv + vec2(0.001, 0.0))).xyz).x;\\n  tmpvar_5.y = (texture (sampler_main, (uv - vec2(0.0, 0.001))).xyz - texture (sampler_main, (uv + vec2(0.0, 0.001))).xyz).x;\\n  uv1_2 = ((0.5 * cos(\\n    (((uv - 0.5) * 1.5) + 1.6)\\n  )) - (3.0 * tmpvar_5));\\n  ret1_1 = ((0.3 * dot (tmpvar_4.xyz, vec3(0.32, 0.49, 0.29))) + ((\\n    clamp ((0.01 / sqrt(dot (uv1_2, uv1_2))), 0.0, 1.0)\\n   * \\n    mix (vec3(dot (((texture (sampler_blur2, uv).xyz * scale2) + bias2), vec3(0.32, 0.49, 0.29))), ret_3, pow (ret_3, vec3((0.05 + (mid_att * 0.03)))))\\n  ) * (\\n    (4.0 + bass)\\n   + \\n    (mid + treb_att)\\n  )));\\n  ret_3 = ret1_1;\\n  vec4 tmpvar_6;\\n  tmpvar_6.w = 1.0;\\n  tmpvar_6.xyz = ret1_1;\\n  ret = tmpvar_6.xyz;\\n }\"}},function(a,e){a.exports={baseVals:{rating:3,gammaadj:1,decay:.997,echo_zoom:.997,echo_orient:1,wave_thick:1,wave_brighten:0,darken:1,wave_a:.001,wave_scale:.01,wave_smoothing:.27,wave_mystery:-.38,modwavealphastart:.71,modwavealphaend:1.3,warpscale:1.331,zoom:.99951,warp:.01,ob_size:.5,ob_r:.01,ib_size:.26,ib_r:1,ib_g:1,ib_b:1,mv_x:64,mv_y:48,mv_l:.85,mv_r:.5,mv_g:.5,mv_b:.5,mv_a:0},shapes:[{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],waves:[{baseVals:{enabled:1},init_eqs_str:\"\",frame_eqs_str:\"\",point_eqs_str:\"a.x=.5+.2*a.bass*Math.sin(20*a.sample*a.time*a.treb);a.y=.5+.2*a.bass*Math.cos(20*a.sample*a.time*a.treb);a.r=1+.5*Math.sin(.1*a.sample+10*a.time*a.bass);a.g=1+.5*Math.sin(2*a.sample+50*a.time*a.treb);a.b=1+.5*Math.sin(5*a.sample+20*a.time*a.mid);a.a=a.r;\"},{baseVals:{enabled:0}},{baseVals:{enabled:0}},{baseVals:{enabled:0}}],init_eqs_str:\"\",frame_eqs_str:\"a.warp=0;a.decay=.92;\",pixel_eqs_str:\"a.zoom+=.03*a.bass_att*a.bass_att*a.rad;a.rot+=a.rad*bitand(-2.5,5*Math.cos(a.time))*.01;\",warp:\" shader_body { \\n  vec4 tmpvar_1;\\n  tmpvar_1.w = 1.0;\\n  tmpvar_1.xyz = texture (sampler_main, uv).xyz;\\n  ret = tmpvar_1.xyz;\\n }\",comp:\" shader_body { \\n  vec2 uv1_1;\\n  vec3 ret_2;\\n  vec3 tmpvar_3;\\n  tmpvar_3 = texture (sampler_main, uv).xyz;\\n  vec2 tmpvar_4;\\n  tmpvar_4.x = (texture (sampler_main, (uv - vec2(0.001, 0.0))).xyz - texture (sampler_main, (uv + vec2(0.001, 0.0))).xyz).x;\\n  tmpvar_4.y = (texture (sampler_main, (uv - vec2(0.0, 0.001))).xyz - texture (sampler_main, (uv + vec2(0.0, 0.001))).xyz).x;\\n  uv1_1 = ((0.5 * cos(\\n    (((uv - 0.5) * 1.5) + 1.6)\\n  )) - (3.0 * tmpvar_4));\\n  ret_2 = (0.8 * ((0.3 * \\n    dot (tmpvar_3, vec3(0.32, 0.49, 0.29))\\n  ) + (\\n    (clamp ((0.01 / sqrt(\\n      dot (uv1_1, uv1_1)\\n    )), 0.0, 1.0) * tmpvar_3)\\n   * \\n    ((4.0 + bass) + (mid + treb_att))\\n  )));\\n  ret_2 = (ret_2 * 0.77);\\n  vec4 tmpvar_5;\\n  tmpvar_5.w = 1.0;\\n  tmpvar_5.xyz = ret_2;\\n  ret = tmpvar_5.xyz;\\n }\"}},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(a,e,t){\"use strict\";Object.defineProperty(e,\"__esModule\",{value:!0});var r=_(t(6)),n=_(t(7));function _(a){return a&&a.__esModule?a:{default:a}}var s={};s[\"$$$ Royal - Mashup (197)\"]=t(35),s[\"$$$ Royal - Mashup (220)\"]=t(36),s[\"$$$ Royal - Mashup (431)\"]=t(37),s[\"_Aderrasi - Wanderer in Curved Space - mash0000 - faclempt kibitzing meshuggana schmaltz (Geiss color mix)\"]=t(38),s[\"_Geiss - Artifact 01\"]=t(39),s[\"_Geiss - Desert Rose 2\"]=t(40),s[\"_Geiss - untitled\"]=t(41),s._Mig_049=t(42),s._Mig_085=t(43),s[\"_Rovastar + Geiss - Hurricane Nightmare (Posterize Mix)\"]=t(44),s[\"Aderrasi + Geiss - Airhandler (Kali Mix) - Canvas Mix\"]=t(45),s[\"Aderrasi - Potion of Spirits\"]=t(20),s[\"Aderrasi - Songflower (Moss Posy)\"]=t(21),s[\"Aderrasi - Storm of the Eye (Thunder) - mash0000 - quasi pseudo meta concentrics\"]=t(46),s[\"An AdamFX n Martin Infusion 2 flexi - Why The Sky Looks Diffrent Today - AdamFx n Martin Infusion - Tack Tile Disfunction B\"]=t(47),s[\"cope + martin - mother-of-pearl\"]=t(48),s[\"Cope - The Neverending Explosion of Red Liquid Fire\"]=t(49),s[\"Eo.S. + Phat - cubetrace - v2\"]=t(22),s[\"Eo.S. + Zylot - skylight (Stained Glass Majesty mix)\"]=t(23),s[\"Eo.S. - glowsticks v2 05 and proton lights (+Krash′s beat code) _Phat_remix02b\"]=t(24),s[\"fiShbRaiN + Flexi - witchcraft 2.0\"]=t(50),s[\"flexi + amandio c - organic [random mashup]\"]=t(51),s[\"flexi + amandio c - organic12-3d-2.milk\"]=t(52),s[\"Flexi + amandio c - piercing 05 - Kopie (2) - Kopie\"]=t(53),s[\"flexi + fishbrain - neon mindblob grafitti\"]=t(54),s[\"flexi + geiss - pogo cubes vs. tokamak vs. game of life [stahls jelly 4.5 finish]\"]=t(55),s[\"Flexi + Martin - astral projection\"]=t(56),s[\"Flexi + Martin - cascading decay swing\"]=t(57),s[\"Flexi + stahlregen - jelly showoff parade\"]=t(58),s[\"Flexi - alien fish pond\"]=t(59),s[\"Flexi - area 51\"]=t(60),s[\"flexi - bouncing balls [double mindblob neon mix]\"]=t(61),s[\"Flexi - infused with the spiral\"]=t(62),s[\"Flexi - mindblob [shiny mix]\"]=t(63),s[\"Flexi - mindblob mix\"]=t(64),s[\"flexi - mom, why the sky looks different today\"]=t(65),s[\"flexi - patternton, district of media, capitol of the united abstractions of fractopia\"]=t(66),s[\"Flexi - predator-prey-spirals\"]=t(67),s[\"Flexi - smashing fractals [acid etching mix]\"]=t(68),s[\"flexi - swing out on the spiral\"]=t(69),s[\"Flexi - truly soft piece of software - this is generic texturing (Jelly) \"]=t(70),s[\"flexi - what is the matrix\"]=t(71),s[\"Flexi, fishbrain, Geiss + Martin - tokamak witchery\"]=t(72),s[\"Flexi, martin + geiss - dedicated to the sherwin maxawow\"]=t(73),s[\"Fumbling_Foo & Flexi, Martin, Orb, Unchained - Star Nova v7b\"]=t(74),s[\"Geiss + Flexi + Martin - disconnected\"]=t(75),s[\"Geiss - Cauldron - painterly 2 (saturation remix)\"]=t(76),s[\"Geiss - Reaction Diffusion 2\"]=t(77),s[\"Geiss - Spiral Artifact\"]=t(78),s[\"Geiss - Thumb Drum\"]=t(79),s[\"Geiss, Flexi + Stahlregen - Thumbdrum Tokamak [crossfiring aftermath jelly mashup]\"]=t(80),s[\"Goody - The Wild Vort\"]=t(81),s[\"high-altitude basket unraveling - singh grooves nitrogen argon nz+\"]=t(82),s[\"Idiot - Star Of Annon\"]=t(25),s[\"Krash + Illusion - Spiral Movement\"]=t(26),s[\"martin + flexi - diamond cutter [prismaticvortex.com] - camille - i wish i wish i wish i was constrained\"]=t(83),s[\"Martin - acid wiring\"]=t(84),s[\"martin - angel flight\"]=t(85),s[\"martin - another kind of groove\"]=t(86),s[\"martin - bombyx mori\"]=t(87),s[\"martin - castle in the air\"]=t(88),s[\"martin - chain breaker\"]=t(89),s[\"Martin - charisma\"]=t(90),s[\"martin - disco mix 4\"]=t(91),s[\"martin - extreme heat\"]=t(92),s[\"martin - frosty caves 2\"]=t(93),s[\"martin - fruit machine\"]=t(94),s[\"martin - ghost city\"]=t(95),s[\"martin - glass corridor\"]=t(96),s[\"martin - infinity (2010 update)\"]=t(27),s[\"Martin - liquid arrows\"]=t(97),s[\"martin - mandelbox explorer - high speed demo version\"]=t(98),s[\"martin - mucus cervix\"]=t(99),s[\"Martin - QBikal - Surface Turbulence IIb\"]=t(100),s[\"martin - reflections on black tiles\"]=t(101),s[\"martin - stormy sea (2010 update)\"]=t(102),s[\"martin - The Bridge of Khazad-Dum\"]=t(103),s[\"martin - witchcraft reloaded\"]=t(104),s[\"martin [shadow harlequins shape code] - fata morgana\"]=t(105),s[\"martin, flexi, fishbrain + sto - enterstate [random mashup]\"]=t(106),s[\"Milk Artist At our Best - FED - SlowFast Ft AdamFX n Martin - HD CosmoFX\"]=t(107),s[\"ORB - Waaa\"]=t(108),s[\"Phat+fiShbRaiN+Eo.S_Mandala_Chasers_remix\"]=t(28),s[\"Rovastar + Loadus + Geiss - FractalDrop (Triple Mix)\"]=t(109),s[\"Rovastar - Oozing Resistance\"]=t(29),s[\"sawtooth grin roam\"]=t(110),s[\"shifter - dark tides bdrv mix 2\"]=t(111),s[\"suksma - heretical crosscut playpen\"]=t(112),s[\"suksma - Rovastar - Sunflower Passion (Enlightment Mix)_Phat_edit + flexi und martin shaders - circumflex in character classes in regular expression\"]=t(113),s[\"suksma - uninitialized variabowl (hydroponic chronic)\"]=t(114),s[\"suksma - vector exp 1 - couldn′t not\"]=t(115),s[\"TonyMilkdrop - Leonardo Da Vinci's Balloon [Flexi - merry-go-round + techstyle]\"]=t(116),s[\"TonyMilkdrop - Magellan's Nebula [Flexi - you enter first + multiverse]\"]=t(117),s[\"Unchained & Rovastar - Wormhole Pillars (Hall of Shadows mix)\"]=t(30),s[\"Unchained - Rewop\"]=t(31),s[\"Unchained - Unified Drag 2\"]=t(32),s[\"yin - 191 - Temporal singularities\"]=t(33),s[\"Zylot - Paint Spill (Music Reactive Paint Mix)\"]=t(118),s[\"Zylot - Star Ornament\"]=t(34),s[\"Zylot - True Visionary (Final Mix)\"]=t(119);var v=function(){function a(){(0,r.default)(this,a)}return(0,n.default)(a,null,[{key:\"getPresets\",value:function(){return s}}]),a}();e.default=v,a.exports=v}])});","vendor/shaders/LICENSE":"MIT License\n\nCopyright (c) 2026 Shader Effects Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","vendor/shaders/runtime.mjs":"var Kd=class extends Error{reason;constructor(e,t,s){super(t??`[gpu] WebGPU unavailable (${e})`),this.name=\"GpuUnavailableError\",this.reason=e,s?.cause!==void 0&&(this.cause=s.cause)}};function Cy(e){return!!e&&typeof e==\"object\"&&e.name===\"GpuUnavailableError\"&&typeof e.reason==\"string\"}var D5=null;function Cf(){if(D5!==null)return D5;let e=globalThis.__SHADERS_DEBUG__;if(e!=null)return!!e;try{if(typeof localStorage<\"u\"&&localStorage.getItem(\"shaders:debug\")===\"1\")return!0}catch{}return!1}var L5=!1;function U5(){L5=!0}function Mf(...e){(Cf()||L5)&&console.error(...e)}function di(...e){Cf()&&console.warn(...e)}function Sr(...e){Cf()&&console.error(...e)}function O5(){return typeof navigator<\"u\"&&!!navigator.gpu}var Pf=null;function G5(){return Pf||(Pf=(async()=>{if(!O5())return{supported:!1,reason:\"unsupported\"};try{return await navigator.gpu.requestAdapter({powerPreference:\"high-performance\"})?{supported:!0}:{supported:!1,reason:\"no-adapter\"}}catch(e){return Sr(\"[gpu] adapter probe threw:\",e),{supported:!1,reason:\"no-adapter\"}}})(),Pf)}var $W=new Set([\"unsupported\",\"no-adapter\",\"no-device\",\"out-of-memory\",\"gpu-error\"]),Py=null;function F5(e){Py||!$W.has(e)||(Py=e)}function I5(){return Py}function Gr(e,t){let s=e.length;Array.isArray(e[0])||(e=[e]),Array.isArray(t[0])||(t=t.map(i=>[i]));let r=t[0].length,o=t[0].map((i,l)=>t.map(c=>c[l])),a=e.map(i=>o.map(l=>{let c=0;if(!Array.isArray(i)){for(let d of l)c+=i*d;return c}for(let d=0;d<i.length;d++)c+=i[d]*(l[d]||0);return c}));return s===1&&(a=a[0]),r===1?a.map(i=>i[0]):a}function Zp(e){return Dl(e)===\"string\"}function Dl(e){return(Object.prototype.toString.call(e).match(/^\\[object\\s+(.*?)\\]$/)[1]||\"\").toLowerCase()}function Gf(e,{precision:t,unit:s}){return Ll(e)?\"none\":G6(e,t)+(s??\"\")}function Ll(e){return Number.isNaN(e)||e instanceof Number&&e?.none}function uo(e){return Ll(e)?0:e}function G6(e,t){if(e===0)return 0;let s=~~e,r=0;s&&t&&(r=~~Math.log10(Math.abs(s))+1);let o=10**(t-r);return Math.floor(e*o+.5)/o}var XW={deg:1,grad:.9,rad:180/Math.PI,turn:360};function F6(e){if(!e)return;e=e.trim();let t=/^([a-z]+)\\((.+?)\\)$/i,s=/^-?[\\d.]+$/,r=/%|deg|g?rad|turn$/,o=/\\/?\\s*(none|[-\\w.]+(?:%|deg|g?rad|turn)?)/g,a=e.match(t);if(a){let i=[];return a[2].replace(o,(l,c)=>{let d=c.match(r),u=c;if(d){let p=d[0],_=u.slice(0,-p.length);p===\"%\"?(u=new Number(_/100),u.type=\"<percentage>\"):(u=new Number(_*XW[p]),u.type=\"<angle>\",u.unit=p)}else s.test(u)?(u=new Number(u),u.type=\"<number>\"):u===\"none\"&&(u=new Number(NaN),u.none=!0);l.startsWith(\"/\")&&(u=u instanceof Number?u:new Number(u),u.alpha=!0),typeof u==\"object\"&&u instanceof Number&&(u.raw=c),i.push(u)}),{name:a[1].toLowerCase(),rawName:a[1],rawArgs:a[2],args:i}}}function I6(e){return e[e.length-1]}function jp(e,t,s){return isNaN(e)?t:isNaN(t)?e:e+(t-e)*s}function Y6(e,t,s){return(s-e)/(t-e)}function $y(e,t,s){return jp(t[0],t[1],Y6(e[0],e[1],s))}function W6(e){return e.map(t=>t.split(\"|\").map(s=>{s=s.trim();let r=s.match(/^(<[a-z]+>)\\[(-?[.\\d]+),\\s*(-?[.\\d]+)\\]?$/);if(r){let o=new String(r[1]);return o.range=[+r[2],+r[3]],o}return s}))}function q6(e,t,s){return Math.max(Math.min(s,t),e)}function Wf(e,t){return Math.sign(e)===Math.sign(t)?e:-e}function ui(e,t){return Wf(Math.abs(e)**t,e)}function Xy(e,t){return t===0?0:e/t}function V6(e,t,s=0,r=e.length){for(;s<r;){let o=s+r>>1;e[o]<t?s=o+1:r=o}return s}var HW=Object.freeze({__proto__:null,bisectLeft:V6,clamp:q6,copySign:Wf,interpolate:jp,interpolateInv:Y6,isNone:Ll,isString:Zp,last:I6,mapRange:$y,multiplyMatrices:Gr,parseCoordGrammar:W6,parseFunction:F6,serializeNumber:Gf,skipNone:uo,spow:ui,toPrecision:G6,type:Dl,zdiv:Xy}),jW=class{add(e,t,s){if(typeof arguments[0]!=\"string\"){for(var e in arguments[0])this.add(e,arguments[0][e],arguments[1]);return}(Array.isArray(e)?e:[e]).forEach(function(r){this[r]=this[r]||[],t&&this[r][s?\"unshift\":\"push\"](t)},this)}run(e,t){this[e]=this[e]||[],this[e].forEach(function(s){s.call(t&&t.context?t.context:t,t)})}},Ul=new jW,on={gamut_mapping:\"css\",precision:5,deltaE:\"76\",verbose:globalThis?.process?.env?.NODE_ENV?.toLowerCase()!==\"test\",warn:function(t){this.verbose&&globalThis?.console?.warn?.(t)}},fa={D50:[.3457/.3585,1,.2958/.3585],D65:[.3127/.329,1,.3583/.329]};function Gy(e){return Array.isArray(e)?e:fa[e]}function Ff(e,t,s,r={}){if(e=Gy(e),t=Gy(t),!e||!t)throw new TypeError(`Missing white point to convert ${e?\"\":\"from\"}${!e&&!t?\"/\":\"\"}${t?\"\":\"to\"}`);if(e===t)return s;let o={W1:e,W2:t,XYZ:s,options:r};if(Ul.run(\"chromatic-adaptation-start\",o),o.M||(o.W1===fa.D65&&o.W2===fa.D50?o.M=[[1.0479297925449969,.022946870601609652,-.05019226628920524],[.02962780877005599,.9904344267538799,-.017073799063418826],[-.009243040646204504,.015055191490298152,.7518742814281371]]:o.W1===fa.D50&&o.W2===fa.D65&&(o.M=[[.955473421488075,-.02309845494876471,.06325924320057072],[-.0283697093338637,1.0099953980813041,.021041441191917323],[.012314014864481998,-.020507649298898964,1.330365926242124]])),Ul.run(\"chromatic-adaptation-end\",o),o.M)return Gr(o.M,o.XYZ);throw new TypeError(\"Only Bradford CAT with white points D50 and D65 supported for now.\")}var KW=new Set([\"<number>\",\"<percentage>\",\"<angle>\"]);function Y5(e,t,s,r){return Object.entries(e.coords).map(([o,a],i)=>{let l=t.coordGrammar[i],c=r[i],d=c?.type,u;if(c.none?u=l.find(f=>KW.has(f)):u=l.find(f=>f==d),!u){let f=a.name||o;throw new TypeError(`${d??c.raw} not allowed for ${f} in ${s}()`)}let p=u.range;d===\"<percentage>\"&&(p||=[0,1]);let _=a.range||a.refRange;return p&&_&&(r[i]=$y(p,_,r[i])),u})}function N6(e,{meta:t}={}){let s={str:String(e)?.trim()};if(Ul.run(\"parse-start\",s),s.color)return s.color;if(s.parsed=F6(s.str),s.parsed){let r=s.parsed.name;if(r===\"color\"){let o=s.parsed.args.shift(),a=o.startsWith(\"--\")?o.substring(2):`--${o}`,i=[o,a],l=s.parsed.rawArgs.indexOf(\"/\")>0?s.parsed.args.pop():1;for(let u of Ht.all){let p=u.getFormat(\"color\");if(p&&(i.includes(p.id)||p.ids?.filter(_=>i.includes(_)).length)){let _=Object.keys(u.coords).map((h,g)=>s.parsed.args[g]||0),f;return p.coordGrammar&&(f=Y5(u,p,\"color\",_)),t&&Object.assign(t,{formatId:\"color\",types:f}),p.id.startsWith(\"--\")&&!o.startsWith(\"--\")&&on.warn(`${u.name} is a non-standard space and not currently supported in the CSS spec. Use prefixed color(${p.id}) instead of color(${o}).`),o.startsWith(\"--\")&&!p.id.startsWith(\"--\")&&on.warn(`${u.name} is a standard space and supported in the CSS spec. Use color(${p.id}) instead of prefixed color(${o}).`),{spaceId:u.id,coords:_,alpha:l}}}let c=\"\",d=o in Ht.registry?o:a;if(d in Ht.registry){let u=Ht.registry[d].formats?.color?.id;u&&(c=`Did you mean color(${u})?`)}throw new TypeError(`Cannot parse color(${o}). `+(c||\"Missing a plugin?\"))}else for(let o of Ht.all){let a=o.getFormat(r);if(a&&a.type===\"function\"){let i=1;(a.lastAlpha||I6(s.parsed.args).alpha)&&(i=s.parsed.args.pop());let l=s.parsed.args,c;return a.coordGrammar&&(c=Y5(o,a,r,l)),t&&Object.assign(t,{formatId:a.name,types:c}),{spaceId:o.id,coords:l,alpha:i}}}}else for(let r of Ht.all)for(let o in r.formats){let a=r.formats[o];if(a.type!==\"custom\"||a.test&&!a.test(s.str))continue;let i=a.parse(s.str);if(i)return i.alpha??=1,t&&(t.formatId=o),i}throw new TypeError(`Could not parse ${e} as a color. Missing a plugin?`)}function xs(e){if(Array.isArray(e))return e.map(xs);if(!e)throw new TypeError(\"Empty color reference\");Zp(e)&&(e=N6(e));let t=e.space||e.spaceId;return t instanceof Ht||(e.space=Ht.get(t)),e.alpha===void 0&&(e.alpha=1),e}var QW=75e-6,Ht=class $n{constructor(t){this.id=t.id,this.name=t.name,this.base=t.base?$n.get(t.base):null,this.aliases=t.aliases,this.base&&(this.fromBase=t.fromBase,this.toBase=t.toBase);let s=t.coords??this.base.coords;for(let r in s)\"name\"in s[r]||(s[r].name=r);this.coords=s,this.white=Gy(t.white??this.base.white??\"D65\"),this.formats=t.formats??{};for(let r in this.formats){let o=this.formats[r];o.type||=\"function\",o.name||=r}this.formats.color?.id||(this.formats.color={...this.formats.color??{},id:t.cssId||this.id}),t.gamutSpace?this.gamutSpace=t.gamutSpace===\"self\"?this:$n.get(t.gamutSpace):this.isPolar?this.gamutSpace=this.base:this.gamutSpace=this,this.gamutSpace.isUnbounded&&(this.inGamut=(r,o)=>!0),this.referred=t.referred,Object.defineProperty(this,\"path\",{value:ZW(this).reverse(),writable:!1,enumerable:!0,configurable:!0}),Ul.run(\"colorspace-init-end\",this)}inGamut(t,{epsilon:s=QW}={}){if(!this.equals(this.gamutSpace))return t=this.to(this.gamutSpace,t),this.gamutSpace.inGamut(t,{epsilon:s});let r=Object.values(this.coords);return t.every((o,a)=>{let i=r[a];if(i.type!==\"angle\"&&i.range){if(Number.isNaN(o))return!0;let[l,c]=i.range;return(l===void 0||o>=l-s)&&(c===void 0||o<=c+s)}return!0})}get isUnbounded(){return Object.values(this.coords).every(t=>!(\"range\"in t))}get cssId(){return this.formats?.color?.id||this.id}get isPolar(){for(let t in this.coords)if(this.coords[t].type===\"angle\")return!0;return!1}getFormat(t){if(typeof t==\"object\")return t=W5(t,this),t;let s;return t===\"default\"?s=Object.values(this.formats)[0]:s=this.formats[t],s?(s=W5(s,this),s):null}equals(t){return t?this===t||this.id===t||this.id===t.id:!1}to(t,s){if(arguments.length===1){let l=xs(t);[t,s]=[l.space,l.coords]}if(t=$n.get(t),this.equals(t))return s;s=s.map(l=>Number.isNaN(l)?0:l);let r=this.path,o=t.path,a,i;for(let l=0;l<r.length&&r[l].equals(o[l]);l++)a=r[l],i=l;if(!a)throw new Error(`Cannot convert between color spaces ${this} and ${t}: no connection space was found`);for(let l=r.length-1;l>i;l--)s=r[l].toBase(s);for(let l=i+1;l<o.length;l++)s=o[l].fromBase(s);return s}from(t,s){if(arguments.length===1){let r=xs(t);[t,s]=[r.space,r.coords]}return t=$n.get(t),t.to(this,s)}toString(){return`${this.name} (${this.id})`}getMinCoords(){let t=[];for(let s in this.coords){let r=this.coords[s],o=r.range||r.refRange;t.push(o?.min??0)}return t}static registry={};static get all(){return[...new Set(Object.values($n.registry))]}static register(t,s){if(arguments.length===1&&(s=arguments[0],t=s.id),s=this.get(s),this.registry[t]&&this.registry[t]!==s)throw new Error(`Duplicate color space registration: '${t}'`);if(this.registry[t]=s,arguments.length===1&&s.aliases)for(let r of s.aliases)this.register(r,s);return s}static get(t,...s){if(!t||t instanceof $n)return t;if(Dl(t)===\"string\"){let r=$n.registry[t.toLowerCase()];if(!r)throw new TypeError(`No color space found with id = \"${t}\"`);return r}if(s.length)return $n.get(...s);throw new TypeError(`${t} is not a valid color space`)}static resolveCoord(t,s){let r=Dl(t),o,a;if(r===\"string\"?t.includes(\".\")?[o,a]=t.split(\".\"):[o,a]=[,t]:Array.isArray(t)?[o,a]=t:(o=t.space,a=t.coordId),o=$n.get(o),o||(o=s),!o)throw new TypeError(`Cannot resolve coordinate reference ${t}: No color space specified and relative references are not allowed here`);if(r=Dl(a),r===\"number\"||r===\"string\"&&a>=0){let c=Object.entries(o.coords)[a];if(c)return{space:o,id:c[0],index:a,...c[1]}}o=$n.get(o);let i=a.toLowerCase(),l=0;for(let c in o.coords){let d=o.coords[c];if(c.toLowerCase()===i||d.name?.toLowerCase()===i)return{space:o,id:c,index:l,...d};l++}throw new TypeError(`No \"${a}\" coordinate found in ${o.name}. Its coordinates are: ${Object.keys(o.coords).join(\", \")}`)}static DEFAULT_FORMAT={type:\"functions\",name:\"color\"}};function ZW(e){let t=[e];for(let s=e;s=s.base;)t.push(s);return t}function W5(e,{coords:t}={}){if(e.coords&&!e.coordGrammar){e.type||=\"function\",e.name||=\"color\",e.coordGrammar=W6(e.coords);let s=Object.entries(t).map(([r,o],a)=>{let i=e.coordGrammar[a][0],l=o.range||o.refRange,c=i.range,d=\"\";return i==\"<percentage>\"?(c=[0,100],d=\"%\"):i==\"<angle>\"&&(d=\"deg\"),{fromRange:l,toRange:c,suffix:d}});e.serializeCoords=(r,o)=>r.map((a,i)=>{let{fromRange:l,toRange:c,suffix:d}=s[i];return l&&c&&(a=$y(l,c,a)),a=Gf(a,{precision:o,unit:d}),a})}return e}var Ho=new Ht({id:\"xyz-d65\",name:\"XYZ D65\",coords:{x:{name:\"X\"},y:{name:\"Y\"},z:{name:\"Z\"}},white:\"D65\",formats:{color:{ids:[\"xyz-d65\",\"xyz\"]}},aliases:[\"xyz\"]}),Oa=class extends Ht{constructor(e){e.coords||(e.coords={r:{range:[0,1],name:\"Red\"},g:{range:[0,1],name:\"Green\"},b:{range:[0,1],name:\"Blue\"}}),e.base||(e.base=Ho),e.toXYZ_M&&e.fromXYZ_M&&(e.toBase??=t=>{let s=Gr(e.toXYZ_M,t);return this.white!==this.base.white&&(s=Ff(this.white,this.base.white,s)),s},e.fromBase??=t=>(t=Ff(this.base.white,this.white,t),Gr(e.fromXYZ_M,t))),e.referred??=\"display\",super(e)}};function Jp(e,t){return e=xs(e),!t||e.space.equals(t)?e.coords.slice():(t=Ht.get(t),t.from(e))}function sn(e,t){e=xs(e);let{space:s,index:r}=Ht.resolveCoord(t,e.space);return Jp(e,s)[r]}function Hy(e,t,s){return e=xs(e),t=Ht.get(t),e.coords=t.to(e.space,s),e}Hy.returns=\"color\";function Ni(e,t,s){if(e=xs(e),arguments.length===2&&Dl(arguments[1])===\"object\"){let r=arguments[1];for(let o in r)Ni(e,o,r[o])}else{typeof s==\"function\"&&(s=s(sn(e,t)));let{space:r,index:o}=Ht.resolveCoord(t,e.space),a=Jp(e,r);a[o]=s,Hy(e,r,a)}return e}Ni.returns=\"color\";var jy=new Ht({id:\"xyz-d50\",name:\"XYZ D50\",white:\"D50\",base:Ho,fromBase:e=>Ff(Ho.white,\"D50\",e),toBase:e=>Ff(\"D50\",Ho.white,e)}),JW=216/24389,q5=24/116,Ef=24389/27,My=fa.D50,rn=new Ht({id:\"lab\",name:\"Lab\",coords:{l:{refRange:[0,100],name:\"Lightness\"},a:{refRange:[-125,125]},b:{refRange:[-125,125]}},white:My,base:jy,fromBase(e){let t=e.map((s,r)=>s/My[r]).map(s=>s>JW?Math.cbrt(s):(Ef*s+16)/116);return[116*t[1]-16,500*(t[0]-t[1]),200*(t[1]-t[2])]},toBase(e){let t=[];return t[1]=(e[0]+16)/116,t[0]=e[1]/500+t[1],t[2]=t[1]-e[2]/200,[t[0]>q5?Math.pow(t[0],3):(116*t[0]-16)/Ef,e[0]>8?Math.pow((e[0]+16)/116,3):e[0]/Ef,t[2]>q5?Math.pow(t[2],3):(116*t[2]-16)/Ef].map((s,r)=>s*My[r])},formats:{lab:{coords:[\"<number> | <percentage>\",\"<number> | <percentage>[-1,1]\",\"<number> | <percentage>[-1,1]\"]}}});function pi(e){return(e%360+360)%360}function eq(e,t){if(e===\"raw\")return t;let[s,r]=t.map(pi),o=r-s;return e===\"increasing\"?o<0&&(r+=360):e===\"decreasing\"?o>0&&(s+=360):e===\"longer\"?-180<o&&o<180&&(o>0?s+=360:r+=360):e===\"shorter\"&&(o>180?s+=360:o<-180&&(r+=360)),[s,r]}var Kp=new Ht({id:\"lch\",name:\"LCH\",coords:{l:{refRange:[0,100],name:\"Lightness\"},c:{refRange:[0,150],name:\"Chroma\"},h:{refRange:[0,360],type:\"angle\",name:\"Hue\"}},base:rn,fromBase(e){let[t,s,r]=e,o,a=.02;return Math.abs(s)<a&&Math.abs(r)<a?o=NaN:o=Math.atan2(r,s)*180/Math.PI,[t,Math.sqrt(s**2+r**2),pi(o)]},toBase(e){let[t,s,r]=e;return s<0&&(s=0),isNaN(r)&&(r=0),[t,s*Math.cos(r*Math.PI/180),s*Math.sin(r*Math.PI/180)]},formats:{lch:{coords:[\"<number> | <percentage>\",\"<number> | <percentage>\",\"<number> | <angle>\"]}}}),V5=25**7,If=Math.PI,N5=180/If,Qd=If/180;function $5(e){let t=e*e;return t*t*t*e}function $6(e,t,{kL:s=1,kC:r=1,kH:o=1}={}){[e,t]=xs([e,t]);let[a,i,l]=rn.from(e),c=Kp.from(rn,[a,i,l])[1],[d,u,p]=rn.from(t),_=Kp.from(rn,[d,u,p])[1];c<0&&(c=0),_<0&&(_=0);let f=$5((c+_)/2),h=.5*(1-Math.sqrt(f/(f+V5))),g=(1+h)*i,y=(1+h)*u,b=Math.sqrt(g**2+l**2),x=Math.sqrt(y**2+p**2),P=g===0&&l===0?0:Math.atan2(l,g),C=y===0&&p===0?0:Math.atan2(p,y);P<0&&(P+=2*If),C<0&&(C+=2*If),P*=N5,C*=N5;let E=d-a,R=x-b,U=C-P,L=P+C,k=Math.abs(U),D;b*x===0?D=0:k<=180?D=U:U>180?D=U-360:U<-180?D=U+360:on.warn(\"the unthinkable has happened\");let O=2*Math.sqrt(x*b)*Math.sin(D*Qd/2),F=(a+d)/2,q=(b+x)/2,Y=$5(q),V;b*x===0?V=L:k<=180?V=L/2:L<360?V=(L+360)/2:V=(L-360)/2;let X=(F-50)**2,Q=1+.015*X/Math.sqrt(20+X),te=1+.045*q,J=1;J-=.17*Math.cos((V-30)*Qd),J+=.24*Math.cos(2*V*Qd),J+=.32*Math.cos((3*V+6)*Qd),J-=.2*Math.cos((4*V-63)*Qd);let oe=1+.015*q*J,le=30*Math.exp(-1*((V-275)/25)**2),se=2*Math.sqrt(Y/(Y+V5)),Te=-1*Math.sin(2*le*Qd)*se,de=(E/(s*Q))**2;return de+=(R/(r*te))**2,de+=(O/(o*oe))**2,de+=Te*(R/(r*te))*(O/(o*oe)),Math.sqrt(de)}var tq=[[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],sq=[[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],rq=[[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],oq=[[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],tu=new Ht({id:\"oklab\",name:\"Oklab\",coords:{l:{refRange:[0,1],name:\"Lightness\"},a:{refRange:[-.4,.4]},b:{refRange:[-.4,.4]}},white:\"D65\",base:Ho,fromBase(e){return Gr(rq,Gr(tq,e).map(t=>Math.cbrt(t)))},toBase(e){return Gr(sq,Gr(oq,e).map(t=>t**3))},formats:{oklab:{coords:[\"<percentage> | <number>\",\"<number> | <percentage>[-1,1]\",\"<number> | <percentage>[-1,1]\"]}}});function Fy(e,t){[e,t]=xs([e,t]);let[s,r,o]=tu.from(e),[a,i,l]=tu.from(t),c=s-a,d=r-i,u=o-l;return Math.sqrt(c**2+d**2+u**2)}var aq=75e-6;function Wc(e,t,{epsilon:s=aq}={}){e=xs(e),t||(t=e.space),t=Ht.get(t);let r=e.coords;return t!==e.space&&(r=t.from(e)),t.inGamut(r,{epsilon:s})}function su(e){return{space:e.space,coords:e.coords.slice(),alpha:e.alpha}}function X6(e,t,s=\"lab\"){s=Ht.get(s);let r=s.from(e),o=s.from(t);return Math.sqrt(r.reduce((a,i,l)=>{let c=o[l];return isNaN(i)||isNaN(c)?a:a+(c-i)**2},0))}function nq(e,t){return X6(e,t,\"lab\")}var X5=Math.PI/180;function iq(e,t,{l:s=2,c:r=1}={}){[e,t]=xs([e,t]);let[o,a,i]=rn.from(e),[,l,c]=Kp.from(rn,[o,a,i]),[d,u,p]=rn.from(t),_=Kp.from(rn,[d,u,p])[1];l<0&&(l=0),_<0&&(_=0);let f=o-d,h=l-_,g=a-u,y=i-p,b=g**2+y**2-h**2,x=.511;o>=16&&(x=.040975*o/(1+.01765*o));let P=.0638*l/(1+.0131*l)+.638,C;Number.isNaN(c)&&(c=0),c>=164&&c<=345?C=.56+Math.abs(.2*Math.cos((c+168)*X5)):C=.36+Math.abs(.4*Math.cos((c+35)*X5));let E=Math.pow(l,4),R=Math.sqrt(E/(E+1900)),U=P*(R*C+1-R),L=(f/(s*x))**2;return L+=(h/(r*P))**2,L+=b/U**2,Math.sqrt(L)}var H5=203,Ky=new Ht({id:\"xyz-abs-d65\",cssId:\"--xyz-abs-d65\",name:\"Absolute XYZ D65\",coords:{x:{refRange:[0,9504.7],name:\"Xa\"},y:{refRange:[0,1e4],name:\"Ya\"},z:{refRange:[0,10888.3],name:\"Za\"}},base:Ho,fromBase(e){return e.map(t=>Math.max(t*H5,0))},toBase(e){return e.map(t=>Math.max(t/H5,0))}}),kf=1.15,Bf=.66,j5=2610/2**14,lq=2**14/2610,K5=3424/2**12,Q5=2413/2**7,Z5=2392/2**7,cq=1.7*2523/2**5,J5=2**5/(1.7*2523),zf=-.56,Ey=16295499532821565e-27,dq=[[.41478972,.579999,.014648],[-.20151,1.120649,.0531008],[-.0166008,.2648,.6684799]],uq=[[1.9242264357876067,-1.0047923125953657,.037651404030618],[.35031676209499907,.7264811939316552,-.06538442294808501],[-.09098281098284752,-.3127282905230739,1.5227665613052603]],pq=[[.5,.5,0],[3.524,-4.066708,.542708],[.199076,1.096799,-1.295875]],mq=[[1,.1386050432715393,.05804731615611886],[.9999999999999999,-.1386050432715393,-.05804731615611886],[.9999999999999998,-.09601924202631895,-.8118918960560388]],H6=new Ht({id:\"jzazbz\",name:\"Jzazbz\",coords:{jz:{refRange:[0,1],name:\"Jz\"},az:{refRange:[-.5,.5]},bz:{refRange:[-.5,.5]}},base:Ky,fromBase(e){let[t,s,r]=e,[o,a,i]=Gr(pq,Gr(dq,[kf*t-(kf-1)*r,Bf*s-(Bf-1)*t,r]).map(function(l){return((K5+Q5*(l/1e4)**j5)/(1+Z5*(l/1e4)**j5))**cq}));return[(1+zf)*o/(1+zf*o)-Ey,a,i]},toBase(e){let[t,s,r]=e,[o,a,i]=Gr(uq,Gr(mq,[(t+Ey)/(1+zf-zf*(t+Ey)),s,r]).map(function(c){return 1e4*((K5-c**J5)/(Z5*c**J5-Q5))**lq})),l=(o+(kf-1)*i)/kf;return[l,(a+(Bf-1)*l)/Bf,i]},formats:{color:{coords:[\"<number> | <percentage>\",\"<number> | <percentage>[-1,1]\",\"<number> | <percentage>[-1,1]\"]}}}),Iy=new Ht({id:\"jzczhz\",name:\"JzCzHz\",coords:{jz:{refRange:[0,1],name:\"Jz\"},cz:{refRange:[0,1],name:\"Chroma\"},hz:{refRange:[0,360],type:\"angle\",name:\"Hue\"}},base:H6,fromBase(e){let[t,s,r]=e,o,a=2e-4;return Math.abs(s)<a&&Math.abs(r)<a?o=NaN:o=Math.atan2(r,s)*180/Math.PI,[t,Math.sqrt(s**2+r**2),pi(o)]},toBase(e){return[e[0],e[1]*Math.cos(e[2]*Math.PI/180),e[1]*Math.sin(e[2]*Math.PI/180)]}});function _q(e,t){[e,t]=xs([e,t]);let[s,r,o]=Iy.from(e),[a,i,l]=Iy.from(t),c=s-a,d=r-i;Number.isNaN(o)&&Number.isNaN(l)?(o=0,l=0):Number.isNaN(o)?o=l:Number.isNaN(l)&&(l=o);let u=o-l,p=2*Math.sqrt(r*i)*Math.sin(u/2*(Math.PI/180));return Math.sqrt(c**2+d**2+p**2)}var j6=3424/4096,K6=2413/128,Q6=2392/128,e6=2610/16384,fq=2523/32,vq=16384/2610,t6=32/2523,hq=[[.3592832590121217,.6976051147779502,-.035891593232029],[-.1920808463704993,1.100476797037432,.0753748658519118],[.0070797844607479,.0748396662186362,.8433265453898765]],gq=[[2048/4096,2048/4096,0],[6610/4096,-13613/4096,7003/4096],[17933/4096,-17390/4096,-543/4096]],yq=[[.9999999999999998,.0086090370379328,.111029625003026],[.9999999999999998,-.0086090370379328,-.1110296250030259],[.9999999999999998,.5600313357106791,-.3206271749873188]],bq=[[2.0701522183894223,-1.3263473389671563,.2066510476294053],[.3647385209748072,.6805660249472273,-.0453045459220347],[-.0497472075358123,-.0492609666966131,1.1880659249923042]],Yy=new Ht({id:\"ictcp\",name:\"ICTCP\",coords:{i:{refRange:[0,1],name:\"I\"},ct:{refRange:[-.5,.5],name:\"CT\"},cp:{refRange:[-.5,.5],name:\"CP\"}},base:Ky,fromBase(e){return xq(Gr(hq,e))},toBase(e){return Gr(bq,Aq(e))}});function xq(e){return Gr(gq,e.map(function(t){return((j6+K6*(t/1e4)**e6)/(1+Q6*(t/1e4)**e6))**fq}))}function Aq(e){return Gr(yq,e).map(function(t){return 1e4*(Math.max(t**t6-j6,0)/(K6-Q6*t**t6))**vq})}function wq(e,t){[e,t]=xs([e,t]);let[s,r,o]=Yy.from(e),[a,i,l]=Yy.from(t);return 720*Math.sqrt((s-a)**2+.25*(r-i)**2+(o-l)**2)}var Tq=fa.D65,Z6=.42,s6=1/Z6,ky=2*Math.PI,J6=[[.401288,.650173,-.051461],[-.250268,1.204414,.045854],[-.002079,.048952,.953127]],Sq=[[1.8620678550872327,-1.0112546305316843,.14918677544445175],[.38752654323613717,.6214474419314753,-.008973985167612518],[-.015841498849333856,-.03412293802851557,1.0499644368778496]],Pq=[[460,451,288],[460,-891,-261],[460,-220,-6300]],Cq={dark:[.8,.525,.8],dim:[.9,.59,.9],average:[1,.69,1]},Yc={h:[20.14,90,164.25,237.53,380.14],e:[.8,.7,1,1.2,.8],H:[0,100,200,300,400]},Mq=180/Math.PI,r6=Math.PI/180;function ew(e,t){return e.map(s=>{let r=ui(t*Math.abs(s)*.01,Z6);return 400*Wf(r,s)/(r+27.13)})}function Eq(e,t){let s=100/t*27.13**s6;return e.map(r=>{let o=Math.abs(r);return Wf(s*ui(o/(400-o),s6),r)})}function kq(e){let t=pi(e);t<=Yc.h[0]&&(t+=360);let s=V6(Yc.h,t)-1,[r,o]=Yc.h.slice(s,s+2),[a,i]=Yc.e.slice(s,s+2),l=Yc.H[s],c=(t-r)/a;return l+100*c/(c+(o-t)/i)}function Bq(e){let t=(e%400+400)%400,s=Math.floor(.01*t);t=t%100;let[r,o]=Yc.h.slice(s,s+2),[a,i]=Yc.e.slice(s,s+2);return pi((t*(i*r-a*o)-100*r*i)/(t*(i-a)-100*i))}function tw(e,t,s,r,o){let a={};a.discounting=o,a.refWhite=e,a.surround=r;let i=e.map(f=>f*100);a.la=t,a.yb=s;let l=i[1],c=Gr(J6,i);r=Cq[a.surround];let d=r[0];a.c=r[1],a.nc=r[2];let u=(1/(5*a.la+1))**4;a.fl=u*a.la+.1*(1-u)*(1-u)*Math.cbrt(5*a.la),a.flRoot=a.fl**.25,a.n=a.yb/l,a.z=1.48+Math.sqrt(a.n),a.nbb=.725*a.n**-.2,a.ncb=a.nbb;let p=o?1:Math.max(Math.min(d*(1-1/3.6*Math.exp((-a.la-42)/92)),1),0);a.dRgb=c.map(f=>jp(1,l/f,p)),a.dRgbInv=a.dRgb.map(f=>1/f);let _=ew(c.map((f,h)=>f*a.dRgb[h]),a.fl);return a.aW=a.nbb*(2*_[0]+_[1]+.05*_[2]),a}var o6=tw(Tq,64/Math.PI*.2,20,\"average\",!1);function Wy(e,t){if(!(e.J!==void 0^e.Q!==void 0))throw new Error(\"Conversion requires one and only one: 'J' or 'Q'\");if(!(e.C!==void 0^e.M!==void 0^e.s!==void 0))throw new Error(\"Conversion requires one and only one: 'C', 'M' or 's'\");if(!(e.h!==void 0^e.H!==void 0))throw new Error(\"Conversion requires one and only one: 'h' or 'H'\");if(e.J===0||e.Q===0)return[0,0,0];let s=0;e.h!==void 0?s=pi(e.h)*r6:s=Bq(e.H)*r6;let r=Math.cos(s),o=Math.sin(s),a=0;e.J!==void 0?a=ui(e.J,1/2)*.1:e.Q!==void 0&&(a=.25*t.c*e.Q/((t.aW+4)*t.flRoot));let i=0;e.C!==void 0?i=e.C/a:e.M!==void 0?i=e.M/t.flRoot/a:e.s!==void 0&&(i=4e-4*e.s**2*(t.aW+4)/t.c);let l=ui(i*Math.pow(1.64-Math.pow(.29,t.n),-.73),10/9),c=.25*(Math.cos(s+2)+3.8),d=t.aW*ui(a,2/t.c/t.z),u=5e4/13*t.nc*t.ncb*c,p=d/t.nbb,_=23*(p+.305)*Xy(l,23*u+l*(11*r+108*o));return Gr(Sq,Eq(Gr(Pq,[p,_*r,_*o]).map(f=>f*1/1403),t.fl).map((f,h)=>f*t.dRgbInv[h])).map(f=>f/100)}function sw(e,t){let s=ew(Gr(J6,e.map(g=>g*100)).map((g,y)=>g*t.dRgb[y]),t.fl),r=s[0]+(-12*s[1]+s[2])/11,o=(s[0]+s[1]-2*s[2])/9,a=(Math.atan2(o,r)%ky+ky)%ky,i=.25*(Math.cos(a+2)+3.8),l=ui(5e4/13*t.nc*t.ncb*Xy(i*Math.sqrt(r**2+o**2),s[0]+s[1]+1.05*s[2]+.305),.9)*Math.pow(1.64-Math.pow(.29,t.n),.73),c=ui(t.nbb*(2*s[0]+s[1]+.05*s[2])/t.aW,.5*t.c*t.z),d=100*ui(c,2),u=4/t.c*c*(t.aW+4)*t.flRoot,p=l*c,_=p*t.flRoot,f=pi(a*Mq),h=kq(f);return{J:d,C:p,h:f,s:50*ui(t.c*l/(t.aW+4),1/2),Q:u,M:_,H:h}}var zq=new Ht({id:\"cam16-jmh\",cssId:\"--cam16-jmh\",name:\"CAM16-JMh\",coords:{j:{refRange:[0,100],name:\"J\"},m:{refRange:[0,105],name:\"Colorfulness\"},h:{refRange:[0,360],type:\"angle\",name:\"Hue\"}},base:Ho,fromBase(e){let t=sw(e,o6);return[t.J,t.M,t.h]},toBase(e){return Wy({J:e[0],M:e[1],h:e[2]},o6)}}),Rq=fa.D65,Dq=216/24389,rw=24389/27;function Lq(e){return 116*(e>Dq?Math.cbrt(e):(rw*e+16)/116)-16}function qy(e){return e>8?Math.pow((e+16)/116,3):e/rw}function Uq(e,t){let[s,r,o]=e,a=[],i=0;if(o===0)return[0,0,0];let l=qy(o);o>0?i=.00379058511492914*o**2+.608983189401032*o+.9155088574762233:i=9514440756550361e-21*o**2+.08693057439788597*o-21.928975842194614;let c=2e-12,d=15,u=0,p=1/0;for(;u<=d;){a=Wy({J:i,C:r,h:s},t);let _=Math.abs(a[1]-l);if(_<p){if(_<=c)return a;p=_}i=i-(a[1]-l)*i/(2*a[1]),u+=1}return Wy({J:i,C:r,h:s},t)}function Oq(e,t){let s=Lq(e[1]);if(s===0)return[0,0,0];let r=sw(e,Qy);return[pi(r.h),r.C,s]}var Qy=tw(Rq,200/Math.PI*qy(50),qy(50)*100,\"average\",!1),Qp=new Ht({id:\"hct\",name:\"HCT\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},c:{refRange:[0,145],name:\"Colorfulness\"},t:{refRange:[0,100],name:\"Tone\"}},base:Ho,fromBase(e){return Oq(e)},toBase(e){return Uq(e,Qy)},formats:{color:{id:\"--hct\",coords:[\"<number> | <angle>\",\"<percentage> | <number>\",\"<percentage> | <number>\"]}}}),Gq=Math.PI/180,a6=[1,.007,.0228];function n6(e){e[1]<0&&(e=Qp.fromBase(Qp.toBase(e)));let t=Math.log(Math.max(1+a6[2]*e[1]*Qy.flRoot,1))/a6[2],s=e[0]*Gq,r=t*Math.cos(s),o=t*Math.sin(s);return[e[2],r,o]}function Fq(e,t){[e,t]=xs([e,t]);let[s,r,o]=n6(Qp.from(e)),[a,i,l]=n6(Qp.from(t));return Math.sqrt((s-a)**2+(r-i)**2+(o-l)**2)}var ru={deltaE76:nq,deltaECMC:iq,deltaE2000:$6,deltaEJz:_q,deltaEITP:wq,deltaEOK:Fy,deltaEHCT:Fq};function Iq(e){let t=e?Math.floor(Math.log10(Math.abs(e))):0;return Math.max(parseFloat(`1e${t-2}`),1e-6)}var i6={hct:{method:\"hct.c\",jnd:2,deltaEMethod:\"hct\",blackWhiteClamp:{}},\"hct-tonal\":{method:\"hct.c\",jnd:0,deltaEMethod:\"hct\",blackWhiteClamp:{channel:\"hct.t\",min:0,max:100}}};function Ol(e,{method:t=on.gamut_mapping,space:s=void 0,deltaEMethod:r=\"\",jnd:o=2,blackWhiteClamp:a={}}={}){if(e=xs(e),Zp(arguments[1])?s=arguments[1]:s||(s=e.space),s=Ht.get(s),Wc(e,s,{epsilon:0}))return e;let i;if(t===\"css\")i=Yq(e,{space:s});else{if(t!==\"clip\"&&!Wc(e,s)){Object.prototype.hasOwnProperty.call(i6,t)&&({method:t,jnd:o,deltaEMethod:r,blackWhiteClamp:a}=i6[t]);let l=$6;if(r!==\"\"){for(let d in ru)if(\"deltae\"+r.toLowerCase()===d.toLowerCase()){l=ru[d];break}}let c=Ol(Or(e,s),{method:\"clip\",space:s});if(l(e,c)>o){if(Object.keys(a).length===3){let b=Ht.resolveCoord(a.channel),x=sn(Or(e,b.space),b.id);if(Ll(x)&&(x=0),x>=a.max)return Or({space:\"xyz-d65\",coords:fa.D65},e.space);if(x<=a.min)return Or({space:\"xyz-d65\",coords:[0,0,0]},e.space)}let d=Ht.resolveCoord(t),u=d.space,p=d.id,_=Or(e,u);_.coords.forEach((b,x)=>{Ll(b)&&(_.coords[x]=0)});let f=(d.range||d.refRange)[0],h=Iq(o),g=f,y=sn(_,p);for(;y-g>h;){let b=su(_);b=Ol(b,{space:s,method:\"clip\"}),l(_,b)-o<h?g=sn(_,p):y=sn(_,p),Ni(_,p,(g+y)/2)}i=Or(_,s)}else i=c}else i=Or(e,s);if(t===\"clip\"||!Wc(i,s,{epsilon:0})){let l=Object.values(s.coords).map(c=>c.range||[]);i.coords=i.coords.map((c,d)=>{let[u,p]=l[d];return u!==void 0&&(c=Math.max(u,c)),p!==void 0&&(c=Math.min(c,p)),c})}}return s!==e.space&&(i=Or(i,e.space)),e.coords=i.coords,e}Ol.returns=\"color\";var l6={WHITE:{space:tu,coords:[1,0,0]},BLACK:{space:tu,coords:[0,0,0]}};function Yq(e,{space:t}={}){e=xs(e),t||(t=e.space),t=Ht.get(t);let o=Ht.get(\"oklch\");if(t.isUnbounded)return Or(e,t);let a=Or(e,o),i=a.coords[0];if(i>=1){let h=Or(l6.WHITE,t);return h.alpha=e.alpha,Or(h,t)}if(i<=0){let h=Or(l6.BLACK,t);return h.alpha=e.alpha,Or(h,t)}if(Wc(a,t,{epsilon:0}))return Or(a,t);function l(h){let g=Or(h,t),y=Object.values(t.coords);return g.coords=g.coords.map((b,x)=>{if(\"range\"in y[x]){let[P,C]=y[x].range;return q6(P,b,C)}return b}),g}let c=0,d=a.coords[1],u=!0,p=su(a),_=l(p),f=Fy(_,p);if(f<.02)return _;for(;d-c>1e-4;){let h=(c+d)/2;if(p.coords[1]=h,u&&Wc(p,t,{epsilon:0}))c=h;else if(_=l(p),f=Fy(_,p),f<.02){if(.02-f<1e-4)break;u=!1,c=h}else d=h}return _}function Or(e,t,{inGamut:s}={}){e=xs(e),t=Ht.get(t);let r=t.from(e),o={space:t,coords:r,alpha:e.alpha};return s&&(o=Ol(o,s===!0?void 0:s)),o}Or.returns=\"color\";function Hp(e,{precision:t=on.precision,format:s=\"default\",inGamut:r=!0,...o}={}){let a;e=xs(e);let i=s;s=e.space.getFormat(s)??e.space.getFormat(\"default\")??Ht.DEFAULT_FORMAT;let l=e.coords.slice();if(r||=s.toGamut,r&&!Wc(e)&&(l=Ol(su(e),r===!0?void 0:r).coords),s.type===\"custom\")if(o.precision=t,s.serialize)a=s.serialize(l,e.alpha,o);else throw new TypeError(`format ${i} can only be used to parse colors, not for serialization`);else{let c=s.name||\"color\";s.serializeCoords?l=s.serializeCoords(l,t):t!==null&&(l=l.map(_=>Gf(_,{precision:t})));let d=[...l];if(c===\"color\"){let _=s.id||s.ids?.[0]||e.space.id;d.unshift(_)}let u=e.alpha;t!==null&&(u=Gf(u,{precision:t}));let p=e.alpha>=1||s.noAlpha?\"\":`${s.commas?\",\":\" /\"} ${u}`;a=`${c}(${d.join(s.commas?\", \":\" \")}${p})`}return a}var qf=new Oa({id:\"rec2020-linear\",cssId:\"--rec2020-linear\",name:\"Linear REC.2020\",white:\"D65\",toXYZ_M:[[.6369580483012914,.14461690358620832,.1688809751641721],[.2627002120112671,.6779980715188708,.05930171646986196],[0,.028072693049087428,1.060985057710791]],fromXYZ_M:[[1.716651187971268,-.355670783776392,-.25336628137366],[-.666684351832489,1.616481236634939,.0157685458139111],[.017639857445311,-.042770613257809,.942103121235474]]}),Rf=1.09929682680944,c6=.018053968510807,ow=new Oa({id:\"rec2020\",name:\"REC.2020\",base:qf,toBase(e){return e.map(function(t){return t<c6*4.5?t/4.5:Math.pow((t+Rf-1)/Rf,1/.45)})},fromBase(e){return e.map(function(t){return t>=c6?Rf*Math.pow(t,.45)-(Rf-1):4.5*t})}}),aw=new Oa({id:\"p3-linear\",cssId:\"--display-p3-linear\",name:\"Linear P3\",white:\"D65\",toXYZ_M:[[.4865709486482162,.26566769316909306,.1982172852343625],[.2289745640697488,.6917385218365064,.079286914093745],[0,.04511338185890264,1.043944368900976]],fromXYZ_M:[[2.493496911941425,-.9313836179191239,-.40271078445071684],[-.8294889695615747,1.7626640603183463,.023624685841943577],[.03584583024378447,-.07617238926804182,.9568845240076872]]}),Wq=[[.41239079926595934,.357584339383878,.1804807884018343],[.21263900587151027,.715168678767756,.07219231536073371],[.01933081871559182,.11919477979462598,.9505321522496607]],bo=[[3.2409699419045226,-1.537383177570094,-.4986107602930034],[-.9692436362808796,1.8759675015077202,.04155505740717559],[.05563007969699366,-.20397695888897652,1.0569715142428786]],nw=new Oa({id:\"srgb-linear\",name:\"Linear sRGB\",white:\"D65\",toXYZ_M:Wq,fromXYZ_M:bo}),d6={aliceblue:[240/255,248/255,1],antiquewhite:[250/255,235/255,215/255],aqua:[0,1,1],aquamarine:[127/255,1,212/255],azure:[240/255,1,1],beige:[245/255,245/255,220/255],bisque:[1,228/255,196/255],black:[0,0,0],blanchedalmond:[1,235/255,205/255],blue:[0,0,1],blueviolet:[138/255,43/255,226/255],brown:[165/255,42/255,42/255],burlywood:[222/255,184/255,135/255],cadetblue:[95/255,158/255,160/255],chartreuse:[127/255,1,0],chocolate:[210/255,105/255,30/255],coral:[1,127/255,80/255],cornflowerblue:[100/255,149/255,237/255],cornsilk:[1,248/255,220/255],crimson:[220/255,20/255,60/255],cyan:[0,1,1],darkblue:[0,0,139/255],darkcyan:[0,139/255,139/255],darkgoldenrod:[184/255,134/255,11/255],darkgray:[169/255,169/255,169/255],darkgreen:[0,100/255,0],darkgrey:[169/255,169/255,169/255],darkkhaki:[189/255,183/255,107/255],darkmagenta:[139/255,0,139/255],darkolivegreen:[85/255,107/255,47/255],darkorange:[1,140/255,0],darkorchid:[153/255,50/255,204/255],darkred:[139/255,0,0],darksalmon:[233/255,150/255,122/255],darkseagreen:[143/255,188/255,143/255],darkslateblue:[72/255,61/255,139/255],darkslategray:[47/255,79/255,79/255],darkslategrey:[47/255,79/255,79/255],darkturquoise:[0,206/255,209/255],darkviolet:[148/255,0,211/255],deeppink:[1,20/255,147/255],deepskyblue:[0,191/255,1],dimgray:[105/255,105/255,105/255],dimgrey:[105/255,105/255,105/255],dodgerblue:[30/255,144/255,1],firebrick:[178/255,34/255,34/255],floralwhite:[1,250/255,240/255],forestgreen:[34/255,139/255,34/255],fuchsia:[1,0,1],gainsboro:[220/255,220/255,220/255],ghostwhite:[248/255,248/255,1],gold:[1,215/255,0],goldenrod:[218/255,165/255,32/255],gray:[128/255,128/255,128/255],green:[0,128/255,0],greenyellow:[173/255,1,47/255],grey:[128/255,128/255,128/255],honeydew:[240/255,1,240/255],hotpink:[1,105/255,180/255],indianred:[205/255,92/255,92/255],indigo:[75/255,0,130/255],ivory:[1,1,240/255],khaki:[240/255,230/255,140/255],lavender:[230/255,230/255,250/255],lavenderblush:[1,240/255,245/255],lawngreen:[124/255,252/255,0],lemonchiffon:[1,250/255,205/255],lightblue:[173/255,216/255,230/255],lightcoral:[240/255,128/255,128/255],lightcyan:[224/255,1,1],lightgoldenrodyellow:[250/255,250/255,210/255],lightgray:[211/255,211/255,211/255],lightgreen:[144/255,238/255,144/255],lightgrey:[211/255,211/255,211/255],lightpink:[1,182/255,193/255],lightsalmon:[1,160/255,122/255],lightseagreen:[32/255,178/255,170/255],lightskyblue:[135/255,206/255,250/255],lightslategray:[119/255,136/255,153/255],lightslategrey:[119/255,136/255,153/255],lightsteelblue:[176/255,196/255,222/255],lightyellow:[1,1,224/255],lime:[0,1,0],limegreen:[50/255,205/255,50/255],linen:[250/255,240/255,230/255],magenta:[1,0,1],maroon:[128/255,0,0],mediumaquamarine:[102/255,205/255,170/255],mediumblue:[0,0,205/255],mediumorchid:[186/255,85/255,211/255],mediumpurple:[147/255,112/255,219/255],mediumseagreen:[60/255,179/255,113/255],mediumslateblue:[123/255,104/255,238/255],mediumspringgreen:[0,250/255,154/255],mediumturquoise:[72/255,209/255,204/255],mediumvioletred:[199/255,21/255,133/255],midnightblue:[25/255,25/255,112/255],mintcream:[245/255,1,250/255],mistyrose:[1,228/255,225/255],moccasin:[1,228/255,181/255],navajowhite:[1,222/255,173/255],navy:[0,0,128/255],oldlace:[253/255,245/255,230/255],olive:[128/255,128/255,0],olivedrab:[107/255,142/255,35/255],orange:[1,165/255,0],orangered:[1,69/255,0],orchid:[218/255,112/255,214/255],palegoldenrod:[238/255,232/255,170/255],palegreen:[152/255,251/255,152/255],paleturquoise:[175/255,238/255,238/255],palevioletred:[219/255,112/255,147/255],papayawhip:[1,239/255,213/255],peachpuff:[1,218/255,185/255],peru:[205/255,133/255,63/255],pink:[1,192/255,203/255],plum:[221/255,160/255,221/255],powderblue:[176/255,224/255,230/255],purple:[128/255,0,128/255],rebeccapurple:[102/255,51/255,153/255],red:[1,0,0],rosybrown:[188/255,143/255,143/255],royalblue:[65/255,105/255,225/255],saddlebrown:[139/255,69/255,19/255],salmon:[250/255,128/255,114/255],sandybrown:[244/255,164/255,96/255],seagreen:[46/255,139/255,87/255],seashell:[1,245/255,238/255],sienna:[160/255,82/255,45/255],silver:[192/255,192/255,192/255],skyblue:[135/255,206/255,235/255],slateblue:[106/255,90/255,205/255],slategray:[112/255,128/255,144/255],slategrey:[112/255,128/255,144/255],snow:[1,250/255,250/255],springgreen:[0,1,127/255],steelblue:[70/255,130/255,180/255],tan:[210/255,180/255,140/255],teal:[0,128/255,128/255],thistle:[216/255,191/255,216/255],tomato:[1,99/255,71/255],turquoise:[64/255,224/255,208/255],violet:[238/255,130/255,238/255],wheat:[245/255,222/255,179/255],white:[1,1,1],whitesmoke:[245/255,245/255,245/255],yellow:[1,1,0],yellowgreen:[154/255,205/255,50/255]},u6=Array(3).fill(\"<percentage> | <number>[0, 255]\"),p6=Array(3).fill(\"<number>[0, 255]\"),ou=new Oa({id:\"srgb\",name:\"sRGB\",base:nw,fromBase:e=>e.map(t=>{let s=t<0?-1:1,r=t*s;return r>.0031308?s*(1.055*r**(1/2.4)-.055):12.92*t}),toBase:e=>e.map(t=>{let s=t<0?-1:1,r=t*s;return r<=.04045?t/12.92:s*((r+.055)/1.055)**2.4}),formats:{rgb:{coords:u6},rgb_number:{name:\"rgb\",commas:!0,coords:p6,noAlpha:!0},color:{},rgba:{coords:u6,commas:!0,lastAlpha:!0},rgba_number:{name:\"rgba\",commas:!0,coords:p6},hex:{type:\"custom\",toGamut:!0,test:e=>/^#([a-f0-9]{3,4}){1,2}$/i.test(e),parse(e){e.length<=5&&(e=e.replace(/[a-f0-9]/gi,\"$&$&\"));let t=[];return e.replace(/[a-f0-9]{2}/gi,s=>{t.push(parseInt(s,16)/255)}),{spaceId:\"srgb\",coords:t.slice(0,3),alpha:t.slice(3)[0]}},serialize:(e,t,{collapse:s=!0}={})=>{t<1&&e.push(t),e=e.map(o=>Math.round(o*255));let r=s&&e.every(o=>o%17===0);return\"#\"+e.map(o=>r?(o/17).toString(16):o.toString(16).padStart(2,\"0\")).join(\"\")}},keyword:{type:\"custom\",test:e=>/^[a-z]+$/i.test(e),parse(e){e=e.toLowerCase();let t={spaceId:\"srgb\",coords:null,alpha:1};if(e===\"transparent\"?(t.coords=d6.black,t.alpha=0):t.coords=d6[e],t.coords)return t}}}}),iw=new Oa({id:\"p3\",cssId:\"display-p3\",name:\"P3\",base:aw,fromBase:ou.fromBase,toBase:ou.toBase});on.display_space=ou;var qq;if(typeof CSS<\"u\"&&CSS.supports)for(let e of[rn,ow,iw]){let t=Hp({space:e,coords:e.getMinCoords(),alpha:1});if(CSS.supports(\"color\",t)){on.display_space=e;break}}function Vq(e,{space:t=on.display_space,...s}={}){let r=Hp(e,s);if(typeof CSS>\"u\"||CSS.supports(\"color\",r)||!on.display_space)r=new String(r),r.color=e;else{let o=e;if((e.coords.some(Ll)||Ll(e.alpha))&&!(qq??=CSS.supports(\"color\",\"hsl(none 50% 50%)\"))&&(o=su(e),o.coords=o.coords.map(uo),o.alpha=uo(o.alpha),r=Hp(o,s),CSS.supports(\"color\",r)))return r=new String(r),r.color=o,r;o=Or(o,t),r=new String(Hp(o,s)),r.color=o}return r}function Nq(e,t){return e=xs(e),t=xs(t),e.space===t.space&&e.alpha===t.alpha&&e.coords.every((s,r)=>s===t.coords[r])}function Gl(e){return sn(e,[Ho,\"y\"])}function lw(e,t){Ni(e,[Ho,\"y\"],t)}function $q(e){Object.defineProperty(e.prototype,\"luminance\",{get(){return Gl(this)},set(t){lw(this,t)}})}var Xq=Object.freeze({__proto__:null,getLuminance:Gl,register:$q,setLuminance:lw});function Hq(e,t){e=xs(e),t=xs(t);let s=Math.max(Gl(e),0),r=Math.max(Gl(t),0);return r>s&&([s,r]=[r,s]),(s+.05)/(r+.05)}var jq=.56,Kq=.57,Qq=.62,Zq=.65,m6=.022,Jq=1.414,eV=.1,tV=5e-4,sV=1.14,_6=.027,rV=1.14;function f6(e){return e>=m6?e:e+(m6-e)**Jq}function Zd(e){let t=e<0?-1:1,s=Math.abs(e);return t*Math.pow(s,2.4)}function oV(e,t){t=xs(t),e=xs(e);let s,r,o,a,i,l;t=Or(t,\"srgb\"),[a,i,l]=t.coords;let c=Zd(a)*.2126729+Zd(i)*.7151522+Zd(l)*.072175;e=Or(e,\"srgb\"),[a,i,l]=e.coords;let d=Zd(a)*.2126729+Zd(i)*.7151522+Zd(l)*.072175,u=f6(c),p=f6(d),_=p>u;return Math.abs(p-u)<tV?r=0:_?(s=p**jq-u**Kq,r=s*sV):(s=p**Zq-u**Qq,r=s*rV),Math.abs(r)<eV?o=0:r>0?o=r-_6:o=r+_6,o*100}function aV(e,t){e=xs(e),t=xs(t);let s=Math.max(Gl(e),0),r=Math.max(Gl(t),0);r>s&&([s,r]=[r,s]);let o=s+r;return o===0?0:(s-r)/o}var nV=5e4;function iV(e,t){e=xs(e),t=xs(t);let s=Math.max(Gl(e),0),r=Math.max(Gl(t),0);return r>s&&([s,r]=[r,s]),r===0?nV:(s-r)/r}function lV(e,t){e=xs(e),t=xs(t);let s=sn(e,[rn,\"l\"]),r=sn(t,[rn,\"l\"]);return Math.abs(s-r)}var cV=216/24389,v6=24/116,Df=24389/27,By=fa.D65,Vy=new Ht({id:\"lab-d65\",name:\"Lab D65\",coords:{l:{refRange:[0,100],name:\"Lightness\"},a:{refRange:[-125,125]},b:{refRange:[-125,125]}},white:By,base:Ho,fromBase(e){let t=e.map((s,r)=>s/By[r]).map(s=>s>cV?Math.cbrt(s):(Df*s+16)/116);return[116*t[1]-16,500*(t[0]-t[1]),200*(t[1]-t[2])]},toBase(e){let t=[];return t[1]=(e[0]+16)/116,t[0]=e[1]/500+t[1],t[2]=t[1]-e[2]/200,[t[0]>v6?Math.pow(t[0],3):(116*t[0]-16)/Df,e[0]>8?Math.pow((e[0]+16)/116,3):e[0]/Df,t[2]>v6?Math.pow(t[2],3):(116*t[2]-16)/Df].map((s,r)=>s*By[r])},formats:{\"lab-d65\":{coords:[\"<number> | <percentage>\",\"<number> | <percentage>[-1,1]\",\"<number> | <percentage>[-1,1]\"]}}}),zy=Math.pow(5,.5)*.5+.5;function dV(e,t){e=xs(e),t=xs(t);let s=sn(e,[Vy,\"l\"]),r=sn(t,[Vy,\"l\"]),o=Math.abs(Math.pow(s,zy)-Math.pow(r,zy)),a=Math.pow(o,1/zy)*Math.SQRT2-40;return a<7.5?0:a}var Of=Object.freeze({__proto__:null,contrastAPCA:oV,contrastDeltaPhi:dV,contrastLstar:lV,contrastMichelson:aV,contrastWCAG21:Hq,contrastWeber:iV});function uV(e,t,s={}){Zp(s)&&(s={algorithm:s});let{algorithm:r,...o}=s;if(!r){let a=Object.keys(Of).map(i=>i.replace(/^contrast/,\"\")).join(\", \");throw new TypeError(`contrast() function needs a contrast algorithm. Please specify one of: ${a}`)}e=xs(e),t=xs(t);for(let a in Of)if(\"contrast\"+r.toLowerCase()===a.toLowerCase())return Of[a](e,t,o);throw new TypeError(`Unknown contrast algorithm: ${r}`)}function Vf(e){let[t,s,r]=Jp(e,Ho),o=t+15*s+3*r;return[4*t/o,9*s/o]}function cw(e){let[t,s,r]=Jp(e,Ho),o=t+s+r;return[t/o,s/o]}function pV(e){Object.defineProperty(e.prototype,\"uv\",{get(){return Vf(this)}}),Object.defineProperty(e.prototype,\"xy\",{get(){return cw(this)}})}var mV=Object.freeze({__proto__:null,register:pV,uv:Vf,xy:cw});function Xp(e,t,s={}){Zp(s)&&(s={method:s});let{method:r=on.deltaE,...o}=s;for(let a in ru)if(\"deltae\"+r.toLowerCase()===a.toLowerCase())return ru[a](e,t,o);throw new TypeError(`Unknown deltaE method: ${r}`)}function _V(e,t=.25){return Ni(e,[Ht.get(\"oklch\",\"lch\"),\"l\"],s=>s*(1+t))}function fV(e,t=.25){return Ni(e,[Ht.get(\"oklch\",\"lch\"),\"l\"],s=>s*(1-t))}var vV=Object.freeze({__proto__:null,darken:fV,lighten:_V});function dw(e,t,s=.5,r={}){return[e,t]=[xs(e),xs(t)],Dl(s)===\"object\"&&([s,r]=[.5,s]),em(e,t,r)(s)}function uw(e,t,s={}){let r;Zy(e)&&([r,s]=[e,t],[e,t]=r.rangeArgs.colors);let{maxDeltaE:o,deltaEMethod:a,steps:i=2,maxSteps:l=1e3,...c}=s;r||([e,t]=[xs(e),xs(t)],r=em(e,t,c));let d=Xp(e,t),u=o>0?Math.max(i,Math.ceil(d/o)+1):i,p=[];if(l!==void 0&&(u=Math.min(u,l)),u===1)p=[{p:.5,color:r(.5)}];else{let _=1/(u-1);p=Array.from({length:u},(f,h)=>{let g=h*_;return{p:g,color:r(g)}})}if(o>0){let _=p.reduce((f,h,g)=>{if(g===0)return 0;let y=Xp(h.color,p[g-1].color,a);return Math.max(f,y)},0);for(;_>o;){_=0;for(let f=1;f<p.length&&p.length<l;f++){let h=p[f-1],g=p[f],y=(g.p+h.p)/2,b=r(y);_=Math.max(_,Xp(b,h.color),Xp(b,g.color)),p.splice(f,0,{p:y,color:r(y)}),f++}}}return p=p.map(_=>_.color),p}function em(e,t,s={}){if(Zy(e)){let[c,d]=[e,t];return em(...c.rangeArgs.colors,{...c.rangeArgs.options,...d})}let{space:r,outputSpace:o,progression:a,premultiplied:i}=s;e=xs(e),t=xs(t),e=su(e),t=su(t);let l={colors:[e,t],options:s};if(r?r=Ht.get(r):r=Ht.registry[on.interpolationSpace]||e.space,o=o?Ht.get(o):r,e=Or(e,r),t=Or(t,r),e=Ol(e),t=Ol(t),r.coords.h&&r.coords.h.type===\"angle\"){let c=s.hue=s.hue||\"shorter\",d=[r,\"h\"],[u,p]=[sn(e,d),sn(t,d)];isNaN(u)&&!isNaN(p)?u=p:isNaN(p)&&!isNaN(u)&&(p=u),[u,p]=eq(c,[u,p]),Ni(e,d,u),Ni(t,d,p)}return i&&(e.coords=e.coords.map(c=>c*e.alpha),t.coords=t.coords.map(c=>c*t.alpha)),Object.assign(c=>{c=a?a(c):c;let d=e.coords.map((_,f)=>{let h=t.coords[f];return jp(_,h,c)}),u=jp(e.alpha,t.alpha,c),p={space:r,coords:d,alpha:u};return i&&(p.coords=p.coords.map(_=>_/u)),o!==r&&(p=Or(p,o)),p},{rangeArgs:l})}function Zy(e){return Dl(e)===\"function\"&&!!e.rangeArgs}on.interpolationSpace=\"lab\";function hV(e){e.defineFunction(\"mix\",dw,{returns:\"color\"}),e.defineFunction(\"range\",em,{returns:\"function<color>\"}),e.defineFunction(\"steps\",uw,{returns:\"array<color>\"})}var gV=Object.freeze({__proto__:null,isRange:Zy,mix:dw,range:em,register:hV,steps:uw}),pw=new Ht({id:\"hsl\",name:\"HSL\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},s:{range:[0,100],name:\"Saturation\"},l:{range:[0,100],name:\"Lightness\"}},base:ou,fromBase:e=>{let t=Math.max(...e),s=Math.min(...e),[r,o,a]=e,[i,l,c]=[NaN,0,(s+t)/2],d=t-s;if(d!==0){switch(l=c===0||c===1?0:(t-c)/Math.min(c,1-c),t){case r:i=(o-a)/d+(o<a?6:0);break;case o:i=(a-r)/d+2;break;case a:i=(r-o)/d+4}i=i*60}return l<0&&(i+=180,l=Math.abs(l)),i>=360&&(i-=360),[i,l*100,c*100]},toBase:e=>{let[t,s,r]=e;t=t%360,t<0&&(t+=360),s/=100,r/=100;function o(a){let i=(a+t/30)%12,l=s*Math.min(r,1-r);return r-l*Math.max(-1,Math.min(i-3,9-i,1))}return[o(0),o(8),o(4)]},formats:{hsl:{coords:[\"<number> | <angle>\",\"<percentage>\",\"<percentage>\"]},hsla:{coords:[\"<number> | <angle>\",\"<percentage>\",\"<percentage>\"],commas:!0,lastAlpha:!0}}}),mw=new Ht({id:\"hsv\",name:\"HSV\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},s:{range:[0,100],name:\"Saturation\"},v:{range:[0,100],name:\"Value\"}},base:pw,fromBase(e){let[t,s,r]=e;s/=100,r/=100;let o=r+s*Math.min(r,1-r);return[t,o===0?0:200*(1-r/o),100*o]},toBase(e){let[t,s,r]=e;s/=100,r/=100;let o=r*(1-s/2);return[t,o===0||o===1?0:(r-o)/Math.min(o,1-o)*100,o*100]},formats:{color:{id:\"--hsv\",coords:[\"<number> | <angle>\",\"<percentage> | <number>\",\"<percentage> | <number>\"]}}}),yV=new Ht({id:\"hwb\",name:\"HWB\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},w:{range:[0,100],name:\"Whiteness\"},b:{range:[0,100],name:\"Blackness\"}},base:mw,fromBase(e){let[t,s,r]=e;return[t,r*(100-s)/100,100-r]},toBase(e){let[t,s,r]=e;s/=100,r/=100;let o=s+r;if(o>=1)return[t,0,s/o*100];let a=1-r;return[t,(a===0?0:1-s/a)*100,a*100]},formats:{hwb:{coords:[\"<number> | <angle>\",\"<percentage> | <number>\",\"<percentage> | <number>\"]}}}),_w=new Oa({id:\"a98rgb-linear\",cssId:\"--a98-rgb-linear\",name:\"Linear Adobe\\xAE 98 RGB compatible\",white:\"D65\",toXYZ_M:[[.5766690429101305,.1855582379065463,.1882286462349947],[.29734497525053605,.6273635662554661,.07529145849399788],[.02703136138641234,.07068885253582723,.9913375368376388]],fromXYZ_M:[[2.0415879038107465,-.5650069742788596,-.34473135077832956],[-.9692436362808795,1.8759675015077202,.04155505740717557],[.013444280632031142,-.11836239223101838,1.0151749943912054]]}),bV=new Oa({id:\"a98rgb\",cssId:\"a98-rgb\",name:\"Adobe\\xAE 98 RGB compatible\",base:_w,toBase:e=>e.map(t=>Math.pow(Math.abs(t),563/256)*Math.sign(t)),fromBase:e=>e.map(t=>Math.pow(Math.abs(t),256/563)*Math.sign(t))}),fw=new Oa({id:\"prophoto-linear\",cssId:\"--prophoto-rgb-linear\",name:\"Linear ProPhoto\",white:\"D50\",base:jy,toXYZ_M:[[.7977666449006423,.13518129740053308,.0313477341283922],[.2880748288194013,.711835234241873,8993693872564e-17],[0,0,.8251046025104602]],fromXYZ_M:[[1.3457868816471583,-.25557208737979464,-.05110186497554526],[-.5446307051249019,1.5082477428451468,.02052744743642139],[0,0,1.2119675456389452]]}),xV=1/512,AV=16/512,wV=new Oa({id:\"prophoto\",cssId:\"prophoto-rgb\",name:\"ProPhoto\",base:fw,toBase(e){return e.map(t=>t<AV?t/16:t**1.8)},fromBase(e){return e.map(t=>t>=xV?t**(1/1.8):16*t)}}),TV=new Ht({id:\"oklch\",name:\"Oklch\",coords:{l:{refRange:[0,1],name:\"Lightness\"},c:{refRange:[0,.4],name:\"Chroma\"},h:{refRange:[0,360],type:\"angle\",name:\"Hue\"}},white:\"D65\",base:tu,fromBase(e){let[t,s,r]=e,o,a=2e-4;return Math.abs(s)<a&&Math.abs(r)<a?o=NaN:o=Math.atan2(r,s)*180/Math.PI,[t,Math.sqrt(s**2+r**2),pi(o)]},toBase(e){let[t,s,r]=e,o,a;return isNaN(r)?(o=0,a=0):(o=s*Math.cos(r*Math.PI/180),a=s*Math.sin(r*Math.PI/180)),[t,o,a]},formats:{oklch:{coords:[\"<percentage> | <number>\",\"<number> | <percentage>[0,1]\",\"<number> | <angle>\"]}}}),vw=fa.D65,SV=216/24389,h6=24389/27,[g6,y6]=Vf({space:Ho,coords:vw}),hw=new Ht({id:\"luv\",name:\"Luv\",coords:{l:{refRange:[0,100],name:\"Lightness\"},u:{refRange:[-215,215]},v:{refRange:[-215,215]}},white:vw,base:Ho,fromBase(e){let t=[uo(e[0]),uo(e[1]),uo(e[2])],s=t[1],[r,o]=Vf({space:Ho,coords:t});if(!Number.isFinite(r)||!Number.isFinite(o))return[0,0,0];let a=s<=SV?h6*s:116*Math.cbrt(s)-16;return[a,13*a*(r-g6),13*a*(o-y6)]},toBase(e){let[t,s,r]=e;if(t===0||Ll(t))return[0,0,0];s=uo(s),r=uo(r);let o=s/(13*t)+g6,a=r/(13*t)+y6,i=t<=8?t/h6:Math.pow((t+16)/116,3);return[i*(9*o/(4*a)),i,i*((12-3*o-20*a)/(4*a))]},formats:{color:{id:\"--luv\",coords:[\"<number> | <percentage>\",\"<number> | <percentage>[-1,1]\",\"<number> | <percentage>[-1,1]\"]}}}),Jy=new Ht({id:\"lchuv\",name:\"LChuv\",coords:{l:{refRange:[0,100],name:\"Lightness\"},c:{refRange:[0,220],name:\"Chroma\"},h:{refRange:[0,360],type:\"angle\",name:\"Hue\"}},base:hw,fromBase(e){let[t,s,r]=e,o,a=.02;return Math.abs(s)<a&&Math.abs(r)<a?o=NaN:o=Math.atan2(r,s)*180/Math.PI,[t,Math.sqrt(s**2+r**2),pi(o)]},toBase(e){let[t,s,r]=e;return s<0&&(s=0),isNaN(r)&&(r=0),[t,s*Math.cos(r*Math.PI/180),s*Math.sin(r*Math.PI/180)]},formats:{color:{id:\"--lchuv\",coords:[\"<number> | <percentage>\",\"<number> | <percentage>\",\"<number> | <angle>\"]}}}),PV=216/24389,CV=24389/27,b6=bo[0][0],x6=bo[0][1],Ry=bo[0][2],A6=bo[1][0],w6=bo[1][1],Dy=bo[1][2],T6=bo[2][0],S6=bo[2][1],Ly=bo[2][2];function Jd(e,t,s){let r=t/(Math.sin(s)-e*Math.cos(s));return r<0?1/0:r}function Yf(e){let t=Math.pow(e+16,3)/1560896,s=t>PV?t:e/CV,r=s*(284517*b6-94839*Ry),o=s*(838422*Ry+769860*x6+731718*b6),a=s*(632260*Ry-126452*x6),i=s*(284517*A6-94839*Dy),l=s*(838422*Dy+769860*w6+731718*A6),c=s*(632260*Dy-126452*w6),d=s*(284517*T6-94839*Ly),u=s*(838422*Ly+769860*S6+731718*T6),p=s*(632260*Ly-126452*S6);return{r0s:r/a,r0i:o*e/a,r1s:r/(a+126452),r1i:(o-769860)*e/(a+126452),g0s:i/c,g0i:l*e/c,g1s:i/(c+126452),g1i:(l-769860)*e/(c+126452),b0s:d/p,b0i:u*e/p,b1s:d/(p+126452),b1i:(u-769860)*e/(p+126452)}}function P6(e,t){let s=t/360*Math.PI*2,r=Jd(e.r0s,e.r0i,s),o=Jd(e.r1s,e.r1i,s),a=Jd(e.g0s,e.g0i,s),i=Jd(e.g1s,e.g1i,s),l=Jd(e.b0s,e.b0i,s),c=Jd(e.b1s,e.b1i,s);return Math.min(r,o,a,i,l,c)}var MV=new Ht({id:\"hsluv\",name:\"HSLuv\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},s:{range:[0,100],name:\"Saturation\"},l:{range:[0,100],name:\"Lightness\"}},base:Jy,gamutSpace:ou,fromBase(e){let[t,s,r]=[uo(e[0]),uo(e[1]),uo(e[2])],o;return t>99.9999999?(o=0,t=100):t<1e-8?(o=0,t=0):o=s/P6(Yf(t),r)*100,[r,o,t]},toBase(e){let[t,s,r]=[uo(e[0]),uo(e[1]),uo(e[2])],o;return r>99.9999999?(r=100,o=0):r<1e-8?(r=0,o=0):o=P6(Yf(r),t)/100*s,[r,o,t]},formats:{color:{id:\"--hsluv\",coords:[\"<number> | <angle>\",\"<percentage> | <number>\",\"<percentage> | <number>\"]}}});bo[0][0];bo[0][1];bo[0][2];bo[1][0];bo[1][1];bo[1][2];bo[2][0];bo[2][1];bo[2][2];function eu(e,t){return Math.abs(t)/Math.sqrt(Math.pow(e,2)+1)}function C6(e){let t=eu(e.r0s,e.r0i),s=eu(e.r1s,e.r1i),r=eu(e.g0s,e.g0i),o=eu(e.g1s,e.g1i),a=eu(e.b0s,e.b0i),i=eu(e.b1s,e.b1i);return Math.min(t,s,r,o,a,i)}var EV=new Ht({id:\"hpluv\",name:\"HPLuv\",coords:{h:{refRange:[0,360],type:\"angle\",name:\"Hue\"},s:{range:[0,100],name:\"Saturation\"},l:{range:[0,100],name:\"Lightness\"}},base:Jy,gamutSpace:\"self\",fromBase(e){let[t,s,r]=[uo(e[0]),uo(e[1]),uo(e[2])],o;return t>99.9999999?(o=0,t=100):t<1e-8?(o=0,t=0):o=s/C6(Yf(t))*100,[r,o,t]},toBase(e){let[t,s,r]=[uo(e[0]),uo(e[1]),uo(e[2])],o;return r>99.9999999?(r=100,o=0):r<1e-8?(r=0,o=0):o=C6(Yf(r))/100*s,[r,o,t]},formats:{color:{id:\"--hpluv\",coords:[\"<number> | <angle>\",\"<percentage> | <number>\",\"<percentage> | <number>\"]}}}),M6=203,E6=2610/2**14,kV=2**14/2610,BV=2523/2**5,k6=2**5/2523,B6=3424/2**12,z6=2413/2**7,R6=2392/2**7,zV=new Oa({id:\"rec2100pq\",cssId:\"rec2100-pq\",name:\"REC.2100-PQ\",base:qf,toBase(e){return e.map(function(t){return(Math.max(t**k6-B6,0)/(z6-R6*t**k6))**kV*1e4/M6})},fromBase(e){return e.map(function(t){let s=Math.max(t*M6/1e4,0);return((B6+z6*s**E6)/(1+R6*s**E6))**BV})}}),D6=.17883277,L6=.28466892,U6=.55991073,Uy=3.7743,RV=new Oa({id:\"rec2100hlg\",cssId:\"rec2100-hlg\",name:\"REC.2100-HLG\",referred:\"scene\",base:qf,toBase(e){return e.map(function(t){return t<=.5?t**2/3*Uy:(Math.exp((t-U6)/D6)+L6)/12*Uy})},fromBase(e){return e.map(function(t){return t/=Uy,t<=1/12?Math.sqrt(3*t):D6*Math.log(12*t-L6)+U6})}}),gw={};Ul.add(\"chromatic-adaptation-start\",e=>{e.options.method&&(e.M=yw(e.W1,e.W2,e.options.method))});Ul.add(\"chromatic-adaptation-end\",e=>{e.M||(e.M=yw(e.W1,e.W2,e.options.method))});function Nf({id:e,toCone_M:t,fromCone_M:s}){gw[e]=arguments[0]}function yw(e,t,s=\"Bradford\"){let r=gw[s],[o,a,i]=Gr(r.toCone_M,e),[l,c,d]=Gr(r.toCone_M,t),u=Gr([[l/o,0,0],[0,c/a,0],[0,0,d/i]],r.toCone_M);return Gr(r.fromCone_M,u)}Nf({id:\"von Kries\",toCone_M:[[.40024,.7076,-.08081],[-.2263,1.16532,.0457],[0,0,.91822]],fromCone_M:[[1.8599363874558397,-1.1293816185800916,.21989740959619328],[.3611914362417676,.6388124632850422,-6370596838649899e-21],[0,0,1.0890636230968613]]});Nf({id:\"Bradford\",toCone_M:[[.8951,.2664,-.1614],[-.7502,1.7135,.0367],[.0389,-.0685,1.0296]],fromCone_M:[[.9869929054667121,-.14705425642099013,.15996265166373122],[.4323052697233945,.5183602715367774,.049291228212855594],[-.00852866457517732,.04004282165408486,.96848669578755]]});Nf({id:\"CAT02\",toCone_M:[[.7328,.4296,-.1624],[-.7036,1.6975,.0061],[.003,.0136,.9834]],fromCone_M:[[1.0961238208355142,-.27886900021828726,.18274517938277307],[.4543690419753592,.4735331543074117,.07209780371722911],[-.009627608738429355,-.00569803121611342,1.0153256399545427]]});Nf({id:\"CAT16\",toCone_M:[[.401288,.650173,-.051461],[-.250268,1.204414,.045854],[-.002079,.048952,.953127]],fromCone_M:[[1.862067855087233,-1.0112546305316845,.14918677544445172],[.3875265432361372,.6214474419314753,-.008973985167612521],[-.01584149884933386,-.03412293802851557,1.0499644368778496]]});Object.assign(fa,{A:[1.0985,1,.35585],C:[.98074,1,1.18232],D55:[.95682,1,.92149],D75:[.94972,1,1.22638],E:[1,1,1],F2:[.99186,1,.67393],F7:[.95041,1,1.08747],F11:[1.00962,1,.6435]});fa.ACES=[.32168/.33767,1,.34065/.33767];var bw=new Oa({id:\"acescg\",cssId:\"--acescg\",name:\"ACEScg\",coords:{r:{range:[0,65504],name:\"Red\"},g:{range:[0,65504],name:\"Green\"},b:{range:[0,65504],name:\"Blue\"}},referred:\"scene\",white:fa.ACES,toXYZ_M:[[.6624541811085053,.13400420645643313,.1561876870049078],[.27222871678091454,.6740817658111484,.05368951740793705],[-.005574649490394108,.004060733528982826,1.0103391003129971]],fromXYZ_M:[[1.6410233796943257,-.32480329418479,-.23642469523761225],[-.6636628587229829,1.6153315916573379,.016756347685530137],[.011721894328375376,-.008284441996237409,.9883948585390215]]}),Lf=2**-16,Oy=-.35828683,Uf=(Math.log2(65504)+9.72)/17.52,DV=new Oa({id:\"acescc\",cssId:\"--acescc\",name:\"ACEScc\",coords:{r:{range:[Oy,Uf],name:\"Red\"},g:{range:[Oy,Uf],name:\"Green\"},b:{range:[Oy,Uf],name:\"Blue\"}},referred:\"scene\",base:bw,toBase(e){let t=-.3013698630136986;return e.map(function(s){return s<=t?(2**(s*17.52-9.72)-Lf)*2:s<Uf?2**(s*17.52-9.72):65504})},fromBase(e){return e.map(function(t){return t<=0?(Math.log2(Lf)+9.72)/17.52:t<Lf?(Math.log2(Lf+t*.5)+9.72)/17.52:(Math.log2(t)+9.72)/17.52})}}),O6=Object.freeze({__proto__:null,A98RGB:bV,A98RGB_Linear:_w,ACEScc:DV,ACEScg:bw,CAM16_JMh:zq,HCT:Qp,HPLuv:EV,HSL:pw,HSLuv:MV,HSV:mw,HWB:yV,ICTCP:Yy,JzCzHz:Iy,Jzazbz:H6,LCH:Kp,LCHuv:Jy,Lab:rn,Lab_D65:Vy,Luv:hw,OKLCH:TV,OKLab:tu,P3:iw,P3_Linear:aw,ProPhoto:wV,ProPhoto_Linear:fw,REC_2020:ow,REC_2020_Linear:qf,REC_2100_HLG:RV,REC_2100_PQ:zV,XYZ_ABS_D65:Ky,XYZ_D50:jy,XYZ_D65:Ho,sRGB:ou,sRGB_Linear:nw}),va=class Ua{constructor(...t){let s;t.length===1&&(s=xs(t[0]));let r,o,a;s?(r=s.space||s.spaceId,o=s.coords,a=s.alpha):[r,o,a]=t,Object.defineProperty(this,\"space\",{value:Ht.get(r),writable:!1,enumerable:!0,configurable:!0}),this.coords=o?o.slice():[0,0,0],this.alpha=a>1||a===void 0?1:a<0?0:a;for(let i=0;i<this.coords.length;i++)this.coords[i]===\"NaN\"&&(this.coords[i]=NaN);for(let i in this.space.coords)Object.defineProperty(this,i,{get:()=>this.get(i),set:l=>this.set(i,l)})}get spaceId(){return this.space.id}clone(){return new Ua(this.space,this.coords,this.alpha)}toJSON(){return{spaceId:this.spaceId,coords:this.coords,alpha:this.alpha}}display(...t){let s=Vq(this,...t);return s.color=new Ua(s.color),s}static get(t,...s){return t instanceof Ua?t:new Ua(t,...s)}static defineFunction(t,s,r=s){let{instance:o=!0,returns:a}=r,i=function(...l){let c=s(...l);if(a===\"color\")c=Ua.get(c);else if(a===\"function<color>\"){let d=c;c=function(...u){let p=d(...u);return Ua.get(p)},Object.assign(c,d)}else a===\"array<color>\"&&(c=c.map(d=>Ua.get(d)));return c};t in Ua||(Ua[t]=i),o&&(Ua.prototype[t]=function(...l){return i(this,...l)})}static defineFunctions(t){for(let s in t)Ua.defineFunction(s,t[s],t[s])}static extend(t){if(t.register)t.register(Ua);else for(let s in t)Ua.defineFunction(s,t[s])}};va.defineFunctions({get:sn,getAll:Jp,set:Ni,setAll:Hy,to:Or,equals:Nq,inGamut:Wc,toGamut:Ol,distance:X6,toString:Hp});Object.assign(va,{util:HW,hooks:Ul,WHITES:fa,Space:Ht,spaces:Ht.registry,parse:N6,defaults:on});for(let e of Object.keys(O6))Ht.register(O6[e]);for(let e in Ht.registry)Ny(e,Ht.registry[e]);Ul.add(\"colorspace-init-end\",e=>{Ny(e.id,e),e.aliases?.forEach(t=>{Ny(t,e)})});function Ny(e,t){let s=e.replace(/-/g,\"_\");Object.defineProperty(va.prototype,s,{get(){let r=this.getAll(e);return typeof Proxy>\"u\"?r:new Proxy(r,{has:(o,a)=>{try{return Ht.resolveCoord([t,a]),!0}catch{}return Reflect.has(o,a)},get:(o,a,i)=>{if(a&&typeof a!=\"symbol\"&&!(a in o)){let{index:l}=Ht.resolveCoord([t,a]);if(l>=0)return o[l]}return Reflect.get(o,a,i)},set:(o,a,i,l)=>{if(a&&typeof a!=\"symbol\"&&!(a in o)||a>=0){let{index:c}=Ht.resolveCoord([t,a]);if(c>=0)return o[c]=i,this.setAll(e,o),!0}return Reflect.set(o,a,i,l)}})},set(r){this.setAll(e,r)},configurable:!0,enumerable:!0})}va.extend(ru);va.extend({deltaE:Xp});Object.assign(va,{deltaEMethods:ru});va.extend(vV);va.extend({contrast:uV});va.extend(mV);va.extend(Xq);va.extend(gV);va.extend(Of);var xw=\"p3-linear\",Aw=e=>{xw=e},rs=e=>e?1:-1;function LV(e){try{let t=new va(e),s=xw===\"srgb\"?\"srgb-linear\":\"p3-linear\",r=t.to(s).coords,o=Number(t.alpha);return{r:Number.isFinite(r?.[0])?r[0]:0,g:Number.isFinite(r?.[1])?r[1]:0,b:Number.isFinite(r?.[2])?r[2]:0,a:Number.isFinite(o)?o:1}}catch{return{r:0,g:0,b:0,a:0}}}var ce=e=>LV(e),Xe=e=>{let t=.5,s=.5,r=(a,i=!1)=>{if(typeof a==\"number\")return a;if(typeof a==\"object\"&&a!==null)return a.value;let l=a.toLowerCase().trim();if(i){if(l===\"top\")return 0;if(l===\"bottom\")return 1;if(l===\"center\")return .5}else{if(l===\"left\")return 0;if(l===\"right\")return 1;if(l===\"center\")return .5}return console.warn(`Invalid position value: ${a}. Defaulting to center.`),.5};if(typeof e==\"string\"){let a=e.toLowerCase().trim().split(/\\s+/);a.includes(\"left\")?t=0:a.includes(\"right\")?t=1:(a.includes(\"center\")||a.length===1&&(a[0]===\"top\"||a[0]===\"bottom\"))&&(t=.5),a.includes(\"top\")?s=0:a.includes(\"bottom\")?s=1:(a.includes(\"center\")||a.length===1&&(a[0]===\"left\"||a[0]===\"right\"))&&(s=.5),a.length===1&&a[0]===\"center\"&&(t=.5,s=.5)}else typeof e==\"object\"&&e!==null?(t=r(e.x,!1),s=r(e.y,!0)):console.warn(`Invalid position value provided: ${e}. Defaulting to center.`);let o=1-s;return{x:t,y:o}},cs=e=>{if(typeof e==\"number\")return(e%360+360)%360;let t=e.toLowerCase().trim();switch(t){case\"to right\":return 0;case\"to bottom\":return 90;case\"to left\":return 180;case\"to top\":return 270;case\"to bottom right\":case\"to right bottom\":return 45;case\"to bottom left\":case\"to left bottom\":return 135;case\"to top left\":case\"to left top\":return 225;case\"to top right\":case\"to right top\":return 315;case\"from left\":return 0;case\"from top\":return 90;case\"from right\":return 180;case\"from bottom\":return 270;case\"from top left\":return 45;case\"from top right\":return 135;case\"from bottom right\":return 225;case\"from bottom left\":return 315;default:let s=t.match(/^(-?\\d*\\.?\\d+)(deg|rad|turn)?$/);if(s){let[,r,o=\"deg\"]=s,a=parseFloat(r);switch(o){case\"deg\":return(a%360+360)%360;case\"rad\":return(a*180/Math.PI%360+360)%360;case\"turn\":return(a*360%360+360)%360;default:return(a%360+360)%360}}return console.warn(`Invalid angle value: ${e}. Defaulting to 0 degrees.`),0}},Fr=e=>{let t={stretch:0,transparent:1,mirror:2,wrap:3}[e.toLowerCase()];return t===void 0?(console.warn(`Invalid edge mode: ${e}. Defaulting to 'stretch'.`),0):t},ww=e=>({outside:0,center:1,inside:2})[e?.toLowerCase()]??1,dt=[{label:\"Linear RGB\",value:\"linear\"},{label:\"OKLCh\",value:\"oklch\"},{label:\"OKLAB\",value:\"oklab\"},{label:\"HSL\",value:\"hsl\"},{label:\"HSV\",value:\"hsv\"},{label:\"LCH\",value:\"lch\"}],at=e=>{let t={linear:0,oklch:1,oklab:2,hsl:3,hsv:4,lch:5}[e.toLowerCase()];return t===void 0?(console.warn(`Invalid color space mode: ${e}. Defaulting to 'linear'.`),0):t};var UV=Object.defineProperty,ko=e=>{let t={};for(var s in e)UV(t,s,{get:e[s],enumerable:!0});return t};function an(e){typeof Symbol[e]>\"u\"&&Object.defineProperty(Symbol,e,{value:Symbol.for(`Symbol.${e}`),writable:!1,enumerable:!1,configurable:!1})}function Tw(e,t){return typeof e?.[t]==\"function\"}function ju(e,t,s,r){let o=jo.deferOperation;return Tw(e,s)&&(o=e[s](e,t)),o===jo.deferOperation&&Tw(t,s)&&(o=t[s](e,t)),o===jo.deferOperation?r(e,t):o}var OV=(e,t)=>e+t;function GV(e,t){return(typeof e==\"number\"||typeof e==\"string\")&&(typeof t==\"number\"||typeof t==\"string\")?e+t:ju(e,t,jo.plus,OV)}var FV=(e,t)=>e-t;function IV(e,t){return typeof e==\"number\"&&typeof t==\"number\"?e-t:ju(e,t,jo.minus,FV)}var YV=(e,t)=>e*t;function WV(e,t){return typeof e==\"number\"&&typeof t==\"number\"?e*t:ju(e,t,jo.star,YV)}var qV=(e,t)=>e/t;function VV(e,t){return typeof e==\"number\"&&typeof t==\"number\"?e/t:ju(e,t,jo.slash,qV)}var NV=(e,t)=>e**t;function $V(e,t){return typeof e==\"number\"&&typeof t==\"number\"?e**t:ju(e,t,jo.starStar,NV)}var XV=(e,t)=>e%t;function HV(e,t){return typeof e==\"number\"&&typeof t==\"number\"?e%t:ju(e,t,jo.percent,XV)}var jo=(an(\"deferOperation\"),an(\"operatorPlus\"),an(\"operatorMinus\"),an(\"operatorStar\"),an(\"operatorSlash\"),an(\"operatorStarStar\"),an(\"operatorPercent\"),an(\"operatorEqEq\"),an(\"operatorPrePlusPlus\"),an(\"operatorPreMinusMinus\"),an(\"operatorPostPlusPlus\"),an(\"operatorPostMinusMinus\"),an(\"operatorPreMinus\"),globalThis.__tsover_add=GV,globalThis.__tsover_sub=IV,globalThis.__tsover_mul=WV,globalThis.__tsover_div=VV,globalThis.__tsover_pow=$V,globalThis.__tsover_mod=HV,{deferOperation:Symbol.deferOperation,plus:Symbol.operatorPlus,minus:Symbol.operatorMinus,star:Symbol.operatorStar,slash:Symbol.operatorSlash,starStar:Symbol.operatorStarStar,percent:Symbol.operatorPercent,eqEq:Symbol.operatorEqEq,prePlusPlus:Symbol.operatorPrePlusPlus,preMinusMinus:Symbol.operatorPreMinusMinus,postPlusPlus:Symbol.operatorPostPlusPlus,postMinusMinus:Symbol.operatorPostMinusMinus,preMinus:Symbol.operatorPreMinus}),fh=!1,Lx=!1,Bo=\"0.12.3\",ve=Symbol(`typegpu:${Bo}:$internal`),He=Symbol(`typegpu:${Bo}:$soul`),Ka=Symbol(`typegpu:${Bo}:$gpuValueOf`),Mo=Symbol(`typegpu:${Bo}:$getNameForward`),ya=Symbol(`typegpu:${Bo}:$providing`),tc=Symbol(`typegpu:${Bo}:$ownSnippet`),Ws=Symbol(`typegpu:${Bo}:$resolve`),Ux=Symbol(`typegpu:${Bo}:$cast`),mo=Symbol(`typegpu:${Bo}:$gpuCallable`),Ku=Symbol(`typegpu:${Bo}:$repr`);`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;`${Bo}`;function _s(e){return!!e?.[ve]}var zr={[ve]:{},type:\"void\",toString(){return\"void\"}},jV=[\"bool\",\"f32\",\"f16\",\"i32\",\"u32\",\"u16\",\"vec2f\",\"vec2h\",\"vec2i\",\"vec2u\",\"vec2<bool>\",\"vec3f\",\"vec3h\",\"vec3i\",\"vec3u\",\"vec3<bool>\",\"vec4f\",\"vec4h\",\"vec4i\",\"vec4u\",\"vec4<bool>\",\"mat2x2f\",\"mat3x3f\",\"mat4x4f\",\"struct\",\"array\",\"ptr\",\"atomic\",\"decorated\",\"abstractInt\",\"abstractFloat\",\"void\",\"texture_1d\",\"texture_storage_1d\",\"texture_2d\",\"texture_storage_2d\",\"texture_multisampled_2d\",\"texture_depth_2d\",\"texture_depth_multisampled_2d\",\"texture_2d_array\",\"texture_storage_2d_array\",\"texture_depth_2d_array\",\"texture_cube\",\"texture_depth_cube\",\"texture_cube_array\",\"texture_depth_cube_array\",\"texture_3d\",\"texture_storage_3d\",\"texture_external\",\"sampler\",\"sampler_comparison\"];function Jo(e){let t=e;return _s(t)&&typeof t.kind==\"string\"&&t.kind.startsWith(\"vec\")}function KV(e){return Jo(e)&&e.kind.includes(\"b\")}function QV(e){let t=e;return _s(t)&&typeof t.type==\"string\"&&t.type.startsWith(\"vec2\")}function ZV(e){let t=e;return _s(t)&&typeof t.type==\"string\"&&t.type.startsWith(\"vec3\")}function JV(e){let t=e;return _s(t)&&typeof t.type==\"string\"&&t.type.startsWith(\"vec4\")}function js(e){return QV(e)||ZV(e)||JV(e)}function Ox(e){return js(e)&&e.type.includes(\"b\")}function Kn(e){let t=e;return _s(t)&&typeof t.kind?.startsWith==\"function\"&&t.kind.startsWith(\"mat\")}function CC(e){return _s(e)&&e?.type===\"mat2x2f\"}function MC(e){return _s(e)&&e?.type===\"mat3x3f\"}function eN(e){return _s(e)&&e?.type===\"mat4x4f\"}function xi(e){return CC(e)||MC(e)||eN(e)}function EC(e){return _s(e)&&[\"abstractInt\",\"i32\",\"u32\"].includes(e?.type)}function tN(e){return js(e)&&EC(e.primitive)}function Sw(e){return Jo(e)&&[\"vec2f\",\"vec3f\",\"vec4f\"].includes(e.kind)}function q1(e){return Jo(e)&&/[iu]$/.test(e.kind)}function kC(e){return Jo(e)&&/[u]$/.test(e.kind)}function Td(e){return _s(e)&&jV.includes(e?.type)}function io(e){return _s(e)&&e?.type===\"array\"}function _r(e){return _s(e)&&e?.type===\"struct\"}function ta(e){return _s(e)&&e?.type===\"ptr\"}function vh(e){return _s(e)&&e?.type===\"atomic\"}function BC(e){return _s(e)&&e?.type===\"@align\"}function zC(e){return _s(e)&&e?.type===\"@size\"}function RC(e){return _s(e)&&e?.type===\"@location\"}function sN(e){return _s(e)&&e?.type===\"@builtin\"}function gl(e){return _s(e)&&e?.type===\"decorated\"}function hh(e){return _s(e)&&e.type===\"void\"}function ql(e){return _s(e)&&e.type===\"bool\"}function tb(e){let t=e?.type;return _s(e)&&(t===\"abstractInt\"||t===\"abstractFloat\"||t===\"f32\"||t===\"f16\"||t===\"i32\"||t===\"u32\")}function Gx(e){let t=e?.type;return _s(e)&&(t===\"f16\"||t===\"vec2h\"||t===\"vec3h\"||t===\"vec4h\")}var rN=[\"abstractInt\",\"abstractFloat\",\"f32\",\"f16\",\"i32\",\"u32\",\"bool\"];function jn(e){return!_s(e)||rN.includes(e?.type)}function gh(e){return e.schema}var oN=[\"deprecated\",\"suspicious\",\"fallback\",\"precision-loss\",\"implicit-conversion\",\"webgpu-feature-missing\",\"webgpu-limits-exceeded\",\"locations-mismatched\",\"log-limit-exceeded\",\"external-omitted\",\"uniform-schema-misaligned\"],aN=class{#e;#t;#s=new WeakMap;constructor(e){e?this.#e=[\"webgpu-feature-missing\",\"webgpu-limits-exceeded\",\"locations-mismatched\",\"log-limit-exceeded\",\"external-omitted\"]:this.#e=oN,this.#t=new Set(this.#e)}disable(e){this.#t.delete(e)}reset(){this.#t=new Set(this.#e)}warn(e,...t){this.#t.has(e)&&console.warn(`\\u26A0\\uFE0F [${e}] `,...t)}warnOnce(e,t,s,...r){if(!this.#t.has(e))return;let o=this.#s.get(t);o||(o=new Set,this.#s.set(t,o));let a=`${e}:${s}`;o.has(a)||(o.add(a),this.warn(e,...r))}},DC=new aN(!(fh||Lx)),rr=DC,LC=DC;function pn(e){if(Array.isArray(e))return`[${e.map(pn).join(\", \")}]`;let t=String(e);if(t!==\"[object Object]\")return t;try{return JSON.stringify(e)}catch(s){return rr.warn(\"suspicious\",\"Error parsing JSON:\",s),\"<invalid json>\"}}function sb(e){return Jo(e)||Kn(e)?e.toString():Array.isArray(e)?`[${e.map(sb).join(\", \")}]`:e&&typeof e==\"object\"?`{ ${Object.entries(e).map(([t,s])=>`${t}: ${sb(s)}`).join(\", \")} }`:String(e)}function nN(e){let t={};for(let[s,r]of Object.entries(e))Object.defineProperty(t,s,{get:r,enumerable:!0});return t}function iN(e){if(e.v===1){let t=e.externals;return{...e,externals:typeof t==\"function\"?t:()=>t}}if(e.v===2){let t=nN(e?.externals);return{...e,externals:()=>t}}throw new Error(`Unrecognized TypeGPU metadata format: ${pn(e)}`)}var UC=\"0.12.3\",cl=globalThis;cl.__TYPEGPU_VERSION__!==void 0&&console.warn(`Found duplicate TypeGPU version. First was ${cl.__TYPEGPU_VERSION__}, this one is ${UC}. This may cause unexpected behavior.`);cl.__TYPEGPU_VERSION__=UC;cl.__TYPEGPU_AUTONAME__=(e,t)=>Su(e)&&_s(e)&&!ct(e)?e.$name(t):e;var V1=new WeakMap;function Su(e){return!!e?.$name}function OC(e){return!!e?.[Mo]}function GC(e){return e?.[He]}function ct(e){return OC(e)?ct(e[Mo]):V1.get(e)??GC(e)?.label??cl.__TYPEGPU_META__?.get(e)?.name}function es(e,t){if(OC(e)){es(e[Mo],t);return}V1.set(e,t);let s=GC(e);s&&(s.label=t)}var Pw=new WeakMap;function Fx(e){let t=cl.__TYPEGPU_META__?.get(e);if(t){cl.__TYPEGPU_META__?.delete(e);let s=iN(t);Pw.set(e,s),t.name&&V1.get(e)===void 0&&V1.set(e,t.name)}return Pw.get(e)}function Pu(e){return typeof e==\"function\"&&!!Fx(e)}var Ix=(fh||Lx)&&{get enabled(){return!!cl.__TYPEGPU_MEASURE_PERF__},record(e,t){let s=cl.__TYPEGPU_PERF_RECORDS__??=new Map,r=s.get(e);r||(r=[],s.set(e,r)),r.push(t)}}||void 0,e0=\"Invariant failed\";function kr(e,t){if(e)return;if(!fh&&!Lx)throw new Error(e0);let s=typeof t==\"function\"?t():t,r=s?`${e0}: ${s}`:e0;throw new Error(r)}var fu=class rb extends Error{cause;trace;constructor(t,s){let r=s.map(o=>`- ${Pu(o)?`fn*:${ct(o)}`:o}`);r.length>20&&(r=[...r.slice(0,11),\"...\",...r.slice(-10)]),super(`Resolution of the following tree failed:\n${r.join(`\n`)}: ${t&&typeof t==\"object\"&&\"message\"in t?t.message:t}`),this.cause=t,this.trace=s,Object.setPrototypeOf(this,rb.prototype)}appendToTrace(t){let s=[t,...this.trace];return new rb(this.cause,s)}},Cw=class ob extends Error{cause;trace;constructor(t,s){let r=s.map(o=>`- ${o}`);r.length>20&&(r=[...r.slice(0,11),\"...\",...r.slice(-10)]),super(`Execution of the following tree failed:\n${r.join(`\n`)}: ${t&&typeof t==\"object\"&&\"message\"in t?t.message:t}`),this.cause=t,this.trace=s,Object.setPrototypeOf(this,ob.prototype)}appendToTrace(t){let s=[t,...this.trace];return new ob(this.cause,s)}},lN=class FC extends Error{slot;constructor(t){super(`Missing value for '${t}'`),this.slot=t,Object.setPrototypeOf(this,FC.prototype)}},cN=class IC extends Error{constructor(t){super(`Buffer '${ct(t)??\"<unnamed>\"}' is not bindable as a uniform. Use .$usage('uniform') to allow it.`),Object.setPrototypeOf(this,IC.prototype)}},dN=class YC extends Error{constructor(t){super(`Missing bind groups for layouts: '${[...t].map(s=>ct(s)??\"<unnamed>\").join(\", \")}'. Please provide it using pipeline.with(bindGroup).(...)`),Object.setPrototypeOf(this,YC.prototype)}},uN=class WC extends Error{constructor(t){super(`Missing vertex buffers for layouts: '${[...t].map(s=>ct(s)??\"<unnamed>\").join(\", \")}'. Please provide it using pipeline.with(layout, buffer).(...)`),Object.setPrototypeOf(this,WC.prototype)}},Mw=class qC extends Error{constructor(t){super(t),Object.setPrototypeOf(this,qC.prototype)}},Ew=class VC extends Error{constructor(t){super(t),Object.setPrototypeOf(this,VC.prototype)}},jt=class NC extends Error{constructor(t){super(t),Object.setPrototypeOf(this,NC.prototype)}},jr=class $C extends Error{constructor(t,s){super(`Unsupported data types: ${t.map(r=>r.type).join(\", \")}. Supported types are: ${s.map(r=>r.type).join(\", \")}.`),Object.setPrototypeOf(this,$C.prototype)}},r_=class XC extends Error{constructor(t,s){super(`Cannot convert value '${t}' to type ${s.type} because of the Finite Math Assumption (see: https://www.w3.org/TR/WGSL/#finite-math-assumption)`),Object.setPrototypeOf(this,XC.prototype)}},HC=[\"uint8\",\"uint8x2\",\"uint8x4\",\"sint8\",\"sint8x2\",\"sint8x4\",\"unorm8\",\"unorm8x2\",\"unorm8x4\",\"snorm8\",\"snorm8x2\",\"snorm8x4\",\"uint16\",\"uint16x2\",\"uint16x4\",\"sint16\",\"sint16x2\",\"sint16x4\",\"unorm16\",\"unorm16x2\",\"unorm16x4\",\"snorm16\",\"snorm16x2\",\"snorm16x4\",\"float16\",\"float16x2\",\"float16x4\",\"float32\",\"float32x2\",\"float32x3\",\"float32x4\",\"uint32\",\"uint32x2\",\"uint32x3\",\"uint32x4\",\"sint32\",\"sint32x2\",\"sint32x3\",\"sint32x4\",\"unorm10-10-10-2\",\"unorm8x4-bgra\"],pN={f32:\"float32\",vec2f:\"float32x2\",vec3f:\"float32x3\",vec4f:\"float32x4\",f16:\"float16\",vec2h:\"float16x2\",vec4h:\"float16x4\",u32:\"uint32\",vec2u:\"uint32x2\",vec3u:\"uint32x3\",vec4u:\"uint32x4\",i32:\"sint32\",vec2i:\"sint32x2\",vec3i:\"sint32x3\",vec4i:\"sint32x4\"};function _n(e){return gl(e)||vc(e)?e.inner:e}function $c(e){return ta(e)?e.inner:e}var mN=[\"unstruct\",\"disarray\",\"loose-decorated\",...HC];function yh(e){return _s(e)&&mN.includes(e?.type)}function Sd(e){return _s(e)&&e?.type===\"disarray\"}function ki(e){return _s(e)&&e?.type===\"unstruct\"}function vc(e){return _s(e)&&e?.type===\"loose-decorated\"}function Tm(e){return e.attribs?.find(BC)?.params[0]}function _N(e){return e.attribs?.find(zC)?.params[0]}function sc(e){return e.attribs?.find(RC)?.params[0]}function o_(e){return Td(e)||yh(e)}var Ns=Symbol(\"UNKNOWN\"),jC=class{matrix;constructor(e){this.matrix=e}};function fN(e){return e===\"runtime\"||e===\"constant\"?e:\"runtime\"}function gi(e){return!(e.origin===\"runtime\"||e.origin===\"constant\")}var vN={uniform:{space:\"uniform\",access:\"read\"},readonly:{space:\"storage\",access:\"read\"},mutable:{space:\"storage\",access:\"read-write\"},workgroup:{space:\"workgroup\",access:\"read-write\"},private:{space:\"private\",access:\"read-write\"},function:{space:\"function\",access:\"read-write\"},\"local-def\":{space:\"function\",access:\"read-write\"}},a_=class{value;dataType;origin;possibleSideEffects;constructor(e,t,s,r){this.value=e,this.dataType=t,this.origin=s,this.possibleSideEffects=r}};function n_(e){return e instanceof a_}function fm(e){return tb(e.dataType)}function Je(e,t,s,r=!0){if(fh&&n_(e))throw new Error(\"Cannot nest snippets\");return new a_(e,_n(t),s,r)}function Yx(e,t){return new a_(t.value,e,t.origin,t.possibleSideEffects)}function KC(e,t){return new a_(e,t.dataType,t.origin,t.possibleSideEffects)}function hN(e,t){return new a_(t.value,t.dataType,t.origin,e)}function gN(e){return hN(!1,e)}function ab(e){return e?.resourceType===\"slot\"}function nb(e){return e?.resourceType===\"lazy\"}function S1(e){return e?.[ya]!==void 0}function Qu(e){return e?.resourceType===\"accessor\"}function Zu(e){return e?.resourceType===\"mutable-accessor\"}var Ju=class{type=\"normal\"},yN=class{type=\"codegen\"},bN=class{type=\"simulate\";buffers;vars;constructor(e,t){this.buffers=e,this.vars=t}};function QC(e){return!!e?.[Ws]}function bh(e){return e?.[tc]}function N1(e){return!!e?.[mo]}function xN(e){return!!e?.[Ux]}function qr(e){return(typeof e.value!=\"string\"||e.dataType===Ns)&&bh(e.value)===void 0}function AN(e){return typeof e==\"number\"||typeof e==\"boolean\"||QC(e)||Td(e)||ab(e)||nb(e)||S1(e)}function ol(e){return!!e&&typeof e==\"object\"&&\"getMappedRange\"in e&&\"mapAsync\"in e}function Fm(e){return e.resourceType===\"buffer\"}function ZC(e){return!!e.usableAsUniform}function $1(e){return!!e?.usableAsStorage}var P1=!1;function wN(e){if(P1)return e();try{return P1=!0,e()}finally{P1=!1}}function X1(){return P1}var Zc;function JC(e,t){if(Zc===e)return t();let s=Zc;Zc=e;try{return t()}finally{Zc=s}}function _d(){return Zc}var eM=new Ju;function kw(){return Zc?.mode??eM}function xh(){return Zc?.mode.type===\"codegen\"}function Ne(e,...t){let s=_d();function r(a){return n_(a)?s.resolveSnippet(a).value:a}let o=\"\";for(let a=0;a<e.length;++a){o+=e[a];let i=t[a];Array.isArray(i)?o+=i.filter(l=>l!==void 0).map(r).join(\", \"):i&&(o+=r(i))}return o}function tM(e){return Ah(\"function\",e,\"read-write\")}function Ah(e,t,s,r=!1){return{[ve]:{},type:\"ptr\",addressSpace:e,inner:t,access:s,implicit:r,toString:()=>`ptr<${e}, ${t}, ${s}>`}}function Wx(e,t){let s=vN[e];if(s)return Ah(s.space,t,s.access)}function sM(e){return Ah(e.addressSpace,e.inner,e.access,!0)}function TN(e){return Ah(e.addressSpace,e.inner,e.access,!1)}var SN=(()=>{let e=(t=>PN(t));return es(e,\"ref\"),e.toString=()=>\"ref\",e[ve]=!0,e[mo]={call(t,[s]){if(s.origin===\"argument\")throw new jt(Ne`d.ref(${s}) is illegal, cannot take a reference of an argument. Copy the value first, and take a reference of the copy.`);if(s.origin===\"constant-immutable-def\"||s.origin===\"runtime-immutable-def\"){let o=t.resolve(s.dataType).value;throw new jt(Ne`d.ref(${s}) is illegal, cannot take a reference to a constant.\n-----\n- Try 'd.ref(${o}(${s}));' instead to create a new referencable value.\n-----`)}if(gi(s)&&jn(s.dataType)){let o=t.resolve(s.dataType).value;throw new jt(Ne`d.ref(${s}) is illegal, cannot take a reference to a scalar value.\n-----\n- Try 'd.ref(${o}(${s}));' instead to create a new referencable scalar.\n-----`)}if(ta(s.dataType))return Yx(TN(s.dataType),s);let r=Wx(s.origin,s.dataType);return Je(new Ji(s,r),r??Ns,\"runtime\",s.possibleSideEffects)}},e})();function rM(e){return e?.[ve]?.type===\"ref\"}function PN(e){let t={[ve]:{type:\"ref\"},get $(){return e},set $(s){if(s&&typeof s==\"object\")for(let r of Object.keys(s))e[r]=s[r];else e=s}};if(e==null)throw new Error(\"Cannot create a ref from undefined or null\");return typeof e==\"object\"?new Proxy(t,{get(s,r){return r in s?s[r]:e[r]},set(s,r,o){return r===ve?!1:Reflect.set(r===\"$\"?t:e,r,o)}}):t}var Ji=class{[ve];snippet;#e;constructor(e,t){this[ve]=!0,this.snippet=e,this.#e=t}get[tc](){if(!this.#e)throw new Error(Ne`Cannot take a reference of ${this.snippet}`);return Je(this,this.#e,this.snippet.origin,this.snippet.possibleSideEffects)}[Ws](){if(!this.#e)throw new Error(Ne`Cannot take a reference of ${this.snippet}`);return Je(Ne`(&${this.snippet})`,this.#e,this.snippet.origin,this.snippet.possibleSideEffects)}};function qx(e){if(!ta(e.dataType))return e;let t=e.dataType.inner;return e.value instanceof Ji?Je(Ne`${e.value.snippet}`,t,e.origin,e.possibleSideEffects):Je(Ne`(*${e})`,t,e.origin,e.possibleSideEffects)}function rc(e,t){let s=e;return xN(s)?s[Ux](t):typeof s!=\"function\"?t:t===void 0?s():s(t)}function oM(e,t,s){if(!N1(t))return s;let r=t;return s===void 0||s.value===void 0?r[mo].call(e,[]):r[mo].call(e,[s])}function Im(e,t){throw new Error(`Failed to handle ${e} at ${t}`)}function aM(e,t){kr(_r(e.dataType)||ki(e.dataType),\"Expected snippet type to be struct/unstruct.\");let s=e.dataType.propTypes[t];if(s)return s=_n(s),Je(Ne`${e}.${t}`,s,e.origin,e.possibleSideEffects)}var Vx={rank:Number.POSITIVE_INFINITY,action:\"none\"};function H1(e,t){let s=_n(e),r=_n(t);if(s.type===r.type)return s.type===\"struct\"&&s!==r?{rank:1,action:\"cast\",targetType:t}:{rank:0,action:\"none\"};if(s.type===\"abstractFloat\"){if(r.type===\"f32\")return{rank:1,action:\"none\"};if(r.type===\"f16\")return{rank:2,action:\"none\"}}if(s.type===\"abstractInt\"){if(r.type===\"i32\")return{rank:3,action:\"none\"};if(r.type===\"u32\")return{rank:4,action:\"none\"};if(r.type===\"abstractFloat\")return{rank:5,action:\"none\"};if(r.type===\"f32\")return{rank:6,action:\"none\"};if(r.type===\"f16\")return{rank:7,action:\"none\"}}return js(s)&&js(r)&&s.type[3]===r.type[3]?H1(s.primitive,r.primitive):xi(s)&&xi(r)&&s.type[3]===r.type[3]?{rank:0,action:\"none\"}:Vx}function CN(e,t){let s=_n(e),r=_n(t);if(ta(s)&&s.implicit&&H1(s.inner,r).rank<Number.POSITIVE_INFINITY)return{rank:0,action:\"deref\"};if(ta(r)&&H1(s,r.inner).rank<Number.POSITIVE_INFINITY)return{rank:1,action:\"ref\"};let o={f32:0,f16:1,i32:2,u32:3,bool:4};if(s.type in o&&r.type in o){let a=s.type,i=r.type;if(a!==i){let l=o[a];return{rank:o[i]<l?10:20,action:\"cast\",targetType:r}}}if((s.type===\"u32\"||s.type===\"i32\")&&r.type===\"abstractFloat\")return{rank:1,action:\"cast\",targetType:r.concretized};if(s.type===\"abstractFloat\"){if(r.type===\"i32\")return{rank:2,action:\"cast\",targetType:r};if(r.type===\"u32\")return{rank:3,action:\"cast\",targetType:r}}return Vx}function MN(e,t,s){let r=H1(e,t);return r.rank<Number.POSITIVE_INFINITY?r:s?CN(e,t):Vx}function ib(e,t,s){let r;for(let a of t){let i=a,l=[],c=0;for(let d of e){let u=MN(d,a,s);if(c+=u.rank,u.rank===Number.POSITIVE_INFINITY)break;l.push(u),u.action===\"cast\"&&(i=u.targetType)}c<(r?.sum??Number.POSITIVE_INFINITY)&&(r={type:i,details:l,sum:c})}if(!r)return;let o=r.details.map((a,i)=>({sourceIndex:i,action:a.action,...a.action===\"cast\"&&{targetType:a.targetType}}));return{targetType:r.type,actions:o,hasImplicitConversions:o.some(a=>a.action===\"cast\")}}function Nx(e,t){if(e.length===0)return;let s=[...new Set((t||e).map(_n))],r=ib(e,s,!1);if(r)return r;let o=ib(e,s,!0);if(o)return o}function EN(e,t,s,r){if(s.action===\"none\")return r===t.dataType?t:Yx(r,t);switch(s.action){case\"ref\":return Je(new Ji(t,r),r,t.origin,t.possibleSideEffects);case\"deref\":return qx(t);case\"cast\":if(_r(t.dataType)&&_r(r)){let o=ct(t.dataType)??\"<unnamed>\",a=ct(r)??\"<unnamed>\";if(t.possibleSideEffects)throw new Error(`Cannot resolve struct cast from '${o}' to '${a}'. Store the value to a variable first, then cast it.`);let i=Object.entries(r.propTypes).map(([c,d])=>{let u=aM(t,c);if(!u)throw new Error(`Cannot auto-convert struct '${o}' to '${a}' because the property '${c}' is missing.`);let p=Nl(e,[u],[d]);if(!p||!p[0])throw new Error(`Cannot auto-convert struct '${o}' to '${a}' because type '${ct(u.dataType)??\"<unnamed>\"}' is not convertible to '${ct(_n(d))??\"<unnamed>\"}'.`);return p[0]}),l=e.resolve(r).value;return Je(`${l}(${i.map(c=>c.value).join(\", \")})`,r,\"runtime\",!1)}return oM(e,r,t);default:Im(s.action,\"applyActionToSnippet\")}}function sa(e,t){if(e.some(r=>r===Ns))return;let s=Nx(e,t);if(s)return e.map(r=>js(r)||xi(r)?r:s.targetType)}function wh(e,t){if(e.some(r=>r===Ns))return;let s=ib(e,[...new Set((t||e).map(_n))],!1);if(s)return e.map(r=>js(r)||xi(r)?r:s.targetType)}function Nl(e,t,s,r=!0){let o=t.map(i=>i.dataType);if(o.some(i=>i===Ns))return;kr(!(Array.isArray(s)&&s.length===0),\"Internal error, expected 'restrictTo' to not be an empty array.\");let a=Nx(o,s);if(a)return r&&a.hasImplicitConversions&&rr.warn(\"implicit-conversion\",`Implicit conversions from [\n${t.map(i=>`  ${e.resolveSnippet(i).value}: ${pn(i.dataType)}`).join(`,\n`)}\n] to ${a.targetType.type} are supported, but not recommended.\nConsider using explicit conversions instead.`),t.map((i,l)=>{let c=a.actions[l];return kr(c,\"Action should not be undefined\"),EN(e,i,c,a.targetType)})}function Ga(e,t,s,r=!0){let o=Array.isArray(s)?s:[s],{value:a,dataType:i,origin:l,possibleSideEffects:c}=t;if(o.length===1){let u=o[0];if(u===i)return Je(a,u,l,c);if(i===Ns)return e.resolveSnippet(Je(a,u,l,c))}let d=Nl(e,[t],o,r);if(d)return d[0];throw new jt(`Cannot convert value of type '${String(i)}' to any of the target types: [${o.map(u=>u.type).join(\", \")}]`)}function Bw(e,t,s){return Object.entries(t.propTypes).map(([r,o])=>{let a=s[r];if(!a)throw new Error(`Missing property ${r}`);return Nl(e,[a],[o])?.[0]??a})}function Pd(e){let t=((...s)=>e.normalImpl(...s));return es(t,e.name),t.toString=()=>e.name,t[mo]={get strictSignature(){},call(s,r){let o=e.argTypes(...r.map(l=>ta(l.dataType)&&l.dataType.implicit?l.dataType.inner:l.dataType)),a=r.map((l,c)=>{let d=o[c];if(!d)throw new Error(\"Function called with invalid arguments\");return Ga(s,l,d,!1)}),i;if(a.every(l=>qr(l))){s.pushMode(new Ju);try{i=Je(e.normalImpl(...a.map(l=>l.value)),e.schema(),\"constant\")}finally{s.popMode(\"normal\")}}else i=e.codegenImpl(s,a);return r.some(l=>l.possibleSideEffects)?i:gN(i)}},t}var kN=Pd({name:\"bool\",schema:()=>ys,argTypes:e=>e?[e]:[],normalImpl(e=!1){if(typeof e==\"boolean\")return e;if(typeof e==\"number\"){if(!Number.isFinite(e))throw new r_(e,ys);return!!e}throw new Error(`Invalid argument type for 'd.bool'. Got '${typeof e}', expected number or boolean`)},codegenImpl:(e,[t])=>{if(t===void 0)return e.gen.typeInstantiation(ys,[]);if(ql(t.dataType)||fm(t))return e.gen.typeInstantiation(ys,[t]);throw t.dataType===Ns?new jt(\"Unknown argument type for 'd.bool'.\"):new jr([t.dataType],[ys,M,j,n,Vr])}}),ys=Object.assign(kN,{[ve]:{},type:\"bool\"}),BN=Pd({name:\"u32\",schema:()=>M,argTypes:e=>e?[e]:[],normalImpl(e){if(e===void 0)return 0;if(typeof e==\"boolean\")return e?1:0;if(!Number.isFinite(e))throw new r_(e,M);if(!Number.isInteger(e)){let t=Math.trunc(e);return t<0?0:t>4294967295?4294967295:t}return(e&4294967295)>>>0},codegenImpl:(e,[t])=>e.gen.typeInstantiation(M,t?[t]:[])}),M=Object.assign(BN,{[ve]:{},type:\"u32\"}),zN=Pd({name:\"i32\",schema:()=>j,argTypes:e=>e?[e]:[],normalImpl(e){if(e===void 0)return 0;if(typeof e==\"boolean\")return e?1:0;if(!Number.isFinite(e))throw new r_(e,j);return e|0},codegenImpl:(e,[t])=>e.gen.typeInstantiation(j,t?[t]:[])}),vve={[ve]:{},type:\"u16\"},j=Object.assign(zN,{[ve]:{},type:\"i32\"}),RN=Pd({name:\"f32\",schema:()=>n,argTypes:e=>e?[e]:[],normalImpl(e){if(e===void 0)return 0;if(typeof e==\"boolean\")return e?1:0;if(!Number.isFinite(e))throw new r_(e,n);return Math.fround(e)},codegenImpl:(e,[t])=>e.gen.typeInstantiation(n,t?[t]:[])}),n=Object.assign(RN,{[ve]:{},type:\"f32\"}),nM=new ArrayBuffer(4),DN=new Float32Array(nM),LN=new Uint32Array(nM);function j1(e){DN[0]=e;let t=LN[0],s=t>>>31&1,r=t>>>23&255,o=t&8388607;if(r===255)return s<<15|31744|(o?512:0);if(r=r-127+15,r<=0){if(r<-10)return s<<15;let c=o|8388608,d=14-r,u=c>>>d-1&1,p=c&(1<<d-1)-1?1:0,_=c>>>d;return u&(p|_&1)&&(_+=1),s<<15|_}if(r>=31)return s<<15|31744;let a=o>>>12&1,i=o&4095?1:0,l=o>>>13;return a&(i|l&1)&&(l+=1),l===1024&&(l=0,++r,r>=31)?s<<15|31744:s<<15|r<<10|l}function $x(e){let t=e&32768?-1:1,s=e>>10&31,r=e&1023;return s===0?r?t*r*2**-24:t*0:s===31?r?NaN:t===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:t*(1+r/1024)*2**(s-15)}function UN(e){return $x(j1(e))}var ON=Pd({name:\"f16\",schema:()=>Vr,argTypes:e=>e?[e]:[],normalImpl(e){if(e===void 0)return 0;if(typeof e==\"boolean\")return e?1:0;if(!Number.isFinite(e))throw new r_(e,Vr);return UN(e)},codegenImpl:(e,[t])=>e.gen.typeInstantiation(Vr,t?[t]:[])}),Vr=Object.assign(ON,{[ve]:{},type:\"f16\"}),i_={[ve]:{},type:\"abstractInt\",toString(){return\"abstractInt\"},concretized:j},fd={[ve]:{},type:\"abstractFloat\",toString(){return\"abstractFloat\"},concretized:n};function Ai(e){return e>=2**63||e<-(2**63)?Je(e,fd,\"constant\",!1):Number.isInteger(e)?(Number.isSafeInteger(e)||rr.warn(\"precision-loss\",`The integer ${e} exceeds the safe integer range and may have lost precision.`),Je(e,i_,\"constant\",!1)):Je(e,fd,\"constant\",!1)}function dn(e){return e.type===\"abstractFloat\"?n:e.type===\"abstractInt\"?j:e}function GN(e){return Yx(dn(e.dataType),e)}function Ya(e){if(n_(e))return e;if(rM(e))throw new Error(\"Cannot use refs (d.ref(...)) from the outer scope.\");let t=bh(e);return t||(Jo(e)||Kn(e)?Je(e,gh(e),\"constant\",!1):typeof e==\"number\"?Ai(e):typeof e==\"boolean\"?Je(e,ys,\"constant\",!1):Je(e,Ns,\"constant\",!1))}var Sm=class{[ve]=!0;type;elements;constructor(e,t){this.type=e,this.elements=t}toString(){return\"ArrayExpression\"}[Ws](e){for(let t of this.elements)if(gi(t)&&!jn(t.dataType)){let s=e.resolveSnippet(t).value,r=e.resolve(dn(t.dataType)).value;throw new jt(`'${s}' reference cannot be used in an array constructor.\n-----\nTry '${r}(${s})' or 'arrayOf(${r}, count)([...])' to copy the value instead.\n-----`)}return e.gen.typeInstantiation(this.type,this.elements)}};function Tn(e){let t=((...s)=>e(...s));return t.toString=()=>\"comptime\",t[Mo]=e,t[mo]={call(s,r){if(!r.every(o=>qr(o)))throw new jt(`Called comptime function with runtime-known values: ${r.filter(o=>!qr(o)).map(o=>`'${o.value}'`).join(\", \")}`);s.pushMode(new Ju);try{return Ya(e(...r.map(o=>o.value)))}finally{s.popMode()}}},t.$name=s=>(es(e,s),t),Object.defineProperty(t,ve,{value:{isComptime:!0}}),t}var vo=class extends Error{constructor(e){super(e),this.name=this.constructor.name}};function je(e){let t=((...s)=>{if(typeof e.normalImpl==\"string\")throw new vo(e.normalImpl);return e.normalImpl(...s)});return e.name&&es(t,e.name),t.toString=()=>e.name??\"<unknown>\",t[mo]={get strictSignature(){return typeof e.signature!=\"function\"?e.signature:void 0},call(s,r){let{argTypes:o,returnType:a}=typeof e.signature==\"function\"?e.signature(...r.map(d=>ta(d.dataType)&&d.dataType.implicit?d.dataType.inner:d.dataType)):e.signature,i=r.map((d,u)=>{let p=o[u];if(!p)throw new Error(\"Function called with invalid arguments\");return Ga(s,d,p,!e.ignoreImplicitCastWarning)});if(!e.noComptime&&i.every(d=>qr(d))&&typeof e.normalImpl==\"function\"){s.pushMode(new Ju);try{return Je(e.normalImpl(...i.map(d=>d.value)),a,\"constant\",e.sideEffects)}catch(d){if(!(d instanceof vo))throw d}finally{s.popMode(\"normal\")}}let l=e.sideEffects||r.some(d=>d.possibleSideEffects),c=dn(a);return Je(e.codegenImpl(s,i,c),c,\"runtime\",l)}},t}var $i=[\"x\",\"y\",\"z\",\"w\"],Xi=[\"r\",\"g\",\"b\",\"a\"],yi=class Nc extends Array{static#e=(()=>{for(let t=0;t<4;t++)for(let s=0;s<4;s++)for(let r=0;r<4;r++)for(let o=0;o<4;o++){let a=`${$i[t]}${$i[s]}${$i[r]}${$i[o]}`,i=`${Xi[t]}${Xi[s]}${Xi[r]}${Xi[o]}`;Object.defineProperty(Nc.prototype,a,{get(){return new this._Vec4(this[t],this[s],this[r],this[o])}}),Object.defineProperty(Nc.prototype,i,{get(){return new this._Vec4(this[t],this[s],this[r],this[o])}})}for(let t=0;t<4;t++)for(let s=0;s<4;s++)for(let r=0;r<4;r++){let o=`${$i[t]}${$i[s]}${$i[r]}`,a=`${Xi[t]}${Xi[s]}${Xi[r]}`;Object.defineProperty(Nc.prototype,o,{get(){return new this._Vec3(this[t],this[s],this[r])}}),Object.defineProperty(Nc.prototype,a,{get(){return new this._Vec3(this[t],this[s],this[r])}})}for(let t=0;t<4;t++)for(let s=0;s<4;s++){let r=`${$i[t]}${$i[s]}`,o=`${Xi[t]}${Xi[s]}`;Object.defineProperty(Nc.prototype,r,{get(){return new this._Vec2(this[t],this[s])}}),Object.defineProperty(Nc.prototype,o,{get(){return new this._Vec2(this[t],this[s])}})}})();castElement(){return this[ve].elementSchema}[Ws](t){let s=gh(this);return this.every(r=>!r)?t.gen.typeInstantiation(s,[]):this.every(r=>this[0]===r)?t.gen.typeInstantiation(s,[Ai(this[0])]):t.gen.typeInstantiation(s,this.map(r=>Ai(r)))}toString(){return`${this.kind}(${this.join(\", \")})`}},l_=class extends yi{e0;e1;constructor(e,t){super(2),this.e0=this.castElement()(e),this.e1=this.castElement()(t??e)}get 0(){return this.e0}get 1(){return this.e1}set 0(e){this.e0=this.castElement()(e)}set 1(e){this.e1=this.castElement()(e)}get x(){return this[0]}get y(){return this[1]}set x(e){this[0]=this.castElement()(e)}set y(e){this[1]=this.castElement()(e)}get r(){return this[0]}get g(){return this[1]}set r(e){this[0]=this.castElement()(e)}set g(e){this[1]=this.castElement()(e)}},c_=class extends yi{e0;e1;e2;constructor(e,t,s){super(3),this.e0=this.castElement()(e),this.e1=this.castElement()(t??e),this.e2=this.castElement()(s??e)}get 0(){return this.e0}get 1(){return this.e1}get 2(){return this.e2}set 0(e){this.e0=this.castElement()(e)}set 1(e){this.e1=this.castElement()(e)}set 2(e){this.e2=this.castElement()(e)}get x(){return this[0]}get y(){return this[1]}get z(){return this[2]}set x(e){this[0]=this.castElement()(e)}set y(e){this[1]=this.castElement()(e)}set z(e){this[2]=this.castElement()(e)}get r(){return this[0]}get g(){return this[1]}get b(){return this[2]}set r(e){this[0]=this.castElement()(e)}set g(e){this[1]=this.castElement()(e)}set b(e){this[2]=this.castElement()(e)}},d_=class extends yi{e0;e1;e2;e3;constructor(e,t,s,r){super(4),this.e0=this.castElement()(e),this.e1=this.castElement()(t??e),this.e2=this.castElement()(s??e),this.e3=this.castElement()(r??e)}get 0(){return this.e0}get 1(){return this.e1}get 2(){return this.e2}get 3(){return this.e3}set 0(e){this.e0=this.castElement()(e)}set 1(e){this.e1=this.castElement()(e)}set 2(e){this.e2=this.castElement()(e)}set 3(e){this.e3=this.castElement()(e)}get x(){return this[0]}get y(){return this[1]}get z(){return this[2]}get w(){return this[3]}set x(e){this[0]=e}set y(e){this[1]=e}set z(e){this[2]=e}set w(e){this[3]=e}get r(){return this[0]}get g(){return this[1]}get b(){return this[2]}get a(){return this[3]}set r(e){this[0]=e}set g(e){this[1]=e}set b(e){this[2]=e}set a(e){this[3]=e}},Xx=class iM extends l_{get[ve](){return{elementSchema:n}}get kind(){return\"vec2f\"}get _Vec2(){return iM}get _Vec3(){return Zx}get _Vec4(){return r3}},Hx=class lM extends l_{get[ve](){return{elementSchema:Vr}}get kind(){return\"vec2h\"}get _Vec2(){return lM}get _Vec3(){return Jx}get _Vec4(){return o3}},jx=class cM extends l_{get[ve](){return{elementSchema:j}}get kind(){return\"vec2i\"}get _Vec2(){return cM}get _Vec3(){return e3}get _Vec4(){return a3}},Kx=class dM extends l_{get[ve](){return{elementSchema:M}}get kind(){return\"vec2u\"}get _Vec2(){return dM}get _Vec3(){return t3}get _Vec4(){return n3}},Qx=class uM extends l_{get[ve](){return{elementSchema:ys}}get kind(){return\"vec2<bool>\"}get _Vec2(){return uM}get _Vec3(){return s3}get _Vec4(){return i3}},Zx=class pM extends c_{get[ve](){return{elementSchema:n}}get kind(){return\"vec3f\"}get _Vec2(){return Xx}get _Vec3(){return pM}get _Vec4(){return r3}},Jx=class mM extends c_{get[ve](){return{elementSchema:Vr}}get kind(){return\"vec3h\"}get _Vec2(){return Hx}get _Vec3(){return mM}get _Vec4(){return o3}},e3=class _M extends c_{get[ve](){return{elementSchema:j}}get kind(){return\"vec3i\"}get _Vec2(){return jx}get _Vec3(){return _M}get _Vec4(){return a3}},t3=class fM extends c_{get[ve](){return{elementSchema:M}}get kind(){return\"vec3u\"}get _Vec2(){return Kx}get _Vec3(){return fM}get _Vec4(){return n3}},s3=class vM extends c_{get[ve](){return{elementSchema:ys}}get kind(){return\"vec3<bool>\"}get _Vec2(){return Qx}get _Vec3(){return vM}get _Vec4(){return i3}},r3=class hM extends d_{get[ve](){return{elementSchema:n}}get kind(){return\"vec4f\"}get _Vec2(){return Xx}get _Vec3(){return Zx}get _Vec4(){return hM}},o3=class gM extends d_{get[ve](){return{elementSchema:Vr}}get kind(){return\"vec4h\"}get _Vec2(){return Hx}get _Vec3(){return Jx}get _Vec4(){return gM}},a3=class yM extends d_{get[ve](){return{elementSchema:j}}get kind(){return\"vec4i\"}get _Vec2(){return jx}get _Vec3(){return e3}get _Vec4(){return yM}},n3=class bM extends d_{get[ve](){return{elementSchema:M}}get kind(){return\"vec4u\"}get _Vec2(){return Kx}get _Vec3(){return t3}get _Vec4(){return bM}},i3=class xM extends d_{get[ve](){return{elementSchema:ys}}get kind(){return\"vec4<bool>\"}get _Vec2(){return Qx}get _Vec3(){return s3}get _Vec4(){return xM}},ps=Ca(Xx,n),To=Ca(Hx,Vr),ir=Ca(jx,j),$s=Ca(Kx,M),Wa=Ca(Qx,ys),Zs=Ca(Zx,n),Pa=Ca(Jx,Vr),Br=Ca(e3,j),Ps=Ca(t3,M),qa=Ca(s3,ys),qt=Ca(r3,n),So=Ca(o3,Vr),sr=Ca(a3,j),Is=Ca(n3,M),Va=Ca(i3,ys),FN={vec2f:ps,vec2h:To,vec2i:ir,vec2u:$s,\"vec2<bool>\":Wa,vec3f:Zs,vec3h:Pa,vec3i:Br,vec3u:Ps,\"vec3<bool>\":qa,vec4f:qt,vec4h:So,vec4i:sr,vec4u:Is,\"vec4<bool>\":Va};function Ca(e,t){let{kind:s,length:r}=new e,o=r,i=Pd({name:s,schema:()=>l,argTypes:(...c)=>c.map(d=>js(d)?d:t),normalImpl:(...c)=>{let d=Array.from({length:c.length}),u=0;for(let p of c)if(typeof p==\"number\"||typeof p==\"boolean\")d[u++]=p;else for(let _=0;_<p.length;++_)d[u++]=p[_];if(d.length<=1||d.length===o)return new e(...d);throw new Error(`'${s}' constructor called with invalid number of arguments.`)},codegenImpl:(c,d)=>c.gen.typeInstantiation(l,d)}),l=Object.assign(i,{[ve]:{},type:s,primitive:t,componentCount:o,[Ku]:void 0});return e.prototype.schema=l,l}var bu=class{};function l3(e){let t=Pd({name:e.type,schema:()=>s,normalImpl:(...r)=>{let o=[];for(let a of r)if(typeof a==\"number\")o.push(a);else for(let i=0;i<a.length;++i)o.push(a[i]);if(o.length!==0&&o.length!==e.columns*e.rows)throw new Error(`'${e.type}' constructor called with invalid number of arguments.`);for(let a=o.length;a<e.columns*e.rows;++a)o.push(0);return new e.MatImpl(...o)},argTypes:(...r)=>r.map(o=>js(o)?o:n),codegenImpl:(r,o)=>r.gen.typeInstantiation(s,o)}),s=Object.assign(t,{[ve]:{},type:e.type,primitive:n,identity:jN[e.columns],translation:e.columns===4?KN:void 0,scaling:e.columns===4?QN:void 0,rotationX:e.columns===4?ZN:void 0,rotationY:e.columns===4?JN:void 0,rotationZ:e.columns===4?e$:void 0});return e.MatImpl.prototype.schema=s,s}var zw=[0,1,2,3],IN=class extends bu{[ve]=!0;columns;length=4;constructor(...e){super(),this.columns=[this.makeColumn(e[0],e[1]),this.makeColumn(e[2],e[3])]}get 0(){return this.columns[0].x}get 1(){return this.columns[0].y}get 2(){return this.columns[1].x}get 3(){return this.columns[1].y}set 0(e){this.columns[0].x=e}set 1(e){this.columns[0].y=e}set 2(e){this.columns[1].x=e}set 3(e){this.columns[1].y=e}*[Symbol.iterator](){yield this[0],yield this[1],yield this[2],yield this[3]}[Ws](e){return e.gen.typeInstantiation(yl,zw.map(t=>Ai(this[t])))}toString(){return`${this.kind}(${zw.map(e=>this[e]).join(\", \")})`}},YN=class extends IN{kind=\"mat2x2f\";makeColumn(e,t){return ps(e,t)}},Rw=[0,1,2,4,5,6,8,9,10],WN=class extends bu{[ve]=!0;columns;length=12;constructor(...e){super(),this.columns=[this.makeColumn(e[0],e[1],e[2]),this.makeColumn(e[3],e[4],e[5]),this.makeColumn(e[6],e[7],e[8])]}get 0(){return this.columns[0].x}get 1(){return this.columns[0].y}get 2(){return this.columns[0].z}get 3(){return 0}get 4(){return this.columns[1].x}get 5(){return this.columns[1].y}get 6(){return this.columns[1].z}get 7(){return 0}get 8(){return this.columns[2].x}get 9(){return this.columns[2].y}get 10(){return this.columns[2].z}get 11(){return 0}set 0(e){this.columns[0].x=e}set 1(e){this.columns[0].y=e}set 2(e){this.columns[0].z=e}set 3(e){}set 4(e){this.columns[1].x=e}set 5(e){this.columns[1].y=e}set 6(e){this.columns[1].z=e}set 7(e){}set 8(e){this.columns[2].x=e}set 9(e){this.columns[2].y=e}set 10(e){this.columns[2].z=e}set 11(e){}*[Symbol.iterator](){for(let e=0;e<12;e++)yield this[e]}[Ws](e){return e.gen.typeInstantiation(bl,Rw.map(t=>Ai(this[t])))}toString(){return`${this.kind}(${Rw.map(e=>this[e]).join(\", \")})`}},qN=class extends WN{kind=\"mat3x3f\";makeColumn(e,t,s){return Zs(e,t,s)}},Dw=Array.from({length:16},(e,t)=>t),VN=class extends bu{[ve]=!0;columns;constructor(...e){super(),this.columns=[this.makeColumn(e[0],e[1],e[2],e[3]),this.makeColumn(e[4],e[5],e[6],e[7]),this.makeColumn(e[8],e[9],e[10],e[11]),this.makeColumn(e[12],e[13],e[14],e[15])]}length=16;get 0(){return this.columns[0].x}get 1(){return this.columns[0].y}get 2(){return this.columns[0].z}get 3(){return this.columns[0].w}get 4(){return this.columns[1].x}get 5(){return this.columns[1].y}get 6(){return this.columns[1].z}get 7(){return this.columns[1].w}get 8(){return this.columns[2].x}get 9(){return this.columns[2].y}get 10(){return this.columns[2].z}get 11(){return this.columns[2].w}get 12(){return this.columns[3].x}get 13(){return this.columns[3].y}get 14(){return this.columns[3].z}get 15(){return this.columns[3].w}set 0(e){this.columns[0].x=e}set 1(e){this.columns[0].y=e}set 2(e){this.columns[0].z=e}set 3(e){this.columns[0].w=e}set 4(e){this.columns[1].x=e}set 5(e){this.columns[1].y=e}set 6(e){this.columns[1].z=e}set 7(e){this.columns[1].w=e}set 8(e){this.columns[2].x=e}set 9(e){this.columns[2].y=e}set 10(e){this.columns[2].z=e}set 11(e){this.columns[2].w=e}set 12(e){this.columns[3].x=e}set 13(e){this.columns[3].y=e}set 14(e){this.columns[3].z=e}set 15(e){this.columns[3].w=e}*[Symbol.iterator](){for(let e=0;e<16;e++)yield this[e]}[Ws](e){return e.gen.typeInstantiation(Eo,Dw.map(t=>Ai(this[t])))}toString(){return`${this.kind}(${Dw.map(e=>this[e]).join(\", \")})`}},NN=class extends VN{kind=\"mat4x4f\";makeColumn(e,t,s,r){return qt(e,t,s,r)}},$N=Tn(()=>yl(1,0,0,1)).$name(\"identity2\"),XN=Tn(()=>bl(1,0,0,0,1,0,0,0,1)).$name(\"identity3\"),HN=Tn(()=>Eo(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)).$name(\"identity4\"),jN={2:$N,3:XN,4:HN},KN=je({name:\"translation4\",normalImpl:e=>Eo(1,0,0,0,0,1,0,0,0,0,1,0,e.x,e.y,e.z,1),get signature(){return{argTypes:[Zs],returnType:Eo}},codegenImpl:(e,[t])=>Ne`mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ${t}.x, ${t}.y, ${t}.z, 1)`,sideEffects:!1}),QN=je({name:\"scaling4\",normalImpl:e=>Eo(e.x,0,0,0,0,e.y,0,0,0,0,e.z,0,0,0,0,1),get signature(){return{argTypes:[Zs],returnType:Eo}},codegenImpl:(e,[t])=>Ne`mat4x4f(${t}.x, 0, 0, 0, 0, ${t}.y, 0, 0, 0, 0, ${t}.z, 0, 0, 0, 0, 1)`,sideEffects:!1}),ZN=je({name:\"rotationX4\",normalImpl:e=>Eo(1,0,0,0,0,Math.cos(e),Math.sin(e),0,0,-Math.sin(e),Math.cos(e),0,0,0,0,1),get signature(){return{argTypes:[n],returnType:Eo}},codegenImpl:(e,[t])=>Ne`mat4x4f(1, 0, 0, 0, 0, cos(${t}), sin(${t}), 0, 0, -sin(${t}), cos(${t}), 0, 0, 0, 0, 1)`,sideEffects:!1}),JN=je({name:\"rotationY4\",normalImpl:e=>Eo(Math.cos(e),0,-Math.sin(e),0,0,1,0,0,Math.sin(e),0,Math.cos(e),0,0,0,0,1),get signature(){return{argTypes:[n],returnType:Eo}},codegenImpl:(e,[t])=>Ne`mat4x4f(cos(${t}), 0, -sin(${t}), 0, 0, 1, 0, 0, sin(${t}), 0, cos(${t}), 0, 0, 0, 0, 1)`,sideEffects:!1}),e$=je({name:\"rotationZ4\",normalImpl:e=>Eo(Math.cos(e),Math.sin(e),0,0,-Math.sin(e),Math.cos(e),0,0,0,0,1,0,0,0,0,1),get signature(){return{argTypes:[n],returnType:Eo}},codegenImpl:(e,[t])=>Ne`mat4x4f(cos(${t}), sin(${t}), 0, 0, -sin(${t}), cos(${t}), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)`,sideEffects:!1}),yl=l3({type:\"mat2x2f\",rows:2,columns:2,MatImpl:YN}),bl=l3({type:\"mat3x3f\",rows:3,columns:3,MatImpl:qN}),Eo=l3({type:\"mat4x4f\",rows:4,columns:4,MatImpl:NN});var t$=(e,t,s)=>{if(e===t)return 0;let r=AM((s-e)/(t-e),0,1);return r*r*(3-2*r)},AM=(e,t,s)=>Math.min(Math.max(t,e),s),c3=new ArrayBuffer(4),wM=new Float32Array(c3),d3=new Uint32Array(c3),s$=new Int32Array(c3);function fi(e){return d3[0]=e,wM[0]}function vi(e){return d3[0]=e,s$[0]}function hi(e){return wM[0]=e,d3[0]}var Lw=e=>Math.sqrt(e.x**2+e.y**2),Uw=e=>Math.sqrt(e.x**2+e.y**2+e.z**2),Ow=e=>Math.sqrt(e.x**2+e.y**2+e.z**2+e.w**2),$f=(e,t)=>e.x*t.x+e.y*t.y,Xf=(e,t)=>e.x*t.x+e.y*t.y+e.z*t.z,Hf=(e,t)=>e.x*t.x+e.y*t.y+e.z*t.z+e.w*t.w,Gw=e=>(t,s)=>$s(e(t.x,s.x),e(t.y,s.y)),Fw=e=>(t,s)=>Ps(e(t.x,s.x),e(t.y,s.y),e(t.z,s.z)),Iw=e=>(t,s)=>Is(e(t.x,s.x),e(t.y,s.y),e(t.z,s.z),e(t.w,s.w)),Yw=e=>(t,s)=>ir(e(t.x,s.x),e(t.y,s.y)),Ww=e=>(t,s)=>Br(e(t.x,s.x),e(t.y,s.y),e(t.z,s.z)),qw=e=>(t,s)=>sr(e(t.x,s.x),e(t.y,s.y),e(t.z,s.z),e(t.w,s.w)),$a={all:{\"vec2<bool>\":e=>e.x&&e.y,\"vec3<bool>\":e=>e.x&&e.y&&e.z,\"vec4<bool>\":e=>e.x&&e.y&&e.z&&e.w},length:{vec2f:Lw,vec2h:Lw,vec3f:Uw,vec3h:Uw,vec4f:Ow,vec4h:Ow},mulMxM:{mat2x2f:(e,t)=>{let s=e.columns,r=t.columns;return yl(s[0].x*r[0].x+s[1].x*r[0].y,s[0].y*r[0].x+s[1].y*r[0].y,s[0].x*r[1].x+s[1].x*r[1].y,s[0].y*r[1].x+s[1].y*r[1].y)},mat3x3f:(e,t)=>{let s=e.columns,r=t.columns;return bl(s[0].x*r[0].x+s[1].x*r[0].y+s[2].x*r[0].z,s[0].y*r[0].x+s[1].y*r[0].y+s[2].y*r[0].z,s[0].z*r[0].x+s[1].z*r[0].y+s[2].z*r[0].z,s[0].x*r[1].x+s[1].x*r[1].y+s[2].x*r[1].z,s[0].y*r[1].x+s[1].y*r[1].y+s[2].y*r[1].z,s[0].z*r[1].x+s[1].z*r[1].y+s[2].z*r[1].z,s[0].x*r[2].x+s[1].x*r[2].y+s[2].x*r[2].z,s[0].y*r[2].x+s[1].y*r[2].y+s[2].y*r[2].z,s[0].z*r[2].x+s[1].z*r[2].y+s[2].z*r[2].z)},mat4x4f:(e,t)=>{let s=e.columns,r=t.columns;return Eo(s[0].x*r[0].x+s[1].x*r[0].y+s[2].x*r[0].z+s[3].x*r[0].w,s[0].y*r[0].x+s[1].y*r[0].y+s[2].y*r[0].z+s[3].y*r[0].w,s[0].z*r[0].x+s[1].z*r[0].y+s[2].z*r[0].z+s[3].z*r[0].w,s[0].w*r[0].x+s[1].w*r[0].y+s[2].w*r[0].z+s[3].w*r[0].w,s[0].x*r[1].x+s[1].x*r[1].y+s[2].x*r[1].z+s[3].x*r[1].w,s[0].y*r[1].x+s[1].y*r[1].y+s[2].y*r[1].z+s[3].y*r[1].w,s[0].z*r[1].x+s[1].z*r[1].y+s[2].z*r[1].z+s[3].z*r[1].w,s[0].w*r[1].x+s[1].w*r[1].y+s[2].w*r[1].z+s[3].w*r[1].w,s[0].x*r[2].x+s[1].x*r[2].y+s[2].x*r[2].z+s[3].x*r[2].w,s[0].y*r[2].x+s[1].y*r[2].y+s[2].y*r[2].z+s[3].y*r[2].w,s[0].z*r[2].x+s[1].z*r[2].y+s[2].z*r[2].z+s[3].z*r[2].w,s[0].w*r[2].x+s[1].w*r[2].y+s[2].w*r[2].z+s[3].w*r[2].w,s[0].x*r[3].x+s[1].x*r[3].y+s[2].x*r[3].z+s[3].x*r[3].w,s[0].y*r[3].x+s[1].y*r[3].y+s[2].y*r[3].z+s[3].y*r[3].w,s[0].z*r[3].x+s[1].z*r[3].y+s[2].z*r[3].z+s[3].z*r[3].w,s[0].w*r[3].x+s[1].w*r[3].y+s[2].w*r[3].z+s[3].w*r[3].w)}},mulMxV:{mat2x2f:(e,t)=>{let s=e.columns;return ps(s[0].x*t.x+s[1].x*t.y,s[0].y*t.x+s[1].y*t.y)},mat3x3f:(e,t)=>{let s=e.columns;return Zs(s[0].x*t.x+s[1].x*t.y+s[2].x*t.z,s[0].y*t.x+s[1].y*t.y+s[2].y*t.z,s[0].z*t.x+s[1].z*t.y+s[2].z*t.z)},mat4x4f:(e,t)=>{let s=e.columns;return qt(s[0].x*t.x+s[1].x*t.y+s[2].x*t.z+s[3].x*t.w,s[0].y*t.x+s[1].y*t.y+s[2].y*t.z+s[3].y*t.w,s[0].z*t.x+s[1].z*t.y+s[2].z*t.z+s[3].z*t.w,s[0].w*t.x+s[1].w*t.y+s[2].w*t.z+s[3].w*t.w)}},mulVxM:{mat2x2f:(e,t)=>{let s=t.columns;return ps(e.x*s[0].x+e.y*s[0].y,e.x*s[1].x+e.y*s[1].y)},mat3x3f:(e,t)=>{let s=t.columns;return Zs(e.x*s[0].x+e.y*s[0].y+e.z*s[0].z,e.x*s[1].x+e.y*s[1].y+e.z*s[1].z,e.x*s[2].x+e.y*s[2].y+e.z*s[2].z)},mat4x4f:(e,t)=>{let s=t.columns;return qt(e.x*s[0].x+e.y*s[0].y+e.z*s[0].z+e.w*s[0].w,e.x*s[1].x+e.y*s[1].y+e.z*s[1].z+e.w*s[1].w,e.x*s[2].x+e.y*s[2].y+e.z*s[2].z+e.w*s[2].w,e.x*s[3].x+e.y*s[3].y+e.z*s[3].z+e.w*s[3].w)}},dot:{vec2f:$f,vec2h:$f,vec2i:$f,vec2u:$f,vec3f:Xf,vec3h:Xf,vec3i:Xf,vec3u:Xf,vec4f:Hf,vec4h:Hf,vec4i:Hf,vec4u:Hf},cross:{vec3f:(e,t)=>Zs(e.y*t.z-e.z*t.y,e.z*t.x-e.x*t.z,e.x*t.y-e.y*t.x),vec3h:(e,t)=>Pa(e.y*t.z-e.z*t.y,e.z*t.x-e.x*t.z,e.x*t.y-e.y*t.x)},bitShiftLeft:{vec2i:Yw((e,t)=>e<<t),vec2u:Gw((e,t)=>e<<t),vec3i:Ww((e,t)=>e<<t),vec3u:Fw((e,t)=>e<<t),vec4i:qw((e,t)=>e<<t),vec4u:Iw((e,t)=>e<<t)},bitShiftRight:{vec2i:Yw((e,t)=>e>>t),vec2u:Gw((e,t)=>e>>>t),vec3i:Ww((e,t)=>e>>t),vec3u:Fw((e,t)=>e>>>t),vec4i:qw((e,t)=>e>>t),vec4u:Iw((e,t)=>e>>>t)},bitcastU32toF32:{vec2u:e=>ps(fi(e.x),fi(e.y)),vec3u:e=>Zs(fi(e.x),fi(e.y),fi(e.z)),vec4u:e=>qt(fi(e.x),fi(e.y),fi(e.z),fi(e.w))},bitcastU32toI32:{vec2u:e=>ir(vi(e.x),vi(e.y)),vec3u:e=>Br(vi(e.x),vi(e.y),vi(e.z)),vec4u:e=>sr(vi(e.x),vi(e.y),vi(e.z),vi(e.w))},bitcastF32toU32:{vec2f:e=>$s(hi(e.x),hi(e.y)),vec3f:e=>Ps(hi(e.x),hi(e.y),hi(e.z)),vec4f:e=>Is(hi(e.x),hi(e.y),hi(e.z),hi(e.w))}},r$={vec2f:Wa,vec2h:Wa,vec2i:Wa,vec2u:Wa,\"vec2<bool>\":Wa,vec3f:qa,vec3h:qa,vec3i:qa,vec3u:qa,\"vec3<bool>\":qa,vec4f:Va,vec4h:Va,vec4i:Va,vec4u:Va,\"vec4<bool>\":Va},lb={...FN,mat2x2f:yl,mat3x3f:bl,mat4x4f:Eo};function o$(e,t){let s=e===\"boolean\"?r$:lb;if(t in s)return s[t];throw new Error(`No corresponding vector/matrix type for '${t}' kind in '${e}' mode.`)}function a$(e){return e.kind.startsWith(\"vec\")?e:e.columns.flat()}function TM(e,t,s){let r=t.map(n$);if(r.every(c=>c===\"boolean\"||c===\"number\"))return e(...t);let o=r[0];kr(o,\"Expected kind of the first argument to be present.\");let a=o$(s,o),i=t.map(a$),l=i[0]?.length;return kr(l!==void 0,\"Expected constructor to have at least one argument.\"),a(...Array.from({length:l},(c,d)=>e(...i.map(u=>u[d]))))}function Bs(e,t){return TM(e,t,\"first\")}function Th(e,t){return TM(e,t,\"boolean\")}function n$(e){return typeof e==\"number\"?\"number\":typeof e==\"boolean\"?\"boolean\":e.kind}function u3(e){let[t,s]=e;if(typeof t==\"number\"&&Jo(s)){let r=lb[s.kind];return[r(t),s]}else if(Jo(t)&&typeof s==\"number\"){let r=lb[t.kind];return[t,r(s)]}return[t,s]}var Vw=e=>\"primitive\"in e?e.primitive:e,p3=e=>(t,s)=>{let{restrict:r}=e??{},o=u=>{throw r?new jr([t,s],r):new Error(`Cannot apply operator to ${t.type} and ${s.type}: ${u}`)};if(e?.noMat&&(xi(t)||xi(s)))return o(\"matrices not supported\");let a=js(t)||xi(t),i=js(s)||xi(s);if(!a&&!i){let u=sa([t,s],r);return u?{argTypes:u,returnType:u[0]}:o(\"incompatible scalar types\")}if(a&&i)return e?.matVecProduct&&js(t)!==js(s)?{argTypes:[t,s],returnType:js(t)?t:s}:t.type!==s.type?o(\"operands must have the same type\"):{argTypes:[t,s],returnType:t};let[l,c]=a?[s,t]:[t,s],d=sa([l],[Vw(c)]);return d?{argTypes:a?[t,d[0]]:[d[0],s],returnType:c}:o(`scalar not convertible to ${Vw(c).type}`)},SM=p3(),i$=p3({matVecProduct:!0}),PM=p3({noMat:!0,restrict:[n,Vr,fd]});function CM(e,t){if(typeof e==\"number\"&&typeof t==\"number\")return e+t;if(typeof e==\"number\"&&Jo(t))return Bs(s=>e+s,[t]);if(Jo(e)&&typeof t==\"number\")return Bs(s=>s+t,[e]);if(Jo(e)&&Jo(t)||Kn(e)&&Kn(t))return Bs((s,r)=>s+r,[e,t]);throw new Error(\"Add/Sub called with invalid arguments.\")}var al=je({name:\"add\",signature:SM,normalImpl:CM,codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"+\",s),sideEffects:!1});function l$(e,t){return CM(e,MM(-1,t))}var As=je({name:\"sub\",signature:SM,normalImpl:l$,codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"-\",s),sideEffects:!1});function MM(e,t){if(typeof e==\"number\"&&typeof t==\"number\")return e*t;if(typeof e==\"number\"&&(Jo(t)||Kn(t)))return Bs(s=>e*s,[t]);if((Jo(e)||Kn(e))&&typeof t==\"number\")return Bs(s=>s*t,[e]);if(Jo(e)&&Jo(t))return Bs((s,r)=>s*r,[e,t]);if(Sw(e)&&Kn(t))return $a.mulVxM[t.kind](e,t);if(Kn(e)&&Sw(t))return $a.mulMxV[e.kind](e,t);if(Kn(e)&&Kn(t))return $a.mulMxM[e.kind](e,t);throw new Error(\"Mul called with invalid arguments.\")}var ra=je({name:\"mul\",signature:i$,normalImpl:MM,codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"*\",s),sideEffects:!1});function c$(e,t){return Bs((s,r)=>s/r,u3([e,t]))}var cb=je({name:\"div\",signature:PM,normalImpl:c$,codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"/\",s),ignoreImplicitCastWarning:!0,sideEffects:!1}),An=je({name:\"mod\",signature:PM,normalImpl:((e,t)=>Bs((s,r)=>s%r,u3([e,t]))),codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"%\",s),sideEffects:!1});function d$(e){return Bs(t=>-t,[e])}var Nw=je({name:\"neg\",signature:e=>({argTypes:[e],returnType:e}),normalImpl:d$,codegenImpl:(e,[t])=>Ne`-(${t})`,sideEffects:!1}),EM={vec2i:$s,vec2u:$s,vec3i:Ps,vec3u:Ps,vec4i:Is,vec4u:Is},kM=(e,t)=>{let s=sa([e],[ir,Br,sr,$s,Ps,Is])?.[0];if(!s||!js(s))throw new jr([e],[ir,Br,sr,$s,Ps,Is]);let r=s.componentCount,o=r===2?$s:r===3?Ps:Is,a=sa([t],[M,o])?.[0];if(!a)throw new jr([t],[M,o]);return{argTypes:[s,a],returnType:s}};function u$(e,t){if(q1(e)&&kC(t)&&e.length==t.length)return $a.bitShiftLeft[e.kind](e,t);if(q1(e)&&typeof t==\"number\"){let s=EM[e.kind](t);return $a.bitShiftLeft[e.kind](e,s)}throw new Error(\"'bitShiftLeft' called with invalid arguments, expected: left-hand side to be an integer vector, right-hand side to be a number or unsigned integer vector of the same arity as the left-hand side.\")}var p$=je({name:\"bitShiftLeft\",signature:kM,normalImpl:u$,codegenImpl:(e,[t,s])=>{if(js(t.dataType)&&!js(s.dataType)){let r=t.dataType.componentCount,o=r===2?$s:r===3?Ps:Is;return e.gen.emitBinaryOp(t,\"<<\",e.gen.typeInstantiation(o,[s]))}return e.gen.emitBinaryOp(t,\"<<\",s)},sideEffects:!1});function m$(e,t){if(q1(e)&&kC(t)&&e.length==t.length)return $a.bitShiftRight[e.kind](e,t);if(q1(e)&&typeof t==\"number\"){let s=EM[e.kind](t);return $a.bitShiftRight[e.kind](e,s)}throw new Error(\"'bitShiftRight' called with invalid arguments, expected: left-hand side to be an integer vector, right-hand side to be a number or unsigned integer vector of the same arity as the left-hand side.\")}var db={add:al,sub:As,mul:ra,div:cb,mod:An,bitShiftLeft:p$,bitShiftRight:je({name:\"bitShiftRight\",signature:kM,normalImpl:m$,codegenImpl:(e,[t,s])=>{if(js(t.dataType)&&!js(s.dataType)){let r=t.dataType.componentCount,o=r===2?$s:r===3?Ps:Is;return e.gen.emitBinaryOp(t,\">>\",e.gen.typeInstantiation(o,[s]))}return e.gen.emitBinaryOp(t,\">>\",s)},sideEffects:!1})},m3=class{lhs;operator;constructor(e,t){this.lhs=e,this.operator=t}};function _$(e){return e instanceof m3}function mi(e,t,s){let r=db[t];Object.defineProperty(e.prototype,s,{value:r});function o(a){return r(this,a)}Object.defineProperty(e.prototype,t,{get(){return xh()?new m3(this,r):o}})}var $w=!1;function qo(){$w||($w=!0,mi(yi,\"add\",jo.plus),mi(bu,\"add\",jo.plus),mi(yi,\"sub\",jo.minus),mi(bu,\"sub\",jo.minus),mi(yi,\"mul\",jo.star),mi(bu,\"mul\",jo.star),mi(yi,\"div\",jo.slash),mi(yi,\"mod\",jo.percent),mi(yi,\"bitShiftLeft\",Symbol()),mi(yi,\"bitShiftRight\",Symbol()))}var _3=new Set([\"alias\",\"break\",\"case\",\"const\",\"const_assert\",\"continue\",\"continuing\",\"default\",\"diagnostic\",\"discard\",\"else\",\"enable\",\"false\",\"fn\",\"for\",\"if\",\"let\",\"loop\",\"override\",\"requires\",\"return\",\"struct\",\"switch\",\"true\",\"var\",\"while\",\"NULL\",\"Self\",\"abstract\",\"active\",\"alignas\",\"alignof\",\"as\",\"asm\",\"asm_fragment\",\"async\",\"attribute\",\"auto\",\"await\",\"become\",\"cast\",\"catch\",\"class\",\"co_await\",\"co_return\",\"co_yield\",\"coherent\",\"column_major\",\"common\",\"compile\",\"compile_fragment\",\"concept\",\"const_cast\",\"consteval\",\"constexpr\",\"constinit\",\"crate\",\"debugger\",\"decltype\",\"delete\",\"demote\",\"demote_to_helper\",\"do\",\"dynamic_cast\",\"enum\",\"explicit\",\"export\",\"extends\",\"extern\",\"external\",\"fallthrough\",\"filter\",\"final\",\"finally\",\"friend\",\"from\",\"fxgroup\",\"get\",\"goto\",\"groupshared\",\"highp\",\"impl\",\"implements\",\"import\",\"inline\",\"instanceof\",\"interface\",\"layout\",\"lowp\",\"macro\",\"macro_rules\",\"match\",\"mediump\",\"meta\",\"mod\",\"module\",\"move\",\"mut\",\"mutable\",\"namespace\",\"new\",\"nil\",\"noexcept\",\"noinline\",\"nointerpolation\",\"non_coherent\",\"noncoherent\",\"noperspective\",\"null\",\"nullptr\",\"of\",\"operator\",\"package\",\"packoffset\",\"partition\",\"pass\",\"patch\",\"pixelfragment\",\"precise\",\"precision\",\"premerge\",\"priv\",\"protected\",\"pub\",\"public\",\"readonly\",\"ref\",\"regardless\",\"register\",\"reinterpret_cast\",\"require\",\"resource\",\"restrict\",\"self\",\"set\",\"shared\",\"sizeof\",\"smooth\",\"snorm\",\"static\",\"static_assert\",\"static_cast\",\"std\",\"subroutine\",\"super\",\"target\",\"template\",\"this\",\"thread_local\",\"throw\",\"trait\",\"try\",\"type\",\"typedef\",\"typeid\",\"typename\",\"typeof\",\"union\",\"unless\",\"unorm\",\"unsafe\",\"unsized\",\"use\",\"using\",\"varying\",\"virtual\",\"volatile\",\"wgsl\",\"where\",\"with\",\"writeonly\",\"yield\",\"sampler\",\"uniform\",\"storage\"]),f$=new Set([\"array\",\"bool\",\"f16\",\"f32\",\"i32\",\"u32\",\"mat2x2\",\"mat2x3\",\"mat2x4\",\"mat3x2\",\"mat3x3\",\"mat3x4\",\"mat4x2\",\"mat4x3\",\"mat4x4\",\"vec2\",\"vec3\",\"vec4\",\"bitcast\",\"all\",\"any\",\"select\",\"arrayLength\",\"abs\",\"acos\",\"acosh\",\"asin\",\"asinh\",\"atan\",\"atanh\",\"atan2\",\"ceil\",\"clamp\",\"cos\",\"cosh\",\"countLeadingZeros\",\"countOneBits\",\"countTrailingZeros\",\"cross\",\"degrees\",\"determinant\",\"distance\",\"dot\",\"dot4U8Packed\",\"dot4I8Packed\",\"exp\",\"exp2\",\"extractBits\",\"faceForward\",\"firstLeadingBit\",\"firstTrailingBit\",\"floor\",\"fma\",\"fract\",\"frexp\",\"insertBits\",\"inverseSqrt\",\"ldexp\",\"length\",\"log\",\"log2\",\"max\",\"min\",\"mix\",\"modf\",\"normalize\",\"pow\",\"quantizeToF16\",\"radians\",\"reflect\",\"refract\",\"reverseBits\",\"round\",\"saturate\",\"sign\",\"sin\",\"sinh\",\"smoothstep\",\"sqrt\",\"step\",\"tan\",\"tanh\",\"transpose\",\"trunc\",\"dpdx\",\"dpdxCoarse\",\"dpdxFine\",\"dpdy\",\"dpdyCoarse\",\"dpdyFine\",\"fwidth\",\"fwidthCoarse\",\"fwidthFine\",\"textureDimensions\",\"textureGather\",\"textureGatherCompare\",\"textureLoad\",\"textureNumLayers\",\"textureNumLevels\",\"textureNumSamples\",\"textureSample\",\"textureSampleBias\",\"textureSampleCompare\",\"textureSampleCompareLevel\",\"textureSampleGrad\",\"textureSampleLevel\",\"textureSampleBaseClampToEdge\",\"textureStore\",\"atomicLoad\",\"atomicStore\",\"atomicAdd\",\"atomicSub\",\"atomicMax\",\"atomicMin\",\"atomicAnd\",\"atomicOr\",\"atomicXor\",\"atomicExchange\",\"atomicCompareExchangeWeak\",\"pack4x8snorm\",\"pack4x8unorm\",\"pack4xI8\",\"pack4xU8\",\"pack4xI8Clamp\",\"pack4xU8Clamp\",\"pack2x16snorm\",\"pack2x16unorm\",\"pack2x16float\",\"unpack4x8snorm\",\"unpack4x8unorm\",\"unpack4xI8\",\"unpack4xU8\",\"unpack2x16snorm\",\"unpack2x16unorm\",\"unpack2x16float\",\"storageBarrier\",\"textureBarrier\",\"workgroupBarrier\",\"workgroupUniformLoad\",\"subgroupAdd\",\"subgroupExclusiveAdd\",\"subgroupInclusiveAdd\",\"subgroupAll\",\"subgroupAnd\",\"subgroupAny\",\"subgroupBallot\",\"subgroupBroadcast\",\"subgroupBroadcastFirst\",\"subgroupElect\",\"subgroupMax\",\"subgroupMin\",\"subgroupMul\",\"subgroupExclusiveMul\",\"subgroupInclusiveMul\",\"subgroupOr\",\"subgroupShuffle\",\"subgroupShuffleDown\",\"subgroupShuffleUp\",\"subgroupShuffleXor\",\"subgroupXor\",\"quadBroadcast\",\"quadSwapDiagonal\",\"quadSwapX\",\"quadSwapY\"]);function v$(e){if(e){let t=e.replaceAll(/\\s/g,\"_\").replaceAll(/[^\\w]/g,\"\");return Sh(t).success?t:\"item\"}return\"item\"}function Sh(e){return e===\"_\"||e===\"\"?{success:!1,error:\"Identifiers cannot be equal to '' or '_'\"}:/\\s/.test(e)?{success:!1,error:\"Identifiers cannot contain whitespace.\"}:e.startsWith(\"__\")?{success:!1,error:\"Identifiers cannot start with double underscores.\"}:/^(([_\\p{XID_Start}][\\p{XID_Continue}]+)|([\\p{XID_Start}]))$/u.test(e)?{success:!0}:{success:!1,error:\"Not compliant with WGSL guidelines.\"}}function BM(e){let t=Sh(e);return t.success?_3.has(e)?{success:!1,error:\"Identifiers cannot start with reserved keywords.\"}:{success:!0}:t}function gt(e){return Ph(e,!1)}function Fo(e){return Ph(e,!0)}function Ph(e,t){Object.keys(e).forEach(r=>{let o=BM(r);if(!o.success)throw new Error(`Invalid property key '${r}'${o.error?`: ${o.error}`:\"\"}`)});let s=r=>Object.fromEntries(Object.entries(e).map(([o,a])=>[o,rc(a,r?.[o])]));return Object.setPrototypeOf(s,h$),s.propTypes=e,Object.defineProperty(s,ve,{value:{isAbstruct:t}}),s}var h$={type:\"struct\",$name(e){return es(this,e),this},toString(){return`struct:${ct(this)??\"<unnamed>\"}`}},Co=(e,t)=>{let s=t-1,r=~s;return(e&s)===0?e:(e&r)+t},ws=class{[ve]={};type;[mo];constructor(e){this.type=e,this[mo]={call:(t,[s])=>oM(t,dl[this.type],s)}}[Ux](e){return rc(dl[this.type],e)}},dl={uint8:M,uint8x2:$s,uint8x4:Is,sint8:j,sint8x2:ir,sint8x4:sr,unorm8:n,unorm8x2:ps,unorm8x4:qt,snorm8:n,snorm8x2:ps,snorm8x4:qt,uint16:M,uint16x2:$s,uint16x4:Is,sint16:j,sint16x2:ir,sint16x4:sr,unorm16:n,unorm16x2:ps,unorm16x4:qt,snorm16:n,snorm16x2:ps,snorm16x4:qt,float16:n,float16x2:ps,float16x4:qt,float32:n,float32x2:ps,float32x3:Zs,float32x4:qt,uint32:M,uint32x2:$s,uint32x3:Ps,uint32x4:Is,sint32:j,sint32x2:ir,sint32x3:Br,sint32x4:sr,\"unorm10-10-10-2\":qt,\"unorm8x4-bgra\":qt},zM=new Set(Object.keys(dl)),hve=new ws(\"uint8\"),gve=new ws(\"uint8x2\"),yve=new ws(\"uint8x4\"),bve=new ws(\"sint8\"),xve=new ws(\"sint8x2\"),Ave=new ws(\"sint8x4\"),wve=new ws(\"unorm8\"),Tve=new ws(\"unorm8x2\"),Sve=new ws(\"unorm8x4\"),Pve=new ws(\"snorm8\"),Cve=new ws(\"snorm8x2\"),Mve=new ws(\"snorm8x4\"),Eve=new ws(\"uint16\"),kve=new ws(\"uint16x2\"),Bve=new ws(\"uint16x4\"),zve=new ws(\"sint16\"),Rve=new ws(\"sint16x2\"),Dve=new ws(\"sint16x4\"),Lve=new ws(\"unorm16\"),Uve=new ws(\"unorm16x2\"),Ove=new ws(\"unorm16x4\"),Gve=new ws(\"snorm16\"),Fve=new ws(\"snorm16x2\"),Ive=new ws(\"snorm16x4\"),Yve=new ws(\"float16\"),Wve=new ws(\"float16x2\"),qve=new ws(\"float16x4\"),Vve=new ws(\"float32\"),Nve=new ws(\"float32x2\"),$ve=new ws(\"float32x3\"),Xve=new ws(\"float32x4\"),Hve=new ws(\"uint32\"),jve=new ws(\"uint32x2\"),Kve=new ws(\"uint32x3\"),Qve=new ws(\"uint32x4\"),Zve=new ws(\"sint32\"),Jve=new ws(\"sint32x2\"),ehe=new ws(\"sint32x3\"),the=new ws(\"sint32x4\"),she=new ws(\"unorm10-10-10-2\"),rhe=new ws(\"unorm8x4-bgra\");function g$(e){return _s(e)&&zM.has(e?.type)}var y$={f32:4,f16:2,i32:4,u32:4,bool:4,u16:2,vec2f:8,vec2h:4,vec2i:8,vec2u:8,\"vec2<bool>\":8,vec3f:16,vec3h:8,vec3i:16,vec3u:16,\"vec3<bool>\":16,vec4f:16,vec4h:8,vec4i:16,vec4u:16,\"vec4<bool>\":16,mat2x2f:8,mat3x3f:16,mat4x4f:16,atomic:4};function b$(e){let t=e?.type,s=y$[t];if(s!==void 0)return s;if(_r(e))return Object.values(e.propTypes).map(xr).reduce((r,o)=>r>o?r:o);if(io(e))return xr(e.elementType);if(ki(e)){let r=Object.values(e.propTypes)[0];return r?Tm(r)??1:1}if(Sd(e))return Tm(e.elementType)??1;if(gl(e)||vc(e))return Tm(e)??xr(e.inner);if(zM.has(t))return 1;throw new Error(`Cannot determine alignment of data: ${pn(e)}`)}function x$(e){if(ki(e)){let t=Object.values(e.propTypes)[0];return t?wn(t):1}return Sd(e)?wn(e.elementType):vc(e)?Tm(e)??wn(e.inner):Tm(e)??1}var Xw=new WeakMap,Hw=new WeakMap;function xr(e){let t=Xw.get(e);return t===void 0&&(t=b$(e),Xw.set(e,t)),t}function wn(e){let t=Hw.get(e);return t===void 0&&(t=x$(e),Hw.set(e,t)),t}var A$={bool:4,f32:4,f16:2,i32:4,u32:4,u16:2,vec2f:8,vec2h:4,vec2i:8,vec2u:8,\"vec2<bool>\":8,vec3f:12,vec3h:6,vec3i:12,vec3u:12,\"vec3<bool>\":12,vec4f:16,vec4h:8,vec4i:16,vec4u:16,\"vec4<bool>\":16,mat2x2f:16,mat3x3f:48,mat4x4f:64,uint8:1,uint8x2:2,uint8x4:4,sint8:1,sint8x2:2,sint8x4:4,unorm8:1,unorm8x2:2,unorm8x4:4,snorm8:1,snorm8x2:2,snorm8x4:4,uint16:2,uint16x2:4,uint16x4:8,sint16:2,sint16x2:4,sint16x4:8,unorm16:2,unorm16x2:4,unorm16x4:8,snorm16:2,snorm16x2:4,snorm16x4:8,float16:2,float16x2:4,float16x4:8,float32:4,float32x2:8,float32x3:12,float32x4:16,uint32:4,uint32x2:8,uint32x3:12,uint32x4:16,sint32:4,sint32x2:8,sint32x3:12,sint32x4:16,\"unorm10-10-10-2\":4,\"unorm8x4-bgra\":4,atomic:4};function w$(e){let t=0,s=0,r=!0,o=!1;for(let i of Object.values(e.propTypes)){if(Number.isNaN(t))throw new Error(\"Only the last property of a struct can be unbounded\");let l=t;t=Co(t,xr(i));let c=l!==t,d=vd(i);if(t+=d.size,Number.isNaN(t)&&i.type!==\"array\")throw new Error(\"Cannot nest unbounded struct within another struct\");o||(!c&&d.isContiguous?s+=d.size:(o=!0,r=!1,c||(s+=d.longestContiguousPrefix)))}let a=Co(t,xr(e));return{isContiguous:t===a&&r,size:a,longestContiguousPrefix:s}}function T$(e){let t=0,s=0,r=!0,o=!1;for(let a of Object.values(e.propTypes)){let i=wn(a),l=t;t=Co(t,i);let c=l!==t;c&&(r=!1);let d=vd(a);t+=d.size,!o&&(!c&&d.isContiguous?s+=d.size:(o=!0,r=!1,c||(s+=d.longestContiguousPrefix)))}return{isContiguous:r,size:t,longestContiguousPrefix:s}}function S$(e){let t=e.elementType,s=vd(t),r=s.size,o=Co(r,xr(t)),a=!(o>r)&&s.isContiguous,i=e.elementCount===0?NaN:e.elementCount*o,l;return a?l=i:l=s.longestContiguousPrefix,{size:i,isContiguous:a,longestContiguousPrefix:l}}function P$(e){let t=e.elementType,s=vd(t),r=s.size,o=Co(r,wn(t)),a=!(o>r)&&s.isContiguous,i=e.elementCount*o,l;return a?l=i:l=s.longestContiguousPrefix,{size:i,isContiguous:a,longestContiguousPrefix:l}}function vd(e){let t=A$[e.type];if(t!==void 0)return{isContiguous:e.type!==\"mat3x3f\",size:t,longestContiguousPrefix:e.type===\"mat3x3f\"?12:t};if(_r(e))return w$(e);if(ki(e))return T$(e);if(io(e))return S$(e);if(Sd(e))return P$(e);if(gl(e)||vc(e)){let s=_N(e),r=vd(_n(e));return s?{isContiguous:s===r.size&&r.isContiguous,size:s,longestContiguousPrefix:r.longestContiguousPrefix}:vd(e.inner)}throw new Error(`Cannot determine memory layout of data: ${e}`)}var jw=new WeakMap;function f3(e,t){let s=jw.get(e);return s===void 0&&(s=vd(e),jw.set(e,s)),s[t]}function ds(e){return f3(e,\"size\")}function Kw(e){return ds(e)}var Fe=Tn(((e,t)=>{if(gl(e)&&!e.attribs.every(RC))throw new Error(\"Arrays cannot hold decorated types other than @location. Wrap it in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).\");return t===void 0?Tn(s=>Qw(e,s)):Qw(e,t)})).$name(\"arrayOf\");function Qw(e,t){let s=r=>{if(r&&r.length!==t)throw new Error(`Array schema of ${t} elements of type ${e.type} called with ${r.length} argument(s).`);return Array.from({length:t},(o,a)=>rc(e,r?.[a]))};if(Object.setPrototypeOf(s,C$),Number.isNaN(ds(e)))throw new Error(\"Cannot nest runtime sized arrays.\");if(s.elementType=e,!Number.isInteger(t)||t<0)throw new Error(`Cannot create array schema with invalid element count: ${t}.`);return s.elementCount=t,s}var C$={[ve]:!0,type:\"array\",toString(){return`arrayOf(${String(this.elementType)}, ${this.elementCount})`}};function M$(e){if(\"multisampled\"in e){if(e.multisampled){if(e.dimension===\"2d\")return k$(e.sampleType);throw new Error(`Multisampled textures only support '2d' dimension, got '${e.dimension}'`)}switch(e.dimension){case\"1d\":return E$(e.sampleType);case\"2d\":return Zt(e.sampleType);case\"2d-array\":return B$(e.sampleType);case\"3d\":return D$(e.sampleType);case\"cube\":return z$(e.sampleType);case\"cube-array\":return R$(e.sampleType);default:throw new Error(`Unsupported texture dimension: '${e.dimension}'`)}}if(!(\"access\"in e))throw new Error(\"Descriptor is neither a sampled nor a storage texture\");switch(e.dimension){case\"1d\":return L$(e.format,e.access);case\"2d\":return zt(e.format,e.access);case\"2d-array\":return U$(e.format,e.access);case\"3d\":return O$(e.format,e.access);default:throw new Error(`Unsupported storage texture dimension: '${e.dimension}'`)}}function Cd(e,t){let s=e.startsWith(\"texture_depth\")?[\"depth\",\"float\",\"unfilterable-float\"]:t.sampleType.type===\"i32\"?[\"sint\"]:t.sampleType.type===\"u32\"?[\"uint\"]:[\"float\",\"unfilterable-float\"];return{[ve]:{},[Ku]:void 0,type:e,bindingSampleType:s,...t}}function Ch(e,t){return{[ve]:{},[Ku]:void 0,type:e,...t}}var Zw=new Map,u_={\"write-only\":\"write\",\"read-only\":\"read\",\"read-write\":\"read_write\"};function Cn(e,t){let s=Zw.get(e);return s||(s=t(),Zw.set(e,s)),s}function E$(e){let t=e||n;return Cn(`texture_1d<${t.type}>`,()=>Cd(\"texture_1d\",{dimension:\"1d\",sampleType:t,multisampled:!1}))}function Zt(e){let t=e||n;return Cn(`texture_2d<${t.type}>`,()=>Cd(\"texture_2d\",{dimension:\"2d\",sampleType:t,multisampled:!1}))}function k$(e){let t=e||n;return Cn(`texture_multisampled_2d<${t.type}>`,()=>Cd(\"texture_multisampled_2d\",{dimension:\"2d\",sampleType:t,multisampled:!0}))}function B$(e){let t=e||n;return Cn(`texture_2d_array<${t.type}>`,()=>Cd(\"texture_2d_array\",{dimension:\"2d-array\",sampleType:t,multisampled:!1}))}function z$(e){let t=e||n;return Cn(`texture_cube<${t.type}>`,()=>Cd(\"texture_cube\",{dimension:\"cube\",sampleType:t,multisampled:!1}))}function R$(e){let t=e||n;return Cn(`texture_cube_array<${t.type}>`,()=>Cd(\"texture_cube_array\",{dimension:\"cube-array\",sampleType:t,multisampled:!1}))}function D$(e){let t=e||n;return Cn(`texture_3d<${t.type}>`,()=>Cd(\"texture_3d\",{dimension:\"3d\",sampleType:t,multisampled:!1}))}function L$(e,t){let s=t||\"write-only\";return Cn(`texture_storage_1d<${e}, ${u_[s]}>`,()=>Ch(\"texture_storage_1d\",{dimension:\"1d\",format:e,access:s}))}function zt(e,t){let s=t||\"write-only\";return Cn(`texture_storage_2d<${e}, ${u_[s]}>`,()=>Ch(\"texture_storage_2d\",{dimension:\"2d\",format:e,access:s}))}function U$(e,t){let s=t||\"write-only\";return Cn(`texture_storage_2d_array<${e}, ${u_[s]}>`,()=>Ch(\"texture_storage_2d_array\",{dimension:\"2d-array\",format:e,access:s}))}function O$(e,t){let s=t||\"write-only\";return Cn(`texture_storage_3d<${e}, ${u_[s]}>`,()=>Ch(\"texture_storage_3d\",{dimension:\"3d\",format:e,access:s}))}function RM(){return Cn(\"texture_external\",()=>({[ve]:{},[Ku]:void 0,type:\"texture_external\",dimension:\"2d\"}))}function DM(e){return!!e[ve]&&typeof e.multisampled==\"boolean\"}function C1(e){return!!e[ve]&&typeof e.format==\"string\"&&typeof e.access==\"string\"}function G$(e){return!!e[ve]&&e.type===\"texture_external\"}function v3(){return{[ve]:{},type:\"sampler\",[Ku]:void 0}}function LM(){return{[ve]:{},type:\"sampler_comparison\",[Ku]:void 0}}function F$(e){return!!e[ve]&&e.type===\"sampler\"}function I$(e){return!!e[ve]&&e.type===\"sampler_comparison\"}var ohe=Tn(((e,t)=>t===void 0?s=>Jw(e,s):Jw(e,t))).$name(\"disarrayOf\");function Jw(e,t){let s=r=>{if(r&&r.length!==t)throw new Error(`Disarray schema of ${t} elements of type ${e.type} called with ${r.length} argument(s).`);return Array.from({length:t},(o,a)=>rc(e,r?.[a]))};if(Object.setPrototypeOf(s,Y$),s.elementType=e,!Number.isInteger(t)||t<0)throw new Error(`Cannot create disarray schema with invalid element count: ${t}.`);return s.elementCount=t,s}var Y$={[ve]:!0,type:\"disarray\",toString(){return`disarrayOf(${this.elementType}, ${this.elementCount})`}};var ahe={[ve]:!0,type:\"unstruct\",$name(e){return es(this,e),this},toString(){return`unstruct:${ct(this)??\"<unnamed>\"}`}};function lo(e){return new W$(e)}var W$=class{[ve]={};type=\"atomic\";inner;constructor(e){this.inner=e}};function Mh(e,t){return gl(e)?new e7(e.inner,[t,...e.attribs]):vc(e)?new t7(e.inner,[t,...e.attribs]):yh(e)?new t7(e,[t]):new e7(e,[t])}function UM(e,t){return Mh(t,{[ve]:!0,type:\"@align\",params:[e]})}function OM(e,t){return Mh(t,{[ve]:!0,type:\"@size\",params:[e]})}function ub(e,t){return Mh(t,{[ve]:!0,type:\"@location\",params:[e]})}function wi(e){return(gl(e)||vc(e))&&e.attribs.find(sN)!==void 0}function Ym(e){return!gl(e)&&!vc(e)?\"\":e.attribs.map(t=>t.params.length===0?`${t.type} `:`${t.type}(${t.params.join(\", \")}) `).join(\"\")}var GM=class{[ve]={};inner;attribs;constructor(e,t){this.inner=e,this.attribs=t;let s=t.find(BC)?.params[0],r=t.find(zC)?.params[0];if(s!==void 0){if(s<=0)throw new Error(`Custom data alignment must be a positive number, got: ${s}.`);if(Math.log2(s)%1!==0)throw new Error(`Alignment has to be a power of 2, got: ${s}.`);if(Td(this.inner)&&s%xr(this.inner)!==0)throw new Error(`Custom alignment has to be a multiple of the standard data alignment. Got: ${s}, expected multiple of: ${xr(this.inner)}.`)}if(r!==void 0){if(r<ds(this.inner))throw new Error(`Custom data size cannot be smaller then the standard data size. Got: ${r}, expected at least: ${ds(this.inner)}.`);if(r<=0)throw new Error(`Custom data size must be a positive number. Got: ${r}.`)}}},e7=class extends GM{[ve]={};type=\"decorated\"},t7=class extends GM{[ve]={};type=\"loose-decorated\"};function FM(e){return f3(e,\"isContiguous\")}function IM(e){return f3(e,\"longestContiguousPrefix\")}var YM=Object.defineProperty,q$=(e,t,s)=>t in e?YM(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s,V$=(e,t)=>{for(var s in t)YM(e,s,{get:t[s],enumerable:!0})},Ls=(e,t,s)=>q$(e,typeof t!=\"symbol\"?t+\"\":t,s);V$({},{BufferReader:()=>ep,BufferWriter:()=>Md,MaxValue:()=>ao,Measurer:()=>Nr,UnresolvedReferenceError:()=>Xc,ValidationError:()=>Eh,arrayOf:()=>j$,bool:()=>Q$,byte:()=>sX,chars:()=>vX,concat:()=>bX,dynamicArrayOf:()=>AX,f16:()=>pX,f32:()=>_X,f32Array:()=>GX,f64Array:()=>FX,generic:()=>gX,genericEnum:()=>yX,i16:()=>oX,i16Array:()=>UX,i32:()=>lX,i32Array:()=>OX,i8:()=>eX,i8Array:()=>LX,keyed:()=>SX,object:()=>hX,optional:()=>CX,string:()=>Z$,tupleOf:()=>kX,u16:()=>nX,u16Array:()=>RX,u32:()=>dX,u32Array:()=>DX,u8:()=>$M,u8Array:()=>BX,u8ClampedArray:()=>zX});var Xc=class WM extends Error{constructor(t){super(t),Object.setPrototypeOf(this,WM.prototype)}},Eh=class qM extends Error{constructor(t){super(t),Object.setPrototypeOf(this,qM.prototype)}},N$=class{constructor(){Ls(this,\"size\",NaN),Ls(this,\"unbounded\",this),Ls(this,\"isUnbounded\",!0)}add(){return this}fork(){return this}},$$=new N$,Nr=class VM{constructor(){Ls(this,\"size\",0),Ls(this,\"unbounded\",$$),Ls(this,\"isUnbounded\",!1)}add(t){return this.size+=t,this}fork(){let t=new VM;return t.size=this.size,t}},ao=Symbol(\"The biggest (in amount of bytes needed) value a schema can represent\"),Vo=class{constructor(){Ls(this,\"__unwrapped\")}resolveReferences(e){}seekProperty(e,t){return null}},X$=class{constructor(e){this.key=e}},Pm={STRING:\"string\",ENUM:\"enum\"},H$=class extends Vo{constructor(e,t){super(),this._unstableElementSchema=e,this.length=t,Ls(this,\"elementSchema\"),this.elementSchema=e}resolveReferences(e){this.elementSchema=e.resolve(this._unstableElementSchema)}write(e,t){if(t.length!==this.length)throw new Eh(`Expected array of length ${this.length}, got ${t.length}`);for(let s of t)this.elementSchema.write(e,s)}read(e){let t=[];for(let s=0;s<this.length;++s)t.push(this.elementSchema.read(e));return t}get maxSize(){return this.elementSchema.measure(ao).size*this.length}measure(e,t=new Nr){for(let s=0;s<this.length;++s)this.elementSchema.measure(e===ao?ao:e[s],t);return t}};function j$(e,t){return new H$(e,t)}var K$=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",1)}read(e){return e.readBool()}write(e,t){e.writeBool(t)}measure(e,t=new Nr){return t.add(1)}},Q$=new K$,NM=class vm extends Vo{static get _encoder(){return vm._cachedEncoder||(vm._cachedEncoder=new TextEncoder),vm._cachedEncoder}read(t){return t.readString()}write(t,s){t.writeString(s)}measure(t,s=new Nr){if(t===ao)return s.unbounded;let r=vm._encoder.encode(t);return s.add(r.byteLength+1)}};Ls(NM,\"_cachedEncoder\");var Z$=new NM,J$=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",1)}read(e){return e.readInt8()}write(e,t){e.writeInt8(t)}measure(e,t=new Nr){return t.add(1)}},eX=new J$,tX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",1)}read(e){return e.readUint8()}write(e,t){e.writeUint8(t)}measure(e,t=new Nr){return t.add(1)}},$M=new tX,sX=$M,rX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",2)}read(e){return e.readInt16()}write(e,t){e.writeInt16(t)}measure(e,t=new Nr){return t.add(2)}},oX=new rX,aX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",2)}read(e){return e.readUint16()}write(e,t){e.writeUint16(t)}measure(e,t=new Nr){return t.add(2)}},nX=new aX,iX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",4)}read(e){return e.readInt32()}write(e,t){e.writeInt32(t)}measure(e,t=new Nr){return t.add(4)}},lX=new iX,cX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",4)}read(e){return e.readUint32()}write(e,t){e.writeUint32(t)}measure(e,t=new Nr){return t.add(4)}},dX=new cX,uX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",2)}read(e){return e.readFloat16()}write(e,t){e.writeFloat16(t)}measure(e,t=new Nr){return t.add(2)}},pX=new uX,mX=class extends Vo{constructor(){super(...arguments),Ls(this,\"maxSize\",4)}read(e){return e.readFloat32()}write(e,t){e.writeFloat32(t)}measure(e,t=new Nr){return t.add(4)}},_X=new mX,fX=class extends Vo{constructor(e){super(),this.length=e}write(e,t){if(t.length!==this.length)throw new Eh(`Expected char-string of length ${this.length}, got ${t.length}`);for(let s=0;s<t.length;++s)e.writeUint8(t.charCodeAt(s))}read(e){let t=\"\";for(let s=0;s<this.length;++s)t+=String.fromCharCode(e.readByte());return t}measure(e,t=new Nr){return t.add(this.length)}};function vX(e){return new fX(e)}function $l(e){return Object.entries(e)}function XM(e,t){let s={};for(let[r,o]of $l(t))s[r]=e.resolve(o);return s}var h3=class extends Vo{constructor(e){super(),this._properties=e,Ls(this,\"properties\"),this.properties=e}resolveReferences(e){this.properties=XM(e,this._properties)}write(e,t){for(let[s,r]of $l(this.properties))r.write(e,t[s])}read(e){let t={};for(let[s,r]of $l(this.properties))t[s]=r.read(e);return t}get maxSize(){let e=new Nr;for(let t of Object.values(this.properties))t.measure(ao,e);return e.size}measure(e,t=new Nr){for(let[s,r]of $l(this.properties))r.measure(e===ao?ao:e[s],t);return t}seekProperty(e,t){let s=0;for(let[r,o]of $l(this.properties)){if(r===t)return{bufferOffset:s,schema:o};s+=o.measure(e).size}return null}};function hX(e){return new h3(e)}var HM=class extends Vo{constructor(e,t,s){super(),this.keyedBy=e,this._subTypeMap=s,Ls(this,\"_baseObject\"),Ls(this,\"subTypeMap\"),this._baseObject=new h3(t),this.subTypeMap=s}resolveReferences(e){this._baseObject.resolveReferences(e),this.subTypeMap=XM(e,this._subTypeMap)}write(e,t){let s=t.type,r=this.subTypeMap[s]||null;if(r===null)throw new Error(`Unknown sub-type '${s.toString()}' in among '${JSON.stringify(Object.keys(this.subTypeMap))}'`);this.keyedBy===Pm.ENUM?e.writeUint8(t.type):e.writeString(t.type),this._baseObject.write(e,t);for(let[o,a]of $l(r.properties))a.write(e,t[o])}read(e){let t=this.keyedBy===Pm.ENUM?e.readByte():e.readString(),s=this.subTypeMap[t]||null;if(s===null)throw new Error(`Unknown sub-type '${t}' in among '${JSON.stringify(Object.keys(this.subTypeMap))}'`);let r=this._baseObject.read(e);if(r.type=t,s!==null)for(let[o,a]of $l(s.properties))r[o]=a.read(e);return r}measure(e,t=new Nr){if(this._baseObject.measure(e,t),this.keyedBy===Pm.ENUM)t.add(1);else if(e!==ao)t.add(e.type.length+1);else return t.unbounded;if(e===ao){let s=Object.values(this.subTypeMap).map(r=>{let o=t.fork();for(let a of Object.values(r.properties))a.measure(ao,o);return[r,o.size]}).reduce((r,o)=>r[1]>o[1]?r:o)[0];for(let r of Object.values(s.properties))r.measure(ao,t)}else{let s=e.type,r=this.subTypeMap[s]||null;if(r===null)throw new Error(`Unknown sub-type '${s.toString()}', expected one of '${JSON.stringify(Object.keys(this.subTypeMap))}'`);for(let[o,a]of $l(r.properties))a.measure(e[o],t)}return t}};function gX(e,t){return new HM(Pm.STRING,e,t)}function yX(e,t){return new HM(Pm.ENUM,e,t)}function bX(e){return new h3(Object.fromEntries(e.flatMap(({properties:t})=>Object.entries(t))))}var xX=class extends Vo{constructor(e){super(),this._unstableElementType=e,Ls(this,\"elementType\"),this.elementType=e}resolveReferences(e){this.elementType=e.resolve(this._unstableElementType)}write(e,t){e.writeUint32(t.length);for(let s of t)this.elementType.write(e,s)}read(e){let t=[],s=e.readUint32();for(let r=0;r<s;++r)t.push(this.elementType.read(e));return t}get maxSize(){return this.measure(ao).size}measure(e,t=new Nr){if(e===ao)return t.unbounded;t.add(4);for(let s of e)this.elementType.measure(s,t);return t}seekProperty(e,t){if(typeof t==\"symbol\")return null;let s=Number.parseInt(String(t),10);if(Number.isNaN(s))return null;if(e===ao)return{bufferOffset:this.elementType.measure(ao).size*s,schema:this.elementType};if(s>=e.length)return null;let r=new Nr;for(let o=0;o<s;++o)this.elementType.measure(e[o],r);return{bufferOffset:r.size,schema:this.elementType}}};function AX(e){return new xX(e)}var jM=class{constructor(e){Ls(this,\"__unwrapped\"),Ls(this,\"ref\"),this.ref=new X$(e)}resolveReferences(){throw new Xc(\"Tried to resolve a reference directly. Do it through a RefResolver instead.\")}read(){throw new Xc(\"Tried to read a reference directly. Resolve it instead.\")}write(){throw new Xc(\"Tried to write a reference directly. Resolve it instead.\")}measure(){throw new Xc(\"Tried to measure size of a reference directly. Resolve it instead.\")}seekProperty(){throw new Xc(\"Tried to seek property of a reference directly. Resolve it instead.\")}},wX=class{constructor(){Ls(this,\"registry\",{})}hasKey(e){return this.registry[e]!==void 0}register(e,t){this.registry[e]=t}resolve(e){if(e instanceof jM){let t=e.ref.key;if(this.registry[t]!==void 0)return this.registry[t];throw new Xc(`Couldn't resolve reference to ${t}. Unknown key.`)}return e.resolveReferences(this),e}},TX=class{constructor(e,t){this.key=e,Ls(this,\"__unwrapped\"),Ls(this,\"__keyDefinition\"),Ls(this,\"innerType\"),this.innerType=t(new jM(e)),this.resolveReferences(new wX)}resolveReferences(e){e.hasKey(this.key)||(e.register(this.key,this.innerType),this.innerType.resolveReferences(e))}read(e){return this.innerType.read(e)}write(e,t){this.innerType.write(e,t)}get maxSize(){return this.measure(ao).size}measure(e,t=new Nr){return this.innerType.measure(e,t)}seekProperty(e,t){return this.innerType.seekProperty(e,t)}};function SX(e,t){return new TX(e,t)}var PX=class extends Vo{constructor(e){super(),this._innerUnstableSchema=e,Ls(this,\"innerSchema\"),this.innerSchema=e}resolveReferences(e){this.innerSchema=e.resolve(this._innerUnstableSchema)}write(e,t){t!=null?(e.writeBool(!0),this.innerSchema.write(e,t)):e.writeBool(!1)}read(e){if(e.readBool())return this.innerSchema.read(e)}get maxSize(){return this.measure(ao).size}measure(e,t=new Nr){return e!==void 0&&this.innerSchema.measure(e,t),t.add(1)}};function CX(e){return new PX(e)}function MX(e,t){return t.map(s=>e.resolve(s))}var EX=class extends Vo{constructor(e){super(),this._unstableSchemas=e,Ls(this,\"schemas\"),this.schemas=e}resolveReferences(e){this.schemas=MX(e,this._unstableSchemas)}write(e,t){if(t.length!==this.schemas.length)throw new Eh(`Expected tuple of length ${this.schemas.length}, got ${t.length}`);for(let s=0;s<this.schemas.length;++s)this.schemas[s].write(e,t[s])}read(e){let t=[];for(let s=0;s<this.schemas.length;++s)t.push(this.schemas[s].read(e));return t}get maxSize(){return this.measure(ao).size}measure(e,t=new Nr){for(let s=0;s<this.schemas.length;++s)this.schemas[s].measure(e===ao?ao:e[s],t);return t}};function kX(e){return new EX(e)}var xl=class extends Vo{constructor(e,t){super(),this.length=e,this._arrayConstructor=t,Ls(this,\"byteLength\"),this.byteLength=e*t.BYTES_PER_ELEMENT}write(e,t){e.writeSlice(t)}read(e){let t=new ArrayBuffer(this.byteLength),s=new this._arrayConstructor(t,0,this.length);return e.readSlice(s,0,this.byteLength),s}measure(e,t=new Nr){return t.add(this.byteLength)}},BX=e=>new xl(e,Uint8Array),zX=e=>new xl(e,Uint8ClampedArray),RX=e=>new xl(e,Uint16Array),DX=e=>new xl(e,Uint32Array),LX=e=>new xl(e,Int8Array),UX=e=>new xl(e,Int16Array),OX=e=>new xl(e,Int32Array),GX=e=>new xl(e,Float32Array),FX=e=>new xl(e,Float64Array);function IX(){let e=new Uint8Array(4),t=new Uint32Array(e.buffer);return t[0]=1,e[0]===0}function g3(){return IX()?\"big\":\"little\"}function y3(e){let t=0,s=e;return s&&\"buffer\"in s&&\"byteOffset\"in s&&(t+=s.byteOffset,s=s.buffer),{buffer:s,byteOffset:t,byteLength:e.byteLength}}var KM=class{constructor(e,t){Ls(this,\"dataView\"),Ls(this,\"littleEndian\"),Ls(this,\"needsByteSwap\"),Ls(this,\"byteOffset\",0),Ls(this,\"endianness\");let{byteOffset:s=0,endianness:r=\"system\"}=t??{};this.byteOffset=s;let o=g3();this.endianness=r===\"system\"?o:r,this.littleEndian=this.endianness===\"little\",this.needsByteSwap=this.endianness!==o;let a=y3(e);this.byteOffset+=a.byteOffset,this.dataView=new DataView(a.buffer)}get currentByteOffset(){return this.byteOffset}seekTo(e){this.byteOffset=e}skipBytes(e){this.byteOffset+=e}},ep=class extends KM{constructor(){super(...arguments),Ls(this,\"_cachedTextDecoder\")}get _textDecoder(){return this._cachedTextDecoder||(this._cachedTextDecoder=new TextDecoder(void 0,{fatal:!0})),this._cachedTextDecoder}readBool(){return this.dataView.getUint8(this.byteOffset++)!==0}readByte(){return this.dataView.getUint8(this.byteOffset++)}readInt8(){return this.dataView.getInt8(this.byteOffset++)}readUint8(){return this.dataView.getUint8(this.byteOffset++)}readInt16(){let e=this.dataView.getInt16(this.byteOffset,this.littleEndian);return this.byteOffset+=2,e}readUint16(){let e=this.dataView.getUint16(this.byteOffset,this.littleEndian);return this.byteOffset+=2,e}readInt32(){let e=this.dataView.getInt32(this.byteOffset,this.littleEndian);return this.byteOffset+=4,e}readUint32(){let e=this.dataView.getUint32(this.byteOffset,this.littleEndian);return this.byteOffset+=4,e}readFloat16(){let e=this.dataView.getFloat16(this.byteOffset,this.littleEndian);return this.byteOffset+=2,e}readFloat32(){let e=this.dataView.getFloat32(this.byteOffset,this.littleEndian);return this.byteOffset+=4,e}readString(){let e=0;for(;this.byteOffset+e<this.dataView.byteLength&&this.dataView.getUint8(this.byteOffset+e++)!==0;);let t=this._textDecoder.decode(new Uint8Array(this.dataView.buffer,this.byteOffset,e-1));return this.byteOffset+=e,t}readSlice(e,t,s){let r=y3(e),o=new Uint8Array(r.buffer,r.byteOffset+t),a=e.BYTES_PER_ELEMENT;if(this.needsByteSwap&&a>1)for(let i=0;i<s;i+=a)for(let l=a-1;l>=0;l--)o[i+l]=this.dataView.getUint8(this.byteOffset++);else o.set(new Uint8Array(this.dataView.buffer,this.byteOffset,s)),this.byteOffset+=s}},Md=class extends KM{constructor(){super(...arguments),Ls(this,\"_cachedTextEncoder\")}get _textEncoder(){return this._cachedTextEncoder||(this._cachedTextEncoder=new TextEncoder),this._cachedTextEncoder}writeBool(e){this.dataView.setUint8(this.byteOffset++,e?1:0)}writeByte(e){this.dataView.setUint8(this.byteOffset++,e)}writeInt8(e){this.dataView.setInt8(this.byteOffset++,e)}writeUint8(e){this.dataView.setUint8(this.byteOffset++,e)}writeInt16(e){this.dataView.setInt16(this.byteOffset,e,this.littleEndian),this.byteOffset+=2}writeUint16(e){this.dataView.setUint16(this.byteOffset,e,this.littleEndian),this.byteOffset+=2}writeInt32(e){this.dataView.setInt32(this.byteOffset,e,this.littleEndian),this.byteOffset+=4}writeUint32(e){this.dataView.setUint32(this.byteOffset,e,this.littleEndian),this.byteOffset+=4}writeFloat16(e){this.dataView.setFloat16(this.byteOffset,e,this.littleEndian),this.byteOffset+=2}writeFloat32(e){this.dataView.setFloat32(this.byteOffset,e,this.littleEndian),this.byteOffset+=4}writeString(e){let t=this._textEncoder.encodeInto(e,new Uint8Array(this.dataView.buffer,this.byteOffset));this.byteOffset+=t.written,this.dataView.setUint8(this.byteOffset++,0)}writeSlice(e){let t=y3(e),s=new Uint8Array(t.buffer,t.byteOffset,t.byteLength),r=e.BYTES_PER_ELEMENT;if(this.needsByteSwap&&r>1)for(let o=0;o<s.length;o+=r)for(let a=r-1;a>=0;a--)this.dataView.setUint8(this.byteOffset++,s[o+a]);else new Uint8Array(this.dataView.buffer,this.byteOffset).set(s),this.byteOffset+=s.length}};function YX(e,t){let s=\"size\"in e?e.size:e.currentByteOffset,r=t-1,o=s&r;\"skipBytes\"in e?e.skipBytes(t-o&r):e.add(t-o&r)}var wo=YX,s7=new WeakMap;function kh(e){let t=s7.get(e);if(t)return t;let s=new Nr,r={},o;for(let a in e.propTypes){let i=e.propTypes[a];if(i===void 0)throw new Error(`Property ${a} is undefined in struct`);let l=s.size;wo(s,ki(e)?wn(i):xr(i)),o&&(o.padding=s.size-l);let c=ds(i);r[a]={offset:s.size,size:c},o=r[a],s.add(c)}return o&&(o.padding=Co(ds(e),xr(e))-s.size),s7.set(e,r),r}var oc=Symbol(\"indirectOffset\"),ac=Symbol(\"indirectContiguous\");function WX(e){return typeof e==\"object\"&&e!==null&&oc in e&&ac in e}function hm(e,t){return{[oc]:e,[ac]:t}}function b3(e,t){if(t===oc)return e[oc];if(t===ac)return e[ac]}function x3(e,t,s=ds(e)){let r=_n(e),o=js(r)?r.componentCount:void 0;if(o!==void 0){let a=ds(r.primitive);return VX(hm(t,s),a,o)}return _r(r)?$X(r,hm(t,s)):io(r)?NX(r,hm(t,s)):hm(t,s)}function qX(e,t=0){return x3(e,t,ds(e))}function VX(e,t,s){let r=e[oc];return new Proxy(e,{get(o,a){let i=b3(o,a);if(i!==void 0)return i;let l=a===\"x\"||a===\"0\"?0:a===\"y\"||a===\"1\"?1:a===\"z\"||a===\"2\"?2:a===\"w\"||a===\"3\"?3:-1;if(l<0||l>=s)return;let c=l*t,d=Math.max(0,o[ac]-c);return hm(r+c,d)}})}function NX(e,t){let s=e.elementType,r=ds(s),o=Co(r,xr(s)),a=o>r;return new Proxy(t,{get(i,l){let c=b3(i,l);if(c!==void 0)return c;if(l===\"length\")return e.elementCount;if(typeof l!=\"string\")return;let d=Number(l);if(!Number.isInteger(d)||d<0||d>=e.elementCount)return;let u=d*o,p=FM(s)?Math.max(0,i[ac]-u):r+IM(s),_=a?Math.min(p,r):p;return x3(s,i[oc]+u,_)}})}function $X(e,t){let s=kh(e),r=e.propTypes,o=Object.keys(r),a=new Map,i=0;for(let l=0;l<o.length;l++){let c=o[l];if(!c)continue;let d=r[c];if(!d)continue;let u=s[c],p=u.padding??0,_=FM(d);if(!(l===o.length-1||p>0||!_))continue;let f=u.offset+(_?u.size:IM(d));for(let h=i;h<=l;h++){let g=o[h];if(!g)continue;let y=s[g];a.set(g,{offset:y.offset,runEnd:f})}i=l+1}return new Proxy(t,{get(l,c){let d=b3(l,c);if(d!==void 0)return d;if(typeof c!=\"string\")return;let u=a.get(c);if(!u)return;let p=Math.max(0,l[ac]-u.offset),_=Math.max(0,u.runEnd-u.offset),f=r[c];if(f)return x3(f,l[oc]+u.offset,ds(e)===u.runEnd?p:_)}})}function XX(e){let t=_n(e);if(_r(t)){let s=kh(t),r=t.propTypes,o=Object.keys(r);for(let a=0;a<o.length;a++){let i=o[a];if(!i)continue;let l=s[i],c=l.padding??0,d=l.offset+l.size;if(a===o.length-1||c>0)return d}return 0}if(io(t)){let s=t.elementType,r=ds(s),o=Co(r,xr(s)),a=ds(e);return Number.isFinite(a)?o>r?r:a:r}return ds(e)}function M1(e,t){if(!t)return{offset:0,contiguous:XX(e)};let s=t(qX(e));if(WX(s))return{offset:s[oc],contiguous:s[ac]};throw new Error(\"memoryLayoutOf: accessor did not return a schema element. Make sure the accessor navigates to a field or element of the schema (e.g. `(s) => s.position.x`).\")}function po(e,t){return Mh(e,{[ve]:!0,type:\"@builtin\",params:[t]})}var Qo={vertexIndex:po(M,\"vertex_index\"),instanceIndex:po(M,\"instance_index\"),clipDistances:po(Fe(M,8),\"clip_distances\"),position:po(qt,\"position\"),frontFacing:po(ys,\"front_facing\"),fragDepth:po(n,\"frag_depth\"),primitiveIndex:po(M,\"primitive_index\"),sampleIndex:po(M,\"sample_index\"),sampleMask:po(M,\"sample_mask\"),localInvocationId:po(Ps,\"local_invocation_id\"),localInvocationIndex:po(M,\"local_invocation_index\"),globalInvocationId:po(Ps,\"global_invocation_id\"),globalInvocationIndex:po(M,\"global_invocation_index\"),workgroupId:po(Ps,\"workgroup_id\"),workgroupIndex:po(M,\"workgroup_index\"),numWorkgroups:po(Ps,\"num_workgroups\"),subgroupInvocationId:po(M,\"subgroup_invocation_id\"),subgroupSize:po(M,\"subgroup_size\"),subgroupId:po(M,\"subgroup_id\"),numSubgroups:po(M,\"num_subgroups\")};var nhe=(qo(),Wa),m=(qo(),ps),ihe=(qo(),To),nc=(qo(),ir),fe=(qo(),$s),lhe=(qo(),qa),A=(qo(),Zs),che=(qo(),Pa),dhe=(qo(),Br),id=(qo(),Ps),uhe=(qo(),Va),S=(qo(),qt),phe=(qo(),So),mhe=(qo(),sr),_he=(qo(),Is),fhe=(qo(),yl),tp=(qo(),bl),vhe=(qo(),Eo);function HX(e){return e?.[Ka]}function A3(e,t={}){let s=0,r=new Set;return Object.fromEntries(Object.entries(e??{}).map(([o,a])=>{let i=sc(a);if(i!==void 0){if(r.has(i))throw new Error(\"Duplicate custom location attributes found\");r.add(i)}return[o,a]}).map(([o,a])=>{if(wi(a))return[o,a];if(sc(a)!==void 0)return[o,a];if(t[o])return[o,ub(t[o],a)];for(;r.has(s);)s++;return[o,ub(s++,a)]}))}function QM(e,t={}){let s=[],r={};for(let[o,a]of Object.entries(e))wi(a)?s.push({schemaKey:o,type:a}):r[o]=a;return{dataSchema:Object.keys(r).length>0?Ph(A3(r,t),!1):void 0,positionalArgs:s}}function jX(e){let t=A3(e);return{dataSchema:void 0,positionalArgs:Object.entries(t).map(([s,r])=>({schemaKey:s,type:r}))}}function p_(e,t={}){return o_(e)?hh(e)||wi(e)||sc(e)!==void 0?e:ub(0,e):Ph(A3(e,t),!1)}var ti=class{#e;#t;#s;#r;#o;#a;constructor(e,t,s){this.#e=e,this.#a=t,this.#t={},this.#r=s,this.#s=new Set}accessProp(e){let t=this.#e[e]??this.#a;if(t)return this.provideProp(e,t)}provideProp(e,t){let s=this.#t[e];if(!s){let r=e.replaceAll(\"$\",\"\");if(this.#s.has(r))throw new Error(`Property name '${r}' causes naming clashes. Choose a different name.`);let o=BM(r);if(!o.success)throw new Error(`Invalid property key '${e}'${o.error?`: ${o.error}`:\"\"}`);this.#s.add(r),s={prop:r,type:t},this.#t[e]=s}return s}get completeStruct(){if(!this.#o){this.#o=p_(Object.fromEntries(Object.values(this.#t).map(t=>[t.prop,t.type])),this.#r);let e=ct(this);e&&es(this.#o,e)}return this.#o}[Ws](e){return e.resolve(this.completeStruct)}toString(){return`auto-struct:${ct(this)??\"<unnamed>\"}`}};ti.prototype[ve]={};ti.prototype.type=\"auto-struct\";var ZM=class{[ve]={};type=\"entry-input-router\";structArg;positionalArgsMap;constructor(e,t){this.structArg=e,this.positionalArgsMap=new Map(t.map(s=>[s.schemaKey,s.arg]))}toString(){return\"entry-input-router\"}accessProp(e){let t=this.positionalArgsMap.get(e);if(t)return t();let s=this.structArg?.();if(s&&_r(s.dataType))return{target:s,prop:e}}},KX=[\"vec2f\",\"vec3f\",\"vec4f\",\"vec2h\",\"vec3h\",\"vec4h\",\"vec2i\",\"vec3i\",\"vec4i\",\"vec2u\",\"vec3u\",\"vec4u\",\"mat2x2f\",\"mat3x3f\",\"mat4x4f\"],QX={f:{1:n,2:ps,3:Zs,4:qt},h:{1:Vr,2:To,3:Pa,4:So},i:{1:j,2:ir,3:Br,4:sr},u:{1:M,2:$s,3:Ps,4:Is},b:{1:ys,2:Wa,3:qa,4:Va}};function Cu(e,t){if(KX.includes(e.dataType.type)&&t in db){let r=db[t];return Je(new m3(e,r),Ns,e.origin,e.possibleSideEffects)}if(io(e.dataType)&&t===\"length\")return e.dataType.elementCount===0?Je(Ne`arrayLength(&${e})`,M,\"runtime\",e.possibleSideEffects):Ai(e.dataType.elementCount);if(xi(e.dataType)&&t===\"columns\")return Je(new jC(e),Ns,e.origin,e.possibleSideEffects);if(_r(e.dataType)||ki(e.dataType))return aM(e,t);if(e.dataType instanceof ti){let r=e.dataType.accessProp(t);return r?Je(Ne`${e}.${r.prop}`,r.type,\"argument\",e.possibleSideEffects):void 0}if(e.dataType instanceof ZM){let r=e.dataType.accessProp(t);return n_(r)?r:r?Cu(r.target,r.prop):void 0}if(ta(e.dataType)){let r=qx(e);return t===\"$\"?r:Cu(r,t)}if(js(e.dataType)&&t===\"kind\")return Je(e.dataType.type,Ns,\"constant\",!1);let s=t.length;if(js(e.dataType)&&s>=1&&s<=4&&/^[xyzw]+$|^[rgba]+$/.test(t)){let r=QX[e.dataType.type.includes(\"bool\")?\"b\":e.dataType.type[4]][s];return r?Je(qr(e)?e.value[t]:Ne`${e}.${t}`,r,s===1?e.origin:e.origin===\"constant\"||e.origin===\"constant-immutable-def\"?\"constant\":\"runtime\",e.possibleSideEffects):void 0}if(qr(e)||e.dataType===Ns){let r=e.value[t];return Su(r)&&ct(r)===void 0&&es(r,t),Ya(r)}}var r7={mat2x2f:ps,mat3x3f:Zs,mat4x4f:qt};function Bh(e,t){let s=typeof t==\"number\"?Ya(t):t;if(io(e.dataType)||Sd(e.dataType)){let r=e.dataType.elementType,o;return e.origin===\"constant-immutable-def\"?o=s.origin===\"constant\"||s.origin===\"constant-immutable-def\"?\"constant-immutable-def\":\"runtime-immutable-def\":e.origin===\"constant\"?o=s.origin===\"constant\"||s.origin===\"constant-immutable-def\"?\"constant\":\"runtime\":o=e.origin,e.value instanceof Sm&&qr(s)?e.value.elements[s.value]:Je(qr(e)&&qr(s)?e.value[s.value]:Ne`${e}[${s}]`,r,o,e.possibleSideEffects||s.possibleSideEffects)}if(js(e.dataType))return Je(qr(e)&&qr(s)?e.value[s.value]:Ne`${e}[${s}]`,e.dataType.primitive,e.origin,e.possibleSideEffects||s.possibleSideEffects);if(ta(e.dataType))return Bh(qx(e),s);if(e.value instanceof jC){let r=r7[e.value.matrix.dataType.type];return Je(Ne`${e.value.matrix}[${s}]`,r,e.origin,e.possibleSideEffects||s.possibleSideEffects)}if(e.dataType.type in r7)throw new Error(\"The only way of accessing matrix elements in TypeGPU functions is through the 'columns' property.\");if(qr(e)&&qr(s))return Ya(e.value[s.value]);if(_r(e.dataType)&&qr(s)&&typeof s.value==\"string\")return Cu(e,s.value)}var Mu={get(e,t){if(t in e)return Reflect.get(e,t);if(t===\"toString\"||t===Symbol.toStringTag||t===Symbol.toPrimitive)return()=>e.toString();if(typeof t==\"symbol\")return;let s=bh(e),r=Number(t);if(!Number.isNaN(r)){let a=Bh(s,r);return a?new Proxy({[ve]:!0,[Ws]:i=>i.resolveSnippet(a),[tc]:a,toString:()=>`${String(e)}[${t}]`},Mu):void 0}let o=Cu(s,String(t));if(o)return new Proxy({[ve]:!0,[Ws]:a=>a.resolveSnippet(o),[tc]:o,toString:()=>`${String(e)}.${t}`},Mu)}};function K1(e){let t=e;for(;;){let s=HX(t);if(!s)break;t=s}return t}function Bi(e,t){return Object.defineProperty(e,Ka,{get(){if(\"get\"in t.codegenMode)return t.codegenMode.get.apply(this);let s=this;return new Proxy({[ve]:!0,get[tc](){if(!(\"getBaseSnippet\"in t.codegenMode))throw new Error(\"Expected 'getBaseSnippet' to be defined.\");return t.codegenMode.getBaseSnippet.apply(s,[this])},[Ws]:r=>r.resolve(s),toString:()=>`${s.toString()}.$`},Mu)}}),Object.defineProperty(e,\"$\",{get(){let s=kw();return s.type===\"codegen\"?this[Ka]:s.type===\"simulate\"?t.simulateMode?.get?t.simulateMode.get.apply(this,[s]):t.normalMode.get.apply(this):s.type===\"normal\"?t.normalMode.get.apply(this):Im(s,\"makeDereferenceable.ts#$ (get)\")},set(s){let r=kw();if(r.type===\"normal\"){if(!t.normalMode.set)throw new Error(`'${this.toString()}' cannot be set in normal mode`);t.normalMode.set.apply(this,[s]);return}if(r.type===\"simulate\"){if(t.simulateMode?.set)t.simulateMode.set.apply(this,[r,s]);else if(t.normalMode.set)t.normalMode.set.apply(this,[s]);else throw new Error(`'${this.toString()}' cannot be set in simulate mode`);return}if(r.type===\"codegen\")throw new Error(\"Unreachable makeDerefenceable.ts#$ (set)\");return Im(r,\"makeDereferenceable.ts#$ (set)\")}}),e}function Al(e,t){return _s(e)||Object.defineProperty(e,ve,{value:!0,writable:!0}),Object.defineProperty(e,\"toString\",{value(){return t.asString.apply(this)}}),Object.defineProperty(e,Ws,{value(s){return t.resolve.apply(this,[s])}}),e}function w3(e,t){if(!o_(e)){if(!Array.isArray(t))throw new Error(`Expected an array value for a partially-applied array schema, but received: ${typeof t}.`);return new o7(e(t.length),t)}return new o7(e,t)}function JM(e){let t=Reflect.ownKeys(e);for(let s of t){let r=e[s];(r&&typeof r==\"object\"||typeof r==\"function\")&&JM(r)}return Object.freeze(e)}var o7=class E1{[He];static#e=void(E1.prototype[ve]={},E1.prototype.resourceType=\"const\",Bi(Al(E1.prototype,{asString(){return`const:${ct(this)??\"<unnamed>\"}`},resolve(t){let s=t.makeUniqueIdentifier(ct(this),\"global\");return t.gen.declareGlobalConst({id:s,dataType:this.dataType,init:Je(this[He].value,this.dataType,\"constant\")})}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.dataType,\"constant-immutable-def\",!1)}},normalMode:{get(){return this[He].value}}}));constructor(t,s){this[He]={type:\"const\",dataType:t,value:s&&typeof s==\"object\"?JM(s):s,label:void 0}}get dataType(){return this[He].dataType}$name(t){return es(this,t),this}};function ZX(e){return AN(e)||yh(e)||Pu(e)}function a7(e){let t=e.pluginProvided??e.userProvided??{},s=Object.defineProperties({},Object.getOwnPropertyDescriptors(t));for(let r of[e.args,e.out].filter(o=>o!==void 0))for(let[o,a]of Object.entries(r)){if(o in s&&s[o]!==a)throw new Error(`Key '${o}' appears in externals despite already being used for argument/return type. Please rename this external.`);s[o]=a}return s}function JX(e,t,s){let r=[...e.matchAll(/:\\s*(?<arg>.*?)\\s*[,)]/g)].map(a=>a?.[1]),o=Object.fromEntries(t.flatMap((a,i)=>{let l=r?.[i];return _r(a)&&l!==void 0?[[l,a]]:[]}));s.setExternals(\"args\",o)}function eE(e,t,s){let r=e.match(/->\\s(?<output>[\\w\\d_]+)\\s{/),o=r?r[1]?.trim():void 0;_r(t)&&o&&!/\\s/g.test(o)&&s.setExternals(\"out\",{[o]:t})}var n7=/([$_\\p{XID_Start}][$\\p{XID_Continue}]*)/u,eH=new RegExp(`(${n7.source})(\\\\.${n7.source})*`,\"ug\"),tH=new RegExp(`(?<![\\\\p{XID_Continue}\\\\$.])${eH.source}(?![\\\\p{XID_Continue}\\\\$])`,\"ug\");function zh(e,t,s){let r=Object.keys(t);if(r.length===0)return s;let o=r.find(a=>a.includes(\".\"));if(o)throw new Error(`External key '${o}' contains invalid character '.'`);return s.replaceAll(tH,a=>{let i=a.split(\".\");if(!Object.hasOwn(t,i.at(0)))return a;let l=t,c=\"\";for(let[d,u]of i.entries()){if(l=l[u],ZX(l)||typeof l==\"string\"){c=i.slice(d+1).map(p=>`.${p}`).join(\"\"),Su(l)&&ct(l)===void 0&&es(l,i.slice(0,d+1).join(\"_\"));break}if(typeof l!=\"object\"||l===null||d===i.length-1)return rr.warn(\"external-omitted\",`During resolution, the external '${i.slice(0,d+1).join(\".\")}' has been omitted. Only TGPU resources, 'use gpu' functions, primitives, and plain JS objects can be used as externals.`),a}return e.resolve(l).value+c})}var sH=class{[ve]=!0;#e;constructor(e){this.#e=e}[Ws](){return Je(this.#e,Ns,\"runtime\",!0)}},T3=new Set([`\n`,\"\\v\",\"\\f\",\"\\r\",\"\\x85\",\"\\u2028\",\"\\u2029\"]),tE=new Set([...T3,\" \",\"\t\",\"\\u200E\",\"\\u200F\"]);function rH(e){let{strippedCode:t,argRange:s}=oH(e),r=new sE(t);r.consume(\"(\");let o=[];for(;!r.isAt(\")\");){let i=[];for(;r.isAt(\"@\");)r.parseUntil(i7,l7),r.consume(\")\"),i.push(r.lastParsed);r.parseUntil(aH);let l=r.lastParsed,c;r.isAt(\":\")&&(r.consume(\":\"),r.parseUntil(nH,lH),c=r.lastParsed),o.push({identifier:l,attributes:i,type:c}),r.isAt(\",\")&&r.consume(\",\")}r.consume(\")\");let a;if(r.isAt(\"->\")){r.consume(\"->\");let i=[];for(;r.isAt(\"@\");)r.parseUntil(i7,l7),r.consume(\")\"),i.push(r.lastParsed);a={type:r.str.slice(r.pos),attributes:i}}return{args:o,ret:a,range:{begin:s[0],end:s[1]}}}function oH(e){let t=new sE(e),s=\"\",r;for(;!t.isFinished();){if(t.isAt(tE)){t.advanceBy(1);continue}if(t.isAt(\"//\")){t.consume(\"//\"),t.parseUntil(T3),t.advanceBy(1);continue}if(t.isAt(\"/*\")){t.parseUntil(iH,cH),t.consume(\"*/\");continue}if(t.isAt(\"{\"))return{strippedCode:s,argRange:[r,t.pos]};t.isAt(\"(\")&&r===void 0&&(r=t.pos),r!==void 0&&(s+=t.str[t.pos]),t.advanceBy(1)}throw new Error(\"Invalid wgsl code!\")}var sE=class{str;#e;#t;constructor(e){this.str=e,this.#t=0}get pos(){return this.#t}get lastParsed(){if(this.#e===void 0)throw new Error(\"Parse was not called yet!\");return this.str.slice(this.#e,this.pos)}isFinished(){return this.#t>=this.str.length}isAt(e){if(typeof e==\"string\"){for(let t=0;t<e.length;t++)if(this.str[this.#t+t]!==e[t])return!1;return!0}for(let t of e)if(this.isAt(t))return!0;return!1}parseUntil(e,t){this.#e=this.#t;let s=0;for(;this.#t<this.str.length;){if(t&&this.isAt(t[0])&&(s+=1),t&&this.isAt(t[1])&&(s-=1),s===0&&this.isAt(e))return this.#t;this.#t+=1}throw new Error(\"Reached the end of the string without finding a match!\")}advanceBy(e){this.#t+=e}consume(e){if(!this.isAt(e))throw new Error(`Expected '${e}' at position ${this.#t}, but found '${this.str.slice(this.#t,this.#t+e.length)}'`);this.advanceBy(e.length)}},i7=new Set([\")\"]),aH=new Set([\":\",\",\",\")\"]),nH=new Set([\",\",\")\"]),iH=new Set([\"*/\"]),l7=[\"(\",\")\"],lH=[\"<\",\">\"],cH=[\"/*\",\"*/\"];function Ed(e,t,s){let r={};return{[Mo]:typeof e==\"function\"?e:void 0,setExternals(o,a){if(o===\"userProvided\"&&\"userProvided\"in r)throw new Error(\"Cannot call '$uses' multiple times. If you wish to override dependencies, use slots or accessors instead.\");if(r[o]=a,r.userProvided&&r.pluginProvided)throw new Error(\"Cannot call '$uses' on functions whose metadata was provided by unplugin-typegpu.\")},resolve(o,a,i,l){let c=\"\";t===\"compute\"?c=`@compute @workgroup_size(${s?.join(\", \")}) `:t===\"vertex\"?c=\"@vertex \":t===\"fragment\"&&(c=\"@fragment \");let d=o.makeUniqueIdentifier(ct(this),\"global\");if(typeof e==\"string\"){if(!i)throw new Error(\"Explicit return type is required for string implementation\");if(l){for(let P of l.positionalArgs){let C=Sh(P.schemaKey);if(!C.success)throw new Error(`Invalid argument name \"${P.schemaKey}\"${C.error?`: ${C.error}`:\"\"}`);if(o.isIdentifierBanned(P.schemaKey))throw new Error(`Invalid argument name \"${P.schemaKey}\", the identifier is a reserved keyword.`)}this.setExternals(\"args\",{in:Object.fromEntries(l.positionalArgs.map(P=>[P.schemaKey,new sH(P.schemaKey)]))})}let y=zh(o,a7(r),e),b=\"\",x=\"\";if(t!==\"normal\"&&l){let{dataSchema:P,positionalArgs:C}=l,E=[];P&&c7(\"in\",y)&&E.push(`in: ${o.resolve(P).value}`);for(let L of C){let k=L.schemaKey;c7(k,y)&&E.push(`${Ym(L.type)}${k}: ${o.resolve(L.type).value}`)}let R=`(${E.join(\", \")})`,U=Td(i)?Ym(i):\"\";b=`${R}${i!==zr?_r(i)?` -> ${o.resolve(i).value} `:` -> ${U!==\"\"?U:\"@location(0)\"} ${o.resolve(i).value} `:\" \"}`,x=y}else{let P=rH(y);if(P.args.length!==a.length)throw new Error(`WGSL implementation has ${P.args.length} arguments, while the shell has ${a.length} arguments.`);b=`(${P.args.map((C,E)=>`${C.identifier}: ${d7(o,`parameter ${C.identifier}`,C.type,a[E])}`).join(\", \")})${i===zr?\" \":` -> ${d7(o,\"return type\",P.ret?.type,i)} `}`,x=y.slice(P.range.end)}return o.addDeclaration(`${c}fn ${d}${b}${x}`,d),Je(d,i,\"runtime\")}let u=Fx(e),p=u?.externals();p&&this.setExternals(\"pluginProvided\",p);let _=u?.ast;if(!_)throw new Error(\"Missing metadata for tgpu.fn function body (either missing 'use gpu' directive, or misconfigured `unplugin-typegpu`)\");let f=_.params[1];f&&f.type===\"i\"&&t!==\"normal\"&&this.setExternals(\"out\",{[f.name]:_n(i)});let{code:h,returnType:g}=o.resolveFunction({functionType:t,name:d,workgroupSize:s,argTypes:a,entryInput:l,params:_.params,returnType:i,body:_.body,externalMap:a7(r)});return o.addDeclaration(h,d),Je(d,g,\"runtime\")}}}function c7(e,t){return new RegExp(`\\\\b${e}\\\\b`).test(t)}function d7(e,t,s,r){let o=e.resolve(r).value.replace(/\\s/g,\"\");if(!s)return o;let a=s.replace(/\\s/g,\"\");if(a!==o)throw new Error(`Type mismatch between TGPU shell and WGSL code string: ${t}, JS type \"${o}\", WGSL type \"${a}\".`);return s}function Rh(e,...t){return dH(e)?uH(e,...t):e}function dH(e){return Array.isArray(e)&&\"raw\"in e&&Array.isArray(e.raw)&&e.raw.every(t=>typeof t==\"string\")}function uH(e,...t){return e.slice(1).reduce((s,r,o)=>`${s}${t[o]}${r}`,e[0])}function nr(e,t){if(typeof e==\"function\")return oE(e,[]);let s=e,r={[ve]:!0,argTypes:s,returnType:t??zr};return Object.assign((a,...i)=>pH(r,Rh(a,...i)),r)}function pb(e){return _s(e)&&e?.resourceType===\"function\"}function mb(e){return _s(e)&&e?.resourceType===\"generic-function\"}function rE([e,t]){return`${ct(e)??\"<unnamed>\"}=${t}`}function pH(e,t){let s=[],r;mb(t)?(s=t[ya]?.pairs??[],r=t[ve].inner):r=t;let o=Ed(r,\"normal\"),a={shell:e,resourceType:\"function\",[ve]:{implementation:r},$uses(c){return o.setExternals(\"userProvided\",c),this},[Mo]:o,$name(c){return es(this,c),this},with:Tn((c,d)=>_b(l,[[Qu(c)||Zu(c)?c.slot:c,d]])),[Ws](c){return typeof r==\"string\"&&(JX(r,e.argTypes,o),eE(r,e.returnType,o)),o.resolve(c,e.argTypes,e.returnType)}},i=je({name:void 0,noComptime:!0,signature:{argTypes:e.argTypes,returnType:e.returnType},normalImpl:(...c)=>wN(()=>{try{if(typeof r==\"string\")throw new Error(\"Cannot execute on the CPU functions constructed with raw WGSL\");let d=c.map((p,_)=>rc(e.argTypes[_],p)),u=r(...d);return rc(e.returnType,u)}catch(d){throw d instanceof Cw?d.appendToTrace(l):new Cw(d,[l])}}),codegenImpl:(c,d)=>c.withResetIndentLevel(()=>Ne`${c.resolve(l).value}(${d})`),sideEffects:!0}),l=Object.assign(i,a);return Object.defineProperty(l,\"toString\",{value(){return`fn:${ct(o)??\"<unnamed>\"}`}}),s.length>0?_b(l,s):l}function _b(e,t){let s={resourceType:\"function\",shell:e.shell,[ve]:{implementation:e[ve].implementation},[ya]:{inner:e,pairs:t},$uses(i){return e.$uses(i),this},$name(i){return es(this,i),this},with:Tn((i,l)=>{let c=Qu(i)||Zu(i)?i.slot:i;return _b(e,[...t,[c,l]])})},r=je({name:void 0,noComptime:!0,signature:{argTypes:e.shell.argTypes,returnType:e.shell.returnType},normalImpl:e,codegenImpl:(i,l)=>i.withResetIndentLevel(()=>Ne`${i.resolve(o).value}(${l})`),sideEffects:!0}),o=Object.assign(r,s);Object.defineProperty(o,\"toString\",{value(){return`fn:${ct(this)??\"<unnamed>\"}[${t.map(rE).join(\", \")}]`}});let a=ct(e);return a&&es(o,a),o}function oE(e,t){let s={[ve]:{inner:e},resourceType:\"generic-function\",[ya]:t.length>0?{inner:e,pairs:t}:void 0,$name(i){return es(this,i),ct(e)||es(e,i),this},[Ws](i){return i.resolve(e)},with(i,l){let c=Qu(i)||Zu(i)?i.slot:i;return oE(e,[...t,[c,l]])}},o=Object.assign((...i)=>e(...i),s),a=ct(e);return a&&es(o,a),Object.defineProperty(o,\"toString\",{value(){let i=ct(o)??\"<unnamed>\";return t.length>0?`fn*:${i}[${t.map(rE).join(\", \")}]`:`fn*:${i}`}}),o}var mH={block:0,binaryExpr:1,assignmentExpr:2,logicalExpr:3,unaryExpr:4,numericLiteral:5,call:6,memberAccess:7,indexAccess:8,return:10,if:11,let:12,const:13,for:14,while:15,continue:16,break:17,forOf:18,arrayExpr:100,preUpdate:101,postUpdate:102,stringLiteral:103,objectExpr:104,conditionalExpr:105},gm={identifier:\"i\",destructuredObject:\"d\"},aE=ko({FuncParameterType:()=>gm,NodeTypeCatalog:()=>mH}),S3=e=>({argTypes:[e],returnType:e}),P3=(...e)=>{let t=sa(e)??e;return{argTypes:t,returnType:t[0]}},Rs=e=>(...t)=>{let s=sa(t,e);if(!s)throw new jr(t,e);return{argTypes:s,returnType:s[0]}};function nE(e){return(t,...s)=>{let r=t;for(let o of s)r=e(r,o);return r}}function iE(e){return(t,[s,...r])=>{let o=Ne`${s}`;for(let a of r)o=Ne`${e}(${o}, ${a})`;return o}}var _H=[n,Vr,fd],Wm=[ps,Zs,qt,To,Pa,So],zs=[..._H,...Wm],fH=[j,M],vH=[ir,Br,sr,$s,Ps,Is],ul=[...fH,...vH];function hH(e){return Bs(Math.abs,[e])}var re=je({name:\"abs\",signature:S3,normalImpl:hH,codegenImpl:(e,[t])=>Ne`abs(${t})`,sideEffects:!1});function gH(e){return Bs(Math.acos,[e])}var yH=je({name:\"acos\",signature:Rs(zs),normalImpl:gH,codegenImpl:(e,[t])=>Ne`acos(${t})`,sideEffects:!1});function bH(e){return Bs(Math.acosh,[e])}var xH=je({name:\"acosh\",signature:Rs(zs),normalImpl:bH,codegenImpl:(e,[t])=>Ne`acosh(${t})`,sideEffects:!1});function AH(e){return Bs(Math.asin,[e])}var fb=je({name:\"asin\",signature:Rs(zs),normalImpl:AH,codegenImpl:(e,[t])=>Ne`asin(${t})`,sideEffects:!1});function wH(e){return Bs(Math.asinh,[e])}var TH=je({name:\"asinh\",signature:Rs(zs),normalImpl:wH,codegenImpl:(e,[t])=>Ne`asinh(${t})`,sideEffects:!1});function SH(e){return Bs(Math.atan,[e])}var PH=je({name:\"atan\",signature:Rs(zs),normalImpl:SH,codegenImpl:(e,[t])=>Ne`atan(${t})`,sideEffects:!1});function CH(e){return Bs(Math.atanh,[e])}var MH=je({name:\"atanh\",signature:Rs(zs),normalImpl:CH,codegenImpl:(e,[t])=>Ne`atanh(${t})`,sideEffects:!1});function EH(e,t){return Bs(Math.atan2,[e,t])}var Xt=je({name:\"atan2\",signature:Rs(zs),normalImpl:EH,codegenImpl:(e,[t,s])=>Ne`atan2(${t}, ${s})`,sideEffects:!1});function kH(e){return Bs(Math.ceil,[e])}var nl=je({name:\"ceil\",signature:Rs(zs),normalImpl:kH,codegenImpl:(e,[t])=>Ne`ceil(${t})`,sideEffects:!1});function BH(e,t,s){return Bs(AM,[e,t,s])}var G=je({name:\"clamp\",signature:P3,normalImpl:BH,codegenImpl:(e,[t,s,r])=>Ne`clamp(${t}, ${s}, ${r})`,sideEffects:!1});function zH(e){return Bs(Math.cos,[e])}var pe=je({name:\"cos\",signature:Rs(zs),normalImpl:zH,codegenImpl:(e,[t])=>Ne`cos(${t})`,sideEffects:!1});function RH(e){return Bs(Math.cosh,[e])}var DH=je({name:\"cosh\",signature:Rs(zs),normalImpl:RH,codegenImpl:(e,[t])=>Ne`cosh(${t})`,sideEffects:!1}),LH=je({name:\"countLeadingZeros\",signature:Rs(ul),normalImpl:\"CPU implementation for countLeadingZeros not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`countLeadingZeros(${t})`,sideEffects:!1});je({name:\"countOneBits\",signature:Rs(ul),normalImpl:\"CPU implementation for countOneBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`countOneBits(${t})`,sideEffects:!1});je({name:\"countTrailingZeros\",signature:Rs(ul),normalImpl:\"CPU implementation for countTrailingZeros not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`countTrailingZeros(${t})`,sideEffects:!1});var u7=je({name:\"cross\",signature:Rs([Zs,Pa]),normalImpl:(e,t)=>$a.cross[e.kind](e,t),codegenImpl:(e,[t,s])=>Ne`cross(${t}, ${s})`,sideEffects:!1});function UH(e){if(typeof e==\"number\")return e*180/Math.PI;throw new vo(\"CPU implementation for degrees on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}je({name:\"degrees\",signature:Rs(zs),normalImpl:UH,codegenImpl:(e,[t])=>Ne`degrees(${t})`,sideEffects:!1});je({name:\"determinant\",signature:e=>{if(!(e.type===\"mat2x2f\"||e.type===\"mat3x3f\"||e.type===\"mat4x4f\"))throw new jr([e],[yl,bl,Eo]);return{argTypes:[e],returnType:n}},normalImpl:\"CPU implementation for determinant not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`determinant(${t})`,sideEffects:!1});function OH(e,t){return typeof e==\"number\"&&typeof t==\"number\"?Math.abs(e-t):he(As(e,t))}je({name:\"distance\",signature:(...e)=>{let t=sa(e,zs);if(!t)throw new jr(e,zs);return{argTypes:t,returnType:Gx(t[0])?Vr:n}},normalImpl:OH,codegenImpl:(e,[t,s])=>Ne`distance(${t}, ${s})`,sideEffects:!1});var Se=je({name:\"dot\",signature:(...e)=>({argTypes:e,returnType:e[0].primitive}),normalImpl:(e,t)=>$a.dot[e.kind](e,t),codegenImpl:(e,[t,s])=>Ne`dot(${t}, ${s})`,sideEffects:!1});je({name:\"dot4U8Packed\",signature:{argTypes:[M,M],returnType:M},normalImpl:\"CPU implementation for dot4U8Packed not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s])=>Ne`dot4U8Packed(${t}, ${s})`,sideEffects:!1});je({name:\"dot4I8Packed\",signature:{argTypes:[M,M],returnType:j},normalImpl:\"CPU implementation for dot4I8Packed not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s])=>Ne`dot4I8Packed(${t}, ${s})`,sideEffects:!1});function GH(e){return Bs(Math.exp,[e])}var Tt=je({name:\"exp\",signature:Rs(zs),normalImpl:GH,codegenImpl:(e,[t])=>Ne`exp(${t})`,sideEffects:!1});function FH(e){return Bs(t=>2**t,[e])}var Ia=je({name:\"exp2\",signature:Rs(zs),normalImpl:FH,codegenImpl:(e,[t])=>Ne`exp2(${t})`,sideEffects:!1}),p7=je({name:\"extractBits\",signature:(e,t,s)=>{let r=sa([e],ul)?.[0];if(!r)throw new jr([e],ul);return{argTypes:[r,M,M],returnType:r}},normalImpl:\"CPU implementation for extractBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s,r])=>Ne`extractBits(${t}, ${s}, ${r})`,sideEffects:!1});je({name:\"faceForward\",signature:Rs(Wm),normalImpl:\"CPU implementation for faceForward not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s,r])=>Ne`faceForward(${t}, ${s}, ${r})`,sideEffects:!1});je({name:\"firstLeadingBit\",signature:S3,normalImpl:\"CPU implementation for firstLeadingBit not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`firstLeadingBit(${t})`,sideEffects:!1});je({name:\"firstTrailingBit\",signature:Rs(ul),normalImpl:\"CPU implementation for firstTrailingBit not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`firstTrailingBit(${t})`,sideEffects:!1});function IH(e){return Bs(Math.floor,[e])}var ie=je({name:\"floor\",signature:Rs(zs),normalImpl:IH,codegenImpl:(e,[t])=>Ne`floor(${t})`,sideEffects:!1});function YH(e,t,s){if(typeof e==\"number\")return e*t+s;throw new vo(\"CPU implementation for fma on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}je({name:\"fma\",signature:Rs(zs),normalImpl:YH,codegenImpl:(e,[t,s,r])=>Ne`fma(${t}, ${s}, ${r})`,sideEffects:!1});function WH(e){return Bs(t=>t-Math.floor(t),[e])}var Re=je({name:\"fract\",signature:Rs(zs),normalImpl:WH,codegenImpl:(e,[t])=>Ne`fract(${t})`,sideEffects:!1}),qH={f32:Fo({fract:n,exp:j}),f16:Fo({fract:Vr,exp:j}),abstractFloat:Fo({fract:fd,exp:i_}),vec2f:Fo({fract:ps,exp:ir}),vec3f:Fo({fract:Zs,exp:Br}),vec4f:Fo({fract:qt,exp:sr}),vec2h:Fo({fract:To,exp:ir}),vec3h:Fo({fract:Pa,exp:Br}),vec4h:Fo({fract:So,exp:sr})};je({name:\"frexp\",normalImpl:\"CPU implementation for frexp not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",signature:e=>{let t=qH[e.type];if(!t)throw new jr([e],zs);return{argTypes:[e],returnType:t}},codegenImpl:(e,[t])=>Ne`frexp(${t})`,sideEffects:!1});je({name:\"insertBits\",signature:(e,t,s,r)=>{let o=sa([e,t],ul);if(!o)throw new jr([e,t],ul);return{argTypes:[...o,M,M],returnType:o[0]}},normalImpl:\"CPU implementation for insertBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s,r,o])=>Ne`insertBits(${t}, ${s}, ${r}, ${o})`,sideEffects:!1});function VH(e){if(typeof e==\"number\")return 1/Math.sqrt(e);throw new vo(\"CPU implementation for inverseSqrt on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}je({name:\"inverseSqrt\",signature:Rs(zs),normalImpl:VH,codegenImpl:(e,[t])=>Ne`inverseSqrt(${t})`,sideEffects:!1});je({name:\"ldexp\",signature:(e,t)=>{switch(e.type){case\"abstractFloat\":return{argTypes:[e,i_],returnType:e};case\"f32\":case\"f16\":return{argTypes:[e,j],returnType:e};case\"vec2f\":case\"vec2h\":return{argTypes:[e,ir],returnType:e};case\"vec3f\":case\"vec3h\":return{argTypes:[e,Br],returnType:e};case\"vec4f\":case\"vec4h\":return{argTypes:[e,sr],returnType:e};default:throw new Error(`Unsupported data type for ldexp: ${e.type}. Supported types are abstractFloat, f32, f16, vec2f, vec2h, vec3f, vec3h, vec4f, vec4h.`)}},normalImpl:\"CPU implementation for ldexp not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s])=>Ne`ldexp(${t}, ${s})`,sideEffects:!1});function NH(e){return typeof e==\"number\"?Math.abs(e):$a.length[e.kind](e)}var he=je({name:\"length\",signature:e=>{let t=sa([e],zs);if(!t)throw new jr([e],zs);return{argTypes:t,returnType:Gx(t[0])?Vr:n}},normalImpl:NH,codegenImpl:(e,[t])=>Ne`length(${t})`,sideEffects:!1});function $H(e){return Bs(Math.log,[e])}var XH=je({name:\"log\",signature:Rs(zs),normalImpl:$H,codegenImpl:(e,[t])=>Ne`log(${t})`,sideEffects:!1});function HH(e){return Bs(Math.log2,[e])}var vb=je({name:\"log2\",signature:Rs(zs),normalImpl:HH,codegenImpl:(e,[t])=>Ne`log2(${t})`,sideEffects:!1});function jH(e,t){return Bs(Math.max,[e,t])}var B=je({name:\"max\",signature:P3,normalImpl:nE(jH),codegenImpl:iE(\"max\"),sideEffects:!1});function KH(e,t){return Bs(Math.min,[e,t])}var ye=je({name:\"min\",signature:P3,normalImpl:nE(KH),codegenImpl:iE(\"min\"),sideEffects:!1});function QH(e,t,s){return Bs((r,o,a)=>r*(1-a)+o*a,[e,...u3([t,s])])}var K=je({name:\"mix\",signature:(e,t,s)=>{if(e.type.startsWith(\"vec\")&&!s.type.startsWith(\"vec\")){let o=sa([s],[e.primitive]);if(!o)throw new jr([s],[e.primitive]);return{argTypes:[e,t,o[0]],returnType:e}}let r=sa([e,t,s],zs);if(!r)throw new jr([e,t,s],zs);return{argTypes:r,returnType:r[0]}},normalImpl:QH,codegenImpl:(e,[t,s,r])=>Ne`mix(${t}, ${s}, ${r})`,sideEffects:!1}),ZH={f32:Fo({fract:n,whole:n}),f16:Fo({fract:Vr,whole:Vr}),abstractFloat:Fo({fract:fd,whole:fd}),vec2f:Fo({fract:ps,whole:ps}),vec3f:Fo({fract:Zs,whole:Zs}),vec4f:Fo({fract:qt,whole:qt}),vec2h:Fo({fract:To,whole:To}),vec3h:Fo({fract:Pa,whole:Pa}),vec4h:Fo({fract:So,whole:So})};je({name:\"modf\",signature:e=>{let t=ZH[e.type];if(!t)throw new Error(`Unsupported data type for modf: ${e.type}. Supported types are f32, f16, abstractFloat, vec2f, vec3f, vec4f, vec2h, vec3h, vec4h.`);return{argTypes:[e],returnType:t}},normalImpl:\"CPU implementation for modf not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`modf(${t})`,sideEffects:!1});var vs=je({name:\"normalize\",signature:Rs(Wm),normalImpl:e=>{let t=he(e);return Bs(s=>s/t,[e])},codegenImpl:(e,[t])=>Ne`normalize(${t})`,sideEffects:!1});function JH(e,t){return Bs((s,r)=>s**r,[e,t])}var vt=je({name:\"pow\",signature:Rs(zs),normalImpl:JH,codegenImpl:(e,[t,s])=>Ne`pow(${t}, ${s})`,sideEffects:!1});je({name:\"quantizeToF16\",signature:e=>{let t=[ps,Zs,qt,n],s=sa([e],t)?.[0];if(!s)throw new jr([e],t);return{argTypes:[s],returnType:s}},normalImpl:\"CPU implementation for quantizeToF16 not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`quantizeToF16(${t})`,sideEffects:!1});function ej(e){if(typeof e==\"number\")return e*Math.PI/180;throw new vo(\"CPU implementation for radians on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}var m7=je({name:\"radians\",signature:Rs(zs),normalImpl:ej,codegenImpl:(e,[t])=>Ne`radians(${t})`,sideEffects:!1});je({name:\"reflect\",signature:(...e)=>{let t=sa(e,Wm);if(!t)throw new jr(e,Wm);return{argTypes:t,returnType:t[0]}},normalImpl:(e,t)=>As(e,ra(2*Se(t,e),t)),codegenImpl:(e,[t,s])=>Ne`reflect(${t}, ${s})`,sideEffects:!1});je({name:\"refract\",normalImpl:\"CPU implementation for refract not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t,s,r])=>Ne`refract(${t}, ${s}, ${r})`,signature:(e,t,s)=>({argTypes:[e,t,Gx(e)?Vr:n],returnType:e}),sideEffects:!1});je({name:\"reverseBits\",signature:Rs(ul),normalImpl:\"CPU implementation for reverseBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`reverseBits(${t})`,sideEffects:!1});function tj(e){if(typeof e==\"number\"){let t=Math.floor(e);return e===t+.5?t%2===0?t:t+1:Math.round(e)}throw new vo(\"CPU implementation for round on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}var Ut=je({name:\"round\",signature:Rs(zs),normalImpl:tj,codegenImpl:(e,[t])=>Ne`round(${t})`,sideEffects:!1});function sj(e){if(typeof e==\"number\")return Math.max(0,Math.min(1,e));throw new vo(\"CPU implementation for saturate on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}je({name:\"saturate\",signature:Rs(zs),normalImpl:sj,codegenImpl:(e,[t])=>e.gen.emitCall(\"saturate\",[],[t]),sideEffects:!1});function rj(e){return Bs(Math.sign,[e])}var no=je({name:\"sign\",signature:e=>{let t=[...zs,j,ir,Br,sr],s=sa([e],t)?.[0];if(!s)throw new jr([e],t);return{argTypes:[s],returnType:s}},normalImpl:rj,codegenImpl:(e,[t])=>Ne`sign(${t})`,sideEffects:!1});function oj(e){return Bs(Math.sin,[e])}var ae=je({name:\"sin\",signature:Rs(zs),normalImpl:oj,codegenImpl:(e,[t])=>Ne`sin(${t})`,sideEffects:!1});function aj(e){return Bs(Math.sinh,[e])}var nj=je({name:\"sinh\",signature:Rs(zs),normalImpl:aj,codegenImpl:(e,[t])=>Ne`sinh(${t})`,sideEffects:!1});function ij(e,t,s){return Bs(t$,[e,t,s])}var be=je({name:\"smoothstep\",signature:Rs(zs),normalImpl:ij,codegenImpl:(e,[t,s,r])=>Ne`smoothstep(${t}, ${s}, ${r})`,sideEffects:!1});function lj(e){return Bs(Math.sqrt,[e])}var Ue=je({name:\"sqrt\",signature:Rs(zs),normalImpl:lj,codegenImpl:(e,[t])=>Ne`sqrt(${t})`,sideEffects:!1});function cj(e,t){if(typeof e==\"number\")return e<=t?1:0;throw new vo(\"CPU implementation for step on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}var Et=je({name:\"step\",signature:Rs(zs),normalImpl:cj,codegenImpl:(e,[t,s])=>Ne`step(${t}, ${s})`,sideEffects:!1});function dj(e){if(typeof e==\"number\")return Math.tan(e);throw new vo(\"CPU implementation for tan on vectors not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\")}var hb=je({name:\"tan\",signature:Rs(zs),normalImpl:dj,codegenImpl:(e,[t])=>Ne`tan(${t})`,sideEffects:!1});function uj(e){return Bs(Math.tanh,[e])}var pj=je({name:\"tanh\",signature:Rs(zs),normalImpl:uj,codegenImpl:(e,[t])=>Ne`tanh(${t})`,sideEffects:!1});function mj(e){let t=gh(e)(),s=e.columns,r=t.columns;if(s.length===2)r[0][0]=s[0][0],r[0][1]=s[1][0],r[1][0]=s[0][1],r[1][1]=s[1][1];else if(s.length===3){r[0][0]=s[0][0],r[0][1]=s[1][0],r[0][2]=s[2][0],r[1][0]=s[0][1],r[1][1]=s[1][1],r[1][2]=s[2][1];let o=r[2];o[0]=s[0][2],o[1]=s[1][2],o[2]=s[2][2]}else if(s.length===4){r[0][0]=s[0][0],r[0][1]=s[1][0],r[0][2]=s[2][0],r[0][3]=s[3][0],r[1][0]=s[0][1],r[1][1]=s[1][1],r[1][2]=s[2][1],r[1][3]=s[3][1];let o=r[2];o[0]=s[0][2],o[1]=s[1][2],o[2]=s[2][2],o[3]=s[3][2];let a=r[3];a[0]=s[0][3],a[1]=s[1][3],a[2]=s[2][3],a[3]=s[3][3]}else Im(s,\"std/numeric.ts#cpuTranspose\");return t}je({name:\"transpose\",signature:S3,normalImpl:mj,codegenImpl:(e,[t])=>Ne`transpose(${t})`,sideEffects:!1});var _j=je({name:\"trunc\",signature:Rs(zs),normalImpl:\"CPU implementation for trunc not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues\",codegenImpl:(e,[t])=>Ne`trunc(${t})`,sideEffects:!1});function fj(e,t){if(typeof e!=\"number\"||typeof t!=\"number\")throw new Error(\"std.intdiv called with invalid arguments.\");return Math.trunc(Math.trunc(e)/Math.trunc(t))}je({name:\"intdiv\",signature:(e,t)=>{let s=sa([e,t],[M,j]);if(!s)throw new jr([e,t],[M,j,i_]);return{argTypes:s,returnType:s[0]}},normalImpl:fj,codegenImpl:(e,[t,s])=>e.gen.emitBinaryOp(t,\"/\",s),sideEffects:!1});function Q1(e){return Jo(e)||Kn(e)?gh(e)(e):Array.isArray(e)?e.map(Q1):typeof e==\"object\"&&e!==null?Object.fromEntries(Object.entries(e).map(([t,s])=>[t,Q1(s)])):e}je({name:\"copy\",signature:e=>({argTypes:[e],returnType:e}),normalImpl:Q1,codegenImpl(e,[t]){return Ne`${t}`},sideEffects:!1});function sp(e){return e.type.includes(\"2\")?Wa:e.type.includes(\"3\")?qa:Va}var vj=je({name:\"allEq\",signature:(...e)=>({argTypes:e,returnType:ys}),normalImpl:(e,t)=>M3(m_(e,t)),codegenImpl:(e,[t,s])=>Ne`all(${t} == ${s})`,sideEffects:!1}),m_=(e,t)=>Th((s,r)=>s===r,[e,t]),hj=je({name:\"eq\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:m_,codegenImpl:(e,[t,s])=>Ne`(${t} == ${s})`,sideEffects:!1}),gj=je({name:\"ne\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:(e,t)=>Io(m_(e,t)),codegenImpl:(e,[t,s])=>Ne`(${t} != ${s})`,sideEffects:!1}),Dh=(e,t)=>Th((s,r)=>s<r,[e,t]),yj=je({name:\"lt\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:Dh,codegenImpl:(e,[t,s])=>Ne`(${t} < ${s})`,sideEffects:!1}),qm=je({name:\"le\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:(e,t)=>C3(Dh(e,t),m_(e,t)),codegenImpl:(e,[t,s])=>Ne`(${t} <= ${s})`,sideEffects:!1}),bj=je({name:\"gt\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:(e,t)=>lE(Io(Dh(e,t)),Io(m_(e,t))),codegenImpl:(e,[t,s])=>Ne`(${t} > ${s})`,sideEffects:!1}),gb=je({name:\"ge\",signature:(...e)=>({argTypes:e,returnType:sp(e[0])}),normalImpl:(e,t)=>Io(Dh(e,t)),codegenImpl:(e,[t,s])=>Ne`(${t} >= ${s})`,sideEffects:!1});function Io(e){if(typeof e==\"boolean\")return!e;if(!KV(e))throw new Error(\"'std.not' requires a boolean or boolean vector.\");switch(e.length){case 2:return Wa(Io(e.x),Io(e.y));case 3:return qa(Io(e.x),Io(e.y),Io(e.z));case 4:return Va(Io(e.x),Io(e.y),Io(e.z),Io(e.w))}}var xj=je({name:\"not\",signature:e=>{if(!ql(e)&&!Ox(e))throw new jr([e],[ys,Wa,qa,Va]);return{argTypes:[e],returnType:e}},normalImpl:Io,codegenImpl:(e,[t])=>Ne`!(${t})`,sideEffects:!1}),C3=(e,t)=>Th((s,r)=>s||r,[e,t]);je({name:\"or\",signature:(...e)=>({argTypes:e,returnType:e[0]}),normalImpl:C3,codegenImpl:(e,[t,s])=>Ne`(${t} | ${s})`,sideEffects:!1});var lE=(e,t)=>Io(C3(Io(e),Io(t)));je({name:\"and\",signature:(...e)=>({argTypes:e,returnType:e[0]}),normalImpl:lE,codegenImpl:(e,[t,s])=>Ne`(${t} & ${s})`,sideEffects:!1});var M3=e=>$a.all[e.kind](e),Aj=je({name:\"all\",signature:(...e)=>({argTypes:e,returnType:ys}),normalImpl:M3,codegenImpl:(e,[t])=>Ne`all(${t})`,sideEffects:!1});je({name:\"any\",signature:(...e)=>({argTypes:e,returnType:ys}),normalImpl:e=>!M3(Io(e)),codegenImpl:(e,[t])=>Ne`any(${t})`,sideEffects:!1});je({name:\"isCloseTo\",signature:(...e)=>({argTypes:e,returnType:ys}),normalImpl:(e,t,s=.01)=>{let r=Th((o,a)=>Math.abs(o-a)<s,[e,t]);return typeof r==\"boolean\"?r:Aj(r)},codegenImpl:(e,[t,s,r=Je(.01,n,\"constant\",!1)])=>fm(t)&&fm(s)?Ne`(abs(f32(${t}) - f32(${s})) <= ${r})`:!fm(t)&&!fm(s)?Ne`all(abs(${t} - ${s}) <= (${t} - ${t}) + ${r})`:\"false\",sideEffects:!1});function wj(e,t,s){return typeof s==\"boolean\"?Q1(s?t:e):Bs((r,o,a)=>a?o:r,[e,t,s])}var k1=[n,Vr,j,M,ys,ps,Zs,qt,To,Pa,So,ir,Br,sr,$s,Ps,Is,Wa,qa,Va],N=je({name:\"select\",signature:(e,t,s)=>{let[r,o]=sa([e,t],k1)??[e,t];return{argTypes:[r,o,s],returnType:r}},normalImpl:wj,codegenImpl:(e,[t,s,r])=>{let o=e.gen.emitCall(\"select\",[],[t,s,r]);if(!k1.includes(t.dataType)||!k1.includes(s.dataType))throw new Error(`'${o}' is invalid, std.select requires both branches to be either scalars or vectors.`);if(t.dataType!==s.dataType){let a=e.resolve(t.dataType),i=e.resolve(s.dataType);throw new Error(`'${o}' is invalid, std.select requires both branches to be the same type, got [${a.value}, ${i.value}].`)}return o},sideEffects:!1});function Lh(e){return typeof e?.format==\"string\"}function Tj(e,t){let s=[];if(o_(e)){if(!Lh(t))throw new Error(\"Shader expected a single attribute, not a record of attributes to be passed in.\");return s.push(t._layout),{usedVertexLayouts:s,bufferDefinitions:[{arrayStride:t._layout.stride,stepMode:t._layout.stepMode,attributes:[{format:t.format,offset:t.offset,shaderLocation:sc(e)??0}]}]}}let r=[],o=new WeakMap,a=0;for(let[i,l]of Object.entries(e)){if(wi(l))continue;let c=t[i];if(!c)throw new Error(`An attribute by the name of '${i}' was not provided to the shader.`);let d=c._layout,u=o.get(d);u||(s.push(d),u=[],r.push({arrayStride:d.stride,stepMode:d.stepMode,attributes:u}),o.set(d,u)),a=sc(l)??a,u.push({format:c.format,offset:c.offset,shaderLocation:a++})}return{usedVertexLayouts:s,bufferDefinitions:r}}var Sj=[\"bool\",\"f32\",\"f16\",\"i32\",\"u32\",\"vec2f\",\"vec3f\",\"vec4f\",\"vec2h\",\"vec3h\",\"vec4h\",\"vec2i\",\"vec3i\",\"vec4i\",\"vec2u\",\"vec3u\",\"vec4u\",\"vec2<bool>\",\"vec3<bool>\",\"vec4<bool>\",\"mat2x2f\",\"mat3x3f\",\"mat4x4f\",\"texture_external\"];function Pj(e){return Sj.includes(e.type)}function yb(e,[t,s]){return`  ${Ym(s)}${t}: ${e.resolve(s).value},\n`}function Cj(e,t){if(t[ve].isAbstruct)throw new Error(\"Cannot resolve abstract struct types to WGSL.\");let s=e.makeUniqueIdentifier(ct(t),\"global\");return e.addDeclaration(`struct ${s} {\n${Object.entries(t.propTypes).map(r=>yb(e,r)).join(\"\")}}`,s),s}function Mj(e,t){let s=e.makeUniqueIdentifier(ct(t),\"global\");return e.addDeclaration(`struct ${s} {\n${Object.entries(t.propTypes).map(r=>Lh(r[1])?yb(e,[r[0],dl[r[1].format]]):yb(e,r)).join(\"\")}\n}`,s),s}function Ej(e,t){let s=e.resolve(t.elementType).value;return t.elementCount===0?`array<${s}>`:`array<${s}, ${t.elementCount}>`}function kj(e,t){let s=e.resolve(Lh(t.elementType)?dl[t.elementType.format]:t.elementType).value;return t.elementCount===0?`array<${s}>`:`array<${s}, ${t.elementCount}>`}function cE(e,t){if(yh(t))return t.type===\"unstruct\"?Mj(e,t):t.type===\"disarray\"?kj(e,t):t.type===\"loose-decorated\"?e.resolve(Lh(t.inner)?dl[t.inner.format]:t.inner).value:e.resolve(dl[t.type]).value;if(Pj(t))return t.type;if(t.type===\"struct\")return Cj(e,t);if(t.type===\"array\")return Ej(e,t);if(t.type===\"atomic\")return`atomic<${cE(e,t.inner)}>`;if(t.type===\"decorated\")return e.resolve(t.inner).value;if(t.type===\"ptr\")return t.addressSpace===\"storage\"?`ptr<storage, ${e.resolve(t.inner).value}, ${t.access===\"read-write\"?\"read_write\":t.access}>`:`ptr<${t.addressSpace}, ${e.resolve(t.inner).value}>`;if(t.type===\"abstractInt\"||t.type===\"abstractFloat\"||t.type===\"void\"||t.type===\"u16\")throw new Error(`${t.type} has no representation in WGSL`);if(C1(t))return`${t.type}<${t.format}, ${u_[t.access]}>`;if(DM(t))return t.type.startsWith(\"texture_depth\")?t.type:`${t.type}<${t.sampleType.type}>`;if(I$(t)||F$(t))return t.type;Im(t,\"resolveData\")}var dE=class{[ve]=!0;snippet;constructor(e){this.snippet=e}[Ws](e){return e.resolveSnippet(this.snippet)}},uE=(()=>{function e(s){return s}let t=e;return es(t,\"unroll\"),t.toString=()=>\"unroll\",t[ve]=!0,t[mo]={call(s,[r]){return KC(new dE(r),r)}},t})(),_7=new Map([[Math.abs,re],[Math.acos,yH],[Math.acosh,xH],[Math.asin,fb],[Math.asinh,TH],[Math.atan,PH],[Math.atan2,Xt],[Math.atanh,MH],[Math.ceil,nl],[Math.cos,pe],[Math.cosh,DH],[Math.exp,Tt],[Math.floor,ie],[Math.fround,n],[Math.clz32,LH],[Math.trunc,_j],[Math.log,XH],[Math.log2,vb],[Math.pow,vt],[Math.sign,no],[Math.sin,ae],[Math.sinh,nj],[Math.sqrt,Ue],[Math.tan,hb],[Math.tanh,pj],[Math.max,B],[Math.min,ye]]),Bj=()=>[console.log,console.debug,console.info,console.warn,console.error,console.clear],f7=e=>ta(e)&&io(e.inner)?e.inner.elementCount:0,zj=je({name:\"arrayLength\",signature:e=>{let t=ta(e)?e:tM(e);return{argTypes:[t],returnType:f7(t)>0?i_:M}},normalImpl:e=>rM(e)?e.$.length:e.length,codegenImpl(e,[t]){let s=f7(t.dataType);return s>0?`${s}`:e.gen.emitCall(\"arrayLength\",[],[t])},sideEffects:!1});Tn((e,t,s=1)=>{if(t===void 0&&(t=e,e=0),!Number.isInteger(e))throw new Error(`'start' must be an integer, got ${e}`);if(!Number.isInteger(t))throw new Error(`'end' must be an integer, got ${t}`);if(!Number.isInteger(s)||s===0)throw new Error(`'step' must be a non-zero integer, got ${s}`);let r=[];if(r.start=e,r.end=t,r.step=s,Math.sign(s)!==Math.sign(t-e))return r;if(s>0)for(let o=e;o<t;o+=s)r.push(o);if(s<0)for(let o=e;o>t;o+=s)r.push(o);return r});function bb(e){return Array.isArray(e)&&\"start\"in e&&\"end\"in e&&\"step\"in e}function Rj(e){return e.origin===\"constant-immutable-def\"?\"const\":\"let\"}function v7(e,t){let s=Bh(e,t);if(!s)throw new jt(\"`for ... of ...` loops only support array or vector iterables\");return s}function Dj(e,t){let s=e.dataType;if(s===Ns)throw new jt(Ne`The elements in iterable ${t} are of unknown type`);if(jn(e.dataType)||e.origin===\"runtime\"||e.origin===\"constant\"||e.origin===\"constant-immutable-def\"||e.origin===\"runtime-immutable-def\")return s;if(!ta(s)){let r=Wx(e.origin,dn(s));kr(r!==void 0,`Creating pointer type from origin ${e.origin}`),s=r}return sM(s)}function Lj(e,t,s=!1){let{value:r,dataType:o}=t;if(bb(r)){let{start:i,end:l,step:c}=r,d=[i,l,c].every(u=>u>=0)?M:j;return{start:Je(i,d,\"constant\",!1),end:Je(l,d,\"constant\",!1),step:Je(c,d,\"constant\",!1),comparison:c<0?\">\":\"<\"}}if(!s&&!gi(t))throw new Error(\"`for ... of ...` loops only support std.range or iterables stored in variables.\\n-----\\nYou can wrap iterable with `tgpu.unroll(...)`. If iterable is known at comptime, the loop will be unrolled.\\n-----\");let a={start:Je(0,M,\"constant\",!1),step:Je(1,M,\"constant\",!1),comparison:\"<\"};if(io(o))return{...a,end:o.elementCount>0?Je(o.elementCount,M,\"constant\",!1):zj[mo].call(e,[t])};if(js(o))return{...a,end:Je(o.componentCount,M,\"constant\",!1)};if(s){if(Array.isArray(r))return{...a,end:Je(r.length,M,\"constant\",!1)};if(r instanceof Sm)return{...a,end:Je(r.elements.length,M,\"constant\",!1)}}throw new jt(\"`for ... of ...` loops only support array or vector iterables\")}var{NodeTypeCatalog:as}=aE;function ur(e){return mE(e)?Ko(e,\"\"):Qi(e,\"\")}function Qi(e,t){if(mE(e))return`${t}${Ko(e,t)};`;if(e[0]===as.block)return`{\n${e[1].map(s=>Qi(s,t+\"  \")).join(`\n`)}\n${t}}`;if(e[0]===as.return)return`${t}return${e[1]===void 0?\"\":` ${Ko(e[1],\"\")}`};`;if(e[0]===as.if){let s=`${t}if (${Ko(e[1],t)}) ${Qi(e[2],t)}`;return e[3]!==void 0?`${s} else ${Qi(e[3],t)}`:s}if(e[0]===as.let)return e[2]!==void 0?`${t}let ${e[1]} = ${Ko(e[2],t)};`:`${t}let ${e[1]};`;if(e[0]===as.const)return e[2]!==void 0?`${t}const ${e[1]} = ${Ko(e[2],t)};`:`${t}const ${e[1]};`;if(e[0]===as.for){let s=e[1]?Qi(e[1],\"\"):\";\",r=e[2]?Ko(e[2],t):\"\",o=e[3]?Qi(e[3],\"\"):\"\",a=Qi(e[4],t);return`${t}for (${s} ${r}; ${o.slice(0,-1)}) ${a}`}if(e[0]===as.while)return`${t}while (${Ko(e[1],t)}) ${Qi(e[2],t)}`;if(e[0]===as.continue)return`${t}continue;`;if(e[0]===as.break)return`${t}break;`;if(e[0]===as.forOf)return`${t}for (${e[1][0]===as.const?\"const\":\"let\"} ${e[1][1]} of ${Ko(e[2],t)}) ${Qi(e[3],t)}`;pE(e)}function Ko(e,t){if(typeof e==\"string\")return e;if(typeof e==\"boolean\")return`${e}`;if(e[0]===as.numericLiteral)return e[1];if(e[0]===as.stringLiteral)return JSON.stringify(e[1]);if(e[0]===as.arrayExpr)return`[${e[1].map(s=>Ko(s,t)).join(\", \")}]`;if(e[0]===as.binaryExpr)return`${nn(e[1],t)} ${e[2]} ${nn(e[3],t)}`;if(e[0]===as.assignmentExpr)return`${Ko(e[1],t)} ${e[2]} ${Ko(e[3],t)}`;if(e[0]===as.logicalExpr)return`${nn(e[1],t)} ${e[2]} ${nn(e[3],t)}`;if(e[0]===as.unaryExpr){let s=e[1].length>1?\" \":\"\";return`${e[1]}${s}${nn(e[2],t)}`}if(e[0]===as.call)return`${nn(e[1],t)}(${e[2].map(s=>Ko(s,t)).join(\", \")})`;if(e[0]===as.memberAccess)return Array.isArray(e[1])&&e[1][0]===as.numericLiteral?`(${Ko(e[1],t)}).${e[2]}`:`${nn(e[1],t)}.${e[2]}`;if(e[0]===as.indexAccess)return`${nn(e[1],t)}[${Ko(e[2],t)}]`;if(e[0]===as.preUpdate)return`${e[1]}${nn(e[2],t)}`;if(e[0]===as.postUpdate)return`${nn(e[2],t)}${e[1]}`;if(e[0]===as.objectExpr)return`{ ${Object.entries(e[1]).map(([s,r])=>`${s}: ${Ko(r,t)}`).join(\", \")} }`;if(e[0]===as.conditionalExpr)return`${nn(e[1],t)} ? ${nn(e[2],t)} : ${nn(e[3],t)}`;pE(e)}function pE(e){throw new Error(`'${JSON.stringify(e)}' was not handled by the stringify function.`)}function mE(e){return typeof e==\"string\"||typeof e==\"boolean\"||e[0]===as.numericLiteral||e[0]===as.stringLiteral||e[0]===as.arrayExpr||e[0]===as.binaryExpr||e[0]===as.assignmentExpr||e[0]===as.logicalExpr||e[0]===as.unaryExpr||e[0]===as.call||e[0]===as.memberAccess||e[0]===as.indexAccess||e[0]===as.preUpdate||e[0]===as.postUpdate||e[0]===as.objectExpr||e[0]===as.conditionalExpr}var Uj=[as.memberAccess,as.indexAccess,as.call,as.arrayExpr,as.stringLiteral,as.numericLiteral];function nn(e,t){let s=Ko(e,t);return typeof e==\"string\"||typeof e==\"boolean\"||Uj.includes(e[0])?s:`(${s})`}var{NodeTypeCatalog:Fs}=aE,Oj=[\"==\",\"!=\",\"===\",\"!==\",\"<\",\"<=\",\">\",\">=\",\"<<\",\">>\",\">>>\",\"+\",\"-\",\"*\",\"/\",\"%\",\"|\",\"^\",\"&\"],Gj=[\"&&\",\"||\",\"==\",\"!=\",\"===\",\"!==\",\"<\",\"<=\",\">\",\">=\"],Fj={\"===\":hj.toString(),\"!==\":gj.toString(),\"<\":yj.toString(),\"<=\":qm.toString(),\">\":bj.toString(),\">=\":gb.toString()},Ij=[\"<<\",\">>\",\"<<=\",\">>=\",\">>>\",\">>>=\"],Yj={\"===\":\"==\",\"!==\":\"!=\",\">>>\":\">>\",get in(){throw new Error(\"The `in` operator is unsupported in TypeGPU functions.\")},get instanceof(){throw new Error(\"The `instanceof` operator is unsupported in TypeGPU functions.\")},get\"|>\"(){throw new Error(\"The `|>` operator is unsupported in TypeGPU functions.\")},get\"??\"(){throw new Error(\"The `??` operator is unsupported in TypeGPU functions.\")},\">>>=\":\">>=\",get\"**=\"(){throw new Error(\"The `**=` operator is unsupported in TypeGPU functions.\")},get\"??=\"(){throw new Error(\"The `??=` operator is unsupported in TypeGPU functions.\")},get\"&&=\"(){throw new Error(\"The `&&=` operator is unsupported in TypeGPU functions.\")},get\"||=\"(){throw new Error(\"The `||=` operator is unsupported in TypeGPU functions.\")}};function h7(e,t,s){return s?Gj.includes(t)?ys:t===\"=\"?s:e:t===\"!\"?ys:e}var Wj={\"-\":Nw[mo].call.bind(Nw),void:()=>Je(void 0,zr,\"constant\",!1),\"!\":(e,[t])=>{if(t===void 0)throw new Error(\"The unary operator `!` expects 1 argument, but 0 were provided.\");if(qr(t))return Je(!t.value,ys,\"constant\",!1);let s=e.resolveSnippet(t).value;if(!ql(t.dataType))throw new jt(`Unary operator ! requires boolean operand. Got ${String(t.dataType)}.${Ox(t.dataType)?` For component-wise negation, use 'std.${xj.toString()}'.`:\"\"}`);return Je(`!(${s})`,ys,\"runtime\",t.possibleSideEffects)}},qj={\"+\":al[mo].call.bind(al),\"-\":As[mo].call.bind(As),\"*\":ra[mo].call.bind(ra),\"/\":cb[mo].call.bind(cb),\"**\":vt[mo].call.bind(vt)},g7={private:\"private\",workgroup:\"workgroup\",uniform:\"uniform\",mutable:\"storage, read_write\",readonly:\"storage, read\"},t0=2,Vj=class _E{#e=void 0;#t=[];static#s=_E.prototype.languageKey=\"wgsl\";initGenerator(t){if(this.#e!==void 0)throw new Error(\"Cannot initialize shader generators twice. Create one generator per resolution.\");this.#e=t}get ctx(){if(!this.#e)throw new Error(\"WGSL Generator has not yet been initialized. Please call initialize(ctx) before using the generator.\");return this.#e}_block([t,s],r,o){this.ctx.pushBlockScope();try{if(o){let d=Object.fromEntries(Object.entries(o).map(([u,p])=>[u,Ya(p)]));this.ctx.setBlockExternals(d)}let a=\"\",i=!1,l;this.ctx.indent();for(let d of s){let u=this._statement(d);if(u.code.length>0&&(a+=`${u.code}\n`),i||=u.definesInNearestScope??!1,u.endsWithControlFlow!==void 0){l=u.endsWithControlFlow;break}}this.ctx.dedent();let c=r&&!i;return this.ctx.blockDepth===t0&&(a=a.replace(/[ ]*return\\s*;\\s*$/u,\"\")),a===\"\"?{code:\"\",endsWithControlFlow:l,definesInNearestScope:!1}:c?{code:this.ctx.getDedented(a.trim()),endsWithControlFlow:l,definesInNearestScope:i}:{code:`{\n${a}${this.ctx.pre}}`,endsWithControlFlow:l,definesInNearestScope:!1}}finally{this.ctx.popBlockScope()}}_blockStatement(t,s){let{code:r,...o}=this._block(t,!0,s);return r===\"\"?{...o,code:\"\"}:{...o,code:`${this.ctx.pre}${r}`}}refVariable(t,s){let r=this.ctx.makeUniqueIdentifier(t,\"block\"),o=tM(s),a=Je(new Ji(Je(r,s,\"function\",!1),o),o,\"function\",!1);return this.ctx.defineVariable(t,a),r}_emitVarDecl(t,s,r,o){return`${this.ctx.pre}${t} ${s} = ${o};`}_identifier(t){if(!t)throw new Error(\"Cannot resolve an empty identifier\");if(t===\"undefined\")return Je(void 0,zr,\"constant\",!1);let s=this.ctx.getById(t);if(!s)throw new Error(`Identifier ${t} not found`);return s}_callShellless(t,s){let r=mb(t),o=r?t[ya]?.pairs??[]:[],a=r?t[ve].inner:t;return this.ctx.withRenamed(a,ct(t),()=>this.ctx.withSlots(o,()=>{let i=this.ctx.shelllessRepo.get(a,s);if(!i)return;let l=s.map((c,d)=>{let u=i.argTypes[d];return Ga(this.ctx,c,u,!1)});return this.ctx.withResetIndentLevel(()=>{let c=this.ctx.resolve(i);return Je(Ne`${c.value}(${l})`,c.dataType,\"runtime\")})}))}_typedExpression(t,s){let r=this.ctx.expectedType;this.ctx.expectedType=s;try{let o=this._expression(t);return s instanceof ti?o:Ga(this.ctx,o,s)}finally{this.ctx.expectedType=r}}_expression(t){if(typeof t==\"string\")return this._identifier(t);if(typeof t==\"boolean\")return Je(t,ys,\"constant\",!1);if(t[0]===Fs.logicalExpr){let[s,r,o,a]=t,i=this._expression(r);if(qr(i)){let u=ql(this.ctx.expectedType);if(!(o===\"&&\"?i.value:!i.value))return u?Je(o===\"||\",ys,\"constant\",!1):Ya(i.value);let p=this._expression(a);if(qr(p)){let _=Ya(p.value);return u?Ga(this.ctx,_,ys,!1):_}if(p.dataType===Ns)throw new jt(`Right-hand side of '${o}' is of unknown type`);return u?Ga(this.ctx,p,ys,!1):p}let l=this._expression(a);if(i.dataType===Ns)throw new jt(`Left-hand side of '${o}' is of unknown type`);if(!qr(l)&&l.dataType===Ns)throw new jt(`Right-hand side of '${o}' is of unknown type`);if(!ql(i.dataType)||!ql(l.dataType))throw new jt(`Logical expression '${o}' requires boolean operands. Got '${String(i.dataType)}' and '${String(l.dataType)}'.`);let c=this.ctx.resolveSnippet(i).value,d=this.ctx.resolveSnippet(l).value;return Je(`(${c} ${o} ${d})`,ys,\"runtime\",i.possibleSideEffects||l.possibleSideEffects)}if(t[0]===Fs.binaryExpr||t[0]===Fs.assignmentExpr){let[s,r,o,a]=t,i=this._expression(r),l=this._expression(a);if(l.value instanceof Ji)throw new jt(Ne`Cannot assign a ref to an existing variable '${ur(r)}', define a new variable instead.`);if(o===\"==\")throw new Error(\"Please use the === operator instead of ==\");if(o===\"!=\")throw new Error(\"Please use the !== operator instead of !=\");let c=Fj[o];if(c&&qr(i)&&qr(l)){let f=i.value,h=l.value;switch(o){case\"===\":return Je(f===h,ys,\"constant\",!1);case\"!==\":return Je(f!==h,ys,\"constant\",!1)}if(typeof f!=\"number\"||typeof h!=\"number\")throw new jt(`Comparison '${o}' requires numeric operands.${js(i.dataType)&&js(l.dataType)?` For component-wise comparison, use 'std.${c}'.`:\"\"}`);switch(o){case\"<\":return Je(f<h,ys,\"constant\",!1);case\"<=\":return Je(f<=h,ys,\"constant\",!1);case\">\":return Je(f>h,ys,\"constant\",!1);case\">=\":return Je(f>=h,ys,\"constant\",!1)}}if(i.dataType===Ns)throw new jt(`Left-hand side of '${o}' is of unknown type`);if(l.dataType===Ns)throw new jt(`Right-hand side of '${o}' is of unknown type`);let d=qj[o];if(d)return d(this.ctx,[i,l]);let u,p;if(Ij.includes(o)){let f=i.dataType;if(!EC(f)&&!tN(f))throw new jt(`Expression: ${ur(t)}\nLeft-hand side of '${o}' must be an integer or vector of integers.\nGot ${this.ctx.resolve(f).value}.`);let h=js(f)?f.primitive:f;if([\">>>\",\">>>=\"].includes(o)&&h.type!==\"u32\")throw new jt(`Expression: ${ur(t)}\nLeft-hand side of '${o}' must be an unsigned integer or vector of unsigned integers.\nGot ${this.ctx.resolve(f).value}.\nUse ${o.slice(1)} instead.`);[\">>\",\">>=\"].includes(o)&&h.type===\"u32\"&&rr.warn(\"deprecated\",`\nExpression: ${ur(t)}\nUsing u32 or vecN<u32> as left-hand side of ${o} is deprecated.\nUse >${o} instead.`);let g;if(js(f)){let y=f.componentCount;g=y===2?$s:y===3?Ps:Is}else g=M;p=Ga(this.ctx,l,g,!1),u=i}else{let f=s===Fs.assignmentExpr?[i.dataType]:void 0;[u,p]=Nl(this.ctx,[i,l],f)??[i,l]}let _=h7(u.dataType,o,p.dataType);if(s===Fs.assignmentExpr&&(y7(u,t),this.tryMarkModified(r),o===\"=\"&&gi(l)&&!jn(l.dataType)))throw new jt(`'${ur(t)}' is invalid, because references cannot be assigned.\n-----\nTry '${ur(r)} = ${this.ctx.resolve($c(l.dataType)).value}(${ur(a)})' to copy the value instead.\n-----`);if(c){let f=[\"===\",\"!==\"].includes(o);if(!(tb(u.dataType)&&tb(p.dataType)||f&&ql(u.dataType)&&ql(p.dataType))){let h=js(u.dataType)&&js(p.dataType);throw new jt(`Comparison '${o}' requires numeric${f?\" or boolean\":\"\"} operands. Got '${String(u.dataType)}' and '${String(p.dataType)}'.${h?` For component-wise comparison, use 'std.${c}'.`:\"\"}`)}}return Je(this.emitBinaryOp(u,Yj[o]??o,p),_,\"runtime\",s===Fs.assignmentExpr||i.possibleSideEffects||l.possibleSideEffects)}if(t[0]===Fs.postUpdate)throw new Error(`'${ur(t)}' is invalid because update is only allowed as a statement.`);if(t[0]===Fs.unaryExpr){let[s,r,o]=t,a=this._expression(o),i=Wj[r];if(i)return i(this.ctx,[a]);let l=this.ctx.resolveSnippet(a).value,c=h7(a.dataType,r);return Je(`${r}${l}`,c,\"runtime\",a.possibleSideEffects)}if(t[0]===Fs.memberAccess){let[s,r,o]=t,a=Cu(this._expression(r),o);if(!a)throw new Error(`Property '${o}' not found on '${ur(r)}'`);return a}if(t[0]===Fs.indexAccess){let[s,r,o]=t,a=this._expression(r),i=this._expression(o),l=Bh(a,Nl(this.ctx,[i],[M,j],!1)?.[0]??i);if(!l)throw new Error(`Index access '${ur(t)}' is invalid. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`);return l}if(t[0]===Fs.numericLiteral){let s=typeof t[1]==\"string\"?Ai($j(t[1])):Ai(t[1]);return kr(s,`Expected ${ur(t)} to be valid numeric literal`),s}if(t[0]===Fs.call){let[s,r,o]=t,a=this._expression(r),i=_7.has(a.value)?Je(_7.get(a.value),Ns,\"runtime\",a.possibleSideEffects):a;if(Bj().includes(i.value))return this.ctx.generateLog(i.value,o.map(d=>this._expression(d)));if(_r(i.value)){if(o.length>1)throw new jt(\"Struct schemas should always be called with at most 1 argument\");if(!o[0])return Je(`${this.ctx.resolve(i.value).value}()`,i.value,\"runtime\",!1);let d=this._typedExpression(o[0],i.value);return Je(this.ctx.resolveSnippet(d).value,i.value,\"runtime\",d.possibleSideEffects)}if(io(i.value)){if(o.length>1)throw new jt(\"Array schemas should always be called with at most 1 argument\");if(!o[0])return this.typeInstantiation(i.value,[]);let d=this._typedExpression(o[0],i.value);return d.value instanceof Sm?this.typeInstantiation(i.value,d.value.elements):Je(this.ctx.resolveSnippet(d).value,i.value,\"runtime\",d.possibleSideEffects)}if(i.value===w3)throw new Error(\"Constants cannot be defined within TypeGPU function scope. To address this, move the constant definition outside the function scope.\");if(_$(i.value)){if(!o[0])throw new jt(`An infix operator '${ct(i.value.operator)}' was called without any arguments`);let d=Ya(i.value.lhs),u=this._expression(o[0]);return i.value.operator[mo].call(this.ctx,[d,u])}if((i.value===SN||i.value===uE)&&o[0]&&this.tryMarkModified(o[0]),N1(i.value)){let d=i.value[mo],u=d.strictSignature,p;u?p=o.map((_,f)=>{let h=u.argTypes[f];if(!h)throw new jt(`Call '${ur(t)}' is invalid since the function expected fewer arguments`);return this._typedExpression(_,h)}):p=o.map(_=>this._expression(_));try{return d.call(this.ctx,p)}catch(_){throw _ instanceof fu?_:new fu(_,[{toString:()=>`fn:${ct(i.value)}`}])}}if(!_s(i.value)||mb(i.value)){let d=o.map(p=>this._expression(p)),u=this._callShellless(i.value,d);if(u)return u}let l=Object.getOwnPropertyNames(Math).find(d=>Math[d]===i.value);if(l)throw new Error(`Unsupported Math functionality 'Math.${l}()'. Use an std alternative, or implement the function manually.`);let c=Object.getOwnPropertyNames(console).find(d=>console[d]===i.value);throw c?new Error(`Unsupported console functionality 'console.${c}()'.`):new Error(`Function '${ct(i.value)??String(i.value)}' is not marked with the 'use gpu' directive and cannot be used in a shader`)}if(t[0]===Fs.objectExpr){let s=t[1],r=this.ctx.expectedType;if(r instanceof ti){let o=Object.fromEntries(Object.entries(s).map(([l,c])=>{let d=r.accessProp(l),u;if(d)u=this._typedExpression(c,d.type);else{if(u=this._expression(c),u.dataType===Ns)throw new jt(Ne`Property ${l} in object literal has a value of unknown type: '${u}'`);d=r.provideProp(l,$c(dn(u.dataType)))}return[d.prop,u]})),a=r.completeStruct,i=Bw(this.ctx,a,o);return Je(Ne`${this.ctx.resolve(r).value}(${i})`,a,\"runtime\")}if(_r(r)){let o=Object.fromEntries(Object.entries(r.propTypes).map(([i,l])=>{let c=s[i];if(c===void 0)throw new jt(`Missing property ${i} in object literal for struct ${r}`);return[i,this._typedExpression(c,l)]})),a=Bw(this.ctx,r,o);return Je(Ne`${this.ctx.resolve(r).value}(${a})`,r,\"runtime\",a.some(i=>i.possibleSideEffects))}throw new jt(`No target type could be inferred for object '${ur(t)}', please wrap the object in the corresponding schema.`)}if(t[0]===Fs.arrayExpr){let[s,r]=t,o=this.ctx.expectedType,a,i;if(io(o)){if(a=o.elementType,i=r.map(d=>this._typedExpression(d,a)),i.length!==o.elementCount)throw new jt(`Cannot create value of type '${o}' from an array of length: ${i.length}`)}else{let d=r.map(p=>this._expression(p));if(d.length===0)throw new jt(\"Cannot infer the type of an empty array literal.\");let u=Nl(this.ctx,d);if(!u)throw new jt(`Values '${ur(t)}' cannot be automatically converted to a common type. Consider wrapping the array in an appropriate schema`);i=u,a=dn(i[0]?.dataType)}let l=Fe(a,i.length),c=i.every(d=>d.origin===\"constant\");return Je(new Sm(l,i),l,c?\"constant\":\"runtime\",i.some(d=>d.possibleSideEffects))}if(t[0]===Fs.conditionalExpr){let[s,r,o,a]=t,i=this._expression(r);if(qr(i))return i.value?this._expression(o):this._expression(a);{let l=Ga(this.ctx,i,ys,!1),c=this._expression(o),d=this._expression(a),[u,p]=Nl(this.ctx,[c,d],k1)??[];if(!u||!p||c.possibleSideEffects||d.possibleSideEffects||gi(c)&&!jn(c.dataType)||gi(d)&&!jn(d.dataType))throw new Error(`Ternary operator '${ur(t)}' is invalid. For more complex branching, please use 'std.select' or if/else statements.`);return Je(Ne`select(${p}, ${u}, ${l})`,u.dataType,\"runtime\",i.possibleSideEffects)}}if(t[0]===Fs.stringLiteral)return Je(t[1],Ns,\"constant\",!1);if(t[0]===Fs.preUpdate)throw new Error(\"Cannot use pre-updates in TypeGPU functions.\");Nj(t)}declareGlobalConst(t){let s=this.ctx.resolve(t.dataType).value,r=this.ctx.resolveSnippet(t.init).value;return this.ctx.addDeclaration(`const ${t.id}: ${s} = ${r};`,t.id),Je(t.id,t.dataType,\"constant-immutable-def\")}declareGlobalVar(t){let s=\"\";return t.group!==void 0&&(s+=`@group(${t.group}) `),t.binding!==void 0&&(s+=`@binding(${t.binding}) `),t.scope in g7?s+=`var<${g7[t.scope]}> `:s+=\"var \",s+=`${t.id}: ${this.ctx.resolve(t.dataType).value}`,this.ctx.addDeclaration(t.init?`${s} = ${this.ctx.resolveSnippet(t.init).value};`:`${s};`,t.id),Je(t.id,t.dataType,t.scope)}functionDefinition(t){kr(this.ctx.blockDepth===t0-1,`Expecting exactly ${t0-1} block(s) before going into the first function block scope`);let s=this._block(t.body,!1),r=this.ctx.topFunctionScope;kr(r,\"Expected function scope to be present\");let o=Object.fromEntries([...r.placeholderForVariable.entries()].map(([d,u])=>[u,r.modifiedVariables.has(d)?\"var\":\"let\"]));if(Object.keys(o).length>0){let d=new RegExp(Object.keys(o).join(\"|\"),\"gi\");s.code=s.code.replace(d,u=>o[u]??\"#ERR\")}let a=t.determineReturnType(),i=t.args.filter(d=>d.used||t.functionType===\"normal\").map(d=>`${Ym(d.decoratedType)}${d.name}: ${this.ctx.resolve(d.decoratedType).value}`).join(\", \"),l=a.type!==\"void\"?`(${i}) -> ${Ym(a)}${this.ctx.resolve(a).value} `:`(${i}) `,c=\"\";if(t.functionType===\"compute\"){if(!t.workgroupSize)throw new Error(\"Compute shaders must have a workgroup size\");c=`@compute @workgroup_size(${t.workgroupSize.join(\", \")}) `}else t.functionType===\"vertex\"?c=\"@vertex \":t.functionType===\"fragment\"&&(c=\"@fragment \");return`${c}fn ${t.name}${l}${s.code||\"{}\"}`}emitTypeAnnotation(t){return cE(this.ctx,t)}typeInstantiation(t,s){return s.length===1&&s[0]?.dataType===t?Je(Ne`${s[0]}`,t,fN(s[0].origin),s[0].possibleSideEffects):Je(Ne`${this.ctx.resolve(t).value}(${s})`,t,\"runtime\",s.some(r=>r.possibleSideEffects))}numericLiteral(t,s){if(!Number.isFinite(t))throw new Error(`Value '${t}' (${s.type}) cannot be resolved due to WGSL's Finite Math Assumption (see: https://www.w3.org/TR/WGSL/#finite-math-assumption). This value might be a result of a comptime-evaluated operation.`);if(s.type===\"abstractInt\")return Je(`${t}`,s,\"constant\",!1);if(s.type===\"u32\")return Je(`${t}u`,s,\"constant\",!1);if(s.type===\"i32\")return Je(`${t}i`,s,\"constant\",!1);let r=t.toExponential(),o=s.type===\"abstractFloat\"&&Number.isInteger(t)?`${t}.`:`${t}`,a=r.length<o.length?r:o;return s.type===\"f32\"?Je(`${a}f`,s,\"constant\",!1):s.type===\"f16\"?Je(`${a}h`,s,\"constant\",!1):Je(a,s,\"constant\",!1)}emitCall(t,s,r){let o=s.map(i=>this.ctx.resolveSnippet(i).value).join(\", \"),a=r.map(i=>this.ctx.resolveSnippet(i).value).join(\", \");return o.length>0?`${t}<${o}>(${a})`:`${t}(${a})`}emitBinaryOp(t,s,r){let o=this.ctx.resolveSnippet(t).value,a=this.ctx.resolveSnippet(r).value;return Oj.includes(s)?`(${o} ${s} ${a})`:`${o} ${s} ${a}`}_return(t){let s=t[1];if(s!==void 0){let r=this.ctx.topFunctionReturnType,o=r?this._typedExpression(s,r):this._expression(s);if(o.value===void 0&&hh(o.dataType))return this.ctx.reportReturnType(zr),`${this.ctx.pre}return;`;if(o.value instanceof Ji)throw new jt(`Cannot return '${ur(s)}' because it is a d.ref`);if(o.origin===\"argument\"&&!jn(o.dataType)&&this.ctx.topFunctionScope?.functionType===\"normal\")throw new jt(`'${ur(t)}' is invalid, cannot return references to arguments. Copy the argument before returning it.`);if(!r&&gi(o)&&!jn(o.dataType)&&o.origin!==\"local-def\"){let a=ur(s),i=this.ctx.resolve($c(o.dataType)).value;throw new jt(`'return ${a};' is invalid, cannot return references.\n-----\nTry 'return ${i}(${a});' instead.\n-----`)}return o=Ga(this.ctx,o,$c(o.dataType),!1),kr(o.dataType!==Ns,\"Return type should be known\"),this.ctx.reportReturnType(o.dataType),Ne`${this.ctx.pre}return ${o};`}return this.ctx.reportReturnType(zr),`${this.ctx.pre}return;`}_letStatement(t){let[s,r,o]=t;if(o===void 0)throw new Error(`'${ur(t)}' is invalid because all variables need initializers.`);let a=this._expression(o);if(a.value instanceof Ji){let f=ur(o);throw new jt(`'let ${r} = ${f}' is invalid, cannot initialize 'let' variables with d.ref()\n-----\n- Try 'const ${r} = ${f}'.\n-----`)}let i=a.dataType;if(i===Ns){let f=ur(o);throw new jt(`'let ${r} = ${f}' is invalid, cannot determine WGSL type of '${f}'\n-----\n- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'let ${r} = Schema(${f})'\n-----`)}if(gi(a)&&!jn(a.dataType)){let f=ur(o),h=this.ctx.resolve($c(a.dataType)).value;throw new jt(`'let ${r} = ${f}' is invalid, because references cannot be assigned to 'let' variable declarations.\n-----\n- Try 'let ${r} = ${h}(${f})' if you need to reassign '${r}' later\n- Try 'const ${r} = ${f}' if you won't reassign '${r}' later.\n-----`)}let l=dn(i),c=Je(this.ctx.makeUniqueIdentifier(r,\"block\"),l,\"local-def\",!1);this.ctx.defineVariable(r,c);let d=Ga(this.ctx,a,i,!1),u=this.ctx.resolveSnippet(d).value,p=this.ctx.topFunctionScope;kr(p,`Expected function scope to be present for ${r}`);let _=`#VAR_${p.placeholderForVariable.size}#`;return p.placeholderForVariable.set(c,_),{code:this._emitVarDecl(_,c.value,l,u),definesInNearestScope:!0}}_constStatement(t){let[s,r,o]=t;if(o===void 0)throw new Error(`'${ur(t)}' is invalid because all variables need initializers.`);let a=this._expression(o);if(a.value instanceof Ji){if(a.dataType!==Ns)throw new jt(\"Cannot store d.ref() in a variable if it references another value. Copy the value passed into d.ref() instead.\");let g=a.value.snippet,y=this.refVariable(r,dn(g.dataType));return{code:Ne`${this.ctx.pre}var ${y} = ${Ga(this.ctx,g,g.dataType,!1)};`,definesInNearestScope:!0}}let i=jn(a.dataType),l=\"local-def\",c=\"<deferred>\",d=a.dataType;if(d===Ns){let g=ur(o);throw new jt(`'const ${r} = ${g}' is invalid, cannot determine WGSL type of '${g}'\n-----\n- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'const ${r} = Schema(${g})'\n-----`)}if(a.origin===\"argument\")c=\"let\",l=i?\"local-def\":\"argument\";else if(a.origin===\"constant-immutable-def\")c=\"const\",l=\"constant-immutable-def\";else if(a.origin===\"runtime-immutable-def\")c=\"let\",l=\"runtime-immutable-def\";else if(i)c=a.origin===\"constant\"?\"const\":\"let\",l=\"local-def\";else if(!gi(a))c=\"<deferred>\",l=\"local-def\";else return this._aliasConstStatement(r,o,a);let u=dn(d),p=Je(this.ctx.makeUniqueIdentifier(r,\"block\"),u,l,!1);this.ctx.defineVariable(r,p);let _=Ga(this.ctx,a,d,!1),f=this.ctx.resolveSnippet(_).value,h;if(c===\"<deferred>\"){let g=this.ctx.topFunctionScope;kr(g,`Expected function scope to be present for ${r}`),h=`#VAR_${g.placeholderForVariable.size}#`,g.placeholderForVariable.set(p,h)}else h=c;return{code:this._emitVarDecl(h,p.value,u,f),definesInNearestScope:!0}}_aliasConstStatement(t,s,r){let o=r.dataType;if(!ta(o)){let d=Wx(r.origin,dn(o));kr(d!==void 0,`Creating pointer type from origin ${r.origin}`),o=d}o=sM(o),this.tryMarkModified(s);let a=dn(o),i=Je(this.ctx.makeUniqueIdentifier(t,\"block\"),a,r.origin,!1);this.ctx.defineVariable(t,i);let l=Ga(this.ctx,r,o,!1),c=this.ctx.resolveSnippet(l).value;return{code:this._emitVarDecl(\"let\",i.value,a,c),definesInNearestScope:!0}}_statement(t){if(typeof t==\"string\"){let o=this._identifier(t),a=o.value!==void 0&&o.value!==null?this.ctx.resolveSnippet(o).value:\"\";return{code:a?`${this.ctx.pre}${a};`:\"\",definesInNearestScope:!1}}if(typeof t==\"boolean\")return{code:`${this.ctx.pre}${t?\"true\":\"false\"};`,definesInNearestScope:!1};if(t[0]===Fs.return)return{code:this._return(t),endsWithControlFlow:\"return\",definesInNearestScope:!1};if(t[0]===Fs.if){let[o,a,i,l]=t,c=this._typedExpression(a,ys);if(typeof c.value==\"boolean\"){let p=c.value?i:l;return p===void 0?{code:\"\",definesInNearestScope:!1}:(Array.isArray(p)||(p=qc(p)),p[0]===Fs.block&&p[1].length===1&&p[1][0][0]===Fs.if?this._statement(p[1][0]):p[0]===Fs.if?this._statement(p):this._blockStatement(qc(p)))}let d=this._block(qc(i),!1),u=l?this._block(qc(l),!1).code:void 0;return u?{code:Ne`\\\n${this.ctx.pre}if (${c}) ${d.code||\"{}\"}\n${this.ctx.pre}else ${u}`,definesInNearestScope:!1}:{code:Ne`${this.ctx.pre}if (${c}) ${d.code||\"{}\"}`,definesInNearestScope:!1}}if(t[0]===Fs.let)return this._letStatement(t);if(t[0]===Fs.const)return this._constStatement(t);if(t[0]===Fs.block)return this._blockStatement(t);if(t[0]===Fs.for){let[o,a,i,l,c]=t,d=this.#t;this.#t=[];try{this.ctx.pushBlockScope();let[u,p,_]=this.ctx.withResetIndentLevel(()=>[a?this._statement(a).code:void 0,i?this._typedExpression(i,ys):void 0,l?this._statement(l).code:void 0]),f=u?u.slice(0,-1):\"\",h=_?_.slice(0,-1):\"\",g=this._block(qc(c),!1).code;return{code:Ne`${this.ctx.pre}for (${f}; ${p}; ${h}) ${g||\"{}\"}`,definesInNearestScope:!1}}finally{this.#t=d,this.ctx.popBlockScope()}}if(t[0]===Fs.while){let o=this.#t;this.#t=[];try{let[a,i,l]=t,c=this._typedExpression(i,ys),d=this.ctx.resolveSnippet(c).value,u=this._block(qc(l),!1).code;return{code:`${this.ctx.pre}while (${d}) ${u||\"{}\"}`,definesInNearestScope:!1}}finally{this.#t=o}}if(t[0]===Fs.forOf){let[o,a,i,l]=t;if(a[0]!==Fs.const)throw new jt(\"Only `for (const ... of ... )` loops are supported\");this.tryMarkModified(i);let c=!1,d=this.#t;try{this.ctx.pushBlockScope();let u=this._expression(i),p=u.value instanceof dE,_=p?u.value.snippet:u,f=Lj(this.ctx,_,p),h=a[1],g=qc(l);if(p){if(!qr(f.end))throw new Error(\"Cannot unroll loop. Length of iterable is unknown at comptime.\");let P=f.end.value;if(P===0)return{code:\"\",definesInNearestScope:!1};let{value:C}=_,E=bb(C)?C.map(D=>Ya(D)):C instanceof Sm?C.elements:Array.from({length:P},(D,O)=>v7(_,Je(O,M,\"constant\"))),R=E[0];if(!gi(R)&&!jn(R.dataType))throw new jt(`Cannot unroll '${ur(i)}'. The elements of iterable are constructed in place but are not value types.`);let U=\"\",L,k=!1;for(let D=0;D<E.length;D++){let O=E[D];this.#t=[...d,D];let F=this._blockStatement(g,{[h]:O});if(k||=F.definesInNearestScope,U+=`${this.ctx.pre}// unrolled iteration ${this.#t.map(q=>`#${q}`).join(\" / \")}\n${F.code}\n`,F.endsWithControlFlow!==void 0){L=F.endsWithControlFlow;break}}return{code:`${U}${this.ctx.pre}// ---`,endsWithControlFlow:L,definesInNearestScope:k}}this.#t=[];let y=this.ctx.makeUniqueIdentifier(\"i\",\"block\"),b=Ne`${this.ctx.pre}for (var ${y} = ${f.start}; ${y} ${f.comparison} ${f.end}; ${y} += ${f.step})`,x=\"\";if(bb(_.value))x=this._block(g,!1,{[h]:Je(y,f.start.dataType,\"runtime\",!1)}).code;else{this.ctx.indent(),c=!0;let P=this.ctx.makeUniqueIdentifier(h,\"block\"),C=v7(_,Je(y,M,\"runtime\")),E=Rj(C),R=Dj(C,_);x=`{\n${Ne`${this.ctx.pre}${E} ${P} = ${Ga(this.ctx,C,R,!1)};`}\n${this._blockStatement(g,{[h]:Je(P,R,C.origin,!1)}).code}\n`,this.ctx.dedent(),x+=`${this.ctx.pre}}`,c=!1}return{code:Ne`${b} ${x.trim()||\"{}\"}`,definesInNearestScope:!1}}finally{c&&this.ctx.dedent(),this.#t=d,this.ctx.popBlockScope()}}if(t[0]===Fs.postUpdate){let[o,a,i]=t,l=this._expression(i),c=this.ctx.resolveSnippet(l).value;return y7(l,t),this.tryMarkModified(i),{code:`${this.ctx.pre}${c}${a};`,definesInNearestScope:!1}}if(t[0]===Fs.continue){if(this.#t.length>0)throw new jt(\"Cannot unroll loop containing `continue`\");return{code:`${this.ctx.pre}continue;`,endsWithControlFlow:\"continue\",definesInNearestScope:!1}}if(t[0]===Fs.break){if(this.#t.length>0)throw new jt(\"Cannot unroll loop containing `break`\");return{code:`${this.ctx.pre}break;`,endsWithControlFlow:\"break\",definesInNearestScope:!1}}let s=this._expression(t),r=s.value!==void 0&&s.value!==null?this.ctx.resolveSnippet(s).value:\"\";return{code:r?`${this.ctx.pre}${r};`:\"\",definesInNearestScope:!1}}tryMarkModified(t){if(!t)return;let s=Xj(t);if(s!==void 0){let r=this.ctx.getById(s),o=this.ctx.topFunctionScope;r&&o&&o.placeholderForVariable.has(r)&&o.modifiedVariables.add(r)}}};function y7(e,t){if(e.origin===\"constant\"||e.origin===\"constant-immutable-def\"||e.origin===\"runtime-immutable-def\")throw qr(e)?new jt(`'${ur(t)}' is invalid, because the left side is defined outside of the shader, and therefore is immutable during its execution. Try using tgpu.privateVar or buffers.`):new jt(`'${ur(t)}' is invalid, because the left side is a constant.`);if(e.origin===\"uniform\")throw new jt(`'${ur(t)}' is invalid, because uniform buffers cannot be mutated.`);if(e.origin===\"readonly\")throw new jt(`'${ur(t)}' is invalid, because readonly buffers cannot be mutated.`);if(e.origin===\"argument\")throw new jt(`'${ur(t)}' is invalid, because non-pointer arguments cannot be mutated.`)}function Nj(e){throw new Error(`'${pn(e)}' was not handled by the WGSL generator.`)}function $j(e){return/^0x[0-9a-f]+$/i.test(e)?Number.parseInt(e):/^0b[01]+$/i.test(e)?Number.parseInt(e.slice(2),2):Number.parseFloat(e)}function qc(e){return typeof e!=\"object\"||e[0]!==Fs.block?[Fs.block,[e]]:e}function Xj(e){let t=e;for(;Array.isArray(t)&&(t[0]===Fs.memberAccess||t[0]===Fs.indexAccess);)t=t[1];if(typeof t==\"string\")return t}var Hj=(()=>{try{return new Function(\"return true\"),!0}catch{return!1}})(),b7=new WeakMap,x7=!1,jf={u32:\"u32\",vec2u:\"u32\",vec3u:\"u32\",vec4u:\"u32\",u16:\"u16\",i32:\"i32\",vec2i:\"i32\",vec3i:\"i32\",vec4i:\"i32\",f32:\"f32\",vec2f:\"f32\",vec3f:\"f32\",vec4f:\"f32\",f16:\"f16\",vec2h:\"f16\",vec3h:\"f16\",vec4h:\"f16\",mat2x2f:\"f32\",mat3x3f:\"f32\",mat4x4f:\"f32\"},jj={uint8:\"u8\",uint8x2:\"u8\",uint8x4:\"u8\",sint8:\"i8\",sint8x2:\"i8\",sint8x4:\"i8\",unorm8:\"u8\",unorm8x2:\"u8\",unorm8x4:\"u8\",snorm8:\"i8\",snorm8x2:\"i8\",snorm8x4:\"i8\",uint16:\"u16\",uint16x2:\"u16\",uint16x4:\"u16\",sint16:\"i16\",sint16x2:\"i16\",sint16x4:\"i16\",unorm16:\"u16\",unorm16x2:\"u16\",unorm16x4:\"u16\",snorm16:\"i16\",snorm16x2:\"i16\",snorm16x4:\"i16\",float16:\"f16\",float16x2:\"f16\",float16x4:\"f16\",float32:\"f32\",float32x2:\"f32\",float32x3:\"f32\",float32x4:\"f32\",uint32:\"u32\",uint32x2:\"u32\",uint32x3:\"u32\",uint32x4:\"u32\",sint32:\"i32\",sint32x2:\"i32\",sint32x3:\"i32\",sint32x4:\"i32\"},Kf={u32:\"setUint32\",i32:\"setInt32\",f32:\"setFloat32\",u16:\"setUint16\",i16:\"setInt16\",f16:\"setFloat16\",u8:\"setUint8\",i8:\"setInt8\"},Kj={unorm8:e=>`Math.round(${e} * 255)`,unorm8x2:e=>`Math.round(${e} * 255)`,unorm8x4:e=>`Math.round(${e} * 255)`,snorm8:e=>`Math.round(${e} * 127)`,snorm8x2:e=>`Math.round(${e} * 127)`,snorm8x4:e=>`Math.round(${e} * 127)`,unorm16:e=>`Math.round(${e} * 65535)`,unorm16x2:e=>`Math.round(${e} * 65535)`,unorm16x4:e=>`Math.round(${e} * 65535)`,snorm16:e=>`Math.round(${e} * 32767)`,snorm16x2:e=>`Math.round(${e} * 32767)`,snorm16x4:e=>`Math.round(${e} * 32767)`},A7={\"unorm10-10-10-2\":{writeFunction:\"setUint32\",generator:(e,t)=>`output.setUint32(${e}, ((${t}.x*1023&0x3FF)<<22)|((${t}.y*1023&0x3FF)<<12)|((${t}.z*1023&0x3FF)<<2)|(${t}.w*3&3), littleEndian);\n`},\"unorm8x4-bgra\":{writeFunction:\"setUint8\",generator:(e,t)=>[\"z\",\"y\",\"x\",\"w\"].map((s,r)=>`output.setUint8((${e} + ${r}), Math.round(${t}.${s} * 255), littleEndian);\n`).join(\"\")}};function w7(e,t,s,r=0,o=!1){let a=(c,d,u)=>o?`if (${d} < endOffset) { output.${c}(${d}, ${u}, littleEndian); }\n`:`output.${c}(${d}, ${u}, littleEndian);\n`,i=(c,d)=>o?`if (${c} < endOffset) {\n${d}}\n`:d;function l(c,d,u,p){let _=[\"i\",\"j\",\"k\"][p]||`i${p}`;if(vh(c)||gl(c))return l(c.inner,d,u,p);if(_r(c)||ki(c))return Object.entries(kh(c)).map(([h,g])=>{let y=c.propTypes[h];return y?l(y,`(${d} + ${g.offset})`,`${u}.${h}`,p):\"\"}).join(\"\");if(io(c)||Sd(c)){if(c.elementCount===0)throw new Error(\"Cannot write using a runtime-sized schema.\");let h=Co(ds(c.elementType),xr(c)),g=c.elementCount*h,y=o?`Math.min(${u}.byteLength, Math.max(0, endOffset - (${d})))`:`Math.min(${u}.byteLength, ${g})`,b=`if (ArrayBuffer.isView(${u})) {\n`;return b+=`  new Uint8Array(output.buffer).set(new Uint8Array(${u}.buffer, ${u}.byteOffset, ${y}), output.byteOffset + (${d}));\n`,b+=`} else {\n`,b+=`for (let ${_} = 0; ${_} < ${c.elementCount}; ${_}++) {\n`,o&&(b+=`if ((${d} + ${_} * ${h}) >= endOffset) return;\n`),b+=l(c.elementType,`(${d} + ${_} * ${h})`,`${u}[${_}]`,p+1),b+=`}\n`,b+=`}\n`,b}if(js(c)){if(Ox(c))throw new Error(\"Compiled writers do not support boolean vectors\");let h=jf[c.type],g=ds(c.primitive),y=Kf[h];return Array.from({length:c.componentCount},(b,x)=>a(y,`(${d} + ${x*g})`,`${u}[${x}]`)).join(\"\")}if(xi(c)){let h=Kf[jf[c.type]],g=CC(c)?2:MC(c)?3:4,y=Co(g*4,8),b=[\"x\",\"y\",\"z\",\"w\"],x=y/4;return Array.from({length:g*g},(P,C)=>{let E=Math.floor(C/g),R=C%g,U=E*g+R,L=E*x+R;return a(h,`(${d} + ${E*y+R*4})`,`ArrayBuffer.isView(${u}) ? ${u}[${L}] : Array.isArray(${u}) ? ${u}[${U}] : ${u}.columns[${E}].${b[R]}`)}).join(\"\")}if(g$(c)){let h=c.type;if(h in A7){let R=A7[h];return i(`(${d})`,R.generator(d,u))}let g=jj[h],y=Kf[g],b=dl[h],x=js(b)?b.componentCount:1,P=g===\"u8\"||g===\"i8\"?1:g===\"u16\"||g===\"i16\"||g===\"f16\"?2:4,C=[\"x\",\"y\",\"z\",\"w\"],E=Kj[h];return Array.from({length:x},(R,U)=>{let L=x===1?u:`${u}.${C[U]}`,k=E?E(L):L;return a(y,`(${d} + ${U*P})`,k)}).join(\"\")}if(!Object.hasOwn(jf,c.type))throw new Error(`Primitive ${c.type} is unsupported by compiled writer`);let f=jf[c.type];return a(Kf[f],d,u)}return l(e,t,s,r)}function E3(e){if(!Hj){x7||(rr.warn(\"fallback\",\"This environment does not allow eval - using default writer as fallback\"),x7=!0);return}let t=b7.get(e);if(t)return t;try{let s=w7(e,\"offset\",\"value\",0,!1),r=`if (offset > 0 || endOffset < output.byteLength) {\n${w7(e,\"offset\",\"value\",0,!0)}} else {\n${s}}\n`,o=new Function(\"output\",\"offset\",\"value\",\"littleEndian=true\",\"endOffset=output.byteLength\",r);return b7.set(e,o),o}catch(s){rr.warn(\"fallback\",`Failed to compile writer for schema: ${e}\nReason: ${s instanceof Error?s.message:String(s)}\nFalling back to default writer`)}}function xo(e,t){e.writeUint16(j1(t))}function Ao(e){return $x(e.readUint16())}var pu={bool(){throw new Error(\"Booleans are not host-shareable\")},f32(e,t,s){e.writeFloat32(s)},f16(e,t,s){xo(e,s)},i32(e,t,s){e.writeInt32(s)},u32(e,t,s){e.writeUint32(s)},u16(e,t,s){e.writeUint16(s)},vec2f(e,t,s){e.writeFloat32(s[0]),e.writeFloat32(s[1])},vec2h(e,t,s){xo(e,s[0]),xo(e,s[1])},vec2i(e,t,s){e.writeInt32(s[0]),e.writeInt32(s[1])},vec2u(e,t,s){e.writeUint32(s[0]),e.writeUint32(s[1])},\"vec2<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},vec3f(e,t,s){e.writeFloat32(s[0]),e.writeFloat32(s[1]),e.writeFloat32(s[2])},vec3h(e,t,s){xo(e,s[0]),xo(e,s[1]),xo(e,s[2])},vec3i(e,t,s){e.writeInt32(s[0]),e.writeInt32(s[1]),e.writeInt32(s[2])},vec3u(e,t,s){e.writeUint32(s[0]),e.writeUint32(s[1]),e.writeUint32(s[2])},\"vec3<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},vec4f(e,t,s){e.writeFloat32(s[0]),e.writeFloat32(s[1]),e.writeFloat32(s[2]),e.writeFloat32(s[3])},vec4h(e,t,s){xo(e,s[0]),xo(e,s[1]),xo(e,s[2]),xo(e,s[3])},vec4i(e,t,s){e.writeInt32(s[0]),e.writeInt32(s[1]),e.writeInt32(s[2]),e.writeInt32(s[3])},vec4u(e,t,s){e.writeUint32(s[0]),e.writeUint32(s[1]),e.writeUint32(s[2]),e.writeUint32(s[3])},\"vec4<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},mat2x2f(e,t,s){for(let r=0;r<s.length;++r)e.writeFloat32(s[r])},mat3x3f(e,t,s){for(let r=0;r<s.length;++r)e.writeFloat32(s[r])},mat4x4f(e,t,s){for(let r=0;r<s.length;++r)e.writeFloat32(s[r])},struct(e,t,s){let r=xr(t);wo(e,r);for(let[o,a]of Object.entries(t.propTypes))wo(e,xr(a)),Z1(e,a,s[o]);wo(e,r)},array(e,t,s){if(t.elementCount===0)throw new Error(\"Cannot write using a runtime-sized schema.\");let r=xr(t);wo(e,r);let o=e.currentByteOffset;for(let a=0;a<Math.min(t.elementCount,s.length);a++)wo(e,r),Z1(e,t.elementType,s[a]);e.seekTo(o+ds(t))},ptr(){throw new Error(\"Pointers are not host-shareable\")},atomic(e,t,s){pu[t.inner.type]?.(e,t,s)},decorated(e,t,s){wo(e,wn(t));let r=e.currentByteOffset;pu[t.inner?.type]?.(e,t.inner,s),e.seekTo(r+ds(t))},uint8(e,t,s){e.writeUint8(s)},uint8x2(e,t,s){e.writeUint8(s.x),e.writeUint8(s.y)},uint8x4(e,t,s){e.writeUint8(s.x),e.writeUint8(s.y),e.writeUint8(s.z),e.writeUint8(s.w)},sint8(e,t,s){e.writeInt8(s)},sint8x2(e,t,s){e.writeInt8(s.x),e.writeInt8(s.y)},sint8x4(e,t,s){e.writeInt8(s.x),e.writeInt8(s.y),e.writeInt8(s.z),e.writeInt8(s.w)},unorm8(e,t,s){e.writeUint8(Math.round(s*255))},unorm8x2(e,t,s){e.writeUint8(Math.round(s.x*255)),e.writeUint8(Math.round(s.y*255))},unorm8x4(e,t,s){e.writeUint8(Math.round(s.x*255)),e.writeUint8(Math.round(s.y*255)),e.writeUint8(Math.round(s.z*255)),e.writeUint8(Math.round(s.w*255))},snorm8(e,t,s){e.writeInt8(Math.round(s*127))},snorm8x2(e,t,s){e.writeInt8(Math.round(s.x*127)),e.writeInt8(Math.round(s.y*127))},snorm8x4(e,t,s){e.writeInt8(Math.round(s.x*127)),e.writeInt8(Math.round(s.y*127)),e.writeInt8(Math.round(s.z*127)),e.writeInt8(Math.round(s.w*127))},uint16(e,t,s){e.writeUint16(s)},uint16x2(e,t,s){e.writeUint16(s.x),e.writeUint16(s.y)},uint16x4(e,t,s){e.writeUint16(s.x),e.writeUint16(s.y),e.writeUint16(s.z),e.writeUint16(s.w)},sint16(e,t,s){e.writeInt16(s)},sint16x2(e,t,s){e.writeInt16(s.x),e.writeInt16(s.y)},sint16x4(e,t,s){e.writeInt16(s.x),e.writeInt16(s.y),e.writeInt16(s.z),e.writeInt16(s.w)},unorm16(e,t,s){e.writeUint16(s*65535)},unorm16x2(e,t,s){e.writeUint16(s.x*65535),e.writeUint16(s.y*65535)},unorm16x4(e,t,s){e.writeUint16(s.x*65535),e.writeUint16(s.y*65535),e.writeUint16(s.z*65535),e.writeUint16(s.w*65535)},snorm16(e,t,s){e.writeInt16(Math.round(s*32767))},snorm16x2(e,t,s){e.writeInt16(Math.round(s.x*32767)),e.writeInt16(Math.round(s.y*32767))},snorm16x4(e,t,s){e.writeInt16(Math.round(s.x*32767)),e.writeInt16(Math.round(s.y*32767)),e.writeInt16(Math.round(s.z*32767)),e.writeInt16(Math.round(s.w*32767))},float16(e,t,s){xo(e,s)},float16x2(e,t,s){xo(e,s.x),xo(e,s.y)},float16x4(e,t,s){xo(e,s.x),xo(e,s.y),xo(e,s.z),xo(e,s.w)},float32(e,t,s){e.writeFloat32(s)},float32x2(e,t,s){e.writeFloat32(s.x),e.writeFloat32(s.y)},float32x3(e,t,s){e.writeFloat32(s.x),e.writeFloat32(s.y),e.writeFloat32(s.z)},float32x4(e,t,s){e.writeFloat32(s.x),e.writeFloat32(s.y),e.writeFloat32(s.z),e.writeFloat32(s.w)},uint32(e,t,s){e.writeUint32(s)},uint32x2(e,t,s){e.writeUint32(s.x),e.writeUint32(s.y)},uint32x3(e,t,s){e.writeUint32(s.x),e.writeUint32(s.y),e.writeUint32(s.z)},uint32x4(e,t,s){e.writeUint32(s.x),e.writeUint32(s.y),e.writeUint32(s.z),e.writeUint32(s.w)},sint32(e,t,s){e.writeInt32(s)},sint32x2(e,t,s){e.writeInt32(s.x),e.writeInt32(s.y)},sint32x3(e,t,s){e.writeInt32(s.x),e.writeInt32(s.y),e.writeInt32(s.z)},sint32x4(e,t,s){e.writeInt32(s.x),e.writeInt32(s.y),e.writeInt32(s.z),e.writeInt32(s.w)},\"unorm10-10-10-2\"(e,t,s){let r=0;r|=(s.x*1023&1023)<<22,r|=(s.y*1023&1023)<<12,r|=(s.z*1023&1023)<<2,r|=s.w*3&3,e.writeUint32(r)},\"unorm8x4-bgra\"(e,t,s){e.writeUint8(s.z*255),e.writeUint8(s.y*255),e.writeUint8(s.x*255),e.writeUint8(s.w*255)},disarray(e,t,s){let r=xr(t);wo(e,r);let o=e.currentByteOffset;for(let a=0;a<Math.min(t.elementCount,s.length);a++)wo(e,r),pu[t.elementType?.type]?.(e,t.elementType,s[a]);e.seekTo(o+ds(t))},unstruct(e,t,s){let r=t.propTypes;for(let[o,a]of Object.entries(r))pu[a.type]?.(e,a,s[o])},\"loose-decorated\"(e,t,s){wo(e,wn(t));let r=e.currentByteOffset,o=pu[t.inner?.type];return o?.(e,t.inner,s),e.seekTo(r+ds(t)),s}};function Z1(e,t,s){if(ArrayBuffer.isView(s)){let o=s,a=ds(t);o.byteLength!==a&&rr.warn(\"suspicious\",`TypedArray size mismatch: schema expects ${a} bytes, got ${o.byteLength}. `+(o.byteLength<a?\"Data truncated.\":\"Excess ignored.\"));let i=e.currentByteOffset;e.writeSlice(new Uint8Array(o.buffer,o.byteOffset,Math.min(o.byteLength,a))),e.seekTo(i+a);return}let r=pu[t.type];if(!r)throw new Error(`Cannot write data of type '${t.type}'.`);r(e,t,s)}var Qj={bool(){throw new Error(\"Booleans are not host-shareable\")},f32(e){return e.readFloat32()},f16(e){return Ao(e)},i32(e){return e.readInt32()},u32(e){return e.readUint32()},u16(e){return e.readUint16()},vec2f(e){return ps(e.readFloat32(),e.readFloat32())},vec3f(e){return Zs(e.readFloat32(),e.readFloat32(),e.readFloat32())},vec4f(e){return qt(e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32())},vec2h(e){return To(Ao(e),Ao(e))},vec3h(e){return Pa(Ao(e),Ao(e),Ao(e))},vec4h(e){return So(Ao(e),Ao(e),Ao(e),Ao(e))},vec2i(e){return ir(e.readInt32(),e.readInt32())},vec3i(e){return Br(e.readInt32(),e.readInt32(),e.readInt32())},vec4i(e){return sr(e.readInt32(),e.readInt32(),e.readInt32(),e.readInt32())},vec2u(e){return $s(e.readUint32(),e.readUint32())},vec3u(e){return Ps(e.readUint32(),e.readUint32(),e.readUint32())},vec4u(e){return Is(e.readUint32(),e.readUint32(),e.readUint32(),e.readUint32())},\"vec2<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},\"vec3<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},\"vec4<bool>\"(){throw new Error(\"Booleans are not host-shareable\")},mat2x2f(e){return yl(e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32())},mat3x3f(e){let t=()=>{let s=e.readFloat32();return e.readFloat32(),s};return bl(e.readFloat32(),e.readFloat32(),t(),e.readFloat32(),e.readFloat32(),t(),e.readFloat32(),e.readFloat32(),t())},mat4x4f(e){return Eo(e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32())},struct(e,t){let s=xr(t);wo(e,s);let r={},o=t.propTypes;for(let[a,i]of Object.entries(o))wo(e,xr(i)),r[a]=Wl(e,i);return wo(e,s),r},array(e,t){if(t.elementCount===0)throw new Error(\"Cannot read using a runtime-sized schema.\");let s=xr(t),r=[];for(let o=0;o<t.elementCount;o++){wo(e,s);let a=t.elementType,i=Wl(e,a);r.push(i)}return wo(e,s),r},ptr(){throw new Error(\"Pointers are not host-shareable\")},atomic(e,t){return Wl(e,t.inner)},decorated(e,t){wo(e,wn(t));let s=e.currentByteOffset,r=Wl(e,t.inner);return e.seekTo(s+ds(t)),r},uint8:e=>e.readUint8(),uint8x2:e=>$s(e.readUint8(),e.readUint8()),uint8x4:e=>Is(e.readUint8(),e.readUint8(),e.readUint8(),e.readUint8()),sint8:e=>e.readInt8(),sint8x2:e=>ir(e.readInt8(),e.readInt8()),sint8x4:e=>sr(e.readInt8(),e.readInt8(),e.readInt8(),e.readInt8()),unorm8:e=>e.readUint8()/255,unorm8x2:e=>ps(e.readUint8()/255,e.readUint8()/255),unorm8x4:e=>qt(e.readUint8()/255,e.readUint8()/255,e.readUint8()/255,e.readUint8()/255),snorm8:e=>e.readInt8()/127,snorm8x2:e=>ps(e.readInt8()/127,e.readInt8()/127),snorm8x4:e=>qt(e.readInt8()/127,e.readInt8()/127,e.readInt8()/127,e.readInt8()/127),uint16:e=>e.readUint16(),uint16x2:e=>$s(e.readUint16(),e.readUint16()),uint16x4:e=>Is(e.readUint16(),e.readUint16(),e.readUint16(),e.readUint16()),sint16:e=>e.readInt16(),sint16x2:e=>ir(e.readInt16(),e.readInt16()),sint16x4:e=>sr(e.readInt16(),e.readInt16(),e.readInt16(),e.readInt16()),unorm16:e=>e.readUint16()/65535,unorm16x2:e=>ps(e.readUint16()/65535,e.readUint16()/65535),unorm16x4:e=>qt(e.readUint16()/65535,e.readUint16()/65535,e.readUint16()/65535,e.readUint16()/65535),snorm16:e=>e.readInt16()/32767,snorm16x2:e=>ps(e.readInt16()/32767,e.readInt16()/32767),snorm16x4:e=>qt(e.readInt16()/32767,e.readInt16()/32767,e.readInt16()/32767,e.readInt16()/32767),float16(e){return Ao(e)},float16x2:e=>ps(Ao(e),Ao(e)),float16x4:e=>qt(Ao(e),Ao(e),Ao(e),Ao(e)),float32:e=>e.readFloat32(),float32x2:e=>ps(e.readFloat32(),e.readFloat32()),float32x3:e=>Zs(e.readFloat32(),e.readFloat32(),e.readFloat32()),float32x4:e=>qt(e.readFloat32(),e.readFloat32(),e.readFloat32(),e.readFloat32()),uint32:e=>e.readUint32(),uint32x2:e=>$s(e.readUint32(),e.readUint32()),uint32x3:e=>Ps(e.readUint32(),e.readUint32(),e.readUint32()),uint32x4:e=>Is(e.readUint32(),e.readUint32(),e.readUint32(),e.readUint32()),sint32:e=>e.readInt32(),sint32x2:e=>ir(e.readInt32(),e.readInt32()),sint32x3:e=>Br(e.readInt32(),e.readInt32(),e.readInt32()),sint32x4:e=>sr(e.readInt32(),e.readInt32(),e.readInt32(),e.readInt32()),\"unorm10-10-10-2\"(e){let t=e.readUint32();return qt((t>>22)/1023,(t>>12&1023)/1023,(t>>2&1023)/1023,(t&3)/3)},\"unorm8x4-bgra\"(e){let t=e.readByte()/255,s=e.readByte()/255;return qt(e.readByte()/255,s,t,e.readByte()/255)},unstruct(e,t){let s={},r=t.propTypes;for(let[o,a]of Object.entries(r))s[o]=Wl(e,a);return s},disarray(e,t){let s=xr(t),r=[];for(let o=0;o<t.elementCount;o++)wo(e,s),r.push(Wl(e,t.elementType));return wo(e,s),r},\"loose-decorated\"(e,t){wo(e,wn(t));let s=e.currentByteOffset,r=Wl(e,t.inner);return e.seekTo(s+ds(t)),r}};function Wl(e,t){let s=Qj[t.type];if(!s)throw new Error(`Cannot read data of type '${t.type}'.`);return s(e,t)}var Zj=g3();function fE(e,t,s){let r=ds(t),o=e?.startOffset??0,a;io(t)&&Array.isArray(s)?a=s.length*Co(ds(t.elementType),xr(t.elementType)):(ArrayBuffer.isView(s)||s instanceof ArrayBuffer)&&(a=s.byteLength);let i=a!==void 0?Math.min(o+a,r):void 0;return{startOffset:o,endOffset:e?.endOffset??i??r}}function s0(e,t,s,r){let{startOffset:o,endOffset:a}=fE(r,t,s);if(s instanceof ArrayBuffer||ArrayBuffer.isView(s)){let u=s instanceof ArrayBuffer?new Uint8Array(s):new Uint8Array(s.buffer,s.byteOffset,s.byteLength),p=a-o;u.byteLength!==p&&rr.warn(\"suspicious\",`Buffer size mismatch: expected ${p} bytes, got ${u.byteLength}. `+(u.byteLength<p?\"Data truncated.\":\"Excess ignored.\"));let _=Math.min(u.byteLength,p);new Uint8Array(e).set(u.subarray(0,_),o);return}let i=new DataView(e),l=Zj===\"little\",c=E3(t);if(c)try{c(i,o,s,l,a);return}catch(u){rr.warn(\"fallback\",`Error when using compiled writer for data type '${t.type}' (${ct(t)??\"unnamed\"}) - this is likely a bug, please submit an issue at https://github.com/software-mansion/TypeGPU/issues\nUsing fallback writer instead.`,u)}let d=new Md(e);d.seekTo(o),Z1(d,t,s)}function r0(e,t){return Wl(new ep(e),t)}function xb(e,t){if(t==null)return t;if(_r(e)||ki(e)){let s={},r=t;for(let o of Object.keys(e.propTypes)){let a=e.propTypes[o],i=r[o];i!==void 0&&a&&(s[o]=xb(a,i))}return s}if(io(e)||Sd(e)){let s=e,r={};for(let{idx:o,value:a}of t)r[o]=xb(s.elementType,a);return r}return t}var Jj=g3()===\"little\";function vE(e,t,s){let r=ds(e);if(r===0||t===void 0||t===null)return[];let o=s??new ArrayBuffer(r),a=new Md(o),i=new DataView(o),l=[];function c(p,_,f,h){if(_==null)return;if(_r(p)||ki(p)){let b=kh(p);for(let[x,P]of Object.entries(b)){let C=_[x],E=p.propTypes[x];C!==void 0&&E&&c(E,C,f+P.offset,P.padding??h)}return}if(io(p)||Sd(p)){let b=p,x=Co(ds(b.elementType),xr(b.elementType)),P=x-ds(b.elementType);if(ArrayBuffer.isView(_)){let E=Math.min(_.byteLength,b.elementCount*x);new Uint8Array(o,f,E).set(new Uint8Array(_.buffer,_.byteOffset,E)),l.push({start:f,end:f+E,padding:h});return}if(Array.isArray(_)){for(let E=0;E<Math.min(b.elementCount,_.length);E++)c(b.elementType,_[E],f+E*x,P);return}let C=_;for(let E of Object.keys(C)){let R=Number(E);Number.isNaN(R)||c(b.elementType,C[E],f+R*x,P)}return}let g=ds(p),y=E3(p);y?y(i,f,_,Jj,f+g):(a.seekTo(f),Z1(a,p,_)),l.push({start:f,end:f+g,padding:h})}c(e,t,0);let d=[],u=null;for(let p of l)u&&p.start===u.end+(u.padding??0)?u={start:u.start,end:p.end,padding:p.padding}:(u&&d.push({gpuOffset:u.start,data:new Uint8Array(o,u.start,u.end-u.start).slice()}),u=p);return u&&d.push({gpuOffset:u.start,data:new Uint8Array(o,u.start,u.end-u.start).slice()}),d}function eK(e,t,s){let r=vE(t,s,e),o=new Uint8Array(e);for(let{data:a,gpuOffset:i}of r)o.set(a,i)}var T7=class hE extends Error{constructor(t){super(`Resource '${ct(t)??\"<unnamed>\"}' cannot be bound as 'storage'. Use .$usage('storage') to allow it.`),Object.setPrototypeOf(this,hE.prototype)}};function __(e){return e instanceof ld}function tK(e){return __(e)&&e.resourceType===\"uniform\"}function sK(e){return __(e)&&e.resourceType===\"mutable\"}function rK(e){return __(e)&&e.resourceType===\"readonly\"}var ld=class Ab{[He];[Mo];resourceType;static#e=void(Ab.prototype[ve]=!0,Bi(Al(Ab.prototype,{asString(){return`${this.resourceType}BufferBinding:${ct(this)??\"<unnamed>\"}`},resolve(t){let s=this.buffer.dataType,r=t.makeUniqueIdentifier(ct(this),\"global\"),{group:o,binding:a}=t.allocateFixedEntry(this.resourceType===\"uniform\"?{uniform:s}:{storage:s,access:this.resourceType},this.buffer);return t.gen.declareGlobalVar({group:o,binding:a,scope:this.resourceType,id:r,dataType:s,init:void 0})}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.buffer.dataType,this.resourceType,!1)}},simulateMode:{get(t){return t.buffers.has(this.buffer)||t.buffers.set(this.buffer,rc(this.buffer.dataType,this.buffer.initial)),t.buffers.get(this.buffer)},set(t,s){t.buffers.set(this.buffer,s)}},normalMode:{get(){throw new Ew(X1()?`Cannot access ${String(this.buffer)}. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation`:\".$ is inaccessible during normal JS execution. Try `.read()`\")},set(){throw new Ew(X1()?`Cannot access ${String(this.buffer)}. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation`:\".$ is inaccessible during normal JS execution. Try `.write()`\")}}}));constructor(t,s){this.resourceType=t,this[Mo]=s,this[He]={type:t,buffer:s,label:void 0}}get buffer(){return this[He].buffer}$name(t){return es(this,t),this}write(t,s){this.buffer.write(t,s)}writePartial(t){this.buffer.writePartial(t)}patch(t){this.buffer.patch(t)}read(){return this.buffer.read()}},S7=new WeakMap;function oK(e){if(!$1(e))throw new Error(`Cannot call as('mutable') on ${e}, as it is not allowed to be used as storage. To allow it, call .$usage('storage') when creating the buffer.`);let t=S7.get(e);return t||(t=new ld(\"mutable\",e),S7.set(e,t)),t}var P7=new WeakMap;function aK(e){if(!$1(e))throw new Error(`Cannot call as('readonly') on ${e}, as it is not allowed to be used as storage. To allow it, call .$usage('storage') when creating the buffer.`);let t=P7.get(e);return t||(t=new ld(\"readonly\",e),P7.set(e,t)),t}var C7=new WeakMap;function nK(e){if(!ZC(e))throw new Error(`Cannot call as('uniform') on ${e}, as it is not allowed to be used as a uniform. To allow it, call .$usage('uniform') when creating the buffer.`);let t=C7.get(e);return t||(t=new ld(\"uniform\",e),C7.set(e,t)),t}function gE(e,t){let s=Object.values(e).flatMap(a=>Object.values(a.entries)).filter(a=>a!==null),r=s.filter(a=>\"uniform\"in a).length,o=s.filter(a=>\"storage\"in a).length;r>t.maxUniformBuffersPerShaderStage&&rr.warn(\"webgpu-limits-exceeded\",`Total number of uniform buffers (${r}) exceeds maxUniformBuffersPerShaderStage (${t.maxUniformBuffersPerShaderStage}). Consider:\n1. Grouping some of the uniforms into one using 'd.struct',\n2. Increasing the limit when requesting a device or creating a root.`),o>t.maxStorageBuffersPerShaderStage&&rr.warn(\"webgpu-limits-exceeded\",`Total number of storage buffers (${o}) exceeds maxStorageBuffersPerShaderStage (${t.maxStorageBuffersPerShaderStage}).`)}function iK(e){return _r(e)||io(e)?Co(xr(e),16):xr(e)}function wb(e){if(io(e)){wb(e.elementType);let t=Co(ds(e.elementType),xr(e.elementType));t%16!==0&&rr.warn(\"uniform-schema-misaligned\",`Schema '${ct(e.elementType)??\"<unnamed>\"}' is used in an array in a uniform buffer, and its stride (${t}) is not a multiple of 16.\nThis is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.\nTo address this, put the element schema in a struct and wrap the prop in 'd.align(16, ...)', or use a different schema like 'vec4f'.`)}if(_r(e)){Object.values(e.propTypes).forEach(wb),Object.entries(e.propTypes).forEach(([s,r])=>{let o=M1(e,i=>i[s]).offset,a=iK(r);o%a&&rr.warn(\"uniform-schema-misaligned\",`Schema '${ct(e)??\"<unnamed>\"}' is used in a uniform buffer, and its property '${s}' does not meet required alignment (offset is ${o}, required alignment is ${a}).\nThis is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.\nTo address this, wrap the property '${s}' in 'd.align(${a}, ...)'.`)});let t=Object.keys(e.propTypes);for(let s=0;s<t.length-1;s++){let r=t[s],o=t[s+1];kr(r&&o);let a=e.propTypes[r];if(kr(a),!_r(a))continue;let i=Co(ds(a),16),l=M1(e,d=>d[r]).offset,c=M1(e,d=>d[o]).offset-l;i>c&&rr.warn(\"uniform-schema-misaligned\",`Schema '${ct(e)??\"<unnamed>\"}' is used in a uniform buffer, and the difference between memory offsets of '${r}' and '${o}' props (${c}) is less than recommended (${i}).\nThis is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.\nTo address this, wrap the '${r}' prop in 'd.size(${i}, ...)'.`)}}}var lK={uniform:nK,mutable:oK,readonly:aK};function Qf(e,t,s){return Td(t)?new M7(e,t,s):new M7(e,t,s,[\"storage\",\"uniform\"])}var M7=class{[ve];[He];resourceType=\"buffer\";#e;#t=!1;#s;get#r(){return this.#s??=new ArrayBuffer(ds(this.dataType))}#o;#a;#i;initial;usableAsUniform=!1;usableAsStorage=!1;usableAsVertex=!1;usableAsIndex=!1;usableAsIndirect=!1;constructor(e,t,s,r){this[He]={type:\"buffer\",device:e.device,dataType:t,flags:GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,usages:[],raw:void 0,label:void 0},this.#i=r,ol(s)?(this.#e=!1,this[He].raw=s):(this.#e=!0,typeof s==\"function\"?this.#a=s:this.initial=s),this[ve]={root:e,materialize:()=>{if(this.#t)throw new Error(\"This buffer has been destroyed\");let o=this[He];return o.raw||(o.raw=o.device.createBuffer({size:ds(o.dataType),usage:o.flags,mappedAtCreation:!!this.initial||!!this.#a,label:ct(this)??\"<unnamed>\"}),(this.initial||this.#a)&&(this.#a?this.#a(this):this.initial&&s0(this.#n(),this.dataType,this.initial),this.#l())),o.raw}}}get dataType(){return this[He].dataType}get flags(){return this[He].flags}set flags(e){this[He].flags=e}get buffer(){return this[ve].materialize()}get destroyed(){return this.#t}get arrayBuffer(){return this.buffer.mapState===\"mapped\"?this.#n():this.#r}#n(){let e=this[He].raw;if(!e||e.mapState!==\"mapped\")throw new Error(\"Buffer is not mapped.\");return this.#o??=e.getMappedRange(),this.#o}#l(){let e=this[He].raw;!e||e.mapState!==\"mapped\"||(this.#o=void 0,e.unmap())}$name(e){es(this,e);let t=this[He].raw;return t&&(t.label=e),this}$usage(...e){for(let t of e){if(this.#i?.includes(t))throw new Error(`Buffer of type ${this.dataType.type} cannot be used as ${t}`);t===\"uniform\"&&wb(this.dataType),this.flags|=t===\"uniform\"?GPUBufferUsage.UNIFORM:0,this.flags|=t===\"storage\"?GPUBufferUsage.STORAGE:0,this.flags|=t===\"vertex\"?GPUBufferUsage.VERTEX:0,this.flags|=t===\"index\"?GPUBufferUsage.INDEX:0,this.flags|=t===\"indirect\"?GPUBufferUsage.INDIRECT:0,this.usableAsUniform=this.usableAsUniform||t===\"uniform\",this.usableAsStorage=this.usableAsStorage||t===\"storage\",this.usableAsVertex=this.usableAsVertex||t===\"vertex\",this.usableAsIndex=this.usableAsIndex||t===\"index\",this.usableAsIndirect=this.usableAsIndirect||t===\"indirect\",this[He].usages.includes(t)||this[He].usages.push(t)}return this}$addFlags(e){if(!this.#e)throw new Error(\"Cannot add flags to a buffer that is not managed by TypeGPU.\");return e&GPUBufferUsage.MAP_READ?(this.flags=GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ,this):e&GPUBufferUsage.MAP_WRITE?(this.flags=GPUBufferUsage.COPY_SRC|GPUBufferUsage.MAP_WRITE,this):(this.flags|=e,this)}compileWriter(){E3(this.dataType)}write(e,t){let s=this.buffer;if(s.mapState===\"mapped\"){let i=this.#n();if(e instanceof ArrayBuffer&&e===i)return;s0(i,this.dataType,e,t);return}e instanceof ArrayBuffer&&e===this.#r||s0(this.#r,this.dataType,e,t);let{startOffset:r,endOffset:o}=fE(t,this.dataType,e),a=o-r;this[He].device.queue.writeBuffer(s,r,this.#r,r,a)}writePartial(e){this.patch(xb(this.dataType,e))}patch(e){let t=this.buffer;if(t.mapState===\"mapped\")eK(this.#n(),this.dataType,e);else{let s=vE(this.dataType,e,this.#r);for(let{data:r,gpuOffset:o}of s)this[He].device.queue.writeBuffer(t,o,r)}}clear(e){let t=this.buffer;if(e){e[ve].rawEncoder.clearBuffer(t);return}if(t.mapState===\"mapped\"){new Uint8Array(this.#n()).fill(0);return}let s=this[He].device.createCommandEncoder();s.clearBuffer(t),this[He].device.queue.submit([s.finish()])}copyFrom(e,t){if(this.buffer.mapState===\"mapped\")throw new Error(\"Cannot copy to a mapped buffer.\");let s=ds(this.dataType);if(t){t[ve].rawEncoder.copyBufferToBuffer(e.buffer,0,this.buffer,0,s);return}let r=this[He].device.createCommandEncoder();r.copyBufferToBuffer(e.buffer,0,this.buffer,0,s),this[He].device.queue.submit([r.finish()])}async read(){let e=this.buffer;if(e.mapState===\"mapped\")return r0(this.#n(),this.dataType);if(e.usage&GPUBufferUsage.MAP_READ){await e.mapAsync(GPUMapMode.READ);let o=r0(this.#n(),this.dataType);return this.#l(),o}let t=this[He].device.createBuffer({size:ds(this.dataType),usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),s=this[He].device.createCommandEncoder();s.copyBufferToBuffer(e,0,t,0,ds(this.dataType)),this[He].device.queue.submit([s.finish()]),await t.mapAsync(GPUMapMode.READ,0,ds(this.dataType));let r=r0(t.getMappedRange(),this.dataType);return t.unmap(),t.destroy(),r}as(e){return lK[e](this)}destroy(){this.#t||(this.#t=!0,this.#o=void 0,this.#e&&this[He].raw?.destroy())}toString(){return`buffer:${ct(this)??\"<unnamed>\"}`}},yE=class Tb{bindings;constructor(t){this.bindings=t}with(t,s){return new Tb([...this.bindings,[Qu(t)||Zu(t)?t.slot:t,s]])}pipe(t){let s=t(this);return new Tb([...this.bindings,...s.bindings])}};function*cK(e){let t=0;for(;;)e.has(t)||(yield t),t++}function dK(e,t){return new xE(v3(),e,t)}function uK(e,t){return new xE(LM(),e,t)}function k3(e){let t=e;return t?.resourceType===\"sampler\"&&!!t[ve]}function B3(e){let t=e;return t?.resourceType===\"sampler-comparison\"&&!!t[ve]}var pK=class bE{[ve]={materialize:void 0};resourceType;schema;#e;static#t=Bi(Al(bE.prototype,{asString(){return`${this.resourceType}:${ct(this)??\"<unnamed>\"}`},resolve(t){let s=t.makeUniqueIdentifier(ct(this),\"global\"),r=t.allocateLayoutEntry(this.#e.layout);return t.addDeclaration(`@group(${r}) @binding(${this.#e.idx}) var ${s}: ${t.resolve(this.schema).value};`,s),Je(s,this.schema,\"handle\")}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.schema,\"handle\",!1)}},normalMode:{get(){throw new Error(\"Direct access to sampler values is possible only as part of a compute dispatch or draw call.\")}}});constructor(t,s){this.schema=t,this.#e=s,this.resourceType=t.type===\"sampler_comparison\"?\"sampler-comparison\":\"sampler\",es(this,s.key)}},xE=class AE{[ve];[He];resourceType;schema;#e;static#t=Bi(Al(AE.prototype,{asString(){return`${this.resourceType}:${ct(this)??\"<unnamed>\"}`},resolve(t){let s=t.makeUniqueIdentifier(ct(this),\"global\"),{group:r,binding:o}=t.allocateFixedEntry(this.schema.type===\"sampler_comparison\"?{sampler:\"comparison\"}:{sampler:this.#e?\"filtering\":\"non-filtering\"},this);return t.addDeclaration(`@group(${r}) @binding(${o}) var ${s}: ${t.resolve(this.schema).value};`,s),Je(s,this.schema,\"handle\")}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.schema,\"handle\",!1)}},normalMode:{get(){throw new Error(\"Direct access to sampler values is possible only as part of a compute dispatch or draw call.\")}}});constructor(t,s,r){this.schema=t,this.resourceType=t.type===\"sampler_comparison\"?\"sampler-comparison\":\"sampler\",this[He]={type:this.resourceType,device:r.device,props:s,raw:void 0,label:void 0},this[ve]={root:r,materialize:()=>{let o=this[He];return o.raw||(o.raw=o.device.createSampler({...o.props,label:ct(this)??\"<unnamed>\"})),o.raw}},this.#e=s.minFilter===\"linear\"||s.magFilter===\"linear\"||s.mipmapFilter===\"linear\"}$name(t){return es(this,t),this}},mK=class{resourceType=\"external-texture\";[ve]=!0;schema;#e;constructor(e,t){this.schema=e,this.#e=t,es(this,t.key)}[Ws](e){let t=e.makeUniqueIdentifier(ct(this),\"global\"),s=e.allocateLayoutEntry(this.#e.layout);return e.addDeclaration(`@group(${s}) @binding(${this.#e.idx}) var ${t}: ${e.resolve(this.schema).value};`,t),Je(t,RM(),\"handle\")}get[Ka](){let e=this.schema;return new Proxy({[ve]:!0,get[tc](){return Je(this,e,\"handle\",!1)},[Ws]:t=>t.resolve(this),toString:()=>`textureExternal:${ct(this)??\"<unnamed>\"}.$`},Mu)}get $(){if(xh())return this[Ka];throw new Error(\"Direct access to texture views values is possible only as part of a compute dispatch or draw call. Try .read() or .write() instead\")}toString(){return`textureExternal:${ct(this)??\"<unnamed>\"}`}},_K={channelType:n,vectorType:qt,sampleTypes:[\"depth\",\"unfilterable-float\"],texelSize:\"non-copyable\"},fK={channelType:n,vectorType:qt,sampleTypes:[\"depth\",\"unfilterable-float\"],texelSize:2},vK={channelType:n,vectorType:qt,sampleTypes:[\"depth\",\"unfilterable-float\"],texelSize:4},hK={channelType:M,vectorType:Is,sampleTypes:[\"uint\"],texelSize:1},E7=new Map;function f_(e){let t=E7.get(e);return t===void 0&&(t=gK(e),E7.set(e,t)),t}function gK(e){let t=xK(e),s=yK(e),r=e.includes(\"stencil\");return{channelType:t,vectorType:t===M?Is:t===j?sr:qt,texelSize:AK(e),sampleTypes:wK(e),canRenderAttachment:bK(e),...s&&{depthAspect:s},...r&&{stencilAspect:hK}}}function yK(e){if(e===\"depth16unorm\")return fK;if(e===\"depth32float\"||e===\"depth32float-stencil8\")return vK;if(e===\"depth24plus\"||e===\"depth24plus-stencil8\")return _K}function bK(e){return!(e.startsWith(\"bc\")||e.startsWith(\"etc2\")||e.startsWith(\"eac\")||e.startsWith(\"astc\")||e===\"rgb9e5ufloat\")}function xK(e){return e===\"stencil8\"||e.includes(\"uint\")?M:e.includes(\"sint\")?j:n}function AK(e){let[,t,s]=e.match(/^(rgba|bgra|rg|r)(8|16|32)/)??[];return t&&s?t.length*Number(s)/8:e===\"stencil8\"?1:e===\"depth16unorm\"?2:e===\"depth32float\"?4:e===\"depth32float-stencil8\"?5:e===\"depth24plus\"||e===\"depth24plus-stencil8\"?\"non-copyable\":/^(bc[14]-|etc2-rgb8|eac-r11)/.test(e)?8:/^(bc|astc-|etc2-rgba|eac-rg)/.test(e)?16:4}function wK(e){return e===\"stencil8\"?[\"uint\"]:e.includes(\"uint\")?[\"uint\"]:e.includes(\"sint\")?[\"sint\"]:e.includes(\"depth\")?[\"depth\",\"unfilterable-float\"]:/^(r|rg|rgba)16(u|s)norm$/.test(e)?[\"unfilterable-float\"]:[\"float\",\"unfilterable-float\"]}var TK=new Set([\"r32float\",\"rg32float\",\"rgba32float\"]);function SK(e,t){return TK.has(t)&&!e.features.has(\"float32-filterable\")?[\"unfilterable-float\"]:f_(t).sampleTypes}function wE(e){let{videoWidth:t,videoHeight:s}=e;if(t&&s)return{width:t,height:s};let{naturalWidth:r,naturalHeight:o}=e;if(r&&o)return{width:r,height:o};let{codedWidth:a,codedHeight:i}=e;if(a&&i)return{width:a,height:i};let{width:l,height:c}=e;if(l&&c)return{width:l,height:c};throw new Error(\"Cannot determine dimensions of the provided image source.\")}var PK=`\nstruct VertexOutput {\n  @builtin(position) pos: vec4f,\n  @location(0) uv: vec2f,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) i: u32) -> VertexOutput {\n  const pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3));\n  const uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1));\n  return VertexOutput(vec4f(pos[i], 0, 1), uv[i]);\n}`,CK=`\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var samp: sampler;\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n  return textureSample(src, samp, uv);\n}`,MK=`\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var samp: sampler;\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n  let r = textureGather(0, src, samp, uv);\n  let g = textureGather(1, src, samp, uv);\n  let b = textureGather(2, src, samp, uv);\n  let a = textureGather(3, src, samp, uv);\n  return vec4f(dot(r, vec4f(0.25)), dot(g, vec4f(0.25)), dot(b, vec4f(0.25)), dot(a, vec4f(0.25)));\n}`,Sb=new WeakMap;function EK(e){let t=Sb.get(e);return t||(t={vertexModule:e.createShaderModule({code:PK}),filterableResources:new Map,layoutResources:new Map},Sb.set(e,t)),t}function kK(e,t,s){let r=EK(e),o=r.filterableResources.get(t);o||(o={fragmentModule:e.createShaderModule({code:t?CK:MK}),sampler:e.createSampler(t?{magFilter:\"linear\",minFilter:\"linear\"}:{})},r.filterableResources.set(t,o));let a=`${t}:${s}`,i=r.layoutResources.get(a);if(!i){let l=e.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:s}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{type:t?\"filtering\":\"non-filtering\"}}]});i={bindGroupLayout:l,pipelineLayout:e.createPipelineLayout({bindGroupLayouts:[l]})},r.layoutResources.set(a,i)}return{vertexModule:r.vertexModule,...o,...i}}function TE(e){let{device:t,source:s,destination:r,format:o,filterable:a,sampleType:i}=e,l=kK(t,a,i),c=t.createRenderPipeline({layout:l.pipelineLayout,vertex:{module:l.vertexModule},fragment:{module:l.fragmentModule,targets:[{format:o}]},primitive:{topology:\"triangle-list\"}}),d=t.createBindGroup({layout:l.bindGroupLayout,entries:[{binding:0,resource:s},{binding:1,resource:l.sampler}]}),u=!e.encoder,p=e.encoder??t.createCommandEncoder(),_=p.beginRenderPass({colorAttachments:[{view:r,loadOp:\"clear\",storeOp:\"store\"}]});_.setPipeline(c),_.setBindGroup(0,d),_.draw(3),_.end(),u&&t.queue.submit([p.finish()])}function BK(e){Sb.delete(e)}function SE(e,t,s){let r=f_(t),o=SK(e,t),a=o.includes(\"float\"),i=o.includes(\"unfilterable-float\");if(!a&&!i)throw new Error(`Cannot ${s} for format '${t}': only float formats are supported.`);if(!r.canRenderAttachment)throw new Error(`Cannot ${s} for format '${t}': format does not support render attachments.`);return{filterable:a,sampleType:a?\"float\":\"unfilterable-float\"}}function zK(e,t,s=0,r){if(t.dimension!==\"2d\")throw new Error(\"Mipmap generation only supports 2D textures.\");let{filterable:o,sampleType:a}=SE(e,t.format,\"generate mipmaps\"),i=r??t.mipLevelCount-s;for(let l=0;l<t.depthOrArrayLayers;l++)for(let c=s;c<s+i-1;c++){let d=u=>({dimension:\"2d\",baseMipLevel:u,mipLevelCount:1,baseArrayLayer:l,arrayLayerCount:1});TE({device:e,source:t.createView(d(c)),destination:t.createView(d(c+1)),format:t.format,filterable:o,sampleType:a})}}function RK(e,t,s,r=0){if(t.dimension!==\"2d\")throw new Error(\"Resampling only supports 2D textures.\");let{filterable:o}=SE(e,t.format,\"resample\"),{width:a,height:i}=wE(s),l=e.createTexture({size:[a,i],format:\"rgba8unorm\",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});e.queue.copyExternalImageToTexture({source:s},{texture:l},[a,i]);let c=e.createTexture({size:[t.width,t.height],format:t.format,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC}),d=e.createCommandEncoder();TE({device:e,source:l.createView(),destination:c.createView(),format:t.format,filterable:o,sampleType:\"float\",encoder:d}),d.copyTextureToTexture({texture:c},{texture:t,origin:{x:0,y:0,z:r}},{width:t.width,height:t.height,depthOrArrayLayers:1}),e.queue.submit([d.finish()]),l.destroy(),c.destroy()}function DK(e){return{dimension:e.dimension??\"2d\",sampleType:f_(e.format).channelType,multisampled:(e.sampleCount??1)!==1}}function LK(e,t,s){return new UK(e,t,s)}function Vm(e){return e?.resourceType===\"texture\"&&!!e[ve]}function Eu(e){return e?.resourceType===\"texture-view\"&&!!e[ve]}var UK=class{[ve];[He];resourceType=\"texture\";usableAsSampled=!1;usableAsStorage=!1;usableAsRender=!1;#e;#t=!1;#s;constructor(e,t,s){this.#s=s===void 0,this[He]={type:\"texture\",device:t.device,props:e,flags:GPUTextureUsage.COPY_DST|GPUTextureUsage.COPY_SRC,flagsOverridden:!1,usages:[],raw:s,label:void 0},this.#e=f_(e.format),this[ve]={root:t,materialize:()=>{if(this.#t)throw new Error(\"This texture has been destroyed\");let r=this[He];if(!r.raw){let o=r.props;r.raw=(globalThis.__TYPEGPU_AUTONAME__??(a=>a))(r.device.createTexture({label:ct(this)??\"<unnamed>\",format:o.format,size:o.size,usage:r.flags,dimension:o.dimension??\"2d\",viewFormats:o.viewFormats??[],mipLevelCount:o.mipLevelCount??1,sampleCount:o.sampleCount??1}),\"raw\")}return r.raw}}}get props(){return this[He].props}$name(e){return es(this,e),this}$usage(...e){let t=this[He];if(t.flagsOverridden)throw new Error(\"Cannot call $usage() after $overrideFlags().\");let s=e.includes(\"storage\"),r=e.includes(\"sampled\"),o=e.includes(\"render\"),a=e.includes(\"transient\"),i=(r?GPUTextureUsage.TEXTURE_BINDING:0)|(s?GPUTextureUsage.STORAGE_BINDING:0),l=GPUTextureUsage.TRANSIENT_ATTACHMENT|GPUTextureUsage.RENDER_ATTACHMENT,c=t.flags|i|(o?GPUTextureUsage.RENDER_ATTACHMENT:0),d=a||!!(t.flags&GPUTextureUsage.TRANSIENT_ATTACHMENT),u=!!(c&(GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.STORAGE_BINDING));if(d&&u)throw new Error(\"Transient texture usage cannot be combined with 'sampled' or 'storage'.\");t.flags=a?l:c,this.usableAsStorage||=s,this.usableAsSampled||=r,this.usableAsRender||=o||a;for(let p of e)t.usages.includes(p)||t.usages.push(p);return this}$overrideFlags(e){let t=this[He];return t.flags=e,t.flagsOverridden=!0,this.usableAsSampled=!0,this.usableAsStorage=!0,this.usableAsRender=!0,this}createView(e,t){return e===\"render\"?new GK(this,t):new OK(e??M$(DK(this.props)),this,t)}#r(e=0){let t=2**e,[s,r,o]=[Math.max(1,Math.floor((this.props.size[0]??1)/t)),Math.max(1,Math.floor((this.props.size[1]??1)/t)),Math.max(1,Math.floor((this.props.size[2]??1)/t))],a=this.#e.texelSize;if(a===\"non-copyable\")throw new Error(`Cannot clear texture with format '${this.props.format}': this format does not support copy operations.`);this[He].device.queue.writeTexture({texture:this[ve].materialize(),mipLevel:e},new Uint8Array(s*r*o*a),{bytesPerRow:a*s,rowsPerImage:r},[s,r,o])}clear(e=\"all\"){if(e===\"all\"){let t=this.props.mipLevelCount??1;for(let s=0;s<t;s++)this.#r(s)}else this.#r(e)}generateMipmaps(e=0,t){if(!this.usableAsRender)throw new Error(\"generateMipmaps called without specifying 'render' usage. Add it via the $usage('render') method.\");let s=t??(this.props.mipLevelCount??1)-e;if(s<=1){rr.warn(\"suspicious\",`generateMipmaps is a no-op: would generate ${s} mip levels (base: ${e}, total: ${this.props.mipLevelCount??1})`);return}if(e>=(this.props.mipLevelCount??1))throw new Error(`Base mip level ${e} is out of range. Texture has ${this.props.mipLevelCount??1} mip levels.`);zK(this[He].device,this[ve].materialize(),e,s)}write(e,t=0){if(e instanceof ArrayBuffer||ArrayBuffer.isView(e)){this.#o(e,typeof t==\"number\"?t:0);return}if(!this.usableAsRender)throw new Error(\"texture.write(...) with image sources requires 'render' usage. Add it via the $usage('render') method.\");let s=typeof t==\"number\"?void 0:t,r=this.props.dimension??\"2d\";if(!Array.isArray(e)){this.#a(e,r===\"3d\"?0:void 0,s);return}let o=this.props.size[2]??1;e.length>o&&rr.warn(\"suspicious\",`Too many image sources provided. Expected ${o} layers, got ${e.length}. Extra sources will be ignored.`);for(let a=0;a<Math.min(e.length,o);a++){let i=e[a];i&&this.#a(i,a,s)}}#o(e,t){let s=Math.max(1,this.props.size[0]>>t),r=Math.max(1,(this.props.size[1]??1)>>t),o=Math.max(1,(this.props.size[2]??1)>>t),a=this.#e.texelSize;if(a===\"non-copyable\")throw new Error(`Cannot write to texture with format '${this.props.format}': this format does not support copy operations.`);let i=s*r*o*a,l=e.byteLength??e.byteLength;if(l!==i)throw new Error(`Buffer size mismatch. Expected ${i} bytes for mip level ${t}, got ${l} bytes.`);this[He].device.queue.writeTexture({texture:this[ve].materialize(),mipLevel:t},\"buffer\"in e?e.buffer:e,{bytesPerRow:a*s,rowsPerImage:r},[s,r,o])}#a(e,t,s){let r=this.props.size[0],o=this.props.size[1]??1,{width:a,height:i}=wE(e);if(a!==r||i!==o){if(s?.fit!==\"stretch\")throw new Error(`Texture write source size ${a}x${i} does not match target size ${r}x${o}. Pass fit: 'stretch' to resize explicitly.`);RK(this[He].device,this[ve].materialize(),e,t);return}this[He].device.queue.copyExternalImageToTexture({source:e},{texture:this[ve].materialize(),...t!==void 0&&{origin:{x:0,y:0,z:t}}},t!==void 0?[r,o,1]:this.props.size)}copyFrom(e){if(e.props.format!==this.props.format)throw new Error(`Texture format mismatch. Source texture has format ${e.props.format}, target texture has format ${this.props.format}`);if(e.props.size[0]!==this.props.size[0]||(e.props.size[1]??1)!==(this.props.size[1]??1)||(e.props.size[2]??1)!==(this.props.size[2]??1))throw new Error(`Texture size mismatch. Source texture has size ${e.props.size.join(\"x\")}, target texture has size ${this.props.size.join(\"x\")}`);let t=this[He].device.createCommandEncoder();t.copyTextureToTexture({texture:e[ve].materialize()},{texture:this[ve].materialize()},e.props.size),this[He].device.queue.submit([t.finish()])}toString(){return`${this.resourceType}:${ct(this)??\"<unnamed>\"}`}get destroyed(){return this.#t}destroy(){this.#t||(this.#t=!0,this.#s&&this[He].raw?.destroy())}},OK=class{[ve];[He];resourceType=\"texture-view\";constructor(e,t,s){this[He]={type:\"texture-view\",texture:t,schema:e,descriptor:s,raw:void 0,label:void 0},this[ve]={unwrap:()=>{let r=this[He];if(!r.raw){let o=r.schema,a=C1(o)?o.format:r.texture.props.format;r.raw=(globalThis.__TYPEGPU_AUTONAME__??(i=>i))(r.texture[ve].materialize().createView({...r.descriptor,label:ct(this)??\"<unnamed>\",format:r.descriptor?.format??a,dimension:o.dimension}),\"raw\")}return r.raw},format:s?.format??(C1(e)?e.format:t.props.format),aspect:s?.aspect}}get schema(){return this[He].schema}$name(e){return es(this,e),this[He].raw&&(this[He].raw.label=e),this}get[Ka](){let e=this.schema;return new Proxy({[ve]:!0,get[tc](){return Je(this,e,\"handle\",!1)},[Ws]:t=>t.resolve(this),toString:()=>`${this.toString()}.$`},Mu)}get $(){if(xh())return this[Ka];throw new Error(\"Direct access to texture view values is possible only as part of a compute dispatch or draw call. Try .read() or .write() instead\")}get size(){return this[He].texture.props.size}toString(){return`textureView:${ct(this)??\"<unnamed>\"}`}[Ws](e){let t=e.makeUniqueIdentifier(ct(this),\"global\"),{group:s,binding:r}=e.allocateFixedEntry(C1(this.schema)?{storageTexture:this.schema}:{texture:this.schema,sampleType:this[He].descriptor?.sampleType??this.schema.bindingSampleType[0]},this);return e.gen.declareGlobalVar({group:s,binding:r,id:t,dataType:this.schema,scope:\"handle\",init:void 0})}},k7=class{[ve]={unwrap:void 0};resourceType=\"texture-view\";#e;schema;constructor(e,t){this.schema=e,this.#e=t,es(this,t.key)}toString(){return`textureView:${ct(this)??\"<unnamed>\"}`}[Ws](e){let t=e.makeUniqueIdentifier(ct(this),\"global\"),s=e.allocateLayoutEntry(this.#e.layout);return e.addDeclaration(`@group(${s}) @binding(${this.#e.idx}) var ${t}: ${e.resolve(this.schema).value};`,t),Je(t,this.schema,\"handle\")}get[Ka](){let e=this.schema;return new Proxy({[ve]:!0,get[tc](){return Je(this,e,\"handle\",!1)},[Ws]:t=>t.resolve(this),toString:()=>`${this.toString()}.$`},Mu)}get $(){if(xh())return this[Ka];throw new Error(`Accessed view '${ct(this)??\"<unnamed>\"}' outside of codegen mode. Direct access to texture views values is possible only as part of a compute dispatch or draw call. Try .read() or .write() instead`)}$name(e){return es(this,e),this}},GK=class{[ve];[He];resourceType=\"texture-view\";constructor(e,t={}){this[He]={type:\"texture-view\",texture:e,schema:\"render\",descriptor:t,raw:void 0,label:void 0},this[ve]={unwrap:()=>e[ve].materialize().createView({label:ct(this)??\"<unnamed>\",...this.descriptor}),format:t.format??e.props.format,aspect:t.aspect}}get descriptor(){return this[He].descriptor??{}}};function FK(e){return!!e?.usableAsSampled}var IK=class PE extends Error{constructor(t){super(`Resource '${ct(t)??\"<unnamed>\"}' cannot be bound as 'sampled'. Use .$usage('sampled') to allow it.`),Object.setPrototypeOf(this,PE.prototype)}},B7=class Pb{[ve];usage;dataType;#e;static#t=void(Pb.prototype.resourceType=\"laid-out-buffer\",Bi(Al(Pb.prototype,{asString(){return`${this.usage}:${ct(this)??\"<unnamed>\"}`},resolve(t){let s=t.makeUniqueIdentifier(ct(this),\"global\"),r=t.allocateLayoutEntry(this.#e.layout);return t.gen.declareGlobalVar({group:r,binding:this.#e.idx,scope:this.usage,id:s,dataType:this.dataType,init:void 0})}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.dataType,this.usage,!1)}},normalMode:{get(){throw new Error(\"Direct access to buffer values is possible only as part of a compute dispatch or draw call. Try .read() or .write() instead\")}}}));constructor(t,s,r){this[ve]={dataType:s},this.usage=t,this.dataType=s,this.#e=r,es(this,r.key)}};function CE(e){return new WK({...e})}function Uh(e){return!!e&&e.resourceType===\"bind-group-layout\"}function ic(e){return!!e&&e.resourceType===\"bind-group\"}var YK=class ME extends Error{constructor(t,s){super(`Bind group '${t??\"<unnamed>\"}' is missing a required binding '${s}'`),Object.setPrototypeOf(this,ME.prototype)}},z7=[\"compute\",\"fragment\"],tm=[\"compute\",\"vertex\",\"fragment\"],WK=class{[ve];[He];resourceType=\"bind-group-layout\";$={};entries;get[Ka](){return this.$}constructor(e){this.entries=e,this[ve]=[];let t={};this[He]={type:\"bind-group-layout\",entries:t,index:void 0,label:void 0};let s=0;for(let[r,o]of Object.entries(e)){if(o===null){t[r]=null,s++;continue}let a={layout:this,key:r,idx:s},i;if(t[r]=o,\"uniform\"in o&&(i=new B7(\"uniform\",o.uniform,a)),\"storage\"in o){let l=\"type\"in o.storage?o.storage:o.storage(0);i=new B7(o.access??\"readonly\",l,a),t[r]={...o,storage:l}}\"texture\"in o&&(i=new k7(o.texture,a)),\"storageTexture\"in o&&(i=new k7(o.storageTexture,a)),\"externalTexture\"in o&&(i=new mK(o.externalTexture,a)),\"sampler\"in o&&(i=new pK(o.sampler===\"comparison\"?LM():v3(),a)),kr(i!==void 0,\"Internal error, expected item to be defined\"),Object.defineProperty(this.$,r,{get:()=>i.$}),this[ve].push(i),s++}}toString(){return`bindGroupLayout:${ct(this)??\"<unnamed>\"}`}get index(){return this[He].index}$name(e){return es(this,e),this}$idx(e){return this[He].index=e,this}unwrap(e){return e.device.createBindGroupLayout({label:ct(this)??\"<unnamed>\",entries:Object.values(this.entries).map((t,s)=>{if(t===null)return null;let r=t.visibility,o={binding:s,visibility:0};if(\"uniform\"in t)r=r??tm,o.buffer={type:\"uniform\"};else if(\"storage\"in t)r=r??(t.access===\"mutable\"?z7:tm),o.buffer={type:t.access===\"mutable\"?\"storage\":\"read-only-storage\"};else if(\"sampler\"in t)r=r??tm,o.sampler={type:t.sampler};else if(\"texture\"in t){r=r??tm;let{multisampled:a,dimension:i,bindingSampleType:l}=t.texture;o.texture={sampleType:t.sampleType??l[0],viewDimension:i,multisampled:a}}else if(\"storageTexture\"in t){r=r??z7;let{dimension:a,access:i,format:l}=t.storageTexture;o.storageTexture={access:i,format:l,viewDimension:a}}else\"externalTexture\"in t&&(r=r??tm,o.externalTexture={});return r?.includes(\"compute\")&&(o.visibility|=GPUShaderStage.COMPUTE),r?.includes(\"vertex\")&&(o.visibility|=GPUShaderStage.VERTEX),r?.includes(\"fragment\")&&(o.visibility|=GPUShaderStage.FRAGMENT),o}).filter(t=>t!==null)})}},EE=class{[ve];[He];resourceType=\"bind-group\";#e;constructor(e,t,s,r){if(this.#e=s,this[He]={type:\"bind-group\",device:e?.device,layout:t,raw:r,label:void 0},this[ve]={materialize:()=>{let o=this[He];return o.raw||(kr(e,\"Cannot unwrap a bind group created outside of a root.\"),o.raw=e.unwrap(this)),o.raw}},!r){for(let o of Object.keys(t.entries))if(t.entries[o]!==null&&!(o in s))throw new YK(ct(t),o)}}get layout(){return this[He].layout}get entries(){return this.#e}unwrap(e){let t=this[He].raw;return t||e.device.createBindGroup({label:ct(this.layout)??\"<unnamed>\",layout:e.unwrap(this.layout),entries:Object.entries(this.layout.entries).map(([s,r],o)=>{if(r===null)return null;let a=this.entries[s];if(a===void 0)throw new Error(`'${s}' is a resource required to populate bind group layout '${ct(this.layout)??\"<unnamed>\"}'.`);if(\"uniform\"in r){let i;if(Fm(a)){if(!ZC(a))throw new cN(a);i={buffer:e.unwrap(a)}}else tK(a)?i={buffer:e.unwrap(a)}:i={buffer:a};return{binding:o,resource:i}}if(\"storage\"in r){let i;if(Fm(a)){if(!$1(a))throw new T7(a);i={buffer:e.unwrap(a)}}else sK(a)||rK(a)?(kr(r.access===\"mutable\"==(a.resourceType===\"mutable\"),\"Invalid buffer access mode.\"),i={buffer:e.unwrap(a)}):i={buffer:a};return{binding:o,resource:i}}if(\"texture\"in r){let i;if(Vm(a)){if(!FK(a))throw new IK(a);i=e.unwrap(a.createView(r.texture))}else Eu(a)?i=e.unwrap(a):i=a;return{binding:o,resource:i}}if(\"storageTexture\"in r){let i;if(Vm(a)){if(!$1(a))throw new T7(a);i=e.unwrap(a.createView(r.storageTexture))}else Eu(a)?i=e.unwrap(a):i=a;return{binding:o,resource:i}}if(\"sampler\"in r)return B3(a)||k3(a)?{binding:o,resource:e.unwrap(a)}:{binding:o,resource:a};if(\"externalTexture\"in r)return{binding:o,resource:a};throw new Error(`Malformed bind group entry: ${pn(a)}`)}).filter(s=>s!==null)})}};function v_(e){return new qK(e)}var qK=class{[ve]=!0;[He];resourceType=\"slot\";constructor(e=void 0){this[He]={type:\"slot\",defaultValue:e,label:void 0}}get defaultValue(){return this[He].defaultValue}$name(e){return es(this,e),this}areEqual(e,t){return Object.is(e,t)}toString(){return`slot:${ct(this)??\"<unnamed>\"}`}get[Ka](){let e=_d();if(!e)throw new Error(\"Cannot access tgpu.slot's value outside of resolution.\");return K1(e.unwrap(this))}get $(){return this[Ka]}},rp=v_(null);function Oh(e,t){return new BE(\"private\",e,t)}function kE(e){return new BE(\"workgroup\",e)}var BE=class zE{[He];static#e=(()=>{let t=zE.prototype;t.resourceType=\"var\",t[ve]={},Bi(Al(t,{asString(){return`var:${ct(this)??\"<unnamed>\"}`},resolve(s){let r=s.makeUniqueIdentifier(ct(this),\"global\"),o=this[He].initialValue?Je(this[He].initialValue,this[He].dataType,\"constant\"):void 0;return s.gen.declareGlobalVar({scope:this[He].scope,id:r,dataType:this[He].dataType,init:o})}}),{codegenMode:{getBaseSnippet(s){return Je(s,this[He].dataType,this[He].scope,!1)}},simulateMode:{get(s){return s.vars[this[He].scope].has(this)||s.vars[this[He].scope].set(this,this[He].initialValue),s.vars[this[He].scope].get(this)},set(s,r){s.vars[this[He].scope].set(this,r)}},normalMode:{get(){throw new Mw(X1()?`Cannot access variable '${ct(this)??\"<unnamed>\"}'. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation`:\"TypeGPU variables are inaccessible during normal JS execution. If you wanted to simulate GPU behavior, try `tgpu.simulate()`\")},set(s){throw new Mw(X1()?`Cannot access variable ${ct(this)??\"<unnamed>\"}. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation`:\"TypeGPU variables are inaccessible during normal JS execution. If you wanted to simulate GPU behavior, try `tgpu.simulate()`\")}}})})();constructor(t,s,r){this[He]={type:\"var\",scope:t,dataType:s,initialValue:r,label:void 0}}$name(t){return es(this,t),this}},RE=Oh(M,0).$name(\"dataBlockIndex\"),DE=Oh(M,0).$name(\"dataByteIndex\"),LE=v_().$name(\"dataBuffer\"),VK=nr([],M)`() {\n  let i = dataByteIndex;\n  dataByteIndex = dataByteIndex + 1u;\n  return i;\n}`.$uses({dataByteIndex:DE}).$name(\"nextByteIndex\"),Pt=\"dataBuffer[dataBlockIndex].serializedData[nextByteIndex()]\",UE={f32:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([n])`(n) {\n  ${Pt} = bitcast<u32>(n);\n}`,\"f32\"),f16:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Vr])`(n) {\n  ${Pt} = pack2x16float(vec2f(f32(n), 0.0));\n}`,\"f16\"),i32:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([j])`(n) {\n  ${Pt} = bitcast<u32>(n);\n}`,\"i32\"),u32:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([M])`(n) {\n  ${Pt} = n;\n}`,\"u32\"),bool:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([ys])`(b) {\n  ${Pt} = u32(b);\n}`,\"bool\"),vec2f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([ps])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n}`,\"vec2f\"),vec3f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Zs])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n  ${Pt} = bitcast<u32>(v.z);\n}`,\"vec3f\"),vec4f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([qt])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n  ${Pt} = bitcast<u32>(v.z);\n  ${Pt} = bitcast<u32>(v.w);\n}`,\"vec4f\"),vec2h:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([To])`(v) {\n  ${Pt} = pack2x16float(vec2f(f32(v.x), f32(v.y)));\n}`,\"vec2h\"),vec3h:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Pa])`(v) {\n  ${Pt} = pack2x16float(vec2f(f32(v.x), f32(v.y)));\n  ${Pt} = pack2x16float(vec2f(f32(v.z), 0.0));\n}`,\"vec3h\"),vec4h:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([So])`(v) {\n  ${Pt} = pack2x16float(vec2f(f32(v.x), f32(v.y)));\n  ${Pt} = pack2x16float(vec2f(f32(v.z), f32(v.w)));\n}`,\"vec4h\"),vec2i:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([ir])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n}`,\"vec2i\"),vec3i:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Br])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n  ${Pt} = bitcast<u32>(v.z);\n}`,\"vec3i\"),vec4i:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([sr])`(v) {\n  ${Pt} = bitcast<u32>(v.x);\n  ${Pt} = bitcast<u32>(v.y);\n  ${Pt} = bitcast<u32>(v.z);\n  ${Pt} = bitcast<u32>(v.w);\n}`,\"vec4i\"),vec2u:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([$s])`(v) {\n  ${Pt} = v.x;\n  ${Pt} = v.y;\n}`,\"vec2u\"),vec3u:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Ps])`(v) {\n  ${Pt} = v.x;\n  ${Pt} = v.y;\n  ${Pt} = v.z;\n}`,\"vec3u\"),vec4u:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Is])`(v) {\n  ${Pt} = v.x;\n  ${Pt} = v.y;\n  ${Pt} = v.z;\n  ${Pt} = v.w;\n}`,\"vec4u\"),\"vec2<bool>\":nr([Wa])`(v) {\n  ${Pt} = u32(v.x);\n  ${Pt} = u32(v.y);\n}`,\"vec3<bool>\":nr([qa])`(v) {\n  ${Pt} = u32(v.x);\n  ${Pt} = u32(v.y);\n  ${Pt} = u32(v.z);\n}`,\"vec4<bool>\":nr([Va])`(v) {\n  ${Pt} = u32(v.x);\n  ${Pt} = u32(v.y);\n  ${Pt} = u32(v.z);\n  ${Pt} = u32(v.w);\n}`,mat2x2f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([yl])`(m) {\n  ${Pt} = bitcast<u32>(m[0][0]);\n  ${Pt} = bitcast<u32>(m[0][1]);\n  ${Pt} = bitcast<u32>(m[1][0]);\n  ${Pt} = bitcast<u32>(m[1][1]);\n}`,\"mat2x2f\"),mat3x3f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([bl])`(m) {\n  ${Pt} = bitcast<u32>(m[0][0]);\n  ${Pt} = bitcast<u32>(m[0][1]);\n  ${Pt} = bitcast<u32>(m[0][2]);\n  ${Pt} = 0u;\n  ${Pt} = bitcast<u32>(m[1][0]);\n  ${Pt} = bitcast<u32>(m[1][1]);\n  ${Pt} = bitcast<u32>(m[1][2]);\n  ${Pt} = 0u;\n  ${Pt} = bitcast<u32>(m[2][0]);\n  ${Pt} = bitcast<u32>(m[2][1]);\n  ${Pt} = bitcast<u32>(m[2][2]);\n  ${Pt} = 0u;\n}`,\"mat3x3f\"),mat4x4f:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(nr([Eo])`(m) {\n  ${Pt} = bitcast<u32>(m[0][0]);\n  ${Pt} = bitcast<u32>(m[0][1]);\n  ${Pt} = bitcast<u32>(m[0][2]);\n  ${Pt} = bitcast<u32>(m[0][3]);\n  ${Pt} = bitcast<u32>(m[1][0]);\n  ${Pt} = bitcast<u32>(m[1][1]);\n  ${Pt} = bitcast<u32>(m[1][2]);\n  ${Pt} = bitcast<u32>(m[1][3]);\n  ${Pt} = bitcast<u32>(m[2][0]);\n  ${Pt} = bitcast<u32>(m[2][1]);\n  ${Pt} = bitcast<u32>(m[2][2]);\n  ${Pt} = bitcast<u32>(m[2][3]);\n  ${Pt} = bitcast<u32>(m[3][0]);\n  ${Pt} = bitcast<u32>(m[3][1]);\n  ${Pt} = bitcast<u32>(m[3][2]);\n  ${Pt} = bitcast<u32>(m[3][3]);\n}`,\"mat4x4f\")};for(let[e,t]of Object.entries(UE))t.$name(`serialize${e[0].toLocaleUpperCase()}${e.slice(1)}`).$uses({dataBlockIndex:RE,nextByteIndex:VK,dataBuffer:LE});function OE(e){return`(${e.map((t,s)=>`_arg_${s}`).join(\", \")})`}function GE(e,t){let s=UE[e.type];if(s)return s.with(LE,t);if(_r(e)){let r=Object.keys(e.propTypes),o=FE(Object.values(e.propTypes),t);return nr([e])`(arg) {\\n  propsSerializer(${r.map(a=>`arg.${a}`).join(\", \")});\\n}`.$uses({propsSerializer:o}).$name(`${ct(e)??\"struct\"}Serializer`)}if(io(e)){let r=e.elementType,o=e.elementCount,a=GE(r,t);return nr([e])`(arg) {\\n${Array.from({length:o},(i,l)=>`  elementSerializer(arg[${l}]);`).join(`\n`)}\\n}`.$uses({elementSerializer:a}).$name(\"arraySerializer\")}throw new Error(`Cannot serialize data of type ${e.type}`)}function FE(e,t){let s={};return(globalThis.__TYPEGPU_AUTONAME__??(r=>r))(nr(e),\"shell\")`${OE(e)} {\\n${e.map((r,o)=>(s[`serializer${o}`]=GE(r,t),`  serializer${o}(_arg_${o});`)).join(`\n`)}\\n}`.$uses(s).$name(\"compoundSerializer\")}function NK(e,t,s,r,o){let a=t.map(ds).reduce((c,d)=>c+d,0);if(a>o.logSizeLimit)throw new Error(`Logged data needs to fit in ${o.logSizeLimit} bytes (one of the logs requires ${a} bytes). Consider increasing the limit by passing appropriate options to tgpu.init().`);let i=FE(t,s).$name(`log${e}serializer`),l=OE(t);return nr(t)`${l} {\n  dataBlockIndex = atomicAdd(&indexBuffer, 1);\n  if (dataBlockIndex >= ${o.logCountLimit}) {\n    return;\n  }\n  dataBuffer[dataBlockIndex].id = ${e};\n  dataByteIndex = 0;\n\n  compoundSerializer${l};\n}`.$uses({indexBuffer:r,dataBuffer:s,dataBlockIndex:RE,dataByteIndex:DE,compoundSerializer:i}).$name(`log${e}`)}var $K={logCountLimit:64,logSizeLimit:252,messagePrefix:\" GPU \"},IE=Je(\"/* console.log() */\",zr,\"runtime\"),XK=class{get logResources(){}generateLog(){return rr.warn(\"fallback\",\"'console.log' is only supported when resolving pipelines.\"),IE}},HK=class{#e;#t;#s=1;#r;#o;constructor(e){this.#e={...$K,...e[ve].logOptions},this.#t=new Map;let t=gt({id:M,serializedData:Fe(M,Math.ceil(this.#e.logSizeLimit/4))}).$name(\"SerializedLogData\");this.#o=e.createMutable(Fe(t,this.#e.logCountLimit)).$name(\"dataBuffer\"),this.#r=e.createMutable(lo(M)).$name(\"indexBuffer\")}generateLog(e,t,s){if(rp.$===\"vertex\")return rr.warn(\"suspicious\",\"'console' operations are not supported in vertex shaders.\"),IE;let r=this.#s++,o=s.map(c=>{if(c.dataType===Ns)return c;let d=Nl(e,[c],[$c(c.dataType)])?.[0];return kr(d,`Internal error. Expected type ${c.dataType} to be convertible to ${$c(c.dataType)}`),d}).map(GN),a=o.filter(c=>c.dataType!==Ns),i=NK(r,a.map(c=>c.dataType),this.#o,this.#r,this.#e),l=Je(Ne`${e.resolve(i).value}(${a})`,zr,\"runtime\");return this.#t.set(r,{op:t,argTypes:o.map(c=>c?.dataType===Ns?c?.value:c?.dataType)}),l}get logResources(){return this.#s===1?void 0:{dataBuffer:this.#o,indexBuffer:this.#r,options:this.#e,logIdToMeta:this.#t}}};function jK(e){return e?.resourceType===\"shellless-impl\"}function KK(e,t){let s=Ed(t,\"normal\");return{[ve]:!0,[Mo]:s,resourceType:\"shellless-impl\",argTypes:e,[Ws](r){return s.resolve(r,e,void 0)},toString(){return`fn*:${ct(s)??\"<unnamed>\"}(${e.map(r=>r.toString()).join(\", \")})`}}}var QK=new RegExp(`[${[...tE].join(\"\")}]+`,\"ug\"),R7=/[.\\p{XID_Continue}]/u;function D7(e){let t=JK(e).split(QK).filter(r=>r!==\"\"),s=\"\";for(let r=0;r<t.length;r++){let o=t[r],a=t[r+1];kr(o),s+=o,ZK(o,a)&&(s+=\" \")}return s}function ZK(e,t){if(t===void 0)return!1;let s=e.at(-1),r=t.at(0);return kr(s?.length===1&&r?.length===1,\"Expected tokens during minification to not be empty.\"),!!(R7.test(s)&&R7.test(r)||[\"//\",\"/*\",\"*/\"].includes(`${s}${r}`))}function JK(e){let t=\"\",s=0,r=0;for(;r<e.length;){if(e.startsWith(\"//\",r)){for(t+=`${e.slice(s,r)} `,r+=2;r<e.length&&!T3.has(e.charAt(r));)r+=1;s=r;continue}if(e.startsWith(\"/*\",r)){t+=`${e.slice(s,r)} `;let o=1;for(r+=2;r<e.length&&o>0;)e.startsWith(\"/*\",r)?(o+=1,r+=2):e.startsWith(\"*/\",r)?(o-=1,r+=2):r+=1;if(o>0)throw new SyntaxError(\"Block comment opening without corresponding closing found during minification.\");s=r;continue}if(e.startsWith(\"*/\",r))throw new SyntaxError(\"Block comment closing without corresponding opening found during minification.\");r+=1}return t+e.slice(s)}var Cb=\"#CATCHALL#\",eQ=class{_stack=[];_itemDepth=0;get itemDepth(){return this._itemDepth}get topItem(){let e=this._stack[this._stack.length-1];if(!e||e.type!==\"item\")throw new Error(\"Internal error, expected item layer to be on top.\");return e}get topFunctionScope(){return this._stack.findLast(e=>e.type===\"functionScope\")}get topBlockScope(){return this._stack.findLast(e=>e.type===\"blockScope\")}get blockDepth(){let e=0;for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"functionScope\")break;s?.type===\"blockScope\"&&e++}return e}pushItem(){this._itemDepth++,this._stack.push({type:\"item\",usedSlots:new Set})}pushSlotBindings(e){this._stack.push({type:\"slotBinding\",bindingMap:new WeakMap(e)})}pushFunctionScope(e,t,s,r){let o={type:\"functionScope\",functionType:e,argAccess:t,returnType:s,externalMap:r,reportedReturnTypes:new Set,placeholderForVariable:new Map,modifiedVariables:new Set};return this._stack.push(o),o}pushBlockScope(){this._stack.push({type:\"blockScope\",takenLocalIdentifiers:new Set,declarations:new Map,externals:new Map})}pop(e){let t=this._stack[this._stack.length-1];if(!t||e&&t.type!==e)throw new Error(`Internal error, expected a ${e} layer to be on top.`);let s=this._stack.pop();return e===\"item\"&&this._itemDepth--,s}readSlot(e){for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"item\")s.usedSlots.add(e);else if(s?.type===\"slotBinding\"){let r=s.bindingMap.get(e);if(r!==void 0)return r}else if(!(s?.type===\"functionScope\"||s?.type===\"blockScope\"))throw new Error(\"Unknown layer type.\")}return e.defaultValue}getSnippetById(e){for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"functionScope\"){let r=s.argAccess[e];if(r)return r();if(Object.hasOwn(s.externalMap,e)){let o=s.externalMap[e];return Su(o)&&ct(o)===void 0&&es(o,e.replaceAll(\".\",\"_\")),Ya(o)}return}if(s?.type===\"blockScope\"){let r=s.declarations.get(e)??s.externals.get(e);if(r!==void 0)return r}}}isIdentifierTakenLocally(e){for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"functionScope\")return!1;if(s?.type===\"blockScope\"&&s.takenLocalIdentifiers.has(e))return!0}return!1}isIdentifierTakenInCallStack(e){for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"blockScope\"&&s.takenLocalIdentifiers.has(e))return!0}return!1}defineBlockVariable(e,t){if(t.dataType===Ns)throw Error(`Tried to define variable '${e}' of unknown type`);for(let s=this._stack.length-1;s>=0;--s){let r=this._stack[s];if(r?.type===\"blockScope\"){r.declarations.set(e,t);return}}throw new Error(\"No block scope found to define a variable in.\")}setBlockExternals(e){for(let t=this._stack.length-1;t>=0;--t){let s=this._stack[t];if(s?.type===\"blockScope\"){Object.entries(e).forEach(([r,o])=>{s.externals.set(r,o)});return}}throw new Error(\"No block scope found to set externals in.\")}clearBlockExternals(){for(let e=this._stack.length-1;e>=0;--e){let t=this._stack[e];if(t?.type===\"blockScope\"){t.externals.clear();return}}throw new Error(\"No block scope found to clear externals in.\")}},Cm=[\"\",\"  \",\"    \",\"      \",\"        \",\"          \",\"            \",\"              \",\"                \"],o0=Cm.length-1,tQ=class{identLevel=0;get pre(){return Cm[this.identLevel]??Cm[o0].repeat(this.identLevel/o0)+Cm[this.identLevel%o0]}indent(){let e=this.pre;return this.identLevel++,e}dedent(){return this.identLevel--,this.pre}withResetLevel(e){let t=this.identLevel;this.identLevel=0;try{return e()}finally{this.identLevel=t}}};function au(e,t,s=\"argument\"){let r=!1;return{name:e,access:()=>(r=!0,Je(e,t,s,!1)),decoratedType:t,get used(){return r}}}function L7(e,t){return()=>{let s=e();if(s)return Cu(s,t)}}var YE=class{#e;_indentController=new tQ;_itemStateStack=new eQ;#t=[];_declarations=[];_varyingLocations;#s=new WeakSet;#r;gen;get varyingLocations(){return this._varyingLocations}[ve]={itemStateStack:this._itemStateStack};bindGroupLayoutsToPlaceholderMap=new Map;_nextFreeLayoutPlaceholderIdx=0;fixedBindings=[];enableExtensions;expectedType;#o=0;constructor(e){this.enableExtensions=e.enableExtensions,this.#r=e.root?new HK(e.root):new XK,this.#e=e.namespace[ve],this.gen=e.shaderGenerator??new Vj,this.gen.initGenerator(this)}isIdentifierBanned(e){return _3.has(e)}isIdentifierTaken(e,t){return this.#e.takenGlobalIdentifiers.has(e)||(t===\"block\"?this._itemStateStack.isIdentifierTakenLocally(e):this._itemStateStack.isIdentifierTakenInCallStack(e))}makeUniqueIdentifier(e=\"item\",t){if(t===\"block\"&&Sh(e).success&&!this.isIdentifierTaken(e,t))return this.reserveIdentifier(e,\"block\"),e;let s=v$(e),r=0,o=this.#e.strategy===\"random\",a=o?`${s}_${this.#o++}`:s;for(;this.isIdentifierTaken(a,t);)a=o?`${s}_${this.#o++}`:`${s}_${++r}`;return this.reserveIdentifier(a,t),a}reserveIdentifier(e,t){if(t===\"block\"){let s=this._itemStateStack.topBlockScope;if(s){s.takenLocalIdentifiers.add(e);return}}this.#e.takenGlobalIdentifiers.add(e)}get pre(){return this._indentController.pre}get topFunctionScope(){return this._itemStateStack.topFunctionScope}get topFunctionReturnType(){let e=this._itemStateStack.topFunctionScope;return kr(e,\"Internal error, expected function scope to be present.\"),e.returnType}get shelllessRepo(){return this.#e.shelllessRepo}get blockDepth(){return this._itemStateStack.blockDepth}indent(){return this._indentController.indent()}dedent(){return this._indentController.dedent()}getDedented(e){return e.replaceAll(`\n${Cm[1]}`,`\n`)}withResetIndentLevel(e){return this._indentController.withResetLevel(e)}getById(e){let t=this._itemStateStack.getSnippetById(e);return t===void 0?null:t}defineVariable(e,t){this._itemStateStack.defineBlockVariable(e,t)}reportReturnType(e){let t=this._itemStateStack.topFunctionScope;kr(t,\"Internal error, expected function scope to be present.\"),t.reportedReturnTypes.add(e)}pushBlockScope(){this._itemStateStack.pushBlockScope()}popBlockScope(){this._itemStateStack.pop(\"blockScope\")}setBlockExternals(e){this._itemStateStack.setBlockExternals(e)}clearBlockExternals(){this._itemStateStack.clearBlockExternals()}generateLog(e,t){return this.#r.generateLog(this,e,t)}get logResources(){return this.#r.logResources}resolveFunction(e){try{let t=this._itemStateStack.pushFunctionScope(e.functionType,{},e.returnType,e.externalMap);this._itemStateStack.pushBlockScope();let s=[];if(e.entryInput){let{dataSchema:a,positionalArgs:i}=e.entryInput,l=e.params[0],c=a?au(this.makeUniqueIdentifier(\"_arg_0\",\"block\"),a):void 0;if(c&&s.push(c),l?.type===gm.destructuredObject)for(let{name:d,alias:u}of l.props){let p=i.find(_=>_.schemaKey===d);if(p){let _=au(this.makeUniqueIdentifier(u,\"block\"),p.type);s.push(_),t.argAccess[u]=_.access}else c&&(t.argAccess[u]=L7(c.access,d))}else if(l?.type===gm.identifier){let d=[];for(let p of i){let _=au(this.makeUniqueIdentifier(p.schemaKey,\"block\"),p.type);s.push(_),d.push({schemaKey:p.schemaKey,arg:_.access})}let u=new ZM(c?.access,d);t.argAccess[l.name]=()=>Je(\"N/A\",u,\"argument\")}else for(let d of i){let u=this.makeUniqueIdentifier(`_arg_${d.schemaKey}`,\"block\"),p=au(u,d.type);s.push(p),t.argAccess[u]=p.access}}else for(let[a,i]of e.argTypes.entries()){let l=e.params[a],c=ta(i)?i.addressSpace===\"storage\"?i.access===\"read\"?\"readonly\":\"mutable\":i.addressSpace:\"argument\";switch(l?.type){case gm.identifier:{let d=au(this.makeUniqueIdentifier(l.name,\"block\"),i,c);s.push(d),t.argAccess[l.name]=d.access;break}case gm.destructuredObject:{let d=au(this.makeUniqueIdentifier(`_arg_${a}`,\"block\"),i,c);s.push(d);for(let{name:u,alias:p}of l.props)t.argAccess[p]=L7(d.access,u);break}case void 0:i instanceof ti||s.push({name:this.makeUniqueIdentifier(`_arg_${a}`,\"block\"),access:()=>{throw new Error(\"Unreachable: Accessing an argument that wasn't named in the function signature\")},decoratedType:i,used:!1})}}let r,o=this.gen.functionDefinition({functionType:e.functionType,name:e.name,workgroupSize:e.workgroupSize,args:s,body:e.body,determineReturnType:()=>{if(r)return r;if(r=e.returnType,r instanceof ti&&(_r(t.reportedReturnTypes.values().next().value)?r=r.completeStruct:r=void 0),!r){let a=[...t.reportedReturnTypes];if(a.length===0)r=zr;else{let i=Nx(a);i&&!i.hasImplicitConversions&&(r=i.targetType)}if(!r)throw new Error(`Expected function to have a single return type, got [${a.join(\", \")}]. Cast explicitly to the desired type.`);r=dn(r),(e.functionType===\"vertex\"||e.functionType===\"fragment\")&&(r=p_(r))}return r}});if(!r)throw new Error(\"Failed to determine return type\");return{code:o,returnType:r}}finally{this._itemStateStack.pop(\"blockScope\"),this._itemStateStack.pop(\"functionScope\")}}addDeclaration(e,t){this._declarations.push({name:t,code:e})}get declarations(){return this._declarations}allocateLayoutEntry(e){let t=this.bindGroupLayoutsToPlaceholderMap,s=t.get(e);return s||(s=`#BIND_GROUP_LAYOUT_${this._nextFreeLayoutPlaceholderIdx++}#`,t.set(e,s)),s}allocateFixedEntry(e,t){let s=this.fixedBindings.length;return this.fixedBindings.push({layoutEntry:e,resource:t}),{group:Cb,binding:s}}readSlot(e){let t=this._itemStateStack.readSlot(e);if(t===void 0)throw new lN(e);return t}withSlots(e,t){if(e.length===0)return t();this._itemStateStack.pushSlotBindings(e);try{return t()}finally{this._itemStateStack.pop(\"slotBinding\")}}withVaryingLocations(e,t){this._varyingLocations=e;try{return t()}finally{this._varyingLocations=void 0}}withRenamed(e,t,s){if(!t)return s();let r=ct(e);try{return es(e,t),s()}finally{r&&es(e,r)}}unwrap(e){if(S1(e))return this.withRenamed(e[ya].inner,ct(e),()=>this.withSlots(e[ya].pairs,()=>this.unwrap(e[ya].inner)));let t=e;for(;;)if(ab(t))t=this.readSlot(t);else if(nb(t))t=this._getOrCompute(t);else break;return t}_getOrCompute(e){let t=this.#e.memoizedLazy.get(e)??[];this._itemStateStack.pushItem();try{for(let o of t)if([...o.slotToValueMap.entries()].every(([a,i])=>a.areEqual(this._itemStateStack.readSlot(a),i)))return o.result;this.pushMode(new Ju);let s;try{s=e[ve].compute()}finally{this.popMode(\"normal\")}let r=new Map;for(let o of this._itemStateStack.topItem.usedSlots)r.set(o,this._itemStateStack.readSlot(o));return t.push({slotToValueMap:r,result:s}),this.#e.memoizedLazy.set(e,t),s}catch(s){throw s instanceof fu?s.appendToTrace(e):new fu(s,[e])}finally{this._itemStateStack.pop(\"item\")}}_getOrInstantiate(e){let t=this.#e.memoizedResolves.get(e)??[];this._itemStateStack.pushItem();try{for(let o of t)if([...o.slotToValueMap.entries()].every(([a,i])=>a.areEqual(this._itemStateStack.readSlot(a),i)))return o.result;let s;if(o_(e))s=Je(this.gen.emitTypeAnnotation(e),zr,\"runtime\");else if(nb(e)||ab(e))s=this.resolve(this.unwrap(e));else if(QC(e))s=e[Ws](this);else if(Pu(e)){let o=this.#e.shelllessRepo.get(e,void 0);if(!o)throw new Error(`Couldn't resolve ${e.name}. Make sure it's a function that accepts no arguments, or call it from another TypeGPU function.`);return this.withResetIndentLevel(()=>this.resolve(o))}else throw new TypeError(`Unresolvable internal value: ${pn(e)}`);let r=new Map;for(let o of this._itemStateStack.topItem.usedSlots)r.set(o,this._itemStateStack.readSlot(o));return t.push({slotToValueMap:r,result:s}),this.#e.memoizedResolves.set(e,t),s}catch(s){throw s instanceof fu?s.appendToTrace(e):new fu(s,[e])}finally{this._itemStateStack.pop(\"item\")}}resolve(e,t){if(typeof e==\"string\"){if(!t||t===Ns)throw new Error(`Strings cannot be injected into WGSL directly (tried to inject '${e}'). Look for TypeGPU APIs that cover your use-case, or resort to using tgpu['~unstable'].rawCodeSnippet for raw code injection.`);return Je(e,t,\"runtime\")}if((pb(e)||jK(e))&&!S1(e)){if(this.#s.has(e)&&!this.#e.memoizedResolves.has(e))throw new Error(`Recursive function ${e} detected. Recursion is not allowed on the GPU.`);this.#s.add(e)}if(S1(e))return this.withRenamed(e[ya].inner,ct(e),()=>this.withSlots(e[ya].pairs,()=>this.resolve(e[ya].inner,t)));if(_s(e)||Pu(e)){if(this._itemStateStack.itemDepth===0)try{this.pushMode(new yN);let s=JC(this,()=>this._getOrInstantiate(e));return Je(`${this._declarations.map(r=>r.code).join(`\n\n`)}${s.value}`,zr,\"runtime\")}finally{this.popMode(\"codegen\")}return this._getOrInstantiate(e)}if(typeof e==\"number\"){let s=t??Ai(e).dataType;return kr(s!==Ns,\"Schema has to be known for resolving numbers\"),this.gen.numericLiteral(e,s)}if(typeof e==\"boolean\")return Je(e?\"true\":\"false\",ys,\"constant\",!1);if(t&&io(t)){if(!Array.isArray(e))throw new jt(`Cannot coerce ${e} into value of type '${t}'`);if(t.elementCount!==e.length)throw new jt(`Cannot create value of type '${t}' from an array of length: ${e.length}`);return this.gen.typeInstantiation(t,e.map(s=>Je(s,t.elementType,\"runtime\")))}if(t&&_r(t))return this.gen.typeInstantiation(t,Object.entries(t.propTypes).map(([s,r])=>Je(e[s],r,\"runtime\")));throw new jt(`Value ${pn(e)} is not resolvable${t&&t!==Ns?` to type ${pn(t)}`:\"\"}`)}resolveSnippet(e){return KC(this.resolve(e.value,e.dataType).value,e)}pushMode(e){this.#t.push(e)}popMode(e){let t=this.#t.pop();e!==void 0&&kr(t?.type===e,\"Unexpected mode\")}get mode(){return this.#t[this.#t.length-1]??eM}};function Gh(e,t){let s=new YE(t),r=(t.config?s.withSlots(t.config(new yE([])).bindings,()=>s.resolve(e)):s.resolve(e)).value,o=s.bindGroupLayoutsToPlaceholderMap,a=[],i=cK(new Set([...o.keys()].map(_=>_.index).filter(_=>_!==void 0))),l=s.fixedBindings.map((_,f)=>[String(f),_.layoutEntry]),c=[],d=()=>{let _=i.next().value,f=(globalThis.__TYPEGPU_AUTONAME__??(h=>h))(CE(Object.fromEntries(l)),\"catchallLayout\");return a[_]=f,r=r.replaceAll(Cb,String(_)),c.push([Cb,String(_)]),[_,new EE(t.root,f,Object.fromEntries(s.fixedBindings.map((h,g)=>[String(g),h.resource])))]},u=l.length>0?d():void 0;for(let[_,f]of o.entries()){let h=_.index??i.next().value;a[h]=_,r=r.replaceAll(f,String(h)),c.push([f,String(h)])}t.enableExtensions&&t.enableExtensions.length>0&&(r=`${t.enableExtensions.map(_=>`enable ${_};`).join(`\n`)}\n\n${r}`);let p=s.declarations.map(({name:_,code:f})=>({name:_,code:c.reduce((h,[g,y])=>h.replaceAll(g,y),f)}));return t.minify&&(r=D7(r),p=p.map(_=>({..._,code:D7(_.code)}))),{code:r,declarations:p,usedBindGroupLayouts:a,catchall:u,logResources:s.logResources}}function WE(e,t,s){return e.flatMap((r,o)=>{let a=t&&o===t[0]?t[1]:s?.get(r);return a?[[r,a]]:[]})}function sQ(e,t){return e.flatMap(s=>{let r=t?.get(s);return r?[[s,r]]:[]})}var rQ={dispatchWorkgroupsIndirect:\"3 x u32\",drawIndirect:\"4 x u32\",drawIndexedIndirect:\"3 x u32, i32, u32\"};function U7(e,t,s,r){if(t+s>e)throw new Error(`Buffer too small for ${r}. Required: ${s} bytes at offset ${t}, but buffer is only ${e} bytes.`);if(t%4!==0)throw new Error(`Indirect buffer offset must be a multiple of 4. Got: ${t}`)}function ku(e,t,s,r){if(ol(e)){let i=typeof t==\"number\"?t:t?.offset??0;return U7(e.size,i,s,r),i}let{offset:o,contiguous:a}=t?typeof t==\"number\"?{offset:t,contiguous:s}:t:M1(e.dataType);return U7(ds(e.dataType),o,s,r),a<s&&rr.warn(\"suspicious\",`${r}: Starting at offset ${o}, only ${a} contiguous bytes are available before padding. '${r}' requires ${s} bytes (${rQ[r]}). Reading across padding may result in undefined behavior.`),o}var J1=[M,$s,Ps,Is];je({name:\"bitcastU32toF32\",normalImpl:(e=>typeof e==\"number\"?fi(e):$a.bitcastU32toF32[e.kind](e)),codegenImpl:(e,[t],s)=>e.gen.emitCall(\"bitcast\",[Ya(s)],[t]),signature:(...e)=>{let t=wh(e,J1);if(!t)throw new jr(e,J1);return{argTypes:t,returnType:js(t[0])?t[0].type===\"vec2u\"?ps:t[0].type===\"vec3u\"?Zs:qt:n}},sideEffects:!1});je({name:\"bitcastU32toI32\",normalImpl:(e=>typeof e==\"number\"?vi(e):$a.bitcastU32toI32[e.kind](e)),codegenImpl:(e,[t],s)=>e.gen.emitCall(\"bitcast\",[Ya(s)],[t]),signature:(...e)=>{let t=wh(e,J1);if(!t)throw new jr(e,J1);return{argTypes:t,returnType:js(t[0])?t[0].type===\"vec2u\"?ir:t[0].type===\"vec3u\"?Br:sr:j}},sideEffects:!1});var O7=[n,ps,Zs,qt];je({name:\"bitcastF32toU32\",normalImpl:(e=>typeof e==\"number\"?hi(e):$a.bitcastF32toU32[e.kind](e)),codegenImpl:(e,[t],s)=>e.gen.emitCall(\"bitcast\",[Ya(s)],[t]),signature:(...e)=>{let t=wh(e,O7);if(!t)throw new jr(e,O7);return{argTypes:t,returnType:js(t[0])?t[0].type===\"vec2f\"?$s:t[0].type===\"vec3f\"?Ps:Is:M}},sideEffects:!1});var Zf=new ArrayBuffer(16),Jf={f32:new Float32Array(Zf),u32:new Uint32Array(Zf),i32:new Int32Array(Zf),u16:new Uint16Array(Zf)};function oQ(e,t){if(typeof e==\"number\")t[0]=e;else for(let s=0;s<e.length;s++)t[s]=e[s]}function aQ(e,t){if(typeof e==\"number\")t[0]=j1(e);else for(let s=0;s<e.length;s++)t[s]=j1(e[s])}function nQ(e,t){let s=\"componentCount\"in t?t.componentCount:1,r=[];for(let o=0;o<s;o++)r.push(e[o]);return t(...r)}function iQ(e,t){let s=\"componentCount\"in t?t.componentCount:1,r=[];for(let o=0;o<s;o++)r.push($x(e[o]));return t(...r)}var lQ=(e,t)=>{let s=\"primitive\"in e?e.primitive:e,r=\"primitive\"in t?t.primitive:t;return o=>(s.type===\"f16\"?aQ(o,Jf.u16):oQ(o,Jf[s.type]),r.type===\"f16\"?iQ(Jf.u16,t):nQ(Jf[r.type],t))};function Qt(e,t){return je({name:\"bitcast\",normalImpl:lQ(e,t),codegenImpl:(s,[r])=>s.gen.emitCall(\"bitcast\",[Ya(t)],[r]),signature:s=>{let r=wh([s],[e]);if(!r)throw new jr([s],[e]);return{argTypes:r,returnType:t}},sideEffects:!1})}var G7={f16:{f16:Qt(Vr,Vr)},f32:{f32:Qt(n,n),i32:Qt(n,j),u32:Qt(n,M),vec2h:Qt(n,To)},i32:{f32:Qt(j,n),i32:Qt(j,j),u32:Qt(j,M),vec2h:Qt(j,To)},u32:{f32:Qt(M,n),i32:Qt(M,j),u32:Qt(M,M),vec2h:Qt(M,To)},vec2h:{f32:Qt(To,n),i32:Qt(To,j),u32:Qt(To,M),vec2h:Qt(To,To)},vec3h:{vec3h:Qt(Pa,Pa)},vec2f:{vec2f:Qt(ps,ps),vec2i:Qt(ps,ir),vec2u:Qt(ps,$s),vec4h:Qt(ps,So)},vec2i:{vec2f:Qt(ir,ps),vec2i:Qt(ir,ir),vec2u:Qt(ir,$s),vec4h:Qt(ir,So)},vec2u:{vec2f:Qt($s,ps),vec2i:Qt($s,ir),vec2u:Qt($s,$s),vec4h:Qt($s,So)},vec4h:{vec2f:Qt(So,ps),vec2i:Qt(So,ir),vec2u:Qt(So,$s),vec4h:Qt(So,So)},vec3f:{vec3f:Qt(Zs,Zs),vec3i:Qt(Zs,Br),vec3u:Qt(Zs,Ps)},vec3i:{vec3f:Qt(Br,Zs),vec3i:Qt(Br,Br),vec3u:Qt(Br,Ps)},vec3u:{vec3f:Qt(Ps,Zs),vec3i:Qt(Ps,Br),vec3u:Qt(Ps,Ps)},vec4f:{vec4f:Qt(qt,qt),vec4i:Qt(qt,sr),vec4u:Qt(qt,Is)},vec4i:{vec4f:Qt(sr,qt),vec4i:Qt(sr,sr),vec4u:Qt(sr,Is)},vec4u:{vec4f:Qt(Is,qt),vec4i:Qt(Is,sr),vec4u:Qt(Is,Is)}};function cQ(e,t){if(\"type\"in e&&e.type in G7){let s=G7[e.type];if(\"type\"in t&&t.type in s)return s[t.type]}throw new Error(`Incorrect bitcast from ${ct(e)} to ${ct(t)}.`)}var qE=Tn(cQ),dQ=je({name:\"unpack2x16float\",normalImpl:e=>{let t=new ArrayBuffer(4);new Md(t).writeUint32(e);let s=new ep(t);return ps(Ao(s),Ao(s))},signature:{argTypes:[M],returnType:ps},codegenImpl:(e,[t])=>Ne`unpack2x16float(${t})`,sideEffects:!1});je({name:\"pack2x16float\",normalImpl:e=>{let t=new ArrayBuffer(4),s=new Md(t);return xo(s,e.x),xo(s,e.y),M(new ep(t).readUint32())},signature:{argTypes:[ps],returnType:M},codegenImpl:(e,[t])=>Ne`pack2x16float(${t})`,sideEffects:!1});je({name:\"unpack4x8unorm\",normalImpl:e=>{let t=new ArrayBuffer(4);new Md(t).writeUint32(e);let s=new ep(t);return qt(s.readUint8()/255,s.readUint8()/255,s.readUint8()/255,s.readUint8()/255)},signature:{argTypes:[M],returnType:qt},codegenImpl:(e,[t])=>Ne`unpack4x8unorm(${t})`,sideEffects:!1});je({name:\"pack4x8unorm\",normalImpl:e=>{let t=new ArrayBuffer(4),s=new Md(t);return s.writeUint8(e.x*255),s.writeUint8(e.y*255),s.writeUint8(e.z*255),s.writeUint8(e.w*255),M(new ep(t).readUint32())},signature:{argTypes:[qt],returnType:M},codegenImpl:(e,[t])=>Ne`pack4x8unorm(${t})`,sideEffects:!1});var ks=e=>qE(M,n)(e??0),_i=e=>qE(M,j)(e??0),nu=e=>dQ(e??0),uQ={f32:e=>ks(e[0]),f16:e=>nu(e[0]).x,i32:e=>_i(e[0]),u32:e=>e[0]??0,bool:e=>!!e[0],vec2f:e=>ps(ks(e[0]),ks(e[1])),vec3f:e=>Zs(ks(e[0]),ks(e[1]),ks(e[2])),vec4f:e=>qt(ks(e[0]),ks(e[1]),ks(e[2]),ks(e[3])),vec2h(e){let t=nu(e[0]);return To(t.x,t.y)},vec3h(e){let t=nu(e[0]),s=nu(e[1]);return Pa(t.x,t.y,s.x)},vec4h(e){let t=nu(e[0]),s=nu(e[1]);return So(t.x,t.y,s.x,s.y)},vec2i:e=>ir(_i(e[0]),_i(e[1])),vec3i:e=>Br(_i(e[0]),_i(e[1]),_i(e[2])),vec4i:e=>sr(_i(e[0]),_i(e[1]),_i(e[2]),_i(e[3])),vec2u:e=>$s(e[0]??0,e[1]??0),vec3u:e=>Ps(e[0]??0,e[1]??0,e[2]??0),vec4u:e=>Is(e[0]??0,e[1]??0,e[2]??0,e[3]??0),\"vec2<bool>\":e=>Wa(!!e[0],!!e[1]),\"vec3<bool>\":e=>qa(!!e[0],!!e[1],!!e[2]),\"vec4<bool>\":e=>Va(!!e[0],!!e[1],!!e[2],!!e[3]),mat2x2f:e=>yl(ks(e[0]),ks(e[1]),ks(e[2]),ks(e[3])),mat3x3f:e=>bl(ks(e[0]),ks(e[1]),ks(e[2]),ks(e[4]),ks(e[5]),ks(e[6]),ks(e[8]),ks(e[9]),ks(e[10])),mat4x4f:e=>Eo(ks(e[0]),ks(e[1]),ks(e[2]),ks(e[3]),ks(e[4]),ks(e[5]),ks(e[6]),ks(e[7]),ks(e[8]),ks(e[9]),ks(e[10]),ks(e[11]),ks(e[12]),ks(e[13]),ks(e[14]),ks(e[15]))};function pQ(e,t){let s=uQ[t.type];if(s)return s(e);if(_r(t)){let r=Object.keys(t.propTypes),o=Mb(e,Object.values(t.propTypes));return Object.fromEntries(r.map((a,i)=>[a,o[i]]))}if(io(t)){let r=t.elementType,o=t.elementCount;return Mb(e,Array.from({length:o},()=>r))}throw new Error(`Cannot deserialize data of type ${t.type}`)}function Mb(e,t){let s=0;return t.map(r=>{if(!Td(r))return r;let o=Math.ceil(ds(r)/4),a=pQ(e.subarray(s,s+o),r);return s+=o,a})}function mQ(e,t){return Mb(e,t).map(sb)}function _Q(e){let{indexBuffer:t,dataBuffer:s,logIdToMeta:r,options:o}=e;s.read().then(a=>{a.filter(i=>i.id).forEach(({id:i,serializedData:l})=>{let{argTypes:c,op:d}=r.get(i),u=mQ(new Uint32Array(l),c);u.length===0&&u.push(\"\"),d.bind(console)(`%c${o.messagePrefix}%c ${u[0]}`,\"background: #936ff5; color: white;\",\"color: inherit; background: none\",...u.slice(1))})}),t.read().then(a=>{a>o.logCountLimit&&rr.warn(\"log-limit-exceeded\",`Log count limit per dispatch (${o.logCountLimit}) exceeded by ${a-o.logCountLimit} calls. Consider increasing the limit by passing appropriate options to tgpu.init().`)}),s.buffer.clear(),t.buffer.clear()}function fQ(e,t,s,r){return new vQ(e,t,s,r)}function z3(e){let t=e;return t?.resourceType===\"query-set\"&&!!t[ve]}var vQ=class{resourceType=\"query-set\";#e;#t=!1;#s=!0;#r=void 0;#o=void 0;[He];[ve];constructor(e,t,s,r){this.#e=!r,this[He]={type:\"query-set\",device:e.device,queryType:t,count:s,raw:r,label:void 0};let o=this;this[ve]={get readBuffer(){return o.#r||(o.#r=o[He].device.createBuffer({size:o.count*BigUint64Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ})),o.#r},get resolveBuffer(){return o.#o||(o.#o=o[He].device.createBuffer({size:o.count*BigUint64Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC})),o.#o},materialize:()=>{if(this.#t)throw new Error(\"This QuerySet has been destroyed.\");let a=this[He];return a.raw||(a.raw=a.device.createQuerySet({type:a.queryType,count:a.count})),a.raw}}}get type(){return this[He].queryType}get count(){return this[He].count}get querySet(){return this[ve].materialize()}get destroyed(){return this.#t}get available(){return this.#s}$name(e){es(this,e);let t=this[He].raw;return t&&(t.label=e),this}resolve(){if(this.#t)throw new Error(\"This QuerySet has been destroyed.\");if(!this.#s)throw new Error(\"This QuerySet is busy resolving or reading.\");let e=this[He].device.createCommandEncoder();e.resolveQuerySet(this.querySet,0,this.count,this[ve].resolveBuffer,0),this[He].device.queue.submit([e.finish()])}async read(){if(!this.#o)throw new Error(\"QuerySet must be resolved before reading.\");this.#s=!1;try{let e=this[He].device.createCommandEncoder();e.copyBufferToBuffer(this[ve].resolveBuffer,0,this[ve].readBuffer,0,this.count*BigUint64Array.BYTES_PER_ELEMENT),this[He].device.queue.submit([e.finish()]);let t=this[ve].readBuffer;await t.mapAsync(GPUMapMode.READ);let s=new BigUint64Array(t.getMappedRange().slice());return t.unmap(),Array.from(s)}finally{this.#s=!0}}destroy(){this.#t||(this.#t=!0,this[He].raw&&this.#e&&this[He].raw.destroy(),this.#r?.destroy(),this.#o?.destroy(),this.#r=this.#o=void 0)}};function VE(e,t,s){return e.timestampWrites?{...e,performanceCallback:t}:{...e,performanceCallback:t,timestampWrites:{querySet:s,beginningOfPassWriteIndex:0,endOfPassWriteIndex:1}}}function NE(e,t,s){if(!s.enabledFeatures.has(\"timestamp-query\"))throw new Error('Timestamp writes require the \"timestamp-query\" feature to be enabled on GPU device.');let r={querySet:t.querySet};return t.beginningOfPassWriteIndex!==void 0&&(r.beginningOfPassWriteIndex=t.beginningOfPassWriteIndex),t.endOfPassWriteIndex!==void 0&&(r.endOfPassWriteIndex=t.endOfPassWriteIndex),{...e,timestampWrites:r}}var F7=new WeakMap;async function hQ(e,t){if(!e.available)return;let s=await e.read();for(let{priors:r,callback:o}of t){let a=s[r.timestampWrites?.beginningOfPassWriteIndex??0],i=s[r.timestampWrites?.endOfPassWriteIndex??1];if(a===void 0||i===void 0)throw new Error(\"QuerySet did not return valid timestamps.\");await o(a,i)}}function gQ(e,t){let s=t.timestampWrites?.querySet,r=t.performanceCallback;if(!s)throw new Error(\"Cannot dispatch workgroups with performance callback without a query set.\");if(!z3(s))throw new Error(\"Performance callback with raw GPUQuerySet is not supported. Use TgpuQuerySet instead.\");let o=e[ve];if(o.adopted)return!1;let{root:a}=o;o.beforeFinish.set(s,c=>{c.resolveQuerySet(a.unwrap(s),0,s.count,s[ve].resolveBuffer,0)});let i=F7.get(e);i||(i=new Map,F7.set(e,i));let l=i.get(s);if(!l){let c=[];l=c,i.set(s,c),o.afterSubmit.set(s,()=>{i.delete(s),hQ(s,c)})}return l.some(c=>c.priors===t)?rr.warnOnce(\"suspicious\",s,\"repeated-timed-execution\",\"Repeated executions of a timed pipeline within one command encoder write to the same query set indices, so the performance callback reports only the last execution.\"):l.push({priors:t,callback:r}),!0}var yQ=class{bindGroups=new Map;vertexBuffers=new Map;currentPipeline;indexBuffer;stencilReference;appliedStencilReference=0;version=0;rawAccessed=!1},bQ=class{bindGroups=new Map;currentPipeline;version=0;rawAccessed=!1};function $E(e,t,s){ic(t)?e.bindGroups.set(t.layout,t):e.bindGroups.set(t,s),e.version++}function XE(e,t){let{priors:s}=t[ve];if(e.currentPipeline=t,s.bindGroupLayoutMap)for(let[r,o]of s.bindGroupLayoutMap)e.bindGroups.set(r,o);if(s.vertexLayoutMap)for(let[r,o]of s.vertexLayoutMap)e.vertexBuffers.set(r,{buffer:o,offset:void 0,size:void 0});s.indexBuffer&&(e.indexBuffer=s.indexBuffer),s.stencilReference!==void 0&&(e.stencilReference=s.stencilReference),e.version++}function HE(e,t){let{priors:s}=t[ve];if(e.currentPipeline=t,s.bindGroupLayoutMap)for(let[r,o]of s.bindGroupLayoutMap)e.bindGroups.set(r,o);e.version++}function xQ(e,t,s){let{buffer:r,indexFormat:o,offsetBytes:a,sizeBytes:i}=s;Fm(r)?e.setIndexBuffer(t.unwrap(r),o,a,i):e.setIndexBuffer(r,o,a,i)}function jE(e,t,s,r,o){let a=new Set(s);if(s.forEach((i,l)=>{if(r&&l===r[0])e.setBindGroup(l,t.unwrap(r[1])),a.delete(i);else{let c=o(i);c!==void 0&&(a.delete(i),ic(c)?e.setBindGroup(l,t.unwrap(c)):e.setBindGroup(l,c))}}),a.size>0)throw new dN(a)}function AQ(e,t,s,r){let o=new Set;if(s.forEach((a,i)=>{let l=r(a);!l||!l.buffer?o.add(a):Fm(l.buffer)?e.setVertexBuffer(i,t.unwrap(l.buffer),l.offset,l.size):e.setVertexBuffer(i,l.buffer,l.offset,l.size)}),o.size>0)throw new uN(o)}function wQ(e,t,s,r){let o=s[ve].core.unwrap();e.setPipeline(o.pipeline),jE(e,t,o.usedBindGroupLayouts,o.catchall,a=>r.bindGroups.get(a)),AQ(e,t,o.usedVertexLayouts,a=>r.vertexBuffers.get(a)),r.indexBuffer!==void 0&&xQ(e,t,r.indexBuffer),typeof e.setStencilReference==\"function\"&&r.stencilReference!==void 0&&(r.rawAccessed||r.stencilReference!==r.appliedStencilReference)&&(e.setStencilReference(r.stencilReference),r.appliedStencilReference=r.stencilReference)}function TQ(e,t,s,r){let o=s[ve].core.unwrap();e.setPipeline(o.pipeline),jE(e,t,o.usedBindGroupLayouts,o.catchall,a=>r.bindGroups.get(a))}function KE(e){if(!e)throw new Error(\"No index buffer is set. Call pipeline.withIndexBuffer or pass.setIndexBuffer before drawing indexed geometry.\")}function I7(e,t){rr.warnOnce(\"suspicious\",e,t,`${t} is ignored when recording into a raw GPUCommandEncoder, since there is no submission to report after. Use root['~unstable'].createCommandEncoder() instead.`)}function QE(e,t){return!e||e[ve].adopted?!1:(e[ve].afterSubmit.set(t,()=>_Q(t)),!0)}function ZE(e,t,s,r){s&&!QE(e,s)&&I7(t,\"Shader console.log output\"),r.performanceCallback&&!gQ(e,r)&&I7(t,\"The performance callback\"),r.encoder===void 0&&e.submit()}var SQ={render:{into:\"drawing into a render pass\",intoRaw:\"drawing into a raw render pass\",begin:\"beginRenderPass\"},compute:{into:\"dispatching into a compute pass\",intoRaw:\"dispatching into a raw compute pass\",begin:\"beginComputePass\"}};function JE(e,t,s,r,o,a=!1){let i=SQ[o];a&&rr.warnOnce(\"suspicious\",e,\"attachments\",`Pipeline-level attachments are ignored when ${i.into}. Pass \\`colorAttachments\\` and \\`depthStencilAttachment\\` to encoder.${i.begin} instead.`),s&&rr.warnOnce(\"suspicious\",e,\"timestampWrites\",`Pipeline-level timestamp writes are ignored when ${i.into}. Pass \\`timestampWrites\\` to encoder.${i.begin} instead.`),r&&!QE(t,r)&&rr.warnOnce(\"suspicious\",e,\"logs\",`Shader console.log output is ignored when ${i.intoRaw} encoder, since there is no submission to read it back after.`)}function Eb(e,t,s,r,o,a=!1){let{state:i,rawPass:l}=t,{core:c,priors:d}=s[ve];i.currentPipeline!==s&&XE(i,s),r&&KE(i.indexBuffer);let u=c.unwrap();a||JE(c,t.owner,!!d.timestampWrites,u.logResources,\"render\",!!d.colorAttachment||!!d.depthStencilAttachment),(i.rawAccessed||t.appliedVersion!==i.version)&&(wQ(l,e,s,i),t.appliedVersion=i.version),o(l)}function kb(e,t,s,r,o=!1){let{state:a,rawPass:i}=t,{core:l,priors:c}=s[ve];a.currentPipeline!==s&&HE(a,s);let d=l.unwrap();o||JE(l,t.owner,!!c.timestampWrites,d.logResources,\"compute\"),(a.rawAccessed||t.appliedVersion!==a.version)&&(TQ(i,e,s,a),t.appliedVersion=a.version),r(i)}function PQ(e){let t=e;return t?.resourceType===\"compute-pipeline\"&&_s(t)}function CQ(e){let t=e;return t?.resourceType===\"render-pipeline\"&&_s(t)}function MQ(e){return CQ(e)||PQ(e)}function R3(e){let t=e;return t?.resourceType===\"command-encoder\"&&_s(t)}function ek(e){let t=e;return(t?.resourceType===\"render-pass\"||t?.resourceType===\"render-bundle-encoder\")&&_s(t)}function tk(e){let t=e;return t?.resourceType===\"compute-pass\"&&_s(t)}function EQ(e){return typeof e?.getCurrentTexture==\"function\"}function sk(e){let t=e;return!_s(t)&&typeof t?.beginRenderPass==\"function\"&&typeof t?.beginComputePass==\"function\"}function kQ(e){let t=e;return!_s(t)&&typeof t?.dispatchWorkgroups==\"function\"&&t?.beginRenderPass===void 0}function BQ(e){let t=e;return!_s(t)&&typeof t?.executeBundles==\"function\"&&typeof t?.draw==\"function\"}function zQ(e){let t=e;return!_s(t)&&typeof t?.draw==\"function\"&&typeof t?.finish==\"function\"&&t?.executeBundles===void 0&&t?.beginRenderPass===void 0&&t?.dispatchWorkgroups===void 0}function a0(e,t){return Vm(t)?e.unwrap(t).createView():Eu(t)?e.unwrap(t):EQ(t)?t.getCurrentTexture().createView():t}function rk(e,t){let{querySet:s,beginningOfPassWriteIndex:r,endOfPassWriteIndex:o}=t,a={querySet:z3(s)?e.unwrap(s):s};return r!==void 0&&(a.beginningOfPassWriteIndex=r),o!==void 0&&(a.endOfPassWriteIndex=o),a}function RQ(e,t){let{rawEncoder:s,root:r}=e[ve],o={};return t?.label!==void 0&&(o.label=t.label),t?.timestampWrites!==void 0&&(o.timestampWrites=rk(r,t.timestampWrites)),new ok(r,s.beginComputePass(o),e)}function DQ(e,t){let s=new ok(e,t,void 0);return s[ve].state.rawAccessed=!0,s}var ok=class{[ve];resourceType=\"compute-pass\";#e;constructor(e,t,s){this.#e=e,this[ve]={rawPass:t,state:new bQ,owner:s,appliedVersion:void 0}}#t(e){let t=this[ve],s=t.state.currentPipeline;if(!s)throw new Error(\"Cannot dispatch without a call to pass.setPipeline\");kb(this.#e,t,s,e)}setPipeline(e){HE(this[ve].state,e)}setBindGroup(e,t){$E(this[ve].state,e,t)}dispatchWorkgroups(e,t,s){this.#t(r=>r.dispatchWorkgroups(e,t,s))}dispatchWorkgroupsIndirect(e,t){let s=ol(e)?e:e.buffer,r=ku(e,t,12,\"dispatchWorkgroupsIndirect\");this.#t(o=>o.dispatchWorkgroupsIndirect(s,r))}end(){this[ve].rawPass.end()}};function LQ(e){let t,s=\"all\";if(Vm(e)?t=e.props.format:Eu(e)&&(t=e[ve].format,s=e[ve].aspect??\"all\"),t!==void 0)return{hasDepth:t.includes(\"depth\")&&s!==\"stencil-only\",hasStencil:t.includes(\"stencil\")&&s!==\"depth-only\"}}function UQ(e,t){let s={...e,view:t},r=LQ(e.view);if(r===void 0&&(e.depthLoadOp!==void 0||e.depthStoreOp!==void 0||e.stencilLoadOp!==void 0||e.stencilStoreOp!==void 0))return s;let{hasDepth:o,hasStencil:a}=r??{hasDepth:!0,hasStencil:!1};return o&&!e.depthReadOnly&&(s.depthLoadOp??=\"clear\",s.depthStoreOp??=\"store\",s.depthClearValue??=1),a&&!e.stencilReadOnly&&(s.stencilLoadOp??=\"clear\",s.stencilStoreOp??=\"store\"),s}function OQ(e,t){let{rawEncoder:s,root:r}=e[ve],o={colorAttachments:(t.colorAttachments===void 0?[]:Array.isArray(t.colorAttachments)?t.colorAttachments:[t.colorAttachments]).map(a=>{if(a===null)return null;let i={...a,loadOp:a.loadOp??\"clear\",storeOp:a.storeOp??\"store\",view:a0(r,a.view)};return a.resolveTarget!==void 0&&(i.resolveTarget=a0(r,a.resolveTarget)),i})};return t.label!==void 0&&(o.label=t.label),t.depthStencilAttachment!==void 0&&(o.depthStencilAttachment=UQ(t.depthStencilAttachment,a0(r,t.depthStencilAttachment.view))),t.occlusionQuerySet!==void 0&&(o.occlusionQuerySet=z3(t.occlusionQuerySet)?r.unwrap(t.occlusionQuerySet):t.occlusionQuerySet),t.timestampWrites!==void 0&&(o.timestampWrites=rk(r,t.timestampWrites)),t.maxDrawCount!==void 0&&(o.maxDrawCount=t.maxDrawCount),new ak(r,s.beginRenderPass(o),e)}function GQ(e,t){return new IQ(e,e.device.createRenderBundleEncoder(t),void 0)}function FQ(e,t){let s=typeof t.executeBundles==\"function\"?new ak(e,t,void 0):new D3(e,t,void 0);return s[ve].state.rawAccessed=!0,s}var D3=class{[ve];resourceType=\"render-bundle-encoder\";#e;constructor(e,t,s){this.#e=e,this[ve]={rawPass:t,state:new yQ,owner:s,appliedVersion:void 0}}#t(e,t){let s=this[ve],r=s.state.currentPipeline;if(!r)throw new Error(\"Cannot draw without a call to pass.setPipeline\");Eb(this.#e,s,r,e,t)}setPipeline(e){XE(this[ve].state,e)}setBindGroup(e,t){$E(this[ve].state,e,t)}setVertexBuffer(e,t,s,r){let{state:o}=this[ve];o.vertexBuffers.set(e,{buffer:t,offset:s,size:r}),o.version++}setIndexBuffer(e,t,s,r){let{state:o}=this[ve];o.indexBuffer={buffer:e,indexFormat:t,offsetBytes:s,sizeBytes:r},o.version++}draw(e,t,s,r){this.#t(!1,o=>o.draw(e,t,s,r))}drawIndexed(e,t,s,r,o){this.#t(!0,a=>a.drawIndexed(e,t,s,r,o))}drawIndirect(e,t){let s=ol(e)?e:e.buffer,r=ku(e,t,16,\"drawIndirect\");this.#t(!1,o=>o.drawIndirect(s,r))}drawIndexedIndirect(e,t){let s=ol(e)?e:e.buffer,r=ku(e,t,20,\"drawIndexedIndirect\");this.#t(!0,o=>o.drawIndexedIndirect(s,r))}},IQ=class extends D3{resourceType=\"render-bundle-encoder\";finish(e){return this[ve].rawPass.finish(e)}},ak=class extends D3{resourceType=\"render-pass\";setViewport(e,t,s,r,o,a){this[ve].rawPass.setViewport(e,t,s,r,o,a)}setScissorRect(e,t,s,r){this[ve].rawPass.setScissorRect(e,t,s,r)}setBlendConstant(e){this[ve].rawPass.setBlendConstant(e)}setStencilReference(e){let{state:t}=this[ve];t.stencilReference=e,t.version++}beginOcclusionQuery(e){this[ve].rawPass.beginOcclusionQuery(e)}endOcclusionQuery(){this[ve].rawPass.endOcclusionQuery()}executeBundles(e){let t=this[ve];t.rawPass.executeBundles(e),t.appliedVersion=void 0}end(){this[ve].rawPass.end()}};function L3(e,t){return new ik(e,e.device.createCommandEncoder(t),!1)}function nk(e,t){return new ik(e,t,!0)}var ik=class{[ve];resourceType=\"command-encoder\";constructor(e,t,s){this[ve]={rawEncoder:t,root:e,adopted:s,beforeFinish:new Map,afterSubmit:new Map}}beginRenderPass(e){return OQ(this,e)}beginComputePass(e){return RQ(this,e)}#e(){let{rawEncoder:e,beforeFinish:t}=this[ve];for(let s of t.values())s(e);t.clear()}submit(){let{rawEncoder:e,root:t,afterSubmit:s}=this[ve];this.#e(),t.device.queue.submit([e.finish()]);for(let r of s.values())r();s.clear()}finish(e){let{rawEncoder:t,root:s,afterSubmit:r}=this[ve];return this.#e(),r.size>0&&rr.warnOnce(\"suspicious\",s,\"finishWithPendingWork\",\"Shader console.log output and performance callbacks do not fire for command buffers produced by encoder.finish(). Use encoder.submit() instead.\"),t.finish(e)}},lk=[\"f16\",\"clip_distances\",\"dual_source_blending\",\"subgroups\",\"primitive_index\"],ck={f16:\"shader-f16\",clip_distances:\"clip-distances\",dual_source_blending:\"dual-source-blending\",subgroups:\"subgroups\",primitive_index:\"primitive-index\"};function Bb(e,t){return e.type!==t.type?!1:ta(e)&&ta(t)?e.access===t.access&&e.addressSpace===t.addressSpace&&e.implicit===t.implicit&&Bb(e.inner,t.inner):io(e)&&io(t)?e.elementCount===t.elementCount&&Bb(e.elementType,t.elementType):_r(e)&&_r(t)?e===t:!0}var YQ=class{cache=new Map;get(e,t){let s=Fx(e);if(!s)return;if(!t&&s.ast.params.length>0)throw new Error(`Cannot resolve '${ct(e)}' directly, because it expects arguments. Either call it from another function, or wrap it in a shell`);let r=(t??[]).map((i,l)=>{if(i.value instanceof Ji){if(i.dataType===Ns)throw new jt(\"d.ref() created with primitive types must be stored in a variable before use\");return i.dataType}if(i.dataType===Ns)throw new Error(`Passed illegal value ${i.value} as the #${l} argument to ${ct(e)??\"<unnamed>\"}(...)\nShellless functions can only accept arguments representing WGSL resources: constructible WGSL types, d.refs, samplers or texture views.\nRemember, that arguments such as samplers, texture views, accessors, slots etc. should be dereferenced via '.$' first.`);let c=dn(i.dataType);return ta(c)&&c.implicit&&(c=c.inner),c}),o=this.cache.get(e);if(o){let i=o.find(l=>l.argTypes.length===r.length&&l.argTypes.every((c,d)=>Bb(c,r[d])));if(i)return i}else o=[],this.cache.set(e,o);let a=KK(r,e);return o.push(a),a}},WQ=class{[ve];constructor(e){this[ve]={strategy:e,takenGlobalIdentifiers:new Set([..._3,...f$]),shelllessRepo:new YQ,memoizedResolves:new WeakMap,memoizedLazy:new WeakMap}}};function op(e){let{names:t=\"strict\"}=e??{};return new WQ(t)}var qQ=new Set([\"bindGroupLayoutMap\",\"vertexLayoutMap\",\"indexBuffer\",\"stencilReference\",\"timestampWrites\",\"performanceCallback\"]);function dk(e){return Object.keys(e).filter(t=>!qQ.has(t)&&e[t]!==void 0)}var uk=class{#e;#t;measureResolve(e){let t=performance.mark(\"typegpu:resolution:start\"),s=e();return this.#e=performance.measure(\"typegpu:resolution\",{start:t.name}),this.#t=s.code.length,s}measureCompile(e){(async()=>{let t=performance.mark(\"typegpu:compile-start\");await e.queue.onSubmittedWorkDone();let s=performance.measure(\"typegpu:compiled\",{start:t.name});Ix?.record(\"resolution\",{resolveDuration:this.#e?.duration,compileDuration:s.duration,wgslSize:this.#t})})()}},pk=class{measureResolve(e){return e()}measureCompile(){}};function VQ(e,t,s){return new NQ(new $Q(e,t,s),{})}var NQ=class mk{[ve];[He];resourceType=\"compute-pipeline\";[Mo];constructor(t,s){this[He]={type:\"compute-pipeline\",device:t.root.device,raw:void 0,label:void 0},this[ve]={core:t,priors:s,root:t.root,materialize:()=>{let r=this[He];if(!r.raw){let o=t.unwrap();r.raw=o.pipeline,r.usedBindGroupLayouts=o.usedBindGroupLayouts,r.bindGroups=WE(o.usedBindGroupLayouts,o.catchall,s.bindGroupLayoutMap),r.timestampWrites=s.timestampWrites,r.performanceCallback=s.performanceCallback,r.nonTransferablePriors=dk(s)}return r.raw}},this[Mo]=t}[Ws](t){return t.resolve(this[ve].core)}toString(){return`computePipeline:${ct(this)??\"<unnamed>\"}`}#e(t){let{core:s,priors:r}=this[ve];return new mk(s,{...r,...t})}with(t,s){let r=this[ve];if(tk(t))return this.#e({pass:t,encoder:void 0});if(R3(t))return this.#e({pass:void 0,encoder:t});if(kQ(t))return this.#e({pass:DQ(r.root,t),encoder:void 0});if(sk(t))return this.#e({pass:void 0,encoder:nk(r.root,t)});if(ic(t)||Uh(t)){let[o,a]=ic(t)?[t.layout,t]:[t,s];return this.#e({bindGroupLayoutMap:new Map([...r.priors.bindGroupLayoutMap??[],[o,a]])})}throw new Error(\"Unsupported value passed into .with()\")}withPerformanceCallback(t){let s=this[ve];if(s.priors.timestampWrites)return this.#e({performanceCallback:t});let r=s.core.performanceCallbackQuerySet;return r?this.#e(VE(s.priors,t,r)):(rr.warn(\"webgpu-feature-missing\",\"Performance callback cannot be used because the timestamp-query feature is not enabled on the root.\"),this)}withTimestampWrites(t){let s=this[ve];return this.#e(NE(s.priors,t,s.root))}dispatchWorkgroups(t,s,r){this.#t(o=>o.dispatchWorkgroups(t,s,r))}dispatchWorkgroupsIndirect(t,s){let r=ol(t)?t:t.buffer,o=ku(t,s,12,\"dispatchWorkgroupsIndirect\");this.#t(a=>a.dispatchWorkgroupsIndirect(r,o))}initAsync(){return this[ve].core.initAsync()}initSync(){this[ve].core.initSync()}#t(t){let{core:s,priors:r,root:o}=this[ve];if(r.pass){kb(o,r.pass[ve],this,t);return}let a=r.encoder??L3(o),i=a.beginComputePass({label:ct(s)??\"<unnamed>\",timestampWrites:r.timestampWrites});kb(o,i[ve],this,t,!0),i.end(),ZE(a,s,s.unwrap().logResources,r)}$name(t){return es(this,t),this}},$Q=class _k{[ve]=!0;root;#e;#t;#s;#r;#o;#a;constructor(t,s,r){this.root=t,this.#r=s,this.#o=r,this.#e=Ix?.enabled?new uk:new pk}static precompiled(t,s){let r=new _k(t,[],void 0);return r.#s=s,r}[Ws](t){let s=this.#o;return s?t.withSlots(this.#r,()=>(t.resolve(s.compute),Je(\"\",zr,\"runtime\"))):Je(\"\",zr,\"runtime\")}toString(){return\"computePipelineCore\"}get performanceCallbackQuerySet(){if(this.root.enabledFeatures.has(\"timestamp-query\"))return this.#a??=(globalThis.__TYPEGPU_AUTONAME__??(t=>t))(this.root.createQuerySet(\"timestamp\",2),\"performanceCallbackQuerySet\")}initAsync(){if(this.#s!==void 0)return Promise.resolve();if(this.#t===void 0){let t=this.root.device,{resolutionResult:s,module:r}=this.resolveAndCreateShaderModule(),{usedBindGroupLayouts:o,catchall:a,logResources:i}=s;this.#t=t.createComputePipelineAsync({label:ct(this)??\"<unnamed>\",layout:t.createPipelineLayout({label:`${ct(this)??\"<unnamed>\"} - Pipeline Layout`,bindGroupLayouts:o.map(l=>this.root.unwrap(l))}),compute:{module:r}}).then(l=>{this.#s={pipeline:l,usedBindGroupLayouts:o,catchall:a,logResources:i},this.#e.measureCompile(t)}).finally(()=>{this.#t=void 0})}return this.#t}initSync(){if(this.#s!==void 0)return;if(this.#t!==void 0)throw new Error(\"'pipeline.initAsync()' was called and is not yet resolved.\");let t=this.root.device,{resolutionResult:s,module:r}=this.resolveAndCreateShaderModule(),{usedBindGroupLayouts:o,catchall:a,logResources:i}=s;this.#s={pipeline:(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(t.createComputePipeline({label:ct(this)??\"<unnamed>\",layout:t.createPipelineLayout({label:`${ct(this)??\"<unnamed>\"} - Pipeline Layout`,bindGroupLayouts:o.map(l=>this.root.unwrap(l))}),compute:{module:r}}),\"pipeline\"),usedBindGroupLayouts:o,catchall:a,logResources:i},this.#e.measureCompile(t)}unwrap(){return this.initSync(),this.#s}resolveAndCreateShaderModule(){let t=this.root.device,s=lk.filter(c=>this.root.enabledFeatures.has(ck[c])),r=op({names:this.root.nameRegistrySetting}),o=this.#e.measureResolve(()=>Gh(this,{namespace:r,minify:this.root.minify,enableExtensions:s,shaderGenerator:this.root.shaderGeneratorClass?new this.root.shaderGeneratorClass:void 0,root:this.root})),{code:a,usedBindGroupLayouts:i,catchall:l}=o;return l!==void 0&&i[l[0]]?.$name(`${ct(this)??\"<unnamed>\"} - Automatic Bind Group & Layout`),gE(i,t.limits),{resolutionResult:o,module:t.createShaderModule({label:`${ct(this)??\"<unnamed>\"} - Shader`,code:a})}}},XQ={$vertexIndex:Qo.vertexIndex,$instanceIndex:Qo.instanceIndex},HQ={$clipDistances:Qo.clipDistances,$position:Qo.position},jQ={$position:Qo.position,$frontFacing:Qo.frontFacing,$primitiveIndex:Qo.primitiveIndex,$sampleIndex:Qo.sampleIndex,$sampleMask:Qo.sampleMask,$subgroupInvocationId:Qo.subgroupInvocationId,$subgroupSize:Qo.subgroupSize},KQ={$fragDepth:Qo.fragDepth,$sampleMask:Qo.sampleMask},U3=class{#e;autoIn;autoOut;constructor(e,t,s){ct(e)||es(e,\"fragmentFn\"),this.#e=Ed(e,\"fragment\"),this.autoIn=new ti({...jQ,...t},void 0,s),es(this.autoIn,\"FragmentIn\"),this.autoOut=new ti(KQ,qt),es(this.autoOut,\"FragmentOut\")}toString(){return\"autoFragmentFn\"}[Ws](e){return e.withSlots([[rp,\"fragment\"]],()=>this.#e.resolve(e,[this.autoIn],this.autoOut))}};U3.prototype[ve]=!0;U3.prototype.resourceType=\"auto-fragment-fn\";var O3=class{#e;autoIn;autoOut;constructor(e,t,s){ct(e)||es(e,\"vertexFn\"),this.#e=Ed(e,\"vertex\"),this.autoIn=new ti({...XQ,...t},void 0,s),es(this.autoIn,\"VertexIn\"),this.autoOut=new ti(HQ,void 0),es(this.autoOut,\"VertexOut\")}toString(){return\"autoVertexFn\"}[Ws](e){return e.withSlots([[rp,\"vertex\"]],()=>this.#e.resolve(e,[this.autoIn],this.autoOut))}};O3.prototype[ve]=!0;O3.prototype.resourceType=\"auto-vertex-fn\";function fk(e,t=\"vertex\"){return new QQ(e,t)}function vk(e){return e?.resourceType===\"vertex-layout\"}var zb=Symbol(\"defaultAttribEntry\");function B1(e,t,s,r,o){if(gl(t)||vc(t)){let a=sc(t);return a!==void 0&&(r[o??zb]=a),B1(e,t.inner,Co(s,wn(t)),r)}if(_r(t)){let a=s,i=t.propTypes;return Object.fromEntries(Object.entries(i).map(([l,c])=>{a=Co(a,xr(c));let d=[l,B1(e,c,a,r,l)];return a+=ds(c),d}))}if(ki(t)){let a=s,i=t.propTypes;return Object.fromEntries(Object.entries(i).map(([l,c])=>{a=Co(a,wn(c));let d=[l,B1(e,c,a,r,l)];return a+=ds(c),d}))}if(\"type\"in t&&typeof t.type==\"string\"){if(HC.includes(t.type))return{_layout:e,format:t.type,offset:s};let a=pN[t.type];if(a)return{_layout:e,format:a,offset:s}}throw new Error(`Unsupported data used in vertex layout: ${String(t)}`)}var QQ=class{[ve]=!0;[He];resourceType=\"vertex-layout\";stride;attrib;schemaForCount;#e={};constructor(e,t){this.schemaForCount=e;let s=e(0);this[He]={type:\"vertex-layout\",schema:s,stepMode:t,label:void 0},this.stride=Co(ds(s.elementType),xr(s)),this.attrib=B1(this,s.elementType,0,this.#e)}get stepMode(){return this[He].stepMode}get vertexLayout(){if(this.#e[zb]!==void 0){if(typeof this.attrib.format!=\"string\"||typeof this.attrib.offset!=\"number\")throw new Error(\"Single attribute vertex layouts must have a format and offset.\");return{arrayStride:this.stride,stepMode:this.stepMode,attributes:[{format:this.attrib.format,offset:this.attrib.offset,shaderLocation:this.#e[zb]}]}}if(!Object.keys(this.attrib).every(e=>this.#e[e]!==void 0))throw new Error(\"All attributes must have custom locations in order to unwrap a vertex layout.\");return{arrayStride:this.stride,stepMode:this.stepMode,attributes:Object.entries(this.attrib).map(([e,t])=>({format:t.format,offset:t.offset,shaderLocation:this.#e[e]}))}}$name(e){return es(this,e),this}};function ZQ(e){return!!e?.view}function JQ(e,t){if(hh(e)||wi(e))return[];if(_r(e)){let s=[];for(let r of Object.keys(e.propTypes)){let o=e.propTypes[r];if(wi(o))continue;let a=t[r];if(!a)throw new Error(`A color attachment by the name of '${r}' was not provided to the shader.`);s.push(a)}return s}if(!ZQ(t))throw new Error(\"Expected a single color attachment, not a record.\");return[t]}function eZ(e,t){let s;if(hh(e)||wi(e))return[null];if(_r(e)){let o=[];for(let a of Object.keys(e.propTypes)){let i=e.propTypes[a];if(wi(i))continue;let l=t[a];o.push({...l,format:l?.format??(s??=navigator.gpu.getPreferredCanvasFormat())})}return o}let r=t;return[{...r,format:r?.format??(s??=navigator.gpu.getPreferredCanvasFormat())}]}function tZ(e){return new sZ(new rZ(e),{})}var sZ=class hk{[ve];[He];resourceType=\"render-pipeline\";[Mo];constructor(t,s){this[He]={type:\"render-pipeline\",device:t.options.root.device,raw:void 0,label:void 0},this[ve]={core:t,priors:s,root:t.options.root,materialize:()=>{let r=this[He];if(!r.raw){let o=t.unwrap();r.raw=o.pipeline,r.usedBindGroupLayouts=o.usedBindGroupLayouts,r.usedVertexLayouts=o.usedVertexLayouts,r.fragmentOut=o.fragmentOut,r.bindGroups=WE(o.usedBindGroupLayouts,o.catchall,s.bindGroupLayoutMap),r.vertexBuffers=sQ(o.usedVertexLayouts,s.vertexLayoutMap),r.indexBuffer=s.indexBuffer,r.stencilReference=s.stencilReference,r.timestampWrites=s.timestampWrites,r.performanceCallback=s.performanceCallback,r.nonTransferablePriors=dk(s)}return r.raw}},this[Mo]=t}[Ws](t){return t.resolve(this[ve].core)}toString(){return`renderPipeline:${ct(this)??\"<unnamed>\"}`}$name(t){return es(this,t),this}#e(t){let{core:s,priors:r}=this[ve];return new hk(s,{...r,...t})}with(t,s){let r=this[ve];if(ek(t))return this.#e({pass:t,encoder:void 0});if(R3(t))return this.#e({pass:void 0,encoder:t});if(BQ(t)||zQ(t))return this.#e({pass:FQ(r.root,t),encoder:void 0});if(sk(t))return this.#e({pass:void 0,encoder:nk(r.root,t)});if(ic(t)||Uh(t)){let[o,a]=ic(t)?[t.layout,t]:[t,s];return this.#e({bindGroupLayoutMap:new Map([...r.priors.bindGroupLayoutMap??[],[o,a]])})}if(vk(t))return this.#e({vertexLayoutMap:new Map([...r.priors.vertexLayoutMap??[],[t,s]])});throw new Error(\"Unsupported value passed into .with()\")}withPerformanceCallback(t){let s=this[ve];if(s.priors.timestampWrites)return this.#e({performanceCallback:t});let r=s.core.performanceCallbackQuerySet;return r?this.#e(VE(s.priors,t,r)):(rr.warn(\"webgpu-feature-missing\",\"Performance callback cannot be used because the timestamp-query feature is not enabled on the root.\"),this)}withTimestampWrites(t){let s=this[ve];return this.#e(NE(s.priors,t,s.root))}withColorAttachment(t){return this.#e({colorAttachment:t})}withDepthStencilAttachment(t){return this.#e({depthStencilAttachment:t})}withStencilReference(t){return this.#e({stencilReference:t})}withIndexBuffer(t,s,r,o){if(ol(t)){if(typeof s!=\"string\")throw new Error(\"If a GPUBuffer is passed, indexFormat must be provided.\");return this.#e({indexBuffer:{buffer:t,indexFormat:s,offsetBytes:r,sizeBytes:o}})}let a={u32:\"uint32\",u16:\"uint16\"},i=t.dataType.elementType;return this.#e({indexBuffer:{buffer:t,indexFormat:a[i.type],offsetBytes:s!==void 0?s*ds(i):void 0,sizeBytes:o!==void 0?o*ds(i):void 0}})}initAsync(){return this[ve].core.initAsync()}initSync(){this[ve].core.initSync()}get hasIndexBuffer(){return this[ve].priors.indexBuffer!==void 0}#t(){let{core:t,priors:s}=this[ve],{fragmentOut:r}=t.unwrap();return{label:ct(t)??\"<unnamed>\",colorAttachments:r?JQ(r,s.colorAttachment??{}):[],depthStencilAttachment:s.depthStencilAttachment,timestampWrites:s.timestampWrites}}#s(t,s){let{core:r,priors:o,root:a}=this[ve];if(o.pass){Eb(a,o.pass[ve],this,t,s);return}t&&KE(o.indexBuffer);let i=o.encoder??L3(a),l=i.beginRenderPass(this.#t());Eb(a,l[ve],this,t,s,!0),l.end(),ZE(i,r,r.unwrap().logResources,o)}draw(t,s,r,o){this.#s(!1,a=>a.draw(t,s,r,o))}drawIndexed(t,s,r,o,a){this.#s(!0,i=>i.drawIndexed(t,s,r,o,a))}drawIndirect(t,s){let r=ol(t)?t:t.buffer,o=ku(t,s,16,\"drawIndirect\");this.#s(!1,a=>a.drawIndirect(r,o))}drawIndexedIndirect(t,s){let r=ol(t)?t:t.buffer,o=ku(t,s,20,\"drawIndexedIndirect\");this.#s(!0,a=>a.drawIndexedIndirect(r,o))}},rZ=class gk{[ve]=!0;options;#e;#t;#s;#r;#o;#a;constructor(t){this.options=t,this.#e=Ix?.enabled?new uk:new pk}static precompiled(t,s){let r=new gk({root:t,slotBindings:[],descriptor:void 0});return r.#s=s,r}[Ws](t){let{slotBindings:s}=this.options,{vertex:r,fragment:o,attribs:a={}}=this.options.descriptor??{};if(!r)return Je(\"\",zr,\"runtime\");this.#r=void 0,this.#o=void 0;let i=oZ(r?.shell?.out,o?.shell?.in,ct(r)??\"<unnamed>\",ct(o)??\"<unnamed>\");return t.withVaryingLocations(i,()=>t.withSlots(s,()=>{let l;if(typeof r==\"function\"){let c=new O3(r,Object.fromEntries(Object.entries(a).map(([d,u])=>[d,dl[u.format]])),i);t.resolve(c),this.#r=c.autoIn.completeStruct.propTypes,l=c.autoOut.completeStruct}else l=t.resolve(r).dataType;if(o)if(typeof o==\"function\"){let c=Object.fromEntries(Object.entries(l.propTypes).filter(([,d])=>!wi(d)));this.#o=t.resolve(new U3(o,c,i)).dataType}else t.resolve(o);return Je(\"\",zr,\"runtime\")}))}toString(){return\"renderPipelineCore\"}get performanceCallbackQuerySet(){if(this.options.root.enabledFeatures.has(\"timestamp-query\"))return this.#a??=(globalThis.__TYPEGPU_AUTONAME__??(t=>t))(this.options.root.createQuerySet(\"timestamp\",2),\"performanceCallbackQuerySet\")}initAsync(){if(this.#s!==void 0)return Promise.resolve();if(this.#t===void 0){let t=this.options.root.device,{resolutionResult:s,descriptor:r,connectedAttribs:o,fragmentOut:a}=this.resolveAndCreateShaderModule(),{usedBindGroupLayouts:i,catchall:l,logResources:c}=s;this.#t=t.createRenderPipelineAsync(r).then(d=>{this.#s={pipeline:d,usedBindGroupLayouts:i,catchall:l,logResources:c,usedVertexLayouts:o.usedVertexLayouts,fragmentOut:a},this.#e.measureCompile(t)}).finally(()=>{this.#t=void 0})}return this.#t}initSync(){if(this.#s!==void 0)return;if(this.#t!==void 0)throw new Error(\"'pipeline.initAsync()' was called and is not yet resolved.\");let t=this.options.root.device,{resolutionResult:s,descriptor:r,connectedAttribs:o,fragmentOut:a}=this.resolveAndCreateShaderModule(),{usedBindGroupLayouts:i,catchall:l,logResources:c}=s;this.#s={pipeline:(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(t.createRenderPipeline(r),\"pipeline\"),usedBindGroupLayouts:i,catchall:l,logResources:c,usedVertexLayouts:o.usedVertexLayouts,fragmentOut:a},this.#e.measureCompile(t)}unwrap(){return this.initSync(),this.#s}resolveAndCreateShaderModule(){let{root:t,descriptor:s}=this.options;if(!s)throw new Error(\"Precompiled pipelines are never resolved again.\");let r=t.device,o=lk.filter(C=>t.enabledFeatures.has(ck[C])),a=op({names:t.nameRegistrySetting}),i=this.#e.measureResolve(()=>Gh(this,{namespace:a,minify:t.minify,enableExtensions:o,shaderGenerator:t.shaderGeneratorClass?new t.shaderGeneratorClass:void 0,root:t})),{code:l,usedBindGroupLayouts:c,catchall:d}=i;d!==void 0&&c[d[0]]?.$name(`${ct(this)??\"<unnamed>\"} - Automatic Bind Group & Layout`),gE(c,r.limits);let u=r.createShaderModule({label:`${ct(this)??\"<unnamed>\"} - Shader`,code:l}),{vertex:p,fragment:_,attribs:f={},targets:h}=s,g=Tj(p?.shell?.in??this.#r??{},f),y=_?.shell?.returnType??this.#o,b=y?eZ(y,h):[null],x={layout:r.createPipelineLayout({label:`${ct(this)??\"<unnamed>\"} - Pipeline Layout`,bindGroupLayouts:c.map(C=>t.unwrap(C))}),vertex:{module:u,buffers:g.bufferDefinitions}},P=ct(this);return P!==void 0&&(x.label=P),s.fragment&&(x.fragment={module:u,targets:b}),s.primitive&&(Td(s.primitive.stripIndexFormat)?x.primitive={...s.primitive,stripIndexFormat:{u32:\"uint32\",u16:\"uint16\"}[s.primitive.stripIndexFormat.type]}:x.primitive=s.primitive),s.depthStencil&&(x.depthStencil=s.depthStencil),s.multisample&&(x.multisample=s.multisample),{resolutionResult:i,descriptor:x,connectedAttribs:g,fragmentOut:y}}};function oZ(e={},t={},s,r){let o={},a=new Set;function i(c,d){o[c]=d,a.add(d)}for(let[c,d]of Object.entries(e)){let u=sc(d);u!==void 0&&i(c,u)}for(let[c,d]of Object.entries(t)){let u=sc(d);u!==void 0&&(o[c]===void 0?i(c,u):o[c]!==u&&rr.warn(\"locations-mismatched\",`Mismatched location between vertexFn (${s}) output (${o[c]}) and fragmentFn (${r}) input (${u}) for the key \"${c}\", using the location set on vertex output.`))}let l=0;for(let c of Object.keys(e??{}))if(!(wi(e[c])||o[c]!==void 0)){for(;a.has(l);)l++;i(c,l)}return o}var Y7=class{#e=new WeakMap;#t;constructor(e){this.#t=e}getOrMake(e,...t){if(this.#e.has(e))return this.#e.get(e);let s=this.#t(e,...t);return this.#e.set(e,s),s}};function G3(e){let t={argTypes:Object.values(e.in??{}),returnType:zr,workgroupSize:e.workgroupSize,entryPoint:\"compute\"},s=QM(e.in??{});return Object.assign((o,...a)=>aZ(t,e.workgroupSize,Rh(o,...a),s),t)}function aZ(e,t,s,r){let o=Ed(s,\"compute\",t);return{shell:e,$uses(a){return o.setExternals(\"userProvided\",a),this},[ve]:!0,[Mo]:o,$name(a){return es(this,a),this},[Ws](a){return a.withSlots([[rp,\"compute\"]],()=>o.resolve(a,[],e.returnType,r))},toString(){return`computeFn:${ct(o)??\"<unnamed>\"}`}}}function nZ(e){if(e.includes(0))throw new Error(\"Size and workgroupSize cannot contain zeroes.\");return Ps(e[0]??1,e[1]??1,e[2]??1)}var iZ=[Ps(1,1,1),Ps(256,1,1),Ps(16,16,1),Ps(8,8,4)],lZ=class z1{resourceType=\"guarded-compute-pipeline\";[He];#e;#t;constructor(t,s,r,o){this.#e=t,this.#t=Ps(),this[He]={type:\"guarded-compute-pipeline\",device:t.device,pipeline:s,sizeUniform:r,workgroupSize:o,label:void 0}}with(t){if(!ic(t))throw new Error(\"Guarded pipelines only accept bind groups in .with(). To record into passes or encoders, use a regular compute pipeline.\");return new z1(this.#e,this[He].pipeline.with(t),this[He].sizeUniform,this[He].workgroupSize)}withPerformanceCallback(t){return new z1(this.#e,this[He].pipeline.withPerformanceCallback(t),this[He].sizeUniform,this[He].workgroupSize)}withTimestampWrites(t){return new z1(this.#e,this[He].pipeline.withTimestampWrites(t),this[He].sizeUniform,this[He].workgroupSize)}dispatchThreads(...t){let s=nZ(t),r=nl(Zs(s).div(Zs(this[He].workgroupSize)));vj(s,this.#t)||(this.#t=s,this[He].sizeUniform.write(s)),this[He].pipeline.dispatchWorkgroups(r.x,r.y,r.z)}initAsync(){return this[He].pipeline.initAsync()}initSync(){this[He].pipeline.initSync()}get pipeline(){return this[He].pipeline}get sizeUniform(){return this[He].sizeUniform}[ve]=!0;get[Mo](){return this[He].pipeline}$name(t){return es(this,t),this}},cZ=class Rb{#e;#t;constructor(t,s){this.#e=t,this.#t=s}with(t,s){return new Rb(this.#e,[...this.#t,[Qu(t)||Zu(t)?t.slot:t,s]])}createComputePipeline(t){return VQ(this.#e(),this.#t,t)}createRenderPipeline(t){return tZ({root:this.#e(),slotBindings:this.#t,descriptor:t})}createGuardedComputePipeline(t){let s=this.#e();if(t.length>=4)throw new Error(\"Guarded compute callback only supports up to three dimensions.\");let r=iZ[t.length],o=nr([M,M,M])(t);ct(o)===void 0&&o.$name(\"wrappedCallback\");let a=(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(s.createUniform(Ps),\"sizeUniform\"),i=(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(G3({workgroupSize:r,in:{id:Qo.globalInvocationId}})`{\n  if (any(in.id >= sizeUniform)) {\n    return;\n  }\n  wrappedCallback(in.id.x, in.id.y, in.id.z);\n}`.$uses({sizeUniform:a,wrappedCallback:o}),\"mainCompute\");return new lZ(s,this.createComputePipeline({compute:i}),a,r)}pipe(t){let s=t(new yE([]));return new Rb(this.#e,[...this.#t,...s.bindings])}},yk=class extends cZ{\"~unstable\";resourceType=\"root\";[He];device;nameRegistrySetting;shaderGeneratorClass;#e=new Y7(e=>e.unwrap(this));#t=new Y7(e=>e.unwrap(this));#s;[ve];constructor(e,t,s,r,o,a){super(()=>this,[]),this.device=e,this.nameRegistrySetting=t,this.#s=r,this.shaderGeneratorClass=a,this[\"~unstable\"]=this,this[He]={type:\"root\",device:e,nameRegistrySetting:t,logOptions:o,minify:s,nonTransferablePriors:a?[\"shaderGeneratorClass\"]:void 0,label:void 0},this[ve]={logOptions:o}}configureContext(e){let t=e.canvas.getContext(\"webgpu\");if(!t)throw new Error(\"Unable to initialize 'webgpu' context on the provided canvas.\");return t.configure({...e,device:this.device,format:e.format??navigator.gpu.getPreferredCanvasFormat()}),t}get enabledFeatures(){return new Set(this.device.features)}get minify(){return this[He].minify}createBuffer(e,t){return Qf(this,e,t)}createUniform(e,t){return new ld(\"uniform\",Qf(this,e,t).$usage(\"uniform\"))}createMutable(e,t){return new ld(\"mutable\",Qf(this,e,t).$usage(\"storage\"))}createReadonly(e,t){return new ld(\"readonly\",Qf(this,e,t).$usage(\"storage\"))}createQuerySet(e,t,s){return fQ(this,e,t,s)}createBindGroup(e,t){return new EE(this,e,t)}destroy(){BK(this.device),this.#s&&this.device.destroy()}createTexture(e){return LK(e,this)}createSampler(e){return dK(e,this)}createComparisonSampler(e){return uK(e,this)}unwrap(e){if(R3(e))return e[ve].rawEncoder;if(ek(e)||tk(e))return e[ve].state.rawAccessed=!0,e[ve].rawPass;if(Uh(e))return this.#e.getOrMake(e);if(ic(e))return this.#t.getOrMake(e);if(__(e))return e.buffer.buffer;if(Eu(e)){if(!e[ve].unwrap)throw new Error(\"Cannot unwrap laid-out texture view as it has no underlying resource.\");return e[ve].unwrap()}if(vk(e))return e.vertexLayout;let t=e[ve];if(t?.materialize)return t.materialize();throw k3(e)||B3(e)?new Error(\"Cannot unwrap laid-out sampler.\"):new Error(`Unknown resource type: ${pn(e)}`)}createCommandEncoder(e){return L3(this,e)}createRenderBundleEncoder(e){return GQ(this,e)}};async function dZ(e){let{adapter:t,device:s,unstable_names:r=\"strict\",unstable_minify:o=!1,unstable_logOptions:a,unstable_shaderGeneratorClass:i}=e??{},{optionalFeatures:l,...c}=s??{};if(!navigator.gpu)throw new Error(\"WebGPU is not supported by this browser.\");let d=await navigator.gpu.requestAdapter(t);if(!d)throw new Error(\"Could not find a compatible GPU\");let u=[];for(let p of c.requiredFeatures??[]){if(!d.features.has(p))throw new Error(`Requested feature \"${p}\" is not supported by the adapter.`);u.push(p)}for(let p of l??[])d.features.has(p)?u.push(p):rr.warn(\"webgpu-feature-missing\",`Optional feature \"${p}\" is not supported by the adapter.`);return new yk(await d.requestDevice({...c,requiredFeatures:u}),r,o,!0,a??{},i)}function uZ(e){let{device:t,unstable_names:s=\"strict\",unstable_minify:r=!1,unstable_logOptions:o,unstable_shaderGeneratorClass:a}=e??{};return new yk(t,s,r,!1,o??{},a)}function bk(e,t){return new pZ(e,t)}function xk(e,t){return new mZ(e,t)}function F3(e){let t=_d(),s=K1(t.unwrap(e.slot));for(;typeof s==\"function\"&&!pb(s)&&!N1(s)&&!Pu(s);)s=s(),n_(s)&&(s=s.value);let r=bh(s);if(r)return r;if(N1(s))return s[mo].call(t,[]);if(pb(s)||Pu(s)){let o=t.resolve(s);return t.withResetIndentLevel(()=>Je(`${o.value}()`,e.schema,\"runtime\",o.possibleSideEffects))}t.pushMode(new Ju);try{return Je(rc(e.schema,s),e.schema,\"constant\",!1)}finally{t.popMode(\"normal\")}}var Ak=class Db{[Mo];[He];slot;static#e=void(Db.prototype[ve]=!0,Al(Db.prototype,{asString(){return`${this.resourceType}:${ct(this)??\"<unnamed>\"}`},resolve(t){return t.resolveSnippet(F3(this))}}));constructor(t,s,r=void 0){this[He]={type:t,schema:o_(s)?s:s(0),defaultValue:r,label:void 0},this.slot=v_(r),this[Mo]=this.slot}get schema(){return this[He].schema}get defaultValue(){return this[He].defaultValue}$name(t){return es(this,t),this.defaultValue&&typeof this.defaultValue==\"function\"&&!ct(this.defaultValue)&&es(this.defaultValue,t),this}},pZ=class Lb extends Ak{static#e=void(Lb.prototype.resourceType=\"accessor\",Bi(Lb.prototype,{codegenMode:{get(){return F3(this)}},normalMode:{get(){throw new Error(\"`tgpu.accessor` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call. Use `tgpu.slot` for non-WGSL values instead.\")}}}));constructor(t,s=void 0){super(\"accessor\",t,s)}},mZ=class Ub extends Ak{static#e=void(Ub.prototype.resourceType=\"mutable-accessor\",Bi(Ub.prototype,{codegenMode:{get(){return F3(this)}},normalMode:{get(){throw new Error(\"`tgpu.mutableAccessor` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call. Use `tgpu.slot` for non-WGSL values instead.\")}}}));constructor(t,s=void 0){super(\"mutable-accessor\",t,s)}};function Ob(e,t){return Array.isArray(e)?vZ(e,t):fZ(e)}function _Z(e,t){return Array.isArray(e)?Ob(e,t).code:Ob(e).code}function fZ(e){let{template:t,externals:s,unstable_shaderGenerator:r,names:o=\"strict\",unstable_minify:a,config:i,enableExtensions:l}=e;t||rr.warn(\"deprecated\",\"Calling resolve with an empty template is deprecated and will soon return an empty string. Consider using the 'tgpu.resolve(resolvableArray, options)' API instead.\");let c={[ve]:!0,[Ws](u){return Je(zh(u,s,t??\"\"),zr,\"runtime\")},toString:()=>\"<root>\"},d=wk(Object.values(s));return Gh(c,{namespace:typeof o==\"string\"?op({names:o}):o,minify:a??d?.minify??!1,enableExtensions:l,shaderGenerator:r,config:i,root:d})}function vZ(e,t){let{unstable_shaderGenerator:s,names:r=\"strict\",unstable_minify:o,config:a,enableExtensions:i}=t??{},l={[ve]:!0,[Ws](d){for(let u of e)if(Uh(u))for(let p of Object.values(u[ve]))d.resolve(p);else d.resolve(u);return Je(\"\",zr,\"runtime\")},toString:()=>\"<root>\"},c=wk(e);return Gh(l,{namespace:typeof r==\"string\"?op({names:r}):r,minify:o??c?.minify??!1,enableExtensions:i,shaderGenerator:s,config:a,root:c})}function wk(e){let t=new Map;for(let s of e){let r=Gb(s);if(r){let o=t.get(r)??new Set;t.set(r,o),o.add(s)}}if(t.size>1){let s=[...t.values()].map((r,o)=>`root ${o+1}: ${[...r].map(a=>ct(a)??\"<unnamed>\").join(\", \")}`).join(\"; \");throw new Error(`Found resources originating from different roots in a single resolve (${s}).`)}return[...t.keys()][0]}function Gb(e){if(MQ(e)||Fm(e)||Vm(e)||k3(e)||B3(e))return e[ve].root;if(__(e))return Gb(e.buffer);if(Eu(e))return Gb(e?.[He]?.texture)}function Tk(e){if(_d())throw new Error(\"Cannot create tgpu.lazy objects during shader resolution.\");return new hZ(e,void 0)}var hZ=class R1{[ve];[ya];static#e=void(R1.prototype.resourceType=\"lazy\",Bi(Al(R1.prototype,{asString:()=>\"lazy\",resolve(t){throw new Error(\"Unreachable, is never resolved directly\")}}),{codegenMode:{get(){return this.#t()}},normalMode:{get(){return this.#t()}}}));constructor(t,s){this[ve]={compute:t},this[ya]=s}get[Ka](){let t=_d();if(!t)throw new Error(\"Cannot access tgpu.lazy's value outside of resolution.\");return K1(t.unwrap(this))}get $(){return this[Ka]}#t(){let t=_d();if(!t)throw new Error(\"Cannot access tgpu.lazy's value outside of resolution.\");return K1(t.unwrap(this))}with(t,s){return new R1(this[ve].compute.bind(this),{inner:this[ya]?.inner??this,pairs:[...this[ya]?.pairs??[],[Qu(t)||Zu(t)?t.slot:t,s]]})}};function Sk(e){let t={in:e.in,out:e.out,returnType:p_(e.out),entryPoint:\"fragment\"};return Object.assign((r,...o)=>gZ(t,Rh(r,...o)),t)}function gZ(e,t){let s=Ed(t,\"fragment\"),r=e.returnType;return typeof t==\"string\"&&eE(t,r,s),{shell:e,outputType:r,$uses(o){return s.setExternals(\"userProvided\",o),this},[ve]:!0,[Mo]:s,$name(o){return es(this,o),Su(r)&&r.$name(`${o}_Output`),this},[Ws](o){let a=QM(e.in??{},o.varyingLocations??{});return a.dataSchema&&Su(a.dataSchema)&&a.dataSchema.$name(`${ct(this)??\"\"}_Input`),typeof t==\"string\"&&s.setExternals(\"out\",{Out:r}),o.withSlots([[rp,\"fragment\"]],()=>s.resolve(o,[],e.returnType,a))},toString(){return`fragmentFn:${ct(s)??\"<unnamed>\"}`}}}function Fh(e){if(Object.keys(e.out).length===0)throw new Error(\"A vertexFn output cannot be empty since it must include the 'position' builtin.\");let t={in:e.in,out:e.out,argTypes:e.in&&Object.keys(e.in).length!==0?[p_(e.in)]:[],entryPoint:\"vertex\"};return Object.assign((r,...o)=>yZ(t,Rh(r,...o)),t)}function yZ(e,t){let s=Ed(t,\"vertex\"),r=jX(e.in??{});return{shell:e,$uses(o){return s.setExternals(\"userProvided\",o),this},[ve]:!0,[Mo]:s,$name(o){return es(this,o),this},[Ws](o){let a=p_(e.out,o.varyingLocations).$name(`${ct(this)??\"\"}_Output`);return typeof t==\"string\"&&s.setExternals(\"out\",{Out:a}),o.withSlots([[rp,\"vertex\"]],()=>s.resolve(o,[],a,r))},toString(){return`vertexFn:${ct(s)??\"<unnamed>\"}`}}}function bZ(e){return new xZ(e)}var xZ=class{[ve]=!0;#e;#t;constructor(e){this.#t=e}$uses(e){if(this.#e!==void 0)throw new Error(\"Cannot call '$uses' multiple times. If you wish to override dependencies, use slots or accessors instead.\");return this.#e=e,this}[Ws](e){let t=zh(e,this.#e??{},this.#t);return e.addDeclaration(t),Je(\"\",zr,\"constant\")}toString(){return`declare: ${this.#t}`}};function AZ(e,t,s=\"runtime\",r=!0){return new wZ(e,t,s,r)}var wZ=class Fb{dataType;origin;possibleSideEffects;#e;#t;static#s=void(Fb.prototype[ve]=!0,Bi(Al(Fb.prototype,{asString(){return`raw(${String(this.dataType)}): \"${this.#e}\"`},resolve(t){return Je(zh(t,this.#t??{},this.#e),this.dataType,this.origin,this.possibleSideEffects)}}),{codegenMode:{getBaseSnippet(t){return Je(t,this.dataType,this.origin,this.possibleSideEffects)}},normalMode:{get(){throw new Error(\"Raw code snippets can only be used on the GPU.\")}}}));constructor(t,s,r,o){this.dataType=s,this.origin=r,this.possibleSideEffects=o,this.#e=t}$uses(t){if(this.#t!==void 0)throw new Error(\"Cannot call '$uses' multiple times. If you wish to override dependencies, use slots or accessors instead.\");return this.#t=t,this}};function TZ(e){let t=_d()??new YE({minify:!1,namespace:op()}),s=[1,1,1],r=[1,1,1],o=[s[0]*r[0],s[1]*r[1],s[2]*r[2]],a=new Map,i=Array.from({length:s[0]},()=>Array.from({length:s[1]},()=>Array.from({length:s[2]},()=>new Map))),l=Array.from({length:o[0]},()=>Array.from({length:o[1]},()=>Array.from({length:o[2]},()=>new Map))),c=Array.from({length:o[0]},(d,u)=>Array.from({length:o[1]},(p,_)=>Array.from({length:o[2]},(f,h)=>{let g=Math.floor(u/r[0]),y=Math.floor(_/r[1]),b=Math.floor(h/r[2]);return new bN(a,{private:l[u][_][h],workgroup:i[g][y][b]})})));t.pushMode(c[0][0][0]);try{return{value:JC(t,e),buffers:a,privateVars:l,workgroupVars:i}}finally{t.popMode(\"simulate\")}}var SZ=ko({accessor:()=>zZ,comptime:()=>CZ,computeFn:()=>MZ,const:()=>PZ,declare:()=>bZ,derived:()=>DZ,fn:()=>EZ,fragmentFn:()=>kZ,mutableAccessor:()=>RZ,namespace:()=>op,privateVar:()=>UZ,rawCodeSnippet:()=>AZ,simulate:()=>TZ,slot:()=>LZ,vertexFn:()=>BZ,vertexLayout:()=>GZ,workgroupVar:()=>OZ}),PZ=w3,CZ=Tn,MZ=G3,EZ=nr,kZ=Sk,BZ=Fh,zZ=bk,RZ=xk,DZ=Tk,LZ=v_,UZ=Oh,OZ=kE,GZ=fk,FZ=ko({accessor:()=>bk,bindGroupLayout:()=>CE,comptime:()=>Tn,computeFn:()=>G3,const:()=>w3,fn:()=>nr,fragmentFn:()=>Sk,init:()=>dZ,initFromDevice:()=>uZ,lazy:()=>Tk,mutableAccessor:()=>xk,privateVar:()=>Oh,resolve:()=>_Z,resolveWithContext:()=>Ob,slot:()=>v_,unroll:()=>uE,vertexFn:()=>Fh,vertexLayout:()=>fk,workgroupVar:()=>kE,\"~unstable\":()=>SZ});je({name:\"workgroupBarrier\",normalImpl:\"workgroupBarrier is a no-op outside of CODEGEN mode.\",signature:{argTypes:[],returnType:zr},codegenImpl:()=>\"workgroupBarrier()\",sideEffects:!0});je({name:\"storageBarrier\",normalImpl:\"storageBarrier is a no-op outside of CODEGEN mode.\",signature:{argTypes:[],returnType:zr},codegenImpl:()=>\"storageBarrier()\",sideEffects:!0});je({name:\"textureBarrier\",normalImpl:\"textureBarrier is a no-op outside of CODEGEN mode.\",signature:{argTypes:[],returnType:zr},codegenImpl:()=>\"textureBarrier()\",sideEffects:!0});var wl=\"Atomic operations are not supported outside of CODEGEN mode.\",ba=je({name:\"atomicLoad\",normalImpl:wl,signature:e=>{if(!vh(e))throw new Error(`Invalid atomic type: ${pn(e)}`);return{argTypes:[e],returnType:e.inner}},codegenImpl:(e,[t])=>Ne`atomicLoad(&${t})`,sideEffects:!0}),IZ=e=>{if(!vh(e))throw new Error(`Invalid atomic type: ${pn(e)}`);return{argTypes:[e,e.inner.type===\"u32\"?M:j],returnType:zr}},kd=e=>{if(!vh(e))throw new Error(`Invalid atomic type: ${pn(e)}`);let t=e.inner.type===\"u32\"?M:j;return{argTypes:[e,t],returnType:t}},xa=je({name:\"atomicStore\",normalImpl:wl,signature:IZ,codegenImpl:(e,[t,s])=>Ne`atomicStore(&${t}, ${s})`,sideEffects:!0}),wa=je({name:\"atomicAdd\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicAdd(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicSub\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicSub(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicMax\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicMax(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicMin\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicMin(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicAnd\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicAnd(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicOr\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicOr(&${t}, ${s})`,sideEffects:!0});je({name:\"atomicXor\",normalImpl:wl,signature:kd,codegenImpl:(e,[t,s])=>Ne`atomicXor(&${t}, ${s})`,sideEffects:!0});var Tl=\"Derivative builtins are not allowed on the CPU\",Bu=je({name:\"dpdx\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdx(${t})`,sideEffects:!1});je({name:\"dpdxCoarse\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdxCoarse(${t})`,sideEffects:!1});je({name:\"dpdxFine\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdxFine(${t})`,sideEffects:!1});var zu=je({name:\"dpdy\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdy(${t})`,sideEffects:!1});je({name:\"dpdyCoarse\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdyCoarse(${t})`,sideEffects:!1});je({name:\"dpdyFine\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`dpdyFine(${t})`,sideEffects:!1});var mr=je({name:\"fwidth\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`fwidth(${t})`,sideEffects:!1});je({name:\"fwidthCoarse\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`fwidthCoarse(${t})`,sideEffects:!1});je({name:\"fwidthFine\",normalImpl:Tl,signature:e=>({argTypes:[e],returnType:e}),codegenImpl:(e,[t])=>Ne`fwidthFine(${t})`,sideEffects:!1});function h_(e){return e.filter(t=>t!==void 0)}function YZ(e,t,s,r,o){throw new vo(\"Texture sampling relies on GPU resources and cannot be executed outside of a draw call\")}je({name:\"textureSample\",normalImpl:YZ,codegenImpl:(e,t)=>e.gen.emitCall(\"textureSample\",[],h_(t)),signature:(...e)=>({argTypes:e,returnType:e[0].type.startsWith(\"texture_depth\")?n:qt}),sideEffects:!1});function WZ(e,t,s,r,o,a){throw new vo(\"Texture sampling with bias relies on GPU resources and cannot be executed outside of a draw call\")}je({name:\"textureSampleBias\",normalImpl:WZ,codegenImpl:(e,t)=>e.gen.emitCall(\"textureSampleBias\",[],h_(t)),signature:(...e)=>({argTypes:e,returnType:qt}),sideEffects:!1});function qZ(e,t,s,r,o,a){throw new vo(\"Texture sampling relies on GPU resources and cannot be executed outside of a draw call\")}var Xl=je({name:\"textureSampleLevel\",normalImpl:qZ,codegenImpl:(e,t)=>e.gen.emitCall(\"textureSampleLevel\",[],h_(t)),signature:(...e)=>({argTypes:e,returnType:e[0].type.startsWith(\"texture_depth\")?n:qt}),sideEffects:!1});function VZ(e,t,s){throw new vo(\"`textureLoad` relies on GPU resources and cannot be executed outside of a draw call\")}var Ye=je({name:\"textureLoad\",normalImpl:VZ,codegenImpl:(e,t)=>e.gen.emitCall(\"textureLoad\",[],h_(t)),signature:(...e)=>{let t=e[0];if(DM(t)){let r=t.type.startsWith(\"texture_depth\"),o=t.sampleType;return{argTypes:e,returnType:r?n:o.type===\"f32\"?qt:o.type===\"u32\"?Is:sr}}if(G$(t))return{argTypes:e,returnType:qt};let s=t.format;return{argTypes:e,returnType:f_(s).vectorType}},sideEffects:!1});function NZ(e,t,s,r){throw new vo(\"`textureStore` relies on GPU resources and cannot be executed outside of a draw call\")}var lt=je({name:\"textureStore\",normalImpl:NZ,codegenImpl:(e,t)=>Ne`textureStore(${t})`,signature:(...e)=>({argTypes:e,returnType:zr}),sideEffects:!0});function $Z(e,t){throw new vo(\"`textureDimensions` relies on GPU resources and cannot be executed outside of a draw call\")}var Ti=je({name:\"textureDimensions\",normalImpl:$Z,codegenImpl:(e,t)=>e.gen.emitCall(\"textureDimensions\",[],h_(t)),signature:(...e)=>{let t=e[0].dimension;return t===\"1d\"?{argTypes:e,returnType:M}:t===\"3d\"?{argTypes:e,returnType:Ps}:{argTypes:e,returnType:$s}},sideEffects:!1}),XZ=(...e)=>{throw new Error(\"Texture gather relies on GPU resources and cannot be executed outside of a draw call\")},HZ={f32:qt,i32:sr,u32:Is};je({name:\"textureGather\",normalImpl:XZ,codegenImpl:(e,t)=>Ne`textureGather(${t})`,signature:(...e)=>{if(e[0].type.startsWith(\"texture\")){let[i,l,c,d,...u]=e;return{argTypes:i.type===\"texture_depth_2d_array\"||i.type===\"texture_depth_cube_array\"?[i,l,c,[M,j],...u]:e,returnType:qt}}let[t,s,r,o,...a]=e;return{argTypes:s.type===\"texture_2d_array\"||s.type===\"texture_cube_array\"?[[M,j],s,r,o,[M,j],...a]:[[M,j],s,r,o,...a],returnType:HZ[s.sampleType.type]}},sideEffects:!1});function jZ(e,t,s,r,o,a){throw new vo(\"Texture comparison sampling relies on GPU resources and cannot be executed outside of a draw call\")}je({name:\"textureSampleCompare\",normalImpl:jZ,codegenImpl:(e,t)=>Ne`textureSampleCompare(${t})`,signature:(...e)=>({argTypes:e,returnType:n}),sideEffects:!1});function KZ(e,t,s,r,o,a){throw new vo(\"Texture comparison sampling with level relies on GPU resources and cannot be executed outside of a draw call\")}je({name:\"textureSampleCompareLevel\",normalImpl:KZ,codegenImpl:(e,t)=>Ne`textureSampleCompareLevel(${t})`,signature:(...e)=>({argTypes:e,returnType:n}),sideEffects:!1});function QZ(e,t,s){throw new vo(\"Texture sampling with base clamp to edge is not supported outside of GPU mode.\")}function ZZ(e,t,s,r,o,a,i){throw new vo(\"Texture sampling with gradients relies on GPU resources and cannot be executed outside of a draw call\")}je({name:\"textureSampleGrad\",normalImpl:ZZ,codegenImpl:(e,t)=>Ne`textureSampleGrad(${t})`,signature:(...e)=>({argTypes:e,returnType:qt}),sideEffects:!1});je({name:\"textureSampleBaseClampToEdge\",normalImpl:QZ,codegenImpl:(e,t)=>Ne`textureSampleBaseClampToEdge(${t})`,signature:(...e)=>({argTypes:e,returnType:qt}),sideEffects:!1});var w=FZ,e1=\"uniforms\";function Pk(e){return e.replace(/[^a-zA-Z0-9_]/g,\"_\")}var JZ=new Set([\"alias\",\"break\",\"case\",\"const\",\"const_assert\",\"continue\",\"continuing\",\"default\",\"diagnostic\",\"discard\",\"else\",\"enable\",\"false\",\"fn\",\"for\",\"if\",\"let\",\"loop\",\"override\",\"requires\",\"return\",\"struct\",\"switch\",\"true\",\"var\",\"while\",\"NULL\",\"Self\",\"abstract\",\"active\",\"alignas\",\"alignof\",\"as\",\"asm\",\"asm_fragment\",\"async\",\"attribute\",\"auto\",\"await\",\"become\",\"cast\",\"catch\",\"class\",\"co_await\",\"co_return\",\"co_yield\",\"coherent\",\"column_major\",\"common\",\"compile\",\"compile_fragment\",\"concept\",\"const_cast\",\"consteval\",\"constexpr\",\"constinit\",\"crate\",\"debugger\",\"decltype\",\"delete\",\"demote\",\"demote_to_helper\",\"do\",\"dynamic_cast\",\"enum\",\"explicit\",\"export\",\"extends\",\"extern\",\"external\",\"fallthrough\",\"filter\",\"final\",\"finally\",\"friend\",\"from\",\"fxgroup\",\"get\",\"goto\",\"groupshared\",\"highp\",\"impl\",\"implements\",\"import\",\"inline\",\"instanceof\",\"interface\",\"layout\",\"lowp\",\"macro\",\"macro_rules\",\"match\",\"mediump\",\"meta\",\"mod\",\"module\",\"move\",\"mut\",\"mutable\",\"namespace\",\"new\",\"nil\",\"noexcept\",\"noinline\",\"nointerpolation\",\"non_coherent\",\"noncoherent\",\"noperspective\",\"null\",\"nullptr\",\"of\",\"operator\",\"package\",\"packoffset\",\"partition\",\"pass\",\"patch\",\"pixelfragment\",\"precise\",\"precision\",\"premerge\",\"priv\",\"protected\",\"pub\",\"public\",\"readonly\",\"ref\",\"regardless\",\"register\",\"reinterpret_cast\",\"require\",\"resource\",\"restrict\",\"self\",\"set\",\"shared\",\"sizeof\",\"smooth\",\"snorm\",\"static\",\"static_assert\",\"static_cast\",\"std\",\"subroutine\",\"super\",\"target\",\"template\",\"this\",\"thread_local\",\"throw\",\"trait\",\"try\",\"type\",\"typedef\",\"typeid\",\"typename\",\"typeof\",\"union\",\"unless\",\"unorm\",\"unsafe\",\"unsized\",\"use\",\"using\",\"varying\",\"virtual\",\"volatile\",\"wgsl\",\"where\",\"with\",\"writeonly\",\"yield\",\"sampler\",\"uniform\",\"storage\"]);function eJ(e){let t=Pk(e);return JZ.has(t)?`${t}_`:t}function Hn(e){return`n_${Pk(e)}`}function Ib(e){return e===\"vec2\"?2:e===\"vec3\"?3:4}function Ck(e){let t=e.type;return t===\"vec2f\"||t===\"vec2i\"||t===\"vec2u\"?\"vec2\":t===\"vec3f\"||t===\"vec3i\"||t===\"vec3u\"?\"vec3\":t===\"vec4f\"||t===\"vec4i\"||t===\"vec4u\"?\"vec4\":\"scalar\"}function tJ(e){return e.type===\"array\"}function Mk(e){if(typeof e==\"number\"||typeof e==\"boolean\")return n;if(e&&typeof e==\"object\"){let t=e.kind;if(typeof t==\"string\"){if(t.startsWith(\"vec2\"))return m;if(t.startsWith(\"vec3\"))return A;if(t.startsWith(\"vec4\"))return S}if(Array.isArray(e))return Fe(n,e.length||1);let s=e;if(\"w\"in s)return S;if(\"z\"in s)return A;if(\"x\"in s&&\"y\"in s)return m}return Array.isArray(e)?Fe(n,e.length||1):n}function Yb(e,t){let s=e;if(s&&typeof s==\"object\"&&\"data\"in s&&!(\"x\"in s)&&(s=s.data),typeof s==\"number\")return new Array(t).fill(s);if(s instanceof Ek)return s.toArray().slice(0,t);if(Array.isArray(s))return W7(s,t);if(s&&typeof s==\"object\"){let r=s;if(\"x\"in r){let o=[r.x,r.y??0];return t>=3&&o.push(r.z??0),t>=4&&o.push(r.w??0),o.slice(0,t)}if(typeof r.length==\"number\")return W7(Array.from(r),t)}throw new Error(`[uniformStore] cannot coerce value to a ${t}-component vector: ${String(e)}`)}function W7(e,t){let s=e.slice(0,t);for(;s.length<t;)s.push(0);return s}function q7(e){let t=e;if(t&&typeof t==\"object\"&&\"data\"in t&&(t=t.data),typeof t==\"number\")return t;if(typeof t==\"boolean\")return t?1:-1;let s=Number(t);return Number.isFinite(s)?s:0}var V7=new Set;function sJ(e){if(e==null)return!0;let t=typeof e;if(t===\"number\"||t===\"boolean\"||t===\"string\")return!0;if(t!==\"object\")return!1;if(Array.isArray(e))return!0;let s=e;return!!(typeof s.kind==\"string\"&&s.kind.startsWith(\"vec\")||\"x\"in s&&\"y\"in s||typeof s.length==\"number\"||\"data\"in s)}var Ek=class{constructor(e){this.handle=e}get n(){return this.handle._componentCount}component(e){return this.handle._mirror[e]??0}write(e,t){this.handle._mirror[e]=t,this.handle._enqueue()}get x(){return this.component(0)}set x(e){this.write(0,e)}get y(){return this.component(1)}set y(e){this.write(1,e)}get z(){return this.component(2)}set z(e){this.write(2,e)}get w(){return this.component(3)}set w(e){this.write(3,e)}get r(){return this.component(0)}set r(e){this.write(0,e)}get g(){return this.component(1)}set g(e){this.write(1,e)}get b(){return this.component(2)}set b(e){this.write(2,e)}get a(){return this.component(3)}set a(e){this.write(3,e)}copy(e){let t=Yb(e,this.n);for(let s=0;s<this.n;s++)this.handle._mirror[s]=t[s];return this.handle._enqueue(),this}set(...e){for(let t=0;t<this.n&&t<e.length;t++)this.handle._mirror[t]=e[t];return this.handle._enqueue(),this}toArray(){return this.handle._mirror.slice(0,this.n)}},si=class{gpuPath;accessorPath;transform;cpu;schema;_componentCount;_mirror;_raw;_kind;_view;_sink;constructor(e){this._sink=e.sink,this.gpuPath=e.gpuPath,this.accessorPath=`${e.gpuPath[0]}.${e.gpuPath[1]}`,this.transform=e.transform,this.cpu=e.cpu??!1,this.schema=e.schema,this._kind=this.cpu||!e.schema?\"scalar\":Ck(e.schema),this._componentCount=this._kind===\"scalar\"?1:Ib(this._kind),this.cpu?(this._mirror=[],this._raw=e.initial):this._kind===\"scalar\"?this._mirror=[q7(e.initial)]:(this._mirror=Yb(e.initial,this._componentCount),this._view=new Ek(this))}_enqueue(){this.cpu||this._sink.markScalar(this)}_patchValue(){return this._kind===\"scalar\"?this._mirror[0]:this._mirror.slice(0,this._componentCount)}get value(){return this.cpu?this._raw:this._kind===\"scalar\"?this._mirror[0]:this._view}set value(e){if(this.cpu){this._raw=e;return}if(this._kind===\"scalar\")this._mirror[0]=q7(e);else{let t=Yb(e,this._componentCount);for(let s=0;s<this._componentCount;s++)this._mirror[s]=t[s]}this._enqueue()}setFromRaw(e){if(this.cpu){this._raw=e;return}this.value=this.transform?this.transform(e):e}setComponents(...e){if(this.cpu||this._kind===\"scalar\"){e.length&&(this.value=e[0]);return}for(let t=0;t<this._componentCount&&t<e.length;t++)this._mirror[t]=e[t];this._enqueue()}},Nm=class{gpuPath;accessorPath;transform;schema;length;elementKind;_mirror;_sink;constructor(e){this._sink=e.sink,this.gpuPath=e.gpuPath,this.accessorPath=`${e.gpuPath[0]}.${e.gpuPath[1]}`,this.transform=e.transform,this.schema=e.schema,this.length=e.schema.elementCount,this.elementKind=Ck(e.schema.elementType),this._mirror=e.initial?e.initial.slice():new Array(this.flatLength).fill(0)}get flatLength(){return this.length*(this.elementKind===\"scalar\"?1:Ib(this.elementKind))}get array(){return this._mirror}set array(e){e!==this._mirror&&(this._mirror=e.slice()),this._sink.markArray(this,\"all\")}setElement(e,t){this._mirror[e]=t,this._sink.markArray(this,e)}get componentsPerElement(){return this.elementKind===\"scalar\"?1:Ib(this.elementKind)}_wholeArray(){let e=this._mirror.slice(0,this.flatLength),t=this.componentsPerElement;if(t===1)return e;let s=[];for(let r=0;r<this.length;r++)s.push(e.slice(r*t,(r+1)*t));return s}_elementAt(e){return this._mirror[e]}_elementTuple(e){let t=this.componentsPerElement;return this._mirror.slice(e*t,(e+1)*t)}},rJ=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({time:n,deltaTime:n,frame:n,viewportSize:m,logicalViewportSize:m,aspect:n,pointer:m,pointerActive:n}),\"SystemUniforms\"),oJ={time:0,deltaTime:0,frame:0,viewportSize:[1,1],logicalViewportSize:[1,1],aspect:1,pointer:[.5,.5],pointerActive:0};function kk(e,t={}){let s=t.systemSchema??rJ,r={...oJ,...t.systemInitial??{}},o=new Map,a=new Map,i={},l=s,c=!1,d=new Set,u=new Map,p=new Map,_={markScalar(x){d.add(x)},markArray(x,P){if(P===\"all\"){u.set(x,\"all\");return}let C=u.get(x);C!==\"all\"&&(C?C.add(P):u.set(x,new Set([P])))}},f;function h(x,P){let C={},E={};for(let R of P){let U=eJ(R.name),L=R.transform?R.transform(R.initial):R.initial,k=R.cpu??typeof L==\"string\";if(R.transform&&!k&&!sJ(L)&&!V7.has(R.transform)&&(V7.add(R.transform),console.warn(`[gpu] prop transform for \"${R.name}\" returned a non-packable value \\u2014 the uniform field will be NaN. A prop transform must return a number, boolean, vector, or array (or a string for cpu-only props).`,L)),k){C[R.name]=new si({sink:_,gpuPath:[x,U],initial:L,transform:R.transform,cpu:!0});continue}let D=L&&typeof L==\"object\"&&\"data\"in L?L.data:L,O=R.schema??Mk(D);if(E[U]=O,tJ(O)){let F=new Nm({sink:_,gpuPath:[x,U],schema:O,initial:R.initial,transform:R.transform});C[R.name]=F}else{let F=new si({sink:_,gpuPath:[x,U],schema:O,initial:L,transform:R.transform});C[R.name]=F}}return{handles:C,struct:(globalThis.__TYPEGPU_AUTONAME__??(R=>R))(gt(E),\"struct\")}}function g(){if(f)throw new Error(\"[uniformStore] cannot register fields after finalize()\")}function y(){let x={};for(let[P,C]of a)x[P]=b(C,P);return x._sys=b(i,\"_sys\"),x}function b(x,P){let C={};for(let E of Object.values(x))E instanceof Nm?C[E.gpuPath[1]]=E._wholeArray():E.cpu||(C[E.gpuPath[1]]=E._patchValue());if(P)for(let E of p.get(P)??[])C[E]=0;return C}return{get schema(){return f?.schema},get layout(){return f?.layout},get buffer(){return f?.buffer},get bindGroup(){return f?.bindGroup},get systemHandles(){return i},defineNode(x,P){g();let C=Hn(x),{handles:E,struct:R}=h(C,P);return o.set(C,R),a.set(C,E),E},defineSystem(x){if(g(),x&&x.length){let P=h(\"_sys\",x);i=P.handles,l=P.struct}else{let P=h(\"_sys\",Object.entries(s.propTypes).map(([C,E])=>({name:C,schema:E,initial:r[C]??0})));i=P.handles,l=P.struct}return c=!0,i},finalize(){if(f)return f;c||this.defineSystem();let x=(D,O)=>{if(Object.keys(O.propTypes).length===0){let Y=[\"_pad0\",\"_pad1\",\"_pad2\",\"_pad3\"];return p.set(D,Y),gt(Object.fromEntries(Y.map(V=>[V,n])))}let F=O,q=[];for(;Kw(F)%16!==0;){let Y=`_pad${q.length}`;for(;Y in O.propTypes;)Y=`_${Y}`;q.push(Y);let V={...O.propTypes};for(let X of q)V[X]=n;F=(globalThis.__TYPEGPU_AUTONAME__??(X=>X))(gt(V),\"padded\")}return q.length&&p.set(D,q),F},P={};for(let[D,O]of o)P[D]=x(D,O);P._sys=x(\"_sys\",l);let C=(globalThis.__TYPEGPU_AUTONAME__??(D=>D))(gt(P),\"combined\"),E=Kw(C),R=e.device?.limits?.maxUniformBufferBindingSize,U=typeof R==\"number\"&&R>0?R:65536;if(E>U)throw new Kd(\"limit-exceeded\",`[gpu] this composition needs ${E} bytes of uniform data across ${o.size} nodes, but the device caps a uniform binding at ${U} bytes. Reduce the number of layers.`);let L=(globalThis.__TYPEGPU_AUTONAME__??(D=>D))(e.createBuffer(C).$usage(\"uniform\"),\"buffer\"),k=(globalThis.__TYPEGPU_AUTONAME__??(D=>D))(w.bindGroupLayout({[e1]:{uniform:C}}),\"layout\");return f={schema:C,layout:k,buffer:L,bindGroup:e.createBindGroup(k,{[e1]:L}),uniformEntryKey:e1},this.writeAll(),f},flush(){if(!f||d.size===0&&u.size===0)return;let x={},P=C=>x[C]??={};for(let C of d)P(C.gpuPath[0])[C.gpuPath[1]]=C._patchValue();for(let[C,E]of u){let R=P(C.gpuPath[0]);if(E===\"all\")R[C.gpuPath[1]]=C._wholeArray();else if(C.componentsPerElement===1){let U={};for(let L of E)U[L]=C._elementAt(L);R[C.gpuPath[1]]=U}else{let U=C.componentsPerElement,L={};for(let k of E){let D=Math.floor(k/U);L[D]=C._elementTuple(D)}R[C.gpuPath[1]]=L}}f.buffer.patch(x),d.clear(),u.clear()},writeAll(){f&&(f.buffer.write(y()),d.clear(),u.clear())},gpuAccessor(x,P=\"layout\"){return`${P}.$.${e1}.${x.accessorPath}`},destroy(){f?.buffer.destroy?.(),f=void 0,o.clear(),a.clear(),d.clear(),u.clear(),p.clear()}}}function Bk(e,t){if(e instanceof Nm){Array.isArray(t)&&(e.array=t);return}e.setFromRaw(t)}var bt=Math.PI/180;180/Math.PI;var Wo=Math.PI,Jt=Math.PI*2,Po=Jt,ev=Math.PI/2,lc=Math.sqrt(3),aJ=2.3999632297286535,ha=w.fn([M,j],M)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t<<M(s)|t>>M(__tsover_sub(j(32),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"k\"}],body:[0,[[10,[1,[1,\"x\",\"<<\",[6,\"d.u32\",[\"k\"]]],\"|\",[1,\"x\",\">>\",[6,\"d.u32\",[[1,[6,\"d.i32\",[[5,\"32\"]]],\"-\",\"k\"]]]]]]]]},externals:{\"d.u32\":()=>M,\"d.i32\":()=>j}})&&e.f)({})),Ru=w.fn([M,M,M],M)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=t,a=s,i=r;return i=i^a,i=__tsover_sub(i,ha(a,j(14))),o=o^i,o=__tsover_sub(o,ha(i,j(11))),a=a^o,a=__tsover_sub(a,ha(o,j(25))),i=i^a,i=__tsover_sub(i,ha(a,j(16))),o=o^i,o=__tsover_sub(o,ha(i,j(4))),a=a^o,a=__tsover_sub(a,ha(o,j(14))),i=i^a,i=__tsover_sub(i,ha(a,j(24))),i}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"a0\"},{type:\"i\",name:\"b0\"},{type:\"i\",name:\"c0\"}],body:[0,[[12,\"a\",\"a0\"],[12,\"b\",\"b0\"],[12,\"c\",\"c0\"],[2,\"c\",\"=\",[1,\"c\",\"^\",\"b\"]],[2,\"c\",\"=\",[1,\"c\",\"-\",[6,\"mxRotl32\",[\"b\",[6,\"d.i32\",[[5,\"14\"]]]]]]],[2,\"a\",\"=\",[1,\"a\",\"^\",\"c\"]],[2,\"a\",\"=\",[1,\"a\",\"-\",[6,\"mxRotl32\",[\"c\",[6,\"d.i32\",[[5,\"11\"]]]]]]],[2,\"b\",\"=\",[1,\"b\",\"^\",\"a\"]],[2,\"b\",\"=\",[1,\"b\",\"-\",[6,\"mxRotl32\",[\"a\",[6,\"d.i32\",[[5,\"25\"]]]]]]],[2,\"c\",\"=\",[1,\"c\",\"^\",\"b\"]],[2,\"c\",\"=\",[1,\"c\",\"-\",[6,\"mxRotl32\",[\"b\",[6,\"d.i32\",[[5,\"16\"]]]]]]],[2,\"a\",\"=\",[1,\"a\",\"^\",\"c\"]],[2,\"a\",\"=\",[1,\"a\",\"-\",[6,\"mxRotl32\",[\"c\",[6,\"d.i32\",[[5,\"4\"]]]]]]],[2,\"b\",\"=\",[1,\"b\",\"^\",\"a\"]],[2,\"b\",\"=\",[1,\"b\",\"-\",[6,\"mxRotl32\",[\"a\",[6,\"d.i32\",[[5,\"14\"]]]]]]],[2,\"c\",\"=\",[1,\"c\",\"^\",\"b\"]],[2,\"c\",\"=\",[1,\"c\",\"-\",[6,\"mxRotl32\",[\"b\",[6,\"d.i32\",[[5,\"24\"]]]]]]],[10,\"c\"]]]},externals:{mxRotl32:()=>ha,\"d.i32\":()=>j}})&&e.f)({})),Du=3735928559,sm=w.fn([j,j],M)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=M(2),o=__tsover_add(__tsover_add(M(Du),r<<M(2)),M(13));return Ru(__tsover_add(o,M(t)),__tsover_add(o,M(s)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"len\",[6,\"d.u32\",[[5,\"2\"]]]],[13,\"seed\",[1,[1,[6,\"d.u32\",[\"DEADBEEF\"]],\"+\",[1,\"len\",\"<<\",[6,\"d.u32\",[[5,\"2\"]]]]],\"+\",[6,\"d.u32\",[[5,\"13\"]]]]],[13,\"a\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"x\"]]]],[13,\"b\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"y\"]]]],[10,[6,\"mxBjfinal\",[\"a\",\"b\",\"seed\"]]]]]},externals:{\"d.u32\":()=>M,DEADBEEF:()=>Du,mxBjfinal:()=>Ru}})&&e.f)({})),cn=w.fn([j,j,j],M)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=M(3),a=__tsover_add(__tsover_add(M(Du),o<<M(2)),M(13));return Ru(__tsover_add(a,M(t)),__tsover_add(a,M(s)),__tsover_add(a,M(r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"z\"}],body:[0,[[13,\"len\",[6,\"d.u32\",[[5,\"3\"]]]],[13,\"seed\",[1,[1,[6,\"d.u32\",[\"DEADBEEF\"]],\"+\",[1,\"len\",\"<<\",[6,\"d.u32\",[[5,\"2\"]]]]],\"+\",[6,\"d.u32\",[[5,\"13\"]]]]],[13,\"a\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"x\"]]]],[13,\"b\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"y\"]]]],[13,\"c\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"z\"]]]],[10,[6,\"mxBjfinal\",[\"a\",\"b\",\"c\"]]]]]},externals:{\"d.u32\":()=>M,DEADBEEF:()=>Du,mxBjfinal:()=>Ru}})&&e.f)({})),t1=w.fn([j,j,j,j],M)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=M(4),i=__tsover_add(__tsover_add(M(Du),a<<M(2)),M(13)),l=__tsover_add(i,M(t)),c=__tsover_add(i,M(s)),d=__tsover_add(i,M(r));return l=__tsover_sub(l,d),l=l^ha(d,j(4)),d=__tsover_add(d,c),c=__tsover_sub(c,l),c=c^ha(l,j(6)),l=__tsover_add(l,d),d=__tsover_sub(d,c),d=d^ha(c,j(8)),c=__tsover_add(c,l),l=__tsover_sub(l,d),l=l^ha(d,j(16)),d=__tsover_add(d,c),c=__tsover_sub(c,l),c=c^ha(l,j(19)),l=__tsover_add(l,d),d=__tsover_sub(d,c),d=d^ha(c,j(4)),c=__tsover_add(c,l),l=__tsover_add(l,M(o)),Ru(l,c,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"z\"},{type:\"i\",name:\"xx\"}],body:[0,[[13,\"len\",[6,\"d.u32\",[[5,\"4\"]]]],[13,\"seed\",[1,[1,[6,\"d.u32\",[\"DEADBEEF\"]],\"+\",[1,\"len\",\"<<\",[6,\"d.u32\",[[5,\"2\"]]]]],\"+\",[6,\"d.u32\",[[5,\"13\"]]]]],[12,\"a\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"x\"]]]],[12,\"b\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"y\"]]]],[12,\"c\",[1,\"seed\",\"+\",[6,\"d.u32\",[\"z\"]]]],[2,\"a\",\"=\",[1,\"a\",\"-\",\"c\"]],[2,\"a\",\"=\",[1,\"a\",\"^\",[6,\"mxRotl32\",[\"c\",[6,\"d.i32\",[[5,\"4\"]]]]]]],[2,\"c\",\"=\",[1,\"c\",\"+\",\"b\"]],[2,\"b\",\"=\",[1,\"b\",\"-\",\"a\"]],[2,\"b\",\"=\",[1,\"b\",\"^\",[6,\"mxRotl32\",[\"a\",[6,\"d.i32\",[[5,\"6\"]]]]]]],[2,\"a\",\"=\",[1,\"a\",\"+\",\"c\"]],[2,\"c\",\"=\",[1,\"c\",\"-\",\"b\"]],[2,\"c\",\"=\",[1,\"c\",\"^\",[6,\"mxRotl32\",[\"b\",[6,\"d.i32\",[[5,\"8\"]]]]]]],[2,\"b\",\"=\",[1,\"b\",\"+\",\"a\"]],[2,\"a\",\"=\",[1,\"a\",\"-\",\"c\"]],[2,\"a\",\"=\",[1,\"a\",\"^\",[6,\"mxRotl32\",[\"c\",[6,\"d.i32\",[[5,\"16\"]]]]]]],[2,\"c\",\"=\",[1,\"c\",\"+\",\"b\"]],[2,\"b\",\"=\",[1,\"b\",\"-\",\"a\"]],[2,\"b\",\"=\",[1,\"b\",\"^\",[6,\"mxRotl32\",[\"a\",[6,\"d.i32\",[[5,\"19\"]]]]]]],[2,\"a\",\"=\",[1,\"a\",\"+\",\"c\"]],[2,\"c\",\"=\",[1,\"c\",\"-\",\"b\"]],[2,\"c\",\"=\",[1,\"c\",\"^\",[6,\"mxRotl32\",[\"b\",[6,\"d.i32\",[[5,\"4\"]]]]]]],[2,\"b\",\"=\",[1,\"b\",\"+\",\"a\"]],[2,\"a\",\"=\",[1,\"a\",\"+\",[6,\"d.u32\",[\"xx\"]]]],[10,[6,\"mxBjfinal\",[\"a\",\"b\",\"c\"]]]]]},externals:{\"d.u32\":()=>M,DEADBEEF:()=>Du,mxRotl32:()=>ha,\"d.i32\":()=>j,mxBjfinal:()=>Ru}})&&e.f)({})),Hl=w.fn([M],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_div(n(t),n(4294967295))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"bits\"}],body:[0,[[10,[1,[6,\"d.f32\",[\"bits\"]],\"/\",[6,\"d.f32\",[[5,\"4294967295\"]]]]]]]},externals:{\"d.f32\":()=>n}})&&e.f)({})),Jc=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(__tsover_mul(__tsover_mul(t,t),t),__tsover_add(__tsover_mul(t,__tsover_sub(__tsover_mul(t,6),15)),10))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[1,[1,[1,\"t\",\"*\",\"t\"],\"*\",\"t\"],\"*\",[1,[1,\"t\",\"*\",[1,[1,\"t\",\"*\",[5,\"6\"]],\"-\",[5,\"15\"]]],\"+\",[5,\"10\"]]]]]]},externals:{}})&&e.f)({})),rm=w.fn([M,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=t&M(7),a=N(r,s,o<M(4)),i=__tsover_mul(2,N(s,r,o<M(4))),l=N(a,-a,(o&M(1))!==M(0)),c=N(i,-i,(o&M(2))!==M(0));return __tsover_add(l,c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"hash\"},{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"h\",[1,\"hash\",\"&\",[6,\"d.u32\",[[5,\"7\"]]]]],[13,\"u\",[6,\"std.select\",[\"y\",\"x\",[1,\"h\",\"<\",[6,\"d.u32\",[[5,\"4\"]]]]]]],[13,\"v\",[1,[5,\"2\"],\"*\",[6,\"std.select\",[\"x\",\"y\",[1,\"h\",\"<\",[6,\"d.u32\",[[5,\"4\"]]]]]]]],[13,\"uu\",[6,\"std.select\",[\"u\",[4,\"-\",\"u\"],[1,[1,\"h\",\"&\",[6,\"d.u32\",[[5,\"1\"]]]],\"!==\",[6,\"d.u32\",[[5,\"0\"]]]]]]],[13,\"vv\",[6,\"std.select\",[\"v\",[4,\"-\",\"v\"],[1,[1,\"h\",\"&\",[6,\"d.u32\",[[5,\"2\"]]]],\"!==\",[6,\"d.u32\",[[5,\"0\"]]]]]]],[10,[1,\"uu\",\"+\",\"vv\"]]]]},externals:{\"d.u32\":()=>M,\"std.select\":()=>N}})&&e.f)({})),Hi=w.fn([M,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=t&M(15),i=N(r,s,a<M(8)),l=N(N(o,s,a===M(12)||a===M(14)),r,a<M(4)),c=N(i,-i,(a&M(1))!==M(0)),d=N(l,-l,(a&M(2))!==M(0));return __tsover_add(c,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"hash\"},{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"z\"}],body:[0,[[13,\"h\",[1,\"hash\",\"&\",[6,\"d.u32\",[[5,\"15\"]]]]],[13,\"u\",[6,\"std.select\",[\"y\",\"x\",[1,\"h\",\"<\",[6,\"d.u32\",[[5,\"8\"]]]]]]],[13,\"inner\",[6,\"std.select\",[\"z\",\"x\",[3,[1,\"h\",\"===\",[6,\"d.u32\",[[5,\"12\"]]]],\"||\",[1,\"h\",\"===\",[6,\"d.u32\",[[5,\"14\"]]]]]]]],[13,\"v\",[6,\"std.select\",[\"inner\",\"y\",[1,\"h\",\"<\",[6,\"d.u32\",[[5,\"4\"]]]]]]],[13,\"uu\",[6,\"std.select\",[\"u\",[4,\"-\",\"u\"],[1,[1,\"h\",\"&\",[6,\"d.u32\",[[5,\"1\"]]]],\"!==\",[6,\"d.u32\",[[5,\"0\"]]]]]]],[13,\"vv\",[6,\"std.select\",[\"v\",[4,\"-\",\"v\"],[1,[1,\"h\",\"&\",[6,\"d.u32\",[[5,\"2\"]]]],\"!==\",[6,\"d.u32\",[[5,\"0\"]]]]]]],[10,[1,\"uu\",\"+\",\"vv\"]]]]},externals:{\"d.u32\":()=>M,\"std.select\":()=>N}})&&e.f)({})),N7=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(.6616,t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[10,[1,[5,\"0.6616\"],\"*\",\"v\"]]]]},externals:{}})&&e.f)({})),$7=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(.982,t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[10,[1,[5,\"0.982\"],\"*\",\"v\"]]]]},externals:{}})&&e.f)({})),X7=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_sub(1,a);return __tsover_add(__tsover_mul(__tsover_sub(1,i),__tsover_add(__tsover_mul(t,l),__tsover_mul(s,a))),__tsover_mul(i,__tsover_add(__tsover_mul(r,l),__tsover_mul(o,a))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v0\"},{type:\"i\",name:\"v1\"},{type:\"i\",name:\"v2\"},{type:\"i\",name:\"v3\"},{type:\"i\",name:\"s\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"s1\",[1,[5,\"1\"],\"-\",\"s\"]],[10,[1,[1,[1,[5,\"1\"],\"-\",\"t\"],\"*\",[1,[1,\"v0\",\"*\",\"s1\"],\"+\",[1,\"v1\",\"*\",\"s\"]]],\"+\",[1,\"t\",\"*\",[1,[1,\"v2\",\"*\",\"s1\"],\"+\",[1,\"v3\",\"*\",\"s\"]]]]]]]},externals:{}})&&e.f)({})),H7=w.fn([n,n,n,n,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=__tsover_sub(1,d),f=__tsover_sub(1,u),h=__tsover_sub(1,p);return __tsover_add(__tsover_mul(h,__tsover_add(__tsover_mul(f,__tsover_add(__tsover_mul(t,_),__tsover_mul(s,d))),__tsover_mul(u,__tsover_add(__tsover_mul(r,_),__tsover_mul(o,d))))),__tsover_mul(p,__tsover_add(__tsover_mul(f,__tsover_add(__tsover_mul(a,_),__tsover_mul(i,d))),__tsover_mul(u,__tsover_add(__tsover_mul(l,_),__tsover_mul(c,d))))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v0\"},{type:\"i\",name:\"v1\"},{type:\"i\",name:\"v2\"},{type:\"i\",name:\"v3\"},{type:\"i\",name:\"v4\"},{type:\"i\",name:\"v5\"},{type:\"i\",name:\"v6\"},{type:\"i\",name:\"v7\"},{type:\"i\",name:\"s\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"r\"}],body:[0,[[13,\"s1\",[1,[5,\"1\"],\"-\",\"s\"]],[13,\"t1\",[1,[5,\"1\"],\"-\",\"t\"]],[13,\"r1\",[1,[5,\"1\"],\"-\",\"r\"]],[10,[1,[1,\"r1\",\"*\",[1,[1,\"t1\",\"*\",[1,[1,\"v0\",\"*\",\"s1\"],\"+\",[1,\"v1\",\"*\",\"s\"]]],\"+\",[1,\"t\",\"*\",[1,[1,\"v2\",\"*\",\"s1\"],\"+\",[1,\"v3\",\"*\",\"s\"]]]]],\"+\",[1,\"r\",\"*\",[1,[1,\"t1\",\"*\",[1,[1,\"v4\",\"*\",\"s1\"],\"+\",[1,\"v5\",\"*\",\"s\"]]],\"+\",[1,\"t\",\"*\",[1,[1,\"v6\",\"*\",\"s1\"],\"+\",[1,\"v7\",\"*\",\"s\"]]]]]]]]]},externals:{}})&&e.f)({})),j7=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=j(ie(t.x)),r=j(ie(t.y)),o=__tsover_sub(t.x,n(s)),a=__tsover_sub(t.y,n(r)),i=Jc(o),l=Jc(a);return N7(X7(rm(sm(s,r),o,a),rm(sm(__tsover_add(s,j(1)),r),__tsover_sub(o,1),a),rm(sm(s,__tsover_add(r,j(1))),o,__tsover_sub(a,1)),rm(sm(__tsover_add(s,j(1)),__tsover_add(r,j(1))),__tsover_sub(o,1),__tsover_sub(a,1)),i,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"X\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"Y\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[13,\"fx\",[1,[7,\"p\",\"x\"],\"-\",[6,\"d.f32\",[\"X\"]]]],[13,\"fy\",[1,[7,\"p\",\"y\"],\"-\",[6,\"d.f32\",[\"Y\"]]]],[13,\"u\",[6,\"mxFade\",[\"fx\"]]],[13,\"v\",[6,\"mxFade\",[\"fy\"]]],[13,\"result\",[6,\"mxBilerp\",[[6,\"mxGradientFloat2\",[[6,\"mxHashInt1\",[\"X\",\"Y\"]],\"fx\",\"fy\"]],[6,\"mxGradientFloat2\",[[6,\"mxHashInt1\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],\"Y\"]],[1,\"fx\",\"-\",[5,\"1\"]],\"fy\"]],[6,\"mxGradientFloat2\",[[6,\"mxHashInt1\",[\"X\",[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],\"fx\",[1,\"fy\",\"-\",[5,\"1\"]]]],[6,\"mxGradientFloat2\",[[6,\"mxHashInt1\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],[1,\"fx\",\"-\",[5,\"1\"]],[1,\"fy\",\"-\",[5,\"1\"]]]],\"u\",\"v\"]]],[10,[6,\"mxGradientScale2d\",[\"result\"]]]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.f32\":()=>n,mxFade:()=>Jc,mxBilerp:()=>X7,mxGradientFloat2:()=>rm,mxHashInt1:()=>sm,mxGradientScale2d:()=>N7}})&&e.f)({})),K7=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=j(ie(t.x)),r=j(ie(t.y)),o=j(ie(t.z)),a=__tsover_sub(t.x,n(s)),i=__tsover_sub(t.y,n(r)),l=__tsover_sub(t.z,n(o)),c=Jc(a),d=Jc(i),u=Jc(l);return $7(H7(Hi(cn(s,r,o),a,i,l),Hi(cn(__tsover_add(s,j(1)),r,o),__tsover_sub(a,1),i,l),Hi(cn(s,__tsover_add(r,j(1)),o),a,__tsover_sub(i,1),l),Hi(cn(__tsover_add(s,j(1)),__tsover_add(r,j(1)),o),__tsover_sub(a,1),__tsover_sub(i,1),l),Hi(cn(s,r,__tsover_add(o,j(1))),a,i,__tsover_sub(l,1)),Hi(cn(__tsover_add(s,j(1)),r,__tsover_add(o,j(1))),__tsover_sub(a,1),i,__tsover_sub(l,1)),Hi(cn(s,__tsover_add(r,j(1)),__tsover_add(o,j(1))),a,__tsover_sub(i,1),__tsover_sub(l,1)),Hi(cn(__tsover_add(s,j(1)),__tsover_add(r,j(1)),__tsover_add(o,j(1))),__tsover_sub(a,1),__tsover_sub(i,1),__tsover_sub(l,1)),c,d,u))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"X\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"Y\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[13,\"Z\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"z\"]]]]]],[13,\"fx\",[1,[7,\"p\",\"x\"],\"-\",[6,\"d.f32\",[\"X\"]]]],[13,\"fy\",[1,[7,\"p\",\"y\"],\"-\",[6,\"d.f32\",[\"Y\"]]]],[13,\"fz\",[1,[7,\"p\",\"z\"],\"-\",[6,\"d.f32\",[\"Z\"]]]],[13,\"u\",[6,\"mxFade\",[\"fx\"]]],[13,\"v\",[6,\"mxFade\",[\"fy\"]]],[13,\"w\",[6,\"mxFade\",[\"fz\"]]],[13,\"result\",[6,\"mxTrilerp\",[[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[\"X\",\"Y\",\"Z\"]],\"fx\",\"fy\",\"fz\"]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],\"Y\",\"Z\"]],[1,\"fx\",\"-\",[5,\"1\"]],\"fy\",\"fz\"]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[\"X\",[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],\"Z\"]],\"fx\",[1,\"fy\",\"-\",[5,\"1\"]],\"fz\"]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],\"Z\"]],[1,\"fx\",\"-\",[5,\"1\"]],[1,\"fy\",\"-\",[5,\"1\"]],\"fz\"]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[\"X\",\"Y\",[1,\"Z\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],\"fx\",\"fy\",[1,\"fz\",\"-\",[5,\"1\"]]]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],\"Y\",[1,\"Z\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],[1,\"fx\",\"-\",[5,\"1\"]],\"fy\",[1,\"fz\",\"-\",[5,\"1\"]]]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[\"X\",[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],[1,\"Z\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],\"fx\",[1,\"fy\",\"-\",[5,\"1\"]],[1,\"fz\",\"-\",[5,\"1\"]]]],[6,\"mxGradientFloat3\",[[6,\"mxHashInt2\",[[1,\"X\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],[1,\"Y\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]],[1,\"Z\",\"+\",[6,\"d.i32\",[[5,\"1\"]]]]]],[1,\"fx\",\"-\",[5,\"1\"]],[1,\"fy\",\"-\",[5,\"1\"]],[1,\"fz\",\"-\",[5,\"1\"]]]],\"u\",\"v\",\"w\"]]],[10,[6,\"mxGradientScale3d\",[\"result\"]]]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.f32\":()=>n,mxFade:()=>Jc,mxTrilerp:()=>H7,mxGradientFloat3:()=>Hi,mxHashInt2:()=>cn,mxGradientScale3d:()=>$7}})&&e.f)({})),Q7=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=j(ie(t.x)),r=j(ie(t.y));return A(Hl(cn(s,r,j(0))),Hl(cn(s,r,j(1))),Hl(cn(s,r,j(2))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"ix\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"iy\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[10,[6,\"d.vec3f\",[[6,\"mxBitsTo01\",[[6,\"mxHashInt2\",[\"ix\",\"iy\",[6,\"d.i32\",[[5,\"0\"]]]]]]],[6,\"mxBitsTo01\",[[6,\"mxHashInt2\",[\"ix\",\"iy\",[6,\"d.i32\",[[5,\"1\"]]]]]]],[6,\"mxBitsTo01\",[[6,\"mxHashInt2\",[\"ix\",\"iy\",[6,\"d.i32\",[[5,\"2\"]]]]]]]]]]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.vec3f\":()=>A,mxBitsTo01:()=>Hl,mxHashInt2:()=>cn}})&&e.f)({})),Z7=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=j(ie(t.x)),r=j(ie(t.y)),o=j(ie(t.z));return A(Hl(t1(s,r,o,j(0))),Hl(t1(s,r,o,j(1))),Hl(t1(s,r,o,j(2))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"ix\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"iy\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[13,\"iz\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"z\"]]]]]],[10,[6,\"d.vec3f\",[[6,\"mxBitsTo01\",[[6,\"mxHashInt3\",[\"ix\",\"iy\",\"iz\",[6,\"d.i32\",[[5,\"0\"]]]]]]],[6,\"mxBitsTo01\",[[6,\"mxHashInt3\",[\"ix\",\"iy\",\"iz\",[6,\"d.i32\",[[5,\"1\"]]]]]]],[6,\"mxBitsTo01\",[[6,\"mxHashInt3\",[\"ix\",\"iy\",\"iz\",[6,\"d.i32\",[[5,\"2\"]]]]]]]]]]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.vec3f\":()=>A,mxBitsTo01:()=>Hl,mxHashInt3:()=>t1}})&&e.f)({})),J7=w.fn([m,j,j,j,j,n,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=Q7(m(n(__tsover_add(s,o)),n(__tsover_add(r,a)))),d=m(c.x,c.y);d=al(ra(As(d,.5),i),.5);let u=As(al(m(n(s),n(r)),d),t);return l===j(2)?__tsover_add(re(u.x),re(u.y)):l===j(3)?B(re(u.x),re(u.y)):Se(u,u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"xoff\"},{type:\"i\",name:\"yoff\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"metric\"}],body:[0,[[13,\"tmp\",[6,\"mxCellNoiseVec3_2d\",[[6,\"d.vec2f\",[[6,\"d.f32\",[[1,\"x\",\"+\",\"xoff\"]]],[6,\"d.f32\",[[1,\"y\",\"+\",\"yoff\"]]]]]]]],[12,\"off\",[6,\"d.vec2f\",[[7,\"tmp\",\"x\"],[7,\"tmp\",\"y\"]]]],[2,\"off\",\"=\",[6,\"std.add\",[[6,\"std.mul\",[[6,\"std.sub\",[\"off\",[5,\"0.5\"]]],\"jitter\"]],[5,\"0.5\"]]]],[13,\"cellpos\",[6,\"std.add\",[[6,\"d.vec2f\",[[6,\"d.f32\",[\"x\"]],[6,\"d.f32\",[\"y\"]]]],\"off\"]]],[13,\"diff\",[6,\"std.sub\",[\"cellpos\",\"p\"]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"2\"]]]],[0,[[10,[1,[6,\"std.abs\",[[7,\"diff\",\"x\"]]],\"+\",[6,\"std.abs\",[[7,\"diff\",\"y\"]]]]]]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"3\"]]]],[0,[[10,[6,\"std.max\",[[6,\"std.abs\",[[7,\"diff\",\"x\"]]],[6,\"std.abs\",[[7,\"diff\",\"y\"]]]]]]]]],[10,[6,\"std.dot\",[\"diff\",\"diff\"]]]]]},externals:{mxCellNoiseVec3_2d:()=>Q7,\"d.vec2f\":()=>m,\"d.f32\":()=>n,\"std.add\":()=>al,\"std.mul\":()=>ra,\"std.sub\":()=>As,\"d.i32\":()=>j,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.dot\":()=>Se}})&&e.f)({})),eT=w.fn([A,j,j,j,j,j,j,n,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=Z7(A(n(__tsover_add(s,a)),n(__tsover_add(r,i)),n(__tsover_add(o,l))));u=al(ra(As(u,.5),c),.5);let p=As(al(A(n(s),n(r),n(o)),u),t);return d===j(2)?__tsover_add(__tsover_add(re(p.x),re(p.y)),re(p.z)):d===j(3)?B(B(re(p.x),re(p.y)),re(p.z)):Se(p,p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"z\"},{type:\"i\",name:\"xoff\"},{type:\"i\",name:\"yoff\"},{type:\"i\",name:\"zoff\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"metric\"}],body:[0,[[12,\"off\",[6,\"mxCellNoiseVec3_3d\",[[6,\"d.vec3f\",[[6,\"d.f32\",[[1,\"x\",\"+\",\"xoff\"]]],[6,\"d.f32\",[[1,\"y\",\"+\",\"yoff\"]]],[6,\"d.f32\",[[1,\"z\",\"+\",\"zoff\"]]]]]]]],[2,\"off\",\"=\",[6,\"std.add\",[[6,\"std.mul\",[[6,\"std.sub\",[\"off\",[5,\"0.5\"]]],\"jitter\"]],[5,\"0.5\"]]]],[13,\"cellpos\",[6,\"std.add\",[[6,\"d.vec3f\",[[6,\"d.f32\",[\"x\"]],[6,\"d.f32\",[\"y\"]],[6,\"d.f32\",[\"z\"]]]],\"off\"]]],[13,\"diff\",[6,\"std.sub\",[\"cellpos\",\"p\"]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"2\"]]]],[0,[[10,[1,[1,[6,\"std.abs\",[[7,\"diff\",\"x\"]]],\"+\",[6,\"std.abs\",[[7,\"diff\",\"y\"]]]],\"+\",[6,\"std.abs\",[[7,\"diff\",\"z\"]]]]]]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"3\"]]]],[0,[[10,[6,\"std.max\",[[6,\"std.max\",[[6,\"std.abs\",[[7,\"diff\",\"x\"]]],[6,\"std.abs\",[[7,\"diff\",\"y\"]]]]],[6,\"std.abs\",[[7,\"diff\",\"z\"]]]]]]]]],[10,[6,\"std.dot\",[\"diff\",\"diff\"]]]]]},externals:{mxCellNoiseVec3_3d:()=>Z7,\"d.vec3f\":()=>A,\"d.f32\":()=>n,\"std.add\":()=>al,\"std.mul\":()=>ra,\"std.sub\":()=>As,\"d.i32\":()=>j,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.dot\":()=>Se}})&&e.f)({})),tT=w.fn([m,n,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=j(ie(t.x)),a=j(ie(t.y)),i=m(__tsover_sub(t.x,n(o)),__tsover_sub(t.y,n(a))),l=n(1e6);for(let c=-1;c<=1;c++)for(let d=-1;d<=1;d++){let u=J7(i,c,d,o,a,s,r);l=ye(l,u)}return r===j(0)&&(l=Ue(l)),l}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"metric\"}],body:[0,[[13,\"X\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"Y\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[13,\"localpos\",[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"-\",[6,\"d.f32\",[\"X\"]]],[1,[7,\"p\",\"y\"],\"-\",[6,\"d.f32\",[\"Y\"]]]]]],[12,\"sqdist\",[6,\"d.f32\",[[5,\"1000000\"]]]],[14,[12,\"x\",[4,\"-\",[5,\"1\"]]],[1,\"x\",\"<=\",[5,\"1\"]],[102,\"++\",\"x\"],[0,[[14,[12,\"y\",[4,\"-\",[5,\"1\"]]],[1,\"y\",\"<=\",[5,\"1\"]],[102,\"++\",\"y\"],[0,[[13,\"dist\",[6,\"mxWorleyDistance2d\",[\"localpos\",\"x\",\"y\",\"X\",\"Y\",\"jitter\",\"metric\"]]],[2,\"sqdist\",\"=\",[6,\"std.min\",[\"sqdist\",\"dist\"]]]]]]]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"0\"]]]],[0,[[2,\"sqdist\",\"=\",[6,\"std.sqrt\",[\"sqdist\"]]]]]],[10,\"sqdist\"]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.vec2f\":()=>m,\"d.f32\":()=>n,mxWorleyDistance2d:()=>J7,\"std.min\":()=>ye,\"std.sqrt\":()=>Ue}})&&e.f)({})),sT=w.fn([A,n,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=j(ie(t.x)),a=j(ie(t.y)),i=j(ie(t.z)),l=A(__tsover_sub(t.x,n(o)),__tsover_sub(t.y,n(a)),__tsover_sub(t.z,n(i))),c=n(1e6);for(let d=-1;d<=1;d++)for(let u=-1;u<=1;u++)for(let p=-1;p<=1;p++){let _=eT(l,d,u,p,o,a,i,s,r);c=ye(c,_)}return r===j(0)&&(c=Ue(c)),c}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"metric\"}],body:[0,[[13,\"X\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"x\"]]]]]],[13,\"Y\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"y\"]]]]]],[13,\"Z\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"p\",\"z\"]]]]]],[13,\"localpos\",[6,\"d.vec3f\",[[1,[7,\"p\",\"x\"],\"-\",[6,\"d.f32\",[\"X\"]]],[1,[7,\"p\",\"y\"],\"-\",[6,\"d.f32\",[\"Y\"]]],[1,[7,\"p\",\"z\"],\"-\",[6,\"d.f32\",[\"Z\"]]]]]],[12,\"sqdist\",[6,\"d.f32\",[[5,\"1000000\"]]]],[14,[12,\"x\",[4,\"-\",[5,\"1\"]]],[1,\"x\",\"<=\",[5,\"1\"]],[102,\"++\",\"x\"],[0,[[14,[12,\"y\",[4,\"-\",[5,\"1\"]]],[1,\"y\",\"<=\",[5,\"1\"]],[102,\"++\",\"y\"],[0,[[14,[12,\"z\",[4,\"-\",[5,\"1\"]]],[1,\"z\",\"<=\",[5,\"1\"]],[102,\"++\",\"z\"],[0,[[13,\"dist\",[6,\"mxWorleyDistance3d\",[\"localpos\",\"x\",\"y\",\"z\",\"X\",\"Y\",\"Z\",\"jitter\",\"metric\"]]],[2,\"sqdist\",\"=\",[6,\"std.min\",[\"sqdist\",\"dist\"]]]]]]]]]]]],[11,[1,\"metric\",\"===\",[6,\"d.i32\",[[5,\"0\"]]]],[0,[[2,\"sqdist\",\"=\",[6,\"std.sqrt\",[\"sqdist\"]]]]]],[10,\"sqdist\"]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,\"d.vec3f\":()=>A,\"d.f32\":()=>n,mxWorleyDistance3d:()=>eT,\"std.min\":()=>ye,\"std.sqrt\":()=>Ue}})&&e.f)({})),la=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return j7(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"mxPerlinNoiseFloat2\",[\"p\"]]]]]},externals:{mxPerlinNoiseFloat2:()=>j7}})&&e.f)({})),Na=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return K7(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"mxPerlinNoiseFloat3\",[\"p\"]]]]]},externals:{mxPerlinNoiseFloat3:()=>K7}})&&e.f)({})),I3=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return tT(t,s,j(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"jitter\"}],body:[0,[[10,[6,\"mxWorleyNoiseFloat2\",[\"p\",\"jitter\",[6,\"d.i32\",[[5,\"1\"]]]]]]]]},externals:{mxWorleyNoiseFloat2:()=>tT,\"d.i32\":()=>j}})&&e.f)({}));w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return sT(t,s,j(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"jitter\"}],body:[0,[[10,[6,\"mxWorleyNoiseFloat3\",[\"p\",\"jitter\",[6,\"d.i32\",[[5,\"1\"]]]]]]]]},externals:{mxWorleyNoiseFloat3:()=>sT,\"d.i32\":()=>j}})&&e.f)({}));var rT=w.fn([n],M)(\"(x: f32) -> u32 { return bitcast<u32>(x); }\"),Lu=w.fn([m],fe)(\"(v: vec2f) -> vec2u { return bitcast<vec2u>(v); }\"),tv=w.fn([A],id)(\"(v: vec3f) -> vec3u { return bitcast<vec3u>(v); }\"),Sn=n(4294967295),Xs=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=rT(__tsover_mul(t,3141592653));return __tsover_div(n(__tsover_mul(__tsover_mul(s,s),M(3141592653))),Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint1\",[[1,\"p\",\"*\",[5,\"3141592653\"]]]]],[10,[1,[6,\"d.f32\",[[1,[1,\"u\",\"*\",\"u\"],\"*\",[6,\"d.u32\",[[5,\"3141592653\"]]]]]],\"/\",\"U32\"]]]]},externals:{floatBitsToUint1:()=>rT,\"d.f32\":()=>n,\"d.u32\":()=>M,U32:()=>Sn}})&&e.f)({})),Ys=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Lu(t.mul(m(141421356,2718281828)));return __tsover_div(n(__tsover_mul(s.x^s.y,M(3141592653))),Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint2\",[[6,[7,\"p\",\"mul\"],[[6,\"d.vec2f\",[[5,\"141421356\"],[5,\"2718281828\"]]]]]]]],[10,[1,[6,\"d.f32\",[[1,[1,[7,\"u\",\"x\"],\"^\",[7,\"u\",\"y\"]],\"*\",[6,\"d.u32\",[[5,\"3141592653\"]]]]]],\"/\",\"U32\"]]]]},externals:{floatBitsToUint2:()=>Lu,\"d.vec2f\":()=>m,\"d.f32\":()=>n,\"d.u32\":()=>M,U32:()=>Sn}})&&e.f)({})),Pr=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Lu(t.mul(m(141421356,2718281828))),r=s.x^s.y,o=ra(fe(r,r),fe(3141592653,1618033988));return m(n(o.x),n(o.y)).div(Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint2\",[[6,[7,\"p\",\"mul\"],[[6,\"d.vec2f\",[[5,\"141421356\"],[5,\"2718281828\"]]]]]]]],[13,\"h\",[1,[7,\"u\",\"x\"],\"^\",[7,\"u\",\"y\"]]],[13,\"prod\",[6,\"std.mul\",[[6,\"d.vec2u\",[\"h\",\"h\"]],[6,\"d.vec2u\",[[5,\"3141592653\"],[5,\"1618033988\"]]]]]],[10,[6,[7,[6,\"d.vec2f\",[[6,\"d.f32\",[[7,\"prod\",\"x\"]]],[6,\"d.f32\",[[7,\"prod\",\"y\"]]]]],\"div\"],[\"U32\"]]]]]},externals:{floatBitsToUint2:()=>Lu,\"d.vec2f\":()=>m,\"std.mul\":()=>ra,\"d.vec2u\":()=>fe,\"d.f32\":()=>n,U32:()=>Sn}})&&e.f)({})),nJ=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Lu(t.mul(m(141421356,2718281828))),r=s.x^s.y,o=ra(id(r,r,r),id(1732050807,2645751311,3316624790));return A(n(o.x),n(o.y),n(o.z)).div(Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint2\",[[6,[7,\"p\",\"mul\"],[[6,\"d.vec2f\",[[5,\"141421356\"],[5,\"2718281828\"]]]]]]]],[13,\"h\",[1,[7,\"u\",\"x\"],\"^\",[7,\"u\",\"y\"]]],[13,\"prod\",[6,\"std.mul\",[[6,\"d.vec3u\",[\"h\",\"h\",\"h\"]],[6,\"d.vec3u\",[[5,\"1732050807\"],[5,\"2645751311\"],[5,\"3316624790\"]]]]]],[10,[6,[7,[6,\"d.vec3f\",[[6,\"d.f32\",[[7,\"prod\",\"x\"]]],[6,\"d.f32\",[[7,\"prod\",\"y\"]]],[6,\"d.f32\",[[7,\"prod\",\"z\"]]]]],\"div\"],[\"U32\"]]]]]},externals:{floatBitsToUint2:()=>Lu,\"d.vec2f\":()=>m,\"std.mul\":()=>ra,\"d.vec3u\":()=>id,\"d.vec3f\":()=>A,\"d.f32\":()=>n,U32:()=>Sn}})&&e.f)({})),Wb=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=tv(t.mul(A(141421356,2718281828,1618033988)));return __tsover_div(n(__tsover_mul(s.x^s.y^s.z,M(3141592653))),Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint3\",[[6,[7,\"p\",\"mul\"],[[6,\"d.vec3f\",[[5,\"141421356\"],[5,\"2718281828\"],[5,\"1618033988\"]]]]]]]],[10,[1,[6,\"d.f32\",[[1,[1,[1,[7,\"u\",\"x\"],\"^\",[7,\"u\",\"y\"]],\"^\",[7,\"u\",\"z\"]],\"*\",[6,\"d.u32\",[[5,\"3141592653\"]]]]]],\"/\",\"U32\"]]]]},externals:{floatBitsToUint3:()=>tv,\"d.vec3f\":()=>A,\"d.f32\":()=>n,\"d.u32\":()=>M,U32:()=>Sn}})&&e.f)({})),ei=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=tv(t.mul(A(141421356,2718281828,1618033988))),r=s.x^s.y^s.z,o=ra(id(r,r,r),id(1732050807,2645751311,3316624790));return A(n(o.x),n(o.y),n(o.z)).div(Sn)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"u\",[6,\"floatBitsToUint3\",[[6,[7,\"p\",\"mul\"],[[6,\"d.vec3f\",[[5,\"141421356\"],[5,\"2718281828\"],[5,\"1618033988\"]]]]]]]],[13,\"h\",[1,[1,[7,\"u\",\"x\"],\"^\",[7,\"u\",\"y\"]],\"^\",[7,\"u\",\"z\"]]],[13,\"prod\",[6,\"std.mul\",[[6,\"d.vec3u\",[\"h\",\"h\",\"h\"]],[6,\"d.vec3u\",[[5,\"1732050807\"],[5,\"2645751311\"],[5,\"3316624790\"]]]]]],[10,[6,[7,[6,\"d.vec3f\",[[6,\"d.f32\",[[7,\"prod\",\"x\"]]],[6,\"d.f32\",[[7,\"prod\",\"y\"]]],[6,\"d.f32\",[[7,\"prod\",\"z\"]]]]],\"div\"],[\"U32\"]]]]]},externals:{floatBitsToUint3:()=>tv,\"d.vec3f\":()=>A,\"std.mul\":()=>ra,\"d.vec3u\":()=>id,\"d.f32\":()=>n,U32:()=>Sn}})&&e.f)({})),Aa=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=t.sub(s),o=r.mul(r).mul(As(3,r.mul(2)));return K(K(Ys(s),Ys(s.add(m(1,0))),o.x),K(Ys(s.add(m(0,1))),Ys(s.add(m(1,1))),o.x),o.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f0\",[6,[7,\"p\",\"sub\"],[\"i\"]]],[13,\"f\",[6,[7,[6,[7,\"f0\",\"mul\"],[\"f0\"]],\"mul\"],[[6,\"std.sub\",[[5,\"3\"],[6,[7,\"f0\",\"mul\"],[[5,\"2\"]]]]]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[[6,\"hash12\",[\"i\"]],[6,\"hash12\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]]]]]],[7,\"f\",\"x\"]]],[6,\"std.mix\",[[6,\"hash12\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]]]]]],[6,\"hash12\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]],[7,\"f\",\"x\"]]],[7,\"f\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.sub\":()=>As,\"std.mix\":()=>K,hash12:()=>Ys,\"d.vec2f\":()=>m}})&&e.f)({})),om=w.fn([m,m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(r,o);return Se(vs(Pr(t.add(a)).sub(.5)),s.sub(a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"},{type:\"i\",name:\"f\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[13,\"o\",[6,\"d.vec2f\",[\"ox\",\"oy\"]]],[10,[6,\"std.dot\",[[6,\"std.normalize\",[[6,[7,[6,\"hash22\",[[6,[7,\"i\",\"add\"],[\"o\"]]]],\"sub\"],[[5,\"0.5\"]]]]],[6,[7,\"f\",\"sub\"],[\"o\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.dot\":()=>Se,\"std.normalize\":()=>vs,hash22:()=>Pr}})&&e.f)({})),Hc=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=t.sub(s),o=r.mul(r).mul(r).mul(r.mul(r.mul(6).sub(15)).add(10)),a=om(s,r,0,0),i=om(s,r,1,0),l=om(s,r,0,1),c=om(s,r,1,1);return __tsover_add(__tsover_mul(K(K(a,i,o.x),K(l,c,o.x),o.y),.7),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f\",[6,[7,\"p\",\"sub\"],[\"i\"]]],[13,\"u\",[6,[7,[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[5,\"6\"]]],\"sub\"],[[5,\"15\"]]]]],\"add\"],[[5,\"10\"]]]]]],[13,\"a\",[6,\"perlinGrad2\",[\"i\",\"f\",[5,\"0\"],[5,\"0\"]]]],[13,\"b\",[6,\"perlinGrad2\",[\"i\",\"f\",[5,\"1\"],[5,\"0\"]]]],[13,\"c\",[6,\"perlinGrad2\",[\"i\",\"f\",[5,\"0\"],[5,\"1\"]]]],[13,\"dd\",[6,\"perlinGrad2\",[\"i\",\"f\",[5,\"1\"],[5,\"1\"]]]],[10,[1,[1,[6,\"std.mix\",[[6,\"std.mix\",[\"a\",\"b\",[7,\"u\",\"x\"]]],[6,\"std.mix\",[\"c\",\"dd\",[7,\"u\",\"x\"]]],[7,\"u\",\"y\"]]],\"*\",[5,\"0.7\"]],\"+\",[5,\"0.5\"]]]]]},externals:{\"std.floor\":()=>ie,perlinGrad2:()=>om,\"std.mix\":()=>K}})&&e.f)({})),Si=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=Re(t),o=r.mul(r).mul(r).mul(r.mul(r.mul(6).sub(15)).add(10)),a=r.mul(r).mul(r.mul(r.sub(2)).add(1)).mul(30),i=Pr(s.add(m(0,0))).mul(2).sub(1),l=Pr(s.add(m(1,0))).mul(2).sub(1),c=Pr(s.add(m(0,1))).mul(2).sub(1),d=Pr(s.add(m(1,1))).mul(2).sub(1),u=Se(i,r.sub(m(0,0))),p=Se(l,r.sub(m(1,0))),_=Se(c,r.sub(m(0,1))),f=Se(d,r.sub(m(1,1))),h=__tsover_add(__tsover_sub(__tsover_sub(u,p),_),f),g=__tsover_add(__tsover_add(__tsover_add(u,__tsover_mul(o.x,__tsover_sub(p,u))),__tsover_mul(o.y,__tsover_sub(_,u))),__tsover_mul(__tsover_mul(o.x,o.y),h)),y=i.add(l.sub(i).mul(o.x)).add(c.sub(i).mul(o.y)).add(i.sub(l).sub(c).add(d).mul(__tsover_mul(o.x,o.y))).add(a.mul(m(o.y,o.x).mul(h).add(m(p,_)).sub(u)));return A(g,y.x,y.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f\",[6,\"std.fract\",[\"p\"]]],[13,\"u\",[6,[7,[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[5,\"6\"]]],\"sub\"],[[5,\"15\"]]]]],\"add\"],[[5,\"10\"]]]]]],[13,\"du\",[6,[7,[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[6,[7,\"f\",\"sub\"],[[5,\"2\"]]]]],\"add\"],[[5,\"1\"]]]]],\"mul\"],[[5,\"30\"]]]],[13,\"ga\",[6,[7,[6,[7,[6,\"hash22\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]]]]],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]],[13,\"gb\",[6,[7,[6,[7,[6,\"hash22\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]]]]]],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]],[13,\"gc\",[6,[7,[6,[7,[6,\"hash22\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]]]]]],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]],[13,\"gd\",[6,[7,[6,[7,[6,\"hash22\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]],[13,\"va\",[6,\"std.dot\",[\"ga\",[6,[7,\"f\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]]]]]],[13,\"vb\",[6,\"std.dot\",[\"gb\",[6,[7,\"f\",\"sub\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]]]]]]],[13,\"vc\",[6,\"std.dot\",[\"gc\",[6,[7,\"f\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]]]]]]],[13,\"vd\",[6,\"std.dot\",[\"gd\",[6,[7,\"f\",\"sub\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]]],[13,\"k\",[1,[1,[1,\"va\",\"-\",\"vb\"],\"-\",\"vc\"],\"+\",\"vd\"]],[13,\"value\",[1,[1,[1,\"va\",\"+\",[1,[7,\"u\",\"x\"],\"*\",[1,\"vb\",\"-\",\"va\"]]],\"+\",[1,[7,\"u\",\"y\"],\"*\",[1,\"vc\",\"-\",\"va\"]]],\"+\",[1,[1,[7,\"u\",\"x\"],\"*\",[7,\"u\",\"y\"]],\"*\",\"k\"]]],[13,\"deriv\",[6,[7,[6,[7,[6,[7,[6,[7,\"ga\",\"add\"],[[6,[7,[6,[7,\"gb\",\"sub\"],[\"ga\"]],\"mul\"],[[7,\"u\",\"x\"]]]]],\"add\"],[[6,[7,[6,[7,\"gc\",\"sub\"],[\"ga\"]],\"mul\"],[[7,\"u\",\"y\"]]]]],\"add\"],[[6,[7,[6,[7,[6,[7,[6,[7,\"ga\",\"sub\"],[\"gb\"]],\"sub\"],[\"gc\"]],\"add\"],[\"gd\"]],\"mul\"],[[1,[7,\"u\",\"x\"],\"*\",[7,\"u\",\"y\"]]]]]],\"add\"],[[6,[7,\"du\",\"mul\"],[[6,[7,[6,[7,[6,[7,[6,\"d.vec2f\",[[7,\"u\",\"y\"],[7,\"u\",\"x\"]]],\"mul\"],[\"k\"]],\"add\"],[[6,\"d.vec2f\",[\"vb\",\"vc\"]]]],\"sub\"],[\"va\"]]]]]]],[10,[6,\"d.vec3f\",[\"value\",[7,\"deriv\",\"x\"],[7,\"deriv\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,hash22:()=>Pr,\"d.vec2f\":()=>m,\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),am=w.fn([S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.mul(t.mul(34).add(1));return s.sub(ie(s.div(289)).mul(289))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"}],body:[0,[[13,\"xx\",[6,[7,\"x\",\"mul\"],[[6,[7,[6,[7,\"x\",\"mul\"],[[5,\"34\"]]],\"add\"],[[5,\"1\"]]]]]],[10,[6,[7,\"xx\",\"sub\"],[[6,[7,[6,\"std.floor\",[[6,[7,\"xx\",\"div\"],[[5,\"289\"]]]]],\"mul\"],[[5,\"289\"]]]]]]]]},externals:{\"std.floor\":()=>ie}})&&e.f)({})),iJ=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=t.sub(s),o=r.mul(r).mul(As(3,r.mul(2))),a=S(s.x,s.x,s.y,s.y).add(S(0,1,0,1)),i=am(S(a.x,a.y,a.x,a.y)),l=am(S(i.x,i.y,i.x,i.y).add(S(a.z,a.z,a.w,a.w))).add(S(s.z,s.z,s.z,s.z)),c=am(l),d=am(l.add(1)),u=K(Re(c.mul(.02439024)),Re(d.mul(.02439024)),S(o.z)),p=K(m(u.x,u.z),m(u.y,u.w),m(o.x));return K(p.x,p.y,o.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"a\",[6,\"std.floor\",[\"p\"]]],[13,\"d0\",[6,[7,\"p\",\"sub\"],[\"a\"]]],[13,\"sd\",[6,[7,[6,[7,\"d0\",\"mul\"],[\"d0\"]],\"mul\"],[[6,\"std.sub\",[[5,\"3\"],[6,[7,\"d0\",\"mul\"],[[5,\"2\"]]]]]]]],[13,\"b\",[6,[7,[6,\"d.vec4f\",[[7,\"a\",\"x\"],[7,\"a\",\"x\"],[7,\"a\",\"y\"],[7,\"a\",\"y\"]]],\"add\"],[[6,\"d.vec4f\",[[5,\"0\"],[5,\"1\"],[5,\"0\"],[5,\"1\"]]]]]],[13,\"k1\",[6,\"perm4\",[[6,\"d.vec4f\",[[7,\"b\",\"x\"],[7,\"b\",\"y\"],[7,\"b\",\"x\"],[7,\"b\",\"y\"]]]]]],[13,\"k2\",[6,[7,[6,\"perm4\",[[6,[7,[6,\"d.vec4f\",[[7,\"k1\",\"x\"],[7,\"k1\",\"y\"],[7,\"k1\",\"x\"],[7,\"k1\",\"y\"]]],\"add\"],[[6,\"d.vec4f\",[[7,\"b\",\"z\"],[7,\"b\",\"z\"],[7,\"b\",\"w\"],[7,\"b\",\"w\"]]]]]]],\"add\"],[[6,\"d.vec4f\",[[7,\"a\",\"z\"],[7,\"a\",\"z\"],[7,\"a\",\"z\"],[7,\"a\",\"z\"]]]]]],[13,\"k3\",[6,\"perm4\",[\"k2\"]]],[13,\"k4\",[6,\"perm4\",[[6,[7,\"k2\",\"add\"],[[5,\"1\"]]]]]],[13,\"o1\",[6,\"std.fract\",[[6,[7,\"k3\",\"mul\"],[[5,\"0.02439024\"]]]]]],[13,\"o2\",[6,\"std.fract\",[[6,[7,\"k4\",\"mul\"],[[5,\"0.02439024\"]]]]]],[13,\"o3\",[6,\"std.mix\",[\"o1\",\"o2\",[6,\"d.vec4f\",[[7,\"sd\",\"z\"]]]]]],[13,\"o4\",[6,\"std.mix\",[[6,\"d.vec2f\",[[7,\"o3\",\"x\"],[7,\"o3\",\"z\"]]],[6,\"d.vec2f\",[[7,\"o3\",\"y\"],[7,\"o3\",\"w\"]]],[6,\"d.vec2f\",[[7,\"sd\",\"x\"]]]]]],[10,[6,\"std.mix\",[[7,\"o4\",\"x\"],[7,\"o4\",\"y\"],[7,\"sd\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.sub\":()=>As,\"d.vec4f\":()=>S,perm4:()=>am,\"std.fract\":()=>Re,\"std.mix\":()=>K,\"d.vec2f\":()=>m}})&&e.f)({})),ji=w.fn([A,A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=A(r,o,a);return Se(s.sub(i),vs(ei(t.add(i)).sub(.5)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"},{type:\"i\",name:\"f\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"},{type:\"i\",name:\"oz\"}],body:[0,[[13,\"o\",[6,\"d.vec3f\",[\"ox\",\"oy\",\"oz\"]]],[10,[6,\"std.dot\",[[6,[7,\"f\",\"sub\"],[\"o\"]],[6,\"std.normalize\",[[6,[7,[6,\"hash33\",[[6,[7,\"i\",\"add\"],[\"o\"]]]],\"sub\"],[[5,\"0.5\"]]]]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.dot\":()=>Se,\"std.normalize\":()=>vs,hash33:()=>ei}})&&e.f)({})),mu=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=t.sub(s),o=r.mul(r).mul(r).mul(r.mul(r.mul(6).sub(15)).add(10)),a=ji(s,r,0,0,0),i=ji(s,r,1,0,0),l=ji(s,r,0,1,0),c=ji(s,r,1,1,0),d=ji(s,r,0,0,1),u=ji(s,r,1,0,1),p=ji(s,r,0,1,1),_=ji(s,r,1,1,1),f=K(K(a,i,o.x),K(l,c,o.x),o.y),h=K(K(d,u,o.x),K(p,_,o.x),o.y);return __tsover_add(__tsover_mul(K(f,h,o.z),.7),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f\",[6,[7,\"p\",\"sub\"],[\"i\"]]],[13,\"u\",[6,[7,[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[5,\"6\"]]],\"sub\"],[[5,\"15\"]]]]],\"add\"],[[5,\"10\"]]]]]],[13,\"a0\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[13,\"b0\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"1\"],[5,\"0\"],[5,\"0\"]]]],[13,\"c0\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"0\"],[5,\"1\"],[5,\"0\"]]]],[13,\"d0\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"1\"],[5,\"1\"],[5,\"0\"]]]],[13,\"a1\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]],[13,\"b1\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"1\"],[5,\"0\"],[5,\"1\"]]]],[13,\"c1\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"0\"],[5,\"1\"],[5,\"1\"]]]],[13,\"d1\",[6,\"perlinGrad3\",[\"i\",\"f\",[5,\"1\"],[5,\"1\"],[5,\"1\"]]]],[13,\"z0\",[6,\"std.mix\",[[6,\"std.mix\",[\"a0\",\"b0\",[7,\"u\",\"x\"]]],[6,\"std.mix\",[\"c0\",\"d0\",[7,\"u\",\"x\"]]],[7,\"u\",\"y\"]]]],[13,\"z1\",[6,\"std.mix\",[[6,\"std.mix\",[\"a1\",\"b1\",[7,\"u\",\"x\"]]],[6,\"std.mix\",[\"c1\",\"d1\",[7,\"u\",\"x\"]]],[7,\"u\",\"y\"]]]],[10,[1,[1,[6,\"std.mix\",[\"z0\",\"z1\",[7,\"u\",\"z\"]]],\"*\",[5,\"0.7\"]],\"+\",[5,\"0.5\"]]]]]},externals:{\"std.floor\":()=>ie,perlinGrad3:()=>ji,\"std.mix\":()=>K}})&&e.f)({})),nm=w.fn([m,m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return ae(__tsover_add(__tsover_mul(r,Se(t,Pr(s.add(m(a,i))))),o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"i\"},{type:\"i\",name:\"freq\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[10,[6,\"std.sin\",[[1,[1,\"freq\",\"*\",[6,\"std.dot\",[\"p\",[6,\"hash22\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[\"ox\",\"oy\"]]]]]]]]],\"+\",\"phase\"]]]]]]},externals:{\"std.sin\":()=>ae,\"std.dot\":()=>Se,hash22:()=>Pr,\"d.vec2f\":()=>m}})&&e.f)({})),lJ=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=ie(t),a=t.sub(o),i=a.mul(a).mul(As(3,a.mul(2)));return K(K(nm(t,o,s,r,0,0),nm(t,o,s,r,1,0),i.x),K(nm(t,o,s,r,0,1),nm(t,o,s,r,1,1),i.x),i.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"freq\"},{type:\"i\",name:\"phase\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f0\",[6,[7,\"p\",\"sub\"],[\"i\"]]],[13,\"f\",[6,[7,[6,[7,\"f0\",\"mul\"],[\"f0\"]],\"mul\"],[[6,\"std.sub\",[[5,\"3\"],[6,[7,\"f0\",\"mul\"],[[5,\"2\"]]]]]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[[6,\"gaborGrain\",[\"p\",\"i\",\"freq\",\"phase\",[5,\"0\"],[5,\"0\"]]],[6,\"gaborGrain\",[\"p\",\"i\",\"freq\",\"phase\",[5,\"1\"],[5,\"0\"]]],[7,\"f\",\"x\"]]],[6,\"std.mix\",[[6,\"gaborGrain\",[\"p\",\"i\",\"freq\",\"phase\",[5,\"0\"],[5,\"1\"]]],[6,\"gaborGrain\",[\"p\",\"i\",\"freq\",\"phase\",[5,\"1\"],[5,\"1\"]]],[7,\"f\",\"x\"]]],[7,\"f\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.sub\":()=>As,\"std.mix\":()=>K,gaborGrain:()=>nm}})&&e.f)({})),Uu=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=m(Hc(t.add(m(.1,0))),Hc(t.add(m(0,.1)))),r=m(Hc(t.sub(m(.1,0))),Hc(t.sub(m(0,.1))));return s.sub(r).div(.1).mul(.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"a\",[6,\"d.vec2f\",[[6,\"perlin12\",[[6,[7,\"p\",\"add\"],[[6,\"d.vec2f\",[[5,\"0.1\"],[5,\"0\"]]]]]]],[6,\"perlin12\",[[6,[7,\"p\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0.1\"]]]]]]]]]],[13,\"b\",[6,\"d.vec2f\",[[6,\"perlin12\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.1\"],[5,\"0\"]]]]]]],[6,\"perlin12\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0.1\"]]]]]]]]]],[10,[6,[7,[6,[7,[6,[7,\"a\",\"sub\"],[\"b\"]],\"div\"],[[5,\"0.1\"]]],\"mul\"],[[5,\"0.5\"]]]]]]},externals:{\"d.vec2f\":()=>m,perlin12:()=>Hc}})&&e.f)({})),sv=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t.add(m(.1,0)),o=t.add(m(0,.1)),a=t.sub(m(.1,0)),i=t.sub(m(0,.1)),l=m(mu(A(r.x,r.y,s)),mu(A(o.x,o.y,s))),c=m(mu(A(a.x,a.y,s)),mu(A(i.x,i.y,s)));return l.sub(c).div(.1).mul(.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"z\"}],body:[0,[[13,\"pax\",[6,[7,\"p\",\"add\"],[[6,\"d.vec2f\",[[5,\"0.1\"],[5,\"0\"]]]]]],[13,\"pay\",[6,[7,\"p\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0.1\"]]]]]],[13,\"pbx\",[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.1\"],[5,\"0\"]]]]]],[13,\"pby\",[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0.1\"]]]]]],[13,\"a\",[6,\"d.vec2f\",[[6,\"perlin13\",[[6,\"d.vec3f\",[[7,\"pax\",\"x\"],[7,\"pax\",\"y\"],\"z\"]]]],[6,\"perlin13\",[[6,\"d.vec3f\",[[7,\"pay\",\"x\"],[7,\"pay\",\"y\"],\"z\"]]]]]]],[13,\"b\",[6,\"d.vec2f\",[[6,\"perlin13\",[[6,\"d.vec3f\",[[7,\"pbx\",\"x\"],[7,\"pbx\",\"y\"],\"z\"]]]],[6,\"perlin13\",[[6,\"d.vec3f\",[[7,\"pby\",\"x\"],[7,\"pby\",\"y\"],\"z\"]]]]]]],[10,[6,[7,[6,[7,[6,[7,\"a\",\"sub\"],[\"b\"]],\"div\"],[[5,\"0.1\"]]],\"mul\"],[[5,\"0.5\"]]]]]]},externals:{\"d.vec2f\":()=>m,perlin13:()=>mu,\"d.vec3f\":()=>A}})&&e.f)({})),cJ=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=n(0),a=n(1),i=n(0),l=m(t.x,t.y);for(let c=0;c<4;c++){let d=l.mul(a),u=Re(ie(d).mul(m(123.34,233.53)));u=u.add(Se(u,u.add(23.234)));let p=__tsover_mul(Re(__tsover_mul(u.x,u.y)),1e3),_=Re(d).sub(.5),f=pe(p),h=ae(p),g=m(__tsover_sub(__tsover_mul(_.x,f),__tsover_mul(_.y,h)),__tsover_add(__tsover_mul(_.x,h),__tsover_mul(_.y,f)));o=__tsover_add(o,__tsover_div(__tsover_mul(ae(__tsover_add(__tsover_mul(g.x,10),s)),be(.25,0,Se(g,g))),a)),l=m(__tsover_sub(__tsover_mul(l.x,.54),__tsover_mul(l.y,.84)),__tsover_add(__tsover_mul(l.x,.84),__tsover_mul(l.y,.54))).add(n(c)),i=__tsover_add(i,__tsover_div(1,a)),a=__tsover_mul(a,r)}return __tsover_div(o,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"scale\"}],body:[0,[[12,\"dsum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"s\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"m\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"pp\",[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"y\"]]]],[14,[12,\"idx\",[5,\"0\"]],[1,\"idx\",\"<\",[5,\"4\"]],[102,\"++\",\"idx\"],[0,[[13,\"q0\",[6,[7,\"pp\",\"mul\"],[\"s\"]]],[12,\"g\",[6,\"std.fract\",[[6,[7,[6,\"std.floor\",[\"q0\"]],\"mul\"],[[6,\"d.vec2f\",[[5,\"123.34\"],[5,\"233.53\"]]]]]]]],[2,\"g\",\"=\",[6,[7,\"g\",\"add\"],[[6,\"std.dot\",[\"g\",[6,[7,\"g\",\"add\"],[[5,\"23.234\"]]]]]]]],[13,\"a\",[1,[6,\"std.fract\",[[1,[7,\"g\",\"x\"],\"*\",[7,\"g\",\"y\"]]]],\"*\",[5,\"1000\"]]],[13,\"qc\",[6,[7,[6,\"std.fract\",[\"q0\"]],\"sub\"],[[5,\"0.5\"]]]],[13,\"ca\",[6,\"std.cos\",[\"a\"]]],[13,\"sa\",[6,\"std.sin\",[\"a\"]]],[13,\"q\",[6,\"d.vec2f\",[[1,[1,[7,\"qc\",\"x\"],\"*\",\"ca\"],\"-\",[1,[7,\"qc\",\"y\"],\"*\",\"sa\"]],[1,[1,[7,\"qc\",\"x\"],\"*\",\"sa\"],\"+\",[1,[7,\"qc\",\"y\"],\"*\",\"ca\"]]]]],[2,\"dsum\",\"=\",[1,\"dsum\",\"+\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"q\",\"x\"],\"*\",[5,\"10\"]],\"+\",\"phase\"]]],\"*\",[6,\"std.smoothstep\",[[5,\"0.25\"],[5,\"0\"],[6,\"std.dot\",[\"q\",\"q\"]]]]],\"/\",\"s\"]]],[2,\"pp\",\"=\",[6,[7,[6,\"d.vec2f\",[[1,[1,[7,\"pp\",\"x\"],\"*\",[5,\"0.54\"]],\"-\",[1,[7,\"pp\",\"y\"],\"*\",[5,\"0.84\"]]],[1,[1,[7,\"pp\",\"x\"],\"*\",[5,\"0.84\"]],\"+\",[1,[7,\"pp\",\"y\"],\"*\",[5,\"0.54\"]]]]],\"add\"],[[6,\"d.f32\",[\"idx\"]]]]],[2,\"m\",\"=\",[1,\"m\",\"+\",[1,[5,\"1\"],\"/\",\"s\"]]],[2,\"s\",\"=\",[1,\"s\",\"*\",\"scale\"]]]]],[10,[1,\"dsum\",\"/\",\"m\"]]]]},externals:{\"d.f32\":()=>n,\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.floor\":()=>ie,\"std.dot\":()=>Se,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.smoothstep\":()=>be}})&&e.f)({})),dJ=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=n(0);for(let r=0;r<9;r++){let o=n(r),a=ie(__tsover_div(o,3)),i=__tsover_sub(o,__tsover_mul(a,3));s=__tsover_add(s,Ys(t.add(m(__tsover_sub(i,1),__tsover_sub(a,1)))))}return __tsover_add(__tsover_mul(.9,__tsover_sub(__tsover_mul(1.125,Ys(t)),__tsover_div(s,8))),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[12,\"v\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",[5,\"9\"]],[102,\"++\",\"k\"],[0,[[13,\"fk\",[6,\"d.f32\",[\"k\"]]],[13,\"row\",[6,\"std.floor\",[[1,\"fk\",\"/\",[5,\"3\"]]]]],[13,\"col\",[1,\"fk\",\"-\",[1,\"row\",\"*\",[5,\"3\"]]]],[2,\"v\",\"=\",[1,\"v\",\"+\",[6,\"hash12\",[[6,[7,\"p\",\"add\"],[[6,\"d.vec2f\",[[1,\"col\",\"-\",[5,\"1\"]],[1,\"row\",\"-\",[5,\"1\"]]]]]]]]]]]]],[10,[1,[1,[5,\"0.9\"],\"*\",[1,[1,[5,\"1.125\"],\"*\",[6,\"hash12\",[\"p\"]]],\"-\",[1,\"v\",\"/\",[5,\"8\"]]]],\"+\",[5,\"0.5\"]]]]]},externals:{\"d.f32\":()=>n,\"std.floor\":()=>ie,hash12:()=>Ys,\"d.vec2f\":()=>m}})&&e.f)({})),oT=w.fn([nc],j)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=j(0),r=t.x,o=t.y;for(let a=256;a>0;a=a>>1){let i=j(a),l=N(j(0),j(1),(r&i)>0),c=N(j(0),j(1),(o&i)>0),d=l<<1|l^c;s=__tsover_add(s,__tsover_mul(__tsover_mul(i,i),d));let u=__tsover_sub(j(1),c),p=__tsover_mul(r^o,u)^__tsover_mul(j(__tsover_sub(a,1)),l&u);r=r^p,o=o^p}return s}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[12,\"i\",[6,\"d.i32\",[[5,\"0\"]]]],[12,\"px\",[7,\"p\",\"x\"]],[12,\"py\",[7,\"p\",\"y\"]],[14,[12,\"s\",[5,\"256\"]],[1,\"s\",\">\",[5,\"0\"]],[2,\"s\",\"=\",[1,\"s\",\">>\",[5,\"1\"]]],[0,[[13,\"S\",[6,\"d.i32\",[\"s\"]]],[13,\"rx\",[6,\"std.select\",[[6,\"d.i32\",[[5,\"0\"]]],[6,\"d.i32\",[[5,\"1\"]]],[1,[1,\"px\",\"&\",\"S\"],\">\",[5,\"0\"]]]]],[13,\"ry\",[6,\"std.select\",[[6,\"d.i32\",[[5,\"0\"]]],[6,\"d.i32\",[[5,\"1\"]]],[1,[1,\"py\",\"&\",\"S\"],\">\",[5,\"0\"]]]]],[13,\"dq\",[1,[1,\"rx\",\"<<\",[5,\"1\"]],\"|\",[1,\"rx\",\"^\",\"ry\"]]],[2,\"i\",\"=\",[1,\"i\",\"+\",[1,[1,\"S\",\"*\",\"S\"],\"*\",\"dq\"]]],[13,\"oneMinusRy\",[1,[6,\"d.i32\",[[5,\"1\"]]],\"-\",\"ry\"]],[13,\"rhs\",[1,[1,[1,\"px\",\"^\",\"py\"],\"*\",\"oneMinusRy\"],\"^\",[1,[6,\"d.i32\",[[1,\"s\",\"-\",[5,\"1\"]]]],\"*\",[1,\"rx\",\"&\",\"oneMinusRy\"]]]],[2,\"px\",\"=\",[1,\"px\",\"^\",\"rhs\"]],[2,\"py\",\"=\",[1,\"py\",\"^\",\"rhs\"]]]]],[10,\"i\"]]]},externals:{\"d.i32\":()=>j,\"std.select\":()=>N}})&&e.f)({}));w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=oT(nc(j(t.x),j(t.y)))&j(262143);return Re(__tsover_mul(.6180339887498949,n(s)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"idx\",[1,[6,\"hilbertEncode512\",[[6,\"d.vec2i\",[[6,\"d.i32\",[[7,\"p\",\"x\"]]],[6,\"d.i32\",[[7,\"p\",\"y\"]]]]]]],\"&\",[6,\"d.i32\",[[5,\"262143\"]]]]],[10,[6,\"std.fract\",[[1,[5,\"0.6180339887498949\"],\"*\",[6,\"d.f32\",[\"idx\"]]]]]]]]},externals:{hilbertEncode512:()=>oT,\"d.vec2i\":()=>nc,\"d.i32\":()=>j,\"std.fract\":()=>Re,\"d.f32\":()=>n}})&&e.f)({}));var aT=w.fn([m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=ie(t),i=t.sub(a),l=ie(__tsover_mul(ae(__tsover_mul(a.x,51024)),3104)),c=ie(__tsover_mul(ae(__tsover_mul(a.y,1324)),554)),d=__tsover_add(l,c),u=i.mul(2).sub(1);u=u.mul(pe(d)).add(m(-u.y,u.x).mul(ae(d))),u=u.add(ae(__tsover_sub(l,c))),u=u.mul(.5).add(.5);let p=__tsover_mul(__tsover_add(ae(__tsover_add(l,__tsover_mul(u.y,Math.PI))),ae(__tsover_add(c,__tsover_mul(u.y,Math.PI)))),.2),_=re(__tsover_add(__tsover_sub(u.x,.5),p));_=__tsover_sub(.5,__tsover_mul(_,__tsover_div(s,o))),_=__tsover_mul(be(-2,__tsover_add(__tsover_mul(mr(_),1.5),16),_),12);let f=__tsover_add(__tsover_mul(ae(__tsover_add(r,__tsover_mul(Ys(a),6.2831853))),.5),.5);return __tsover_mul(__tsover_mul(_,u.y),f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"f\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"thickness\"}],body:[0,[[13,\"seed0\",[6,\"std.floor\",[\"uv\"]]],[13,\"lo\",[6,[7,\"uv\",\"sub\"],[\"seed0\"]]],[13,\"sx\",[6,\"std.floor\",[[1,[6,\"std.sin\",[[1,[7,\"seed0\",\"x\"],\"*\",[5,\"51024\"]]]],\"*\",[5,\"3104\"]]]]],[13,\"sy\",[6,\"std.floor\",[[1,[6,\"std.sin\",[[1,[7,\"seed0\",\"y\"],\"*\",[5,\"1324\"]]]],\"*\",[5,\"554\"]]]]],[13,\"ss\",[1,\"sx\",\"+\",\"sy\"]],[12,\"u2\",[6,[7,[6,[7,\"lo\",\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]],[2,\"u2\",\"=\",[6,[7,[6,[7,\"u2\",\"mul\"],[[6,\"std.cos\",[\"ss\"]]]],\"add\"],[[6,[7,[6,\"d.vec2f\",[[4,\"-\",[7,\"u2\",\"y\"]],[7,\"u2\",\"x\"]]],\"mul\"],[[6,\"std.sin\",[\"ss\"]]]]]]],[2,\"u2\",\"=\",[6,[7,\"u2\",\"add\"],[[6,\"std.sin\",[[1,\"sx\",\"-\",\"sy\"]]]]]],[2,\"u2\",\"=\",[6,[7,[6,[7,\"u2\",\"mul\"],[[5,\"0.5\"]]],\"add\"],[[5,\"0.5\"]]]],[13,\"WAVYNESS\",[5,\"0.2\"]],[13,\"sVal\",[1,[1,[6,\"std.sin\",[[1,\"sx\",\"+\",[1,[7,\"u2\",\"y\"],\"*\",\"Math.PI\"]]]],\"+\",[6,\"std.sin\",[[1,\"sy\",\"+\",[1,[7,\"u2\",\"y\"],\"*\",\"Math.PI\"]]]]],\"*\",\"WAVYNESS\"]],[12,\"x\",[6,\"std.abs\",[[1,[1,[7,\"u2\",\"x\"],\"-\",[5,\"0.5\"]],\"+\",\"sVal\"]]]],[2,\"x\",\"=\",[1,[5,\"0.5\"],\"-\",[1,\"x\",\"*\",[1,\"f\",\"/\",\"thickness\"]]]],[2,\"x\",\"=\",[1,[6,\"std.smoothstep\",[[4,\"-\",[5,\"2\"]],[1,[1,[6,\"std.fwidth\",[\"x\"]],\"*\",[5,\"1.5\"]],\"+\",[5,\"16\"]],\"x\"]],\"*\",[5,\"12\"]]],[13,\"pulse\",[1,[1,[6,\"std.sin\",[[1,\"time\",\"+\",[1,[6,\"hash12\",[\"seed0\"]],\"*\",[5,\"6.2831853\"]]]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[10,[1,[1,\"x\",\"*\",[7,\"u2\",\"y\"]],\"*\",\"pulse\"]]]]},externals:{\"std.floor\":()=>ie,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"d.vec2f\":()=>m,\"Math.PI\":()=>Math.PI,\"std.abs\":()=>re,\"std.smoothstep\":()=>be,\"std.fwidth\":()=>mr,hash12:()=>Ys}})&&e.f)({})),uJ=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=n(0),a=m(t.x,t.y),i=__tsover_div(1,he(mr(t)));for(let l=0;l<8;l++)o=B(o,aT(a,i,s,r)),a=m(__tsover_add(a.x,__tsover_mul(a.y,.7)),__tsover_add(__tsover_mul(a.x,-.7),a.y)).sub(12.31);return o}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"thickness\"}],body:[0,[[12,\"scratches\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"p\",[6,\"d.vec2f\",[[7,\"uv\",\"x\"],[7,\"uv\",\"y\"]]]],[13,\"f\",[1,[5,\"1\"],\"/\",[6,\"std.length\",[[6,\"std.fwidth\",[\"uv\"]]]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"8\"]],[102,\"++\",\"i\"],[0,[[2,\"scratches\",\"=\",[6,\"std.max\",[\"scratches\",[6,\"scratchOne\",[\"p\",\"f\",\"time\",\"thickness\"]]]]],[2,\"p\",\"=\",[6,[7,[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",[5,\"0.7\"]]],[1,[1,[7,\"p\",\"x\"],\"*\",[4,\"-\",[5,\"0.7\"]]],\"+\",[7,\"p\",\"y\"]]]],\"sub\"],[[5,\"12.31\"]]]]]]],[10,\"scratches\"]]]},externals:{\"d.f32\":()=>n,\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.fwidth\":()=>mr,\"std.max\":()=>B,scratchOne:()=>aT}})&&e.f)({})),nT=w.fn([m,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=n(0),o=n(0),a=n(1),i=m(t.x,t.y);for(let l=0;l<s;l++)r=__tsover_add(r,__tsover_mul(a,Hc(i))),o=__tsover_add(o,a),a=__tsover_mul(a,.5),i=i.mul(2);return __tsover_div(r,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"octaves\"}],body:[0,[[12,\"s\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"m\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"a\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"pp\",[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"y\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"octaves\"],[102,\"++\",\"i\"],[0,[[2,\"s\",\"=\",[1,\"s\",\"+\",[1,\"a\",\"*\",[6,\"perlin12\",[\"pp\"]]]]],[2,\"m\",\"=\",[1,\"m\",\"+\",\"a\"]],[2,\"a\",\"=\",[1,\"a\",\"*\",[5,\"0.5\"]]],[2,\"pp\",\"=\",[6,[7,\"pp\",\"mul\"],[[5,\"2\"]]]]]]],[10,[1,\"s\",\"/\",\"m\"]]]]},externals:{\"d.f32\":()=>n,\"d.vec2f\":()=>m,perlin12:()=>Hc}})&&e.f)({})),iT=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=A(0,0,0),r=n(1),o=m(t.x,t.y);for(let a=0;a<6;a++)s=s.add(Si(o).mul(r)),r=__tsover_mul(r,.5),o=o.mul(2);return s}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[12,\"s\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[12,\"a\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"pp\",[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"y\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"6\"]],[102,\"++\",\"i\"],[0,[[2,\"s\",\"=\",[6,[7,\"s\",\"add\"],[[6,[7,[6,\"perlin12d\",[\"pp\"]],\"mul\"],[\"a\"]]]]],[2,\"a\",\"=\",[1,\"a\",\"*\",[5,\"0.5\"]]],[2,\"pp\",\"=\",[6,[7,\"pp\",\"mul\"],[[5,\"2\"]]]]]]],[10,\"s\"]]]},externals:{\"d.vec3f\":()=>A,\"d.f32\":()=>n,\"d.vec2f\":()=>m,perlin12d:()=>Si}})&&e.f)({})),pJ=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=iT(t);return nT(t.add(m(s.y,s.z).mul(.4)),j(6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"fs\",[6,\"fbmStone\",[\"p\"]]],[10,[6,\"fbmPerlin\",[[6,[7,\"p\",\"add\"],[[6,[7,[6,\"d.vec2f\",[[7,\"fs\",\"y\"],[7,\"fs\",\"z\"]]],\"mul\"],[[5,\"0.4\"]]]]],[6,\"d.i32\",[[5,\"6\"]]]]]]]]},externals:{fbmStone:()=>iT,fbmPerlin:()=>nT,\"d.vec2f\":()=>m,\"d.i32\":()=>j}})&&e.f)({})),lT=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=m(0,0),r=n(0),o=n(1),a=m(t.x,t.y);for(let i=0;i<6;i++)s=s.add(Uu(a).mul(o)),r=__tsover_add(r,o),o=__tsover_mul(o,.5),a=a.mul(2);return s.div(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[12,\"s\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"m\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"a\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"pp\",[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"y\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"6\"]],[102,\"++\",\"i\"],[0,[[2,\"s\",\"=\",[6,[7,\"s\",\"add\"],[[6,[7,[6,\"curl22\",[\"pp\"]],\"mul\"],[\"a\"]]]]],[2,\"m\",\"=\",[1,\"m\",\"+\",\"a\"]],[2,\"a\",\"=\",[1,\"a\",\"*\",[5,\"0.5\"]]],[2,\"pp\",\"=\",[6,[7,\"pp\",\"mul\"],[[5,\"2\"]]]]]]],[10,[6,[7,\"s\",\"div\"],[\"m\"]]]]]},externals:{\"d.vec2f\":()=>m,\"d.f32\":()=>n,curl22:()=>Uu}})&&e.f)({})),mJ=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=lT(t);return B(re(s.x),re(s.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"n\",[6,\"fbmWool\",[\"p\"]]],[10,[6,\"std.max\",[[6,\"std.abs\",[[7,\"n\",\"x\"]]],[6,\"std.abs\",[[7,\"n\",\"y\"]]]]]]]]},externals:{fbmWool:()=>lT,\"std.max\":()=>B,\"std.abs\":()=>re}})&&e.f)({})),cT=w.fn([m,m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=m(-s.y,s.x).mul(Math.PI),o=ie(t),a=t.sub(o),i=m(0,0),l=n(0);for(let c=-1;c<=2;c++)for(let d=-1;d<=2;d++){let u=m(n(c),n(d)),p=a.sub(u).sub(Pr(o.add(u))).add(.5),_=Se(p,p),f=B(n(0),__tsover_sub(Tt(__tsover_mul(_,-2)),.01111));l=__tsover_add(l,f);let h=Se(p,r);i=i.add(m(pe(h),-ae(h)).mul(f))}return A(i.x,__tsover_mul(i.y,r.x),__tsover_mul(i.y,r.y)).div(l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"slope\"}],body:[0,[[13,\"sideDir\",[6,[7,[6,\"d.vec2f\",[[4,\"-\",[7,\"slope\",\"y\"]],[7,\"slope\",\"x\"]]],\"mul\"],[\"Math.PI\"]]],[13,\"id\",[6,\"std.floor\",[\"p\"]]],[13,\"pp\",[6,[7,\"p\",\"sub\"],[\"id\"]]],[12,\"heightSlope\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"wSum\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"x\",[4,\"-\",[5,\"1\"]]],[1,\"x\",\"<=\",[5,\"2\"]],[102,\"++\",\"x\"],[0,[[14,[12,\"y\",[4,\"-\",[5,\"1\"]]],[1,\"y\",\"<=\",[5,\"2\"]],[102,\"++\",\"y\"],[0,[[13,\"off\",[6,\"d.vec2f\",[[6,\"d.f32\",[\"x\"]],[6,\"d.f32\",[\"y\"]]]]],[13,\"c\",[6,[7,[6,[7,[6,[7,\"pp\",\"sub\"],[\"off\"]],\"sub\"],[[6,\"hash22\",[[6,[7,\"id\",\"add\"],[\"off\"]]]]]],\"add\"],[[5,\"0.5\"]]]],[13,\"dist2\",[6,\"std.dot\",[\"c\",\"c\"]]],[13,\"w\",[6,\"std.max\",[[6,\"d.f32\",[[5,\"0\"]]],[1,[6,\"std.exp\",[[1,\"dist2\",\"*\",[4,\"-\",[5,\"2\"]]]]],\"-\",[5,\"0.01111\"]]]]],[2,\"wSum\",\"=\",[1,\"wSum\",\"+\",\"w\"]],[13,\"t\",[6,\"std.dot\",[\"c\",\"sideDir\"]]],[2,\"heightSlope\",\"=\",[6,[7,\"heightSlope\",\"add\"],[[6,[7,[6,\"d.vec2f\",[[6,\"std.cos\",[\"t\"]],[4,\"-\",[6,\"std.sin\",[\"t\"]]]]],\"mul\"],[\"w\"]]]]]]]]]]],[10,[6,[7,[6,\"d.vec3f\",[[7,\"heightSlope\",\"x\"],[1,[7,\"heightSlope\",\"y\"],\"*\",[7,\"sideDir\",\"x\"]],[1,[7,\"heightSlope\",\"y\"],\"*\",[7,\"sideDir\",\"y\"]]]],\"div\"],[\"wSum\"]]]]]},externals:{\"d.vec2f\":()=>m,\"Math.PI\":()=>Math.PI,\"std.floor\":()=>ie,\"d.f32\":()=>n,hash22:()=>Pr,\"std.dot\":()=>Se,\"std.max\":()=>B,\"std.exp\":()=>Tt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec3f\":()=>A}})&&e.f)({})),_J=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Si(t),r=n(.25),o=n(8),a=n(1);for(let i=0;i<4;i++){let l=m(s.y,s.z),c=B(Se(l,l),n(1e-4)),d=l.mul(vt(c,n(__tsover_mul(.5,__tsover_sub(.5,1)))));s=s.add(cT(t.mul(o),d).mul(r).mul(A(1,o,o))),a=__tsover_add(a,r),r=__tsover_mul(r,.5),o=__tsover_mul(o,2)}return s.div(a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[12,\"nd\",[6,\"perlin12d\",[\"p\"]]],[12,\"strength\",[6,\"d.f32\",[[5,\"0.25\"]]]],[12,\"freq\",[6,\"d.f32\",[[5,\"8\"]]]],[12,\"total\",[6,\"d.f32\",[[5,\"1\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"4\"]],[102,\"++\",\"i\"],[0,[[13,\"grad\",[6,\"d.vec2f\",[[7,\"nd\",\"y\"],[7,\"nd\",\"z\"]]]],[13,\"len2\",[6,\"std.max\",[[6,\"std.dot\",[\"grad\",\"grad\"]],[6,\"d.f32\",[[5,\"0.0001\"]]]]]],[13,\"slope\",[6,[7,\"grad\",\"mul\"],[[6,\"std.pow\",[\"len2\",[6,\"d.f32\",[[1,[5,\"0.5\"],\"*\",[1,[5,\"0.5\"],\"-\",[5,\"1\"]]]]]]]]]],[2,\"nd\",\"=\",[6,[7,\"nd\",\"add\"],[[6,[7,[6,[7,[6,\"gullies\",[[6,[7,\"p\",\"mul\"],[\"freq\"]],\"slope\"]],\"mul\"],[\"strength\"]],\"mul\"],[[6,\"d.vec3f\",[[5,\"1\"],\"freq\",\"freq\"]]]]]]],[2,\"total\",\"=\",[1,\"total\",\"+\",\"strength\"]],[2,\"strength\",\"=\",[1,\"strength\",\"*\",[5,\"0.5\"]]],[2,\"freq\",\"=\",[1,\"freq\",\"*\",[5,\"2\"]]]]]],[10,[6,[7,\"nd\",\"div\"],[\"total\"]]]]]},externals:{perlin12d:()=>Si,\"d.f32\":()=>n,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.dot\":()=>Se,\"std.pow\":()=>vt,gullies:()=>cT,\"d.vec3f\":()=>A}})&&e.f)({})),dT=w.fn([m,j],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=m(0,0),o=n(0),a=n(1),i=m(t.x,t.y);for(let l=0;l<s;l++)r=r.add(G(Uu(i).mul(.5).add(.5),m(0,0),m(1,1)).mul(a)),o=__tsover_add(o,a),a=__tsover_mul(a,.8),i=i.mul(2);return r.div(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"octaves\"}],body:[0,[[12,\"s\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"m\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"a\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"pp\",[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"y\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"octaves\"],[102,\"++\",\"i\"],[0,[[2,\"s\",\"=\",[6,[7,\"s\",\"add\"],[[6,[7,[6,\"std.clamp\",[[6,[7,[6,[7,[6,\"curl22\",[\"pp\"]],\"mul\"],[[5,\"0.5\"]]],\"add\"],[[5,\"0.5\"]]],[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]],\"mul\"],[\"a\"]]]]],[2,\"m\",\"=\",[1,\"m\",\"+\",\"a\"]],[2,\"a\",\"=\",[1,\"a\",\"*\",[5,\"0.8\"]]],[2,\"pp\",\"=\",[6,[7,\"pp\",\"mul\"],[[5,\"2\"]]]]]]],[10,[6,[7,\"s\",\"div\"],[\"m\"]]]]]},externals:{\"d.vec2f\":()=>m,\"d.f32\":()=>n,\"std.clamp\":()=>G,curl22:()=>Uu}})&&e.f)({})),uT=w.fn([m,j],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_add(__tsover_mul(__tsover_div(he(dT(t,s)),1.414),.6),.4)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"octaves\"}],body:[0,[[10,[1,[1,[1,[6,\"std.length\",[[6,\"fbmPaper\",[\"p\",\"octaves\"]]]],\"/\",[5,\"1.414\"]],\"*\",[5,\"0.6\"]],\"+\",[5,\"0.4\"]]]]]},externals:{\"std.length\":()=>he,fbmPaper:()=>dT}})&&e.f)({}));w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(__tsover_add(__tsover_mul(t.x,.2126),__tsover_mul(t.y,.7152)),__tsover_mul(t.z,.0722))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[1,[1,[1,[7,\"rgb\",\"x\"],\"*\",[5,\"0.2126\"]],\"+\",[1,[7,\"rgb\",\"y\"],\"*\",[5,\"0.7152\"]]],\"+\",[1,[7,\"rgb\",\"z\"],\"*\",[5,\"0.0722\"]]]]]]},externals:{}})&&e.f)({}));w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(__tsover_add(__tsover_mul(t.x,.299),__tsover_mul(t.y,.587)),__tsover_mul(t.z,.114))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[1,[1,[1,[7,\"rgb\",\"x\"],\"*\",[5,\"0.299\"]],\"+\",[1,[7,\"rgb\",\"y\"],\"*\",[5,\"0.587\"]]],\"+\",[1,[7,\"rgb\",\"z\"],\"*\",[5,\"0.114\"]]]]]]},externals:{}})&&e.f)({}));var pT=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Se(t,A(.2126,.7152,.0722))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),rv=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Se(t,A(.299,.587,.114))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({}));w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return G(__tsover_add(__tsover_mul(__tsover_sub(t,r),s),r),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"pivot\"}],body:[0,[[10,[6,\"std.clamp\",[[1,[1,[1,\"x\",\"-\",\"pivot\"],\"*\",\"contrast\"],\"+\",\"pivot\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({}));var fJ=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(__tsover_mul(t,.5),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[10,[1,[1,\"v\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]},externals:{}})&&e.f)({}));w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_sub(1,re(t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.abs\",[\"v\"]]]]]]},externals:{\"std.abs\":()=>re}})&&e.f)({}));var zk=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=G(__tsover_add(__tsover_add(__tsover_mul(__tsover_sub(t,.5),__tsover_add(s,1)),.5),r),0,1);return __tsover_sub(1,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"noise01\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"balance\"}],body:[0,[[13,\"k\",[6,\"std.clamp\",[[1,[1,[1,[1,\"noise01\",\"-\",[5,\"0.5\"]],\"*\",[1,\"contrast\",\"+\",[5,\"1\"]]],\"+\",[5,\"0.5\"]],\"+\",\"balance\"],[5,\"0\"],[5,\"1\"]]]],[10,[1,[5,\"1\"],\"-\",\"k\"]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({})),Rk=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_add(__tsover_mul(t,__tsover_add(s,1)),r),a=G(__tsover_add(__tsover_mul(o,.5),.5),0,1);return __tsover_sub(1,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"balance\"}],body:[0,[[13,\"adjusted\",[1,[1,\"v\",\"*\",[1,\"contrast\",\"+\",[5,\"1\"]]],\"+\",\"balance\"]],[13,\"k\",[6,\"std.clamp\",[[1,[1,\"adjusted\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[10,[1,[5,\"1\"],\"-\",\"k\"]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({})),vJ=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(__tsover_mul(t,2),1),a=__tsover_add(__tsover_mul(o,s),r);return G(__tsover_add(__tsover_mul(a,.5),.5),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"balance\"}],body:[0,[[13,\"signed\",[1,[1,\"v\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"adjusted\",[1,[1,\"signed\",\"*\",\"contrast\"],\"+\",\"balance\"]],[10,[6,\"std.clamp\",[[1,[1,\"adjusted\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({})),hJ=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=vt(t,__tsover_div(5,s)),i=G(__tsover_add(__tsover_mul(__tsover_sub(a,.5),r),.5),0,1),l=__tsover_sub(__tsover_div(o,100),.5),c=G(__tsover_add(i,l),0,1);return __tsover_sub(1,c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"glow\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"balance\"}],body:[0,[[13,\"spread\",[6,\"std.pow\",[\"v\",[1,[5,\"5\"],\"/\",\"glow\"]]]],[13,\"contrasted\",[6,\"std.clamp\",[[1,[1,[1,\"spread\",\"-\",[5,\"0.5\"]],\"*\",\"contrast\"],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"balanceShift\",[1,[1,\"balance\",\"/\",[5,\"100\"]],\"-\",[5,\"0.5\"]]],[13,\"balanced\",[6,\"std.clamp\",[[1,\"contrasted\",\"+\",\"balanceShift\"],[5,\"0\"],[5,\"1\"]]]],[10,[1,[5,\"1\"],\"-\",\"balanced\"]]]]},externals:{\"std.pow\":()=>vt,\"std.clamp\":()=>G}})&&e.f)({}));function Dk(e){let{domain:t,contrastMode:s,invert:r,glowGamma:o=!1,balance:a=\"plain\"}=e;if(o){if(t===\"unit\"&&s===\"multiplicative\"&&r&&a===\"percentCentred\")return hJ}else if(a===\"plain\"){if(t===\"unit\"&&s===\"additive\"&&r)return zk;if(t===\"signed\"&&s===\"additive\"&&r)return Rk;if(t===\"unit\"&&s===\"multiplicative\"&&!r)return vJ}throw new Error(`toneRemap: no variant for {domain: '${t}', contrastMode: '${s}', invert: ${r}, glowGamma: ${o}, balance: '${a}'} \\u2014 add its body to kit/tone.ts rather than approximating.`)}var gJ=w.fn([S,m,m,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Ys(s.mul(r).add(m(o,__tsover_mul(o,1.618))));return S(G(t.xyz.add(__tsover_mul(__tsover_sub(a,.5),.004)),A(0),A(1)),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"n\",[6,\"hash12\",[[6,[7,[6,[7,\"uv\",\"mul\"],[\"viewport\"]],\"add\"],[[6,\"d.vec2f\",[\"animTime\",[1,\"animTime\",\"*\",[5,\"1.618\"]]]]]]]]],[13,\"rgb\",[6,\"std.clamp\",[[6,[7,[7,\"base\",\"xyz\"],\"add\"],[[1,[1,\"n\",\"-\",[5,\"0.5\"]],\"*\",[5,\"0.004\"]]]],[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[10,[6,\"d.vec4f\",[\"rgb\",[7,\"base\",\"w\"]]]]]]},externals:{hash12:()=>Ys,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Cs=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_div(t.x,B(t.y,1e-6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"viewport\"}],body:[0,[[10,[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]]]]},externals:{\"std.max\":()=>B}})&&e.f)({})),Ou=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_sub(t,__tsover_mul(s,ie(__tsover_div(t,s))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"m\"}],body:[0,[[10,[1,\"x\",\"-\",[1,\"m\",\"*\",[6,\"std.floor\",[[1,\"x\",\"/\",\"m\"]]]]]]]]},externals:{\"std.floor\":()=>ie}})&&e.f)({})),n0=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(__tsover_sub(t.x,__tsover_mul(s.x,ie(__tsover_div(t.x,s.x)))),__tsover_sub(t.y,__tsover_mul(s.y,ie(__tsover_div(t.y,s.y)))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"m\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"-\",[1,[7,\"m\",\"x\"],\"*\",[6,\"std.floor\",[[1,[7,\"p\",\"x\"],\"/\",[7,\"m\",\"x\"]]]]]],[1,[7,\"p\",\"y\"],\"-\",[1,[7,\"m\",\"y\"],\"*\",[6,\"std.floor\",[[1,[7,\"p\",\"y\"],\"/\",[7,\"m\",\"y\"]]]]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.floor\":()=>ie}})&&e.f)({}));w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=N(B(s,1e-5),ye(s,-1e-5),s<0);return __tsover_div(t,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"n\"},{type:\"i\",name:\"den\"}],body:[0,[[13,\"guarded\",[6,\"std.select\",[[6,\"std.max\",[\"den\",[5,\"0.00001\"]]],[6,\"std.min\",[\"den\",[4,\"-\",[5,\"0.00001\"]]]],[1,\"den\",\"<\",[5,\"0\"]]]]],[10,[1,\"n\",\"/\",\"guarded\"]]]]},externals:{\"std.select\":()=>N,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({}));var Pn=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(__tsover_sub(__tsover_mul(t.x,s),__tsover_mul(t.y,r)),__tsover_add(__tsover_mul(t.x,r),__tsover_mul(t.y,s)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"cosA\"},{type:\"i\",name:\"sinA\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[7,\"p\",\"x\"],\"*\",\"cosA\"],\"-\",[1,[7,\"p\",\"y\"],\"*\",\"sinA\"]],[1,[1,[7,\"p\",\"x\"],\"*\",\"sinA\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"cosA\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),mT=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(s.x,B(s.y,1e-6));return m(__tsover_mul(t.x,r),t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),ov=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(s.x,B(s.y,1e-6));return m(__tsover_mul(t.x,r),__tsover_sub(1,t.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"uv\",\"y\"]]]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),i0=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return m(t.x,__tsover_sub(1,t.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"d.vec2f\",[[7,\"p\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"p\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Zi=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(__tsover_mul(t.x,s),__tsover_sub(1,t.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"aspect\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"pos\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"pos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),cc=w.fn([m,m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(r.x,B(r.y,1e-6));return m(__tsover_sub(__tsover_mul(t.x,o),__tsover_mul(s.x,o)),__tsover_sub(t.y,__tsover_sub(1,s.y)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,[7,\"center\",\"x\"],\"*\",\"aspect\"]],[1,[7,\"uv\",\"y\"],\"-\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),dc=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(__tsover_sub(__tsover_mul(t.x,s),__tsover_mul(s,.5)),__tsover_sub(t.y,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,\"aspect\",\"*\",[5,\"0.5\"]]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Gu=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,.5),i=.5,l=__tsover_sub(t.x,a),c=__tsover_sub(t.y,i);return m(__tsover_add(__tsover_sub(__tsover_mul(l,r),__tsover_mul(c,o)),a),__tsover_add(__tsover_add(__tsover_mul(l,o),__tsover_mul(c,r)),i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"cosA\"},{type:\"i\",name:\"sinA\"}],body:[0,[[13,\"centerX\",[1,\"aspect\",\"*\",[5,\"0.5\"]]],[13,\"centerY\",[5,\"0.5\"]],[13,\"cx\",[1,[7,\"p\",\"x\"],\"-\",\"centerX\"]],[13,\"cy\",[1,[7,\"p\",\"y\"],\"-\",\"centerY\"]],[10,[6,\"d.vec2f\",[[1,[1,[1,\"cx\",\"*\",\"cosA\"],\"-\",[1,\"cy\",\"*\",\"sinA\"]],\"+\",\"centerX\"],[1,[1,[1,\"cx\",\"*\",\"sinA\"],\"+\",[1,\"cy\",\"*\",\"cosA\"]],\"+\",\"centerY\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),av=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_add(__tsover_mul(t.x,s),__tsover_mul(t.y,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"cosA\"},{type:\"i\",name:\"sinA\"}],body:[0,[[10,[1,[1,[7,\"p\",\"x\"],\"*\",\"cosA\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"sinA\"]]]]]},externals:{}})&&e.f)({}));w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return m(he(t),Xt(t.y,t.x))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"d.vec2f\",[[6,\"std.length\",[\"p\"]],[6,\"std.atan2\",[[7,\"p\",\"y\"],[7,\"p\",\"x\"]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.atan2\":()=>Xt}})&&e.f)({}));w.fn([n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(__tsover_mul(t,pe(s)),__tsover_mul(t,ae(s)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"r\"},{type:\"i\",name:\"theta\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,\"r\",\"*\",[6,\"std.cos\",[\"theta\"]]],[1,\"r\",\"*\",[6,\"std.sin\",[\"theta\"]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({}));var _T=2/Math.sqrt(3),l0=gt({cell:m,local:m}).$name(\"CellTile\"),Fu=w.fn([m],l0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return l0({cell:ie(t),local:Re(t)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"CellTile\",[[104,{cell:[6,\"std.floor\",[\"p\"]],local:[6,\"std.fract\",[\"p\"]]}]]]]]]},externals:{CellTile:()=>l0,\"std.floor\":()=>ie,\"std.fract\":()=>Re}})&&e.f)({})),c0=gt({gv:m,id:m}).$name(\"HexCell\"),nv=w.fn([m,m],c0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=s.mul(.5),o=n0(t,s).sub(r),a=n0(t.sub(r),s).sub(r),i=N(a,o,Se(o,o)<Se(a,a));return c0({gv:i,id:t.sub(i)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"s\"}],body:[0,[[13,\"sh\",[6,[7,\"s\",\"mul\"],[[5,\"0.5\"]]]],[13,\"a\",[6,[7,[6,\"flooredMod2\",[\"p\",\"s\"]],\"sub\"],[\"sh\"]]],[13,\"b\",[6,[7,[6,\"flooredMod2\",[[6,[7,\"p\",\"sub\"],[\"sh\"]],\"s\"]],\"sub\"],[\"sh\"]]],[13,\"gv\",[6,\"std.select\",[\"b\",\"a\",[1,[6,\"std.dot\",[\"a\",\"a\"]],\"<\",[6,\"std.dot\",[\"b\",\"b\"]]]]]],[10,[6,\"HexCell\",[[104,{gv:\"gv\",id:[6,[7,\"p\",\"sub\"],[\"gv\"]]}]]]]]]},externals:{flooredMod2:()=>n0,\"std.select\":()=>N,\"std.dot\":()=>Se,HexCell:()=>c0}})&&e.f)({})),fT=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return m(__tsover_sub(t.x,__tsover_div(t.y,lc)),__tsover_mul(t.y,_T))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"-\",[1,[7,\"p\",\"y\"],\"/\",\"SQRT3\"]],[1,[7,\"p\",\"y\"],\"*\",\"TWO_OVER_SQRT3\"]]]]]]},externals:{\"d.vec2f\":()=>m,SQRT3:()=>lc,TWO_OVER_SQRT3:()=>_T}})&&e.f)({})),yJ=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(s.x,B(s.y,1e-6)),a=__tsover_mul(t.x,o),i=__tsover_sub(1,t.y),l=__tsover_div(__tsover_add(ie(__tsover_mul(a,r)),.5),r),c=__tsover_div(__tsover_add(ie(__tsover_mul(i,r)),.5),r);return m(__tsover_div(l,o),__tsover_sub(1,c))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[13,\"correctedX\",[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"]],[13,\"correctedY\",[1,[5,\"1\"],\"-\",[7,\"uv\",\"y\"]]],[13,\"cellCenterCorrectedX\",[1,[1,[6,\"std.floor\",[[1,\"correctedX\",\"*\",\"cells\"]]],\"+\",[5,\"0.5\"]],\"/\",\"cells\"]],[13,\"cellCenterCorrectedY\",[1,[1,[6,\"std.floor\",[[1,\"correctedY\",\"*\",\"cells\"]]],\"+\",[5,\"0.5\"]],\"/\",\"cells\"]],[10,[6,\"d.vec2f\",[[1,\"cellCenterCorrectedX\",\"/\",\"aspect\"],[1,[5,\"1\"],\"-\",\"cellCenterCorrectedY\"]]]]]]},externals:{\"std.max\":()=>B,\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),vT=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_div(s.x,B(s.y,1e-6)),i=__tsover_mul(t.x,a),l=__tsover_sub(1,t.y),c=pe(o),d=ae(o),u=__tsover_mul(a,.5),p=.5,_=__tsover_sub(i,u),f=__tsover_sub(l,p),h=__tsover_add(__tsover_sub(__tsover_mul(_,c),__tsover_mul(f,d)),u),g=__tsover_add(__tsover_add(__tsover_mul(_,d),__tsover_mul(f,c)),p),y=__tsover_div(__tsover_add(ie(__tsover_mul(h,r)),.5),r),b=__tsover_div(__tsover_add(ie(__tsover_mul(g,r)),.5),r),x=__tsover_sub(y,u),P=__tsover_sub(b,p),C=__tsover_add(__tsover_add(__tsover_mul(x,c),__tsover_mul(P,d)),u),E=__tsover_add(__tsover_add(__tsover_mul(x,-d),__tsover_mul(P,c)),p);return m(__tsover_div(C,a),__tsover_sub(1,E))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"rotationRad\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[13,\"correctedX\",[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"]],[13,\"correctedY\",[1,[5,\"1\"],\"-\",[7,\"uv\",\"y\"]]],[13,\"cosR\",[6,\"std.cos\",[\"rotationRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotationRad\"]]],[13,\"centerX\",[1,\"aspect\",\"*\",[5,\"0.5\"]]],[13,\"centerY\",[5,\"0.5\"]],[13,\"cx\",[1,\"correctedX\",\"-\",\"centerX\"]],[13,\"cy\",[1,\"correctedY\",\"-\",\"centerY\"]],[13,\"rotX\",[1,[1,[1,\"cx\",\"*\",\"cosR\"],\"-\",[1,\"cy\",\"*\",\"sinR\"]],\"+\",\"centerX\"]],[13,\"rotY\",[1,[1,[1,\"cx\",\"*\",\"sinR\"],\"+\",[1,\"cy\",\"*\",\"cosR\"]],\"+\",\"centerY\"]],[13,\"cellRX\",[1,[1,[6,\"std.floor\",[[1,\"rotX\",\"*\",\"cells\"]]],\"+\",[5,\"0.5\"]],\"/\",\"cells\"]],[13,\"cellRY\",[1,[1,[6,\"std.floor\",[[1,\"rotY\",\"*\",\"cells\"]]],\"+\",[5,\"0.5\"]],\"/\",\"cells\"]],[13,\"ccx\",[1,\"cellRX\",\"-\",\"centerX\"]],[13,\"ccy\",[1,\"cellRY\",\"-\",\"centerY\"]],[13,\"unrotX\",[1,[1,[1,\"ccx\",\"*\",\"cosR\"],\"+\",[1,\"ccy\",\"*\",\"sinR\"]],\"+\",\"centerX\"]],[13,\"unrotY\",[1,[1,[1,\"ccx\",\"*\",[4,\"-\",\"sinR\"]],\"+\",[1,\"ccy\",\"*\",\"cosR\"]],\"+\",\"centerY\"]],[10,[6,\"d.vec2f\",[[1,\"unrotX\",\"/\",\"aspect\"],[1,[5,\"1\"],\"-\",\"unrotY\"]]]]]]},externals:{\"std.max\":()=>B,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),iv=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Re(t.mul(m(123.34,345.45))),r=s.add(Se(s,s.add(34.345)));return Re(__tsover_mul(r.x,r.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"id\"}],body:[0,[[13,\"q0\",[6,\"std.fract\",[[6,[7,\"id\",\"mul\"],[[6,\"d.vec2f\",[[5,\"123.34\"],[5,\"345.45\"]]]]]]]],[13,\"q1\",[6,[7,\"q0\",\"add\"],[[6,\"std.dot\",[\"q0\",[6,[7,\"q0\",\"add\"],[[5,\"34.345\"]]]]]]]],[10,[6,\"std.fract\",[[1,[7,\"q1\",\"x\"],\"*\",[7,\"q1\",\"y\"]]]]]]]},externals:{\"std.fract\":()=>Re,\"d.vec2f\":()=>m,\"std.dot\":()=>Se}})&&e.f)({})),lv=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Re(__tsover_mul(ae(__tsover_mul(t,127.1)),43758.5453))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"row\"}],body:[0,[[10,[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,\"row\",\"*\",[5,\"127.1\"]]]],\"*\",[5,\"43758.5453\"]]]]]]]},externals:{\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({})),cv=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_add(1,__tsover_mul(__tsover_mul(__tsover_sub(t,.5),4),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rowHash\"},{type:\"i\",name:\"variance\"}],body:[0,[[10,[1,[5,\"1\"],\"+\",[1,[1,[1,\"rowHash\",\"-\",[5,\"0.5\"]],\"*\",[5,\"4\"]],\"*\",\"variance\"]]]]]},externals:{}})&&e.f)({})),hT=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return B(__tsover_add(1,__tsover_mul(__tsover_mul(__tsover_sub(t,.5),2),s)),0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rand\"},{type:\"i\",name:\"variation\"}],body:[0,[[10,[6,\"std.max\",[[1,[5,\"1\"],\"+\",[1,[1,[1,\"rand\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",\"variation\"]],[5,\"0\"]]]]]]},externals:{\"std.max\":()=>B}})&&e.f)({})),dv=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=B(re(Bu(t)),re(zu(t)));return B(__tsover_add(r,s),1e-5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"baseW\",[6,\"std.max\",[[6,\"std.abs\",[[6,\"std.dpdx\",[\"x\"]]]],[6,\"std.abs\",[[6,\"std.dpdy\",[\"x\"]]]]]]],[10,[6,\"std.max\",[[1,\"baseW\",\"+\",\"softness\"],[5,\"0.00001\"]]]]]]},externals:{\"std.max\":()=>B,\"std.abs\":()=>re,\"std.dpdx\":()=>Bu,\"std.dpdy\":()=>zu}})&&e.f)({})),gT=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Bu(t),o=zu(t),a=m(B(re(r.x),re(o.x)),B(re(r.y),re(o.y)));return m(B(__tsover_add(a.x,s),1e-5),B(__tsover_add(a.y,s),1e-5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"dpx\",[6,\"std.dpdx\",[\"p\"]]],[13,\"dpy\",[6,\"std.dpdy\",[\"p\"]]],[13,\"baseW\",[6,\"d.vec2f\",[[6,\"std.max\",[[6,\"std.abs\",[[7,\"dpx\",\"x\"]]],[6,\"std.abs\",[[7,\"dpy\",\"x\"]]]]],[6,\"std.max\",[[6,\"std.abs\",[[7,\"dpx\",\"y\"]]],[6,\"std.abs\",[[7,\"dpy\",\"y\"]]]]]]]],[10,[6,\"d.vec2f\",[[6,\"std.max\",[[1,[7,\"baseW\",\"x\"],\"+\",\"softness\"],[5,\"0.00001\"]]],[6,\"std.max\",[[1,[7,\"baseW\",\"y\"],\"+\",\"softness\"],[5,\"0.00001\"]]]]]]]]},externals:{\"std.dpdx\":()=>Bu,\"std.dpdy\":()=>zu,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.abs\":()=>re}})&&e.f)({})),yT=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_add(t,__tsover_mul(s,.5)),a=__tsover_sub(t,__tsover_mul(s,.5)),i=__tsover_sub(1,r),l=__tsover_add(__tsover_mul(ie(o),i),B(__tsover_sub(Re(o),r),0)),c=__tsover_add(__tsover_mul(ie(a),i),B(__tsover_sub(Re(a),r),0));return G(__tsover_div(__tsover_sub(l,c),s),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"w\"},{type:\"i\",name:\"threshold\"}],body:[0,[[13,\"a\",[1,\"p\",\"+\",[1,\"w\",\"*\",[5,\"0.5\"]]]],[13,\"b\",[1,\"p\",\"-\",[1,\"w\",\"*\",[5,\"0.5\"]]]],[13,\"oneMinusThreshold\",[1,[5,\"1\"],\"-\",\"threshold\"]],[13,\"Fa\",[1,[1,[6,\"std.floor\",[\"a\"]],\"*\",\"oneMinusThreshold\"],\"+\",[6,\"std.max\",[[1,[6,\"std.fract\",[\"a\"]],\"-\",\"threshold\"],[5,\"0\"]]]]],[13,\"Fb\",[1,[1,[6,\"std.floor\",[\"b\"]],\"*\",\"oneMinusThreshold\"],\"+\",[6,\"std.max\",[[1,[6,\"std.fract\",[\"b\"]],\"-\",\"threshold\"],[5,\"0\"]]]]],[10,[6,\"std.clamp\",[[1,[1,\"Fa\",\"-\",\"Fb\"],\"/\",\"w\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.max\":()=>B,\"std.fract\":()=>Re,\"std.clamp\":()=>G}})&&e.f)({})),d0=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_add(t,__tsover_mul(s,.5)),a=__tsover_sub(t,__tsover_mul(s,.5));return __tsover_div(__tsover_sub(__tsover_sub(__tsover_add(ie(o),ye(__tsover_mul(Re(o),r),1)),ie(a)),ye(__tsover_mul(Re(a),r),1)),__tsover_mul(r,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"w\"},{type:\"i\",name:\"N\"}],body:[0,[[13,\"a\",[1,\"p\",\"+\",[1,\"w\",\"*\",[5,\"0.5\"]]]],[13,\"b\",[1,\"p\",\"-\",[1,\"w\",\"*\",[5,\"0.5\"]]]],[10,[1,[1,[1,[1,[6,\"std.floor\",[\"a\"]],\"+\",[6,\"std.min\",[[1,[6,\"std.fract\",[\"a\"]],\"*\",\"N\"],[5,\"1\"]]]],\"-\",[6,\"std.floor\",[\"b\"]]],\"-\",[6,\"std.min\",[[1,[6,\"std.fract\",[\"b\"]],\"*\",\"N\"],[5,\"1\"]]]],\"/\",[1,\"N\",\"*\",\"w\"]]]]]},externals:{\"std.floor\":()=>ie,\"std.min\":()=>ye,\"std.fract\":()=>Re}})&&e.f)({})),bT=w.fn([m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=m(__tsover_mul(s.x,.5),__tsover_mul(s.y,.5)),o=re(__tsover_sub(Re(__tsover_mul(__tsover_sub(t.x,r.x),.5)),.5)),a=re(__tsover_sub(Re(__tsover_mul(__tsover_sub(t.y,r.y),.5)),.5)),i=re(__tsover_sub(Re(__tsover_mul(__tsover_add(t.x,r.x),.5)),.5)),l=re(__tsover_sub(Re(__tsover_mul(__tsover_add(t.y,r.y),.5)),.5)),c=__tsover_div(__tsover_mul(__tsover_sub(o,i),2),s.x),d=__tsover_div(__tsover_mul(__tsover_sub(a,l),2),s.y);return G(__tsover_sub(.5,__tsover_mul(__tsover_mul(c,d),.5)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"w\"}],body:[0,[[13,\"aHalf\",[6,\"d.vec2f\",[[1,[7,\"w\",\"x\"],\"*\",[5,\"0.5\"]],[1,[7,\"w\",\"y\"],\"*\",[5,\"0.5\"]]]]],[13,\"minusSideX\",[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,[1,[7,\"p\",\"x\"],\"-\",[7,\"aHalf\",\"x\"]],\"*\",[5,\"0.5\"]]]],\"-\",[5,\"0.5\"]]]]],[13,\"minusSideY\",[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,[1,[7,\"p\",\"y\"],\"-\",[7,\"aHalf\",\"y\"]],\"*\",[5,\"0.5\"]]]],\"-\",[5,\"0.5\"]]]]],[13,\"plusSideX\",[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,[1,[7,\"p\",\"x\"],\"+\",[7,\"aHalf\",\"x\"]],\"*\",[5,\"0.5\"]]]],\"-\",[5,\"0.5\"]]]]],[13,\"plusSideY\",[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,[1,[7,\"p\",\"y\"],\"+\",[7,\"aHalf\",\"y\"]],\"*\",[5,\"0.5\"]]]],\"-\",[5,\"0.5\"]]]]],[13,\"iX\",[1,[1,[1,\"minusSideX\",\"-\",\"plusSideX\"],\"*\",[5,\"2\"]],\"/\",[7,\"w\",\"x\"]]],[13,\"iY\",[1,[1,[1,\"minusSideY\",\"-\",\"plusSideY\"],\"*\",[5,\"2\"]],\"/\",[7,\"w\",\"y\"]]],[10,[6,\"std.clamp\",[[1,[5,\"0.5\"],\"-\",[1,[1,\"iX\",\"*\",\"iY\"],\"*\",[5,\"0.5\"]]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.fract\":()=>Re,\"std.clamp\":()=>G}})&&e.f)({})),uv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return __tsover_mul(__tsover_sub(1,be(r,o,t)),Et(1e-4,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"field\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"lo\"},{type:\"i\",name:\"hi\"}],body:[0,[[10,[1,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"lo\",\"hi\",\"field\"]]],\"*\",[6,\"std.step\",[[5,\"0.0001\"],\"lineWidth\"]]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.step\":()=>Et}})&&e.f)({})),xu=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_mul(be(s,r,t),be(s,r,__tsover_sub(1,t)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"lo\"},{type:\"i\",name:\"hi\"}],body:[0,[[10,[1,[6,\"std.smoothstep\",[\"lo\",\"hi\",\"x\"]],\"*\",[6,\"std.smoothstep\",[\"lo\",\"hi\",[1,[5,\"1\"],\"-\",\"x\"]]]]]]]},externals:{\"std.smoothstep\":()=>be}})&&e.f)({})),xT=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_sub(1,be(__tsover_sub(s,__tsover_mul(r,.5)),s,t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"footprint\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"radius\",\"-\",[1,\"footprint\",\"*\",[5,\"0.5\"]]],\"radius\",\"dist\"]]]]]]},externals:{\"std.smoothstep\":()=>be}})&&e.f)({})),bJ=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Cs(s);return m(__tsover_mul(t.x,a),t.y).mul(Tt(r)).add(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[10,[6,[7,[6,[7,\"aspectUV\",\"mul\"],[[6,\"std.exp\",[\"scale\"]]]],\"add\"],[\"seed\"]]]]]},externals:{aspectOf:()=>Cs,\"d.vec2f\":()=>m,\"std.exp\":()=>Tt}})&&e.f)({})),xJ=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return ie(t.mul(s).div(r)).add(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"grain\"},{type:\"i\",name:\"seed\"}],body:[0,[[10,[6,[7,[6,\"std.floor\",[[6,[7,[6,[7,\"uv\",\"mul\"],[\"viewport\"]],\"div\"],[\"grain\"]]]],\"add\"],[\"seed\"]]]]]},externals:{\"std.floor\":()=>ie}})&&e.f)({})),AJ=w.fn([m,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return A(t.x,t.y,__tsover_mul(s,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"rate\"}],body:[0,[[10,[6,\"d.vec3f\",[[7,\"pos\",\"x\"],[7,\"pos\",\"y\"],[1,\"t\",\"*\",\"rate\"]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),AT=new WeakMap,wJ=0;function Y3(e){let t=e,s=AT.get(t);if(s!==void 0)return s;let r=wJ++;return AT.set(t,r),r}function Lk(e,t){let{maxOctaves:s,drift:r,stride:o=[17,31],name:a=\"fbmGated\"}=t,i=`${Y3(e)}|${a}|${s}|${r}|${o[0]},${o[1]}`,l=wT.get(i);if(l)return l;let c=s,d=o[0],u=o[1],p=`${r===\"goldenAngle\"?\"Golden\":\"TimeSeed\"}${c}`,_;if(r===\"goldenAngle\"){let f=e;_=w.fn([m,n,n,n,n,m,n],m)((h=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(h.f=((g,y,b,x,P,C,E)=>{\"use gpu\";let R=j(E),U=y,L=n(1),k=n(0),D=n(0);for(let O=0;O<c;O++){let F=n(O),q=pe(__tsover_mul(F,2.3999632297286535)),Y=ae(__tsover_mul(F,2.3999632297286535)),V=m(__tsover_add(__tsover_add(__tsover_mul(g.x,U),__tsover_mul(P,q)),C.x),__tsover_add(__tsover_add(__tsover_mul(g.y,U),__tsover_mul(P,Y)),C.y)),X=N(n(0),n(1),O<R);k=__tsover_add(k,__tsover_mul(__tsover_mul(f(V),L),X)),D=__tsover_add(D,__tsover_mul(L,X)),U=__tsover_mul(U,b),L=__tsover_mul(L,x)}return m(k,D)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"baseFreq\"},{type:\"i\",name:\"lacunarity\"},{type:\"i\",name:\"persistence\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"seedOffset\"},{type:\"i\",name:\"octaveCount\"}],body:[0,[[13,\"n\",[6,\"d.i32\",[\"octaveCount\"]]],[12,\"freq\",\"baseFreq\"],[12,\"weight\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"accumulated\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"totalWeight\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"N\"],[102,\"++\",\"i\"],[0,[[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"dirX\",[6,\"std.cos\",[[1,\"fi\",\"*\",\"GOLDEN\"]]]],[13,\"dirY\",[6,\"std.sin\",[[1,\"fi\",\"*\",\"GOLDEN\"]]]],[13,\"coord\",[6,\"d.vec2f\",[[1,[1,[1,[7,\"base\",\"x\"],\"*\",\"freq\"],\"+\",[1,\"animTime\",\"*\",\"dirX\"]],\"+\",[7,\"seedOffset\",\"x\"]],[1,[1,[1,[7,\"base\",\"y\"],\"*\",\"freq\"],\"+\",[1,\"animTime\",\"*\",\"dirY\"]],\"+\",[7,\"seedOffset\",\"y\"]]]]],[13,\"active\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"i\",\"<\",\"n\"]]]],[2,\"accumulated\",\"=\",[1,\"accumulated\",\"+\",[1,[1,[6,\"octaveField\",[\"coord\"]],\"*\",\"weight\"],\"*\",\"active\"]]],[2,\"totalWeight\",\"=\",[1,\"totalWeight\",\"+\",[1,\"weight\",\"*\",\"active\"]]],[2,\"freq\",\"=\",[1,\"freq\",\"*\",\"lacunarity\"]],[2,\"weight\",\"=\",[1,\"weight\",\"*\",\"persistence\"]]]]],[10,[6,\"d.vec2f\",[\"accumulated\",\"totalWeight\"]]]]]},externals:{\"d.i32\":()=>j,\"d.f32\":()=>n,N:()=>c,\"std.cos\":()=>pe,GOLDEN:()=>2.3999632297286535,\"std.sin\":()=>ae,\"d.vec2f\":()=>m,\"std.select\":()=>N,octaveField:()=>f}})&&h.f)({})).$name(`${a}${p}`)}else{let f=e;_=w.fn([m,n,n,n,n,n,A,n],m)((h=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(h.f=((g,y,b,x,P,C,E,R)=>{\"use gpu\";let U=y,L=n(1),k=n(0),D=n(0);for(let O=0;O<c;O++){let F=n(O),q=N(n(0),n(1),F<R),Y=f(m(__tsover_mul(g.x,U),__tsover_mul(g.y,U)),__tsover_add(P,__tsover_mul(F,d)),__tsover_add(C,__tsover_mul(F,u)),E.x,E.y,E.z);k=__tsover_add(k,__tsover_mul(__tsover_mul(Y,L),q)),D=__tsover_add(D,__tsover_mul(L,q)),L=__tsover_mul(L,x),U=__tsover_mul(U,b)}return m(k,D)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"baseFreq\"},{type:\"i\",name:\"lacunarity\"},{type:\"i\",name:\"persistence\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"extra\"},{type:\"i\",name:\"octaveCount\"}],body:[0,[[12,\"freq\",\"baseFreq\"],[12,\"amp\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"accumulated\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"totalWeight\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"o\",[5,\"0\"]],[1,\"o\",\"<\",\"N\"],[102,\"++\",\"o\"],[0,[[13,\"fo\",[6,\"d.f32\",[\"o\"]]],[13,\"active\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"fo\",\"<\",\"octaveCount\"]]]],[13,\"oUV\",[6,\"d.vec2f\",[[1,[7,\"base\",\"x\"],\"*\",\"freq\"],[1,[7,\"base\",\"y\"],\"*\",\"freq\"]]]],[13,\"animT\",[1,\"animTime\",\"+\",[1,\"fo\",\"*\",\"TIME_STRIDE\"]]],[13,\"seedOff\",[1,\"seed\",\"+\",[1,\"fo\",\"*\",\"SEED_STRIDE\"]]],[13,\"raw\",[6,\"octaveField\",[\"oUV\",\"animT\",\"seedOff\",[7,\"extra\",\"x\"],[7,\"extra\",\"y\"],[7,\"extra\",\"z\"]]]],[2,\"accumulated\",\"=\",[1,\"accumulated\",\"+\",[1,[1,\"raw\",\"*\",\"amp\"],\"*\",\"active\"]]],[2,\"totalWeight\",\"=\",[1,\"totalWeight\",\"+\",[1,\"amp\",\"*\",\"active\"]]],[2,\"amp\",\"=\",[1,\"amp\",\"*\",\"persistence\"]],[2,\"freq\",\"=\",[1,\"freq\",\"*\",\"lacunarity\"]]]]],[10,[6,\"d.vec2f\",[\"accumulated\",\"totalWeight\"]]]]]},externals:{\"d.f32\":()=>n,N:()=>c,\"std.select\":()=>N,\"d.vec2f\":()=>m,TIME_STRIDE:()=>d,SEED_STRIDE:()=>u,octaveField:()=>f}})&&h.f)({})).$name(`${a}${p}`)}return wT.set(i,_),_}var wT=new Map,Uk={q:[[.3,-.2,0],[5.2,1.3,.25]],r:[[1.7,9.2,.15],[8.3,2.8,.13]]};function Ok(e,t={}){let{offsets:s=Uk,name:r=\"domainWarp2\"}=t,o=`${Y3(e)}|${r}|${JSON.stringify(s)}`,a=pv.get(o);if(a)return a;let i=e,[l,c,d]=s.q[0],[u,p,_]=s.q[1],[f,h,g]=s.r[0],[y,b,x]=s.r[1],P=w.fn([A,n,n],A)((C=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(C.f=((E,R,U)=>{\"use gpu\";let L=i(E.add(A(__tsover_mul(R,l),__tsover_mul(R,c),d))),k=i(E.add(A(u,p,__tsover_mul(R,_)))),D=E.add(A(L,k,0).mul(U)),O=i(D.add(A(f,h,__tsover_mul(R,g)))),F=i(D.add(A(y,b,__tsover_mul(R,x))));return E.add(A(O,F,0).mul(U))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"warpAmount\"}],body:[0,[[13,\"qx\",[6,\"warpField\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec3f\",[[1,\"t\",\"*\",\"QX\"],[1,\"t\",\"*\",\"QY\"],\"QZ\"]]]]]]],[13,\"qy\",[6,\"warpField\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec3f\",[\"Q2X\",\"Q2Y\",[1,\"t\",\"*\",\"Q2Z\"]]]]]]]],[13,\"warped1\",[6,[7,\"pos\",\"add\"],[[6,[7,[6,\"d.vec3f\",[\"qx\",\"qy\",[5,\"0\"]]],\"mul\"],[\"warpAmount\"]]]]],[13,\"rx\",[6,\"warpField\",[[6,[7,\"warped1\",\"add\"],[[6,\"d.vec3f\",[\"RX\",\"RY\",[1,\"t\",\"*\",\"RZ\"]]]]]]]],[13,\"ry\",[6,\"warpField\",[[6,[7,\"warped1\",\"add\"],[[6,\"d.vec3f\",[\"R2X\",\"R2Y\",[1,\"t\",\"*\",\"R2Z\"]]]]]]]],[10,[6,[7,\"pos\",\"add\"],[[6,[7,[6,\"d.vec3f\",[\"rx\",\"ry\",[5,\"0\"]]],\"mul\"],[\"warpAmount\"]]]]]]]},externals:{warpField:()=>i,\"d.vec3f\":()=>A,QX:()=>l,QY:()=>c,QZ:()=>d,Q2X:()=>u,Q2Y:()=>p,Q2Z:()=>_,RX:()=>f,RY:()=>h,RZ:()=>g,R2X:()=>y,R2Y:()=>b,R2Z:()=>x}})&&C.f)({})).$name(r);return pv.set(o,P),P}function TJ(e,t={}){let{offsets:s=Uk,name:r=\"domainWarp1\"}=t,o=`1|${Y3(e)}|${r}|${JSON.stringify(s)}`,a=pv.get(o);if(a)return a;let i=e,[l,c,d]=s.q[0],[u,p,_]=s.q[1],f=w.fn([A,n,n],A)((h=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(h.f=((g,y,b)=>{\"use gpu\";let x=i(g.add(A(__tsover_mul(y,l),__tsover_mul(y,c),d))),P=i(g.add(A(u,p,__tsover_mul(y,_))));return g.add(A(x,P,0).mul(b))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"warpAmount\"}],body:[0,[[13,\"qx\",[6,\"warpField\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec3f\",[[1,\"t\",\"*\",\"QX\"],[1,\"t\",\"*\",\"QY\"],\"QZ\"]]]]]]],[13,\"qy\",[6,\"warpField\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec3f\",[\"Q2X\",\"Q2Y\",[1,\"t\",\"*\",\"Q2Z\"]]]]]]]],[10,[6,[7,\"pos\",\"add\"],[[6,[7,[6,\"d.vec3f\",[\"qx\",\"qy\",[5,\"0\"]]],\"mul\"],[\"warpAmount\"]]]]]]]},externals:{warpField:()=>i,\"d.vec3f\":()=>A,QX:()=>l,QY:()=>c,QZ:()=>d,Q2X:()=>u,Q2Y:()=>p,Q2Z:()=>_}})&&h.f)({})).$name(r);return pv.set(o,f),f}var pv=new Map,SJ=w.fn([m,n,m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(t.x,s),t.y),l=m(__tsover_mul(r.x,s),__tsover_sub(1,r.y)),c=he(i.sub(l));return be(o,__tsover_add(o,a),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"falloff\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dist\",[6,\"std.length\",[[6,[7,\"aspectUV\",\"sub\"],[\"centerPos\"]]]]],[10,[6,\"std.smoothstep\",[\"radius\",[1,\"radius\",\"+\",\"falloff\"],\"dist\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.smoothstep\":()=>be}})&&e.f)({})),u0=.11,mv=.6180339887,Mm=8,TT=w.fn([n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_add(__tsover_mul(t,2.3999632297286535),__tsover_mul(Xs(__tsover_add(__tsover_mul(r,.7311),1.618)),Jt)),c=Ue(__tsover_div(__tsover_add(t,.5),s)),d=Pr(m(__tsover_add(__tsover_mul(t,3.77),.917),__tsover_add(__tsover_mul(r,1.093),2.236))).sub(.5).mul(.26),u=Pr(m(__tsover_add(__tsover_mul(t,7.13),1.618),__tsover_add(__tsover_mul(r,.531),3.71))).mul(Jt),p=__tsover_add(.5,__tsover_mul(Xs(__tsover_add(__tsover_add(__tsover_mul(t,5.417),__tsover_mul(r,.291)),.917)),.7)),_=ae(__tsover_add(__tsover_mul(__tsover_mul(i,u0),p),u.x)),f=pe(__tsover_add(__tsover_mul(__tsover_mul(__tsover_mul(i,u0),p),1.37),u.y)),h=__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(pe(l),c),.62),d.x),__tsover_mul(__tsover_mul(_,o),.16)),g=__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(ae(l),c),.62),d.y),__tsover_mul(__tsover_mul(f,o),.16));return m(__tsover_mul(__tsover_add(.5,h),a),__tsover_add(.5,g))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"drift\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"theta\",[1,[1,\"fi\",\"*\",\"GOLDEN\"],\"+\",[1,[6,\"hash11\",[[1,[1,\"seed\",\"*\",[5,\"0.7311\"]],\"+\",[5,\"1.618\"]]]],\"*\",\"TAU\"]]],[13,\"r\",[6,\"std.sqrt\",[[1,[1,\"fi\",\"+\",[5,\"0.5\"]],\"/\",\"count\"]]]],[13,\"j\",[6,[7,[6,[7,[6,\"hash22\",[[6,\"d.vec2f\",[[1,[1,\"fi\",\"*\",[5,\"3.77\"]],\"+\",[5,\"0.917\"]],[1,[1,\"seed\",\"*\",[5,\"1.093\"]],\"+\",[5,\"2.236\"]]]]]],\"sub\"],[[5,\"0.5\"]]],\"mul\"],[[5,\"0.26\"]]]],[13,\"ph\",[6,[7,[6,\"hash22\",[[6,\"d.vec2f\",[[1,[1,\"fi\",\"*\",[5,\"7.13\"]],\"+\",[5,\"1.618\"]],[1,[1,\"seed\",\"*\",[5,\"0.531\"]],\"+\",[5,\"3.71\"]]]]]],\"mul\"],[\"TAU\"]]],[13,\"w1\",[1,[5,\"0.5\"],\"+\",[1,[6,\"hash11\",[[1,[1,[1,\"fi\",\"*\",[5,\"5.417\"]],\"+\",[1,\"seed\",\"*\",[5,\"0.291\"]]],\"+\",[5,\"0.917\"]]]],\"*\",[5,\"0.7\"]]]],[13,\"dx\",[6,\"std.sin\",[[1,[1,[1,\"animTime\",\"*\",\"SCATTER_DRIFT_RATE\"],\"*\",\"w1\"],\"+\",[7,\"ph\",\"x\"]]]]],[13,\"dy\",[6,\"std.cos\",[[1,[1,[1,[1,\"animTime\",\"*\",\"SCATTER_DRIFT_RATE\"],\"*\",\"w1\"],\"*\",[5,\"1.37\"]],\"+\",[7,\"ph\",\"y\"]]]]],[13,\"nx\",[1,[1,[1,[1,[6,\"std.cos\",[\"theta\"]],\"*\",\"r\"],\"*\",[5,\"0.62\"]],\"+\",[7,\"j\",\"x\"]],\"+\",[1,[1,\"dx\",\"*\",\"drift\"],\"*\",[5,\"0.16\"]]]],[13,\"ny\",[1,[1,[1,[1,[6,\"std.sin\",[\"theta\"]],\"*\",\"r\"],\"*\",[5,\"0.62\"]],\"+\",[7,\"j\",\"y\"]],\"+\",[1,[1,\"dy\",\"*\",\"drift\"],\"*\",[5,\"0.16\"]]]],[10,[6,\"d.vec2f\",[[1,[1,[5,\"0.5\"],\"+\",\"nx\"],\"*\",\"aspect\"],[1,[5,\"0.5\"],\"+\",\"ny\"]]]]]]},externals:{GOLDEN:()=>2.3999632297286535,hash11:()=>Xs,TAU:()=>Jt,\"std.sqrt\":()=>Ue,hash22:()=>Pr,\"d.vec2f\":()=>m,\"std.sin\":()=>ae,SCATTER_DRIFT_RATE:()=>u0,\"std.cos\":()=>pe}})&&e.f)({})),PJ=w.fn([m,n,n,Fe(S,Mm/2),n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=n(0),l=n(0),c=Xs(__tsover_add(__tsover_mul(r,.531),.77));for(let d=0;d<Mm;d++){let u=n(d),p=N(n(0),n(1),u<__tsover_sub(s,.5)),_=o[__tsover_div(d,2)],f=N(_.xy,_.zw,(d&1)===1),h=B(he(t.sub(f)),.01),g=__tsover_div(p,vt(h,a)),y=Re(__tsover_add(__tsover_mul(u,mv),c));i=__tsover_add(i,__tsover_mul(g,y)),l=__tsover_add(l,g)}return __tsover_div(i,B(l,1e-6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"anchors\"},{type:\"i\",name:\"power\"}],body:[0,[[12,\"num\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"den\",[6,\"d.f32\",[[5,\"0\"]]]],[13,\"seedShuffle\",[6,\"hash11\",[[1,[1,\"seed\",\"*\",[5,\"0.531\"]],\"+\",[5,\"0.77\"]]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"SCATTER_MAX\"],[102,\"++\",\"i\"],[0,[[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"active\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"fi\",\"<\",[1,\"count\",\"-\",[5,\"0.5\"]]]]]],[13,\"pair\",[8,\"anchors\",[1,\"i\",\"/\",[5,\"2\"]]]],[13,\"anchor\",[6,\"std.select\",[[7,\"pair\",\"xy\"],[7,\"pair\",\"zw\"],[1,[1,\"i\",\"&\",[5,\"1\"]],\"===\",[5,\"1\"]]]]],[13,\"dist\",[6,\"std.max\",[[6,\"std.length\",[[6,[7,\"q\",\"sub\"],[\"anchor\"]]]],[5,\"0.01\"]]]],[13,\"w\",[1,\"active\",\"/\",[6,\"std.pow\",[\"dist\",\"power\"]]]],[13,\"ti\",[6,\"std.fract\",[[1,[1,\"fi\",\"*\",\"GOLDEN_CONJ\"],\"+\",\"seedShuffle\"]]]],[2,\"num\",\"=\",[1,\"num\",\"+\",[1,\"w\",\"*\",\"ti\"]]],[2,\"den\",\"=\",[1,\"den\",\"+\",\"w\"]]]]],[10,[1,\"num\",\"/\",[6,\"std.max\",[\"den\",[5,\"0.000001\"]]]]]]]},externals:{\"d.f32\":()=>n,hash11:()=>Xs,SCATTER_MAX:()=>Mm,\"std.select\":()=>N,\"std.max\":()=>B,\"std.length\":()=>he,\"std.pow\":()=>vt,\"std.fract\":()=>Re,GOLDEN_CONJ:()=>mv}})&&e.f)({})),CJ=w.fn([m,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=n(0),d=n(0);for(let u=0;u<Mm;u++){let p=n(u),_=N(n(0),n(1),p<__tsover_sub(s,.5)),f=TT(p,s,r,o,a,i),h=B(he(t.sub(f)),.01),g=__tsover_div(_,vt(h,l)),y=Re(__tsover_add(__tsover_mul(p,mv),Xs(__tsover_add(__tsover_mul(r,.531),.77))));c=__tsover_add(c,__tsover_mul(g,y)),d=__tsover_add(d,g)}return __tsover_div(c,B(d,1e-6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"drift\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"power\"}],body:[0,[[12,\"num\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"den\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"SCATTER_MAX\"],[102,\"++\",\"i\"],[0,[[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"active\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"fi\",\"<\",[1,\"count\",\"-\",[5,\"0.5\"]]]]]],[13,\"anchor\",[6,\"spiralScatter\",[\"fi\",\"count\",\"seed\",\"drift\",\"aspect\",\"animTime\"]]],[13,\"dist\",[6,\"std.max\",[[6,\"std.length\",[[6,[7,\"q\",\"sub\"],[\"anchor\"]]]],[5,\"0.01\"]]]],[13,\"w\",[1,\"active\",\"/\",[6,\"std.pow\",[\"dist\",\"power\"]]]],[13,\"ti\",[6,\"std.fract\",[[1,[1,\"fi\",\"*\",\"GOLDEN_CONJ\"],\"+\",[6,\"hash11\",[[1,[1,\"seed\",\"*\",[5,\"0.531\"]],\"+\",[5,\"0.77\"]]]]]]]],[2,\"num\",\"=\",[1,\"num\",\"+\",[1,\"w\",\"*\",\"ti\"]]],[2,\"den\",\"=\",[1,\"den\",\"+\",\"w\"]]]]],[10,[1,\"num\",\"/\",[6,\"std.max\",[\"den\",[5,\"0.000001\"]]]]]]]},externals:{\"d.f32\":()=>n,SCATTER_MAX:()=>Mm,\"std.select\":()=>N,spiralScatter:()=>TT,\"std.max\":()=>B,\"std.length\":()=>he,\"std.pow\":()=>vt,\"std.fract\":()=>Re,GOLDEN_CONJ:()=>mv,hash11:()=>Xs}})&&e.f)({})),MJ=w.fn([m,n,n,m,m,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=A(__tsover_mul(t.x,s),__tsover_mul(t.y,s),r),d=Na(c.add(A(o.x,o.y,0))),u=Na(c.add(A(a.x,a.y,0)));return S(__tsover_add(t.x,__tsover_mul(__tsover_mul(d,i),l)),__tsover_add(t.y,__tsover_mul(__tsover_mul(u,i),l)),d,u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"z\"},{type:\"i\",name:\"offA\"},{type:\"i\",name:\"offB\"},{type:\"i\",name:\"k\"},{type:\"i\",name:\"strength\"}],body:[0,[[13,\"wp\",[6,\"d.vec3f\",[[1,[7,\"pos\",\"x\"],\"*\",\"scale\"],[1,[7,\"pos\",\"y\"],\"*\",\"scale\"],\"z\"]]],[13,\"nx\",[6,\"mxNoiseFloat3\",[[6,[7,\"wp\",\"add\"],[[6,\"d.vec3f\",[[7,\"offA\",\"x\"],[7,\"offA\",\"y\"],[5,\"0\"]]]]]]]],[13,\"ny\",[6,\"mxNoiseFloat3\",[[6,[7,\"wp\",\"add\"],[[6,\"d.vec3f\",[[7,\"offB\",\"x\"],[7,\"offB\",\"y\"],[5,\"0\"]]]]]]]],[10,[6,\"d.vec4f\",[[1,[7,\"pos\",\"x\"],\"+\",[1,[1,\"nx\",\"*\",\"k\"],\"*\",\"strength\"]],[1,[7,\"pos\",\"y\"],\"+\",[1,[1,\"ny\",\"*\",\"k\"],\"*\",\"strength\"]],\"nx\",\"ny\"]]]]]},externals:{\"d.vec3f\":()=>A,mxNoiseFloat3:()=>Na,\"d.vec4f\":()=>S}})&&e.f)({})),s1=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Re(ae(m(Se(t,m(127.1,311.7)),Se(t,m(269.5,183.3)))).mul(43758.5453))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"h\",[6,\"d.vec2f\",[[6,\"std.dot\",[\"p\",[6,\"d.vec2f\",[[5,\"127.1\"],[5,\"311.7\"]]]]],[6,\"std.dot\",[\"p\",[6,\"d.vec2f\",[[5,\"269.5\"],[5,\"183.3\"]]]]]]]],[10,[6,\"std.fract\",[[6,[7,[6,\"std.sin\",[\"h\"]],\"mul\"],[[5,\"43758.5453\"]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.dot\":()=>Se,\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({}));function Gk(e){let{distance:t,jitter:s,name:r=\"nearest2Cells\"}=e,o=`${r}|${t}|${s}`,a=ST.get(o);if(a)return a;let i=`${t===\"length\"?\"Len\":\"Sel\"}${s===\"mix\"?\"Jit\":\"\"}`,l;return t===\"length\"&&s===\"none\"?l=w.fn([m,n,n],m)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=ie(d),f=Re(d),h=n(10),g=n(10);for(let y=-1;y<=1;y++)for(let b=-1;b<=1;b++){let x=m(n(b),n(y)),P=s1(m(__tsover_add(__tsover_add(_.x,x.x),p),__tsover_add(__tsover_add(_.y,x.y),p))),C=G(__tsover_add(P.x,__tsover_mul(ae(__tsover_add(u,__tsover_mul(P.x,Jt))),.15)),.05,.95),E=G(__tsover_add(P.y,__tsover_mul(pe(__tsover_add(__tsover_mul(u,.7),__tsover_mul(P.y,Jt))),.15)),.05,.95),R=he(m(__tsover_sub(f.x,__tsover_add(x.x,C)),__tsover_sub(f.y,__tsover_add(x.y,E))));g=ye(g,B(R,h)),h=ye(h,R)}return m(h,g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"animT\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"cell\",[6,\"std.floor\",[\"scaledUV\"]]],[13,\"localUV\",[6,\"std.fract\",[\"scaledUV\"]]],[12,\"d1\",[6,\"d.f32\",[[5,\"10\"]]]],[12,\"d2\",[6,\"d.f32\",[[5,\"10\"]]]],[14,[12,\"ny\",[4,\"-\",[5,\"1\"]]],[1,\"ny\",\"<=\",[5,\"1\"]],[102,\"++\",\"ny\"],[0,[[14,[12,\"nx\",[4,\"-\",[5,\"1\"]]],[1,\"nx\",\"<=\",[5,\"1\"]],[102,\"++\",\"nx\"],[0,[[13,\"offset\",[6,\"d.vec2f\",[[6,\"d.f32\",[\"nx\"]],[6,\"d.f32\",[\"ny\"]]]]],[13,\"h\",[6,\"cellHash2\",[[6,\"d.vec2f\",[[1,[1,[7,\"cell\",\"x\"],\"+\",[7,\"offset\",\"x\"]],\"+\",\"seed\"],[1,[1,[7,\"cell\",\"y\"],\"+\",[7,\"offset\",\"y\"]],\"+\",\"seed\"]]]]]],[13,\"pointX\",[6,\"std.clamp\",[[1,[7,\"h\",\"x\"],\"+\",[1,[6,\"std.sin\",[[1,\"animT\",\"+\",[1,[7,\"h\",\"x\"],\"*\",\"TAU\"]]]],\"*\",[5,\"0.15\"]]],[5,\"0.05\"],[5,\"0.95\"]]]],[13,\"pointY\",[6,\"std.clamp\",[[1,[7,\"h\",\"y\"],\"+\",[1,[6,\"std.cos\",[[1,[1,\"animT\",\"*\",[5,\"0.7\"]],\"+\",[1,[7,\"h\",\"y\"],\"*\",\"TAU\"]]]],\"*\",[5,\"0.15\"]]],[5,\"0.05\"],[5,\"0.95\"]]]],[13,\"dd\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"localUV\",\"x\"],\"-\",[1,[7,\"offset\",\"x\"],\"+\",\"pointX\"]],[1,[7,\"localUV\",\"y\"],\"-\",[1,[7,\"offset\",\"y\"],\"+\",\"pointY\"]]]]]]],[2,\"d2\",\"=\",[6,\"std.min\",[\"d2\",[6,\"std.max\",[\"dd\",\"d1\"]]]]],[2,\"d1\",\"=\",[6,\"std.min\",[\"d1\",\"dd\"]]]]]]]]],[10,[6,\"d.vec2f\",[\"d1\",\"d2\"]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"d.f32\":()=>n,\"d.vec2f\":()=>m,cellHash2:()=>s1,\"std.clamp\":()=>G,\"std.sin\":()=>ae,TAU:()=>Jt,\"std.cos\":()=>pe,\"std.length\":()=>he,\"std.min\":()=>ye,\"std.max\":()=>B}})&&c.f)({})).$name(`${r}${i}`):l=w.fn([m,n,n,n,n],m)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p,_,f)=>{\"use gpu\";let h=__tsover_mul(u,.7),g=ie(d),y=Re(d),b=n(10),x=n(10);for(let P=-1;P<=1;P++)for(let C=-1;C<=1;C++){let E=m(n(C),n(P)),R=s1(m(__tsover_add(__tsover_add(g.x,E.x),p),__tsover_add(__tsover_add(g.y,E.y),p))),U=__tsover_add(R.x,__tsover_mul(ae(__tsover_add(u,__tsover_mul(R.x,Jt))),.15)),L=__tsover_add(R.y,__tsover_mul(pe(__tsover_add(h,__tsover_mul(R.y,Jt))),.15)),k=K(.5,G(U,.05,.95),_),D=K(.5,G(L,.05,.95),_),O=__tsover_sub(y.x,__tsover_add(E.x,k)),F=__tsover_sub(y.y,__tsover_add(E.y,D)),q=__tsover_add(__tsover_mul(O,O),__tsover_mul(F,F)),Y=__tsover_add(re(O),re(F)),V=B(re(O),re(F)),X=q;X=N(X,Y,f===1),X=N(X,V,f===2),x=ye(x,B(X,b)),b=ye(b,X)}return m(b,x)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"animT\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"jitterAmount\"},{type:\"i\",name:\"metric\"}],body:[0,[[13,\"animTSlow\",[1,\"animT\",\"*\",[5,\"0.7\"]]],[13,\"cell\",[6,\"std.floor\",[\"scaledUV\"]]],[13,\"localUV\",[6,\"std.fract\",[\"scaledUV\"]]],[12,\"d1\",[6,\"d.f32\",[[5,\"10\"]]]],[12,\"d2\",[6,\"d.f32\",[[5,\"10\"]]]],[14,[12,\"ny\",[4,\"-\",[5,\"1\"]]],[1,\"ny\",\"<=\",[5,\"1\"]],[102,\"++\",\"ny\"],[0,[[14,[12,\"nx\",[4,\"-\",[5,\"1\"]]],[1,\"nx\",\"<=\",[5,\"1\"]],[102,\"++\",\"nx\"],[0,[[13,\"offset\",[6,\"d.vec2f\",[[6,\"d.f32\",[\"nx\"]],[6,\"d.f32\",[\"ny\"]]]]],[13,\"h\",[6,\"cellHash2\",[[6,\"d.vec2f\",[[1,[1,[7,\"cell\",\"x\"],\"+\",[7,\"offset\",\"x\"]],\"+\",\"seed\"],[1,[1,[7,\"cell\",\"y\"],\"+\",[7,\"offset\",\"y\"]],\"+\",\"seed\"]]]]]],[13,\"px\",[1,[7,\"h\",\"x\"],\"+\",[1,[6,\"std.sin\",[[1,\"animT\",\"+\",[1,[7,\"h\",\"x\"],\"*\",\"TAU\"]]]],\"*\",[5,\"0.15\"]]]],[13,\"py\",[1,[7,\"h\",\"y\"],\"+\",[1,[6,\"std.cos\",[[1,\"animTSlow\",\"+\",[1,[7,\"h\",\"y\"],\"*\",\"TAU\"]]]],\"*\",[5,\"0.15\"]]]],[13,\"jx\",[6,\"std.mix\",[[5,\"0.5\"],[6,\"std.clamp\",[\"px\",[5,\"0.05\"],[5,\"0.95\"]]],\"jitterAmount\"]]],[13,\"jy\",[6,\"std.mix\",[[5,\"0.5\"],[6,\"std.clamp\",[\"py\",[5,\"0.05\"],[5,\"0.95\"]]],\"jitterAmount\"]]],[13,\"deltaX\",[1,[7,\"localUV\",\"x\"],\"-\",[1,[7,\"offset\",\"x\"],\"+\",\"jx\"]]],[13,\"deltaY\",[1,[7,\"localUV\",\"y\"],\"-\",[1,[7,\"offset\",\"y\"],\"+\",\"jy\"]]],[13,\"dEuclid\",[1,[1,\"deltaX\",\"*\",\"deltaX\"],\"+\",[1,\"deltaY\",\"*\",\"deltaY\"]]],[13,\"dManhattan\",[1,[6,\"std.abs\",[\"deltaX\"]],\"+\",[6,\"std.abs\",[\"deltaY\"]]]],[13,\"dChebyshev\",[6,\"std.max\",[[6,\"std.abs\",[\"deltaX\"]],[6,\"std.abs\",[\"deltaY\"]]]]],[12,\"dd\",\"dEuclid\"],[2,\"dd\",\"=\",[6,\"std.select\",[\"dd\",\"dManhattan\",[1,\"metric\",\"===\",[5,\"1\"]]]]],[2,\"dd\",\"=\",[6,\"std.select\",[\"dd\",\"dChebyshev\",[1,\"metric\",\"===\",[5,\"2\"]]]]],[2,\"d2\",\"=\",[6,\"std.min\",[\"d2\",[6,\"std.max\",[\"dd\",\"d1\"]]]]],[2,\"d1\",\"=\",[6,\"std.min\",[\"d1\",\"dd\"]]]]]]]]],[10,[6,\"d.vec2f\",[\"d1\",\"d2\"]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"d.f32\":()=>n,\"d.vec2f\":()=>m,cellHash2:()=>s1,\"std.sin\":()=>ae,TAU:()=>Jt,\"std.cos\":()=>pe,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.select\":()=>N,\"std.min\":()=>ye}})&&c.f)({})).$name(`${r}${i}`),ST.set(o,l),l}var ST=new Map,EJ={f1:0,f2:1,f2MinusF1:2,f1PlusF2:3,f1TimesF2:4},kJ={euclidean:0,manhattan:1,chebyshev:2},PT=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=r===0,i=N(t,Ue(t),a),l=N(s,Ue(s),a),c=i;return c=N(c,l,o===1),c=N(c,__tsover_sub(l,i),o===2),c=N(c,__tsover_add(i,l),o===3),c=N(c,__tsover_mul(i,l),o===4),c}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"d1\"},{type:\"i\",name:\"d2\"},{type:\"i\",name:\"metric\"},{type:\"i\",name:\"mode\"}],body:[0,[[13,\"isEuclid\",[1,\"metric\",\"===\",[5,\"0\"]]],[13,\"sd1\",[6,\"std.select\",[\"d1\",[6,\"std.sqrt\",[\"d1\"]],\"isEuclid\"]]],[13,\"sd2\",[6,\"std.select\",[\"d2\",[6,\"std.sqrt\",[\"d2\"]],\"isEuclid\"]]],[12,\"raw\",\"sd1\"],[2,\"raw\",\"=\",[6,\"std.select\",[\"raw\",\"sd2\",[1,\"mode\",\"===\",[5,\"1\"]]]]],[2,\"raw\",\"=\",[6,\"std.select\",[\"raw\",[1,\"sd2\",\"-\",\"sd1\"],[1,\"mode\",\"===\",[5,\"2\"]]]]],[2,\"raw\",\"=\",[6,\"std.select\",[\"raw\",[1,\"sd1\",\"+\",\"sd2\"],[1,\"mode\",\"===\",[5,\"3\"]]]]],[2,\"raw\",\"=\",[6,\"std.select\",[\"raw\",[1,\"sd1\",\"*\",\"sd2\"],[1,\"mode\",\"===\",[5,\"4\"]]]]],[10,\"raw\"]]]},externals:{\"std.select\":()=>N,\"std.sqrt\":()=>Ue}})&&e.f)({})),CT=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(__tsover_add(t,s),1e-4);return G(vt(G(__tsover_div(__tsover_mul(t,2),o),0,1),r),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"d1\"},{type:\"i\",name:\"d2\"},{type:\"i\",name:\"power\"}],body:[0,[[13,\"safeSum\",[6,\"std.max\",[[1,\"d1\",\"+\",\"d2\"],[5,\"0.0001\"]]]],[13,\"normalizedDist\",[6,\"std.clamp\",[[1,[1,\"d1\",\"*\",[5,\"2\"]],\"/\",\"safeSum\"],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"std.clamp\",[[6,\"std.pow\",[\"normalizedDist\",\"power\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.pow\":()=>vt}})&&e.f)({})),MT=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(__tsover_add(t,s),1e-4),a=__tsover_div(__tsover_sub(s,t),o);return be(0,__tsover_add(r,.001),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"d1\"},{type:\"i\",name:\"d2\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"safeSum\",[6,\"std.max\",[[1,\"d1\",\"+\",\"d2\"],[5,\"0.0001\"]]]],[13,\"edgeMetric\",[1,[1,\"d2\",\"-\",\"d1\"],\"/\",\"safeSum\"]],[10,[6,\"std.smoothstep\",[[5,\"0\"],[1,\"softness\",\"+\",[5,\"0.001\"]],\"edgeMetric\"]]]]]},externals:{\"std.max\":()=>B,\"std.smoothstep\":()=>be}})&&e.f)({})),ET=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Tt(-__tsover_mul(__tsover_mul(t,t),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"sharpness\"}],body:[0,[[10,[6,\"std.exp\",[[4,\"-\",[1,[1,\"dist\",\"*\",\"dist\"],\"*\",\"sharpness\"]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})),D1=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return Tt(__tsover_mul(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s)),-r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"du\"},{type:\"i\",name:\"dv\"},{type:\"i\",name:\"sharpness\"}],body:[0,[[10,[6,\"std.exp\",[[1,[1,[1,\"du\",\"*\",\"du\"],\"+\",[1,\"dv\",\"*\",\"dv\"]],\"*\",[4,\"-\",\"sharpness\"]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})),BJ=w.fn([m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(__tsover_sub(t.x,r),s),l=__tsover_sub(t.y,o),c=__tsover_add(__tsover_mul(i,i),__tsover_mul(l,l));return Tt(__tsover_div(__tsover_mul(-.5,c),a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"lx\"},{type:\"i\",name:\"ly\"},{type:\"i\",name:\"sizeSq\"}],body:[0,[[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"lx\"],\"*\",\"aspect\"]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",\"ly\"]],[13,\"dd2\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[10,[6,\"std.exp\",[[1,[1,[4,\"-\",[5,\"0.5\"]],\"*\",\"dd2\"],\"/\",\"sizeSq\"]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})),kT=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return vt(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(t,s),r)),.5),.5),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angle\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"sharpness\"}],body:[0,[[13,\"t\",[1,[1,[6,\"std.sin\",[[1,[1,\"angle\",\"*\",\"count\"],\"+\",\"phase\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[10,[6,\"std.pow\",[\"t\",\"sharpness\"]]]]]},externals:{\"std.sin\":()=>ae,\"std.pow\":()=>vt}})&&e.f)({})),p0=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return vt(re(pe(__tsover_mul(t,s))),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angle\"},{type:\"i\",name:\"halfCount\"},{type:\"i\",name:\"power\"}],body:[0,[[10,[6,\"std.pow\",[[6,\"std.abs\",[[6,\"std.cos\",[[1,\"angle\",\"*\",\"halfCount\"]]]]],\"power\"]]]]]},externals:{\"std.pow\":()=>vt,\"std.abs\":()=>re,\"std.cos\":()=>pe}})&&e.f)({})),BT=w.fn([m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Xt(t.y,t.x);return A(s,__tsover_sub(s,__tsover_mul(Jt,ie(__tsover_div(s,Jt)))),be(-.15,.15,t.x))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"delta\"}],body:[0,[[13,\"angle\",[6,\"std.atan2\",[[7,\"delta\",\"y\"],[7,\"delta\",\"x\"]]]],[13,\"angleWrapped\",[1,\"angle\",\"-\",[1,\"TAU\",\"*\",[6,\"std.floor\",[[1,\"angle\",\"/\",\"TAU\"]]]]]],[13,\"blend\",[6,\"std.smoothstep\",[[4,\"-\",[5,\"0.15\"]],[5,\"0.15\"],[7,\"delta\",\"x\"]]]],[10,[6,\"d.vec3f\",[\"angle\",\"angleWrapped\",\"blend\"]]]]]},externals:{\"std.atan2\":()=>Xt,TAU:()=>Jt,\"std.floor\":()=>ie,\"std.smoothstep\":()=>be,\"d.vec3f\":()=>A}})&&e.f)({})),_v=w.fn([n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=B(o,1e-4);return A(__tsover_sub(1,be(0,a,re(__tsover_sub(t,__tsover_add(s,r))))),__tsover_sub(1,be(0,a,re(__tsover_sub(t,s)))),__tsover_sub(1,be(0,a,re(__tsover_sub(t,__tsover_sub(s,r))))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"spread\"},{type:\"i\",name:\"width\"}],body:[0,[[13,\"w\",[6,\"std.max\",[\"width\",[5,\"0.0001\"]]]],[13,\"r\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],\"w\",[6,\"std.abs\",[[1,\"dist\",\"-\",[1,\"center\",\"+\",\"spread\"]]]]]]]],[13,\"g\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],\"w\",[6,\"std.abs\",[[1,\"dist\",\"-\",\"center\"]]]]]]],[13,\"b\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],\"w\",[6,\"std.abs\",[[1,\"dist\",\"-\",[1,\"center\",\"-\",\"spread\"]]]]]]]],[10,[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]]]]]},externals:{\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"std.abs\":()=>re,\"d.vec3f\":()=>A}})&&e.f)({})),zT=w.fn([m,m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=m(__tsover_sub(r.x,s.x),__tsover_sub(r.y,s.y)),a=m(__tsover_sub(t.x,s.x),__tsover_sub(t.y,s.y)),i=__tsover_add(__tsover_mul(a.x,o.x),__tsover_mul(a.y,o.y)),l=__tsover_add(__tsover_mul(o.x,o.x),__tsover_mul(o.y,o.y)),c=G(__tsover_div(i,B(l,1e-4)),0,1),d=m(__tsover_add(s.x,__tsover_mul(o.x,c)),__tsover_add(s.y,__tsover_mul(o.y,c))),u=m(__tsover_sub(t.x,d.x),__tsover_sub(t.y,d.y));return m(c,Ue(__tsover_add(__tsover_mul(u.x,u.x),__tsover_mul(u.y,u.y))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"}],body:[0,[[13,\"lineVec\",[6,\"d.vec2f\",[[1,[7,\"b\",\"x\"],\"-\",[7,\"a\",\"x\"]],[1,[7,\"b\",\"y\"],\"-\",[7,\"a\",\"y\"]]]]],[13,\"toPoint\",[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"-\",[7,\"a\",\"x\"]],[1,[7,\"p\",\"y\"],\"-\",[7,\"a\",\"y\"]]]]],[13,\"dotProduct\",[1,[1,[7,\"toPoint\",\"x\"],\"*\",[7,\"lineVec\",\"x\"]],\"+\",[1,[7,\"toPoint\",\"y\"],\"*\",[7,\"lineVec\",\"y\"]]]],[13,\"lineLengthSq\",[1,[1,[7,\"lineVec\",\"x\"],\"*\",[7,\"lineVec\",\"x\"]],\"+\",[1,[7,\"lineVec\",\"y\"],\"*\",[7,\"lineVec\",\"y\"]]]],[13,\"t\",[6,\"std.clamp\",[[1,\"dotProduct\",\"/\",[6,\"std.max\",[\"lineLengthSq\",[5,\"0.0001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"closestPoint\",[6,\"d.vec2f\",[[1,[7,\"a\",\"x\"],\"+\",[1,[7,\"lineVec\",\"x\"],\"*\",\"t\"]],[1,[7,\"a\",\"y\"],\"+\",[1,[7,\"lineVec\",\"y\"],\"*\",\"t\"]]]]],[13,\"distVec\",[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"-\",[7,\"closestPoint\",\"x\"]],[1,[7,\"p\",\"y\"],\"-\",[7,\"closestPoint\",\"y\"]]]]],[10,[6,\"d.vec2f\",[\"t\",[6,\"std.sqrt\",[[1,[1,[7,\"distVec\",\"x\"],\"*\",[7,\"distVec\",\"x\"]],\"+\",[1,[7,\"distVec\",\"y\"],\"*\",[7,\"distVec\",\"y\"]]]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"std.max\":()=>B,\"std.sqrt\":()=>Ue}})&&e.f)({})),RT=w.fn([n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=K(r,o,s),c=B(K(a,i,s),1e-4),d=__tsover_div(t,B(l,1e-4)),u=__tsover_add(1,c),p=vt(__tsover_sub(1,be(1,u,d)),__tsover_add(1,__tsover_mul(c,1.5)));return m(be(__tsover_sub(1,c),__tsover_add(1,c),d),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"startThickness\"},{type:\"i\",name:\"endThickness\"},{type:\"i\",name:\"startSoftness\"},{type:\"i\",name:\"endSoftness\"}],body:[0,[[13,\"thickness\",[6,\"std.mix\",[\"startThickness\",\"endThickness\",\"t\"]]],[13,\"softness\",[6,\"std.max\",[[6,\"std.mix\",[\"startSoftness\",\"endSoftness\",\"t\"]],[5,\"0.0001\"]]]],[13,\"normalizedDist\",[1,\"dist\",\"/\",[6,\"std.max\",[\"thickness\",[5,\"0.0001\"]]]]],[13,\"edgeEnd\",[1,[5,\"1\"],\"+\",\"softness\"]],[13,\"alphaBase\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"1\"],\"edgeEnd\",\"normalizedDist\"]]]],[13,\"softnessFactor\",[1,[5,\"1\"],\"+\",[1,\"softness\",\"*\",[5,\"1.5\"]]]],[13,\"alpha\",[6,\"std.pow\",[\"alphaBase\",\"softnessFactor\"]]],[13,\"colorGradientStart\",[1,[5,\"1\"],\"-\",\"softness\"]],[13,\"colorGradientEnd\",[1,[5,\"1\"],\"+\",\"softness\"]],[13,\"colorT\",[6,\"std.smoothstep\",[\"colorGradientStart\",\"colorGradientEnd\",\"normalizedDist\"]]],[10,[6,\"d.vec2f\",[\"colorT\",\"alpha\"]]]]]},externals:{\"std.mix\":()=>K,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"std.pow\":()=>vt,\"d.vec2f\":()=>m}})&&e.f)({})),m0=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return Tt(__tsover_div(__tsover_mul(__tsover_mul(__tsover_sub(t,s),__tsover_sub(t,s)),-1),__tsover_mul(r,r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"u\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"width\"}],body:[0,[[10,[6,\"std.exp\",[[1,[1,[1,[1,\"u\",\"-\",\"center\"],\"*\",[1,\"u\",\"-\",\"center\"]],\"*\",[4,\"-\",[5,\"1\"]]],\"/\",[1,\"width\",\"*\",\"width\"]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})),r1=w.fn([n,A,A,A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=be(0,.5,t),i=be(.55,1.15,t);return K(K(o,r,A(a)),s,A(i)).mul(G(t,0,1.6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"heat\"},{type:\"i\",name:\"hot\"},{type:\"i\",name:\"mid\"},{type:\"i\",name:\"fringe\"}],body:[0,[[13,\"toMid\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.5\"],\"heat\"]]],[13,\"toHot\",[6,\"std.smoothstep\",[[5,\"0.55\"],[5,\"1.15\"],\"heat\"]]],[13,\"rgb\",[6,\"std.mix\",[[6,\"std.mix\",[\"fringe\",\"mid\",[6,\"d.vec3f\",[\"toMid\"]]]],\"hot\",[6,\"d.vec3f\",[\"toHot\"]]]]],[10,[6,[7,\"rgb\",\"mul\"],[[6,\"std.clamp\",[\"heat\",[5,\"0\"],[5,\"1.6\"]]]]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.mix\":()=>K,\"d.vec3f\":()=>A,\"std.clamp\":()=>G}})&&e.f)({})),zJ=w.fn([n,A,A,A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=r1(__tsover_mul(t,1.08),s,r,o),i=r1(t,s,r,o),l=r1(__tsover_mul(t,.88),s,r,o);return A(a.x,i.y,l.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"heat\"},{type:\"i\",name:\"hot\"},{type:\"i\",name:\"mid\"},{type:\"i\",name:\"fringe\"}],body:[0,[[13,\"rampR\",[6,\"heatRamp3\",[[1,\"heat\",\"*\",[5,\"1.08\"]],\"hot\",\"mid\",\"fringe\"]]],[13,\"rampG\",[6,\"heatRamp3\",[\"heat\",\"hot\",\"mid\",\"fringe\"]]],[13,\"rampB\",[6,\"heatRamp3\",[[1,\"heat\",\"*\",[5,\"0.88\"]],\"hot\",\"mid\",\"fringe\"]]],[10,[6,\"d.vec3f\",[[7,\"rampR\",\"x\"],[7,\"rampG\",\"y\"],[7,\"rampB\",\"z\"]]]]]]},externals:{heatRamp3:()=>r1,\"d.vec3f\":()=>A}})&&e.f)({})),RJ=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s,__tsover_sub(1,r));return __tsover_sub(1,be(o,s,t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"feather\"}],body:[0,[[13,\"falloffStart\",[1,\"radius\",\"*\",[1,[5,\"1\"],\"-\",\"feather\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"falloffStart\",\"radius\",\"dist\"]]]]]]},externals:{\"std.smoothstep\":()=>be}})&&e.f)({})),DJ=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return kT(t,s,r,__tsover_mul(__tsover_div(1,__tsover_add(o,.05)),.3))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angle\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"sharpness\",[1,[1,[5,\"1\"],\"/\",[1,\"softness\",\"+\",[5,\"0.05\"]]],\"*\",[5,\"0.3\"]]],[10,[6,\"angularSineLobes\",[\"angle\",\"count\",\"phase\",\"sharpness\"]]]]]},externals:{angularSineLobes:()=>kT}})&&e.f)({})),LJ=w.fn([m,m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s),a=r.x,i=__tsover_sub(1,r.y),l=__tsover_sub(__tsover_mul(t.x,o),__tsover_mul(a,o)),c=__tsover_sub(t.y,i);return m(Ue(__tsover_add(__tsover_mul(l,l),__tsover_mul(c,c))),Xt(c,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"center\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"cx\",[7,\"center\",\"x\"]],[13,\"cy\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,\"cx\",\"*\",\"aspect\"]]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",\"cy\"]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[13,\"angle\",[6,\"std.atan2\",[\"dy\",\"dx\"]]],[10,[6,\"d.vec2f\",[\"dist\",\"angle\"]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"std.sqrt\":()=>Ue,\"std.atan2\":()=>Xt,\"d.vec2f\":()=>m}})&&e.f)({})),UJ=w.fn([m,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s),a=__tsover_mul(__tsover_sub(t.x,.5),o),i=__tsover_sub(t.y,.5),l=__tsover_add(__tsover_mul(a,a),__tsover_mul(i,i));return G(__tsover_sub(1,__tsover_mul(l,r)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"strength\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"vigX\",[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"]],[13,\"vigY\",[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]],[13,\"vigD2\",[1,[1,\"vigX\",\"*\",\"vigX\"],\"+\",[1,\"vigY\",\"*\",\"vigY\"]]],[10,[6,\"std.clamp\",[[1,[5,\"1\"],\"-\",[1,\"vigD2\",\"*\",\"strength\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"std.clamp\":()=>G}})&&e.f)({})),OJ=w.fn([S,A],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t.xyz,o=G(A(1,1,1).sub(A(1,1,1).sub(G(r,A(0),A(1))).mul(A(1,1,1).sub(G(s,A(0),A(1))))).add(s.mul(.16)),A(0),A(1)),a=G(rv(s),0,1);return S(o,G(__tsover_add(t.w,__tsover_mul(a,.85)),0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"leakRgb\"}],body:[0,[[13,\"base\",[7,\"color\",\"xyz\"]],[13,\"screened\",[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"sub\"],[[6,[7,[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"sub\"],[[6,\"std.clamp\",[\"base\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]]],\"mul\"],[[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"sub\"],[[6,\"std.clamp\",[\"leakRgb\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]]]]]]]],[13,\"lifted\",[6,[7,\"screened\",\"add\"],[[6,[7,\"leakRgb\",\"mul\"],[[5,\"0.16\"]]]]]],[13,\"outRgb\",[6,\"std.clamp\",[\"lifted\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[13,\"leakLuma\",[6,\"std.clamp\",[[6,\"tone.luma601Dot\",[\"leakRgb\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"outA\",[6,\"std.clamp\",[[1,[7,\"color\",\"w\"],\"+\",[1,\"leakLuma\",\"*\",[5,\"0.85\"]]],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[\"outRgb\",\"outA\"]]]]]},externals:{\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"tone.luma601Dot\":()=>rv,\"d.vec4f\":()=>S}})&&e.f)({})),GJ=w.fn([m,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=m(__tsover_mul(t.x,s),t.y),a=m(__tsover_mul(r.x,s),__tsover_sub(1,r.y)),i=m(__tsover_mul(s,.5),.5).sub(a),l=he(i),c=N(__tsover_div(i.x,B(l,.001)),n(1),l<.02),d=N(__tsover_div(i.y,B(l,.001)),n(0),l<.02),u=__tsover_sub(o.x,a.x),p=__tsover_sub(o.y,a.y);return m(__tsover_add(__tsover_mul(u,c),__tsover_mul(p,d)),__tsover_sub(__tsover_mul(p,c),__tsover_mul(u,d)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"anchorRaw\"}],body:[0,[[13,\"pA\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"anchor\",[6,\"d.vec2f\",[[1,[7,\"anchorRaw\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"anchorRaw\",\"y\"]]]]],[13,\"toCenter\",[6,[7,[6,\"d.vec2f\",[[1,\"aspect\",\"*\",[5,\"0.5\"]],[5,\"0.5\"]]],\"sub\"],[\"anchor\"]]],[13,\"cLen\",[6,\"std.length\",[\"toCenter\"]]],[13,\"dirX\",[6,\"std.select\",[[1,[7,\"toCenter\",\"x\"],\"/\",[6,\"std.max\",[\"cLen\",[5,\"0.001\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"cLen\",\"<\",[5,\"0.02\"]]]]],[13,\"dirY\",[6,\"std.select\",[[1,[7,\"toCenter\",\"y\"],\"/\",[6,\"std.max\",[\"cLen\",[5,\"0.001\"]]]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"cLen\",\"<\",[5,\"0.02\"]]]]],[13,\"relX\",[1,[7,\"pA\",\"x\"],\"-\",[7,\"anchor\",\"x\"]]],[13,\"relY\",[1,[7,\"pA\",\"y\"],\"-\",[7,\"anchor\",\"y\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"relX\",\"*\",\"dirX\"],\"+\",[1,\"relY\",\"*\",\"dirY\"]],[1,[1,\"relY\",\"*\",\"dirX\"],\"-\",[1,\"relX\",\"*\",\"dirY\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.select\":()=>N,\"std.max\":()=>B,\"d.f32\":()=>n}})&&e.f)({})),Mn=ko({MixStep:()=>Xa,convertMixSpaceToP3:()=>YJ,convertP3ToMixSpace:()=>IJ,convertP3ToMixSpaceCPU:()=>fl,hslToRgb:()=>ml,hsvToRgb:()=>hd,labToLch:()=>Bm,labToRgb:()=>gd,lchToLab:()=>yd,mixColors:()=>FJ,mixColorsHsl:()=>Yk,mixColorsHsv:()=>Wk,mixColorsLch:()=>qk,mixColorsLinear:()=>Ar,mixColorsOklab:()=>Ik,mixColorsOklch:()=>Fk,mixColorsVariants:()=>No,mixPreconvertedColors:()=>WJ,mixPreconvertedHsl:()=>$k,mixPreconvertedHsv:()=>Xk,mixPreconvertedInSpace:()=>_l,mixPreconvertedLch:()=>Hk,mixPreconvertedLinear:()=>hc,mixPreconvertedOklab:()=>Nk,mixPreconvertedOklch:()=>Vk,mixPreconvertedVariants:()=>Bd,mixToward:()=>Kk,oklabToOklch:()=>Zl,oklabToRgb:()=>oa,oklchToOklab:()=>pl,p3ToSRGB:()=>Cr,rgbToHsl:()=>cd,rgbToHsv:()=>Em,rgbToLab:()=>km,rgbToOklab:()=>Ta,sRGBToP3:()=>Hs,selectRGBBySector:()=>$m}),bi=1e-4,uc=1.0471975512,Zn=6/29,fv=500,vv=200,o1=Zn*Zn*Zn,jl=4/29,a1=1/(3*Zn*Zn),n1=3*Zn*Zn,Cr=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return A(__tsover_sub(__tsover_sub(__tsover_mul(t.x,1.2249401),__tsover_mul(t.y,.2249404)),__tsover_mul(t.z,0)),__tsover_add(__tsover_add(__tsover_mul(t.x,-.0420569),__tsover_mul(t.y,1.0420571)),__tsover_mul(t.z,0)),__tsover_add(__tsover_sub(__tsover_mul(t.x,-.0196376),__tsover_mul(t.y,.0786361)),__tsover_mul(t.z,1.0982735)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p3\"}],body:[0,[[13,\"r\",[1,[1,[1,[7,\"p3\",\"x\"],\"*\",[5,\"1.2249401\"]],\"-\",[1,[7,\"p3\",\"y\"],\"*\",[5,\"0.2249404\"]]],\"-\",[1,[7,\"p3\",\"z\"],\"*\",[5,\"0\"]]]],[13,\"g\",[1,[1,[1,[7,\"p3\",\"x\"],\"*\",[4,\"-\",[5,\"0.0420569\"]]],\"+\",[1,[7,\"p3\",\"y\"],\"*\",[5,\"1.0420571\"]]],\"+\",[1,[7,\"p3\",\"z\"],\"*\",[5,\"0\"]]]],[13,\"b\",[1,[1,[1,[7,\"p3\",\"x\"],\"*\",[4,\"-\",[5,\"0.0196376\"]]],\"-\",[1,[7,\"p3\",\"y\"],\"*\",[5,\"0.0786361\"]]],\"+\",[1,[7,\"p3\",\"z\"],\"*\",[5,\"1.0982735\"]]]],[10,[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),Hs=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return A(__tsover_add(__tsover_add(__tsover_mul(t.x,.8224621),__tsover_mul(t.y,.177538)),__tsover_mul(t.z,0)),__tsover_add(__tsover_add(__tsover_mul(t.x,.0331941),__tsover_mul(t.y,.9668058)),__tsover_mul(t.z,0)),__tsover_add(__tsover_add(__tsover_mul(t.x,.0170826),__tsover_mul(t.y,.0723974)),__tsover_mul(t.z,.9105199)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"srgb\"}],body:[0,[[13,\"r\",[1,[1,[1,[7,\"srgb\",\"x\"],\"*\",[5,\"0.8224621\"]],\"+\",[1,[7,\"srgb\",\"y\"],\"*\",[5,\"0.177538\"]]],\"+\",[1,[7,\"srgb\",\"z\"],\"*\",[5,\"0\"]]]],[13,\"g\",[1,[1,[1,[7,\"srgb\",\"x\"],\"*\",[5,\"0.0331941\"]],\"+\",[1,[7,\"srgb\",\"y\"],\"*\",[5,\"0.9668058\"]]],\"+\",[1,[7,\"srgb\",\"z\"],\"*\",[5,\"0\"]]]],[13,\"b\",[1,[1,[1,[7,\"srgb\",\"x\"],\"*\",[5,\"0.0170826\"]],\"+\",[1,[7,\"srgb\",\"y\"],\"*\",[5,\"0.0723974\"]]],\"+\",[1,[7,\"srgb\",\"z\"],\"*\",[5,\"0.9105199\"]]]],[10,[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),Ta=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_add(__tsover_add(__tsover_mul(s,.4122214708),__tsover_mul(r,.5363325363)),__tsover_mul(o,.0514459929)),i=__tsover_add(__tsover_mul(s,.2119034982),__tsover_add(__tsover_mul(r,.6806995451),__tsover_mul(o,.1073969566))),l=__tsover_add(__tsover_add(__tsover_mul(s,.0883024619),__tsover_mul(r,.2817188376)),__tsover_mul(o,.6299787005)),c=__tsover_mul(no(a),vt(re(a),__tsover_div(1,3))),d=__tsover_mul(no(i),vt(re(i),__tsover_div(1,3))),u=__tsover_mul(no(l),vt(re(l),__tsover_div(1,3)));return A(__tsover_sub(__tsover_add(__tsover_mul(c,.2104542553),__tsover_mul(d,.793617785)),__tsover_mul(u,.0040720468)),__tsover_add(__tsover_sub(__tsover_mul(c,1.9779984951),__tsover_mul(d,2.428592205)),__tsover_mul(u,.4505937099)),__tsover_sub(__tsover_add(__tsover_mul(c,.0259040371),__tsover_mul(d,.7827717662)),__tsover_mul(u,.808675766)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[13,\"r\",[7,\"rgb\",\"x\"]],[13,\"g\",[7,\"rgb\",\"y\"]],[13,\"b\",[7,\"rgb\",\"z\"]],[13,\"l\",[1,[1,[1,\"r\",\"*\",[5,\"0.4122214708\"]],\"+\",[1,\"g\",\"*\",[5,\"0.5363325363\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.0514459929\"]]]],[13,\"m\",[1,[1,\"r\",\"*\",[5,\"0.2119034982\"]],\"+\",[1,[1,\"g\",\"*\",[5,\"0.6806995451\"]],\"+\",[1,\"b\",\"*\",[5,\"0.1073969566\"]]]]],[13,\"s\",[1,[1,[1,\"r\",\"*\",[5,\"0.0883024619\"]],\"+\",[1,\"g\",\"*\",[5,\"0.2817188376\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.6299787005\"]]]],[13,\"l_\",[1,[6,\"std.sign\",[\"l\"]],\"*\",[6,\"std.pow\",[[6,\"std.abs\",[\"l\"]],[1,[5,\"1\"],\"/\",[5,\"3\"]]]]]],[13,\"m_\",[1,[6,\"std.sign\",[\"m\"]],\"*\",[6,\"std.pow\",[[6,\"std.abs\",[\"m\"]],[1,[5,\"1\"],\"/\",[5,\"3\"]]]]]],[13,\"s_\",[1,[6,\"std.sign\",[\"s\"]],\"*\",[6,\"std.pow\",[[6,\"std.abs\",[\"s\"]],[1,[5,\"1\"],\"/\",[5,\"3\"]]]]]],[10,[6,\"d.vec3f\",[[1,[1,[1,\"l_\",\"*\",[5,\"0.2104542553\"]],\"+\",[1,\"m_\",\"*\",[5,\"0.793617785\"]]],\"-\",[1,\"s_\",\"*\",[5,\"0.0040720468\"]]],[1,[1,[1,\"l_\",\"*\",[5,\"1.9779984951\"]],\"-\",[1,\"m_\",\"*\",[5,\"2.428592205\"]]],\"+\",[1,\"s_\",\"*\",[5,\"0.4505937099\"]]],[1,[1,[1,\"l_\",\"*\",[5,\"0.0259040371\"]],\"+\",[1,\"m_\",\"*\",[5,\"0.7827717662\"]]],\"-\",[1,\"s_\",\"*\",[5,\"0.808675766\"]]]]]]]]},externals:{\"std.sign\":()=>no,\"std.pow\":()=>vt,\"std.abs\":()=>re,\"d.vec3f\":()=>A}})&&e.f)({})),oa=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_add(__tsover_add(s,__tsover_mul(r,.3963377774)),__tsover_mul(o,.2158037573)),i=__tsover_sub(__tsover_sub(s,__tsover_mul(r,.1055613458)),__tsover_mul(o,.0638541728)),l=__tsover_sub(__tsover_sub(s,__tsover_mul(r,.0894841775)),__tsover_mul(o,1.291485548)),c=vt(a,3),d=vt(i,3),u=vt(l,3);return A(__tsover_add(__tsover_sub(__tsover_mul(c,4.0767416621),__tsover_mul(d,3.3077115913)),__tsover_mul(u,.2309699292)),__tsover_sub(__tsover_add(__tsover_mul(c,-1.2684380046),__tsover_mul(d,2.6097574011)),__tsover_mul(u,.3413193965)),__tsover_add(__tsover_sub(__tsover_mul(c,-.0041960863),__tsover_mul(d,.7034186147)),__tsover_mul(u,1.707614701)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lab\"}],body:[0,[[13,\"L\",[7,\"lab\",\"x\"]],[13,\"a\",[7,\"lab\",\"y\"]],[13,\"b\",[7,\"lab\",\"z\"]],[13,\"l_\",[1,[1,\"L\",\"+\",[1,\"a\",\"*\",[5,\"0.3963377774\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.2158037573\"]]]],[13,\"m_\",[1,[1,\"L\",\"-\",[1,\"a\",\"*\",[5,\"0.1055613458\"]]],\"-\",[1,\"b\",\"*\",[5,\"0.0638541728\"]]]],[13,\"s_\",[1,[1,\"L\",\"-\",[1,\"a\",\"*\",[5,\"0.0894841775\"]]],\"-\",[1,\"b\",\"*\",[5,\"1.291485548\"]]]],[13,\"l\",[6,\"std.pow\",[\"l_\",[5,\"3\"]]]],[13,\"m\",[6,\"std.pow\",[\"m_\",[5,\"3\"]]]],[13,\"s\",[6,\"std.pow\",[\"s_\",[5,\"3\"]]]],[10,[6,\"d.vec3f\",[[1,[1,[1,\"l\",\"*\",[5,\"4.0767416621\"]],\"-\",[1,\"m\",\"*\",[5,\"3.3077115913\"]]],\"+\",[1,\"s\",\"*\",[5,\"0.2309699292\"]]],[1,[1,[1,\"l\",\"*\",[4,\"-\",[5,\"1.2684380046\"]]],\"+\",[1,\"m\",\"*\",[5,\"2.6097574011\"]]],\"-\",[1,\"s\",\"*\",[5,\"0.3413193965\"]]],[1,[1,[1,\"l\",\"*\",[4,\"-\",[5,\"0.0041960863\"]]],\"-\",[1,\"m\",\"*\",[5,\"0.7034186147\"]]],\"+\",[1,\"s\",\"*\",[5,\"1.707614701\"]]]]]]]]},externals:{\"std.pow\":()=>vt,\"d.vec3f\":()=>A}})&&e.f)({})),Zl=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z;return A(s,Ue(__tsover_add(__tsover_mul(r,r),__tsover_mul(o,o))),Xt(o,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lab\"}],body:[0,[[13,\"L\",[7,\"lab\",\"x\"]],[13,\"a\",[7,\"lab\",\"y\"]],[13,\"b\",[7,\"lab\",\"z\"]],[13,\"C\",[6,\"std.sqrt\",[[1,[1,\"a\",\"*\",\"a\"],\"+\",[1,\"b\",\"*\",\"b\"]]]]],[13,\"h\",[6,\"std.atan2\",[\"b\",\"a\"]]],[10,[6,\"d.vec3f\",[\"L\",\"C\",\"h\"]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.atan2\":()=>Xt,\"d.vec3f\":()=>A}})&&e.f)({})),pl=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z;return A(s,__tsover_mul(r,pe(o)),__tsover_mul(r,ae(o)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lch\"}],body:[0,[[13,\"L\",[7,\"lch\",\"x\"]],[13,\"C\",[7,\"lch\",\"y\"]],[13,\"h\",[7,\"lch\",\"z\"]],[10,[6,\"d.vec3f\",[\"L\",[1,\"C\",\"*\",[6,\"std.cos\",[\"h\"]]],[1,\"C\",\"*\",[6,\"std.sin\",[\"h\"]]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),$m=w.fn([n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=n(0),a=n(0),i=n(0);return r<1?(o=t,a=s):r<2?(o=s,a=t):r<3?(a=t,i=s):r<4?(a=s,i=t):r<5?(o=s,i=t):(o=t,i=s),A(o,a,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"c\"},{type:\"i\",name:\"x\"},{type:\"i\",name:\"sector\"}],body:[0,[[12,\"r\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"g\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"b\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"sector\",\"<\",[5,\"1\"]],[0,[[2,\"r\",\"=\",\"c\"],[2,\"g\",\"=\",\"x\"]]],[11,[1,\"sector\",\"<\",[5,\"2\"]],[0,[[2,\"r\",\"=\",\"x\"],[2,\"g\",\"=\",\"c\"]]],[11,[1,\"sector\",\"<\",[5,\"3\"]],[0,[[2,\"g\",\"=\",\"c\"],[2,\"b\",\"=\",\"x\"]]],[11,[1,\"sector\",\"<\",[5,\"4\"]],[0,[[2,\"g\",\"=\",\"x\"],[2,\"b\",\"=\",\"c\"]]],[11,[1,\"sector\",\"<\",[5,\"5\"]],[0,[[2,\"r\",\"=\",\"x\"],[2,\"b\",\"=\",\"c\"]]],[0,[[2,\"r\",\"=\",\"c\"],[2,\"b\",\"=\",\"x\"]]]]]]]],[10,[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]]]]]},externals:{\"d.f32\":()=>n,\"d.vec3f\":()=>A}})&&e.f)({})),cd=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=B(B(s,r),o),i=ye(ye(s,r),o),l=__tsover_sub(a,i),c=__tsover_mul(__tsover_add(a,i),.5),d=B(__tsover_sub(1,re(__tsover_sub(__tsover_mul(c,2),1))),bi),u=__tsover_div(l,d),p=a===s,_=a===r,f=__tsover_div(__tsover_sub(r,o),B(l,bi)),h=__tsover_add(2,__tsover_div(__tsover_sub(o,s),B(l,bi))),g=N(N(__tsover_add(4,__tsover_div(__tsover_sub(s,r),B(l,bi))),h,_),f,p);return A(__tsover_mul(g,uc),u,c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[13,\"r\",[7,\"rgb\",\"x\"]],[13,\"g\",[7,\"rgb\",\"y\"]],[13,\"b\",[7,\"rgb\",\"z\"]],[13,\"maxVal\",[6,\"std.max\",[[6,\"std.max\",[\"r\",\"g\"]],\"b\"]]],[13,\"minVal\",[6,\"std.min\",[[6,\"std.min\",[\"r\",\"g\"]],\"b\"]]],[13,\"delta\",[1,\"maxVal\",\"-\",\"minVal\"]],[13,\"l\",[1,[1,\"maxVal\",\"+\",\"minVal\"],\"*\",[5,\"0.5\"]]],[13,\"satDenom\",[6,\"std.max\",[[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,[1,\"l\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]],\"EPSILON\"]]],[13,\"s\",[1,\"delta\",\"/\",\"satDenom\"]],[13,\"isRMax\",[1,\"maxVal\",\"===\",\"r\"]],[13,\"isGMax\",[1,\"maxVal\",\"===\",\"g\"]],[13,\"hueR\",[1,[1,\"g\",\"-\",\"b\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]],[13,\"hueG\",[1,[5,\"2\"],\"+\",[1,[1,\"b\",\"-\",\"r\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]]],[13,\"hueB\",[1,[5,\"4\"],\"+\",[1,[1,\"r\",\"-\",\"g\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]]],[13,\"hue\",[6,\"std.select\",[[6,\"std.select\",[\"hueB\",\"hueG\",\"isGMax\"]],\"hueR\",\"isRMax\"]]],[13,\"h\",[1,\"hue\",\"*\",\"PI_OVER_3\"]],[10,[6,\"d.vec3f\",[\"h\",\"s\",\"l\"]]]]]},externals:{\"std.max\":()=>B,\"std.min\":()=>ye,\"std.abs\":()=>re,EPSILON:()=>bi,\"std.select\":()=>N,PI_OVER_3:()=>uc,\"d.vec3f\":()=>A}})&&e.f)({})),ml=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_mul(r,__tsover_sub(1,re(__tsover_sub(__tsover_mul(o,2),1)))),i=__tsover_div(s,uc),l=__tsover_mul(a,__tsover_sub(1,re(__tsover_sub(An(i,2),1)))),c=__tsover_sub(o,__tsover_mul(a,.5));return $m(a,l,ie(i)).add(c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"hsl\"}],body:[0,[[13,\"h\",[7,\"hsl\",\"x\"]],[13,\"s\",[7,\"hsl\",\"y\"]],[13,\"l\",[7,\"hsl\",\"z\"]],[13,\"c\",[1,\"s\",\"*\",[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,[1,\"l\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]]]],[13,\"hPrime\",[1,\"h\",\"/\",\"PI_OVER_3\"]],[13,\"x\",[1,\"c\",\"*\",[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,[6,\"std.mod\",[\"hPrime\",[5,\"2\"]]],\"-\",[5,\"1\"]]]]]]],[13,\"m\",[1,\"l\",\"-\",[1,\"c\",\"*\",[5,\"0.5\"]]]],[13,\"sector\",[6,\"std.floor\",[\"hPrime\"]]],[10,[6,[7,[6,\"selectRGBBySector\",[\"c\",\"x\",\"sector\"]],\"add\"],[\"m\"]]]]]},externals:{\"std.abs\":()=>re,PI_OVER_3:()=>uc,\"std.mod\":()=>An,\"std.floor\":()=>ie,selectRGBBySector:()=>$m}})&&e.f)({})),Em=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=B(B(s,r),o),i=ye(ye(s,r),o),l=__tsover_sub(a,i),c=a,d=__tsover_div(l,B(a,bi)),u=a===s,p=a===r,_=__tsover_div(__tsover_sub(r,o),B(l,bi)),f=__tsover_add(2,__tsover_div(__tsover_sub(o,s),B(l,bi))),h=N(N(__tsover_add(4,__tsover_div(__tsover_sub(s,r),B(l,bi))),f,p),_,u);return A(__tsover_mul(h,uc),d,c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[13,\"r\",[7,\"rgb\",\"x\"]],[13,\"g\",[7,\"rgb\",\"y\"]],[13,\"b\",[7,\"rgb\",\"z\"]],[13,\"maxVal\",[6,\"std.max\",[[6,\"std.max\",[\"r\",\"g\"]],\"b\"]]],[13,\"minVal\",[6,\"std.min\",[[6,\"std.min\",[\"r\",\"g\"]],\"b\"]]],[13,\"delta\",[1,\"maxVal\",\"-\",\"minVal\"]],[13,\"v\",\"maxVal\"],[13,\"s\",[1,\"delta\",\"/\",[6,\"std.max\",[\"maxVal\",\"EPSILON\"]]]],[13,\"isRMax\",[1,\"maxVal\",\"===\",\"r\"]],[13,\"isGMax\",[1,\"maxVal\",\"===\",\"g\"]],[13,\"hueR\",[1,[1,\"g\",\"-\",\"b\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]],[13,\"hueG\",[1,[5,\"2\"],\"+\",[1,[1,\"b\",\"-\",\"r\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]]],[13,\"hueB\",[1,[5,\"4\"],\"+\",[1,[1,\"r\",\"-\",\"g\"],\"/\",[6,\"std.max\",[\"delta\",\"EPSILON\"]]]]],[13,\"hue\",[6,\"std.select\",[[6,\"std.select\",[\"hueB\",\"hueG\",\"isGMax\"]],\"hueR\",\"isRMax\"]]],[13,\"h\",[1,\"hue\",\"*\",\"PI_OVER_3\"]],[10,[6,\"d.vec3f\",[\"h\",\"s\",\"v\"]]]]]},externals:{\"std.max\":()=>B,\"std.min\":()=>ye,EPSILON:()=>bi,\"std.select\":()=>N,PI_OVER_3:()=>uc,\"d.vec3f\":()=>A}})&&e.f)({})),hd=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_mul(o,r),i=__tsover_div(s,uc),l=__tsover_mul(a,__tsover_sub(1,re(__tsover_sub(An(i,2),1)))),c=__tsover_sub(o,a);return $m(a,l,ie(i)).add(c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"hsv\"}],body:[0,[[13,\"h\",[7,\"hsv\",\"x\"]],[13,\"s\",[7,\"hsv\",\"y\"]],[13,\"v\",[7,\"hsv\",\"z\"]],[13,\"c\",[1,\"v\",\"*\",\"s\"]],[13,\"hPrime\",[1,\"h\",\"/\",\"PI_OVER_3\"]],[13,\"x\",[1,\"c\",\"*\",[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,[6,\"std.mod\",[\"hPrime\",[5,\"2\"]]],\"-\",[5,\"1\"]]]]]]],[13,\"m\",[1,\"v\",\"-\",\"c\"]],[13,\"sector\",[6,\"std.floor\",[\"hPrime\"]]],[10,[6,[7,[6,\"selectRGBBySector\",[\"c\",\"x\",\"sector\"]],\"add\"],[\"m\"]]]]]},externals:{PI_OVER_3:()=>uc,\"std.abs\":()=>re,\"std.mod\":()=>An,\"std.floor\":()=>ie,selectRGBBySector:()=>$m}})&&e.f)({})),km=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_add(__tsover_add(__tsover_mul(s,.4124564),__tsover_mul(r,.3575761)),__tsover_mul(o,.1804375)),i=__tsover_add(__tsover_add(__tsover_mul(s,.2126729),__tsover_mul(r,.7151522)),__tsover_mul(o,.072175)),l=__tsover_add(__tsover_add(__tsover_mul(s,.0193339),__tsover_mul(r,.119192)),__tsover_mul(o,.9503041)),c=__tsover_div(a,.95047),d=__tsover_div(i,1),u=__tsover_div(l,1.08883),p=N(__tsover_add(__tsover_mul(c,a1),jl),vt(c,__tsover_div(1,3)),c>o1),_=N(__tsover_add(__tsover_mul(d,a1),jl),vt(d,__tsover_div(1,3)),d>o1),f=N(__tsover_add(__tsover_mul(u,a1),jl),vt(u,__tsover_div(1,3)),u>o1);return A(__tsover_sub(__tsover_mul(_,116),16),__tsover_mul(__tsover_sub(p,_),fv),__tsover_mul(__tsover_sub(_,f),vv))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[13,\"r\",[7,\"rgb\",\"x\"]],[13,\"g\",[7,\"rgb\",\"y\"]],[13,\"b\",[7,\"rgb\",\"z\"]],[13,\"x\",[1,[1,[1,\"r\",\"*\",[5,\"0.4124564\"]],\"+\",[1,\"g\",\"*\",[5,\"0.3575761\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.1804375\"]]]],[13,\"y\",[1,[1,[1,\"r\",\"*\",[5,\"0.2126729\"]],\"+\",[1,\"g\",\"*\",[5,\"0.7151522\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.072175\"]]]],[13,\"z\",[1,[1,[1,\"r\",\"*\",[5,\"0.0193339\"]],\"+\",[1,\"g\",\"*\",[5,\"0.119192\"]]],\"+\",[1,\"b\",\"*\",[5,\"0.9503041\"]]]],[13,\"xn\",[1,\"x\",\"/\",[5,\"0.95047\"]]],[13,\"yn\",[1,\"y\",\"/\",[5,\"1\"]]],[13,\"zn\",[1,\"z\",\"/\",[5,\"1.08883\"]]],[13,\"fx\",[6,\"std.select\",[[1,[1,\"xn\",\"*\",\"LAB_FWD_SLOPE\"],\"+\",\"LAB_OFFSET\"],[6,\"std.pow\",[\"xn\",[1,[5,\"1\"],\"/\",[5,\"3\"]]]],[1,\"xn\",\">\",\"LAB_DELTA3\"]]]],[13,\"fy\",[6,\"std.select\",[[1,[1,\"yn\",\"*\",\"LAB_FWD_SLOPE\"],\"+\",\"LAB_OFFSET\"],[6,\"std.pow\",[\"yn\",[1,[5,\"1\"],\"/\",[5,\"3\"]]]],[1,\"yn\",\">\",\"LAB_DELTA3\"]]]],[13,\"fz\",[6,\"std.select\",[[1,[1,\"zn\",\"*\",\"LAB_FWD_SLOPE\"],\"+\",\"LAB_OFFSET\"],[6,\"std.pow\",[\"zn\",[1,[5,\"1\"],\"/\",[5,\"3\"]]]],[1,\"zn\",\">\",\"LAB_DELTA3\"]]]],[13,\"L\",[1,[1,\"fy\",\"*\",[5,\"116\"]],\"-\",[5,\"16\"]]],[13,\"a\",[1,[1,\"fx\",\"-\",\"fy\"],\"*\",\"LAB_KAPPA\"]],[13,\"bLab\",[1,[1,\"fy\",\"-\",\"fz\"],\"*\",\"LAB_LAMBDA\"]],[10,[6,\"d.vec3f\",[\"L\",\"a\",\"bLab\"]]]]]},externals:{\"std.select\":()=>N,LAB_FWD_SLOPE:()=>a1,LAB_OFFSET:()=>jl,\"std.pow\":()=>vt,LAB_DELTA3:()=>o1,LAB_KAPPA:()=>fv,LAB_LAMBDA:()=>vv,\"d.vec3f\":()=>A}})&&e.f)({})),gd=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z,a=__tsover_div(__tsover_add(s,16),116),i=__tsover_add(__tsover_div(r,fv),a),l=__tsover_sub(a,__tsover_div(o,vv)),c=N(__tsover_mul(__tsover_sub(i,jl),n1),vt(i,3),i>Zn),d=N(__tsover_mul(__tsover_sub(a,jl),n1),vt(a,3),a>Zn),u=N(__tsover_mul(__tsover_sub(l,jl),n1),vt(l,3),l>Zn),p=__tsover_mul(c,.95047),_=__tsover_mul(d,1),f=__tsover_mul(u,1.08883);return A(__tsover_sub(__tsover_sub(__tsover_mul(p,3.2404542),__tsover_mul(_,1.5371385)),__tsover_mul(f,.4985314)),__tsover_add(__tsover_add(__tsover_mul(p,-.969266),__tsover_mul(_,1.8760108)),__tsover_mul(f,.041556)),__tsover_add(__tsover_sub(__tsover_mul(p,.0556434),__tsover_mul(_,.2040259)),__tsover_mul(f,1.0572252)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lab\"}],body:[0,[[13,\"L\",[7,\"lab\",\"x\"]],[13,\"a\",[7,\"lab\",\"y\"]],[13,\"b\",[7,\"lab\",\"z\"]],[13,\"fy\",[1,[1,\"L\",\"+\",[5,\"16\"]],\"/\",[5,\"116\"]]],[13,\"fx\",[1,[1,\"a\",\"/\",\"LAB_KAPPA\"],\"+\",\"fy\"]],[13,\"fz\",[1,\"fy\",\"-\",[1,\"b\",\"/\",\"LAB_LAMBDA\"]]],[13,\"xn\",[6,\"std.select\",[[1,[1,\"fx\",\"-\",\"LAB_OFFSET\"],\"*\",\"LAB_INV_SLOPE\"],[6,\"std.pow\",[\"fx\",[5,\"3\"]]],[1,\"fx\",\">\",\"LAB_DELTA\"]]]],[13,\"yn\",[6,\"std.select\",[[1,[1,\"fy\",\"-\",\"LAB_OFFSET\"],\"*\",\"LAB_INV_SLOPE\"],[6,\"std.pow\",[\"fy\",[5,\"3\"]]],[1,\"fy\",\">\",\"LAB_DELTA\"]]]],[13,\"zn\",[6,\"std.select\",[[1,[1,\"fz\",\"-\",\"LAB_OFFSET\"],\"*\",\"LAB_INV_SLOPE\"],[6,\"std.pow\",[\"fz\",[5,\"3\"]]],[1,\"fz\",\">\",\"LAB_DELTA\"]]]],[13,\"x\",[1,\"xn\",\"*\",[5,\"0.95047\"]]],[13,\"y\",[1,\"yn\",\"*\",[5,\"1\"]]],[13,\"z\",[1,\"zn\",\"*\",[5,\"1.08883\"]]],[13,\"r\",[1,[1,[1,\"x\",\"*\",[5,\"3.2404542\"]],\"-\",[1,\"y\",\"*\",[5,\"1.5371385\"]]],\"-\",[1,\"z\",\"*\",[5,\"0.4985314\"]]]],[13,\"g\",[1,[1,[1,\"x\",\"*\",[4,\"-\",[5,\"0.969266\"]]],\"+\",[1,\"y\",\"*\",[5,\"1.8760108\"]]],\"+\",[1,\"z\",\"*\",[5,\"0.041556\"]]]],[13,\"bRgb\",[1,[1,[1,\"x\",\"*\",[5,\"0.0556434\"]],\"-\",[1,\"y\",\"*\",[5,\"0.2040259\"]]],\"+\",[1,\"z\",\"*\",[5,\"1.0572252\"]]]],[10,[6,\"d.vec3f\",[\"r\",\"g\",\"bRgb\"]]]]]},externals:{LAB_KAPPA:()=>fv,LAB_LAMBDA:()=>vv,\"std.select\":()=>N,LAB_OFFSET:()=>jl,LAB_INV_SLOPE:()=>n1,\"std.pow\":()=>vt,LAB_DELTA:()=>Zn,\"d.vec3f\":()=>A}})&&e.f)({})),Bm=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z;return A(s,Ue(__tsover_add(__tsover_mul(r,r),__tsover_mul(o,o))),Xt(o,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lab\"}],body:[0,[[13,\"L\",[7,\"lab\",\"x\"]],[13,\"a\",[7,\"lab\",\"y\"]],[13,\"b\",[7,\"lab\",\"z\"]],[13,\"C\",[6,\"std.sqrt\",[[1,[1,\"a\",\"*\",\"a\"],\"+\",[1,\"b\",\"*\",\"b\"]]]]],[13,\"h\",[6,\"std.atan2\",[\"b\",\"a\"]]],[10,[6,\"d.vec3f\",[\"L\",\"C\",\"h\"]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.atan2\":()=>Xt,\"d.vec3f\":()=>A}})&&e.f)({})),yd=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.x,r=t.y,o=t.z;return A(s,__tsover_mul(r,pe(o)),__tsover_mul(r,ae(o)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lch\"}],body:[0,[[13,\"L\",[7,\"lch\",\"x\"]],[13,\"C\",[7,\"lch\",\"y\"]],[13,\"h\",[7,\"lch\",\"z\"]],[10,[6,\"d.vec3f\",[\"L\",[1,\"C\",\"*\",[6,\"std.cos\",[\"h\"]]],[1,\"C\",\"*\",[6,\"std.sin\",[\"h\"]]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),Mr=w.fn([A,A,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return t.mul(r).add(s.mul(o)).div(a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"wA\"},{type:\"i\",name:\"wB\"},{type:\"i\",name:\"sw\"}],body:[0,[[10,[6,[7,[6,[7,[6,[7,\"a\",\"mul\"],[\"wA\"]],\"add\"],[[6,[7,\"b\",\"mul\"],[\"wB\"]]]],\"div\"],[\"sw\"]]]]]},externals:{}})&&e.f)({})),Ar=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Mr(t.xyz,s.xyz,o,a,l),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"rgb\",[6,\"awMix\",[[7,\"colorA\",\"xyz\"],[7,\"colorB\",\"xyz\"],\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S}})&&e.f)({})),Fk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Hs(oa(pl(Mr(Zl(Ta(Cr(t.xyz))),Zl(Ta(Cr(s.xyz))),o,a,l)))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"oklchA\",[6,\"oklabToOklch\",[[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"colorA\",\"xyz\"]]]]]]]],[13,\"oklchB\",[6,\"oklabToOklch\",[[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"colorB\",\"xyz\"]]]]]]]],[13,\"mixed\",[6,\"awMix\",[\"oklchA\",\"oklchB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[[6,\"oklchToOklab\",[\"mixed\"]]]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,oklabToOklch:()=>Zl,rgbToOklab:()=>Ta,p3ToSRGB:()=>Cr,awMix:()=>Mr,sRGBToP3:()=>Hs,oklabToRgb:()=>oa,oklchToOklab:()=>pl,\"d.vec4f\":()=>S}})&&e.f)({})),Ik=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Hs(oa(Mr(Ta(Cr(t.xyz)),Ta(Cr(s.xyz)),o,a,l))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"oklabA\",[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"colorA\",\"xyz\"]]]]]],[13,\"oklabB\",[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"colorB\",\"xyz\"]]]]]],[13,\"mixed\",[6,\"awMix\",[\"oklabA\",\"oklabB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[\"mixed\"]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,rgbToOklab:()=>Ta,p3ToSRGB:()=>Cr,awMix:()=>Mr,sRGBToP3:()=>Hs,oklabToRgb:()=>oa,\"d.vec4f\":()=>S}})&&e.f)({})),Yk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Hs(ml(Mr(cd(Cr(t.xyz)),cd(Cr(s.xyz)),o,a,l))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"hslA\",[6,\"rgbToHsl\",[[6,\"p3ToSRGB\",[[7,\"colorA\",\"xyz\"]]]]]],[13,\"hslB\",[6,\"rgbToHsl\",[[6,\"p3ToSRGB\",[[7,\"colorB\",\"xyz\"]]]]]],[13,\"mixed\",[6,\"awMix\",[\"hslA\",\"hslB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"hslToRgb\",[\"mixed\"]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,rgbToHsl:()=>cd,p3ToSRGB:()=>Cr,awMix:()=>Mr,sRGBToP3:()=>Hs,hslToRgb:()=>ml,\"d.vec4f\":()=>S}})&&e.f)({})),Wk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Hs(hd(Mr(Em(Cr(t.xyz)),Em(Cr(s.xyz)),o,a,l))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"hsvA\",[6,\"rgbToHsv\",[[6,\"p3ToSRGB\",[[7,\"colorA\",\"xyz\"]]]]]],[13,\"hsvB\",[6,\"rgbToHsv\",[[6,\"p3ToSRGB\",[[7,\"colorB\",\"xyz\"]]]]]],[13,\"mixed\",[6,\"awMix\",[\"hsvA\",\"hsvB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"hsvToRgb\",[\"mixed\"]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,rgbToHsv:()=>Em,p3ToSRGB:()=>Cr,awMix:()=>Mr,sRGBToP3:()=>Hs,hsvToRgb:()=>hd,\"d.vec4f\":()=>S}})&&e.f)({})),qk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_mul(s.w,r),i=__tsover_add(o,a),l=B(i,.001);return S(Hs(gd(yd(Mr(Bm(km(Cr(t.xyz))),Bm(km(Cr(s.xyz))),o,a,l)))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"lchA\",[6,\"labToLch\",[[6,\"rgbToLab\",[[6,\"p3ToSRGB\",[[7,\"colorA\",\"xyz\"]]]]]]]],[13,\"lchB\",[6,\"labToLch\",[[6,\"rgbToLab\",[[6,\"p3ToSRGB\",[[7,\"colorB\",\"xyz\"]]]]]]]],[13,\"mixed\",[6,\"awMix\",[\"lchA\",\"lchB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"labToRgb\",[[6,\"lchToLab\",[\"mixed\"]]]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,labToLch:()=>Bm,rgbToLab:()=>km,p3ToSRGB:()=>Cr,awMix:()=>Mr,sRGBToP3:()=>Hs,labToRgb:()=>gd,lchToLab:()=>yd,\"d.vec4f\":()=>S}})&&e.f)({})),No={0:Ar,1:Fk,2:Ik,3:Yk,4:Wk,5:qk},FJ=(e,t,s,r)=>(No[r]??Ar)(e,t,s),IJ=(e,t)=>{switch(t){case 1:return Zl(Ta(Cr(e)));case 2:return Ta(Cr(e));case 3:return cd(Cr(e));case 4:return Em(Cr(e));case 5:return Bm(km(Cr(e)));default:return e}},YJ=(e,t)=>{switch(t){case 1:return Hs(oa(pl(e)));case 2:return Hs(oa(e));case 3:return Hs(ml(e));case 4:return Hs(hd(e));case 5:return Hs(gd(yd(e)));default:return e}},Xa=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({conv:A,alpha:n}),\"MixStep\"),_l=w.fn([A,n,A,n,n],Xa)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(s,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return Xa({conv:Mr(t,r,i,l,B(c,.001)),alpha:c})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[10,[6,\"MixStep\",[[104,{conv:[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]],alpha:\"totalWeight\"}]]]]]]},externals:{\"std.max\":()=>B,MixStep:()=>Xa,awMix:()=>Mr}})&&e.f)({})),hc=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Mr(t,s,i,l,B(c,.001)),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[\"mixed\",\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S}})&&e.f)({})),Vk=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Hs(oa(pl(Mr(t,s,i,l,B(c,.001))))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[[6,\"oklchToOklab\",[\"mixed\"]]]]]],\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S,sRGBToP3:()=>Hs,oklabToRgb:()=>oa,oklchToOklab:()=>pl}})&&e.f)({})),Nk=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Hs(oa(Mr(t,s,i,l,B(c,.001)))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[\"mixed\"]]]],\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S,sRGBToP3:()=>Hs,oklabToRgb:()=>oa}})&&e.f)({})),$k=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Hs(ml(Mr(t,s,i,l,B(c,.001)))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[[6,\"sRGBToP3\",[[6,\"hslToRgb\",[\"mixed\"]]]],\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S,sRGBToP3:()=>Hs,hslToRgb:()=>ml}})&&e.f)({})),Xk=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Hs(hd(Mr(t,s,i,l,B(c,.001)))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[[6,\"sRGBToP3\",[[6,\"hsvToRgb\",[\"mixed\"]]]],\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S,sRGBToP3:()=>Hs,hsvToRgb:()=>hd}})&&e.f)({})),Hk=w.fn([A,A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,__tsover_sub(1,a)),l=__tsover_mul(o,a),c=__tsover_add(i,l);return S(Hs(gd(yd(Mr(t,s,i,l,B(c,.001))))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"convA\"},{type:\"i\",name:\"convB\"},{type:\"i\",name:\"alphaA\"},{type:\"i\",name:\"alphaB\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"weightA\",[1,\"alphaA\",\"*\",[1,[5,\"1\"],\"-\",\"t\"]]],[13,\"weightB\",[1,\"alphaB\",\"*\",\"t\"]],[13,\"totalWeight\",[1,\"weightA\",\"+\",\"weightB\"]],[13,\"safeWeight\",[6,\"std.max\",[\"totalWeight\",[5,\"0.001\"]]]],[13,\"mixed\",[6,\"awMix\",[\"convA\",\"convB\",\"weightA\",\"weightB\",\"safeWeight\"]]],[10,[6,\"d.vec4f\",[[6,\"sRGBToP3\",[[6,\"labToRgb\",[[6,\"lchToLab\",[\"mixed\"]]]]]],\"totalWeight\"]]]]]},externals:{\"std.max\":()=>B,awMix:()=>Mr,\"d.vec4f\":()=>S,sRGBToP3:()=>Hs,labToRgb:()=>gd,lchToLab:()=>yd}})&&e.f)({})),Bd={0:hc,1:Vk,2:Nk,3:$k,4:Xk,5:Hk},WJ=(e,t,s,r,o,a)=>(Bd[a]??hc)(e,t,s,r,o),W3=1e-4,_0=6/29,qJ=(e,t,s)=>[e*1.2249401-t*.2249404,e*-.0420569+t*1.0420571,e*-.0196376-t*.0786361+s*1.0982735],DT=(e,t,s)=>{let r=Math.cbrt(e*.4122214708+t*.5363325363+s*.0514459929),o=Math.cbrt(e*.2119034982+t*.6806995451+s*.1073969566),a=Math.cbrt(e*.0883024619+t*.2817188376+s*.6299787005);return[r*.2104542553+o*.793617785-a*.0040720468,r*1.9779984951-o*2.428592205+a*.4505937099,r*.0259040371+o*.7827717662-a*.808675766]},jk=(e,t,s,r,o)=>{let a=Math.max(o,W3);return(r===e?(t-s)/a:r===t?2+(s-e)/a:4+(e-t)/a)*(Math.PI/3)},VJ=(e,t,s)=>{let r=Math.max(e,t,s),o=Math.min(e,t,s),a=r-o,i=(r+o)*.5,l=a/Math.max(1-Math.abs(2*i-1),W3);return[jk(e,t,s,r,a),l,i]},NJ=(e,t,s)=>{let r=Math.max(e,t,s),o=r-Math.min(e,t,s),a=o/Math.max(r,W3);return[jk(e,t,s,r,o),a,r]},$J=(e,t,s)=>{let r=(e*.4124564+t*.3575761+s*.1804375)/.95047,o=e*.2126729+t*.7151522+s*.072175,a=(e*.0193339+t*.119192+s*.9503041)/1.08883,i=_0**3,l=p=>p>i?Math.cbrt(p):p/(3*_0*_0)+4/29,c=l(r),d=l(o),u=l(a);return[d*116-16,(c-d)*500,(d-u)*200]},fl=(e,t,s,r)=>{let[o,a,i]=qJ(e,t,s);switch(r){case 1:{let[l,c,d]=DT(o,a,i);return[l,Math.sqrt(c*c+d*d),Math.atan2(d,c)]}case 2:return DT(o,a,i);case 3:return VJ(o,a,i);case 4:return NJ(o,a,i);case 5:{let[l,c,d]=$J(o,a,i);return[l,Math.sqrt(c*c+d*d),Math.atan2(d,c)]}default:return[e,t,s]}},Kk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=K(A(t.x,t.y,t.z),A(s.x,s.y,s.z),A(r));return S(o.x,o.y,o.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"target\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]],[13,\"targetRgb\",[6,\"d.vec3f\",[[7,\"target\",\"x\"],[7,\"target\",\"y\"],[7,\"target\",\"z\"]]]],[13,\"mixed\",[6,\"std.mix\",[\"rgb\",\"targetRgb\",[6,\"d.vec3f\",[\"amount\"]]]]],[10,[6,\"d.vec4f\",[[7,\"mixed\",\"x\"],[7,\"mixed\",\"y\"],[7,\"mixed\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),Qk=ko({applyBlendMode:()=>XJ,blendModes:()=>pc,color:()=>l4,colorBurn:()=>p4,colorDodge:()=>n4,darken:()=>d4,difference:()=>a4,exclusion:()=>i4,hardLight:()=>f4,hue:()=>v4,lighten:()=>u4,linearBurn:()=>m4,linearDodge:()=>r4,luminance:()=>mn,luminosity:()=>c4,multiply:()=>t4,normal:()=>Zk,normalOklab:()=>e4,normalOklch:()=>Jk,overlay:()=>o4,saturation:()=>h4,screen:()=>s4,softLight:()=>_4,unpremultiplyAlpha:()=>Yt}),mn=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(__tsover_add(__tsover_mul(t.x,.2126),__tsover_mul(t.y,.7152)),__tsover_mul(t.z,.0722))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[1,[1,[1,[7,\"rgb\",\"x\"],\"*\",[5,\"0.2126\"]],\"+\",[1,[7,\"rgb\",\"y\"],\"*\",[5,\"0.7152\"]]],\"+\",[1,[7,\"rgb\",\"z\"],\"*\",[5,\"0.0722\"]]]]]]},externals:{}})&&e.f)({})),Pi=w.fn([A,A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return N(A(0),A(1),gb(s,t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"edge\"},{type:\"i\",name:\"x\"}],body:[0,[[10,[6,\"std.select\",[[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]],[6,\"std.ge\",[\"x\",\"edge\"]]]]]]]},externals:{\"std.select\":()=>N,\"d.vec3f\":()=>A,\"std.ge\":()=>gb}})&&e.f)({})),qs=w.fn([S,A,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t.w,__tsover_sub(1,r)),a=__tsover_add(r,o);return S(s.mul(r).add(t.xyz.mul(o)),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"blended\"},{type:\"i\",name:\"overlayAlpha\"}],body:[0,[[13,\"baseWeight\",[1,[7,\"base\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"overlayAlpha\"]]],[13,\"finalAlpha\",[1,\"overlayAlpha\",\"+\",\"baseWeight\"]],[13,\"rgb\",[6,[7,[6,[7,\"blended\",\"mul\"],[\"overlayAlpha\"]],\"add\"],[[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[\"baseWeight\"]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"finalAlpha\"]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),Yt=w.fn([S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.w;return S(N(A(0,0,0),t.xyz.div(s),s>.001),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"alpha\",[7,\"color\",\"w\"]],[13,\"straightRGB\",[6,\"std.select\",[[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,[7,[7,\"color\",\"xyz\"],\"div\"],[\"alpha\"]],[1,\"alpha\",\">\",[5,\"0.001\"]]]]],[10,[6,\"d.vec4f\",[\"straightRGB\",\"alpha\"]]]]]},externals:{\"std.select\":()=>N,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Zk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,s.xyz,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[10,[6,\"overComposite\",[\"base\",[7,\"overlay\",\"xyz\"],\"overlayAlpha\"]]]]]},externals:{overComposite:()=>qs}})&&e.f)({})),Jk=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=__tsover_mul(t.w,__tsover_sub(1,o)),i=__tsover_add(o,a),l=Zl(Ta(Cr(t.xyz)));return S(Hs(oa(pl(Zl(Ta(Cr(s.xyz))).mul(o).add(l.mul(a))))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseWeight\",[1,[7,\"base\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"overlayAlpha\"]]],[13,\"finalAlpha\",[1,\"overlayAlpha\",\"+\",\"baseWeight\"]],[13,\"baseOklch\",[6,\"oklabToOklch\",[[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"base\",\"xyz\"]]]]]]]],[13,\"overlayOklch\",[6,\"oklabToOklch\",[[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"overlay\",\"xyz\"]]]]]]]],[13,\"blendedOklch\",[6,[7,[6,[7,\"overlayOklch\",\"mul\"],[\"overlayAlpha\"]],\"add\"],[[6,[7,\"baseOklch\",\"mul\"],[\"baseWeight\"]]]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[[6,\"oklchToOklab\",[\"blendedOklch\"]]]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"finalAlpha\"]]]]]},externals:{oklabToOklch:()=>Zl,rgbToOklab:()=>Ta,p3ToSRGB:()=>Cr,sRGBToP3:()=>Hs,oklabToRgb:()=>oa,oklchToOklab:()=>pl,\"d.vec4f\":()=>S}})&&e.f)({})),e4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=__tsover_mul(t.w,__tsover_sub(1,o)),i=__tsover_add(o,a),l=Ta(Cr(t.xyz));return S(Hs(oa(Ta(Cr(s.xyz)).mul(o).add(l.mul(a)))),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseWeight\",[1,[7,\"base\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"overlayAlpha\"]]],[13,\"finalAlpha\",[1,\"overlayAlpha\",\"+\",\"baseWeight\"]],[13,\"baseOklab\",[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"base\",\"xyz\"]]]]]],[13,\"overlayOklab\",[6,\"rgbToOklab\",[[6,\"p3ToSRGB\",[[7,\"overlay\",\"xyz\"]]]]]],[13,\"blendedOklab\",[6,[7,[6,[7,\"overlayOklab\",\"mul\"],[\"overlayAlpha\"]],\"add\"],[[6,[7,\"baseOklab\",\"mul\"],[\"baseWeight\"]]]]],[13,\"rgb\",[6,\"sRGBToP3\",[[6,\"oklabToRgb\",[\"blendedOklab\"]]]]],[10,[6,\"d.vec4f\",[\"rgb\",\"finalAlpha\"]]]]]},externals:{rgbToOklab:()=>Ta,p3ToSRGB:()=>Cr,sRGBToP3:()=>Hs,oklabToRgb:()=>oa,\"d.vec4f\":()=>S}})&&e.f)({})),t4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,t.xyz.mul(s.xyz),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"overlay\",\"xyz\"]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{overComposite:()=>qs}})&&e.f)({})),s4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,As(1,As(1,t.xyz).mul(As(1,s.xyz))),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,\"std.sub\",[[5,\"1\"],[6,[7,[6,\"std.sub\",[[5,\"1\"],[7,\"base\",\"xyz\"]]],\"mul\"],[[6,\"std.sub\",[[5,\"1\"],[7,\"overlay\",\"xyz\"]]]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.sub\":()=>As,overComposite:()=>qs}})&&e.f)({})),r4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,t.xyz.add(s.xyz),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,[7,[7,\"base\",\"xyz\"],\"add\"],[[7,\"overlay\",\"xyz\"]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{overComposite:()=>qs}})&&e.f)({})),o4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,K(t.xyz.mul(s.xyz).mul(2),As(1,As(1,t.xyz).mul(As(1,s.xyz)).mul(2)),Pi(A(.5),t.xyz)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"mult\",[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"overlay\",\"xyz\"]]],\"mul\"],[[5,\"2\"]]]],[13,\"scr\",[6,\"std.sub\",[[5,\"1\"],[6,[7,[6,[7,[6,\"std.sub\",[[5,\"1\"],[7,\"base\",\"xyz\"]]],\"mul\"],[[6,\"std.sub\",[[5,\"1\"],[7,\"overlay\",\"xyz\"]]]]],\"mul\"],[[5,\"2\"]]]]]],[13,\"blended\",[6,\"std.mix\",[\"mult\",\"scr\",[6,\"step3\",[[6,\"d.vec3f\",[[5,\"0.5\"]]],[7,\"base\",\"xyz\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.sub\":()=>As,\"std.mix\":()=>K,step3:()=>Pi,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),a4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,re(t.xyz.sub(s.xyz)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,\"std.abs\",[[6,[7,[7,\"base\",\"xyz\"],\"sub\"],[[7,\"overlay\",\"xyz\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.abs\":()=>re,overComposite:()=>qs}})&&e.f)({})),n4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=As(1,s.xyz);return qs(t,K(t.xyz,ye(t.xyz.div(a),A(1)),Pi(A(.001),a)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"oneMinusOverlay\",[6,\"std.sub\",[[5,\"1\"],[7,\"overlay\",\"xyz\"]]]],[13,\"dodged\",[6,\"std.mix\",[[7,\"base\",\"xyz\"],[6,\"std.min\",[[6,[7,[7,\"base\",\"xyz\"],\"div\"],[\"oneMinusOverlay\"]],[6,\"d.vec3f\",[[5,\"1\"]]]]],[6,\"step3\",[[6,\"d.vec3f\",[[5,\"0.001\"]]],\"oneMinusOverlay\"]]]]],[10,[6,\"overComposite\",[\"base\",\"dodged\",\"overlayAlpha\"]]]]]},externals:{\"std.sub\":()=>As,\"std.mix\":()=>K,\"std.min\":()=>ye,\"d.vec3f\":()=>A,step3:()=>Pi,overComposite:()=>qs}})&&e.f)({})),i4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,t.xyz.add(s.xyz).sub(t.xyz.mul(s.xyz).mul(2)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"add\"],[[7,\"overlay\",\"xyz\"]]],\"sub\"],[[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"overlay\",\"xyz\"]]],\"mul\"],[[5,\"2\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{overComposite:()=>qs}})&&e.f)({})),l4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=mn(t.xyz),i=mn(s.xyz),l=__tsover_sub(a,i);return qs(t,s.xyz.add(l),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseLum\",[6,\"luminance\",[[7,\"base\",\"xyz\"]]]],[13,\"overlayLum\",[6,\"luminance\",[[7,\"overlay\",\"xyz\"]]]],[13,\"lumDiff\",[1,\"baseLum\",\"-\",\"overlayLum\"]],[13,\"blended\",[6,[7,[7,\"overlay\",\"xyz\"],\"add\"],[\"lumDiff\"]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{luminance:()=>mn,overComposite:()=>qs}})&&e.f)({})),c4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=mn(t.xyz),i=mn(s.xyz),l=__tsover_div(i,__tsover_add(a,1e-4));return qs(t,t.xyz.mul(l),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseLum\",[6,\"luminance\",[[7,\"base\",\"xyz\"]]]],[13,\"overlayLum\",[6,\"luminance\",[[7,\"overlay\",\"xyz\"]]]],[13,\"lumRatio\",[1,\"overlayLum\",\"/\",[1,\"baseLum\",\"+\",[5,\"0.0001\"]]]],[13,\"blended\",[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[\"lumRatio\"]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{luminance:()=>mn,overComposite:()=>qs}})&&e.f)({})),d4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,ye(t.xyz,s.xyz),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,\"std.min\",[[7,\"base\",\"xyz\"],[7,\"overlay\",\"xyz\"]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.min\":()=>ye,overComposite:()=>qs}})&&e.f)({})),u4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,B(t.xyz,s.xyz),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,\"std.max\",[[7,\"base\",\"xyz\"],[7,\"overlay\",\"xyz\"]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.max\":()=>B,overComposite:()=>qs}})&&e.f)({})),p4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=B(As(1,As(1,t.xyz).div(s.xyz.add(1e-4))),A(0));return qs(t,K(A(0),a,Pi(A(1e-4),s.xyz)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"burned\",[6,\"std.max\",[[6,\"std.sub\",[[5,\"1\"],[6,[7,[6,\"std.sub\",[[5,\"1\"],[7,\"base\",\"xyz\"]]],\"div\"],[[6,[7,[7,\"overlay\",\"xyz\"],\"add\"],[[5,\"0.0001\"]]]]]]],[6,\"d.vec3f\",[[5,\"0\"]]]]]],[13,\"blended\",[6,\"std.mix\",[[6,\"d.vec3f\",[[5,\"0\"]]],\"burned\",[6,\"step3\",[[6,\"d.vec3f\",[[5,\"0.0001\"]]],[7,\"overlay\",\"xyz\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.max\":()=>B,\"std.sub\":()=>As,\"d.vec3f\":()=>A,\"std.mix\":()=>K,step3:()=>Pi,overComposite:()=>qs}})&&e.f)({})),m4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,B(t.xyz.add(s.xyz).add(-1),A(0)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"blended\",[6,\"std.max\",[[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"add\"],[[7,\"overlay\",\"xyz\"]]],\"add\"],[[4,\"-\",[5,\"1\"]]]],[6,\"d.vec3f\",[[5,\"0\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.max\":()=>B,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),_4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=t.xyz.mul(s.xyz).mul(2),i=t.xyz.mul(t.xyz),l=a.add(i.mul(As(1,s.xyz.mul(2)))),c=Ue(t.xyz);return qs(t,K(l,t.xyz.mul(As(1,s.xyz)).mul(2).add(c.mul(s.xyz.mul(2).sub(1))),Pi(A(.5),s.xyz)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"mult\",[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"overlay\",\"xyz\"]]],\"mul\"],[[5,\"2\"]]]],[13,\"baseSquared\",[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"base\",\"xyz\"]]]],[13,\"softDark\",[6,[7,\"mult\",\"add\"],[[6,[7,\"baseSquared\",\"mul\"],[[6,\"std.sub\",[[5,\"1\"],[6,[7,[7,\"overlay\",\"xyz\"],\"mul\"],[[5,\"2\"]]]]]]]]]],[13,\"baseSqrt\",[6,\"std.sqrt\",[[7,\"base\",\"xyz\"]]]],[13,\"softLightVal\",[6,[7,[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[6,\"std.sub\",[[5,\"1\"],[7,\"overlay\",\"xyz\"]]]]],\"mul\"],[[5,\"2\"]]],\"add\"],[[6,[7,\"baseSqrt\",\"mul\"],[[6,[7,[6,[7,[7,\"overlay\",\"xyz\"],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]]]]]],[13,\"blended\",[6,\"std.mix\",[\"softDark\",\"softLightVal\",[6,\"step3\",[[6,\"d.vec3f\",[[5,\"0.5\"]]],[7,\"overlay\",\"xyz\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.sub\":()=>As,\"std.sqrt\":()=>Ue,\"std.mix\":()=>K,step3:()=>Pi,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),f4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r);return qs(t,K(t.xyz.mul(s.xyz).mul(2),As(1,As(1,t.xyz).mul(As(1,s.xyz)).mul(2)),Pi(A(.5),s.xyz)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"mult\",[6,[7,[6,[7,[7,\"base\",\"xyz\"],\"mul\"],[[7,\"overlay\",\"xyz\"]]],\"mul\"],[[5,\"2\"]]]],[13,\"scr\",[6,\"std.sub\",[[5,\"1\"],[6,[7,[6,[7,[6,\"std.sub\",[[5,\"1\"],[7,\"base\",\"xyz\"]]],\"mul\"],[[6,\"std.sub\",[[5,\"1\"],[7,\"overlay\",\"xyz\"]]]]],\"mul\"],[[5,\"2\"]]]]]],[13,\"blended\",[6,\"std.mix\",[\"mult\",\"scr\",[6,\"step3\",[[6,\"d.vec3f\",[[5,\"0.5\"]]],[7,\"overlay\",\"xyz\"]]]]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{\"std.sub\":()=>As,\"std.mix\":()=>K,step3:()=>Pi,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),v4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=mn(t.xyz),i=mn(s.xyz),l=t.xyz.sub(a),c=s.xyz.sub(i),d=he(l),u=he(c);return qs(t,N(A(0),c.div(u),u>1e-4).mul(d).add(a),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseLum\",[6,\"luminance\",[[7,\"base\",\"xyz\"]]]],[13,\"overlayLum\",[6,\"luminance\",[[7,\"overlay\",\"xyz\"]]]],[13,\"baseChromaticity\",[6,[7,[7,\"base\",\"xyz\"],\"sub\"],[\"baseLum\"]]],[13,\"overlayChromaticity\",[6,[7,[7,\"overlay\",\"xyz\"],\"sub\"],[\"overlayLum\"]]],[13,\"baseChromaLength\",[6,\"std.length\",[\"baseChromaticity\"]]],[13,\"overlayChromaLength\",[6,\"std.length\",[\"overlayChromaticity\"]]],[13,\"overlayChromaDir\",[6,\"std.select\",[[6,\"d.vec3f\",[[5,\"0\"]]],[6,[7,\"overlayChromaticity\",\"div\"],[\"overlayChromaLength\"]],[1,\"overlayChromaLength\",\">\",[5,\"0.0001\"]]]]],[13,\"blended\",[6,[7,[6,[7,\"overlayChromaDir\",\"mul\"],[\"baseChromaLength\"]],\"add\"],[\"baseLum\"]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{luminance:()=>mn,\"std.length\":()=>he,\"std.select\":()=>N,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),h4=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s.w,r),a=mn(t.xyz),i=mn(s.xyz),l=t.xyz.sub(a),c=s.xyz.sub(i),d=he(l),u=N(A(0),l.div(d),d>1e-4),p=he(c);return qs(t,u.mul(p).add(a),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"overlay\"},{type:\"i\",name:\"opacity\"}],body:[0,[[13,\"overlayAlpha\",[1,[7,\"overlay\",\"w\"],\"*\",\"opacity\"]],[13,\"baseLum\",[6,\"luminance\",[[7,\"base\",\"xyz\"]]]],[13,\"overlayLum\",[6,\"luminance\",[[7,\"overlay\",\"xyz\"]]]],[13,\"baseChromaticity\",[6,[7,[7,\"base\",\"xyz\"],\"sub\"],[\"baseLum\"]]],[13,\"overlayChromaticity\",[6,[7,[7,\"overlay\",\"xyz\"],\"sub\"],[\"overlayLum\"]]],[13,\"baseChromaLength\",[6,\"std.length\",[\"baseChromaticity\"]]],[13,\"baseChromaDir\",[6,\"std.select\",[[6,\"d.vec3f\",[[5,\"0\"]]],[6,[7,\"baseChromaticity\",\"div\"],[\"baseChromaLength\"]],[1,\"baseChromaLength\",\">\",[5,\"0.0001\"]]]]],[13,\"overlayChromaLength\",[6,\"std.length\",[\"overlayChromaticity\"]]],[13,\"blended\",[6,[7,[6,[7,\"baseChromaDir\",\"mul\"],[\"overlayChromaLength\"]],\"add\"],[\"baseLum\"]]],[10,[6,\"overComposite\",[\"base\",\"blended\",\"overlayAlpha\"]]]]]},externals:{luminance:()=>mn,\"std.length\":()=>he,\"std.select\":()=>N,\"d.vec3f\":()=>A,overComposite:()=>qs}})&&e.f)({})),pc={normal:Zk,\"normal-oklch\":Jk,\"normal-oklab\":e4,multiply:t4,screen:s4,linearDodge:r4,overlay:o4,difference:a4,colorDodge:n4,exclusion:i4,color:l4,luminosity:c4,darken:d4,lighten:u4,colorBurn:p4,linearBurn:m4,softLight:_4,hardLight:f4,hue:v4,saturation:h4},XJ=(e,t,s=\"normal\",r=1)=>(pc[s]??pc.normal)(e,t,r),i1=A(.2126,.7152,.0722),LT={alpha:w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t.xyz,__tsover_mul(t.w,s.w))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"target\"},{type:\"i\",name:\"mask\"}],body:[0,[[10,[6,\"d.vec4f\",[[7,\"target\",\"xyz\"],[1,[7,\"target\",\"w\"],\"*\",[7,\"mask\",\"w\"]]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),alphaInverted:w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t.xyz,__tsover_mul(t.w,__tsover_sub(1,s.w)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"target\"},{type:\"i\",name:\"mask\"}],body:[0,[[10,[6,\"d.vec4f\",[[7,\"target\",\"xyz\"],[1,[7,\"target\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",[7,\"mask\",\"w\"]]]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),luminance:w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Se(s.xyz,i1);return S(t.xyz,__tsover_mul(t.w,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"target\"},{type:\"i\",name:\"mask\"}],body:[0,[[13,\"lum\",[6,\"std.dot\",[[7,\"mask\",\"xyz\"],\"LUMINANCE_WEIGHTS\"]]],[10,[6,\"d.vec4f\",[[7,\"target\",\"xyz\"],[1,[7,\"target\",\"w\"],\"*\",\"lum\"]]]]]]},externals:{\"std.dot\":()=>Se,LUMINANCE_WEIGHTS:()=>i1,\"d.vec4f\":()=>S}})&&e.f)({})),luminanceInverted:w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Se(s.xyz,i1);return S(t.xyz,__tsover_mul(t.w,__tsover_sub(1,r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"target\"},{type:\"i\",name:\"mask\"}],body:[0,[[13,\"lum\",[6,\"std.dot\",[[7,\"mask\",\"xyz\"],\"LUMINANCE_WEIGHTS\"]]],[10,[6,\"d.vec4f\",[[7,\"target\",\"xyz\"],[1,[7,\"target\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"lum\"]]]]]]]},externals:{\"std.dot\":()=>Se,LUMINANCE_WEIGHTS:()=>i1,\"d.vec4f\":()=>S}})&&e.f)({}))},HJ=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return A(t.x,t.y,t.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[10,[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),jJ=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return G(t.div(t.add(1)),A(0),A(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[10,[6,\"std.clamp\",[[6,[7,\"color\",\"div\"],[[6,[7,\"color\",\"add\"],[[5,\"1\"]]]]],[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec3f\":()=>A}})&&e.f)({})),KJ=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=B(t.sub(.004),A(0)),r=s.mul(s.mul(6.2).add(.5)),o=s.mul(s.mul(6.2).add(1.7)).add(.06);return vt(r.div(o),A(2.2))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"c\",[6,\"std.max\",[[6,[7,\"color\",\"sub\"],[[5,\"0.004\"]]],[6,\"d.vec3f\",[[5,\"0\"]]]]]],[13,\"a\",[6,[7,\"c\",\"mul\"],[[6,[7,[6,[7,\"c\",\"mul\"],[[5,\"6.2\"]]],\"add\"],[[5,\"0.5\"]]]]]],[13,\"b\",[6,[7,[6,[7,\"c\",\"mul\"],[[6,[7,[6,[7,\"c\",\"mul\"],[[5,\"6.2\"]]],\"add\"],[[5,\"1.7\"]]]]],\"add\"],[[5,\"0.06\"]]]],[10,[6,\"std.pow\",[[6,[7,\"a\",\"div\"],[\"b\"]],[6,\"d.vec3f\",[[5,\"2.2\"]]]]]]]]},externals:{\"std.max\":()=>B,\"d.vec3f\":()=>A,\"std.pow\":()=>vt}})&&e.f)({})),UT=tp(.59719,.076,.0284,.35458,.90834,.13383,.04823,.01566,.83777),OT=tp(1.60475,-.10208,-.00327,-.53108,1.10813,-.07276,-.07367,-.00605,1.07602),GT=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.mul(t.add(.0245786)).sub(90537e-9),r=t.mul(t.add(.432951).mul(.983729)).add(.238081);return s.div(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[13,\"a\",[6,[7,[6,[7,\"v\",\"mul\"],[[6,[7,\"v\",\"add\"],[[5,\"0.0245786\"]]]]],\"sub\"],[[5,\"0.000090537\"]]]],[13,\"b\",[6,[7,[6,[7,\"v\",\"mul\"],[[6,[7,[6,[7,\"v\",\"add\"],[[5,\"0.432951\"]]],\"mul\"],[[5,\"0.983729\"]]]]],\"add\"],[[5,\"0.238081\"]]]],[10,[6,[7,\"a\",\"div\"],[\"b\"]]]]]},externals:{}})&&e.f)({})),QJ=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.div(.6);return s=UT.mul(s),s=GT(s),s=OT.mul(s),G(s,A(0),A(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[12,\"c\",[6,[7,\"color\",\"div\"],[[5,\"0.6\"]]]],[2,\"c\",\"=\",[6,\"ACES_INPUT.mul\",[\"c\"]]],[2,\"c\",\"=\",[6,\"rrtAndODTFit\",[\"c\"]]],[2,\"c\",\"=\",[6,\"ACES_OUTPUT.mul\",[\"c\"]]],[10,[6,\"std.clamp\",[\"c\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]]]]},externals:{\"ACES_INPUT.mul\":()=>UT.mul,rrtAndODTFit:()=>GT,\"ACES_OUTPUT.mul\":()=>OT.mul,\"std.clamp\":()=>G,\"d.vec3f\":()=>A}})&&e.f)({})),FT=tp(.6274,.0691,.0164,.3293,.9195,.088,.0433,.0113,.8956),IT=tp(1.6605,-.1246,-.0182,-.5876,1.1329,-.1006,-.0728,-.0083,1.1187),YT=tp(.856627153315983,.137318972929847,.11189821299995,.0951212405381588,.761241990602591,.0767994186031903,.0482516061458583,.101439036467562,.811302368396859),WT=tp(1.1271005818144368,-.1413297634984383,-.14132976349843826,-.11060664309660323,1.157823702216272,-.11060664309660294,-.016493938717834573,-.016493938717834257,1.2519364065950405),f0=-12.47393,qT=4.026069,VT=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.mul(t),r=s.mul(s);return r.mul(s).mul(15.5).sub(r.mul(t).mul(40.14)).add(r.mul(31.96).sub(s.mul(t).mul(6.868)).add(s.mul(.4298).add(t.mul(.1191).sub(.00232))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"}],body:[0,[[13,\"x2\",[6,[7,\"x\",\"mul\"],[\"x\"]]],[13,\"x4\",[6,[7,\"x2\",\"mul\"],[\"x2\"]]],[10,[6,[7,[6,[7,[6,[7,[6,[7,\"x4\",\"mul\"],[\"x2\"]],\"mul\"],[[5,\"15.5\"]]],\"sub\"],[[6,[7,[6,[7,\"x4\",\"mul\"],[\"x\"]],\"mul\"],[[5,\"40.14\"]]]]],\"add\"],[[6,[7,[6,[7,[6,[7,\"x4\",\"mul\"],[[5,\"31.96\"]]],\"sub\"],[[6,[7,[6,[7,\"x2\",\"mul\"],[\"x\"]],\"mul\"],[[5,\"6.868\"]]]]],\"add\"],[[6,[7,[6,[7,\"x2\",\"mul\"],[[5,\"0.4298\"]]],\"add\"],[[6,[7,[6,[7,\"x\",\"mul\"],[[5,\"0.1191\"]]],\"sub\"],[[5,\"0.00232\"]]]]]]]]]]]]},externals:{}})&&e.f)({})),ZJ=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=FT.mul(t);return s=YT.mul(s),s=B(s,A(1e-10)),s=vb(s),s=s.sub(f0).div(__tsover_sub(qT,f0)),s=G(s,A(0),A(1)),s=VT(s),s=WT.mul(s),s=vt(B(s,A(0)),A(2.2)),s=IT.mul(s),G(s,A(0),A(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[12,\"c\",[6,\"LINEAR_SRGB_TO_LINEAR_REC2020.mul\",[\"color\"]]],[2,\"c\",\"=\",[6,\"AGX_INSET.mul\",[\"c\"]]],[2,\"c\",\"=\",[6,\"std.max\",[\"c\",[6,\"d.vec3f\",[[5,\"1e-10\"]]]]]],[2,\"c\",\"=\",[6,\"std.log2\",[\"c\"]]],[2,\"c\",\"=\",[6,[7,[6,[7,\"c\",\"sub\"],[\"AGX_MIN_EV\"]],\"div\"],[[1,\"AGX_MAX_EV\",\"-\",\"AGX_MIN_EV\"]]]],[2,\"c\",\"=\",[6,\"std.clamp\",[\"c\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[2,\"c\",\"=\",[6,\"agxContrastApprox\",[\"c\"]]],[2,\"c\",\"=\",[6,\"AGX_OUTSET.mul\",[\"c\"]]],[2,\"c\",\"=\",[6,\"std.pow\",[[6,\"std.max\",[\"c\",[6,\"d.vec3f\",[[5,\"0\"]]]]],[6,\"d.vec3f\",[[5,\"2.2\"]]]]]],[2,\"c\",\"=\",[6,\"LINEAR_REC2020_TO_LINEAR_SRGB.mul\",[\"c\"]]],[10,[6,\"std.clamp\",[\"c\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]]]]},externals:{\"LINEAR_SRGB_TO_LINEAR_REC2020.mul\":()=>FT.mul,\"AGX_INSET.mul\":()=>YT.mul,\"std.max\":()=>B,\"d.vec3f\":()=>A,\"std.log2\":()=>vb,AGX_MIN_EV:()=>f0,AGX_MAX_EV:()=>qT,\"std.clamp\":()=>G,agxContrastApprox:()=>VT,\"AGX_OUTSET.mul\":()=>WT.mul,\"std.pow\":()=>vt,\"LINEAR_REC2020_TO_LINEAR_SRGB.mul\":()=>IT.mul}})&&e.f)({})),l1=.76,NT=.15,g4=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ye(t.x,ye(t.y,t.z)),r=N(.04,__tsover_sub(s,__tsover_mul(__tsover_mul(6.25,s),s)),s<.08),o=t.sub(r),a=B(o.x,B(o.y,o.z));if(a<l1)return o;let i=__tsover_sub(1,l1),l=__tsover_sub(1,__tsover_div(__tsover_mul(i,i),__tsover_sub(__tsover_add(a,i),l1)));o=o.mul(__tsover_div(l,a));let c=__tsover_sub(1,__tsover_div(1,__tsover_add(__tsover_mul(NT,__tsover_sub(a,l)),1)));return K(o,A(l),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"x\",[6,\"std.min\",[[7,\"color\",\"x\"],[6,\"std.min\",[[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]]]],[13,\"offset\",[6,\"std.select\",[[5,\"0.04\"],[1,\"x\",\"-\",[1,[1,[5,\"6.25\"],\"*\",\"x\"],\"*\",\"x\"]],[1,\"x\",\"<\",[5,\"0.08\"]]]]],[12,\"c\",[6,[7,\"color\",\"sub\"],[\"offset\"]]],[13,\"peak\",[6,\"std.max\",[[7,\"c\",\"x\"],[6,\"std.max\",[[7,\"c\",\"y\"],[7,\"c\",\"z\"]]]]]],[11,[1,\"peak\",\"<\",\"NEUTRAL_START_COMPRESSION\"],[0,[[10,\"c\"]]]],[13,\"dd\",[1,[5,\"1\"],\"-\",\"NEUTRAL_START_COMPRESSION\"]],[13,\"newPeak\",[1,[5,\"1\"],\"-\",[1,[1,\"dd\",\"*\",\"dd\"],\"/\",[1,[1,\"peak\",\"+\",\"dd\"],\"-\",\"NEUTRAL_START_COMPRESSION\"]]]],[2,\"c\",\"=\",[6,[7,\"c\",\"mul\"],[[1,\"newPeak\",\"/\",\"peak\"]]]],[13,\"g\",[1,[5,\"1\"],\"-\",[1,[5,\"1\"],\"/\",[1,[1,\"NEUTRAL_DESATURATION\",\"*\",[1,\"peak\",\"-\",\"newPeak\"]],\"+\",[5,\"1\"]]]]],[10,[6,\"std.mix\",[\"c\",[6,\"d.vec3f\",[\"newPeak\"]],\"g\"]]]]]},externals:{\"std.min\":()=>ye,\"std.select\":()=>N,\"std.max\":()=>B,NEUTRAL_START_COMPRESSION:()=>l1,NEUTRAL_DESATURATION:()=>NT,\"std.mix\":()=>K,\"d.vec3f\":()=>A}})&&e.f)({})),$T={linear:HJ,reinhard:jJ,cineon:KJ,aces:QJ,agx:ZJ,neutral:g4,hable:w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.mul(16),r=s.mul(s.mul(.15).add(.05)).add(.004),o=s.mul(s.mul(.15).add(.5)).add(.06);return r.div(o).sub(__tsover_div(.02,.3))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"x\",[6,[7,\"color\",\"mul\"],[[5,\"16\"]]]],[13,\"num\",[6,[7,[6,[7,\"x\",\"mul\"],[[6,[7,[6,[7,\"x\",\"mul\"],[[5,\"0.15\"]]],\"add\"],[[5,\"0.05\"]]]]],\"add\"],[[5,\"0.004\"]]]],[13,\"den\",[6,[7,[6,[7,\"x\",\"mul\"],[[6,[7,[6,[7,\"x\",\"mul\"],[[5,\"0.15\"]]],\"add\"],[[5,\"0.5\"]]]]],\"add\"],[[5,\"0.06\"]]]],[10,[6,[7,[6,[7,\"num\",\"div\"],[\"den\"]],\"sub\"],[[1,[5,\"0.02\"],\"/\",[5,\"0.3\"]]]]]]]},externals:{}})&&e.f)({})),unreal:w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return t.div(t.add(.155)).mul(1.019)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[10,[6,[7,[6,[7,\"color\",\"div\"],[[6,[7,\"color\",\"add\"],[[5,\"0.155\"]]]]],\"mul\"],[[5,\"1.019\"]]]]]]},externals:{}})&&e.f)({}))},Xm=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return N(vt(t,A(.41666)).mul(1.055).sub(.055),t.mul(12.92),qm(t,A(.0031308)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"a\",[6,[7,[6,[7,[6,\"std.pow\",[\"color\",[6,\"d.vec3f\",[[5,\"0.41666\"]]]]],\"mul\"],[[5,\"1.055\"]]],\"sub\"],[[5,\"0.055\"]]]],[13,\"b\",[6,[7,\"color\",\"mul\"],[[5,\"12.92\"]]]],[13,\"factor\",[6,\"std.le\",[\"color\",[6,\"d.vec3f\",[[5,\"0.0031308\"]]]]]],[10,[6,\"std.select\",[\"a\",\"b\",\"factor\"]]]]]},externals:{\"std.pow\":()=>vt,\"d.vec3f\":()=>A,\"std.le\":()=>qm,\"std.select\":()=>N}})&&e.f)({})),Iu=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return N(vt(t.add(A(.055)).div(A(1.055)),A(2.4)),t.div(A(12.92)),qm(t,A(.04045)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"hi\",[6,\"std.pow\",[[6,[7,[6,[7,\"color\",\"add\"],[[6,\"d.vec3f\",[[5,\"0.055\"]]]]],\"div\"],[[6,\"d.vec3f\",[[5,\"1.055\"]]]]],[6,\"d.vec3f\",[[5,\"2.4\"]]]]]],[13,\"lo\",[6,[7,\"color\",\"div\"],[[6,\"d.vec3f\",[[5,\"12.92\"]]]]]],[13,\"isLow\",[6,\"std.le\",[\"color\",[6,\"d.vec3f\",[[5,\"0.04045\"]]]]]],[10,[6,\"std.select\",[\"hi\",\"lo\",\"isLow\"]]]]]},externals:{\"std.pow\":()=>vt,\"d.vec3f\":()=>A,\"std.le\":()=>qm,\"std.select\":()=>N}})&&e.f)({})),el=e=>e?e.offsetX!==0||e.offsetY!==0||e.rotation!==0||e.scale!==1||e.anchorX!==.5||e.anchorY!==.5:!1,JJ=w.fn([m,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_div(__tsover_mul(o,Math.PI),180),u=pe(d),p=ae(d),_=__tsover_div(__tsover_sub(t.x,i),a),f=__tsover_div(__tsover_sub(t.y,l),a),h=__tsover_mul(_,c),g=f,y=__tsover_sub(__tsover_mul(h,u),__tsover_mul(g,p)),b=__tsover_add(__tsover_mul(h,p),__tsover_mul(g,u));return m(__tsover_add(__tsover_sub(__tsover_div(y,c),s),i),__tsover_add(__tsover_sub(b,r),l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"offsetX\"},{type:\"i\",name:\"offsetY\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"anchorX\"},{type:\"i\",name:\"anchorY\"},{type:\"i\",name:\"aspectRatio\"}],body:[0,[[13,\"rotationRad\",[1,[1,\"rotation\",\"*\",\"Math.PI\"],\"/\",[5,\"180\"]]],[13,\"cosAngle\",[6,\"std.cos\",[\"rotationRad\"]]],[13,\"sinAngle\",[6,\"std.sin\",[\"rotationRad\"]]],[13,\"centeredX\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"anchorX\"],\"/\",\"scale\"]],[13,\"centeredY\",[1,[1,[7,\"uv\",\"y\"],\"-\",\"anchorY\"],\"/\",\"scale\"]],[13,\"squareX\",[1,\"centeredX\",\"*\",\"aspectRatio\"]],[13,\"squareY\",\"centeredY\"],[13,\"rotatedSquareX\",[1,[1,\"squareX\",\"*\",\"cosAngle\"],\"-\",[1,\"squareY\",\"*\",\"sinAngle\"]]],[13,\"rotatedSquareY\",[1,[1,\"squareX\",\"*\",\"sinAngle\"],\"+\",[1,\"squareY\",\"*\",\"cosAngle\"]]],[13,\"finalX\",[1,[1,[1,\"rotatedSquareX\",\"/\",\"aspectRatio\"],\"-\",\"offsetX\"],\"+\",\"anchorX\"]],[13,\"finalY\",[1,[1,\"rotatedSquareY\",\"-\",\"offsetY\"],\"+\",\"anchorY\"]],[10,[6,\"d.vec2f\",[\"finalX\",\"finalY\"]]]]]},externals:{\"Math.PI\":()=>Math.PI,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({}));w.fn([m,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_add(__tsover_sub(t.x,i),s),u=__tsover_add(__tsover_sub(t.y,l),r),p=__tsover_mul(d,c),_=u,f=-__tsover_div(__tsover_mul(o,Math.PI),180),h=pe(f),g=ae(f),y=__tsover_sub(__tsover_mul(p,h),__tsover_mul(_,g)),b=__tsover_add(__tsover_mul(p,g),__tsover_mul(_,h)),x=__tsover_mul(__tsover_div(y,c),a),P=__tsover_mul(b,a);return m(__tsover_add(x,i),__tsover_add(P,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"offsetX\"},{type:\"i\",name:\"offsetY\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"anchorX\"},{type:\"i\",name:\"anchorY\"},{type:\"i\",name:\"aspectRatio\"}],body:[0,[[13,\"centeredX\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"anchorX\"],\"+\",\"offsetX\"]],[13,\"centeredY\",[1,[1,[7,\"uv\",\"y\"],\"-\",\"anchorY\"],\"+\",\"offsetY\"]],[13,\"squareX\",[1,\"centeredX\",\"*\",\"aspectRatio\"]],[13,\"squareY\",\"centeredY\"],[13,\"rotationRad\",[4,\"-\",[1,[1,\"rotation\",\"*\",\"Math.PI\"],\"/\",[5,\"180\"]]]],[13,\"cosAngle\",[6,\"std.cos\",[\"rotationRad\"]]],[13,\"sinAngle\",[6,\"std.sin\",[\"rotationRad\"]]],[13,\"rotatedSquareX\",[1,[1,\"squareX\",\"*\",\"cosAngle\"],\"-\",[1,\"squareY\",\"*\",\"sinAngle\"]]],[13,\"rotatedSquareY\",[1,[1,\"squareX\",\"*\",\"sinAngle\"],\"+\",[1,\"squareY\",\"*\",\"cosAngle\"]]],[13,\"scaledX\",[1,[1,\"rotatedSquareX\",\"/\",\"aspectRatio\"],\"*\",\"scale\"]],[13,\"scaledY\",[1,\"rotatedSquareY\",\"*\",\"scale\"]],[10,[6,\"d.vec2f\",[[1,\"scaledX\",\"+\",\"anchorX\"],[1,\"scaledY\",\"+\",\"anchorY\"]]]]]]},externals:{\"Math.PI\":()=>Math.PI,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({}));var q3=(e,t,s)=>{let r=x=>x.unit===\"px\"?x.value/t:x.value,o=x=>x.unit===\"px\"?x.value/s:x.value,a=r(e.x),i=o(e.y),l=r(e.width),c=o(e.height),d=(x,P,C)=>C===\"end\"?1-x-P:C===\"center\"?.5+x-P/2:x,u=e.origin??\"top-left\",p=u.includes(\"left\")?\"start\":u.includes(\"right\")?\"end\":\"center\",_=u.includes(\"top\")?\"start\":u.includes(\"bottom\")?\"end\":\"center\",f=d(a,l,p),h=d(i,c,_),g=f+l/2,y=h+c/2,b=e.cornerRadius?e.cornerRadius.unit===\"px\"?e.cornerRadius.value/s:e.cornerRadius.value:0;return{centerX:g,centerY:y,halfWidthUV:l/2,halfHeightUV:c/2,cornerRadiusUV:b,rotation:e.rotation}},eee=w.fn([m,n,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=__tsover_sub(t.x,s),p=__tsover_sub(t.y,r),_=-__tsover_div(__tsover_mul(l,Math.PI),180),f=pe(_),h=ae(_),g=__tsover_mul(u,c),y=p,b=__tsover_sub(__tsover_mul(g,f),__tsover_mul(y,h)),x=__tsover_add(__tsover_mul(g,h),__tsover_mul(y,f)),P=__tsover_mul(o,c),C=a,E=__tsover_sub(P,i),R=__tsover_sub(C,i),U=__tsover_sub(re(b),E),L=__tsover_sub(re(x),R),k=m(B(U,0),B(L,0)),D=__tsover_sub(__tsover_add(he(k),ye(B(U,L),0)),i),O=B(d,1e-7);return N(__tsover_sub(1,be(-O,O,D)),N(n(0),n(1),D<=0),d<=0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"centerX\"},{type:\"i\",name:\"centerY\"},{type:\"i\",name:\"halfWidth\"},{type:\"i\",name:\"halfHeight\"},{type:\"i\",name:\"cornerRadius\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"aspectRatio\"},{type:\"i\",name:\"aaFeather\"}],body:[0,[[13,\"centeredX\",[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"]],[13,\"centeredY\",[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"]],[13,\"rotRad\",[4,\"-\",[1,[1,\"rotation\",\"*\",\"Math.PI\"],\"/\",[5,\"180\"]]]],[13,\"cosA\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"sqX\",[1,\"centeredX\",\"*\",\"aspectRatio\"]],[13,\"sqY\",\"centeredY\"],[13,\"rotatedX\",[1,[1,\"sqX\",\"*\",\"cosA\"],\"-\",[1,\"sqY\",\"*\",\"sinA\"]]],[13,\"rotatedY\",[1,[1,\"sqX\",\"*\",\"sinA\"],\"+\",[1,\"sqY\",\"*\",\"cosA\"]]],[13,\"halfWidthSq\",[1,\"halfWidth\",\"*\",\"aspectRatio\"]],[13,\"halfHeightSq\",\"halfHeight\"],[13,\"innerHalfW\",[1,\"halfWidthSq\",\"-\",\"cornerRadius\"]],[13,\"innerHalfH\",[1,\"halfHeightSq\",\"-\",\"cornerRadius\"]],[13,\"qx\",[1,[6,\"std.abs\",[\"rotatedX\"]],\"-\",\"innerHalfW\"]],[13,\"qy\",[1,[6,\"std.abs\",[\"rotatedY\"]],\"-\",\"innerHalfH\"]],[13,\"qClamped\",[6,\"d.vec2f\",[[6,\"std.max\",[\"qx\",[5,\"0\"]]],[6,\"std.max\",[\"qy\",[5,\"0\"]]]]]],[13,\"sdf\",[1,[1,[6,\"std.length\",[\"qClamped\"]],\"+\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",\"qy\"]],[5,\"0\"]]]],\"-\",\"cornerRadius\"]],[13,\"feather\",[6,\"std.max\",[\"aaFeather\",[5,\"1e-7\"]]]],[13,\"ramp\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"feather\"],\"feather\",\"sdf\"]]]],[13,\"binary\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"sdf\",\"<=\",[5,\"0\"]]]]],[10,[6,\"std.select\",[\"ramp\",\"binary\",[1,\"aaFeather\",\"<=\",[5,\"0\"]]]]]]]},externals:{\"Math.PI\":()=>Math.PI,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.abs\":()=>re,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.length\":()=>he,\"std.min\":()=>ye,\"std.smoothstep\":()=>be,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})),XT=w.fn([m,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_sub(t.x,s),d=__tsover_sub(t.y,r),u=-__tsover_div(__tsover_mul(i,Math.PI),180),p=pe(u),_=ae(u),f=__tsover_mul(c,l),h=d,g=__tsover_sub(__tsover_mul(f,p),__tsover_mul(h,_)),y=__tsover_add(__tsover_mul(f,_),__tsover_mul(h,p)),b=B(__tsover_mul(o,l),1e-6),x=B(a,1e-6);return m(__tsover_add(__tsover_div(g,__tsover_mul(b,2)),.5),__tsover_add(__tsover_div(y,__tsover_mul(x,2)),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"centerX\"},{type:\"i\",name:\"centerY\"},{type:\"i\",name:\"halfWidth\"},{type:\"i\",name:\"halfHeight\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"aspectRatio\"}],body:[0,[[13,\"centeredX\",[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"]],[13,\"centeredY\",[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"]],[13,\"rotRad\",[4,\"-\",[1,[1,\"rotation\",\"*\",\"Math.PI\"],\"/\",[5,\"180\"]]]],[13,\"cosA\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"sqX\",[1,\"centeredX\",\"*\",\"aspectRatio\"]],[13,\"sqY\",\"centeredY\"],[13,\"rotatedX\",[1,[1,\"sqX\",\"*\",\"cosA\"],\"-\",[1,\"sqY\",\"*\",\"sinA\"]]],[13,\"rotatedY\",[1,[1,\"sqX\",\"*\",\"sinA\"],\"+\",[1,\"sqY\",\"*\",\"cosA\"]]],[13,\"halfWidthSq\",[6,\"std.max\",[[1,\"halfWidth\",\"*\",\"aspectRatio\"],[5,\"0.000001\"]]]],[13,\"halfHeightSq\",[6,\"std.max\",[\"halfHeight\",[5,\"0.000001\"]]]],[13,\"localX\",[1,[1,\"rotatedX\",\"/\",[1,\"halfWidthSq\",\"*\",[5,\"2\"]]],\"+\",[5,\"0.5\"]]],[13,\"localY\",[1,[1,\"rotatedY\",\"/\",[1,\"halfHeightSq\",\"*\",[5,\"2\"]]],\"+\",[5,\"0.5\"]]],[10,[6,\"d.vec2f\",[\"localX\",\"localY\"]]]]]},externals:{\"Math.PI\":()=>Math.PI,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),y4=(e,t,s)=>{let r=s.rotationDeg*Math.PI/180,o=Math.cos(r),a=Math.sin(r),i=(e-s.centerX)*s.aspectRatio,l=t-s.centerY,c=i*o+l*a,d=i*-a+l*o,u=Math.max(s.halfWidthUV*s.aspectRatio,1e-6),p=Math.max(s.halfHeightUV,1e-6);return{x:c/(2*u)+.5,y:d/(2*p)+.5}},ym={x:{value:0,unit:\"uv\"},y:{value:0,unit:\"uv\"},width:{value:1,unit:\"uv\"},height:{value:1,unit:\"uv\"},origin:\"top-left\",rotation:0};function Ih(e){if(!e||typeof e!=\"object\")return;let t=(s,r)=>s&&typeof s==\"object\"?{value:s.value??r.value,unit:s.unit??r.unit}:{...r};return{x:t(e.x,ym.x),y:t(e.y,ym.y),width:t(e.width,ym.width),height:t(e.height,ym.height),origin:e.origin??\"top-left\",rotation:e.rotation??0,...e.cornerRadius?{cornerRadius:e.cornerRadius}:{},...e.lockAspect!==void 0?{lockAspect:e.lockAspect}:{}}}function Yh(e){if(!e||typeof e!=\"object\")return!1;let t=(s,r)=>!!s&&s.unit===\"uv\"&&s.value===r;return t(e.x,0)&&t(e.y,0)&&t(e.width,1)&&t(e.height,1)&&(e.rotation??0)===0&&(e.cornerRadius?.value??0)===0}var fs=class L1{_emit;constructor(t){this._emit=t}member(t){return new L1(s=>`${tee(this._emit(s))}.${t}`)}mul(t){return new L1(s=>`(${this._emit(s)} * ${HT(t,s)})`)}add(t){return new L1(s=>`(${this._emit(s)} + ${HT(t,s)})`)}};function tee(e){return/^[\\w.$]+$/.test(e)?e:`(${e})`}function HT(e,t){return typeof e==\"number\"?fo(e):e._emit(t)}function fo(e){return Number.isFinite(e)?Number.isInteger(e)?`${e}.0`:`${e}`:e>0?\"3.4e38\":e<0?\"-3.4e38\":\"0.0\"}var V3=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({linearClamp:{sampler:\"filtering\"},nearestClamp:{sampler:\"filtering\"},linearRepeat:{sampler:\"filtering\"},nearestRepeat:{sampler:\"filtering\"}}).$idx(2),\"SHARED_SAMPLER_LAYOUT\"),bm={uniforms:0,textures:1,samplers:2,external:3};function ca(e){return new fs(()=>e)}function $e(e){return new fs(()=>fo(e))}function W(e,t,s){return new fs(r=>`${r.external(e,t)}(${s.map(o=>o._emit(r)).join(\", \")})`)}function Ct(...e){return new fs(t=>`vec4f(${e.map(s=>b4(s,t)).join(\", \")})`)}function ea(e,t,s){return new fs(r=>`mix(${e._emit(r)}, ${t._emit(r)}, ${b4(s,r)})`)}function see(e,t){return new fs(s=>`array<${e}, ${t.length}>(${t.map(r=>r._emit(s)).join(\", \")})`)}var ree=0;function ns(e,t){let s=ree++;return new fs(r=>r.memo(`local:${s}`,()=>{let o=r.freshLocal(t);return r.statement(`let ${o} = ${e._emit(r)};`),o}))}var xt=ca(\"vec4f(0.0, 0.0, 0.0, 0.0)\"),v0=ca(\"vec4f(1.0, 1.0, 1.0, 0.0)\");function b4(e,t){return typeof e==\"number\"?fo(e):e._emit(t)}function Fl(e,t,s,r){return W(pc[s]??pc.normal,`blend_${hv(s)}`,[e,t,r])}function oee(e,t,s){return W(LT[s]??LT.alpha,`mask_${hv(s)}`,[e,t])}function hv(e){return e.replace(/[^a-zA-Z0-9_]/g,\"_\")}function jT(e,t,s,r){let o={},a=new Map,i=new Set,l=[],c=new Map,d=0,u={external(g,y){let b=a.get(g);if(b)return b;let x=hv(y);if(i.has(x)){let P=1;for(;i.has(`${x}_${P}`);)P++;x=`${x}_${P}`}return i.add(x),a.set(g,x),o[x]=g,x},statement(g){l.push(g)},freshLocal(g){return`${hv(g)}_${d++}`},memo(g,y){let b=c.get(g);if(b!==void 0)return b;let x=y();return c.set(g,x),x}},p=s?\"vec2f(in.uv.x, 1.0 - in.uv.y)\":\"in.uv\";l.push(`var uv = ${p};`);let _=e._emit(u),f=t(_,u),h=`${l.join(`\n`)}\n${_}\n${f}`;return r&&/\\b(?:tex|samp|ext)\\.\\$\\./.test(h)&&!/\\buni\\.\\$\\./.test(h)&&(u.external(r,\"uni\"),l.unshift(\"_ = uni.$.uniforms._sys.time;\")),{body:`{\n  ${l.join(`\n  `)}\n  return ${f};\n}`,externals:o}}function x4(e,t={}){let s={sampler:V3},r=[],o=[],a=[],i=[],l=new Set,c=[],d=[],u=[],p=[],_=0,f=0,h=0,g=0,y=new Set,b=e.store.layout;b&&b.$idx(bm.uniforms),s.uniform=b;let x=_e=>new fs(We=>(b&&We.external(b,\"uni\"),_e)),P=_e=>x(`uni.$.uniforms.${_e}`),C=_e=>x(e.store.gpuAccessor(_e,\"uni\")),E={uv:new fs(()=>\"uv\"),time:P(\"_sys.time\"),viewportSize:P(\"_sys.viewportSize\"),logicalViewportSize:P(\"_sys.logicalViewportSize\"),aspect:P(\"_sys.aspect\"),pointer:P(\"_sys.pointer\")},R=_e=>({key:_e,sample:(We,Le=\"linearClamp\")=>new fs(Be=>`textureSample(tex.$.${_e}, samp.$.${Le}, ${We._emit(Be)})`),sampleLevel:(We,Le=\"linearClamp\")=>new fs(Be=>`textureSampleLevel(tex.$.${_e}, samp.$.${Le}, ${We._emit(Be)}, 0.0)`),accessor:()=>new fs(()=>`tex.$.${_e}`),dimensions:()=>new fs(()=>`vec2f(textureDimensions(tex.$.${_e}))`)}),U=_e=>({key:_e,sample:(We,Le=\"linearClamp\")=>new fs(Be=>`textureSampleBaseClampToEdge(ext.$.${_e}, samp.$.${Le}, ${We._emit(Be)})`),sampleLevel:(We,Le=\"linearClamp\")=>new fs(Be=>`textureSampleBaseClampToEdge(ext.$.${_e}, samp.$.${Le}, ${We._emit(Be)})`),accessor:()=>new fs(()=>`ext.$.${_e}`),dimensions:()=>new fs(()=>`vec2f(textureDimensions(ext.$.${_e}))`)}),L=_e=>{let We=`rtt_${_++}`;return r.push({key:We,root:_e}),o.push({key:We,kind:\"rtt\"}),R(We)},k=new Map,D=(_e,We,Le)=>{let Be=`${_e.id}:${We}`,Oe=k.get(Be);if(Oe!==void 0)return Oe;let tt=Le?e.resolveCustomId(Le):null,_t=tt?L(pt(tt,new Set([_e.id]))):null;return k.set(Be,_t),_t},O=new Map,F=_e=>{let We=O.get(_e.id);if(We)return We;let Le={},Be=_e.metadata.maps;if(Be){let Oe={},tt=_e.definition.mapSampleUVs;if(tt){let _t={};for(let[Bt,Dt]of Object.entries(_e.handles))Dt.cpu||(_t[Bt]=C(Dt));Oe=tt({uniforms:_t,ctx:E})??{}}for(let[_t,Bt]of Object.entries(Be)){if(!(_t in _e.handles))continue;let Dt=X(Bt,_e,_t,Oe[_t]);Dt&&(Le[_t]=Dt)}}return O.set(_e.id,Le),Le},q=_e=>{let We=Hn(_e.id),Le=_e.metadata.maps;if(!Le||Object.keys(Le).length===0)return P(We);let Be=F(_e);return Object.keys(Be).length===0?P(We):new fs(Oe=>Oe.memo(`props:${_e.id}`,()=>{b&&Oe.external(b,\"uni\");let tt=Oe.freshLocal(`p_${We}`);Oe.statement(`var ${tt} = uni.$.uniforms.${We};`);for(let[_t,Bt]of Object.entries(Be))Oe.statement(`${tt}.${_t} = ${Bt._emit(Oe)};`);return tt}))},Y=_e=>{let We={};for(let[Le,Be]of Object.entries(_e.handles))We[Le]=Be.value;return We},V=_e=>{let We={};Hn(_e.id);let Le=q(_e);for(let[Oe,tt]of Object.entries(_e.handles)){if(tt.cpu)continue;let _t=_e.metadata.maps;_t&&Oe in _t?We[Oe]=Le.member(Oe):We[Oe]=C(tt)}let Be=_e.definition.extraFields;if(Be)for(let Oe of Object.keys(Be))We[Oe]=Le.member(Oe);return We},X=(_e,We,Le,Be)=>{let Oe=_e,tt=Hn(We.id);if(Oe.type===\"map\"){let _t=D(We,Le,Oe.source);return _t?iee(nee(_t.sample(Be??E.uv),Oe.channel??\"luminance\"),P(`${tt}._map_${Le}_inputMin`),P(`${tt}._map_${Le}_inputMax`),P(`${tt}._map_${Le}_outputMin`),P(`${tt}._map_${Le}_outputMax`),P(`${tt}._map_${Le}_curve`)):null}return Oe.type===\"mouse-position\"||Oe.type===\"mouse\"||Oe.type===\"auto-animate\"?P(`${tt}._smoothed_${Le}`):null},Q=(_e,We,Le,Be)=>{let Oe=We.metadata.mask;if(!Oe?.source)return _e;let tt=e.resolveCustomId(Oe.source);if(!tt)return _e;if(Le.has(tt))return console.error(\"[gpu] circular dependency in shader masks.\"),_e;let _t=new Set(Le);_t.add(We.id);let Bt=pt(tt,_t);return Be&&(Bt=L(Bt).sample(Be)),oee(_e,Bt,Oe.type??\"alpha\")},te=_e=>P(`${Hn(_e.id)}._opacity`),J=_e=>!!_e.metadata.boundingBox&&!!_e.definition.boundingBoxDeclaration&&!Yh(_e.metadata.boundingBox),oe=_e=>_e.handles.cutout?.value===1,le=_e=>!!_e.definition.requiresChild,se=_e=>!!_e.definition.blendWithChildren,Te=_e=>J(_e)&&!_e.definition.boundingBoxDeclaration?.boxResamplesContent&&le(_e)&&_e.metadata.blendMode===\"normal\"&&!oe(_e)&&!se(_e),de=()=>new fs(_e=>`(0.5 / max(${E.viewportSize.member(\"y\")._emit(_e)}, 1.0))`),xe=(_e,We=E.uv)=>{let Le=Hn(_e.id),Be=Oe=>P(`${Le}._bbox_${Oe}`);return W(eee,\"bboxClipMask\",[We,Be(\"centerX\"),Be(\"centerY\"),Be(\"halfWidth\"),Be(\"halfHeight\"),Be(\"cornerRadius\"),Be(\"rotation\"),Be(\"aspectRatio\"),de()])},Ae=(_e,We)=>{if(!J(We))return _e;if(We.definition.boundingBoxDeclaration?.boxResamplesContent){let Be=Hn(We.id),Oe=Dt=>P(`${Be}._bbox_${Dt}`),tt=L(_e),_t=W(XT,\"bboxGenUVContext\",[E.uv,Oe(\"centerX\"),Oe(\"centerY\"),Oe(\"halfWidth\"),Oe(\"halfHeight\"),Oe(\"rotation\"),Oe(\"aspectRatio\")]),Bt=W(Yt,\"unpremultiplyAlpha\",[tt.sample(_t)]);return Ct(Bt.member(\"rgb\"),Bt.member(\"a\").mul(xe(We)))}let Le=xe(We);return Ct(_e.member(\"rgb\"),_e.member(\"a\").mul(Le))},De=(_e,We)=>{let Le=We.metadata.transform;if(!Le||!el(Le))return _e;let Be=Hn(We.id),Oe=L(_e),tt=_t=>P(`${Be}._xform_${_t}`);return Ha(W(JJ,\"applyUVTransform\",[E.uv,tt(\"offsetX\"),tt(\"offsetY\"),tt(\"rotation\"),tt(\"scale\"),tt(\"anchorX\"),tt(\"anchorY\"),tt(\"aspectRatio\")]),_t=>Oe.sample(_t),lee[Le.edges]??0)},Ee=(_e,We,Le)=>J(Le)?We!==void 0&&Te(Le)?ea(We,_e,xe(Le)):Ae(_e,Le):De(_e,Le),ze=(_e,We)=>Te(We)?{transformedFragment:_e,clipMaskNode:xe(We)}:{transformedFragment:J(We)?Ae(_e,We):De(_e,We),clipMaskNode:void 0},Ie=(_e,We,Le,Be)=>{let Oe=te(Le),tt=se(Le)?_e!==void 0?Fl(_e,We,\"normal\",Oe):ea(xt,We,Oe):ea(_e??xt,We,Oe);return Be===void 0?tt:ea(_e??xt,tt,Be)},ke=(_e,We)=>{if(_e.definition.compute)try{let Le=_e.definition.compute(We);if(Le)return i.push({nodeId:_e.id,getComputeNodes:Le.getComputeNodes,bindInputs:Le.bindInputs}),Le.outputs}catch(Le){Sr(`[gpu] error initializing compute for node ${_e.id}:`,Le)}},Me=_e=>{let We=Hn(_e.id),Le=Be=>P(`${We}._bbox_${Be}`);return{centerX:Le(\"centerX\"),centerY:Le(\"centerY\"),halfWidth:Le(\"halfWidth\"),halfHeight:Le(\"halfHeight\"),rotation:Le(\"rotation\")}},we=_e=>{let We=_e.definition.boundingBoxDeclaration;return!!We&&(!!We.computeBounds||!!We.propBindings)},Ve=_e=>{if(!_e.definition.wantsBoundsParams)return{};let We={};J(_e)&&(We.ownBoundsParams=Me(_e));let Le=[...e.getChildren(_e.id)].sort((Be,Oe)=>Be.metadata.renderOrder-Oe.metadata.renderOrder);for(let Be of Le)if(Be.metadata.visible!==!1){if(J(Be)){We.childBoundsParams=Me(Be);break}if(we(Be)){let Oe=Hn(_e.id),tt=_t=>P(`${Oe}._childBounds_${_t}`);We.childBoundsParams={centerX:tt(\"centerX\"),centerY:tt(\"centerY\"),halfWidth:tt(\"halfWidth\"),halfHeight:tt(\"halfHeight\"),rotation:$e(0)};break}}return We},qe=!1,st=(_e,We,Le={})=>({noteCustomWgsl:()=>{qe=!0},props:q(_e),uniforms:V(_e),propValues:Y(_e),childNode:We,ctx:E,logicalViewportSize:E.logicalViewportSize,convertToTexture:L,declareTexture:Be=>{o.some(Oe=>Oe.key===Be.key)||o.push({key:Be.key,kind:\"media\",resource:Be._resource})},registerExternalTexture:Be=>{let Oe=`video_${h++}`;return a.push({key:Oe,getSource:Be}),U(Oe)},registerComputeTexture:Be=>{let Oe=`compute_${f++}`;o.push({key:Oe,kind:\"compute\",resource:{texture:Be}});let tt=Be?.props?.format;return(tt===\"r32float\"||tt===\"rg32float\"||tt===\"rgba32float\")&&y.add(Oe),R(Oe)},createMediaTexture:Be=>t.createMediaTexture?.(Be)??(()=>{throw new Error(\"[gpu] createMediaTexture unavailable (no TextureManager injected)\")})(),createDataTexture:Be=>t.createDataTexture?.(Be)??(()=>{throw new Error(\"[gpu] createDataTexture unavailable (no TextureManager injected)\")})(),registerMediaTexture:Be=>{let Oe=`media_${g++}`;return o.push({key:Oe,kind:\"media\",resource:{get texture(){return Be()}}}),R(Oe)},getCpuValue:Be=>{if((_e.metadata.maps?.[Be]||Be.startsWith(\"_animTime\"))&&t.resolveDriverCpuValue){let Oe=t.resolveDriverCpuValue(_e.id,Be);if(Oe!==void 0)return Oe}return _e.handles[Be]?.value},getMapInfo:Be=>{let Oe=_e.metadata.maps?.[Be];if(!Oe||Oe.type!==\"map\")return null;let tt=D(_e,Be,Oe.source);return tt?{sourceTexture:tt,channel:Oe.channel??\"luminance\",window:()=>t.resolveMapWindow?.(_e.id,Be)??{inputMin:typeof Oe.inputMin==\"number\"?Oe.inputMin:0,inputMax:typeof Oe.inputMax==\"number\"?Oe.inputMax:1,outputMin:typeof Oe.outputMin==\"number\"?Oe.outputMin:0,outputMax:typeof Oe.outputMax==\"number\"?Oe.outputMax:1,curve:typeof Oe.curve==\"number\"?Oe.curve:0}}:null},getLayerTexture:Be=>{let Oe=_e.handles[Be]?.value;return typeof Oe!=\"string\"||Oe===\"\"?null:D(_e,Be,Oe)},setExtraField:(Be,Oe)=>t.writeExtraField?.(_e.id,Be,Oe),onCleanup:Be=>p.push(Be),onBeforeRender:Be=>c.push(Be),onAfterRender:Be=>d.push(Be),onResize:Be=>u.push(Be),canvas:t.canvas,dimensions:t.dimensions??{width:1,height:1},gpu:t.gpu,domCanvas:_e.domCanvas,...Ve(_e),...Le});function pt(_e,We=new Set){let Le=e.getNode(_e);if(!Le)return xt;let Be=e.getChildren(_e);if(Be.length>0&&Le.definition.providesUVContextViaCompute){let Dt=yr(Le,Be,We);if(Dt!==null)return Dt}if(Be.length>0&&Le.definition.uvRemap){let Dt=gs(Le,Be);if(Dt!==null)return Dt;let Nt=or(Le,Be,We);if(Nt!==null)return Nt}if(Be.length===0){l.add(_e);let Dt=st(Le,void 0,{bboxMask:ht(Le),...nt(Le)});Dt.computeOutputs=ke(Le,Dt);let Nt=Le.definition.fragment(Dt);return Q(J(Le)?Ae(Nt,Le):De(Nt,Le),Le,We)}let Oe=[...Be].sort((Dt,Nt)=>Dt.metadata.renderOrder-Nt.metadata.renderOrder),tt=Gs(Oe,We);if(tt!==null){let Dt=st(Le,tt,{bboxMask:ht(Le)});Dt.computeOutputs=ke(Le,Dt);let Nt=Le.definition.fragment(Dt);return Q(J(Le)?Ae(Nt,Le):De(Nt,Le),Le,We)}let _t=Ze(Le,Oe,We);l.add(_e);let Bt=st(Le,_t,{bboxMask:ht(Le)});return Bt.computeOutputs=ke(Le,Bt),Q(Ee(Le.definition.fragment(Bt),_t,Le),Le,We)}function Ze(_e,We,Le){let Be;for(let Oe=0;Oe<We.length;Oe++){let tt=We[Oe];if(tt.metadata.visible===!1)continue;if(Be!==void 0&&St(tt)){let Nt=Ss(We,Oe,Be,Le);if(Nt){Be=Nt.node,Oe=Nt.endIndex;continue}}l.add(tt.id);let _t=e.getChildren(tt.id).length>0,Bt,Dt;if(_t)Bt=pt(tt.id,Le);else{let Nt=Be;tt.definition.requiresRTT&&Be!==void 0&&(Nt=Fl(xt,Be,\"normal\",$e(1))),tt.definition.acceptsOptionalChild&&(Nt=void 0);let Ks=st(tt,Nt,{bboxMask:ht(tt),...nt(tt)});Ks.computeOutputs=ke(tt,Ks);let Ds=ze(tt.definition.fragment(Ks),tt);Dt=Ds.clipMaskNode,Bt=Q(Ds.transformedFragment,tt,Le)}if(tt.definition.requiresChild&&tt.metadata.blendMode===\"normal\"&&!_t)Be=Ie(Be,Bt,tt,Dt);else{let Nt=tt.metadata.blendMode??\"normal\",Ks=tt.metadata.opacity??1;Be===void 0?Be=_e.definition.requiresRTT||Ks!==1?Fl(v0,Bt,Nt,te(tt)):Bt:Be=Fl(Be,Bt,Nt,te(tt))}}return Be}function nt(_e,We=E.uv){if(!J(_e))return{};if(!_e.definition.boundingBoxDeclaration?.supportsResizeFit)return{};let Le=Hn(_e.id),Be=Oe=>P(`${Le}._bbox_${Oe}`);return{uvContext:W(XT,\"bboxGenUVContext\",[We,Be(\"centerX\"),Be(\"centerY\"),Be(\"halfWidth\"),Be(\"halfHeight\"),Be(\"rotation\"),Be(\"aspectRatio\")]),effectiveViewportSize:Ct(Be(\"halfWidth\").mul(2).mul(E.viewportSize.member(\"x\")),Be(\"halfHeight\").mul(2).mul(E.viewportSize.member(\"y\")),0,0).member(\"xy\")}}function ht(_e){if(!(!J(_e)||!_e.definition.requiresRTT)&&!_e.definition.boundingBoxDeclaration?.supportsResizeFit)return xe(_e)}let yt=_e=>!!_e.metadata.mask||_e.metadata.opacity!==void 0&&_e.metadata.opacity<1||(_e.metadata.blendMode??\"normal\")!==\"normal\"||el(_e.metadata.transform)||J(_e),St=_e=>!!_e.definition.uvRemap&&e.getChildren(_e.id).length===0&&!yt(_e);function Mt(_e,We,Le){l.add(_e.id);let Be=ke(_e,st(_e,void 0));return _e.definition.uvRemap({uv:We,mask:Le,uniforms:V(_e),propValues:Y(_e),props:q(_e),aspect:E.aspect,onBeforeRender:Oe=>c.push(Oe),computeOutputs:Be})}function Rt(_e){let We=E.uv,Le=$e(1);for(let Be=_e.length-1;Be>=0;Be--){let Oe=Mt(_e[Be],We,Le);We=Oe.uv,Le=Oe.mask}return{uv:We,mask:Le}}let Kt=24,Wt=$e(1),ls=(_e,We)=>{if(We.left--<=0||_e.metadata.mask||el(_e.metadata.transform))return!1;let Le=e.getChildren(_e.id).filter(Be=>Be.metadata.visible!==!1);return Le.length===0?J(_e)&&_e.definition.boundingBoxDeclaration?.boxResamplesContent?!1:!!_e.definition.acceptsUVContext&&!_e.definition.requiresChild:!_e.definition.uvRemap||J(_e)?!1:Le.every(Be=>ls(Be,We))};function us(_e,We,Le){let Be=Le??Wt,Oe=Mt(_e,We,Be);return{uv:Oe.uv,mask:Oe.mask===Be?Le:Oe.mask}}let os=(_e,We)=>_e===void 0?We:We===void 0?_e:_e.mul(We);function bs(_e,We,Le){let Be=e.getChildren(_e.id).filter(Nt=>Nt.metadata.visible!==!1);if(Be.length>0){if(!_e.definition.uvRemap)return null;let Nt=us(_e,We,Le);return hs(Be,ns(Nt.uv,\"foldUV\"),Nt.mask)}if(!_e.definition.acceptsUVContext)return null;l.add(_e.id);let Oe=nt(_e,We),tt=st(_e,void 0,Object.keys(Oe).length>0?Oe:{uvContext:We});tt.computeOutputs=ke(_e,tt);let _t=_e.definition.fragment(tt),Bt=os(Le,J(_e)?xe(_e,We):void 0);if(Bt===void 0)return _t;let Dt=ns(_t,\"gen\");return Ct(Dt.member(\"rgb\"),Dt.member(\"a\").mul(Bt))}function hs(_e,We,Le){let Be=[..._e].sort((_t,Bt)=>_t.metadata.renderOrder-Bt.metadata.renderOrder),Oe;for(let _t of Be){let Bt=bs(_t,We,void 0);if(Bt===null)return null;let Dt=_t.metadata.blendMode??\"normal\",Nt=_t.metadata.opacity??1;Oe===void 0?Oe=Dt===\"normal\"&&Nt===1?Bt:Fl(v0,Bt,Dt,te(_t)):Oe=Fl(Oe,Bt,Dt,te(_t))}if(Oe===void 0)return null;if(Le===void 0)return Oe;let tt=ns(Oe,\"sibs\");return Ct(tt.member(\"rgb\"),tt.member(\"a\").mul(Le))}function gs(_e,We){if(!_e.definition.uvRemap||yt(_e))return null;let Le=We.filter(_t=>_t.metadata.visible!==!1);if(Le.length===0)return null;let Be={left:Kt};if(!Le.every(_t=>ls(_t,Be)))return null;l.add(_e.id);let{uv:Oe,mask:tt}=us(_e,E.uv,void 0);return hs(Le,ns(Oe,\"foldUV\"),tt)}function Gs(_e,We){let Le=_e.filter(Ds=>Ds.metadata.visible!==!1);if(!Le.some(Ds=>!!Ds.definition.uvRemap)||Le.length<2)return null;let Be=Le.length;for(let Ds=Le.length-1;Ds>=0&&St(Le[Ds]);Ds--)Be=Ds;let Oe=Le.slice(Be),tt=Le.slice(0,Be);if(Oe.length===0||tt.length!==1)return null;let _t=tt[0];if(!_t.definition.acceptsUVContext||e.getChildren(_t.id).length>0||yt(_t))return null;l.add(_t.id);for(let Ds of Oe)l.add(Ds.id);let{uv:Bt,mask:Dt}=Rt(Oe),Nt=st(_t,void 0,{uvContext:Bt});Nt.computeOutputs=ke(_t,Nt);let Ks=_t.definition.fragment(Nt);return Ct(Ks.member(\"rgb\"),Ks.member(\"a\").mul(Dt))}function Ss(_e,We,Le,Be){if(!St(_e[We]))return null;let Oe=[],tt=We;for(let Nt=We;Nt<_e.length;Nt++){let Ks=_e[Nt];if(Ks.metadata.visible!==!1){if(!St(Ks))break;Oe.push(Ks),tt=Nt}}if(Oe.length===0)return null;for(let Nt of Oe)l.add(Nt.id);let{uv:_t,mask:Bt}=Rt(Oe),Dt=L(Fl(xt,Le,\"normal\",$e(1))).sample(_t);return{node:Ct(Dt.member(\"rgb\"),Dt.member(\"a\").mul(Bt)),endIndex:tt}}function or(_e,We,Le){if(!_e.definition.uvRemap||yt(_e))return null;let Be=8,Oe=[],tt=_e,_t=We,Bt=null,Dt=null;for(let ar=0;ar<Be;ar++){Oe.push(tt);let Xo=_t.filter(za=>za.metadata.visible!==!1);if(Xo.length!==1)return null;let $r=Xo[0],cr=e.getChildren($r.id);if($r.definition.acceptsUVContext&&cr.length===0&&!yt($r)){Bt=$r;break}if($r.definition.uvRemap&&!yt($r)&&cr.length===1){tt=$r,_t=cr;continue}Dt=$r;break}if(!Bt&&!Dt)return null;for(let ar of Oe)l.add(ar.id);let{uv:Nt,mask:Ks}=Rt([...Oe].reverse()),Ds;if(Bt){l.add(Bt.id);let ar=st(Bt,void 0,{uvContext:Nt});ar.computeOutputs=ke(Bt,ar);let Xo=Bt.definition.fragment(ar);Ds=Ct(Xo.member(\"rgb\"),Xo.member(\"a\").mul(Ks))}else{let ar=L(Fl(v0,pt(Dt.id,Le),Dt.metadata.blendMode??\"normal\",te(Dt))).sample(Nt);Ds=Ct(ar.member(\"rgb\"),ar.member(\"a\").mul(Ks))}return Q(J(_e)?Ae(Ds,_e):De(Ds,_e),_e,Le)}function yr(_e,We,Le){let Be=cr=>!!cr.metadata.mask||cr.metadata.opacity!==void 0&&cr.metadata.opacity<1||(cr.metadata.blendMode??\"normal\")!==\"normal\"||el(cr.metadata.transform)||J(cr);if(!_e.definition.providesUVContextViaCompute||Be(_e))return null;let Oe=[],tt=_e,_t=null,Bt=We,Dt=8;for(let cr=0;cr<Dt;cr++){Oe.push(tt);let za=Bt.filter(Wn=>Wn.metadata.visible!==!1);if(za.length!==1)return null;let ia=za[0];if(Be(ia))return null;let Ra=e.getChildren(ia.id).length>0;if(ia.definition.acceptsUVContext&&!Ra){_t=ia;break}if(ia.definition.providesUVContextViaCompute){tt=ia,Bt=e.getChildren(ia.id);continue}return null}if(!_t)return null;l.add(_t.id);for(let cr of Oe)l.add(cr.id);let Nt=[],Ks;for(let cr=0;cr<Oe.length;cr++){let za=Oe[cr],ia=st(za,void 0,{computeOutputs:Ks?{_upstreamUVMap:Ks}:void 0}),Ra=ke(za,ia);if(!Ra||!Ra.uvMaskMap)return null;ia.computeOutputs=Ra,Nt.push(ia),Ks=Ra.uvMaskMap}let Ds=Ks.sample(E.uv),ar=Ct(Ds.member(\"r\"),Ds.member(\"g\"),0,0).member(\"xy\"),Xo=st(_t,void 0,{uvContext:ar});Xo.computeOutputs=ke(_t,Xo);let $r=_t.definition.fragment(Xo);for(let cr=Oe.length-1;cr>=0;cr--)Nt[cr].childNode=$r,Nt[cr].uvPropagationActive=!0,$r=Oe[cr].definition.fragment(Nt[cr]);return Q(J(_e)?Ae($r,_e):De($r,_e),_e,Le)}let lr=e.rootId?pt(e.rootId):xt,Ur=r.map(_e=>{let{body:We,externals:Le}=jT(_e.root,Be=>Be,t.flipY??!0,s.uniform);return{textureKey:_e.key,fragment:KT(We,Le,`rtt_${_e.key}`,s.sampler,y)}}),so=t.toneMapping??\"linear\",ro=jT(lr,(_e,We)=>aee(_e,We,so,t.premultiplyAlpha??!1),t.flipY??!0,s.uniform);return{computeSteps:i,rttPasses:Ur,finalPass:KT(ro.body,ro.externals,\"final\",s.sampler,y),textures:o,externalTextures:a,layouts:{uniforms:s.uniform,samplers:s.sampler},onBeforeRender:c,onAfterRender:d,onResize:u,onCleanup:p,composedNodeIds:l,usesCustomWgsl:qe}}function aee(e,t,s,r){let o=t.external($T[s]??$T.linear,`tone_${s}`),a=t.external(Xm,\"linearToSrgb\"),i=t.freshLocal(\"composed\");t.statement(`var ${i} = ${e};`);let l=`${a}(${o}(${i}.rgb))`;return r?`vec4f(${l} * ${i}.a, ${i}.a)`:`vec4f(${l}, ${i}.a)`}function KT(e,t,s,r,o=new Set){let a=QT(e,/\\btex\\.\\$\\.(\\w+)/g),i=QT(e,/\\bext\\.\\$\\.(\\w+)/g),l=/\\bsamp\\.\\$\\./.test(e),c;if(a.length>0){let u={};for(let p of a)u[p]=o.has(p)?{texture:Zt(n),sampleType:\"unfilterable-float\"}:{texture:Zt(n)};c=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(w.bindGroupLayout(u).$idx(bm.textures),\"textureLayout\"),t.tex=c}let d;if(i.length>0){let u={};for(let f of i)u[f]={externalTexture:RM()};let p=new Set([bm.uniforms]);a.length>0&&p.add(bm.textures),l&&p.add(bm.samplers);let _=0;for(;p.has(_);)_++;d=(globalThis.__TYPEGPU_AUTONAME__??(f=>f))(w.bindGroupLayout(u).$idx(_),\"externalLayout\"),t.ext=d}return l&&(t.samp=r),{body:e,externals:t,entry:w.fragmentFn({in:{uv:m},out:S})(e).$uses(t).$name(s),reads:a,externalReads:i,usesSamplers:l,textureLayout:c,externalLayout:d}}function QT(e,t){let s=new Set;for(let r of e.matchAll(t))s.add(r[1]);return[...s]}function nee(e,t){switch(t){case\"alpha\":return e.member(\"a\");case\"alphaInverted\":return new fs(s=>`(1.0 - ${e._emit(s)}.a)`);case\"luminanceInverted\":return new fs(s=>`(1.0 - dot(${e._emit(s)}.rgb, vec3f(0.2126, 0.7152, 0.0722)))`);default:return new fs(s=>`dot(${e._emit(s)}.rgb, vec3f(0.2126, 0.7152, 0.0722))`)}}function iee(e,t,s,r,o,a){return new fs(i=>{let l=e._emit(i),c=t._emit(i),d=s._emit(i),u=r._emit(i),p=o._emit(i),_=a._emit(i);return`mix(${u}, ${p}, ${`pow(${`clamp((${l} - ${c}) / max(${d} - ${c}, 0.0001), 0.0, 1.0)`}, pow(2.0, -${_} * 2.0))`})`})}var lee={stretch:0,transparent:1,mirror:2,wrap:3};function A4(e,t={}){let s=[];s.push(`tone:${t.toneMapping??\"linear\"}`),s.push(`flipY:${t.flipY??!0}`),s.push(`premul:${t.premultiplyAlpha??!1}`);let r=o=>{let a=e.getNode(o);if(!a)return;let i=a.metadata,l=a.definition,c=el(i.transform),d=!!i.boundingBox&&!!l.boundingBoxDeclaration&&!Yh(i.boundingBox),u=(i.opacity??1)===0?\"zero\":\"nonzero\",p=i.mask?`${i.mask.source}:${i.mask.type}`:\"none\",_=[];for(let[f,h]of Object.entries(l.props??{}))if(h.compileTime){let g=a.handles[f];_.push(`${f}=${g?String(g.value):\"?\"}`)}s.push([`id:${a.id}`,`parent:${a.parentId??\"\"}`,`name:${a.componentName}`,`rev:${l.revision??\"\"}`,`blend:${i.blendMode??\"normal\"}`,`mask:${p}`,`order:${i.renderOrder??0}`,`visible:${i.visible!==!1}`,`opacity:${u}`,`xform:${c}${c?`:${i.transform?.edges??\"transparent\"}`:\"\"}`,`bbox:${d}:${d?l.boundingBoxDeclaration?.boxResamplesContent?\"resample\":l.boundingBoxDeclaration?.supportsResizeFit?\"resize\":\"clip\":\"\"}`,`rtt:${!!l.requiresRTT}`,`maps:${i.maps?Object.keys(i.maps).sort().join(\",\"):\"\"}`,`ct:${_.sort().join(\"|\")}`].join(\"|\"));for(let f of e.getChildren(o).sort((h,g)=>h.metadata.renderOrder-g.metadata.renderOrder))r(f.id)};return e.rootId&&r(e.rootId),s}var cee=w.fn([n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(t,s),o=__tsover_add(ie(__tsover_sub(r,.5)),.5),a=__tsover_sub(r,o),i=__tsover_add(1,__tsover_mul(__tsover_mul(a,a),__tsover_add(-2.5,__tsover_mul(1.5,a)))),l=__tsover_mul(a,__tsover_add(.5,__tsover_mul(a,__tsover_sub(2,__tsover_mul(1.5,a))))),c=B(__tsover_add(i,l),1e-6),d=__tsover_div(l,c);return A(__tsover_div(__tsover_sub(o,1),s),__tsover_div(__tsover_add(o,d),s),__tsover_div(__tsover_add(o,2),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"texSize\"}],body:[0,[[13,\"samplePos\",[1,\"uv\",\"*\",\"texSize\"]],[13,\"texPos1\",[1,[6,\"std.floor\",[[1,\"samplePos\",\"-\",[5,\"0.5\"]]]],\"+\",[5,\"0.5\"]]],[13,\"f\",[1,\"samplePos\",\"-\",\"texPos1\"]],[13,\"w1\",[1,[5,\"1\"],\"+\",[1,[1,\"f\",\"*\",\"f\"],\"*\",[1,[4,\"-\",[5,\"2.5\"]],\"+\",[1,[5,\"1.5\"],\"*\",\"f\"]]]]],[13,\"w2\",[1,\"f\",\"*\",[1,[5,\"0.5\"],\"+\",[1,\"f\",\"*\",[1,[5,\"2\"],\"-\",[1,[5,\"1.5\"],\"*\",\"f\"]]]]]],[13,\"w12\",[6,\"std.max\",[[1,\"w1\",\"+\",\"w2\"],[5,\"0.000001\"]]]],[13,\"offset12\",[1,\"w2\",\"/\",\"w12\"]],[10,[6,\"d.vec3f\",[[1,[1,\"texPos1\",\"-\",[5,\"1\"]],\"/\",\"texSize\"],[1,[1,\"texPos1\",\"+\",\"offset12\"],\"/\",\"texSize\"],[1,[1,\"texPos1\",\"+\",[5,\"2\"]],\"/\",\"texSize\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.max\":()=>B,\"d.vec3f\":()=>A}})&&e.f)({})),dee=w.fn([n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(t,s),o=__tsover_add(ie(__tsover_sub(r,.5)),.5),a=__tsover_sub(r,o),i=__tsover_mul(a,__tsover_add(-.5,__tsover_mul(a,__tsover_sub(1,__tsover_mul(.5,a))))),l=__tsover_add(1,__tsover_mul(__tsover_mul(a,a),__tsover_add(-2.5,__tsover_mul(1.5,a)))),c=__tsover_mul(a,__tsover_add(.5,__tsover_mul(a,__tsover_sub(2,__tsover_mul(1.5,a))))),d=__tsover_mul(__tsover_mul(a,a),__tsover_add(-.5,__tsover_mul(.5,a)));return A(i,__tsover_add(l,c),d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"texSize\"}],body:[0,[[13,\"samplePos\",[1,\"uv\",\"*\",\"texSize\"]],[13,\"texPos1\",[1,[6,\"std.floor\",[[1,\"samplePos\",\"-\",[5,\"0.5\"]]]],\"+\",[5,\"0.5\"]]],[13,\"f\",[1,\"samplePos\",\"-\",\"texPos1\"]],[13,\"w0\",[1,\"f\",\"*\",[1,[4,\"-\",[5,\"0.5\"]],\"+\",[1,\"f\",\"*\",[1,[5,\"1\"],\"-\",[1,[5,\"0.5\"],\"*\",\"f\"]]]]]],[13,\"w1\",[1,[5,\"1\"],\"+\",[1,[1,\"f\",\"*\",\"f\"],\"*\",[1,[4,\"-\",[5,\"2.5\"]],\"+\",[1,[5,\"1.5\"],\"*\",\"f\"]]]]],[13,\"w2\",[1,\"f\",\"*\",[1,[5,\"0.5\"],\"+\",[1,\"f\",\"*\",[1,[5,\"2\"],\"-\",[1,[5,\"1.5\"],\"*\",\"f\"]]]]]],[13,\"w3\",[1,[1,\"f\",\"*\",\"f\"],\"*\",[1,[4,\"-\",[5,\"0.5\"]],\"+\",[1,[5,\"0.5\"],\"*\",\"f\"]]]],[10,[6,\"d.vec3f\",[\"w0\",[1,\"w1\",\"+\",\"w2\"],\"w3\"]]]]]},externals:{\"std.floor\":()=>ie,\"d.vec3f\":()=>A}})&&e.f)({})),ZT=new Map;function uee(e,t,s){let r=gt({uvA:m,uvB:m,uvC:m,uvD:m,w:S}).$name(s);return{Taps:r,setup:w.fn([m],r)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=(a=>{\"use gpu\";let i=n(e),l=__tsover_sub(__tsover_mul(a.x,i),.5),c=__tsover_sub(__tsover_mul(a.y,i),.5),d=ie(l),u=ie(c),p=__tsover_sub(l,d),_=__tsover_sub(c,u),f=__tsover_sub(1,p),h=__tsover_mul(__tsover_mul(__tsover_mul(f,f),f),.16666667),g=__tsover_add(__tsover_sub(__tsover_mul(__tsover_mul(__tsover_mul(p,p),p),.5),__tsover_mul(p,p)),.66666667),y=__tsover_mul(__tsover_mul(__tsover_mul(p,p),p),.16666667),b=__tsover_sub(__tsover_sub(__tsover_sub(1,h),g),y),x=__tsover_sub(1,_),P=__tsover_mul(__tsover_mul(__tsover_mul(x,x),x),.16666667),C=__tsover_add(__tsover_sub(__tsover_mul(__tsover_mul(__tsover_mul(_,_),_),.5),__tsover_mul(_,_)),.66666667),E=__tsover_mul(__tsover_mul(__tsover_mul(_,_),_),.16666667),R=__tsover_sub(__tsover_sub(__tsover_sub(1,P),C),E),U=__tsover_add(h,g),L=__tsover_add(b,y),k=__tsover_add(P,C),D=__tsover_add(R,E),O=__tsover_add(__tsover_sub(d,1),__tsover_div(g,U)),F=__tsover_add(__tsover_add(d,1),__tsover_div(y,L)),q=__tsover_add(__tsover_sub(u,1),__tsover_div(C,k)),Y=__tsover_add(__tsover_add(u,1),__tsover_div(E,D));return r({uvA:m(__tsover_div(__tsover_add(O,.5),i),__tsover_div(__tsover_add(q,.5),i)),uvB:m(__tsover_div(__tsover_add(F,.5),i),__tsover_div(__tsover_add(q,.5),i)),uvC:m(__tsover_div(__tsover_add(O,.5),i),__tsover_div(__tsover_add(Y,.5),i)),uvD:m(__tsover_div(__tsover_add(F,.5),i),__tsover_div(__tsover_add(Y,.5),i)),w:S(__tsover_mul(U,k),__tsover_mul(L,k),__tsover_mul(U,D),__tsover_mul(L,D))})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"gf\",[6,\"d.f32\",[\"gridSize\"]]],[13,\"tx\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"gf\"],\"-\",[5,\"0.5\"]]],[13,\"ty\",[1,[1,[7,\"uv\",\"y\"],\"*\",\"gf\"],\"-\",[5,\"0.5\"]]],[13,\"pxf\",[6,\"std.floor\",[\"tx\"]]],[13,\"pyf\",[6,\"std.floor\",[\"ty\"]]],[13,\"fx\",[1,\"tx\",\"-\",\"pxf\"]],[13,\"fy\",[1,\"ty\",\"-\",\"pyf\"]],[13,\"ofx\",[1,[5,\"1\"],\"-\",\"fx\"]],[13,\"wx0\",[1,[1,[1,\"ofx\",\"*\",\"ofx\"],\"*\",\"ofx\"],\"*\",[5,\"0.16666667\"]]],[13,\"wx1\",[1,[1,[1,[1,[1,\"fx\",\"*\",\"fx\"],\"*\",\"fx\"],\"*\",[5,\"0.5\"]],\"-\",[1,\"fx\",\"*\",\"fx\"]],\"+\",[5,\"0.66666667\"]]],[13,\"wx3\",[1,[1,[1,\"fx\",\"*\",\"fx\"],\"*\",\"fx\"],\"*\",[5,\"0.16666667\"]]],[13,\"wx2\",[1,[1,[1,[5,\"1\"],\"-\",\"wx0\"],\"-\",\"wx1\"],\"-\",\"wx3\"]],[13,\"ofy\",[1,[5,\"1\"],\"-\",\"fy\"]],[13,\"wy0\",[1,[1,[1,\"ofy\",\"*\",\"ofy\"],\"*\",\"ofy\"],\"*\",[5,\"0.16666667\"]]],[13,\"wy1\",[1,[1,[1,[1,[1,\"fy\",\"*\",\"fy\"],\"*\",\"fy\"],\"*\",[5,\"0.5\"]],\"-\",[1,\"fy\",\"*\",\"fy\"]],\"+\",[5,\"0.66666667\"]]],[13,\"wy3\",[1,[1,[1,\"fy\",\"*\",\"fy\"],\"*\",\"fy\"],\"*\",[5,\"0.16666667\"]]],[13,\"wy2\",[1,[1,[1,[5,\"1\"],\"-\",\"wy0\"],\"-\",\"wy1\"],\"-\",\"wy3\"]],[13,\"g0x\",[1,\"wx0\",\"+\",\"wx1\"]],[13,\"g1x\",[1,\"wx2\",\"+\",\"wx3\"]],[13,\"g0y\",[1,\"wy0\",\"+\",\"wy1\"]],[13,\"g1y\",[1,\"wy2\",\"+\",\"wy3\"]],[13,\"h0x\",[1,[1,\"pxf\",\"-\",[5,\"1\"]],\"+\",[1,\"wx1\",\"/\",\"g0x\"]]],[13,\"h1x\",[1,[1,\"pxf\",\"+\",[5,\"1\"]],\"+\",[1,\"wx3\",\"/\",\"g1x\"]]],[13,\"h0y\",[1,[1,\"pyf\",\"-\",[5,\"1\"]],\"+\",[1,\"wy1\",\"/\",\"g0y\"]]],[13,\"h1y\",[1,[1,\"pyf\",\"+\",[5,\"1\"]],\"+\",[1,\"wy3\",\"/\",\"g1y\"]]],[10,[6,\"Taps\",[[104,{uvA:[6,\"d.vec2f\",[[1,[1,\"h0x\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"],[1,[1,\"h0y\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"]]],uvB:[6,\"d.vec2f\",[[1,[1,\"h1x\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"],[1,[1,\"h0y\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"]]],uvC:[6,\"d.vec2f\",[[1,[1,\"h0x\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"],[1,[1,\"h1y\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"]]],uvD:[6,\"d.vec2f\",[[1,[1,\"h1x\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"],[1,[1,\"h1y\",\"+\",[5,\"0.5\"]],\"/\",\"gf\"]]],w:[6,\"d.vec4f\",[[1,\"g0x\",\"*\",\"g0y\"],[1,\"g1x\",\"*\",\"g0y\"],[1,\"g0x\",\"*\",\"g1y\"],[1,\"g1x\",\"*\",\"g1y\"]]]}]]]]]]},externals:{\"d.f32\":()=>n,gridSize:()=>e,\"std.floor\":()=>ie,Taps:()=>r,\"d.vec2f\":()=>m,\"d.vec4f\":()=>S}})&&o.f)({})).$name(t)}}function w4(e,t={}){let s=t.fnName??\"bSplineTapSetup\",r=t.structName??\"BSplineTaps\",o=`${e}|${s}|${r}`,a=ZT.get(o);if(a)return a;let i=uee(e,s,r);return ZT.set(o,i),i}var pee=0,g_=(e,t,s=\"linearClamp\",r=\"positive\")=>{let o=pee++;return new fs(a=>a.memo(`catmullRom:${o}`,()=>{let i=a.freshLocal(\"crUV\"),l=a.freshLocal(\"crSize\"),c=a.freshLocal(\"crPosX\"),d=a.freshLocal(\"crPosY\"),u=a.freshLocal(\"crWx\"),p=a.freshLocal(\"crWy\"),_=a.freshLocal(\"crOut\");a.statement(`let ${i} = ${t._emit(a)};`),a.statement(`let ${l} = ${e.dimensions()._emit(a)};`);let f=a.external(cee,\"catmullRomAxisPositions\"),h=a.external(dee,\"catmullRomAxisWeights\");a.statement(`let ${c} = ${f}(${i}.x, ${l}.x);`),a.statement(`let ${d} = ${f}(${i}.y, ${l}.y);`),a.statement(`let ${u} = ${h}(${i}.x, ${l}.x);`),a.statement(`let ${p} = ${h}(${i}.y, ${l}.y);`),a.statement(`var ${_} = vec4f(0.0, 0.0, 0.0, 0.0);`);let g=[\"x\",\"y\",\"z\"];for(let y of[0,1,2])for(let b of[0,1,2]){let x=new fs(()=>`vec2f(${c}.${g[b]}, ${d}.${g[y]})`),P=`(${u}.${g[b]} * ${p}.${g[y]})`;a.statement(`${_} += ${e.sample(x,s)._emit(a)} * ${P};`)}return a.statement(r===\"unit\"?`${_} = clamp(${_}, vec4f(0.0, 0.0, 0.0, 0.0), vec4f(1.0, 1.0, 1.0, 1.0));`:`${_} = max(${_}, vec4f(0.0, 0.0, 0.0, 0.0));`),_}))},N3=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return G(t,m(0,0),m(1,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[10,[6,\"std.clamp\",[\"uv\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m}})&&e.f)({})),Hm=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=An(re(t.x),2),r=An(re(t.y),2);return m(N(s,__tsover_sub(2,s),s>=1),N(r,__tsover_sub(2,r),r>=1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"mirrorX\",[6,\"std.mod\",[[6,\"std.abs\",[[7,\"uv\",\"x\"]]],[5,\"2\"]]]],[13,\"mirrorY\",[6,\"std.mod\",[[6,\"std.abs\",[[7,\"uv\",\"y\"]]],[5,\"2\"]]]],[10,[6,\"d.vec2f\",[[6,\"std.select\",[\"mirrorX\",[1,[5,\"2\"],\"-\",\"mirrorX\"],[1,\"mirrorX\",\">=\",[5,\"1\"]]]],[6,\"std.select\",[\"mirrorY\",[1,[5,\"2\"],\"-\",\"mirrorY\"],[1,\"mirrorY\",\">=\",[5,\"1\"]]]]]]]]]},externals:{\"std.mod\":()=>An,\"std.abs\":()=>re,\"d.vec2f\":()=>m,\"std.select\":()=>N}})&&e.f)({})),jm=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Re(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[10,[6,\"std.fract\",[\"uv\"]]]]]},externals:{\"std.fract\":()=>Re}})&&e.f)({})),$3=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=__tsover_mul(Et(0,t.x),Et(t.x,1)),r=__tsover_mul(Et(0,t.y),Et(t.y,1));return __tsover_mul(s,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"inX\",[1,[6,\"std.step\",[[5,\"0\"],[7,\"uv\",\"x\"]]],\"*\",[6,\"std.step\",[[7,\"uv\",\"x\"],[5,\"1\"]]]]],[13,\"inY\",[1,[6,\"std.step\",[[5,\"0\"],[7,\"uv\",\"y\"]]],\"*\",[6,\"std.step\",[[7,\"uv\",\"y\"],[5,\"1\"]]]]],[10,[1,\"inX\",\"*\",\"inY\"]]]]},externals:{\"std.step\":()=>Et}})&&e.f)({})),X3=1,H3=2,j3=3;w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t.x,t.y,t.z,__tsover_mul(t.w,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"m\"}],body:[0,[[10,[6,\"d.vec4f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"],[1,[7,\"color\",\"w\"],\"*\",\"m\"]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({}));var mee=(e,t)=>{switch(t){case X3:return e;case H3:return W(Hm,\"edgeMirrorUV\",[e]);case j3:return W(jm,\"edgeWrapUV\",[e]);default:return W(N3,\"edgeClampUV\",[e])}},_ee=(e,t,s)=>{if(s===X3){let r=W($3,\"edgeTransparentMask\",[e]);return{uv:e,mask:t.mul(r)}}return{uv:mee(e,s),mask:t}},Ha=(e,t,s)=>{if(s===X3){let r=t(e),o=W($3,\"edgeTransparentMask\",[e]);return Ct(r.member(\"rgb\"),r.member(\"a\").mul(o))}return t(s===H3?W(Hm,\"edgeMirrorUV\",[e]):s===j3?W(jm,\"edgeWrapUV\",[e]):e)},T4=(e,t,s)=>Ha(t,s===H3?r=>e.sample(r):r=>g_(e,r,s===j3?\"linearRepeat\":\"linearClamp\"),s),h0=gt({rUV:m,gUV:m,bUV:m}).$name(\"ChromaticSplitUVs\"),fee=w.fn([m,m,n,n],h0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=s.mul(__tsover_mul(r,.1)),i=.15,l=G(a,m(-i,-i),m(i,i)),c=__tsover_mul(o,.1);return h0({rUV:t.sub(l.mul(__tsover_add(1,c))),gUV:t.sub(l),bUV:t.sub(l.mul(__tsover_sub(1,c)))})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"disp\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"chromaticSplit\"}],body:[0,[[13,\"scaled\",[6,[7,\"disp\",\"mul\"],[[1,\"strength\",\"*\",[5,\"0.1\"]]]]],[13,\"maxDisp\",[5,\"0.15\"]],[13,\"clamped\",[6,\"std.clamp\",[\"scaled\",[6,\"d.vec2f\",[[4,\"-\",\"maxDisp\"],[4,\"-\",\"maxDisp\"]]],[6,\"d.vec2f\",[\"maxDisp\",\"maxDisp\"]]]]],[13,\"chromaticScale\",[1,\"chromaticSplit\",\"*\",[5,\"0.1\"]]],[13,\"rUV\",[6,[7,\"uv\",\"sub\"],[[6,[7,\"clamped\",\"mul\"],[[1,[5,\"1\"],\"+\",\"chromaticScale\"]]]]]],[13,\"gUV\",[6,[7,\"uv\",\"sub\"],[\"clamped\"]]],[13,\"bUV\",[6,[7,\"uv\",\"sub\"],[[6,[7,\"clamped\",\"mul\"],[[1,[5,\"1\"],\"-\",\"chromaticScale\"]]]]]],[10,[6,\"ChromaticSplitUVs\",[[104,{rUV:\"rUV\",gUV:\"gUV\",bUV:\"bUV\"}]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m,ChromaticSplitUVs:()=>h0}})&&e.f)({})),JT=Jt,vee=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(s,bt),o=pe(r),a=ae(r),i=__tsover_div(1,3),l=__tsover_sub(1,o),c=__tsover_mul(l,i),d=__tsover_div(a,lc),u=__tsover_add(o,c),p=__tsover_sub(c,d),_=__tsover_add(c,d),f=__tsover_add(c,d),h=__tsover_add(o,c),g=__tsover_sub(c,d),y=__tsover_sub(c,d),b=__tsover_add(c,d),x=__tsover_add(o,c),P=t.x,C=t.y,E=t.z;return S(__tsover_add(__tsover_add(__tsover_mul(P,u),__tsover_mul(C,p)),__tsover_mul(E,_)),__tsover_add(__tsover_add(__tsover_mul(P,f),__tsover_mul(C,h)),__tsover_mul(E,g)),__tsover_add(__tsover_add(__tsover_mul(P,y),__tsover_mul(C,b)),__tsover_mul(E,x)),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"shiftDeg\"}],body:[0,[[13,\"angle\",[1,\"shiftDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angle\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angle\"]]],[13,\"k\",[1,[5,\"1\"],\"/\",[5,\"3\"]]],[13,\"oneMinusCos\",[1,[5,\"1\"],\"-\",\"cosA\"]],[13,\"kOneMinusCos\",[1,\"oneMinusCos\",\"*\",\"k\"]],[13,\"sinOverSqrt3\",[1,\"sinA\",\"/\",\"SQRT3\"]],[13,\"m00\",[1,\"cosA\",\"+\",\"kOneMinusCos\"]],[13,\"m01\",[1,\"kOneMinusCos\",\"-\",\"sinOverSqrt3\"]],[13,\"m02\",[1,\"kOneMinusCos\",\"+\",\"sinOverSqrt3\"]],[13,\"m10\",[1,\"kOneMinusCos\",\"+\",\"sinOverSqrt3\"]],[13,\"m11\",[1,\"cosA\",\"+\",\"kOneMinusCos\"]],[13,\"m12\",[1,\"kOneMinusCos\",\"-\",\"sinOverSqrt3\"]],[13,\"m20\",[1,\"kOneMinusCos\",\"-\",\"sinOverSqrt3\"]],[13,\"m21\",[1,\"kOneMinusCos\",\"+\",\"sinOverSqrt3\"]],[13,\"m22\",[1,\"cosA\",\"+\",\"kOneMinusCos\"]],[13,\"r\",[7,\"color\",\"x\"]],[13,\"g\",[7,\"color\",\"y\"]],[13,\"b\",[7,\"color\",\"z\"]],[13,\"newR\",[1,[1,[1,\"r\",\"*\",\"m00\"],\"+\",[1,\"g\",\"*\",\"m01\"]],\"+\",[1,\"b\",\"*\",\"m02\"]]],[13,\"newG\",[1,[1,[1,\"r\",\"*\",\"m10\"],\"+\",[1,\"g\",\"*\",\"m11\"]],\"+\",[1,\"b\",\"*\",\"m12\"]]],[13,\"newB\",[1,[1,[1,\"r\",\"*\",\"m20\"],\"+\",[1,\"g\",\"*\",\"m21\"]],\"+\",[1,\"b\",\"*\",\"m22\"]]],[10,[6,\"d.vec4f\",[\"newR\",\"newG\",\"newB\",[7,\"color\",\"w\"]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,SQRT3:()=>lc,\"d.vec4f\":()=>S}})&&e.f)({})),hee=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=A(t.x,t.y,t.z),o=Se(r,A(.2126,.7152,.0722)),a=K(A(o,o,o),r,A(s));return S(a.x,a.y,a.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]],[13,\"luminance\",[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"gray\",[6,\"d.vec3f\",[\"luminance\",\"luminance\",\"luminance\"]]],[13,\"saturated\",[6,\"std.mix\",[\"gray\",\"rgb\",[6,\"d.vec3f\",[\"intensity\"]]]]],[10,[6,\"d.vec4f\",[[7,\"saturated\",\"x\"],[7,\"saturated\",\"y\"],[7,\"saturated\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.dot\":()=>Se,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),gee=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t.x,o=t.y,a=t.z,i=__tsover_div(__tsover_add(__tsover_add(r,o),a),3),l=B(r,B(o,a)),c=__tsover_mul(__tsover_mul(__tsover_sub(l,i),s),-3),d=K(A(r,o,a),A(l,l,l),A(c));return S(d.x,d.y,d.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"adjustment\"}],body:[0,[[13,\"r\",[7,\"color\",\"x\"]],[13,\"g\",[7,\"color\",\"y\"]],[13,\"b\",[7,\"color\",\"z\"]],[13,\"average\",[1,[1,[1,\"r\",\"+\",\"g\"],\"+\",\"b\"],\"/\",[5,\"3\"]]],[13,\"mx\",[6,\"std.max\",[\"r\",[6,\"std.max\",[\"g\",\"b\"]]]]],[13,\"amt\",[1,[1,[1,\"mx\",\"-\",\"average\"],\"*\",\"adjustment\"],\"*\",[4,\"-\",[5,\"3\"]]]],[13,\"rgb\",[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]]],[13,\"vibrant\",[6,\"std.mix\",[\"rgb\",[6,\"d.vec3f\",[\"mx\",\"mx\",\"mx\"]],[6,\"d.vec3f\",[\"amt\"]]]]],[10,[6,\"d.vec4f\",[[7,\"vibrant\",\"x\"],[7,\"vibrant\",\"y\"],[7,\"vibrant\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"std.max\":()=>B,\"d.vec3f\":()=>A,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),yee=w.fn([S,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_add(r,1),a=A(.5,.5,.5);return S(t.xyz.sub(a).mul(o).add(a).add(A(s,s,s)),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"brightness\"},{type:\"i\",name:\"contrastRaw\"}],body:[0,[[13,\"contrast\",[1,\"contrastRaw\",\"+\",[5,\"1\"]]],[13,\"half\",[6,\"d.vec3f\",[[5,\"0.5\"],[5,\"0.5\"],[5,\"0.5\"]]]],[13,\"contrasted\",[6,[7,[6,[7,[6,[7,[7,\"color\",\"xyz\"],\"sub\"],[\"half\"]],\"mul\"],[\"contrast\"]],\"add\"],[\"half\"]]],[13,\"result\",[6,[7,\"contrasted\",\"add\"],[[6,\"d.vec3f\",[\"brightness\",\"brightness\",\"brightness\"]]]]],[10,[6,\"d.vec4f\",[\"result\",[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),bee=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t.xyz.mul(s),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"gain\"}],body:[0,[[13,\"rgb\",[6,[7,[7,\"color\",\"xyz\"],\"mul\"],[\"gain\"]]],[10,[6,\"d.vec4f\",[\"rgb\",[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),xee=w.fn([S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Se(t.xyz,A(.2126,.7152,.0722));return S(s,s,s,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[13,\"luminance\",[6,\"std.dot\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[10,[6,\"d.vec4f\",[\"luminance\",\"luminance\",\"luminance\",[7,\"color\",\"w\"]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Aee=w.fn([S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return S(__tsover_sub(1,t.x),__tsover_sub(1,t.y),__tsover_sub(1,t.z),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[10,[6,\"d.vec4f\",[[1,[5,\"1\"],\"-\",[7,\"color\",\"x\"]],[1,[5,\"1\"],\"-\",[7,\"color\",\"y\"]],[1,[5,\"1\"],\"-\",[7,\"color\",\"z\"]],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),wee=w.fn([S,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=A(t.x,t.y,t.z),a=Se(o,A(.299,.587,.114)),i=K(o,N(o,A(__tsover_sub(1,t.x),__tsover_sub(1,t.y),__tsover_sub(1,t.z)),a>s),A(r));return S(i.x,i.y,i.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"threshold\"},{type:\"i\",name:\"strength\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]],[13,\"luminance\",[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"inverted\",[6,\"d.vec3f\",[[1,[5,\"1\"],\"-\",[7,\"color\",\"x\"]],[1,[5,\"1\"],\"-\",[7,\"color\",\"y\"]],[1,[5,\"1\"],\"-\",[7,\"color\",\"z\"]]]]],[13,\"solarized\",[6,\"std.select\",[\"rgb\",\"inverted\",[1,\"luminance\",\">\",\"threshold\"]]]],[13,\"result\",[6,\"std.mix\",[\"rgb\",\"solarized\",[6,\"d.vec3f\",[\"strength\"]]]]],[10,[6,\"d.vec4f\",[[7,\"result\",\"x\"],[7,\"result\",\"y\"],[7,\"result\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.dot\":()=>Se,\"std.select\":()=>N,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),Tee=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=G(s,n(2),n(20));return S(__tsover_div(ie(__tsover_mul(t.x,r)),r),__tsover_div(ie(__tsover_mul(t.y,r)),r),__tsover_div(ie(__tsover_mul(t.z,r)),r),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"steps\"}],body:[0,[[13,\"s\",[6,\"std.clamp\",[\"steps\",[6,\"d.f32\",[[5,\"2\"]]],[6,\"d.f32\",[[5,\"20\"]]]]]],[13,\"r\",[1,[6,\"std.floor\",[[1,[7,\"color\",\"x\"],\"*\",\"s\"]]],\"/\",\"s\"]],[13,\"g\",[1,[6,\"std.floor\",[[1,[7,\"color\",\"y\"],\"*\",\"s\"]]],\"/\",\"s\"]],[13,\"b\",[1,[6,\"std.floor\",[[1,[7,\"color\",\"z\"],\"*\",\"s\"]]],\"/\",\"s\"]],[10,[6,\"d.vec4f\",[\"r\",\"g\",\"b\",[7,\"color\",\"w\"]]]]]]},externals:{\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.vec4f\":()=>S}})&&e.f)({})),See=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=K(A(t.x,t.y,t.z),A(s.x,s.y,s.z),A(r));return S(o.x,o.y,o.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"tintColor\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]],[13,\"tinted\",[6,\"std.mix\",[\"rgb\",[6,\"d.vec3f\",[[7,\"tintColor\",\"x\"],[7,\"tintColor\",\"y\"],[7,\"tintColor\",\"z\"]]],[6,\"d.vec3f\",[\"amount\"]]]]],[10,[6,\"d.vec4f\",[[7,\"tinted\",\"x\"],[7,\"tinted\",\"y\"],[7,\"tinted\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),Pee=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=A(t.x,t.y,t.z),a=K(o,A(s.x,s.y,s.z),A(r)),i=A(.299,.587,.114),l=Se(o,i),c=Se(a,i),d=a.mul(__tsover_div(l,B(c,1e-4)));return S(d.x,d.y,d.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"tintColor\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]],[13,\"tinted\",[6,\"std.mix\",[\"rgb\",[6,\"d.vec3f\",[[7,\"tintColor\",\"x\"],[7,\"tintColor\",\"y\"],[7,\"tintColor\",\"z\"]]],[6,\"d.vec3f\",[\"amount\"]]]]],[13,\"luminanceWeights\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]],[13,\"originalLum\",[6,\"std.dot\",[\"rgb\",\"luminanceWeights\"]]],[13,\"tintedLum\",[6,\"std.dot\",[\"tinted\",\"luminanceWeights\"]]],[13,\"result\",[6,[7,\"tinted\",\"mul\"],[[1,\"originalLum\",\"/\",[6,\"std.max\",[\"tintedLum\",[5,\"0.0001\"]]]]]]],[10,[6,\"d.vec4f\",[[7,\"result\",\"x\"],[7,\"result\",\"y\"],[7,\"result\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.mix\":()=>K,\"std.dot\":()=>Se,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&e.f)({})),Cee=w.fn([S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Se(t.xyz,A(.299,.587,.114));return be(__tsover_sub(s,.5),__tsover_add(s,.5),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"blend\"}],body:[0,[[13,\"luminance\",[6,\"std.dot\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[10,[6,\"std.smoothstep\",[[1,\"blend\",\"-\",[5,\"0.5\"]],[1,\"blend\",\"+\",[5,\"0.5\"]],\"luminance\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.smoothstep\":()=>be}})&&e.f)({})),Mee=w.fn([S,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Se(t.xyz,A(.299,.587,.114));return A(be(__tsover_sub(s,.25),s,r),be(s,__tsover_add(s,.25),r),be(__tsover_sub(s,.1),__tsover_add(s,.1),r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"blendMid\"}],body:[0,[[13,\"luminance\",[6,\"std.dot\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"shadowToMid\",[6,\"std.smoothstep\",[[1,\"blendMid\",\"-\",[5,\"0.25\"]],\"blendMid\",\"luminance\"]]],[13,\"midToHighlight\",[6,\"std.smoothstep\",[\"blendMid\",[1,\"blendMid\",\"+\",[5,\"0.25\"]],\"luminance\"]]],[13,\"finalT\",[6,\"std.smoothstep\",[[1,\"blendMid\",\"-\",[5,\"0.1\"]],[1,\"blendMid\",\"+\",[5,\"0.1\"]],\"luminance\"]]],[10,[6,\"d.vec3f\",[\"shadowToMid\",\"midToHighlight\",\"finalT\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.smoothstep\":()=>be}})&&e.f)({})),Eee=w.fn([S,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Se(t.xyz,A(.2126,.7152,.0722)),i=B(__tsover_sub(r,s),1e-4),l=G(__tsover_div(__tsover_sub(a,s),i),0,1);return l=G(__tsover_add(__tsover_mul(__tsover_sub(l,.5),o),.5),0,1),l}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"black\"},{type:\"i\",name:\"white\"},{type:\"i\",name:\"contrast\"}],body:[0,[[13,\"luma\",[6,\"std.dot\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"range\",[6,\"std.max\",[[1,\"white\",\"-\",\"black\"],[5,\"0.0001\"]]]],[12,\"t\",[6,\"std.clamp\",[[1,[1,\"luma\",\"-\",\"black\"],\"/\",\"range\"],[5,\"0\"],[5,\"1\"]]]],[2,\"t\",\"=\",[6,\"std.clamp\",[[1,[1,[1,\"t\",\"-\",[5,\"0.5\"]],\"*\",\"contrast\"],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[10,\"t\"]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.max\":()=>B,\"std.clamp\":()=>G}})&&e.f)({})),kee=w.fn([n,n,A,A,A,A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=a.mul(t).add(i).add(A(s,s,s)).mul(JT);return G(r.add(o.mul(pe(l))),A(0,0,0),A(1,1,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"pa\"},{type:\"i\",name:\"pb\"},{type:\"i\",name:\"pc\"},{type:\"i\",name:\"pd\"}],body:[0,[[13,\"arg\",[6,[7,[6,[7,[6,[7,[6,[7,\"pc\",\"mul\"],[\"t\"]],\"add\"],[\"pd\"]],\"add\"],[[6,\"d.vec3f\",[\"phase\",\"phase\",\"phase\"]]]],\"mul\"],[\"TWO_PI\"]]],[13,\"mapped\",[6,[7,\"pa\",\"add\"],[[6,[7,\"pb\",\"mul\"],[[6,\"std.cos\",[\"arg\"]]]]]]],[10,[6,\"std.clamp\",[\"mapped\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]]]]},externals:{\"d.vec3f\":()=>A,TWO_PI:()=>JT,\"std.cos\":()=>pe,\"std.clamp\":()=>G}})&&e.f)({})),Bee=w.fn([n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Re(__tsover_add(t,s)),o=__tsover_mul(r,3);return m(Re(o),ie(o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"phase\"}],body:[0,[[13,\"t01\",[6,\"std.fract\",[[1,\"t\",\"+\",\"phase\"]]]],[13,\"t3\",[1,\"t01\",\"*\",[5,\"3\"]]],[10,[6,\"d.vec2f\",[[6,\"std.fract\",[\"t3\"]],[6,\"std.floor\",[\"t3\"]]]]]]]},externals:{\"std.fract\":()=>Re,\"d.vec2f\":()=>m,\"std.floor\":()=>ie}})&&e.f)({})),zee=w.fn([S,S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return N(N(r,s,o<2),t,o<1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"seg0\"},{type:\"i\",name:\"seg1\"},{type:\"i\",name:\"seg2\"},{type:\"i\",name:\"i\"}],body:[0,[[13,\"upper\",[6,\"std.select\",[\"seg2\",\"seg1\",[1,\"i\",\"<\",[5,\"2\"]]]]],[10,[6,\"std.select\",[\"upper\",\"seg0\",[1,\"i\",\"<\",[5,\"1\"]]]]]]]},externals:{\"std.select\":()=>N}})&&e.f)({})),eS=w.fn([S,A,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return S(K(t.xyz,s,A(r,r,r)),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"mapped\"},{type:\"i\",name:\"strength\"}],body:[0,[[13,\"outRgb\",[6,\"std.mix\",[[7,\"color\",\"xyz\"],\"mapped\",[6,\"d.vec3f\",[\"strength\",\"strength\",\"strength\"]]]]],[10,[6,\"d.vec4f\",[\"outRgb\",[7,\"color\",\"w\"]]]]]]},externals:{\"std.mix\":()=>K,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Ree=w.fn([S,m,m,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Ys(s.mul(r).add(m(i,__tsover_mul(i,1.6180339)))),c=__tsover_sub(__tsover_mul(l,2),1),d=G(Se(t.xyz,A(.2126,.7152,.0722)),0,1),u=__tsover_sub(1,d),p=vt(__tsover_add(u,1e-6),a),_=__tsover_mul(__tsover_mul(c,p),__tsover_mul(o,.1));return S(t.xyz.add(_),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"bias\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"pixelCoord\",[6,[7,\"uv\",\"mul\"],[\"viewport\"]]],[13,\"noiseVal\",[6,\"hash12\",[[6,[7,\"pixelCoord\",\"add\"],[[6,\"d.vec2f\",[\"animTime\",[1,\"animTime\",\"*\",[5,\"1.6180339\"]]]]]]]]],[13,\"grain\",[1,[1,\"noiseVal\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"brightness\",[6,\"std.clamp\",[[6,\"std.dot\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"darkness\",[1,[5,\"1\"],\"-\",\"brightness\"]],[13,\"darkFactor\",[6,\"std.pow\",[[1,\"darkness\",\"+\",[5,\"0.000001\"]],\"bias\"]]],[13,\"grainIntensity\",[1,[1,\"grain\",\"*\",\"darkFactor\"],\"*\",[1,\"strength\",\"*\",[5,\"0.1\"]]]],[13,\"grainedColor\",[6,[7,[7,\"color\",\"xyz\"],\"add\"],[\"grainIntensity\"]]],[10,[6,\"d.vec4f\",[\"grainedColor\",[7,\"color\",\"w\"]]]]]]},externals:{hash12:()=>Ys,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.pow\":()=>vt,\"d.vec4f\":()=>S}})&&e.f)({})),gv=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=vt(N(t,__tsover_sub(1,t),i>.5),a),c=__tsover_add(__tsover_mul(l,s),.5),d=B(mr(c),1e-4),u=re(__tsover_sub(Re(__tsover_add(c,.5)),.5)),p=__tsover_div(u,d),_=__tsover_add(__tsover_mul(o,.99),.01),f=__tsover_mul(r,__tsover_sub(1,_));return __tsover_sub(1,be(f,r,p))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sourceValue\"},{type:\"i\",name:\"levels\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"gamma\"},{type:\"i\",name:\"invert\"}],body:[0,[[13,\"invertedValue\",[6,\"std.select\",[\"sourceValue\",[1,[5,\"1\"],\"-\",\"sourceValue\"],[1,\"invert\",\">\",[5,\"0.5\"]]]]],[13,\"sv\",[6,\"std.pow\",[\"invertedValue\",\"gamma\"]]],[13,\"scaledValue\",[1,[1,\"sv\",\"*\",\"levels\"],\"+\",[5,\"0.5\"]]],[13,\"fw\",[6,\"std.max\",[[6,\"std.fwidth\",[\"scaledValue\"]],[5,\"0.0001\"]]]],[13,\"distFromBoundary\",[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,\"scaledValue\",\"+\",[5,\"0.5\"]]]],\"-\",[5,\"0.5\"]]]]],[13,\"normalizedDist\",[1,\"distFromBoundary\",\"/\",\"fw\"]],[13,\"transitionFactor\",[1,[1,\"softness\",\"*\",[5,\"0.99\"]],\"+\",[5,\"0.01\"]]],[13,\"innerEdge\",[1,\"lineWidth\",\"*\",[1,[5,\"1\"],\"-\",\"transitionFactor\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"innerEdge\",\"lineWidth\",\"normalizedDist\"]]]]]]},externals:{\"std.select\":()=>N,\"std.pow\":()=>vt,\"std.max\":()=>B,\"std.fwidth\":()=>mr,\"std.abs\":()=>re,\"std.fract\":()=>Re,\"std.smoothstep\":()=>be}})&&e.f)({})),Dee=w.fn([S,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return gv(pT(t.xyz),s,r,o,a,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"levels\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"gamma\"},{type:\"i\",name:\"invert\"}],body:[0,[[10,[6,\"contourLineMask\",[[6,\"luma709Dot\",[[7,\"color\",\"xyz\"]]],\"levels\",\"lineWidth\",\"softness\",\"gamma\",\"invert\"]]]]]},externals:{contourLineMask:()=>gv,luma709Dot:()=>pT}})&&e.f)({})),Lee=w.fn([S,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return gv(t.w,s,r,o,a,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"levels\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"gamma\"},{type:\"i\",name:\"invert\"}],body:[0,[[10,[6,\"contourLineMask\",[[7,\"color\",\"w\"],\"levels\",\"lineWidth\",\"softness\",\"gamma\",\"invert\"]]]]]},externals:{contourLineMask:()=>gv}})&&e.f)({})),Uee=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=K(A(t.x,t.y,t.z),A(s.x,s.y,s.z),A(r)),a=K(t.w,s.w,r);return S(o.x,o.y,o.z,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"bg\"},{type:\"i\",name:\"line\"},{type:\"i\",name:\"mask\"}],body:[0,[[13,\"rgb\",[6,\"std.mix\",[[6,\"d.vec3f\",[[7,\"bg\",\"x\"],[7,\"bg\",\"y\"],[7,\"bg\",\"z\"]]],[6,\"d.vec3f\",[[7,\"line\",\"x\"],[7,\"line\",\"y\"],[7,\"line\",\"z\"]]],[6,\"d.vec3f\",[\"mask\"]]]]],[13,\"alpha\",[6,\"std.mix\",[[7,\"bg\",\"w\"],[7,\"line\",\"w\"],\"mask\"]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"alpha\"]]]]]},externals:{\"std.mix\":()=>K,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Vl=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(__tsover_sub(Re(__tsover_mul(ae(__tsover_mul(t,12.9898)),43758.5453)),.5),2)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"}],body:[0,[[10,[1,[1,[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,\"x\",\"*\",[5,\"12.9898\"]]]],\"*\",[5,\"43758.5453\"]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]]]]},externals:{\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({})),Oee=w.fn([m,n,m,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(__tsover_mul(r.x,o),r.y),i=m(__tsover_mul(t.x,o),__tsover_sub(1,t.y)),l=a.sub(i),c=__tsover_mul(s,bt),d=m(pe(c),ae(c)),u=m(__tsover_mul(d.y,-1),d.x),p=Se(l,u),_=l.sub(u.mul(__tsover_mul(p,2))),f=i.add(_),h=m(__tsover_div(f.x,o),f.y),g=Et(0,p);return A(h.x,h.y,g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[\"centerPos\"]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"lineDirection\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angleRad\"]],[6,\"std.sin\",[\"angleRad\"]]]]],[13,\"lineNormal\",[6,\"d.vec2f\",[[1,[7,\"lineDirection\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[7,\"lineDirection\",\"x\"]]]],[13,\"signedDistance\",[6,\"std.dot\",[\"delta\",\"lineNormal\"]]],[13,\"reflectedDelta\",[6,[7,\"delta\",\"sub\"],[[6,[7,\"lineNormal\",\"mul\"],[[1,\"signedDistance\",\"*\",[5,\"2\"]]]]]]],[13,\"mirroredUV\",[6,[7,\"centerPos\",\"add\"],[\"reflectedDelta\"]]],[13,\"finalMirroredUV\",[6,\"d.vec2f\",[[1,[7,\"mirroredUV\",\"x\"],\"/\",\"aspect\"],[7,\"mirroredUV\",\"y\"]]]],[13,\"shouldMirror\",[6,\"std.step\",[[5,\"0\"],\"signedDistance\"]]],[10,[6,\"d.vec3f\",[[7,\"finalMirroredUV\",\"x\"],[7,\"finalMirroredUV\",\"y\"],\"shouldMirror\"]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.step\":()=>Et,\"d.vec3f\":()=>A}})&&e.f)({})),Gee=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(N(t.x,__tsover_sub(1,t.x),s>0),N(t.y,__tsover_sub(1,t.y),r>0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"flipX\"},{type:\"i\",name:\"flipY\"}],body:[0,[[13,\"x\",[6,\"std.select\",[[7,\"uv\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"uv\",\"x\"]],[1,\"flipX\",\">\",[5,\"0\"]]]]],[13,\"y\",[6,\"std.select\",[[7,\"uv\",\"y\"],[1,[5,\"1\"],\"-\",[7,\"uv\",\"y\"]],[1,\"flipY\",\">\",[5,\"0\"]]]]],[10,[6,\"d.vec2f\",[\"x\",\"y\"]]]]]},externals:{\"std.select\":()=>N,\"d.vec2f\":()=>m}})&&e.f)({})),Vc=2,tS=1.2,Fee=w.fn([n,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,bt),l=m(pe(i),ae(i)),c=m(__tsover_mul(l.y,-1),l.x),d=m(__tsover_mul(__tsover_sub(o.x,.5),a),__tsover_sub(o.y,.5)),u=Se(d,l),p=Se(d,c),_=B(__tsover_mul(__tsover_add(__tsover_mul(re(l.x),a),re(l.y)),.5),.001),f=__tsover_div(u,_),h=__tsover_mul(t,tS),g=__tsover_mul(G(s,0,1),.9),y=__tsover_sub(1,g),b=re(f),x=N(n(-1),n(1),f>=0),P=__tsover_mul(h,b),C=__tsover_add(__tsover_mul(__tsover_mul(__tsover_mul(Vc,Vc),y),y),__tsover_mul(__tsover_mul(__tsover_mul(4,P),Vc),__tsover_sub(b,g))),E=re(P)<1e-5,R=N(__tsover_mul(P,2),n(1),E),U=N(__tsover_div(__tsover_sub(Ue(B(C,0)),__tsover_mul(Vc,y)),R),__tsover_div(__tsover_sub(b,g),y),E),L=N(n(6),B(U,0),C>=0),k=__tsover_mul(x,__tsover_add(g,__tsover_mul(L,y))),D=b<=g,O=N(k,f,D),F=N(__tsover_mul(__tsover_mul(h,L),L),n(0),D),q=__tsover_div(__tsover_mul(p,__tsover_sub(Vc,F)),Vc),Y=l.mul(__tsover_mul(O,_)).add(c.mul(q));return m(__tsover_add(__tsover_div(Y.x,a),.5),__tsover_add(Y.y,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"strength\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"dir\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angleRad\"]],[6,\"std.sin\",[\"angleRad\"]]]]],[13,\"perp\",[6,\"d.vec2f\",[[1,[7,\"dir\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[7,\"dir\",\"x\"]]]],[13,\"p\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]],[13,\"sScreen\",[6,\"std.dot\",[\"p\",\"dir\"]]],[13,\"tScreen\",[6,\"std.dot\",[\"p\",\"perp\"]]],[13,\"sHalf\",[6,\"std.max\",[[1,[1,[1,[6,\"std.abs\",[[7,\"dir\",\"x\"]]],\"*\",\"aspect\"],\"+\",[6,\"std.abs\",[[7,\"dir\",\"y\"]]]],\"*\",[5,\"0.5\"]],[5,\"0.001\"]]]],[13,\"snScreen\",[1,\"sScreen\",\"/\",\"sHalf\"]],[13,\"b\",[1,\"strength\",\"*\",\"B_MAX\"]],[13,\"f\",[1,[6,\"std.clamp\",[\"falloff\",[5,\"0\"],[5,\"1\"]]],\"*\",[5,\"0.9\"]]],[13,\"fSpan\",[1,[5,\"1\"],\"-\",\"f\"]],[13,\"m\",[6,\"std.abs\",[\"snScreen\"]]],[13,\"sig\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"snScreen\",\">=\",[5,\"0\"]]]]],[13,\"a2\",[1,\"b\",\"*\",\"m\"]],[13,\"disc\",[1,[1,[1,[1,\"CAMERA_D\",\"*\",\"CAMERA_D\"],\"*\",\"fSpan\"],\"*\",\"fSpan\"],\"+\",[1,[1,[1,[5,\"4\"],\"*\",\"a2\"],\"*\",\"CAMERA_D\"],\"*\",[1,\"m\",\"-\",\"f\"]]]],[13,\"nearZero\",[1,[6,\"std.abs\",[\"a2\"]],\"<\",[5,\"0.00001\"]]],[13,\"denom\",[6,\"std.select\",[[1,\"a2\",\"*\",[5,\"2\"]],[6,\"d.f32\",[[5,\"1\"]]],\"nearZero\"]]],[13,\"uQuad\",[1,[1,[6,\"std.sqrt\",[[6,\"std.max\",[\"disc\",[5,\"0\"]]]]],\"-\",[1,\"CAMERA_D\",\"*\",\"fSpan\"]],\"/\",\"denom\"]],[13,\"uOn\",[6,\"std.select\",[\"uQuad\",[1,[1,\"m\",\"-\",\"f\"],\"/\",\"fSpan\"],\"nearZero\"]]],[13,\"u\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"6\"]]],[6,\"std.max\",[\"uOn\",[5,\"0\"]]],[1,\"disc\",\">=\",[5,\"0\"]]]]],[13,\"snBend\",[1,\"sig\",\"*\",[1,\"f\",\"+\",[1,\"u\",\"*\",\"fSpan\"]]]],[13,\"inFlat\",[1,\"m\",\"<=\",\"f\"]],[13,\"sn\",[6,\"std.select\",[\"snBend\",\"snScreen\",\"inFlat\"]]],[13,\"z\",[6,\"std.select\",[[1,[1,\"b\",\"*\",\"u\"],\"*\",\"u\"],[6,\"d.f32\",[[5,\"0\"]]],\"inFlat\"]]],[13,\"tSrc\",[1,[1,\"tScreen\",\"*\",[1,\"CAMERA_D\",\"-\",\"z\"]],\"/\",\"CAMERA_D\"]],[13,\"srcP\",[6,[7,[6,[7,\"dir\",\"mul\"],[[1,\"sn\",\"*\",\"sHalf\"]]],\"add\"],[[6,[7,\"perp\",\"mul\"],[\"tSrc\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,[7,\"srcP\",\"x\"],\"/\",\"aspect\"],\"+\",[5,\"0.5\"]],[1,[7,\"srcP\",\"y\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.max\":()=>B,\"std.abs\":()=>re,B_MAX:()=>tS,\"std.clamp\":()=>G,\"std.select\":()=>N,\"d.f32\":()=>n,CAMERA_D:()=>Vc,\"std.sqrt\":()=>Ue}})&&e.f)({})),Iee=w.fn([m,n,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(__tsover_mul(a.x,i),a.y),c=m(__tsover_mul(t.x,i),__tsover_sub(1,t.y)),d=l.sub(c),u=he(d),p=__tsover_mul(r,.5),_=__tsover_mul(p,B(__tsover_sub(__tsover_sub(1,o),.001),0)),f=__tsover_sub(1,be(_,p,u)),h=__tsover_div(u,p),g=__tsover_mul(h,h),y=B(0,__tsover_sub(1,g)),b=__tsover_mul(f,y),x=__tsover_mul(s,-1),P=__tsover_mul(x,b),C=__tsover_add(1,P),E=d.mul(C),R=c.add(E);return m(__tsover_div(R.x,i),R.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[\"centerPos\"]]],[13,\"distance\",[6,\"std.length\",[\"delta\"]]],[13,\"effectRadius\",[1,\"radius\",\"*\",[5,\"0.5\"]]],[13,\"innerRadius\",[1,\"effectRadius\",\"*\",[6,\"std.max\",[[1,[1,[5,\"1\"],\"-\",\"falloff\"],\"-\",[5,\"0.001\"]],[5,\"0\"]]]]],[13,\"smoothFalloff\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"innerRadius\",\"effectRadius\",\"distance\"]]]],[13,\"normalizedDist\",[1,\"distance\",\"/\",\"effectRadius\"]],[13,\"distSq\",[1,\"normalizedDist\",\"*\",\"normalizedDist\"]],[13,\"quadraticFalloff\",[6,\"std.max\",[[5,\"0\"],[1,[5,\"1\"],\"-\",\"distSq\"]]]],[13,\"falloffTotal\",[1,\"smoothFalloff\",\"*\",\"quadraticFalloff\"]],[13,\"negStrength\",[1,\"strength\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"displacementAmount\",[1,\"negStrength\",\"*\",\"falloffTotal\"]],[13,\"scaleFactor\",[1,[5,\"1\"],\"+\",\"displacementAmount\"]],[13,\"bulgedDelta\",[6,[7,\"delta\",\"mul\"],[\"scaleFactor\"]]],[13,\"bulgedUV\",[6,[7,\"centerPos\",\"add\"],[\"bulgedDelta\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"bulgedUV\",\"x\"],\"/\",\"aspect\"],[7,\"bulgedUV\",\"y\"]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.max\":()=>B,\"std.smoothstep\":()=>be}})&&e.f)({})),Yee=w.fn([m,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(t.x,__tsover_sub(1,t.y)),i=r.sub(a),l=m(__tsover_mul(i.x,o),i.y),c=__tsover_mul(s,he(l)),d=pe(c),u=ae(c),p=__tsover_sub(__tsover_mul(d,l.x),__tsover_mul(u,l.y)),_=__tsover_add(__tsover_mul(u,l.x),__tsover_mul(d,l.y));return m(__tsover_add(__tsover_div(p,o),a.x),__tsover_add(_,a.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"uv\",\"sub\"],[\"centerPos\"]]],[13,\"acd\",[6,\"d.vec2f\",[[1,[7,\"delta\",\"x\"],\"*\",\"aspect\"],[7,\"delta\",\"y\"]]]],[13,\"angle\",[1,\"intensity\",\"*\",[6,\"std.length\",[\"acd\"]]]],[13,\"cosA\",[6,\"std.cos\",[\"angle\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angle\"]]],[13,\"rotatedX\",[1,[1,\"cosA\",\"*\",[7,\"acd\",\"x\"]],\"-\",[1,\"sinA\",\"*\",[7,\"acd\",\"y\"]]]],[13,\"rotatedY\",[1,[1,\"sinA\",\"*\",[7,\"acd\",\"x\"]],\"+\",[1,\"cosA\",\"*\",[7,\"acd\",\"y\"]]]],[10,[6,\"d.vec2f\",[[1,[1,\"rotatedX\",\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,\"rotatedY\",\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),Wee=w.fn([m,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(o.x,a),o.y),l=m(__tsover_mul(t.x,a),__tsover_sub(1,t.y)),c=i.sub(l),d=he(c),u=Xt(c.y,c.x),p=__tsover_add(u,__tsover_mul(r,bt)),_=__tsover_div(Jt,s),f=__tsover_mul(_,2),h=__tsover_sub(p,__tsover_mul(f,ie(__tsover_div(p,f)))),g=N(h,__tsover_sub(f,h),h>_),y=__tsover_mul(pe(g),d),b=__tsover_mul(ae(g),d),x=l.add(m(y,b));return m(__tsover_div(x.x,a),x.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"segments\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[\"centerPos\"]]],[13,\"radius\",[6,\"std.length\",[\"delta\"]]],[13,\"currentAngle\",[6,\"std.atan2\",[[7,\"delta\",\"y\"],[7,\"delta\",\"x\"]]]],[13,\"rotatedAngle\",[1,\"currentAngle\",\"+\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]]],[13,\"segmentAngle\",[1,\"TAU\",\"/\",\"segments\"]],[13,\"segTwo\",[1,\"segmentAngle\",\"*\",[5,\"2\"]]],[13,\"wrappedAngle\",[1,\"rotatedAngle\",\"-\",[1,\"segTwo\",\"*\",[6,\"std.floor\",[[1,\"rotatedAngle\",\"/\",\"segTwo\"]]]]]],[13,\"finalAngle\",[6,\"std.select\",[\"wrappedAngle\",[1,\"segTwo\",\"-\",\"wrappedAngle\"],[1,\"wrappedAngle\",\">\",\"segmentAngle\"]]]],[13,\"kaleidoscopeX\",[1,[6,\"std.cos\",[\"finalAngle\"]],\"*\",\"radius\"]],[13,\"kaleidoscopeY\",[1,[6,\"std.sin\",[\"finalAngle\"]],\"*\",\"radius\"]],[13,\"kaleidoscopePos\",[6,[7,\"centerPos\",\"add\"],[[6,\"d.vec2f\",[\"kaleidoscopeX\",\"kaleidoscopeY\"]]]]],[10,[6,\"d.vec2f\",[[1,[7,\"kaleidoscopePos\",\"x\"],\"/\",\"aspect\"],[7,\"kaleidoscopePos\",\"y\"]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.atan2\":()=>Xt,DEG_TO_RAD:()=>bt,TAU:()=>Jt,\"std.floor\":()=>ie,\"std.select\":()=>N,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),qee=w.fn([m,n,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(__tsover_mul(a.x,i),a.y),c=m(__tsover_mul(t.x,i),__tsover_sub(1,t.y)),d=l.sub(c),u=__tsover_mul(r,bt),p=m(pe(u),ae(u)),_=Se(d,p),f=p.mul(_),h=d.sub(f),g=K(.001,75,o),y=G(__tsover_div(_,g),0,1),b=__tsover_mul(s,100),x=__tsover_add(1,__tsover_mul(b,y)),P=__tsover_div(_,x),C=p.mul(P).add(h),E=c.add(C);return m(__tsover_div(E.x,i),E.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[\"centerPos\"]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"directionVector\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angleRad\"]],[6,\"std.sin\",[\"angleRad\"]]]]],[13,\"projection\",[6,\"std.dot\",[\"delta\",\"directionVector\"]]],[13,\"parallel\",[6,[7,\"directionVector\",\"mul\"],[\"projection\"]]],[13,\"perpendicular\",[6,[7,\"delta\",\"sub\"],[\"parallel\"]]],[13,\"transitionWidth\",[6,\"std.mix\",[[5,\"0.001\"],[5,\"75\"],\"falloff\"]]],[13,\"effectMask\",[6,\"std.clamp\",[[1,\"projection\",\"/\",\"transitionWidth\"],[5,\"0\"],[5,\"1\"]]]],[13,\"scaledStrength\",[1,\"strength\",\"*\",[5,\"100\"]]],[13,\"stretchFactor\",[1,[5,\"1\"],\"+\",[1,\"scaledStrength\",\"*\",\"effectMask\"]]],[13,\"finalProjection\",[1,\"projection\",\"/\",\"stretchFactor\"]],[13,\"finalParallel\",[6,[7,\"directionVector\",\"mul\"],[\"finalProjection\"]]],[13,\"stretchedDelta\",[6,[7,\"finalParallel\",\"add\"],[\"perpendicular\"]]],[13,\"stretchedUV\",[6,[7,\"centerPos\",\"add\"],[\"stretchedDelta\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"stretchedUV\",\"x\"],\"/\",\"aspect\"],[7,\"stretchedUV\",\"y\"]]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.mix\":()=>K,\"std.clamp\":()=>G}})&&e.f)({})),Ci=w.fn([m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=t.sub(m(.5,.5)),l=m(__tsover_mul(i.x,s),i.y),c=__tsover_mul(r,bt),d=pe(c),u=ae(c),p=__tsover_add(__tsover_mul(l.x,u),__tsover_mul(l.y,d));return __tsover_add(__tsover_mul(__tsover_mul(__tsover_add(p,.5),o),Jt),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"centeredUV\",\"x\"],\"*\",\"aspect\"],[7,\"centeredUV\",\"y\"]]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rotatedY\",[1,[1,[7,\"aspectCorrectedUV\",\"x\"],\"*\",\"sinA\"],\"+\",[1,[7,\"aspectCorrectedUV\",\"y\"],\"*\",\"cosA\"]]],[10,[1,[1,[1,[1,\"rotatedY\",\"+\",[5,\"0.5\"]],\"*\",\"frequency\"],\"*\",\"TAU\"],\"+\",\"t\"]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,TAU:()=>Jt}})&&e.f)({})),Mi=w.fn([m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,bt),l=pe(i),c=ae(i),d=__tsover_mul(__tsover_mul(a,o),.5),u=__tsover_div(__tsover_mul(d,l),s),p=__tsover_mul(d,c);return m(__tsover_add(t.x,u),__tsover_add(t.y,p))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"wave\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"displacement\",[1,[1,\"wave\",\"*\",\"strength\"],\"*\",[5,\"0.5\"]]],[13,\"displacementX\",[1,[1,\"displacement\",\"*\",\"cosA\"],\"/\",\"aspect\"]],[13,\"displacementY\",[1,\"displacement\",\"*\",\"sinA\"]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",\"displacementX\"],[1,[7,\"uv\",\"y\"],\"+\",\"displacementY\"]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),sS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return ae(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[10,[6,\"std.sin\",[\"phase\"]]]]]},externals:{\"std.sin\":()=>ae}})&&e.f)({})),rS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Re(__tsover_div(t,Jt));return __tsover_sub(__tsover_mul(re(__tsover_sub(__tsover_mul(s,2),1)),2),1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[13,\"normalizedPhase\",[6,\"std.fract\",[[1,\"phase\",\"/\",\"TAU\"]]]],[10,[1,[1,[6,\"std.abs\",[[1,[1,\"normalizedPhase\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]]},externals:{\"std.fract\":()=>Re,TAU:()=>Jt,\"std.abs\":()=>re}})&&e.f)({})),oS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Re(__tsover_div(t,Jt));return __tsover_sub(__tsover_mul(Et(.5,s),2),1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[13,\"normalizedPhase\",[6,\"std.fract\",[[1,\"phase\",\"/\",\"TAU\"]]]],[10,[1,[1,[6,\"std.step\",[[5,\"0.5\"],\"normalizedPhase\"]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]]},externals:{\"std.fract\":()=>Re,TAU:()=>Jt,\"std.step\":()=>Et}})&&e.f)({})),aS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Re(__tsover_div(t,Jt));return __tsover_sub(__tsover_mul(s,2),1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[13,\"normalizedPhase\",[6,\"std.fract\",[[1,\"phase\",\"/\",\"TAU\"]]]],[10,[1,[1,\"normalizedPhase\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]]},externals:{\"std.fract\":()=>Re,TAU:()=>Jt}})&&e.f)({})),nS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return re(ae(t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[10,[6,\"std.abs\",[[6,\"std.sin\",[\"phase\"]]]]]]]},externals:{\"std.abs\":()=>re,\"std.sin\":()=>ae}})&&e.f)({})),Vee=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return Mi(t,s,r,a,sS(Ci(t,s,r,o,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"wave\",[6,\"waveformSine\",[[6,\"wavePhase\",[\"uv\",\"aspect\",\"angle\",\"frequency\",\"t\"]]]]],[10,[6,\"waveApply\",[\"uv\",\"aspect\",\"angle\",\"strength\",\"wave\"]]]]]},externals:{waveformSine:()=>sS,wavePhase:()=>Ci,waveApply:()=>Mi}})&&e.f)({})),Nee=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return Mi(t,s,r,a,rS(Ci(t,s,r,o,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"wave\",[6,\"waveformTriangle\",[[6,\"wavePhase\",[\"uv\",\"aspect\",\"angle\",\"frequency\",\"t\"]]]]],[10,[6,\"waveApply\",[\"uv\",\"aspect\",\"angle\",\"strength\",\"wave\"]]]]]},externals:{waveformTriangle:()=>rS,wavePhase:()=>Ci,waveApply:()=>Mi}})&&e.f)({})),$ee=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return Mi(t,s,r,a,oS(Ci(t,s,r,o,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"wave\",[6,\"waveformSquare\",[[6,\"wavePhase\",[\"uv\",\"aspect\",\"angle\",\"frequency\",\"t\"]]]]],[10,[6,\"waveApply\",[\"uv\",\"aspect\",\"angle\",\"strength\",\"wave\"]]]]]},externals:{waveformSquare:()=>oS,wavePhase:()=>Ci,waveApply:()=>Mi}})&&e.f)({})),Xee=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return Mi(t,s,r,a,aS(Ci(t,s,r,o,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"wave\",[6,\"waveformSawtooth\",[[6,\"wavePhase\",[\"uv\",\"aspect\",\"angle\",\"frequency\",\"t\"]]]]],[10,[6,\"waveApply\",[\"uv\",\"aspect\",\"angle\",\"strength\",\"wave\"]]]]]},externals:{waveformSawtooth:()=>aS,wavePhase:()=>Ci,waveApply:()=>Mi}})&&e.f)({})),Hee=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return Mi(t,s,r,a,nS(Ci(t,s,r,o,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"wave\",[6,\"waveformBounce\",[[6,\"wavePhase\",[\"uv\",\"aspect\",\"angle\",\"frequency\",\"t\"]]]]],[10,[6,\"waveApply\",[\"uv\",\"aspect\",\"angle\",\"strength\",\"wave\"]]]]]},externals:{waveformBounce:()=>nS,wavePhase:()=>Ci,waveApply:()=>Mi}})&&e.f)({})),jee=w.fn([m,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(o.x,a),o.y),l=m(__tsover_mul(t.x,a),__tsover_sub(1,t.y)),c=i.sub(l),d=Xt(c.y,c.x),u=__tsover_div(__tsover_add(d,Wo),Jt),p=he(c);return m(__tsover_mul(u,s),__tsover_mul(p,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"wrap\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[\"centerPos\"]]],[13,\"theta\",[6,\"std.atan2\",[[7,\"delta\",\"y\"],[7,\"delta\",\"x\"]]]],[13,\"normalizedAngle\",[1,[1,\"theta\",\"+\",\"PI\"],\"/\",\"TAU\"]],[13,\"r\",[6,\"std.length\",[\"delta\"]]],[13,\"u\",[1,\"normalizedAngle\",\"*\",\"wrap\"]],[13,\"v\",[1,\"r\",\"*\",\"radius\"]],[10,[6,\"d.vec2f\",[\"u\",\"v\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.atan2\":()=>Xt,PI:()=>Wo,TAU:()=>Jt,\"std.length\":()=>he}})&&e.f)({})),Kee=w.fn([m,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(__tsover_mul(r.x,Jt),Wo),i=__tsover_mul(r.y,s),l=__tsover_mul(i,pe(a)),c=__tsover_mul(i,ae(a)),d=m(t.x,__tsover_sub(1,t.y));return m(__tsover_add(__tsover_div(l,o),d.x),__tsover_add(c,d.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"theta\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"TAU\"],\"-\",\"PI\"]],[13,\"r\",[1,[7,\"uv\",\"y\"],\"*\",\"scale\"]],[13,\"rectX\",[1,\"r\",\"*\",[6,\"std.cos\",[\"theta\"]]]],[13,\"rectY\",[1,\"r\",\"*\",[6,\"std.sin\",[\"theta\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,\"rectX\",\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,\"rectY\",\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{TAU:()=>Jt,PI:()=>Wo,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),Au=1e-8,im=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_div(t,N(s,Au,re(s)<Au))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"n\"},{type:\"i\",name:\"dv\"}],body:[0,[[10,[1,\"n\",\"/\",[6,\"std.select\",[\"dv\",\"EPS\",[1,[6,\"std.abs\",[\"dv\"]],\"<\",\"EPS\"]]]]]]]},externals:{\"std.select\":()=>N,EPS:()=>Au,\"std.abs\":()=>re}})&&e.f)({})),vu=w.fn([m,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(r.x,s.x),a=__tsover_sub(r.y,s.y),i=__tsover_sub(t.x,s.x),l=__tsover_sub(t.y,s.y);return __tsover_sub(__tsover_mul(o,l),__tsover_mul(a,i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"}],body:[0,[[13,\"bax\",[1,[7,\"b\",\"x\"],\"-\",[7,\"a\",\"x\"]]],[13,\"bay\",[1,[7,\"b\",\"y\"],\"-\",[7,\"a\",\"y\"]]],[13,\"pax\",[1,[7,\"p\",\"x\"],\"-\",[7,\"a\",\"x\"]]],[13,\"pay\",[1,[7,\"p\",\"y\"],\"-\",[7,\"a\",\"y\"]]],[10,[1,[1,\"bax\",\"*\",\"pay\"],\"-\",[1,\"bay\",\"*\",\"pax\"]]]]]},externals:{}})&&e.f)({})),iS=w.fn([m,m,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=vu(t,s,r),i=vu(t,r,o),l=vu(t,o,s),c=__tsover_mul(__tsover_mul(Et(n(0),a),Et(n(0),i)),Et(n(0),l)),d=__tsover_mul(__tsover_mul(Et(a,n(0)),Et(i,n(0))),Et(l,n(0)));return __tsover_add(c,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"c\"}],body:[0,[[13,\"d1\",[6,\"cornerSideOf\",[\"p\",\"a\",\"b\"]]],[13,\"d2\",[6,\"cornerSideOf\",[\"p\",\"b\",\"c\"]]],[13,\"d3\",[6,\"cornerSideOf\",[\"p\",\"c\",\"a\"]]],[13,\"allPos\",[1,[1,[6,\"std.step\",[[6,\"d.f32\",[[5,\"0\"]]],\"d1\"]],\"*\",[6,\"std.step\",[[6,\"d.f32\",[[5,\"0\"]]],\"d2\"]]],\"*\",[6,\"std.step\",[[6,\"d.f32\",[[5,\"0\"]]],\"d3\"]]]],[13,\"allNeg\",[1,[1,[6,\"std.step\",[\"d1\",[6,\"d.f32\",[[5,\"0\"]]]]],\"*\",[6,\"std.step\",[\"d2\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",[6,\"std.step\",[\"d3\",[6,\"d.f32\",[[5,\"0\"]]]]]]],[10,[1,\"allPos\",\"+\",\"allNeg\"]]]]},externals:{cornerSideOf:()=>vu,\"std.step\":()=>Et,\"d.f32\":()=>n}})&&e.f)({})),lS=w.fn([m,m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=r.sub(s),i=G(__tsover_div(Se(t.sub(s),a),B(Se(a,a),Au)),0,1),l=s.add(a.mul(i)),c=m(__tsover_mul(a.y,-1),a.x).div(B(he(a),Au)),d=__tsover_mul(__tsover_mul(no(o),-1),.004);return l.add(c.mul(d))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"refSide\"}],body:[0,[[13,\"ab\",[6,[7,\"b\",\"sub\"],[\"a\"]]],[13,\"t\",[6,\"std.clamp\",[[1,[6,\"std.dot\",[[6,[7,\"p\",\"sub\"],[\"a\"]],\"ab\"]],\"/\",[6,\"std.max\",[[6,\"std.dot\",[\"ab\",\"ab\"]],\"EPS\"]]],[5,\"0\"],[5,\"1\"]]]],[13,\"foot\",[6,[7,\"a\",\"add\"],[[6,[7,\"ab\",\"mul\"],[\"t\"]]]]],[13,\"perp\",[6,[7,[6,\"d.vec2f\",[[1,[7,\"ab\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[7,\"ab\",\"x\"]]],\"div\"],[[6,\"std.max\",[[6,\"std.length\",[\"ab\"]],\"EPS\"]]]]],[13,\"nudge\",[1,[1,[6,\"std.sign\",[\"refSide\"]],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",[5,\"0.004\"]]],[10,[6,[7,\"foot\",\"add\"],[[6,[7,\"perp\",\"mul\"],[\"nudge\"]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.dot\":()=>Se,\"std.max\":()=>B,EPS:()=>Au,\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.sign\":()=>no}})&&e.f)({})),lm=w.fn([m,m,m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=iS(t,s,r,o)>.5;return N(t,lS(t,s,r,vu(o,s,r)),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"o\"}],body:[0,[[13,\"inside\",[1,[6,\"cornerInsideTri\",[\"p\",\"a\",\"b\",\"o\"]],\">\",[5,\"0.5\"]]],[13,\"proj\",[6,\"cornerProjectToDiagonal\",[\"p\",\"a\",\"b\",[6,\"cornerSideOf\",[\"o\",\"a\",\"b\"]]]]],[10,[6,\"std.select\",[\"p\",\"proj\",\"inside\"]]]]]},externals:{cornerInsideTri:()=>iS,cornerProjectToDiagonal:()=>lS,cornerSideOf:()=>vu,\"std.select\":()=>N}})&&e.f)({})),Qee=w.fn([m,n,m,m,m,m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(s,s),c=K(m(0,0),m(r.x,__tsover_sub(1,r.y)),l),d=K(m(1,0),m(o.x,__tsover_sub(1,o.y)),l),u=K(m(1,1),m(a.x,__tsover_sub(1,a.y)),l),p=K(m(0,1),m(i.x,__tsover_sub(1,i.y)),l),_=lm(c,d,p,u),f=lm(d,c,u,p),h=lm(u,d,p,c),g=lm(p,c,u,d),y=__tsover_sub(f.x,h.x),b=__tsover_sub(g.x,h.x),x=__tsover_sub(__tsover_add(__tsover_sub(_.x,f.x),h.x),g.x),P=__tsover_sub(f.y,h.y),C=__tsover_sub(g.y,h.y),E=__tsover_sub(__tsover_add(__tsover_sub(_.y,f.y),h.y),g.y),R=__tsover_sub(__tsover_mul(y,C),__tsover_mul(b,P)),U=im(__tsover_sub(__tsover_mul(x,C),__tsover_mul(b,E)),R),L=im(__tsover_sub(__tsover_mul(y,E),__tsover_mul(x,P)),R),k=__tsover_add(__tsover_sub(f.x,_.x),__tsover_mul(U,f.x)),D=__tsover_add(__tsover_sub(g.x,_.x),__tsover_mul(L,g.x)),O=_.x,F=__tsover_add(__tsover_sub(f.y,_.y),__tsover_mul(U,f.y)),q=__tsover_add(__tsover_sub(g.y,_.y),__tsover_mul(L,g.y)),Y=_.y,V=__tsover_sub(q,__tsover_mul(Y,L)),X=__tsover_mul(__tsover_sub(D,__tsover_mul(O,L)),-1),Q=__tsover_sub(__tsover_mul(D,Y),__tsover_mul(O,q)),te=__tsover_mul(__tsover_sub(F,__tsover_mul(Y,U)),-1),J=__tsover_sub(k,__tsover_mul(O,U)),oe=__tsover_mul(__tsover_sub(__tsover_mul(k,Y),__tsover_mul(O,F)),-1),le=__tsover_sub(__tsover_mul(F,L),__tsover_mul(q,U)),se=__tsover_mul(__tsover_sub(__tsover_mul(k,L),__tsover_mul(D,U)),-1),Te=__tsover_sub(__tsover_mul(k,q),__tsover_mul(D,F)),de=t.x,xe=t.y,Ae=__tsover_add(__tsover_add(__tsover_mul(le,de),__tsover_mul(se,xe)),Te),De=im(__tsover_add(__tsover_add(__tsover_mul(V,de),__tsover_mul(X,xe)),Q),Ae),Ee=im(__tsover_add(__tsover_add(__tsover_mul(te,de),__tsover_mul(J,xe)),oe),Ae),ze=__tsover_add(__tsover_add(__tsover_mul(k,V),__tsover_mul(D,te)),__tsover_mul(O,le));return A(De,Ee,Et(n(0),__tsover_mul(Ae,ze)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"c0\"},{type:\"i\",name:\"c1\"},{type:\"i\",name:\"c2\"},{type:\"i\",name:\"c3\"}],body:[0,[[13,\"amt\",[6,\"d.vec2f\",[\"amount\",\"amount\"]]],[13,\"r0\",[6,\"std.mix\",[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,\"d.vec2f\",[[7,\"c0\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"c0\",\"y\"]]]],\"amt\"]]],[13,\"r1\",[6,\"std.mix\",[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]],[6,\"d.vec2f\",[[7,\"c1\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"c1\",\"y\"]]]],\"amt\"]]],[13,\"r2\",[6,\"std.mix\",[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]],[6,\"d.vec2f\",[[7,\"c2\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"c2\",\"y\"]]]],\"amt\"]]],[13,\"r3\",[6,\"std.mix\",[[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]],[6,\"d.vec2f\",[[7,\"c3\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"c3\",\"y\"]]]],\"amt\"]]],[13,\"q0\",[6,\"cornerConvexify\",[\"r0\",\"r1\",\"r3\",\"r2\"]]],[13,\"q1\",[6,\"cornerConvexify\",[\"r1\",\"r0\",\"r2\",\"r3\"]]],[13,\"q2\",[6,\"cornerConvexify\",[\"r2\",\"r1\",\"r3\",\"r0\"]]],[13,\"q3\",[6,\"cornerConvexify\",[\"r3\",\"r0\",\"r2\",\"r1\"]]],[13,\"dx1\",[1,[7,\"q1\",\"x\"],\"-\",[7,\"q2\",\"x\"]]],[13,\"dx2\",[1,[7,\"q3\",\"x\"],\"-\",[7,\"q2\",\"x\"]]],[13,\"dx3\",[1,[1,[1,[7,\"q0\",\"x\"],\"-\",[7,\"q1\",\"x\"]],\"+\",[7,\"q2\",\"x\"]],\"-\",[7,\"q3\",\"x\"]]],[13,\"dy1\",[1,[7,\"q1\",\"y\"],\"-\",[7,\"q2\",\"y\"]]],[13,\"dy2\",[1,[7,\"q3\",\"y\"],\"-\",[7,\"q2\",\"y\"]]],[13,\"dy3\",[1,[1,[1,[7,\"q0\",\"y\"],\"-\",[7,\"q1\",\"y\"]],\"+\",[7,\"q2\",\"y\"]],\"-\",[7,\"q3\",\"y\"]]],[13,\"denA\",[1,[1,\"dx1\",\"*\",\"dy2\"],\"-\",[1,\"dx2\",\"*\",\"dy1\"]]],[13,\"a13\",[6,\"cornerPinSafeDiv\",[[1,[1,\"dx3\",\"*\",\"dy2\"],\"-\",[1,\"dx2\",\"*\",\"dy3\"]],\"denA\"]]],[13,\"a23\",[6,\"cornerPinSafeDiv\",[[1,[1,\"dx1\",\"*\",\"dy3\"],\"-\",[1,\"dx3\",\"*\",\"dy1\"]],\"denA\"]]],[13,\"a11\",[1,[1,[7,\"q1\",\"x\"],\"-\",[7,\"q0\",\"x\"]],\"+\",[1,\"a13\",\"*\",[7,\"q1\",\"x\"]]]],[13,\"a21\",[1,[1,[7,\"q3\",\"x\"],\"-\",[7,\"q0\",\"x\"]],\"+\",[1,\"a23\",\"*\",[7,\"q3\",\"x\"]]]],[13,\"a31\",[7,\"q0\",\"x\"]],[13,\"a12\",[1,[1,[7,\"q1\",\"y\"],\"-\",[7,\"q0\",\"y\"]],\"+\",[1,\"a13\",\"*\",[7,\"q1\",\"y\"]]]],[13,\"a22\",[1,[1,[7,\"q3\",\"y\"],\"-\",[7,\"q0\",\"y\"]],\"+\",[1,\"a23\",\"*\",[7,\"q3\",\"y\"]]]],[13,\"a32\",[7,\"q0\",\"y\"]],[13,\"c00\",[1,\"a22\",\"-\",[1,\"a32\",\"*\",\"a23\"]]],[13,\"c10\",[1,[1,\"a21\",\"-\",[1,\"a31\",\"*\",\"a23\"]],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"c20\",[1,[1,\"a21\",\"*\",\"a32\"],\"-\",[1,\"a31\",\"*\",\"a22\"]]],[13,\"c01\",[1,[1,\"a12\",\"-\",[1,\"a32\",\"*\",\"a13\"]],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"c11\",[1,\"a11\",\"-\",[1,\"a31\",\"*\",\"a13\"]]],[13,\"c21\",[1,[1,[1,\"a11\",\"*\",\"a32\"],\"-\",[1,\"a31\",\"*\",\"a12\"]],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"c02\",[1,[1,\"a12\",\"*\",\"a23\"],\"-\",[1,\"a22\",\"*\",\"a13\"]]],[13,\"c12\",[1,[1,[1,\"a11\",\"*\",\"a23\"],\"-\",[1,\"a21\",\"*\",\"a13\"]],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"c22\",[1,[1,\"a11\",\"*\",\"a22\"],\"-\",[1,\"a21\",\"*\",\"a12\"]]],[13,\"px\",[7,\"coord\",\"x\"]],[13,\"py\",[7,\"coord\",\"y\"]],[13,\"den\",[1,[1,[1,\"c02\",\"*\",\"px\"],\"+\",[1,\"c12\",\"*\",\"py\"]],\"+\",\"c22\"]],[13,\"su\",[6,\"cornerPinSafeDiv\",[[1,[1,[1,\"c00\",\"*\",\"px\"],\"+\",[1,\"c10\",\"*\",\"py\"]],\"+\",\"c20\"],\"den\"]]],[13,\"sv\",[6,\"cornerPinSafeDiv\",[[1,[1,[1,\"c01\",\"*\",\"px\"],\"+\",[1,\"c11\",\"*\",\"py\"]],\"+\",\"c21\"],\"den\"]]],[13,\"det\",[1,[1,[1,\"a11\",\"*\",\"c00\"],\"+\",[1,\"a21\",\"*\",\"c01\"]],\"+\",[1,\"a31\",\"*\",\"c02\"]]],[13,\"front\",[6,\"std.step\",[[6,\"d.f32\",[[5,\"0\"]]],[1,\"den\",\"*\",\"det\"]]]],[10,[6,\"d.vec3f\",[\"su\",\"sv\",\"front\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.mix\":()=>K,cornerConvexify:()=>lm,cornerPinSafeDiv:()=>im,\"std.step\":()=>Et,\"d.f32\":()=>n,\"d.vec3f\":()=>A}})&&e.f)({})),Zee=w.fn([m,n,n,n,n,m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=m(t.x,__tsover_sub(1,t.y)),d=__tsover_div(__tsover_sub(l.x,c.x),a),u=__tsover_div(__tsover_sub(l.y,c.y),a),p=__tsover_mul(s,bt),_=__tsover_mul(r,bt),f=__tsover_mul(o,bt),h=pe(p),g=ae(p),y=pe(_),b=ae(_),x=__tsover_mul(hb(__tsover_mul(f,.5)),2),P=B(__tsover_add(h,__tsover_mul(__tsover_mul(g,d),x)),.001),C=__tsover_div(d,P),E=__tsover_div(__tsover_mul(u,h),P),R=B(__tsover_add(y,__tsover_mul(__tsover_mul(b,E),x)),.001),U=__tsover_div(__tsover_mul(C,y),R),L=__tsover_div(E,R),k=m(i.x,__tsover_sub(1,i.y));return m(U,L).add(c).sub(k).add(m(.5,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"pan\"},{type:\"i\",name:\"tilt\"},{type:\"i\",name:\"fov\"},{type:\"i\",name:\"zoom\"},{type:\"i\",name:\"offset\"},{type:\"i\",name:\"uv\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"x\",[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]],\"/\",\"zoom\"]],[13,\"y\",[1,[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]],\"/\",\"zoom\"]],[13,\"panRad\",[1,\"pan\",\"*\",\"DEG_TO_RAD\"]],[13,\"tiltRad\",[1,\"tilt\",\"*\",\"DEG_TO_RAD\"]],[13,\"fovRad\",[1,\"fov\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosPan\",[6,\"std.cos\",[\"panRad\"]]],[13,\"sinPan\",[6,\"std.sin\",[\"panRad\"]]],[13,\"cosTilt\",[6,\"std.cos\",[\"tiltRad\"]]],[13,\"sinTilt\",[6,\"std.sin\",[\"tiltRad\"]]],[13,\"perspectiveFactor\",[1,[6,\"std.tan\",[[1,\"fovRad\",\"*\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"panDivisor\",[1,\"cosPan\",\"+\",[1,[1,\"sinPan\",\"*\",\"x\"],\"*\",\"perspectiveFactor\"]]],[13,\"safePanDivisor\",[6,\"std.max\",[\"panDivisor\",[5,\"0.001\"]]]],[13,\"afterPanX\",[1,\"x\",\"/\",\"safePanDivisor\"]],[13,\"afterPanY\",[1,[1,\"y\",\"*\",\"cosPan\"],\"/\",\"safePanDivisor\"]],[13,\"tiltDivisor\",[1,\"cosTilt\",\"+\",[1,[1,\"sinTilt\",\"*\",\"afterPanY\"],\"*\",\"perspectiveFactor\"]]],[13,\"safeTiltDivisor\",[6,\"std.max\",[\"tiltDivisor\",[5,\"0.001\"]]]],[13,\"finalX\",[1,[1,\"afterPanX\",\"*\",\"cosTilt\"],\"/\",\"safeTiltDivisor\"]],[13,\"finalY\",[1,\"afterPanY\",\"/\",\"safeTiltDivisor\"]],[13,\"offsetPos\",[6,\"d.vec2f\",[[7,\"offset\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"offset\",\"y\"]]]]],[10,[6,[7,[6,[7,[6,[7,[6,\"d.vec2f\",[\"finalX\",\"finalY\"]],\"add\"],[\"centerPos\"]],\"sub\"],[\"offsetPos\"]],\"add\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.tan\":()=>hb,\"std.max\":()=>B}})&&e.f)({})),cS=w.fn([m,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(r.x,__tsover_sub(1,r.y)),i=__tsover_mul(__tsover_sub(t.x,a.x),s),l=__tsover_sub(t.y,a.y),c=pe(o),d=ae(o),u=__tsover_sub(__tsover_mul(i,c),__tsover_mul(l,d)),p=__tsover_add(__tsover_mul(i,d),__tsover_mul(l,c));return m(__tsover_add(__tsover_div(u,s),a.x),__tsover_add(p,a.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"angle\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]],\"*\",\"aspect\"]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"cosA\",[6,\"std.cos\",[\"angle\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angle\"]]],[13,\"rotX\",[1,[1,\"dx\",\"*\",\"cosA\"],\"-\",[1,\"dy\",\"*\",\"sinA\"]]],[13,\"rotY\",[1,[1,\"dx\",\"*\",\"sinA\"],\"+\",[1,\"dy\",\"*\",\"cosA\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"rotX\",\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,\"rotY\",\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),Jee=w.fn([m,n,n,n,n,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=m(t.x,__tsover_sub(1,t.y)),p=he(m(__tsover_mul(__tsover_sub(c.x,u.x),d),__tsover_sub(c.y,u.y))),_=__tsover_mul(p,r),f=ie(_),h=Re(_),g=__tsover_mul(s,bt),y=__tsover_mul(a,.137),b=__tsover_mul(Vl(__tsover_add(f,y)),g),x=__tsover_mul(Vl(__tsover_add(__tsover_add(f,1),y)),g),P=K(1,Vl(__tsover_add(f,42.7)),i),C=K(1,Vl(__tsover_add(__tsover_add(f,1),42.7)),i),E=__tsover_mul(__tsover_mul(l,.25),P),R=__tsover_mul(__tsover_mul(l,.25),C),U=__tsover_add(b,E),L=__tsover_add(x,R),k=__tsover_add(__tsover_sub(L,U),Wo),D=__tsover_sub(__tsover_sub(k,__tsover_mul(Jt,ie(__tsover_div(k,Jt)))),Wo),O=__tsover_add(__tsover_mul(o,.5),.001),F=be(__tsover_sub(.5,O),__tsover_add(.5,O),h);return cS(c,d,t,__tsover_add(U,__tsover_mul(D,F)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"rings\"},{type:\"i\",name:\"smoothness\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"speedRandomness\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]],\"*\",\"aspect\"]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"dist\",[6,\"std.length\",[[6,\"d.vec2f\",[\"dx\",\"dy\"]]]]],[13,\"ringCoord\",[1,\"dist\",\"*\",\"rings\"]],[13,\"ringIndex\",[6,\"std.floor\",[\"ringCoord\"]]],[13,\"ringFrac\",[6,\"std.fract\",[\"ringCoord\"]]],[13,\"maxAngle\",[1,\"intensity\",\"*\",\"DEG_TO_RAD\"]],[13,\"seedOffset\",[1,\"seed\",\"*\",[5,\"0.137\"]]],[13,\"staticA\",[1,[6,\"legacySinHash11\",[[1,\"ringIndex\",\"+\",\"seedOffset\"]]],\"*\",\"maxAngle\"]],[13,\"staticB\",[1,[6,\"legacySinHash11\",[[1,[1,\"ringIndex\",\"+\",[5,\"1\"]],\"+\",\"seedOffset\"]]],\"*\",\"maxAngle\"]],[13,\"ringSpeedA\",[6,\"std.mix\",[[5,\"1\"],[6,\"legacySinHash11\",[[1,\"ringIndex\",\"+\",[5,\"42.7\"]]]],\"speedRandomness\"]]],[13,\"ringSpeedB\",[6,\"std.mix\",[[5,\"1\"],[6,\"legacySinHash11\",[[1,[1,\"ringIndex\",\"+\",[5,\"1\"]],\"+\",[5,\"42.7\"]]]],\"speedRandomness\"]]],[13,\"animA\",[1,[1,\"animTime\",\"*\",[5,\"0.25\"]],\"*\",\"ringSpeedA\"]],[13,\"animB\",[1,[1,\"animTime\",\"*\",[5,\"0.25\"]],\"*\",\"ringSpeedB\"]],[13,\"totalA\",[1,\"staticA\",\"+\",\"animA\"]],[13,\"totalB\",[1,\"staticB\",\"+\",\"animB\"]],[13,\"wrapArg\",[1,[1,\"totalB\",\"-\",\"totalA\"],\"+\",\"PI\"]],[13,\"diff\",[1,[1,\"wrapArg\",\"-\",[1,\"TAU\",\"*\",[6,\"std.floor\",[[1,\"wrapArg\",\"/\",\"TAU\"]]]]],\"-\",\"PI\"]],[13,\"halfEdge\",[1,[1,\"smoothness\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.001\"]]],[13,\"blend\",[6,\"std.smoothstep\",[[1,[5,\"0.5\"],\"-\",\"halfEdge\"],[1,[5,\"0.5\"],\"+\",\"halfEdge\"],\"ringFrac\"]]],[13,\"angle\",[1,\"totalA\",\"+\",[1,\"diff\",\"*\",\"blend\"]]],[10,[6,\"csRotateUV\",[\"uv\",\"aspect\",\"center\",\"angle\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.floor\":()=>ie,\"std.fract\":()=>Re,DEG_TO_RAD:()=>bt,legacySinHash11:()=>Vl,\"std.mix\":()=>K,PI:()=>Wo,TAU:()=>Jt,\"std.smoothstep\":()=>be,csRotateUV:()=>cS}})&&e.f)({})),ete=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(__tsover_mul(t.x,s),t.y),c=__tsover_mul(a,.7),d=__tsover_mul(i,.2),u=__tsover_mul(Na(A(__tsover_add(__tsover_mul(l.x,r),__tsover_mul(c,.3)),__tsover_add(__tsover_mul(l.y,r),__tsover_mul(c,.5)),d)),Po),p=m(pe(u),ae(u)),_=__tsover_mul(a,.5),f=__tsover_mul(i,.15),h=__tsover_mul(Na(A(__tsover_add(__tsover_add(__tsover_mul(l.x,__tsover_mul(r,2.3)),__tsover_mul(_,-.4)),173.5),__tsover_add(__tsover_add(__tsover_mul(l.y,__tsover_mul(r,2.3)),__tsover_mul(_,.3)),291.7),__tsover_add(f,50))),Po),g=m(pe(h),ae(h)),y=__tsover_mul(a,.3),b=__tsover_mul(i,.1),x=__tsover_mul(Na(A(__tsover_add(__tsover_add(__tsover_mul(l.x,__tsover_mul(r,4.7)),__tsover_mul(y,.2)),527.3),__tsover_add(__tsover_add(__tsover_mul(l.y,__tsover_mul(r,4.7)),__tsover_mul(y,-.6)),839.1),__tsover_add(b,100))),Po),P=m(pe(x),ae(x)),C=p.add(g.mul(.5)).add(P.mul(.25)),E=B(he(C),.001),R=C.div(E),U=m(__tsover_div(__tsover_mul(R.x,o),s),__tsover_mul(R.y,o));return m(__tsover_add(t.x,U.x),__tsover_add(t.y,U.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"evolutionTime\"}],body:[0,[[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"drift1\",[1,\"time\",\"*\",[5,\"0.7\"]]],[13,\"evolve1\",[1,\"evolutionTime\",\"*\",[5,\"0.2\"]]],[13,\"angle1\",[1,[6,\"mxNoiseFloat3\",[[6,\"d.vec3f\",[[1,[1,[7,\"aspectCorrectedUV\",\"x\"],\"*\",\"scale\"],\"+\",[1,\"drift1\",\"*\",[5,\"0.3\"]]],[1,[1,[7,\"aspectCorrectedUV\",\"y\"],\"*\",\"scale\"],\"+\",[1,\"drift1\",\"*\",[5,\"0.5\"]]],\"evolve1\"]]]],\"*\",\"TWO_PI\"]],[13,\"flow1\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angle1\"]],[6,\"std.sin\",[\"angle1\"]]]]],[13,\"drift2\",[1,\"time\",\"*\",[5,\"0.5\"]]],[13,\"evolve2\",[1,\"evolutionTime\",\"*\",[5,\"0.15\"]]],[13,\"angle2\",[1,[6,\"mxNoiseFloat3\",[[6,\"d.vec3f\",[[1,[1,[1,[7,\"aspectCorrectedUV\",\"x\"],\"*\",[1,\"scale\",\"*\",[5,\"2.3\"]]],\"+\",[1,\"drift2\",\"*\",[4,\"-\",[5,\"0.4\"]]]],\"+\",[5,\"173.5\"]],[1,[1,[1,[7,\"aspectCorrectedUV\",\"y\"],\"*\",[1,\"scale\",\"*\",[5,\"2.3\"]]],\"+\",[1,\"drift2\",\"*\",[5,\"0.3\"]]],\"+\",[5,\"291.7\"]],[1,\"evolve2\",\"+\",[5,\"50\"]]]]]],\"*\",\"TWO_PI\"]],[13,\"flow2\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angle2\"]],[6,\"std.sin\",[\"angle2\"]]]]],[13,\"drift3\",[1,\"time\",\"*\",[5,\"0.3\"]]],[13,\"evolve3\",[1,\"evolutionTime\",\"*\",[5,\"0.1\"]]],[13,\"angle3\",[1,[6,\"mxNoiseFloat3\",[[6,\"d.vec3f\",[[1,[1,[1,[7,\"aspectCorrectedUV\",\"x\"],\"*\",[1,\"scale\",\"*\",[5,\"4.7\"]]],\"+\",[1,\"drift3\",\"*\",[5,\"0.2\"]]],\"+\",[5,\"527.3\"]],[1,[1,[1,[7,\"aspectCorrectedUV\",\"y\"],\"*\",[1,\"scale\",\"*\",[5,\"4.7\"]]],\"+\",[1,\"drift3\",\"*\",[4,\"-\",[5,\"0.6\"]]]],\"+\",[5,\"839.1\"]],[1,\"evolve3\",\"+\",[5,\"100\"]]]]]],\"*\",\"TWO_PI\"]],[13,\"flow3\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"angle3\"]],[6,\"std.sin\",[\"angle3\"]]]]],[13,\"combinedFlow\",[6,[7,[6,[7,\"flow1\",\"add\"],[[6,[7,\"flow2\",\"mul\"],[[5,\"0.5\"]]]]],\"add\"],[[6,[7,\"flow3\",\"mul\"],[[5,\"0.25\"]]]]]],[13,\"flowLength\",[6,\"std.max\",[[6,\"std.length\",[\"combinedFlow\"]],[5,\"0.001\"]]]],[13,\"normalizedFlow\",[6,[7,\"combinedFlow\",\"div\"],[\"flowLength\"]]],[13,\"finalFlow\",[6,\"d.vec2f\",[[1,[1,[7,\"normalizedFlow\",\"x\"],\"*\",\"strength\"],\"/\",\"aspect\"],[1,[7,\"normalizedFlow\",\"y\"],\"*\",\"strength\"]]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[7,\"finalFlow\",\"x\"]],[1,[7,\"uv\",\"y\"],\"+\",[7,\"finalFlow\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m,mxNoiseFloat3:()=>Na,\"d.vec3f\":()=>A,TWO_PI:()=>Po,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.max\":()=>B,\"std.length\":()=>he}})&&e.f)({})),tte=w.fn([m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,bt),l=m(pe(i),ae(i)),c=m(__tsover_mul(__tsover_sub(t.x,.5),s),__tsover_sub(t.y,.5)),d=Se(c,l),u=__tsover_mul(.5,__tsover_add(__tsover_mul(s,re(l.x)),re(l.y))),p=__tsover_add(__tsover_div(d,__tsover_mul(2,u)),.5),_=ie(__tsover_mul(p,B(o,1))),f=__tsover_sub(__tsover_mul(Re(__tsover_mul(_,.5)),4),1),h=m(-l.y,l.x),g=__tsover_mul(.5,__tsover_add(__tsover_mul(s,re(l.y)),re(l.x))),y=__tsover_mul(__tsover_mul(a,__tsover_mul(2.02,g)),f),b=c.add(h.mul(y));return m(__tsover_add(__tsover_div(b.x,s),.5),__tsover_add(b.y,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"sliceCount\"},{type:\"i\",name:\"progress\"}],body:[0,[[13,\"a\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"dir\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"a\"]],[6,\"std.sin\",[\"a\"]]]]],[13,\"p\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]],[13,\"grad\",[6,\"std.dot\",[\"p\",\"dir\"]]],[13,\"ext\",[1,[5,\"0.5\"],\"*\",[1,[1,\"aspect\",\"*\",[6,\"std.abs\",[[7,\"dir\",\"x\"]]]],\"+\",[6,\"std.abs\",[[7,\"dir\",\"y\"]]]]]],[13,\"t\",[1,[1,\"grad\",\"/\",[1,[5,\"2\"],\"*\",\"ext\"]],\"+\",[5,\"0.5\"]]],[13,\"idx\",[6,\"std.floor\",[[1,\"t\",\"*\",[6,\"std.max\",[\"sliceCount\",[5,\"1\"]]]]]]],[13,\"sign\",[1,[1,[6,\"std.fract\",[[1,\"idx\",\"*\",[5,\"0.5\"]]]],\"*\",[5,\"4\"]],\"-\",[5,\"1\"]]],[13,\"perp\",[6,\"d.vec2f\",[[4,\"-\",[7,\"dir\",\"y\"]],[7,\"dir\",\"x\"]]]],[13,\"extPerp\",[1,[5,\"0.5\"],\"*\",[1,[1,\"aspect\",\"*\",[6,\"std.abs\",[[7,\"dir\",\"y\"]]]],\"+\",[6,\"std.abs\",[[7,\"dir\",\"x\"]]]]]],[13,\"slide\",[1,[1,\"progress\",\"*\",[1,[5,\"2.02\"],\"*\",\"extPerp\"]],\"*\",\"sign\"]],[13,\"q\",[6,[7,\"p\",\"add\"],[[6,[7,\"perp\",\"mul\"],[\"slide\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,[7,\"q\",\"x\"],\"/\",\"aspect\"],\"+\",[5,\"0.5\"]],[1,[7,\"q\",\"y\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.abs\":()=>re,\"std.floor\":()=>ie,\"std.max\":()=>B,\"std.fract\":()=>Re}})&&e.f)({})),dS=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(r,bt),i=pe(a),l=ae(a),c=__tsover_mul(t.x,s),d=t.y,u=__tsover_sub(c,__tsover_mul(s,.5)),p=__tsover_sub(d,.5),_=__tsover_add(__tsover_mul(u,i),__tsover_mul(p,l)),f=__tsover_add(__tsover_mul(__tsover_mul(u,l),-1),__tsover_mul(p,i)),h=__tsover_add(f,o),g=__tsover_sub(__tsover_mul(_,i),__tsover_mul(h,l)),y=__tsover_add(__tsover_mul(_,l),__tsover_mul(h,i));return m(__tsover_add(__tsover_div(g,s),.5),__tsover_add(y,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"offset\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"au\",[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"]],[13,\"av\",[7,\"uv\",\"y\"]],[13,\"cu\",[1,\"au\",\"-\",[1,\"aspect\",\"*\",[5,\"0.5\"]]]],[13,\"cv\",[1,\"av\",\"-\",[5,\"0.5\"]]],[13,\"projectedU\",[1,[1,\"cu\",\"*\",\"cosA\"],\"+\",[1,\"cv\",\"*\",\"sinA\"]]],[13,\"projectedV\",[1,[1,[1,\"cu\",\"*\",\"sinA\"],\"*\",[4,\"-\",[5,\"1\"]]],\"+\",[1,\"cv\",\"*\",\"cosA\"]]],[13,\"distPV\",[1,\"projectedV\",\"+\",\"offset\"]],[13,\"dcu\",[1,[1,\"projectedU\",\"*\",\"cosA\"],\"-\",[1,\"distPV\",\"*\",\"sinA\"]]],[13,\"dcv\",[1,[1,\"projectedU\",\"*\",\"sinA\"],\"+\",[1,\"distPV\",\"*\",\"cosA\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"dcu\",\"/\",\"aspect\"],\"+\",[5,\"0.5\"]],[1,\"dcv\",\"+\",[5,\"0.5\"]]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),ste=w.fn([n,n,n,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_mul(s,bt),d=pe(c),u=ae(c),p=B(l,1),_=__tsover_mul(i.x,l),f=i.y,h=__tsover_sub(_,__tsover_mul(l,.5)),g=__tsover_sub(f,.5),y=__tsover_add(__tsover_mul(h,d),__tsover_mul(g,u)),b=ie(__tsover_div(__tsover_mul(y,t),p)),x=__tsover_mul(o,.137),P=Vl(__tsover_add(b,x)),C=Vl(__tsover_add(__tsover_add(b,x),42.7)),E=__tsover_mul(__tsover_mul(a,C),.2);return dS(i,l,s,__tsover_mul(__tsover_add(P,E),r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"count\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"longestAxis\",[6,\"std.max\",[\"aspect\",[5,\"1\"]]]],[13,\"au\",[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"]],[13,\"av\",[7,\"uv\",\"y\"]],[13,\"cu\",[1,\"au\",\"-\",[1,\"aspect\",\"*\",[5,\"0.5\"]]]],[13,\"cv\",[1,\"av\",\"-\",[5,\"0.5\"]]],[13,\"projectedU\",[1,[1,\"cu\",\"*\",\"cosA\"],\"+\",[1,\"cv\",\"*\",\"sinA\"]]],[13,\"barCoord\",[1,[1,\"projectedU\",\"*\",\"count\"],\"/\",\"longestAxis\"]],[13,\"barIndex\",[6,\"std.floor\",[\"barCoord\"]]],[13,\"seedOffset\",[1,\"seed\",\"*\",[5,\"0.137\"]]],[13,\"staticOffset\",[6,\"legacySinHash11\",[[1,\"barIndex\",\"+\",\"seedOffset\"]]]],[13,\"barSpeed\",[6,\"legacySinHash11\",[[1,[1,\"barIndex\",\"+\",\"seedOffset\"],\"+\",[5,\"42.7\"]]]]],[13,\"animOffset\",[1,[1,\"animTime\",\"*\",\"barSpeed\"],\"*\",[5,\"0.2\"]]],[13,\"offset\",[1,[1,\"staticOffset\",\"+\",\"animOffset\"],\"*\",\"intensity\"]],[10,[6,\"barSampleUV\",[\"uv\",\"aspect\",\"angle\",\"offset\"]]]]]},externals:{DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.max\":()=>B,\"std.floor\":()=>ie,legacySinHash11:()=>Vl,barSampleUV:()=>dS}})&&e.f)({})),rte=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=r>1,a=B(N(__tsover_mul(s,r),s,o),1),i=B(N(s,__tsover_div(s,r),o),1),l=ie(__tsover_mul(t.x,a)),c=ie(__tsover_mul(t.y,i));return m(__tsover_div(__tsover_add(l,.5),a),__tsover_div(__tsover_add(c,.5),i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"gridSize\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"isWide\",[1,\"aspect\",\">\",[5,\"1\"]]],[13,\"cellsX\",[6,\"std.max\",[[6,\"std.select\",[[1,\"gridSize\",\"*\",\"aspect\"],\"gridSize\",\"isWide\"]],[5,\"1\"]]]],[13,\"cellsY\",[6,\"std.max\",[[6,\"std.select\",[\"gridSize\",[1,\"gridSize\",\"/\",\"aspect\"],\"isWide\"]],[5,\"1\"]]]],[13,\"cellIndexX\",[6,\"std.floor\",[[1,[7,\"uv\",\"x\"],\"*\",\"cellsX\"]]]],[13,\"cellIndexY\",[6,\"std.floor\",[[1,[7,\"uv\",\"y\"],\"*\",\"cellsY\"]]]],[10,[6,\"d.vec2f\",[[1,[1,\"cellIndexX\",\"+\",[5,\"0.5\"]],\"/\",\"cellsX\"],[1,[1,\"cellIndexY\",\"+\",[5,\"0.5\"]],\"/\",\"cellsY\"]]]]]]},externals:{\"std.max\":()=>B,\"std.select\":()=>N,\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),ote=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let o=G(s,m(-.1,-.1),m(.1,.1));return t.sub(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"disp\"}],body:[0,[[13,\"maxDisp\",[5,\"0.1\"]],[13,\"clamped\",[6,\"std.clamp\",[\"disp\",[6,\"d.vec2f\",[[4,\"-\",\"maxDisp\"],[4,\"-\",\"maxDisp\"]]],[6,\"d.vec2f\",[\"maxDisp\",\"maxDisp\"]]]]],[10,[6,[7,\"uv\",\"sub\"],[\"clamped\"]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m}})&&e.f)({})),ate=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=s.mul(__tsover_mul(r,.1)),a=.15,i=G(o,m(-a,-a),m(a,a));return t.sub(i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"disp\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"scaled\",[6,[7,\"disp\",\"mul\"],[[1,\"intensity\",\"*\",[5,\"0.1\"]]]]],[13,\"maxDisp\",[5,\"0.15\"]],[13,\"clamped\",[6,\"std.clamp\",[\"scaled\",[6,\"d.vec2f\",[[4,\"-\",\"maxDisp\"],[4,\"-\",\"maxDisp\"]]],[6,\"d.vec2f\",[\"maxDisp\",\"maxDisp\"]]]]],[10,[6,[7,\"uv\",\"sub\"],[\"clamped\"]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m}})&&e.f)({})),nte=w.fn([m,S,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=B(s.w,1e-4),i=__tsover_div(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_sub(__tsover_div(s.x,a),.5),2),s.w),o),r),l=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_sub(__tsover_div(s.y,a),.5),2),s.w),o);return m(__tsover_add(t.x,i),__tsover_add(t.y,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"src\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"a\",[6,\"std.max\",[[7,\"src\",\"w\"],[5,\"0.0001\"]]]],[13,\"dx\",[1,[1,[1,[1,[1,[1,[7,\"src\",\"x\"],\"/\",\"a\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[7,\"src\",\"w\"]],\"*\",\"amount\"],\"/\",\"aspect\"]],[13,\"dy\",[1,[1,[1,[1,[1,[7,\"src\",\"y\"],\"/\",\"a\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[7,\"src\",\"w\"]],\"*\",\"amount\"]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",\"dx\"],[1,[7,\"uv\",\"y\"],\"+\",\"dy\"]]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),ite=w.fn([m,S,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=B(s.w,1e-4),l=Se(A(__tsover_div(s.x,i),__tsover_div(s.y,i),__tsover_div(s.z,i)),A(.2126,.7152,.0722)),c=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_sub(l,.5),2),s.w),o),d=__tsover_mul(a,bt),u=__tsover_div(__tsover_mul(c,pe(d)),r),p=__tsover_mul(c,ae(d));return m(__tsover_add(t.x,u),__tsover_add(t.y,p))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"src\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"angleDeg\"}],body:[0,[[13,\"a\",[6,\"std.max\",[[7,\"src\",\"w\"],[5,\"0.0001\"]]]],[13,\"lum\",[6,\"std.dot\",[[6,\"d.vec3f\",[[1,[7,\"src\",\"x\"],\"/\",\"a\"],[1,[7,\"src\",\"y\"],\"/\",\"a\"],[1,[7,\"src\",\"z\"],\"/\",\"a\"]]],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"scalar\",[1,[1,[1,[1,\"lum\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[7,\"src\",\"w\"]],\"*\",\"amount\"]],[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"dx\",[1,[1,\"scalar\",\"*\",[6,\"std.cos\",[\"angleRad\"]]],\"/\",\"aspect\"]],[13,\"dy\",[1,\"scalar\",\"*\",[6,\"std.sin\",[\"angleRad\"]]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",\"dx\"],[1,[7,\"uv\",\"y\"],\"+\",\"dy\"]]]]]]},externals:{\"std.max\":()=>B,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),g0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({refractedUV:m,chrOff:m,slope:n}),\"FlutedGeom\"),lte=w.fn([m,n,n,n,n,n,n,n,n,n,A],g0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=p.x,f=p.y,h=p.z,g=__tsover_div(__tsover_mul(o,Wo),180),y=pe(g),b=ae(g),x=m(__tsover_mul(__tsover_sub(t.x,.5),s),__tsover_sub(t.y,.5)),P=__tsover_add(__tsover_mul(x.x,y),__tsover_mul(x.y,b)),C=__tsover_add(__tsover_mul(-x.x,b),__tsover_mul(x.y,y)),E=__tsover_mul(ae(__tsover_add(__tsover_mul(__tsover_mul(C,c),__tsover_mul(Wo,2)),__tsover_mul(r,2))),l),R=__tsover_add(P,__tsover_mul(h,E)),U=__tsover_add(__tsover_mul(R,a),r),L=__tsover_mul(__tsover_sub(Re(U),.5),2),k=B(re(L),1e-4),D=K(_,f,i),O=__tsover_mul(no(L),vt(k,D)),F=__tsover_div(.5,B(a,.001)),q=-__tsover_mul(__tsover_mul(O,d),F),Y=m(__tsover_add(t.x,__tsover_div(__tsover_mul(q,y),s)),__tsover_add(t.y,__tsover_mul(q,b))),V=__tsover_mul(__tsover_mul(q,u),.5);return g0({refractedUV:Y,chrOff:m(__tsover_div(__tsover_mul(V,y),s),__tsover_mul(V,b)),slope:O})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"waveAmp\"},{type:\"i\",name:\"waveFreq\"},{type:\"i\",name:\"refraction\"},{type:\"i\",name:\"aberration\"},{type:\"i\",name:\"shapeParams\"}],body:[0,[[13,\"expHi\",[7,\"shapeParams\",\"x\"]],[13,\"expLo\",[7,\"shapeParams\",\"y\"]],[13,\"wavesFlag\",[7,\"shapeParams\",\"z\"]],[13,\"r\",[1,[1,\"angleDeg\",\"*\",\"PI\"],\"/\",[5,\"180\"]]],[13,\"cosA\",[6,\"std.cos\",[\"r\"]]],[13,\"sinA\",[6,\"std.sin\",[\"r\"]]],[13,\"aspCorr\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]],[13,\"u\",[1,[1,[7,\"aspCorr\",\"x\"],\"*\",\"cosA\"],\"+\",[1,[7,\"aspCorr\",\"y\"],\"*\",\"sinA\"]]],[13,\"v\",[1,[1,[4,\"-\",[7,\"aspCorr\",\"x\"]],\"*\",\"sinA\"],\"+\",[1,[7,\"aspCorr\",\"y\"],\"*\",\"cosA\"]]],[13,\"waveTerm\",[1,[6,\"std.sin\",[[1,[1,[1,\"v\",\"*\",\"waveFreq\"],\"*\",[1,\"PI\",\"*\",[5,\"2\"]]],\"+\",[1,\"t\",\"*\",[5,\"2\"]]]]],\"*\",\"waveAmp\"]],[13,\"fluteU\",[1,\"u\",\"+\",[1,\"wavesFlag\",\"*\",\"waveTerm\"]]],[13,\"flutePos\",[1,[1,\"fluteU\",\"*\",\"frequency\"],\"+\",\"t\"]],[13,\"cellPos\",[1,[1,[6,\"std.fract\",[\"flutePos\"]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"absCell\",[6,\"std.max\",[[6,\"std.abs\",[\"cellPos\"]],[5,\"0.0001\"]]]],[13,\"exponent\",[6,\"std.mix\",[\"expHi\",\"expLo\",\"softness\"]]],[13,\"slope\",[1,[6,\"std.sign\",[\"cellPos\"]],\"*\",[6,\"std.pow\",[\"absCell\",\"exponent\"]]]],[13,\"halfCell\",[1,[5,\"0.5\"],\"/\",[6,\"std.max\",[\"frequency\",[5,\"0.001\"]]]]],[13,\"refrU\",[4,\"-\",[1,[1,\"slope\",\"*\",\"refraction\"],\"*\",\"halfCell\"]]],[13,\"refractedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,[1,\"refrU\",\"*\",\"cosA\"],\"/\",\"aspect\"]],[1,[7,\"uv\",\"y\"],\"+\",[1,\"refrU\",\"*\",\"sinA\"]]]]],[13,\"chrU\",[1,[1,\"refrU\",\"*\",\"aberration\"],\"*\",[5,\"0.5\"]]],[13,\"chrOff\",[6,\"d.vec2f\",[[1,[1,\"chrU\",\"*\",\"cosA\"],\"/\",\"aspect\"],[1,\"chrU\",\"*\",\"sinA\"]]]],[10,[6,\"FlutedGeom\",[[104,{refractedUV:\"refractedUV\",chrOff:\"chrOff\",slope:\"slope\"}]]]]]]},externals:{PI:()=>Wo,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.mix\":()=>K,\"std.sign\":()=>no,\"std.pow\":()=>vt,FlutedGeom:()=>g0}})&&e.f)({})),cte=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=ye(__tsover_mul(t,t),1),i=Ue(__tsover_sub(1,a)),l=__tsover_div(__tsover_mul(s,Wo),360),c=ae(l),d=pe(l),u=B(__tsover_add(__tsover_mul(t,c),__tsover_mul(i,d)),0),p=Ia(__tsover_sub(8,__tsover_mul(o,7))),_=vt(__tsover_sub(1,i),5),f=__tsover_add(.04,__tsover_mul(.96,_));return __tsover_mul(__tsover_mul(vt(u,p),f),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"slope\"},{type:\"i\",name:\"lightAngleDeg\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"slopeSq\",[6,\"std.min\",[[1,\"slope\",\"*\",\"slope\"],[5,\"1\"]]]],[13,\"nz\",[6,\"std.sqrt\",[[1,[5,\"1\"],\"-\",\"slopeSq\"]]]],[13,\"half\",[1,[1,\"lightAngleDeg\",\"*\",\"PI\"],\"/\",[5,\"360\"]]],[13,\"hx\",[6,\"std.sin\",[\"half\"]]],[13,\"hy\",[6,\"std.cos\",[\"half\"]]],[13,\"nDotH\",[6,\"std.max\",[[1,[1,\"slope\",\"*\",\"hx\"],\"+\",[1,\"nz\",\"*\",\"hy\"]],[5,\"0\"]]]],[13,\"shininess\",[6,\"std.exp2\",[[1,[5,\"8\"],\"-\",[1,\"softness\",\"*\",[5,\"7\"]]]]]],[13,\"fresnel\",[6,\"std.pow\",[[1,[5,\"1\"],\"-\",\"nz\"],[5,\"5\"]]]],[13,\"fresnelMix\",[1,[5,\"0.04\"],\"+\",[1,[5,\"0.96\"],\"*\",\"fresnel\"]]],[10,[1,[1,[6,\"std.pow\",[\"nDotH\",\"shininess\"]],\"*\",\"fresnelMix\"],\"*\",\"intensity\"]]]]},externals:{\"std.min\":()=>ye,\"std.sqrt\":()=>Ue,PI:()=>Wo,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.max\":()=>B,\"std.exp2\":()=>Ia,\"std.pow\":()=>vt}})&&e.f)({})),y0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({curlUV:m,theta:n,distPastCrease:n,distIntoPeel:n,flatReach:n,peelReach:n,foldBlend:n,showCurl:n}),\"PagePeelGeom\"),dte=w.fn([m,n,n,n,n,n,n,n],y0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=m(__tsover_mul(t.x,s),t.y),u=m(__tsover_mul(r,s),o),p=m(__tsover_mul(a,s),i).sub(u),_=he(p),f=p.mul(__tsover_div(1,_)),h=Se(d.sub(u),f),g=__tsover_mul(l,_),y=g,b=B(__tsover_mul(c,_),1e-4),x=__tsover_div(g,b),P=__tsover_div(__tsover_sub(h,y),b),C=fb(G(P,0,1)),E=__tsover_mul(Et(0,P),Et(P,1)),R=Et(C,x),U=__tsover_sub(g,__tsover_mul(b,C)),L=d.add(f.mul(__tsover_sub(U,h))),k=m(__tsover_div(L.x,s),L.y),D=__tsover_mul(__tsover_mul(__tsover_mul(Et(0,k.x),Et(k.x,1)),Et(0,k.y)),Et(k.y,1)),O=__tsover_sub(1,pe(G(x,0,ev))),F=__tsover_mul(b,O),q=B(__tsover_add(b,__tsover_mul(F,.5)),1e-4),Y=__tsover_sub(h,y),V=B(__tsover_mul(F,2.5),1e-4);return y0({curlUV:k,theta:C,distPastCrease:Y,distIntoPeel:__tsover_sub(y,h),flatReach:q,peelReach:V,foldBlend:be(__tsover_sub(y,.0015),__tsover_add(y,.0015),h),showCurl:__tsover_mul(__tsover_mul(E,R),D)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"cornerX\"},{type:\"i\",name:\"cornerY\"},{type:\"i\",name:\"oppX\"},{type:\"i\",name:\"oppY\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"radius\"}],body:[0,[[13,\"pA\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"cornerA\",[6,\"d.vec2f\",[[1,\"cornerX\",\"*\",\"aspect\"],\"cornerY\"]]],[13,\"oppA\",[6,\"d.vec2f\",[[1,\"oppX\",\"*\",\"aspect\"],\"oppY\"]]],[13,\"axisVec\",[6,[7,\"oppA\",\"sub\"],[\"cornerA\"]]],[13,\"L\",[6,\"std.length\",[\"axisVec\"]]],[13,\"axis\",[6,[7,\"axisVec\",\"mul\"],[[1,[5,\"1\"],\"/\",\"L\"]]]],[13,\"t\",[6,\"std.dot\",[[6,[7,\"pA\",\"sub\"],[\"cornerA\"]],\"axis\"]]],[13,\"peel\",[1,\"amount\",\"*\",\"L\"]],[13,\"cFold\",\"peel\"],[13,\"R\",[6,\"std.max\",[[1,\"radius\",\"*\",\"L\"],[5,\"0.0001\"]]]],[13,\"thetaMax\",[1,\"peel\",\"/\",\"R\"]],[13,\"X\",[1,[1,\"t\",\"-\",\"cFold\"],\"/\",\"R\"]],[13,\"Xc\",[6,\"std.clamp\",[\"X\",[5,\"0\"],[5,\"1\"]]]],[13,\"theta\",[6,\"std.asin\",[\"Xc\"]]],[13,\"inBand\",[1,[6,\"std.step\",[[5,\"0\"],\"X\"]],\"*\",[6,\"std.step\",[\"X\",[5,\"1\"]]]]],[13,\"paper\",[6,\"std.step\",[\"theta\",\"thetaMax\"]]],[13,\"cCurl\",[1,\"peel\",\"-\",[1,\"R\",\"*\",\"theta\"]]],[13,\"sourceA\",[6,[7,\"pA\",\"add\"],[[6,[7,\"axis\",\"mul\"],[[1,\"cCurl\",\"-\",\"t\"]]]]]],[13,\"curlUV\",[6,\"d.vec2f\",[[1,[7,\"sourceA\",\"x\"],\"/\",\"aspect\"],[7,\"sourceA\",\"y\"]]]],[13,\"curlInBounds\",[1,[1,[1,[6,\"std.step\",[[5,\"0\"],[7,\"curlUV\",\"x\"]]],\"*\",[6,\"std.step\",[[7,\"curlUV\",\"x\"],[5,\"1\"]]]],\"*\",[6,\"std.step\",[[5,\"0\"],[7,\"curlUV\",\"y\"]]]],\"*\",[6,\"std.step\",[[7,\"curlUV\",\"y\"],[5,\"1\"]]]]],[13,\"liftFrac\",[1,[5,\"1\"],\"-\",[6,\"std.cos\",[[6,\"std.clamp\",[\"thetaMax\",[5,\"0\"],\"HALF_PI\"]]]]]],[13,\"lipHeight\",[1,\"R\",\"*\",\"liftFrac\"]],[13,\"flatReach\",[6,\"std.max\",[[1,\"R\",\"+\",[1,\"lipHeight\",\"*\",[5,\"0.5\"]]],[5,\"0.0001\"]]]],[13,\"distPastCrease\",[1,\"t\",\"-\",\"cFold\"]],[13,\"peelReach\",[6,\"std.max\",[[1,\"lipHeight\",\"*\",[5,\"2.5\"]],[5,\"0.0001\"]]]],[13,\"distIntoPeel\",[1,\"cFold\",\"-\",\"t\"]],[13,\"foldBlend\",[6,\"std.smoothstep\",[[1,\"cFold\",\"-\",[5,\"0.0015\"]],[1,\"cFold\",\"+\",[5,\"0.0015\"]],\"t\"]]],[13,\"showCurl\",[1,[1,\"inBand\",\"*\",\"paper\"],\"*\",\"curlInBounds\"]],[10,[6,\"PagePeelGeom\",[[104,{curlUV:\"curlUV\",theta:\"theta\",distPastCrease:\"distPastCrease\",distIntoPeel:\"distIntoPeel\",flatReach:\"flatReach\",peelReach:\"peelReach\",foldBlend:\"foldBlend\",showCurl:\"showCurl\"}]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.dot\":()=>Se,\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.asin\":()=>fb,\"std.step\":()=>Et,\"std.cos\":()=>pe,HALF_PI:()=>ev,\"std.smoothstep\":()=>be,PagePeelGeom:()=>y0}})&&e.f)({})),ute=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=pe(t);return __tsover_sub(1,__tsover_mul(s,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"theta\"},{type:\"i\",name:\"shading\"}],body:[0,[[13,\"facing\",[6,\"std.cos\",[\"theta\"]]],[10,[1,[5,\"1\"],\"-\",[1,\"shading\",\"*\",\"facing\"]]]]]},externals:{\"std.cos\":()=>pe}})&&e.f)({})),pte=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(t,ev),a=K(.05,.4,r),i=__tsover_div(__tsover_sub(o,.65),a);return __tsover_mul(Tt(__tsover_mul(__tsover_mul(i,i),-.5)),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"theta\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"thetaN\",[1,\"theta\",\"/\",\"HALF_PI\"]],[13,\"specSigma\",[6,\"std.mix\",[[5,\"0.05\"],[5,\"0.4\"],\"softness\"]]],[13,\"specDelta\",[1,[1,\"thetaN\",\"-\",[5,\"0.65\"]],\"/\",\"specSigma\"]],[10,[1,[6,\"std.exp\",[[1,[1,\"specDelta\",\"*\",\"specDelta\"],\"*\",[4,\"-\",[5,\"0.5\"]]]]],\"*\",\"intensity\"]]]]},externals:{HALF_PI:()=>ev,\"std.mix\":()=>K,\"std.exp\":()=>Tt}})&&e.f)({})),yv=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(be(0,.04,t),vt(__tsover_sub(1,t),.7))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"amount\"}],body:[0,[[10,[1,[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.04\"],\"amount\"]],\"*\",[6,\"std.pow\",[[1,[5,\"1\"],\"-\",\"amount\"],[5,\"0.7\"]]]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.pow\":()=>vt}})&&e.f)({})),bv=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_mul(vt(__tsover_sub(1,be(0,s,t)),r),Et(0,t))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"reach\"},{type:\"i\",name:\"exponent\"}],body:[0,[[10,[1,[6,\"std.pow\",[[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],\"reach\",\"dist\"]]],\"exponent\"]],\"*\",[6,\"std.step\",[[5,\"0\"],\"dist\"]]]]]]},externals:{\"std.pow\":()=>vt,\"std.smoothstep\":()=>be,\"std.step\":()=>Et}})&&e.f)({})),mte=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(bv(t,s,2),yv(r));return __tsover_sub(1,__tsover_mul(__tsover_mul(o,a),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"reach\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"shadow\"}],body:[0,[[13,\"fall\",[1,[6,\"shadowFalloff\",[\"dist\",\"reach\",[5,\"2\"]]],\"*\",[6,\"liftShadowStrength\",[\"amount\"]]]],[10,[1,[5,\"1\"],\"-\",[1,[1,\"shadow\",\"*\",\"fall\"],\"*\",[5,\"0.5\"]]]]]]},externals:{shadowFalloff:()=>bv,liftShadowStrength:()=>yv}})&&e.f)({})),_te=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(__tsover_mul(bv(t,s,1.5),yv(r)),o);return __tsover_mul(a,.6)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"reach\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"shadow\"}],body:[0,[[13,\"fall\",[1,[1,[6,\"shadowFalloff\",[\"dist\",\"reach\",[5,\"1.5\"]]],\"*\",[6,\"liftShadowStrength\",[\"amount\"]]],\"*\",\"shadow\"]],[10,[1,\"fall\",\"*\",[5,\"0.6\"]]]]]},externals:{shadowFalloff:()=>bv,liftShadowStrength:()=>yv}})&&e.f)({})),fte=w.fn([S,S,n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=S(t.xyz.mul(r).add(__tsover_mul(o,t.w)),t.w),u=S(s.xyz.mul(a),s.w);return N(K(S(0,0,0,i),u,S(l)),d,c>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"curlSample\"},{type:\"i\",name:\"flatSample\"},{type:\"i\",name:\"curlBright\"},{type:\"i\",name:\"spec\"},{type:\"i\",name:\"flatShade\"},{type:\"i\",name:\"peelAlpha\"},{type:\"i\",name:\"foldBlend\"},{type:\"i\",name:\"showCurl\"}],body:[0,[[13,\"litRgb\",[6,[7,[6,[7,[7,\"curlSample\",\"xyz\"],\"mul\"],[\"curlBright\"]],\"add\"],[[1,\"spec\",\"*\",[7,\"curlSample\",\"w\"]]]]],[13,\"curlColor\",[6,\"d.vec4f\",[\"litRgb\",[7,\"curlSample\",\"w\"]]]],[13,\"flatColor\",[6,\"d.vec4f\",[[6,[7,[7,\"flatSample\",\"xyz\"],\"mul\"],[\"flatShade\"]],[7,\"flatSample\",\"w\"]]]],[13,\"peeledColor\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],\"peelAlpha\"]]],[13,\"base\",[6,\"std.mix\",[\"peeledColor\",\"flatColor\",[6,\"d.vec4f\",[\"foldBlend\"]]]]],[10,[6,\"std.select\",[\"base\",\"curlColor\",[1,\"showCurl\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"d.vec4f\":()=>S,\"std.mix\":()=>K,\"std.select\":()=>N}})&&e.f)({})),b0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({uv:m,coverage:n,normal:A}),\"SphereBulge\"),vte=w.fn([m,m,m,n,n],b0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_div(s.x,s.y),l=r.x,c=__tsover_sub(1,r.y),d=__tsover_mul(__tsover_sub(t.x,l),i),u=__tsover_sub(t.y,c),p=__tsover_div(__tsover_mul(d,2),o),_=__tsover_div(__tsover_mul(u,2),o),f=__tsover_add(__tsover_mul(p,p),__tsover_mul(_,_)),h=__tsover_sub(1,be(.98,1,f)),g=Ue(B(n(0),__tsover_sub(1,f))),y=__tsover_add(1,__tsover_mul(g,a)),b=__tsover_div(__tsover_mul(__tsover_div(p,y),o),2),x=__tsover_div(__tsover_mul(__tsover_div(_,y),o),2),P=__tsover_add(__tsover_div(b,i),l),C=__tsover_add(x,c),E=vs(A(p,_,g));return b0({uv:m(P,C),coverage:h,normal:E})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"depth\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"centerX\",[7,\"center\",\"x\"]],[13,\"centerY\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"cuX\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"],\"*\",\"aspect\"]],[13,\"cuY\",[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"]],[13,\"sphereX\",[1,[1,\"cuX\",\"*\",[5,\"2\"]],\"/\",\"radius\"]],[13,\"sphereY\",[1,[1,\"cuY\",\"*\",[5,\"2\"]],\"/\",\"radius\"]],[13,\"radiusSq\",[1,[1,\"sphereX\",\"*\",\"sphereX\"],\"+\",[1,\"sphereY\",\"*\",\"sphereY\"]]],[13,\"sphereAlpha\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0.98\"],[5,\"1\"],\"radiusSq\"]]]],[13,\"z\",[6,\"std.sqrt\",[[6,\"std.max\",[[6,\"d.f32\",[[5,\"0\"]]],[1,[5,\"1\"],\"-\",\"radiusSq\"]]]]]],[13,\"depthFactor\",[1,[5,\"1\"],\"+\",[1,\"z\",\"*\",\"depth\"]]],[13,\"finalX\",[1,[1,[1,\"sphereX\",\"/\",\"depthFactor\"],\"*\",\"radius\"],\"/\",[5,\"2\"]]],[13,\"finalY\",[1,[1,[1,\"sphereY\",\"/\",\"depthFactor\"],\"*\",\"radius\"],\"/\",[5,\"2\"]]],[13,\"transformedX\",[1,[1,\"finalX\",\"/\",\"aspect\"],\"+\",\"centerX\"]],[13,\"transformedY\",[1,\"finalY\",\"+\",\"centerY\"]],[13,\"normal\",[6,\"std.normalize\",[[6,\"d.vec3f\",[\"sphereX\",\"sphereY\",\"z\"]]]]],[10,[6,\"SphereBulge\",[[104,{uv:[6,\"d.vec2f\",[\"transformedX\",\"transformedY\"]],coverage:\"sphereAlpha\",normal:\"normal\"}]]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"d.f32\":()=>n,\"std.normalize\":()=>vs,\"d.vec3f\":()=>A,SphereBulge:()=>b0,\"d.vec2f\":()=>m}})&&e.f)({})),hte=w.fn([A,m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=A(0,0,1),i=__tsover_add(1,__tsover_mul(4,__tsover_sub(1,o))),l=vt(__tsover_sub(1,B(Se(t,a),n(0))),i),c=__tsover_mul(__tsover_sub(s.x,.5),2),d=__tsover_mul(__tsover_sub(__tsover_sub(1,s.y),.5),2),u=vs(m(__tsover_add(t.x,1e-4),__tsover_add(t.y,1e-4))),p=vs(m(__tsover_add(c,1e-4),__tsover_add(d,1e-4))),_=vt(B(n(0),Se(u,p)),n(2));return __tsover_mul(__tsover_mul(__tsover_mul(l,_),r),2)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"normal\"},{type:\"i\",name:\"lightPos\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"viewDir\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"1\"]]]],[13,\"fresnelPower\",[1,[5,\"1\"],\"+\",[1,[5,\"4\"],\"*\",[1,[5,\"1\"],\"-\",\"softness\"]]]],[13,\"fresnel\",[6,\"std.pow\",[[1,[5,\"1\"],\"-\",[6,\"std.max\",[[6,\"std.dot\",[\"normal\",\"viewDir\"]],[6,\"d.f32\",[[5,\"0\"]]]]]],\"fresnelPower\"]]],[13,\"lightDir2Dx\",[1,[1,[7,\"lightPos\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"lightDir2Dy\",[1,[1,[1,[5,\"1\"],\"-\",[7,\"lightPos\",\"y\"]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"normalDir\",[6,\"std.normalize\",[[6,\"d.vec2f\",[[1,[7,\"normal\",\"x\"],\"+\",[5,\"0.0001\"]],[1,[7,\"normal\",\"y\"],\"+\",[5,\"0.0001\"]]]]]]],[13,\"lightDirNorm\",[6,\"std.normalize\",[[6,\"d.vec2f\",[[1,\"lightDir2Dx\",\"+\",[5,\"0.0001\"]],[1,\"lightDir2Dy\",\"+\",[5,\"0.0001\"]]]]]]],[13,\"directionality\",[6,\"std.max\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"std.dot\",[\"normalDir\",\"lightDirNorm\"]]]]],[13,\"directionalBias\",[6,\"std.pow\",[\"directionality\",[6,\"d.f32\",[[5,\"2\"]]]]]],[10,[1,[1,[1,\"fresnel\",\"*\",\"directionalBias\"],\"*\",\"intensity\"],\"*\",[5,\"2\"]]]]]},externals:{\"d.vec3f\":()=>A,\"std.pow\":()=>vt,\"std.max\":()=>B,\"std.dot\":()=>Se,\"d.f32\":()=>n,\"std.normalize\":()=>vs,\"d.vec2f\":()=>m}})&&e.f)({})),gte=w.fn([S,A,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return S(__tsover_add(t.x,__tsover_mul(s.x,r)),__tsover_add(t.y,__tsover_mul(s.y,r)),__tsover_add(t.z,__tsover_mul(s.z,r)),__tsover_mul(t.w,o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"straight\"},{type:\"i\",name:\"lightColor\"},{type:\"i\",name:\"rim\"},{type:\"i\",name:\"coverage\"}],body:[0,[[13,\"r\",[1,[7,\"straight\",\"x\"],\"+\",[1,[7,\"lightColor\",\"x\"],\"*\",\"rim\"]]],[13,\"g\",[1,[7,\"straight\",\"y\"],\"+\",[1,[7,\"lightColor\",\"y\"],\"*\",\"rim\"]]],[13,\"b\",[1,[7,\"straight\",\"z\"],\"+\",[1,[7,\"lightColor\",\"z\"],\"*\",\"rim\"]]],[13,\"a\",[1,[7,\"straight\",\"w\"],\"*\",\"coverage\"]],[10,[6,\"d.vec4f\",[\"r\",\"g\",\"b\",\"a\"]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({}));function yte(e,t,s){let r=e(t.add(s)),o=e(t),a=e(t.add(s.mul(-1)));return Ct(r.member(\"r\"),o.member(\"g\"),a.member(\"b\"),o.member(\"a\"))}function bte(e,t,s){return s===1?e(t).mul(W($3,\"edgeTransparentMask\",[t])):e(s===2?W(Hm,\"edgeMirrorUV\",[t]):s===3?W(jm,\"edgeWrapUV\",[t]):W(N3,\"edgeClampUV\",[t]))}var uS=1/360,xte=w.fn([m,m,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=i0(t),l=i0(s),c=l.sub(i),d=he(c),u=vs(c),p=B(a.y,1e-6),_=__tsover_div(a.x,p),f=__tsover_mul(__tsover_mul(r,bt),-1),h=pe(f),g=ae(f),y=i.add(l).mul(.5),b=o.sub(y),x=Pn(m(__tsover_mul(b.x,_),b.y),h,g),P=Se(m(__tsover_div(x.x,_),x.y).add(y).sub(i),u);return __tsover_div(P,B(d,1e-6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"start\"},{type:\"i\",name:\"end\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"startPos\",[6,\"unflipPosition\",[\"start\"]]],[13,\"endPos\",[6,\"unflipPosition\",[\"end\"]]],[13,\"gradientVector\",[6,[7,\"endPos\",\"sub\"],[\"startPos\"]]],[13,\"gradientLength\",[6,\"std.length\",[\"gradientVector\"]]],[13,\"gradientDir\",[6,\"std.normalize\",[\"gradientVector\"]]],[13,\"safeViewportY\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]],[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",\"safeViewportY\"]],[13,\"angleRad\",[1,[1,\"angle\",\"*\",\"DEG_TO_RAD\"],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"cosAngle\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinAngle\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"midpoint\",[6,[7,[6,[7,\"startPos\",\"add\"],[\"endPos\"]],\"mul\"],[[5,\"0.5\"]]]],[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[\"midpoint\"]]],[13,\"rotated\",[6,\"geom.rotate2\",[[6,\"d.vec2f\",[[1,[7,\"centeredUV\",\"x\"],\"*\",\"aspect\"],[7,\"centeredUV\",\"y\"]]],\"cosAngle\",\"sinAngle\"]]],[13,\"rotatedUV\",[6,[7,[6,\"d.vec2f\",[[1,[7,\"rotated\",\"x\"],\"/\",\"aspect\"],[7,\"rotated\",\"y\"]]],\"add\"],[\"midpoint\"]]],[13,\"relativePos\",[6,[7,\"rotatedUV\",\"sub\"],[\"startPos\"]]],[13,\"projection\",[6,\"std.dot\",[\"relativePos\",\"gradientDir\"]]],[10,[1,\"projection\",\"/\",[6,\"std.max\",[\"gradientLength\",[5,\"0.000001\"]]]]]]]},externals:{unflipPosition:()=>i0,\"std.length\":()=>he,\"std.normalize\":()=>vs,\"std.max\":()=>B,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"geom.rotate2\":()=>Pn,\"d.vec2f\":()=>m,\"std.dot\":()=>Se}})&&e.f)({})),S4=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=An(re(t),2);return N(s,__tsover_sub(2,s),s>1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[13,\"m\",[6,\"std.mod\",[[6,\"std.abs\",[\"t\"]],[5,\"2\"]]]],[10,[6,\"std.select\",[\"m\",[1,[5,\"2\"],\"-\",\"m\"],[1,\"m\",\">\",[5,\"1\"]]]]]]]},externals:{\"std.mod\":()=>An,\"std.abs\":()=>re,\"std.select\":()=>N}})&&e.f)({})),Ate=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Re(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[6,\"std.fract\",[\"t\"]]]]]},externals:{\"std.fract\":()=>Re}})&&e.f)({})),pS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return G(t,0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[6,\"std.clamp\",[\"t\",[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({})),wte=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=N(n(0),n(1),t>=0),r=N(n(0),n(1),t<=1);return __tsover_mul(s,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[13,\"inLow\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"t\",\">=\",[5,\"0\"]]]]],[13,\"inHigh\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"t\",\"<=\",[5,\"1\"]]]]],[10,[1,\"inLow\",\"*\",\"inHigh\"]]]]},externals:{\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})),Tte=w.fn([m,n,n,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=cc(a,t,i),c=__tsover_mul(s,bt),d=Pn(l,pe(c),-ae(c)),u=he(m(__tsover_mul(d.x,r),d.y)),p=B(o,1e-6);return __tsover_div(u,p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"skewAngle\"},{type:\"i\",name:\"ellipseAspect\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"delta\",[6,\"aspectCenteredDelta\",[\"uv\",\"center\",\"viewport\"]]],[13,\"skewRad\",[1,\"skewAngle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosS\",[6,\"std.cos\",[\"skewRad\"]]],[13,\"sinS\",[6,\"std.sin\",[\"skewRad\"]]],[13,\"rotated\",[6,\"geom.rotate2\",[\"delta\",\"cosS\",[4,\"-\",\"sinS\"]]]],[13,\"stretched\",[6,\"d.vec2f\",[[1,[7,\"rotated\",\"x\"],\"*\",\"ellipseAspect\"],[7,\"rotated\",\"y\"]]]],[13,\"dist\",[6,\"std.length\",[\"stretched\"]]],[13,\"safeRadius\",[6,\"std.max\",[\"radius\",[5,\"0.000001\"]]]],[10,[1,\"dist\",\"/\",\"safeRadius\"]]]]},externals:{aspectCenteredDelta:()=>cc,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"geom.rotate2\":()=>Pn,\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.max\":()=>B}})&&e.f)({})),Ste=w.fn([m,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=cc(r,t,o),i=Xt(a.y,a.x),l=__tsover_sub(.5,__tsover_div(i,Jt)),c=__tsover_mul(s,uS);return __tsover_add(l,c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"delta\",[6,\"aspectCenteredDelta\",[\"uv\",\"center\",\"viewport\"]]],[13,\"angle\",[6,\"std.atan2\",[[7,\"delta\",\"y\"],[7,\"delta\",\"x\"]]]],[13,\"normalized\",[1,[5,\"0.5\"],\"-\",[1,\"angle\",\"/\",\"TAU\"]]],[13,\"rotationFraction\",[1,\"rotation\",\"*\",\"INV_360\"]],[10,[1,\"normalized\",\"+\",\"rotationFraction\"]]]]},externals:{aspectCenteredDelta:()=>cc,\"std.atan2\":()=>Xt,TAU:()=>Jt,INV_360:()=>uS}})&&e.f)({})),Pte=w.fn([m,n,n,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=cc(a,t,i),c=__tsover_mul(r,bt),d=Pn(l,pe(c),-ae(c)),u=re(d.x),p=re(d.y),_=K(__tsover_add(u,p),B(u,p),o);return __tsover_div(_,s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"size\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"roundness\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"delta\",[6,\"aspectCenteredDelta\",[\"uv\",\"center\",\"viewport\"]]],[13,\"rotRad\",[1,\"rotation\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rotated\",[6,\"geom.rotate2\",[\"delta\",\"cosR\",[4,\"-\",\"sinR\"]]]],[13,\"absDx\",[6,\"std.abs\",[[7,\"rotated\",\"x\"]]]],[13,\"absDy\",[6,\"std.abs\",[[7,\"rotated\",\"y\"]]]],[13,\"l1\",[1,\"absDx\",\"+\",\"absDy\"]],[13,\"linf\",[6,\"std.max\",[\"absDx\",\"absDy\"]]],[13,\"dist\",[6,\"std.mix\",[\"l1\",\"linf\",\"roundness\"]]],[10,[1,\"dist\",\"/\",\"size\"]]]]},externals:{aspectCenteredDelta:()=>cc,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"geom.rotate2\":()=>Pn,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.mix\":()=>K}})&&e.f)({})),Cte=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(t,s);return N(G(r,0,1),Re(r),s>1.0001)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"repeat\"}],body:[0,[[13,\"s\",[1,\"t\",\"*\",\"repeat\"]],[10,[6,\"std.select\",[[6,\"std.clamp\",[\"s\",[5,\"0\"],[5,\"1\"]]],[6,\"std.fract\",[\"s\"]],[1,\"repeat\",\">\",[5,\"1.0001\"]]]]]]]},externals:{\"std.select\":()=>N,\"std.clamp\":()=>G,\"std.fract\":()=>Re}})&&e.f)({})),Mte=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Re(__tsover_mul(t,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"repeat\"}],body:[0,[[10,[6,\"std.fract\",[[1,\"t\",\"*\",\"repeat\"]]]]]]},externals:{\"std.fract\":()=>Re}})&&e.f)({})),Ete=w.fn([m,m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,bt),l=Cs(s),c=__tsover_add(av(dc(t,l),pe(i),ae(i)),__tsover_mul(l,.5));return Re(__tsover_add(__tsover_mul(__tsover_mul(c,o),.2),a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"projectedCoord\",[1,[6,\"directionalProjection\",[[6,\"canvasCentredDelta\",[\"uv\",\"aspect\"]],[6,\"std.cos\",[\"angleRad\"]],[6,\"std.sin\",[\"angleRad\"]]]],\"+\",[1,\"aspect\",\"*\",[5,\"0.5\"]]]],[10,[6,\"std.fract\",[[1,[1,[1,\"projectedCoord\",\"*\",\"scale\"],\"*\",[5,\"0.2\"]],\"+\",\"animTime\"]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"geom.aspectOf\":()=>Cs,directionalProjection:()=>av,canvasCentredDelta:()=>dc,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.fract\":()=>Re}})&&e.f)({})),kte=w.fn([n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=__tsover_mul(t,6);return S(G(__tsover_sub(re(__tsover_sub(s,3)),1),0,1),G(__tsover_sub(2,re(__tsover_sub(s,2))),0,1),G(__tsover_sub(2,re(__tsover_sub(s,4))),0,1),1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[13,\"h6\",[1,\"t\",\"*\",[5,\"6\"]]],[13,\"r\",[6,\"std.clamp\",[[1,[6,\"std.abs\",[[1,\"h6\",\"-\",[5,\"3\"]]]],\"-\",[5,\"1\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"g\",[6,\"std.clamp\",[[1,[5,\"2\"],\"-\",[6,\"std.abs\",[[1,\"h6\",\"-\",[5,\"2\"]]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"b\",[6,\"std.clamp\",[[1,[5,\"2\"],\"-\",[6,\"std.abs\",[[1,\"h6\",\"-\",[5,\"4\"]]]]],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[\"r\",\"g\",\"b\",[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G,\"std.abs\":()=>re,\"d.vec4f\":()=>S}})&&e.f)({})),Bte=w.fn([n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=__tsover_mul(t,3);return m(Re(s),ie(s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[13,\"t3\",[1,\"t\",\"*\",[5,\"3\"]]],[10,[6,\"d.vec2f\",[[6,\"std.fract\",[\"t3\"]],[6,\"std.floor\",[\"t3\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.floor\":()=>ie}})&&e.f)({})),zte=w.fn([S,S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return K(K(r,s,Et(o,1.5)),t,Et(o,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"c01\"},{type:\"i\",name:\"c12\"},{type:\"i\",name:\"c20\"},{type:\"i\",name:\"floorT3\"}],body:[0,[[13,\"seg1\",[6,\"std.mix\",[\"c20\",\"c12\",[6,\"std.step\",[\"floorT3\",[5,\"1.5\"]]]]]],[10,[6,\"std.mix\",[\"seg1\",\"c01\",[6,\"std.step\",[\"floorT3\",[5,\"0.5\"]]]]]]]]},externals:{\"std.mix\":()=>K,\"std.step\":()=>Et}})&&e.f)({})),mS=w.fn([m,n,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_div(a.x,a.y),l=__tsover_sub(__tsover_mul(o.x,i),__tsover_mul(t.x,i)),c=__tsover_sub(o.y,__tsover_sub(1,t.y)),d=he(m(l,c)),u=__tsover_sub(Xt(c,l),r);return __tsover_add(__tsover_mul(d,s),__tsover_div(u,Po))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,[7,\"center\",\"x\"],\"*\",\"aspect\"]]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]],[13,\"l\",[6,\"std.length\",[[6,\"d.vec2f\",[\"dx\",\"dy\"]]]]],[13,\"angle\",[1,[6,\"std.atan2\",[\"dy\",\"dx\"]],\"-\",\"animTime\"]],[10,[1,[1,\"l\",\"*\",\"scale\"],\"+\",[1,\"angle\",\"/\",\"TWO_PI\"]]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.atan2\":()=>Xt,TWO_PI:()=>Po}})&&e.f)({})),Rte=w.fn([m,n,n,n,n,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_div(c.x,c.y),u=he(m(__tsover_sub(__tsover_mul(l.x,d),__tsover_mul(t.x,d)),__tsover_sub(l.y,__tsover_sub(1,t.y)))),p=mS(t,s,i,l,c),_=Re(p),f=__tsover_mul(re(__tsover_sub(_,.5)),2),h=G(r,__tsover_mul(o,.005),1),g=__tsover_sub(1,__tsover_mul(G(o,0,1),u)),y=__tsover_mul(h,g),b=mr(p),x=__tsover_sub(4,__tsover_mul(__tsover_mul(be(.05,.4,__tsover_mul(r,2)),be(.05,.4,__tsover_mul(__tsover_sub(1,r),2))),3)),P=K(__tsover_mul(x,b),mr(f),G(b,0,1));return be(__tsover_sub(__tsover_sub(y,P),a),__tsover_add(__tsover_add(y,P),a),f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"strokeWidth\"},{type:\"i\",name:\"strokeFalloff\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,[7,\"center\",\"x\"],\"*\",\"aspect\"]]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]],[13,\"l\",[6,\"std.length\",[[6,\"d.vec2f\",[\"dx\",\"dy\"]]]]],[13,\"offset\",[6,\"spiralOffset\",[\"center\",\"scale\",\"animTime\",\"uv\",\"viewport\"]]],[13,\"stripe\",[6,\"std.fract\",[\"offset\"]]],[13,\"shape\",[1,[6,\"std.abs\",[[1,\"stripe\",\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"baseWidth\",[6,\"std.clamp\",[\"strokeWidth\",[1,\"strokeFalloff\",\"*\",[5,\"0.005\"]],[5,\"1\"]]]],[13,\"falloffAmount\",[1,[5,\"1\"],\"-\",[1,[6,\"std.clamp\",[\"strokeFalloff\",[5,\"0\"],[5,\"1\"]]],\"*\",\"l\"]]],[13,\"width\",[1,\"baseWidth\",\"*\",\"falloffAmount\"]],[13,\"fw\",[6,\"std.fwidth\",[\"offset\"]]],[13,\"fwMult\",[1,[5,\"4\"],\"-\",[1,[1,[6,\"std.smoothstep\",[[5,\"0.05\"],[5,\"0.4\"],[1,\"strokeWidth\",\"*\",[5,\"2\"]]]],\"*\",[6,\"std.smoothstep\",[[5,\"0.05\"],[5,\"0.4\"],[1,[1,[5,\"1\"],\"-\",\"strokeWidth\"],\"*\",[5,\"2\"]]]]],\"*\",[5,\"3\"]]]],[13,\"pixelSize\",[6,\"std.mix\",[[1,\"fwMult\",\"*\",\"fw\"],[6,\"std.fwidth\",[\"shape\"]],[6,\"std.clamp\",[\"fw\",[5,\"0\"],[5,\"1\"]]]]]],[10,[6,\"std.smoothstep\",[[1,[1,\"width\",\"-\",\"pixelSize\"],\"-\",\"softness\"],[1,[1,\"width\",\"+\",\"pixelSize\"],\"+\",\"softness\"],\"shape\"]]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,spiralOffset:()=>mS,\"std.fract\":()=>Re,\"std.abs\":()=>re,\"std.clamp\":()=>G,\"std.fwidth\":()=>mr,\"std.smoothstep\":()=>be,\"std.mix\":()=>K}})&&e.f)({})),iu=w.fn([m,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(t.x,s.x),a=__tsover_sub(t.y,s.y),i=__tsover_add(Ue(__tsover_add(__tsover_mul(o,o),__tsover_mul(a,a))),.001);return __tsover_div(1,vt(i,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvAspect\"},{type:\"i\",name:\"pos\"},{type:\"i\",name:\"effPower\"}],body:[0,[[13,\"dx\",[1,[7,\"uvAspect\",\"x\"],\"-\",[7,\"pos\",\"x\"]]],[13,\"dy\",[1,[7,\"uvAspect\",\"y\"],\"-\",[7,\"pos\",\"y\"]]],[13,\"dist\",[1,[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]],\"+\",[5,\"0.001\"]]],[10,[1,[5,\"1\"],\"/\",[6,\"std.pow\",[\"dist\",\"effPower\"]]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.pow\":()=>vt}})&&e.f)({})),Dte=w.fn([m,m,m,m,m,m,m,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=Cs(s),u=m(__tsover_mul(t.x,d),t.y),p=Zi(r,d),_=Zi(o,d),f=Zi(a,d),h=Zi(i,d),g=Zi(l,d),y=ye(__tsover_div(8,__tsover_add(c,.5)),8),b=iu(u,p,y),x=iu(u,_,y),P=iu(u,f,y),C=iu(u,h,y),E=iu(u,g,y),R=b,U=__tsover_div(x,__tsover_add(R,x)),L=__tsover_add(R,x),k=__tsover_div(P,__tsover_add(L,P)),D=__tsover_add(L,P),O=__tsover_div(C,__tsover_add(D,C)),F=__tsover_add(D,C);return S(U,k,O,__tsover_div(E,__tsover_add(F,E)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"vp\"},{type:\"i\",name:\"posA\"},{type:\"i\",name:\"posB\"},{type:\"i\",name:\"posC\"},{type:\"i\",name:\"posD\"},{type:\"i\",name:\"posE\"},{type:\"i\",name:\"smoothness\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"vp\"]]],[13,\"uvAspect\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"pA\",[6,\"aspectCentrePosition\",[\"posA\",\"aspect\"]]],[13,\"pB\",[6,\"aspectCentrePosition\",[\"posB\",\"aspect\"]]],[13,\"pC\",[6,\"aspectCentrePosition\",[\"posC\",\"aspect\"]]],[13,\"pD\",[6,\"aspectCentrePosition\",[\"posD\",\"aspect\"]]],[13,\"pE\",[6,\"aspectCentrePosition\",[\"posE\",\"aspect\"]]],[13,\"effPower\",[6,\"std.min\",[[1,[5,\"8\"],\"/\",[1,\"smoothness\",\"+\",[5,\"0.5\"]]],[5,\"8\"]]]],[13,\"wA\",[6,\"mpgWeight\",[\"uvAspect\",\"pA\",\"effPower\"]]],[13,\"wB\",[6,\"mpgWeight\",[\"uvAspect\",\"pB\",\"effPower\"]]],[13,\"wC\",[6,\"mpgWeight\",[\"uvAspect\",\"pC\",\"effPower\"]]],[13,\"wD\",[6,\"mpgWeight\",[\"uvAspect\",\"pD\",\"effPower\"]]],[13,\"wE\",[6,\"mpgWeight\",[\"uvAspect\",\"pE\",\"effPower\"]]],[13,\"accW1\",\"wA\"],[13,\"t1\",[1,\"wB\",\"/\",[1,\"accW1\",\"+\",\"wB\"]]],[13,\"accW2\",[1,\"accW1\",\"+\",\"wB\"]],[13,\"t2\",[1,\"wC\",\"/\",[1,\"accW2\",\"+\",\"wC\"]]],[13,\"accW3\",[1,\"accW2\",\"+\",\"wC\"]],[13,\"t3\",[1,\"wD\",\"/\",[1,\"accW3\",\"+\",\"wD\"]]],[13,\"accW4\",[1,\"accW3\",\"+\",\"wD\"]],[13,\"t4\",[1,\"wE\",\"/\",[1,\"accW4\",\"+\",\"wE\"]]],[10,[6,\"d.vec4f\",[\"t1\",\"t2\",\"t3\",\"t4\"]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec2f\":()=>m,aspectCentrePosition:()=>Zi,\"std.min\":()=>ye,mpgWeight:()=>iu,\"d.vec4f\":()=>S}})&&e.f)({})),Lte=w.fn([m,m,m,m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=Cs(s),u=m(r.x,__tsover_sub(1,r.y)),p=m(o.x,__tsover_sub(1,o.y)),_=m(__tsover_mul(u.x,d),u.y),f=m(__tsover_mul(p.x,d),p.y),h=zT(m(__tsover_mul(t.x,d),t.y),_,f),g=RT(h.y,h.x,__tsover_mul(a,.25),__tsover_mul(i,.25),l,c),y=g.x,b=g.y;return m(y,b)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"vp\"},{type:\"i\",name:\"start\"},{type:\"i\",name:\"end\"},{type:\"i\",name:\"startThickness\"},{type:\"i\",name:\"endThickness\"},{type:\"i\",name:\"startSoftness\"},{type:\"i\",name:\"endSoftness\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"vp\"]]],[13,\"startPos\",[6,\"d.vec2f\",[[7,\"start\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"start\",\"y\"]]]]],[13,\"endPos\",[6,\"d.vec2f\",[[7,\"end\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"end\",\"y\"]]]]],[13,\"acStart\",[6,\"d.vec2f\",[[1,[7,\"startPos\",\"x\"],\"*\",\"aspect\"],[7,\"startPos\",\"y\"]]]],[13,\"acEnd\",[6,\"d.vec2f\",[[1,[7,\"endPos\",\"x\"],\"*\",\"aspect\"],[7,\"endPos\",\"y\"]]]],[13,\"acUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"proj\",[6,\"lightfields.pointToSegment\",[\"acUV\",\"acStart\",\"acEnd\"]]],[13,\"field\",[6,\"lightfields.taperedSegmentGlow\",[[7,\"proj\",\"y\"],[7,\"proj\",\"x\"],[1,\"startThickness\",\"*\",[5,\"0.25\"]],[1,\"endThickness\",\"*\",[5,\"0.25\"]],\"startSoftness\",\"endSoftness\"]]],[13,\"colorT\",[7,\"field\",\"x\"]],[13,\"alpha\",[7,\"field\",\"y\"]],[10,[6,\"d.vec2f\",[\"colorT\",\"alpha\"]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec2f\":()=>m,\"lightfields.pointToSegment\":()=>zT,\"lightfields.taperedSegmentGlow\":()=>RT}})&&e.f)({})),Ute=Dk({domain:\"unit\",contrastMode:\"multiplicative\",invert:!0,glowGamma:!0,balance:\"percentCentred\"}),Ote=w.fn([m,m,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s);return A(__tsover_mul(t.x,o),t.y,0).mul(Tt(__tsover_sub(r,1)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"scale\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"base\",[6,\"d.vec3f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"],[5,\"0\"]]]],[10,[6,[7,\"base\",\"mul\"],[[6,\"std.exp\",[[1,\"scale\",\"-\",[5,\"1\"]]]]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec3f\":()=>A,\"std.exp\":()=>Tt}})&&e.f)({})),Gte=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(__tsover_mul(Na(t),.5),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"}],body:[0,[[10,[1,[1,[6,\"noise.mxNoiseFloat3\",[\"pos\"]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]},externals:{\"noise.mxNoiseFloat3\":()=>Na}})&&e.f)({})),Fte=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=r,a=s,i=m(__tsover_add(t.x,__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(t.y,__tsover_mul(a,1.7)),__tsover_mul(o,.8))),.12),__tsover_mul(pe(__tsover_sub(__tsover_mul(t.x,__tsover_mul(a,.9)),__tsover_mul(o,.5))),.05))),__tsover_add(t.y,__tsover_add(__tsover_mul(pe(__tsover_sub(__tsover_mul(t.x,__tsover_mul(a,1.3)),__tsover_mul(o,.6))),.12),__tsover_mul(ae(__tsover_add(__tsover_mul(t.y,__tsover_mul(a,1.1)),__tsover_mul(o,.7))),.05)))),l=ae(__tsover_add(__tsover_add(__tsover_mul(i.x,__tsover_mul(a,2.1)),__tsover_mul(i.y,__tsover_mul(a,1.8))),__tsover_mul(o,.4))),c=__tsover_mul(s,2.1),d=m(__tsover_add(i.x,__tsover_add(__tsover_mul(pe(__tsover_sub(__tsover_mul(i.y,__tsover_mul(c,2.7)),__tsover_mul(o,.45))),.07),__tsover_mul(ae(__tsover_add(__tsover_mul(i.x,__tsover_mul(c,1.9)),__tsover_mul(o,.6))),.04))),__tsover_add(i.y,__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(i.x,__tsover_mul(c,2.3)),__tsover_mul(o,.65))),.07),__tsover_mul(pe(__tsover_sub(__tsover_mul(i.y,__tsover_mul(c,1.6)),__tsover_mul(o,.4))),.04)))),u=pe(__tsover_add(__tsover_sub(__tsover_mul(d.x,__tsover_mul(c,1.4)),__tsover_mul(d.y,__tsover_mul(c,1.9))),__tsover_mul(o,.35))),p=__tsover_mul(s,3.7),_=m(__tsover_add(d.x,__tsover_add(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(d.y,__tsover_mul(p,1.8)),__tsover_mul(o,.85))),.04),__tsover_mul(pe(__tsover_sub(__tsover_mul(d.x,__tsover_mul(p,1.3)),__tsover_mul(o,.55))),.025)),__tsover_mul(ae(__tsover_add(__tsover_mul(__tsover_add(d.x,d.y),__tsover_mul(p,.7)),__tsover_mul(o,.9))),.02))),__tsover_add(d.y,__tsover_add(__tsover_add(__tsover_mul(pe(__tsover_sub(__tsover_mul(d.x,__tsover_mul(p,1.6)),__tsover_mul(o,.75))),.04),__tsover_mul(ae(__tsover_add(__tsover_mul(d.y,__tsover_mul(p,1.1)),__tsover_mul(o,.5))),.025)),__tsover_mul(pe(__tsover_sub(__tsover_mul(__tsover_add(d.x,d.y),__tsover_mul(p,.8)),__tsover_mul(o,.95))),.02)))),f=ae(__tsover_sub(__tsover_add(__tsover_mul(_.x,__tsover_mul(p,1.1)),__tsover_mul(_.y,__tsover_mul(p,1.5))),__tsover_mul(o,.55)));return __tsover_add(__tsover_add(__tsover_mul(l,.45),__tsover_mul(u,.35)),__tsover_mul(f,.2))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"detail\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"t\",\"animTime\"],[13,\"freq1\",\"detail\"],[13,\"d1\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"uv\",\"y\"],\"*\",[1,\"freq1\",\"*\",[5,\"1.7\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.8\"]]]]],\"*\",[5,\"0.12\"]],\"+\",[1,[6,\"std.cos\",[[1,[1,[7,\"uv\",\"x\"],\"*\",[1,\"freq1\",\"*\",[5,\"0.9\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.5\"]]]]],\"*\",[5,\"0.05\"]]]],[1,[7,\"uv\",\"y\"],\"+\",[1,[1,[6,\"std.cos\",[[1,[1,[7,\"uv\",\"x\"],\"*\",[1,\"freq1\",\"*\",[5,\"1.3\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.6\"]]]]],\"*\",[5,\"0.12\"]],\"+\",[1,[6,\"std.sin\",[[1,[1,[7,\"uv\",\"y\"],\"*\",[1,\"freq1\",\"*\",[5,\"1.1\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.7\"]]]]],\"*\",[5,\"0.05\"]]]]]]],[13,\"pattern1\",[6,\"std.sin\",[[1,[1,[1,[7,\"d1\",\"x\"],\"*\",[1,\"freq1\",\"*\",[5,\"2.1\"]]],\"+\",[1,[7,\"d1\",\"y\"],\"*\",[1,\"freq1\",\"*\",[5,\"1.8\"]]]],\"+\",[1,\"t\",\"*\",[5,\"0.4\"]]]]]],[13,\"freq2\",[1,\"detail\",\"*\",[5,\"2.1\"]]],[13,\"d2\",[6,\"d.vec2f\",[[1,[7,\"d1\",\"x\"],\"+\",[1,[1,[6,\"std.cos\",[[1,[1,[7,\"d1\",\"y\"],\"*\",[1,\"freq2\",\"*\",[5,\"2.7\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.45\"]]]]],\"*\",[5,\"0.07\"]],\"+\",[1,[6,\"std.sin\",[[1,[1,[7,\"d1\",\"x\"],\"*\",[1,\"freq2\",\"*\",[5,\"1.9\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.6\"]]]]],\"*\",[5,\"0.04\"]]]],[1,[7,\"d1\",\"y\"],\"+\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"d1\",\"x\"],\"*\",[1,\"freq2\",\"*\",[5,\"2.3\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.65\"]]]]],\"*\",[5,\"0.07\"]],\"+\",[1,[6,\"std.cos\",[[1,[1,[7,\"d1\",\"y\"],\"*\",[1,\"freq2\",\"*\",[5,\"1.6\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.4\"]]]]],\"*\",[5,\"0.04\"]]]]]]],[13,\"pattern2\",[6,\"std.cos\",[[1,[1,[1,[7,\"d2\",\"x\"],\"*\",[1,\"freq2\",\"*\",[5,\"1.4\"]]],\"-\",[1,[7,\"d2\",\"y\"],\"*\",[1,\"freq2\",\"*\",[5,\"1.9\"]]]],\"+\",[1,\"t\",\"*\",[5,\"0.35\"]]]]]],[13,\"freq3\",[1,\"detail\",\"*\",[5,\"3.7\"]]],[13,\"d3\",[6,\"d.vec2f\",[[1,[7,\"d2\",\"x\"],\"+\",[1,[1,[1,[6,\"std.sin\",[[1,[1,[7,\"d2\",\"y\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.8\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.85\"]]]]],\"*\",[5,\"0.04\"]],\"+\",[1,[6,\"std.cos\",[[1,[1,[7,\"d2\",\"x\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.3\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.55\"]]]]],\"*\",[5,\"0.025\"]]],\"+\",[1,[6,\"std.sin\",[[1,[1,[1,[7,\"d2\",\"x\"],\"+\",[7,\"d2\",\"y\"]],\"*\",[1,\"freq3\",\"*\",[5,\"0.7\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.9\"]]]]],\"*\",[5,\"0.02\"]]]],[1,[7,\"d2\",\"y\"],\"+\",[1,[1,[1,[6,\"std.cos\",[[1,[1,[7,\"d2\",\"x\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.6\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.75\"]]]]],\"*\",[5,\"0.04\"]],\"+\",[1,[6,\"std.sin\",[[1,[1,[7,\"d2\",\"y\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.1\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.5\"]]]]],\"*\",[5,\"0.025\"]]],\"+\",[1,[6,\"std.cos\",[[1,[1,[1,[7,\"d2\",\"x\"],\"+\",[7,\"d2\",\"y\"]],\"*\",[1,\"freq3\",\"*\",[5,\"0.8\"]]],\"-\",[1,\"t\",\"*\",[5,\"0.95\"]]]]],\"*\",[5,\"0.02\"]]]]]]],[13,\"pattern3\",[6,\"std.sin\",[[1,[1,[1,[7,\"d3\",\"x\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.1\"]]],\"+\",[1,[7,\"d3\",\"y\"],\"*\",[1,\"freq3\",\"*\",[5,\"1.5\"]]]],\"-\",[1,\"t\",\"*\",[5,\"0.55\"]]]]]],[10,[1,[1,[1,\"pattern1\",\"*\",[5,\"0.45\"]],\"+\",[1,\"pattern2\",\"*\",[5,\"0.35\"]]],\"+\",[1,\"pattern3\",\"*\",[5,\"0.2\"]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.sin\":()=>ae,\"std.cos\":()=>pe}})&&e.f)({})),Ite=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return be(r,o,__tsover_add(__tsover_add(__tsover_mul(t,.5),.5),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pattern\"},{type:\"i\",name:\"bias\"},{type:\"i\",name:\"low\"},{type:\"i\",name:\"high\"}],body:[0,[[13,\"normalized\",[1,[1,[1,\"pattern\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],\"+\",\"bias\"]],[10,[6,\"std.smoothstep\",[\"low\",\"high\",\"normalized\"]]]]]},externals:{\"std.smoothstep\":()=>be}})&&e.f)({})),Yte=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return __tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(s,r),__tsover_mul(t,o))),a),1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pattern\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"speed\"},{type:\"i\",name:\"span\"},{type:\"i\",name:\"depth\"}],body:[0,[[10,[1,[1,[6,\"std.sin\",[[1,[1,\"animTime\",\"*\",\"speed\"],\"+\",[1,\"pattern\",\"*\",\"span\"]]]],\"*\",\"depth\"],\"+\",[5,\"1\"]]]]]},externals:{\"std.sin\":()=>ae}})&&e.f)({})),cm=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Ys(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"noise.hash12\",[\"p\"]]]]]},externals:{\"noise.hash12\":()=>Ys}})&&e.f)({})),x0=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=Re(t),o=cm(s),a=cm(m(__tsover_add(s.x,1),s.y)),i=cm(m(s.x,__tsover_add(s.y,1))),l=cm(m(__tsover_add(s.x,1),__tsover_add(s.y,1))),c=r.mul(r).mul(r.mul(-2).add(3));return K(K(o,a,c.x),K(i,l,c.x),c.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"st\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"st\"]]],[13,\"f\",[6,\"std.fract\",[\"st\"]]],[13,\"a\",[6,\"godraysHash\",[\"i\"]]],[13,\"b\",[6,\"godraysHash\",[[6,\"d.vec2f\",[[1,[7,\"i\",\"x\"],\"+\",[5,\"1\"]],[7,\"i\",\"y\"]]]]]],[13,\"c\",[6,\"godraysHash\",[[6,\"d.vec2f\",[[7,\"i\",\"x\"],[1,[7,\"i\",\"y\"],\"+\",[5,\"1\"]]]]]]],[13,\"e\",[6,\"godraysHash\",[[6,\"d.vec2f\",[[1,[7,\"i\",\"x\"],\"+\",[5,\"1\"]],[1,[7,\"i\",\"y\"],\"+\",[5,\"1\"]]]]]]],[13,\"u\",[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[4,\"-\",[5,\"2\"]]]],\"add\"],[[5,\"3\"]]]]]],[13,\"x1\",[6,\"std.mix\",[\"a\",\"b\",[7,\"u\",\"x\"]]]],[13,\"x2\",[6,\"std.mix\",[\"c\",\"e\",[7,\"u\",\"x\"]]]],[10,[6,\"std.mix\",[\"x1\",\"x2\",[7,\"u\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,godraysHash:()=>cm,\"d.vec2f\":()=>m,\"std.mix\":()=>K}})&&e.f)({})),A0=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=vt(x0(m(__tsover_mul(t,a),o)),i);return K(vt(x0(m(__tsover_mul(s,a),o)),i),l,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"a\"},{type:\"i\",name:\"aWrapped\"},{type:\"i\",name:\"blend\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"freq\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"nLeft\",[6,\"std.pow\",[[6,\"godraysValueNoise\",[[6,\"d.vec2f\",[[1,\"a\",\"*\",\"freq\"],\"r\"]]]],\"intensity\"]]],[13,\"nRight\",[6,\"std.pow\",[[6,\"godraysValueNoise\",[[6,\"d.vec2f\",[[1,\"aWrapped\",\"*\",\"freq\"],\"r\"]]]],\"intensity\"]]],[10,[6,\"std.mix\",[\"nRight\",\"nLeft\",\"blend\"]]]]]},externals:{\"std.pow\":()=>vt,godraysValueNoise:()=>x0,\"d.vec2f\":()=>m,\"std.mix\":()=>K}})&&e.f)({})),Wte=w.fn([m,m,m],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s),a=m(r.x,__tsover_sub(1,r.y)),i=t.sub(a),l=m(__tsover_mul(i.x,o),i.y),c=BT(l);return S(c.x,c.y,c.z,he(l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"center\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"delta\",[6,[7,\"uv\",\"sub\"],[\"centerPos\"]]],[13,\"shapeUV\",[6,\"d.vec2f\",[[1,[7,\"delta\",\"x\"],\"*\",\"aspect\"],[7,\"delta\",\"y\"]]]],[13,\"ang\",[6,\"lightfields.seamlessAngularField\",[\"shapeUV\"]]],[10,[6,\"d.vec4f\",[[7,\"ang\",\"x\"],[7,\"ang\",\"y\"],[7,\"ang\",\"z\"],[6,\"std.length\",[\"shapeUV\"]]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec2f\":()=>m,\"lightfields.seamlessAngularField\":()=>BT,\"d.vec4f\":()=>S,\"std.length\":()=>he}})&&e.f)({})),w0=w.fn([S,n,n,n,n,S,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_sub(__tsover_mul(t.w,i.x),__tsover_mul(s,i.y)),d=__tsover_sub(__tsover_mul(__tsover_mul(t.w,i.z),__tsover_add(1,r)),__tsover_mul(s,i.w)),u=__tsover_mul(a,l.x),p=A0(t.x,t.y,t.z,c,u,o),_=A0(t.x,t.y,t.z,d,__tsover_mul(u,l.y),o);return __tsover_mul(p,_)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"frame\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"spots\"},{type:\"i\",name:\"intensityCalc\"},{type:\"i\",name:\"dens\"},{type:\"i\",name:\"sched\"},{type:\"i\",name:\"freqs\"}],body:[0,[[13,\"rA\",[1,[1,[7,\"frame\",\"w\"],\"*\",[7,\"sched\",\"x\"]],\"-\",[1,\"animTime\",\"*\",[7,\"sched\",\"y\"]]]],[13,\"rB\",[1,[1,[1,[7,\"frame\",\"w\"],\"*\",[7,\"sched\",\"z\"]],\"*\",[1,[5,\"1\"],\"+\",\"spots\"]],\"-\",[1,\"animTime\",\"*\",[7,\"sched\",\"w\"]]]],[13,\"f\",[1,\"dens\",\"*\",[7,\"freqs\",\"x\"]]],[13,\"shapeA\",[6,\"godraysRaysShape\",[[7,\"frame\",\"x\"],[7,\"frame\",\"y\"],[7,\"frame\",\"z\"],\"rA\",\"f\",\"intensityCalc\"]]],[13,\"shapeB\",[6,\"godraysRaysShape\",[[7,\"frame\",\"x\"],[7,\"frame\",\"y\"],[7,\"frame\",\"z\"],\"rB\",[1,\"f\",\"*\",[7,\"freqs\",\"y\"]],\"intensityCalc\"]]],[10,[1,\"shapeA\",\"*\",\"shapeB\"]]]]},externals:{godraysRaysShape:()=>A0}})&&e.f)({})),qte=w.fn([S,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(s,.2),l=__tsover_mul(6.5,re(a)),c=__tsover_sub(4,__tsover_mul(3,G(o,0,1))),d=__tsover_mul(6,r),u=w0(t,i,l,c,d,S(1,3,.5,2),m(5,4)),p=w0(t,i,l,c,d,S(1.4,2.5,.7,1.8),m(4.5,3.5));return G(__tsover_add(u,__tsover_mul(p,.7)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"frame\"},{type:\"i\",name:\"animTimeRaw\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"spotty\"}],body:[0,[[13,\"animTime\",[1,\"animTimeRaw\",\"*\",[5,\"0.2\"]]],[13,\"spots\",[1,[5,\"6.5\"],\"*\",[6,\"std.abs\",[\"spotty\"]]]],[13,\"intensityCalc\",[1,[5,\"4\"],\"-\",[1,[5,\"3\"],\"*\",[6,\"std.clamp\",[\"intensity\",[5,\"0\"],[5,\"1\"]]]]]],[13,\"dens\",[1,[5,\"6\"],\"*\",\"density\"]],[13,\"layer1\",[6,\"godraysRayLayer\",[\"frame\",\"animTime\",\"spots\",\"intensityCalc\",\"dens\",[6,\"d.vec4f\",[[5,\"1\"],[5,\"3\"],[5,\"0.5\"],[5,\"2\"]]],[6,\"d.vec2f\",[[5,\"5\"],[5,\"4\"]]]]]],[13,\"layer2\",[6,\"godraysRayLayer\",[\"frame\",\"animTime\",\"spots\",\"intensityCalc\",\"dens\",[6,\"d.vec4f\",[[5,\"1.4\"],[5,\"2.5\"],[5,\"0.7\"],[5,\"1.8\"]]],[6,\"d.vec2f\",[[5,\"4.5\"],[5,\"3.5\"]]]]]],[10,[6,\"std.clamp\",[[1,\"layer1\",\"+\",[1,\"layer2\",\"*\",[5,\"0.7\"]]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.abs\":()=>re,\"std.clamp\":()=>G,godraysRayLayer:()=>w0,\"d.vec4f\":()=>S,\"d.vec2f\":()=>m}})&&e.f)({})),Vte=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(r,t.w),a=__tsover_add(o,__tsover_mul(s.w,__tsover_sub(1,o))),i=G(a,.001,1),l=t.xyz.mul(o),c=s.xyz.mul(s.w).mul(__tsover_sub(1,o));return S(l.add(c).div(i),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rayColor\"},{type:\"i\",name:\"bgColor\"},{type:\"i\",name:\"coverage\"}],body:[0,[[13,\"rayA\",[1,\"coverage\",\"*\",[7,\"rayColor\",\"w\"]]],[13,\"finalAlpha\",[1,\"rayA\",\"+\",[1,[7,\"bgColor\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"rayA\"]]]],[13,\"safeAlpha\",[6,\"std.clamp\",[\"finalAlpha\",[5,\"0.001\"],[5,\"1\"]]]],[13,\"rayContribution\",[6,[7,[7,\"rayColor\",\"xyz\"],\"mul\"],[\"rayA\"]]],[13,\"bgContribution\",[6,[7,[6,[7,[7,\"bgColor\",\"xyz\"],\"mul\"],[[7,\"bgColor\",\"w\"]]],\"mul\"],[[1,[5,\"1\"],\"-\",\"rayA\"]]]],[13,\"finalColor\",[6,[7,[6,[7,\"rayContribution\",\"add\"],[\"bgContribution\"]],\"div\"],[\"safeAlpha\"]]],[10,[6,\"d.vec4f\",[\"finalColor\",\"finalAlpha\"]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({})),Nte=w.fn([n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Aa(m(__tsover_mul(t,.9),__tsover_add(__tsover_mul(s,.53),11.7))),a=Aa(m(__tsover_add(__tsover_mul(t,2.7),41.3),__tsover_add(__tsover_mul(s,.29),5.1))),i=__tsover_add(__tsover_mul(o,.65),__tsover_mul(a,.35));return S(K(1,__tsover_add(.25,__tsover_mul(1.5,i)),r),Aa(m(__tsover_add(__tsover_mul(t,.13),3.1),__tsover_add(__tsover_mul(s,.41),27))),Aa(m(__tsover_add(__tsover_mul(t,.09),17.7),__tsover_add(__tsover_mul(s,.61),8.4))),Aa(m(__tsover_add(__tsover_mul(t,.17),51.2),__tsover_add(__tsover_mul(s,.23),2.9))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"flicker\"}],body:[0,[[13,\"nA\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,\"t\",\"*\",[5,\"0.9\"]],[1,[1,\"seed\",\"*\",[5,\"0.53\"]],\"+\",[5,\"11.7\"]]]]]]],[13,\"nB\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,\"t\",\"*\",[5,\"2.7\"]],\"+\",[5,\"41.3\"]],[1,[1,\"seed\",\"*\",[5,\"0.29\"]],\"+\",[5,\"5.1\"]]]]]]],[13,\"breathe\",[1,[1,\"nA\",\"*\",[5,\"0.65\"]],\"+\",[1,\"nB\",\"*\",[5,\"0.35\"]]]],[13,\"flickerMod\",[6,\"std.mix\",[[5,\"1\"],[1,[5,\"0.25\"],\"+\",[1,[5,\"1.5\"],\"*\",\"breathe\"]],\"flicker\"]]],[13,\"evo1\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,\"t\",\"*\",[5,\"0.13\"]],\"+\",[5,\"3.1\"]],[1,[1,\"seed\",\"*\",[5,\"0.41\"]],\"+\",[5,\"27\"]]]]]]],[13,\"evo2\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,\"t\",\"*\",[5,\"0.09\"]],\"+\",[5,\"17.7\"]],[1,[1,\"seed\",\"*\",[5,\"0.61\"]],\"+\",[5,\"8.4\"]]]]]]],[13,\"evo3\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,\"t\",\"*\",[5,\"0.17\"]],\"+\",[5,\"51.2\"]],[1,[1,\"seed\",\"*\",[5,\"0.23\"]],\"+\",[5,\"2.9\"]]]]]]],[10,[6,\"d.vec4f\",[\"flickerMod\",\"evo1\",\"evo2\",\"evo3\"]]]]]},externals:{\"noise.value12\":()=>Aa,\"d.vec2f\":()=>m,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),$te=w.fn([n,n,n,S],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=o.z,i=B(r,.05),l=__tsover_mul(i,__tsover_add(1.5,__tsover_mul(.7,a))),c=__tsover_div(t,i),d=__tsover_div(s,l);return m(D1(c,d,1.9),__tsover_mul(D1(c,d,.45),.22))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"u\"},{type:\"i\",name:\"v\"},{type:\"i\",name:\"spread\"},{type:\"i\",name:\"drift\"}],body:[0,[[13,\"evo2\",[7,\"drift\",\"z\"]],[13,\"sp\",[6,\"std.max\",[\"spread\",[5,\"0.05\"]]]],[13,\"elong\",[1,\"sp\",\"*\",[1,[5,\"1.5\"],\"+\",[1,[5,\"0.7\"],\"*\",\"evo2\"]]]],[13,\"du\",[1,\"u\",\"/\",\"sp\"]],[13,\"dv\",[1,\"v\",\"/\",\"elong\"]],[13,\"main\",[6,\"lightfields.anisotropicGaussianSpot\",[\"du\",\"dv\",[5,\"1.9\"]]]],[13,\"shoulder\",[1,[6,\"lightfields.anisotropicGaussianSpot\",[\"du\",\"dv\",[5,\"0.45\"]]],\"*\",[5,\"0.22\"]]],[10,[6,\"d.vec2f\",[\"main\",\"shoulder\"]]]]]},externals:{\"std.max\":()=>B,\"lightfields.anisotropicGaussianSpot\":()=>D1,\"d.vec2f\":()=>m}})&&e.f)({})),Xte=w.fn([n,n,n,n,S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=a.y,l=a.z,c=a.w,d=B(r,.05),u=__tsover_mul(d,__tsover_add(1.5,__tsover_mul(.7,l))),p=__tsover_div(s,u),_=Tt(__tsover_mul(__tsover_mul(p,p),-.55)),f=__tsover_mul(d,__tsover_add(.42,__tsover_mul(.22,i))),h=B(__tsover_mul(d,__tsover_add(.07,__tsover_mul(.07,c))),.008),g=__tsover_mul(m0(t,f,h),_),y=__tsover_mul(d,__tsover_add(.8,__tsover_mul(.3,i))),b=B(__tsover_mul(d,__tsover_add(.16,__tsover_mul(.12,c))),.015),x=__tsover_mul(__tsover_mul(m0(t,y,b),_),.45);return __tsover_mul(__tsover_add(g,x),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"u\"},{type:\"i\",name:\"v\"},{type:\"i\",name:\"spread\"},{type:\"i\",name:\"streaks\"},{type:\"i\",name:\"drift\"}],body:[0,[[13,\"evo1\",[7,\"drift\",\"y\"]],[13,\"evo2\",[7,\"drift\",\"z\"]],[13,\"evo3\",[7,\"drift\",\"w\"]],[13,\"sp\",[6,\"std.max\",[\"spread\",[5,\"0.05\"]]]],[13,\"elong\",[1,\"sp\",\"*\",[1,[5,\"1.5\"],\"+\",[1,[5,\"0.7\"],\"*\",\"evo2\"]]]],[13,\"dv\",[1,\"v\",\"/\",\"elong\"]],[13,\"alongFade\",[6,\"std.exp\",[[1,[1,\"dv\",\"*\",\"dv\"],\"*\",[4,\"-\",[5,\"0.55\"]]]]]],[13,\"s1c\",[1,\"sp\",\"*\",[1,[5,\"0.42\"],\"+\",[1,[5,\"0.22\"],\"*\",\"evo1\"]]]],[13,\"s1w\",[6,\"std.max\",[[1,\"sp\",\"*\",[1,[5,\"0.07\"],\"+\",[1,[5,\"0.07\"],\"*\",\"evo3\"]]],[5,\"0.008\"]]]],[13,\"streak1\",[1,[6,\"lightfields.streakBand\",[\"u\",\"s1c\",\"s1w\"]],\"*\",\"alongFade\"]],[13,\"s2c\",[1,\"sp\",\"*\",[1,[5,\"0.8\"],\"+\",[1,[5,\"0.3\"],\"*\",\"evo1\"]]]],[13,\"s2w\",[6,\"std.max\",[[1,\"sp\",\"*\",[1,[5,\"0.16\"],\"+\",[1,[5,\"0.12\"],\"*\",\"evo3\"]]],[5,\"0.015\"]]]],[13,\"streak2\",[1,[1,[6,\"lightfields.streakBand\",[\"u\",\"s2c\",\"s2w\"]],\"*\",\"alongFade\"],\"*\",[5,\"0.45\"]]],[10,[1,[1,\"streak1\",\"+\",\"streak2\"],\"*\",\"streaks\"]]]]},externals:{\"std.max\":()=>B,\"std.exp\":()=>Tt,\"lightfields.streakBand\":()=>m0}})&&e.f)({})),Hte=w.fn([n,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return B(__tsover_add(t,__tsover_mul(__tsover_sub(Ys(s.mul(1024).add(m(r,__tsover_mul(r,1.7)))),.5),.012)),0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"heat\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"dithered\",[1,\"heat\",\"+\",[1,[1,[6,\"noise.hash12\",[[6,[7,[6,[7,\"uv\",\"mul\"],[[5,\"1024\"]]],\"add\"],[[6,\"d.vec2f\",[\"t\",[1,\"t\",\"*\",[5,\"1.7\"]]]]]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.012\"]]]],[10,[6,\"std.max\",[\"dithered\",[5,\"0\"]]]]]]},externals:{\"noise.hash12\":()=>Ys,\"d.vec2f\":()=>m,\"std.max\":()=>B}})&&e.f)({})),jte=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(t,s),o=__tsover_mul(Re(__tsover_mul(r,.5)),2);return K(__tsover_sub(1,re(__tsover_sub(o,1))),.5,be(.5,1.5,__tsover_mul(dv(t,0),s)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"cycles\"}],body:[0,[[13,\"s\",[1,\"t\",\"*\",\"cycles\"]],[13,\"m\",[1,[6,\"std.fract\",[[1,\"s\",\"*\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"band\",[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,\"m\",\"-\",[5,\"1\"]]]]]],[13,\"fp\",[1,[6,\"footprint1\",[\"t\",[5,\"0\"]]],\"*\",\"cycles\"]],[10,[6,\"std.mix\",[\"band\",[5,\"0.5\"],[6,\"std.smoothstep\",[[5,\"0.5\"],[5,\"1.5\"],\"fp\"]]]]]]]},externals:{\"std.fract\":()=>Re,\"std.abs\":()=>re,footprint1:()=>dv,\"std.mix\":()=>K,\"std.smoothstep\":()=>be}})&&e.f)({}));function P4(e,t){return{guarded:e,with(s){return P4(e.with(s),t)},dispatch(){if(!t)throw new Error(\"[gpu] KitComputePipeline.dispatch(): no pre-bound size \\u2014 pass opts.size to createGuardedCompute or call dispatchThreads(...) directly\");e.dispatchThreads(...t)},dispatchThreads(...s){e.dispatchThreads(...s)}}}function wr(e,t,s={}){let r=(globalThis.__TYPEGPU_AUTONAME__??(o=>o))(e.createGuardedComputePipeline(t),\"guarded\");return s.bindGroup&&(r=r.with(s.bindGroup)),P4(r,s.size)}function C4(e){return{dispatch(t){for(let s of t)typeof s==\"function\"?s():s.dispatch()}}}function ap(e,t){let s=!1;return{a:e,b:t,orientation:()=>s?1:0,readSource:()=>s?t:e,writeTarget:()=>s?e:t,swap(){s=!s},reset(){s=!1},groups(a){let i=a(e,t),l=a(t,e);return()=>s?l:i},perSide(a){let i=a(e),l=a(t);return{read:()=>s?l:i,written:()=>s?i:l}}}}function pr(e,t,s){return(globalThis.__TYPEGPU_AUTONAME__??(r=>r))(e.createBuffer(Fe(t,s)),\"buffer\").$usage(\"storage\")}var xv=bt,M4=24,Wh=1024,Il=.5,Yu=\"rgba16float\",qh=\"rgba16float\",ri=\"rgba32float\";function Kte(e){let t=e/3,s=e*2+1,r=[],o=0;for(let a=-e;a<=e;a++){let i=Math.exp(-(a*a)/(2*t*t));r.push(i),o+=i}for(let a=0;a<s;a++)r[a]/=o;return r}function _S(e,t){let s=e*2+1,r=Math.max(1,Math.min(e,Math.ceil(t*3))),o=new Array(s).fill(0),a=0;for(let i=-e;i<=e;i++)if(Math.abs(i)<=r){let l=Math.exp(-(i*i)/(2*t*t));o[i+e]=l,a+=l}for(let i=0;i<s;i++)o[i]/=a;return{weights:o,activeHalf:r}}function Qte(){return gt({activeHalf:j,inputWidth:n,inputHeight:n})}function Zte(){return gt({activeHalf:j})}function Jte(e,t,s){let r=e*2+1,o=e,a=Qte(),i=Zte(),l=(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(w.bindGroupLayout({input:{texture:Zt(n)},intermediate:{storageTexture:zt(Yu,\"write-only\")},weights:{storage:Fe(n,r),access:\"readonly\"},params:{uniform:a}}),\"hLayout\"),c=(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(w.bindGroupLayout({src:{storageTexture:zt(Yu,\"read-only\")},output:{storageTexture:zt(qh,\"write-only\")},weights:{storage:Fe(n,r),access:\"readonly\"},params:{uniform:i}}),\"vLayout\");return{hLayout:l,vLayout:c,kernelH:w.fn([M,M])((d=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(d.f=((u,p)=>{\"use gpu\";let _=l.$.params,f=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(u),.5),_.inputWidth),t),.5),h=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(p),.5),_.inputHeight),s),.5),g=j(__tsover_sub(_.inputWidth,1)),y=j(__tsover_sub(_.inputHeight,1)),b=j(Ut(f)),x=G(j(Ut(h)),0,y),P=_.activeHalf,C=__tsover_add(__tsover_mul(P,2),1),E=S(0,0,0,0);for(let R=0;R<C;R++){let U=__tsover_sub(R,P),L=G(__tsover_add(b,U),0,g),k=Ye(l.$.input,fe(M(L),M(x)),0);E=E.add(k.mul(l.$.weights[__tsover_add(U,o)]))}lt(l.$.intermediate,fe(u,p),E)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"hLayout\",\"$\"],\"params\"]],[13,\"inputCx\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputWidth\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]]],[13,\"inputCy\",[1,[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputHeight\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"inputWm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[13,\"inputCxI\",[6,\"d.i32\",[[6,\"std.round\",[\"inputCx\"]]]]],[13,\"inputCyI\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCy\"]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"ah\",[7,\"p\",\"activeHalf\"]],[13,\"count\",[1,[1,\"ah\",\"*\",[5,\"2\"]],\"+\",[5,\"1\"]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"count\"],[102,\"++\",\"i\"],[0,[[13,\"off\",[1,\"i\",\"-\",\"ah\"]],[13,\"sampleX\",[6,\"std.clamp\",[[1,\"inputCxI\",\"+\",\"off\"],[5,\"0\"],\"inputWm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sampleX\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"hLayout\",\"$\"],\"weights\"],[1,\"off\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"hLayout\",\"$\"],\"intermediate\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]},externals:{hLayout:()=>l,\"d.f32\":()=>n,computeWidth:()=>t,computeHeight:()=>s,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.clamp\":()=>G,\"d.vec4f\":()=>S,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,HALF_KERNEL:()=>o,\"std.textureStore\":()=>lt}})&&d.f)({})).$name(\"gaussianBlurH\"),kernelV:w.fn([M,M])((d=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(d.f=((u,p)=>{\"use gpu\";let _=c.$.params.activeHalf,f=__tsover_add(__tsover_mul(_,2),1),h=j(p),g=S(0,0,0,0);for(let y=0;y<f;y++){let b=__tsover_sub(y,_),x=G(__tsover_add(h,b),0,__tsover_sub(s,1)),P=Ye(c.$.src,fe(u,M(x)));g=g.add(P.mul(c.$.weights[__tsover_add(b,o)]))}lt(c.$.output,fe(u,p),g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"av\",[7,[7,[7,\"vLayout\",\"$\"],\"params\"],\"activeHalf\"]],[13,\"count\",[1,[1,\"av\",\"*\",[5,\"2\"]],\"+\",[5,\"1\"]]],[13,\"cyi\",[6,\"d.i32\",[\"cy\"]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"count\"],[102,\"++\",\"i\"],[0,[[13,\"off\",[1,\"i\",\"-\",\"av\"]],[13,\"sampleY\",[6,\"std.clamp\",[[1,\"cyi\",\"+\",\"off\"],[5,\"0\"],[1,\"computeHeight\",\"-\",[5,\"1\"]]]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",[6,\"d.u32\",[\"sampleY\"]]]]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"vLayout\",\"$\"],\"weights\"],[1,\"off\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"vLayout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]},externals:{vLayout:()=>c,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.clamp\":()=>G,computeHeight:()=>s,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,HALF_KERNEL:()=>o,\"std.textureStore\":()=>lt}})&&d.f)({})).$name(\"gaussianBlurV\"),HParams:a,VParams:i,KERNEL_SIZE:r}}function Vh(e,t,s,r,o,a=M4,i=Wh,l=640){let c=a,d=Jte(a,i,l),{hLayout:u,vLayout:p,kernelH:_,kernelV:f,KERNEL_SIZE:h}=d,g=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createTexture({size:[i,l],format:Yu}).$usage(\"storage\"),\"intermediateTex\"),y=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createTexture({size:[i,l],format:qh}).$usage(\"storage\",\"sampled\"),\"outputTex\"),b=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createBuffer(Fe(n,h)).$usage(\"storage\"),\"weightsHBuf\"),x=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createBuffer(Fe(n,h)).$usage(\"storage\"),\"weightsVBuf\"),P=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createUniform(d.HParams),\"paramsH\"),C=(globalThis.__TYPEGPU_AUTONAME__??(V=>V))(e.createUniform(d.VParams),\"paramsV\");o(()=>{g.destroy(),y.destroy()});let E=e.createBindGroup(p,{src:g,output:y,weights:x,params:C.buffer}),R=wr(e,(V=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(V.f=((X,Q)=>{\"use gpu\";_(X,Q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernelH\",[\"cx\",\"cy\"]]]]},externals:{kernelH:()=>_}})&&V.f)({}),{size:[i,l]}),U=[R,wr(e,(V=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(V.f=((X,Q)=>{\"use gpu\";f(X,Q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernelV\",[\"cx\",\"cy\"]]]]},externals:{kernelV:()=>f}})&&V.f)({}),{size:[i,l],bindGroup:E})];function L(V){let X=e.createBindGroup(u,{input:V,intermediate:g,weights:b,params:P.buffer});U[0]=R.with(X)}t!=null&&L(t);let k=r/l,D=s,O=r,F=-1;function q(){let V=F<0?4:F,X=Math.max(V*.5,.001),Q=Math.max(X/k,.001),te=_S(c,X),J=_S(c,Q);b.write(te.weights),x.write(J.weights),P.write({activeHalf:te.activeHalf,inputWidth:D,inputHeight:O}),C.write({activeHalf:J.activeHalf})}function Y(V){Math.abs(V-F)<.01||(F=V,q())}return Y(4),{computeSteps:U,outputTexture:y,updateRadius:Y,computeWidth:i,computeHeight:l,setInputTexture:L,setInputDimensions:(V,X)=>{D=V,O=X,P.patch({inputWidth:V,inputHeight:X});let Q=X/l;Math.abs(Q-k)>.001&&(k=Q,q())}}}function ese(){return gt({inputWidth:n,inputHeight:n,jitter:n,dispersion:n})}function tse(){return gt({scaleY:n,jitter:n,dispersion:n})}function sse(e,t,s,r={}){let o=e*2+1,a=e,i=1/a,l=r.chromatic===!0,c=ese(),d=tse(),u=(globalThis.__TYPEGPU_AUTONAME__??(y=>y))(w.bindGroupLayout({blurMap:{storageTexture:zt(ri,\"read-only\")},input:{texture:Zt(n)},intermediate:{storageTexture:zt(Yu,\"write-only\")},weights:{storage:Fe(n,o),access:\"readonly\"},params:{uniform:c}}),\"hLayout\"),p=(globalThis.__TYPEGPU_AUTONAME__??(y=>y))(w.bindGroupLayout({blurMap:{storageTexture:zt(ri,\"read-only\")},src:{storageTexture:zt(Yu,\"read-only\")},output:{storageTexture:zt(qh,\"write-only\")},weights:{storage:Fe(n,o),access:\"readonly\"},params:{uniform:d}}),\"vLayout\"),_=w.fn([M,M])((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";let P=u.$.params,C=Ye(u.$.blurMap,fe(b,x)).r,E=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(b),.5),P.inputWidth),t),.5),R=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(x),.5),P.inputHeight),s),.5),U=j(__tsover_sub(P.inputWidth,1)),L=j(__tsover_sub(P.inputHeight,1)),k=G(j(Ut(R)),0,L);if(C<Il){let D=G(j(Ut(E)),0,U),O=Ye(u.$.input,fe(M(D),M(k)),0);lt(u.$.intermediate,fe(b,x),O)}else{let D=__tsover_mul(__tsover_sub(Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(n(b),.06711056),__tsover_mul(n(x),.00583715))))),.5),P.jitter),O=S(0,0,0,0);for(let F=-a;F<=a;F++){let q=__tsover_mul(__tsover_mul(__tsover_add(n(F),D),i),C),Y=G(j(Ut(__tsover_add(E,q))),0,U),V=Ye(u.$.input,fe(M(Y),M(k)),0);O=O.add(V.mul(u.$.weights[__tsover_add(F,a)]))}lt(u.$.intermediate,fe(b,x),O)}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"hLayout\",\"$\"],\"params\"]],[13,\"blurRadius\",[7,[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]],\"r\"]],[13,\"inputCx\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputWidth\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]]],[13,\"inputCy\",[1,[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputHeight\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"inputWm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[13,\"inputCyI\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCy\"]]]],[5,\"0\"],\"inputHm1\"]]],[11,[1,\"blurRadius\",\"<\",\"PASSTHROUGH_THRESHOLD\"],[0,[[13,\"cxClamp\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCx\"]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"cxClamp\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[6,\"std.textureStore\",[[7,[7,\"hLayout\",\"$\"],\"intermediate\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"jitterH\",[1,[1,[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"*\",[5,\"0.06711056\"]],\"+\",[1,[6,\"d.f32\",[\"cy\"]],\"*\",[5,\"0.00583715\"]]]]]]]],\"-\",[5,\"0.5\"]],\"*\",[7,\"p\",\"jitter\"]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[4,\"-\",\"HALF_KERNEL\"]],[1,\"i\",\"<=\",\"HALF_KERNEL\"],[102,\"++\",\"i\"],[0,[[13,\"offset\",[1,[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",\"jitterH\"],\"*\",\"invHalf\"],\"*\",\"blurRadius\"]],[13,\"sampleX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",\"offset\"]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sampleX\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"hLayout\",\"$\"],\"weights\"],[1,\"i\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"hLayout\",\"$\"],\"intermediate\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]]]]},externals:{hLayout:()=>u,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.f32\":()=>n,computeWidth:()=>t,computeHeight:()=>s,\"d.i32\":()=>j,\"std.clamp\":()=>G,\"std.round\":()=>Ut,PASSTHROUGH_THRESHOLD:()=>Il,\"d.u32\":()=>M,\"std.textureStore\":()=>lt,\"std.fract\":()=>Re,\"d.vec4f\":()=>S,HALF_KERNEL:()=>a,invHalf:()=>i}})&&y.f)({})).$name(\"variableBlurH\"),f=w.fn([M,M])((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";let P=u.$.params,C=Ye(u.$.blurMap,fe(b,x)).r,E=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(b),.5),P.inputWidth),t),.5),R=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(x),.5),P.inputHeight),s),.5),U=j(__tsover_sub(P.inputWidth,1)),L=j(__tsover_sub(P.inputHeight,1)),k=G(j(Ut(R)),0,L);if(C<Il){let D=G(j(Ut(E)),0,U),O=Ye(u.$.input,fe(M(D),M(k)),0);lt(u.$.intermediate,fe(b,x),O)}else{let D=__tsover_mul(__tsover_sub(Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(n(b),.06711056),__tsover_mul(n(x),.00583715))))),.5),P.jitter),O=__tsover_mul(C,__tsover_add(1,P.dispersion)),F=__tsover_mul(C,__tsover_sub(1,P.dispersion)),q=S(0,0,0,0);for(let Y=-a;Y<=a;Y++){let V=__tsover_mul(__tsover_add(n(Y),D),i),X=G(j(Ut(__tsover_add(E,__tsover_mul(V,C)))),0,U),Q=G(j(Ut(__tsover_add(E,__tsover_mul(V,O)))),0,U),te=G(j(Ut(__tsover_add(E,__tsover_mul(V,F)))),0,U),J=Ye(u.$.input,fe(M(X),M(k)),0),oe=Ye(u.$.input,fe(M(Q),M(k)),0),le=Ye(u.$.input,fe(M(te),M(k)),0),se=S(oe.x,J.y,le.z,J.w);q=q.add(se.mul(u.$.weights[__tsover_add(Y,a)]))}lt(u.$.intermediate,fe(b,x),q)}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"hLayout\",\"$\"],\"params\"]],[13,\"blurRadius\",[7,[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]],\"r\"]],[13,\"inputCx\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputWidth\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]]],[13,\"inputCy\",[1,[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputHeight\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"inputWm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[13,\"inputCyI\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCy\"]]]],[5,\"0\"],\"inputHm1\"]]],[11,[1,\"blurRadius\",\"<\",\"PASSTHROUGH_THRESHOLD\"],[0,[[13,\"cxClamp\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCx\"]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"cxClamp\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[6,\"std.textureStore\",[[7,[7,\"hLayout\",\"$\"],\"intermediate\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"jitterH\",[1,[1,[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"*\",[5,\"0.06711056\"]],\"+\",[1,[6,\"d.f32\",[\"cy\"]],\"*\",[5,\"0.00583715\"]]]]]]]],\"-\",[5,\"0.5\"]],\"*\",[7,\"p\",\"jitter\"]]],[13,\"radiusR\",[1,\"blurRadius\",\"*\",[1,[5,\"1\"],\"+\",[7,\"p\",\"dispersion\"]]]],[13,\"radiusB\",[1,\"blurRadius\",\"*\",[1,[5,\"1\"],\"-\",[7,\"p\",\"dispersion\"]]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[4,\"-\",\"HALF_KERNEL\"]],[1,\"i\",\"<=\",\"HALF_KERNEL\"],[102,\"++\",\"i\"],[0,[[13,\"t\",[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",\"jitterH\"],\"*\",\"invHalf\"]],[13,\"xG\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[1,\"t\",\"*\",\"blurRadius\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"xR\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[1,\"t\",\"*\",\"radiusR\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"xB\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[1,\"t\",\"*\",\"radiusB\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"texG\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"xG\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[13,\"texR\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"xR\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[13,\"texB\",[6,\"std.textureLoad\",[[7,[7,\"hLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"xB\"]],[6,\"d.u32\",[\"inputCyI\"]]]],[5,\"0\"]]]],[13,\"texel\",[6,\"d.vec4f\",[[7,\"texR\",\"x\"],[7,\"texG\",\"y\"],[7,\"texB\",\"z\"],[7,\"texG\",\"w\"]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"hLayout\",\"$\"],\"weights\"],[1,\"i\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"hLayout\",\"$\"],\"intermediate\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]]]]},externals:{hLayout:()=>u,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.f32\":()=>n,computeWidth:()=>t,computeHeight:()=>s,\"d.i32\":()=>j,\"std.clamp\":()=>G,\"std.round\":()=>Ut,PASSTHROUGH_THRESHOLD:()=>Il,\"d.u32\":()=>M,\"std.textureStore\":()=>lt,\"std.fract\":()=>Re,\"d.vec4f\":()=>S,HALF_KERNEL:()=>a,invHalf:()=>i}})&&y.f)({})).$name(\"variableBlurHChromatic\"),h=w.fn([M,M])((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";let P=Ye(p.$.blurMap,fe(b,x)).r;if(P<Il){let C=Ye(p.$.src,fe(b,x));lt(p.$.output,fe(b,x),C)}else{let C=j(x),E=__tsover_div(P,p.$.params.scaleY),R=__tsover_mul(__tsover_sub(Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(n(b),.00583715),__tsover_mul(n(x),.06711056))))),.5),p.$.params.jitter),U=S(0,0,0,0);for(let L=-a;L<=a;L++){let k=__tsover_mul(__tsover_mul(__tsover_add(n(L),R),i),E),D=G(__tsover_add(C,j(Ut(k))),0,__tsover_sub(s,1)),O=Ye(p.$.src,fe(b,M(D)));U=U.add(O.mul(p.$.weights[__tsover_add(L,a)]))}lt(p.$.output,fe(b,x),U)}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"blurRadius\",[7,[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]],\"r\"]],[11,[1,\"blurRadius\",\"<\",\"PASSTHROUGH_THRESHOLD\"],[0,[[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]]],[6,\"std.textureStore\",[[7,[7,\"vLayout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"yi\",[6,\"d.i32\",[\"cy\"]]],[13,\"radiusComputeY\",[1,\"blurRadius\",\"/\",[7,[7,[7,\"vLayout\",\"$\"],\"params\"],\"scaleY\"]]],[13,\"jitterV\",[1,[1,[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"*\",[5,\"0.00583715\"]],\"+\",[1,[6,\"d.f32\",[\"cy\"]],\"*\",[5,\"0.06711056\"]]]]]]]],\"-\",[5,\"0.5\"]],\"*\",[7,[7,[7,\"vLayout\",\"$\"],\"params\"],\"jitter\"]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[4,\"-\",\"HALF_KERNEL\"]],[1,\"i\",\"<=\",\"HALF_KERNEL\"],[102,\"++\",\"i\"],[0,[[13,\"offset\",[1,[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",\"jitterV\"],\"*\",\"invHalf\"],\"*\",\"radiusComputeY\"]],[13,\"sampleY\",[6,\"std.clamp\",[[1,\"yi\",\"+\",[6,\"d.i32\",[[6,\"std.round\",[\"offset\"]]]]],[5,\"0\"],[1,\"computeHeight\",\"-\",[5,\"1\"]]]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",[6,\"d.u32\",[\"sampleY\"]]]]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"vLayout\",\"$\"],\"weights\"],[1,\"i\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"vLayout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]]]]},externals:{\"std.textureLoad\":()=>Ye,vLayout:()=>p,\"d.vec2u\":()=>fe,PASSTHROUGH_THRESHOLD:()=>Il,\"std.textureStore\":()=>lt,\"d.i32\":()=>j,\"std.fract\":()=>Re,\"d.f32\":()=>n,\"d.vec4f\":()=>S,HALF_KERNEL:()=>a,invHalf:()=>i,\"std.clamp\":()=>G,\"std.round\":()=>Ut,computeHeight:()=>s,\"d.u32\":()=>M}})&&y.f)({})).$name(\"variableBlurV\"),g=w.fn([M,M])((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";let P=Ye(p.$.blurMap,fe(b,x)).r;if(P<Il){let C=Ye(p.$.src,fe(b,x));lt(p.$.output,fe(b,x),C)}else{let C=j(x),E=p.$.params,R=__tsover_div(P,E.scaleY),U=__tsover_mul(R,__tsover_add(1,E.dispersion)),L=__tsover_mul(R,__tsover_sub(1,E.dispersion)),k=__tsover_mul(__tsover_sub(Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(n(b),.00583715),__tsover_mul(n(x),.06711056))))),.5),E.jitter),D=S(0,0,0,0);for(let O=-a;O<=a;O++){let F=__tsover_mul(__tsover_add(n(O),k),i),q=G(__tsover_add(C,j(Ut(__tsover_mul(F,R)))),0,__tsover_sub(s,1)),Y=G(__tsover_add(C,j(Ut(__tsover_mul(F,U)))),0,__tsover_sub(s,1)),V=G(__tsover_add(C,j(Ut(__tsover_mul(F,L)))),0,__tsover_sub(s,1)),X=Ye(p.$.src,fe(b,M(q))),Q=Ye(p.$.src,fe(b,M(Y))),te=Ye(p.$.src,fe(b,M(V))),J=S(Q.x,X.y,te.z,X.w);D=D.add(J.mul(p.$.weights[__tsover_add(O,a)]))}lt(p.$.output,fe(b,x),D)}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"blurRadius\",[7,[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]],\"r\"]],[11,[1,\"blurRadius\",\"<\",\"PASSTHROUGH_THRESHOLD\"],[0,[[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]]]]],[6,\"std.textureStore\",[[7,[7,\"vLayout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"yi\",[6,\"d.i32\",[\"cy\"]]],[13,\"p\",[7,[7,\"vLayout\",\"$\"],\"params\"]],[13,\"radiusComputeY\",[1,\"blurRadius\",\"/\",[7,\"p\",\"scaleY\"]]],[13,\"radiusR\",[1,\"radiusComputeY\",\"*\",[1,[5,\"1\"],\"+\",[7,\"p\",\"dispersion\"]]]],[13,\"radiusB\",[1,\"radiusComputeY\",\"*\",[1,[5,\"1\"],\"-\",[7,\"p\",\"dispersion\"]]]],[13,\"jitterV\",[1,[1,[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"*\",[5,\"0.00583715\"]],\"+\",[1,[6,\"d.f32\",[\"cy\"]],\"*\",[5,\"0.06711056\"]]]]]]]],\"-\",[5,\"0.5\"]],\"*\",[7,\"p\",\"jitter\"]]],[12,\"sum\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[4,\"-\",\"HALF_KERNEL\"]],[1,\"i\",\"<=\",\"HALF_KERNEL\"],[102,\"++\",\"i\"],[0,[[13,\"t\",[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",\"jitterV\"],\"*\",\"invHalf\"]],[13,\"yG\",[6,\"std.clamp\",[[1,\"yi\",\"+\",[6,\"d.i32\",[[6,\"std.round\",[[1,\"t\",\"*\",\"radiusComputeY\"]]]]]],[5,\"0\"],[1,\"computeHeight\",\"-\",[5,\"1\"]]]]],[13,\"yR\",[6,\"std.clamp\",[[1,\"yi\",\"+\",[6,\"d.i32\",[[6,\"std.round\",[[1,\"t\",\"*\",\"radiusR\"]]]]]],[5,\"0\"],[1,\"computeHeight\",\"-\",[5,\"1\"]]]]],[13,\"yB\",[6,\"std.clamp\",[[1,\"yi\",\"+\",[6,\"d.i32\",[[6,\"std.round\",[[1,\"t\",\"*\",\"radiusB\"]]]]]],[5,\"0\"],[1,\"computeHeight\",\"-\",[5,\"1\"]]]]],[13,\"texG\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",[6,\"d.u32\",[\"yG\"]]]]]]],[13,\"texR\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",[6,\"d.u32\",[\"yR\"]]]]]]],[13,\"texB\",[6,\"std.textureLoad\",[[7,[7,\"vLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",[6,\"d.u32\",[\"yB\"]]]]]]],[13,\"texel\",[6,\"d.vec4f\",[[7,\"texR\",\"x\"],[7,\"texG\",\"y\"],[7,\"texB\",\"z\"],[7,\"texG\",\"w\"]]]],[2,\"sum\",\"=\",[6,[7,\"sum\",\"add\"],[[6,[7,\"texel\",\"mul\"],[[8,[7,[7,\"vLayout\",\"$\"],\"weights\"],[1,\"i\",\"+\",\"HALF_KERNEL\"]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"vLayout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"sum\"]]]]]]]},externals:{\"std.textureLoad\":()=>Ye,vLayout:()=>p,\"d.vec2u\":()=>fe,PASSTHROUGH_THRESHOLD:()=>Il,\"std.textureStore\":()=>lt,\"d.i32\":()=>j,\"std.fract\":()=>Re,\"d.f32\":()=>n,\"d.vec4f\":()=>S,HALF_KERNEL:()=>a,invHalf:()=>i,\"std.clamp\":()=>G,\"std.round\":()=>Ut,computeHeight:()=>s,\"d.u32\":()=>M}})&&y.f)({})).$name(\"variableBlurVChromatic\");return{hLayout:u,vLayout:p,kernelH:l?f:_,kernelV:l?g:h,HParams:c,VParams:d,KERNEL_SIZE:o}}function E4(e,t,s,r,o,a=M4,i=Wh,l=640,c){let d=a,u=sse(a,i,l,{chromatic:c?.chromatic}),{hLayout:p,vLayout:_,kernelH:f,kernelV:h,KERNEL_SIZE:g}=u,y=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createTexture({size:[i,l],format:ri}).$usage(\"storage\"),\"blurMapTex\"),b=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createTexture({size:[i,l],format:Yu}).$usage(\"storage\"),\"intermediateTex\"),x=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createTexture({size:[i,l],format:qh}).$usage(\"storage\",\"sampled\"),\"outputTex\"),P=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createBuffer(Fe(n,g)).$usage(\"storage\"),\"weightsBuf\");P.write(Kte(d));let C=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createUniform(u.HParams),\"paramsH\"),E=(globalThis.__TYPEGPU_AUTONAME__??(O=>O))(e.createUniform(u.VParams),\"paramsV\"),R={jitter:c?.jitter??0,dispersion:0};C.write({inputWidth:s,inputHeight:r,...R}),E.write({scaleY:r/l,...R}),o(()=>{y.destroy(),b.destroy(),x.destroy()});let U=e.createBindGroup(_,{blurMap:y,src:b,output:x,weights:P,params:E.buffer}),L=wr(e,(O=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(O.f=((F,q)=>{\"use gpu\";f(F,q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernelH\",[\"cx\",\"cy\"]]]]},externals:{kernelH:()=>f}})&&O.f)({}),{size:[i,l]}),k=[L,wr(e,(O=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(O.f=((F,q)=>{\"use gpu\";h(F,q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernelV\",[\"cx\",\"cy\"]]]]},externals:{kernelV:()=>h}})&&O.f)({}),{size:[i,l],bindGroup:U})];function D(O){let F=e.createBindGroup(p,{blurMap:y,input:O,intermediate:b,weights:P,params:C.buffer});k[0]=L.with(F)}return t!=null&&D(t),{computeSteps:k,outputTexture:x,blurMapTexture:y,computeWidth:i,computeHeight:l,setInputTexture:D,setInputDimensions:(O,F)=>{C.patch({inputWidth:O,inputHeight:F}),E.patch({scaleY:F/l})},setDetail:(O,F)=>{O===R.jitter&&F===R.dispersion||(R.jitter=O,R.dispersion=F,C.patch({jitter:O,dispersion:F}),E.patch({jitter:O,dispersion:F}))}}}function rse(e,t){let s=Number((t*t).toPrecision(15)),r=e-1,o=Array.from({length:e},(i,l)=>{let c=(l/r-.5)*2;return Math.exp(-.5*c*c/s)}),a=o.reduce((i,l)=>i+l);return o.map(i=>i/a)}function qb(e){let{weights:t,tapCoord:s,sample:r,component:o}=e,a;for(let i=0;i<t.length;i++){let l=r(s(i)),c=(o?l.member(o):l).mul(t[i]);a=a?a.add(c):c}return a}var np={inputWidth:n,inputHeight:n},y_={inputMin:n,inputMax:n,outputMin:n,outputMax:n,curve:n},fS=new Map;function b_(e){let t=fS.get(e);if(t)return t;let s=w.fn([S],n)((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=(o=>{\"use gpu\";return e===\"alpha\"?o.w:e===\"alphaInverted\"?__tsover_sub(1,o.w):e===\"luminanceInverted\"?__tsover_sub(1,Se(o.xyz,A(.2126,.7152,.0722))):Se(o.xyz,A(.2126,.7152,.0722))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sample\"}],body:[0,[[10,[105,[1,\"channel\",\"===\",[103,\"alpha\"]],[7,\"sample\",\"w\"],[105,[1,\"channel\",\"===\",[103,\"alphaInverted\"]],[1,[5,\"1\"],\"-\",[7,\"sample\",\"w\"]],[105,[1,\"channel\",\"===\",[103,\"luminanceInverted\"]],[1,[5,\"1\"],\"-\",[6,\"std.dot\",[[7,\"sample\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[6,\"std.dot\",[[7,\"sample\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]]]]]]]},externals:{channel:()=>e,\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&r.f)({})).$name(`mapSourceScalar_${e}`);return fS.set(e,s),s}var Ei=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=B(__tsover_sub(r,s),1e-4);return K(o,a,vt(G(__tsover_div(__tsover_sub(t,s),l),0,1),vt(2,__tsover_mul(-i,2))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"raw\"},{type:\"i\",name:\"inputMin\"},{type:\"i\",name:\"inputMax\"},{type:\"i\",name:\"outputMin\"},{type:\"i\",name:\"outputMax\"},{type:\"i\",name:\"curve\"}],body:[0,[[13,\"inputRange\",[6,\"std.max\",[[1,\"inputMax\",\"-\",\"inputMin\"],[5,\"0.0001\"]]]],[13,\"normalised\",[6,\"std.clamp\",[[1,[1,\"raw\",\"-\",\"inputMin\"],\"/\",\"inputRange\"],[5,\"0\"],[5,\"1\"]]]],[13,\"exponent\",[6,\"std.pow\",[[5,\"2\"],[1,[4,\"-\",\"curve\"],\"*\",[5,\"2\"]]]]],[13,\"eased\",[6,\"std.pow\",[\"normalised\",\"exponent\"]]],[10,[6,\"std.mix\",[\"outputMin\",\"outputMax\",\"eased\"]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.pow\":()=>vt,\"std.mix\":()=>K}})&&e.f)({})).$name(\"applyRemapWindow\");function Nh(e){return{inputMin:e.inputMin,inputMax:e.inputMax,outputMin:e.outputMin,outputMax:e.outputMax,curve:e.curve}}var Av=w.fn([Zt(n),M,M,m,m,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=o.x,c=o.y,d=j(__tsover_sub(l,1)),u=j(__tsover_sub(c,1)),p=S(0,0,0,0);for(let b=0;b<4;b++)for(let x=0;x<4;x++){let P=__tsover_div(__tsover_add(n(s),__tsover_div(__tsover_add(n(x),.5),4)),a.x),C=__tsover_div(__tsover_add(n(r),__tsover_div(__tsover_add(n(b),.5),4)),a.y),E=G(j(__tsover_mul(P,l)),0,d),R=G(j(__tsover_mul(C,c)),0,u);p=p.add(Ye(t,fe(M(E),M(R)),0))}p=p.mul(.0625);let _=__tsover_add(__tsover_mul(i,.5),1e-4),f=Se(p.xyz,A(.2126,.7152,.0722)),h=G(__tsover_add(__tsover_sub(f,i),_),0,__tsover_mul(_,2)),g=__tsover_div(__tsover_mul(h,h),__tsover_mul(_,4)),y=__tsover_div(B(g,__tsover_sub(f,i)),B(f,1e-4));return S(p.xyz.mul(y),__tsover_mul(p.w,y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"childTexture\"},{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"},{type:\"i\",name:\"inputSize\"},{type:\"i\",name:\"computeSize\"},{type:\"i\",name:\"threshold\"}],body:[0,[[13,\"iw\",[7,\"inputSize\",\"x\"]],[13,\"ih\",[7,\"inputSize\",\"y\"]],[13,\"inputWm1\",[6,\"d.i32\",[[1,\"iw\",\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,\"ih\",\"-\",[5,\"1\"]]]]],[12,\"sample\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"ty\",[5,\"0\"]],[1,\"ty\",\"<\",[5,\"4\"]],[102,\"++\",\"ty\"],[0,[[14,[12,\"tx\",[5,\"0\"]],[1,\"tx\",\"<\",[5,\"4\"]],[102,\"++\",\"tx\"],[0,[[13,\"su\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[1,[1,[6,\"d.f32\",[\"tx\"]],\"+\",[5,\"0.5\"]],\"/\",[5,\"4\"]]],\"/\",[7,\"computeSize\",\"x\"]]],[13,\"sv\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[1,[1,[6,\"d.f32\",[\"ty\"]],\"+\",[5,\"0.5\"]],\"/\",[5,\"4\"]]],\"/\",[7,\"computeSize\",\"y\"]]],[13,\"sx\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,\"su\",\"*\",\"iw\"]]],[5,\"0\"],\"inputWm1\"]]],[13,\"sy\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,\"sv\",\"*\",\"ih\"]]],[5,\"0\"],\"inputHm1\"]]],[2,\"sample\",\"=\",[6,[7,\"sample\",\"add\"],[[6,\"std.textureLoad\",[\"childTexture\",[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]]]]]]]]]]],[2,\"sample\",\"=\",[6,[7,\"sample\",\"mul\"],[[5,\"0.0625\"]]]],[13,\"knee\",[1,[1,\"threshold\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.0001\"]]],[13,\"lum\",[6,\"std.dot\",[[7,\"sample\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"soft\",[6,\"std.clamp\",[[1,[1,\"lum\",\"-\",\"threshold\"],\"+\",\"knee\"],[5,\"0\"],[1,\"knee\",\"*\",[5,\"2\"]]]]],[13,\"softCurve\",[1,[1,\"soft\",\"*\",\"soft\"],\"/\",[1,\"knee\",\"*\",[5,\"4\"]]]],[13,\"mask\",[1,[6,\"std.max\",[\"softCurve\",[1,\"lum\",\"-\",\"threshold\"]]],\"/\",[6,\"std.max\",[\"lum\",[5,\"0.0001\"]]]]],[10,[6,\"d.vec4f\",[[6,[7,[7,\"sample\",\"xyz\"],\"mul\"],[\"mask\"]],[1,[7,\"sample\",\"w\"],\"*\",\"mask\"]]]]]]},externals:{\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"d.f32\":()=>n,\"std.clamp\":()=>G,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.max\":()=>B}})&&e.f)({})).$name(\"bloomExtractSoftKnee\"),K3=\"rgba16float\";function k4(e,t,s){let r=(globalThis.__TYPEGPU_AUTONAME__??(a=>a))(gt({threshold:n,size:n,...np}),\"Params\"),o=(globalThis.__TYPEGPU_AUTONAME__??(a=>a))(w.bindGroupLayout({childTexture:{texture:Zt(n)},brightMap:{storageTexture:zt(K3,\"write-only\")},blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:r}}),\"layout\");return{layout:o,kernel:w.fn([M,M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";let c=o.$.params,d=Av(o.$.childTexture,i,l,m(c.inputWidth,c.inputHeight),m(e,t),c.threshold);lt(o.$.brightMap,fe(i,l),d),lt(o.$.blurMap,fe(i,l),S(c.size,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"bright\",[6,\"bloomExtractSoftKnee\",[[7,[7,\"layout\",\"$\"],\"childTexture\"],\"cx\",\"cy\",[6,\"d.vec2f\",[[7,\"p\",\"inputWidth\"],[7,\"p\",\"inputHeight\"]]],[6,\"d.vec2f\",[\"computeWidth\",\"computeHeight\"]],[7,\"p\",\"threshold\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"brightMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"bright\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"p\",\"size\"],[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>o,bloomExtractSoftKnee:()=>Av,\"d.vec2f\":()=>m,computeWidth:()=>e,computeHeight:()=>t,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&a.f)({})).$name(s),Params:r}}function ose(e,t,s,r){let o=(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(gt({threshold:n,...np,...y_}),\"Params\"),a=(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(w.bindGroupLayout({childTexture:{texture:Zt(n)},source:{texture:Zt(n)},brightMap:{storageTexture:zt(K3,\"write-only\")},blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:o}}),\"layout\"),i=b_(r);return{layout:a,kernel:w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";let u=a.$.params,p=u.inputWidth,_=u.inputHeight,f=Av(a.$.childTexture,c,d,m(p,_),m(e,t),u.threshold);lt(a.$.brightMap,fe(c,d),f);let h=__tsover_div(__tsover_add(n(c),.5),e),g=__tsover_div(__tsover_add(n(d),.5),t),y=G(j(Ut(__tsover_mul(h,p))),0,j(__tsover_sub(p,1))),b=G(j(Ut(__tsover_mul(g,_))),0,j(__tsover_sub(_,1))),x=Ei(i(Ye(a.$.source,fe(M(y),M(b)),0)),u.inputMin,u.inputMax,u.outputMin,u.outputMax,u.curve);lt(a.$.blurMap,fe(c,d),S(x,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"iw\",[7,\"p\",\"inputWidth\"]],[13,\"ih\",[7,\"p\",\"inputHeight\"]],[13,\"bright\",[6,\"bloomExtractSoftKnee\",[[7,[7,\"layout\",\"$\"],\"childTexture\"],\"cx\",\"cy\",[6,\"d.vec2f\",[\"iw\",\"ih\"]],[6,\"d.vec2f\",[\"computeWidth\",\"computeHeight\"]],[7,\"p\",\"threshold\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"brightMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"bright\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"srcX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",\"iw\"]]]]],[5,\"0\"],[6,\"d.i32\",[[1,\"iw\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",\"ih\"]]]]],[5,\"0\"],[6,\"d.i32\",[[1,\"ih\",\"-\",[5,\"1\"]]]]]]],[13,\"mapSample\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"source\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"srcX\"]],[6,\"d.u32\",[\"srcY\"]]]],[5,\"0\"]]]],[13,\"radius\",[6,\"applyRemapWindow\",[[6,\"channelScalar\",[\"mapSample\"]],[7,\"p\",\"inputMin\"],[7,\"p\",\"inputMax\"],[7,\"p\",\"outputMin\"],[7,\"p\",\"outputMax\"],[7,\"p\",\"curve\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>a,bloomExtractSoftKnee:()=>Av,\"d.vec2f\":()=>m,computeWidth:()=>e,computeHeight:()=>t,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.f32\":()=>n,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.u32\":()=>M,applyRemapWindow:()=>Ei,channelScalar:()=>i,\"d.vec4f\":()=>S}})&&l.f)({})).$name(s),Params:o}}function ase(e,t){let s=Math.max(Wh,640),r=t>0?e/t:1;return{width:Math.max(8,r>=1?s:Math.round(s*r)),height:Math.max(8,r>=1?Math.round(s/r):s)}}var $h=\"blurredTexture\";function Wu(e){return{width:Math.max(1,Math.round(e.width)),height:Math.max(1,Math.round(e.height))}}function B4(e,t){let{childNode:s,gpu:r,convertToTexture:o,registerComputeTexture:a,onCleanup:i,onResize:l,dimensions:c}=e;if(!s)return null;let d=r?.root;if(!d)return null;let u=t.outputKey??$h,p=o(s),_=Wu(c),f=Vh(d,null,_.width,_.height,i,t.halfKernel),h=a(f.outputTexture);return l(({width:g,height:y})=>{let b=Wu({width:g,height:y});f.setInputDimensions(b.width,b.height)}),{outputs:{childTexture:p,[u]:h},bindInputs:g=>{let y=g(p.key);y&&f.setInputTexture(y.texture)},getComputeNodes:()=>(f.updateRadius(t.radius()),f.computeSteps)}}function qu(e,t){let{childNode:s,gpu:r,convertToTexture:o,registerComputeTexture:a,onCleanup:i,onResize:l,dimensions:c}=e;if(!s)return null;let d=r?.root;if(!d)return null;let u=t.outputKey??$h,p=t.source,_=o(s),f=Wu(c),h=E4(d,null,f.width,f.height,i,t.halfKernel,void 0,void 0,{chromatic:t.chromatic}),g=a(h.outputTexture),y=t.buildFill(h.computeWidth,h.computeHeight),b=(globalThis.__TYPEGPU_AUTONAME__??(E=>E))(d.createUniform(y.Params),\"fillParams\"),x=h.blurMapTexture,P={size:[h.computeWidth,h.computeHeight]},C=p?wr(d,(E=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(E.f=((R,U)=>{\"use gpu\";y.kernel(R,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"fill.kernel\",[\"cx\",\"cy\"]]]]},externals:{\"fill.kernel\":()=>y.kernel}})&&E.f)({}),P):wr(d,(E=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(E.f=((R,U)=>{\"use gpu\";y.kernel(R,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"fill.kernel\",[\"cx\",\"cy\"]]]]},externals:{\"fill.kernel\":()=>y.kernel}})&&E.f)({}),{...P,bindGroup:d.createBindGroup(y.layout,{blurMap:x,params:b.buffer})});return l(({width:E,height:R})=>{f=Wu({width:E,height:R}),h.setInputDimensions(f.width,f.height)}),{outputs:{childTexture:_,[u]:g},bindInputs:E=>{let R=E(_.key);if(R&&h.setInputTexture(R.texture),!p)return;let U=E(p.sourceTexture.key);if(U){let L=d.createBindGroup(y.layout,{source:U.texture,blurMap:x,params:b.buffer});C=C.with(L)}},getComputeNodes:()=>{if(b.write(t.fillValues(f,p?.window())),t.detail){let E=t.detail();h.setDetail(E.jitter,E.dispersion)}return[C,...h.computeSteps]}}}function z4(e,t){let{childNode:s,gpu:r,convertToTexture:o,registerComputeTexture:a,onCleanup:i,onResize:l,dimensions:c}=e;if(!s)return null;let d=r?.root;if(!d)return null;let u=t.outputKey??$h,p=t.mapInfo??null,_=o(s),f=Wu(c),h=ase(f.width,f.height),g=(globalThis.__TYPEGPU_AUTONAME__??(E=>E))(d.createTexture({size:[h.width,h.height],format:K3}).$usage(\"storage\",\"sampled\"),\"brightBuffer\");i(()=>g.destroy());let y=E4(d,g,h.width,h.height,i,void 0,h.width,h.height,{jitter:1}),b=a(y.outputTexture);l(({width:E,height:R})=>{f=Wu({width:E,height:R})});let x=p&&t.buildExtractMap?t.buildExtractMap(h.width,h.height,p.channel):t.buildExtract(h.width,h.height),P=(globalThis.__TYPEGPU_AUTONAME__??(E=>E))(d.createUniform(x.Params),\"prepassParams\"),C=wr(d,(E=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(E.f=((R,U)=>{\"use gpu\";x.kernel(R,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"graph.kernel\",[\"cx\",\"cy\"]]]]},externals:{\"graph.kernel\":()=>x.kernel}})&&E.f)({}),{size:[h.width,h.height]});return{outputs:{childTexture:_,[u]:b},bindInputs:E=>{let R=E(_.key);if(!R)return;let U=p?E(p.sourceTexture.key):void 0;if(p&&!U)return;let L={childTexture:R.texture,brightMap:g,blurMap:y.blurMapTexture,params:P.buffer};U&&(L.source=U.texture),C=C.with(d.createBindGroup(x.layout,L))},getComputeNodes:()=>{let E=t.threshold(),R=p?{threshold:E,inputWidth:f.width,inputHeight:f.height,...Nh(p.window())}:{threshold:E,size:t.radius(),inputWidth:f.width,inputHeight:f.height};return P.write(R),[C,...y.computeSteps]}}}function x_(e,t,s){let{childNode:r,computeOutputs:o,ctx:a,convertToTexture:i}=e;if(!r)return xt;let l=o?.[s?.blurredKey??$h],c=o?.[s?.sharpKey??\"childTexture\"];if(!l||!c)return W(Yt,\"unpremultiplyAlpha\",[i(r).sample(a.uv)]);let d=c.sample(a.uv);return W(Yt,\"unpremultiplyAlpha\",[t(l.sample(a.uv),d)])}var nse=.36,Q3=e=>e*nse,ise=.5,T0=.1;function lse(){return gt({...np,...y_})}function cse(e,t,s){let r=lse(),o=(globalThis.__TYPEGPU_AUTONAME__??(i=>i))(w.bindGroupLayout({source:{texture:Zt(n)},blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:r}}),\"layout\"),a=b_(s);return{layout:o,kernel:w.fn([M,M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c)=>{\"use gpu\";let d=o.$.params,u=__tsover_div(__tsover_add(n(l),.5),e),p=__tsover_div(__tsover_add(n(c),.5),t),_=G(j(Ut(__tsover_mul(u,d.inputWidth))),0,j(__tsover_sub(d.inputWidth,1))),f=G(j(Ut(__tsover_mul(p,d.inputHeight))),0,j(__tsover_sub(d.inputHeight,1))),h=Ei(a(Ye(o.$.source,fe(M(_),M(f)),0)),d.inputMin,d.inputMax,d.outputMin,d.outputMax,d.curve),g=__tsover_mul(h,.36);lt(o.$.blurMap,fe(l,c),S(g,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"srcX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",[7,\"p\",\"inputWidth\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",[7,\"p\",\"inputHeight\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]]]],[13,\"sample\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"source\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"srcX\"]],[6,\"d.u32\",[\"srcY\"]]]],[5,\"0\"]]]],[13,\"remapped\",[6,\"applyRemapWindow\",[[6,\"channelScalar\",[\"sample\"]],[7,\"p\",\"inputMin\"],[7,\"p\",\"inputMax\"],[7,\"p\",\"outputMin\"],[7,\"p\",\"outputMax\"],[7,\"p\",\"curve\"]]]],[13,\"radius\",[1,\"remapped\",\"*\",[5,\"0.36\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>o,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,applyRemapWindow:()=>Ei,channelScalar:()=>a,\"std.textureStore\":()=>lt,\"d.vec4f\":()=>S}})&&i.f)({})).$name(\"fillBlurMap\"),Params:r}}var dse=w.fn([S,S,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,.1),l=__tsover_mul(o,.1),c=__tsover_mul(a,.1),d=B(B(B(i,l),c),.01),u=__tsover_div(i,d),p=__tsover_div(l,d),_=__tsover_div(c,d);return S(K(t.x,s.x,u),K(t.y,s.y,p),K(t.z,s.z,_),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sharp\"},{type:\"i\",name:\"blurred\"},{type:\"i\",name:\"redIntensity\"},{type:\"i\",name:\"greenIntensity\"},{type:\"i\",name:\"blueIntensity\"}],body:[0,[[13,\"redRadius\",[1,\"redIntensity\",\"*\",[5,\"0.1\"]]],[13,\"greenRadius\",[1,\"greenIntensity\",\"*\",[5,\"0.1\"]]],[13,\"blueRadius\",[1,\"blueIntensity\",\"*\",[5,\"0.1\"]]],[13,\"maxRadius\",[6,\"std.max\",[[6,\"std.max\",[[6,\"std.max\",[\"redRadius\",\"greenRadius\"]],\"blueRadius\"]],[5,\"0.01\"]]]],[13,\"rMix\",[1,\"redRadius\",\"/\",\"maxRadius\"]],[13,\"gMix\",[1,\"greenRadius\",\"/\",\"maxRadius\"]],[13,\"bMix\",[1,\"blueRadius\",\"/\",\"maxRadius\"]],[13,\"finalR\",[6,\"std.mix\",[[7,\"sharp\",\"x\"],[7,\"blurred\",\"x\"],\"rMix\"]]],[13,\"finalG\",[6,\"std.mix\",[[7,\"sharp\",\"y\"],[7,\"blurred\",\"y\"],\"gMix\"]]],[13,\"finalB\",[6,\"std.mix\",[[7,\"sharp\",\"z\"],[7,\"blurred\",\"z\"],\"bMix\"]]],[10,[6,\"d.vec4f\",[\"finalR\",\"finalG\",\"finalB\",[7,\"sharp\",\"w\"]]]]]]},externals:{\"std.max\":()=>B,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"channelBlurCompose\"),wv=w.fn([n,n,n,n,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_mul(t,xv),c=m(__tsover_div(pe(l),i),ae(l)),d=m(s,__tsover_sub(1,r));return be(0,o,B(0,Se(a.sub(d),c)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"centerX\"},{type:\"i\",name:\"centerY\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"direction\",[6,\"d.vec2f\",[[1,[6,\"std.cos\",[\"angleRad\"]],\"/\",\"aspect\"],[6,\"std.sin\",[\"angleRad\"]]]]],[13,\"centerPos\",[6,\"d.vec2f\",[\"centerX\",[1,[5,\"1\"],\"-\",\"centerY\"]]]],[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[\"centerPos\"]]],[13,\"directionalDist\",[6,\"std.max\",[[5,\"0\"],[6,\"std.dot\",[\"centeredUV\",\"direction\"]]]]],[10,[6,\"std.smoothstep\",[[5,\"0\"],\"falloff\",\"directionalDist\"]]]]]},externals:{DEG_TO_RAD:()=>xv,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.max\":()=>B,\"std.dot\":()=>Se,\"std.smoothstep\":()=>be}})&&e.f)({})).$name(\"progressiveBlurAmount\");function use(){return gt({angle:n,centerX:n,centerY:n,falloff:n,aspect:n,maxRadius:n})}function pse(){return gt({angle:n,centerX:n,centerY:n,falloff:n,aspect:n,...np,...y_})}function mse(e,t){let s=use(),r=(globalThis.__TYPEGPU_AUTONAME__??(o=>o))(w.bindGroupLayout({blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:s}}),\"layout\");return{layout:r,kernel:w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=r.$.params,c=__tsover_div(__tsover_add(n(a),.5),e),d=__tsover_div(__tsover_add(n(i),.5),t),u=wv(l.angle,l.centerX,l.centerY,l.falloff,m(c,d),l.aspect),p=__tsover_mul(u,l.maxRadius);lt(r.$.blurMap,fe(a,i),S(p,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"blurAmount\",[6,\"progressiveBlurAmount\",[[7,\"p\",\"angle\"],[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"],[7,\"p\",\"falloff\"],[6,\"d.vec2f\",[\"u\",\"v\"]],[7,\"p\",\"aspect\"]]]],[13,\"radius\",[1,\"blurAmount\",\"*\",[7,\"p\",\"maxRadius\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>r,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,progressiveBlurAmount:()=>wv,\"d.vec2f\":()=>m,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(\"progressiveBlurFillBlurMap\"),Params:s}}function _se(e,t,s){let r=pse(),o=(globalThis.__TYPEGPU_AUTONAME__??(i=>i))(w.bindGroupLayout({source:{texture:Zt(n)},blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:r}}),\"layout\"),a=b_(s);return{layout:o,kernel:w.fn([M,M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c)=>{\"use gpu\";let d=o.$.params,u=__tsover_div(__tsover_add(n(l),.5),e),p=__tsover_div(__tsover_add(n(c),.5),t),_=wv(d.angle,d.centerX,d.centerY,d.falloff,m(u,p),d.aspect),f=G(j(Ut(__tsover_mul(u,d.inputWidth))),0,j(__tsover_sub(d.inputWidth,1))),h=G(j(Ut(__tsover_mul(p,d.inputHeight))),0,j(__tsover_sub(d.inputHeight,1))),g=Ei(a(Ye(o.$.source,fe(M(f),M(h)),0)),d.inputMin,d.inputMax,d.outputMin,d.outputMax,d.curve),y=__tsover_mul(_,__tsover_mul(g,.36));lt(o.$.blurMap,fe(l,c),S(y,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"blurAmount\",[6,\"progressiveBlurAmount\",[[7,\"p\",\"angle\"],[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"],[7,\"p\",\"falloff\"],[6,\"d.vec2f\",[\"u\",\"v\"]],[7,\"p\",\"aspect\"]]]],[13,\"srcX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",[7,\"p\",\"inputWidth\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",[7,\"p\",\"inputHeight\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]]]],[13,\"sample\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"source\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"srcX\"]],[6,\"d.u32\",[\"srcY\"]]]],[5,\"0\"]]]],[13,\"remapped\",[6,\"applyRemapWindow\",[[6,\"channelScalar\",[\"sample\"]],[7,\"p\",\"inputMin\"],[7,\"p\",\"inputMax\"],[7,\"p\",\"outputMin\"],[7,\"p\",\"outputMax\"],[7,\"p\",\"curve\"]]]],[13,\"radius\",[1,\"blurAmount\",\"*\",[1,\"remapped\",\"*\",\"INTENSITY_TO_RADIUS\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>o,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,progressiveBlurAmount:()=>wv,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,applyRemapWindow:()=>Ei,channelScalar:()=>a,INTENSITY_TO_RADIUS:()=>.36,\"std.textureStore\":()=>lt,\"d.vec4f\":()=>S}})&&i.f)({})).$name(\"progressiveBlurFillBlurMapVariable\"),Params:r}}var Km=w.fn([n,m,n,n,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_mul(t,xv),c=m(__tsover_mul(ae(l),-1),pe(l)),d=m(s.x,__tsover_sub(1,s.y)),u=a.sub(d),p=re(Se(m(__tsover_mul(u.x,i),u.y),c)),_=__tsover_mul(r,.5);return be(_,__tsover_add(_,o),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"width\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"perpVector\",[6,\"d.vec2f\",[[1,[6,\"std.sin\",[\"angleRad\"]],\"*\",[4,\"-\",[5,\"1\"]]],[6,\"std.cos\",[\"angleRad\"]]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[\"centerPos\"]]],[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"centeredUV\",\"x\"],\"*\",\"aspect\"],[7,\"centeredUV\",\"y\"]]]],[13,\"distFromLine\",[6,\"std.abs\",[[6,\"std.dot\",[\"aspectCorrectedUV\",\"perpVector\"]]]]],[13,\"focusWidth\",[1,\"width\",\"*\",[5,\"0.5\"]]],[10,[6,\"std.smoothstep\",[\"focusWidth\",[1,\"focusWidth\",\"+\",\"falloff\"],\"distFromLine\"]]]]]},externals:{DEG_TO_RAD:()=>xv,\"d.vec2f\":()=>m,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.abs\":()=>re,\"std.dot\":()=>Se,\"std.smoothstep\":()=>be}})&&e.f)({})).$name(\"tiltShiftBlurAmount\");function fse(){return gt({angle:n,centerX:n,centerY:n,width:n,falloff:n,aspect:n,maxRadius:n})}function vse(){return gt({angle:n,centerX:n,centerY:n,width:n,falloff:n,aspect:n,...np,...y_})}function hse(e,t){let s=fse(),r=(globalThis.__TYPEGPU_AUTONAME__??(o=>o))(w.bindGroupLayout({blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:s}}),\"layout\");return{layout:r,kernel:w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=r.$.params,c=__tsover_div(__tsover_add(n(a),.5),e),d=__tsover_div(__tsover_add(n(i),.5),t),u=Km(l.angle,m(l.centerX,l.centerY),l.width,l.falloff,m(c,d),l.aspect),p=__tsover_mul(u,l.maxRadius);lt(r.$.blurMap,fe(a,i),S(p,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"blurAmount\",[6,\"tiltShiftBlurAmount\",[[7,\"p\",\"angle\"],[6,\"d.vec2f\",[[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"]]],[7,\"p\",\"width\"],[7,\"p\",\"falloff\"],[6,\"d.vec2f\",[\"u\",\"v\"]],[7,\"p\",\"aspect\"]]]],[13,\"radius\",[1,\"blurAmount\",\"*\",[7,\"p\",\"maxRadius\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>r,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,tiltShiftBlurAmount:()=>Km,\"d.vec2f\":()=>m,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(\"tiltShiftFillBlurMap\"),Params:s}}function gse(e,t,s){let r=vse(),o=(globalThis.__TYPEGPU_AUTONAME__??(i=>i))(w.bindGroupLayout({source:{texture:Zt(n)},blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:r}}),\"layout\"),a=b_(s);return{layout:o,kernel:w.fn([M,M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c)=>{\"use gpu\";let d=o.$.params,u=__tsover_div(__tsover_add(n(l),.5),e),p=__tsover_div(__tsover_add(n(c),.5),t),_=Km(d.angle,m(d.centerX,d.centerY),d.width,d.falloff,m(u,p),d.aspect),f=G(j(Ut(__tsover_mul(u,d.inputWidth))),0,j(__tsover_sub(d.inputWidth,1))),h=G(j(Ut(__tsover_mul(p,d.inputHeight))),0,j(__tsover_sub(d.inputHeight,1))),g=Ei(a(Ye(o.$.source,fe(M(f),M(h)),0)),d.inputMin,d.inputMax,d.outputMin,d.outputMax,d.curve),y=__tsover_mul(_,__tsover_mul(g,.36));lt(o.$.blurMap,fe(l,c),S(y,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"blurAmount\",[6,\"tiltShiftBlurAmount\",[[7,\"p\",\"angle\"],[6,\"d.vec2f\",[[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"]]],[7,\"p\",\"width\"],[7,\"p\",\"falloff\"],[6,\"d.vec2f\",[\"u\",\"v\"]],[7,\"p\",\"aspect\"]]]],[13,\"srcX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",[7,\"p\",\"inputWidth\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",[7,\"p\",\"inputHeight\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]]]],[13,\"sample\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"source\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"srcX\"]],[6,\"d.u32\",[\"srcY\"]]]],[5,\"0\"]]]],[13,\"remapped\",[6,\"applyRemapWindow\",[[6,\"channelScalar\",[\"sample\"]],[7,\"p\",\"inputMin\"],[7,\"p\",\"inputMax\"],[7,\"p\",\"outputMin\"],[7,\"p\",\"outputMax\"],[7,\"p\",\"curve\"]]]],[13,\"radius\",[1,\"blurAmount\",\"*\",[1,\"remapped\",\"*\",\"INTENSITY_TO_RADIUS\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>o,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,tiltShiftBlurAmount:()=>Km,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,applyRemapWindow:()=>Ei,channelScalar:()=>a,INTENSITY_TO_RADIUS:()=>.36,\"std.textureStore\":()=>lt,\"d.vec4f\":()=>S}})&&i.f)({})).$name(\"tiltShiftFillBlurMapVariable\"),Params:r}}var yse=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=t.xyz.add(s.xyz.mul(r)),a=G(__tsover_mul(s.w,r),0,1);return S(o,__tsover_add(t.w,__tsover_mul(a,__tsover_sub(1,t.w))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"original\"},{type:\"i\",name:\"bloom\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"finalColor\",[6,[7,[7,\"original\",\"xyz\"],\"add\"],[[6,[7,[7,\"bloom\",\"xyz\"],\"mul\"],[\"intensity\"]]]]],[13,\"glowAlpha\",[6,\"std.clamp\",[[1,[7,\"bloom\",\"w\"],\"*\",\"intensity\"],[5,\"0\"],[5,\"1\"]]]],[13,\"finalAlpha\",[1,[7,\"original\",\"w\"],\"+\",[1,\"glowAlpha\",\"*\",[1,[5,\"1\"],\"-\",[7,\"original\",\"w\"]]]]],[10,[6,\"d.vec4f\",[\"finalColor\",\"finalAlpha\"]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"glowCompose\"),bse=(e,t)=>k4(e,t,\"glowExtractAndFill\"),xse=(e,t,s)=>ose(e,t,\"glowExtractAndFillVariable\",s),Ase=Math.PI*(3-Math.sqrt(5)),Z3=Math.PI*2,wse=.8,Tse=e=>e*wse,vS=.35,hS=.6,Sse=8,Pse=.25,Tv=.5,Sv=1.4,R4=\"rgba16float\",Cse=e=>{let t=Math.round(e);return t>=3?t:5},Mse=(e,t)=>{if(t<3)return 1;let s=Z3/t,r=e-s*Math.floor(e/s+.5);return Math.cos(s*.5)/Math.cos(r)},Ese=Math.SQRT2/4,kse=(e,t)=>{let s=1.3333333333333333,r=Math.abs(e)/s,o=-t/s+.6;if(r+o>1){let u=r-.25,p=o-.75;return(Math.hypot(u,p)-Ese)*s}let a=o-1,i=r*r+a*a,l=Math.max(r+o,0)*.5,c=r-l,d=o-l;return Math.sqrt(Math.min(i,c*c+d*d))*Math.sign(r-o)*s},Bse=(e,t,s,r)=>{let o=Math.hypot(e,t),a=Math.atan2(t,e),i=Z3/s,l=a-Math.floor(a/i+.5)*i,c=Math.abs(o*Math.sin(l)),d=o*Math.cos(l),u=Math.PI/s,p=r*Math.sin(u),_=r*Math.cos(u)-1,f=c,h=d-1,g=Math.min(Math.max((f*p+h*_)/(p*p+_*_),0),1),y=c-p*g,b=d-(1+_*g);return Math.hypot(y,b)*Math.sign(p*h-_*f)},zse=(e,t,s,r)=>{let o=Math.atan2(t,e),a=Math.hypot(e,t),i=o*s/Z3,l=i-Math.floor(i),c=Math.abs(l*2-1);return a-(r+(1-r)*c)},gS=.92,yS=.3,Rse=.08,Dse=(e,t)=>{let s=Math.abs(e),r=Math.abs(t),o=Math.max(s-gS,r-yS),a=Math.max(r-gS,s-yS);return Math.min(o,a)-Rse},Lse=.72,Use=.28,Ose=(e,t)=>Math.abs(Math.hypot(e,t)-Lse)-Use,bS=(e,t)=>{let s=1,r=0,o=e;for(;o>0;)s/=t,r+=s*(o%t),o=Math.floor(o/t);return r},xS=(e,t)=>[-t,e],Gse={star:(e,t,s)=>{let[r,o]=xS(e,t);return Bse(r,o,s,.5)},flower:(e,t,s)=>{let[r,o]=xS(e,t);return zse(r,o,s,.55)},heart:(e,t)=>kse(e,t),cross:(e,t)=>Dse(e,t),ring:(e,t)=>Ose(e,t)};function D4(e,t,s){let r=[],o=Gse[e];if(!o){let d=e===\"circle\"?0:t;for(let u=0;u<s;u++){let p=(u+.5)/s,_=Math.sqrt(p),f=u*Ase,h=_*Mse(f,d);r.push({x:-Math.cos(f)*h,y:-Math.sin(f)*h,rim:1+hS*Math.pow(_,Sse)})}return r}let a=Cse(t),i=(d,u)=>o(d,u,a),l=2e4;for(let d=1;r.length<s&&d<=l;d++){let u=bS(d,2)*2-1,p=bS(d,3)*2-1,_=i(u,p);if(_>0)continue;let f=Math.min(Math.max(1+_/Pse,0),1);r.push({x:-u,y:-p,rim:1+hS*f*f})}if(r.length===0)return D4(\"circle\",0,s);for(let d=0;r.length<s;d++)r.push(r[d%r.length]);let c=r.reduce((d,u)=>Math.max(d,Math.hypot(u.x,u.y)),0);if(c>1){let d=1/c;for(let u of r)u.x*=d,u.y*=d}return r}function Fse(){return gt({radius:n,highlightGain:n,highlightThreshold:n,rotCos:n,rotSin:n,chromaticFringe:n,catEye:n,inputWidth:n,inputHeight:n})}function Ise(){return gt({highlightGain:n,highlightThreshold:n,rotCos:n,rotSin:n,chromaticFringe:n,catEye:n,...np,...y_})}var Pv=w.fn([S,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Se(t.xyz,A(.2126,.7152,.0722)),i=be(s,__tsover_add(s,vS),a);return __tsover_mul(__tsover_add(1,__tsover_mul(__tsover_mul(r,i),i)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"threshold\"},{type:\"i\",name:\"gain\"},{type:\"i\",name:\"rim\"}],body:[0,[[13,\"lum\",[6,\"std.dot\",[[7,\"base\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"hi\",[6,\"std.smoothstep\",[\"threshold\",[1,\"threshold\",\"+\",\"HIGHLIGHT_KNEE\"],\"lum\"]]],[10,[1,[1,[5,\"1\"],\"+\",[1,[1,\"gain\",\"*\",\"hi\"],\"*\",\"hi\"]],\"*\",\"rim\"]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.smoothstep\":()=>be,HIGHLIGHT_KNEE:()=>vS}})&&e.f)({})).$name(\"bokehHighlightWeight\");function Yse(e,t,s){let r=s,o=Fse(),a=Fe(S,r),i=(globalThis.__TYPEGPU_AUTONAME__??(l=>l))(w.bindGroupLayout({input:{texture:Zt(n)},output:{storageTexture:zt(R4,\"write-only\")},params:{uniform:o},taps:{uniform:a}}),\"layout\");return{layout:i,kernel:w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";let u=i.$.params,p=u.radius,_=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(c),.5),u.inputWidth),e),.5),f=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(d),.5),u.inputHeight),t),.5),h=j(__tsover_sub(u.inputWidth,1)),g=j(__tsover_sub(u.inputHeight,1));if(p<Tv){let y=G(j(Ut(_)),0,h),b=G(j(Ut(f)),0,g),x=Ye(i.$.input,fe(M(y),M(b)),0);lt(i.$.output,fe(c,d),x)}else{let y=u.highlightGain,b=u.highlightThreshold,x=u.chromaticFringe,P=u.rotCos,C=u.rotSin,E=__tsover_div(u.inputWidth,u.inputHeight),R=__tsover_mul(__tsover_sub(__tsover_div(__tsover_add(n(c),.5),e),.5),E),U=__tsover_sub(__tsover_div(__tsover_add(n(d),.5),t),.5),L=Ue(__tsover_add(__tsover_mul(R,R),__tsover_mul(U,U))),k=Ue(__tsover_add(__tsover_mul(__tsover_mul(.25,E),E),.25)),D=__tsover_div(__tsover_mul(__tsover_mul(u.catEye,__tsover_div(L,k)),Sv),B(L,1e-4)),O=m(__tsover_mul(R,D),__tsover_mul(U,D)),F=A(0,0,0),q=n(0),Y=n(0);for(let X=0;X<r;X++){let Q=i.$.taps[X],te=__tsover_sub(__tsover_mul(Q.x,P),__tsover_mul(Q.y,C)),J=__tsover_add(__tsover_mul(Q.x,C),__tsover_mul(Q.y,P)),oe=m(__tsover_mul(te,p),__tsover_mul(J,p)),le=G(j(Ut(__tsover_add(_,oe.x))),0,h),se=G(j(Ut(__tsover_add(f,oe.y))),0,g),Te=Ye(i.$.input,fe(M(le),M(se)),0),de=Te.x,xe=Te.z;if(x>.001){let ze=oe.mul(__tsover_add(1,__tsover_mul(x,.5))),Ie=G(j(Ut(__tsover_add(_,ze.x))),0,h),ke=G(j(Ut(__tsover_add(f,ze.y))),0,g);de=Ye(i.$.input,fe(M(Ie),M(ke)),0).x;let Me=oe.mul(__tsover_sub(1,__tsover_mul(x,.5))),we=G(j(Ut(__tsover_add(_,Me.x))),0,h),Ve=G(j(Ut(__tsover_add(f,Me.y))),0,g);xe=Ye(i.$.input,fe(M(we),M(Ve)),0).z}let Ae=A(de,Te.y,xe),De=__tsover_sub(1,be(1.02,1.12,he(m(te,J).add(O)))),Ee=__tsover_mul(Pv(Te,b,y,Q.z),De);F=F.add(Ae.mul(Ee)),q=__tsover_add(q,__tsover_mul(Te.w,Ee)),Y=__tsover_add(Y,Ee)}let V=__tsover_div(1,B(Y,1e-4));lt(i.$.output,fe(c,d),S(F.mul(V),__tsover_mul(q,V)))}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"radius\",[7,\"p\",\"radius\"]],[13,\"inputCx\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputWidth\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]]],[13,\"inputCy\",[1,[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputHeight\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"inputWm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[11,[1,\"radius\",\"<\",\"PASSTHROUGH_RADIUS\"],[0,[[13,\"sx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCx\"]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"sy\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCy\"]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"gain\",[7,\"p\",\"highlightGain\"]],[13,\"threshold\",[7,\"p\",\"highlightThreshold\"]],[13,\"fringe\",[7,\"p\",\"chromaticFringe\"]],[13,\"rotC\",[7,\"p\",\"rotCos\"]],[13,\"rotS\",[7,\"p\",\"rotSin\"]],[13,\"aspect\",[1,[7,\"p\",\"inputWidth\"],\"/\",[7,\"p\",\"inputHeight\"]]],[13,\"vu\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"]],[13,\"vv\",[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"vLen\",[6,\"std.sqrt\",[[1,[1,\"vu\",\"*\",\"vu\"],\"+\",[1,\"vv\",\"*\",\"vv\"]]]]],[13,\"vMax\",[6,\"std.sqrt\",[[1,[1,[1,[5,\"0.25\"],\"*\",\"aspect\"],\"*\",\"aspect\"],\"+\",[5,\"0.25\"]]]]],[13,\"shift\",[1,[1,[1,[7,\"p\",\"catEye\"],\"*\",[1,\"vLen\",\"/\",\"vMax\"]],\"*\",\"BOKEH_CATEYE_SHIFT\"],\"/\",[6,\"std.max\",[\"vLen\",[5,\"0.0001\"]]]]],[13,\"barrelShift\",[6,\"d.vec2f\",[[1,\"vu\",\"*\",\"shift\"],[1,\"vv\",\"*\",\"shift\"]]]],[12,\"accum\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[12,\"alphaAccum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"wSum\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"TAPS\"],[102,\"++\",\"i\"],[0,[[13,\"tap\",[8,[7,[7,\"layout\",\"$\"],\"taps\"],\"i\"]],[13,\"ox\",[1,[1,[7,\"tap\",\"x\"],\"*\",\"rotC\"],\"-\",[1,[7,\"tap\",\"y\"],\"*\",\"rotS\"]]],[13,\"oy\",[1,[1,[7,\"tap\",\"x\"],\"*\",\"rotS\"],\"+\",[1,[7,\"tap\",\"y\"],\"*\",\"rotC\"]]],[13,\"off\",[6,\"d.vec2f\",[[1,\"ox\",\"*\",\"radius\"],[1,\"oy\",\"*\",\"radius\"]]]],[13,\"sx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"off\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"sy\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"off\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"base\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]]],[12,\"colR\",[7,\"base\",\"x\"]],[12,\"colB\",[7,\"base\",\"z\"]],[11,[1,\"fringe\",\">\",[5,\"0.001\"]],[0,[[13,\"offR\",[6,[7,\"off\",\"mul\"],[[1,[5,\"1\"],\"+\",[1,\"fringe\",\"*\",[5,\"0.5\"]]]]]],[13,\"rx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"offR\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"ry\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"offR\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[2,\"colR\",\"=\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"rx\"]],[6,\"d.u32\",[\"ry\"]]]],[5,\"0\"]]],\"x\"]],[13,\"offB\",[6,[7,\"off\",\"mul\"],[[1,[5,\"1\"],\"-\",[1,\"fringe\",\"*\",[5,\"0.5\"]]]]]],[13,\"bx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"offB\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"by\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"offB\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[2,\"colB\",\"=\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"bx\"]],[6,\"d.u32\",[\"by\"]]]],[5,\"0\"]]],\"z\"]]]]],[13,\"col\",[6,\"d.vec3f\",[\"colR\",[7,\"base\",\"y\"],\"colB\"]]],[13,\"barrel\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"1.02\"],[5,\"1.12\"],[6,\"std.length\",[[6,[7,[6,\"d.vec2f\",[\"ox\",\"oy\"]],\"add\"],[\"barrelShift\"]]]]]]]],[13,\"w\",[1,[6,\"bokehHighlightWeight\",[\"base\",\"threshold\",\"gain\",[7,\"tap\",\"z\"]]],\"*\",\"barrel\"]],[2,\"accum\",\"=\",[6,[7,\"accum\",\"add\"],[[6,[7,\"col\",\"mul\"],[\"w\"]]]]],[2,\"alphaAccum\",\"=\",[1,\"alphaAccum\",\"+\",[1,[7,\"base\",\"w\"],\"*\",\"w\"]]],[2,\"wSum\",\"=\",[1,\"wSum\",\"+\",\"w\"]]]]],[13,\"invW\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[\"wSum\",[5,\"0.0001\"]]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[6,[7,\"accum\",\"mul\"],[\"invW\"]],[1,\"alphaAccum\",\"*\",\"invW\"]]]]]]]]]]},externals:{layout:()=>i,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,\"d.i32\":()=>j,PASSTHROUGH_RADIUS:()=>Tv,\"std.clamp\":()=>G,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"std.textureStore\":()=>lt,\"std.sqrt\":()=>Ue,BOKEH_CATEYE_SHIFT:()=>Sv,\"std.max\":()=>B,\"d.vec2f\":()=>m,\"d.vec3f\":()=>A,TAPS:()=>r,\"std.smoothstep\":()=>be,\"std.length\":()=>he,bokehHighlightWeight:()=>Pv,\"d.vec4f\":()=>S}})&&l.f)({})).$name(\"bokehGather\"),Params:o,TapArray:a}}function Wse(e,t,s,r){let o=s,a=Ise(),i=Fe(S,o),l=b_(r),c=(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(w.bindGroupLayout({input:{texture:Zt(n)},source:{texture:Zt(n)},output:{storageTexture:zt(R4,\"write-only\")},params:{uniform:a},taps:{uniform:i}}),\"layout\");return{layout:c,kernel:w.fn([M,M])((d=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(d.f=((u,p)=>{\"use gpu\";let _=c.$.params,f=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(u),.5),_.inputWidth),e),.5),h=__tsover_sub(__tsover_div(__tsover_mul(__tsover_add(n(p),.5),_.inputHeight),t),.5),g=j(__tsover_sub(_.inputWidth,1)),y=j(__tsover_sub(_.inputHeight,1)),b=__tsover_div(__tsover_add(n(u),.5),e),x=__tsover_div(__tsover_add(n(p),.5),t),P=G(j(Ut(__tsover_mul(b,_.inputWidth))),0,g),C=G(j(Ut(__tsover_mul(x,_.inputHeight))),0,y),E=Ei(l(Ye(c.$.source,fe(M(P),M(C)),0)),_.inputMin,_.inputMax,_.outputMin,_.outputMax,_.curve),R=__tsover_mul(E,.8);if(R<Tv){let U=G(j(Ut(f)),0,g),L=G(j(Ut(h)),0,y),k=Ye(c.$.input,fe(M(U),M(L)),0);lt(c.$.output,fe(u,p),k)}else{let U=_.highlightGain,L=_.highlightThreshold,k=_.chromaticFringe,D=_.rotCos,O=_.rotSin,F=__tsover_div(_.inputWidth,_.inputHeight),q=__tsover_mul(__tsover_sub(__tsover_div(__tsover_add(n(u),.5),e),.5),F),Y=__tsover_sub(__tsover_div(__tsover_add(n(p),.5),t),.5),V=Ue(__tsover_add(__tsover_mul(q,q),__tsover_mul(Y,Y))),X=Ue(__tsover_add(__tsover_mul(__tsover_mul(.25,F),F),.25)),Q=__tsover_div(__tsover_mul(__tsover_mul(_.catEye,__tsover_div(V,X)),Sv),B(V,1e-4)),te=m(__tsover_mul(q,Q),__tsover_mul(Y,Q)),J=A(0,0,0),oe=n(0),le=n(0);for(let Te=0;Te<o;Te++){let de=c.$.taps[Te],xe=__tsover_sub(__tsover_mul(de.x,D),__tsover_mul(de.y,O)),Ae=__tsover_add(__tsover_mul(de.x,O),__tsover_mul(de.y,D)),De=m(__tsover_mul(xe,R),__tsover_mul(Ae,R)),Ee=G(j(Ut(__tsover_add(f,De.x))),0,g),ze=G(j(Ut(__tsover_add(h,De.y))),0,y),Ie=Ye(c.$.input,fe(M(Ee),M(ze)),0),ke=Ie.x,Me=Ie.z;if(k>.001){let st=De.mul(__tsover_add(1,__tsover_mul(k,.5))),pt=G(j(Ut(__tsover_add(f,st.x))),0,g),Ze=G(j(Ut(__tsover_add(h,st.y))),0,y);ke=Ye(c.$.input,fe(M(pt),M(Ze)),0).x;let nt=De.mul(__tsover_sub(1,__tsover_mul(k,.5))),ht=G(j(Ut(__tsover_add(f,nt.x))),0,g),yt=G(j(Ut(__tsover_add(h,nt.y))),0,y);Me=Ye(c.$.input,fe(M(ht),M(yt)),0).z}let we=A(ke,Ie.y,Me),Ve=__tsover_sub(1,be(1.02,1.12,he(m(xe,Ae).add(te)))),qe=__tsover_mul(Pv(Ie,L,U,de.z),Ve);J=J.add(we.mul(qe)),oe=__tsover_add(oe,__tsover_mul(Ie.w,qe)),le=__tsover_add(le,qe)}let se=__tsover_div(1,B(le,1e-4));lt(c.$.output,fe(u,p),S(J.mul(se),__tsover_mul(oe,se)))}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"inputCx\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputWidth\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]]],[13,\"inputCy\",[1,[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"*\",[7,\"p\",\"inputHeight\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"inputWm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"inputHm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"srcX\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",[7,\"p\",\"inputWidth\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"srcY\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",[7,\"p\",\"inputHeight\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"mapSample\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"source\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"srcX\"]],[6,\"d.u32\",[\"srcY\"]]]],[5,\"0\"]]]],[13,\"remapped\",[6,\"applyRemapWindow\",[[6,\"channelScalar\",[\"mapSample\"]],[7,\"p\",\"inputMin\"],[7,\"p\",\"inputMax\"],[7,\"p\",\"outputMin\"],[7,\"p\",\"outputMax\"],[7,\"p\",\"curve\"]]]],[13,\"radius\",[1,\"remapped\",\"*\",\"BOKEH_RADIUS_SCALE\"]],[11,[1,\"radius\",\"<\",\"PASSTHROUGH_RADIUS\"],[0,[[13,\"sx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCx\"]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"sy\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[\"inputCy\"]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"texel\"]]]],[0,[[13,\"gain\",[7,\"p\",\"highlightGain\"]],[13,\"threshold\",[7,\"p\",\"highlightThreshold\"]],[13,\"fringe\",[7,\"p\",\"chromaticFringe\"]],[13,\"rotC\",[7,\"p\",\"rotCos\"]],[13,\"rotS\",[7,\"p\",\"rotSin\"]],[13,\"aspect\",[1,[7,\"p\",\"inputWidth\"],\"/\",[7,\"p\",\"inputHeight\"]]],[13,\"vu\",[1,[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeWidth\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"]],[13,\"vv\",[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"],\"-\",[5,\"0.5\"]]],[13,\"vLen\",[6,\"std.sqrt\",[[1,[1,\"vu\",\"*\",\"vu\"],\"+\",[1,\"vv\",\"*\",\"vv\"]]]]],[13,\"vMax\",[6,\"std.sqrt\",[[1,[1,[1,[5,\"0.25\"],\"*\",\"aspect\"],\"*\",\"aspect\"],\"+\",[5,\"0.25\"]]]]],[13,\"shift\",[1,[1,[1,[7,\"p\",\"catEye\"],\"*\",[1,\"vLen\",\"/\",\"vMax\"]],\"*\",\"BOKEH_CATEYE_SHIFT\"],\"/\",[6,\"std.max\",[\"vLen\",[5,\"0.0001\"]]]]],[13,\"barrelShift\",[6,\"d.vec2f\",[[1,\"vu\",\"*\",\"shift\"],[1,\"vv\",\"*\",\"shift\"]]]],[12,\"accum\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[12,\"alphaAccum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"wSum\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"TAPS\"],[102,\"++\",\"i\"],[0,[[13,\"tap\",[8,[7,[7,\"layout\",\"$\"],\"taps\"],\"i\"]],[13,\"ox\",[1,[1,[7,\"tap\",\"x\"],\"*\",\"rotC\"],\"-\",[1,[7,\"tap\",\"y\"],\"*\",\"rotS\"]]],[13,\"oy\",[1,[1,[7,\"tap\",\"x\"],\"*\",\"rotS\"],\"+\",[1,[7,\"tap\",\"y\"],\"*\",\"rotC\"]]],[13,\"off\",[6,\"d.vec2f\",[[1,\"ox\",\"*\",\"radius\"],[1,\"oy\",\"*\",\"radius\"]]]],[13,\"sx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"off\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"sy\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"off\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[13,\"base\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]]],[12,\"colR\",[7,\"base\",\"x\"]],[12,\"colB\",[7,\"base\",\"z\"]],[11,[1,\"fringe\",\">\",[5,\"0.001\"]],[0,[[13,\"offR\",[6,[7,\"off\",\"mul\"],[[1,[5,\"1\"],\"+\",[1,\"fringe\",\"*\",[5,\"0.5\"]]]]]],[13,\"rx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"offR\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"ry\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"offR\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[2,\"colR\",\"=\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"rx\"]],[6,\"d.u32\",[\"ry\"]]]],[5,\"0\"]]],\"x\"]],[13,\"offB\",[6,[7,\"off\",\"mul\"],[[1,[5,\"1\"],\"-\",[1,\"fringe\",\"*\",[5,\"0.5\"]]]]]],[13,\"bx\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCx\",\"+\",[7,\"offB\",\"x\"]]]]]],[5,\"0\"],\"inputWm1\"]]],[13,\"by\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"inputCy\",\"+\",[7,\"offB\",\"y\"]]]]]],[5,\"0\"],\"inputHm1\"]]],[2,\"colB\",\"=\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"bx\"]],[6,\"d.u32\",[\"by\"]]]],[5,\"0\"]]],\"z\"]]]]],[13,\"col\",[6,\"d.vec3f\",[\"colR\",[7,\"base\",\"y\"],\"colB\"]]],[13,\"barrel\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"1.02\"],[5,\"1.12\"],[6,\"std.length\",[[6,[7,[6,\"d.vec2f\",[\"ox\",\"oy\"]],\"add\"],[\"barrelShift\"]]]]]]]],[13,\"w\",[1,[6,\"bokehHighlightWeight\",[\"base\",\"threshold\",\"gain\",[7,\"tap\",\"z\"]]],\"*\",\"barrel\"]],[2,\"accum\",\"=\",[6,[7,\"accum\",\"add\"],[[6,[7,\"col\",\"mul\"],[\"w\"]]]]],[2,\"alphaAccum\",\"=\",[1,\"alphaAccum\",\"+\",[1,[7,\"base\",\"w\"],\"*\",\"w\"]]],[2,\"wSum\",\"=\",[1,\"wSum\",\"+\",\"w\"]]]]],[13,\"invW\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[\"wSum\",[5,\"0.0001\"]]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"output\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[6,[7,\"accum\",\"mul\"],[\"invW\"]],[1,\"alphaAccum\",\"*\",\"invW\"]]]]]]]]]]},externals:{layout:()=>c,\"d.f32\":()=>n,computeWidth:()=>e,computeHeight:()=>t,\"d.i32\":()=>j,\"std.clamp\":()=>G,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,applyRemapWindow:()=>Ei,channelScalar:()=>l,BOKEH_RADIUS_SCALE:()=>.8,PASSTHROUGH_RADIUS:()=>Tv,\"std.textureStore\":()=>lt,\"std.sqrt\":()=>Ue,BOKEH_CATEYE_SHIFT:()=>Sv,\"std.max\":()=>B,\"d.vec2f\":()=>m,\"d.vec3f\":()=>A,TAPS:()=>o,\"std.smoothstep\":()=>be,\"std.length\":()=>he,bokehHighlightWeight:()=>Pv,\"d.vec4f\":()=>S}})&&d.f)({})).$name(\"bokehGatherVariable\"),Params:a,TapArray:i}}var Vu=bt,L4=rse(32,.8),U4=w.fn([m,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(s,Vu),l=__tsover_div(o.x,o.y),c=__tsover_div(pe(i),l),d=ae(i),u=__tsover_mul(__tsover_div(__tsover_mul(c,r),o.x),2),p=__tsover_mul(__tsover_div(__tsover_mul(d,r),o.y),2);return m(__tsover_add(t.x,__tsover_mul(u,a)),__tsover_add(t.y,__tsover_mul(p,a)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"tapT\"}],body:[0,[[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"dirX\",[1,[6,\"std.cos\",[\"angleRad\"]],\"/\",\"aspect\"]],[13,\"dirY\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"bvX\",[1,[1,[1,\"dirX\",\"*\",\"intensity\"],\"/\",[7,\"viewport\",\"x\"]],\"*\",[5,\"2\"]]],[13,\"bvY\",[1,[1,[1,\"dirY\",\"*\",\"intensity\"],\"/\",[7,\"viewport\",\"y\"]],\"*\",[5,\"2\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,\"bvX\",\"*\",\"tapT\"]],[1,[7,\"uv\",\"y\"],\"+\",[1,\"bvY\",\"*\",\"tapT\"]]]]]]]},externals:{DEG_TO_RAD:()=>Vu,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),O4=w.fn([m,n,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(t.x,__tsover_sub(1,t.y)),l=__tsover_div(__tsover_mul(s,.005),31),c=__tsover_mul(__tsover_sub(a,15.5),l),d=pe(c),u=ae(c),p=__tsover_sub(r.x,i.x),_=__tsover_sub(r.y,i.y),f=__tsover_mul(p,o),h=_,g=__tsover_sub(__tsover_mul(f,d),__tsover_mul(h,u)),y=__tsover_add(__tsover_mul(f,u),__tsover_mul(h,d));return m(__tsover_add(__tsover_div(g,o),i.x),__tsover_add(y,i.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"tapIndex\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"angleStep\",[1,[1,\"intensity\",\"*\",[5,\"0.005\"]],\"/\",[5,\"31\"]]],[13,\"angle\",[1,[1,\"tapIndex\",\"-\",[5,\"15.5\"]],\"*\",\"angleStep\"]],[13,\"cosA\",[6,\"std.cos\",[\"angle\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angle\"]]],[13,\"initX\",[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]]],[13,\"initY\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"acX\",[1,\"initX\",\"*\",\"aspect\"]],[13,\"acY\",\"initY\"],[13,\"rotX\",[1,[1,\"acX\",\"*\",\"cosA\"],\"-\",[1,\"acY\",\"*\",\"sinA\"]]],[13,\"rotY\",[1,[1,\"acX\",\"*\",\"sinA\"],\"+\",[1,\"acY\",\"*\",\"cosA\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"rotX\",\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,\"rotY\",\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),G4=w.fn([m,n,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(t.x,__tsover_sub(1,t.y)),l=__tsover_mul(s,.01),c=__tsover_add(1,__tsover_mul(l,__tsover_div(a,31))),d=__tsover_sub(r.x,i.x),u=__tsover_sub(r.y,i.y),p=__tsover_mul(d,o),_=u,f=__tsover_div(p,c),h=__tsover_div(_,c);return m(__tsover_add(__tsover_div(f,o),i.x),__tsover_add(h,i.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"tapIndex\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"radius\",[1,\"intensity\",\"*\",[5,\"0.01\"]]],[13,\"scale\",[1,[5,\"1\"],\"+\",[1,\"radius\",\"*\",[1,\"tapIndex\",\"/\",[5,\"31\"]]]]],[13,\"deltaX\",[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]]],[13,\"deltaY\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"acdX\",[1,\"deltaX\",\"*\",\"aspect\"]],[13,\"acdY\",\"deltaY\"],[13,\"scaledDeltaX\",[1,\"acdX\",\"/\",\"scale\"]],[13,\"scaledDeltaY\",[1,\"acdY\",\"/\",\"scale\"]],[10,[6,\"d.vec2f\",[[1,[1,\"scaledDeltaX\",\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,\"scaledDeltaY\",\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({}));function qse(e,t,s){let{focus:r,amount:o,uv:a,aspect:i,viewportSize:l}=t;return qb({weights:L4,tapCoord:e===\"linear\"?c=>W(U4,\"linearBlurTapCoord\",[a,r,o,l,$e(c/31-.5)]):e===\"orbit\"?c=>W(O4,\"angularBlurTapCoord\",[r,o,a,i,$e(c)]):c=>W(G4,\"zoomBlurTapCoord\",[r,o,a,i,$e(c)]),sample:s})}function Vse(e){let t=s=>{let r=e*6+s,o=r-6*Math.floor(r/6),a=Math.min(1,Math.max(0,Math.abs(o-3)-1));return a*a*(3-2*a)};return[t(0),t(4),t(2)]}var Nse=.75,F4=Array.from({length:32},(e,t)=>{let[s,r,o]=Vse(t/31*Nse);return[s-1,r-1,o-1]}),$se=[...F4].reverse(),S0=.3,Xse=w.fn([m,n,m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(t.x,__tsover_sub(1,t.y)),c=__tsover_mul(s,.01),d=__tsover_div(a,31),u=__tsover_add(1,__tsover_mul(c,d)),p=__tsover_mul(__tsover_mul(__tsover_mul(i,c),d),1.2),_=pe(p),f=ae(p),h=__tsover_mul(__tsover_sub(r.x,l.x),o),g=__tsover_sub(r.y,l.y),y=__tsover_sub(__tsover_mul(h,_),__tsover_mul(g,f)),b=__tsover_add(__tsover_mul(h,f),__tsover_mul(g,_));return m(__tsover_add(__tsover_div(__tsover_div(y,u),o),l.x),__tsover_add(__tsover_div(b,u),l.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"tapIndex\"},{type:\"i\",name:\"spiral\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"radius\",[1,\"intensity\",\"*\",[5,\"0.01\"]]],[13,\"along\",[1,\"tapIndex\",\"/\",[5,\"31\"]]],[13,\"scale\",[1,[5,\"1\"],\"+\",[1,\"radius\",\"*\",\"along\"]]],[13,\"angle\",[1,[1,[1,\"spiral\",\"*\",\"radius\"],\"*\",\"along\"],\"*\",\"MOTION_BLUR_SPIRAL_TWIST\"]],[13,\"cosA\",[6,\"std.cos\",[\"angle\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angle\"]]],[13,\"acdX\",[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]],\"*\",\"aspect\"]],[13,\"acdY\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"rotX\",[1,[1,\"acdX\",\"*\",\"cosA\"],\"-\",[1,\"acdY\",\"*\",\"sinA\"]]],[13,\"rotY\",[1,[1,\"acdX\",\"*\",\"sinA\"],\"+\",[1,\"acdY\",\"*\",\"cosA\"]]],[10,[6,\"d.vec2f\",[[1,[1,[1,\"rotX\",\"/\",\"scale\"],\"/\",\"aspect\"],\"+\",[7,\"centerPos\",\"x\"]],[1,[1,\"rotY\",\"/\",\"scale\"],\"+\",[7,\"centerPos\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m,MOTION_BLUR_SPIRAL_TWIST:()=>1.2,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),Hse=w.fn([m,m,n,n,n,n,n,m],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=m(s.x,__tsover_sub(1,s.y)),u=__tsover_mul(__tsover_sub(t.x,d.x),r),p=__tsover_sub(t.y,d.y),_=Ue(__tsover_add(__tsover_mul(u,u),__tsover_mul(p,p))),f=__tsover_mul(B(d.x,__tsover_sub(1,d.x)),r),h=B(d.y,__tsover_sub(1,d.y)),g=B(Ue(__tsover_add(__tsover_mul(f,f),__tsover_mul(h,h))),1e-4),y=G(__tsover_div(_,g),0,1),b=K(n(1),__tsover_mul(y,y),a),x=__tsover_mul(o,b),P=B(x,0),C=B(__tsover_mul(x,-1),0),E=be(__tsover_mul(i,__tsover_sub(1,S0)),__tsover_add(__tsover_mul(i,__tsover_add(1,S0)),1e-4),y),R=K(n(1),E,Et(1e-4,i)),U=m(__tsover_mul(t.x,c.x),__tsover_mul(t.y,c.y)),L=Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(U.x,.06711056),__tsover_mul(U.y,.00583715)))));return S(P,C,R,__tsover_mul(__tsover_sub(L,.5),l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"dispersion\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"focus\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"centerPos\",[6,\"d.vec2f\",[[7,\"center\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]],\"*\",\"aspect\"]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[13,\"farX\",[1,[6,\"std.max\",[[7,\"centerPos\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"centerPos\",\"x\"]]]],\"*\",\"aspect\"]],[13,\"farY\",[6,\"std.max\",[[7,\"centerPos\",\"y\"],[1,[5,\"1\"],\"-\",[7,\"centerPos\",\"y\"]]]]],[13,\"dMax\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"farX\",\"*\",\"farX\"],\"+\",[1,\"farY\",\"*\",\"farY\"]]]],[5,\"0.0001\"]]]],[13,\"dNorm\",[6,\"std.clamp\",[[1,\"dist\",\"/\",\"dMax\"],[5,\"0\"],[5,\"1\"]]]],[13,\"ramp\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],[1,\"dNorm\",\"*\",\"dNorm\"],\"falloff\"]]],[13,\"strength\",[1,\"dispersion\",\"*\",\"ramp\"]],[13,\"dPos\",[6,\"std.max\",[\"strength\",[5,\"0\"]]]],[13,\"dNeg\",[6,\"std.max\",[[1,\"strength\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"]]]],[13,\"focusEdge\",[6,\"std.smoothstep\",[[1,\"focus\",\"*\",[1,[5,\"1\"],\"-\",\"FOCUS_SOFTNESS\"]],[1,[1,\"focus\",\"*\",[1,[5,\"1\"],\"+\",\"FOCUS_SOFTNESS\"]],\"+\",[5,\"0.0001\"]],\"dNorm\"]]],[13,\"focusMask\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],\"focusEdge\",[6,\"std.step\",[[5,\"0.0001\"],\"focus\"]]]]],[13,\"px\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",[7,\"viewport\",\"x\"]],[1,[7,\"uv\",\"y\"],\"*\",[7,\"viewport\",\"y\"]]]]],[13,\"ign\",[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[7,\"px\",\"x\"],\"*\",[5,\"0.06711056\"]],\"+\",[1,[7,\"px\",\"y\"],\"*\",[5,\"0.00583715\"]]]]]]]]],[13,\"jitterPhase\",[1,[1,\"ign\",\"-\",[5,\"0.5\"]],\"*\",\"jitter\"]],[10,[6,\"d.vec4f\",[\"dPos\",\"dNeg\",\"focusMask\",\"jitterPhase\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.mix\":()=>K,\"d.f32\":()=>n,\"std.smoothstep\":()=>be,FOCUS_SOFTNESS:()=>S0,\"std.step\":()=>Et,\"std.fract\":()=>Re,\"d.vec4f\":()=>S}})&&e.f)({})),jse=w.fn([S,n,A,A,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Se(t.xyz,A(.2126,.7152,.0722)),c=__tsover_mul(s,__tsover_add(1,__tsover_mul(__tsover_mul(i,l),l))),d=A(1,1,1).add(r.mul(a.x)).add(o.mul(a.y));return S(__tsover_mul(d.x,c),__tsover_mul(d.y,c),__tsover_mul(d.z,c),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sample\"},{type:\"i\",name:\"weight\"},{type:\"i\",name:\"tintForward\"},{type:\"i\",name:\"tintReversed\"},{type:\"i\",name:\"field\"},{type:\"i\",name:\"highlightGain\"}],body:[0,[[13,\"lum\",[6,\"std.dot\",[[7,\"sample\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"boost\",[1,\"weight\",\"*\",[1,[5,\"1\"],\"+\",[1,[1,\"highlightGain\",\"*\",\"lum\"],\"*\",\"lum\"]]]],[13,\"tint\",[6,[7,[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"add\"],[[6,[7,\"tintForward\",\"mul\"],[[7,\"field\",\"x\"]]]]],\"add\"],[[6,[7,\"tintReversed\",\"mul\"],[[7,\"field\",\"y\"]]]]]],[10,[6,\"d.vec4f\",[[1,[7,\"tint\",\"x\"],\"*\",\"boost\"],[1,[7,\"tint\",\"y\"],\"*\",\"boost\"],[1,[7,\"tint\",\"z\"],\"*\",\"boost\"],\"boost\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Kse=w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t.div(B(s,S(1e-4,1e-4,1e-4,1e-4)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorSum\"},{type:\"i\",name:\"weightSum\"}],body:[0,[[10,[6,[7,\"colorSum\",\"div\"],[[6,\"std.max\",[\"weightSum\",[6,\"d.vec4f\",[[5,\"0.0001\"],[5,\"0.0001\"],[5,\"0.0001\"],[5,\"0.0001\"]]]]]]]]]]},externals:{\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&e.f)({}));function AS([e,t,s]){return new fs(()=>`vec3f(${fo(e)}, ${fo(t)}, ${fo(s)})`)}function Qse(e,t,s,r){let{focus:o,uv:a,aspect:i,viewportSize:l}=t,c=ns(W(Hse,\"motionBlurDetailField\",[a,s.center,i,s.dispersion,s.falloff,s.focus,s.jitter,l]),\"mbField\"),d=ns(t.amount.mul(c.member(\"z\")),\"mbAmount\"),u=ns(s.bias,\"mbBias\"),p=c.member(\"w\"),_=ns(s.highlights.mul(12),\"mbHighlight\"),f=31,h=f/2,g=x=>{if(e===\"linear\"){let C=x/f-.5;return W(U4,\"linearBlurTapCoord\",[a,o,d,l,$e(C).add(u.mul(Math.abs(C))).add(p.mul(1/f))])}if(e===\"orbit\")return W(O4,\"angularBlurTapCoord\",[o,d,a,i,$e(x).add(u.mul(Math.abs(x-h))).add(p)]);let P=$e(x).add(u.mul(-h)).add(p);return s.spiral?W(Xse,\"zoomBlurSpiralTapCoord\",[o,d,a,i,P,s.spiral]):W(G4,\"zoomBlurTapCoord\",[o,d,a,i,P])},y,b;for(let x=0;x<32;x++){let P=ns(r(g(x)),\"mbTap\"),C=ns(W(jse,\"motionBlurDetailTapWeight\",[P,$e(L4[x]),AS(F4[x]),AS($se[x]),c,_]),\"mbWeight\"),E=P.mul(C);y=y?y.add(E):E,b=b?b.add(C):C}return W(Kse,\"motionBlurDetailResolve\",[y,b])}var Zse=w.fn([m,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Pr(t.mul(1e3)).mul(2).sub(m(1,1)).mul(s).div(r);return t.add(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"seed\",[6,[7,\"uv\",\"mul\"],[[5,\"1000\"]]]],[13,\"randVec\",[6,\"noise.hash22\",[\"seed\"]]],[13,\"offset\",[6,[7,[6,[7,[6,[7,[6,[7,\"randVec\",\"mul\"],[[5,\"2\"]]],\"sub\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]],\"mul\"],[\"intensity\"]],\"div\"],[\"viewport\"]]],[10,[6,[7,\"uv\",\"add\"],[\"offset\"]]]]]},externals:{\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m}})&&e.f)({})),Jse=w.fn([m,n,m,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=t.mul(1e3),l=Pr(i),c=Pr(i.add(a)),d=l.add(c.sub(l).mul(o)).mul(2).sub(m(1,1)).mul(s).div(r);return t.add(d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"chromatic\"},{type:\"i\",name:\"salt\"}],body:[0,[[13,\"seed\",[6,[7,\"uv\",\"mul\"],[[5,\"1000\"]]]],[13,\"shared\",[6,\"noise.hash22\",[\"seed\"]]],[13,\"own\",[6,\"noise.hash22\",[[6,[7,\"seed\",\"add\"],[\"salt\"]]]]],[13,\"randVec\",[6,[7,\"shared\",\"add\"],[[6,[7,[6,[7,\"own\",\"sub\"],[\"shared\"]],\"mul\"],[\"chromatic\"]]]]],[13,\"offset\",[6,[7,[6,[7,[6,[7,[6,[7,\"randVec\",\"mul\"],[[5,\"2\"]]],\"sub\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]],\"mul\"],[\"intensity\"]],\"div\"],[\"viewport\"]]],[10,[6,[7,\"uv\",\"add\"],[\"offset\"]]]]]},externals:{\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m}})&&e.f)({})),ere=[17.3,91.7],tre=[53.1,29.9];function sre(e){let t=W(Zse,\"diffuseBlurUV\",[e.uv,e.amount,e.viewportSize]),s=e.chromatic;if(!s)return Ha(t,e.sample,e.edgeMode);let r=ns(Ha(t,e.sample,e.edgeMode),\"scatterShared\"),o=l=>{let c=new fs(()=>`vec2f(${fo(l[0])}, ${fo(l[1])})`);return Ha(W(Jse,\"diffuseBlurChannelUV\",[e.uv,e.amount,e.viewportSize,s,c]),e.sample,e.edgeMode)},a=o(ere),i=o(tre);return Ct(a.member(\"r\"),r.member(\"g\"),i.member(\"b\"),r.member(\"a\"))}var rre=[.056,.135,.278,.487,.726,.923,1,.923,.726,.487,.278,.135,.056],ore=6.214,wS=rre.map(e=>e/ore),TS=6,are=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(r,Vu),i=__tsover_mul(o,.2),l=__tsover_div(s.x,s.y),c=__tsover_div(__tsover_mul(ae(a),i),l),d=__tsover_mul(__tsover_mul(pe(a),-1),i);return m(__tsover_sub(t.x,c),__tsover_sub(t.y,d))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"distance\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"dist\",[1,\"distance\",\"*\",[5,\"0.2\"]]],[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"ox\",[1,[1,[6,\"std.sin\",[\"angleRad\"]],\"*\",\"dist\"],\"/\",\"aspect\"]],[13,\"oy\",[1,[1,[6,\"std.cos\",[\"angleRad\"]],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"dist\"]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"ox\"],[1,[7,\"uv\",\"y\"],\"-\",\"oy\"]]]]]]},externals:{DEG_TO_RAD:()=>Vu,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"d.vec2f\":()=>m}})&&e.f)({})),nre=w.fn([m,m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return m(__tsover_add(t.x,__tsover_div(__tsover_mul(o,r),s.x)),__tsover_add(t.y,__tsover_div(__tsover_mul(a,r),s.y)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"blurRadius\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,[1,\"ox\",\"*\",\"blurRadius\"],\"/\",[7,\"viewport\",\"x\"]]],[1,[7,\"uv\",\"y\"],\"+\",[1,[1,\"oy\",\"*\",\"blurRadius\"],\"/\",[7,\"viewport\",\"y\"]]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),ire=w.fn([S,S,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=A(s.x,s.y,s.z),i=__tsover_mul(s.w,r),l=G(__tsover_sub(i,t.w),0,1),c=a.mul(s.w).mul(l),d=a.mul(s.w).mul(r),u=__tsover_sub(1,t.w),p=A(t.x,t.y,t.z).add(d.mul(u)),_=B(__tsover_add(t.w,__tsover_mul(i,u)),t.w),f=o>.5,h=N(p,c,f),g=N(_,l,f);return S(h.x,h.y,h.z,g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"originalColor\"},{type:\"i\",name:\"shadowColor\"},{type:\"i\",name:\"shadowAlpha\"},{type:\"i\",name:\"cutout\"}],body:[0,[[13,\"shadowRgb\",[6,\"d.vec3f\",[[7,\"shadowColor\",\"x\"],[7,\"shadowColor\",\"y\"],[7,\"shadowColor\",\"z\"]]]],[13,\"shadowA\",[1,[7,\"shadowColor\",\"w\"],\"*\",\"shadowAlpha\"]],[13,\"cutoutA\",[6,\"std.clamp\",[[1,\"shadowA\",\"-\",[7,\"originalColor\",\"w\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"cutoutRgb\",[6,[7,[6,[7,\"shadowRgb\",\"mul\"],[[7,\"shadowColor\",\"w\"]]],\"mul\"],[\"cutoutA\"]]],[13,\"shadowPremulRgb\",[6,[7,[6,[7,\"shadowRgb\",\"mul\"],[[7,\"shadowColor\",\"w\"]]],\"mul\"],[\"shadowAlpha\"]]],[13,\"oneMinusOrigA\",[1,[5,\"1\"],\"-\",[7,\"originalColor\",\"w\"]]],[13,\"normalRgb\",[6,[7,[6,\"d.vec3f\",[[7,\"originalColor\",\"x\"],[7,\"originalColor\",\"y\"],[7,\"originalColor\",\"z\"]]],\"add\"],[[6,[7,\"shadowPremulRgb\",\"mul\"],[\"oneMinusOrigA\"]]]]],[13,\"normalA\",[6,\"std.max\",[[1,[7,\"originalColor\",\"w\"],\"+\",[1,\"shadowA\",\"*\",\"oneMinusOrigA\"]],[7,\"originalColor\",\"w\"]]]],[13,\"useCut\",[1,\"cutout\",\">\",[5,\"0.5\"]]],[13,\"rgb\",[6,\"std.select\",[\"normalRgb\",\"cutoutRgb\",\"useCut\"]]],[13,\"a\",[6,\"std.select\",[\"normalA\",\"cutoutA\",\"useCut\"]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"a\"]]]]]},externals:{\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"std.max\":()=>B,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&e.f)({}));function lre(e){let{at:t,uv:s,viewportSize:r,blurRadius:o,sample:a,convertToTexture:i}=e,l=(d,u,p)=>W(nre,\"dropShadowTapUV\",[d,r,o,$e(u),$e(p)]),c=i(Ct(qb({weights:wS,tapCoord:d=>l(t,d-TS,0),sample:a,component:\"a\"}),$e(0),$e(0),$e(1)));return qb({weights:wS,tapCoord:d=>l(s,0,d-TS),sample:d=>c.sample(d),component:\"r\"})}var c1=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return m(__tsover_add(t.x,__tsover_div(r,s.x)),__tsover_add(t.y,__tsover_div(o,s.y)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"dirX\"},{type:\"i\",name:\"dirY\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,\"dirX\",\"/\",[7,\"viewport\",\"x\"]]],[1,[7,\"uv\",\"y\"],\"+\",[1,\"dirY\",\"/\",[7,\"viewport\",\"y\"]]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),cre=w.fn([S,S,S,S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_add(1,__tsover_mul(i,4)),c=__tsover_mul(i,-1),d=G(t.xyz.mul(l).add(s.xyz.mul(c)).add(r.xyz.mul(c)).add(o.xyz.mul(c)).add(a.xyz.mul(c)),A(0,0,0),A(1,1,1));return S(d.x,d.y,d.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"top\"},{type:\"i\",name:\"bottom\"},{type:\"i\",name:\"left\"},{type:\"i\",name:\"right\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"centerWeight\",[1,[5,\"1\"],\"+\",[1,\"amount\",\"*\",[5,\"4\"]]]],[13,\"neighborWeight\",[1,\"amount\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"rgb\",[6,[7,[6,[7,[6,[7,[6,[7,[6,[7,[7,\"center\",\"xyz\"],\"mul\"],[\"centerWeight\"]],\"add\"],[[6,[7,[7,\"top\",\"xyz\"],\"mul\"],[\"neighborWeight\"]]]],\"add\"],[[6,[7,[7,\"bottom\",\"xyz\"],\"mul\"],[\"neighborWeight\"]]]],\"add\"],[[6,[7,[7,\"left\",\"xyz\"],\"mul\"],[\"neighborWeight\"]]]],\"add\"],[[6,[7,[7,\"right\",\"xyz\"],\"mul\"],[\"neighborWeight\"]]]]],[13,\"clamped\",[6,\"std.clamp\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[10,[6,\"d.vec4f\",[[7,\"clamped\",\"x\"],[7,\"clamped\",\"y\"],[7,\"clamped\",\"z\"],[7,\"center\",\"w\"]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({}));function dre(e){let{uv:t,viewportSize:s,amount:r,sample:o}=e;return W(cre,\"sharpnessCompose\",[o(t),o(W(c1,\"sharpnessTapUV\",[t,s,$e(0),$e(1)])),o(W(c1,\"sharpnessTapUV\",[t,s,$e(0),$e(-1)])),o(W(c1,\"sharpnessTapUV\",[t,s,$e(-1),$e(0)])),o(W(c1,\"sharpnessTapUV\",[t,s,$e(1),$e(0)])),r])}var ure=w.fn([m,n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=s>1,l=B(N(__tsover_mul(r,s),r,i),1),c=B(N(r,__tsover_div(r,s),i),1),d=m(__tsover_div(ie(__tsover_mul(t.x,l)),l),__tsover_div(ie(__tsover_mul(t.y,c)),c)),u=m(__tsover_sub(Re(__tsover_mul(t.x,l)),.5),__tsover_sub(Re(__tsover_mul(t.y,c)),.5)),p=__tsover_sub(.5,__tsover_mul(o,.5)),_=__tsover_mul(a,p),f=B(__tsover_add(__tsover_sub(re(u.x),p),_),0),h=B(__tsover_add(__tsover_sub(re(u.y),p),_),0),g=__tsover_sub(he(m(f,h)),_),y=N(n(0),n(1),g<=0);return A(d.x,d.y,y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"gap\"},{type:\"i\",name:\"roundness\"}],body:[0,[[13,\"isWide\",[1,\"aspect\",\">\",[5,\"1\"]]],[13,\"pixelCountX\",[6,\"std.max\",[[6,\"std.select\",[[1,\"scale\",\"*\",\"aspect\"],\"scale\",\"isWide\"]],[5,\"1\"]]]],[13,\"pixelCountY\",[6,\"std.max\",[[6,\"std.select\",[\"scale\",[1,\"scale\",\"/\",\"aspect\"],\"isWide\"]],[5,\"1\"]]]],[13,\"sampleUV\",[6,\"d.vec2f\",[[1,[6,\"std.floor\",[[1,[7,\"uv\",\"x\"],\"*\",\"pixelCountX\"]]],\"/\",\"pixelCountX\"],[1,[6,\"std.floor\",[[1,[7,\"uv\",\"y\"],\"*\",\"pixelCountY\"]]],\"/\",\"pixelCountY\"]]]],[13,\"localUV\",[6,\"d.vec2f\",[[1,[6,\"std.fract\",[[1,[7,\"uv\",\"x\"],\"*\",\"pixelCountX\"]]],\"-\",[5,\"0.5\"]],[1,[6,\"std.fract\",[[1,[7,\"uv\",\"y\"],\"*\",\"pixelCountY\"]]],\"-\",[5,\"0.5\"]]]]],[13,\"halfSize\",[1,[5,\"0.5\"],\"-\",[1,\"gap\",\"*\",[5,\"0.5\"]]]],[13,\"cornerRadius\",[1,\"roundness\",\"*\",\"halfSize\"]],[13,\"qx\",[6,\"std.max\",[[1,[1,[6,\"std.abs\",[[7,\"localUV\",\"x\"]]],\"-\",\"halfSize\"],\"+\",\"cornerRadius\"],[5,\"0\"]]]],[13,\"qy\",[6,\"std.max\",[[1,[1,[6,\"std.abs\",[[7,\"localUV\",\"y\"]]],\"-\",\"halfSize\"],\"+\",\"cornerRadius\"],[5,\"0\"]]]],[13,\"sdf\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"qx\",\"qy\"]]]],\"-\",\"cornerRadius\"]],[13,\"mask\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"sdf\",\"<=\",[5,\"0\"]]]]],[10,[6,\"d.vec3f\",[[7,\"sampleUV\",\"x\"],[7,\"sampleUV\",\"y\"],\"mask\"]]]]]},externals:{\"std.max\":()=>B,\"std.select\":()=>N,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"std.abs\":()=>re,\"std.length\":()=>he,\"d.f32\":()=>n,\"d.vec3f\":()=>A}})&&e.f)({}));function pre(e){let t=W(ure,\"pixelateSample\",[e.uv,e.aspect,e.scale,e.gap,e.roundness]),s=e.sampleStraight(t.member(\"xy\"));return Ct(s.member(\"rgb\"),s.member(\"a\").mul(t.member(\"z\")))}var mre=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=s>1,c=m(N(__tsover_mul(o,s),o,l),N(o,__tsover_div(o,s),l)),d=m(__tsover_mul(t.x,s),t.y),u=__tsover_mul(a,Vu),p=pe(u),_=ae(u),f=d.sub(m(__tsover_mul(.5,s),.5)),h=m(__tsover_sub(__tsover_mul(f.x,p),__tsover_mul(f.y,_)),__tsover_add(__tsover_mul(f.x,_),__tsover_mul(f.y,p))).add(m(__tsover_mul(.5,s),.5)),g=m(__tsover_div(h.x,s),h.y),y=ie(g.mul(c)).div(c),b=g.sub(y),x=m(1,1).div(c),P=b.div(x).sub(m(.5,.5)),C=__tsover_add(__tsover_mul(P.x,P.x),__tsover_mul(P.y,P.y)),E=B(__tsover_sub(1,__tsover_mul(C,__tsover_mul(i,4))),0),R=__tsover_mul(r,.025),U=__tsover_mul(R,E),L=m(__tsover_div(__tsover_mul(P.x,U),s),__tsover_mul(P.y,U));return t.add(L)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"baseTileCount\"},{type:\"i\",name:\"rotationDegrees\"},{type:\"i\",name:\"roundnessAmount\"}],body:[0,[[13,\"isWide\",[1,\"aspect\",\">\",[5,\"1\"]]],[13,\"tileCount\",[6,\"d.vec2f\",[[6,\"std.select\",[[1,\"baseTileCount\",\"*\",\"aspect\"],\"baseTileCount\",\"isWide\"]],[6,\"std.select\",[\"baseTileCount\",[1,\"baseTileCount\",\"/\",\"aspect\"],\"isWide\"]]]]],[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"rotationRadians\",[1,\"rotationDegrees\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"rotationRadians\"]]],[13,\"sinA\",[6,\"std.sin\",[\"rotationRadians\"]]],[13,\"centered\",[6,[7,\"aspectCorrectedUV\",\"sub\"],[[6,\"d.vec2f\",[[1,[5,\"0.5\"],\"*\",\"aspect\"],[5,\"0.5\"]]]]]],[13,\"rotatedX\",[1,[1,[7,\"centered\",\"x\"],\"*\",\"cosA\"],\"-\",[1,[7,\"centered\",\"y\"],\"*\",\"sinA\"]]],[13,\"rotatedY\",[1,[1,[7,\"centered\",\"x\"],\"*\",\"sinA\"],\"+\",[1,[7,\"centered\",\"y\"],\"*\",\"cosA\"]]],[13,\"rotatedUV\",[6,[7,[6,\"d.vec2f\",[\"rotatedX\",\"rotatedY\"]],\"add\"],[[6,\"d.vec2f\",[[1,[5,\"0.5\"],\"*\",\"aspect\"],[5,\"0.5\"]]]]]],[13,\"gridUV\",[6,\"d.vec2f\",[[1,[7,\"rotatedUV\",\"x\"],\"/\",\"aspect\"],[7,\"rotatedUV\",\"y\"]]]],[13,\"cellCoord\",[6,[7,[6,\"std.floor\",[[6,[7,\"gridUV\",\"mul\"],[\"tileCount\"]]]],\"div\"],[\"tileCount\"]]],[13,\"localUV\",[6,[7,\"gridUV\",\"sub\"],[\"cellCoord\"]]],[13,\"oneOverTile\",[6,[7,[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]],\"div\"],[\"tileCount\"]]],[13,\"normalizedLocalUV\",[6,[7,\"localUV\",\"div\"],[\"oneOverTile\"]]],[13,\"fromCenter\",[6,[7,\"normalizedLocalUV\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"distFromCenter\",[1,[1,[7,\"fromCenter\",\"x\"],\"*\",[7,\"fromCenter\",\"x\"]],\"+\",[1,[7,\"fromCenter\",\"y\"],\"*\",[7,\"fromCenter\",\"y\"]]]],[13,\"roundnessFactor\",[1,[5,\"1\"],\"-\",[1,\"distFromCenter\",\"*\",[1,\"roundnessAmount\",\"*\",[5,\"4\"]]]]],[13,\"clampedRoundness\",[6,\"std.max\",[\"roundnessFactor\",[5,\"0\"]]]],[13,\"distortionFactor\",[1,\"intensity\",\"*\",[5,\"0.025\"]]],[13,\"scaleFactor\",[1,\"distortionFactor\",\"*\",\"clampedRoundness\"]],[13,\"distortion\",[6,\"d.vec2f\",[[1,[1,[7,\"fromCenter\",\"x\"],\"*\",\"scaleFactor\"],\"/\",\"aspect\"],[1,[7,\"fromCenter\",\"y\"],\"*\",\"scaleFactor\"]]]],[10,[6,[7,\"uv\",\"add\"],[\"distortion\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.select\":()=>N,DEG_TO_RAD:()=>Vu,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.floor\":()=>ie,\"std.max\":()=>B}})&&e.f)({}));function I4(e){return W(mre,\"glassTilesUV\",[e.uv,e.aspect,e.intensity,e.tileCount,e.rotation,e.roundness])}function Y4(e){let t=I4(e);return W(Yt,\"unpremultiplyAlpha\",[g_(e.texture,t)])}function W4(e){return W(N3,\"edgeClampUV\",[I4(e)])}var SS=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s,.002);return m(__tsover_add(t.x,__tsover_mul(o,r)),t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"colorShift\"},{type:\"i\",name:\"dir\"}],body:[0,[[13,\"scaledColorShift\",[1,\"colorShift\",\"*\",[5,\"0.002\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,\"scaledColorShift\",\"*\",\"dir\"]],[7,\"uv\",\"y\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),_re=w.fn([S,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return A(t.x,s.y,r.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"redSample\"},{type:\"i\",name:\"greenSample\"},{type:\"i\",name:\"blueSample\"}],body:[0,[[10,[6,\"d.vec3f\",[[7,\"redSample\",\"x\"],[7,\"greenSample\",\"y\"],[7,\"blueSample\",\"z\"]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),fre=w.fn([A,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return t.sub(A(.5)).mul(s).add(A(.5)).mul(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"brightness\"}],body:[0,[[10,[6,[7,[6,[7,[6,[7,[6,[7,\"color\",\"sub\"],[[6,\"d.vec3f\",[[5,\"0.5\"]]]]],\"mul\"],[\"contrast\"]],\"add\"],[[6,\"d.vec3f\",[[5,\"0.5\"]]]]],\"mul\"],[\"brightness\"]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})),vre=w.fn([A,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,r),i=ae(__tsover_mul(a,__tsover_mul(3.14159,2))),l=__tsover_sub(1,__tsover_mul(o,__tsover_add(__tsover_mul(i,.5),.5)));return t.mul(l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"scanlineFrequency\"},{type:\"i\",name:\"scanlineIntensity\"}],body:[0,[[13,\"scanlinePosition\",[1,\"uvY\",\"*\",\"scanlineFrequency\"]],[13,\"scanlineWave\",[6,\"std.sin\",[[1,\"scanlinePosition\",\"*\",[1,[5,\"3.14159\"],\"*\",[5,\"2\"]]]]]],[13,\"scanlineEffect\",[1,[5,\"1\"],\"-\",[1,\"scanlineIntensity\",\"*\",[1,[1,\"scanlineWave\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]],[10,[6,[7,\"color\",\"mul\"],[\"scanlineEffect\"]]]]]},externals:{\"std.sin\":()=>ae}})&&e.f)({})),hre=w.fn([A,m,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(r,.5),a=Re(s.mul(o)),i=__tsover_add(__tsover_mul(ae(__tsover_mul(a.x,6.28318)),.1),.95),l=__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(a.x,6.28318),2.09)),.1),.95),c=__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(a.x,6.28318),4.18)),.1),.95);return A(__tsover_mul(t.x,i),__tsover_mul(t.y,l),__tsover_mul(t.z,c))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"pixelSize\"}],body:[0,[[13,\"phosphorScale\",[1,\"pixelSize\",\"*\",[5,\"0.5\"]]],[13,\"pixelPhase\",[6,\"std.fract\",[[6,[7,\"uv\",\"mul\"],[\"phosphorScale\"]]]]],[13,\"redMask\",[1,[1,[6,\"std.sin\",[[1,[7,\"pixelPhase\",\"x\"],\"*\",[5,\"6.28318\"]]]],\"*\",[5,\"0.1\"]],\"+\",[5,\"0.95\"]]],[13,\"greenMask\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"pixelPhase\",\"x\"],\"*\",[5,\"6.28318\"]],\"+\",[5,\"2.09\"]]]],\"*\",[5,\"0.1\"]],\"+\",[5,\"0.95\"]]],[13,\"blueMask\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"pixelPhase\",\"x\"],\"*\",[5,\"6.28318\"]],\"+\",[5,\"4.18\"]]]],\"*\",[5,\"0.1\"]],\"+\",[5,\"0.95\"]]],[10,[6,\"d.vec3f\",[[1,[7,\"color\",\"x\"],\"*\",\"redMask\"],[1,[7,\"color\",\"y\"],\"*\",\"greenMask\"],[1,[7,\"color\",\"z\"],\"*\",\"blueMask\"]]]]]]},externals:{\"std.fract\":()=>Re,\"std.sin\":()=>ae,\"d.vec3f\":()=>A}})&&e.f)({})),gre=w.fn([A,m,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=s.sub(m(.5,.5)),l=he(m(__tsover_mul(i.x,r),i.y)),c=__tsover_sub(1,__tsover_mul(o,.9)),d=__tsover_add(c,.5),u=K(1,__tsover_sub(1,be(c,d,l)),a);return t.mul(u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"vignetteRadius\"},{type:\"i\",name:\"vignetteIntensity\"}],body:[0,[[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"aspectCorrectedUV\",[6,\"d.vec2f\",[[1,[7,\"centeredUV\",\"x\"],\"*\",\"aspect\"],[7,\"centeredUV\",\"y\"]]]],[13,\"vignetteDistance\",[6,\"std.length\",[\"aspectCorrectedUV\"]]],[13,\"innerEdge\",[1,[5,\"1\"],\"-\",[1,\"vignetteRadius\",\"*\",[5,\"0.9\"]]]],[13,\"outerEdge\",[1,\"innerEdge\",\"+\",[5,\"0.5\"]]],[13,\"vignetteValue\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"innerEdge\",\"outerEdge\",\"vignetteDistance\"]]]],[13,\"vignette\",[6,\"std.mix\",[[5,\"1\"],\"vignetteValue\",\"vignetteIntensity\"]]],[10,[6,[7,\"color\",\"mul\"],[\"vignette\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.smoothstep\":()=>be,\"std.mix\":()=>K}})&&e.f)({})),PS=487,dm=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Ys(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"noise.hash12\",[\"p\"]]]]]},externals:{\"noise.hash12\":()=>Ys}})&&e.f)({})),il=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=Re(t),o=r.mul(r).mul(m(3,3).sub(r.mul(2))),a=dm(s),i=dm(s.add(m(1,0))),l=dm(s.add(m(0,1))),c=dm(s.add(m(1,1)));return K(K(a,i,o.x),K(l,c,o.x),o.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f\",[6,\"std.fract\",[\"p\"]]],[13,\"u\",[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,\"d.vec2f\",[[5,\"3\"],[5,\"3\"]]],\"sub\"],[[6,[7,\"f\",\"mul\"],[[5,\"2\"]]]]]]]],[13,\"a\",[6,\"hash2D\",[\"i\"]]],[13,\"b\",[6,\"hash2D\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]]]]]]],[13,\"c\",[6,\"hash2D\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]]]]]]],[13,\"dd\",[6,\"hash2D\",[[6,[7,\"i\",\"add\"],[[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[\"a\",\"b\",[7,\"u\",\"x\"]]],[6,\"std.mix\",[\"c\",\"dd\",[7,\"u\",\"x\"]]],[7,\"u\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"d.vec2f\":()=>m,hash2D:()=>dm,\"std.mix\":()=>K}})&&e.f)({})),CS=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return be(.45,.8,il(m(__tsover_mul(t,.4),0)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[6,\"std.smoothstep\",[[5,\"0.45\"],[5,\"0.8\"],[6,\"smoothNoise2D\",[[6,\"d.vec2f\",[[1,\"t\",\"*\",[5,\"0.4\"]],[5,\"0\"]]]]]]]]]]},externals:{\"std.smoothstep\":()=>be,smoothNoise2D:()=>il,\"d.vec2f\":()=>m}})&&e.f)({})),MS=w.fn([n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=ie(__tsover_mul(t,PS)),i=__tsover_mul(__tsover_sub(Ys(m(a,s)),.5),2),l=__tsover_mul(__tsover_sub(Ys(m(a,__tsover_add(s,69.42))),.5),2),c=__tsover_add(.25,__tsover_mul(o,.75)),d=__tsover_mul(__tsover_mul(__tsover_mul(i,r),.008),c);return m(__tsover_mul(__tsover_mul(__tsover_mul(l,r),.004),c),d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"scanlineNoise\"},{type:\"i\",name:\"burst\"}],body:[0,[[13,\"rowInt\",[6,\"std.floor\",[[1,\"uvY\",\"*\",\"FIELD_LINES\"]]]],[13,\"h0\",[1,[1,[6,\"noise.hash12\",[[6,\"d.vec2f\",[\"rowInt\",\"t\"]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"h1\",[1,[1,[6,\"noise.hash12\",[[6,\"d.vec2f\",[\"rowInt\",[1,\"t\",\"+\",[5,\"69.42\"]]]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"fineGate\",[1,[5,\"0.25\"],\"+\",[1,\"burst\",\"*\",[5,\"0.75\"]]]],[13,\"chromaRowOffset\",[1,[1,[1,\"h0\",\"*\",\"scanlineNoise\"],\"*\",[5,\"0.008\"]],\"*\",\"fineGate\"]],[13,\"lumaRowOffset\",[1,[1,[1,\"h1\",\"*\",\"scanlineNoise\"],\"*\",[5,\"0.004\"]],\"*\",\"fineGate\"]],[10,[6,\"d.vec2f\",[\"lumaRowOffset\",\"chromaRowOffset\"]]]]]},externals:{\"std.floor\":()=>ie,FIELD_LINES:()=>PS,\"noise.hash12\":()=>Ys,\"d.vec2f\":()=>m}})&&e.f)({})),ES=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(il(m(__tsover_mul(t,3),__tsover_mul(s,.8))),.5),a=__tsover_sub(il(m(__tsover_mul(t,30),__tsover_mul(s,6))),.5);return __tsover_mul(__tsover_add(__tsover_mul(o,.008),__tsover_mul(a,.002)),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"wobble\"}],body:[0,[[13,\"wave1\",[1,[6,\"smoothNoise2D\",[[6,\"d.vec2f\",[[1,\"uvY\",\"*\",[5,\"3\"]],[1,\"t\",\"*\",[5,\"0.8\"]]]]]],\"-\",[5,\"0.5\"]]],[13,\"wave2\",[1,[6,\"smoothNoise2D\",[[6,\"d.vec2f\",[[1,\"uvY\",\"*\",[5,\"30\"]],[1,\"t\",\"*\",[5,\"6\"]]]]]],\"-\",[5,\"0.5\"]]],[10,[1,[1,[1,\"wave1\",\"*\",[5,\"0.008\"]],\"+\",[1,\"wave2\",\"*\",[5,\"0.002\"]]],\"*\",\"wobble\"]]]]},externals:{smoothNoise2D:()=>il,\"d.vec2f\":()=>m}})&&e.f)({})),kS=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=be(.92,.99,ae(__tsover_sub(__tsover_mul(t,8),__tsover_mul(s,3.77)))),a=be(.3,1,il(m(__tsover_mul(t,4.77),s)));return __tsover_mul(__tsover_mul(__tsover_mul(o,a),r),-.018)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"wobble\"}],body:[0,[[13,\"creasePhase\",[6,\"std.smoothstep\",[[5,\"0.92\"],[5,\"0.99\"],[6,\"std.sin\",[[1,[1,\"uvY\",\"*\",[5,\"8\"]],\"-\",[1,\"t\",\"*\",[5,\"3.77\"]]]]]]]],[13,\"creaseNoise\",[6,\"std.smoothstep\",[[5,\"0.3\"],[5,\"1\"],[6,\"smoothNoise2D\",[[6,\"d.vec2f\",[[1,\"uvY\",\"*\",[5,\"4.77\"]],\"t\"]]]]]]],[10,[1,[1,[1,\"creasePhase\",\"*\",\"creaseNoise\"],\"*\",\"wobble\"],\"*\",[4,\"-\",[5,\"0.018\"]]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.sin\":()=>ae,smoothNoise2D:()=>il,\"d.vec2f\":()=>m}})&&e.f)({})),BS=w.fn([n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(1,be(0,.06,t)),i=__tsover_sub(il(m(__tsover_mul(t,60),__tsover_mul(s,14))),.5);return m(__tsover_mul(__tsover_mul(__tsover_mul(a,i),r),.09),__tsover_mul(__tsover_mul(__tsover_mul(a,r),o),.02))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"wobble\"},{type:\"i\",name:\"burst\"}],body:[0,[[13,\"switchPhase\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.06\"],\"uvY\"]]]],[13,\"switchNoise\",[1,[6,\"smoothNoise2D\",[[6,\"d.vec2f\",[[1,\"uvY\",\"*\",[5,\"60\"]],[1,\"t\",\"*\",[5,\"14\"]]]]]],\"-\",[5,\"0.5\"]]],[13,\"switchX\",[1,[1,[1,\"switchPhase\",\"*\",\"switchNoise\"],\"*\",\"wobble\"],\"*\",[5,\"0.09\"]]],[13,\"switchY\",[1,[1,[1,\"switchPhase\",\"*\",\"wobble\"],\"*\",\"burst\"],\"*\",[5,\"0.02\"]]],[10,[6,\"d.vec2f\",[\"switchX\",\"switchY\"]]]]]},externals:{\"std.smoothstep\":()=>be,smoothNoise2D:()=>il,\"d.vec2f\":()=>m}})&&e.f)({})),yre=w.fn([m,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(s,r),l=CS(i),c=MS(t.y,i,a,l),d=BS(t.y,i,o,l),u=__tsover_add(__tsover_add(ES(t.y,i,o),kS(t.y,i,o)),d.x),p=__tsover_add(t.y,d.y);return S(__tsover_add(__tsover_add(t.x,u),c.x),p,__tsover_add(__tsover_add(t.x,u),c.y),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"speed\"},{type:\"i\",name:\"wobble\"},{type:\"i\",name:\"scanlineNoise\"}],body:[0,[[13,\"t\",[1,\"time\",\"*\",\"speed\"]],[13,\"burst\",[6,\"burstGate\",[\"t\"]]],[13,\"jitter\",[6,\"rowJitter\",[[7,\"uv\",\"y\"],\"t\",\"scanlineNoise\",\"burst\"]]],[13,\"sw\",[6,\"headSwitch\",[[7,\"uv\",\"y\"],\"t\",\"wobble\",\"burst\"]]],[13,\"globalX\",[1,[1,[6,\"tapeWave\",[[7,\"uv\",\"y\"],\"t\",\"wobble\"]],\"+\",[6,\"creaseShift\",[[7,\"uv\",\"y\"],\"t\",\"wobble\"]]],\"+\",[7,\"sw\",\"x\"]]],[13,\"sy\",[1,[7,\"uv\",\"y\"],\"+\",[7,\"sw\",\"y\"]]],[10,[6,\"d.vec4f\",[[1,[1,[7,\"uv\",\"x\"],\"+\",\"globalX\"],\"+\",[7,\"jitter\",\"x\"]],\"sy\",[1,[1,[7,\"uv\",\"x\"],\"+\",\"globalX\"],\"+\",[7,\"jitter\",\"y\"]],\"sy\"]]]]]},externals:{burstGate:()=>CS,rowJitter:()=>MS,headSwitch:()=>BS,tapeWave:()=>ES,creaseShift:()=>kS,\"d.vec4f\":()=>S}})&&e.f)({})),bre=w.fn([m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,r);return __tsover_add(1,__tsover_mul(__tsover_mul(pe(__tsover_add(__tsover_mul(An(a,6.2831853),2),__tsover_mul(t.y,.5))),.015),o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"speed\"},{type:\"i\",name:\"wobble\"}],body:[0,[[13,\"t\",[1,\"time\",\"*\",\"speed\"]],[10,[1,[5,\"1\"],\"+\",[1,[1,[6,\"std.cos\",[[1,[1,[6,\"std.mod\",[\"t\",[5,\"6.2831853\"]]],\"*\",[5,\"2\"]],\"+\",[1,[7,\"uv\",\"y\"],\"*\",[5,\"0.5\"]]]]],\"*\",[5,\"0.015\"]],\"*\",\"wobble\"]]]]]},externals:{\"std.cos\":()=>pe,\"std.mod\":()=>An}})&&e.f)({})),xre=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(__tsover_add(t.x,__tsover_mul(__tsover_mul(r,-1),s)),t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"chromaUV\"},{type:\"i\",name:\"smearScale\"},{type:\"i\",name:\"i\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"chromaUV\",\"x\"],\"+\",[1,[1,\"i\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"smearScale\"]],[7,\"chromaUV\",\"y\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Are=w.fn([S,S,S,S,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Se(t.xyz,A(.299,.587,.114)),c=A(.596,-.274,-.322),d=A(.211,-.523,.312),u=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(Se(s.xyz,c),.06666666666666667),__tsover_mul(Se(r.xyz,c),.13333333333333333)),__tsover_mul(Se(o.xyz,c),.2)),__tsover_mul(Se(a.xyz,c),.26666666666666666)),__tsover_mul(Se(i.xyz,c),.3333333333333333)),p=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(Se(s.xyz,d),.06666666666666667),__tsover_mul(Se(r.xyz,d),.13333333333333333)),__tsover_mul(Se(o.xyz,d),.2)),__tsover_mul(Se(a.xyz,d),.26666666666666666)),__tsover_mul(Se(i.xyz,d),.3333333333333333));return A(__tsover_add(__tsover_add(l,__tsover_mul(u,.956)),__tsover_mul(p,.621)),__tsover_sub(__tsover_sub(l,__tsover_mul(u,.272)),__tsover_mul(p,.647)),__tsover_add(__tsover_sub(l,__tsover_mul(u,1.106)),__tsover_mul(p,1.703)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"luma\"},{type:\"i\",name:\"c1\"},{type:\"i\",name:\"c2\"},{type:\"i\",name:\"c3\"},{type:\"i\",name:\"c4\"},{type:\"i\",name:\"c5\"}],body:[0,[[13,\"sharpY\",[6,\"std.dot\",[[7,\"luma\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"iAxis\",[6,\"d.vec3f\",[[5,\"0.596\"],[4,\"-\",[5,\"0.274\"]],[4,\"-\",[5,\"0.322\"]]]]],[13,\"qAxis\",[6,\"d.vec3f\",[[5,\"0.211\"],[4,\"-\",[5,\"0.523\"]],[5,\"0.312\"]]]],[13,\"accI\",[1,[1,[1,[1,[1,[6,\"std.dot\",[[7,\"c1\",\"xyz\"],\"iAxis\"]],\"*\",[5,\"0.06666666666666667\"]],\"+\",[1,[6,\"std.dot\",[[7,\"c2\",\"xyz\"],\"iAxis\"]],\"*\",[5,\"0.13333333333333333\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c3\",\"xyz\"],\"iAxis\"]],\"*\",[5,\"0.2\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c4\",\"xyz\"],\"iAxis\"]],\"*\",[5,\"0.26666666666666666\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c5\",\"xyz\"],\"iAxis\"]],\"*\",[5,\"0.3333333333333333\"]]]],[13,\"accQ\",[1,[1,[1,[1,[1,[6,\"std.dot\",[[7,\"c1\",\"xyz\"],\"qAxis\"]],\"*\",[5,\"0.06666666666666667\"]],\"+\",[1,[6,\"std.dot\",[[7,\"c2\",\"xyz\"],\"qAxis\"]],\"*\",[5,\"0.13333333333333333\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c3\",\"xyz\"],\"qAxis\"]],\"*\",[5,\"0.2\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c4\",\"xyz\"],\"qAxis\"]],\"*\",[5,\"0.26666666666666666\"]]],\"+\",[1,[6,\"std.dot\",[[7,\"c5\",\"xyz\"],\"qAxis\"]],\"*\",[5,\"0.3333333333333333\"]]]],[10,[6,\"d.vec3f\",[[1,[1,\"sharpY\",\"+\",[1,\"accI\",\"*\",[5,\"0.956\"]]],\"+\",[1,\"accQ\",\"*\",[5,\"0.621\"]]],[1,[1,\"sharpY\",\"-\",[1,\"accI\",\"*\",[5,\"0.272\"]]],\"-\",[1,\"accQ\",\"*\",[5,\"0.647\"]]],[1,[1,\"sharpY\",\"-\",[1,\"accI\",\"*\",[5,\"1.106\"]]],\"+\",[1,\"accQ\",\"*\",[5,\"1.703\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),wre=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=G(t.xyz.mul(s),A(0,0,0),A(1,1,1));return S(r.x,r.y,r.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"acBeat\"}],body:[0,[[13,\"clamped\",[6,\"std.clamp\",[[6,[7,[7,\"color\",\"xyz\"],\"mul\"],[\"acBeat\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[10,[6,\"d.vec4f\",[[7,\"clamped\",\"x\"],[7,\"clamped\",\"y\"],[7,\"clamped\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),Yo=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Xs(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"noise.hash11\",[\"p\"]]]]]},externals:{\"noise.hash11\":()=>Xs}})&&e.f)({})),zS=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Yo(t),a=__tsover_mul(__tsover_mul(o,o),o),i=Yo(__tsover_add(s,73)),l=__tsover_mul(i,i),c=__tsover_sub(1,r),d=Et(c,a),u=Et(__tsover_add(c,.15),l);return __tsover_mul(G(__tsover_add(__tsover_mul(d,.8),__tsover_mul(u,.4)),0,1),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"slowFrame\"},{type:\"i\",name:\"fastFrame\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"slowHash\",[6,\"hash11\",[\"slowFrame\"]]],[13,\"slowPulse\",[1,[1,\"slowHash\",\"*\",\"slowHash\"],\"*\",\"slowHash\"]],[13,\"fastHash\",[6,\"hash11\",[[1,\"fastFrame\",\"+\",[5,\"73\"]]]]],[13,\"fastPulse\",[1,\"fastHash\",\"*\",\"fastHash\"]],[13,\"threshold\",[1,[5,\"1\"],\"-\",\"intensity\"]],[13,\"majorBurst\",[6,\"std.step\",[\"threshold\",\"slowPulse\"]]],[13,\"minorFlicker\",[6,\"std.step\",[[1,\"threshold\",\"+\",[5,\"0.15\"]],\"fastPulse\"]]],[10,[1,[6,\"std.clamp\",[[1,[1,\"majorBurst\",\"*\",[5,\"0.8\"]],\"+\",[1,\"minorFlicker\",\"*\",[5,\"0.4\"]]],[5,\"0\"],[5,\"1\"]]],\"*\",\"intensity\"]]]]},externals:{hash11:()=>Yo,\"std.step\":()=>Et,\"std.clamp\":()=>G}})&&e.f)({})),d1=w.fn([n,n,n,n,n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_add(__tsover_mul(t,r),__tsover_mul(s,o)),u=Yo(d),p=Et(__tsover_sub(1,__tsover_mul(c,a)),u);return A(__tsover_mul(__tsover_mul(__tsover_sub(Yo(__tsover_add(d,i)),.5),l),p),p,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"bandY\"},{type:\"i\",name:\"frame\"},{type:\"i\",name:\"seedScale\"},{type:\"i\",name:\"frameScale\"},{type:\"i\",name:\"gateFactor\"},{type:\"i\",name:\"hashOffset\"},{type:\"i\",name:\"amplitude\"},{type:\"i\",name:\"glitchStrength\"}],body:[0,[[13,\"seed\",[1,[1,\"bandY\",\"*\",\"seedScale\"],\"+\",[1,\"frame\",\"*\",\"frameScale\"]]],[13,\"bandHash\",[6,\"hash11\",[\"seed\"]]],[13,\"active\",[6,\"std.step\",[[1,[5,\"1\"],\"-\",[1,\"glitchStrength\",\"*\",\"gateFactor\"]],\"bandHash\"]]],[13,\"displace\",[1,[1,[1,[6,\"hash11\",[[1,\"seed\",\"+\",\"hashOffset\"]]],\"-\",[5,\"0.5\"]],\"*\",\"amplitude\"],\"*\",\"active\"]],[10,[6,\"d.vec3f\",[\"displace\",\"active\",\"seed\"]]]]]},externals:{hash11:()=>Yo,\"std.step\":()=>Et,\"d.vec3f\":()=>A}})&&e.f)({})),RS=w.fn([n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_add(__tsover_add(__tsover_mul(t,.41),__tsover_mul(s,.67)),__tsover_mul(r,2.31)),i=Yo(a),l=Et(__tsover_sub(1,__tsover_mul(o,.35)),i);return m(__tsover_mul(__tsover_mul(__tsover_sub(Yo(__tsover_add(a,77)),.5),.18),l),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colX\"},{type:\"i\",name:\"bandY1\"},{type:\"i\",name:\"slowFrame\"},{type:\"i\",name:\"glitchStrength\"}],body:[0,[[13,\"blockSeed\",[1,[1,[1,\"colX\",\"*\",[5,\"0.41\"]],\"+\",[1,\"bandY1\",\"*\",[5,\"0.67\"]]],\"+\",[1,\"slowFrame\",\"*\",[5,\"2.31\"]]]],[13,\"blockHash\",[6,\"hash11\",[\"blockSeed\"]]],[13,\"blockActive\",[6,\"std.step\",[[1,[5,\"1\"],\"-\",[1,\"glitchStrength\",\"*\",[5,\"0.35\"]]],\"blockHash\"]]],[13,\"displace\",[1,[1,[1,[6,\"hash11\",[[1,\"blockSeed\",\"+\",[5,\"77\"]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.18\"]],\"*\",\"blockActive\"]],[10,[6,\"d.vec2f\",[\"displace\",\"blockActive\"]]]]]},externals:{hash11:()=>Yo,\"std.step\":()=>Et,\"d.vec2f\":()=>m}})&&e.f)({})),DS=w.fn([m,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_add(__tsover_add(__tsover_mul(s,.83),__tsover_mul(r,1.17)),__tsover_mul(o,4.57)),d=Yo(c),u=Yo(__tsover_add(c,500)),p=__tsover_mul(Et(__tsover_sub(1,__tsover_mul(i,l)),d),a),_=__tsover_mul(Et(__tsover_sub(1,__tsover_mul(__tsover_mul(i,l),.4)),u),a);return m(K(t.x,__tsover_sub(1,t.x),p),K(t.y,__tsover_sub(1,t.y),_))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"displacedUV\"},{type:\"i\",name:\"colX\"},{type:\"i\",name:\"bandY1\"},{type:\"i\",name:\"slowFrame\"},{type:\"i\",name:\"bandActive1\"},{type:\"i\",name:\"mirrorChance\"},{type:\"i\",name:\"glitchStrength\"}],body:[0,[[13,\"mirrorSeed\",[1,[1,[1,\"colX\",\"*\",[5,\"0.83\"]],\"+\",[1,\"bandY1\",\"*\",[5,\"1.17\"]]],\"+\",[1,\"slowFrame\",\"*\",[5,\"4.57\"]]]],[13,\"hMirrorHash\",[6,\"hash11\",[\"mirrorSeed\"]]],[13,\"vMirrorHash\",[6,\"hash11\",[[1,\"mirrorSeed\",\"+\",[5,\"500\"]]]]],[13,\"hMirrorActive\",[1,[6,\"std.step\",[[1,[5,\"1\"],\"-\",[1,\"mirrorChance\",\"*\",\"glitchStrength\"]],\"hMirrorHash\"]],\"*\",\"bandActive1\"]],[13,\"vMirrorActive\",[1,[6,\"std.step\",[[1,[5,\"1\"],\"-\",[1,[1,\"mirrorChance\",\"*\",\"glitchStrength\"],\"*\",[5,\"0.4\"]]],\"vMirrorHash\"]],\"*\",\"bandActive1\"]],[10,[6,\"d.vec2f\",[[6,\"std.mix\",[[7,\"displacedUV\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"displacedUV\",\"x\"]],\"hMirrorActive\"]],[6,\"std.mix\",[[7,\"displacedUV\",\"y\"],[1,[5,\"1\"],\"-\",[7,\"displacedUV\",\"y\"]],\"vMirrorActive\"]]]]]]]},externals:{hash11:()=>Yo,\"std.step\":()=>Et,\"d.vec2f\":()=>m,\"std.mix\":()=>K}})&&e.f)({})),P0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({mirroredUV:m,spread:n,strength:n,bandY1:n,slowFrame:n,bandGate:n,blockGate:n,distortion:n}),\"GlitchGeom\"),Tre=w.fn([m,n,n,n,n,n],P0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_mul(s,o),c=ie(__tsover_mul(l,2.5)),d=ie(__tsover_mul(l,10)),u=zS(c,d,r),p=ie(__tsover_mul(t.y,a)),_=d1(p,c,.37,7.13,.5,42,.25,u),f=d1(ie(__tsover_mul(t.y,__tsover_mul(a,2))),d,.53,3.37,.7,99,.12,u),h=d1(ie(__tsover_mul(t.y,__tsover_mul(a,4))),d,.71,11.71,.9,173,.05,u),g=ie(__tsover_mul(t.x,4)),y=RS(g,p,c,u),b=__tsover_add(__tsover_add(__tsover_add(_.x,f.x),h.x),y.x),x=__tsover_mul(__tsover_mul(_.y,__tsover_sub(Yo(__tsover_add(_.z,200)),.5)),.012),P=DS(m(G(__tsover_add(t.x,__tsover_mul(b,u)),0,1),G(__tsover_add(t.y,__tsover_mul(x,u)),0,1)),g,p,c,_.y,i,u),C=__tsover_add(u,__tsover_mul(re(b),2)),E=G(__tsover_add(__tsover_add(_.y,f.y),h.y),0,1);return P0({mirroredUV:P,spread:C,strength:u,bandY1:p,slowFrame:c,bandGate:_.y,blockGate:y.y,distortion:E})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"speed\"},{type:\"i\",name:\"blockDensity\"},{type:\"i\",name:\"mirrorChance\"}],body:[0,[[13,\"t\",[1,\"time\",\"*\",\"speed\"]],[13,\"slowFrame\",[6,\"std.floor\",[[1,\"t\",\"*\",[5,\"2.5\"]]]]],[13,\"fastFrame\",[6,\"std.floor\",[[1,\"t\",\"*\",[5,\"10\"]]]]],[13,\"glitchStrength\",[6,\"burstPulse\",[\"slowFrame\",\"fastFrame\",\"intensity\"]]],[13,\"bandY1\",[6,\"std.floor\",[[1,[7,\"uv\",\"y\"],\"*\",\"blockDensity\"]]]],[13,\"band1\",[6,\"bandJitter\",[\"bandY1\",\"slowFrame\",[5,\"0.37\"],[5,\"7.13\"],[5,\"0.5\"],[5,\"42\"],[5,\"0.25\"],\"glitchStrength\"]]],[13,\"bandY2\",[6,\"std.floor\",[[1,[7,\"uv\",\"y\"],\"*\",[1,\"blockDensity\",\"*\",[5,\"2\"]]]]]],[13,\"band2\",[6,\"bandJitter\",[\"bandY2\",\"fastFrame\",[5,\"0.53\"],[5,\"3.37\"],[5,\"0.7\"],[5,\"99\"],[5,\"0.12\"],\"glitchStrength\"]]],[13,\"bandY3\",[6,\"std.floor\",[[1,[7,\"uv\",\"y\"],\"*\",[1,\"blockDensity\",\"*\",[5,\"4\"]]]]]],[13,\"band3\",[6,\"bandJitter\",[\"bandY3\",\"fastFrame\",[5,\"0.71\"],[5,\"11.71\"],[5,\"0.9\"],[5,\"173\"],[5,\"0.05\"],\"glitchStrength\"]]],[13,\"colX\",[6,\"std.floor\",[[1,[7,\"uv\",\"x\"],\"*\",[5,\"4\"]]]]],[13,\"block\",[6,\"blockShift\",[\"colX\",\"bandY1\",\"slowFrame\",\"glitchStrength\"]]],[13,\"totalDisplaceX\",[1,[1,[1,[7,\"band1\",\"x\"],\"+\",[7,\"band2\",\"x\"]],\"+\",[7,\"band3\",\"x\"]],\"+\",[7,\"block\",\"x\"]]],[13,\"vertJitter\",[1,[1,[7,\"band1\",\"y\"],\"*\",[1,[6,\"hash11\",[[1,[7,\"band1\",\"z\"],\"+\",[5,\"200\"]]]],\"-\",[5,\"0.5\"]]],\"*\",[5,\"0.012\"]]],[13,\"displacedUV\",[6,\"d.vec2f\",[[6,\"std.clamp\",[[1,[7,\"uv\",\"x\"],\"+\",[1,\"totalDisplaceX\",\"*\",\"glitchStrength\"]],[5,\"0\"],[5,\"1\"]]],[6,\"std.clamp\",[[1,[7,\"uv\",\"y\"],\"+\",[1,\"vertJitter\",\"*\",\"glitchStrength\"]],[5,\"0\"],[5,\"1\"]]]]]],[13,\"mirroredUV\",[6,\"mirrorFlips\",[\"displacedUV\",\"colX\",\"bandY1\",\"slowFrame\",[7,\"band1\",\"y\"],\"mirrorChance\",\"glitchStrength\"]]],[13,\"spread\",[1,\"glitchStrength\",\"+\",[1,[6,\"std.abs\",[\"totalDisplaceX\"]],\"*\",[5,\"2\"]]]],[13,\"anyDistortion\",[6,\"std.clamp\",[[1,[1,[7,\"band1\",\"y\"],\"+\",[7,\"band2\",\"y\"]],\"+\",[7,\"band3\",\"y\"]],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"GlitchGeom\",[[104,{mirroredUV:\"mirroredUV\",spread:\"spread\",strength:\"glitchStrength\",bandY1:\"bandY1\",slowFrame:\"slowFrame\",bandGate:[7,\"band1\",\"y\"],blockGate:[7,\"block\",\"y\"],distortion:\"anyDistortion\"}]]]]]]},externals:{\"std.floor\":()=>ie,burstPulse:()=>zS,bandJitter:()=>d1,blockShift:()=>RS,hash11:()=>Yo,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,mirrorFlips:()=>DS,\"std.abs\":()=>re,GlitchGeom:()=>P0}})&&e.f)({})),Sre=w.fn([m,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(__tsover_mul(s,.003),r),a=G(__tsover_add(t.x,o),0,1),i=G(__tsover_sub(t.x,o),0,1);return S(a,t.y,i,t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"mirroredUV\"},{type:\"i\",name:\"rgbShift\"},{type:\"i\",name:\"spread\"}],body:[0,[[13,\"rgbOffset\",[1,[1,\"rgbShift\",\"*\",[5,\"0.003\"]],\"*\",\"spread\"]],[13,\"redX\",[6,\"std.clamp\",[[1,[7,\"mirroredUV\",\"x\"],\"+\",\"rgbOffset\"],[5,\"0\"],[5,\"1\"]]]],[13,\"blueX\",[6,\"std.clamp\",[[1,[7,\"mirroredUV\",\"x\"],\"-\",\"rgbOffset\"],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[\"redX\",[7,\"mirroredUV\",\"y\"],\"blueX\",[7,\"mirroredUV\",\"y\"]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({})),Pre=w.fn([S,m,n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=ie(__tsover_mul(s.x,4)),u=__tsover_add(__tsover_add(__tsover_mul(d,.41),__tsover_mul(r,.67)),__tsover_mul(o,2.31)),p=Yo(__tsover_add(u,555)),_=Yo(__tsover_add(__tsover_add(r,__tsover_mul(o,3.33)),333)),f=Re(__tsover_add(__tsover_mul(s.x,3),__tsover_mul(_,7))),h=ie(__tsover_mul(f,7)),g=A(__tsover_add(__tsover_sub(1,Et(2,h)),__tsover_mul(Et(4,h),__tsover_sub(1,Et(6,h)))),__tsover_sub(1,Et(4,h)),__tsover_sub(1,Et(.5,Re(__tsover_mul(h,.5))))),y=Re(__tsover_add(__tsover_mul(Yo(__tsover_add(r,13)),.8),__tsover_mul(o,.07))),b=__tsover_mul(y,6),x=K(A(G(__tsover_sub(re(__tsover_sub(b,3)),1),0,1),G(__tsover_sub(2,re(__tsover_sub(b,2))),0,1),G(__tsover_sub(2,re(__tsover_sub(b,4))),0,1)).mul(1.3),g,A(Et(.5,p))),P=__tsover_mul(__tsover_mul(__tsover_mul(l,c),a),i),C=K(t.xyz,x.mul(t.w),A(P));return S(C.x,C.y,C.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"bandY1\"},{type:\"i\",name:\"slowFrame\"},{type:\"i\",name:\"colorBarMix\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"bandGate\"},{type:\"i\",name:\"blockGate\"}],body:[0,[[13,\"colX\",[6,\"std.floor\",[[1,[7,\"uv\",\"x\"],\"*\",[5,\"4\"]]]]],[13,\"blockSeed\",[1,[1,[1,\"colX\",\"*\",[5,\"0.41\"]],\"+\",[1,\"bandY1\",\"*\",[5,\"0.67\"]]],\"+\",[1,\"slowFrame\",\"*\",[5,\"2.31\"]]]],[13,\"fillStyleHash\",[6,\"hash11\",[[1,\"blockSeed\",\"+\",[5,\"555\"]]]]],[13,\"barOffset\",[6,\"hash11\",[[1,[1,\"bandY1\",\"+\",[1,\"slowFrame\",\"*\",[5,\"3.33\"]]],\"+\",[5,\"333\"]]]]],[13,\"barX\",[6,\"std.fract\",[[1,[1,[7,\"uv\",\"x\"],\"*\",[5,\"3\"]],\"+\",[1,\"barOffset\",\"*\",[5,\"7\"]]]]]],[13,\"barIndex\",[6,\"std.floor\",[[1,\"barX\",\"*\",[5,\"7\"]]]]],[13,\"barR\",[1,[1,[5,\"1\"],\"-\",[6,\"std.step\",[[5,\"2\"],\"barIndex\"]]],\"+\",[1,[6,\"std.step\",[[5,\"4\"],\"barIndex\"]],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.step\",[[5,\"6\"],\"barIndex\"]]]]]],[13,\"barG\",[1,[5,\"1\"],\"-\",[6,\"std.step\",[[5,\"4\"],\"barIndex\"]]]],[13,\"barB\",[1,[5,\"1\"],\"-\",[6,\"std.step\",[[5,\"0.5\"],[6,\"std.fract\",[[1,\"barIndex\",\"*\",[5,\"0.5\"]]]]]]]],[13,\"smpteColor\",[6,\"d.vec3f\",[\"barR\",\"barG\",\"barB\"]]],[13,\"solidHue\",[6,\"std.fract\",[[1,[1,[6,\"hash11\",[[1,\"bandY1\",\"+\",[5,\"13\"]]]],\"*\",[5,\"0.8\"]],\"+\",[1,\"slowFrame\",\"*\",[5,\"0.07\"]]]]]],[13,\"sh6\",[1,\"solidHue\",\"*\",[5,\"6\"]]],[13,\"solidColor\",[6,[7,[6,\"d.vec3f\",[[6,\"std.clamp\",[[1,[6,\"std.abs\",[[1,\"sh6\",\"-\",[5,\"3\"]]]],\"-\",[5,\"1\"]],[5,\"0\"],[5,\"1\"]]],[6,\"std.clamp\",[[1,[5,\"2\"],\"-\",[6,\"std.abs\",[[1,\"sh6\",\"-\",[5,\"2\"]]]]],[5,\"0\"],[5,\"1\"]]],[6,\"std.clamp\",[[1,[5,\"2\"],\"-\",[6,\"std.abs\",[[1,\"sh6\",\"-\",[5,\"4\"]]]]],[5,\"0\"],[5,\"1\"]]]]],\"mul\"],[[5,\"1.3\"]]]],[13,\"fillColor\",[6,\"std.mix\",[\"solidColor\",\"smpteColor\",[6,\"d.vec3f\",[[6,\"std.step\",[[5,\"0.5\"],\"fillStyleHash\"]]]]]]],[13,\"colorBarStrength\",[1,[1,[1,\"bandGate\",\"*\",\"blockGate\"],\"*\",\"colorBarMix\"],\"*\",\"strength\"]],[13,\"withBars\",[6,\"std.mix\",[[7,\"color\",\"xyz\"],[6,[7,\"fillColor\",\"mul\"],[[7,\"color\",\"w\"]]],[6,\"d.vec3f\",[\"colorBarStrength\"]]]]],[10,[6,\"d.vec4f\",[[7,\"withBars\",\"x\"],[7,\"withBars\",\"y\"],[7,\"withBars\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"std.floor\":()=>ie,hash11:()=>Yo,\"std.fract\":()=>Re,\"std.step\":()=>Et,\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"std.abs\":()=>re,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})),Cre=w.fn([S,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(s,r),l=__tsover_add(__tsover_mul(ae(__tsover_mul(i,3.14159265)),.5),.5),c=__tsover_sub(1,__tsover_mul(__tsover_mul(__tsover_mul(o,l),.3),a)),d=t.xyz.mul(c);return S(d.x,d.y,d.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"viewportY\"},{type:\"i\",name:\"scanlines\"},{type:\"i\",name:\"distortion\"}],body:[0,[[13,\"pixelY\",[1,\"uvY\",\"*\",\"viewportY\"]],[13,\"scanlineWave\",[1,[1,[6,\"std.sin\",[[1,\"pixelY\",\"*\",[5,\"3.14159265\"]]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[13,\"scanlineEffect\",[1,[5,\"1\"],\"-\",[1,[1,[1,\"scanlines\",\"*\",\"scanlineWave\"],\"*\",[5,\"0.3\"]],\"*\",\"distortion\"]]],[13,\"shaded\",[6,[7,[7,\"color\",\"xyz\"],\"mul\"],[\"scanlineEffect\"]]],[10,[6,\"d.vec4f\",[[7,\"shaded\",\"x\"],[7,\"shaded\",\"y\"],[7,\"shaded\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"std.sin\":()=>ae,\"d.vec4f\":()=>S}})&&e.f)({})),LS=2/Math.sqrt(3),hu=Math.sqrt(3)/2,Mre=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Cs(s),i=m(__tsover_mul(t.x,a),__tsover_sub(1,t.y)),l=__tsover_mul(o,bt);return Gu(i,a,pe(l),ae(l)).mul(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"rotationDeg\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"correctedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"uv\",\"y\"]]]]],[13,\"rotRad\",[1,\"rotationDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[10,[6,[7,[6,\"rotateAboutCanvasCentre\",[\"correctedUV\",\"aspect\",\"cosR\",\"sinR\"]],\"mul\"],[\"cells\"]]]]]},externals:{aspectOf:()=>Cs,\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,rotateAboutCanvasCentre:()=>Gu}})&&e.f)({})),Ere=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Cs(s),i=m(__tsover_mul(t.x,a),t.y),l=-__tsover_mul(o,bt);return Gu(i,a,pe(l),ae(l)).mul(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"rotationDeg\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"correctedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"ang\",[4,\"-\",[1,\"rotationDeg\",\"*\",\"DEG_TO_RAD\"]]],[13,\"cosR\",[6,\"std.cos\",[\"ang\"]]],[13,\"sinR\",[6,\"std.sin\",[\"ang\"]]],[10,[6,[7,[6,\"rotateAboutCanvasCentre\",[\"correctedUV\",\"aspect\",\"cosR\",\"sinR\"]],\"mul\"],[\"cells\"]]]]]},externals:{aspectOf:()=>Cs,\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,rotateAboutCanvasCentre:()=>Gu}})&&e.f)({})),kre=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Cs(s),i=m(__tsover_mul(t.x,a),t.y),l=__tsover_mul(o,bt);return Gu(i,a,pe(l),ae(l)).mul(r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"rotationDeg\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"correctedUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"rotRad\",[1,\"rotationDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[10,[6,[7,[6,\"rotateAboutCanvasCentre\",[\"correctedUV\",\"aspect\",\"cosR\",\"sinR\"]],\"mul\"],[\"cells\"]]]]]},externals:{aspectOf:()=>Cs,\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,rotateAboutCanvasCentre:()=>Gu}})&&e.f)({})),mc=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return hT(iv(t),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cellId\"},{type:\"i\",name:\"variation\"}],body:[0,[[10,[6,\"variationFactor\",[[6,\"cellHash\",[\"cellId\"]],\"variation\"]]]]]},externals:{variationFactor:()=>hT,cellHash:()=>iv}})&&e.f)({})),Cv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(o,.5);return uv(t,s,B(__tsover_sub(s,a),0),__tsover_add(__tsover_add(s,r),a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"field\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"pixel\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"soft\",[1,\"softness\",\"*\",[5,\"0.5\"]]],[13,\"lo\",[6,\"std.max\",[[1,\"lineWidth\",\"-\",\"soft\"],[5,\"0\"]]]],[13,\"hi\",[1,[1,\"lineWidth\",\"+\",\"pixel\"],\"+\",\"soft\"]],[10,[6,\"lineMaskFromField\",[\"field\",\"lineWidth\",\"lo\",\"hi\"]]]]]},externals:{\"std.max\":()=>B,lineMaskFromField:()=>uv}})&&e.f)({})),Mv=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return uv(t,s,__tsover_sub(s,r),__tsover_add(s,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"field\"},{type:\"i\",name:\"lineWidth\"},{type:\"i\",name:\"aaWidth\"}],body:[0,[[10,[6,\"lineMaskFromField\",[\"field\",\"lineWidth\",[1,\"lineWidth\",\"-\",\"aaWidth\"],[1,\"lineWidth\",\"+\",\"aaWidth\"]]]]]]},externals:{lineMaskFromField:()=>uv}})&&e.f)({})),Bre=w.fn([m,m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=ov(t,s).mul(r);return bT(a,gT(a,o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"p\",[6,[7,[6,\"aspectCorrectedUVFlipY\",[\"uv\",\"viewport\"]],\"mul\"],[\"cells\"]]],[10,[6,\"quilezCheckerFilter\",[\"p\",[6,\"footprint2\",[\"p\",\"softness\"]]]]]]]},externals:{aspectCorrectedUVFlipY:()=>ov,quilezCheckerFilter:()=>bT,footprint2:()=>gT}})&&e.f)({})),US=w.fn([m,m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_mul(r,bt),c=Cs(s),d=__tsover_add(av(dc(t,c),pe(l),ae(l)),__tsover_mul(c,.5));return __tsover_add(__tsover_add(__tsover_mul(d,o),a),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"offset\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"rotatedCoord\",[1,[6,\"directionalProjection\",[[6,\"canvasCentredDelta\",[\"uv\",\"aspect\"]],[6,\"std.cos\",[\"angleRad\"]],[6,\"std.sin\",[\"angleRad\"]]]],\"+\",[1,\"aspect\",\"*\",[5,\"0.5\"]]]],[10,[1,[1,[1,\"rotatedCoord\",\"*\",\"density\"],\"+\",\"animTime\"],\"+\",\"offset\"]]]]},externals:{DEG_TO_RAD:()=>bt,aspectOf:()=>Cs,directionalProjection:()=>av,canvasCentredDelta:()=>dc,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),zre=w.fn([m,m,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=US(t,s,r,o,a,i);return yT(d,dv(d,c),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"offset\"},{type:\"i\",name:\"balance\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"p\",[6,\"stripeCoordP\",[\"uv\",\"viewport\",\"angle\",\"density\",\"animTime\",\"offset\"]]],[10,[6,\"quilezStepFilter\",[\"p\",[6,\"footprint1\",[\"p\",\"softness\"]],\"balance\"]]]]]},externals:{stripeCoordP:()=>US,quilezStepFilter:()=>yT,footprint1:()=>dv}})&&e.f)({})),Rre=w.fn([m,m,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_mul(o,bt),u=pe(d),p=ae(d),_=Pn(dc(t,Cs(s)),u,-p),f=__tsover_mul(re(__tsover_sub(Re(__tsover_mul(_.y,r)),.5)),2),h=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(_.x,r),__tsover_mul(f,.5)),c),l),g=Re(h),y=mr(h);return be(__tsover_sub(__tsover_sub(a,i),y),__tsover_add(__tsover_add(a,i),y),g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"count\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"balance\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"offset\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rot\",[6,\"rotate2\",[[6,\"canvasCentredDelta\",[\"uv\",[6,\"aspectOf\",[\"viewport\"]]]],\"cosA\",[4,\"-\",\"sinA\"]]]],[13,\"zigzag\",[1,[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,[7,\"rot\",\"y\"],\"*\",\"count\"]]],\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"stripeCoord\",[1,[1,[1,[1,[7,\"rot\",\"x\"],\"*\",\"count\"],\"+\",[1,\"zigzag\",\"*\",[5,\"0.5\"]]],\"+\",\"animTime\"],\"+\",\"offset\"]],[13,\"pattern\",[6,\"std.fract\",[\"stripeCoord\"]]],[13,\"fw\",[6,\"std.fwidth\",[\"stripeCoord\"]]],[10,[6,\"std.smoothstep\",[[1,[1,\"balance\",\"-\",\"softness\"],\"-\",\"fw\"],[1,[1,\"balance\",\"+\",\"softness\"],\"+\",\"fw\"],\"pattern\"]]]]]},externals:{DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,rotate2:()=>Pn,canvasCentredDelta:()=>dc,aspectOf:()=>Cs,\"std.abs\":()=>re,\"std.fract\":()=>Re,\"std.fwidth\":()=>mr,\"std.smoothstep\":()=>be}})&&e.f)({})),Dre=w.fn([m,n,n,n,n,n,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_div(c.x,c.y),u=he(m(__tsover_sub(__tsover_mul(l.x,d),__tsover_mul(t.x,d)),__tsover_sub(l.y,__tsover_sub(1,t.y)))),p=ae(__tsover_add(__tsover_sub(__tsover_mul(u,s),i),a)),_=__tsover_sub(__tsover_mul(o,2),1),f=be(__tsover_sub(_,r),__tsover_add(_,r),p);return __tsover_sub(1,f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"phase\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"dx\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,[7,\"center\",\"x\"],\"*\",\"aspect\"]]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]],[13,\"dist\",[6,\"std.length\",[[6,\"d.vec2f\",[\"dx\",\"dy\"]]]]],[13,\"waveInput\",[1,[1,[1,\"dist\",\"*\",\"frequency\"],\"-\",\"animTime\"],\"+\",\"phase\"]],[13,\"baseWave\",[6,\"std.sin\",[\"waveInput\"]]],[13,\"thicknessThreshold\",[1,[1,\"thickness\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"banded\",[6,\"std.smoothstep\",[[1,\"thicknessThreshold\",\"-\",\"softness\"],[1,\"thicknessThreshold\",\"+\",\"softness\"],\"baseWave\"]]],[10,[1,[5,\"1\"],\"-\",\"banded\"]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.sin\":()=>ae,\"std.smoothstep\":()=>be}})&&e.f)({})),OS=w.fn([n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return m(Re(__tsover_mul(ae(__tsover_mul(t,127.1)),43758.5453123)),Re(__tsover_mul(ae(__tsover_mul(t,311.7)),38291.3421)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"idx\"}],body:[0,[[10,[6,\"d.vec2f\",[[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,\"idx\",\"*\",[5,\"127.1\"]]]],\"*\",[5,\"43758.5453123\"]]]],[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,\"idx\",\"*\",[5,\"311.7\"]]]],\"*\",[5,\"38291.3421\"]]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({})),GS=w.fn([n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_mul(o,a),d=__tsover_mul(B(__tsover_sub(t,s),0),r),u=G(__tsover_div(d,B(c,1e-4)),0,1),p=__tsover_mul(o,Ue(B(__tsover_sub(1,__tsover_mul(u,u)),0)));return __tsover_mul(__tsover_sub(1,be(B(__tsover_sub(p,i),0),B(__tsover_add(p,i),.001),l)),be(0,i,p))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"vPeriodic\"},{type:\"i\",name:\"trailLen\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"hw\"},{type:\"i\",name:\"rounding\"},{type:\"i\",name:\"fwU\"},{type:\"i\",name:\"distFromCenter\"}],body:[0,[[13,\"maxDepth\",[1,\"hw\",\"*\",\"rounding\"]],[13,\"gapDist\",[1,[6,\"std.max\",[[1,\"vPeriodic\",\"-\",\"trailLen\"],[5,\"0\"]]],\"*\",\"density\"]],[13,\"tcap\",[6,\"std.clamp\",[[1,\"gapDist\",\"/\",[6,\"std.max\",[\"maxDepth\",[5,\"0.0001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"capHW\",[1,\"hw\",\"*\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[5,\"1\"],\"-\",[1,\"tcap\",\"*\",\"tcap\"]],[5,\"0\"]]]]]]],[10,[1,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[6,\"std.max\",[[1,\"capHW\",\"-\",\"fwU\"],[5,\"0\"]]],[6,\"std.max\",[[1,\"capHW\",\"+\",\"fwU\"],[5,\"0.001\"]]],\"distFromCenter\"]]],\"*\",[6,\"std.smoothstep\",[[5,\"0\"],\"fwU\",\"capHW\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.sqrt\":()=>Ue,\"std.smoothstep\":()=>be}})&&e.f)({})),FS=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_sub(__tsover_mul(2,s),1),o=B(__tsover_mul(2,__tsover_sub(1,s)),.001);return G(__tsover_div(__tsover_add(t,r),o),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"withinStreak\"},{type:\"i\",name:\"balance\"}],body:[0,[[13,\"balanceNum\",[1,[1,[5,\"2\"],\"*\",\"balance\"],\"-\",[5,\"1\"]]],[13,\"balanceDen\",[6,\"std.max\",[[1,[5,\"2\"],\"*\",[1,[5,\"1\"],\"-\",\"balance\"]],[5,\"0.001\"]]]],[10,[6,\"std.clamp\",[[1,[1,\"withinStreak\",\"+\",\"balanceNum\"],\"/\",\"balanceDen\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G}})&&e.f)({})),Lre=w.fn([m,m,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=Cs(s),_=m(__tsover_mul(t.x,p),t.y).sub(m(__tsover_mul(p,.5),.5)),f=__tsover_mul(r,bt),h=pe(f),g=ae(f),y=__tsover_sub(__tsover_mul(_.y,h),__tsover_mul(_.x,g)),b=__tsover_add(__tsover_mul(_.x,h),__tsover_mul(_.y,g)),x=__tsover_mul(y,o),P=ie(x),C=Re(x),E=OS(P),R=__tsover_add(1,__tsover_mul(__tsover_mul(__tsover_sub(E.x,.5),a),2)),U=E.y,L=Re(__tsover_mul(u,R)),k=Re(__tsover_sub(__tsover_sub(b,L),U)),D=i,O=G(__tsover_div(k,D),0,1),F=G(mr(b),.001,.1),q=G(__tsover_mul(mr(y),o),.001,.1),Y=__tsover_sub(1,be(__tsover_sub(D,F),__tsover_add(D,F),k)),V=__tsover_mul(l,.5),X=re(__tsover_sub(C,.5)),Q=__tsover_sub(1,be(__tsover_sub(V,q),__tsover_add(V,q),X));return m(B(__tsover_mul(Y,Q),GS(k,D,o,V,c,q,X)),FS(O,d))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"vp\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"speedVariance\"},{type:\"i\",name:\"trailLength\"},{type:\"i\",name:\"strokeWidth\"},{type:\"i\",name:\"rounding\"},{type:\"i\",name:\"balance\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"vp\"]]],[13,\"corrUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centeredUV\",[6,[7,\"corrUV\",\"sub\"],[[6,\"d.vec2f\",[[1,\"aspect\",\"*\",[5,\"0.5\"]],[5,\"0.5\"]]]]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"u\",[1,[1,[7,\"centeredUV\",\"y\"],\"*\",\"cosA\"],\"-\",[1,[7,\"centeredUV\",\"x\"],\"*\",\"sinA\"]]],[13,\"v\",[1,[1,[7,\"centeredUV\",\"x\"],\"*\",\"cosA\"],\"+\",[1,[7,\"centeredUV\",\"y\"],\"*\",\"sinA\"]]],[13,\"colF\",[1,\"u\",\"*\",\"density\"]],[13,\"colIdx\",[6,\"std.floor\",[\"colF\"]]],[13,\"colLocalU\",[6,\"std.fract\",[\"colF\"]]],[13,\"h\",[6,\"colHash\",[\"colIdx\"]]],[13,\"speedMod\",[1,[5,\"1\"],\"+\",[1,[1,[1,[7,\"h\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"speedVariance\"],\"*\",[5,\"2\"]]]],[13,\"phase\",[7,\"h\",\"y\"]],[13,\"animOffset\",[6,\"std.fract\",[[1,\"animTime\",\"*\",\"speedMod\"]]]],[13,\"vPeriodic\",[6,\"std.fract\",[[1,[1,\"v\",\"-\",\"animOffset\"],\"-\",\"phase\"]]]],[13,\"trailLen\",\"trailLength\"],[13,\"withinStreak\",[6,\"std.clamp\",[[1,\"vPeriodic\",\"/\",\"trailLen\"],[5,\"0\"],[5,\"1\"]]]],[13,\"fwV\",[6,\"std.clamp\",[[6,\"std.fwidth\",[\"v\"]],[5,\"0.001\"],[5,\"0.1\"]]]],[13,\"fwU\",[6,\"std.clamp\",[[1,[6,\"std.fwidth\",[\"u\"]],\"*\",\"density\"],[5,\"0.001\"],[5,\"0.1\"]]]],[13,\"streakMask\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"trailLen\",\"-\",\"fwV\"],[1,\"trailLen\",\"+\",\"fwV\"],\"vPeriodic\"]]]],[13,\"hw\",[1,\"strokeWidth\",\"*\",[5,\"0.5\"]]],[13,\"distFromCenter\",[6,\"std.abs\",[[1,\"colLocalU\",\"-\",[5,\"0.5\"]]]]],[13,\"widthMask\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"hw\",\"-\",\"fwU\"],[1,\"hw\",\"+\",\"fwU\"],\"distFromCenter\"]]]],[13,\"rectMask\",[1,\"streakMask\",\"*\",\"widthMask\"]],[13,\"capAlpha\",[6,\"fallingCapAlpha\",[\"vPeriodic\",\"trailLen\",\"density\",\"hw\",\"rounding\",\"fwU\",\"distFromCenter\"]]],[13,\"finalMask\",[6,\"std.max\",[\"rectMask\",\"capAlpha\"]]],[10,[6,\"d.vec2f\",[\"finalMask\",[6,\"fallingBalanceRemap\",[\"withinStreak\",\"balance\"]]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.floor\":()=>ie,\"std.fract\":()=>Re,colHash:()=>OS,\"std.clamp\":()=>G,\"std.fwidth\":()=>mr,\"std.smoothstep\":()=>be,\"std.abs\":()=>re,fallingCapAlpha:()=>GS,\"std.max\":()=>B,fallingBalanceRemap:()=>FS}})&&e.f)({})),Ure=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return vT(t,s,r,__tsover_mul(o,bt))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cells\"},{type:\"i\",name:\"rotation\"}],body:[0,[[10,[6,\"cellCentreUVRotated\",[\"uv\",\"viewport\",\"cells\",[1,\"rotation\",\"*\",\"DEG_TO_RAD\"]]]]]]},externals:{cellCentreUVRotated:()=>vT,DEG_TO_RAD:()=>bt}})&&e.f)({})),Ore=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,.01),i=B(a,1e-4),l=__tsover_mul(i,2),c=__tsover_div(1,l),d=t.add(i),u=Bu(d),p=zu(d),_=__tsover_add(B(B(re(u.x),re(p.x)),1e-5),r),f=__tsover_add(B(B(re(u.y),re(p.y)),1e-5),r),h=d0(d.x,_,c),g=d0(d.y,f,c),y=__tsover_mul(__tsover_sub(1,h),__tsover_sub(1,g));return m(__tsover_mul(__tsover_sub(1,y),Et(1e-4,a)),mc(ie(t),o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gridUV\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"variation\"}],body:[0,[[13,\"lineWidth\",[1,\"thickness\",\"*\",[5,\"0.01\"]]],[13,\"safeLine\",[6,\"std.max\",[\"lineWidth\",[5,\"0.0001\"]]]],[13,\"lineFraction\",[1,\"safeLine\",\"*\",[5,\"2\"]]],[13,\"N\",[1,[5,\"1\"],\"/\",\"lineFraction\"]],[13,\"p\",[6,[7,\"gridUV\",\"add\"],[\"safeLine\"]]],[13,\"dpx\",[6,\"std.dpdx\",[\"p\"]]],[13,\"dpy\",[6,\"std.dpdy\",[\"p\"]]],[13,\"wx\",[1,[6,\"std.max\",[[6,\"std.max\",[[6,\"std.abs\",[[7,\"dpx\",\"x\"]]],[6,\"std.abs\",[[7,\"dpy\",\"x\"]]]]],[5,\"0.00001\"]]],\"+\",\"softness\"]],[13,\"wy\",[1,[6,\"std.max\",[[6,\"std.max\",[[6,\"std.abs\",[[7,\"dpx\",\"y\"]]],[6,\"std.abs\",[[7,\"dpy\",\"y\"]]]]],[5,\"0.00001\"]]],\"+\",\"softness\"]],[13,\"iX\",[6,\"quilezLineFilterAxis\",[[7,\"p\",\"x\"],\"wx\",\"N\"]]],[13,\"iY\",[6,\"quilezLineFilterAxis\",[[7,\"p\",\"y\"],\"wy\",\"N\"]]],[13,\"gapBoth\",[1,[1,[5,\"1\"],\"-\",\"iX\"],\"*\",[1,[5,\"1\"],\"-\",\"iY\"]]],[13,\"gridMask\",[1,[1,[5,\"1\"],\"-\",\"gapBoth\"],\"*\",[6,\"std.step\",[[5,\"0.0001\"],\"lineWidth\"]]]],[13,\"variedFactor\",[6,\"cellVariation\",[[6,\"std.floor\",[\"gridUV\"]],\"variation\"]]],[10,[6,\"d.vec2f\",[\"gridMask\",\"variedFactor\"]]]]]},externals:{\"std.max\":()=>B,\"std.dpdx\":()=>Bu,\"std.dpdy\":()=>zu,\"std.abs\":()=>re,quilezLineFilterAxis:()=>d0,\"std.step\":()=>Et,cellVariation:()=>mc,\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),Gre=w.fn([m,m,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=ov(t,s),p=u.mul(r),_=ie(p.y),f=cv(lv(_),i),h=__tsover_mul(c,f),g=__tsover_mul(a,Ou(_,2)),y=Fu(m(__tsover_sub(__tsover_sub(p.x,g),h),p.y)),b=he(y.local.sub(.5)),x=he(mr(u.mul(r))),P=xT(b,__tsover_mul(o,.5),x),C=Re(__tsover_mul(ae(__tsover_add(__tsover_mul(y.cell.x,12.9898),__tsover_mul(y.cell.y,78.233))),43758.5453)),E=__tsover_add(__tsover_mul(d,2),__tsover_mul(C,Jt)),R=K(1,__tsover_add(__tsover_mul(ae(E),.5),.5),l);return __tsover_mul(P,R)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"density\"},{type:\"i\",name:\"dotSize\"},{type:\"i\",name:\"offset\"},{type:\"i\",name:\"speedVariance\"},{type:\"i\",name:\"twinkle\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"time\"}],body:[0,[[13,\"correctedUV\",[6,\"aspectCorrectedUVFlipY\",[\"uv\",\"viewport\"]]],[13,\"gridUV\",[6,[7,\"correctedUV\",\"mul\"],[\"density\"]]],[13,\"rowIndex\",[6,\"std.floor\",[[7,\"gridUV\",\"y\"]]]],[13,\"speedMod\",[6,\"rowSpeedMultiplier\",[[6,\"rowSpeedHash\",[\"rowIndex\"]],\"speedVariance\"]]],[13,\"drift\",[1,\"animTime\",\"*\",\"speedMod\"]],[13,\"staticStagger\",[1,\"offset\",\"*\",[6,\"flooredMod1\",[\"rowIndex\",[5,\"2\"]]]]],[13,\"shiftedGrid\",[6,\"d.vec2f\",[[1,[1,[7,\"gridUV\",\"x\"],\"-\",\"staticStagger\"],\"-\",\"drift\"],[7,\"gridUV\",\"y\"]]]],[13,\"tile\",[6,\"squareTiling\",[\"shiftedGrid\"]]],[13,\"centerDistance\",[6,\"std.length\",[[6,[7,[7,\"tile\",\"local\"],\"sub\"],[[5,\"0.5\"]]]]]],[13,\"pixelSize\",[6,\"std.length\",[[6,\"std.fwidth\",[[6,[7,\"correctedUV\",\"mul\"],[\"density\"]]]]]]],[13,\"coverage\",[6,\"discCoverage\",[\"centerDistance\",[1,\"dotSize\",\"*\",[5,\"0.5\"]],\"pixelSize\"]]],[13,\"dotPhase\",[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,[1,[7,[7,\"tile\",\"cell\"],\"x\"],\"*\",[5,\"12.9898\"]],\"+\",[1,[7,[7,\"tile\",\"cell\"],\"y\"],\"*\",[5,\"78.233\"]]]]],\"*\",[5,\"43758.5453\"]]]]],[13,\"twinkleTime\",[1,[1,\"time\",\"*\",[5,\"2\"]],\"+\",[1,\"dotPhase\",\"*\",\"TAU\"]]],[13,\"twinkleValue\",[1,[1,[6,\"std.sin\",[\"twinkleTime\"]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[13,\"twinkleModifier\",[6,\"std.mix\",[[5,\"1\"],\"twinkleValue\",\"twinkle\"]]],[10,[1,\"coverage\",\"*\",\"twinkleModifier\"]]]]},externals:{aspectCorrectedUVFlipY:()=>ov,\"std.floor\":()=>ie,rowSpeedMultiplier:()=>cv,rowSpeedHash:()=>lv,flooredMod1:()=>Ou,\"d.vec2f\":()=>m,squareTiling:()=>Fu,\"std.length\":()=>he,\"std.fwidth\":()=>mr,discCoverage:()=>xT,\"std.fract\":()=>Re,\"std.sin\":()=>ae,TAU:()=>Jt,\"std.mix\":()=>K}})&&e.f)({})),Fre=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=nv(t,m(lc,1)),i=mc(m(Ut(__tsover_mul(a.id.x,LS)),Ut(__tsover_mul(a.id.y,2))),o),l=m(re(a.gv.x),re(a.gv.y)),c=B(Se(l,m(hu,.5)),l.y);return m(Cv(__tsover_sub(.5,c),__tsover_mul(s,.01),__tsover_mul(mr(t.x),.5),r),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"variation\"}],body:[0,[[13,\"hex\",[6,\"hexTiling\",[\"scaledUV\",[6,\"d.vec2f\",[\"SQRT3\",[5,\"1\"]]]]]],[13,\"ix\",[6,\"std.round\",[[1,[7,[7,\"hex\",\"id\"],\"x\"],\"*\",\"INV_SQRT3_2\"]]]],[13,\"iy\",[6,\"std.round\",[[1,[7,[7,\"hex\",\"id\"],\"y\"],\"*\",[5,\"2\"]]]]],[13,\"variedFactor\",[6,\"cellVariation\",[[6,\"d.vec2f\",[\"ix\",\"iy\"]],\"variation\"]]],[13,\"pAbs\",[6,\"d.vec2f\",[[6,\"std.abs\",[[7,[7,\"hex\",\"gv\"],\"x\"]]],[6,\"std.abs\",[[7,[7,\"hex\",\"gv\"],\"y\"]]]]]],[13,\"hexDist\",[6,\"std.max\",[[6,\"std.dot\",[\"pAbs\",[6,\"d.vec2f\",[\"SQRT3_2\",[5,\"0.5\"]]]]],[7,\"pAbs\",\"y\"]]]],[13,\"edgeDist\",[1,[5,\"0.5\"],\"-\",\"hexDist\"]],[13,\"lineWidth\",[1,\"thickness\",\"*\",[5,\"0.01\"]]],[13,\"pixel\",[1,[6,\"std.fwidth\",[[7,\"scaledUV\",\"x\"]]],\"*\",[5,\"0.5\"]]],[13,\"lineMask\",[6,\"strokeBandFloored\",[\"edgeDist\",\"lineWidth\",\"pixel\",\"softness\"]]],[10,[6,\"d.vec2f\",[\"lineMask\",\"variedFactor\"]]]]]},externals:{hexTiling:()=>nv,\"d.vec2f\":()=>m,SQRT3:()=>lc,\"std.round\":()=>Ut,INV_SQRT3_2:()=>LS,cellVariation:()=>mc,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.dot\":()=>Se,SQRT3_2:()=>hu,\"std.fwidth\":()=>mr,strokeBandFloored:()=>Cv}})&&e.f)({})),Ire=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=fT(t),c=cv(lv(ie(l.y)),a),d=__tsover_mul(i,c),u=m(__tsover_sub(l.x,d),l.y),p=ie(u),_=Re(u),f=Et(1,__tsover_add(_.x,_.y)),h=mc(m(__tsover_add(__tsover_mul(p.x,2),f),p.y),o);return m(Mv(ye(ye(ye(_.x,__tsover_sub(1,_.x)),ye(_.y,__tsover_sub(1,_.y))),re(__tsover_sub(__tsover_add(_.x,_.y),1))),__tsover_mul(s,.02),__tsover_add(B(mr(l.x),mr(l.y)),__tsover_mul(r,.5))),h)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"variation\"},{type:\"i\",name:\"speedVariance\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"ps\",[6,\"triLattice\",[\"p\"]]],[13,\"rowIndex\",[6,\"std.floor\",[[7,\"ps\",\"y\"]]]],[13,\"speedMod\",[6,\"rowSpeedMultiplier\",[[6,\"rowSpeedHash\",[\"rowIndex\"]],\"speedVariance\"]]],[13,\"drift\",[1,\"animTime\",\"*\",\"speedMod\"]],[13,\"psShifted\",[6,\"d.vec2f\",[[1,[7,\"ps\",\"x\"],\"-\",\"drift\"],[7,\"ps\",\"y\"]]]],[13,\"cell\",[6,\"std.floor\",[\"psShifted\"]]],[13,\"f\",[6,\"std.fract\",[\"psShifted\"]]],[13,\"upper\",[6,\"std.step\",[[5,\"1\"],[1,[7,\"f\",\"x\"],\"+\",[7,\"f\",\"y\"]]]]],[13,\"triId\",[6,\"d.vec2f\",[[1,[1,[7,\"cell\",\"x\"],\"*\",[5,\"2\"]],\"+\",\"upper\"],[7,\"cell\",\"y\"]]]],[13,\"variedFactor\",[6,\"cellVariation\",[\"triId\",\"variation\"]]],[13,\"dEdge\",[6,\"std.min\",[[6,\"std.min\",[[6,\"std.min\",[[7,\"f\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"f\",\"x\"]]]],[6,\"std.min\",[[7,\"f\",\"y\"],[1,[5,\"1\"],\"-\",[7,\"f\",\"y\"]]]]]],[6,\"std.abs\",[[1,[1,[7,\"f\",\"x\"],\"+\",[7,\"f\",\"y\"]],\"-\",[5,\"1\"]]]]]]],[13,\"lineWidth\",[1,\"thickness\",\"*\",[5,\"0.02\"]]],[13,\"aaWidth\",[1,[6,\"std.max\",[[6,\"std.fwidth\",[[7,\"ps\",\"x\"]]],[6,\"std.fwidth\",[[7,\"ps\",\"y\"]]]]],\"+\",[1,\"softness\",\"*\",[5,\"0.5\"]]]],[13,\"lineMask\",[6,\"strokeBandSymmetric\",[\"dEdge\",\"lineWidth\",\"aaWidth\"]]],[10,[6,\"d.vec2f\",[\"lineMask\",\"variedFactor\"]]]]]},externals:{triLattice:()=>fT,\"std.floor\":()=>ie,rowSpeedMultiplier:()=>cv,rowSpeedHash:()=>lv,\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.step\":()=>Et,cellVariation:()=>mc,\"std.min\":()=>ye,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.fwidth\":()=>mr,strokeBandSymmetric:()=>Mv}})&&e.f)({})),IS=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s),a=dc(t,o),i=__tsover_mul(r,bt),l=Pn(a,pe(i),-ae(i));return m(__tsover_add(__tsover_div(l.x,o),.5),__tsover_add(l.y,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"rotation\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"centred\",[6,\"canvasCentredDelta\",[\"uv\",\"aspect\"]]],[13,\"rotRad\",[1,\"rotation\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rot\",[6,\"rotate2\",[\"centred\",\"cosR\",[4,\"-\",\"sinR\"]]]],[10,[6,\"d.vec2f\",[[1,[1,[7,\"rot\",\"x\"],\"/\",\"aspect\"],\"+\",[5,\"0.5\"]],[1,[7,\"rot\",\"y\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{aspectOf:()=>Cs,canvasCentredDelta:()=>dc,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,rotate2:()=>Pn,\"d.vec2f\":()=>m}})&&e.f)({})),YS=w.fn([n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Ou(t,2);return m(__tsover_mul(o,.5),K(__tsover_sub(__tsover_mul(o,2),1),__tsover_mul(__tsover_sub(Re(__tsover_mul(ae(__tsover_add(t,__tsover_mul(s,.137))),43758.5453)),.5),2),r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"row\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"speedVariance\"}],body:[0,[[13,\"rowMod2\",[6,\"flooredMod1\",[\"row\",[5,\"2\"]]]],[13,\"rowOffset\",[1,\"rowMod2\",\"*\",[5,\"0.5\"]]],[13,\"rowDir\",[1,[1,\"rowMod2\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"rowHash\",[1,[1,[6,\"std.fract\",[[1,[6,\"std.sin\",[[1,\"row\",\"+\",[1,\"seed\",\"*\",[5,\"0.137\"]]]]],\"*\",[5,\"43758.5453\"]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[10,[6,\"d.vec2f\",[\"rowOffset\",[6,\"std.mix\",[\"rowDir\",\"rowHash\",\"speedVariance\"]]]]]]]},externals:{flooredMod1:()=>Ou,\"std.fract\":()=>Re,\"std.sin\":()=>ae,\"d.vec2f\":()=>m,\"std.mix\":()=>K}})&&e.f)({})),WS=w.fn([m,m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=xu(t.x,__tsover_sub(s.x,r.x),__tsover_add(s.x,r.x)),a=xu(t.y,__tsover_sub(s.y,r.y),__tsover_add(s.y,r.y));return __tsover_mul(o,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cellUV\"},{type:\"i\",name:\"mortarUV\"},{type:\"i\",name:\"aaUV\"}],body:[0,[[13,\"brickU\",[6,\"bandMask\",[[7,\"cellUV\",\"x\"],[1,[7,\"mortarUV\",\"x\"],\"-\",[7,\"aaUV\",\"x\"]],[1,[7,\"mortarUV\",\"x\"],\"+\",[7,\"aaUV\",\"x\"]]]]],[13,\"brickV\",[6,\"bandMask\",[[7,\"cellUV\",\"y\"],[1,[7,\"mortarUV\",\"y\"],\"-\",[7,\"aaUV\",\"y\"]],[1,[7,\"mortarUV\",\"y\"],\"+\",[7,\"aaUV\",\"y\"]]]]],[10,[1,\"brickU\",\"*\",\"brickV\"]]]]},externals:{bandMask:()=>xu}})&&e.f)({})),Yre=w.fn([m,m,n,n,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_)=>{\"use gpu\";let f=Cs(s),h=IS(t,s,c),g=h.x,y=h.y,b=__tsover_mul(a,.3),x=__tsover_div(__tsover_mul(__tsover_mul(b,f),o),r),P=ie(__tsover_mul(y,o)),C=YS(P,p,u),E=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(g,r),C.x),__tsover_mul(_,C.y)),d),R=__tsover_mul(y,o),U=Re(E),L=Re(R),k=__tsover_mul(i,.5),D=__tsover_add(mr(E),k),O=__tsover_add(mr(R),k);return m(WS(m(U,L),m(b,x),m(D,O)),mc(m(ie(E),P),l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cellsX\"},{type:\"i\",name:\"cellsY\"},{type:\"i\",name:\"mortar\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"variation\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"offset\"},{type:\"i\",name:\"speedVariance\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"frame\",[6,\"brickFrame\",[\"uv\",\"viewport\",\"rotation\"]]],[13,\"u\",[7,\"frame\",\"x\"]],[13,\"v\",[7,\"frame\",\"y\"]],[13,\"mortarU\",[1,\"mortar\",\"*\",[5,\"0.3\"]]],[13,\"mortarV\",[1,[1,[1,\"mortarU\",\"*\",\"aspect\"],\"*\",\"cellsY\"],\"/\",\"cellsX\"]],[13,\"row\",[6,\"std.floor\",[[1,\"v\",\"*\",\"cellsY\"]]]],[13,\"stagger\",[6,\"brickRowStagger\",[\"row\",\"seed\",\"speedVariance\"]]],[13,\"brickArg\",[1,[1,[1,[1,\"u\",\"*\",\"cellsX\"],\"+\",[7,\"stagger\",\"x\"]],\"+\",[1,\"animTime\",\"*\",[7,\"stagger\",\"y\"]]],\"+\",\"offset\"]],[13,\"vScaled\",[1,\"v\",\"*\",\"cellsY\"]],[13,\"cellU\",[6,\"std.fract\",[\"brickArg\"]]],[13,\"cellV\",[6,\"std.fract\",[\"vScaled\"]]],[13,\"soft\",[1,\"softness\",\"*\",[5,\"0.5\"]]],[13,\"aaU\",[1,[6,\"std.fwidth\",[\"brickArg\"]],\"+\",\"soft\"]],[13,\"aaV\",[1,[6,\"std.fwidth\",[\"vScaled\"]],\"+\",\"soft\"]],[13,\"brickMask\",[6,\"brickMortarMask\",[[6,\"d.vec2f\",[\"cellU\",\"cellV\"]],[6,\"d.vec2f\",[\"mortarU\",\"mortarV\"]],[6,\"d.vec2f\",[\"aaU\",\"aaV\"]]]]],[13,\"brickId\",[6,\"d.vec2f\",[[6,\"std.floor\",[\"brickArg\"]],\"row\"]]],[13,\"variedFactor\",[6,\"cellVariation\",[\"brickId\",\"variation\"]]],[10,[6,\"d.vec2f\",[\"brickMask\",\"variedFactor\"]]]]]},externals:{aspectOf:()=>Cs,brickFrame:()=>IS,\"std.floor\":()=>ie,brickRowStagger:()=>YS,\"std.fract\":()=>Re,\"std.fwidth\":()=>mr,brickMortarMask:()=>WS,\"d.vec2f\":()=>m,cellVariation:()=>mc}})&&e.f)({})),qS=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Se(t,m(127.1,311.7));return Re(__tsover_mul(ae(s),43758.5453))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"h\",[6,\"std.dot\",[\"p\",[6,\"d.vec2f\",[[5,\"127.1\"],[5,\"311.7\"]]]]]],[10,[6,\"std.fract\",[[1,[6,\"std.sin\",[\"h\"]],\"*\",[5,\"43758.5453\"]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec2f\":()=>m,\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({})),Wre=w.fn([m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Fu(t),i=qS(m(__tsover_add(a.cell.x,o),__tsover_add(a.cell.y,o)))>.5,l=N(n(0),n(1),i),c=N(n(1),n(0),i);return Cv(ye(re(__tsover_sub(he(m(__tsover_sub(a.local.x,l),a.local.y)),.5)),re(__tsover_sub(he(m(__tsover_sub(a.local.x,c),__tsover_sub(a.local.y,1))),.5))),__tsover_mul(s,.005),__tsover_mul(mr(t.x),.5),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"tile\",[6,\"squareTiling\",[\"scaledUV\"]]],[13,\"h\",[6,\"truchetHash\",[[6,\"d.vec2f\",[[1,[7,[7,\"tile\",\"cell\"],\"x\"],\"+\",\"seed\"],[1,[7,[7,\"tile\",\"cell\"],\"y\"],\"+\",\"seed\"]]]]]],[13,\"flip\",[1,\"h\",\">\",[5,\"0.5\"]]],[13,\"c1x\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],\"flip\"]]],[13,\"c2x\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[5,\"0\"]]],\"flip\"]]],[13,\"d1\",[6,\"std.abs\",[[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,[7,\"tile\",\"local\"],\"x\"],\"-\",\"c1x\"],[7,[7,\"tile\",\"local\"],\"y\"]]]]],\"-\",[5,\"0.5\"]]]]],[13,\"d2\",[6,\"std.abs\",[[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,[7,\"tile\",\"local\"],\"x\"],\"-\",\"c2x\"],[1,[7,[7,\"tile\",\"local\"],\"y\"],\"-\",[5,\"1\"]]]]]],\"-\",[5,\"0.5\"]]]]],[13,\"arcDist\",[6,\"std.min\",[\"d1\",\"d2\"]]],[13,\"lineWidth\",[1,\"thickness\",\"*\",[5,\"0.005\"]]],[13,\"pixel\",[1,[6,\"std.fwidth\",[[7,\"scaledUV\",\"x\"]]],\"*\",[5,\"0.5\"]]],[10,[6,\"strokeBandFloored\",[\"arcDist\",\"lineWidth\",\"pixel\",\"softness\"]]]]]},externals:{squareTiling:()=>Fu,truchetHash:()=>qS,\"d.vec2f\":()=>m,\"std.select\":()=>N,\"d.f32\":()=>n,\"std.abs\":()=>re,\"std.length\":()=>he,\"std.min\":()=>ye,\"std.fwidth\":()=>mr,strokeBandFloored:()=>Cv}})&&e.f)({})),qre=w.fn([m,n,S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Fu(t),i=__tsover_mul(mr(t.x),.5),l=xu(a.local.y,s,__tsover_add(s,i)),c=xu(a.local.x,s,__tsover_add(s,i)),d=Ou(__tsover_add(a.cell.x,a.cell.y),2),u=__tsover_mul(l,c),p=G(__tsover_sub(l,__tsover_mul(u,d)),0,1),_=G(__tsover_sub(c,__tsover_mul(u,__tsover_sub(1,d))),0,1),f=__tsover_mul(r.w,p),h=__tsover_mul(o.w,_),g=G(__tsover_add(f,h),0,1),y=B(__tsover_add(f,h),.001);return S(__tsover_div(__tsover_add(__tsover_mul(r.x,f),__tsover_mul(o.x,h)),y),__tsover_div(__tsover_add(__tsover_mul(r.y,f),__tsover_mul(o.y,h)),y),__tsover_div(__tsover_add(__tsover_mul(r.z,f),__tsover_mul(o.z,h)),y),g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"gap\"},{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"}],body:[0,[[13,\"tile\",[6,\"squareTiling\",[\"scaledUV\"]]],[13,\"pixel\",[1,[6,\"std.fwidth\",[[7,\"scaledUV\",\"x\"]]],\"*\",[5,\"0.5\"]]],[13,\"hBand\",[6,\"bandMask\",[[7,[7,\"tile\",\"local\"],\"y\"],\"gap\",[1,\"gap\",\"+\",\"pixel\"]]]],[13,\"vBand\",[6,\"bandMask\",[[7,[7,\"tile\",\"local\"],\"x\"],\"gap\",[1,\"gap\",\"+\",\"pixel\"]]]],[13,\"isHTopFloat\",[6,\"flooredMod1\",[[1,[7,[7,\"tile\",\"cell\"],\"x\"],\"+\",[7,[7,\"tile\",\"cell\"],\"y\"]],[5,\"2\"]]]],[13,\"intersection\",[1,\"hBand\",\"*\",\"vBand\"]],[13,\"hVis\",[6,\"std.clamp\",[[1,\"hBand\",\"-\",[1,\"intersection\",\"*\",\"isHTopFloat\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"vVis\",[6,\"std.clamp\",[[1,\"vBand\",\"-\",[1,\"intersection\",\"*\",[1,[5,\"1\"],\"-\",\"isHTopFloat\"]]],[5,\"0\"],[5,\"1\"]]]],[13,\"weightA\",[1,[7,\"colorA\",\"w\"],\"*\",\"hVis\"]],[13,\"weightB\",[1,[7,\"colorB\",\"w\"],\"*\",\"vVis\"]],[13,\"totalAlpha\",[6,\"std.clamp\",[[1,\"weightA\",\"+\",\"weightB\"],[5,\"0\"],[5,\"1\"]]]],[13,\"safeWeight\",[6,\"std.max\",[[1,\"weightA\",\"+\",\"weightB\"],[5,\"0.001\"]]]],[13,\"r\",[1,[1,[1,[7,\"colorA\",\"x\"],\"*\",\"weightA\"],\"+\",[1,[7,\"colorB\",\"x\"],\"*\",\"weightB\"]],\"/\",\"safeWeight\"]],[13,\"g\",[1,[1,[1,[7,\"colorA\",\"y\"],\"*\",\"weightA\"],\"+\",[1,[7,\"colorB\",\"y\"],\"*\",\"weightB\"]],\"/\",\"safeWeight\"]],[13,\"b\",[1,[1,[1,[7,\"colorA\",\"z\"],\"*\",\"weightA\"],\"+\",[1,[7,\"colorB\",\"z\"],\"*\",\"weightB\"]],\"/\",\"safeWeight\"]],[10,[6,\"d.vec4f\",[\"r\",\"g\",\"b\",\"totalAlpha\"]]]]]},externals:{squareTiling:()=>Fu,\"std.fwidth\":()=>mr,bandMask:()=>xu,flooredMod1:()=>Ou,\"std.clamp\":()=>G,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&e.f)({})),VS=2/Math.sqrt(3),Vre=w.fn([m,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=nv(t,m(1,lc)),a=Se(o.gv,m(0,1)),i=Se(o.gv,m(-hu,-.5)),l=Se(o.gv,m(hu,-.5)),c=a>=B(i,l),d=N(N(.74,.5,i>=l),1,c),u=iv(m(Ut(__tsover_mul(o.id.x,2)),Ut(__tsover_mul(o.id.y,VS)))),p=B(a,B(i,l)),_=ye(a,ye(i,l)),f=__tsover_sub(__tsover_sub(__tsover_add(__tsover_add(a,i),l),p),_),h=__tsover_sub(p,f),g=m(re(o.gv.x),re(o.gv.y)),y=B(Se(g,m(.5,hu)),g.x);return A(u,d,Mv(ye(h,__tsover_sub(.5,y)),__tsover_mul(s,.02),__tsover_add(__tsover_mul(mr(t.x),.5),__tsover_mul(r,.5))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"scaledUV\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"}],body:[0,[[13,\"hex\",[6,\"hexTiling\",[\"scaledUV\",[6,\"d.vec2f\",[[5,\"1\"],\"SQRT3\"]]]]],[13,\"s0\",[6,\"std.dot\",[[7,\"hex\",\"gv\"],[6,\"d.vec2f\",[[5,\"0\"],[5,\"1\"]]]]]],[13,\"s1\",[6,\"std.dot\",[[7,\"hex\",\"gv\"],[6,\"d.vec2f\",[[4,\"-\",\"SQRT3_2\"],[4,\"-\",[5,\"0.5\"]]]]]]],[13,\"s2\",[6,\"std.dot\",[[7,\"hex\",\"gv\"],[6,\"d.vec2f\",[\"SQRT3_2\",[4,\"-\",[5,\"0.5\"]]]]]]],[13,\"isTop\",[1,\"s0\",\">=\",[6,\"std.max\",[\"s1\",\"s2\"]]]],[13,\"isLeft\",[1,\"s1\",\">=\",\"s2\"]],[13,\"tone\",[6,\"std.select\",[[6,\"std.select\",[[5,\"0.74\"],[5,\"0.5\"],\"isLeft\"]],[5,\"1\"],\"isTop\"]]],[13,\"ix\",[6,\"std.round\",[[1,[7,[7,\"hex\",\"id\"],\"x\"],\"*\",[5,\"2\"]]]]],[13,\"iy\",[6,\"std.round\",[[1,[7,[7,\"hex\",\"id\"],\"y\"],\"*\",\"INV_2_SQRT3\"]]]],[13,\"rand\",[6,\"cellHash\",[[6,\"d.vec2f\",[\"ix\",\"iy\"]]]]],[13,\"mx\",[6,\"std.max\",[\"s0\",[6,\"std.max\",[\"s1\",\"s2\"]]]]],[13,\"mn\",[6,\"std.min\",[\"s0\",[6,\"std.min\",[\"s1\",\"s2\"]]]]],[13,\"midScore\",[1,[1,[1,[1,\"s0\",\"+\",\"s1\"],\"+\",\"s2\"],\"-\",\"mx\"],\"-\",\"mn\"]],[13,\"internal\",[1,\"mx\",\"-\",\"midScore\"]],[13,\"pAbs\",[6,\"d.vec2f\",[[6,\"std.abs\",[[7,[7,\"hex\",\"gv\"],\"x\"]]],[6,\"std.abs\",[[7,[7,\"hex\",\"gv\"],\"y\"]]]]]],[13,\"hexDist\",[6,\"std.max\",[[6,\"std.dot\",[\"pAbs\",[6,\"d.vec2f\",[[5,\"0.5\"],\"SQRT3_2\"]]]],[7,\"pAbs\",\"x\"]]]],[13,\"rimDist\",[1,[5,\"0.5\"],\"-\",\"hexDist\"]],[13,\"edgeField\",[6,\"std.min\",[\"internal\",\"rimDist\"]]],[13,\"lineWidth\",[1,\"thickness\",\"*\",[5,\"0.02\"]]],[13,\"aaWidth\",[1,[1,[6,\"std.fwidth\",[[7,\"scaledUV\",\"x\"]]],\"*\",[5,\"0.5\"]],\"+\",[1,\"softness\",\"*\",[5,\"0.5\"]]]],[13,\"wire\",[6,\"strokeBandSymmetric\",[\"edgeField\",\"lineWidth\",\"aaWidth\"]]],[10,[6,\"d.vec3f\",[\"rand\",\"tone\",\"wire\"]]]]]},externals:{hexTiling:()=>nv,\"d.vec2f\":()=>m,SQRT3:()=>lc,\"std.dot\":()=>Se,SQRT3_2:()=>hu,\"std.max\":()=>B,\"std.select\":()=>N,\"std.round\":()=>Ut,INV_2_SQRT3:()=>VS,cellHash:()=>iv,\"std.min\":()=>ye,\"std.abs\":()=>re,\"std.fwidth\":()=>mr,strokeBandSymmetric:()=>Mv,\"d.vec3f\":()=>A}})&&e.f)({})),ed=.1,Nre=w.fn([S,m,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(s.x,r),s.y),l=__tsover_mul(o,bt),c=pe(l),d=ae(l),u=Re(m(Se(i,m(c,__tsover_mul(d,-1))),Se(i,m(d,c))).mul(a)).sub(m(.5,.5)),p=Se(A(t.x,t.y,t.z),A(.299,.587,.114)),_=__tsover_add(__tsover_mul(p,.7),.15),f=he(u),h=__tsover_sub(1,be(__tsover_sub(_,ed),__tsover_add(_,ed),f));return S(__tsover_mul(t.x,h),__tsover_mul(t.y,h),__tsover_mul(t.z,h),__tsover_mul(t.w,h))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"childColor\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"frequency\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"c\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"s\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rotatedUV\",[6,\"d.vec2f\",[[6,\"std.dot\",[\"aspectUV\",[6,\"d.vec2f\",[\"c\",[1,\"s\",\"*\",[4,\"-\",[5,\"1\"]]]]]]],[6,\"std.dot\",[\"aspectUV\",[6,\"d.vec2f\",[\"s\",\"c\"]]]]]]],[13,\"gridUV\",[6,[7,[6,\"std.fract\",[[6,[7,\"rotatedUV\",\"mul\"],[\"frequency\"]]]],\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"brightness\",[6,\"std.dot\",[[6,\"d.vec3f\",[[7,\"childColor\",\"x\"],[7,\"childColor\",\"y\"],[7,\"childColor\",\"z\"]]],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"dotSize\",[1,[1,\"brightness\",\"*\",[5,\"0.7\"]],\"+\",[5,\"0.15\"]]],[13,\"dist\",[6,\"std.length\",[\"gridUV\"]]],[13,\"dotPattern\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"dotSize\",\"-\",\"SMOOTHNESS\"],[1,\"dotSize\",\"+\",\"SMOOTHNESS\"],\"dist\"]]]],[10,[6,\"d.vec4f\",[[1,[7,\"childColor\",\"x\"],\"*\",\"dotPattern\"],[1,[7,\"childColor\",\"y\"],\"*\",\"dotPattern\"],[1,[7,\"childColor\",\"z\"],\"*\",\"dotPattern\"],[1,[7,\"childColor\",\"w\"],\"*\",\"dotPattern\"]]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.fract\":()=>Re,\"d.vec3f\":()=>A,\"std.length\":()=>he,\"std.smoothstep\":()=>be,SMOOTHNESS:()=>ed,\"d.vec4f\":()=>S}})&&e.f)({})),$re=w.fn([m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(__tsover_add(s,r),bt);return m(__tsover_add(t.x,__tsover_mul(pe(i),o)),__tsover_add(t.y,__tsover_div(__tsover_mul(ae(i),o),a)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"misprintAngleDeg\"},{type:\"i\",name:\"plateIndexDeg\"},{type:\"i\",name:\"misprint\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"angleRad\",[1,[1,\"misprintAngleDeg\",\"+\",\"plateIndexDeg\"],\"*\",\"DEG_TO_RAD\"]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,[6,\"std.cos\",[\"angleRad\"]],\"*\",\"misprint\"]],[1,[7,\"uv\",\"y\"],\"+\",[1,[1,[6,\"std.sin\",[\"angleRad\"]],\"*\",\"misprint\"],\"/\",\"aspect\"]]]]]]]},externals:{DEG_TO_RAD:()=>bt,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),bd=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_sub(1,B(B(t.x,t.y),t.z))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.max\",[[6,\"std.max\",[[7,\"s\",\"x\"],[7,\"s\",\"y\"]]],[7,\"s\",\"z\"]]]]]]]},externals:{\"std.max\":()=>B}})&&e.f)({})),Xre=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=bd(t),r=B(__tsover_sub(1,s),1e-4);return __tsover_div(__tsover_sub(__tsover_sub(1,t.x),s),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[13,\"k\",[6,\"halftoneChannelK\",[\"s\"]]],[13,\"invK\",[6,\"std.max\",[[1,[5,\"1\"],\"-\",\"k\"],[5,\"0.0001\"]]]],[10,[1,[1,[1,[5,\"1\"],\"-\",[7,\"s\",\"x\"]],\"-\",\"k\"],\"/\",\"invK\"]]]]},externals:{halftoneChannelK:()=>bd,\"std.max\":()=>B}})&&e.f)({})),Hre=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=bd(t),r=B(__tsover_sub(1,s),1e-4);return __tsover_div(__tsover_sub(__tsover_sub(1,t.y),s),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[13,\"k\",[6,\"halftoneChannelK\",[\"s\"]]],[13,\"invK\",[6,\"std.max\",[[1,[5,\"1\"],\"-\",\"k\"],[5,\"0.0001\"]]]],[10,[1,[1,[1,[5,\"1\"],\"-\",[7,\"s\",\"y\"]],\"-\",\"k\"],\"/\",\"invK\"]]]]},externals:{halftoneChannelK:()=>bd,\"std.max\":()=>B}})&&e.f)({})),jre=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=bd(t),r=B(__tsover_sub(1,s),1e-4);return __tsover_div(__tsover_sub(__tsover_sub(1,t.z),s),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[13,\"k\",[6,\"halftoneChannelK\",[\"s\"]]],[13,\"invK\",[6,\"std.max\",[[1,[5,\"1\"],\"-\",\"k\"],[5,\"0.0001\"]]]],[10,[1,[1,[1,[5,\"1\"],\"-\",[7,\"s\",\"z\"]],\"-\",\"k\"],\"/\",\"invK\"]]]]},externals:{halftoneChannelK:()=>bd,\"std.max\":()=>B}})&&e.f)({})),Kre=w.fn([m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(t.x,s),t.y),l=__tsover_mul(r,bt),c=pe(l),d=ae(l),u=he(Re(m(Se(i,m(c,__tsover_mul(d,-1))),Se(i,m(d,c))).mul(a)).sub(m(.5,.5))),p=__tsover_sub(__tsover_mul(o,.75),ed);return __tsover_sub(1,be(__tsover_sub(p,ed),__tsover_add(p,ed),u))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"frequency\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"c\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"s\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rotatedUV\",[6,\"d.vec2f\",[[6,\"std.dot\",[\"aspectUV\",[6,\"d.vec2f\",[\"c\",[1,\"s\",\"*\",[4,\"-\",[5,\"1\"]]]]]]],[6,\"std.dot\",[\"aspectUV\",[6,\"d.vec2f\",[\"s\",\"c\"]]]]]]],[13,\"grid\",[6,[7,[6,\"std.fract\",[[6,[7,\"rotatedUV\",\"mul\"],[\"frequency\"]]]],\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"dist\",[6,\"std.length\",[\"grid\"]]],[13,\"size\",[1,[1,\"intensity\",\"*\",[5,\"0.75\"]],\"-\",\"SMOOTHNESS\"]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"size\",\"-\",\"SMOOTHNESS\"],[1,\"size\",\"+\",\"SMOOTHNESS\"],\"dist\"]]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.fract\":()=>Re,\"std.length\":()=>he,SMOOTHNESS:()=>ed,\"std.smoothstep\":()=>be}})&&e.f)({})),Qre=w.fn([S,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return K(A(1,1,1),A(t.x,t.y,t.z),A(__tsover_mul(s,t.w)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"inkColor\"},{type:\"i\",name:\"inkMask\"}],body:[0,[[10,[6,\"std.mix\",[[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],[6,\"d.vec3f\",[[7,\"inkColor\",\"x\"],[7,\"inkColor\",\"y\"],[7,\"inkColor\",\"z\"]]],[6,\"d.vec3f\",[[1,\"inkMask\",\"*\",[7,\"inkColor\",\"w\"]]]]]]]]]},externals:{\"std.mix\":()=>K,\"d.vec3f\":()=>A}})&&e.f)({})),Zre=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return ie(m(__tsover_div(__tsover_mul(t.x,s.x),r),__tsover_div(__tsover_mul(t.y,s.y),r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"gridRes\"},{type:\"i\",name:\"pixelSize\"}],body:[0,[[10,[6,\"std.floor\",[[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"*\",[7,\"gridRes\",\"x\"]],\"/\",\"pixelSize\"],[1,[1,[7,\"uv\",\"y\"],\"*\",[7,\"gridRes\",\"y\"]],\"/\",\"pixelSize\"]]]]]]]]},externals:{\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),Jre=w.fn([m,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(__tsover_div(__tsover_mul(__tsover_add(t.x,.5),s),r.x),__tsover_div(__tsover_mul(__tsover_add(t.y,.5),s),r.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"},{type:\"i\",name:\"pixelSize\"},{type:\"i\",name:\"gridRes\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[1,[7,\"coord\",\"x\"],\"+\",[5,\"0.5\"]],\"*\",\"pixelSize\"],\"/\",[7,\"gridRes\",\"x\"]],[1,[1,[1,[7,\"coord\",\"y\"],\"+\",[5,\"0.5\"]],\"*\",\"pixelSize\"],\"/\",[7,\"gridRes\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),NS=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(Se(A(t.x,t.y,t.z),A(.299,.587,.114)),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"c\"}],body:[0,[[10,[1,[6,\"std.dot\",[[6,\"d.vec3f\",[[7,\"c\",\"x\"],[7,\"c\",\"y\"],[7,\"c\",\"z\"]]],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]],\"*\",[7,\"c\",\"w\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),u1=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_sub(__tsover_add(__tsover_mul(s,3),__tsover_mul(t,2)),__tsover_mul(__tsover_mul(t,s),4))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"}],body:[0,[[10,[1,[1,[1,\"b\",\"*\",[5,\"3\"]],\"+\",[1,\"a\",\"*\",[5,\"2\"]]],\"-\",[1,[1,\"a\",\"*\",\"b\"],\"*\",[5,\"4\"]]]]]]},externals:{}})&&e.f)({})),eoe=w.fn([m],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=__tsover_sub(t.x,__tsover_mul(8,ie(__tsover_div(t.x,8)))),r=__tsover_sub(t.y,__tsover_mul(8,ie(__tsover_div(t.y,8)))),o=__tsover_sub(s,__tsover_mul(2,ie(__tsover_div(s,2)))),a=__tsover_sub(r,__tsover_mul(2,ie(__tsover_div(r,2)))),i=__tsover_sub(s,__tsover_mul(4,ie(__tsover_div(s,4)))),l=__tsover_sub(r,__tsover_mul(4,ie(__tsover_div(r,4)))),c=ie(__tsover_div(i,2)),d=ie(__tsover_div(l,2)),u=ie(__tsover_div(s,4)),p=ie(__tsover_div(r,4)),_=u1(o,a),f=u1(c,d),h=u1(u,p),g=__tsover_div(_,4),y=__tsover_div(__tsover_add(__tsover_mul(_,4),f),16),b=__tsover_div(__tsover_add(__tsover_add(__tsover_mul(_,16),__tsover_mul(f,4)),h),64),x=__tsover_add(__tsover_mul(j(l),4),j(i)),P=.75;return P=N(P,.3125,x===1),P=N(P,.375,x===2),P=N(P,.8125,x===3),P=N(P,.25,x===4),P=N(P,n(0),x===5),P=N(P,.0625,x===6),P=N(P,.4375,x===7),P=N(P,.6875,x===8),P=N(P,.1875,x===9),P=N(P,.125,x===10),P=N(P,.5,x===11),P=N(P,.9375,x===12),P=N(P,.625,x===13),P=N(P,.5625,x===14),P=N(P,.875,x===15),S(g,y,b,P)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"}],body:[0,[[13,\"tx\",[1,[7,\"coord\",\"x\"],\"-\",[1,[5,\"8\"],\"*\",[6,\"std.floor\",[[1,[7,\"coord\",\"x\"],\"/\",[5,\"8\"]]]]]]],[13,\"ty\",[1,[7,\"coord\",\"y\"],\"-\",[1,[5,\"8\"],\"*\",[6,\"std.floor\",[[1,[7,\"coord\",\"y\"],\"/\",[5,\"8\"]]]]]]],[13,\"x2\",[1,\"tx\",\"-\",[1,[5,\"2\"],\"*\",[6,\"std.floor\",[[1,\"tx\",\"/\",[5,\"2\"]]]]]]],[13,\"y2\",[1,\"ty\",\"-\",[1,[5,\"2\"],\"*\",[6,\"std.floor\",[[1,\"ty\",\"/\",[5,\"2\"]]]]]]],[13,\"x4\",[1,\"tx\",\"-\",[1,[5,\"4\"],\"*\",[6,\"std.floor\",[[1,\"tx\",\"/\",[5,\"4\"]]]]]]],[13,\"y4\",[1,\"ty\",\"-\",[1,[5,\"4\"],\"*\",[6,\"std.floor\",[[1,\"ty\",\"/\",[5,\"4\"]]]]]]],[13,\"x4h\",[6,\"std.floor\",[[1,\"x4\",\"/\",[5,\"2\"]]]]],[13,\"y4h\",[6,\"std.floor\",[[1,\"y4\",\"/\",[5,\"2\"]]]]],[13,\"x8h\",[6,\"std.floor\",[[1,\"tx\",\"/\",[5,\"4\"]]]]],[13,\"y8h\",[6,\"std.floor\",[[1,\"ty\",\"/\",[5,\"4\"]]]]],[13,\"b00\",[6,\"ditherBayerQuad\",[\"x2\",\"y2\"]]],[13,\"b11\",[6,\"ditherBayerQuad\",[\"x4h\",\"y4h\"]]],[13,\"b22\",[6,\"ditherBayerQuad\",[\"x8h\",\"y8h\"]]],[13,\"bayer2\",[1,\"b00\",\"/\",[5,\"4\"]]],[13,\"bayer4\",[1,[1,[1,\"b00\",\"*\",[5,\"4\"]],\"+\",\"b11\"],\"/\",[5,\"16\"]]],[13,\"bayer8\",[1,[1,[1,[1,\"b00\",\"*\",[5,\"16\"]],\"+\",[1,\"b11\",\"*\",[5,\"4\"]]],\"+\",\"b22\"],\"/\",[5,\"64\"]]],[13,\"idx\",[1,[1,[6,\"d.i32\",[\"y4\"]],\"*\",[5,\"4\"]],\"+\",[6,\"d.i32\",[\"x4\"]]]],[12,\"cd\",[5,\"0.75\"]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.3125\"],[1,\"idx\",\"===\",[5,\"1\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.375\"],[1,\"idx\",\"===\",[5,\"2\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.8125\"],[1,\"idx\",\"===\",[5,\"3\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.25\"],[1,\"idx\",\"===\",[5,\"4\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[6,\"d.f32\",[[5,\"0\"]]],[1,\"idx\",\"===\",[5,\"5\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.0625\"],[1,\"idx\",\"===\",[5,\"6\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.4375\"],[1,\"idx\",\"===\",[5,\"7\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.6875\"],[1,\"idx\",\"===\",[5,\"8\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.1875\"],[1,\"idx\",\"===\",[5,\"9\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.125\"],[1,\"idx\",\"===\",[5,\"10\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.5\"],[1,\"idx\",\"===\",[5,\"11\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.9375\"],[1,\"idx\",\"===\",[5,\"12\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.625\"],[1,\"idx\",\"===\",[5,\"13\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.5625\"],[1,\"idx\",\"===\",[5,\"14\"]]]]],[2,\"cd\",\"=\",[6,\"std.select\",[\"cd\",[5,\"0.875\"],[1,\"idx\",\"===\",[5,\"15\"]]]]],[10,[6,\"d.vec4f\",[\"bayer2\",\"bayer4\",\"bayer8\",\"cd\"]]]]]},externals:{\"std.floor\":()=>ie,ditherBayerQuad:()=>u1,\"d.i32\":()=>j,\"std.select\":()=>N,\"d.f32\":()=>n,\"d.vec4f\":()=>S}})&&e.f)({})),toe=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=t.add(m(.5,.5));return Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(s.x,.06711056),__tsover_mul(s.y,.00583715)))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"}],body:[0,[[13,\"c\",[6,[7,\"coord\",\"add\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[10,[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[7,\"c\",\"x\"],\"*\",[5,\"0.06711056\"]],\"+\",[1,[7,\"c\",\"y\"],\"*\",[5,\"0.00583715\"]]]]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.fract\":()=>Re}})&&e.f)({})),soe=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Ys(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"}],body:[0,[[10,[6,\"noise.hash12\",[\"coord\"]]]]]},externals:{\"noise.hash12\":()=>Ys}})&&e.f)({})),roe=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return Et(__tsover_add(.5,__tsover_mul(__tsover_sub(t,.5),o)),__tsover_add(s,__tsover_sub(r,.5)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ditherValue\"},{type:\"i\",name:\"luminance\"},{type:\"i\",name:\"threshold\"},{type:\"i\",name:\"spread\"}],body:[0,[[13,\"adjustedDither\",[1,[5,\"0.5\"],\"+\",[1,[1,\"ditherValue\",\"-\",[5,\"0.5\"]],\"*\",\"spread\"]]],[10,[6,\"std.step\",[\"adjustedDither\",[1,\"luminance\",\"+\",[1,\"threshold\",\"-\",[5,\"0.5\"]]]]]]]]},externals:{\"std.step\":()=>Et}})&&e.f)({})),ooe=w.fn([m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return ie(t.div(m(8,8))).mul(m(8,8))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"}],body:[0,[[10,[6,[7,[6,\"std.floor\",[[6,[7,\"coord\",\"div\"],[[6,\"d.vec2f\",[[5,\"8\"],[5,\"8\"]]]]]]],\"mul\"],[[6,\"d.vec2f\",[[5,\"8\"],[5,\"8\"]]]]]]]]},externals:{\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})),aoe=w.fn([m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_add(__tsover_mul(__tsover_sub(t.y,s.y),8),__tsover_sub(t.x,s.x))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"},{type:\"i\",name:\"blockOrigin\"}],body:[0,[[10,[1,[1,[1,[7,\"coord\",\"y\"],\"-\",[7,\"blockOrigin\",\"y\"]],\"*\",[5,\"8\"]],\"+\",[1,[7,\"coord\",\"x\"],\"-\",[7,\"blockOrigin\",\"x\"]]]]]]},externals:{}})&&e.f)({})),noe=w.fn([m,n,n,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return m(__tsover_div(__tsover_mul(__tsover_add(__tsover_add(t.x,s),.5),o),a.x),__tsover_div(__tsover_mul(__tsover_add(__tsover_add(t.y,r),.5),o),a.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"blockOrigin\"},{type:\"i\",name:\"lx\"},{type:\"i\",name:\"ly\"},{type:\"i\",name:\"pixelSize\"},{type:\"i\",name:\"gridRes\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[1,[1,[7,\"blockOrigin\",\"x\"],\"+\",\"lx\"],\"+\",[5,\"0.5\"]],\"*\",\"pixelSize\"],\"/\",[7,\"gridRes\",\"x\"]],[1,[1,[1,[1,[7,\"blockOrigin\",\"y\"],\"+\",\"ly\"],\"+\",[5,\"0.5\"]],\"*\",\"pixelSize\"],\"/\",[7,\"gridRes\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),ioe=w.fn([Fe(n,64),n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(.5,__tsover_sub(r,.5)),i=Fe(n,8)(),l=Fe(n,8)(),c=j(s),d=n(0);for(let u=0;u<8;u++){let p=(u&1)===0;for(let _=0;_<8;_++){let f=N(__tsover_sub(7,_),_,p),h=__tsover_add(__tsover_mul(u,8),f),g=__tsover_add(t[h],i[f]),y=Et(a,g);d=N(d,y,h===c);let b=__tsover_mul(__tsover_sub(g,y),o),x=N(-1,1,p),P=__tsover_add(f,x);if(P>=0&&P<8&&(i[P]=__tsover_add(i[P],__tsover_mul(b,.4375))),u<7){let C=__tsover_sub(f,x),E=__tsover_add(f,x);C>=0&&C<8&&(l[C]=__tsover_add(l[C],__tsover_mul(b,.1875))),l[f]=__tsover_add(l[f],__tsover_mul(b,.3125)),E>=0&&E<8&&(l[E]=__tsover_add(l[E],__tsover_mul(b,.0625)))}}for(let _=0;_<8;_++)i[_]=l[_],l[_]=n(0)}return d}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lum\"},{type:\"i\",name:\"lic\"},{type:\"i\",name:\"threshold\"},{type:\"i\",name:\"spread\"}],body:[0,[[13,\"thr\",[1,[5,\"0.5\"],\"-\",[1,\"threshold\",\"-\",[5,\"0.5\"]]]],[13,\"ecur\",[6,[6,\"d.arrayOf\",[\"d.f32\",[5,\"8\"]]],[]]],[13,\"enext\",[6,[6,\"d.arrayOf\",[\"d.f32\",[5,\"8\"]]],[]]],[13,\"licInt\",[6,\"d.i32\",[\"lic\"]]],[12,\"result\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"y\",[5,\"0\"]],[1,\"y\",\"<\",[5,\"8\"]],[102,\"++\",\"y\"],[0,[[13,\"ltr\",[1,[1,\"y\",\"&\",[5,\"1\"]],\"===\",[5,\"0\"]]],[14,[12,\"xi\",[5,\"0\"]],[1,\"xi\",\"<\",[5,\"8\"]],[102,\"++\",\"xi\"],[0,[[13,\"x\",[6,\"std.select\",[[1,[5,\"7\"],\"-\",\"xi\"],\"xi\",\"ltr\"]]],[13,\"idx\",[1,[1,\"y\",\"*\",[5,\"8\"]],\"+\",\"x\"]],[13,\"val\",[1,[8,\"lum\",\"idx\"],\"+\",[8,\"ecur\",\"x\"]]],[13,\"qv\",[6,\"std.step\",[\"thr\",\"val\"]]],[2,\"result\",\"=\",[6,\"std.select\",[\"result\",\"qv\",[1,\"idx\",\"===\",\"licInt\"]]]],[13,\"e\",[1,[1,\"val\",\"-\",\"qv\"],\"*\",\"spread\"]],[13,\"dir\",[6,\"std.select\",[[4,\"-\",[5,\"1\"]],[5,\"1\"],\"ltr\"]]],[13,\"xr\",[1,\"x\",\"+\",\"dir\"]],[11,[1,\"xr\",\">=\",[5,\"0\"]],[0,[[11,[1,\"xr\",\"<\",[5,\"8\"]],[0,[[2,[8,\"ecur\",\"xr\"],\"=\",[1,[8,\"ecur\",\"xr\"],\"+\",[1,\"e\",\"*\",[5,\"0.4375\"]]]]]]]]]],[11,[1,\"y\",\"<\",[5,\"7\"]],[0,[[13,\"xbl\",[1,\"x\",\"-\",\"dir\"]],[13,\"xbr\",[1,\"x\",\"+\",\"dir\"]],[11,[1,\"xbl\",\">=\",[5,\"0\"]],[0,[[11,[1,\"xbl\",\"<\",[5,\"8\"]],[0,[[2,[8,\"enext\",\"xbl\"],\"=\",[1,[8,\"enext\",\"xbl\"],\"+\",[1,\"e\",\"*\",[5,\"0.1875\"]]]]]]]]]],[2,[8,\"enext\",\"x\"],\"=\",[1,[8,\"enext\",\"x\"],\"+\",[1,\"e\",\"*\",[5,\"0.3125\"]]]],[11,[1,\"xbr\",\">=\",[5,\"0\"]],[0,[[11,[1,\"xbr\",\"<\",[5,\"8\"]],[0,[[2,[8,\"enext\",\"xbr\"],\"=\",[1,[8,\"enext\",\"xbr\"],\"+\",[1,\"e\",\"*\",[5,\"0.0625\"]]]]]]]]]]]]]]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",[5,\"8\"]],[102,\"++\",\"k\"],[0,[[2,[8,\"ecur\",\"k\"],\"=\",[8,\"enext\",\"k\"]],[2,[8,\"enext\",\"k\"],\"=\",[6,\"d.f32\",[[5,\"0\"]]]]]]]]]],[10,\"result\"]]]},externals:{\"d.arrayOf\":()=>Fe,\"d.f32\":()=>n,\"d.i32\":()=>j,\"std.select\":()=>N,\"std.step\":()=>Et}})&&e.f)({})),loe=w.fn([S,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=K(A(t.x,t.y,t.z),A(s.x,s.y,s.z),A(r)),a=K(t.w,s.w,r);return S(o.x,o.y,o.z,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colorA\"},{type:\"i\",name:\"colorB\"},{type:\"i\",name:\"ditherResult\"}],body:[0,[[13,\"rgb\",[6,\"std.mix\",[[6,\"d.vec3f\",[[7,\"colorA\",\"x\"],[7,\"colorA\",\"y\"],[7,\"colorA\",\"z\"]]],[6,\"d.vec3f\",[[7,\"colorB\",\"x\"],[7,\"colorB\",\"y\"],[7,\"colorB\",\"z\"]]],[6,\"d.vec3f\",[\"ditherResult\"]]]]],[13,\"a\",[6,\"std.mix\",[[7,\"colorA\",\"w\"],[7,\"colorB\",\"w\"],\"ditherResult\"]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"a\"]]]]]},externals:{\"std.mix\":()=>K,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),coe=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=A(t.x,t.y,t.z),o=K(r.mul(.3),ye(r.mul(1.3),A(1,1,1)),A(s));return S(o.x,o.y,o.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sourceColor\"},{type:\"i\",name:\"ditherResult\"}],body:[0,[[13,\"rgb\",[6,\"d.vec3f\",[[7,\"sourceColor\",\"x\"],[7,\"sourceColor\",\"y\"],[7,\"sourceColor\",\"z\"]]]],[13,\"dark\",[6,[7,\"rgb\",\"mul\"],[[5,\"0.3\"]]]],[13,\"bright\",[6,\"std.min\",[[6,[7,\"rgb\",\"mul\"],[[5,\"1.3\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[13,\"out\",[6,\"std.mix\",[\"dark\",\"bright\",[6,\"d.vec3f\",[\"ditherResult\"]]]]],[10,[6,\"d.vec4f\",[[7,\"out\",\"x\"],[7,\"out\",\"y\"],[7,\"out\",\"z\"],[7,\"sourceColor\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.min\":()=>ye,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({}));function doe(e){return e.map((t,s)=>({stop:t,index:s})).sort((t,s)=>t.stop.position-s.stop.position||t.index-s.index).map(({stop:t})=>t)}function J3(e,t){let s=new Array(32).fill(0),r=new Array(8).fill(0),o=Array.isArray(e)&&e.length>0?doe(e):[],a=Math.min(o.length,8);for(let i=0;i<a;i++){let l=t(o[i].color);s[i*4]=l[0],s[i*4+1]=l[1],s[i*4+2]=l[2],s[i*4+3]=l[3],r[i]=o[i].position}return{colors:s,positions:r,stopCount:a}}function eA(e,t,s){let r=new Array(24).fill(0);for(let o=0;o<t;o++){let a=fl(e[o*4],e[o*4+1],e[o*4+2],s);r[o*3]=a[0],r[o*3+1]=a[1],r[o*3+2]=a[2]}return r}w.fn([A,n],Xa)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Xa({conv:t,alpha:s})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"conv\"},{type:\"i\",name:\"alpha\"}],body:[0,[[10,[6,\"MixStep\",[[104,{conv:\"conv\",alpha:\"alpha\"}]]]]]]},externals:{MixStep:()=>Xa}})&&e.f)({}));w.fn([Xa,A,n,n,n,n],Xa)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=G(__tsover_div(__tsover_sub(o,a),B(__tsover_sub(i,a),1e-6)),0,1);return _l(t.conv,t.alpha,s,r,l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"running\"},{type:\"i\",name:\"convI\"},{type:\"i\",name:\"alphaI\"},{type:\"i\",name:\"tClamped\"},{type:\"i\",name:\"posA\"},{type:\"i\",name:\"posB\"}],body:[0,[[13,\"seg\",[6,\"std.clamp\",[[1,[1,\"tClamped\",\"-\",\"posA\"],\"/\",[6,\"std.max\",[[1,\"posB\",\"-\",\"posA\"],[5,\"0.000001\"]]]],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"mixPreconvertedInSpace\",[[7,\"running\",\"conv\"],[7,\"running\",\"alpha\"],\"convI\",\"alphaI\",\"seg\"]]]]]},externals:{\"std.clamp\":()=>G,\"std.max\":()=>B,mixPreconvertedInSpace:()=>_l}})&&e.f)({}));w.fn([Xa],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return t.conv}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[10,[7,\"s\",\"conv\"]]]]},externals:{}})&&e.f)({}));w.fn([Xa],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return t.alpha}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"}],body:[0,[[10,[7,\"s\",\"alpha\"]]]]},externals:{}})&&e.f)({}));w.fn([A,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t,s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"a\"}],body:[0,[[10,[6,\"d.vec4f\",[\"rgb\",\"a\"]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({}));var Vb=w.fn([n,Fe(S,8),Fe(S,8/4),Fe(A,8),n],Xa)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=G(t,0,1),l=j(a),c=o[0],d=A(c.x,c.y,c.z),u=s[0].w;for(let p=1;p<l;p++){let _=r[__tsover_sub(p,1)>>2],f=r[p>>2],h=_[__tsover_sub(p,1)&3],g=f[p&3],y=G(__tsover_div(__tsover_sub(i,h),B(__tsover_sub(g,h),1e-6)),0,1),b=_l(d,u,o[p],s[p].w,y);d=A(b.conv.x,b.conv.y,b.conv.z),u=b.alpha}return Xa({conv:d,alpha:u})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"colors\"},{type:\"i\",name:\"positions\"},{type:\"i\",name:\"converted\"},{type:\"i\",name:\"stopCount\"}],body:[0,[[13,\"clampedT\",[6,\"std.clamp\",[\"t\",[5,\"0\"],[5,\"1\"]]]],[13,\"n\",[6,\"d.i32\",[\"stopCount\"]]],[13,\"c0\",[8,\"converted\",[5,\"0\"]]],[12,\"conv\",[6,\"d.vec3f\",[[7,\"c0\",\"x\"],[7,\"c0\",\"y\"],[7,\"c0\",\"z\"]]]],[12,\"alpha\",[7,[8,\"colors\",[5,\"0\"]],\"w\"]],[14,[12,\"i\",[5,\"1\"]],[1,\"i\",\"<\",\"n\"],[102,\"++\",\"i\"],[0,[[13,\"va\",[8,\"positions\",[1,[1,\"i\",\"-\",[5,\"1\"]],\">>\",[5,\"2\"]]]],[13,\"vb\",[8,\"positions\",[1,\"i\",\">>\",[5,\"2\"]]]],[13,\"posA\",[8,\"va\",[1,[1,\"i\",\"-\",[5,\"1\"]],\"&\",[5,\"3\"]]]],[13,\"posB\",[8,\"vb\",[1,\"i\",\"&\",[5,\"3\"]]]],[13,\"seg\",[6,\"std.clamp\",[[1,[1,\"clampedT\",\"-\",\"posA\"],\"/\",[6,\"std.max\",[[1,\"posB\",\"-\",\"posA\"],[5,\"0.000001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"step\",[6,\"mixPreconvertedInSpace\",[\"conv\",\"alpha\",[8,\"converted\",\"i\"],[7,[8,\"colors\",\"i\"],\"w\"],\"seg\"]]],[2,\"conv\",\"=\",[6,\"d.vec3f\",[[7,[7,\"step\",\"conv\"],\"x\"],[7,[7,\"step\",\"conv\"],\"y\"],[7,[7,\"step\",\"conv\"],\"z\"]]]],[2,\"alpha\",\"=\",[7,\"step\",\"alpha\"]]]]],[10,[6,\"MixStep\",[[104,{conv:\"conv\",alpha:\"alpha\"}]]]]]]},externals:{\"std.clamp\":()=>G,\"d.i32\":()=>j,\"d.vec3f\":()=>A,\"std.max\":()=>B,mixPreconvertedInSpace:()=>_l,MixStep:()=>Xa}})&&e.f)({}));function q4(e,t){switch(t){case 1:return W(Hs,\"sRGBToP3\",[W(oa,\"oklabToRgb\",[W(pl,\"oklchToOklab\",[e])])]);case 2:return W(Hs,\"sRGBToP3\",[W(oa,\"oklabToRgb\",[e])]);case 3:return W(Hs,\"sRGBToP3\",[W(ml,\"hslToRgb\",[e])]);case 4:return W(Hs,\"sRGBToP3\",[W(hd,\"hsvToRgb\",[e])]);case 5:return W(Hs,\"sRGBToP3\",[W(gd,\"labToRgb\",[W(yd,\"lchToLab\",[e])])]);default:return e}}function En(e,t,s){let r=ns(W(Vb,\"gradientStopsInSpace\",[e,t.colorsArray,t.positionsArray,t.convertedColorsArray,t.stopCount]),\"stopMix\");return Ct(q4(r.member(\"conv\"),s),r.member(\"alpha\"))}var V4=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Cs(s);return m(__tsover_mul(t.x,r),t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]]]]},externals:{aspectOf:()=>Cs,\"d.vec2f\":()=>m}})&&e.f)({})),uoe=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_div(s.x,s.y);return m(__tsover_mul(t.x,a),t.y).mul(Tt(r)).add(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"aspect\",[1,[7,\"viewport\",\"x\"],\"/\",[7,\"viewport\",\"y\"]]],[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[10,[6,[7,[6,[7,\"aspectUV\",\"mul\"],[[6,\"std.exp\",[\"scale\"]]]],\"add\"],[\"seed\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.exp\":()=>Tt}})&&e.f)({})),poe=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Cs(s),a=__tsover_mul(r,bt),i=pe(a),l=ae(a),c=__tsover_sub(__tsover_mul(t.x,o),__tsover_mul(o,.5)),d=__tsover_sub(t.y,.5);return m(__tsover_add(__tsover_mul(c,i),__tsover_mul(d,l)),__tsover_add(__tsover_mul(c,-l),__tsover_mul(d,i)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"}],body:[0,[[13,\"aspect\",[6,\"aspectOf\",[\"viewport\"]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"au\",[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",[1,\"aspect\",\"*\",[5,\"0.5\"]]]],[13,\"av\",[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]],[13,\"ru\",[1,[1,\"au\",\"*\",\"cosA\"],\"+\",[1,\"av\",\"*\",\"sinA\"]]],[13,\"rv\",[1,[1,\"au\",\"*\",[4,\"-\",\"sinA\"]],\"+\",[1,\"av\",\"*\",\"cosA\"]]],[10,[6,\"d.vec2f\",[\"ru\",\"rv\"]]]]]},externals:{aspectOf:()=>Cs,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),moe=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_div(he(sv(t,s)),1.414)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"z\"}],body:[0,[[10,[1,[6,\"std.length\",[[6,\"curl22z\",[\"pos\",\"z\"]]]],\"/\",[5,\"1.414\"]]]]]},externals:{\"std.length\":()=>he,curl22z:()=>sv}})&&e.f)({})),$S=Lk(la,{maxOctaves:8,drift:\"goldenAngle\",name:\"fractalOctaveSum\"}),_oe=w.fn([m,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";return $S(t,2,s,r,o,m(a,__tsover_mul(a,.7)),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"detail\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"octaveCount\"}],body:[0,[[10,[6,\"fractalOctaveSum\",[\"pos\",[5,\"2\"],\"detail\",\"contrast\",\"animTime\",[6,\"d.vec2f\",[\"seed\",[1,\"seed\",\"*\",[5,\"0.7\"]]]],\"octaveCount\"]]]]]},externals:{fractalOctaveSum:()=>$S,\"d.vec2f\":()=>m}})&&e.f)({})),foe=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return G(__tsover_add(__tsover_mul(__tsover_div(t.x,t.y),.5),.5),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sum\"}],body:[0,[[10,[6,\"std.clamp\",[[1,[1,[1,[7,\"sum\",\"x\"],\"/\",[7,\"sum\",\"y\"]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({}));var XS=Gk({distance:\"selectableSquared\",jitter:\"mix\",name:\"worleyNearest2\"}),HS=Lk(w.fn([m,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=XS(t,s,r,o,a);return PT(l.x,l.y,a,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"oUV\"},{type:\"i\",name:\"animT\"},{type:\"i\",name:\"seedOff\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"distNum\"},{type:\"i\",name:\"modeNum\"}],body:[0,[[13,\"dd\",[6,\"worleyNearest2\",[\"oUV\",\"animT\",\"seedOff\",\"jitter\",\"distNum\"]]],[10,[6,\"cellReduceSelectable\",[[7,\"dd\",\"x\"],[7,\"dd\",\"y\"],\"distNum\",\"modeNum\"]]]]]},externals:{worleyNearest2:()=>XS,cellReduceSelectable:()=>PT}})&&e.f)({})),{maxOctaves:4,drift:\"timeSeed\",name:\"worleyOctaveSum\"}),voe=w.fn([m,n,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";return HS(t,s,a,i,l,r,A(o,d,u),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"jitter\"},{type:\"i\",name:\"lacunarity\"},{type:\"i\",name:\"persistence\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"octaves\"},{type:\"i\",name:\"distNum\"},{type:\"i\",name:\"modeNum\"}],body:[0,[[10,[6,\"worleyOctaveSum\",[\"pos\",\"scale\",\"lacunarity\",\"persistence\",\"animTime\",\"seed\",[6,\"d.vec3f\",[\"jitter\",\"distNum\",\"modeNum\"]],\"octaves\"]]]]]},externals:{worleyOctaveSum:()=>HS,\"d.vec3f\":()=>A}})&&e.f)({})),hoe=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_mul(__tsover_div(t.x,B(t.y,1e-4)),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sum\"},{type:\"i\",name:\"modeScale\"}],body:[0,[[10,[1,[1,[7,\"sum\",\"x\"],\"/\",[6,\"std.max\",[[7,\"sum\",\"y\"],[5,\"0.0001\"]]]],\"*\",\"modeScale\"]]]]},externals:{\"std.max\":()=>B}})&&e.f)({})),goe=Dk({domain:\"unit\",contrastMode:\"multiplicative\",invert:!1}),N4=e=>EJ[e]??0,$4=e=>kJ[e]??0;function yoe(e){return typeof e==\"number\"?e:N4(e)}function boe(e){return typeof e==\"number\"?e:$4(e)}function xoe(e,t){let s=1.5;return e===1?s=1:e===2?s=2.5:e===3?s=.6:e===4&&(s=2),t===1?s*=.7:t===2&&(s*=1.4),s}var Aoe=Gk({distance:\"length\",jitter:\"none\",name:\"voronoiNearest2\"}),woe=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_sub(4,__tsover_mul(s,3));return CT(t.x,t.y,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dd\"},{type:\"i\",name:\"edgeIntensity\"}],body:[0,[[13,\"gradientPower\",[1,[5,\"4\"],\"-\",[1,\"edgeIntensity\",\"*\",[5,\"3\"]]]],[10,[6,\"cellRatio\",[[7,\"dd\",\"x\"],[7,\"dd\",\"y\"],\"gradientPower\"]]]]]},externals:{cellRatio:()=>CT}})&&e.f)({})),Toe=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(__tsover_mul(s,r),6);return MT(t.x,t.y,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dd\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"scale\"}],body:[0,[[13,\"scaledEdgeSoftness\",[1,[1,\"softness\",\"*\",\"scale\"],\"/\",[5,\"6\"]]],[10,[6,\"cellEdgeMask\",[[7,\"dd\",\"x\"],[7,\"dd\",\"y\"],\"scaledEdgeSoftness\"]]]]]},externals:{cellEdgeMask:()=>MT}})&&e.f)({})),Soe=w.fn([A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return vt(t,A(.85,.85,.85))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"}],body:[0,[[10,[6,\"std.pow\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.85\"],[5,\"0.85\"],[5,\"0.85\"]]]]]]]]},externals:{\"std.pow\":()=>vt,\"d.vec3f\":()=>A}})&&e.f)({})),zm=8;if(zm!==8)throw new Error(`noisePaints: MAX (${zm}) out of sync with MAX_COLOR_STOPS (8)`);var jS=w.fn([m,m,m,m,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_div(s.x,B(s.y,1e-6)),l=m(__tsover_mul(r.x,i),__tsover_sub(1,r.y)),c=m(__tsover_mul(o.x,i),__tsover_sub(1,o.y)),d=m(__tsover_mul(t.x,i),t.y),u=m(__tsover_sub(c.x,l.x),__tsover_sub(c.y,l.y)),p=B(he(u),1e-6),_=m(__tsover_div(u.x,p),__tsover_div(u.y,p)),f=m(-_.y,_.x),h=m(__tsover_sub(d.x,l.x),__tsover_sub(d.y,l.y)),g=__tsover_div(Se(h,_),p),y=Se(h,f),b=a>0,x=G(g,0,1),P=N(n(1),ae(__tsover_mul(x,Wo)),b),C=__tsover_mul(be(0,.06,g),__tsover_sub(1,be(.94,1,g)));return S(g,y,P,N(n(1),C,b))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"start\"},{type:\"i\",name:\"end\"},{type:\"i\",name:\"pinFlag\"}],body:[0,[[13,\"aspectRatio\",[1,[7,\"viewport\",\"x\"],\"/\",[6,\"std.max\",[[7,\"viewport\",\"y\"],[5,\"0.000001\"]]]]],[13,\"startPos\",[6,\"d.vec2f\",[[1,[7,\"start\",\"x\"],\"*\",\"aspectRatio\"],[1,[5,\"1\"],\"-\",[7,\"start\",\"y\"]]]]],[13,\"endPos\",[6,\"d.vec2f\",[[1,[7,\"end\",\"x\"],\"*\",\"aspectRatio\"],[1,[5,\"1\"],\"-\",[7,\"end\",\"y\"]]]]],[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspectRatio\"],[7,\"uv\",\"y\"]]]],[13,\"direction\",[6,\"d.vec2f\",[[1,[7,\"endPos\",\"x\"],\"-\",[7,\"startPos\",\"x\"]],[1,[7,\"endPos\",\"y\"],\"-\",[7,\"startPos\",\"y\"]]]]],[13,\"len\",[6,\"std.max\",[[6,\"std.length\",[\"direction\"]],[5,\"0.000001\"]]]],[13,\"normalizedDir\",[6,\"d.vec2f\",[[1,[7,\"direction\",\"x\"],\"/\",\"len\"],[1,[7,\"direction\",\"y\"],\"/\",\"len\"]]]],[13,\"perpDir\",[6,\"d.vec2f\",[[4,\"-\",[7,\"normalizedDir\",\"y\"]],[7,\"normalizedDir\",\"x\"]]]],[13,\"relativePos\",[6,\"d.vec2f\",[[1,[7,\"aspectUV\",\"x\"],\"-\",[7,\"startPos\",\"x\"]],[1,[7,\"aspectUV\",\"y\"],\"-\",[7,\"startPos\",\"y\"]]]]],[13,\"progress\",[1,[6,\"std.dot\",[\"relativePos\",\"normalizedDir\"]],\"/\",\"len\"]],[13,\"perpDist\",[6,\"std.dot\",[\"relativePos\",\"perpDir\"]]],[13,\"pinOn\",[1,\"pinFlag\",\">\",[5,\"0\"]]],[13,\"clampedProg\",[6,\"std.clamp\",[\"progress\",[5,\"0\"],[5,\"1\"]]]],[13,\"pinch\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"std.sin\",[[1,\"clampedProg\",\"*\",\"PI\"]]],\"pinOn\"]]],[13,\"fade\",[1,[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.06\"],\"progress\"]],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0.94\"],[5,\"1\"],\"progress\"]]]]],[13,\"edgeFade\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"fade\",\"pinOn\"]]],[10,[6,\"d.vec4f\",[\"progress\",\"perpDist\",\"pinch\",\"edgeFade\"]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.dot\":()=>Se,\"std.clamp\":()=>G,\"std.select\":()=>N,\"d.f32\":()=>n,\"std.sin\":()=>ae,PI:()=>Wo,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S}})&&e.f)({})),Poe=w.fn([m,m,m,m,S,S,S,Fe(S,zm),Fe(S,zm/4),Fe(A,zm)],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=a.x,_=a.y,f=a.z,h=a.w,g=i.x,y=i.y,b=i.z,x=i.w,P=l.y,C=l.z,E=l.w,R=jS(t,s,r,o,l.x),U=R.x,L=R.y,k=R.z,D=R.w,O=__tsover_mul(h,.07),F=__tsover_add(__tsover_mul(g,.08),8e-4),q=B(__tsover_sub(f,1),1),Y=A(0,0,0),V=n(0),X=__tsover_mul(__tsover_mul(U,Jt),_),Q=__tsover_add(__tsover_mul(U,8),__tsover_mul(P,2)),te=__tsover_add(__tsover_mul(U,b),C),J=j(Ut(f));for(let oe=0;oe<J;oe++){let le=n(oe),se=__tsover_div(le,q),Te=__tsover_mul(le,.9),de=__tsover_mul(P,__tsover_add(__tsover_mul(se,.6),.7)),xe=__tsover_add(ae(__tsover_add(__tsover_add(X,de),Te)),__tsover_mul(ae(__tsover_add(__tsover_sub(__tsover_mul(X,2.1),__tsover_mul(de,.7)),Te)),.5)),Ae=__tsover_mul(__tsover_mul(xe,p),.06),De=__tsover_mul(__tsover_sub(se,.5),y),Ee=__tsover_mul(__tsover_add(De,Ae),k),ze=re(__tsover_sub(L,Ee)),Ie=__tsover_sub(1,be(O,__tsover_add(O,F),ze)),ke=__tsover_add(__tsover_mul(ae(__tsover_add(Q,le)),.15),.85),Me=__tsover_add(te,__tsover_mul(se,x)),we=Vb(__tsover_sub(1,re(__tsover_sub(__tsover_mul(Re(__tsover_mul(Me,.5)),2),1))),c,d,u,E).conv,Ve=__tsover_mul(Ie,ke);Y=Y.add(we.mul(Ve)),V=__tsover_add(V,Ve)}return S(Y.div(B(V,1e-4)),G(__tsover_mul(V,D),0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"start\"},{type:\"i\",name:\"end\"},{type:\"i\",name:\"packed1\"},{type:\"i\",name:\"packed2\"},{type:\"i\",name:\"packed3\"},{type:\"i\",name:\"colors\"},{type:\"i\",name:\"positions\"},{type:\"i\",name:\"converted\"}],body:[0,[[13,\"amplitude\",[7,\"packed1\",\"x\"]],[13,\"frequency\",[7,\"packed1\",\"y\"]],[13,\"lineCount\",[7,\"packed1\",\"z\"]],[13,\"lineWidth\",[7,\"packed1\",\"w\"]],[13,\"softnessP\",[7,\"packed2\",\"x\"]],[13,\"spread\",[7,\"packed2\",\"y\"]],[13,\"colorScale\",[7,\"packed2\",\"z\"]],[13,\"colorVariance\",[7,\"packed2\",\"w\"]],[13,\"waveT\",[7,\"packed3\",\"y\"]],[13,\"colorT\",[7,\"packed3\",\"z\"]],[13,\"stopCount\",[7,\"packed3\",\"w\"]],[13,\"frame\",[6,\"ribbonPathFrame\",[\"uv\",\"viewport\",\"start\",\"end\",[7,\"packed3\",\"x\"]]]],[13,\"progress\",[7,\"frame\",\"x\"]],[13,\"perpDist\",[7,\"frame\",\"y\"]],[13,\"pinch\",[7,\"frame\",\"z\"]],[13,\"edgeFade\",[7,\"frame\",\"w\"]],[13,\"thickness\",[1,\"lineWidth\",\"*\",[5,\"0.07\"]]],[13,\"softness\",[1,[1,\"softnessP\",\"*\",[5,\"0.08\"]],\"+\",[5,\"0.0008\"]]],[13,\"denom\",[6,\"std.max\",[[1,\"lineCount\",\"-\",[5,\"1\"]],[5,\"1\"]]]],[12,\"accumColor\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[12,\"accumWeight\",[6,\"d.f32\",[[5,\"0\"]]]],[13,\"waveBase\",[1,[1,\"progress\",\"*\",\"TAU\"],\"*\",\"frequency\"]],[13,\"shimmerBase\",[1,[1,\"progress\",\"*\",[5,\"8\"]],\"+\",[1,\"waveT\",\"*\",[5,\"2\"]]]],[13,\"colorBase\",[1,[1,\"progress\",\"*\",\"colorScale\"],\"+\",\"colorT\"]],[13,\"n\",[6,\"d.i32\",[[6,\"std.round\",[\"lineCount\"]]]]],[14,[12,\"idx\",[5,\"0\"]],[1,\"idx\",\"<\",\"n\"],[102,\"++\",\"idx\"],[0,[[13,\"fi\",[6,\"d.f32\",[\"idx\"]]],[13,\"norm\",[1,\"fi\",\"/\",\"denom\"]],[13,\"phase\",[1,\"fi\",\"*\",[5,\"0.9\"]]],[13,\"strandT\",[1,\"waveT\",\"*\",[1,[1,\"norm\",\"*\",[5,\"0.6\"]],\"+\",[5,\"0.7\"]]]],[13,\"wave\",[1,[6,\"std.sin\",[[1,[1,\"waveBase\",\"+\",\"strandT\"],\"+\",\"phase\"]]],\"+\",[1,[6,\"std.sin\",[[1,[1,[1,\"waveBase\",\"*\",[5,\"2.1\"]],\"-\",[1,\"strandT\",\"*\",[5,\"0.7\"]]],\"+\",\"phase\"]]],\"*\",[5,\"0.5\"]]]],[13,\"displacement\",[1,[1,\"wave\",\"*\",\"amplitude\"],\"*\",[5,\"0.06\"]]],[13,\"baseline\",[1,[1,\"norm\",\"-\",[5,\"0.5\"]],\"*\",\"spread\"]],[13,\"centre\",[1,[1,\"baseline\",\"+\",\"displacement\"],\"*\",\"pinch\"]],[13,\"dd\",[6,\"std.abs\",[[1,\"perpDist\",\"-\",\"centre\"]]]],[13,\"line\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"thickness\",[1,\"thickness\",\"+\",\"softness\"],\"dd\"]]]],[13,\"shimmer\",[1,[1,[6,\"std.sin\",[[1,\"shimmerBase\",\"+\",\"fi\"]]],\"*\",[5,\"0.15\"]],\"+\",[5,\"0.85\"]]],[13,\"cc\",[1,\"colorBase\",\"+\",[1,\"norm\",\"*\",\"colorVariance\"]]],[13,\"colorCoord\",[1,[5,\"1\"],\"-\",[6,\"std.abs\",[[1,[1,[6,\"std.fract\",[[1,\"cc\",\"*\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]]],[13,\"stepRes\",[6,\"gradientStopsInSpace\",[\"colorCoord\",\"colors\",\"positions\",\"converted\",\"stopCount\"]]],[13,\"strandColor\",[7,\"stepRes\",\"conv\"]],[13,\"weight\",[1,\"line\",\"*\",\"shimmer\"]],[2,\"accumColor\",\"=\",[6,[7,\"accumColor\",\"add\"],[[6,[7,\"strandColor\",\"mul\"],[\"weight\"]]]]],[2,\"accumWeight\",\"=\",[1,\"accumWeight\",\"+\",\"weight\"]]]]],[13,\"workingColor\",[6,[7,\"accumColor\",\"div\"],[[6,\"std.max\",[\"accumWeight\",[5,\"0.0001\"]]]]]],[13,\"alpha\",[6,\"std.clamp\",[[1,\"accumWeight\",\"*\",\"edgeFade\"],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[\"workingColor\",\"alpha\"]]]]]},externals:{ribbonPathFrame:()=>jS,\"std.max\":()=>B,\"d.vec3f\":()=>A,\"d.f32\":()=>n,TAU:()=>Jt,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.sin\":()=>ae,\"std.abs\":()=>re,\"std.smoothstep\":()=>be,\"std.fract\":()=>Re,gradientStopsInSpace:()=>Vb,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({})),X4=w.fn([m,n,n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(__tsover_mul(t.x,s),t.y),c=__tsover_mul(r,6),d=l.mul(c).add(o),u=Uu(d).mul(a).mul(.0025),p=t.add(u),_=uT(d,j(6)),f=G(__tsover_add(1,__tsover_mul(__tsover_sub(_,.7),__tsover_mul(i,1.2))),0,1.5);return A(p.x,p.y,f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"grainScale\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"displacement\"},{type:\"i\",name:\"roughness\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"grainFreq\",[1,\"grainScale\",\"*\",[5,\"6\"]]],[13,\"grainUV\",[6,[7,[6,[7,\"aspectUV\",\"mul\"],[\"grainFreq\"]],\"add\"],[\"seed\"]]],[13,\"disp\",[6,[7,[6,[7,[6,\"curl22\",[\"grainUV\"]],\"mul\"],[\"displacement\"]],\"mul\"],[[5,\"0.0025\"]]]],[13,\"displacedUV\",[6,[7,\"uv\",\"add\"],[\"disp\"]]],[13,\"grain\",[6,\"paper12\",[\"grainUV\",[6,\"d.i32\",[[5,\"6\"]]]]]],[13,\"brightness\",[6,\"std.clamp\",[[1,[5,\"1\"],\"+\",[1,[1,\"grain\",\"-\",[5,\"0.7\"]],\"*\",[1,\"roughness\",\"*\",[5,\"1.2\"]]]],[5,\"0\"],[5,\"1.5\"]]]],[10,[6,\"d.vec3f\",[[7,\"displacedUV\",\"x\"],[7,\"displacedUV\",\"y\"],\"brightness\"]]]]]},externals:{\"d.vec2f\":()=>m,curl22:()=>Uu,paper12:()=>uT,\"d.i32\":()=>j,\"std.clamp\":()=>G,\"d.vec3f\":()=>A}})&&e.f)({})),gu=1e-5,KS=w.fn([m,m,m],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(t.x,s.x),a=__tsover_sub(t.y,s.y),i=__tsover_sub(r.x,s.x),l=__tsover_sub(r.y,s.y),c=B(he(m(i,l)),n(gu)),d=__tsover_div(__tsover_add(__tsover_mul(o,i),__tsover_mul(a,l)),c),u=B(B(-d,__tsover_sub(d,c)),n(0)),p=G(__tsover_div(d,c),n(0),n(1)),_=he(m(__tsover_sub(o,__tsover_mul(i,p)),__tsover_sub(a,__tsover_mul(l,p))));return A(d,Ue(B(__tsover_sub(__tsover_mul(_,_),__tsover_mul(u,u)),n(0))),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"}],body:[0,[[13,\"pax\",[1,[7,\"p\",\"x\"],\"-\",[7,\"a\",\"x\"]]],[13,\"pay\",[1,[7,\"p\",\"y\"],\"-\",[7,\"a\",\"y\"]]],[13,\"bax\",[1,[7,\"b\",\"x\"],\"-\",[7,\"a\",\"x\"]]],[13,\"bay\",[1,[7,\"b\",\"y\"],\"-\",[7,\"a\",\"y\"]]],[13,\"len\",[6,\"std.max\",[[6,\"std.length\",[[6,\"d.vec2f\",[\"bax\",\"bay\"]]]],[6,\"d.f32\",[\"EPS\"]]]]],[13,\"t\",[1,[1,[1,\"pax\",\"*\",\"bax\"],\"+\",[1,\"pay\",\"*\",\"bay\"]],\"/\",\"len\"]],[13,\"overshoot\",[6,\"std.max\",[[6,\"std.max\",[[4,\"-\",\"t\"],[1,\"t\",\"-\",\"len\"]]],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"h\",[6,\"std.clamp\",[[1,\"t\",\"/\",\"len\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"dSeg\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,\"pax\",\"-\",[1,\"bax\",\"*\",\"h\"]],[1,\"pay\",\"-\",[1,\"bay\",\"*\",\"h\"]]]]]]],[13,\"perp\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[1,\"dSeg\",\"*\",\"dSeg\"],\"-\",[1,\"overshoot\",\"*\",\"overshoot\"]],[6,\"d.f32\",[[5,\"0\"]]]]]]]],[10,[6,\"d.vec3f\",[\"t\",\"perp\",\"len\"]]]]]},externals:{\"std.max\":()=>B,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"d.f32\":()=>n,EPS:()=>gu,\"std.clamp\":()=>G,\"std.sqrt\":()=>Ue,\"d.vec3f\":()=>A}})&&e.f)({})),Coe=w.fn([m,m,m,m,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=Cs(s),_=KS(mT(t,s),Zi(r,p),Zi(o,p)),f=_.x,h=_.y,g=_.z,y=__tsover_mul(a,.5),b=__tsover_sub(f,__tsover_mul(g,.5)),x=__tsover_mul(g,.5),P=B(l,n(gu)),C=__tsover_add(P,c),E=B(ie(__tsover_add(__tsover_div(__tsover_add(g,c),C),.5)),n(1)),R=__tsover_div(g,B(__tsover_add(__tsover_mul(E,P),__tsover_mul(__tsover_sub(E,1),c)),n(gu))),U=__tsover_mul(P,R),L=__tsover_mul(C,R),k=G(ie(__tsover_div(f,L)),n(0),__tsover_sub(E,1)),D=__tsover_sub(f,__tsover_add(__tsover_mul(k,L),__tsover_mul(U,.5))),O=B(__tsover_sub(__tsover_mul(U,.5),y),n(0)),F=__tsover_mul(U,.5),q=B(__tsover_add(a,c),n(gu)),Y=B(ie(__tsover_add(__tsover_div(g,q),.5)),n(1)),V=__tsover_div(g,Y),X=G(ie(__tsover_add(__tsover_div(f,V),.5)),n(0),Y),Q=__tsover_sub(f,__tsover_mul(X,V)),te=i>=.5,J=i>=1.5,oe=N(b,N(D,Q,J),te),le=N(x,N(O,n(0),J),te),se=N(x,N(F,y,J),te),Te=N(d,u,oe>0),de=__tsover_sub(he(m(B(__tsover_sub(re(oe),le),n(0)),h)),y),xe=__tsover_sub(re(oe),se),Ae=__tsover_sub(h,y),De=N(__tsover_add(he(m(B(xe,n(0)),B(Ae,n(0)))),ye(B(xe,Ae),n(0))),de,Te>=.5),Ee=__tsover_div(1,s.y);return __tsover_sub(1,be(-Ee,Ee,De))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"pointA\"},{type:\"i\",name:\"pointB\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"style\"},{type:\"i\",name:\"dashLength\"},{type:\"i\",name:\"gapLength\"},{type:\"i\",name:\"capStart\"},{type:\"i\",name:\"capEnd\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"p\",[6,\"geom.aspectCorrectedUV\",[\"uv\",\"viewport\"]]],[13,\"a\",[6,\"geom.aspectCentrePosition\",[\"pointA\",\"aspect\"]]],[13,\"b\",[6,\"geom.aspectCentrePosition\",[\"pointB\",\"aspect\"]]],[13,\"seg\",[6,\"segmentArcPerp\",[\"p\",\"a\",\"b\"]]],[13,\"t\",[7,\"seg\",\"x\"]],[13,\"perp\",[7,\"seg\",\"y\"]],[13,\"len\",[7,\"seg\",\"z\"]],[13,\"halfT\",[1,\"thickness\",\"*\",[5,\"0.5\"]]],[13,\"cSolid\",[1,\"t\",\"-\",[1,\"len\",\"*\",[5,\"0.5\"]]]],[13,\"axSolid\",[1,\"len\",\"*\",[5,\"0.5\"]]],[13,\"rawDash\",[6,\"std.max\",[\"dashLength\",[6,\"d.f32\",[\"EPS\"]]]]],[13,\"rawPeriod\",[1,\"rawDash\",\"+\",\"gapLength\"]],[13,\"nDash\",[6,\"std.max\",[[6,\"std.floor\",[[1,[1,[1,\"len\",\"+\",\"gapLength\"],\"/\",\"rawPeriod\"],\"+\",[5,\"0.5\"]]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"scale\",[1,\"len\",\"/\",[6,\"std.max\",[[1,[1,\"nDash\",\"*\",\"rawDash\"],\"+\",[1,[1,\"nDash\",\"-\",[5,\"1\"]],\"*\",\"gapLength\"]],[6,\"d.f32\",[\"EPS\"]]]]]],[13,\"dashL\",[1,\"rawDash\",\"*\",\"scale\"]],[13,\"period\",[1,\"rawPeriod\",\"*\",\"scale\"]],[13,\"kDash\",[6,\"std.clamp\",[[6,\"std.floor\",[[1,\"t\",\"/\",\"period\"]]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"nDash\",\"-\",[5,\"1\"]]]]],[13,\"cDash\",[1,\"t\",\"-\",[1,[1,\"kDash\",\"*\",\"period\"],\"+\",[1,\"dashL\",\"*\",[5,\"0.5\"]]]]],[13,\"axDashRound\",[6,\"std.max\",[[1,[1,\"dashL\",\"*\",[5,\"0.5\"]],\"-\",\"halfT\"],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"axDashSquare\",[1,\"dashL\",\"*\",[5,\"0.5\"]]],[13,\"rawDotPeriod\",[6,\"std.max\",[[1,\"thickness\",\"+\",\"gapLength\"],[6,\"d.f32\",[\"EPS\"]]]]],[13,\"nDot\",[6,\"std.max\",[[6,\"std.floor\",[[1,[1,\"len\",\"/\",\"rawDotPeriod\"],\"+\",[5,\"0.5\"]]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"dotPeriod\",[1,\"len\",\"/\",\"nDot\"]],[13,\"kDot\",[6,\"std.clamp\",[[6,\"std.floor\",[[1,[1,\"t\",\"/\",\"dotPeriod\"],\"+\",[5,\"0.5\"]]]],[6,\"d.f32\",[[5,\"0\"]]],\"nDot\"]]],[13,\"cDot\",[1,\"t\",\"-\",[1,\"kDot\",\"*\",\"dotPeriod\"]]],[13,\"isPattern\",[1,\"style\",\">=\",[5,\"0.5\"]]],[13,\"isDotted\",[1,\"style\",\">=\",[5,\"1.5\"]]],[13,\"c\",[6,\"std.select\",[\"cSolid\",[6,\"std.select\",[\"cDash\",\"cDot\",\"isDotted\"]],\"isPattern\"]]],[13,\"axRound\",[6,\"std.select\",[\"axSolid\",[6,\"std.select\",[\"axDashRound\",[6,\"d.f32\",[[5,\"0\"]]],\"isDotted\"]],\"isPattern\"]]],[13,\"axSquare\",[6,\"std.select\",[\"axSolid\",[6,\"std.select\",[\"axDashSquare\",\"halfT\",\"isDotted\"]],\"isPattern\"]]],[13,\"capHere\",[6,\"std.select\",[\"capStart\",\"capEnd\",[1,\"c\",\">\",[5,\"0\"]]]]],[13,\"sdRound\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[[1,[6,\"std.abs\",[\"c\"]],\"-\",\"axRound\"],[6,\"d.f32\",[[5,\"0\"]]]]],\"perp\"]]]],\"-\",\"halfT\"]],[13,\"qx\",[1,[6,\"std.abs\",[\"c\"]],\"-\",\"axSquare\"]],[13,\"qy\",[1,\"perp\",\"-\",\"halfT\"]],[13,\"sdSquare\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"qx\",[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[\"qy\",[6,\"d.f32\",[[5,\"0\"]]]]]]]]],\"+\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",\"qy\"]],[6,\"d.f32\",[[5,\"0\"]]]]]]],[13,\"sd\",[6,\"std.select\",[\"sdSquare\",\"sdRound\",[1,\"capHere\",\">=\",[5,\"0.5\"]]]]],[13,\"feather\",[1,[5,\"1\"],\"/\",[7,\"viewport\",\"y\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"feather\"],\"feather\",\"sd\"]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"geom.aspectCorrectedUV\":()=>mT,\"geom.aspectCentrePosition\":()=>Zi,segmentArcPerp:()=>KS,\"std.max\":()=>B,\"d.f32\":()=>n,EPS:()=>gu,\"std.floor\":()=>ie,\"std.clamp\":()=>G,\"std.select\":()=>N,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.smoothstep\":()=>be}})&&e.f)({})),Moe=w.fn([m,m,n,m,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=cc(t,o,s),p=__tsover_mul(r,bt),_=Pn(u,pe(p),ae(p)),f=__tsover_add(__tsover_mul(__tsover_mul(_.x,a),Jt),d),h=__tsover_mul(ae(f),i),g=re(__tsover_sub(_.y,h)),y=__tsover_mul(l,.5),b=__tsover_mul(c,.5);return __tsover_sub(1,be(__tsover_sub(y,b),__tsover_add(y,b),g))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"position\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"amplitude\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"centered\",[6,\"geom.aspectCenteredDelta\",[\"uv\",\"position\",\"viewport\"]]],[13,\"angleRad\",[1,\"angle\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosAngle\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinAngle\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rotated\",[6,\"geom.rotate2\",[\"centered\",\"cosAngle\",\"sinAngle\"]]],[13,\"waveInput\",[1,[1,[1,[7,\"rotated\",\"x\"],\"*\",\"frequency\"],\"*\",\"TAU\"],\"+\",\"animTime\"]],[13,\"sineWave\",[1,[6,\"std.sin\",[\"waveInput\"]],\"*\",\"amplitude\"]],[13,\"distanceFromWave\",[6,\"std.abs\",[[1,[7,\"rotated\",\"y\"],\"-\",\"sineWave\"]]]],[13,\"halfThickness\",[1,\"thickness\",\"*\",[5,\"0.5\"]]],[13,\"halfSoftness\",[1,\"softness\",\"*\",[5,\"0.5\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"halfThickness\",\"-\",\"halfSoftness\"],[1,\"halfThickness\",\"+\",\"halfSoftness\"],\"distanceFromWave\"]]]]]]},externals:{\"geom.aspectCenteredDelta\":()=>cc,DEG_TO_RAD:()=>bt,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"geom.rotate2\":()=>Pn,TAU:()=>Jt,\"std.abs\":()=>re,\"std.smoothstep\":()=>be}})&&e.f)({})),QS=w.fn([n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return A(__tsover_add(__tsover_mul(pe(__tsover_mul(Jt,t)),.5),.5),__tsover_add(__tsover_mul(pe(__tsover_mul(Jt,__tsover_sub(t,.333))),.5),.5),__tsover_add(__tsover_mul(pe(__tsover_mul(Jt,__tsover_sub(t,.666))),.5),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[6,\"d.vec3f\",[[1,[1,[6,\"std.cos\",[[1,\"TAU_FULL\",\"*\",\"t\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[6,\"std.cos\",[[1,\"TAU_FULL\",\"*\",[1,\"t\",\"-\",[5,\"0.333\"]]]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[6,\"std.cos\",[[1,\"TAU_FULL\",\"*\",[1,\"t\",\"-\",[5,\"0.666\"]]]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.cos\":()=>pe,TAU_FULL:()=>Jt}})&&e.f)({})),C0=gt({aspect:n,lightPos:m,flareAxis:m,masterFade:n,lightDist:n,lightAngle:n}).$name(\"FlareFrame\"),Eoe=w.fn([m,m,n,m,n,n],C0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Cs(s),c=m(o.x,__tsover_sub(1,o.y)),d=m(__tsover_sub(.5,c.x),__tsover_sub(.5,c.y)),u=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(ae(__tsover_mul(r,3.7)),.04),__tsover_mul(ae(__tsover_mul(r,7.3)),.03)),__tsover_mul(ae(__tsover_mul(r,13.1)),.02)),.97),p=B(__tsover_mul(re(__tsover_sub(c.x,.5)),2),__tsover_mul(re(__tsover_sub(c.y,.5)),2)),_=__tsover_sub(1,__tsover_mul(i,.5)),f=K(1,__tsover_sub(1,be(_,1,p)),i),h=__tsover_mul(__tsover_mul(a,f),u),g=m(__tsover_mul(__tsover_sub(t.x,c.x),l),__tsover_sub(t.y,c.y));return C0({aspect:l,lightPos:c,flareAxis:d,masterFade:h,lightDist:he(g),lightAngle:Xt(g.y,g.x)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"lightPosRaw\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"edgeFade\"}],body:[0,[[13,\"aspect\",[6,\"geom.aspectOf\",[\"viewport\"]]],[13,\"lightPos\",[6,\"d.vec2f\",[[7,\"lightPosRaw\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"lightPosRaw\",\"y\"]]]]],[13,\"flareAxis\",[6,\"d.vec2f\",[[1,[5,\"0.5\"],\"-\",[7,\"lightPos\",\"x\"]],[1,[5,\"0.5\"],\"-\",[7,\"lightPos\",\"y\"]]]]],[13,\"shimmer\",[1,[1,[1,[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"3.7\"]]]],\"*\",[5,\"0.04\"]],\"+\",[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"7.3\"]]]],\"*\",[5,\"0.03\"]]],\"+\",[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"13.1\"]]]],\"*\",[5,\"0.02\"]]],\"+\",[5,\"0.97\"]]],[13,\"edgeDist\",[6,\"std.max\",[[1,[6,\"std.abs\",[[1,[7,\"lightPos\",\"x\"],\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]],[1,[6,\"std.abs\",[[1,[7,\"lightPos\",\"y\"],\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]]]],[13,\"fadeStart\",[1,[5,\"1\"],\"-\",[1,\"edgeFade\",\"*\",[5,\"0.5\"]]]],[13,\"edgeFadeV\",[6,\"std.mix\",[[5,\"1\"],[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"fadeStart\",[5,\"1\"],\"edgeDist\"]]],\"edgeFade\"]]],[13,\"masterFade\",[1,[1,\"intensity\",\"*\",\"edgeFadeV\"],\"*\",\"shimmer\"]],[13,\"toLightAR\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"lightPos\",\"x\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[7,\"lightPos\",\"y\"]]]]],[13,\"lightDist\",[6,\"std.length\",[\"toLightAR\"]]],[13,\"lightAngle\",[6,\"std.atan2\",[[7,\"toLightAR\",\"y\"],[7,\"toLightAR\",\"x\"]]]],[10,[6,\"FlareFrame\",[[104,{aspect:\"aspect\",lightPos:\"lightPos\",flareAxis:\"flareAxis\",masterFade:\"masterFade\",lightDist:\"lightDist\",lightAngle:\"lightAngle\"}]]]]]]},externals:{\"geom.aspectOf\":()=>Cs,\"d.vec2f\":()=>m,\"std.sin\":()=>ae,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.mix\":()=>K,\"std.smoothstep\":()=>be,\"std.length\":()=>he,\"std.atan2\":()=>Xt,FlareFrame:()=>C0}})&&e.f)({})),koe=w.fn([m,n,m,m,n,n,n,n,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=d.x,_=d.y,f=d.z,h=d.w,g=u.x,y=u.y,b=u.z,x=u.w,P=r.add(o.mul(__tsover_mul(__tsover_mul(p,i),2))),C=he(m(__tsover_mul(__tsover_sub(t.x,P.x),s),__tsover_sub(t.y,P.y))),E=_,R=__tsover_sub(1,be(__tsover_mul(E,.12),E,C)),U=__tsover_sub(1,be(__tsover_mul(E,.05),__tsover_mul(E,.45),C)),L=__tsover_mul(R,__tsover_sub(1,__tsover_mul(U,h))),k=__tsover_add(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(a,4.3),x)),.08),__tsover_mul(ae(__tsover_add(__tsover_mul(a,9.1),__tsover_mul(x,1.7))),.05)),.95),D=_v(C,__tsover_mul(E,.78),__tsover_mul(__tsover_mul(E,l),.14),__tsover_mul(E,.1)),O=D.x,F=D.y,q=D.z,Y=__tsover_mul(__tsover_mul(f,c),k);return A(__tsover_mul(__tsover_add(__tsover_mul(L,g),__tsover_mul(O,l)),Y),__tsover_mul(__tsover_add(__tsover_mul(L,y),__tsover_mul(F,l)),Y),__tsover_mul(__tsover_add(__tsover_mul(L,b),__tsover_mul(q,l)),Y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"lightPos\"},{type:\"i\",name:\"flareAxis\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"ghostSpread\"},{type:\"i\",name:\"ghostChroma\"},{type:\"i\",name:\"ghostInt\"},{type:\"i\",name:\"disc\"},{type:\"i\",name:\"tint\"}],body:[0,[[13,\"offset\",[7,\"disc\",\"x\"]],[13,\"size\",[7,\"disc\",\"y\"]],[13,\"bright\",[7,\"disc\",\"z\"]],[13,\"hollow\",[7,\"disc\",\"w\"]],[13,\"r\",[7,\"tint\",\"x\"]],[13,\"g\",[7,\"tint\",\"y\"]],[13,\"b\",[7,\"tint\",\"z\"]],[13,\"gPhase\",[7,\"tint\",\"w\"]],[13,\"ghostPos\",[6,[7,\"lightPos\",\"add\"],[[6,[7,\"flareAxis\",\"mul\"],[[1,[1,\"offset\",\"*\",\"ghostSpread\"],\"*\",[5,\"2\"]]]]]]],[13,\"toGhost\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"ghostPos\",\"x\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[7,\"ghostPos\",\"y\"]]]]],[13,\"dist\",[6,\"std.length\",[\"toGhost\"]]],[13,\"sz\",\"size\"],[13,\"discShape\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"sz\",\"*\",[5,\"0.12\"]],\"sz\",\"dist\"]]]],[13,\"innerFade\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"sz\",\"*\",[5,\"0.05\"]],[1,\"sz\",\"*\",[5,\"0.45\"]],\"dist\"]]]],[13,\"shape\",[1,\"discShape\",\"*\",[1,[5,\"1\"],\"-\",[1,\"innerFade\",\"*\",\"hollow\"]]]],[13,\"gShimmer\",[1,[1,[1,[6,\"std.sin\",[[1,[1,\"t\",\"*\",[5,\"4.3\"]],\"+\",\"gPhase\"]]],\"*\",[5,\"0.08\"]],\"+\",[1,[6,\"std.sin\",[[1,[1,\"t\",\"*\",[5,\"9.1\"]],\"+\",[1,\"gPhase\",\"*\",[5,\"1.7\"]]]]],\"*\",[5,\"0.05\"]]],\"+\",[5,\"0.95\"]]],[13,\"ringCenter\",[1,\"sz\",\"*\",[5,\"0.78\"]]],[13,\"chromaSpread\",[1,[1,\"sz\",\"*\",\"ghostChroma\"],\"*\",[5,\"0.14\"]]],[13,\"ringWidth\",[1,\"sz\",\"*\",[5,\"0.1\"]]],[13,\"ring\",[6,\"lightfields.chromaticRingBand\",[\"dist\",\"ringCenter\",\"chromaSpread\",\"ringWidth\"]]],[13,\"rRing\",[7,\"ring\",\"x\"]],[13,\"gRing\",[7,\"ring\",\"y\"]],[13,\"bRing\",[7,\"ring\",\"z\"]],[13,\"brightness\",[1,[1,\"bright\",\"*\",\"ghostInt\"],\"*\",\"gShimmer\"]],[10,[6,\"d.vec3f\",[[1,[1,[1,\"shape\",\"*\",\"r\"],\"+\",[1,\"rRing\",\"*\",\"ghostChroma\"]],\"*\",\"brightness\"],[1,[1,[1,\"shape\",\"*\",\"g\"],\"+\",[1,\"gRing\",\"*\",\"ghostChroma\"]],\"*\",\"brightness\"],[1,[1,[1,\"shape\",\"*\",\"b\"],\"+\",[1,\"bRing\",\"*\",\"ghostChroma\"]],\"*\",\"brightness\"]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.smoothstep\":()=>be,\"std.sin\":()=>ae,\"lightfields.chromaticRingBand\":()=>_v,\"d.vec3f\":()=>A}})&&e.f)({})),Boe=w.fn([m,n,m,n,n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=m(__tsover_sub(1,r.x),__tsover_sub(1,r.y)),u=m(__tsover_mul(__tsover_sub(t.x,d.x),s),__tsover_sub(t.y,d.y)),p=_v(he(u),i,__tsover_mul(__tsover_mul(i,l),.06),__tsover_add(__tsover_mul(c,.12),.005)),_=Xt(u.y,u.x),f=__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(_,3),__tsover_mul(o,.5))),.12),.88);return p.mul(a).mul(f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"lightPos\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"haloInt\"},{type:\"i\",name:\"haloRad\"},{type:\"i\",name:\"haloChroma\"},{type:\"i\",name:\"haloSoft\"}],body:[0,[[13,\"antiLight\",[6,\"d.vec2f\",[[1,[5,\"1\"],\"-\",[7,\"lightPos\",\"x\"]],[1,[5,\"1\"],\"-\",[7,\"lightPos\",\"y\"]]]]],[13,\"toAntiLight\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"antiLight\",\"x\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[7,\"antiLight\",\"y\"]]]]],[13,\"antiLightDist\",[6,\"std.length\",[\"toAntiLight\"]]],[13,\"haloChromaSpread\",[1,[1,\"haloRad\",\"*\",\"haloChroma\"],\"*\",[5,\"0.06\"]]],[13,\"haloWidth\",[1,[1,\"haloSoft\",\"*\",[5,\"0.12\"]],\"+\",[5,\"0.005\"]]],[13,\"halo\",[6,\"lightfields.chromaticRingBand\",[\"antiLightDist\",\"haloRad\",\"haloChromaSpread\",\"haloWidth\"]]],[13,\"haloAngle\",[6,\"std.atan2\",[[7,\"toAntiLight\",\"y\"],[7,\"toAntiLight\",\"x\"]]]],[13,\"haloAngVar\",[1,[1,[6,\"std.sin\",[[1,[1,\"haloAngle\",\"*\",[5,\"3\"]],\"+\",[1,\"t\",\"*\",[5,\"0.5\"]]]]],\"*\",[5,\"0.12\"]],\"+\",[5,\"0.88\"]]],[10,[6,[7,[6,[7,\"halo\",\"mul\"],[\"haloInt\"]],\"mul\"],[\"haloAngVar\"]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"lightfields.chromaticRingBand\":()=>_v,\"std.atan2\":()=>Xt,\"std.sin\":()=>ae}})&&e.f)({})),zoe=w.fn([n,m,n,n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=Xt(s.y,__tsover_mul(s.x,r)),d=__tsover_add(__tsover_mul(c,.5),__tsover_mul(o,.06)),u=__tsover_add(t,d),p=p0(u,__tsover_div(i,2),3.5),_=__tsover_mul(p0(__tsover_add(u,.25),__tsover_div(i,2),7),.4),f=__tsover_div(1,__tsover_add(1,__tsover_mul(__tsover_mul(a,a),60))),h=QS(__tsover_add(__tsover_div(u,Jt),.5));return K(A(1,.97,.92),h,.12).mul(__tsover_mul(__tsover_mul(__tsover_add(p,_),f),l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"flareAxis\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"lightDist\"},{type:\"i\",name:\"starburstPts\"},{type:\"i\",name:\"starburstInt\"}],body:[0,[[13,\"axisAng\",[6,\"std.atan2\",[[7,\"flareAxis\",\"y\"],[1,[7,\"flareAxis\",\"x\"],\"*\",\"aspect\"]]]],[13,\"rotation\",[1,[1,\"axisAng\",\"*\",[5,\"0.5\"]],\"+\",[1,\"t\",\"*\",[5,\"0.06\"]]]],[13,\"spikeAngle\",[1,\"lightAngle\",\"+\",\"rotation\"]],[13,\"primarySpikes\",[6,\"lightfields.angularCosineSpikes\",[\"spikeAngle\",[1,\"starburstPts\",\"/\",[5,\"2\"]],[5,\"3.5\"]]]],[13,\"secondarySpikes\",[1,[6,\"lightfields.angularCosineSpikes\",[[1,\"spikeAngle\",\"+\",[5,\"0.25\"]],[1,\"starburstPts\",\"/\",[5,\"2\"]],[5,\"7\"]]],\"*\",[5,\"0.4\"]]],[13,\"radialFall\",[1,[5,\"1\"],\"/\",[1,[5,\"1\"],\"+\",[1,[1,\"lightDist\",\"*\",\"lightDist\"],\"*\",[5,\"60\"]]]]],[13,\"starSpectral\",[6,\"lensFlareSpectral\",[[1,[1,\"spikeAngle\",\"/\",\"TAU_FULL\"],\"+\",[5,\"0.5\"]]]]],[13,\"starTint\",[6,\"std.mix\",[[6,\"d.vec3f\",[[5,\"1\"],[5,\"0.97\"],[5,\"0.92\"]]],\"starSpectral\",[5,\"0.12\"]]]],[10,[6,[7,\"starTint\",\"mul\"],[[1,[1,[1,\"primarySpikes\",\"+\",\"secondarySpikes\"],\"*\",\"radialFall\"],\"*\",\"starburstInt\"]]]]]]},externals:{\"std.atan2\":()=>Xt,\"lightfields.angularCosineSpikes\":()=>p0,lensFlareSpectral:()=>QS,TAU_FULL:()=>Jt,\"std.mix\":()=>K,\"d.vec3f\":()=>A}})&&e.f)({})),Roe=w.fn([m,m,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=re(__tsover_sub(t.y,s.y)),i=re(__tsover_sub(t.x,s.x)),l=Tt(__tsover_mul(__tsover_mul(-800,a),a)),c=__tsover_add(__tsover_mul(__tsover_mul(r,r),.25),.001),d=Tt(__tsover_div(__tsover_mul(__tsover_mul(-1,i),i),__tsover_mul(c,.7))),u=Tt(__tsover_div(__tsover_mul(__tsover_mul(-1,i),i),c)),p=Tt(__tsover_div(__tsover_mul(__tsover_mul(-1,i),i),__tsover_mul(c,1.4)));return A(__tsover_mul(__tsover_mul(l,d),.5),__tsover_mul(__tsover_mul(l,u),.65),__tsover_mul(__tsover_mul(l,p),.9)).mul(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"lightPos\"},{type:\"i\",name:\"streakLen\"},{type:\"i\",name:\"streakInt\"}],body:[0,[[13,\"dy\",[6,\"std.abs\",[[1,[7,\"uv\",\"y\"],\"-\",[7,\"lightPos\",\"y\"]]]]],[13,\"dx\",[6,\"std.abs\",[[1,[7,\"uv\",\"x\"],\"-\",[7,\"lightPos\",\"x\"]]]]],[13,\"vertFall\",[6,\"std.exp\",[[1,[1,[4,\"-\",[5,\"800\"]],\"*\",\"dy\"],\"*\",\"dy\"]]]],[13,\"lenSq\",[1,[1,[1,\"streakLen\",\"*\",\"streakLen\"],\"*\",[5,\"0.25\"]],\"+\",[5,\"0.001\"]]],[13,\"rStreak\",[6,\"std.exp\",[[1,[1,[1,[4,\"-\",[5,\"1\"]],\"*\",\"dx\"],\"*\",\"dx\"],\"/\",[1,\"lenSq\",\"*\",[5,\"0.7\"]]]]]],[13,\"gStreak\",[6,\"std.exp\",[[1,[1,[1,[4,\"-\",[5,\"1\"]],\"*\",\"dx\"],\"*\",\"dx\"],\"/\",\"lenSq\"]]]],[13,\"bStreak\",[6,\"std.exp\",[[1,[1,[1,[4,\"-\",[5,\"1\"]],\"*\",\"dx\"],\"*\",\"dx\"],\"/\",[1,\"lenSq\",\"*\",[5,\"1.4\"]]]]]],[10,[6,[7,[6,\"d.vec3f\",[[1,[1,\"vertFall\",\"*\",\"rStreak\"],\"*\",[5,\"0.5\"]],[1,[1,\"vertFall\",\"*\",\"gStreak\"],\"*\",[5,\"0.65\"]],[1,[1,\"vertFall\",\"*\",\"bStreak\"],\"*\",[5,\"0.9\"]]]],\"mul\"],[\"streakInt\"]]]]]},externals:{\"std.abs\":()=>re,\"std.exp\":()=>Tt,\"d.vec3f\":()=>A}})&&e.f)({})),Doe=w.fn([n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=ET(t,__tsover_div(1,__tsover_add(__tsover_mul(s,s),.01)));return A(1,.98,.95).mul(__tsover_mul(o,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lightDist\"},{type:\"i\",name:\"glareSz\"},{type:\"i\",name:\"glareInt\"}],body:[0,[[13,\"glareSharpness\",[1,[5,\"1\"],\"/\",[1,[1,\"glareSz\",\"*\",\"glareSz\"],\"+\",[5,\"0.01\"]]]],[13,\"glareFalloff\",[6,\"lightfields.radialGaussianFalloff\",[\"lightDist\",\"glareSharpness\"]]],[10,[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"0.98\"],[5,\"0.95\"]]],\"mul\"],[[1,\"glareFalloff\",\"*\",\"glareInt\"]]]]]]},externals:{\"lightfields.radialGaussianFalloff\":()=>ET,\"d.vec3f\":()=>A}})&&e.f)({})),Loe=w.fn([n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Tt(__tsover_mul(__tsover_mul(-500,t),t)),o=__tsover_sub(1,be(0,.025,t));return A(1,.98,.96).mul(__tsover_mul(__tsover_add(r,__tsover_mul(o,1.5)),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lightDist\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"coreSoft\",[6,\"std.exp\",[[1,[1,[4,\"-\",[5,\"500\"]],\"*\",\"lightDist\"],\"*\",\"lightDist\"]]]],[13,\"coreSharp\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.025\"],\"lightDist\"]]]],[10,[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"0.98\"],[5,\"0.96\"]]],\"mul\"],[[1,[1,\"coreSoft\",\"+\",[1,\"coreSharp\",\"*\",[5,\"1.5\"]]],\"*\",\"intensity\"]]]]]]},externals:{\"std.exp\":()=>Tt,\"std.smoothstep\":()=>be,\"d.vec3f\":()=>A}})&&e.f)({})),Uoe=w.fn([A,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t.mul(s);return S(r,G(__tsover_mul(rv(r),2),0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"flareTotal\"},{type:\"i\",name:\"masterFade\"}],body:[0,[[13,\"finalColor\",[6,[7,\"flareTotal\",\"mul\"],[\"masterFade\"]]],[13,\"flareAlpha\",[6,\"std.clamp\",[[1,[6,\"tone.luma601Dot\",[\"finalColor\"]],\"*\",[5,\"2\"]],[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[\"finalColor\",\"flareAlpha\"]]]]]},externals:{\"std.clamp\":()=>G,\"tone.luma601Dot\":()=>rv,\"d.vec4f\":()=>S}})&&e.f)({})),Ooe=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(o,.03),i=m(Aa(t.mul(5).add(m(a,a))),Aa(t.mul(5).add(m(31.4,31.4)).add(m(a,a)))).sub(m(.5,.5)),l=m(__tsover_div(1,s.x),__tsover_div(1,s.y));return t.add(i.mul(r).mul(l).mul(8))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"bleed\"},{type:\"i\",name:\"time\"}],body:[0,[[13,\"t\",[1,\"time\",\"*\",[5,\"0.03\"]]],[13,\"wob\",[6,[7,[6,\"d.vec2f\",[[6,\"noise.value12\",[[6,[7,[6,[7,\"uv\",\"mul\"],[[5,\"5\"]]],\"add\"],[[6,\"d.vec2f\",[\"t\",\"t\"]]]]]],[6,\"noise.value12\",[[6,[7,[6,[7,[6,[7,\"uv\",\"mul\"],[[5,\"5\"]]],\"add\"],[[6,\"d.vec2f\",[[5,\"31.4\"],[5,\"31.4\"]]]]],\"add\"],[[6,\"d.vec2f\",[\"t\",\"t\"]]]]]]]],\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"texel\",[6,\"d.vec2f\",[[1,[5,\"1\"],\"/\",[7,\"viewport\",\"x\"]],[1,[5,\"1\"],\"/\",[7,\"viewport\",\"y\"]]]]],[10,[6,[7,\"uv\",\"add\"],[[6,[7,[6,[7,[6,[7,\"wob\",\"mul\"],[\"bleed\"]],\"mul\"],[\"texel\"]],\"mul\"],[[5,\"8\"]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"noise.value12\":()=>Aa}})&&e.f)({})),Goe=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return m(__tsover_add(t.x,__tsover_div(r,s.x)),__tsover_add(t.y,__tsover_div(o,s.y)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvBase\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"uvBase\",\"x\"],\"+\",[1,\"ox\",\"/\",[7,\"viewport\",\"x\"]]],[1,[7,\"uvBase\",\"y\"],\"+\",[1,\"oy\",\"/\",[7,\"viewport\",\"y\"]]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Foe=w.fn([m,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Aa(t.mul(s).mul(.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"}],body:[0,[[10,[6,\"noise.value12\",[[6,[7,[6,[7,\"uv\",\"mul\"],[\"viewport\"]],\"mul\"],[[5,\"0.5\"]]]]]]]]},externals:{\"noise.value12\":()=>Aa}})&&e.f)({})),Ioe=w.fn([S,S,S,S,A,A,A,A,n,S,S,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_,f,h)=>{\"use gpu\";let g=t.mul(d),y=s.mul(d),b=r.mul(d),x=o.mul(d),P=A(g.x,g.y,g.z),C=A(y.x,y.y,y.z),E=A(b.x,b.y,b.z),R=A(x.x,x.y,x.z),U=a.mul(d).sub(P.mul(P)),L=i.mul(d).sub(C.mul(C)),k=l.mul(d).sub(E.mul(E)),D=c.mul(d).sub(R.mul(R)),O=__tsover_add(__tsover_add(U.x,U.y),U.z),F=__tsover_add(__tsover_add(L.x,L.y),L.z),q=__tsover_add(__tsover_add(k.x,k.y),k.z),Y=__tsover_add(__tsover_add(D.x,D.y),D.z),V=S(g.x,g.y,g.z,g.w),X=O;V=N(V,y,F<X),X=ye(X,F),V=N(V,b,q<X),X=ye(X,q),V=N(V,x,Y<X),X=ye(X,Y);let Q=Yt(V),te=A(Q.x,Q.y,Q.z),J=K(1,h,_),oe=A(p.x,p.y,p.z);return te=K(te.mul(J),te.mul(oe),A(__tsover_mul(_,.4))),K(A(u.x,u.y,u.z),te,A(f))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sum0\"},{type:\"i\",name:\"sum1\"},{type:\"i\",name:\"sum2\"},{type:\"i\",name:\"sum3\"},{type:\"i\",name:\"sq0\"},{type:\"i\",name:\"sq1\"},{type:\"i\",name:\"sq2\"},{type:\"i\",name:\"sq3\"},{type:\"i\",name:\"invN\"},{type:\"i\",name:\"originalBleed\"},{type:\"i\",name:\"paperColor\"},{type:\"i\",name:\"paper\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"grain\"}],body:[0,[[13,\"mean0\",[6,[7,\"sum0\",\"mul\"],[\"invN\"]]],[13,\"mean1\",[6,[7,\"sum1\",\"mul\"],[\"invN\"]]],[13,\"mean2\",[6,[7,\"sum2\",\"mul\"],[\"invN\"]]],[13,\"mean3\",[6,[7,\"sum3\",\"mul\"],[\"invN\"]]],[13,\"rgb0\",[6,\"d.vec3f\",[[7,\"mean0\",\"x\"],[7,\"mean0\",\"y\"],[7,\"mean0\",\"z\"]]]],[13,\"rgb1\",[6,\"d.vec3f\",[[7,\"mean1\",\"x\"],[7,\"mean1\",\"y\"],[7,\"mean1\",\"z\"]]]],[13,\"rgb2\",[6,\"d.vec3f\",[[7,\"mean2\",\"x\"],[7,\"mean2\",\"y\"],[7,\"mean2\",\"z\"]]]],[13,\"rgb3\",[6,\"d.vec3f\",[[7,\"mean3\",\"x\"],[7,\"mean3\",\"y\"],[7,\"mean3\",\"z\"]]]],[13,\"var0\",[6,[7,[6,[7,\"sq0\",\"mul\"],[\"invN\"]],\"sub\"],[[6,[7,\"rgb0\",\"mul\"],[\"rgb0\"]]]]],[13,\"var1\",[6,[7,[6,[7,\"sq1\",\"mul\"],[\"invN\"]],\"sub\"],[[6,[7,\"rgb1\",\"mul\"],[\"rgb1\"]]]]],[13,\"var2\",[6,[7,[6,[7,\"sq2\",\"mul\"],[\"invN\"]],\"sub\"],[[6,[7,\"rgb2\",\"mul\"],[\"rgb2\"]]]]],[13,\"var3\",[6,[7,[6,[7,\"sq3\",\"mul\"],[\"invN\"]],\"sub\"],[[6,[7,\"rgb3\",\"mul\"],[\"rgb3\"]]]]],[13,\"sigma0\",[1,[1,[7,\"var0\",\"x\"],\"+\",[7,\"var0\",\"y\"]],\"+\",[7,\"var0\",\"z\"]]],[13,\"sigma1\",[1,[1,[7,\"var1\",\"x\"],\"+\",[7,\"var1\",\"y\"]],\"+\",[7,\"var1\",\"z\"]]],[13,\"sigma2\",[1,[1,[7,\"var2\",\"x\"],\"+\",[7,\"var2\",\"y\"]],\"+\",[7,\"var2\",\"z\"]]],[13,\"sigma3\",[1,[1,[7,\"var3\",\"x\"],\"+\",[7,\"var3\",\"y\"]],\"+\",[7,\"var3\",\"z\"]]],[12,\"picked\",[6,\"d.vec4f\",[[7,\"mean0\",\"x\"],[7,\"mean0\",\"y\"],[7,\"mean0\",\"z\"],[7,\"mean0\",\"w\"]]]],[12,\"best\",\"sigma0\"],[2,\"picked\",\"=\",[6,\"std.select\",[\"picked\",\"mean1\",[1,\"sigma1\",\"<\",\"best\"]]]],[2,\"best\",\"=\",[6,\"std.min\",[\"best\",\"sigma1\"]]],[2,\"picked\",\"=\",[6,\"std.select\",[\"picked\",\"mean2\",[1,\"sigma2\",\"<\",\"best\"]]]],[2,\"best\",\"=\",[6,\"std.min\",[\"best\",\"sigma2\"]]],[2,\"picked\",\"=\",[6,\"std.select\",[\"picked\",\"mean3\",[1,\"sigma3\",\"<\",\"best\"]]]],[2,\"best\",\"=\",[6,\"std.min\",[\"best\",\"sigma3\"]]],[13,\"straight\",[6,\"blend.unpremultiplyAlpha\",[\"picked\"]]],[12,\"result\",[6,\"d.vec3f\",[[7,\"straight\",\"x\"],[7,\"straight\",\"y\"],[7,\"straight\",\"z\"]]]],[13,\"paperMul\",[6,\"std.mix\",[[5,\"1\"],\"grain\",\"paper\"]]],[13,\"paperTint\",[6,\"d.vec3f\",[[7,\"paperColor\",\"x\"],[7,\"paperColor\",\"y\"],[7,\"paperColor\",\"z\"]]]],[2,\"result\",\"=\",[6,\"std.mix\",[[6,[7,\"result\",\"mul\"],[\"paperMul\"]],[6,[7,\"result\",\"mul\"],[\"paperTint\"]],[6,\"d.vec3f\",[[1,\"paper\",\"*\",[5,\"0.4\"]]]]]]],[13,\"origRgb\",[6,\"d.vec3f\",[[7,\"originalBleed\",\"x\"],[7,\"originalBleed\",\"y\"],[7,\"originalBleed\",\"z\"]]]],[13,\"finalRGB\",[6,\"std.mix\",[\"origRgb\",\"result\",[6,\"d.vec3f\",[\"strength\"]]]]],[10,\"finalRGB\"]]]},externals:{\"d.vec3f\":()=>A,\"d.vec4f\":()=>S,\"std.select\":()=>N,\"std.min\":()=>ye,\"blend.unpremultiplyAlpha\":()=>Yt,\"std.mix\":()=>K}})&&e.f)({})),ZS=Math.sin(.785),JS=Math.cos(.785),eP=Math.sin(-.785),tP=Math.cos(-.785),um=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Ys(t)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[10,[6,\"noise.hash12\",[\"p\"]]]]]},externals:{\"noise.hash12\":()=>Ys}})&&e.f)({})),M0=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=ie(t),r=Re(t),o=r.mul(r).mul(r.mul(-2).add(3)),a=um(s),i=um(m(__tsover_add(s.x,1),s.y)),l=um(m(s.x,__tsover_add(s.y,1))),c=um(m(__tsover_add(s.x,1),__tsover_add(s.y,1)));return K(K(a,i,o.x),K(l,c,o.x),o.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"i\",[6,\"std.floor\",[\"p\"]]],[13,\"f\",[6,\"std.fract\",[\"p\"]]],[13,\"u\",[6,[7,[6,[7,\"f\",\"mul\"],[\"f\"]],\"mul\"],[[6,[7,[6,[7,\"f\",\"mul\"],[[4,\"-\",[5,\"2\"]]]],\"add\"],[[5,\"3\"]]]]]],[13,\"a\",[6,\"chalkHash21\",[\"i\"]]],[13,\"b\",[6,\"chalkHash21\",[[6,\"d.vec2f\",[[1,[7,\"i\",\"x\"],\"+\",[5,\"1\"]],[7,\"i\",\"y\"]]]]]],[13,\"c\",[6,\"chalkHash21\",[[6,\"d.vec2f\",[[7,\"i\",\"x\"],[1,[7,\"i\",\"y\"],\"+\",[5,\"1\"]]]]]]],[13,\"dd\",[6,\"chalkHash21\",[[6,\"d.vec2f\",[[1,[7,\"i\",\"x\"],\"+\",[5,\"1\"]],[1,[7,\"i\",\"y\"],\"+\",[5,\"1\"]]]]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[\"a\",\"b\",[7,\"u\",\"x\"]]],[6,\"std.mix\",[\"c\",\"dd\",[7,\"u\",\"x\"]]],[7,\"u\",\"y\"]]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,chalkHash21:()=>um,\"d.vec2f\":()=>m,\"std.mix\":()=>K}})&&e.f)({})),sP=w.fn([S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Se(A(t.x,t.y,t.z),A(.299,.587,.114))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"}],body:[0,[[10,[6,\"std.dot\",[[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),Yoe=w.fn([m,m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_div(1,s.x),l=__tsover_div(1,s.y);return m(__tsover_add(t.x,__tsover_mul(__tsover_mul(i,r),o)),__tsover_add(t.y,__tsover_mul(__tsover_mul(l,r),a)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"edgeThickness\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[13,\"texelX\",[1,[5,\"1\"],\"/\",[7,\"viewport\",\"x\"]]],[13,\"texelY\",[1,[5,\"1\"],\"/\",[7,\"viewport\",\"y\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",[1,[1,\"texelX\",\"*\",\"edgeThickness\"],\"*\",\"ox\"]],[1,[7,\"uv\",\"y\"],\"+\",[1,[1,\"texelY\",\"*\",\"edgeThickness\"],\"*\",\"oy\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),p1=w.fn([m,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_add(__tsover_mul(t.x,o),__tsover_mul(t.y,a)),c=__tsover_mul(re(__tsover_sub(Re(__tsover_div(l,s)),.5)),2),d=__tsover_sub(1,be(0,.35,c));return __tsover_mul(d,Et(i,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"P\"},{type:\"i\",name:\"hatchScale\"},{type:\"i\",name:\"shade\"},{type:\"i\",name:\"sa\"},{type:\"i\",name:\"ca\"},{type:\"i\",name:\"thr\"}],body:[0,[[13,\"ry\",[1,[1,[7,\"P\",\"x\"],\"*\",\"sa\"],\"+\",[1,[7,\"P\",\"y\"],\"*\",\"ca\"]]],[13,\"hl\",[1,[6,\"std.abs\",[[1,[6,\"std.fract\",[[1,\"ry\",\"/\",\"hatchScale\"]]],\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"line\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.35\"],\"hl\"]]]],[10,[1,\"line\",\"*\",[6,\"std.step\",[\"thr\",\"shade\"]]]]]]},externals:{\"std.abs\":()=>re,\"std.fract\":()=>Re,\"std.smoothstep\":()=>be,\"std.step\":()=>Et}})&&e.f)({})),Woe=w.fn([S,S,m,m,S,S,S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=t.x,u=t.y,p=t.z,_=t.w,f=s.x,h=s.y,g=s.z,y=s.w,b=a.x,x=a.y,P=i.x,C=i.y,E=i.z,R=i.w,U=__tsover_add(__tsover_add(__tsover_add(__tsover_sub(__tsover_sub(-d,__tsover_mul(_,2)),h),p),__tsover_mul(f,2)),y),L=__tsover_add(__tsover_add(__tsover_add(__tsover_sub(__tsover_sub(-d,__tsover_mul(u,2)),p),h),__tsover_mul(g,2)),y),k=Ue(__tsover_add(__tsover_mul(U,U),__tsover_mul(L,L))),D=K(n(1),.08,P),O=be(__tsover_mul(D,.4),D,k),F=__tsover_sub(1,b),q=r.mul(o),Y=__tsover_mul(B(B(p1(q,C,F,ZS,JS,.22),p1(q,C,F,eP,tP,.5)),p1(q,C,F,0,1,.78)),E),V=K(__tsover_sub(1,R),n(1),M0(q.mul(.5))),X=G(__tsover_mul(B(O,Y),V),0,1),Q=__tsover_mul(__tsover_sub(M0(q.mul(.9)),.5),.04);return S(K(l.xyz.add(Q),c.xyz,A(X)),x)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sobelA\"},{type:\"i\",name:\"sobelB\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"misc\"},{type:\"i\",name:\"params\"},{type:\"i\",name:\"boardColor\"},{type:\"i\",name:\"chalkColor\"}],body:[0,[[13,\"tl\",[7,\"sobelA\",\"x\"]],[13,\"t\",[7,\"sobelA\",\"y\"]],[13,\"tr\",[7,\"sobelA\",\"z\"]],[13,\"l\",[7,\"sobelA\",\"w\"]],[13,\"r\",[7,\"sobelB\",\"x\"]],[13,\"bl\",[7,\"sobelB\",\"y\"]],[13,\"bo\",[7,\"sobelB\",\"z\"]],[13,\"br\",[7,\"sobelB\",\"w\"]],[13,\"centerLum\",[7,\"misc\",\"x\"]],[13,\"centerAlpha\",[7,\"misc\",\"y\"]],[13,\"edgeSensitivity\",[7,\"params\",\"x\"]],[13,\"hatchScale\",[7,\"params\",\"y\"]],[13,\"shading\",[7,\"params\",\"z\"]],[13,\"grain\",[7,\"params\",\"w\"]],[13,\"gx\",[1,[1,[1,[1,[1,[4,\"-\",\"tl\"],\"-\",[1,\"l\",\"*\",[5,\"2\"]]],\"-\",\"bl\"],\"+\",\"tr\"],\"+\",[1,\"r\",\"*\",[5,\"2\"]]],\"+\",\"br\"]],[13,\"gy\",[1,[1,[1,[1,[1,[4,\"-\",\"tl\"],\"-\",[1,\"t\",\"*\",[5,\"2\"]]],\"-\",\"tr\"],\"+\",\"bl\"],\"+\",[1,\"bo\",\"*\",[5,\"2\"]]],\"+\",\"br\"]],[13,\"edgeMag\",[6,\"std.sqrt\",[[1,[1,\"gx\",\"*\",\"gx\"],\"+\",[1,\"gy\",\"*\",\"gy\"]]]]],[13,\"edgeHigh\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],[5,\"0.08\"],\"edgeSensitivity\"]]],[13,\"edge\",[6,\"std.smoothstep\",[[1,\"edgeHigh\",\"*\",[5,\"0.4\"]],\"edgeHigh\",\"edgeMag\"]]],[13,\"shade\",[1,[5,\"1\"],\"-\",\"centerLum\"]],[13,\"P\",[6,[7,\"uv\",\"mul\"],[\"viewport\"]]],[13,\"hatch\",[1,[6,\"std.max\",[[6,\"std.max\",[[6,\"chalkHatchLine\",[\"P\",\"hatchScale\",\"shade\",\"SIN_045\",\"COS_045\",[5,\"0.22\"]]],[6,\"chalkHatchLine\",[\"P\",\"hatchScale\",\"shade\",\"SIN_N45\",\"COS_N45\",[5,\"0.5\"]]]]],[6,\"chalkHatchLine\",[\"P\",\"hatchScale\",\"shade\",[5,\"0\"],[5,\"1\"],[5,\"0.78\"]]]]],\"*\",\"shading\"]],[13,\"dust\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",\"grain\"],[6,\"d.f32\",[[5,\"1\"]]],[6,\"chalkVnoise\",[[6,[7,\"P\",\"mul\"],[[5,\"0.5\"]]]]]]]],[13,\"chalkMask\",[6,\"std.clamp\",[[1,[6,\"std.max\",[\"edge\",\"hatch\"]],\"*\",\"dust\"],[5,\"0\"],[5,\"1\"]]]],[13,\"boardGrain\",[1,[1,[6,\"chalkVnoise\",[[6,[7,\"P\",\"mul\"],[[5,\"0.9\"]]]]],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.04\"]]],[13,\"boardRGB\",[6,[7,[7,\"boardColor\",\"xyz\"],\"add\"],[\"boardGrain\"]]],[13,\"col\",[6,\"std.mix\",[\"boardRGB\",[7,\"chalkColor\",\"xyz\"],[6,\"d.vec3f\",[\"chalkMask\"]]]]],[10,[6,\"d.vec4f\",[\"col\",\"centerAlpha\"]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.mix\":()=>K,\"d.f32\":()=>n,\"std.smoothstep\":()=>be,\"std.max\":()=>B,chalkHatchLine:()=>p1,SIN_045:()=>ZS,COS_045:()=>JS,SIN_N45:()=>eP,COS_N45:()=>tP,chalkVnoise:()=>M0,\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})),rP=1080,E0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cellCenter:m,cellUV:m,isOutside:n}),\"AsciiGrid\"),qoe=w.fn([m,m,n,n],E0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_div(s.y,rP),i=__tsover_mul(r,a),l=m(__tsover_div(s.x,i),__tsover_div(s.y,i)),c=m(__tsover_mul(t.x,l.x),__tsover_mul(t.y,l.y)),d=ie(c),u=Re(c),p=__tsover_sub(u.x,.5),_=__tsover_sub(u.y,.5),f=m(__tsover_add(__tsover_div(p,o),.5),__tsover_add(__tsover_div(_,o),.5)),h=__tsover_mul(o,.5),g=N(n(0),n(1),re(p)>h||re(_)>h);return E0({cellCenter:m(__tsover_div(__tsover_add(d.x,.5),l.x),__tsover_div(__tsover_add(d.y,.5),l.y)),cellUV:f,isOutside:g})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"cellSize\"},{type:\"i\",name:\"spacing\"}],body:[0,[[13,\"scaleFactor\",[1,[7,\"viewport\",\"y\"],\"/\",\"REFERENCE_HEIGHT\"]],[13,\"effectiveCellSize\",[1,\"cellSize\",\"*\",\"scaleFactor\"]],[13,\"mainGridSize\",[6,\"d.vec2f\",[[1,[7,\"viewport\",\"x\"],\"/\",\"effectiveCellSize\"],[1,[7,\"viewport\",\"y\"],\"/\",\"effectiveCellSize\"]]]],[13,\"gridCoords\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",[7,\"mainGridSize\",\"x\"]],[1,[7,\"uv\",\"y\"],\"*\",[7,\"mainGridSize\",\"y\"]]]]],[13,\"cellCoords\",[6,\"std.floor\",[\"gridCoords\"]]],[13,\"rawCellUV\",[6,\"std.fract\",[\"gridCoords\"]]],[13,\"centeredU\",[1,[7,\"rawCellUV\",\"x\"],\"-\",[5,\"0.5\"]]],[13,\"centeredV\",[1,[7,\"rawCellUV\",\"y\"],\"-\",[5,\"0.5\"]]],[13,\"cellUV\",[6,\"d.vec2f\",[[1,[1,\"centeredU\",\"/\",\"spacing\"],\"+\",[5,\"0.5\"]],[1,[1,\"centeredV\",\"/\",\"spacing\"],\"+\",[5,\"0.5\"]]]]],[13,\"half\",[1,\"spacing\",\"*\",[5,\"0.5\"]]],[13,\"isOutside\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[3,[1,[6,\"std.abs\",[\"centeredU\"]],\">\",\"half\"],\"||\",[1,[6,\"std.abs\",[\"centeredV\"]],\">\",\"half\"]]]]],[13,\"cellCenter\",[6,\"d.vec2f\",[[1,[1,[7,\"cellCoords\",\"x\"],\"+\",[5,\"0.5\"]],\"/\",[7,\"mainGridSize\",\"x\"]],[1,[1,[7,\"cellCoords\",\"y\"],\"+\",[5,\"0.5\"]],\"/\",[7,\"mainGridSize\",\"y\"]]]]],[10,[6,\"AsciiGrid\",[[104,{cellCenter:\"cellCenter\",cellUV:\"cellUV\",isOutside:\"isOutside\"}]]]]]]},externals:{REFERENCE_HEIGHT:()=>rP,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"std.select\":()=>N,\"d.f32\":()=>n,\"std.abs\":()=>re,AsciiGrid:()=>E0}})&&e.f)({})).$name(\"asciiGrid\"),Voe=w.fn([A,n,n,n,n,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=vt(Se(t,A(.299,.587,.114)),s),c=__tsover_sub(1,l),d=G(ie(__tsover_mul(c,r)),0,__tsover_sub(r,1)),u=__tsover_sub(d,__tsover_mul(o,ie(__tsover_div(d,o)))),p=ie(__tsover_div(d,o)),_=__tsover_mul(__tsover_div(1,o),a),f=__tsover_mul(u,_),h=__tsover_mul(p,_);return m(__tsover_add(f,__tsover_mul(i.x,_)),__tsover_add(h,__tsover_mul(i.y,_)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cellRgb\"},{type:\"i\",name:\"gamma\"},{type:\"i\",name:\"charCount\"},{type:\"i\",name:\"atlasSize\"},{type:\"i\",name:\"atlasScale\"},{type:\"i\",name:\"cellUV\"}],body:[0,[[13,\"rawBrightness\",[6,\"std.dot\",[\"cellRgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"adjustedBrightness\",[6,\"std.pow\",[\"rawBrightness\",\"gamma\"]]],[13,\"invertedBrightness\",[1,[5,\"1\"],\"-\",\"adjustedBrightness\"]],[13,\"scaledBrightness\",[1,\"invertedBrightness\",\"*\",\"charCount\"]],[13,\"charIndex\",[6,\"std.clamp\",[[6,\"std.floor\",[\"scaledBrightness\"]],[5,\"0\"],[1,\"charCount\",\"-\",[5,\"1\"]]]]],[13,\"atlasCol\",[1,\"charIndex\",\"-\",[1,\"atlasSize\",\"*\",[6,\"std.floor\",[[1,\"charIndex\",\"/\",\"atlasSize\"]]]]]],[13,\"atlasRow\",[6,\"std.floor\",[[1,\"charIndex\",\"/\",\"atlasSize\"]]]],[13,\"atlasCellSize\",[1,[1,[5,\"1\"],\"/\",\"atlasSize\"],\"*\",\"atlasScale\"]],[13,\"baseU\",[1,\"atlasCol\",\"*\",\"atlasCellSize\"]],[13,\"baseV\",[1,\"atlasRow\",\"*\",\"atlasCellSize\"]],[10,[6,\"d.vec2f\",[[1,\"baseU\",\"+\",[1,[7,\"cellUV\",\"x\"],\"*\",\"atlasCellSize\"]],[1,\"baseV\",\"+\",[1,[7,\"cellUV\",\"y\"],\"*\",\"atlasCellSize\"]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.pow\":()=>vt,\"std.clamp\":()=>G,\"std.floor\":()=>ie,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"asciiAtlasUV\"),Noe=w.fn([A,S,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Se(t,A(.299,.587,.114))<.1,l=s.w,c=l<o,d=N(n(1),l,a>.5),u=i||r>.5||c,p=N(d,n(0),u);return S(__tsover_mul(t.x,s.x),__tsover_mul(t.y,s.y),__tsover_mul(t.z,s.z),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"asciiRgb\"},{type:\"i\",name:\"cellColor\"},{type:\"i\",name:\"isOutside\"},{type:\"i\",name:\"alphaThreshold\"},{type:\"i\",name:\"preserveAlpha\"}],body:[0,[[13,\"charBrightness\",[6,\"std.dot\",[\"asciiRgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"isBackground\",[1,\"charBrightness\",\"<\",[5,\"0.1\"]]],[13,\"sourceAlpha\",[7,\"cellColor\",\"w\"]],[13,\"isBelowThreshold\",[1,\"sourceAlpha\",\"<\",\"alphaThreshold\"]],[13,\"outputAlpha\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"sourceAlpha\",[1,\"preserveAlpha\",\">\",[5,\"0.5\"]]]]],[13,\"hide\",[3,[3,\"isBackground\",\"||\",[1,\"isOutside\",\">\",[5,\"0.5\"]]],\"||\",\"isBelowThreshold\"]],[13,\"finalAlpha\",[6,\"std.select\",[\"outputAlpha\",[6,\"d.f32\",[[5,\"0\"]]],\"hide\"]]],[10,[6,\"d.vec4f\",[[1,[7,\"asciiRgb\",\"x\"],\"*\",[7,\"cellColor\",\"x\"]],[1,[7,\"asciiRgb\",\"y\"],\"*\",[7,\"cellColor\",\"y\"]],[1,[7,\"asciiRgb\",\"z\"],\"*\",[7,\"cellColor\",\"z\"]],\"finalAlpha\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.select\":()=>N,\"d.f32\":()=>n,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"asciiCompose\"),Xh=ko({TangentFrame:()=>U1,WardSpecularInput:()=>rA,bevelledFlatNormal:()=>H4,cosinePalette:()=>A_,fieldGradient:()=>tA,insideMask:()=>Nb,orthonormalTangentFrame:()=>$b,outsideShape:()=>K4,patternCoords:()=>j4,perspectiveViewRay:()=>Q4,volumetricNormal:()=>sA,wardAnisotropicSpecular:()=>Z4}),tA=w.fn([S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return m(__tsover_div(__tsover_sub(s.x,t.x),o),__tsover_div(__tsover_sub(r.x,t.x),o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"surf0\"},{type:\"i\",name:\"surfX\"},{type:\"i\",name:\"surfY\"},{type:\"i\",name:\"eps\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[7,\"surfX\",\"x\"],\"-\",[7,\"surf0\",\"x\"]],\"/\",\"eps\"],[1,[1,[7,\"surfY\",\"x\"],\"-\",[7,\"surf0\",\"x\"]],\"/\",\"eps\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),sA=w.fn([S,S,S,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return vs(A(G(__tsover_div(__tsover_sub(s.w,t.w),o),__tsover_mul(a,-1),a),G(__tsover_div(__tsover_sub(r.w,t.w),o),__tsover_mul(a,-1),a),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"surf0\"},{type:\"i\",name:\"surfX\"},{type:\"i\",name:\"surfY\"},{type:\"i\",name:\"eps\"},{type:\"i\",name:\"gradClamp\"}],body:[0,[[13,\"ddx\",[6,\"std.clamp\",[[1,[1,[7,\"surfX\",\"w\"],\"-\",[7,\"surf0\",\"w\"]],\"/\",\"eps\"],[1,\"gradClamp\",\"*\",[4,\"-\",[5,\"1\"]]],\"gradClamp\"]]],[13,\"ddy\",[6,\"std.clamp\",[[1,[1,[7,\"surfY\",\"w\"],\"-\",[7,\"surf0\",\"w\"]],\"/\",\"eps\"],[1,\"gradClamp\",\"*\",[4,\"-\",[5,\"1\"]]],\"gradClamp\"]]],[10,[6,\"std.normalize\",[[6,\"d.vec3f\",[\"ddx\",\"ddy\",[4,\"-\",[5,\"1\"]]]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.normalize\":()=>vs,\"d.vec3f\":()=>A}})&&e.f)({})),H4=w.fn([m,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=B(he(t),1e-4),o=ye(s,.9995);return A(__tsover_mul(__tsover_div(t.x,r),o),__tsover_mul(__tsover_div(t.y,r),o),__tsover_mul(Ue(__tsover_sub(1,__tsover_mul(o,o))),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"grad\"},{type:\"i\",name:\"sinTilt\"}],body:[0,[[13,\"glen\",[6,\"std.max\",[[6,\"std.length\",[\"grad\"]],[5,\"0.0001\"]]]],[13,\"sB\",[6,\"std.min\",[\"sinTilt\",[5,\"0.9995\"]]]],[10,[6,\"d.vec3f\",[[1,[1,[7,\"grad\",\"x\"],\"/\",\"glen\"],\"*\",\"sB\"],[1,[1,[7,\"grad\",\"y\"],\"/\",\"glen\"],\"*\",\"sB\"],[1,[6,\"std.sqrt\",[[1,[5,\"1\"],\"-\",[1,\"sB\",\"*\",\"sB\"]]]],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{\"std.max\":()=>B,\"std.length\":()=>he,\"std.min\":()=>ye,\"d.vec3f\":()=>A,\"std.sqrt\":()=>Ue}})&&e.f)({})),j4=w.fn([m,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(N(__tsover_sub(t.x,.5),s.y,r>.5),N(__tsover_sub(t.y,.5),s.z,r>.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfUV\"},{type:\"i\",name:\"surf0\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[10,[6,\"d.vec2f\",[[6,\"std.select\",[[1,[7,\"sdfUV\",\"x\"],\"-\",[5,\"0.5\"]],[7,\"surf0\",\"y\"],[1,\"volumetric\",\">\",[5,\"0.5\"]]]],[6,\"std.select\",[[1,[7,\"sdfUV\",\"y\"],\"-\",[5,\"0.5\"]],[7,\"surf0\",\"z\"],[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.select\":()=>N}})&&e.f)({})),K4=w.fn([n,n],ys)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t>__tsover_mul(s,2)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"pxH\"}],body:[0,[[10,[1,\"sdf\",\">\",[1,\"pxH\",\"*\",[5,\"2\"]]]]]]},externals:{}})&&e.f)({})),Nb=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=B(__tsover_mul(s,.03125),__tsover_mul(r,o));return G(__tsover_div(__tsover_mul(t,-1),a),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"sharpEdge\"},{type:\"i\",name:\"pxH\"},{type:\"i\",name:\"minPixels\"}],body:[0,[[13,\"w\",[6,\"std.max\",[[1,\"sharpEdge\",\"*\",[5,\"0.03125\"]],[1,\"pxH\",\"*\",\"minPixels\"]]]],[10,[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"w\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G}})&&e.f)({})),Q4=w.fn([m,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return vs(A(__tsover_mul(__tsover_mul(__tsover_sub(t.x,.5),s),r),__tsover_mul(__tsover_sub(t.y,.5),r),1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"fov\"}],body:[0,[[10,[6,\"std.normalize\",[[6,\"d.vec3f\",[[1,[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"],\"*\",\"fov\"],[1,[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]],\"*\",\"fov\"],[5,\"1\"]]]]]]]]},externals:{\"std.normalize\":()=>vs,\"d.vec3f\":()=>A}})&&e.f)({})),U1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({tangent:A,bitangent:A}),\"TangentFrame\"),$b=w.fn([A,A],U1)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=vs(s.sub(t.mul(Se(t,s))));return U1({tangent:r,bitangent:A(__tsover_sub(__tsover_mul(t.y,r.z),__tsover_mul(t.z,r.y)),__tsover_sub(__tsover_mul(t.z,r.x),__tsover_mul(t.x,r.z)),__tsover_sub(__tsover_mul(t.x,r.y),__tsover_mul(t.y,r.x)))})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"normal\"},{type:\"i\",name:\"dir\"}],body:[0,[[13,\"T3\",[6,\"std.normalize\",[[6,[7,\"dir\",\"sub\"],[[6,[7,\"normal\",\"mul\"],[[6,\"std.dot\",[\"normal\",\"dir\"]]]]]]]]],[13,\"B3\",[6,\"d.vec3f\",[[1,[1,[7,\"normal\",\"y\"],\"*\",[7,\"T3\",\"z\"]],\"-\",[1,[7,\"normal\",\"z\"],\"*\",[7,\"T3\",\"y\"]]],[1,[1,[7,\"normal\",\"z\"],\"*\",[7,\"T3\",\"x\"]],\"-\",[1,[7,\"normal\",\"x\"],\"*\",[7,\"T3\",\"z\"]]],[1,[1,[7,\"normal\",\"x\"],\"*\",[7,\"T3\",\"y\"]],\"-\",[1,[7,\"normal\",\"y\"],\"*\",[7,\"T3\",\"x\"]]]]]],[10,[6,\"TangentFrame\",[[104,{tangent:\"T3\",bitangent:\"B3\"}]]]]]]},externals:{\"std.normalize\":()=>vs,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,TangentFrame:()=>U1}})&&e.f)({})),rA=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({normal:A,tangent:A,light:A,view:A,alphaAlong:n,alphaAcross:n}),\"WardSpecularInput\"),Z4=w.fn([rA],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=$b(t.normal,t.tangent),r=vs(t.light.sub(t.view)),o=Se(r,s.tangent),a=Se(r,s.bitangent),i=G(re(Se(r,t.normal)),1e-4,1),l=B(t.alphaAlong,1e-4),c=B(t.alphaAcross,1e-4),d=__tsover_div(__tsover_add(__tsover_mul(__tsover_div(o,l),__tsover_div(o,l)),__tsover_mul(__tsover_div(a,c),__tsover_div(a,c))),__tsover_mul(__tsover_add(i,1),.5));return Tt(__tsover_mul(d,-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"frame\",[6,\"orthonormalTangentFrame\",[[7,\"p\",\"normal\"],[7,\"p\",\"tangent\"]]]],[13,\"H\",[6,\"std.normalize\",[[6,[7,[7,\"p\",\"light\"],\"sub\"],[[7,\"p\",\"view\"]]]]]],[13,\"hT\",[6,\"std.dot\",[\"H\",[7,\"frame\",\"tangent\"]]]],[13,\"hB\",[6,\"std.dot\",[\"H\",[7,\"frame\",\"bitangent\"]]]],[13,\"hN\",[6,\"std.clamp\",[[6,\"std.abs\",[[6,\"std.dot\",[\"H\",[7,\"p\",\"normal\"]]]]],[5,\"0.0001\"],[5,\"1\"]]]],[13,\"aT\",[6,\"std.max\",[[7,\"p\",\"alphaAlong\"],[5,\"0.0001\"]]]],[13,\"aB\",[6,\"std.max\",[[7,\"p\",\"alphaAcross\"],[5,\"0.0001\"]]]],[13,\"expo\",[1,[1,[1,[1,\"hT\",\"/\",\"aT\"],\"*\",[1,\"hT\",\"/\",\"aT\"]],\"+\",[1,[1,\"hB\",\"/\",\"aB\"],\"*\",[1,\"hB\",\"/\",\"aB\"]]],\"/\",[1,[1,\"hN\",\"+\",[5,\"1\"]],\"*\",[5,\"0.5\"]]]],[10,[6,\"std.exp\",[[1,\"expo\",\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{orthonormalTangentFrame:()=>$b,\"std.normalize\":()=>vs,\"std.dot\":()=>Se,\"std.clamp\":()=>G,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.exp\":()=>Tt}})&&e.f)({})),A_=w.fn([n,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return A(__tsover_add(__tsover_mul(pe(__tsover_mul(__tsover_add(t,s),Po)),.5),.5),__tsover_add(__tsover_mul(pe(__tsover_mul(__tsover_add(t,r),Po)),.5),.5),__tsover_add(__tsover_mul(pe(__tsover_mul(__tsover_add(t,o),Po)),.5),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"phaseR\"},{type:\"i\",name:\"phaseG\"},{type:\"i\",name:\"phaseB\"}],body:[0,[[10,[6,\"d.vec3f\",[[1,[1,[6,\"std.cos\",[[1,[1,\"t\",\"+\",\"phaseR\"],\"*\",\"TWO_PI\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[6,\"std.cos\",[[1,[1,\"t\",\"+\",\"phaseG\"],\"*\",\"TWO_PI\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[6,\"std.cos\",[[1,[1,\"t\",\"+\",\"phaseB\"],\"*\",\"TWO_PI\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.cos\":()=>pe,TWO_PI:()=>Po}})&&e.f)({})),$oe=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return be(0,1,Nb(t,B(__tsover_mul(s,.5),.001),r,1.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"edgeSoftness\"},{type:\"i\",name:\"pxH\"}],body:[0,[[13,\"sharpEdge\",[6,\"std.max\",[[1,\"edgeSoftness\",\"*\",[5,\"0.5\"]],[5,\"0.001\"]]]],[10,[6,\"std.smoothstep\",[[5,\"0\"],[5,\"1\"],[6,\"insideMask\",[\"sdf\",\"sharpEdge\",\"pxH\",[5,\"1.5\"]]]]]]]]},externals:{\"std.max\":()=>B,\"std.smoothstep\":()=>be,insideMask:()=>Nb}})&&e.f)({})),k0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({L:A,H:A}),\"KeyLight\"),Xoe=w.fn([n,n,n],k0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=vs(A(t,s,r));return k0({L:o,H:vs(o.add(A(0,0,-1)))})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"lx\"},{type:\"i\",name:\"ly\"},{type:\"i\",name:\"lz\"}],body:[0,[[13,\"L\",[6,\"std.normalize\",[[6,\"d.vec3f\",[\"lx\",\"ly\",\"lz\"]]]]],[13,\"H\",[6,\"std.normalize\",[[6,[7,\"L\",\"add\"],[[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[4,\"-\",[5,\"1\"]]]]]]]]],[10,[6,\"KeyLight\",[[104,{L:\"L\",H:\"H\"}]]]]]]},externals:{\"std.normalize\":()=>vs,\"d.vec3f\":()=>A,KeyLight:()=>k0}})&&e.f)({})),Hoe=w.fn([A],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_sub(1,G(__tsover_mul(t.z,-1),0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"n\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.clamp\",[[1,[7,\"n\",\"z\"],\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({})),joe=w.fn([n,n,S],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_mul(vt(t,K(r.x,r.y,s)),K(r.z,r.w,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ndh\"},{type:\"i\",name:\"sharp\"},{type:\"i\",name:\"cfg\"}],body:[0,[[10,[1,[6,\"std.pow\",[\"ndh\",[6,\"std.mix\",[[7,\"cfg\",\"x\"],[7,\"cfg\",\"y\"],\"sharp\"]]]],\"*\",[6,\"std.mix\",[[7,\"cfg\",\"z\"],[7,\"cfg\",\"w\"],\"sharp\"]]]]]]},externals:{\"std.pow\":()=>vt,\"std.mix\":()=>K}})&&e.f)({})),Koe=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=n(4),o=Si(t);return m(G(__tsover_mul(o.y,s),__tsover_mul(r,-1),r),G(__tsover_mul(o.z,s),__tsover_mul(r,-1),r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"gain\"}],body:[0,[[13,\"gMax\",[6,\"d.f32\",[[5,\"4\"]]]],[13,\"nd\",[6,\"perlin12d\",[\"q\"]]],[10,[6,\"d.vec2f\",[[6,\"std.clamp\",[[1,[7,\"nd\",\"y\"],\"*\",\"gain\"],[1,\"gMax\",\"*\",[4,\"-\",[5,\"1\"]]],\"gMax\"]],[6,\"std.clamp\",[[1,[7,\"nd\",\"z\"],\"*\",\"gain\"],[1,\"gMax\",\"*\",[4,\"-\",[5,\"1\"]]],\"gMax\"]]]]]]]},externals:{\"d.f32\":()=>n,perlin12d:()=>Si,\"d.vec2f\":()=>m,\"std.clamp\":()=>G}})&&e.f)({})),Qoe=w.fn([A,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return vs(A(__tsover_add(t.x,s),__tsover_add(t.y,r),t.z))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"n\"},{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"}],body:[0,[[10,[6,\"std.normalize\",[[6,\"d.vec3f\",[[1,[7,\"n\",\"x\"],\"+\",\"dx\"],[1,[7,\"n\",\"y\"],\"+\",\"dy\"],[7,\"n\",\"z\"]]]]]]]]},externals:{\"std.normalize\":()=>vs,\"d.vec3f\":()=>A}})&&e.f)({})),Zoe=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=G(__tsover_mul(__tsover_mul(t,-1),2),0,1.2);return N(__tsover_mul(G(__tsover_mul(t,-1),0,.5),2),r,s>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"thickVol\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",[5,\"2\"]],[5,\"0\"],[5,\"1.2\"]]]],[13,\"thickFlat\",[1,[6,\"std.clamp\",[[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"],[5,\"0.5\"]]],\"*\",[5,\"2\"]]],[10,[6,\"std.select\",[\"thickFlat\",\"thickVol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.select\":()=>N}})&&e.f)({})),Joe=w.fn([n,A],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return A(Tt(__tsover_mul(__tsover_mul(t,s.x),-1)),Tt(__tsover_mul(__tsover_mul(t,s.y),-1)),Tt(__tsover_mul(__tsover_mul(t,s.z),-1)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"absorb\"}],body:[0,[[10,[6,\"d.vec3f\",[[6,\"std.exp\",[[1,[1,\"thickness\",\"*\",[7,\"absorb\",\"x\"]],\"*\",[4,\"-\",[5,\"1\"]]]]],[6,\"std.exp\",[[1,[1,\"thickness\",\"*\",[7,\"absorb\",\"y\"]],\"*\",[4,\"-\",[5,\"1\"]]]]],[6,\"std.exp\",[[1,[1,\"thickness\",\"*\",[7,\"absorb\",\"z\"]],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.exp\":()=>Tt}})&&e.f)({})),eae=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(__tsover_mul(ae(__tsover_add(__tsover_add(__tsover_mul(t.x,1.7),__tsover_mul(s,.7)),1.7)),pe(__tsover_add(__tsover_add(__tsover_mul(t.y,1.3),__tsover_mul(s,.5)),2.3))),__tsover_mul(pe(__tsover_add(__tsover_add(__tsover_mul(t.x,1.4),__tsover_mul(s,.9)),8.7)),ae(__tsover_add(__tsover_add(__tsover_mul(t.y,1.6),__tsover_mul(s,.6)),5.1))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"warpInput\"},{type:\"i\",name:\"flowT\"}],body:[0,[[13,\"wx\",[1,[6,\"std.sin\",[[1,[1,[1,[7,\"warpInput\",\"x\"],\"*\",[5,\"1.7\"]],\"+\",[1,\"flowT\",\"*\",[5,\"0.7\"]]],\"+\",[5,\"1.7\"]]]],\"*\",[6,\"std.cos\",[[1,[1,[1,[7,\"warpInput\",\"y\"],\"*\",[5,\"1.3\"]],\"+\",[1,\"flowT\",\"*\",[5,\"0.5\"]]],\"+\",[5,\"2.3\"]]]]]],[13,\"wy\",[1,[6,\"std.cos\",[[1,[1,[1,[7,\"warpInput\",\"x\"],\"*\",[5,\"1.4\"]],\"+\",[1,\"flowT\",\"*\",[5,\"0.9\"]]],\"+\",[5,\"8.7\"]]]],\"*\",[6,\"std.sin\",[[1,[1,[1,[7,\"warpInput\",\"y\"],\"*\",[5,\"1.6\"]],\"+\",[1,\"flowT\",\"*\",[5,\"0.6\"]]],\"+\",[5,\"5.1\"]]]]]],[10,[6,\"d.vec2f\",[\"wx\",\"wy\"]]]]]},externals:{\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"d.vec2f\":()=>m}})&&e.f)({})),tae=w.fn([n,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_sub(__tsover_mul(t,r),__tsover_mul(s,o)),u=__tsover_add(__tsover_mul(t,o),__tsover_mul(s,r)),p=m(d,u),_=be(-.7,.85,u),f=__tsover_mul(__tsover_mul(_,_),c),h=__tsover_mul(a,1);f=__tsover_add(f,__tsover_mul(__tsover_mul(be(h,__tsover_mul(h,.2),he(p.sub(m(__tsover_add(0,i),.55)))),1.7),l));let g=__tsover_mul(a,1.15);f=__tsover_add(f,__tsover_mul(__tsover_mul(be(g,__tsover_mul(g,.2),he(p.sub(m(__tsover_add(-.55,i),.18)))),1.05),l));let y=__tsover_mul(a,1);f=__tsover_add(f,__tsover_mul(__tsover_mul(be(y,__tsover_mul(y,.2),he(p.sub(m(__tsover_add(.55,i),.1)))),1),l));let b=__tsover_mul(a,1.3);f=__tsover_add(f,__tsover_mul(__tsover_mul(be(b,__tsover_mul(b,.2),he(p.sub(m(__tsover_add(.2,i),-.5)))),.7),l));let x=__tsover_mul(a,1.1);return f=__tsover_add(f,__tsover_mul(__tsover_mul(be(x,__tsover_mul(x,.2),he(p.sub(m(__tsover_add(-.35,i),-.25)))),.85),l)),f}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rx\"},{type:\"i\",name:\"ry\"},{type:\"i\",name:\"envC\"},{type:\"i\",name:\"envS\"},{type:\"i\",name:\"keyRad\"},{type:\"i\",name:\"drift\"},{type:\"i\",name:\"envStr\"},{type:\"i\",name:\"skyGain\"}],body:[0,[[13,\"ex\",[1,[1,\"rx\",\"*\",\"envC\"],\"-\",[1,\"ry\",\"*\",\"envS\"]]],[13,\"ey\",[1,[1,\"rx\",\"*\",\"envS\"],\"+\",[1,\"ry\",\"*\",\"envC\"]]],[13,\"p\",[6,\"d.vec2f\",[\"ex\",\"ey\"]]],[13,\"sky\",[6,\"std.smoothstep\",[[4,\"-\",[5,\"0.7\"]],[5,\"0.85\"],\"ey\"]]],[12,\"lum\",[1,[1,\"sky\",\"*\",\"sky\"],\"*\",\"skyGain\"]],[13,\"r0\",[1,\"keyRad\",\"*\",[5,\"1\"]]],[2,\"lum\",\"=\",[1,\"lum\",\"+\",[1,[1,[6,\"std.smoothstep\",[\"r0\",[1,\"r0\",\"*\",[5,\"0.2\"]],[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[1,[5,\"0\"],\"+\",\"drift\"],[5,\"0.55\"]]]]]]]]],\"*\",[5,\"1.7\"]],\"*\",\"envStr\"]]],[13,\"r1\",[1,\"keyRad\",\"*\",[5,\"1.15\"]]],[2,\"lum\",\"=\",[1,\"lum\",\"+\",[1,[1,[6,\"std.smoothstep\",[\"r1\",[1,\"r1\",\"*\",[5,\"0.2\"]],[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[1,[4,\"-\",[5,\"0.55\"]],\"+\",\"drift\"],[5,\"0.18\"]]]]]]]]],\"*\",[5,\"1.05\"]],\"*\",\"envStr\"]]],[13,\"r2\",[1,\"keyRad\",\"*\",[5,\"1\"]]],[2,\"lum\",\"=\",[1,\"lum\",\"+\",[1,[1,[6,\"std.smoothstep\",[\"r2\",[1,\"r2\",\"*\",[5,\"0.2\"]],[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[1,[5,\"0.55\"],\"+\",\"drift\"],[5,\"0.1\"]]]]]]]]],\"*\",[5,\"1\"]],\"*\",\"envStr\"]]],[13,\"r3\",[1,\"keyRad\",\"*\",[5,\"1.3\"]]],[2,\"lum\",\"=\",[1,\"lum\",\"+\",[1,[1,[6,\"std.smoothstep\",[\"r3\",[1,\"r3\",\"*\",[5,\"0.2\"]],[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[1,[5,\"0.2\"],\"+\",\"drift\"],[4,\"-\",[5,\"0.5\"]]]]]]]]]],\"*\",[5,\"0.7\"]],\"*\",\"envStr\"]]],[13,\"r4\",[1,\"keyRad\",\"*\",[5,\"1.1\"]]],[2,\"lum\",\"=\",[1,\"lum\",\"+\",[1,[1,[6,\"std.smoothstep\",[\"r4\",[1,\"r4\",\"*\",[5,\"0.2\"]],[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[6,\"d.vec2f\",[[1,[4,\"-\",[5,\"0.35\"]],\"+\",\"drift\"],[4,\"-\",[5,\"0.25\"]]]]]]]]]],\"*\",[5,\"0.85\"]],\"*\",\"envStr\"]]],[10,\"lum\"]]]},externals:{\"d.vec2f\":()=>m,\"std.smoothstep\":()=>be,\"std.length\":()=>he}})&&e.f)({})),Xb=new Map,Hb=new Set;function oA(e,t,s){if(!e||!(t>0)||!(s>0))return;let r=Xb.get(e);if(!(r&&r.width===t&&r.height===s)){Xb.set(e,{width:t,height:s});for(let o of Hb)try{o()}catch{}}}function J4(e){return Xb.get(e)??null}function eB(e){return Hb.add(e),()=>{Hb.delete(e)}}function sae(e,t){return Math.floor(Math.log2(Math.max(e,t)))+1}function gc(e,t){let{createMediaTexture:s,registerMediaTexture:r,onCleanup:o}=e,a=t.mipmaps??!0,i=(u,p)=>s({width:u,height:p,label:t.label,...a?{mipLevelCount:sae(u,p)}:{},...t.format?{format:t.format}:{}}),l=i(Math.max(1,t.initial?.width??1),Math.max(1,t.initial?.height??1)),c=!1,d=r(()=>l.texture);return o(()=>{c=!0,l.destroy()}),{kit:d,ensureSize(u,p){let _=Math.max(1,u),f=Math.max(1,p);if(c||l.width===_&&l.height===f)return;let h=i(_,f),g=l;l=h,g.destroy()},write(u){c||(l.write(u),a&&l.generateMipmaps())},unwrap:()=>l.unwrap(),get width(){return l.width},get height(){return l.height},get disposed(){return c}}}function tB(e,t){let{getCpuValue:s,onBeforeRender:r,onCleanup:o}=e,a=\"\",i=!1,l=!1,c=null,d=p=>{if(!p||p.trim()===\"\"||i||l)return;i=!0;let _={isDisposed:()=>l,commit:()=>{a=p}};(async()=>{try{await t.load(p,_)}catch(f){t.onError?.(f,p)}finally{i=!1,c=a===p?null:p}})()},u=()=>s(t.prop)??\"\";return setTimeout(()=>{let p=u();p&&p.trim()!==\"\"&&d(p)},0),r(()=>{let p=u();p===a||i||p!==c&&(c=null,d(p))}),o(()=>{l=!0,a=\"\"}),{currentUrl:()=>a,isLoading:()=>i,request:d}}var lu=2048;function rae(e){return/\\.svg($|\\?|#)/i.test(e)}function oae(e){return new Promise((t,s)=>{let r=new Image;r.crossOrigin=\"anonymous\",r.onload=()=>{let o=r.naturalWidth||lu,a=r.naturalHeight||lu,i=o/a,l=i>=1?lu:Math.round(lu*i),c=i>=1?Math.round(lu/i):lu,d=document.createElement(\"canvas\");d.width=l,d.height=c;let u=d.getContext(\"2d\");if(!u){s(new Error(\"Could not get 2D context\"));return}u.drawImage(r,0,0,l,c),t({source:d,width:l,height:c,naturalWidth:o,naturalHeight:a,close:()=>{}})},r.onerror=()=>s(new Error(`SVG image failed to load: ${e}`)),r.src=e})}async function aae(e){if(rae(e))return oae(e);let t=await fetch(e,{mode:\"cors\"});if(!t.ok)throw new Error(`HTTP ${t.status}`);let s=await t.blob(),r=await createImageBitmap(s),o=!1;return{source:r,width:r.width,height:r.height,naturalWidth:r.width,naturalHeight:r.height,close:()=>{o||(o=!0,r.close())}}}function oP(){let e=document.createElement(\"video\");return e.playsInline=!0,e.muted=!0,e}function aP(e,t){return new Promise((s,r)=>{e.onloadedmetadata=()=>s(),e.onerror=()=>r(new Error(\"Failed to load video\")),t>0&&setTimeout(()=>r(new Error(\"Video load timeout\")),t)})}function sB(e,t){let{getCpuValue:s,onBeforeRender:r,onCleanup:o}=e,a=t.metadataTimeoutMs??0,i=t.autoplayRequired??!1,l=null,c=null,d=!1,u=()=>{if(!t.loop)return!1;let f=s(t.loop.prop);return f===!0||f===void 0||typeof f==\"number\"&&f>0},p=async(f,h)=>{try{await f.play()}catch(y){if(t.onError?.(y,{url:h,stage:\"autoplay\"}),i)throw y}if(d)return;l&&l!==f&&(l.pause(),l.src=\"\"),l=f;let g=t.naturalSizeKey??h;g&&oA(g,f.videoWidth,f.videoHeight)};if(t.source.kind===\"url\"){let f=t.source.prop;tB(e,{prop:f,async load(h,g){let y=oP();y.src=h,y.crossOrigin=\"anonymous\",y.loop=u();try{await aP(y,a)}catch(b){throw y.src=\"\",b}if(g.isDisposed()){y.src=\"\";return}g.commit(),await p(y,h)},onError(h,g){t.onError?.(h,{url:g,stage:\"acquire\"})}}),t.loop&&r(()=>{if(!l)return;let h=u();l.loop!==h&&(l.loop=h)}),o(()=>{d=!0,l&&(l.pause(),l.src=\"\",l=null)})}else{let f=t.source.constraints??{video:!0,audio:!1},h=!1,g=async()=>{if(!(h||d))try{if(c=await navigator.mediaDevices.getUserMedia(f),d){c.getTracks().forEach(b=>b.stop()),c=null;return}let y=oP();if(y.srcObject=c,await aP(y,a),d||(await p(y,null),d))return;h=!0,di(`[media] Video source started: ${y.videoWidth}x${y.videoHeight}`)}catch(y){t.onError?.(y,{url:null,stage:\"acquire\"})}};setTimeout(()=>{g()},0),r(()=>{}),o(()=>{d=!0,c&&(c.getTracks().forEach(y=>y.stop()),c=null),l&&(l.srcObject=null,l=null),h=!1})}return{getSource:()=>!l||d||l.readyState<2||l.videoWidth===0?null:l,element:()=>l}}function w_(e,t={}){let{canvas:s,dimensions:r,onCleanup:o}=e,a=t.maxAxis??4096,i=t.supersample??1,l=null,c=null,d=!1,u=()=>d?null:(l||(l=document.createElement(\"canvas\"),l.width=2,l.height=2,c=l.getContext(\"2d\",t.contextAttributes)),c);return o(()=>{d=!0,l&&(l.width=0,l.height=0),l=null,c=null}),{context:u,canvas:()=>l,devicePixelRatio(){let p=r.width||1;return Math.max(.1,(s.width||p)/p)},rasterSize(p,_){let f=r.width||1,h=Math.max(.1,(s.width||f)/f)*i;return h=Math.min(h,a/p,a/_),{scale:h,texW:Math.max(2,Math.round(p*h)),texH:Math.max(2,Math.round(_*h))}},resize(p,_){let f=u();return!f||!l?null:(l.width=p,l.height=_,f.clearRect(0,0,p,_),f)},get disposed(){return d}}}function T_(e,t){let s=t.minIntervalMs??16,r=t.initialKey??\"\",o=0;e.onBeforeRender(()=>{let a=Date.now();if(a-o<s)return;o=a;let i=t.key();i!==r&&(r=i,t.run())})}function S_(e){let t=\"\";return{ensure(){let s=e.key();s!==t&&(t=s,e.load().then(()=>e.onLoaded()).catch(()=>{}))}}}function nae(e,t=400,s=!1,r){let o=`https://fonts.googleapis.com/css2?family=${e.trim().replace(/ /g,\"+\")}:ital,wght@${s?1:0},${t}&display=swap`;return r&&(o+=`&text=${encodeURIComponent(r)}`),o}var nP=new Set,B0=new Map;function Hh(e,t=400,s=!1){if(typeof document>\"u\"||!e)return Promise.resolve();let r=`${e}|${t}|${s}`;if(nP.has(r))return Promise.resolve();let o=B0.get(r);if(o)return o;let a=iae(e,t,s).catch(()=>{if(t!==400||s)return Hh(e,400,!1)}).then(()=>{nP.add(r),B0.delete(r)});return B0.set(r,a),a}function iae(e,t,s){let r=nae(e,t,s);return new Promise((o,a)=>{if(document.querySelector(`link[href=\"${r}\"]`)){o();return}let i=document.createElement(\"link\");i.rel=\"stylesheet\",i.href=r,i.onload=()=>o(),i.onerror=()=>{i.remove(),a(new Error(`Failed to load font stylesheet: ${e}`))},document.head.appendChild(i)}).then(async()=>{if(document.fonts?.load){let o=`${s?\"italic \":\"\"}${t} 12px \"${e}\"`;try{if(!(await document.fonts.load(o)).length)throw new Error(`No font faces loaded for ${o}`)}catch(a){if(!(await document.fonts.load(`12px \"${e}\"`)).length)throw a}await new Promise(a=>setTimeout(a,300))}else await new Promise(o=>setTimeout(o,1e3))})}function rB(e,t=400,s=!1){if(typeof document>\"u\"||!document.fonts?.check)return!1;try{return document.fonts.check(`${s?\"italic \":\"\"}${t} 12px \"${e}\"`)}catch{return!1}}var Qm=fo;function ll(e,t,s){return`{ let av = clamp(${s}, 0.0, 1.0); ${e} = vec4f((${t}) * av + ${e}.rgb * (1.0 - av), av + ${e}.a * (1.0 - av)); }`}function aA(e,t){let{q:s,sd:r}=e,{pp:o,half:a,radius:i}=t;return i===void 0?[`let ${s} = abs(${o}) - ${a};`,`let ${r} = length(max(${s}, vec2f(0.0))) + min(max(${s}.x, ${s}.y), ${Qm(0)});`]:[`let ${s} = abs(${o}) - ${a} + ${i};`,`let ${r} = length(max(${s}, vec2f(0.0))) + min(max(${s}.x, ${s}.y), ${Qm(0)}) - ${i};`]}function oB(e,t){let{bl:s,inCorner:r}=e,{blInit:o,px:a,py:i,hx:l,hy:c,stroke:d}=t;return[`let ${s} = ${o};`,`let ${r} = abs(${a}) > ${l} - ${s} && abs(${i}) > ${c} - ${s};`,`${d} = ${d} * select(${Qm(0)}, ${Qm(1)}, ${r});`]}function aB(e){return`vec4f(${e}.rgb / max(${e}.a, ${Qm(1e-4)}), ${e}.a)`}var lae=\"0123456789x%\",nB=16,Ev=64,kv=96,z0=nB*Ev,R0=kv,cae=3,iP=4,lP=\"vec3f(0.299, 0.587, 0.114)\",dae=(e,t)=>typeof e==\"number\"?e:t;function uae(e,t={}){let{getCpuValue:s}=e,r=gc(e,{label:t.label??\"GlyphStrip:atlas\",initial:{width:z0,height:R0}}),o=w_(e,{contextAttributes:{willReadFrequently:!0}}),a=()=>({family:String(s(t.familyProp??\"fontFamily\")??\"Inter\"),weight:dae(s(t.weightProp??\"fontWeight\"),500)}),i=(u,p)=>{if(r.disposed)return;let _=o.canvas()?o.context():o.resize(z0,R0);if(!_)return;_.clearRect(0,0,z0,R0),_.fillStyle=\"#ffffff\",_.font=`${p} ${Math.round(kv*.62)}px \"${u}\", ui-monospace, monospace`,_.textAlign=\"center\",_.textBaseline=\"middle\";let f=t.glyphs??lae;for(let h=0;h<f.length;h++)_.fillText(f[h],h*Ev+Ev/2,kv/2+1);r.write(o.canvas())},l=a();i(l.family,l.weight);let c=()=>{let u=a();i(u.family,u.weight)},d=S_({key:()=>{let u=a();return`${u.family}|${u.weight}`},load:()=>{let u=a();return Hh(u.family,u.weight)},onLoaded:c});return setTimeout(()=>d.ensure(),0),T_(e,{initialKey:`${l.family}|${l.weight}`,minIntervalMs:100,key:()=>{let u=a();return`${u.family}|${u.weight}`},run:()=>{c(),d.ensure()}}),r.kit}function pae(e,t,s){let{p:r,L:o,modeStr:a}=e;return a===\"alpha\"?`${t}.a > ${r}_thr`:a===\"bright\"?`dot(${s}.rgb, ${lP}) > ${r}_thr && ${t}.a > ${o(.02)}`:a===\"dark\"?`dot(${s}.rgb, ${lP}) < ${r}_thr && ${t}.a > ${o(.5)}`:a===\"red\"?`${s}.r > ${r}_thr && ${s}.r >= ${s}.g && ${s}.r >= ${s}.b && ${t}.a > ${o(.02)}`:a===\"green\"?`${s}.g > ${r}_thr && ${s}.g >= ${s}.r && ${s}.g >= ${s}.b && ${t}.a > ${o(.02)}`:`${s}.b > ${r}_thr && ${s}.b >= ${s}.r && ${s}.b >= ${s}.g && ${t}.a > ${o(.02)}`}function iB(e,t,s,r,o,a,i){let{p:l,L:c,s:d,sampleChild:u,UN:p,modeStr:_}=e;d.push(`var ${t}_cnt = ${c(0)};`),i&&d.push(`var ${t}_minX = ${c(1e9)}; var ${t}_maxX = ${c(-1e9)}; var ${t}_minY = ${c(1e9)}; var ${t}_maxY = ${c(-1e9)};`);let f=1/a,h=0;for(let g=0;g<a;g++)for(let y=0;y<a;y++){let b=`${t}_px${h}x`,x=`${t}_px${h}y`;d.push(`let ${b} = ${s} + ${o} * ${c((y+.5)*f)};`),d.push(`let ${x} = ${r} + ${o} * ${c((g+.5)*f)};`),d.push(`let ${t}_s${h} = ${u(`vec2f(${b}, ${x}) / ${l}_res`)};`),_!==\"alpha\"&&d.push(`let ${t}_u${h} = ${p}(${t}_s${h});`),d.push(`let ${t}_c${h} = ${pae(e,`${t}_s${h}`,`${t}_u${h}`)};`),d.push(`${t}_cnt = ${t}_cnt + select(${c(0)}, ${c(1)}, ${t}_c${h});`),i&&(d.push(`${t}_minX = select(${t}_minX, min(${t}_minX, ${b}), ${t}_c${h});`),d.push(`${t}_maxX = select(${t}_maxX, max(${t}_maxX, ${b}), ${t}_c${h});`),d.push(`${t}_minY = select(${t}_minY, min(${t}_minY, ${x}), ${t}_c${h});`),d.push(`${t}_maxY = select(${t}_maxY, max(${t}_maxY, ${x}), ${t}_c${h});`)),h++}d.push(`let ${t}_coverage = ${t}_cnt / ${c(a*a)};`)}function mae(e,t,s){let{p:r,L:o,s:a}=e;a.push(`var ${r}_origin = floor(${r}_fragPx / ${r}_cellPx) * ${r}_cellPx;`),a.push(`var ${r}_size = ${r}_cellPx;`);for(let i=0;i<s;i++){let l=`${r}_w${i}`;t===\"mosaic\"?(a.push(`let ${l}_cellIx = ${r}_origin / ${r}_size;`),a.push(`let ${l}_doSplit = fract(sin(dot(${l}_cellIx, vec2f(${o(127.1)}, ${o(311.7)})) + ${o(i*53.13)}) * ${o(43758.5453)}) > ${o(.5)};`)):(iB(e,l,`${r}_origin.x`,`${r}_origin.y`,`${r}_size`,cae,!1),a.push(`let ${l}_doSplit = ${l}_coverage > ${o(.02)} && ${l}_coverage < ${o(.98)};`)),a.push(`let ${l}_half = ${r}_size * ${o(.5)};`),a.push(`let ${l}_qx = step(${r}_origin.x + ${l}_half, ${r}_fragPx.x);`),a.push(`let ${l}_qy = step(${r}_origin.y + ${l}_half, ${r}_fragPx.y);`),a.push(`let ${l}_childOrigin = vec2f(${r}_origin.x + ${l}_qx * ${l}_half, ${r}_origin.y + ${l}_qy * ${l}_half);`),a.push(`${r}_origin = select(${r}_origin, ${l}_childOrigin, ${l}_doSplit);`),a.push(`${r}_size = select(${r}_size, ${l}_half, ${l}_doSplit);`)}}function _ae(e){let{p:t,L:s,s:r}=e;iB(e,`${t}_leaf`,`${t}_origin.x`,`${t}_origin.y`,`${t}_size`,iP,!0),r.push(`let ${t}_halfStep = ${t}_size / ${s(iP)} * ${s(.5)};`),r.push(`let ${t}_innerMinX = ${t}_origin.x + ${t}_boxInset;`),r.push(`let ${t}_innerMaxX = max(${t}_origin.x + ${t}_size - ${t}_boxInset, ${t}_innerMinX);`),r.push(`let ${t}_innerMinY = ${t}_origin.y + ${t}_boxInset;`),r.push(`let ${t}_innerMaxY = max(${t}_origin.y + ${t}_size - ${t}_boxInset, ${t}_innerMinY);`),r.push(`let ${t}_bMinX = clamp(${t}_leaf_minX - ${t}_halfStep, ${t}_innerMinX, ${t}_innerMaxX);`),r.push(`let ${t}_bMaxX = clamp(${t}_leaf_maxX + ${t}_halfStep, ${t}_innerMinX, ${t}_innerMaxX);`),r.push(`let ${t}_bMinY = clamp(${t}_leaf_minY - ${t}_halfStep, ${t}_innerMinY, ${t}_innerMaxY);`),r.push(`let ${t}_bMaxY = clamp(${t}_leaf_maxY + ${t}_halfStep, ${t}_innerMinY, ${t}_innerMaxY);`),r.push(`let ${t}_boxW = ${t}_bMaxX - ${t}_bMinX;`),r.push(`let ${t}_boxH = ${t}_bMaxY - ${t}_bMinY;`),r.push(`let ${t}_detected = ${t}_leaf_coverage > ${s(.02)} && ${t}_boxW > ${t}_minSize && ${t}_boxH > ${t}_minSize;`),r.push(`let ${t}_detF = select(${s(0)}, ${s(1)}, ${t}_detected);`)}function fae(e,t){let{p:s,U:r,L:o,s:a}=e;a.push(`let ${s}_center = vec2f((${s}_bMinX + ${s}_bMaxX) * ${o(.5)}, (${s}_bMinY + ${s}_bMaxY) * ${o(.5)});`),a.push(`let ${s}_halfB = vec2f(${s}_boxW * ${o(.5)}, ${s}_boxH * ${o(.5)});`);let i=t?o(0):`${s}_cornerRadiusPx`;a.push(`let ${s}_cR = max(min(min(${i}, ${s}_halfB.x), ${s}_halfB.y), ${o(0)});`),a.push(`let ${s}_pp = ${s}_fragPx - ${s}_center;`),a.push(...aA({q:`${s}_q`,sd:`${s}_sd`},{pp:`${s}_pp`,half:`${s}_halfB`,radius:`${s}_cR`})),a.push(`let ${s}_fillA = (${o(1)} - smoothstep(${o(0)}, ${s}_aa, ${s}_sd)) * ${r(\"fillColor\")}.a * ${s}_detF;`),a.push(`var ${s}_strokeA = ${o(1)} - smoothstep(${o(0)}, ${s}_aa, abs(${s}_sd) - ${s}_lineHalf);`),t&&a.push(...oB({bl:`${s}_bl`,inCorner:`${s}_inCorner`},{blInit:`min(${s}_halfB.x, ${s}_halfB.y) * ${o(.35)}`,px:`${s}_pp.x`,py:`${s}_pp.y`,hx:`${s}_halfB.x`,hy:`${s}_halfB.y`,stroke:`${s}_strokeA`})),a.push(`${s}_strokeA = ${s}_strokeA * ${r(\"strokeColor\")}.a * ${s}_detF;`),a.push(ll(`${s}_dst`,`${r(\"fillColor\")}.rgb`,`${s}_fillA`)),a.push(ll(`${s}_dst`,`${r(\"strokeColor\")}.rgb`,`${s}_strokeA`))}var D0=(e,t)=>`(${e(1)} + step(${e(10)}, ${t}) + step(${e(100)}, ${t}) + step(${e(1e3)}, ${t}) + step(${e(1e4)}, ${t}))`,L0=(e,t,s)=>{let r=`floor(${t} / pow(${e(10)}, ${s}))`;return`(${r} - ${e(10)} * floor((${r}) / ${e(10)}))`};function vae(e,t){let{p:s,U:r,L:o,s:a}=e,{labelStr:i,labelPos:l,atlasKey:c}=t;a.push(`let ${s}_textH = max(${r(\"fontSize\")} * ${s}_res.y, ${o(1)});`),a.push(`let ${s}_glyphW = ${s}_textH * ${o(Ev/kv)};`),a.push(`let ${s}_advance = ${s}_glyphW + ${s}_textH * ${r(\"letterSpacing\")};`),a.push(`let ${s}_padX = ${s}_textH * ${o(.38)};`),a.push(`let ${s}_padY = ${s}_textH * ${o(.24)};`),a.push(`let ${s}_gap = ${s}_textH * ${o(.28)};`),i===\"dimensions\"?(a.push(`let ${s}_w = clamp(floor(${s}_boxW + ${o(.5)}), ${o(1)}, ${o(99999)});`),a.push(`let ${s}_h = clamp(floor(${s}_boxH + ${o(.5)}), ${o(1)}, ${o(99999)});`),a.push(`let ${s}_dw = ${D0(o,`${s}_w`)};`),a.push(`let ${s}_dh = ${D0(o,`${s}_h`)};`),a.push(`let ${s}_total = ${s}_dw + ${o(1)} + ${s}_dh;`)):(a.push(`let ${s}_pc = clamp(floor(${s}_leaf_coverage * ${o(100)} + ${o(.5)}), ${o(0)}, ${o(100)});`),a.push(`let ${s}_dp = ${D0(o,`${s}_pc`)};`),a.push(`let ${s}_total = ${s}_dp + ${o(1)};`)),a.push(`let ${s}_labelW = ${s}_total * ${s}_advance + ${s}_padX * ${o(2)};`),a.push(`let ${s}_labelH = ${s}_textH + ${s}_padY * ${o(2)};`);let d=l===1||l===3,u=l===2||l===3;a.push(`let ${s}_inset = ${r(\"labelInset\")} > ${o(.5)};`);let p=d?`${s}_bMaxX - ${s}_labelW - ${s}_gap`:`${s}_bMinX + ${s}_gap`,_=d?`${s}_bMaxX - ${s}_labelW`:`${s}_bMinX`;a.push(`var ${s}_labelLeft = select(${_}, ${p}, ${s}_inset);`);let f=u?`${s}_bMinY + ${s}_gap`:`${s}_bMaxY - ${s}_labelH - ${s}_gap`,h=u?`${s}_bMinY - ${s}_labelH - ${s}_gap`:`${s}_bMaxY + ${s}_gap`;a.push(`var ${s}_labelTop = select(${h}, ${f}, ${s}_inset);`),a.push(`let ${s}_cellMaxX = ${s}_origin.x + ${s}_size;`),a.push(`let ${s}_cellMaxY = ${s}_origin.y + ${s}_size;`),a.push(`${s}_labelLeft = clamp(${s}_labelLeft, ${s}_origin.x, max(${s}_cellMaxX - ${s}_labelW, ${s}_origin.x));`),a.push(`${s}_labelTop = clamp(${s}_labelTop, ${s}_origin.y, max(${s}_cellMaxY - ${s}_labelH, ${s}_origin.y));`),a.push(`let ${s}_labelCenter = vec2f(${s}_labelLeft + ${s}_labelW * ${o(.5)}, ${s}_labelTop + ${s}_labelH * ${o(.5)});`),a.push(`let ${s}_pillHalf = vec2f(${s}_labelW * ${o(.5)}, ${s}_labelH * ${o(.5)});`),a.push(`let ${s}_pillR = max(min(min(${r(\"labelRadius\")} * ${s}_scaleFactor, ${s}_pillHalf.x), ${s}_pillHalf.y), ${o(0)});`),a.push(`let ${s}_pillP = ${s}_fragPx - ${s}_labelCenter;`),a.push(...aA({q:`${s}_pillQ`,sd:`${s}_pillSD`},{pp:`${s}_pillP`,half:`${s}_pillHalf`,radius:`${s}_pillR`})),a.push(`let ${s}_pillA = (${o(1)} - smoothstep(${o(0)}, ${s}_aa, ${s}_pillSD)) * ${r(\"labelBackgroundColor\")}.a * ${s}_detF;`),a.push(ll(`${s}_dst`,`${r(\"labelBackgroundColor\")}.rgb`,`${s}_pillA`)),a.push(`let ${s}_rel = ${s}_fragPx.x - ${s}_labelLeft - ${s}_padX;`),a.push(`let ${s}_slot = floor(${s}_rel / ${s}_advance);`),a.push(`let ${s}_within = fract(${s}_rel / ${s}_advance);`),a.push(`let ${s}_uInChar = ${s}_within * (${s}_advance / ${s}_glyphW);`),a.push(`let ${s}_vInChar = (${s}_fragPx.y - ${s}_labelTop - ${s}_padY) / ${s}_textH;`),i===\"dimensions\"?(a.push(`let ${s}_wDigit = ${L0(o,`${s}_w`,`${s}_dw - ${o(1)} - ${s}_slot`)};`),a.push(`let ${s}_hDigit = ${L0(o,`${s}_h`,`${s}_dh + ${s}_dw - ${s}_slot`)};`),a.push(`var ${s}_g = ${s}_hDigit;`),a.push(`${s}_g = select(${s}_g, ${o(10)}, ${s}_slot == ${s}_dw);`),a.push(`${s}_g = select(${s}_g, ${s}_wDigit, ${s}_slot < ${s}_dw);`)):a.push(`var ${s}_g = select(${o(11)}, ${L0(o,`${s}_pc`,`${s}_dp - ${o(1)} - ${s}_slot`)}, ${s}_slot < ${s}_dp);`),a.push(`let ${s}_atlasU = (${s}_g + clamp(${s}_uInChar, ${o(0)}, ${o(1)})) / ${o(nB)};`),a.push(`let ${s}_glyphCov = textureSampleLevel(tex.$.${c}, samp.$.linearClamp, vec2f(${s}_atlasU, ${s}_vInChar), 0.0).a;`),a.push(`let ${s}_inText = ${s}_rel >= ${o(0)} && ${s}_slot >= ${o(0)} && ${s}_slot < ${s}_total && ${s}_uInChar <= ${o(1)} && ${s}_vInChar >= ${o(0)} && ${s}_vInChar <= ${o(1)};`),a.push(`let ${s}_textA = ${s}_glyphCov * select(${o(0)}, ${o(1)}, ${s}_inText) * ${r(\"labelColor\")}.a * ${s}_detF;`),a.push(ll(`${s}_dst`,`${r(\"labelColor\")}.rgb`,`${s}_textA`))}var hae=e=>{let{p:t,U:s,L:r,s:o}=e;o.push(`let dsz = ${t}_ms * ${r(.16)};`),o.push(`let dSd = (abs(dp.x) + abs(dp.y)) * ${r(.7071)} - dsz;`),o.push(ll(`${t}_dst`,`${s(\"keyframeColor\")}.rgb`,`(${r(1)} - smoothstep(${r(0)}, ${t}_aa, dSd)) * ${s(\"keyframeColor\")}.a * fade * ${s(\"trail\")}`))};function gae(e){return t=>{let{p:s,U:r,L:o,s:a}=t;a.push(`let validP = select(${o(0)}, ${o(1)}, abs(ePrev.w - mta.z) < ${o(.5)}) * select(${o(0)}, ${o(1)}, ePrev.z > ${o(.5)});`),a.push(\"let stampGap = abs(e.z - ePrev.z);\"),a.push(`let contiguous = select(${o(0)}, ${o(1)}, stampGap < ${o(e*1.5)});`),a.push(`let epp = vec2f(ePrev.x * ${s}_res.x, ePrev.y * ${s}_res.y);`),a.push(`let pa = ${s}_px - epp; let ba = ep - epp;`),a.push(`let h = clamp(dot(pa, ba) / max(dot(ba, ba), ${o(1e-4)}), ${o(0)}, ${o(1)});`),a.push(\"let segD = length(pa - ba * h);\"),a.push(ll(`${s}_dst`,`${r(\"pathColor\")}.rgb`,`(${o(1)} - smoothstep(${s}_lw * ${o(.4)}, ${s}_lw * ${o(.4)} + ${s}_aa, segD)) * ${r(\"pathColor\")}.a * fade * validP * contiguous * ${r(\"trail\")} * ${o(.8)}`))}}function yae(e,t){let{p:s,U:r,L:o,s:a}=e,i=t.len*t.every;a.push(`if (${r(\"trail\")} > ${o(.001)}) {`),a.push(`var ePrev = ${t.load(`${2+t.len-1}u`)};`),a.push(`for (var j = 0u; j < ${t.len}u; j = j + 1u) {`),a.push(`let e = ${t.load(\"2u + j\")};`),a.push(`let valid = select(${o(0)}, ${o(1)}, abs(e.w - mta.z) < ${o(.5)}) * select(${o(0)}, ${o(1)}, e.z > ${o(.5)});`),a.push(\"let age = head.z - e.z;\"),a.push(`let fade = clamp(${o(1)} - age / ${o(i)}, ${o(0)}, ${o(1)}) * valid;`),a.push(`let ep = vec2f(e.x * ${s}_res.x, e.y * ${s}_res.y);`),a.push(`let dp = ${s}_px - ep;`);for(let l of t.stages)l(e);a.push(\"ePrev = e;\"),a.push(\"}\"),a.push(\"}\")}var bae=e=>{let{p:t,U:s,L:r,s:o}=e;o.push(`let gp = ${t}_px - tp;`),o.push(`let hlf = ${t}_ms * ${r(.5)};`),o.push(...aA({q:\"q\",sd:\"boxSd\"},{pp:\"gp\",half:\"vec2f(hlf)\"})),o.push(`var strokeA = ${r(1)} - smoothstep(${r(0)}, ${t}_aa, abs(boxSd) - ${t}_lw * ${r(.5)});`),o.push(...oB({bl:\"bl\",inCorner:\"inCorner\"},{blInit:`hlf * ${r(.45)}`,px:\"gp.x\",py:\"gp.y\",hx:\"hlf\",hy:\"hlf\",stroke:\"strokeA\"})),o.push(ll(`${t}_dst`,`${s(\"markerColor\")}.rgb`,`strokeA * ${s(\"markerColor\")}.a * searching`)),o.push(`let tick = hlf * ${r(.55)};`),o.push(`let chH = ${r(1)} - smoothstep(${r(0)}, ${t}_aa, max(abs(gp.y) - ${t}_lw * ${r(.5)}, abs(gp.x) - tick));`),o.push(`let chV = ${r(1)} - smoothstep(${r(0)}, ${t}_aa, max(abs(gp.x) - ${t}_lw * ${r(.5)}, abs(gp.y) - tick));`),o.push(ll(`${t}_dst`,`${s(\"markerColor\")}.rgb`,`max(chH, chV) * ${r(.55)} * ${s(\"markerColor\")}.a * searching`)),o.push(ll(`${t}_dst`,\"vec3f(1.0, 1.0, 1.0)\",`(${r(1)} - smoothstep(${t}_lw, ${t}_lw + ${t}_aa, length(gp))) * searching`))},xae=ko({accumulateInstances:()=>fB,formatFloat:()=>fo,gridPlacement:()=>dB,instanceHueShift:()=>_B,instanceVariation:()=>mB,linePlacement:()=>pB,placementField:()=>cB,radialPlacement:()=>uB,sourceRect:()=>lB}),Rm=Math.PI;function lB(e,t,s){let{p:r,L:o}=e;return[`let ${r}_cropL = ${s.left};`,`let ${r}_cropR = ${s.right};`,`let ${r}_cropT = ${s.top};`,`let ${r}_cropB = ${s.bottom};`,`let ${r}_srcCx = (${t.cx}) + (${r}_cropL - ${r}_cropR) * (${t.hw});`,`let ${r}_srcCy = (${t.cy}) + (${r}_cropT - ${r}_cropB) * (${t.hh});`,`let ${r}_srcHw = max((${t.hw}) * (1.0 - ${r}_cropL - ${r}_cropR), ${o(1e-6)});`,`let ${r}_srcHh = max((${t.hh}) * (1.0 - ${r}_cropT - ${r}_cropB), ${o(1e-6)});`,`let ${r}_srcHwSq = ${r}_srcHw * ${r}_aspect;`,`let ${r}_srcHhSq = ${r}_srcHh;`]}function cB(e,t){let{p:s,L:r}=e;return[`let ${s}_fieldCenterSq = vec2f((${t.cx}) * ${s}_aspect, ${t.cy});`,`let ${s}_fieldHwSq = max((${t.hw}) * ${s}_aspect, ${r(1e-6)});`,`let ${s}_fieldHh = max(${t.hh}, ${r(1e-6)});`]}function dB(e,t){return{requested:()=>`(${e.columns} * ${e.rows})`,body:s=>{let{p:r,L:o}=s,a=[`let ${r}_cols = ${e.columns};`,`let ${r}_rows = ${e.rows};`,`let ${r}_col = ${r}_j - ${r}_cols * floor(${r}_j / ${r}_cols);`,`let ${r}_row = floor(${r}_j / ${r}_cols);`,`let ${r}_gapXSq = ${e.gapX} * ${r}_aspect;`,`let ${r}_gapY = ${e.gapY};`,`let ${r}_fieldW = ${r}_fieldHwSq * 2.0;`,`let ${r}_fieldH = ${r}_fieldHh * 2.0;`,`let ${r}_pitchX = (${r}_fieldW - ${r}_gapXSq * (${r}_cols - 1.0)) / ${r}_cols;`,`let ${r}_pitchY = (${r}_fieldH - ${r}_gapY * (${r}_rows - 1.0)) / ${r}_rows;`,`let ${r}_periodX = ${r}_pitchX + ${r}_gapXSq;`,`let ${r}_periodY = ${r}_pitchY + ${r}_gapY;`,`${r}_cellFitInv = max(${r}_srcHwSq * 2.0 / max(${r}_pitchX, ${o(1e-5)}), ${r}_srcHhSq * 2.0 / max(${r}_pitchY, ${o(1e-5)}));`,`let ${r}_rowMod2 = ${r}_row - 2.0 * floor(${r}_row / 2.0);`,`let ${r}_staggerOff = ${r}_rowMod2 * ${e.stagger} * ${r}_periodX;`,`let ${r}_cxArg = ${r}_col * ${r}_periodX + ${r}_staggerOff + ${r}_phase * ${r}_periodX;`,`let ${r}_cxMod = ${r}_periodX * ${r}_cols;`,`let ${r}_cellX = ${r}_cxArg - ${r}_cxMod * floor(${r}_cxArg / ${r}_cxMod);`,`let ${r}_cyArg = ${r}_row * ${r}_periodY + ${r}_phase * ${r}_periodY;`,`let ${r}_cyMod = ${r}_periodY * ${r}_rows;`,`let ${r}_cellY = ${r}_cyArg - ${r}_cyMod * floor(${r}_cyArg / ${r}_cyMod);`,`${r}_pos = vec2f(${r}_fieldCenterSq.x - ${r}_fieldW * 0.5 + ${r}_pitchX * 0.5 + ${r}_cellX, ${r}_fieldCenterSq.y - ${r}_fieldH * 0.5 + ${r}_pitchY * 0.5 + ${r}_cellY);`];return(t===1||t===3)&&a.push(`${r}_flipX = 1.0 - (${r}_col - 2.0 * floor(${r}_col / 2.0)) * 2.0;`),(t===2||t===3)&&a.push(`${r}_flipY = 1.0 - (${r}_row - 2.0 * floor(${r}_row / 2.0)) * 2.0;`),a}}}function uB(e,t){return{requested:()=>t.count,body:s=>{let{p:r,L:o}=s;return[`let ${r}_cSq = ${e};`,`let ${r}_sweep = ${t.sweep} * ${o(Rm/180)};`,`let ${r}_start = ${t.startAngle} * ${o(Rm/180)};`,`let ${r}_ang = ${r}_start + ${r}_sweep * (${r}_j / ${r}_totalN) + ${r}_phase * ${r}_sweep;`,`${r}_pos = ${r}_cSq + vec2f(cos(${r}_ang), sin(${r}_ang)) * ${t.radius};`,`${r}_layoutRot = select(0.0, ${r}_ang + ${o(Rm/2)}, ${t.faceCenter} > 0.0);`]}}}function pB(e,t){return{requested:()=>t.count,body:s=>{let{p:r,L:o}=s;return[`let ${r}_cSq = ${e};`,`let ${r}_dirRad = ${t.direction} * ${o(Rm/180)};`,`let ${r}_dir = vec2f(cos(${r}_dirRad), sin(${r}_dirRad));`,`let ${r}_tArg = ${r}_j + ${r}_phase;`,`let ${r}_t = ${r}_tArg - ${r}_totalN * floor(${r}_tArg / ${r}_totalN);`,`${r}_pos = ${r}_cSq + ${r}_dir * ${t.spacing} * ${r}_t;`]}}}function mB(e,t){let{p:s,L:r}=e,o=a=>`fract(sin((${s}_hashBase + ${r(a)}) * 78.233) * 43758.5453)`;return[`let ${s}_hashBase = ${s}_j + ${s}_seed * 7.0;`,`let ${s}_h1 = ${o(.123)};`,`let ${s}_h2 = ${o(5.456)};`,`let ${s}_h3 = ${o(9.789)};`,`let ${s}_h4 = ${o(13.21)};`,`let ${s}_h5 = ${o(17.65)};`,`let ${s}_scale = max(pow(${t.scale}, ${s}_j) * (1.0 + ${t.jitterScale} * (${s}_h3 - 0.5)), ${r(1e-4)});`,`let ${s}_rot = ${s}_layoutRot + ${t.rotation} * ${s}_j + ${t.jitterRotation} * (${s}_h4 - 0.5) * ${r(Rm*2)};`,`let ${s}_instOpacity = pow(${t.opacity}, ${s}_j) * (1.0 - ${t.jitterOpacity} * ${s}_h5);`,`${s}_pos = ${s}_pos + vec2f((${s}_h1 - 0.5) * ${s}_fieldHwSq, (${s}_h2 - 0.5) * ${s}_fieldHh) * ${t.jitterPosition};`]}function _B(e,t){let{p:s,L:r}=e;return[`let ${s}_hueAngle = ${t} * ${s}_j;`,`let ${s}_cosH = cos(${s}_hueAngle);`,`let ${s}_sinH = sin(${s}_hueAngle);`,`let ${s}_kOMC = (1.0 - ${s}_cosH) * ${r(1/3)};`,`let ${s}_sOS3 = ${s}_sinH / 1.7320508075688772;`,`let ${s}_mA = ${s}_cosH + ${s}_kOMC;`,`let ${s}_mB = ${s}_kOMC - ${s}_sOS3;`,`let ${s}_mC = ${s}_kOMC + ${s}_sOS3;`,`${s}_rgb = vec3f(${s}_src.r * ${s}_mA + ${s}_src.g * ${s}_mB + ${s}_src.b * ${s}_mC, ${s}_src.r * ${s}_mC + ${s}_src.g * ${s}_mA + ${s}_src.b * ${s}_mB, ${s}_src.r * ${s}_mB + ${s}_src.g * ${s}_mC + ${s}_src.b * ${s}_mA);`]}function fB(e,t){let{p:s}=e,r=[];r.push(`let ${s}_totalN = min(max(${t.placement.requested()}, 1.0), 64.0);`),r.push(`var ${s}_accum = vec4f(0.0, 0.0, 0.0, 0.0);`),r.push(`var ${s}_cellFitInv = 1.0;`);let o=[];o.push(`let ${s}_fi = f32(${s}_i);`),o.push(t.zBack?`let ${s}_j = ${s}_totalN - 1.0 - ${s}_fi;`:`let ${s}_j = ${s}_fi;`),o.push(`var ${s}_pos = vec2f(0.0, 0.0);`),o.push(`var ${s}_layoutRot = 0.0;`),o.push(`var ${s}_flipX = 1.0;`),o.push(`var ${s}_flipY = 1.0;`),o.push(...t.placement.body(e)),o.push(...t.variation),o.push(`let ${s}_negRot = -${s}_rot;`),o.push(`let ${s}_cosA = cos(${s}_negRot);`),o.push(`let ${s}_sinA = sin(${s}_negRot);`),o.push(`let ${s}_d0 = ${s}_pix - ${s}_pos;`),o.push(`let ${s}_dr = vec2f(${s}_d0.x * ${s}_cosA - ${s}_d0.y * ${s}_sinA, ${s}_d0.x * ${s}_sinA + ${s}_d0.y * ${s}_cosA);`),o.push(`let ${s}_dl = ${s}_dr / ${s}_scale;`),o.push(`let ${s}_localU = ${s}_dl.x * ${s}_flipX * ${s}_cellFitInv / (${s}_srcHwSq * 2.0) + 0.5;`),o.push(`let ${s}_localV = ${s}_dl.y * ${s}_flipY * ${s}_cellFitInv / (${s}_srcHhSq * 2.0) + 0.5;`);let a=[];a.push(`let ${s}_sampleUV = vec2f(${s}_srcCx - ${s}_srcHw + ${s}_localU * ${s}_srcHw * 2.0, ${s}_srcCy - ${s}_srcHh + ${s}_localV * ${s}_srcHh * 2.0);`),a.push(`let ${s}_src = textureSampleLevel(tex.$.${t.texKey}, samp.$.linearClamp, ${s}_sampleUV, 0.0);`),a.push(`var ${s}_rgb = ${s}_src.rgb;`),a.push(...t.hue),a.push(`let ${s}_a = ${s}_src.a * ${s}_instOpacity;`),a.push(`let ${s}_premul = ${s}_rgb * ${s}_a;`),a.push(`${s}_accum = vec4f(${s}_premul + ${s}_accum.rgb * (1.0 - ${s}_a), ${s}_a + ${s}_accum.a * (1.0 - ${s}_a));`),o.push(`if (${s}_localU >= 0.0 && ${s}_localU <= 1.0 && ${s}_localV >= 0.0 && ${s}_localV <= 1.0) {`);for(let i of a)o.push(`  ${i}`);o.push(\"}\"),r.push(`for (var ${s}_i = 0u; ${s}_i < u32(${s}_totalN); ${s}_i++) {`);for(let i of o)r.push(`  ${i}`);return r.push(\"}\"),r}function Rr(){if(typeof window>\"u\")return!1;let e=!!(window.matchMedia&&window.matchMedia(\"(pointer: coarse)\").matches),t=Math.max(window.innerWidth||0,window.innerHeight||0);return e&&t>0&&t<=1366}function vB(){return Rr()?1.5:2}var Aae=Rr()?24:50;function Bv(e,t,s,r){let o=`${e.p}_vn${e.noiseIdx.value++}`;t.push(`let ${o}_st = ${s};`),t.push(`let ${o}_fi = floor(${o}_st);`),t.push(`let ${o}_ff = fract(${o}_st);`);for(let[a,i]of[[\"a\",\"vec2f(0.0, 0.0)\"],[\"b\",\"vec2f(1.0, 0.0)\"],[\"c\",\"vec2f(0.0, 1.0)\"],[\"d\",\"vec2f(1.0, 1.0)\"]])t.push(`var ${o}_q${a} = fract((${o}_fi + ${i}) * vec2f(0.3183099, 0.3678794)) + vec2f(0.1);`),t.push(`${o}_q${a} = ${o}_q${a} + vec2f(dot(${o}_q${a}, ${o}_q${a} + vec2f(19.19)));`),t.push(`let ${o}_h${a} = fract(${o}_q${a}.x * ${o}_q${a}.y);`);t.push(`let ${o}_u = ${o}_ff * ${o}_ff * (vec2f(3.0) - 2.0 * ${o}_ff);`),t.push(`let ${r} = mix(mix(${o}_ha, ${o}_hb, ${o}_u.x), mix(${o}_hc, ${o}_hd, ${o}_u.x), ${o}_u.y);`)}function wae(e,t,s){let{p:r,stmts:o,U:a}=e;o.push(`let ${r}_uv = ${t};`),o.push(`let ${r}_aspect = ${s};`),o.push(`let ${r}_invAspect = 1.0 / ${r}_aspect;`),o.push(`let ${r}_inradius = 0.5 * min(${r}_aspect, 1.0);`),o.push(`let ${r}_outradius = 0.5 * length(vec2f(${r}_aspect, 1.0));`),o.push(`let ${r}_spread = ${a(\"spread\")};`),o.push(`let ${r}_reach = 0.7 * pow(${r}_spread, 1.3 + 2.7 * ${r}_spread);`),o.push(`let ${r}_centerU = ${a(\"center\")};`),o.push(`let ${r}_c = vec2f(${r}_centerU.x, 1.0 - ${r}_centerU.y);`),o.push(`let ${r}_fromCenter = vec2f((${r}_uv.x - ${r}_c.x) * ${r}_aspect, ${r}_uv.y - ${r}_c.y);`),o.push(`let ${r}_radius = length(${r}_fromCenter);`),o.push(`let ${r}_lensBulge = ${a(\"lensBulge\")};`),o.push(`let ${r}_lensCircle = ${a(\"lensCircle\")};`),o.push(`var ${r}_bulgeFade = 1.0;`),o.push(`var ${r}_warped = ${r}_fromCenter;`),o.push(`let ${r}_rSafe = max(${r}_radius, 1e-5);`),o.push(`if (${r}_lensBulge != 0.0) {`),o.push(`  let ${r}_rn = ${r}_rSafe / ${r}_inradius;`),o.push(`  let ${r}_bulgeAmt = abs(${r}_lensBulge) * select(1.2, 1.4, ${r}_lensBulge > 0.0);`),o.push(`  ${r}_bulgeFade = select(1.0, 1.0 - smoothstep(1.45, 1.53, ${r}_rn * ${r}_bulgeAmt), ${r}_lensBulge > 0.0);`),o.push(`  let ${r}_mapPos = tan(min(${r}_rn * ${r}_bulgeAmt, 1.53)) / tan(${r}_bulgeAmt);`),o.push(`  let ${r}_mapNeg = atan(${r}_rn * tan(${r}_bulgeAmt)) / ${r}_bulgeAmt;`),o.push(`  let ${r}_bulgeScale = select(${r}_mapNeg, ${r}_mapPos, ${r}_lensBulge > 0.0) / ${r}_rn;`),o.push(`  ${r}_warped = ${r}_warped * ${r}_bulgeScale;`),o.push(\"}\"),o.push(`if (${r}_lensCircle > 0.0) {`),o.push(`  let ${r}_wr = length(${r}_warped);`),o.push(`  let ${r}_dir = ${r}_warped / max(${r}_wr, 1e-5);`),o.push(`  let ${r}_halfBox = vec2f(${r}_aspect, 1.0) * 0.5;`),o.push(`  let ${r}_boxOff = vec2f((${r}_c.x - 0.5) * ${r}_aspect, ${r}_c.y - 0.5);`),o.push(`  let ${r}_rBox = min((${r}_halfBox.x - sign(${r}_dir.x) * ${r}_boxOff.x) / max(abs(${r}_dir.x), 1e-4), (${r}_halfBox.y - sign(${r}_dir.y) * ${r}_boxOff.y) / max(abs(${r}_dir.y), 1e-4));`),o.push(`  let ${r}_band = ${r}_inradius * mix(0.03, 0.2, 0.5 * (${r}_lensBulge + 1.0));`),o.push(`  let ${r}_over = smoothstep(0.0, 1.0, (${r}_wr - (${r}_inradius - ${r}_band)) / ${r}_band);`),o.push(`  let ${r}_g = ${r}_wr + (${r}_rBox - ${r}_inradius) * pow(${r}_over, 14.0);`),o.push(`  ${r}_warped = ${r}_dir * mix(${r}_wr, ${r}_g, ${r}_lensCircle);`),o.push(\"}\"),o.push(`let ${r}_baseUV = vec2f(${r}_warped.x * ${r}_invAspect, ${r}_warped.y) + ${r}_c;`),o.push(`let ${r}_baseDeriv = fwidth(${r}_baseUV);`),o.push(`let ${r}_edgeAA = clamp(2.0 * max(${r}_baseDeriv.x, ${r}_baseDeriv.y), 0.001, 0.02);`),o.push(`let ${r}_frameInvAA = 1.0 / clamp(${r}_baseDeriv, vec2f(1e-5), vec2f(0.02));`),o.push(`let ${r}_grainUV = (${r}_uv - vec2f(0.5)) * (0.8 / vec2f(length(dpdx(${r}_uv)), length(dpdy(${r}_uv)))) + vec2f(0.5);`)}function Tae(e){let{p:t,stmts:s,U:r}=e;s.push(`let ${t}_angleRad = radians(${r(\"angle\")});`),s.push(`let ${t}_uniformDir = vec2f(cos(${t}_angleRad), sin(${t}_angleRad));`),s.push(`let ${t}_invInradius = 1.0 / ${t}_inradius;`),s.push(`let ${t}_maxLen = (${t}_outradius + ${t}_reach) * ${t}_invInradius;`),s.push(`let ${t}_radialLen = ${t}_radius * ${t}_invInradius;`),s.push(`let ${t}_radialDir = ${t}_fromCenter * ${t}_invInradius * min(1.0, ${t}_maxLen / max(${t}_radialLen, 1e-6));`),s.push(`let ${t}_bandProximity = smoothstep(${t}_inradius * 0.8, ${t}_inradius, ${t}_radius);`),s.push(`let ${t}_circleMaxing = ${t}_bandProximity * ${t}_lensCircle * ${t}_lensCircle * ${t}_lensCircle;`),s.push(`let ${t}_spreadDir = mix(mix(${t}_uniformDir, ${t}_radialDir, ${r(\"perspective\")}), ${t}_radialDir, ${t}_circleMaxing);`),s.push(`let ${t}_warpedAbs = abs(${t}_baseUV - vec2f(0.5));`),s.push(`let ${t}_focusCenter = ${r(\"focusCenter\")};`),s.push(`let ${t}_inner = mix(1.0, smoothstep(0.0, mix(${t}_inradius, ${t}_outradius, ${t}_focusCenter), ${t}_radius), ${t}_focusCenter);`),s.push(`let ${t}_boxDist = max(${t}_warpedAbs.x, ${t}_warpedAbs.y) * 2.0;`),s.push(`let ${t}_outer = mix(1.0, 1.0 - min(${t}_boxDist, 1.0), ${r(\"focusEdges\")});`),s.push(`var ${t}_strength = ${t}_inner * ${t}_outer * mix(1.0, mix(0.15, 0.03, max(-${t}_lensBulge, 0.0)), ${t}_circleMaxing);`),s.push(`let ${t}_outside = max(${t}_warpedAbs - vec2f(0.5), vec2f(0.0));`),s.push(`let ${t}_margin = max(${t}_reach * length(${t}_spreadDir) * (1.0 - ${t}_lensCircle), ${t}_edgeAA);`),s.push(`${t}_strength = ${t}_strength * (1.0 - smoothstep(0.0, ${t}_margin, length(${t}_outside)));`),s.push(`var ${t}_axis = ${t}_spreadDir * (${t}_reach * ${t}_strength);`),s.push(`let ${t}_noise = ${r(\"noise\")};`),s.push(`if (${t}_noise > 0.0) {`);{let o=[];Bv(e,o,`${t}_fromCenter * (${r(\"noiseFrequency\")} * 18.0) + vec2f(${r(\"noiseOffset\")} * 30.0)`,`${t}_turnNoise`);for(let a of o)s.push(`  ${a}`)}s.push(`  let ${t}_turn = (${t}_turnNoise - 0.5) * 2.0 * ${t}_noise;`),s.push(`  let ${t}_tc = cos(${t}_turn);`),s.push(`  let ${t}_ts = sin(${t}_turn);`),s.push(`  ${t}_axis = vec2f(${t}_tc * ${t}_axis.x + ${t}_ts * ${t}_axis.y, -${t}_ts * ${t}_axis.x + ${t}_tc * ${t}_axis.y);`),s.push(\"}\"),s.push(`${t}_axis = vec2f(${t}_axis.x * ${t}_invAspect, ${t}_axis.y);`),s.push(`let ${t}_grainMixer = ${r(\"grainMixer\")};`),s.push(`if (${t}_grainMixer > 0.0) {`);{let o=[];Bv(e,o,`${t}_grainUV`,`${t}_grainSeed`);for(let a of o)s.push(`  ${a}`)}s.push(`  let ${t}_jit = fract(${t}_grainSeed * vec2f(157.31, 113.57)) * 2.0 - vec2f(1.0);`),s.push(`  ${t}_axis = ${t}_axis + ${t}_jit * (0.3 * ${t}_grainMixer * length(${t}_axis));`),s.push(\"}\")}function Sae(e,t){let{p:s}=e;t.push(`let ${s}_lo = clamp(${s}_sampleUV * ${s}_frameInvAA + vec2f(0.5), vec2f(0.0), vec2f(1.0));`),t.push(`let ${s}_hi = clamp((vec2f(1.0) - ${s}_sampleUV) * ${s}_frameInvAA + vec2f(0.5), vec2f(0.0), vec2f(1.0));`),t.push(`let ${s}_frame = ${s}_lo.x * ${s}_hi.x * ${s}_lo.y * ${s}_hi.y;`)}function Pae(e,t){let{p:s,stmts:r,U:o,L:a}=e,i=Math.PI;r.push(`let ${s}_countF = clamp(floor(${o(\"count\")}), 2.0, ${a(Aae)});`),r.push(`let ${s}_hueBase = ${o(\"dispersionColor\")} + 0.5;`),r.push(`let ${s}_bias = ${o(\"bias\")};`),r.push(`let ${s}_biasPower = 1.0 + 2.0 * abs(${s}_bias);`),r.push(`let ${s}_biasNeg = ${s}_bias < 0.0;`),r.push(`let ${s}_dispShift = ${o(\"dispersionShift\")};`),r.push(`let ${s}_icMask = 1.0 - smoothstep(0.5 * ${s}_inradius, 1.1 * ${s}_inradius, ${s}_radius);`),r.push(`let ${s}_dispAmt = ${o(\"dispersion\")} * mix(clamp(1.0 + ${s}_dispShift, 0.0, 1.0), clamp(1.0 - ${s}_dispShift, 0.0, 1.0), ${s}_icMask);`),r.push(`let ${s}_dispPower = pow(${s}_dispAmt, 0.8);`),r.push(`let ${s}_warpedRadius = length(${s}_warped);`),r.push(`let ${s}_swirlAngle = ${o(\"swirl\")} * ${a(.4*i)} * ${s}_strength * ${s}_spread * min(1.0, ${s}_inradius / max(${s}_warpedRadius, 1e-4));`),r.push(`let ${s}_texel = max((${s}_baseDeriv.x + ${s}_baseDeriv.y) * 0.5, 1e-6);`),r.push(`let ${s}_fanPx = (2.0 * length(${s}_axis) + abs(${s}_swirlAngle) * ${s}_warpedRadius) / ${s}_texel;`),r.push(`let ${s}_tapsF = clamp(ceil(${s}_fanPx * ${s}_biasPower) + 1.0, 2.0, ${s}_countF);`),r.push(`let ${s}_invCount = 1.0 / ${s}_tapsF;`),r.push(`let ${s}_invSpan = 1.0 / max(${s}_tapsF - 1.0, 1.0);`),r.push(`var ${s}_colorSum = vec3f(0.0);`),r.push(`var ${s}_weightSum = vec3f(0.0);`),r.push(`var ${s}_coverSum = 0.0;`);let l=[];l.push(`let ${s}_layer = f32(${s}_i);`),l.push(`let ${s}_hue = ${s}_hueBase + ${s}_layer * ${s}_invCount;`),l.push(`let ${s}_t0 = ${s}_layer * ${s}_invSpan;`),l.push(`let ${s}_curved = pow(select(${s}_t0, 1.0 - ${s}_t0, ${s}_biasNeg), ${s}_biasPower);`),l.push(`let ${s}_spreadT = select(${s}_curved, 1.0 - ${s}_curved, ${s}_biasNeg);`),l.push(`let ${s}_fanPos = 1.0 - 2.0 * ${s}_spreadT;`),l.push(`let ${s}_tap0 = ${s}_baseUV + ${s}_axis * ${s}_fanPos - ${s}_c;`),l.push(`let ${s}_sa = ${s}_swirlAngle * ${s}_fanPos;`),l.push(`let ${s}_sc = cos(${s}_sa);`),l.push(`let ${s}_ss = sin(${s}_sa);`),l.push(`let ${s}_tx = ${s}_tap0.x * ${s}_aspect;`),l.push(`let ${s}_sampleUV = vec2f((${s}_sc * ${s}_tx - ${s}_ss * ${s}_tap0.y) * ${s}_invAspect, ${s}_ss * ${s}_tx + ${s}_sc * ${s}_tap0.y) + ${s}_c;`),Sae(e,l),l.push(`let ${s}_tapC = textureSampleLevel(tex.$.${t}, samp.$.linearClamp, ${s}_sampleUV, 0.0);`),l.push(`let ${s}_cover = ${s}_tapC.a * ${s}_frame;`),l.push(`let ${s}_overWhite = vec3f(1.0 - ${s}_cover) + ${s}_tapC.rgb * ${s}_frame;`),l.push(`let ${s}_hraw = ${s}_hue * 6.0 + vec3f(0.0, 4.0, 2.0);`),l.push(`let ${s}_hmod = ${s}_hraw - 6.0 * floor(${s}_hraw / 6.0);`),l.push(`let ${s}_hclamped = clamp(abs(${s}_hmod - vec3f(3.0)) - vec3f(1.0), vec3f(0.0), vec3f(1.0));`),l.push(`let ${s}_hrgb = ${s}_hclamped * ${s}_hclamped * (vec3f(3.0) - 2.0 * ${s}_hclamped);`),l.push(`let ${s}_weight = vec3f(1.0) - ${s}_dispPower * ${s}_hrgb;`),l.push(`${s}_colorSum = ${s}_colorSum + ${s}_overWhite * ${s}_weight;`),l.push(`${s}_weightSum = ${s}_weightSum + ${s}_weight;`),l.push(`${s}_coverSum = ${s}_coverSum + ${s}_cover;`),r.push(`for (var ${s}_i = 0u; ${s}_i < u32(${s}_tapsF); ${s}_i++) {`);for(let c of l)r.push(`  ${c}`);r.push(\"}\"),r.push(`let ${s}_color = ${s}_colorSum / max(${s}_weightSum, vec3f(1e-4));`),r.push(`let ${s}_coverAvg = ${s}_coverSum * ${s}_invCount;`),r.push(`let ${s}_ground = min(${s}_color.r, min(${s}_color.g, ${s}_color.b));`),r.push(`let ${s}_alpha = max(${s}_coverAvg, 1.0 - ${s}_ground);`),r.push(`let ${s}_premul = max(${s}_color - vec3f(1.0 - ${s}_alpha), vec3f(0.0));`),r.push(`var ${s}_out = vec4f(${s}_premul, ${s}_alpha) * ${s}_bulgeFade;`)}function Cae(e){let{p:t,stmts:s,L:r}=e;s.push(`let ${t}_grainOverlay = ${e.U(\"grainOverlay\")};`),s.push(`if (${t}_grainOverlay > 0.0) {`);{let o=[],a=r(Math.cos(1)),i=r(Math.sin(1)),l=r(Math.cos(2)),c=r(Math.sin(2));o.push(`let ${t}_gr1 = vec2f(${a} * ${t}_grainUV.x - ${i} * ${t}_grainUV.y, ${i} * ${t}_grainUV.x + ${a} * ${t}_grainUV.y) + vec2f(3.0);`),o.push(`let ${t}_gr2 = vec2f(${l} * ${t}_grainUV.x - ${c} * ${t}_grainUV.y, ${c} * ${t}_grainUV.x + ${l} * ${t}_grainUV.y) + vec2f(-1.0);`),Bv(e,o,`${t}_gr1`,`${t}_gn1`),Bv(e,o,`${t}_gr2`,`${t}_gn2`),o.push(`let ${t}_grain = pow(mix(${t}_gn1, ${t}_gn2, 0.5), 1.3);`),o.push(`let ${t}_grainV = ${t}_grain * 2.0 - 1.0;`),o.push(`let ${t}_grainStrength = pow(${t}_grainOverlay * abs(${t}_grainV), 0.8) * ${t}_out.a;`),o.push(`${t}_out = vec4f(mix(${t}_out.rgb, vec3f(step(0.0, ${t}_grainV)) * ${t}_out.a, 0.35 * ${t}_grainStrength), ${t}_out.a);`);for(let d of o)s.push(`  ${d}`)}s.push(\"}\")}function nA(e,t){let{childNode:s,convertToTexture:r}=e;if(!s||!r)return null;let o=r(s),a=null;return{childTexture:o,bindInputs:i=>{let l=i(o.key);l&&(a=t(l.texture))},bound:()=>a,ready:()=>a!==null}}var Mae=e=>typeof e==\"number\"?[e,e]:[e[0],e[1]];function hB(e,t){let{gpu:s,registerComputeTexture:r,onCleanup:o,getCpuValue:a}=e,i=s?.root;if(!i)return null;let{format:l}=t,c=k=>{let D=i.createTexture({size:Mae(k),format:l}).$usage(\"storage\",\"sampled\");return{texture:D,destroy:()=>D.destroy()}},d=[],u=k=>(d.push(k),k),p=u(c(t.size)),_=u(c(t.size)),f=u(c(t.size)),h=Object.keys(t.extraSlots??{}),g={},y={};for(let k of h)g[k]=u(c(t.extraSlots[k])).texture,y[k]=u(c(t.extraSlots[k])).texture;let b={};for(let[k,D]of Object.entries(t.textures??{}))b[k]=u(c(D)).texture;o(()=>{for(let k of d)k.destroy()});let x=r(f.texture),P=ap({state:p.texture,extra:g},{state:_.texture,extra:y}),C=null,E,R=nA(e,k=>{let D={childTexture:k,display:f.texture,textures:b};return E=t.staticGroups?t.staticGroups(D):void 0,C=P.groups((O,F)=>t.bindGroups(D,O,F)),!0});if(!R)return null;let U=t.maxDeltaTime??.05,L=0;return{childTexture:R.childTexture,display:x,pair:P,bindInputs:R.bindInputs,tick:(k,D)=>{if(!R.ready()||!C)return null;let O=k?.deltaTime??.016,F=t.speedProp?a(t.speedProp):void 0,q=typeof F==\"number\"&&Number.isFinite(F)?F:1,Y=Math.min(O,U)*q;L+=Y;let V=D({groups:C(),shared:E,dt:Y,localTime:L,rawDeltaTime:O});return V&&P.swap(),V}}}var cP=64,U0=12,O0=8,O1=14,Eae=O1+1,zv=\"rgba32float\",tl=96,G0=Po,iA=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,trackersF:n,agility:n,threshold:n,variance:n,lifespan:n}),\"TrackParams\"),xm=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({src:{texture:Zt(n)},grid:{storageTexture:zt(zv,\"write-only\")},params:{uniform:iA}}),\"featureLayout\"),Go=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({src:{texture:Zt(n)},featGrid:{texture:Zt(n),sampleType:\"unfilterable-float\"},prev:{texture:Zt(n),sampleType:\"unfilterable-float\"},next:{storageTexture:zt(zv,\"write-only\")},present:{storageTexture:zt(zv,\"write-only\")},params:{uniform:iA}}),\"trackLayout\"),ln=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return Re(__tsover_mul(ae(t),43758.5453123))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"}],body:[0,[[10,[6,\"std.fract\",[[1,[6,\"std.sin\",[\"x\"]],\"*\",[5,\"43758.5453123\"]]]]]]]},externals:{\"std.fract\":()=>Re,\"std.sin\":()=>ae}})&&e.f)({})),\"trackHash\");function gB(e){return w.fn([S],n)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=(s=>{\"use gpu\";let r=B(s.w,1e-4),o=G(__tsover_div(__tsover_add(__tsover_add(__tsover_mul(s.x,.299),__tsover_mul(s.y,.587)),__tsover_mul(s.z,.114)),r),0,1),a=n(0);return e===\"alpha\"?a=s.w:e===\"dark\"?a=__tsover_mul(__tsover_sub(1,o),N(n(0),n(1),s.w>.5)):e===\"red\"?a=__tsover_mul(__tsover_mul(__tsover_mul(G(__tsover_div(s.x,r),0,1),N(n(0),n(1),s.x>=s.y)),N(n(0),n(1),s.x>=s.z)),N(n(0),n(1),s.w>.02)):e===\"green\"?a=__tsover_mul(__tsover_mul(__tsover_mul(G(__tsover_div(s.y,r),0,1),N(n(0),n(1),s.y>=s.x)),N(n(0),n(1),s.y>=s.z)),N(n(0),n(1),s.w>.02)):e===\"blue\"?a=__tsover_mul(__tsover_mul(__tsover_mul(G(__tsover_div(s.z,r),0,1),N(n(0),n(1),s.z>=s.x)),N(n(0),n(1),s.z>=s.y)),N(n(0),n(1),s.w>.02)):a=__tsover_mul(o,N(n(0),n(1),s.w>.02)),a}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"c\"}],body:[0,[[13,\"a\",[6,\"std.max\",[[7,\"c\",\"w\"],[5,\"0.0001\"]]]],[13,\"lumaU\",[6,\"std.clamp\",[[1,[1,[1,[1,[7,\"c\",\"x\"],\"*\",[5,\"0.299\"]],\"+\",[1,[7,\"c\",\"y\"],\"*\",[5,\"0.587\"]]],\"+\",[1,[7,\"c\",\"z\"],\"*\",[5,\"0.114\"]]],\"/\",\"a\"],[5,\"0\"],[5,\"1\"]]]],[12,\"score\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"mode\",\"===\",[103,\"alpha\"]],[2,\"score\",\"=\",[7,\"c\",\"w\"]],[11,[1,\"mode\",\"===\",[103,\"dark\"]],[2,\"score\",\"=\",[1,[1,[5,\"1\"],\"-\",\"lumaU\"],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"w\"],\">\",[5,\"0.5\"]]]]]],[11,[1,\"mode\",\"===\",[103,\"red\"]],[2,\"score\",\"=\",[1,[1,[1,[6,\"std.clamp\",[[1,[7,\"c\",\"x\"],\"/\",\"a\"],[5,\"0\"],[5,\"1\"]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"x\"],\">=\",[7,\"c\",\"y\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"x\"],\">=\",[7,\"c\",\"z\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"w\"],\">\",[5,\"0.02\"]]]]]],[11,[1,\"mode\",\"===\",[103,\"green\"]],[2,\"score\",\"=\",[1,[1,[1,[6,\"std.clamp\",[[1,[7,\"c\",\"y\"],\"/\",\"a\"],[5,\"0\"],[5,\"1\"]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"y\"],\">=\",[7,\"c\",\"x\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"y\"],\">=\",[7,\"c\",\"z\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"w\"],\">\",[5,\"0.02\"]]]]]],[11,[1,\"mode\",\"===\",[103,\"blue\"]],[2,\"score\",\"=\",[1,[1,[1,[6,\"std.clamp\",[[1,[7,\"c\",\"z\"],\"/\",\"a\"],[5,\"0\"],[5,\"1\"]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"z\"],\">=\",[7,\"c\",\"x\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"z\"],\">=\",[7,\"c\",\"y\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"w\"],\">\",[5,\"0.02\"]]]]]],[2,\"score\",\"=\",[1,\"lumaU\",\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"c\",\"w\"],\">\",[5,\"0.02\"]]]]]]]]]]],[10,\"score\"]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"d.f32\":()=>n,mode:()=>e,\"std.select\":()=>N}})&&t.f)({})).$name(`featureScore_${e}`)}function kae(e){return w.fn([M,M])((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r)=>{\"use gpu\";let o=Ti(xm.$.src),a=n(o.x),i=n(o.y),l=n(tl),c=__tsover_div(__tsover_add(n(s),.5),l),d=__tsover_div(__tsover_add(n(r),.5),l),u=__tsover_div(.25,l),p=n(0);for(let _=0;_<2;_++)for(let f=0;f<2;f++){let h=__tsover_add(c,__tsover_mul(__tsover_mul(__tsover_sub(n(f),.5),2),u)),g=__tsover_add(d,__tsover_mul(__tsover_mul(__tsover_sub(n(_),.5),2),u)),y=M(G(__tsover_mul(h,a),n(0),__tsover_sub(a,1))),b=M(G(__tsover_mul(g,i),n(0),__tsover_sub(i,1)));p=B(p,e(Ye(xm.$.src,fe(y,b),0)))}lt(xm.$.grid,fe(s,r),S(p,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gx\"},{type:\"i\",name:\"gy\"}],body:[0,[[13,\"srcDims\",[6,\"std.textureDimensions\",[[7,[7,\"featureLayout\",\"$\"],\"src\"]]]],[13,\"srcW\",[6,\"d.f32\",[[7,\"srcDims\",\"x\"]]]],[13,\"srcH\",[6,\"d.f32\",[[7,\"srcDims\",\"y\"]]]],[13,\"g\",[6,\"d.f32\",[\"FEAT_GRID\"]]],[13,\"cu\",[1,[1,[6,\"d.f32\",[\"gx\"]],\"+\",[5,\"0.5\"]],\"/\",\"g\"]],[13,\"cv\",[1,[1,[6,\"d.f32\",[\"gy\"]],\"+\",[5,\"0.5\"]],\"/\",\"g\"]],[13,\"q\",[1,[5,\"0.25\"],\"/\",\"g\"]],[12,\"score\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"sy\",[5,\"0\"]],[1,\"sy\",\"<\",[5,\"2\"]],[102,\"++\",\"sy\"],[0,[[14,[12,\"sx\",[5,\"0\"]],[1,\"sx\",\"<\",[5,\"2\"]],[102,\"++\",\"sx\"],[0,[[13,\"u\",[1,\"cu\",\"+\",[1,[1,[1,[6,\"d.f32\",[\"sx\"]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",\"q\"]]],[13,\"v\",[1,\"cv\",\"+\",[1,[1,[1,[6,\"d.f32\",[\"sy\"]],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",\"q\"]]],[13,\"tx\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"u\",\"*\",\"srcW\"],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcW\",\"-\",[5,\"1\"]]]]]]],[13,\"ty\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"v\",\"*\",\"srcH\"],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcH\",\"-\",[5,\"1\"]]]]]]],[2,\"score\",\"=\",[6,\"std.max\",[\"score\",[6,\"scoreFn\",[[6,\"std.textureLoad\",[[7,[7,\"featureLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"tx\",\"ty\"]],[5,\"0\"]]]]]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"featureLayout\",\"$\"],\"grid\"],[6,\"d.vec2u\",[\"gx\",\"gy\"]],[6,\"d.vec4f\",[\"score\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{\"std.textureDimensions\":()=>Ti,featureLayout:()=>xm,\"d.f32\":()=>n,FEAT_GRID:()=>tl,\"d.u32\":()=>M,\"std.clamp\":()=>G,\"std.max\":()=>B,scoreFn:()=>e,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.textureStore\":()=>lt,\"d.vec4f\":()=>S}})&&t.f)({})).$name(\"featureGridScan\")}function Bae(e,t){let s=t??gB(e);return w.fn([M])((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=(o=>{\"use gpu\";let a=Go.$.params,i=n(o),l=Ye(Go.$.prev,fe(M(0),o),0),c=Ye(Go.$.prev,fe(M(1),o),0),d=l.x,u=l.y,p=l.z,_=__tsover_sub(l.w,1),f=c.x,h=c.z,g=c.w;l.w<.5&&(d=__tsover_add(.15,__tsover_mul(.7,ln(__tsover_add(__tsover_mul(i,7.31),1.3)))),u=__tsover_add(.15,__tsover_mul(.7,ln(__tsover_add(__tsover_mul(i,11.93),4.7)))),p=n(0),_=n(0),f=n(0),h=n(1)),p=__tsover_add(p,1);let y=B(a.threshold,.02),b=B(__tsover_sub(1,__tsover_mul(a.variance,ln(__tsover_add(__tsover_add(__tsover_mul(i,6.13),__tsover_mul(h,.37)),2.9)))),y),x=Ti(Go.$.src),P=n(x.x),C=n(x.y),E=__tsover_add(.035,__tsover_mul(.11,a.agility)),R=n(0),U=n(0),L=n(0);for(let ke=-3;ke<=3;ke++)for(let Me=-3;Me<=3;Me++){let we=__tsover_mul(__tsover_div(n(Me),3),E),Ve=__tsover_mul(__tsover_div(n(ke),3),E),qe=G(__tsover_add(d,we),.02,.98),st=G(__tsover_add(u,Ve),.02,.98),pt=M(G(__tsover_mul(qe,P),n(0),__tsover_sub(P,1))),Ze=M(G(__tsover_mul(st,C),n(0),__tsover_sub(C,1))),nt=s(Ye(Go.$.src,fe(pt,Ze),0)),ht=N(n(0),n(1),nt>y),yt=__tsover_sub(nt,b),St=__tsover_mul(Tt(__tsover_mul(__tsover_mul(yt,yt),-24)),ht),Mt=__tsover_div(Ue(__tsover_add(__tsover_mul(we,we),__tsover_mul(Ve,Ve))),B(E,.001)),Rt=__tsover_mul(St,__tsover_sub(1,__tsover_mul(.3,Mt)));R=__tsover_add(R,Rt),U=__tsover_add(U,__tsover_mul(qe,Rt)),L=__tsover_add(L,__tsover_mul(st,Rt))}let k=R>.8,D=d,O=u;if(k&&(D=__tsover_div(U,R),O=__tsover_div(L,R)),f=N(__tsover_add(f,1),n(0),k),!k){let ke=n(tl),Me=n(0),we=d,Ve=u;for(let qe=0;qe<tl;qe++)for(let st=0;st<tl;st++){let pt=Ye(Go.$.featGrid,fe(M(st),M(qe)),0).x,Ze=N(n(0),n(1),pt>y),nt=__tsover_sub(pt,b),ht=__tsover_mul(Tt(__tsover_mul(__tsover_mul(nt,nt),-14)),Ze),yt=__tsover_div(__tsover_add(n(st),.5),ke),St=__tsover_div(__tsover_add(n(qe),.5),ke),Mt=__tsover_sub(yt,d),Rt=__tsover_sub(St,u),Kt=__tsover_add(.6,__tsover_mul(.8,ln(__tsover_add(__tsover_add(__tsover_mul(i,3.17),__tsover_mul(n(st),13.71)),__tsover_mul(n(qe),29.37))))),Wt=__tsover_div(__tsover_mul(ht,Kt),__tsover_add(1,__tsover_mul(5,__tsover_add(__tsover_mul(Mt,Mt),__tsover_mul(Rt,Rt)))));Wt>Me&&(Me=Wt,we=yt,Ve=St)}if(Me>.02)D=G(__tsover_add(we,__tsover_div(__tsover_sub(ln(__tsover_add(__tsover_mul(i,5.77),__tsover_mul(h,2.13))),.5),ke)),.02,.98),O=G(__tsover_add(Ve,__tsover_div(__tsover_sub(ln(__tsover_add(__tsover_mul(i,8.31),__tsover_mul(h,4.97))),.5),ke)),.02,.98);else{let qe=__tsover_add(__tsover_mul(ln(__tsover_add(__tsover_mul(i,13.77),__tsover_mul(h,3.19))),G0),__tsover_mul(ae(__tsover_add(__tsover_mul(p,.02),i)),1.4));D=G(__tsover_add(d,__tsover_mul(pe(qe),.08)),.03,.97),O=G(__tsover_add(u,__tsover_mul(ae(qe),.08)),.03,.97)}}let F=__tsover_mul(a.dt,__tsover_add(.15,__tsover_mul(1.4,a.agility))),q=__tsover_sub(D,d),Y=__tsover_sub(O,u),V=Ue(__tsover_add(__tsover_mul(q,q),__tsover_mul(Y,Y))),X=ye(n(1),__tsover_div(F,B(V,1e-5)));d=__tsover_add(d,__tsover_mul(q,X)),u=__tsover_add(u,__tsover_mul(Y,X));let Q=n(0),te=j(o);for(let ke=0;ke<te;ke++){let Me=n(ke),we=Ye(Go.$.prev,fe(M(0),M(ke)),0),Ve=__tsover_sub(we.x,d),qe=__tsover_sub(we.y,u),st=N(n(0),n(1),Ue(__tsover_add(__tsover_mul(Ve,Ve),__tsover_mul(qe,qe)))<.022);Q=B(Q,__tsover_mul(__tsover_mul(st,we.w),N(n(0),n(1),Me<a.trackersF)))}if(g=N(n(0),__tsover_add(g,1),Q>.5),g>30){let ke=__tsover_mul(ln(__tsover_add(__tsover_add(__tsover_mul(i,9.13),__tsover_mul(h,5.77)),2.3)),G0),Me=__tsover_add(.05,__tsover_mul(.08,ln(__tsover_add(__tsover_mul(i,4.71),__tsover_mul(h,8.11)))));d=G(__tsover_add(d,__tsover_mul(pe(ke),Me)),.03,.97),u=G(__tsover_add(u,__tsover_mul(ae(ke),Me)),.03,.97),h=__tsover_add(h,1),_=p,f=n(0),g=n(0)}let J=__tsover_mul(__tsover_mul(a.lifespan,60),__tsover_add(.7,__tsover_mul(.6,ln(__tsover_add(__tsover_mul(i,4.77),__tsover_mul(h,1.31)))))),oe=__tsover_mul(N(n(0),n(1),__tsover_sub(p,_)>J),N(n(0),n(1),a.lifespan>.01)),le=N(n(0),n(1),f>90);__tsover_add(oe,le)>.5&&(d=__tsover_add(.1,__tsover_mul(.8,ln(__tsover_add(__tsover_add(__tsover_mul(i,7.31),__tsover_mul(h,13.7)),__tsover_mul(p,.013))))),u=__tsover_add(.1,__tsover_mul(.8,ln(__tsover_add(__tsover_add(__tsover_mul(i,11.93),__tsover_mul(h,17.3)),__tsover_mul(p,.019))))),h=__tsover_add(h,1),_=p,f=n(0),g=n(0));let se=__tsover_mod(ie(__tsover_div(p,n(O0))),n(U0)),Te=N(n(0),n(1),__tsover_mod(p,n(O0))<.5),de=d,xe=u,Ae=d,De=u;for(let ke=0;ke<U0;ke++){let Me=n(ke),we=Ye(Go.$.prev,fe(M(__tsover_add(2,ke)),o),0),Ve=N(n(0),n(1),re(__tsover_sub(Me,se))<.5),qe=__tsover_mul(Ve,Te);we=K(we,S(d,u,p,h),qe),lt(Go.$.next,fe(M(__tsover_add(2,ke)),o),we),lt(Go.$.present,fe(M(__tsover_add(2,ke)),o),we);let st=__tsover_mul(N(n(0),n(1),re(__tsover_sub(we.w,h))<.5),N(n(0),n(1),we.z>.5));de=ye(de,K(d,we.x,st)),xe=ye(xe,K(u,we.y,st)),Ae=B(Ae,K(d,we.x,st)),De=B(De,K(u,we.y,st))}let Ee=S(de,xe,Ae,De);lt(Go.$.next,fe(M(O1),o),Ee),lt(Go.$.present,fe(M(O1),o),Ee);let ze=S(d,u,p,__tsover_add(_,1)),Ie=S(f,R,h,g);lt(Go.$.next,fe(M(0),o),ze),lt(Go.$.present,fe(M(0),o),ze),lt(Go.$.next,fe(M(1),o),Ie),lt(Go.$.present,fe(M(1),o),Ie)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ti\"}],body:[0,[[13,\"P\",[7,[7,\"trackLayout\",\"$\"],\"params\"]],[13,\"iF\",[6,\"d.f32\",[\"ti\"]]],[13,\"head\",[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"0\"]]],\"ti\"]],[5,\"0\"]]]],[13,\"meta\",[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"1\"]]],\"ti\"]],[5,\"0\"]]]],[12,\"posX\",[7,\"head\",\"x\"]],[12,\"posY\",[7,\"head\",\"y\"]],[12,\"frame\",[7,\"head\",\"z\"]],[12,\"genStart\",[1,[7,\"head\",\"w\"],\"-\",[5,\"1\"]]],[12,\"missStreak\",[7,\"meta\",\"x\"]],[12,\"gen\",[7,\"meta\",\"z\"]],[12,\"crowdStreak\",[7,\"meta\",\"w\"]],[11,[1,[7,\"head\",\"w\"],\"<\",[5,\"0.5\"]],[0,[[2,\"posX\",\"=\",[1,[5,\"0.15\"],\"+\",[1,[5,\"0.7\"],\"*\",[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"7.31\"]],\"+\",[5,\"1.3\"]]]]]]],[2,\"posY\",\"=\",[1,[5,\"0.15\"],\"+\",[1,[5,\"0.7\"],\"*\",[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"11.93\"]],\"+\",[5,\"4.7\"]]]]]]],[2,\"frame\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]],[2,\"genStart\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]],[2,\"missStreak\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]],[2,\"gen\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]]]]],[2,\"frame\",\"=\",[1,\"frame\",\"+\",[5,\"1\"]]],[13,\"minFeat\",[6,\"std.max\",[[7,\"P\",\"threshold\"],[5,\"0.02\"]]]],[13,\"Lpref\",[6,\"std.max\",[[1,[5,\"1\"],\"-\",[1,[7,\"P\",\"variance\"],\"*\",[6,\"trackHash\",[[1,[1,[1,\"iF\",\"*\",[5,\"6.13\"]],\"+\",[1,\"gen\",\"*\",[5,\"0.37\"]]],\"+\",[5,\"2.9\"]]]]]],\"minFeat\"]]],[13,\"srcDims\",[6,\"std.textureDimensions\",[[7,[7,\"trackLayout\",\"$\"],\"src\"]]]],[13,\"srcW\",[6,\"d.f32\",[[7,\"srcDims\",\"x\"]]]],[13,\"srcH\",[6,\"d.f32\",[[7,\"srcDims\",\"y\"]]]],[13,\"radius\",[1,[5,\"0.035\"],\"+\",[1,[5,\"0.11\"],\"*\",[7,\"P\",\"agility\"]]]],[12,\"sumW\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"sumX\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"sumY\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"dy\",[4,\"-\",[5,\"3\"]]],[1,\"dy\",\"<=\",[5,\"3\"]],[102,\"++\",\"dy\"],[0,[[14,[12,\"dx\",[4,\"-\",[5,\"3\"]]],[1,\"dx\",\"<=\",[5,\"3\"]],[102,\"++\",\"dx\"],[0,[[13,\"ox\",[1,[1,[6,\"d.f32\",[\"dx\"]],\"/\",[5,\"3\"]],\"*\",\"radius\"]],[13,\"oy\",[1,[1,[6,\"d.f32\",[\"dy\"]],\"/\",[5,\"3\"]],\"*\",\"radius\"]],[13,\"cx01\",[6,\"std.clamp\",[[1,\"posX\",\"+\",\"ox\"],[5,\"0.02\"],[5,\"0.98\"]]]],[13,\"cy01\",[6,\"std.clamp\",[[1,\"posY\",\"+\",\"oy\"],[5,\"0.02\"],[5,\"0.98\"]]]],[13,\"tx\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cx01\",\"*\",\"srcW\"],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcW\",\"-\",[5,\"1\"]]]]]]],[13,\"ty\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cy01\",\"*\",\"srcH\"],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcH\",\"-\",[5,\"1\"]]]]]]],[13,\"s\",[6,\"score\",[[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"tx\",\"ty\"]],[5,\"0\"]]]]]],[13,\"gate\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"s\",\">\",\"minFeat\"]]]],[13,\"dBand\",[1,\"s\",\"-\",\"Lpref\"]],[13,\"aff\",[1,[6,\"std.exp\",[[1,[1,\"dBand\",\"*\",\"dBand\"],\"*\",[4,\"-\",[5,\"24\"]]]]],\"*\",\"gate\"]],[13,\"off\",[1,[6,\"std.sqrt\",[[1,[1,\"ox\",\"*\",\"ox\"],\"+\",[1,\"oy\",\"*\",\"oy\"]]]],\"/\",[6,\"std.max\",[\"radius\",[5,\"0.001\"]]]]],[13,\"w\",[1,\"aff\",\"*\",[1,[5,\"1\"],\"-\",[1,[5,\"0.3\"],\"*\",\"off\"]]]],[2,\"sumW\",\"=\",[1,\"sumW\",\"+\",\"w\"]],[2,\"sumX\",\"=\",[1,\"sumX\",\"+\",[1,\"cx01\",\"*\",\"w\"]]],[2,\"sumY\",\"=\",[1,\"sumY\",\"+\",[1,\"cy01\",\"*\",\"w\"]]]]]]]]],[13,\"locked\",[1,\"sumW\",\">\",[5,\"0.8\"]]],[12,\"bestX\",\"posX\"],[12,\"bestY\",\"posY\"],[11,\"locked\",[0,[[2,\"bestX\",\"=\",[1,\"sumX\",\"/\",\"sumW\"]],[2,\"bestY\",\"=\",[1,\"sumY\",\"/\",\"sumW\"]]]]],[2,\"missStreak\",\"=\",[6,\"std.select\",[[1,\"missStreak\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"locked\"]]],[11,[4,\"!\",\"locked\"],[0,[[13,\"g\",[6,\"d.f32\",[\"FEAT_GRID\"]]],[12,\"bestW\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"tgtX\",\"posX\"],[12,\"tgtY\",\"posY\"],[14,[12,\"gy\",[5,\"0\"]],[1,\"gy\",\"<\",\"FEAT_GRID\"],[102,\"++\",\"gy\"],[0,[[14,[12,\"gx\",[5,\"0\"]],[1,\"gx\",\"<\",\"FEAT_GRID\"],[102,\"++\",\"gx\"],[0,[[13,\"cs\",[7,[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"featGrid\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"gx\"]],[6,\"d.u32\",[\"gy\"]]]],[5,\"0\"]]],\"x\"]],[13,\"cGate\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"cs\",\">\",\"minFeat\"]]]],[13,\"dB\",[1,\"cs\",\"-\",\"Lpref\"]],[13,\"aff\",[1,[6,\"std.exp\",[[1,[1,\"dB\",\"*\",\"dB\"],\"*\",[4,\"-\",[5,\"14\"]]]]],\"*\",\"cGate\"]],[13,\"cx\",[1,[1,[6,\"d.f32\",[\"gx\"]],\"+\",[5,\"0.5\"]],\"/\",\"g\"]],[13,\"cy\",[1,[1,[6,\"d.f32\",[\"gy\"]],\"+\",[5,\"0.5\"]],\"/\",\"g\"]],[13,\"ddx\",[1,\"cx\",\"-\",\"posX\"]],[13,\"ddy\",[1,\"cy\",\"-\",\"posY\"]],[13,\"pref\",[1,[5,\"0.6\"],\"+\",[1,[5,\"0.8\"],\"*\",[6,\"trackHash\",[[1,[1,[1,\"iF\",\"*\",[5,\"3.17\"]],\"+\",[1,[6,\"d.f32\",[\"gx\"]],\"*\",[5,\"13.71\"]]],\"+\",[1,[6,\"d.f32\",[\"gy\"]],\"*\",[5,\"29.37\"]]]]]]]],[13,\"wgt\",[1,[1,\"aff\",\"*\",\"pref\"],\"/\",[1,[5,\"1\"],\"+\",[1,[5,\"5\"],\"*\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]]]]],[11,[1,\"wgt\",\">\",\"bestW\"],[0,[[2,\"bestW\",\"=\",\"wgt\"],[2,\"tgtX\",\"=\",\"cx\"],[2,\"tgtY\",\"=\",\"cy\"]]]]]]]]]],[11,[1,\"bestW\",\">\",[5,\"0.02\"]],[0,[[2,\"bestX\",\"=\",[6,\"std.clamp\",[[1,\"tgtX\",\"+\",[1,[1,[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"5.77\"]],\"+\",[1,\"gen\",\"*\",[5,\"2.13\"]]]]],\"-\",[5,\"0.5\"]],\"/\",\"g\"]],[5,\"0.02\"],[5,\"0.98\"]]]],[2,\"bestY\",\"=\",[6,\"std.clamp\",[[1,\"tgtY\",\"+\",[1,[1,[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"8.31\"]],\"+\",[1,\"gen\",\"*\",[5,\"4.97\"]]]]],\"-\",[5,\"0.5\"]],\"/\",\"g\"]],[5,\"0.02\"],[5,\"0.98\"]]]]]],[0,[[13,\"ang\",[1,[1,[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"13.77\"]],\"+\",[1,\"gen\",\"*\",[5,\"3.19\"]]]]],\"*\",\"TWO_PI\"],\"+\",[1,[6,\"std.sin\",[[1,[1,\"frame\",\"*\",[5,\"0.02\"]],\"+\",\"iF\"]]],\"*\",[5,\"1.4\"]]]],[2,\"bestX\",\"=\",[6,\"std.clamp\",[[1,\"posX\",\"+\",[1,[6,\"std.cos\",[\"ang\"]],\"*\",[5,\"0.08\"]]],[5,\"0.03\"],[5,\"0.97\"]]]],[2,\"bestY\",\"=\",[6,\"std.clamp\",[[1,\"posY\",\"+\",[1,[6,\"std.sin\",[\"ang\"]],\"*\",[5,\"0.08\"]]],[5,\"0.03\"],[5,\"0.97\"]]]]]]]]]],[13,\"maxStep\",[1,[7,\"P\",\"dt\"],\"*\",[1,[5,\"0.15\"],\"+\",[1,[5,\"1.4\"],\"*\",[7,\"P\",\"agility\"]]]]],[13,\"dX\",[1,\"bestX\",\"-\",\"posX\"]],[13,\"dY\",[1,\"bestY\",\"-\",\"posY\"]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"dX\",\"*\",\"dX\"],\"+\",[1,\"dY\",\"*\",\"dY\"]]]]],[13,\"stepK\",[6,\"std.min\",[[6,\"d.f32\",[[5,\"1\"]]],[1,\"maxStep\",\"/\",[6,\"std.max\",[\"dist\",[5,\"0.00001\"]]]]]]],[2,\"posX\",\"=\",[1,\"posX\",\"+\",[1,\"dX\",\"*\",\"stepK\"]]],[2,\"posY\",\"=\",[1,\"posY\",\"+\",[1,\"dY\",\"*\",\"stepK\"]]],[12,\"crowded\",[6,\"d.f32\",[[5,\"0\"]]]],[13,\"iN\",[6,\"d.i32\",[\"ti\"]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",\"iN\"],[102,\"++\",\"k\"],[0,[[13,\"kF\",[6,\"d.f32\",[\"k\"]]],[13,\"other\",[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"0\"]]],[6,\"d.u32\",[\"k\"]]]],[5,\"0\"]]]],[13,\"odx\",[1,[7,\"other\",\"x\"],\"-\",\"posX\"]],[13,\"ody\",[1,[7,\"other\",\"y\"],\"-\",\"posY\"]],[13,\"near\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"std.sqrt\",[[1,[1,\"odx\",\"*\",\"odx\"],\"+\",[1,\"ody\",\"*\",\"ody\"]]]],\"<\",[5,\"0.022\"]]]]],[2,\"crowded\",\"=\",[6,\"std.max\",[\"crowded\",[1,[1,\"near\",\"*\",[7,\"other\",\"w\"]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"kF\",\"<\",[7,\"P\",\"trackersF\"]]]]]]]]]]],[2,\"crowdStreak\",\"=\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[1,\"crowdStreak\",\"+\",[5,\"1\"]],[1,\"crowded\",\">\",[5,\"0.5\"]]]]],[11,[1,\"crowdStreak\",\">\",[5,\"30\"]],[0,[[13,\"hopAng\",[1,[6,\"trackHash\",[[1,[1,[1,\"iF\",\"*\",[5,\"9.13\"]],\"+\",[1,\"gen\",\"*\",[5,\"5.77\"]]],\"+\",[5,\"2.3\"]]]],\"*\",\"TWO_PI\"]],[13,\"hopDist\",[1,[5,\"0.05\"],\"+\",[1,[5,\"0.08\"],\"*\",[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"4.71\"]],\"+\",[1,\"gen\",\"*\",[5,\"8.11\"]]]]]]]],[2,\"posX\",\"=\",[6,\"std.clamp\",[[1,\"posX\",\"+\",[1,[6,\"std.cos\",[\"hopAng\"]],\"*\",\"hopDist\"]],[5,\"0.03\"],[5,\"0.97\"]]]],[2,\"posY\",\"=\",[6,\"std.clamp\",[[1,\"posY\",\"+\",[1,[6,\"std.sin\",[\"hopAng\"]],\"*\",\"hopDist\"]],[5,\"0.03\"],[5,\"0.97\"]]]],[2,\"gen\",\"=\",[1,\"gen\",\"+\",[5,\"1\"]]],[2,\"genStart\",\"=\",\"frame\"],[2,\"missStreak\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]],[2,\"crowdStreak\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]]]]],[13,\"lifeFrames\",[1,[1,[7,\"P\",\"lifespan\"],\"*\",[5,\"60\"]],\"*\",[1,[5,\"0.7\"],\"+\",[1,[5,\"0.6\"],\"*\",[6,\"trackHash\",[[1,[1,\"iF\",\"*\",[5,\"4.77\"]],\"+\",[1,\"gen\",\"*\",[5,\"1.31\"]]]]]]]]],[13,\"expired\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[1,\"frame\",\"-\",\"genStart\"],\">\",\"lifeFrames\"]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"P\",\"lifespan\"],\">\",[5,\"0.01\"]]]]]],[13,\"lost\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"missStreak\",\">\",[5,\"90\"]]]]],[11,[1,[1,\"expired\",\"+\",\"lost\"],\">\",[5,\"0.5\"]],[0,[[2,\"posX\",\"=\",[1,[5,\"0.1\"],\"+\",[1,[5,\"0.8\"],\"*\",[6,\"trackHash\",[[1,[1,[1,\"iF\",\"*\",[5,\"7.31\"]],\"+\",[1,\"gen\",\"*\",[5,\"13.7\"]]],\"+\",[1,\"frame\",\"*\",[5,\"0.013\"]]]]]]]],[2,\"posY\",\"=\",[1,[5,\"0.1\"],\"+\",[1,[5,\"0.8\"],\"*\",[6,\"trackHash\",[[1,[1,[1,\"iF\",\"*\",[5,\"11.93\"]],\"+\",[1,\"gen\",\"*\",[5,\"17.3\"]]],\"+\",[1,\"frame\",\"*\",[5,\"0.019\"]]]]]]]],[2,\"gen\",\"=\",[1,\"gen\",\"+\",[5,\"1\"]]],[2,\"genStart\",\"=\",\"frame\"],[2,\"missStreak\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]],[2,\"crowdStreak\",\"=\",[6,\"d.f32\",[[5,\"0\"]]]]]]],[13,\"writeIdx\",[1,[6,\"std.floor\",[[1,\"frame\",\"/\",[6,\"d.f32\",[\"TRAIL_EVERY\"]]]]],\"%\",[6,\"d.f32\",[\"TRAIL_LEN\"]]]],[13,\"shouldWrite\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[1,\"frame\",\"%\",[6,\"d.f32\",[\"TRAIL_EVERY\"]]],\"<\",[5,\"0.5\"]]]]],[12,\"bbMinX\",\"posX\"],[12,\"bbMinY\",\"posY\"],[12,\"bbMaxX\",\"posX\"],[12,\"bbMaxY\",\"posY\"],[14,[12,\"j\",[5,\"0\"]],[1,\"j\",\"<\",\"TRAIL_LEN\"],[102,\"++\",\"j\"],[0,[[13,\"jF\",[6,\"d.f32\",[\"j\"]]],[12,\"e\",[6,\"std.textureLoad\",[[7,[7,\"trackLayout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[1,[5,\"2\"],\"+\",\"j\"]]],\"ti\"]],[5,\"0\"]]]],[13,\"isSlot\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"std.abs\",[[1,\"jF\",\"-\",\"writeIdx\"]]],\"<\",[5,\"0.5\"]]]]],[13,\"doWrite\",[1,\"isSlot\",\"*\",\"shouldWrite\"]],[2,\"e\",\"=\",[6,\"std.mix\",[\"e\",[6,\"d.vec4f\",[\"posX\",\"posY\",\"frame\",\"gen\"]],\"doWrite\"]]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"next\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[1,[5,\"2\"],\"+\",\"j\"]]],\"ti\"]],\"e\"]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"present\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[1,[5,\"2\"],\"+\",\"j\"]]],\"ti\"]],\"e\"]],[13,\"live\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"std.abs\",[[1,[7,\"e\",\"w\"],\"-\",\"gen\"]]],\"<\",[5,\"0.5\"]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"e\",\"z\"],\">\",[5,\"0.5\"]]]]]],[2,\"bbMinX\",\"=\",[6,\"std.min\",[\"bbMinX\",[6,\"std.mix\",[\"posX\",[7,\"e\",\"x\"],\"live\"]]]]],[2,\"bbMinY\",\"=\",[6,\"std.min\",[\"bbMinY\",[6,\"std.mix\",[\"posY\",[7,\"e\",\"y\"],\"live\"]]]]],[2,\"bbMaxX\",\"=\",[6,\"std.max\",[\"bbMaxX\",[6,\"std.mix\",[\"posX\",[7,\"e\",\"x\"],\"live\"]]]]],[2,\"bbMaxY\",\"=\",[6,\"std.max\",[\"bbMaxY\",[6,\"std.mix\",[\"posY\",[7,\"e\",\"y\"],\"live\"]]]]]]]],[13,\"bbOut\",[6,\"d.vec4f\",[\"bbMinX\",\"bbMinY\",\"bbMaxX\",\"bbMaxY\"]]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"next\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"AABB_COL\"]],\"ti\"]],\"bbOut\"]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"present\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"AABB_COL\"]],\"ti\"]],\"bbOut\"]],[13,\"headOut\",[6,\"d.vec4f\",[\"posX\",\"posY\",\"frame\",[1,\"genStart\",\"+\",[5,\"1\"]]]]],[13,\"metaOut\",[6,\"d.vec4f\",[\"missStreak\",\"sumW\",\"gen\",\"crowdStreak\"]]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"next\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"0\"]]],\"ti\"]],\"headOut\"]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"present\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"0\"]]],\"ti\"]],\"headOut\"]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"next\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"1\"]]],\"ti\"]],\"metaOut\"]],[6,\"std.textureStore\",[[7,[7,\"trackLayout\",\"$\"],\"present\"],[6,\"d.vec2u\",[[6,\"d.u32\",[[5,\"1\"]]],\"ti\"]],\"metaOut\"]]]]},externals:{trackLayout:()=>Go,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,trackHash:()=>ln,\"std.max\":()=>B,\"std.textureDimensions\":()=>Ti,\"std.clamp\":()=>G,score:()=>s,\"std.select\":()=>N,\"std.exp\":()=>Tt,\"std.sqrt\":()=>Ue,FEAT_GRID:()=>tl,TWO_PI:()=>G0,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.min\":()=>ye,\"d.i32\":()=>j,\"std.floor\":()=>ie,TRAIL_EVERY:()=>O0,TRAIL_LEN:()=>U0,\"std.abs\":()=>re,\"std.mix\":()=>K,\"d.vec4f\":()=>S,\"std.textureStore\":()=>lt,AABB_COL:()=>O1}})&&r.f)({})).$name(\"trackerPursuit\")}function yB(e){return t=>{let{childNode:s,gpu:r,getCpuValue:o}=t;if(!s)return null;let a=r?.root;if(!a)return null;let i=gB(e.mode),l=kae(i),c=Bae(e.mode,i),d=(globalThis.__TYPEGPU_AUTONAME__??(f=>f))(a.createUniform(iA),\"paramsBuf\"),u=hB(t,{size:[Eae,cP],format:zv,textures:{grid:tl},bindGroups:({childTexture:f,display:h,textures:g},y,b)=>a.createBindGroup(Go,{src:f,featGrid:g.grid,prev:y.state,next:b.state,present:h,params:d.buffer}),staticGroups:({childTexture:f,textures:h})=>a.createBindGroup(xm,{src:f,grid:h.grid,params:d.buffer})});if(!u)return null;let p=wr(a,(f=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(f.f=((h,g)=>{\"use gpu\";l(h,g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gx\"},{type:\"i\",name:\"gy\"}],body:[0,[[6,\"featureKernel\",[\"gx\",\"gy\"]]]]},externals:{featureKernel:()=>l}})&&f.f)({}),{size:[tl,tl]}),_=wr(a,(f=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(f.f=(h=>{\"use gpu\";c(h)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ti\"}],body:[0,[[6,\"trackKernel\",[\"ti\"]]]]},externals:{trackKernel:()=>c}})&&f.f)({}),{size:[cP]});return{outputs:{childTexture:u.childTexture,trackState:u.display},bindInputs:u.bindInputs,getComputeNodes:f=>u.tick(f,({groups:h,shared:g,dt:y})=>(d.write({dt:y,trackersF:o(e.trackers)??4,agility:o(e.agility)??.5,threshold:o(e.threshold)??.15,variance:o(e.variance)??.5,lifespan:o(e.lifespan)??6}),[p.with(g),_.with(h)]))}}}function Sl(e={}){let t=e.smoothing??.2,s=e.teleportGuard??.25,r=e.minDrag??6e-4,o=e.initialX??.5,a=e.initialY??.5,i=0,l=0,c=!1,d=!1;return{update(u,p){let _=u?.seen===!1;_?c=!0:u?.x!==void 0&&c&&!d&&(d=!0,o=u.x,a=u.y??a);let f=o,h=a,g=_?o:u?.x??o,y=_?a:u?.y??a,b=g-f,x=y-h,P=Math.sqrt(b*b+x*x),C=P>=s;o=g,a=y;let E=C?0:b,R=C?0:x,U=1/Math.max(p,.001),L=E*U,k=R*U;return i=i*(1-t)+L*t,l=l*(1-t)+k*t,{x:g,y,prevX:f,prevY:h,dx:E,dy:R,dragDist:P,velX:L,velY:k,smoothVelX:i,smoothVelY:l,smoothSpeed:Math.sqrt(i*i+l*l),teleport:C,moving:!C&&P>r}}}}function lA(e={}){let t=e.warmupFrames??0,s=!1,r=0,o=0,a=()=>o>=t;return{markActive(){s=!0,r=0},tickFrame(i){o++,r+=i},get neverActive(){return!s},get warmedUp(){return a()},shouldSkip(i,l=!1){return l||!a()?!1:s?r>i:!0}}}function jh(e,t,s=.05){return Math.log(e)/Math.max(t,s)}function cA(e,t){let s=t.scale??1,r=Math.min(t.maxSteps,Math.max(1,Math.ceil(t.dragDist/t.stepSize)));for(let o=0;o<r;o++){let a=(o+.5)/r,i=(t.fromX+t.dx*a)*s,l=(t.fromY+t.dy*a)*s,c=t.prepare?t.prepare(a):void 0;e.push(()=>t.write(i,l,a,c)),e.push(t.pass)}return r}var dP=3,jb=Math.exp(-(dP*dP)),uP=1/(1-jb),Kb=.004,bB=\"rgba16float\";function zae(e){return w.bindGroupLayout({readBuf:{storage:Fe(n,e),access:\"readonly\"},writeBuf:{storage:Fe(n,e),access:\"mutable\"},params:{uniform:(globalThis.__TYPEGPU_AUTONAME__??(t=>t))(gt({cursorX:n,cursorY:n,cursorSpeed:n,dt:n,damping:n,radius:n,aspect:n}),\"uniform\")}})}function Rae(e){return w.bindGroupLayout({srcBuf:{storage:Fe(n,e),access:\"readonly\"},dispTex:{storageTexture:zt(bB,\"write-only\")}})}var pP=new Map;function Dae(e){let t=pP.get(e);if(t)return t;let s=e*e,r=e-1,o=zae(s),a=Rae(s),i={resolution:e,propagateLayout:o,gradientLayout:a,propagateKernel:w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";if(c>M(0)&&c<M(r)&&d>M(0)&&d<M(r)){let u=o.$.params,p=M(e),_=__tsover_add(__tsover_mul(d,p),c),f=o.$.readBuf[__tsover_sub(_,M(1))],h=o.$.readBuf[__tsover_add(_,M(1))],g=o.$.readBuf[__tsover_sub(_,p)],y=o.$.readBuf[__tsover_add(_,p)],b=__tsover_mul(__tsover_add(__tsover_add(__tsover_add(f,h),g),y),.5),x=o.$.writeBuf[_],P=__tsover_sub(1,__tsover_mul(u.damping,Kb)),C=__tsover_mul(__tsover_sub(b,x),P),E=__tsover_div(__tsover_add(n(c),.5),n(e)),R=__tsover_div(__tsover_add(n(d),.5),n(e)),U=N(__tsover_sub(E,u.cursorX),__tsover_mul(__tsover_sub(E,u.cursorX),u.aspect),u.aspect>=1),L=N(__tsover_div(__tsover_sub(R,u.cursorY),u.aspect),__tsover_sub(R,u.cursorY),u.aspect>=1),k=__tsover_add(__tsover_mul(U,U),__tsover_mul(L,L)),D=__tsover_mul(u.radius,3);if(k<__tsover_mul(D,D)&&u.cursorSpeed>.01){let O=__tsover_mul(__tsover_sub(Tt(__tsover_mul(__tsover_div(k,__tsover_mul(u.radius,u.radius)),-1)),jb),uP);C=__tsover_sub(C,__tsover_mul(__tsover_mul(__tsover_mul(O,u.cursorSpeed),u.dt),3))}o.$.writeBuf[_]=C}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[11,[3,[3,[3,[1,\"cx\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],\"&&\",[1,\"cx\",\"<\",[6,\"d.u32\",[\"gridMax\"]]]],\"&&\",[1,\"cy\",\">\",[6,\"d.u32\",[[5,\"0\"]]]]],\"&&\",[1,\"cy\",\"<\",[6,\"d.u32\",[\"gridMax\"]]]],[0,[[13,\"p\",[7,[7,\"propagateLayout\",\"$\"],\"params\"]],[13,\"gs\",[6,\"d.u32\",[\"resolution\"]]],[13,\"idx\",[1,[1,\"cy\",\"*\",\"gs\"],\"+\",\"cx\"]],[13,\"left\",[8,[7,[7,\"propagateLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"right\",[8,[7,[7,\"propagateLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"up\",[8,[7,[7,\"propagateLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"-\",\"gs\"]]],[13,\"down\",[8,[7,[7,\"propagateLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"+\",\"gs\"]]],[13,\"avg\",[1,[1,[1,[1,\"left\",\"+\",\"right\"],\"+\",\"up\"],\"+\",\"down\"],\"*\",[5,\"0.5\"]]],[13,\"prev\",[8,[7,[7,\"propagateLayout\",\"$\"],\"writeBuf\"],\"idx\"]],[13,\"dampFactor\",[1,[5,\"1\"],\"-\",[1,[7,\"p\",\"damping\"],\"*\",\"DAMPING_SCALE\"]]],[12,\"next\",[1,[1,\"avg\",\"-\",\"prev\"],\"*\",\"dampFactor\"]],[13,\"cellX\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"resolution\"]]]],[13,\"cellY\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"resolution\"]]]],[13,\"ddx\",[6,\"std.select\",[[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],[1,[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],\"*\",[7,\"p\",\"aspect\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"ddy\",[6,\"std.select\",[[1,[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],\"/\",[7,\"p\",\"aspect\"]],[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"distSq\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]],[13,\"influenceRadius\",[1,[7,\"p\",\"radius\"],\"*\",[5,\"3\"]]],[11,[3,[1,\"distSq\",\"<\",[1,\"influenceRadius\",\"*\",\"influenceRadius\"]],\"&&\",[1,[7,\"p\",\"cursorSpeed\"],\">\",[5,\"0.01\"]]],[0,[[13,\"influence\",[1,[1,[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]]],\"*\",[4,\"-\",[5,\"1\"]]]]],\"-\",\"BRUSH_EDGE\"],\"*\",\"BRUSH_EDGE_GAIN\"]],[2,\"next\",\"=\",[1,\"next\",\"-\",[1,[1,[1,\"influence\",\"*\",[7,\"p\",\"cursorSpeed\"]],\"*\",[7,\"p\",\"dt\"]],\"*\",[5,\"3\"]]]]]]],[2,[8,[7,[7,\"propagateLayout\",\"$\"],\"writeBuf\"],\"idx\"],\"=\",\"next\"]]]]]]},externals:{\"d.u32\":()=>M,gridMax:()=>r,propagateLayout:()=>o,resolution:()=>e,DAMPING_SCALE:()=>Kb,\"d.f32\":()=>n,\"std.select\":()=>N,\"std.exp\":()=>Tt,BRUSH_EDGE:()=>jb,BRUSH_EDGE_GAIN:()=>uP}})&&l.f)({})).$name(`waveFieldPropagate_${e}`),gradientKernel:w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";if(c>M(0)&&c<M(r)&&d>M(0)&&d<M(r)){let u=M(e),p=__tsover_add(__tsover_mul(d,u),c),_=a.$.srcBuf[__tsover_add(p,M(1))],f=a.$.srcBuf[__tsover_sub(p,M(1))],h=a.$.srcBuf[__tsover_add(p,u)],g=a.$.srcBuf[__tsover_sub(p,u)],y=__tsover_mul(__tsover_sub(_,f),.5),b=__tsover_mul(__tsover_sub(h,g),.5);lt(a.$.dispTex,fe(c,d),S(y,b,0,1))}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[11,[3,[3,[3,[1,\"cx\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],\"&&\",[1,\"cx\",\"<\",[6,\"d.u32\",[\"gridMax\"]]]],\"&&\",[1,\"cy\",\">\",[6,\"d.u32\",[[5,\"0\"]]]]],\"&&\",[1,\"cy\",\"<\",[6,\"d.u32\",[\"gridMax\"]]]],[0,[[13,\"gs\",[6,\"d.u32\",[\"resolution\"]]],[13,\"idx\",[1,[1,\"cy\",\"*\",\"gs\"],\"+\",\"cx\"]],[13,\"right\",[8,[7,[7,\"gradientLayout\",\"$\"],\"srcBuf\"],[1,\"idx\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"left\",[8,[7,[7,\"gradientLayout\",\"$\"],\"srcBuf\"],[1,\"idx\",\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"down\",[8,[7,[7,\"gradientLayout\",\"$\"],\"srcBuf\"],[1,\"idx\",\"+\",\"gs\"]]],[13,\"up\",[8,[7,[7,\"gradientLayout\",\"$\"],\"srcBuf\"],[1,\"idx\",\"-\",\"gs\"]]],[13,\"dx\",[1,[1,\"right\",\"-\",\"left\"],\"*\",[5,\"0.5\"]]],[13,\"dy\",[1,[1,\"down\",\"-\",\"up\"],\"*\",[5,\"0.5\"]]],[6,\"std.textureStore\",[[7,[7,\"gradientLayout\",\"$\"],\"dispTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"dx\",\"dy\",[5,\"0\"],[5,\"1\"]]]]]]]]]]},externals:{\"d.u32\":()=>M,gridMax:()=>r,resolution:()=>e,gradientLayout:()=>a,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&l.f)({})).$name(`waveFieldGradient_${e}`)};return pP.set(e,i),i}function Lae(e,t){let{gpu:s,registerComputeTexture:r,getCpuValue:o,onCleanup:a,onResize:i,dimensions:l}=e,c=s?.root;if(!c)return null;let{resolution:d,dampingProp:u,radiusProp:p,radiusScale:_,speedMax:f,teleportGuard:h=\"on\"}=t,{propagateLayout:g,gradientLayout:y,propagateKernel:b,gradientKernel:x}=Dae(d),P=d*d,C=pr(c,n,P),E=pr(c,n,P),R=(globalThis.__TYPEGPU_AUTONAME__??(J=>J))(c.createTexture({size:[d,d],format:bB}).$usage(\"storage\",\"sampled\"),\"dispTex\");a(()=>R.destroy());let U=r(R),L=(globalThis.__TYPEGPU_AUTONAME__??(J=>J))(c.createUniform(gt({cursorX:n,cursorY:n,cursorSpeed:n,dt:n,damping:n,radius:n,aspect:n})),\"propParams\"),k=Math.max(1,Math.round(l.width)),D=Math.max(1,Math.round(l.height));i(({width:J,height:oe})=>{k=Math.max(1,Math.round(J)),D=Math.max(1,Math.round(oe))});let O=[d,d],F=ap(C,E),q=F.groups((J,oe)=>c.createBindGroup(g,{readBuf:J,writeBuf:oe,params:L.buffer})),Y=F.perSide(J=>c.createBindGroup(y,{srcBuf:J,dispTex:R})),V=wr(c,(J=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(J.f=((oe,le)=>{\"use gpu\";b(oe,le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"propagateKernel\",[\"cx\",\"cy\"]]]]},externals:{propagateKernel:()=>b}})&&J.f)({}),{size:O}),X=wr(c,(J=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(J.f=((oe,le)=>{\"use gpu\";x(oe,le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"gradientKernel\",[\"cx\",\"cy\"]]]]},externals:{gradientKernel:()=>x}})&&J.f)({}),{size:O}),Q=Sl(h===\"off\"?{teleportGuard:Number.POSITIVE_INFINITY}:{}),te=Date.now();return{displacement:U,getComputeNodes:J=>{let oe=J,le=Date.now(),se=Math.min(oe.deltaTime??0,.016),Te=Math.max(1,oe.dimensions?.width??k)/Math.max(1,oe.dimensions?.height??D),de=o(u)??10,xe=(o(p)??.5)*_,Ae=Q.update(oe.pointer,se),De=Math.min(Math.sqrt(Ae.velX*Ae.velX+Ae.velY*Ae.velY),f);De>.01&&(te=le);let Ee=1-de*Kb,ze=Ee>=1?1/0:Math.min(3e4,Math.log(1e-6)/Math.log(Math.max(Ee,.001))*16.67);if(le-te>ze)return null;L.write({cursorX:Ae.x,cursorY:Ae.y,cursorSpeed:De,dt:se,damping:de,radius:xe,aspect:Te});let Ie=[V.with(q()),X.with(Y.written())];return F.swap(),Ie}}}var Uae=zk;function Oae(e,t,s={}){let{uniforms:r,propValues:o}=e,a=o.colorSpace??0;return(o.stopCount??0)>1?En(t,{colorsArray:r.colorsArray,positionsArray:r.positionsArray,convertedColorsArray:r.convertedColorsArray,stopCount:r.stopCount},a):W(No[a]??Ar,\"mixColors\",[r[s.colorA??\"colorA\"],r[s.colorB??\"colorB\"],t])}var Gae=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(__tsover_mul(t.x,s),t.y),i=__tsover_mul(r,4);return a.mul(i).add(o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"freq\",[1,\"scale\",\"*\",[5,\"4\"]]],[10,[6,[7,[6,[7,\"aspectUV\",\"mul\"],[\"freq\"]],\"add\"],[\"seed\"]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Fae=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Si(s),a=m(o.y,o.z).mul(r).mul(.012);return t.add(a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"pos\"},{type:\"i\",name:\"distortion\"}],body:[0,[[13,\"grad\",[6,\"perlin12d\",[\"pos\"]]],[13,\"disp\",[6,[7,[6,[7,[6,\"d.vec2f\",[[7,\"grad\",\"y\"],[7,\"grad\",\"z\"]]],\"mul\"],[\"distortion\"]],\"mul\"],[[5,\"0.012\"]]]],[10,[6,[7,\"uv\",\"add\"],[\"disp\"]]]]]},externals:{perlin12d:()=>Si,\"d.vec2f\":()=>m}})&&e.f)({})),Iae=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_add(__tsover_mul(__tsover_sub(t,.5),__tsover_add(s,1)),.5);return G(__tsover_add(1,__tsover_mul(__tsover_sub(o,.5),__tsover_mul(r,1.1))),0,1.6)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"contrast\"},{type:\"i\",name:\"intensity\"}],body:[0,[[13,\"hAdj\",[1,[1,[1,\"h\",\"-\",[5,\"0.5\"]],\"*\",[1,\"contrast\",\"+\",[5,\"1\"]]],\"+\",[5,\"0.5\"]]],[10,[6,\"std.clamp\",[[1,[5,\"1\"],\"+\",[1,[1,\"hAdj\",\"-\",[5,\"0.5\"]],\"*\",[1,\"intensity\",\"*\",[5,\"1.1\"]]]],[5,\"0\"],[5,\"1.6\"]]]]]]},externals:{\"std.clamp\":()=>G}})&&e.f)({}));function Yae(e,t,s){let{childNode:r,uniforms:o,ctx:a,convertToTexture:i}=e;if(!r)return xt;let l=i(r),c=W(Gae,\"reliefPos\",[a.uv,a.aspect,o.scale,o.seed]),d=W(Fae,\"reliefDisplacedUV\",[a.uv,c,o.distortion]),u=W(Yt,\"unpremultiplyAlpha\",[l.sample(d)]),p=W(Iae,\"reliefBrightness\",[W(t,s,[c]),o.contrast,o.intensity]);return Ct(u.member(\"rgb\").mul(p),u.member(\"a\"))}var Wae=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=N(t,__tsover_sub(1,t),o>0),i=B(r,1e-4),l=__tsover_sub(__tsover_mul(s,__tsover_add(1,__tsover_mul(2,i))),i);return be(__tsover_sub(l,i),__tsover_add(l,i),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"coord\"},{type:\"i\",name:\"progress\"},{type:\"i\",name:\"feather\"},{type:\"i\",name:\"invert\"}],body:[0,[[13,\"c\",[6,\"std.select\",[\"coord\",[1,[5,\"1\"],\"-\",\"coord\"],[1,\"invert\",\">\",[5,\"0\"]]]]],[13,\"f\",[6,\"std.max\",[\"feather\",[5,\"0.0001\"]]]],[13,\"front\",[1,[1,\"progress\",\"*\",[1,[5,\"1\"],\"+\",[1,[5,\"2\"],\"*\",\"f\"]]],\"-\",\"f\"]],[10,[6,\"std.smoothstep\",[[1,\"front\",\"-\",\"f\"],[1,\"front\",\"+\",\"f\"],\"c\"]]]]]},externals:{\"std.select\":()=>N,\"std.max\":()=>B,\"std.smoothstep\":()=>be}})&&e.f)({})),qae=w.fn([S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return S(t.x,t.y,t.z,__tsover_mul(t.w,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"reveal\"}],body:[0,[[10,[6,\"d.vec4f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"],[1,[7,\"color\",\"w\"],\"*\",\"reveal\"]]]]]]},externals:{\"d.vec4f\":()=>S}})&&e.f)({})),Vae=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(r,bt),a=m(pe(o),ae(o)),i=Se(m(__tsover_mul(__tsover_sub(t.x,.5),s),__tsover_sub(t.y,.5)),a),l=__tsover_mul(.5,__tsover_add(__tsover_mul(s,re(a.x)),re(a.y)));return __tsover_add(__tsover_div(i,__tsover_mul(2,l)),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"angleDeg\"}],body:[0,[[13,\"a\",[1,\"angleDeg\",\"*\",\"DEG_TO_RAD\"]],[13,\"dir\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"a\"]],[6,\"std.sin\",[\"a\"]]]]],[13,\"p\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]],[13,\"grad\",[6,\"std.dot\",[\"p\",\"dir\"]]],[13,\"ext\",[1,[5,\"0.5\"],\"*\",[1,[1,\"aspect\",\"*\",[6,\"std.abs\",[[7,\"dir\",\"x\"]]]],\"+\",[6,\"std.abs\",[[7,\"dir\",\"y\"]]]]]],[10,[1,[1,\"grad\",\"/\",[1,[5,\"2\"],\"*\",\"ext\"]],\"+\",[5,\"0.5\"]]]]]},externals:{DEG_TO_RAD:()=>bt,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.abs\":()=>re}})&&e.f)({})),Nae=w.fn([m,n,m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(r.x,s),a=__tsover_sub(1,r.y),i=he(m(__tsover_sub(__tsover_mul(t.x,s),o),__tsover_sub(t.y,a))),l=he(m(B(o,__tsover_sub(s,o)),B(a,__tsover_sub(1,a))));return __tsover_div(i,B(l,1e-4))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"}],body:[0,[[13,\"cx\",[1,[7,\"center\",\"x\"],\"*\",\"aspect\"]],[13,\"cy\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"rel\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"-\",\"cx\"],[1,[7,\"uv\",\"y\"],\"-\",\"cy\"]]]],[13,\"dist\",[6,\"std.length\",[\"rel\"]]],[13,\"dx\",[6,\"std.max\",[\"cx\",[1,\"aspect\",\"-\",\"cx\"]]]],[13,\"dy\",[6,\"std.max\",[\"cy\",[1,[5,\"1\"],\"-\",\"cy\"]]]],[13,\"maxDist\",[6,\"std.length\",[[6,\"d.vec2f\",[\"dx\",\"dy\"]]]]],[10,[1,\"dist\",\"/\",[6,\"std.max\",[\"maxDist\",[5,\"0.0001\"]]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.max\":()=>B}})&&e.f)({})),$ae=w.fn([m,n,m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m(__tsover_mul(__tsover_sub(t.x,r.x),s),__tsover_sub(t.y,__tsover_sub(1,r.y))),l=Xt(i.y,i.x),c=__tsover_mul(o,bt),d=__tsover_div(__tsover_sub(l,c),Po);d=__tsover_sub(d,ie(d));let u=__tsover_sub(1,d),p=__tsover_mul(ye(d,__tsover_sub(1,d)),2),_=N(d,u,a>.5);return _=N(_,p,a>1.5),_}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"startDeg\"},{type:\"i\",name:\"mode\"}],body:[0,[[13,\"rel\",[6,\"d.vec2f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",[7,\"center\",\"x\"]],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]]],[13,\"ang\",[6,\"std.atan2\",[[7,\"rel\",\"y\"],[7,\"rel\",\"x\"]]]],[13,\"start\",[1,\"startDeg\",\"*\",\"DEG_TO_RAD\"]],[12,\"frac\",[1,[1,\"ang\",\"-\",\"start\"],\"/\",\"TWO_PI\"]],[2,\"frac\",\"=\",[1,\"frac\",\"-\",[6,\"std.floor\",[\"frac\"]]]],[13,\"ccwCoord\",[1,[5,\"1\"],\"-\",\"frac\"]],[13,\"bothCoord\",[1,[6,\"std.min\",[\"frac\",[1,[5,\"1\"],\"-\",\"frac\"]]],\"*\",[5,\"2\"]]],[12,\"coord\",[6,\"std.select\",[\"frac\",\"ccwCoord\",[1,\"mode\",\">\",[5,\"0.5\"]]]]],[2,\"coord\",\"=\",[6,\"std.select\",[\"coord\",\"bothCoord\",[1,\"mode\",\">\",[5,\"1.5\"]]]]],[10,\"coord\"]]]},externals:{\"d.vec2f\":()=>m,\"std.atan2\":()=>Xt,DEG_TO_RAD:()=>bt,TWO_PI:()=>Po,\"std.floor\":()=>ie,\"std.min\":()=>ye,\"std.select\":()=>N}})&&e.f)({})),F0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cell:m,local:m,grid:m}),\"CellGrid\"),Nu=w.fn([m,n,n],F0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(1,B(r,.001)),a=B(__tsover_div(o,s),1),i=__tsover_mul(t.x,o),l=__tsover_mul(t.y,a);return F0({cell:m(ie(i),ie(l)),local:m(Re(i),Re(l)),grid:m(o,a)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"size\"}],body:[0,[[13,\"gridX\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[\"size\",[5,\"0.001\"]]]]],[13,\"gridY\",[6,\"std.max\",[[1,\"gridX\",\"/\",\"aspect\"],[5,\"1\"]]]],[13,\"sx\",[1,[7,\"uv\",\"x\"],\"*\",\"gridX\"]],[13,\"sy\",[1,[7,\"uv\",\"y\"],\"*\",\"gridY\"]],[10,[6,\"CellGrid\",[[104,{cell:[6,\"d.vec2f\",[[6,\"std.floor\",[\"sx\"]],[6,\"std.floor\",[\"sy\"]]]],local:[6,\"d.vec2f\",[[6,\"std.fract\",[\"sx\"]],[6,\"std.fract\",[\"sy\"]]]],grid:[6,\"d.vec2f\",[\"gridX\",\"gridY\"]]}]]]]]]},externals:{\"std.max\":()=>B,CellGrid:()=>F0,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"std.fract\":()=>Re}})&&e.f)({})),Xae=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_mul(re(__tsover_sub(t,.5)),2)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"}],body:[0,[[10,[1,[6,\"std.abs\",[[1,\"t\",\"-\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]]]]},externals:{\"std.abs\":()=>re}})&&e.f)({})),Hae=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Re(__tsover_mul(t,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"count\"}],body:[0,[[10,[6,\"std.fract\",[[1,\"t\",\"*\",\"count\"]]]]]]},externals:{\"std.fract\":()=>Re}})&&e.f)({})),jae=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=B(s,1);return G(__tsover_div(__tsover_add(ie(__tsover_mul(t,r)),.5),r),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"count\"}],body:[0,[[13,\"n\",[6,\"std.max\",[\"count\",[5,\"1\"]]]],[10,[6,\"std.clamp\",[[1,[1,[6,\"std.floor\",[[1,\"t\",\"*\",\"n\"]]],\"+\",[5,\"0.5\"]],\"/\",\"n\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.floor\":()=>ie}})&&e.f)({})),Kae=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=ie(__tsover_mul(t,B(s,1)));return Xs(__tsover_add(r,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"count\"}],body:[0,[[13,\"idx\",[6,\"std.floor\",[[1,\"t\",\"*\",[6,\"std.max\",[\"count\",[5,\"1\"]]]]]]],[10,[6,\"hash11\",[[1,\"idx\",\"+\",[5,\"0.5\"]]]]]]]},externals:{\"std.floor\":()=>ie,\"std.max\":()=>B,hash11:()=>Xs}})&&e.f)({})),Qae=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Nu(t,s,r);return __tsover_add(re(__tsover_sub(o.local.x,.5)),re(__tsover_sub(o.local.y,.5)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"size\"}],body:[0,[[13,\"g\",[6,\"cellGrid\",[\"uv\",\"aspect\",\"size\"]]],[10,[1,[6,\"std.abs\",[[1,[7,[7,\"g\",\"local\"],\"x\"],\"-\",[5,\"0.5\"]]]],\"+\",[6,\"std.abs\",[[1,[7,[7,\"g\",\"local\"],\"y\"],\"-\",[5,\"0.5\"]]]]]]]]},externals:{cellGrid:()=>Nu,\"std.abs\":()=>re}})&&e.f)({})),Zae=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Nu(t,s,r),a=__tsover_mul(Re(__tsover_mul(__tsover_add(o.cell.x,o.cell.y),.5)),2),i=__tsover_mul(__tsover_add(__tsover_div(o.cell.x,o.grid.x),__tsover_div(o.cell.y,o.grid.y)),.5);return G(__tsover_add(__tsover_mul(.5,a),__tsover_mul(.5,i)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"size\"}],body:[0,[[13,\"g\",[6,\"cellGrid\",[\"uv\",\"aspect\",\"size\"]]],[13,\"parity\",[1,[6,\"std.fract\",[[1,[1,[7,[7,\"g\",\"cell\"],\"x\"],\"+\",[7,[7,\"g\",\"cell\"],\"y\"]],\"*\",[5,\"0.5\"]]]],\"*\",[5,\"2\"]]],[13,\"diag\",[1,[1,[1,[7,[7,\"g\",\"cell\"],\"x\"],\"/\",[7,[7,\"g\",\"grid\"],\"x\"]],\"+\",[1,[7,[7,\"g\",\"cell\"],\"y\"],\"/\",[7,[7,\"g\",\"grid\"],\"y\"]]],\"*\",[5,\"0.5\"]]],[10,[6,\"std.clamp\",[[1,[1,[5,\"0.5\"],\"*\",\"parity\"],\"+\",[1,[5,\"0.5\"],\"*\",\"diag\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{cellGrid:()=>Nu,\"std.fract\":()=>Re,\"std.clamp\":()=>G}})&&e.f)({})),Jae=w.fn([m,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return Ys(Nu(t,s,r).cell)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"size\"}],body:[0,[[10,[6,\"hash12\",[[7,[6,\"cellGrid\",[\"uv\",\"aspect\",\"size\"]],\"cell\"]]]]]]},externals:{hash12:()=>Ys,cellGrid:()=>Nu}})&&e.f)({})),ene=w.fn([m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(__tsover_add(__tsover_mul(__tsover_mul(t.x,s),r),__tsover_mul(o,13.7)),__tsover_add(__tsover_mul(t.y,r),__tsover_mul(o,7.9))),i=la(a),l=__tsover_mul(la(m(__tsover_add(__tsover_add(__tsover_mul(a.x,2),17.3),__tsover_mul(o,3.1)),__tsover_add(__tsover_mul(a.y,2),9.1))),.5),c=__tsover_mul(la(m(__tsover_add(__tsover_mul(a.x,4),41.7),__tsover_add(__tsover_add(__tsover_mul(a.y,4),27.9),__tsover_mul(o,5.3)))),.25),d=__tsover_div(__tsover_add(__tsover_add(i,l),c),1.75);return G(__tsover_add(.5,__tsover_mul(d,.9)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"p\",[6,\"d.vec2f\",[[1,[1,[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],\"*\",\"scale\"],\"+\",[1,\"seed\",\"*\",[5,\"13.7\"]]],[1,[1,[7,\"uv\",\"y\"],\"*\",\"scale\"],\"+\",[1,\"seed\",\"*\",[5,\"7.9\"]]]]]],[13,\"n1\",[6,\"mxNoiseFloat2\",[\"p\"]]],[13,\"n2\",[1,[6,\"mxNoiseFloat2\",[[6,\"d.vec2f\",[[1,[1,[1,[7,\"p\",\"x\"],\"*\",[5,\"2\"]],\"+\",[5,\"17.3\"]],\"+\",[1,\"seed\",\"*\",[5,\"3.1\"]]],[1,[1,[7,\"p\",\"y\"],\"*\",[5,\"2\"]],\"+\",[5,\"9.1\"]]]]]],\"*\",[5,\"0.5\"]]],[13,\"n3\",[1,[6,\"mxNoiseFloat2\",[[6,\"d.vec2f\",[[1,[1,[7,\"p\",\"x\"],\"*\",[5,\"4\"]],\"+\",[5,\"41.7\"]],[1,[1,[1,[7,\"p\",\"y\"],\"*\",[5,\"4\"]],\"+\",[5,\"27.9\"]],\"+\",[1,\"seed\",\"*\",[5,\"5.3\"]]]]]]],\"*\",[5,\"0.25\"]]],[13,\"n\",[1,[1,[1,\"n1\",\"+\",\"n2\"],\"+\",\"n3\"],\"/\",[5,\"1.75\"]]],[10,[6,\"std.clamp\",[[1,[5,\"0.5\"],\"+\",[1,\"n\",\"*\",[5,\"0.9\"]]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"d.vec2f\":()=>m,mxNoiseFloat2:()=>la,\"std.clamp\":()=>G}})&&e.f)({})),xB=new Float32Array(1),tne=new Int32Array(xB.buffer);function _o(e){xB[0]=Math.max(-65504,Math.min(65504,e));let t=tne[0],s=t>>16&32768,r=t>>12&2047,o=t>>23&255;return o<103?s:o>142?(s|=31744,s|=(o===255?0:1)&&t&8388607,s):o<113?(r|=2048,s|=(r>>114-o)+(r>>113-o&1),s):(s|=o-112<<10|r>>1,s+=r&1,s)}var yc=ko({ANALYTIC_SDF_EXTRA_FIELDS:()=>sne,APERTURE_TO_HALFANGLE:()=>CB,SVG_SDF_SIZE:()=>512,ShapeMask:()=>G1,analyticSubPropValues:()=>Hv,arcSdf:()=>Nv,buildAnalyticSdfFn:()=>P_,circleSdf:()=>Dm,createAnalyticSdfSampler:()=>MB,createSdfDataTexture:()=>uA,createSvgSdfSampler:()=>SB,createSvgSdfSamplerWithGradients:()=>PB,crescentSdf:()=>Wv,crossSdf:()=>Gv,driveAnalyticSubProps:()=>EB,ellipseSdf:()=>Iv,fillStrokeColor:()=>TB,flowerSdf:()=>Lv,heartSdf:()=>Vv,loadSdfFromUrl:()=>dA,parallelogramSdf:()=>Xv,parseShapeConfigValue:()=>jv,polygonSdf:()=>Dv,ringSdf:()=>Ov,roundedRectSdf:()=>Fv,shapeLocalCoords:()=>AB,starSdf:()=>Uv,strokeMaskFromSdf:()=>wB,teardropSdf:()=>$v,trapezoidSdf:()=>qv,vesicaSdf:()=>Yv}),mP=3.141592653589793,$u=6.283185307179586,_P=.35355339059327373,Rv=.017453292519943295,Dm=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_sub(Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s))),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"}],body:[0,[[10,[1,[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]],\"-\",\"radius\"]]]]},externals:{\"std.sqrt\":()=>Ue}})&&e.f)({})),Dv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s))),i=Xt(s,t),l=__tsover_div($u,o),c=ie(__tsover_add(__tsover_div(i,l),.5)),d=__tsover_sub(i,__tsover_mul(c,l));return __tsover_sub(__tsover_mul(a,pe(d)),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"sides\"}],body:[0,[[13,\"len\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[13,\"angle\",[6,\"std.atan2\",[\"dy\",\"dx\"]]],[13,\"sectorAngle\",[1,\"TWO_PI\",\"/\",\"sides\"]],[13,\"sectorIdx\",[6,\"std.floor\",[[1,[1,\"angle\",\"/\",\"sectorAngle\"],\"+\",[5,\"0.5\"]]]]],[13,\"canonicalAngle\",[1,\"angle\",\"-\",[1,\"sectorIdx\",\"*\",\"sectorAngle\"]]],[10,[1,[1,\"len\",\"*\",[6,\"std.cos\",[\"canonicalAngle\"]]],\"-\",\"radius\"]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.atan2\":()=>Xt,TWO_PI:()=>$u,\"std.floor\":()=>ie,\"std.cos\":()=>pe}})&&e.f)({})),Lv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Xt(s,t),l=Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s))),c=__tsover_mul(r,a),d=__tsover_div(__tsover_mul(i,o),$u),u=__tsover_sub(d,ie(d)),p=re(__tsover_sub(__tsover_mul(u,2),1)),_=__tsover_add(c,__tsover_mul(__tsover_sub(r,c),p));return __tsover_sub(l,_)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"outerRadius\"},{type:\"i\",name:\"sides\"},{type:\"i\",name:\"innerRatio\"}],body:[0,[[13,\"angle\",[6,\"std.atan2\",[\"dy\",\"dx\"]]],[13,\"len\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[13,\"innerRadius\",[1,\"outerRadius\",\"*\",\"innerRatio\"]],[13,\"tAngle\",[1,[1,\"angle\",\"*\",\"sides\"],\"/\",\"TWO_PI\"]],[13,\"tFrac\",[1,\"tAngle\",\"-\",[6,\"std.floor\",[\"tAngle\"]]]],[13,\"t\",[6,\"std.abs\",[[1,[1,\"tFrac\",\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]],[13,\"boundaryR\",[1,\"innerRadius\",\"+\",[1,[1,\"outerRadius\",\"-\",\"innerRadius\"],\"*\",\"t\"]]],[10,[1,\"len\",\"-\",\"boundaryR\"]]]]},externals:{\"std.atan2\":()=>Xt,\"std.sqrt\":()=>Ue,TWO_PI:()=>$u,\"std.floor\":()=>ie,\"std.abs\":()=>re}})&&e.f)({})),Uv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_mul(r,a),l=Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s))),c=Xt(s,t),d=__tsover_div($u,o),u=ie(__tsover_add(__tsover_div(c,d),.5)),p=__tsover_sub(c,__tsover_mul(u,d)),_=re(__tsover_mul(l,ae(p))),f=__tsover_mul(l,pe(p)),h=__tsover_div(mP,o),g=ae(h),y=pe(h),b=__tsover_mul(i,g),x=__tsover_sub(__tsover_mul(i,y),r),P=_,C=__tsover_sub(f,r),E=__tsover_add(__tsover_mul(P,b),__tsover_mul(C,x)),R=__tsover_add(__tsover_mul(b,b),__tsover_mul(x,x)),U=G(__tsover_div(E,R),0,1),L=__tsover_sub(_,__tsover_mul(b,U)),k=__tsover_sub(f,__tsover_add(r,__tsover_mul(x,U))),D=Ue(__tsover_add(__tsover_mul(L,L),__tsover_mul(k,k))),O=__tsover_sub(__tsover_mul(b,C),__tsover_mul(x,P));return __tsover_mul(D,no(O))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"outerRadius\"},{type:\"i\",name:\"sides\"},{type:\"i\",name:\"innerRatio\"}],body:[0,[[13,\"innerRadius\",[1,\"outerRadius\",\"*\",\"innerRatio\"]],[13,\"len\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[13,\"angle\",[6,\"std.atan2\",[\"dy\",\"dx\"]]],[13,\"sectorAngle\",[1,\"TWO_PI\",\"/\",\"sides\"]],[13,\"sectorIdx\",[6,\"std.floor\",[[1,[1,\"angle\",\"/\",\"sectorAngle\"],\"+\",[5,\"0.5\"]]]]],[13,\"bn\",[1,\"angle\",\"-\",[1,\"sectorIdx\",\"*\",\"sectorAngle\"]]],[13,\"fpx\",[6,\"std.abs\",[[1,\"len\",\"*\",[6,\"std.sin\",[\"bn\"]]]]]],[13,\"fpy\",[1,\"len\",\"*\",[6,\"std.cos\",[\"bn\"]]]],[13,\"an\",[1,\"PI\",\"/\",\"sides\"]],[13,\"sinAn\",[6,\"std.sin\",[\"an\"]]],[13,\"cosAn\",[6,\"std.cos\",[\"an\"]]],[13,\"ex\",[1,\"innerRadius\",\"*\",\"sinAn\"]],[13,\"ey\",[1,[1,\"innerRadius\",\"*\",\"cosAn\"],\"-\",\"outerRadius\"]],[13,\"qx\",\"fpx\"],[13,\"qy\",[1,\"fpy\",\"-\",\"outerRadius\"]],[13,\"dotQE\",[1,[1,\"qx\",\"*\",\"ex\"],\"+\",[1,\"qy\",\"*\",\"ey\"]]],[13,\"dotEE\",[1,[1,\"ex\",\"*\",\"ex\"],\"+\",[1,\"ey\",\"*\",\"ey\"]]],[13,\"t\",[6,\"std.clamp\",[[1,\"dotQE\",\"/\",\"dotEE\"],[5,\"0\"],[5,\"1\"]]]],[13,\"nx\",[1,\"fpx\",\"-\",[1,\"ex\",\"*\",\"t\"]]],[13,\"ny\",[1,\"fpy\",\"-\",[1,\"outerRadius\",\"+\",[1,\"ey\",\"*\",\"t\"]]]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"nx\",\"*\",\"nx\"],\"+\",[1,\"ny\",\"*\",\"ny\"]]]]],[13,\"cross\",[1,[1,\"ex\",\"*\",\"qy\"],\"-\",[1,\"ey\",\"*\",\"qx\"]]],[10,[1,\"dist\",\"*\",[6,\"std.sign\",[\"cross\"]]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.atan2\":()=>Xt,TWO_PI:()=>$u,\"std.floor\":()=>ie,\"std.abs\":()=>re,\"std.sin\":()=>ae,\"std.cos\":()=>pe,PI:()=>mP,\"std.clamp\":()=>G,\"std.sign\":()=>no}})&&e.f)({})),Ov=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s)));return __tsover_sub(re(__tsover_sub(a,r)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"thickness\"}],body:[0,[[13,\"len\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]]],[10,[1,[6,\"std.abs\",[[1,\"len\",\"-\",\"radius\"]]],\"-\",\"thickness\"]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.abs\":()=>re}})&&e.f)({})),Gv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=re(t),l=re(s),c=B(__tsover_sub(i,r),__tsover_sub(l,o)),d=B(__tsover_sub(l,r),__tsover_sub(i,o));return __tsover_sub(ye(c,d),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"size\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"rounding\"}],body:[0,[[13,\"px\",[6,\"std.abs\",[\"dx\"]]],[13,\"py\",[6,\"std.abs\",[\"dy\"]]],[13,\"dHoriz\",[6,\"std.max\",[[1,\"px\",\"-\",\"size\"],[1,\"py\",\"-\",\"thickness\"]]]],[13,\"dVert\",[6,\"std.max\",[[1,\"py\",\"-\",\"size\"],[1,\"px\",\"-\",\"thickness\"]]]],[10,[1,[6,\"std.min\",[\"dHoriz\",\"dVert\"]],\"-\",\"rounding\"]]]]},externals:{\"std.abs\":()=>re,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})),Fv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_add(__tsover_sub(re(t),r),a),l=__tsover_add(__tsover_sub(re(s),o),a),c=B(i,0),d=B(l,0),u=Ue(__tsover_add(__tsover_mul(c,c),__tsover_mul(d,d))),p=ye(B(i,l),0);return __tsover_sub(__tsover_add(u,p),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"width\"},{type:\"i\",name:\"height\"},{type:\"i\",name:\"rounding\"}],body:[0,[[13,\"qx\",[1,[1,[6,\"std.abs\",[\"dx\"]],\"-\",\"width\"],\"+\",\"rounding\"]],[13,\"qy\",[1,[1,[6,\"std.abs\",[\"dy\"]],\"-\",\"height\"],\"+\",\"rounding\"]],[13,\"mqx\",[6,\"std.max\",[\"qx\",[5,\"0\"]]]],[13,\"mqy\",[6,\"std.max\",[\"qy\",[5,\"0\"]]]],[13,\"outerDist\",[6,\"std.sqrt\",[[1,[1,\"mqx\",\"*\",\"mqx\"],\"+\",[1,\"mqy\",\"*\",\"mqy\"]]]]],[13,\"innerDist\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",\"qy\"]],[5,\"0\"]]]],[10,[1,[1,\"outerDist\",\"+\",\"innerDist\"],\"-\",\"rounding\"]]]]},externals:{\"std.abs\":()=>re,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.min\":()=>ye}})&&e.f)({})),Iv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_div(t,r),i=__tsover_div(s,o),l=Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(i,i)));return __tsover_mul(__tsover_sub(l,1),ye(r,o))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radiusX\"},{type:\"i\",name:\"radiusY\"}],body:[0,[[13,\"nx\",[1,\"dx\",\"/\",\"radiusX\"]],[13,\"ny\",[1,\"dy\",\"/\",\"radiusY\"]],[13,\"k\",[6,\"std.sqrt\",[[1,[1,\"nx\",\"*\",\"nx\"],\"+\",[1,\"ny\",\"*\",\"ny\"]]]]],[10,[1,[1,\"k\",\"-\",[5,\"1\"]],\"*\",[6,\"std.min\",[\"radiusX\",\"radiusY\"]]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.min\":()=>ye}})&&e.f)({})),Yv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=re(t),i=re(s),l=__tsover_mul(r,o),c=Ue(B(__tsover_sub(__tsover_mul(r,r),__tsover_mul(l,l)),0)),d=__tsover_mul(__tsover_sub(i,c),l)>__tsover_mul(a,c),u=Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(__tsover_sub(i,c),__tsover_sub(i,c))));return N(__tsover_sub(Ue(__tsover_add(__tsover_mul(__tsover_add(a,l),__tsover_add(a,l)),__tsover_mul(i,i))),r),u,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"spread\"}],body:[0,[[13,\"px\",[6,\"std.abs\",[\"dx\"]]],[13,\"py\",[6,\"std.abs\",[\"dy\"]]],[13,\"dd\",[1,\"radius\",\"*\",\"spread\"]],[13,\"b\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[1,\"radius\",\"*\",\"radius\"],\"-\",[1,\"dd\",\"*\",\"dd\"]],[5,\"0\"]]]]]],[13,\"cond\",[1,[1,[1,\"py\",\"-\",\"b\"],\"*\",\"dd\"],\">\",[1,\"px\",\"*\",\"b\"]]],[13,\"dist1\",[6,\"std.sqrt\",[[1,[1,\"px\",\"*\",\"px\"],\"+\",[1,[1,\"py\",\"-\",\"b\"],\"*\",[1,\"py\",\"-\",\"b\"]]]]]],[13,\"dist2\",[1,[6,\"std.sqrt\",[[1,[1,[1,\"px\",\"+\",\"dd\"],\"*\",[1,\"px\",\"+\",\"dd\"]],\"+\",[1,\"py\",\"*\",\"py\"]]]],\"-\",\"radius\"]],[10,[6,\"std.select\",[\"dist2\",\"dist1\",\"cond\"]]]]]},externals:{\"std.abs\":()=>re,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"std.select\":()=>N}})&&e.f)({})),Wv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=re(s),l=r,c=__tsover_mul(r,o),d=a,u=__tsover_div(__tsover_add(__tsover_sub(__tsover_mul(l,l),__tsover_mul(c,c)),__tsover_mul(d,d)),B(__tsover_mul(d,2),.001)),p=Ue(B(__tsover_sub(__tsover_mul(l,l),__tsover_mul(u,u)),0)),_=__tsover_mul(d,__tsover_sub(__tsover_mul(t,p),__tsover_mul(i,u))),f=__tsover_mul(__tsover_mul(d,d),B(__tsover_sub(p,i),0)),h=__tsover_sub(t,u),g=__tsover_sub(i,p),y=Ue(__tsover_add(__tsover_mul(h,h),__tsover_mul(g,g))),b=Ue(__tsover_add(__tsover_mul(t,t),__tsover_mul(i,i))),x=__tsover_sub(t,d),P=Ue(__tsover_add(__tsover_mul(x,x),__tsover_mul(i,i)));return N(B(__tsover_sub(b,l),__tsover_mul(__tsover_sub(P,c),-1)),y,_>f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"outerRadius\"},{type:\"i\",name:\"innerRatio\"},{type:\"i\",name:\"offset\"}],body:[0,[[13,\"py\",[6,\"std.abs\",[\"dy\"]]],[13,\"ra\",\"outerRadius\"],[13,\"rb\",[1,\"outerRadius\",\"*\",\"innerRatio\"]],[13,\"off\",\"offset\"],[13,\"a\",[1,[1,[1,[1,\"ra\",\"*\",\"ra\"],\"-\",[1,\"rb\",\"*\",\"rb\"]],\"+\",[1,\"off\",\"*\",\"off\"]],\"/\",[6,\"std.max\",[[1,\"off\",\"*\",[5,\"2\"]],[5,\"0.001\"]]]]],[13,\"bSq\",[6,\"std.max\",[[1,[1,\"ra\",\"*\",\"ra\"],\"-\",[1,\"a\",\"*\",\"a\"]],[5,\"0\"]]]],[13,\"b\",[6,\"std.sqrt\",[\"bSq\"]]],[13,\"lhs\",[1,\"off\",\"*\",[1,[1,\"dx\",\"*\",\"b\"],\"-\",[1,\"py\",\"*\",\"a\"]]]],[13,\"rhs\",[1,[1,\"off\",\"*\",\"off\"],\"*\",[6,\"std.max\",[[1,\"b\",\"-\",\"py\"],[5,\"0\"]]]]],[13,\"d1x\",[1,\"dx\",\"-\",\"a\"]],[13,\"d1y\",[1,\"py\",\"-\",\"b\"]],[13,\"dist1\",[6,\"std.sqrt\",[[1,[1,\"d1x\",\"*\",\"d1x\"],\"+\",[1,\"d1y\",\"*\",\"d1y\"]]]]],[13,\"lenP\",[6,\"std.sqrt\",[[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"py\",\"*\",\"py\"]]]]],[13,\"innerX\",[1,\"dx\",\"-\",\"off\"]],[13,\"lenInner\",[6,\"std.sqrt\",[[1,[1,\"innerX\",\"*\",\"innerX\"],\"+\",[1,\"py\",\"*\",\"py\"]]]]],[13,\"dist2\",[6,\"std.max\",[[1,\"lenP\",\"-\",\"ra\"],[1,[1,\"lenInner\",\"-\",\"rb\"],\"*\",[4,\"-\",[5,\"1\"]]]]]],[10,[6,\"std.select\",[\"dist2\",\"dist1\",[1,\"lhs\",\">\",\"rhs\"]]]]]]},externals:{\"std.abs\":()=>re,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.select\":()=>N}})&&e.f)({})),qv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=re(t),l=o,c=a,d=__tsover_sub(o,r),u=__tsover_mul(a,2),p=N(o,r,s<0),_=__tsover_sub(i,ye(i,p)),f=__tsover_sub(re(s),a),h=__tsover_sub(l,i),g=__tsover_sub(c,s),y=__tsover_add(__tsover_mul(h,d),__tsover_mul(g,u)),b=B(__tsover_add(__tsover_mul(d,d),__tsover_mul(u,u)),1e-4),x=G(__tsover_div(y,b),0,1),P=__tsover_add(__tsover_sub(i,l),__tsover_mul(d,x)),C=__tsover_add(__tsover_sub(s,c),__tsover_mul(u,x)),E=N(n(1),n(-1),f<0),R=N(n(1),E,P<0),U=__tsover_add(__tsover_mul(_,_),__tsover_mul(f,f)),L=__tsover_add(__tsover_mul(P,P),__tsover_mul(C,C));return __tsover_mul(R,Ue(ye(U,L)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"r1\"},{type:\"i\",name:\"r2\"},{type:\"i\",name:\"he\"}],body:[0,[[13,\"px\",[6,\"std.abs\",[\"dx\"]]],[13,\"k1x\",\"r2\"],[13,\"k1y\",\"he\"],[13,\"k2x\",[1,\"r2\",\"-\",\"r1\"]],[13,\"k2y\",[1,\"he\",\"*\",[5,\"2\"]]],[13,\"clampWidth\",[6,\"std.select\",[\"r2\",\"r1\",[1,\"dy\",\"<\",[5,\"0\"]]]]],[13,\"cax\",[1,\"px\",\"-\",[6,\"std.min\",[\"px\",\"clampWidth\"]]]],[13,\"cay\",[1,[6,\"std.abs\",[\"dy\"]],\"-\",\"he\"]],[13,\"diffX\",[1,\"k1x\",\"-\",\"px\"]],[13,\"diffY\",[1,\"k1y\",\"-\",\"dy\"]],[13,\"dotNum\",[1,[1,\"diffX\",\"*\",\"k2x\"],\"+\",[1,\"diffY\",\"*\",\"k2y\"]]],[13,\"dotDen\",[6,\"std.max\",[[1,[1,\"k2x\",\"*\",\"k2x\"],\"+\",[1,\"k2y\",\"*\",\"k2y\"]],[5,\"0.0001\"]]]],[13,\"t\",[6,\"std.clamp\",[[1,\"dotNum\",\"/\",\"dotDen\"],[5,\"0\"],[5,\"1\"]]]],[13,\"cbx\",[1,[1,\"px\",\"-\",\"k1x\"],\"+\",[1,\"k2x\",\"*\",\"t\"]]],[13,\"cby\",[1,[1,\"dy\",\"-\",\"k1y\"],\"+\",[1,\"k2y\",\"*\",\"t\"]]],[13,\"sInner\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[1,\"cay\",\"<\",[5,\"0\"]]]]],[13,\"s\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"sInner\",[1,\"cbx\",\"<\",[5,\"0\"]]]]],[13,\"dca\",[1,[1,\"cax\",\"*\",\"cax\"],\"+\",[1,\"cay\",\"*\",\"cay\"]]],[13,\"dcb\",[1,[1,\"cbx\",\"*\",\"cbx\"],\"+\",[1,\"cby\",\"*\",\"cby\"]]],[10,[1,\"s\",\"*\",[6,\"std.sqrt\",[[6,\"std.min\",[\"dca\",\"dcb\"]]]]]]]]},externals:{\"std.abs\":()=>re,\"std.select\":()=>N,\"std.min\":()=>ye,\"std.max\":()=>B,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.sqrt\":()=>Ue}})&&e.f)({})),Vv=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_div(r,.75),a=__tsover_div(re(t),o),i=__tsover_add(__tsover_div(__tsover_mul(s,-1),o),.6),l=__tsover_sub(a,.25),c=__tsover_sub(i,.75),d=__tsover_sub(Ue(__tsover_add(__tsover_mul(l,l),__tsover_mul(c,c))),_P),u=__tsover_sub(i,1),p=__tsover_add(__tsover_mul(a,a),__tsover_mul(u,u)),_=__tsover_mul(B(__tsover_add(a,i),0),.5),f=__tsover_sub(a,_),h=__tsover_sub(i,_),g=__tsover_add(__tsover_mul(f,f),__tsover_mul(h,h)),y=__tsover_mul(Ue(ye(p,g)),no(__tsover_sub(a,i)));return __tsover_mul(N(y,d,__tsover_add(a,i)>1),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"}],body:[0,[[13,\"S\",[1,\"radius\",\"/\",[5,\"0.75\"]]],[13,\"px\",[1,[6,\"std.abs\",[\"dx\"]],\"/\",\"S\"]],[13,\"py\",[1,[1,[1,\"dy\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"S\"],\"+\",[5,\"0.6\"]]],[13,\"ax\",[1,\"px\",\"-\",[5,\"0.25\"]]],[13,\"ay\",[1,\"py\",\"-\",[5,\"0.75\"]]],[13,\"distA\",[1,[6,\"std.sqrt\",[[1,[1,\"ax\",\"*\",\"ax\"],\"+\",[1,\"ay\",\"*\",\"ay\"]]]],\"-\",\"SQRT2_OVER_4\"]],[13,\"b1y\",[1,\"py\",\"-\",[5,\"1\"]]],[13,\"dot1\",[1,[1,\"px\",\"*\",\"px\"],\"+\",[1,\"b1y\",\"*\",\"b1y\"]]],[13,\"m\",[1,[6,\"std.max\",[[1,\"px\",\"+\",\"py\"],[5,\"0\"]]],\"*\",[5,\"0.5\"]]],[13,\"b2x\",[1,\"px\",\"-\",\"m\"]],[13,\"b2y\",[1,\"py\",\"-\",\"m\"]],[13,\"dot2v\",[1,[1,\"b2x\",\"*\",\"b2x\"],\"+\",[1,\"b2y\",\"*\",\"b2y\"]]],[13,\"distB\",[1,[6,\"std.sqrt\",[[6,\"std.min\",[\"dot1\",\"dot2v\"]]]],\"*\",[6,\"std.sign\",[[1,\"px\",\"-\",\"py\"]]]]],[10,[1,[6,\"std.select\",[\"distB\",\"distA\",[1,[1,\"px\",\"+\",\"py\"],\">\",[5,\"1\"]]]],\"*\",\"S\"]]]]},externals:{\"std.abs\":()=>re,\"std.sqrt\":()=>Ue,SQRT2_OVER_4:()=>_P,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.sign\":()=>no,\"std.select\":()=>N}})&&e.f)({})),Nv=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=re(t),i=ae(o),l=pe(o),c=__tsover_sub(Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(s,s))),r),d=G(__tsover_add(__tsover_mul(a,i),__tsover_mul(s,l)),0,r),u=__tsover_sub(a,__tsover_mul(i,d)),p=__tsover_sub(s,__tsover_mul(l,d)),_=Ue(__tsover_add(__tsover_mul(u,u),__tsover_mul(p,p)));return B(c,__tsover_mul(_,no(__tsover_sub(__tsover_mul(l,a),__tsover_mul(i,s)))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"halfAngle\"}],body:[0,[[13,\"px\",[6,\"std.abs\",[\"dx\"]]],[13,\"cx\",[6,\"std.sin\",[\"halfAngle\"]]],[13,\"cy\",[6,\"std.cos\",[\"halfAngle\"]]],[13,\"l\",[1,[6,\"std.sqrt\",[[1,[1,\"px\",\"*\",\"px\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]]],\"-\",\"radius\"]],[13,\"clamped\",[6,\"std.clamp\",[[1,[1,\"px\",\"*\",\"cx\"],\"+\",[1,\"dy\",\"*\",\"cy\"]],[5,\"0\"],\"radius\"]]],[13,\"mx\",[1,\"px\",\"-\",[1,\"cx\",\"*\",\"clamped\"]]],[13,\"my\",[1,\"dy\",\"-\",[1,\"cy\",\"*\",\"clamped\"]]],[13,\"m\",[6,\"std.sqrt\",[[1,[1,\"mx\",\"*\",\"mx\"],\"+\",[1,\"my\",\"*\",\"my\"]]]]],[10,[6,\"std.max\",[\"l\",[1,\"m\",\"*\",[6,\"std.sign\",[[1,[1,\"cy\",\"*\",\"px\"],\"-\",[1,\"cx\",\"*\",\"dy\"]]]]]]]]]]},externals:{\"std.abs\":()=>re,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.sqrt\":()=>Ue,\"std.clamp\":()=>G,\"std.max\":()=>B,\"std.sign\":()=>no}})&&e.f)({})),$v=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(__tsover_sub(o,r),.5),i=re(t),l=__tsover_add(__tsover_mul(s,-1),a),c=B(o,1e-4),d=__tsover_div(r,c),u=Ue(B(__tsover_sub(1,__tsover_mul(d,d)),0)),p=__tsover_add(__tsover_mul(i,__tsover_mul(d,-1)),__tsover_mul(l,u)),_=__tsover_sub(Ue(__tsover_add(__tsover_mul(i,i),__tsover_mul(l,l))),r),f=__tsover_sub(l,o),h=Ue(__tsover_add(__tsover_mul(i,i),__tsover_mul(f,f)));return N(N(__tsover_sub(__tsover_add(__tsover_mul(i,u),__tsover_mul(l,d)),r),h,p>__tsover_mul(u,o)),_,p<0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"mid\",[1,[1,\"h\",\"-\",\"radius\"],\"*\",[5,\"0.5\"]]],[13,\"qx\",[6,\"std.abs\",[\"dx\"]]],[13,\"qy\",[1,[1,\"dy\",\"*\",[4,\"-\",[5,\"1\"]]],\"+\",\"mid\"]],[13,\"hSafe\",[6,\"std.max\",[\"h\",[5,\"0.0001\"]]]],[13,\"b\",[1,\"radius\",\"/\",\"hSafe\"]],[13,\"a\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[5,\"1\"],\"-\",[1,\"b\",\"*\",\"b\"]],[5,\"0\"]]]]]],[13,\"k\",[1,[1,\"qx\",\"*\",[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]]],\"+\",[1,\"qy\",\"*\",\"a\"]]],[13,\"cap0\",[1,[6,\"std.sqrt\",[[1,[1,\"qx\",\"*\",\"qx\"],\"+\",[1,\"qy\",\"*\",\"qy\"]]]],\"-\",\"radius\"]],[13,\"qyTip\",[1,\"qy\",\"-\",\"h\"]],[13,\"cap1\",[6,\"std.sqrt\",[[1,[1,\"qx\",\"*\",\"qx\"],\"+\",[1,\"qyTip\",\"*\",\"qyTip\"]]]]],[13,\"body\",[1,[1,[1,\"qx\",\"*\",\"a\"],\"+\",[1,\"qy\",\"*\",\"b\"]],\"-\",\"radius\"]],[10,[6,\"std.select\",[[6,\"std.select\",[\"body\",\"cap1\",[1,\"k\",\">\",[1,\"a\",\"*\",\"h\"]]]],\"cap0\",[1,\"k\",\"<\",[5,\"0\"]]]]]]]},externals:{\"std.abs\":()=>re,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.select\":()=>N}})&&e.f)({})),Xv=w.fn([n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=a,l=o,c=s<0,d=N(t,__tsover_mul(t,-1),c),u=N(s,__tsover_mul(s,-1),c),p=__tsover_sub(d,i),_=__tsover_sub(u,l),f=__tsover_sub(p,G(p,__tsover_mul(r,-1),r)),h=__tsover_add(__tsover_mul(f,f),__tsover_mul(_,_)),g=__tsover_mul(_,-1),y=__tsover_sub(__tsover_mul(d,l),__tsover_mul(u,i)),b=y<0,x=N(d,__tsover_mul(d,-1),b),P=N(u,__tsover_mul(u,-1),b),C=__tsover_sub(x,r),E=__tsover_add(__tsover_mul(C,i),__tsover_mul(P,l)),R=B(__tsover_add(__tsover_mul(i,i),__tsover_mul(l,l)),1e-4),U=G(__tsover_div(E,R),-1,1),L=__tsover_sub(C,__tsover_mul(i,U)),k=__tsover_sub(P,__tsover_mul(l,U)),D=ye(h,__tsover_add(__tsover_mul(L,L),__tsover_mul(k,k))),O=ye(g,__tsover_sub(__tsover_mul(r,o),re(y)));return __tsover_mul(Ue(D),no(__tsover_mul(O,-1)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dx\"},{type:\"i\",name:\"dy\"},{type:\"i\",name:\"wi\"},{type:\"i\",name:\"he\"},{type:\"i\",name:\"sk\"}],body:[0,[[13,\"ex\",\"sk\"],[13,\"ey\",\"he\"],[13,\"flip1\",[1,\"dy\",\"<\",[5,\"0\"]]],[13,\"px0\",[6,\"std.select\",[\"dx\",[1,\"dx\",\"*\",[4,\"-\",[5,\"1\"]]],\"flip1\"]]],[13,\"py0\",[6,\"std.select\",[\"dy\",[1,\"dy\",\"*\",[4,\"-\",[5,\"1\"]]],\"flip1\"]]],[13,\"wx0\",[1,\"px0\",\"-\",\"ex\"]],[13,\"wy\",[1,\"py0\",\"-\",\"ey\"]],[13,\"wx\",[1,\"wx0\",\"-\",[6,\"std.clamp\",[\"wx0\",[1,\"wi\",\"*\",[4,\"-\",[5,\"1\"]]],\"wi\"]]]],[13,\"dX\",[1,[1,\"wx\",\"*\",\"wx\"],\"+\",[1,\"wy\",\"*\",\"wy\"]]],[13,\"dY\",[1,\"wy\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"s\",[1,[1,\"px0\",\"*\",\"ey\"],\"-\",[1,\"py0\",\"*\",\"ex\"]]],[13,\"flip2\",[1,\"s\",\"<\",[5,\"0\"]]],[13,\"px\",[6,\"std.select\",[\"px0\",[1,\"px0\",\"*\",[4,\"-\",[5,\"1\"]]],\"flip2\"]]],[13,\"py\",[6,\"std.select\",[\"py0\",[1,\"py0\",\"*\",[4,\"-\",[5,\"1\"]]],\"flip2\"]]],[13,\"vx0\",[1,\"px\",\"-\",\"wi\"]],[13,\"dotve\",[1,[1,\"vx0\",\"*\",\"ex\"],\"+\",[1,\"py\",\"*\",\"ey\"]]],[13,\"dotee\",[6,\"std.max\",[[1,[1,\"ex\",\"*\",\"ex\"],\"+\",[1,\"ey\",\"*\",\"ey\"]],[5,\"0.0001\"]]]],[13,\"tt\",[6,\"std.clamp\",[[1,\"dotve\",\"/\",\"dotee\"],[4,\"-\",[5,\"1\"]],[5,\"1\"]]]],[13,\"vx\",[1,\"vx0\",\"-\",[1,\"ex\",\"*\",\"tt\"]]],[13,\"vy\",[1,\"py\",\"-\",[1,\"ey\",\"*\",\"tt\"]]],[13,\"finalX\",[6,\"std.min\",[\"dX\",[1,[1,\"vx\",\"*\",\"vx\"],\"+\",[1,\"vy\",\"*\",\"vy\"]]]]],[13,\"finalY\",[6,\"std.min\",[\"dY\",[1,[1,\"wi\",\"*\",\"he\"],\"-\",[6,\"std.abs\",[\"s\"]]]]]],[10,[1,[6,\"std.sqrt\",[\"finalX\"]],\"*\",[6,\"std.sign\",[[1,\"finalY\",\"*\",[4,\"-\",[5,\"1\"]]]]]]]]]},externals:{\"std.select\":()=>N,\"std.clamp\":()=>G,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.abs\":()=>re,\"std.sqrt\":()=>Ue,\"std.sign\":()=>no}})&&e.f)({})),AB=w.fn([m,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=m(__tsover_mul(r.x,o),r.y),i=m(__tsover_mul(t.x,o),__tsover_sub(1,t.y)),l=__tsover_sub(a.x,i.x),c=__tsover_sub(a.y,i.y),d=__tsover_mul(s,Rv),u=pe(d),p=ae(d);return m(__tsover_add(__tsover_mul(l,u),__tsover_mul(c,p)),__tsover_sub(__tsover_mul(c,u),__tsover_mul(l,p)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dx\",[1,[7,\"aspectUV\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]]],[13,\"dy\",[1,[7,\"aspectUV\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"rotRad\",[1,\"rotation\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rdx\",[1,[1,\"dx\",\"*\",\"cosR\"],\"+\",[1,\"dy\",\"*\",\"sinR\"]]],[13,\"rdy\",[1,[1,\"dy\",\"*\",\"cosR\"],\"-\",[1,\"dx\",\"*\",\"sinR\"]]],[10,[6,\"d.vec2f\",[\"rdx\",\"rdy\"]]]]]},externals:{\"d.vec2f\":()=>m,DEG_TO_RAD:()=>Rv,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({})),G1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({overallMask:n,strokeBlend:n}),\"ShapeMask\"),wB=w.fn([n,n,n,n],G1)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=r>0,i=o>=1.5,l=o>=.5,c=__tsover_mul(r,.5),d=__tsover_mul(r,-1),u=__tsover_mul(c,-1),p=N(N(n(0),u,l),d,i),_=N(N(r,c,l),n(0),i),f=__tsover_sub(1,be(__tsover_sub(_,s),_,t)),h=be(__tsover_sub(p,s),p,t);return G1({overallMask:f,strokeBlend:N(n(0),h,a)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dist\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"strokeThickness\"},{type:\"i\",name:\"strokePosition\"}],body:[0,[[13,\"hasStroke\",[1,\"strokeThickness\",\">\",[5,\"0\"]]],[13,\"isInside\",[1,\"strokePosition\",\">=\",[5,\"1.5\"]]],[13,\"isCenterOrAbove\",[1,\"strokePosition\",\">=\",[5,\"0.5\"]]],[13,\"halfThickness\",[1,\"strokeThickness\",\"*\",[5,\"0.5\"]]],[13,\"negThickness\",[1,\"strokeThickness\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"negHalf\",[1,\"halfThickness\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"strokeInner\",[6,\"std.select\",[[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"negHalf\",\"isCenterOrAbove\"]],\"negThickness\",\"isInside\"]]],[13,\"strokeOuter\",[6,\"std.select\",[[6,\"std.select\",[\"strokeThickness\",\"halfThickness\",\"isCenterOrAbove\"]],[6,\"d.f32\",[[5,\"0\"]]],\"isInside\"]]],[13,\"overallMask\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"strokeOuter\",\"-\",\"softness\"],\"strokeOuter\",\"dist\"]]]],[13,\"blend\",[6,\"std.smoothstep\",[[1,\"strokeInner\",\"-\",\"softness\"],\"strokeInner\",\"dist\"]]],[13,\"strokeBlend\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"blend\",\"hasStroke\"]]],[10,[6,\"ShapeMask\",[[104,{overallMask:\"overallMask\",strokeBlend:\"strokeBlend\"}]]]]]]},externals:{\"std.select\":()=>N,\"d.f32\":()=>n,\"std.smoothstep\":()=>be,ShapeMask:()=>G1}})&&e.f)({}));function TB(e,t,s,r){let o=W(No[r]??Ar,\"mixColors\",[t,s,e.member(\"strokeBlend\")]);return Ct(o.member(\"rgb\"),o.member(\"a\").mul(e.member(\"overallMask\")))}async function dA(e,t){let s=await fetch(e,{mode:\"cors\"});if(!s.ok)throw new Error(`SDF fetch failed: HTTP ${s.status}`);let r=await s.arrayBuffer(),o=512*512;if(r.byteLength===o*2){let a=new Uint16Array(r),i=Math.min(a.length,t.length);for(let l=0;l<i;l++)t[l]=a[l]/32767.5-1}else if(r.byteLength===o*4){let a=new Float32Array(r),i=Math.min(a.length,t.length);for(let l=0;l<i;l++)t[l]=a[l]}else throw new Error(`SDF binary has unexpected size (${r.byteLength} bytes); expected ${o*2} (compact) or ${o*4} (legacy). The URL may point to a non-SDF file (e.g. the raw SVG).`)}function uA(e,t){let r=new Float32Array(262144).fill(.5),o=new Uint16Array(262144),a=_o(.5);for(let d=0;d<262144;d++)o[d]=a;let i=e.createDataTexture({width:512,height:512,format:\"r16float\",data:o,label:\"svg-sdf\"});e.onCleanup(()=>i.destroy());let l=!1,c=0;return t&&dA(t,r).then(()=>{for(let d=0;d<262144;d++)o[d]=_o(r[d]);l=!0}).catch(d=>di(\"[SDF] Failed to load SDF texture:\",d)),e.onBeforeRender(()=>{l&&(l=!1,i.write(o),c++)}),{kitTexture:e.registerMediaTexture(()=>i.texture),texture:i,getVersion:()=>c}}function SB(e,t){let{kitTexture:s}=uA(e,t);return r=>s.sampleLevel(r)}function PB(e,t){let o=new Float32Array(262144).fill(.5),a=new Uint16Array(262144*4),i=_o(.5),l=_o(1);for(let g=0;g<262144;g++)a[g*4]=i,a[g*4+3]=l;let c=e.createMediaTexture({width:512,height:512,format:\"rgba16float\",label:\"svg-sdf-grad\"});e.onCleanup(()=>c.destroy());let d=(g,y)=>{let b=Math.floor(g),x=Math.floor(y),P=g-b,C=y-x,E=D=>Math.min(Math.max(D,0),511),R=E(b),U=E(b+1),L=E(x),k=E(x+1);return o[L*512+R]*(1-P)*(1-C)+o[L*512+U]*P*(1-C)+o[k*512+R]*(1-P)*C+o[k*512+U]*P*C},u=.01,p=u*512,_=()=>{for(let g=0;g<512;g++)for(let y=0;y<512;y++){let b=g*512+y,x=o[b],P=(d(y+p,g)-x)/u,C=(d(y,g+p)-x)/u;a[b*4]=_o(x),a[b*4+1]=_o(P),a[b*4+2]=_o(C)}},f=!1;t&&dA(t,o).then(()=>{_(),f=!0}).catch(g=>di(\"[SDF] Failed to load SDF texture:\",g)),e.onBeforeRender(()=>{f&&(f=!1,c.write(a))});let h=e.registerMediaTexture(()=>c.texture);return g=>h.sampleLevel(g)}var CB=.008726646259971648;function P_(e){return w.fn([m,n,n,n,n,n,n,n,n],S)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=__tsover_sub(s.x,.5),_=__tsover_sub(s.y,.5),f=__tsover_mul(l,Rv),h=pe(f),g=ae(f),y=__tsover_add(__tsover_mul(p,h),__tsover_mul(_,g)),b=__tsover_sub(__tsover_mul(_,h),__tsover_mul(p,g)),x=n(0);return e===\"polygonSDF\"?x=K(Dv(y,b,r,o),Dm(y,b,r),a):e===\"flowerSDF\"?x=Lv(y,b,r,o,i):e===\"ringSDF\"?x=Ov(y,b,r,i):e===\"crossSDF\"?x=Gv(y,b,r,i,a):e===\"starSDF\"?x=Uv(y,b,r,o,i):e===\"roundedRectSDF\"?x=Fv(y,b,r,c,a):e===\"ellipseSDF\"?x=Iv(y,b,r,c):e===\"vesicaSDF\"?x=Yv(y,b,r,i):e===\"crescentSDF\"?x=Wv(y,b,r,i,d):e===\"trapezoidSDF\"?x=qv(y,b,i,r,c):e===\"heartSDF\"?x=Vv(y,b,r):e===\"arcSDF\"?x=Nv(y,b,r,__tsover_mul(u,.008726646259971648)):e===\"teardropSDF\"?x=$v(y,b,r,c):e===\"parallelogramSDF\"?x=Xv(y,b,r,c,d):x=Dm(y,b,r),S(x,0,0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"round\"},{type:\"i\",name:\"inner\"},{type:\"i\",name:\"rot\"},{type:\"i\",name:\"h\"},{type:\"i\",name:\"off\"},{type:\"i\",name:\"ap\"}],body:[0,[[13,\"dx\",[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]],[13,\"rotRad\",[1,\"rot\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rdx\",[1,[1,\"dx\",\"*\",\"cosR\"],\"+\",[1,\"dy\",\"*\",\"sinR\"]]],[13,\"rdy\",[1,[1,\"dy\",\"*\",\"cosR\"],\"-\",[1,\"dx\",\"*\",\"sinR\"]]],[12,\"dist\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"polygonSDF\"]],[2,\"dist\",\"=\",[6,\"std.mix\",[[6,\"polygonSdf\",[\"rdx\",\"rdy\",\"r\",\"n\"]],[6,\"circleSdf\",[\"rdx\",\"rdy\",\"r\"]],\"round\"]]],[11,[1,\"shapeType\",\"===\",[103,\"flowerSDF\"]],[2,\"dist\",\"=\",[6,\"flowerSdf\",[\"rdx\",\"rdy\",\"r\",\"n\",\"inner\"]]],[11,[1,\"shapeType\",\"===\",[103,\"ringSDF\"]],[2,\"dist\",\"=\",[6,\"ringSdf\",[\"rdx\",\"rdy\",\"r\",\"inner\"]]],[11,[1,\"shapeType\",\"===\",[103,\"crossSDF\"]],[2,\"dist\",\"=\",[6,\"crossSdf\",[\"rdx\",\"rdy\",\"r\",\"inner\",\"round\"]]],[11,[1,\"shapeType\",\"===\",[103,\"starSDF\"]],[2,\"dist\",\"=\",[6,\"starSdf\",[\"rdx\",\"rdy\",\"r\",\"n\",\"inner\"]]],[11,[1,\"shapeType\",\"===\",[103,\"roundedRectSDF\"]],[2,\"dist\",\"=\",[6,\"roundedRectSdf\",[\"rdx\",\"rdy\",\"r\",\"h\",\"round\"]]],[11,[1,\"shapeType\",\"===\",[103,\"ellipseSDF\"]],[2,\"dist\",\"=\",[6,\"ellipseSdf\",[\"rdx\",\"rdy\",\"r\",\"h\"]]],[11,[1,\"shapeType\",\"===\",[103,\"vesicaSDF\"]],[2,\"dist\",\"=\",[6,\"vesicaSdf\",[\"rdx\",\"rdy\",\"r\",\"inner\"]]],[11,[1,\"shapeType\",\"===\",[103,\"crescentSDF\"]],[2,\"dist\",\"=\",[6,\"crescentSdf\",[\"rdx\",\"rdy\",\"r\",\"inner\",\"off\"]]],[11,[1,\"shapeType\",\"===\",[103,\"trapezoidSDF\"]],[2,\"dist\",\"=\",[6,\"trapezoidSdf\",[\"rdx\",\"rdy\",\"inner\",\"r\",\"h\"]]],[11,[1,\"shapeType\",\"===\",[103,\"heartSDF\"]],[2,\"dist\",\"=\",[6,\"heartSdf\",[\"rdx\",\"rdy\",\"r\"]]],[11,[1,\"shapeType\",\"===\",[103,\"arcSDF\"]],[2,\"dist\",\"=\",[6,\"arcSdf\",[\"rdx\",\"rdy\",\"r\",[1,\"ap\",\"*\",\"APERTURE_TO_HALFANGLE\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"teardropSDF\"]],[2,\"dist\",\"=\",[6,\"teardropSdf\",[\"rdx\",\"rdy\",\"r\",\"h\"]]],[11,[1,\"shapeType\",\"===\",[103,\"parallelogramSDF\"]],[2,\"dist\",\"=\",[6,\"parallelogramSdf\",[\"rdx\",\"rdy\",\"r\",\"h\",\"off\"]]],[2,\"dist\",\"=\",[6,\"circleSdf\",[\"rdx\",\"rdy\",\"r\"]]]]]]]]]]]]]]]]],[10,[6,\"d.vec4f\",[\"dist\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{DEG_TO_RAD:()=>Rv,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.f32\":()=>n,shapeType:()=>e,\"std.mix\":()=>K,polygonSdf:()=>Dv,circleSdf:()=>Dm,flowerSdf:()=>Lv,ringSdf:()=>Ov,crossSdf:()=>Gv,starSdf:()=>Uv,roundedRectSdf:()=>Fv,ellipseSdf:()=>Iv,vesicaSdf:()=>Yv,crescentSdf:()=>Wv,trapezoidSdf:()=>qv,heartSdf:()=>Vv,arcSdf:()=>Nv,APERTURE_TO_HALFANGLE:()=>.008726646259971648,teardropSdf:()=>$v,parallelogramSdf:()=>Xv,\"d.vec4f\":()=>S}})&&t.f)({})).$name(`analyticSdf_${e}`)}function MB(e,t,s=\"none\"){let r=t.radius??$e(0),o=t.sides??$e(0),a=t.rounding??$e(0),i=t.innerRatio??$e(0),l=t.rotation??$e(0),c=t.height??$e(0),d=t.offset??$e(0),u=t.aperture??$e(0),p=P_(e);return _=>W(p,`analyticSdf_${e}`,[_,r,o,a,i,l,c,d,u])}var sne={_saRadius:{schema:n,initial:.35},_saSides:{schema:n,initial:6},_saRounding:{schema:n,initial:0},_saInnerRatio:{schema:n,initial:.4},_saRotation:{schema:n,initial:0},_saHeight:{schema:n,initial:.25},_saOffset:{schema:n,initial:.2},_saAperture:{schema:n,initial:270}};function Hv(e){let t=(s,r)=>typeof s==\"number\"?s:r;return{radius:t(e.radius,t(e.width,t(e.bottomWidth,.35))),sides:t(e.sides,6),rounding:t(e.rounding,0),innerRatio:t(e.innerRatio,t(e.thickness,t(e.spread,t(e.topWidth,t(e.topRatio,.4))))),rotation:t(e.rotation,0),height:t(e.height,.25),offset:t(e.offset,t(e.skew,.2)),aperture:t(e.aperture,270)}}function jv(e){if(e&&typeof e==\"object\")return e;if(typeof e==\"string\")try{return JSON.parse(e)}catch{return{}}return{}}function EB(e,t){let s=\"\",r=jv(t()),o=()=>{let i=Hv(r);e.setExtraField(\"_saRadius\",i.radius),e.setExtraField(\"_saSides\",i.sides),e.setExtraField(\"_saRounding\",i.rounding),e.setExtraField(\"_saInnerRatio\",i.innerRatio),e.setExtraField(\"_saRotation\",i.rotation),e.setExtraField(\"_saHeight\",i.height),e.setExtraField(\"_saOffset\",i.offset),e.setExtraField(\"_saAperture\",i.aperture)};e.onBeforeRender(()=>{let i=t();if(i&&typeof i==\"object\")r=i;else if(typeof i==\"string\"&&i!==s){s=i;try{r=JSON.parse(i)}catch{return}}o()});let a=e.uniforms;return{radius:a._saRadius,sides:a._saSides,rounding:a._saRounding,innerRatio:a._saInnerRatio,rotation:a._saRotation,height:a._saHeight,offset:a._saOffset,aperture:a._saAperture}}var Ki=512,td={},Qb=new Set,Zb={};function Kh(e,t){return e?e in td?td[e]:(t&&(Zb[e]??=[]).push(t),Qb.has(e)||(Qb.add(e),rne(e)),null):null}async function rne(e){try{let t=await fetch(e,{mode:\"cors\"});if(!t.ok){td[e]=null;return}let s=await t.arrayBuffer(),r=Ki*Ki;if(s.byteLength!==r*2&&s.byteLength!==r*4){td[e]=null;return}let o=s.byteLength===r*2?(()=>{let d=new Uint16Array(s);return u=>d[u]/32767.5-1})():(()=>{let d=new Float32Array(s);return u=>d[u]})(),a=Ki,i=Ki,l=-1,c=-1;for(let d=0;d<Ki;d++){let u=d*Ki;for(let p=0;p<Ki;p++)o(u+p)<0&&(p<a&&(a=p),p>l&&(l=p),d<i&&(i=d),d>c&&(c=d))}td[e]=l<0?null:{hwf:(l-a+1)/Ki/2,hhf:(c-i+1)/Ki/2}}catch{td[e]=null}finally{Qb.delete(e);let t=Zb[e];if(t){delete Zb[e];for(let s of t)s()}}}function kB(e){return!!e&&e in td}var fP=1/60;function C_(e,t,s,r,o,a){if(r===0&&o===0)return[s,0];let i=200*Math.pow(.01,r),l=2*Math.sqrt(i)*(1-o*.85),c=a>fP?Math.ceil(a/fP):1,d=a/c,u=e,p=t;for(let _=0;_<c;_++){let f=i*(s-u);p+=(f-l*p)*d,u+=p*d}return[u,p]}function one(e){return e<1/2.75?7.5625*e*e:e<2/2.75?(e-=1.5/2.75,7.5625*e*e+.75):e<2.5/2.75?(e-=2.25/2.75,7.5625*e*e+.9375):(e-=2.625/2.75,7.5625*e*e+.984375)}function pA(e,t){switch(t){case\"linear\":return e;case\"quad\":return e<.5?2*e*e:1-Math.pow(-2*e+2,2)/2;case\"expo\":return e===0?0:e===1?1:e<.5?Math.pow(2,20*e-10)/2:(2-Math.pow(2,-20*e+10))/2;case\"bounce\":return one(e);default:return(1-Math.cos(Math.PI*e))/2}}function BB(e,t,s){let r=s.pixelRatio||1,o=Math.max(1,Math.floor(s.maxTextureDim/r)-1),a=Math.min(e,s.viewportWidth,o),i=Math.min(t,s.viewportHeight,o),l=Math.min(a/e,i/t,1);return{width:Math.max(1,Math.round(e*l)),height:Math.max(1,Math.round(t*l))}}function zB(e,t,s,r){return e!=null?(r-e)/1e3:t+s}function RB(e,t){let{lastRenderTime:s,isVisible:r,forceFullFrameRate:o,minInterval:a=0}=t,i=s>0?(e-s)/1e3:.016;if(s>0){let l=e-s;if(!r&&!o&&l<1e3)return{render:!1,deltaTime:i};if((r||o)&&l<Math.max(15.666666666666668,a))return{render:!1,deltaTime:i}}return{render:!0,deltaTime:Math.min(i,.1)}}async function mA(e){try{let t=e.device?.queue;if(t&&typeof t.onSubmittedWorkDone==\"function\"){await t.onSubmittedWorkDone();return}}catch{}await new Promise(t=>setTimeout(t,16))}function DB(e){e.updateDrivers(),e.ensureComposition()&&(e.beforeRender(),e.flush(),e.render()!==!1&&e.markReady(),e.afterRender())}function LB(e){let t=null,s=a=>typeof requestAnimationFrame==\"function\"?requestAnimationFrame(a):setTimeout(()=>a(performance.now()),16),r=a=>{typeof cancelAnimationFrame==\"function\"?cancelAnimationFrame(a):clearTimeout(a)},o=a=>{e(a),t!==null&&(t=s(o))};return{start(){t===null&&(t=s(o))},stop(){t!==null&&(r(t),t=null)},get running(){return t!==null}}}var UB=[\"sphere3D\",\"cube3D\",\"torus3D\",\"octahedron3D\",\"cylinder3D\",\"capsule3D\",\"cone3D\",\"pyramid3D\",\"prism3D\",\"ellipsoid3D\",\"diamond3D\",\"link3D\",\"gem3D\",\"helix3D\",\"metaballs3D\",\"dodecahedron3D\",\"hemisphere3D\",\"ribbon3D\",\"blob3D\",\"gyroscope3D\"];function OB(e){return UB.includes(e)}function GB(e){return e===\"svgExtrude3D\"}var FB={depth:.12,bevel:.02,rotX:15,rotY:30,rotZ:0};function IB(e,t){if(typeof e==\"number\")return e;if(e&&typeof e==\"object\"&&(e.type===\"auto-animate\"||e.type===\"mouse\")){let s=typeof e.outputMin==\"number\"?e.outputMin:t,r=typeof e.outputMax==\"number\"?e.outputMax:t;return Math.max(s,r)}return t}var YB={sphere3D:{radius:.35,rotX:0,rotY:0,rotZ:0},cube3D:{sizeX:.27,sizeY:.27,sizeZ:.27,rounding:.02,rotX:25,rotY:35,rotZ:0},torus3D:{radius:.3,tube:.12,rotX:55,rotY:0,rotZ:0},octahedron3D:{radius:.42,rotX:10,rotY:25,rotZ:0},cylinder3D:{radius:.22,height:.26,rounding:.02,rotX:60,rotY:0,rotZ:30},capsule3D:{radius:.16,height:.2,rotX:0,rotY:0,rotZ:35},cone3D:{radius:.3,topRadius:.02,height:.3,rotX:25,rotY:0,rotZ:15},pyramid3D:{size:.3,height:.45,rotX:-10,rotY:30,rotZ:0},prism3D:{radius:.28,height:.14,rotX:25,rotY:30,rotZ:0},ellipsoid3D:{radiusX:.38,radiusY:.22,radiusZ:.3,rotX:0,rotY:0,rotZ:20},diamond3D:{radius:.3,height:.42,rotX:15,rotY:0,rotZ:0},link3D:{radius:.18,length:.15,tube:.08,rotX:20,rotY:30,rotZ:0},gem3D:{radius:.3,height:.32,facets:8,rotX:15,rotY:0,rotZ:0},helix3D:{radius:.22,tube:.06,pitch:.16,rotX:20,rotY:0,rotZ:0},metaballs3D:{balls:4,ballRadius:.16,spread:.2,blend:.12,speed:1,rotX:0,rotY:0,rotZ:0},dodecahedron3D:{radius:.34,rotX:20,rotY:10,rotZ:0},hemisphere3D:{radius:.4,cut:0,rotX:30,rotY:0,rotZ:0},ribbon3D:{width:.15,thickness:.03,length:.38,wave:.1,waveFrequency:7,twist:140,speed:1,rotX:12,rotY:18,rotZ:0},blob3D:{radius:.3,wobble:.07,wobbliness:5,speed:1,rotX:0,rotY:0,rotZ:0},gyroscope3D:{radius:.34,width:.1,thickness:.028,rounding:.006,core:.14,speed:1,rotX:15,rotY:0,rotZ:0}};function WB(e){let t=YB[e.type]??{},s=(r,o=.3)=>IB(e[r],t[r]??o);switch(e.type){case\"cube3D\":return Math.hypot(s(\"sizeX\"),s(\"sizeY\"),s(\"sizeZ\"))+s(\"rounding\",0);case\"torus3D\":return s(\"radius\")+s(\"tube\");case\"cylinder3D\":return Math.hypot(s(\"radius\"),s(\"height\"))+s(\"rounding\",0);case\"capsule3D\":return s(\"height\")+s(\"radius\");case\"cone3D\":return Math.hypot(Math.max(s(\"radius\"),s(\"topRadius\")),s(\"height\"));case\"pyramid3D\":return Math.hypot(s(\"size\")*Math.SQRT2,s(\"height\")*.5);case\"prism3D\":return Math.hypot(s(\"radius\")*1.1548,s(\"height\"));case\"ellipsoid3D\":return Math.max(s(\"radiusX\"),s(\"radiusY\"),s(\"radiusZ\"));case\"diamond3D\":return Math.max(s(\"radius\"),s(\"height\"));case\"link3D\":return s(\"length\")+s(\"radius\")+s(\"tube\");case\"gem3D\":return Math.max(s(\"radius\"),s(\"height\"));case\"helix3D\":return Math.hypot(s(\"radius\")+s(\"tube\"),s(\"pitch\")*3*.5+s(\"tube\"));case\"metaballs3D\":return s(\"spread\")+s(\"ballRadius\",.16)+s(\"blend\",.12);case\"dodecahedron3D\":return s(\"radius\")*1.32;case\"hemisphere3D\":return s(\"radius\");case\"ribbon3D\":return Math.hypot(s(\"length\",.38),s(\"wave\",.1)*1.2+Math.hypot(s(\"width\",.15),s(\"thickness\",.03)))+.01;case\"blob3D\":return s(\"radius\",.3)+s(\"wobble\",.07)+.02;case\"gyroscope3D\":return s(\"radius\",.34)+s(\"thickness\",.028)*.5+s(\"width\",.1)*.5+s(\"rounding\",.006);default:return s(\"radius\",.35)}}var is={propBindings:{x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"},rotation:{prop:\"rotation\",as:\"degrees\"}},computeBounds(e,t,s){let r=e.scale??1,o=typeof e.center?.x==\"number\"?e.center.x:.5,a=typeof e.center?.y==\"number\"?e.center.y:.5,{hwf:i,hhf:l}=Jb(e),c=r*s;return{centerXPx:o*t,centerYPx:a*s,widthPx:2*i*c,heightPx:2*l*c,rotationDeg:e.rotation??0}},writeBounds(e,t,s,r){let o={...t.center??{x:.5,y:.5},x:e.centerXPx/s,y:e.centerYPx/r},a=t.scale??1,i=ane(t)?Kv(t.shape):null;if(i&&a>0)return{center:o,rotation:e.rotationDeg,shape:JSON.stringify({...i,radius:Math.max(.001,e.widthPx/(2*a*r)),height:Math.max(.001,e.heightPx/(2*a*r))})};let{hwf:l}=Jb(t);return{center:o,scale:l>0?Math.max(.01,e.widthPx/(2*l*r)):.01,rotation:e.rotationDeg}}},pm=new Map;function Kv(e){if(typeof e!=\"string\")return e??null;if(pm.has(e))return pm.get(e);let t=null;try{t=JSON.parse(e)}catch{t=null}return pm.size>=64&&pm.clear(),pm.set(e,t),t}function ane(e){if(e.shapeSdfUrl)return!1;let t=Kv(e.shape)?.type;return t===\"roundedRectSDF\"||t===\"ellipseSDF\"}function Jb(e){if(e.shapeSdfUrl){let r=e._shapeBounds??Kh(e.shapeSdfUrl),o=r&&typeof r.hwf==\"number\"&&typeof r.hhf==\"number\"?{hwf:r.hwf,hhf:r.hhf}:{hwf:.5,hhf:.5},a=e.shapeType;if(typeof a==\"string\"&&GB(a)){let i=Kv(e.shape)??{},l=d=>IB(i[d],FB[d]??0),c=Math.hypot(o.hwf,o.hhf,l(\"depth\")*.5)+l(\"bevel\")+.02;return{hwf:c,hhf:c}}return o}let t=Kv(e.shape);if(!t||typeof t!=\"object\")return{hwf:.5,hhf:.5};if(typeof t.type==\"string\"&&OB(t.type)){let r=WB(t);return{hwf:r,hhf:r}}let s=typeof t.radius==\"number\"?t.radius:.35;switch(t.type){case\"roundedRectSDF\":case\"ellipseSDF\":return{hwf:s,hhf:typeof t.height==\"number\"?t.height:s};case\"ringSDF\":{let r=typeof t.thickness==\"number\"?t.thickness:0;return{hwf:s+r,hhf:s+r}}case\"trapezoidSDF\":{let r=typeof t.bottomWidth==\"number\"?t.bottomWidth:.3,o=typeof t.topWidth==\"number\"?t.topWidth:.2,a=typeof t.height==\"number\"?t.height:.25;return{hwf:Math.max(r,o),hhf:a}}case\"vesicaSDF\":{let r=typeof t.spread==\"number\"?Math.min(1,Math.max(0,t.spread)):.4;return{hwf:s*(1-r),hhf:s*Math.sqrt(Math.max(0,1-r*r))}}case\"teardropSDF\":return{hwf:s,hhf:((typeof t.height==\"number\"?t.height:.4)+s)/2};case\"parallelogramSDF\":{let r=typeof t.width==\"number\"?t.width:.32,o=typeof t.height==\"number\"?t.height:.22,a=typeof t.skew==\"number\"?t.skew:0;return{hwf:r+Math.abs(a),hhf:o}}default:return{hwf:s,hhf:s}}}function bc(e,t){if(e)return e;let s=t;if(typeof t==\"string\")try{s=JSON.parse(t)}catch{return\"circleSDF\"}if(s&&typeof s==\"object\"){let r=s.type;if(typeof r==\"string\"&&r)return r}return\"circleSDF\"}var kn=ko({HELIX_TURNS:()=>3,MAX_METABALLS:()=>8,MarchParams:()=>vl,RotStruct:()=>Ad,SHAPE3D_DEFAULTS:()=>Qv,SHAPE3D_TYPES:()=>qB,SVG3D_DEFAULTS:()=>NB,SVG3D_TYPES:()=>nne,SampleParams:()=>yx,VOLUMETRIC_FIELD_EXTRA_FIELDS:()=>pne,VOLUMETRIC_FIELD_FORMAT:()=>Zh,VOLUMETRIC_FIELD_RES:()=>HB,VOLUMETRIC_FIELD_RES_MOBILE:()=>768,boxFaceUV:()=>EA,buildFieldSampleGraph:()=>une,buildFieldSampleGraphArgs:()=>bA,buildRaymarchedField:()=>cne,buildRaymarchedFieldFn:()=>fA,buildVolumetricFieldKernel:()=>gA,buildVolumetricFieldSampler:()=>KB,capsuleSurfaceUV:()=>BA,createAnalytic2dExtrudeSetup:()=>QB,createAnalytic3dSdfSetup:()=>AA,createSvg3dSdfSetup:()=>wA,createVolumetricFieldCompute:()=>yA,createVolumetricFieldComputeNode:()=>_ne,eulerRotatePacked:()=>Hr,is3dShapeType:()=>M_,isMobileGpuViewport:()=>Rr,isSvg3dShapeType:()=>Qh,isVolumetricShapeType:()=>VB,makeVolumetricFieldLayout:()=>hA,makeVolumetricSvgFieldLayout:()=>jB,mobiusSurfaceUV:()=>RA,packedParamExtraFields:()=>UA,paramBoxSdf:()=>MA,paramCapsuleSdf:()=>kA,paramMobiusSdf:()=>zA,paramSphereSdf:()=>TA,paramTorusSdf:()=>PA,paramTwistedRibbonSdf:()=>DA,resolveActiveFieldRes:()=>vA,resolveShapeFieldSampler:()=>mne,resolveShapeSubProp:()=>_A,resolveVolumetricFieldRes:()=>E_,rotateVec3:()=>ja,rotateVecCpu:()=>Jl,sdBicone:()=>Zm,sdCappedCone:()=>ax,sdCapsule:()=>ox,sdCutSphere:()=>fx,sdDodecahedron:()=>_x,sdEllipsoid:()=>lx,sdFlatBand:()=>Am,sdGem:()=>ux,sdGyroscope:()=>gx,sdHelix:()=>px,sdHexPrism:()=>ix,sdLink:()=>cx,sdMetaballs:()=>mx,sdOctahedron:()=>sx,sdPyramid:()=>nx,sdRibbon:()=>vx,sdRoundBox:()=>xd,sdRoundCylinder:()=>rx,sdSphere:()=>ex,sdTorus:()=>tx,sdWobbleBlob:()=>hx,shape3dBoundingRadius:()=>XB,shapeSubPropMax:()=>$B,smin:()=>dx,sphereSurfaceUV:()=>SA,torusSurfaceUV:()=>CA,twistedRibbonSurfaceUV:()=>LA}),vP=3.141592653589793,sd=6.283185307179586,qB=[\"sphere3D\",\"cube3D\",\"torus3D\",\"octahedron3D\",\"cylinder3D\",\"capsule3D\",\"cone3D\",\"pyramid3D\",\"prism3D\",\"ellipsoid3D\",\"diamond3D\",\"link3D\",\"gem3D\",\"helix3D\",\"metaballs3D\",\"dodecahedron3D\",\"hemisphere3D\",\"ribbon3D\",\"blob3D\",\"gyroscope3D\"];function M_(e){return qB.includes(e)}var nne=[\"svgExtrude3D\"];function Qh(e){return e===\"svgExtrude3D\"}function VB(e){return M_(e)||Qh(e)}var NB={depth:.12,bevel:.02,rotX:15,rotY:30,rotZ:0},ine=e=>e<1/2.75?7.5625*e*e:e<2/2.75?(e-=1.5/2.75,7.5625*e*e+.75):e<2.5/2.75?(e-=2.25/2.75,7.5625*e*e+.9375):(e-=2.625/2.75,7.5625*e*e+.984375),lne=(e,t)=>{switch(t){case\"linear\":return e;case\"quad\":return e<.5?2*e*e:1-Math.pow(-2*e+2,2)/2;case\"expo\":return e===0?0:e===1?1:e<.5?Math.pow(2,20*e-10)/2:(2-Math.pow(2,-20*e+10))/2;case\"bounce\":return ine(e);default:return(1-Math.cos(Math.PI*e))/2}};function _A(e,t,s,r){if(typeof e==\"number\")return e;if(e&&typeof e==\"object\"){if(e.type===\"auto-animate\"){let o=(s.elapsed*(e.speed??1)*.2%1+1)%1,a=lne(e.mode===\"loop\"?o:o<.5?o*2:(1-o)*2,e.easing??e.waveform??\"sine\"),i=typeof e.outputMin==\"number\"?e.outputMin:t;return i+a*((typeof e.outputMax==\"number\"?e.outputMax:t)-i)}if(e.type===\"mouse\"){let o=e.axis===\"y\"?s.pointerY:s.pointerX,a=s.springs.get(r);a||(a={current:o,velocity:0},s.springs.set(r,a));let[i,l]=C_(a.current,a.velocity,o,e.smoothing??0,e.momentum??0,s.deltaTime);a.current=i,a.velocity=l;let c=Math.max(.001,i),d=typeof e.outputMin==\"number\"?e.outputMin:t,u=typeof e.outputMax==\"number\"?e.outputMax:t,p=Math.pow(2,-(e.curve??0)*2);return d+Math.pow(c,p)*(u-d)}}return t}function $B(e,t){if(typeof e==\"number\")return e;if(e&&typeof e==\"object\"&&(e.type===\"auto-animate\"||e.type===\"mouse\")){let s=typeof e.outputMin==\"number\"?e.outputMin:t,r=typeof e.outputMax==\"number\"?e.outputMax:t;return Math.max(s,r)}return t}var Qv={sphere3D:{radius:.35,rotX:0,rotY:0,rotZ:0},cube3D:{sizeX:.27,sizeY:.27,sizeZ:.27,rounding:.02,rotX:25,rotY:35,rotZ:0},torus3D:{radius:.3,tube:.12,rotX:55,rotY:0,rotZ:0},octahedron3D:{radius:.42,rotX:10,rotY:25,rotZ:0},cylinder3D:{radius:.22,height:.26,rounding:.02,rotX:60,rotY:0,rotZ:30},capsule3D:{radius:.16,height:.2,rotX:0,rotY:0,rotZ:35},cone3D:{radius:.3,topRadius:.02,height:.3,rotX:25,rotY:0,rotZ:15},pyramid3D:{size:.3,height:.45,rotX:-10,rotY:30,rotZ:0},prism3D:{radius:.28,height:.14,rotX:25,rotY:30,rotZ:0},ellipsoid3D:{radiusX:.38,radiusY:.22,radiusZ:.3,rotX:0,rotY:0,rotZ:20},diamond3D:{radius:.3,height:.42,rotX:15,rotY:0,rotZ:0},link3D:{radius:.18,length:.15,tube:.08,rotX:20,rotY:30,rotZ:0},gem3D:{radius:.3,height:.32,facets:8,rotX:15,rotY:0,rotZ:0},helix3D:{radius:.22,tube:.06,pitch:.16,rotX:20,rotY:0,rotZ:0},metaballs3D:{balls:4,ballRadius:.16,spread:.2,blend:.12,speed:1,rotX:0,rotY:0,rotZ:0},dodecahedron3D:{radius:.34,rotX:20,rotY:10,rotZ:0},hemisphere3D:{radius:.4,cut:0,rotX:30,rotY:0,rotZ:0},ribbon3D:{width:.15,thickness:.03,length:.38,wave:.1,waveFrequency:7,twist:140,speed:1,rotX:12,rotY:18,rotZ:0},blob3D:{radius:.3,wobble:.07,wobbliness:5,speed:1,rotX:0,rotY:0,rotZ:0},gyroscope3D:{radius:.34,width:.1,thickness:.028,rounding:.006,core:.14,speed:1,rotX:15,rotY:0,rotZ:0}};function XB(e){let t=Qv[e.type]??{},s=(r,o=.3)=>$B(e[r],t[r]??o);switch(e.type){case\"cube3D\":return Math.hypot(s(\"sizeX\"),s(\"sizeY\"),s(\"sizeZ\"))+s(\"rounding\",0);case\"torus3D\":return s(\"radius\")+s(\"tube\");case\"cylinder3D\":return Math.hypot(s(\"radius\"),s(\"height\"))+s(\"rounding\",0);case\"capsule3D\":return s(\"height\")+s(\"radius\");case\"cone3D\":return Math.hypot(Math.max(s(\"radius\"),s(\"topRadius\")),s(\"height\"));case\"pyramid3D\":return Math.hypot(s(\"size\")*Math.SQRT2,s(\"height\")*.5);case\"prism3D\":return Math.hypot(s(\"radius\")*1.1548,s(\"height\"));case\"ellipsoid3D\":return Math.max(s(\"radiusX\"),s(\"radiusY\"),s(\"radiusZ\"));case\"diamond3D\":return Math.max(s(\"radius\"),s(\"height\"));case\"link3D\":return s(\"length\")+s(\"radius\")+s(\"tube\");case\"gem3D\":return Math.max(s(\"radius\"),s(\"height\"));case\"helix3D\":return Math.hypot(s(\"radius\")+s(\"tube\"),s(\"pitch\")*3*.5+s(\"tube\"));case\"metaballs3D\":return s(\"spread\")+s(\"ballRadius\",.16)+s(\"blend\",.12);case\"dodecahedron3D\":return s(\"radius\")*1.32;case\"hemisphere3D\":return s(\"radius\");case\"ribbon3D\":return Math.hypot(s(\"length\",.38),s(\"wave\",.1)*1.2+Math.hypot(s(\"width\",.15),s(\"thickness\",.03)))+.01;case\"blob3D\":return s(\"radius\",.3)+s(\"wobble\",.07)+.02;case\"gyroscope3D\":return s(\"radius\",.34)+s(\"thickness\",.028)*.5+s(\"width\",.1)*.5+s(\"rounding\",.006);default:return s(\"radius\",.35)}}function Jl(e,t,s,r){let o=e*r.cy+s*r.sy,a=-e*r.sy+s*r.cy,i=t*r.cx-a*r.sx,l=t*r.sx+a*r.cx;return{x:o*r.cz-i*r.sz,y:o*r.sz+i*r.cz,z:l}}var ex=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_sub(he(t),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"}],body:[0,[[10,[1,[6,\"std.length\",[\"p\"]],\"-\",\"r\"]]]]},externals:{\"std.length\":()=>he}})&&e.f)({})),xd=w.fn([A,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=__tsover_add(__tsover_sub(re(t.x),s),a),l=__tsover_add(__tsover_sub(re(t.y),r),a),c=__tsover_add(__tsover_sub(re(t.z),o),a),d=he(A(B(i,0),B(l,0),B(c,0))),u=ye(B(i,B(l,c)),0);return __tsover_sub(__tsover_add(d,u),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"hx\"},{type:\"i\",name:\"hy\"},{type:\"i\",name:\"hz\"},{type:\"i\",name:\"rounding\"}],body:[0,[[13,\"qx\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"x\"]]],\"-\",\"hx\"],\"+\",\"rounding\"]],[13,\"qy\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"y\"]]],\"-\",\"hy\"],\"+\",\"rounding\"]],[13,\"qz\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"z\"]]],\"-\",\"hz\"],\"+\",\"rounding\"]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec3f\",[[6,\"std.max\",[\"qx\",[5,\"0\"]]],[6,\"std.max\",[\"qy\",[5,\"0\"]]],[6,\"std.max\",[\"qz\",[5,\"0\"]]]]]]]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",[6,\"std.max\",[\"qy\",\"qz\"]]]],[5,\"0\"]]]],[10,[1,[1,\"outer\",\"+\",\"inner\"],\"-\",\"rounding\"]]]]},externals:{\"std.abs\":()=>re,\"std.length\":()=>he,\"d.vec3f\":()=>A,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})),tx=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_sub(he(m(t.x,t.z)),s);return __tsover_sub(he(m(o,t.y)),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"ringR\"},{type:\"i\",name:\"tubeR\"}],body:[0,[[13,\"qx\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]],\"-\",\"ringR\"]],[10,[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"qx\",[7,\"p\",\"y\"]]]]],\"-\",\"tubeR\"]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})),sx=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_mul(__tsover_sub(__tsover_add(__tsover_add(re(t.x),re(t.y)),re(t.z)),s),.57735027)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"s\"}],body:[0,[[10,[1,[1,[1,[1,[6,\"std.abs\",[[7,\"p\",\"x\"]]],\"+\",[6,\"std.abs\",[[7,\"p\",\"y\"]]]],\"+\",[6,\"std.abs\",[[7,\"p\",\"z\"]]]],\"-\",\"s\"],\"*\",[5,\"0.57735027\"]]]]]},externals:{\"std.abs\":()=>re}})&&e.f)({})),rx=w.fn([A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_add(__tsover_sub(he(m(t.x,t.z)),s),o),i=__tsover_add(__tsover_sub(re(t.y),r),o),l=ye(B(a,i),0),c=he(m(B(a,0),B(i,0)));return __tsover_sub(__tsover_add(l,c),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"h\"},{type:\"i\",name:\"rounding\"}],body:[0,[[13,\"dx\",[1,[1,[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]],\"-\",\"r\"],\"+\",\"rounding\"]],[13,\"dy\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"y\"]]],\"-\",\"h\"],\"+\",\"rounding\"]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"dx\",\"dy\"]],[5,\"0\"]]]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"dx\",[5,\"0\"]]],[6,\"std.max\",[\"dy\",[5,\"0\"]]]]]]]],[10,[1,[1,\"inner\",\"+\",\"outer\"],\"-\",\"rounding\"]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.max\":()=>B}})&&e.f)({})),ox=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=G(t.y,__tsover_mul(r,-1),r);return __tsover_sub(he(A(t.x,__tsover_sub(t.y,o),t.z)),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"cyc\",[6,\"std.clamp\",[[7,\"p\",\"y\"],[1,\"h\",\"*\",[4,\"-\",[5,\"1\"]]],\"h\"]]],[10,[1,[6,\"std.length\",[[6,\"d.vec3f\",[[7,\"p\",\"x\"],[1,[7,\"p\",\"y\"],\"-\",\"cyc\"],[7,\"p\",\"z\"]]]]],\"-\",\"r\"]]]]},externals:{\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec3f\":()=>A}})&&e.f)({})),ax=w.fn([A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=he(m(t.x,t.z)),i=t.y,l=__tsover_sub(r,s),c=__tsover_mul(o,2),d=__tsover_sub(a,ye(a,N(r,s,i<0))),u=__tsover_sub(re(i),o),p=__tsover_add(__tsover_mul(__tsover_sub(r,a),l),__tsover_mul(__tsover_sub(o,i),c)),_=B(__tsover_add(__tsover_mul(l,l),__tsover_mul(c,c)),1e-6),f=G(__tsover_div(p,_),0,1),h=__tsover_add(__tsover_sub(a,r),__tsover_mul(l,f)),g=__tsover_add(__tsover_sub(i,o),__tsover_mul(c,f)),y=N(n(1),n(-1),u<0),b=N(n(1),y,h<0),x=__tsover_add(__tsover_mul(d,d),__tsover_mul(u,u)),P=__tsover_add(__tsover_mul(h,h),__tsover_mul(g,g));return __tsover_mul(b,Ue(ye(x,P)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r1\"},{type:\"i\",name:\"r2\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"qx\",[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]]],[13,\"qy\",[7,\"p\",\"y\"]],[13,\"k2x\",[1,\"r2\",\"-\",\"r1\"]],[13,\"k2y\",[1,\"h\",\"*\",[5,\"2\"]]],[13,\"cax\",[1,\"qx\",\"-\",[6,\"std.min\",[\"qx\",[6,\"std.select\",[\"r2\",\"r1\",[1,\"qy\",\"<\",[5,\"0\"]]]]]]]],[13,\"cay\",[1,[6,\"std.abs\",[\"qy\"]],\"-\",\"h\"]],[13,\"dotNum\",[1,[1,[1,\"r2\",\"-\",\"qx\"],\"*\",\"k2x\"],\"+\",[1,[1,\"h\",\"-\",\"qy\"],\"*\",\"k2y\"]]],[13,\"dotDen\",[6,\"std.max\",[[1,[1,\"k2x\",\"*\",\"k2x\"],\"+\",[1,\"k2y\",\"*\",\"k2y\"]],[5,\"0.000001\"]]]],[13,\"t\",[6,\"std.clamp\",[[1,\"dotNum\",\"/\",\"dotDen\"],[5,\"0\"],[5,\"1\"]]]],[13,\"cbx\",[1,[1,\"qx\",\"-\",\"r2\"],\"+\",[1,\"k2x\",\"*\",\"t\"]]],[13,\"cby\",[1,[1,\"qy\",\"-\",\"h\"],\"+\",[1,\"k2y\",\"*\",\"t\"]]],[13,\"sInner\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[1,\"cay\",\"<\",[5,\"0\"]]]]],[13,\"s\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"sInner\",[1,\"cbx\",\"<\",[5,\"0\"]]]]],[13,\"dca\",[1,[1,\"cax\",\"*\",\"cax\"],\"+\",[1,\"cay\",\"*\",\"cay\"]]],[13,\"dcb\",[1,[1,\"cbx\",\"*\",\"cbx\"],\"+\",[1,\"cby\",\"*\",\"cby\"]]],[10,[1,\"s\",\"*\",[6,\"std.sqrt\",[[6,\"std.min\",[\"dca\",\"dcb\"]]]]]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.min\":()=>ye,\"std.select\":()=>N,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.sqrt\":()=>Ue}})&&e.f)({})),nx=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(r,.5),a=Ue(__tsover_add(__tsover_mul(r,r),__tsover_mul(s,s))),i=__tsover_div(__tsover_add(__tsover_mul(r,re(t.x)),__tsover_mul(s,__tsover_sub(t.y,o))),a),l=__tsover_div(__tsover_add(__tsover_mul(r,re(t.z)),__tsover_mul(s,__tsover_sub(t.y,o))),a),c=__tsover_sub(__tsover_mul(t.y,-1),o);return B(B(i,l),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"ht\"}],body:[0,[[13,\"h2\",[1,\"ht\",\"*\",[5,\"0.5\"]]],[13,\"denom\",[6,\"std.sqrt\",[[1,[1,\"ht\",\"*\",\"ht\"],\"+\",[1,\"b\",\"*\",\"b\"]]]]],[13,\"dx\",[1,[1,[1,\"ht\",\"*\",[6,\"std.abs\",[[7,\"p\",\"x\"]]]],\"+\",[1,\"b\",\"*\",[1,[7,\"p\",\"y\"],\"-\",\"h2\"]]],\"/\",\"denom\"]],[13,\"dz\",[1,[1,[1,\"ht\",\"*\",[6,\"std.abs\",[[7,\"p\",\"z\"]]]],\"+\",[1,\"b\",\"*\",[1,[7,\"p\",\"y\"],\"-\",\"h2\"]]],\"/\",\"denom\"]],[13,\"dBase\",[1,[1,[7,\"p\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],\"-\",\"h2\"]],[10,[6,\"std.max\",[[6,\"std.max\",[\"dx\",\"dz\"]],\"dBase\"]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.abs\":()=>re,\"std.max\":()=>B}})&&e.f)({})),ix=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=-.8660254,a=.5,i=.57735,l=re(t.x),c=re(t.y),d=re(t.z),u=__tsover_mul(ye(__tsover_add(__tsover_mul(o,l),__tsover_mul(a,c)),0),2),p=__tsover_sub(l,__tsover_mul(u,o)),_=__tsover_sub(c,__tsover_mul(u,a)),f=G(p,__tsover_mul(__tsover_mul(i,s),-1),__tsover_mul(i,s)),h=__tsover_sub(p,f),g=__tsover_sub(_,s),y=__tsover_mul(Ue(__tsover_add(__tsover_mul(h,h),__tsover_mul(g,g))),no(__tsover_sub(_,s))),b=__tsover_sub(d,r),x=ye(B(y,b),0),P=he(m(B(y,0),B(b,0)));return __tsover_add(x,P)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"he\"}],body:[0,[[13,\"kx\",[4,\"-\",[5,\"0.8660254\"]]],[13,\"ky\",[5,\"0.5\"]],[13,\"kz\",[5,\"0.57735\"]],[13,\"ax\",[6,\"std.abs\",[[7,\"p\",\"x\"]]]],[13,\"ay\",[6,\"std.abs\",[[7,\"p\",\"y\"]]]],[13,\"az\",[6,\"std.abs\",[[7,\"p\",\"z\"]]]],[13,\"dk\",[1,[6,\"std.min\",[[1,[1,\"kx\",\"*\",\"ax\"],\"+\",[1,\"ky\",\"*\",\"ay\"]],[5,\"0\"]]],\"*\",[5,\"2\"]]],[13,\"px\",[1,\"ax\",\"-\",[1,\"dk\",\"*\",\"kx\"]]],[13,\"py\",[1,\"ay\",\"-\",[1,\"dk\",\"*\",\"ky\"]]],[13,\"cx\",[6,\"std.clamp\",[\"px\",[1,[1,\"kz\",\"*\",\"r\"],\"*\",[4,\"-\",[5,\"1\"]]],[1,\"kz\",\"*\",\"r\"]]]],[13,\"ex\",[1,\"px\",\"-\",\"cx\"]],[13,\"ey\",[1,\"py\",\"-\",\"r\"]],[13,\"dHex\",[1,[6,\"std.sqrt\",[[1,[1,\"ex\",\"*\",\"ex\"],\"+\",[1,\"ey\",\"*\",\"ey\"]]]],\"*\",[6,\"std.sign\",[[1,\"py\",\"-\",\"r\"]]]]],[13,\"dZ\",[1,\"az\",\"-\",\"he\"]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"dHex\",\"dZ\"]],[5,\"0\"]]]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"dHex\",[5,\"0\"]]],[6,\"std.max\",[\"dZ\",[5,\"0\"]]]]]]]],[10,[1,\"inner\",\"+\",\"outer\"]]]]},externals:{\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.clamp\":()=>G,\"std.sqrt\":()=>Ue,\"std.sign\":()=>no,\"std.max\":()=>B,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})),lx=w.fn([A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=he(A(__tsover_div(t.x,s),__tsover_div(t.y,r),__tsover_div(t.z,o))),i=he(A(__tsover_div(t.x,__tsover_mul(s,s)),__tsover_div(t.y,__tsover_mul(r,r)),__tsover_div(t.z,__tsover_mul(o,o))));return __tsover_div(__tsover_mul(a,__tsover_sub(a,1)),B(i,1e-6))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rx\"},{type:\"i\",name:\"ry\"},{type:\"i\",name:\"rz\"}],body:[0,[[13,\"k0\",[6,\"std.length\",[[6,\"d.vec3f\",[[1,[7,\"p\",\"x\"],\"/\",\"rx\"],[1,[7,\"p\",\"y\"],\"/\",\"ry\"],[1,[7,\"p\",\"z\"],\"/\",\"rz\"]]]]]],[13,\"k1\",[6,\"std.length\",[[6,\"d.vec3f\",[[1,[7,\"p\",\"x\"],\"/\",[1,\"rx\",\"*\",\"rx\"]],[1,[7,\"p\",\"y\"],\"/\",[1,\"ry\",\"*\",\"ry\"]],[1,[7,\"p\",\"z\"],\"/\",[1,\"rz\",\"*\",\"rz\"]]]]]]],[10,[1,[1,\"k0\",\"*\",[1,\"k0\",\"-\",[5,\"1\"]]],\"/\",[6,\"std.max\",[\"k1\",[5,\"0.000001\"]]]]]]]},externals:{\"std.length\":()=>he,\"d.vec3f\":()=>A,\"std.max\":()=>B}})&&e.f)({})),Zm=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=he(m(t.x,t.z)),a=re(t.y),i=__tsover_mul(s,-1),l=r,c=__tsover_sub(o,s),d=a,u=B(__tsover_add(__tsover_mul(i,i),__tsover_mul(l,l)),1e-6),p=G(__tsover_div(__tsover_add(__tsover_mul(c,i),__tsover_mul(d,l)),u),0,1),_=__tsover_sub(c,__tsover_mul(i,p)),f=__tsover_sub(d,__tsover_mul(l,p)),h=Ue(__tsover_add(__tsover_mul(_,_),__tsover_mul(f,f)));return __tsover_mul(h,no(__tsover_sub(__tsover_add(__tsover_mul(r,o),__tsover_mul(s,a)),__tsover_mul(s,r))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"qx\",[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]]],[13,\"qy\",[6,\"std.abs\",[[7,\"p\",\"y\"]]]],[13,\"abx\",[1,\"r\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"aby\",\"h\"],[13,\"apx\",[1,\"qx\",\"-\",\"r\"]],[13,\"apy\",\"qy\"],[13,\"den\",[6,\"std.max\",[[1,[1,\"abx\",\"*\",\"abx\"],\"+\",[1,\"aby\",\"*\",\"aby\"]],[5,\"0.000001\"]]]],[13,\"t\",[6,\"std.clamp\",[[1,[1,[1,\"apx\",\"*\",\"abx\"],\"+\",[1,\"apy\",\"*\",\"aby\"]],\"/\",\"den\"],[5,\"0\"],[5,\"1\"]]]],[13,\"dxx\",[1,\"apx\",\"-\",[1,\"abx\",\"*\",\"t\"]]],[13,\"dyy\",[1,\"apy\",\"-\",[1,\"aby\",\"*\",\"t\"]]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"dxx\",\"*\",\"dxx\"],\"+\",[1,\"dyy\",\"*\",\"dyy\"]]]]],[10,[1,\"dist\",\"*\",[6,\"std.sign\",[[1,[1,[1,\"h\",\"*\",\"qx\"],\"+\",[1,\"r\",\"*\",\"qy\"]],\"-\",[1,\"r\",\"*\",\"h\"]]]]]]]]},externals:{\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.sqrt\":()=>Ue,\"std.sign\":()=>no}})&&e.f)({})),cx=w.fn([A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=B(__tsover_sub(re(t.y),s),0),i=__tsover_sub(he(m(t.x,a)),r);return __tsover_sub(he(m(i,t.z)),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"le\"},{type:\"i\",name:\"r1\"},{type:\"i\",name:\"r2\"}],body:[0,[[13,\"qy\",[6,\"std.max\",[[1,[6,\"std.abs\",[[7,\"p\",\"y\"]]],\"-\",\"le\"],[5,\"0\"]]]],[13,\"ring\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],\"qy\"]]]],\"-\",\"r1\"]],[10,[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"ring\",[7,\"p\",\"z\"]]]]],\"-\",\"r2\"]]]]},externals:{\"std.max\":()=>B,\"std.abs\":()=>re,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})),dx=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=G(__tsover_add(__tsover_mul(__tsover_div(__tsover_sub(s,t),r),.5),.5),0,1);return __tsover_sub(K(s,t,o),__tsover_mul(__tsover_mul(r,o),__tsover_sub(1,o)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"a\"},{type:\"i\",name:\"b\"},{type:\"i\",name:\"k\"}],body:[0,[[13,\"h\",[6,\"std.clamp\",[[1,[1,[1,[1,\"b\",\"-\",\"a\"],\"/\",\"k\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[10,[1,[6,\"std.mix\",[\"b\",\"a\",\"h\"]],\"-\",[1,[1,\"k\",\"*\",\"h\"],\"*\",[1,[5,\"1\"],\"-\",\"h\"]]]]]]},externals:{\"std.clamp\":()=>G,\"std.mix\":()=>K}})&&e.f)({})),ux=w.fn([A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=Zm(t,s,r),i=he(m(t.x,t.z)),l=Xt(t.z,t.x),c=__tsover_div(sd,o),d=ie(__tsover_add(__tsover_div(l,c),.5)),u=__tsover_sub(l,__tsover_mul(d,c)),p=__tsover_mul(s,pe(__tsover_div(vP,o)));return B(B(a,__tsover_sub(__tsover_mul(i,pe(u)),p)),__tsover_sub(t.y,__tsover_mul(r,.45)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"h\"},{type:\"i\",name:\"sides\"}],body:[0,[[13,\"base\",[6,\"sdBicone\",[\"p\",\"r\",\"h\"]]],[13,\"lenXZ\",[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]]],[13,\"ang\",[6,\"std.atan2\",[[7,\"p\",\"z\"],[7,\"p\",\"x\"]]]],[13,\"sector\",[1,\"TWO_PI\",\"/\",\"sides\"]],[13,\"idx\",[6,\"std.floor\",[[1,[1,\"ang\",\"/\",\"sector\"],\"+\",[5,\"0.5\"]]]]],[13,\"ca\",[1,\"ang\",\"-\",[1,\"idx\",\"*\",\"sector\"]]],[13,\"ri\",[1,\"r\",\"*\",[6,\"std.cos\",[[1,\"PI\",\"/\",\"sides\"]]]]],[13,\"facet\",[1,[1,\"lenXZ\",\"*\",[6,\"std.cos\",[\"ca\"]]],\"-\",\"ri\"]],[13,\"dd\",[6,\"std.max\",[\"base\",\"facet\"]]],[13,\"table\",[1,[7,\"p\",\"y\"],\"-\",[1,\"h\",\"*\",[5,\"0.45\"]]]],[10,[6,\"std.max\",[\"dd\",\"table\"]]]]]},externals:{sdBicone:()=>Zm,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.atan2\":()=>Xt,TWO_PI:()=>sd,\"std.floor\":()=>ie,\"std.cos\":()=>pe,PI:()=>vP,\"std.max\":()=>B}})&&e.f)({})),px=w.fn([A,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Xt(t.z,t.x),l=ie(__tsover_add(__tsover_sub(__tsover_div(t.y,o),__tsover_div(i,sd)),.5)),c=__tsover_add(i,__tsover_mul(sd,l)),d=__tsover_mul(s,pe(c)),u=__tsover_div(__tsover_mul(o,c),sd),p=__tsover_mul(s,ae(c));return B(__tsover_sub(he(A(__tsover_sub(t.x,d),__tsover_sub(t.y,u),__tsover_sub(t.z,p))),r),__tsover_sub(re(t.y),__tsover_mul(__tsover_mul(o,a),.5)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"R\"},{type:\"i\",name:\"tube\"},{type:\"i\",name:\"pitch\"},{type:\"i\",name:\"turns\"}],body:[0,[[13,\"psi\",[6,\"std.atan2\",[[7,\"p\",\"z\"],[7,\"p\",\"x\"]]]],[13,\"k\",[6,\"std.floor\",[[1,[1,[1,[7,\"p\",\"y\"],\"/\",\"pitch\"],\"-\",[1,\"psi\",\"/\",\"TWO_PI\"]],\"+\",[5,\"0.5\"]]]]],[13,\"theta\",[1,\"psi\",\"+\",[1,\"TWO_PI\",\"*\",\"k\"]]],[13,\"ccx\",[1,\"R\",\"*\",[6,\"std.cos\",[\"theta\"]]]],[13,\"ccy\",[1,[1,\"pitch\",\"*\",\"theta\"],\"/\",\"TWO_PI\"]],[13,\"ccz\",[1,\"R\",\"*\",[6,\"std.sin\",[\"theta\"]]]],[13,\"dStrand\",[1,[6,\"std.length\",[[6,\"d.vec3f\",[[1,[7,\"p\",\"x\"],\"-\",\"ccx\"],[1,[7,\"p\",\"y\"],\"-\",\"ccy\"],[1,[7,\"p\",\"z\"],\"-\",\"ccz\"]]]]],\"-\",\"tube\"]],[13,\"slab\",[1,[6,\"std.abs\",[[7,\"p\",\"y\"]]],\"-\",[1,[1,\"pitch\",\"*\",\"turns\"],\"*\",[5,\"0.5\"]]]],[10,[6,\"std.max\",[\"dStrand\",\"slab\"]]]]]},externals:{\"std.atan2\":()=>Xt,\"std.floor\":()=>ie,TWO_PI:()=>sd,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.length\":()=>he,\"d.vec3f\":()=>A,\"std.abs\":()=>re,\"std.max\":()=>B}})&&e.f)({})),hP=Fe(A,8),gP=8,mx=w.fn([A,A,A,A,A,A,A,A,A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=hP();_[0]=A(s),_[1]=A(r),_[2]=A(o),_[3]=A(a),_[4]=A(i),_[5]=A(l),_[6]=A(c),_[7]=A(d);let f=__tsover_sub(he(t.sub(_[0])),u);for(let h=1;h<gP;h++)f=dx(f,__tsover_sub(he(t.sub(_[h])),u),p);return f}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"b0\"},{type:\"i\",name:\"b1\"},{type:\"i\",name:\"b2\"},{type:\"i\",name:\"b3\"},{type:\"i\",name:\"b4\"},{type:\"i\",name:\"b5\"},{type:\"i\",name:\"b6\"},{type:\"i\",name:\"b7\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"k\"}],body:[0,[[13,\"balls\",[6,\"MetaballArray\",[]]],[2,[8,\"balls\",[5,\"0\"]],\"=\",[6,\"d.vec3f\",[\"b0\"]]],[2,[8,\"balls\",[5,\"1\"]],\"=\",[6,\"d.vec3f\",[\"b1\"]]],[2,[8,\"balls\",[5,\"2\"]],\"=\",[6,\"d.vec3f\",[\"b2\"]]],[2,[8,\"balls\",[5,\"3\"]],\"=\",[6,\"d.vec3f\",[\"b3\"]]],[2,[8,\"balls\",[5,\"4\"]],\"=\",[6,\"d.vec3f\",[\"b4\"]]],[2,[8,\"balls\",[5,\"5\"]],\"=\",[6,\"d.vec3f\",[\"b5\"]]],[2,[8,\"balls\",[5,\"6\"]],\"=\",[6,\"d.vec3f\",[\"b6\"]]],[2,[8,\"balls\",[5,\"7\"]],\"=\",[6,\"d.vec3f\",[\"b7\"]]],[12,\"dd\",[1,[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[8,\"balls\",[5,\"0\"]]]]]],\"-\",\"r\"]],[14,[12,\"i\",[5,\"1\"]],[1,\"i\",\"<\",\"METABALL_COUNT\"],[102,\"++\",\"i\"],[0,[[2,\"dd\",\"=\",[6,\"smin\",[\"dd\",[1,[6,\"std.length\",[[6,[7,\"p\",\"sub\"],[[8,\"balls\",\"i\"]]]]],\"-\",\"r\"],\"k\"]]]]]],[10,\"dd\"]]]},externals:{MetaballArray:()=>hP,\"d.vec3f\":()=>A,\"std.length\":()=>he,METABALL_COUNT:()=>gP,smin:()=>dx}})&&e.f)({})),_x=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=A(re(t.x),re(t.y),re(t.z)),o=Se(r,A(0,.5257311,.8506508)),a=Se(r,A(.8506508,0,.5257311)),i=Se(r,A(.5257311,.8506508,0));return __tsover_sub(B(B(o,a),i),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"}],body:[0,[[13,\"q\",[6,\"d.vec3f\",[[6,\"std.abs\",[[7,\"p\",\"x\"]]],[6,\"std.abs\",[[7,\"p\",\"y\"]]],[6,\"std.abs\",[[7,\"p\",\"z\"]]]]]],[13,\"a\",[6,\"std.dot\",[\"q\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0.5257311\"],[5,\"0.8506508\"]]]]]],[13,\"b\",[6,\"std.dot\",[\"q\",[6,\"d.vec3f\",[[5,\"0.8506508\"],[5,\"0\"],[5,\"0.5257311\"]]]]]],[13,\"c\",[6,\"std.dot\",[\"q\",[6,\"d.vec3f\",[[5,\"0.5257311\"],[5,\"0.8506508\"],[5,\"0\"]]]]]],[10,[1,[6,\"std.max\",[[6,\"std.max\",[\"a\",\"b\"]],\"c\"]],\"-\",\"r\"]]]]},externals:{\"d.vec3f\":()=>A,\"std.abs\":()=>re,\"std.dot\":()=>Se,\"std.max\":()=>B}})&&e.f)({})),fx=w.fn([A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Ue(B(__tsover_sub(__tsover_mul(s,s),__tsover_mul(r,r)),0)),a=he(m(t.x,t.z)),i=t.y,l=B(__tsover_add(__tsover_mul(__tsover_mul(__tsover_sub(r,s),a),a),__tsover_mul(__tsover_mul(o,o),__tsover_sub(__tsover_add(r,s),__tsover_mul(i,2)))),__tsover_sub(__tsover_mul(r,a),__tsover_mul(o,i))),c=__tsover_sub(he(m(a,i)),s),d=__tsover_sub(r,i);return N(N(he(m(__tsover_sub(a,o),__tsover_sub(i,r))),d,a<o),c,l<0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"w\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[1,\"r\",\"*\",\"r\"],\"-\",[1,\"h\",\"*\",\"h\"]],[5,\"0\"]]]]]],[13,\"qx\",[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],[7,\"p\",\"z\"]]]]]],[13,\"qy\",[7,\"p\",\"y\"]],[13,\"s\",[6,\"std.max\",[[1,[1,[1,[1,\"h\",\"-\",\"r\"],\"*\",\"qx\"],\"*\",\"qx\"],\"+\",[1,[1,\"w\",\"*\",\"w\"],\"*\",[1,[1,\"h\",\"+\",\"r\"],\"-\",[1,\"qy\",\"*\",[5,\"2\"]]]]],[1,[1,\"h\",\"*\",\"qx\"],\"-\",[1,\"w\",\"*\",\"qy\"]]]]],[13,\"case0\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"qx\",\"qy\"]]]],\"-\",\"r\"]],[13,\"case1\",[1,\"h\",\"-\",\"qy\"]],[13,\"case2\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,\"qx\",\"-\",\"w\"],[1,\"qy\",\"-\",\"h\"]]]]]],[13,\"inner\",[6,\"std.select\",[\"case2\",\"case1\",[1,\"qx\",\"<\",\"w\"]]]],[10,[6,\"std.select\",[\"inner\",\"case0\",[1,\"s\",\"<\",[5,\"0\"]]]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.select\":()=>N}})&&e.f)({})),vx=w.fn([A,n,n,n,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=__tsover_mul(a,ae(__tsover_add(__tsover_mul(i,t.x),l))),f=__tsover_mul(__tsover_mul(a,.6),ae(__tsover_add(__tsover_mul(__tsover_mul(i,.8),t.x),c))),h=__tsover_sub(t.y,_),g=__tsover_sub(t.z,f),y=__tsover_add(__tsover_mul(d,t.x),u),b=pe(y),x=ae(y),P=__tsover_add(__tsover_mul(h,b),__tsover_mul(g,x)),C=__tsover_sub(__tsover_mul(g,b),__tsover_mul(h,x)),E=__tsover_mul(r,.4),R=__tsover_add(__tsover_sub(re(P),s),E),U=__tsover_add(__tsover_sub(re(C),r),E),L=__tsover_sub(__tsover_add(he(m(B(R,0),B(U,0))),ye(B(R,U),0)),E),k=__tsover_sub(re(t.x),o),D=__tsover_add(he(m(B(L,0),B(k,0))),ye(B(L,k),0));return __tsover_mul(D,p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"w\"},{type:\"i\",name:\"th\"},{type:\"i\",name:\"len\"},{type:\"i\",name:\"amp\"},{type:\"i\",name:\"freq\"},{type:\"i\",name:\"ph1\"},{type:\"i\",name:\"ph2\"},{type:\"i\",name:\"twRate\"},{type:\"i\",name:\"twPh\"},{type:\"i\",name:\"lip\"}],body:[0,[[13,\"yc\",[1,\"amp\",\"*\",[6,\"std.sin\",[[1,[1,\"freq\",\"*\",[7,\"p\",\"x\"]],\"+\",\"ph1\"]]]]],[13,\"zc\",[1,[1,\"amp\",\"*\",[5,\"0.6\"]],\"*\",[6,\"std.sin\",[[1,[1,[1,\"freq\",\"*\",[5,\"0.8\"]],\"*\",[7,\"p\",\"x\"]],\"+\",\"ph2\"]]]]],[13,\"qy\",[1,[7,\"p\",\"y\"],\"-\",\"yc\"]],[13,\"qz\",[1,[7,\"p\",\"z\"],\"-\",\"zc\"]],[13,\"ang\",[1,[1,\"twRate\",\"*\",[7,\"p\",\"x\"]],\"+\",\"twPh\"]],[13,\"c\",[6,\"std.cos\",[\"ang\"]]],[13,\"s\",[6,\"std.sin\",[\"ang\"]]],[13,\"ry\",[1,[1,\"qy\",\"*\",\"c\"],\"+\",[1,\"qz\",\"*\",\"s\"]]],[13,\"rz\",[1,[1,\"qz\",\"*\",\"c\"],\"-\",[1,\"qy\",\"*\",\"s\"]]],[13,\"round\",[1,\"th\",\"*\",[5,\"0.4\"]]],[13,\"bx\",[1,[1,[6,\"std.abs\",[\"ry\"]],\"-\",\"w\"],\"+\",\"round\"]],[13,\"by\",[1,[1,[6,\"std.abs\",[\"rz\"]],\"-\",\"th\"],\"+\",\"round\"]],[13,\"d2\",[1,[1,[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"bx\",[5,\"0\"]]],[6,\"std.max\",[\"by\",[5,\"0\"]]]]]]],\"+\",[6,\"std.min\",[[6,\"std.max\",[\"bx\",\"by\"]],[5,\"0\"]]]],\"-\",\"round\"]],[13,\"dx\",[1,[6,\"std.abs\",[[7,\"p\",\"x\"]]],\"-\",\"len\"]],[13,\"dd\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"d2\",[5,\"0\"]]],[6,\"std.max\",[\"dx\",[5,\"0\"]]]]]]],\"+\",[6,\"std.min\",[[6,\"std.max\",[\"d2\",\"dx\"]],[5,\"0\"]]]]],[10,[1,\"dd\",\"*\",\"lip\"]]]]},externals:{\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.abs\":()=>re,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})),hx=w.fn([A,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(t.x,o),a)),ae(__tsover_add(__tsover_mul(__tsover_mul(t.y,o),1.3),i))),__tsover_mul(ae(__tsover_add(__tsover_mul(__tsover_mul(t.y,o),.8),__tsover_mul(i,1.1))),ae(__tsover_add(__tsover_mul(__tsover_mul(t.z,o),1.1),__tsover_mul(a,.9))))),d=__tsover_sub(__tsover_sub(he(t),s),__tsover_mul(__tsover_mul(r,c),.5));return __tsover_mul(d,l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"r\"},{type:\"i\",name:\"amp\"},{type:\"i\",name:\"freq\"},{type:\"i\",name:\"ph1\"},{type:\"i\",name:\"ph2\"},{type:\"i\",name:\"lip\"}],body:[0,[[13,\"w\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"p\",\"x\"],\"*\",\"freq\"],\"+\",\"ph1\"]]],\"*\",[6,\"std.sin\",[[1,[1,[1,[7,\"p\",\"y\"],\"*\",\"freq\"],\"*\",[5,\"1.3\"]],\"+\",\"ph2\"]]]],\"+\",[1,[6,\"std.sin\",[[1,[1,[1,[7,\"p\",\"y\"],\"*\",\"freq\"],\"*\",[5,\"0.8\"]],\"+\",[1,\"ph2\",\"*\",[5,\"1.1\"]]]]],\"*\",[6,\"std.sin\",[[1,[1,[1,[7,\"p\",\"z\"],\"*\",\"freq\"],\"*\",[5,\"1.1\"]],\"+\",[1,\"ph1\",\"*\",[5,\"0.9\"]]]]]]]],[13,\"dd\",[1,[1,[6,\"std.length\",[\"p\"]],\"-\",\"r\"],\"-\",[1,[1,\"amp\",\"*\",\"w\"],\"*\",[5,\"0.5\"]]]],[10,[1,\"dd\",\"*\",\"lip\"]]]]},externals:{\"std.sin\":()=>ae,\"std.length\":()=>he}})&&e.f)({})),Am=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_add(__tsover_sub(re(__tsover_sub(t,r)),o),i),c=__tsover_add(__tsover_sub(re(s),a),i),d=he(m(B(l,0),B(c,0))),u=ye(B(l,c),0);return __tsover_sub(__tsover_add(d,u),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"radialDist\"},{type:\"i\",name:\"axialDist\"},{type:\"i\",name:\"R\"},{type:\"i\",name:\"ht\"},{type:\"i\",name:\"hw\"},{type:\"i\",name:\"rnd\"}],body:[0,[[13,\"bx\",[1,[1,[6,\"std.abs\",[[1,\"radialDist\",\"-\",\"R\"]]],\"-\",\"ht\"],\"+\",\"rnd\"]],[13,\"by\",[1,[1,[6,\"std.abs\",[\"axialDist\"]],\"-\",\"hw\"],\"+\",\"rnd\"]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"bx\",[5,\"0\"]]],[6,\"std.max\",[\"by\",[5,\"0\"]]]]]]]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"bx\",\"by\"]],[5,\"0\"]]]],[10,[1,[1,\"outer\",\"+\",\"inner\"],\"-\",\"rnd\"]]]]},externals:{\"std.abs\":()=>re,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})),gx=w.fn([A,n,n,n,n,n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_)=>{\"use gpu\";let f=__tsover_mul(r,.5),h=__tsover_mul(o,.5),g=__tsover_sub(he(t),i),y=__tsover_add(__tsover_mul(t.y,l),__tsover_mul(t.z,c)),b=__tsover_sub(__tsover_mul(t.z,l),__tsover_mul(t.y,c)),x=Am(he(m(t.x,b)),y,s,h,f,a),P=__tsover_sub(__tsover_mul(t.x,d),__tsover_mul(t.z,u)),C=__tsover_add(__tsover_mul(t.z,d),__tsover_mul(t.x,u)),E=Am(he(m(P,t.y)),C,__tsover_mul(s,.78),h,f,a),R=__tsover_add(__tsover_mul(t.x,p),__tsover_mul(t.y,_)),U=Am(he(m(__tsover_sub(__tsover_mul(t.y,p),__tsover_mul(t.x,_)),t.z)),R,__tsover_mul(s,.56),h,f,a);return ye(ye(g,x),ye(E,U))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"width\"},{type:\"i\",name:\"thick\"},{type:\"i\",name:\"rnd\"},{type:\"i\",name:\"core\"},{type:\"i\",name:\"c1\"},{type:\"i\",name:\"s1\"},{type:\"i\",name:\"c2\"},{type:\"i\",name:\"s2\"},{type:\"i\",name:\"c3\"},{type:\"i\",name:\"s3\"}],body:[0,[[13,\"hw\",[1,\"width\",\"*\",[5,\"0.5\"]]],[13,\"ht\",[1,\"thick\",\"*\",[5,\"0.5\"]]],[13,\"dCore\",[1,[6,\"std.length\",[\"p\"]],\"-\",\"core\"]],[13,\"p1y\",[1,[1,[7,\"p\",\"y\"],\"*\",\"c1\"],\"+\",[1,[7,\"p\",\"z\"],\"*\",\"s1\"]]],[13,\"p1z\",[1,[1,[7,\"p\",\"z\"],\"*\",\"c1\"],\"-\",[1,[7,\"p\",\"y\"],\"*\",\"s1\"]]],[13,\"d1\",[6,\"sdFlatBand\",[[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"p\",\"x\"],\"p1z\"]]]],\"p1y\",\"radius\",\"ht\",\"hw\",\"rnd\"]]],[13,\"p2x\",[1,[1,[7,\"p\",\"x\"],\"*\",\"c2\"],\"-\",[1,[7,\"p\",\"z\"],\"*\",\"s2\"]]],[13,\"p2z\",[1,[1,[7,\"p\",\"z\"],\"*\",\"c2\"],\"+\",[1,[7,\"p\",\"x\"],\"*\",\"s2\"]]],[13,\"d2\",[6,\"sdFlatBand\",[[6,\"std.length\",[[6,\"d.vec2f\",[\"p2x\",[7,\"p\",\"y\"]]]]],\"p2z\",[1,\"radius\",\"*\",[5,\"0.78\"]],\"ht\",\"hw\",\"rnd\"]]],[13,\"p3x\",[1,[1,[7,\"p\",\"x\"],\"*\",\"c3\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"s3\"]]],[13,\"p3y\",[1,[1,[7,\"p\",\"y\"],\"*\",\"c3\"],\"-\",[1,[7,\"p\",\"x\"],\"*\",\"s3\"]]],[13,\"d3\",[6,\"sdFlatBand\",[[6,\"std.length\",[[6,\"d.vec2f\",[\"p3y\",[7,\"p\",\"z\"]]]]],\"p3x\",[1,\"radius\",\"*\",[5,\"0.56\"]],\"ht\",\"hw\",\"rnd\"]]],[10,[6,\"std.min\",[[6,\"std.min\",[\"dCore\",\"d1\"]],[6,\"std.min\",[\"d2\",\"d3\"]]]]]]]},externals:{\"std.length\":()=>he,sdFlatBand:()=>Am,\"d.vec2f\":()=>m,\"std.min\":()=>ye}})&&e.f)({})),Ad=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cx:n,sx:n,cy:n,sy:n,cz:n,sz:n}),\"RotStruct\"),ja=w.fn([A,Ad],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_add(__tsover_mul(t.x,s.cy),__tsover_mul(t.z,s.sy)),o=__tsover_sub(__tsover_mul(t.z,s.cy),__tsover_mul(t.x,s.sy)),a=__tsover_sub(__tsover_mul(t.y,s.cx),__tsover_mul(o,s.sx)),i=__tsover_add(__tsover_mul(t.y,s.sx),__tsover_mul(o,s.cx));return A(__tsover_sub(__tsover_mul(r,s.cz),__tsover_mul(a,s.sz)),__tsover_add(__tsover_mul(r,s.sz),__tsover_mul(a,s.cz)),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"rot\"}],body:[0,[[13,\"x1\",[1,[1,[7,\"v\",\"x\"],\"*\",[7,\"rot\",\"cy\"]],\"+\",[1,[7,\"v\",\"z\"],\"*\",[7,\"rot\",\"sy\"]]]],[13,\"z1\",[1,[1,[7,\"v\",\"z\"],\"*\",[7,\"rot\",\"cy\"]],\"-\",[1,[7,\"v\",\"x\"],\"*\",[7,\"rot\",\"sy\"]]]],[13,\"y2\",[1,[1,[7,\"v\",\"y\"],\"*\",[7,\"rot\",\"cx\"]],\"-\",[1,\"z1\",\"*\",[7,\"rot\",\"sx\"]]]],[13,\"z2\",[1,[1,[7,\"v\",\"y\"],\"*\",[7,\"rot\",\"sx\"]],\"+\",[1,\"z1\",\"*\",[7,\"rot\",\"cx\"]]]],[13,\"x3\",[1,[1,\"x1\",\"*\",[7,\"rot\",\"cz\"]],\"-\",[1,\"y2\",\"*\",[7,\"rot\",\"sz\"]]]],[13,\"y3\",[1,[1,\"x1\",\"*\",[7,\"rot\",\"sz\"]],\"+\",[1,\"y2\",\"*\",[7,\"rot\",\"cz\"]]]],[10,[6,\"d.vec3f\",[\"x3\",\"y3\",\"z2\"]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({}));function fA(e,t=\"none\",s=\"span\"){let r=Rr()?64:96,o=Rr()?224:384,a=Rr()?140:220;return w.fn([m,Ad,n],S)((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c,d)=>{\"use gpu\";let u=A(__tsover_sub(l.x,.5),__tsover_sub(.5,l.y),-1.2),p=A(0,0,1),_=ja(u,c),f=ja(p,c),h=j(G(__tsover_mul(d,n(a)),n(r),n(o))),g=Se(_,f),y=Ue(B(__tsover_sub(Se(_,_),__tsover_mul(g,g)),0)),b=Ue(B(__tsover_sub(__tsover_mul(d,d),__tsover_mul(y,y)),0)),x=B(__tsover_sub(__tsover_mul(g,-1),b),0),P=__tsover_add(__tsover_add(__tsover_mul(g,-1),b),.01),C=x,E=x,R=n(10),U=n(0);for(let F=0;F<h;F++){let q=e(_.add(f.mul(C)));if(R=ye(R,q),q<0){U=n(1);break}if(C>P)break;E=C,C=__tsover_add(C,B(q,.002))}let L=R,k=n(0),D=n(0),O=__tsover_add(P,.4);if(U>.5){let F=E,q=C;for(let Q=0;Q<10;Q++){let te=__tsover_mul(__tsover_add(F,q),.5);e(_.add(f.mul(te)))>0?F=te:q=te}let Y=__tsover_mul(__tsover_add(F,q),.5),V=Y;if(s===\"firstLobe\"){let Q=q,te=__tsover_add(q,.002);for(let le=0;le<h;le++){let se=e(_.add(f.mul(te)));if(se>=0||(Q=te,te=__tsover_add(te,B(__tsover_mul(se,-1),.002)),te>P))break}let J=Q,oe=te;for(let le=0;le<10;le++){let se=__tsover_mul(__tsover_add(J,oe),.5);e(_.add(f.mul(se)))<0?J=se:oe=se}V=__tsover_mul(__tsover_add(J,oe),.5)}if(s===\"span\"){let Q=_.add(f.mul(P)),te=__tsover_add(__tsover_sub(P,x),.01),J=n(0),oe=n(0);for(let Te=0;Te<h;Te++){let de=e(Q.sub(f.mul(J)));if(de<0||J>te)break;oe=J,J=__tsover_add(J,B(de,.002))}let le=oe,se=J;for(let Te=0;Te<10;Te++){let de=__tsover_mul(__tsover_add(le,se),.5);e(Q.sub(f.mul(de)))>0?le=de:se=de}V=__tsover_sub(P,__tsover_mul(__tsover_add(le,se),.5))}let X=__tsover_mul(B(__tsover_sub(V,Y),0),.5);if(L=__tsover_mul(X,-1),O=Y,t===\"raw\"){let Q=_.add(f.mul(Y));k=__tsover_add(Q.x,__tsover_mul(Q.z,.7)),D=__tsover_add(Q.y,__tsover_mul(Q.z,.5))}if(t===\"triplanar\"){let Q=_.add(f.mul(Y)),te=.004,J=__tsover_sub(e(A(__tsover_add(Q.x,te),Q.y,Q.z)),e(A(__tsover_sub(Q.x,te),Q.y,Q.z))),oe=__tsover_sub(e(A(Q.x,__tsover_add(Q.y,te),Q.z)),e(A(Q.x,__tsover_sub(Q.y,te),Q.z))),le=__tsover_sub(e(A(Q.x,Q.y,__tsover_add(Q.z,te))),e(A(Q.x,Q.y,__tsover_sub(Q.z,te)))),se=re(J),Te=re(oe),de=re(le),xe=m(__tsover_add(Q.y,2.17),__tsover_add(Q.z,5.31)),Ae=m(__tsover_add(Q.z,7.73),__tsover_add(Q.x,1.13)),De=N(m(Q.x,Q.y),Ae,Te>=de),Ee=N(De,N(De,xe,se>=de),se>=Te);k=Ee.x,D=Ee.y}}return S(L,k,D,O)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fuv\"},{type:\"i\",name:\"rot\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"ro0\",[6,\"d.vec3f\",[[1,[7,\"fuv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[5,\"0.5\"],\"-\",[7,\"fuv\",\"y\"]],[4,\"-\",[5,\"1.2\"]]]]],[13,\"rd0\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"1\"]]]],[13,\"ro\",[6,\"rotateVec3\",[\"ro0\",\"rot\"]]],[13,\"rd\",[6,\"rotateVec3\",[\"rd0\",\"rot\"]]],[13,\"steps\",[6,\"d.i32\",[[6,\"std.clamp\",[[1,\"rBound\",\"*\",[6,\"d.f32\",[\"STEP_SCALE\"]]],[6,\"d.f32\",[\"STEP_MIN\"]],[6,\"d.f32\",[\"STEP_MAX\"]]]]]]],[13,\"b\",[6,\"std.dot\",[\"ro\",\"rd\"]]],[13,\"perp\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[6,\"std.dot\",[\"ro\",\"ro\"]],\"-\",[1,\"b\",\"*\",\"b\"]],[5,\"0\"]]]]]],[13,\"halfSpan\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[1,\"rBound\",\"*\",\"rBound\"],\"-\",[1,\"perp\",\"*\",\"perp\"]],[5,\"0\"]]]]]],[13,\"tStart\",[6,\"std.max\",[[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"-\",\"halfSpan\"],[5,\"0\"]]]],[13,\"tEnd\",[1,[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"+\",\"halfSpan\"],\"+\",[5,\"0.01\"]]],[12,\"t\",\"tStart\"],[12,\"tPrev\",\"tStart\"],[12,\"minD\",[6,\"d.f32\",[[5,\"10\"]]]],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"steps\"],[102,\"++\",\"i\"],[0,[[13,\"p\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"t\"]]]]],[13,\"dd\",[6,\"sdfFn\",[\"p\"]]],[2,\"minD\",\"=\",[6,\"std.min\",[\"minD\",\"dd\"]]],[11,[1,\"dd\",\"<\",[5,\"0\"]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[17]]]],[11,[1,\"t\",\">\",\"tEnd\"],[0,[[17]]]],[2,\"tPrev\",\"=\",\"t\"],[2,\"t\",\"=\",[1,\"t\",\"+\",[6,\"std.max\",[\"dd\",[5,\"0.002\"]]]]]]]],[12,\"fieldVal\",\"minD\"],[12,\"patternU\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"patternV\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"viewDepth\",[1,\"tEnd\",\"+\",[5,\"0.4\"]]],[11,[1,\"hit\",\">\",[5,\"0.5\"]],[0,[[12,\"eLo\",\"tPrev\"],[12,\"eHi\",\"t\"],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"10\"]],[102,\"++\",\"i\"],[0,[[13,\"mid\",[1,[1,\"eLo\",\"+\",\"eHi\"],\"*\",[5,\"0.5\"]]],[13,\"dm\",[6,\"sdfFn\",[[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"mid\"]]]]]]],[11,[1,\"dm\",\">\",[5,\"0\"]],[0,[[2,\"eLo\",\"=\",\"mid\"]]],[0,[[2,\"eHi\",\"=\",\"mid\"]]]]]]],[13,\"tEnter\",[1,[1,\"eLo\",\"+\",\"eHi\"],\"*\",[5,\"0.5\"]]],[12,\"tExit\",\"tEnter\"],[11,[1,\"chordMode\",\"===\",[103,\"firstLobe\"]],[0,[[12,\"sInPrev\",\"eHi\"],[12,\"sIn\",[1,\"eHi\",\"+\",[5,\"0.002\"]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"steps\"],[102,\"++\",\"i\"],[0,[[13,\"dd\",[6,\"sdfFn\",[[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"sIn\"]]]]]]],[11,[1,\"dd\",\">=\",[5,\"0\"]],[0,[[17]]]],[2,\"sInPrev\",\"=\",\"sIn\"],[2,\"sIn\",\"=\",[1,\"sIn\",\"+\",[6,\"std.max\",[[1,\"dd\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0.002\"]]]]],[11,[1,\"sIn\",\">\",\"tEnd\"],[0,[[17]]]]]]],[12,\"fLo\",\"sInPrev\"],[12,\"fHi\",\"sIn\"],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"10\"]],[102,\"++\",\"i\"],[0,[[13,\"mid\",[1,[1,\"fLo\",\"+\",\"fHi\"],\"*\",[5,\"0.5\"]]],[13,\"dm\",[6,\"sdfFn\",[[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"mid\"]]]]]]],[11,[1,\"dm\",\"<\",[5,\"0\"]],[0,[[2,\"fLo\",\"=\",\"mid\"]]],[0,[[2,\"fHi\",\"=\",\"mid\"]]]]]]],[2,\"tExit\",\"=\",[1,[1,\"fLo\",\"+\",\"fHi\"],\"*\",[5,\"0.5\"]]]]]],[11,[1,\"chordMode\",\"===\",[103,\"span\"]],[0,[[13,\"roB\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"tEnd\"]]]]],[13,\"span\",[1,[1,\"tEnd\",\"-\",\"tStart\"],\"+\",[5,\"0.01\"]]],[12,\"s\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"sPrev\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"steps\"],[102,\"++\",\"i\"],[0,[[13,\"p\",[6,[7,\"roB\",\"sub\"],[[6,[7,\"rd\",\"mul\"],[\"s\"]]]]],[13,\"dd\",[6,\"sdfFn\",[\"p\"]]],[11,[1,\"dd\",\"<\",[5,\"0\"]],[0,[[17]]]],[11,[1,\"s\",\">\",\"span\"],[0,[[17]]]],[2,\"sPrev\",\"=\",\"s\"],[2,\"s\",\"=\",[1,\"s\",\"+\",[6,\"std.max\",[\"dd\",[5,\"0.002\"]]]]]]]],[12,\"xLo\",\"sPrev\"],[12,\"xHi\",\"s\"],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"10\"]],[102,\"++\",\"i\"],[0,[[13,\"mid\",[1,[1,\"xLo\",\"+\",\"xHi\"],\"*\",[5,\"0.5\"]]],[13,\"dm\",[6,\"sdfFn\",[[6,[7,\"roB\",\"sub\"],[[6,[7,\"rd\",\"mul\"],[\"mid\"]]]]]]],[11,[1,\"dm\",\">\",[5,\"0\"]],[0,[[2,\"xLo\",\"=\",\"mid\"]]],[0,[[2,\"xHi\",\"=\",\"mid\"]]]]]]],[2,\"tExit\",\"=\",[1,\"tEnd\",\"-\",[1,[1,\"xLo\",\"+\",\"xHi\"],\"*\",[5,\"0.5\"]]]]]]],[13,\"halfChord\",[1,[6,\"std.max\",[[1,\"tExit\",\"-\",\"tEnter\"],[5,\"0\"]]],\"*\",[5,\"0.5\"]]],[2,\"fieldVal\",\"=\",[1,\"halfChord\",\"*\",[4,\"-\",[5,\"1\"]]]],[2,\"viewDepth\",\"=\",\"tEnter\"],[11,[1,\"patternMode\",\"===\",[103,\"raw\"]],[0,[[13,\"hp\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"tEnter\"]]]]],[2,\"patternU\",\"=\",[1,[7,\"hp\",\"x\"],\"+\",[1,[7,\"hp\",\"z\"],\"*\",[5,\"0.7\"]]]],[2,\"patternV\",\"=\",[1,[7,\"hp\",\"y\"],\"+\",[1,[7,\"hp\",\"z\"],\"*\",[5,\"0.5\"]]]]]]],[11,[1,\"patternMode\",\"===\",[103,\"triplanar\"]],[0,[[13,\"hp\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"tEnter\"]]]]],[13,\"e2\",[5,\"0.004\"]],[13,\"nlx\",[1,[6,\"sdfFn\",[[6,\"d.vec3f\",[[1,[7,\"hp\",\"x\"],\"+\",\"e2\"],[7,\"hp\",\"y\"],[7,\"hp\",\"z\"]]]]],\"-\",[6,\"sdfFn\",[[6,\"d.vec3f\",[[1,[7,\"hp\",\"x\"],\"-\",\"e2\"],[7,\"hp\",\"y\"],[7,\"hp\",\"z\"]]]]]]],[13,\"nly\",[1,[6,\"sdfFn\",[[6,\"d.vec3f\",[[7,\"hp\",\"x\"],[1,[7,\"hp\",\"y\"],\"+\",\"e2\"],[7,\"hp\",\"z\"]]]]],\"-\",[6,\"sdfFn\",[[6,\"d.vec3f\",[[7,\"hp\",\"x\"],[1,[7,\"hp\",\"y\"],\"-\",\"e2\"],[7,\"hp\",\"z\"]]]]]]],[13,\"nlz\",[1,[6,\"sdfFn\",[[6,\"d.vec3f\",[[7,\"hp\",\"x\"],[7,\"hp\",\"y\"],[1,[7,\"hp\",\"z\"],\"+\",\"e2\"]]]]],\"-\",[6,\"sdfFn\",[[6,\"d.vec3f\",[[7,\"hp\",\"x\"],[7,\"hp\",\"y\"],[1,[7,\"hp\",\"z\"],\"-\",\"e2\"]]]]]]],[13,\"anx\",[6,\"std.abs\",[\"nlx\"]]],[13,\"anyv\",[6,\"std.abs\",[\"nly\"]]],[13,\"anz\",[6,\"std.abs\",[\"nlz\"]]],[13,\"branch1\",[6,\"d.vec2f\",[[1,[7,\"hp\",\"y\"],\"+\",[5,\"2.17\"]],[1,[7,\"hp\",\"z\"],\"+\",[5,\"5.31\"]]]]],[13,\"branch2\",[6,\"d.vec2f\",[[1,[7,\"hp\",\"z\"],\"+\",[5,\"7.73\"]],[1,[7,\"hp\",\"x\"],\"+\",[5,\"1.13\"]]]]],[13,\"branch3\",[6,\"d.vec2f\",[[7,\"hp\",\"x\"],[7,\"hp\",\"y\"]]]],[13,\"branch23\",[6,\"std.select\",[\"branch3\",\"branch2\",[1,\"anyv\",\">=\",\"anz\"]]]],[13,\"innerPick\",[6,\"std.select\",[\"branch23\",\"branch1\",[1,\"anx\",\">=\",\"anz\"]]]],[13,\"pattern\",[6,\"std.select\",[\"branch23\",\"innerPick\",[1,\"anx\",\">=\",\"anyv\"]]]],[2,\"patternU\",\"=\",[7,\"pattern\",\"x\"]],[2,\"patternV\",\"=\",[7,\"pattern\",\"y\"]]]]]]]],[10,[6,\"d.vec4f\",[\"fieldVal\",\"patternU\",\"patternV\",\"viewDepth\"]]]]]},externals:{\"d.vec3f\":()=>A,rotateVec3:()=>ja,\"d.i32\":()=>j,\"std.clamp\":()=>G,\"d.f32\":()=>n,STEP_SCALE:()=>a,STEP_MIN:()=>r,STEP_MAX:()=>o,\"std.dot\":()=>Se,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,sdfFn:()=>e,\"std.min\":()=>ye,chordMode:()=>s,patternMode:()=>t,\"std.abs\":()=>re,\"d.vec2f\":()=>m,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&i.f)({})).$name(\"raymarchedField\")}function cne(e,t=\"none\",s=\"span\"){return fA(e,t,s)}var Zh=\"rgba32float\",HB=1536;function E_(){return Rr()?768:HB}var dne=[256,320,384,512,640,768,896,1024,1280,1536];function vA(e,t,s,r,o,a){if(!(r>0)||!(s>0))return o;let i=Rr()?.5:1,l=Math.max(e,t)*s*r*i,c=o;for(let d of dne){if(d>o)break;if(d>=l){c=d;break}}return a>0&&c<a&&l>a*.78&&(c=Math.min(a,o)),c}var vl=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({rot:UM(16,OM(32,Ad)),rBound:n,spanX:n,spanY:n,originX:n,originY:n,activeRes:M,pA:n,pB:n,pC:n,pD:n,mb0:A,mb1:A,mb2:A,mb3:A,mb4:A,mb5:A,mb6:A,mb7:A}),\"MarchParams\");function hA(){return{layout:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({field:{storageTexture:zt(Zh,\"write-only\")},params:{uniform:vl}}),\"layout\"),MarchParams:vl}}function gA(e,t){return w.fn([M,M])((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o)=>{\"use gpu\";let a=e.$.params,i=n(a.activeRes),l=t(m(__tsover_add(a.originX,__tsover_mul(__tsover_div(__tsover_add(n(r),.5),i),a.spanX)),__tsover_add(a.originY,__tsover_mul(__tsover_div(__tsover_add(n(o),.5),i),a.spanY))),a.rot,a.rBound);lt(e.$.field,fe(r,o),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"activeResF\",[6,\"d.f32\",[[7,\"p\",\"activeRes\"]]]],[13,\"uv\",[6,\"d.vec2f\",[[1,[7,\"p\",\"originX\"],\"+\",[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"activeResF\"],\"*\",[7,\"p\",\"spanX\"]]],[1,[7,\"p\",\"originY\"],\"+\",[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"activeResF\"],\"*\",[7,\"p\",\"spanY\"]]]]]],[13,\"field\",[6,\"marchFn\",[\"uv\",[7,\"p\",\"rot\"],[7,\"p\",\"rBound\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"field\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"field\"]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,\"d.vec2f\":()=>m,marchFn:()=>t,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe}})&&s.f)({})).$name(\"volumetricFieldMarch\")}var yx=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({originX:n,originY:n,spanX:n,spanY:n,activeResF:n,rBound:n}),\"SampleParams\");function une(){let e=(globalThis.__TYPEGPU_AUTONAME__??(s=>s))(w.bindGroupLayout({field:{texture:Zt(n),sampleType:\"unfilterable-float\"},params:{uniform:yx}}),\"sampleLayout\"),t=w.fn([n,n],S)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o)=>{\"use gpu\";let a=__tsover_sub(e.$.params.activeResF,1),i=M(G(r,n(0),a)),l=M(G(o,n(0),a));return Ye(e.$.field,fe(i,l),0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"xi\"},{type:\"i\",name:\"yi\"}],body:[0,[[13,\"maxT\",[1,[7,[7,[7,\"sampleLayout\",\"$\"],\"params\"],\"activeResF\"],\"-\",[5,\"1\"]]],[13,\"tx\",[6,\"d.u32\",[[6,\"std.clamp\",[\"xi\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"ty\",[6,\"d.u32\",[[6,\"std.clamp\",[\"yi\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[10,[6,\"std.textureLoad\",[[7,[7,\"sampleLayout\",\"$\"],\"field\"],[6,\"d.vec2u\",[\"tx\",\"ty\"]],[5,\"0\"]]]]]]},externals:{sampleLayout:()=>e,\"d.u32\":()=>M,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe}})&&s.f)({})).$name(\"fieldFetchTexel\");return{sampleLayout:e,fieldSample:w.fn([m],S)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=e.$.params,a=G(r.x,o.originX,__tsover_add(o.originX,o.spanX)),i=G(r.y,o.originY,__tsover_add(o.originY,o.spanY)),l=he(m(__tsover_sub(r.x,a),__tsover_sub(r.y,i))),c=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(a,o.originX),o.spanX),o.activeResF),.5),d=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(i,o.originY),o.spanY),o.activeResF),.5),u=ie(c),p=ie(d),_=__tsover_sub(c,u),f=__tsover_sub(d,p),h=__tsover_mul(_,_),g=__tsover_mul(h,_),y=__tsover_sub(__tsover_add(__tsover_mul(g,-.5),h),__tsover_mul(_,.5)),b=__tsover_add(__tsover_sub(__tsover_mul(g,1.5),__tsover_mul(h,2.5)),1),x=__tsover_add(__tsover_add(__tsover_mul(g,-1.5),__tsover_mul(h,2)),__tsover_mul(_,.5)),P=__tsover_sub(__tsover_mul(g,.5),__tsover_mul(h,.5)),C=__tsover_mul(f,f),E=__tsover_mul(C,f),R=__tsover_sub(__tsover_add(__tsover_mul(E,-.5),C),__tsover_mul(f,.5)),U=__tsover_add(__tsover_sub(__tsover_mul(E,1.5),__tsover_mul(C,2.5)),1),L=__tsover_add(__tsover_add(__tsover_mul(E,-1.5),__tsover_mul(C,2)),__tsover_mul(f,.5)),k=__tsover_sub(__tsover_mul(E,.5),__tsover_mul(C,.5)),D=t(__tsover_sub(u,1),__tsover_sub(p,1)),O=t(u,__tsover_sub(p,1)),F=t(__tsover_add(u,1),__tsover_sub(p,1)),q=t(__tsover_add(u,2),__tsover_sub(p,1)),Y=t(__tsover_sub(u,1),p),V=t(u,p),X=t(__tsover_add(u,1),p),Q=t(__tsover_add(u,2),p),te=t(__tsover_sub(u,1),__tsover_add(p,1)),J=t(u,__tsover_add(p,1)),oe=t(__tsover_add(u,1),__tsover_add(p,1)),le=t(__tsover_add(u,2),__tsover_add(p,1)),se=t(__tsover_sub(u,1),__tsover_add(p,2)),Te=t(u,__tsover_add(p,2)),de=t(__tsover_add(u,1),__tsover_add(p,2)),xe=t(__tsover_add(u,2),__tsover_add(p,2)),Ae=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(D.r,y),R),__tsover_mul(__tsover_mul(O.r,b),R)),__tsover_mul(__tsover_mul(F.r,x),R)),__tsover_mul(__tsover_mul(q.r,P),R)),__tsover_mul(__tsover_mul(Y.r,y),U)),__tsover_mul(__tsover_mul(V.r,b),U)),__tsover_mul(__tsover_mul(X.r,x),U)),__tsover_mul(__tsover_mul(Q.r,P),U)),__tsover_mul(__tsover_mul(te.r,y),L)),__tsover_mul(__tsover_mul(J.r,b),L)),__tsover_mul(__tsover_mul(oe.r,x),L)),__tsover_mul(__tsover_mul(le.r,P),L)),__tsover_mul(__tsover_mul(se.r,y),k)),__tsover_mul(__tsover_mul(Te.r,b),k)),__tsover_mul(__tsover_mul(de.r,x),k)),__tsover_mul(__tsover_mul(xe.r,P),k)),De=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(D.a,y),R),__tsover_mul(__tsover_mul(O.a,b),R)),__tsover_mul(__tsover_mul(F.a,x),R)),__tsover_mul(__tsover_mul(q.a,P),R)),__tsover_mul(__tsover_mul(Y.a,y),U)),__tsover_mul(__tsover_mul(V.a,b),U)),__tsover_mul(__tsover_mul(X.a,x),U)),__tsover_mul(__tsover_mul(Q.a,P),U)),__tsover_mul(__tsover_mul(te.a,y),L)),__tsover_mul(__tsover_mul(J.a,b),L)),__tsover_mul(__tsover_mul(oe.a,x),L)),__tsover_mul(__tsover_mul(le.a,P),L)),__tsover_mul(__tsover_mul(se.a,y),k)),__tsover_mul(__tsover_mul(Te.a,b),k)),__tsover_mul(__tsover_mul(de.a,x),k)),__tsover_mul(__tsover_mul(xe.a,P),k)),Ee=K(K(V,X,_),K(J,oe,_),f),ze=ye(ye(V.r,X.r),ye(J.r,oe.r)),Ie=B(B(V.r,X.r),B(J.r,oe.r)),ke=G(De,ye(ye(V.a,X.a),ye(J.a,oe.a)),B(B(V.a,X.a),B(J.a,oe.a))),Me=__tsover_add(G(Ae,ze,Ie),l);return S(K(Me,B(__tsover_sub(he(m(__tsover_sub(r.x,.5),__tsover_sub(r.y,.5))),o.rBound),Me),be(n(0),.25,l)),Ee.g,Ee.b,ke)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"p\",[7,[7,\"sampleLayout\",\"$\"],\"params\"]],[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],[7,\"p\",\"originX\"],[1,[7,\"p\",\"originX\"],\"+\",[7,\"p\",\"spanX\"]]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],[7,\"p\",\"originY\"],[1,[7,\"p\",\"originY\"],\"+\",[7,\"p\",\"spanY\"]]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"px\",[1,[1,[1,[1,\"uxc\",\"-\",[7,\"p\",\"originX\"]],\"/\",[7,\"p\",\"spanX\"]],\"*\",[7,\"p\",\"activeResF\"]],\"-\",[5,\"0.5\"]]],[13,\"py\",[1,[1,[1,[1,\"uyc\",\"-\",[7,\"p\",\"originY\"]],\"/\",[7,\"p\",\"spanY\"]],\"*\",[7,\"p\",\"activeResF\"]],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"px\"]]],[13,\"y0\",[6,\"std.floor\",[\"py\"]]],[13,\"fx\",[1,\"px\",\"-\",\"x0\"]],[13,\"fy\",[1,\"py\",\"-\",\"y0\"]],[13,\"tx2\",[1,\"fx\",\"*\",\"fx\"]],[13,\"tx3\",[1,\"tx2\",\"*\",\"fx\"]],[13,\"wx0\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"tx2\"],\"-\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx1\",[1,[1,[1,\"tx3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wx2\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"tx2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx3\",[1,[1,\"tx3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"0.5\"]]]],[13,\"ty2\",[1,\"fy\",\"*\",\"fy\"]],[13,\"ty3\",[1,\"ty2\",\"*\",\"fy\"]],[13,\"wy0\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"ty2\"],\"-\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy1\",[1,[1,[1,\"ty3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wy2\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"ty2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy3\",[1,[1,\"ty3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"0.5\"]]]],[13,\"k00\",[6,\"fetchTexel\",[[1,\"x0\",\"-\",[5,\"1\"]],[1,\"y0\",\"-\",[5,\"1\"]]]]],[13,\"k01\",[6,\"fetchTexel\",[\"x0\",[1,\"y0\",\"-\",[5,\"1\"]]]]],[13,\"k02\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],[1,\"y0\",\"-\",[5,\"1\"]]]]],[13,\"k03\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"2\"]],[1,\"y0\",\"-\",[5,\"1\"]]]]],[13,\"k10\",[6,\"fetchTexel\",[[1,\"x0\",\"-\",[5,\"1\"]],\"y0\"]]],[13,\"k11\",[6,\"fetchTexel\",[\"x0\",\"y0\"]]],[13,\"k12\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],\"y0\"]]],[13,\"k13\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"2\"]],\"y0\"]]],[13,\"k20\",[6,\"fetchTexel\",[[1,\"x0\",\"-\",[5,\"1\"]],[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"k21\",[6,\"fetchTexel\",[\"x0\",[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"k22\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"k23\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"2\"]],[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"k30\",[6,\"fetchTexel\",[[1,\"x0\",\"-\",[5,\"1\"]],[1,\"y0\",\"+\",[5,\"2\"]]]]],[13,\"k31\",[6,\"fetchTexel\",[\"x0\",[1,\"y0\",\"+\",[5,\"2\"]]]]],[13,\"k32\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],[1,\"y0\",\"+\",[5,\"2\"]]]]],[13,\"k33\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"2\"]],[1,\"y0\",\"+\",[5,\"2\"]]]]],[13,\"rAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"aAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"bl\",[6,\"std.mix\",[[6,\"std.mix\",[\"k11\",\"k12\",\"fx\"]],[6,\"std.mix\",[\"k21\",\"k22\",\"fx\"]],\"fy\"]]],[13,\"rMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.min\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"rMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.max\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"aMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.min\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.max\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aOut\",[6,\"std.clamp\",[\"aAcc\",\"aMin\",\"aMax\"]]],[13,\"rNear\",[1,[6,\"std.clamp\",[\"rAcc\",\"rMin\",\"rMax\"]],\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",[7,\"p\",\"rBound\"]]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[10,[6,\"d.vec4f\",[\"rOut\",[7,\"bl\",\"g\"],[7,\"bl\",\"b\"],\"aOut\"]]]]]},externals:{sampleLayout:()=>e,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,fetchTexel:()=>t,\"std.mix\":()=>K,\"std.min\":()=>ye,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.f32\":()=>n,\"d.vec4f\":()=>S}})&&s.f)({})).$name(\"fieldSample\"),fieldSampleFast:w.fn([m],S)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=e.$.params,a=G(r.x,o.originX,__tsover_add(o.originX,o.spanX)),i=G(r.y,o.originY,__tsover_add(o.originY,o.spanY)),l=he(m(__tsover_sub(r.x,a),__tsover_sub(r.y,i))),c=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(a,o.originX),o.spanX),o.activeResF),.5),d=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(i,o.originY),o.spanY),o.activeResF),.5),u=ie(c),p=ie(d),_=__tsover_sub(c,u),f=__tsover_sub(d,p),h=t(u,p),g=t(__tsover_add(u,1),p),y=t(u,__tsover_add(p,1)),b=t(__tsover_add(u,1),__tsover_add(p,1)),x=K(K(h,g,_),K(y,b,_),f),P=__tsover_add(x.r,l);return S(K(P,B(__tsover_sub(he(m(__tsover_sub(r.x,.5),__tsover_sub(r.y,.5))),o.rBound),P),be(n(0),.25,l)),x.g,x.b,x.a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"p\",[7,[7,\"sampleLayout\",\"$\"],\"params\"]],[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],[7,\"p\",\"originX\"],[1,[7,\"p\",\"originX\"],\"+\",[7,\"p\",\"spanX\"]]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],[7,\"p\",\"originY\"],[1,[7,\"p\",\"originY\"],\"+\",[7,\"p\",\"spanY\"]]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"px\",[1,[1,[1,[1,\"uxc\",\"-\",[7,\"p\",\"originX\"]],\"/\",[7,\"p\",\"spanX\"]],\"*\",[7,\"p\",\"activeResF\"]],\"-\",[5,\"0.5\"]]],[13,\"py\",[1,[1,[1,[1,\"uyc\",\"-\",[7,\"p\",\"originY\"]],\"/\",[7,\"p\",\"spanY\"]],\"*\",[7,\"p\",\"activeResF\"]],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"px\"]]],[13,\"y0\",[6,\"std.floor\",[\"py\"]]],[13,\"fx\",[1,\"px\",\"-\",\"x0\"]],[13,\"fy\",[1,\"py\",\"-\",\"y0\"]],[13,\"t00\",[6,\"fetchTexel\",[\"x0\",\"y0\"]]],[13,\"t10\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],\"y0\"]]],[13,\"t01\",[6,\"fetchTexel\",[\"x0\",[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"t11\",[6,\"fetchTexel\",[[1,\"x0\",\"+\",[5,\"1\"]],[1,\"y0\",\"+\",[5,\"1\"]]]]],[13,\"bl\",[6,\"std.mix\",[[6,\"std.mix\",[\"t00\",\"t10\",\"fx\"]],[6,\"std.mix\",[\"t01\",\"t11\",\"fx\"]],\"fy\"]]],[13,\"rNear\",[1,[7,\"bl\",\"r\"],\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",[7,\"p\",\"rBound\"]]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[10,[6,\"d.vec4f\",[\"rOut\",[7,\"bl\",\"g\"],[7,\"bl\",\"b\"],[7,\"bl\",\"a\"]]]]]]},externals:{sampleLayout:()=>e,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,fetchTexel:()=>t,\"std.mix\":()=>K,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.f32\":()=>n,\"d.vec4f\":()=>S}})&&s.f)({})).$name(\"fieldSampleFast\"),SampleParams:yx}}function yA(e,t,s,r,o={}){let a=E_(),i=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(e.createTexture({size:[a,a],format:Zh}).$usage(\"storage\",\"sampled\"),\"fieldTexture\"),l=o.paramsUniform??e.createUniform(vl);r(()=>i.destroy());let c=e.createBindGroup(t,{field:i,params:l.buffer,...o.sdfSourceTexture?{sdfSource:o.sdfSourceTexture}:{}}),d=wr(e,(p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";s(_,f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>s}})&&p.f)({}),{bindGroup:c});for(let p of o.extraBindGroups??[])d=d.with(p);let u=a;return{computeStep:d,fieldTexture:i,maxRes:a,get activeRes(){return u},writeParams(p){l.write(p)},dispatch(p){u=p,d.dispatchThreads(p,p)}}}function jB(){return{layout:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({field:{storageTexture:zt(Zh,\"write-only\")},params:{uniform:vl},sdfSource:{texture:Zt(n)}}),\"layout\"),MarchParams:vl}}function bA(){return{fieldSampleArg:w.fn([m,Zt(n),n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=G(t.x,r,__tsover_add(r,a)),u=G(t.y,o,__tsover_add(o,i)),p=he(m(__tsover_sub(t.x,d),__tsover_sub(t.y,u))),_=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(d,r),a),l),.5),f=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(u,o),i),l),.5),h=ie(_),g=ie(f),y=__tsover_sub(_,h),b=__tsover_sub(f,g),x=__tsover_mul(y,y),P=__tsover_mul(x,y),C=__tsover_sub(__tsover_add(__tsover_mul(P,-.5),x),__tsover_mul(y,.5)),E=__tsover_add(__tsover_sub(__tsover_mul(P,1.5),__tsover_mul(x,2.5)),1),R=__tsover_add(__tsover_add(__tsover_mul(P,-1.5),__tsover_mul(x,2)),__tsover_mul(y,.5)),U=__tsover_sub(__tsover_mul(P,.5),__tsover_mul(x,.5)),L=__tsover_mul(b,b),k=__tsover_mul(L,b),D=__tsover_sub(__tsover_add(__tsover_mul(k,-.5),L),__tsover_mul(b,.5)),O=__tsover_add(__tsover_sub(__tsover_mul(k,1.5),__tsover_mul(L,2.5)),1),F=__tsover_add(__tsover_add(__tsover_mul(k,-1.5),__tsover_mul(L,2)),__tsover_mul(b,.5)),q=__tsover_sub(__tsover_mul(k,.5),__tsover_mul(L,.5)),Y=__tsover_sub(l,1),V=M(G(__tsover_sub(h,1),n(0),Y)),X=M(G(h,n(0),Y)),Q=M(G(__tsover_add(h,1),n(0),Y)),te=M(G(__tsover_add(h,2),n(0),Y)),J=M(G(__tsover_sub(g,1),n(0),Y)),oe=M(G(g,n(0),Y)),le=M(G(__tsover_add(g,1),n(0),Y)),se=M(G(__tsover_add(g,2),n(0),Y)),Te=Ye(s,fe(V,J),0),de=Ye(s,fe(X,J),0),xe=Ye(s,fe(Q,J),0),Ae=Ye(s,fe(te,J),0),De=Ye(s,fe(V,oe),0),Ee=Ye(s,fe(X,oe),0),ze=Ye(s,fe(Q,oe),0),Ie=Ye(s,fe(te,oe),0),ke=Ye(s,fe(V,le),0),Me=Ye(s,fe(X,le),0),we=Ye(s,fe(Q,le),0),Ve=Ye(s,fe(te,le),0),qe=Ye(s,fe(V,se),0),st=Ye(s,fe(X,se),0),pt=Ye(s,fe(Q,se),0),Ze=Ye(s,fe(te,se),0),nt=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(Te.r,C),D),__tsover_mul(__tsover_mul(de.r,E),D)),__tsover_mul(__tsover_mul(xe.r,R),D)),__tsover_mul(__tsover_mul(Ae.r,U),D)),__tsover_mul(__tsover_mul(De.r,C),O)),__tsover_mul(__tsover_mul(Ee.r,E),O)),__tsover_mul(__tsover_mul(ze.r,R),O)),__tsover_mul(__tsover_mul(Ie.r,U),O)),__tsover_mul(__tsover_mul(ke.r,C),F)),__tsover_mul(__tsover_mul(Me.r,E),F)),__tsover_mul(__tsover_mul(we.r,R),F)),__tsover_mul(__tsover_mul(Ve.r,U),F)),__tsover_mul(__tsover_mul(qe.r,C),q)),__tsover_mul(__tsover_mul(st.r,E),q)),__tsover_mul(__tsover_mul(pt.r,R),q)),__tsover_mul(__tsover_mul(Ze.r,U),q)),ht=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(Te.a,C),D),__tsover_mul(__tsover_mul(de.a,E),D)),__tsover_mul(__tsover_mul(xe.a,R),D)),__tsover_mul(__tsover_mul(Ae.a,U),D)),__tsover_mul(__tsover_mul(De.a,C),O)),__tsover_mul(__tsover_mul(Ee.a,E),O)),__tsover_mul(__tsover_mul(ze.a,R),O)),__tsover_mul(__tsover_mul(Ie.a,U),O)),__tsover_mul(__tsover_mul(ke.a,C),F)),__tsover_mul(__tsover_mul(Me.a,E),F)),__tsover_mul(__tsover_mul(we.a,R),F)),__tsover_mul(__tsover_mul(Ve.a,U),F)),__tsover_mul(__tsover_mul(qe.a,C),q)),__tsover_mul(__tsover_mul(st.a,E),q)),__tsover_mul(__tsover_mul(pt.a,R),q)),__tsover_mul(__tsover_mul(Ze.a,U),q)),yt=K(K(Ee,ze,y),K(Me,we,y),b),St=ye(ye(Ee.r,ze.r),ye(Me.r,we.r)),Mt=B(B(Ee.r,ze.r),B(Me.r,we.r)),Rt=G(ht,ye(ye(Ee.a,ze.a),ye(Me.a,we.a)),B(B(Ee.a,ze.a),B(Me.a,we.a))),Kt=__tsover_add(G(nt,St,Mt),p);return S(K(Kt,B(__tsover_sub(he(m(__tsover_sub(t.x,.5),__tsover_sub(t.y,.5))),c),Kt),be(n(0),.25,p)),yt.g,yt.b,Rt)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"field\"},{type:\"i\",name:\"originX\"},{type:\"i\",name:\"originY\"},{type:\"i\",name:\"spanX\"},{type:\"i\",name:\"spanY\"},{type:\"i\",name:\"activeResF\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],\"originX\",[1,\"originX\",\"+\",\"spanX\"]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],\"originY\",[1,\"originY\",\"+\",\"spanY\"]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"px\",[1,[1,[1,[1,\"uxc\",\"-\",\"originX\"],\"/\",\"spanX\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"py\",[1,[1,[1,[1,\"uyc\",\"-\",\"originY\"],\"/\",\"spanY\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"px\"]]],[13,\"y0\",[6,\"std.floor\",[\"py\"]]],[13,\"fx\",[1,\"px\",\"-\",\"x0\"]],[13,\"fy\",[1,\"py\",\"-\",\"y0\"]],[13,\"tx2\",[1,\"fx\",\"*\",\"fx\"]],[13,\"tx3\",[1,\"tx2\",\"*\",\"fx\"]],[13,\"wx0\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"tx2\"],\"-\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx1\",[1,[1,[1,\"tx3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wx2\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"tx2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx3\",[1,[1,\"tx3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"0.5\"]]]],[13,\"ty2\",[1,\"fy\",\"*\",\"fy\"]],[13,\"ty3\",[1,\"ty2\",\"*\",\"fy\"]],[13,\"wy0\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"ty2\"],\"-\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy1\",[1,[1,[1,\"ty3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wy2\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"ty2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy3\",[1,[1,\"ty3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"0.5\"]]]],[13,\"maxT\",[1,\"activeResF\",\"-\",[5,\"1\"]]],[13,\"cxm\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"-\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"x0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx2\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"2\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cym\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"-\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"y0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy2\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"2\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"k00\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cym\"]],[5,\"0\"]]]],[13,\"k01\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cym\"]],[5,\"0\"]]]],[13,\"k02\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cym\"]],[5,\"0\"]]]],[13,\"k03\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cym\"]],[5,\"0\"]]]],[13,\"k10\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy0\"]],[5,\"0\"]]]],[13,\"k11\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy0\"]],[5,\"0\"]]]],[13,\"k12\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy0\"]],[5,\"0\"]]]],[13,\"k13\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy0\"]],[5,\"0\"]]]],[13,\"k20\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy1\"]],[5,\"0\"]]]],[13,\"k21\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy1\"]],[5,\"0\"]]]],[13,\"k22\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy1\"]],[5,\"0\"]]]],[13,\"k23\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy1\"]],[5,\"0\"]]]],[13,\"k30\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy2\"]],[5,\"0\"]]]],[13,\"k31\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy2\"]],[5,\"0\"]]]],[13,\"k32\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy2\"]],[5,\"0\"]]]],[13,\"k33\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy2\"]],[5,\"0\"]]]],[13,\"rAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"aAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"bl\",[6,\"std.mix\",[[6,\"std.mix\",[\"k11\",\"k12\",\"fx\"]],[6,\"std.mix\",[\"k21\",\"k22\",\"fx\"]],\"fy\"]]],[13,\"rMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.min\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"rMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.max\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"aMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.min\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.max\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aOut\",[6,\"std.clamp\",[\"aAcc\",\"aMin\",\"aMax\"]]],[13,\"rNear\",[1,[6,\"std.clamp\",[\"rAcc\",\"rMin\",\"rMax\"]],\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",\"rBound\"]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[10,[6,\"d.vec4f\",[\"rOut\",[7,\"bl\",\"g\"],[7,\"bl\",\"b\"],\"aOut\"]]]]]},externals:{\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,\"std.min\":()=>ye,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"fieldSampleArg\"),fieldSampleGradArg:w.fn([m,Zt(n),n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=G(t.x,r,__tsover_add(r,a)),u=G(t.y,o,__tsover_add(o,i)),p=he(m(__tsover_sub(t.x,d),__tsover_sub(t.y,u))),_=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(d,r),a),l),.5),f=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(u,o),i),l),.5),h=ie(_),g=ie(f),y=__tsover_sub(_,h),b=__tsover_sub(f,g),x=__tsover_mul(y,y),P=__tsover_mul(x,y),C=__tsover_sub(__tsover_add(__tsover_mul(P,-.5),x),__tsover_mul(y,.5)),E=__tsover_add(__tsover_sub(__tsover_mul(P,1.5),__tsover_mul(x,2.5)),1),R=__tsover_add(__tsover_add(__tsover_mul(P,-1.5),__tsover_mul(x,2)),__tsover_mul(y,.5)),U=__tsover_sub(__tsover_mul(P,.5),__tsover_mul(x,.5)),L=__tsover_mul(b,b),k=__tsover_mul(L,b),D=__tsover_sub(__tsover_add(__tsover_mul(k,-.5),L),__tsover_mul(b,.5)),O=__tsover_add(__tsover_sub(__tsover_mul(k,1.5),__tsover_mul(L,2.5)),1),F=__tsover_add(__tsover_add(__tsover_mul(k,-1.5),__tsover_mul(L,2)),__tsover_mul(b,.5)),q=__tsover_sub(__tsover_mul(k,.5),__tsover_mul(L,.5)),Y=__tsover_sub(1,y),V=__tsover_mul(__tsover_mul(__tsover_mul(Y,Y),Y),.16666667),X=__tsover_add(__tsover_sub(__tsover_mul(P,.5),x),.66666667),Q=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(P,-.5),__tsover_mul(x,.5)),__tsover_mul(y,.5)),.16666667),te=__tsover_mul(P,.16666667),J=__tsover_mul(__tsover_mul(Y,Y),-.5),oe=__tsover_sub(__tsover_mul(x,1.5),__tsover_mul(y,2)),le=__tsover_add(__tsover_add(__tsover_mul(x,-1.5),y),.5),se=__tsover_mul(x,.5),Te=__tsover_sub(1,b),de=__tsover_mul(__tsover_mul(__tsover_mul(Te,Te),Te),.16666667),xe=__tsover_add(__tsover_sub(__tsover_mul(k,.5),L),.66666667),Ae=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(k,-.5),__tsover_mul(L,.5)),__tsover_mul(b,.5)),.16666667),De=__tsover_mul(k,.16666667),Ee=__tsover_mul(__tsover_mul(Te,Te),-.5),ze=__tsover_sub(__tsover_mul(L,1.5),__tsover_mul(b,2)),Ie=__tsover_add(__tsover_add(__tsover_mul(L,-1.5),b),.5),ke=__tsover_mul(L,.5),Me=__tsover_sub(l,1),we=M(G(__tsover_sub(h,1),n(0),Me)),Ve=M(G(h,n(0),Me)),qe=M(G(__tsover_add(h,1),n(0),Me)),st=M(G(__tsover_add(h,2),n(0),Me)),pt=M(G(__tsover_sub(g,1),n(0),Me)),Ze=M(G(g,n(0),Me)),nt=M(G(__tsover_add(g,1),n(0),Me)),ht=M(G(__tsover_add(g,2),n(0),Me)),yt=Ye(s,fe(we,pt),0),St=Ye(s,fe(Ve,pt),0),Mt=Ye(s,fe(qe,pt),0),Rt=Ye(s,fe(st,pt),0),Kt=Ye(s,fe(we,Ze),0),Wt=Ye(s,fe(Ve,Ze),0),ls=Ye(s,fe(qe,Ze),0),us=Ye(s,fe(st,Ze),0),os=Ye(s,fe(we,nt),0),bs=Ye(s,fe(Ve,nt),0),hs=Ye(s,fe(qe,nt),0),gs=Ye(s,fe(st,nt),0),Gs=Ye(s,fe(we,ht),0),Ss=Ye(s,fe(Ve,ht),0),or=Ye(s,fe(qe,ht),0),yr=Ye(s,fe(st,ht),0),lr=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(yt.r,C),D),__tsover_mul(__tsover_mul(St.r,E),D)),__tsover_mul(__tsover_mul(Mt.r,R),D)),__tsover_mul(__tsover_mul(Rt.r,U),D)),__tsover_mul(__tsover_mul(Kt.r,C),O)),__tsover_mul(__tsover_mul(Wt.r,E),O)),__tsover_mul(__tsover_mul(ls.r,R),O)),__tsover_mul(__tsover_mul(us.r,U),O)),__tsover_mul(__tsover_mul(os.r,C),F)),__tsover_mul(__tsover_mul(bs.r,E),F)),__tsover_mul(__tsover_mul(hs.r,R),F)),__tsover_mul(__tsover_mul(gs.r,U),F)),__tsover_mul(__tsover_mul(Gs.r,C),q)),__tsover_mul(__tsover_mul(Ss.r,E),q)),__tsover_mul(__tsover_mul(or.r,R),q)),__tsover_mul(__tsover_mul(yr.r,U),q)),Ur=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(yt.a,C),D),__tsover_mul(__tsover_mul(St.a,E),D)),__tsover_mul(__tsover_mul(Mt.a,R),D)),__tsover_mul(__tsover_mul(Rt.a,U),D)),__tsover_mul(__tsover_mul(Kt.a,C),O)),__tsover_mul(__tsover_mul(Wt.a,E),O)),__tsover_mul(__tsover_mul(ls.a,R),O)),__tsover_mul(__tsover_mul(us.a,U),O)),__tsover_mul(__tsover_mul(os.a,C),F)),__tsover_mul(__tsover_mul(bs.a,E),F)),__tsover_mul(__tsover_mul(hs.a,R),F)),__tsover_mul(__tsover_mul(gs.a,U),F)),__tsover_mul(__tsover_mul(Gs.a,C),q)),__tsover_mul(__tsover_mul(Ss.a,E),q)),__tsover_mul(__tsover_mul(or.a,R),q)),__tsover_mul(__tsover_mul(yr.a,U),q)),so=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(yt.r,J),de),__tsover_mul(__tsover_mul(St.r,oe),de)),__tsover_mul(__tsover_mul(Mt.r,le),de)),__tsover_mul(__tsover_mul(Rt.r,se),de)),__tsover_mul(__tsover_mul(Kt.r,J),xe)),__tsover_mul(__tsover_mul(Wt.r,oe),xe)),__tsover_mul(__tsover_mul(ls.r,le),xe)),__tsover_mul(__tsover_mul(us.r,se),xe)),__tsover_mul(__tsover_mul(os.r,J),Ae)),__tsover_mul(__tsover_mul(bs.r,oe),Ae)),__tsover_mul(__tsover_mul(hs.r,le),Ae)),__tsover_mul(__tsover_mul(gs.r,se),Ae)),__tsover_mul(__tsover_mul(Gs.r,J),De)),__tsover_mul(__tsover_mul(Ss.r,oe),De)),__tsover_mul(__tsover_mul(or.r,le),De)),__tsover_mul(__tsover_mul(yr.r,se),De)),ro=__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(yt.r,V),Ee),__tsover_mul(__tsover_mul(St.r,X),Ee)),__tsover_mul(__tsover_mul(Mt.r,Q),Ee)),__tsover_mul(__tsover_mul(Rt.r,te),Ee)),__tsover_mul(__tsover_mul(Kt.r,V),ze)),__tsover_mul(__tsover_mul(Wt.r,X),ze)),__tsover_mul(__tsover_mul(ls.r,Q),ze)),__tsover_mul(__tsover_mul(us.r,te),ze)),__tsover_mul(__tsover_mul(os.r,V),Ie)),__tsover_mul(__tsover_mul(bs.r,X),Ie)),__tsover_mul(__tsover_mul(hs.r,Q),Ie)),__tsover_mul(__tsover_mul(gs.r,te),Ie)),__tsover_mul(__tsover_mul(Gs.r,V),ke)),__tsover_mul(__tsover_mul(Ss.r,X),ke)),__tsover_mul(__tsover_mul(or.r,Q),ke)),__tsover_mul(__tsover_mul(yr.r,te),ke)),_e=ye(ye(Wt.r,ls.r),ye(bs.r,hs.r)),We=B(B(Wt.r,ls.r),B(bs.r,hs.r)),Le=G(Ur,ye(ye(Wt.a,ls.a),ye(bs.a,hs.a)),B(B(Wt.a,ls.a),B(bs.a,hs.a))),Be=__tsover_add(__tsover_mul(__tsover_sub(We,_e),.3),1e-6),Oe=G(__tsover_add(__tsover_mul(__tsover_div(__tsover_sub(We,lr),Be),.5),.5),0,1),tt=__tsover_sub(K(We,lr,Oe),__tsover_mul(__tsover_mul(Be,Oe),__tsover_sub(1,Oe))),_t=G(__tsover_add(__tsover_mul(__tsover_div(__tsover_sub(tt,_e),Be),.5),.5),0,1),Bt=__tsover_add(K(_e,tt,_t),__tsover_mul(__tsover_mul(Be,_t),__tsover_sub(1,_t))),Dt=__tsover_add(Bt,p),Nt=K(Dt,B(__tsover_sub(he(m(__tsover_sub(t.x,.5),__tsover_sub(t.y,.5))),c),Dt),be(n(0),.25,p)),Ks=N(n(0),n(1),re(__tsover_sub(t.x,d))<1e-7),Ds=N(n(0),n(1),re(__tsover_sub(t.y,u))<1e-7),ar=__tsover_div(1,B(p,1e-5));return S(Nt,__tsover_add(__tsover_mul(__tsover_mul(so,__tsover_div(l,a)),Ks),__tsover_mul(__tsover_sub(t.x,d),ar)),__tsover_add(__tsover_mul(__tsover_mul(ro,__tsover_div(l,i)),Ds),__tsover_mul(__tsover_sub(t.y,u),ar)),Le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"field\"},{type:\"i\",name:\"originX\"},{type:\"i\",name:\"originY\"},{type:\"i\",name:\"spanX\"},{type:\"i\",name:\"spanY\"},{type:\"i\",name:\"activeResF\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],\"originX\",[1,\"originX\",\"+\",\"spanX\"]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],\"originY\",[1,\"originY\",\"+\",\"spanY\"]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"px\",[1,[1,[1,[1,\"uxc\",\"-\",\"originX\"],\"/\",\"spanX\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"py\",[1,[1,[1,[1,\"uyc\",\"-\",\"originY\"],\"/\",\"spanY\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"px\"]]],[13,\"y0\",[6,\"std.floor\",[\"py\"]]],[13,\"fx\",[1,\"px\",\"-\",\"x0\"]],[13,\"fy\",[1,\"py\",\"-\",\"y0\"]],[13,\"tx2\",[1,\"fx\",\"*\",\"fx\"]],[13,\"tx3\",[1,\"tx2\",\"*\",\"fx\"]],[13,\"wx0\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"tx2\"],\"-\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx1\",[1,[1,[1,\"tx3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wx2\",[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"tx2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fx\",\"*\",[5,\"0.5\"]]]],[13,\"wx3\",[1,[1,\"tx3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"tx2\",\"*\",[5,\"0.5\"]]]],[13,\"ty2\",[1,\"fy\",\"*\",\"fy\"]],[13,\"ty3\",[1,\"ty2\",\"*\",\"fy\"]],[13,\"wy0\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",\"ty2\"],\"-\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy1\",[1,[1,[1,\"ty3\",\"*\",[5,\"1.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"2.5\"]]],\"+\",[5,\"1\"]]],[13,\"wy2\",[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",[1,\"ty2\",\"*\",[5,\"2\"]]],\"+\",[1,\"fy\",\"*\",[5,\"0.5\"]]]],[13,\"wy3\",[1,[1,\"ty3\",\"*\",[5,\"0.5\"]],\"-\",[1,\"ty2\",\"*\",[5,\"0.5\"]]]],[13,\"oxf\",[1,[5,\"1\"],\"-\",\"fx\"]],[13,\"bx0\",[1,[1,[1,\"oxf\",\"*\",\"oxf\"],\"*\",\"oxf\"],\"*\",[5,\"0.16666667\"]]],[13,\"bx1\",[1,[1,[1,\"tx3\",\"*\",[5,\"0.5\"]],\"-\",\"tx2\"],\"+\",[5,\"0.66666667\"]]],[13,\"bx2\",[1,[1,[1,[1,\"tx3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",[1,\"tx2\",\"*\",[5,\"0.5\"]]],\"+\",[1,\"fx\",\"*\",[5,\"0.5\"]]],\"+\",[5,\"0.16666667\"]]],[13,\"bx3\",[1,\"tx3\",\"*\",[5,\"0.16666667\"]]],[13,\"dbx0\",[1,[1,\"oxf\",\"*\",\"oxf\"],\"*\",[4,\"-\",[5,\"0.5\"]]]],[13,\"dbx1\",[1,[1,\"tx2\",\"*\",[5,\"1.5\"]],\"-\",[1,\"fx\",\"*\",[5,\"2\"]]]],[13,\"dbx2\",[1,[1,[1,\"tx2\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",\"fx\"],\"+\",[5,\"0.5\"]]],[13,\"dbx3\",[1,\"tx2\",\"*\",[5,\"0.5\"]]],[13,\"oyf\",[1,[5,\"1\"],\"-\",\"fy\"]],[13,\"by0\",[1,[1,[1,\"oyf\",\"*\",\"oyf\"],\"*\",\"oyf\"],\"*\",[5,\"0.16666667\"]]],[13,\"by1\",[1,[1,[1,\"ty3\",\"*\",[5,\"0.5\"]],\"-\",\"ty2\"],\"+\",[5,\"0.66666667\"]]],[13,\"by2\",[1,[1,[1,[1,\"ty3\",\"*\",[4,\"-\",[5,\"0.5\"]]],\"+\",[1,\"ty2\",\"*\",[5,\"0.5\"]]],\"+\",[1,\"fy\",\"*\",[5,\"0.5\"]]],\"+\",[5,\"0.16666667\"]]],[13,\"by3\",[1,\"ty3\",\"*\",[5,\"0.16666667\"]]],[13,\"dby0\",[1,[1,\"oyf\",\"*\",\"oyf\"],\"*\",[4,\"-\",[5,\"0.5\"]]]],[13,\"dby1\",[1,[1,\"ty2\",\"*\",[5,\"1.5\"]],\"-\",[1,\"fy\",\"*\",[5,\"2\"]]]],[13,\"dby2\",[1,[1,[1,\"ty2\",\"*\",[4,\"-\",[5,\"1.5\"]]],\"+\",\"fy\"],\"+\",[5,\"0.5\"]]],[13,\"dby3\",[1,\"ty2\",\"*\",[5,\"0.5\"]]],[13,\"maxT\",[1,\"activeResF\",\"-\",[5,\"1\"]]],[13,\"cxm\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"-\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"x0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx2\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"2\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cym\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"-\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"y0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy2\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"2\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"k00\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cym\"]],[5,\"0\"]]]],[13,\"k01\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cym\"]],[5,\"0\"]]]],[13,\"k02\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cym\"]],[5,\"0\"]]]],[13,\"k03\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cym\"]],[5,\"0\"]]]],[13,\"k10\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy0\"]],[5,\"0\"]]]],[13,\"k11\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy0\"]],[5,\"0\"]]]],[13,\"k12\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy0\"]],[5,\"0\"]]]],[13,\"k13\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy0\"]],[5,\"0\"]]]],[13,\"k20\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy1\"]],[5,\"0\"]]]],[13,\"k21\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy1\"]],[5,\"0\"]]]],[13,\"k22\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy1\"]],[5,\"0\"]]]],[13,\"k23\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy1\"]],[5,\"0\"]]]],[13,\"k30\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cxm\",\"cy2\"]],[5,\"0\"]]]],[13,\"k31\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy2\"]],[5,\"0\"]]]],[13,\"k32\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy2\"]],[5,\"0\"]]]],[13,\"k33\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx2\",\"cy2\"]],[5,\"0\"]]]],[13,\"rAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"r\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"r\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"r\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"r\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"aAcc\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy0\"],\"+\",[1,[1,[7,\"k01\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k02\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k03\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy0\"]],\"+\",[1,[1,[7,\"k10\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k11\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k12\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k13\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy1\"]],\"+\",[1,[1,[7,\"k20\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k21\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k22\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k23\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy2\"]],\"+\",[1,[1,[7,\"k30\",\"a\"],\"*\",\"wx0\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k31\",\"a\"],\"*\",\"wx1\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k32\",\"a\"],\"*\",\"wx2\"],\"*\",\"wy3\"]],\"+\",[1,[1,[7,\"k33\",\"a\"],\"*\",\"wx3\"],\"*\",\"wy3\"]]],[13,\"drdpx\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"r\"],\"*\",\"dbx0\"],\"*\",\"by0\"],\"+\",[1,[1,[7,\"k01\",\"r\"],\"*\",\"dbx1\"],\"*\",\"by0\"]],\"+\",[1,[1,[7,\"k02\",\"r\"],\"*\",\"dbx2\"],\"*\",\"by0\"]],\"+\",[1,[1,[7,\"k03\",\"r\"],\"*\",\"dbx3\"],\"*\",\"by0\"]],\"+\",[1,[1,[7,\"k10\",\"r\"],\"*\",\"dbx0\"],\"*\",\"by1\"]],\"+\",[1,[1,[7,\"k11\",\"r\"],\"*\",\"dbx1\"],\"*\",\"by1\"]],\"+\",[1,[1,[7,\"k12\",\"r\"],\"*\",\"dbx2\"],\"*\",\"by1\"]],\"+\",[1,[1,[7,\"k13\",\"r\"],\"*\",\"dbx3\"],\"*\",\"by1\"]],\"+\",[1,[1,[7,\"k20\",\"r\"],\"*\",\"dbx0\"],\"*\",\"by2\"]],\"+\",[1,[1,[7,\"k21\",\"r\"],\"*\",\"dbx1\"],\"*\",\"by2\"]],\"+\",[1,[1,[7,\"k22\",\"r\"],\"*\",\"dbx2\"],\"*\",\"by2\"]],\"+\",[1,[1,[7,\"k23\",\"r\"],\"*\",\"dbx3\"],\"*\",\"by2\"]],\"+\",[1,[1,[7,\"k30\",\"r\"],\"*\",\"dbx0\"],\"*\",\"by3\"]],\"+\",[1,[1,[7,\"k31\",\"r\"],\"*\",\"dbx1\"],\"*\",\"by3\"]],\"+\",[1,[1,[7,\"k32\",\"r\"],\"*\",\"dbx2\"],\"*\",\"by3\"]],\"+\",[1,[1,[7,\"k33\",\"r\"],\"*\",\"dbx3\"],\"*\",\"by3\"]]],[13,\"drdpy\",[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[1,[7,\"k00\",\"r\"],\"*\",\"bx0\"],\"*\",\"dby0\"],\"+\",[1,[1,[7,\"k01\",\"r\"],\"*\",\"bx1\"],\"*\",\"dby0\"]],\"+\",[1,[1,[7,\"k02\",\"r\"],\"*\",\"bx2\"],\"*\",\"dby0\"]],\"+\",[1,[1,[7,\"k03\",\"r\"],\"*\",\"bx3\"],\"*\",\"dby0\"]],\"+\",[1,[1,[7,\"k10\",\"r\"],\"*\",\"bx0\"],\"*\",\"dby1\"]],\"+\",[1,[1,[7,\"k11\",\"r\"],\"*\",\"bx1\"],\"*\",\"dby1\"]],\"+\",[1,[1,[7,\"k12\",\"r\"],\"*\",\"bx2\"],\"*\",\"dby1\"]],\"+\",[1,[1,[7,\"k13\",\"r\"],\"*\",\"bx3\"],\"*\",\"dby1\"]],\"+\",[1,[1,[7,\"k20\",\"r\"],\"*\",\"bx0\"],\"*\",\"dby2\"]],\"+\",[1,[1,[7,\"k21\",\"r\"],\"*\",\"bx1\"],\"*\",\"dby2\"]],\"+\",[1,[1,[7,\"k22\",\"r\"],\"*\",\"bx2\"],\"*\",\"dby2\"]],\"+\",[1,[1,[7,\"k23\",\"r\"],\"*\",\"bx3\"],\"*\",\"dby2\"]],\"+\",[1,[1,[7,\"k30\",\"r\"],\"*\",\"bx0\"],\"*\",\"dby3\"]],\"+\",[1,[1,[7,\"k31\",\"r\"],\"*\",\"bx1\"],\"*\",\"dby3\"]],\"+\",[1,[1,[7,\"k32\",\"r\"],\"*\",\"bx2\"],\"*\",\"dby3\"]],\"+\",[1,[1,[7,\"k33\",\"r\"],\"*\",\"bx3\"],\"*\",\"dby3\"]]],[13,\"rMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.min\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"rMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"r\"],[7,\"k12\",\"r\"]]],[6,\"std.max\",[[7,\"k21\",\"r\"],[7,\"k22\",\"r\"]]]]]],[13,\"aMin\",[6,\"std.min\",[[6,\"std.min\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.min\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aMax\",[6,\"std.max\",[[6,\"std.max\",[[7,\"k11\",\"a\"],[7,\"k12\",\"a\"]]],[6,\"std.max\",[[7,\"k21\",\"a\"],[7,\"k22\",\"a\"]]]]]],[13,\"aOut\",[6,\"std.clamp\",[\"aAcc\",\"aMin\",\"aMax\"]]],[13,\"kSoft\",[1,[1,[1,\"rMax\",\"-\",\"rMin\"],\"*\",[5,\"0.3\"]],\"+\",[5,\"0.000001\"]]],[13,\"hCap\",[6,\"std.clamp\",[[1,[1,[1,[1,\"rMax\",\"-\",\"rAcc\"],\"/\",\"kSoft\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"rCap\",[1,[6,\"std.mix\",[\"rMax\",\"rAcc\",\"hCap\"]],\"-\",[1,[1,\"kSoft\",\"*\",\"hCap\"],\"*\",[1,[5,\"1\"],\"-\",\"hCap\"]]]],[13,\"hFlr\",[6,\"std.clamp\",[[1,[1,[1,[1,\"rCap\",\"-\",\"rMin\"],\"/\",\"kSoft\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"rSoft\",[1,[6,\"std.mix\",[\"rMin\",\"rCap\",\"hFlr\"]],\"+\",[1,[1,\"kSoft\",\"*\",\"hFlr\"],\"*\",[1,[5,\"1\"],\"-\",\"hFlr\"]]]],[13,\"rNear\",[1,\"rSoft\",\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",\"rBound\"]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[13,\"insideX\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"std.abs\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"]]],\"<\",[5,\"1e-7\"]]]]],[13,\"insideY\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"std.abs\",[[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]],\"<\",[5,\"1e-7\"]]]]],[13,\"invOut\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[\"outsideDist\",[5,\"0.00001\"]]]]],[13,\"gU\",[1,[1,[1,\"drdpx\",\"*\",[1,\"activeResF\",\"/\",\"spanX\"]],\"*\",\"insideX\"],\"+\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],\"*\",\"invOut\"]]],[13,\"gV\",[1,[1,[1,\"drdpy\",\"*\",[1,\"activeResF\",\"/\",\"spanY\"]],\"*\",\"insideY\"],\"+\",[1,[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"],\"*\",\"invOut\"]]],[10,[6,\"d.vec4f\",[\"rOut\",\"gU\",\"gV\",\"aOut\"]]]]]},externals:{\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.min\":()=>ye,\"std.max\":()=>B,\"std.mix\":()=>K,\"std.smoothstep\":()=>be,\"std.select\":()=>N,\"std.abs\":()=>re,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"fieldSampleGradArg\"),fieldSampleFastArg:w.fn([m,Zt(n),n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=G(t.x,r,__tsover_add(r,a)),u=G(t.y,o,__tsover_add(o,i)),p=he(m(__tsover_sub(t.x,d),__tsover_sub(t.y,u))),_=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(d,r),a),l),.5),f=__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(u,o),i),l),.5),h=ie(_),g=ie(f),y=__tsover_sub(_,h),b=__tsover_sub(f,g),x=__tsover_sub(l,1),P=M(G(h,n(0),x)),C=M(G(__tsover_add(h,1),n(0),x)),E=M(G(g,n(0),x)),R=M(G(__tsover_add(g,1),n(0),x)),U=Ye(s,fe(P,E),0),L=Ye(s,fe(C,E),0),k=Ye(s,fe(P,R),0),D=Ye(s,fe(C,R),0),O=K(K(U,L,y),K(k,D,y),b),F=__tsover_add(O.r,p);return S(K(F,B(__tsover_sub(he(m(__tsover_sub(t.x,.5),__tsover_sub(t.y,.5))),c),F),be(n(0),.25,p)),O.g,O.b,O.a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"field\"},{type:\"i\",name:\"originX\"},{type:\"i\",name:\"originY\"},{type:\"i\",name:\"spanX\"},{type:\"i\",name:\"spanY\"},{type:\"i\",name:\"activeResF\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],\"originX\",[1,\"originX\",\"+\",\"spanX\"]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],\"originY\",[1,\"originY\",\"+\",\"spanY\"]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"px\",[1,[1,[1,[1,\"uxc\",\"-\",\"originX\"],\"/\",\"spanX\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"py\",[1,[1,[1,[1,\"uyc\",\"-\",\"originY\"],\"/\",\"spanY\"],\"*\",\"activeResF\"],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"px\"]]],[13,\"y0\",[6,\"std.floor\",[\"py\"]]],[13,\"fx\",[1,\"px\",\"-\",\"x0\"]],[13,\"fy\",[1,\"py\",\"-\",\"y0\"]],[13,\"maxT\",[1,\"activeResF\",\"-\",[5,\"1\"]]],[13,\"cx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"x0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"y0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"t00\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy0\"]],[5,\"0\"]]]],[13,\"t10\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy0\"]],[5,\"0\"]]]],[13,\"t01\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx0\",\"cy1\"]],[5,\"0\"]]]],[13,\"t11\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[\"cx1\",\"cy1\"]],[5,\"0\"]]]],[13,\"bl\",[6,\"std.mix\",[[6,\"std.mix\",[\"t00\",\"t10\",\"fx\"]],[6,\"std.mix\",[\"t01\",\"t11\",\"fx\"]],\"fy\"]]],[13,\"rNear\",[1,[7,\"bl\",\"r\"],\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",\"rBound\"]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[10,[6,\"d.vec4f\",[\"rOut\",[7,\"bl\",\"g\"],[7,\"bl\",\"b\"],[7,\"bl\",\"a\"]]]]]]},externals:{\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"fieldSampleFastArg\"),fieldSampleTexelArg:w.fn([m,Zt(n),n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=G(t.x,r,__tsover_add(r,a)),u=G(t.y,o,__tsover_add(o,i)),p=he(m(__tsover_sub(t.x,d),__tsover_sub(t.y,u))),_=__tsover_sub(l,1),f=G(ie(__tsover_mul(__tsover_div(__tsover_sub(d,r),a),l)),n(0),_),h=G(ie(__tsover_mul(__tsover_div(__tsover_sub(u,o),i),l)),n(0),_),g=Ye(s,fe(M(f),M(h)),0),y=__tsover_add(g.r,p);return S(K(y,B(__tsover_sub(he(m(__tsover_sub(t.x,.5),__tsover_sub(t.y,.5))),c),y),be(n(0),.25,p)),g.g,g.b,g.a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"field\"},{type:\"i\",name:\"originX\"},{type:\"i\",name:\"originY\"},{type:\"i\",name:\"spanX\"},{type:\"i\",name:\"spanY\"},{type:\"i\",name:\"activeResF\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"uxc\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],\"originX\",[1,\"originX\",\"+\",\"spanX\"]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],\"originY\",[1,\"originY\",\"+\",\"spanY\"]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"uxc\"],[1,[7,\"uv\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"maxT\",[1,\"activeResF\",\"-\",[5,\"1\"]]],[13,\"px\",[6,\"std.clamp\",[[6,\"std.floor\",[[1,[1,[1,\"uxc\",\"-\",\"originX\"],\"/\",\"spanX\"],\"*\",\"activeResF\"]]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]],[13,\"py\",[6,\"std.clamp\",[[6,\"std.floor\",[[1,[1,[1,\"uyc\",\"-\",\"originY\"],\"/\",\"spanY\"],\"*\",\"activeResF\"]]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]],[13,\"t\",[6,\"std.textureLoad\",[\"field\",[6,\"d.vec2u\",[[6,\"d.u32\",[\"px\"]],[6,\"d.u32\",[\"py\"]]]],[5,\"0\"]]]],[13,\"rNear\",[1,[7,\"t\",\"r\"],\"+\",\"outsideDist\"]],[13,\"rCircle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",\"rBound\"]],[13,\"rOut\",[6,\"std.mix\",[\"rNear\",[6,\"std.max\",[\"rCircle\",\"rNear\"]],[6,\"std.smoothstep\",[[6,\"d.f32\",[[5,\"0\"]]],[5,\"0.25\"],\"outsideDist\"]]]]],[10,[6,\"d.vec4f\",[\"rOut\",[7,\"t\",\"g\"],[7,\"t\",\"b\"],[7,\"t\",\"a\"]]]]]]},externals:{\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.floor\":()=>ie,\"d.f32\":()=>n,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"std.mix\":()=>K,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"fieldSampleTexelArg\"),texelSpanFn:w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_div(B(t,s),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"spanX\"},{type:\"i\",name:\"spanY\"},{type:\"i\",name:\"activeResF\"}],body:[0,[[10,[1,[6,\"std.max\",[\"spanX\",\"spanY\"]],\"/\",\"activeResF\"]]]]},externals:{\"std.max\":()=>B}})&&e.f)({})).$name(\"fieldTexelSpan\")}}var pne={_vfOriginX:{schema:n,initial:0},_vfOriginY:{schema:n,initial:0},_vfSpanX:{schema:n,initial:1},_vfSpanY:{schema:n,initial:1},_vfActiveRes:{schema:n,initial:1},_vfRBound:{schema:n,initial:.6}};function KB(e,t){let{fieldSampleArg:s,fieldSampleGradArg:r,fieldSampleFastArg:o,fieldSampleTexelArg:a,texelSpanFn:i}=bA(),l=(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(e.accessor(),\"acc\"),c=d=>[d,l,t.originX,t.originY,t.spanX,t.spanY,t.activeResF,t.rBound];return{volumetricFieldSampler:d=>W(s,\"vfSample\",c(d)),volumetricFieldSamplerGrad:d=>W(r,\"vfSampleGrad\",c(d)),volumetricFieldSamplerFast:d=>W(o,\"vfSampleFast\",c(d)),volumetricFieldSamplerTexel:d=>W(a,\"vfSampleTexel\",c(d)),volumetricFieldTexelSpan:W(i,\"vfTexelSpan\",[t.spanX,t.spanY,t.activeResF])}}function mne(e,t={}){let{patternMode:s=\"none\",gradSampler:r=\"fast\",bakedGradients:o=\"none\"}=t,{computeOutputs:a,uniforms:i,propValues:l,getCpuValue:c}=e,d=a?.volumetricFieldTexture;if(d){let h=KB(d,{originX:i._vfOriginX,originY:i._vfOriginY,spanX:i._vfSpanX,spanY:i._vfSpanY,activeResF:i._vfActiveRes,rBound:i._vfRBound}),g=o!==\"none\",y=g?h.volumetricFieldSamplerGrad:h.volumetricFieldSampler;return{sampler:y,gradSampler:r===\"same\"?y:h.volumetricFieldSamplerFast,texelSampler:h.volumetricFieldSamplerTexel,volumetric:!0,bakedGradients:g}}let u=l.shapeSdfUrl||\"\",p=l.shapeType||\"\";if(u){let h=o===\"all\",g=h?PB(e,u):SB(e,u);return{sampler:g,gradSampler:g,texelSampler:g,volumetric:VB(p),bakedGradients:h}}let _=bc(p,c(\"shape\")),f=MB(_,EB(e,()=>c(\"shape\")),s);return{sampler:f,gradSampler:f,texelSampler:f,volumetric:M_(_),bakedGradients:!1}}var rd=Math.PI/180;function xA(e){if(e&&typeof e==\"object\")return e;if(typeof e==\"string\")try{return JSON.parse(e)}catch{return{}}return{}}function AA(e,t,s,r,o={}){let a=Qv[t]??Qv.sphere3D,i=o.extraPad??(()=>0),{layout:l}=hA(),c=(globalThis.__TYPEGPU_AUTONAME__??(oe=>oe))(e.createUniform(vl),\"marchParamsBuffer\"),d=w.fn([A],n)((oe=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(oe.f=(le=>{\"use gpu\";let se=l.$.params;return t===\"cube3D\"?xd(le,se.pA,se.pB,se.pC,se.pD):t===\"torus3D\"?tx(le,se.pA,se.pB):t===\"octahedron3D\"?sx(le,se.pA):t===\"cylinder3D\"?rx(le,se.pA,se.pB,se.pD):t===\"capsule3D\"?ox(le,se.pA,se.pB):t===\"cone3D\"?ax(le,se.pA,se.pC,se.pB):t===\"pyramid3D\"?nx(le,se.pA,se.pB):t===\"prism3D\"?ix(le,se.pA,se.pB):t===\"ellipsoid3D\"?lx(le,se.pA,se.pB,se.pC):t===\"diamond3D\"?Zm(le,se.pA,se.pB):t===\"link3D\"?cx(le,se.pB,se.pA,se.pC):t===\"gem3D\"?ux(le,se.pA,se.pB,se.pC):t===\"helix3D\"?px(le,se.pA,se.pB,se.pC,n(3)):t===\"metaballs3D\"?mx(le,se.mb0,se.mb1,se.mb2,se.mb3,se.mb4,se.mb5,se.mb6,se.mb7,se.pA,se.pD):t===\"dodecahedron3D\"?_x(le,se.pA):t===\"hemisphere3D\"?fx(le,se.pA,se.pB):t===\"ribbon3D\"?vx(le,se.pA,se.pB,se.pC,se.mb0.x,se.mb0.y,se.mb0.z,se.mb1.x,se.mb1.y,se.mb1.z,se.mb2.x):t===\"blob3D\"?hx(le,se.pA,se.mb0.x,se.mb0.y,se.mb0.z,se.mb1.x,se.mb2.x):t===\"gyroscope3D\"?gx(le,se.pA,se.pB,se.pD,se.mb3.x,se.pC,se.mb0.x,se.mb0.y,se.mb1.x,se.mb1.y,se.mb2.x,se.mb2.y):ex(le,se.pA)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"pp\",[7,[7,\"layout\",\"$\"],\"params\"]],[11,[1,\"shapeType\",\"===\",[103,\"cube3D\"]],[10,[6,\"sdRoundBox\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"],[7,\"pp\",\"pD\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"torus3D\"]],[10,[6,\"sdTorus\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"octahedron3D\"]],[10,[6,\"sdOctahedron\",[\"p\",[7,\"pp\",\"pA\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"cylinder3D\"]],[10,[6,\"sdRoundCylinder\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pD\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"capsule3D\"]],[10,[6,\"sdCapsule\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"cone3D\"]],[10,[6,\"sdCappedCone\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pC\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"pyramid3D\"]],[10,[6,\"sdPyramid\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"prism3D\"]],[10,[6,\"sdHexPrism\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"ellipsoid3D\"]],[10,[6,\"sdEllipsoid\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"diamond3D\"]],[10,[6,\"sdBicone\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"link3D\"]],[10,[6,\"sdLink\",[\"p\",[7,\"pp\",\"pB\"],[7,\"pp\",\"pA\"],[7,\"pp\",\"pC\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"gem3D\"]],[10,[6,\"sdGem\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"helix3D\"]],[10,[6,\"sdHelix\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"],[6,\"d.f32\",[\"HELIX_TURNS\"]]]]],[11,[1,\"shapeType\",\"===\",[103,\"metaballs3D\"]],[10,[6,\"sdMetaballs\",[\"p\",[7,\"pp\",\"mb0\"],[7,\"pp\",\"mb1\"],[7,\"pp\",\"mb2\"],[7,\"pp\",\"mb3\"],[7,\"pp\",\"mb4\"],[7,\"pp\",\"mb5\"],[7,\"pp\",\"mb6\"],[7,\"pp\",\"mb7\"],[7,\"pp\",\"pA\"],[7,\"pp\",\"pD\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"dodecahedron3D\"]],[10,[6,\"sdDodecahedron\",[\"p\",[7,\"pp\",\"pA\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"hemisphere3D\"]],[10,[6,\"sdCutSphere\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"ribbon3D\"]],[10,[6,\"sdRibbon\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"],[7,[7,\"pp\",\"mb0\"],\"x\"],[7,[7,\"pp\",\"mb0\"],\"y\"],[7,[7,\"pp\",\"mb0\"],\"z\"],[7,[7,\"pp\",\"mb1\"],\"x\"],[7,[7,\"pp\",\"mb1\"],\"y\"],[7,[7,\"pp\",\"mb1\"],\"z\"],[7,[7,\"pp\",\"mb2\"],\"x\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"blob3D\"]],[10,[6,\"sdWobbleBlob\",[\"p\",[7,\"pp\",\"pA\"],[7,[7,\"pp\",\"mb0\"],\"x\"],[7,[7,\"pp\",\"mb0\"],\"y\"],[7,[7,\"pp\",\"mb0\"],\"z\"],[7,[7,\"pp\",\"mb1\"],\"x\"],[7,[7,\"pp\",\"mb2\"],\"x\"]]]],[11,[1,\"shapeType\",\"===\",[103,\"gyroscope3D\"]],[10,[6,\"sdGyroscope\",[\"p\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pD\"],[7,[7,\"pp\",\"mb3\"],\"x\"],[7,\"pp\",\"pC\"],[7,[7,\"pp\",\"mb0\"],\"x\"],[7,[7,\"pp\",\"mb0\"],\"y\"],[7,[7,\"pp\",\"mb1\"],\"x\"],[7,[7,\"pp\",\"mb1\"],\"y\"],[7,[7,\"pp\",\"mb2\"],\"x\"],[7,[7,\"pp\",\"mb2\"],\"y\"]]]],[10,[6,\"sdSphere\",[\"p\",[7,\"pp\",\"pA\"]]]]]]]]]]]]]]]]]]]]]]]]]},externals:{layout:()=>l,shapeType:()=>t,sdRoundBox:()=>xd,sdTorus:()=>tx,sdOctahedron:()=>sx,sdRoundCylinder:()=>rx,sdCapsule:()=>ox,sdCappedCone:()=>ax,sdPyramid:()=>nx,sdHexPrism:()=>ix,sdEllipsoid:()=>lx,sdBicone:()=>Zm,sdLink:()=>cx,sdGem:()=>ux,sdHelix:()=>px,\"d.f32\":()=>n,HELIX_TURNS:()=>3,sdMetaballs:()=>mx,sdDodecahedron:()=>_x,sdCutSphere:()=>fx,sdRibbon:()=>vx,sdWobbleBlob:()=>hx,sdGyroscope:()=>gx,sdSphere:()=>ex}})&&oe.f)({})),u=0,p=\"\",_=s,f=new Map,h=.35,g=.3,y=.3,b=0,x=.6,P=1,C=0,E=1,R=0,U=1,L=0,k=1,D=1,O=0,F=0,q=E_(),Y=Array.from({length:8},()=>[0,0,0]),V=()=>{c.write({rot:{cx:P,sx:C,cy:E,sy:R,cz:U,sz:L},rBound:x,spanX:k,spanY:D,originX:O,originY:F,activeRes:q,pA:h,pB:g,pC:y,pD:b,mb0:A(Y[0][0],Y[0][1],Y[0][2]),mb1:A(Y[1][0],Y[1][1],Y[1][2]),mb2:A(Y[2][0],Y[2][1],Y[2][2]),mb3:A(Y[3][0],Y[3][1],Y[3][2]),mb4:A(Y[4][0],Y[4][1],Y[4][2]),mb5:A(Y[5][0],Y[5][1],Y[5][2]),mb6:A(Y[6][0],Y[6][1],Y[6][2]),mb7:A(Y[7][0],Y[7][1],Y[7][2])})},X=oe=>{let le=oe?.deltaTime??0;u+=le;let se=r();if(se&&typeof se==\"object\")_=se;else if(typeof se==\"string\"&&se!==p){p=se;try{_=JSON.parse(se)}catch{}}let Te={elapsed:u,deltaTime:le,pointerX:oe?.pointer?.x??.5,pointerY:oe?.pointer?.y??.5,springs:f},de=ze=>_A(_[ze],a[ze]??0,Te,ze);g=.3,y=.3,b=0;for(let ze=0;ze<Y.length;ze++)Y[ze]=[0,0,0];switch(t){case\"cube3D\":h=de(\"sizeX\"),g=de(\"sizeY\"),y=de(\"sizeZ\"),b=de(\"rounding\");break;case\"torus3D\":h=de(\"radius\"),g=de(\"tube\");break;case\"cylinder3D\":h=de(\"radius\"),g=de(\"height\"),b=de(\"rounding\");break;case\"capsule3D\":h=de(\"radius\"),g=de(\"height\");break;case\"cone3D\":h=de(\"radius\"),g=de(\"height\"),y=de(\"topRadius\");break;case\"pyramid3D\":h=de(\"size\"),g=de(\"height\");break;case\"prism3D\":case\"diamond3D\":h=de(\"radius\"),g=de(\"height\");break;case\"ellipsoid3D\":h=de(\"radiusX\"),g=de(\"radiusY\"),y=de(\"radiusZ\");break;case\"link3D\":h=de(\"radius\"),g=de(\"length\"),y=de(\"tube\");break;case\"gem3D\":h=de(\"radius\"),g=de(\"height\"),y=de(\"facets\");break;case\"helix3D\":h=de(\"radius\"),g=de(\"tube\"),y=de(\"pitch\");break;case\"metaballs3D\":{h=de(\"ballRadius\"),g=de(\"spread\"),b=de(\"blend\");let ze=Math.min(8,Math.max(1,Math.round(de(\"balls\")))),Ie=g,ke=u*de(\"speed\")*.6;for(let Me=0;Me<Y.length;Me++){if(Me>=ze){Y[Me]=[0,99,0];continue}let we=Me*(sd/ze),Ve=we+ke,qe=.75+.25*Math.sin(ke*1.3+Me*1.7);Y[Me]=[Ie*Math.cos(Ve)*qe,Ie*.6*Math.sin(ke*.9+we*1.5),Ie*Math.sin(Ve)*qe]}break}case\"hemisphere3D\":h=de(\"radius\"),g=de(\"cut\");break;case\"ribbon3D\":{h=de(\"width\"),g=de(\"thickness\"),y=de(\"length\");let ze=u*de(\"speed\"),Ie=de(\"wave\")*(.8+.2*Math.sin(ze*.9)),ke=de(\"waveFrequency\"),Me=de(\"twist\")*rd/Math.max(y*2,.01);Y[0]=[Ie,ke,ze*1.1],Y[1]=[ze*.7+2.1,Me,ze*.45];let we=Math.abs(Ie)*ke+Math.abs(Me)*Math.hypot(h,g);Y[2]=[1/Math.sqrt(1+we*we),0,0];break}case\"blob3D\":{h=de(\"radius\");let ze=u*de(\"speed\"),Ie=de(\"wobble\")*(.75+.25*Math.sin(ze*.83)),ke=de(\"wobbliness\");Y[0]=[Ie,ke,ze*1.2],Y[1]=[ze*.77+1.3,0,0],Y[2]=[1/(1+Math.abs(Ie)*ke*1.3),0,0];break}case\"gyroscope3D\":{h=de(\"radius\"),g=de(\"width\"),y=de(\"core\"),b=de(\"thickness\");let ze=Math.max(0,Math.min(de(\"rounding\"),Math.min(g,b)*.5)),Ie=u*de(\"speed\"),ke=Ie*.9,Me=Ie*.63+1.1,we=Ie*1.21+2.3;Y[0]=[Math.cos(ke),Math.sin(ke),0],Y[1]=[Math.cos(Me),Math.sin(Me),0],Y[2]=[Math.cos(we),Math.sin(we),0],Y[3]=[ze,0,0];break}default:h=de(\"radius\");break}x=XB({..._,type:t})+.02+i();let xe=de(\"rotX\")*rd,Ae=de(\"rotY\")*rd,De=de(\"rotZ\")*rd;P=Math.cos(xe),C=Math.sin(xe),E=Math.cos(Ae),R=Math.sin(Ae),U=Math.cos(De),L=Math.sin(De);let Ee=x+.05;k=Ee*2,D=Ee*2,O=.5-Ee,F=.5-Ee,V()};X();let Q=oe=>{oe!==q&&(q=oe,V())},te=()=>`${h},${g},${y},${b},${x},`+Y.map(oe=>`${oe[0]},${oe[1]},${oe[2]}`).join(\",\");return{layout:l,marchParamsBuffer:c,sdfFn:d,update:X,setActiveRes:Q,getStateKey:()=>`${te()},${P},${C},${E},${R},${U},${L}`,getFootprint:()=>({spanX:k,spanY:D,originX:O,originY:F,rBound:x}),getRotation:()=>({cx:P,sx:C,cy:E,sy:R,cz:U,sz:L}),getGeometryKey:te}}function wA(e,t,s,r={}){let o=e.gpu.root,a=r.extraPad??(()=>0),{texture:i,getVersion:l}=uA(e,t),{layout:c}=jB(),d=(globalThis.__TYPEGPU_AUTONAME__??(oe=>oe))(o.createUniform(vl),\"marchParamsBuffer\"),u=n(512),p=n(511),_=w.fn([A],n)((oe=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(oe.f=(le=>{\"use gpu\";let se=c.$.params,Te=se.pA,de=se.pB,xe=__tsover_sub(__tsover_mul(__tsover_add(le.x,.5),u),.5),Ae=__tsover_sub(__tsover_mul(__tsover_sub(.5,le.y),u),.5),De=ie(xe),Ee=ie(Ae),ze=__tsover_sub(xe,De),Ie=__tsover_sub(Ae,Ee),ke=M(G(De,n(0),p)),Me=M(G(__tsover_add(De,1),n(0),p)),we=M(G(Ee,n(0),p)),Ve=M(G(__tsover_add(Ee,1),n(0),p)),qe=Ye(c.$.sdfSource,fe(ke,we),0).r,st=Ye(c.$.sdfSource,fe(Me,we),0).r,pt=Ye(c.$.sdfSource,fe(ke,Ve),0).r,Ze=Ye(c.$.sdfSource,fe(Me,Ve),0).r,nt=K(K(qe,st,ze),K(pt,Ze,ze),Ie),ht=__tsover_add(nt,de),yt=__tsover_add(__tsover_sub(re(le.z),Te),de),St=ye(B(ht,yt),n(0)),Mt=he(m(B(ht,n(0)),B(yt,n(0))));return __tsover_sub(__tsover_add(St,Mt),de)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"pp\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"halfDepth\",[7,\"pp\",\"pA\"]],[13,\"bevel\",[7,\"pp\",\"pB\"]],[13,\"u\",[1,[1,[1,[7,\"p\",\"x\"],\"+\",[5,\"0.5\"]],\"*\",\"SIZE_F\"],\"-\",[5,\"0.5\"]]],[13,\"v\",[1,[1,[1,[5,\"0.5\"],\"-\",[7,\"p\",\"y\"]],\"*\",\"SIZE_F\"],\"-\",[5,\"0.5\"]]],[13,\"x0\",[6,\"std.floor\",[\"u\"]]],[13,\"y0\",[6,\"std.floor\",[\"v\"]]],[13,\"fx\",[1,\"u\",\"-\",\"x0\"]],[13,\"fy\",[1,\"v\",\"-\",\"y0\"]],[13,\"cx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"x0\",[6,\"d.f32\",[[5,\"0\"]]],\"MAX_TEXEL\"]]]]],[13,\"cx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"MAX_TEXEL\"]]]]],[13,\"cy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"y0\",[6,\"d.f32\",[[5,\"0\"]]],\"MAX_TEXEL\"]]]]],[13,\"cy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"MAX_TEXEL\"]]]]],[13,\"s00\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[\"cx0\",\"cy0\"]],[5,\"0\"]]],\"r\"]],[13,\"s10\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[\"cx1\",\"cy0\"]],[5,\"0\"]]],\"r\"]],[13,\"s01\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[\"cx0\",\"cy1\"]],[5,\"0\"]]],\"r\"]],[13,\"s11\",[7,[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[\"cx1\",\"cy1\"]],[5,\"0\"]]],\"r\"]],[13,\"field2d\",[6,\"std.mix\",[[6,\"std.mix\",[\"s00\",\"s10\",\"fx\"]],[6,\"std.mix\",[\"s01\",\"s11\",\"fx\"]],\"fy\"]]],[13,\"d2\",[1,\"field2d\",\"+\",\"bevel\"]],[13,\"wz\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"z\"]]],\"-\",\"halfDepth\"],\"+\",\"bevel\"]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"d2\",\"wz\"]],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"d2\",[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[\"wz\",[6,\"d.f32\",[[5,\"0\"]]]]]]]]]],[10,[1,[1,\"inner\",\"+\",\"outer\"],\"-\",\"bevel\"]]]]},externals:{layout:()=>c,SIZE_F:()=>u,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"std.clamp\":()=>G,\"d.f32\":()=>n,MAX_TEXEL:()=>p,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.max\":()=>B,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&oe.f)({})),f=0,h=\"\",g=xA(s()),y=new Map,b=.06,x=.02,P=.75,C=1,E=0,R=1,U=0,L=1,k=0,D=1,O=1,F=0,q=0,Y=E_(),V=()=>{d.write({rot:{cx:C,sx:E,cy:R,sy:U,cz:L,sz:k},rBound:P,spanX:D,spanY:O,originX:F,originY:q,activeRes:Y,pA:b,pB:x,pC:0,pD:0,mb0:A(0,0,0),mb1:A(0,0,0),mb2:A(0,0,0),mb3:A(0,0,0),mb4:A(0,0,0),mb5:A(0,0,0),mb6:A(0,0,0),mb7:A(0,0,0)})},X=oe=>{let le=oe?.deltaTime??0;f+=le;let se=s();if(se&&typeof se==\"object\")g=se;else if(typeof se==\"string\"&&se!==h){h=se;try{g=JSON.parse(se)}catch{}}let Te={elapsed:f,deltaTime:le,pointerX:oe?.pointer?.x??.5,pointerY:oe?.pointer?.y??.5,springs:y},de=ht=>_A(g[ht],NB[ht]??0,Te,ht);b=Math.max(.001,de(\"depth\")*.5),x=Math.min(Math.max(0,de(\"bevel\")),b);let xe=Kh(t),Ae=xe?Math.min(.5,xe.hwf+.02):.5,De=xe?Math.min(.5,xe.hhf+.02):.5,Ee=a();P=Math.hypot(Ae,De,b)+x+.03+Ee;let ze=de(\"rotX\")*rd,Ie=de(\"rotY\")*rd,ke=de(\"rotZ\")*rd;C=Math.cos(ze),E=Math.sin(ze),R=Math.cos(Ie),U=Math.sin(Ie),L=Math.cos(ke),k=Math.sin(ke);let Me={cx:C,sx:E,cy:R,sy:U,cz:L,sz:k},we=Jl(1,0,0,Me),Ve=Jl(0,1,0,Me),qe=x+.03+Ee,st=Math.abs(we.x)*Ae+Math.abs(we.y)*De+Math.abs(we.z)*b+qe,pt=Math.abs(Ve.x)*Ae+Math.abs(Ve.y)*De+Math.abs(Ve.z)*b+qe,Ze=st+.05,nt=pt+.05;D=Ze*2,O=nt*2,F=.5-Ze,q=.5-nt,V()};X();let Q=oe=>{oe!==Y&&(Y=oe,V())},te=()=>`${b},${x},${P},${l()}`,J=()=>`${te()},${D},${O},${C},${E},${R},${U},${L},${k}`;return{layout:c,marchParamsBuffer:d,sdfFn:_,sdfSourceTexture:i.texture,update:X,setActiveRes:Q,getStateKey:J,getFootprint:()=>({spanX:D,spanY:O,originX:F,originY:q,rBound:P}),getRotation:()=>({cx:C,sx:E,cy:R,sy:U,cz:L,sz:k}),getGeometryKey:te}}function QB(e,t,s,r,o={}){let a=o.extraPad??(()=>0),{layout:i}=hA(),l=(globalThis.__TYPEGPU_AUTONAME__??(L=>L))(e.createUniform(vl),\"marchParamsBuffer\"),c=P_(t),d=w.fn([A],n)((L=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(L.f=(k=>{\"use gpu\";let D=i.$.params,O=c(m(__tsover_add(k.x,.5),__tsover_sub(.5,k.y)),D.pA,D.pB,D.pC,D.pD,D.mb0.x,D.mb0.y,D.mb0.z,D.mb1.x).x,F=__tsover_sub(re(k.z),D.mb1.y),q=ye(B(O,F),n(0)),Y=he(m(B(O,n(0)),B(F,n(0))));return __tsover_add(q,Y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"}],body:[0,[[13,\"pp\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"uv\",[6,\"d.vec2f\",[[1,[7,\"p\",\"x\"],\"+\",[5,\"0.5\"]],[1,[5,\"0.5\"],\"-\",[7,\"p\",\"y\"]]]]],[13,\"d2\",[7,[6,\"analytic\",[\"uv\",[7,\"pp\",\"pA\"],[7,\"pp\",\"pB\"],[7,\"pp\",\"pC\"],[7,\"pp\",\"pD\"],[7,[7,\"pp\",\"mb0\"],\"x\"],[7,[7,\"pp\",\"mb0\"],\"y\"],[7,[7,\"pp\",\"mb0\"],\"z\"],[7,[7,\"pp\",\"mb1\"],\"x\"]]],\"x\"]],[13,\"wz\",[1,[6,\"std.abs\",[[7,\"p\",\"z\"]]],\"-\",[7,[7,\"pp\",\"mb1\"],\"y\"]]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"d2\",\"wz\"]],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec2f\",[[6,\"std.max\",[\"d2\",[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[\"wz\",[6,\"d.f32\",[[5,\"0\"]]]]]]]]]],[10,[1,\"inner\",\"+\",\"outer\"]]]]},externals:{layout:()=>i,\"d.vec2f\":()=>m,analytic:()=>c,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.max\":()=>B,\"d.f32\":()=>n,\"std.length\":()=>he}})&&L.f)({})),u=\"\",p=xA(s()),_=Hv(p),f=.06,h=.6,g=1,y=1,b=0,x=0,P=E_(),C=()=>{l.write({rot:{cx:1,sx:0,cy:1,sy:0,cz:1,sz:0},rBound:h,spanX:g,spanY:y,originX:b,originY:x,activeRes:P,pA:_.radius,pB:_.sides,pC:_.rounding,pD:_.innerRatio,mb0:A(_.rotation,_.height,_.offset),mb1:A(_.aperture,f,0),mb2:A(0,0,0),mb3:A(0,0,0),mb4:A(0,0,0),mb5:A(0,0,0),mb6:A(0,0,0),mb7:A(0,0,0)})},E=()=>{let L=s();if(L&&typeof L==\"object\")p=L;else if(typeof L==\"string\"&&L!==u){u=L;try{p=JSON.parse(L)}catch{}}_=Hv(p),f=Math.max(.001,r());let{hwf:k,hhf:D}=Jb({shape:{...p,radius:_.radius,height:_.height}});h=Math.hypot(k,D,f)+.02+a();let O=h+.05;g=O*2,y=O*2,b=.5-O,x=.5-O,C()};E();let R=L=>{L!==P&&(P=L,C())},U=()=>`${_.radius},${_.sides},${_.rounding},${_.innerRatio},${_.rotation},${_.height},${_.offset},${_.aperture},${f},${h}`;return{layout:i,marchParamsBuffer:l,sdfFn:d,update:E,setActiveRes:R,getStateKey:U,getFootprint:()=>({spanX:g,spanY:y,originX:b,originY:x,rBound:h}),getRotation:()=>({cx:1,sx:0,cy:1,sy:0,cz:1,sz:0}),getGeometryKey:U}}function _ne(e,t,s=\"none\",r=\"span\"){let o=e.gpu?.root;if(!o)return null;let a=xA(t()),i=e.getCpuValue(\"shapeSdfUrl\")||\"\",l=e.getCpuValue(\"shapeType\")||a.type||\"\",c=null;if(i&&Qh(l)?c=wA(e,i,t):!i&&M_(l)&&(c=AA(o,l,a,t)),!c)return null;let d=fA(c.sdfFn,s,r),u=gA(c.layout,d),p=yA(o,c.layout,u,e.onCleanup,{paramsUniform:c.marchParamsBuffer,sdfSourceTexture:c.sdfSourceTexture}),_=e.registerComputeTexture(p.fieldTexture),f=p.maxRes,h=\"\",g=Rr()?2:1,y=0,b={originX:0,originY:0,spanX:1,spanY:1,activeRes:1,rBound:.6};return{outputs:{volumetricFieldTexture:_},getComputeNodes:P=>{c.update(P);let C=c.getFootprint(),E=e.getCpuValue(\"scale\")??1,R=e.dimensions.height;f=vA(C.spanX,C.spanY,E,R,p.maxRes,f);let U=`${c.getStateKey()},${f}`;return U===h||(y++,g>1&&y%g!==0)?null:(h=U,c.setActiveRes(f),e.setExtraField(\"_vfOriginX\",C.originX),e.setExtraField(\"_vfOriginY\",C.originY),e.setExtraField(\"_vfSpanX\",C.spanX),e.setExtraField(\"_vfSpanY\",C.spanY),e.setExtraField(\"_vfActiveRes\",f),e.setExtraField(\"_vfRBound\",C.rBound),b.originX=C.originX,b.originY=C.originY,b.spanX=C.spanX,b.spanY=C.spanY,b.activeRes=f,b.rBound=C.rBound,[()=>p.dispatch(f)])},fieldTexture:p.fieldTexture,getFieldDomain:()=>({...b})}}var Zv=.08,Hr=w.fn([A,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=s.x,a=s.y,i=s.z,l=s.w,c=r.x,d=r.y,u=A(__tsover_add(__tsover_mul(t.x,i),__tsover_mul(t.z,l)),t.y,__tsover_add(__tsover_mul(__tsover_mul(t.x,l),-1),__tsover_mul(t.z,i))),p=A(u.x,__tsover_sub(__tsover_mul(u.y,o),__tsover_mul(u.z,a)),__tsover_add(__tsover_mul(u.y,a),__tsover_mul(u.z,o)));return A(__tsover_sub(__tsover_mul(p.x,c),__tsover_mul(p.y,d)),__tsover_add(__tsover_mul(p.x,d),__tsover_mul(p.y,c)),p.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"}],body:[0,[[13,\"cx\",[7,\"rp0\",\"x\"]],[13,\"sx\",[7,\"rp0\",\"y\"]],[13,\"cy\",[7,\"rp0\",\"z\"]],[13,\"sy\",[7,\"rp0\",\"w\"]],[13,\"cz\",[7,\"rp1\",\"x\"]],[13,\"sz\",[7,\"rp1\",\"y\"]],[13,\"py\",[6,\"d.vec3f\",[[1,[1,[7,\"p\",\"x\"],\"*\",\"cy\"],\"+\",[1,[7,\"p\",\"z\"],\"*\",\"sy\"]],[7,\"p\",\"y\"],[1,[1,[1,[7,\"p\",\"x\"],\"*\",\"sy\"],\"*\",[4,\"-\",[5,\"1\"]]],\"+\",[1,[7,\"p\",\"z\"],\"*\",\"cy\"]]]]],[13,\"px\",[6,\"d.vec3f\",[[7,\"py\",\"x\"],[1,[1,[7,\"py\",\"y\"],\"*\",\"cx\"],\"-\",[1,[7,\"py\",\"z\"],\"*\",\"sx\"]],[1,[1,[7,\"py\",\"y\"],\"*\",\"sx\"],\"+\",[1,[7,\"py\",\"z\"],\"*\",\"cx\"]]]]],[10,[6,\"d.vec3f\",[[1,[1,[7,\"px\",\"x\"],\"*\",\"cz\"],\"-\",[1,[7,\"px\",\"y\"],\"*\",\"sz\"]],[1,[1,[7,\"px\",\"x\"],\"*\",\"sz\"],\"+\",[1,[7,\"px\",\"y\"],\"*\",\"cz\"]],[7,\"px\",\"z\"]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})).$name(\"eulerRotatePacked\"),TA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";return __tsover_sub(he(Hr(t,s,r)),r.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[10,[1,[6,\"std.length\",[[6,\"eulerRotatePacked\",[\"p\",\"rp0\",\"rp1\"]]]],\"-\",[7,\"rp1\",\"z\"]]]]]},externals:{\"std.length\":()=>he,eulerRotatePacked:()=>Hr}})&&e.f)({})).$name(\"paramSphereSdf\"),SA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=vs(Hr(t,r,o));return m(__tsover_add(__tsover_div(Xt(l.z,l.x),6.283185307179586),.5),__tsover_mul(__tsover_add(l.y,1),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"n\",[6,\"std.normalize\",[[6,\"eulerRotatePacked\",[\"h\",\"rp0\",\"rp1\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,[6,\"std.atan2\",[[7,\"n\",\"z\"],[7,\"n\",\"x\"]]],\"/\",[5,\"6.283185307179586\"]],\"+\",[5,\"0.5\"]],[1,[1,[7,\"n\",\"y\"],\"+\",[5,\"1\"]],\"*\",[5,\"0.5\"]]]]]]]},externals:{\"std.normalize\":()=>vs,eulerRotatePacked:()=>Hr,\"d.vec2f\":()=>m,\"std.atan2\":()=>Xt}})&&e.f)({})).$name(\"sphereSurfaceUV\"),PA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Hr(t,s,r),l=m(__tsover_sub(he(m(i.x,i.z)),r.z),i.y);return __tsover_sub(he(l),r.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"p\",\"rp0\",\"rp1\"]]],[13,\"q\",[6,\"d.vec2f\",[[1,[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"rp\",\"x\"],[7,\"rp\",\"z\"]]]]],\"-\",[7,\"rp1\",\"z\"]],[7,\"rp\",\"y\"]]]],[10,[1,[6,\"std.length\",[\"q\"]],\"-\",[7,\"rp1\",\"w\"]]]]]},externals:{eulerRotatePacked:()=>Hr,\"d.vec2f\":()=>m,\"std.length\":()=>he}})&&e.f)({})).$name(\"paramTorusSdf\"),CA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Hr(t,r,o),c=__tsover_add(__tsover_div(Xt(l.z,l.x),6.283185307179586),.5),d=__tsover_sub(he(m(l.x,l.z)),o.z);return m(c,__tsover_add(__tsover_div(Xt(l.y,d),6.283185307179586),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"h\",\"rp0\",\"rp1\"]]],[13,\"u\",[1,[1,[6,\"std.atan2\",[[7,\"rp\",\"z\"],[7,\"rp\",\"x\"]]],\"/\",[5,\"6.283185307179586\"]],\"+\",[5,\"0.5\"]]],[13,\"ringDist\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[7,\"rp\",\"x\"],[7,\"rp\",\"z\"]]]]],\"-\",[7,\"rp1\",\"z\"]]],[10,[6,\"d.vec2f\",[\"u\",[1,[1,[6,\"std.atan2\",[[7,\"rp\",\"y\"],\"ringDist\"]],\"/\",[5,\"6.283185307179586\"]],\"+\",[5,\"0.5\"]]]]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.atan2\":()=>Xt,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"torusSurfaceUV\"),MA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Hr(t,s,r),l=o.y,c=__tsover_add(__tsover_sub(re(i.x),r.z),l),d=__tsover_add(__tsover_sub(re(i.y),r.w),l),u=__tsover_add(__tsover_sub(re(i.z),o.x),l),p=he(A(B(c,n(0)),B(d,n(0)),B(u,n(0)))),_=ye(B(c,B(d,u)),n(0));return __tsover_sub(__tsover_add(p,_),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"p\",\"rp0\",\"rp1\"]]],[13,\"rounding\",[7,\"rp2\",\"y\"]],[13,\"qx\",[1,[1,[6,\"std.abs\",[[7,\"rp\",\"x\"]]],\"-\",[7,\"rp1\",\"z\"]],\"+\",\"rounding\"]],[13,\"qy\",[1,[1,[6,\"std.abs\",[[7,\"rp\",\"y\"]]],\"-\",[7,\"rp1\",\"w\"]],\"+\",\"rounding\"]],[13,\"qz\",[1,[1,[6,\"std.abs\",[[7,\"rp\",\"z\"]]],\"-\",[7,\"rp2\",\"x\"]],\"+\",\"rounding\"]],[13,\"outer\",[6,\"std.length\",[[6,\"d.vec3f\",[[6,\"std.max\",[\"qx\",[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[\"qy\",[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[\"qz\",[6,\"d.f32\",[[5,\"0\"]]]]]]]]]],[13,\"inner\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",[6,\"std.max\",[\"qy\",\"qz\"]]]],[6,\"d.f32\",[[5,\"0\"]]]]]],[10,[1,[1,\"outer\",\"+\",\"inner\"],\"-\",\"rounding\"]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.abs\":()=>re,\"std.length\":()=>he,\"d.vec3f\":()=>A,\"std.max\":()=>B,\"d.f32\":()=>n,\"std.min\":()=>ye}})&&e.f)({})).$name(\"paramBoxSdf\"),EA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Hr(t,r,o),c=Hr(s,r,o),d=A(re(c.x),re(c.y),re(c.z)),u=o.z,p=o.w,_=a.x,f=__tsover_div(.5,B(p,_)),h=__tsover_div(.5,B(u,_)),g=__tsover_div(.5,B(u,p)),y=m(__tsover_add(__tsover_mul(l.z,f),.5),__tsover_add(__tsover_mul(l.y,f),.5)),b=m(__tsover_add(__tsover_mul(l.x,h),.5),__tsover_add(__tsover_mul(l.z,h),.5)),x=m(__tsover_add(__tsover_mul(l.x,g),.5),__tsover_add(__tsover_mul(l.y,g),.5)),P=N(x,y,d.x>d.z);return N(N(x,b,d.y>d.z),P,d.x>d.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"h\",\"rp0\",\"rp1\"]]],[13,\"rn\",[6,\"eulerRotatePacked\",[\"n\",\"rp0\",\"rp1\"]]],[13,\"an\",[6,\"d.vec3f\",[[6,\"std.abs\",[[7,\"rn\",\"x\"]]],[6,\"std.abs\",[[7,\"rn\",\"y\"]]],[6,\"std.abs\",[[7,\"rn\",\"z\"]]]]]],[13,\"hx\",[7,\"rp1\",\"z\"]],[13,\"hy\",[7,\"rp1\",\"w\"]],[13,\"hz\",[7,\"rp2\",\"x\"]],[13,\"scaleX\",[1,[5,\"0.5\"],\"/\",[6,\"std.max\",[\"hy\",\"hz\"]]]],[13,\"scaleY\",[1,[5,\"0.5\"],\"/\",[6,\"std.max\",[\"hx\",\"hz\"]]]],[13,\"scaleZ\",[1,[5,\"0.5\"],\"/\",[6,\"std.max\",[\"hx\",\"hy\"]]]],[13,\"uvX\",[6,\"d.vec2f\",[[1,[1,[7,\"rp\",\"z\"],\"*\",\"scaleX\"],\"+\",[5,\"0.5\"]],[1,[1,[7,\"rp\",\"y\"],\"*\",\"scaleX\"],\"+\",[5,\"0.5\"]]]]],[13,\"uvY\",[6,\"d.vec2f\",[[1,[1,[7,\"rp\",\"x\"],\"*\",\"scaleY\"],\"+\",[5,\"0.5\"]],[1,[1,[7,\"rp\",\"z\"],\"*\",\"scaleY\"],\"+\",[5,\"0.5\"]]]]],[13,\"uvZ\",[6,\"d.vec2f\",[[1,[1,[7,\"rp\",\"x\"],\"*\",\"scaleZ\"],\"+\",[5,\"0.5\"]],[1,[1,[7,\"rp\",\"y\"],\"*\",\"scaleZ\"],\"+\",[5,\"0.5\"]]]]],[13,\"pickXZ\",[6,\"std.select\",[\"uvZ\",\"uvX\",[1,[7,\"an\",\"x\"],\">\",[7,\"an\",\"z\"]]]]],[13,\"pickYZ\",[6,\"std.select\",[\"uvZ\",\"uvY\",[1,[7,\"an\",\"y\"],\">\",[7,\"an\",\"z\"]]]]],[10,[6,\"std.select\",[\"pickYZ\",\"pickXZ\",[1,[7,\"an\",\"x\"],\">\",[7,\"an\",\"y\"]]]]]]]},externals:{eulerRotatePacked:()=>Hr,\"d.vec3f\":()=>A,\"std.abs\":()=>re,\"std.max\":()=>B,\"d.vec2f\":()=>m,\"std.select\":()=>N}})&&e.f)({})).$name(\"boxFaceUV\"),kA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Hr(t,s,r),l=r.z,c=r.w,d=G(i.y,__tsover_mul(c,-1),c);return __tsover_sub(he(A(i.x,__tsover_sub(i.y,d),i.z)),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"p\",\"rp0\",\"rp1\"]]],[13,\"radius\",[7,\"rp1\",\"z\"]],[13,\"halfHeight\",[7,\"rp1\",\"w\"]],[13,\"clampedY\",[6,\"std.clamp\",[[7,\"rp\",\"y\"],[1,\"halfHeight\",\"*\",[4,\"-\",[5,\"1\"]]],\"halfHeight\"]]],[10,[1,[6,\"std.length\",[[6,\"d.vec3f\",[[7,\"rp\",\"x\"],[1,[7,\"rp\",\"y\"],\"-\",\"clampedY\"],[7,\"rp\",\"z\"]]]]],\"-\",\"radius\"]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec3f\":()=>A}})&&e.f)({})).$name(\"paramCapsuleSdf\"),BA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Hr(t,r,o),c=__tsover_add(__tsover_div(Xt(l.z,l.x),6.283185307179586),.5),d=__tsover_add(o.w,o.z);return m(c,G(__tsover_add(__tsover_mul(__tsover_div(l.y,d),.5),.5),0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"h\",\"rp0\",\"rp1\"]]],[13,\"u\",[1,[1,[6,\"std.atan2\",[[7,\"rp\",\"z\"],[7,\"rp\",\"x\"]]],\"/\",[5,\"6.283185307179586\"]],\"+\",[5,\"0.5\"]]],[13,\"span\",[1,[7,\"rp1\",\"w\"],\"+\",[7,\"rp1\",\"z\"]]],[10,[6,\"d.vec2f\",[\"u\",[6,\"std.clamp\",[[1,[1,[1,[7,\"rp\",\"y\"],\"/\",\"span\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]]]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.atan2\":()=>Xt,\"d.vec2f\":()=>m,\"std.clamp\":()=>G}})&&e.f)({})).$name(\"capsuleSurfaceUV\"),zA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=Hr(t,s,r),l=r.z,c=r.w,d=o.x,u=Xt(i.z,i.x),p=A(__tsover_mul(pe(u),l),0,__tsover_mul(ae(u),l)),_=A(__tsover_sub(i.x,p.x),__tsover_sub(i.y,p.y),__tsover_sub(i.z,p.z)),f=__tsover_mul(u,.5),h=A(__tsover_mul(pe(u),pe(f)),ae(f),__tsover_mul(ae(u),pe(f))),g=Se(_,h),y=B(__tsover_sub(re(g),c),n(0)),b=A(__tsover_sub(_.x,__tsover_mul(h.x,g)),__tsover_sub(_.y,__tsover_mul(h.y,g)),__tsover_sub(_.z,__tsover_mul(h.z,g)));return __tsover_sub(he(m(y,he(b))),d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"p\",\"rp0\",\"rp1\"]]],[13,\"ringRadius\",[7,\"rp1\",\"z\"]],[13,\"halfWidth\",[7,\"rp1\",\"w\"]],[13,\"thickness\",[7,\"rp2\",\"x\"]],[13,\"phi\",[6,\"std.atan2\",[[7,\"rp\",\"z\"],[7,\"rp\",\"x\"]]]],[13,\"spine\",[6,\"d.vec3f\",[[1,[6,\"std.cos\",[\"phi\"]],\"*\",\"ringRadius\"],[5,\"0\"],[1,[6,\"std.sin\",[\"phi\"]],\"*\",\"ringRadius\"]]]],[13,\"dvec\",[6,\"d.vec3f\",[[1,[7,\"rp\",\"x\"],\"-\",[7,\"spine\",\"x\"]],[1,[7,\"rp\",\"y\"],\"-\",[7,\"spine\",\"y\"]],[1,[7,\"rp\",\"z\"],\"-\",[7,\"spine\",\"z\"]]]]],[13,\"halfPhi\",[1,\"phi\",\"*\",[5,\"0.5\"]]],[13,\"ev\",[6,\"d.vec3f\",[[1,[6,\"std.cos\",[\"phi\"]],\"*\",[6,\"std.cos\",[\"halfPhi\"]]],[6,\"std.sin\",[\"halfPhi\"]],[1,[6,\"std.sin\",[\"phi\"]],\"*\",[6,\"std.cos\",[\"halfPhi\"]]]]]],[13,\"vProj\",[6,\"std.dot\",[\"dvec\",\"ev\"]]],[13,\"du\",[6,\"std.max\",[[1,[6,\"std.abs\",[\"vProj\"]],\"-\",\"halfWidth\"],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"perp\",[6,\"d.vec3f\",[[1,[7,\"dvec\",\"x\"],\"-\",[1,[7,\"ev\",\"x\"],\"*\",\"vProj\"]],[1,[7,\"dvec\",\"y\"],\"-\",[1,[7,\"ev\",\"y\"],\"*\",\"vProj\"]],[1,[7,\"dvec\",\"z\"],\"-\",[1,[7,\"ev\",\"z\"],\"*\",\"vProj\"]]]]],[10,[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"du\",[6,\"std.length\",[\"perp\"]]]]]],\"-\",\"thickness\"]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.atan2\":()=>Xt,\"d.vec3f\":()=>A,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"std.max\":()=>B,\"std.abs\":()=>re,\"d.f32\":()=>n,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"paramMobiusSdf\"),RA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=Hr(t,r,o),c=o.z,d=o.w,u=Xt(l.z,l.x),p=A(__tsover_mul(pe(u),c),0,__tsover_mul(ae(u),c)),_=A(__tsover_sub(l.x,p.x),__tsover_sub(l.y,p.y),__tsover_sub(l.z,p.z)),f=__tsover_mul(u,.5),h=Se(_,A(__tsover_mul(pe(u),pe(f)),ae(f),__tsover_mul(ae(u),pe(f))));return m(__tsover_add(__tsover_div(u,6.283185307179586),.5),__tsover_add(__tsover_mul(__tsover_div(h,d),.5),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"_t\"}],body:[0,[[13,\"rp\",[6,\"eulerRotatePacked\",[\"h\",\"rp0\",\"rp1\"]]],[13,\"ringRadius\",[7,\"rp1\",\"z\"]],[13,\"halfWidth\",[7,\"rp1\",\"w\"]],[13,\"phi\",[6,\"std.atan2\",[[7,\"rp\",\"z\"],[7,\"rp\",\"x\"]]]],[13,\"spine\",[6,\"d.vec3f\",[[1,[6,\"std.cos\",[\"phi\"]],\"*\",\"ringRadius\"],[5,\"0\"],[1,[6,\"std.sin\",[\"phi\"]],\"*\",\"ringRadius\"]]]],[13,\"dvec\",[6,\"d.vec3f\",[[1,[7,\"rp\",\"x\"],\"-\",[7,\"spine\",\"x\"]],[1,[7,\"rp\",\"y\"],\"-\",[7,\"spine\",\"y\"]],[1,[7,\"rp\",\"z\"],\"-\",[7,\"spine\",\"z\"]]]]],[13,\"halfPhi\",[1,\"phi\",\"*\",[5,\"0.5\"]]],[13,\"ev\",[6,\"d.vec3f\",[[1,[6,\"std.cos\",[\"phi\"]],\"*\",[6,\"std.cos\",[\"halfPhi\"]]],[6,\"std.sin\",[\"halfPhi\"]],[1,[6,\"std.sin\",[\"phi\"]],\"*\",[6,\"std.cos\",[\"halfPhi\"]]]]]],[13,\"vProj\",[6,\"std.dot\",[\"dvec\",\"ev\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"phi\",\"/\",[5,\"6.283185307179586\"]],\"+\",[5,\"0.5\"]],[1,[1,[1,\"vProj\",\"/\",\"halfWidth\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]]},externals:{eulerRotatePacked:()=>Hr,\"std.atan2\":()=>Xt,\"d.vec3f\":()=>A,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.dot\":()=>Se,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"mobiusSurfaceUV\"),DA=w.fn([A,S,S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=s.x,l=s.y,c=s.z,d=r.x,u=r.y,p=r.z,_=pe(i),f=ae(i),h=__tsover_add(__tsover_mul(t.x,_),__tsover_mul(t.y,f)),g=__tsover_add(__tsover_mul(__tsover_mul(t.x,f),-1),__tsover_mul(t.y,_)),y=__tsover_add(__tsover_add(__tsover_mul(h,l),u),__tsover_mul(a,Zv)),b=pe(y),x=ae(y),P=__tsover_add(__tsover_mul(g,b),__tsover_mul(t.z,x)),C=__tsover_add(__tsover_mul(__tsover_mul(g,x),-1),__tsover_mul(t.z,b)),E=B(__tsover_sub(re(P),c),n(0));return __tsover_div(__tsover_sub(he(m(E,C)),d),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"angleRad\",[7,\"rp0\",\"x\"]],[13,\"twist\",[7,\"rp0\",\"y\"]],[13,\"halfW\",[7,\"rp0\",\"z\"]],[13,\"thickness\",[7,\"rp1\",\"x\"]],[13,\"phase\",[7,\"rp1\",\"y\"]],[13,\"lipschitz\",[7,\"rp1\",\"z\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rpx\",[1,[1,[7,\"p\",\"x\"],\"*\",\"cosA\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"sinA\"]]],[13,\"rpy\",[1,[1,[1,[7,\"p\",\"x\"],\"*\",\"sinA\"],\"*\",[4,\"-\",[5,\"1\"]]],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"cosA\"]]],[13,\"twistAngle\",[1,[1,[1,\"rpx\",\"*\",\"twist\"],\"+\",\"phase\"],\"+\",[1,\"t\",\"*\",\"TWISTED_RIBBON_TWIST_RATE\"]]],[13,\"ca\",[6,\"std.cos\",[\"twistAngle\"]]],[13,\"sa\",[6,\"std.sin\",[\"twistAngle\"]]],[13,\"localU\",[1,[1,\"rpy\",\"*\",\"ca\"],\"+\",[1,[7,\"p\",\"z\"],\"*\",\"sa\"]]],[13,\"localV\",[1,[1,[1,\"rpy\",\"*\",\"sa\"],\"*\",[4,\"-\",[5,\"1\"]]],\"+\",[1,[7,\"p\",\"z\"],\"*\",\"ca\"]]],[13,\"du\",[6,\"std.max\",[[1,[6,\"std.abs\",[\"localU\"]],\"-\",\"halfW\"],[6,\"d.f32\",[[5,\"0\"]]]]]],[10,[1,[1,[6,\"std.length\",[[6,\"d.vec2f\",[\"du\",\"localV\"]]]],\"-\",\"thickness\"],\"/\",\"lipschitz\"]]]]},externals:{\"std.cos\":()=>pe,\"std.sin\":()=>ae,TWISTED_RIBBON_TWIST_RATE:()=>Zv,\"std.max\":()=>B,\"std.abs\":()=>re,\"d.f32\":()=>n,\"std.length\":()=>he,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"paramTwistedRibbonSdf\"),LA=w.fn([A,A,S,S,S,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=r.x,c=r.y,d=r.z,u=r.w,p=o.y,_=pe(l),f=ae(l),h=__tsover_add(__tsover_mul(t.x,_),__tsover_mul(t.y,f)),g=__tsover_add(__tsover_mul(__tsover_mul(t.x,f),-1),__tsover_mul(t.y,_)),y=__tsover_add(__tsover_add(__tsover_mul(h,c),p),__tsover_mul(i,Zv)),b=pe(y),x=ae(y),P=B(ye(__tsover_add(__tsover_mul(g,b),__tsover_mul(t.z,x)),d),__tsover_mul(d,-1));return m(__tsover_add(__tsover_mul(h,.15),.5),__tsover_add(__tsover_div(P,u),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"h\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"_rp2\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"angleRad\",[7,\"rp0\",\"x\"]],[13,\"twist\",[7,\"rp0\",\"y\"]],[13,\"halfW\",[7,\"rp0\",\"z\"]],[13,\"width\",[7,\"rp0\",\"w\"]],[13,\"phase\",[7,\"rp1\",\"y\"]],[13,\"cosA\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"sinA\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"rpx\",[1,[1,[7,\"h\",\"x\"],\"*\",\"cosA\"],\"+\",[1,[7,\"h\",\"y\"],\"*\",\"sinA\"]]],[13,\"rpy\",[1,[1,[1,[7,\"h\",\"x\"],\"*\",\"sinA\"],\"*\",[4,\"-\",[5,\"1\"]]],\"+\",[1,[7,\"h\",\"y\"],\"*\",\"cosA\"]]],[13,\"twistAngle\",[1,[1,[1,\"rpx\",\"*\",\"twist\"],\"+\",\"phase\"],\"+\",[1,\"t\",\"*\",\"TWISTED_RIBBON_TWIST_RATE\"]]],[13,\"ca\",[6,\"std.cos\",[\"twistAngle\"]]],[13,\"sa\",[6,\"std.sin\",[\"twistAngle\"]]],[13,\"localU\",[1,[1,\"rpy\",\"*\",\"ca\"],\"+\",[1,[7,\"h\",\"z\"],\"*\",\"sa\"]]],[13,\"localUClamped\",[6,\"std.max\",[[6,\"std.min\",[\"localU\",\"halfW\"]],[1,\"halfW\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[10,[6,\"d.vec2f\",[[1,[1,\"rpx\",\"*\",[5,\"0.15\"]],\"+\",[5,\"0.5\"]],[1,[1,\"localUClamped\",\"/\",\"width\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{\"std.cos\":()=>pe,\"std.sin\":()=>ae,TWISTED_RIBBON_TWIST_RATE:()=>Zv,\"std.max\":()=>B,\"std.min\":()=>ye,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"twistedRibbonSurfaceUV\");function UA(e,t){let s={};return t.forEach((r,o)=>{s[`${e}${o}`]={schema:S,initial:r}}),s}var Lm=Rr()?128:192,OA=Math.ceil(Lm/4)*Lm*Lm,sl=Rr()?512:1024,ZB=\"r32float\",m1=Math.PI/180,fne=.8660254,yP=1.74,I0=2.39996323,bP=.27,xP=1e9,GA=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cam:UM(16,OM(32,Ad)),lightDir:A,voxelSize:n,lightT1:A,fill:n,lightT2:A,voxelScale:n,bevel:n,gridOrigin:n,gridN:n,gridW:n}),\"VoxelMarchParams\");function vne(){return w.bindGroupLayout({vox:{uniform:GA},occ:{storage:Fe(M,OA),access:\"readonly\"},shadowMap:{storageTexture:zt(ZB,\"write-only\")}})}function hne(){return w.bindGroupLayout({vox:{uniform:GA},occ:{storage:Fe(M,OA),access:\"mutable\"}})}function gne(e,t,s,r){let o=w.fn([A],A)((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=(i=>{\"use gpu\";return r===\"view\"?ja(ja(i,s.$.vox.cam),t.$.params.rot):A(i.x,i.y,i.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pG\"}],body:[0,[[11,[1,\"gridSpace\",\"===\",[103,\"view\"]],[10,[6,\"rotateVec3\",[[6,\"rotateVec3\",[\"pG\",[7,[7,[7,\"bakeLayout\",\"$\"],\"vox\"],\"cam\"]]],[7,[7,[7,\"marchLayout\",\"$\"],\"params\"],\"rot\"]]]],[10,[6,\"d.vec3f\",[[7,\"pG\",\"x\"],[7,\"pG\",\"y\"],[7,\"pG\",\"z\"]]]]]]]},externals:{gridSpace:()=>r,rotateVec3:()=>ja,bakeLayout:()=>s,marchLayout:()=>t,\"d.vec3f\":()=>A}})&&a.f)({}));return w.fn([M,M,M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l,c)=>{\"use gpu\";let d=s.$.vox,u=M(d.gridN),p=M(d.gridW);if(i<p&&l<u&&c<u){let _=d.voxelSize,f=d.gridOrigin,h=M(0),g=M(1);for(let y=0;y<4;y++){let b=__tsover_add(__tsover_mul(i,M(4)),M(y)),x=M(1);if(b<u){let P=e(o(A(__tsover_add(__tsover_mul(__tsover_add(n(b),.5),_),f),__tsover_add(__tsover_mul(__tsover_add(n(l),.5),_),f),__tsover_add(__tsover_mul(__tsover_add(n(c),.5),_),f)))),C=G(__tsover_sub(__tsover_sub(__tsover_div(P,_),d.fill),yP),0,254);x=N(__tsover_add(M(1),M(ie(C))),M(0),P<__tsover_mul(d.fill,_))}h=__tsover_add(h,__tsover_mul(x,g)),g=__tsover_mul(g,M(256))}s.$.occ[__tsover_add(i,__tsover_mul(__tsover_add(l,__tsover_mul(c,u)),p))]=h}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"wx\"},{type:\"i\",name:\"iy\"},{type:\"i\",name:\"iz\"}],body:[0,[[13,\"vx\",[7,[7,\"bakeLayout\",\"$\"],\"vox\"]],[13,\"n\",[6,\"d.u32\",[[7,\"vx\",\"gridN\"]]]],[13,\"w\",[6,\"d.u32\",[[7,\"vx\",\"gridW\"]]]],[11,[3,[3,[1,\"wx\",\"<\",\"w\"],\"&&\",[1,\"iy\",\"<\",\"n\"]],\"&&\",[1,\"iz\",\"<\",\"n\"]],[0,[[13,\"v\",[7,\"vx\",\"voxelSize\"]],[13,\"o\",[7,\"vx\",\"gridOrigin\"]],[12,\"word\",[6,\"d.u32\",[[5,\"0\"]]]],[12,\"scale\",[6,\"d.u32\",[[5,\"1\"]]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",[5,\"4\"]],[102,\"++\",\"k\"],[0,[[13,\"ix\",[1,[1,\"wx\",\"*\",[6,\"d.u32\",[[5,\"4\"]]]],\"+\",[6,\"d.u32\",[\"k\"]]]],[12,\"val\",[6,\"d.u32\",[[5,\"1\"]]]],[11,[1,\"ix\",\"<\",\"n\"],[0,[[13,\"c\",[6,\"d.vec3f\",[[1,[1,[1,[6,\"d.f32\",[\"ix\"]],\"+\",[5,\"0.5\"]],\"*\",\"v\"],\"+\",\"o\"],[1,[1,[1,[6,\"d.f32\",[\"iy\"]],\"+\",[5,\"0.5\"]],\"*\",\"v\"],\"+\",\"o\"],[1,[1,[1,[6,\"d.f32\",[\"iz\"]],\"+\",[5,\"0.5\"]],\"*\",\"v\"],\"+\",\"o\"]]]],[13,\"dd\",[6,\"sdfFn\",[[6,\"gridToShape\",[\"c\"]]]]],[13,\"clear\",[6,\"std.clamp\",[[1,[1,[1,\"dd\",\"/\",\"v\"],\"-\",[7,\"vx\",\"fill\"]],\"-\",\"CLEARANCE_MARGIN\"],[5,\"0\"],[5,\"254\"]]]],[2,\"val\",\"=\",[6,\"std.select\",[[1,[6,\"d.u32\",[[5,\"1\"]]],\"+\",[6,\"d.u32\",[[6,\"std.floor\",[\"clear\"]]]]],[6,\"d.u32\",[[5,\"0\"]]],[1,\"dd\",\"<\",[1,[7,\"vx\",\"fill\"],\"*\",\"v\"]]]]]]]],[2,\"word\",\"=\",[1,\"word\",\"+\",[1,\"val\",\"*\",\"scale\"]]],[2,\"scale\",\"=\",[1,\"scale\",\"*\",[6,\"d.u32\",[[5,\"256\"]]]]]]]],[2,[8,[7,[7,\"bakeLayout\",\"$\"],\"occ\"],[1,\"wx\",\"+\",[1,[1,\"iy\",\"+\",[1,\"iz\",\"*\",\"n\"]],\"*\",\"w\"]]],\"=\",\"word\"]]]]]]},externals:{bakeLayout:()=>s,\"d.u32\":()=>M,\"d.vec3f\":()=>A,\"d.f32\":()=>n,sdfFn:()=>e,gridToShape:()=>o,\"std.clamp\":()=>G,CLEARANCE_MARGIN:()=>yP,\"std.select\":()=>N,\"std.floor\":()=>ie}})&&a.f)({})).$name(\"voxelGridBake\")}function JB(e,t){let s=w.fn([n,n,n],n)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=e.$.vox,f=_.gridN,h=n(1);if(d>=0&&d<f&&u>=0&&u<f&&p>=0&&p<f){let g=ie(__tsover_mul(d,.25)),y=__tsover_sub(d,__tsover_mul(g,4)),b=__tsover_add(M(g),__tsover_mul(__tsover_add(M(u),__tsover_mul(M(p),M(f))),M(_.gridW))),x=e.$.occ[b];h=n(p7(x,__tsover_mul(M(y),M(8)),M(8)))}return h}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"},{type:\"i\",name:\"cz\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[13,\"n\",[7,\"vx\",\"gridN\"]],[12,\"out\",[6,\"d.f32\",[[5,\"1\"]]]],[11,[3,[3,[3,[3,[3,[1,\"cx\",\">=\",[5,\"0\"]],\"&&\",[1,\"cx\",\"<\",\"n\"]],\"&&\",[1,\"cy\",\">=\",[5,\"0\"]]],\"&&\",[1,\"cy\",\"<\",\"n\"]],\"&&\",[1,\"cz\",\">=\",[5,\"0\"]]],\"&&\",[1,\"cz\",\"<\",\"n\"]],[0,[[13,\"wx\",[6,\"std.floor\",[[1,\"cx\",\"*\",[5,\"0.25\"]]]]],[13,\"sub\",[1,\"cx\",\"-\",[1,\"wx\",\"*\",[5,\"4\"]]]],[13,\"idx\",[1,[6,\"d.u32\",[\"wx\"]],\"+\",[1,[1,[6,\"d.u32\",[\"cy\"]],\"+\",[1,[6,\"d.u32\",[\"cz\"]],\"*\",[6,\"d.u32\",[\"n\"]]]],\"*\",[6,\"d.u32\",[[7,\"vx\",\"gridW\"]]]]]],[13,\"word\",[8,[7,[7,\"voxLayout\",\"$\"],\"occ\"],\"idx\"]],[2,\"out\",\"=\",[6,\"d.f32\",[[6,\"std.extractBits\",[\"word\",[1,[6,\"d.u32\",[\"sub\"]],\"*\",[6,\"d.u32\",[[5,\"8\"]]]],[6,\"d.u32\",[[5,\"8\"]]]]]]]]]]],[10,\"out\"]]]},externals:{voxLayout:()=>e,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"std.extractBits\":()=>p7}})&&c.f)({})),r=w.fn([n,n,n],A)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=e.$.vox;return A(__tsover_add(__tsover_mul(__tsover_add(d,.5),_.voxelSize),_.gridOrigin),__tsover_add(__tsover_mul(__tsover_add(u,.5),_.voxelSize),_.gridOrigin),__tsover_add(__tsover_mul(__tsover_add(p,.5),_.voxelSize),_.gridOrigin))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"},{type:\"i\",name:\"cz\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[10,[6,\"d.vec3f\",[[1,[1,[1,\"cx\",\"+\",[5,\"0.5\"]],\"*\",[7,\"vx\",\"voxelSize\"]],\"+\",[7,\"vx\",\"gridOrigin\"]],[1,[1,[1,\"cy\",\"+\",[5,\"0.5\"]],\"*\",[7,\"vx\",\"voxelSize\"]],\"+\",[7,\"vx\",\"gridOrigin\"]],[1,[1,[1,\"cz\",\"+\",[5,\"0.5\"]],\"*\",[7,\"vx\",\"voxelSize\"]],\"+\",[7,\"vx\",\"gridOrigin\"]]]]]]]},externals:{voxLayout:()=>e,\"d.vec3f\":()=>A}})&&c.f)({})),o=w.fn([n,n,n],n)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";return N(n(0),n(1),s(d,u,p)<.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"},{type:\"i\",name:\"cz\"}],body:[0,[[10,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[6,\"cellValue\",[\"cx\",\"cy\",\"cz\"]],\"<\",[5,\"0.5\"]]]]]]]},externals:{\"std.select\":()=>N,\"d.f32\":()=>n,cellValue:()=>s}})&&c.f)({})),a=(globalThis.__TYPEGPU_AUTONAME__??(c=>c))(gt({hit:n,t:n}),\"SubHit\"),i=w.fn([A,A,A,n],a)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p,_)=>{\"use gpu\";let f=e.$.vox,h=__tsover_mul(__tsover_mul(f.voxelSize,.5),f.voxelScale),g=n(0),y=_;if(t===\"sphere\"){let b=d.sub(p),x=Se(b,u),P=__tsover_sub(Se(b,b),__tsover_mul(h,h)),C=__tsover_sub(__tsover_mul(x,x),P);if(C>=0){let E=__tsover_sub(__tsover_mul(x,-1),Ue(C));E>=__tsover_sub(_,1e-4)&&(g=n(1),y=E)}}else{let b=N(n(-1),n(1),u.x>=0),x=N(n(-1),n(1),u.y>=0),P=N(n(-1),n(1),u.z>=0),C=__tsover_div(b,B(re(u.x),1e-6)),E=__tsover_div(x,B(re(u.y),1e-6)),R=__tsover_div(P,B(re(u.z),1e-6)),U=__tsover_mul(__tsover_sub(__tsover_sub(p.x,h),d.x),C),L=__tsover_mul(__tsover_sub(__tsover_add(p.x,h),d.x),C),k=__tsover_mul(__tsover_sub(__tsover_sub(p.y,h),d.y),E),D=__tsover_mul(__tsover_sub(__tsover_add(p.y,h),d.y),E),O=__tsover_mul(__tsover_sub(__tsover_sub(p.z,h),d.z),R),F=__tsover_mul(__tsover_sub(__tsover_add(p.z,h),d.z),R),q=B(ye(U,L),B(ye(k,D),ye(O,F))),Y=ye(B(U,L),ye(B(k,D),B(O,F)));if(q<=Y&&Y>=__tsover_sub(_,1e-4))if(t===\"rounded\"){let V=__tsover_mul(G(f.bevel,n(0),n(1)),h),X=B(q,__tsover_sub(_,1e-4));for(let Q=0;Q<12;Q++){let te=xd(d.add(u.mul(X)).sub(p),h,h,h,V);if(te<__tsover_mul(h,.003)){g=n(1);break}if(X=__tsover_add(X,te),X>Y)break}y=X}else g=n(1),y=B(q,_)}return a({hit:g,t:y})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ro\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"c\"},{type:\"i\",name:\"tNow\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[13,\"h\",[1,[1,[7,\"vx\",\"voxelSize\"],\"*\",[5,\"0.5\"]],\"*\",[7,\"vx\",\"voxelScale\"]]],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"t\",\"tNow\"],[11,[1,\"style\",\"===\",[103,\"sphere\"]],[0,[[13,\"oc\",[6,[7,\"ro\",\"sub\"],[\"c\"]]],[13,\"b\",[6,\"std.dot\",[\"oc\",\"rd\"]]],[13,\"cc\",[1,[6,\"std.dot\",[\"oc\",\"oc\"]],\"-\",[1,\"h\",\"*\",\"h\"]]],[13,\"disc\",[1,[1,\"b\",\"*\",\"b\"],\"-\",\"cc\"]],[11,[1,\"disc\",\">=\",[5,\"0\"]],[0,[[13,\"ts\",[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"-\",[6,\"std.sqrt\",[\"disc\"]]]],[11,[1,\"ts\",\">=\",[1,\"tNow\",\"-\",[5,\"0.0001\"]]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[2,\"t\",\"=\",\"ts\"]]]]]]]]],[0,[[13,\"sx\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"x\"],\">=\",[5,\"0\"]]]]],[13,\"sy\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"y\"],\">=\",[5,\"0\"]]]]],[13,\"sz\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"z\"],\">=\",[5,\"0\"]]]]],[13,\"ix\",[1,\"sx\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"x\"]]],[5,\"0.000001\"]]]]],[13,\"iy\",[1,\"sy\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"y\"]]],[5,\"0.000001\"]]]]],[13,\"iz\",[1,\"sz\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"z\"]]],[5,\"0.000001\"]]]]],[13,\"t1x\",[1,[1,[1,[7,\"c\",\"x\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"x\"]],\"*\",\"ix\"]],[13,\"t2x\",[1,[1,[1,[7,\"c\",\"x\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"x\"]],\"*\",\"ix\"]],[13,\"t1y\",[1,[1,[1,[7,\"c\",\"y\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"y\"]],\"*\",\"iy\"]],[13,\"t2y\",[1,[1,[1,[7,\"c\",\"y\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"y\"]],\"*\",\"iy\"]],[13,\"t1z\",[1,[1,[1,[7,\"c\",\"z\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"z\"]],\"*\",\"iz\"]],[13,\"t2z\",[1,[1,[1,[7,\"c\",\"z\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"z\"]],\"*\",\"iz\"]],[13,\"tIn\",[6,\"std.max\",[[6,\"std.min\",[\"t1x\",\"t2x\"]],[6,\"std.max\",[[6,\"std.min\",[\"t1y\",\"t2y\"]],[6,\"std.min\",[\"t1z\",\"t2z\"]]]]]]],[13,\"tOut\",[6,\"std.min\",[[6,\"std.max\",[\"t1x\",\"t2x\"]],[6,\"std.min\",[[6,\"std.max\",[\"t1y\",\"t2y\"]],[6,\"std.max\",[\"t1z\",\"t2z\"]]]]]]],[11,[1,\"tIn\",\"<=\",\"tOut\"],[0,[[11,[1,\"tOut\",\">=\",[1,\"tNow\",\"-\",[5,\"0.0001\"]]],[0,[[11,[1,\"style\",\"===\",[103,\"rounded\"]],[0,[[13,\"r\",[1,[6,\"std.clamp\",[[7,\"vx\",\"bevel\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]],\"*\",\"h\"]],[12,\"s\",[6,\"std.max\",[\"tIn\",[1,\"tNow\",\"-\",[5,\"0.0001\"]]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"12\"]],[102,\"++\",\"i\"],[0,[[13,\"p\",[6,[7,[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"s\"]]]],\"sub\"],[\"c\"]]],[13,\"dd\",[6,\"sdRoundBox\",[\"p\",\"h\",\"h\",\"h\",\"r\"]]],[11,[1,\"dd\",\"<\",[1,\"h\",\"*\",[5,\"0.003\"]]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[17]]]],[2,\"s\",\"=\",[1,\"s\",\"+\",\"dd\"]],[11,[1,\"s\",\">\",\"tOut\"],[0,[[17]]]]]]],[2,\"t\",\"=\",\"s\"]]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[2,\"t\",\"=\",[6,\"std.max\",[\"tIn\",\"tNow\"]]]]]]]]]]]]]]],[10,[6,\"SubHit\",[[104,{hit:\"hit\",t:\"t\"}]]]]]]},externals:{voxLayout:()=>e,\"d.f32\":()=>n,style:()=>t,\"std.dot\":()=>Se,\"std.sqrt\":()=>Ue,\"std.select\":()=>N,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.clamp\":()=>G,sdRoundBox:()=>xd,SubHit:()=>a}})&&c.f)({})),l=(globalThis.__TYPEGPU_AUTONAME__??(c=>c))(gt({hit:n,t:n,cx:n,cy:n,cz:n,minD:n}),\"DdaHit\");return{cellValue:s,cellCentre:r,occupied:o,subShapeHit:i,voxelDda:w.fn([A,A,n,n,A,j],l)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p,_,f,h)=>{\"use gpu\";let g=e.$.vox,y=g.voxelSize,b=g.gridOrigin,x=d.add(u.mul(p)),P=ie(__tsover_div(__tsover_sub(x.x,b),y)),C=ie(__tsover_div(__tsover_sub(x.y,b),y)),E=ie(__tsover_div(__tsover_sub(x.z,b),y)),R=N(n(-1),n(1),u.x>=0),U=N(n(-1),n(1),u.y>=0),L=N(n(-1),n(1),u.z>=0),k=__tsover_div(1,B(re(u.x),1e-6)),D=__tsover_div(1,B(re(u.y),1e-6)),O=__tsover_div(1,B(re(u.z),1e-6)),F=__tsover_mul(y,k),q=__tsover_mul(y,D),Y=__tsover_mul(y,O),V=__tsover_add(__tsover_mul(__tsover_add(P,B(R,n(0))),y),b),X=__tsover_add(__tsover_mul(__tsover_add(C,B(U,n(0))),y),b),Q=__tsover_add(__tsover_mul(__tsover_add(E,B(L,n(0))),y),b),te=__tsover_add(p,__tsover_mul(__tsover_mul(__tsover_sub(V,x.x),R),k)),J=__tsover_add(p,__tsover_mul(__tsover_mul(__tsover_sub(X,x.y),U),D)),oe=__tsover_add(p,__tsover_mul(__tsover_mul(__tsover_sub(Q,x.z),L),O)),le=p,se=n(0),Te=_,de=n(255);for(let xe=0;xe<h;xe++){let Ae=Et(__tsover_add(__tsover_add(re(__tsover_sub(P,f.x)),re(__tsover_sub(C,f.y))),re(__tsover_sub(E,f.z))),.5),De=s(P,C,E),Ee=B(__tsover_sub(De,1),0);if(de=ye(de,Ee),De<.5&&Ae<.5){let ze=i(d,u,r(P,C,E),le);if(ze.hit>.5){se=n(1),Te=ze.t;break}}if(Ee>=1){le=__tsover_add(le,__tsover_mul(Ee,y));let ze=d.add(u.mul(le));P=ie(__tsover_div(__tsover_sub(ze.x,b),y)),C=ie(__tsover_div(__tsover_sub(ze.y,b),y)),E=ie(__tsover_div(__tsover_sub(ze.z,b),y)),te=__tsover_add(le,__tsover_mul(__tsover_mul(__tsover_sub(__tsover_add(__tsover_mul(__tsover_add(P,B(R,n(0))),y),b),ze.x),R),k)),J=__tsover_add(le,__tsover_mul(__tsover_mul(__tsover_sub(__tsover_add(__tsover_mul(__tsover_add(C,B(U,n(0))),y),b),ze.y),U),D)),oe=__tsover_add(le,__tsover_mul(__tsover_mul(__tsover_sub(__tsover_add(__tsover_mul(__tsover_add(E,B(L,n(0))),y),b),ze.z),L),O))}else te<J?te<oe?(le=te,P=__tsover_add(P,R),te=__tsover_add(te,F)):(le=oe,E=__tsover_add(E,L),oe=__tsover_add(oe,Y)):J<oe?(le=J,C=__tsover_add(C,U),J=__tsover_add(J,q)):(le=oe,E=__tsover_add(E,L),oe=__tsover_add(oe,Y));if(le>_)break}return l({hit:se,t:Te,cx:P,cy:C,cz:E,minD:__tsover_mul(de,y)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ro\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"tStart\"},{type:\"i\",name:\"tEnd\"},{type:\"i\",name:\"skip\"},{type:\"i\",name:\"maxSteps\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[13,\"v\",[7,\"vx\",\"voxelSize\"]],[13,\"o\",[7,\"vx\",\"gridOrigin\"]],[13,\"p0\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"tStart\"]]]]],[12,\"cx\",[6,\"std.floor\",[[1,[1,[7,\"p0\",\"x\"],\"-\",\"o\"],\"/\",\"v\"]]]],[12,\"cy\",[6,\"std.floor\",[[1,[1,[7,\"p0\",\"y\"],\"-\",\"o\"],\"/\",\"v\"]]]],[12,\"cz\",[6,\"std.floor\",[[1,[1,[7,\"p0\",\"z\"],\"-\",\"o\"],\"/\",\"v\"]]]],[13,\"sx\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"x\"],\">=\",[5,\"0\"]]]]],[13,\"sy\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"y\"],\">=\",[5,\"0\"]]]]],[13,\"sz\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"z\"],\">=\",[5,\"0\"]]]]],[13,\"ax\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"x\"]]],[5,\"0.000001\"]]]]],[13,\"ay\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"y\"]]],[5,\"0.000001\"]]]]],[13,\"az\",[1,[5,\"1\"],\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"z\"]]],[5,\"0.000001\"]]]]],[13,\"dtx\",[1,\"v\",\"*\",\"ax\"]],[13,\"dty\",[1,\"v\",\"*\",\"ay\"]],[13,\"dtz\",[1,\"v\",\"*\",\"az\"]],[13,\"bx\",[1,[1,[1,\"cx\",\"+\",[6,\"std.max\",[\"sx\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"]],[13,\"by\",[1,[1,[1,\"cy\",\"+\",[6,\"std.max\",[\"sy\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"]],[13,\"bz\",[1,[1,[1,\"cz\",\"+\",[6,\"std.max\",[\"sz\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"]],[12,\"tmx\",[1,\"tStart\",\"+\",[1,[1,[1,\"bx\",\"-\",[7,\"p0\",\"x\"]],\"*\",\"sx\"],\"*\",\"ax\"]]],[12,\"tmy\",[1,\"tStart\",\"+\",[1,[1,[1,\"by\",\"-\",[7,\"p0\",\"y\"]],\"*\",\"sy\"],\"*\",\"ay\"]]],[12,\"tmz\",[1,\"tStart\",\"+\",[1,[1,[1,\"bz\",\"-\",[7,\"p0\",\"z\"]],\"*\",\"sz\"],\"*\",\"az\"]]],[12,\"t\",\"tStart\"],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"tHit\",\"tEnd\"],[12,\"minClear\",[6,\"d.f32\",[[5,\"255\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"maxSteps\"],[102,\"++\",\"i\"],[0,[[13,\"same\",[6,\"std.step\",[[1,[1,[6,\"std.abs\",[[1,\"cx\",\"-\",[7,\"skip\",\"x\"]]]],\"+\",[6,\"std.abs\",[[1,\"cy\",\"-\",[7,\"skip\",\"y\"]]]]],\"+\",[6,\"std.abs\",[[1,\"cz\",\"-\",[7,\"skip\",\"z\"]]]]],[5,\"0.5\"]]]],[13,\"val\",[6,\"cellValue\",[\"cx\",\"cy\",\"cz\"]]],[13,\"clear\",[6,\"std.max\",[[1,\"val\",\"-\",[5,\"1\"]],[5,\"0\"]]]],[2,\"minClear\",\"=\",[6,\"std.min\",[\"minClear\",\"clear\"]]],[11,[1,\"val\",\"<\",[5,\"0.5\"]],[0,[[11,[1,\"same\",\"<\",[5,\"0.5\"]],[0,[[13,\"sh\",[6,\"subShapeHit\",[\"ro\",\"rd\",[6,\"cellCentre\",[\"cx\",\"cy\",\"cz\"]],\"t\"]]],[11,[1,[7,\"sh\",\"hit\"],\">\",[5,\"0.5\"]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[2,\"tHit\",\"=\",[7,\"sh\",\"t\"]],[17]]]]]]]]]],[11,[1,\"clear\",\">=\",[5,\"1\"]],[0,[[2,\"t\",\"=\",[1,\"t\",\"+\",[1,\"clear\",\"*\",\"v\"]]],[13,\"pj\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"t\"]]]]],[2,\"cx\",\"=\",[6,\"std.floor\",[[1,[1,[7,\"pj\",\"x\"],\"-\",\"o\"],\"/\",\"v\"]]]],[2,\"cy\",\"=\",[6,\"std.floor\",[[1,[1,[7,\"pj\",\"y\"],\"-\",\"o\"],\"/\",\"v\"]]]],[2,\"cz\",\"=\",[6,\"std.floor\",[[1,[1,[7,\"pj\",\"z\"],\"-\",\"o\"],\"/\",\"v\"]]]],[2,\"tmx\",\"=\",[1,\"t\",\"+\",[1,[1,[1,[1,[1,[1,\"cx\",\"+\",[6,\"std.max\",[\"sx\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"],\"-\",[7,\"pj\",\"x\"]],\"*\",\"sx\"],\"*\",\"ax\"]]],[2,\"tmy\",\"=\",[1,\"t\",\"+\",[1,[1,[1,[1,[1,[1,\"cy\",\"+\",[6,\"std.max\",[\"sy\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"],\"-\",[7,\"pj\",\"y\"]],\"*\",\"sy\"],\"*\",\"ay\"]]],[2,\"tmz\",\"=\",[1,\"t\",\"+\",[1,[1,[1,[1,[1,[1,\"cz\",\"+\",[6,\"std.max\",[\"sz\",[6,\"d.f32\",[[5,\"0\"]]]]]],\"*\",\"v\"],\"+\",\"o\"],\"-\",[7,\"pj\",\"z\"]],\"*\",\"sz\"],\"*\",\"az\"]]]]],[0,[[11,[1,\"tmx\",\"<\",\"tmy\"],[0,[[11,[1,\"tmx\",\"<\",\"tmz\"],[0,[[2,\"t\",\"=\",\"tmx\"],[2,\"cx\",\"=\",[1,\"cx\",\"+\",\"sx\"]],[2,\"tmx\",\"=\",[1,\"tmx\",\"+\",\"dtx\"]]]],[0,[[2,\"t\",\"=\",\"tmz\"],[2,\"cz\",\"=\",[1,\"cz\",\"+\",\"sz\"]],[2,\"tmz\",\"=\",[1,\"tmz\",\"+\",\"dtz\"]]]]]]],[0,[[11,[1,\"tmy\",\"<\",\"tmz\"],[0,[[2,\"t\",\"=\",\"tmy\"],[2,\"cy\",\"=\",[1,\"cy\",\"+\",\"sy\"]],[2,\"tmy\",\"=\",[1,\"tmy\",\"+\",\"dty\"]]]],[0,[[2,\"t\",\"=\",\"tmz\"],[2,\"cz\",\"=\",[1,\"cz\",\"+\",\"sz\"]],[2,\"tmz\",\"=\",[1,\"tmz\",\"+\",\"dtz\"]]]]]]]]]]],[11,[1,\"t\",\">\",\"tEnd\"],[0,[[17]]]]]]],[10,[6,\"DdaHit\",[[104,{hit:\"hit\",t:\"tHit\",cx:\"cx\",cy:\"cy\",cz:\"cz\",minD:[1,\"minClear\",\"*\",\"v\"]}]]]]]]},externals:{voxLayout:()=>e,\"std.floor\":()=>ie,\"std.select\":()=>N,\"d.f32\":()=>n,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.step\":()=>Et,cellValue:()=>s,\"std.min\":()=>ye,subShapeHit:()=>i,cellCentre:()=>r,DdaHit:()=>l}})&&c.f)({}))}}var ez=()=>Rr()?288:720;function yne(e,t){let{voxelDda:s}=JB(e,t),r=sl,o=ez();return w.fn([M,M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";let c=e.$.vox,d=__tsover_mul(c.gridOrigin,-1),u=__tsover_mul(__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(n(i),.5),n(r)),2),1),d),p=__tsover_mul(__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(n(l),.5),n(r)),2),1),d),_=c.lightDir,f=c.lightT1.mul(u).add(c.lightT2.mul(p)).add(_.mul(d)),h=_.mul(-1),g=n(xP),y=__tsover_add(__tsover_mul(u,u),__tsover_mul(p,p));if(y<__tsover_mul(d,d)){let b=Ue(__tsover_sub(__tsover_mul(d,d),y)),x=j(G(__tsover_add(__tsover_div(__tsover_mul(d,6),c.voxelSize),8),n(8),n(o))),P=A(-9999,-9999,-9999),C=s(f,h,__tsover_sub(d,b),__tsover_add(__tsover_add(d,b),.01),P,x);C.hit>.5&&(g=C.t)}lt(e.$.shadowMap,fe(i,l),S(g,0,0,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"},{type:\"i\",name:\"j\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[13,\"rB\",[1,[7,\"vx\",\"gridOrigin\"],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"u\",[1,[1,[1,[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"RES\"]]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]],\"*\",\"rB\"]],[13,\"w\",[1,[1,[1,[1,[1,[6,\"d.f32\",[\"j\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"RES\"]]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]],\"*\",\"rB\"]],[13,\"L\",[7,\"vx\",\"lightDir\"]],[13,\"p0\",[6,[7,[6,[7,[6,[7,[7,\"vx\",\"lightT1\"],\"mul\"],[\"u\"]],\"add\"],[[6,[7,[7,\"vx\",\"lightT2\"],\"mul\"],[\"w\"]]]],\"add\"],[[6,[7,\"L\",\"mul\"],[\"rB\"]]]]],[13,\"rd\",[6,[7,\"L\",\"mul\"],[[4,\"-\",[5,\"1\"]]]]],[12,\"depth\",[6,\"d.f32\",[\"SHADOW_FAR\"]]],[13,\"perp2\",[1,[1,\"u\",\"*\",\"u\"],\"+\",[1,\"w\",\"*\",\"w\"]]],[11,[1,\"perp2\",\"<\",[1,\"rB\",\"*\",\"rB\"]],[0,[[13,\"half\",[6,\"std.sqrt\",[[1,[1,\"rB\",\"*\",\"rB\"],\"-\",\"perp2\"]]]],[13,\"maxSteps\",[6,\"d.i32\",[[6,\"std.clamp\",[[1,[1,[1,\"rB\",\"*\",[5,\"6\"]],\"/\",[7,\"vx\",\"voxelSize\"]],\"+\",[5,\"8\"]],[6,\"d.f32\",[[5,\"8\"]]],[6,\"d.f32\",[\"STEP_CAP\"]]]]]]],[13,\"none\",[6,\"d.vec3f\",[[4,\"-\",[5,\"9999\"]],[4,\"-\",[5,\"9999\"]],[4,\"-\",[5,\"9999\"]]]]],[13,\"hh\",[6,\"voxelDda\",[\"p0\",\"rd\",[1,\"rB\",\"-\",\"half\"],[1,[1,\"rB\",\"+\",\"half\"],\"+\",[5,\"0.01\"]],\"none\",\"maxSteps\"]]],[11,[1,[7,\"hh\",\"hit\"],\">\",[5,\"0.5\"]],[0,[[2,\"depth\",\"=\",[7,\"hh\",\"t\"]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"voxLayout\",\"$\"],\"shadowMap\"],[6,\"d.vec2u\",[\"i\",\"j\"]],[6,\"d.vec4f\",[\"depth\",[5,\"0\"],[5,\"0\"],[5,\"0\"]]]]]]]},externals:{voxLayout:()=>e,\"d.f32\":()=>n,RES:()=>r,SHADOW_FAR:()=>xP,\"std.sqrt\":()=>Ue,\"d.i32\":()=>j,\"std.clamp\":()=>G,STEP_CAP:()=>o,\"d.vec3f\":()=>A,voxelDda:()=>s,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&a.f)({})).$name(\"voxelShadowMap\")}var Y0=w.fn([Zt(n),n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=n(__tsover_sub(sl,1)),i=ie(s),l=ie(r),c=__tsover_sub(s,i),d=__tsover_sub(r,l),u=M(G(i,n(0),a)),p=M(G(__tsover_add(i,1),n(0),a)),_=M(G(l,n(0),a)),f=M(G(__tsover_add(l,1),n(0),a)),h=N(n(0),n(1),Ye(t,fe(u,_),0).r<o),g=N(n(0),n(1),Ye(t,fe(p,_),0).r<o),y=N(n(0),n(1),Ye(t,fe(u,f),0).r<o),b=N(n(0),n(1),Ye(t,fe(p,f),0).r<o);return K(K(h,g,c),K(y,b,c),d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"tex\"},{type:\"i\",name:\"mx\"},{type:\"i\",name:\"my\"},{type:\"i\",name:\"ref\"}],body:[0,[[13,\"maxT\",[6,\"d.f32\",[[1,\"VOXEL_SHADOW_RES\",\"-\",[5,\"1\"]]]]],[13,\"x0\",[6,\"std.floor\",[\"mx\"]]],[13,\"y0\",[6,\"std.floor\",[\"my\"]]],[13,\"fx\",[1,\"mx\",\"-\",\"x0\"]],[13,\"fy\",[1,\"my\",\"-\",\"y0\"]],[13,\"cx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"x0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"x0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"y0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"cy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"y0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"s00\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,[6,\"std.textureLoad\",[\"tex\",[6,\"d.vec2u\",[\"cx0\",\"cy0\"]],[5,\"0\"]]],\"r\"],\"<\",\"ref\"]]]],[13,\"s10\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,[6,\"std.textureLoad\",[\"tex\",[6,\"d.vec2u\",[\"cx1\",\"cy0\"]],[5,\"0\"]]],\"r\"],\"<\",\"ref\"]]]],[13,\"s01\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,[6,\"std.textureLoad\",[\"tex\",[6,\"d.vec2u\",[\"cx0\",\"cy1\"]],[5,\"0\"]]],\"r\"],\"<\",\"ref\"]]]],[13,\"s11\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,[6,\"std.textureLoad\",[\"tex\",[6,\"d.vec2u\",[\"cx1\",\"cy1\"]],[5,\"0\"]]],\"r\"],\"<\",\"ref\"]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[\"s00\",\"s10\",\"fx\"]],[6,\"std.mix\",[\"s01\",\"s11\",\"fx\"]],\"fy\"]]]]]},externals:{\"d.f32\":()=>n,VOXEL_SHADOW_RES:()=>sl,\"std.floor\":()=>ie,\"d.u32\":()=>M,\"std.clamp\":()=>G,\"std.select\":()=>N,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K}})&&e.f)({})).$name(\"voxelShadowCompare\"),bne=w.fn([Zt(n),A,A,A,A,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=n(sl),u=__tsover_div(__tsover_mul(i,2),d),p=Se(s,o),_=Se(s,a),f=__tsover_sub(i,Se(s,r)),h=__tsover_sub(__tsover_mul(__tsover_add(__tsover_mul(__tsover_div(p,i),.5),.5),d),.5),g=__tsover_sub(__tsover_mul(__tsover_add(__tsover_mul(__tsover_div(_,i),.5),.5),d),.5),y=__tsover_sub(f,__tsover_add(__tsover_mul(c,.05),__tsover_mul(u,2))),b=n(0);if(l<.001)b=Y0(t,h,g,y);else{let x=__tsover_mul(l,bP),P=B(__tsover_div(ye(__tsover_mul(i,x),__tsover_mul(i,.25)),u),1),C=n(0),E=n(0);for(let R=0;R<8;R++){let U=__tsover_mul(Ue(__tsover_div(__tsover_add(n(R),.5),8)),P),L=__tsover_mul(n(R),I0),k=G(__tsover_add(h,__tsover_mul(pe(L),U)),n(0),__tsover_sub(d,1)),D=G(__tsover_add(g,__tsover_mul(ae(L),U)),n(0),__tsover_sub(d,1)),O=Ye(t,fe(M(k),M(D)),0).r;O<y&&(C=__tsover_add(C,O),E=__tsover_add(E,1))}if(E>.5){let R=__tsover_div(C,E),U=G(__tsover_div(__tsover_mul(__tsover_sub(f,R),x),u),1,P),L=n(0);for(let k=0;k<12;k++){let D=__tsover_mul(Ue(__tsover_div(__tsover_add(n(k),.5),12)),U),O=__tsover_add(__tsover_mul(n(k),I0),.7);L=__tsover_add(L,Y0(t,__tsover_add(h,__tsover_mul(pe(O),D)),__tsover_add(g,__tsover_mul(ae(O),D)),y))}b=__tsover_div(L,12)}}return b}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"tex\"},{type:\"i\",name:\"p\"},{type:\"i\",name:\"L\"},{type:\"i\",name:\"T1\"},{type:\"i\",name:\"T2\"},{type:\"i\",name:\"rBound\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"voxel\"}],body:[0,[[13,\"RES\",[6,\"d.f32\",[\"VOXEL_SHADOW_RES\"]]],[13,\"texelWorld\",[1,[1,\"rBound\",\"*\",[5,\"2\"]],\"/\",\"RES\"]],[13,\"u\",[6,\"std.dot\",[\"p\",\"T1\"]]],[13,\"w\",[6,\"std.dot\",[\"p\",\"T2\"]]],[13,\"dL\",[1,\"rBound\",\"-\",[6,\"std.dot\",[\"p\",\"L\"]]]],[13,\"mx\",[1,[1,[1,[1,[1,\"u\",\"/\",\"rBound\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],\"*\",\"RES\"],\"-\",[5,\"0.5\"]]],[13,\"my\",[1,[1,[1,[1,[1,\"w\",\"/\",\"rBound\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],\"*\",\"RES\"],\"-\",[5,\"0.5\"]]],[13,\"ref\",[1,\"dL\",\"-\",[1,[1,\"voxel\",\"*\",[5,\"0.05\"]],\"+\",[1,\"texelWorld\",\"*\",[5,\"2\"]]]]],[12,\"shadow\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"softness\",\"<\",[5,\"0.001\"]],[0,[[2,\"shadow\",\"=\",[6,\"shadowCompare\",[\"tex\",\"mx\",\"my\",\"ref\"]]]]],[0,[[13,\"k\",[1,\"softness\",\"*\",\"SOFT_TAN\"]],[13,\"rSearch\",[6,\"std.max\",[[1,[6,\"std.min\",[[1,\"rBound\",\"*\",\"k\"],[1,\"rBound\",\"*\",[5,\"0.25\"]]]],\"/\",\"texelWorld\"],[5,\"1\"]]]],[12,\"sum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"count\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"s\",[5,\"0\"]],[1,\"s\",\"<\",[5,\"8\"]],[102,\"++\",\"s\"],[0,[[13,\"r\",[1,[6,\"std.sqrt\",[[1,[1,[6,\"d.f32\",[\"s\"]],\"+\",[5,\"0.5\"]],\"/\",[5,\"8\"]]]],\"*\",\"rSearch\"]],[13,\"th\",[1,[6,\"d.f32\",[\"s\"]],\"*\",\"GOLDEN_ANGLE\"]],[13,\"sx\",[6,\"std.clamp\",[[1,\"mx\",\"+\",[1,[6,\"std.cos\",[\"th\"]],\"*\",\"r\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"RES\",\"-\",[5,\"1\"]]]]],[13,\"sy\",[6,\"std.clamp\",[[1,\"my\",\"+\",[1,[6,\"std.sin\",[\"th\"]],\"*\",\"r\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"RES\",\"-\",[5,\"1\"]]]]],[13,\"dep\",[7,[6,\"std.textureLoad\",[\"tex\",[6,\"d.vec2u\",[[6,\"d.u32\",[\"sx\"]],[6,\"d.u32\",[\"sy\"]]]],[5,\"0\"]]],\"r\"]],[11,[1,\"dep\",\"<\",\"ref\"],[0,[[2,\"sum\",\"=\",[1,\"sum\",\"+\",\"dep\"]],[2,\"count\",\"=\",[1,\"count\",\"+\",[5,\"1\"]]]]]]]]],[11,[1,\"count\",\">\",[5,\"0.5\"]],[0,[[13,\"avgBlocker\",[1,\"sum\",\"/\",\"count\"]],[13,\"penumbra\",[6,\"std.clamp\",[[1,[1,[1,\"dL\",\"-\",\"avgBlocker\"],\"*\",\"k\"],\"/\",\"texelWorld\"],[5,\"1\"],\"rSearch\"]]],[12,\"acc\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"s\",[5,\"0\"]],[1,\"s\",\"<\",[5,\"12\"]],[102,\"++\",\"s\"],[0,[[13,\"r\",[1,[6,\"std.sqrt\",[[1,[1,[6,\"d.f32\",[\"s\"]],\"+\",[5,\"0.5\"]],\"/\",[5,\"12\"]]]],\"*\",\"penumbra\"]],[13,\"th\",[1,[1,[6,\"d.f32\",[\"s\"]],\"*\",\"GOLDEN_ANGLE\"],\"+\",[5,\"0.7\"]]],[2,\"acc\",\"=\",[1,\"acc\",\"+\",[6,\"shadowCompare\",[\"tex\",[1,\"mx\",\"+\",[1,[6,\"std.cos\",[\"th\"]],\"*\",\"r\"]],[1,\"my\",\"+\",[1,[6,\"std.sin\",[\"th\"]],\"*\",\"r\"]],\"ref\"]]]]]]],[2,\"shadow\",\"=\",[1,\"acc\",\"/\",[5,\"12\"]]]]]]]]],[10,\"shadow\"]]]},externals:{\"d.f32\":()=>n,VOXEL_SHADOW_RES:()=>sl,\"std.dot\":()=>Se,shadowCompare:()=>Y0,SOFT_TAN:()=>bP,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.sqrt\":()=>Ue,GOLDEN_ANGLE:()=>I0,\"std.clamp\":()=>G,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M}})&&e.f)({})).$name(\"voxelShadowLookup\"),bx=w.fn([A,A,A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=N(n(-1),n(1),s.x>=0),i=N(n(-1),n(1),s.y>=0),l=N(n(-1),n(1),s.z>=0),c=__tsover_div(a,B(re(s.x),1e-6)),d=__tsover_div(i,B(re(s.y),1e-6)),u=__tsover_div(l,B(re(s.z),1e-6)),p=__tsover_mul(__tsover_sub(__tsover_sub(r.x,o),t.x),c),_=__tsover_mul(__tsover_sub(__tsover_add(r.x,o),t.x),c),f=__tsover_mul(__tsover_sub(__tsover_sub(r.y,o),t.y),d),h=__tsover_mul(__tsover_sub(__tsover_add(r.y,o),t.y),d),g=__tsover_mul(__tsover_sub(__tsover_sub(r.z,o),t.z),u),y=__tsover_mul(__tsover_sub(__tsover_add(r.z,o),t.z),u),b=B(ye(p,_),B(ye(f,h),ye(g,y))),x=ye(B(p,_),ye(B(f,h),B(g,y))),P=__tsover_mul(N(n(0),n(1),b<=x),N(n(0),n(1),x>=0));return N(n(-1),B(b,n(0)),P>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ro\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"c\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"sx\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"x\"],\">=\",[5,\"0\"]]]]],[13,\"sy\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"y\"],\">=\",[5,\"0\"]]]]],[13,\"sz\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rd\",\"z\"],\">=\",[5,\"0\"]]]]],[13,\"ix\",[1,\"sx\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"x\"]]],[5,\"0.000001\"]]]]],[13,\"iy\",[1,\"sy\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"y\"]]],[5,\"0.000001\"]]]]],[13,\"iz\",[1,\"sz\",\"/\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rd\",\"z\"]]],[5,\"0.000001\"]]]]],[13,\"t1x\",[1,[1,[1,[7,\"c\",\"x\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"x\"]],\"*\",\"ix\"]],[13,\"t2x\",[1,[1,[1,[7,\"c\",\"x\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"x\"]],\"*\",\"ix\"]],[13,\"t1y\",[1,[1,[1,[7,\"c\",\"y\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"y\"]],\"*\",\"iy\"]],[13,\"t2y\",[1,[1,[1,[7,\"c\",\"y\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"y\"]],\"*\",\"iy\"]],[13,\"t1z\",[1,[1,[1,[7,\"c\",\"z\"],\"-\",\"h\"],\"-\",[7,\"ro\",\"z\"]],\"*\",\"iz\"]],[13,\"t2z\",[1,[1,[1,[7,\"c\",\"z\"],\"+\",\"h\"],\"-\",[7,\"ro\",\"z\"]],\"*\",\"iz\"]],[13,\"tIn\",[6,\"std.max\",[[6,\"std.min\",[\"t1x\",\"t2x\"]],[6,\"std.max\",[[6,\"std.min\",[\"t1y\",\"t2y\"]],[6,\"std.min\",[\"t1z\",\"t2z\"]]]]]]],[13,\"tOut\",[6,\"std.min\",[[6,\"std.max\",[\"t1x\",\"t2x\"]],[6,\"std.min\",[[6,\"std.max\",[\"t1y\",\"t2y\"]],[6,\"std.max\",[\"t1z\",\"t2z\"]]]]]]],[13,\"ok\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"tIn\",\"<=\",\"tOut\"]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"tOut\",\">=\",[5,\"0\"]]]]]],[10,[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"std.max\",[\"tIn\",[6,\"d.f32\",[[5,\"0\"]]]]],[1,\"ok\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.select\":()=>N,\"d.f32\":()=>n,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.min\":()=>ye}})&&e.f)({})).$name(\"voxelRayBox\"),xne=w.fn([A,A,A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=t.sub(r),i=Se(a,s),l=__tsover_sub(Se(a,a),__tsover_mul(o,o)),c=__tsover_sub(__tsover_mul(i,i),l),d=__tsover_sub(__tsover_mul(i,-1),Ue(B(c,0))),u=__tsover_mul(N(n(0),n(1),c>=0),N(n(0),n(1),d>=0));return N(n(-1),d,u>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ro\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"c\"},{type:\"i\",name:\"h\"}],body:[0,[[13,\"oc\",[6,[7,\"ro\",\"sub\"],[\"c\"]]],[13,\"b\",[6,\"std.dot\",[\"oc\",\"rd\"]]],[13,\"cc\",[1,[6,\"std.dot\",[\"oc\",\"oc\"]],\"-\",[1,\"h\",\"*\",\"h\"]]],[13,\"disc\",[1,[1,\"b\",\"*\",\"b\"],\"-\",\"cc\"]],[13,\"t\",[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"-\",[6,\"std.sqrt\",[[6,\"std.max\",[\"disc\",[5,\"0\"]]]]]]],[13,\"ok\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"disc\",\">=\",[5,\"0\"]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"t\",\">=\",[5,\"0\"]]]]]],[10,[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],\"t\",[1,\"ok\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.dot\":()=>Se,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})).$name(\"voxelRaySphere\"),Ane=w.fn([A,A,A,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=bx(t,s,r,o),l=n(-1);if(i>=0){let c=i,d=n(0);for(let u=0;u<8;u++){let p=xd(t.add(s.mul(c)).sub(r),o,o,o,a);if(p<__tsover_mul(o,.004)){d=n(1);break}if(c=__tsover_add(c,p),c>__tsover_add(i,__tsover_mul(o,3.5)))break}l=N(n(-1),c,d>.5)}return l}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ro\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"c\"},{type:\"i\",name:\"h\"},{type:\"i\",name:\"r\"}],body:[0,[[13,\"tBox\",[6,\"voxelRayBox\",[\"ro\",\"rd\",\"c\",\"h\"]]],[12,\"t\",[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]]],[11,[1,\"tBox\",\">=\",[5,\"0\"]],[0,[[12,\"s\",\"tBox\"],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"8\"]],[102,\"++\",\"i\"],[0,[[13,\"p\",[6,[7,[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"s\"]]]],\"sub\"],[\"c\"]]],[13,\"dd\",[6,\"sdRoundBox\",[\"p\",\"h\",\"h\",\"h\",\"r\"]]],[11,[1,\"dd\",\"<\",[1,\"h\",\"*\",[5,\"0.004\"]]],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[17]]]],[2,\"s\",\"=\",[1,\"s\",\"+\",\"dd\"]],[11,[1,\"s\",\">\",[1,\"tBox\",\"+\",[1,\"h\",\"*\",[5,\"3.5\"]]]],[0,[[17]]]]]]],[2,\"t\",\"=\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],\"s\",[1,\"hit\",\">\",[5,\"0.5\"]]]]]]]],[10,\"t\"]]]},externals:{voxelRayBox:()=>bx,\"d.f32\":()=>n,sdRoundBox:()=>xd,\"std.select\":()=>N}})&&e.f)({})).$name(\"voxelRayRoundedBox\");function wne(e,t,s){let{style:r,gridSpace:o}=s,a=ez(),{cellCentre:i,occupied:l,voxelDda:c}=JB(t,r),d=w.fn([A,Ad],A)((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";return o===\"shape\"?ja(ja(_,t.$.vox.cam),f):A(_.x,_.y,_.z)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"rot\"}],body:[0,[[11,[1,\"gridSpace\",\"===\",[103,\"shape\"]],[10,[6,\"rotateVec3\",[[6,\"rotateVec3\",[\"v\",[7,[7,[7,\"voxLayout\",\"$\"],\"vox\"],\"cam\"]]],\"rot\"]]],[10,[6,\"d.vec3f\",[[7,\"v\",\"x\"],[7,\"v\",\"y\"],[7,\"v\",\"z\"]]]]]]]},externals:{gridSpace:()=>o,rotateVec3:()=>ja,voxLayout:()=>t,\"d.vec3f\":()=>A}})&&p.f)({})),u=w.fn([n,n,n],n)((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f,h)=>{\"use gpu\";return N(__tsover_div(__tsover_sub(3,__tsover_add(__tsover_add(_,f),h)),3),n(0),__tsover_add(_,f)>1.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s1\"},{type:\"i\",name:\"s2\"},{type:\"i\",name:\"k\"}],body:[0,[[13,\"open\",[1,[1,[5,\"3\"],\"-\",[1,[1,\"s1\",\"+\",\"s2\"],\"+\",\"k\"]],\"/\",[5,\"3\"]]],[10,[6,\"std.select\",[\"open\",[6,\"d.f32\",[[5,\"0\"]]],[1,[1,\"s1\",\"+\",\"s2\"],\">\",[5,\"1.5\"]]]]]]]},externals:{\"std.select\":()=>N,\"d.f32\":()=>n}})&&p.f)({}));return w.fn([m,Ad,n],S)((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f,h)=>{\"use gpu\";let g=t.$.vox,y=g.voxelSize,b=d(A(__tsover_sub(_.x,.5),__tsover_sub(.5,_.y),-1.2),f),x=d(A(0,0,1),f),P=Se(b,x),C=B(__tsover_sub(Se(b,b),__tsover_mul(P,P)),0),E=Ue(B(__tsover_sub(__tsover_mul(h,h),C),0)),R=B(__tsover_sub(__tsover_mul(P,-1),E),0),U=__tsover_add(__tsover_add(__tsover_mul(P,-1),E),.01),L=e.$.params,k=__tsover_div(B(L.spanX,L.spanY),n(L.activeRes)),D=__tsover_add(__tsover_sub(Ue(C),h),k),O=n(0),F=n(0),q=__tsover_add(U,.4);if(C<__tsover_mul(h,h)){let Y=j(G(__tsover_add(__tsover_div(__tsover_mul(h,6),y),8),n(8),n(a))),V=c(b,x,R,U,A(-9999,-9999,-9999),Y);if(V.hit>.5){let X=b.add(x.mul(V.t)),Q=i(V.cx,V.cy,V.cz),te=X.sub(Q),J=re(te.x),oe=re(te.y),le=re(te.z),se=n(0);oe>J?oe>=le?se=n(1):se=n(2):le>J&&(se=n(2));let Te=N(N(te.x,te.y,se>.5),te.z,se>1.5),de=N(n(-1),n(1),Te>=0),xe=N(n(0),n(1),se<.5),Ae=N(n(0),n(1),se>1.5),De=__tsover_sub(__tsover_sub(1,xe),Ae),Ee=__tsover_mul(de,xe),ze=__tsover_mul(de,De),Ie=__tsover_mul(de,Ae),ke=__tsover_sub(1,xe),Me=xe,we=Ae,Ve=__tsover_sub(1,Ae),qe=__tsover_add(__tsover_mul(te.x,ke),__tsover_mul(te.y,Me)),st=__tsover_add(__tsover_mul(te.y,we),__tsover_mul(te.z,Ve)),pt=__tsover_mul(__tsover_mul(y,.5),g.voxelScale),Ze=G(__tsover_add(__tsover_div(qe,__tsover_mul(2,pt)),.5),0,1),nt=G(__tsover_add(__tsover_div(st,__tsover_mul(2,pt)),.5),0,1),ht=__tsover_add(V.cx,Ee),yt=__tsover_add(V.cy,ze),St=__tsover_add(V.cz,Ie),Mt=l(__tsover_sub(ht,ke),__tsover_sub(yt,Me),St),Rt=l(__tsover_add(ht,ke),__tsover_add(yt,Me),St),Kt=l(ht,__tsover_sub(yt,we),__tsover_sub(St,Ve)),Wt=l(ht,__tsover_add(yt,we),__tsover_add(St,Ve)),ls=l(__tsover_sub(ht,ke),__tsover_sub(__tsover_sub(yt,Me),we),__tsover_sub(St,Ve)),us=l(__tsover_add(ht,ke),__tsover_sub(__tsover_add(yt,Me),we),__tsover_sub(St,Ve)),os=l(__tsover_sub(ht,ke),__tsover_add(__tsover_sub(yt,Me),we),__tsover_add(St,Ve)),bs=l(__tsover_add(ht,ke),__tsover_add(__tsover_add(yt,Me),we),__tsover_add(St,Ve)),hs=K(K(u(Mt,Kt,ls),u(Rt,Kt,us),Ze),K(u(Mt,Wt,os),u(Rt,Wt,bs),Ze),nt),gs=__tsover_add(__tsover_mul(se,2),B(de,n(0)));D=__tsover_mul(k,-1),O=__tsover_add(__tsover_mul(gs,65536),__tsover_mul(ie(__tsover_add(__tsover_mul(hs,255),.5)),256)),F=__tsover_add(__tsover_add(V.cx,__tsover_mul(V.cy,256)),__tsover_mul(V.cz,65536)),q=V.t}else D=__tsover_add(k,B(__tsover_sub(V.minD,y),0))}return S(D,O,F,q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fuv\"},{type:\"i\",name:\"rot\"},{type:\"i\",name:\"rBound\"}],body:[0,[[13,\"vx\",[7,[7,\"voxLayout\",\"$\"],\"vox\"]],[13,\"v\",[7,\"vx\",\"voxelSize\"]],[13,\"ro\",[6,\"viewToGrid\",[[6,\"d.vec3f\",[[1,[7,\"fuv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[5,\"0.5\"],\"-\",[7,\"fuv\",\"y\"]],[4,\"-\",[5,\"1.2\"]]]],\"rot\"]]],[13,\"rd\",[6,\"viewToGrid\",[[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"1\"]]],\"rot\"]]],[13,\"b\",[6,\"std.dot\",[\"ro\",\"rd\"]]],[13,\"perp2\",[6,\"std.max\",[[1,[6,\"std.dot\",[\"ro\",\"ro\"]],\"-\",[1,\"b\",\"*\",\"b\"]],[5,\"0\"]]]],[13,\"halfSpan\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,[1,\"rBound\",\"*\",\"rBound\"],\"-\",\"perp2\"],[5,\"0\"]]]]]],[13,\"tStart\",[6,\"std.max\",[[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"-\",\"halfSpan\"],[5,\"0\"]]]],[13,\"tEnd\",[1,[1,[1,\"b\",\"*\",[4,\"-\",[5,\"1\"]]],\"+\",\"halfSpan\"],\"+\",[5,\"0.01\"]]],[13,\"mp\",[7,[7,\"marchLayout\",\"$\"],\"params\"]],[13,\"texel\",[1,[6,\"std.max\",[[7,\"mp\",\"spanX\"],[7,\"mp\",\"spanY\"]]],\"/\",[6,\"d.f32\",[[7,\"mp\",\"activeRes\"]]]]],[12,\"fieldVal\",[1,[1,[6,\"std.sqrt\",[\"perp2\"]],\"-\",\"rBound\"],\"+\",\"texel\"]],[12,\"payload\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"cellPacked\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"depth\",[1,\"tEnd\",\"+\",[5,\"0.4\"]]],[11,[1,\"perp2\",\"<\",[1,\"rBound\",\"*\",\"rBound\"]],[0,[[13,\"maxSteps\",[6,\"d.i32\",[[6,\"std.clamp\",[[1,[1,[1,\"rBound\",\"*\",[5,\"6\"]],\"/\",\"v\"],\"+\",[5,\"8\"]],[6,\"d.f32\",[[5,\"8\"]]],[6,\"d.f32\",[\"STEP_CAP\"]]]]]]],[13,\"none\",[6,\"d.vec3f\",[[4,\"-\",[5,\"9999\"]],[4,\"-\",[5,\"9999\"]],[4,\"-\",[5,\"9999\"]]]]],[13,\"hh\",[6,\"voxelDda\",[\"ro\",\"rd\",\"tStart\",\"tEnd\",\"none\",\"maxSteps\"]]],[11,[1,[7,\"hh\",\"hit\"],\">\",[5,\"0.5\"]],[0,[[13,\"hp\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[[7,\"hh\",\"t\"]]]]]],[13,\"c\",[6,\"cellCentre\",[[7,\"hh\",\"cx\"],[7,\"hh\",\"cy\"],[7,\"hh\",\"cz\"]]]],[13,\"l\",[6,[7,\"hp\",\"sub\"],[\"c\"]]],[13,\"alx\",[6,\"std.abs\",[[7,\"l\",\"x\"]]]],[13,\"aly\",[6,\"std.abs\",[[7,\"l\",\"y\"]]]],[13,\"alz\",[6,\"std.abs\",[[7,\"l\",\"z\"]]]],[12,\"axis\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"aly\",\">\",\"alx\"],[0,[[11,[1,\"aly\",\">=\",\"alz\"],[0,[[2,\"axis\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]]]],[0,[[2,\"axis\",\"=\",[6,\"d.f32\",[[5,\"2\"]]]]]]]]],[0,[[11,[1,\"alz\",\">\",\"alx\"],[0,[[2,\"axis\",\"=\",[6,\"d.f32\",[[5,\"2\"]]]]]]]]]],[13,\"la\",[6,\"std.select\",[[6,\"std.select\",[[7,\"l\",\"x\"],[7,\"l\",\"y\"],[1,\"axis\",\">\",[5,\"0.5\"]]]],[7,\"l\",\"z\"],[1,\"axis\",\">\",[5,\"1.5\"]]]]],[13,\"sgn\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"la\",\">=\",[5,\"0\"]]]]],[13,\"isX\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"axis\",\"<\",[5,\"0.5\"]]]]],[13,\"isZ\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"axis\",\">\",[5,\"1.5\"]]]]],[13,\"isY\",[1,[1,[5,\"1\"],\"-\",\"isX\"],\"-\",\"isZ\"]],[13,\"nx\",[1,\"sgn\",\"*\",\"isX\"]],[13,\"ny\",[1,\"sgn\",\"*\",\"isY\"]],[13,\"nz\",[1,\"sgn\",\"*\",\"isZ\"]],[13,\"bxv\",[1,[5,\"1\"],\"-\",\"isX\"]],[13,\"byv\",\"isX\"],[13,\"eyv\",\"isZ\"],[13,\"ezv\",[1,[5,\"1\"],\"-\",\"isZ\"]],[13,\"lb\",[1,[1,[7,\"l\",\"x\"],\"*\",\"bxv\"],\"+\",[1,[7,\"l\",\"y\"],\"*\",\"byv\"]]],[13,\"lc\",[1,[1,[7,\"l\",\"y\"],\"*\",\"eyv\"],\"+\",[1,[7,\"l\",\"z\"],\"*\",\"ezv\"]]],[13,\"h\",[1,[1,\"v\",\"*\",[5,\"0.5\"]],\"*\",[7,\"vx\",\"voxelScale\"]]],[13,\"u\",[6,\"std.clamp\",[[1,[1,\"lb\",\"/\",[1,[5,\"2\"],\"*\",\"h\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"w\",[6,\"std.clamp\",[[1,[1,\"lc\",\"/\",[1,[5,\"2\"],\"*\",\"h\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"fx\",[1,[7,\"hh\",\"cx\"],\"+\",\"nx\"]],[13,\"fy\",[1,[7,\"hh\",\"cy\"],\"+\",\"ny\"]],[13,\"fz\",[1,[7,\"hh\",\"cz\"],\"+\",\"nz\"]],[13,\"sB1\",[6,\"occupied\",[[1,\"fx\",\"-\",\"bxv\"],[1,\"fy\",\"-\",\"byv\"],\"fz\"]]],[13,\"sB2\",[6,\"occupied\",[[1,\"fx\",\"+\",\"bxv\"],[1,\"fy\",\"+\",\"byv\"],\"fz\"]]],[13,\"sC1\",[6,\"occupied\",[\"fx\",[1,\"fy\",\"-\",\"eyv\"],[1,\"fz\",\"-\",\"ezv\"]]]],[13,\"sC2\",[6,\"occupied\",[\"fx\",[1,\"fy\",\"+\",\"eyv\"],[1,\"fz\",\"+\",\"ezv\"]]]],[13,\"k11\",[6,\"occupied\",[[1,\"fx\",\"-\",\"bxv\"],[1,[1,\"fy\",\"-\",\"byv\"],\"-\",\"eyv\"],[1,\"fz\",\"-\",\"ezv\"]]]],[13,\"k21\",[6,\"occupied\",[[1,\"fx\",\"+\",\"bxv\"],[1,[1,\"fy\",\"+\",\"byv\"],\"-\",\"eyv\"],[1,\"fz\",\"-\",\"ezv\"]]]],[13,\"k12\",[6,\"occupied\",[[1,\"fx\",\"-\",\"bxv\"],[1,[1,\"fy\",\"-\",\"byv\"],\"+\",\"eyv\"],[1,\"fz\",\"+\",\"ezv\"]]]],[13,\"k22\",[6,\"occupied\",[[1,\"fx\",\"+\",\"bxv\"],[1,[1,\"fy\",\"+\",\"byv\"],\"+\",\"eyv\"],[1,\"fz\",\"+\",\"ezv\"]]]],[13,\"ao0\",[6,\"std.mix\",[[6,\"vertexAo\",[\"sB1\",\"sC1\",\"k11\"]],[6,\"vertexAo\",[\"sB2\",\"sC1\",\"k21\"]],\"u\"]]],[13,\"ao1\",[6,\"std.mix\",[[6,\"vertexAo\",[\"sB1\",\"sC2\",\"k12\"]],[6,\"vertexAo\",[\"sB2\",\"sC2\",\"k22\"]],\"u\"]]],[13,\"ao\",[6,\"std.mix\",[\"ao0\",\"ao1\",\"w\"]]],[13,\"faceId\",[1,[1,\"axis\",\"*\",[5,\"2\"]],\"+\",[6,\"std.max\",[\"sgn\",[6,\"d.f32\",[[5,\"0\"]]]]]]],[2,\"fieldVal\",\"=\",[1,\"texel\",\"*\",[4,\"-\",[5,\"1\"]]]],[2,\"payload\",\"=\",[1,[1,\"faceId\",\"*\",[5,\"65536\"]],\"+\",[1,[6,\"std.floor\",[[1,[1,\"ao\",\"*\",[5,\"255\"]],\"+\",[5,\"0.5\"]]]],\"*\",[5,\"256\"]]]],[2,\"cellPacked\",\"=\",[1,[1,[7,\"hh\",\"cx\"],\"+\",[1,[7,\"hh\",\"cy\"],\"*\",[5,\"256\"]]],\"+\",[1,[7,\"hh\",\"cz\"],\"*\",[5,\"65536\"]]]],[2,\"depth\",\"=\",[7,\"hh\",\"t\"]]]],[0,[[2,\"fieldVal\",\"=\",[1,\"texel\",\"+\",[6,\"std.max\",[[1,[7,\"hh\",\"minD\"],\"-\",\"v\"],[5,\"0\"]]]]]]]]]]],[10,[6,\"d.vec4f\",[\"fieldVal\",\"payload\",\"cellPacked\",\"depth\"]]]]]},externals:{voxLayout:()=>t,viewToGrid:()=>d,\"d.vec3f\":()=>A,\"std.dot\":()=>Se,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,marchLayout:()=>e,\"d.f32\":()=>n,\"d.i32\":()=>j,\"std.clamp\":()=>G,STEP_CAP:()=>a,voxelDda:()=>c,cellCentre:()=>i,\"std.abs\":()=>re,\"std.select\":()=>N,occupied:()=>l,\"std.mix\":()=>K,vertexAo:()=>u,\"std.floor\":()=>ie,\"d.vec4f\":()=>S}})&&p.f)({})).$name(\"voxelFieldMarch\")}var Tne={_vxMx:{schema:A,initial:[1,0,0]},_vxMy:{schema:A,initial:[0,1,0]},_vxMz:{schema:A,initial:[0,0,1]},_vxVoxel:{schema:n,initial:.03},_vxGridOrigin:{schema:n,initial:-.6},_vxL:{schema:A,initial:[0,0,-1]},_vxT1:{schema:A,initial:[1,0,0]},_vxT2:{schema:A,initial:[0,1,0]}};function W0(e,t){let s=2*(t+.002)/Lm;return Math.max(Number.isFinite(e)?e:.03,s,.002)}var AP=(e,t)=>({x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}),Sne=e=>{let t=Math.hypot(e.x,e.y,e.z)||1;return{x:e.x/t,y:e.y/t,z:e.z/t}},_1=(e,t,s)=>Math.min(s,Math.max(t,e));function Pne(e,t){let s=e.gpu?.root;if(!s)return null;let r=jv(t.getShapeConfig()),o=e.getCpuValue(\"shapeSdfUrl\")||\"\",a=e.getCpuValue(\"shapeType\")||r.type||\"\",i=t.getValues(),l=.6,c=W0(i.voxelSize,l),d=()=>c*fne+.01,u;o?u=wA(e,o,Qh(a)?t.getShapeConfig:()=>({...jv(t.getShapeConfig()),type:\"svgExtrude3D\",depth:i.depth,bevel:0,rotX:0,rotY:0,rotZ:0}),{extraPad:d}):M_(a)?u=AA(s,a,r,t.getShapeConfig,{extraPad:d}):u=QB(s,bc(a,r),t.getShapeConfig,()=>i.depth*.5,{extraPad:d});let p=vne(),_=hne(),f=(globalThis.__TYPEGPU_AUTONAME__??(J=>J))(s.createUniform(GA),\"voxUniform\"),h=pr(s,M,OA),g=(globalThis.__TYPEGPU_AUTONAME__??(J=>J))(s.createTexture({size:[sl,sl],format:ZB}).$usage(\"storage\",\"sampled\"),\"shadowTexture\");e.onCleanup(()=>{h.destroy(),g.destroy()});let y=s.createBindGroup(p,{vox:f.buffer,occ:h,shadowMap:g}),b=s.createBindGroup(_,{vox:f.buffer,occ:h}),x=wne(u.layout,p,{style:t.style,gridSpace:t.gridSpace}),P=gA(u.layout,x),C=yA(s,u.layout,P,e.onCleanup,{paramsUniform:u.marchParamsBuffer,sdfSourceTexture:u.sdfSourceTexture,extraBindGroups:[y]}),E=e.registerComputeTexture(C.fieldTexture),R=e.registerComputeTexture(g),U=gne(u.sdfFn,u.layout,_,t.gridSpace),L=s.createBindGroup(u.layout,{field:C.fieldTexture,params:u.marchParamsBuffer.buffer,...u.sdfSourceTexture?{sdfSource:u.sdfSourceTexture}:{}}),k=wr(s,(J=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(J.f=((oe,le,se)=>{\"use gpu\";U(oe,le,se)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"wx\"},{type:\"i\",name:\"iy\"},{type:\"i\",name:\"iz\"}],body:[0,[[6,\"bakeFn\",[\"wx\",\"iy\",\"iz\"]]]]},externals:{bakeFn:()=>U}})&&J.f)({}),{bindGroup:L}).with(b),D=yne(p,t.style),O=wr(s,(J=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(J.f=((oe,le)=>{\"use gpu\";D(oe,le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"},{type:\"i\",name:\"j\"}],body:[0,[[6,\"shadowFn\",[\"i\",\"j\"]]]]},externals:{shadowFn:()=>D}})&&J.f)({}),{bindGroup:y,size:[sl,sl]}),F=C.maxRes,q=\"\",Y=\"\",V=\"\",X=Rr()?2:1,Q=0;return{outputs:{volumetricFieldTexture:E,voxelShadowMap:R},getComputeNodes:J=>{i=t.getValues(),c=W0(i.voxelSize,l),u.update(J);let oe=u.getFootprint();l=oe.rBound,c=W0(c,oe.rBound);let le=e.getCpuValue(\"scale\")??1;F=vA(oe.spanX,oe.spanY,le,e.dimensions.height,C.maxRes,F);let se=i.pitch*m1,Te=i.yaw*m1,de={cx:Math.cos(se),sx:Math.sin(se),cy:Math.cos(Te),sy:Math.sin(Te),cz:1,sz:0},xe=u.getRotation(),Ae=(Gs,Ss,or)=>{let yr=Jl(Gs,Ss,or,de);return Jl(yr.x,yr.y,yr.z,xe)},De=Ae(1,0,0),Ee=Ae(0,1,0),ze=Ae(0,0,1),Ie=i.lightAngle*m1,ke=_1(i.lightElevation,1,89)*m1,Me={x:Math.cos(Ie)*Math.cos(ke),y:-Math.sin(Ie)*Math.cos(ke),z:-Math.sin(ke)},we=t.gridSpace===\"shape\"?Ae(Me.x,Me.y,Me.z):Me,Ve=Sne(AP(we,Math.abs(we.x)<.9?{x:1,y:0,z:0}:{x:0,y:1,z:0})),qe=AP(we,Ve),st=-oe.rBound,pt=Math.min(Lm,Math.ceil(2*oe.rBound/c)),Ze=Math.ceil(pt/4),nt=_1(i.fill,-.5,.5),ht=_1(i.voxelScale,.2,1),yt=_1(i.bevel,0,1);f.write({cam:de,lightDir:A(we.x,we.y,we.z),voxelSize:c,lightT1:A(Ve.x,Ve.y,Ve.z),fill:nt,lightT2:A(qe.x,qe.y,qe.z),voxelScale:ht,bevel:yt,gridOrigin:st,gridN:pt,gridW:Ze});let St=u.getGeometryKey?u.getGeometryKey():u.getStateKey(),Mt=t.gridSpace===\"view\"?`${u.getStateKey()},${i.pitch},${i.yaw},${c},${nt},${pt}`:`${St},${c},${nt},${pt}`,Rt=`${ht},${yt}`,Kt=`${we.x},${we.y},${we.z}`,Wt=Mt,ls=`${Mt},${Rt},${Kt}`,us=`${Mt},${Rt},${u.getStateKey()},${i.pitch},${i.yaw},${F}`,os=Wt!==Y,bs=ls!==V,hs=us!==q;if(!os&&!bs&&!hs||hs&&(Q++,X>1&&Q%X!==0&&!os))return null;Y=Wt,V=ls;let gs=[];return os&&gs.push(()=>k.dispatchThreads(Ze,pt,pt)),bs&&(e.setExtraField(\"_vxL\",[we.x,we.y,we.z]),e.setExtraField(\"_vxT1\",[Ve.x,Ve.y,Ve.z]),e.setExtraField(\"_vxT2\",[qe.x,qe.y,qe.z]),gs.push(O)),hs&&(q=us,u.setActiveRes(F),e.setExtraField(\"_vfOriginX\",oe.originX),e.setExtraField(\"_vfOriginY\",oe.originY),e.setExtraField(\"_vfSpanX\",oe.spanX),e.setExtraField(\"_vfSpanY\",oe.spanY),e.setExtraField(\"_vfActiveRes\",F),e.setExtraField(\"_vfRBound\",oe.rBound),e.setExtraField(\"_vxMx\",[De.x,De.y,De.z]),e.setExtraField(\"_vxMy\",[Ee.x,Ee.y,Ee.z]),e.setExtraField(\"_vxMz\",[ze.x,ze.y,ze.z]),e.setExtraField(\"_vxVoxel\",c),e.setExtraField(\"_vxGridOrigin\",st),gs.push(()=>C.dispatch(F))),gs}}}var tz=ko({bevelSin:()=>sz}),sz=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_sub(1,t),o=be(0,.14,t),a=be(.68,.92,t);return K(r,K(n(1),__tsover_mul(.72,__tsover_sub(1,a)),o),s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"shape\"}],body:[0,[[13,\"sRound\",[1,[5,\"1\"],\"-\",\"t\"]],[13,\"k1\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.14\"],\"t\"]]],[13,\"k2\",[6,\"std.smoothstep\",[[5,\"0.68\"],[5,\"0.92\"],\"t\"]]],[13,\"sMach\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],[1,[5,\"0.72\"],\"*\",[1,[5,\"1\"],\"-\",\"k2\"]],\"k1\"]]],[10,[6,\"std.mix\",[\"sRound\",\"sMach\",\"shape\"]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.mix\":()=>K,\"d.f32\":()=>n}})&&e.f)({})),ip=ko({BLUR_DISK:()=>jc,GlassLensUVs:()=>F1,applyGlassEffect:()=>Mne,glassBlurOffset:()=>oz,glassBorderHighlight:()=>wx,glassComposite:()=>az,glassFresnelRim:()=>Sx,glassGradient:()=>Jh,glassLensUVs:()=>rz,glassRefrStrength:()=>Jm,glassSpecular:()=>Tx,glassTint:()=>Ax,offsetUV:()=>da,sdfSpaceUV:()=>xc}),ec=.017453292519943295,f1=.4472135954999579,Cne=2.3999632297286535,xx=9,jc=[];for(let e=0;e<xx;e++){let t=e*Cne,s=Math.sqrt(e/xx);jc.push([Math.cos(t)*s,Math.sin(t)*s])}var xc=w.fn([m,n,n,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=t.x,l=__tsover_sub(1,t.y),c=__tsover_mul(__tsover_sub(o.x,i),a),d=__tsover_sub(o.y,l),u=__tsover_mul(r,ec),p=pe(u),_=ae(u),f=__tsover_add(__tsover_mul(c,p),__tsover_mul(d,_)),h=__tsover_sub(__tsover_mul(d,p),__tsover_mul(c,_));return m(__tsover_add(__tsover_div(f,s),.5),__tsover_add(__tsover_div(h,s),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"centerX\",[7,\"center\",\"x\"]],[13,\"centerY\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"dxAc\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"],\"*\",\"aspect\"]],[13,\"dy\",[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"]],[13,\"rotRad\",[1,\"rotation\",\"*\",\"DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rdx\",[1,[1,\"dxAc\",\"*\",\"cosR\"],\"+\",[1,\"dy\",\"*\",\"sinR\"]]],[13,\"rdy\",[1,[1,\"dy\",\"*\",\"cosR\"],\"-\",[1,\"dxAc\",\"*\",\"sinR\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"rdx\",\"/\",\"scale\"],\"+\",[5,\"0.5\"]],[1,[1,\"rdy\",\"/\",\"scale\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{DEG_TO_RAD:()=>ec,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({})),da=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(__tsover_add(t.x,s),__tsover_add(t.y,r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"+\",\"ox\"],[1,[7,\"uv\",\"y\"],\"+\",\"oy\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Jh=w.fn([n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return m(__tsover_div(__tsover_sub(s,t),.01),__tsover_div(__tsover_sub(r,t),.01))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfRaw\"},{type:\"i\",name:\"sdfX\"},{type:\"i\",name:\"sdfY\"}],body:[0,[[13,\"eps\",[5,\"0.01\"]],[10,[6,\"d.vec2f\",[[1,[1,\"sdfX\",\"-\",\"sdfRaw\"],\"/\",\"eps\"],[1,[1,\"sdfY\",\"-\",\"sdfRaw\"],\"/\",\"eps\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Jm=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(__tsover_mul(s,.3),.005),a=G(__tsover_div(__tsover_mul(t,-1),o),0,1),i=be(0,1,a);return N(__tsover_mul(__tsover_sub(1,a),__tsover_sub(1,a)),i,r>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"thicknessRange\",[6,\"std.max\",[[1,\"thickness\",\"*\",[5,\"0.3\"]],[5,\"0.005\"]]]],[13,\"depthNorm\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"thicknessRange\"],[5,\"0\"],[5,\"1\"]]]],[13,\"vol\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"1\"],\"depthNorm\"]]],[13,\"flat\",[1,[1,[5,\"1\"],\"-\",\"depthNorm\"],\"*\",[1,[5,\"1\"],\"-\",\"depthNorm\"]]],[10,[6,\"std.select\",[\"flat\",\"vol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.smoothstep\":()=>be,\"std.select\":()=>N}})&&e.f)({})),F1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({lensUV:m,rUV:m,bUV:m}),\"GlassLensUVs\"),rz=w.fn([m,m,n,n,n,n,n,n,n,n,n,n],F1)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_)=>{\"use gpu\";let f=Jm(__tsover_div(r,o),d,_),h=__tsover_mul(l,.15),g=B(Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(i,i))),1e-4),y=ye(n(1),__tsover_div(12,g)),b=__tsover_div(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(a,y),-1),h),f),p),x=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(i,y),-1),h),f),P=s.x,C=__tsover_sub(1,s.y),E=__tsover_add(P,__tsover_div(__tsover_sub(t.x,P),c)),R=__tsover_add(C,__tsover_div(__tsover_sub(t.y,C),c)),U=__tsover_add(E,b),L=__tsover_add(R,x),k=__tsover_mul(u,.06),D=__tsover_mul(b,k),O=__tsover_mul(x,k);return F1({lensUV:m(U,L),rUV:m(__tsover_add(U,D),__tsover_add(L,O)),bUV:m(__tsover_sub(U,D),__tsover_sub(L,O))})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"sdfRaw\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"refraction\"},{type:\"i\",name:\"innerZoom\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"aberration\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"sdf\",[1,\"sdfRaw\",\"/\",\"scale\"]],[13,\"refrStrength\",[6,\"glassRefrStrength\",[\"sdf\",\"thickness\",\"volumetric\"]]],[13,\"refrScale\",[1,\"refraction\",\"*\",[5,\"0.15\"]]],[13,\"slopeLen\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"gradX\",\"*\",\"gradX\"],\"+\",[1,\"gradY\",\"*\",\"gradY\"]]]],[5,\"0.0001\"]]]],[13,\"slopeScale\",[6,\"std.min\",[[6,\"d.f32\",[[5,\"1\"]]],[1,[5,\"12\"],\"/\",\"slopeLen\"]]]],[13,\"offsetX\",[1,[1,[1,[1,[1,\"gradX\",\"*\",\"slopeScale\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"refrScale\"],\"*\",\"refrStrength\"],\"/\",\"aspect\"]],[13,\"offsetY\",[1,[1,[1,[1,\"gradY\",\"*\",\"slopeScale\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"refrScale\"],\"*\",\"refrStrength\"]],[13,\"centerX\",[7,\"center\",\"x\"]],[13,\"centerY\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"zoomedX\",[1,\"centerX\",\"+\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"],\"/\",\"innerZoom\"]]],[13,\"zoomedY\",[1,\"centerY\",\"+\",[1,[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"],\"/\",\"innerZoom\"]]],[13,\"lensX\",[1,\"zoomedX\",\"+\",\"offsetX\"]],[13,\"lensY\",[1,\"zoomedY\",\"+\",\"offsetY\"]],[13,\"chrScale\",[1,\"aberration\",\"*\",[5,\"0.06\"]]],[13,\"chrOffX\",[1,\"offsetX\",\"*\",\"chrScale\"]],[13,\"chrOffY\",[1,\"offsetY\",\"*\",\"chrScale\"]],[10,[6,\"GlassLensUVs\",[[104,{lensUV:[6,\"d.vec2f\",[\"lensX\",\"lensY\"]],rUV:[6,\"d.vec2f\",[[1,\"lensX\",\"+\",\"chrOffX\"],[1,\"lensY\",\"+\",\"chrOffY\"]]],bUV:[6,\"d.vec2f\",[[1,\"lensX\",\"-\",\"chrOffX\"],[1,\"lensY\",\"-\",\"chrOffY\"]]]}]]]]]]},externals:{glassRefrStrength:()=>Jm,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.min\":()=>ye,\"d.f32\":()=>n,GlassLensUVs:()=>F1,\"d.vec2f\":()=>m}})&&e.f)({})),oz=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,2);return m(__tsover_mul(__tsover_div(r,t.x),a),__tsover_mul(__tsover_div(o,t.y),a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"viewportSize\"},{type:\"i\",name:\"blur\"},{type:\"i\",name:\"ox\"},{type:\"i\",name:\"oy\"}],body:[0,[[13,\"m\",[1,\"blur\",\"*\",[5,\"2\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"ox\",\"/\",[7,\"viewportSize\",\"x\"]],\"*\",\"m\"],[1,[1,\"oy\",\"/\",[7,\"viewportSize\",\"y\"]],\"*\",\"m\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Ax=w.fn([A,A,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=A(.299,.587,.114),i=Se(t,a),l=K(t,s,r),c=Se(l,a);return K(l,l.mul(__tsover_div(i,B(c,1e-4))),N(n(0),n(1),o>.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"tintColor\"},{type:\"i\",name:\"tintIntensity\"},{type:\"i\",name:\"preserveLum\"}],body:[0,[[13,\"lumWeights\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]],[13,\"origLum\",[6,\"std.dot\",[\"rgb\",\"lumWeights\"]]],[13,\"tinted\",[6,\"std.mix\",[\"rgb\",\"tintColor\",\"tintIntensity\"]]],[13,\"tintedLum\",[6,\"std.dot\",[\"tinted\",\"lumWeights\"]]],[13,\"lumPreserved\",[6,[7,\"tinted\",\"mul\"],[[1,\"origLum\",\"/\",[6,\"std.max\",[\"tintedLum\",[5,\"0.0001\"]]]]]]],[13,\"preserveFactor\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"preserveLum\",\">\",[5,\"0.5\"]]]]],[10,[6,\"std.mix\",[\"tinted\",\"lumPreserved\",\"preserveFactor\"]]]]]},externals:{\"d.vec3f\":()=>A,\"std.dot\":()=>Se,\"std.mix\":()=>K,\"std.max\":()=>B,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})),wx=w.fn([n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=B(__tsover_mul(s,.5),.001),d=B(__tsover_mul(c,.0625),__tsover_mul(r,1.5)),u=__tsover_sub(t,r),p=__tsover_sub(G(__tsover_div(__tsover_mul(u,-1),d),0,1),G(__tsover_div(__tsover_mul(t,-1),d),0,1)),_=pe(__tsover_mul(i,ec)),f=ae(__tsover_mul(i,ec)),h=__tsover_add(__tsover_mul(o,_),__tsover_mul(a,f)),g=G(__tsover_add(__tsover_mul(h,.5),.5),0,1);return __tsover_mul(__tsover_mul(p,g),l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"edgeSoftness\"},{type:\"i\",name:\"pxH\"},{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"highlight\"}],body:[0,[[13,\"sharp\",[6,\"std.max\",[[1,\"edgeSoftness\",\"*\",[5,\"0.5\"]],[5,\"0.001\"]]]],[13,\"w2\",[6,\"std.max\",[[1,\"sharp\",\"*\",[5,\"0.0625\"]],[1,\"pxH\",\"*\",[5,\"1.5\"]]]]],[13,\"sdfOuter\",[1,\"sdf\",\"-\",\"pxH\"]],[13,\"rb2base\",[1,[6,\"std.clamp\",[[1,[1,\"sdfOuter\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"w2\"],[5,\"0\"],[5,\"1\"]]],\"-\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"w2\"],[5,\"0\"],[5,\"1\"]]]]],[13,\"lx\",[6,\"std.cos\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"ly\",[6,\"std.sin\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"normalDotLight\",[1,[1,\"gradX\",\"*\",\"lx\"],\"+\",[1,\"gradY\",\"*\",\"ly\"]]],[13,\"lightFacing\",[6,\"std.clamp\",[[1,[1,\"normalDotLight\",\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[10,[1,[1,\"rb2base\",\"*\",\"lightFacing\"],\"*\",\"highlight\"]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,\"std.cos\":()=>pe,DEG_TO_RAD:()=>ec,\"std.sin\":()=>ae}})&&e.f)({})),Tx=w.fn([n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=pe(__tsover_mul(r,ec)),c=ae(__tsover_mul(r,ec)),d=n(2),u=Ue(__tsover_add(__tsover_add(__tsover_mul(t,t),__tsover_mul(s,s)),__tsover_mul(d,d))),p=__tsover_div(t,u),_=__tsover_div(s,u),f=__tsover_div(d,u),h=__tsover_add(__tsover_add(__tsover_mul(p,__tsover_mul(l,f1)),__tsover_mul(_,__tsover_mul(c,f1))),__tsover_mul(f,__tsover_mul(2,f1))),g=Ia(__tsover_sub(8,__tsover_mul(a,7)));return __tsover_mul(__tsover_mul(vt(G(h,0,1),g),o),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"highlight\"},{type:\"i\",name:\"highlightSoftness\"},{type:\"i\",name:\"refrStrength\"}],body:[0,[[13,\"lx\",[6,\"std.cos\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"ly\",[6,\"std.sin\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"nz\",[6,\"d.f32\",[[5,\"2\"]]]],[13,\"nLen\",[6,\"std.sqrt\",[[1,[1,[1,\"gradX\",\"*\",\"gradX\"],\"+\",[1,\"gradY\",\"*\",\"gradY\"]],\"+\",[1,\"nz\",\"*\",\"nz\"]]]]],[13,\"nx\",[1,\"gradX\",\"/\",\"nLen\"]],[13,\"ny\",[1,\"gradY\",\"/\",\"nLen\"]],[13,\"nnz\",[1,\"nz\",\"/\",\"nLen\"]],[13,\"nDotH\",[1,[1,[1,\"nx\",\"*\",[1,\"lx\",\"*\",\"INV_HLEN_GLASS\"]],\"+\",[1,\"ny\",\"*\",[1,\"ly\",\"*\",\"INV_HLEN_GLASS\"]]],\"+\",[1,\"nnz\",\"*\",[1,[5,\"2\"],\"*\",\"INV_HLEN_GLASS\"]]]],[13,\"shininess\",[6,\"std.exp2\",[[1,[5,\"8\"],\"-\",[1,\"highlightSoftness\",\"*\",[5,\"7\"]]]]]],[10,[1,[1,[6,\"std.pow\",[[6,\"std.clamp\",[\"nDotH\",[5,\"0\"],[5,\"1\"]]],\"shininess\"]],\"*\",\"highlight\"],\"*\",\"refrStrength\"]]]]},externals:{\"std.cos\":()=>pe,DEG_TO_RAD:()=>ec,\"std.sin\":()=>ae,\"d.f32\":()=>n,\"std.sqrt\":()=>Ue,INV_HLEN_GLASS:()=>f1,\"std.exp2\":()=>Ia,\"std.pow\":()=>vt,\"std.clamp\":()=>G}})&&e.f)({})),Sx=w.fn([n,n,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_add(__tsover_mul(s,s),__tsover_mul(r,r)),d=__tsover_div(1,Ue(__tsover_add(c,1))),u=K(6,1.5,G(a,0,1)),p=__tsover_mul(__tsover_mul(__tsover_mul(vt(__tsover_sub(1,d),u),o),2),i),_=B(__tsover_mul(a,.06),.001),f=G(__tsover_div(__tsover_mul(t,-1),_),0,1);return N(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_sub(1,f),__tsover_sub(1,f)),o),i),p,l>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"fresnel\"},{type:\"i\",name:\"fresnelSoftness\"},{type:\"i\",name:\"rb1\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"slopeSq\",[1,[1,\"gradX\",\"*\",\"gradX\"],\"+\",[1,\"gradY\",\"*\",\"gradY\"]]],[13,\"cosView\",[1,[5,\"1\"],\"/\",[6,\"std.sqrt\",[[1,\"slopeSq\",\"+\",[5,\"1\"]]]]]],[13,\"exponent\",[6,\"std.mix\",[[5,\"6\"],[5,\"1.5\"],[6,\"std.clamp\",[\"fresnelSoftness\",[5,\"0\"],[5,\"1\"]]]]]],[13,\"volRim\",[1,[1,[1,[6,\"std.pow\",[[1,[5,\"1\"],\"-\",\"cosView\"],\"exponent\"]],\"*\",\"fresnel\"],\"*\",[5,\"2\"]],\"*\",\"rb1\"]],[13,\"fresnelWidth\",[6,\"std.max\",[[1,\"fresnelSoftness\",\"*\",[5,\"0.06\"]],[5,\"0.001\"]]]],[13,\"fresnelDepth\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"fresnelWidth\"],[5,\"0\"],[5,\"1\"]]]],[13,\"flatRim\",[1,[1,[1,[1,[5,\"1\"],\"-\",\"fresnelDepth\"],\"*\",[1,[5,\"1\"],\"-\",\"fresnelDepth\"]],\"*\",\"fresnel\"],\"*\",\"rb1\"]],[10,[6,\"std.select\",[\"flatRim\",\"volRim\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.sqrt\":()=>Ue,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"std.pow\":()=>vt,\"std.max\":()=>B,\"std.select\":()=>N}})&&e.f)({})),az=w.fn([S,S,n,n,n,n,m,n,n,n,A,n,n,A,n,n,A,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_,f,h,g,y,b,x,P,C,E)=>{\"use gpu\";let R=B(__tsover_mul(c,.5),.001),U=__tsover_div(1,l.y),L=__tsover_div(r,o),k=B(__tsover_mul(R,.03125),__tsover_mul(U,1.5)),D=G(__tsover_div(__tsover_mul(L,-1),k),0,1),O=Jm(L,y,E),F=wx(L,c,U,a,i,d,u),q=Ax(A(s.x,s.y,s.z),b,x,P),Y=Tx(a,i,d,u,_,O),V=Sx(L,a,i,f,g,D,E),X=S(__tsover_add(__tsover_add(__tsover_add(q.x,__tsover_mul(p.x,F)),__tsover_mul(p.x,Y)),__tsover_mul(h.x,V)),__tsover_add(__tsover_add(__tsover_add(q.y,__tsover_mul(p.y,F)),__tsover_mul(p.y,Y)),__tsover_mul(h.y,V)),__tsover_add(__tsover_add(__tsover_add(q.z,__tsover_mul(p.z,F)),__tsover_mul(p.z,Y)),__tsover_mul(h.z,V)),__tsover_add(s.w,F)),Q=be(0,1,D),te=K(t,X,Q),J=N(n(1),Q,C>.5);return S(te.x,te.y,te.z,__tsover_mul(te.w,J))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"baseColor\"},{type:\"i\",name:\"blurred\"},{type:\"i\",name:\"sdfRaw\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"viewportSize\"},{type:\"i\",name:\"edgeSoftness\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"highlight\"},{type:\"i\",name:\"highlightColor\"},{type:\"i\",name:\"highlightSoftness\"},{type:\"i\",name:\"fresnel\"},{type:\"i\",name:\"fresnelColor\"},{type:\"i\",name:\"fresnelSoftness\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"tintColor\"},{type:\"i\",name:\"tintIntensity\"},{type:\"i\",name:\"tintPreserveLuminosity\"},{type:\"i\",name:\"cutout\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"sharp\",[6,\"std.max\",[[1,\"edgeSoftness\",\"*\",[5,\"0.5\"]],[5,\"0.001\"]]]],[13,\"pxH\",[1,[5,\"1\"],\"/\",[7,\"viewportSize\",\"y\"]]],[13,\"sdf\",[1,\"sdfRaw\",\"/\",\"scale\"]],[13,\"w1\",[6,\"std.max\",[[1,\"sharp\",\"*\",[5,\"0.03125\"]],[1,\"pxH\",\"*\",[5,\"1.5\"]]]]],[13,\"rb1\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"w1\"],[5,\"0\"],[5,\"1\"]]]],[13,\"refrStrength\",[6,\"glassRefrStrength\",[\"sdf\",\"thickness\",\"volumetric\"]]],[13,\"rb2\",[6,\"glassBorderHighlight\",[\"sdf\",\"edgeSoftness\",\"pxH\",\"gradX\",\"gradY\",\"lightAngle\",\"highlight\"]]],[13,\"blurredRgb\",[6,\"d.vec3f\",[[7,\"blurred\",\"x\"],[7,\"blurred\",\"y\"],[7,\"blurred\",\"z\"]]]],[13,\"tintedRgb\",[6,\"glassTint\",[\"blurredRgb\",\"tintColor\",\"tintIntensity\",\"tintPreserveLuminosity\"]]],[13,\"specGlint\",[6,\"glassSpecular\",[\"gradX\",\"gradY\",\"lightAngle\",\"highlight\",\"highlightSoftness\",\"refrStrength\"]]],[13,\"fresnelRim\",[6,\"glassFresnelRim\",[\"sdf\",\"gradX\",\"gradY\",\"fresnel\",\"fresnelSoftness\",\"rb1\",\"volumetric\"]]],[13,\"litR\",[1,[1,[1,[7,\"tintedRgb\",\"x\"],\"+\",[1,[7,\"highlightColor\",\"x\"],\"*\",\"rb2\"]],\"+\",[1,[7,\"highlightColor\",\"x\"],\"*\",\"specGlint\"]],\"+\",[1,[7,\"fresnelColor\",\"x\"],\"*\",\"fresnelRim\"]]],[13,\"litG\",[1,[1,[1,[7,\"tintedRgb\",\"y\"],\"+\",[1,[7,\"highlightColor\",\"y\"],\"*\",\"rb2\"]],\"+\",[1,[7,\"highlightColor\",\"y\"],\"*\",\"specGlint\"]],\"+\",[1,[7,\"fresnelColor\",\"y\"],\"*\",\"fresnelRim\"]]],[13,\"litB\",[1,[1,[1,[7,\"tintedRgb\",\"z\"],\"+\",[1,[7,\"highlightColor\",\"z\"],\"*\",\"rb2\"]],\"+\",[1,[7,\"highlightColor\",\"z\"],\"*\",\"specGlint\"]],\"+\",[1,[7,\"fresnelColor\",\"z\"],\"*\",\"fresnelRim\"]]],[13,\"litA\",[1,[7,\"blurred\",\"w\"],\"+\",\"rb2\"]],[13,\"lighting\",[6,\"d.vec4f\",[\"litR\",\"litG\",\"litB\",\"litA\"]]],[13,\"transition\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"1\"],\"rb1\"]]],[13,\"composited\",[6,\"std.mix\",[\"baseColor\",\"lighting\",\"transition\"]]],[13,\"cutoutAlpha\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"transition\",[1,\"cutout\",\">\",[5,\"0.5\"]]]]],[10,[6,\"d.vec4f\",[[7,\"composited\",\"x\"],[7,\"composited\",\"y\"],[7,\"composited\",\"z\"],[1,[7,\"composited\",\"w\"],\"*\",\"cutoutAlpha\"]]]]]]},externals:{\"std.max\":()=>B,\"std.clamp\":()=>G,glassRefrStrength:()=>Jm,glassBorderHighlight:()=>wx,\"d.vec3f\":()=>A,glassTint:()=>Ax,glassSpecular:()=>Tx,glassFresnelRim:()=>Sx,\"d.vec4f\":()=>S,\"std.smoothstep\":()=>be,\"std.mix\":()=>K,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({}));function Mne(e,t,s,r,o){let{uniforms:a,propValues:i,ctx:l}=e,c=o?.volumetric??!1,d=o?.bakedGradients??!1,u=$e(c?1:0),p=ns(W(xc,\"sdfSpaceUV\",[a.center,a.scale,a.rotation,l.uv,l.aspect]),\"glassSdfUV\"),_=ns(t(p),\"glassSurf\"),f=_.member(\"r\"),h,g;if(d)h=_.member(\"g\"),g=_.member(\"b\");else{let F=t(W(da,\"offsetUV\",[p,$e(.01),$e(0)])),q=t(W(da,\"offsetUV\",[p,$e(0),$e(.01)])),Y=ns(W(Jh,\"glassGradient\",[f,F.member(\"r\"),q.member(\"r\")]),\"glassGrad\");h=Y.member(\"x\"),g=Y.member(\"y\")}let y=ns(W(rz,\"glassLensUVs\",[l.uv,a.center,f,a.scale,h,g,a.refraction,a.innerZoom,a.thickness,a.aberration,l.aspect,u]),\"glassLens\"),b=y.member(\"lensUV\"),x=y.member(\"rUV\"),P=y.member(\"bUV\"),C=i.blur,E=typeof C==\"number\"?C>0:!0,R=i.aberration,U=typeof R==\"number\"?R>0:!0,L=(F,q,Y,V)=>F.sample(q.add(W(oz,\"glassBlurOffset\",[l.viewportSize,a.blur,$e(Y),$e(V)]))),k=(F,q)=>{let Y=L(F,q,jc[0][0],jc[0][1]);for(let V=1;V<jc.length;V++)Y=Y.add(L(F,q,jc[V][0],jc[V][1]));return Y.mul(1/xx)},D;if(r&&E)D=U?Ct(r.sample(x).member(\"r\"),r.sample(b).member(\"g\"),r.sample(P).member(\"b\"),r.sample(b).member(\"a\")):r.sample(b);else if(!E&&!U)D=s.sample(b);else if(!E&&U)D=Ct(s.sample(x).member(\"r\"),s.sample(b).member(\"g\"),s.sample(P).member(\"b\"),s.sample(b).member(\"a\"));else if(E&&!U)D=k(s,b);else{let F=k(s,x),q=k(s,b),Y=k(s,P);D=Ct(F.member(\"r\"),q.member(\"g\"),Y.member(\"b\"),q.member(\"a\"))}let O=W(Yt,\"unpremultiplyAlpha\",[D]);return W(az,\"glassComposite\",[W(Yt,\"unpremultiplyAlpha\",[s.sample(l.uv)]),O,f,a.scale,h,g,l.viewportSize,a.edgeSoftness,a.lightAngle,a.highlight,a.highlightColor.member(\"rgb\"),a.highlightSoftness,a.fresnel,a.fresnelColor.member(\"rgb\"),a.fresnelSoftness,a.thickness,a.tintColor.member(\"rgb\"),a.tintIntensity,a.tintPreserveLuminosity,a.cutout,u])}var Ene=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(t.x,t.y),o=__tsover_div(s.x,s.y),a=B(__tsover_div(o,r),1);return m(__tsover_mul(__tsover_div(r,o),a),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dims\"},{type:\"i\",name:\"vp\"}],body:[0,[[13,\"aspect\",[1,[7,\"dims\",\"x\"],\"/\",[7,\"dims\",\"y\"]]],[13,\"viewportAspect\",[1,[7,\"vp\",\"x\"],\"/\",[7,\"vp\",\"y\"]]],[13,\"coverScale\",[6,\"std.max\",[[1,\"viewportAspect\",\"/\",\"aspect\"],[5,\"1\"]]]],[10,[6,\"d.vec2f\",[[1,[1,\"aspect\",\"/\",\"viewportAspect\"],\"*\",\"coverScale\"],\"coverScale\"]]]]]},externals:{\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),kne=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(t.x,t.y),o=__tsover_div(s.x,s.y),a=ye(__tsover_div(o,r),1);return m(__tsover_mul(__tsover_div(r,o),a),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dims\"},{type:\"i\",name:\"vp\"}],body:[0,[[13,\"aspect\",[1,[7,\"dims\",\"x\"],\"/\",[7,\"dims\",\"y\"]]],[13,\"viewportAspect\",[1,[7,\"vp\",\"x\"],\"/\",[7,\"vp\",\"y\"]]],[13,\"containScale\",[6,\"std.min\",[[1,\"viewportAspect\",\"/\",\"aspect\"],[5,\"1\"]]]],[10,[6,\"d.vec2f\",[[1,[1,\"aspect\",\"/\",\"viewportAspect\"],\"*\",\"containScale\"],\"containScale\"]]]]]},externals:{\"std.min\":()=>ye,\"d.vec2f\":()=>m}})&&e.f)({})),Bne=w.fn([],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(()=>{\"use gpu\";return m(1,1)}),{v:2,name:void 0,ast:{params:[],body:[0,[[10,[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),zne=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(t.x,t.y),o=__tsover_div(s.x,s.y),a=ye(ye(__tsover_div(o,r),1),ye(__tsover_div(s.x,t.x),__tsover_div(s.y,t.y)));return m(__tsover_mul(__tsover_div(r,o),a),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dims\"},{type:\"i\",name:\"vp\"}],body:[0,[[13,\"aspect\",[1,[7,\"dims\",\"x\"],\"/\",[7,\"dims\",\"y\"]]],[13,\"viewportAspect\",[1,[7,\"vp\",\"x\"],\"/\",[7,\"vp\",\"y\"]]],[13,\"containScale\",[6,\"std.min\",[[1,\"viewportAspect\",\"/\",\"aspect\"],[5,\"1\"]]]],[13,\"naturalScale\",[6,\"std.min\",[[1,[7,\"vp\",\"x\"],\"/\",[7,\"dims\",\"x\"]],[1,[7,\"vp\",\"y\"],\"/\",[7,\"dims\",\"y\"]]]]],[13,\"scaleDownScale\",[6,\"std.min\",[\"containScale\",\"naturalScale\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"aspect\",\"/\",\"viewportAspect\"],\"*\",\"scaleDownScale\"],\"scaleDownScale\"]]]]]},externals:{\"std.min\":()=>ye,\"d.vec2f\":()=>m}})&&e.f)({})),Rne=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_div(t.x,t.y),o=__tsover_div(s.x,s.y),a=ye(__tsover_div(s.x,t.x),__tsover_div(s.y,t.y));return m(__tsover_mul(__tsover_div(r,o),a),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dims\"},{type:\"i\",name:\"vp\"}],body:[0,[[13,\"aspect\",[1,[7,\"dims\",\"x\"],\"/\",[7,\"dims\",\"y\"]]],[13,\"viewportAspect\",[1,[7,\"vp\",\"x\"],\"/\",[7,\"vp\",\"y\"]]],[13,\"noneScale\",[6,\"std.min\",[[1,[7,\"vp\",\"x\"],\"/\",[7,\"dims\",\"x\"]],[1,[7,\"vp\",\"y\"],\"/\",[7,\"dims\",\"y\"]]]]],[10,[6,\"d.vec2f\",[[1,[1,\"aspect\",\"/\",\"viewportAspect\"],\"*\",\"noneScale\"],\"noneScale\"]]]]]},externals:{\"std.min\":()=>ye,\"d.vec2f\":()=>m}})&&e.f)({})),Dne=w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t.sub(m(.5,.5));return m(__tsover_div(r.x,s.x),__tsover_div(r.y,s.y)).add(m(.5,.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"uvScale\"}],body:[0,[[13,\"centeredUV\",[6,[7,\"uv\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"scaledUV\",[6,\"d.vec2f\",[[1,[7,\"centeredUV\",\"x\"],\"/\",[7,\"uvScale\",\"x\"]],[1,[7,\"centeredUV\",\"y\"],\"/\",[7,\"uvScale\",\"y\"]]]]],[10,[6,[7,\"scaledUV\",\"add\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),Lne=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(N(t.x,__tsover_sub(1,t.x),s>0),t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"mirror\"}],body:[0,[[13,\"mirroredX\",[6,\"std.select\",[[7,\"uv\",\"x\"],[1,[5,\"1\"],\"-\",[7,\"uv\",\"x\"]],[1,\"mirror\",\">\",[5,\"0\"]]]]],[10,[6,\"d.vec2f\",[\"mirroredX\",[7,\"uv\",\"y\"]]]]]]},externals:{\"std.select\":()=>N,\"d.vec2f\":()=>m}})&&e.f)({})),Une=w.fn([S,m],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Iu(A(t.x,t.y,t.z)),o=s.x<0||s.x>1||s.y<0||s.y>1,a=N(t.w,0,o);return S(r.x,r.y,r.z,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"uv\"}],body:[0,[[13,\"linear\",[6,\"srgbToLinear\",[[6,\"d.vec3f\",[[7,\"color\",\"x\"],[7,\"color\",\"y\"],[7,\"color\",\"z\"]]]]]],[13,\"outOfBounds\",[3,[3,[3,[1,[7,\"uv\",\"x\"],\"<\",[5,\"0\"]],\"||\",[1,[7,\"uv\",\"x\"],\">\",[5,\"1\"]]],\"||\",[1,[7,\"uv\",\"y\"],\"<\",[5,\"0\"]]],\"||\",[1,[7,\"uv\",\"y\"],\">\",[5,\"1\"]]]],[13,\"alpha\",[6,\"std.select\",[[7,\"color\",\"w\"],[5,\"0\"],\"outOfBounds\"]]],[10,[6,\"d.vec4f\",[[7,\"linear\",\"x\"],[7,\"linear\",\"y\"],[7,\"linear\",\"z\"],\"alpha\"]]]]]},externals:{srgbToLinear:()=>Iu,\"d.vec3f\":()=>A,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&e.f)({})),One={cover:0,contain:1,fill:2,\"scale-down\":3,none:2},Gne={cover:0,contain:1,fill:2,\"scale-down\":3,none:4};function Fne(e){let t=e.allowNone?Gne:One,s=e.allowNone?0:2,r=typeof e.fit==\"number\"?e.fit:t[e.fit]??s,o=e.texture.dimensions(),a;r===1?a=W(kne,\"scaleContain\",[o,e.viewport]):r===2?a=W(Bne,\"scaleFill\",[]):r===3?a=W(zne,\"scaleScaleDown\",[o,e.viewport]):r===4&&e.allowNone?a=W(Rne,\"scaleNone\",[o,e.viewport]):a=W(Ene,\"scaleCover\",[o,e.viewport]);let i=W(Dne,\"mediaSampleUV\",[e.uv,a]),l=e.uvPostProcess?e.uvPostProcess(i):i;return W(Une,\"mediaAlphaCut\",[e.texture.sample(l),l])}var nz=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=re(s),i=__tsover_sub(r,t),l=o,c=__tsover_mul(r,2),d=G(__tsover_div(__tsover_add(__tsover_mul(a,l),__tsover_mul(i,c)),B(__tsover_add(__tsover_mul(l,l),__tsover_mul(c,c)),1e-9)),0,1),u=__tsover_sub(a,__tsover_mul(l,d)),p=__tsover_sub(i,__tsover_mul(c,d)),_=__tsover_sub(a,__tsover_mul(l,G(__tsover_div(a,B(l,1e-9)),0,1))),f=__tsover_sub(i,c),h=ye(__tsover_add(__tsover_mul(u,u),__tsover_mul(p,p)),__tsover_add(__tsover_mul(_,_),__tsover_mul(f,f))),g=ye(__tsover_sub(__tsover_mul(i,l),__tsover_mul(a,c)),__tsover_sub(c,i))>0;return N(Ue(h),-Ue(h),g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"hl\"},{type:\"i\",name:\"hw\"}],body:[0,[[13,\"px\",[6,\"std.abs\",[\"n\"]]],[13,\"py\",[1,\"hl\",\"-\",\"t\"]],[13,\"qx\",\"hw\"],[13,\"qy\",[1,\"hl\",\"*\",[5,\"2\"]]],[13,\"k\",[6,\"std.clamp\",[[1,[1,[1,\"px\",\"*\",\"qx\"],\"+\",[1,\"py\",\"*\",\"qy\"]],\"/\",[6,\"std.max\",[[1,[1,\"qx\",\"*\",\"qx\"],\"+\",[1,\"qy\",\"*\",\"qy\"]],[5,\"1e-9\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"ax\",[1,\"px\",\"-\",[1,\"qx\",\"*\",\"k\"]]],[13,\"ay\",[1,\"py\",\"-\",[1,\"qy\",\"*\",\"k\"]]],[13,\"bx\",[1,\"px\",\"-\",[1,\"qx\",\"*\",[6,\"std.clamp\",[[1,\"px\",\"/\",[6,\"std.max\",[\"qx\",[5,\"1e-9\"]]]],[5,\"0\"],[5,\"1\"]]]]]],[13,\"by\",[1,\"py\",\"-\",\"qy\"]],[13,\"dSq\",[6,\"std.min\",[[1,[1,\"ax\",\"*\",\"ax\"],\"+\",[1,\"ay\",\"*\",\"ay\"]],[1,[1,\"bx\",\"*\",\"bx\"],\"+\",[1,\"by\",\"*\",\"by\"]]]]],[13,\"sA\",[1,[1,\"py\",\"*\",\"qx\"],\"-\",[1,\"px\",\"*\",\"qy\"]]],[13,\"sB\",[1,\"qy\",\"-\",\"py\"]],[13,\"inside\",[1,[6,\"std.min\",[\"sA\",\"sB\"]],\">\",[5,\"0\"]]],[10,[6,\"std.select\",[[6,\"std.sqrt\",[\"dSq\"]],[4,\"-\",[6,\"std.sqrt\",[\"dSq\"]]],\"inside\"]]]]]},externals:{\"std.abs\":()=>re,\"std.clamp\":()=>G,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.select\":()=>N,\"std.sqrt\":()=>Ue}})&&e.f)({})).$name(\"agentArrowSdf\"),iz=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(t,G(t,-r,r));return __tsover_sub(Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(s,s))),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"hl\"},{type:\"i\",name:\"r\"}],body:[0,[[13,\"tc\",[1,\"t\",\"-\",[6,\"std.clamp\",[\"t\",[4,\"-\",\"hl\"],\"hl\"]]]],[10,[1,[6,\"std.sqrt\",[[1,[1,\"tc\",\"*\",\"tc\"],\"+\",[1,\"n\",\"*\",\"n\"]]]],\"-\",\"r\"]]]]},externals:{\"std.clamp\":()=>G,\"std.sqrt\":()=>Ue}})&&e.f)({})).$name(\"agentCapsuleSdf\"),lz=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(re(t),r),i=__tsover_sub(re(s),o),l=B(a,0),c=B(i,0);return __tsover_add(Ue(__tsover_add(__tsover_mul(l,l),__tsover_mul(c,c))),ye(B(a,i),0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"hl\"},{type:\"i\",name:\"hw\"}],body:[0,[[13,\"qx\",[1,[6,\"std.abs\",[\"t\"]],\"-\",\"hl\"]],[13,\"qy\",[1,[6,\"std.abs\",[\"n\"]],\"-\",\"hw\"]],[13,\"ox\",[6,\"std.max\",[\"qx\",[5,\"0\"]]]],[13,\"oy\",[6,\"std.max\",[\"qy\",[5,\"0\"]]]],[10,[1,[6,\"std.sqrt\",[[1,[1,\"ox\",\"*\",\"ox\"],\"+\",[1,\"oy\",\"*\",\"oy\"]]]],\"+\",[6,\"std.min\",[[6,\"std.max\",[\"qx\",\"qy\"]],[5,\"0\"]]]]]]]},externals:{\"std.abs\":()=>re,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.min\":()=>ye}})&&e.f)({})).$name(\"agentBoxSdf\"),zd={arrow:{kind:1,hl:2.3,hw:1,soft:0,ext:2.65},streak:{kind:0,hl:1.7,hw:.8,soft:0,ext:2.6},dot:{kind:0,hl:0,hw:1,soft:0,ext:1.2},square:{kind:2,hl:.9,hw:.9,soft:0,ext:1.5},glow:{kind:0,hl:0,hw:0,soft:1,ext:2.1}};function wd(e,t){return typeof e==\"string\"&&e in zd?e:t}var lp=[{label:\"Arrow\",value:\"arrow\"},{label:\"Streak\",value:\"streak\"},{label:\"Dot\",value:\"dot\"},{label:\"Square\",value:\"square\"},{label:\"Glow\",value:\"glow\"}],cp=[{label:\"Dot\",value:\"dot\"},{label:\"Square\",value:\"square\"},{label:\"Glow\",value:\"glow\"}];function Ine(e){let t=zd[wd(e,\"dot\")],s=t.kind===1?nz:t.kind===2?lz:iz,r=t.hl,o=t.hw;return w.fn([n,n,n,n,n],n)((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l,c,d,u)=>{\"use gpu\";let p=s(i,l,__tsover_mul(c,r),__tsover_mul(c,o)),_=__tsover_sub(1,be(-d,d,p)),f=__tsover_div(B(p,0),B(__tsover_mul(c,.75),1e-6));return K(_,Tt(__tsover_mul(__tsover_mul(f,f),-1)),u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"},{type:\"i\",name:\"soft\"}],body:[0,[[13,\"sd\",[6,\"sdFn\",[\"t\",\"n\",[1,\"bodyR\",\"*\",\"HL\"],[1,\"bodyR\",\"*\",\"HW\"]]]],[13,\"wHard\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"aaW\"],\"aaW\",\"sd\"]]]],[13,\"gq\",[1,[6,\"std.max\",[\"sd\",[5,\"0\"]]],\"/\",[6,\"std.max\",[[1,\"bodyR\",\"*\",[5,\"0.75\"]],[5,\"0.000001\"]]]]],[13,\"wSoft\",[6,\"std.exp\",[[1,[1,\"gq\",\"*\",\"gq\"],\"*\",[4,\"-\",[5,\"1\"]]]]]],[10,[6,\"std.mix\",[\"wHard\",\"wSoft\",\"soft\"]]]]]},externals:{sdFn:()=>s,HL:()=>r,HW:()=>o,\"std.smoothstep\":()=>be,\"std.max\":()=>B,\"std.exp\":()=>Tt,\"std.mix\":()=>K}})&&a.f)({})).$name(\"agentWeight\")}function k_(e){let t=zd[wd(e,\"dot\")],s=t.kind===1?nz:t.kind===2?lz:iz,r=t.hl,o=t.hw,a=t.kind===0&&t.hl===0,i=t.soft===1;return a?i?w.fn([n,n,n,n,n],n)((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d,u,p,_)=>{\"use gpu\";let f=__tsover_mul(p,__tsover_sub(Ue(c),o)),h=__tsover_div(B(f,0),B(__tsover_mul(p,.75),1e-6));return Tt(__tsover_mul(__tsover_mul(h,h),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"_t\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"_aaW\"}],body:[0,[[13,\"sd\",[1,\"bodyR\",\"*\",[1,[6,\"std.sqrt\",[\"q\"]],\"-\",\"HW\"]]],[13,\"gq\",[1,[6,\"std.max\",[\"sd\",[5,\"0\"]]],\"/\",[6,\"std.max\",[[1,\"bodyR\",\"*\",[5,\"0.75\"]],[5,\"0.000001\"]]]]],[10,[6,\"std.exp\",[[1,[1,\"gq\",\"*\",\"gq\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{\"std.sqrt\":()=>Ue,HW:()=>o,\"std.max\":()=>B,\"std.exp\":()=>Tt}})&&l.f)({})).$name(\"agentTexelWeight\"):w.fn([n,n,n,n,n],n)((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d,u,p,_)=>{\"use gpu\";let f=__tsover_mul(p,__tsover_sub(Ue(c),o));return __tsover_sub(1,be(-_,_,f))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"_t\"},{type:\"i\",name:\"_n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"}],body:[0,[[13,\"sd\",[1,\"bodyR\",\"*\",[1,[6,\"std.sqrt\",[\"q\"]],\"-\",\"HW\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"aaW\"],\"aaW\",\"sd\"]]]]]]},externals:{\"std.sqrt\":()=>Ue,HW:()=>o,\"std.smoothstep\":()=>be}})&&l.f)({})).$name(\"agentTexelWeight\"):i?w.fn([n,n,n,n,n],n)((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d,u,p,_)=>{\"use gpu\";let f=s(d,u,__tsover_mul(p,r),__tsover_mul(p,o)),h=__tsover_div(B(f,0),B(__tsover_mul(p,.75),1e-6));return Tt(__tsover_mul(__tsover_mul(h,h),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_q\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"_aaW\"}],body:[0,[[13,\"sd\",[6,\"sdFn\",[\"t\",\"n\",[1,\"bodyR\",\"*\",\"HL\"],[1,\"bodyR\",\"*\",\"HW\"]]]],[13,\"gq\",[1,[6,\"std.max\",[\"sd\",[5,\"0\"]]],\"/\",[6,\"std.max\",[[1,\"bodyR\",\"*\",[5,\"0.75\"]],[5,\"0.000001\"]]]]],[10,[6,\"std.exp\",[[1,[1,\"gq\",\"*\",\"gq\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{sdFn:()=>s,HL:()=>r,HW:()=>o,\"std.max\":()=>B,\"std.exp\":()=>Tt}})&&l.f)({})).$name(\"agentTexelWeight\"):w.fn([n,n,n,n,n],n)((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d,u,p,_)=>{\"use gpu\";let f=s(d,u,__tsover_mul(p,r),__tsover_mul(p,o));return __tsover_sub(1,be(-_,_,f))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_q\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"}],body:[0,[[13,\"sd\",[6,\"sdFn\",[\"t\",\"n\",[1,\"bodyR\",\"*\",\"HL\"],[1,\"bodyR\",\"*\",\"HW\"]]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"aaW\"],\"aaW\",\"sd\"]]]]]]},externals:{sdFn:()=>s,HL:()=>r,HW:()=>o,\"std.smoothstep\":()=>be}})&&l.f)({})).$name(\"agentTexelWeight\")}function cz(e,t){let s=wd(e,\"streak\"),r=zd[s],o=t?.comet!==!1;return{name:s,soft:r.soft,hl:o&&s===\"glow\"?1:r.hl,ext:o&&s===\"glow\"?r.ext+1:r.ext,symmetric:r.kind===0&&r.hl===0&&!(o&&s===\"glow\")}}function Yne(e){let t=cz(e),s=k_(t.name),r=t.hl,o=t.soft;return w.fn([n,n,n,n],n)((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l,c,d)=>{\"use gpu\";let u=__tsover_mul(c,r),p=N(i,__tsover_sub(i,G(i,-u,u)),n(o)>.5),_=G(__tsover_div(__tsover_sub(u,i),B(__tsover_mul(u,2),1e-6)),0,1),f=__tsover_sub(1,__tsover_mul(__tsover_mul(.55,_),n(o))),h=__tsover_div(__tsover_add(__tsover_mul(p,p),__tsover_mul(l,l)),__tsover_mul(c,c));return __tsover_mul(s(h,p,l,c,d),f)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"}],body:[0,[[13,\"hl\",[1,\"bodyR\",\"*\",\"HL\"]],[13,\"tGlow\",[1,\"t\",\"-\",[6,\"std.clamp\",[\"t\",[4,\"-\",\"hl\"],\"hl\"]]]],[13,\"tEval\",[6,\"std.select\",[\"t\",\"tGlow\",[1,[6,\"d.f32\",[\"SOFT\"]],\">\",[5,\"0.5\"]]]]],[13,\"tk\",[6,\"std.clamp\",[[1,[1,\"hl\",\"-\",\"t\"],\"/\",[6,\"std.max\",[[1,\"hl\",\"*\",[5,\"2\"]],[5,\"0.000001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"bright\",[1,[5,\"1\"],\"-\",[1,[1,[5,\"0.55\"],\"*\",\"tk\"],\"*\",[6,\"d.f32\",[\"SOFT\"]]]]],[13,\"qEval\",[1,[1,[1,\"tEval\",\"*\",\"tEval\"],\"+\",[1,\"n\",\"*\",\"n\"]],\"/\",[1,\"bodyR\",\"*\",\"bodyR\"]]],[10,[1,[6,\"weightFn\",[\"qEval\",\"tEval\",\"n\",\"bodyR\",\"aaW\"]],\"*\",\"bright\"]]]]},externals:{HL:()=>r,\"std.clamp\":()=>G,\"std.select\":()=>N,\"d.f32\":()=>n,SOFT:()=>o,\"std.max\":()=>B,weightFn:()=>s}})&&a.f)({})).$name(\"agentCometWeight\")}var v1=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=he(t),i=N(m(0,0),t.div(B(a,1e-5)).mul(r),a>1e-5).sub(s),l=he(i);return N(i,i.div(B(l,1e-5)).mul(o),l>o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dir\"},{type:\"i\",name:\"vel\"},{type:\"i\",name:\"maxSpeed\"},{type:\"i\",name:\"maxForce\"}],body:[0,[[13,\"len\",[6,\"std.length\",[\"dir\"]]],[13,\"desired\",[6,\"std.select\",[[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,[7,[6,[7,\"dir\",\"div\"],[[6,\"std.max\",[\"len\",[5,\"0.00001\"]]]]],\"mul\"],[\"maxSpeed\"]],[1,\"len\",\">\",[5,\"0.00001\"]]]]],[13,\"steer\",[6,[7,\"desired\",\"sub\"],[\"vel\"]]],[13,\"sl\",[6,\"std.length\",[\"steer\"]]],[10,[6,\"std.select\",[\"steer\",[6,[7,[6,[7,\"steer\",\"div\"],[[6,\"std.max\",[\"sl\",[5,\"0.00001\"]]]]],\"mul\"],[\"maxForce\"]],[1,\"sl\",\">\",\"maxForce\"]]]]]]},externals:{\"std.length\":()=>he,\"std.select\":()=>N,\"d.vec2f\":()=>m,\"std.max\":()=>B}})&&e.f)({})).$name(\"agentReynoldsSteer\"),wP=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(.82,__tsover_mul(Xs(__tsover_add(__tsover_mul(t,.61803),.13)),.4))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[10,[1,[5,\"0.82\"],\"+\",[1,[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"0.61803\"]],\"+\",[5,\"0.13\"]]]],\"*\",[5,\"0.4\"]]]]]]},externals:{\"noise.hash11\":()=>Xs}})&&e.f)({})).$name(\"agentCruiseVariation\"),h1=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_add(.55,__tsover_mul(Xs(__tsover_add(__tsover_mul(t,1.91),2.53)),.7))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[10,[1,[5,\"0.55\"],\"+\",[1,[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"1.91\"]],\"+\",[5,\"2.53\"]]]],\"*\",[5,\"0.7\"]]]]]]},externals:{\"noise.hash11\":()=>Xs}})&&e.f)({})).$name(\"agentPresenceVariation\"),dz=w.fn([S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=G(t.w,0,1),o=__tsover_add(r,__tsover_mul(s.w,__tsover_sub(1,r))),a=t.xyz.mul(r).add(s.xyz.mul(__tsover_mul(s.w,__tsover_sub(1,r)))).div(B(o,1e-4));return S(a.x,a.y,a.z,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"c\"}],body:[0,[[13,\"pa\",[6,\"std.clamp\",[[7,\"p\",\"w\"],[5,\"0\"],[5,\"1\"]]]],[13,\"outA\",[1,\"pa\",\"+\",[1,[7,\"c\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"pa\"]]]],[13,\"rgb\",[6,[7,[6,[7,[6,[7,[7,\"p\",\"xyz\"],\"mul\"],[\"pa\"]],\"add\"],[[6,[7,[7,\"c\",\"xyz\"],\"mul\"],[[1,[7,\"c\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",\"pa\"]]]]]],\"div\"],[[6,\"std.max\",[\"outA\",[5,\"0.0001\"]]]]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"outA\"]]]]]},externals:{\"std.clamp\":()=>G,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"agentStraightAlphaOver\"),TP=w.fn([m,m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=Se(s,t);return N(t.mul(__tsover_mul(2,o)).sub(s),t,r>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rhat\"},{type:\"i\",name:\"axis\"},{type:\"i\",name:\"fieldType\"}],body:[0,[[13,\"mdotr\",[6,\"std.dot\",[\"axis\",\"rhat\"]]],[13,\"dipoleB\",[6,[7,[6,[7,\"rhat\",\"mul\"],[[1,[5,\"2\"],\"*\",\"mdotr\"]]],\"sub\"],[\"axis\"]]],[10,[6,\"std.select\",[\"dipoleB\",\"rhat\",[1,\"fieldType\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.dot\":()=>Se,\"std.select\":()=>N}})&&e.f)({})).$name(\"agentDipoleOrRadialField\"),Jv=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";return __tsover_mul(t,ae(__tsover_mul(2,__tsover_sub(s,r))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"k\"},{type:\"i\",name:\"target\"},{type:\"i\",name:\"theta\"}],body:[0,[[10,[1,\"k\",\"*\",[6,\"std.sin\",[[1,[5,\"2\"],\"*\",[1,\"target\",\"-\",\"theta\"]]]]]]]]},externals:{\"std.sin\":()=>ae}})&&e.f)({})).$name(\"agentNematicTorque\"),SP=w.fn([S,n,S,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(r.w,o),i=s>=a,l=B(s,a),c=N(A(r.x,r.y,r.z),A(t.x,t.y,t.z),i);return S(c.x,c.y,c.z,l)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"col\"},{type:\"i\",name:\"curA\"},{type:\"i\",name:\"prev\"},{type:\"i\",name:\"trails\"}],body:[0,[[13,\"prevA\",[1,[7,\"prev\",\"w\"],\"*\",\"trails\"]],[13,\"win\",[1,\"curA\",\">=\",\"prevA\"]],[13,\"outA\",[6,\"std.max\",[\"curA\",\"prevA\"]]],[13,\"outRGB\",[6,\"std.select\",[[6,\"d.vec3f\",[[7,\"prev\",\"x\"],[7,\"prev\",\"y\"],[7,\"prev\",\"z\"]]],[6,\"d.vec3f\",[[7,\"col\",\"x\"],[7,\"col\",\"y\"],[7,\"col\",\"z\"]]],\"win\"]]],[10,[6,\"d.vec4f\",[[7,\"outRGB\",\"x\"],[7,\"outRGB\",\"y\"],[7,\"outRGB\",\"z\"],\"outA\"]]]]]},externals:{\"std.max\":()=>B,\"std.select\":()=>N,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"agentTrailMaxDecay\"),eh=w.fn([A,S,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return A(__tsover_add(__tsover_add(__tsover_mul(s.x,t.x),__tsover_mul(s.y,t.y)),__tsover_mul(s.z,t.z)),__tsover_add(__tsover_add(__tsover_mul(r.x,t.x),__tsover_mul(r.y,t.y)),__tsover_mul(r.z,t.z)),__tsover_add(__tsover_add(__tsover_mul(o.x,t.x),__tsover_mul(o.y,t.y)),__tsover_mul(o.z,t.z)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pw\"},{type:\"i\",name:\"rowX\"},{type:\"i\",name:\"rowY\"},{type:\"i\",name:\"rowZ\"}],body:[0,[[10,[6,\"d.vec3f\",[[1,[1,[1,[7,\"rowX\",\"x\"],\"*\",[7,\"pw\",\"x\"]],\"+\",[1,[7,\"rowX\",\"y\"],\"*\",[7,\"pw\",\"y\"]]],\"+\",[1,[7,\"rowX\",\"z\"],\"*\",[7,\"pw\",\"z\"]]],[1,[1,[1,[7,\"rowY\",\"x\"],\"*\",[7,\"pw\",\"x\"]],\"+\",[1,[7,\"rowY\",\"y\"],\"*\",[7,\"pw\",\"y\"]]],\"+\",[1,[7,\"rowY\",\"z\"],\"*\",[7,\"pw\",\"z\"]]],[1,[1,[1,[7,\"rowZ\",\"x\"],\"*\",[7,\"pw\",\"x\"]],\"+\",[1,[7,\"rowZ\",\"y\"],\"*\",[7,\"pw\",\"y\"]]],\"+\",[1,[7,\"rowZ\",\"z\"],\"*\",[7,\"pw\",\"z\"]]]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})).$name(\"agentApplyCameraRows\"),PP=w.fn([A,S,S,S],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";return A(__tsover_add(__tsover_add(__tsover_mul(s.x,t.x),__tsover_mul(r.x,t.y)),__tsover_mul(o.x,t.z)),__tsover_add(__tsover_add(__tsover_mul(s.y,t.x),__tsover_mul(r.y,t.y)),__tsover_mul(o.y,t.z)),__tsover_add(__tsover_add(__tsover_mul(s.z,t.x),__tsover_mul(r.z,t.y)),__tsover_mul(o.z,t.z)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"},{type:\"i\",name:\"rowX\"},{type:\"i\",name:\"rowY\"},{type:\"i\",name:\"rowZ\"}],body:[0,[[10,[6,\"d.vec3f\",[[1,[1,[1,[7,\"rowX\",\"x\"],\"*\",[7,\"v\",\"x\"]],\"+\",[1,[7,\"rowY\",\"x\"],\"*\",[7,\"v\",\"y\"]]],\"+\",[1,[7,\"rowZ\",\"x\"],\"*\",[7,\"v\",\"z\"]]],[1,[1,[1,[7,\"rowX\",\"y\"],\"*\",[7,\"v\",\"x\"]],\"+\",[1,[7,\"rowY\",\"y\"],\"*\",[7,\"v\",\"y\"]]],\"+\",[1,[7,\"rowZ\",\"y\"],\"*\",[7,\"v\",\"z\"]]],[1,[1,[1,[7,\"rowX\",\"z\"],\"*\",[7,\"v\",\"x\"]],\"+\",[1,[7,\"rowY\",\"z\"],\"*\",[7,\"v\",\"y\"]]],\"+\",[1,[7,\"rowZ\",\"z\"],\"*\",[7,\"v\",\"z\"]]]]]]]]},externals:{\"d.vec3f\":()=>A}})&&e.f)({})).$name(\"agentApplyCameraRowsT\"),q0=gt({x0:M,x1:M,y0:M,y1:M,fx:n,fy:n}).$name(\"AgentBilinearFrame\"),th=w.fn([m,m],q0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_sub(__tsover_mul(t.x,s.x),.5),o=__tsover_sub(__tsover_mul(t.y,s.y),.5),a=ie(r),i=ie(o);return q0({x0:M(G(a,n(0),__tsover_sub(s.x,1))),x1:M(G(__tsover_add(a,1),n(0),__tsover_sub(s.x,1))),y0:M(G(i,n(0),__tsover_sub(s.y,1))),y1:M(G(__tsover_add(i,1),n(0),__tsover_sub(s.y,1))),fx:__tsover_sub(r,a),fy:__tsover_sub(o,i)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv01\"},{type:\"i\",name:\"dims\"}],body:[0,[[13,\"cu\",[1,[1,[7,\"uv01\",\"x\"],\"*\",[7,\"dims\",\"x\"]],\"-\",[5,\"0.5\"]]],[13,\"cv\",[1,[1,[7,\"uv01\",\"y\"],\"*\",[7,\"dims\",\"y\"]],\"-\",[5,\"0.5\"]]],[13,\"cx0\",[6,\"std.floor\",[\"cu\"]]],[13,\"cy0\",[6,\"std.floor\",[\"cv\"]]],[10,[6,\"BilinearFrame\",[[104,{x0:[6,\"d.u32\",[[6,\"std.clamp\",[\"cx0\",[6,\"d.f32\",[[5,\"0\"]]],[1,[7,\"dims\",\"x\"],\"-\",[5,\"1\"]]]]]],x1:[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cx0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,[7,\"dims\",\"x\"],\"-\",[5,\"1\"]]]]]],y0:[6,\"d.u32\",[[6,\"std.clamp\",[\"cy0\",[6,\"d.f32\",[[5,\"0\"]]],[1,[7,\"dims\",\"y\"],\"-\",[5,\"1\"]]]]]],y1:[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cy0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,[7,\"dims\",\"y\"],\"-\",[5,\"1\"]]]]]],fx:[1,\"cu\",\"-\",\"cx0\"],fy:[1,\"cv\",\"-\",\"cy0\"]}]]]]]]},externals:{\"std.floor\":()=>ie,BilinearFrame:()=>q0,\"d.u32\":()=>M,\"std.clamp\":()=>G,\"d.f32\":()=>n}})&&e.f)({})).$name(\"agentBilinearTexelFrame\"),sh=w.fn([m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Se(t,t);return Tt(__tsover_mul(__tsover_div(r,B(s,1e-5)),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"delta\"},{type:\"i\",name:\"radSq\"}],body:[0,[[13,\"dl\",[6,\"std.dot\",[\"delta\",\"delta\"]]],[10,[6,\"std.exp\",[[1,[1,\"dl\",\"/\",[6,\"std.max\",[\"radSq\",[5,\"0.00001\"]]]],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"std.exp\":()=>Tt,\"std.max\":()=>B}})&&e.f)({})).$name(\"agentCursorFalloff\"),Xu=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(he(t),1e-4);return t.div(o).mul(__tsover_mul(sh(t,s),r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"delta\"},{type:\"i\",name:\"radSq\"},{type:\"i\",name:\"force\"}],body:[0,[[13,\"dl\",[6,\"std.max\",[[6,\"std.length\",[\"delta\"]],[5,\"0.0001\"]]]],[10,[6,[7,[6,[7,\"delta\",\"div\"],[\"dl\"]],\"mul\"],[[1,[6,\"cursorFalloff\",[\"delta\",\"radSq\"]],\"*\",\"force\"]]]]]]},externals:{\"std.max\":()=>B,\"std.length\":()=>he,cursorFalloff:()=>sh}})&&e.f)({})).$name(\"agentCursorMagnet\");function eg(e,t){return w.fn([M])((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=e.$.params,a=n(r),i=ei(A(__tsover_add(__tsover_mul(a,1.6180339),.71),__tsover_add(__tsover_mul(a,2.7182818),4.17),__tsover_add(__tsover_mul(a,3.1415926),9.03)));e.$.agents[r]=S(__tsover_mul(i.x,o.aspect),i.y,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"r\",[6,\"noise.hash33\",[[6,\"d.vec3f\",[[1,[1,\"fi\",\"*\",[5,\"1.6180339\"]],\"+\",[5,\"0.71\"]],[1,[1,\"fi\",\"*\",[5,\"2.7182818\"]],\"+\",[5,\"4.17\"]],[1,[1,\"fi\",\"*\",[5,\"3.1415926\"]],\"+\",[5,\"9.03\"]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"r\",\"x\"],\"*\",[7,\"prm\",\"aspect\"]],[7,\"r\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,\"noise.hash33\":()=>ei,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&s.f)({})).$name(t)}function uz(e,t,s){let r=s?.radius??.55,o=s?.zSquash??.8;return w.fn([M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=(i=>{\"use gpu\";let l=n(i),c=vs(ei(A(__tsover_add(__tsover_mul(l,1.6180339),.71),__tsover_add(__tsover_mul(l,2.7182818),4.17),__tsover_add(__tsover_mul(l,3.1415926),9.03))).sub(A(.5,.5,.5)).add(A(1e-4,2e-4,3e-4))),d=__tsover_mul(vt(Xs(__tsover_add(__tsover_mul(l,2.2360679),1.7)),n(.3333)),r),u=Xs(__tsover_add(__tsover_mul(l,1.7320508),.37));e.$.pos[i]=S(__tsover_mul(c.x,d),__tsover_mul(c.y,d),__tsover_mul(__tsover_mul(c.z,d),o),u),e.$.vel[i]=S(0,0,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"h3\",[6,\"noise.hash33\",[[6,\"d.vec3f\",[[1,[1,\"fi\",\"*\",[5,\"1.6180339\"]],\"+\",[5,\"0.71\"]],[1,[1,\"fi\",\"*\",[5,\"2.7182818\"]],\"+\",[5,\"4.17\"]],[1,[1,\"fi\",\"*\",[5,\"3.1415926\"]],\"+\",[5,\"9.03\"]]]]]]],[13,\"dir\",[6,\"std.normalize\",[[6,[7,[6,[7,\"h3\",\"sub\"],[[6,\"d.vec3f\",[[5,\"0.5\"],[5,\"0.5\"],[5,\"0.5\"]]]]],\"add\"],[[6,\"d.vec3f\",[[5,\"0.0001\"],[5,\"0.0002\"],[5,\"0.0003\"]]]]]]]],[13,\"rad\",[1,[6,\"std.pow\",[[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"2.2360679\"]],\"+\",[5,\"1.7\"]]]],[6,\"d.f32\",[[5,\"0.3333\"]]]]],\"*\",\"RAD\"]],[13,\"seed\",[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"1.7320508\"]],\"+\",[5,\"0.37\"]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"dir\",\"x\"],\"*\",\"rad\"],[1,[7,\"dir\",\"y\"],\"*\",\"rad\"],[1,[1,[7,\"dir\",\"z\"],\"*\",\"rad\"],\"*\",\"ZS\"],\"seed\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"],\"=\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{\"d.f32\":()=>n,\"noise.hash33\":()=>ei,\"d.vec3f\":()=>A,\"std.normalize\":()=>vs,\"std.pow\":()=>vt,\"noise.hash11\":()=>Xs,RAD:()=>r,layout:()=>e,\"d.vec4f\":()=>S,ZS:()=>o}})&&a.f)({})).$name(t)}function pz(e,t,s){let r=s?.agentsPerCluster??250,o=s?.minClusters??3,a=6.283185307179586;return w.fn([M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=(l=>{\"use gpu\";let c=e.$.params,d=n(l),u=__tsover_mul(c.seed,13.137),p=B(ie(__tsover_div(c.count,n(r))),n(o)),_=__tsover_sub(d,__tsover_mul(ie(__tsover_div(d,p)),p)),f=ei(A(__tsover_add(__tsover_add(__tsover_mul(_,7.1315),u),.71),__tsover_add(__tsover_add(__tsover_mul(_,3.7719),u),4.17),__tsover_add(__tsover_add(__tsover_mul(_,9.4103),u),9.03))),h=__tsover_mul(f.z,a),g=m(pe(h),ae(h)),y=m(__tsover_mul(__tsover_add(.12,__tsover_mul(f.x,.76)),c.domainX),__tsover_add(.12,__tsover_mul(f.y,.76))),b=ei(A(__tsover_add(__tsover_add(__tsover_mul(d,1.6180339),u),.71),__tsover_add(__tsover_add(__tsover_mul(d,2.7182818),u),4.17),__tsover_add(__tsover_add(__tsover_mul(d,3.1415926),u),9.03))),x=__tsover_mul(__tsover_sub(b.x,.5),.6),P=__tsover_mul(__tsover_sub(b.y,.5),.16),C=G(__tsover_sub(__tsover_add(y.x,__tsover_mul(g.x,x)),__tsover_mul(g.y,P)),0,c.domainX),E=G(__tsover_add(__tsover_add(y.y,__tsover_mul(g.y,x)),__tsover_mul(g.x,P)),0,1),R=__tsover_add(h,__tsover_mul(__tsover_sub(b.z,.5),.5)),U=__tsover_mul(c.maxSpeed,__tsover_add(.75,__tsover_mul(b.y,.25)));e.$.agents[l]=S(C,E,__tsover_mul(pe(R),U),__tsover_mul(ae(R),U)),e.$.agit[l]=n(0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"sd\",[1,[7,\"prm\",\"seed\"],\"*\",[5,\"13.137\"]]],[13,\"flocks\",[6,\"std.max\",[[6,\"std.floor\",[[1,[7,\"prm\",\"count\"],\"/\",[6,\"d.f32\",[\"PER\"]]]]],[6,\"d.f32\",[\"MIN\"]]]]],[13,\"c\",[1,\"fi\",\"-\",[1,[6,\"std.floor\",[[1,\"fi\",\"/\",\"flocks\"]]],\"*\",\"flocks\"]]],[13,\"rc\",[6,\"noise.hash33\",[[6,\"d.vec3f\",[[1,[1,[1,\"c\",\"*\",[5,\"7.1315\"]],\"+\",\"sd\"],\"+\",[5,\"0.71\"]],[1,[1,[1,\"c\",\"*\",[5,\"3.7719\"]],\"+\",\"sd\"],\"+\",[5,\"4.17\"]],[1,[1,[1,\"c\",\"*\",[5,\"9.4103\"]],\"+\",\"sd\"],\"+\",[5,\"9.03\"]]]]]]],[13,\"ang\",[1,[7,\"rc\",\"z\"],\"*\",\"TAU\"]],[13,\"hd\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"ang\"]],[6,\"std.sin\",[\"ang\"]]]]],[13,\"center\",[6,\"d.vec2f\",[[1,[1,[5,\"0.12\"],\"+\",[1,[7,\"rc\",\"x\"],\"*\",[5,\"0.76\"]]],\"*\",[7,\"prm\",\"domainX\"]],[1,[5,\"0.12\"],\"+\",[1,[7,\"rc\",\"y\"],\"*\",[5,\"0.76\"]]]]]],[13,\"ra\",[6,\"noise.hash33\",[[6,\"d.vec3f\",[[1,[1,[1,\"fi\",\"*\",[5,\"1.6180339\"]],\"+\",\"sd\"],\"+\",[5,\"0.71\"]],[1,[1,[1,\"fi\",\"*\",[5,\"2.7182818\"]],\"+\",\"sd\"],\"+\",[5,\"4.17\"]],[1,[1,[1,\"fi\",\"*\",[5,\"3.1415926\"]],\"+\",\"sd\"],\"+\",[5,\"9.03\"]]]]]]],[13,\"t\",[1,[1,[7,\"ra\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.6\"]]],[13,\"n\",[1,[1,[7,\"ra\",\"y\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.16\"]]],[13,\"px\",[6,\"std.clamp\",[[1,[1,[7,\"center\",\"x\"],\"+\",[1,[7,\"hd\",\"x\"],\"*\",\"t\"]],\"-\",[1,[7,\"hd\",\"y\"],\"*\",\"n\"]],[5,\"0\"],[7,\"prm\",\"domainX\"]]]],[13,\"py\",[6,\"std.clamp\",[[1,[1,[7,\"center\",\"y\"],\"+\",[1,[7,\"hd\",\"y\"],\"*\",\"t\"]],\"+\",[1,[7,\"hd\",\"x\"],\"*\",\"n\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"jAng\",[1,\"ang\",\"+\",[1,[1,[7,\"ra\",\"z\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"0.5\"]]]],[13,\"sp\",[1,[7,\"prm\",\"maxSpeed\"],\"*\",[1,[5,\"0.75\"],\"+\",[1,[7,\"ra\",\"y\"],\"*\",[5,\"0.25\"]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[\"px\",\"py\",[1,[6,\"std.cos\",[\"jAng\"]],\"*\",\"sp\"],[1,[6,\"std.sin\",[\"jAng\"]],\"*\",\"sp\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"=\",[6,\"d.f32\",[[5,\"0\"]]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,\"std.max\":()=>B,\"std.floor\":()=>ie,PER:()=>r,MIN:()=>o,\"noise.hash33\":()=>ei,\"d.vec3f\":()=>A,TAU:()=>a,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&i.f)({})).$name(t)}function mz(e,t){let s=3.141592653589793;return w.fn([M])((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=(o=>{\"use gpu\";let a=n(o),i=__tsover_mul(Xs(__tsover_add(__tsover_mul(a,1.618034),.31)),s);e.$.agents[o]=S(n(0),n(0),i,n(0)),e.$.agit[o]=n(0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"theta0\",[1,[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"1.618034\"]],\"+\",[5,\"0.31\"]]]],\"*\",\"PI\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"0\"]]],\"theta0\",[6,\"d.f32\",[[5,\"0\"]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"=\",[6,\"d.f32\",[[5,\"0\"]]]]]]},externals:{\"d.f32\":()=>n,\"noise.hash11\":()=>Xs,PI:()=>s,layout:()=>e,\"d.vec4f\":()=>S}})&&r.f)({})).$name(t)}function _z(e){let t=e.maxPoints,s=e.subdivisions;return w.fn([Zt(n),m,n,n,n,n,n],m)((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a,i,l,c,d,u)=>{\"use gpu\";let p=__tsover_mul(a.x,i),_=a.y,f=__tsover_mul(l,.1),h=__tsover_add(__tsover_div(1.5,B(u,1)),__tsover_mul(d,f)),g=__tsover_div(4,B(f,.005)),y=n(100),b=n(0),x=n(0),P=S(0,0,1,0),C=S(0,0,1,0);for(let R=0;R<t;R++){let U=Ye(o,fe(M(R),0),0);if(U.z<1){let L=__tsover_mul(U.x,i),k=U.y,D=N(n(0),n(1),U.w>0),O=N(n(0),n(1),P.z<1);if(__tsover_mul(D,O)>.5){let F=__tsover_mul(N(n(0),n(1),P.w>0),N(n(0),n(1),C.z<1))>.5,q=__tsover_mul(P.x,i),Y=P.y,V=N(q,__tsover_mul(__tsover_add(__tsover_mul(C.x,i),q),.5),F),X=N(Y,__tsover_mul(__tsover_add(C.y,Y),.5),F),Q=N(P.z,__tsover_mul(__tsover_add(C.z,P.z),.5),F),te=re(P.w),J=N(te,__tsover_mul(__tsover_add(re(C.w),te),.5),F),oe=__tsover_mul(__tsover_add(q,L),.5),le=__tsover_mul(__tsover_add(Y,k),.5),se=__tsover_mul(__tsover_add(P.z,U.z),.5),Te=__tsover_mul(__tsover_add(te,re(U.w)),.5),de=V,xe=X,Ae=Q,De=J,Ee=n(100),ze=n(1);for(let ke=0;ke<s;ke++){let Me=__tsover_div(__tsover_add(n(ke),1),s),we=__tsover_sub(1,Me),Ve=__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(we,we),V),__tsover_mul(__tsover_mul(__tsover_mul(2,we),Me),q)),__tsover_mul(__tsover_mul(Me,Me),oe)),qe=__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(we,we),X),__tsover_mul(__tsover_mul(__tsover_mul(2,we),Me),Y)),__tsover_mul(__tsover_mul(Me,Me),le)),st=K(Q,se,Me),pt=K(J,Te,Me),Ze=__tsover_mul(__tsover_mul(f,De),__tsover_sub(1,__tsover_mul(Ae,c))),nt=__tsover_mul(__tsover_mul(f,pt),__tsover_sub(1,__tsover_mul(st,c))),ht=__tsover_sub(Ve,de),yt=__tsover_sub(qe,xe),St=G(__tsover_div(__tsover_add(__tsover_mul(__tsover_sub(p,de),ht),__tsover_mul(__tsover_sub(_,xe),yt)),B(__tsover_add(__tsover_mul(ht,ht),__tsover_mul(yt,yt)),1e-8)),0,1),Mt=__tsover_sub(he(m(__tsover_sub(p,__tsover_add(de,__tsover_mul(ht,St))),__tsover_sub(_,__tsover_add(xe,__tsover_mul(yt,St))))),K(Ze,nt,St));Mt<Ee&&(Ee=Mt,ze=K(Ae,st,St)),de=Ve,xe=qe,Ae=st,De=pt}y=ye(y,Ee);let Ie=Tt(__tsover_mul(__tsover_mul(Ee,-1),g));b=__tsover_add(b,__tsover_mul(ze,Ie)),x=__tsover_add(x,Ie)}else{let F=__tsover_sub(he(m(__tsover_sub(p,L),__tsover_sub(_,k))),__tsover_mul(__tsover_mul(f,re(U.w)),__tsover_sub(1,__tsover_mul(U.z,c))));y=ye(y,F);let q=Tt(__tsover_mul(__tsover_mul(F,-1),g));b=__tsover_add(b,__tsover_mul(U.z,q)),x=__tsover_add(x,q)}}C=S(P),P=S(U)}let E=__tsover_div(b,B(x,1e-20));return m(E,__tsover_mul(__tsover_sub(1,be(__tsover_mul(h,-1),h,y)),__tsover_sub(1,be(.8,1,E))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pointsTex\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"shrink\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"viewportH\"}],body:[0,[[13,\"px\",[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"]],[13,\"py\",[7,\"uv\",\"y\"]],[13,\"baseRadius\",[1,\"radius\",\"*\",[5,\"0.1\"]]],[13,\"edgeW\",[1,[1,[5,\"1.5\"],\"/\",[6,\"std.max\",[\"viewportH\",[5,\"1\"]]]],\"+\",[1,\"softness\",\"*\",\"baseRadius\"]]],[13,\"sharp\",[1,[5,\"4\"],\"/\",[6,\"std.max\",[\"baseRadius\",[5,\"0.005\"]]]]],[12,\"minSdf\",[6,\"d.f32\",[[5,\"100\"]]]],[12,\"ageSum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"wSum\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"prev\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"1\"],[5,\"0\"]]]],[12,\"prev2\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"1\"],[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"MAX_POINTS\"],[102,\"++\",\"i\"],[0,[[13,\"cur\",[6,\"std.textureLoad\",[\"pointsTex\",[6,\"d.vec2u\",[[6,\"d.u32\",[\"i\"]],[5,\"0\"]]],[5,\"0\"]]]],[11,[1,[7,\"cur\",\"z\"],\"<\",[5,\"1\"]],[0,[[13,\"cx\",[1,[7,\"cur\",\"x\"],\"*\",\"aspect\"]],[13,\"cy\",[7,\"cur\",\"y\"]],[13,\"linked\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"cur\",\"w\"],\">\",[5,\"0\"]]]]],[13,\"prevAlive\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"prev\",\"z\"],\"<\",[5,\"1\"]]]]],[11,[1,[1,\"linked\",\"*\",\"prevAlive\"],\">\",[5,\"0.5\"]],[0,[[13,\"usePrev2\",[1,[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"prev\",\"w\"],\">\",[5,\"0\"]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"prev2\",\"z\"],\"<\",[5,\"1\"]]]]],\">\",[5,\"0.5\"]]],[13,\"jx\",[1,[7,\"prev\",\"x\"],\"*\",\"aspect\"]],[13,\"jy\",[7,\"prev\",\"y\"]],[13,\"sx0\",[6,\"std.select\",[\"jx\",[1,[1,[1,[7,\"prev2\",\"x\"],\"*\",\"aspect\"],\"+\",\"jx\"],\"*\",[5,\"0.5\"]],\"usePrev2\"]]],[13,\"sy0\",[6,\"std.select\",[\"jy\",[1,[1,[7,\"prev2\",\"y\"],\"+\",\"jy\"],\"*\",[5,\"0.5\"]],\"usePrev2\"]]],[13,\"ageS\",[6,\"std.select\",[[7,\"prev\",\"z\"],[1,[1,[7,\"prev2\",\"z\"],\"+\",[7,\"prev\",\"z\"]],\"*\",[5,\"0.5\"]],\"usePrev2\"]]],[13,\"widPrev\",[6,\"std.abs\",[[7,\"prev\",\"w\"]]]],[13,\"widS\",[6,\"std.select\",[\"widPrev\",[1,[1,[6,\"std.abs\",[[7,\"prev2\",\"w\"]]],\"+\",\"widPrev\"],\"*\",[5,\"0.5\"]],\"usePrev2\"]]],[13,\"ex\",[1,[1,\"jx\",\"+\",\"cx\"],\"*\",[5,\"0.5\"]]],[13,\"ey\",[1,[1,\"jy\",\"+\",\"cy\"],\"*\",[5,\"0.5\"]]],[13,\"ageE\",[1,[1,[7,\"prev\",\"z\"],\"+\",[7,\"cur\",\"z\"]],\"*\",[5,\"0.5\"]]],[13,\"widE\",[1,[1,\"widPrev\",\"+\",[6,\"std.abs\",[[7,\"cur\",\"w\"]]]],\"*\",[5,\"0.5\"]]],[12,\"sx\",\"sx0\"],[12,\"sy\",\"sy0\"],[12,\"sAge\",\"ageS\"],[12,\"sWid\",\"widS\"],[12,\"jMin\",[6,\"d.f32\",[[5,\"100\"]]]],[12,\"jAge\",[6,\"d.f32\",[[5,\"1\"]]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",\"SUBDIVISIONS\"],[102,\"++\",\"k\"],[0,[[13,\"t\",[1,[1,[6,\"d.f32\",[\"k\"]],\"+\",[5,\"1\"]],\"/\",\"SUBDIVISIONS\"]],[13,\"omt\",[1,[5,\"1\"],\"-\",\"t\"]],[13,\"bx\",[1,[1,[1,[1,\"omt\",\"*\",\"omt\"],\"*\",\"sx0\"],\"+\",[1,[1,[1,[5,\"2\"],\"*\",\"omt\"],\"*\",\"t\"],\"*\",\"jx\"]],\"+\",[1,[1,\"t\",\"*\",\"t\"],\"*\",\"ex\"]]],[13,\"by\",[1,[1,[1,[1,\"omt\",\"*\",\"omt\"],\"*\",\"sy0\"],\"+\",[1,[1,[1,[5,\"2\"],\"*\",\"omt\"],\"*\",\"t\"],\"*\",\"jy\"]],\"+\",[1,[1,\"t\",\"*\",\"t\"],\"*\",\"ey\"]]],[13,\"bAge\",[6,\"std.mix\",[\"ageS\",\"ageE\",\"t\"]]],[13,\"bWid\",[6,\"std.mix\",[\"widS\",\"widE\",\"t\"]]],[13,\"ra\",[1,[1,\"baseRadius\",\"*\",\"sWid\"],\"*\",[1,[5,\"1\"],\"-\",[1,\"sAge\",\"*\",\"shrink\"]]]],[13,\"rb\",[1,[1,\"baseRadius\",\"*\",\"bWid\"],\"*\",[1,[5,\"1\"],\"-\",[1,\"bAge\",\"*\",\"shrink\"]]]],[13,\"bax\",[1,\"bx\",\"-\",\"sx\"]],[13,\"bay\",[1,\"by\",\"-\",\"sy\"]],[13,\"tt\",[6,\"std.clamp\",[[1,[1,[1,[1,\"px\",\"-\",\"sx\"],\"*\",\"bax\"],\"+\",[1,[1,\"py\",\"-\",\"sy\"],\"*\",\"bay\"]],\"/\",[6,\"std.max\",[[1,[1,\"bax\",\"*\",\"bax\"],\"+\",[1,\"bay\",\"*\",\"bay\"]],[5,\"1e-8\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"dist\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,\"px\",\"-\",[1,\"sx\",\"+\",[1,\"bax\",\"*\",\"tt\"]]],[1,\"py\",\"-\",[1,\"sy\",\"+\",[1,\"bay\",\"*\",\"tt\"]]]]]]],\"-\",[6,\"std.mix\",[\"ra\",\"rb\",\"tt\"]]]],[11,[1,\"dist\",\"<\",\"jMin\"],[0,[[2,\"jMin\",\"=\",\"dist\"],[2,\"jAge\",\"=\",[6,\"std.mix\",[\"sAge\",\"bAge\",\"tt\"]]]]]],[2,\"sx\",\"=\",\"bx\"],[2,\"sy\",\"=\",\"by\"],[2,\"sAge\",\"=\",\"bAge\"],[2,\"sWid\",\"=\",\"bWid\"]]]],[2,\"minSdf\",\"=\",[6,\"std.min\",[\"minSdf\",\"jMin\"]]],[13,\"w\",[6,\"std.exp\",[[1,[1,\"jMin\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"sharp\"]]]],[2,\"ageSum\",\"=\",[1,\"ageSum\",\"+\",[1,\"jAge\",\"*\",\"w\"]]],[2,\"wSum\",\"=\",[1,\"wSum\",\"+\",\"w\"]]]],[0,[[13,\"dist\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,\"px\",\"-\",\"cx\"],[1,\"py\",\"-\",\"cy\"]]]]],\"-\",[1,[1,\"baseRadius\",\"*\",[6,\"std.abs\",[[7,\"cur\",\"w\"]]]],\"*\",[1,[5,\"1\"],\"-\",[1,[7,\"cur\",\"z\"],\"*\",\"shrink\"]]]]],[2,\"minSdf\",\"=\",[6,\"std.min\",[\"minSdf\",\"dist\"]]],[13,\"w\",[6,\"std.exp\",[[1,[1,\"dist\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"sharp\"]]]],[2,\"ageSum\",\"=\",[1,\"ageSum\",\"+\",[1,[7,\"cur\",\"z\"],\"*\",\"w\"]]],[2,\"wSum\",\"=\",[1,\"wSum\",\"+\",\"w\"]]]]]]]],[2,\"prev2\",\"=\",[6,\"d.vec4f\",[\"prev\"]]],[2,\"prev\",\"=\",[6,\"d.vec4f\",[\"cur\"]]]]]],[13,\"age\",[1,\"ageSum\",\"/\",[6,\"std.max\",[\"wSum\",[5,\"1e-20\"]]]]],[13,\"coverage\",[1,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"edgeW\",\"*\",[4,\"-\",[5,\"1\"]]],\"edgeW\",\"minSdf\"]]],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0.8\"],[5,\"1\"],\"age\"]]]]],[10,[6,\"d.vec2f\",[\"age\",\"coverage\"]]]]]},externals:{\"std.max\":()=>B,\"d.f32\":()=>n,\"d.vec4f\":()=>S,MAX_POINTS:()=>t,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"std.select\":()=>N,\"std.abs\":()=>re,SUBDIVISIONS:()=>s,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.min\":()=>ye,\"std.exp\":()=>Tt,\"std.smoothstep\":()=>be}})&&r.f)({})).$name(e.name)}var hl=e=>e;function Ms(){return{default:null,transform:hl,compileTimeWhen:(e,t)=>{let s=r=>{let o=Array.isArray(r)?r.length:0;return o>1?o:0};return s(e)!==s(t)},description:\"Multi-stop gradient colors (overrides Color A / Color B when set)\",ui:{type:\"gradient-stops\",label:\"Colors\",group:\"Colors\"}}}var _c=e=>e;function fz(e,t){let s={type:\"list\",label:t.label,group:t.group,item:e.item,maxItems:e.maxItems,minItems:e.minItems??0,itemLabel:e.itemLabel};return{default:t.default,transform:_c,description:t.description,ui:s}}function Wne(e){let t=e?.ui;return!t||t.type!==\"list\"||!t.item||typeof t.maxItems!=\"number\"?null:{item:t.item,maxItems:t.maxItems,minItems:t.minItems,itemLabel:t.itemLabel}}var tg=(e,t)=>`${e}_${t}`,sg=e=>`${e}Count`;function rg(e){return!!e&&typeof e==\"object\"&&e.type===\"mouse-position\"}var vz=(e,t,s)=>`${e}.${t}.${s}`;function FA(e){let t=/^([^.]+)\\.(\\d+)\\.([^.]+)$/.exec(e);return t?{prop:t[1],index:Number(t[2]),field:t[3]}:null}function IA(e,t,s){return(Array.isArray(e)?e.slice(0,t.maxItems):[]).map((r,o)=>{let a=r&&typeof r==\"object\"?r:{},i={};for(let[l,c]of Object.entries(t.item)){let d=a[l]===void 0||a[l]===null?c.default:a[l];i[l]=qne(c.kind,d,o,l,s)}return i})}function qne(e,t,s,r,o){switch(e){case\"position\":{if(rg(t)){let i=o.drivenPosition?.(s,r,t);return i||{x:typeof t.x==\"number\"?t.x:t.originX??.5,y:1-(typeof t.y==\"number\"?t.y:t.originY??.5)}}let a=Xe(t);return{x:a.x,y:a.y}}case\"color\":return o.color(typeof t==\"string\"?t:\"#ffffff\");case\"boolean\":return t?1:-1;default:return typeof t==\"number\"?t:0}}function Vne(e){return typeof e==\"number\"?[e,0,0,0]:Array.isArray(e)?e:[e.x,e.y,0,0]}function YA(e,t){let s={};for(let r of Object.keys(t.item)){let o=new Array(t.maxItems*4).fill(0);for(let a=0;a<Math.min(e.length,t.maxItems);a++){let i=Vne(e[a][r]);o[a*4]=i[0],o[a*4+1]=i[1],o[a*4+2]=i[2],o[a*4+3]=i[3]}s[r]=o}return{count:e.length,fields:s}}function WA(e,t){return Array.isArray(e)?e.some(s=>s&&typeof s==\"object\"&&Object.entries(t.item).some(([r,o])=>o.kind===\"position\"&&rg(s[r]))):!1}var hz=\"p3-linear\";function gz(e){hz=e;try{Aw(e)}catch{}}function Nne(e){try{let t=new va(e),s=hz===\"srgb\"?\"srgb-linear\":\"p3-linear\",r=t.to(s).coords,o=Number(t.alpha);return{r:Number.isFinite(r?.[0])?r[0]:0,g:Number.isFinite(r?.[1])?r[1]:0,b:Number.isFinite(r?.[2])?r[2]:0,a:Number.isFinite(o)?o:1}}catch{return{r:0,g:0,b:0,a:0}}}function yz(e){let{r:t,g:s,b:r,a:o}=Nne(e);return S(t,s,r,o)}function B_(e){let t=yz(e);return[t.x,t.y,t.z,t.w]}function CP(e,t){if(typeof e==\"number\")return e;if(typeof e==\"object\"&&e!==null)return e.value;let s=e.toLowerCase().trim();if(t){if(s===\"top\")return 0;if(s===\"bottom\")return 1;if(s===\"center\")return .5}else{if(s===\"left\")return 0;if(s===\"right\")return 1;if(s===\"center\")return .5}return console.warn(`Invalid position value: ${e}. Defaulting to center.`),.5}function $ne(e){let t=.5,s=.5;if(typeof e==\"string\"){let r=e.toLowerCase().trim().split(/\\s+/);r.includes(\"left\")?t=0:r.includes(\"right\")?t=1:(r.includes(\"center\")||r.length===1&&(r[0]===\"top\"||r[0]===\"bottom\"))&&(t=.5),r.includes(\"top\")?s=0:r.includes(\"bottom\")?s=1:(r.includes(\"center\")||r.length===1&&(r[0]===\"left\"||r[0]===\"right\"))&&(s=.5),r.length===1&&r[0]===\"center\"&&(t=.5,s=.5)}else typeof e==\"object\"&&e!==null?(t=CP(e.x,!1),s=CP(e.y,!0)):console.warn(`Invalid position value provided: ${e}. Defaulting to center.`);return m(t,1-s)}function bz(e){if(e)return e===ce?yz:e===Xe?$ne:e===hl?hl:e===_c?_c:e}function qA(e,t){e>0,t>0}function z_(e){return e!=null&&typeof e==\"object\"&&typeof e.value==\"number\"&&(e.unit===\"px\"||e.unit===\"uv\")}function VA(e){if(!e)return;let t;for(let[s,r]of Object.entries(e)){let o=r?.ui?.dimensional;(o===\"canvas-height\"||o===\"half-canvas-height\"||o===\"canvas-width\"||o===\"count-canvas-height\")&&(t??=new Map).set(s,o)}return t}function dp(e,t){let s=e?.propBindings,r=new Set,o=new Map;if(s)for(let a of[\"x\",\"y\",\"width\",\"height\"]){let i=s[a];i&&(i.as===\"position-x\"||i.as===\"position-y\"?r.add(i.prop):(i.as===\"canvas-height\"||i.as===\"half-canvas-height\")&&o.set(i.prop,i.as))}if(t)for(let[a,i]of t)o.set(a,i);return r.size>0||o.size>0?{positionProps:r,sizeProps:o}:null}var Xne=e=>({ox:e.includes(\"left\")?\"start\":e.includes(\"right\")?\"end\":\"center\",oy:e.includes(\"top\")?\"start\":e.includes(\"bottom\")?\"end\":\"center\"}),Hne=(e,t,s)=>t===\"start\"?e+s:t===\"end\"?e-s:e;function e_(e,t,s,r){let o=(a,i)=>z_(a)?a.unit===\"px\"?a.value:a.value*i:typeof a==\"number\"?a*i:0;return e.as===\"canvas-height\"?o(t,r):e.as===\"half-canvas-height\"?o(t,2*r):e.as===\"canvas-width\"?o(t,s):0}function NA(e,t,s,r){if(e?.computeBounds){let l=e.computeBounds(t,s,r);return{hwx:s>0?l.widthPx/s/2:0,hhy:r>0?l.heightPx/r/2:0}}let o=e?.propBindings,a=0,i=0;return o?.width&&s>0&&(a=e_(o.width,t[o.width.prop],s,r)/(2*s)),o?.height&&r>0&&(i=e_(o.height,t[o.height.prop],s,r)/(2*r)),{hwx:a,hhy:i}}function MP(e,t,s,r){let o;if(z_(e))if(e.unit===\"px\"){let a=t>0?e.value/t:0;o=s===\"end\"?1-a:s===\"center\"?.5+a:a}else o=e.value;else if(typeof e==\"number\")o=e;else return e;return Hne(o,s,r)}function jne(e,t,s,r){return e.unit!==\"px\"?e.value:t===\"canvas-width\"?s>0?e.value/s:e.value:r<=0?e.value:t===\"count-canvas-height\"?e.value>0?r/e.value:e.value:t===\"canvas-height\"?e.value/r:e.value/(2*r)}function R_(e,t,s,r,o,a,i,l){let c=dp(e,i);if(!c)return s;let{ox:d,oy:u}=Xne(r);if(c.positionProps.has(t))return!s||typeof s!=\"object\"?s:{...s,x:MP(s.x,o,d,l?.hwx??0),y:MP(s.y,a,u,l?.hhy??0)};let p=c.sizeProps.get(t);return p&&z_(s)?jne(s,p,o,a):s}function $A(e,t,s){let r=dp(e,s);return r?r.positionProps.has(t)||r.sizeProps.has(t):!1}function V0(e){let{body:t,build:s,args:r,compose:o,identity:a,missingChildMessage:i,setup:l,...c}=e;if(!t&&!s)throw new Error(`definePointwiseFilter(${e.name}): needs a body or a build`);return{...c,requiresChild:!0,fragment:u=>{if(!u.childNode)return i&&console.error(i),xt;let p=u;if(XA(a,p))return p.childNode;l?.(p);let _;if(s)_=s(p);else{let{fn:f,hint:h}=typeof t==\"function\"?t(p.propValues):t;_=W(f,h,[p.childNode,...r?.(p)??[]])}return o?o(_,p):_}}}function XA(e,t){if(!e)return!1;for(let s of e.props)if(t.getMapInfo?.(s))return!1;return e.when(t.propValues)}function EP(e){let{build:t,resultAlpha:s,identity:r,missingChildMessage:o,setup:a,...i}=e;return{...i,requiresRTT:!0,requiresChild:!0,fragment:c=>{if(!c.childNode)return o&&console.error(o),xt;let d=c.convertToTexture(c.childNode),u=f=>W(Yt,\"unpremultiplyAlpha\",[d.sample(f)]),p={...c,childNode:c.childNode,texture:d,sampleStraight:u};if(XA(r,c))return u(c.ctx.uv);a?.(p);let _=t(p);return s===\"straight\"?_:W(Yt,\"unpremultiplyAlpha\",[_])}}}function xz(e,t){return{map:(s,r)=>{let o=Px(e.map(s,r)),a=ea(s,o.uv,t(s,r));return o.coverage?{uv:a,coverage:o.coverage}:a}}}function Kne(e,t,s){return{map:(r,o)=>ea(e(r,o),t(r,o),s(r,o))}}function Px(e){return\"uv\"in e?e:{uv:e}}function Qne(e,t={}){let s=typeof e==\"function\"?e:()=>e,r=t.edges??\"prop\",o=a=>r===\"none\"?null:typeof r==\"number\"?r:a.edges??0;return{fragment:a=>{let{childNode:i,ctx:l,convertToTexture:c,propValues:d}=a;if(!i)return t.requireChildMessage&&console.error(t.requireChildMessage),xt;let u=c(i),p=o(d),_=Px(s(a).map(l.uv,l.aspect)),f=p===null?u.sample(_.uv):t.resample===\"bilinear\"?Ha(_.uv,h=>u.sample(h),p):T4(u,_.uv,p);return W(Yt,\"unpremultiplyAlpha\",[_.coverage?Ct(f.member(\"rgb\"),f.member(\"a\").mul(_.coverage)):f])},uvRemap:a=>{if(t.uvRemapIdentityWhen?.(a))return{uv:a.uv,mask:a.mask};let i=o(a.propValues),l=Px(s(a).map(a.uv,a.aspect)),c=i===null?{uv:l.uv,mask:a.mask}:_ee(l.uv,a.mask,i);return l.coverage?{uv:c.uv,mask:c.mask.mul(l.coverage)}:c}}}function fr(e,t,s){return{default:e,description:t,transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:s?.label??\"Edges\",group:s?.group??\"Effect\"}}}function zo(e,t){let s={type:\"position\",label:t?.label??\"Center\",group:t?.group??\"Position\"};return t?.units&&(s.units=[...t.units]),{default:t?.default??{x:.5,y:.5},transform:Xe,description:e,ui:s}}function Zne(){return{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}}}function Jne(e){return{softness:{default:0,description:e?.softnessDescription??\"Edge softness for antialiasing\",ui:{type:\"range\",min:0,max:.1,step:.001,label:\"Softness\",group:\"Effect\"}},strokeThickness:{default:0,description:\"Stroke thickness. Zero means no stroke.\",ui:{type:\"range\",min:0,max:e?.strokeThicknessMax??.2,step:.005,label:\"Stroke Thickness\",group:\"Stroke\"}},strokeColor:{default:\"#000000\",transform:ce,description:e?.strokeColorDescription??\"Color of the stroke outline\",ui:{type:\"color\",label:\"Stroke Color\",group:\"Colors\"}},strokePosition:{default:\"center\",transform:ww,description:e?.strokePositionDescription??\"Position of the stroke relative to the shape edge\",ui:{type:\"select\",options:[{label:\"Outside\",value:\"outside\"},{label:\"Center\",value:\"center\"},{label:\"Inside\",value:\"inside\"}],label:\"Stroke Position\",group:\"Stroke\"}}}}function eie(e){return{default:\"linear\",transform:at,compileTime:!0,description:e?.description??\"Color space for blending fill and stroke colors\",ui:{type:\"select\",options:dt,label:\"Color Blending\",group:\"Colors\"}}}function up(e,t,s){let r=s.allowNone?a=>({cover:0,contain:1,fill:2,\"scale-down\":3,none:4})[a]??0:a=>({cover:0,contain:1,fill:2,\"scale-down\":3,none:2})[a]??2,o=[{label:\"Cover\",value:\"cover\"},{label:\"Contain\",value:\"contain\"},{label:\"Fill\",value:\"fill\"},{label:\"Scale Down\",value:\"scale-down\"}];return s.allowNone&&o.push({label:\"None\",value:\"none\"}),{default:e,description:t,transform:r,compileTime:!0,ui:{type:\"select\",options:o,label:\"Object Fit\",group:\"Sizing\"}}}var tie={refraction:{default:.5,description:\"How strongly the shell bends what's seen through or inside it\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Refraction\",group:\"Glass\"}},dispersion:{default:.25,description:\"Spectral splitting at the shell \\u2014 rainbow fringes where light bends hardest\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Dispersion\",group:\"Glass\"}},thickness:{default:.2,description:\"Shell depth \\u2014 how far inward from the edge the shell's optics extend\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Thickness\",group:\"Glass\"}},environment:{default:1,description:\"Strength of the studio lighting reflected in the shell\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Environment\",group:\"Glass\"}},envRotation:{default:0,description:\"Rotates the reflected studio \\u2014 spins where the bright reflections fall\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Env Rotation\",group:\"Glass\"}},lightAngle:{default:315,description:\"Direction of the key light, in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Glass\"}},highlight:{default:1,description:\"Sharp key-light glint on the shell\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Highlight\",group:\"Highlight\"}},highlightColor:{default:\"#ffffff\",transform:ce,description:\"Color of the key-light highlight\",ui:{type:\"color\",label:\"Highlight Color\",group:\"Highlight\"}},highlightSoftness:{default:.5,description:\"Specular highlight softness \\u2014 lower is a tighter, sharper glint\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlight Softness\",group:\"Highlight\"}},fresnel:{default:.1,description:\"Fresnel rim glow \\u2014 the luminous edge where the shell turns away from view\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Fresnel\",group:\"Fresnel\"}},fresnelSoftness:{default:.1,description:\"Fresnel rim width \\u2014 higher values spread the glow further inward\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Fresnel Softness\",group:\"Fresnel\"}},fresnelColor:{default:\"#ffffff\",transform:ce,description:\"Color of the fresnel rim glow\",ui:{type:\"color\",label:\"Fresnel Color\",group:\"Fresnel\"}},edgeSoftness:{default:.05,description:\"Softness of the shape boundary edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Glass\"}}};function gr(e){let t={};for(let s of Object.keys(e)){let r=e[s];if(!r)continue;let o=tie[s];t[s]=r===!0?o:{...o,...r,ui:{...o.ui,...r.ui??{}}}}return t}function sie(e){return{default:0,description:e,ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Shape\"}}}function rie(e){return{default:\"#ffffff\",transform:ce,description:e,ui:{type:\"color\",label:\"Color\",group:\"Colors\"}}}function oie(e,t){if(e.custom)return e.custom;let s=e.size.as??\"half-canvas-height\",r={x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"},width:{prop:e.size.width,as:s},height:{prop:e.size.height,as:s}};return t&&(r.rotation={prop:\"rotation\",as:\"degrees\"}),{propBindings:r}}function aie(e,t,s){if(!t)return e;let r=Object.keys(e).filter(i=>!t.includes(i)),o=t.filter(i=>!(i in e));if(r.length||o.length)throw new Error(`defineSdfShapeShader(${s}): propOrder must list every prop exactly once (missing: ${r.join(\", \")||\"none\"}; unknown: ${o.join(\", \")||\"none\"})`);let a={};for(let i of t)a[i]=e[i];return a}function nie(e){let t=e.rotatable!==!1,s=Jne(e.stroke),r={origin:Zne(),color:rie(e.colorDescription),center:zo(e.centerDescription,{units:[\"%\",\"px\"],...e.centerLabel?{label:e.centerLabel}:{}}),...e.shapeProps,...t?{rotation:sie(e.rotationDescription??\"Rotation in degrees\")}:{},softness:s.softness,strokeThickness:s.strokeThickness,strokeColor:s.strokeColor,strokePosition:s.strokePosition,colorSpace:eie(e.colorSpaceDescription?{description:e.colorSpaceDescription}:void 0),...e.propOverrides};return{name:e.name,category:e.category??\"Shapes\",description:e.description,acceptsUVContext:!0,boundingBoxDeclaration:oie(e.bounds,t),props:aie(r,e.propOrder,e.name),fragment:o=>{let{uniforms:a,ctx:i,propValues:l}=o,c=o.uvContext??i.uv,d=l.colorSpace??0;return TB(ns(iie(e,a,c,i.aspect),\"shapeMask\"),a.color,a.strokeColor,d)}}}function iie(e,t,s,r){let o=t.rotation??ca(\"0f\"),a=ns(W(AB,\"shapeLocalCoords\",[t.center,o,s,r]),\"shapeLocal\"),i=l=>{let c=t[l];if(!c)throw new Error(`defineSdfShapeShader(${e.name}): distance reads unknown prop '${l}'`);return c};return W(wB,\"strokeMaskFromSdf\",[e.distance({x:a.member(\"x\"),y:a.member(\"y\")},i),t.softness,t.strokeThickness,t.strokePosition])}function T(e){return{kind:\"prop\",name:e}}var zi=class Cx{constructor(t){this.node=t}times(t){return new Cx({kind:\"mul\",a:this.node,b:t instanceof Cx?t.node:t})}};function Ro(e){return{uv:e.uvContext??e.ctx.uv,viewport:e.effectiveViewportSize??e.ctx.viewportSize}}function me(e,t){let s=t.uniforms[e.name];if(!s)throw new Error(`std: effect binds unknown prop '${e.name}'`);return s}function fn(e,t){let s=e instanceof zi?e.node:e;switch(s.kind){case\"prop\":return me(s,t);case\"mul\":return fn(s.a,t).mul(fn(s.b,t));case\"radialMask\":if(!t.ctx)throw new Error(\"std: radialMask needs a fragment host (warp slots have no ctx)\");return W(SJ,\"radialFalloffMask\",[t.ctx.uv,t.ctx.aspect,me(s.center,t),me(s.radius,t),me(s.falloff,t)]);case\"signal\":return s.build(t)}}function At(e,t){return Az(e,t)}function Az(e,t){if(typeof e==\"number\")return $e(e);if(e instanceof zi)return fn(e,t);switch(e.kind){case\"prop\":return me(e,t);case\"ctx\":if(!t.ctx)throw new Error(`std: ctx.${e.name} needs a fragment host (warp slots have no ctx)`);return t.ctx[e.name];default:return fn(e,t)}}function Bn(e,t,s,r){return{kind:\"pointwise\",body:{fn:e,hint:t},args:o=>s.map(a=>At(a,o)),compose:r?.compose,setup:r?.setup}}function HA(e,t,s){let r={},o=e.boundingBoxDeclaration,a=VA(e.props),i=lie(e.props,t);for(let[l,c]of Object.entries(e.props)){let d=c.transform;if(d===hl){cie(r,l,c,t[l],i);continue}if(d===_c){die(r,l,c,t[l]);continue}let u=t[l],p=u;p==null&&(p=c.default),p==null&&(console.error(`[Shaders] Uniform \"${l}\" is null/undefined after fallback. PropConfig:`,{propName:l,initialValue:u,default:c.default,hasTransform:!!d}),p=0);let _=bz(d),f=$A(o,l,a),h=!_&&!f&&(typeof p==\"string\"||typeof p==\"object\"&&p!==null&&!Array.isArray(p));r[l]={value:p,transform:_,compileTime:c.compileTime||!1,...c.compileTimeWhen?{compileTimeWhen:c.compileTimeWhen,_lastCompiledValue:u??c.default}:{},...f?{_rawDimensional:u}:{},...h?{cpu:!0}:{}}}return r}function lie(e,t){for(let[s,r]of Object.entries(e))if(r.transform===at){let o=t[s]??r.default;return typeof o==\"string\"?at(o):0}return 0}function cie(e,t,s,r,o){let a=r??null,i=J3(a,B_),l=eA(i.colors,i.stopCount,o);e[t]={value:a,transform:hl,cpu:!0,compileTime:s.compileTime||!1,...s.compileTimeWhen?{compileTimeWhen:s.compileTimeWhen,_lastCompiledValue:a??s.default}:{}},e.colorsArray={value:i.colors,schema:Fe(S,8)},e.positionsArray={value:i.positions,schema:Fe(S,8/4)},e.convertedColorsArray={value:l,schema:Fe(A,8)},e.stopCount={value:i.stopCount,schema:n}}function die(e,t,s,r){let o=Wne(s);if(!o){console.error(`[Shaders] List prop \"${t}\" has no item spec in its ui config`);return}let a=IA(r??s.default,o,{color:B_}),i=YA(a,o);e[t]={value:a,transform:_c,cpu:!0,compileTime:s.compileTime||!1,_rawList:r??s.default,_listSpec:o};for(let[l,c]of Object.entries(i.fields))e[tg(t,l)]={value:c,schema:Fe(S,o.maxItems)};e[sg(t)]={value:i.count,schema:n}}function wz(e){return Rr()?e.mobile:e.desktop}function vn(e){return wz(e)}var Ri=1024,rh={HARD:637,SOFT:255},kP=[.7548776662466927,.5698402909980532],N0=[.8191725133961645,.6710436067037893,.5497004779019703];function Di(e,t=16/9){let s=e,r=s?.dimensions?.height??0;return{dt:Math.min(Math.max(s?.deltaTime??.016,.001),.033),aspect:r>0?(s?.dimensions?.width??0)/r:t,pointerX:s?.pointer?.x??.5,pointerY:s?.pointer?.y??.5}}function Li(e){return(t,s)=>{let r=e(t);return typeof r==\"number\"?r:s}}function uie(e,t,s){return Math.min(Math.max(Math.round(e),t),s)}var wu=gt({x0:j,x1:j,y0:j,y1:j}).$name(\"SplatWindow\"),oh=w.fn([m,n,n,n],wu)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=B(r,1e-5),i=j(ie(__tsover_mul(__tsover_div(t.x,a),o))),l=j(ie(__tsover_mul(t.y,o))),c=j(nl(__tsover_div(__tsover_mul(s,o),a))),d=j(nl(__tsover_mul(s,o))),u=__tsover_sub(j(o),1);return wu({x0:B(__tsover_sub(i,c),0),x1:ye(__tsover_add(i,c),u),y0:B(__tsover_sub(l,d),0),y1:ye(__tsover_add(l,d),u)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"reach\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"res\"}],body:[0,[[13,\"asp\",[6,\"std.max\",[\"aspect\",[5,\"0.00001\"]]]],[13,\"cxi\",[6,\"d.i32\",[[6,\"std.floor\",[[1,[1,[7,\"pos\",\"x\"],\"/\",\"asp\"],\"*\",\"res\"]]]]]],[13,\"cyi\",[6,\"d.i32\",[[6,\"std.floor\",[[1,[7,\"pos\",\"y\"],\"*\",\"res\"]]]]]],[13,\"radX\",[6,\"d.i32\",[[6,\"std.ceil\",[[1,[1,\"reach\",\"*\",\"res\"],\"/\",\"asp\"]]]]]],[13,\"radY\",[6,\"d.i32\",[[6,\"std.ceil\",[[1,\"reach\",\"*\",\"res\"]]]]]],[13,\"last\",[1,[6,\"d.i32\",[\"res\"]],\"-\",[5,\"1\"]]],[10,[6,\"SplatWindow\",[[104,{x0:[6,\"std.max\",[[1,\"cxi\",\"-\",\"radX\"],[5,\"0\"]]],x1:[6,\"std.min\",[[1,\"cxi\",\"+\",\"radX\"],\"last\"]],y0:[6,\"std.max\",[[1,\"cyi\",\"-\",\"radY\"],[5,\"0\"]]],y1:[6,\"std.min\",[[1,\"cyi\",\"+\",\"radY\"],\"last\"]]}]]]]]]},externals:{\"std.max\":()=>B,\"d.i32\":()=>j,\"std.floor\":()=>ie,\"std.ceil\":()=>nl,SplatWindow:()=>wu,\"std.min\":()=>ye}})&&e.f)({})).$name(\"worldSplatWindow\"),ah=w.fn([m,j,nc],wu)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=j(ie(t.x)),a=j(ie(t.y));return wu({x0:B(__tsover_sub(o,s),0),x1:ye(__tsover_add(o,s),r.x),y0:B(__tsover_sub(a,s),0),y1:ye(__tsover_add(a,s),r.y)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"center\"},{type:\"i\",name:\"rad\"},{type:\"i\",name:\"last\"}],body:[0,[[13,\"cxi\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"center\",\"x\"]]]]]],[13,\"cyi\",[6,\"d.i32\",[[6,\"std.floor\",[[7,\"center\",\"y\"]]]]]],[10,[6,\"SplatWindow\",[[104,{x0:[6,\"std.max\",[[1,\"cxi\",\"-\",\"rad\"],[5,\"0\"]]],x1:[6,\"std.min\",[[1,\"cxi\",\"+\",\"rad\"],[7,\"last\",\"x\"]]],y0:[6,\"std.max\",[[1,\"cyi\",\"-\",\"rad\"],[5,\"0\"]]],y1:[6,\"std.min\",[[1,\"cyi\",\"+\",\"rad\"],[7,\"last\",\"y\"]]]}]]]]]]},externals:{\"d.i32\":()=>j,\"std.floor\":()=>ie,SplatWindow:()=>wu,\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})).$name(\"texelSplatWindow\"),Kl=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_sub(1,Tt(__tsover_mul(__tsover_mul(t,s),-1)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"energy\"},{type:\"i\",name:\"k\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.exp\",[[1,[1,\"energy\",\"*\",\"k\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})).$name(\"energyCoverageAlpha\"),nh=w.fn([A,A,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=t.add(s.mul(r)).mul(o),l=he(i);return i.mul(__tsover_div(ye(l,a),B(l,1e-5)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"vel\"},{type:\"i\",name:\"force\"},{type:\"i\",name:\"dt\"},{type:\"i\",name:\"dragMul\"},{type:\"i\",name:\"maxSpeed\"}],body:[0,[[13,\"v\",[6,[7,[6,[7,\"vel\",\"add\"],[[6,[7,\"force\",\"mul\"],[\"dt\"]]]],\"mul\"],[\"dragMul\"]]],[13,\"spd\",[6,\"std.length\",[\"v\"]]],[10,[6,[7,\"v\",\"mul\"],[[1,[6,\"std.min\",[\"spd\",\"maxSpeed\"]],\"/\",[6,\"std.max\",[\"spd\",[5,\"0.00001\"]]]]]]]]]},externals:{\"std.length\":()=>he,\"std.min\":()=>ye,\"std.max\":()=>B}})&&e.f)({})).$name(\"integrateSemiImplicitEuler\");function pie(e){let t=e?.storage;return!!t&&t.type===\"array\"}function mie(e){return!!e&&typeof e==\"object\"&&\"uniform\"in e}function _ie(e,t){let{gpu:s,registerComputeTexture:r,onCleanup:o}=e,a=s?.root;if(!a)return null;let{layout:i,output:l,maxAgents:c,externalKeys:d=[],extraBindGroup:u}=t,p=(globalThis.__TYPEGPU_AUTONAME__??(E=>E))(a.createTexture({size:l.size,format:l.format}).$usage(\"storage\",\"sampled\"),\"outTex\");o(()=>p.destroy());let _=(globalThis.__TYPEGPU_AUTONAME__??(E=>E))(a.createUniform(t.params),\"paramsU\"),f={[l.key]:p};for(let[E,R]of Object.entries(i.entries))pie(R)?f[E]=pr(a,R.storage.elementType,R.storage.elementCount):mie(R)&&(f[E]=_.buffer);let h=null,g={},y=()=>{for(let[E,R]of Object.entries(t.pipelines)){let U=R.kernel,L={size:R.threads===\"fixed\"?R.size:void 0,bindGroup:h},k=R.threads===\"grid\"||R.threads===\"fixed\"&&(R.size?.length??1)===2,D;if(k){let O=U;D=wr(a,(F=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(F.f=((q,Y)=>{\"use gpu\";O(q,Y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[6,\"k2\",[\"x\",\"y\"]]]]},externals:{k2:()=>O}})&&F.f)({}),L)}else{let O=U;D=wr(a,(F=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(F.f=(q=>{\"use gpu\";O(q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[6,\"k1\",[\"i\"]]]]},externals:{k1:()=>O}})&&F.f)({}),L)}R.extra&&u&&(D=D.with(u)),g[E]=D}};d.length===0&&(h=a.createBindGroup(i,f),y());let b=t.countCap?wz(t.countCap):c,x=!1,P=0,C=(E,R,U)=>{let L=g[E],k=t.pipelines[E];if(k.threads===\"fixed\")return L;if(k.threads===\"grid\"){let[O,F]=U??[1,1];return()=>L.dispatchThreads(O,F)}let D=k.threads===\"max\"?c:R;return()=>L.dispatchThreads(D)};return{outputs:{[l.name]:r(p)},writeParams:E=>_.write(E),countCap:b,resolveCount:(E,R=16)=>uie(E,R,b),pipelines:g,bindExternal:E=>{for(let R of d){if(E[R]===void 0)return;f[R]=E[R]}h=a.createBindGroup(i,f),y()},frame:E=>{if(!h)return null;let R=E?.count??b,U=[];if(t.initStep){let L=E?.reseed!==void 0&&E.reseed!==P;(!x||L)&&(x=!0,E?.reseed!==void 0&&(P=E.reseed),U.push(C(t.initStep,R,E?.grid)))}for(let L of t.program)U.push(C(L,R,E?.grid));return U}}}var Mx=Math.exp(-1),BP=1/(1-Mx),ga=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Tt(__tsover_mul(__tsover_div(t,s),-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"distSq\"},{type:\"i\",name:\"radiusSq\"}],body:[0,[[10,[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",\"radiusSq\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&e.f)({})),Ex=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return G(__tsover_mul(__tsover_sub(Tt(__tsover_mul(__tsover_div(t,s),-1)),Mx),BP),n(0),n(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"distSq\"},{type:\"i\",name:\"radiusSq\"}],body:[0,[[10,[6,\"std.clamp\",[[1,[1,[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",\"radiusSq\"],\"*\",[4,\"-\",[5,\"1\"]]]]],\"-\",\"EDGE\"],\"*\",\"INV_EDGE\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.exp\":()=>Tt,EDGE:()=>Mx,INV_EDGE:()=>BP,\"d.f32\":()=>n}})&&e.f)({}));w.fn([m,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=As(t,s);return ga(Se(o,o),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cellPos\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"radiusSq\"}],body:[0,[[13,\"off\",[6,\"std.sub\",[\"cellPos\",\"center\"]]],[10,[6,\"gaussianBrushSq\",[[6,\"std.dot\",[\"off\",\"off\"]],\"radiusSq\"]]]]]},externals:{\"std.sub\":()=>As,gaussianBrushSq:()=>ga,\"std.dot\":()=>Se}})&&e.f)({}));w.fn([m,m,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=As(t,s);return Ex(Se(o,o),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cellPos\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"radiusSq\"}],body:[0,[[13,\"off\",[6,\"std.sub\",[\"cellPos\",\"center\"]]],[10,[6,\"gaussianBrushShiftedSq\",[[6,\"std.dot\",[\"off\",\"off\"]],\"radiusSq\"]]]]]},externals:{\"std.sub\":()=>As,gaussianBrushShiftedSq:()=>Ex,\"std.dot\":()=>Se}})&&e.f)({}));var zP=new Map,RP=1;function fie(e,t){return`${e}${String(t).replace(/[^0-9A-Za-z]/g,\"_\")}`}function vie(e=RP){if(e===RP)return Ex;let t=zP.get(e);if(t)return t;let s=Math.exp(-(e*e)),r=1/(1-s),o=w.fn([n,n],n)((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";return G(__tsover_mul(__tsover_sub(Tt(__tsover_mul(__tsover_div(i,l),-1)),s),r),n(0),n(1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"distSq\"},{type:\"i\",name:\"radiusSq\"}],body:[0,[[10,[6,\"std.clamp\",[[1,[1,[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",\"radiusSq\"],\"*\",[4,\"-\",[5,\"1\"]]]]],\"-\",\"edge\"],\"*\",\"invEdge\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.exp\":()=>Tt,edge:()=>s,invEdge:()=>r,\"d.f32\":()=>n}})&&a.f)({})).$name(fie(\"gaussianBrushShiftedSq\",e));return zP.set(e,o),o}function hie(e){return e.edgeShift?vie(e.cutoffSigmas):ga}function Tz(e,t){let{n:s,namePrefix:r}=t;return w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=e.$.sparams,c=__tsover_add(__tsover_mul(i,M(s)),a),d=__tsover_add(n(a),.5),u=__tsover_add(n(i),.5),p=__tsover_sub(d,l.posX),_=__tsover_sub(u,l.posY),f=ga(__tsover_add(__tsover_mul(p,p),__tsover_mul(_,_)),B(__tsover_mul(l.radius,l.radius),n(1))),h=e.$.velA[c];e.$.velA[c]=S(__tsover_add(h.x,__tsover_mul(l.velX,f)),__tsover_add(h.y,__tsover_mul(l.velY,f)),h.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"sparams\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dx\",[1,\"cellX\",\"-\",[7,\"p\",\"posX\"]]],[13,\"dy\",[1,\"cellY\",\"-\",[7,\"p\",\"posY\"]]],[13,\"distSq\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[13,\"radSq\",[6,\"std.max\",[[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"inf\",[6,\"gaussianBrushSq\",[\"distSq\",\"radSq\"]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"vel\",\"x\"],\"+\",[1,[7,\"p\",\"velX\"],\"*\",\"inf\"]],[1,[7,\"vel\",\"y\"],\"+\",[1,[7,\"p\",\"velY\"],\"*\",\"inf\"]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,n:()=>s,\"d.f32\":()=>n,\"std.max\":()=>B,gaussianBrushSq:()=>ga,\"d.vec4f\":()=>S}})&&o.f)({})).$name(`${r}Force`)}function Sz(e,t){let{n:s,namePrefix:r,colorSpace:o}=t,a=Bd[o]??hc,i=hie(t.brush??{edgeShift:!1});return w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";let u=e.$.sparams,p=__tsover_add(__tsover_mul(d,M(s)),c),_=__tsover_add(n(c),.5),f=__tsover_add(n(d),.5),h=__tsover_sub(_,u.posX),g=__tsover_sub(f,u.posY),y=i(__tsover_add(__tsover_mul(h,h),__tsover_mul(g,g)),B(__tsover_mul(u.radius,u.radius),n(1))),b=G(__tsover_mul(y,u.strength),n(0),n(1)),x=a(A(u.colR,u.colG,u.colB),A(u.col2R,u.col2G,u.col2B),1,1,u.mixT),P=e.$.dyeA[p];e.$.dyeA[p]=S(K(P.x,x.x,b),K(P.y,x.y,b),K(P.z,x.z,b),0);let C=e.$.velA[p];e.$.velA[p]=S(__tsover_add(C.x,__tsover_mul(u.velX,y)),__tsover_add(C.y,__tsover_mul(u.velY,y)),C.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"sparams\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dx\",[1,\"cellX\",\"-\",[7,\"p\",\"posX\"]]],[13,\"dy\",[1,\"cellY\",\"-\",[7,\"p\",\"posY\"]]],[13,\"distSq\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[13,\"radSq\",[6,\"std.max\",[[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"inf\",[6,\"brushWeight\",[\"distSq\",\"radSq\"]]],[13,\"w\",[6,\"std.clamp\",[[1,\"inf\",\"*\",[7,\"p\",\"strength\"]],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"brush\",[6,\"mixFn\",[[6,\"d.vec3f\",[[7,\"p\",\"colR\"],[7,\"p\",\"colG\"],[7,\"p\",\"colB\"]]],[6,\"d.vec3f\",[[7,\"p\",\"col2R\"],[7,\"p\",\"col2G\"],[7,\"p\",\"col2B\"]]],[5,\"1\"],[5,\"1\"],[7,\"p\",\"mixT\"]]]],[13,\"cell\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.mix\",[[7,\"cell\",\"x\"],[7,\"brush\",\"x\"],\"w\"]],[6,\"std.mix\",[[7,\"cell\",\"y\"],[7,\"brush\",\"y\"],\"w\"]],[6,\"std.mix\",[[7,\"cell\",\"z\"],[7,\"brush\",\"z\"],\"w\"]],[5,\"0\"]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"vel\",\"x\"],\"+\",[1,[7,\"p\",\"velX\"],\"*\",\"inf\"]],[1,[7,\"vel\",\"y\"],\"+\",[1,[7,\"p\",\"velY\"],\"*\",\"inf\"]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,n:()=>s,\"d.f32\":()=>n,\"std.max\":()=>B,brushWeight:()=>i,\"std.clamp\":()=>G,mixFn:()=>a,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&l.f)({})).$name(`${r}Splat${o}`)}function pp(e,t){let s=e,{n:r,namePrefix:o,densityGain:a,velocityBlendGain:i}=t,l=t.masked??!1,c=t.gated??!1,d=r*.08;if(c&&(t.cone||t.cursorPush||l))throw new Error(`buildEmitterSplatKernel: 'gated' is the cursor-puff shape \\u2014 it does not combine with cone/cursorPush/masked (got cone=${t.cone}, cursorPush=${t.cursorPush}, masked=${l})`);if(!c&&(!t.cone||!t.cursorPush))throw new Error(`buildEmitterSplatKernel: the fixed-source shape ships as cone+cursorPush \\u2014 pass both (got cone=${t.cone}, cursorPush=${t.cursorPush})`);return c?w.fn([M,M])((u=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(u.f=((p,_)=>{\"use gpu\";let f=s.$.params,h=__tsover_add(__tsover_mul(_,M(r)),p),g=__tsover_add(n(p),.5),y=__tsover_add(n(_),.5),b=__tsover_sub(g,f.emitX),x=__tsover_sub(y,f.emitY),P=__tsover_add(__tsover_mul(b,b),__tsover_mul(x,x)),C=B(__tsover_mul(f.emitRad,f.emitRad),n(1)),E=__tsover_mul(ga(P,C),f.emitGate),R=s.$.dyeA[h],U=R.x,L=__tsover_mul(__tsover_mul(__tsover_mul(f.emitIntensity,E),f.dt),a),k=ye(__tsover_add(U,L),1),D=__tsover_div(L,B(k,n(.001)));s.$.dyeA[h]=S(k,__tsover_mul(R.y,__tsover_sub(1,ye(D,1))),0,0);let O=s.$.velA[h],F=ye(__tsover_mul(__tsover_mul(E,f.dt),i),1),q=__tsover_mul(__tsover_mul(__tsover_mul(f.gravity,U),f.dt),d);s.$.velA[h]=S(K(O.x,f.emitVelX,F),__tsover_add(K(O.y,f.emitVelY,F),q),O.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dx\",[1,\"cellX\",\"-\",[7,\"p\",\"emitX\"]]],[13,\"dy\",[1,\"cellY\",\"-\",[7,\"p\",\"emitY\"]]],[13,\"distSq\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[13,\"radSq\",[6,\"std.max\",[[1,[7,\"p\",\"emitRad\"],\"*\",[7,\"p\",\"emitRad\"]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"influence\",[1,[6,\"gaussianBrushSq\",[\"distSq\",\"radSq\"]],\"*\",[7,\"p\",\"emitGate\"]]],[13,\"cell\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[13,\"oldDens\",[7,\"cell\",\"x\"]],[13,\"addDens\",[1,[1,[1,[7,\"p\",\"emitIntensity\"],\"*\",\"influence\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"densityGain\"]],[13,\"newDens\",[6,\"std.min\",[[1,\"oldDens\",\"+\",\"addDens\"],[5,\"1\"]]]],[13,\"rejuvenate\",[1,\"addDens\",\"/\",[6,\"std.max\",[\"newDens\",[6,\"d.f32\",[[5,\"0.001\"]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"newDens\",[1,[7,\"cell\",\"y\"],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.min\",[\"rejuvenate\",[5,\"1\"]]]]],[5,\"0\"],[5,\"0\"]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"br\",[6,\"std.min\",[[1,[1,\"influence\",\"*\",[7,\"p\",\"dt\"]],\"*\",\"velocityBlendGain\"],[5,\"1\"]]]],[13,\"gravForce\",[1,[1,[1,[7,\"p\",\"gravity\"],\"*\",\"oldDens\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"gravityGain\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.mix\",[[7,\"vel\",\"x\"],[7,\"p\",\"emitVelX\"],\"br\"]],[1,[6,\"std.mix\",[[7,\"vel\",\"y\"],[7,\"p\",\"emitVelY\"],\"br\"]],\"+\",\"gravForce\"],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,\"std.max\":()=>B,gaussianBrushSq:()=>ga,densityGain:()=>a,\"std.min\":()=>ye,\"d.vec4f\":()=>S,velocityBlendGain:()=>i,gravityGain:()=>d,\"std.mix\":()=>K}})&&u.f)({})).$name(`${o}Splat`):l?w.fn([M,M])((u=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(u.f=((p,_)=>{\"use gpu\";let f=s.$.params,h=__tsover_add(__tsover_mul(_,M(r)),p),g=s.$.maskBuf[h].x,y=__tsover_add(n(p),.5),b=__tsover_add(n(_),.5),x=__tsover_sub(y,f.emitX),P=__tsover_sub(b,f.emitY),C=__tsover_add(__tsover_mul(x,x),__tsover_mul(P,P)),E=B(__tsover_mul(f.emitRad,f.emitRad),n(1)),R=__tsover_mul(ga(C,E),g),U=s.$.dyeA[h],L=U.x,k=__tsover_mul(__tsover_mul(__tsover_mul(f.emitIntensity,R),f.dt),a),D=ye(__tsover_add(L,k),1),O=__tsover_div(k,B(D,n(.001)));s.$.dyeA[h]=S(D,__tsover_mul(U.y,__tsover_sub(1,ye(O,1))),0,0);let F=s.$.velA[h],q=__tsover_div(__tsover_add(__tsover_mul(x,f.perpDirX),__tsover_mul(P,f.perpDirY)),B(f.emitRad,n(1))),Y=B(__tsover_add(__tsover_mul(f.emitVelX,f.emitVelX),__tsover_mul(f.emitVelY,f.emitVelY)),n(1)),V=__tsover_add(f.emitVelX,__tsover_mul(__tsover_mul(__tsover_mul(f.perpDirX,q),f.spreadFactor),Ue(Y))),X=__tsover_add(f.emitVelY,__tsover_mul(__tsover_mul(__tsover_mul(f.perpDirY,q),f.spreadFactor),Ue(Y))),Q=ye(__tsover_mul(__tsover_mul(R,f.dt),i),1),te=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(f.gravity,L),f.dt),d),g),J=__tsover_sub(y,f.cursorX),oe=__tsover_sub(b,f.cursorY),le=__tsover_add(__tsover_mul(J,J),__tsover_mul(oe,oe)),se=__tsover_mul(__tsover_mul(ga(le,B(f.mouseRadSq,n(1))),f.mouseActive),g);s.$.velA[h]=S(__tsover_add(K(F.x,V,Q),__tsover_mul(f.cursorVelX,se)),__tsover_add(__tsover_add(K(F.y,X,Q),te),__tsover_mul(f.cursorVelY,se)),F.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"mask\",[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],\"idx\"],\"x\"]],[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dx\",[1,\"cellX\",\"-\",[7,\"p\",\"emitX\"]]],[13,\"dy\",[1,\"cellY\",\"-\",[7,\"p\",\"emitY\"]]],[13,\"distSq\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[13,\"radSq\",[6,\"std.max\",[[1,[7,\"p\",\"emitRad\"],\"*\",[7,\"p\",\"emitRad\"]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"influence\",[1,[6,\"gaussianBrushSq\",[\"distSq\",\"radSq\"]],\"*\",\"mask\"]],[13,\"cell\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[13,\"oldDens\",[7,\"cell\",\"x\"]],[13,\"addDens\",[1,[1,[1,[7,\"p\",\"emitIntensity\"],\"*\",\"influence\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"densityGain\"]],[13,\"newDens\",[6,\"std.min\",[[1,\"oldDens\",\"+\",\"addDens\"],[5,\"1\"]]]],[13,\"rejuvenate\",[1,\"addDens\",\"/\",[6,\"std.max\",[\"newDens\",[6,\"d.f32\",[[5,\"0.001\"]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"newDens\",[1,[7,\"cell\",\"y\"],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.min\",[\"rejuvenate\",[5,\"1\"]]]]],[5,\"0\"],[5,\"0\"]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"perpOff\",[1,[1,[1,\"dx\",\"*\",[7,\"p\",\"perpDirX\"]],\"+\",[1,\"dy\",\"*\",[7,\"p\",\"perpDirY\"]]],\"/\",[6,\"std.max\",[[7,\"p\",\"emitRad\"],[6,\"d.f32\",[[5,\"1\"]]]]]]],[13,\"vmSq\",[6,\"std.max\",[[1,[1,[7,\"p\",\"emitVelX\"],\"*\",[7,\"p\",\"emitVelX\"]],\"+\",[1,[7,\"p\",\"emitVelY\"],\"*\",[7,\"p\",\"emitVelY\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"tvx\",[1,[7,\"p\",\"emitVelX\"],\"+\",[1,[1,[1,[7,\"p\",\"perpDirX\"],\"*\",\"perpOff\"],\"*\",[7,\"p\",\"spreadFactor\"]],\"*\",[6,\"std.sqrt\",[\"vmSq\"]]]]],[13,\"tvy\",[1,[7,\"p\",\"emitVelY\"],\"+\",[1,[1,[1,[7,\"p\",\"perpDirY\"],\"*\",\"perpOff\"],\"*\",[7,\"p\",\"spreadFactor\"]],\"*\",[6,\"std.sqrt\",[\"vmSq\"]]]]],[13,\"br\",[6,\"std.min\",[[1,[1,\"influence\",\"*\",[7,\"p\",\"dt\"]],\"*\",\"velocityBlendGain\"],[5,\"1\"]]]],[13,\"gravForce\",[1,[1,[1,[1,[7,\"p\",\"gravity\"],\"*\",\"oldDens\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"gravityGain\"],\"*\",\"mask\"]],[13,\"cursorDX\",[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]]],[13,\"cursorDY\",[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]]],[13,\"cursorDistSq\",[1,[1,\"cursorDX\",\"*\",\"cursorDX\"],\"+\",[1,\"cursorDY\",\"*\",\"cursorDY\"]]],[13,\"cursorInf\",[1,[1,[6,\"gaussianBrushSq\",[\"cursorDistSq\",[6,\"std.max\",[[7,\"p\",\"mouseRadSq\"],[6,\"d.f32\",[[5,\"1\"]]]]]]],\"*\",[7,\"p\",\"mouseActive\"]],\"*\",\"mask\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[6,\"std.mix\",[[7,\"vel\",\"x\"],\"tvx\",\"br\"]],\"+\",[1,[7,\"p\",\"cursorVelX\"],\"*\",\"cursorInf\"]],[1,[1,[6,\"std.mix\",[[7,\"vel\",\"y\"],\"tvy\",\"br\"]],\"+\",\"gravForce\"],\"+\",[1,[7,\"p\",\"cursorVelY\"],\"*\",\"cursorInf\"]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,\"std.max\":()=>B,gaussianBrushSq:()=>ga,densityGain:()=>a,\"std.min\":()=>ye,\"d.vec4f\":()=>S,\"std.sqrt\":()=>Ue,velocityBlendGain:()=>i,gravityGain:()=>d,\"std.mix\":()=>K}})&&u.f)({})).$name(`${o}Splat`):w.fn([M,M])((u=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(u.f=((p,_)=>{\"use gpu\";let f=s.$.params,h=__tsover_add(__tsover_mul(_,M(r)),p),g=__tsover_add(n(p),.5),y=__tsover_add(n(_),.5),b=__tsover_sub(g,f.emitX),x=__tsover_sub(y,f.emitY),P=ga(__tsover_add(__tsover_mul(b,b),__tsover_mul(x,x)),B(__tsover_mul(f.emitRad,f.emitRad),n(1))),C=s.$.dyeA[h],E=C.x,R=__tsover_mul(__tsover_mul(__tsover_mul(f.emitIntensity,P),f.dt),a),U=ye(__tsover_add(E,R),1),L=__tsover_div(R,B(U,n(.001)));s.$.dyeA[h]=S(U,__tsover_mul(C.y,__tsover_sub(1,ye(L,1))),0,0);let k=s.$.velA[h],D=__tsover_div(__tsover_add(__tsover_mul(b,f.perpDirX),__tsover_mul(x,f.perpDirY)),B(f.emitRad,n(1))),O=B(__tsover_add(__tsover_mul(f.emitVelX,f.emitVelX),__tsover_mul(f.emitVelY,f.emitVelY)),n(1)),F=__tsover_add(f.emitVelX,__tsover_mul(__tsover_mul(__tsover_mul(f.perpDirX,D),f.spreadFactor),Ue(O))),q=__tsover_add(f.emitVelY,__tsover_mul(__tsover_mul(__tsover_mul(f.perpDirY,D),f.spreadFactor),Ue(O))),Y=ye(__tsover_mul(__tsover_mul(P,f.dt),i),1),V=__tsover_mul(__tsover_mul(__tsover_mul(f.gravity,E),f.dt),d),X=__tsover_sub(g,f.cursorX),Q=__tsover_sub(y,f.cursorY),te=__tsover_add(__tsover_mul(X,X),__tsover_mul(Q,Q)),J=__tsover_mul(ga(te,B(f.mouseRadSq,n(1))),f.mouseActive);s.$.velA[h]=S(__tsover_add(K(k.x,F,Y),__tsover_mul(f.cursorVelX,J)),__tsover_add(__tsover_add(K(k.y,q,Y),V),__tsover_mul(f.cursorVelY,J)),k.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dx\",[1,\"cellX\",\"-\",[7,\"p\",\"emitX\"]]],[13,\"dy\",[1,\"cellY\",\"-\",[7,\"p\",\"emitY\"]]],[13,\"distSq\",[1,[1,\"dx\",\"*\",\"dx\"],\"+\",[1,\"dy\",\"*\",\"dy\"]]],[13,\"radSq\",[6,\"std.max\",[[1,[7,\"p\",\"emitRad\"],\"*\",[7,\"p\",\"emitRad\"]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"influence\",[6,\"gaussianBrushSq\",[\"distSq\",\"radSq\"]]],[13,\"cell\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[13,\"oldDens\",[7,\"cell\",\"x\"]],[13,\"addDens\",[1,[1,[1,[7,\"p\",\"emitIntensity\"],\"*\",\"influence\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"densityGain\"]],[13,\"newDens\",[6,\"std.min\",[[1,\"oldDens\",\"+\",\"addDens\"],[5,\"1\"]]]],[13,\"rejuvenate\",[1,\"addDens\",\"/\",[6,\"std.max\",[\"newDens\",[6,\"d.f32\",[[5,\"0.001\"]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"newDens\",[1,[7,\"cell\",\"y\"],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.min\",[\"rejuvenate\",[5,\"1\"]]]]],[5,\"0\"],[5,\"0\"]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"perpOff\",[1,[1,[1,\"dx\",\"*\",[7,\"p\",\"perpDirX\"]],\"+\",[1,\"dy\",\"*\",[7,\"p\",\"perpDirY\"]]],\"/\",[6,\"std.max\",[[7,\"p\",\"emitRad\"],[6,\"d.f32\",[[5,\"1\"]]]]]]],[13,\"vmSq\",[6,\"std.max\",[[1,[1,[7,\"p\",\"emitVelX\"],\"*\",[7,\"p\",\"emitVelX\"]],\"+\",[1,[7,\"p\",\"emitVelY\"],\"*\",[7,\"p\",\"emitVelY\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"tvx\",[1,[7,\"p\",\"emitVelX\"],\"+\",[1,[1,[1,[7,\"p\",\"perpDirX\"],\"*\",\"perpOff\"],\"*\",[7,\"p\",\"spreadFactor\"]],\"*\",[6,\"std.sqrt\",[\"vmSq\"]]]]],[13,\"tvy\",[1,[7,\"p\",\"emitVelY\"],\"+\",[1,[1,[1,[7,\"p\",\"perpDirY\"],\"*\",\"perpOff\"],\"*\",[7,\"p\",\"spreadFactor\"]],\"*\",[6,\"std.sqrt\",[\"vmSq\"]]]]],[13,\"br\",[6,\"std.min\",[[1,[1,\"influence\",\"*\",[7,\"p\",\"dt\"]],\"*\",\"velocityBlendGain\"],[5,\"1\"]]]],[13,\"gravForce\",[1,[1,[1,[7,\"p\",\"gravity\"],\"*\",\"oldDens\"],\"*\",[7,\"p\",\"dt\"]],\"*\",\"gravityGain\"]],[13,\"cursorDX\",[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]]],[13,\"cursorDY\",[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]]],[13,\"cursorDistSq\",[1,[1,\"cursorDX\",\"*\",\"cursorDX\"],\"+\",[1,\"cursorDY\",\"*\",\"cursorDY\"]]],[13,\"cursorInf\",[1,[6,\"gaussianBrushSq\",[\"cursorDistSq\",[6,\"std.max\",[[7,\"p\",\"mouseRadSq\"],[6,\"d.f32\",[[5,\"1\"]]]]]]],\"*\",[7,\"p\",\"mouseActive\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[6,\"std.mix\",[[7,\"vel\",\"x\"],\"tvx\",\"br\"]],\"+\",[1,[7,\"p\",\"cursorVelX\"],\"*\",\"cursorInf\"]],[1,[1,[6,\"std.mix\",[[7,\"vel\",\"y\"],\"tvy\",\"br\"]],\"+\",\"gravForce\"],\"+\",[1,[7,\"p\",\"cursorVelY\"],\"*\",\"cursorInf\"]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,\"std.max\":()=>B,gaussianBrushSq:()=>ga,densityGain:()=>a,\"std.min\":()=>ye,\"d.vec4f\":()=>S,\"std.sqrt\":()=>Ue,velocityBlendGain:()=>i,gravityGain:()=>d,\"std.mix\":()=>K}})&&u.f)({})).$name(`${o}Splat`)}var DP=new Map;function gie(e,t){return t===\"toroidal\"?w.fn([j,j,j,j],M)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o,a,i)=>{\"use gpu\";let l=__tsover_mod(__tsover_add(__tsover_add(r,a),e),e),c=__tsover_mod(__tsover_add(__tsover_add(o,i),e),e);return M(__tsover_add(__tsover_mul(l,e),c))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ii\"},{type:\"i\",name:\"ji\"},{type:\"i\",name:\"di\"},{type:\"i\",name:\"dj\"}],body:[0,[[13,\"row\",[1,[1,[1,\"ii\",\"+\",\"di\"],\"+\",\"n\"],\"%\",\"n\"]],[13,\"col\",[1,[1,[1,\"ji\",\"+\",\"dj\"],\"+\",\"n\"],\"%\",\"n\"]],[10,[6,\"d.u32\",[[1,[1,\"row\",\"*\",\"n\"],\"+\",\"col\"]]]]]]},externals:{n:()=>e,\"d.u32\":()=>M}})&&s.f)({})).$name(\"nw\"):w.fn([j,j,j,j],M)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o,a,i)=>{\"use gpu\";let l=G(__tsover_add(r,a),0,__tsover_sub(e,1)),c=G(__tsover_add(o,i),0,__tsover_sub(e,1));return M(__tsover_add(__tsover_mul(l,e),c))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ii\"},{type:\"i\",name:\"ji\"},{type:\"i\",name:\"di\"},{type:\"i\",name:\"dj\"}],body:[0,[[13,\"row\",[6,\"std.clamp\",[[1,\"ii\",\"+\",\"di\"],[5,\"0\"],[1,\"n\",\"-\",[5,\"1\"]]]]],[13,\"col\",[6,\"std.clamp\",[[1,\"ji\",\"+\",\"dj\"],[5,\"0\"],[1,\"n\",\"-\",[5,\"1\"]]]]],[10,[6,\"d.u32\",[[1,[1,\"row\",\"*\",\"n\"],\"+\",\"col\"]]]]]]},externals:{\"std.clamp\":()=>G,n:()=>e,\"d.u32\":()=>M}})&&s.f)({})).$name(\"nidx\")}function yie(e,t){let s=`${e}:${t}`,r=DP.get(s);if(r)return r;let o=gie(e,t);return DP.set(s,o),o}function zn(e,t){let s=e,{n:r,namePrefix:o,boundary:a,dye:i}=t,l=yie(r,a),c=t.pressureDecay??.8,d=a===\"toroidal\",u=i===\"rgb\"?\"Dye\":\"Dens\";if(i===\"densityAge\"&&(t.dyeDissipation??!1)!==(t.ageAdvance??!1))throw new Error(`buildStableFluidsKernels: dye 'densityAge' supports dyeDissipation and ageAdvance only together or not at all (got dyeDissipation=${t.dyeDissipation??!1}, ageAdvance=${t.ageAdvance??!1})`);if(t.solidMask&&d)throw new Error(\"buildStableFluidsKernels: solidMask requires boundary 'clamped' \\u2014 a toroidal field with interior walls is not supported (the masked divergence pass assumes clamped edges)\");let p=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=s.$.velA[l(D,O,0,-1)],q=s.$.velA[l(D,O,0,1)],Y=s.$.velA[l(D,O,-1,0)],V=s.$.velA[l(D,O,1,0)],X=__tsover_mul(__tsover_sub(__tsover_sub(q.y,F.y),__tsover_sub(V.x,Y.x)),.5),Q=s.$.velA[k];s.$.velA[k]=S(Q.x,Q.y,X,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"vL\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]]],[13,\"vR\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]]],[13,\"vU\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]]],[13,\"vD\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]]],[13,\"curlZ\",[1,[1,[1,[7,\"vR\",\"y\"],\"-\",[7,\"vL\",\"y\"]],\"-\",[1,[7,\"vD\",\"x\"],\"-\",[7,\"vU\",\"x\"]]],\"*\",[5,\"0.5\"]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"vel\",\"x\"],[7,\"vel\",\"y\"],\"curlZ\",[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,layout:()=>s,nb:()=>l,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}Curl`),_=t.ambientWind?.gain??0,f=t.ambientWind?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=j(L),F=j(U),q=re(s.$.velA[l(O,F,0,-1)].z),Y=re(s.$.velA[l(O,F,0,1)].z),V=re(s.$.velA[l(O,F,-1,0)].z),X=re(s.$.velA[l(O,F,1,0)].z),Q=s.$.velA[D].z,te=__tsover_mul(__tsover_sub(Y,q),.5),J=__tsover_mul(__tsover_sub(X,V),.5),oe=B(Ue(__tsover_add(__tsover_mul(te,te),__tsover_mul(J,J))),n(1e-5)),le=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_div(J,oe),Q),k.curlStrength),k.dt),se=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_div(te,oe),-1),Q),k.curlStrength),k.dt),Te=n(0),de=n(0);if(k.ambient>0){let Ae=__tsover_div(__tsover_add(n(U),.5),n(r)),De=__tsover_div(__tsover_add(n(L),.5),n(r)),Ee=sv(m(__tsover_mul(Ae,k.ambientFreq),__tsover_mul(De,k.ambientFreq)),k.ambientTime),ze=__tsover_mul(__tsover_mul(k.ambient,_),k.dt);Te=__tsover_mul(Ee.x,ze),de=__tsover_mul(Ee.y,ze)}let xe=s.$.velA[D];s.$.velA[D]=S(__tsover_add(__tsover_add(xe.x,le),Te),__tsover_add(__tsover_add(xe.y,se),de),xe.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"cL\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"z\"]]]],[13,\"cR\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"z\"]]]],[13,\"cU\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"z\"]]]],[13,\"cD\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"z\"]]]],[13,\"cc\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"z\"]],[13,\"gx\",[1,[1,\"cR\",\"-\",\"cL\"],\"*\",[5,\"0.5\"]]],[13,\"gy\",[1,[1,\"cD\",\"-\",\"cU\"],\"*\",[5,\"0.5\"]]],[13,\"gl\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"gx\",\"*\",\"gx\"],\"+\",[1,\"gy\",\"*\",\"gy\"]]]],[6,\"d.f32\",[[5,\"0.00001\"]]]]]],[13,\"fx\",[1,[1,[1,[1,\"gy\",\"/\",\"gl\"],\"*\",\"cc\"],\"*\",[7,\"p\",\"curlStrength\"]],\"*\",[7,\"p\",\"dt\"]]],[13,\"fy\",[1,[1,[1,[1,[1,\"gx\",\"/\",\"gl\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"cc\"],\"*\",[7,\"p\",\"curlStrength\"]],\"*\",[7,\"p\",\"dt\"]]],[12,\"ax\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"ay\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,[7,\"p\",\"ambient\"],\">\",[5,\"0\"]],[0,[[13,\"uvx\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"uvy\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"amb\",[6,\"noise.curl22z\",[[6,\"d.vec2f\",[[1,\"uvx\",\"*\",[7,\"p\",\"ambientFreq\"]],[1,\"uvy\",\"*\",[7,\"p\",\"ambientFreq\"]]]],[7,\"p\",\"ambientTime\"]]]],[13,\"af\",[1,[1,[7,\"p\",\"ambient\"],\"*\",\"ambientGain\"],\"*\",[7,\"p\",\"dt\"]]],[2,\"ax\",\"=\",[1,[7,\"amb\",\"x\"],\"*\",\"af\"]],[2,\"ay\",\"=\",[1,[7,\"amb\",\"y\"],\"*\",\"af\"]]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[1,[7,\"vel\",\"x\"],\"+\",\"fx\"],\"+\",\"ax\"],[1,[1,[7,\"vel\",\"y\"],\"+\",\"fy\"],\"+\",\"ay\"],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,\"std.abs\":()=>re,nb:()=>l,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"d.f32\":()=>n,\"noise.curl22z\":()=>sv,\"d.vec2f\":()=>m,ambientGain:()=>_,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}Vorticity`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=j(L),F=j(U),q=re(s.$.velA[l(O,F,0,-1)].z),Y=re(s.$.velA[l(O,F,0,1)].z),V=re(s.$.velA[l(O,F,-1,0)].z),X=re(s.$.velA[l(O,F,1,0)].z),Q=s.$.velA[D].z,te=__tsover_mul(__tsover_sub(Y,q),.5),J=__tsover_mul(__tsover_sub(X,V),.5),oe=B(Ue(__tsover_add(__tsover_mul(te,te),__tsover_mul(J,J))),n(1e-5)),le=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_div(J,oe),Q),k.curlStrength),k.dt),se=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_div(te,oe),-1),Q),k.curlStrength),k.dt),Te=s.$.velA[D];s.$.velA[D]=S(__tsover_add(Te.x,le),__tsover_add(Te.y,se),Te.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"cL\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"z\"]]]],[13,\"cR\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"z\"]]]],[13,\"cU\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"z\"]]]],[13,\"cD\",[6,\"std.abs\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"z\"]]]],[13,\"cc\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"z\"]],[13,\"gx\",[1,[1,\"cR\",\"-\",\"cL\"],\"*\",[5,\"0.5\"]]],[13,\"gy\",[1,[1,\"cD\",\"-\",\"cU\"],\"*\",[5,\"0.5\"]]],[13,\"gl\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"gx\",\"*\",\"gx\"],\"+\",[1,\"gy\",\"*\",\"gy\"]]]],[6,\"d.f32\",[[5,\"0.00001\"]]]]]],[13,\"fx\",[1,[1,[1,[1,\"gy\",\"/\",\"gl\"],\"*\",\"cc\"],\"*\",[7,\"p\",\"curlStrength\"]],\"*\",[7,\"p\",\"dt\"]]],[13,\"fy\",[1,[1,[1,[1,[1,\"gx\",\"/\",\"gl\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"cc\"],\"*\",[7,\"p\",\"curlStrength\"]],\"*\",[7,\"p\",\"dt\"]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"vel\",\"x\"],\"+\",\"fx\"],[1,[7,\"vel\",\"y\"],\"+\",\"fy\"],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,\"std.abs\":()=>re,nb:()=>l,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"d.f32\":()=>n,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}Vorticity`),h=t.solidMask?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=n(U),q=n(L),Y=s.$.maskBuf[k].x,V=s.$.velA[k],X=F<.5||s.$.maskBuf[l(D,O,0,-1)].x<.5,Q=F>n(__tsover_sub(r,1.5))||s.$.maskBuf[l(D,O,0,1)].x<.5,te=q<.5||s.$.maskBuf[l(D,O,-1,0)].x<.5,J=q>n(__tsover_sub(r,1.5))||s.$.maskBuf[l(D,O,1,0)].x<.5,oe=N(s.$.velA[l(D,O,0,-1)].x,__tsover_mul(V.x,-1),X),le=N(s.$.velA[l(D,O,0,1)].x,__tsover_mul(V.x,-1),Q),se=N(s.$.velA[l(D,O,-1,0)].y,__tsover_mul(V.y,-1),te),Te=N(s.$.velA[l(D,O,1,0)].y,__tsover_mul(V.y,-1),J),de=__tsover_mul(__tsover_mul(__tsover_add(__tsover_sub(le,oe),__tsover_sub(Te,se)),.5),Y);s.$.pressure[k]=__tsover_mul(__tsover_mul(s.$.pressure[k],c),Y),s.$.divergence[k]=de}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"jf\",[6,\"d.f32\",[\"cx\"]]],[13,\"iF\",[6,\"d.f32\",[\"cy\"]]],[13,\"selfMask\",[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],\"idx\"],\"x\"]],[13,\"selfVel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"isWallL\",[3,[1,\"jf\",\"<\",[5,\"0.5\"]],\"||\",[1,[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"x\"],\"<\",[5,\"0.5\"]]]],[13,\"isWallR\",[3,[1,\"jf\",\">\",[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1.5\"]]]]],\"||\",[1,[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"x\"],\"<\",[5,\"0.5\"]]]],[13,\"isWallT\",[3,[1,\"iF\",\"<\",[5,\"0.5\"]],\"||\",[1,[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"x\"],\"<\",[5,\"0.5\"]]]],[13,\"isWallB\",[3,[1,\"iF\",\">\",[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1.5\"]]]]],\"||\",[1,[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"x\"],\"<\",[5,\"0.5\"]]]],[13,\"L\",[6,\"std.select\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"x\"],[1,[7,\"selfVel\",\"x\"],\"*\",[4,\"-\",[5,\"1\"]]],\"isWallL\"]]],[13,\"R\",[6,\"std.select\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"x\"],[1,[7,\"selfVel\",\"x\"],\"*\",[4,\"-\",[5,\"1\"]]],\"isWallR\"]]],[13,\"T\",[6,\"std.select\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"y\"],[1,[7,\"selfVel\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],\"isWallT\"]]],[13,\"B\",[6,\"std.select\",[[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"y\"],[1,[7,\"selfVel\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],\"isWallB\"]]],[13,\"div\",[1,[1,[1,[1,\"R\",\"-\",\"L\"],\"+\",[1,\"B\",\"-\",\"T\"]],\"*\",[5,\"0.5\"]],\"*\",\"selfMask\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"=\",[1,[1,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"*\",\"pressureDecay\"],\"*\",\"selfMask\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"divergence\"],\"idx\"],\"=\",\"div\"]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,\"d.f32\":()=>n,layout:()=>s,nb:()=>l,\"std.select\":()=>N,pressureDecay:()=>c}})&&R.f)({})).$name(`${o}Divergence`):d?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=s.$.velA[l(D,O,0,-1)].x,q=s.$.velA[l(D,O,0,1)].x,Y=s.$.velA[l(D,O,-1,0)].y,V=s.$.velA[l(D,O,1,0)].y,X=__tsover_mul(__tsover_add(__tsover_sub(q,F),__tsover_sub(V,Y)),.5);s.$.pressure[k]=__tsover_mul(s.$.pressure[k],c),s.$.divergence[k]=X}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"vL\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"x\"]],[13,\"vR\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"x\"]],[13,\"vU\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"y\"]],[13,\"vD\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"y\"]],[13,\"div\",[1,[1,[1,\"vR\",\"-\",\"vL\"],\"+\",[1,\"vD\",\"-\",\"vU\"]],\"*\",[5,\"0.5\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"=\",[1,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"*\",\"pressureDecay\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"divergence\"],\"idx\"],\"=\",\"div\"]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,layout:()=>s,nb:()=>l,pressureDecay:()=>c}})&&R.f)({})).$name(`${o}Divergence`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=n(U),q=n(L),Y=s.$.velA[k],V=s.$.velA[l(D,O,0,-1)].x,X=s.$.velA[l(D,O,0,1)].x,Q=s.$.velA[l(D,O,-1,0)].y,te=s.$.velA[l(D,O,1,0)].y,J=N(V,__tsover_mul(Y.x,-1),F<.5),oe=N(X,__tsover_mul(Y.x,-1),F>n(__tsover_sub(r,1.5))),le=N(Q,__tsover_mul(Y.y,-1),q<.5),se=N(te,__tsover_mul(Y.y,-1),q>n(__tsover_sub(r,1.5))),Te=__tsover_mul(__tsover_add(__tsover_sub(oe,J),__tsover_sub(se,le)),.5);s.$.pressure[k]=__tsover_mul(s.$.pressure[k],c),s.$.divergence[k]=Te}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"jf\",[6,\"d.f32\",[\"cx\"]]],[13,\"iF\",[6,\"d.f32\",[\"cy\"]]],[13,\"selfVel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"normalL\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],\"x\"]],[13,\"normalR\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],\"x\"]],[13,\"normalU\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],\"y\"]],[13,\"normalD\",[7,[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],\"y\"]],[13,\"L\",[6,\"std.select\",[\"normalL\",[1,[7,\"selfVel\",\"x\"],\"*\",[4,\"-\",[5,\"1\"]]],[1,\"jf\",\"<\",[5,\"0.5\"]]]]],[13,\"R\",[6,\"std.select\",[\"normalR\",[1,[7,\"selfVel\",\"x\"],\"*\",[4,\"-\",[5,\"1\"]]],[1,\"jf\",\">\",[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1.5\"]]]]]]]],[13,\"T\",[6,\"std.select\",[\"normalU\",[1,[7,\"selfVel\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[1,\"iF\",\"<\",[5,\"0.5\"]]]]],[13,\"B\",[6,\"std.select\",[\"normalD\",[1,[7,\"selfVel\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[1,\"iF\",\">\",[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1.5\"]]]]]]]],[13,\"div\",[1,[1,[1,\"R\",\"-\",\"L\"],\"+\",[1,\"B\",\"-\",\"T\"]],\"*\",[5,\"0.5\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"=\",[1,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"*\",\"pressureDecay\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"divergence\"],\"idx\"],\"=\",\"div\"]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,\"d.f32\":()=>n,layout:()=>s,nb:()=>l,\"std.select\":()=>N,pressureDecay:()=>c}})&&R.f)({})).$name(`${o}Divergence`),g=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=s.$.pressure[l(D,O,0,-1)],q=s.$.pressure[l(D,O,0,1)],Y=s.$.pressure[l(D,O,-1,0)],V=s.$.pressure[l(D,O,1,0)],X=s.$.divergence[k];s.$.pressure[k]=__tsover_mul(__tsover_sub(__tsover_add(__tsover_add(__tsover_add(F,q),Y),V),X),.25)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"pL\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]]],[13,\"pR\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]]],[13,\"pU\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]]],[13,\"pD\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]]],[13,\"div\",[8,[7,[7,\"layout\",\"$\"],\"divergence\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"=\",[1,[1,[1,[1,[1,\"pL\",\"+\",\"pR\"],\"+\",\"pU\"],\"+\",\"pD\"],\"-\",\"div\"],\"*\",[5,\"0.25\"]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,layout:()=>s,nb:()=>l}})&&R.f)({})).$name(`${o}Jacobi`),y=t.velocityCap,b=y===void 0?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=s.$.pressure[l(D,O,0,-1)],q=s.$.pressure[l(D,O,0,1)],Y=s.$.pressure[l(D,O,-1,0)],V=s.$.pressure[l(D,O,1,0)],X=s.$.velA[k];s.$.velA[k]=S(__tsover_sub(X.x,__tsover_mul(__tsover_sub(q,F),.5)),__tsover_sub(X.y,__tsover_mul(__tsover_sub(V,Y),.5)),X.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"pL\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]]],[13,\"pR\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]]],[13,\"pU\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]]],[13,\"pD\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[7,\"vel\",\"x\"],\"-\",[1,[1,\"pR\",\"-\",\"pL\"],\"*\",[5,\"0.5\"]]],[1,[7,\"vel\",\"y\"],\"-\",[1,[1,\"pD\",\"-\",\"pU\"],\"*\",[5,\"0.5\"]]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,layout:()=>s,nb:()=>l,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}GradSubtract`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=j(L),O=j(U),F=s.$.pressure[l(D,O,0,-1)],q=s.$.pressure[l(D,O,0,1)],Y=s.$.pressure[l(D,O,-1,0)],V=s.$.pressure[l(D,O,1,0)],X=s.$.velA[k],Q=__tsover_sub(X.x,__tsover_mul(__tsover_sub(q,F),.5)),te=__tsover_sub(X.y,__tsover_mul(__tsover_sub(V,Y),.5)),J=n(y),oe=B(Ue(__tsover_add(__tsover_mul(Q,Q),__tsover_mul(te,te))),n(1e-6)),le=__tsover_div(J,B(oe,J));s.$.velA[k]=S(__tsover_mul(Q,le),__tsover_mul(te,le),X.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"pL\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]]],[13,\"pR\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]]],[13,\"pU\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]]],[13,\"pD\",[8,[7,[7,\"layout\",\"$\"],\"pressure\"],[6,\"nb\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"vx\",[1,[7,\"vel\",\"x\"],\"-\",[1,[1,\"pR\",\"-\",\"pL\"],\"*\",[5,\"0.5\"]]]],[13,\"vy\",[1,[7,\"vel\",\"y\"],\"-\",[1,[1,\"pD\",\"-\",\"pU\"],\"*\",[5,\"0.5\"]]]],[13,\"maxVel\",[6,\"d.f32\",[\"velocityCap\"]]],[13,\"vLen\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"vx\",\"*\",\"vx\"],\"+\",[1,\"vy\",\"*\",\"vy\"]]]],[6,\"d.f32\",[[5,\"0.000001\"]]]]]],[13,\"capScale\",[1,\"maxVel\",\"/\",[6,\"std.max\",[\"vLen\",\"maxVel\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,\"vx\",\"*\",\"capScale\"],[1,\"vy\",\"*\",\"capScale\"],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,\"d.i32\":()=>j,layout:()=>s,nb:()=>l,\"d.f32\":()=>n,velocityCap:()=>y,\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}GradSubtract`),x=t.solidMask??!1,P={curl:p,vorticity:f,divergence:h,jacobi:g,gradSubtract:b,advectVel:d?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=__tsover_mod(__tsover_add(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),n(r)),n(r)),q=__tsover_mod(__tsover_add(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),n(r)),n(r)),Y=ie(F),V=ie(q),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=__tsover_mod(__tsover_add(te,1),r),le=__tsover_mod(__tsover_add(J,1),r),se=s.$.velA[M(__tsover_add(__tsover_mul(J,r),te))],Te=s.$.velA[M(__tsover_add(__tsover_mul(J,r),oe))],de=s.$.velA[M(__tsover_add(__tsover_mul(le,r),te))],xe=s.$.velA[M(__tsover_add(__tsover_mul(le,r),oe))],Ae=__tsover_add(1,__tsover_mul(k.velFade,k.dt));s.$.velB[D]=S(__tsover_div(K(K(se.x,Te.x,X),K(de.x,xe.x,X),Q),Ae),__tsover_div(K(K(se.y,Te.y,X),K(de.y,xe.y,X),Q),Ae),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],\"+\",[6,\"d.f32\",[\"n\"]]],\"%\",[6,\"d.f32\",[\"n\"]]]],[13,\"srcY\",[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],\"+\",[6,\"d.f32\",[\"n\"]]],\"%\",[6,\"d.f32\",[\"n\"]]]],[13,\"x0\",[6,\"std.floor\",[\"srcX\"]]],[13,\"y0\",[6,\"std.floor\",[\"srcY\"]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"ix1\",[1,[1,\"ix0\",\"+\",[5,\"1\"]],\"%\",\"n\"]],[13,\"iy1\",[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"%\",\"n\"]],[13,\"v00\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v10\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix1\"]]]]],[13,\"v01\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy1\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v11\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy1\",\"*\",\"n\"],\"+\",\"ix1\"]]]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"velFade\"],\"*\",[7,\"p\",\"dt\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"x\"],[7,\"v10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"x\"],[7,\"v11\",\"x\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"y\"],[7,\"v10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"y\"],[7,\"v11\",\"y\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&R.f)({})).$name(`${o}AdvectVel`):x?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.velA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.velA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.velA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.velA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))],de=__tsover_add(1,__tsover_mul(k.velFade,k.dt)),xe=s.$.maskBuf[D].x;s.$.velB[D]=S(__tsover_mul(__tsover_div(K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),de),xe),__tsover_mul(__tsover_div(K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),de),xe),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"v00\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v10\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"v01\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v11\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"velFade\"],\"*\",[7,\"p\",\"dt\"]]]],[13,\"m\",[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],\"idx\"],\"x\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"x\"],[7,\"v10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"x\"],[7,\"v11\",\"x\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],\"*\",\"m\"],[1,[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"y\"],[7,\"v10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"y\"],[7,\"v11\",\"y\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],\"*\",\"m\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&R.f)({})).$name(`${o}AdvectVel`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.velA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.velA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.velA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.velA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))],de=__tsover_add(1,__tsover_mul(k.velFade,k.dt));s.$.velB[D]=S(__tsover_div(K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),de),__tsover_div(K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),de),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"v00\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v10\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"v01\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"v11\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"velFade\"],\"*\",[7,\"p\",\"dt\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"x\"],[7,\"v10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"x\"],[7,\"v11\",\"x\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],[1,[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"y\"],[7,\"v10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"v01\",\"y\"],[7,\"v11\",\"y\"],\"sx\"]],\"sy\"]],\"/\",\"decay\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&R.f)({})).$name(`${o}AdvectVel`),copyVel:t.publishVelocityTexture?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=s.$.velB[k];s.$.velA[k]=S(D.x,D.y,0,0),lt(s.$.velOutTex,fe(U,L),S(D.x,D.y,0,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"v\",[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"v\",\"x\"],[7,\"v\",\"y\"],[5,\"0\"],[5,\"0\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"velOutTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"v\",\"x\"],[7,\"v\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,layout:()=>s,\"d.vec4f\":()=>S,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe}})&&R.f)({})).$name(`${o}CopyVel`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=s.$.velB[k];s.$.velA[k]=S(D.x,D.y,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"v\",[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"v\",\"x\"],[7,\"v\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,layout:()=>s,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}CopyVel`)};if(i===\"none\")return P;let C=t.dyeDissipation??!1,E=t.ageAdvance??!1;return i===\"rgb\"?(P.advectDye=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.dyeA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))],de=K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),xe=K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),Ae=K(K(oe.z,le.z,X),K(se.z,Te.z,X),Q),De=__tsover_add(1,__tsover_mul(k.dyeFade,k.dt));s.$.dyeB[D]=S(B(__tsover_div(de,De),n(0)),B(__tsover_div(xe,De),n(0)),B(__tsover_div(Ae,De),n(0)),0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"c00\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"c10\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"c01\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"c11\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"r\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"c00\",\"x\"],[7,\"c10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"c01\",\"x\"],[7,\"c11\",\"x\"],\"sx\"]],\"sy\"]]],[13,\"g\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"c00\",\"y\"],[7,\"c10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"c01\",\"y\"],[7,\"c11\",\"y\"],\"sx\"]],\"sy\"]]],[13,\"b\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"c00\",\"z\"],[7,\"c10\",\"z\"],\"sx\"]],[6,\"std.mix\",[[7,\"c01\",\"z\"],[7,\"c11\",\"z\"],\"sx\"]],\"sy\"]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"dyeFade\"],\"*\",[7,\"p\",\"dt\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.max\",[[1,\"r\",\"/\",\"decay\"],[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[[1,\"g\",\"/\",\"decay\"],[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.max\",[[1,\"b\",\"/\",\"decay\"],[6,\"d.f32\",[[5,\"0\"]]]]],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"std.mix\":()=>K,\"d.vec4f\":()=>S,\"std.max\":()=>B}})&&R.f)({})).$name(`${o}Advect${u}`),P.copyDye=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=s.$.dyeB[k];s.$.dyeA[k]=S(D.x,D.y,D.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"c\",[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"c\",\"x\"],[7,\"c\",\"y\"],[7,\"c\",\"z\"],[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,layout:()=>s,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}Copy${u}`),P):(C&&E&&x?P.advectDye=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.dyeA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))],de=K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),xe=K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),Ae=__tsover_add(1,__tsover_mul(k.dyeFade,k.dt)),De=s.$.maskBuf[D].x;s.$.dyeB[D]=S(__tsover_mul(B(__tsover_div(de,Ae),n(0)),De),ye(__tsover_add(xe,__tsover_mul(__tsover_mul(k.dt,.4),k.colorDecay)),1),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"d00\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d10\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"d01\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d11\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"dens\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"x\"],[7,\"d10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"x\"],[7,\"d11\",\"x\"],\"sx\"]],\"sy\"]]],[13,\"age\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"y\"],[7,\"d10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"y\"],[7,\"d11\",\"y\"],\"sx\"]],\"sy\"]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"dyeFade\"],\"*\",[7,\"p\",\"dt\"]]]],[13,\"m\",[7,[8,[7,[7,\"layout\",\"$\"],\"maskBuf\"],\"idx\"],\"x\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[6,\"std.max\",[[1,\"dens\",\"/\",\"decay\"],[6,\"d.f32\",[[5,\"0\"]]]]],\"*\",\"m\"],[6,\"std.min\",[[1,\"age\",\"+\",[1,[1,[7,\"p\",\"dt\"],\"*\",[5,\"0.4\"]],\"*\",[7,\"p\",\"colorDecay\"]]],[5,\"1\"]]],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"std.mix\":()=>K,\"d.vec4f\":()=>S,\"std.max\":()=>B,\"std.min\":()=>ye}})&&R.f)({})).$name(`${o}Advect${u}`):C&&E?P.advectDye=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.dyeA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))],de=K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),xe=K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),Ae=__tsover_add(1,__tsover_mul(k.dyeFade,k.dt));s.$.dyeB[D]=S(B(__tsover_div(de,Ae),n(0)),ye(__tsover_add(xe,__tsover_mul(__tsover_mul(k.dt,.4),k.colorDecay)),1),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"d00\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d10\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"d01\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d11\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"dens\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"x\"],[7,\"d10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"x\"],[7,\"d11\",\"x\"],\"sx\"]],\"sy\"]]],[13,\"age\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"y\"],[7,\"d10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"y\"],[7,\"d11\",\"y\"],\"sx\"]],\"sy\"]]],[13,\"decay\",[1,[5,\"1\"],\"+\",[1,[7,\"p\",\"dyeFade\"],\"*\",[7,\"p\",\"dt\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.max\",[[1,\"dens\",\"/\",\"decay\"],[6,\"d.f32\",[[5,\"0\"]]]]],[6,\"std.min\",[[1,\"age\",\"+\",[1,[1,[7,\"p\",\"dt\"],\"*\",[5,\"0.4\"]],\"*\",[7,\"p\",\"colorDecay\"]]],[5,\"1\"]]],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"std.mix\":()=>K,\"d.vec4f\":()=>S,\"std.max\":()=>B,\"std.min\":()=>ye}})&&R.f)({})).$name(`${o}Advect${u}`):P.advectDye=d?w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=__tsover_mod(__tsover_add(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),n(r)),n(r)),q=__tsover_mod(__tsover_add(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),n(r)),n(r)),Y=ie(F),V=ie(q),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=__tsover_mod(__tsover_add(te,1),r),le=__tsover_mod(__tsover_add(J,1),r),se=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),te))],Te=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),oe))],de=s.$.dyeA[M(__tsover_add(__tsover_mul(le,r),te))],xe=s.$.dyeA[M(__tsover_add(__tsover_mul(le,r),oe))];s.$.dyeB[D]=S(K(K(se.x,Te.x,X),K(de.x,xe.x,X),Q),K(K(se.y,Te.y,X),K(de.y,xe.y,X),Q),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[1,[1,[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],\"+\",[6,\"d.f32\",[\"n\"]]],\"%\",[6,\"d.f32\",[\"n\"]]]],[13,\"srcY\",[1,[1,[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],\"+\",[6,\"d.f32\",[\"n\"]]],\"%\",[6,\"d.f32\",[\"n\"]]]],[13,\"x0\",[6,\"std.floor\",[\"srcX\"]]],[13,\"y0\",[6,\"std.floor\",[\"srcY\"]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"ix1\",[1,[1,\"ix0\",\"+\",[5,\"1\"]],\"%\",\"n\"]],[13,\"iy1\",[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"%\",\"n\"]],[13,\"d00\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d10\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix1\"]]]]],[13,\"d01\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy1\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d11\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy1\",\"*\",\"n\"],\"+\",\"ix1\"]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"x\"],[7,\"d10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"x\"],[7,\"d11\",\"x\"],\"sx\"]],\"sy\"]],[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"y\"],[7,\"d10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"y\"],[7,\"d11\",\"y\"],\"sx\"]],\"sy\"]],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&R.f)({})).$name(`${o}Advect${u}`):w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=s.$.params,D=__tsover_add(__tsover_mul(L,M(r)),U),O=s.$.velA[D],F=G(__tsover_sub(n(U),__tsover_mul(O.x,k.dt)),0,n(__tsover_sub(r,1))),q=G(__tsover_sub(n(L),__tsover_mul(O.y,k.dt)),0,n(__tsover_sub(r,1))),Y=G(ie(F),0,n(__tsover_sub(r,2))),V=G(ie(q),0,n(__tsover_sub(r,2))),X=__tsover_sub(F,Y),Q=__tsover_sub(q,V),te=j(Y),J=j(V),oe=s.$.dyeA[M(__tsover_add(__tsover_mul(J,r),te))],le=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(J,r),te),1))],se=s.$.dyeA[M(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te))],Te=s.$.dyeA[M(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(J,1),r),te),1))];s.$.dyeB[D]=S(K(K(oe.x,le.x,X),K(se.x,Te.x,X),Q),K(K(oe.y,le.y,X),K(se.y,Te.y,X),Q),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"srcX\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cx\"]],\"-\",[1,[7,\"vel\",\"x\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"srcY\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"cy\"]],\"-\",[1,[7,\"vel\",\"y\"],\"*\",[7,\"p\",\"dt\"]]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"1\"]]]]]]],[13,\"x0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcX\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"y0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"srcY\"]],[5,\"0\"],[6,\"d.f32\",[[1,\"n\",\"-\",[5,\"2\"]]]]]]],[13,\"sx\",[1,\"srcX\",\"-\",\"x0\"]],[13,\"sy\",[1,\"srcY\",\"-\",\"y0\"]],[13,\"ix0\",[6,\"d.i32\",[\"x0\"]]],[13,\"iy0\",[6,\"d.i32\",[\"y0\"]]],[13,\"d00\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d10\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[13,\"d01\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"]]]]],[13,\"d11\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],[6,\"d.u32\",[[1,[1,[1,[1,\"iy0\",\"+\",[5,\"1\"]],\"*\",\"n\"],\"+\",\"ix0\"],\"+\",[5,\"1\"]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"x\"],[7,\"d10\",\"x\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"x\"],[7,\"d11\",\"x\"],\"sx\"]],\"sy\"]],[6,\"std.mix\",[[6,\"std.mix\",[[7,\"d00\",\"y\"],[7,\"d10\",\"y\"],\"sx\"]],[6,\"std.mix\",[[7,\"d01\",\"y\"],[7,\"d11\",\"y\"],\"sx\"]],\"sy\"]],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&R.f)({})).$name(`${o}Advect${u}`),P.copyDye=w.fn([M,M])((R=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(R.f=((U,L)=>{\"use gpu\";let k=__tsover_add(__tsover_mul(L,M(r)),U),D=s.$.dyeB[k];s.$.dyeA[k]=S(D.x,D.y,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"dd\",[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"dd\",\"x\"],[7,\"dd\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{\"d.u32\":()=>M,n:()=>r,layout:()=>s,\"d.vec4f\":()=>S}})&&R.f)({})).$name(`${o}Copy${u}`),P)}function Ui(e,t){let{n:s,namePrefix:r}=t;return t.dye===\"rgb\"?w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=__tsover_add(__tsover_mul(i,M(s)),a),c=e.$.dyeA[l];lt(e.$.outTex,fe(a,i),S(c.x,c.y,c.z,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"c\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"c\",\"x\"],[7,\"c\",\"y\"],[7,\"c\",\"z\"],[5,\"1\"]]]]]]]},externals:{\"d.u32\":()=>M,n:()=>s,layout:()=>e,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(`${r}Output`):w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=__tsover_add(__tsover_mul(i,M(s)),a),c=e.$.dyeA[l];lt(e.$.outTex,fe(a,i),S(c.x,c.y,0,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"dd\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"dd\",\"x\"],[7,\"dd\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]]},externals:{\"d.u32\":()=>M,n:()=>s,layout:()=>e,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(`${r}Output`)}var mm=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Ys(m(t,s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"px\"},{type:\"i\",name:\"py\"}],body:[0,[[10,[6,\"noise.hash12\",[[6,\"d.vec2f\",[\"px\",\"py\"]]]]]]]},externals:{\"noise.hash12\":()=>Ys,\"d.vec2f\":()=>m}})&&e.f)({})),g1=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=ie(t),o=ie(s),a=Re(t),i=Re(s),l=__tsover_mul(__tsover_mul(a,a),__tsover_sub(3,__tsover_mul(a,2))),c=__tsover_mul(__tsover_mul(i,i),__tsover_sub(3,__tsover_mul(i,2)));return K(K(mm(r,o),mm(__tsover_add(r,1),o),l),K(mm(r,__tsover_add(o,1)),mm(__tsover_add(r,1),__tsover_add(o,1)),l),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"nx\"},{type:\"i\",name:\"ny\"}],body:[0,[[13,\"ix\",[6,\"std.floor\",[\"nx\"]]],[13,\"iy\",[6,\"std.floor\",[\"ny\"]]],[13,\"sx\",[6,\"std.fract\",[\"nx\"]]],[13,\"sy\",[6,\"std.fract\",[\"ny\"]]],[13,\"ux\",[1,[1,\"sx\",\"*\",\"sx\"],\"*\",[1,[5,\"3\"],\"-\",[1,\"sx\",\"*\",[5,\"2\"]]]]],[13,\"uy\",[1,[1,\"sy\",\"*\",\"sy\"],\"*\",[1,[5,\"3\"],\"-\",[1,\"sy\",\"*\",[5,\"2\"]]]]],[10,[6,\"std.mix\",[[6,\"std.mix\",[[6,\"valueHash\",[\"ix\",\"iy\"]],[6,\"valueHash\",[[1,\"ix\",\"+\",[5,\"1\"]],\"iy\"]],\"ux\"]],[6,\"std.mix\",[[6,\"valueHash\",[\"ix\",[1,\"iy\",\"+\",[5,\"1\"]]]],[6,\"valueHash\",[[1,\"ix\",\"+\",[5,\"1\"]],[1,\"iy\",\"+\",[5,\"1\"]]]],\"ux\"]],\"uy\"]]]]]},externals:{\"std.floor\":()=>ie,\"std.fract\":()=>Re,\"std.mix\":()=>K,valueHash:()=>mm}})&&e.f)({})),Um=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_add(__tsover_add(__tsover_mul(g1(t,s),.5),__tsover_mul(g1(__tsover_add(__tsover_mul(t,2),5.37),__tsover_add(__tsover_mul(s,2),9.13)),.3)),__tsover_mul(g1(__tsover_add(__tsover_mul(t,4),1.79),__tsover_add(__tsover_mul(s,4),3.51)),.2))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"nx\"},{type:\"i\",name:\"ny\"}],body:[0,[[10,[1,[1,[1,[6,\"valueNoise2D\",[\"nx\",\"ny\"]],\"*\",[5,\"0.5\"]],\"+\",[1,[6,\"valueNoise2D\",[[1,[1,\"nx\",\"*\",[5,\"2\"]],\"+\",[5,\"5.37\"]],[1,[1,\"ny\",\"*\",[5,\"2\"]],\"+\",[5,\"9.13\"]]]],\"*\",[5,\"0.3\"]]],\"+\",[1,[6,\"valueNoise2D\",[[1,[1,\"nx\",\"*\",[5,\"4\"]],\"+\",[5,\"1.79\"]],[1,[1,\"ny\",\"*\",[5,\"4\"]],\"+\",[5,\"3.51\"]]]],\"*\",[5,\"0.2\"]]]]]]},externals:{valueNoise2D:()=>g1}})&&e.f)({})),ih=w.fn([n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(Xs(__tsover_mul(r,37.31)),97),i=__tsover_mul(Xs(__tsover_mul(r,83.17)),97);return m(__tsover_add(__tsover_mul(t,o),a),__tsover_add(__tsover_mul(s,o),i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"cellsPerTexel\"}],body:[0,[[13,\"sdx\",[1,[6,\"noise.hash11\",[[1,\"seed\",\"*\",[5,\"37.31\"]]]],\"*\",[5,\"97\"]]],[13,\"sdy\",[1,[6,\"noise.hash11\",[[1,\"seed\",\"*\",[5,\"83.17\"]]]],\"*\",[5,\"97\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"x\",\"*\",\"cellsPerTexel\"],\"+\",\"sdx\"],[1,[1,\"y\",\"*\",\"cellsPerTexel\"],\"+\",\"sdy\"]]]]]]},externals:{\"noise.hash11\":()=>Xs,\"d.vec2f\":()=>m}})&&e.f)({}));function Pz(e,t){let s=e,{n:r,namePrefix:o}=t,a=(t.frequency??3)/r,i=t.density??{scale:.75,bias:.25,min:.15,max:.9},l=i.scale,c=i.bias,d=i.min,u=i.max;return w.fn([M,M])((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";let h=s.$.params,g=__tsover_add(__tsover_mul(f,M(r)),_),y=ih(n(_),n(f),h.seed,a),b=G(__tsover_add(__tsover_mul(Um(__tsover_add(y.x,31.7),__tsover_add(y.y,53.3)),l),c),d,u),x=G(Um(y.x,y.y),0,1);s.$.dyeA[g]=S(b,x,0,0),s.$.dyeB[g]=S(b,x,0,0),s.$.velA[g]=S(0,0,0,0),s.$.velB[g]=S(0,0,0,0),s.$.pressure[g]=0,s.$.divergence[g]=0}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"lat\",[6,\"seedLattice\",[[6,\"d.f32\",[\"cx\"]],[6,\"d.f32\",[\"cy\"]],[7,\"p\",\"seed\"],\"cellsPerTexel\"]]],[13,\"dens0\",[6,\"std.clamp\",[[1,[1,[6,\"fbm\",[[1,[7,\"lat\",\"x\"],\"+\",[5,\"31.7\"]],[1,[7,\"lat\",\"y\"],\"+\",[5,\"53.3\"]]]],\"*\",\"dScale\"],\"+\",\"dBias\"],\"dMin\",\"dMax\"]]],[13,\"colorVar\",[6,\"std.clamp\",[[6,\"fbm\",[[7,\"lat\",\"x\"],[7,\"lat\",\"y\"]]],[5,\"0\"],[5,\"1\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"dens0\",\"colorVar\",[5,\"0\"],[5,\"0\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"dens0\",\"colorVar\",[5,\"0\"],[5,\"0\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velB\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pressure\"],\"idx\"],\"=\",[5,\"0\"]],[2,[8,[7,[7,\"layout\",\"$\"],\"divergence\"],\"idx\"],\"=\",[5,\"0\"]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,seedLattice:()=>ih,\"d.f32\":()=>n,cellsPerTexel:()=>a,\"std.clamp\":()=>G,fbm:()=>Um,dScale:()=>l,dBias:()=>c,dMin:()=>d,dMax:()=>u,\"d.vec4f\":()=>S}})&&p.f)({})).$name(`${o}Init`)}var $0=Jt;function Cz(e,t){let s=e,{n:r,namePrefix:o}=t,a=r*(t.gain??.04);return w.fn([M,M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c)=>{\"use gpu\";let d=s.$.params,u=__tsover_add(__tsover_mul(c,M(r)),l),p=__tsover_add(n(l),.5),_=__tsover_add(n(c),.5),f=__tsover_mul(__tsover_div(p,n(r)),$0),h=__tsover_mul(__tsover_div(_,n(r)),$0),g=d.time,y=s.$.velA[u],b=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(f,2),__tsover_mul(g,.31))),pe(__tsover_add(__tsover_mul(h,1.5),__tsover_mul(g,.23)))),__tsover_mul(ae(__tsover_add(__tsover_add(__tsover_mul(f,.7),__tsover_mul(h,1.3)),__tsover_mul(g,.17))),.4)),d.turbulence),a),d.dt),x=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_add(__tsover_mul(pe(__tsover_add(__tsover_mul(f,1.5),__tsover_mul(g,.27))),ae(__tsover_add(__tsover_mul(h,2),__tsover_mul(g,.19)))),__tsover_mul(pe(__tsover_add(__tsover_add(__tsover_mul(f,1.1),__tsover_mul(h,.9)),__tsover_mul(g,.21))),.4)),d.turbulence),a),d.dt),P=__tsover_sub(p,d.cursorX),C=__tsover_sub(_,d.cursorY),E=__tsover_add(__tsover_mul(P,P),__tsover_mul(C,C)),R=__tsover_mul(ga(E,B(d.mouseRadSq,n(1))),d.mouseActive);s.$.velA[u]=S(__tsover_add(__tsover_add(y.x,b),__tsover_mul(d.cursorVelX,R)),__tsover_add(__tsover_add(y.y,x),__tsover_mul(d.cursorVelY,R)),y.z,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"jf\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"iF\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"x\",[1,[1,\"jf\",\"/\",[6,\"d.f32\",[\"n\"]]],\"*\",\"TAU\"]],[13,\"y\",[1,[1,\"iF\",\"/\",[6,\"d.f32\",[\"n\"]]],\"*\",\"TAU\"]],[13,\"t\",[7,\"p\",\"time\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"]],[13,\"fx\",[1,[1,[1,[1,[1,[6,\"std.sin\",[[1,[1,\"x\",\"*\",[5,\"2\"]],\"+\",[1,\"t\",\"*\",[5,\"0.31\"]]]]],\"*\",[6,\"std.cos\",[[1,[1,\"y\",\"*\",[5,\"1.5\"]],\"+\",[1,\"t\",\"*\",[5,\"0.23\"]]]]]],\"+\",[1,[6,\"std.sin\",[[1,[1,[1,\"x\",\"*\",[5,\"0.7\"]],\"+\",[1,\"y\",\"*\",[5,\"1.3\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.17\"]]]]],\"*\",[5,\"0.4\"]]],\"*\",[7,\"p\",\"turbulence\"]],\"*\",\"gain\"],\"*\",[7,\"p\",\"dt\"]]],[13,\"fy\",[1,[1,[1,[1,[1,[6,\"std.cos\",[[1,[1,\"x\",\"*\",[5,\"1.5\"]],\"+\",[1,\"t\",\"*\",[5,\"0.27\"]]]]],\"*\",[6,\"std.sin\",[[1,[1,\"y\",\"*\",[5,\"2\"]],\"+\",[1,\"t\",\"*\",[5,\"0.19\"]]]]]],\"+\",[1,[6,\"std.cos\",[[1,[1,[1,\"x\",\"*\",[5,\"1.1\"]],\"+\",[1,\"y\",\"*\",[5,\"0.9\"]]],\"+\",[1,\"t\",\"*\",[5,\"0.21\"]]]]],\"*\",[5,\"0.4\"]]],\"*\",[7,\"p\",\"turbulence\"]],\"*\",\"gain\"],\"*\",[7,\"p\",\"dt\"]]],[13,\"cdx\",[1,\"jf\",\"-\",[7,\"p\",\"cursorX\"]]],[13,\"cdy\",[1,\"iF\",\"-\",[7,\"p\",\"cursorY\"]]],[13,\"cursorDistSq\",[1,[1,\"cdx\",\"*\",\"cdx\"],\"+\",[1,\"cdy\",\"*\",\"cdy\"]]],[13,\"cursorInf\",[1,[6,\"gaussianBrushSq\",[\"cursorDistSq\",[6,\"std.max\",[[7,\"p\",\"mouseRadSq\"],[6,\"d.f32\",[[5,\"1\"]]]]]]],\"*\",[7,\"p\",\"mouseActive\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"velA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[1,[1,[7,\"vel\",\"x\"],\"+\",\"fx\"],\"+\",[1,[7,\"p\",\"cursorVelX\"],\"*\",\"cursorInf\"]],[1,[1,[7,\"vel\",\"y\"],\"+\",\"fy\"],\"+\",[1,[7,\"p\",\"cursorVelY\"],\"*\",\"cursorInf\"]],[7,\"vel\",\"z\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,\"d.f32\":()=>n,TAU:()=>$0,\"std.sin\":()=>ae,\"std.cos\":()=>pe,gain:()=>a,gaussianBrushSq:()=>ga,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&i.f)({})).$name(`${o}Force`)}function Mz(e,t){let s=e,{n:r,namePrefix:o}=t,a=(t.frequency??3)/r;return w.fn([M,M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c)=>{\"use gpu\";let d=s.$.params,u=__tsover_add(__tsover_mul(c,M(r)),l),p=ih(n(l),n(c),d.seed,a),_=Um(p.x,p.y),f=__tsover_sub(1,d.blending),h=__tsover_add(1,__tsover_mul(f,5)),g=G(__tsover_add(__tsover_mul(__tsover_sub(_,.5),h),.5),0,1),y=s.$.dyeA[u],b=__tsover_add(1,__tsover_mul(f,2.5)),x=ye(__tsover_mul(__tsover_mul(d.dt,.5),b),.3);s.$.dyeA[u]=S(y.x,K(y.y,g,x),0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"lat\",[6,\"seedLattice\",[[6,\"d.f32\",[\"cx\"]],[6,\"d.f32\",[\"cy\"]],[7,\"p\",\"seed\"],\"cellsPerTexel\"]]],[13,\"refRaw\",[6,\"fbm\",[[7,\"lat\",\"x\"],[7,\"lat\",\"y\"]]]],[13,\"invBlend\",[1,[5,\"1\"],\"-\",[7,\"p\",\"blending\"]]],[13,\"contrastBoost\",[1,[5,\"1\"],\"+\",[1,\"invBlend\",\"*\",[5,\"5\"]]]],[13,\"refBoosted\",[6,\"std.clamp\",[[1,[1,[1,\"refRaw\",\"-\",[5,\"0.5\"]],\"*\",\"contrastBoost\"],\"+\",[5,\"0.5\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"cell\",[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"]],[13,\"rateMult\",[1,[5,\"1\"],\"+\",[1,\"invBlend\",\"*\",[5,\"2.5\"]]]],[13,\"blendRate\",[6,\"std.min\",[[1,[1,[7,\"p\",\"dt\"],\"*\",[5,\"0.5\"]],\"*\",\"rateMult\"],[5,\"0.3\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"dyeA\"],\"idx\"],\"=\",[6,\"d.vec4f\",[[7,\"cell\",\"x\"],[6,\"std.mix\",[[7,\"cell\",\"y\"],\"refBoosted\",\"blendRate\"]],[5,\"0\"],[5,\"0\"]]]]]]},externals:{layout:()=>s,\"d.u32\":()=>M,n:()=>r,seedLattice:()=>ih,\"d.f32\":()=>n,cellsPerTexel:()=>a,fbm:()=>Um,\"std.clamp\":()=>G,\"std.min\":()=>ye,\"d.vec4f\":()=>S,\"std.mix\":()=>K}})&&i.f)({})).$name(`${o}ColorRestore`)}var y1=512,cu=w.fn([n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=__tsover_mul(__tsover_sub(t,r),l),d=__tsover_sub(s,o),u=pe(i),p=ae(i),_=__tsover_add(__tsover_mul(c,u),__tsover_mul(d,p)),f=__tsover_sub(__tsover_mul(d,u),__tsover_mul(c,p));return m(__tsover_add(__tsover_div(_,a),.5),__tsover_add(__tsover_div(f,a),.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"u\"},{type:\"i\",name:\"v\"},{type:\"i\",name:\"centerX\"},{type:\"i\",name:\"centerY\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"rotation\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"dxAc\",[1,[1,\"u\",\"-\",\"centerX\"],\"*\",\"aspect\"]],[13,\"dy\",[1,\"v\",\"-\",\"centerY\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotation\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotation\"]]],[13,\"rdx\",[1,[1,\"dxAc\",\"*\",\"cosR\"],\"+\",[1,\"dy\",\"*\",\"sinR\"]]],[13,\"rdy\",[1,[1,\"dy\",\"*\",\"cosR\"],\"-\",[1,\"dxAc\",\"*\",\"sinR\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"rdx\",\"/\",\"scale\"],\"+\",[5,\"0.5\"]],[1,[1,\"rdy\",\"/\",\"scale\"],\"+\",[5,\"0.5\"]]]]]]]},externals:{\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"d.vec2f\":()=>m}})&&e.f)({}));gt({centerX:n,centerY:n,scale:n,rotation:n,aspect:n,saRadius:n,saSides:n,saRounding:n,saInnerRatio:n,saRotation:n,saHeight:n,saOffset:n,saAperture:n,vfOriginX:n,vfOriginY:n,vfSpanX:n,vfSpanY:n,vfActiveRes:n});function Ez(e,t){let{n:s,namePrefix:r}=t,o=e,a={storage:Fe(S,s*s),access:\"mutable\"},i=(globalThis.__TYPEGPU_AUTONAME__??(u=>u))(w.bindGroupLayout({maskBuf:a,params:{uniform:o}}),\"analyticLayout\"),l=(globalThis.__TYPEGPU_AUTONAME__??(u=>u))(w.bindGroupLayout({maskBuf:a,params:{uniform:o},sdfSource:{texture:Zt(n)}}),\"svgLayout\"),c=(globalThis.__TYPEGPU_AUTONAME__??(u=>u))(w.bindGroupLayout({maskBuf:a,params:{uniform:o},fieldTex:{texture:Zt(n),sampleType:\"unfilterable-float\"}}),\"fieldLayout\");return{analyticLayout:i,svgLayout:l,fieldLayout:c,analytic:u=>{let p=P_(u);return w.fn([M,M])((_=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(_.f=((f,h)=>{\"use gpu\";let g=i.$.params,y=__tsover_add(__tsover_mul(h,M(s)),f),b=p(cu(__tsover_div(__tsover_add(n(f),.5),n(s)),__tsover_div(__tsover_add(n(h),.5),n(s)),g.centerX,g.centerY,g.scale,g.rotation,g.aspect),g.saRadius,g.saSides,g.saRounding,g.saInnerRatio,g.saRotation,g.saHeight,g.saOffset,g.saAperture),x=N(n(0),n(1),b.x<0);i.$.maskBuf[y]=S(x,b.x,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"analyticLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"sdfUV\",[6,\"gridShapeUV\",[\"u\",\"v\",[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"],[7,\"p\",\"scale\"],[7,\"p\",\"rotation\"],[7,\"p\",\"aspect\"]]]],[13,\"res\",[6,\"sdfFn\",[\"sdfUV\",[7,\"p\",\"saRadius\"],[7,\"p\",\"saSides\"],[7,\"p\",\"saRounding\"],[7,\"p\",\"saInnerRatio\"],[7,\"p\",\"saRotation\"],[7,\"p\",\"saHeight\"],[7,\"p\",\"saOffset\"],[7,\"p\",\"saAperture\"]]]],[13,\"inside\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"res\",\"x\"],\"<\",[5,\"0\"]]]]],[2,[8,[7,[7,\"analyticLayout\",\"$\"],\"maskBuf\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"inside\",[7,\"res\",\"x\"],[5,\"0\"],[5,\"0\"]]]]]]},externals:{analyticLayout:()=>i,\"d.u32\":()=>M,n:()=>s,\"d.f32\":()=>n,gridShapeUV:()=>cu,sdfFn:()=>p,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&_.f)({})).$name(`${r}Mask`)},svgKernel:w.fn([M,M])((u=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(u.f=((p,_)=>{\"use gpu\";let f=l.$.params,h=__tsover_add(__tsover_mul(_,M(s)),p),g=cu(__tsover_div(__tsover_add(n(p),.5),n(s)),__tsover_div(__tsover_add(n(_),.5),n(s)),f.centerX,f.centerY,f.scale,f.rotation,f.aspect),y=__tsover_sub(y1,1),b=G(j(__tsover_mul(g.x,y1)),0,y),x=G(j(__tsover_mul(g.y,y1)),0,y),P=Ye(l.$.sdfSource,fe(M(b),M(x)),0).x,C=N(n(0),n(1),P<0);l.$.maskBuf[h]=S(C,P,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"svgLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"sdfUV\",[6,\"gridShapeUV\",[\"u\",\"v\",[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"],[7,\"p\",\"scale\"],[7,\"p\",\"rotation\"],[7,\"p\",\"aspect\"]]]],[13,\"sizeM1\",[1,\"SVG_SDF_SIZE\",\"-\",[5,\"1\"]]],[13,\"tx\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,[7,\"sdfUV\",\"x\"],\"*\",\"SVG_SDF_SIZE\"]]],[5,\"0\"],\"sizeM1\"]]],[13,\"ty\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,[7,\"sdfUV\",\"y\"],\"*\",\"SVG_SDF_SIZE\"]]],[5,\"0\"],\"sizeM1\"]]],[13,\"dist\",[7,[6,\"std.textureLoad\",[[7,[7,\"svgLayout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"tx\"]],[6,\"d.u32\",[\"ty\"]]]],[5,\"0\"]]],\"x\"]],[13,\"inside\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"dist\",\"<\",[5,\"0\"]]]]],[2,[8,[7,[7,\"svgLayout\",\"$\"],\"maskBuf\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"inside\",\"dist\",[5,\"0\"],[5,\"0\"]]]]]]},externals:{svgLayout:()=>l,\"d.u32\":()=>M,n:()=>s,\"d.f32\":()=>n,gridShapeUV:()=>cu,SVG_SDF_SIZE:()=>y1,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&u.f)({})).$name(`${r}SvgMask`),fieldKernel:w.fn([M,M])((u=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(u.f=((p,_)=>{\"use gpu\";let f=c.$.params,h=__tsover_add(__tsover_mul(_,M(s)),p),g=cu(__tsover_div(__tsover_add(n(p),.5),n(s)),__tsover_div(__tsover_add(n(_),.5),n(s)),f.centerX,f.centerY,f.scale,f.rotation,f.aspect),y=G(g.x,f.vfOriginX,__tsover_add(f.vfOriginX,f.vfSpanX)),b=G(g.y,f.vfOriginY,__tsover_add(f.vfOriginY,f.vfSpanY)),x=he(m(__tsover_sub(g.x,y),__tsover_sub(g.y,b))),P=__tsover_sub(f.vfActiveRes,1),C=G(__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(y,f.vfOriginX),f.vfSpanX),f.vfActiveRes),.5),n(0),P),E=G(__tsover_sub(__tsover_mul(__tsover_div(__tsover_sub(b,f.vfOriginY),f.vfSpanY),f.vfActiveRes),.5),n(0),P),R=__tsover_add(Ye(c.$.fieldTex,fe(M(C),M(E)),0).x,x),U=N(n(0),n(1),R<0);c.$.maskBuf[h]=S(U,R,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"fieldLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"sdfUV\",[6,\"gridShapeUV\",[\"u\",\"v\",[7,\"p\",\"centerX\"],[7,\"p\",\"centerY\"],[7,\"p\",\"scale\"],[7,\"p\",\"rotation\"],[7,\"p\",\"aspect\"]]]],[13,\"uxc\",[6,\"std.clamp\",[[7,\"sdfUV\",\"x\"],[7,\"p\",\"vfOriginX\"],[1,[7,\"p\",\"vfOriginX\"],\"+\",[7,\"p\",\"vfSpanX\"]]]]],[13,\"uyc\",[6,\"std.clamp\",[[7,\"sdfUV\",\"y\"],[7,\"p\",\"vfOriginY\"],[1,[7,\"p\",\"vfOriginY\"],\"+\",[7,\"p\",\"vfSpanY\"]]]]],[13,\"outsideDist\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"sdfUV\",\"x\"],\"-\",\"uxc\"],[1,[7,\"sdfUV\",\"y\"],\"-\",\"uyc\"]]]]]],[13,\"maxT\",[1,[7,\"p\",\"vfActiveRes\"],\"-\",[5,\"1\"]]],[13,\"tx\",[6,\"std.clamp\",[[1,[1,[1,[1,\"uxc\",\"-\",[7,\"p\",\"vfOriginX\"]],\"/\",[7,\"p\",\"vfSpanX\"]],\"*\",[7,\"p\",\"vfActiveRes\"]],\"-\",[5,\"0.5\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]],[13,\"ty\",[6,\"std.clamp\",[[1,[1,[1,[1,\"uyc\",\"-\",[7,\"p\",\"vfOriginY\"]],\"/\",[7,\"p\",\"vfSpanY\"]],\"*\",[7,\"p\",\"vfActiveRes\"]],\"-\",[5,\"0.5\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]],[13,\"dist\",[1,[7,[6,\"std.textureLoad\",[[7,[7,\"fieldLayout\",\"$\"],\"fieldTex\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"tx\"]],[6,\"d.u32\",[\"ty\"]]]],[5,\"0\"]]],\"x\"],\"+\",\"outsideDist\"]],[13,\"inside\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"dist\",\"<\",[5,\"0\"]]]]],[2,[8,[7,[7,\"fieldLayout\",\"$\"],\"maskBuf\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"inside\",\"dist\",[5,\"0\"],[5,\"0\"]]]]]]},externals:{fieldLayout:()=>c,\"d.u32\":()=>M,n:()=>s,\"d.f32\":()=>n,gridShapeUV:()=>cu,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.select\":()=>N,\"d.vec4f\":()=>S}})&&u.f)({})).$name(`${r}VfMask`)}}function D_(e,t,s){return wr(e,(r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a)=>{\"use gpu\";t(o,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>t}})&&r.f)({}),{size:[s.n,s.n],bindGroup:s.bindGroup})}function jA(e,t,s){let r=[s.n,s.n],o=i=>wr(e,(l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";i(c,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>i}})&&l.f)({}),{size:r,bindGroup:s.bindGroup}),a={curl:o(t.curl),vorticity:o(t.vorticity),divergence:o(t.divergence),jacobi:o(t.jacobi),gradSubtract:o(t.gradSubtract),advectVel:o(t.advectVel),copyVel:o(t.copyVel),advectDye:t.advectDye?o(t.advectDye):void 0,copyDye:t.copyDye?o(t.copyDye):void 0};return{...a,solveSteps({jacobiIters:i,vorticity:l}){let c=[a.curl,l??a.vorticity,a.divergence];for(let d=0;d<i;d++)c.push(a.jacobi);return c.push(a.gradSubtract,a.advectVel,a.copyVel),a.advectDye&&c.push(a.advectDye),a.copyDye&&c.push(a.copyDye),c}}}var LP=Po;var dd=256*256,od=255;var UP=1-10*.004,kz=UP>=1?1/0:Math.min(3e4,Math.log(1e-6)/Math.log(Math.max(UP,.001))*16.67),Bz=.02,t_=\"rgba16float\",KA=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({amp:n,freq:n,seed:n,edgePin:n,cursorIntensity:n,t:n,aspect:n,focal:n,camPos:A,camForward:A,camRight:A,camUp:A,lighting:n,glossiness:n,highlights:n,lightX:n,lightY:n,lightZ:n,lightColor:A,nearCutoff:n,farCutoff:n,cursorWaveX:n,cursorWaveY:n,mouseSpeed:n,dt:n,decay:n,radius:n,waveSpeed:n,heightEnvelope:n,cursorActive:n}),\"HeightFieldParams\");function zz(e){let t=(globalThis.__TYPEGPU_AUTONAME__??(s=>s))(e.createUniform(KA),\"u\");return{buffer:t.buffer,write:s=>t.write({...s,camPos:A(...s.camPos),camForward:A(...s.camForward),camRight:A(...s.camRight),camUp:A(...s.camUp),lightColor:A(...s.lightColor)})}}var bn=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({readSrc:{storage:Fe(n,dd),access:\"readonly\"},writeBuf:{storage:Fe(n,dd),access:\"mutable\"},readBuf:{storage:Fe(n,dd),access:\"mutable\"},params:{uniform:KA}}),\"waveEquationLayout\"),xn=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({readBuf:{storage:Fe(n,dd),access:\"readonly\"},params:{uniform:KA},uvMaskTex:{storageTexture:zt(t_,\"write-only\")},litTex:{storageTexture:zt(t_,\"write-only\")}}),\"heightFieldMarchLayout\");function Rz(e){return{bufferA:pr(e,n,dd),bufferB:pr(e,n,dd),readBuf:pr(e,n,dd)}}function bie(e,t,s){let r=1/(2-Math.pow(.5,Math.max(t,1)-1));return w.fn([m],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=(a=>{\"use gpu\";let i=xn.$.params,l=a.x,c=a.y,d=i.amp,u=i.freq,p=i.seed,_=n(0);if(e===1){let x=n(0);for(let P=0;P<t;P++){let C=n(P),E=Ia(C),R=Ia(__tsover_mul(C,-1)),U=pe(__tsover_mul(C,1.111)),L=ae(__tsover_mul(C,1.111)),k=__tsover_sub(__tsover_mul(l,U),__tsover_mul(c,L)),D=__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(__tsover_mul(k,u),E),LP),__tsover_mul(i.t,__tsover_add(1,__tsover_mul(C,.2)))),__tsover_mul(p,__tsover_add(1,__tsover_mul(C,.5))));x=__tsover_add(x,__tsover_mul(ae(D),R))}_=__tsover_mul(x,r)}else if(e===2){let x=n(0);for(let C=0;C<t;C++){let E=n(C),R=Ia(E),U=Ia(__tsover_mul(E,-1)),L=pe(__tsover_mul(E,2.39996)),k=ae(__tsover_mul(E,2.39996)),D=m(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(l,u),R),__tsover_mul(i.t,__tsover_mul(L,.3))),p),__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(c,u),R),__tsover_mul(i.t,__tsover_mul(k,.3))),__tsover_mul(p,.7)));x=__tsover_add(x,__tsover_mul(la(D),U))}let P=__tsover_mul(x,r);_=__tsover_sub(__tsover_mul(__tsover_sub(1,re(P)),2),1)}else{let x=__tsover_mul(u,.5),P=la(m(__tsover_add(__tsover_add(__tsover_mul(l,x),__tsover_mul(i.t,.1)),p),__tsover_add(__tsover_mul(c,x),__tsover_mul(p,1.3)))),C=la(m(__tsover_add(__tsover_add(__tsover_mul(l,x),p),17.3),__tsover_add(__tsover_add(__tsover_add(__tsover_mul(c,x),__tsover_mul(i.t,.07)),p),31.1))),E=__tsover_add(l,__tsover_mul(P,.4)),R=__tsover_add(c,__tsover_mul(C,.4)),U=n(0);for(let L=0;L<t;L++){let k=n(L),D=Ia(k),O=Ia(__tsover_mul(k,-1)),F=pe(__tsover_mul(k,2.39996)),q=ae(__tsover_mul(k,2.39996)),Y=m(__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(E,u),D),__tsover_mul(i.t,__tsover_mul(F,.3))),p),__tsover_add(__tsover_add(__tsover_mul(__tsover_mul(R,u),D),__tsover_mul(i.t,__tsover_mul(q,.3))),__tsover_mul(p,.7)));U=__tsover_add(U,__tsover_mul(la(Y),O))}_=__tsover_mul(U,r)}let f=__tsover_mul(__tsover_sub(l,.5),2),h=__tsover_mul(__tsover_sub(c,.5),2),g=be(.6,1,B(re(f),re(h))),y=K(n(1),__tsover_sub(1,g),i.edgePin),b=__tsover_mul(__tsover_mul(_,d),y);if(s){let x=n(0);if(i.cursorActive>.5){let P=n(3),C=__tsover_add(__tsover_div(__tsover_sub(a.x,.5),P),.5),E=__tsover_add(__tsover_div(__tsover_sub(a.y,.5),P),.5),R=n(od),U=__tsover_mul(G(C,0,1),R),L=__tsover_mul(G(E,0,1),R),k=M(G(U,0,R)),D=M(G(L,0,R)),O=ye(__tsover_add(k,M(1)),M(od)),F=ye(__tsover_add(D,M(1)),M(od)),q=__tsover_sub(U,ie(U)),Y=__tsover_sub(L,ie(L)),V=M(256),X=xn.$.readBuf[__tsover_add(__tsover_mul(D,V),k)],Q=xn.$.readBuf[__tsover_add(__tsover_mul(D,V),O)],te=xn.$.readBuf[__tsover_add(__tsover_mul(F,V),k)],J=xn.$.readBuf[__tsover_add(__tsover_mul(F,V),O)],oe=K(K(X,Q,q),K(te,J,q),Y);x=__tsover_mul(oe,__tsover_mul(i.cursorIntensity,.05))}return __tsover_add(b,x)}else return b}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"p\",[7,[7,\"heightFieldMarchLayout\",\"$\"],\"params\"]],[13,\"u\",[7,\"uv\",\"x\"]],[13,\"v\",[7,\"uv\",\"y\"]],[13,\"amp\",[7,\"p\",\"amp\"]],[13,\"freq\",[7,\"p\",\"freq\"]],[13,\"seedU\",[7,\"p\",\"seed\"]],[12,\"waveVal\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"waveType\",\"===\",[5,\"1\"]],[0,[[12,\"accSin\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"octaveCount\"],[102,\"++\",\"i\"],[0,[[13,\"iF\",[6,\"d.f32\",[\"i\"]]],[13,\"scale\",[6,\"std.exp2\",[\"iF\"]]],[13,\"weight\",[6,\"std.exp2\",[[1,\"iF\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"cu\",[6,\"std.cos\",[[1,\"iF\",\"*\",[5,\"1.111\"]]]]],[13,\"su\",[6,\"std.sin\",[[1,\"iF\",\"*\",[5,\"1.111\"]]]]],[13,\"ru\",[1,[1,\"u\",\"*\",\"cu\"],\"-\",[1,\"v\",\"*\",\"su\"]]],[13,\"phase\",[1,[1,[1,[1,[1,\"ru\",\"*\",\"freq\"],\"*\",\"scale\"],\"*\",\"TWO_PI\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[1,[5,\"1\"],\"+\",[1,\"iF\",\"*\",[5,\"0.2\"]]]]],\"+\",[1,\"seedU\",\"*\",[1,[5,\"1\"],\"+\",[1,\"iF\",\"*\",[5,\"0.5\"]]]]]],[2,\"accSin\",\"=\",[1,\"accSin\",\"+\",[1,[6,\"std.sin\",[\"phase\"]],\"*\",\"weight\"]]]]]],[2,\"waveVal\",\"=\",[1,\"accSin\",\"*\",\"invTotalWeight\"]]]],[11,[1,\"waveType\",\"===\",[5,\"2\"]],[0,[[12,\"accR\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"octaveCount\"],[102,\"++\",\"i\"],[0,[[13,\"iF\",[6,\"d.f32\",[\"i\"]]],[13,\"scale\",[6,\"std.exp2\",[\"iF\"]]],[13,\"weight\",[6,\"std.exp2\",[[1,\"iF\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"driftX\",[6,\"std.cos\",[[1,\"iF\",\"*\",[5,\"2.39996\"]]]]],[13,\"driftY\",[6,\"std.sin\",[[1,\"iF\",\"*\",[5,\"2.39996\"]]]]],[13,\"coord\",[6,\"d.vec2f\",[[1,[1,[1,[1,\"u\",\"*\",\"freq\"],\"*\",\"scale\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[1,\"driftX\",\"*\",[5,\"0.3\"]]]],\"+\",\"seedU\"],[1,[1,[1,[1,\"v\",\"*\",\"freq\"],\"*\",\"scale\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[1,\"driftY\",\"*\",[5,\"0.3\"]]]],\"+\",[1,\"seedU\",\"*\",[5,\"0.7\"]]]]]],[2,\"accR\",\"=\",[1,\"accR\",\"+\",[1,[6,\"noise.mxNoiseFloat2\",[\"coord\"]],\"*\",\"weight\"]]]]]],[13,\"n\",[1,\"accR\",\"*\",\"invTotalWeight\"]],[2,\"waveVal\",\"=\",[1,[1,[1,[5,\"1\"],\"-\",[6,\"std.abs\",[\"n\"]]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]]]],[0,[[13,\"warpFreq\",[1,\"freq\",\"*\",[5,\"0.5\"]]],[13,\"wx\",[6,\"noise.mxNoiseFloat2\",[[6,\"d.vec2f\",[[1,[1,[1,\"u\",\"*\",\"warpFreq\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[5,\"0.1\"]]],\"+\",\"seedU\"],[1,[1,\"v\",\"*\",\"warpFreq\"],\"+\",[1,\"seedU\",\"*\",[5,\"1.3\"]]]]]]]],[13,\"wy\",[6,\"noise.mxNoiseFloat2\",[[6,\"d.vec2f\",[[1,[1,[1,\"u\",\"*\",\"warpFreq\"],\"+\",\"seedU\"],\"+\",[5,\"17.3\"]],[1,[1,[1,[1,\"v\",\"*\",\"warpFreq\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[5,\"0.07\"]]],\"+\",\"seedU\"],\"+\",[5,\"31.1\"]]]]]]],[13,\"wu\",[1,\"u\",\"+\",[1,\"wx\",\"*\",[5,\"0.4\"]]]],[13,\"wv\",[1,\"v\",\"+\",[1,\"wy\",\"*\",[5,\"0.4\"]]]],[12,\"accW\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"octaveCount\"],[102,\"++\",\"i\"],[0,[[13,\"iF\",[6,\"d.f32\",[\"i\"]]],[13,\"scale\",[6,\"std.exp2\",[\"iF\"]]],[13,\"weight\",[6,\"std.exp2\",[[1,\"iF\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"driftX\",[6,\"std.cos\",[[1,\"iF\",\"*\",[5,\"2.39996\"]]]]],[13,\"driftY\",[6,\"std.sin\",[[1,\"iF\",\"*\",[5,\"2.39996\"]]]]],[13,\"coord\",[6,\"d.vec2f\",[[1,[1,[1,[1,\"wu\",\"*\",\"freq\"],\"*\",\"scale\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[1,\"driftX\",\"*\",[5,\"0.3\"]]]],\"+\",\"seedU\"],[1,[1,[1,[1,\"wv\",\"*\",\"freq\"],\"*\",\"scale\"],\"+\",[1,[7,\"p\",\"t\"],\"*\",[1,\"driftY\",\"*\",[5,\"0.3\"]]]],\"+\",[1,\"seedU\",\"*\",[5,\"0.7\"]]]]]],[2,\"accW\",\"=\",[1,\"accW\",\"+\",[1,[6,\"noise.mxNoiseFloat2\",[\"coord\"]],\"*\",\"weight\"]]]]]],[2,\"waveVal\",\"=\",[1,\"accW\",\"*\",\"invTotalWeight\"]]]]]],[13,\"cu\",[1,[1,\"u\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"cv\",[1,[1,\"v\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[13,\"inner\",[6,\"std.max\",[[6,\"std.abs\",[\"cu\"]],[6,\"std.abs\",[\"cv\"]]]]],[13,\"pinFalloff\",[6,\"std.smoothstep\",[[5,\"0.6\"],[5,\"1\"],\"inner\"]]],[13,\"pinMask\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],[1,[5,\"1\"],\"-\",\"pinFalloff\"],[7,\"p\",\"edgePin\"]]]],[13,\"baseHeight\",[1,[1,\"waveVal\",\"*\",\"amp\"],\"*\",\"pinMask\"]],[11,\"cursorEnabled\",[0,[[12,\"ripple\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,[7,\"p\",\"cursorActive\"],\">\",[5,\"0.5\"]],[0,[[13,\"H\",[6,\"d.f32\",[\"WAVE_HALFEXTENT\"]]],[13,\"waveU\",[1,[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"/\",\"H\"],\"+\",[5,\"0.5\"]]],[13,\"waveV\",[1,[1,[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]],\"/\",\"H\"],\"+\",[5,\"0.5\"]]],[13,\"Nm1\",[6,\"d.f32\",[\"WAVE_MAX\"]]],[13,\"gx\",[1,[6,\"std.clamp\",[\"waveU\",[5,\"0\"],[5,\"1\"]]],\"*\",\"Nm1\"]],[13,\"gy\",[1,[6,\"std.clamp\",[\"waveV\",[5,\"0\"],[5,\"1\"]]],\"*\",\"Nm1\"]],[13,\"ix\",[6,\"d.u32\",[[6,\"std.clamp\",[\"gx\",[5,\"0\"],\"Nm1\"]]]]],[13,\"iy\",[6,\"d.u32\",[[6,\"std.clamp\",[\"gy\",[5,\"0\"],\"Nm1\"]]]]],[13,\"ix1\",[6,\"std.min\",[[1,\"ix\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]],[6,\"d.u32\",[\"WAVE_MAX\"]]]]],[13,\"iy1\",[6,\"std.min\",[[1,\"iy\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]],[6,\"d.u32\",[\"WAVE_MAX\"]]]]],[13,\"fx\",[1,\"gx\",\"-\",[6,\"std.floor\",[\"gx\"]]]],[13,\"fy\",[1,\"gy\",\"-\",[6,\"std.floor\",[\"gy\"]]]],[13,\"W\",[6,\"d.u32\",[\"WAVE_GRID\"]]],[13,\"v00\",[8,[7,[7,\"heightFieldMarchLayout\",\"$\"],\"readBuf\"],[1,[1,\"iy\",\"*\",\"W\"],\"+\",\"ix\"]]],[13,\"v10\",[8,[7,[7,\"heightFieldMarchLayout\",\"$\"],\"readBuf\"],[1,[1,\"iy\",\"*\",\"W\"],\"+\",\"ix1\"]]],[13,\"v01\",[8,[7,[7,\"heightFieldMarchLayout\",\"$\"],\"readBuf\"],[1,[1,\"iy1\",\"*\",\"W\"],\"+\",\"ix\"]]],[13,\"v11\",[8,[7,[7,\"heightFieldMarchLayout\",\"$\"],\"readBuf\"],[1,[1,\"iy1\",\"*\",\"W\"],\"+\",\"ix1\"]]],[13,\"waveSample\",[6,\"std.mix\",[[6,\"std.mix\",[\"v00\",\"v10\",\"fx\"]],[6,\"std.mix\",[\"v01\",\"v11\",\"fx\"]],\"fy\"]]],[2,\"ripple\",\"=\",[1,\"waveSample\",\"*\",[1,[7,\"p\",\"cursorIntensity\"],\"*\",[5,\"0.05\"]]]]]]],[10,[1,\"baseHeight\",\"+\",\"ripple\"]]]],[0,[[10,\"baseHeight\"]]]]]]},externals:{heightFieldMarchLayout:()=>xn,\"d.f32\":()=>n,waveType:()=>e,octaveCount:()=>t,\"std.exp2\":()=>Ia,\"std.cos\":()=>pe,\"std.sin\":()=>ae,TWO_PI:()=>LP,invTotalWeight:()=>r,\"d.vec2f\":()=>m,\"noise.mxNoiseFloat2\":()=>la,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"std.mix\":()=>K,cursorEnabled:()=>s,WAVE_HALFEXTENT:()=>3,WAVE_MAX:()=>od,\"std.clamp\":()=>G,\"d.u32\":()=>M,\"std.min\":()=>ye,\"std.floor\":()=>ie,WAVE_GRID:()=>256}})&&o.f)({})).$name(\"spectralHeightField\")}function Dz(){return w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";if(t>M(0)&&t<M(od)&&s>M(0)&&s<M(od)){let r=bn.$.params,o=M(256),a=__tsover_add(__tsover_mul(s,o),t),i=bn.$.readSrc[__tsover_sub(a,M(1))],l=bn.$.readSrc[__tsover_add(a,M(1))],c=bn.$.readSrc[__tsover_sub(a,o)],d=bn.$.readSrc[__tsover_add(a,o)],u=bn.$.readSrc[a],p=__tsover_add(__tsover_add(__tsover_add(i,l),c),d),_=bn.$.writeBuf[a],f=__tsover_sub(1,__tsover_mul(r.decay,.004)),h=__tsover_mul(.5,__tsover_mul(r.waveSpeed,r.waveSpeed)),g=__tsover_sub(p,__tsover_mul(u,4)),y=__tsover_mul(__tsover_sub(__tsover_add(__tsover_mul(u,2),__tsover_mul(g,h)),_),f),b=__tsover_div(__tsover_add(n(t),.5),n(256)),x=__tsover_div(__tsover_add(n(s),.5),n(256)),P=__tsover_sub(b,r.cursorWaveX),C=__tsover_sub(x,r.cursorWaveY),E=__tsover_add(__tsover_mul(P,P),__tsover_mul(C,C)),R=__tsover_mul(r.radius,3);if(E<__tsover_mul(R,R)&&r.mouseSpeed>.01){let L=Tt(__tsover_mul(__tsover_div(E,B(__tsover_mul(r.radius,r.radius),n(1e-4))),-1));y=__tsover_sub(y,__tsover_mul(__tsover_mul(__tsover_mul(L,r.mouseSpeed),r.dt),3))}let U=n(6);y=G(y,__tsover_mul(U,-1),U),bn.$.writeBuf[a]=y,bn.$.readBuf[a]=y}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[11,[3,[3,[3,[1,\"cx\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],\"&&\",[1,\"cx\",\"<\",[6,\"d.u32\",[\"WAVE_MAX\"]]]],\"&&\",[1,\"cy\",\">\",[6,\"d.u32\",[[5,\"0\"]]]]],\"&&\",[1,\"cy\",\"<\",[6,\"d.u32\",[\"WAVE_MAX\"]]]],[0,[[13,\"p\",[7,[7,\"waveEquationLayout\",\"$\"],\"params\"]],[13,\"gs\",[6,\"d.u32\",[\"WAVE_GRID\"]]],[13,\"idx\",[1,[1,\"cy\",\"*\",\"gs\"],\"+\",\"cx\"]],[13,\"left\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readSrc\"],[1,\"idx\",\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"right\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readSrc\"],[1,\"idx\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"up\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readSrc\"],[1,\"idx\",\"-\",\"gs\"]]],[13,\"down\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readSrc\"],[1,\"idx\",\"+\",\"gs\"]]],[13,\"center\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readSrc\"],\"idx\"]],[13,\"sum\",[1,[1,[1,\"left\",\"+\",\"right\"],\"+\",\"up\"],\"+\",\"down\"]],[13,\"prev\",[8,[7,[7,\"waveEquationLayout\",\"$\"],\"writeBuf\"],\"idx\"]],[13,\"dampFactor\",[1,[5,\"1\"],\"-\",[1,[7,\"p\",\"decay\"],\"*\",\"WAVE_DECAY_PER_STEP\"]]],[13,\"k2\",[1,[5,\"0.5\"],\"*\",[1,[7,\"p\",\"waveSpeed\"],\"*\",[7,\"p\",\"waveSpeed\"]]]],[13,\"lap\",[1,\"sum\",\"-\",[1,\"center\",\"*\",[5,\"4\"]]]],[12,\"next\",[1,[1,[1,[1,\"center\",\"*\",[5,\"2\"]],\"+\",[1,\"lap\",\"*\",\"k2\"]],\"-\",\"prev\"],\"*\",\"dampFactor\"]],[13,\"cellU\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"WAVE_GRID\"]]]],[13,\"cellV\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"WAVE_GRID\"]]]],[13,\"ddx\",[1,\"cellU\",\"-\",[7,\"p\",\"cursorWaveX\"]]],[13,\"ddy\",[1,\"cellV\",\"-\",[7,\"p\",\"cursorWaveY\"]]],[13,\"distSq\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]],[13,\"influenceRadius\",[1,[7,\"p\",\"radius\"],\"*\",[5,\"3\"]]],[11,[3,[1,\"distSq\",\"<\",[1,\"influenceRadius\",\"*\",\"influenceRadius\"]],\"&&\",[1,[7,\"p\",\"mouseSpeed\"],\">\",[5,\"0.01\"]]],[0,[[13,\"influence\",[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",[6,\"std.max\",[[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]],[6,\"d.f32\",[[5,\"0.0001\"]]]]]],\"*\",[4,\"-\",[5,\"1\"]]]]]],[2,\"next\",\"=\",[1,\"next\",\"-\",[1,[1,[1,\"influence\",\"*\",[7,\"p\",\"mouseSpeed\"]],\"*\",[7,\"p\",\"dt\"]],\"*\",[5,\"3\"]]]]]]],[13,\"bound\",[6,\"d.f32\",[\"CURSOR_WAVE_BOUND\"]]],[2,\"next\",\"=\",[6,\"std.clamp\",[\"next\",[1,\"bound\",\"*\",[4,\"-\",[5,\"1\"]]],\"bound\"]]],[2,[8,[7,[7,\"waveEquationLayout\",\"$\"],\"writeBuf\"],\"idx\"],\"=\",\"next\"],[2,[8,[7,[7,\"waveEquationLayout\",\"$\"],\"readBuf\"],\"idx\"],\"=\",\"next\"]]]]]]},externals:{\"d.u32\":()=>M,WAVE_MAX:()=>od,waveEquationLayout:()=>bn,WAVE_GRID:()=>256,WAVE_DECAY_PER_STEP:()=>.004,\"d.f32\":()=>n,\"std.exp\":()=>Tt,\"std.max\":()=>B,CURSOR_WAVE_BOUND:()=>6,\"std.clamp\":()=>G}})&&e.f)({})).$name(\"waveEquationStep\")}function Lz(e){let{waveType:t,octaveCount:s,edgeMode:r,lightingEnabled:o,cursorEnabled:a,marchSteps:i,marchRefine:l,computeW:c,computeH:d}=e,u=bie(t,s,a);return w.fn([M,M])((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";let h=xn.$.params,g=__tsover_div(__tsover_add(n(_),.5),n(c)),y=__tsover_div(__tsover_add(n(f),.5),n(d)),b=__tsover_mul(__tsover_mul(__tsover_sub(g,.5),2),h.aspect),x=__tsover_mul(__tsover_mul(__tsover_sub(y,.5),2),-1),P=vs(h.camForward.mul(h.focal).add(h.camRight.mul(b)).add(h.camUp.mul(x))),C=h.heightEnvelope,E=N(n(-1),n(1),P.z>=0),R=__tsover_mul(E,B(re(P.z),n(1e-6))),U=__tsover_div(__tsover_sub(C,h.camPos.z),R),L=__tsover_div(__tsover_sub(__tsover_mul(C,-1),h.camPos.z),R),k=B(ye(U,L),n(0)),D=ye(B(U,L),n(15)),O=h.camPos.add(P.mul(n(15))),F=n(0),q=n(15),Y=__tsover_add(__tsover_mul(O.x,.5),.5),V=__tsover_add(__tsover_mul(O.y,.5),.5),X=A(1,1,1);if(D>k){let De=__tsover_div(__tsover_sub(D,k),n(i)),Ee=k,ze=h.camPos.add(P.mul(k)),Ie=__tsover_sub(ze.z,u(m(__tsover_add(__tsover_mul(ze.x,.5),.5),__tsover_add(__tsover_mul(ze.y,.5),.5))));for(let ke=0;ke<i;ke++){Ee=__tsover_add(Ee,De);let Me=h.camPos.add(P.mul(Ee)),we=u(m(__tsover_add(__tsover_mul(Me.x,.5),.5),__tsover_add(__tsover_mul(Me.y,.5),.5))),Ve=__tsover_sub(Me.z,we);if(Ve<0&&Ie>0){F=1;break}Ie=Ve}if(F>.5){let ke=__tsover_sub(Ee,De),Me=Ee;for(let Ve=0;Ve<l;Ve++){let qe=__tsover_mul(__tsover_add(ke,Me),.5),st=h.camPos.add(P.mul(qe));__tsover_sub(st.z,u(m(__tsover_add(__tsover_mul(st.x,.5),.5),__tsover_add(__tsover_mul(st.y,.5),.5))))>0?ke=qe:Me=qe}q=__tsover_mul(__tsover_add(ke,Me),.5);let we=h.camPos.add(P.mul(q));if(Y=__tsover_add(__tsover_mul(we.x,.5),.5),V=__tsover_add(__tsover_mul(we.y,.5),.5),o){let qe=u(m(Y,V)),st=u(m(__tsover_add(Y,.01),V)),pt=u(m(Y,__tsover_add(V,.01))),Ze=__tsover_div(__tsover_sub(st,qe),.01),nt=__tsover_div(__tsover_sub(pt,qe),.01),ht=vs(A(__tsover_mul(Ze,-.5),__tsover_mul(nt,-.5),1)),yt=vs(A(h.lightX,h.lightY,h.lightZ)),St=P.mul(-1),Mt=G(Se(ht,yt),0,1),Rt=vs(yt.add(St)),Kt=__tsover_add(__tsover_mul(h.glossiness,128),4),Wt=__tsover_mul(vt(G(Se(ht,Rt),0,1),Kt),h.highlights),ls=A(.4,.4,.4),us=h.lightColor.mul(__tsover_mul(Mt,.6)),os=h.lightColor.mul(Wt),bs=ls.add(us).add(os);X=G(K(A(1,1,1),bs,h.lighting),A(0,0,0),A(3,3,3))}}}let Q=m(Y,__tsover_sub(1,V)),te=m(Q.x,Q.y),J=n(1);if(r===2)te=Hm(Q);else if(r===3)te=jm(Q);else if(r===1){let De=Q.x>=0&&Q.x<=1&&Q.y>=0&&Q.y<=1;J=N(n(0),n(1),De)}else te=G(Q,m(0,0),m(1,1));let oe=n(15),le=.05,se=__tsover_mul(h.nearCutoff,oe),Te=__tsover_mul(h.farCutoff,oe),de=be(__tsover_sub(se,le),__tsover_add(se,le),q),xe=be(__tsover_add(Te,le),__tsover_sub(Te,le),q),Ae=__tsover_mul(__tsover_mul(F,J),__tsover_mul(de,xe));lt(xn.$.uvMaskTex,fe(_,f),S(te.x,te.y,Ae,0)),lt(xn.$.litTex,fe(_,f),S(X,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"heightFieldMarchLayout\",\"$\"],\"params\"]],[13,\"screenU\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"computeW\"]]]],[13,\"screenV\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"computeH\"]]]],[13,\"ndcX\",[1,[1,[1,\"screenU\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[7,\"p\",\"aspect\"]]],[13,\"ndcY\",[1,[1,[1,\"screenV\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"rayDir\",[6,\"std.normalize\",[[6,[7,[6,[7,[6,[7,[7,\"p\",\"camForward\"],\"mul\"],[[7,\"p\",\"focal\"]]],\"add\"],[[6,[7,[7,\"p\",\"camRight\"],\"mul\"],[\"ndcX\"]]]],\"add\"],[[6,[7,[7,\"p\",\"camUp\"],\"mul\"],[\"ndcY\"]]]]]]],[13,\"envelope\",[7,\"p\",\"heightEnvelope\"]],[13,\"dzSign\",[6,\"std.select\",[[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"rayDir\",\"z\"],\">=\",[5,\"0\"]]]]],[13,\"dzSafe\",[1,\"dzSign\",\"*\",[6,\"std.max\",[[6,\"std.abs\",[[7,\"rayDir\",\"z\"]]],[6,\"d.f32\",[[5,\"0.000001\"]]]]]]],[13,\"tTop\",[1,[1,\"envelope\",\"-\",[7,[7,\"p\",\"camPos\"],\"z\"]],\"/\",\"dzSafe\"]],[13,\"tBot\",[1,[1,[1,\"envelope\",\"*\",[4,\"-\",[5,\"1\"]]],\"-\",[7,[7,\"p\",\"camPos\"],\"z\"]],\"/\",\"dzSafe\"]],[13,\"tEnter\",[6,\"std.max\",[[6,\"std.min\",[\"tTop\",\"tBot\"]],[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"tExit\",[6,\"std.min\",[[6,\"std.max\",[\"tTop\",\"tBot\"]],[6,\"d.f32\",[\"MARCH_FAR\"]]]]],[13,\"farPos\",[6,[7,[7,\"p\",\"camPos\"],\"add\"],[[6,[7,\"rayDir\",\"mul\"],[[6,\"d.f32\",[\"MARCH_FAR\"]]]]]]],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"finalT\",[6,\"d.f32\",[\"MARCH_FAR\"]]],[12,\"hitU\",[1,[1,[7,\"farPos\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[12,\"hitV\",[1,[1,[7,\"farPos\",\"y\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[12,\"lit\",[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]],[11,[1,\"tExit\",\">\",\"tEnter\"],[0,[[13,\"stepSize\",[1,[1,\"tExit\",\"-\",\"tEnter\"],\"/\",[6,\"d.f32\",[\"marchSteps\"]]]],[12,\"tMarch\",\"tEnter\"],[13,\"pStart\",[6,[7,[7,\"p\",\"camPos\"],\"add\"],[[6,[7,\"rayDir\",\"mul\"],[\"tEnter\"]]]]],[12,\"prevDiff\",[1,[7,\"pStart\",\"z\"],\"-\",[6,\"heightFn\",[[6,\"d.vec2f\",[[1,[1,[7,\"pStart\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[7,\"pStart\",\"y\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]]],[14,[12,\"s\",[5,\"0\"]],[1,\"s\",\"<\",\"marchSteps\"],[102,\"++\",\"s\"],[0,[[2,\"tMarch\",\"=\",[1,\"tMarch\",\"+\",\"stepSize\"]],[13,\"pos\",[6,[7,[7,\"p\",\"camPos\"],\"add\"],[[6,[7,\"rayDir\",\"mul\"],[\"tMarch\"]]]]],[13,\"h\",[6,\"heightFn\",[[6,\"d.vec2f\",[[1,[1,[7,\"pos\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[7,\"pos\",\"y\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]],[13,\"diff\",[1,[7,\"pos\",\"z\"],\"-\",\"h\"]],[11,[3,[1,\"diff\",\"<\",[5,\"0\"]],\"&&\",[1,\"prevDiff\",\">\",[5,\"0\"]]],[0,[[2,\"hit\",\"=\",[5,\"1\"]],[17]]]],[2,\"prevDiff\",\"=\",\"diff\"]]]],[11,[1,\"hit\",\">\",[5,\"0.5\"]],[0,[[12,\"tLow\",[1,\"tMarch\",\"-\",\"stepSize\"]],[12,\"tHigh\",\"tMarch\"],[14,[12,\"r\",[5,\"0\"]],[1,\"r\",\"<\",\"marchRefine\"],[102,\"++\",\"r\"],[0,[[13,\"tMid\",[1,[1,\"tLow\",\"+\",\"tHigh\"],\"*\",[5,\"0.5\"]]],[13,\"pMid\",[6,[7,[7,\"p\",\"camPos\"],\"add\"],[[6,[7,\"rayDir\",\"mul\"],[\"tMid\"]]]]],[13,\"dMid\",[1,[7,\"pMid\",\"z\"],\"-\",[6,\"heightFn\",[[6,\"d.vec2f\",[[1,[1,[7,\"pMid\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]],[1,[1,[7,\"pMid\",\"y\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]]]]]]],[11,[1,\"dMid\",\">\",[5,\"0\"]],[0,[[2,\"tLow\",\"=\",\"tMid\"]]],[0,[[2,\"tHigh\",\"=\",\"tMid\"]]]]]]],[2,\"finalT\",\"=\",[1,[1,\"tLow\",\"+\",\"tHigh\"],\"*\",[5,\"0.5\"]]],[13,\"hitPos\",[6,[7,[7,\"p\",\"camPos\"],\"add\"],[[6,[7,\"rayDir\",\"mul\"],[\"finalT\"]]]]],[2,\"hitU\",\"=\",[1,[1,[7,\"hitPos\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[2,\"hitV\",\"=\",[1,[1,[7,\"hitPos\",\"y\"],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[11,\"lightingEnabled\",[0,[[13,\"normalEps\",[5,\"0.01\"]],[13,\"hC\",[6,\"heightFn\",[[6,\"d.vec2f\",[\"hitU\",\"hitV\"]]]]],[13,\"hR\",[6,\"heightFn\",[[6,\"d.vec2f\",[[1,\"hitU\",\"+\",\"normalEps\"],\"hitV\"]]]]],[13,\"hUp\",[6,\"heightFn\",[[6,\"d.vec2f\",[\"hitU\",[1,\"hitV\",\"+\",\"normalEps\"]]]]]],[13,\"dhdu\",[1,[1,\"hR\",\"-\",\"hC\"],\"/\",\"normalEps\"]],[13,\"dhdv\",[1,[1,\"hUp\",\"-\",\"hC\"],\"/\",\"normalEps\"]],[13,\"nrm\",[6,\"std.normalize\",[[6,\"d.vec3f\",[[1,\"dhdu\",\"*\",[4,\"-\",[5,\"0.5\"]]],[1,\"dhdv\",\"*\",[4,\"-\",[5,\"0.5\"]]],[5,\"1\"]]]]]],[13,\"lightDir\",[6,\"std.normalize\",[[6,\"d.vec3f\",[[7,\"p\",\"lightX\"],[7,\"p\",\"lightY\"],[7,\"p\",\"lightZ\"]]]]]],[13,\"viewDir\",[6,[7,\"rayDir\",\"mul\"],[[4,\"-\",[5,\"1\"]]]]],[13,\"diffuse\",[6,\"std.clamp\",[[6,\"std.dot\",[\"nrm\",\"lightDir\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"halfVec\",[6,\"std.normalize\",[[6,[7,\"lightDir\",\"add\"],[\"viewDir\"]]]]],[13,\"specPow\",[1,[1,[7,\"p\",\"glossiness\"],\"*\",[5,\"128\"]],\"+\",[5,\"4\"]]],[13,\"specular\",[1,[6,\"std.pow\",[[6,\"std.clamp\",[[6,\"std.dot\",[\"nrm\",\"halfVec\"]],[5,\"0\"],[5,\"1\"]]],\"specPow\"]],\"*\",[7,\"p\",\"highlights\"]]],[13,\"ambient\",[6,\"d.vec3f\",[[5,\"0.4\"],[5,\"0.4\"],[5,\"0.4\"]]]],[13,\"tintedDiffuse\",[6,[7,[7,\"p\",\"lightColor\"],\"mul\"],[[1,\"diffuse\",\"*\",[5,\"0.6\"]]]]],[13,\"tintedSpec\",[6,[7,[7,\"p\",\"lightColor\"],\"mul\"],[\"specular\"]]],[13,\"totalLight\",[6,[7,[6,[7,\"ambient\",\"add\"],[\"tintedDiffuse\"]],\"add\"],[\"tintedSpec\"]]],[2,\"lit\",\"=\",[6,\"std.clamp\",[[6,\"std.mix\",[[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"totalLight\",[7,\"p\",\"lighting\"]]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"3\"],[5,\"3\"],[5,\"3\"]]]]]]]]]]]]]]],[13,\"rawSampleUV\",[6,\"d.vec2f\",[\"hitU\",[1,[5,\"1\"],\"-\",\"hitV\"]]]],[12,\"finalUV\",[6,\"d.vec2f\",[[7,\"rawSampleUV\",\"x\"],[7,\"rawSampleUV\",\"y\"]]]],[12,\"outOfBoundsMask\",[6,\"d.f32\",[[5,\"1\"]]]],[11,[1,\"edgeMode\",\"===\",[5,\"2\"]],[0,[[2,\"finalUV\",\"=\",[6,\"edges.edgeMirrorUV\",[\"rawSampleUV\"]]]]],[11,[1,\"edgeMode\",\"===\",[5,\"3\"]],[0,[[2,\"finalUV\",\"=\",[6,\"edges.edgeWrapUV\",[\"rawSampleUV\"]]]]],[11,[1,\"edgeMode\",\"===\",[5,\"1\"]],[0,[[13,\"inside\",[3,[3,[3,[1,[7,\"rawSampleUV\",\"x\"],\">=\",[5,\"0\"]],\"&&\",[1,[7,\"rawSampleUV\",\"x\"],\"<=\",[5,\"1\"]]],\"&&\",[1,[7,\"rawSampleUV\",\"y\"],\">=\",[5,\"0\"]]],\"&&\",[1,[7,\"rawSampleUV\",\"y\"],\"<=\",[5,\"1\"]]]],[2,\"outOfBoundsMask\",\"=\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],\"inside\"]]]]],[0,[[2,\"finalUV\",\"=\",[6,\"std.clamp\",[\"rawSampleUV\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]]]]]]],[13,\"CUTOFF_RANGE\",[6,\"d.f32\",[\"MARCH_FAR\"]]],[13,\"edgeAA\",[5,\"0.05\"]],[13,\"nearT\",[1,[7,\"p\",\"nearCutoff\"],\"*\",\"CUTOFF_RANGE\"]],[13,\"farT\",[1,[7,\"p\",\"farCutoff\"],\"*\",\"CUTOFF_RANGE\"]],[13,\"nearMask\",[6,\"std.smoothstep\",[[1,\"nearT\",\"-\",\"edgeAA\"],[1,\"nearT\",\"+\",\"edgeAA\"],\"finalT\"]]],[13,\"farMask\",[6,\"std.smoothstep\",[[1,\"farT\",\"+\",\"edgeAA\"],[1,\"farT\",\"-\",\"edgeAA\"],\"finalT\"]]],[13,\"finalMask\",[1,[1,\"hit\",\"*\",\"outOfBoundsMask\"],\"*\",[1,\"nearMask\",\"*\",\"farMask\"]]],[6,\"std.textureStore\",[[7,[7,\"heightFieldMarchLayout\",\"$\"],\"uvMaskTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"finalUV\",\"x\"],[7,\"finalUV\",\"y\"],\"finalMask\",[5,\"0\"]]]]],[6,\"std.textureStore\",[[7,[7,\"heightFieldMarchLayout\",\"$\"],\"litTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"lit\",[5,\"0\"]]]]]]]},externals:{heightFieldMarchLayout:()=>xn,\"d.f32\":()=>n,computeW:()=>c,computeH:()=>d,\"std.normalize\":()=>vs,\"std.select\":()=>N,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.min\":()=>ye,MARCH_FAR:()=>15,\"d.vec3f\":()=>A,marchSteps:()=>i,heightFn:()=>u,\"d.vec2f\":()=>m,marchRefine:()=>l,lightingEnabled:()=>o,\"std.clamp\":()=>G,\"std.dot\":()=>Se,\"std.pow\":()=>vt,\"std.mix\":()=>K,edgeMode:()=>r,\"edges.edgeMirrorUV\":()=>Hm,\"edges.edgeWrapUV\":()=>jm,\"std.smoothstep\":()=>be,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&p.f)({})).$name(\"heightFieldMarch\")}function QA(e,t,s={}){let r=t;return wr(e,(o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";r(a,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"k\",[\"cx\",\"cy\"]]]]},externals:{k:()=>r}})&&o.f)({}),s)}function ai(e,t,s={}){return wr(e,(r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a)=>{\"use gpu\";t(o,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>t}})&&r.f)({}),{...s.size?{size:s.size}:{},...s.bindGroup?{bindGroup:s.bindGroup}:{}})}var xie=()=>gt({feed:n,kill:n,dv:n,brushRadSq:n,brushStrength:n,cursorX:n,cursorY:n,cursorActive:n,driveInfluence:n,driveContrast:n,driveThreshold:n,driveInvert:n,driveMapX:n,driveMapY:n,_pad0:n,_pad1:n}).$name(\"GrayScottParams\"),Aie=(e,t)=>w.bindGroupLayout({readBuf:{storage:Fe(m,e*e),access:\"readonly\"},writeBuf:{storage:Fe(m,e*e),access:\"mutable\"},params:{uniform:t}}),wie=(e,t)=>w.bindGroupLayout({readBuf:{storage:Fe(m,e*e),access:\"readonly\"},writeBuf:{storage:Fe(m,e*e),access:\"mutable\"},params:{uniform:t},driveTex:{texture:Zt(n)}});function ZA(e){let{n:t,namePrefix:s,drive:r}=e,o=xie(),a=r?\"Drive\":\"\",i=r?wie(t,o):Aie(t,o),l=r?i:null,c=w.fn([j,j,j,j],m)((_=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(_.f=((f,h,g,y)=>{\"use gpu\";let b=G(__tsover_add(f,g),0,__tsover_sub(t,1)),x=G(__tsover_add(h,y),0,__tsover_sub(t,1));return i.$.readBuf[M(__tsover_add(__tsover_mul(b,t),x))]}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"ii\"},{type:\"i\",name:\"ji\"},{type:\"i\",name:\"di\"},{type:\"i\",name:\"dj\"}],body:[0,[[13,\"row\",[6,\"std.clamp\",[[1,\"ii\",\"+\",\"di\"],[5,\"0\"],[1,\"n\",\"-\",[5,\"1\"]]]]],[13,\"col\",[6,\"std.clamp\",[[1,\"ji\",\"+\",\"dj\"],[5,\"0\"],[1,\"n\",\"-\",[5,\"1\"]]]]],[10,[8,[7,[7,\"layout\",\"$\"],\"readBuf\"],[6,\"d.u32\",[[1,[1,\"row\",\"*\",\"n\"],\"+\",\"col\"]]]]]]]},externals:{\"std.clamp\":()=>G,n:()=>t,layout:()=>i,\"d.u32\":()=>M}})&&_.f)({})).$name(`${s}Nbr${a}`),d=r&&l?w.fn([M,M],n)((_=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(_.f=((f,h)=>{\"use gpu\";let g=l.$.params,y=Ti(l.$.driveTex),b=n(y.x),x=n(y.y),P=__tsover_div(__tsover_add(n(f),.5),n(t)),C=__tsover_div(__tsover_add(n(h),.5),n(t)),E=G(__tsover_add(__tsover_mul(__tsover_sub(P,.5),g.driveMapX),.5),n(0),n(1)),R=G(__tsover_add(__tsover_mul(__tsover_sub(C,.5),g.driveMapY),.5),n(0),n(1)),U=M(ye(__tsover_mul(E,b),__tsover_sub(b,1))),L=M(ye(__tsover_mul(R,x),__tsover_sub(x,1))),k=Ye(l.$.driveTex,fe(U,L),0),D=G(k.w,n(0),n(1)),O=Se(k.xyz.div(B(k.w,n(.001))),A(.2126,.7152,.0722)),F=__tsover_add(n(1),__tsover_mul(g.driveContrast,n(r.contrastMax))),q=G(__tsover_add(__tsover_mul(__tsover_sub(O,g.driveThreshold),F),.5),n(0),n(1)),Y=__tsover_mul(__tsover_sub(q,.5),2);return Y=N(Y,__tsover_mul(Y,-1),g.driveInvert>.5),__tsover_mul(Y,D)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"driveLayout\",\"$\"],\"params\"]],[13,\"dims\",[6,\"std.textureDimensions\",[[7,[7,\"driveLayout\",\"$\"],\"driveTex\"]]]],[13,\"fw\",[6,\"d.f32\",[[7,\"dims\",\"x\"]]]],[13,\"fh\",[6,\"d.f32\",[[7,\"dims\",\"y\"]]]],[13,\"fx\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"fy\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"n\"]]]],[13,\"cu\",[6,\"std.clamp\",[[1,[1,[1,\"fx\",\"-\",[5,\"0.5\"]],\"*\",[7,\"p\",\"driveMapX\"]],\"+\",[5,\"0.5\"]],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"cv\",[6,\"std.clamp\",[[1,[1,[1,\"fy\",\"-\",[5,\"0.5\"]],\"*\",[7,\"p\",\"driveMapY\"]],\"+\",[5,\"0.5\"]],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"px\",[6,\"d.u32\",[[6,\"std.min\",[[1,\"cu\",\"*\",\"fw\"],[1,\"fw\",\"-\",[5,\"1\"]]]]]]],[13,\"py\",[6,\"d.u32\",[[6,\"std.min\",[[1,\"cv\",\"*\",\"fh\"],[1,\"fh\",\"-\",[5,\"1\"]]]]]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"driveLayout\",\"$\"],\"driveTex\"],[6,\"d.vec2u\",[\"px\",\"py\"]],[5,\"0\"]]]],[13,\"cov\",[6,\"std.clamp\",[[7,\"texel\",\"w\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"rgb\",[6,[7,[7,\"texel\",\"xyz\"],\"div\"],[[6,\"std.max\",[[7,\"texel\",\"w\"],[6,\"d.f32\",[[5,\"0.001\"]]]]]]]],[13,\"luma\",[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.2126\"],[5,\"0.7152\"],[5,\"0.0722\"]]]]]],[13,\"gain\",[1,[6,\"d.f32\",[[5,\"1\"]]],\"+\",[1,[7,\"p\",\"driveContrast\"],\"*\",[6,\"d.f32\",[\"drive.contrastMax\"]]]]],[13,\"shaped\",[6,\"std.clamp\",[[1,[1,[1,\"luma\",\"-\",[7,\"p\",\"driveThreshold\"]],\"*\",\"gain\"],\"+\",[5,\"0.5\"]],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]],[12,\"signed\",[1,[1,\"shaped\",\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]]],[2,\"signed\",\"=\",[6,\"std.select\",[\"signed\",[1,\"signed\",\"*\",[4,\"-\",[5,\"1\"]]],[1,[7,\"p\",\"driveInvert\"],\">\",[5,\"0.5\"]]]]],[10,[1,\"signed\",\"*\",\"cov\"]]]]},externals:{driveLayout:()=>l,\"std.textureDimensions\":()=>Ti,\"d.f32\":()=>n,n:()=>t,\"std.clamp\":()=>G,\"d.u32\":()=>M,\"std.min\":()=>ye,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.max\":()=>B,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"drive.contrastMax\":()=>r.contrastMax,\"std.select\":()=>N}})&&_.f)({})).$name(`${s}DriveMap`):null,p=r&&d?((_,f)=>w.fn([n,n,M,M],m)((h=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(h.f=((g,y,b,x)=>{\"use gpu\";let P=__tsover_mul(f(b,x),i.$.params.driveInfluence);return m(G(__tsover_add(g,__tsover_mul(P,n(_.dFeed))),n(_.feedMin),n(_.feedMax)),G(__tsover_sub(y,__tsover_mul(P,n(_.dKill))),n(_.killMin),n(_.killMax)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"feed\"},{type:\"i\",name:\"kill\"},{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"mod\",[1,[6,\"dm\",[\"cx\",\"cy\"]],\"*\",[7,[7,[7,\"layout\",\"$\"],\"params\"],\"driveInfluence\"]]],[10,[6,\"d.vec2f\",[[6,\"std.clamp\",[[1,\"feed\",\"+\",[1,\"mod\",\"*\",[6,\"d.f32\",[\"drv.dFeed\"]]]],[6,\"d.f32\",[\"drv.feedMin\"]],[6,\"d.f32\",[\"drv.feedMax\"]]]],[6,\"std.clamp\",[[1,\"kill\",\"-\",[1,\"mod\",\"*\",[6,\"d.f32\",[\"drv.dKill\"]]]],[6,\"d.f32\",[\"drv.killMin\"]],[6,\"d.f32\",[\"drv.killMax\"]]]]]]]]]},externals:{dm:()=>f,layout:()=>i,\"d.vec2f\":()=>m,\"std.clamp\":()=>G,\"d.f32\":()=>n,\"drv.dFeed\":()=>_.dFeed,\"drv.feedMin\":()=>_.feedMin,\"drv.feedMax\":()=>_.feedMax,\"drv.dKill\":()=>_.dKill,\"drv.killMin\":()=>_.killMin,\"drv.killMax\":()=>_.killMax}})&&h.f)({})).$name(`${s}Rates${a}`))(r,d):w.fn([n,n,M,M],m)((_=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(_.f=((f,h)=>{\"use gpu\";return m(f,h)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"feed\"},{type:\"i\",name:\"kill\"}],body:[0,[[10,[6,\"d.vec2f\",[\"feed\",\"kill\"]]]]]},externals:{\"d.vec2f\":()=>m}})&&_.f)({})).$name(`${s}Rates`);return{layout:i,Params:o,kernel:w.fn([M,M])((_=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(_.f=((f,h)=>{\"use gpu\";let g=i.$.params,y=__tsover_add(__tsover_mul(h,M(t)),f),b=i.$.readBuf[y],x=j(h),P=j(f),C=c(x,P,0,-1),E=c(x,P,0,1),R=c(x,P,-1,0),U=c(x,P,1,0),L=c(x,P,-1,-1),k=c(x,P,-1,1),D=c(x,P,1,-1),O=c(x,P,1,1),F=__tsover_sub(__tsover_add(__tsover_mul(__tsover_add(__tsover_add(__tsover_add(C.x,E.x),R.x),U.x),.2),__tsover_mul(__tsover_add(__tsover_add(__tsover_add(L.x,k.x),D.x),O.x),.05)),b.x),q=__tsover_sub(__tsover_add(__tsover_mul(__tsover_add(__tsover_add(__tsover_add(C.y,E.y),R.y),U.y),.2),__tsover_mul(__tsover_add(__tsover_add(__tsover_add(L.y,k.y),D.y),O.y),.05)),b.y),Y=p(g.feed,g.kill,f,h),V=Y.x,X=Y.y,Q=b.x,te=b.y,J=__tsover_mul(__tsover_mul(Q,te),te),oe=__tsover_add(Q,__tsover_add(__tsover_sub(F,J),__tsover_mul(V,__tsover_sub(1,Q)))),le=__tsover_add(te,__tsover_sub(__tsover_add(__tsover_mul(g.dv,q),J),__tsover_mul(__tsover_add(V,X),te)));if(g.cursorActive>0){let se=__tsover_add(n(f),.5),Te=__tsover_add(n(h),.5),de=__tsover_sub(se,g.cursorX),xe=__tsover_sub(Te,g.cursorY),Ae=__tsover_add(__tsover_mul(de,de),__tsover_mul(xe,xe)),De=__tsover_mul(Tt(__tsover_mul(__tsover_div(Ae,B(g.brushRadSq,n(1))),-1)),g.cursorActive);le=ye(__tsover_add(le,__tsover_mul(De,g.brushStrength)),n(1))}oe=G(oe,0,1),le=G(le,0,1),i.$.writeBuf[y]=m(oe,le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"self\",[8,[7,[7,\"layout\",\"$\"],\"readBuf\"],\"idx\"]],[13,\"ii\",[6,\"d.i32\",[\"cy\"]]],[13,\"ji\",[6,\"d.i32\",[\"cx\"]]],[13,\"L\",[6,\"nbr\",[\"ii\",\"ji\",[5,\"0\"],[4,\"-\",[5,\"1\"]]]]],[13,\"R\",[6,\"nbr\",[\"ii\",\"ji\",[5,\"0\"],[5,\"1\"]]]],[13,\"T\",[6,\"nbr\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"0\"]]]],[13,\"B\",[6,\"nbr\",[\"ii\",\"ji\",[5,\"1\"],[5,\"0\"]]]],[13,\"TL\",[6,\"nbr\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[4,\"-\",[5,\"1\"]]]]],[13,\"TR\",[6,\"nbr\",[\"ii\",\"ji\",[4,\"-\",[5,\"1\"]],[5,\"1\"]]]],[13,\"BL\",[6,\"nbr\",[\"ii\",\"ji\",[5,\"1\"],[4,\"-\",[5,\"1\"]]]]],[13,\"BR\",[6,\"nbr\",[\"ii\",\"ji\",[5,\"1\"],[5,\"1\"]]]],[13,\"lapU\",[1,[1,[1,[1,[1,[1,[7,\"L\",\"x\"],\"+\",[7,\"R\",\"x\"]],\"+\",[7,\"T\",\"x\"]],\"+\",[7,\"B\",\"x\"]],\"*\",[5,\"0.2\"]],\"+\",[1,[1,[1,[1,[7,\"TL\",\"x\"],\"+\",[7,\"TR\",\"x\"]],\"+\",[7,\"BL\",\"x\"]],\"+\",[7,\"BR\",\"x\"]],\"*\",[5,\"0.05\"]]],\"-\",[7,\"self\",\"x\"]]],[13,\"lapV\",[1,[1,[1,[1,[1,[1,[7,\"L\",\"y\"],\"+\",[7,\"R\",\"y\"]],\"+\",[7,\"T\",\"y\"]],\"+\",[7,\"B\",\"y\"]],\"*\",[5,\"0.2\"]],\"+\",[1,[1,[1,[1,[7,\"TL\",\"y\"],\"+\",[7,\"TR\",\"y\"]],\"+\",[7,\"BL\",\"y\"]],\"+\",[7,\"BR\",\"y\"]],\"*\",[5,\"0.05\"]]],\"-\",[7,\"self\",\"y\"]]],[13,\"fk\",[6,\"rates\",[[7,\"p\",\"feed\"],[7,\"p\",\"kill\"],\"cx\",\"cy\"]]],[13,\"feedCell\",[7,\"fk\",\"x\"]],[13,\"killCell\",[7,\"fk\",\"y\"]],[13,\"U\",[7,\"self\",\"x\"]],[13,\"V\",[7,\"self\",\"y\"]],[13,\"uvv\",[1,[1,\"U\",\"*\",\"V\"],\"*\",\"V\"]],[12,\"newU\",[1,\"U\",\"+\",[1,[1,\"lapU\",\"-\",\"uvv\"],\"+\",[1,\"feedCell\",\"*\",[1,[5,\"1\"],\"-\",\"U\"]]]]],[12,\"newV\",[1,\"V\",\"+\",[1,[1,[1,[7,\"p\",\"dv\"],\"*\",\"lapV\"],\"+\",\"uvv\"],\"-\",[1,[1,\"feedCell\",\"+\",\"killCell\"],\"*\",\"V\"]]]],[11,[1,[7,\"p\",\"cursorActive\"],\">\",[5,\"0\"]],[0,[[13,\"cellX\",[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]]],[13,\"cellY\",[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]]],[13,\"dxp\",[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]]],[13,\"dyp\",[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]]],[13,\"distSq\",[1,[1,\"dxp\",\"*\",\"dxp\"],\"+\",[1,\"dyp\",\"*\",\"dyp\"]]],[13,\"inf\",[1,[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",[6,\"std.max\",[[7,\"p\",\"brushRadSq\"],[6,\"d.f32\",[[5,\"1\"]]]]]],\"*\",[4,\"-\",[5,\"1\"]]]]],\"*\",[7,\"p\",\"cursorActive\"]]],[2,\"newV\",\"=\",[6,\"std.min\",[[1,\"newV\",\"+\",[1,\"inf\",\"*\",[7,\"p\",\"brushStrength\"]]],[6,\"d.f32\",[[5,\"1\"]]]]]]]]],[2,\"newU\",\"=\",[6,\"std.clamp\",[\"newU\",[5,\"0\"],[5,\"1\"]]]],[2,\"newV\",\"=\",[6,\"std.clamp\",[\"newV\",[5,\"0\"],[5,\"1\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"writeBuf\"],\"idx\"],\"=\",[6,\"d.vec2f\",[\"newU\",\"newV\"]]]]]},externals:{layout:()=>i,\"d.u32\":()=>M,n:()=>t,\"d.i32\":()=>j,nbr:()=>c,rates:()=>p,\"d.f32\":()=>n,\"std.exp\":()=>Tt,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.clamp\":()=>G,\"d.vec2f\":()=>m}})&&_.f)({})).$name(`${s}React${a}`)}}function Uz(e){let{n:t,density:s,namePrefix:r}=e,o=(globalThis.__TYPEGPU_AUTONAME__??(a=>a))(w.bindGroupLayout({stateBuf:{storage:Fe(m,t*t),access:\"mutable\"}}),\"layout\");return{layout:o,kernel:w.fn([M,M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";let c=__tsover_add(__tsover_mul(l,M(t)),i),d=Ys(m(n(i),n(l))),u=N(n(0),n(1),d<s);o.$.stateBuf[c]=m(1,u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"h\",[6,\"noise.hash12\",[[6,\"d.vec2f\",[[6,\"d.f32\",[\"cx\"]],[6,\"d.f32\",[\"cy\"]]]]]]],[13,\"vSeed\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"h\",\"<\",\"density\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"stateBuf\"],\"idx\"],\"=\",[6,\"d.vec2f\",[[5,\"1\"],\"vSeed\"]]]]]},externals:{\"d.u32\":()=>M,n:()=>t,\"noise.hash12\":()=>Ys,\"d.vec2f\":()=>m,\"d.f32\":()=>n,\"std.select\":()=>N,density:()=>s,layout:()=>o}})&&a.f)({})).$name(`${r}Seed`)}}function Oz(e){let{n:t,format:s,namePrefix:r}=e,o=(globalThis.__TYPEGPU_AUTONAME__??(a=>a))(w.bindGroupLayout({srcBuf:{storage:Fe(m,t*t),access:\"readonly\"},outTex:{storageTexture:zt(s,\"write-only\")}}),\"layout\");return{layout:o,kernel:w.fn([M,M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";let c=__tsover_add(__tsover_mul(l,M(t)),i),d=o.$.srcBuf[c];lt(o.$.outTex,fe(i,l),S(d.x,d.y,0,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"s\",[8,[7,[7,\"layout\",\"$\"],\"srcBuf\"],\"idx\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"s\",\"x\"],[7,\"s\",\"y\"],[5,\"0\"],[5,\"0\"]]]]]]]},externals:{\"d.u32\":()=>M,n:()=>t,layout:()=>o,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&a.f)({})).$name(`${r}Output`)}}var OP=new Map;function Tie(e){return w.fn([n,n,n,n,n,n,n],n)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o,a,i,l,c)=>{\"use gpu\";let d=__tsover_sub(__tsover_div(s,e),o),u=__tsover_sub(__tsover_div(r,e),a),p=N(d,__tsover_mul(d,c),c>=1),_=N(__tsover_div(u,c),u,c>=1),f=Ue(__tsover_add(__tsover_mul(p,p),__tsover_mul(_,_))),h=__tsover_mul(i,__tsover_sub(1,l));return __tsover_sub(1,be(h,i,f))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cxf\"},{type:\"i\",name:\"cyf\"},{type:\"i\",name:\"mouseX\"},{type:\"i\",name:\"mouseY\"},{type:\"i\",name:\"radius\"},{type:\"i\",name:\"falloff\"},{type:\"i\",name:\"aspect\"}],body:[0,[[13,\"dx\",[1,[1,\"cxf\",\"/\",\"n\"],\"-\",\"mouseX\"]],[13,\"dy\",[1,[1,\"cyf\",\"/\",\"n\"],\"-\",\"mouseY\"]],[13,\"ddx\",[6,\"std.select\",[\"dx\",[1,\"dx\",\"*\",\"aspect\"],[1,\"aspect\",\">=\",[5,\"1\"]]]]],[13,\"ddy\",[6,\"std.select\",[[1,\"dy\",\"/\",\"aspect\"],\"dy\",[1,\"aspect\",\">=\",[5,\"1\"]]]]],[13,\"dist\",[6,\"std.sqrt\",[[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]]]],[13,\"inner\",[1,\"radius\",\"*\",[1,[5,\"1\"],\"-\",\"falloff\"]]],[10,[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"inner\",\"radius\",\"dist\"]]]]]]},externals:{n:()=>e,\"std.select\":()=>N,\"std.sqrt\":()=>Ue,\"std.smoothstep\":()=>be}})&&t.f)({})).$name(\"aspectBrushWeight\")}function Sie(e){let t=OP.get(e);if(t)return t;let s=Tie(e);return OP.set(e,s),s}function Gz(e){let{n:t,format:s,namePrefix:r,vertical:o,direction:a}=e,i=t*t,l=t,c=t-1,d=gt({inputWidth:n,inputHeight:n}).$name(\"SortKeyParams\"),u=gt({mouseX:n,mouseY:n,radius:n,falloff:n,decay:n,aspect:n,seed:n}).$name(\"SortSwapParams\"),p=(globalThis.__TYPEGPU_AUTONAME__??(x=>x))(w.bindGroupLayout({input:{texture:Zt(n)},lumaBuf:{storage:Fe(n,i),access:\"mutable\"},params:{uniform:d}}),\"lumaLayout\"),_=(globalThis.__TYPEGPU_AUTONAME__??(x=>x))(w.bindGroupLayout({readBuf:{storage:Fe(n,i),access:\"readonly\"},writeBuf:{storage:Fe(n,i),access:\"mutable\"},lumaBuf:{storage:Fe(n,i),access:\"readonly\"},params:{uniform:u}}),\"swapLayout\"),f=(globalThis.__TYPEGPU_AUTONAME__??(x=>x))(w.bindGroupLayout({srcBuf:{storage:Fe(n,i),access:\"readonly\"},stateTex:{storageTexture:zt(s,\"write-only\")}}),\"outputLayout\"),h=Sie(t),g=w.fn([M,M])((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=p.$.params,R=__tsover_add(__tsover_mul(C,M(t)),P),U=__tsover_div(__tsover_add(n(P),.5),l),L=__tsover_div(__tsover_add(n(C),.5),l),k=G(j(Ut(__tsover_mul(U,E.inputWidth))),0,j(__tsover_sub(E.inputWidth,1))),D=G(j(Ut(__tsover_mul(L,E.inputHeight))),0,j(__tsover_sub(E.inputHeight,1))),O=Yt(Ye(p.$.input,fe(M(k),M(D)),0)).xyz;p.$.lumaBuf[R]=Se(O,A(.299,.587,.114))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"lumaLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"nF\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"nF\"]],[13,\"ix\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"u\",\"*\",[7,\"p\",\"inputWidth\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]]]],[13,\"iy\",[6,\"std.clamp\",[[6,\"d.i32\",[[6,\"std.round\",[[1,\"v\",\"*\",[7,\"p\",\"inputHeight\"]]]]]],[5,\"0\"],[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"lumaLayout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"ix\"]],[6,\"d.u32\",[\"iy\"]]]],[5,\"0\"]]]],[13,\"rgb\",[7,[6,\"blend.unpremultiplyAlpha\",[\"texel\"]],\"xyz\"]],[2,[8,[7,[7,\"lumaLayout\",\"$\"],\"lumaBuf\"],\"idx\"],\"=\",[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]]]]},externals:{lumaLayout:()=>p,\"d.u32\":()=>M,n:()=>t,\"d.f32\":()=>n,nF:()=>l,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.round\":()=>Ut,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"blend.unpremultiplyAlpha\":()=>Yt,\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&x.f)({})).$name(`${r}Luma`),y=x=>w.fn([M,M])((P=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(P.f=((C,E)=>{\"use gpu\";let R=_.$.params,U=__tsover_add(__tsover_mul(E,M(t)),C),L=n(C),k=n(E),D=o?E:C,O=o?C:E,F=n(D),q=j(D),Y=_.$.readBuf[U],V=G(__tsover_add(F,Y),0,c),X=__tsover_mod(__tsover_add(D,M(x)),M(2))===M(0),Q=N(__tsover_sub(q,1),__tsover_add(q,1),X),te=Q>=0&&Q<t,J=G(Q,0,__tsover_sub(t,1)),oe=M(J),le=o?O:oe,se=o?oe:O,Te=__tsover_add(__tsover_mul(se,M(t)),le),de=_.$.readBuf[Te],xe=G(__tsover_add(n(J),de),0,c),Ae=G(ie(V),0,c),De=ye(__tsover_add(Ae,1),c),Ee=o?__tsover_add(__tsover_mul(M(Ae),M(t)),O):__tsover_add(__tsover_mul(O,M(t)),M(Ae)),ze=o?__tsover_add(__tsover_mul(M(De),M(t)),O):__tsover_add(__tsover_mul(O,M(t)),M(De)),Ie=K(_.$.lumaBuf[Ee],_.$.lumaBuf[ze],__tsover_sub(V,Ae)),ke=G(ie(xe),0,c),Me=ye(__tsover_add(ke,1),c),we=o?__tsover_add(__tsover_mul(M(ke),M(t)),O):__tsover_add(__tsover_mul(O,M(t)),M(ke)),Ve=o?__tsover_add(__tsover_mul(M(Me),M(t)),O):__tsover_add(__tsover_mul(O,M(t)),M(Me)),qe=K(_.$.lumaBuf[we],_.$.lumaBuf[Ve],__tsover_sub(xe,ke)),st=h(L,k,R.mouseX,R.mouseY,R.radius,R.falloff,R.aspect),pt=h(n(le),n(se),R.mouseX,R.mouseY,R.radius,R.falloff,R.aspect),Ze=Wb(A(L,k,R.seed)),nt=te&&ye(st,pt)>Ze,ht=__tsover_mul(__tsover_sub(Ie,qe),a),yt=N(ht<0,ht>0,X),St=K(N(V,xe,nt&&yt),F,R.decay);_.$.writeBuf[U]=__tsover_sub(St,F)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"swapLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"xf\",[6,\"d.f32\",[\"cx\"]]],[13,\"yf\",[6,\"d.f32\",[\"cy\"]]],[13,\"lineC\",[105,\"vertical\",\"cy\",\"cx\"]],[13,\"fixedC\",[105,\"vertical\",\"cx\",\"cy\"]],[13,\"lineCf\",[6,\"d.f32\",[\"lineC\"]]],[13,\"lineCi\",[6,\"d.i32\",[\"lineC\"]]],[13,\"myOff\",[8,[7,[7,\"swapLayout\",\"$\"],\"readBuf\"],\"idx\"]],[13,\"myCol\",[6,\"std.clamp\",[[1,\"lineCf\",\"+\",\"myOff\"],[5,\"0\"],\"maxC\"]]],[13,\"isLeft\",[1,[1,[1,\"lineC\",\"+\",[6,\"d.u32\",[\"parity\"]]],\"%\",[6,\"d.u32\",[[5,\"2\"]]]],\"===\",[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"partnerLineI\",[6,\"std.select\",[[1,\"lineCi\",\"-\",[5,\"1\"]],[1,\"lineCi\",\"+\",[5,\"1\"]],\"isLeft\"]]],[13,\"valid\",[3,[1,\"partnerLineI\",\">=\",[5,\"0\"]],\"&&\",[1,\"partnerLineI\",\"<\",\"n\"]]],[13,\"pClamped\",[6,\"std.clamp\",[\"partnerLineI\",[5,\"0\"],[1,\"n\",\"-\",[5,\"1\"]]]]],[13,\"pClampedU\",[6,\"d.u32\",[\"pClamped\"]]],[13,\"partnerX\",[105,\"vertical\",\"fixedC\",\"pClampedU\"]],[13,\"partnerY\",[105,\"vertical\",\"pClampedU\",\"fixedC\"]],[13,\"partnerIdx\",[1,[1,\"partnerY\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"partnerX\"]],[13,\"partnerOff\",[8,[7,[7,\"swapLayout\",\"$\"],\"readBuf\"],\"partnerIdx\"]],[13,\"partnerCol\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"pClamped\"]],\"+\",\"partnerOff\"],[5,\"0\"],\"maxC\"]]],[13,\"myC0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"myCol\"]],[5,\"0\"],\"maxC\"]]],[13,\"myC1\",[6,\"std.min\",[[1,\"myC0\",\"+\",[5,\"1\"]],\"maxC\"]]],[13,\"myIdx0\",[105,\"vertical\",[1,[1,[6,\"d.u32\",[\"myC0\"]],\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"fixedC\"],[1,[1,\"fixedC\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",[6,\"d.u32\",[\"myC0\"]]]]],[13,\"myIdx1\",[105,\"vertical\",[1,[1,[6,\"d.u32\",[\"myC1\"]],\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"fixedC\"],[1,[1,\"fixedC\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",[6,\"d.u32\",[\"myC1\"]]]]],[13,\"myKey\",[6,\"std.mix\",[[8,[7,[7,\"swapLayout\",\"$\"],\"lumaBuf\"],\"myIdx0\"],[8,[7,[7,\"swapLayout\",\"$\"],\"lumaBuf\"],\"myIdx1\"],[1,\"myCol\",\"-\",\"myC0\"]]]],[13,\"pC0\",[6,\"std.clamp\",[[6,\"std.floor\",[\"partnerCol\"]],[5,\"0\"],\"maxC\"]]],[13,\"pC1\",[6,\"std.min\",[[1,\"pC0\",\"+\",[5,\"1\"]],\"maxC\"]]],[13,\"pIdx0\",[105,\"vertical\",[1,[1,[6,\"d.u32\",[\"pC0\"]],\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"fixedC\"],[1,[1,\"fixedC\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",[6,\"d.u32\",[\"pC0\"]]]]],[13,\"pIdx1\",[105,\"vertical\",[1,[1,[6,\"d.u32\",[\"pC1\"]],\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"fixedC\"],[1,[1,\"fixedC\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",[6,\"d.u32\",[\"pC1\"]]]]],[13,\"partnerKey\",[6,\"std.mix\",[[8,[7,[7,\"swapLayout\",\"$\"],\"lumaBuf\"],\"pIdx0\"],[8,[7,[7,\"swapLayout\",\"$\"],\"lumaBuf\"],\"pIdx1\"],[1,\"partnerCol\",\"-\",\"pC0\"]]]],[13,\"wMy\",[6,\"brushWeight\",[\"xf\",\"yf\",[7,\"p\",\"mouseX\"],[7,\"p\",\"mouseY\"],[7,\"p\",\"radius\"],[7,\"p\",\"falloff\"],[7,\"p\",\"aspect\"]]]],[13,\"wP\",[6,\"brushWeight\",[[6,\"d.f32\",[\"partnerX\"]],[6,\"d.f32\",[\"partnerY\"]],[7,\"p\",\"mouseX\"],[7,\"p\",\"mouseY\"],[7,\"p\",\"radius\"],[7,\"p\",\"falloff\"],[7,\"p\",\"aspect\"]]]],[13,\"rnd\",[6,\"noise.hash13\",[[6,\"d.vec3f\",[\"xf\",\"yf\",[7,\"p\",\"seed\"]]]]]],[13,\"gate\",[3,\"valid\",\"&&\",[1,[6,\"std.min\",[\"wMy\",\"wP\"]],\">\",\"rnd\"]]],[13,\"keyDiff\",[1,[1,\"myKey\",\"-\",\"partnerKey\"],\"*\",\"direction\"]],[13,\"takePartner\",[6,\"std.select\",[[1,\"keyDiff\",\"<\",[5,\"0\"]],[1,\"keyDiff\",\">\",[5,\"0\"]],\"isLeft\"]]],[13,\"newCol\",[6,\"std.select\",[\"myCol\",\"partnerCol\",[3,\"gate\",\"&&\",\"takePartner\"]]]],[13,\"decayed\",[6,\"std.mix\",[\"newCol\",\"lineCf\",[7,\"p\",\"decay\"]]]],[2,[8,[7,[7,\"swapLayout\",\"$\"],\"writeBuf\"],\"idx\"],\"=\",[1,\"decayed\",\"-\",\"lineCf\"]]]]},externals:{swapLayout:()=>_,\"d.u32\":()=>M,n:()=>t,\"d.f32\":()=>n,vertical:()=>o,\"d.i32\":()=>j,\"std.clamp\":()=>G,maxC:()=>c,parity:()=>x,\"std.select\":()=>N,\"std.floor\":()=>ie,\"std.min\":()=>ye,\"std.mix\":()=>K,brushWeight:()=>h,\"noise.hash13\":()=>Wb,\"d.vec3f\":()=>A,direction:()=>a}})&&P.f)({})).$name(`${r}Swap`),b=w.fn([M,M])((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=__tsover_add(__tsover_mul(C,M(t)),P),R=o?C:P,U=G(__tsover_add(n(R),f.$.srcBuf[E]),0,c),L=__tsover_div(__tsover_add(U,.5),l);lt(f.$.stateTex,fe(P,C),S(L,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"n\"]]],\"+\",\"cx\"]],[13,\"lineC\",[105,\"vertical\",\"cy\",\"cx\"]],[13,\"col\",[6,\"std.clamp\",[[1,[6,\"d.f32\",[\"lineC\"]],\"+\",[8,[7,[7,\"outputLayout\",\"$\"],\"srcBuf\"],\"idx\"]],[5,\"0\"],\"maxC\"]]],[13,\"u\",[1,[1,\"col\",\"+\",[5,\"0.5\"]],\"/\",\"nF\"]],[6,\"std.textureStore\",[[7,[7,\"outputLayout\",\"$\"],\"stateTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"u\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{\"d.u32\":()=>M,n:()=>t,vertical:()=>o,\"std.clamp\":()=>G,\"d.f32\":()=>n,outputLayout:()=>f,maxC:()=>c,nF:()=>l,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&x.f)({})).$name(`${r}Output`);return{lumaLayout:p,LumaParams:d,swapLayout:_,SwapParams:u,outputLayout:f,key:g,swap0:y(0),swap1:y(1),output:b}}var GP=w.fn([S,S,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=re(t.xyz.sub(s.xyz)),a=re(__tsover_sub(t.w,s.w)),i=__tsover_add(Se(o,A(.299,.587,.114)),a);return be(r,__tsover_add(__tsover_mul(r,2),.001),i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"live\"},{type:\"i\",name:\"prev\"},{type:\"i\",name:\"threshold\"}],body:[0,[[13,\"dRgb\",[6,\"std.abs\",[[6,[7,[7,\"live\",\"xyz\"],\"sub\"],[[7,\"prev\",\"xyz\"]]]]]],[13,\"dA\",[6,\"std.abs\",[[1,[7,\"live\",\"w\"],\"-\",[7,\"prev\",\"w\"]]]]],[13,\"diff\",[1,[6,\"std.dot\",[\"dRgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]],\"+\",\"dA\"]],[10,[6,\"std.smoothstep\",[\"threshold\",[1,[1,\"threshold\",\"*\",[5,\"2\"]],\"+\",[5,\"0.001\"]],\"diff\"]]]]]},externals:{\"std.abs\":()=>re,\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.smoothstep\":()=>be}})&&e.f)({})).$name(\"frameDiffMask\"),FP=w.fn([A,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=cd(t);return ml(A(Re(__tsover_add(r.x,s)),r.y,r.z))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"amount\"}],body:[0,[[13,\"hsl\",[6,\"colorMixing.rgbToHsl\",[\"rgb\"]]],[13,\"rotated\",[6,\"d.vec3f\",[[6,\"std.fract\",[[1,[7,\"hsl\",\"x\"],\"+\",\"amount\"]]],[7,\"hsl\",\"y\"],[7,\"hsl\",\"z\"]]]],[10,[6,\"colorMixing.hslToRgb\",[\"rotated\"]]]]]},externals:{\"colorMixing.rgbToHsl\":()=>cd,\"d.vec3f\":()=>A,\"std.fract\":()=>Re,\"colorMixing.hslToRgb\":()=>ml}})&&e.f)({})).$name(\"hueRotateTurns\");function Fz(e){let{res:t,format:s,namePrefix:r,source:o,hueCycle:a,diffuseHalfUv:i,driftScale:l}=e,c=gt({persistence:n,diffusion:n,driftX:n,driftY:n,dt:n,zoom:n,hueRate:n,blendMode:n,motionThreshold:n}).$name(\"FeedbackTrailParams\"),d=(globalThis.__TYPEGPU_AUTONAME__??(x=>x))(w.bindGroupLayout({src:{texture:Zt(n)},prev:{texture:Zt(n)},next:{storageTexture:zt(s,\"write-only\")},display:{storageTexture:zt(s,\"write-only\")},prevLive:{texture:Zt(n)},prevLiveOut:{storageTexture:zt(s,\"write-only\")},samp:{sampler:\"filtering\"},params:{uniform:c}}),\"layout\"),u=w.fn([n,n],S)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";return Xl(d.$.prev,d.$.samp,m(P,C),n(0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvx\"},{type:\"i\",name:\"uvy\"}],body:[0,[[10,[6,\"std.textureSampleLevel\",[[7,[7,\"layout\",\"$\"],\"prev\"],[7,[7,\"layout\",\"$\"],\"samp\"],[6,\"d.vec2f\",[\"uvx\",\"uvy\"]],[6,\"d.f32\",[[5,\"0\"]]]]]]]]},externals:{\"std.textureSampleLevel\":()=>Xl,layout:()=>d,\"d.vec2f\":()=>m,\"d.f32\":()=>n}})&&x.f)({})).$name(`${r}SampleHistory`),p=w.fn([n,n],S)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";return Xl(d.$.src,d.$.samp,m(P,C),n(0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uvx\"},{type:\"i\",name:\"uvy\"}],body:[0,[[10,[6,\"std.textureSampleLevel\",[[7,[7,\"layout\",\"$\"],\"src\"],[7,[7,\"layout\",\"$\"],\"samp\"],[6,\"d.vec2f\",[\"uvx\",\"uvy\"]],[6,\"d.f32\",[[5,\"0\"]]]]]]]]},externals:{\"std.textureSampleLevel\":()=>Xl,layout:()=>d,\"d.vec2f\":()=>m,\"d.f32\":()=>n}})&&x.f)({})).$name(`${r}SampleLive`),_=w.fn([M,M],m)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=n(t);return m(__tsover_div(__tsover_add(n(P),.5),E),__tsover_div(__tsover_add(n(C),.5),E))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"r\",[6,\"d.f32\",[\"res\"]]],[10,[6,\"d.vec2f\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"r\"],[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"r\"]]]]]]},externals:{\"d.f32\":()=>n,res:()=>t,\"d.vec2f\":()=>m}})&&x.f)({})).$name(`${r}TexelUv`),f=w.fn([m],m)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=(P=>{\"use gpu\";let C=d.$.params,E=B(C.zoom,.01);return m(__tsover_sub(__tsover_add(__tsover_div(__tsover_sub(P.x,.5),E),.5),__tsover_mul(__tsover_mul(C.driftX,C.dt),l)),__tsover_sub(__tsover_add(__tsover_div(__tsover_sub(P.y,.5),E),.5),__tsover_mul(__tsover_mul(C.driftY,C.dt),l)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"zoom\",[6,\"std.max\",[[7,\"P\",\"zoom\"],[5,\"0.01\"]]]],[10,[6,\"d.vec2f\",[[1,[1,[1,[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],\"/\",\"zoom\"],\"+\",[5,\"0.5\"]],\"-\",[1,[1,[7,\"P\",\"driftX\"],\"*\",[7,\"P\",\"dt\"]],\"*\",\"driftScale\"]],[1,[1,[1,[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]],\"/\",\"zoom\"],\"+\",[5,\"0.5\"]],\"-\",[1,[1,[7,\"P\",\"driftY\"],\"*\",[7,\"P\",\"dt\"]],\"*\",\"driftScale\"]]]]]]]},externals:{layout:()=>d,\"std.max\":()=>B,\"d.vec2f\":()=>m,driftScale:()=>l}})&&x.f)({})).$name(`${r}FeedbackUv`),h=w.fn([n,n],S)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=d.$.params,R=__tsover_mul(E.diffusion,i);return u(__tsover_sub(P,R),__tsover_sub(C,R)).add(u(__tsover_add(P,R),__tsover_sub(C,R))).add(u(__tsover_sub(P,R),__tsover_add(C,R))).add(u(__tsover_add(P,R),__tsover_add(C,R))).mul(__tsover_mul(E.persistence,.25))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"bx\"},{type:\"i\",name:\"by\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"h\",[1,[7,\"P\",\"diffusion\"],\"*\",\"diffuseHalfUv\"]],[13,\"sum\",[6,[7,[6,[7,[6,[7,[6,\"sampleHistory\",[[1,\"bx\",\"-\",\"h\"],[1,\"by\",\"-\",\"h\"]]],\"add\"],[[6,\"sampleHistory\",[[1,\"bx\",\"+\",\"h\"],[1,\"by\",\"-\",\"h\"]]]]],\"add\"],[[6,\"sampleHistory\",[[1,\"bx\",\"-\",\"h\"],[1,\"by\",\"+\",\"h\"]]]]],\"add\"],[[6,\"sampleHistory\",[[1,\"bx\",\"+\",\"h\"],[1,\"by\",\"+\",\"h\"]]]]]],[10,[6,[7,\"sum\",\"mul\"],[[1,[7,\"P\",\"persistence\"],\"*\",[5,\"0.25\"]]]]]]]},externals:{layout:()=>d,diffuseHalfUv:()=>i,sampleHistory:()=>u}})&&x.f)({})).$name(`${r}Decayed`),g=w.fn([n,n],S)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=h(P,C),R=B(E.w,1e-4);return S(FP(E.xyz.div(R),d.$.params.hueRate).mul(E.w),E.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"bx\"},{type:\"i\",name:\"by\"}],body:[0,[[13,\"dec\",[6,\"decayed\",[\"bx\",\"by\"]]],[13,\"a\",[6,\"std.max\",[[7,\"dec\",\"w\"],[5,\"0.0001\"]]]],[13,\"rotated\",[6,\"hueRotateTurns\",[[6,[7,[7,\"dec\",\"xyz\"],\"div\"],[\"a\"]],[7,[7,[7,\"layout\",\"$\"],\"params\"],\"hueRate\"]]]],[10,[6,\"d.vec4f\",[[6,[7,\"rotated\",\"mul\"],[[7,\"dec\",\"w\"]]],[7,\"dec\",\"w\"]]]]]]},externals:{decayed:()=>h,\"std.max\":()=>B,hueRotateTurns:()=>FP,layout:()=>d,\"d.vec4f\":()=>S}})&&x.f)({})).$name(`${r}DecayedTinted`),y=a?g:h,b=w.fn([S,n,S],S)((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C,E)=>{\"use gpu\";let R=d.$.params,U=S(P.xyz.mul(C),__tsover_mul(P.w,C)),L=__tsover_add(U.w,__tsover_mul(E.w,__tsover_sub(1,U.w))),k=U.xyz.add(E.xyz.mul(__tsover_sub(1,U.w))),D=U.xyz.add(E.xyz).sub(U.xyz.mul(E.xyz)),O=U.xyz.add(E.xyz);return S(N(N(k,D,R.blendMode>.5),O,R.blendMode>1.5),L)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"live\"},{type:\"i\",name:\"m\"},{type:\"i\",name:\"trail\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"s\",[6,\"d.vec4f\",[[6,[7,[7,\"live\",\"xyz\"],\"mul\"],[\"m\"]],[1,[7,\"live\",\"w\"],\"*\",\"m\"]]]],[13,\"outA\",[1,[7,\"s\",\"w\"],\"+\",[1,[7,\"trail\",\"w\"],\"*\",[1,[5,\"1\"],\"-\",[7,\"s\",\"w\"]]]]],[13,\"rgbNormal\",[6,[7,[7,\"s\",\"xyz\"],\"add\"],[[6,[7,[7,\"trail\",\"xyz\"],\"mul\"],[[1,[5,\"1\"],\"-\",[7,\"s\",\"w\"]]]]]]],[13,\"rgbScreen\",[6,[7,[6,[7,[7,\"s\",\"xyz\"],\"add\"],[[7,\"trail\",\"xyz\"]]],\"sub\"],[[6,[7,[7,\"s\",\"xyz\"],\"mul\"],[[7,\"trail\",\"xyz\"]]]]]],[13,\"rgbAdd\",[6,[7,[7,\"s\",\"xyz\"],\"add\"],[[7,\"trail\",\"xyz\"]]]],[13,\"rgbSN\",[6,\"std.select\",[\"rgbNormal\",\"rgbScreen\",[1,[7,\"P\",\"blendMode\"],\">\",[5,\"0.5\"]]]]],[10,[6,\"d.vec4f\",[[6,\"std.select\",[\"rgbSN\",\"rgbAdd\",[1,[7,\"P\",\"blendMode\"],\">\",[5,\"1.5\"]]]],\"outA\"]]]]]},externals:{layout:()=>d,\"d.vec4f\":()=>S,\"std.select\":()=>N}})&&x.f)({})).$name(`${r}Stamp`);return{layout:d,Params:c,kernel:o===\"motion\"?w.fn([M,M])((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=_(P,C),R=f(E),U=y(R.x,R.y),L=p(E.x,E.y),k=GP(L,Ye(d.$.prevLive,fe(P,C),0),d.$.params.motionThreshold);lt(d.$.next,fe(P,C),b(L,k,U)),lt(d.$.display,fe(P,C),U),lt(d.$.prevLiveOut,fe(P,C),L)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"uv\",[6,\"texelUv\",[\"cx\",\"cy\"]]],[13,\"b\",[6,\"feedbackUv\",[\"uv\"]]],[13,\"trail\",[6,\"trailFn\",[[7,\"b\",\"x\"],[7,\"b\",\"y\"]]]],[13,\"live\",[6,\"sampleLive\",[[7,\"uv\",\"x\"],[7,\"uv\",\"y\"]]]],[13,\"pl\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"prevLive\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[5,\"0\"]]]],[13,\"m\",[6,\"frameDiffMask\",[\"live\",\"pl\",[7,[7,[7,\"layout\",\"$\"],\"params\"],\"motionThreshold\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"next\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"stamp\",[\"live\",\"m\",\"trail\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"display\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"trail\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"prevLiveOut\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"live\"]]]]},externals:{texelUv:()=>_,feedbackUv:()=>f,trailFn:()=>y,sampleLive:()=>p,\"std.textureLoad\":()=>Ye,layout:()=>d,\"d.vec2u\":()=>fe,frameDiffMask:()=>GP,\"std.textureStore\":()=>lt,stamp:()=>b}})&&x.f)({})).$name(`${r}Step`):w.fn([M,M])((x=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(x.f=((P,C)=>{\"use gpu\";let E=_(P,C),R=f(E),U=y(R.x,R.y),L=b(p(E.x,E.y),n(1),U);lt(d.$.next,fe(P,C),L),lt(d.$.display,fe(P,C),L)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"uv\",[6,\"texelUv\",[\"cx\",\"cy\"]]],[13,\"b\",[6,\"feedbackUv\",[\"uv\"]]],[13,\"trail\",[6,\"trailFn\",[[7,\"b\",\"x\"],[7,\"b\",\"y\"]]]],[13,\"live\",[6,\"sampleLive\",[[7,\"uv\",\"x\"],[7,\"uv\",\"y\"]]]],[13,\"outC\",[6,\"stamp\",[\"live\",[6,\"d.f32\",[[5,\"1\"]]],\"trail\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"next\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"outC\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"display\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"outC\"]]]]},externals:{texelUv:()=>_,feedbackUv:()=>f,trailFn:()=>y,sampleLive:()=>p,stamp:()=>b,\"d.f32\":()=>n,\"std.textureStore\":()=>lt,layout:()=>d,\"d.vec2u\":()=>fe}})&&x.f)({})).$name(`${r}StepAlpha`)}}function Iz(e){let{res:t,format:s,namePrefix:r}=e,o=e.blockMix?.coarseScale??2.6,a=e.blockMix?.coarsePick??.35,i=e.generationLoss?.levels??18,l=e.generationLoss?.amount??.12,c=e.generationLoss?.decay??.9985,d=gt({time:n,dt:n,seed:n,intensity:n,blockSize:n,drift:n,churn:n}).$name(\"MacroblockAdvectParams\"),u=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(w.bindGroupLayout({src:{texture:Zt(n)},prev:{texture:Zt(n)},next:{storageTexture:zt(s,\"write-only\")},display:{storageTexture:zt(s,\"write-only\")},params:{uniform:d}}),\"layout\");return{layout:u,Params:d,kernel:w.fn([M,M])((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";let h=u.$.params,g=n(t),y=n(_),b=n(f),x=__tsover_div(__tsover_add(y,.5),g),P=__tsover_div(__tsover_add(b,.5),g),C=__tsover_add(.15,__tsover_mul(h.churn,.8)),E=B(h.blockSize,8),R=__tsover_mul(E,o),U=ie(__tsover_div(y,R)),L=ie(__tsover_div(b,R)),k=Xs(__tsover_add(__tsover_add(__tsover_mul(U,91.17),__tsover_mul(L,41.23)),__tsover_mul(h.seed,3.7))),D=ie(__tsover_add(__tsover_mul(__tsover_mul(h.time,C),.25),k)),O=N(E,R,Xs(__tsover_add(__tsover_add(__tsover_add(__tsover_mul(U,12.9898),__tsover_mul(L,78.233)),__tsover_mul(D,37.71)),__tsover_mul(h.seed,3.7)))<a),F=ie(__tsover_div(y,O)),q=ie(__tsover_div(b,O)),Y=Xs(__tsover_add(__tsover_add(__tsover_mul(F,127.1),__tsover_mul(q,311.7)),__tsover_mul(h.seed,17.9))),V=__tsover_mul(C,__tsover_add(.7,__tsover_mul(.6,Xs(__tsover_add(__tsover_add(__tsover_mul(F,53.7),__tsover_mul(q,97.3)),__tsover_mul(h.seed,5.3)))))),X=ie(__tsover_add(__tsover_mul(h.time,V),Y)),Q=__tsover_add(__tsover_add(__tsover_add(__tsover_mul(F,127.1),__tsover_mul(q,311.7)),__tsover_mul(X,74.7)),__tsover_mul(h.seed,17.9)),te=Xs(__tsover_add(Q,.13)),J=Xs(__tsover_add(Q,7.77)),oe=Xs(__tsover_add(Q,23.19)),le=__tsover_div(__tsover_mul(__tsover_add(F,.5),O),g),se=__tsover_div(__tsover_mul(__tsover_add(q,.5),O),g),Te=__tsover_add(__tsover_add(.5,__tsover_mul(__tsover_mul(.25,ae(__tsover_add(__tsover_add(__tsover_mul(le,2.7),__tsover_mul(h.time,.31)),h.seed))),ae(__tsover_add(__tsover_sub(__tsover_mul(se,2.1),__tsover_mul(h.time,.23)),__tsover_mul(h.seed,.4))))),__tsover_mul(.25,ae(__tsover_add(__tsover_add(__tsover_mul(__tsover_add(le,se),1.6),__tsover_mul(h.time,.17)),__tsover_mul(h.seed,.7))))),de=__tsover_sub(1,__tsover_mul(h.intensity,.55)),xe=be(__tsover_sub(de,.09),__tsover_add(de,.09),__tsover_add(Te,__tsover_mul(__tsover_sub(te,.5),.3))),Ae=__tsover_add(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(le,1.9),__tsover_mul(h.seed,3.1))),1.7),__tsover_mul(pe(__tsover_sub(__tsover_mul(se,2.3),__tsover_mul(h.seed,2.7))),1.3)),__tsover_mul(h.time,.15)),De=__tsover_add(Ae,__tsover_mul(__tsover_sub(J,.5),1.1)),Ee=__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(__tsover_add(.5,__tsover_mul(.5,oe)),h.drift),h.dt),10),__tsover_div(O,g)),ze=__tsover_sub(x,__tsover_mul(pe(De),Ee)),Ie=__tsover_sub(P,__tsover_mul(ae(De),Ee)),ke=__tsover_mul(__tsover_mul(__tsover_mul(N(n(0),n(1),ze>=0),N(n(0),n(1),ze<=1)),N(n(0),n(1),Ie>=0)),N(n(0),n(1),Ie<=1)),Me=__tsover_sub(__tsover_mul(G(ze,0,1),g),.5),we=__tsover_sub(__tsover_mul(G(Ie,0,1),g),.5),Ve=ie(Me),qe=ie(we),st=__tsover_sub(Me,Ve),pt=__tsover_sub(we,qe),Ze=__tsover_sub(g,1),nt=M(G(Ve,n(0),Ze)),ht=M(G(__tsover_add(Ve,1),n(0),Ze)),yt=M(G(qe,n(0),Ze)),St=M(G(__tsover_add(qe,1),n(0),Ze)),Mt=Ye(u.$.prev,fe(nt,yt),0),Rt=Ye(u.$.prev,fe(ht,yt),0),Kt=Ye(u.$.prev,fe(nt,St),0),Wt=Ye(u.$.prev,fe(ht,St),0),ls=K(K(Mt,Rt,st),K(Kt,Wt,st),pt),us=ie(ls.xyz.mul(i)).add(A(.5,.5,.5)).div(i),os=S(K(ls.xyz,us,l).mul(c),ls.w),bs=G(__tsover_mul(h.dt,__tsover_add(1.2,__tsover_mul(__tsover_sub(1,h.intensity),2))),0,.2),hs=Ti(u.$.src),gs=n(hs.x),Gs=n(hs.y),Ss=__tsover_sub(__tsover_mul(x,gs),.5),or=__tsover_sub(__tsover_mul(P,Gs),.5),yr=ie(Ss),lr=ie(or),Ur=__tsover_sub(Ss,yr),so=__tsover_sub(or,lr),ro=M(G(yr,n(0),__tsover_sub(gs,1))),_e=M(G(__tsover_add(yr,1),n(0),__tsover_sub(gs,1))),We=M(G(lr,n(0),__tsover_sub(Gs,1))),Le=M(G(__tsover_add(lr,1),n(0),__tsover_sub(Gs,1))),Be=Ye(u.$.src,fe(ro,We),0),Oe=Ye(u.$.src,fe(_e,We),0),tt=Ye(u.$.src,fe(ro,Le),0),_t=Ye(u.$.src,fe(_e,Le),0),Bt=K(K(Be,Oe,Ur),K(tt,_t,Ur),so),Dt=K(Bt,K(os,Bt,bs),__tsover_mul(xe,ke));lt(u.$.next,fe(_,f),Dt),lt(u.$.display,fe(_,f),Dt)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"resF\",[6,\"d.f32\",[\"res\"]]],[13,\"px\",[6,\"d.f32\",[\"cx\"]]],[13,\"py\",[6,\"d.f32\",[\"cy\"]]],[13,\"fx\",[1,[1,\"px\",\"+\",[5,\"0.5\"]],\"/\",\"resF\"]],[13,\"fy\",[1,[1,\"py\",\"+\",[5,\"0.5\"]],\"/\",\"resF\"]],[13,\"churnRate\",[1,[5,\"0.15\"],\"+\",[1,[7,\"P\",\"churn\"],\"*\",[5,\"0.8\"]]]],[13,\"bsF\",[6,\"std.max\",[[7,\"P\",\"blockSize\"],[5,\"8\"]]]],[13,\"coarse\",[1,\"bsF\",\"*\",\"coarseScale\"]],[13,\"cbx\",[6,\"std.floor\",[[1,\"px\",\"/\",\"coarse\"]]]],[13,\"cby\",[6,\"std.floor\",[[1,\"py\",\"/\",\"coarse\"]]]],[13,\"cPhase\",[6,\"noise.hash11\",[[1,[1,[1,\"cbx\",\"*\",[5,\"91.17\"]],\"+\",[1,\"cby\",\"*\",[5,\"41.23\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"3.7\"]]]]]],[13,\"cEpoch\",[6,\"std.floor\",[[1,[1,[1,[7,\"P\",\"time\"],\"*\",\"churnRate\"],\"*\",[5,\"0.25\"]],\"+\",\"cPhase\"]]]],[13,\"scalePick\",[6,\"noise.hash11\",[[1,[1,[1,[1,\"cbx\",\"*\",[5,\"12.9898\"]],\"+\",[1,\"cby\",\"*\",[5,\"78.233\"]]],\"+\",[1,\"cEpoch\",\"*\",[5,\"37.71\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"3.7\"]]]]]],[13,\"bs\",[6,\"std.select\",[\"bsF\",\"coarse\",[1,\"scalePick\",\"<\",\"coarsePick\"]]]],[13,\"bx\",[6,\"std.floor\",[[1,\"px\",\"/\",\"bs\"]]]],[13,\"by\",[6,\"std.floor\",[[1,\"py\",\"/\",\"bs\"]]]],[13,\"bPhase\",[6,\"noise.hash11\",[[1,[1,[1,\"bx\",\"*\",[5,\"127.1\"]],\"+\",[1,\"by\",\"*\",[5,\"311.7\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"17.9\"]]]]]],[13,\"bRate\",[1,\"churnRate\",\"*\",[1,[5,\"0.7\"],\"+\",[1,[5,\"0.6\"],\"*\",[6,\"noise.hash11\",[[1,[1,[1,\"bx\",\"*\",[5,\"53.7\"]],\"+\",[1,\"by\",\"*\",[5,\"97.3\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"5.3\"]]]]]]]]],[13,\"bEpoch\",[6,\"std.floor\",[[1,[1,[7,\"P\",\"time\"],\"*\",\"bRate\"],\"+\",\"bPhase\"]]]],[13,\"bh\",[1,[1,[1,[1,\"bx\",\"*\",[5,\"127.1\"]],\"+\",[1,\"by\",\"*\",[5,\"311.7\"]]],\"+\",[1,\"bEpoch\",\"*\",[5,\"74.7\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"17.9\"]]]],[13,\"h1\",[6,\"noise.hash11\",[[1,\"bh\",\"+\",[5,\"0.13\"]]]]],[13,\"h2\",[6,\"noise.hash11\",[[1,\"bh\",\"+\",[5,\"7.77\"]]]]],[13,\"h3\",[6,\"noise.hash11\",[[1,\"bh\",\"+\",[5,\"23.19\"]]]]],[13,\"bcx\",[1,[1,[1,\"bx\",\"+\",[5,\"0.5\"]],\"*\",\"bs\"],\"/\",\"resF\"]],[13,\"bcy\",[1,[1,[1,\"by\",\"+\",[5,\"0.5\"]],\"*\",\"bs\"],\"/\",\"resF\"]],[13,\"field\",[1,[1,[5,\"0.5\"],\"+\",[1,[1,[5,\"0.25\"],\"*\",[6,\"std.sin\",[[1,[1,[1,\"bcx\",\"*\",[5,\"2.7\"]],\"+\",[1,[7,\"P\",\"time\"],\"*\",[5,\"0.31\"]]],\"+\",[7,\"P\",\"seed\"]]]]],\"*\",[6,\"std.sin\",[[1,[1,[1,\"bcy\",\"*\",[5,\"2.1\"]],\"-\",[1,[7,\"P\",\"time\"],\"*\",[5,\"0.23\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"0.4\"]]]]]]],\"+\",[1,[5,\"0.25\"],\"*\",[6,\"std.sin\",[[1,[1,[1,[1,\"bcx\",\"+\",\"bcy\"],\"*\",[5,\"1.6\"]],\"+\",[1,[7,\"P\",\"time\"],\"*\",[5,\"0.17\"]]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"0.7\"]]]]]]]],[13,\"thresh\",[1,[5,\"1\"],\"-\",[1,[7,\"P\",\"intensity\"],\"*\",[5,\"0.55\"]]]],[13,\"hold\",[6,\"std.smoothstep\",[[1,\"thresh\",\"-\",[5,\"0.09\"]],[1,\"thresh\",\"+\",[5,\"0.09\"]],[1,\"field\",\"+\",[1,[1,\"h1\",\"-\",[5,\"0.5\"]],\"*\",[5,\"0.3\"]]]]]],[13,\"flowAng\",[1,[1,[1,[6,\"std.sin\",[[1,[1,\"bcx\",\"*\",[5,\"1.9\"]],\"+\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"3.1\"]]]]],\"*\",[5,\"1.7\"]],\"+\",[1,[6,\"std.cos\",[[1,[1,\"bcy\",\"*\",[5,\"2.3\"]],\"-\",[1,[7,\"P\",\"seed\"],\"*\",[5,\"2.7\"]]]]],\"*\",[5,\"1.3\"]]],\"+\",[1,[7,\"P\",\"time\"],\"*\",[5,\"0.15\"]]]],[13,\"ang\",[1,\"flowAng\",\"+\",[1,[1,\"h2\",\"-\",[5,\"0.5\"]],\"*\",[5,\"1.1\"]]]],[13,\"mag\",[1,[1,[1,[1,[1,[5,\"0.5\"],\"+\",[1,[5,\"0.5\"],\"*\",\"h3\"]],\"*\",[7,\"P\",\"drift\"]],\"*\",[7,\"P\",\"dt\"]],\"*\",[5,\"10\"]],\"*\",[1,\"bs\",\"/\",\"resF\"]]],[13,\"suRaw\",[1,\"fx\",\"-\",[1,[6,\"std.cos\",[\"ang\"]],\"*\",\"mag\"]]],[13,\"svRaw\",[1,\"fy\",\"-\",[1,[6,\"std.sin\",[\"ang\"]],\"*\",\"mag\"]]],[13,\"inBounds\",[1,[1,[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"suRaw\",\">=\",[5,\"0\"]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"suRaw\",\"<=\",[5,\"1\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"svRaw\",\">=\",[5,\"0\"]]]]],\"*\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"svRaw\",\"<=\",[5,\"1\"]]]]]],[13,\"su\",[1,[1,[6,\"std.clamp\",[\"suRaw\",[5,\"0\"],[5,\"1\"]]],\"*\",\"resF\"],\"-\",[5,\"0.5\"]]],[13,\"sv\",[1,[1,[6,\"std.clamp\",[\"svRaw\",[5,\"0\"],[5,\"1\"]]],\"*\",\"resF\"],\"-\",[5,\"0.5\"]]],[13,\"sx0\",[6,\"std.floor\",[\"su\"]]],[13,\"sy0\",[6,\"std.floor\",[\"sv\"]]],[13,\"sfx\",[1,\"su\",\"-\",\"sx0\"]],[13,\"sfy\",[1,\"sv\",\"-\",\"sy0\"]],[13,\"maxT\",[1,\"resF\",\"-\",[5,\"1\"]]],[13,\"ix0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"sx0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"ix1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"sx0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"iy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"sy0\",[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"iy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"sy0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],\"maxT\"]]]]],[13,\"p00\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[\"ix0\",\"iy0\"]],[5,\"0\"]]]],[13,\"p10\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[\"ix1\",\"iy0\"]],[5,\"0\"]]]],[13,\"p01\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[\"ix0\",\"iy1\"]],[5,\"0\"]]]],[13,\"p11\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"prev\"],[6,\"d.vec2u\",[\"ix1\",\"iy1\"]],[5,\"0\"]]]],[13,\"prevC\",[6,\"std.mix\",[[6,\"std.mix\",[\"p00\",\"p10\",\"sfx\"]],[6,\"std.mix\",[\"p01\",\"p11\",\"sfx\"]],\"sfy\"]]],[13,\"quant\",[6,[7,[6,[7,[6,\"std.floor\",[[6,[7,[7,\"prevC\",\"xyz\"],\"mul\"],[\"quantLevels\"]]]],\"add\"],[[6,\"d.vec3f\",[[5,\"0.5\"],[5,\"0.5\"],[5,\"0.5\"]]]]],\"div\"],[\"quantLevels\"]]],[13,\"heldRgb\",[6,[7,[6,\"std.mix\",[[7,\"prevC\",\"xyz\"],\"quant\",\"quantAmount\"]],\"mul\"],[\"energyDecay\"]]],[13,\"held\",[6,\"d.vec4f\",[\"heldRgb\",[7,\"prevC\",\"w\"]]]],[13,\"rekey\",[6,\"std.clamp\",[[1,[7,\"P\",\"dt\"],\"*\",[1,[5,\"1.2\"],\"+\",[1,[1,[5,\"1\"],\"-\",[7,\"P\",\"intensity\"]],\"*\",[5,\"2\"]]]],[5,\"0\"],[5,\"0.2\"]]]],[13,\"srcDims\",[6,\"std.textureDimensions\",[[7,[7,\"layout\",\"$\"],\"src\"]]]],[13,\"srcW\",[6,\"d.f32\",[[7,\"srcDims\",\"x\"]]]],[13,\"srcH\",[6,\"d.f32\",[[7,\"srcDims\",\"y\"]]]],[13,\"cu\",[1,[1,\"fx\",\"*\",\"srcW\"],\"-\",[5,\"0.5\"]]],[13,\"cv\",[1,[1,\"fy\",\"*\",\"srcH\"],\"-\",[5,\"0.5\"]]],[13,\"cx0\",[6,\"std.floor\",[\"cu\"]]],[13,\"cy0\",[6,\"std.floor\",[\"cv\"]]],[13,\"cfx\",[1,\"cu\",\"-\",\"cx0\"]],[13,\"cfy\",[1,\"cv\",\"-\",\"cy0\"]],[13,\"jx0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"cx0\",[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcW\",\"-\",[5,\"1\"]]]]]]],[13,\"jx1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cx0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcW\",\"-\",[5,\"1\"]]]]]]],[13,\"jy0\",[6,\"d.u32\",[[6,\"std.clamp\",[\"cy0\",[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcH\",\"-\",[5,\"1\"]]]]]]],[13,\"jy1\",[6,\"d.u32\",[[6,\"std.clamp\",[[1,\"cy0\",\"+\",[5,\"1\"]],[6,\"d.f32\",[[5,\"0\"]]],[1,\"srcH\",\"-\",[5,\"1\"]]]]]]],[13,\"c00\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"jx0\",\"jy0\"]],[5,\"0\"]]]],[13,\"c10\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"jx1\",\"jy0\"]],[5,\"0\"]]]],[13,\"c01\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"jx0\",\"jy1\"]],[5,\"0\"]]]],[13,\"c11\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"jx1\",\"jy1\"]],[5,\"0\"]]]],[13,\"fresh\",[6,\"std.mix\",[[6,\"std.mix\",[\"c00\",\"c10\",\"cfx\"]],[6,\"std.mix\",[\"c01\",\"c11\",\"cfx\"]],\"cfy\"]]],[13,\"heldAnchored\",[6,\"std.mix\",[\"held\",\"fresh\",\"rekey\"]]],[13,\"outC\",[6,\"std.mix\",[\"fresh\",\"heldAnchored\",[1,\"hold\",\"*\",\"inBounds\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"next\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"outC\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"display\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"outC\"]]]]},externals:{layout:()=>u,\"d.f32\":()=>n,res:()=>t,\"std.max\":()=>B,coarseScale:()=>o,\"std.floor\":()=>ie,\"noise.hash11\":()=>Xs,\"std.select\":()=>N,coarsePick:()=>a,\"std.sin\":()=>ae,\"std.smoothstep\":()=>be,\"std.cos\":()=>pe,\"std.clamp\":()=>G,\"d.u32\":()=>M,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,quantLevels:()=>i,\"d.vec3f\":()=>A,quantAmount:()=>l,energyDecay:()=>c,\"d.vec4f\":()=>S,\"std.textureDimensions\":()=>Ti,\"std.textureStore\":()=>lt}})&&p.f)({})).$name(`${r}Step`)}}var Pie=[16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99],Cie=[17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99],Yz=[];for(let e=0;e<8;e++)for(let t=0;t<8;t++)Yz[e*8+t]=Math.cos((2*e+1)*t*Math.PI/16);var X0=Math.SQRT1_2,H0=Math.PI/16,j0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.const(Fe(n,64),Yz),\"COS_LUT\"),IP=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.const(Fe(n,64),Pie),\"LUMA_QUANT_C\"),YP=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.const(Fe(n,64),Cie),\"CHROMA_QUANT_C\"),WP=new Map;function Mie(e){return w.fn([Fe(A,64),n,n,n],A)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o,a)=>{\"use gpu\";let i=G(a,1,100),l=N(__tsover_sub(200,__tsover_mul(i,2)),__tsover_div(5e3,i),i<50),c=Fe(n,64)(),d=Fe(n,64)(),u=Fe(n,64)();for(let E=0;E<64;E++){let R=s[E];c[E]=__tsover_sub(__tsover_add(__tsover_add(__tsover_mul(R.x,.299),__tsover_mul(R.y,.587)),__tsover_mul(R.z,.114)),.5),d[E]=__tsover_add(__tsover_add(__tsover_mul(R.x,-.168736),__tsover_mul(R.y,-.331264)),__tsover_mul(R.z,.5)),u[E]=__tsover_add(__tsover_add(__tsover_mul(R.x,.5),__tsover_mul(R.y,-.418688)),__tsover_mul(R.z,-.081312))}let p=Fe(n,__tsover_mul(8,e))();for(let E=0;E<8;E++)for(let R=0;R<e;R++){let U=n(0);for(let L=0;L<8;L++)U=__tsover_add(U,__tsover_mul(c[__tsover_add(__tsover_mul(E,8),L)],j0.$[__tsover_add(__tsover_mul(L,8),R)]));p[__tsover_add(__tsover_mul(E,e),R)]=U}let _=n(0);for(let E=0;E<e;E++){let R=N(1,X0,E===0),U=pe(__tsover_mul(__tsover_add(__tsover_mul(o,2),1),__tsover_mul(n(E),H0)));for(let L=0;L<e;L++){let k=N(1,X0,L===0),D=n(0);for(let V=0;V<8;V++)D=__tsover_add(D,__tsover_mul(p[__tsover_add(__tsover_mul(V,e),L)],j0.$[__tsover_add(__tsover_mul(V,8),E)]));let O=__tsover_mul(D,__tsover_mul(__tsover_mul(.25,k),R)),F=__tsover_div(G(ie(__tsover_div(__tsover_add(__tsover_mul(l,IP.$[__tsover_add(__tsover_mul(E,8),L)]),50),100)),1,255),255),q=__tsover_mul(ie(__tsover_add(__tsover_div(O,F),.5)),F),Y=pe(__tsover_mul(__tsover_add(__tsover_mul(r,2),1),__tsover_mul(n(L),H0)));_=__tsover_add(_,__tsover_mul(__tsover_mul(__tsover_mul(q,__tsover_mul(k,R)),Y),U))}}let f=__tsover_add(__tsover_mul(_,.25),.5),h=n(0),g=n(0);for(let E=0;E<64;E++)h=__tsover_add(h,d[E]),g=__tsover_add(g,u[E]);let y=__tsover_div(G(ie(__tsover_div(__tsover_add(__tsover_mul(l,YP.$[0]),50),100)),1,255),255),b=__tsover_div(h,8),x=__tsover_div(g,8),P=__tsover_div(__tsover_mul(ie(__tsover_add(__tsover_div(b,y),.5)),y),8),C=__tsover_div(__tsover_mul(ie(__tsover_add(__tsover_div(x,y),.5)),y),8);return G(A(__tsover_add(f,__tsover_mul(C,1.402)),__tsover_sub(__tsover_sub(f,__tsover_mul(P,.344136)),__tsover_mul(C,.714136)),__tsover_add(f,__tsover_mul(P,1.772))),A(0,0,0),A(1,1,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"block\"},{type:\"i\",name:\"localX\"},{type:\"i\",name:\"localY\"},{type:\"i\",name:\"quality\"}],body:[0,[[13,\"qualityC\",[6,\"std.clamp\",[\"quality\",[5,\"1\"],[5,\"100\"]]]],[13,\"qScale\",[6,\"std.select\",[[1,[5,\"200\"],\"-\",[1,\"qualityC\",\"*\",[5,\"2\"]]],[1,[5,\"5000\"],\"/\",\"qualityC\"],[1,\"qualityC\",\"<\",[5,\"50\"]]]]],[13,\"Yv\",[6,[6,\"d.arrayOf\",[\"d.f32\",[5,\"64\"]]],[]]],[13,\"Cbv\",[6,[6,\"d.arrayOf\",[\"d.f32\",[5,\"64\"]]],[]]],[13,\"Crv\",[6,[6,\"d.arrayOf\",[\"d.f32\",[5,\"64\"]]],[]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"64\"]],[102,\"++\",\"i\"],[0,[[13,\"c\",[8,\"block\",\"i\"]],[2,[8,\"Yv\",\"i\"],\"=\",[1,[1,[1,[1,[7,\"c\",\"x\"],\"*\",[5,\"0.299\"]],\"+\",[1,[7,\"c\",\"y\"],\"*\",[5,\"0.587\"]]],\"+\",[1,[7,\"c\",\"z\"],\"*\",[5,\"0.114\"]]],\"-\",[5,\"0.5\"]]],[2,[8,\"Cbv\",\"i\"],\"=\",[1,[1,[1,[7,\"c\",\"x\"],\"*\",[4,\"-\",[5,\"0.168736\"]]],\"+\",[1,[7,\"c\",\"y\"],\"*\",[4,\"-\",[5,\"0.331264\"]]]],\"+\",[1,[7,\"c\",\"z\"],\"*\",[5,\"0.5\"]]]],[2,[8,\"Crv\",\"i\"],\"=\",[1,[1,[1,[7,\"c\",\"x\"],\"*\",[5,\"0.5\"]],\"+\",[1,[7,\"c\",\"y\"],\"*\",[4,\"-\",[5,\"0.418688\"]]]],\"+\",[1,[7,\"c\",\"z\"],\"*\",[4,\"-\",[5,\"0.081312\"]]]]]]]],[13,\"rowCoef\",[6,[6,\"d.arrayOf\",[\"d.f32\",[1,[5,\"8\"],\"*\",\"nLuma\"]]],[]]],[14,[12,\"y\",[5,\"0\"]],[1,\"y\",\"<\",[5,\"8\"]],[102,\"++\",\"y\"],[0,[[14,[12,\"u\",[5,\"0\"]],[1,\"u\",\"<\",\"nLuma\"],[102,\"++\",\"u\"],[0,[[12,\"sum\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"x\",[5,\"0\"]],[1,\"x\",\"<\",[5,\"8\"]],[102,\"++\",\"x\"],[2,\"sum\",\"=\",[1,\"sum\",\"+\",[1,[8,\"Yv\",[1,[1,\"y\",\"*\",[5,\"8\"]],\"+\",\"x\"]],\"*\",[8,[7,\"COS_LUT\",\"$\"],[1,[1,\"x\",\"*\",[5,\"8\"]],\"+\",\"u\"]]]]]],[2,[8,\"rowCoef\",[1,[1,\"y\",\"*\",\"nLuma\"],\"+\",\"u\"]],\"=\",\"sum\"]]]]]]],[12,\"acc\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"v\",[5,\"0\"]],[1,\"v\",\"<\",\"nLuma\"],[102,\"++\",\"v\"],[0,[[13,\"av\",[6,\"std.select\",[[5,\"1\"],\"SQRT1_2\",[1,\"v\",\"===\",[5,\"0\"]]]]],[13,\"cy\",[6,\"std.cos\",[[1,[1,[1,\"localY\",\"*\",[5,\"2\"]],\"+\",[5,\"1\"]],\"*\",[1,[6,\"d.f32\",[\"v\"]],\"*\",\"PI_OVER_16\"]]]]],[14,[12,\"u\",[5,\"0\"]],[1,\"u\",\"<\",\"nLuma\"],[102,\"++\",\"u\"],[0,[[13,\"au\",[6,\"std.select\",[[5,\"1\"],\"SQRT1_2\",[1,\"u\",\"===\",[5,\"0\"]]]]],[12,\"colSum\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"y\",[5,\"0\"]],[1,\"y\",\"<\",[5,\"8\"]],[102,\"++\",\"y\"],[2,\"colSum\",\"=\",[1,\"colSum\",\"+\",[1,[8,\"rowCoef\",[1,[1,\"y\",\"*\",\"nLuma\"],\"+\",\"u\"]],\"*\",[8,[7,\"COS_LUT\",\"$\"],[1,[1,\"y\",\"*\",[5,\"8\"]],\"+\",\"v\"]]]]]],[13,\"G\",[1,\"colSum\",\"*\",[1,[1,[5,\"0.25\"],\"*\",\"au\"],\"*\",\"av\"]]],[13,\"q\",[1,[6,\"std.clamp\",[[6,\"std.floor\",[[1,[1,[1,\"qScale\",\"*\",[8,[7,\"LUMA_QUANT_C\",\"$\"],[1,[1,\"v\",\"*\",[5,\"8\"]],\"+\",\"u\"]]],\"+\",[5,\"50\"]],\"/\",[5,\"100\"]]]],[5,\"1\"],[5,\"255\"]]],\"/\",[5,\"255\"]]],[13,\"Gq\",[1,[6,\"std.floor\",[[1,[1,\"G\",\"/\",\"q\"],\"+\",[5,\"0.5\"]]]],\"*\",\"q\"]],[13,\"cxv\",[6,\"std.cos\",[[1,[1,[1,\"localX\",\"*\",[5,\"2\"]],\"+\",[5,\"1\"]],\"*\",[1,[6,\"d.f32\",[\"u\"]],\"*\",\"PI_OVER_16\"]]]]],[2,\"acc\",\"=\",[1,\"acc\",\"+\",[1,[1,[1,\"Gq\",\"*\",[1,\"au\",\"*\",\"av\"]],\"*\",\"cxv\"],\"*\",\"cy\"]]]]]]]]],[13,\"Y\",[1,[1,\"acc\",\"*\",[5,\"0.25\"]],\"+\",[5,\"0.5\"]]],[12,\"sumCb\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"sumCr\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",[5,\"64\"]],[102,\"++\",\"i\"],[0,[[2,\"sumCb\",\"=\",[1,\"sumCb\",\"+\",[8,\"Cbv\",\"i\"]]],[2,\"sumCr\",\"=\",[1,\"sumCr\",\"+\",[8,\"Crv\",\"i\"]]]]]],[13,\"qChroma\",[1,[6,\"std.clamp\",[[6,\"std.floor\",[[1,[1,[1,\"qScale\",\"*\",[8,[7,\"CHROMA_QUANT_C\",\"$\"],[5,\"0\"]]],\"+\",[5,\"50\"]],\"/\",[5,\"100\"]]]],[5,\"1\"],[5,\"255\"]]],\"/\",[5,\"255\"]]],[13,\"GdcCb\",[1,\"sumCb\",\"/\",[5,\"8\"]]],[13,\"GdcCr\",[1,\"sumCr\",\"/\",[5,\"8\"]]],[13,\"Cb\",[1,[1,[6,\"std.floor\",[[1,[1,\"GdcCb\",\"/\",\"qChroma\"],\"+\",[5,\"0.5\"]]]],\"*\",\"qChroma\"],\"/\",[5,\"8\"]]],[13,\"Cr\",[1,[1,[6,\"std.floor\",[[1,[1,\"GdcCr\",\"/\",\"qChroma\"],\"+\",[5,\"0.5\"]]]],\"*\",\"qChroma\"],\"/\",[5,\"8\"]]],[13,\"r\",[1,\"Y\",\"+\",[1,\"Cr\",\"*\",[5,\"1.402\"]]]],[13,\"g\",[1,[1,\"Y\",\"-\",[1,\"Cb\",\"*\",[5,\"0.344136\"]]],\"-\",[1,\"Cr\",\"*\",[5,\"0.714136\"]]]],[13,\"b\",[1,\"Y\",\"+\",[1,\"Cb\",\"*\",[5,\"1.772\"]]]],[10,[6,\"std.clamp\",[[6,\"d.vec3f\",[\"r\",\"g\",\"b\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.select\":()=>N,\"d.arrayOf\":()=>Fe,\"d.f32\":()=>n,nLuma:()=>e,COS_LUT:()=>j0,SQRT1_2:()=>X0,\"std.cos\":()=>pe,PI_OVER_16:()=>H0,\"std.floor\":()=>ie,LUMA_QUANT_C:()=>IP,CHROMA_QUANT_C:()=>YP,\"d.vec3f\":()=>A}})&&t.f)({})).$name(\"jpegBlockRgb\")}function JA(e){let t=WP.get(e);if(t)return t;let s=Mie(e);return WP.set(e,s),s}function Wz(e){let{nLuma:t,scaleN:s,namePrefix:r,format:o}=e,a=8*s,i=gt({inputWidth:n,inputHeight:n,quality:n}).$name(\"BlockQuantizeParams\"),l=(globalThis.__TYPEGPU_AUTONAME__??(d=>d))(w.bindGroupLayout({input:{texture:Zt(n)},cellTex:{storageTexture:zt(o,\"write-only\")},params:{uniform:i}}),\"layout\"),c=JA(t);return{layout:l,kernel:w.fn([M,M])((d=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(d.f=((u,p)=>{\"use gpu\";let _=l.$.params,f=n(u),h=n(p),g=ie(__tsover_div(f,8)),y=ie(__tsover_div(h,8)),b=__tsover_sub(f,__tsover_mul(g,8)),x=__tsover_sub(h,__tsover_mul(y,8)),P=__tsover_mul(g,a),C=__tsover_mul(y,a),E=Fe(A,64)(),R=j(__tsover_sub(_.inputWidth,1)),U=j(__tsover_sub(_.inputHeight,1));for(let k=0;k<8;k++)for(let D=0;D<8;D++){let O=__tsover_add(__tsover_add(P,__tsover_mul(n(D),s)),__tsover_mul(s,.5)),F=__tsover_add(__tsover_add(C,__tsover_mul(n(k),s)),__tsover_mul(s,.5)),q=G(j(O),0,R),Y=G(j(F),0,U),V=Ye(l.$.input,fe(M(q),M(Y)),0);E[__tsover_add(__tsover_mul(k,8),D)]=Yt(V).xyz}let L=c(E,b,x,_.quality);lt(l.$.cellTex,fe(u,p),S(L,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"cellXf\",[6,\"d.f32\",[\"cx\"]]],[13,\"cellYf\",[6,\"d.f32\",[\"cy\"]]],[13,\"blockCol\",[6,\"std.floor\",[[1,\"cellXf\",\"/\",[5,\"8\"]]]]],[13,\"blockRow\",[6,\"std.floor\",[[1,\"cellYf\",\"/\",[5,\"8\"]]]]],[13,\"localX\",[1,\"cellXf\",\"-\",[1,\"blockCol\",\"*\",[5,\"8\"]]]],[13,\"localY\",[1,\"cellYf\",\"-\",[1,\"blockRow\",\"*\",[5,\"8\"]]]],[13,\"originX\",[1,\"blockCol\",\"*\",\"blockPx\"]],[13,\"originY\",[1,\"blockRow\",\"*\",\"blockPx\"]],[13,\"block\",[6,[6,\"d.arrayOf\",[\"d.vec3f\",[5,\"64\"]]],[]]],[13,\"wm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputWidth\"],\"-\",[5,\"1\"]]]]],[13,\"hm1\",[6,\"d.i32\",[[1,[7,\"p\",\"inputHeight\"],\"-\",[5,\"1\"]]]]],[14,[12,\"y\",[5,\"0\"]],[1,\"y\",\"<\",[5,\"8\"]],[102,\"++\",\"y\"],[0,[[14,[12,\"x\",[5,\"0\"]],[1,\"x\",\"<\",[5,\"8\"]],[102,\"++\",\"x\"],[0,[[13,\"px\",[1,[1,\"originX\",\"+\",[1,[6,\"d.f32\",[\"x\"]],\"*\",\"scaleN\"]],\"+\",[1,\"scaleN\",\"*\",[5,\"0.5\"]]]],[13,\"py\",[1,[1,\"originY\",\"+\",[1,[6,\"d.f32\",[\"y\"]],\"*\",\"scaleN\"]],\"+\",[1,\"scaleN\",\"*\",[5,\"0.5\"]]]],[13,\"ix\",[6,\"std.clamp\",[[6,\"d.i32\",[\"px\"]],[5,\"0\"],\"wm1\"]]],[13,\"iy\",[6,\"std.clamp\",[[6,\"d.i32\",[\"py\"]],[5,\"0\"],\"hm1\"]]],[13,\"texel\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"input\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"ix\"]],[6,\"d.u32\",[\"iy\"]]]],[5,\"0\"]]]],[2,[8,\"block\",[1,[1,\"y\",\"*\",[5,\"8\"]],\"+\",\"x\"]],\"=\",[7,[6,\"blend.unpremultiplyAlpha\",[\"texel\"]],\"xyz\"]]]]]]]],[13,\"rgb\",[6,\"blockRgb\",[\"block\",\"localX\",\"localY\",[7,\"p\",\"quality\"]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"cellTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"rgb\",[5,\"1\"]]]]]]]},externals:{layout:()=>l,\"d.f32\":()=>n,\"std.floor\":()=>ie,blockPx:()=>a,\"d.arrayOf\":()=>Fe,\"d.vec3f\":()=>A,\"d.i32\":()=>j,scaleN:()=>s,\"std.clamp\":()=>G,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"blend.unpremultiplyAlpha\":()=>Yt,blockRgb:()=>c,\"std.textureStore\":()=>lt,\"d.vec4f\":()=>S}})&&d.f)({})).$name(`${r}Cell`),Params:i}}var qz=w.fn([S,S,S,S,S,S,S,S,S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u)=>{\"use gpu\";let p=be(0,.1,__tsover_mul(__tsover_add(__tsover_add(__tsover_add(__tsover_add(t.z,s.z),r.z),o.z),a.z),.2)),_=t.xy.add(s.xy).add(r.xy).add(o.xy).add(a.xy).mul(.2),f=he(_),h=be(.01,.1,f),g=__tsover_div(_.x,__tsover_add(f,.001)),y=__tsover_div(_.y,__tsover_add(f,.001)),b=be(0,.7,B(g,0)),x=be(0,.7,B(__tsover_mul(g,-1),0)),P=be(0,.7,B(y,0)),C=be(0,.7,B(__tsover_mul(y,-1),0)),E=d.mul(x).add(u.mul(b)),R=c.mul(C).add(l.mul(P)),U=__tsover_add(x,b),L=__tsover_add(P,C),k=__tsover_add(__tsover_add(U,L),.001);return K(i,E.mul(__tsover_div(U,k)).add(R.mul(__tsover_div(L,k))),S(h)).mul(p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s0\"},{type:\"i\",name:\"s1\"},{type:\"i\",name:\"s2\"},{type:\"i\",name:\"s3\"},{type:\"i\",name:\"s4\"},{type:\"i\",name:\"base\"},{type:\"i\",name:\"up\"},{type:\"i\",name:\"down\"},{type:\"i\",name:\"left\"},{type:\"i\",name:\"right\"}],body:[0,[[13,\"smoothedLiquid\",[1,[1,[1,[1,[1,[7,\"s0\",\"z\"],\"+\",[7,\"s1\",\"z\"]],\"+\",[7,\"s2\",\"z\"]],\"+\",[7,\"s3\",\"z\"]],\"+\",[7,\"s4\",\"z\"]],\"*\",[5,\"0.2\"]]],[13,\"liquidIntensity\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.1\"],\"smoothedLiquid\"]]],[13,\"flow\",[6,[7,[6,[7,[6,[7,[6,[7,[6,[7,[7,\"s0\",\"xy\"],\"add\"],[[7,\"s1\",\"xy\"]]],\"add\"],[[7,\"s2\",\"xy\"]]],\"add\"],[[7,\"s3\",\"xy\"]]],\"add\"],[[7,\"s4\",\"xy\"]]],\"mul\"],[[5,\"0.2\"]]]],[13,\"flowMagnitude\",[6,\"std.length\",[\"flow\"]]],[13,\"hasFlow\",[6,\"std.smoothstep\",[[5,\"0.01\"],[5,\"0.1\"],\"flowMagnitude\"]]],[13,\"normalizedX\",[1,[7,\"flow\",\"x\"],\"/\",[1,\"flowMagnitude\",\"+\",[5,\"0.001\"]]]],[13,\"normalizedY\",[1,[7,\"flow\",\"y\"],\"/\",[1,\"flowMagnitude\",\"+\",[5,\"0.001\"]]]],[13,\"rightAmount\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.7\"],[6,\"std.max\",[\"normalizedX\",[5,\"0\"]]]]]],[13,\"leftAmount\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.7\"],[6,\"std.max\",[[1,\"normalizedX\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"]]]]]],[13,\"upAmount\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.7\"],[6,\"std.max\",[\"normalizedY\",[5,\"0\"]]]]]],[13,\"downAmount\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.7\"],[6,\"std.max\",[[1,\"normalizedY\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"]]]]]],[13,\"horizontalColor\",[6,[7,[6,[7,\"left\",\"mul\"],[\"leftAmount\"]],\"add\"],[[6,[7,\"right\",\"mul\"],[\"rightAmount\"]]]]],[13,\"verticalColor\",[6,[7,[6,[7,\"down\",\"mul\"],[\"downAmount\"]],\"add\"],[[6,[7,\"up\",\"mul\"],[\"upAmount\"]]]]],[13,\"horizontalWeight\",[1,\"leftAmount\",\"+\",\"rightAmount\"]],[13,\"verticalWeight\",[1,\"upAmount\",\"+\",\"downAmount\"]],[13,\"totalWeight\",[1,[1,\"horizontalWeight\",\"+\",\"verticalWeight\"],\"+\",[5,\"0.001\"]]],[13,\"directionalColor\",[6,[7,[6,[7,\"horizontalColor\",\"mul\"],[[1,\"horizontalWeight\",\"/\",\"totalWeight\"]]],\"add\"],[[6,[7,\"verticalColor\",\"mul\"],[[1,\"verticalWeight\",\"/\",\"totalWeight\"]]]]]],[13,\"finalColor\",[6,\"std.mix\",[\"base\",\"directionalColor\",[6,\"d.vec4f\",[\"hasFlow\"]]]]],[10,[6,[7,\"finalColor\",\"mul\"],[\"liquidIntensity\"]]]]]},externals:{\"std.smoothstep\":()=>be,\"std.length\":()=>he,\"std.max\":()=>B,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"flowDirectionColor\"),og={name:\"Root\",props:{},fragment:({childNode:e})=>e??ca(\"vec4f(0.0, 0.0, 0.0, 0.0)\")};function wt(e,t,s=\"_animTime\"){let r=e.props.member(s);if(t){let o=e.uniforms[t];if(o)return r.add(o)}return r}var Eie=class{kind=\"wgsl\";spec;identifiers;fns=new Map;constructor(e){if(typeof e.body!=\"string\"||e.body.trim()===\"\")throw new Error(\"wgsl: the body must be a non-empty WGSL string\");if(!/\\breturn\\b/.test(Vz(e.body)))throw new Error(\"wgsl: the body must `return` a vec4f color\");this.spec=e,this.identifiers=kie(e.body)}get samplesChild(){return this.identifiers.has(\"childTexture\")}fnFor(e,t){let s=this.fns.get(e);return s||(s=t(),this.fns.set(e,s)),s}};function L_(e){return e instanceof Eie}function Vz(e){return e.replace(/\\/\\*[\\s\\S]*?\\*\\//g,\" \").replace(/\\/\\/[^\\n]*/g,\" \")}function kie(e){let t=new Set,s=new RegExp(\"(?<![\\\\w.])[A-Za-z_][A-Za-z0-9_]*\",\"g\");for(let r of Vz(e).matchAll(s))t.add(r[0]);return t}function Nz(e){let t=e.transform;if(t===ce)return\"vec4f\";if(t===Xe)return\"vec2f\";if(t===hl||t===_c)return null;let s=e.default;if(z_(s))return\"f32\";let r=bz(t);if(!r&&(typeof s==\"string\"||typeof s==\"object\"&&s!==null&&!Array.isArray(s)))return null;let o=s;if(r)try{o=r(s)}catch{o=s}switch(o&&typeof o==\"object\"&&\"data\"in o&&(o=o.data),Mk(o).type){case\"f32\":return\"f32\";case\"vec2f\":return\"vec2f\";case\"vec3f\":return\"vec3f\";case\"vec4f\":return\"vec4f\";default:return null}}var Bie={f32:n,vec2f:m,vec3f:A,vec4f:S,\"texture_2d<f32>\":Zt(n),sampler:v3()},zie=new Set([\"viewport\",\"child\",\"childTexture\",\"childSampler\"]),$z={uv:\"vec2f\",aspect:\"f32\",time:\"f32\",viewportSize:\"vec2f\",logicalViewportSize:\"vec2f\",pointer:\"vec2f\"};function qP(e){return/^[A-Za-z_][A-Za-z0-9_]*$/.test(e)?e:e.replace(/[^A-Za-z0-9_]/g,\"_\")}function e2(e,t,s){let r=qP(e.spec.name??Rie(s.name)+Die(t)),o=Lie(t,s),a=Uie(e.spec.inputs??{},s.props),i=new Map;for(let u of o)e.identifiers.has(u.name)&&i.set(u.name,u);for(let u of a)i.set(u.name,u);let l=[...i.values()],c=`(${l.map(u=>`${qP(u.name)}: ${u.type}`).join(\", \")}) -> vec4f`,d=l.map(u=>`${u.name}:${u.type}`).join(\",\");return u=>(U5(),u.noteCustomWgsl?.(),W(e.fnFor(d,()=>(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(w.fn(l.map(p=>Bie[p.type]),S),\"shell\")(`${c} {\n${e.spec.body}\n}`)),r,l.map(p=>p.value(u))))}function Rie(e){return e?e[0].toLowerCase()+e.slice(1):\"custom\"}function Die(e){return e===\"generator\"?\"Paint\":\"Filter\"}function Lie(e,t){let s=[],r=e===\"generator\";s.push({name:\"uv\",type:\"vec2f\",value:o=>r?o.uvContext??o.ctx.uv:o.ctx.uv}),s.push({name:\"time\",type:\"f32\",value:o=>t.animatedTime?wt(o):o.ctx.time}),s.push({name:\"aspect\",type:\"f32\",value:o=>o.ctx.aspect}),s.push({name:\"viewport\",type:\"vec2f\",value:o=>r?o.effectiveViewportSize??o.ctx.viewportSize:o.ctx.viewportSize}),s.push({name:\"pointer\",type:\"vec2f\",value:o=>o.ctx.pointer}),e===\"pointwise\"&&s.push({name:\"child\",type:\"vec4f\",value:o=>o.childNode}),e===\"gather\"&&(s.push({name:\"childTexture\",type:\"texture_2d<f32>\",value:o=>o.texture.accessor()}),s.push({name:\"childSampler\",type:\"sampler\",value:()=>ca(\"samp.$.linearClamp\")}));for(let[o,a]of Object.entries(t.props)){if($z[o]||zie.has(o))continue;let i=Nz(a);if(!i)continue;let l=a.transform===Xe;s.push({name:o,type:i,value:c=>{let d=c.uniforms[o];if(!d)throw new Error(`wgsl: prop '${o}' has no GPU uniform (is it CPU-only?)`);return l?new fs(u=>`vec2f(${d.member(\"x\")._emit(u)}, 1.0 - ${d.member(\"y\")._emit(u)})`):d}})}return s}function Uie(e,t){let s=[];for(let[r,o]of Object.entries(e)){let a=o!==null&&typeof o==\"object\"&&\"value\"in o&&\"type\"in o,i=a?o.value:o,l=Oie(i,t),c=a?o.type:l;if(a&&c!==l)throw new Error(`wgsl: input '${r}' is declared ${c} but its source is ${l}`);s.push({name:r,type:c,value:d=>typeof i==\"number\"?$e(i):Az(i,d)})}return s}function Oie(e,t){if(typeof e==\"number\"||e instanceof zi)return\"f32\";if(e.kind===\"ctx\")return $z[e.name]??\"f32\";if(e.kind===\"prop\"){let s=t[e.name];if(!s)throw new Error(`wgsl: input binds unknown prop '${e.name}'`);let r=Nz(s);if(!r)throw new Error(`wgsl: prop '${e.name}' is CPU-only and cannot be bound as a WGSL parameter`);return r}return\"f32\"}function fc(e){let t={};for(let s of Object.keys(e)){let{recompile:r,...o}=e[s],a={...o};if(r?.kind===\"crosses\"){let i=r.value;a.compileTimeWhen=(l,c)=>l===i!=(c===i)}else r?.kind===\"custom\"&&(a.compileTimeWhen=r.predicate);t[s]=a}return t}function Xz(e,t){switch(e.kind){case\"isZero\":case\"isValue\":{let s=e.kind===\"isZero\"?0:e.value,r=t[e.prop];return{props:[e.prop],when:o=>(o[e.prop]??r)===s}}case\"allOf\":{let s=e.rules.map(r=>Xz(r,t));return{props:[...new Set(s.flatMap(r=>r.props))],when:r=>s.every(o=>o.when(r))}}case\"custom\":return{props:e.props,when:e.when}}}function lh(e,t){if(!e)return;let s={};for(let[r,o]of Object.entries(t))s[r]=o.default;return Xz(e,s)}function Gie(e){let{role:t,species:s,effect:r,identityWhen:o,missingChildMessage:a,props:i,...l}=e,c={...l,props:fc(i),identity:lh(o,i),missingChildMessage:a};if(r.kind===\"tintToward\"){let{color:p,amount:_}=r;return V0({...c,body:{fn:Kk,hint:\"mixToward\"},args:f=>[me(p,f),fn(_,f)]})}if(L_(r))return V0({...c,build:e2(r,\"pointwise\",t2(e))});let{kind:d,...u}=r;return V0({...c,...u})}function t2(e){return{name:e.name,props:e.props,animatedTime:e.animatedTime}}function Fie(e){let{role:t,species:s,effect:r,identityWhen:o,missingChildMessage:a,props:i,...l}=e;if(r.kind===\"displaceBy\")return Iie(e);if(L_(r))return EP({...l,props:fc(i),identity:lh(o,i),missingChildMessage:a,build:e2(r,\"gather\",t2(e)),resultAlpha:r.spec.alpha??\"premultiplied\"});let{kind:c,...d}=r;return EP({...l,props:fc(i),identity:lh(o,i),missingChildMessage:a,...d})}function Iie(e){let{role:t,species:s,effect:r,identityWhen:o,missingChildMessage:a,props:i,...l}=e,c=r,d=lh(o,i),u=c.field.sim.config,p=c.field.output,_=u.step.find(h=>h.kind===\"op.wave\"),f=u.step.find(h=>h.kind===\"op.splat\");if(!_||!f||u.step.length!==2)throw new Error(`std: simulate.grid currently implements exactly [op.wave, op.splat] (got: ${u.step.map(h=>h.kind).join(\", \")})`);if(u.history!==2)throw new Error(`std: op.wave reads t\\u22121 and t\\u22122 \\u2014 declare history: 2 (got ${u.history})`);if(u.derive[p]?.kind!==\"op.gradient\")throw new Error(`std: displaceBy consumes a vector field \\u2014 derive '${p}' must be op.gradient()`);if(u.rest&&u.rest.settlesWhen!==\"derived-from-damping\")throw new Error(\"std: simulate.grid rest supports settlesWhen: 'derived-from-damping' only\");return{...l,props:fc(i),requiresRTT:!0,requiresChild:!0,usesPointer:f.at.kind===\"pointer\",compute:h=>{if(!h.childNode)return null;let g=Lae(h,{resolution:u.resolution,dampingProp:_.damping.name,radiusProp:f.radius.name,radiusScale:.05,speedMax:f.amount.max,teleportGuard:f.at.teleportGuard});return g?{outputs:{[p]:g.displacement},getComputeNodes:g.getComputeNodes}:null},fragment:h=>{let{childNode:g,ctx:y,computeOutputs:b,propValues:x,convertToTexture:P}=h;if(!g)return xt;let C=P(g);if(XA(d,h))return W(Yt,\"unpremultiplyAlpha\",[C.sample(y.uv)]);let E=x[c.edges.name]??0,R=b?.[p],U=R?R.sample(y.uv,\"linearClamp\").member(\"xy\"):ca(\"vec2f(0.0, 0.0)\"),L=W(fee,\"chromaticDisplaceUVs\",[y.uv,U,me(c.strength,h),me(c.chromatic,h)]),k=q=>C.sample(q),D=Ha(L.member(\"rUV\"),k,E),O=Ha(L.member(\"gUV\"),k,E),F=Ha(L.member(\"bUV\"),k,E);return W(Yt,\"unpremultiplyAlpha\",[Ct(D.member(\"r\"),O.member(\"g\"),F.member(\"b\"),O.member(\"a\"))])}}}function Yie(e){let{role:t,map:s,edges:r,resample:o,uvRemapIdentityWhen:a,missingChildMessage:i,props:l,...c}=e;return{...c,props:fc(l),requiresRTT:!0,requiresChild:!0,...Qne(s,{edges:r,resample:o,uvRemapIdentityWhen:a,requireChildMessage:i})}}function Wie(e){let{role:t,name:s,description:r,category:o,shape:a}=e;return nie({name:s,description:r,category:o,...a})}function qie(e){let{role:t,paint:s,props:r,...o}=e;return L_(s)?{acceptsUVContext:!0,...o,props:fc(r),fragment:e2(s,\"generator\",t2(e))}:{...o,props:fc(r),fragment:s}}function Vie(e){let{role:t,species:s,gpu:r,props:o,...a}=e;return{...a,props:fc(o),fragment:r.fragment,...r.uvRemap?{uvRemap:r.uvRemap}:{}}}function Nie(e){let t=[],s=e.paint,r=e.effect;for(let a of[s,r])L_(a)&&t.push(a.spec.body,$ie(a.spec.inputs??{}),a.spec.alpha??\"\");if(t.length===0)return;let o=2166136261;for(let a of t.join(\"\\0\"))o^=a.charCodeAt(0),o=Math.imul(o,16777619)>>>0;return o.toString(16)}function $ie(e){let t=r=>{if(typeof r!=\"object\"||r===null)return String(r);let o=r;switch(o.kind){case\"prop\":return`p:${o.name}`;case\"ctx\":return`c:${o.name}`;case\"mul\":return`mul(${t(o.a)},${t(o.b)})`;case\"radialMask\":return`radialMask(${o.center?.name},${o.radius?.name},${o.falloff?.name})`;default:return o.kind}},s=r=>typeof r==\"number\"?`n:${r}`:r instanceof zi?`s:${t(r.node)}`:t(r);return Object.keys(e).sort().map(r=>{let o=e[r];return o!==null&&typeof o==\"object\"&&\"value\"in o&&\"type\"in o?`${r}=${s(o.value)}:${o.type}`:`${r}=${s(o)}`}).join(\",\")}var Xie=new Set([\"blendMode\",\"opacity\",\"visible\",\"id\",\"maskSource\",\"maskType\",\"renderOrder\",\"transform\",\"boundingBox\",\"flow\",\"absolute\",\"children\",\"ref\",\"key\",\"src\"]),Hie=new Set([\"_animTime\",\"_opacity\"]),jie=[\"_pad\",\"_bbox_\",\"_map_\",\"_childBounds_\",\"_animTime_\"],Kie=/^[A-Za-z_][A-Za-z0-9_]*$/;function Qie(e,t){for(let s of Object.keys(t)){if(!Kie.test(s))throw new Error(`defineShader(\"${e}\"): prop '${s}' is not a valid identifier (letters, digits and _ only, not starting with a digit)`);if(Xie.has(s))throw new Error(`defineShader(\"${e}\"): '${s}' is a layer prop every component already has (blend mode, opacity, layout, \\u2026) \\u2014 rename the shader prop`);if(Hie.has(s)||jie.some(r=>s.startsWith(r)))throw new Error(`defineShader(\"${e}\"): '${s}' collides with a field the renderer manages itself \\u2014 rename the shader prop`)}}function H(e){\"props\"in e&&e.props&&Qie(e.name,e.props);let t=tle(e),s=Nie(e);return s?{...t,revision:s}:t}function Zie(e){let t=\"shape\"in e?\"shape\":\"map\"in e?\"warp\":\"gpu\"in e?\"custom\":\"paint\"in e?\"generator\":\"effect\"in e?\"filter\":null,s=String(e.name??\"?\");if(!t)throw new Error(`defineShader(\"${s}\"): give it a GPU half \\u2014 paint: (generator), effect: (filter), map: (warp), shape: (shape) or gpu: (custom)`);let r=e.role;if(r&&t!==\"custom\"&&r!==t)throw new Error(`defineShader(\"${s}\"): role '${r}' contradicts its ${Jie[t]} field (a ${t})`);return t}var Jie={shape:\"shape:\",warp:\"map:\",custom:\"gpu:\",generator:\"paint:\",filter:\"effect:\"};function ele(e){let{effect:t,species:s}=e;if(L_(t))return s??(t.samplesChild?\"gather\":\"pointwise\");let r=t.kind===\"gather\"||t.kind===\"displaceBy\"?\"gather\":\"pointwise\";if(s&&s!==r)throw new Error(`defineShader(\"${e.name}\"): species '${s}' contradicts its '${t.kind}' effect (${r})`);return r}function tle(e){switch(Zie(e)){case\"shape\":return Wie(e);case\"warp\":return Yie(e);case\"custom\":return Vie(e);case\"generator\":return qie(e);case\"filter\":{let t=e;return ele(t)===\"gather\"?Fie({...t,species:\"gather\"}):Gie({...t,species:\"pointwise\"})}}}var kx=e=>typeof e==\"number\"?$e(e):e,ud=(e,t)=>typeof e==\"number\"?$e(e)._emit(t):e._emit(t);function Wr(e,...t){return new fs(s=>`${e}(${t.map(r=>ud(r,s)).join(\", \")})`)}function Ac(e,t,s){return new fs(r=>`(${ud(t,r)} ${e} ${ud(s,r)})`)}var I=(e,t)=>Ac(\"+\",e,t),ne=(e,t)=>Ac(\"-\",e,t),v=(e,t)=>Ac(\"*\",e,t),Ce=(e,t)=>Ac(\"/\",e,t),er=e=>new fs(t=>`(-(${ud(e,t)}))`),ms=e=>Wr(\"abs\",e),Xr=e=>Wr(\"floor\",e);var Qr=e=>Wr(\"fract\",e),ho=e=>Wr(\"sqrt\",e),ts=(e,t)=>Wr(\"pow\",e,t),tr=e=>Wr(\"exp\",e);var Vt=e=>Wr(\"sin\",e),Do=e=>Wr(\"cos\",e);var sle=(e,t)=>Wr(\"atan2\",e,t),Yr=(e,t)=>Wr(\"min\",e,t),it=(e,t)=>Wr(\"max\",e,t),Ge=(e,t,s)=>Wr(\"clamp\",e,t,s),et=(e,t,s)=>ea(kx(e),kx(t),s),ag=(e,t)=>Wr(\"step\",e,t),ot=(e,t,s)=>Wr(\"smoothstep\",e,t,s),Bx=e=>Wr(\"sign\",e),Kr=e=>Wr(\"length\",e);var $t=(e,t)=>Wr(\"dot\",e,t),Os=e=>Wr(\"normalize\",e);var ng=(e,t,s)=>rt(ne(v(b1(e,\"x\"),t),v(b1(e,\"y\"),s)),I(v(b1(e,\"x\"),s),v(b1(e,\"y\"),t))),b1=(e,t)=>typeof e==\"number\"?e:e.member(t),Lo=(e,t)=>Wr(\"reflect\",e,t),ig=(e,t,s)=>Wr(\"refract\",e,t,s),U_=e=>Wr(\"exp2\",e),s2=(e,t)=>v(Qr(v(Vt(v(e,t.k)),t.big)),6.283185307179586),Xn=(e,t)=>Ac(\"<\",e,t),Hz=(e,t)=>Ac(\"<=\",e,t),Zo=(e,t)=>Ac(\">\",e,t),ch=(e,t)=>Ac(\">=\",e,t),br=(e,t,s)=>new fs(r=>`select(${ud(s,r)}, ${ud(t,r)}, ${ud(e,r)})`),It=e=>$e(e),rt=(e,t)=>Wr(\"vec2f\",e,t),Qe=(e,t,s)=>Wr(\"vec3f\",e,t,s),Ot=(...e)=>Ct(...e.map(kx)),kt=e=>Wr(\"vec3f\",e),z=(e,t)=>ns(e,t),jz=(e,t)=>{let s=z(e,\"sp\");return v(I(s,ho(I(v(s,s),v(t,t)))),.5)},O_=(e,t)=>tr(er(v(v(e,e),t)));function Kz(e,t){return{kind:\"tintToward\",color:e,amount:t.amount}}function Qz(e,t){return{kind:\"displaceBy\",field:e,strength:t.strength,chromatic:t.chromatic,edges:t.edges}}function Rd(e){return{kind:\"gather\",...e}}function Zz(e){return v(ot(e.below.from,e.below.to,e.distance),ne(1,ot(e.above.from??0,e.above.to,e.distance)))}function Jz(e){let{width:t,curve:s}=e.softness;return ts(ne(1,ot(ne(e.radius,t),I(e.radius,t),e.dist)),s)}function mp(e){let t=v(e.footprint,.5);return ne(1,ot(er(t),t,e.distance))}function eR(e){return mp({distance:ne(ms(e.distance),v(e.width,.5)),footprint:e.footprint})}function tR(e){return new zi({kind:\"radialMask\",center:e.center,radius:e.radius,falloff:e.falloff})}var lg={wave(e){return{kind:\"op.wave\",damping:e.damping}},splat(e){return{kind:\"op.splat\",at:e.at,amount:e.amount,radius:e.radius}},gradient(){return{kind:\"op.gradient\"}}},rle=class{constructor(e){this.config=e}output(e){if(!(e in this.config.derive))throw new Error(`std: simulation has no derived output '${e}' (declared: ${Object.keys(this.config.derive).join(\", \")})`);return{kind:\"simOutput\",sim:this,output:e}}},sR={grid(e){return new rle(e)}};function rR(e){return{kind:\"pointer\",teleportGuard:e?.teleportGuard??\"on\"}}function oR(e){return{kind:\"pointerSpeed\",max:e?.max??2}}function aR(e,t,s){e.onBeforeRender(()=>{let r=e.getCpuValue(t.x),o=e.getCpuValue(t.y),a=e.getCpuValue(t.z),i=Math.sqrt(r*r+o*o+a*a);i>0?(e.setExtraField(s.x,r/i),e.setExtraField(s.y,o/i),e.setExtraField(s.z,a/i)):(e.setExtraField(s.x,0),e.setExtraField(s.y,0),e.setExtraField(s.z,0))})}function wc(e){let t=Math.sin(e*12.9898)*43758.5453;return t-Math.floor(t)}function Lt(e){return{kind:\"crosses\",value:e}}function hn(e){return{kind:\"custom\",predicate:e}}function Uo(e){return{kind:\"isZero\",prop:e}}function cg(e,t){return{kind:\"isValue\",prop:e,value:t}}function nR(...e){return{kind:\"allOf\",rules:e}}var ole=e=>typeof e==\"number\"?$e(e):e,Ma=(e,t)=>(...s)=>W(e,t,s.map(ole)),_p=Ma(Dm,\"circleSdf\"),iR=Ma(Iv,\"ellipseSdf\"),dg=Ma(Fv,\"roundedRectSdf\"),lR=Ma(Dv,\"polygonSdf\"),cR=Ma(Uv,\"starSdf\"),dR=Ma(Lv,\"flowerSdf\"),uR=Ma(Vv,\"heartSdf\"),pR=Ma(Gv,\"crossSdf\"),mR=Ma(Ov,\"ringSdf\"),_R=Ma(Yv,\"vesicaSdf\"),fR=Ma(Wv,\"crescentSdf\"),vR=Ma(qv,\"trapezoidSdf\"),hR=Ma($v,\"teardropSdf\"),gR=Ma(Xv,\"parallelogramSdf\"),yR=Ma(Nv,\"arcSdf\"),bR=CB,ft=Math.fround,ale=ft(aJ),K0=ft(Jt),nle=.11,zx=ft(4294967295),VP=new DataView(new ArrayBuffer(4));function Rx(e){return VP.setFloat32(0,ft(e),!0),VP.getUint32(0,!0)}function dh(e,t){return Math.imul(e>>>0,t>>>0)>>>0}function NP(e){let t=Rx(ft(ft(e)*ft(3141592653)));return ft(ft(dh(dh(t,t),3141592653))/zx)}function $P(e,t){let s=(Rx(ft(ft(e)*ft(141421356)))^Rx(ft(ft(t)*ft(2718281828))))>>>0;return[ft(ft(dh(s,3141592653))/zx),ft(ft(dh(s,1618033988))/zx)]}function ile(e,t,s,r,o,a){let i=ft(ft(e*ale)+ft(NP(ft(ft(s*ft(.7311))+ft(1.618)))*K0)),l=ft(Math.sqrt(ft(ft(e+.5)/ft(t)))),c=$P(ft(ft(e*ft(3.77))+ft(.917)),ft(ft(s*ft(1.093))+ft(2.236))),d=ft(ft(c[0]-.5)*ft(.26)),u=ft(ft(c[1]-.5)*ft(.26)),p=$P(ft(ft(e*ft(7.13))+ft(1.618)),ft(ft(s*ft(.531))+ft(3.71))),_=ft(p[0]*K0),f=ft(p[1]*K0),h=ft(.5+ft(NP(ft(ft(ft(e*ft(5.417))+ft(s*ft(.291)))+ft(.917)))*ft(.7))),g=ft(ft(a*ft(nle))*h),y=ft(Math.sin(ft(g+_))),b=ft(Math.cos(ft(ft(g*ft(1.37))+f))),x=ft(ft(ft(ft(Math.cos(i))*l)*ft(.62))+d)+ft(ft(y*ft(r))*ft(.16)),P=ft(ft(ft(ft(Math.sin(i))*l)*ft(.62))+u)+ft(ft(b*ft(r))*ft(.16));return[ft(ft(.5+ft(x))*ft(o)),ft(.5+ft(P))]}function lle(e,t,s,r,o){let a=new Array(16);for(let i=0;i<8;i++){let[l,c]=ile(i,e,t,s,r,o);a[i*2]=l,a[i*2+1]=c}return a}function XP(e,t){return typeof e==\"number\"?$e(e):e instanceof zi||e.kind!==\"ctx\"?fn(e,t):t.ctx[e.name]}function oo(e,t){if(typeof e==\"object\"&&e!==null&&\"base\"in e){let s=XP(e.base,t);return e.add!==void 0&&(s=s.add(e.add)),e.mul!==void 0&&(s=s.mul(e.mul)),s}return XP(e,t)}function Kc(e){return{uv:e.uvContext??e.ctx.uv,viewport:e.effectiveViewportSize??e.ctx.viewportSize}}function r2(e,t){return t.propValues[e.name]}function G_(e){return typeof e==\"number\"?e:typeof e==\"string\"?at(e):0}function ug(e,t){return No[e?G_(t.propValues[e.name]):0]??Ar}function pg(e){return(t,s,r)=>{let o=ug(e.space,r);return W(o,\"mixColors\",[W(o,\"mixColors\",[me(e.a,r),me(e.b,r),t]),me(e.c,r),s])}}function xR(e,t,s){return(r,o)=>W(No[G_(r2(s,o))]??Ar,\"mixColors\",[me(e,o),me(t,o),r])}function Us(e){return(t,s)=>{let r=G_(r2(e,s));return(s.propValues.stopCount??0)>1?En(t,{colorsArray:s.uniforms.colorsArray,positionsArray:s.uniforms.positionsArray,convertedColorsArray:s.uniforms.convertedColorsArray,stopCount:s.uniforms.stopCount},r):W(No[r]??Ar,\"mixColors\",[s.uniforms.colorA,s.uniforms.colorB,t])}}var HP=1,cle=2,dle=3;function Vs(e,t,s){return r=>{let{uv:o}=Kc(r),a=e(r,o);if(!s?.edges)return t(a,r);let i=r2(s.edges,r)??0,l;i===cle?l=W(S4,\"gradientEdgeMirror\",[a]):i===dle?l=W(Ate,\"gradientEdgeWrap\",[a]):i===HP?l=a:l=W(pS,\"gradientClamp01\",[a]);let c=t(W(pS,\"gradientClamp01\",[l]),r);if(i===HP){let d=W(wte,\"gradientTransparentAlpha\",[a]);return Ct(c.member(\"rgb\"),c.member(\"a\").mul(d))}return c}}var Oi={linear(e){return(t,s)=>{let{viewport:r}=Kc(t);return W(xte,\"gradientRawT\",[me(e.from,t),me(e.to,t),oo(e.angle,t),s,r])}},radial(e){return(t,s)=>{let{viewport:r}=Kc(t);return W(Tte,\"radialDist\",[me(e.center,t),oo(e.skew,t),oo(e.aspect,t),oo(e.radius,t),s,r])}},conic(e){return(t,s)=>{let{viewport:r}=Kc(t);return W(Ste,\"conicSweep\",[me(e.center,t),oo(e.rotation,t),s,r])}},diamond(e){return(t,s)=>{let{viewport:r}=Kc(t);return W(Pte,\"diamondDist\",[me(e.center,t),oo(e.size,t),oo(e.rotation,t),oo(e.roundness,t),s,r])}},spiral(e){return(t,s)=>{let{viewport:r}=Kc(t);return W(Rte,\"spiralMask\",[me(e.center,t),oo(e.scale,t),oo(e.width,t),oo(e.falloff,t),oo(e.softness,t),wt(t),s,r])}}};function mg(e,t){return(s,r)=>W(Cte,\"repeatRings\",[e(s,r),oo(t,s)])}function AR(e,t){return(s,r)=>W(Mte,\"repeatWrap\",[e(s,r),oo(t,s)])}function wR(e,t){return(s,r)=>W(Ute,\"toneRemap\",[e(s,r),oo(t.glow,s),oo(t.contrast,s),oo(t.balance,s)])}function TR(e,t){return(s,r)=>W(Ite,\"unitThreshold\",[e(s,r),t.bias!==void 0?oo(t.bias,s):$e(0),$e(t.low),$e(t.high)])}function F_(e){let t=new WeakMap;return(s,r)=>{let o=t.get(s);return o||(o=ns(e(s,r),\"field\"),t.set(s,o)),o}}var ule={mx3:{fn:()=>Gte,hint:\"unitNoise3\"},perlin:{fn:()=>mu,hint:\"perlin13\"},value:{fn:()=>iJ,hint:\"value13\"},mx3signed:{fn:()=>Na,hint:\"mxNoiseFloat3\"}};function Tc(e){let t=ule[e];return(s,r)=>W(t.fn(),t.hint,[r])}function SR(e,t){let s=Ok(Na,{name:\"warpDomain\"});return(r,o)=>{let a=oo(t.amount,r);t.amountScale!==void 0&&(a=a.mul(t.amountScale));let i=wt(r);return t.timeScale!==void 0&&(i=i.mul(t.timeScale)),e(r,W(s,\"warpDomain\",[o,i,a]))}}function PR(e,t){return(s,r)=>{let{viewport:o}=Kc(s);return e(s,W(Ote,\"volumeDomain\",[r,o,oo(t.scale,s)]))}}function CR(e){return(t,s)=>W(Fte,\"flowLayers\",[s,oo(e.detail,t),wt(t)])}function MR(e,t){return(s,r)=>W(Yte,\"shimmerPulse\",[e(s,r),wt(s),$e(t.speed),$e(t.span),$e(t.depth)])}function ER(e){let t=z(v(Qr(e),6),\"hue6\");return Qe(Ge(ne(ms(ne(t,3)),1),0,1),Ge(ne(2,ms(ne(t,2))),0,1),Ge(ne(2,ms(ne(t,4))),0,1))}function kR(e,t){return s=>e(s).mul(t(s,s.ctx.uv))}function BR(e){let t=s=>typeof s==\"number\"?$e(s):s;return e.anchors?W(PJ,\"scatterField\",[e.at,t(e.count),t(e.seed),e.anchors,t(e.softness)]):W(CJ,\"scatterField\",[e.at,t(e.count),t(e.seed),t(e.drift),t(e.aspect),t(e.time),t(e.softness)])}function zR(e,t){let s=e.dimensions.width/Math.max(e.dimensions.height,1e-6);e.onResize(({width:o,height:a})=>{s=o/Math.max(a,1e-6)});let r=\"\";return e.onBeforeRender(()=>{let o=e.getCpuValue(\"_animTime\"),a=e.getCpuValue(t.count.name),i=e.getCpuValue(t.seed.name),l=e.getCpuValue(t.drift.name);if(typeof o!=\"number\"||typeof a!=\"number\"||typeof i!=\"number\"||typeof l!=\"number\")return;let c=`${a}|${i}|${l}|${s}|${o}`;c!==r&&(r=c,e.setExtraField(t.field,lle(a,i,l,s,o)))}),e.uniforms[t.field]}function RR(e){return W(jte,\"rampWrap\",[e.t,typeof e.cycles==\"number\"?$e(e.cycles):e.cycles])}function DR(e){return W(S4,\"gradientEdgeMirror\",[e])}function LR(e){let t=s=>typeof s==\"number\"?$e(s):s;return e.levels===1?W(TJ(Na,{name:\"warpDomain1\"}),\"warpDomain1\",[e.at,t(e.time),t(e.amount)]):W(Ok(Na,{name:\"warpDomain\"}),\"warpDomain\",[e.at,t(e.time),t(e.amount)])}function o2(e){let t=s=>typeof s==\"number\"?$e(s):s;return W(MJ,\"chainedWarpStep\",[e.at,$e(e.scale),t(e.z),rt(e.offsets[0][0],e.offsets[0][1]),rt(e.offsets[1][0],e.offsets[1][1]),$e(e.reach),t(e.strength)])}function UR(e,t){return Oae(t,e)}function OR(e){return(t,s,r)=>{let{uniforms:o}=r,a=G_(r.propValues[e.space.name]),i=me(e.a,r),l=me(e.b,r),c=me(e.c,r),d=me(e.d,r);if(a!==0){let u=\"\";r.onBeforeRender(()=>{let f=[e.a.name,e.b.name,e.c.name,e.d.name].map(y=>r.getCpuValue(y));if(f.some(y=>!y))return;let h=f.map(y=>`${y.x},${y.y},${y.z}`).join(\"|\");if(h===u)return;u=h;let g=[\"convA\",\"convB\",\"convC\",\"convD\"];f.forEach((y,b)=>{let x=fl(y.x,y.y,y.z,a);r.setExtraField(g[b],[x[0],x[1],x[2]])})});let p=ns(W(_l,\"mixInSpace\",[o.convA,i.member(\"a\"),o.convB,l.member(\"a\"),t]),\"flowRow1\"),_=ns(W(_l,\"mixInSpace\",[o.convC,c.member(\"a\"),o.convD,d.member(\"a\"),t]),\"flowRow2\");return W(Bd[a]??hc,\"mixPreconvertedColors\",[p.member(\"conv\"),_.member(\"conv\"),p.member(\"alpha\"),_.member(\"alpha\"),s])}return W(Ar,\"mixColors\",[ns(W(Ar,\"mixColors\",[i,l,t]),\"flowRow1\"),ns(W(Ar,\"mixColors\",[c,d,t]),\"flowRow2\"),s])}}function ad(e,t){return typeof e==\"number\"?$e(e):e instanceof zi||e.kind!==\"ctx\"?fn(e,t):t.ctx[e.name]}function a2(e){return{uv:e.uvContext??e.ctx.uv,viewport:e.effectiveViewportSize??e.ctx.viewportSize}}function uh(e,t){return t.propValues[e.name]}function GR(e){return t=>me(e,t)}function FR(e){return t=>{let{uv:s,viewport:r}=a2(t),o=wt(t),a=W(Ete,\"colorWheelT\",[s,r,ad(e.direction,t),ad(e.scale,t),o]);if((uh(e.mode,t)??\"rainbow\")===\"rainbow\")return W(kte,\"colorWheelRainbow\",[a]);let i=z(W(Bte,\"colorWheelPhase\",[a]),\"wheelPhase\"),l=i.member(\"x\"),c=i.member(\"y\"),d=No[uh(e.space,t)??0]??Ar;return W(zte,\"colorWheelSelect\",[W(d,\"mixColors\",[me(e.palette.a,t),me(e.palette.b,t),l]),W(d,\"mixColors\",[me(e.palette.b,t),me(e.palette.c,t),l]),W(d,\"mixColors\",[me(e.palette.c,t),me(e.palette.a,t),l]),c])}}function IR(e){if(e.points.length!==5)throw new Error(\"pointCloudGradient expects exactly 5 points\");return t=>{let{uv:s,viewport:r}=a2(t),o=z(W(Dte,\"mpgFactors\",[s,r,...e.points.map(c=>me(c.position,t)),ad(e.smoothness,t)]),\"mpgFactors\"),a=No[uh(e.space,t)??0]??Ar,i=me(e.points[0].color,t),l=[\"x\",\"y\",\"z\",\"w\"];for(let c=1;c<5;c++)i=W(a,\"mixColors\",[i,me(e.points[c].color,t),o.member(l[c-1])]);return i}}var YR={convA:{schema:A,initial:[0,0,0]},convB:{schema:A,initial:[0,0,0]}};function WR(e){return t=>{let{uv:s,viewport:r}=a2(t),o=uh(e.space,t)??0,a=z(W(Lte,\"beamField\",[s,r,me(e.from,t),me(e.to,t),ad(e.thickness.start,t),ad(e.thickness.end,t),ad(e.softness.start,t),ad(e.softness.end,t)]),\"beamField\"),i=a.member(\"x\"),l=a.member(\"y\"),c=me(e.colors.inside,t),d=me(e.colors.outside,t),u;if(o!==0){let _=\"\";t.onBeforeRender(()=>{let f=t.getCpuValue(e.colors.inside.name),h=t.getCpuValue(e.colors.outside.name);if(!f||!h)return;let g=`${f.x},${f.y},${f.z}|${h.x},${h.y},${h.z}`;if(g===_)return;_=g;let y=fl(f.x,f.y,f.z,o),b=fl(h.x,h.y,h.z,o);t.setExtraField(\"convA\",[y[0],y[1],y[2]]),t.setExtraField(\"convB\",[b[0],b[1],b[2]])}),u=W(Bd[o]??hc,\"mixPreconvertedColors\",[t.uniforms.convA,t.uniforms.convB,c.member(\"a\"),d.member(\"a\"),i]).member(\"rgb\")}else u=W(Ar,\"mixColors\",[c,d,i]).member(\"rgb\");let p=c.member(\"a\").add(d.member(\"a\")).mul(.5);return Ct(u,p.mul(l))}}function Sa(e,t){return typeof e==\"function\"?e(t):me(e,t)}function oi(e){let t=new WeakMap;return s=>{let r=t.get(s);if(r)return r;let o=e(s);return t.set(s,o),o}}var ple={flippedY:{fn:()=>Mre,hint:\"latticeFrameFlipY\"},clockwise:{fn:()=>Ere,hint:\"latticeFrameCW\"},plain:{fn:()=>kre,hint:\"latticeFrame\"}};function Rn(e){let t=ple[e.convention];return oi(s=>{let{uv:r,viewport:o}=Ro(s);return W(t.fn(),t.hint,[r,o,me(e.cells,s),me(e.rotation,s)])})}function Sc(e,t){return s=>{let r=Sa(e,s);return Ct(r.member(\"rgb\").mul(Sa(t,s)),r.member(\"a\"))}}function Zr(e){return oi(t=>W(ug(e.space,t),\"mixColors\",[Sa(e.fill,t),Sa(e.stroke,t),Sa(e.mask,t)]))}function _g(e){return({uniforms:t,ctx:s})=>{let r={uniforms:t},o=e.rotation?W(Ure,\"gridCellCenterUV\",[s.uv,s.viewportSize,me(e.cells,r),me(e.rotation,r)]):W(yJ,\"cellCentreUV\",[s.uv,s.viewportSize,me(e.cells,r)]),a={};for(let i of e.props)a[i]=o;return a}}function qR(e,t){let s=z(Xr(ne(Ce(e,t.cellSize),.5)),\"scatterBase\");return[[0,0],[1,0],[0,1],[1,1]].map(([r,o])=>mle(z(I(s,rt(r,o)),\"scatterCell\"),e,t))}function mle(e,t,s){let r=z(W(nJ,\"hash32\",[I(e,rt(v(s.seed,97.13),v(s.seed,31.71)))]),\"scatterRandom\"),o=v(ne(r.member(\"xy\"),.5),s.jitter),a=z(v(I(I(e,.5),o),s.cellSize),\"scatterPoint\");return{point:a,offset:z(ne(t,a),\"scatterOffset\"),random:r}}function fp(e,t){return s=>Ct(Sa(e,s).member(\"rgb\"),Sa(t,s))}function I_(e){return t=>Sa(e,t).member(\"a\")}function n2(e){return t=>Sa(e,t).member(\"rgb\")}function Y_(e,t){return s=>Sa(e,s).mul(Sa(t,s))}function fg(e,t,s){return r=>ea(Sa(e,r),Sa(t,r),Sa(s,r))}function VR(e){return t=>Ct(Sa(e,t),1)}function NR(e){return t=>{let{uv:s,viewport:r}=Ro(t);return W(Bre,\"checkerBlend\",[s,r,me(e.cells,t),me(e.softness,t)])}}function $R(e){return t=>{let{uv:s,viewport:r}=Ro(t);return W(zre,\"stripesMask\",[s,r,me(e.angle,t),me(e.density,t),wt(t),me(e.offset,t),me(e.balance,t),me(e.softness,t)])}}function XR(e){return oi(t=>{let{uv:s,viewport:r}=Ro(t);return W(Rre,\"chevronMask\",[s,r,me(e.count,t),me(e.angle,t),me(e.balance,t),me(e.softness,t),me(e.offset,t),wt(t)])})}function HR(e){return t=>{let{uv:s,viewport:r}=Ro(t);return W(Dre,\"ripplesMask\",[me(e.center,t),me(e.frequency,t),me(e.softness,t),me(e.thickness,t),me(e.phase,t),wt(t),s,r])}}function jR(e){let t=oi(s=>{let{uv:r,viewport:o}=Ro(s);return z(W(Lre,\"fallingLinesField\",[r,o,me(e.angle,s),me(e.density,s),me(e.speedVariance,s),me(e.trailLength,s),me(e.strokeWidth,s),me(e.rounding,s),me(e.balance,s),wt(s)]),\"fallingField\")});return{mask:s=>t(s).member(\"x\"),fade:s=>t(s).member(\"y\")}}function KR(e){return t=>{let{uv:s,viewport:r}=Ro(t);return W(Gre,\"dotGridAlpha\",[s,r,me(e.density,t),me(e.dotSize,t),me(e.offset,t),me(e.speedVariance,t),me(e.twinkle,t),wt(t),t.ctx.time])}}function QR(e){let t=oi(s=>z(W(Ore,\"gridField\",[e.frame(s),me(e.thickness,s),me(e.softness,s),me(e.variation,s)]),\"gridField\"));return{lines:s=>t(s).member(\"x\"),shade:s=>t(s).member(\"y\")}}function ZR(e){let t=oi(s=>z(W(Fre,\"hexGridField\",[e.frame(s),me(e.thickness,s),me(e.softness,s),me(e.variation,s)]),\"hexField\"));return{lines:s=>t(s).member(\"x\"),shade:s=>t(s).member(\"y\")}}function JR(e){let t=oi(s=>z(W(Ire,\"triangularGridField\",[e.frame(s),me(e.thickness,s),me(e.softness,s),me(e.variation,s),me(e.speedVariance,s),wt(s)]),\"triField\"));return{lines:s=>t(s).member(\"x\"),shade:s=>t(s).member(\"y\")}}function eD(e){let t=oi(s=>{let{uv:r,viewport:o}=Ro(s);return z(W(Yre,\"brickField\",[r,o,me(e.cellsX,s),me(e.cellsY,s),me(e.mortar,s),me(e.softness,s),me(e.variation,s),me(e.rotation,s),me(e.offset,s),me(e.speedVariance,s),me(e.seed,s),wt(s)]),\"brickField\")});return{bricks:s=>t(s).member(\"x\"),shade:s=>t(s).member(\"y\")}}function tD(e){return t=>W(Wre,\"truchetField\",[e.frame(t),me(e.thickness,t),me(e.softness,t),me(e.seed,t)])}function sD(e){return t=>W(qre,\"weaveColor\",[e.frame(t),me(e.gap,t),me(e.colors[0],t),me(e.colors[1],t)])}function rD(e){let t=oi(s=>z(W(Vre,\"isoCubeField\",[e.frame(s),me(e.thickness,s),me(e.softness,s)]),\"isoCubeField\"));return{random:s=>t(s).member(\"x\"),tone:s=>t(s).member(\"y\"),wire:s=>t(s).member(\"z\")}}function _le(e,t,s,r,o){return W(Kre,\"halftonePlateGrid\",[e,t,s,r,o])}function fle(e,t){return W(Qre,\"halftoneTransmission\",[e,t])}function oD(e){return Rd({resultAlpha:\"straight\",build:({sampleStraight:t,ctx:s,uniforms:r})=>W(Nre,\"halftoneClassic\",[t(s.uv),s.uv,s.aspect,me(e.angle,{uniforms:r}),me(e.frequency,{uniforms:r})])})}var vle={cyan:{fn:()=>Xre,hint:\"halftoneChannelC\"},magenta:{fn:()=>Hre,hint:\"halftoneChannelM\"},yellow:{fn:()=>jre,hint:\"halftoneChannelY\"},black:{fn:()=>bd,hint:\"halftoneChannelK\"}};function aD(e){return Rd({resultAlpha:\"straight\",build:({sampleStraight:t,ctx:s,uniforms:r})=>{let o=i=>me(i,{uniforms:r}),a=o(e.paper).member(\"rgb\");return e.plates.forEach((i,l)=>{let c=t(W($re,\"halftonePlateUV\",[s.uv,o(e.misprintAngle),$e(l*90),o(e.misprint),s.aspect])),d=vle[i.channel],u=W(d.fn(),d.hint,[c]);a=a.mul(fle(o(i.ink),_le(s.uv,s.aspect,o(i.screenAngle),u,o(e.frequency))))}),Ct(a,t(s.uv).member(\"a\"))}})}function nD(e,t,s){let r=Object.values(s),o=r[0]?.resultAlpha;for(let a of r)if(a.resultAlpha!==o||a.setup)throw new Error(\"std: chosenBy recipes must share resultAlpha and carry no setup hooks\");return Rd({resultAlpha:o,build:a=>s[t(a.propValues[e.name])].build(a)})}var hle=e=>typeof e==\"number\"?e:{bayer2:0,bayer4:1,bayer8:2,clusteredDot:3,blueNoise:4,whiteNoise:5,floydSteinberg:6}[e]??1,gle=e=>typeof e==\"number\"?e:e===\"source\"?1:0;function iD(e){let t=oi(r=>z(W(Zre,\"ditherCoord\",[r.ctx.uv,r.ctx.logicalViewportSize,me(e.size,r)]),\"ditherCoord\")),s=oi(r=>z(r.sampleStraight(W(Jre,\"ditherPixUV\",[t(r),me(e.size,r),r.ctx.logicalViewportSize])),\"ditherSource\"));return{size:e.size,coord:t,source:s}}function yle(e,t){if(e===4)return W(toe,\"ditherBlueNoise\",[t]);if(e===5||e>6||e<0)return W(soe,\"ditherWhiteNoise\",[t]);let s=W(eoe,\"ditherPeriodic\",[t]);switch(e){case 0:return s.member(\"x\");case 1:return s.member(\"y\");case 2:return s.member(\"z\");default:return s.member(\"w\")}}function lD(e){return t=>{let s=c=>me(c,t),r=s(e.threshold),o=s(e.spread),a=e.grid.coord(t),i=hle(t.propValues[e.pattern.name]);if(i===6){let c=t.ctx.logicalViewportSize,d=s(e.grid.size),u=W(ooe,\"ditherBlockOrigin\",[a]),p=W(aoe,\"ditherLocalCellIndex\",[a,u]),_=[];for(let f=0;f<8;f++)for(let h=0;h<8;h++){let g=W(noe,\"ditherCellUV\",[u,$e(h),$e(f),d,c]);_.push(W(NS,\"ditherLuma\",[t.sampleStraight(g)]))}return W(ioe,\"ditherFloydSteinberg\",[see(\"f32\",_),p,r,o])}let l=W(NS,\"ditherLuma\",[e.grid.source(t)]);return W(roe,\"ditherOrderedResult\",[yle(i,a),l,r,o])}}function cD(e){return Rd({resultAlpha:\"straight\",build:t=>{let s=o=>me(o,t),r=e.levels(t);return gle(t.propValues[e.mode.name])===0?W(loe,\"ditherComposeCustom\",[s(e.colors[0]),s(e.colors[1]),r]):W(coe,\"ditherComposeSource\",[e.grid.source(t),r])}})}function dD(e){let t=I(v(e.along,e.frequency),v(e.time,e.speed));return e.phase!==void 0&&(t=I(t,e.phase)),ts(I(v(Vt(t),.5),.5),e.sharpness)}function W_(e){let t=s=>typeof s==\"number\"?$e(s):s;return W(BJ,\"glowAt\",[e.uv,t(e.aspect),t(e.x),t(e.y),t(e.sizeSq)])}function uD(e){let t=e.bulge?.tilt??.2,s=e.bulge?.drop??.1,r=ns(ot(0,e.radius,e.dist),\"domeSlope\"),o=I(r,t);return Os(Qe(v(e.delta.member(\"x\"),o),v(e.delta.member(\"y\"),o),ne(1,v(r,s))))}function pD(e){return ts(it(0,Lo(v(e.light,-1),e.normal).member(\"z\")),e.gloss)}function hr(e,t){return typeof e==\"number\"?$e(e):e instanceof zi||e.kind!==\"ctx\"?fn(e,t):t.ctx[e.name]}function Dd(e){return{uv:e.uvContext??e.ctx.uv,viewport:e.effectiveViewportSize??e.ctx.viewportSize}}function mD(e){return t=>{let{uv:s,viewport:r}=Dd(t);return W(LJ,\"radialFrame\",[s,r,me(e,t)])}}function _D(e){return t=>{let{uv:s,viewport:r}=Dd(t);return W(Wte,\"godraysFrame\",[s,r,me(e,t)])}}function fD(e){return t=>W(GJ,\"beamFrame\",[t.ctx.uv,t.ctx.aspect,me(e,t)])}function vg(e,t,s){return r=>t(s?ns(e(r),s):e(r),r)}function vD(e,...t){return(s,r)=>t.reduce((o,a)=>o.mul(a(s,r)),e(s,r))}function hD(e){return(t,s)=>W(DJ,\"softRayLobes\",[t.member(\"y\"),hr(e.count,s),wt(s).mul(e.spin),hr(e.softness,s)])}function gD(e){return(t,s)=>W(RJ,\"featherMask\",[t.member(\"x\"),hr(e.radius,s),hr(e.feather,s)])}function yD(e){return(t,s)=>W(qte,\"godraysRayStack\",[t,wt(s),hr(e.density,s),hr(e.intensity,s),hr(e.spotty,s)])}function bD(e){return(t,s)=>W(Nte,\"leakDrift\",[t,hr(e.seed,s),hr(e.flicker,s)])}function xD(e){return(t,s,r)=>W($te,\"leakBloom\",[t.member(\"x\"),t.member(\"y\"),hr(e.spread,r),s])}function AD(e){return(t,s,r)=>W(Xte,\"leakStreaks\",[t.member(\"x\"),t.member(\"y\"),hr(e.spread,r),hr(e.strength,r),s])}function wD(e,t,s){return W(Hte,\"heatDither\",[e,t,s])}function q_(e,t,s){return W(D1,\"glowSpot\",[e,t,s])}function TD(e,t){let s=z(ms(e.member(\"x\")),\"glintX\"),r=z(ms(e.member(\"y\")),\"glintY\"),o=typeof t.core==\"number\"?$e(t.core):t.core,a=q_(e.member(\"x\"),e.member(\"y\"),o),i=(c,d)=>tr(er(I(v(c,t.rayWidth),v(d,t.rayLength)))),l=I(a,v(I(i(s,r),i(r,s)),t.rays));return t.reach===void 0?l:v(l,ne(1,ot(v(t.reach,.6),t.reach,it(s,r))))}function SD(e){return t=>{let{uv:s,viewport:r}=Dd(t);return W(Eoe,\"flareFrame\",[s,r,wt(t),me(e.position,t),hr(e.intensity,t),hr(e.edgeFade,t)])}}function PD(e){return(t,s,r,o)=>{let{uv:a}=Dd(o);return W(koe,\"lensFlareGhost\",[a,t.member(\"aspect\"),t.member(\"lightPos\"),t.member(\"flareAxis\"),wt(o),hr(e.spread,o),hr(e.chroma,o),hr(e.intensity,o),Ct(...s),Ct(...r)])}}function CD(e){return(t,s)=>{let{uv:r}=Dd(s);return W(Boe,\"flareHalo\",[r,t.member(\"aspect\"),t.member(\"lightPos\"),wt(s),hr(e.intensity,s),hr(e.radius,s),hr(e.chroma,s),hr(e.softness,s)])}}function MD(e){return(t,s)=>W(zoe,\"flareStarburst\",[t.member(\"lightAngle\"),t.member(\"flareAxis\"),t.member(\"aspect\"),wt(s),t.member(\"lightDist\"),hr(e.points,s),hr(e.intensity,s)])}function ED(e){return(t,s)=>{let{uv:r}=Dd(s);return W(Roe,\"flareStreak\",[r,t.member(\"lightPos\"),hr(e.length,s),hr(e.intensity,s)])}}function kD(e){return(t,s)=>W(Doe,\"flareGlare\",[t.member(\"lightDist\"),hr(e.size,s),hr(e.intensity,s)])}function BD(e){return(t,s)=>W(Loe,\"flareCore\",[t.member(\"lightDist\"),hr(e.intensity,s)])}function zD(e,t){return W(Uoe,\"flareComposite\",[e,t.member(\"masterFade\")])}function RD(e){return t=>{let{uv:s,viewport:r}=Dd(t),o=hr(e.strength,t);return e.scale!==void 0&&(o=o.mul(e.scale)),W(UJ,\"vignetteMask\",[s,r,o])}}function i2(e,...t){return t.reduce((s,r)=>s.add(r),e)}function DD(e){return{kind:\"heatRamp\",taps:(t,s)=>W(zJ,\"chromaticHeatTaps\",[t,me(e.hot,s).member(\"rgb\"),me(e.mid,s).member(\"rgb\"),me(e.fringe,s).member(\"rgb\")])}}function LD(e){return t=>{let s=e.hint?ns(e.coverage(t),e.hint):e.coverage(t),r=me(e.color,t),o=me(e.background,t);return Ct(ea(o.member(\"rgb\"),r.member(\"rgb\"),s),ea(o.member(\"a\"),r.member(\"a\"),s))}}function UD(e){return t=>W(Vte,\"coverageOver\",[me(e.color,t),me(e.background,t),e.coverage(t)])}function OD(e){return{kind:\"pointwise\",body:{fn:OJ,hint:\"screenGlowComposite\"},args:t=>[e(t)]}}var ble=6.283185307179586;function vp(e,t){let s=(a,i,l)=>{if(i!==void 0){if(a===void 0)throw new Error(`std: waves() schedule uses axis '${l}' but the axes object doesn't provide it`);return i===1?a:v(a,i)}},r=a=>{let i=[s(e.x,a.x,\"x\"),s(e.y,a.y,\"y\"),s(e.t,a.t,\"t\"),a.phase].filter(l=>l!==void 0);if(i.length===0)throw new Error(\"std: a wave needs at least one axis coefficient or phase\");return Vt(i.reduce((l,c)=>I(l,c)))},o=t.map(a=>{if(a.waves.length===0)throw new Error(\"std: a waves() term needs at least one wave\");let i=a.waves.map(r).reduce((l,c)=>v(l,c));return a.weight===void 0||a.weight===1?i:v(i,a.weight)});if(o.length===0)throw new Error(\"std: waves() needs at least one term\");return o.reduce((a,i)=>I(a,i))}function l2(e){return vp({x:e.along,t:e.time},e.waves.map(t=>({waves:[{x:t.freq,t:t.speed,phase:t.phase}],weight:t.amount})))}function GD(e){let t=Math.max(1,Math.round(e.count)),s=e.jitter??.6,r=Ce(e.spread,t),o=(a,i)=>Qr(v(Vt(v(I(e.seed,.37+a*31.7),i)),43758.5453));return Array.from({length:t},(a,i)=>{let l=(i+.5)/t-.5;return{offset:z(I(v(e.spread,l),v(v(ne(o(i,12.9898),.5),s),r)),`fold${i}Off`),width:z(v(e.width,et(.6,1.5,o(i,78.233))),`fold${i}W`),height:z(v(e.height,et(.35,1,o(i,39.425))),`fold${i}H`),phase:z(v(o(i,94.673),ble),`fold${i}Ph`)}})}function FD(e,t,s){return YD(e,t,s).map(r=>v(r.rise,r.profile)).reduce((r,o)=>I(r,o))}function ID(e,t,s){let r=s.foreshorten??.25,o=YD(e,t,s),a=o.map(l=>v(l.profile,ne(l.spine,l.fold.offset))).reduce((l,c)=>I(l,c)),i=o.map(l=>v(v(l.fold.height,r),v(Bx(l.d),ne(1,l.profile)))).reduce((l,c)=>I(l,c));return{along:typeof e.along==\"number\"?$e(e.along):e.along,across:z(I(ne(e.across,a),i),\"clothAcross\")}}function YD(e,t,s){let r=s.pinning??0,o=s.sharpness??2,a=s.crease??0,i=s.swell??{amount:.25,freq:2.7},l=i.speed??.4,c=z(Ge(e.along,0,1),\"hang\"),d=z(et(1,c,r),\"free\"),u=z(et(1,et(.7,1.2,c),r),\"open\");return t.map(p=>{let _=l2({along:e.along,time:s.time,waves:s.ripple.waves.map((x,P)=>({...x,phase:v(p.phase,P+1)}))}),f=s.ripple.gain===void 0?_:v(_,s.ripple.gain),h=I(p.offset,v(f,d));if(s.sway){let x=Vt(I(v(s.time,s.sway.rate??.7),p.phase));h=I(h,v(v(x,s.sway.amount),d))}let g=z(h,\"spine\"),y=z(Ce(ne(e.across,g),v(p.width,u)),\"foldD\"),b=i.amount>0?v(p.height,I(1-i.amount,v(Vt(I(I(v(e.along,i.freq),p.phase),v(s.time,l))),i.amount))):p.height;return{fold:p,spine:g,d:y,profile:z(xle(y,o,a),\"profile\"),rise:b}})}function xle(e,t,s){if(s<=0)return O_(e,t);let r=z(v(e,e),\"foldD2\");return tr(er(v(et(r,v(r,r),s),t)))}function Qa(e,t){return(s,r)=>{let{viewport:o}=Ro(s);return e(s,W(bJ,\"aspectScaledDomain\",[r,o,me(t.scale,s),me(t.seed,s)]))}}function WD(e,t){return(s,r)=>{let{viewport:o}=Ro(s);return e(s,W(uoe,\"rawAspectScaledDomain\",[r,o,me(t.scale,s),me(t.seed,s)]))}}function qD(e,t){return(s,r)=>{let{viewport:o}=Ro(s);return e(s,W(xJ,\"pixelGridDomain\",[r,o,me(t.grain,s),me(t.seed,s)]))}}function hp(e,t){return(s,r)=>e(s,W(AJ,\"timeAxisDomain\",[r,wt(s),$e(t.rate)]))}function VD(e){return(t,s)=>W(lJ,\"gabor12\",[s,me(e.frequency,t),wt(t)])}function ND(e){return(t,s)=>W(cJ,\"wavelet12\",[s,wt(t),me(e.detail,t)])}function $D(){return(e,t)=>W(dJ,\"blue12\",[t])}function XD(){return(e,t)=>W(_J,\"erosion12\",[t]).member(\"x\")}function HD(e){return(t,s)=>W(moe,\"curlMagnitude\",[s,wt(t).mul(e.rate)])}function jD(e){return(t,s)=>W(uJ,\"scratches12\",[s,wt(t),me(e.thickness,t)])}function gp(e){return(t,s)=>W(fJ,\"signedToUnit\",[e(t,s)])}function ua(e,t){return(s,r)=>W(Uae,\"noiseToneKColor\",[e(s,r),t?.contrast?me(t.contrast,s):$e(0),t?.balance?me(t.balance,s):$e(0)])}function KD(e,t){return(s,r)=>W(Rk,\"noiseToneSigned\",[e(s,r),me(t.contrast,s),me(t.balance,s)])}function Ale(e,t){return(s,r)=>W(goe,\"worleyTone\",[e(s,r),me(t.contrast,s),me(t.balance,s)])}function QD(e,t){return(s,r)=>W(Ar,\"mixColors\",[me(e,r),me(t,r),s])}function ZD(e){return Vs((s,r)=>{let{viewport:o}=Ro(s);return W(foe,\"fbmNormalize\",[W(_oe,\"fractalSum\",[W(poe,\"rotatedCenteredDomain\",[r,o,me(e.angle,s)]),me(e.detail,s),me(e.contrast,s),wt(s),me(e.seed,s),me(e.octaves,s)])])},Us(e.space))}function JD(e){return Vs(Ale((s,r)=>{let{viewport:o}=Ro(s),a=yoe(s.propValues[e.mode.name]),i=boe(s.propValues[e.distance.name]),l=Math.max(1,Math.min(4,Math.round(s.propValues[e.octaves.name]??1)));return W(hoe,\"worleyNormalize\",[W(voe,\"worleyCells\",[W(V4,\"aspectPlane\",[r,o]),me(e.scale,s),me(e.seed,s),me(e.jitter,s),me(e.lacunarity,s),me(e.persistence,s),wt(s),$e(l),$e(i),$e(a)]),$e(xoe(a,i))])},{contrast:e.contrast,balance:e.balance}),Us(e.space))}function eL(e){return(t,s)=>{let{viewport:r}=Ro(t);return W(Aoe,\"voronoiNearest2\",[W(V4,\"aspectPlane\",[s,r]).mul(me(e.scale,t)),wt(t),me(e.seed,t)])}}function tL(e,t){return(s,r)=>W(woe,\"cellFill\",[e(s,r),me(t.edgeIntensity,s)])}function sL(e,t){return(s,r)=>W(Toe,\"cellBorders\",[e(s,r),me(t.softness,s),me(t.scale,s)])}function rL(e,t,s){return r=>{let{uv:o}=Ro(r),a=ns(e(r),\"base\");return Ct(ea(me(s.color,r).member(\"rgb\"),a.member(\"rgb\"),t(r,o)),a.member(\"a\"))}}function oL(e,...t){return s=>{let r=e(s),o=r.member(\"rgb\");for(let a of t)o=a(o,s);return Ct(o,r.member(\"a\"))}}function aL(e){return(t,s)=>q4(t,e.space?G_(s.propValues[e.space.name]):0)}function nL(){return e=>W(Soe,\"toneLift\",[e])}function iL(e){return t=>{let{uniforms:s}=t,{uv:r,viewport:o}=Ro(t),a=wt(t),i=wt(t,void 0,\"_animTime_color\"),l=Ct(me(e.amplitude,t),me(e.frequency,t),me(e.count,t),me(e.width,t)),c=Ct(me(e.softness,t),me(e.spread,t),me(e.colorScale,t),me(e.colorVariance,t)),d=Ct(me(e.pinEdges,t),a,i,s.stopCount);return W(Poe,\"ribbonsField\",[r,o,me(e.from,t),me(e.to,t),l,c,d,s.colorsArray,s.positionsArray,s.convertedColorsArray])}}var hg={stone:{fn:pJ,hint:\"stone12\"},wool:{fn:mJ,hint:\"wool12\"}};function gg(e){return t=>Yae(t,e.fn,e.hint)}var lL=N4,cL=$4;var{sdfSpaceUV:dL,offsetUV:x1}=ip,{ANALYTIC_SDF_EXTRA_FIELDS:wle}=yc,{createVolumetricFieldComputeNode:Tle,resolveShapeFieldSampler:Sle,VOLUMETRIC_FIELD_EXTRA_FIELDS:Ple}=kn,Cle=0;function Js(e,t,s,r=\"guarded\",o=[]){let a=Cle++;return new fs(i=>i.memo(`guard:${a}`,()=>{let l=i.freshLocal(r),c=e._emit(i),d=s._emit(i);for(let _ of o)_._emit(i);let u=[],p=t._emit({external:(_,f)=>i.external(_,f),statement:_=>u.push(_),freshLocal:_=>i.freshLocal(_),memo:(_,f)=>i.memo(_,f)});return i.statement(`var ${l} = ${d};`),i.statement(`if (${c}) {\n  ${u.join(`\n  `)}\n  ${l} = ${p};\n}`),l}))}function Dr(e,t){let s=W(K4,\"outsideShape\",[e,t]);return new fs(r=>`(!(${s._emit(r)}))`)}function Ts(e){let t=e.pattern??\"none\",s=e.chord??\"span\",r=e.stencil??{kind:\"forward\",eps:.01},o=i=>Tle(i,()=>i.getCpuValue(\"shape\"),t,s),a=i=>{let{childNode:l,computeOutputs:c,convertToTexture:d,uniforms:u,ctx:p}=i,_;if(e.child===\"required\"){if(!l)return xt;_=c?.childTexture??d(l)}let{sampler:f,gradSampler:h,texelSampler:g,volumetric:y,bakedGradients:b}=Sle(i,{patternMode:t,gradSampler:e.gradSampler??\"fast\",bakedGradients:e.bakedGradients??\"none\"}),x={sampler:f,gradSampler:h,texelSampler:g,volumetric:y,bakedGradients:b,volFlag:$e(y?1:0),childTexture:_};if(r!==\"none\"){let P=e.placeUV?e.placeUV(i):p.uv;if(x.sdfUV=z(W(dL,\"sdfSpaceUV\",[u.center,u.scale,u.rotation,P,p.aspect]),\"sdfUV\"),x.surf0=(e.centreTap===\"fast\"?h:f)(x.sdfUV),r!==\"centre\"){let{kind:C,eps:E}=r;x.surfX=h(W(x1,\"offsetUV\",[x.sdfUV,$e(E),$e(0)])),C===\"central\"&&(x.surfL=h(W(x1,\"offsetUV\",[x.sdfUV,$e(-E),$e(0)]))),x.surfY=h(W(x1,\"offsetUV\",[x.sdfUV,$e(0),$e(E)])),C===\"central\"&&(x.surfD=h(W(x1,\"offsetUV\",[x.sdfUV,$e(0),$e(-E)])))}}return e.surface(x,i)};return{extraFields:{...Ple,...wle},compute:o,gpu:{fragment:a}}}function Lr(e,t,s={}){let{ctx:r}=t,o=z(e.surf0,\"s0\"),a=z(Ce(1,r.viewportSize.member(\"y\")),\"pxH\"),i=z(s.scale?Ce(o.member(\"x\"),s.scale):o.member(\"x\"),\"sdf\"),l=Ce(r.viewportSize.member(\"x\"),r.viewportSize.member(\"y\"));return{s0:o,sX:e.surfX?z(e.surfX,\"sX\"):void 0,sY:e.surfY?z(e.surfY,\"sY\"):void 0,sdf:i,pxH:a,aspect:l}}function Dn(e,t,s){let r=It(s.eps??.01),o=s.hint??\"nGeo\";if(e.volumetric)return z(W(sA,\"volumetricNormal\",[t.s0,t.sX,t.sY,r,It(s.sharpness??5)]),o);let a=Ge(Ce(er(t.sdf),it(s.bevelWidth,.003)),0,1);return z(W(H4,\"bevelledFlatNormal\",[W(tA,\"fieldGradient\",[t.s0,t.sX,t.sY,r]),W(sz,\"bevelSin\",[a,s.bevelShape])]),o)}function go(e,t,s={}){return z(W(j4,\"patternCoords\",[s.uv??e.sdfUV,t.s0,e.volFlag]),\"pat\")}function Ln(e,t=.01){return z(W(tA,\"fieldGradient\",[e.s0,e.sX,e.sY,It(t)]),\"grad\")}function Ea(e,t,s=.6){return z(W(Q4,\"perspectiveViewRay\",[e.ctx.uv,t.aspect,It(s)]),\"viewI\")}function ni(e,t){return z(ne(1,Ge(er($t(e,t)),0,1)),\"grazing\")}function yg(e,t,s,r=\"n\"){let o=i=>{let l=[];return s.across&&l.push(v(t.perp.member(i),s.across)),s.along&&l.push(v(t.tangent.member(i),s.along)),l},a=(i,l)=>l.reduce((c,d)=>I(c,d),i);return z(Os(Qe(a(e.member(\"x\"),o(\"x\")),a(e.member(\"y\"),o(\"y\")),e.member(\"z\"))),r)}function Pc(e,t,s){let r=o=>typeof o==\"number\"?It(o):o;return W(tae,\"studio5Softbox\",[e,t,s.rotation.member(\"x\"),s.rotation.member(\"y\"),r(s.keyRadius),r(s.drift),r(s.strength),It(s.skyGain)])}function Cc(e,t){let s=v(e.along,t.freq[0]),r=v(e.across,t.freq[1]);return t.offset?.[0]&&(s=I(s,t.offset[0])),t.offset?.[1]&&(r=I(r,t.offset[1])),t.drift&&(s=I(s,t.drift)),W(la,\"mxNoiseFloat2\",[rt(s,r)])}function uL(e,t){let s=e.taps??9,r=e.sigma??2.1,o=(s-1)/2,a=Array.from({length:s},(u,p)=>Math.exp(-((p-o)**2)/(2*r*r))),i=a.reduce((u,p)=>u+p,0),l=a.map(u=>u/i),c=z(Ce(e.spread,o),\"step\"),d=It(0);for(let u=0;u<s;u++){let p=z(v(c,It(u-o)),\"off\");d=I(d,v(t(I(e.center.member(\"x\"),v(e.axis.member(\"x\"),p)),I(e.center.member(\"y\"),v(e.axis.member(\"y\"),p))),It(l[u])))}return z(d,e.hint??\"smear\")}function Ld(e,t,s=\"L\"){return z(Os(Qe(e.member(\"x\"),e.member(\"y\"),It(t))),s)}function V_(e,t){let s=z(et(.05,.45,e),\"baseA\");return{along:v(s,I(1,v(t,7))),across:it(v(s,ne(1,v(t,.55))),.02)}}function Ud(e){let t=W(Z4,\"wardAnisotropicSpecular\",[W(rA,\"WardSpecularInput\",[e.normal,Qe(e.tangent.member(\"x\"),e.tangent.member(\"y\"),0),e.light,e.view,e.alphas.along,e.alphas.across])]);return e.gain===void 0?t:v(t,e.gain)}function pL(e,t,s,r=\"n\"){return z(Os(Qe(I(e.member(\"x\"),v(t.member(\"x\"),s)),I(e.member(\"y\"),v(t.member(\"y\"),s)),e.member(\"z\"))),r)}function mL(e,t){return I(1,v(ts(e,It(t.power??3)),t.amount))}function _L(e,t,s){return et(t.member(\"rgb\"),s.member(\"rgb\"),kt(e))}function Jr(e,t){return W($oe,\"silhouetteAlpha\",[e.sdf,t,e.pxH])}function yp(e){return W(g4,\"tonemapNeutral\",[e])}function Gi(e,t,s,r=\"n\"){return z(W(Qoe,\"nudgeNormal\",[e,t,s]),r)}function Mc(e,t=5,s=\"nGeo\"){return z(W(sA,\"volumetricNormal\",[e.s0,e.sX,e.sY,It(.01),It(t)]),s)}function Un(e,t,s=\"kl\"){return bg(e.member(\"x\"),e.member(\"y\"),t,s)}function bg(e,t,s,r=\"kl\"){return z(W(Xoe,\"keyLight\",[e,t,It(s)]),r)}function Fi(e){return z(W(Hoe,\"grazingView\",[e]),\"grazing\")}function xg(e,t,s){return W(joe,\"sharpGlint\",[e,t,s])}function Ag(e,t){return W(Koe,\"clampedPerlinGrad\",[e,typeof t==\"number\"?It(t):t])}function Hu(e,t){return W(Zoe,\"opticalThickness\",[e.sdf,t.volFlag])}function wg(e,t){return W(Joe,\"beerLambert\",[e,t])}function Tg(e,t){return W(eae,\"flowWarpOffset\",[e,t])}function co(e,t){return W(la,\"mxNoiseFloat2\",[rt(e,t)])}function pa(e){return W(la,\"mxNoiseFloat2\",[e])}function Jn(e){return W(Na,\"mxNoiseFloat3\",[e])}function fL(e,t){return W(A_,\"cosinePalette\",[e,It(t[0]),It(t[1]),It(t[2])])}function N_(e){return W(I3,\"mxWorleyNoiseFloat2Pub\",[e,It(1)])}function bp(e,t,s,r=\"fd\"){let o=z(e(t),`${r}0`);return{value:o,dx:z(Ce(ne(e(I(t,rt(s,0))),o),s),`${r}X`),dy:z(Ce(ne(e(I(t,rt(0,s))),o),s),`${r}Y`)}}function vL(e,t,s,r=\"fd\"){let o=z(e(t),`${r}0`),a=z(e(I(t,rt(s,0))),`${r}Px`),i=z(e(ne(t,rt(s,0))),`${r}Mx`),l=z(e(I(t,rt(0,s))),`${r}Py`),c=z(e(ne(t,rt(0,s))),`${r}My`);return{value:o,dx:z(Ce(ne(a,i),2*s),`${r}X`),dy:z(Ce(ne(l,c),2*s),`${r}Y`),curvature:z(Ce(ne(I(I(a,i),I(l,c)),v(o,4)),s*s),`${r}Curv`)}}function xp(e,t){return W(Ys,\"hash12\",[rt(e,t)])}function hL(e,t){return W(Aa,\"value12\",[rt(e,t)])}function gL(e){let t=z(e.gradSampler(rt(.5,.5)),\"s0C\");return{x:z(v(er(t.member(\"y\")),e.volFlag),\"rotSenseX\"),y:z(v(er(t.member(\"z\")),e.volFlag),\"rotSenseY\")}}function yL(e,t,s){let r=z(ne(e.sdfUV.member(\"x\"),.5),\"cx\"),o=z(ne(e.sdfUV.member(\"y\"),.5),\"cy\");return{x:z(I(r,v(t.member(\"x\"),s)),\"bentX\"),y:z(I(o,v(t.member(\"y\"),s)),\"bentY\"),centered:{x:r,y:o}}}function Ap(e,t){return I(t.r0??.04,v(ts(e,It(t.power??5)),t.gain))}function c2(e,t,s){return I(ne(1,s.amount),v(Vt(I(v(e,s.rate),v(t,s.spread))),s.amount))}function d2(e,t){let s=t.hint??\"stars\",r=z(I(v(pa(v(e,t.variationFreq)),.5),.5),`${s}V`),o=t.shimmer?t.shimmer(r):It(1),a=z(v(v(v(ot(I(t.radius[0],v(r,t.radius[1])),5e-4,N_(e)),ot(t.keep[0],t.keep[1],r)),v(I(t.brightness[0],v(v(r,r),t.brightness[1])),o)),t.gain),s);return{glint:a,variation:r,rgb:t.tint?v(et(Qe(...t.tint.from),Qe(...t.tint.to),kt(r)),kt(v(a,t.tint.gain))):void 0}}function bL(e,t,s=\"placed\"){let{uniforms:r,ctx:o}=e,a=rt(t.member(\"x\"),ne(1,t.member(\"y\")));return z(W(dL,\"sdfSpaceUV\",[r.center,r.scale,r.rotation,a,o.aspect]),s)}function xL(e,t,s){if(!(!e.volumetric&&s.propValues.shapeSdfUrl))return{sdf:t.sdf,outward:rt(0,0),at:l=>e.gradSampler(l).member(\"x\")};let r=e.sdfUV,o=l=>ne(l,Ge(l,rt(0,0),rt(1,1))),a=z(o(r),\"outward\"),i=(l,c)=>it(l,it(c,ne(l,c)));return{sdf:z(i(t.sdf,Kr(a)),\"sdfFar\"),outward:a,at:l=>{let c=Kr(o(l));return i(e.gradSampler(l).member(\"x\"),c)}}}function AL(e,t,s,r=\"edge\"){let o=z(Ce(s,it(Kr(s),1e-4)),`${r}N`);return{point:z(ne(e,v(o,it(t,0))),`${r}P`),normal:o}}function u2(e,t,s=\"pl\"){let r=z(ne(e.position,t),`${s}D`),o=z(Kr(r),`${s}Dist`),a=z(Ce(r,it(o,1e-4)),`${s}Dir`);return{L:z(Os(Qe(r.member(\"x\"),r.member(\"y\"),er(e.height))),`${s}L`),toLight:a,distance:o}}function Pl(e,t,s={}){let r=$t(e,t);return s.wrap===void 0?it(r,0):Ge(Ce(I(r,s.wrap),I(1,s.wrap)),0,1)}function p2(e,t){let s=Ce(e,t);return Ce(1,I(1,v(s,s)))}function m2(e){return ne(1,tr(er(e)))}function wL(e,t){let s=t.softness?z(U_(et(2,-2,t.softness)),\"lobeWiden\"):void 0,r=o=>s?v(o,s):It(o);return v(I(v(ts(e,r(t.core[0])),t.core[1]),v(ts(e,r(t.halo[0])),t.halo[1])),t.gain)}function TL(e){let t=v(ts(ni(e.normal,e.view),z(et(3,1,e.roughness),\"sheenPow\")),Pl(e.normal,e.light,{wrap:.5}));return e.gain===void 0?t:v(t,e.gain)}function SL(e,t,s){let r=z(v(e.s0.member(\"w\"),t.volFlag),\"rayEntry\"),o=z(s.lengthScale===void 0?Hu(e,t):v(Hu(e,t),s.lengthScale),\"rayLen\"),a=s.view?z(v(s.view.member(\"x\"),s.shear??1),\"rayShearX\"):void 0,i=s.view?z(v(s.view.member(\"y\"),s.shear??1),\"rayShearY\"):void 0;return{entry:r,length:o,at(l){return{x:a?I(s.origin.x,v(a,l)):s.origin.x,y:i?I(s.origin.y,v(i,l)):s.origin.y}}}}function _2(e,t){let s=t.hint??\"plane\",r=z(I(e.entry,v(e.length,t.depth)),`${s}Z`),o=e.at(r);if(!t.pan)return{x:o.x,y:o.y,z:r};let a=t.panRate??1;return{x:I(o.x,v(t.pan.x,a)),y:I(o.y,v(t.pan.y,a)),z:r}}function PL(e,t){let s=t.hint??\"\",r=t.drift,o=t.seed,a=(p,_,f,h)=>{let g=_===void 0?v(p,t.frequency):v(p,v(t.frequency,_));return r&&(g=I(g,f===1?r:v(r,f))),o&&(g=I(g,h===1?o:v(o,h))),g},i=z(Qe(a(e.x,void 0,1,1),a(e.y,void 0,-.7,.53),a(e.z,1.25,1.3,.79)),`q${s}`),l=z(Jn(r?I(v(i,.5),Qe(v(r,1.4),0,v(r,-.9))):v(i,.5)),`warp${s}`),c=t.billow?z(I(i,v(Qe(l,v(l,-.73),v(l,.41)),t.billow)),`qw${s}`):i,d=z(Jn(I(v(c,2.13),Qe(5.2,1.3,8.4))),`mid${s}`),u=z(Jn(I(v(c,4.31),Qe(9.1,3.7,1.2))),`fine${s}`);return{density:z(v(I(I(Jn(c),v(d,.5)),v(u,.27)),.57),`fbm${s}`),mid:d,fine:u}}function CL(e,t){let s=kt(1),r=Qe(0,0,0);for(let o=0;o<e.steps;o++){let a=(o+.5)/e.steps,i=z(I(e.entry,v(e.length,e.jitter?I(a,e.jitter):a)),`z${o}`),{emit:l,absorb:c}=t({i:o,t:a,z:i}),d=v(l,s);r=I(r,e.emissionGain?v(d,kt(e.emissionGain)):d),s=z(v(s,tr(er(c))),`transmit${o}`)}return{color:z(r,e.hint??\"marchAcc\"),transmittance:s}}function ML(e,t){let s,r;if(e.forEach((o,a)=>{let{value:i,weight:l}=t(o,a),c=v(i,l);s=s===void 0?c:I(s,c),r=r===void 0?l:I(r,l)}),!s||!r)throw new Error(\"std: layered([]) needs at least one layer\");return{weightedSum:s,totalWeight:r}}function EL(e){let t;for(let s of e){let r=s instanceof Object&&\"paint\"in s?s:{paint:s},o=r.paint;r.opacity!==void 0&&(o=v(o,r.opacity)),r.behind&&(o=v(o,r.behind)),t=t===void 0?o:r.blend===\"screen\"?ne(I(t,o),v(t,o)):I(t,o)}if(!t)throw new Error(\"std: layers([]) needs at least one layer\");return t}function kL(e,t=\"emissive\"){let s=z(e,t),r=z(Ge(it(it(s.member(\"x\"),s.member(\"y\")),s.member(\"z\")),0,1),`${t}A`);return Ot(Ce(s,it(r,1e-4)),r)}function BL(e,t){let{uv:s,viewport:r}=Ro(t);return W(gJ,\"rgbDither\",[e,s,r,wt(t)])}function Qn(e,t){return typeof e==\"number\"?$e(e):e instanceof zi||e.kind!==\"ctx\"?fn(e,t):t.ctx[e.name]}function zL(e,t,s){let r=me(e,s);return Ct(r.member(\"rgb\"),r.member(\"a\").mul(t))}function RL(e){return t=>{let{uv:s,viewport:r}=Ro(t),o=W(Coe,\"lineMask\",[s,r,me(e.from,t),me(e.to,t),Qn(e.width,t),Qn(e.style,t),Qn(e.dashLength,t),Qn(e.gapLength,t),Qn(e.capStart,t),Qn(e.capEnd,t)]);return zL(e.color,o,t)}}function DL(e){return t=>{let{uv:s,viewport:r}=Ro(t),o=W(Moe,\"sineWaveMask\",[s,r,Qn(e.angle,t),me(e.position,t),Qn(e.frequency,t),Qn(e.amplitude,t),Qn(e.thickness,t),Qn(e.softness,t),wt(t)]);return zL(e.color,o,t)}}function f2(e,t,s,r){let{ctx:o,propValues:a,uvContext:i,effectiveViewportSize:l}=e;return Fne({texture:s,uv:i??o.uv,viewport:l??o.viewportSize,fit:a[t.fit.name],decode:t.decode,...r})}function LL(e){return{gpu:{fragment:t=>{let s=gc(t,{label:e.label});return tB(t,{prop:e.src.name,async load(r,o){let a=await aae(r);if(o.isDisposed()){a.close();return}o.commit(),oA(r,a.naturalWidth,a.naturalHeight),s.ensureSize(a.width,a.height),s.write(a.source),a.close()},onError:e.onError}),f2(t,e,s.kit)}}}}function UL(e){return{gpu:{fragment:t=>{let s=sB(t,{source:{kind:\"url\",prop:e.src.name},loop:{prop:e.loop.name},metadataTimeoutMs:e.metadataTimeoutMs,onError:e.onError});return f2(t,e,t.registerExternalTexture(s.getSource))}}}}function OL(e){return{gpu:{fragment:t=>{let s=sB(t,{source:{kind:\"webcam\",constraints:e.constraints},naturalSizeKey:e.naturalSizeKey,autoplayRequired:!0,onError:e.onError});return f2(t,e,t.registerExternalTexture(s.getSource),{allowNone:!0,uvPostProcess:r=>W(Lne,\"applyMirror\",[r,t.uniforms[e.mirror.name]])})}}}}var{sdfSpaceUV:jP}=ip,GL=\"rgba16float\",Sg=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({center:m,lightPos:Fe(S,8),lightColor:Fe(S,8),lightCount:n,scale:n,rotation:n,aspect:n,reach:n,wrap:n,lightRange:n,shadowSoftness:n,eps:n,res:n,frame:n,rays:n,saRadius:n,saSides:n,saRounding:n,saInnerRatio:n,saRotation:n,saHeight:n,saOffset:n,saAperture:n,vfOriginX:n,vfOriginY:n,vfSpanX:n,vfSpanY:n,vfActiveRes:n,vfRBound:n}),\"IrradianceParams\"),Od={storageTexture:zt(GL,\"write-only\")},ph=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({params:{uniform:Sg},outTex:Od}),\"irradianceAnalyticLayout\"),Om=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({params:{uniform:Sg},outTex:Od,sdfSource:{texture:Zt(n)},samp:{sampler:\"filtering\"}}),\"irradianceSvgLayout\"),yu=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({params:{uniform:Sg},outTex:Od,silhouetteTex:{texture:Zt(n)},samp:{sampler:\"filtering\"}}),\"irradianceFieldLayout\"),nd=1e3,mh=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return B(ye(t,nd),__tsover_mul(nd,-1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"v\"}],body:[0,[[10,[6,\"std.max\",[[6,\"std.min\",[\"v\",\"FAR\"]],[1,\"FAR\",\"*\",[4,\"-\",[5,\"1\"]]]]]]]]},externals:{\"std.max\":()=>B,\"std.min\":()=>ye,FAR:()=>nd}})&&e.f)({})).$name(\"irradianceSane\"),KP=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=G(t,m(0,0),m(1,1));return he(t.sub(s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"c\",[6,\"std.clamp\",[\"uv\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],[6,\"d.vec2f\",[[5,\"1\"],[5,\"1\"]]]]]],[10,[6,\"std.length\",[[6,[7,\"uv\",\"sub\"],[\"c\"]]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m,\"std.length\":()=>he}})&&e.f)({})).$name(\"irradianceBeyondSquare\");function Mle(e){let t=P_(e);return w.fn([m],n)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=ph.$.params;return t(r,o.saRadius,o.saSides,o.saRounding,o.saInnerRatio,o.saRotation,o.saHeight,o.saOffset,o.saAperture).x}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"p\",[7,[7,\"irradianceAnalyticLayout\",\"$\"],\"params\"]],[10,[7,[6,\"sdfFn\",[\"uv\",[7,\"p\",\"saRadius\"],[7,\"p\",\"saSides\"],[7,\"p\",\"saRounding\"],[7,\"p\",\"saInnerRatio\"],[7,\"p\",\"saRotation\"],[7,\"p\",\"saHeight\"],[7,\"p\",\"saOffset\"],[7,\"p\",\"saAperture\"]]],\"x\"]]]]},externals:{irradianceAnalyticLayout:()=>ph,sdfFn:()=>t}})&&s.f)({})).$name(\"irradianceAnalyticField\")}var Ele=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Xl(Om.$.sdfSource,Om.$.samp,t,n(0)).x,r=KP(t);return mh(N(s,B(r,__tsover_sub(s,r)),r>0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"border\",[7,[6,\"std.textureSampleLevel\",[[7,[7,\"irradianceSvgLayout\",\"$\"],\"sdfSource\"],[7,[7,\"irradianceSvgLayout\",\"$\"],\"samp\"],\"uv\",[6,\"d.f32\",[[5,\"0\"]]]]],\"x\"]],[13,\"beyond\",[6,\"beyondSquare\",[\"uv\"]]],[10,[6,\"sane\",[[6,\"std.select\",[\"border\",[6,\"std.max\",[\"beyond\",[1,\"border\",\"-\",\"beyond\"]]],[1,\"beyond\",\">\",[5,\"0\"]]]]]]]]]},externals:{\"std.textureSampleLevel\":()=>Xl,irradianceSvgLayout:()=>Om,\"d.f32\":()=>n,beyondSquare:()=>KP,sane:()=>mh,\"std.select\":()=>N,\"std.max\":()=>B}})&&e.f)({})).$name(\"irradianceSvgField\"),kle=w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=yu.$.params,r=G(t.x,s.vfOriginX,__tsover_add(s.vfOriginX,s.vfSpanX)),o=G(t.y,s.vfOriginY,__tsover_add(s.vfOriginY,s.vfSpanY)),a=he(m(__tsover_sub(t.x,r),__tsover_sub(t.y,o))),i=__tsover_sub(he(m(__tsover_sub(t.x,.5),__tsover_sub(t.y,.5))),s.vfRBound),l=m(__tsover_div(__tsover_sub(r,s.vfOriginX),s.vfSpanX),__tsover_div(__tsover_sub(o,s.vfOriginY),s.vfSpanY)),c=Xl(yu.$.silhouetteTex,yu.$.samp,l,n(0)),d=__tsover_mul(c.z,c.w);return mh(N(d,B(B(a,i),__tsover_sub(d,a)),a>0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"}],body:[0,[[13,\"p\",[7,[7,\"irradianceFieldLayout\",\"$\"],\"params\"]],[13,\"cx\",[6,\"std.clamp\",[[7,\"uv\",\"x\"],[7,\"p\",\"vfOriginX\"],[1,[7,\"p\",\"vfOriginX\"],\"+\",[7,\"p\",\"vfSpanX\"]]]]],[13,\"cy\",[6,\"std.clamp\",[[7,\"uv\",\"y\"],[7,\"p\",\"vfOriginY\"],[1,[7,\"p\",\"vfOriginY\"],\"+\",[7,\"p\",\"vfSpanY\"]]]]],[13,\"outside\",[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",\"cx\"],[1,[7,\"uv\",\"y\"],\"-\",\"cy\"]]]]]],[13,\"circle\",[1,[6,\"std.length\",[[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"uv\",\"y\"],\"-\",[5,\"0.5\"]]]]]],\"-\",[7,\"p\",\"vfRBound\"]]],[13,\"tuv\",[6,\"d.vec2f\",[[1,[1,\"cx\",\"-\",[7,\"p\",\"vfOriginX\"]],\"/\",[7,\"p\",\"vfSpanX\"]],[1,[1,\"cy\",\"-\",[7,\"p\",\"vfOriginY\"]],\"/\",[7,\"p\",\"vfSpanY\"]]]]],[13,\"sil\",[6,\"std.textureSampleLevel\",[[7,[7,\"irradianceFieldLayout\",\"$\"],\"silhouetteTex\"],[7,[7,\"irradianceFieldLayout\",\"$\"],\"samp\"],\"tuv\",[6,\"d.f32\",[[5,\"0\"]]]]]],[13,\"inDomain\",[1,[7,\"sil\",\"z\"],\"*\",[7,\"sil\",\"w\"]]],[13,\"beyond\",[6,\"std.max\",[[6,\"std.max\",[\"outside\",\"circle\"]],[1,\"inDomain\",\"-\",\"outside\"]]]],[10,[6,\"sane\",[[6,\"std.select\",[\"inDomain\",\"beyond\",[1,\"outside\",\">\",[5,\"0\"]]]]]]]]]},externals:{irradianceFieldLayout:()=>yu,\"std.clamp\":()=>G,\"std.length\":()=>he,\"d.vec2f\":()=>m,\"std.textureSampleLevel\":()=>Xl,\"d.f32\":()=>n,\"std.max\":()=>B,sane:()=>mh,\"std.select\":()=>N}})&&e.f)({})).$name(\"irradianceVolumetricField\"),v2=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({vfOriginX:n,vfOriginY:n,vfSpanX:n,vfSpanY:n,vfActiveRes:n,vfRBound:n,res:n,step:n}),\"SilhouetteParams\"),pd=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({params:{uniform:v2},fieldTex:{texture:Zt(n),sampleType:\"unfilterable-float\"},out:Od}),\"silhouetteSeedLayout\"),Qc=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({params:{uniform:v2},src:{texture:Zt(n)},out:Od}),\"silhouetteStepLayout\"),{fieldSampleFastArg:QP}=bA(),du=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=pd.$.params,o=QP(m(__tsover_add(r.vfOriginX,__tsover_mul(__tsover_div(__tsover_add(t,.5),r.res),r.vfSpanX)),__tsover_add(r.vfOriginY,__tsover_mul(__tsover_div(__tsover_add(s,.5),r.res),r.vfSpanY))),pd.$.fieldTex,r.vfOriginX,r.vfOriginY,r.vfSpanX,r.vfSpanY,r.vfActiveRes,r.vfRBound).x;return N(n(1),n(-1),o<0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"tx\"},{type:\"i\",name:\"ty\"}],body:[0,[[13,\"p\",[7,[7,\"silhouetteSeedLayout\",\"$\"],\"params\"]],[13,\"uv\",[6,\"d.vec2f\",[[1,[7,\"p\",\"vfOriginX\"],\"+\",[1,[1,[1,\"tx\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanX\"]]],[1,[7,\"p\",\"vfOriginY\"],\"+\",[1,[1,[1,\"ty\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanY\"]]]]]],[13,\"v\",[7,[6,\"fieldSampleFastArg\",[\"uv\",[7,[7,\"silhouetteSeedLayout\",\"$\"],\"fieldTex\"],[7,\"p\",\"vfOriginX\"],[7,\"p\",\"vfOriginY\"],[7,\"p\",\"vfSpanX\"],[7,\"p\",\"vfSpanY\"],[7,\"p\",\"vfActiveRes\"],[7,\"p\",\"vfRBound\"]]],\"x\"]],[10,[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[1,\"v\",\"<\",[5,\"0\"]]]]]]]},externals:{silhouetteSeedLayout:()=>pd,\"d.vec2f\":()=>m,fieldSampleFastArg:()=>QP,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})).$name(\"silhouetteInside\"),Ble=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=pd.$.params,o=n(t),a=n(s),i=du(o,a),l=__tsover_sub(r.res,1),c=du(B(__tsover_sub(o,1),0),a),d=du(ye(__tsover_add(o,1),l),a),u=du(o,B(__tsover_sub(a,1),0)),p=du(o,ye(__tsover_add(a,1),l)),_=c!==i||d!==i||u!==i||p!==i,f=__tsover_add(r.vfOriginX,__tsover_mul(__tsover_div(__tsover_add(o,.5),r.res),r.vfSpanX)),h=__tsover_add(r.vfOriginY,__tsover_mul(__tsover_div(__tsover_add(a,.5),r.res),r.vfSpanY)),g=N(S(-1,-1,6e4,i),S(f,h,0,i),_);lt(pd.$.out,fe(t,s),g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"silhouetteSeedLayout\",\"$\"],\"params\"]],[13,\"fx\",[6,\"d.f32\",[\"cx\"]]],[13,\"fy\",[6,\"d.f32\",[\"cy\"]]],[13,\"s0\",[6,\"silhouetteInside\",[\"fx\",\"fy\"]]],[13,\"maxT\",[1,[7,\"p\",\"res\"],\"-\",[5,\"1\"]]],[13,\"sL\",[6,\"silhouetteInside\",[[6,\"std.max\",[[1,\"fx\",\"-\",[5,\"1\"]],[5,\"0\"]]],\"fy\"]]],[13,\"sR\",[6,\"silhouetteInside\",[[6,\"std.min\",[[1,\"fx\",\"+\",[5,\"1\"]],\"maxT\"]],\"fy\"]]],[13,\"sD\",[6,\"silhouetteInside\",[\"fx\",[6,\"std.max\",[[1,\"fy\",\"-\",[5,\"1\"]],[5,\"0\"]]]]]],[13,\"sU\",[6,\"silhouetteInside\",[\"fx\",[6,\"std.min\",[[1,\"fy\",\"+\",[5,\"1\"]],\"maxT\"]]]]],[13,\"boundary\",[3,[3,[3,[1,\"sL\",\"!==\",\"s0\"],\"||\",[1,\"sR\",\"!==\",\"s0\"]],\"||\",[1,\"sD\",\"!==\",\"s0\"]],\"||\",[1,\"sU\",\"!==\",\"s0\"]]],[13,\"sx\",[1,[7,\"p\",\"vfOriginX\"],\"+\",[1,[1,[1,\"fx\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanX\"]]]],[13,\"sy\",[1,[7,\"p\",\"vfOriginY\"],\"+\",[1,[1,[1,\"fy\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanY\"]]]],[13,\"seed\",[6,\"std.select\",[[6,\"d.vec4f\",[[4,\"-\",[5,\"1\"]],[4,\"-\",[5,\"1\"]],[5,\"60000\"],\"s0\"]],[6,\"d.vec4f\",[\"sx\",\"sy\",[5,\"0\"],\"s0\"]],\"boundary\"]]],[6,\"std.textureStore\",[[7,[7,\"silhouetteSeedLayout\",\"$\"],\"out\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],\"seed\"]]]]},externals:{silhouetteSeedLayout:()=>pd,\"d.f32\":()=>n,silhouetteInside:()=>du,\"std.max\":()=>B,\"std.min\":()=>ye,\"std.select\":()=>N,\"d.vec4f\":()=>S,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe}})&&e.f)({})).$name(\"silhouetteSeed\"),ZP=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=Qc.$.params,o=n(t),a=n(s),i=m(__tsover_add(r.vfOriginX,__tsover_mul(__tsover_div(__tsover_add(o,.5),r.res),r.vfSpanX)),__tsover_add(r.vfOriginY,__tsover_mul(__tsover_div(__tsover_add(a,.5),r.res),r.vfSpanY))),l=Ye(Qc.$.src,fe(t,s),0),c=S(l),d=__tsover_sub(r.res,1);for(let u=-1;u<=1;u++)for(let p=-1;p<=1;p++){let _=G(__tsover_add(o,__tsover_mul(n(p),r.step)),0,d),f=G(__tsover_add(a,__tsover_mul(n(u),r.step)),0,d),h=Ye(Qc.$.src,fe(M(_),M(f)),0);if(h.x>=0){let g=he(h.xy.sub(i));g<c.z&&(c=S(h.x,h.y,g,c.w))}}lt(Qc.$.out,fe(t,s),S(c.x,c.y,c.z,l.w))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"silhouetteStepLayout\",\"$\"],\"params\"]],[13,\"tx\",[6,\"d.f32\",[\"cx\"]]],[13,\"ty\",[6,\"d.f32\",[\"cy\"]]],[13,\"here\",[6,\"d.vec2f\",[[1,[7,\"p\",\"vfOriginX\"],\"+\",[1,[1,[1,\"tx\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanX\"]]],[1,[7,\"p\",\"vfOriginY\"],\"+\",[1,[1,[1,\"ty\",\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],\"*\",[7,\"p\",\"vfSpanY\"]]]]]],[13,\"self\",[6,\"std.textureLoad\",[[7,[7,\"silhouetteStepLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[5,\"0\"]]]],[12,\"best\",[6,\"d.vec4f\",[\"self\"]]],[13,\"maxT\",[1,[7,\"p\",\"res\"],\"-\",[5,\"1\"]]],[14,[12,\"j\",[4,\"-\",[5,\"1\"]]],[1,\"j\",\"<=\",[5,\"1\"]],[102,\"++\",\"j\"],[0,[[14,[12,\"i\",[4,\"-\",[5,\"1\"]]],[1,\"i\",\"<=\",[5,\"1\"]],[102,\"++\",\"i\"],[0,[[13,\"nx\",[6,\"std.clamp\",[[1,\"tx\",\"+\",[1,[6,\"d.f32\",[\"i\"]],\"*\",[7,\"p\",\"step\"]]],[5,\"0\"],\"maxT\"]]],[13,\"ny\",[6,\"std.clamp\",[[1,\"ty\",\"+\",[1,[6,\"d.f32\",[\"j\"]],\"*\",[7,\"p\",\"step\"]]],[5,\"0\"],\"maxT\"]]],[13,\"n\",[6,\"std.textureLoad\",[[7,[7,\"silhouetteStepLayout\",\"$\"],\"src\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"nx\"]],[6,\"d.u32\",[\"ny\"]]]],[5,\"0\"]]]],[11,[1,[7,\"n\",\"x\"],\">=\",[5,\"0\"]],[0,[[13,\"dist\",[6,\"std.length\",[[6,[7,[7,\"n\",\"xy\"],\"sub\"],[\"here\"]]]]],[11,[1,\"dist\",\"<\",[7,\"best\",\"z\"]],[0,[[2,\"best\",\"=\",[6,\"d.vec4f\",[[7,\"n\",\"x\"],[7,\"n\",\"y\"],\"dist\",[7,\"best\",\"w\"]]]]]]]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"silhouetteStepLayout\",\"$\"],\"out\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"best\",\"x\"],[7,\"best\",\"y\"],[7,\"best\",\"z\"],[7,\"self\",\"w\"]]]]]]]},externals:{silhouetteStepLayout:()=>Qc,\"d.f32\":()=>n,\"d.vec2f\":()=>m,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S,\"std.clamp\":()=>G,\"d.u32\":()=>M,\"std.length\":()=>he,\"std.textureStore\":()=>lt}})&&e.f)({})).$name(\"silhouetteStep\");function Q0(e,t,s){let{steps:r,shadowSteps:o,namePrefix:a}=s,i=3,l=o>0,c=Math.max(o,1),d=e,u=w.fn([m,m,n,n],n)((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f,h,g)=>{\"use gpu\";let y=f.sub(_),b=he(y),x=y.div(B(b,1e-4)),P=n(1),C=__tsover_mul(h,2);for(let E=0;E<c&&!(C>=b);E++){let R=t(_.add(x.mul(C)));if(P=ye(P,G(__tsover_div(R,B(__tsover_mul(C,g),h)),0,1)),P<=0)break;C=__tsover_add(C,B(R,h))}return P}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"from\"},{type:\"i\",name:\"toward\"},{type:\"i\",name:\"eps\"},{type:\"i\",name:\"soft\"}],body:[0,[[13,\"seg\",[6,[7,\"toward\",\"sub\"],[\"from\"]]],[13,\"len\",[6,\"std.length\",[\"seg\"]]],[13,\"dir\",[6,[7,\"seg\",\"div\"],[[6,\"std.max\",[\"len\",[5,\"0.0001\"]]]]]],[12,\"vis\",[6,\"d.f32\",[[5,\"1\"]]]],[12,\"t\",[1,\"eps\",\"*\",[5,\"2\"]]],[14,[12,\"k\",[5,\"0\"]],[1,\"k\",\"<\",\"shadowLoop\"],[102,\"++\",\"k\"],[0,[[11,[1,\"t\",\">=\",\"len\"],[0,[[17]]]],[13,\"dd\",[6,\"fieldAt\",[[6,[7,\"from\",\"add\"],[[6,[7,\"dir\",\"mul\"],[\"t\"]]]]]]],[2,\"vis\",\"=\",[6,\"std.min\",[\"vis\",[6,\"std.clamp\",[[1,\"dd\",\"/\",[6,\"std.max\",[[1,\"t\",\"*\",\"soft\"],\"eps\"]]],[5,\"0\"],[5,\"1\"]]]]]],[11,[1,\"vis\",\"<=\",[5,\"0\"]],[0,[[17]]]],[2,\"t\",\"+=\",[6,\"std.max\",[\"dd\",\"eps\"]]]]]],[10,\"vis\"]]]},externals:{\"std.length\":()=>he,\"std.max\":()=>B,\"d.f32\":()=>n,shadowLoop:()=>c,fieldAt:()=>t,\"std.min\":()=>ye,\"std.clamp\":()=>G}})&&p.f)({})).$name(`${a}Visibility`);return w.fn([M,M])((p=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(p.f=((_,f)=>{\"use gpu\";let h=d.$.params,g=h.eps,y=m(__tsover_div(__tsover_add(n(_),.5),h.res),__tsover_div(__tsover_add(n(f),.5),h.res)),b=jP(h.center,h.scale,h.rotation,y,h.aspect),x=t(b);if(x<__tsover_mul(g,-1)){lt(d.$.outTex,fe(_,f),S(0,0,0,0));return}let P=m(b);if(x<__tsover_mul(g,2)){let V=__tsover_sub(t(b.add(m(g,0))),x),X=__tsover_sub(t(b.add(m(0,g))),x),Q=vs(m(V,__tsover_add(X,1e-6)));P=b.add(Q.mul(__tsover_sub(__tsover_mul(g,2),x)))}let C=B(t(P),g),E=__tsover_add(C,i),R=Re(__tsover_mul(52.9829189,Re(__tsover_add(__tsover_mul(n(_),.06711056),__tsover_mul(n(f),.00583715))))),U=Re(__tsover_add(R,__tsover_mul(h.frame,.6180339887))),L=j(h.rays),k=__tsover_div(Jt,h.rays),D=j(h.lightCount),O=A(0,0,0);for(let V=0;V<L;V++){let X=__tsover_mul(__tsover_add(n(V),U),k),Q=m(pe(X),ae(X)),te=C,J=n(1);for(let oe=0;oe<r;oe++){let le=t(P.add(Q.mul(te)));if(le<g){J=le;break}if(te=__tsover_add(te,B(le,g)),te>E)break}if(J<g){let oe=P.add(Q.mul(te)),le=__tsover_sub(t(oe.add(m(g,0))),J),se=__tsover_sub(t(oe.add(m(0,g))),J),Te=vs(m(le,__tsover_add(se,1e-6))),de=oe.sub(Te.mul(B(J,0))),xe=de.add(Te.mul(__tsover_mul(g,2)));for(let Ae=0;Ae<D;Ae++){let De=h.lightPos[Ae],Ee=h.lightColor[Ae],ze=De.xy,Ie=ze.sub(de),ke=he(Ie),Me=Ie.div(B(ke,1e-4)),we=G(__tsover_div(__tsover_add(Se(Te,Me),h.wrap),__tsover_add(1,h.wrap)),0,1);if(we>0){let Ve=__tsover_div(ke,h.lightRange),qe=__tsover_div(we,__tsover_add(1,__tsover_mul(Ve,Ve)));l&&(qe=__tsover_mul(qe,u(xe,ze,g,h.shadowSoftness))),O=O.add(Ee.xyz.mul(__tsover_mul(qe,Ee.w)))}}}}let F=__tsover_sub(1,be(__tsover_mul(h.reach,.25),h.reach,B(x,0))),q=G(__tsover_add(__tsover_div(x,g),1),0,1),Y=ye(B(O.div(h.rays),A(0,0,0)),A(nd,nd,nd));lt(d.$.outTex,fe(_,f),S(Y.mul(__tsover_mul(F,q)),q))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"lay\",\"$\"],\"params\"]],[13,\"eps\",[7,\"p\",\"eps\"]],[13,\"uv\",[6,\"d.vec2f\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]],[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"p\",\"res\"]]]]],[13,\"sdfUV\",[6,\"sdfSpaceUV\",[[7,\"p\",\"center\"],[7,\"p\",\"scale\"],[7,\"p\",\"rotation\"],\"uv\",[7,\"p\",\"aspect\"]]]],[13,\"d0\",[6,\"fieldAt\",[\"sdfUV\"]]],[11,[1,\"d0\",\"<\",[1,\"eps\",\"*\",[4,\"-\",[5,\"1\"]]]],[0,[[6,\"std.textureStore\",[[7,[7,\"lay\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]]]],[10]]]],[12,\"o\",[6,\"d.vec2f\",[\"sdfUV\"]]],[11,[1,\"d0\",\"<\",[1,\"eps\",\"*\",[5,\"2\"]]],[0,[[13,\"gx\",[1,[6,\"fieldAt\",[[6,[7,\"sdfUV\",\"add\"],[[6,\"d.vec2f\",[\"eps\",[5,\"0\"]]]]]]],\"-\",\"d0\"]],[13,\"gy\",[1,[6,\"fieldAt\",[[6,[7,\"sdfUV\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],\"eps\"]]]]]],\"-\",\"d0\"]],[13,\"g\",[6,\"std.normalize\",[[6,\"d.vec2f\",[\"gx\",[1,\"gy\",\"+\",[5,\"0.000001\"]]]]]]],[2,\"o\",\"=\",[6,[7,\"sdfUV\",\"add\"],[[6,[7,\"g\",\"mul\"],[[1,[1,\"eps\",\"*\",[5,\"2\"]],\"-\",\"d0\"]]]]]]]]],[13,\"start\",[6,\"std.max\",[[6,\"fieldAt\",[\"o\"]],\"eps\"]]],[13,\"tMax\",[1,\"start\",\"+\",\"RAY_SPAN\"]],[13,\"ign\",[6,\"std.fract\",[[1,[5,\"52.9829189\"],\"*\",[6,\"std.fract\",[[1,[1,[6,\"d.f32\",[\"cx\"]],\"*\",[5,\"0.06711056\"]],\"+\",[1,[6,\"d.f32\",[\"cy\"]],\"*\",[5,\"0.00583715\"]]]]]]]]],[13,\"u\",[6,\"std.fract\",[[1,\"ign\",\"+\",[1,[7,\"p\",\"frame\"],\"*\",[5,\"0.6180339887\"]]]]]],[13,\"rays\",[6,\"d.i32\",[[7,\"p\",\"rays\"]]]],[13,\"rayStep\",[1,\"TAU\",\"/\",[7,\"p\",\"rays\"]]],[13,\"lightCount\",[6,\"d.i32\",[[7,\"p\",\"lightCount\"]]]],[12,\"acc\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"rays\"],[102,\"++\",\"i\"],[0,[[13,\"ang\",[1,[1,[6,\"d.f32\",[\"i\"]],\"+\",\"u\"],\"*\",\"rayStep\"]],[13,\"dir\",[6,\"d.vec2f\",[[6,\"std.cos\",[\"ang\"]],[6,\"std.sin\",[\"ang\"]]]]],[12,\"t\",\"start\"],[12,\"hitD\",[6,\"d.f32\",[[5,\"1\"]]]],[14,[12,\"s\",[5,\"0\"]],[1,\"s\",\"<\",\"steps\"],[102,\"++\",\"s\"],[0,[[13,\"dd\",[6,\"fieldAt\",[[6,[7,\"o\",\"add\"],[[6,[7,\"dir\",\"mul\"],[\"t\"]]]]]]],[11,[1,\"dd\",\"<\",\"eps\"],[0,[[2,\"hitD\",\"=\",\"dd\"],[17]]]],[2,\"t\",\"+=\",[6,\"std.max\",[\"dd\",\"eps\"]]],[11,[1,\"t\",\">\",\"tMax\"],[0,[[17]]]]]]],[11,[1,\"hitD\",\"<\",\"eps\"],[0,[[13,\"pos\",[6,[7,\"o\",\"add\"],[[6,[7,\"dir\",\"mul\"],[\"t\"]]]]],[13,\"nx\",[1,[6,\"fieldAt\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec2f\",[\"eps\",[5,\"0\"]]]]]]],\"-\",\"hitD\"]],[13,\"ny\",[1,[6,\"fieldAt\",[[6,[7,\"pos\",\"add\"],[[6,\"d.vec2f\",[[5,\"0\"],\"eps\"]]]]]],\"-\",\"hitD\"]],[13,\"n\",[6,\"std.normalize\",[[6,\"d.vec2f\",[\"nx\",[1,\"ny\",\"+\",[5,\"0.000001\"]]]]]]],[13,\"hitP\",[6,[7,\"pos\",\"sub\"],[[6,[7,\"n\",\"mul\"],[[6,\"std.max\",[\"hitD\",[5,\"0\"]]]]]]]],[13,\"shadowFrom\",[6,[7,\"hitP\",\"add\"],[[6,[7,\"n\",\"mul\"],[[1,\"eps\",\"*\",[5,\"2\"]]]]]]],[14,[12,\"l\",[5,\"0\"]],[1,\"l\",\"<\",\"lightCount\"],[102,\"++\",\"l\"],[0,[[13,\"lp\",[8,[7,\"p\",\"lightPos\"],\"l\"]],[13,\"lc\",[8,[7,\"p\",\"lightColor\"],\"l\"]],[13,\"lightUV\",[7,\"lp\",\"xy\"]],[13,\"toL\",[6,[7,\"lightUV\",\"sub\"],[\"hitP\"]]],[13,\"dist\",[6,\"std.length\",[\"toL\"]]],[13,\"dirL\",[6,[7,\"toL\",\"div\"],[[6,\"std.max\",[\"dist\",[5,\"0.0001\"]]]]]],[13,\"facing\",[6,\"std.clamp\",[[1,[1,[6,\"std.dot\",[\"n\",\"dirL\"]],\"+\",[7,\"p\",\"wrap\"]],\"/\",[1,[5,\"1\"],\"+\",[7,\"p\",\"wrap\"]]],[5,\"0\"],[5,\"1\"]]]],[11,[1,\"facing\",\">\",[5,\"0\"]],[0,[[13,\"r\",[1,\"dist\",\"/\",[7,\"p\",\"lightRange\"]]],[12,\"struck\",[1,\"facing\",\"/\",[1,[5,\"1\"],\"+\",[1,\"r\",\"*\",\"r\"]]]],[11,\"shadows\",[0,[[2,\"struck\",\"=\",[1,\"struck\",\"*\",[6,\"visibility\",[\"shadowFrom\",\"lightUV\",\"eps\",[7,\"p\",\"shadowSoftness\"]]]]]]]],[2,\"acc\",\"=\",[6,[7,\"acc\",\"add\"],[[6,[7,[7,\"lc\",\"xyz\"],\"mul\"],[[1,\"struck\",\"*\",[7,\"lc\",\"w\"]]]]]]]]]]]]]]]]]]],[13,\"fade\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,[7,\"p\",\"reach\"],\"*\",[5,\"0.25\"]],[7,\"p\",\"reach\"],[6,\"std.max\",[\"d0\",[5,\"0\"]]]]]]],[13,\"m\",[6,\"std.clamp\",[[1,[1,\"d0\",\"/\",\"eps\"],\"+\",[5,\"1\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"irr\",[6,\"std.min\",[[6,\"std.max\",[[6,[7,\"acc\",\"div\"],[[7,\"p\",\"rays\"]]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]]],[6,\"d.vec3f\",[\"FAR\",\"FAR\",\"FAR\"]]]]],[6,\"std.textureStore\",[[7,[7,\"lay\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[6,[7,\"irr\",\"mul\"],[[1,\"fade\",\"*\",\"m\"]]],\"m\"]]]]]]},externals:{lay:()=>d,\"d.vec2f\":()=>m,\"d.f32\":()=>n,sdfSpaceUV:()=>jP,fieldAt:()=>t,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S,\"std.normalize\":()=>vs,\"std.max\":()=>B,RAY_SPAN:()=>i,\"std.fract\":()=>Re,\"d.i32\":()=>j,TAU:()=>Jt,\"d.vec3f\":()=>A,\"std.cos\":()=>pe,\"std.sin\":()=>ae,steps:()=>r,\"std.length\":()=>he,\"std.clamp\":()=>G,\"std.dot\":()=>Se,shadows:()=>l,visibility:()=>u,\"std.smoothstep\":()=>be,\"std.min\":()=>ye,FAR:()=>nd}})&&p.f)({})).$name(`${a}Gather`)}function Yl(e,t,s){return wr(e,(r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a)=>{\"use gpu\";t(o,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>t}})&&r.f)({}),{size:[s.res,s.res],bindGroup:s.bindGroup})}var FL=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({weight:n}),\"AccumulateParams\"),_u=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({sample:{texture:Zt(n)},prev:{texture:Zt(n)},out:Od,params:{uniform:FL}}),\"accumulateLayout\"),zle=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=fe(t,s),o=Ye(_u.$.sample,r,0),a=Ye(_u.$.prev,r,0),i=_u.$.params.weight;lt(_u.$.out,r,N(a.add(o.sub(a).mul(i)),o,i>=1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"at\",[6,\"d.vec2u\",[\"cx\",\"cy\"]]],[13,\"s\",[6,\"std.textureLoad\",[[7,[7,\"accumulateLayout\",\"$\"],\"sample\"],\"at\",[5,\"0\"]]]],[13,\"prev\",[6,\"std.textureLoad\",[[7,[7,\"accumulateLayout\",\"$\"],\"prev\"],\"at\",[5,\"0\"]]]],[13,\"w\",[7,[7,[7,\"accumulateLayout\",\"$\"],\"params\"],\"weight\"]],[6,\"std.textureStore\",[[7,[7,\"accumulateLayout\",\"$\"],\"out\"],\"at\",[6,\"std.select\",[[6,[7,\"prev\",\"add\"],[[6,[7,[6,[7,\"s\",\"sub\"],[\"prev\"]],\"mul\"],[\"w\"]]]],\"s\",[1,\"w\",\">=\",[5,\"1\"]]]]]]]]},externals:{\"d.vec2u\":()=>fe,\"std.textureLoad\":()=>Ye,accumulateLayout:()=>_u,\"std.textureStore\":()=>lt,\"std.select\":()=>N}})&&e.f)({})).$name(\"irradianceAccumulate\"),I1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({src:{texture:Zt(n)},out:Od}),\"copyLayout\"),Rle=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=fe(t,s);lt(I1.$.out,r,Ye(I1.$.src,r,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"at\",[6,\"d.vec2u\",[\"cx\",\"cy\"]]],[6,\"std.textureStore\",[[7,[7,\"copyLayout\",\"$\"],\"out\"],\"at\",[6,\"std.textureLoad\",[[7,[7,\"copyLayout\",\"$\"],\"src\"],\"at\",[5,\"0\"]]]]]]]},externals:{\"d.vec2u\":()=>fe,\"std.textureStore\":()=>lt,copyLayout:()=>I1,\"std.textureLoad\":()=>Ye}})&&e.f)({})).$name(\"irradianceCopy\");var Dle=0,Lle=e=>new fs(()=>e),JP=(e,t)=>typeof e==\"number\"?fo(e):e._emit(t);function Ule(e,t){return{external:(s,r)=>e.external(s,r),statement:s=>t.push(s),freshLocal:s=>e.freshLocal(s),memo:(s,r)=>e.memo(s,r)}}function IL(e){let t=Dle++,s=e.hint??\"visibility\";return new fs(r=>r.memo(`shadow:${t}`,()=>{let o=r.freshLocal(s),a=r.freshLocal(\"shFrom\"),i=r.freshLocal(\"shSeg\"),l=r.freshLocal(\"shLen\"),c=r.freshLocal(\"shDir\"),d=r.freshLocal(\"shEps\"),u=r.freshLocal(\"shSoft\"),p=r.freshLocal(\"shT\"),_=r.freshLocal(\"shPos\"),f=r.freshLocal(\"shD\"),h=r.freshLocal(\"shStep\");r.statement(`let ${a} = ${e.from._emit(r)};`),r.statement(`let ${i} = ${e.toward._emit(r)} - ${a};`),r.statement(`let ${l} = length(${i});`),r.statement(`let ${c} = ${i} / max(${l}, 0.0001);`),r.statement(`let ${d} = ${JP(e.surfaceEps,r)};`),r.statement(`let ${u} = ${JP(e.softness??0,r)};`),r.statement(`var ${o} = 1.0;`);let g=[],y=e.field(Lle(_))._emit(Ule(r,g));return r.statement([\"{\",`  var ${p} = ${d} * 2.0;`,`  for (var ${h} = 0u; ${h} < ${e.steps}u; ${h}++) {`,`    if (${p} >= ${l}) { break; }`,`    let ${_} = ${a} + ${c} * ${p};`,...g.map(b=>`    ${b}`),`    let ${f} = ${y};`,`    ${o} = min(${o}, clamp(${f} / max(${p} * ${u}, ${d}), 0.0, 1.0));`,`    if (${o} <= 0.0) { break; }`,`    ${p} += max(${f}, ${d});`,\"  }\",\"}\"].join(`\n`)),o}))}var Ole=3,Gle=6,Fle=3,{createSdfDataTexture:Ile,analyticSubPropValues:Yle,parseShapeConfigValue:Wle}=yc,{createVolumetricFieldComputeNode:qle}=kn;function Vle(e,t,s,r,o){let a=(o.x-e.x)*r,i=1-o.y-(1-e.y),l=s*Math.PI/180,c=Math.cos(l),d=Math.sin(l),u=Math.max(t,.001);return{x:(a*c+i*d)/u+.5,y:(i*c-a*d)/u+.5}}function Nle(e,t,s){let r=(Array.isArray(e)?e:[]).slice(0,8),o=[],a=[];for(let i=0;i<8;i++){let l=r[i],c=l?.[t.position]??{x:.5,y:.5},d=Vle(s.center,s.scale,s.rotation,s.aspect,c),u=l?.[t.color]??[1,1,1,1],p=typeof l?.[t.intensity]==\"number\"?l[t.intensity]:0;o.push(l?[d.x,d.y,0,0]:[0,0,0,0]),a.push(l?[u[0],u[1],u[2],p]:[0,0,0,0])}return{pos:o,color:a,count:r.length}}function h2(e){let t=e.outputKey??\"irradianceTexture\",s=e.namePrefix??\"irradiance\",r={steps:e.steps??32,shadowSteps:e.shadowSteps??12,namePrefix:s},o=e.resolution;return{compute:l=>{let{gpu:c,getCpuValue:d,registerComputeTexture:u,onCleanup:p,onResize:_}=l,f=c?.root;if(!f)return null;let h=d(\"shapeSdfUrl\")||\"\",g=bc(d(\"shapeType\")||\"\",d(\"shape\")),y=qle(l,()=>d(\"shape\"),\"none\",\"span\"),b=()=>{let de=f.createTexture({size:[o,o],format:GL}).$usage(\"storage\",\"sampled\");return p(()=>de.destroy()),de},x=b(),P=b(),C=b(),E=Vh(f,P,o,o,p,Gle,o,o);E.updateRadius(Ole);let R=u(E.outputTexture),U=f.createUniform(Sg),L=f.createUniform(FL),k=Yl(f,zle,{res:o,bindGroup:f.createBindGroup(_u,{sample:x,prev:C,out:P,params:L.buffer})}),D=Yl(f,Rle,{res:o,bindGroup:f.createBindGroup(I1,{src:P,out:C})}),O,F=()=>0,q=null;if(y){let de=b(),xe=b(),Ae=f.createUniform(v2),De=Yl(f,Ble,{res:o,bindGroup:f.createBindGroup(pd,{params:Ae.buffer,fieldTex:y.fieldTexture,out:de})}),Ee=Yl(f,ZP,{res:o,bindGroup:f.createBindGroup(Qc,{params:Ae.buffer,src:de,out:xe})}),ze=Yl(f,ZP,{res:o,bindGroup:f.createBindGroup(Qc,{params:Ae.buffer,src:xe,out:de})}),Ie=[];for(let we=Math.ceil(o/2);we>=1&&(Ie.push(we),we!==1);we=Math.floor(we/2));let ke=Ie.length%2===1?xe:de;q={steps:we=>{let Ve=[],qe={vfOriginX:we.originX,vfOriginY:we.originY,vfSpanX:we.spanX,vfSpanY:we.spanY,vfActiveRes:we.activeRes,vfRBound:we.rBound,res:o};return Ve.push(()=>Ae.write({...qe,step:0}),De),Ie.forEach((st,pt)=>{Ve.push(()=>Ae.write({...qe,step:st}),pt%2===0?Ee:ze)}),Ve}};let Me=f.createSampler({magFilter:\"linear\",minFilter:\"linear\",addressModeU:\"clamp-to-edge\",addressModeV:\"clamp-to-edge\"});O=Yl(f,Q0(yu,kle,r),{res:o,bindGroup:f.createBindGroup(yu,{params:U.buffer,outTex:x,silhouetteTex:ke,samp:Me})})}else if(h){let de=Ile(l,h);F=()=>de.getVersion();let xe=f.createSampler({magFilter:\"linear\",minFilter:\"linear\",addressModeU:\"clamp-to-edge\",addressModeV:\"clamp-to-edge\"});O=Yl(f,Q0(Om,Ele,r),{res:o,bindGroup:f.createBindGroup(Om,{params:U.buffer,outTex:x,sdfSource:de.texture.texture,samp:xe})})}else O=Yl(f,Q0(ph,Mle(g),r),{res:o,bindGroup:f.createBindGroup(ph,{params:U.buffer,outTex:x})});let Y=Math.max(1,l.dimensions.width),V=Math.max(1,l.dimensions.height);_(({width:de,height:xe})=>{Y=Math.max(1,de),V=Math.max(1,xe)});let X=(de,xe)=>{let Ae=d(de);return typeof Ae==\"number\"?Ae:xe},Q=de=>{let xe=d(de);return{x:typeof xe?.x==\"number\"?xe.x:.5,y:typeof xe?.y==\"number\"?xe.y:.5}},te=\"\",J=!1,oe=0,le=Fle,se=0,Te=!1;return{outputs:{[t]:R,...y?.outputs??{}},getComputeNodes:de=>{let xe=y?.getComputeNodes(de)??null,Ae=[];if(q&&(xe||!J)){let yt=y.getFieldDomain();Ae=q.steps(yt),J=!0}let De=Q(\"center\"),Ee=X(\"scale\",1),ze=X(\"rotation\",0),Ie=Nle(d(e.lights.prop),e.lights,{center:De,scale:Ee,rotation:ze,aspect:Y/V}),ke=Yle(Wle(d(\"shape\"))),Me=y?.getFieldDomain(),we={center:[De.x,De.y],lightPos:Ie.pos,lightColor:Ie.color,lightCount:Ie.count,scale:Ee,rotation:ze,aspect:Y/V,reach:X(e.reach,3),wrap:X(e.wrap,0),lightRange:X(e.lightRange,1),shadowSoftness:X(e.shadowSoftness,0),eps:1/o/Math.max(Ee,.001),res:o,saRadius:ke.radius,saSides:ke.sides,saRounding:ke.rounding,saInnerRatio:ke.innerRatio,saRotation:ke.rotation,saHeight:ke.height,saOffset:ke.offset,saAperture:ke.aperture,vfOriginX:Me?.originX??0,vfOriginY:Me?.originY??0,vfSpanX:Me?.spanX??1,vfSpanY:Me?.spanY??1,vfActiveRes:Me?.activeRes??1,vfRBound:Me?.rBound??.6,fieldVersion:F()},Ve=JSON.stringify(we),qe=le>0;qe&&le--;let st=Ve!==te||!!xe||Ae.length>0||qe,pt;if(st)te=Ve,oe=0,se=0,pt=Te?e.rays.motion:e.rays.burst;else{if(oe>e.rays.refineFrames)return Te=!1,null;pt=e.rays.refine}Te=st;let Ze=oe++,nt=pt/(se+pt);se+=pt;let ht=xe?[...xe,...Ae]:[...Ae];return ht.push(()=>{let{fieldVersion:yt,...St}=we;U.write({...St,frame:Ze,rays:pt,center:m(we.center[0],we.center[1]),lightPos:we.lightPos.map(Mt=>S(Mt[0],Mt[1],Mt[2],Mt[3])),lightColor:we.lightColor.map(Mt=>S(Mt[0],Mt[1],Mt[2],Mt[3]))}),L.write({weight:nt})}),ht.push(O,k,D,...E.computeSteps),ht}}},sample:l=>{let c=l.computeOutputs?.[t];if(!c)return;let d=z(c.sampleLevel(l.ctx.uv),\"irrTex\");return Ce(d.member(\"rgb\"),it(d.member(\"a\"),.001))}}}var eC=[[1/16,-3/16],[-1/16,3/16],[5/16,1/16],[-3/16,-5/16],[-5/16,5/16],[-7/16,-1/16],[3/16,7/16],[7/16,-7/16]],Z0=1e9,tC=e=>e===\"sphere\"||e===\"rounded\"?e:\"cube\",sC=e=>e===\"view\"?\"view\":\"shape\";function YL(e){let t=Ts({stencil:\"centre\",centreTap:\"fast\",surface:(r,o)=>{let{uniforms:a}=o,i=tC(o.propValues[e.style.name]),l=sC(o.propValues[e.gridSpace.name]),c=Lr(r,o,{scale:a.scale}),d=Ea(o,c),u=a._vxMx,p=a._vxMy,_=a._vxMz,f=Ze=>l===\"shape\"?I(I(v(u,Ze.member(\"x\")),v(p,Ze.member(\"y\"))),v(_,Ze.member(\"z\"))):Ze,h=Ze=>l===\"shape\"?Qe($t(u,Ze),$t(p,Ze),$t(_,Ze)):Ze,g=r.sdfUV,y=z(f(Qe(ne(g.member(\"x\"),.5),ne(.5,g.member(\"y\")),-1.2)),\"vxRo\"),b=z(f(Qe(0,0,1)),\"vxRd\"),x=z(Ce(c.pxH,it(a.scale,.001)),\"vxPx\"),P=z(f(Qe(x,0,0)),\"vxEx\"),C=z(f(Qe(0,er(x),0)),\"vxEy\"),E=z(a._vxVoxel,\"vxSize\"),R=z(a._vxGridOrigin,\"vxOrigin\"),U=z(v(v(E,.5),me(e.voxelScale,o)),\"vxHalf\"),L=z(it(v(me(e.bevel,o),U),v(U,.04)),\"vxREff\"),k=z(v(R,-2),\"vxSpan\"),D=(Ze,nt)=>i===\"sphere\"?W(xne,\"voxelRaySphere\",[Ze,b,nt,U]):i===\"rounded\"?W(Ane,\"voxelRayRoundedBox\",[Ze,b,nt,U,v(me(e.bevel,o),U)]):W(bx,\"voxelRayBox\",[Ze,b,nt,U]),O=z(a._vfActiveRes,\"vxRes\"),F=a._vfOriginX,q=a._vfOriginY,Y=a._vfSpanX,V=a._vfSpanY,X=ne(O,1),Q=ne(v(Ce(ne(g.member(\"x\"),F),Y),O),.5),te=ne(v(Ce(ne(g.member(\"y\"),q),V),O),.5),J=z(Ge(Xr(Q),0,X),\"vxX0\"),oe=z(Ge(Xr(te),0,X),\"vxY0\"),le=Yr(I(J,1),X),se=Yr(I(oe,1),X),Te=(Ze,nt)=>rt(I(F,v(Ce(I(Ze,.5),O),Y)),I(q,v(Ce(I(nt,.5),O),V))),de=[{tx:J,ty:oe,tag:\"00\"},{tx:le,ty:oe,tag:\"10\"},{tx:J,ty:se,tag:\"01\"},{tx:le,ty:se,tag:\"11\"}].map(Ze=>{let nt=$r=>`${$r}${Ze.tag}`,ht=z(Te(Ze.tx,Ze.ty),nt(\"vxUv\")),yt=z(r.texelSampler(ht),nt(\"vxTex\")),St=z(br(Xn(yt.member(\"x\"),0),1,0),nt(\"cov\")),Mt=yt.member(\"y\"),Rt=z(Ce(Xr(Ce(ne(Mt,v(Xr(Ce(Mt,65536)),65536)),256)),255),nt(\"ao\")),Kt=yt.member(\"z\"),Wt=z(Xr(Ce(Kt,65536)),nt(\"cz\")),ls=ne(Kt,v(Wt,65536)),us=z(Xr(Ce(ls,256)),nt(\"cy\")),os=z(Qe(z(ne(ls,v(us,256)),nt(\"cx\")),us,Wt),nt(\"cell\")),bs=z(I(v(I(os,.5),E),R),nt(\"centre\")),hs=yt.member(\"w\"),gs=z(f(Qe(ne(ht.member(\"x\"),.5),ne(.5,ht.member(\"y\")),ne(hs,1.2))),nt(\"texHp\")),Gs=z(br(Zo(St,.5),D(y,bs),-1),nt(\"tC\")),Ss=Zo(Gs,-.5),or=z(br(Ss,I(y,v(b,Gs)),gs),nt(\"hp\")),yr=z(br(Ss,Gs,hs),nt(\"depth\")),lr=z(ne(or,bs),nt(\"l\")),Ur=z(ms(lr),nt(\"al\")),so=it(Ur.member(\"x\"),it(Ur.member(\"y\"),Ur.member(\"z\"))),ro=z(br(ch(Ur.member(\"x\"),so),1,0),nt(\"isX\")),_e=z(v(br(ch(Ur.member(\"y\"),so),1,0),ne(1,ro)),nt(\"isY\")),We=ne(ne(1,ro),_e),Le=z(v(Bx(lr),Qe(ro,_e,We)),nt(\"faceN\")),Be=z(ne(1,ms(Le)),nt(\"tMask\")),Oe;if(i===\"sphere\")Oe=Os(lr);else{let $r=v(v(Bx(lr),it(ne(Ur,ne(U,L)),Qe(0,0,0))),Be);Oe=Os(I(v(Le,L),$r))}let tt=z(Oe,nt(\"nG\")),_t=z(h(tt),nt(\"nV\")),Bt=z(Qe(_t.member(\"x\"),er(_t.member(\"y\")),_t.member(\"z\")),nt(\"n\")),Dt=v(Ur,Be),Nt=z(Ge(Ce(it(it(Dt.member(\"x\"),Dt.member(\"y\")),Dt.member(\"z\")),U),0,1),nt(\"edge\")),Ks=z(W(Wb,\"hash13\",[I(os,Qe(.37,.61,.83))]),nt(\"hash\")),Ds=z(Ge(Ce(v(I(us,.5),E),k),0,1),nt(\"heightT\")),ar=z(Ge(Ce(ne(yr,I(1.2,R)),k),0,1),nt(\"depthT\")),Xo=z(br(Ss,Gs,Z0),nt(\"tSort\"));return{tag:Ze.tag,cov:St,cell:os,centre:bs,hp:or,nG:tt,tSort:Xo,frame:{field:c,coverage:St,normal:Bt,ao:Rt,cell:os,cellHash:Ks,heightT:Ds,depthT:ar,edge:Nt,view:d,style:i}}}),xe=Ze=>z(v(br(Xn(ms(ne(de[Ze].cell,de[0].cell)).member(\"x\"),.5),1,0),v(br(Xn(ms(ne(de[Ze].cell,de[0].cell)).member(\"y\"),.5),1,0),br(Xn(ms(ne(de[Ze].cell,de[0].cell)).member(\"z\"),.5),1,0))),`vxSame${Ze}`),Ae=[$e(1),xe(1),xe(2),xe(3)],De=z(v(v(v(Ae[1],Ae[2]),Ae[3]),v(v(de[0].cov,de[1].cov),v(de[2].cov,de[3].cov))),\"vxAllOne\"),Ee=o.computeOutputs?.voxelShadowMap,ze=$e(0);if(Ee){let Ze=(Rt,Kt)=>{let Wt=Xn(Rt.tSort,Kt.tSort);return{tSort:br(Wt,Rt.tSort,Kt.tSort),hp:br(Wt,Rt.hp,Kt.hp),nG:br(Wt,Rt.nG,Kt.nG)}},nt=Ze(Ze(de[0],de[1]),Ze(de[2],de[3])),ht=z(nt.hp,\"vxHpShadow\"),yt=z(nt.nG,\"vxNShadow\"),St=W(bne,\"voxelShadowLookup\",[Ee.accessor(),I(ht,v(yt,v(E,.12))),a._vxL,a._vxT1,a._vxT2,er(R),me(e.shadowSoftness,o),E]),Mt=Zo($t(yt,a._vxL),-.12);ze=Js(Zo(v(br(Zo(a.shadows,.001),1,0),br(Mt,1,0)),.5),St,$e(1),\"shadow\",[ht,yt])}let Ie=de.flatMap(Ze=>[Ze.cov,Ze.centre,Ze.tSort,Ze.frame.normal,Ze.frame.ao,Ze.frame.cellHash,Ze.frame.heightT,Ze.frame.depthT,Ze.frame.edge]),ke=z(e.surface({...de[0].frame,shadow:ze},r,o).member(\"rgb\"),\"vxShade00\"),Me=de.map((Ze,nt)=>nt===0?ke:Js(Xn(Ae[nt],.5),e.surface({...Ze.frame,shadow:ze},r,o).member(\"rgb\"),ke,`vxShade${Ze.tag}`,nt===1?Ie:[])),we=$e(0),Ve=Qe(0,0,0);eC.forEach(([Ze,nt],ht)=>{let yt=z(I(I(y,v(P,Ze)),v(C,nt)),`vxRo${ht}`),St=de.map(us=>{let os=z(br(Zo(us.cov,.5),D(yt,us.centre),-1),`vxTr${ht}_${us.tag}`);return z(br(Zo(os,-.5),os,Z0),`vxT${ht}_${us.tag}`)}),Mt=Yr(St[2],St[3]),Rt=Yr(St[1],Mt),Kt=Yr(St[0],Rt),Wt=br(Xn(St[0],Rt),Me[0],br(Xn(St[1],Mt),Me[1],br(Xn(St[2],St[3]),Me[2],Me[3]))),ls=z(br(Xn(Kt,Z0*.5),1,0),`vxHit${ht}`);we=I(we,ls),Ve=I(Ve,v(Wt,ls))});let qe=Ot(Ce(Ve,it(we,1e-4)),Ce(we,eC.length)),st=Ot(ke,1),pt=Js(Xn(De,.5),qe,st,\"vxOut\",[ke,...Me.slice(1)]);return Js(Dr(c.sdf,c.pxH),pt,xt,\"voxels\")}}),s=r=>{let o=(i,l)=>{if(!i)return l;let c=r.getCpuValue(i.name);return typeof c==\"number\"&&Number.isFinite(c)?c:l},a=()=>{let i=r.getCpuValue(\"_animTime\");return typeof i==\"number\"&&Number.isFinite(i)?i:0};return Pne(r,{style:tC(r.propValues[e.style.name]),gridSpace:sC(r.propValues[e.gridSpace.name]),getShapeConfig:()=>r.getCpuValue(\"shape\"),getValues:()=>({voxelSize:o(e.voxelSize,.03),fill:o(e.fill,0),voxelScale:o(e.voxelScale,1),bevel:o(e.bevel,0),pitch:o(e.pitch,0),yaw:o(e.yaw,0)+(e.spin?a()*36:0),lightAngle:o(e.lightAngle,225),lightElevation:o(e.lightElevation,45),depth:o(e.depth,.12)})})};return{extraFields:{...t.extraFields,...Tne},compute:s,gpu:t.gpu}}var $le=bt;function aa(e){let t=e.uvContext??e.ctx.uv,s=e.effectiveViewportSize??e.ctx.viewportSize;return{uv:t,viewport:s,aspect:z(Ce(s.member(\"x\"),s.member(\"y\")),\"aspect\")}}function WL(e){let t=e.uvContext??e.ctx.uv,s=e.ctx.viewportSize;return{uv:t,viewport:s,aspect:z(Ce(s.member(\"x\"),s.member(\"y\")),\"aspect\")}}function qL(e){return(t,s)=>{let r=me(e.center,t),{uv:o,aspect:a}=s,i=z(rt(ne(v(o.member(\"x\"),a),v(r.member(\"x\"),a)),ne(o.member(\"y\"),ne(1,r.member(\"y\")))),\"centred\");return{delta:i,dist:z(Kr(i),\"centredDist\")}}}function Pg(e){return(t,s)=>{let r=me(e.from,t),o=me(e.to,t),{uv:a,aspect:i}=s,l=v(r.member(\"x\"),i),c=ne(1,r.member(\"y\")),d=z(rt(ne(v(a.member(\"x\"),i),l),ne(a.member(\"y\"),c)),\"rel\"),u=z(rt(ne(v(o.member(\"x\"),i),l),ne(ne(1,o.member(\"y\")),c)),\"toRel\"),p=z(it(Kr(u),1e-4),\"segLen\"),_=z(Ce(u,p),\"segDir\");return{along:z($t(d,_),\"along\"),across:z(I(v(d.member(\"x\"),er(_.member(\"y\"))),v(d.member(\"y\"),_.member(\"x\"))),\"across\"),length:p}}}function Er(e,t=\"dir\"){let s=v(e,$le);return z(rt(Do(s),Vt(s)),t)}function ii(e,t=\"grain\"){let s=Er(e,`${t}T`),r=z(rt(er(s.member(\"y\")),s.member(\"x\")),`${t}P`);return{tangent:s,perp:r,coordsOf(o){return{along:z($t(o,s),\"along\"),across:z($t(o,r),\"across\")}}}}function g2(e,t,s,r=.5){let o=3.141592653589793,a=z(ne(e,r),\"kfDx\"),i=z(ne(t,r),\"kfDy\"),l=z(ho(I(v(a,a),v(i,i))),\"kfR\"),c=z(v(Qr(Ce(I(sle(i,a),o),s)),s),\"kfInSector\"),d=z(Yr(c,ne(s,c)),\"kfFolded\");return rt(v(l,Do(d)),v(l,Vt(d)))}function Ft(e,t){if(typeof e==\"number\")return $e(e);if(e instanceof zi)return fn(e,t);if(typeof e==\"object\"&&e!==null&&\"kind\"in e)return e.kind===\"prop\"?me(e,t):fn(e,t);throw new Error(\"std/warps: warp slots accept prop bindings, numbers, and Scalar signals\")}function VL(e){return t=>{let s=(r,o)=>W(Oee,\"mirrorReflect\",[Ft(e.center,t),Ft(e.angle,t),r,o]);return Kne(r=>r,(r,o)=>s(r,o).member(\"xy\"),(r,o)=>s(r,o).member(\"z\"))}}function NL(e){return t=>({map:s=>W(Gee,\"flipUV\",[s,Ft(e.flipX,t),Ft(e.flipY,t)])})}function $L(e){return t=>({map:(s,r)=>W(Fee,\"bendRemap\",[Ft(e.strength,t),Ft(e.falloff,t),Ft(e.angle,t),s,r])})}function XL(e){return t=>({map:(s,r)=>W(Iee,\"bulgeUV\",[Ft(e.center,t),Ft(e.strength,t),Ft(e.radius,t),Ft(e.falloff,t),s,r])})}function HL(e){return t=>({map:(s,r)=>W(Yee,\"twirlUV\",[Ft(e.center,t),Ft(e.intensity,t),s,r])})}function jL(e){return t=>({map:(s,r)=>W(Wee,\"kaleidoscopeUV\",[Ft(e.center,t),Ft(e.segments,t),Ft(e.angle,t),s,r])})}function KL(e){return t=>({map:(s,r)=>W(qee,\"stretchUV\",[Ft(e.center,t),Ft(e.strength,t),Ft(e.angle,t),Ft(e.falloff,t),s,r])})}var Xle={sine:0,triangle:1,square:2,sawtooth:3,bounce:4};function Hle(e){return typeof e==\"number\"?e:Xle[e]??0}function jle(e){switch(e){case 1:return{fn:Nee,hint:\"waveDistortTriangle\"};case 2:return{fn:$ee,hint:\"waveDistortSquare\"};case 3:return{fn:Xee,hint:\"waveDistortSawtooth\"};case 4:return{fn:Hee,hint:\"waveDistortBounce\"};default:return{fn:Vee,hint:\"waveDistortSine\"}}}function QL(e){return t=>{let{fn:s,hint:r}=jle(Hle(t.propValues[e.waveType.name])),o=wt(t).mul(.5);return{map:(a,i)=>W(s,r,[a,i,Ft(e.angle,t),Ft(e.frequency,t),Ft(e.strength,t),o])}}}function ZL(e){return t=>xz({map:(s,r)=>W(jee,\"polarCoordsUV\",[Ft(e.center,t),Ft(e.wrap,t),Ft(e.radius,t),s,r])},()=>Ft(e.intensity,t))}function JL(e){return t=>xz({map:(s,r)=>W(Kee,\"rectCoordsUV\",[Ft(e.center,t),Ft(e.scale,t),s,r])},()=>Ft(e.intensity,t))}function eU(e){return t=>({map:s=>{let r=W(Qee,\"cornerPinSample\",[s,Ft(e.amount,t),Ft(e.topLeft,t),Ft(e.topRight,t),Ft(e.bottomRight,t),Ft(e.bottomLeft,t)]);return{uv:r.member(\"xy\"),coverage:r.member(\"z\")}}})}function tU(e){return t=>({map:s=>W(Zee,\"perspectiveUV\",[Ft(e.center,t),Ft(e.pan,t),Ft(e.tilt,t),Ft(e.fov,t),Ft(e.zoom,t),Ft(e.offset,t),s])})}function sU(e){return t=>{let s=wt(t);return{map:(r,o)=>W(Jee,\"concentricSpinUV\",[Ft(e.center,t),Ft(e.intensity,t),Ft(e.rings,t),Ft(e.smoothness,t),Ft(e.seed,t),Ft(e.speedRandomness,t),s,r,o])}}}function rU(e){return t=>{let s=wt(t,e.seed.name).mul(.1),r=wt(t,e.seed.name,\"_animTime_evolution\").mul(.1);return{map:(o,a)=>W(ete,\"flowFieldUV\",[o,a,Ft(e.detail,t),Ft(e.strength,t),s,r])}}}function oU(e){return t=>({map:(s,r)=>W(tte,\"sliceWipeUV\",[s,r,Ft(e.angle,t),Ft(e.count,t),Ft(e.progress,t)])})}function aU(e){return t=>{let s=wt(t);return{map:(r,o)=>W(ste,\"barShiftUV\",[Ft(e.count,t),Ft(e.angle,t),Ft(e.intensity,t),Ft(e.seed,t),s,r,o])}}}function nU(e){return t=>({map:(s,r)=>{let o=t.computeOutputs?.[e.output],a=W(rte,\"gridCellSnap\",[s,Ft(e.gridSize,t),r]);return W(ote,\"gridDistortOffsetUV\",[s,o?o.sample(a,\"linearClamp\").member(\"xy\"):ca(\"vec2f(0.0, 0.0)\")])}})}function iU(e){return t=>({map:s=>{let r=t.computeOutputs?.[e.output];return W(ate,\"liquifyOffsetUV\",[s,r?r.sample(s,\"linearClamp\").member(\"xy\"):ca(\"vec2f(0.0, 0.0)\"),Ft(e.intensity,t)])}})}var Kle={twoAxis:0,directional:1};function Qle(e){return typeof e==\"number\"?e:Kle[e]??0}function lU(e){return{kind:\"gather\",build:t=>{let s=t.getLayerTexture(e.source.name);if(!s)return t.texture.sample(t.ctx.uv);let r=t.propValues[e.edges.name]??0,o=s.sample(t.ctx.uv),a=Qle(t.propValues[e.channels.name])===1?W(ite,\"dmDisplaceLuminance\",[t.ctx.uv,o,t.ctx.aspect,Ft(e.amount,t),Ft(e.angle,t)]):W(nte,\"dmDisplaceRG\",[t.ctx.uv,o,t.ctx.aspect,Ft(e.amount,t)]);return T4(t.texture,a,r)}}}function cU(e,t){let s=z(it(Vt(I(v(e,t.rate),t.offset??0)),0),\"flash\");for(let r=1;r<t.sharpness;r*=2)s=z(v(s,s),\"flash\");return s}function $o(e,t,s){return{kind:\"gather\",resultAlpha:s?.resultAlpha,build:r=>t.reduce((o,a)=>a(o,r),e(r))}}function Cg(e){let t=new WeakMap;return s=>{let r=t.get(s);if(r)return r;let o=e(s);return t.set(s,o),o}}function dU(e){return t=>{let s=At(e.amount,t),r=W(SS,\"crtSampleUV\",[t.ctx.uv,s,$e(1)]),o=W(SS,\"crtSampleUV\",[t.ctx.uv,s,$e(-1)]);return W(_re,\"rgbSplitCombine\",[t.texture.sample(r),t.texture.sample(t.ctx.uv),t.texture.sample(o)])}}function uU(e){return t=>W(yre,\"vhsSampleUVs\",[t.ctx.uv,t.ctx.time,At(e.speed,t),At(e.wobble,t),At(e.jitter,t)])}function pU(e){return t=>{let s=ns(e.warp(t),\"tapeUVs\"),r=s.member(\"zw\"),o=t.texture.sample(s.member(\"xy\")),a=At(e.smear,t).mul(.0075),i=l=>t.texture.sample(W(xre,\"vhsChromaTapUV\",[r,a,$e(l)]));return Ct(W(Are,\"yiqRecombine\",[o,i(1),i(2),i(3),i(4),i(5)]),o.member(\"a\"))}}function mU(e,t,s){let r=i=>v($t(i.member(\"rgb\"),Qe(.2126,.7152,.0722)),i.member(\"a\")),o=z(e(t),\"nearC\"),a=z(r(o),\"nearL\");for(let i=0;i<s.taps;i++){let l=((s.angle??0)+360*i/s.taps)*(Math.PI/180),c=z(e(I(t,v(rt(Math.cos(l),Math.sin(l)),s.radius))),\"nearT\"),d=z(r(c),\"nearTL\");o=z(br(Zo(d,a),c,o),\"nearC\"),a=z(it(d,a),\"nearL\")}return{color:o,luma:a}}function _U(e){return(t,s)=>W(fre,\"adjustShade\",[t,At(e.contrast,s),At(e.brightness,s)])}function fU(e){return(t,s)=>W(vre,\"scanlineShade\",[t,s.ctx.uv.member(\"y\"),At(e.frequency,s),At(e.intensity,s)])}function vU(e){return(t,s)=>W(hre,\"phosphorShade\",[t,s.ctx.uv,At(e.pitch,s)])}function hU(e){return(t,s)=>W(gre,\"vignetteShade\",[t,s.ctx.uv,s.ctx.aspect,At(e.radius,s),At(e.intensity,s)])}function gU(){return e=>Ct(e,$e(1))}function yU(e){return(t,s)=>W(wre,\"beatShade\",[t,W(bre,\"vhsAcBeat\",[s.ctx.uv,s.ctx.time,At(e.speed,s),At(e.wobble,s)])])}var wp={linear:e=>({kind:\"linear\",focus:e}),orbit:e=>({kind:\"orbit\",focus:e}),zoom:e=>({kind:\"zoom\",focus:e})};function Tp(e){let{path:t,amount:s,detail:r}=e;return{kind:\"gather\",build:o=>{let a={focus:At(t.focus,o),amount:At(s,o),uv:o.ctx.uv,aspect:o.ctx.aspect,viewportSize:o.ctx.viewportSize},i=d=>o.texture.sample(d);if(!r||!Zle(r,o))return qse(t.kind,a,i);let l=d=>d?me(d,o):$e(0),c=t.kind===\"zoom\"&&r.spiral&&Mg(r.spiral,o)?me(r.spiral,o):void 0;return Qse(t.kind,a,{dispersion:l(r.dispersion),falloff:l(r.falloff),focus:l(r.focus),bias:l(r.bias),highlights:l(r.highlights),jitter:l(r.jitter),spiral:c,center:t.kind===\"linear\"?ca(\"vec2f(0.5, 0.5)\"):a.focus},i)}}}function Mg(e,t){if(t.getMapInfo?.(e.name))return!0;let s=t.propValues?.[e.name];return typeof s==\"number\"?s!==0:!!s}function Zle(e,t){return Object.values(e).some(s=>s&&Mg(s,t))}function bU(e){let{amount:t,edges:s,chromatic:r}=e;return{kind:\"gather\",build:o=>sre({uv:o.ctx.uv,amount:At(t,o),viewportSize:o.ctx.viewportSize,edgeMode:o.propValues[s.name]??0,sample:a=>o.texture.sample(a),chromatic:r&&Mg(r,o)?me(r,o):void 0})}}function xU(e){return{kind:\"gather\",build:t=>dre({uv:t.ctx.uv,viewportSize:t.ctx.viewportSize,amount:At(e,t),sample:s=>t.texture.sample(s)})}}function AU(e){let{scale:t,gap:s,roundness:r}=e;return{kind:\"gather\",resultAlpha:\"straight\",build:o=>pre({uv:o.ctx.uv,aspect:o.ctx.aspect,scale:At(t,o),gap:At(s,o),roundness:At(r,o),sampleStraight:o.sampleStraight})}}function wU(e){return Cg(t=>ns(W(vte,\"sphereBulge\",[t.ctx.uv,t.ctx.viewportSize,At(e.center,t),At(e.radius,t),At(e.depth,t)]),\"sphere\"))}function TU(e){return t=>W(Yt,\"unpremultiplyAlpha\",[g_(t.texture,e(t).member(\"uv\"))])}function SU(e,t){return(s,r)=>{let o=W(hte,\"rimLight\",[e(r).member(\"normal\"),At(t.position,r),At(t.intensity,r),At(t.softness,r)]);return W(gte,\"rimComposite\",[s,At(t.color,r).member(\"rgb\"),o,e(r).member(\"coverage\")])}}function PU(e){return Cg(t=>ns(W(Tre,\"glitchGeom\",[t.ctx.uv,t.ctx.time,At(e.intensity,t),At(e.speed,t),At(e.blockDensity,t),At(e.mirrorAmount,t)]),\"glitch\"))}function CU(e,t){return s=>{let r=e(s),o=ns(W(Sre,\"glitchSplitUVs\",[r.member(\"mirroredUV\"),At(t.shift,s),r.member(\"spread\")]),\"splitUVs\"),a=s.texture.sample(r.member(\"mirroredUV\"));return Ct(s.texture.sample(o.member(\"xy\")).member(\"r\"),a.member(\"g\"),s.texture.sample(o.member(\"zw\")).member(\"b\"),a.member(\"a\"))}}function MU(e,t){return(s,r)=>{let o=e(r);return W(Pre,\"fillBarsShade\",[s,r.ctx.uv,o.member(\"bandY1\"),o.member(\"slowFrame\"),At(t.intensity,r),o.member(\"strength\"),o.member(\"bandGate\"),o.member(\"blockGate\")])}}function EU(e,t){return(s,r)=>W(Cre,\"distortScanShade\",[s,r.ctx.uv.member(\"y\"),r.ctx.viewportSize.member(\"y\"),At(t.intensity,r),e(r).member(\"distortion\")])}function kU(e){let t=e.intensity.name;return{compute:s=>{let{getCpuValue:r,getMapInfo:o}=s,a=o(t);return a?qu(s,{source:a,buildFill:(i,l)=>cse(i,l,a.channel),fillValues:(i,l)=>({inputWidth:i.width,inputHeight:i.height,...Nh(l)})}):B4(s,{radius:()=>{let i=r(t);return typeof i==\"number\"?Q3(i):4}})},gpu:{fragment:s=>x_(s,(r,o)=>Ct(r.member(\"rgb\"),o.member(\"a\")))}}}function BU(e){let{red:t,green:s,blue:r}=e;return{compute:o=>B4(o,{radius:()=>{let a=(o.getCpuValue(t.name)??0)*T0,i=(o.getCpuValue(s.name)??0)*T0,l=(o.getCpuValue(r.name)??0)*T0;return Math.max(a,i,l,.01)}}),gpu:{fragment:o=>x_(o,(a,i)=>W(dse,\"channelBlurCompose\",[i,a,me(t,o),me(s,o),me(r,o)]))}}}function zU(e,t){if(!e)return{};let{dispersion:s,jitter:r}=e,o=s?Mg(s,t):!1,a=i=>i?t.getCpuValue(i.name)??0:0;return{chromatic:o,detail:()=>({jitter:a(r),dispersion:o?a(s)*ise:0})}}function RU(e){let{intensity:t,angle:s,center:r,falloff:o,detail:a}=e;return{compute:i=>{let{getCpuValue:l,getMapInfo:c}=i,d=zU(a,i),u=_=>{let f=l(r.name);return{angle:l(s.name)??0,centerX:typeof f?.x==\"number\"?f.x:0,centerY:typeof f?.y==\"number\"?f.y:.5,falloff:l(o.name)??1,aspect:_.width/_.height}},p=c(t.name);return p?qu(i,{source:p,buildFill:(_,f)=>_se(_,f,p.channel),...d,fillValues:(_,f)=>({...u(_),inputWidth:_.width,inputHeight:_.height,...Nh(f)})}):qu(i,{buildFill:mse,...d,fillValues:_=>({...u(_),maxRadius:Q3(l(t.name)??0)})})},gpu:{fragment:i=>x_(i,(l,c)=>Ct(l.member(\"rgb\"),c.member(\"a\")))}}}function DU(e){let{intensity:t,width:s,falloff:r,angle:o,center:a,detail:i}=e;return{compute:l=>{let{getCpuValue:c,getMapInfo:d}=l,u=zU(i,l),p=f=>{let h=c(a.name);return{angle:c(o.name)??0,centerX:typeof h?.x==\"number\"?h.x:.5,centerY:typeof h?.y==\"number\"?h.y:.5,width:c(s.name)??.3,falloff:c(r.name)??.3,aspect:f.width/f.height}},_=d(t.name);return _?qu(l,{halfKernel:14,source:_,buildFill:(f,h)=>gse(f,h,_.channel),...u,fillValues:(f,h)=>({...p(f),inputWidth:f.width,inputHeight:f.height,...Nh(h)})}):qu(l,{halfKernel:14,buildFill:hse,...u,fillValues:f=>({...p(f),maxRadius:Q3(c(t.name)??0)})})},gpu:{fragment:l=>x_(l,(c,d)=>ea(d,c,W(Km,\"tiltShiftBlurAmount\",[me(o,l),me(a,l),me(s,l),me(r,l),l.ctx.uv,l.ctx.aspect])))}}}function LU(e){let{intensity:t,threshold:s,size:r}=e;return{compute:o=>{let{getCpuValue:a,getMapInfo:i}=o,l=i(r.name);return!l&&(a(r.name)??0)===0?null:z4(o,{mapInfo:l,buildExtract:bse,buildExtractMap:xse,threshold:()=>a(s.name)??.5,radius:()=>a(r.name)??25})},gpu:{fragment:o=>x_(o,(a,i)=>W(yse,\"glowCompose\",[i,a,me(t,o)]))}}}function UU(){return e=>e.texture.sample(e.ctx.uv)}function OU(e){return t=>W(are,\"dropShadowUV\",[t.ctx.uv,t.ctx.viewportSize,At(e.angle,t),At(e.distance,t)])}function GU(e){return t=>lre({at:e.at(t),uv:t.ctx.uv,viewportSize:t.ctx.viewportSize,blurRadius:At(e.blur,t),sample:s=>t.texture.sample(s),convertToTexture:t.convertToTexture})}function FU(e){return(t,s)=>{let r=e.coverage(s).mul(At(e.intensity,s));return W(ire,\"dropShadowComposite\",[t,At(e.color,s),r,$e(s.propValues[e.cutout.name]===1?1:0)])}}var rC={\"top-left\":{corner:[0,0],opposite:[1,1]},\"top-right\":{corner:[1,0],opposite:[0,1]},\"bottom-left\":{corner:[0,1],opposite:[1,0]},\"bottom-right\":{corner:[1,1],opposite:[0,0]}};function IU(e){return Cg(t=>{let s=t.propValues[e.shape.name]??0,r=s===0?16:8,o=s===0?4:3,a=s===2?1:0,i=wt(t).mul(-1);return ns(W(lte,\"flutedGlassGeom\",[t.ctx.uv,t.ctx.aspect,i,At(e.angle,t),At(e.frequency,t),At(e.softness,t),At(e.waveAmplitude,t),At(e.waveFrequency,t),At(e.refraction,t),At(e.aberration,t),ca(`vec3f(${fo(r)}, ${fo(o)}, ${fo(a)})`)]),\"flute\")})}function YU(e,t){return s=>{let r=s.propValues[t.edges.name]??2,o=(s.propValues[t.aberration.name]??.2)>0,a=e(s),i=c=>bte(d=>s.texture.sample(d),c,r),l=a.member(\"refractedUV\");return W(Yt,\"unpremultiplyAlpha\",[o?yte(i,l,a.member(\"chrOff\")):i(l)])}}function WU(e,t){return(s,r)=>{let o=W(cte,\"blinnHighlight\",[e(r).member(\"slope\"),At(t.lightAngle,r),At(t.highlight,r),At(t.softness,r)]).mul(s.member(\"a\"));return Ct(s.member(\"rgb\").add(At(t.color,r).member(\"rgb\").mul(o)),s.member(\"a\"))}}function qU(e){return Cg(t=>{let s=rC[t.propValues[e.corner.name]??\"bottom-right\"]??rC[\"bottom-right\"];return ns(W(dte,\"pagePeelGeom\",[t.ctx.uv,t.ctx.aspect,$e(s.corner[0]),$e(s.corner[1]),$e(s.opposite[0]),$e(s.opposite[1]),At(e.amount,t),At(e.radius,t)]),\"peel\")})}function VU(e,t){return s=>W(ute,\"curlShade\",[e(s).member(\"theta\"),At(t.shading,s)])}function NU(e,t){return s=>W(pte,\"curlSheen\",[e(s).member(\"theta\"),At(t.highlight,s),At(t.softness,s)])}function $U(e,t){return s=>{let r=e(s);return W(mte,\"overhangShade\",[r.member(\"distPastCrease\"),r.member(\"flatReach\"),At(t.amount,s),At(t.shadow,s)])}}function XU(e,t){return s=>{let r=e(s);return W(_te,\"revealShadow\",[r.member(\"distIntoPeel\"),r.member(\"peelReach\"),At(t.amount,s),At(t.shadow,s)])}}function HU(e,t){return s=>{let r=e(s);return W(fte,\"pagePeelCompose\",[s.texture.sample(r.member(\"curlUV\")),s.texture.sample(s.ctx.uv),t.shade(s),t.sheen(s),t.overhang(s),t.reveal(s),r.member(\"foldBlend\"),r.member(\"showCurl\")])}}var Jle=w.fn([m,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return m(t.x,__tsover_sub(__tsover_mul(s,2),t.y))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"lineY\"}],body:[0,[[10,[6,\"d.vec2f\",[[7,\"uv\",\"x\"],[1,[1,\"lineY\",\"*\",[5,\"2\"]],\"-\",[7,\"uv\",\"y\"]]]]]]]},externals:{\"d.vec2f\":()=>m}})&&e.f)({})),ece=w.fn([S,S,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=B(__tsover_sub(r,o),0),c=B(a,.001),d=B(__tsover_mul(c,i),1e-4),u=__tsover_sub(c,d),p=__tsover_sub(1,be(u,c,l)),_=__tsover_mul(s.w,p),f=__tsover_sub(1,_);return K(t,S(s.xyz.mul(p).add(t.xyz.mul(f)),__tsover_add(_,__tsover_mul(t.w,f))),S(Et(o,r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"original\"},{type:\"i\",name:\"refl\"},{type:\"i\",name:\"uvY\"},{type:\"i\",name:\"lineY\"},{type:\"i\",name:\"distance\"},{type:\"i\",name:\"falloff\"}],body:[0,[[13,\"distBelow\",[6,\"std.max\",[[1,\"uvY\",\"-\",\"lineY\"],[5,\"0\"]]]],[13,\"distanceMax\",[6,\"std.max\",[\"distance\",[5,\"0.001\"]]]],[13,\"fadeWidth\",[6,\"std.max\",[[1,\"distanceMax\",\"*\",\"falloff\"],[5,\"0.0001\"]]]],[13,\"fadeStart\",[1,\"distanceMax\",\"-\",\"fadeWidth\"]],[13,\"visibility\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"fadeStart\",\"distanceMax\",\"distBelow\"]]]],[13,\"effReflA\",[1,[7,\"refl\",\"w\"],\"*\",\"visibility\"]],[13,\"oneMinusReflA\",[1,[5,\"1\"],\"-\",\"effReflA\"]],[13,\"belowRgb\",[6,[7,[6,[7,[7,\"refl\",\"xyz\"],\"mul\"],[\"visibility\"]],\"add\"],[[6,[7,[7,\"original\",\"xyz\"],\"mul\"],[\"oneMinusReflA\"]]]]],[13,\"belowA\",[1,\"effReflA\",\"+\",[1,[7,\"original\",\"w\"],\"*\",\"oneMinusReflA\"]]],[13,\"belowLine\",[6,\"d.vec4f\",[\"belowRgb\",\"belowA\"]]],[13,\"isBelowLine\",[6,\"std.step\",[\"lineY\",\"uvY\"]]],[10,[6,\"std.mix\",[\"original\",\"belowLine\",[6,\"d.vec4f\",[\"isBelowLine\"]]]]]]]},externals:{\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S,\"std.step\":()=>Et,\"std.mix\":()=>K}})&&e.f)({})),oC=12;function tce(){return gt({height:n,blurAmount:n,blurDistance:n})}function sce(e,t){let s=tce(),r=(globalThis.__TYPEGPU_AUTONAME__??(o=>o))(w.bindGroupLayout({blurMap:{storageTexture:zt(ri,\"write-only\")},params:{uniform:s}}),\"layout\");return{layout:r,kernel:w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=r.$.params,c=__tsover_div(__tsover_add(n(i),.5),t),d=B(__tsover_sub(l.height,c),0),u=be(0,l.blurDistance,d),p=__tsover_mul(__tsover_mul(l.blurAmount,u),oC);lt(r.$.blurMap,fe(a,i),S(p,0,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"computeHeight\"]],[13,\"distBelowAtOutput\",[6,\"std.max\",[[1,[7,\"p\",\"height\"],\"-\",\"v\"],[5,\"0\"]]]],[13,\"ramp\",[6,\"std.smoothstep\",[[5,\"0\"],[7,\"p\",\"blurDistance\"],\"distBelowAtOutput\"]]],[13,\"radius\",[1,[1,[7,\"p\",\"blurAmount\"],\"*\",\"ramp\"],\"*\",\"DEPTH_RAMP_RADIUS_PX\"]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"blurMap\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"radius\",[5,\"0\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{layout:()=>r,\"d.f32\":()=>n,computeHeight:()=>t,\"std.max\":()=>B,\"std.smoothstep\":()=>be,DEPTH_RAMP_RADIUS_PX:()=>oC,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(\"depthRampFillBlurMap\"),Params:s}}function jU(e){return({ctx:t,uniforms:s})=>W(Jle,\"mirrorRowUV\",[t.uv,s[e.name]])}function KU(e){return{compute:t=>qu(t,{halfKernel:e.halfKernel,buildFill:sce,fillValues:()=>({height:t.getCpuValue(e.line.name)??.5,blurAmount:t.getCpuValue(e.blur.name)??1,blurDistance:Math.max(.01,t.getCpuValue(e.blurDistance.name)??.5)})})}}function QU(e,t,s,r){return o=>{let{ctx:a,uniforms:i,propValues:l}=o,c=l[e.edges.name]??0;return W(ece,\"planarReflectionCompose\",[t,Ha(r(o),s,c),a.uv.member(\"y\"),i[e.line.name],i[e.distance.name],i[e.falloff.name]])}}var rce=w.fn([S,S,S],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=r.w,a=G(s.xyz.mul(r.xyz).mul(o),A(0,0,0),A(1,1,1)),i=A(1,1,1),l=i.sub(i.sub(t.xyz).mul(i.sub(a))),c=__tsover_add(t.w,__tsover_mul(__tsover_mul(s.w,o),__tsover_sub(1,t.w)));return S(l.x,l.y,l.z,G(c,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"base\"},{type:\"i\",name:\"glow\"},{type:\"i\",name:\"tintStrength\"}],body:[0,[[13,\"strength\",[7,\"tintStrength\",\"w\"]],[13,\"bloomC\",[6,\"std.clamp\",[[6,[7,[6,[7,[7,\"glow\",\"xyz\"],\"mul\"],[[7,\"tintStrength\",\"xyz\"]]],\"mul\"],[\"strength\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[13,\"one\",[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]],[13,\"screened\",[6,[7,\"one\",\"sub\"],[[6,[7,[6,[7,\"one\",\"sub\"],[[7,\"base\",\"xyz\"]]],\"mul\"],[[6,[7,\"one\",\"sub\"],[\"bloomC\"]]]]]]],[13,\"a\",[1,[7,\"base\",\"w\"],\"+\",[1,[1,[7,\"glow\",\"w\"],\"*\",\"strength\"],\"*\",[1,[5,\"1\"],\"-\",[7,\"base\",\"w\"]]]]],[10,[6,\"d.vec4f\",[[7,\"screened\",\"x\"],[7,\"screened\",\"y\"],[7,\"screened\",\"z\"],[6,\"std.clamp\",[\"a\",[5,\"0\"],[5,\"1\"]]]]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"tintedScreenGlow\");function ZU(e){return{compute:t=>{let{getCpuValue:s}=t;return(s(e.strength.name)??0)===0?null:z4(t,{outputKey:e.output,buildExtract:(r,o)=>k4(r,o,e.extractName),threshold:()=>e.threshold,radius:()=>s(e.radius.name)??28})},screen:(t,s,r)=>{let o=r.computeOutputs?.[e.output];return W(rce,\"tintedScreenGlow\",[t,o?o.sample(s):ca(\"vec4f(0.0, 0.0, 0.0, 0.0)\"),Ct(e.tint[0],e.tint[1],e.tint[2],r.uniforms[e.strength.name])])}}}var oce=w.fn([m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(s,.006),a=__tsover_mul(__tsover_add(__tsover_mul(ae(__tsover_mul(r,1.7)),.5),__tsover_mul(ae(__tsover_add(__tsover_mul(r,.9),1.3)),.5)),o),i=__tsover_mul(__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(r,1.3),.7)),.5),__tsover_mul(ae(__tsover_mul(r,2.3)),.5)),o),l=__tsover_mul(__tsover_mul(ae(__tsover_mul(r,.6)),s),.004),c=pe(l),d=ae(l),u=t.sub(m(.5,.5)),p=__tsover_sub(__tsover_mul(u.x,c),__tsover_mul(u.y,d)),_=__tsover_add(__tsover_mul(u.x,d),__tsover_mul(u.y,c));return m(__tsover_add(__tsover_add(p,.5),a),__tsover_add(__tsover_add(_,.5),i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"amount\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"amp\",[1,\"amount\",\"*\",[5,\"0.006\"]]],[13,\"ox\",[1,[1,[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"1.7\"]]]],\"*\",[5,\"0.5\"]],\"+\",[1,[6,\"std.sin\",[[1,[1,\"t\",\"*\",[5,\"0.9\"]],\"+\",[5,\"1.3\"]]]],\"*\",[5,\"0.5\"]]],\"*\",\"amp\"]],[13,\"oy\",[1,[1,[1,[6,\"std.sin\",[[1,[1,\"t\",\"*\",[5,\"1.3\"]],\"+\",[5,\"0.7\"]]]],\"*\",[5,\"0.5\"]],\"+\",[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"2.3\"]]]],\"*\",[5,\"0.5\"]]],\"*\",\"amp\"]],[13,\"ang\",[1,[1,[6,\"std.sin\",[[1,\"t\",\"*\",[5,\"0.6\"]]]],\"*\",\"amount\"],\"*\",[5,\"0.004\"]]],[13,\"c\",[6,\"std.cos\",[\"ang\"]]],[13,\"s\",[6,\"std.sin\",[\"ang\"]]],[13,\"p\",[6,[7,\"uv\",\"sub\"],[[6,\"d.vec2f\",[[5,\"0.5\"],[5,\"0.5\"]]]]]],[13,\"rx\",[1,[1,[7,\"p\",\"x\"],\"*\",\"c\"],\"-\",[1,[7,\"p\",\"y\"],\"*\",\"s\"]]],[13,\"ry\",[1,[1,[7,\"p\",\"x\"],\"*\",\"s\"],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"c\"]]],[10,[6,\"d.vec2f\",[[1,[1,\"rx\",\"+\",[5,\"0.5\"]],\"+\",\"ox\"],[1,[1,\"ry\",\"+\",[5,\"0.5\"]],\"+\",\"oy\"]]]]]]},externals:{\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"d.vec2f\":()=>m}})&&e.f)({})).$name(\"swayUV\");function JU(e){return t=>{let s=0;return t.onBeforeRender(({deltaTime:r})=>{let o=t.getCpuValue(e.amount.name)??0;s+=r*(o>0?1:0),t.setExtraField(e.field,s)}),W(oce,\"swayUV\",[t.ctx.uv,t.uniforms[e.amount.name],t.uniforms[e.field]])}}var ace=bt,nce=\"rgba16float\";function aC(e,t,s){let r=\"\";return()=>{let o=t(e.shape.name)??\"blades\",a=t(e.blades.name)??6,i=`${o}|${a}`;i!==r&&(r=i,s.write(D4(o,a,100).map(l=>S(l.x,l.y,l.rim,0))))}}function e8(e){return{compute:t=>{let{childNode:s,gpu:r,convertToTexture:o,registerComputeTexture:a,getCpuValue:i,getMapInfo:l,onCleanup:c,onResize:d,dimensions:u}=t;if(!s)return null;let p=r?.root;if(!p)return null;let _=o(s),f=Math.max(1,Math.round(u.width)),h=Math.max(1,Math.round(u.height)),g=Math.max(Wh,640),y=h>0?f/h:1,b=Math.max(8,y>=1?g:Math.round(g*y)),x=Math.max(8,y>=1?Math.round(g/y):g),P=(globalThis.__TYPEGPU_AUTONAME__??(F=>F))(p.createTexture({size:[b,x],format:nce}).$usage(\"storage\",\"sampled\"),\"outputTex\");c(()=>P.destroy());let C=a(P);d(({width:F,height:q})=>{f=Math.max(1,Math.round(F)),h=Math.max(1,Math.round(q))});let E=()=>{let F=(i(e.rotation.name)??0)*ace;return{highlightGain:i(e.gain.name)??4,highlightThreshold:i(e.threshold.name)??.6,rotCos:Math.cos(F),rotSin:Math.sin(F),chromaticFringe:i(e.fringe.name)??.2,catEye:e.catEye?i(e.catEye.name)??0:0}},R=l(e.radius.name);if(R){let F=Wse(b,x,100,R.channel),q=(globalThis.__TYPEGPU_AUTONAME__??(Q=>Q))(p.createUniform(F.Params),\"kernelParams\"),Y=(globalThis.__TYPEGPU_AUTONAME__??(Q=>Q))(p.createUniform(F.TapArray),\"tapsUniform\"),V=aC({shape:e.shape,blades:e.blades},i,Y),X=wr(p,(Q=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(Q.f=((te,J)=>{\"use gpu\";F.kernel(te,J)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"graph.kernel\",[\"cx\",\"cy\"]]]]},externals:{\"graph.kernel\":()=>F.kernel}})&&Q.f)({}),{size:[b,x]});return{outputs:{blurredTexture:C},bindInputs:Q=>{let te=Q(_.key),J=Q(R.sourceTexture.key);if(!te||!J)return;let oe=p.createBindGroup(F.layout,{input:te.texture,source:J.texture,output:P,params:q.buffer,taps:Y.buffer});X=X.with(oe)},getComputeNodes:()=>{V();let Q=R.window();return q.write({...E(),inputWidth:f,inputHeight:h,inputMin:Q.inputMin,inputMax:Q.inputMax,outputMin:Q.outputMin,outputMax:Q.outputMax,curve:Q.curve}),[X]}}}let U=Yse(b,x,100),L=(globalThis.__TYPEGPU_AUTONAME__??(F=>F))(p.createUniform(U.Params),\"kernelParams\"),k=(globalThis.__TYPEGPU_AUTONAME__??(F=>F))(p.createUniform(U.TapArray),\"tapsUniform\"),D=aC({shape:e.shape,blades:e.blades},i,k),O=wr(p,(F=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(F.f=((q,Y)=>{\"use gpu\";U.kernel(q,Y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"graph.kernel\",[\"cx\",\"cy\"]]]]},externals:{\"graph.kernel\":()=>U.kernel}})&&F.f)({}),{size:[b,x]});return{outputs:{blurredTexture:C},bindInputs:F=>{let q=F(_.key);if(!q)return;let Y=p.createBindGroup(U.layout,{input:q.texture,output:P,params:L.buffer,taps:k.buffer});O=O.with(Y)},getComputeNodes:()=>{D();let F=i(e.radius.name)??50;return L.write({radius:Tse(F),...E(),inputWidth:f,inputHeight:h}),[O]}}},gpu:{fragment:({childNode:t,computeOutputs:s,ctx:r,convertToTexture:o})=>{if(!t)return xt;let a=s?.blurredTexture;return a?W(Yt,\"unpremultiplyAlpha\",[a.sample(r.uv)]):W(Yt,\"unpremultiplyAlpha\",[o(t).sample(r.uv)])}}}}function t8(e){return Bn(vee,\"hueRotate\",[e])}function s8(e){return Bn(hee,\"saturate\",[e])}function r8(e){return Bn(gee,\"vibrance\",[e])}function o8(e){return Bn(yee,\"brightnessContrast\",[e.brightness,e.contrast])}function a8(e){return Bn(bee,\"exposure\",[e])}function n8(){return Bn(xee,\"grayscale\",[])}function i8(){return Bn(Aee,\"invert\",[])}function l8(e){return Bn(wee,\"solarize\",[e.threshold,e.strength])}function c8(e){return Bn(Tee,\"posterize\",[e])}function d8(e){return{kind:\"pointwise\",body:t=>Number(t[e.preserveLuminosity.name])>0?{fn:Pee,hint:\"tintPreserveLuma\"}:{fn:See,hint:\"tintPlain\"},args:t=>[At(e.color,t),At(e.amount,t)]}}function u8(e){return{kind:\"pointwise\",body:{fn:Ree,hint:\"filmGrain\"},args:t=>[t.ctx.uv,t.ctx.viewportSize,At(e.strength,t),At(e.bias,t),t.uniforms.animTime],setup:t=>{let s=0;t.onBeforeRender(({deltaTime:r})=>{let o=t.getCpuValue(e.animated.name)>0?1:0;s+=r*o*10,t.setExtraField(\"animTime\",s)})}}}var ice=e=>typeof e==\"number\"?e:e===\"alpha\"?1:0,lce=e=>typeof e==\"number\"?e:e===\"custom\"?1:0;function p8(e){return{kind:\"pointwise\",body:t=>ice(t[e.source.name])===1?{fn:Lee,hint:\"isolinesFromAlpha\"}:{fn:Dee,hint:\"isolinesFromLuma\"},args:t=>[e.levels,e.lineWidth,e.softness,e.gamma,e.invert].map(s=>At(s,t)),compose:(t,s)=>{let r=lce(s.propValues[e.colorMode.name])===1;return W(Uee,\"contourCompose\",[r?me(e.backgroundColor,s):xt,r?me(e.lineColor,s):Ct(s.childNode.member(\"rgb\"),$e(1)),t])}}}function y2(e,t){return No[t.propValues[e.name]??0]??Ar}function m8(e){return Bn(Cee,\"duotoneT\",[e.blend],{compose:(t,s)=>Ct(W(y2(e.colorSpace,s),\"mixColors\",[me(e.colorA,s),me(e.colorB,s),t]).member(\"rgb\"),s.childNode.member(\"a\"))})}function _8(e){return Bn(Mee,\"tritoneFactors\",[e.blendMid],{compose:(t,s)=>{let r=t.member(\"x\"),o=t.member(\"y\"),a=t.member(\"z\"),i=y2(e.colorSpace,s),l=me(e.colorA,s),c=me(e.colorB,s),d=me(e.colorC,s);return Ct(W(i,\"mixColors\",[W(i,\"mixColors\",[l,c,r]),W(i,\"mixColors\",[c,d,o]),a]).member(\"rgb\"),s.childNode.member(\"a\"))}})}var nC={0:{a:[.5,.5,.5],b:[.5,.5,.5],c:[1,1,1],d:[0,.333,.667]},1:{a:[.5,.5,.5],b:[.5,.5,.5],c:[1,1,1],d:[0,.1,.2]},2:{a:[.5,.5,.5],b:[.5,.5,.5],c:[1,1,1],d:[.3,.2,.2]},3:{a:[.5,.5,.5],b:[.5,.5,.5],c:[1,1,1],d:[.8,.9,.3]},4:{a:[.5,.5,.5],b:[.25,.25,.25],c:[1,1,1],d:[0,.25,.5]},5:{a:[.5,.5,.5],b:[.5,.5,.5],c:[2,1,1],d:[.5,.2,.25]}},cce={rainbow:0,sunset:1,ocean:2,fire:3,pastel:4,neon:5,custom:6},A1=e=>ca(`vec3f(${fo(e[0])}, ${fo(e[1])}, ${fo(e[2])})`);function f8(e){return Bn(Eee,\"gradientMapT\",[e.blackPoint,e.whitePoint,e.contrast],{compose:(t,s)=>{let{childNode:r,propValues:o,ctx:a}=s,i=me(e.strength,s),l=me(e.speed,s).mul(a.time),c=cce[o[e.palette.name]??\"rainbow\"]??0;if(c!==6){let y=nC[c]??nC[0];return W(eS,\"gradientMapCompose\",[r,W(kee,\"gradientMapCosine\",[t,l,A1(y.a),A1(y.b),A1(y.c),A1(y.d)]),i])}let d=y2(e.colorSpace,s),u=W(Bee,\"gradientMapCustomPhase\",[t,l]),p=u.member(\"x\"),_=u.member(\"y\"),f=me(e.colorLow,s),h=me(e.colorMid,s),g=me(e.colorHigh,s);return W(eS,\"gradientMapCompose\",[r,W(zee,\"gradientMapSelect\",[W(d,\"mixColors\",[f,h,p]),W(d,\"mixColors\",[h,g,p]),W(d,\"mixColors\",[g,f,p]),_]).member(\"rgb\"),i])}})}function dce(e){let t=e-1,s=1/(e*e),r=1/e;return w.fn([S,n],m)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=Xm(G(a.xyz,A(0,0,0),A(1,1,1))),c=__tsover_mul(l.z,n(t)),d=G(__tsover_add(ie(c),i),n(0),n(t));return m(__tsover_mul(__tsover_add(__tsover_add(__tsover_mul(d,n(e)),.5),__tsover_mul(l.x,n(t))),n(s)),__tsover_mul(__tsover_add(.5,__tsover_mul(l.y,n(t))),n(r)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"hi\"}],body:[0,[[13,\"enc\",[6,\"tonemap.linearToSrgb\",[[6,\"std.clamp\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]]]],[13,\"slice\",[1,[7,\"enc\",\"z\"],\"*\",[6,\"d.f32\",[\"LUT_MAX\"]]]],[13,\"s\",[6,\"std.clamp\",[[1,[6,\"std.floor\",[\"slice\"]],\"+\",\"hi\"],[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[\"LUT_MAX\"]]]]],[13,\"u\",[1,[1,[1,[1,\"s\",\"*\",[6,\"d.f32\",[\"size\"]]],\"+\",[5,\"0.5\"]],\"+\",[1,[7,\"enc\",\"x\"],\"*\",[6,\"d.f32\",[\"LUT_MAX\"]]]],\"*\",[6,\"d.f32\",[\"INV_ATLAS_W\"]]]],[13,\"v\",[1,[1,[5,\"0.5\"],\"+\",[1,[7,\"enc\",\"y\"],\"*\",[6,\"d.f32\",[\"LUT_MAX\"]]]],\"*\",[6,\"d.f32\",[\"INV_LUT_N\"]]]],[10,[6,\"d.vec2f\",[\"u\",\"v\"]]]]]},externals:{\"tonemap.linearToSrgb\":()=>Xm,\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.f32\":()=>n,LUT_MAX:()=>t,\"std.floor\":()=>ie,size:()=>e,INV_ATLAS_W:()=>s,INV_LUT_N:()=>r,\"d.vec2f\":()=>m}})&&o.f)({})).$name(\"lutAtlasUv\")}function uce(e){let t=e-1;return w.fn([S,S,S,n],S)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o,a,i)=>{\"use gpu\";let l=Xm(G(r.xyz,A(0,0,0),A(1,1,1))),c=__tsover_mul(l.z,n(t)),d=__tsover_sub(c,ie(c)),u=Iu(K(o.xyz,a.xyz,A(d,d,d))),p=K(r.xyz,u,A(i,i,i));return S(p.x,p.y,p.z,r.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"color\"},{type:\"i\",name:\"tapLo\"},{type:\"i\",name:\"tapHi\"},{type:\"i\",name:\"strength\"}],body:[0,[[13,\"enc\",[6,\"tonemap.linearToSrgb\",[[6,\"std.clamp\",[[7,\"color\",\"xyz\"],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]]]],[13,\"slice\",[1,[7,\"enc\",\"z\"],\"*\",[6,\"d.f32\",[\"LUT_MAX\"]]]],[13,\"f\",[1,\"slice\",\"-\",[6,\"std.floor\",[\"slice\"]]]],[13,\"graded\",[6,\"tonemap.srgbToLinear\",[[6,\"std.mix\",[[7,\"tapLo\",\"xyz\"],[7,\"tapHi\",\"xyz\"],[6,\"d.vec3f\",[\"f\",\"f\",\"f\"]]]]]]],[13,\"rgb\",[6,\"std.mix\",[[7,\"color\",\"xyz\"],\"graded\",[6,\"d.vec3f\",[\"strength\",\"strength\",\"strength\"]]]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],[7,\"color\",\"w\"]]]]]]},externals:{\"tonemap.linearToSrgb\":()=>Xm,\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.f32\":()=>n,LUT_MAX:()=>t,\"std.floor\":()=>ie,\"tonemap.srgbToLinear\":()=>Iu,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&s.f)({})).$name(\"lutAtlasApply\")}var iC=new Map;function pce(e){let t=iC.get(e);return t||(t={uv:dce(e),apply:uce(e)},iC.set(e,t)),t}function v8(e){let t=pce(e.size);return(s,r)=>{let{propValues:o,uniforms:a,createDataTexture:i,registerMediaTexture:l,onCleanup:c}=r,d=o[e.select.name]??e.fallback,u=i({width:e.size*e.size,height:e.size,format:\"rgba8unorm\",data:e.decode(d),label:`${e.label}-${d}`});c(()=>u.destroy());let p=l(()=>u.texture),_=p.sample(W(t.uv,\"lutAtlasUvLo\",[s,$e(0)]),\"linearClamp\"),f=p.sample(W(t.uv,\"lutAtlasUvHi\",[s,$e(1)]),\"linearClamp\");return W(t.apply,\"lutAtlasApply\",[s,_,f,a[e.strength.name]])}}var Gm=Po,lC=.35,h8=w.fn([n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=__tsover_mul(.18,t),r=vt(re(ae(__tsover_mul(s,Gm))),10),o=vt(re(ae(__tsover_mul(__tsover_sub(s,.15),Gm))),10);return G(__tsover_add(r,__tsover_mul(.6,o)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"animTime\"}],body:[0,[[13,\"time\",[1,[5,\"0.18\"],\"*\",\"animTime\"]],[13,\"first\",[6,\"std.pow\",[[6,\"std.abs\",[[6,\"std.sin\",[[1,\"time\",\"*\",\"TWO_PI\"]]]]],[5,\"10\"]]]],[13,\"second\",[6,\"std.pow\",[[6,\"std.abs\",[[6,\"std.sin\",[[1,[1,\"time\",\"-\",[5,\"0.15\"]],\"*\",\"TWO_PI\"]]]]],[5,\"10\"]]]],[10,[6,\"std.clamp\",[[1,\"first\",\"+\",[1,[5,\"0.6\"],\"*\",\"second\"]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.pow\":()=>vt,\"std.abs\":()=>re,\"std.sin\":()=>ae,TWO_PI:()=>Gm,\"std.clamp\":()=>G}})&&e.f)({})),g8=w.fn([n,m,m,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=__tsover_add(__tsover_mul(d,1.2),131),f=B(__tsover_mul(__tsover_mul(.5,a),lC),.002),h=re(__tsover_add(__tsover_div(t,B(o,.001)),K(f,n(0),i))),g=K(f,__tsover_mul(3,f),i),y=__tsover_div(2,__tsover_mul(r.y,B(o,.001))),b=ye(K(g,__tsover_mul(g,-1),i),__tsover_add(g,y)),x=B(K(g,__tsover_mul(g,-1),i),__tsover_add(g,y)),P=__tsover_sub(1,be(b,x,h));P=vt(G(P,0,1),__tsover_add(1,i));let C=m(__tsover_sub(s.x,.5),__tsover_sub(s.y,.5)).mul(__tsover_add(.5,__tsover_mul(2.5,l))),E=__tsover_mul(.5,_),R=Aa(m(__tsover_add(__tsover_mul(C.x,2.7),E),__tsover_add(__tsover_mul(C.y,2.7),E))),U=Aa(m(__tsover_sub(__tsover_mul(C.x,3.4),E),__tsover_sub(__tsover_mul(C.y,3.4),E))),L=__tsover_sub(G(__tsover_mul(3,R),0,1),U),k=G(__tsover_add(f,.2),.1,.4),D=vt(G(__tsover_sub(1,be(__tsover_mul(k,-1),__tsover_add(k,y),h)),0,1),2);L=__tsover_mul(L,D),L=__tsover_mul(__tsover_mul(__tsover_mul(30,L),L),__tsover_mul(__tsover_mul(.5,c),c)),L=__tsover_mul(L,K(n(1),p,u)),P=G(__tsover_add(P,G(L,0,1)),0,1);let O=__tsover_div(Xt(__tsover_sub(s.y,.5),__tsover_sub(s.x,.5)),Gm);return m(P,O)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fieldRaw\"},{type:\"i\",name:\"sdfUV\"},{type:\"i\",name:\"viewport\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"smokeSize\"},{type:\"i\",name:\"smokeAmt\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"pulse\"},{type:\"i\",name:\"beat\"}],body:[0,[[13,\"t\",[1,[1,\"animTime\",\"*\",[5,\"1.2\"]],\"+\",[5,\"131\"]]],[13,\"th\",[6,\"std.max\",[[1,[1,[5,\"0.5\"],\"*\",\"thickness\"],\"*\",\"BAND_THICKNESS_REF\"],[5,\"0.002\"]]]],[13,\"f\",[1,[1,\"fieldRaw\",\"/\",[6,\"std.max\",[\"scale\",[5,\"0.001\"]]]],\"+\",[6,\"std.mix\",[\"th\",[6,\"d.f32\",[[5,\"0\"]]],\"softness\"]]]],[13,\"bd\",[6,\"std.abs\",[\"f\"]]],[13,\"borderTh\",[6,\"std.mix\",[\"th\",[1,[5,\"3\"],\"*\",\"th\"],\"softness\"]]],[13,\"aa\",[1,[5,\"2\"],\"/\",[1,[7,\"viewport\",\"y\"],\"*\",[6,\"std.max\",[\"scale\",[5,\"0.001\"]]]]]],[13,\"e0\",[6,\"std.min\",[[6,\"std.mix\",[\"borderTh\",[1,\"borderTh\",\"*\",[4,\"-\",[5,\"1\"]]],\"softness\"]],[1,\"borderTh\",\"+\",\"aa\"]]]],[13,\"e1\",[6,\"std.max\",[[6,\"std.mix\",[\"borderTh\",[1,\"borderTh\",\"*\",[4,\"-\",[5,\"1\"]]],\"softness\"]],[1,\"borderTh\",\"+\",\"aa\"]]]],[12,\"border\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[\"e0\",\"e1\",\"bd\"]]]],[2,\"border\",\"=\",[6,\"std.pow\",[[6,\"std.clamp\",[\"border\",[5,\"0\"],[5,\"1\"]]],[1,[5,\"1\"],\"+\",\"softness\"]]]],[13,\"suv\",[6,[7,[6,\"d.vec2f\",[[1,[7,\"sdfUV\",\"x\"],\"-\",[5,\"0.5\"]],[1,[7,\"sdfUV\",\"y\"],\"-\",[5,\"0.5\"]]]],\"mul\"],[[1,[5,\"0.5\"],\"+\",[1,[5,\"2.5\"],\"*\",\"smokeSize\"]]]]],[13,\"drift\",[1,[5,\"0.5\"],\"*\",\"t\"]],[13,\"n1\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,[7,\"suv\",\"x\"],\"*\",[5,\"2.7\"]],\"+\",\"drift\"],[1,[1,[7,\"suv\",\"y\"],\"*\",[5,\"2.7\"]],\"+\",\"drift\"]]]]]],[13,\"n2\",[6,\"noise.value12\",[[6,\"d.vec2f\",[[1,[1,[7,\"suv\",\"x\"],\"*\",[5,\"3.4\"]],\"-\",\"drift\"],[1,[1,[7,\"suv\",\"y\"],\"*\",[5,\"3.4\"]],\"-\",\"drift\"]]]]]],[12,\"smoke\",[1,[6,\"std.clamp\",[[1,[5,\"3\"],\"*\",\"n1\"],[5,\"0\"],[5,\"1\"]]],\"-\",\"n2\"]],[13,\"smokeTh\",[6,\"std.clamp\",[[1,\"th\",\"+\",[5,\"0.2\"]],[5,\"0.1\"],[5,\"0.4\"]]]],[13,\"smokeBand\",[6,\"std.pow\",[[6,\"std.clamp\",[[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[1,\"smokeTh\",\"*\",[4,\"-\",[5,\"1\"]]],[1,\"smokeTh\",\"+\",\"aa\"],\"bd\"]]],[5,\"0\"],[5,\"1\"]]],[5,\"2\"]]]],[2,\"smoke\",\"=\",[1,\"smoke\",\"*\",\"smokeBand\"]],[2,\"smoke\",\"=\",[1,[1,[1,[5,\"30\"],\"*\",\"smoke\"],\"*\",\"smoke\"],\"*\",[1,[1,[5,\"0.5\"],\"*\",\"smokeAmt\"],\"*\",\"smokeAmt\"]]],[2,\"smoke\",\"=\",[1,\"smoke\",\"*\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],\"beat\",\"pulse\"]]]],[2,\"border\",\"=\",[6,\"std.clamp\",[[1,\"border\",\"+\",[6,\"std.clamp\",[\"smoke\",[5,\"0\"],[5,\"1\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"angle01\",[1,[6,\"std.atan2\",[[1,[7,\"sdfUV\",\"y\"],\"-\",[5,\"0.5\"]],[1,[7,\"sdfUV\",\"x\"],\"-\",[5,\"0.5\"]]]],\"/\",\"TWO_PI\"]],[10,[6,\"d.vec2f\",[\"border\",\"angle01\"]]]]]},externals:{\"std.max\":()=>B,BAND_THICKNESS_REF:()=>lC,\"std.mix\":()=>K,\"d.f32\":()=>n,\"std.abs\":()=>re,\"std.min\":()=>ye,\"std.smoothstep\":()=>be,\"std.pow\":()=>vt,\"std.clamp\":()=>G,\"d.vec2f\":()=>m,\"noise.value12\":()=>Aa,\"std.atan2\":()=>Xt,TWO_PI:()=>Gm}})&&e.f)({})),y8=w.fn([n,n,n,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p)=>{\"use gpu\";let _=__tsover_add(__tsover_mul(r,1.2),131),f=__tsover_add(1,__tsover_mul(__tsover_add(1,__tsover_mul(4,d)),c)),h=n(0),g=n(0);for(let y=0;y<5;y++){let b=n(y),x=N(n(0),n(1),b<a),P=Pr(m(__tsover_add(__tsover_add(__tsover_mul(b,10),2),__tsover_mul(u,7.31)),__tsover_add(__tsover_add(40,p),__tsover_mul(u,3.17)))),C=__tsover_add(.1,__tsover_mul(.15,re(__tsover_mul(ae(__tsover_mul(b,__tsover_add(2,p))),pe(__tsover_mul(b,__tsover_add(2,__tsover_mul(2.5,p)))))))),E=N(n(1),n(-1),P.y<.5),R=__tsover_mul(__tsover_add(__tsover_mul(C,_),__tsover_mul(P.x,3)),E),U=re(__tsover_sub(p,__tsover_mul(2,ie(__tsover_mul(p,.5)))))<.5,L=ae(__tsover_add(_,__tsover_mul(b,__tsover_sub(5,__tsover_mul(1.5,p))))),k=pe(__tsover_add(_,__tsover_mul(b,__tsover_add(3,__tsover_mul(1.3,p))))),D=__tsover_add(.5,__tsover_mul(.5,N(k,L,U))),O=G(__tsover_sub(__tsover_mul(2,l),P.x),0,1);D=K(D,o,O);let F=Re(__tsover_add(t,R)),q=__tsover_add(__tsover_add(.05,__tsover_mul(__tsover_mul(.6,i),i)),__tsover_mul(.05,P.x));q=K(q,.1,O);let Y=__tsover_mul(be(__tsover_sub(.5,q),.5,F),__tsover_sub(1,be(.5,__tsover_add(.5,q),F)));Y=__tsover_mul(G(__tsover_mul(__tsover_mul(__tsover_mul(Y,D),s),f),0,1),x),h=__tsover_add(h,__tsover_mul(__tsover_sub(1,h),Y)),g=__tsover_add(g,Y)}return m(h,g)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"angle01\"},{type:\"i\",name:\"border\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"beat\"},{type:\"i\",name:\"spotsF\"},{type:\"i\",name:\"spotSize\"},{type:\"i\",name:\"pulse\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"softness\"},{type:\"i\",name:\"seed\"},{type:\"i\",name:\"colorIdxF\"}],body:[0,[[13,\"t\",[1,[1,\"animTime\",\"*\",[5,\"1.2\"]],\"+\",[5,\"131\"]]],[13,\"intensityEff\",[1,[5,\"1\"],\"+\",[1,[1,[5,\"1\"],\"+\",[1,[5,\"4\"],\"*\",\"softness\"]],\"*\",\"intensity\"]]],[12,\"overAcc\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"addAcc\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"s\",[5,\"0\"]],[1,\"s\",\"<\",[5,\"5\"]],[102,\"++\",\"s\"],[0,[[13,\"sF\",[6,\"d.f32\",[\"s\"]]],[13,\"gate\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"sF\",\"<\",\"spotsF\"]]]],[13,\"randVal\",[6,\"noise.hash22\",[[6,\"d.vec2f\",[[1,[1,[1,\"sF\",\"*\",[5,\"10\"]],\"+\",[5,\"2\"]],\"+\",[1,\"seed\",\"*\",[5,\"7.31\"]]],[1,[1,[5,\"40\"],\"+\",\"colorIdxF\"],\"+\",[1,\"seed\",\"*\",[5,\"3.17\"]]]]]]]],[13,\"speedFactor\",[1,[5,\"0.1\"],\"+\",[1,[5,\"0.15\"],\"*\",[6,\"std.abs\",[[1,[6,\"std.sin\",[[1,\"sF\",\"*\",[1,[5,\"2\"],\"+\",\"colorIdxF\"]]]],\"*\",[6,\"std.cos\",[[1,\"sF\",\"*\",[1,[5,\"2\"],\"+\",[1,[5,\"2.5\"],\"*\",\"colorIdxF\"]]]]]]]]]]],[13,\"dir\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[1,[7,\"randVal\",\"y\"],\"<\",[5,\"0.5\"]]]]],[13,\"sTime\",[1,[1,[1,\"speedFactor\",\"*\",\"t\"],\"+\",[1,[7,\"randVal\",\"x\"],\"*\",[5,\"3\"]]],\"*\",\"dir\"]],[13,\"evenCond\",[1,[6,\"std.abs\",[[1,\"colorIdxF\",\"-\",[1,[5,\"2\"],\"*\",[6,\"std.floor\",[[1,\"colorIdxF\",\"*\",[5,\"0.5\"]]]]]]]],\"<\",[5,\"0.5\"]]],[13,\"sinVal\",[6,\"std.sin\",[[1,\"t\",\"+\",[1,\"sF\",\"*\",[1,[5,\"5\"],\"-\",[1,[5,\"1.5\"],\"*\",\"colorIdxF\"]]]]]]],[13,\"cosVal\",[6,\"std.cos\",[[1,\"t\",\"+\",[1,\"sF\",\"*\",[1,[5,\"3\"],\"+\",[1,[5,\"1.3\"],\"*\",\"colorIdxF\"]]]]]]],[12,\"mask\",[1,[5,\"0.5\"],\"+\",[1,[5,\"0.5\"],\"*\",[6,\"std.select\",[\"cosVal\",\"sinVal\",\"evenCond\"]]]]],[13,\"pWeight\",[6,\"std.clamp\",[[1,[1,[5,\"2\"],\"*\",\"pulse\"],\"-\",[7,\"randVal\",\"x\"]],[5,\"0\"],[5,\"1\"]]]],[2,\"mask\",\"=\",[6,\"std.mix\",[\"mask\",\"beat\",\"pWeight\"]]],[13,\"atg\",[6,\"std.fract\",[[1,\"angle01\",\"+\",\"sTime\"]]]],[12,\"ss\",[1,[1,[5,\"0.05\"],\"+\",[1,[1,[5,\"0.6\"],\"*\",\"spotSize\"],\"*\",\"spotSize\"]],\"+\",[1,[5,\"0.05\"],\"*\",[7,\"randVal\",\"x\"]]]],[2,\"ss\",\"=\",[6,\"std.mix\",[\"ss\",[5,\"0.1\"],\"pWeight\"]]],[12,\"sector\",[1,[6,\"std.smoothstep\",[[1,[5,\"0.5\"],\"-\",\"ss\"],[5,\"0.5\"],\"atg\"]],\"*\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0.5\"],[1,[5,\"0.5\"],\"+\",\"ss\"],\"atg\"]]]]],[2,\"sector\",\"=\",[1,[6,\"std.clamp\",[[1,[1,[1,\"sector\",\"*\",\"mask\"],\"*\",\"border\"],\"*\",\"intensityEff\"],[5,\"0\"],[5,\"1\"]]],\"*\",\"gate\"]],[2,\"overAcc\",\"=\",[1,\"overAcc\",\"+\",[1,[1,[5,\"1\"],\"-\",\"overAcc\"],\"*\",\"sector\"]]],[2,\"addAcc\",\"=\",[1,\"addAcc\",\"+\",\"sector\"]]]]],[10,[6,\"d.vec2f\",[\"overAcc\",\"addAcc\"]]]]]},externals:{\"d.f32\":()=>n,\"std.select\":()=>N,\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.sin\":()=>ae,\"std.cos\":()=>pe,\"std.floor\":()=>ie,\"std.clamp\":()=>G,\"std.mix\":()=>K,\"std.fract\":()=>Re,\"std.smoothstep\":()=>be}})&&e.f)({})),Ql=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({rgb:A,w:n,ba:n,ac:A,aa:n}),\"EdgeGlowAccum\"),b8=w.fn([],Ql)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(()=>{\"use gpu\";return Ql({rgb:A(0,0,0),w:n(0),ba:n(0),ac:A(0,0,0),aa:n(0)})}),{v:2,name:void 0,ast:{params:[],body:[0,[[10,[6,\"EdgeGlowAccum\",[[104,{rgb:[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],w:[6,\"d.f32\",[[5,\"0\"]]],ba:[6,\"d.f32\",[[5,\"0\"]]],ac:[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],aa:[6,\"d.f32\",[[5,\"0\"]]]}]]]]]]},externals:{EdgeGlowAccum:()=>Ql,\"d.vec3f\":()=>A,\"d.f32\":()=>n}})&&e.f)({})),cC=new Map;function mce(e){let t=No[e]??Ar;return w.fn([Ql,S,m],Ql)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=((r,o,a)=>{\"use gpu\";let i=__tsover_mul(o.w,a.x),l=__tsover_div(i,B(__tsover_add(r.w,i),1e-5)),c=t(S(r.rgb,1),S(o.xyz,1),l),d=__tsover_add(r.ba,__tsover_mul(__tsover_sub(1,r.ba),i)),u=r.ac.add(o.xyz.mul(__tsover_mul(o.w,a.y))),p=__tsover_add(r.aa,__tsover_mul(o.w,a.y));return Ql({rgb:c.xyz,w:__tsover_add(r.w,i),ba:d,ac:u,aa:p})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"st\"},{type:\"i\",name:\"color\"},{type:\"i\",name:\"sectors\"}],body:[0,[[13,\"wNew\",[1,[7,\"color\",\"w\"],\"*\",[7,\"sectors\",\"x\"]]],[13,\"t\",[1,\"wNew\",\"/\",[6,\"std.max\",[[1,[7,\"st\",\"w\"],\"+\",\"wNew\"],[5,\"0.00001\"]]]]],[13,\"mixed\",[6,\"variant\",[[6,\"d.vec4f\",[[7,\"st\",\"rgb\"],[5,\"1\"]]],[6,\"d.vec4f\",[[7,\"color\",\"xyz\"],[5,\"1\"]]],\"t\"]]],[13,\"ba\",[1,[7,\"st\",\"ba\"],\"+\",[1,[1,[5,\"1\"],\"-\",[7,\"st\",\"ba\"]],\"*\",\"wNew\"]]],[13,\"ac\",[6,[7,[7,\"st\",\"ac\"],\"add\"],[[6,[7,[7,\"color\",\"xyz\"],\"mul\"],[[1,[7,\"color\",\"w\"],\"*\",[7,\"sectors\",\"y\"]]]]]]],[13,\"aa\",[1,[7,\"st\",\"aa\"],\"+\",[1,[7,\"color\",\"w\"],\"*\",[7,\"sectors\",\"y\"]]]],[10,[6,\"EdgeGlowAccum\",[[104,{rgb:[7,\"mixed\",\"xyz\"],w:[1,[7,\"st\",\"w\"],\"+\",\"wNew\"],ba:\"ba\",ac:\"ac\",aa:\"aa\"}]]]]]]},externals:{\"std.max\":()=>B,variant:()=>t,\"d.vec4f\":()=>S,EdgeGlowAccum:()=>Ql}})&&s.f)({})).$name(`edgeGlowAccumColor_${e}`)}function x8(e){let t=cC.get(e);return t||(t=mce(e),cC.set(e,t)),t}var A8=w.fn([Fe(S,8),n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=t[j(s)];return S(r.x,r.y,r.z,r.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"colors\"},{type:\"i\",name:\"idx\"}],body:[0,[[13,\"c\",[8,\"colors\",[6,\"d.i32\",[\"idx\"]]]],[10,[6,\"d.vec4f\",[[7,\"c\",\"x\"],[7,\"c\",\"y\"],[7,\"c\",\"z\"],[7,\"c\",\"w\"]]]]]]},externals:{\"d.i32\":()=>j,\"d.vec4f\":()=>S}})&&e.f)({})),w8=w.fn([Ql,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_mul(s,4),o=K(t.rgb.mul(t.ba),t.ac,A(r)),a=G(K(t.ba,t.aa,r),0,1);return S(G(o.div(B(a,1e-4)),A(0),A(1)),a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"st\"},{type:\"i\",name:\"bloom\"}],body:[0,[[13,\"k\",[1,\"bloom\",\"*\",[5,\"4\"]]],[13,\"blendPremult\",[6,[7,[7,\"st\",\"rgb\"],\"mul\"],[[7,\"st\",\"ba\"]]]],[13,\"accumC\",[6,\"std.mix\",[\"blendPremult\",[7,\"st\",\"ac\"],[6,\"d.vec3f\",[\"k\"]]]]],[13,\"accumA\",[6,\"std.clamp\",[[6,\"std.mix\",[[7,\"st\",\"ba\"],[7,\"st\",\"aa\"],\"k\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"straight\",[6,\"std.clamp\",[[6,[7,\"accumC\",\"div\"],[[6,\"std.max\",[\"accumA\",[5,\"0.0001\"]]]]],[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[10,[6,\"d.vec4f\",[\"straight\",\"accumA\"]]]]]},externals:{\"std.mix\":()=>K,\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&e.f)({}));var T8=\"rgba16float\";function _ce(e,t){let s=(globalThis.__TYPEGPU_AUTONAME__??(r=>r))(w.bindGroupLayout({cellPos:{storage:Fe(n,e*2),access:\"readonly\"},voronoiTex:{storageTexture:zt(T8,\"write-only\")}}),\"layout\");return{layout:s,kernel:w.fn([M,M])((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a)=>{\"use gpu\";let i=__tsover_div(__tsover_add(n(o),.5),t),l=__tsover_div(__tsover_add(n(a),.5),t),c=n(99999),d=n(99999),u=n(0),p=n(0);for(let _=0;_<e;_++){let f=s.$.cellPos[__tsover_mul(_,2)],h=s.$.cellPos[__tsover_add(__tsover_mul(_,2),1)],g=__tsover_sub(i,f),y=__tsover_sub(l,h),b=__tsover_add(__tsover_mul(g,g),__tsover_mul(y,y));b<c?(d=c,p=u,c=b,u=n(_)):b<d&&(d=b,p=n(_))}lt(s.$.voronoiTex,fe(o,a),S(u,p,0,0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"u\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",\"size\"]],[13,\"v\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",\"size\"]],[12,\"d1\",[6,\"d.f32\",[[5,\"99999\"]]]],[12,\"d2\",[6,\"d.f32\",[[5,\"99999\"]]]],[12,\"idx1\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"idx2\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"count\"],[102,\"++\",\"i\"],[0,[[13,\"cellX\",[8,[7,[7,\"layout\",\"$\"],\"cellPos\"],[1,\"i\",\"*\",[5,\"2\"]]]],[13,\"cellY\",[8,[7,[7,\"layout\",\"$\"],\"cellPos\"],[1,[1,\"i\",\"*\",[5,\"2\"]],\"+\",[5,\"1\"]]]],[13,\"ddx\",[1,\"u\",\"-\",\"cellX\"]],[13,\"ddy\",[1,\"v\",\"-\",\"cellY\"]],[13,\"distSq\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]],[11,[1,\"distSq\",\"<\",\"d1\"],[0,[[2,\"d2\",\"=\",\"d1\"],[2,\"idx2\",\"=\",\"idx1\"],[2,\"d1\",\"=\",\"distSq\"],[2,\"idx1\",\"=\",[6,\"d.f32\",[\"i\"]]]]],[0,[[11,[1,\"distSq\",\"<\",\"d2\"],[0,[[2,\"d2\",\"=\",\"distSq\"],[2,\"idx2\",\"=\",[6,\"d.f32\",[\"i\"]]]]]]]]]]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"voronoiTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[\"idx1\",\"idx2\",[5,\"0\"],[5,\"0\"]]]]]]]},externals:{\"d.f32\":()=>n,size:()=>t,count:()=>e,layout:()=>s,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&r.f)({})).$name(\"voronoiNearest2\")}}var dC=new Map;function fce(e,t){let s=`${e}|${t}`,r=dC.get(s);return r||(r=_ce(e,t),dC.set(s,r)),r}var uC=new Map;function vce(e){return w.fn([n,n],m)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r)=>{\"use gpu\";return m(__tsover_div(__tsover_add(__tsover_add(__tsover_mul(s,4),r),.5),e),.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"idx\"},{type:\"i\",name:\"field\"}],body:[0,[[10,[6,\"d.vec2f\",[[1,[1,[1,[1,\"idx\",\"*\",[5,\"4\"]],\"+\",\"field\"],\"+\",[5,\"0.5\"]],\"/\",\"laneWidth\"],[5,\"0.5\"]]]]]]},externals:{\"d.vec2f\":()=>m,laneWidth:()=>e}})&&t.f)({}))}function S8(e){let t=uC.get(e);return t||(t=vce(e),uC.set(e,t)),t}var J0=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({crackIntensity:n,edgeNormal:m,displacedUV:m,disp:m}),\"CrackGeom\"),P8=w.fn([m,n,n,n,n,n,n,n],J0)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let d=m(s,r),u=m(o,a),p=m(i,l),_=t.sub(d),f=he(_),h=he(t.sub(u)),g=he(p),y=__tsover_sub(h,f),b=__tsover_mul(c,.005),x=__tsover_sub(1,be(0,b,y)),P=be(0,.01,g),C=__tsover_mul(x,P),E=_.div(B(f,1e-5));return J0({crackIntensity:C,edgeNormal:m(__tsover_mul(E.y,-1),E.x),displacedUV:t.sub(p),disp:p})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"nPosX\"},{type:\"i\",name:\"nPosY\"},{type:\"i\",name:\"sPosX\"},{type:\"i\",name:\"sPosY\"},{type:\"i\",name:\"dispX\"},{type:\"i\",name:\"dispY\"},{type:\"i\",name:\"crackWidth\"}],body:[0,[[13,\"nearestPos\",[6,\"d.vec2f\",[\"nPosX\",\"nPosY\"]]],[13,\"secondPos\",[6,\"d.vec2f\",[\"sPosX\",\"sPosY\"]]],[13,\"disp\",[6,\"d.vec2f\",[\"dispX\",\"dispY\"]]],[13,\"fromNearest\",[6,[7,\"uv\",\"sub\"],[\"nearestPos\"]]],[13,\"minDist1\",[6,\"std.length\",[\"fromNearest\"]]],[13,\"minDist2\",[6,\"std.length\",[[6,[7,\"uv\",\"sub\"],[\"secondPos\"]]]]],[13,\"displacementMag\",[6,\"std.length\",[\"disp\"]]],[13,\"edgeDiff\",[1,\"minDist2\",\"-\",\"minDist1\"]],[13,\"crackThreshold\",[1,\"crackWidth\",\"*\",[5,\"0.005\"]]],[13,\"baseCrackIntensity\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[5,\"0\"],\"crackThreshold\",\"edgeDiff\"]]]],[13,\"displacementFactor\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.01\"],\"displacementMag\"]]],[13,\"crackIntensity\",[1,\"baseCrackIntensity\",\"*\",\"displacementFactor\"]],[13,\"toNearest\",[6,[7,\"fromNearest\",\"div\"],[[6,\"std.max\",[\"minDist1\",[5,\"0.00001\"]]]]]],[13,\"edgeNormal\",[6,\"d.vec2f\",[[1,[7,\"toNearest\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],[7,\"toNearest\",\"x\"]]]],[13,\"displacedUV\",[6,[7,\"uv\",\"sub\"],[\"disp\"]]],[10,[6,\"CrackGeom\",[[104,{crackIntensity:\"crackIntensity\",edgeNormal:\"edgeNormal\",displacedUV:\"displacedUV\",disp:\"disp\"}]]]]]]},externals:{\"d.vec2f\":()=>m,\"std.length\":()=>he,\"std.smoothstep\":()=>be,\"std.max\":()=>B,CrackGeom:()=>J0}})&&e.f)({})),C8=w.fn([m,m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=s.mul(__tsover_mul(o,.01)),c=__tsover_mul(a,.005),d=s.mul(__tsover_mul(i,c)),u=l.add(d);return t.add(u.mul(r))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"displacedUV\"},{type:\"i\",name:\"edgeNormal\"},{type:\"i\",name:\"crackIntensity\"},{type:\"i\",name:\"refractionStrength\"},{type:\"i\",name:\"chromaticSplit\"},{type:\"i\",name:\"channel\"}],body:[0,[[13,\"refractionOffset\",[6,[7,\"edgeNormal\",\"mul\"],[[1,\"refractionStrength\",\"*\",[5,\"0.01\"]]]]],[13,\"chromaticOffset\",[1,\"chromaticSplit\",\"*\",[5,\"0.005\"]]],[13,\"chromaShift\",[6,[7,\"edgeNormal\",\"mul\"],[[1,\"channel\",\"*\",\"chromaticOffset\"]]]],[13,\"offset\",[6,[7,\"refractionOffset\",\"add\"],[\"chromaShift\"]]],[10,[6,[7,\"displacedUV\",\"add\"],[[6,[7,\"offset\",\"mul\"],[\"crackIntensity\"]]]]]]]},externals:{}})&&e.f)({})),pC=.3/Math.hypot(.3,.6),mC=.6/Math.hypot(.3,.6),M8=w.fn([S,S,S,S,n,m,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=A(s.x,r.y,o.z),d=t.xyz.mul(__tsover_sub(1,a)).add(c.mul(a)),u=he(i),p=__tsover_div(i.x,__tsover_add(u,.001)),_=__tsover_div(i.y,__tsover_add(u,.001)),f=__tsover_add(__tsover_mul(p,pC),__tsover_mul(_,mC)),h=__tsover_add(1,__tsover_mul(f,l)),g=be(0,.02,u),y=__tsover_add(1,__tsover_mul(__tsover_sub(h,1),g));return S(d.mul(y),t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"normalColor\"},{type:\"i\",name:\"rFinal\"},{type:\"i\",name:\"gFinal\"},{type:\"i\",name:\"bFinal\"},{type:\"i\",name:\"crackIntensity\"},{type:\"i\",name:\"disp\"},{type:\"i\",name:\"shardLighting\"}],body:[0,[[13,\"refractedRGB\",[6,\"d.vec3f\",[[7,\"rFinal\",\"x\"],[7,\"gFinal\",\"y\"],[7,\"bFinal\",\"z\"]]]],[13,\"shadedRGB\",[6,[7,[6,[7,[7,\"normalColor\",\"xyz\"],\"mul\"],[[1,[5,\"1\"],\"-\",\"crackIntensity\"]]],\"add\"],[[6,[7,\"refractedRGB\",\"mul\"],[\"crackIntensity\"]]]]],[13,\"displacementLength\",[6,\"std.length\",[\"disp\"]]],[13,\"tiltX\",[1,[7,\"disp\",\"x\"],\"/\",[1,\"displacementLength\",\"+\",[5,\"0.001\"]]]],[13,\"tiltY\",[1,[7,\"disp\",\"y\"],\"/\",[1,\"displacementLength\",\"+\",[5,\"0.001\"]]]],[13,\"normalDot\",[1,[1,\"tiltX\",\"*\",\"SHARD_LIGHT_DIR_X\"],\"+\",[1,\"tiltY\",\"*\",\"SHARD_LIGHT_DIR_Y\"]]],[13,\"lightingFactor\",[1,[5,\"1\"],\"+\",[1,\"normalDot\",\"*\",\"shardLighting\"]]],[13,\"lightingIntensity\",[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.02\"],\"displacementLength\"]]],[13,\"finalLighting\",[1,[5,\"1\"],\"+\",[1,[1,\"lightingFactor\",\"-\",[5,\"1\"]],\"*\",\"lightingIntensity\"]]],[13,\"finalRGB\",[6,[7,\"shadedRGB\",\"mul\"],[\"finalLighting\"]]],[10,[6,\"d.vec4f\",[\"finalRGB\",[7,\"normalColor\",\"w\"]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.length\":()=>he,SHARD_LIGHT_DIR_X:()=>pC,SHARD_LIGHT_DIR_Y:()=>mC,\"std.smoothstep\":()=>be,\"d.vec4f\":()=>S}})&&e.f)({}));function E8(e){return t=>{let{gpu:s,registerComputeTexture:r,getCpuValue:o,onCleanup:a}=t,i=s?.root;if(!i)return null;let{layout:l,kernel:c}=fce(e.count,e.size),d=pr(i,n,e.count*2),u=(globalThis.__TYPEGPU_AUTONAME__??(b=>b))(i.createTexture({size:[e.size,e.size],format:T8}).$usage(\"storage\",\"sampled\"),\"voronoiTex\");a(()=>u.destroy());let p=r(u),_=i.createBindGroup(l,{cellPos:d,voronoiTex:u}),f=wr(i,(b=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(b.f=((x,P)=>{\"use gpu\";c(x,P)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"kernel\",[\"cx\",\"cy\"]]]]},externals:{kernel:()=>c}})&&b.f)({}),{size:[e.size,e.size],bindGroup:_}),h=b=>{d.write(e.sites(b))},g=o(e.seedProp)??e.seedDefault;h(g);let y=!0;return{outputs:{[e.output]:p},getComputeNodes:()=>{let b=o(e.seedProp)??e.seedDefault;return b!==g&&(g=b,h(g),y=!0),y?(y=!1,[f]):null}}}}var{formatFloat:hce}=xae,k8=e=>({kind:\"grid\",slots:e}),B8=e=>({kind:\"radial\",slots:e}),z8=e=>({kind:\"line\",slots:e}),R8=(e,t)=>({mode:e,members:t}),gce={grid:0,radial:1,linear:2},yce={none:0,\"alternate-flip-x\":1,\"alternate-flip-y\":2,both:3};function D8(e){return t=>{let{uniforms:s,childNode:r,ctx:o,propValues:a,childBoundsParams:i,ownBoundsParams:l,convertToTexture:c}=t;if(!r)return xt;let d=c(r).key,u=(h,g)=>{let y=a[h.name];return typeof y==\"number\"?y:g[y]??0},p=\"members\"in e.placement?[e.placement.members.grid,e.placement.members.radial,e.placement.members.linear][u(e.placement.mode,gce)]??e.placement.members.grid:e.placement,_=e.order?a[e.order.name]===\"backward\"||a[e.order.name]===1:!1,f=e.hueShift?(a[e.hueShift.name]??0)!==0:!1;return new fs(h=>{let g={p:h.freshLocal(\"rep\"),L:hce},{p:y,L:b}=g,x=U=>{let L=s[U.name];if(!L)throw new Error(`std: repeatInstances binds unknown prop '${U.name}'`);return L._emit(h)},P=[];P.push(`let ${y}_uv = ${o.uv._emit(h)};`),P.push(`let ${y}_aspect = ${o.aspect._emit(h)};`),P.push(`let ${y}_pix = vec2f(${y}_uv.x * ${y}_aspect, ${y}_uv.y);`);let C={cx:i?i.centerX._emit(h):b(.5),cy:i?i.centerY._emit(h):b(.5),hw:i?i.halfWidth._emit(h):b(.5),hh:i?i.halfHeight._emit(h):b(.5)};P.push(...lB(g,C,{left:x(e.source.crop.left),right:x(e.source.crop.right),top:x(e.source.crop.top),bottom:x(e.source.crop.bottom)})),P.push(...cB(g,{cx:l?l.centerX._emit(h):b(.5),cy:l?l.centerY._emit(h):b(.5),hw:l?l.halfWidth._emit(h):b(.5),hh:l?l.halfHeight._emit(h):b(.5)})),P.push(`let ${y}_phase = ${x(e.variation.phase)};`),P.push(`let ${y}_seed = ${x(e.variation.seed)};`);let E=i?`vec2f((${i.centerX._emit(h)}) * ${y}_aspect, ${i.centerY._emit(h)})`:`${y}_fieldCenterSq`,R=p.kind===\"grid\"?dB({columns:x(p.slots.columns),rows:x(p.slots.rows),gapX:x(p.slots.gapX),gapY:x(p.slots.gapY),stagger:x(p.slots.stagger)},u(p.slots.flip,yce)):p.kind===\"radial\"?uB(E,{count:x(p.slots.count),sweep:x(p.slots.sweep),startAngle:x(p.slots.startAngle),radius:x(p.slots.radius),faceCenter:x(p.slots.faceCenter)}):pB(E,{count:x(p.slots.count),direction:x(p.slots.direction),spacing:x(p.slots.spacing)});P.push(...fB(g,{placement:R,variation:mB(g,{scale:x(e.variation.scale),rotation:x(e.variation.rotation),opacity:x(e.variation.opacity),jitterPosition:x(e.variation.jitter.position),jitterRotation:x(e.variation.jitter.rotation),jitterScale:x(e.variation.jitter.scale),jitterOpacity:x(e.variation.jitter.opacity)}),hue:f&&e.hueShift?_B(g,x(e.hueShift)):[],zBack:_,texKey:d}));for(let U of P)h.statement(U);return W(Yt,\"unpremultiplyAlpha\",[ca(`${y}_accum`)])._emit(h)})}}var bce=w.fn([m,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a)=>{\"use gpu\";let i=m7(s),l=m(__tsover_div(pe(i),o),ae(i)),c=__tsover_mul(r,.1),d=l.mul(c);return t.add(d.mul(a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"angle\"},{type:\"i\",name:\"strength\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"channelOffset\"}],body:[0,[[13,\"angleRad\",[6,\"std.radians\",[\"angle\"]]],[13,\"direction\",[6,\"d.vec2f\",[[1,[6,\"std.cos\",[\"angleRad\"]],\"/\",\"aspect\"],[6,\"std.sin\",[\"angleRad\"]]]]],[13,\"scaledStrength\",[1,\"strength\",\"*\",[5,\"0.1\"]]],[13,\"offsetBase\",[6,[7,\"direction\",\"mul\"],[\"scaledStrength\"]]],[10,[6,[7,\"uv\",\"add\"],[[6,[7,\"offsetBase\",\"mul\"],[\"channelOffset\"]]]]]]]},externals:{\"std.radians\":()=>m7,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&e.f)({}));function L8(e){return t=>{let s=i=>W(bce,\"chromaticOffsetUV\",[t.ctx.uv,At(e.angle,t),At(e.strength,t),t.ctx.aspect,At(i,t)]),r=t.texture.sample(s(e.red)),o=t.texture.sample(s(e.green)),a=t.texture.sample(s(e.blue));return Ct(r.member(\"r\"),o.member(\"g\"),a.member(\"b\"),o.member(\"a\"))}}function U8(e){return t=>{let{uniforms:s,childNode:r,ctx:o,convertToTexture:a}=t;if(!r)return xt;let i=a(r).key;return new fs(l=>{let c={p:l.freshLocal(\"lens\"),stmts:[],U:d=>s[e[d].name]._emit(l),L:d=>fo(d),noiseIdx:{value:0}};wae(c,o.uv._emit(l),o.aspect._emit(l)),Tae(c),Pae(c,i),Cae(c);for(let d of c.stmts)l.statement(d);return W(Yt,\"unpremultiplyAlpha\",[ca(`${c.p}_out`)])._emit(l)})}}var xce=(e,t)=>typeof e==\"number\"?e:t,O8=1080;function G8(e){return t=>{let{uniforms:s,ctx:r,childNode:o,convertToTexture:a,propValues:i}=t;if(!o)return xt;let l=a(o).key,c={alpha:0,bright:1,dark:2,red:3,green:4,blue:5},d={grid:0,quadtree:1,mosaic:2},u={none:0,dimensions:1,percentage:2},p={\"bottom-left\":0,\"bottom-right\":1,\"top-left\":2,\"top-right\":3},_=i[e.detectionMode.name],f=[\"alpha\",\"bright\",\"dark\",\"red\",\"green\",\"blue\"][typeof _==\"number\"?_:c[_]??1],h=i[e.layout.name],g=[\"grid\",\"quadtree\",\"mosaic\"][typeof h==\"number\"?h:d[h]??2],y=Math.round(xce(i[e.maxDepth.name],2));Number.isFinite(y)||(y=2),y=Math.max(0,Math.min(5,y)),g===\"grid\"&&(y=0);let b=i[e.boxStyle.name],x=(typeof b==\"string\"?b:\"corners\")===\"corners\",P=i[e.labelMode.name],C=[\"none\",\"dimensions\",\"percentage\"][typeof P==\"number\"?P:u[P]??0],E=C!==\"none\",R=i[e.labelPosition.name],U=typeof R==\"number\"?R:p[R]??1,L=E?uae(t,{label:\"DetectionOverlay:atlas\",familyProp:e.fontFamily.name,weightProp:e.fontWeight.name}).key:void 0;return new fs(k=>{let D=k.freshLocal(\"ot\"),O=[],F={p:D,U:X=>s[e[X].name]._emit(k),L:X=>fo(X),s:O,sampleChild:X=>`textureSampleLevel(tex.$.${l}, samp.$.linearClamp, ${X}, 0.0)`,UN:k.external(Yt,\"unpremultiplyAlpha\"),modeStr:f},{U:q,L:Y}=F;O.push(`let ${D}_res = ${r.logicalViewportSize._emit(k)};`),O.push(`let ${D}_scaleFactor = ${D}_res.y / ${Y(O8)};`);let V=r.uv._emit(k);O.push(`let ${D}_fragPx = vec2f((${V}).x * ${D}_res.x, (${V}).y * ${D}_res.y);`),O.push(`let ${D}_aa = max(max(fwidth(${D}_fragPx.x), fwidth(${D}_fragPx.y)), ${Y(1e-4)});`),O.push(`let ${D}_cellPx = max(${q(\"cellSize\")} * ${D}_scaleFactor, ${Y(4)});`),O.push(`let ${D}_minSize = ${Y(3)} * ${D}_scaleFactor;`),O.push(`let ${D}_lineHalf = max(${q(\"lineWidth\")} * ${D}_scaleFactor * ${Y(.5)}, ${Y(.5)});`),O.push(`let ${D}_cornerRadiusPx = ${q(\"cornerRadius\")} * ${D}_scaleFactor;`),O.push(`let ${D}_boxInset = ${D}_lineHalf + ${D}_scaleFactor * ${Y(1.5)};`),O.push(`let ${D}_thr = ${q(\"threshold\")};`),mae(F,g,y),_ae(F),O.push(`var ${D}_dst = ${F.sampleChild(`(${V})`)};`),fae(F,x),E&&L&&vae(F,{labelStr:C,labelPos:U,atlasKey:L});for(let X of O)k.statement(X);return aB(`${D}_dst`)})}}function F8(e){return t=>{let{uniforms:s,ctx:r,childNode:o,convertToTexture:a,computeOutputs:i}=t;if(!o)return xt;let l=(i?.childTexture??a(o)).key,c=i?.trackState;return new fs(d=>{let u=d.freshLocal(\"kft\"),p=[],_={p:u,U:y=>s[e[y].name]._emit(d),L:y=>fo(y),s:p},{U:f,L:h}=_,g=r.uv._emit(d);if(p.push(`let ${u}_res = ${r.logicalViewportSize._emit(d)};`),p.push(`let ${u}_sf = ${u}_res.y / ${h(O8)};`),p.push(`let ${u}_px = vec2f((${g}).x * ${u}_res.x, (${g}).y * ${u}_res.y);`),p.push(`let ${u}_aa = max(max(fwidth(${u}_px.x), fwidth(${u}_px.y)), ${h(1e-4)});`),p.push(`let ${u}_lw = max(${f(\"lineWidth\")} * ${u}_sf, ${h(.5)});`),p.push(`let ${u}_ms = max(${f(\"markerSize\")} * ${u}_sf, ${h(4)});`),p.push(`var ${u}_dst = textureSampleLevel(tex.$.${l}, samp.$.linearClamp, (${g}), 0.0);`),c){let y=c.key,b=x=>`textureLoad(tex.$.${y}, vec2u(${x}, ti), 0)`;p.push(`let ${u}_n = u32(clamp(${f(\"trackers\")}, ${h(1)}, ${h(64)}));`),p.push(`for (var ti = 0u; ti < ${u}_n; ti = ti + 1u) {`),p.push(`let head = ${b(\"0u\")};`),p.push(\"if (head.w < 0.5) { continue; }\"),p.push(`let bb = ${b(\"14u\")};`),p.push(`let pad = ${u}_ms + ${u}_lw * ${h(4)} + ${u}_aa * ${h(2)};`),p.push(`let bbMin = vec2f(bb.x * ${u}_res.x, bb.y * ${u}_res.y) - vec2f(pad);`),p.push(`let bbMax = vec2f(bb.z * ${u}_res.x, bb.w * ${u}_res.y) + vec2f(pad);`),p.push(`if (all(${u}_px >= bbMin) && all(${u}_px <= bbMax)) {`),p.push(`let mta = ${b(\"1u\")};`),p.push(`let tp = vec2f(head.x * ${u}_res.x, head.y * ${u}_res.y);`),p.push(`let searching = select(${h(1)}, ${h(.45)}, mta.x > ${h(4)});`),yae(_,{len:12,every:8,load:b,stages:[hae,gae(8)]}),bae(_),p.push(\"}\"),p.push(\"}\")}for(let y of p)d.statement(y);return aB(`${u}_dst`)})}}function Ec(e){return{compute:t=>{let{gpu:s,getCpuValue:r}=t,o=s?.root;if(!o)return null;let a=e(t,o);if(!a)return null;let i=(l,c)=>{let d=r(l);return typeof d==\"number\"?d:c};return{outputs:a.outputs,...a.bindInputs?{bindInputs:a.bindInputs}:{},getComputeNodes:l=>{for(let _ of a.stages)if(_.ready&&!_.ready())return null;let c=l,d=Math.min(c.deltaTime??0,a.clampDt);if(a.zeroDt===\"skip\"&&d<=0)return null;let u={dt:d,now:Date.now(),num:i,getCpuValue:r,frameParams:c},p=[];for(let _ of a.stages)_.tick?.(u);for(let _ of a.stages)if(_.skip?.(u))return null;for(let _ of a.stages)_.emit?.(u,p);return p}}}}}var Ir={host(e,t){return{tick:t}},values(e,t){return{emit:s=>t(s)}},settle(e){let t=0;return{tick(s){e.activeWhen(s)&&(t=0)},skip(s){let r=t>e.settleMs(s);return r||(t+=s.dt*1e3),r}}},readyWhen(e){return{ready:e}},pass(e){return{emit:(t,s)=>s.push(e)}},cache(e){return{emit:(t,s)=>s.push(e(t))}},publish(e){return{emit:(t,s)=>s.push(e(t))}},seedOnce(e){return{emit(t,s){e.staleWhen(t)&&(e.onSeed?.(),s.push(e.pass))}}},iterate(e){return{emit(t,s){let r=e.count(t);for(let o=0;o<r;o++)e.step(o,s,t)}}},sortPass(e){let t=\"A\",s=0;return{emit(r,o){let a=e.passes(r);for(let i=0;i<a;i++)o.push(e.pass(s%2,t)),t=t===\"A\"?\"B\":\"A\",s++},side:()=>t}}};function Eg(e){let t=Math.max(1,Math.round(e.dimensions.width)),s=Math.max(1,Math.round(e.dimensions.height));return e.onResize(({width:r,height:o})=>{t=Math.max(1,Math.round(r)),s=Math.max(1,Math.round(o))}),{tracked:()=>({width:t,height:s}),safe:r=>({width:Math.max(1,r.dimensions?.width??t),height:Math.max(1,r.dimensions?.height??s)})}}var gn=(e,t)=>({name:e,run:t});function kg(e){let t=Date.now();return s=>{let{pointer:r}=s,o=Date.now(),a=Math.min((o-t)/1e3,e.clampDt);t=o;let i={dt:a,now:o,pointer:r,frameParams:s};for(let l of e.steps)if(l.run(i)===\"skip\")return}}function Bg(e,t){let s=new Uint16Array(t.size*t.size*4),r=e.createDataTexture({width:t.size,height:t.size,format:\"rgba16float\",data:s,label:t.label});return e.onCleanup(()=>r.destroy()),{texData:s,texture:e.registerMediaTexture(()=>r.texture),upload:()=>r.write(s)}}var zg=\"rgba16float\",b2=16384,Rg=e=>Math.max(8,Math.min(128,Math.floor(e))),wm=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({buffer:{storage:Fe(S,b2),access:\"mutable\"},params:{uniform:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cursorX:n,cursorY:n,mouseVelX:n,mouseVelY:n,dt:n,decay:n,intensity:n,radius:n,aspect:n}),\"uniform\")}}),\"splatUpdateLayout\"),Y1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({buffer:{storage:Fe(S,b2),access:\"readonly\"},dispTex:{storageTexture:zt(zg,\"write-only\")}}),\"splatOutputLayout\");function Ace(e){return w.fn([M,M])((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r)=>{\"use gpu\";let o=wm.$.params,a=__tsover_add(__tsover_mul(r,M(e)),s),i=wm.$.buffer[a],l=__tsover_mul(i.x,__tsover_sub(1,__tsover_mul(o.decay,o.dt))),c=__tsover_mul(i.y,__tsover_sub(1,__tsover_mul(o.decay,o.dt))),d=__tsover_div(__tsover_add(n(s),.5),n(e)),u=__tsover_div(__tsover_add(n(r),.5),n(e)),p=N(__tsover_sub(d,o.cursorX),__tsover_mul(__tsover_sub(d,o.cursorX),o.aspect),o.aspect>=1),_=N(__tsover_div(__tsover_sub(u,o.cursorY),o.aspect),__tsover_sub(u,o.cursorY),o.aspect>=1),f=__tsover_add(__tsover_mul(p,p),__tsover_mul(_,_)),h=__tsover_mul(o.radius,2);if(f<__tsover_mul(h,h)&&__tsover_add(re(o.mouseVelX),re(o.mouseVelY))>.01){let g=Tt(__tsover_mul(__tsover_div(f,__tsover_mul(o.radius,o.radius)),-1));l=__tsover_add(l,__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(o.mouseVelX,g),o.intensity),o.dt),.5)),c=__tsover_add(c,__tsover_mul(__tsover_mul(__tsover_mul(__tsover_mul(o.mouseVelY,g),o.intensity),o.dt),.5))}l=G(l,-1,1),c=G(c,-1,1),wm.$.buffer[a]=S(l,c,0,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"p\",[7,[7,\"splatUpdateLayout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"gridSize\"]]],\"+\",\"cx\"]],[13,\"cell\",[8,[7,[7,\"splatUpdateLayout\",\"$\"],\"buffer\"],\"idx\"]],[12,\"dx\",[1,[7,\"cell\",\"x\"],\"*\",[1,[5,\"1\"],\"-\",[1,[7,\"p\",\"decay\"],\"*\",[7,\"p\",\"dt\"]]]]],[12,\"dy\",[1,[7,\"cell\",\"y\"],\"*\",[1,[5,\"1\"],\"-\",[1,[7,\"p\",\"decay\"],\"*\",[7,\"p\",\"dt\"]]]]],[13,\"cellX\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"gridSize\"]]]],[13,\"cellY\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"gridSize\"]]]],[13,\"ddx\",[6,\"std.select\",[[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],[1,[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],\"*\",[7,\"p\",\"aspect\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"ddy\",[6,\"std.select\",[[1,[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],\"/\",[7,\"p\",\"aspect\"]],[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"distSq\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]],[13,\"radius2\",[1,[7,\"p\",\"radius\"],\"*\",[5,\"2\"]]],[11,[3,[1,\"distSq\",\"<\",[1,\"radius2\",\"*\",\"radius2\"]],\"&&\",[1,[1,[6,\"std.abs\",[[7,\"p\",\"mouseVelX\"]]],\"+\",[6,\"std.abs\",[[7,\"p\",\"mouseVelY\"]]]],\">\",[5,\"0.01\"]]],[0,[[13,\"influence\",[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]]],\"*\",[4,\"-\",[5,\"1\"]]]]]],[2,\"dx\",\"=\",[1,\"dx\",\"+\",[1,[1,[1,[1,[7,\"p\",\"mouseVelX\"],\"*\",\"influence\"],\"*\",[7,\"p\",\"intensity\"]],\"*\",[7,\"p\",\"dt\"]],\"*\",[5,\"0.5\"]]]],[2,\"dy\",\"=\",[1,\"dy\",\"+\",[1,[1,[1,[1,[7,\"p\",\"mouseVelY\"],\"*\",\"influence\"],\"*\",[7,\"p\",\"intensity\"]],\"*\",[7,\"p\",\"dt\"]],\"*\",[5,\"0.5\"]]]]]]],[2,\"dx\",\"=\",[6,\"std.clamp\",[\"dx\",[4,\"-\",[5,\"1\"]],[5,\"1\"]]]],[2,\"dy\",\"=\",[6,\"std.clamp\",[\"dy\",[4,\"-\",[5,\"1\"]],[5,\"1\"]]]],[2,[8,[7,[7,\"splatUpdateLayout\",\"$\"],\"buffer\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"dx\",\"dy\",[5,\"0\"],[5,\"0\"]]]]]]},externals:{splatUpdateLayout:()=>wm,\"d.u32\":()=>M,gridSize:()=>e,\"d.f32\":()=>n,\"std.select\":()=>N,\"std.abs\":()=>re,\"std.exp\":()=>Tt,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&t.f)({})).$name(\"pointerSplatUpdate\")}function wce(e){return w.fn([M,M])((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r)=>{\"use gpu\";let o=__tsover_add(__tsover_mul(r,M(e)),s),a=Y1.$.buffer[o];lt(Y1.$.dispTex,fe(s,r),S(a.x,a.y,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"gridSize\"]]],\"+\",\"cx\"]],[13,\"cell\",[8,[7,[7,\"splatOutputLayout\",\"$\"],\"buffer\"],\"idx\"]],[6,\"std.textureStore\",[[7,[7,\"splatOutputLayout\",\"$\"],\"dispTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"cell\",\"x\"],[7,\"cell\",\"y\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{\"d.u32\":()=>M,gridSize:()=>e,splatOutputLayout:()=>Y1,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&t.f)({})).$name(\"pointerSplatOutput\")}function I8(e){return(t,s)=>{let{getCpuValue:r,registerComputeTexture:o,onCleanup:a}=t,i=Rg(r(e.gridSize.name)??20),l=pr(s,S,b2),c=(globalThis.__TYPEGPU_AUTONAME__??(D=>D))(s.createTexture({size:[i,i],format:zg}).$usage(\"storage\",\"sampled\"),\"dispTex\");a(()=>c.destroy());let d=o(c),u=(globalThis.__TYPEGPU_AUTONAME__??(D=>D))(s.createUniform(gt({cursorX:n,cursorY:n,mouseVelX:n,mouseVelY:n,dt:n,decay:n,intensity:n,radius:n,aspect:n})),\"updateParams\"),p=Eg(t),_=[i,i],f=Ace(i),h=wce(i),g=s.createBindGroup(wm,{buffer:l,params:u.buffer}),y=s.createBindGroup(Y1,{buffer:l,dispTex:c}),b=wr(s,(D=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(D.f=((O,F)=>{\"use gpu\";f(O,F)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"updateKernel\",[\"cx\",\"cy\"]]]]},externals:{updateKernel:()=>f}})&&D.f)({}),{size:_,bindGroup:g}),x=wr(s,(D=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(D.f=((O,F)=>{\"use gpu\";h(O,F)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"outputKernel\",[\"cx\",\"cy\"]]]]},externals:{outputKernel:()=>h}})&&D.f)({}),{size:_,bindGroup:y}),P=.5,C=.5,E=0,R=0,U=.5,L=.5,k=!1;return{outputs:{[e.output]:d},clampDt:.016,stages:[Ir.host(\"cursorVelocity\",D=>{U=D.frameParams.pointer?.x??.5,L=D.frameParams.pointer?.y??.5;let O=D.dt>0?(U-P)/D.dt:0,F=D.dt>0?(L-C)/D.dt:0;E=E*.85+O*.15,R=R*.85+F*.15,P=U,C=L,k=Math.abs(O)+Math.abs(F)>.01}),Ir.settle({activeWhen:()=>k,settleMs:D=>{let O=D.num(e.decay.name,3);return O>0?Math.min(3e4,Math.log(1e-4)/Math.log(Math.max(1e-6,1-O*.016))*16.67):3e4}}),Ir.values(\"brush+decay\",D=>{let O=p.safe(D.frameParams);u.write({cursorX:U,cursorY:L,mouseVelX:E,mouseVelY:R,dt:D.dt,decay:D.num(e.decay.name,3),intensity:D.num(e.intensity.name,1),radius:D.num(e.radius.name,1)*.05,aspect:O.width/O.height})}),Ir.pass(b),Ir.publish(()=>x)]}}}var un=64,s_=un*un,eb=un-1,Fa=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({readBuf:{storage:Fe(S,s_),access:\"readonly\"},writeBuf:{storage:Fe(S,s_),access:\"mutable\"},params:{uniform:(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({cursorX:n,cursorY:n,dirX:n,dirY:n,clampedSpeed:n,dt:n,subDt:n,stiffness:n,dampFactor:n,radius:n,aspect:n}),\"uniform\")}}),\"latticeLayout\"),W1=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({srcBuf:{storage:Fe(S,s_),access:\"readonly\"},dispTex:{storageTexture:zt(zg,\"write-only\")}}),\"latticeOutputLayout\"),_C=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";if(t>M(0)&&t<M(eb)&&s>M(0)&&s<M(eb)){let r=Fa.$.params,o=M(un),a=__tsover_add(__tsover_mul(s,o),t),i=Fa.$.readBuf[a],l=i.x,c=i.y,d=i.z,u=i.w,p=Fa.$.readBuf[__tsover_sub(a,M(1))],_=Fa.$.readBuf[__tsover_add(a,M(1))],f=Fa.$.readBuf[__tsover_sub(a,o)],h=Fa.$.readBuf[__tsover_add(a,o)],g=Fa.$.readBuf[__tsover_sub(__tsover_sub(a,o),M(1))],y=Fa.$.readBuf[__tsover_add(__tsover_sub(a,o),M(1))],b=Fa.$.readBuf[__tsover_sub(__tsover_add(a,o),M(1))],x=Fa.$.readBuf[__tsover_add(__tsover_add(a,o),M(1))],P=__tsover_mul(r.stiffness,__tsover_add(__tsover_add(__tsover_add(__tsover_sub(p.x,l),__tsover_sub(_.x,l)),__tsover_sub(f.x,l)),__tsover_sub(h.x,l))),C=__tsover_mul(r.stiffness,__tsover_add(__tsover_add(__tsover_add(__tsover_sub(p.y,c),__tsover_sub(_.y,c)),__tsover_sub(f.y,c)),__tsover_sub(h.y,c))),E=__tsover_mul(r.stiffness,.35);P=__tsover_add(P,__tsover_mul(E,__tsover_add(__tsover_add(__tsover_add(__tsover_sub(g.x,l),__tsover_sub(y.x,l)),__tsover_sub(b.x,l)),__tsover_sub(x.x,l)))),C=__tsover_add(C,__tsover_mul(E,__tsover_add(__tsover_add(__tsover_add(__tsover_sub(g.y,c),__tsover_sub(y.y,c)),__tsover_sub(b.y,c)),__tsover_sub(x.y,c)))),P=__tsover_sub(P,__tsover_mul(__tsover_mul(r.stiffness,.1),l)),C=__tsover_sub(C,__tsover_mul(__tsover_mul(r.stiffness,.1),c));let R=__tsover_add(__tsover_mul(d,r.dampFactor),__tsover_mul(P,r.subDt)),U=__tsover_add(__tsover_mul(u,r.dampFactor),__tsover_mul(C,r.subDt));if(r.clampedSpeed>.01){let D=__tsover_div(__tsover_add(n(t),.5),n(un)),O=__tsover_div(__tsover_add(n(s),.5),n(un)),F=N(__tsover_sub(D,r.cursorX),__tsover_mul(__tsover_sub(D,r.cursorX),r.aspect),r.aspect>=1),q=N(__tsover_div(__tsover_sub(O,r.cursorY),r.aspect),__tsover_sub(O,r.cursorY),r.aspect>=1),Y=__tsover_add(__tsover_mul(F,F),__tsover_mul(q,q)),V=__tsover_mul(r.radius,3);if(Y<__tsover_mul(V,V)){let X=Tt(__tsover_mul(__tsover_div(Y,__tsover_mul(r.radius,r.radius)),-1)),Q=__tsover_mul(__tsover_mul(__tsover_mul(X,r.clampedSpeed),r.dt),2);R=__tsover_add(R,__tsover_mul(r.dirX,Q)),U=__tsover_add(U,__tsover_mul(r.dirY,Q))}}let L=G(__tsover_add(l,__tsover_mul(R,r.subDt)),-.5,.5),k=G(__tsover_add(c,__tsover_mul(U,r.subDt)),-.5,.5);Fa.$.writeBuf[a]=S(L,k,R,U)}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[11,[3,[3,[3,[1,\"cx\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],\"&&\",[1,\"cx\",\"<\",[6,\"d.u32\",[\"LATTICE_MAX\"]]]],\"&&\",[1,\"cy\",\">\",[6,\"d.u32\",[[5,\"0\"]]]]],\"&&\",[1,\"cy\",\"<\",[6,\"d.u32\",[\"LATTICE_MAX\"]]]],[0,[[13,\"p\",[7,[7,\"latticeLayout\",\"$\"],\"params\"]],[13,\"gs\",[6,\"d.u32\",[\"LATTICE_GRID\"]]],[13,\"idx\",[1,[1,\"cy\",\"*\",\"gs\"],\"+\",\"cx\"]],[13,\"current\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],\"idx\"]],[13,\"dispX\",[7,\"current\",\"x\"]],[13,\"dispY\",[7,\"current\",\"y\"]],[13,\"velX\",[7,\"current\",\"z\"]],[13,\"velY\",[7,\"current\",\"w\"]],[13,\"nL\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"nR\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"nU\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"-\",\"gs\"]]],[13,\"nD\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,\"idx\",\"+\",\"gs\"]]],[13,\"nUL\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,[1,\"idx\",\"-\",\"gs\"],\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"nUR\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,[1,\"idx\",\"-\",\"gs\"],\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"nDL\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,[1,\"idx\",\"+\",\"gs\"],\"-\",[6,\"d.u32\",[[5,\"1\"]]]]]],[13,\"nDR\",[8,[7,[7,\"latticeLayout\",\"$\"],\"readBuf\"],[1,[1,\"idx\",\"+\",\"gs\"],\"+\",[6,\"d.u32\",[[5,\"1\"]]]]]],[12,\"fx\",[1,[7,\"p\",\"stiffness\"],\"*\",[1,[1,[1,[1,[7,\"nL\",\"x\"],\"-\",\"dispX\"],\"+\",[1,[7,\"nR\",\"x\"],\"-\",\"dispX\"]],\"+\",[1,[7,\"nU\",\"x\"],\"-\",\"dispX\"]],\"+\",[1,[7,\"nD\",\"x\"],\"-\",\"dispX\"]]]],[12,\"fy\",[1,[7,\"p\",\"stiffness\"],\"*\",[1,[1,[1,[1,[7,\"nL\",\"y\"],\"-\",\"dispY\"],\"+\",[1,[7,\"nR\",\"y\"],\"-\",\"dispY\"]],\"+\",[1,[7,\"nU\",\"y\"],\"-\",\"dispY\"]],\"+\",[1,[7,\"nD\",\"y\"],\"-\",\"dispY\"]]]],[13,\"shearK\",[1,[7,\"p\",\"stiffness\"],\"*\",[5,\"0.35\"]]],[2,\"fx\",\"=\",[1,\"fx\",\"+\",[1,\"shearK\",\"*\",[1,[1,[1,[1,[7,\"nUL\",\"x\"],\"-\",\"dispX\"],\"+\",[1,[7,\"nUR\",\"x\"],\"-\",\"dispX\"]],\"+\",[1,[7,\"nDL\",\"x\"],\"-\",\"dispX\"]],\"+\",[1,[7,\"nDR\",\"x\"],\"-\",\"dispX\"]]]]],[2,\"fy\",\"=\",[1,\"fy\",\"+\",[1,\"shearK\",\"*\",[1,[1,[1,[1,[7,\"nUL\",\"y\"],\"-\",\"dispY\"],\"+\",[1,[7,\"nUR\",\"y\"],\"-\",\"dispY\"]],\"+\",[1,[7,\"nDL\",\"y\"],\"-\",\"dispY\"]],\"+\",[1,[7,\"nDR\",\"y\"],\"-\",\"dispY\"]]]]],[2,\"fx\",\"=\",[1,\"fx\",\"-\",[1,[1,[7,\"p\",\"stiffness\"],\"*\",[5,\"0.1\"]],\"*\",\"dispX\"]]],[2,\"fy\",\"=\",[1,\"fy\",\"-\",[1,[1,[7,\"p\",\"stiffness\"],\"*\",[5,\"0.1\"]],\"*\",\"dispY\"]]],[12,\"newVelX\",[1,[1,\"velX\",\"*\",[7,\"p\",\"dampFactor\"]],\"+\",[1,\"fx\",\"*\",[7,\"p\",\"subDt\"]]]],[12,\"newVelY\",[1,[1,\"velY\",\"*\",[7,\"p\",\"dampFactor\"]],\"+\",[1,\"fy\",\"*\",[7,\"p\",\"subDt\"]]]],[11,[1,[7,\"p\",\"clampedSpeed\"],\">\",[5,\"0.01\"]],[0,[[13,\"cellX\",[1,[1,[6,\"d.f32\",[\"cx\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"LATTICE_GRID\"]]]],[13,\"cellY\",[1,[1,[6,\"d.f32\",[\"cy\"]],\"+\",[5,\"0.5\"]],\"/\",[6,\"d.f32\",[\"LATTICE_GRID\"]]]],[13,\"ddx\",[6,\"std.select\",[[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],[1,[1,\"cellX\",\"-\",[7,\"p\",\"cursorX\"]],\"*\",[7,\"p\",\"aspect\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"ddy\",[6,\"std.select\",[[1,[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],\"/\",[7,\"p\",\"aspect\"]],[1,\"cellY\",\"-\",[7,\"p\",\"cursorY\"]],[1,[7,\"p\",\"aspect\"],\">=\",[5,\"1\"]]]]],[13,\"distSq\",[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]]],[13,\"influenceRadius\",[1,[7,\"p\",\"radius\"],\"*\",[5,\"3\"]]],[11,[1,\"distSq\",\"<\",[1,\"influenceRadius\",\"*\",\"influenceRadius\"]],[0,[[13,\"influence\",[6,\"std.exp\",[[1,[1,\"distSq\",\"/\",[1,[7,\"p\",\"radius\"],\"*\",[7,\"p\",\"radius\"]]],\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"forceMag\",[1,[1,[1,\"influence\",\"*\",[7,\"p\",\"clampedSpeed\"]],\"*\",[7,\"p\",\"dt\"]],\"*\",[5,\"2\"]]],[2,\"newVelX\",\"=\",[1,\"newVelX\",\"+\",[1,[7,\"p\",\"dirX\"],\"*\",\"forceMag\"]]],[2,\"newVelY\",\"=\",[1,\"newVelY\",\"+\",[1,[7,\"p\",\"dirY\"],\"*\",\"forceMag\"]]]]]]]]],[13,\"newDispX\",[6,\"std.clamp\",[[1,\"dispX\",\"+\",[1,\"newVelX\",\"*\",[7,\"p\",\"subDt\"]]],[4,\"-\",[5,\"0.5\"]],[5,\"0.5\"]]]],[13,\"newDispY\",[6,\"std.clamp\",[[1,\"dispY\",\"+\",[1,\"newVelY\",\"*\",[7,\"p\",\"subDt\"]]],[4,\"-\",[5,\"0.5\"]],[5,\"0.5\"]]]],[2,[8,[7,[7,\"latticeLayout\",\"$\"],\"writeBuf\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"newDispX\",\"newDispY\",\"newVelX\",\"newVelY\"]]]]]]]]},externals:{\"d.u32\":()=>M,LATTICE_MAX:()=>eb,latticeLayout:()=>Fa,LATTICE_GRID:()=>un,\"d.f32\":()=>n,\"std.select\":()=>N,\"std.exp\":()=>Tt,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"springLatticeStep\"),fC=w.fn([M,M])((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=__tsover_add(__tsover_mul(s,M(un)),t),o=W1.$.srcBuf[r];lt(W1.$.dispTex,fe(t,s),S(o.x,o.y,0,1))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[13,\"idx\",[1,[1,\"cy\",\"*\",[6,\"d.u32\",[\"LATTICE_GRID\"]]],\"+\",\"cx\"]],[13,\"cell\",[8,[7,[7,\"latticeOutputLayout\",\"$\"],\"srcBuf\"],\"idx\"]],[6,\"std.textureStore\",[[7,[7,\"latticeOutputLayout\",\"$\"],\"dispTex\"],[6,\"d.vec2u\",[\"cx\",\"cy\"]],[6,\"d.vec4f\",[[7,\"cell\",\"x\"],[7,\"cell\",\"y\"],[5,\"0\"],[5,\"1\"]]]]]]]},externals:{\"d.u32\":()=>M,LATTICE_GRID:()=>un,latticeOutputLayout:()=>W1,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&e.f)({})).$name(\"springLatticeOutput\");function Y8(e){return(t,s)=>{let{registerComputeTexture:r,onCleanup:o}=t,a=pr(s,S,s_),i=pr(s,S,s_),l=(globalThis.__TYPEGPU_AUTONAME__??(k=>k))(s.createTexture({size:[un,un],format:zg}).$usage(\"storage\",\"sampled\"),\"dispTex\");o(()=>l.destroy());let c=r(l),d=(globalThis.__TYPEGPU_AUTONAME__??(k=>k))(s.createUniform(gt({cursorX:n,cursorY:n,dirX:n,dirY:n,clampedSpeed:n,dt:n,subDt:n,stiffness:n,dampFactor:n,radius:n,aspect:n})),\"springParams\"),u=Eg(t),p=[un,un],_=s.createBindGroup(Fa,{readBuf:a,writeBuf:i,params:d.buffer}),f=s.createBindGroup(Fa,{readBuf:i,writeBuf:a,params:d.buffer}),h=wr(s,(k=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(k.f=((D,O)=>{\"use gpu\";_C(D,O)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"springLatticeKernel\",[\"cx\",\"cy\"]]]]},externals:{springLatticeKernel:()=>_C}})&&k.f)({}),{size:p}),g=s.createBindGroup(W1,{srcBuf:a,dispTex:l}),y=wr(s,(k=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(k.f=((D,O)=>{\"use gpu\";fC(D,O)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"}],body:[0,[[6,\"springLatticeOutputKernel\",[\"cx\",\"cy\"]]]]},externals:{springLatticeOutputKernel:()=>fC}})&&k.f)({}),{size:p,bindGroup:g}),b=.5,x=.5,P=.5,C=.5,E=0,R=0,U=0,L=0;return{outputs:{[e.output]:c},clampDt:.016,zeroDt:\"skip\",stages:[Ir.host(\"cursorImpulse\",k=>{P=k.frameParams.pointer?.x??.5,C=k.frameParams.pointer?.y??.5,E=(P-b)/k.dt,R=(C-x)/k.dt,U=Math.sqrt(E*E+R*R),L=Math.min(U,3),b=P,x=C}),Ir.settle({activeWhen:()=>L>.01,settleMs:k=>{let D=k.num(e.damping.name,3);return D>0?Math.min(3e4,5e3/D):3e4}}),Ir.values(\"springs+impulse\",k=>{let D=u.safe(k.frameParams),O=k.num(e.damping.name,3),F=k.dt/2;d.write({cursorX:P,cursorY:C,dirX:U>.01?E/U:0,dirY:U>.01?R/U:0,clampedSpeed:L,dt:k.dt,subDt:F,stiffness:k.num(e.stiffness.name,3),dampFactor:Math.max(0,Math.min(1,1-O*F)),radius:k.num(e.radius.name,1)*.08,aspect:D.width/D.height})}),Ir.pass(h.with(_)),Ir.pass(h.with(f)),Ir.publish(()=>y)]}}}var rl=class Dx{constructor(t){this.build=t}folded(){return new Dx(t=>W(Xae,\"foldAboutCenter\",[this.build(t)]))}tiled(t){return new Dx(s=>W(Hae,\"tilePhase\",[this.build(s),At(t,s)]))}bands(t){return new Tce(this,t)}},Tce=class extends rl{constructor(e,t){super(s=>W(jae,\"bandMidpointCoord\",[e.build(s),At(t,s)])),this.base=e,this.count=t}base;count;shuffled(){return new rl(e=>W(Kae,\"bandShuffleCoord\",[this.base.build(e),At(this.count,e)]))}},Sce=class{constructor(e){this.size=e}diamond(){return new rl(e=>W(Qae,\"cellDiamondCoord\",[e.ctx.uv,e.ctx.aspect,At(this.size,e)]))}checker(){return new rl(e=>W(Zae,\"cellCheckerCoord\",[e.ctx.uv,e.ctx.aspect,At(this.size,e)]))}shuffled(){return new rl(e=>W(Jae,\"cellShuffleCoord\",[e.ctx.uv,e.ctx.aspect,At(this.size,e)]))}},Pce={cw:0,ccw:1,both:2},eo={directional(e){return new rl(t=>W(Vae,\"directionalCoord\",[t.ctx.uv,t.ctx.aspect,At(e,t)]))},radial(e){return new rl(t=>W(Nae,\"radialCornerNormCoord\",[t.ctx.uv,t.ctx.aspect,At(e,t)]))},angular(e,t,s){return new rl(r=>{let o=Pce[typeof s==\"string\"?s:r.propValues[s.name]??\"cw\"]??0;return W($ae,\"angularCoord\",[r.ctx.uv,r.ctx.aspect,At(e,r),At(t,r),$e(o)])})},cells(e){return new Sce(e)},noise(e,t){return new rl(s=>W(ene,\"fbmCoverageCoord\",[s.ctx.uv,s.ctx.aspect,At(e,s),At(t,s)]))}};function to(e){return{kind:\"pointwise\",body:{fn:qae,hint:\"applyReveal\"},args:t=>[W(Wae,\"revealMask\",[e.coverage.build(t),At(e.progress,t),At(e.feather,t),At(e.invert,t)])]}}function $_(e){let t=new WeakMap;return s=>{let r=t.get(s);if(r!==void 0)return r;let o=e(s);return t.set(s,o),o}}function W8(e){return $_(t=>(t.propValues[e.amount.name]??0)===0?t.ctx.uv:ns(W(Ooe,\"watercolorUvBase\",[t.ctx.uv,t.ctx.logicalViewportSize,me(e.amount,t),t.ctx.time]),\"wcUvBase\"))}function q8(e){return $_(t=>{let s=Math.max(1,Math.min(8,Math.round(t.propValues[e.radius.name]??4))),r=Math.max(2,Math.ceil((s+1)/2)),o=1/(r*r),a=e.at(t),i=t.ctx.logicalViewportSize,l=(c,d)=>{let u=ca(\"vec4f(0.0, 0.0, 0.0, 0.0)\"),p=ca(\"vec3f(0.0, 0.0, 0.0)\");for(let _=0;_<r;_++)for(let f=0;f<r;f++){let h=W(Goe,\"watercolorTapUV\",[a,i,$e((2*f+.5)*c),$e((2*_+.5)*d)]),g=ns(t.texture.sample(h),\"wcTap\"),y=g.member(\"rgb\");u=u.add(g),p=p.add(y.mul(y))}return{sum:u,sq:p}};return{quadrants:[l(-1,-1),l(1,-1),l(-1,1),l(1,1)],invN:o}})}function V8(e){return t=>(t.propValues[e.amount.name]??0)===0?$e(0):W(Foe,\"watercolorGrain\",[t.ctx.uv,t.ctx.viewportSize])}function N8(e){return Rd({resultAlpha:\"straight\",build:t=>{let s=u=>me(u,t),r=ns(t.sampleStraight(e.at(t)),\"wcOriginal\");if((t.propValues[e.strength.name]??1)===0)return r;let{quadrants:[o,a,i,l],invN:c}=e.brush(t),d=e.grain(t);return Ct(W(Ioe,\"watercolorCompose\",[o.sum,a.sum,i.sum,l.sum,o.sq,a.sq,i.sq,l.sq,$e(c),r,s(e.paperColor),s(e.paper),s(e.strength),d]),r.member(\"a\"))}})}function $8(e){return $_(t=>{let{sampleStraight:s,ctx:r}=t,o=r.viewportSize,a=(y,b)=>W(sP,\"chalkLuma\",[s(W(Yoe,\"chalkOffsetUV\",[r.uv,o,me(e.spacing,t),$e(y),$e(b)]))]),i=a(-1,-1),l=a(0,-1),c=a(1,-1),d=a(-1,0),u=a(1,0),p=a(-1,1),_=a(0,1),f=a(1,1),h=s(r.uv),g=W(sP,\"chalkLuma\",[h]);return{sobelA:Ct(i,l,c,d),sobelB:Ct(u,p,_,f),centre:h,centreLum:g}})}function X8(e){return Rd({resultAlpha:\"straight\",build:t=>{let s=d=>me(d,t),{sobelA:r,sobelB:o,centre:a,centreLum:i}=e.edges(t),l=Ct(i,a.member(\"a\"),0,0),c=Ct(s(e.sensitivity),s(e.hatchScale),s(e.shading),s(e.grain));return W(Woe,\"chalkboardCompose\",[r,o,t.ctx.uv,t.ctx.viewportSize,l,c,s(e.board),s(e.chalk)])}})}function H8(e){return $_(t=>W(qoe,\"asciiGrid\",[t.ctx.uv,t.ctx.viewportSize,me(e.cellSize,t),me(e.spacing,t)]))}function j8(e){return $_(t=>W(Yt,\"unpremultiplyAlpha\",[t.convertToTexture(t.childNode).sample(e.grid(t).member(\"cellCenter\"))]))}function K8(e){return t=>{let s=e.atlas(t),r=W(Voe,\"asciiAtlasUV\",[e.cell(t).member(\"rgb\"),me(e.gamma,t),t.uniforms._charCount,t.uniforms._atlasSize,t.uniforms._atlasScale,e.grid(t).member(\"cellUV\")]);return s.sample(r,\"nearestClamp\")}}function Q8(e){return t=>{if(!t.childNode)return xt;let s=e.cell(t);return W(Noe,\"asciiCompose\",[e.glyph(t).member(\"rgb\"),s,e.grid(t).member(\"isOutside\"),me(e.alphaThreshold,t),me(e.preserveAlpha,t)])}}var vC=bt,Z8=w.fn([m,n,m,n,n,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=m(__tsover_mul(t.x,s),t.y),c=m(__tsover_mul(r.x,s),__tsover_sub(1,r.y)),d=__tsover_sub(l.x,c.x),u=__tsover_sub(l.y,c.y),p=__tsover_mul(o,vC),_=pe(p),f=ae(p),h=__tsover_add(__tsover_mul(d,_),__tsover_mul(u,f)),g=__tsover_sub(__tsover_mul(u,_),__tsover_mul(d,f)),y=__tsover_add(__tsover_div(h,__tsover_mul(a,2)),.5),b=__tsover_add(__tsover_div(g,__tsover_mul(i,2)),.5);return A(y,b,__tsover_mul(__tsover_mul(__tsover_mul(Et(0,y),Et(y,1)),Et(0,b)),Et(b,1)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"rotationDeg\"},{type:\"i\",name:\"halfW\"},{type:\"i\",name:\"halfH\"}],body:[0,[[13,\"aspectUV\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"centerPos\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[13,\"dx\",[1,[7,\"aspectUV\",\"x\"],\"-\",[7,\"centerPos\",\"x\"]]],[13,\"dy\",[1,[7,\"aspectUV\",\"y\"],\"-\",[7,\"centerPos\",\"y\"]]],[13,\"rotRad\",[1,\"rotationDeg\",\"*\",\"OBF_DEG_TO_RAD\"]],[13,\"cosR\",[6,\"std.cos\",[\"rotRad\"]]],[13,\"sinR\",[6,\"std.sin\",[\"rotRad\"]]],[13,\"rdx\",[1,[1,\"dx\",\"*\",\"cosR\"],\"+\",[1,\"dy\",\"*\",\"sinR\"]]],[13,\"rdy\",[1,[1,\"dy\",\"*\",\"cosR\"],\"-\",[1,\"dx\",\"*\",\"sinR\"]]],[13,\"localU\",[1,[1,\"rdx\",\"/\",[1,\"halfW\",\"*\",[5,\"2\"]]],\"+\",[5,\"0.5\"]]],[13,\"localV\",[1,[1,\"rdy\",\"/\",[1,\"halfH\",\"*\",[5,\"2\"]]],\"+\",[5,\"0.5\"]]],[13,\"insideMask\",[1,[1,[1,[6,\"std.step\",[[5,\"0\"],\"localU\"]],\"*\",[6,\"std.step\",[\"localU\",[5,\"1\"]]]],\"*\",[6,\"std.step\",[[5,\"0\"],\"localV\"]]],\"*\",[6,\"std.step\",[\"localV\",[5,\"1\"]]]]],[10,[6,\"d.vec3f\",[\"localU\",\"localV\",\"insideMask\"]]]]]},externals:{\"d.vec2f\":()=>m,OBF_DEG_TO_RAD:()=>vC,\"std.cos\":()=>pe,\"std.sin\":()=>ae,\"std.step\":()=>Et,\"d.vec3f\":()=>A}})&&e.f)({})),md=Jt,hC=bt,_m=w.fn([m,n,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=__tsover_mul(s,hC),c=pe(l),d=ae(l),u=__tsover_add(__tsover_mul(t.x,__tsover_mul(d,-1)),__tsover_mul(t.y,c)),p=pe(__tsover_add(__tsover_mul(__tsover_mul(u,r),md),o)),_=K(-1.15,1.15,G(a,0,1));return be(__tsover_sub(_,i),__tsover_add(_,i),p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"angleDeg\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"reliefPhase\"},{type:\"i\",name:\"level\"},{type:\"i\",name:\"aa\"}],body:[0,[[13,\"angleRad\",[1,\"angleDeg\",\"*\",\"HP_DEG_TO_RAD\"]],[13,\"c\",[6,\"std.cos\",[\"angleRad\"]]],[13,\"s\",[6,\"std.sin\",[\"angleRad\"]]],[13,\"w\",[1,[1,[7,\"p\",\"x\"],\"*\",[1,\"s\",\"*\",[4,\"-\",[5,\"1\"]]]],\"+\",[1,[7,\"p\",\"y\"],\"*\",\"c\"]]],[13,\"phase\",[1,[1,[1,\"w\",\"*\",\"frequency\"],\"*\",\"HP_TAU\"],\"+\",\"reliefPhase\"]],[13,\"v\",[6,\"std.cos\",[\"phase\"]]],[13,\"thr\",[6,\"std.mix\",[[4,\"-\",[5,\"1.15\"]],[5,\"1.15\"],[6,\"std.clamp\",[\"level\",[5,\"0\"],[5,\"1\"]]]]]],[10,[6,\"std.smoothstep\",[[1,\"thr\",\"-\",\"aa\"],[1,\"thr\",\"+\",\"aa\"],\"v\"]]]]]},externals:{HP_DEG_TO_RAD:()=>hC,\"std.cos\":()=>pe,\"std.sin\":()=>ae,HP_TAU:()=>md,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"std.smoothstep\":()=>be}})&&e.f)({})),gC=w.fn([m,m,n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i)=>{\"use gpu\";let l=t.sub(s),c=he(l),d=Xt(l.y,l.x),u=pe(__tsover_add(__tsover_mul(__tsover_sub(__tsover_mul(c,r),__tsover_mul(d,__tsover_div(1,md))),md),o)),p=K(-1.15,1.15,G(a,0,1));return be(__tsover_sub(p,i),__tsover_add(p,i),u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p\"},{type:\"i\",name:\"c\"},{type:\"i\",name:\"frequency\"},{type:\"i\",name:\"reliefPhase\"},{type:\"i\",name:\"level\"},{type:\"i\",name:\"aa\"}],body:[0,[[13,\"dv\",[6,[7,\"p\",\"sub\"],[\"c\"]]],[13,\"r\",[6,\"std.length\",[\"dv\"]]],[13,\"theta\",[6,\"std.atan2\",[[7,\"dv\",\"y\"],[7,\"dv\",\"x\"]]]],[13,\"phase\",[1,[1,[1,[1,\"r\",\"*\",\"frequency\"],\"-\",[1,\"theta\",\"*\",[1,[5,\"1\"],\"/\",\"HP_TAU\"]]],\"*\",\"HP_TAU\"],\"+\",\"reliefPhase\"]],[13,\"v\",[6,\"std.cos\",[\"phase\"]]],[13,\"thr\",[6,\"std.mix\",[[4,\"-\",[5,\"1.15\"]],[5,\"1.15\"],[6,\"std.clamp\",[\"level\",[5,\"0\"],[5,\"1\"]]]]]],[10,[6,\"std.smoothstep\",[[1,\"thr\",\"-\",\"aa\"],[1,\"thr\",\"+\",\"aa\"],\"v\"]]]]]},externals:{\"std.length\":()=>he,\"std.atan2\":()=>Xt,HP_TAU:()=>md,\"std.cos\":()=>pe,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"std.smoothstep\":()=>be}})&&e.f)({}));function Cce(e){return w.fn([S,m,n,S,S,m,S,S],S)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o,a,i,l,c,d)=>{\"use gpu\";let u=c.x,p=c.y,_=c.z,f=c.w,h=d.x,g=d.y,y=Se(A(s.x,s.y,s.z),A(.299,.587,.114)),b=G(__tsover_add(.5,__tsover_mul(__tsover_sub(y,.5),h)),0,1),x=m(__tsover_mul(r.x,o),r.y),P=la(x.mul(3.1)),C=la(x.mul(3.1).add(m(7.31,3.77))),E=x.add(m(P,C).mul(__tsover_mul(f,.06))),R=G(__tsover_div(__tsover_mul(p,md),B(g,1)),.02,1),U=__tsover_mul(__tsover_mul(_,b),9.42),L=n(0);if(e===2)L=gC(E,m(__tsover_mul(l.x,o),__tsover_sub(1,l.y)),p,U,b,R);else if(e===1){let O=_m(E,u,p,U,b,R),F=G(__tsover_div(b,.55),0,1),q=_m(E,__tsover_add(u,72),__tsover_mul(p,.92),__tsover_mul(U,.7),F,R),Y=G(__tsover_div(b,.28),0,1);L=B(O,B(q,_m(E,__tsover_sub(u,38),__tsover_mul(p,1.13),__tsover_mul(U,.5),Y,R)))}else L=_m(E,u,p,U,b,R);let k=K(A(a.x,a.y,a.z),A(i.x,i.y,i.z),A(L)),D=__tsover_mul(s.w,K(a.w,i.w,L));return S(k.x,k.y,k.z,D)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"childColor\"},{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"paper\"},{type:\"i\",name:\"ink\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"p0\"},{type:\"i\",name:\"p1\"}],body:[0,[[13,\"angleDeg\",[7,\"p0\",\"x\"]],[13,\"frequency\",[7,\"p0\",\"y\"]],[13,\"relief\",[7,\"p0\",\"z\"]],[13,\"waviness\",[7,\"p0\",\"w\"]],[13,\"contrast\",[7,\"p1\",\"x\"]],[13,\"viewportY\",[7,\"p1\",\"y\"]],[13,\"lum\",[6,\"std.dot\",[[6,\"d.vec3f\",[[7,\"childColor\",\"x\"],[7,\"childColor\",\"y\"],[7,\"childColor\",\"z\"]]],[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]],[13,\"level\",[6,\"std.clamp\",[[1,[5,\"0.5\"],\"+\",[1,[1,\"lum\",\"-\",[5,\"0.5\"]],\"*\",\"contrast\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"pa\",[6,\"d.vec2f\",[[1,[7,\"uv\",\"x\"],\"*\",\"aspect\"],[7,\"uv\",\"y\"]]]],[13,\"nx\",[6,\"noise.mxNoiseFloat2\",[[6,[7,\"pa\",\"mul\"],[[5,\"3.1\"]]]]]],[13,\"ny\",[6,\"noise.mxNoiseFloat2\",[[6,[7,[6,[7,\"pa\",\"mul\"],[[5,\"3.1\"]]],\"add\"],[[6,\"d.vec2f\",[[5,\"7.31\"],[5,\"3.77\"]]]]]]]],[13,\"p\",[6,[7,\"pa\",\"add\"],[[6,[7,[6,\"d.vec2f\",[\"nx\",\"ny\"]],\"mul\"],[[1,\"waviness\",\"*\",[5,\"0.06\"]]]]]]],[13,\"aa\",[6,\"std.clamp\",[[1,[1,\"frequency\",\"*\",\"HP_TAU\"],\"/\",[6,\"std.max\",[\"viewportY\",[5,\"1\"]]]],[5,\"0.02\"],[5,\"1\"]]]],[13,\"reliefPhase\",[1,[1,\"relief\",\"*\",\"level\"],\"*\",[5,\"9.42\"]]],[12,\"cover\",[6,\"d.f32\",[[5,\"0\"]]]],[11,[1,\"style\",\"===\",[5,\"2\"]],[0,[[13,\"sc\",[6,\"d.vec2f\",[[1,[7,\"center\",\"x\"],\"*\",\"aspect\"],[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]]]],[2,\"cover\",\"=\",[6,\"spiralPlate\",[\"p\",\"sc\",\"frequency\",\"reliefPhase\",\"level\",\"aa\"]]]]],[11,[1,\"style\",\"===\",[5,\"1\"]],[0,[[13,\"ink1\",[6,\"hatchPlate\",[\"p\",\"angleDeg\",\"frequency\",\"reliefPhase\",\"level\",\"aa\"]]],[13,\"level2\",[6,\"std.clamp\",[[1,\"level\",\"/\",[5,\"0.55\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"ink2\",[6,\"hatchPlate\",[\"p\",[1,\"angleDeg\",\"+\",[5,\"72\"]],[1,\"frequency\",\"*\",[5,\"0.92\"]],[1,\"reliefPhase\",\"*\",[5,\"0.7\"]],\"level2\",\"aa\"]]],[13,\"level3\",[6,\"std.clamp\",[[1,\"level\",\"/\",[5,\"0.28\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"ink3\",[6,\"hatchPlate\",[\"p\",[1,\"angleDeg\",\"-\",[5,\"38\"]],[1,\"frequency\",\"*\",[5,\"1.13\"]],[1,\"reliefPhase\",\"*\",[5,\"0.5\"]],\"level3\",\"aa\"]]],[2,\"cover\",\"=\",[6,\"std.max\",[\"ink1\",[6,\"std.max\",[\"ink2\",\"ink3\"]]]]]]],[0,[[2,\"cover\",\"=\",[6,\"hatchPlate\",[\"p\",\"angleDeg\",\"frequency\",\"reliefPhase\",\"level\",\"aa\"]]]]]]],[13,\"rgb\",[6,\"std.mix\",[[6,\"d.vec3f\",[[7,\"paper\",\"x\"],[7,\"paper\",\"y\"],[7,\"paper\",\"z\"]]],[6,\"d.vec3f\",[[7,\"ink\",\"x\"],[7,\"ink\",\"y\"],[7,\"ink\",\"z\"]]],[6,\"d.vec3f\",[\"cover\"]]]]],[13,\"alpha\",[1,[7,\"childColor\",\"w\"],\"*\",[6,\"std.mix\",[[7,\"paper\",\"w\"],[7,\"ink\",\"w\"],\"cover\"]]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"alpha\"]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"d.vec2f\":()=>m,\"noise.mxNoiseFloat2\":()=>la,HP_TAU:()=>md,\"std.max\":()=>B,\"d.f32\":()=>n,style:()=>e,spiralPlate:()=>gC,hatchPlate:()=>_m,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&t.f)({})).$name(`lineworkComposite_${e}`)}var Mce={line:0,crosshatch:1,spiral:2};function J8(e){return Rd({resultAlpha:\"straight\",build:({sampleStraight:t,ctx:s,uniforms:r,propValues:o})=>{let a=t(s.uv),i=o[e.style.name],l=typeof i==\"number\"?i:Mce[String(i)]??1,c=Cce(l),d=Ct(r[e.angle.name],r[e.frequency.name],r[e.relief.name],r[e.waviness.name]),u=Ct(r[e.contrast.name],s.viewportSize.member(\"y\"),$e(0),$e(0));return W(c,`lineworkComposite_${l}`,[a,s.uv,s.aspect,r[e.paper.name],r[e.ink.name],r[e.center.name],d,u])}})}var uu=gt({pos:m,dir:m,spd:n}).$name(\"AgentPose\");function Ece(e,t){let s=e,r=cz(t.shape,{comet:t.comet}),o=t.res,a=1/o,i=.75*a,l=t.splatRCap,c=r.ext,d=r.soft?rh.SOFT:rh.HARD,u=t.home,p=t.heading===\"angle\"?w.fn([M],uu)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=(b=>{\"use gpu\";let x=s.$.agents[b],P=u(n(b));return uu({pos:m(__tsover_add(P.x,x.x),__tsover_add(P.y,x.y)),dir:m(pe(x.z),ae(x.z)),spd:n(0)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"a\",[8,[7,[7,\"L\",\"$\"],\"agents\"],\"i\"]],[13,\"homeP\",[6,\"home\",[[6,\"d.f32\",[\"i\"]]]]],[10,[6,\"AgentPose\",[[104,{pos:[6,\"d.vec2f\",[[1,[7,\"homeP\",\"x\"],\"+\",[7,\"a\",\"x\"]],[1,[7,\"homeP\",\"y\"],\"+\",[7,\"a\",\"y\"]]]],dir:[6,\"d.vec2f\",[[6,\"std.cos\",[[7,\"a\",\"z\"]]],[6,\"std.sin\",[[7,\"a\",\"z\"]]]]],spd:[6,\"d.f32\",[[5,\"0\"]]]}]]]]]]},externals:{L:()=>s,home:()=>u,\"d.f32\":()=>n,AgentPose:()=>uu,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&y.f)({})).$name(`${t.name}Pose`):w.fn([M],uu)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=(b=>{\"use gpu\";let x=s.$.agents[b],P=m(x.z,x.w),C=he(P);return uu({pos:m(x.x,x.y),dir:N(m(1,0),P.div(B(C,1e-5)),C>1e-5),spd:C})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"a\",[8,[7,[7,\"L\",\"$\"],\"agents\"],\"i\"]],[13,\"vel\",[6,\"d.vec2f\",[[7,\"a\",\"z\"],[7,\"a\",\"w\"]]]],[13,\"spd\",[6,\"std.length\",[\"vel\"]]],[10,[6,\"AgentPose\",[[104,{pos:[6,\"d.vec2f\",[[7,\"a\",\"x\"],[7,\"a\",\"y\"]]],dir:[6,\"std.select\",[[6,\"d.vec2f\",[[5,\"1\"],[5,\"0\"]]],[6,[7,\"vel\",\"div\"],[[6,\"std.max\",[\"spd\",[5,\"0.00001\"]]]]],[1,\"spd\",\">\",[5,\"0.00001\"]]]],spd:\"spd\"}]]]]]]},externals:{L:()=>s,\"d.vec2f\":()=>m,\"std.length\":()=>he,AgentPose:()=>uu,\"std.select\":()=>N,\"std.max\":()=>B}})&&y.f)({})).$name(`${t.name}Pose`),_=t.agitation===\"buffer\"?w.fn([M,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";return G(s.$.agit[b],0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"},{type:\"i\",name:\"_spd\"}],body:[0,[[10,[6,\"std.clamp\",[[8,[7,[7,\"L\",\"$\"],\"agit\"],\"i\"],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G,L:()=>s}})&&y.f)({})).$name(`${t.name}Excitement`):w.fn([M,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x)=>{\"use gpu\";return G(__tsover_div(x,B(s.$.params.speedNorm,1e-5)),0,1)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_i\"},{type:\"i\",name:\"spd\"}],body:[0,[[10,[6,\"std.clamp\",[[1,\"spd\",\"/\",[6,\"std.max\",[[7,[7,[7,\"L\",\"$\"],\"params\"],\"speedNorm\"],[5,\"0.00001\"]]]],[5,\"0\"],[5,\"1\"]]]]]]},externals:{\"std.clamp\":()=>G,\"std.max\":()=>B,L:()=>s}})&&y.f)({})).$name(`${t.name}Excitement`),f=Yne(t.shape),h=k_(r.name),g=t.comet&&!r.symmetric?w.fn([n,n,n,m,n,n,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x,P,C,E,R,U)=>{\"use gpu\";return f(__tsover_add(__tsover_mul(x,C.x),__tsover_mul(P,C.y)),__tsover_add(__tsover_mul(x,-C.y),__tsover_mul(P,C.x)),E,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_dSq\"},{type:\"i\",name:\"offX\"},{type:\"i\",name:\"offY\"},{type:\"i\",name:\"dir\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"_bodyRSq\"},{type:\"i\",name:\"aaW\"}],body:[0,[[13,\"t\",[1,[1,\"offX\",\"*\",[7,\"dir\",\"x\"]],\"+\",[1,\"offY\",\"*\",[7,\"dir\",\"y\"]]]],[13,\"n\",[1,[1,\"offX\",\"*\",[4,\"-\",[7,\"dir\",\"y\"]]],\"+\",[1,\"offY\",\"*\",[7,\"dir\",\"x\"]]]],[10,[6,\"cometWeight\",[\"t\",\"n\",\"bodyR\",\"aaW\"]]]]]},externals:{cometWeight:()=>f}})&&y.f)({})).$name(`${t.name}Weight`):r.symmetric?w.fn([n,n,n,m,n,n,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x,P,C,E,R,U)=>{\"use gpu\";return h(__tsover_div(b,R),x,P,E,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dSq\"},{type:\"i\",name:\"offX\"},{type:\"i\",name:\"offY\"},{type:\"i\",name:\"_dir\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"bodyRSq\"},{type:\"i\",name:\"aaW\"}],body:[0,[[10,[6,\"plainWeight\",[[1,\"dSq\",\"/\",\"bodyRSq\"],\"offX\",\"offY\",\"bodyR\",\"aaW\"]]]]]},externals:{plainWeight:()=>h}})&&y.f)({})).$name(`${t.name}Weight`):w.fn([n,n,n,m,n,n,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x,P,C,E,R,U)=>{\"use gpu\";let L=__tsover_add(__tsover_mul(x,C.x),__tsover_mul(P,C.y)),k=__tsover_add(__tsover_mul(x,-C.y),__tsover_mul(P,C.x));return h(__tsover_div(b,R),L,k,E,U)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"dSq\"},{type:\"i\",name:\"offX\"},{type:\"i\",name:\"offY\"},{type:\"i\",name:\"dir\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"bodyRSq\"},{type:\"i\",name:\"aaW\"}],body:[0,[[13,\"t\",[1,[1,\"offX\",\"*\",[7,\"dir\",\"x\"]],\"+\",[1,\"offY\",\"*\",[7,\"dir\",\"y\"]]]],[13,\"n\",[1,[1,\"offX\",\"*\",[4,\"-\",[7,\"dir\",\"y\"]]],\"+\",[1,\"offY\",\"*\",[7,\"dir\",\"x\"]]]],[10,[6,\"plainWeight\",[[1,\"dSq\",\"/\",\"bodyRSq\"],\"t\",\"n\",\"bodyR\",\"aaW\"]]]]]},externals:{plainWeight:()=>h}})&&y.f)({})).$name(`${t.name}Weight`);return w.fn([M])((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=(b=>{\"use gpu\";let x=s.$.params,P=p(b),C=_(b,P.spd),E=__tsover_mul(C,n(d)),R=B(x.aspect,1e-5),U=G(x.bodyR,1e-6,__tsover_mul(n(l),a)),L=__tsover_mul(U,U),k=__tsover_add(__tsover_mul(U,c),__tsover_mul(i,2)),D=__tsover_mul(k,k),O=oh(P.pos,k,R,n(o));for(let F=O.y0;F<=O.y1;F++){let q=__tsover_sub(__tsover_mul(__tsover_add(n(F),.5),a),P.pos.y),Y=__tsover_mul(q,q);for(let V=O.x0;V<=O.x1;V++){let X=__tsover_sub(__tsover_mul(__tsover_mul(__tsover_add(n(V),.5),a),R),P.pos.x),Q=__tsover_add(__tsover_mul(X,X),Y);if(Q<D){let te=g(Q,X,q,P.dir,U,L,i),J=M(__tsover_mul(te,n(d)));if(J>M(0)){let oe=M(__tsover_add(__tsover_mul(F,j(o)),V));wa(s.$.accumE[oe],J),C>0&&wa(s.$.accumS[oe],M(__tsover_mul(te,E)))}}}}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"L\",\"$\"],\"params\"]],[13,\"p\",[6,\"pose\",[\"i\"]]],[13,\"agitI\",[6,\"excitement\",[\"i\",[7,\"p\",\"spd\"]]]],[13,\"agitGain\",[1,\"agitI\",\"*\",[6,\"d.f32\",[\"GAIN\"]]]],[13,\"aspect\",[6,\"std.max\",[[7,\"prm\",\"aspect\"],[5,\"0.00001\"]]]],[13,\"bodyR\",[6,\"std.clamp\",[[7,\"prm\",\"bodyR\"],[5,\"0.000001\"],[1,[6,\"d.f32\",[\"SPLAT_R\"]],\"*\",\"INV_RES\"]]]],[13,\"bodyRSq\",[1,\"bodyR\",\"*\",\"bodyR\"]],[13,\"radW\",[1,[1,\"bodyR\",\"*\",\"EXT\"],\"+\",[1,\"AA_W\",\"*\",[5,\"2\"]]]],[13,\"extSq\",[1,\"radW\",\"*\",\"radW\"]],[13,\"win\",[6,\"worldSplatWindow\",[[7,\"p\",\"pos\"],\"radW\",\"aspect\",[6,\"d.f32\",[\"RES\"]]]]],[14,[12,\"py\",[7,\"win\",\"y0\"]],[1,\"py\",\"<=\",[7,\"win\",\"y1\"]],[102,\"++\",\"py\"],[0,[[13,\"offY\",[1,[1,[1,[6,\"d.f32\",[\"py\"]],\"+\",[5,\"0.5\"]],\"*\",\"INV_RES\"],\"-\",[7,[7,\"p\",\"pos\"],\"y\"]]],[13,\"offYSq\",[1,\"offY\",\"*\",\"offY\"]],[14,[12,\"px\",[7,\"win\",\"x0\"]],[1,\"px\",\"<=\",[7,\"win\",\"x1\"]],[102,\"++\",\"px\"],[0,[[13,\"offX\",[1,[1,[1,[1,[6,\"d.f32\",[\"px\"]],\"+\",[5,\"0.5\"]],\"*\",\"INV_RES\"],\"*\",\"aspect\"],\"-\",[7,[7,\"p\",\"pos\"],\"x\"]]],[13,\"dSq\",[1,[1,\"offX\",\"*\",\"offX\"],\"+\",\"offYSq\"]],[11,[1,\"dSq\",\"<\",\"extSq\"],[0,[[13,\"w\",[6,\"texelWeight\",[\"dSq\",\"offX\",\"offY\",[7,\"p\",\"dir\"],\"bodyR\",\"bodyRSq\",\"AA_W\"]]],[13,\"e\",[6,\"d.u32\",[[1,\"w\",\"*\",[6,\"d.f32\",[\"GAIN\"]]]]]],[11,[1,\"e\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],[0,[[13,\"idx\",[6,\"d.u32\",[[1,[1,\"py\",\"*\",[6,\"d.i32\",[\"RES\"]]],\"+\",\"px\"]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"L\",\"$\"],\"accumE\"],\"idx\"],\"e\"]],[11,[1,\"agitI\",\">\",[5,\"0\"]],[0,[[6,\"std.atomicAdd\",[[8,[7,[7,\"L\",\"$\"],\"accumS\"],\"idx\"],[6,\"d.u32\",[[1,\"w\",\"*\",\"agitGain\"]]]]]]]]]]]]]]]]]]]]]]},externals:{L:()=>s,pose:()=>p,excitement:()=>_,\"d.f32\":()=>n,GAIN:()=>d,\"std.max\":()=>B,\"std.clamp\":()=>G,SPLAT_R:()=>l,INV_RES:()=>a,EXT:()=>c,AA_W:()=>i,worldSplatWindow:()=>oh,RES:()=>o,texelWeight:()=>g,\"d.u32\":()=>M,\"d.i32\":()=>j,\"std.atomicAdd\":()=>wa}})&&y.f)({})).$name(t.name)}var Dg={orbitalPlace(e,t){let s=t.rate,r=t.radius,o=6.283185307179586;return w.fn([n,m],m)((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=((i,l)=>{\"use gpu\";let c=e.$.params,d=Pr(m(__tsover_add(__tsover_mul(i,2.41),.37),__tsover_add(__tsover_mul(i,.577),8.19))),u=__tsover_mul(__tsover_mul(__tsover_add(.5,d.x),s),c.randomness),p=__tsover_mul(d.y,o),_=__tsover_mul(__tsover_mul(r,c.randomness),__tsover_add(.5,__tsover_mul(d.x,.5))),f=__tsover_add(__tsover_mul(c.time,u),p);return l.add(m(pe(f),ae(f)).mul(_))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"},{type:\"i\",name:\"stored\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"h\",[6,\"noise.hash22\",[[6,\"d.vec2f\",[[1,[1,\"fi\",\"*\",[5,\"2.41\"]],\"+\",[5,\"0.37\"]],[1,[1,\"fi\",\"*\",[5,\"0.577\"]],\"+\",[5,\"8.19\"]]]]]]],[13,\"rate\",[1,[1,[1,[5,\"0.5\"],\"+\",[7,\"h\",\"x\"]],\"*\",\"RATE\"],\"*\",[7,\"prm\",\"randomness\"]]],[13,\"ph\",[1,[7,\"h\",\"y\"],\"*\",\"TAU\"]],[13,\"rad\",[1,[1,\"RAD\",\"*\",[7,\"prm\",\"randomness\"]],\"*\",[1,[5,\"0.5\"],\"+\",[1,[7,\"h\",\"x\"],\"*\",[5,\"0.5\"]]]]],[13,\"t\",[1,[1,[7,\"prm\",\"time\"],\"*\",\"rate\"],\"+\",\"ph\"]],[10,[6,[7,\"stored\",\"add\"],[[6,[7,[6,\"d.vec2f\",[[6,\"std.cos\",[\"t\"]],[6,\"std.sin\",[\"t\"]]]],\"mul\"],[\"rad\"]]]]]]]},externals:{layout:()=>e,\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m,RATE:()=>s,TAU:()=>o,RAD:()=>r,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&a.f)({})).$name(\"pointSlotOrbitalPlace\")},twinkleBrightness(e,t){let s=t.freq,r=6.283185307179586;return w.fn([n],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=(a=>{\"use gpu\";let i=e.$.params,l=__tsover_mul(Xs(__tsover_add(__tsover_mul(a,3.7),.93)),r),c=__tsover_add(__tsover_mul(ae(__tsover_add(__tsover_mul(i.time,s),l)),.5),.5);return __tsover_mul(K(n(1),c,i.twinkle),__tsover_add(.4,__tsover_mul(.6,h1(a))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"ph\",[1,[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"3.7\"]],\"+\",[5,\"0.93\"]]]],\"*\",\"TAU\"]],[13,\"twk\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"prm\",\"time\"],\"*\",\"FREQ\"],\"+\",\"ph\"]]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[10,[1,[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],\"twk\",[7,\"prm\",\"twinkle\"]]],\"*\",[1,[5,\"0.4\"],\"+\",[1,[5,\"0.6\"],\"*\",[6,\"agents.presenceVariation\",[\"fi\"]]]]]]]]},externals:{layout:()=>e,\"noise.hash11\":()=>Xs,TAU:()=>r,\"std.sin\":()=>ae,FREQ:()=>s,\"std.mix\":()=>K,\"d.f32\":()=>n,\"agents.presenceVariation\":()=>h1}})&&o.f)({})).$name(\"pointSlotTwinkleBrightness\")},presenceRadius(e){return w.fn([n],n)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=(s=>{\"use gpu\";return B(__tsover_mul(e.$.params.bodyR,__tsover_add(.55,__tsover_mul(.45,h1(s)))),1e-6)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[10,[6,\"std.max\",[[1,[7,[7,[7,\"layout\",\"$\"],\"params\"],\"bodyR\"],\"*\",[1,[5,\"0.55\"],\"+\",[1,[5,\"0.45\"],\"*\",[6,\"agents.presenceVariation\",[\"fi\"]]]]],[5,\"0.000001\"]]]]]]},externals:{\"std.max\":()=>B,layout:()=>e,\"agents.presenceVariation\":()=>h1}})&&t.f)({})).$name(\"pointSlotPresenceRadius\")}};function kce(e,t){let s=zd[wd(t.shape,\"dot\")],r=wd(t.shape,\"dot\"),o=s.soft,a=s.ext,i=t.res,l=1/i,c=.75*l,d=rh.HARD,u=rh.SOFT,{place:p,brightness:_,bodyRadius:f}=t,h=s.soft===1?(()=>{let g=k_(r);return w.fn([n,n,n,n,n,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x,P,C,E,R)=>{\"use gpu\";return g(b,x,P,C,E)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"},{type:\"i\",name:\"_soft\"}],body:[0,[[10,[6,\"fast\",[\"q\",\"t\",\"n\",\"bodyR\",\"aaW\"]]]]]},externals:{fast:()=>g}})&&y.f)({})).$name(\"agentPointWeight\")})():(()=>{let g=Ine(r);return w.fn([n,n,n,n,n,n],n)((y=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(y.f=((b,x,P,C,E,R)=>{\"use gpu\";return g(x,P,C,E,R)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_q\"},{type:\"i\",name:\"t\"},{type:\"i\",name:\"n\"},{type:\"i\",name:\"bodyR\"},{type:\"i\",name:\"aaW\"},{type:\"i\",name:\"soft\"}],body:[0,[[10,[6,\"general\",[\"t\",\"n\",\"bodyR\",\"aaW\",\"soft\"]]]]]},externals:{general:()=>g}})&&y.f)({})).$name(\"agentPointWeight\")})();return w.fn([M])((g=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(g.f=(y=>{\"use gpu\";let b=e.$.params,x=e.$.agents[y],P=n(y),C=p(P,m(x.x,x.y)),E=_(P),R=B(b.aspect,1e-5),U=B(b.softness,n(o)),L=K(n(a),B(n(a),2.4),U),k=K(n(d),n(u),U),D=f(P),O=__tsover_div(1,D),F=__tsover_add(__tsover_mul(D,L),__tsover_mul(c,2)),q=__tsover_mul(__tsover_mul(F,O),__tsover_mul(F,O)),Y=oh(C,F,R,n(i));for(let V=Y.y0;V<=Y.y1;V++){let X=__tsover_sub(__tsover_mul(__tsover_add(n(V),.5),l),C.y);for(let Q=Y.x0;Q<=Y.x1;Q++){let te=__tsover_sub(__tsover_mul(__tsover_mul(__tsover_add(n(Q),.5),l),R),C.x),J=__tsover_mul(__tsover_mul(__tsover_add(__tsover_mul(te,te),__tsover_mul(X,X)),O),O);if(J<q){let oe=__tsover_mul(h(J,te,X,D,c,U),E),le=M(__tsover_mul(oe,k));if(le>M(0)){let se=M(__tsover_add(__tsover_mul(V,i),Q));wa(e.$.accumE[se],le)}}}}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"s4\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"pos\",[6,\"place\",[\"fi\",[6,\"d.vec2f\",[[7,\"s4\",\"x\"],[7,\"s4\",\"y\"]]]]]],[13,\"bright\",[6,\"brightness\",[\"fi\"]]],[13,\"aspect\",[6,\"std.max\",[[7,\"prm\",\"aspect\"],[5,\"0.00001\"]]]],[13,\"softV\",[6,\"std.max\",[[7,\"prm\",\"softness\"],[6,\"d.f32\",[\"FORCE_SOFT\"]]]]],[13,\"extK\",[6,\"std.mix\",[[6,\"d.f32\",[\"EXT\"]],[6,\"std.max\",[[6,\"d.f32\",[\"EXT\"]],[5,\"2.4\"]]],\"softV\"]]],[13,\"gain\",[6,\"std.mix\",[[6,\"d.f32\",[\"HARD_GAIN\"]],[6,\"d.f32\",[\"SOFT_GAIN\"]],\"softV\"]]],[13,\"bodyR\",[6,\"bodyRadius\",[\"fi\"]]],[13,\"invBodyR\",[1,[5,\"1\"],\"/\",\"bodyR\"]],[13,\"reach\",[1,[1,\"bodyR\",\"*\",\"extK\"],\"+\",[1,\"AA_W\",\"*\",[5,\"2\"]]]],[13,\"reachQ\",[1,[1,\"reach\",\"*\",\"invBodyR\"],\"*\",[1,\"reach\",\"*\",\"invBodyR\"]]],[13,\"win\",[6,\"worldSplatWindow\",[\"pos\",\"reach\",\"aspect\",[6,\"d.f32\",[\"RES\"]]]]],[14,[12,\"py\",[7,\"win\",\"y0\"]],[1,\"py\",\"<=\",[7,\"win\",\"y1\"]],[102,\"++\",\"py\"],[0,[[13,\"offY\",[1,[1,[1,[6,\"d.f32\",[\"py\"]],\"+\",[5,\"0.5\"]],\"*\",\"INV_RES\"],\"-\",[7,\"pos\",\"y\"]]],[14,[12,\"px\",[7,\"win\",\"x0\"]],[1,\"px\",\"<=\",[7,\"win\",\"x1\"]],[102,\"++\",\"px\"],[0,[[13,\"offX\",[1,[1,[1,[1,[6,\"d.f32\",[\"px\"]],\"+\",[5,\"0.5\"]],\"*\",\"INV_RES\"],\"*\",\"aspect\"],\"-\",[7,\"pos\",\"x\"]]],[13,\"q\",[1,[1,[1,[1,\"offX\",\"*\",\"offX\"],\"+\",[1,\"offY\",\"*\",\"offY\"]],\"*\",\"invBodyR\"],\"*\",\"invBodyR\"]],[11,[1,\"q\",\"<\",\"reachQ\"],[0,[[13,\"w\",[1,[6,\"weightFn\",[\"q\",\"offX\",\"offY\",\"bodyR\",\"AA_W\",\"softV\"]],\"*\",\"bright\"]],[13,\"e\",[6,\"d.u32\",[[1,\"w\",\"*\",\"gain\"]]]],[11,[1,\"e\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],[0,[[13,\"idx\",[6,\"d.u32\",[[1,[1,\"py\",\"*\",\"RES\"],\"+\",\"px\"]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumE\"],\"idx\"],\"e\"]]]]]]]]]]]]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,place:()=>p,\"d.vec2f\":()=>m,brightness:()=>_,\"std.max\":()=>B,FORCE_SOFT:()=>o,\"std.mix\":()=>K,EXT:()=>a,HARD_GAIN:()=>d,SOFT_GAIN:()=>u,bodyRadius:()=>f,AA_W:()=>c,worldSplatWindow:()=>oh,RES:()=>i,INV_RES:()=>l,weightFn:()=>h,\"d.u32\":()=>M,\"std.atomicAdd\":()=>wa}})&&g.f)({})).$name(t.name)}function Bce(e,t){let s=w.fn([n,n,n,n,n],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i,l,c,d)=>{\"use gpu\";return Tt(__tsover_mul(a,-2.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"_ddx\"},{type:\"i\",name:\"_ddy\"},{type:\"i\",name:\"_size\"},{type:\"i\",name:\"_soft\"}],body:[0,[[10,[6,\"std.exp\",[[1,\"q\",\"*\",[4,\"-\",[5,\"2.5\"]]]]]]]]},externals:{\"std.exp\":()=>Tt}})&&o.f)({})).$name(t.glow);if(zd[e].soft===1)return s;let r=k_(e);return w.fn([n,n,n,n,n],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i,l,c,d)=>{\"use gpu\";return K(r(a,i,l,c,.9),Tt(__tsover_mul(a,-2.5)),d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"q\"},{type:\"i\",name:\"ddx\"},{type:\"i\",name:\"ddy\"},{type:\"i\",name:\"size\"},{type:\"i\",name:\"soft\"}],body:[0,[[13,\"wHard\",[6,\"weightFn\",[\"q\",\"ddx\",\"ddy\",\"size\",[5,\"0.9\"]]]],[13,\"wSoft\",[6,\"std.exp\",[[1,\"q\",\"*\",[4,\"-\",[5,\"2.5\"]]]]]],[10,[6,\"std.mix\",[\"wHard\",\"wSoft\",\"soft\"]]]]]},externals:{weightFn:()=>r,\"std.exp\":()=>Tt,\"std.mix\":()=>K}})&&o.f)({})).$name(t.blend)}function zce(e,t){let s=Bce(wd(t.shape,\"dot\"),{glow:t.names.glow,blend:t.names.profile}),r=t.outRes,o=t.maxSplatSize,a=t.extQ,i=Math.sqrt(t.extQ);return w.fn([M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=(c=>{\"use gpu\";let d=e.$.params,u=e.$.pos[c],p=e.$.vel[c],_=__tsover_div(1,G(__tsover_sub(1,__tsover_mul(u.z,.35)),.45,2)),f=__tsover_mul(u.x,_),h=__tsover_mul(__tsover_mul(u.y,_),-1),g=__tsover_sub(__tsover_mul(f,d.rotC),__tsover_mul(h,d.rotS)),y=__tsover_add(__tsover_mul(h,d.rotC),__tsover_mul(f,d.rotS)),b=__tsover_add(d.centerX,__tsover_div(__tsover_mul(g,d.scale),d.aspect)),x=__tsover_add(d.centerYv,__tsover_mul(y,d.scale)),P=n(r),C=__tsover_mul(b,P),E=__tsover_mul(x,P),R=G(__tsover_mul(__tsover_mul(__tsover_mul(d.size,__tsover_add(.65,__tsover_mul(.7,u.w))),_),d.scale),.6,n(o)),U=__tsover_mul(__tsover_mul(__tsover_add(.7,__tsover_mul(.6,u.w)),_),_),L=G(__tsover_mul(p.w,d.speedColorK),0,1),k=__tsover_mul(__tsover_mul(U,L),256)>=1,D=j(nl(__tsover_mul(R,n(i)))),O=n(D);if(C>__tsover_mul(O,-1)&&C<__tsover_add(P,O)&&E>__tsover_mul(O,-1)&&E<__tsover_add(P,O)){let F=ah(m(C,E),D,nc(j(__tsover_sub(r,1)),j(__tsover_sub(r,1)))),q=B(__tsover_mul(R,R),.01);for(let Y=F.y0;Y<=F.y1;Y++){let V=__tsover_sub(__tsover_add(n(Y),.5),E),X=__tsover_mul(V,V);for(let Q=F.x0;Q<=F.x1;Q++){let te=__tsover_sub(__tsover_add(n(Q),.5),C),J=__tsover_div(__tsover_add(__tsover_mul(te,te),X),q);if(J<a){let oe=s(J,te,V,R,d.softness),le=M(__tsover_mul(__tsover_mul(oe,U),256));if(le>M(0)){let se=M(__tsover_add(__tsover_mul(Y,j(r)),Q));wa(e.$.accumE[se],le),k&&wa(e.$.accumS[se],M(__tsover_mul(__tsover_mul(__tsover_mul(oe,U),L),256)))}}}}}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"pos\",[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"]],[13,\"vel\",[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"]],[13,\"persp\",[1,[5,\"1\"],\"/\",[6,\"std.clamp\",[[1,[5,\"1\"],\"-\",[1,[7,\"pos\",\"z\"],\"*\",[5,\"0.35\"]]],[5,\"0.45\"],[5,\"2\"]]]]],[13,\"xs\",[1,[7,\"pos\",\"x\"],\"*\",\"persp\"]],[13,\"ys\",[1,[1,[7,\"pos\",\"y\"],\"*\",\"persp\"],\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"rx\",[1,[1,\"xs\",\"*\",[7,\"prm\",\"rotC\"]],\"-\",[1,\"ys\",\"*\",[7,\"prm\",\"rotS\"]]]],[13,\"ry\",[1,[1,\"ys\",\"*\",[7,\"prm\",\"rotC\"]],\"+\",[1,\"xs\",\"*\",[7,\"prm\",\"rotS\"]]]],[13,\"u\",[1,[7,\"prm\",\"centerX\"],\"+\",[1,[1,\"rx\",\"*\",[7,\"prm\",\"scale\"]],\"/\",[7,\"prm\",\"aspect\"]]]],[13,\"v\",[1,[7,\"prm\",\"centerYv\"],\"+\",[1,\"ry\",\"*\",[7,\"prm\",\"scale\"]]]],[13,\"outF\",[6,\"d.f32\",[\"OUT_RES\"]]],[13,\"tx\",[1,\"u\",\"*\",\"outF\"]],[13,\"ty\",[1,\"v\",\"*\",\"outF\"]],[13,\"size\",[6,\"std.clamp\",[[1,[1,[1,[7,\"prm\",\"size\"],\"*\",[1,[5,\"0.65\"],\"+\",[1,[5,\"0.7\"],\"*\",[7,\"pos\",\"w\"]]]],\"*\",\"persp\"],\"*\",[7,\"prm\",\"scale\"]],[5,\"0.6\"],[6,\"d.f32\",[\"MAX_SPLAT_SIZE\"]]]]],[13,\"bright\",[1,[1,[1,[5,\"0.7\"],\"+\",[1,[5,\"0.6\"],\"*\",[7,\"pos\",\"w\"]]],\"*\",\"persp\"],\"*\",\"persp\"]],[13,\"speedNorm\",[6,\"std.clamp\",[[1,[7,\"vel\",\"w\"],\"*\",[7,\"prm\",\"speedColorK\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"doSpeed\",[1,[1,[1,\"bright\",\"*\",\"speedNorm\"],\"*\",[5,\"256\"]],\">=\",[5,\"1\"]]],[13,\"rad\",[6,\"d.i32\",[[6,\"std.ceil\",[[1,\"size\",\"*\",[6,\"d.f32\",[\"EXT\"]]]]]]]],[13,\"radF\",[6,\"d.f32\",[\"rad\"]]],[13,\"onTarget\",[3,[3,[3,[1,\"tx\",\">\",[1,\"radF\",\"*\",[4,\"-\",[5,\"1\"]]]],\"&&\",[1,\"tx\",\"<\",[1,\"outF\",\"+\",\"radF\"]]],\"&&\",[1,\"ty\",\">\",[1,\"radF\",\"*\",[4,\"-\",[5,\"1\"]]]]],\"&&\",[1,\"ty\",\"<\",[1,\"outF\",\"+\",\"radF\"]]]],[11,\"onTarget\",[0,[[13,\"win\",[6,\"texelSplatWindow\",[[6,\"d.vec2f\",[\"tx\",\"ty\"]],\"rad\",[6,\"d.vec2i\",[[6,\"d.i32\",[[1,\"OUT_RES\",\"-\",[5,\"1\"]]]],[6,\"d.i32\",[[1,\"OUT_RES\",\"-\",[5,\"1\"]]]]]]]]],[13,\"sizeSq\",[6,\"std.max\",[[1,\"size\",\"*\",\"size\"],[5,\"0.01\"]]]],[14,[12,\"py\",[7,\"win\",\"y0\"]],[1,\"py\",\"<=\",[7,\"win\",\"y1\"]],[102,\"++\",\"py\"],[0,[[13,\"ddy\",[1,[1,[6,\"d.f32\",[\"py\"]],\"+\",[5,\"0.5\"]],\"-\",\"ty\"]],[13,\"ddySq\",[1,\"ddy\",\"*\",\"ddy\"]],[14,[12,\"px\",[7,\"win\",\"x0\"]],[1,\"px\",\"<=\",[7,\"win\",\"x1\"]],[102,\"++\",\"px\"],[0,[[13,\"ddx\",[1,[1,[6,\"d.f32\",[\"px\"]],\"+\",[5,\"0.5\"]],\"-\",\"tx\"]],[13,\"q\",[1,[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",\"ddySq\"],\"/\",\"sizeSq\"]],[11,[1,\"q\",\"<\",\"EXTQ\"],[0,[[13,\"w\",[6,\"profileFn\",[\"q\",\"ddx\",\"ddy\",\"size\",[7,\"prm\",\"softness\"]]]],[13,\"e\",[6,\"d.u32\",[[1,[1,\"w\",\"*\",\"bright\"],\"*\",[5,\"256\"]]]]],[11,[1,\"e\",\">\",[6,\"d.u32\",[[5,\"0\"]]]],[0,[[13,\"idx\",[6,\"d.u32\",[[1,[1,\"py\",\"*\",[6,\"d.i32\",[\"OUT_RES\"]]],\"+\",\"px\"]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumE\"],\"idx\"],\"e\"]],[11,\"doSpeed\",[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumS\"],\"idx\"],[6,\"d.u32\",[[1,[1,[1,\"w\",\"*\",\"bright\"],\"*\",\"speedNorm\"],\"*\",[5,\"256\"]]]]]]]]]]]]]]]]]]]]]]]]},externals:{layout:()=>e,\"std.clamp\":()=>G,\"d.f32\":()=>n,OUT_RES:()=>r,MAX_SPLAT_SIZE:()=>o,\"d.i32\":()=>j,\"std.ceil\":()=>nl,EXT:()=>i,texelSplatWindow:()=>ah,\"d.vec2f\":()=>m,\"d.vec2i\":()=>nc,\"std.max\":()=>B,EXTQ:()=>a,profileFn:()=>s,\"d.u32\":()=>M,\"std.atomicAdd\":()=>wa}})&&l.f)({})).$name(t.names.splat)}function Rce(e,t){let s=wd(t.shape,\"dot\"),r=zd[s],o=k_(s),a=r.soft===1?Math.max(r.ext,2.4):r.ext,i=t.splatRCap,l=t.aaW,c=t.minAlpha,d=t.fp,u=1/t.fp,p=t.wobbleFreq,_=t.perspK,f=6.283185307179586;return w.fn([M,M])((h=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(h.f=((g,y)=>{\"use gpu\";let b=e.$.params,x=__tsover_add(__tsover_mul(y,M(b.gridW)),g),P=e.$.pos[x],C=e.$.col[x],E=C.w;if(E>c){let R=__tsover_div(__tsover_add(n(g),.5),b.gridW),U=__tsover_div(__tsover_add(n(y),.5),b.gridH),L=__tsover_mul(P.w,f),k=b.wobbleAmp,D=__tsover_mul(__tsover_mul(ae(__tsover_add(__tsover_mul(b.time,p),L)),k),.012),O=__tsover_mul(__tsover_mul(pe(__tsover_add(__tsover_mul(__tsover_mul(b.time,p),1.13),__tsover_mul(L,1.7))),k),.012),F=__tsover_mul(__tsover_mul(ae(__tsover_add(__tsover_mul(__tsover_mul(b.time,p),.87),__tsover_mul(L,2.3))),k),.06),q=eh(A(__tsover_add(__tsover_add(__tsover_mul(__tsover_sub(R,.5),b.aspect),P.x),D),__tsover_add(__tsover_add(__tsover_sub(U,.5),P.y),O),__tsover_add(P.z,F)),b.rowX,b.rowY,b.rowZ),Y=__tsover_div(1,G(__tsover_sub(1,__tsover_mul(q.z,_)),.35,2.6)),V=__tsover_mul(Y,b.zoom),X=__tsover_add(__tsover_add(.5,__tsover_div(__tsover_mul(q.x,V),b.aspect)),b.transX),Q=__tsover_add(__tsover_add(.5,__tsover_mul(q.y,V)),b.transY),te=__tsover_mul(X,b.outW),J=__tsover_mul(Q,b.outH),oe=G(__tsover_mul(__tsover_mul(__tsover_mul(b.particleSize,b.spacing),.5),V),.5,n(i)),le=B(b.depth,.001),se=be(__tsover_mul(le,-1),le,q.z),Te=K(n(1),__tsover_add(.35,__tsover_mul(.65,se)),b.depthShading),de=__tsover_mul(__tsover_mul(Y,Y),Te),xe=__tsover_mul(E,de),Ae=__tsover_add(n(a),__tsover_div(n(l),oe)),De=j(nl(__tsover_mul(oe,Ae))),Ee=n(De),ze=te>__tsover_mul(Ee,-1)&&te<__tsover_add(b.outW,Ee)&&J>__tsover_mul(Ee,-1)&&J<__tsover_add(b.outH,Ee);if(xe>u&&ze){let Ie=M(b.outW),ke=ah(m(te,J),De,nc(__tsover_sub(j(b.outW),1),__tsover_sub(j(b.outH),1)));for(let Me=ke.y0;Me<=ke.y1;Me++){let we=__tsover_sub(__tsover_add(n(Me),.5),J);for(let Ve=ke.x0;Ve<=ke.x1;Ve++){let qe=__tsover_sub(__tsover_add(n(Ve),.5),te),st=__tsover_div(__tsover_add(__tsover_mul(qe,qe),__tsover_mul(we,we)),B(__tsover_mul(oe,oe),.01));if(st<__tsover_mul(Ae,Ae)){let pt=__tsover_mul(o(st,qe,we,oe,n(l)),xe);if(pt>0){let Ze=__tsover_add(__tsover_mul(M(Me),Ie),M(Ve));wa(e.$.accumR[Ze],M(__tsover_mul(__tsover_mul(C.x,pt),d))),wa(e.$.accumG[Ze],M(__tsover_mul(__tsover_mul(C.y,pt),d))),wa(e.$.accumB[Ze],M(__tsover_mul(__tsover_mul(C.z,pt),d))),wa(e.$.accumW[Ze],M(__tsover_mul(pt,d)))}}}}}}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gx\"},{type:\"i\",name:\"gy\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"i\",[1,[1,\"gy\",\"*\",[6,\"d.u32\",[[7,\"P\",\"gridW\"]]]],\"+\",\"gx\"]],[13,\"p4\",[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"]],[13,\"col\",[8,[7,[7,\"layout\",\"$\"],\"col\"],\"i\"]],[13,\"childA\",[7,\"col\",\"w\"]],[11,[1,\"childA\",\">\",\"MIN_ALPHA\"],[0,[[13,\"homeU\",[1,[1,[6,\"d.f32\",[\"gx\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"P\",\"gridW\"]]],[13,\"homeV\",[1,[1,[6,\"d.f32\",[\"gy\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"P\",\"gridH\"]]],[13,\"ph\",[1,[7,\"p4\",\"w\"],\"*\",\"TAU\"]],[13,\"wob\",[7,\"P\",\"wobbleAmp\"]],[13,\"wx\",[1,[1,[6,\"std.sin\",[[1,[1,[7,\"P\",\"time\"],\"*\",\"WOBBLE_FREQ\"],\"+\",\"ph\"]]],\"*\",\"wob\"],\"*\",[5,\"0.012\"]]],[13,\"wy\",[1,[1,[6,\"std.cos\",[[1,[1,[1,[7,\"P\",\"time\"],\"*\",\"WOBBLE_FREQ\"],\"*\",[5,\"1.13\"]],\"+\",[1,\"ph\",\"*\",[5,\"1.7\"]]]]],\"*\",\"wob\"],\"*\",[5,\"0.012\"]]],[13,\"wz\",[1,[1,[6,\"std.sin\",[[1,[1,[1,[7,\"P\",\"time\"],\"*\",\"WOBBLE_FREQ\"],\"*\",[5,\"0.87\"]],\"+\",[1,\"ph\",\"*\",[5,\"2.3\"]]]]],\"*\",\"wob\"],\"*\",[5,\"0.06\"]]],[13,\"pw\",[6,\"d.vec3f\",[[1,[1,[1,[1,\"homeU\",\"-\",[5,\"0.5\"]],\"*\",[7,\"P\",\"aspect\"]],\"+\",[7,\"p4\",\"x\"]],\"+\",\"wx\"],[1,[1,[1,\"homeV\",\"-\",[5,\"0.5\"]],\"+\",[7,\"p4\",\"y\"]],\"+\",\"wy\"],[1,[7,\"p4\",\"z\"],\"+\",\"wz\"]]]],[13,\"rv\",[6,\"agents.applyCameraRows\",[\"pw\",[7,\"P\",\"rowX\"],[7,\"P\",\"rowY\"],[7,\"P\",\"rowZ\"]]]],[13,\"persp0\",[1,[5,\"1\"],\"/\",[6,\"std.clamp\",[[1,[5,\"1\"],\"-\",[1,[7,\"rv\",\"z\"],\"*\",\"PERSP_K\"]],[5,\"0.35\"],[5,\"2.6\"]]]]],[13,\"persp\",[1,\"persp0\",\"*\",[7,\"P\",\"zoom\"]]],[13,\"su\",[1,[1,[5,\"0.5\"],\"+\",[1,[1,[7,\"rv\",\"x\"],\"*\",\"persp\"],\"/\",[7,\"P\",\"aspect\"]]],\"+\",[7,\"P\",\"transX\"]]],[13,\"sv\",[1,[1,[5,\"0.5\"],\"+\",[1,[7,\"rv\",\"y\"],\"*\",\"persp\"]],\"+\",[7,\"P\",\"transY\"]]],[13,\"tx\",[1,\"su\",\"*\",[7,\"P\",\"outW\"]]],[13,\"ty\",[1,\"sv\",\"*\",[7,\"P\",\"outH\"]]],[13,\"dotR\",[6,\"std.clamp\",[[1,[1,[1,[7,\"P\",\"particleSize\"],\"*\",[7,\"P\",\"spacing\"]],\"*\",[5,\"0.5\"]],\"*\",\"persp\"],[5,\"0.5\"],[6,\"d.f32\",[\"SPLAT_R\"]]]]],[13,\"dr\",[6,\"std.max\",[[7,\"P\",\"depth\"],[5,\"0.001\"]]]],[13,\"near\",[6,\"std.smoothstep\",[[1,\"dr\",\"*\",[4,\"-\",[5,\"1\"]]],\"dr\",[7,\"rv\",\"z\"]]]],[13,\"shade\",[6,\"std.mix\",[[6,\"d.f32\",[[5,\"1\"]]],[1,[5,\"0.35\"],\"+\",[1,[5,\"0.65\"],\"*\",\"near\"]],[7,\"P\",\"depthShading\"]]]],[13,\"depthWeight\",[1,[1,\"persp0\",\"*\",\"persp0\"],\"*\",\"shade\"]],[13,\"cw\",[1,\"childA\",\"*\",\"depthWeight\"]],[13,\"extR\",[1,[6,\"d.f32\",[\"EXT_SHAPE\"]],\"+\",[1,[6,\"d.f32\",[\"AA_W\"]],\"/\",\"dotR\"]]],[13,\"rad\",[6,\"d.i32\",[[6,\"std.ceil\",[[1,\"dotR\",\"*\",\"extR\"]]]]]],[13,\"radF\",[6,\"d.f32\",[\"rad\"]]],[13,\"onTarget\",[3,[3,[3,[1,\"tx\",\">\",[1,\"radF\",\"*\",[4,\"-\",[5,\"1\"]]]],\"&&\",[1,\"tx\",\"<\",[1,[7,\"P\",\"outW\"],\"+\",\"radF\"]]],\"&&\",[1,\"ty\",\">\",[1,\"radF\",\"*\",[4,\"-\",[5,\"1\"]]]]],\"&&\",[1,\"ty\",\"<\",[1,[7,\"P\",\"outH\"],\"+\",\"radF\"]]]],[11,[3,[1,\"cw\",\">\",\"MIN_WEIGHT\"],\"&&\",\"onTarget\"],[0,[[13,\"owU\",[6,\"d.u32\",[[7,\"P\",\"outW\"]]]],[13,\"win\",[6,\"texelSplatWindow\",[[6,\"d.vec2f\",[\"tx\",\"ty\"]],\"rad\",[6,\"d.vec2i\",[[1,[6,\"d.i32\",[[7,\"P\",\"outW\"]]],\"-\",[5,\"1\"]],[1,[6,\"d.i32\",[[7,\"P\",\"outH\"]]],\"-\",[5,\"1\"]]]]]]],[14,[12,\"py\",[7,\"win\",\"y0\"]],[1,\"py\",\"<=\",[7,\"win\",\"y1\"]],[102,\"++\",\"py\"],[0,[[13,\"ddy\",[1,[1,[6,\"d.f32\",[\"py\"]],\"+\",[5,\"0.5\"]],\"-\",\"ty\"]],[14,[12,\"px\",[7,\"win\",\"x0\"]],[1,\"px\",\"<=\",[7,\"win\",\"x1\"]],[102,\"++\",\"px\"],[0,[[13,\"ddx\",[1,[1,[6,\"d.f32\",[\"px\"]],\"+\",[5,\"0.5\"]],\"-\",\"tx\"]],[13,\"q\",[1,[1,[1,\"ddx\",\"*\",\"ddx\"],\"+\",[1,\"ddy\",\"*\",\"ddy\"]],\"/\",[6,\"std.max\",[[1,\"dotR\",\"*\",\"dotR\"],[5,\"0.01\"]]]]],[11,[1,\"q\",\"<\",[1,\"extR\",\"*\",\"extR\"]],[0,[[13,\"wc\",[1,[6,\"weightFn\",[\"q\",\"ddx\",\"ddy\",\"dotR\",[6,\"d.f32\",[\"AA_W\"]]]],\"*\",\"cw\"]],[11,[1,\"wc\",\">\",[5,\"0\"]],[0,[[13,\"idx\",[1,[1,[6,\"d.u32\",[\"py\"]],\"*\",\"owU\"],\"+\",[6,\"d.u32\",[\"px\"]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumR\"],\"idx\"],[6,\"d.u32\",[[1,[1,[7,\"col\",\"x\"],\"*\",\"wc\"],\"*\",\"FP\"]]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumG\"],\"idx\"],[6,\"d.u32\",[[1,[1,[7,\"col\",\"y\"],\"*\",\"wc\"],\"*\",\"FP\"]]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumB\"],\"idx\"],[6,\"d.u32\",[[1,[1,[7,\"col\",\"z\"],\"*\",\"wc\"],\"*\",\"FP\"]]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"accumW\"],\"idx\"],[6,\"d.u32\",[[1,\"wc\",\"*\",\"FP\"]]]]]]]]]]]]]]]]]]]]]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,MIN_ALPHA:()=>c,\"d.f32\":()=>n,TAU:()=>f,\"std.sin\":()=>ae,WOBBLE_FREQ:()=>p,\"std.cos\":()=>pe,\"d.vec3f\":()=>A,\"agents.applyCameraRows\":()=>eh,\"std.clamp\":()=>G,PERSP_K:()=>_,SPLAT_R:()=>i,\"std.max\":()=>B,\"std.smoothstep\":()=>be,\"std.mix\":()=>K,EXT_SHAPE:()=>a,AA_W:()=>l,\"d.i32\":()=>j,\"std.ceil\":()=>nl,MIN_WEIGHT:()=>u,texelSplatWindow:()=>ah,\"d.vec2f\":()=>m,\"d.vec2i\":()=>nc,weightFn:()=>o,\"std.atomicAdd\":()=>wa,FP:()=>d}})&&h.f)({})).$name(t.name)}function Dce(e,t){let s=e,r=No[t.colorSpace]??Ar,o=t.res,a=t.invGain,i=t.exposure===!0?w.fn([n],n)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";return __tsover_mul(__tsover_mul(d,a),s.$.params.exposure)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"eRaw\"}],body:[0,[[10,[1,[1,\"eRaw\",\"*\",\"INV_GAIN\"],\"*\",[7,[7,[7,\"L\",\"$\"],\"params\"],\"exposure\"]]]]]},externals:{INV_GAIN:()=>a,L:()=>s}})&&c.f)({})).$name(`${t.name}Energy`):w.fn([n],n)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";return __tsover_mul(d,a)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"eRaw\"}],body:[0,[[10,[1,\"eRaw\",\"*\",\"INV_GAIN\"]]]]},externals:{INV_GAIN:()=>a}})&&c.f)({})).$name(`${t.name}Energy`);if(t.trails===\"none\")return w.fn([M,M])((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u)=>{\"use gpu\";let p=s.$.params,_=__tsover_add(__tsover_mul(u,M(o)),d),f=n(ba(s.$.accumE[_]));xa(s.$.accumE[_],M(0));let h=n(ba(s.$.accumS[_]));if(xa(s.$.accumS[_],M(0)),f>0){let g=i(f),y=__tsover_div(h,B(f,1)),b=r(p.colA,p.colB,G(y,0,1)),x=__tsover_mul(Kl(g,1.6),b.w);lt(s.$.outTex,fe(d,u),S(b.x,b.y,b.z,x))}else lt(s.$.outTex,fe(d,u),S(p.colA.x,p.colA.y,p.colA.z,n(0)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"prm\",[7,[7,\"L\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"y\",\"*\",[6,\"d.u32\",[\"RES\"]]],\"+\",\"x\"]],[13,\"eRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"L\",\"$\"],\"accumE\"],\"idx\"]]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"L\",\"$\"],\"accumE\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"sRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"L\",\"$\"],\"accumS\"],\"idx\"]]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"L\",\"$\"],\"accumS\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[11,[1,\"eRaw\",\">\",[5,\"0\"]],[0,[[13,\"energy\",[6,\"energyOf\",[\"eRaw\"]]],[13,\"agitAvg\",[1,\"sRaw\",\"/\",[6,\"std.max\",[\"eRaw\",[5,\"1\"]]]]],[13,\"col\",[6,\"mixFn\",[[7,\"prm\",\"colA\"],[7,\"prm\",\"colB\"],[6,\"std.clamp\",[\"agitAvg\",[5,\"0\"],[5,\"1\"]]]]]],[13,\"a\",[1,[6,\"energyCoverageAlpha\",[\"energy\",[5,\"1.6\"]]],\"*\",[7,\"col\",\"w\"]]],[6,\"std.textureStore\",[[7,[7,\"L\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"x\",\"y\"]],[6,\"d.vec4f\",[[7,\"col\",\"x\"],[7,\"col\",\"y\"],[7,\"col\",\"z\"],\"a\"]]]]]],[0,[[6,\"std.textureStore\",[[7,[7,\"L\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"x\",\"y\"]],[6,\"d.vec4f\",[[7,[7,\"prm\",\"colA\"],\"x\"],[7,[7,\"prm\",\"colA\"],\"y\"],[7,[7,\"prm\",\"colA\"],\"z\"],[6,\"d.f32\",[[5,\"0\"]]]]]]]]]]]]},externals:{L:()=>s,\"d.u32\":()=>M,RES:()=>o,\"d.f32\":()=>n,\"std.atomicLoad\":()=>ba,\"std.atomicStore\":()=>xa,energyOf:()=>i,\"std.max\":()=>B,mixFn:()=>r,\"std.clamp\":()=>G,energyCoverageAlpha:()=>Kl,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&c.f)({})).$name(t.name);let l=t.trails===\"on\"||t.trails.baked?w.fn([S,n,M],S)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";return SP(d,u,s.$.trailBuf[p],s.$.params.trails)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"col\"},{type:\"i\",name:\"curA\"},{type:\"i\",name:\"idx\"}],body:[0,[[10,[6,\"agents.trailMaxDecay\",[\"col\",\"curA\",[8,[7,[7,\"L\",\"$\"],\"trailBuf\"],\"idx\"],[7,[7,[7,\"L\",\"$\"],\"params\"],\"trails\"]]]]]]},externals:{\"agents.trailMaxDecay\":()=>SP,L:()=>s}})&&c.f)({})).$name(`${t.name}TrailComposite`):w.fn([S,n,M],S)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";return S(d.x,d.y,d.z,u)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"col\"},{type:\"i\",name:\"curA\"},{type:\"i\",name:\"_idx\"}],body:[0,[[10,[6,\"d.vec4f\",[[7,\"col\",\"x\"],[7,\"col\",\"y\"],[7,\"col\",\"z\"],\"curA\"]]]]]},externals:{\"d.vec4f\":()=>S}})&&c.f)({})).$name(`${t.name}TrailComposite`);return w.fn([M,M])((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u)=>{\"use gpu\";let p=s.$.params,_=__tsover_add(__tsover_mul(u,M(o)),d),f=n(ba(s.$.accumE[_]));xa(s.$.accumE[_],M(0));let h=n(ba(s.$.accumS[_]));xa(s.$.accumS[_],M(0));let g=i(f),y=S(p.colA);if(f>0){let P=__tsover_div(h,B(f,1));y=r(p.colA,p.colB,G(P,0,1))}let b=__tsover_mul(Kl(g,1.6),y.w),x=l(y,b,_);s.$.trailBuf[_]=S(x),lt(s.$.outTex,fe(d,u),x)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"prm\",[7,[7,\"L\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"y\",\"*\",[6,\"d.u32\",[\"RES\"]]],\"+\",\"x\"]],[13,\"eRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"L\",\"$\"],\"accumE\"],\"idx\"]]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"L\",\"$\"],\"accumE\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"sRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"L\",\"$\"],\"accumS\"],\"idx\"]]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"L\",\"$\"],\"accumS\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"energy\",[6,\"energyOf\",[\"eRaw\"]]],[12,\"col\",[6,\"d.vec4f\",[[7,\"prm\",\"colA\"]]]],[11,[1,\"eRaw\",\">\",[5,\"0\"]],[0,[[13,\"agitAvg\",[1,\"sRaw\",\"/\",[6,\"std.max\",[\"eRaw\",[5,\"1\"]]]]],[2,\"col\",\"=\",[6,\"mixFn\",[[7,\"prm\",\"colA\"],[7,\"prm\",\"colB\"],[6,\"std.clamp\",[\"agitAvg\",[5,\"0\"],[5,\"1\"]]]]]]]]],[13,\"curA\",[1,[6,\"energyCoverageAlpha\",[\"energy\",[5,\"1.6\"]]],\"*\",[7,\"col\",\"w\"]]],[13,\"out\",[6,\"composite\",[\"col\",\"curA\",\"idx\"]]],[2,[8,[7,[7,\"L\",\"$\"],\"trailBuf\"],\"idx\"],\"=\",[6,\"d.vec4f\",[\"out\"]]],[6,\"std.textureStore\",[[7,[7,\"L\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"x\",\"y\"]],\"out\"]]]]},externals:{L:()=>s,\"d.u32\":()=>M,RES:()=>o,\"d.f32\":()=>n,\"std.atomicLoad\":()=>ba,\"std.atomicStore\":()=>xa,energyOf:()=>i,\"d.vec4f\":()=>S,\"std.max\":()=>B,mixFn:()=>r,\"std.clamp\":()=>G,energyCoverageAlpha:()=>Kl,composite:()=>l,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe}})&&c.f)({})).$name(t.name)}function Lce(e,t){let s=t.res;return w.fn([M,M])((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a)=>{\"use gpu\";let i=e.$.params,l=__tsover_add(__tsover_mul(a,M(s)),o),c=__tsover_mul(n(ba(e.$.accumE[l])),__tsover_div(1,255));xa(e.$.accumE[l],M(0));let d=__tsover_mul(Kl(c,1.6),i.color.w);lt(e.$.outTex,fe(o,a),S(i.color.x,i.color.y,i.color.z,d))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"y\",\"*\",[6,\"d.u32\",[\"RES\"]]],\"+\",\"x\"]],[13,\"energy\",[1,[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"accumE\"],\"idx\"]]]]],\"*\",[1,[5,\"1\"],\"/\",[5,\"255\"]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"accumE\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"a\",[1,[6,\"energyCoverageAlpha\",[\"energy\",[5,\"1.6\"]]],\"*\",[7,[7,\"prm\",\"color\"],\"w\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"x\",\"y\"]],[6,\"d.vec4f\",[[7,[7,\"prm\",\"color\"],\"x\"],[7,[7,\"prm\",\"color\"],\"y\"],[7,[7,\"prm\",\"color\"],\"z\"],\"a\"]]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,RES:()=>s,\"d.f32\":()=>n,\"std.atomicLoad\":()=>ba,\"std.atomicStore\":()=>xa,energyCoverageAlpha:()=>Kl,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&r.f)({})).$name(t.name)}function Uce(e,t){let s=t.fp,r=t.alphaK;return w.fn([M,M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i)=>{\"use gpu\";let l=e.$.params,c=__tsover_add(__tsover_mul(i,M(l.outW)),a),d=n(ba(e.$.accumW[c])),u=n(ba(e.$.accumR[c])),p=n(ba(e.$.accumG[c])),_=n(ba(e.$.accumB[c]));xa(e.$.accumR[c],M(0)),xa(e.$.accumG[c],M(0)),xa(e.$.accumB[c],M(0)),xa(e.$.accumW[c],M(0));let f=B(d,n(1)),h=__tsover_div(u,f),g=__tsover_div(p,f),y=__tsover_div(_,f),b=Kl(__tsover_mul(d,__tsover_div(1,s)),r);lt(e.$.outTex,fe(a,i),S(h,g,y,b))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"x\"},{type:\"i\",name:\"y\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"idx\",[1,[1,\"y\",\"*\",[6,\"d.u32\",[[7,\"P\",\"outW\"]]]],\"+\",\"x\"]],[13,\"wRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"accumW\"],\"idx\"]]]]]],[13,\"rRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"accumR\"],\"idx\"]]]]]],[13,\"gRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"accumG\"],\"idx\"]]]]]],[13,\"bRaw\",[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"accumB\"],\"idx\"]]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"accumR\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"accumG\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"accumB\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"accumW\"],\"idx\"],[6,\"d.u32\",[[5,\"0\"]]]]],[13,\"denom\",[6,\"std.max\",[\"wRaw\",[6,\"d.f32\",[[5,\"1\"]]]]]],[13,\"r\",[1,\"rRaw\",\"/\",\"denom\"]],[13,\"g\",[1,\"gRaw\",\"/\",\"denom\"]],[13,\"b\",[1,\"bRaw\",\"/\",\"denom\"]],[13,\"alpha\",[6,\"energyCoverageAlpha\",[[1,\"wRaw\",\"*\",[1,[5,\"1\"],\"/\",\"FP\"]],\"ALPHA_K\"]]],[6,\"std.textureStore\",[[7,[7,\"layout\",\"$\"],\"outTex\"],[6,\"d.vec2u\",[\"x\",\"y\"]],[6,\"d.vec4f\",[\"r\",\"g\",\"b\",\"alpha\"]]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.atomicLoad\":()=>ba,\"std.atomicStore\":()=>xa,\"std.max\":()=>B,energyCoverageAlpha:()=>Kl,FP:()=>s,ALPHA_K:()=>r,\"std.textureStore\":()=>lt,\"d.vec2u\":()=>fe,\"d.vec4f\":()=>S}})&&o.f)({})).$name(t.name)}var w1={orientedWorld:Ece,pointWorld:kce,volume:zce,relief:Rce},T1={ramp:Dce,tint:Lce,weightedColor:Uce};function Oce(e,t){let s=t.gridDim,r=t.domain,o=w.fn([j,j,j],n)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=j(s),f=G(d,0,__tsover_sub(_,1)),h=G(u,0,__tsover_sub(_,1)),g=G(p,0,__tsover_sub(_,1)),y=M(__tsover_add(__tsover_mul(__tsover_add(__tsover_mul(g,_),h),_),f));return __tsover_mul(n(ba(e.$.dens[y])),__tsover_div(1,64))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"cx\"},{type:\"i\",name:\"cy\"},{type:\"i\",name:\"cz\"}],body:[0,[[13,\"g\",[6,\"d.i32\",[\"DGRID\"]]],[13,\"x\",[6,\"std.clamp\",[\"cx\",[5,\"0\"],[1,\"g\",\"-\",[5,\"1\"]]]]],[13,\"y\",[6,\"std.clamp\",[\"cy\",[5,\"0\"],[1,\"g\",\"-\",[5,\"1\"]]]]],[13,\"z\",[6,\"std.clamp\",[\"cz\",[5,\"0\"],[1,\"g\",\"-\",[5,\"1\"]]]]],[13,\"idx\",[6,\"d.u32\",[[1,[1,[1,[1,\"z\",\"*\",\"g\"],\"+\",\"y\"],\"*\",\"g\"],\"+\",\"x\"]]]],[10,[1,[6,\"d.f32\",[[6,\"std.atomicLoad\",[[8,[7,[7,\"layout\",\"$\"],\"dens\"],\"idx\"]]]]],\"*\",[1,[5,\"1\"],\"/\",[5,\"64\"]]]]]]},externals:{\"d.i32\":()=>j,DGRID:()=>s,\"std.clamp\":()=>G,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.atomicLoad\":()=>ba,layout:()=>e}})&&c.f)({})),a=w.fn([A],S)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";let u=e.$.params,p=n(s),_=A(__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(d.x,u.gridOffX),r),__tsover_mul(2,r)),p),.5),__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(d.y,u.gridOffY),r),__tsover_mul(2,r)),p),.5),__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(d.z,u.gridOffZ),r),__tsover_mul(2,r)),p),.5)),f=G(ie(_),A(0,0,0),A(__tsover_sub(p,2),__tsover_sub(p,2),__tsover_sub(p,2))),h=G(_.sub(f),A(0,0,0),A(1,1,1)),g=j(f.x),y=j(f.y),b=j(f.z),x=n(0),P=n(0),C=n(0),E=n(0);for(let R=0;R<2;R++)for(let U=0;U<2;U++)for(let L=0;L<2;L++){let k=K(__tsover_sub(1,h.x),h.x,n(L)),D=K(__tsover_sub(1,h.y),h.y,n(U)),O=K(__tsover_sub(1,h.z),h.z,n(R)),F=__tsover_sub(__tsover_mul(n(L),2),1),q=__tsover_sub(__tsover_mul(n(U),2),1),Y=__tsover_sub(__tsover_mul(n(R),2),1),V=o(__tsover_add(g,L),__tsover_add(y,U),__tsover_add(b,R));x=__tsover_add(x,__tsover_mul(V,__tsover_mul(__tsover_mul(k,D),O))),P=__tsover_add(P,__tsover_mul(V,__tsover_mul(__tsover_mul(F,D),O))),C=__tsover_add(C,__tsover_mul(V,__tsover_mul(__tsover_mul(k,q),O))),E=__tsover_add(E,__tsover_mul(V,__tsover_mul(__tsover_mul(k,D),Y)))}return S(P,C,E,x)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"g\",[6,\"d.f32\",[\"DGRID\"]]],[13,\"c\",[6,\"d.vec3f\",[[1,[1,[1,[1,[1,[7,\"pos\",\"x\"],\"+\",[7,\"prm\",\"gridOffX\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]],[1,[1,[1,[1,[1,[7,\"pos\",\"y\"],\"+\",[7,\"prm\",\"gridOffY\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]],[1,[1,[1,[1,[1,[7,\"pos\",\"z\"],\"+\",[7,\"prm\",\"gridOffZ\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]]]]],[13,\"base\",[6,\"std.clamp\",[[6,\"std.floor\",[\"c\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[1,\"g\",\"-\",[5,\"2\"]],[1,\"g\",\"-\",[5,\"2\"]],[1,\"g\",\"-\",[5,\"2\"]]]]]]],[13,\"f\",[6,\"std.clamp\",[[6,[7,\"c\",\"sub\"],[\"base\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[13,\"bi\",[6,\"d.i32\",[[7,\"base\",\"x\"]]]],[13,\"bj\",[6,\"d.i32\",[[7,\"base\",\"y\"]]]],[13,\"bk\",[6,\"d.i32\",[[7,\"base\",\"z\"]]]],[12,\"acc\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"gx\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"gy\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"gz\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"dz\",[5,\"0\"]],[1,\"dz\",\"<\",[5,\"2\"]],[102,\"++\",\"dz\"],[0,[[14,[12,\"dy\",[5,\"0\"]],[1,\"dy\",\"<\",[5,\"2\"]],[102,\"++\",\"dy\"],[0,[[14,[12,\"dx\",[5,\"0\"]],[1,\"dx\",\"<\",[5,\"2\"]],[102,\"++\",\"dx\"],[0,[[13,\"wx\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"x\"]],[7,\"f\",\"x\"],[6,\"d.f32\",[\"dx\"]]]]],[13,\"wy\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"y\"]],[7,\"f\",\"y\"],[6,\"d.f32\",[\"dy\"]]]]],[13,\"wz\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"z\"]],[7,\"f\",\"z\"],[6,\"d.f32\",[\"dz\"]]]]],[13,\"sx\",[1,[1,[6,\"d.f32\",[\"dx\"]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"sy\",[1,[1,[6,\"d.f32\",[\"dy\"]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"sz\",[1,[1,[6,\"d.f32\",[\"dz\"]],\"*\",[5,\"2\"]],\"-\",[5,\"1\"]]],[13,\"cv\",[6,\"densAt\",[[1,\"bi\",\"+\",\"dx\"],[1,\"bj\",\"+\",\"dy\"],[1,\"bk\",\"+\",\"dz\"]]]],[2,\"acc\",\"=\",[1,\"acc\",\"+\",[1,\"cv\",\"*\",[1,[1,\"wx\",\"*\",\"wy\"],\"*\",\"wz\"]]]],[2,\"gx\",\"=\",[1,\"gx\",\"+\",[1,\"cv\",\"*\",[1,[1,\"sx\",\"*\",\"wy\"],\"*\",\"wz\"]]]],[2,\"gy\",\"=\",[1,\"gy\",\"+\",[1,\"cv\",\"*\",[1,[1,\"wx\",\"*\",\"sy\"],\"*\",\"wz\"]]]],[2,\"gz\",\"=\",[1,\"gz\",\"+\",[1,\"cv\",\"*\",[1,[1,\"wx\",\"*\",\"wy\"],\"*\",\"sz\"]]]]]]]]]]]]],[10,[6,\"d.vec4f\",[\"gx\",\"gy\",\"gz\",\"acc\"]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,DGRID:()=>s,\"d.vec3f\":()=>A,DOMAIN:()=>r,\"std.clamp\":()=>G,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"std.mix\":()=>K,densAt:()=>o,\"d.vec4f\":()=>S}})&&c.f)({})),i=w.fn([M])((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";xa(e.$.dens[d],M(0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[6,\"std.atomicStore\",[[8,[7,[7,\"layout\",\"$\"],\"dens\"],\"i\"],[6,\"d.u32\",[[5,\"0\"]]]]]]]},externals:{\"std.atomicStore\":()=>xa,layout:()=>e,\"d.u32\":()=>M}})&&c.f)({})).$name(t.names.clear),l=w.fn([M])((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";let u=e.$.params,p=e.$.pos[d],_=n(s),f=A(__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(p.x,u.gridOffX),r),__tsover_mul(2,r)),_),.5),__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(p.y,u.gridOffY),r),__tsover_mul(2,r)),_),.5),__tsover_sub(__tsover_mul(__tsover_div(__tsover_add(__tsover_add(p.z,u.gridOffZ),r),__tsover_mul(2,r)),_),.5)),h=G(ie(f),A(0,0,0),A(__tsover_sub(_,2),__tsover_sub(_,2),__tsover_sub(_,2))),g=G(f.sub(h),A(0,0,0),A(1,1,1)),y=j(h.x),b=j(h.y),x=j(h.z),P=j(s);for(let C=0;C<2;C++)for(let E=0;E<2;E++)for(let R=0;R<2;R++){let U=K(__tsover_sub(1,g.x),g.x,n(R)),L=K(__tsover_sub(1,g.y),g.y,n(E)),k=K(__tsover_sub(1,g.z),g.z,n(C)),D=__tsover_mul(__tsover_mul(U,L),k),O=M(__tsover_add(__tsover_mul(__tsover_add(__tsover_mul(__tsover_add(x,C),P),__tsover_add(b,E)),P),__tsover_add(y,R)));wa(e.$.dens[O],M(__tsover_add(__tsover_mul(D,64),.5)))}}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"pos\",[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"]],[13,\"g\",[6,\"d.f32\",[\"DGRID\"]]],[13,\"c\",[6,\"d.vec3f\",[[1,[1,[1,[1,[1,[7,\"pos\",\"x\"],\"+\",[7,\"prm\",\"gridOffX\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]],[1,[1,[1,[1,[1,[7,\"pos\",\"y\"],\"+\",[7,\"prm\",\"gridOffY\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]],[1,[1,[1,[1,[1,[7,\"pos\",\"z\"],\"+\",[7,\"prm\",\"gridOffZ\"]],\"+\",\"DOMAIN\"],\"/\",[1,[5,\"2\"],\"*\",\"DOMAIN\"]],\"*\",\"g\"],\"-\",[5,\"0.5\"]]]]],[13,\"base\",[6,\"std.clamp\",[[6,\"std.floor\",[\"c\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[1,\"g\",\"-\",[5,\"2\"]],[1,\"g\",\"-\",[5,\"2\"]],[1,\"g\",\"-\",[5,\"2\"]]]]]]],[13,\"f\",[6,\"std.clamp\",[[6,[7,\"c\",\"sub\"],[\"base\"]],[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]]]],[13,\"bi\",[6,\"d.i32\",[[7,\"base\",\"x\"]]]],[13,\"bj\",[6,\"d.i32\",[[7,\"base\",\"y\"]]]],[13,\"bk\",[6,\"d.i32\",[[7,\"base\",\"z\"]]]],[13,\"gi\",[6,\"d.i32\",[\"DGRID\"]]],[14,[12,\"dz\",[5,\"0\"]],[1,\"dz\",\"<\",[5,\"2\"]],[102,\"++\",\"dz\"],[0,[[14,[12,\"dy\",[5,\"0\"]],[1,\"dy\",\"<\",[5,\"2\"]],[102,\"++\",\"dy\"],[0,[[14,[12,\"dx\",[5,\"0\"]],[1,\"dx\",\"<\",[5,\"2\"]],[102,\"++\",\"dx\"],[0,[[13,\"wx\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"x\"]],[7,\"f\",\"x\"],[6,\"d.f32\",[\"dx\"]]]]],[13,\"wy\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"y\"]],[7,\"f\",\"y\"],[6,\"d.f32\",[\"dy\"]]]]],[13,\"wz\",[6,\"std.mix\",[[1,[5,\"1\"],\"-\",[7,\"f\",\"z\"]],[7,\"f\",\"z\"],[6,\"d.f32\",[\"dz\"]]]]],[13,\"w\",[1,[1,\"wx\",\"*\",\"wy\"],\"*\",\"wz\"]],[13,\"idx\",[6,\"d.u32\",[[1,[1,[1,[1,[1,\"bk\",\"+\",\"dz\"],\"*\",\"gi\"],\"+\",[1,\"bj\",\"+\",\"dy\"]],\"*\",\"gi\"],\"+\",[1,\"bi\",\"+\",\"dx\"]]]]],[6,\"std.atomicAdd\",[[8,[7,[7,\"layout\",\"$\"],\"dens\"],\"idx\"],[6,\"d.u32\",[[1,[1,\"w\",\"*\",[5,\"64\"]],\"+\",[5,\"0.5\"]]]]]]]]]]]]]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,DGRID:()=>s,\"d.vec3f\":()=>A,DOMAIN:()=>r,\"std.clamp\":()=>G,\"std.floor\":()=>ie,\"d.i32\":()=>j,\"std.mix\":()=>K,\"d.u32\":()=>M,\"std.atomicAdd\":()=>wa}})&&c.f)({})).$name(t.names.splat);return{force:w.fn([A,A,n],A)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=e.$.params,f=a(d),h=__tsover_div(__tsover_mul(_.spread,4.4),B(__tsover_add(__tsover_mul(f.w,2),6),6));return A(__tsover_mul(__tsover_mul(f.x,-1),h),__tsover_mul(__tsover_mul(f.y,-1),h),__tsover_mul(__tsover_mul(f.z,-1),h))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"_seed\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"gv\",[6,\"densTrilinearGrad\",[\"pos\"]]],[13,\"pressNorm\",[1,[1,[7,\"prm\",\"spread\"],\"*\",[5,\"4.4\"]],\"/\",[6,\"std.max\",[[1,[1,[7,\"gv\",\"w\"],\"*\",[5,\"2\"]],\"+\",[5,\"6\"]],[5,\"6\"]]]]],[10,[6,\"d.vec3f\",[[1,[1,[7,\"gv\",\"x\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"pressNorm\"],[1,[1,[7,\"gv\",\"y\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"pressNorm\"],[1,[1,[7,\"gv\",\"z\"],\"*\",[4,\"-\",[5,\"1\"]]],\"*\",\"pressNorm\"]]]]]]},externals:{layout:()=>e,densTrilinearGrad:()=>a,\"std.max\":()=>B,\"d.vec3f\":()=>A}})&&c.f)({})).$name(\"forcePressure\"),clearKernel:i,splatKernel:l,cells:s*s*s}}function Gce(e,t){let s=t.field,r=t.gradEps,{wall:o,recall:a,homing:i,entrainment:l}=t;return w.fn([A,A,n],A)((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=((d,u,p)=>{\"use gpu\";let _=e.$.params,f=s(d),h=n(r),g=A(__tsover_sub(s(A(__tsover_add(d.x,h),d.y,d.z)),s(A(__tsover_sub(d.x,h),d.y,d.z))),__tsover_sub(s(A(d.x,__tsover_add(d.y,h),d.z)),s(A(d.x,__tsover_sub(d.y,h),d.z))),__tsover_sub(s(A(d.x,d.y,__tsover_add(d.z,h))),s(A(d.x,d.y,__tsover_sub(d.z,h))))),y=g.div(B(he(g),1e-4)),b=A(0,0,0),x=B(f,0),P=__tsover_add(__tsover_mul(be(o.featherFrom,o.featherTo,f),o.base),__tsover_mul(x,o.springK));b=b.sub(y.mul(P));let C=he(d),E=__tsover_mul(be(a.from,a.to,C),a.k);b=b.sub(d.mul(__tsover_div(E,B(C,1e-4))));let R=__tsover_mul(be(i.from,i.to,f),i.k);b=b.sub(d.mul(__tsover_div(R,B(C,1e-4))));let U=u7(A(_.omegaX,_.omegaY,_.omegaZ),d),L=__tsover_sub(1,be(-l.featherHalf,l.featherHalf,f));return b=b.add(U.sub(u).mul(__tsover_mul(_.entrain,L))),b}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"vel\"},{type:\"i\",name:\"_seed\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"sd\",[6,\"field\",[\"pos\"]]],[13,\"e\",[6,\"d.f32\",[\"E\"]]],[13,\"gx\",[1,[6,\"field\",[[6,\"d.vec3f\",[[1,[7,\"pos\",\"x\"],\"+\",\"e\"],[7,\"pos\",\"y\"],[7,\"pos\",\"z\"]]]]],\"-\",[6,\"field\",[[6,\"d.vec3f\",[[1,[7,\"pos\",\"x\"],\"-\",\"e\"],[7,\"pos\",\"y\"],[7,\"pos\",\"z\"]]]]]]],[13,\"gy\",[1,[6,\"field\",[[6,\"d.vec3f\",[[7,\"pos\",\"x\"],[1,[7,\"pos\",\"y\"],\"+\",\"e\"],[7,\"pos\",\"z\"]]]]],\"-\",[6,\"field\",[[6,\"d.vec3f\",[[7,\"pos\",\"x\"],[1,[7,\"pos\",\"y\"],\"-\",\"e\"],[7,\"pos\",\"z\"]]]]]]],[13,\"gz\",[1,[6,\"field\",[[6,\"d.vec3f\",[[7,\"pos\",\"x\"],[7,\"pos\",\"y\"],[1,[7,\"pos\",\"z\"],\"+\",\"e\"]]]]],\"-\",[6,\"field\",[[6,\"d.vec3f\",[[7,\"pos\",\"x\"],[7,\"pos\",\"y\"],[1,[7,\"pos\",\"z\"],\"-\",\"e\"]]]]]]],[13,\"gv\",[6,\"d.vec3f\",[\"gx\",\"gy\",\"gz\"]]],[13,\"gn\",[6,[7,\"gv\",\"div\"],[[6,\"std.max\",[[6,\"std.length\",[\"gv\"]],[5,\"0.0001\"]]]]]],[12,\"F\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]]],[13,\"out\",[6,\"std.max\",[\"sd\",[5,\"0\"]]]],[13,\"wallF\",[1,[1,[6,\"std.smoothstep\",[\"wall.featherFrom\",\"wall.featherTo\",\"sd\"]],\"*\",\"wall.base\"],\"+\",[1,\"out\",\"*\",\"wall.springK\"]]],[2,\"F\",\"=\",[6,[7,\"F\",\"sub\"],[[6,[7,\"gn\",\"mul\"],[\"wallF\"]]]]],[13,\"rl\",[6,\"std.length\",[\"pos\"]]],[13,\"recallF\",[1,[6,\"std.smoothstep\",[\"recall.from\",\"recall.to\",\"rl\"]],\"*\",\"recall.k\"]],[2,\"F\",\"=\",[6,[7,\"F\",\"sub\"],[[6,[7,\"pos\",\"mul\"],[[1,\"recallF\",\"/\",[6,\"std.max\",[\"rl\",[5,\"0.0001\"]]]]]]]]],[13,\"homingF\",[1,[6,\"std.smoothstep\",[\"homing.from\",\"homing.to\",\"sd\"]],\"*\",\"homing.k\"]],[2,\"F\",\"=\",[6,[7,\"F\",\"sub\"],[[6,[7,\"pos\",\"mul\"],[[1,\"homingF\",\"/\",[6,\"std.max\",[\"rl\",[5,\"0.0001\"]]]]]]]]],[13,\"featureVel\",[6,\"std.cross\",[[6,\"d.vec3f\",[[7,\"prm\",\"omegaX\"],[7,\"prm\",\"omegaY\"],[7,\"prm\",\"omegaZ\"]]],\"pos\"]]],[13,\"insideM\",[1,[5,\"1\"],\"-\",[6,\"std.smoothstep\",[[4,\"-\",\"entrainment.featherHalf\"],\"entrainment.featherHalf\",\"sd\"]]]],[2,\"F\",\"=\",[6,[7,\"F\",\"add\"],[[6,[7,[6,[7,\"featureVel\",\"sub\"],[\"vel\"]],\"mul\"],[[1,[7,\"prm\",\"entrain\"],\"*\",\"insideM\"]]]]]],[10,\"F\"]]]},externals:{layout:()=>e,field:()=>s,\"d.f32\":()=>n,E:()=>r,\"d.vec3f\":()=>A,\"std.max\":()=>B,\"std.length\":()=>he,\"std.smoothstep\":()=>be,\"wall.featherFrom\":()=>o.featherFrom,\"wall.featherTo\":()=>o.featherTo,\"wall.base\":()=>o.base,\"wall.springK\":()=>o.springK,\"recall.from\":()=>a.from,\"recall.to\":()=>a.to,\"recall.k\":()=>a.k,\"homing.from\":()=>i.from,\"homing.to\":()=>i.to,\"homing.k\":()=>i.k,\"std.cross\":()=>u7,\"entrainment.featherHalf\":()=>l.featherHalf}})&&c.f)({})).$name(\"forceContainment\")}function Fce(e){return w.fn([A,A,n],A)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";let a=e.$.params,i=Xu(m(__tsover_sub(s.x,a.cursorX),__tsover_sub(s.y,a.cursorY)),a.cursorRadSq,a.cursorForce);return A(i.x,i.y,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"_seed\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"mxy\",[6,\"agents.cursorMagnet\",[[6,\"d.vec2f\",[[1,[7,\"pos\",\"x\"],\"-\",[7,\"prm\",\"cursorX\"]],[1,[7,\"pos\",\"y\"],\"-\",[7,\"prm\",\"cursorY\"]]]],[7,\"prm\",\"cursorRadSq\"],[7,\"prm\",\"cursorForce\"]]]],[10,[6,\"d.vec3f\",[[7,\"mxy\",\"x\"],[7,\"mxy\",\"y\"],[5,\"0\"]]]]]]},externals:{layout:()=>e,\"agents.cursorMagnet\":()=>Xu,\"d.vec2f\":()=>m,\"d.vec3f\":()=>A}})&&t.f)({})).$name(\"forceCursorXY\")}function Ice(e){return w.fn([A,A,n],A)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";let a=e.$.params;return A(a.gravX,a.gravY,0)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"_seed\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[10,[6,\"d.vec3f\",[[7,\"prm\",\"gravX\"],[7,\"prm\",\"gravY\"],[5,\"0\"]]]]]]},externals:{layout:()=>e,\"d.vec3f\":()=>A}})&&t.f)({})).$name(\"forceGravity\")}function Yce(e,t){let{posScale:s,timeX:r,timeY:o,seedScale:a,gain:i}=t;return w.fn([A,A,n],A)((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d,u)=>{\"use gpu\";let p=e.$.params;return ei(c.mul(s).add(A(__tsover_mul(p.time,r),__tsover_mul(p.time,o),__tsover_mul(u,a)))).sub(A(.5,.5,.5)).mul(__tsover_mul(p.agitation,i))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"seed\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"rnd\",[6,[7,[6,\"noise.hash33\",[[6,[7,[6,[7,\"pos\",\"mul\"],[\"posScale\"]],\"add\"],[[6,\"d.vec3f\",[[1,[7,\"prm\",\"time\"],\"*\",\"timeX\"],[1,[7,\"prm\",\"time\"],\"*\",\"timeY\"],[1,\"seed\",\"*\",\"seedScale\"]]]]]]],\"sub\"],[[6,\"d.vec3f\",[[5,\"0.5\"],[5,\"0.5\"],[5,\"0.5\"]]]]]],[10,[6,[7,\"rnd\",\"mul\"],[[1,[7,\"prm\",\"agitation\"],\"*\",\"gain\"]]]]]]},externals:{layout:()=>e,\"noise.hash33\":()=>ei,posScale:()=>s,\"d.vec3f\":()=>A,timeX:()=>r,timeY:()=>o,seedScale:()=>a,gain:()=>i}})&&l.f)({})).$name(\"forceTurbulence\")}function Wce(e){return e.reduce((t,s,r)=>w.fn([A,A,n],A)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i,l)=>{\"use gpu\";return t(a,i,l).add(s(a,i,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"vel\"},{type:\"i\",name:\"seed\"}],body:[0,[[10,[6,[7,[6,\"acc\",[\"pos\",\"vel\",\"seed\"]],\"add\"],[[6,\"part\",[\"pos\",\"vel\",\"seed\"]]]]]]]},externals:{acc:()=>t,part:()=>s}})&&o.f)({})).$name(`forceSum${r}`))}function qce(e,t){let s=Wce(t.forces),r=t.maxSpeed,o=t.posClamp;return w.fn([M])((a=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(a.f=(i=>{\"use gpu\";let l=e.$.params,c=e.$.pos[i],d=e.$.vel[i],u=A(c.x,c.y,c.z),p=A(d.x,d.y,d.z),_=l.dt,f=nh(p,s(u,p,c.w),_,l.dragMul,n(r));u=G(u.add(f.mul(_)),A(-o,-o,-o),A(o,o,o)),e.$.pos[i]=S(u.x,u.y,u.z,c.w),e.$.vel[i]=S(f.x,f.y,f.z,he(f))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"p4\",[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"]],[13,\"v4\",[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"]],[12,\"pos\",[6,\"d.vec3f\",[[7,\"p4\",\"x\"],[7,\"p4\",\"y\"],[7,\"p4\",\"z\"]]]],[13,\"vPrev\",[6,\"d.vec3f\",[[7,\"v4\",\"x\"],[7,\"v4\",\"y\"],[7,\"v4\",\"z\"]]]],[13,\"dt\",[7,\"prm\",\"dt\"]],[13,\"F\",[6,\"total\",[\"pos\",\"vPrev\",[7,\"p4\",\"w\"]]]],[13,\"v3\",[6,\"integrateSemiImplicitEuler\",[\"vPrev\",\"F\",\"dt\",[7,\"prm\",\"dragMul\"],[6,\"d.f32\",[\"MAX_SPEED\"]]]]],[2,\"pos\",\"=\",[6,\"std.clamp\",[[6,[7,\"pos\",\"add\"],[[6,[7,\"v3\",\"mul\"],[\"dt\"]]]],[6,\"d.vec3f\",[[4,\"-\",\"C\"],[4,\"-\",\"C\"],[4,\"-\",\"C\"]]],[6,\"d.vec3f\",[\"C\",\"C\",\"C\"]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"pos\",\"x\"],[7,\"pos\",\"y\"],[7,\"pos\",\"z\"],[7,\"p4\",\"w\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"v3\",\"x\"],[7,\"v3\",\"y\"],[7,\"v3\",\"z\"],[6,\"std.length\",[\"v3\"]]]]]]]},externals:{layout:()=>e,\"d.vec3f\":()=>A,total:()=>s,integrateSemiImplicitEuler:()=>nh,\"d.f32\":()=>n,MAX_SPEED:()=>r,\"std.clamp\":()=>G,C:()=>o,\"d.vec4f\":()=>S,\"std.length\":()=>he}})&&a.f)({})).$name(t.name)}function Vce(e,t){let s=t.maxAgents;return w.fn([m,m,n],m)((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a,i)=>{\"use gpu\";let l=e.$.params,c=m(0,0),d=m(0,0),u=m(0,0),p=n(0),_=n(0);for(let h=0;h<s&&!(n(h)>=l.count);h++){let g=e.$.agents[M(h)],y=m(g.x,g.y),b=o.sub(y),x=Se(b,b);x>1e-7&&x<l.perceptionSq&&(d=d.add(m(g.z,g.w)),u=u.add(y),p=__tsover_add(p,1),x<l.sepRadiusSq&&(c=c.add(b.div(x)),_=__tsover_add(_,1)))}let f=m(0,0);if(p>.5){let h=v1(d.div(p),a,i,l.maxForce),g=v1(u.div(p).sub(o),a,i,l.maxForce);f=f.add(h.mul(l.aliW)).add(g.mul(l.cohW))}return _>.5&&(f=f.add(v1(c,a,i,l.maxForce).mul(l.sepW))),f}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"vel\"},{type:\"i\",name:\"maxSpeedI\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[12,\"sep\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"ali\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"coh\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[12,\"nCount\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"sCount\",[6,\"d.f32\",[[5,\"0\"]]]],[14,[12,\"j\",[5,\"0\"]],[1,\"j\",\"<\",\"MAX_AGENTS\"],[102,\"++\",\"j\"],[0,[[11,[1,[6,\"d.f32\",[\"j\"]],\">=\",[7,\"prm\",\"count\"]],[0,[[17]]]],[13,\"other\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],[6,\"d.u32\",[\"j\"]]]],[13,\"opos\",[6,\"d.vec2f\",[[7,\"other\",\"x\"],[7,\"other\",\"y\"]]]],[13,\"off\",[6,[7,\"pos\",\"sub\"],[\"opos\"]]],[13,\"dsq\",[6,\"std.dot\",[\"off\",\"off\"]]],[11,[3,[1,\"dsq\",\">\",[5,\"1e-7\"]],\"&&\",[1,\"dsq\",\"<\",[7,\"prm\",\"perceptionSq\"]]],[0,[[2,\"ali\",\"=\",[6,[7,\"ali\",\"add\"],[[6,\"d.vec2f\",[[7,\"other\",\"z\"],[7,\"other\",\"w\"]]]]]],[2,\"coh\",\"=\",[6,[7,\"coh\",\"add\"],[\"opos\"]]],[2,\"nCount\",\"=\",[1,\"nCount\",\"+\",[5,\"1\"]]],[11,[1,\"dsq\",\"<\",[7,\"prm\",\"sepRadiusSq\"]],[0,[[2,\"sep\",\"=\",[6,[7,\"sep\",\"add\"],[[6,[7,\"off\",\"div\"],[\"dsq\"]]]]],[2,\"sCount\",\"=\",[1,\"sCount\",\"+\",[5,\"1\"]]]]]]]]]]]],[12,\"accel\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[11,[1,\"nCount\",\">\",[5,\"0.5\"]],[0,[[13,\"aliSteer\",[6,\"agents.reynoldsSteer\",[[6,[7,\"ali\",\"div\"],[\"nCount\"]],\"vel\",\"maxSpeedI\",[7,\"prm\",\"maxForce\"]]]],[13,\"cohSteer\",[6,\"agents.reynoldsSteer\",[[6,[7,[6,[7,\"coh\",\"div\"],[\"nCount\"]],\"sub\"],[\"pos\"]],\"vel\",\"maxSpeedI\",[7,\"prm\",\"maxForce\"]]]],[2,\"accel\",\"=\",[6,[7,[6,[7,\"accel\",\"add\"],[[6,[7,\"aliSteer\",\"mul\"],[[7,\"prm\",\"aliW\"]]]]],\"add\"],[[6,[7,\"cohSteer\",\"mul\"],[[7,\"prm\",\"cohW\"]]]]]]]]],[11,[1,\"sCount\",\">\",[5,\"0.5\"]],[0,[[2,\"accel\",\"=\",[6,[7,\"accel\",\"add\"],[[6,[7,[6,\"agents.reynoldsSteer\",[\"sep\",\"vel\",\"maxSpeedI\",[7,\"prm\",\"maxForce\"]]],\"mul\"],[[7,\"prm\",\"sepW\"]]]]]]]]],[10,\"accel\"]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"d.f32\":()=>n,MAX_AGENTS:()=>s,\"d.u32\":()=>M,\"std.dot\":()=>Se,\"agents.reynoldsSteer\":()=>v1}})&&r.f)({})).$name(\"forceFlocking\")}function Nce(e){return w.fn([m,m,n],m)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";let a=e.$.params,i=m(0,0);if(a.cursorMode>.5){let l=m(a.cursorX,a.cursorY).sub(s),c=Se(l,l);if(c>1e-7&&c<a.cursorRadiusSq){let d=Ue(c),u=__tsover_sub(1,__tsover_div(d,B(a.cursorRadius,1e-5))),p=N(n(1),n(-1),a.cursorMode>1.5);i=l.div(d).mul(__tsover_mul(__tsover_mul(p,u),a.cursorForce))}}return i}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"_maxSpeedI\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[12,\"accel\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[11,[1,[7,\"prm\",\"cursorMode\"],\">\",[5,\"0.5\"]],[0,[[13,\"cd\",[6,[7,[6,\"d.vec2f\",[[7,\"prm\",\"cursorX\"],[7,\"prm\",\"cursorY\"]]],\"sub\"],[\"pos\"]]],[13,\"cdsq\",[6,\"std.dot\",[\"cd\",\"cd\"]]],[11,[3,[1,\"cdsq\",\">\",[5,\"1e-7\"]],\"&&\",[1,\"cdsq\",\"<\",[7,\"prm\",\"cursorRadiusSq\"]]],[0,[[13,\"cdist\",[6,\"std.sqrt\",[\"cdsq\"]]],[13,\"falloff\",[1,[5,\"1\"],\"-\",[1,\"cdist\",\"/\",[6,\"std.max\",[[7,\"prm\",\"cursorRadius\"],[5,\"0.00001\"]]]]]],[13,\"sign\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],[6,\"d.f32\",[[4,\"-\",[5,\"1\"]]]],[1,[7,\"prm\",\"cursorMode\"],\">\",[5,\"1.5\"]]]]],[2,\"accel\",\"=\",[6,[7,[6,[7,\"cd\",\"div\"],[\"cdist\"]],\"mul\"],[[1,[1,\"sign\",\"*\",\"falloff\"],\"*\",[7,\"prm\",\"cursorForce\"]]]]]]]]]]],[10,\"accel\"]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"std.dot\":()=>Se,\"std.sqrt\":()=>Ue,\"std.max\":()=>B,\"std.select\":()=>N,\"d.f32\":()=>n}})&&t.f)({})).$name(\"forceCursorSteer\")}function $ce(e){return w.fn([m,m,n],m)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";let a=e.$.params,i=a.margin,l=a.turnForce;return m(__tsover_add(N(n(0),l,s.x<i),N(n(0),-l,s.x>__tsover_sub(a.domainX,i))),__tsover_add(N(n(0),l,s.y<i),N(n(0),-l,s.y>__tsover_sub(1,i))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"_vel\"},{type:\"i\",name:\"_maxSpeedI\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"m\",[7,\"prm\",\"margin\"]],[13,\"tf\",[7,\"prm\",\"turnForce\"]],[13,\"ax\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"tf\",[1,[7,\"pos\",\"x\"],\"<\",\"m\"]]],\"+\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[4,\"-\",\"tf\"],[1,[7,\"pos\",\"x\"],\">\",[1,[7,\"prm\",\"domainX\"],\"-\",\"m\"]]]]]],[13,\"ay\",[1,[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"tf\",[1,[7,\"pos\",\"y\"],\"<\",\"m\"]]],\"+\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[4,\"-\",\"tf\"],[1,[7,\"pos\",\"y\"],\">\",[1,[5,\"1\"],\"-\",\"m\"]]]]]],[10,[6,\"d.vec2f\",[\"ax\",\"ay\"]]]]]},externals:{layout:()=>e,\"std.select\":()=>N,\"d.f32\":()=>n,\"d.vec2f\":()=>m}})&&t.f)({})).$name(\"forceWallTurn\")}function Xce(e){return e.reduce((t,s,r)=>w.fn([m,m,n],m)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i,l)=>{\"use gpu\";return t(a,i,l).add(s(a,i,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"},{type:\"i\",name:\"vel\"},{type:\"i\",name:\"maxSpeedI\"}],body:[0,[[10,[6,[7,[6,\"acc\",[\"pos\",\"vel\",\"maxSpeedI\"]],\"add\"],[[6,\"part\",[\"pos\",\"vel\",\"maxSpeedI\"]]]]]]]},externals:{acc:()=>t,part:()=>s}})&&o.f)({})).$name(`steerSum${r}`))}function Hce(e,t){let s=Xce(t.forces),{rest:r,gain:o,cool:a}=t.agitation,i=t.cruiseFloor,l=t.wallRestitution;return w.fn([M])((c=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(c.f=(d=>{\"use gpu\";let u=e.$.params,p=e.$.agents[d],_=m(p.x,p.y),f=m(p.z,p.w),h=u.dt,g=__tsover_mul(u.maxSpeed,wP(n(d))),y=s(_,f,g),b=__tsover_div(he(y),B(u.maxForce,1e-5)),x=G(__tsover_mul(__tsover_sub(b,r),o),0,1),P=__tsover_mul(e.$.agit[d],Tt(__tsover_mul(h,-a)));e.$.agit[d]=B(x,P),f=f.add(y.mul(h));let C=he(f),E=G(C,__tsover_mul(g,i),g);f=f.div(B(C,1e-5)).mul(E),_=_.add(f.mul(h));let R=_.x<0||_.x>u.domainX,U=_.y<0||_.y>1,L=N(f.x,__tsover_mul(f.x,-l),R),k=N(f.y,__tsover_mul(f.y,-l),U),D=G(_.x,0,u.domainX),O=G(_.y,0,1);e.$.agents[d]=S(D,O,L,k)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"self\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"]],[12,\"pos\",[6,\"d.vec2f\",[[7,\"self\",\"x\"],[7,\"self\",\"y\"]]]],[12,\"vel\",[6,\"d.vec2f\",[[7,\"self\",\"z\"],[7,\"self\",\"w\"]]]],[13,\"dt\",[7,\"prm\",\"dt\"]],[13,\"maxSpeedI\",[1,[7,\"prm\",\"maxSpeed\"],\"*\",[6,\"agents.cruiseVariation\",[[6,\"d.f32\",[\"i\"]]]]]],[13,\"accel\",[6,\"total\",[\"pos\",\"vel\",\"maxSpeedI\"]]],[13,\"effort\",[1,[6,\"std.length\",[\"accel\"]],\"/\",[6,\"std.max\",[[7,\"prm\",\"maxForce\"],[5,\"0.00001\"]]]]],[13,\"raw\",[6,\"std.clamp\",[[1,[1,\"effort\",\"-\",\"AGIT_REST\"],\"*\",\"AGIT_GAIN\"],[5,\"0\"],[5,\"1\"]]]],[13,\"cooled\",[1,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"*\",[6,\"std.exp\",[[1,\"dt\",\"*\",[4,\"-\",\"AGIT_COOL\"]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"=\",[6,\"std.max\",[\"raw\",\"cooled\"]]],[2,\"vel\",\"=\",[6,[7,\"vel\",\"add\"],[[6,[7,\"accel\",\"mul\"],[\"dt\"]]]]],[13,\"spd\",[6,\"std.length\",[\"vel\"]]],[13,\"clamped\",[6,\"std.clamp\",[\"spd\",[1,\"maxSpeedI\",\"*\",\"FLOOR\"],\"maxSpeedI\"]]],[2,\"vel\",\"=\",[6,[7,[6,[7,\"vel\",\"div\"],[[6,\"std.max\",[\"spd\",[5,\"0.00001\"]]]]],\"mul\"],[\"clamped\"]]],[2,\"pos\",\"=\",[6,[7,\"pos\",\"add\"],[[6,[7,\"vel\",\"mul\"],[\"dt\"]]]]],[13,\"hitX\",[3,[1,[7,\"pos\",\"x\"],\"<\",[5,\"0\"]],\"||\",[1,[7,\"pos\",\"x\"],\">\",[7,\"prm\",\"domainX\"]]]],[13,\"hitY\",[3,[1,[7,\"pos\",\"y\"],\"<\",[5,\"0\"]],\"||\",[1,[7,\"pos\",\"y\"],\">\",[5,\"1\"]]]],[13,\"nvx\",[6,\"std.select\",[[7,\"vel\",\"x\"],[1,[7,\"vel\",\"x\"],\"*\",[4,\"-\",\"REST\"]],\"hitX\"]]],[13,\"nvy\",[6,\"std.select\",[[7,\"vel\",\"y\"],[1,[7,\"vel\",\"y\"],\"*\",[4,\"-\",\"REST\"]],\"hitY\"]]],[13,\"cx\",[6,\"std.clamp\",[[7,\"pos\",\"x\"],[5,\"0\"],[7,\"prm\",\"domainX\"]]]],[13,\"cy\",[6,\"std.clamp\",[[7,\"pos\",\"y\"],[5,\"0\"],[5,\"1\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[\"cx\",\"cy\",\"nvx\",\"nvy\"]]]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"agents.cruiseVariation\":()=>wP,\"d.f32\":()=>n,total:()=>s,\"std.length\":()=>he,\"std.max\":()=>B,\"std.clamp\":()=>G,AGIT_REST:()=>r,AGIT_GAIN:()=>o,\"std.exp\":()=>Tt,AGIT_COOL:()=>a,FLOOR:()=>i,\"std.select\":()=>N,REST:()=>l,\"d.vec4f\":()=>S}})&&c.f)({})).$name(t.name)}var Tu=gt({rhat:m,strength:n,phi:n}).$name(\"AgentFieldSample\");function jce(e){return w.fn([m],Tu)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=(s=>{\"use gpu\";let r=e.$.params,o=m(r.cursorX,r.cursorY),a=s.sub(o),i=B(he(a),1e-4),l=a.div(i),c=TP(l,m(r.axisX,r.axisY),r.fieldType);return Tu({rhat:l,strength:__tsover_mul(r.strength,Tt(__tsover_mul(__tsover_div(__tsover_mul(i,i),B(r.reachSq,1e-6)),-1))),phi:Xt(c.y,c.x)})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"cursor\",[6,\"d.vec2f\",[[7,\"prm\",\"cursorX\"],[7,\"prm\",\"cursorY\"]]]],[13,\"delta\",[6,[7,\"pos\",\"sub\"],[\"cursor\"]]],[13,\"r\",[6,\"std.max\",[[6,\"std.length\",[\"delta\"]],[5,\"0.0001\"]]]],[13,\"rhat\",[6,[7,\"delta\",\"div\"],[\"r\"]]],[13,\"axis\",[6,\"d.vec2f\",[[7,\"prm\",\"axisX\"],[7,\"prm\",\"axisY\"]]]],[13,\"B\",[6,\"agents.dipoleOrRadialField\",[\"rhat\",\"axis\",[7,\"prm\",\"fieldType\"]]]],[13,\"fieldStr\",[1,[7,\"prm\",\"strength\"],\"*\",[6,\"std.exp\",[[1,[1,[1,\"r\",\"*\",\"r\"],\"/\",[6,\"std.max\",[[7,\"prm\",\"reachSq\"],[5,\"0.000001\"]]]],\"*\",[4,\"-\",[5,\"1\"]]]]]]],[10,[6,\"FieldSample\",[[104,{rhat:\"rhat\",strength:\"fieldStr\",phi:[6,\"std.atan2\",[[7,\"B\",\"y\"],[7,\"B\",\"x\"]]]}]]]]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"std.length\":()=>he,\"agents.dipoleOrRadialField\":()=>TP,\"std.exp\":()=>Tt,FieldSample:()=>Tu,\"std.atan2\":()=>Xt}})&&t.f)({})).$name(\"agentFieldDipoleOrRadial\")}var yC=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return N(N(__tsover_mul(Xs(__tsover_add(__tsover_mul(t,2.3987),.517)),3.141592653589793),n(0),s>.5),n(1.5707963267948966),s>1.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"},{type:\"i\",name:\"restMode\"}],body:[0,[[13,\"randAng\",[1,[6,\"noise.hash11\",[[1,[1,\"fi\",\"*\",[5,\"2.3987\"]],\"+\",[5,\"0.517\"]]]],\"*\",[5,\"3.141592653589793\"]]],[13,\"a\",[6,\"std.select\",[\"randAng\",[6,\"d.f32\",[[5,\"0\"]]],[1,\"restMode\",\">\",[5,\"0.5\"]]]]],[10,[6,\"std.select\",[\"a\",[6,\"d.f32\",[[5,\"1.5707963267948966\"]]],[1,\"restMode\",\">\",[5,\"1.5\"]]]]]]]},externals:{\"noise.hash11\":()=>Xs,\"std.select\":()=>N,\"d.f32\":()=>n}})&&e.f)({})).$name(\"agentRestAngle\");function Kce(e){return w.fn([Tu,n,n],n)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";return Jv(__tsover_mul(e.$.params.alignK,s.strength),s.phi,r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fld\"},{type:\"i\",name:\"theta\"},{type:\"i\",name:\"_fi\"}],body:[0,[[10,[6,\"agents.nematicTorque\",[[1,[7,[7,[7,\"layout\",\"$\"],\"params\"],\"alignK\"],\"*\",[7,\"fld\",\"strength\"]],[7,\"fld\",\"phi\"],\"theta\"]]]]]},externals:{\"agents.nematicTorque\":()=>Jv,layout:()=>e}})&&t.f)({})).$name(\"torqueAlignToField\")}function Qce(e){return w.fn([Tu,n,n],n)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=((s,r,o)=>{\"use gpu\";let a=e.$.params;return Jv(a.restK,yC(o,a.restMode),r)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_fld\"},{type:\"i\",name:\"theta\"},{type:\"i\",name:\"fi\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[10,[6,\"agents.nematicTorque\",[[7,\"prm\",\"restK\"],[6,\"restOrientationAngle\",[\"fi\",[7,\"prm\",\"restMode\"]]],\"theta\"]]]]]},externals:{layout:()=>e,\"agents.nematicTorque\":()=>Jv,restOrientationAngle:()=>yC}})&&t.f)({})).$name(\"torqueRest\")}function Zce(e){return e.reduce((t,s,r)=>w.fn([Tu,n,n],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=((a,i,l)=>{\"use gpu\";return __tsover_add(t(a,i,l),s(a,i,l))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fld\"},{type:\"i\",name:\"theta\"},{type:\"i\",name:\"fi\"}],body:[0,[[10,[1,[6,\"acc\",[\"fld\",\"theta\",\"fi\"]],\"+\",[6,\"part\",[\"fld\",\"theta\",\"fi\"]]]]]]},externals:{acc:()=>t,part:()=>s}})&&o.f)({})).$name(`torqueSum${r}`))}function Jce(e,t){let s=t.overscan;return w.fn([n],m)((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=(o=>{\"use gpu\";let a=e.$.params,i=a.gridCols,l=ie(__tsover_div(o,B(i,1))),c=__tsover_sub(o,__tsover_mul(l,B(i,1))),d=Pr(m(__tsover_add(__tsover_mul(o,.7548),.11),__tsover_add(__tsover_mul(o,1.3236),5.31))),u=__tsover_mul(__tsover_sub(d.x,.5),a.jitter),p=__tsover_mul(__tsover_sub(d.y,.5),a.jitter);return m(__tsover_sub(__tsover_mul(__tsover_add(__tsover_add(c,.5),u),a.cellW),s),__tsover_sub(__tsover_mul(__tsover_add(__tsover_add(l,.5),p),a.cellH),s))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"cols\",[7,\"prm\",\"gridCols\"]],[13,\"row\",[6,\"std.floor\",[[1,\"fi\",\"/\",[6,\"std.max\",[\"cols\",[5,\"1\"]]]]]]],[13,\"col\",[1,\"fi\",\"-\",[1,\"row\",\"*\",[6,\"std.max\",[\"cols\",[5,\"1\"]]]]]],[13,\"h\",[6,\"noise.hash22\",[[6,\"d.vec2f\",[[1,[1,\"fi\",\"*\",[5,\"0.7548\"]],\"+\",[5,\"0.11\"]],[1,[1,\"fi\",\"*\",[5,\"1.3236\"]],\"+\",[5,\"5.31\"]]]]]]],[13,\"jx\",[1,[1,[7,\"h\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",[7,\"prm\",\"jitter\"]]],[13,\"jy\",[1,[1,[7,\"h\",\"y\"],\"-\",[5,\"0.5\"]],\"*\",[7,\"prm\",\"jitter\"]]],[10,[6,\"d.vec2f\",[[1,[1,[1,[1,\"col\",\"+\",[5,\"0.5\"]],\"+\",\"jx\"],\"*\",[7,\"prm\",\"cellW\"]],\"-\",\"OVERSCAN\"],[1,[1,[1,[1,\"row\",\"+\",[5,\"0.5\"]],\"+\",\"jy\"],\"*\",[7,\"prm\",\"cellH\"]],\"-\",\"OVERSCAN\"]]]]]]},externals:{layout:()=>e,\"std.floor\":()=>ie,\"std.max\":()=>B,\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m,OVERSCAN:()=>s}})&&r.f)({})).$name(\"agentJitteredHome\")}function ede(e,t){let{home:s,fieldAt:r}=t,o=Zce(t.torques),a=t.maxOffset;return w.fn([M])((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=(l=>{\"use gpu\";let c=e.$.params,d=e.$.agents[l],u=m(d.x,d.y),p=d.z,_=d.w,f=c.dt,h=n(l),g=s(h),y=r(m(__tsover_add(g.x,u.x),__tsover_add(g.y,u.y)));_=__tsover_add(_,__tsover_mul(o(y,p,h),f)),_=__tsover_mul(_,Tt(__tsover_mul(__tsover_mul(c.damping,f),-1))),p=__tsover_add(p,__tsover_mul(_,f));let b=G(__tsover_div(re(_),B(c.omegaRef,1e-5)),0,1),x=__tsover_mul(e.$.agit[l],Tt(__tsover_mul(f,-c.agitCool)));e.$.agit[l]=B(b,x);let P=y.rhat.mul(__tsover_mul(__tsover_mul(c.pullK,y.strength),-1));u=u.add(P.mul(f)),u=u.mul(Tt(__tsover_mul(__tsover_mul(c.homeK,f),-1)));let C=he(u);u=N(u,u.div(B(C,1e-5)).mul(a),C>a),e.$.agents[l]=S(u.x,u.y,p,_)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"st\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"]],[12,\"off\",[6,\"d.vec2f\",[[7,\"st\",\"x\"],[7,\"st\",\"y\"]]]],[12,\"theta\",[7,\"st\",\"z\"]],[12,\"omega\",[7,\"st\",\"w\"]],[13,\"dt\",[7,\"prm\",\"dt\"]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"homeP\",[6,\"home\",[\"fi\"]]],[13,\"pos\",[6,\"d.vec2f\",[[1,[7,\"homeP\",\"x\"],\"+\",[7,\"off\",\"x\"]],[1,[7,\"homeP\",\"y\"],\"+\",[7,\"off\",\"y\"]]]]],[13,\"fld\",[6,\"fieldAt\",[\"pos\"]]],[2,\"omega\",\"=\",[1,\"omega\",\"+\",[1,[6,\"total\",[\"fld\",\"theta\",\"fi\"]],\"*\",\"dt\"]]],[2,\"omega\",\"=\",[1,\"omega\",\"*\",[6,\"std.exp\",[[1,[1,[7,\"prm\",\"damping\"],\"*\",\"dt\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]],[2,\"theta\",\"=\",[1,\"theta\",\"+\",[1,\"omega\",\"*\",\"dt\"]]],[13,\"raw\",[6,\"std.clamp\",[[1,[6,\"std.abs\",[\"omega\"]],\"/\",[6,\"std.max\",[[7,\"prm\",\"omegaRef\"],[5,\"0.00001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"cooled\",[1,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"*\",[6,\"std.exp\",[[1,\"dt\",\"*\",[4,\"-\",[7,\"prm\",\"agitCool\"]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agit\"],\"i\"],\"=\",[6,\"std.max\",[\"raw\",\"cooled\"]]],[13,\"pull\",[6,[7,[7,\"fld\",\"rhat\"],\"mul\"],[[1,[1,[7,\"prm\",\"pullK\"],\"*\",[7,\"fld\",\"strength\"]],\"*\",[4,\"-\",[5,\"1\"]]]]]],[2,\"off\",\"=\",[6,[7,\"off\",\"add\"],[[6,[7,\"pull\",\"mul\"],[\"dt\"]]]]],[2,\"off\",\"=\",[6,[7,\"off\",\"mul\"],[[6,\"std.exp\",[[1,[1,[7,\"prm\",\"homeK\"],\"*\",\"dt\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]]],[13,\"offLen\",[6,\"std.length\",[\"off\"]]],[2,\"off\",\"=\",[6,\"std.select\",[\"off\",[6,[7,[6,[7,\"off\",\"div\"],[[6,\"std.max\",[\"offLen\",[5,\"0.00001\"]]]]],\"mul\"],[\"MAX_OFFSET\"]],[1,\"offLen\",\">\",\"MAX_OFFSET\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"off\",\"x\"],[7,\"off\",\"y\"],\"theta\",\"omega\"]]]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"d.f32\":()=>n,home:()=>s,fieldAt:()=>r,total:()=>o,\"std.exp\":()=>Tt,\"std.clamp\":()=>G,\"std.abs\":()=>re,\"std.max\":()=>B,\"std.length\":()=>he,\"std.select\":()=>N,MAX_OFFSET:()=>a,\"d.vec4f\":()=>S}})&&i.f)({})).$name(t.name)}function tde(e){return w.fn([n],m)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=(s=>{\"use gpu\";let r=e.$.params,o=Pr(m(__tsover_add(__tsover_mul(s,.7548),.13),__tsover_add(__tsover_mul(s,1.3236),5.71))),a=__tsover_add(1,__tsover_mul(__tsover_sub(o.x,.5),r.speedVar)),i=__tsover_add(r.angleRad,__tsover_mul(__tsover_mul(__tsover_sub(o.y,.5),2),r.angleVarRad));return m(pe(i),ae(i)).mul(__tsover_mul(r.driftBase,a))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"fi\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"h\",[6,\"noise.hash22\",[[6,\"d.vec2f\",[[1,[1,\"fi\",\"*\",[5,\"0.7548\"]],\"+\",[5,\"0.13\"]],[1,[1,\"fi\",\"*\",[5,\"1.3236\"]],\"+\",[5,\"5.71\"]]]]]]],[13,\"speedMul\",[1,[5,\"1\"],\"+\",[1,[1,[7,\"h\",\"x\"],\"-\",[5,\"0.5\"]],\"*\",[7,\"prm\",\"speedVar\"]]]],[13,\"ang\",[1,[7,\"prm\",\"angleRad\"],\"+\",[1,[1,[1,[7,\"h\",\"y\"],\"-\",[5,\"0.5\"]],\"*\",[5,\"2\"]],\"*\",[7,\"prm\",\"angleVarRad\"]]]],[10,[6,[7,[6,\"d.vec2f\",[[6,\"std.cos\",[\"ang\"]],[6,\"std.sin\",[\"ang\"]]]],\"mul\"],[[1,[7,\"prm\",\"driftBase\"],\"*\",\"speedMul\"]]]]]]},externals:{layout:()=>e,\"noise.hash22\":()=>Pr,\"d.vec2f\":()=>m,\"std.cos\":()=>pe,\"std.sin\":()=>ae}})&&t.f)({})).$name(\"driftVariedHeading\")}function sde(e){return w.fn([m],m)((t=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(t.f=(s=>{\"use gpu\";let r=e.$.params,o=m(0,0);return r.cursorForce>1e-5&&(o=Xu(s.sub(m(r.cursorX,r.cursorY)),r.cursorRadSq,r.cursorForce).mul(r.dt)),o}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"pos\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[12,\"impulse\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]]],[11,[1,[7,\"prm\",\"cursorForce\"],\">\",[5,\"0.00001\"]],[0,[[13,\"delta\",[6,[7,\"pos\",\"sub\"],[[6,\"d.vec2f\",[[7,\"prm\",\"cursorX\"],[7,\"prm\",\"cursorY\"]]]]]],[2,\"impulse\",\"=\",[6,[7,[6,\"agents.cursorMagnet\",[\"delta\",[7,\"prm\",\"cursorRadSq\"],[7,\"prm\",\"cursorForce\"]]],\"mul\"],[[7,\"prm\",\"dt\"]]]]]]],[10,\"impulse\"]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"agents.cursorMagnet\":()=>Xu}})&&t.f)({})).$name(\"forceCursorGust\")}function rde(e,t){let{drift:s,gust:r}=t;return w.fn([M])((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=(a=>{\"use gpu\";let i=e.$.params,l=e.$.agents[a],c=n(a),d=i.dt,u=m(l.x,l.y),p=m(l.z,l.w);p=p.add(r(u)),p=p.mul(i.dragMul),u=u.add(s(c).add(p).mul(d));let _=__tsover_sub(u.x,__tsover_mul(ie(__tsover_div(u.x,B(i.domainX,1e-4))),i.domainX)),f=__tsover_sub(u.y,ie(u.y));e.$.agents[a]=S(_,f,p.x,p.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"s\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"dt\",[7,\"prm\",\"dt\"]],[12,\"pos\",[6,\"d.vec2f\",[[7,\"s\",\"x\"],[7,\"s\",\"y\"]]]],[12,\"gustV\",[6,\"d.vec2f\",[[7,\"s\",\"z\"],[7,\"s\",\"w\"]]]],[2,\"gustV\",\"=\",[6,[7,\"gustV\",\"add\"],[[6,\"gust\",[\"pos\"]]]]],[2,\"gustV\",\"=\",[6,[7,\"gustV\",\"mul\"],[[7,\"prm\",\"dragMul\"]]]],[2,\"pos\",\"=\",[6,[7,\"pos\",\"add\"],[[6,[7,[6,[7,[6,\"drift\",[\"fi\"]],\"add\"],[\"gustV\"]],\"mul\"],[\"dt\"]]]]],[13,\"wx\",[1,[7,\"pos\",\"x\"],\"-\",[1,[6,\"std.floor\",[[1,[7,\"pos\",\"x\"],\"/\",[6,\"std.max\",[[7,\"prm\",\"domainX\"],[5,\"0.0001\"]]]]]],\"*\",[7,\"prm\",\"domainX\"]]]],[13,\"wy\",[1,[7,\"pos\",\"y\"],\"-\",[6,\"std.floor\",[[7,\"pos\",\"y\"]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[\"wx\",\"wy\",[7,\"gustV\",\"x\"],[7,\"gustV\",\"y\"]]]]]]},externals:{layout:()=>e,\"d.f32\":()=>n,\"d.vec2f\":()=>m,gust:()=>r,drift:()=>s,\"std.floor\":()=>ie,\"std.max\":()=>B,\"d.vec4f\":()=>S}})&&o.f)({})).$name(t.name)}var bC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return Se(t,A(.299,.587,.114))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[10,[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),xC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return __tsover_sub(1,Se(t,A(.299,.587,.114)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[10,[1,[5,\"1\"],\"-\",[6,\"std.dot\",[\"rgb\",[6,\"d.vec3f\",[[5,\"0.299\"],[5,\"0.587\"],[5,\"0.114\"]]]]]]]]]},externals:{\"std.dot\":()=>Se,\"d.vec3f\":()=>A}})&&e.f)({})),AC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t.x}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[10,[7,\"rgb\",\"x\"]]]]},externals:{}})&&e.f)({})),wC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t.y}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[10,[7,\"rgb\",\"y\"]]]]},externals:{}})&&e.f)({})),TC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return t.z}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[10,[7,\"rgb\",\"z\"]]]]},externals:{}})&&e.f)({})),SC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=B(B(t.x,t.y),t.z),o=ye(ye(t.x,t.y),t.z);return __tsover_sub(r,o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"rgb\"},{type:\"i\",name:\"_a\"}],body:[0,[[13,\"mx\",[6,\"std.max\",[[6,\"std.max\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"]]],[7,\"rgb\",\"z\"]]]],[13,\"mn\",[6,\"std.min\",[[6,\"std.min\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"]]],[7,\"rgb\",\"z\"]]]],[10,[1,\"mx\",\"-\",\"mn\"]]]]},externals:{\"std.max\":()=>B,\"std.min\":()=>ye}})&&e.f)({})),PC=w.fn([A,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return s}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"_rgb\"},{type:\"i\",name:\"a\"}],body:[0,[[10,\"a\"]]]},externals:{}})&&e.f)({})),x2={luminance:bC,luminanceInverted:xC,red:AC,green:wC,blue:TC,saturation:SC,alpha:PC,byMode:{0:bC,1:xC,2:AC,3:wC,4:TC,5:SC,6:PC}};function ode(e,t){let s=t.perspK;return w.fn([m,m,n],A)((r=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(r.f=((o,a,i)=>{\"use gpu\";let l=e.$.params,c=eh(A(__tsover_add(o.x,a.x),__tsover_add(o.y,a.y),i),l.rowX,l.rowY,l.rowZ),d=__tsover_div(l.zoom,G(__tsover_sub(1,__tsover_mul(c.z,s)),.35,2.6)),u=__tsover_add(__tsover_add(.5,__tsover_div(__tsover_mul(c.x,d),l.aspect)),l.transX),p=__tsover_add(__tsover_add(.5,__tsover_mul(c.y,d)),l.transY),_=m(__tsover_mul(__tsover_sub(u,l.pointerX),l.aspect),__tsover_sub(p,l.pointerY)),f=Xu(_,l.cursorRadSq,l.cursorForce),h=__tsover_mul(sh(_,l.cursorRadSq),l.cursorZForce);return PP(A(f.x,f.y,h),l.rowX,l.rowY,l.rowZ)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"homeW\"},{type:\"i\",name:\"off\"},{type:\"i\",name:\"z\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"pw\",[6,\"d.vec3f\",[[1,[7,\"homeW\",\"x\"],\"+\",[7,\"off\",\"x\"]],[1,[7,\"homeW\",\"y\"],\"+\",[7,\"off\",\"y\"]],\"z\"]]],[13,\"rv\",[6,\"agents.applyCameraRows\",[\"pw\",[7,\"P\",\"rowX\"],[7,\"P\",\"rowY\"],[7,\"P\",\"rowZ\"]]]],[13,\"persp\",[1,[7,\"P\",\"zoom\"],\"/\",[6,\"std.clamp\",[[1,[5,\"1\"],\"-\",[1,[7,\"rv\",\"z\"],\"*\",\"PERSP_K\"]],[5,\"0.35\"],[5,\"2.6\"]]]]],[13,\"su\",[1,[1,[5,\"0.5\"],\"+\",[1,[1,[7,\"rv\",\"x\"],\"*\",\"persp\"],\"/\",[7,\"P\",\"aspect\"]]],\"+\",[7,\"P\",\"transX\"]]],[13,\"sv\",[1,[1,[5,\"0.5\"],\"+\",[1,[7,\"rv\",\"y\"],\"*\",\"persp\"]],\"+\",[7,\"P\",\"transY\"]]],[13,\"deltaS\",[6,\"d.vec2f\",[[1,[1,\"su\",\"-\",[7,\"P\",\"pointerX\"]],\"*\",[7,\"P\",\"aspect\"]],[1,\"sv\",\"-\",[7,\"P\",\"pointerY\"]]]]],[13,\"mxy\",[6,\"agents.cursorMagnet\",[\"deltaS\",[7,\"P\",\"cursorRadSq\"],[7,\"P\",\"cursorForce\"]]]],[13,\"mz\",[1,[6,\"agents.cursorFalloff\",[\"deltaS\",[7,\"P\",\"cursorRadSq\"]]],\"*\",[7,\"P\",\"cursorZForce\"]]],[10,[6,\"agents.applyCameraRowsTransposed\",[[6,\"d.vec3f\",[[7,\"mxy\",\"x\"],[7,\"mxy\",\"y\"],\"mz\"]],[7,\"P\",\"rowX\"],[7,\"P\",\"rowY\"],[7,\"P\",\"rowZ\"]]]]]]},externals:{layout:()=>e,\"d.vec3f\":()=>A,\"agents.applyCameraRows\":()=>eh,\"std.clamp\":()=>G,PERSP_K:()=>s,\"d.vec2f\":()=>m,\"agents.cursorMagnet\":()=>Xu,\"agents.cursorFalloff\":()=>sh,\"agents.applyCameraRowsTransposed\":()=>PP}})&&r.f)({})).$name(\"forceCursorInView\")}function ade(e,t){let{channel:s,cursor:r}=t,o=t.stiffness,a=t.maxSpeed,i=t.minAlpha;return w.fn([M,M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=((c,d)=>{\"use gpu\";let u=e.$.params,p=__tsover_add(__tsover_mul(d,M(u.gridW)),c),_=__tsover_div(__tsover_add(n(c),.5),u.gridW),f=__tsover_div(__tsover_add(n(d),.5),u.gridH),h=Ti(e.$.src),g=th(m(_,f),m(n(h.x),n(h.y))),y=Ye(e.$.src,fe(g.x0,g.y0),0),b=Ye(e.$.src,fe(g.x1,g.y0),0),x=Ye(e.$.src,fe(g.x0,g.y1),0),P=Ye(e.$.src,fe(g.x1,g.y1),0),C=K(K(y,b,g.fx),K(x,P,g.fx),g.fy),E=C.w,R=C.xyz.div(B(E,n(1e-4)));e.$.col[p]=S(R.x,R.y,R.z,E);let U=s(R,E),L=__tsover_mul(__tsover_sub(U,.5),u.depth),k=e.$.pos[p],D=e.$.vel[p],O=u.dt,F=u.snap>.5||D.w<.5,q=m(__tsover_mul(__tsover_sub(_,.5),u.aspect),__tsover_sub(f,.5)),Y=m(k.x,k.y),V=r(q,Y,k.z),X=A(__tsover_mul(Y.x,-o),__tsover_mul(Y.y,-o),__tsover_mul(__tsover_sub(L,k.z),o)).add(V),Q=nh(A(D.x,D.y,D.z),X,O,u.dragMul,n(a)),te=Y.add(m(Q.x,Q.y).mul(O)),J=__tsover_add(k.z,__tsover_mul(Q.z,O));te=N(te,m(0,0),F),J=N(J,L,F),Q=N(Q,A(0,0,0),F);let oe=Ys(m(__tsover_add(n(c),.5),__tsover_add(n(d),.5))),le=N(D.w,n(1),E>i);e.$.pos[p]=S(te.x,te.y,J,oe),e.$.vel[p]=S(Q.x,Q.y,Q.z,le)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"gx\"},{type:\"i\",name:\"gy\"}],body:[0,[[13,\"P\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"i\",[1,[1,\"gy\",\"*\",[6,\"d.u32\",[[7,\"P\",\"gridW\"]]]],\"+\",\"gx\"]],[13,\"homeU\",[1,[1,[6,\"d.f32\",[\"gx\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"P\",\"gridW\"]]],[13,\"homeV\",[1,[1,[6,\"d.f32\",[\"gy\"]],\"+\",[5,\"0.5\"]],\"/\",[7,\"P\",\"gridH\"]]],[13,\"dims\",[6,\"std.textureDimensions\",[[7,[7,\"layout\",\"$\"],\"src\"]]]],[13,\"bf\",[6,\"agents.bilinearTexelFrame\",[[6,\"d.vec2f\",[\"homeU\",\"homeV\"]],[6,\"d.vec2f\",[[6,\"d.f32\",[[7,\"dims\",\"x\"]]],[6,\"d.f32\",[[7,\"dims\",\"y\"]]]]]]]],[13,\"c00\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[[7,\"bf\",\"x0\"],[7,\"bf\",\"y0\"]]],[5,\"0\"]]]],[13,\"c10\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[[7,\"bf\",\"x1\"],[7,\"bf\",\"y0\"]]],[5,\"0\"]]]],[13,\"c01\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[[7,\"bf\",\"x0\"],[7,\"bf\",\"y1\"]]],[5,\"0\"]]]],[13,\"c11\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"src\"],[6,\"d.vec2u\",[[7,\"bf\",\"x1\"],[7,\"bf\",\"y1\"]]],[5,\"0\"]]]],[13,\"prem\",[6,\"std.mix\",[[6,\"std.mix\",[\"c00\",\"c10\",[7,\"bf\",\"fx\"]]],[6,\"std.mix\",[\"c01\",\"c11\",[7,\"bf\",\"fx\"]]],[7,\"bf\",\"fy\"]]]],[13,\"a\",[7,\"prem\",\"w\"]],[13,\"rgb\",[6,[7,[7,\"prem\",\"xyz\"],\"div\"],[[6,\"std.max\",[\"a\",[6,\"d.f32\",[[5,\"0.0001\"]]]]]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"col\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"a\"]]],[13,\"ch\",[6,\"channel\",[\"rgb\",\"a\"]]],[13,\"targetZ\",[1,[1,\"ch\",\"-\",[5,\"0.5\"]],\"*\",[7,\"P\",\"depth\"]]],[13,\"p4\",[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"]],[13,\"v4\",[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"]],[13,\"dt\",[7,\"P\",\"dt\"]],[13,\"needsSnap\",[3,[1,[7,\"P\",\"snap\"],\">\",[5,\"0.5\"]],\"||\",[1,[7,\"v4\",\"w\"],\"<\",[5,\"0.5\"]]]],[13,\"homeW\",[6,\"d.vec2f\",[[1,[1,\"homeU\",\"-\",[5,\"0.5\"]],\"*\",[7,\"P\",\"aspect\"]],[1,\"homeV\",\"-\",[5,\"0.5\"]]]]],[13,\"off\",[6,\"d.vec2f\",[[7,\"p4\",\"x\"],[7,\"p4\",\"y\"]]]],[13,\"cf\",[6,\"cursor\",[\"homeW\",\"off\",[7,\"p4\",\"z\"]]]],[13,\"F\",[6,[7,[6,\"d.vec3f\",[[1,[7,\"off\",\"x\"],\"*\",[4,\"-\",\"STIFF\"]],[1,[7,\"off\",\"y\"],\"*\",[4,\"-\",\"STIFF\"]],[1,[1,\"targetZ\",\"-\",[7,\"p4\",\"z\"]],\"*\",\"STIFF\"]]],\"add\"],[\"cf\"]]],[12,\"v3\",[6,\"integrateSemiImplicitEuler\",[[6,\"d.vec3f\",[[7,\"v4\",\"x\"],[7,\"v4\",\"y\"],[7,\"v4\",\"z\"]]],\"F\",\"dt\",[7,\"P\",\"dragMul\"],[6,\"d.f32\",[\"MAX_SPEED\"]]]]],[12,\"offN\",[6,[7,\"off\",\"add\"],[[6,[7,[6,\"d.vec2f\",[[7,\"v3\",\"x\"],[7,\"v3\",\"y\"]]],\"mul\"],[\"dt\"]]]]],[12,\"curZ\",[1,[7,\"p4\",\"z\"],\"+\",[1,[7,\"v3\",\"z\"],\"*\",\"dt\"]]],[2,\"offN\",\"=\",[6,\"std.select\",[\"offN\",[6,\"d.vec2f\",[[5,\"0\"],[5,\"0\"]]],\"needsSnap\"]]],[2,\"curZ\",\"=\",[6,\"std.select\",[\"curZ\",\"targetZ\",\"needsSnap\"]]],[2,\"v3\",\"=\",[6,\"std.select\",[\"v3\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"]]],\"needsSnap\"]]],[13,\"seed\",[6,\"noise.hash12\",[[6,\"d.vec2f\",[[1,[6,\"d.f32\",[\"gx\"]],\"+\",[5,\"0.5\"]],[1,[6,\"d.f32\",[\"gy\"]],\"+\",[5,\"0.5\"]]]]]]],[13,\"seenN\",[6,\"std.select\",[[7,\"v4\",\"w\"],[6,\"d.f32\",[[5,\"1\"]]],[1,\"a\",\">\",\"MIN_ALPHA\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"pos\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"offN\",\"x\"],[7,\"offN\",\"y\"],\"curZ\",\"seed\"]]],[2,[8,[7,[7,\"layout\",\"$\"],\"vel\"],\"i\"],\"=\",[6,\"d.vec4f\",[[7,\"v3\",\"x\"],[7,\"v3\",\"y\"],[7,\"v3\",\"z\"],\"seenN\"]]]]]},externals:{layout:()=>e,\"d.u32\":()=>M,\"d.f32\":()=>n,\"std.textureDimensions\":()=>Ti,\"agents.bilinearTexelFrame\":()=>th,\"d.vec2f\":()=>m,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,\"std.max\":()=>B,\"d.vec4f\":()=>S,channel:()=>s,cursor:()=>r,\"d.vec3f\":()=>A,STIFF:()=>o,integrateSemiImplicitEuler:()=>nh,MAX_SPEED:()=>a,\"std.select\":()=>N,\"noise.hash12\":()=>Ys,MIN_ALPHA:()=>i}})&&l.f)({})).$name(t.name)}function nde(e,t){let s=t.gridN,r=t.inertiaRate,o=t.homeRate,a=kP[0],i=kP[1];return w.fn([M])((l=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(l.f=(c=>{\"use gpu\";let d=e.$.params,u=e.$.agents[c],p=m(u.x,u.y),_=d.dt,f=B(d.aspect,1e-5),h=n(s),g=th(m(__tsover_div(p.x,f),p.y),m(h,h)),y=Ye(e.$.velTex,fe(g.x0,g.y0),0),b=Ye(e.$.velTex,fe(g.x1,g.y0),0),x=Ye(e.$.velTex,fe(g.x0,g.y1),0),P=Ye(e.$.velTex,fe(g.x1,g.y1),0),C=K(K(y.x,b.x,g.fx),K(x.x,P.x,g.fx),g.fy),E=K(K(y.y,b.y,g.fx),K(x.y,P.y,g.fx),g.fy),R=__tsover_mul(__tsover_mul(__tsover_div(C,h),f),d.advect),U=__tsover_mul(__tsover_div(E,h),d.advect),L=__tsover_sub(1,Tt(__tsover_mul(__tsover_mul(_,r),-1))),k=n(c),D=__tsover_mul(Re(__tsover_add(.5,__tsover_mul(k,a))),f),O=Re(__tsover_add(.5,__tsover_mul(k,i))),F=__tsover_add(K(u.z,R,L),__tsover_mul(__tsover_sub(D,p.x),__tsover_mul(o,_))),q=__tsover_add(K(u.w,U,L),__tsover_mul(__tsover_sub(O,p.y),__tsover_mul(o,_))),Y=G(__tsover_add(p.x,__tsover_mul(F,_)),0,f),V=G(__tsover_add(p.y,__tsover_mul(q,_)),0,1);e.$.agents[c]=S(Y,V,F,q)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"i\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"a\",[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"]],[13,\"pos\",[6,\"d.vec2f\",[[7,\"a\",\"x\"],[7,\"a\",\"y\"]]]],[13,\"dt\",[7,\"prm\",\"dt\"]],[13,\"aspect\",[6,\"std.max\",[[7,\"prm\",\"aspect\"],[5,\"0.00001\"]]]],[13,\"NF\",[6,\"d.f32\",[\"N\"]]],[13,\"bf\",[6,\"agents.bilinearTexelFrame\",[[6,\"d.vec2f\",[[1,[7,\"pos\",\"x\"],\"/\",\"aspect\"],[7,\"pos\",\"y\"]]],[6,\"d.vec2f\",[\"NF\",\"NF\"]]]]],[13,\"v00\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"velTex\"],[6,\"d.vec2u\",[[7,\"bf\",\"x0\"],[7,\"bf\",\"y0\"]]],[5,\"0\"]]]],[13,\"v10\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"velTex\"],[6,\"d.vec2u\",[[7,\"bf\",\"x1\"],[7,\"bf\",\"y0\"]]],[5,\"0\"]]]],[13,\"v01\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"velTex\"],[6,\"d.vec2u\",[[7,\"bf\",\"x0\"],[7,\"bf\",\"y1\"]]],[5,\"0\"]]]],[13,\"v11\",[6,\"std.textureLoad\",[[7,[7,\"layout\",\"$\"],\"velTex\"],[6,\"d.vec2u\",[[7,\"bf\",\"x1\"],[7,\"bf\",\"y1\"]]],[5,\"0\"]]]],[13,\"gvx\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"x\"],[7,\"v10\",\"x\"],[7,\"bf\",\"fx\"]]],[6,\"std.mix\",[[7,\"v01\",\"x\"],[7,\"v11\",\"x\"],[7,\"bf\",\"fx\"]]],[7,\"bf\",\"fy\"]]]],[13,\"gvy\",[6,\"std.mix\",[[6,\"std.mix\",[[7,\"v00\",\"y\"],[7,\"v10\",\"y\"],[7,\"bf\",\"fx\"]]],[6,\"std.mix\",[[7,\"v01\",\"y\"],[7,\"v11\",\"y\"],[7,\"bf\",\"fx\"]]],[7,\"bf\",\"fy\"]]]],[13,\"fieldVx\",[1,[1,[1,\"gvx\",\"/\",\"NF\"],\"*\",\"aspect\"],\"*\",[7,\"prm\",\"advect\"]]],[13,\"fieldVy\",[1,[1,\"gvy\",\"/\",\"NF\"],\"*\",[7,\"prm\",\"advect\"]]],[13,\"blend\",[1,[5,\"1\"],\"-\",[6,\"std.exp\",[[1,[1,\"dt\",\"*\",\"INERTIA_RATE\"],\"*\",[4,\"-\",[5,\"1\"]]]]]]],[13,\"fi\",[6,\"d.f32\",[\"i\"]]],[13,\"homeX\",[1,[6,\"std.fract\",[[1,[5,\"0.5\"],\"+\",[1,\"fi\",\"*\",\"R2A\"]]]],\"*\",\"aspect\"]],[13,\"homeY\",[6,\"std.fract\",[[1,[5,\"0.5\"],\"+\",[1,\"fi\",\"*\",\"R2B\"]]]]],[13,\"velX\",[1,[6,\"std.mix\",[[7,\"a\",\"z\"],\"fieldVx\",\"blend\"]],\"+\",[1,[1,\"homeX\",\"-\",[7,\"pos\",\"x\"]],\"*\",[1,\"HOME_RATE\",\"*\",\"dt\"]]]],[13,\"velY\",[1,[6,\"std.mix\",[[7,\"a\",\"w\"],\"fieldVy\",\"blend\"]],\"+\",[1,[1,\"homeY\",\"-\",[7,\"pos\",\"y\"]],\"*\",[1,\"HOME_RATE\",\"*\",\"dt\"]]]],[13,\"nposX\",[6,\"std.clamp\",[[1,[7,\"pos\",\"x\"],\"+\",[1,\"velX\",\"*\",\"dt\"]],[5,\"0\"],\"aspect\"]]],[13,\"nposY\",[6,\"std.clamp\",[[1,[7,\"pos\",\"y\"],\"+\",[1,\"velY\",\"*\",\"dt\"]],[5,\"0\"],[5,\"1\"]]]],[2,[8,[7,[7,\"layout\",\"$\"],\"agents\"],\"i\"],\"=\",[6,\"d.vec4f\",[\"nposX\",\"nposY\",\"velX\",\"velY\"]]]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,\"std.max\":()=>B,\"d.f32\":()=>n,N:()=>s,\"agents.bilinearTexelFrame\":()=>th,\"std.textureLoad\":()=>Ye,\"d.vec2u\":()=>fe,\"std.mix\":()=>K,\"std.exp\":()=>Tt,INERTIA_RATE:()=>r,\"std.fract\":()=>Re,R2A:()=>a,R2B:()=>i,HOME_RATE:()=>o,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&l.f)({})).$name(t.name)}var Za={pressure:Oce,containment:Gce,cursorXY:Fce,gravity:Ice,turbulence:Yce,flocking:Vce,cursorSteer:Nce,wallTurn:$ce,cursorGust:sde,cursorInView:ode},A2={alignToField:Kce,rest:Qce},w2={dipoleOrRadial:jce,jitteredGridHome:Jce},eO={variedHeading:tde},On={forces3d:qce,steering2d:Hce,orientation2d:ede,drift2d:rde,relief:ade,advect2d:nde};var _h=w.fn([n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=B(t,0),o=B(s,0);return __tsover_add(ye(B(t,s),0),Ue(__tsover_add(__tsover_mul(r,r),__tsover_mul(o,o))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"d2\"},{type:\"i\",name:\"wz\"}],body:[0,[[13,\"ox\",[6,\"std.max\",[\"d2\",[5,\"0\"]]]],[13,\"oz\",[6,\"std.max\",[\"wz\",[5,\"0\"]]]],[10,[1,[6,\"std.min\",[[6,\"std.max\",[\"d2\",\"wz\"]],[5,\"0\"]]],\"+\",[6,\"std.sqrt\",[[1,[1,\"ox\",\"*\",\"ox\"],\"+\",[1,\"oz\",\"*\",\"oz\"]]]]]]]]},externals:{\"std.max\":()=>B,\"std.min\":()=>ye,\"std.sqrt\":()=>Ue}})&&e.f)({})).$name(\"shapeFieldExtrudeZ\");function ide(e,t){return w.fn([A],n)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=e.$.params,a=t(m(__tsover_add(r.x,.5),__tsover_sub(.5,r.y)),o.saRadius,o.saSides,o.saRounding,o.saInnerRatio,o.saRotation,o.saHeight,o.saOffset,o.saAperture).x;return _h(a,__tsover_sub(re(r.z),o.halfDepth))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p3\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"uv2\",[6,\"d.vec2f\",[[1,[7,\"p3\",\"x\"],\"+\",[5,\"0.5\"]],[1,[5,\"0.5\"],\"-\",[7,\"p3\",\"y\"]]]]],[13,\"d2\",[7,[6,\"sdf2d\",[\"uv2\",[7,\"prm\",\"saRadius\"],[7,\"prm\",\"saSides\"],[7,\"prm\",\"saRounding\"],[7,\"prm\",\"saInnerRatio\"],[7,\"prm\",\"saRotation\"],[7,\"prm\",\"saHeight\"],[7,\"prm\",\"saOffset\"],[7,\"prm\",\"saAperture\"]]],\"x\"]],[10,[6,\"extrudeZ\",[\"d2\",[1,[6,\"std.abs\",[[7,\"p3\",\"z\"]]],\"-\",[7,\"prm\",\"halfDepth\"]]]]]]]},externals:{layout:()=>e,\"d.vec2f\":()=>m,sdf2d:()=>t,extrudeZ:()=>_h,\"std.abs\":()=>re}})&&s.f)({})).$name(\"shapeFieldAnalyticExtruded\")}function lde(e,t,s){let r=s.size;return w.fn([A],n)((o=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(o.f=(a=>{\"use gpu\";let i=e.$.params,l=__tsover_sub(r,1),c=G(j(__tsover_mul(__tsover_add(a.x,.5),r)),0,l),d=G(j(__tsover_mul(__tsover_sub(.5,a.y),r)),0,l),u=Ye(t.$.sdfSource,fe(M(c),M(d)),0).x,p=B(__tsover_sub(re(a.x),.5),0),_=B(__tsover_sub(re(a.y),.5),0);return _h(__tsover_add(u,Ue(__tsover_add(__tsover_mul(p,p),__tsover_mul(_,_)))),__tsover_sub(re(a.z),i.halfDepth))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p3\"}],body:[0,[[13,\"prm\",[7,[7,\"layout\",\"$\"],\"params\"]],[13,\"sizeM1\",[1,\"SIZE\",\"-\",[5,\"1\"]]],[13,\"tx\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,[1,[7,\"p3\",\"x\"],\"+\",[5,\"0.5\"]],\"*\",\"SIZE\"]]],[5,\"0\"],\"sizeM1\"]]],[13,\"ty\",[6,\"std.clamp\",[[6,\"d.i32\",[[1,[1,[5,\"0.5\"],\"-\",[7,\"p3\",\"y\"]],\"*\",\"SIZE\"]]],[5,\"0\"],\"sizeM1\"]]],[13,\"d2\",[7,[6,\"std.textureLoad\",[[7,[7,\"svgLayout\",\"$\"],\"sdfSource\"],[6,\"d.vec2u\",[[6,\"d.u32\",[\"tx\"]],[6,\"d.u32\",[\"ty\"]]]],[5,\"0\"]]],\"x\"]],[13,\"exX\",[6,\"std.max\",[[1,[6,\"std.abs\",[[7,\"p3\",\"x\"]]],\"-\",[5,\"0.5\"]],[5,\"0\"]]]],[13,\"exY\",[6,\"std.max\",[[1,[6,\"std.abs\",[[7,\"p3\",\"y\"]]],\"-\",[5,\"0.5\"]],[5,\"0\"]]]],[13,\"d2e\",[1,\"d2\",\"+\",[6,\"std.sqrt\",[[1,[1,\"exX\",\"*\",\"exX\"],\"+\",[1,\"exY\",\"*\",\"exY\"]]]]]],[10,[6,\"extrudeZ\",[\"d2e\",[1,[6,\"std.abs\",[[7,\"p3\",\"z\"]]],\"-\",[7,\"prm\",\"halfDepth\"]]]]]]]},externals:{layout:()=>e,SIZE:()=>r,\"std.clamp\":()=>G,\"d.i32\":()=>j,\"std.textureLoad\":()=>Ye,svgLayout:()=>t,\"d.vec2u\":()=>fe,\"d.u32\":()=>M,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.sqrt\":()=>Ue,extrudeZ:()=>_h}})&&o.f)({})).$name(\"shapeFieldSvgExtruded\")}function cde(e,t){return w.fn([A],n)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";return t(ja(r,e.$.params.rot))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p3\"}],body:[0,[[10,[6,\"baked\",[[6,\"sdf3d.rotateVec3\",[\"p3\",[7,[7,[7,\"layout\",\"$\"],\"params\"],\"rot\"]]]]]]]]},externals:{baked:()=>t,\"sdf3d.rotateVec3\":()=>ja,layout:()=>e}})&&s.f)({})).$name(\"shapeFieldAnalytic3d\")}function dde(e,t){return w.fn([A],n)((s=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(s.f=(r=>{\"use gpu\";let o=ja(r,e.$.params.rot),a=B(__tsover_sub(re(o.x),.5),0),i=B(__tsover_sub(re(o.y),.5),0);return __tsover_add(t(o),Ue(__tsover_add(__tsover_mul(a,a),__tsover_mul(i,i))))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"p3\"}],body:[0,[[13,\"ps\",[6,\"sdf3d.rotateVec3\",[\"p3\",[7,[7,[7,\"layout\",\"$\"],\"params\"],\"rot\"]]]],[13,\"exX\",[6,\"std.max\",[[1,[6,\"std.abs\",[[7,\"ps\",\"x\"]]],\"-\",[5,\"0.5\"]],[5,\"0\"]]]],[13,\"exY\",[6,\"std.max\",[[1,[6,\"std.abs\",[[7,\"ps\",\"y\"]]],\"-\",[5,\"0.5\"]],[5,\"0\"]]]],[10,[1,[6,\"baked\",[\"ps\"]],\"+\",[6,\"std.sqrt\",[[1,[1,\"exX\",\"*\",\"exX\"],\"+\",[1,\"exY\",\"*\",\"exY\"]]]]]]]]},externals:{\"sdf3d.rotateVec3\":()=>ja,layout:()=>e,\"std.max\":()=>B,\"std.abs\":()=>re,baked:()=>t,\"std.sqrt\":()=>Ue}})&&s.f)({})).$name(\"shapeFieldSvgLifted3d\")}var X_={analyticExtruded:ide,svgExtruded:lde,analytic3d:cde,svgLifted3d:dde};function Sp(e,t){return Math.exp(-e*t)}function tO(e){let t=(e.pointerX-e.centerX)*e.aspect,s=e.pointerY-e.centerYv;return{x:(t*e.rotC+s*e.rotS)/e.scale,y:-((s*e.rotC-t*e.rotS)/e.scale)}}function T2(e,t,s){if(!e)return null;let r=(t.x-e.x)/s,o=(t.y-e.y)/s,a=(t.z-e.z)/s,i={cx:Math.cos(t.x),sx:Math.sin(t.x),cy:Math.cos(t.y),sy:Math.sin(t.y),cz:Math.cos(t.z),sz:Math.sin(t.z)},l=Jl(1,0,0,i),c=Jl(0,1,0,i),d=Jl(0,0,1,i);return{x:-(l.x*r+l.y*o+l.z*a),y:-(c.x*r+c.y*o+c.z*a),z:-(d.x*r+d.y*o+d.z*a)}}function sO(e,t){let s=Math.hypot(e.x,e.y,e.z),r=s>t.cap?t.cap/s:1;return{omegaX:e.x*r,omegaY:e.y*r,omegaZ:e.z*r,entrain:Math.min(1,s*t.gainRate)*t.gainMax}}function rO(e){let t=e?.smoothing??.1,s=e?.teleport??.25,r=.5,o=.5,a=1,i=0;return{update(l,c){let d=l-r,u=c-o,p=Math.hypot(d,u);if(p>.001&&p<s){let _=d/p,f=u/p;a+=(_-a)*t,i+=(f-i)*t;let h=Math.hypot(a,i)||1;a/=h,i/=h}return r=l,o=c,{x:a,y:i}}}}function oO(e,t,s){let r=t+2*s,o=1+2*s,a=Math.max(1,Math.round(Math.sqrt(e*(r/o)))),i=Math.max(1,Math.ceil(e/a));return{cols:a,cellW:r/a,cellH:o/i}}function aO(e,t,s,r){let o=Math.round(Math.sqrt(e*t));o=Math.min(Math.max(o,s),r);let a=Math.round(e/o);return a=Math.min(Math.max(a,s),r),{w:o,h:a}}function nO(e,t,s){let r=Math.cos(e),o=Math.sin(e),a=Math.cos(t),i=Math.sin(t),l=Math.cos(s),c=Math.sin(s),d=l*a,u=l*i*o-c*r,p=l*i*r+c*o,_=c*a,f=c*i*o+l*r,h=c*i*r-l*o,g=-i,y=a*o,b=a*r;return{rowX:[d,-u,p],rowY:[-_,f,-h],rowZ:[g,-y,b]}}function iO(e,t){return{x:e*N0[0]%1*t,y:e*N0[1]%1*t,z:e*N0[2]%1*t}}var Gn={orientedWorld(e,t){let{ramp:s,names:r,extraOnSplat:o,...a}=t;return{splat:{kernel:w1.orientedWorld(e,{...a,name:r.splat}),threads:\"agents\",...o?{extra:!0}:{}},resolve:{kernel:T1.ramp(e,{invGain:1/255,...s,res:t.res,name:r.resolve}),threads:\"fixed\",size:[t.res,t.res]}}},pointWorld(e,t){let{names:s,...r}=t;return{splat:{kernel:w1.pointWorld(e,{...r,name:s.splat}),threads:\"agents\"},resolve:{kernel:T1.tint(e,{res:t.res,name:s.resolve}),threads:\"fixed\",size:[t.res,t.res]}}},volume(e,t){let{ramp:s,names:r,extraOnSplat:o,...a}=t;return{splat:{kernel:w1.volume(e,{...a,names:r}),threads:\"agents\",...o?{extra:!0}:{}},resolve:{kernel:T1.ramp(e,{invGain:1/256,...s,res:t.outRes,name:r.resolve}),threads:\"fixed\",size:[t.outRes,t.outRes]}}},relief(e,t){let{out:s,weightedColor:r,names:o,...a}=t;return{splat:{kernel:w1.relief(e,{...a,name:o.splat}),threads:\"grid\"},resolve:{kernel:T1.weightedColor(e,{fp:t.fp,alphaK:r.alphaK,name:o.resolve}),threads:\"fixed\",size:s}}}};function Fn(e){return{compute:t=>{if(e.childTexture&&!t.childNode||!t.gpu?.root)return null;let s=e.childTexture?t.convertToTexture(t.childNode):null,r=e.bake(t);if(!r)return null;let o=_ie(t,{layout:e.layout,params:e.params,maxAgents:e.maxAgents,output:r.outputSize?{...e.output,size:r.outputSize}:e.output,...e.countCap?{countCap:e.countCap}:{},...e.externalKeys?{externalKeys:e.externalKeys}:{},...r.extraBindGroup?{extraBindGroup:r.extraBindGroup}:{},pipelines:r.pipelines,program:r.program,...r.initStep?{initStep:r.initStep}:{}});if(!o)return null;r.bindNow&&o.bindExternal(r.bindNow);let a=e.frame(o,t,r.setup);return{outputs:s?{childTexture:s,...o.outputs}:o.outputs,...s&&e.childTexture?{bindInputs:i=>{let l=i(s.key);l&&o.bindExternal({[e.childTexture.externalKey]:l.texture})}}:{},getComputeNodes:a}},gpu:{fragment:t=>{let{ctx:s,childNode:r,computeOutputs:o}=t,a=o?.[e.fragment.output];if(!a)return e.fragment.fallback===\"child\"?r??xt:xt;let i=a.sample(s.uv,\"linearClamp\");return e.fragment.compose?e.fragment.compose(i,t):i}}}}function Lg(e){return{compute:t=>{let{childNode:s,gpu:r}=t;if(!s)return null;let o=r?.root;if(!o)return null;let a=e(t,o),i=o.createUniform(a.paramsSchema),l=hB(t,{size:a.size,format:a.format,extraSlots:a.extraSlots,textures:a.textures,speedProp:a.speedProp,maxDeltaTime:a.maxDeltaTime,bindGroups:(d,u,p)=>a.bindGroups({...d,paramsBuffer:i.buffer},u,p),staticGroups:a.staticGroups?d=>a.staticGroups({...d,paramsBuffer:i.buffer}):void 0});if(!l)return null;let c=D_(o,a.step,{n:a.size,bindGroup:void 0});return{outputs:{childTexture:l.childTexture,display:l.display,...a.outputs??{}},bindInputs:l.bindInputs,getComputeNodes:d=>l.tick(d,u=>a.skip?.(u)?null:(i.write(a.values(u)),[c.with(u.groups)]))}}}}var lO=e=>({stage:\"pre\",kernel:e}),cO=e=>({stage:\"post\",kernel:e}),ude=\"rgba16float\";function Ii(e){return{compute:t=>{let{gpu:s,getCpuValue:r,registerComputeTexture:o,onCleanup:a}=t,i=s?.root;if(!i)return null;let l=e(t,i),c=l.resolution,d=c*c,u=pr(i,S,d),p=pr(i,S,d),_=pr(i,S,d),f=pr(i,S,d),h=pr(i,n,d),g=pr(i,n,d),y=l.container?pr(i,S,d):void 0,b=i.createTexture({size:[c,c],format:ude}).$usage(\"storage\",\"sampled\");a(()=>b.destroy());let x=o(b),P=i.createUniform(l.paramsSchema),C={velA:u,velB:p,dyeA:_,dyeB:f,pressure:h,divergence:g,params:P.buffer};y&&(C.maskBuf=y);let E=i.createBindGroup(l.layout,C),R=i.createBindGroup(l.outputLayout,{dyeA:_,outTex:b}),U={params:t,root:i,n:c,buffers:{velA:u,velB:p,dyeA:_,dyeB:f,pressure:h,divergence:g,maskBuf:y},paramsBuffer:P.buffer,fluidBindGroup:E,pass:(le,se=E)=>D_(i,le,{n:c,bindGroup:se})},L=l.container?.setup(U);l.init?.setup?.(U);let k=l.inject??[];for(let le of k)le.setup?.(U);let D=(l.solve??[]).filter(le=>le.stage===\"pre\").map(le=>U.pass(le.kernel)),O=(l.solve??[]).filter(le=>le.stage===\"post\").map(le=>U.pass(le.kernel)),F=jA(i,l.solver.kernels,{n:c,bindGroup:E}),q=U.pass(l.output.kernel,R),Y=(le,se)=>{le.push(...D,...F.solveSteps({jacobiIters:se}),...O)},V=l.pointer?Sl(l.pointer):null,X=l.maxDeltaTime??.033,Q=Date.now(),te=0,J=(le,se)=>{let Te=r(le);return typeof Te==\"number\"?Te:se},oe=le=>P.write(le);return{outputs:{[l.output.key]:x,...L?.outputs??{}},getComputeNodes:le=>{let se=le,Te=L?.preFrame?L.preFrame(le):null,de=Date.now(),xe=Math.min(se.deltaTime??(de-Q)/1e3,X);if(Q=de,xe<.001)return Te;te+=xe;let Ae={dt:xe,now:de,frameParams:se,num:J,getCpuValue:r,writeParams:oe,ptr:V?V.update(se.pointer,xe):null,get elapsed(){return te},setElapsed(Ie){te=Ie}},De=Te?[...Te]:[],Ee=!1;l.init?.stale(Ae)&&(Ee=!0,l.init.run(Ae,{nodes:De,writeParams:Ie=>()=>P.write(Ie),solve:Y}));for(let Ie of k)Ie.tick?.(Ae);for(let Ie of k)if(Ie.skip?.(Ae))return Ee?De:null;let ze=l.values(Ae);(l.write??\"direct\")===\"thunk\"?De.push(()=>P.write(ze)):P.write(ze),L&&De.push(L.maskPass);for(let Ie of k)Ie.emit?.(Ae,De,ze);return Y(De,l.solver.jacobiIters),De.push(q),De}}}}}function Ug(e){let t=null;return{setup(s){t=\"kernel\"in e?s.pass(e.kernel):e.setup(s)},emit(s,r){t&&r.push(t)}}}function Og(e){let t=Sl(e.tracker),s=lA(),r=null,o=null,a=1,i=!1;return{setup(l){a=l.n,r=\"kernel\"in e.pass?l.pass(e.pass.kernel):e.pass.setup(l)},tick(l){o=t.update(l.frameParams.pointer,l.dt),e.onFrame?.(l,o),i=e.activeWhen(o,l),i&&s.markActive()},skip(l){let c=s.shouldSkip(e.fadeSeconds(l));return c||s.tickFrame(l.dt),c},emit(l,c,d){if(!i||!o||!r)return;let u=e.ribbon(l,o,d);cA(c,{fromX:o.prevX,fromY:o.prevY,dx:o.dx,dy:o.dy,dragDist:o.dragDist,stepSize:u.stepSize,maxSteps:u.maxSteps,scale:a,prepare:u.prepare,write:u.write,pass:r})},ptr:()=>o,active:()=>i}}function dO(e){let t=null,s=!1,r=-1;return{setup(o){t=o.pass(e.kernel)},stale(o){return!s||e.seed(o)!==r},run(o,a){s=!0,r=e.seed(o),a.nodes.push(a.writeParams(e.warm.values(0,o))),t&&a.nodes.push(t);let i=e.warm.startTime();for(let l=0;l<e.warm.steps;l++){let c=i;i+=e.warm.dt,a.nodes.push(a.writeParams(e.warm.values(c,o))),a.solve(a.nodes,e.warm.jacobiIters)}o.setElapsed(i)}}}function Gg(e){return{intensity:{default:e.intensity,description:\"Strength of the relief shading applied to the child content\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Intensity\",group:\"Texture\"}},scale:{default:e.scale,description:\"Scale of the pattern \\u2014 lower = larger features, higher = finer grain\",ui:{type:\"range\",min:.1,max:8,step:.05,label:\"Scale\",group:\"Texture\"}},contrast:{default:e.contrast,description:\"Contrast of the texture \\u2014 negative flattens it into a subtle overlay\",ui:{type:\"range\",min:-1,max:2,step:.05,label:\"Contrast\",group:\"Texture\"}},distortion:{default:e.distortion,description:\"Surface distortion \\u2014 warps the child along the texture like carved relief\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Distortion\",group:\"Texture\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Texture\"}}}}var pde=0,mde=e=>new fs(()=>e);function uO(e,t){let s=(o,a)=>{let i=e.uniforms[tg(t,o)];if(!i)throw new Error(`std: list '${t}' has no field '${o}'`);return new fs(l=>`${i._emit(l)}[${a._emit(l)}]`)},r=e.uniforms[sg(t)];if(!r)throw new Error(`std: '${t}' is not a list prop`);return{count:r,at:o=>({position:a=>s(a,o).member(\"xy\"),color:a=>s(a,o),number:a=>s(a,o).member(\"x\")})}}function S2(e,t,s={}){let r=pde++,o=s.zero===\"vec3f\"?\"vec3f(0.0)\":\"0.0\",a=s.hint??\"sum\";return new fs(i=>i.memo(`accumulate:${r}`,()=>{for(let _ of s.deps??[])_._emit(i);let l=i.freshLocal(a),c=i.freshLocal(\"item\"),d=e.count._emit(i),u=[],p=t(mde(c))._emit({external:(_,f)=>i.external(_,f),statement:_=>u.push(_),freshLocal:_=>i.freshLocal(_),memo:(_,f)=>i.memo(_,f)});return i.statement(`var ${l} = ${o};`),i.statement([`for (var ${c} = 0u; ${c} < u32(${d}); ${c}++) {`,...u.map(_=>`  ${_}`),`  ${l} += ${p};`,\"}\"].join(`\n`)),l}))}var _de=new Map;function P2(e){return _de.get(e)}var fde=ko({applyNeonEffect:()=>vde,neonComposite:()=>_O,neonFlicker:()=>k2,neonFlow:()=>B2,neonGlow:()=>E2,neonSmoothEPS:()=>mO}),pO=.017453292519943295,C2=.7,Pp=1/Math.sqrt(1+C2*C2),M2=C2*Pp,Ig=M2+1,Fg=1/Math.sqrt(Pp*Pp+Ig*Ig),mO=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(t,.006);return N(__tsover_add(.008,__tsover_mul(s,.03)),o,r>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"tubeThickness\"},{type:\"i\",name:\"cornerSmoothing\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"vol\",[6,\"std.max\",[\"tubeThickness\",[5,\"0.006\"]]]],[13,\"flat\",[1,[5,\"0.008\"],\"+\",[1,\"cornerSmoothing\",\"*\",[5,\"0.03\"]]]],[10,[6,\"std.select\",[\"flat\",\"vol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.max\":()=>B,\"std.select\":()=>N}})&&e.f)({})),E2=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=B(s,.001),a=B(t,0),i=__tsover_mul(Tt(__tsover_mul(__tsover_mul(a,-1),__tsover_div(12,o))),.7),l=__tsover_mul(Tt(__tsover_mul(__tsover_mul(a,-1),__tsover_div(4,o))),.35),c=__tsover_mul(__tsover_add(i,l),r),d=be(-.003,.003,t);return __tsover_mul(c,d)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdf\"},{type:\"i\",name:\"glowRadius\"},{type:\"i\",name:\"glowIntensity\"}],body:[0,[[13,\"glowR\",[6,\"std.max\",[\"glowRadius\",[5,\"0.001\"]]]],[13,\"sdfOutside\",[6,\"std.max\",[\"sdf\",[5,\"0\"]]]],[13,\"innerGlow\",[1,[6,\"std.exp\",[[1,[1,\"sdfOutside\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",[1,[5,\"12\"],\"/\",\"glowR\"]]]],\"*\",[5,\"0.7\"]]],[13,\"outerGlow\",[1,[6,\"std.exp\",[[1,[1,\"sdfOutside\",\"*\",[4,\"-\",[5,\"1\"]]],\"*\",[1,[5,\"4\"],\"/\",\"glowR\"]]]],\"*\",[5,\"0.35\"]]],[13,\"totalGlow\",[1,[1,\"innerGlow\",\"+\",\"outerGlow\"],\"*\",\"glowIntensity\"]],[13,\"outsideFactor\",[6,\"std.smoothstep\",[[4,\"-\",[5,\"0.003\"]],[5,\"0.003\"],\"sdf\"]]],[10,[1,\"totalGlow\",\"*\",\"outsideFactor\"]]]]},externals:{\"std.max\":()=>B,\"std.exp\":()=>Tt,\"std.smoothstep\":()=>be}})&&e.f)({})),k2=w.fn([n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r)=>{\"use gpu\";let o=__tsover_mul(t,s),a=ae(__tsover_mul(o,17.13)),i=ae(__tsover_mul(o,31.71)),l=ae(__tsover_mul(o,47.29)),c=__tsover_add(__tsover_add(a,__tsover_mul(i,.5)),__tsover_mul(l,.3)),d=K(-2,.8,r);return be(__tsover_sub(d,.1),__tsover_add(d,.1),c)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"time\"},{type:\"i\",name:\"flickerSpeed\"},{type:\"i\",name:\"flickerAmount\"}],body:[0,[[13,\"flickerTime\",[1,\"time\",\"*\",\"flickerSpeed\"]],[13,\"f1\",[6,\"std.sin\",[[1,\"flickerTime\",\"*\",[5,\"17.13\"]]]]],[13,\"f2\",[6,\"std.sin\",[[1,\"flickerTime\",\"*\",[5,\"31.71\"]]]]],[13,\"f3\",[6,\"std.sin\",[[1,\"flickerTime\",\"*\",[5,\"47.29\"]]]]],[13,\"combined\",[1,[1,\"f1\",\"+\",[1,\"f2\",\"*\",[5,\"0.5\"]]],\"+\",[1,\"f3\",\"*\",[5,\"0.3\"]]]],[13,\"threshold\",[6,\"std.mix\",[[4,\"-\",[5,\"2\"]],[5,\"0.8\"],\"flickerAmount\"]]],[10,[6,\"std.smoothstep\",[[1,\"threshold\",\"-\",[5,\"0.1\"]],[1,\"threshold\",\"+\",[5,\"0.1\"]],\"combined\"]]]]]},externals:{\"std.sin\":()=>ae,\"std.mix\":()=>K,\"std.smoothstep\":()=>be}})&&e.f)({})),B2=w.fn([m,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_sub(t.x,.5),i=Xt(__tsover_sub(t.y,.5),a),l=__tsover_add(__tsover_mul(i,2),__tsover_mul(s,r));return K(1,__tsover_add(__tsover_mul(ae(l),.5),.5),o)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfUV\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"flowSpeed\"},{type:\"i\",name:\"flowAmount\"}],body:[0,[[13,\"flowDx\",[1,[7,\"sdfUV\",\"x\"],\"-\",[5,\"0.5\"]]],[13,\"flowDy\",[1,[7,\"sdfUV\",\"y\"],\"-\",[5,\"0.5\"]]],[13,\"flowAngle\",[6,\"std.atan2\",[\"flowDy\",\"flowDx\"]]],[13,\"flowPhase\",[1,[1,\"flowAngle\",\"*\",[5,\"2\"]],\"+\",[1,\"time\",\"*\",\"flowSpeed\"]]],[13,\"flowWave\",[1,[1,[6,\"std.sin\",[\"flowPhase\"]],\"*\",[5,\"0.5\"]],\"+\",[5,\"0.5\"]]],[10,[6,\"std.mix\",[[5,\"1\"],\"flowWave\",\"flowAmount\"]]]]]},externals:{\"std.atan2\":()=>Xt,\"std.sin\":()=>ae,\"std.mix\":()=>K}})&&e.f)({})),_O=w.fn([S,S,S,S,S,n,m,n,m,n,n,n,A,A,n,A,n,n,n,n,n,n,n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_,f,h,g,y,b,x,P,C,E,R,U,L,k,D,O,F)=>{\"use gpu\";let q=t.x,Y=s.x,V=r.x,X=o.x,Q=a.x,te=__tsover_div(1,d.y),J=B(C,.001),oe=B(__tsover_div(te,c),.004),le=__tsover_div(__tsover_sub(Y,V),__tsover_mul(i,2)),se=__tsover_div(__tsover_sub(X,Q),__tsover_mul(i,2)),Te=K(q,__tsover_div(__tsover_add(__tsover_add(__tsover_add(__tsover_add(q,Y),V),X),Q),5),p),de=N(le,__tsover_sub(s.w,r.w),F>.5),xe=N(se,__tsover_sub(o.w,a.w),F>.5),Ae=B(Ue(__tsover_add(__tsover_mul(de,de),__tsover_mul(xe,xe))),1e-4),De=__tsover_div(de,Ae),Ee=__tsover_div(xe,Ae),ze=G(__tsover_div(__tsover_mul(Te,-1),B(_,.001)),0,1),Ie=be(__tsover_add(_,oe),__tsover_sub(_,oe),__tsover_mul(Te,-1)),ke=__tsover_add(re(__tsover_sub(__tsover_add(s.w,r.w),__tsover_mul(t.w,2))),re(__tsover_sub(__tsover_add(o.w,a.w),__tsover_mul(t.w,2)))),Me=__tsover_add(re(de),re(xe)),we=__tsover_div(ke,__tsover_add(__tsover_add(ke,__tsover_mul(i,.6)),__tsover_mul(Me,.35))),Ve=be(.08,.35,we),qe=N(ze,we,F>.5),st=N(Ie,Ve,F>.5),pt=be(oe,__tsover_mul(oe,-1),Te),Ze=__tsover_mul(pt,st),nt=qe,ht=Ue(B(__tsover_mul(nt,__tsover_sub(2,nt)),0)),yt=__tsover_sub(1,nt),St=__tsover_mul(De,yt),Mt=__tsover_mul(Ee,yt),Rt=ht,Kt=__tsover_mul(E,pO),Wt=pe(Kt),ls=ae(Kt),us=__tsover_mul(Wt,Pp),os=__tsover_mul(ls,Pp),bs=M2,hs=G(__tsover_add(__tsover_add(__tsover_mul(St,us),__tsover_mul(Mt,os)),__tsover_mul(Rt,bs)),0,1),gs=__tsover_add(__tsover_add(__tsover_mul(St,__tsover_mul(us,Fg)),__tsover_mul(Mt,__tsover_mul(os,Fg))),__tsover_mul(Rt,__tsover_mul(Ig,Fg))),Gs=Ia(__tsover_sub(8,__tsover_mul(U,7))),Ss=__tsover_mul(vt(G(gs,0,1),Gs),R),or=B2(l,u,D,O),yr=K(f,K(h,f,hs),g),lr=.12,Ur=__tsover_mul(ht,.35),so=yr.mul(__tsover_add(__tsover_add(lr,__tsover_mul(hs,.55)),Ur)),ro=A(__tsover_add(so.x,Ss),__tsover_add(so.y,Ss),__tsover_add(so.z,Ss)),_e=be(.55,1,qe),We=__tsover_mul(__tsover_mul(_e,x),or),Le=K(ro,K(f,A(1,1,1),.75),We).mul(K(1,or,.4)),Be=E2(Te,C,P),Oe=k2(u,L,k),tt=__tsover_mul(b,Oe),_t=Le.mul(tt),Bt=y.mul(__tsover_mul(Be,tt)),Dt=S(K(Bt.x,_t.x,Ze),K(Bt.y,_t.y,Ze),K(Bt.z,_t.z,Ze),B(Ze,G(__tsover_mul(Be,.6),0,1))),Nt=__tsover_add(__tsover_add(__tsover_mul(J,1.5),oe),i);return N(Dt,S(0,0,0,0),q>Nt)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sC\"},{type:\"i\",name:\"sR\"},{type:\"i\",name:\"sL\"},{type:\"i\",name:\"sU\"},{type:\"i\",name:\"sD\"},{type:\"i\",name:\"smoothEPS\"},{type:\"i\",name:\"sdfUV\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"viewportSize\"},{type:\"i\",name:\"time\"},{type:\"i\",name:\"cornerSmoothing\"},{type:\"i\",name:\"tubeThickness\"},{type:\"i\",name:\"color\"},{type:\"i\",name:\"secondaryColor\"},{type:\"i\",name:\"secondaryBlend\"},{type:\"i\",name:\"glowColor\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"hotCoreIntensity\"},{type:\"i\",name:\"glowIntensity\"},{type:\"i\",name:\"glowRadius\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"specularIntensity\"},{type:\"i\",name:\"specularSize\"},{type:\"i\",name:\"flickerSpeed\"},{type:\"i\",name:\"flickerAmount\"},{type:\"i\",name:\"flowSpeed\"},{type:\"i\",name:\"flowAmount\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"sdfCenter\",[7,\"sC\",\"x\"]],[13,\"sdfRight\",[7,\"sR\",\"x\"]],[13,\"sdfLeft\",[7,\"sL\",\"x\"]],[13,\"sdfUp\",[7,\"sU\",\"x\"]],[13,\"sdfDown\",[7,\"sD\",\"x\"]],[13,\"pxH\",[1,[5,\"1\"],\"/\",[7,\"viewportSize\",\"y\"]]],[13,\"glowR\",[6,\"std.max\",[\"glowRadius\",[5,\"0.001\"]]]],[13,\"softPx\",[6,\"std.max\",[[1,\"pxH\",\"/\",\"scale\"],[5,\"0.004\"]]]],[13,\"gradX\",[1,[1,\"sdfRight\",\"-\",\"sdfLeft\"],\"/\",[1,\"smoothEPS\",\"*\",[5,\"2\"]]]],[13,\"gradY\",[1,[1,\"sdfUp\",\"-\",\"sdfDown\"],\"/\",[1,\"smoothEPS\",\"*\",[5,\"2\"]]]],[13,\"smoothedSdf\",[1,[1,[1,[1,[1,\"sdfCenter\",\"+\",\"sdfRight\"],\"+\",\"sdfLeft\"],\"+\",\"sdfUp\"],\"+\",\"sdfDown\"],\"/\",[5,\"5\"]]],[13,\"sdf\",[6,\"std.mix\",[\"sdfCenter\",\"smoothedSdf\",\"cornerSmoothing\"]]],[13,\"gxRaw\",[6,\"std.select\",[\"gradX\",[1,[7,\"sR\",\"w\"],\"-\",[7,\"sL\",\"w\"]],[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"gyRaw\",[6,\"std.select\",[\"gradY\",[1,[7,\"sU\",\"w\"],\"-\",[7,\"sD\",\"w\"]],[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"gradLen\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"gxRaw\",\"*\",\"gxRaw\"],\"+\",[1,\"gyRaw\",\"*\",\"gyRaw\"]]]],[5,\"0.0001\"]]]],[13,\"gnx\",[1,\"gxRaw\",\"/\",\"gradLen\"]],[13,\"gny\",[1,\"gyRaw\",\"/\",\"gradLen\"]],[13,\"depthNorm2D\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",[6,\"std.max\",[\"tubeThickness\",[5,\"0.001\"]]]],[5,\"0\"],[5,\"1\"]]]],[13,\"innerMask2D\",[6,\"std.smoothstep\",[[1,\"tubeThickness\",\"+\",\"softPx\"],[1,\"tubeThickness\",\"-\",\"softPx\"],[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"lap\",[1,[6,\"std.abs\",[[1,[1,[7,\"sR\",\"w\"],\"+\",[7,\"sL\",\"w\"]],\"-\",[1,[7,\"sC\",\"w\"],\"*\",[5,\"2\"]]]]],\"+\",[6,\"std.abs\",[[1,[1,[7,\"sU\",\"w\"],\"+\",[7,\"sD\",\"w\"]],\"-\",[1,[7,\"sC\",\"w\"],\"*\",[5,\"2\"]]]]]]],[13,\"slopeMag\",[1,[6,\"std.abs\",[\"gxRaw\"]],\"+\",[6,\"std.abs\",[\"gyRaw\"]]]],[13,\"crease\",[1,\"lap\",\"/\",[1,[1,\"lap\",\"+\",[1,\"smoothEPS\",\"*\",[5,\"0.6\"]]],\"+\",[1,\"slopeMag\",\"*\",[5,\"0.35\"]]]]],[13,\"innerMaskVol\",[6,\"std.smoothstep\",[[5,\"0.08\"],[5,\"0.35\"],\"crease\"]]],[13,\"depthNorm\",[6,\"std.select\",[\"depthNorm2D\",\"crease\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"innerMask\",[6,\"std.select\",[\"innerMask2D\",\"innerMaskVol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"outerMask\",[6,\"std.smoothstep\",[\"softPx\",[1,\"softPx\",\"*\",[4,\"-\",[5,\"1\"]]],\"sdf\"]]],[13,\"tubeMask\",[1,\"outerMask\",\"*\",\"innerMask\"]],[13,\"t\",\"depthNorm\"],[13,\"cylinderZ\",[6,\"std.sqrt\",[[6,\"std.max\",[[1,\"t\",\"*\",[1,[5,\"2\"],\"-\",\"t\"]],[5,\"0\"]]]]]],[13,\"cylinderR\",[1,[5,\"1\"],\"-\",\"t\"]],[13,\"nx\",[1,\"gnx\",\"*\",\"cylinderR\"]],[13,\"ny\",[1,\"gny\",\"*\",\"cylinderR\"]],[13,\"nz\",\"cylinderZ\"],[13,\"lightRad\",[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]],[13,\"lx\",[6,\"std.cos\",[\"lightRad\"]]],[13,\"ly\",[6,\"std.sin\",[\"lightRad\"]]],[13,\"lxn\",[1,\"lx\",\"*\",\"_INV_LIGHT_LEN\"]],[13,\"lyn\",[1,\"ly\",\"*\",\"_INV_LIGHT_LEN\"]],[13,\"lzn\",\"_LZN\"],[13,\"NdotL\",[1,[1,[1,\"nx\",\"*\",\"lxn\"],\"+\",[1,\"ny\",\"*\",\"lyn\"]],\"+\",[1,\"nz\",\"*\",\"lzn\"]]],[13,\"diffuse\",[6,\"std.clamp\",[\"NdotL\",[5,\"0\"],[5,\"1\"]]]],[13,\"NdotH\",[1,[1,[1,\"nx\",\"*\",[1,\"lxn\",\"*\",\"_INV_H_LEN\"]],\"+\",[1,\"ny\",\"*\",[1,\"lyn\",\"*\",\"_INV_H_LEN\"]]],\"+\",[1,\"nz\",\"*\",[1,\"_HZ\",\"*\",\"_INV_H_LEN\"]]]],[13,\"shininess\",[6,\"std.exp2\",[[1,[5,\"8\"],\"-\",[1,\"specularSize\",\"*\",[5,\"7\"]]]]]],[13,\"specular\",[1,[6,\"std.pow\",[[6,\"std.clamp\",[\"NdotH\",[5,\"0\"],[5,\"1\"]]],\"shininess\"]],\"*\",\"specularIntensity\"]],[13,\"flowMod\",[6,\"neonFlow\",[\"sdfUV\",\"time\",\"flowSpeed\",\"flowAmount\"]]],[13,\"twoTone\",[6,\"std.mix\",[\"secondaryColor\",\"color\",\"diffuse\"]]],[13,\"baseColor\",[6,\"std.mix\",[\"color\",\"twoTone\",\"secondaryBlend\"]]],[13,\"ambient\",[5,\"0.12\"]],[13,\"selfEmission\",[1,\"cylinderZ\",\"*\",[5,\"0.35\"]]],[13,\"shadedRgb\",[6,[7,\"baseColor\",\"mul\"],[[1,[1,\"ambient\",\"+\",[1,\"diffuse\",\"*\",[5,\"0.55\"]]],\"+\",\"selfEmission\"]]]],[13,\"tubeRgb\",[6,\"d.vec3f\",[[1,[7,\"shadedRgb\",\"x\"],\"+\",\"specular\"],[1,[7,\"shadedRgb\",\"y\"],\"+\",\"specular\"],[1,[7,\"shadedRgb\",\"z\"],\"+\",\"specular\"]]]],[13,\"coreProfile\",[6,\"std.smoothstep\",[[5,\"0.55\"],[5,\"1\"],\"depthNorm\"]]],[13,\"hotCoreAmt\",[1,[1,\"coreProfile\",\"*\",\"hotCoreIntensity\"],\"*\",\"flowMod\"]],[13,\"hotCoreCol\",[6,\"std.mix\",[\"color\",[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],[5,\"0.75\"]]]],[13,\"tubeWithCore\",[6,\"std.mix\",[\"tubeRgb\",\"hotCoreCol\",\"hotCoreAmt\"]]],[13,\"tubeFlowed\",[6,[7,\"tubeWithCore\",\"mul\"],[[6,\"std.mix\",[[5,\"1\"],\"flowMod\",[5,\"0.4\"]]]]]],[13,\"glowMasked\",[6,\"neonGlow\",[\"sdf\",\"glowRadius\",\"glowIntensity\"]]],[13,\"flickerValue\",[6,\"neonFlicker\",[\"time\",\"flickerSpeed\",\"flickerAmount\"]]],[13,\"intensityMul\",[1,\"intensity\",\"*\",\"flickerValue\"]],[13,\"finalTubeRgb\",[6,[7,\"tubeFlowed\",\"mul\"],[\"intensityMul\"]]],[13,\"finalGlowRgb\",[6,[7,\"glowColor\",\"mul\"],[[1,\"glowMasked\",\"*\",\"intensityMul\"]]]],[13,\"finalR\",[6,\"std.mix\",[[7,\"finalGlowRgb\",\"x\"],[7,\"finalTubeRgb\",\"x\"],\"tubeMask\"]]],[13,\"finalG\",[6,\"std.mix\",[[7,\"finalGlowRgb\",\"y\"],[7,\"finalTubeRgb\",\"y\"],\"tubeMask\"]]],[13,\"finalB\",[6,\"std.mix\",[[7,\"finalGlowRgb\",\"z\"],[7,\"finalTubeRgb\",\"z\"],\"tubeMask\"]]],[13,\"glowAlpha\",[6,\"std.clamp\",[[1,\"glowMasked\",\"*\",[5,\"0.6\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"finalA\",[6,\"std.max\",[\"tubeMask\",\"glowAlpha\"]]],[13,\"result\",[6,\"d.vec4f\",[\"finalR\",\"finalG\",\"finalB\",\"finalA\"]]],[13,\"threshOut\",[1,[1,[1,\"glowR\",\"*\",[5,\"1.5\"]],\"+\",\"softPx\"],\"+\",\"smoothEPS\"]],[10,[6,\"std.select\",[\"result\",[6,\"d.vec4f\",[[5,\"0\"],[5,\"0\"],[5,\"0\"],[5,\"0\"]]],[1,\"sdfCenter\",\">\",\"threshOut\"]]]]]]},externals:{\"std.max\":()=>B,\"std.mix\":()=>K,\"std.select\":()=>N,\"std.sqrt\":()=>Ue,\"std.clamp\":()=>G,\"std.smoothstep\":()=>be,\"std.abs\":()=>re,DEG_TO_RAD:()=>pO,\"std.cos\":()=>pe,\"std.sin\":()=>ae,_INV_LIGHT_LEN:()=>Pp,_LZN:()=>M2,_INV_H_LEN:()=>Fg,_HZ:()=>Ig,\"std.exp2\":()=>Ia,\"std.pow\":()=>vt,neonFlow:()=>B2,\"d.vec3f\":()=>A,neonGlow:()=>E2,neonFlicker:()=>k2,\"d.vec4f\":()=>S}})&&e.f)({}));function vde(e,t,s,r){let{uniforms:o,ctx:a}=e,i=r?.gradSampler??t,l=$e(r?.volumetric?1:0),c=W(xc,\"sdfSpaceUV\",[o.center,o.scale,o.rotation,a.uv,a.aspect]),d=W(mO,\"neonSmoothEPS\",[o.tubeThickness,o.cornerSmoothing,l]),u=d.mul(-1);return W(_O,\"neonComposite\",[t(c),i(W(da,\"offsetUV\",[c,d,$e(0)])),i(W(da,\"offsetUV\",[c,u,$e(0)])),i(W(da,\"offsetUV\",[c,$e(0),d])),i(W(da,\"offsetUV\",[c,$e(0),u])),d,c,o.scale,a.viewportSize,s,o.cornerSmoothing,o.tubeThickness,o.color.member(\"rgb\"),o.secondaryColor.member(\"rgb\"),o.secondaryBlend,o.glowColor.member(\"rgb\"),o.intensity,o.hotCoreIntensity,o.glowIntensity,o.glowRadius,o.lightAngle,o.specularIntensity,o.specularSize,o.flickerSpeed,o.flickerAmount,o.flowSpeed,o.flowAmount,l])}var{applyNeonEffect:hde}=fde,gde=JSON.stringify({type:\"sphere3D\",radius:.35}),fO=H({name:\"Neon\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photorealistic neon tube / 3D pipe effect driven by a custom shape\",...Ts({chord:\"firstLobe\",stencil:\"none\",surface:(e,t)=>hde(t,e.sampler,t.ctx.time,{volumetric:e.volumetric,gradSampler:e.gradSampler})}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the neon shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the neon shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the neon shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},color:{default:\"#00ddff\",transform:ce,description:\"Primary neon tube color\",ui:{type:\"color\",label:\"Tube Color\",group:\"Colors\"}},secondaryColor:{default:\"#ff00aa\",transform:ce,description:\"Shadow-side color for a two-tone / dual-lit pipe look\",ui:{type:\"color\",label:\"Secondary Color\",group:\"Colors\"}},secondaryBlend:{default:.5,description:\"Blend between mono (0) and two-tone (1) tube coloring\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Blend\",group:\"Colors\"}},glowColor:{default:\"#00ddff\",transform:ce,description:\"Color of the outer glow / bloom\",ui:{type:\"color\",label:\"Glow Color\",group:\"Colors\"}},tubeThickness:{default:.2,transform:e=>e*.05,description:\"How far inward from the boundary the tube extends. Low = thin neon outline, high = thick 3D pipe\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Tube Thickness\",group:\"Tube\"}},intensity:{default:1.5,description:\"Overall brightness multiplier\",ui:{type:\"range\",min:.5,max:4,step:.01,label:\"Intensity\",group:\"Tube\"}},hotCoreIntensity:{default:.6,description:\"Bright white-hot center line \\u2014 the gas discharge glow inside the tube\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Hot Core\",group:\"Tube\"}},glowIntensity:{default:.6,description:\"Outer glow / bloom strength\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Glow Intensity\",group:\"Glow\"}},glowRadius:{default:.25,description:\"How far the glow extends beyond the tube\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Glow Radius\",group:\"Glow\"}},lightAngle:{default:300,description:\"Directional light angle in degrees \\u2014 controls 3D shading on the tube\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Lighting\"}},specularIntensity:{default:.5,description:\"Specular highlight brightness on the tube surface\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Specular\",group:\"Lighting\"}},specularSize:{default:.5,description:\"Specular highlight size \\u2014 0 = tight pinpoint, 1 = broad sheen\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Specular Size\",group:\"Lighting\"}},cornerSmoothing:{default:.15,description:\"Rounds sharp corners to mimic how real glass tubes curve at bends\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Corner Smoothing\",group:\"Tube\"}},flickerSpeed:{default:0,description:\"Flicker animation speed \\u2014 0 = off, higher = faster sporadic on/off\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Flicker\"}},flickerAmount:{default:.2,description:\"How often the neon flickers off \\u2014 0 = always on, 1 = frequent outages\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Amount\",group:\"Flicker\"}},flowSpeed:{default:0,description:\"Flow animation speed \\u2014 0 = off, light rotates through the tube\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Flow\"}},flowAmount:{default:.3,description:\"Strength of the flowing brightness variation \\u2014 0 = uniform, 1 = dramatic\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Amount\",group:\"Flow\"}},shape:{default:gde,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file \\u2014 when non-empty, activates SVG mode and triggers a shader recompile\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type \\u2014 triggers recompile when shape is switched. When empty, derived from shape JSON at mount time.\"}}});var yde=ko({applyEmbossEffect:()=>bde,embossComposite:()=>gO,embossEdgeLighting:()=>R2,embossHeight:()=>Yi,embossTraceUV:()=>z2,embossWarpUV:()=>hO}),Cp=.017453292519943295,Yg=1/Math.sqrt(1.49),vO=.7*Yg,Yi=w.fn([n,n,n,n],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=__tsover_mul(s,G(__tsover_div(__tsover_mul(t,-1),.18),0,1));return N(__tsover_mul(s,be(r,__tsover_mul(r,-1),t)),a,o>.5)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"s\"},{type:\"i\",name:\"depth\"},{type:\"i\",name:\"transitionW\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"vol\",[1,\"depth\",\"*\",[6,\"std.clamp\",[[1,[1,\"s\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",[5,\"0.18\"]],[5,\"0\"],[5,\"1\"]]]]],[13,\"flat\",[1,\"depth\",\"*\",[6,\"std.smoothstep\",[\"transitionW\",[1,\"transitionW\",\"*\",[4,\"-\",[5,\"1\"]]],\"s\"]]]],[10,[6,\"std.select\",[\"flat\",\"vol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]]]]},externals:{\"std.clamp\":()=>G,\"std.smoothstep\":()=>be,\"std.select\":()=>N}})&&e.f)({})),z2=w.fn([m,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=pe(__tsover_mul(s,Cp)),i=ae(__tsover_mul(s,Cp)),l=B(re(r),1e-4),c=__tsover_mul(l,.06);return m(__tsover_add(t.x,__tsover_mul(__tsover_mul(a,c),o)),__tsover_add(t.y,__tsover_mul(__tsover_mul(i,c),o)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfUV\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"depth\"},{type:\"i\",name:\"frac\"}],body:[0,[[13,\"lx\",[6,\"std.cos\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"ly\",[6,\"std.sin\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"absDepth\",[6,\"std.max\",[[6,\"std.abs\",[\"depth\"]],[5,\"0.0001\"]]]],[13,\"spread\",[1,\"absDepth\",\"*\",[5,\"0.06\"]]],[10,[6,\"d.vec2f\",[[1,[7,\"sdfUV\",\"x\"],\"+\",[1,[1,\"lx\",\"*\",\"spread\"],\"*\",\"frac\"]],[1,[7,\"sdfUV\",\"y\"],\"+\",[1,[1,\"ly\",\"*\",\"spread\"],\"*\",\"frac\"]]]]]]]},externals:{\"std.cos\":()=>pe,DEG_TO_RAD:()=>Cp,\"std.sin\":()=>ae,\"std.max\":()=>B,\"std.abs\":()=>re,\"d.vec2f\":()=>m}})&&e.f)({})),hO=w.fn([m,m,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let i=be(.003,__tsover_mul(.003,-1),r),l=__tsover_sub(1,__tsover_mul(__tsover_mul(o,.05),i)),c=s.x,d=__tsover_sub(1,s.y);return m(__tsover_add(c,__tsover_mul(__tsover_sub(t.x,c),l)),__tsover_add(d,__tsover_mul(__tsover_sub(t.y,d),l)))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"sdfC\"},{type:\"i\",name:\"depth\"}],body:[0,[[13,\"tw\",[5,\"0.003\"]],[13,\"inside\",[6,\"std.smoothstep\",[\"tw\",[1,\"tw\",\"*\",[4,\"-\",[5,\"1\"]]],\"sdfC\"]]],[13,\"dispScale\",[1,[5,\"1\"],\"-\",[1,[1,\"depth\",\"*\",[5,\"0.05\"]],\"*\",\"inside\"]]],[13,\"centerX\",[7,\"center\",\"x\"]],[13,\"centerY\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[10,[6,\"d.vec2f\",[[1,\"centerX\",\"+\",[1,[1,[7,\"uv\",\"x\"],\"-\",\"centerX\"],\"*\",\"dispScale\"]],[1,\"centerY\",\"+\",[1,[1,[7,\"uv\",\"y\"],\"-\",\"centerY\"],\"*\",\"dispScale\"]]]]]]]},externals:{\"std.smoothstep\":()=>be,\"d.vec2f\":()=>m}})&&e.f)({})),R2=w.fn([n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c)=>{\"use gpu\";let p=Yi(t,a,.003,c),_=Yi(s,a,.003,c),f=Yi(r,a,.003,c),h=Yi(o,a,.003,c),g=__tsover_div(__tsover_sub(p,_),__tsover_mul(.003,2)),y=__tsover_div(__tsover_sub(f,h),__tsover_mul(.003,2)),b=Ue(__tsover_add(__tsover_add(__tsover_mul(g,g),__tsover_mul(y,y)),1)),x=__tsover_div(__tsover_mul(g,-1),b),P=__tsover_div(__tsover_mul(y,-1),b),C=__tsover_div(1,b),E=pe(__tsover_mul(i,Cp)),R=ae(__tsover_mul(i,Cp)),U=__tsover_mul(E,Yg),L=__tsover_mul(R,Yg),k=vO,D=__tsover_add(__tsover_add(__tsover_mul(x,U),__tsover_mul(P,L)),__tsover_mul(C,k)),O=__tsover_mul(__tsover_sub(D,k),l);return m(B(O,0),B(__tsover_mul(O,-1),0))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfR\"},{type:\"i\",name:\"sdfL\"},{type:\"i\",name:\"sdfU\"},{type:\"i\",name:\"sdfD\"},{type:\"i\",name:\"depth\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"lightIntensity\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"eps\",[5,\"0.003\"]],[13,\"tw\",[5,\"0.003\"]],[13,\"hR\",[6,\"embossHeight\",[\"sdfR\",\"depth\",\"tw\",\"volumetric\"]]],[13,\"hL\",[6,\"embossHeight\",[\"sdfL\",\"depth\",\"tw\",\"volumetric\"]]],[13,\"hU\",[6,\"embossHeight\",[\"sdfU\",\"depth\",\"tw\",\"volumetric\"]]],[13,\"hD\",[6,\"embossHeight\",[\"sdfD\",\"depth\",\"tw\",\"volumetric\"]]],[13,\"dhx\",[1,[1,\"hR\",\"-\",\"hL\"],\"/\",[1,\"eps\",\"*\",[5,\"2\"]]]],[13,\"dhy\",[1,[1,\"hU\",\"-\",\"hD\"],\"/\",[1,\"eps\",\"*\",[5,\"2\"]]]],[13,\"nLen\",[6,\"std.sqrt\",[[1,[1,[1,\"dhx\",\"*\",\"dhx\"],\"+\",[1,\"dhy\",\"*\",\"dhy\"]],\"+\",[5,\"1\"]]]]],[13,\"nx\",[1,[1,\"dhx\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"nLen\"]],[13,\"ny\",[1,[1,\"dhy\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"nLen\"]],[13,\"nz\",[1,[5,\"1\"],\"/\",\"nLen\"]],[13,\"lx\",[6,\"std.cos\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"ly\",[6,\"std.sin\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"lxn\",[1,\"lx\",\"*\",\"_INV_LIGHT_LEN\"]],[13,\"lyn\",[1,\"ly\",\"*\",\"_INV_LIGHT_LEN\"]],[13,\"lzn\",\"_LZN\"],[13,\"NdotL\",[1,[1,[1,\"nx\",\"*\",\"lxn\"],\"+\",[1,\"ny\",\"*\",\"lyn\"]],\"+\",[1,\"nz\",\"*\",\"lzn\"]]],[13,\"edgeLighting\",[1,[1,\"NdotL\",\"-\",\"lzn\"],\"*\",\"lightIntensity\"]],[13,\"highlight\",[6,\"std.max\",[\"edgeLighting\",[5,\"0\"]]]],[13,\"edgeShadow\",[6,\"std.max\",[[1,\"edgeLighting\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"]]]],[10,[6,\"d.vec2f\",[\"highlight\",\"edgeShadow\"]]]]]},externals:{embossHeight:()=>Yi,\"std.sqrt\":()=>Ue,\"std.cos\":()=>pe,DEG_TO_RAD:()=>Cp,\"std.sin\":()=>ae,_INV_LIGHT_LEN:()=>Yg,_LZN:()=>vO,\"std.max\":()=>B,\"d.vec2f\":()=>m}})&&e.f)({})),gO=w.fn([S,n,n,n,n,n,n,n,n,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_,f)=>{\"use gpu\";let g=R2(r,o,a,i,d,u,p,f),y=g.x,b=g.y,x=Yi(s,d,.003,f),P=B(re(d),1e-4),C=__tsover_mul(P,.06),E=Yi(l,d,C,f),R=Yi(c,d,C,f),U=__tsover_div(__tsover_sub(E,x),P),L=__tsover_div(__tsover_sub(R,x),P),k=__tsover_add(__tsover_mul(be(0,.5,U),.55),__tsover_mul(be(0,.5,L),.45)),D=__tsover_mul(G(__tsover_add(b,k),0,1),_),O=A(t.x,t.y,t.z),F=A(y,y,y),q=G(O.mul(__tsover_sub(1,D)).add(F),A(0),A(1));return S(q.x,q.y,q.z,t.w)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"childColor\"},{type:\"i\",name:\"sdfC\"},{type:\"i\",name:\"sdfR\"},{type:\"i\",name:\"sdfL\"},{type:\"i\",name:\"sdfU\"},{type:\"i\",name:\"sdfD\"},{type:\"i\",name:\"h1raw\"},{type:\"i\",name:\"h2raw\"},{type:\"i\",name:\"depth\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"lightIntensity\"},{type:\"i\",name:\"shadowIntensity\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"tw\",[5,\"0.003\"]],[13,\"light\",[6,\"embossEdgeLighting\",[\"sdfR\",\"sdfL\",\"sdfU\",\"sdfD\",\"depth\",\"lightAngle\",\"lightIntensity\",\"volumetric\"]]],[13,\"highlight\",[7,\"light\",\"x\"]],[13,\"edgeShadow\",[7,\"light\",\"y\"]],[13,\"hC\",[6,\"embossHeight\",[\"sdfC\",\"depth\",\"tw\",\"volumetric\"]]],[13,\"absDepth\",[6,\"std.max\",[[6,\"std.abs\",[\"depth\"]],[5,\"0.0001\"]]]],[13,\"shadowTw\",[1,\"absDepth\",\"*\",[5,\"0.06\"]]],[13,\"h1\",[6,\"embossHeight\",[\"h1raw\",\"depth\",\"shadowTw\",\"volumetric\"]]],[13,\"h2\",[6,\"embossHeight\",[\"h2raw\",\"depth\",\"shadowTw\",\"volumetric\"]]],[13,\"diff1\",[1,[1,\"h1\",\"-\",\"hC\"],\"/\",\"absDepth\"]],[13,\"diff2\",[1,[1,\"h2\",\"-\",\"hC\"],\"/\",\"absDepth\"]],[13,\"castShadow\",[1,[1,[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.5\"],\"diff1\"]],\"*\",[5,\"0.55\"]],\"+\",[1,[6,\"std.smoothstep\",[[5,\"0\"],[5,\"0.5\"],\"diff2\"]],\"*\",[5,\"0.45\"]]]],[13,\"totalShadow\",[1,[6,\"std.clamp\",[[1,\"edgeShadow\",\"+\",\"castShadow\"],[5,\"0\"],[5,\"1\"]]],\"*\",\"shadowIntensity\"]],[13,\"childRgb\",[6,\"d.vec3f\",[[7,\"childColor\",\"x\"],[7,\"childColor\",\"y\"],[7,\"childColor\",\"z\"]]]],[13,\"highlightRgb\",[6,\"d.vec3f\",[\"highlight\",\"highlight\",\"highlight\"]]],[13,\"finalRgb\",[6,\"std.clamp\",[[6,[7,[6,[7,\"childRgb\",\"mul\"],[[1,[5,\"1\"],\"-\",\"totalShadow\"]]],\"add\"],[\"highlightRgb\"]],[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[10,[6,\"d.vec4f\",[[7,\"finalRgb\",\"x\"],[7,\"finalRgb\",\"y\"],[7,\"finalRgb\",\"z\"],[7,\"childColor\",\"w\"]]]]]]},externals:{embossEdgeLighting:()=>R2,embossHeight:()=>Yi,\"std.max\":()=>B,\"std.abs\":()=>re,\"std.smoothstep\":()=>be,\"std.clamp\":()=>G,\"d.vec3f\":()=>A,\"d.vec4f\":()=>S}})&&e.f)({}));function bde(e,t,s,r){let{uniforms:o,ctx:a}=e,i=r?.gradSampler??t,l=$e(r?.volumetric?1:0),c=.003,d=W(xc,\"sdfSpaceUV\",[o.center,o.scale,o.rotation,a.uv,a.aspect]),u=t(d).member(\"r\"),p=i(W(da,\"offsetUV\",[d,$e(c),$e(0)])).member(\"r\"),_=i(W(da,\"offsetUV\",[d,$e(-c),$e(0)])).member(\"r\"),f=i(W(da,\"offsetUV\",[d,$e(0),$e(c)])).member(\"r\"),h=i(W(da,\"offsetUV\",[d,$e(0),$e(-c)])).member(\"r\"),g=i(W(z2,\"embossTraceUV\",[d,o.lightAngle,o.depth,$e(.5)])).member(\"r\"),y=i(W(z2,\"embossTraceUV\",[d,o.lightAngle,o.depth,$e(1)])).member(\"r\"),b=W(hO,\"embossWarpUV\",[a.uv,o.center,u,o.depth]);return W(gO,\"embossComposite\",[W(Yt,\"unpremultiplyAlpha\",[s.sample(b)]),u,p,_,f,h,g,y,o.depth,o.lightAngle,o.lightIntensity,o.shadowIntensity,l])}var{applyEmbossEffect:xde}=yde,Ade=JSON.stringify({type:\"sphere3D\",radius:.35}),yO=H({name:\"Emboss\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Embossed / debossed relief shading on top of child content, driven by a custom shape\",requiresRTT:!0,requiresChild:!0,...Ts({chord:\"firstLobe\",stencil:\"none\",child:\"required\",surface:(e,t)=>xde(t,e.sampler,e.childTexture,{volumetric:e.volumetric,gradSampler:e.gradSampler})}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the embossed shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the embossed shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the embossed shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},depth:{default:-.5,description:\"Relief depth \\u2014 negative = inset (debossed), positive = raised (embossed)\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Depth\",group:\"Relief\"}},lightAngle:{default:260,description:\"Directional light angle in degrees \\u2014 controls highlight and shadow direction\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Light Angle\",group:\"Lighting\"}},lightIntensity:{default:.6,description:\"Strength of the directional edge highlights and shadows\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Light Intensity\",group:\"Lighting\"}},shadowIntensity:{default:.3,description:\"Darkness of the relief shadow\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadow Intensity\",group:\"Relief\"}},shape:{default:Ade,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file \\u2014 when non-empty, activates SVG mode and triggers a shader recompile\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type \\u2014 triggers recompile when shape is switched. When empty, derived from shape JSON at mount time.\"}}});var wde=ko({Distortion3DTrace:()=>qg,applyUVMode3d:()=>Tde,buildParamThreadedTrace:()=>Pde,distortion3dLighting:()=>Sde}),qg=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({hitPos:A,normal:A,rd:A,hit:n}),\"Distortion3DTrace\"),Tde=w.fn([m,n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";let r=G(t,m(0),m(1)),o=re(Re(t.mul(.5)).mul(2).sub(1)),a=Re(t),i=N(n(0),n(1),t.y<=1),l=N(n(0),i,t.y>=0),c=N(n(0),l,t.x<=1),d=N(n(0),c,t.x>=0),u=N(N(N(a,o,s<2.5),t,s<1.5),r,s<.5),p=N(N(n(1),d,s<1.5),n(1),s<.5);return A(u.x,u.y,p)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"uvMode\"}],body:[0,[[13,\"stretched\",[6,\"std.clamp\",[\"uv\",[6,\"d.vec2f\",[[5,\"0\"]]],[6,\"d.vec2f\",[[5,\"1\"]]]]]],[13,\"mirrored\",[6,\"std.abs\",[[6,[7,[6,[7,[6,\"std.fract\",[[6,[7,\"uv\",\"mul\"],[[5,\"0.5\"]]]]],\"mul\"],[[5,\"2\"]]],\"sub\"],[[5,\"1\"]]]]]],[13,\"wrapped\",[6,\"std.fract\",[\"uv\"]]],[13,\"tv1\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,[7,\"uv\",\"y\"],\"<=\",[5,\"1\"]]]]],[13,\"tv2\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"tv1\",[1,[7,\"uv\",\"y\"],\">=\",[5,\"0\"]]]]],[13,\"tv3\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"tv2\",[1,[7,\"uv\",\"x\"],\"<=\",[5,\"1\"]]]]],[13,\"transparentVisible\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],\"tv3\",[1,[7,\"uv\",\"x\"],\">=\",[5,\"0\"]]]]],[13,\"fuv2\",[6,\"std.select\",[\"wrapped\",\"mirrored\",[1,\"uvMode\",\"<\",[5,\"2.5\"]]]]],[13,\"fuv1\",[6,\"std.select\",[\"fuv2\",\"uv\",[1,\"uvMode\",\"<\",[5,\"1.5\"]]]]],[13,\"finalUV\",[6,\"std.select\",[\"fuv1\",\"stretched\",[1,\"uvMode\",\"<\",[5,\"0.5\"]]]]],[13,\"vis1\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"1\"]]],\"transparentVisible\",[1,\"uvMode\",\"<\",[5,\"1.5\"]]]]],[13,\"visible\",[6,\"std.select\",[\"vis1\",[6,\"d.f32\",[[5,\"1\"]]],[1,\"uvMode\",\"<\",[5,\"0.5\"]]]]],[10,[6,\"d.vec3f\",[[7,\"finalUV\",\"x\"],[7,\"finalUV\",\"y\"],\"visible\"]]]]]},externals:{\"std.clamp\":()=>G,\"d.vec2f\":()=>m,\"std.abs\":()=>re,\"std.fract\":()=>Re,\"std.select\":()=>N,\"d.f32\":()=>n,\"d.vec3f\":()=>A}})&&e.f)({})),Sde=w.fn([S,A,A,n,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l)=>{\"use gpu\";let c=vs(A(.4,.7,-.6)),d=r.mul(-1),u=G(Se(s,c),0,1),p=vs(c.add(d)),_=__tsover_add(__tsover_mul(i,256),4),f=__tsover_mul(__tsover_mul(vt(G(Se(s,p),0,1),_),i),1.5),h=G(Se(s,d),0,1),g=__tsover_mul(__tsover_mul(vt(__tsover_sub(1,h),3),.5),i),y=G(K(1,__tsover_add(__tsover_add(__tsover_add(.35,__tsover_mul(u,.65)),f),g),l),0,3),b=G(t.xyz.mul(y),A(0),A(1)),x=__tsover_mul(__tsover_mul(o,t.w),a);return S(b.x,b.y,b.z,x)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"surfaceColor\"},{type:\"i\",name:\"normal\"},{type:\"i\",name:\"rd\"},{type:\"i\",name:\"hit\"},{type:\"i\",name:\"uvVisible\"},{type:\"i\",name:\"glossAmt\"},{type:\"i\",name:\"lightAmt\"}],body:[0,[[13,\"lightDir\",[6,\"std.normalize\",[[6,\"d.vec3f\",[[5,\"0.4\"],[5,\"0.7\"],[4,\"-\",[5,\"0.6\"]]]]]]],[13,\"viewDir\",[6,[7,\"rd\",\"mul\"],[[4,\"-\",[5,\"1\"]]]]],[13,\"diffuse\",[6,\"std.clamp\",[[6,\"std.dot\",[\"normal\",\"lightDir\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"halfVec\",[6,\"std.normalize\",[[6,[7,\"lightDir\",\"add\"],[\"viewDir\"]]]]],[13,\"specPow\",[1,[1,\"glossAmt\",\"*\",[5,\"256\"]],\"+\",[5,\"4\"]]],[13,\"specular\",[1,[1,[6,\"std.pow\",[[6,\"std.clamp\",[[6,\"std.dot\",[\"normal\",\"halfVec\"]],[5,\"0\"],[5,\"1\"]]],\"specPow\"]],\"*\",\"glossAmt\"],\"*\",[5,\"1.5\"]]],[13,\"nDotV\",[6,\"std.clamp\",[[6,\"std.dot\",[\"normal\",\"viewDir\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"fresnel\",[1,[1,[6,\"std.pow\",[[1,[5,\"1\"],\"-\",\"nDotV\"],[5,\"3\"]]],\"*\",[5,\"0.5\"]],\"*\",\"glossAmt\"]],[13,\"ambient\",[5,\"0.35\"]],[13,\"lit\",[6,\"std.mix\",[[5,\"1\"],[1,[1,[1,\"ambient\",\"+\",[1,\"diffuse\",\"*\",[5,\"0.65\"]]],\"+\",\"specular\"],\"+\",\"fresnel\"],\"lightAmt\"]]],[13,\"litClamped\",[6,\"std.clamp\",[\"lit\",[5,\"0\"],[5,\"3\"]]]],[13,\"col\",[6,[7,[7,\"surfaceColor\",\"xyz\"],\"mul\"],[\"litClamped\"]]],[13,\"colClamped\",[6,\"std.clamp\",[\"col\",[6,\"d.vec3f\",[[5,\"0\"]]],[6,\"d.vec3f\",[[5,\"1\"]]]]]],[13,\"alpha\",[1,[1,\"hit\",\"*\",[7,\"surfaceColor\",\"w\"]],\"*\",\"uvVisible\"]],[10,[6,\"d.vec4f\",[[7,\"colClamped\",\"x\"],[7,\"colClamped\",\"y\"],[7,\"colClamped\",\"z\"],\"alpha\"]]]]]},externals:{\"std.normalize\":()=>vs,\"d.vec3f\":()=>A,\"std.clamp\":()=>G,\"std.dot\":()=>Se,\"std.pow\":()=>vt,\"std.mix\":()=>K,\"d.vec4f\":()=>S}})&&e.f)({}));function Pde(e,t){let s=t.steps,r=t.stepCap,o=t.hitEps,a=t.maxDistance??20;return w.fn([m,n,m,n,S,S,S,n],qg)((i=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(i.f=((l,c,d,u,p,_,f,h)=>{\"use gpu\";let g=d.x,y=__tsover_sub(1,d.y),b=__tsover_sub(8,__tsover_mul(u,.06)),x=A(0,0,__tsover_mul(b,-1)),P=vs(A(__tsover_mul(__tsover_sub(l.x,g),c),__tsover_sub(l.y,y),1.5)),C=n(0),E=n(0),R=n(a);for(let Y=0;Y<s;Y++){let V=e(x.add(P.mul(C)),p,_,f,h);if(V<o){E=n(1);break}if(C>R)break;C=__tsover_add(C,ye(V,r))}let U=x.add(P.mul(C)),L=.001,k=__tsover_mul(L,-1),D=e(U.add(A(L,k,k)),p,_,f,h),O=e(U.add(A(k,k,L)),p,_,f,h),F=e(U.add(A(k,L,k)),p,_,f,h),q=e(U.add(A(L,L,L)),p,_,f,h);return qg({hitPos:U,normal:vs(A(1,-1,-1).mul(D).add(A(-1,-1,1).mul(O)).add(A(-1,1,-1).mul(F)).add(A(1,1,1).mul(q))),rd:P,hit:E})}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"aspect\"},{type:\"i\",name:\"center\"},{type:\"i\",name:\"zoom\"},{type:\"i\",name:\"rp0\"},{type:\"i\",name:\"rp1\"},{type:\"i\",name:\"rp2\"},{type:\"i\",name:\"t\"}],body:[0,[[13,\"cx\",[7,\"center\",\"x\"]],[13,\"cy\",[1,[5,\"1\"],\"-\",[7,\"center\",\"y\"]]],[13,\"camDist\",[1,[5,\"8\"],\"-\",[1,\"zoom\",\"*\",[5,\"0.06\"]]]],[13,\"ro\",[6,\"d.vec3f\",[[5,\"0\"],[5,\"0\"],[1,\"camDist\",\"*\",[4,\"-\",[5,\"1\"]]]]]],[13,\"rd\",[6,\"std.normalize\",[[6,\"d.vec3f\",[[1,[1,[7,\"uv\",\"x\"],\"-\",\"cx\"],\"*\",\"aspect\"],[1,[7,\"uv\",\"y\"],\"-\",\"cy\"],[5,\"1.5\"]]]]]],[12,\"rayT\",[6,\"d.f32\",[[5,\"0\"]]]],[12,\"hit\",[6,\"d.f32\",[[5,\"0\"]]]],[13,\"maxDistF\",[6,\"d.f32\",[\"MAX_DIST\"]]],[14,[12,\"i\",[5,\"0\"]],[1,\"i\",\"<\",\"STEPS\"],[102,\"++\",\"i\"],[0,[[13,\"p\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"rayT\"]]]]],[13,\"dd\",[6,\"sdf\",[\"p\",\"rp0\",\"rp1\",\"rp2\",\"t\"]]],[11,[1,\"dd\",\"<\",\"HIT_EPS\"],[0,[[2,\"hit\",\"=\",[6,\"d.f32\",[[5,\"1\"]]]],[17]]]],[11,[1,\"rayT\",\">\",\"maxDistF\"],[0,[[17]]]],[2,\"rayT\",\"=\",[1,\"rayT\",\"+\",[6,\"std.min\",[\"dd\",\"STEP_CAP\"]]]]]]],[13,\"hitPos\",[6,[7,\"ro\",\"add\"],[[6,[7,\"rd\",\"mul\"],[\"rayT\"]]]]],[13,\"hh\",[5,\"0.001\"]],[13,\"hn\",[1,\"hh\",\"*\",[4,\"-\",[5,\"1\"]]]],[13,\"k0\",[6,\"sdf\",[[6,[7,\"hitPos\",\"add\"],[[6,\"d.vec3f\",[\"hh\",\"hn\",\"hn\"]]]],\"rp0\",\"rp1\",\"rp2\",\"t\"]]],[13,\"k1\",[6,\"sdf\",[[6,[7,\"hitPos\",\"add\"],[[6,\"d.vec3f\",[\"hn\",\"hn\",\"hh\"]]]],\"rp0\",\"rp1\",\"rp2\",\"t\"]]],[13,\"k2\",[6,\"sdf\",[[6,[7,\"hitPos\",\"add\"],[[6,\"d.vec3f\",[\"hn\",\"hh\",\"hn\"]]]],\"rp0\",\"rp1\",\"rp2\",\"t\"]]],[13,\"k3\",[6,\"sdf\",[[6,[7,\"hitPos\",\"add\"],[[6,\"d.vec3f\",[\"hh\",\"hh\",\"hh\"]]]],\"rp0\",\"rp1\",\"rp2\",\"t\"]]],[13,\"normal\",[6,\"std.normalize\",[[6,[7,[6,[7,[6,[7,[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[4,\"-\",[5,\"1\"]],[4,\"-\",[5,\"1\"]]]],\"mul\"],[\"k0\"]],\"add\"],[[6,[7,[6,\"d.vec3f\",[[4,\"-\",[5,\"1\"]],[4,\"-\",[5,\"1\"]],[5,\"1\"]]],\"mul\"],[\"k1\"]]]],\"add\"],[[6,[7,[6,\"d.vec3f\",[[4,\"-\",[5,\"1\"]],[5,\"1\"],[4,\"-\",[5,\"1\"]]]],\"mul\"],[\"k2\"]]]],\"add\"],[[6,[7,[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]],\"mul\"],[\"k3\"]]]]]]],[10,[6,\"Distortion3DTrace\",[[104,{hitPos:\"hitPos\",normal:\"normal\",rd:\"rd\",hit:\"hit\"}]]]]]]},externals:{\"d.vec3f\":()=>A,\"std.normalize\":()=>vs,\"d.f32\":()=>n,MAX_DIST:()=>a,STEPS:()=>s,sdf:()=>e,HIT_EPS:()=>o,\"std.min\":()=>ye,STEP_CAP:()=>r,Distortion3DTrace:()=>qg}})&&i.f)({}))}var{applyUVMode3d:Cde,distortion3dLighting:Mde,buildParamThreadedTrace:Ede}=wde,Wg=bt,D2=Math.PI*2/10,kde=JSON.stringify({type:\"ribbon\",angle:0,twist:50,width:40,thickness:20,seed:0}),bO={sphere:{sdf:TA,surfaceUV:SA,uvHint:\"sphereSurfaceUV\",steps:64,stepCap:1.5,hitEps:.001},torus:{sdf:PA,surfaceUV:CA,uvHint:\"torusSurfaceUV\",steps:96,stepCap:.5,hitEps:.001},box:{sdf:MA,surfaceUV:EA,uvHint:\"boxFaceUV\",steps:96,stepCap:.5,hitEps:.001},capsule:{sdf:kA,surfaceUV:BA,uvHint:\"capsuleSurfaceUV\",steps:64,stepCap:1.5,hitEps:.001},mobius:{sdf:zA,surfaceUV:RA,uvHint:\"mobiusSurfaceUV\",steps:96,stepCap:.3,hitEps:.001},ribbon:{sdf:DA,surfaceUV:LA,uvHint:\"twistedRibbonSurfaceUV\",steps:128,stepCap:.08,hitEps:.002}},Bde={torus:{rotX:-90,spinY:.05},box:{rotX:15,spinY:.1},sphere:{spinY:.1},capsule:{spinY:.1},mobius:{rotX:-30,spinY:.05}},xO=H({name:\"Form3D\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Distortions\",description:\"Wraps child content onto a 3D raymarched shape with lighting.\",requiresRTT:!0,requiresChild:!0,animatedTime:{speed:\"speed\"},extraFields:UA(\"_f3p\",[[1,0,1,0],[1,0,.35,.1],[.3,.02,0,0]]),props:{shape3d:{default:kde,description:\"3D shape and its parameters\",ui:{type:\"shape3d\",label:\"Shape\",group:\"Shape\"}},shape3dType:{default:\"ribbon\",compileTime:!0,description:\"Active shape type \\u2014 triggers recompile when shape is switched\"},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape on screen\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},zoom:{default:50,description:\"Camera zoom level\",ui:{type:[\"range\",\"map\"],min:10,max:200,step:1,label:\"Zoom\",group:\"Scene\"}},glossiness:{default:50,description:\"Specular highlight intensity and sharpness\",ui:{type:[\"range\",\"map\"],min:0,max:200,step:1,label:\"Glossiness\",group:\"Lighting\"}},lighting:{default:50,description:\"Overall intensity of lighting effects\",ui:{type:[\"range\",\"map\"],min:0,max:200,step:1,label:\"Lighting\",group:\"Lighting\"}},uvMode:{default:\"stretch\",transform:Fr,description:\"How to handle UV coordinates at shape boundaries\",ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"UV Edges\",group:\"Scene\"}},speed:{default:1,description:\"Animation speed \\u2014 scales all spin rates\",ui:{type:\"range\",min:-10,max:10,step:.1,label:\"Speed\",group:\"Animation\"}}},gpu:{fragment:e=>{let{childNode:t,convertToTexture:s,uniforms:r,propValues:o,getCpuValue:a,onBeforeRender:i,ctx:l}=e;if(!t)return xt;let c=s(t),d=o.shape3dType||\"ribbon\",u=bO[d]??bO.ribbon,p=0,_=0,f=0,h=\"\",g=(()=>{try{return JSON.parse(a(\"shape3d\"))}catch{return{}}})(),y=(O,F)=>typeof g[O]==\"number\"?g[O]:F,b=O=>{let F=a(\"shape3d\");if(typeof F==\"string\"){if(F!==h){h=F;try{g=JSON.parse(F)}catch{}}}else F&&typeof F==\"object\"&&(g=F);let q=a(\"speed\")??1;if(d===\"ribbon\"){let le=y(\"angle\",0)*Wg,se=y(\"twist\",50)*.03,Te=y(\"width\",40)*.012,de=Te*.5,xe=y(\"thickness\",20)*.001+.01,Ae=y(\"seed\",0),De=Math.max(de*se,1);e.setExtraField(\"_f3p0\",[le,se,de,Te]),e.setExtraField(\"_f3p1\",[xe,Ae,De,0]),e.setExtraField(\"_f3p2\",[0,0,0,0]);return}let Y=Bde[d]??{};p+=O*q*y(\"spinX\",Y.spinX??0)*D2,_+=O*q*y(\"spinY\",Y.spinY??0)*D2,f+=O*q*y(\"spinZ\",Y.spinZ??0)*D2;let V=y(\"rotX\",Y.rotX??0)*Wg+p,X=y(\"rotY\",Y.rotY??0)*Wg+_,Q=y(\"rotZ\",Y.rotZ??0)*Wg+f,te=[Math.cos(V),Math.sin(V),Math.cos(X),Math.sin(X)],J=Math.cos(Q),oe=Math.sin(Q);d===\"sphere\"?(e.setExtraField(\"_f3p0\",te),e.setExtraField(\"_f3p1\",[J,oe,y(\"radius\",60)*.008+.1,0]),e.setExtraField(\"_f3p2\",[0,0,0,0])):d===\"torus\"?(e.setExtraField(\"_f3p0\",te),e.setExtraField(\"_f3p1\",[J,oe,y(\"outerRadius\",60)*.008+.1,y(\"tubeRadius\",25)*.006+.05]),e.setExtraField(\"_f3p2\",[0,0,0,0])):d===\"box\"?(e.setExtraField(\"_f3p0\",te),e.setExtraField(\"_f3p1\",[J,oe,y(\"sizeX\",50)*.006,y(\"sizeY\",50)*.006]),e.setExtraField(\"_f3p2\",[y(\"sizeZ\",50)*.006,y(\"rounding\",20)*.001,0,0])):d===\"capsule\"?(e.setExtraField(\"_f3p0\",te),e.setExtraField(\"_f3p1\",[J,oe,y(\"radius\",25)*.006+.05,y(\"height\",80)*.005]),e.setExtraField(\"_f3p2\",[0,0,0,0])):(e.setExtraField(\"_f3p0\",te),e.setExtraField(\"_f3p1\",[J,oe,y(\"ringRadius\",60)*.008+.1,y(\"halfWidth\",30)*.005]),e.setExtraField(\"_f3p2\",[y(\"thickness\",8)*.001+.005,0,0,0]))};b(0),i(O=>b(O?.deltaTime??0));let x=wt(e),P=W(Ede(u.sdf,{steps:u.steps,stepCap:u.stepCap,hitEps:u.hitEps}),`form3dTrace_${d}`,[l.uv,l.aspect,r.center,r.zoom,r._f3p0,r._f3p1,r._f3p2,x]),C=P.member(\"hitPos\"),E=P.member(\"normal\"),R=P.member(\"rd\"),U=P.member(\"hit\"),L=W(Cde,\"applyUVMode3d\",[W(u.surfaceUV,u.uvHint,[C,E,r._f3p0,r._f3p1,r._f3p2,x]),r.uvMode]),k=L.member(\"xy\"),D=L.member(\"z\");return W(Mde,\"distortion3dLighting\",[W(Yt,\"unpremultiplyAlpha\",[c.sample(k)]),E,R,U,D,r.glossiness.mul(.005),r.lighting.mul(.005)])}}});var zde=ko({applyThinFilmEffect:()=>Rde,thinFilmCompose:()=>PO,thinFilmCustomParams:()=>SO,thinFilmRainbow:()=>TO,thinFilmShade:()=>wO}),L2=.017453292519943295,AO=6.283185307179586,wO=w.fn([n,n,n,n,n,n,n,n,n,n,n,n],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o,a,i,l,c,d,u,p,_)=>{\"use gpu\";let f=__tsover_div(t,s),h=B(__tsover_mul(a,.5),.001),g=B(Ue(__tsover_add(__tsover_mul(r,r),__tsover_mul(o,o))),1e-4),y=__tsover_div(r,g),b=__tsover_div(o,g),x=G(__tsover_mul(__tsover_div(__tsover_mul(f,-1),h),32),0,1),P=B(__tsover_mul(i,.1),.001),C=G(__tsover_div(__tsover_mul(f,-1),P),0,1),E=__tsover_div(1,Ue(__tsover_add(__tsover_mul(g,g),1))),R=K(7,1.5,G(i,0,1)),U=__tsover_mul(vt(__tsover_sub(1,E),R),x),L=N(__tsover_mul(__tsover_mul(__tsover_sub(1,C),__tsover_sub(1,C)),x),U,_>.5),k=pe(__tsover_mul(u,L2)),D=ae(__tsover_mul(u,L2)),O=__tsover_add(__tsover_mul(y,k),__tsover_mul(b,D)),F=__tsover_sub(__tsover_mul(b,k),__tsover_mul(y,D)),q=__tsover_mul(p,AO),Y=__tsover_add(__tsover_mul(O,pe(q)),__tsover_mul(F,ae(q))),V=__tsover_mul(__tsover_mul(l,B(__tsover_mul(f,-1),0)),10),X=N(__tsover_mul(__tsover_mul(l,C),2),V,_>.5);return m(__tsover_add(__tsover_add(d,__tsover_mul(c,Y)),X),L)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"sdfRaw\"},{type:\"i\",name:\"scale\"},{type:\"i\",name:\"gradX\"},{type:\"i\",name:\"gradY\"},{type:\"i\",name:\"edgeSoftness\"},{type:\"i\",name:\"rimWidthUniform\"},{type:\"i\",name:\"thickness\"},{type:\"i\",name:\"dispersion\"},{type:\"i\",name:\"hueShift\"},{type:\"i\",name:\"lightAngle\"},{type:\"i\",name:\"animTime\"},{type:\"i\",name:\"volumetric\"}],body:[0,[[13,\"sdf\",[1,\"sdfRaw\",\"/\",\"scale\"]],[13,\"sharp\",[6,\"std.max\",[[1,\"edgeSoftness\",\"*\",[5,\"0.5\"]],[5,\"0.001\"]]]],[13,\"gLen\",[6,\"std.max\",[[6,\"std.sqrt\",[[1,[1,\"gradX\",\"*\",\"gradX\"],\"+\",[1,\"gradY\",\"*\",\"gradY\"]]]],[5,\"0.0001\"]]]],[13,\"nx\",[1,\"gradX\",\"/\",\"gLen\"]],[13,\"ny\",[1,\"gradY\",\"/\",\"gLen\"]],[13,\"rb1\",[6,\"std.clamp\",[[1,[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"sharp\"],\"*\",[5,\"32\"]],[5,\"0\"],[5,\"1\"]]]],[13,\"rimWidth\",[6,\"std.max\",[[1,\"rimWidthUniform\",\"*\",[5,\"0.1\"]],[5,\"0.001\"]]]],[13,\"depth\",[6,\"std.clamp\",[[1,[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],\"/\",\"rimWidth\"],[5,\"0\"],[5,\"1\"]]]],[13,\"cosView\",[1,[5,\"1\"],\"/\",[6,\"std.sqrt\",[[1,[1,\"gLen\",\"*\",\"gLen\"],\"+\",[5,\"1\"]]]]]],[13,\"exponent\",[6,\"std.mix\",[[5,\"7\"],[5,\"1.5\"],[6,\"std.clamp\",[\"rimWidthUniform\",[5,\"0\"],[5,\"1\"]]]]]],[13,\"volRim\",[1,[6,\"std.pow\",[[1,[5,\"1\"],\"-\",\"cosView\"],\"exponent\"]],\"*\",\"rb1\"]],[13,\"flatRim\",[1,[1,[1,[5,\"1\"],\"-\",\"depth\"],\"*\",[1,[5,\"1\"],\"-\",\"depth\"]],\"*\",\"rb1\"]],[13,\"rim\",[6,\"std.select\",[\"flatRim\",\"volRim\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"lx\",[6,\"std.cos\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"ly\",[6,\"std.sin\",[[1,\"lightAngle\",\"*\",\"DEG_TO_RAD\"]]]],[13,\"baseCos\",[1,[1,\"nx\",\"*\",\"lx\"],\"+\",[1,\"ny\",\"*\",\"ly\"]]],[13,\"baseSin\",[1,[1,\"ny\",\"*\",\"lx\"],\"-\",[1,\"nx\",\"*\",\"ly\"]]],[13,\"theta\",[1,\"animTime\",\"*\",\"TWO_PI\"]],[13,\"rotatedDot\",[1,[1,\"baseCos\",\"*\",[6,\"std.cos\",[\"theta\"]]],\"+\",[1,\"baseSin\",\"*\",[6,\"std.sin\",[\"theta\"]]]]],[13,\"thickVol\",[1,[1,\"thickness\",\"*\",[6,\"std.max\",[[1,\"sdf\",\"*\",[4,\"-\",[5,\"1\"]]],[5,\"0\"]]]],\"*\",[5,\"10\"]]],[13,\"thickFlat\",[1,[1,\"thickness\",\"*\",\"depth\"],\"*\",[5,\"2\"]]],[13,\"thicknessTerm\",[6,\"std.select\",[\"thickFlat\",\"thickVol\",[1,\"volumetric\",\">\",[5,\"0.5\"]]]]],[13,\"phase\",[1,[1,\"hueShift\",\"+\",[1,\"dispersion\",\"*\",\"rotatedDot\"]],\"+\",\"thicknessTerm\"]],[10,[6,\"d.vec2f\",[\"phase\",\"rim\"]]]]]},externals:{\"std.max\":()=>B,\"std.sqrt\":()=>Ue,\"std.clamp\":()=>G,\"std.mix\":()=>K,\"std.pow\":()=>vt,\"std.select\":()=>N,\"std.cos\":()=>pe,DEG_TO_RAD:()=>L2,\"std.sin\":()=>ae,TWO_PI:()=>AO,\"d.vec2f\":()=>m}})&&e.f)({})),TO=w.fn([n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return A_(t,0,.3333,.6667)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[10,[6,\"cosinePalette\",[\"phase\",[5,\"0\"],[5,\"0.3333\"],[5,\"0.6667\"]]]]]]},externals:{cosinePalette:()=>A_}})&&e.f)({})),SO=w.fn([n],A)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";let s=Re(t),r=__tsover_mul(s,3),o=Re(r),a=ie(r);return A(o,N(n(0),n(1),a<=1.5),N(n(0),n(1),a<=.5))}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"phase\"}],body:[0,[[13,\"t\",[6,\"std.fract\",[\"phase\"]]],[13,\"t3\",[1,\"t\",\"*\",[5,\"3\"]]],[13,\"si\",[6,\"std.fract\",[\"t3\"]]],[13,\"floorT3\",[6,\"std.floor\",[\"t3\"]]],[13,\"gate12\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"floorT3\",\"<=\",[5,\"1.5\"]]]]],[13,\"gate01\",[6,\"std.select\",[[6,\"d.f32\",[[5,\"0\"]]],[6,\"d.f32\",[[5,\"1\"]]],[1,\"floorT3\",\"<=\",[5,\"0.5\"]]]]],[10,[6,\"d.vec3f\",[\"si\",\"gate12\",\"gate01\"]]]]]},externals:{\"std.fract\":()=>Re,\"std.floor\":()=>ie,\"std.select\":()=>N,\"d.f32\":()=>n,\"d.vec3f\":()=>A}})&&e.f)({})),PO=w.fn([A,n,n,n],S)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s,r,o)=>{\"use gpu\";let a=K(A(1,1,1),t,o).mul(s).mul(r),i=G(s,0,1);return S(a.x,a.y,a.z,i)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"baseColor\"},{type:\"i\",name:\"rim\"},{type:\"i\",name:\"intensity\"},{type:\"i\",name:\"saturation\"}],body:[0,[[13,\"white\",[6,\"d.vec3f\",[[5,\"1\"],[5,\"1\"],[5,\"1\"]]]],[13,\"col\",[6,\"std.mix\",[\"white\",\"baseColor\",\"saturation\"]]],[13,\"rgb\",[6,[7,[6,[7,\"col\",\"mul\"],[\"rim\"]],\"mul\"],[\"intensity\"]]],[13,\"alpha\",[6,\"std.clamp\",[\"rim\",[5,\"0\"],[5,\"1\"]]]],[10,[6,\"d.vec4f\",[[7,\"rgb\",\"x\"],[7,\"rgb\",\"y\"],[7,\"rgb\",\"z\"],\"alpha\"]]]]]},externals:{\"d.vec3f\":()=>A,\"std.mix\":()=>K,\"std.clamp\":()=>G,\"d.vec4f\":()=>S}})&&e.f)({}));function Rde(e,t,s){let{uniforms:r,propValues:o,ctx:a}=e,i=s?.volumetric??!1,l=s?.gradSampler??t,c=$e(i?1:0),d=e.props.member(\"_animTime\"),u=W(xc,\"sdfSpaceUV\",[r.center,r.scale,r.rotation,a.uv,a.aspect]),p=t(u),_=p.member(\"r\"),f,h;if(s?.bakedGradients)f=p.member(\"g\"),h=p.member(\"b\");else{let P=l(W(da,\"offsetUV\",[u,$e(.01),$e(0)])),C=l(W(da,\"offsetUV\",[u,$e(0),$e(.01)])),E=W(Jh,\"glassGradient\",[_,P.member(\"r\"),C.member(\"r\")]);f=E.member(\"x\"),h=E.member(\"y\")}let g=W(wO,\"thinFilmShade\",[_,r.scale,f,h,r.edgeSoftness,r.rimWidth,r.thickness,r.dispersion,r.hueShift,r.lightAngle,d,c]),y=g.member(\"x\"),b=g.member(\"y\"),x;if(o.mode===\"custom\"){let P=No[typeof o.colorSpace==\"number\"?o.colorSpace:0]??Ar,C=W(SO,\"thinFilmCustomParams\",[y]),E=C.member(\"x\"),R=C.member(\"y\"),U=C.member(\"z\"),L=W(P,\"mixColors\",[r.colorA,r.colorB,E]),k=W(P,\"mixColors\",[r.colorB,r.colorC,E]);x=ea(ea(W(P,\"mixColors\",[r.colorC,r.colorA,E]),k,R),L,U).member(\"rgb\")}else x=W(TO,\"thinFilmRainbow\",[y]);return W(PO,\"thinFilmCompose\",[x,b,r.intensity,r.saturation])}var{applyThinFilmEffect:Dde}=zde,Lde=JSON.stringify({type:\"sphere3D\",radius:.35}),CO=H({name:\"ThinFilm\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Iridescent thin-film edge\",animatedTime:{speed:\"speed\"},...Ts({chord:\"firstLobe\",gradSampler:\"same\",bakedGradients:\"volumetric\",stencil:\"none\",surface:(e,t)=>Dde(t,e.sampler,{volumetric:e.volumetric,bakedGradients:e.bakedGradients})}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the shape (1 = default size)\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},intensity:{default:1,description:\"Rim brightness \\u2014 values above 1 push the edge past white for HDR bloom\",ui:{type:[\"range\",\"map\"],min:0,max:4,step:.01,label:\"Intensity\",group:\"Film\"}},rimWidth:{default:1,description:\"How far inward from the edge the iridescence reaches\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Rim Width\",group:\"Film\"}},edgeSoftness:{default:.3,description:\"Softness of the shape boundary\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Film\"}},thickness:{default:.5,description:\"Film thickness \\u2014 higher values pack more spectral bands across the rim\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Thickness\",group:\"Film\"}},dispersion:{default:.5,description:\"How strongly color separates around the perimeter (warm vs cool sides)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Dispersion\",group:\"Film\"}},saturation:{default:1,description:\"Spectral saturation \\u2014 0 = white rim, 1 = full iridescence\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Saturation\",group:\"Film\"}},hueShift:{default:0,description:\"Rotate the spectrum to reposition the colors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Hue Shift\",group:\"Film\"}},lightAngle:{default:300,description:\"Light angle in degrees \\u2014 sets which side of the rim runs warm vs cool\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Film\"}},mode:{default:\"rainbow\",compileTime:!0,description:\"Rainbow uses the full iridescent spectrum; Custom cycles through your three chosen colors\",ui:{type:\"select\",options:[{label:\"Rainbow\",value:\"rainbow\"},{label:\"Custom\",value:\"custom\"}],label:\"Mode\",group:\"Colors\"}},colorA:{default:\"#2b6fff\",transform:ce,description:\"First color in the rim cycle\",ui:{type:\"color\",label:\"Color 1\",group:\"Colors\",condition:{mode:\"custom\"}}},colorB:{default:\"#ffffff\",transform:ce,description:\"Second color in the rim cycle\",ui:{type:\"color\",label:\"Color 2\",group:\"Colors\",condition:{mode:\"custom\"}}},colorC:{default:\"#ff7a21\",transform:ce,description:\"Third color in the rim cycle\",ui:{type:\"color\",label:\"Color 3\",group:\"Colors\",condition:{mode:\"custom\"}}},colorSpace:{default:\"oklch\",transform:at,compileTime:!0,description:\"color space used to blend between custom colors\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\",condition:{mode:\"custom\"}}},speed:{default:.1,description:\"Speed at which the colors rotate around the rim (0 = static)\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Speed\",group:\"Animation\"}},shape:{default:Lde,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file \\u2014 when non-empty, activates SVG mode and triggers a shader recompile\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type \\u2014 triggers recompile when shape is switched. When empty, derived from shape JSON at mount time.\"}}});var MO=H({name:\"AngularBlur\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Radial motion blur rotating around a center point\",props:{intensity:{default:20,description:\"Intensity of the angular blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Blur Intensity\",group:\"Effect\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the rotation\",ui:{type:\"position\",label:\"Center Position\",group:\"Position\"}},dispersion:{default:0,description:\"Spectral color fringing along the streak, like light through a lens; negative flips which end is red\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Dispersion\",group:\"Lens\"}},falloff:{default:0,description:\"Concentrates the dispersion away from the center; 0 fringes everywhere equally\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Lens\"}},focus:{default:0,description:\"Radius of a clean zone around the center that stays sharp\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Focus\",group:\"Lens\"}},bias:{default:0,description:\"Trails the sweep off one side of each pixel instead of both; the sign picks the direction\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Bias\",group:\"Streak\"}},highlights:{default:0,description:\"Lets bright pixels streak further than dark ones, like light rays\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlights\",group:\"Streak\"}},jitter:{default:0,description:\"Per-pixel noise on the tap spacing that hides banding in long streaks\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Jitter\",group:\"Streak\"}}},effect:Tp({path:wp.orbit(T(\"center\")),amount:T(\"intensity\"),detail:{dispersion:T(\"dispersion\"),falloff:T(\"falloff\"),focus:T(\"focus\"),bias:T(\"bias\"),highlights:T(\"highlights\"),jitter:T(\"jitter\")}})});var EO=H({role:\"shape\",name:\"Arc\",description:\"Pie sector (arc wedge) with adjustable radius and aperture angle\",shape:{distance:({x:e,y:t},s)=>yR(e,t,s(\"radius\"),v(s(\"aperture\"),bR)),colorDescription:\"Fill color of the arc\",centerDescription:\"Center position of the arc\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.38,description:\"Radius of the pie sector\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Size\",group:\"Shape\"}},aperture:{default:270,description:\"Aperture angle of the wedge in degrees (360 = full circle)\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Aperture\",group:\"Shape\"}}}}});var kc=2048,kO={\"JetBrains Mono\":\"JetBrains+Mono\",\"Fira Code\":\"Fira+Code\",\"Source Code Pro\":\"Source+Code+Pro\",\"IBM Plex Mono\":\"IBM+Plex+Mono\",\"Space Mono\":\"Space+Mono\",\"Roboto Mono\":\"Roboto+Mono\",\"Courier Prime\":\"Courier+Prime\",\"Geist Mono\":\"Geist+Mono\",VT323:\"VT323\",\"Press Start 2P\":\"Press+Start+2P\",Silkscreen:\"Silkscreen\",\"Major Mono Display\":\"Major+Mono+Display\",\"Syne Mono\":\"Syne+Mono\",\"Nova Mono\":\"Nova+Mono\",\"Xanh Mono\":\"Xanh+Mono\",\"Cutive Mono\":\"Cutive+Mono\",\"Share Tech Mono\":\"Share+Tech+Mono\",\"Martian Mono\":\"Martian+Mono\",\"Azeret Mono\":\"Azeret+Mono\"},BO=new Set;async function Ude(e){if(BO.has(e)||!kO[e])return;let t=kO[e];if(!document.querySelector(`link[href*=\"${t}\"]`)){let s=document.createElement(\"link\");s.rel=\"stylesheet\",s.href=`https://fonts.googleapis.com/css2?family=${t}:wght@400&display=swap`,document.head.appendChild(s)}try{if(document.fonts&&document.fonts.load){for(let s of[`400 12px \"${e}\"`,`12px \"${e}\"`,`400 12px ${e}`,`12px ${e}`])try{await document.fonts.load(s);break}catch{}await new Promise(s=>setTimeout(s,500))}else await new Promise(s=>setTimeout(s,1e3));BO.add(e)}catch{}}var Ode=(e,t)=>typeof e==\"number\"?e:t,Gde=e=>{let{getCpuValue:t,setExtraField:s}=e,r=gc(e,{label:\"Ascii:atlas\",initial:{width:kc,height:kc}}),o=w_(e,{contextAttributes:{willReadFrequently:!0}}),a=(d,u,p)=>{if(r.disposed||!d||d.length===0)return;let _=d.length,f=Math.max(2,Math.ceil(Math.sqrt(_))),h=128,g=Math.max(1,2/u),y=Math.min(h*g,kc/f),b=y*.75,x=o.canvas()?o.context():o.resize(kc,kc);if(!x)return;x.clearRect(0,0,kc,kc),x.fillStyle=\"#ffffff\",x.font=`${b}px \"${p}\", ${p}, monospace`,!x.font.includes(p)&&!x.font.includes(\"IBM\")&&!x.font.includes(\"Plex\")&&(x.font=`${b}px ${p}, monospace`),x.textAlign=\"center\",x.textBaseline=\"middle\";for(let C=0;C<_;C++){let E=Math.floor(C/f),R=C%f;x.fillText(d[C],R*y+y/2,E*y+y/2)}r.write(o.canvas());let P=f*y/kc;s(\"_charCount\",_),s(\"_atlasScale\",P),s(\"_atlasSize\",f)},i=()=>({characters:String(t(\"characters\")??Vg.props.characters.default),fontFamily:String(t(\"fontFamily\")??Vg.props.fontFamily.default),spacing:Ode(t(\"spacing\"),1.5)}),l=()=>{let d=i();a(d.characters,d.spacing,d.fontFamily)},c=S_({key:()=>i().fontFamily,load:()=>Ude(i().fontFamily),onLoaded:l});return setTimeout(()=>{l(),c.ensure()},0),T_(e,{key:()=>{let d=i();return`${d.characters}|${d.fontFamily}|${d.spacing}`},run:()=>{l(),c.ensure()}}),r.kit},U2=H8({cellSize:T(\"cellSize\"),spacing:T(\"spacing\")}),zO=j8({grid:U2}),Vg=H({name:\"Ascii\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Convert imagery to ASCII character art\",requiresRTT:!0,requiresChild:!0,extraFields:{_charCount:{schema:n,initial:9},_atlasScale:{schema:n,initial:1},_atlasSize:{schema:n,initial:3}},props:{characters:{default:\"@%#*+=-:.\",description:\"Characters ordered from dense to sparse. First character is used for bright areas, last for dark areas.\",ui:{type:\"text\",label:\"Characters\",group:\"Effect\"}},cellSize:{default:30,description:\"Size of each ASCII character cell (normalized to 1080p reference, scales proportionally at other resolutions)\",ui:{type:\"range\",min:8,max:100,step:1,label:\"Cell Size\",group:\"Effect\"}},fontFamily:{default:\"JetBrains Mono\",description:\"Font family for characters\",ui:{type:\"select\",options:[{label:\"Azeret Mono\",value:\"Azeret Mono\"},{label:\"Courier Prime\",value:\"Courier Prime\"},{label:\"Cutive Mono\",value:\"Cutive Mono\"},{label:\"Fira Code\",value:\"Fira Code\"},{label:\"Geist Mono\",value:\"Geist Mono\"},{label:\"IBM Plex Mono\",value:\"IBM Plex Mono\"},{label:\"JetBrains Mono\",value:\"JetBrains Mono\"},{label:\"Major Mono Display\",value:\"Major Mono Display\"},{label:\"Martian Mono\",value:\"Martian Mono\"},{label:\"Nova Mono\",value:\"Nova Mono\"},{label:\"Press Start 2P\",value:\"Press Start 2P\"},{label:\"Roboto Mono\",value:\"Roboto Mono\"},{label:\"Share Tech Mono\",value:\"Share Tech Mono\"},{label:\"Silkscreen\",value:\"Silkscreen\"},{label:\"Source Code Pro\",value:\"Source Code Pro\"},{label:\"Space Mono\",value:\"Space Mono\"},{label:\"Syne Mono\",value:\"Syne Mono\"},{label:\"VT323\",value:\"VT323\"},{label:\"Xanh Mono\",value:\"Xanh Mono\"}],label:\"Font Family\",group:\"Font\"}},spacing:{default:1,description:\"Character size within each cell (1.0 = optimal size, 0.0 = smallest)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Character Size\",group:\"Effect\"},transform:e=>.1+e*1.4},gamma:{default:1,description:\"Brightness curve adjustment. <1 brightens darks (more light characters), >1 darkens midtones (more dark characters). Use to better fit characters to image brightness range.\",ui:{type:[\"range\",\"map\"],min:.25,max:3,step:.01,label:\"Gamma\",group:\"Effect\"}},alphaThreshold:{default:0,description:\"Pixels with alpha below this threshold become fully transparent.\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Alpha Threshold\",group:\"Effect\"}},preserveAlpha:{default:!0,description:\"When enabled, output alpha matches input alpha. When disabled, pixels above the alpha threshold become fully opaque.\",ui:{type:\"checkbox\",label:\"Preserve Alpha\",group:\"Effect\"},transform:rs}},gpu:{fragment:Q8({grid:U2,cell:zO,glyph:K8({grid:U2,cell:zO,gamma:T(\"gamma\"),atlas:Gde}),alphaThreshold:T(\"alphaThreshold\"),preserveAlpha:T(\"preserveAlpha\")})}});var Fde=[{freq:.7,speed:.4,amount:.15,salt:{k:12.9,big:4758.5}},{freq:1.6,speed:.65,amount:.08,salt:{k:78.2,big:2847.1}},{freq:2.9,speed:.3,amount:.04,salt:{k:41.6,big:1593.7}}],Ide={k:53.7,big:3847.2},Yde=[{timeOffset:0,weight:1},{timeOffset:3.1,weight:.65},{timeOffset:6.7,weight:.45},{timeOffset:9.4,weight:.3}],RO=H({name:\"Aurora\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Mesmerizing aurora borealis with layered curtains, vertical rays, and flowing light.\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#a533f8\",transform:ce,description:\"Edge color at the curtain base\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#22ee88\",transform:ce,description:\"Core color in the bright center\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},colorC:{default:\"#1694e8\",transform:ce,description:\"Tip color at the ray ends\",ui:{type:\"color\",label:\"Color C\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},balance:{default:50,description:\"Shifts color distribution across the curtain height\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Balance\",group:\"Colors\"}},intensity:{default:80,description:\"Overall aurora brightness\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Aurora\"}},curtainCount:{default:4,compileTime:!0,description:\"Number of aurora curtain layers\",ui:{type:\"range\",min:1,max:4,step:1,label:\"Curtains\",group:\"Aurora\"}},speed:{default:5,description:\"Animation speed\",ui:{type:\"range\",min:-10,max:10,step:.1,label:\"Speed\",group:\"Aurora\"}},waviness:{default:50,description:\"How much the curtains undulate\",ui:{type:[\"range\",\"map\"],min:0,max:200,step:1,label:\"Waviness\",group:\"Aurora\"}},rayDensity:{default:20,description:\"Density of vertical ray structures\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Ray Detail\",group:\"Aurora\"}},height:{default:120,description:\"How tall the aurora extends\",ui:{type:[\"range\",\"map\"],min:10,max:200,step:1,label:\"Height\",group:\"Scene\"}},center:{default:{x:.5,y:0},transform:Xe,description:\"Center position of the aurora\",ui:{type:\"position\",label:\"Center\",group:\"Scene\"}},seed:{default:0,description:\"Random seed for variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Aurora\"}}},paint:e=>{let{uv:t,viewport:s}=aa(e),r=z(Ce(s.member(\"x\"),it(s.member(\"y\"),1e-6)),\"aspect\"),o=z(wt(e),\"auroraTime\"),a=me(T(\"seed\"),e),i=me(T(\"center\"),e),l=i.member(\"x\"),c=ne(1,i.member(\"y\")),d=v(me(T(\"waviness\"),e),.01),u=v(me(T(\"rayDensity\"),e),.01),p=v(me(T(\"height\"),e),.01),_=v(me(T(\"intensity\"),e),.018),f=et(2.5,.4,v(me(T(\"balance\"),e),.01)),h=(R,U)=>{let L=z(I(v(o,.06),U),\"tSlow\"),k=z(I(a,Math.fround(R*Math.fround(31.7))),\"lSeed\"),D=z(v(v(ne(t.member(\"x\"),l),r),2),\"wx\"),O=l2({along:D,time:L,waves:Fde.map(Te=>({...Te,phase:s2(k,Te.salt)}))}),F=z(Jn(Qe(v(D,.5),v(L,.25),v(k,.1))),\"curtainNoise\"),q=I(I(c,v(O,d)),v(v(F,.14),d)),Y=z(dD({along:I(D,v(F,2.5)),frequency:v(et(6,20,u),.15),time:L,speed:.1,phase:s2(k,Ide),sharpness:1.5}),\"curtainRays\"),V=v(p,et(.4,1,Y)),X=z(ne(t.member(\"y\"),q),\"distFromPath\"),Q=z(v(V,I(v(Y,.4),.6)),\"topExtent\"),te=Zz({distance:X,below:{from:-.04,to:.015},above:{to:Q}}),J=z(Ge(Ce(X,I(Q,.001)),0,1),\"colorT\"),oe=et(.4,1,I(v(F,.5),.5)),le=ne(J,.2),se=I(v(tr(v(v(le,le),-6)),.3),1);return{colorT:J,alpha:z(v(v(v(te,Y),oe),se),\"curtainAlpha\")}},g=Math.max(1,Math.min(4,Math.round(e.propValues.curtainCount??4))),{weightedSum:y,totalWeight:b}=ML(Yde.slice(0,g),({timeOffset:R,weight:U},L)=>{let{colorT:k,alpha:D}=h(L,R);return{value:k,weight:U===1?D:v(D,U)}}),x=z(b,\"auroraAlpha\"),P=z(ts(Ce(y,I(x,.001)),f),\"auroraAvgT\"),C=pg({a:T(\"colorA\"),b:T(\"colorB\"),c:T(\"colorC\"),space:T(\"colorSpace\")})(ot(0,.2,P),ot(.4,.85,P),e),E=z(v(x,_),\"auroraGlow\");return Ot(Ge(v(C.member(\"rgb\"),E),kt(0),kt(1)),Ge(v(E,.5),0,1))}});var DO=H({name:\"BarnDoors\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Split the content along a center line and wipe outward in both directions\",props:{progress:{default:.5,description:\"How far the doors have opened (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},angle:{default:0,description:\"Direction the doors open in degrees (0 = apart horizontally)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Transition\"}},feather:{default:.1,description:\"Softness of the wipe edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Close in from both edges instead of opening from the center\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.directional(T(\"angle\")).folded(),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Barn Doors shader.\"});var LO=H({name:\"BarShift\",role:\"warp\",category:\"Distortions\",description:\"Slices content into parallel bars, each offset independently for a fractured or glitch-like effect\",boundingBoxDeclaration:{aspectRatio:null},animatedTime:{speed:\"speed\"},props:{count:{default:6,description:\"Number of bars across the longest viewport dimension\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Count\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Angle of bar orientation in degrees (0 = vertical bars, 90 = horizontal bars)\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Angle\",group:\"Effect\"}},intensity:{default:.15,description:\"Maximum displacement per bar\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}},seed:{default:0,description:\"Randomization seed for per-bar offset variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},speed:{default:0,description:\"Animation speed \\u2014 each bar drifts at its own rate and direction\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},edges:fr(\"mirror\",\"How to handle edges when distortion pushes content out of bounds\")},map:aU({count:T(\"count\"),angle:T(\"angle\"),intensity:T(\"intensity\"),seed:T(\"seed\")}),missingChildMessage:\"You must pass a child component into the BarShift shader.\"});var UO=H({name:\"Beam\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"A beam of light from one point to another.\",acceptsUVContext:!0,extraFields:YR,props:{startPosition:{default:{x:.2,y:.5},transform:Xe,description:\"Starting point of the beam\",ui:{type:\"position\",label:\"Start Position\",group:\"Position\"}},endPosition:{default:{x:.8,y:.5},transform:Xe,description:\"Ending point of the beam\",ui:{type:\"position\",label:\"End Position\",group:\"Position\"}},startThickness:{default:.2,description:\"Thickness at the start of the beam\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Start Thickness\",group:\"Effect\"}},endThickness:{default:.2,description:\"Thickness at the end of the beam\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"End Thickness\",group:\"Effect\"}},startSoftness:{default:.5,description:\"Edge softness at the start of the beam\",ui:{type:[\"range\",\"map\"],min:0,max:50,step:.1,label:\"Start Softness\",group:\"Effect\"}},endSoftness:{default:.5,description:\"Edge softness at the end of the beam\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"End Softness\",group:\"Effect\"}},insideColor:{default:\"#FF0000\",transform:ce,description:\"Color at the center of the beam\",ui:{type:\"color\",label:\"Inside Color\",group:\"Colors\"}},outsideColor:{default:\"#0000FF\",transform:ce,description:\"Color at the edges of the beam\",ui:{type:\"color\",label:\"Outside Color\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:WR({from:T(\"startPosition\"),to:T(\"endPosition\"),thickness:{start:T(\"startThickness\"),end:T(\"endThickness\")},softness:{start:T(\"startSoftness\"),end:T(\"endSoftness\")},colors:{inside:T(\"insideColor\"),outside:T(\"outsideColor\")},space:T(\"colorSpace\")})});var OO=H({name:\"Bend\",role:\"warp\",category:\"Distortions\",description:\"Bends the ends of the frame toward you like a curved display \\u2014 content at the edges swells closer under real perspective, or curls away when the strength goes negative\",boundingBoxDeclaration:{aspectRatio:null},props:{strength:{default:.5,description:\"How hard the ends bend \\u2014 positive curls them toward you (edges magnify), negative curls them away (edges recede into the frame)\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},falloff:{default:0,description:\"Concentrates the bend toward the ends \\u2014 0 curves the whole frame, higher keeps the middle flat and bends only near the edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Effect\"}},angle:{default:0,description:\"Direction of the bend axis in degrees \\u2014 0 bends the left/right ends, 90 bends the top/bottom\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},edges:fr(\"transparent\",\"How to handle edges when the bend pulls content away from the frame border\")},map:$L({strength:T(\"strength\"),falloff:T(\"falloff\"),angle:T(\"angle\")}),missingChildMessage:\"You must pass a child component into the Bend shader.\"});var Wde=[{amount:.15,schedule:[{waves:[{x:3.2,t:.8},{y:2.8,t:.6}]},{waves:[{x:4.8,y:-3.6,t:-.4}]}]},{amount:.12,schedule:[{waves:[{x:5.6,t:-.5},{y:4.4,t:.7}]}]},{amount:.1,schedule:[{waves:[{x:7.2,y:6.4,t:.3}]},{waves:[{x:2.4,t:-.9}]}]},{amount:.04,schedule:[{waves:[{x:8.8,t:.2},{y:7.6,t:-.8}]}]}],qde=[{waves:[{x:1,t:.4},{y:1,t:.3}]},{waves:[{x:.7,y:.8,t:.2}]}],Vde=[{waves:[{x:1.3,t:-.5},{y:1.1,t:.6}]},{waves:[{x:.5,y:-.6,t:.3}]}],GO=H({name:\"Blob\",role:\"generator\",category:\"Textures\",description:\"Organic animated blob with 3D lighting and gradients\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},extraFields:{normLx:{schema:n,initial:0},normLy:{schema:n,initial:0},normLz:{schema:n,initial:1}},boundingBoxDeclaration:{propBindings:{x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"}},computeBounds(e,t,s){let r=e.size??.5,o=typeof e.center?.x==\"number\"?e.center.x:.5,a=typeof e.center?.y==\"number\"?e.center.y:.5,i=r*2*s;return{centerXPx:o*t,centerYPx:a*s,widthPx:i,heightPx:i,rotationDeg:0}},writeBounds(e,t,s,r){let o=Math.max(0,e.widthPx/(2*r));return{center:{...t.center??{x:.5,y:.5},x:e.centerXPx/s,y:e.centerYPx/r},size:o}},softnessBinding:{prop:\"softness\",toPx:(e,t,s)=>e*.3*s,fromPx:(e,t,s)=>Math.max(0,Math.min(1,e/(.3*s)))}},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},colorA:{default:\"#ff6b35\",transform:ce,description:\"Primary color of the blob\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#e91e63\",transform:ce,description:\"Secondary color of the blob\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),size:{default:.5,description:\"Size of the blob\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Size\",group:\"Effect\"}},deformation:{default:.5,description:\"How organic and blobby the shape is (0 = circle, 1 = very blobby)\",ui:{type:\"range\",min:0,max:1,step:.1,label:\"Deformation\",group:\"Effect\"}},softness:{default:.5,description:\"Softness of the blob edges (combines edge width and transition curve)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Softness\",group:\"Effect\"}},highlightIntensity:{default:.5,description:\"Intensity of specular highlight effect\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Highlight Intensity\",group:\"Highlight\"}},highlightX:{default:.3,description:\"Light direction X component\",ui:{type:\"range\",min:-1,max:1,step:.1,label:\"Highlight X\",group:\"Highlight\"}},highlightY:{default:-.3,description:\"Light direction Y component\",ui:{type:\"range\",min:-1,max:1,step:.1,label:\"Highlight Y\",group:\"Highlight\"}},highlightZ:{default:.4,description:\"Light direction Z component\",ui:{type:\"range\",min:-1,max:1,step:.1,label:\"Highlight Z\",group:\"Highlight\"}},highlightColor:{default:\"#ffe11a\",transform:ce,description:\"Color of the specular highlight\",ui:{type:\"color\",label:\"Highlight Color\",group:\"Highlight\"}},speed:{default:.5,description:\"Animation speed\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:1,description:\"Adjusts the starting state, useful for variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the blob\",ui:{type:\"position\",label:\"Center Position\",group:\"Position\",units:[\"%\",\"px\"]}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:e=>{let{uniforms:t}=e,s=b=>fn(b,e);aR(e,{x:\"highlightX\",y:\"highlightY\",z:\"highlightZ\"},{x:\"normLx\",y:\"normLy\",z:\"normLz\"});let r=z(wt(e,\"seed\"),\"blobTime\"),{delta:o,dist:a}=qL({center:T(\"center\")})(e,WL(e)),i={x:o.member(\"x\"),y:o.member(\"y\"),t:r},l=s(T(\"deformation\")),c=z(I(s(T(\"size\")),Wde.map(({schedule:b,amount:x})=>v(v(vp(i,b),x),l)).reduce((b,x)=>I(b,x))),\"blobRadius\"),d=z(s(T(\"softness\")),\"blobSoftness\"),u=z(Jz({dist:a,radius:c,softness:{width:v(d,.3),curve:I(v(d,2),.5)}}),\"blobMask\"),p=v(v(v(pD({normal:uD({delta:o,dist:a,radius:c}),light:Qe(t.normLx,t.normLy,t.normLz),gloss:32}),I(v(ot(0,c,a),.5),.5)),s(T(\"highlightIntensity\"))),u),_={x:v(i.x,3),y:v(i.y,3),t:r},f=b=>I(v(vp(_,b),.5),.5),h=Us(T(\"colorSpace\"))(ot(.1,.9,I(v(f(qde),.6),v(f(Vde),.4))),e),g=me(T(\"highlightColor\"),e),y=v(I(t.colorA.member(\"a\"),t.colorB.member(\"a\")),.5);return Ot(I(h.member(\"rgb\"),v(g.member(\"rgb\"),p)),v(y,u))}});var FO=H({name:\"BlockDissolve\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Dissolve the content away as a grid of blocks vanishing in random order\",props:{progress:{default:.5,description:\"How far the dissolve has progressed (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},blockSize:{default:.08,description:\"Size of each block as a fraction of the frame width\",ui:{type:[\"range\",\"map\"],min:.01,max:.5,step:.01,label:\"Block Size\",group:\"Transition\"}},softness:{default:.15,description:\"How softly each block fades out (0 = hard-edged blocks)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse the order blocks dissolve in\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.cells(T(\"blockSize\")).shuffled(),progress:T(\"progress\"),feather:T(\"softness\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Block Dissolve shader.\"});function ma(){return{colorA:{default:\"#ffffff\",description:\"First color\",transform:ce,ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#000000\",description:\"Second color\",transform:ce,ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}}}function IO(){return{colorA:{default:\"#ffffff\",description:\"First color\",transform:ce,ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#000000\",description:\"Second color\",transform:ce,ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}}}}function Ja(){return{contrast:{default:0,description:\"Pattern contrast (higher = sharper transitions)\",ui:{type:[\"range\",\"map\"],min:-1,max:5,step:.1,label:\"Contrast\",group:\"Effect\"}},balance:{default:0,description:\"Balance between colors (negative = more colorB, positive = more colorA)\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.05,label:\"Balance\",group:\"Effect\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}}}}function In(e=1){return{speed:{default:e,description:\"Animation speed\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}}}}var YO=H({name:\"BlockNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Blocky value noise with soft cells that morph over time\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:2,description:\"Pattern scale (higher = larger patterns)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},...Ja(),...In()},paint:Vs(ua(Qa(hp(Tc(\"value\"),{rate:.3}),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var WO=H({name:\"BlueNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"High-frequency blue noise \\u2014 even, grainy speckle ideal for dithering\",acceptsUVContext:!0,props:{...ma(),grain:{default:1,description:\"Grain size in pixels (1 = per-pixel, higher = chunkier speckle)\",ui:{type:\"range\",min:1,max:32,step:1,label:\"Grain Size\",group:\"Effect\"}},...Ja()},paint:Vs(ua(qD($D(),{grain:T(\"grain\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var qO=H({name:\"Blur\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"A simple Gaussian blur effect\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:50,description:\"Intensity of the blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:200,step:1,label:\"Intensity\",group:\"Effect\"}}},...kU({intensity:T(\"intensity\")})});var H_=4096,Nde=1200,Cl=vn({desktop:1024,mobile:640}),VO=\"rgba16float\",$de=16,Xde=.13,Hde=3.5,jde=1.5,Kde=.4545,Qde=1.3,NO=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({colA:S,colB:S,count:n,trails:n,dt:n,aspect:n,domainX:n,maxSpeed:n,maxForce:n,perceptionSq:n,sepRadiusSq:n,sepW:n,aliW:n,cohW:n,cursorX:n,cursorY:n,cursorMode:n,cursorRadius:n,cursorRadiusSq:n,cursorForce:n,bodyR:n,margin:n,turnForce:n,seed:n}),\"SimParams\"),Gd=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({agents:{storage:Fe(S,H_),access:\"mutable\"},agit:{storage:Fe(n,H_),access:\"mutable\"},accumE:{storage:Fe(lo(M),Cl*Cl),access:\"mutable\"},accumS:{storage:Fe(lo(M),Cl*Cl),access:\"mutable\"},trailBuf:{storage:Fe(S,Cl*Cl),access:\"mutable\"},params:{uniform:NO},outTex:{storageTexture:zt(VO,\"write-only\")}}),\"simLayout\"),Zde=pz(Gd,\"boidsInit\"),Jde=On.steering2d(Gd,{forces:[Za.flocking(Gd,{maxAgents:H_}),Za.cursorSteer(Gd),Za.wallTurn(Gd)],agitation:{rest:jde,gain:Kde,cool:Qde},cruiseFloor:.35,wallRestitution:.6,name:\"boidsUpdate\"});function O2(e,t){return Gn.orientedWorld(Gd,{shape:e,res:Cl,splatRCap:$de,heading:\"velocity\",agitation:\"buffer\",comet:!0,ramp:{colorSpace:t,trails:\"on\"},names:{splat:\"boidsSplat\",resolve:\"boidsResolve\"}})}var eue=e=>O2(e,2).splat.kernel,tue=e=>O2(\"arrow\",e).resolve.kernel,o0e=eue(\"arrow\"),a0e=tue(2),$O=H({name:\"Boids\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"A living murmuration of hundreds of flocking agents drawn as crisp arrows, streaks, dots or glowing comets \\u2014 separation, alignment and cohesion drive fluid, splitting-and-merging ribbons, agents flash toward an excited color when scattered, and the cursor acts as an attractor or a predator\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{colorA:{default:\"#8ec5ff\",transform:ce,description:\"Color of agents cruising calmly with the flock\",ui:{type:\"color\",label:\"Rest Color\",group:\"Colors\"}},colorB:{default:\"#ff7ad9\",transform:ce,description:\"Color agents flash toward when agitated \\u2014 scattering from the predator cursor or banking through a hard turn\",ui:{type:\"color\",label:\"Excited Color\",group:\"Colors\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the rest\\u2192excited color ramp\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},agentShape:{default:\"arrow\",compileTime:!0,description:\"What each agent is drawn as \\u2014 a hard-edged arrow, streak or square pointing along its heading, a plain dot, or a soft glowing comet\",ui:{type:\"select\",options:lp,label:\"Shape\",group:\"Flock\"}},count:{default:2e3,description:\"Number of flocking agents\",ui:{type:\"range\",min:100,max:4e3,step:100,label:\"Count\",group:\"Flock\"}},speed:{default:2,description:\"Overall cruising speed of the flock\",ui:{type:\"range\",min:.2,max:5,step:.1,label:\"Speed\",group:\"Flock\"}},seed:{default:0,description:\"Re-rolls the flock's starting formation \\u2014 each value spawns the murmuration in a different arrangement\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Flock\"}},size:{default:1.5,description:\"Size of each agent\",ui:{type:\"range\",min:.5,max:3,step:.05,label:\"Size\",group:\"Flock\"}},trails:{default:0,description:\"How long each agent's motion trail persists \\u2014 0 draws crisp agents, 1 leaves long ribbons\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Trails\",group:\"Flock\"}},separation:{default:1.7,description:\"How strongly agents steer away from crowding their neighbours\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Separation\",group:\"Flocking\"}},alignment:{default:1.5,description:\"How strongly agents match their neighbours' heading \\u2014 the driver of coherent murmuration ribbons\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Alignment\",group:\"Flocking\"}},cohesion:{default:1,description:\"How strongly agents steer toward the local center of the flock\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Cohesion\",group:\"Flocking\"}},perception:{default:.12,description:\"How far each agent can sense its neighbours (in screen heights) \\u2014 larger sees bigger, smoother flocks\",ui:{type:\"range\",min:.05,max:.35,step:.01,label:\"Perception\",group:\"Flocking\"}},cursorMode:{default:\"repel\",description:\"How the flock reacts to the cursor\",ui:{type:\"select\",options:[{label:\"None\",value:\"none\"},{label:\"Attract\",value:\"attract\"},{label:\"Predator\",value:\"repel\"}],label:\"Cursor\",group:\"Interaction\"}},cursorStrength:{default:1.5,description:\"Strength of the cursor's pull or push on the flock\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Cursor Strength\",group:\"Interaction\"}}},...Fn({layout:Gd,params:NO,maxAgents:H_,countCap:{desktop:H_,mobile:Nde},output:{key:\"outTex\",name:\"boidsTexture\",size:[Cl,Cl],format:VO},bake:({getCpuValue:e})=>{let t=O2(e(\"agentShape\")||\"arrow\",e(\"colorSpace\")??2);return{pipelines:{init:{kernel:Zde,threads:\"max\"},update:{kernel:Jde,threads:\"agents\"},splat:t.splat,resolve:t.resolve},initStep:\"init\",program:[\"update\",\"splat\",\"resolve\"]}},frame:(e,{getCpuValue:t})=>{let s=Li(t);return r=>{let{dt:o,aspect:a,pointerX:i,pointerY:l}=Di(r),c=Math.max(a,.01),d=e.resolveCount(s(\"count\",2e3)),u=s(\"seed\",0),p=Xde*s(\"speed\",2),_=p*Hde,f=s(\"perception\",.12),h=t(\"cursorMode\")??\"repel\",g=h===\"attract\"?1:h===\"repel\"?2:0,y=i*a,b=l,x=.22,P=t(\"colorA\"),C=t(\"colorB\"),E=Math.min(Math.max(s(\"size\",1.5),.5),3)*2/Ri;return e.writeParams({colA:S(P?.x??.56,P?.y??.77,P?.z??1,P?.w??1),colB:S(C?.x??1,C?.y??.48,C?.z??.85,C?.w??1),count:d,trails:Math.min(Math.max(s(\"trails\",.35),0),1)*.92,dt:o,aspect:a,domainX:c,maxSpeed:p,maxForce:_,perceptionSq:f*f,sepRadiusSq:f*.5*(f*.5),sepW:s(\"separation\",1.7),aliW:s(\"alignment\",1.5),cohW:s(\"cohesion\",1),cursorX:y,cursorY:b,cursorMode:g,cursorRadius:x,cursorRadiusSq:x*x,cursorForce:s(\"cursorStrength\",1.5)*p*12,bodyR:E,margin:.07,turnForce:p*2.2,seed:u}),e.frame({count:d,reseed:u})}},fragment:{output:\"boidsTexture\",fallback:\"transparent\"}})});var XO=H({name:\"BokehBlur\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Photographic lens blur where bright highlights bloom into aperture-shaped discs\",requiresRTT:!0,requiresChild:!0,props:{radius:{default:50,description:\"Defocus amount \\u2014 how far the lens blur spreads\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Radius\",group:\"Effect\"}},highlightGain:{default:4,description:\"How strongly bright highlights bloom into discs\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Highlight Bloom\",group:\"Highlights\"}},highlightThreshold:{default:.6,description:\"Brightness above which a highlight forms a disc\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlight Threshold\",group:\"Highlights\"}},bladeShape:{default:\"blades\",description:\"Aperture shape the highlights bloom into \\u2014 bladed iris or a novelty cut-out (heart, star, \\u2026)\",ui:{type:\"select\",options:[{label:\"Blades\",value:\"blades\"},{label:\"Circle\",value:\"circle\"},{label:\"Star\",value:\"star\"},{label:\"Heart\",value:\"heart\"},{label:\"Flower\",value:\"flower\"},{label:\"Cross\",value:\"cross\"},{label:\"Ring\",value:\"ring\"}],label:\"Blade Shape\",group:\"Aperture\"}},bladeCount:{default:6,description:\"Blade or point count for the Blades, Star and Flower shapes \\u2014 0\\u20132 blades is a circular iris\",ui:{type:\"range\",min:0,max:9,step:1,label:\"Aperture Blades\",group:\"Aperture\"}},bladeRotation:{default:0,transform:cs,description:\"Rotation of the aperture shape (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Blade Rotation\",group:\"Aperture\"}},chromaticFringe:{default:.2,description:\"Lens color fringing on the disc edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Chromatic Fringe\",group:\"Aperture\"}},catEye:{default:0,description:\"Clips the discs into cat-eye shapes toward the frame corners, like a real lens vignetting its aperture\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Cat Eye\",group:\"Aperture\"}}},...e8({radius:T(\"radius\"),gain:T(\"highlightGain\"),threshold:T(\"highlightThreshold\"),shape:T(\"bladeShape\"),blades:T(\"bladeCount\"),rotation:T(\"bladeRotation\"),fringe:T(\"chromaticFringe\"),catEye:T(\"catEye\")})});var HO=eD({cellsX:T(\"cellsX\"),cellsY:T(\"cellsY\"),mortar:T(\"mortar\"),softness:T(\"softness\"),variation:T(\"variation\"),rotation:T(\"rotation\"),offset:T(\"offset\"),speedVariance:T(\"speedVariance\"),seed:T(\"seed\")}),jO=H({name:\"BrickPattern\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Classic brick wall pattern with alternating rows and mortar gaps\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorBrick:{default:\"#000000\",transform:ce,description:\"Brick color\",ui:{type:\"color\",label:\"Brick Color\",group:\"Colors\"}},colorMortar:{default:\"#ffffff\",transform:ce,description:\"Mortar / gap color\",ui:{type:\"color\",label:\"Mortar Color\",group:\"Colors\"}},cellsX:{default:8,description:\"Number of bricks per row\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Bricks per Row\",group:\"Effect\"}},cellsY:{default:10,description:\"Number of brick rows\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Rows\",group:\"Effect\"}},mortar:{default:.05,description:\"Width of mortar gaps \\u2014 equal pixel thickness in both directions\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Mortar Gap\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the brick edges (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},variation:{default:0,description:\"Per-brick random lightening/darkening of the brick color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Effect\"}},rotation:{default:0,transform:cs,description:\"Rotation of the pattern in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},speed:{default:0,description:\"Animation speed\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},offset:{default:0,description:\"Static horizontal offset \\u2014 shifts the brick pattern without animating\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Offset\",group:\"Animation\"}},speedVariance:{default:0,description:\"How much each row's speed varies \\u2014 at high values rows move at different speeds and directions\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Speed Variance\",group:\"Animation\"}},seed:{default:0,description:\"Random seed for per-row speed variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:T(\"colorMortar\"),stroke:Sc(T(\"colorBrick\"),HO.shade),mask:HO.bricks,space:T(\"colorSpace\")})});var KO=H({name:\"BrightnessContrast\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Adjust brightness and contrast of the image\",props:{brightness:{default:0,description:\"Brightness adjustment (-1 to 1)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Brightness\",group:\"Adjustments\"}},contrast:{default:0,description:\"Contrast adjustment (-1 to 1)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Contrast\",group:\"Adjustments\"}}},effect:o8({brightness:T(\"brightness\"),contrast:T(\"contrast\")}),identityWhen:nR(Uo(\"brightness\"),Uo(\"contrast\")),missingChildMessage:\"You must pass a child component into the Brightness Contrast shader.\"});var sue=JSON.stringify({type:\"sphere3D\",radius:.35}),rue=.38,QO=H({name:\"BrushedMetal\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photorealistic brushed metal \\u2014 a satin anodised surface combed with fine directional grain that smears the reflected studio and key light into the long anisotropic streaks of real brushed aluminium, steel, gold or copper\",animatedTime:{speed:\"speed\"},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the metal shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the metal shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the metal shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},lightColor:{default:\"#e9ebef\",transform:ce,description:\"The bright tone of the polished grain \\u2014 recolor for gold (#ffe8b0), copper (#ffc8a0) or steel\",ui:{type:\"color\",label:\"Light Color\",group:\"Metal\"}},darkColor:{default:\"#26282d\",transform:ce,description:\"The dark tone the brushed grain falls to in shadow. Tint it warm for gold/bronze, cool for steel.\",ui:{type:\"color\",label:\"Dark Color\",group:\"Metal\"}},brushAngle:{default:0,description:\"Direction the surface is brushed, in degrees \\u2014 the grain and every reflection streak run along this axis\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Brush Angle\",group:\"Brush\"}},anisotropy:{default:.4,description:\"How directional the brushing is \\u2014 1 = long smeared streaks along the grain, 0 = isotropic satin with no direction\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Anisotropy\",group:\"Brush\"}},grain:{default:.4,description:\"Depth of the individual brush scratches \\u2014 the fine satin texture combed into the metal\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Grain\",group:\"Brush\"}},grainScale:{default:2,description:\"Density of the brush lines \\u2014 higher = finer, tighter scratches\",ui:{type:\"range\",min:.25,max:4,step:.01,label:\"Grain Scale\",group:\"Brush\"}},roughness:{default:.5,description:\"Overall satin softness \\u2014 0 = near-polished with crisp reflections, 1 = soft matte brushed finish\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Roughness\",group:\"Surface\"}},...gr({environment:{default:1.4,description:\"Strength of the reflected studio lighting \\u2014 the softboxes seen smeared across the metal\",ui:{group:\"Surface\"}},envRotation:{ui:{group:\"Surface\"}},lightAngle:{default:215,description:\"Direction of the key light for the anisotropic specular streak, in degrees\",ui:{group:\"Surface\"}}}),speed:{default:.4,description:\"Speed of the slowly drifting reflection \\u2014 the studio gently orbits the surface. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Surface\"}},bevelWidth:{default:.05,description:\"Width of the edge bevel, relative to the shape \\u2014 thin for machined jewellery edges, wide for soft pillowed metal\",ui:{type:[\"range\",\"map\"],min:.005,max:.2,step:.001,label:\"Bevel Width\",group:\"Surface\"}},bevelShape:{default:0,description:\"Bevel profile \\u2014 0 = one smooth round fillet, 1 = machined: a steep outer fillet, a chamfer plateau and an inner knee, each catching its own line of light\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bevel Profile\",group:\"Surface\"}},...gr({edgeSoftness:{ui:{group:\"Surface\"}}}),shape:{default:sue,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=Lr(e,t,{scale:s.scale}),a=Dn(e,o,{bevelWidth:s.bevelWidth,bevelShape:s.bevelShape}),i=ii(s.brushAngle,\"brush\"),l=i.coordsOf(go(e,o)),c=z(s.anisotropy,\"aniso\"),d=z(v(s.grainScale,150),\"dens\"),u=z(v(Vt(v(r,.05)),.35),\"driftA\"),p=z(I(v(c,.5),.08),\"sheenAmt\"),_=z(Cc(l,{freq:[1.3,6.5],drift:u}),\"sheen\"),f=z(I(Cc(l,{freq:[4.5,d]}),v(Cc(l,{freq:[8,v(d,2.3)],offset:[3.1,7.7]}),.5)),\"lines\"),h=yg(a,i,{across:z(I(v(_,p),v(f,v(s.grain,.16))),\"rAcross\"),along:z(v(Cc(l,{freq:[1.3,6.5],offset:[15.21,0],drift:u}),v(p,.12)),\"rAlong\")}),g=Ea(t,o),y=z(Ge(s.roughness,0,1),\"rough\"),b=z(Lo(g,h),\"R\"),x={rotation:Er(s.envRotation,\"env\"),keyRadius:z(et(.42,.16,ne(1,y)),\"keyRad\"),drift:z(v(Vt(v(r,.07)),.05),\"drift\"),strength:z(s.environment,\"envStr\"),skyGain:rue},P=_L(z(Ge(v(I(uL({center:b,axis:i.tangent,spread:v(v(c,I(v(y,.6),.25)),.42),hint:\"envLum\"},(C,E)=>Pc(C,E,x)),Ud({normal:h,tangent:i.tangent,view:g,light:Ld(Er(s.lightAngle,\"light\"),-.7),alphas:V_(y,c),gain:et(.45,1.1,ne(1,y))})),mL(ni(h,g),{amount:.6})),0,1.7),\"lit\"),s.darkColor,s.lightColor);return Js(Dr(o.sdf,o.pxH),Ot(P,Jr(o,s.edgeSoftness)),xt,\"brushedMetal\")}})});var ZO=H({name:\"Bulge\",role:\"warp\",category:\"Distortions\",description:\"Magnify or pinch content around a center point\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point of the bulge effect\"),strength:{default:1,description:\"The intensity of the bulge effect (positive = bulge out, negative = pinch in)\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},radius:{default:1,description:\"The radius of the bulge effect area\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Radius\",group:\"Effect\"}},falloff:{default:.5,description:\"Controls the smoothness of the transition (0 = hard edge, 1 = very smooth)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},map:XL({center:T(\"center\"),strength:T(\"strength\"),radius:T(\"radius\"),falloff:T(\"falloff\")}),missingChildMessage:\"You must pass a child component into the Bulge shader.\"});var oue=Wo,aue=2*Math.PI*7,nue=JSON.stringify({type:\"sphere3D\",radius:.35}),JO=H({name:\"CarbonFiber\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photorealistic woven carbon fibre \\u2014 interlaced tows whose anisotropic sheen flips ninety degrees cell to cell, raised into a quilted weave and finished with a glossy clearcoat that mirrors the studio. Plain or twill, in any shape.\",props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the carbon shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the carbon shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the carbon shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},lightColor:{default:\"#b9bdc6\",transform:ce,description:\"The bright tone of the fibre sheen \\u2014 recolor for forged/colored carbon (red, blue, bronze)\",ui:{type:\"color\",label:\"Fiber Sheen\",group:\"Carbon\"}},darkColor:{default:\"#0b0c0e\",transform:ce,description:\"The deep resin tone the weave falls to in shadow \\u2014 near-black for classic carbon\",ui:{type:\"color\",label:\"Resin Color\",group:\"Carbon\"}},weaveStyle:{default:\"twill\",compileTime:!0,description:\"Weave pattern \\u2014 plain (checkerboard) or 2\\xD72 twill (the diagonal supercar weave)\",ui:{type:\"select\",options:[{label:\"Twill (2\\xD72)\",value:\"twill\"},{label:\"Plain\",value:\"plain\"}],label:\"Weave\",group:\"Carbon\"}},weaveScale:{default:30,description:\"Number of woven tows across the shape \\u2014 higher = finer, denser weave\",ui:{type:\"range\",min:2,max:50,step:.5,label:\"Weave Scale\",group:\"Carbon\"}},weaveAngle:{default:0,description:\"Rotates the whole weave grid in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Weave Angle\",group:\"Carbon\"}},relief:{default:.5,description:\"Depth of the quilted weave \\u2014 how far each tow bulges, catching light on its crown and shadowing the valleys between\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Weave Depth\",group:\"Carbon\"}},fiberSheen:{default:1.2,description:\"Strength of the anisotropic fibre sheen \\u2014 the directional satin glint running along each tow\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Fiber Sheen\",group:\"Carbon\"}},roughness:{default:.5,description:\"Softness of the fibre sheen \\u2014 0 = tight crisp glints, 1 = soft matte satin\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Roughness\",group:\"Surface\"}},clearcoat:{default:1.2,description:\"Strength of the glossy lacquer coat \\u2014 the sharp studio reflections of the wet clearcoat over the weave\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Clearcoat\",group:\"Surface\"}},...gr({environment:{default:2,description:\"Strength of the reflected studio lighting seen in the clearcoat\",ui:{group:\"Surface\"}},envRotation:{ui:{group:\"Surface\"}},lightAngle:{default:215,description:\"Direction of the key light for the fibre sheen, in degrees\",ui:{group:\"Surface\"}}}),bevelWidth:{default:.05,description:\"Width of the edge bevel, relative to the shape \\u2014 thin for machined jewellery edges, wide for soft pillowed metal\",ui:{type:[\"range\",\"map\"],min:.005,max:.2,step:.001,label:\"Bevel Width\",group:\"Surface\"}},bevelShape:{default:0,description:\"Bevel profile \\u2014 0 = one smooth round fillet, 1 = machined: a steep outer fillet, a chamfer plateau and an inner knee, each catching its own line of light\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bevel Profile\",group:\"Surface\"}},...gr({edgeSoftness:{ui:{group:\"Surface\"}}}),shape:{default:nue,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s,propValues:r}=t,o=(r.weaveStyle||\"twill\")===\"plain\"?\"plain\":\"twill\",a=Lr(e,t,{scale:s.scale}),{sdf:i,pxH:l}=a,c=Dn(e,a,{bevelWidth:s.bevelWidth,bevelShape:s.bevelShape}),d=ii(s.weaveAngle,\"weave\"),{along:u,across:p}=d.coordsOf(go(e,a)),_=z(v(u,s.weaveScale),\"wA\"),f=z(v(p,s.weaveScale),\"wB\"),h=z(Xr(_),\"cellX\"),g=z(Xr(f),\"cellY\"),y=z(ne(Qr(_),.5),\"fA\"),b=z(ne(Qr(f),.5),\"fB\"),x;if(o===\"plain\"){let se=z(I(h,g),\"s2\");x=z(ne(se,v(2,Xr(Ce(se,2)))),\"parity\")}else{let se=z(I(ne(h,g),64),\"s4\");x=z(br(ch(ne(se,v(4,Xr(Ce(se,4)))),2),It(1),It(0)),\"parity\")}let P=z(et(d.tangent,d.perp,x),\"fiber\"),C=z(et(d.perp,d.tangent,x),\"crossAxis\"),E=z(et(b,y,x),\"acrossLocal\"),R=z(et(u,p,x),\"alongLocal\"),U=v(s.relief,.32),L=v(Vt(v(E,oue)),U),k=v(co(v(R,2),v(E,40)),.18),D=pL(c,C,z(I(L,v(I(Vt(v(E,aue)),k),.13)),\"crossTilt\")),O=et(.5,1,ot(0,.05,Yr(ne(.5,ms(y)),ne(.5,ms(b))))),F=Ea(t,a),q=ni(D,F),Y=z(Lo(F,D),\"R\"),V=z(Ge(s.roughness,0,1),\"rough\"),X=Ld(Er(s.lightAngle,\"light\"),-.7),Q=z(et(.06,.5,V),\"baseA\"),te=Ud({normal:D,tangent:P,light:X,view:F,alphas:{along:v(Q,6),across:Q},gain:s.fiberSheen}),J=Ge(I(I(.035,v(Ge(I(v($t(D,X),.5),.5),0,1),.1)),te),0,1.3),oe=I(v(et(s.darkColor.member(\"rgb\"),s.lightColor.member(\"rgb\"),kt(J)),O),kt(z(v(v(Pc(Y.member(\"x\"),Y.member(\"y\"),{rotation:Er(s.envRotation,\"env\"),keyRadius:.16,drift:0,strength:s.environment,skyGain:.3}),I(.05,v(ts(q,5),.95))),s.clearcoat),\"coat\"))),le=Jr(a,s.edgeSoftness);return Js(Dr(i,l),Ot(oe,le),xt,\"carbonFiber\")}})});var eG=H({name:\"Chalkboard\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Renders content as a chalk drawing on a blackboard, with edge strokes and cross-hatch shading\",props:{boardColor:{default:\"#000000\",transform:ce,description:\"Background board color\",ui:{type:\"color\",label:\"Board\",group:\"Colors\"}},chalkColor:{default:\"#eceadb\",transform:ce,description:\"Chalk stroke color\",ui:{type:\"color\",label:\"Chalk\",group:\"Colors\"}},edgeSensitivity:{default:.5,description:\"How readily edges are detected and drawn as chalk strokes\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Edge Sensitivity\",group:\"Effect\"}},edgeThickness:{default:1.5,description:\"Thickness of the chalk outline strokes\",ui:{type:[\"range\",\"map\"],min:.5,max:4,step:.1,label:\"Edge Thickness\",group:\"Effect\"}},shading:{default:.05,description:\"Amount of cross-hatch shading filling darker regions\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shading\",group:\"Effect\"}},hatchScale:{default:16,description:\"Spacing of the cross-hatch lines in pixels\",ui:{type:[\"range\",\"map\"],min:3,max:24,step:.5,label:\"Hatch Scale\",group:\"Effect\"}},grain:{default:.45,description:\"Chalk dustiness / grain that breaks up the strokes\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Grain\",group:\"Effect\"}}},effect:X8({edges:$8({spacing:T(\"edgeThickness\")}),sensitivity:T(\"edgeSensitivity\"),hatchScale:T(\"hatchScale\"),shading:T(\"shading\"),grain:T(\"grain\"),board:T(\"boardColor\"),chalk:T(\"chalkColor\")})});var tG=H({name:\"ChannelBlur\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Independent blur for red, green, and blue channels\",requiresRTT:!0,requiresChild:!0,props:{redIntensity:{default:0,description:\"Blur intensity for red channel\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Red Intensity\",group:\"Effect\"}},greenIntensity:{default:20,description:\"Blur intensity for green channel\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Green Intensity\",group:\"Effect\"}},blueIntensity:{default:40,description:\"Blur intensity for blue channel\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Blue Intensity\",group:\"Effect\"}}},...BU({red:T(\"redIntensity\"),green:T(\"greenIntensity\"),blue:T(\"blueIntensity\")})});var sG=H({name:\"Checkerboard\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Classic checkerboard pattern with two alternating colors\",acceptsUVContext:!0,props:{colorA:{default:\"#cccccc\",transform:ce,description:\"First color of the checkerboard pattern\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#999999\",transform:ce,description:\"Second color of the checkerboard pattern\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},cells:{default:8,description:\"Number of cells along the canvas height (creates square cells). Switch the unit to px in the editor to set an absolute cell size instead.\",ui:{type:[\"range\",\"map\"],min:1,max:50,step:1,label:\"Cells\",group:\"Effect\",dimensional:\"count-canvas-height\"}},softness:{default:0,description:\"Smoothness of the transition between colors (0 = hard edges, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Softness\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:T(\"colorA\"),stroke:T(\"colorB\"),mask:NR({cells:T(\"cells\"),softness:T(\"softness\")}),space:T(\"colorSpace\")})});var rG=H({name:\"CheckerWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away as a checkerboard of fading squares\",props:{progress:{default:.5,description:\"How far the wipe has progressed (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},blockSize:{default:.1,description:\"Size of each square as a fraction of the frame width\",ui:{type:[\"range\",\"map\"],min:.02,max:.5,step:.01,label:\"Block Size\",group:\"Transition\"}},softness:{default:.15,description:\"How softly each square fades out (0 = hard-edged squares)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse the order the squares wipe in\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.cells(T(\"blockSize\")).checker(),progress:T(\"progress\"),feather:T(\"softness\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Checker Wipe shader.\"});var oG=XR({count:T(\"count\"),angle:T(\"angle\"),balance:T(\"balance\"),softness:T(\"softness\"),offset:T(\"offset\")}),aG=H({name:\"Chevron\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Animated chevron / zigzag stripe pattern\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#000000\",transform:ce,description:\"First color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Second color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},count:{default:5,description:\"Number of chevron pairs visible\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Count\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Rotation angle of the chevrons\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Angle\",group:\"Effect\"}},balance:{default:.5,description:\"Ratio of the two colors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Balance\",group:\"Effect\"}},softness:{default:0,description:\"Edge softness\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},speed:{default:0,description:\"Animation speed\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},offset:{default:0,description:\"Phase offset for pattern positioning\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Offset\",group:\"Animation\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:fp(Zr({fill:T(\"colorA\"),stroke:T(\"colorB\"),mask:oG,space:T(\"colorSpace\")}),fg(I_(T(\"colorA\")),I_(T(\"colorB\")),oG))});var yo=128,G2=(e,t)=>typeof e==\"number\"?e:t,nG=H({name:\"ChromaFlow\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Interactive liquid flow effect that follows your cursor\",usesPointer:!0,props:{baseColor:{default:\"#0066ff\",transform:ce,description:\"Base liquid color\",ui:{type:\"color\",label:\"Base Color\",group:\"Colors\"}},upColor:{default:\"#00ff00\",transform:ce,description:\"Color for upward movement\",ui:{type:\"color\",label:\"Up Color\",group:\"Colors\"}},downColor:{default:\"#ff0000\",transform:ce,description:\"Color for downward movement\",ui:{type:\"color\",label:\"Down Color\",group:\"Colors\"}},leftColor:{default:\"#0000ff\",transform:ce,description:\"Color for leftward movement\",ui:{type:\"color\",label:\"Left Color\",group:\"Colors\"}},rightColor:{default:\"#ffff00\",transform:ce,description:\"Color for rightward movement\",ui:{type:\"color\",label:\"Right Color\",group:\"Colors\"}},intensity:{default:1,description:\"Strength of the liquid effect\",ui:{type:\"range\",min:.5,max:1.5,step:.1,label:\"Intensity\",group:\"Effect\"}},radius:{default:3,description:\"Radius of the liquid effect\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Radius\",group:\"Effect\"}},momentum:{default:30,description:\"How much momentum colors retain in their flow direction\",ui:{type:\"range\",min:10,max:60,step:1,label:\"Momentum\",group:\"Effect\"}}},gpu:{fragment:e=>{let{uniforms:t,ctx:s,uvContext:r,onBeforeRender:o,dimensions:a,getCpuValue:i}=e,l=new Float32Array(yo*yo*4),c=new Float32Array(yo*yo*4),d=Bg(e,{size:yo,label:\"chromaflow-field\"}),u=d.texture,p=0,_=0,f=.5,h=.5,g=0,y=1,b=!1,x=0,P=1,C=.15;o(kg({clampDt:.016,steps:[gn(\"cursorVelocity\",k=>{y=a.width/Math.max(1,a.height);let D=k.dt>0?(k.pointer.x-f)/k.dt:0,O=k.dt>0?(k.pointer.y-h)/k.dt:0;p=p*.85+D*.15,_=_*.85+O*.15,f=k.pointer.x,h=k.pointer.y,b=Math.abs(D)+Math.abs(O)>.01}),gn(\"idleSkip\",()=>!b&&g<1e-4?\"skip\":void 0),gn(\"advect+decay\",k=>{P=G2(i(\"intensity\"),1),C=G2(i(\"radius\"),3)*.05;let D=G2(i(\"momentum\"),30),O=1-k.dt/Math.max(.1,1),F=1-k.dt,q=D*50*k.dt;x=0;for(let Y=0;Y<yo;Y++)for(let V=0;V<yo;V++){let X=(Y*yo+V)*4,Q=l[X],te=l[X+1];if(c[X]=Q*O,c[X+1]=te*O,c[X+2]=l[X+2]*F,Math.abs(Q)>.001||Math.abs(te)>.001){let oe=V-Q*q,le=Y-te*q,se=Math.floor(oe),Te=Math.floor(le),de=se+1,xe=Te+1;if(se>=0&&Te>=0&&de<yo&&xe<yo){let Ae=oe-se,De=le-Te,Ee=(Te*yo+se)*4,ze=(Te*yo+de)*4,Ie=(xe*yo+se)*4,ke=(xe*yo+de)*4,Me=l[Ee+2]*(1-Ae)*(1-De)+l[ze+2]*Ae*(1-De)+l[Ie+2]*(1-Ae)*De+l[ke+2]*Ae*De;c[X+2]=Me*F}}let J=Math.max(Math.abs(c[X]),Math.abs(c[X+1]),c[X+2]);J>x&&(x=J)}}),gn(\"inject(brush)\",k=>{if(!b)return;let D=Math.sqrt(p*p+_*_),O=Math.min(D*D*20,1),F=C*O,q=F*2*(F*2),Y=F*F,V=P*100*k.dt*.01,X=Math.min(D*10,1);for(let Q=0;Q<yo;Q++)for(let te=0;te<yo;te++){let J=(te+.5)/yo,oe=(Q+.5)/yo,le=y>=1?(J-k.pointer.x)*y:J-k.pointer.x,se=y>=1?oe-k.pointer.y:(oe-k.pointer.y)/y,Te=le*le+se*se;if(Te<q){let de=(Q*yo+te)*4,xe=Math.exp(-Te/Y);c[de]+=p*xe*V,c[de+1]+=_*xe*V,c[de+2]+=xe*V*X,c[de]=Math.max(-1,Math.min(1,c[de])),c[de+1]=Math.max(-1,Math.min(1,c[de+1])),c[de+2]=Math.max(0,Math.min(1,c[de+2]))}}}),gn(\"publish(halfFloat)\",()=>{g=b?1:x,l.set(c);for(let k=0;k<d.texData.length;k++)d.texData[k]=_o(l[k]);d.upload()})]}));let E=r??s.uv,R=1/yo,U=\"linearClamp\",L=(k,D)=>u.sample(I(E,rt(k,D)),U);return W(qz,\"flowDirectionColor\",[L(0,0),L(R,0),L(0,R),L(-R,0),L(0,-R),t.baseColor,t.upColor,t.downColor,t.leftColor,t.rightColor])}}});var iG=H({name:\"ChromaticAberration\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Separate RGB channels for a prismatic distortion effect\",props:{strength:{default:.2,description:\"Overall strength of the chromatic aberration effect\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Direction of the chromatic aberration in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},redOffset:{default:-1,description:\"Red channel offset multiplier\",ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.1,label:\"Red Offset\",group:\"Effect\"}},greenOffset:{default:0,description:\"Green channel offset multiplier\",ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.1,label:\"Green Offset\",group:\"Effect\"}},blueOffset:{default:1,description:\"Blue channel offset multiplier\",ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.1,label:\"Blue Offset\",group:\"Effect\"}}},effect:$o(L8({strength:T(\"strength\"),angle:T(\"angle\"),red:T(\"redOffset\"),green:T(\"greenOffset\"),blue:T(\"blueOffset\")}),[])});var{bevelSin:iue}=tz,{fieldGradient:lue}=Xh,{isMobileGpuViewport:cue}=kn,due=bt,uue=JSON.stringify({type:\"sphere3D\",radius:.35});function F2(e,t,s,r,o,a,i,l,c){let d=z(e,\"ex\"),u=z(t,\"ey\"),p=z(s,\"ez\"),_=z(Ce(d,I(Kr(rt(d,u)),1e-4)),\"horiz\"),f=z(I(.35,v(.65,ot(-.7,.7,_))),\"warmSide\"),h=z(I(.45,v(.55,ot(-.7,.7,v(_,-1)))),\"coolSide\"),g=z(ot(-.65,1,u),\"up\"),y=I(v(c,.03),Qe(.003,.003,.004)),b=et(et(Qe(.17,.175,.19),y,kt(i)),Qe(.56,.57,.6),kt(v(g,g))),x=ot(.3,.9,u);b=I(b,v(Qe(.55,.55,.56),x));let P=z(Ce(I(u,.45),.2),\"cd\"),C=tr(v(v(P,P),-1));b=I(b,v(c,v(v(v(C,a),.2),h)));let E=z(ot(.18,-.1,p),\"facing\"),R=z(it(ne(.32,p),.06),\"pz\"),U=z(Ce(d,R),\"px\"),L=z(Ce(u,R),\"py\"),k=z(ne(ms(U),.62),\"bx\"),D=z(ne(ms(ne(L,.3)),.28),\"by\"),O=Yr(it(k,D),0),F=z(I(Kr(it(rt(k,D),rt(0,0))),O),\"dbox\"),q=z(I(.05,v(r,.4)),\"boxSoft\"),Y=v(ot(q,v(q,-.6),F),E);b=I(b,v(Qe(2.9,2.87,2.82),Y));let V=ot(.5,.92,ms(d)),X=et(.55,.07,i);b=v(b,et(It(1),X,V));let Q=z(I(.065,v(o,.05)),\"gw\"),te=z(it(F,0),\"dOut\"),J=v(tr(v(v(Ce(te,Q),Ce(te,Q)),-1)),ot(-.06,.1,F));b=I(b,v(l,v(v(v(v(J,a),1.3),f),E)));let oe=z(Ce(I(u,.12),.05),\"fb\");b=I(b,v(l,v(v(v(v(tr(v(v(oe,oe),-1)),a),.5),f),ne(1,E))));let le=z(ne(ms(U),.7),\"cbx\"),se=z(ne(ms(I(L,.78)),.05),\"cby\"),Te=z(I(Kr(it(rt(le,se),rt(0,0))),Yr(it(le,se),0)),\"dcard\"),de=v(ot(q,v(q,-.6),Te),E);return I(b,v(Qe(.5,.5,.52),de))}var lG=H({name:\"Chrome\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Studio-lit mirror chrome \\u2014 a precision-machined shape with a controllable polished bevel, softly convex faces and a photographic studio reflected in them: one huge frontal softbox, black flags, and thin warm/cool strip lights that fringe amber and ice-blue exactly where the reflections break\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=e.volumetric,a=cue(),i=.01,l=.0081,c=Lr(e,t,{scale:s.scale}),{s0:d,sX:u,sY:p,sdf:_,pxH:f}=c,h=go(e,c),g=h.member(\"x\"),y=h.member(\"y\"),b;if(o)b=Mc(c);else{let qe=z(W(lue,\"fieldGradient\",[d,u,p,It(i)]),\"grad\"),st=z(it(Kr(qe),1e-4),\"glen\"),pt=z(Ce(qe.member(\"x\"),st),\"gx\"),Ze=z(Ce(qe.member(\"y\"),st),\"gy\"),nt=it(s.bevelWidth,.003),ht=z(v(_,-1),\"depth\"),yt=z(Ge(Ce(ht,nt),0,1),\"t0\"),St=z(W(iue,\"bevelSin\",[Ge(I(yt,v(v(v(v(v(z(W(Si,\"perlin12d\",[rt(v(g,21),v(y,21))]),\"rimN\").member(\"x\"),s.waviness),.22),yt),ne(1,yt)),4)),0,1),s.bevelShape]),\"sBev\"),Mt=v(v(s.curvature,.14),Ce(l,I(v(ht,ht),l))),Rt=z(Kr(rt(g,y)),\"flen\"),Kt=z(Ce(g,it(Rt,1e-4)),\"dgx\"),Wt=z(Ce(y,it(Rt,1e-4)),\"dgy\"),ls=z(v(v(s.curvature,.75),ot(0,.5,Rt)),\"sSphere\"),us=z(it(St,Mt),\"sEdge\"),os=z(rt(I(v(pt,us),v(Kt,ls)),I(v(Ze,us),v(Wt,ls))),\"txy\"),bs=z(Yr(Kr(os),.9995),\"tl\"),hs=z(v(os,Ce(bs,it(Kr(os),1e-4))),\"tn\"),gs=ho(ne(1,v(bs,bs)));b=z(Qe(hs.member(\"x\"),hs.member(\"y\"),v(gs,-1)),\"nGeo\")}let x=z(Ag(rt(I(v(g,1.05),v(r,.08)),v(y,1.05)),1),\"wg\"),P=z(v(s.waviness,.2),\"wAmt\"),C=Gi(b,v(x.member(\"x\"),P),v(x.member(\"y\"),P)),E=Ea(t,c,.55),R=z(Lo(E,C),\"R\"),U=ni(C,E),L=z(I(v(s.envRotation,due),v(Vt(v(r,.23)),.05)),\"rotA\"),k=z(Do(L),\"envC\"),D=z(Vt(L),\"envS\"),O=z(v(R.member(\"y\"),-1),\"upY\"),F=z(ne(v(R.member(\"x\"),k),v(O,D)),\"rx0\"),q=z(I(v(R.member(\"x\"),D),v(O,k)),\"ry0\"),Y=z(I(v(Vt(v(r,.5)),.4),v(Vt(I(v(r,.19),1.7)),.2)),\"orbit\"),V=z(Do(Y),\"oc\"),X=z(Vt(Y),\"os\"),Q=z(I(v(F,V),v(R.member(\"z\"),X)),\"rx\"),te=z(ne(v(R.member(\"z\"),V),v(F,X)),\"rz0\"),J=z(v(Vt(I(v(r,.31),.9)),.12),\"nod\"),oe=z(Do(J),\"nc\"),le=z(Vt(J),\"ns\"),se=z(ne(v(q,oe),v(te,le)),\"ry\"),Te=z(I(v(te,oe),v(q,le)),\"rz\"),de=ot(.75,1,v(C.member(\"z\"),-1)),xe=z(s.softness,\"soft\"),Ae=z(I(xe,v(de,.2)),\"softEff\"),De=z(s.spectral,\"spectral\"),Ee=z(s.shadows,\"shadows\"),ze=z(s.warmColor.member(\"rgb\"),\"warmC\"),Ie=z(s.coolColor.member(\"rgb\"),\"coolC\"),ke=z(F2(Q,se,Te,Ae,xe,De,Ee,ze,Ie),\"envG\"),Me,we;if(a)Me=ke.member(\"x\"),we=ke.member(\"z\");else{let qe=z(v(s.dispersion,I(.004,v(v(U,U),.06))),\"delta\"),st=z(F2(Q,I(se,qe),Te,Ae,xe,De,Ee,ze,Ie),\"envRt\"),pt=z(F2(Q,ne(se,qe),Te,Ae,xe,De,Ee,ze,Ie),\"envBt\");Me=st.member(\"x\"),we=pt.member(\"z\")}let Ve=yp(v(v(Qe(Me,ke.member(\"y\"),we),s.environment),s.tint.member(\"rgb\")));return Js(Dr(_,f),Ot(Ve,Jr(c,s.edgeSoftness)),xt,\"chrome\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the chrome shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the chrome shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the chrome shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},tint:{default:\"#ffffff\",transform:ce,description:\"Metal tint multiplied into the reflection \\u2014 white for silver chrome, warm ivory for gold, dusky rose for copper\",ui:{type:\"color\",label:\"Tint\",group:\"Metal\"}},warmColor:{default:\"#ffa94d\",transform:ce,description:\"Color of the warm strip light hugging the studio horizon \\u2014 the amber band that fringes the reflections\",ui:{type:\"color\",label:\"Warm Light\",group:\"Metal\"}},coolColor:{default:\"#82a7e6\",transform:ce,description:\"Color of the cool wash below the horizon \\u2014 the steel-blue zone the darker reflections fall into\",ui:{type:\"color\",label:\"Cool Light\",group:\"Metal\"}},bevelWidth:{default:.028,description:\"Width of the polished edge bevel, relative to the shape \\u2014 thin for machined jewellery edges, wide for soft pillowed metal\",ui:{type:[\"range\",\"map\"],min:.005,max:.2,step:.001,label:\"Bevel Width\",group:\"Bevel\"}},bevelShape:{default:.55,description:\"Bevel profile \\u2014 0 = one smooth round fillet, 1 = machined: a steep outer fillet, a chamfer plateau and an inner knee, each catching its own line of light\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bevel Profile\",group:\"Bevel\"}},curvature:{default:.5,description:\"Convex doming of the faces \\u2014 sweeps the big soft studio gradients across otherwise flat metal\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Curvature\",group:\"Bevel\"}},waviness:{default:.15,description:\"Pressed-metal imperfection \\u2014 gently bends the face reflections and wobbles the bevel light lines so the surface reads as real\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Waviness\",group:\"Bevel\"}},...gr({environment:{description:\"Exposure of the reflected studio \\u2014 the overall brilliance of the chrome\",ui:{group:\"Studio\"}},envRotation:{description:\"Rotates the whole reflected studio \\u2014 spins where the softbox, flags and strips fall\",ui:{type:[\"range\",\"map\"],group:\"Studio\"}}}),softness:{default:.3,description:\"Softness of the studio edges \\u2014 0 = razor polished reflections, 1 = satin diffusion\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Studio\"}},spectral:{default:.9,description:\"The colored accent lights: an amber glow hugging the softbox edge and an ice-blue wash below it. 0 = a fully achromatic black-and-white studio.\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Accent Lights\",group:\"Studio\"}},...gr({dispersion:{default:.3,description:\"Prismatic RGB splitting concentrated on the fast-curving bevel \\u2014 thin rainbow micro-fringes along the edge highlights, distinct from the broad accent lights\",ui:{group:\"Studio\"}}}),shadows:{default:.7,description:\"Depth of the dark studio reflections \\u2014 how hard the black side flags and floor crush toward graphite. 0 lifts them to a soft gray studio.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadows\",group:\"Studio\"}},speed:{default:.5,description:\"Speed of the studio orbit \\u2014 the whole lighting environment slowly wanders around the piece, sliding the reflections across faces and edges. 0 freezes it.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Studio\"}},...gr({edgeSoftness:{ui:{group:\"Surface\"}}}),shape:{default:uue,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var cG=H({role:\"shape\",name:\"Circle\",description:\"Generate a circle with adjustable size and softness\",shape:{distance:({x:e,y:t},s)=>_p(e,t,v(s(\"radius\"),.5)),rotatable:!1,colorDescription:\"The color of the circle\",centerDescription:\"The center point of the circle\",centerLabel:\"Center Position\",bounds:{size:{width:\"radius\",height:\"radius\",as:\"canvas-height\"}},shapeProps:{radius:{default:1,description:\"The radius of the circle. A value of one (1) is sets the circle to fit the canvas.\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Radius\",group:\"Effect\",units:[\"%\",\"px\"]}}},stroke:{strokeColorDescription:\"The color of the stroke outline\",strokePositionDescription:\"Position of the stroke relative to the circle edge\"},colorSpaceDescription:\"Color space for blending fill and stroke colors in soft edges\",propOverrides:{softness:{default:0,description:\"Edge softness. Lower values like zero (0) are sharp, higher values like one (1) are softer.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\",dimensional:\"canvas-height\"}},strokeThickness:{default:0,description:\"The thickness of the stroke outline. Zero (0) means no stroke.\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.01,label:\"Stroke Thickness\",group:\"Stroke\"}}},propOrder:[\"origin\",\"color\",\"radius\",\"softness\",\"center\",\"strokeThickness\",\"strokeColor\",\"strokePosition\",\"colorSpace\"]}});var dG=H({name:\"ColorWheel\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"A directional gradient that smoothly cycles through rainbow colors or a custom set of three colors\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{mode:{default:\"rainbow\",compileTime:!0,description:\"Rainbow cycles through the full spectrum; Custom loops through your three chosen colors\",ui:{type:\"select\",options:[{label:\"Rainbow\",value:\"rainbow\"},{label:\"Custom\",value:\"custom\"}],label:\"Mode\",group:\"Colors\"}},colorA:{default:\"#ff0000\",transform:ce,description:\"First color in the cycle\",ui:{type:\"color\",label:\"Color 1\",group:\"Colors\",condition:{mode:\"custom\"}}},colorB:{default:\"#00ff88\",transform:ce,description:\"Second color in the cycle\",ui:{type:\"color\",label:\"Color 2\",group:\"Colors\",condition:{mode:\"custom\"}}},colorC:{default:\"#0066ff\",transform:ce,description:\"Third color in the cycle\",ui:{type:\"color\",label:\"Color 3\",group:\"Colors\",condition:{mode:\"custom\"}}},scale:{default:1,description:\"Number of color cycles across the viewport\",ui:{type:[\"range\",\"map\"],min:.1,max:10,step:.1,label:\"Scale\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Direction the gradient flows\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Angle\",group:\"Effect\"}},speed:{default:.05,description:\"Speed at which the gradient cycles\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Speed\",group:\"Animation\"}},colorSpace:{default:\"oklch\",transform:at,compileTime:!0,description:\"Color space for blending between custom colors\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\",condition:{mode:\"custom\"}}}},paint:FR({mode:T(\"mode\"),direction:T(\"angle\"),scale:T(\"scale\"),palette:{a:T(\"colorA\"),b:T(\"colorB\"),c:T(\"colorC\")},space:T(\"colorSpace\")})});var uG=6,pG=4,Ng=pG,Bc=1024,mG=\"rgba16float\",pue=()=>Wz({nLuma:uG,scaleN:pG,format:mG,namePrefix:\"compression\"}),Z0e=JA(uG);function mue(e,t,s){let{ctx:r}=s,o=W(Yt,\"unpremultiplyAlpha\",[t.sample(r.uv)]),a=z(Xr(v(r.uv,r.viewportSize)),\"pPix\"),i=Ge(Xr(Ce(a.member(\"x\"),Ng)),0,Bc-1),l=Ge(Xr(Ce(a.member(\"y\"),Ng)),0,Bc-1),c=rt(Ce(I(i,.5),Bc),Ce(I(l,.5),Bc));return Ct(e.sample(c,\"nearestClamp\").member(\"rgb\"),o.member(\"a\"))}function _ue(e){return{compute:t=>{let{childNode:s,gpu:r,convertToTexture:o,registerComputeTexture:a,getCpuValue:i,onCleanup:l,onResize:c,dimensions:d}=t;if(!s)return null;let u=r?.root;if(!u)return null;let p=o(s),_=u.createTexture({size:[Bc,Bc],format:mG}).$usage(\"storage\",\"sampled\");l(()=>_.destroy());let f=a(_),h=pue(),g=u.createUniform(h.Params),y=Math.max(1,Math.round(d.width)),b=Math.max(1,Math.round(d.height)),x=(L,k)=>[Math.min(Math.ceil(L/Ng),Bc),Math.min(Math.ceil(k/Ng),Bc)],[P,C]=x(y,b),E=ai(u,h.kernel),R=E,U=!1;return c(({width:L,height:k})=>{y=Math.max(1,Math.round(L)),b=Math.max(1,Math.round(k)),[P,C]=x(y,b)}),{outputs:{childTexture:p,jpegCellTexture:f},bindInputs:L=>{let k=L(p.key);if(k){let D=u.createBindGroup(h.layout,{input:k.texture,cellTex:_,params:g.buffer});R=E.with(D),U=!0}},getComputeNodes:()=>{let L=i(e.quality.name);return g.write({inputWidth:y,inputHeight:b,quality:typeof L==\"number\"?L:12}),[()=>{U&&R.dispatchThreads(P,C)}]}}},gpu:{fragment:t=>{let{childNode:s,ctx:r,computeOutputs:o,convertToTexture:a}=t;if(!s)return xt;let i=o?.jpegCellTexture,l=o?.childTexture;return!i||!l?W(Yt,\"unpremultiplyAlpha\",[a(s).sample(r.uv)]):mue(i,l,t)}}}}var _G=H({name:\"CompressionArtifacts\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Simulates lossy JPEG compression \\u2014 8\\xD78 DCT block quantization, blockiness, ringing and color bleed\",requiresRTT:!0,requiresChild:!0,props:{quality:{default:12,description:\"JPEG quality (1 = heavily destroyed, 100 = near lossless)\",ui:{type:[\"range\",\"map\"],min:1,max:100,step:1,label:\"Quality\",group:\"Effect\"}}},..._ue({quality:T(\"quality\")})});var fG=H({name:\"ConcentricSpin\",role:\"warp\",category:\"Distortions\",description:\"Concentric rings that each rotate the underlying image by different amounts\",boundingBoxDeclaration:{aspectRatio:null},animatedTime:{speed:\"speed\"},props:{intensity:{default:20,description:\"Maximum rotation angle per ring\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},rings:{default:8,description:\"Number of concentric rings\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Rings\",group:\"Effect\"}},smoothness:{default:.03,description:\"Softness of transitions between rings\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Smoothness\",group:\"Effect\"}},seed:{default:0,description:\"Randomization seed for per-ring rotation variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},speed:{default:.1,description:\"Speed of continuous ring rotation\",ui:{type:\"range\",min:-5,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},speedRandomness:{default:.5,description:\"How much each ring varies in rotation speed and direction\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Speed Randomness\",group:\"Animation\"}},edges:fr(\"mirror\",\"How to handle edges when distortion pushes content out of bounds\"),center:zo(\"Center point of the concentric rings\",{label:\"Center Position\"})},map:sU({center:T(\"center\"),intensity:T(\"intensity\"),rings:T(\"rings\"),smoothness:T(\"smoothness\"),seed:T(\"seed\"),speedRandomness:T(\"speedRandomness\")}),missingChildMessage:\"You must pass a child component into the ConcentricSpin shader.\"});var vG=H({name:\"ConicGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Colors sweep in a full circle around a center point, like a color wheel\",acceptsUVContext:!0,props:{colorA:{default:\"#FF0080\",transform:ce,description:\"Starting color of the sweep\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#00BFFF\",transform:ce,description:\"Ending color of the sweep (wraps back to Color A)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),center:{default:{x:.5,y:.5},transform:Xe,description:\"Center point of the sweep\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},rotation:{default:0,description:\"Rotation offset in degrees \\u2014 shifts where Color A begins\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},repeat:{default:1,description:\"Number of times the gradient repeats around the circle. Values above 1 create a starburst pattern.\",ui:{type:[\"range\",\"map\"],min:1,max:24,step:1,label:\"Repeat\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(AR(Oi.conic({center:T(\"center\"),rotation:T(\"rotation\")}),T(\"repeat\")),Us(T(\"colorSpace\")))});var fue=e=>e===\"alpha\"?1:0,vue=e=>e===\"custom\"?1:0,hG=H({name:\"ContourLines\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Draw topographical contour lines based on luminance or alpha\",blendWithChildren:!1,props:{levels:{default:5,description:\"Number of contour levels\",ui:{type:[\"range\",\"map\"],min:2,max:30,step:1,label:\"Levels\",group:\"Effect\"}},lineWidth:{default:2,description:\"Width of the contour lines in pixels\",ui:{type:[\"range\",\"map\"],min:.5,max:5,step:.1,label:\"Line Width\",group:\"Effect\"}},softness:{default:0,description:\"Edge softness of the lines (0 = sharp, 1 = soft)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},gamma:{default:.5,description:\"Contour distribution. <1 clusters in bright, >1 clusters in dark\",ui:{type:[\"range\",\"map\"],min:.1,max:2,step:.01,label:\"Gamma\",group:\"Effect\"}},invert:{default:!1,description:\"Invert the source values\",transform:rs,ui:{type:\"checkbox\",label:\"Invert\",group:\"Effect\"}},source:{default:\"luminance\",description:\"Use luminance or alpha channel for contours\",transform:fue,compileTime:!0,ui:{type:\"select\",options:[{label:\"Luminance\",value:\"luminance\"},{label:\"Alpha\",value:\"alpha\"}],label:\"Source\",group:\"Effect\"}},colorMode:{default:\"source\",description:\"Use source image colors or custom colors\",transform:vue,compileTime:!0,ui:{type:\"select\",options:[{label:\"Source\",value:\"source\"},{label:\"Custom\",value:\"custom\"}],label:\"Color Mode\",group:\"Colors\"}},lineColor:{default:\"#000000\",description:\"Color of the contour lines (custom mode)\",transform:ce,ui:{type:\"color\",label:\"Line Color\",group:\"Colors\",condition:{colorMode:\"custom\"}}},backgroundColor:{default:\"transparent\",description:\"Background color (custom mode)\",transform:ce,ui:{type:\"color\",label:\"Background Color\",group:\"Colors\",condition:{colorMode:\"custom\"}}}},effect:p8({source:T(\"source\"),levels:T(\"levels\"),lineWidth:T(\"lineWidth\"),softness:T(\"softness\"),gamma:T(\"gamma\"),invert:T(\"invert\"),colorMode:T(\"colorMode\"),lineColor:T(\"lineColor\"),backgroundColor:T(\"backgroundColor\")})});var gG=H({name:\"CornerPin\",role:\"warp\",category:\"Distortions\",description:\"Pin each corner of the content to an arbitrary position for a free perspective warp\",boundingBoxDeclaration:{aspectRatio:null},props:{topLeft:{default:{x:0,y:0},transform:Xe,description:\"Position of the top-left corner\",ui:{type:\"position\",label:\"Top Left\",group:\"Corners\"}},topRight:{default:{x:1,y:0},transform:Xe,description:\"Position of the top-right corner\",ui:{type:\"position\",label:\"Top Right\",group:\"Corners\"}},bottomLeft:{default:{x:0,y:1},transform:Xe,description:\"Position of the bottom-left corner\",ui:{type:\"position\",label:\"Bottom Left\",group:\"Corners\"}},bottomRight:{default:{x:1,y:1},transform:Xe,description:\"Position of the bottom-right corner\",ui:{type:\"position\",label:\"Bottom Right\",group:\"Corners\"}},amount:{default:1,description:\"Blends the warp in and out \\u2014 0 returns the content to its original rectangle, 1 fully pins the corners\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Amount\",group:\"Effect\"}},edges:fr(\"transparent\",\"How to handle areas outside the pinned quad\")},map:eU({topLeft:T(\"topLeft\"),topRight:T(\"topRight\"),bottomRight:T(\"bottomRight\"),bottomLeft:T(\"bottomLeft\"),amount:T(\"amount\")})});var yG=H({role:\"shape\",name:\"Crescent\",description:\"Crescent moon shape \\u2014 an outer circle with an inner circle subtracted\",shape:{distance:({x:e,y:t},s)=>fR(e,t,s(\"radius\"),s(\"innerRatio\"),s(\"offset\")),colorDescription:\"Fill color of the crescent\",centerDescription:\"Center position of the crescent\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.3,description:\"Outer circle radius\",ui:{type:\"range\",min:.05,max:1,step:.01,label:\"Size\",group:\"Shape\"}},innerRatio:{default:.8,description:\"Inner (bite) circle radius as a fraction of outer radius\",ui:{type:\"range\",min:.3,max:1.2,step:.01,label:\"Bite Size\",group:\"Shape\"}},offset:{default:.2,description:\"Horizontal distance the bite circle is shifted from center\",ui:{type:\"range\",min:.01,max:.5,step:.01,label:\"Bite Offset\",group:\"Shape\"}}}}});var bG=H({role:\"shape\",name:\"Cross\",description:\"Plus / cross shape with adjustable arm length, width, and rounding\",shape:{distance:({x:e,y:t},s)=>pR(e,t,s(\"radius\"),s(\"thickness\"),s(\"rounding\")),colorDescription:\"Fill color of the cross\",centerDescription:\"Center position of the cross\",rotationDescription:\"Rotation in degrees (45\\xB0 turns a plus into an \\xD7)\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.35,description:\"Arm half-length \\u2014 distance from center to the end of each arm\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Arm Length\",group:\"Shape\"}},thickness:{default:.08,description:\"Arm half-width \\u2014 controls how wide each arm is\",ui:{type:\"range\",min:.01,max:.5,step:.005,label:\"Arm Width\",group:\"Shape\"}},rounding:{default:0,description:\"Corner rounding \\u2014 rounds the arm ends and concave corners\",ui:{type:\"range\",min:0,max:.2,step:.005,label:\"Rounding\",group:\"Shape\"}}}}});var xG=H({name:\"CRTScreen\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Retro CRT monitor simulation with scanlines\",props:{pixelSize:{default:128,description:\"Density of the RGB phosphor stripes \\u2014 about half this many across the canvas width (higher = smaller, finer pixels)\",ui:{type:[\"range\",\"map\"],min:8,max:128,step:1,label:\"Pixel Size\",group:\"Effect\"}},colorShift:{default:1,description:\"Chromatic aberration amount\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Color Shift\",group:\"Effect\"}},scanlineIntensity:{default:.3,description:\"Strength of horizontal scanlines\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Scanline Intensity\",group:\"Effect\"}},scanlineFrequency:{default:200,description:\"Number of scanlines across screen\",ui:{type:[\"range\",\"map\"],min:100,max:800,step:10,label:\"Scanline Frequency\",group:\"Effect\"}},brightness:{default:1,description:\"Screen brightness boost\",ui:{type:[\"range\",\"map\"],min:.5,max:2,step:.1,label:\"Brightness\",group:\"Adjustments\"}},contrast:{default:1,description:\"Screen contrast enhancement\",ui:{type:[\"range\",\"map\"],min:.5,max:2,step:.1,label:\"Contrast\",group:\"Adjustments\"}},vignetteIntensity:{default:1,description:\"Strength of corner darkening effect (0 = off)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Vignette Intensity\",group:\"Effect\"}},vignetteRadius:{default:.5,description:\"How far the vignette extends inward (0 = edges only, 1 = reaches center)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Vignette Radius\",group:\"Effect\"}}},effect:$o(dU({amount:T(\"colorShift\")}),[_U({brightness:T(\"brightness\"),contrast:T(\"contrast\")}),fU({frequency:T(\"scanlineFrequency\"),intensity:T(\"scanlineIntensity\")}),vU({pitch:T(\"pixelSize\")}),hU({radius:T(\"vignetteRadius\"),intensity:T(\"vignetteIntensity\")}),gU()])});var{insideMask:hue}=Xh,{unpremultiplyAlpha:j_}=Qk,gue=bt,yue=Po,bue=JSON.stringify({type:\"sphere3D\",radius:.35}),AG=H({name:\"Crystal\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Diamond-like crystal lens with faceted refraction.\",requiresRTT:!0,requiresChild:!0,...Ts({pattern:\"triplanar\",child:\"required\",surface:(e,t)=>{let{uniforms:s,propValues:r,ctx:o}=t,a=e.childTexture,i=e.volumetric,l=Lr(e,t,{scale:s.scale}),c=z(e.sdfUV,\"suv\"),d=z(Ce(o.viewportSize.member(\"x\"),o.viewportSize.member(\"y\")),\"aspect\"),u=z(it(v(s.edgeSoftness,.5),.001),\"sharp\"),{sdf:p,pxH:_}=l,f=z(s.center.member(\"x\"),\"centerPosX\"),h=z(ne(1,s.center.member(\"y\")),\"centerPosY\"),g=Ln(l),y=g.member(\"x\"),b=g.member(\"y\"),x=z(W(hue,\"insideMask\",[p,u,_,It(1.5)]),\"rb1\"),P=v(s.lightAngle,gue),C=z(I(Do(P),v(ne(f,.5),3)),\"lx\"),E=z(I(Vt(P),v(ne(h,.5),3)),\"ly\"),R=bg(C,E,-.75,\"kl1\"),U=R.member(\"L\"),L=R.member(\"H\"),k=bg(v(E,-1),C,-.4,\"kl2\"),D=k.member(\"L\"),O=k.member(\"H\"),F=Ge(Ce(v(p,-1),.15),0,1),q=z(v(ne(1,ot(0,.03,p)),.4),\"outerWarp\"),Y=v(s.refraction,.15),V=z(Ce(v(v(v(y,-1),Y),q),d),\"haloOffX\"),X=z(v(v(v(b,-1),Y),q),\"haloOffY\"),Q=go(e,l,{uv:c}),te=Q.member(\"x\"),J=Q.member(\"y\"),oe=z(Ce(yue,s.facets),\"sAngle\"),le=z(g2(I(te,.5),I(J,.5),oe),\"fUv\"),se=z(Ce(ho(I(v(te,te),v(J,J))),.35),\"crystalRNorm\"),Te=z(ne(1,ot(.22,.32,se)),\"tableMask\"),de=I(1,v(Ge(se,0,1),.6)),xe=z(ne(1,v(Te,.9)),\"tableSuppress\"),Ae;if(i)Ae=Mc(l,4,\"nSurf\");else{let Af=z(v(v(ne(1,ot(0,1,F)),1.7),ne(1,Te)),\"bevelSlope\");Ae=z(Os(Qe(v(y,Af),v(b,Af),-1)),\"nSurf\")}let De=Fi(Ae),Ee=.02,ze=z(v(v(s.facets,1.2),de),\"vScale1\"),Ie=z(v(le,ze),\"fUvS1\"),ke=bp(N_,Ie,Ee,\"facet1\"),Me=ke.dx,we=ke.dy,Ve=z(it(ho(I(v(Me,Me),v(we,we))),.001),\"gLen1\"),qe=z(Ce(Me,Ve),\"nx1\"),st=z(Ce(we,Ve),\"ny1\"),pt=z(Xr(Ie),\"cellId1\"),Ze=z(Qr(v(Vt(I(v(pt.member(\"x\"),12.9898),v(pt.member(\"y\"),78.233))),43758.5453)),\"cellRand\"),nt=z(Os(I(Ae,Qe(v(qe,.55),v(st,.55),0))),\"nF1\"),ht=z($t(Ae,U),\"ndlS\"),yt=z(v(ne($t(nt,U),ht),2.2),\"ndl1\"),St=bp(N_,z(I(v(le,v(ze,1.7)),rt(3.7,1.2)),\"fUvS2\"),Ee,\"facet2\"),Mt=St.dx,Rt=St.dy,Kt=z(it(ho(I(v(Mt,Mt),v(Rt,Rt))),.001),\"gLen2\"),Wt=z(Ce(Mt,Kt),\"nx2\"),ls=z(Ce(Rt,Kt),\"ny2\"),us=z(Os(I(Ae,Qe(v(Wt,.4),v(ls,.4),0))),\"nF2\"),os=z(v(ne($t(us,D),$t(Ae,D)),2.2),\"ndl2\"),bs=Qe(0,0,1),hs=z(Ge(v(s.refraction,.8),0,1.6),\"facetRefrAmp\"),gs=z(ig(bs,z(Os(I(I(Ae,Qe(v(v(v(qe,.75),hs),xe),v(v(v(st,.75),hs),xe),0)),Qe(v(v(Wt,.55),hs),v(v(ls,.55),hs),0))),\"nRefr\"),Ce(1,I(1,v(s.refraction,.55)))),\"refr\"),Gs=z(i?Ge(v(l.s0.member(\"x\"),-1),0,.6):v(Ge(v(l.s0.member(\"x\"),-1),0,.12),1.6),\"thickEff\"),Ss=z(v(v(v(Gs,I(.55,v(Ze,.9))),s.scale),x),\"offMag\"),or=z(I(Ce(v(Ce(gs.member(\"x\"),it(gs.member(\"z\"),.25)),Ss),d),V),\"totalOffX\"),yr=z(I(v(Ce(gs.member(\"y\"),it(gs.member(\"z\"),.25)),Ss),X),\"totalOffY\"),lr=z(Lo(gs,z(Os(Qe(v(Wt,.7),v(ls,.7),-1)),\"nBack\")),\"bounce\"),Ur=z(v(v(v(Gs,s.scale),x),.8),\"bounceMag\"),so=Ce(v(Ce(lr.member(\"x\"),it(ms(lr.member(\"z\")),.3)),Ur),d),ro=v(Ce(lr.member(\"y\"),it(ms(lr.member(\"z\")),.3)),Ur),_e=z(v(v(I(v(De,.5),.18),I(v(Ze,.7),.3)),x),\"innerW\"),We=I(te,v(v(Ce(gs.member(\"x\"),it(gs.member(\"z\"),.25)),Gs),1.2)),Le=I(J,v(v(Ce(gs.member(\"y\"),it(gs.member(\"z\"),.25)),Gs),1.2)),Be=z(I(v(g2(I(We,.5),I(Le,.5),oe),v(ze,1.4)),rt(9.1,4.7)),\"fUvS3\"),Oe=bp(N_,Be,Ee,\"facet3\"),tt=Oe.dx,_t=Oe.dy,Bt=z(it(ho(I(v(tt,tt),v(_t,_t))),.001),\"gLen3\"),Dt=z(Ce(tt,Bt),\"nx3\"),Nt=z(Ce(_t,Bt),\"ny3\"),Ks=z(Xr(Be),\"cellId3\"),Ds=z(Qr(v(Vt(I(v(Ks.member(\"x\"),31.727),v(Ks.member(\"y\"),57.131))),43758.5453)),\"cellRand3\"),ar=z(v(v(Ge(v(Gs,2.5),0,1),Ge(v(s.refraction,1.2),0,1)),x),\"w3\"),Xo=z(I(v(I(v(Dt,C),v(Nt,E)),.5),.5),\"wedgeLit\"),$r=v(v(I(v(Ds,.9),.55),I(v(Xo,.7),.65)),I(v(ot(.15,.7,Bt),.45),.55)),cr=v(v(ot(.75,1,v(Xo,I(Ds,.4))),ar),.8),za=I(f,Ce(ne(o.uv.member(\"x\"),f),s.innerZoom)),ia=I(h,Ce(ne(o.uv.member(\"y\"),h),s.innerZoom)),Ra=z(I(za,or),\"lensX\"),Wn=z(I(ia,yr),\"lensY\"),Uc=Ge(yt,0,1),Gp=Ge(v(yt,-1),0,1),Fp=it(ne(I(1,v(Uc,s.highlights)),v(Gp,s.shadows)),.02),Oc=Ge(os,0,1),$d=Ge(v(os,-1),0,1),af=ne(I(1,v(Oc,v(s.highlights,.5))),v($d,v(s.shadows,.5))),qi=ne(I(1,v(v(Ge(ht,0,1),s.highlights),.4)),v(v(Ge(v(ht,-1),0,1),s.shadows),.6)),Qs=v(v(Fp,af),qi),Ip=v(s.dispersion,.12),nf=z(v(or,Ip),\"chrOffX\"),lf=z(v(yr,Ip),\"chrOffY\"),cf=I(v(et(ot(.2,.8,Ve),1,Te),.4),.6),vy=v(v(v(I(Qs,v(Te,.25)),cf),et(It(1),$r,ar)),s.brightness),df=z(Ge($t(nt,L),0,1),\"ndh1\"),hy=Ge($t(us,O),0,1),gy=v(ts(df,110),2.2),yy=v(ts(hy,56),.9),by=v(ot(.965,.995,df),3),uf=z(v(v(I(I(I(gy,yy),by),cr),s.highlights),s.brightness),\"flashContrib\"),pf=v(v(v(ts(De,et(It(6),It(1.5),Ge(v(s.fresnelSoftness,.5),0,1))),s.fresnel),2),x),xy=v(s.fresnelColor.member(\"rgb\"),pf),mf=I(v(I(v(y,C),v(b,E)),.5),.5),Vi=z(v(v(ne(Ge(v(Ce(v(ne(p,_),-1),u),16),0,1),Ge(v(Ce(v(p,-1),u),16),0,1)),mf),.35),\"rb2\"),_f=z(ot(0,1,x),\"transition\"),ff=Number(r.cutout)>0,vf=rt(I(o.uv.member(\"x\"),V),I(o.uv.member(\"y\"),X)),hf=ff?o.uv:vf,Ay=z(W(j_,\"unpremultiplyAlpha\",[a.sample(rt(I(Ra,nf),I(Wn,lf)))]),\"sampledR\"),qn=z(W(j_,\"unpremultiplyAlpha\",[a.sample(rt(Ra,Wn))]),\"sampledG\"),Da=z(W(j_,\"unpremultiplyAlpha\",[a.sample(rt(ne(Ra,nf),ne(Wn,lf)))]),\"sampledB\"),Xd=z(W(j_,\"unpremultiplyAlpha\",[a.sample(rt(I(Ra,so),I(Wn,ro)))]),\"innerSample\"),gf=z(W(j_,\"unpremultiplyAlpha\",[a.sample(hf)]),\"baseColor\"),yf=et(Qe(Ay.member(\"x\"),qn.member(\"y\"),Da.member(\"z\")),Qe(Xd.member(\"x\"),Xd.member(\"y\"),Xd.member(\"z\")),kt(_e)),bf=qn.member(\"w\"),Hd=z(v(yf,vy),\"litRgb\"),Yp=Qe(.299,.587,.114),xf=$t(Hd,Yp),Gc=z(et(Hd,s.tintColor.member(\"rgb\"),kt(s.tintIntensity)),\"tinted\"),Wp=v(Gc,Ce(xf,it($t(Gc,Yp),1e-4))),Fc=z(et(gf,Ot(I(I(I(br(Zo(s.tintPreserveLuminosity,.5),Wp,Gc),kt(uf)),xy),kt(Vi)),bf),Ot(_f)),\"composited\"),qp=ff?_f:It(1);return Ot(Fc.member(\"xyz\"),v(Fc.member(\"w\"),qp))}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the crystal shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the crystal shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the crystal shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},cutout:{default:!1,transform:rs,description:\"Cut out alpha outside the crystal shape\",compileTime:!0,ui:{type:\"checkbox\",label:\"Cutout\",group:\"Crystal\"}},...gr({refraction:{description:\"How strongly the crystal refracts content beneath\",ui:{type:\"range\",max:3,group:\"Crystal\"}},dispersion:{default:.5,description:\"Prismatic rainbow dispersion \\u2014 splits light into spectral colors\",ui:{type:\"range\",max:2,group:\"Crystal\"}}}),facets:{default:5,description:\"Symmetry order \\u2014 how many times the facet pattern repeats around the center\",ui:{type:\"range\",min:3,max:24,step:1,label:\"Facets\",group:\"Crystal\"}},...gr({fresnel:{default:.05,description:\"Fresnel rim glow intensity around the crystal boundary\",ui:{type:\"range\"}},fresnelSoftness:{default:1,ui:{type:\"range\",max:2}},fresnelColor:!0,edgeSoftness:{default:0,description:\"Softness of the crystal boundary edge\",ui:{type:\"range\",group:\"Crystal\"}}}),innerZoom:{default:1.5,description:\"Magnification of content seen through the crystal\",ui:{type:\"range\",min:.5,max:3,step:.01,label:\"Inner Zoom\",group:\"Crystal\"}},...gr({lightAngle:{default:270,description:\"Light direction angle in degrees\",ui:{group:\"Lighting\"}}}),highlights:{default:.5,description:\"Additive brightness on light-facing facets \\u2014 never darkens\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Highlights\",group:\"Lighting\"}},shadows:{default:.3,description:\"Darkening on shadow-facing facets \\u2014 never brightens\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Shadows\",group:\"Lighting\"}},brightness:{default:1.2,description:\"Overall crystal brightness \\u2014 higher values push facets toward brilliant white\",ui:{type:\"range\",min:.5,max:3,step:.01,label:\"Brightness\",group:\"Lighting\"}},tintColor:{default:\"#e8e0ff\",transform:ce,description:\"Crystal body tint color\",ui:{type:\"color\",label:\"Tint Color\",group:\"Tint\"}},tintIntensity:{default:0,description:\"How much tint color is applied to the crystal interior\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Tint Intensity\",group:\"Tint\"}},tintPreserveLuminosity:{default:!0,transform:rs,description:\"Preserve original brightness when tinting\",ui:{type:\"checkbox\",label:\"Preserve Luminosity\",group:\"Tint\"}},shape:{default:bue,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var wG=H({name:\"CurlNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Swirling divergence-free flow field that drifts over time\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:2,description:\"Pattern scale (higher = larger patterns)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},...Ja(),...In()},paint:Vs(ua(Qa(HD({rate:.2}),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var xue=sR.grid({resolution:128,history:2,step:[lg.wave({damping:T(\"decay\")}),lg.splat({at:rR({teleportGuard:\"on\"}),amount:oR({max:2}),radius:T(\"radius\")})],derive:{displacement:lg.gradient()},rest:{settlesWhen:\"derived-from-damping\"}}),TG=H({name:\"CursorRipples\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Fluid-like ripple distortion\",props:{intensity:{default:10,description:\"Strength of the ripple distortion\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Intensity\",group:\"Effect\"}},decay:{default:10,description:\"How quickly ripples fade (higher = faster)\",ui:{type:\"range\",min:0,max:20,step:.1,label:\"Decay\",group:\"Effect\"}},radius:{default:.5,description:\"Radius of cursor influence\",ui:{type:\"range\",min:.1,max:1,step:.1,label:\"Radius\",group:\"Effect\"}},chromaticSplit:{default:1,description:\"RGB channel separation along ripple edges\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.1,label:\"Chromatic Split\",group:\"Effect\"}},edges:{default:\"stretch\",description:\"How to handle edges when distortion pushes content out of bounds\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},effect:Qz(xue.output(\"displacement\"),{strength:T(\"intensity\"),chromatic:T(\"chromaticSplit\"),edges:T(\"edges\")})});var Mp=64,SG=_o(1),Aue=.2,wue=1.5,Tue=_z({maxPoints:Mp,subdivisions:3,name:\"cursorTrailScan\"});function Sue(e){let{createDataTexture:t,registerMediaTexture:s,onBeforeRender:r,onCleanup:o,onResize:a,getCpuValue:i,dimensions:l}=e,c=[],d=new Uint16Array(Mp*4);for(let b=0;b<Mp;b++)d[b*4+2]=SG;let u=t({width:Mp,height:1,format:\"rgba16float\",data:d,label:\"cursortrail-points\"});o(()=>u.destroy());let p=s(()=>u.texture),_=Sl({minDrag:.001,teleportGuard:Number.POSITIVE_INFINITY}),f=Math.max(1,Math.round(l.width)),h=Math.max(1,Math.round(l.height));a(({width:b,height:x})=>{f=Math.max(1,Math.round(b)),h=Math.max(1,Math.round(x))});let g=0,y=!1;return r(b=>{let{pointer:x,deltaTime:P}=b,C=Math.max(0,P??0),E=Math.min(C,.1),R=i(\"length\")??.5,U=(i(\"radius\")??.5)*.1,L=f/h,k=_.update(x,E),D=Math.min(C,.25)/Math.max(.1,R);for(let F of c)F.age=Math.min(1,F.age+D);for(;c.length>0&&c[0].age>=1;)c.shift();if(k.dragDist>.001){let F=Math.sqrt(k.dx*L*(k.dx*L)+k.dy*k.dy),q=Math.min(1,Math.max(Aue,Math.sqrt(k.smoothSpeed/wue)));if(c.length===0)c.push({x:k.prevX,y:k.prevY,age:0,link:0,width:q}),c.push({x:k.x,y:k.y,age:0,link:1,width:q}),g=F;else{let Y=c[c.length-1];if(Y.x=k.x,Y.y=k.y,Y.age=0,Y.width=q,g+=F,g>=Math.max(.006,U*.5)){let V=c.length;V>=3&&c[V-2].link===1&&(c[V-2].x=.25*c[V-3].x+.5*c[V-2].x+.25*c[V-1].x,c[V-2].y=.25*c[V-3].y+.5*c[V-2].y+.25*c[V-1].y),c.push({x:k.x,y:k.y,age:0,link:1,width:q}),g=0}}for(;c.length>Mp-1;)c.shift()}let O=c.length>0;if(O||y){let F=c.length>0?c.length+1:0;for(let q=0;q<Mp;q++){let Y=q*4;if(q<F){let V=c[Math.min(q,c.length-1)];d[Y]=_o(V.x),d[Y+1]=_o(V.y),d[Y+2]=_o(V.age),d[Y+3]=_o(q===c.length||V.link===1?V.width:-V.width)}else d[Y]=0,d[Y+1]=0,d[Y+2]=SG,d[Y+3]=0}u.write(d)}y=O}),p}function Pue(e,t){let{propValues:s,uniforms:r}=e,o=s.colorSpace??0;if((s.stopCount??0)>1)return En(t,{colorsArray:r.colorsArray,positionsArray:r.positionsArray,convertedColorsArray:r.convertedColorsArray,stopCount:r.stopCount},o);let a=Mn;return W(a.mixColorsVariants[o]??a.mixColorsLinear,\"mixColors\",[r.colorA,r.colorB,t])}var PG=H({name:\"CursorTrail\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Animated trail effect that tracks cursor movement\",usesPointer:!0,props:{colorA:{default:\"#00aaff\",transform:ce,description:\"Color of fresh trails\",ui:{type:\"color\",label:\"Start Color\",group:\"Colors\"}},colorB:{default:\"#ff00aa\",transform:ce,description:\"Color trails transition to as they fade\",ui:{type:\"color\",label:\"End Color\",group:\"Colors\"}},stops:Ms(),radius:{default:.5,description:\"Base radius of the trail stroke\",ui:{type:\"range\",min:.5,max:2,step:.1,label:\"Radius\",group:\"Effect\"}},length:{default:.5,description:\"How long the trail persists (in seconds)\",ui:{type:\"range\",min:.1,max:2,step:.1,label:\"Trail Length\",group:\"Animation\"}},shrink:{default:1,description:\"How much the stroke tapers as it fades out (0 = no taper, 1 = full taper)\",ui:{type:\"range\",min:0,max:1,step:.1,label:\"Shrink Amount\",group:\"Effect\"}},softness:{default:0,description:\"Edge softness of the trail (0 = crisp paint edge, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},gpu:{fragment:e=>{let{ctx:t,uniforms:s,gpu:r}=e;if(!r?.root)return xt;let o=W(Tue,\"cursorTrailScan\",[Sue(e).accessor(),t.uv,t.aspect,s.radius,s.shrink,s.softness,t.viewportSize.member(\"y\")]),a=o.member(\"x\"),i=o.member(\"y\"),l=Pue(e,a);return Ct(l.member(\"rgb\"),l.member(\"a\").mul(i))}}});var CG=768,MG=\"rgba16float\",I2=Iz({res:CG,format:MG,namePrefix:\"dataMosh\",blockMix:{coarseScale:2.6,coarsePick:.35},generationLoss:{levels:18,amount:.12,decay:.9985}}),Cue=I2.kernel,EG=H({name:\"DataMosh\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Corrupted-codec motion smearing \\u2014 macroblocks lock in place and drag their pixels across the frame in liquid trails, each recovering on its own clock, like a video stream that lost its keyframes\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:.7,description:\"How much of the image locks and smears \\u2014 the fraction of corrupted macroblocks\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Mosh\"}},blockSize:{default:48,description:\"Macroblock size \\u2014 the granularity of the corruption\",ui:{type:\"range\",min:30,max:150,step:1,label:\"Block Size\",group:\"Mosh\"}},drift:{default:.35,description:\"Motion vector strength \\u2014 how fast corrupted areas drag their pixels away\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Drift\",group:\"Mosh\"}},churn:{default:.4,description:\"How often each block re-rolls its fate \\u2014 low values let corrupted regions melt for a long time before recovering\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Churn\",group:\"Mosh\"}},blend:{default:1,description:\"Blend between the live image and the moshed stream\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Blend\",group:\"Mosh\"}},speed:{default:1,description:\"Simulation speed. 0 freezes the decay in place.\",ui:{type:\"range\",min:0,max:4,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed for the corruption layout\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}}},...Lg((e,t)=>{let{getCpuValue:s}=e;return{size:CG,format:MG,speedProp:\"speed\",paramsSchema:I2.Params,bindGroups:({childTexture:r,display:o,paramsBuffer:a},i,l)=>t.createBindGroup(I2.layout,{src:r,prev:i.state,next:l.state,display:o,params:a}),step:Cue,values:({dt:r,localTime:o})=>({time:o,dt:r,seed:s(\"seed\")??0,intensity:s(\"intensity\")??.7,blockSize:s(\"blockSize\")??48,drift:s(\"drift\")??.35,churn:s(\"churn\")??.4})}}),gpu:{fragment:({childNode:e,ctx:t,computeOutputs:s,uniforms:r,convertToTexture:o})=>{if(!e)return xt;let a=s?.display,i=(s?.childTexture??o(e)).sample(t.uv);return a?W(Yt,\"unpremultiplyAlpha\",[ea(i,a.sample(t.uv,\"linearClamp\"),r.blend)]):W(Yt,\"unpremultiplyAlpha\",[i])}}});var kG=H({name:\"DiamondGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Diamond-shaped gradient radiating from a center point using Manhattan distance\",acceptsUVContext:!0,props:{colorA:{default:\"#4ffb4a\",transform:ce,description:\"Color at the center of the diamond\",ui:{type:\"color\",label:\"Color A (Center)\",group:\"Colors\"}},colorB:{default:\"#4f1238\",transform:ce,description:\"Color at the outer edges of the diamond\",ui:{type:\"color\",label:\"Color B (Edge)\",group:\"Colors\"}},stops:Ms(),center:{default:{x:.5,y:.5},transform:Xe,description:\"Center point of the diamond\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},size:{default:.7,description:\"Extent of the gradient \\u2014 controls how far Color A reaches before transitioning to Color B\",ui:{type:[\"range\",\"map\"],min:.01,max:2,step:.01,label:\"Size\",group:\"Effect\"}},rotation:{default:0,description:\"Rotation in degrees \\u2014 tilts the diamond into a rhombus\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},repeat:{default:1,description:\"Number of times the gradient repeats outward. Values above 1 create concentric diamond or square bands.\",ui:{type:[\"range\",\"map\"],min:1,max:16,step:.5,label:\"Repeat\",group:\"Effect\"}},roundness:{default:0,description:\"Morphs from a sharp diamond (0) to a square (1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Roundness\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(mg(Oi.diamond({center:T(\"center\"),size:T(\"size\"),rotation:T(\"rotation\"),roundness:T(\"roundness\")}),T(\"repeat\")),Us(T(\"colorSpace\")))});var BG=H({name:\"DiamondWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away through a lattice of growing diamonds\",props:{progress:{default:.5,description:\"How far the diamonds have grown (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},size:{default:.15,description:\"Size of each diamond cell as a fraction of the frame width\",ui:{type:[\"range\",\"map\"],min:.02,max:.5,step:.01,label:\"Size\",group:\"Transition\"}},feather:{default:.1,description:\"Softness of each diamond edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Wipe from the cell corners inward instead\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.cells(T(\"size\")).diamond(),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Diamond Wipe shader.\"});var zG=H({name:\"DiffuseBlur\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Grain-like pixel displacement at random\",props:{intensity:{default:30,description:\"Intensity of the diffuse blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},edges:{default:\"stretch\",description:\"How to handle edges when distortion pushes content out of bounds\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}},chromatic:{default:0,description:\"Scatters red and blue separately for a sparkly color grain\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Chromatic\",group:\"Effect\"}}},effect:bU({amount:T(\"intensity\"),edges:T(\"edges\"),chromatic:T(\"chromatic\")})});var Mue={twoAxis:0,directional:1},RG=H({name:\"DisplacementMap\",role:\"filter\",species:\"gather\",category:\"Distortions\",description:\"Distorts child content using another layer's pixels as a displacement map\",boundingBoxDeclaration:{aspectRatio:null},props:{source:{default:\"\",description:\"The layer whose pixels drive the displacement \\u2014 its red/green (or luminance) push the content around\",compileTime:!0,ui:{type:\"layer\",label:\"Source Layer\",group:\"Effect\"}},amount:{default:.3,description:\"Overall displacement strength\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Amount\",group:\"Effect\"}},channelMode:{default:\"twoAxis\",description:\"How the source drives the push \\u2014 red\\u2192X / green\\u2192Y, or luminance along a fixed angle\",compileTime:!0,transform:e=>Mue[e]??0,ui:{type:\"select\",options:[{label:\"Red / Green\",value:\"twoAxis\"},{label:\"Luminance\",value:\"directional\"}],label:\"Channels\",group:\"Effect\"}},angle:{default:0,description:\"Push direction in degrees (used by the Luminance channel mode)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\",condition:{channelMode:\"directional\"}}},edges:{default:\"mirror\",description:\"How to handle edges when distortion pushes content out of bounds\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},effect:lU({source:T(\"source\"),amount:T(\"amount\"),channels:T(\"channelMode\"),angle:T(\"angle\"),edges:T(\"edges\")})});var Eue=e=>({bayer2:0,bayer4:1,bayer8:2,clusteredDot:3,blueNoise:4,whiteNoise:5,floydSteinberg:6})[e]??1,kue=e=>e===\"source\"?1:0,DG=iD({size:T(\"pixelSize\")}),LG=H({name:\"Dither\",role:\"filter\",species:\"gather\",category:\"Stylize\",description:\"Dithering effect with multiple pattern options\",blendWithChildren:!1,boundingBoxDeclaration:{aspectRatio:null},props:{pattern:{default:\"bayer4\",transform:Eue,compileTime:!0,description:\"Dithering pattern algorithm\",ui:{type:\"select\",options:[{label:\"Bayer 2x2\",value:\"bayer2\"},{label:\"Bayer 4x4\",value:\"bayer4\"},{label:\"Bayer 8x8\",value:\"bayer8\"},{label:\"Clustered Dot\",value:\"clusteredDot\"},{label:\"Blue Noise\",value:\"blueNoise\"},{label:\"White Noise\",value:\"whiteNoise\"},{label:\"Floyd-Steinberg\",value:\"floydSteinberg\"}],label:\"Pattern\",group:\"Effect\"}},pixelSize:{default:4,description:\"Size of dithering pixels\",ui:{type:\"range\",min:1,max:20,step:1,label:\"Pixel Size\",group:\"Effect\"}},threshold:{default:.5,description:\"Luminance threshold for dithering\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Threshold\",group:\"Effect\"}},spread:{default:1,description:\"How much of the luminance range participates in dithering (lower = more solid areas)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Spread\",group:\"Effect\"}},colorMode:{default:\"custom\",transform:kue,compileTime:!0,description:\"How colors are determined\",ui:{type:\"select\",options:[{label:\"Custom Colors\",value:\"custom\"},{label:\"Source Colors\",value:\"source\"}],label:\"Color Mode\",group:\"Colors\"}},colorA:{default:\"transparent\",transform:ce,description:\"Dark color for dithering\",ui:{type:\"color\",label:\"Dark Color\",condition:{colorMode:\"custom\"},group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Light color for dithering\",ui:{type:\"color\",label:\"Light Color\",condition:{colorMode:\"custom\"},group:\"Colors\"}}},effect:cD({grid:DG,levels:lD({grid:DG,pattern:T(\"pattern\"),threshold:T(\"threshold\"),spread:T(\"spread\")}),mode:T(\"colorMode\"),colors:[T(\"colorA\"),T(\"colorB\")]})});var UG=H({name:\"DotGrid\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Grid of dots with optional twinkling animation\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{color:{default:\"#ffffff\",description:\"The color of the dot\",transform:ce,ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},density:{default:30,description:\"The number of dots along the canvas height (the width fits as many as the aspect ratio allows)\",ui:{type:\"range\",min:1,max:200,step:1,label:\"Density\",group:\"Effect\"}},dotSize:{default:.3,description:\"The size of each dot, zero (0) being invisible, one (1) filled the grid with no gaps\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Dot Size\",group:\"Effect\"}},offset:{default:0,description:\"Horizontal stagger of alternating rows (0.5 = classic polka-dot brick offset)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Row Offset\",group:\"Effect\"}},speed:{default:0,description:\"Animates the rows drifting horizontally (0 = static)\",ui:{type:\"range\",min:-3,max:3,step:.01,label:\"Speed\",group:\"Animation\"}},speedVariance:{default:.3,description:\"Per-row random speed variance for irregular drifting motion\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Speed Variance\",group:\"Animation\"}},twinkle:{default:0,description:\"Intensity of the twinkle effect (0 = off, 1 = full twinkle)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Twinkle\",group:\"Animation\"}}},mapSampleUVs:_g({cells:T(\"density\"),props:[\"dotSize\",\"twinkle\"]}),paint:fp(T(\"color\"),KR({density:T(\"density\"),dotSize:T(\"dotSize\"),offset:T(\"offset\"),speedVariance:T(\"speedVariance\"),twinkle:T(\"twinkle\")}))});var OG=H({name:\"DropShadow\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Adds a soft shadow behind the child content based on its alpha silhouette\",props:{color:{default:\"#000000\",transform:ce,description:\"Shadow color\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},distance:{default:.1,description:\"How far the shadow is offset from the content\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Distance\",group:\"Effect\"}},angle:{default:135,description:\"Direction the shadow is cast (compass degrees: 0=up, 90=right, 135=lower-right, 180=down)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},blur:{default:5,description:\"Shadow softness (blur radius in pixels)\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.5,label:\"Blur\",group:\"Effect\"}},intensity:{default:.5,description:\"Shadow intensity \\u2014 how strong/visible the shadow is\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}},cutout:{default:!1,transform:rs,compileTime:!0,description:\"Hide the original layer and show only the shadow\",ui:{type:\"checkbox\",label:\"Cutout\",group:\"Effect\"}}},effect:$o(UU(),[FU({coverage:GU({at:OU({angle:T(\"angle\"),distance:T(\"distance\")}),blur:T(\"blur\")}),color:T(\"color\"),intensity:T(\"intensity\"),cutout:T(\"cutout\")})])});var GG=H({name:\"Duotone\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Map colors to two tones based on luminance\",props:{colorA:{default:\"#ff0000\",transform:ce,description:\"First color (used for darker areas)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#023af4\",transform:ce,description:\"Second color (used for brighter areas)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},blend:{default:.5,description:\"Blend point between the two colors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Blend\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},effect:m8({colorA:T(\"colorA\"),colorB:T(\"colorB\"),blend:T(\"blend\"),colorSpace:T(\"colorSpace\")})});var FG=H({role:\"shape\",name:\"Ellipse\",description:\"Ellipse with independently adjustable horizontal and vertical radii\",shape:{distance:({x:e,y:t},s)=>iR(e,t,s(\"radiusX\"),s(\"radiusY\")),colorDescription:\"Fill color of the ellipse\",centerDescription:\"Center position of the ellipse\",bounds:{size:{width:\"radiusX\",height:\"radiusY\"}},shapeProps:{radiusX:{default:.35,description:\"Horizontal semi-axis radius\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Width\",group:\"Shape\"}},radiusY:{default:.2,description:\"Vertical semi-axis radius\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Height\",group:\"Shape\"}}}}});var Bue=e=>e===\"spiral\"?2:e===\"line\"?0:1,IG=H({name:\"Engraving\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Copper-plate line engraving \\u2014 the image is redrawn as flowing line work whose weight swells with darkness, lines displaced by the form like a banknote portrait: a single plate, cross-hatched shadow plates, or one continuous spiral cut\",blendWithChildren:!1,props:{style:{default:\"crosshatch\",transform:Bue,compileTime:!0,description:\"The engraving cut: a single line plate, cross-hatched plates building up the shadows, or one continuous spiral\",ui:{type:\"select\",options:[{label:\"Line\",value:\"line\"},{label:\"Cross-Hatch\",value:\"crosshatch\"},{label:\"Spiral\",value:\"spiral\"}],label:\"Style\",group:\"Lines\"}},frequency:{default:90,description:\"Line density \\u2014 how many engraved lines span the height of the canvas\",ui:{type:[\"range\",\"map\"],min:20,max:300,step:1,label:\"Frequency\",group:\"Lines\"}},angle:{default:8,transform:cs,description:\"Direction of the base line work (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Lines\",condition:{style:\"line\"}}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center the spiral coils outward from\",ui:{type:\"position\",label:\"Center\",group:\"Lines\",condition:{style:\"spiral\"}}},relief:{default:.6,description:\"How strongly the image brightness displaces the lines \\u2014 the engraved 'lines climb over the form' effect\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Relief\",group:\"Lines\"}},waviness:{default:.35,description:\"Organic meander of the line work, like a hand-pulled burin stroke\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Waviness\",group:\"Lines\"}},contrast:{default:1.15,description:\"Tonal contrast applied before the lines are cut \\u2014 higher pushes mid-tones toward pure line or pure paper\",ui:{type:[\"range\",\"map\"],min:.25,max:3,step:.01,label:\"Contrast\",group:\"Tone\"}},inkColor:{default:\"#1a1410\",transform:ce,description:\"Color of the engraved ink\",ui:{type:\"color\",label:\"Ink\",group:\"Colors\"}},paperColor:{default:\"#f4eee2\",transform:ce,description:\"Paper color shown between the lines\",ui:{type:\"color\",label:\"Paper\",group:\"Colors\"}}},effect:J8({style:T(\"style\"),frequency:T(\"frequency\"),angle:T(\"angle\"),center:T(\"center\"),relief:T(\"relief\"),waviness:T(\"waviness\"),contrast:T(\"contrast\"),ink:T(\"inkColor\"),paper:T(\"paperColor\")})});var YG=H({name:\"ErosionNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Branching, hydraulic-erosion ridges carved into noise\",acceptsUVContext:!0,props:{...ma(),scale:{default:1.5,description:\"Pattern scale (higher = larger patterns)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},...Ja()},paint:Vs(ua(Qa(gp(XD()),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var WG=H({name:\"Exposure\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Multiplicative exposure (gain) on the child. Unlike additive brightness, it scales values around black \\u2014 blacks stay black while highlights can be pushed past 1.0 to feed HDR bloom and tone-mapping roll-off. Pointwise, so no extra render pass is needed.\",props:{exposure:{default:1,description:\"Brightness multiplier (gain). 1 = unchanged, >1 pushes highlights past white for HDR bloom, <1 darkens.\",recompile:Lt(1),ui:{type:[\"range\",\"map\"],min:0,max:8,step:.1,label:\"Exposure\",group:\"Adjustments\"}}},effect:a8(T(\"exposure\")),identityWhen:cg(\"exposure\",1),missingChildMessage:\"You must pass a child component into the Exposure shader.\"});var VG=jR({angle:T(\"angle\"),density:T(\"density\"),speedVariance:T(\"speedVariance\"),trailLength:T(\"trailLength\"),strokeWidth:T(\"strokeWidth\"),rounding:T(\"rounding\"),balance:T(\"balance\")}),qG=Zr({fill:T(\"colorB\"),stroke:T(\"colorA\"),mask:VG.fade,space:T(\"colorSpace\")}),NG=H({name:\"FallingLines\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Directional falling lines with a leading-to-trailing color fade\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#ffffff\",transform:ce,description:\"Color at the leading edge of each line\",ui:{type:\"color\",label:\"Lead Color\",group:\"Colors\"}},colorB:{default:\"#ffffff00\",transform:ce,description:\"Color at the trailing edge (transparent by default)\",ui:{type:\"color\",label:\"Trail Color\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for interpolation between lead and trail colors\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},angle:{default:90,description:\"Direction of movement in degrees (90=down, 270=up, 0=right, 180=left)\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},speed:{default:.5,description:\"Movement speed\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Speed\",group:\"Animation\"}},speedVariance:{default:.3,description:\"Per-line speed variance (0=uniform, 1=high variance)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Speed Variance\",group:\"Animation\"}},density:{default:15,description:\"Number of line columns across the canvas\",ui:{type:\"range\",min:1,max:60,step:1,label:\"Density\",group:\"Effect\"}},trailLength:{default:.35,description:\"Streak length relative to spacing (0=point, 1=continuous)\",ui:{type:[\"range\",\"map\"],min:.01,max:1,step:.01,label:\"Trail Length\",group:\"Effect\"}},balance:{default:.5,description:\"Color mix midpoint (0.5=linear, 0=all trailing/colorB, 1=all leading/colorA)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Balance\",group:\"Colors\"}},strokeWidth:{default:.15,description:\"Line thickness as fraction of column width (0=hairline, 1=full width)\",ui:{type:[\"range\",\"map\"],min:.02,max:1,step:.02,label:\"Stroke Width\",group:\"Effect\"}},rounding:{default:1,description:\"Rounds the leading edge (0=flat/square, 1=fully rounded cap)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Rounding\",group:\"Effect\"}}},paint:fp(qG,Y_(I_(qG),VG.mask))});var $G=H({name:\"FilmGrain\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Analog film grain texture overlay, weighted toward darker areas\",extraFields:{animTime:{schema:n,initial:0}},props:{strength:{default:.5,description:\"Intensity of the film grain noise\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.025,label:\"Strength\",group:\"Effect\"}},bias:{default:2,description:\"Concentrates grain in darker areas. Higher values focus grain more heavily on shadows; 0 applies grain uniformly.\",ui:{type:\"range\",min:0,max:10,step:.1,label:\"Bias\",group:\"Effect\"}},animated:{default:!1,description:\"When enabled, the grain pattern changes each frame for a dynamic film effect\",ui:{type:\"checkbox\",label:\"Animated\",group:\"Effect\"},transform:e=>e?1:0}},effect:u8({strength:T(\"strength\"),bias:T(\"bias\"),animated:T(\"animated\")}),identityWhen:Uo(\"strength\")});var zue=[{label:\"Portrait 400\",value:\"portrait400\"},{label:\"Vivid 100\",value:\"vivid100\"},{label:\"Chrome 64\",value:\"chrome64\"},{label:\"Slide 100\",value:\"slide100\"},{label:\"Velvet 50\",value:\"velvet50\"},{label:\"Everyday 400\",value:\"everyday400\"},{label:\"Pastel 400\",value:\"pastel400\"},{label:\"Warm 200\",value:\"warm200\"},{label:\"Instant\",value:\"instant\"},{label:\"Mono 400\",value:\"mono400\"}],XG={portrait400:\"BAQECAQEEwUEIwUDNwICTwABagAAhgAAoQAAuQAAzwAA4AAA7gAA+wwA/yAA/y4A/zkABAgFBwgEEwsEIw0DNwsBTwgAagEAhQAAoAAAuAAAzgAA3wAA7gsA+h0A/yoA/zQA/z8AAhAGBhAGDxIEIhcCOBgAUBkAahcAhRQAoBAAuA4AzRQA3hwA7CUA+S4A/zcA/z8A/0cAAB0GAhwICh4GHCIDNCcATioAaisAhSwAnywAty4AzDIA3DUA6jsA+EEA/0cA/00A/1QAAC4IACwKAiwLFC8GLDUASTsAZz8AhEIAnkMAtEYAyUgA2UsA6E8A9VMA/1gA/1wA/2EAAEAMAD8NAD0QCD8NI0UGP0sAY1MAf1cAmlkAsVsAxV0A1l8A5WIA8mUA/2gA/2sA/24AAFQPAFQSAFIVAFAWGFQQNVsFVGEAeGkAlG0Aq28AwHEA0nIA4XQA73YA/HgA/3oA/30AAGgWAGgYAGUaAGIdCWQbLGkUSW8FaXYAjH0ApoEAuoMAzYUA3YUA7IYA+ogA/4gA/4oAAHseAHofAHghAHUkAHMmI3chQHwZXoIKfIgAnI4AtZIAx5QA2JUA6JUA9pUA/5YA/5cAAIolAIsnAIkoAIYrAIMuHIQtOoknVY0gcpITj5cAq50Aw6EA1KMA46MA8qMA/6MA/6MAAJovAJoxAJkyAJc0AJM2GpE4OJU1UpkwbJ0phqIeoKYKuasAz68A37AA7rEA/bEA/7EAAKk7AKk9AKg9AKY/AKRAIaBCO6FCU6U/a6k6gqw0mrAssLQgxrgE27sA670A+r0A/74AALhKALdKALdLALVMEbNNMLBPRK5RWLFObLRMgbdHlrtCqr47vsEx0sQi58gT9skA/8oAAMVaAMVbBMRbIcNcNMJdRb9eU71gYr5gc8BfhMJblcVYqMhUuctOzM5G39E889Uz/9YsN9NvO9NwQNJwSNFxUdBxXM9zaM51csx3fs11jNB0mtNyqNVvudhryNpl2t1f699X/uJQYOKIZOOKZ+KKbOKLceGMeN+Ngd+Pid2Skt6Tm9+RpeGQsOOOvOWLyuiI2OqE5+x+9+52ifKmi/KmjPGnkPGok/ComfCqn/Cspe+urO6xse+wuPGvwPKvyfWu0vas3fiq6fqo9fukBAUJCgQJEwMKJAEKOAAJUAAIawAGhwACoQAAugAAzwAA4AAA7QAA+gEA/xoA/ykA/zYAAwkICQoKFQoJJAkIOAcIUAIGagAEhgABoQAAuQAAzQAA3wAA7QAA+hcA/yYA/zEA/zwAARQLCBMLEhUKJBYHORcGURcEbBQBhg8AoAgAuAYAzQ0A3hcA7SEA+SsA/zQA/z0A/0UAACINAyEODSAOHiMKNygFUCkAayoAhioAoSoAuCwAzC8A3DMA6zkA+D8A/0UA/0sA/1IAADIPADARBi4TFzEQLjYJTDwAaUAAhUIAn0MAtkQAyUcA2koA6E4A9VIA/1YA/1sA/18AAEMSAEMUAEEWDkEWJkcRQk0FZFQAgFgAm1kAsloAxVwA1l8A5WEA8mQA/2cA/2oA/20AAFcWAFgZAFUbAFIeHVYZOFwRVmIBemoAlW0ArG8AwXEA0nEA4nQA73UA/XgA/3oA/3wAAGocAGseAGkgAGUjEGUiLmocS3ATa3cAjX4ApoEAu4MAzYQA3YUA7IYA+ocA/4gA/4kAAH0jAH0kAHsmAHgoAXQrJ3goQn0hX4MWfogBno8AtZIAyJQA2JUA6JUA9pUA/5UA/5YAAIwqAI0sAIsuAIgvAIUyIYUyPIktV44nc5MdkJgKrJ4Aw6EA1KIA46QA86QA/6MA/6MAAJs0AJs1AJs2AJg4AJY6H5E8OpU5VJk1bZ4vh6ImoaYYuasA0K8A4LAA77EA/bEA/7EAAKo/AKtBAKpCAKdDAKVEJqJGPaFGVKVDa6k/g6w5mrEysLQnxrgV3LwA670A+r4A/74AALhMALhOALhOALZPGLRQNLFSRq5TWbFSbbRPgbdLlrtGqr4/vsE308Qr58gf9skG/8oAAMVcAMZeDsVeJcRfN8JfRsBhVb5iY75jdMBhhMJelsVbqMlXustRzM5K39FA89U4/9YzONNwPdNxQtJyStJzVNF0XtB1aM53c8x5f853jNB2mtJ0qdVxuNhtydtp2dxi699Z/uJUYuOKZeOLaOKLbOGMcuGNeeCPgt+Rit6Tk96Um9+SpeGRsOOPveaMyuiJ1+mE5ut+9+13ifKmi/KnjfGnkPGolPGqmfGrn/Ctpu+vrO6ysu+xuPGwwfOwyfSu0vas3fir6fqp9fulBQUUDQQVGAMXJQEXOgAZUgAWbQATiQAUowASuwAO0AAI4AAC7gAA+wAA/xUA/yYA/zQAAgsTCQoUFwkWJggXOwMWUwAUbQAViAATogAQugAMzwAG3wAA7gAA+hAA/yEA/y8A/zoAABYWBhYWFRcWJxcWOxYVUxQVbRATiQcRowANuwAIzgEB3g8A7RwA+ScA/zEA/zsA/0QAACgZAicaEyUZIiYYOykUVCkRbigPiCgLoygGuSgAzSwA3TAA6zYA+D0A/0MA/0oA/1EAADkeADccCjUdHDQdMjkYTz4SbEAJiEAAoUEAt0MAykUA20gA6UwA9lAA/1UA/1oA/14AAEoeAEkeAEcgF0QjKkkeRU8YZ1YNg1gAnVkAtFoAx1sA110A5WAA82QA/2YA/2kA/20AAF4hAF0iAFskB1cnIlgmO14gWWQXfWsFl28ArnAAwnAA03EA4nMA8HUA/XcA/3kA/3sAAHAlAHAnAG4pAGorGmctM2woTnIibXgXj38Ap4IAu4MAzoQA3oQA7IUA+oYA/4gA/4kAAIEsAIEtAH8uAHwwDXgzLHkxRn8sYYQlf4kan48BtpMAyJQA2ZUA6JUA9pUA/5UA/5YAAJAzAI80AI82AIw3AIg5J4Y6QIo2WY8xdZQqkZkfrZ8Iw6IA1KMA5KQA86MA/6MA/6MAAJ47AJ49AJ0+AJo+AJhAJpRCP5ZBVpo9bp44iKMxoqgouqwX0K8B4LAA77EA/bAA/7AAAKxFAKxHAKtHAKlIA6dKLKRLQaJMVqVJbalGg61Bm7A7sbUyx7km3Lwa670A+r0A/74AALlSALlTALlTALdUILVVOLNWSbBYW7FXbrRVgrhRlrtNq75Hv8E/08U258gt9skf/8oFAMZgAsZiF8ViKsViO8NkScFkV79mZL1ndMBlhcNjlsZgqMlcuctXzM9Q39FI8tVB/9Y9OtRzP9R0RNN0TNN2VdJ3YNF4as95dM17gM57jNB5mtJ2qdV0udhxyNps2t1n699e/uJaYuOLZeOMaOKNbOKNc+GOeuGQgt+Sit6Uk96VnN+UpuGTsOORveaPyueL2OmH5uuB9+17ivKni/KojfKokPKplPGqmfCrn++tpu+wre+ysu+yufGywfOxyfWw0/au3fes6fmq9fumAwUlDAQmGwIpLAArPAAqVgAucgAtjQArpwAmvwAm0QAm4QAk7wAh+wAd/w8a/yQV/zMRAAskBwsmGQooLAcrPAMqVgAtcQArjAAopgAlvQAn0QAl4QAi7gAf+wYb/x4Y/ywT/zkPABgkBBglFRgoKxcrPhUrVxAqcgQpjAAopgAovQAm0AAj4AQg7hcd+iQa/zAV/zkQ/0MKACkpACkpDyopJyoqPioqVykocSgnjCcnpiUluyUizykg3i0c7DQZ+TsU/0IQ/0kJ/1AEAEEtAEAtCj8tJDotNz0rVEAncUAji0AhpEEfuUIczEQZ3EcV6ksR908L/1QE/1kA/14AAFMxAFQyAFExHk4xMU0xS1Msa1gmh1kfoFgWtVkRyFsN2F0H52AC9GMA/2UA/2gA/2wAAGY1AGY1AGQyEGA0LFw2QWEyXmgsgG4lmnAasHAMxHAA1HEA43MA8HQA/ncA/3gA/3sAAHc2AHg2AHU2AHI4JWw6OW84U3U0cHstkoEjqYMXvYQEz4QA34MA7YUA+oYA/4cA/4kAAIg5AIc6AIU7AII9G34/NHs/S4E7ZYY2gosvoZEkt5QYyZQC2pQA6ZUA95QA/5QA/5YAAJQ/AJVBAJNBAJFCEo1EMYhGRoxDXpFAeJU6k5ozr6ApxKMd1aMH5KQA86MA/6IA/6IAAKJGAKJIAKFIAJ5JEJxLMZhMRZdMWptJcqBFiqRAo6g5vK0v0LAm4LAV77AA/bAA/7AAAK9PAK9QAK5RAKxSGqpTNadUR6NVW6ZTcKpQha5Nm7JIsrVCyLk53Lwy670k+r4O/70AALxaALtbALpbC7lbK7ddP7VeTrJfXrFfcLRcg7hal7tWq79Sv8JM08VE58k+9sk1/8onAsdnFshoI8dpMcZoQMVpTsNrWsBrZ75tdcBrhsNql8ZnqMlkusxgzM9a39FS89VO/9ZJPtV4Q9V5SNR5T9N6WNN7YtF8bM99ds1/gc5/jtB+m9J7qdV6udh3ydtz2t1u699m/uNjZOOOaOSQauOQb+ORdeKSe+GTg+CUi96XlN2Ynd+YpuGWseOUveWSyuiQ2OmL5+yG9+2AifKoi/KpjfKpkPKqlPGrmvGtn/CupvCxre+zs++0ufGzwfOzyvWy0/ew3feu6fqs9fqoAAU6BgQ8GgE+MgBDSABGWABDdQBIkQBJqwBHwQBD1AA+5QA68AA+/AA9/xA7/yU5/zQ3AAw5AQw7GAs+MAZCSABFWABDdQBHkABHqgBFwQBC1AA84wA87wA+/AY8/x45/yw3/zo2ABo4ABo6Exk9LRhBRxVFWRFDdQJGkQBFqgBDwQA/0wA94QE+7hY9+yQ7/zA5/zo2/0Q1ACo6ACo6DSs7JytARCxEWilEdiZEkSFCqR1AviVB0Cc+4C097TM6+Ts4/0I2/0g0/1AzAEBAAEBAAEFBIUFCPkJCWUJCdEJBj0JApkI/vEI9zUQ63Uc560s39081/1Qz/1kx/10vAF5GAF5HAF1GIFpGO1VFUVhDcVs/jFk6o1k4uFo3yVs12V006GAy9WIw/2Ut/2kr/2wqAHBJAHFKAG9LIGxKNmZISWZIZGxEhHA+nXA4s3AxxXAu1XEs5HMq8XUp/ncm/3gk/3sjAIFPAIFRAIBOBHxKL3dLQ3JLWXlJdn5EloM+q4Q3v4Qu0YQl4IQg7oUe+4Yc/4cZ/4kYAJBRAI9RAI5OAItNKIZOQIFQUoROaolLho5FpJM/uZU4y5Uv25Uj6pQT+JQJ/5QE/5UBAJtSAJxSAJpRAJhSJZRSPZBUTo5TY5NRe5dNlpxIsaFBxaM71qQx5aQl9KMS/6IA/6IAAKdVAKdWAKZXAKRXJaFYPZ1ZTplaYJxXdaFVjKVRpapMva5G0bBA4LE277Eq/bAX/7AAALNcALNdALJdBbBdK65fQKtgUKdgYKdgc6tdh69bnbNXsrZSyLpM3L1H7L4++r4z/74jAL5lAL5lAr1lIbxmNrpnR7hnVrVoY7Jpc7VnhbhlmLxirL9fwMJa08ZU58lQ9spI/8o+GspwJMpxLclwOshxRsZxU8VyX8J0a791eMF0h8Nyl8ZwqcltusxqzM9l4NJg89Vc/9dYQ9Z+R9Z/TNaAU9WAXNSBZdOCb9GDec+Eg86Fj9CEnNODqtWAudh+ydt72d127OBx/uNuZuSSaeSTbOWUceSVduOVfeKWheGYjd+Zld6cnt+cp+GasuOZveaXyueU2OmR5+uM9+6GivOqjPOrjfKrkfOslPGtmvKvn/GwpvCyre+1tO+2uvG1wfK1yfSz0/ay3few6fmu9vusAARSAANTFgBWMQBbUQBiaQBldgBfkwBkrgBoxABl1gBh5QBc8wBY/wBT/wFU/y5Y/ztXAA1QAAxSFAtWMAZaTwBhaQBkdgBekwBjrgBmwwBj1gBg5QBb8gBX/gBS/x9X/zRX/0BWABtQABpTDhpVLRlaTRZgZw9jdwZflABkrgBlxABi1QBe5ABZ8QBV/BpW/zZZ/z9X/0lVACxMACxPBS1TJyxXRy1dZStieClglSNkrR1iwhlf0xha4h1X7jRZ+T9Z/0ZX/0xV/1RUAD5RAD9SAEBTH0FTP0NaYUVheURhk0Jgq0Bevz9bzkZb3UpZ605Y91NW/1dU/1tS/2BSAFlaAFtcAFtcDlxdOl1dWV5edl5dj11cpl1auV1Yy15X2mBV6GJU9WVS/2hQ/2pP/25OAHtkAH1kAHtkAHliPHVhVW5fa3FdiXNZoXJUtXJSx3JR1nNQ5HRO8XZN/nhM/3lK/3xKAItjAItkAIpkHIhlQYRkUH5gYXxffIJdmYZZroZTwYVM0oRJ4IVI7oZH+4dG/4hE/4pEAJhoAJhpCZdpLpVpOZFjTYxiXIdicYxgi5Fdp5ZZu5ZTzJZM3JVE65Q/+JU+/5U8/5Y7AaJtEKNtGqJrJaBnNp1lS5hlWpJmapVkgJphmZ9etKRbxqVU1qVN5qRF9aM7/6Iy/6IyAK1tBK1tDq1rHatoNqhoSqRpWaBpaJ5oeqNmkKdkp6thv7Bc0bFX4bFQ8LFH/rA8/68uALduALhuCLdtJbVtOrNtTLBuWqxuZ6hueKxtirBrn7RotLdkyrtg3b1d675V+75M/75CAsJyEcJzIsByM8BzQr5zUbx0XrhzarV0d7VziLlymrxwrcBtwMNq1MZl6Mli9spc/8tUKsx6Ms17Oct7Q8t7Tsl8Wsh9ZcV9cMJ+e8B9isN8mcZ7qcp5u812zM9y4NNu89Vs/9doSdiGTdiHUtiHWNeIYNaIadSJctOKfNCKhs6MkNCLndOKqtWIudiGyduD2t6A6+B7/uN6aeaYa+WYbuWYc+WZeeSaf+ObhuKcj+Gel9+fn9+gqOGfsuOevuacy+ia2OqX5+yT9+2OivOtjPSujvOukfSvlfKvmvKxoPKypvG0rfC2tO+5u/G4wvK3yvW31Pa23vi06fmy9fuvAARrAANtDwBvMQB0UQB6dACCjACDlwB9sACAxgCC2ACB5gB88wB4/gB0/wBv/wBs/yZqAA5pAA5sDQxvMAZzTwB4cwCBigCClgB8rwB/xgCC1wCA5gB88gB3/gBz/wBv/xVr/zZsABtpABxrBBtuLRpyTRd3cA5/iQCBlgB8sACAxQCC1gB+5QB78QB2/glz/x9u/y5r/01yAC5mAC9oAC1sJi5wSC51bC59iCuBlih9ryKAxB2A1B584yR58Ct1/DNx/ztu/09w/1xzAD9iAEBjAEFmHUNsP0RyZUd7hEh/lUZ9rUN+wUJ80UN54EV17Uly+VBw/150/2Jx/2dxAFZrAFdsAFhtEFltOFptWl52fWF8kmB8qV56vF53zV502mN052h09Gpy/21w/29u/3NuAHN0AHZ2AHV2AHZ2M3d3Vnd4dHd4jXZ2onZ1tnZzx3Zy1ndx5Hhv8Xpu/Xts/3xr/39rAJR+AJWAAJV/AJN9MZB7Wox6b4V4g4d2nIlysYdtw4ds0odr4Ihq7ohp+oln/4pm/4tmAKJ9AKJ+AKF+G559Rpx8YJh8a5F4epB3kJV1qZhyvJhszZZm3ZZj6pZi95Zh/5Zf/5deAKp+AKuAIqqAPqiAVaaAWqJ6aJx5dJh4hp13naF1tqZyx6Zt16Vn5qRg9aJZ/6JX/6NYLbODOLSERLOETLKDULB/V6x7Zqh7cqJ6gKV5lKp3qa11wbJz0rJu4bJn8LFg/q9X/65OQLyGRL2GRbyESbuCTbl/WLZ9ZrJ9ca59fq58jrF7obV5tbl3y7103b5w7L5q+r1i/71aQMaHQsWGRMWFR8SDTsKBW8CBZ72BcrmBfLaBi7l/nL1+rsF8wcR61cd358p09spu/8pnRNCJRtCJSM+ITc6HV82HYcuHbMiId8aHgMKIjMSHm8eGqsqEu82CzdCA4NN889Z7/9d3UdqPVNuQWNuQXtmQZdiRbteRd9WRgNOSidCTk9CTntOSrNWRutiQyduN2t6L6+CH/eOGa+edbueeceeedeefeuafgeSgieShkOKimOCjod+lqeGls+OjvuWiy+ig2eqe6Oya9+6WivSwi/SwjfSxkPSxlfSymvOzoPO1pvK2rvG4tfC7vPG8w/K7y/S60/a53vi46vq29vu0AASDAAKFCgCHMgCLUwCPdACVmACcqwCetgCZxwCY2ACZ5wCZ9ACU/wCR/wCM/xOJ/y2GABCCAA6EBw2GMAeKUQCPcgCUlgCbqgCdtACYxgCX2ACY5gCY8wCU/gCQ/waL/yKI/zaGAB6BAB6DAB6FLRyIThmNcBGTlAKbqgCdswCXxQCX1wCY5QCX8gKT/BmP/ymL/zWI/0OGAC5/AC6AAC+CJi+GSjCMbTCTkS+apyycsSqWxCeX1ieY4yuV8DKR+zqO/0GL/0mI/1OGAEJ6AEN8AER/F0OEQUWJZkiQikuXo0uarUmUwUiW00iW4EuT7U+P+FOM/1eJ/1yG/2SGAFV5AFd7AFd7C1h8OVuDW16KgGOSnGWXqmOTvmKUzmOS3GSQ6WaN9WiK/2uH/3SI/3uLAG6CAG+DAHCDAHCDM3GEU3KEdHeMlHuTpXqSuXmRyXmO13qM5HyL8ICL+4GK/4KI/4SHAIqKAIyMAIyMAIyML4yNVI2Nco2Oi42OoIyNsoyMw4yK0oyJ34yI7YyG+Y2F/42D/4+DAKiUAKmWAKiWAKiVMaaUWaOSdp6QhpiNlpqMrJuJv5qFzpmD3JiC6piA95h//5h+/5l8ALOSALSUALKTLbGSTa+SZ6yRe6iRgqKMjqCLoqWKuKiIyaeD2KZ+56R59KN4/6N2/6R3FruTK7uTO7qTTLmTX7eTcLSTdbGOfquMiKeLmKyKrbCJw7OH07SC4bJ88bF2/q9v/69vRcOVTcOWVMGWX8GWar+Wbr2TcbmOfbWNhbCLk7OLpLeKt7qJzb6H3b+D7L99+r12/7xvXMqaYMqaY8mZZsmYZ8eVasaSccKPe7+PhLqOj7qNn76MsMKLwsWK18mI6MuG9sp//8p6YdObYtObZNOaZdKYZ9GWas+TdM2TfcmThsaSkMOSnceRrMqRvM6PztGO4dSL8taJ/9eGZt2eaN2faN2datycbNuac9qae9ibhNabjdObltCcoNKbrdWau9mZytuY2t6W7OGT/eOTdOmndeqmdummeOmmfeimg+emiuankuSomuKpouCqquCqtOOqv+apy+in2eql5+yi9+6ei/a0jPa0jfa0kPW0lPW1mfS2oPS4p/O5rvK7tfG+vfDAxPK/y/S+1Pa93vi86fm69vy5AASYAAKZEQCaNwCeVgCidQCmlgCstgCzxQCzzwCv2ACr5wCq8wCq/wCn/wOk/yOg/zedABGWABCYDg6aNQmdVACgcwCllACstQCyxACyzQCv1wCq5gCq8wCp/gCn/xuj/y6g/z6dACCWACCXBh+ZMh6cUhygchalkw2rswSywgCxzACu1wCq5Qiq8hep/San/zKj/z2f/0qdADCTADGWADGXLDGbTjKfbzKkkDKprzOwwDKwyTCs1TKq5DWq8Dqp+0Gl/0mi/1Cf/1mdAEWQAEaTAESVHkWYRkecaEmhiUynqVCtu1Gvw0+p0k+p4VGo7VSn+Fmk/12h/2Ge/2icAFaJAFiMAFmOElqSOluYXl6dgGOjoWmptWqsvminz2mo3Wqn6Wyl9W6i/3Cf/3Kd/3icAGqMAGyOAGyPAG2OMW6PVXKWdXaeln2lroCpun6lyn6m2H6l5X+i8YCg/IGd/4Ob/4idAIOVAIWWAIWWAIWWMoaWUoaWbIeXi4yfp5GltZCkxZCj04+h4JCf7ZCd95Se/5Sc/5WcAJ6eAJ6eAJ6eAJ+eN56eWJ+fcp+fiZ+gnp+gsJ+fv56ezp6d3J2c6Z2a9Z2Z/52X/52WALalALemALemE7emQremX7WleLKjja2hmaeeqKmeuqqby6iX2aeV5qeU86aS/6aR/6aRAMKlCsKlKsGlQ8ClWb6kbbyjgLqij7ahlbCdn6+csbObxbaa1LWV47KQ8bGL/rCK/7CKPcmkRsmlT8mlW8ekacakd8OkhsGjir2fkbicmbSbqLmbur2az8CZ3sCU7b+Q+72J/7uDWM+lXdCmY8+ma86mdc2mgMumg8iihsWdj8Gclrybor+assOaxMeZ2MuY6MuV9suQ/8qKatepbdepctepeNapfdWoftOlgNGihs+fj8uflseeoMedrsudvs6cz9Kb4tWZ8taY/9eUeeCte+Cte+CtfN+rft+qf92ngtylitqkktelmtOkotKkrtWkvNmjytui3N+h7eGf/uSfgeuyg+yyg+uyhOuxhuqviOmujOiulOeunOWvpeOwrOCxteOwwOWwzOiv2eqt6e2r9+6okfe7kve8k/e8lPe7lva6mfa7n/W7pvS9rvS/tfPBvfHDxPLEzPTD1fbC3/jB6vrA9vu+AAWpAAOqIACsPgCuWwCxdwC1lQC5sQC+zADD1wDC4AC/5wC68wC5/wG4/xy3/zC0/0GxABOoABKpHhCrPAytWQSwdgC0lAC4sAC9ygDC1gDB3wC/5gC58gS5/Ru4/ym2/ziz/0exACKoACKoGiGqOyCtWB6wdRu0kxe4rxa9yRvC1BrB3hi/5SK58iq5/TO4/z23/0az/1KxADOmADOoDTSpNTSrVDSucjSyjzW3qzi7xT3A0j7A2zy+40C58EW5+0q4/1C2/1ay/2CxAEakAEWlAEanKkepTEmsa0qvik61plK5wFm+zlm+1le74Fm47Vy3+WC3/2O0/2ix/22wAFmdAFugAFyiH1ylQVyoYmCsgWOxnmm2uXC7yHG80G+43HC36XG39XS2/3az/3iw/32vAGiWAGqYAGuZBG2dOHChWHKnd3aslnyysYO4woW6yoO12IO25oW28YW0/Iey/4ev/4uuAH+dAICfAICfAIGfMIGeToGfboami4ysqJKzvJa3xpS01ZS14pW07ZWy+ZWw/5Wu/5etAJelAJemAJemBZemOJemVZembZimg5imnZ6ttaOzwqKz0KKy3aGw6qGv9aGt/6Ot/6StAKyrAK6tAK6tI62tRK2tXq2tda2uiq2unK6urq6vva6uzK2t2ays5qyr8qyq/quo/6yoAMKzAMS0H8O0O8O0U8O0acO1fcG0j76yn7uwqbWttresx7iq1rWm5LWl8LSj/bSj/7SiMM+0PM+1R8+0Vc61ZcyzdcuzhMiyk8axocKwpbyrrruqvr+q0cKp38Gl7b+g+72c/72cU9WzWdazXtWzZ9SzctOyftGyi8+xl8yxm8mtocSqqMCptsSox8io2syo6Myk98uf/8qbZ9yza9y0b9y0ddu0ftq0h9mzkdezltSwl9Gsn82rpcipscupv8+p0dOp5Neo89em/9iieOS3e+S3fuS3guO3ieO3kOK3kuC0lN6xmNyvoNiup9StsdWtvtmtzNyt3N+s7uKr/uSqie69i+69je29ke29k+28lOu6luu4mOq2n+i2p+a2r+O3t+K3weW3zei22uq16ey0+e+ymfjFmvjEmvjEm/jEnfjDn/fCovbBpvbBrvXCtfTEvvPGxvLJzfTI1vbI4PjH6/rF9/zEAgm4Gwe5MQS6SQC8YQC+ewDBlgDFrgDIxgDM2wDQ5QDO7gDN8hLG/iPG/zDE/zzD/0rCABa3GRa4MBW5SBK8YQy+egTAlADDrQDHxQHM2xbQ5BfP7RfN8iTG/S/F/znE/0PD/0/BACa3FSa5Lia5RiS7XyO9eSHAkyDDrCLHwyjL2TPQ4zPO7DPM8TnG/EDF/0fE/0/D/1nBADa1BDe3Jze4QTe6XDi8dji/kDnCqTzGwEPK1k3O4EzN6UvL70/G+lPF/1nE/17D/2fBAEi0AEi1FUm2OEm3VUu6cE28i1DApFTEvFvI0mTM3GPM5mLK7GXF+GfE/2vE/27D/3PAAFyvAF6xAF2yKFy0Sl63aGC5hGS9nmrBtnDFzHnK2HnK4HfI6XjE9HrE/3zD/37C/4LAAGypAG2qAG6sHnCvQ3KyXXK2e3e5lny+r4PCxorH04zI24nF5YrD8YrD/IzD/43A/5C/AHymAH2nAH2nAH2nMX+qU4OvcYa1jYu5p5G+v5nEzpvG1ZnC4pnD7prD+prB/5q//5u+AJKtAJKtAJKtA5KtNJKtT5KtZpKtgpeznZ25t6TAyajE0abB36bC66bB9qW//6W9/6a8AKazAKezAKe0I6azQqezWaazb6azgqazlKezrq27w7LAzrLA27G/57C+87C8/rC7/7K8ALq5Cbu6Jru6PLu6Urq6Zrq6ebu6i7q7nLq7rbu8vLu8yru817q75Lq68Lm5/Lm3/7m3K87AN8/BQ8/BUc7BYc/Bcs/Bgs7Bks3Bocu/sMe9t8G6xMO608S44MK07sGy+sCx/8CxTdvCU9vCWdvCY9rBb9rBfNjBidbAl9S/pNG9sM28s8i4u8e3ysu328626c2y+Muv/8mrY+HAZ+LAbOHAc+HAfODAht/AkN2/nNu+pti+q9S6r8+3tsy1w9C109W25ti29Niz/9evdejBeOjBe+jBgOjBh+fBj+bBmOTBoeLApuC+p966rtq4tdW3wNm3zdy33uC37+O3/uW2hfDEiPDFivDEjvDFk+/Fmu/Foe7FpezEp+vBq+m+s+e/uuS/w+W/zui+2+q+6e29+u+7mfrLmfrLm/rLnvnLo/nMp/nMqvjMrPjKr/fJtvbJvvXLx/PMz/TO1vbO4PjN6/rM9/vKKRHHNBDHQw7IVQrJagTKgADNlwDPrQDSwgLV1hjY6Cvc8S3b+i7Z/TfS/0HR/0nP/1TOJxzFMxzHQhvHVBnIaRfKgBTMlhLOrBTSwRvV1SnY6Dfb8Dja+TnZ/D/S/0fQ/0/P/1nPJCrFMivHQCrHUyrIaCnKfinMlCnOqyzRwDLU1D3Y5kjb70nZ+EjY+03R/1PQ/1rP/2LPGzrEKzvFOzvGTzvHZDzJez3Lkj/NqELQvUnT0VLX5Fza7VzZ9lvY+V3R/2LR/2fP/27PAkvCG0zDMUzDSE3FX07HdlDJjlPLpFjOuV7SzWfV4HDY6W/Y8m7W92/R/3LQ/3XP/3rPAFu+AF3AHF3BO1/CVWDEb2PGiGfJn2zMtHLPyXrT3IPX5YLW7oDV9IDQ/oLP/4TP/4jPAG+5AHG7DnG8M3K+SHHAZXTDf3jFl33JroPNw4vR15PV4ZPV6ZDT8ZHQ/JHP/5LP/5XOAH2zAH60AH+1F4C3PoK6XIS+dYbBj4vFqJLJvZjO0aDT3aHT5J7R7p7P+Z/P/5/O/6DNAI+1AI+1AI+1AI+1LI60S4+2apO7hZfAnpzFtqPKy6vQ2qzS36rO66rO96rP/6nN/6rLAKG5AKK6AKK6G6K6OqG6UqG6ZqC5eaG5k6W+razGxbPM1bfP3LXN6bXO9LTN/rTL/7TKALS/ArTAH7TANrTAS7TAXrTAcbPAgrO/lLO/pLO/u7rH0MDN2r/M5r/L8b7K/L3J/73IK8bFNsbGQMfGTcfGXcbGbcbGfcbGjcbGncbHrMfHu8fIycjJ1sfI4sfH7sbG+sbF/8bESNnMTdnMVdnMX9nNa9nNednNh9nNltnOpNnNsdbLvdLJxMzGz8/F3s/D687A+M2//82/YebPZOfPaebOcObOeeXOhOTOj+PNm+LMp9/LstzKvdnIv9PDx9LC1tfD59rD9Ni//9i9ce3Nde3NeO3NfuzNhevNjuvNl+nMoejMq+bLteTKueDHvdzDw9nC0N3C3+HD8eTD/uXBgvTOhfTOh/TOi/POkPPOmPLOoPHOqPDOsO/Otu3MuOvJv+jHxeTH0OjH3OvH6+3G+/DGk/zSlf3Sl/zSmvzSn/vSpPvTq/vTsvrUuPnTuvjRv/fQx/bS0PXU2PXU4fjU7PvT+PzRQxzTSRvTUxrUYhnVcxfWhhbYmRbZrRnbwCHe0i3h5Dvk8kjl+krk/0vj/0/d/1fb/2DaQCTSSCTTUiTTYSTUcyPVhSLXmSPZrCfbvy7e0Tfg4kTj8k/l+lDk/1Hj/1Tc/1vb/2PZPzHSRjHSUTLTXzHUcTHVhDLWmDTZqzfavj3d0Ebg4VLj8Fzl+Fzj/1zi/17c/2Ta/2zaOkDRQkHSTUHSXEHTbkLUgUPVlUbXqErZvFDczVjf32Li7mvk9mvj/2ri/2zc/3Db/3baMVDPOlHQRlHQV1LRaVPSflXUkVjWpV3YuGPbymre3HTh7Hzk9Hvj/Hnh/3rb/33a/4LaGF/MKmHNOmLOTWPPYmTQd2fSjGrUoG/WtHXZxnzc2ITf6Y3i8Yvh+Yng/Ynb/4ra/47aCnPIIXTKL3TKPXLLVnTNbnfPhXvRmn/UroTWwYva1JPd5Zvh7Zng9pff+5fa/5ja/5raAIHDAILEEoPFNIPGToXIYoXLe4jOk4zRqJLUvJnXz5/b4aff6qbf8qPd+KPa/6Pa/6XaAI29AI2+AI6+BY/AOJDCVpLFcZTIiJfMoJ3QtqPUyqrZ3LHd57He7q/c9q7Z/67a/6/aAJ7BAJ7CAJ7BCZ7BMJ7BSJ3AXp3Be6DFlqXLrqvQw7LW17nb47vc6bnZ9LnZ/rjZ/7nZAK/GALDGDrDGLK/GQa/GVa7GZ67Fea7Fiq3Eo7LJu7nR0cHX4MTb58PZ8sLZ/cPZ/8LXH8DLK8HLNsHMRMDLU8DMZMDLdMDLhL/LlL/Lo7/Lsr/KyMbS3M3Z5czY8MvX+svW/8vVQ9LRSdLRT9LRWdLRZtLRc9LSgdHRj9HSndLSrNLSutLTyNPT1dTU4dPT7NLT+NLS/9PSWePWXuTXY+TXa+TXdeTYgOTYjOTYmuTYp+TZtOTZv+HXyt7V0NjR2trR6dvP9trM/9nMcfHadPHaePDaffHahPDajfDalu/ZoO7ZrOzYturXwOfWyeTUy97P097O4uPP8uXP/+XNfvjZgPfZhPfZiPfZj/fZlvbZnvXYp/TYsfPYuvLYw/DXxu3Uy+nR0ufQ3evQ6+3Q/PDRjv/aj/7Zkv/alf/amv7aoP3aqP3br/3bt/zbwPvcx/rbyfjY0ffZ2fXa4vjb7fva+v3aVijeWyjfYijfbijgfCjhjSninSvkri7lvzXnzz7p30nr7lbt+1/u/2Dt/2Ds/2Pn/2vlVTDeWi/eYi/fbTDffDDgjDHinDLjrTflvj3mzkXo3lDq7Vvt+mTt/2Xs/2Xs/2fm/27kUzveWTveYTvebTzfezzgiz7im0DirEPkvUnmzVHo3Frq7GXs+W3t/23s/27s/27m/3XkUEjdVkndXkndakrfeUvfiEzgmU/iqlPju1nly2Dn22np6nLs93rt/3ns/3ns/3rm/37kSlfcUFjcWVjcZVnddVvehV3fll/gp2TiuGnkyHDm2Hjo6IDr9Yfs/Ybs/4Xr/4bl/4nkPmbZR2faUWjaXmnbb2rcgG3dkXDfo3TgtHnixYDl1Ibn5Y/p8pXs+pTr/5Lq/5Hk/5TkLnXXN3bXQnbYU3fZZXnaeHvbi3/dnoLesIjhwY3j0ZTm4Zzo8KLr96Dq/57p/53k/5/kHobSLYfTOofUS4jVWobWbojYg4vZl4/cqpTevJrhzqDk3qbn7a7q9avp/ano/6jk/6rkAJHNAJLOFZPPM5LPTJPRZJXTeJbWjZnZpJ7bt6Peyani2rDl6rfp8rXo+rPn/7Pk/7PkAJ3KAJ7LAJ3LAJzKK5zKTZ3NaaDQgqLTmKXYr6vbw7Lf1bjj57/n8L/n9r3m/r3k/73kAK3OAK3OAK3OHqzONqzOSqvNXarMb6nMi63QpbLWu7jbz7/g4sfl7cnn8sbk/cbk/8fkCLzSHL3TKr3TObzTSrzSWrzSarvSerrRirrQmLnPsL3Ux8Xb3M3i6tHm8dDk+8/k/9DkOM3XP83YRczXUMzXXMzXaszXeMzXhsvWlMvWosrWsMrVvcrV09Ld5tnj79jj+Nji/9fhVN3cWN3dXd3dZN3cbt3ced3dhdzcktzcn93drN3dut3dx97e1N7e4N/f69/f99/f/97eaO7ia+7ib+7ide7ifu7ih+7iku7jnu7jqu7jt+7kwu7kzOzj1unh2+Pc5eXc9Ofb/+XYf/rkgvrlhfrlifrkj/nllvrlnvnkp/nksfjkuvbjxPXizfPi1vDg2Ovc4Ovb7u/c/vLciP/jif/ji//jkP/jlf/jnP/jpP/krP/jtf7kvv3kx/3k0Pvk1fri3Pni4/ji7vri+v3iajrrbDnqcjnqfDrrhzvslD3toj/usEPvvkjwzU/x21fy6mH092v1/3L1/3P1/3P0/3bxaD7pbEDqckDqe0Drh0Hsk0PsoUTsr0juvU3vzFTx2lzy6GXz9nD0/3b1/3f1/3b0/3nwZ0jpa0npckrqeknqhkvrkkzsoE/sr1LuvVfvy17w2WXx523z9Xf0/371/330/330/3/wZVToaVXpcFbpeFbphFfqkVnrnlzsrV/tu2PuyWrw13Dx5nnz9IH0/4j1/4f0/4b0/4fwYWLnZWPobGPodWTogWbpjmfqnGrrqm3suXLtx3fv1X3w5IXy8Y3z/ZP0/5Hz/5D0/5DvWW/mX3HnZnHncHLnfHPoiXXomHjqp3vqtoDsxITu04vv4ZHx75jy+570/5zz/5vz/5ruTnzkVH3lXH7lZ3/ldIHmg4Pnk4Xoo4jpso3qwZHs0Jfu353w7aPx+anz/6fz/6Ty/6TuSY3hUI3iVo3iXIvjaozke47ljJDmnZTnrpjpvZzqzaHs3Kfu6q3x+LPz/rHy/6/y/67tM5jeO5jeRZjfUpjfYpngcprigpnjlZ3lqKHmuaXpyavr2bDt57bv9rzy/bry/7jx/7ftAKHYA6LZH6LaNqLaTKLbYqPed6Tfi6binqjksq3mxLPp1bjr5L7u88Tx+8Tx/8Lw/8HtAK3ZAK7ZAK3ZH6zYNazYR6vYYavZe63ck7DfqLPjvbnm0L/p4Mbt78zw+M3x/srv/8rtB7vcGrzcJ7vcNbvcRLvbVLrbY7nbcrjagbfZmrrdsr/hyMXm28zq7NPv9tXw+tPt/9PtMsrfOMrgP8rfSsnfVcngYsnfcMjffcjei8femMbdpcXcu8jf09Hm59nr897v+dzu/9ztTNjjUNnjVdnjXdnkZtjjcdjkfdjjidfjltfjotfir9fivNbhyNXg3t3o8eXu+OTt/+PtYujoZenoaejob+jod+jogOjniujolujooujorefnuujoxujo0+np3+np6+rr9enq/+npdfjsd/fsevfsgPjth/jsjvftmPfso/jtrvjtufjuxfju0Pfu2fbt4fPr5u/n8PHo/vLmiP/tiv/tjf/ukf/ulv/tnf/upP/urP/utP/uvf/tx//u0P7t2f3t4vzs5vrq7/vq/P3re1D1flD2glD1iVH1klL2nFT2p1b3s1n3wF34zGL52Gj55W768nf7/4D8/4T8/4T8/4T8elT1fVX1glX2iVb1kVf2m1j1pln2slz3v2H4y2X52Gv55XL68Xr7/oL8/4f8/4b7/4f8eVv0fVz0gl31iF31kV/2m2D2pmL2smT2vmn4ymz413L543j68ID7/Yj8/4z8/4z8/4z7eWb0fGf0gGj1h2j1kGn1mWr1pWz2sG/2vXL3ynf41nz544L574j6/JD7/5T8/5P8/5P8d3LzeXLzfnP0hHP0jXX0l3X0onj1r3r2u332x4H31If44Yz57pL5+5n7/538/pv7/5r7cX3ydX7yeX7ygX/ziYDzlILzn4P0rIb0uYn1xY320pH34Jf47Jz5+KL6/6X6/6P6/6P6a4jxcInxdIrxe4rxhYzykI3ynI/zqZH0tpT0w5j10Zz23aH36qX496v5/6/6/636/6z6Y5PvZpPva5TvdJXwfpXwi5fxmJnypZvys57zwKL0zqX126r36K739bT5/7j6/7b6/7T6XaDtYqHtZ6Dtb6HueKDugaDvkKHwn6TxrqbyvKrzy6702bL25rb28rz4/sD5/775/735SqnqUKrqVqrqYKrrbKrseqrtiKvtlavvpq7wt7HxxrXy1br047728MP3/Mn5/8f5/8b5JrLmMLLmOrLnR7LnV7LoaLLperPqjLTsnrbtrrfvwLzx0cHz4Mb17sv3+tH4/9D4/8/4M77oOr/pP77oR77oUr3oXb3oabznervokLzqpL7st8Hvysbx3Mz069L299j4/9n5/9f3QsvrRsvrSsvqUsvrW8rrZcrqcMnpfMjpiMfplMXoqcfqwMzu1NLx5tj09d/3/eH4/+D3UtjuVNjtWNjtXtjtZ9jtcNftetfthdbtkdbsndXsqNTrtNPqyNXs393x8eX2++j4/+f2YeXwY+bwZuXwbOXwdObxfOXwheXwkOXwm+XwpuTwsuTvvePvyOLu1OHt6unz+e/3/+72cfTzc/T0dfTzevTzgfTziPPzkfT0m/PzpvTzsPPzvPPzyPPz0/Pz3/T06vT09fT1/vT0ff/1f//2gv/2hv/1jP/2k//1nP/1pf/2r//2u//2xv/20f/23P/35P/37P318vz0/f70jm//kG//k2//l2//nXD/pXH/rnP/t3X/wnf/zHr/2H3/44H/7oX/+ov//5H//5T//5T/jXH/j3H/k3L/l3L/nXP/pXT/rXX/uHf/wnn/zHz/13//4oP/7oj/+Y3//pL+/5b//5b/jXf/j3j/knj/l3n/nXn/pHn/rXv/t3z/wX7/y4H/14X/4oj/7o3/+JH//5f//5n//5r/jX//j3/+koD+l4D/nYH/pIH/rYP/toT/wYb/y4n/1ov/4pD/7ZP/95f//53//5///5//jYj+jon+kYn+lon+nIr+o4v/rIz/tY3/wI//ypH/1ZT/4Jf/7Jv/95///6T//6b//6X/ipD9jJH9kJL+lJL+m5L+opP+q5T+tJb+vpf+yZr/1Jz/35//66P/9qb//6v//63//6z/h5r8i5v9jZv9kpv9mJz9oJ39qZ79sp/+vaH+yKP+06X+3qj/6av/9K///7P//7X//7T/hKP8h6T8iqT8j6T8laT8naX9pqb9sKj9u6n9xqv+0a7/3LD+6LP/87b//br//73//7z/hK77hq77iK77iq37j6z7mK38oq78rK/8t7D9w7P9z7X+2rf+5rr/8b7//ML//8X//8T/e7X6frX5gbb6hrb6jLX6lLb7m7X7prX7s7f8v7n9zLz92L7+5MH+78X/+sr//8z//8v/cL34c774d774fL75g775jL75lr76ob76rL77ucD8x8L81cX94sn+7cz++NH+/9X//9T/acf4asf3bcj3ccf4d8f4gMb4jMb5mMb6pcb6tMj7wcn80Mz83s/96tP999j//93//9z/bNL5bdL5cNL5c9H5eNH5f9H5htD4j9D5ms/5qc/5udD7yNH82db859n99N/+/uP+/+P/cNz6ct37dN37eNz7fdz6g9z6itz6k9v6ndv7ptr6r9n5vtn60dz84t/88eX+++n+/+v/dOj8def8eej9fOj8gef8h+j8j+f8l+j8oOf8quf8tOf8veb8x+T72OX87Or9+vD+//L/efT+evP9ffT+gPP9hvT+jPP9lPT+nPP+pfP+r/P9ufP9w/P9zfL92PL94vH99PP9//j+ff7+f//+gf7+hf/+iv/+kf/+mf7+ov//q//+tf//vv7+yf7+1P7+3/7+6f/+9P//////\",vivid100:\"AAAAFAAAJAAANAEAQwEAUQMAYQMAcwUAiggAoQsAsw4AwxAA1BIA5BUA9BcA/xkA/xsAAw0BDg4BHw8BLhEBPRIATRMAXRQAbxUAhxYAnhcAsRgAwhcA1BYA5hUA9hcA/xkA/xsACBsFEhwFHR0EKx0EOh4ESh8EWiADbSEDhCMCmyQCriQBvyQB0CMA4iEA9B8A/xsA/xsAECgNFygNHykMKioMOCoMRysLVywKay0Kgy8ImTAHqzAGvDAFzS8E4C0D8SsC/ygB/yIBFzMTGzQTITQTKjUTNTYSRTcSVjgRaTkQgTsPlzwNqDwMuTwLyjsK3DoI7TgG/TUF/zADFUAZGEAZHUEZJkEZM0IYQ0MYVEQXaUYWgUcVlEgUpUkTtkgRx0gQ2EgO6UYN+UMK/z4IEU4fE08fGE8eHk8eLlAeP1EdUlIdaFQcf1UaklYZolYYslYXw1YW1VUU5VQT9FEQ/04OAmAjAmAjBGEjD2EjJWIjOGMiTWQhZWUge2YfjGcenWcdrWccvmYa0GUZ4GQY72EW/l4TAHInAHInAXInDXMmG3QmLnUlSXYkYXgjdHkihXohlXogpXsft3seyHod2Hkb53Ya93IYAH8qAH8pAIApAYApC4EoMIIoSoQnYIUmcYYlgIckkIgjoYkisYkhwYog0Ike34gd7ocbAIwtAIwtAI0tB44sH44sNpArTJEqXpIpb5MofZQnjZUmnpYlrpYkvZcjzJch2ZYg6JUeAZoxA5sxBpsxC5wwIJwvOJ0vS54uXJ8ta6AseqEriqIqmqMoqqMnuaQlx6Qj1KQi5KMgAKg1Aqg1C6k1Gak0Jqo0OKozSaszWawyaK0xd64wh68vl7AtprEstLEqwbIoz7Il37EiALQ6AbU6BLU6FbU5KbY5ObY5Sbc4Wbg3aLk3d7o2hrs0lbwzo70ysL0wvb4uyr4r3L8nFcBAGMBAHMBAIsA/MME/PsE/TcI+XMM+a8Q9ecU8h8Y7lcc6occ5rsg3usk1yMoy28suGctGHMxFIsxFK8xFN8xFRM1FUs5EX85Ebc9DetBCh9FBk9JAn9M/q9Q+t9Q8xtU53Nc0Et5KFd5KGt5KIt9JLt9JPd9JS+BJWOFIZuJIcuJHfuNGieRFleVEoeZDruZAweg92uk3AAALFwAKJwAJNgEJRgEIVAMIZAMHdgUGjQgFpAsEtg0DxhAD1xIC6BUB+BcB/xkA/xsABA8QEBAQIREPMBIPPxMPThQOXxUNcRYNiRgLoRkKsxkJxBkH1hcG6BYF+RcE/xkC/xsBChwUEx0UHh4ULB8TPCATSyESWyISbiMRhiQPnSUOsCUNwSUM0iQK5CMI9iAH/xwF/xsDESgZGCkZICoYKisYOSsXSCwXWS0WbC4VhDAUmjETrTERvTAQzy8P4S4N8iwL/ykJ/yMHFzQdGzQdIjUdKzYcNjccRjgbVzkbajoagzwYmD0XqT0Wuj0UyzwT3TsS7jkQ/jUO/zALFkEhGUEhHkEhJkIhNEMgQ0QgVUUfakYegkgdlUkcpkkat0kZyEkY2UgW6kYV+kQT/z8QEU8lE08lF1AlHlAlL1EkQFIkU1MjaVQigFUhklYgo1Yfs1YexFYc1lYb5lQZ9VIX/04VAWEpAmEoA2EoEWIoJWIoOGMnTmQmZmYlfGckjWcjnWcirmchv2cg0GYe4WQd8GIb/14ZAHIrAHIrAnMrDnMrG3QqL3UpSncoYngndXkmhXollXslpnskt3siyHsh2Hkg6HYe93IdAH8uAH8uAIAuAYAtDIEtMYMsS4QrYIUqcoYpgYcokYgnoYkmsoolwook0Yoi34kh7ocfAIwyAI0yAI0xCI4xII8wN5AvTZEuX5Itb5MsfpQrjpUqnpYpr5YnvpcmzJcl2pcj6ZYhAps1A5s1Bps1C5w1Ip00OZ0zTJ4zXJ8ybKAxe6Ewi6Ivm6Mtq6QsuqQqx6Qo1aQm5KMjAag5A6k5DKk5Gqk5Jqo4Oas4Sqs3Wqw3aa02eK41iK80mLAyp7ExtbIvwrItz7Ir4LIoALU/AbU/BLU+GLU+KrY+O7Y+Src9Wbg8abk8eLo7h7s6lrw4pL03sb41vr40y78x3b8tFsBEGcBEHcBEI8BEMcFEQMFDTsJDXcNCa8RBecVBiMZAlcc/osg9rsg8u8k6yco33MszG8xKHsxKJMxKLcxKOM1KRc1JU85JYM9Ibs9Ie9BHiNFGlNJFoNNEq9RDuNRBx9U+3dc5FN5PF95PHN5PJN9PMN9PPuBOTOBOWeFOZ+JNcuNNf+NMiuRLluVKouZIr+dGwehD2+k9AAAXGAAWKAAVOAEVRwEVVgMUZgMTeAUSjwgRpgsPuA0OyBAN2RIL6RUK+RcI/xkG/xsEBBAdEREcIhIbMRMbQBQaTxUaYBYZchgYihkXohoVtRoUxhoT1xgR6hcQ+hcO/xkL/xsJCx0gFB4fHx8fLSAePCEeTCIdXCMdbyQchyUbnyYZsSYYwiYX1CUV5SQT9yES/x0P/xsNEikjGSojISojKysiOiwiSS0hWi4hbS8ghTEenDIdrjIcvzEb0DAZ4i8Y9C0W/ykT/yQRFzUnHDUmIjYmKzYmNzclRzglWDkkbDsjhDwhmT0gqj4fuz0ezD0c3jwb8DkZ/jYX/zEVF0EqGUIpHkIpJ0MpNEMpREQoVkUna0cmg0kllkkkp0ojuEoiyUkg20kf60cd+0Qb/0AZEFAtE1AsF1AsH1EsMFEsQVIrVFMqalUpgVYok1cnpFcmtFclxVck11Yi51Uh9lIf/08dAWEvAWIvA2IvEmIuJmMuOWQtTmUsZ2crfGgqjWgpnmgormgnwGcm0WYl4mUk8WIi/18gAHMyAHMyA3MxDnQxHHUxMHYwS3cvYnkudnoshnsrlnsqp3wpuHwoyXsn2Xom6Hcl+HMjAIA1AIA1AIA1AIE0D4I0M4MzTIQyYYYxc4cwgogvkokuookts4orw4op0ooo4Ikn74clAI05AI05AI05Co44IY84OJA3TpE2YJI1cJM0f5Qzj5Uyn5Yxr5cvv5cuzZcs25cq6pYnAps9A5s9Bps9DJw8JJ08O547TZ86XaA6baA5fKE4jKI3nKM2rKQ0uqUzyKUx1qQv5aQsAqlBBalBD6lBG6pAKKpAO6s/S6w/W60+aq09ea49ia88mbA6qLE5trI4w7I20LI04bIxAbVGArVGCbVFG7VFLbZFPbdFTLdEW7hEarlDebpCiLtBl7xApb0/sr49v748zL853r82GMBMGsBMHsBMJsBLNMFLQsFLUMJKXsNKbcRJe8VJicZIlsdHo8hGr8hEvMlDyspA3cs8HsxSIcxSJ8xSMMxSO81SSM1SVc5RYs9Rb9BQfNBQidFPldJOodNNrdRMudVKyNZI3tdDGN5YGt9XH99XJt9XM99XQeBXTuFWW+FWaOJWdONVgORVi+RUl+VTo+ZSsOdQwuhN3OpHAAAjGQEiKQEhOQEhSAIgVwIgaAMfeQUekQgdqAsbug0ayhAY2hIX6xUV+xcT/xkR/xsOBREoEhInIxMnMhQmQRUmUBclYRckdBgjjBoioxsgthsfxxse2Rkc6xgb/BcZ/xkX/xsUDB4rFR8qICAqLiEpPSIpTSMoXSQncCUmiSYloCcjsiciwych1SYg5yUe+CIc/x4a/xsYEyotGiotIistLCwsOy0sSi4rWy8qbjAqhzIonTMnrzMmwDIl0TEj4zAi9S4g/yoe/yUcGDUwHDYwIzYwLDcvODgvSDkuWToubTsthT0rmj4qrD4pvD4ozj4m4D0l8Toj/zci/zIgF0IzGkIzH0MzJ0MyNUQyRUUxV0YxbEgvhEkumEotqEssuUsrykoq3Eoo7Egn/EUl/0EjEFA2ElE2F1E2H1I1MVI1QlM0VFQza1YyglcxlFgwpVgvtVguxlgt2Fcr6FYq91Mo/1AnAGI5AWM4AmM4E2M4JmQ4OWU3T2Y2aGg1fWk0jmkzn2kyr2kxwWgv0mcu42Yt8mMr/2AqAHM8AHQ7BHQ7D3Q7HHU7MnY6THg5ZHk4d3s3h3s2l3w1qHw0uX0yynwx2nsv6Xgu+XQsAIA/AIA/AIE/AIE+EoI+NIM9ToU8Y4Y7dIc6g4g5k4k5o4o3tIs2xIs104sz4Yox8IgvAI1DAI1DAI5CDo9CIo9BOpFBUJJAYpM/cZQ/gJU+kJY9oJc8sZc6wJg5zpg33Jc165czAptHA5tHBpxGDp1GJ51GPZ5FT59EX6BEbqFDfaJCjaNBnaRAraQ/vKU+yaU816U656Q4BKlLCKlLEqlLHapKK6tKPatKTaxJXa1JbK5Ie69Hi7BHmrFGqbJEt7JDxLJC0rNA4rI9ArVQBLVQEbVQILZQMLZQP7dQTrhPXbhPbLlOe7pOirtNmbxMpr1Ls75KwL9Izr9G38BDG8BXHcBXIcBXK8FXOMFXRcJXU8JWYMNWb8RVfcVVi8ZUmMdTpchSsclRvslQzMpO38tKI8xeJsxeLMxeNM1eP81eS85eV85eZM9dcdBdftFci9Jcl9Jbo9Nar9RZu9VYytZV4ddRHN9jHt9jIt9jK99jN+BjROBiUeFiXeJiauJhduNhguRgjeVgmeZfpOdesudcxOla3upVAAAuGgEtKwEsOgIsSQIsWAMraQMqewUpkggoqgsmvA0lzBAk3BIi7RUh/Rcf/xkd/xsaBhIyExMyJBQxMxUxQhcwURgwYhgvdRkujRstpRwrtxwqyBwp2hsn7Bkm/hck/xki/xsgDR81FiA0ISE0LyIzPiMzTiQyXiUyciYxiicvoSgusygtxCgs1icq6CYp+iMn/x8l/xsjFCs3Gis3Iyw3LS02Oy42Sy81XDA1bzE0iDMynjQxsDQwwTMv0zIu5TEs9i8r/ysp/yYnGDY6HDc6Izc6LDg5OTk5STo4Wjs4bjw3hj41mz80rT8zvT8yzz8x4T4v8jsu/zgs/zMrGEM9G0M9IEQ9KEQ9NkU8RkY8WEc7bUk6hUo5mUs3qkw2ukw1y0s03Uoz7kkx/UYw/0IuD1FAElJAFlJAIFNAMVM/QlQ/VVU+bFc9g1g8lVk7plk6tlk5yFk42Vg26lc1+VQz/1AxAGNDAGRDAWRDE2VDJ2VCOmZCUGdBaWlAfmo/j2o+oGo9sWo8wmo702k65Gc482Q3/2E1AHRHAHRHBnVGEHVGHXZGM3dFTXlEZXpDeHtCiHxBmH1BqX1Aun0+y30923w86nk6+nU4AIFKAIFKAIFKAIJKFoNJNoRJUIVIZIdHdYhGhIlGlIpFpYtEtotCxoxB1ItA44s+8ok8AI5OAI5OAY5OEY9OJJBNPJFNUpJMY5NMc5RLgpVKkpZJopdIsphHwphG0JhF3phD7ZdBApxTA5xTBpxTEJ1SKp5SP59SUaBRYaFRcKFQf6JPj6NPn6ROr6VNvaZMy6ZK2aVJ6KVGCKlYDKpYFapYIKpYL6tYQKxYUKxXX61Xbq5Wfa9WjbBVnLFUq7JTubNSxrNR1LNP5LNNBbVfDbVfGbZfJ7ZfNbdfQ7deUbheYLleb7pdfbtdjLxcm71bqb5btr5awr9Y0L9X4sBUHsBmIcBmJ8FmMcFmPMFmScJlVsNlZMNlcsRkf8VkjcZjm8djp8his8lhwMpgzspe4stbKMxsK8xrMM1rOM1rQ81rTs5rWs9rZ89qdNBqgdFpjdJpmdNopdRosdRnvdVmzdZk49hgIN9vIt9vJuBvL+BuO+BuR+FuU+FuYOJubONteORtg+Vsj+VsmuZrpudqtOhpxuln4OtjAAE5GwE3KwI3OwI2SgM2WQQ1agQ1fQU0lAgyrAsxvQ0wzRAv3hIt7xUs/hcq/xko/xsmBxM9FBQ8JBY8Mxc7Qxg7Uhk6Yxo5dhs5jhw3ph02uB41yh002xwy7hox/xcw/xku/xssDSA/FiE/ISI/MCM+PyQ+TiU9XyY8cyc8iyg6oik5tSk4xik31yg26ic0+yQz/yAx/xsvFSxCGyxCIy1CLi5BPC9BTDBAXTFAcDI/iTQ9nzU8sTU7wjQ61DM55jI4+DA2/yw1/yczGDdFHDhFIzhFLDlEOjpESTtEWzxDbz1CiD9BnUBArkA/v0A+0EA94j878zw6/zk4/zQ3GURIHERIIEVIKEVIN0ZHR0dHWUhGbkpFh0tEmkxDq0xCu0xBzUxA3kw/70o9/kc8/0M6D1NMEVNMFlNLIVRLMlRLQ1VKVlZKbVhJhFlIl1pHp1pGuFpFyVpE2llD61hB+lVA/1E+AGVPAGVPAWVPFWZPKGZPO2dOUWlNampMf2tLkGxLoWxKsmtJw2tI1WpH5WhF9GVE/2JCAHVTAnVTCHVTEXZTHndSNXhST3lRZ3tQenxQin1Pmn5Oqn5NvH5MzX5L3H1K7HpI/HdHAIFYAIFYAIJYAIJXGoNXOYVWUoZWZodVd4lUholUlopTp4tSt4xRx4xQ1oxP5ItN84pLAI5dAI9dA49dFJBdJ5FcP5JcVJNbZZRbdZVahJZalJdZpJhYtJhXxJlW0plV4JlU75hRApxjA51jB51jF55iL55iQ59iVKBhY6FhcqJhgaNgkaRfoaVfsaZewKZdzaZc26Za6qVYDappEappGqppJKtoNKtoRKxoU61oYq5nca9nf7Bnj7Fmn7JlrbJku7NkyLNi1rRh57NfELZuF7ZuILZuLLZuObduR7duVbhuY7ltcbptgLtsj7xsnb1rq75quL9qxb9p0sBn5MBlIcB0JsFzLMFzNcFzQMJzTMJzWcNzZsRydMVygsZxkMdxnchwqchwtslvwspu0Mts5MxqLM13L813NM13PM13Rs53Uc53Xc93adB2dtF2gtF1j9J1m9N0p9R0stVzv9Zyz9dw5dhuI+B5JOB5KuB5M+F5PuF5SeJ5VeJ5YuN4buR4eeR4heV3kOZ3nOd2p+h1tel0yOpy4uxvAAFDGwJCLANCOwNBSwNBWgVAawVAfgU/lgg9rQs8vw07zxA64BI58RU3/xc2/xk0/xsyBxRIFBZHJRdHNBhGQxlGUxpFZBtFdxxEjx5Dpx9Buh9Ayx4/3R0+7xw9/xg8/xk6/xs4DiFLFyJKIiNKMCRKQCVJTyZJYCdIdChHjCpGpCtFtitExypD2SlC6yhA/SU//yE+/xs8Fi1OHC1NJC5NLi9NPTBNTTFMXjJLcTNLijVJoTZIszZHxDZG1TVF5zNE+TFD/y1B/yg/GDhRHDlRIzlRLTpQOztQSjxQXD1PcD5OiUBNnkFMr0FLwEFK0kFJ5EBI9T5H/zpF/zVEGUVVHEVVIUZUKUZUOEdUSEhUWklTb0tSiExRm01QrE5PvU5Pzk1O4E1M8UtL/0hK/0RID1RZEVRZFVRZIlVZM1ZYRFdYV1hXb1lXhlpWmFtVqFtUuVtTy1tS3FpR7VlQ/FZP/1JNAGZdAGZdA2ZdF2ddKWhdPGldUmpca2tbgGxakm1aom1Zs21YxWxY1mtX52lV9mdU/2NSAXVjBHZjCnZjEndiH3diOHliUXphaXxgfH1gjH5fnH9frH9evoBdzn9c3n5b7Xxa/XhYAIJoAIJoAIJoAINoH4RoPIVnVIdnaYhmeolmiYplmItlqYxkuo1jyo1i2I1h5oxg9opeAI9uAI9uCJBuGJBuKpFtQpJtV5RsaJVsd5Zshpdrlpdrpphqt5lpxppo1Jpn4plm8ZhkAp1zBJ1zCZ1zHZ5zM59yRqByV6FyZqJxdaNxg6Rwk6Rwo6Vvs6Zuwqduz6dt3ads7aZqEKp3Fap3HKt3J6t3OKx3R613Vq12ZK52c692grB1kbF0obJ0r7NzvbRyyrRy2LRw6bRvGLZ7HLZ7JbZ7MLd7Pbd7Srh7V7l6Zbl6dLp6grt5kbx5n754rb53ur93xsB21MB15sFzJsF/KsF/MMF/OcF/Q8J/T8N+W8N+aMR+dsV9hMZ9kcd8n8h8q8l7t8p7xMp60st45sx2L82CMs2CN82CP86CSM6CU8+CX8+Ba9CBeNGBhNKAkdOAndR/qNV/tNV+wdZ90Nd86Nh5I+GEJuGELeGENuGEQOKDS+KDV+ODY+SDcOSCe+WChuaCkueBneiBqemAt+p/yut95e16AQFPHAJOLQNNPARNSwVMWwVMbAZLfwdLmAhKrwtIwQ1H0RBG4hJF8xRE/xdC/xlB/xs/CBZUFRdTJRhTNBlTRBpSVBtSZRxReB1RkR9PqCBOuyBNzCBM3h9L8B1K/xpJ/xlH/xtFDyNXGCNXIyRWMSVWQCZWUCdVYShVdSlUjitTpSxStyxRyCtQ2ipP7SlO/idN/yNL/xxKFy5aHS5aJS9aLzBaPjFaTjJZXzNZczRYjDZXojdWtDdVxTdV1zZU6TVT+zJR/y5Q/ylOGDlfHTpfJDpfLTtePDxeTD1eXT5eckBdi0FcoEJbsUNawkJa1EJZ5UFY9z9X/zxV/zZTGkZkHUZkIkdkKkdjOUhjSUljW0pjcUxiik5hnU5grk9gv09f0E9e4k5d80xc/0lb/0VZD1VpEVVpFFZpI1ZpNFdpRVhpWVlocFpnh1xnmlxmql1lu11lzVxk3lxj71pi/lhh/1RfAGduAGduBmhuGWhuKmluPmpuVGttbW1tgm5sk25rpG5rtW5qx25q2G1p6Wto+Ghn/2RlA3Z0BnZ0DHd0FHdzIXhzOnlzU3tya31yfn5xjn9xnYBwroBvwIFv0IFu4IBt731s/3prAIJ5AIN4AIN4AoR4I4V4P4Z3Vod3a4l2fIp2iot1mox1q410vI1zzI5z2o5y6I1x+ItvAJB9ApB9DJB9GpF8LJJ8RJN7WZR7apV6eZZ6iJd6mJh5qJl4uJp4yJp31pt25Jp185l0Ap2AA56ACp6AIJ+ANaB/SKF/WKF/aKJ+dqN+haR9laV9paZ8tad7w6h70ah636d576d4E6uDFquDHquDKqyDOqyDSa2CWK6CZq+CdbCBg7GBk7KAorOAsbR/v7R+zLV+2rV967V7HLaHIbeHKbeGM7eGP7iGTLiGWbmGZ7qFdbuFg7yEkr2Eob6Dr7+Du8CCyMCB1sGA6MF/KsGKLsGKNMKKPMKKRsKJUcOJXcSJasSJeMWIhcaIk8eHoMiHrcmGucqGxcuF1MyE6c2CM86NNc6NOs6NQc6MS8+MVc+MYdCMbdGMetKLhtKLktOKntSKqtWJttaJwteI0tiH6tmEJuKOKuKOL+KOOOKOQuOOTeOOWeSOZeSNceWNfOaNiOeMk+iMnumLqumLuOqKy+yI5+6GAQJbHQNbLgRaPQVaTAZaXAdabQdZgAhYmQpXsQtWww1V0xBU5BJU9RRS/xdR/xlQ/xtOCRdhFhhhJhlhNRthRRxhVR1gZh5geR9fkiFeqiJdvSJdziFc4CBb8h5a/xxZ/xlX/xtVECRmGSVmJCZlMidlQShlUSllYyplditkjyxjpy1iuS1iyi1h3Cxg7ipf/yhe/yRd/x5bGC9qHjBqJjBqMTFqPzJqTzNqYDRqdDZpjjdopDhntjlnxzhm2Thl6zZk/DRj/zBi/yphGTpwHTtwJDtwLzxvPT1vTT5vXj9vc0FujENtoURts0RsxERs1URr50Nq+UFp/z1o/zhmG0d1HUd1Ikh1K0l1O0l1S0p0XEx0ck1zi09zn1BysFBxwFBx0lBw5E9v9U5u/0tt/0ZsD1d6EFd6E1d6I1h6NFl5Rll5Wlp5cVx4iV13m152rF52vF51zl514F108Fxz/1ly/1VxAGh+AGl+CGl+Gmp+Kmp+Pmt9VW19bm58g297lHB7pXB6tnB5yHB52W946m13+Wp2/2Z1BXeCCHeCDniCFniCInmBO3qBVHyAbH5/f39/j4B+n4F+sIF9wYJ80oJ84YF78H96/3t5AIOFAIOFAISFBISFJIWFQIeEWIiEbIqDfYuCjIyCm42BrI6BvY6AzY9/3I9+6o5++Y18AJCJApGJDZGIGpKILpOIRpSHWpWHa5aGepeGiZiFmZmFqZqEupuDyZuD15uC5ZuB9ZqAAZ6MA56LCp+LIp+LNqCLSaGKWqKKaaOKeKSJhqWJlqaIpqeItqiHxKiG0qmG4KiF8KiDFKuOF6uOH6yOLKyOPK2OS66OWa6NZ6+NdrCMhbGMlLKLpLOLsrSKwLWKzbWJ27aI7LWHILeRJLeRK7eRNriRQbiRTrmRW7mRabqQd7uQhbyPlL2Por+OsL+OvcCNysGM18GM6sKKLsKVMcKVN8KVPsKUSMOUU8OUX8SUbMWUesaTh8eTlciSosmSrsqRu8uRx8uQ1syP682NNc6YOM6YPc6YRM+XTc+XV9CXY9CXb9GXe9KWiNOWlNSWoNWVq9aVt9aUxNeT1NiS7NqQKeKZLeOZMuOZOuOZROOZT+SZW+WZZ+WZc+aYfueYieiYlOmXoOqXq+qWuuuVze2U6e+SAQNqHgRqLgVpPgZpTgdpXQhpbglpggpomwxnswxmxg1m1g9l5xJk+BRj/xdi/xlh/xtfChlyFxpyJxtxNhxxRh1xVh5xZx9xeyBwlCJvrCNuviNu0CNt4iJs9CBr/x1q/xlp/xtoESV2GiZ2JSd2Myh2Qil2Uyp1ZCt1eCx1kS50qC9zuy9yzC9y3i5x8Cxw/ypv/yZu/yBtGTB7HzF7JzJ6MTN6QDN6UDR6YTZ6dTd5jzl4pTp3tzp3yDp22jl17Th0/jV0/zJz/yxxGDx/HTx/JD1/Lz5/Pj5+Tj9+X0F+dEJ9jkR8o0V7tEV7xUV610V56UR5+kJ4/z93/zl2GkmDHUmDIUmDK0qDO0uCS0yCXU2Cc0+BjFCAoFF/sVJ/wlJ+01F95VF89k98/017/0h6DViHDlmGEVmGIlqGNFqGRluGWVyFcV6EiV+EnGCDrGCCvWCCz1+B4V+A8V1//1t//1d9AGqKAGqKCWqKGmuKKWyJPm2JVG6IbnCIhHGHlXKGpXKGtnKFyHKE2nGE62+D+myC/2iBBXiNCXiNDnmNFXmNInqMO3uMVX2LbX+LgICKkIGJoIKJsYOIwoOI04OH4oKG8YGF/32EAISQAISQAIWQBYWQJYaPQIiPWImObYuOfoyNjY2NnY6MrY+Mvo+LzpCK3ZCK64+J+46IAJGTAJGTDJKTG5KTLpSSRpWSW5aRbJeRe5iRipmQmpqQq5uPu5yOypyO2JyN5pyM9puLAJ+WAZ+WCaCWI6CWN6GVSqKVW6OVaqSUeaWUh6aTl6eTp6iSt6mSxqmR06mQ4amQ8qmPFayZGKyZIKyZLq2ZPa6YTK6YW6+YabCYd7GXhrKXlbOWpbSWtLWVwbaVz7aU3LaT7raSI7ecJ7ecLricOLicQ7mcULmcXbqbarubebybh72alr6apL+ZssCZvsGYy8GY2cKX7MOVMcKfNMKfOcKfQcOfSsOfVcSfYcSfbsWffMaeiceel8idpMmdsMqdvMucycyb182b7c6ZOM+iO8+iP8+iRs+iT9CiWdCiZNGicdKhfdOhidShltWhodWgrdagudefxdif1tme7tqcLOOkL+OkNeSkPeSkRuSkUeWkXOakaOakdOejf+iji+mjluqioeuireuiu+yhz+6f6/CdAgR7HwZ6Lwd6Pgh6Tgl6Xgp6bwt5gwx5nA54tA93yA522A926RJ1+hR0/xdz/xly/xtxCxuBGByBJx2BNh6BRh+AVyCAaCGAfCJ/lSR+rSV9wCV90SV84yR79iJ6/yB5/xt4/xt3EieFGyiFJSmEMyqEQyuEUyyEZC2DeC6DkjCCqTGBvDGAzTCA3y9/8i5+/yx9/yh8/yJ7GTKIHzKIJzOIMTSIQDWIUDaHYjeHdjmGkDuFpjyEuDyEyTyD2zuC7jqB/jeB/zSA/y5/Fz2MHD6MIz6LLj+LPUCLTkGLX0KKdESJjkaIo0eItUeHxkeG2EeG6kaF+0SE/0GD/zuCGEqPG0uPIEuPKkyOOk2OSk6OXU+Nc1CNjFKMoFOLsVOKwlOK1FOJ5lKI91GI/06H/0qGC1qSDFqSD1uSIFuSMlyRRV2RWV6RcWCQiWGPnGKOrWKOvmKN0GGM4mGM8l+L/1yK/1iJAGuVAGuVCmyVGWyVKG2UPW6UVHCUbnGThHOSlXSRpnSRt3SQyXSQ23OP63GO+26O/2qNBHmYB3mYDHqYFHqYIXuYO3yXVX6WbYCWgIGVkIKVoIOUsYSUw4ST1IWS44SS8oKR/3+QAIWbAIWbAIabBYabJIebQIiaWYqabYyZf42Zjo6YnY+YrpCXv5CWz5GW3pGV7JCU/I+TAJKfAZKeDJOeGpOeLZSeRpadW5edbZicfJmci5qcm5ubrJybvJ2ay52Z2p2Z6J2Y+JyXAKCiAKChCKChI6GhOKKhS6OgXKSga6WgeqafiKefmKifqameuKqex6qd1aqc46qc86qbFaylGK2kIK2kL62kP66kTq+kXLCkarGjebKjh7Ojl7SiprWitbahw7ah0Leg3ref8LeeJrioKrioMLinOrmnRbmnUrqnXrqnbLunerymiL2ml7+lpsCls8GkwMGkzcKj28Oj7sOhM8KrNsOrO8OrQ8OrTMSrV8SqY8WqcMaqfceqisipmMmppcqossuovsyoysyn2c2m786lOs+uPc+uQdCuSNCuUNCtW9GtZtKtctKtftOti9Ssl9Wso9asr9erutirx9iq2Nmp8duoLuSwMeSwN+WwPuWvSOWvU+avXuevauevdeivgOmujOqul+uuouytru2tve6s0e+r7fGpAgWJHweILwiIPwqITguHXwyHcA2Hgw6GnRCFtRKEyBGD2RGD6xKC/BSB/xeA/xl//xt+Cx2OGB6OJx+NNiCNRiGNVyKMaCOMfCSLlSaKrSeJwCeJ0ieI5CaH9iSH/yKG/x2F/xuEEimRGimRJSqQMiuQQiyQUy6QZC+PeDCOkjKNqjONvDOMzjOL4DKK8jCK/y6J/yqI/ySHFzOUHTSUJTWUMDaTQDeTUDiTYTmSdjqSkDyRpz2QuT6Pyj6O3D2O7zyN/zmM/zaL/zCKFz+XGj+XIkCXLUGWPUKWTUOWX0SVdEWVjkeUpEiTtkmSx0mS2UmR60iQ/EaP/0OP/z6OFUyaGEyaHU2aKE6aOU6ZSk+ZXFGZc1KYjFSXoVWWslWWw1WV1VWU51SU+FOT/1CS/0yRCFydCV2dDF2dHl6dMF6dQ1+cWGCccGKbiWOanGSarWSZvmSY0GOY4mOX82GW/16W/1qVAG2gAG2gCW2gGW6gJ2+gPHCfU3GfbXOehHWdlXWdpnact3acyXab23Wb7HOa+3CZ/2yYAnqkBXqkCnukEnujH3yjOn2jVH+ibYGhgYKhkYOgoYSgsoWfxIaf1Iae5Iad84Sd/4GcAIanAIanAIanBoenJIimP4mmWYulbo2lf46kjo+knpCjr5GjwJGi0JKi35Kh7pGg/ZCgAJOqAZOqC5SqGJSqLZWpRpepXJiobZmofZqojJunnJynrZ2mvZ6mzJ6l256l6Z6k+Z2jAKCtAKGtB6GtI6KtOKOsS6SsXKWsbKare6ariairmamqqqqquaupyKup1quo5Kuo9aunFq2wGa2wIK6wMK6wQK+vTrCvXbCva7GverKuiLOumLWtqLattretxLes0bis37ir8riqJ7izK7izMbmzO7mzR7qzU7qyYLuybbyye72yib6xmL+xp8CwtcGwwcKvzsOv3MOu8MStNcO2OMO2PcO2RMS2TsS2WMW1ZMW1cca1f8e1jMi0mcm0p8q0s8uzv8yzzM2y286y8c+wO9C5PtC4QtC4SdG4UtG4XNK4Z9K4c9O4gNS3jNW3mNa3pNe2sNi2vNi2yNm12tq089yzMOW6M+W6OOa6P+a6Sea6U+e6X+i6aui5dum5geq5jOu5mOy4o+24r+64vu+30vC28PK0AweVHgmULguUPgyTTg6TXg+TcA+SgxGRnRORtRSPyROP2hOO7RKN/RSN/xeM/xmL/xuKCh+ZFyCZJSGZNSKYRSOYViSYZyWXeyeXlSiWriqVwSqU0imT5SiT9yeS/ySR/yCQ/xuPESqcGiucJCycMS2bQS6bUjCbZDGaeDKakjSZqjWYvTWXzjWX4DSW8zOV/zCU/yyU/yaTFjWfHDafJDafLzefPjieTzqeYTuddTydkD6cpz+buUCay0Ca3T+Z7z6Y/zyY/ziX/zOWFkGiGkGiIEKiK0OiO0SiTEWhXkahdEegjkmfpEqetkuex0ud2Uud60qc/Uib/0Wb/0CaEU6lFU6lGk+lJlClN1GlSFKlW1OkclSjjFajoVeislehw1eh1Veg6Faf+FWf/1Ke/06dBF6pBV+pCF+pG2CoLmGoQWGoVmOnb2SniGamnGalrWalvmak0Wak42Wj9GOi/2Gi/1yhAG6sAG6sB2+sGG+sJXCsOnGrUnOrbXWqhHaplXeppniot3ioynin23im7Ham/HOl/26lAnuvBHuvB3yvD3yvHX2vOX+uVICubYKtgYSskYWsooass4arxIer1Yiq5Yep9Iap/4OoAIezAIezAIeyBoiyJImyPoqxWIyxbo6wgI+wj5Cvn5GvsJKuwZOu0ZOt4JOt75Os/pKrAJS2AJS2CJS1FZW1Kpa1RZi0W5m0bZqzfZuzjJyznJ2yrZ6yvp+xzaCx3KCw6p+w+p6vAKG4AKG4BaK4IaK4N6O3S6S3XKW3bKa2e6e2iqm2mqq1qqu1uqy0yay016yz5ayz9qyyFa67Ga67H667MK+6QK+6T7C6XbG6bLK5erO5ibS5mbW4qLa4t7e3xbi307m24bm287m1KLm9LLm9Mrm9PLq9R7q9U7u9YLy9bry8fL28ir+8mcC7qMG7tsK6w8O6z8O53sS58sW4NcPAOcTAPsTARcTATsXAWcW/Zca/cse/f8i/jcm+msq+qMu+tMy9wc29zc683M6889C6O9HCPtHCQtHCSdHCUtLCXNLCZ9PCdNTBgNXBjNbBmdfApdjAsdjAvNm/ytq/29u+9d29L+bEMubEN+fEP+fDSefDU+jDX+nDa+nDd+rDgevCjezCmO3Co+7CsO/BvvDB0/LA8vS+AwqgHQyfLQ2fPQ6fTRCeXhGebxKdgxOdnRWcthebyRaa2haa7RSZ/xSY/xeX/xmX/xuWCiGlFyKkIyOkMySkRCakVSejZyijeymilSuhriygwSyg0yyf5Sue+Cme/yed/yOc/xybESyoGS2nJC6nLy+nQDCnUTKmYzOmdzSlkjakqjekvTijzjei4Tei9DWh/zOg/y+g/ymfEzerGTirIjiqLTmqPTqqTjuqYD2pdT6pj0Cop0KnukKmy0Km3UKl8ECk/z6k/zuj/zWiFUOuGUOuHkSuKUStOkatS0etXUitc0msjkurpE2qtk2qx02p2k2p7Eyo/Uun/0in/0OmDVCxEFGxF1GxIlKxNFOxRlSwWlWwcVavjFiuoVmuslqtxFqt1lms6Fmr+Ver/1Wq/1CpAWG0AWG0BGG0F2K0K2O0PmS0VGWzbmeyh2ixm2mxrWmwvmmw0Wmv42iv9Gau/2Ou/1+tAHC4AHC4BHC3FHG3I3K3N3O3UHS2a3a1g3i1lXm0pnq0t3qzynuz3Hqy7Hmx/Hax/3GwAHy7AX27A326CX66GH66N4C6UoG5bIO4gYW4kYa3ooe3s4i2xYm21om15om19Ii0/4azAIi+AIi+AIm9A4m9IYq9PIy8V428bY+7f5C7j5G6n5K6sJO5wpS50pW44ZW475S3/5O2AJXAAJXABJXAEJbAJpe/Q5m/Wpq+bZu+fZy9jJ29nZ69rp+8vqC8zqG73KG766G6+6C5AKLDAKLCBKPCHaPCNKTCSqXBXKbBbKfBe6jAiqrAmqu/q6y/u62+yq2+2K695q699628Eq7FFa7FHK/ELq/EP7DETrHEXbLEbLPDerTDibXDmbbCqbfCuLjBxrnB07rA4rrA9Lq/KLnHK7nHMbrHO7rHR7vGU7vGYLzGbr3GfL7Fi7/FmsDFqcLEtsPEw8PD0MTD38XC88bBNcTJOMTJPcTJRcXJTsXJWcbJZcfJcsfIgMjIjcnIm8vHqMzHtc3Gwc3Gzs7G3s/F9dDEOtHLPdLLQtLLSNLLUdPLW9PLZ9TLdNXKgNbKjdfKmdjKpdjJsdnJvdrJytvI3NzH997GLufNMejNNujNPujNSOnMU+nMXurMauvMduzMgezMje3LmO7LpO/LsPDKv/LK1PPJ9PXHAwyrGw6rLBCqOxGqTBKqXRSqbhWpghepnBiothmnyRmm2xmm7hil/xak/xaj/xmj/xuiCSOwFiSwIiawMSewQiivVCmvZiqveiyulC6tri+swS+r0y+r5S6q+Cyq/yqp/yao/x+oEC6zGC+zIzCzLjGzPjOyUDSyYjWydjexkTiwqjqvvTqvzjqu4Tmu9Dit/zas/zKs/yyrEDm2Fjq2Hzq2Kzu2Oz22TD61Xz+1dEG0j0Ozp0SyuUSyy0Sx3USx8EOw/0Gv/z6v/ziuFEW5GEW5HUa5Jke5N0i5SUm4XEq4cky3jU62pE+2tk+1x1C12k+07E+z/U2z/0qy/0axClO9DVO8EVS8HFW8MFW8Q1a8V1i7b1m6ilu5oFy5sly4w1y41ly36Fu2+Vq2/1e1/1O1AGO/AGO/AGS/D2W/JmW/Oma/UWi+a2m9hmu8mmy8rGy7vWy70Gy642u69Gm5/2a4/2K4AHHCAHHCAXLCD3PCH3PCM3XBTnbBanjAgnq/lHu/pXy+t32+yX2923287Hy8/Hm7/3S7AH7FAH7FAH7FBH/FEYDEM4HEUIPDa4XCgIbCkYfBoonBs4rAxYrA1ou/5ou/9Yq+/4i+AInIAInHAIrHAIrHHYvHOI3GVY7GbJDFf5HFjpPEn5TEsJXDwpbC0pbC4ZbB8JbB/5XAAJbKAJbKA5fKC5fJH5jJQJrIWJvIbJzHfJ7HjJ/HnKDGraHGvqLFzqLE3aPE66LE/KHDAKPMAKPMAqTMF6TLMKXLR6bLWqjKa6nKeqrJiavJmqzJq63Iu67Iyq/H2K/H5q/G+K/FDK/OD6/OF7DOKbDNPLHNTbLNXLPNa7TMerXMibbMmbfLqbjLuLnKxrrK1LvJ4rvJ9bvIJbrQKLrQL7rQObvQRbvQUrzPX73Pbb7PfL/Oi8DOmsHOqcLNt8PNxMTN0cXM4MbM9cfLMsXSNsXSO8XSQ8XSTcbSWMbSZMfSccjRf8nRjcrRm8vQqczQts3Qws7Pz8/P39DO9tHNONLVO9LVP9PURtPUT9PUWtTUZtXUc9bUgNfUjNjTmdnTptnTstrSvtvSy9zS3d3R+d/QK+nWLunWM+nWO+nWRurWUerWXevWaezWdu3Wge7Vje/VmPDVpPHVsPLVv/PU1fXU9vfSAxG3FxK3KRO2ORS2ShW2Wxe1bRi1gRq0mxuztR2yyR2y2xyx7hux/xqw/xav/xmv/xquBia8FCe8ICi7Lim7QCu7Uiy6ZC26eC+5kzC4rTK3wTK30jK25TG2+DC1/y20/ym0/yOzDjG/FjK/IDO+KzS+OzW+Tja9YDi9dTm8jzu7qT26vD26zj254T259Du4/zm3/zW3/y+2DjvBEzzBGj3BJz7BOD/BSkDAXUHAckO/jUW+pke9uUe9y0e83Ue78Ea7/0S6/0G6/zy5EkfEFUjEGkjEIknEM0rDRkzDWU3Db07Ci1DBo1LAtVLAx1K/2VK+7FK+/VC9/029/0m8CVbHC1bHDlfHE1jGKljGP1nGVFvFbFzFiF7En1/DsV/Cwl/C1V/B6F7B+V3A/1q//1a/AGXKAGbJAGbJBmfJH2jJNWnITWrIaGzHg27GmG/FqnDFvG/Ez2/E4m7D82zD/2nC/2XCAHPMAHPMAHTMCXTMGHXLLnbLSnjKZ3rJgHzJk33IpX7Itn/HyYDG24DG63/F+33F/3jEAH/OAH/OAIDOAIDOBYHOLILNTITNaIbMf4jLkInLoYrKsovKxIzJ1o3I5o3I9YzH/4rHAIrRAIvRAIvQAIzQFY3QMY7PUZDPapLOfZPNjZTNnpXNsJbMwZfL0pjL4ZjK8JjK/5fJAJfTAJfTAJjTB5jSFZrSOpvSVZ3Rap7Qe5/QiqDQm6HPraLPvqPOzqTO3aTN66TN/KPMAKTVAKTVAKTVDqXUKabUQ6fUV6nTaarTeavTiKzSma3Sqq7Ru6/RyrDQ2LHQ57HP+bDPArDXBrDXDrDXIbHXOLLWSrPWWrTWabXVebbViLfVmLjUqbnUuLrUx7vT1LzT47zS9r3SHrrZI7vZKrvZNbvZQrzZUL3ZXr7ZbL7Ye8DYisHYmsLXqcPXt8TXxcXX0sbW4cfW9sjVLsXcMsXcOMbcQMbcS8bcVsfcY8jccMncf8rbjcvbm8zbqc3bts7bw8/a0NDa4NHa+NLZM9PgN9PgPNTgQ9TgTdTgWNXgZNbgcdfff9jfjNnfmdrfptvfstvfvtzfzN3f3t7e++DdJurjKerjL+rjOOvjQ+vjT+zjXOzjaO3jde7jge/jjfDjmPHjpPLksfTkwPXj1vfj+frhBBTCFBXCJhbBNhfBSBnBWRrAaxvAgB2/mh6+tCC9yCC92iC87R+7/x27/xq6/xi5/xq5BSjHESrGHivGKyzGPS7FTy/FYjDEdjLEkTTDrDXCwDbB0jXB5TXA+DO//zG//y2+/ye9CTPJETTJHDXJKDbIODjISjnIXTvHcjzGjT7FqEDEvEDEzUDD4EDD8z/C/z3B/znB/zPACz7MED/LFz/LIkDLM0LLRkPKWkTKb0bJi0jIpUrHuErGykrG3UrF8ErE/0jE/0XD/0DDC0rOD0vOFEvOHUzNLU3NQU/NVVDMbFHLiFPKoVXKtFbJxlbI2VbI7FXH/VTH/1HG/03FBFnQBlrQCVrQDVvQIVzQOF3PTl7PZ2DOhGLNnGPMr2PMwWPL1GPK52LK+GHJ/17J/1rIAGjTAGjTAGnTAGrSGGvSLmzSR23RY2/QgHHPlnLPqHPOunPOzXPN4HLM8nHM/27L/2nLAHXVAHXVAHXVBXbVE3fUJnjURHrTYnzTfn7SkYDRo4HRtYHQx4LP2oLP64LO+4DO/33NAIDXAIHXAIHXAILXAIPXI4TWRobWZYjVfIrUjovUn4zTsY3TxI7S1Y/S5Y/R9Y7R/43QAIzaAIzaAIzaAI3ZCo7ZKJDZS5HYZpPXe5XXi5bWnZfWrpjVwJnV0ZrU4ZrU8JrT/5nTAJjcAJncAJncAprcDJvbMZzbUJ7aZp/aeKHZiKLZmqPZq6TYvaXYzabX3KbX66bX/aXWAKXfAKXfAKXfAqbeIafePKneU6rdZqvdd6zdhq3cmK/cqbDcurHbyrLb2LLb57La+bLaALDiALHiAbHiEbLhMLPhRbThV7XhZ7bhd7fgh7jgl7ngqLrguLzfx73f1b3f477f977eFLvmGrvlI7zlLrzlPr3lTb3lXL7lar/lesDlicLlmcPlqcTkt8Xkxcbk08fk4sjk98nkJ8bqK8bqMsbqPMfqR8fqU8jqYMnqbsrqfcvqjMzqm83qqc7qt8/qxNDr0dHr4dLr+tTqK9TwL9TwNdXwPtXwSNXwVNbxYdfxb9jxfdnxi9rxmdvxptzxst3yv97yzd/y4ODy/eLyH+v3Iuv3J+z3Mez3Puz3S+33We74Zu/4dPD4gPH5jfL5mfP6pfT6sfb7wff81/n8//39\",chrome64:\"AAAABgAAEAAAHAAALQEAQQIAXQUAeQkAjg0AoBAAshMAwRUA0BgA3hoA7R0A/CAA/SAAAwcBCggBFAkBIAoBMQwBRQ8BYRIBfBUAjxcAoRkAshoAwRoAzxoA3RoA7BwA+x8A/SAACRMFDxMFFxQFJBUFNRcFSRkEZRwEfh8DkiEDoyMCtCQCwyQB0SQB3iMA7CIA+iAA/CAAEiINFyINHSINKCMMOiUMTiYLayoKgiwKlS0Jpi4Hti4GxS4F0i0E4C0D7isC/CkB/SkBGzUWHjUWJTYWLzcVQDgVVjoUcz0TiD8Smj8Rqz8Quj4OyD0N1TsK4zkI8DYG/TME/jMDGE4gHE8gJU8gNlAfSFEfYFMee1UdjlUcoFUbsFUav1QYzVMW2lAU6E4S9UkP/0ML/0MKGmkqIWkqLGoqO2sqUG0pbG8ognEolXInpnMmtnMkxXMj03Eh4W4f7mkc+2MZ/10V/1wVIHw1KHw1NH01RX40XYA0dYMziIUymYYxqocvuoguyYks14kq5Yko84gm/4Yk/4Eh/4AgMI5BNo5BQo9BVpBAa5JAfZQ/jZY+nZc9rZg8vZo7y5s52Zs455w19Zwy/5sv/5kq/5kpRp5MTZ9MWJ9MZ6FLdaJLhKNLkqVKoaZJsahIv6lHzapG26tE6axC96xA/609/6w4/6w4Xa1aYa1aaK5aca5afK9aibFZlrJZpbNYs7VXwLZWzrdV3LlT6rpR+LtO/7tL/7xH/7xGablra7prcLpreLtrgrxqjb1qmb5ppr9ptMBowMJnzsNm28Rl6cZj98dg/8hc/8lX/8lXc8Z4dcZ4ecZ4gMd4iMh4ksl3nsp3qst2tsx2ws51zs9029Bz6dFy9tNw/9Rt/9Vp/9VpfdKDf9ODg9ODiNODkNSDmdWDo9aDrteCudiCxdqB0NuA3dx/6t5+9999/+B7/+F4/+F3h9+Pid+PjOCPkeCPmOGOoOKOqeOOsuSOveWNx+aN0+eM3+mL7OqL+euJ/+yH/+6F/+6Ekeyak+yale2ame2an+6apu6aru+Zt/CZwfGZy/KY1vOY4vWX7vaX+viV//mU//uR//uRmvqnm/qnnfqnofunpvunrPyntP2mvP6mxf+mzv+m2v+l5f+l8v+l/v+k//+j//+h//+gAAABAwABDAABGQABKwABPwEBXAQBdgcBiwoBnQ0ArhAAvhMAzRYA3BgA6xsA+h4A/B4AAQUHBgYHEQcGHQgGLwoGQw0GYBAFeRQFjRUEnxcDrxgDvhkCzBkB2hgB6RoA+B0A+h0ABxEMDBEMFBIMIhMMMxULRxgLZBsKfB4JkCAIoSEHsSIGwCIFziIE3CID6iEC+B4B+R4BESETFSETGyETJyITOSQSTSYSaikRgSsQkywPpC0NtC0Mwy0L0CwJ3isH7CoF+igD+ygDGjQcHTQcJDUcLjYbPzcbVTkacjwZhj4YmD4XqT4VuD4UxjwS1DoQ4TgN7zUK/DIH/TIHF04mG04mI08lNFAlRlEkYFIkelQjjVUhnlUgrlQfvVMdy1Ib2VAZ5k0W80kT/0MP/0IPF2gwHmgwKWkwOWowT2wvam4ugHAtk3EspHIrtHIqw3Io0XAm320k7Gkh+WMe/1wa/1sZHXs7JXs7MXw7Q307W386dII5hoM4l4U3qIY2uIc1x4gz1Ygx44gv8Ycs/oUp/4Al/38lLY1GNI1GQY5FVI9FaZFEe5NEi5VDnJZCq5hBu5lAyZo/15o95Zs785s5/5o1/5gx/5gwRJ1SS55RV59RZaBRc6FRgqJQkKRQn6VPr6dOvahNy6lM2apK56tI9atG/6xC/6s+/6s+WqtgXqxgZaxgbq1geq5ghq9flLFforJesLRdvrVczLZb2bdZ57hX9blV/7pR/7pM/7pMZrhvablvbrlvdrpvf7tvi7xul71upL5tsr9tvsFszMJr2cNq58Ro9MVm/8di/8de/8hdccV7dMV7eMZ7fsZ7h8d7kch6nMl6qMp6tMx5wM14zc542s9359F19dJ0/9Nx/9Ru/9RtfNKGftKGgtKGh9OGj9SGmNWGotaFrdeFuNiFw9mEz9qD3NyD6d2B9t6A/99+/+B7/+B7h9+RiN+Ri9+RkOCRl+CQn+GQqOKQsuOQvOSPx+aP0ueO3uiO6+mN+OuM/+yK/+2H/+2HkOyckuycleycme2cnu2cpe6bru+btvCbwPGbyvKa1fOa4fSZ7faZ+veX//mW//qU//qTmfqpm/qpnfqooPuopfuorPyos/2ou/2oxP+ozf+o2f+n5f+n8f+m/f+m//+k//+i//+iAAAFAgAECgAEFwAEKQAEPgEEWwMDdQYDiQgDnAsDrA4CvBECyxQC2hcB6RoB+B0A+h0AAQUNBQUNDwYMHAcMLgkMQgsLXxAKeBIKixQJnRYIrRcHvBcGyhcE2BcD5xkC9hwB+BwBBRATChASEhESIBISMhQRRhYRYxoQex0Pjh8PnyANryEMviELzCEJ2iEH6B8F9h0D9x0DDh8aEyAaGSAaJSEZNyMZTCUYaSgXfyoWkSsVoisUsisSwSsRzisP3CoN6ikK+CcH+SYGGDMjGzMjIjQjLTUiPTYiVDghcDsghDwflj0epz0ctjwbxDsZ0jkW3zcT7DQR+jEN/DENFk0tGU0tIE4tME8tQ1AsXlIrd1MqilQpm1MorFMmulIlyVAj1k4g40wd8Uca/UEV/kEVEWc5GWc4Jmg4Nmk4TGs3aG03fm82kHA1oXEzsXEywHEwzm8u3Gwr6Wgo9mEl/1sg/1ogGXpCIHpCLXtCQHxCWX5BcYBAg4JAlIQ/pYU+tYY9xIY70oY64IY37oU1/IMx/34s/34sKYtOMIxNPY1NUo5NZpBNeJJMiJNLmJVLqJZKuJdJxphH1JlG4plE8JlB/pk+/5c6/5c5P5tcR5xcU5xbYJ5bb59bfaBbjKJam6NZqqRYuaZXx6dW1ahV46lS8alQ/6pM/6lI/6lHValqWqpqYKpqaqtqdaxqgq1pj69pnrBorLFoubNnx7Rm1bVk47Zi8bdg/rhd/7hY/7hYY7d2Zrd2a7h2c7h2fLl1iLp1lLt1ob10r750u79zycBy1sJx48Nw8cRu/sVr/8Vo/8Vob8SAcsWAdsWAfMWAhcaAj8d/msh/psl/sst+vsx+y8192M585dB78tF6/9J3/9J0/9N0etGKfNGKgNKKhtKKjdOKltSKoNWJq9aJtteJwtiIztqI2tuH59yG9N2F/96D/9+A/9+Ahd6Uh96Uit+Uj9+UleCUneGUpuKUsOOUu+STxeWT0eaS3eeS6emR9uqQ/+uO/+yM/+yMj+ufkeufk+yfl+yfne2fpO2frO6fte+ev/CeyfGe1POd4PSd7PWc+Peb//ia//mY//qXmPmrmfqrnPqrn/qrpPurqvursvyruv2rw/6rzP+q1/+q4/+q8P+p/P+p//+n//+m//+lAAALAQAKBwAKFAAKJwAKPAAKWgIJcwQJhwcImQoHqg0Gug8GyRIF2BUE5xgD9hsC+BsCAAMVAwQVDAUUGQYUKwcUQAoTXg4SdRERiRMQmxQPqxUOuhYMyBYL1hUI5RcH9BoF9hoFAw4bBw4bDw8bHRAbLxIaRBUaYRgZeBsYix0XnB4WrB8Uux8Tyh8R2B8P5R4M9BsI9RsICx0jDx4jFh4jIR8jNCEiSSMiZiYheyggjikfnykdrikcvSkayykY2SgV5ycS9SUO9iQOFjEtGTEtHzItKjMsOTQsUTYrbTkqgDopkjsoozsmsjolwDkjzjch2zQe6TIa9y8W+C8WFEs4GEw3Hkw3LE03QE42W1A1dFI0hlIzl1IyqFEwtlAvxU4t0kwr30ko7UUk+j8f+z8fCGVCEmVCIWZCMmdCSWlBZWtBem1AjG4/nW8+rW89vG87ym052Go35WYz8l8v/lkq/1gqGHdOHHhOJ3lOPHpNVnxNbX5Mf4BMkIFLoYJKsINJv4RIzoRG24RE6YNB94E+/3w5/3w4H4hcKIlbNopbTItbYY1bco9agpBak5JZo5NZspRXwJVWz5ZU3ZZT65ZQ+ZZM/5RH/5RHOZlpQJlpTJpoWptoaZxod55ohp9nlaBnpaJms6NlwaRkz6Vj3aZh66Zf+qdc/6ZX/6ZXUahzVahzXKhzZqlzcapzfqtzi61ymq5yqK9xtrFxw7Jw0bNv3rRt7LRr+rVp/7Vl/7VlYLZ9Y7Z9aLZ9cLd9ebh9hbl8kbp8nrt8q7x7uL57xr9608B54MF47sJ2/MN0/8Ny/8NxbcOGb8OGc8SGesSGgsWGjcaGmMeGo8iFsMmFvMuEyMyE1c2D4s6C8M+B/dB//9F9/9F8eNCQetCQftGQg9GQi9KQlNOPntSPqdWPtNaPv9eOy9iO2NqN5duM8tyL/t2J/96H/96Hg92ahd2aiN6Zjd6Zk9+Zm+CZpOGZruKZuOOYw+SYzuWY2+aX5+iW9OmV/+qU/+uS/+uSjeqkj+ukkeukluukm+yjouyjqu2js+6jve+jx/Ci0vKi3vOi6vSh9vag//ef//id//mdl/mvmPmvmvmvnvqvo/qvqfuvsPyvufyvwv2vy/+u1f+u4f+u7v+t+/+t//+s//+q//+qAAAUAAATBAATEQATJAATOQATWAEScAMRhAURlgcQpwoPtw0OxhAM1RML5BYJ9BkH9RkHAAIfAQMfBwMfFQQfKAUePQceWwwdcg4chRAblxIapxMZthMXxBMV0hMS4RQQ8RcN8hgNAQsmBAwmCg0mGA4mKxAlQBIlXhYkdBgjhxoimBshqBwgtxwexR0c0xwa4RsW7xkS8RgSBhsuCxsuEhwuHh0uMB4tRiEtYyQsdyUriSYqmiYoqiYnuCYlxiYj1CUh4iQe8CEa8iEZES84FS84HDA3JzE3NTI3TjQ2aTc1fDg0jjkznjkyrTgwuzYuyTQs1jIp5C8m8iwi8ywiEklEFkpEHEpDJ0tDPExDV05Cb1BBgVBAk1A/o089sU08v0w6zUo32kc050Iw9Twr9jwrAmJRBmJRGGNRK2RRQ2ZRYGhQdGpQhmtPl2xOp2xMtmxLxGpJ0mhG32ND7F0++VY4+1U4EnReFnReH3VeNHZeT3ldZntdeHxdiX5cmn9bqYBauIBZxoBX1YBV439T8X1P/nlK/3hJD4VpHYZpL4dpRohpW4ppbIxofI1ojY5nnZBnrJFmupJlyJJk1pNi5JNg8pJd/5BZ/5BZNZZzPZdzSJhzVplzZJpyc5tygp1ykZ5xoJ9xr6FwvaJvyqJu2KNt5qRs9KNp/6Nm/6NmTqZ8UqZ8Wad8Yqd8bah8eqp7iKt7lqx7pK16sq96v7B5zbF42rJ36LJ29rN0/7Nx/7JxXbSFYLSFZbWFbbWFdraEgreEjriEm7qEqLuDtbyDwr2Cz76C3b+B6sB/+MF+/8F7/8F7asKObMKOcMKOd8ONf8SNisWNlcaNoceNrciMucmMxcqL0suL382K7M6J+s6H/8+F/8+Fdc+Xd8+Xe8+WgdCWiNGWkdKWm9OWptOWsdWVvdaVydeV1diU4tmT79uS/NyR/9yP/9yPgNyggtyghd2git2gkd6fmd6fot+fq+CftuGfweOezOSe2OWe5ead8eic/umb/+qZ/+qZi+mpjOqpj+qpk+qpmeupoOupqOypse2pu+6oxe+o0PGo2/Kn6POn9PWm//al//ej//ejlfi0lvi0mPi0nPm0ofm0p/q0rvu0t/y0wP20yf6z0/+z3/+z7P+y+P+y//+x//+v//+vAAAfAAAfAgAeDAAeHwAeNQAeVAAdawEdfwMdkQUcogcbsgoawQ0Z0A8X4BMV7xYS8RYRAAErAAEqAwIqEAIqIwMqOQUqVwkpbQsogA0nkg4moQ8lsBAkvxAizQ8f3BAd6xMa7RQaAAkyAQkyBgoyEwsyJg0xPA8xWhMwbxUwgRcvkxguoxkssRkrwBkpzhgn3Bck6hUg6xUfAxg7BRk7DBk7GRo7Kxs7QR06XiA6ciI5hCM4lSM2pCM1syMzwSIxzyEu3SAr6h4n7B0nCytHDixHFixHIS1HLy9HSTFGYjNFdTRFhzVElzVCpzRBtTI/wjA80C453is16ygw7SgwEEZVFEZVGUdVIUhUNElUUUtTaExTekxSi0xRm0tPqklOuEhMxUVJ00NG4D9C7Tg97zg8A15hBV9hC2BhImFhPWNgWWVgbmdff2hfkWheoGldr2hcvWday2RY2F9V5FlR8VFM81BLC3FrEnJrHHJrLnRqS3ZqYXhqc3pphHtplHxopH1nsn1mwX1lz31k3Hxi6npf+HVc+XVbCYN0FYR0K4V0QoZ0V4hzaIpzeItziIxymI5yp49xto9wxJBv0ZBu35Bt7Y9r+41o/Y1nMpV9OZV8RZZ8U5d8YZh8b5p8fpt8jZx7nJ57q596uaB6xqB51KF44qF28KF1/qBy/6BySqSFTqWFVaWFX6aFaqeEd6iEhKmEkqqEoKyDrq2DvK6Cya+C1rCB5LCA8rB+/7B8/7B8WrONXbONYrONabSNc7WNf7aNi7eMmLiMpbmMsrqLv7uLzLyK2b2K576J9L+H/7+F/7+FZsCVacGVbcGVdMGVfMKVh8OVksSVncWVqcaUtsiUwsmUz8qT3MuS6cyR982Q/82O/82Oc86edc6eeM6efs+ehc+ejtCdmNGdo9KdrtOdutSdxtWc0tec39ib7Nma+tqZ/9uX/9uXftumgNumg9umiNymjt2mlt2mn96mqd+ms+CmvuGlyeOl1eSk4uWk7+aj/Oei/+ig/+mgieiviuivjemvkemvl+qvnuqvpuuvr+yvuO2vwu6uzfCu2fGu5fKt8vOs/vWr//aq//aqk/e6lPe6lvi6mvi6n/m5pfm5rPq5tPu5vvy5x/250f653f+46f+49v+4//+3//+1//+1AAArAAArAAArBgArGgArMAArTwAqZgAqeQEqjAIpnAQorAYnuwkmygsl2g4j6RIg6xIgAAA5AAA5AAA5CQE5HQI5MgM5UQU5Zwc4eQg4igo3mgo1qgs0uAsyxgsv1Qss5Q8p5g8oAARDAAVDAgVDDAZDHwhDNQtCUw5CaBBBehJBixNAmxM+qhQ9uBQ7xhM51BI14xAx5BAwABRNARRNBRVNEhZNIxdMOxlMVhxLah1LfB5KjR5JnB5Hqh5GuB1ExhxC1Bo/4hg64xg6BSdXCChXDyhXGylWKStWRC1WXC9VbzFUgDFUkDFTnzBRrS5QuixOyClL1SZI4yJE5CJEDURiEURiF0ViHkViL0dhTUlhY0pgdUpfhklelkhdpEZcskVav0JYzD9W2TpT5TRP5zNOBFxsBlxsCV1sHF5sOmFsVWNraWRre2VqjGZqnGZpqmZouGRnxWJl0l1i31Zf605b7E1aA291DHB1GXB1K3J1SHR1XXZ0b3d0gHl0kHpzoHtyrntyvHtxyntw13pu5Xds8nNp9HJoBYJ+EYJ+KYN+QIV9U4Z9ZIh9dYl9hYp8lIx8o417so17v456zY5524546Y1294t0+ItzL5OGN5SGQpSGT5WGXZaFbJiFe5mFipqFmZyEp52EtZ6Dwp6D0J+C3p+B659/+p59+559R6OOS6OOUqOOW6SOZqWOc6aNgaeNj6mNnaqNqquMuKyMxa2L066K4K6J7q+I/K6G/q6GVrGWWbGWXrKWZrKVcLOVe7SViLWVlLaVobeVrrmUu7qUyLuT1byT47yS8b2R/r2P/72OY7+dZr+dar+dccCdecGdg8Kdj8OdmsSdpsWcssacv8eczMib2Mmb5sqa9MuZ/8uX/8uXcMylcsyldc2le82lg86li8+lldCloNGlq9Kkt9Okw9Skz9Wj3Naj6dei9tih/9mf/9mfe9qtfdqtgNqthduti9utk9ytnN2tpt6tsd+su+Csx+Gs0+Ks3+Sr7OWq+eap/+eo/+eohue1iOe1iui1jui1lOm1m+m1o+q1rOu1tuy1wO21y+601vC04/G07/Kz/PSy//Wx//WwkPe/kve/lPe/mPe/nPi/o/i/qvm/svq/u/u/xfy/z/2/2v++5v++8/++//+9//+7//+7AAA8AAA9AAA9AQA9EQA9KAA9RwA9XgA8cQA8gwA8kwE7owI6sgQ5wgY40Qk14gwy4wwyAABLAABLAABLAwBLFQBLLAFKSgJKXwNJcQRJgwVIkwZHoQZGrwZEvQZCzAZA3Ak+3Qk9AAJSAAJSAANSBgNSGQVRMAdRTQpRYgxQcw1PhA9PlA9Oog9NsA9Lvg5JzA1H2gpE2wpEABBaABFaAhFZDRNZHhRZNxZZUhlYZRpXdxtXhxtWlhpVpBpUshlSvxhQzRZO2xNL3BNLAiViBSViCyZiFydiJShiQCthWC1hay5gfC5gjC5fmi1dqCtctSlawiVYzyJW3B5T3R5SCkJtDkJtFENtHERtLEVsSkdsX0hrcUhrgkhqkUZpoERnrUJmukBkxzxi0zdg4DBc4S9cBlp3B1t3Clt3Fl13N192UmF2ZmJ2d2N1iGR1l2R0pmRztGJywWBwzltu2lRr5ktn50pnAm1/CG5/Fm9/KnB/RXJ/WnR+a3Z+fHd+jHh9m3l9qnl8uHl7xXl603d54HV37nB073B0BYCHEICHJoKHPoOHUISHYYaHcYeGgYmGkYqGn4uFrouFu4yEyYyD14yC5IuB8oh+9Ih+LZKPNJKPP5OPTJSPWpWPaJaPd5eOhpiOlZqOo5uNsZyNv5yMzJ2M2p2L552J9pyH95uHRKGXSKGXTqKWWKOWY6OWcKSWfqaWjKeWmaiVp6mVtKqVwquUz6yU3a2T6q2R+ayQ+qyQU6+eVrCeW7CeY7GebbGeeLKehbSdkbWdnradq7eduLicxbmc0rqb37ua7buZ/LuY/buYYL2lY76lZ76lbr6ldr+lgMCljMGll8Klo8Okr8SkvMWkyMej1cij4smi8Mmh/sqg/8qfbcusb8usc8useMysgM2sic2sk86snc+sqNCstNKswNOrzNSr2dWq5taq89ep/9en/9enedm0e9m0ftm0gtm0idq0kdu0mty0o920rt6zud+zxOCz0OGz3OKy6eOy9+Wx/+Wv/+avhOa8hea8iOe8jOe8kue8mei8oem8quq8s+u7vey7yO271O674PC67fG6+vK5//S4//S3jvbFj/bFkvbFlfbFmvfFoPjFqPjFsPnFufrFwvvFzf3F2P7E5P/E8P/E/v/D///C///CAABMAABMAABMAABMCwBMIwBMQQBMVwBLagBLfABLjABLnABKqwFKugNJyQRH2QdF2gdFAABXAABWAABXAQBWDwBWKABWRgFWWgJVbAJVfQNUjQNTmwRTqQNStwNQxQNO1QVN1gVNAAFdAAFdAAFdAwJdFANdLQVcSQdcXQlcbwpbfwtajwxanQxZqwtYuApWxglU1AZS1QZRAA5lAA5lAQ9lCRBkGhJkNRRkThdjYRhjchhighhhkRdgnxdfrRZeuhRcxxJa1Q9Y1g9YASNtAyNtCCRtFCVtIyZtPilsVStsZyxreCxqhyxqlippoylnsCZmvSNkyh9i1xtf2BtfCEB4C0F4EUF4GUJ3KkR3R0Z3XEd2bUd1fkZ0jUVzm0JyqUBxtj1vwjptzzVr2y5n3C1nBlmBB1mBClqBE1uBNV2AT1+AYmCAc2F/hGJ/k2J+omJ9sGF8vV57yVl51lJ24kly40hxAGyJBGyJFG2JKW+JQ3GIV3KIaHSIeXWIiXaHmHeHpneGtHeFwneEz3WD3HOB6m5/625+CX+REn+RJYCRO4GQTYOQXoSQboWQfoeQjYiPnImPqoqOuIqOxYqN04qM4ImK7oaI8IaILJCYMpCYPZGYSZKYV5OYZZSXdJWXg5eXkpiXoJmWrZqWu5uVyJuV1puU5JuT8pqR85mRQaCfRaCfTKCfVaGfYKKfbaOfe6SeiaWelqaepKiesamdvqmdzKqc2aub56ua9aqZ96qZUK6mU66mWK+mYK+marCmdbGmgrKlj7Olm7SlqLWltbakwrekz7ij3Lmj6rmi+Lmg+rmgXrytYLytZL2ta72tdL6tfr+sicCslMGsoMKsrMOsucSrxcWr0sar38eq7cip+8io/cioasq0bMq0cMq0dsu0fcu0hsy0kM2zms6zps+zsdCzvdGzydKy1tOy49Sx8NWx/tav/9avdte7eNi7e9i7gNi7h9m7jtq7l9q7odu7q9y6tt26wd+6zeC62eG55uK59OO4/+S3/+S3guXCg+XChuXCiubCj+bClufCnujCp+nCserCu+vCxuzC0e3B3e7B6vDB9/HA//K///K/jPXMjfXMj/XMk/bMmPbLnvfLpfjLrvjLt/nLwPvLy/zL1f3L4f7L7v/K/P/K///I///IAABXAABXAABXAABXBgBXHwBXPQBXUgBXZQBXdwBWhwBWlgBWpQBWtAFVwwJU0gNT1ARTAABhAABhAABiAABiCwBhJQBhQgBhVgFhaAFgeQJgiAJflgJepAJdsgFcwAFazwJZ0AJZAABoAABoAAFoAgFoEQJnKwNnRgVnWQZmaghmewlligllmAlkpghjswdhwQZgzgRdzwNdAAxvAAxvAA1vBw5vFw9vMxJuSxRuXRVtbhVsfhVsjRVrmhRqqBNptRFnww9m0Axj0QxjACF3ASF3BiJ3ESN3ICR3PCd2Uil2Yyp1dCp1gyl0kihznyZyrCNwuSBuxRxs0hhq0xdpBz+CCj+BD0CBGEGBKUOBRUSAWUWAakV/e0V+ikN9mEF8pT57sjt5vjh3yzN11yty2CpyBleKCFeKCliKElqKNFyKTV2JX1+JcGCJgWCIkGGInmCHrF+GuVyFxliD0lCA3Ud830Z8AGqSAWuSEWySKG2SQW+RVHGRZXKRdXORhXSQlHWQo3WPsHWPvnWOy3SN2XGL5m2J52yIDX2ZE36ZJH+ZOYCZS4GZW4OZa4SYe4WYioaYmYeYp4iXtIiXwoiWz4iV3YeU64WS7ISSK4+gMY+gOpCgRpGgVJKgYpOgcZSggJWfj5afnZefqpieuJmexZmd05qd4Jmb7pia8JiaP56nQ56nSZ+nUqCnXqCnaqGneKOmhqSmk6Wmoaamrqelu6ilyamk1qmk46mj8qmh86mhTq2tUa2tVq2tXa6tZ6+tc6+tf7GtjLKtmLOtpbStsrWsv7aszLer2ber57iq9bip9ripW7u0Xbu0Yru0aLy0cby0e720hr60kr+0nsC0qsGztsOzwsSzz8Wy3MWy6sax+Maw+sawaMi7asi7bsm7c8m7e8q7g8u7jcy7mM27o866rs+6utC6xtG609K54NO57dS4/NS3/dS3dNbCdtbCedfCftfChNjCjNjCldnCntrCqdvBs9zBv93By97B1+DB4+HA8eK//+O+/+O+f+TJgeTJg+TJiOXJjeXJlObJnOfJpefJrujJuOrJw+vIz+zI2+3I5+7I9fDH//HG//HGivTRi/TRjfXRkfXRlvXRnPbRo/fRq/jRtPnRvvrRyPvR0/zR3/7R6//Q+f/Q///P///PAABiAABiAABiAABiBABiHABiOgBiTgBiYQBicgBhggBhkQBhoABhrwBgvQFgzQJfzgJfAABsAABsAABsAABsCABsIwBrPwBrUgBrZABqdAFqhAFpkgFpnwForQBmuwBlygFkywFkAAByAAByAAByAQByDgFxKQJxQwRxVQVwZwZwdwZvhgZvlAZuoQZtrwVsvARqyQJoywJoAAp5AAp5AAt5BQx5FQ14MRB4SBJ3WhN3axN2exN1iRJ1lhF0pBBzsQ9xvgxwywluzQltAB+BAR+BBSCBDyGAHyOAOyaATyd/YCh/cSh+gCd9jiZ8myR7qCF6tR54wRl2zhV0zxRzBT6LCD6LDj+LF0CKKUKKQ0OKVkSJZ0SIeEOIh0KHlD+FoT2ErjqDuzaBxzB/0yl81Ch7B1WTCFaTC1eTEliTNFqTS1ySXV2Sbl6Sfl+RjV+Rm1+QqF2PtluOwlaMzk+J2kWG20SFAGmaAGmaEGqaKGyaP26aUW+aYnGac3KZg3OZkXOZn3SYrXSXu3OXyHKW1XCU4muS5GqSEXyhFX2hJH6hOH+hSYChWIGhaIKheIShh4Wgloago4agsYefvoeezIae2oWc54Ob6IObKo2oMI6oOY6oRI+oUpCoYJGob5KofZSnjJWnmpanp5entZemwpim0Jil3Zek65aj7JaiPZ2vQZ2vR52vUJ6vW5+uaKCudqGug6KukaOunqSuq6WtuKatxaet06es4Ker7qeq8KeqS6u1Tqy1U6y1W6y1Za21cK61fK+1ibC1lrG0orK0r7O0vLS0ybWz1raz5Lay8rax87axWbm8W7q8X7q8Zrq7b7u7eby7hL27j767m7+7p8C7s8G7wMK6zcO62cS658W59cW498W4ZcfCZ8fCa8fCccjCeMnCgcnCi8rClcvCoMzCrM3Bt87BxNDB0NHB3dHA6tLA+dO/+tO/ctXJc9XJdtXJe9bJgtbJidfJktjJnNnIptrIsdvIvNzIyN3I1N7I4d/H7uDH/OHG/uHGfePPfuPPgePPheTPi+TPkeXPmeXPoubPrOfPtujPwenPzOvP2OzO5O3O8u7N//DN//DMiPPYifTYi/TYj/TYk/XYmvXYofbYqffYsvjYu/nYxvrY0fvX3P3X6P7X9v/X///W///WAABsAABsAABsAABsAgBsGwBsNgBsSgBrXQBrbgBrfgBrjQBrmwBrqgBruQBqyABpyQBpAAB1AAB1AAB1AAB1BgB1IgB1PAB1TwB0YAB0cQBzgABzjgBymwBxqQBwtgBvxQBuxwBuAAB7AAB7AAB7AQB7DAB7KAF6QAN6UgN6YwR5cwR5ggV4kAR3nQR2qwN1uAJ0xQFyxgFyAAiCAAmCAAmCBAqCFAyBMQ+BRhCBVxGAaBF/dxF/hhB+kw99oA58rQx7ugp5xwZ3yQZ3AB6KAB6KAx+JDSCJHyKJOSSJTSaIXiaIbiaHfSaGiySFmCKEpR+DsRuBvRd/yhJ9yxJ9BT2TBz2TDT6TFj+TKUGTQkKSVEOSZUORdUKQhEGPkT6OnjuNqziMtzSKxC+IzyeF0CaEB1ScCFWcDFWbFFebM1mbSVubWlyba12ae12ail6ZmF2ZpVyYslqWv1WVy06S1kOP2EOOAGijAGijEGmjKGuiPW2iT26iYG+icHCigHGhj3KhnXKhqnKgt3KfxXCe0m6d32mb4GmaE3upF3ypJXypN32pR3+pVoCpZoGpdoKphYOpk4SooYWoroWnu4WnyYWm1oSl5IGj5YGjKoywL4ywN42wQ46wUI+wXpCwbJGwe5KviZOvl5SvpJWvspauv5auzJat2pas6JSr6ZSrO5u2P5y2RZy2Tp22WZ62Zp+2c6C2gaG2jqK2m6O2qKS1taW1wqW10Ka03aaz66Wy7aWySaq9TKq9Uau9Wau9Y6y9bq29eq68h6+8k7C8oLG8rLK8ubO8xrS707S74LW677W58LS5V7jDWbjDXbnDZLnDbLrDdrvDgbvDjbzDmL3DpL/CsMDCvcHCysLC1sLB5MPB8sPA9MPAY8bJZcbJacbJb8fJdsfJf8jJiMnJk8rJnsvJqczJtc3Jwc7Jzc/I2tDI59HH9tHG99HGb9TPcdTPdNTPedTPf9XPh9bPkNfPmdfPo9jPrtnPudrPxdzP0d3P3t7O69/O+uDN++DNe+LWfOLWf+LWg+LWiOPWj+PWl+TWoOXWqebWs+fWvujWyunW1evW4uzV7+3V/u7U/+7UhvPeh/PfifPfjfPfkfTfl/Tfn/Xfp/bfsPffufjfw/nfzvve2vze5v3e8//e///d///dAAB2AAB2AAB2AAB2AQB2GgB1NAB1RwB1WQB1agB1egB1iQB0lwB0pgB0tAB0wwBzxQBzAAB+AAB+AAB+AAB+BQB+IQB+OQB+TAB9XQB9bQB8fAB8igB7lwB6pQB5sgB4wQB3wgB3AACEAACEAACEAACECwCDKAGDPgKDTwKCYQOCcAOCfwOBjAOAmgN/pwJ+tAF9wQB7wgB7AAeLAAeLAAiKAgmKEwuKMA2KRA+JVQ+JZQ+IdA+Hgg6Hjw2GnAuFqQmDtgeCwwWAxQSAAB2SAB2SAx6SDB+SHyGSOCORSySRWyWQayWQeiSPiCOOlSGNoR6MrhmKuhWIxhCGyBCGBDycBzycDD2cFj6cKkCbQEKbUkKaY0Kac0GZgUCYjj2XmzqWqDeUtDKTwC2QzCWOzSSNBlOkCFOkDFSkFlajM1ijR1qjWFujaVyjeVyih1yilVyholugr1ifvFSdyE2b00KX1EGXAGerAmirEmmrKWqqPGuqTm2qXm6qbm+qfXCqjHGpmnGpp3GotHCowm+nz2yl3Gij3WejFXqxGXuxJXuxNXyxRX2xVH6xZICxc4GxgoKxkIOwnoOwq4SwuISvxoOu04Kt4H+s4n+sKYu4LYu4Noy4QYy4To24XI64apC4eZG3h5K3lJO3opS3r5S2vJW2yZW115S15JOz5pOzOZq+PZq+Q5u+TJu+V5y+ZJ2+cZ6+fp++jKC+mKG+paK9sqO9v6S9zaS82qS76KO66aO6SKnESqnET6nEV6rEYarEbKvEeKzEhK3Eka7Ena/EqbDEtrHDw7LD0LPD3bPC67PB7bPBVbfKV7fKW7fKYrjKarjKdLnKf7rKirvKlrzKor3Krr7Kur/Kx8DJ1MHJ4cHJ78HI8MHIYcTQY8XQZ8XQbcXQdMbQfcfQhsjQkMjQm8nQpsvQsszQvs3Qy87P187P5M/P89DO9NDObdLXb9PXctPXd9PXfdTXhdTXjdXXl9bXodfXrNjWt9nWwtrWz9vW29zW6N3V997V+N7VeeDdeuHdfeHdgeHdhuLdjeLdlePdneTdp+Xdsebdu+fdx+jd0+nd3+rd7Ozc++3c/e3chPLlhfLlh/Lli/PlkPPllvTlnPTlpfXlrvbmtvfmwfjmzPrm1/vl4/3l8P7l///k///kAAB/AAB/AAB/AAB+AQB+GQB+MQB+RAB+VgB9ZwB9dgB9hQB9kwB9ogB9sAB9vwB8wQB8AACHAACHAACHAACHBACHIQCGNwCGSQCGWgCGagCFeQCFhwCElACDoQCCrgCBvQCAvgCAAACMAACMAACMAACMCgCMJwCMPAGLTQGLXgKLbQKKfAKKiQKJlgGIowGHsACGvQCEvgCEAAaTAAaTAAeTAgiTFAqSLwySQg2SUw6RYw6RcQ2QfwyPjAuOmQmNpgeMswWLvwOJwQOJABubAByaAhyaDB6aICCaOCKaSSOZWSOZaSOYeCKXhSGXkh+WnhyUqhiTthORww2PxA2OBDukBjykDDykFz6kK0CkP0GjUEGjYUGicEChfz+hjDygmDmepTWdsTGbvSuZyCOWySKWB1KsCFKsDFSsGFWsM1erRlirVlmrZ1qrdluqhVuqk1qpoFmprFeouVOmxUyj0ECg0UCgAGazBGezFmizKmmzPGqyTGuyXG2ybG6ye26yim+xl2+xpG+xsW+wv22vzGuu2Was2masF3m5Gnq5Jnq5NXu5RHy5U325Yn65cX+5gIC5joG5m4K4qIK4tYK3w4K30IC23X603320KIrALYrANYrAQIvATIzAWo3AaI7Ad4+/hJC/kpG/n5K/rJO/uZO+xpO+1JK94ZG845G8OJnGPJnGQprGSprGVpvGYpzGb53GfJ7GiZ/GlqDFo6HFsKLFvaLFyqPE16PE5aLD5qLDRqfMSajMTqjMVajMX6nMaqrMdqvMgqzMjq3Mmq7Mp6/Ms7DLwLHLzbHL2rHK6LHJ6rHJU7XSVbbSWrbSYLbSabfScrjSfbnRiLrRk7vRn7zRq73Rt77RxL7R0b/R3sDQ7MDP7cDPYMPYYsPYZcTYa8TYcsXYe8XYhMbYjsfYmcjYpMnYr8rYu8vXyMzX1M3X4c7X8M7W8c7WbNHebdHecNHeddLee9Leg9Pei9TeldXen9beqdfetNjewNnezNre2Nve5dzd9Nzd9dzdd9/leN/le+Dlf+DlhODli+Hlk+Llm+PlpeTlruTlueblxOfl0Ojl3Onk6erk+evj++vjgvHsg/HshvHsifLsjvLtlPPtm/Tto/Ttq/XttPftv/jtyfnt1frt4Pzt7v3t/v/s///sAACHAACHAACHAACHAQCHGQCHLwCHQQCGUwCGZACGcwCGggCGkACGngCGrACGuwCFvQCFAACPAACPAACPAACPBACPIACPNQCPRwCOWACOZwCOdgCNgwCNkACMnQCLqgCKuQCJuwCJAACVAACVAACVAACVCwCUJwCUOgGUSwGTWwGTagGTeAGShgGRkwGRoACQrQCOuQCNuwCNAAWbAAWbAAabAgebFgmbLwuaQAyaUAyaYAyZbwuYfAqYiQmXlgeWogWVrwSTvAKSvQGRABqjABujAhyjDB2jIR+iNyGiSCGiVyKhZyKhdSGggiCfjx2emxqdpxabsxCZvwuXwAuXBDqtBjusDDysGT2sLD+sP0CsT0CrX0Crbj+qfD6piTuoljinojSlri+kuimixSGfxiCfB1G0CVK0DlO0HFS0M1a0RVezVVizZVmzdFmzglmykFmynVixqlawtlKvwkqszT+pzj6oAGW7CGa7Gme7K2i7O2m7S2q7Wmu6amy6eW26h266lW65oW65rm24u2y4yWq21WW11mS0GnjBHXjBJ3nBNXrBQ3vBUXzBYH3Bb37BfX/Bi4DBmYDApYHAsoHAwIC/zX++2ny923y9KYjILonINYnIP4rIS4vIWYzIZo3IdY7Igo/Hj5DHnJDHqZHHtpHHw5HG0ZHF3o/E34/EOJjOO5jOQZjOSpnOVJrOYJrObZvOep3Oh57OlJ7NoJ/NraDNuqHNx6HN1KHM4qDL46DLRabTSKbTTafTVKfTXqjTaKnTdKrTgKrTjKvTmKzTpK3Tsa7Tva/TyrDS17DS5a/R5q/RUrTZVLTZWbXZX7XZZ7bZcbbZe7fZhrjZkbnZnbrZqLvZtbzZwb3Zzr7Z277Y6b7Y6r7YX8LfYcLfZMLfacPfccPfecTfgsXgjMbgl8fgosjgrcngucrfxcvf0cvf3szf7Mze7szea9DmbNDmb9DmdNHmetHmgdLmitLmk9PmnNTmp9Xmstbmvdfmydjm1dnm4trl8dvl8tvldt7sd97set7sft/sg9/sieDskeDsmeHso+LsrOPtt+TtwuXtzebt2eft5uns9urs+OrsgfD0gvD0hPH0iPH0jPH0kvL0mfP1ofT1qfX1svb1vPf1x/j10vn13fv26v32/f/1///1AACMAACMAACMAACMAQCMGQCLLgCLQACLUQCLYgCLcQCLgACLjgCLnACLqgCKuQCKuwCKAACUAACUAACUAACUBQCUIACTNACTRgCTVgCTZgCSdACSggCRjwCRmwCQqACPtwCOuACOAACZAACZAACZAACZDQCZJwCZOQCYSgGYWgGYaQGXdwGXhACWkQCVngCVqwCTtwCSuACRAASgAAWgAAWgAgagFwifLgqfQAufTwueXwuebQqdewmciAiblAaboAWarQOYugGXuwGWABqnABqnAhunDR2nIh+nNyCnRyGmViGmZiGldCCkgR+kjRyjmRmipRWgsQ+evQmcvgmcBDqxBjqxDTuxGz2xLD+wPkCwT0CwXkCvbT+vez2uiDutlDesoDOqrC6puCinwyCkxB+kB1G5ClG5D1K4HlS4M1W4RFa4VFe4ZFi4c1m3gVm3j1i2nFe2qFW1tFGzwEqxyz6uzD2tAmW/Cma/G2a/LGe/O2i/S2m/Wmq/aWu/eGy/hm2+k22+oG2+rWy9umu8x2m71GS51WO5G3jGH3jGKHjGNXnGQ3rGUXvGYHzGbn3GfH7Gin/Fl3/FpIDFsYDEvn/Ey37D2HvC2nvCKYjLLYjLNYnLP4nLS4rLWIvLZozLdI3LgY7Ljo/Lm5DLqJDLtZDKwpDKz5DJ3I7I3Y7IOJfSO5fSQZfSSZjSVJnSYJrSbJvSeZzShp3Skp7Rn57Rq5/RuKDRxaDR0qDQ4J/P4Z/PRaXYSKbYTabYVKbYXafYaKjYc6nYf6rYi6vYl6zYo63Yr63XvK7Xya/X1q/X467W5a7WUrTeVLTeWLTeX7TeZ7XecLbeerfehbjekLnenLnep7res7vewLzezL3d2b3d573d6L3dXsHkYMHkZMLkacLkcMPkeMPkgcTki8XklsbkocfkrMjkt8nkw8rk0Mrk3cvk68vj7Mvjas/qbM/qb8/qc9DqedDqgNHqidLqktPqnNPrptTrsNXrvNbrx9fr09jr4Nnr79rq8Nrqdd3xd93xed7xfd7xgt7xid/xkODxmeDxouHxq+LytePywOTyzOXy1+by5Ofy9Ojy9ujygPD6gvD6hPD6h/D6i/H6kfH6mPL6oPP6qPT7sfX7uvb7xff80Pn82/r96Pz9/P7+//7+AACMAACMAACMAACMAQCMGQCLLgCLQACLUQCLYgCLcQCLgACLjgCLnACLqgCKuQCKuwCKAACUAACUAACUAACUBQCUIACTNACTRgCTVgCTZgCSdACSggCRjwCRmwCQqACPtwCOuACOAACZAACZAACZAACZDQCZJwCZOQCYSgGYWgGYaQGXdwGXhACWkQCVngCVqwCTtwCSuACRAASgAAWgAAWgAgagFwifLgqfQAufTwueXwuebQqdewmciAiblAaboAWarQOYugGXuwGWABqnABqnAhunDR2nIh+nNyCnRyGmViGmZiGldCCkgR+kjRyjmRmipRWgsQ+evQmcvgmcBDqxBjqxDTuxGz2xLD+wPkCwT0CwXkCvbT+vez2uiDutlDesoDOqrC6puCinwyCkxB+kB1G5ClG5D1K4HlS4M1W4RFa4VFe4ZFi4c1m3gVm3j1i2nFe2qFW1tFGzwEqxyz6uzD2tAmW/Cma/G2a/LGe/O2i/S2m/Wmq/aWu/eGy/hm2+k22+oG2+rWy9umu8x2m71GS51WO5G3jGH3jGKHjGNXnGQ3rGUXvGYHzGbn3GfH7Gin/Fl3/FpIDFsYDEvn/Ey37D2HvC2nvCKYjLLYjLNYnLP4nLS4rLWIvLZozLdI3LgY7Ljo/Lm5DLqJDLtZDKwpDKz5DJ3I7I3Y7IOJfSO5fSQZfSSZjSVJnSYJrSbJvSeZzShp3Skp7Rn57Rq5/RuKDRxaDR0qDQ4J/P4Z/PRaXYSKbYTabYVKbYXafYaKjYc6nYf6rYi6vYl6zYo63Yr63XvK7Xya/X1q/X467W5a7WUrTeVLTeWLTeX7TeZ7XecLbeerfehbjekLnenLnep7res7vewLzezL3d2b3d573d6L3dXsHkYMHkZMLkacLkcMPkeMPkgcTki8XklsbkocfkrMjkt8nkw8rk0Mrk3cvk68vj7Mvjas/qbM/qb8/qc9DqedDqgNHqidLqktPqnNPrptTrsNXrvNbrx9fr09jr4Nnr79rq8Nrqdd3xd93xed7xfd7xgt7xid/xkODxmeDxouHxq+LytePywOTyzOXy1+by5Ofy9Ojy9ujygPD6gvD6hPD6h/D6i/H6kfH6mPL6oPP6qPT7sfX7uvb7xff80Pn82/r96Pz9/P7+//7+\",slide100:\"AAAAAwAACQAAEgAAHwAAMQAARAAAWAAAbwAAhwAAnAAAsAAAxAAA2AAA7AAA/wAA/wAAAgQABgQADwQAGgMAKQMAOwMATQMAYQIAdwIAjQEAogEAtQAAyQAA3AAA7wAA/wAA/wAABg4DCg4CEw0CIA0BMAwAQQsAUgoAZgkAewgAkQYApAUAtwMAygIA3QAA8QAA/wAA/wAADR4LER0KGB0IJRwGNRsERRoCVxgBahYAfxQAkxIApw4AuQsAzAgA3gUA8gIA/wAA/wAAFzAUGzAUIi8SLC4QOi4NSi0KWysHbyoEgicCliQBqSAAuxsAzhYA4BAA8woA/wQA/wMAG0EcH0EbJ0EaMUAYQD8WUD8UYT0RdDwNhzoKmjgHqzQGvTEF0CwF4iYE9R4D/xMD/xECHVUkIlUjLFUiN1QhRlQfVlMdZ1IaeVEXi08UnU0SrkoQwEYQ0kIQ5D0P9jYP/y0P/ysPK2kpMGgpN2goQmgnUGgmXmckbmYifmUfkGQdoGIbsmAZw14Z1Foa5lYb+FAb/0cc/0UdPHwuQHwtRnwsUHwrW3wqaHsodnsnhnollnkjpnghtnYgx3Qf2HEg6W0i+mkl/2Io/2EpTY80UI80VY8zXY8yZ48wco4tf44qjo4onI0mrIwlvIojzIki3Ycj7YQl/oAp/3ou/3kvXaI7X6I7Y6E6aaE5cqE3fKE1iKEylqAvpKArs58nwp4m0p0l4psk8pkl/5Ur/5Ez/480bLRCbbRCcbRBdrRAfbM/h7M9krM6nrM3rLI0urIwybEr2LAn6K8m+K0m/6sp/6Y0/6U2e8VJfMVJf8VIhMVHi8VGk8VEncVCqMU/tcU8wsQ5z8Q13sMw7sIq/cEn/78o/7sv/7syjNZPjdZPkNZOlNZOmtZModZLqtZJtNZHv9VEytVB19U+5dQ589Q1/9Mv/9Ip/9Ar/88sn+dVoOdVoudUpudTqudSsedRuOdPwedOyudL1edJ4OdG7OZC+eY+/+U6/+Q1/+Mu/+MstPhatfhat/hZufhZvfhYwvhXyfhV0PhU2PhS4fhQ6vhN9fhK//hH//dD//c///Y5//Y41P9f1P9f1v9e2P9e2/9d3v9d4/9c6f9a7/9Z9f9X/f9W//9T//9R//9O//9L//9G//9FAAABAQABBgABDgABGwAALAAAPwAAUwAAawAAggAAlwAArAAAwAAA1AAA6QAA/gAA/wAAAQQEBAMEDAMDFwMCJgMBOAIASQIAXQIAcwEAiQEAnQAAsQAAxQAA2AAA7AAA/wAA/wAABA4ICA0IEA0HHQwFLQsDPgoCTwoBYwgAeAcAjQUAoQQAtAIAxwIA2gAA7gAA/wAA/wAACx4RDx0QFh0OIxwMMxsJQxkGVRgEaBYCfRMBkREBpA0AtwoAyQcA3AQA8AIA/wAA/wAAFjAZGi8YIS8XKy4VOC4SSCwQWisNbSkJgScHlCMFpx8EuRsDzBUC3g8B8gkB/wQB/wMBGkEgHkEfJUEeMEAdPj8aTj8YYD0VcjwShToQmDcOqTQNuzANzisL4CYK8x4I/xMG/xEGGlUnIFUnKlUmNlQkRVQjVVMgZlIeeFEbik8Zm00XrUoXvkYX0EIW4jwV9TYV/y0U/ysUKmksLmgsNWgrQWgqT2goXWcnbWclfmUij2Qhn2IfsGAfwV0f01og5FUg908h/0ch/0UhO3wwP3wvRnwvT3wtWnwsZ3sqdXsphXonlXklpXgktXYjxnQj13El520n+Wgp/2Is/2EsTY81UI80VY8zXY8yZ48wco8uf44sjY4rnI0pq4wnu4omy4km3IYn7IMq/H8u/3oz/3k0XaI8X6I7Y6I6aaI5cqE4fKE1iKEylqEvpKAss58qwp4p0Z0n4Zsn8Zgq/5Ux/5A3/485bLRDbbRCcbRBdrRAfbQ/h7M9krM6nrM3rLM0urIwybEr2LAq568p960q/6ov/6Y5/6U7e8VJfcVJf8VIhMVHi8VGk8VEncVCqMU/tcU8wsQ50MQ13sMw7sIr/cEq/78s/7s1/7o4jNZPjdZPkNZPlNZOmtZModZLqtZJtNZHv9VEy9VB19U+5dU689Q1/9Mw/9Iq/9Av/88wn+dVoOdVoudUpudTq+dSsedRuOdQwedOyudM1edJ4OdG7OZC+eY+/+U6/+U1/+Mu/+MstPhatfhat/hZuvhZvvhYw/hXyfhV0PhU2PhS4fhQ6vhN9fhL//hH//dD//c///Y6//Y41P9f1f9f1v9f2P9e2/9d3/9d4/9c6f9a7/9Z9v9X/f9W//9T//9R//9O//9L//9H//9GAAAGAAAGBAAGCwAGFwAGKQAFPAAEUAADaAACfwABlQABqgABvgAB0gAB5wAC/QAC/wACAAQKAgMKCQMJFAMJIwIHNAIGRgIFWgEDcAEChgABmwABrwAAwwAA1gAA6wAA/wAB/wABAw4PBg0ODQwNGgwMKgsKOwoITAkHYAcFdQYEigUCngMBsgIBxQEB2AAB7QAA/wAA/wABCR4XDh0WFBwVIBsTMRoQQRkOUhgMZRUKehMHjhAFog0EtAkDxwcC2gMC7gEC/wAC/wABFDAfGC8eHy8dKS4bNi0ZRiwXVysVaikTfiYRkSMOpB8MtxoJyhUH3Q8G8AkF/wQE/wMEGUElHUEkI0EjLUAiPD8gTD4eXj0ccDsagzkZlTcXpzMWuTAUzCsT3yUR8h0P/xMM/xALF1UrHVUrJ1QqNFQpQ1MnU1MlZFIjdlAhh04gmUwgqkkfvEYez0Ed4Twc8zUc/ywa/yoZKGkwLWgvM2gvP2gtTWgsXGcqbGYpfGUnjWQmnWImrmAmv10m0Vkm41Um9U8n/0cn/0UnOXw0PXwzRHwzTXwyWXwwZnsvdHsthHorlHkqo3cps3YqxHMr1XAs5m0t92gw/2Iy/2EyTY83UI82VY82XI81ZY80cY8zfo4xjI4vmo0uqowsuooryogt2YYw6oMy+382/3o6/3g7XaI8X6I7Y6I7aaI5cqI4fKI2iKE0laEzo6AxsZ8wwZ4u0J0u35sw75g1/pU6/5BA/49BbLRDbbRCcbRCdrRBfbQ/h7Q9krQ7n7M4rLM0urIyyLEw17Aw5q8v9a0y/6o6/6VC/6REe8ZJfcZJf8ZIhMZHi8ZGk8ZEnsVCqcVAtcU8wsU50MQ13sQw7cIv/MEw/740/7pA/7pCjNZPjdZPkNZPlNZOmtZNodZLqtZJtNZHv9ZEy9ZB19U+5dU689Q1/9Qw/9Iw/881/884n+dVoOdVoudUpudUq+dTsedRuOdQwedOyudM1edJ4OdG7OdD+eY//+Y6/+U1/+Mv/+MwtPhatfhat/hZuvhZvvhYw/hXyfhV0PhU2PhS4fhQ6/hN9fhL//hH//hE//c///Y6//Y41P9f1f9f1v9f2P9e2/9e3/9d5P9c6f9a7/9Z9v9Y/v9W//9T//9R//9O//9L//9H//9GAAAQAAARAQARBwARFAAQJgAPOQANTQALZQAJfQAHkwAGpwAGvAAF0QAF5gAF/AAG/wAGAAMVAQMVBQIVEAIUHwITMQEQQwEOVwEMbgEJhAAHmQAFrQAEwQAD1QAD6QAD/gAE/wAEAg0ZBAwZCgsYFgsXJwoVOAkTSQgRXQcOcwYMiAQJnAMIrwIGwwEG1wAF6wAD/wAD/wADBh0gCh0fERweHRsdLRkbPRgZTxcXYhUUdxIRjA8OnwwMsggLxQYJ2QMI7QEG/wAF/wAFEi8nFS8nHC4mJi4kMiwjQisiVCogZygdeyUbjyIYoh4VtBkTyBQR2w4O7wgM/wMK/wIJF0EtGkEsIEArKkArOD8qST4oWjwnbTslgDkjkjYhpDMgty8eyioc3CUa8B0X/xIS/xASFFUyGFUyI1QxMFQwQFMvUFIuYVEtclAshE4rlkwpqEkoukUnzEEm3jsl8jUj/ywh/yogJGg3KWg3MGg2PGg1Smc0WWczaGYyeWUyiWMxmmExq18wvVwwzlkw4FUw808v/0cu/0UtNnw6Onw6QXw5S3w5Vnw3Y3s2cXo1gXk1kHg1oHc2sHU2wXM20nA342w49Wg6/2E6/2E6So89TY89Uo88Wo87Y486b445e444io03mIw3p4s3t4k4xog614U854I++H5B/3lD/3hEXKI/XqI/YqI+aKI+cKI9eqI8hqE7k6E5oaA4sJ83vp44zZw63Jo97JdB/JRF/49K/45LbLVDbbVDcbRCdrRBfbQ/h7Q9krQ8nrM7q7M6uLI5x7E41bA45K468qxA/6lG/6VN/6RPe8ZKfcZJgMZJhMZIi8ZGlMZEnsZCqcZAtcU9wsU50MQ33cM368I3+sA5/75C/7pM/7lOjNdQjddPkNdPlNdOmtdNoddLqtZJtNZHv9ZEy9ZB2NY+5dU689U1/9Q2/9I5/89D/85Hn+hVoOhVouhUpuhUq+hTsehRuehQwehOy+hM1edJ4OdG7OdD+uc//+Y6/+U1/+M3/+M5tflatvlat/lZuvlZvvlYw/lXyflW0PlU2PlS4flQ6/lO9vlL//hH//hE//g///c6//Y41f9f1v9f1/9f2f9e3P9e4P9d5P9c6v9b8P9Z9v9Y/v9W//9U//9R//9O//9L//9H//9GAAAbAAAbAAAbBAAbEAAaIgAZNgAXSwAVYwASegAQkQAOpgANuwAM0AAL5QAL+wAL/wALAAMgAAIgAwIfDAIfGwEdLgEbQAEZVAEWawATgQAQlgAOqwAMvwAK0wAI6AAI/QAI/wAJAAwkAgskBgsjEgoiIwkgNAgeRgccWgYZcAUWhQMUmQISrQEQwQEO1QAL6gAJ/wAH/wAIBBwsBxwrDhsqGRooKRkmOhckSxYiXxQfdBEciQ4ZnAsXsAgUwwUS1wIQ7AEN/wAL/wALDi4zEi4yGS0xIy0wLiwuPyssUCkqZCcoeCUljCIinx0gshkdxRMa2Q0X7QgT/wMQ/wIQFEA5F0A4HD84Jj83Mz41RT00VjwyaTowfDgujzUsojIqtC4oxyol2iQi7xwe/xIZ/w8YEVQ/E1Q/G1M+KlM+OlI9S1E7XFA5bk84gU02k0s1pEgzt0QyyUAw3Dsu8DQr/ywo/yknHGhEImhEK2dDNmdDRGdCVGZBZGVAdWQ/hmI+l2E9qF48uVw8zFg73lQ68U44/0Y2/0Q1MHxFNHxFPHxFRntFUntEX3pEbXpEfHlDjHdDnHZDrXRDvXJDz29D4GxE82dD/2FD/2BDRo9GSY9FTo9FVo9FYI5Ea45EeI1Eho1FlItFo4pGs4lHw4dH04RJ5IFK9X5M/3lN/3dNWaJHW6JGX6JGZaJGbaJFeKFEg6FEkKBEnp9ErJ5Gup1IyZtJ2ZlM6JZO+ZNS/49V/45VarVHbLVHb7VHdLRGfLRGhbRFkLREnLNDqLNDtrJDw7FE0a9H361L7qtP/ahU/6Ra/6Nbe8dKfcdJf8dJhMdIi8ZHlMZFncZDqMZDtMVCwMVBzcRB28NC6MFF9b9L/7xS/7lb/7hdjNdQjddQkNdPlNdOmtdNotdLqtdJtNdHv9dFy9ZC2NY+5dU+8tQ//tNB/9FJ/81W/81Zn+hVoOhVo+hVpuhUq+hTsehSuehQwehOy+hM1ehJ4ehG7edD+uc//+Y6/+U8/+NF/+JJtflatvlauPlauvlZvvlYw/lXyflW0flU2flS4vlQ6/lO9vlL//lI//hE//g///c6//c41v9f1/9f2P9f2v9e3f9e4P9d5f9c6v9b8f9Z9/9Y//9W//9U//9R//9P//9L//9H//9GAAAlAAAlAAAlAgAlDAAkHwAiMwAgSAAeYQAbeAAZjwAWpAAUuQATzgAS5AAR+gAR/wARAAIqAAIqAQIqCAEpFwEoKwElPQEjUQAgaAAdfwAalAAXqAAUvQAR0gAP5wAP/AAP/wAPAAsvAQsvBAouDgktHwgrMQcpQgYnVwUkbQQhggMelwIbqwEZvwAW0wAT6QAQ/gAO/wAOAhw3BBs3Cho2FRk0JRgyNhYwSBUtXBMqcRAnhg0kmgohrQcewQUb1QIY6gEV/wAS/wARCS0/DS0+FC09Hyw8KSs6Oio4TCg2YCYzdSQwiSEtnB0qsBgmwxMj1w0e7Acb/wMX/wIXET9FFD9FGT9EIj5DLj1CQDxAUjs+ZTk8eTc6jDU3nzE1sS4yxSkv2CQr7Rwn/xEh/w8gD1NMEFNMFlNMI1JLNFJKRlFIWFBGak5FfUxDj0pBoUc/tEQ9x0A62jo37jQ0/ysv/ykuE2dSGWdRI2dRMGZQPmZPTmVOX2RNcGNMgmJLk2BKpF5JtltHyVhG21RE705B/0Y+/0Q9JXtUK3tUM3tUPnpTS3pTWXlSaHlSeHhRiHZRmHVRqXNQunFQzG5P3mtO8GdN/2FM/2BLPo5VQY5VR45VT45UWY1UZY1UcoxUgItUj4pUn4lVrohVv4ZWz4RW4YFW831W/3hW/3dWU6FTVaFTWaFTX6FTaKFUcqBUfaBUip9VmJ5Wpp1WtZxYxZpZ1Jhb5JZc9ZJe/45f/41fZrRRaLRRa7RRcLRReLRRgbNRi7NRl7JSo7FUsLBWvq9XzK5a2qxd6apg+adj/6Nm/6JnecdQesdQfcdPgsZPicZPkcZOmsZOpcVOsMVOvMRPyMNS1cJW4sBa8L5f/rtk/7hq/7drjNhQjdhQkNhPlNdOmtdNotdMqtdKtNdKvtdKydZJ1dZK4dVL7dRP+dJX/89f/8xp/8trn+lWoOlVo+lVpulUq+lTselSuelQwelOy+hM1uhJ4ehH7ehD+edE/+ZG/+RN/+Fd/+BgtflatvlauPlau/lZv/pYxPpXyvpW0fpU2fpT4vpQ6/pO9vlL//lI//lE//hA//dC//ZE1/9f1/9f2f9f2/9f3f9e4f9d5v9c6/9b8f9a+P9Y//9W//9U//9S//9P//9M//9H//9GAAAwAAAwAAAwAAAvCAAuHAAsMAAqRQAnXgAkdgAhjQAeogAcuAAazQAZ4wAY+QAX/gAXAAI2AAI2AAE2BAE1EwEzJwEwOQAuTgAqZQAnfAAjkgAgpgAduwAZ0AAX5gAW+wAV/wAVAAo7AAo7Agk6CQg5Gwc3LQY0PwUyUwUuagMrgAMolAEkqQEhvQAe0gAb5wAX/AAV/wAVABtDAhpDBhlCERlAIBc+MRY7RBQ4WBI1bg8xgw0tlwkqqwYmvwQj0wIf6AAc/QAZ/wAYBixLCSxLECxKGitIJSpGNilESSdCXSY/ciM7hiA4mRw0rRgwwRIr1Qwn6gYi/wIf/wEeDT5TED5SFT5RHT1QKjxPOztNTjpLYThIdTdFiTRCnDE/ry08wyk41iM06xsv/xAo/w4nDFJaDlJaElJZG1FYLlFXQVBVU09TZk1ReUxPjElNnkdKsUNHxD9E2DpA7DM8/ys3/yg2BmZgDGZgGWZfKGVfN2VeSGRdWmNba2JafWFYj19XoV1Vs1tSxldQ2VNO7U5K/0ZH/0RGG3pkIHpkKXlkNnljRHljUnhiYnhhcndhg3VglHRfpXJetnBcyG5b22tZ7mZX/2BV/19UM41mN41mPY1mRoxmUYxmXYxma4tmeopliollmohlqodkuoVkzINj3oBi8Hxh/3hg/3ZgSKBnS6BnT6BnVp9nX59nap9ndp5ohJ5okp1ooZxpsJppwJlp0Jdq4ZVq8pJq/41q/4xqXLNlXrNmYrNmaLJmb7JmebJnhLFnj7FonLBpqa9qt65rxqxs1atu5alv9qZw/6Jy/6FycsZfdMZfd8ZffMVfgsVgisVhlMRincRkqcNltcJnwcFpzsBr3L5u671x+rp0/7d3/7Z4iddYi9dYjddYkddXl9dXntdXptdXr9ZYudZaw9VcztRg2tNk5tJo89Bt/85z/8t5/8p7n+lWoOlWo+lVpulUq+lTselSuelRwelPyulP1OhP3+hQ6udR9eZV/+Ve/+Jn/99z/992tfpbtvpauPpau/pZv/pZxPpYyvpW0fpV2fpT4vpR7PpO9vpL//pI//lF//hJ//ZX//Vd2P9g2P9g2v9f3P9f3v9e4v9d5/9c7P9b8v9a+f9Y//9W//9U//9S//9P//9M//9I//9HAAA7AAA6AAA6AAA5BQA4GAA1LQAyQgAvXAAsdAApigAmoAAktgAhywAg4QAe+AAd/QAdAAFBAAFBAAFAAgE/DwE9IwE6NgA3SwA0YwAwegAsjwAopAAluQAhzgAe5AAd+gAc/wAcAAlGAAhGAQhFBQdEFgZBKQY+OwU7UAQ4ZwM0fQIxkgEtpgApuwAm0AAi5QAf+wAc/wAcABpPABlOAxhNDRhLGxZJLRVGQBNDVRE/aw87gAw3lAgzqQYvvQMr0QEo5wAk/AAg/wAfAytXBStXCytWFipUIilSMShQRCdNWSVJbiJGgx9Clhs+qhc5vxE00wsv6AYr/QIm/wElCj1fDD1fEj1eGTxdJjtbNjpZSTlWXThTcjZQhTNMmTBJrC1FwChB1CI96Rs3/hAx/w0vCVFoClFnDlFnF1BmJlBkO09iTk5gYk1ddUtbiElYm0ZVrkNSwT9O1TlK6jNF/ipA/yg/AGVvAWVvCmRuHmRtMGRsQmNrVGJpZmFneWBli15jnVxgsFpew1db1lNY6k1U/kVQ/0NPFHh0F3h0Hnh0K3hzO3hyS3dxXHZwbXZvfnVtj3NsoXJqs3BoxW1m2Gpk62Zh/2Be/19eJ4x5Kot4MYt4PIt4SIt3Vop3ZIp2dIl1hIh0lYdzpYZytoRwyIJv2n9u7Xxs/3dq/3ZqPJ57P557RJ57TJ57Vp57YZ17b516fZx6jJt5m5p5q5l4u5h4zJZ33ZR275F1/410/4x0UbF7U7F7V7F7XbF8ZbB8cLB8e7B8iK98la58o618sqx8wat90ap94ah98qV9/6J9/6F9Z8R3acR3bMR3ccN4eMN4gcN5i8J6lcJ7ocF8rcF9usB+yL9/172A5ruB9rmC/7aE/7aEgdZsg9ZshdZtidZtj9VuldVvndVxptRysNR0u9N2xtJ509F74NB+7c6B/M2E/8qI/8mJneldnuldoOldpOldqOleruletOhevOhfxehhzudk1+Zo4eVt7ORy+ON4/+F//96G/92ItvpbtvpbuPpau/pav/pZxPtYyvtW0ftV2vtT4/tR7PpP9vpQ//lT//hY//dk//R1//N52f9g2f9g2v9f3P9f3/9e4/9e6P9d7f9b8/9a+v9Z//9X//9V//9S//9P//9M//9I//9HAABFAABEAABDAABCAwBBFAA+KQA7PwA4WQA0cQAxiAAungArtAApygAn4AAl9wAk/AAkAAFLAAFLAABKAQBJCwBHHwBEMgBASAA9XwA5dwA0jQAxogAttwApzAAm4gAk+QAj/QAjAAhRAAhQAAdQAwZOEgZLJAVJNwRFTANCZAI+egI6jwE2pAAyuQAuzgAq4wAm+gAj/gAjABlaABhZARhYCBdWFhVUKRRRPBNNURBJZw5FfQtBkQg8pgU4ugM0zwEw5QAs+wAo/wAnASpjAipiByphESlfHShdLCdbQCZYVSRUayJQfx9MkxtIpxZDvBA+0Ao55gUz+wEu/wEtBjxsCTxrDjxqFTtpITtnMDpkRDliWTdebjVbgjJXlS9TqSxPvSdL0SJG5xpA/A85/ww3B1B1CFB0C1BzE1ByH09xNU5uSU1sXUxpcUpmhEhjl0Vgq0Jcvj5Y0jlU5zJP/SlJ/ydHAGR8AGR8AWR7E2N6KGN5O2J3TmJ1YmFzdV9xh15umlxsrFlpwFZl01Ji6E1e/UVZ/0NXD3eDEneDF3eCIXeBM3eARHZ/VnZ9aHV8eXR6i3J4nXF2r29zwm1x1Wlu6WVr/V9n/15nGoqIH4qIJoqHMYqHP4qGTomFXomEb4iDf4eBkIaAoYV+s4N8xYF713956nt2/nd0/3VzLp2MMp2MOZ2MQp2LTZyLWpyKaJyKd5uJhpqIlpqHppiGt5eEyJWD2pOC7JCA/ox//4t+Ra+PR6+PTK+PU6+OXK+OZ6+Oc66Nga6Nj62MnayMrauLvKqLzamK3aeJ76WJ/6GI/6CIW8KOXcKOYcKOZsKObsGOd8GOgsGPjcCPmsCPp7+Ptb6Pw72P0ryP4ruP8riP/7WQ/7WQddSIdtSIedSIfdOIg9OJi9OKlNOKndKLp9KMs9GNv9GOzNCP2s+Q6M2S98yT/8mV/8iWk+d3lOd3lud4med4nud5pOZ6q+Z8suZ9uuV/xOWCzuSF2eOI5OKL8eGO/t+S/92W/9yYtftdtvtduPtduvtdvvtdw/tdyPpezvpe1vpf3fph5vll7vhr9/dy//Z6//SD//KO//GQ2v9g2v9g2/9g3f9f4P9f5P9e6P9d7v9c9P9a+v9Z//9X//9V//9S//9Q//9N//9S//9XAABNAABMAABLAABLAQBJEABHJQBEPABAVgA9bwA5hQA2nAAzsgAwyAAu3gAs9QAq+gAqAAFVAABVAABUAABTBgBRGgBOLgBKRABHXABCdAA+igA6nwA2tQAyygAu4AAs9wAq/AAqAAdcAAdbAAZaAQVZDAVWIARTMwNQSANMYAJIdwFDjAE/oQA7tgA3ywAz4QAv+AAq/QAqABhlABdlABdkBBZhERReJBNcNxFYTRBUZA1PegpLjgdGowRCuAI9zQE54gA0+QAw/gAuACpvACluBCltDShrGSdoJyZmOyVjUSNfZyFbfB5WkBpSpBVNuRBHzgpC5AQ8+gE2/gE1Azx4BTt3CTt2ETt0HDpyKzlwPzhtVTZpajRmfjJiki9epitauidVzyFQ5BlJ+w5B/wxABU+BBk+ACE+AD09+G058Lk16REx3WEt0bUlxgEdulEVqp0Fmuz1i0Dhd5TJY+ylR/yZPAGOJAGOIAGOIBWKHH2KFNGKDSWGBXWB/cF98g115llt2qVlzvVZv0FJr5Uxn+0Rh/0JgCHeQC3aPEHaPGnaOKXaNPHWLUHWKYnSIdXOGh3KDmXCBq25+v2x70ml45mV1+19w/15vCImWDomVGomVJomUNomTR4mSWIiRaYePeoaOi4WMnYSKr4OIwYGG1H6D53uA/HZ9/3V8H5ybJJybLJyaN5yaRJuZUpuYYZuXcZqWgZqVkZmTopiSs5aQxJWP1pKN6ZCL/YyI/4qIOa6fPK6fQa6eSa6eU66eX66dbK2de62ciaybmKuaqKuZuKqYyKiW2qaV66SU/qGT/6CSUMChUsChVsChXcCgZcCgb8CgesCfhr+fk7+fob6er72evrydzrud3rqc77ic/7Sb/7SbadKfa9KfbtKfc9KfedGfgdGfi9GfldGfoNCfrNCfuc+fxs6g1c2g48yg88uh/8ii/8eihuWWh+SWieSWjeSWkuSXmOSYn+SYp+OZsOOau+ObxuKc0eGd3uGf69+g+d6i/9yl/9umq/l4rPl4rvl5sPl5tPl7uPh8vfh+w/iAyveD0veG2vaJ4/WN7fWR+PSW//Kb//Ci/++j2v9g2/9g3P9g3v9g4f9f5f9e6f9d7/9c9f9b+/9Z//9X//9V//9U//9X//9g//93//9+AABXAABWAABVAABUAABTCwBQIQBNOABKUgBGawBCgwA/mQA8rwA5xgA23AA09AAx+QAxAABgAABfAABeAABdAwBbFQBYKgBUPwBRWABMcABHhwBDnAA/sgA7yAA23gA09QAy+gAxAAZnAAZmAAVlAAVjBgRhGwNdLgNaRAJWXAFRcwFNiQBJngBFswBAyQA83wA39gAy+wAyABdwABZwABZuAhVsDBNpHhJmMhBjSA5eYAxZdglViwZQoARLtQJGygBB4AA99wA3/AA2ACl6ACh5ASh4CCd2FCZzIiVxNiRtTCJpYyBleB1gjRlcoRVWtg9RywlK4QRE+AE+/QA9ATuDAjuCBjqBDDp/Fzl9Jzh7Ojd4UDV0ZjNwezFsji5noypjtyZezCBY4hhS+Q1J/gtHA0+MBE+MBk6LC06JF02IJk2FPkyCVEp/aUl8fUd4kER0pEFwuD1rzThm4jFg+ShZ/iZXAGKVAGKUAGKUAWKSFGGRLGGPQ2CMWF+KbF6Hf1yEklqBplh9ulV5zlF140xv+URp/UJoAXacA3acCXWbE3WaIHWZNXWXSXSVXXOTcHKRgnGOlXCMqG6Ju2uFz2iC42R++V55/l14AImjAoiiCYiiGoihLIigPoifUYedY4ecdYaah4WYmYOVq4KTvoCQ0X2N5XqK+XWG/nSFEJuoFJuoHZuoK5unOpunSpqmWpqka5mjfJmhjJignpeer5acwZSa05KX5o+V+ouS/oqRKq2tLq2tNa2tPq2sSq2sV62rZayqdKypg6uok6uno6qls6mkxKei1qWh6KOf/KCd/5+cRL+xR7+wTL+wU7+wXL+wZ7+vc76vgL6ujb6tm72sqryrubyqybqp2rmo67en/rSm/7OmXtCyYNCyZNCxadCxcNCxedCxg9Cxjs+wmc+wps+ws86vwc2vz8yu38uu78qu/8eu/8eueuKufOKufuKuguKuh+KujuKuluKun+KuqOGvs+Gvv+Gvy+Cw2N+w5t6x9N2y/9uz/9qznfadnvadn/aeovWepvWfqvWgsPWht/WivvWjx/Sk0PSm2vOo5fOq8fKs/fGv/++z/+602/9h3P9h3f9h3/9h4f9h5P9i6f9i7f9i8/9k+P9m/v9p//9u//92//9///+L//+a//+eAABhAABgAABfAABeAABdBgBaHQBXNABTTgBPaABLgABHlgBErQBBxAA+2gA78gA49wA4AABqAABpAABoAABnAQBlEABiJQBeOwBaVABVbQBRhABMmQBIrwBDxgA+3AA88wA5+AA4AAVxAAVwAAVvAARuAwNrFQNoKQJkQAJgWAFbcABWhQBSmwBNsABJxwBE3QA/9AA5+QA5ABZ7ABV6ABV5ABR3BxJ0GBFwLQ9tRA5oXAtjcgheiAVZnQNUsgFPyABK3gBE9QA/+gA9ACiEACeEACeCBCaADyZ+HSV7MSN3RyFzXx9vdBxqiRhlnhRfsw5ZyQhT3wNM9gBG+wBEADqOATqNAzmMCDmKEziIIjeFNDaCSzV+YjN6dzB1iy1xoCpstCVnyh9h4Bha9w1R/ApPAk6XAk6XBE2WCU2UE02SIEyQOEuNT0qJZEiGeUaCjUN+oUB5tjx0yzdv4DBo9ydh/CVfAGKgAGGgAGGfAGGeCGGcJGCaPF+XUl6UZ12Re1yOj1qLoliHtlWCy1F+4Et490Nx/EFvAHWoAHWnAnWnCnWmGXSkK3SjQ3OgV3Kea3KcfnCZkW+WpG2TuGuPzGiL4WSH9l6B+12AAIiuAIiuAIiuCIetH4esNoerSoapXYancIWlgoSilYOgp4Gdun+azn2X4nqT93WP/HSOCZq1C5q1Dpq0G5q0L5qzQZmyU5mxZZmvdpith5ermZapq5WnvZOk0JGi446e+Iqb/YmaG6y7IKy7Jqy6Mqy6QKy5Tqy4Xqu3bau2fqq1jqqznqmyr6iwwKeu0qWr5aOp+Z+m/p6lN76/Or6/QL6/SL6/Ur6+Xr69a729eb28h727lry6pby4tbu3xbq21ri057ay+7Ow/7OwU8/CVc/CWc/CX8/CZ8/BcM/Be87Ahs7Aks6/n86/rc2+vMy9y8y82sq768m6/ce5/8a5cOHCceHCdOHCeOHCfuHCheHBjeDBl+DBoeDBrODBuN/Axd/A0t7A4N3A8Ny//9rA/9nAkfO5kvO5k/O5lvO5mvO5oPO6pvO6rfO7tfO7vvK8yPK80/K93vG+6vC/9+/B/+7D/+3Dzf+Izf+Izv+J0P+J0v+L1f+M2f+O3f+Q4v+T6P+W7f+Z9P+d/P+h//+l//+r//+0//+2AABrAABqAABpAABoAABmAwBjGABgLwBcSgBYZQBUfQBQkwBMqgBJwQBF2ABC8QA/9gA/AAB0AABzAAByAABxAABvCgBrIABoNwBkUABfaQBagABVlgBQrQBLwwBG2gBD8gBA9wA/AAV7AAR6AAR5AAN4AQN1DwJxJAJuOwFpVAFkbABfggBbmABWrgBRxABM2wBG8gBB+ABAABWFABSEABSDABOBBBF+ExB6KA92Pw1yVwpsbwdnhAVimQNdrwFXxQBS3ABM8wBG+ABEACePACaOACaNASWLCiWIGCSFKyKBQyF9Wh54cRtzhhhumxNosQ1ixwdb3QNU9ABN+QBLADmZADmYATmWBTiVDjeSHTaPLzWMRjSIXTKDcy9/iCx6nCl1sSRvxx9p3Rdi9QxY+glWAU2iAU2hAk2gBkyfD0ydHEuaMUqXSUmTYEePdUWLiUOHnUCCsjx9yDZ33jBw9Sdo+iRmAGGrAGGqAGGqAGCoA2CmGl+kNV+iTV6eYl2bd1uYi1mUn1eQs1SLyFCG3kuA9EN5+kF3AHSzAHSyAHSyA3SxEXSvJHOtO3OrUXKpZnGmenCjjW6goWyctWqYyWeU3mOP9F2J+VyIAIe6AIe6AIe5AIe4Doa3K4a2Qoa0V4Wya4SvfYOtkIKqo4Gnt3+kynyg33mc9XSX+nOWA5nBBZnBCZnBD5nAIJm/N5m+S5i8Xpi6cJe4gpa2lZW0p5SxuZOuzJGr4I6o9Yqj+omiD6vIEqvIGavHJKvHNKvGRavFVqrEZ6rCd6rBiKm/mai9qqe7vaa4z6S14qKy9p+v+56uJ73NK73NMb3NO73MSL3MVb3LY7zKcbzJgLzIkLvGoLvFsLrDwbnB0re/5La9+LO6/bK5Rs7RSc7RTc7RVM7RXc7QZ87Qcs3Pf83OjM3Nmc3Mp8zLtsvKxsvI1srH58jF+sbD/8XCZN/TZt/Tad/Tbt/TdN/TfN/Shd/Sj9/Smt/Rpd7Qst7Qv97Pzd3O29zN69vM/dnL/9nLhfHPhvHPiPHPjPHPkPHPlvHPnPHPpPHPrPHPtvHPwPDPy/DP2PDP5O/Q8u7Q/+3R/+zRu/+0vP+0vf+0vv+1wf+1xP+2yf+2zf+30/+42v+54f+76P+88f+++v/B///E///J///LAAB0AABzAAByAABxAABvAQBsEgBpKwBlRgBgYQBceQBYkABUqABQvwBN1gBJ7wBG9ABFAAB+AAB9AAB8AAB6AAB4BQB0GgBxMQBtTABnZQBifQBdkwBYqgBTwQBO2ABK8ABH9QBGAASFAASEAAODAAOBAAJ+CgJ7HwF3NgFzTwBtaABofwBjlQBeqwBZwgBT2QBN8QBI9gBHABSPABOOABONABKLARGIDQ+EIg6AOgx7Uwl1awZwgQRqlgJlrAFfwwBZ2QBT8QBN9gBLACaZACaYACWXACWUBSSSEyOOJSGLPiCGVh2BbRp8ghd2mBJwrgxqxAZj2wJb8gBU9wBSADijADiiADihAjefCTecGDaZKjWWQTORWTGMby6IhCyDmSh9riR3xR5x2xZp8wtf+AhdAEysAEysAUyrBEypCkunGEqkKUmhREidW0eZcEWVhUKQmj+LrzuFxTZ/2y948yZv+CNtAGC1AGC1AGC0AGCzAl+xDV+uLV6rR12oXVylclqhh1mdm1aZsFOUxVCP20qI8kKA+EB+AHO+AHO9AHO8AHO7B3O6HHK4MnK1S3GzYXCwdW+siW6pnWylsWqhxmed22KX8l2R91yPAIbFAIbFAIbEAIbDAYbCHYXBOoW/UIS8ZYS6eIO3jIK0n4Cws36tx3yp3Hik8nSf93KeAJnNAJnMApjMCJjLFJjKK5jJQpjHV5fFapbDfZbBkJW+o5S7tpK4yZC03Y2w84ms+IiqA6rUBarUC6rTGKrTJqrSOqrRTarPX6nOcanMg6jKlKjIpqfFuKXCy6S/36G89J63+Z23FrzaG7zaIrzZLbzZO7zYS7zXWrzWarvVervTirvSmrrQq7nOvLjLzrfJ4bXG9bLC+rLCOM3fO83fQM3fSM3fUs3eXc3daszcd8zbhczak8zZosvXscvWwcrU0snS48jQ98XN/MXMWd7iWt7iXt7iY97iat7ict7hfN7hh97gkt7fnt3eq93dud3cyNzb19vZ59rY+tjW/tjWevDie/DifvDigfDihvDijPDik/DhnPDhpO/hru/gue/gxe/g0e7f3u7e7e3e/ezd/+vdrf/Trv/Tr//Tsf/Ts//Tt//UvP/Uwf/Ux//Vzv/V1v/V3v/W5//X8f/Y+//Z///c///dAAB9AAB8AAB7AAB6AAB4AAB1DQBxJgBuQgBpXQBkdgBgjgBcpQBYvQBU1ABQ7QBM8wBMAACHAACGAACFAACDAACBAgB9FAB5LQB1RwBwYQBreQBlkABgpwBbvwBV1gBR7gBO8wBNAASOAAONAAOMAAKKAAKIBAGEGQGAMQF7SwB2ZABwewBrkQBmqABhvwBb1gBV7wBO9ABOABOYABKXABKWABGUABCRBw6NHAyJNQqETgh+ZgV4fQNzkwJtqQFnwABh1wBb7wBU9QBSACWiACWhACSgACSeAiObDSKYHiCUOB+PURyKaBmEfhZ/lBF4qgtxwQZq2AJj8ABb9QBZADetADesADeqADaoBTamEzWjJTSfOzKaVDCVai6QgCuLlieGqyN/wh142RVw8Qpm9gdkAEu2AEu2AEu0AkuzB0qxFEquIkmqPUemVkaibESdgUGZlz6TrDqNwjWH2S5/8SV29iN0AF/AAF+/AF++AF+9AF+7B164I121QFyyWFuubVqqgliml1airVOcwk+X2UqQ8EKI9T+FAHPIAHPIAHLHAHLGAHLEEnLCKXG/RHC8W3C5cG62hG2ymWuurmmqw2al2WKf8FyY9VuXAIXQAIXQAIXPAIXOAIXNCIXLL4TJSYTGX4PDc4LAh4G9m3+5r361xHux2Xis8HOm9XKlAJjYAJjXAJjXApjWCpfVHJfUOJfST5bQZJbNd5XLi5TInpPEspHBxo+92o248Imz9oiyAKrfAKreAareBqrdGancLKnbQ6nZV6nYaqjWfajUj6fRoqbOtKXLx6PI3KHE8Z6/9p2+BLvlBbvlDbvlHbvkLbvkP7vjUbvhYrvgcrreg7rclbnaprnYuLjVyrbS3bTP8rHL97HKJMzsKMzsL8zrOczrRczqU8zqYMzpbsvnfcvmjMvknMrjrMrhvMnfzsjc4MfZ9MXW+cTVS93xTd3xUd3xV93wX93waN3vc93vft3uit3tl9zrpdzqs9zpwtvn0tvl49rj9tjg+9ffbu7zcO7zcu7zdu7ze+7zgu7ziu7yku7ynO7xpu7wsu7wvu7vy+3u2e3s6Ozr+uvp/uvpoP/soP/sov/spP/sp//sq//ssP/stv/svP/sxP/szP/s1f/s3//s6f/s9P/s///t///uAACFAACFAACEAACCAACAAAB+BwB6IAB1PQBxWQBscgBoigBjogBfugBb0gBX6wBT8QBSAACQAACPAACNAACMAACKAACGDgCCJwB9QgB4XQBzdQBtjQBopABivABc0wBY7ABU8QBTAAOXAAOWAAKVAAKTAAKRAQGNEgGIKwCDRgB+XwB4dwBzjgBupQBovABi1ABb7QBV8gBUABKhABGgABGfAA+dAA6aAw2WFQySLwmMSQeGYgWAeQN6jwF1pgBvvQBo1QBh7QBa8wBYACSrACSrACOqACOnACKkByGhGB+dMR6YTBuSZBiMehWGkRCApwp5vgVx1gFp7gBh8wBfADe2ADa1ADa0ADWyAjWvDDSsHzOoNDGjTi+eZi2ZfCqTkiaNqCKHvxyA1hR37whs9AZqAEvAAEu/AEq+AUq8BEm6Dkm3HUizNkevUEWqZ0OlfUGhkz2bqTmVvzSO1i2G7iR99CJ6AF/JAF/JAF7IAF7GAF7EBF3CFl2+OFy7UVu3aFmyfleuk1WpqVKkv06e1kmX7kGO8z6MAHLSAHLSAHLRAHLPAHHOBXHLIHHJO3DFVG/Cam6+f2y6lWu2qmixwGWs1mGm7luf81qdAIXaAIXZAIXZAITXAITWAITUIYTSQIPPWILMbYHJgoDFl3/CrH29wXu51nez7nOt83GrAJfhAJfhAJfgAJfgApfeEZbdKpbbRpbZXJXWcZTThZPQmZLNrpHJwo/F14zA7oi684e5AKnpAKnpAKnoAKnoBqnnHqnlNqjkTajiYqjgdqfdiabanaXXsKTUxKLQ2KDM753H9JzFALvxALvwALrwBLrvGrrvL7ruRLrsWLrqarrpfLnmjrnkobjhs7fexrbb2rTX8LHT9bHSBsv4DMv4F8v3JMv3NMv2Rcv1Vcv0ZMvzdMrxhcrvlcrtpsnrt8npycjm3Mbi8cTe9sTdONz+O9z+QNz9R9z9Udz9XNz8aNz7dNz6gdz5j9z4ntv2rdv0vdvyzdrw39nt89fp+NfoYO3/Yu3/ZO3/ae3/b+3/du3/f+3/iO3/k+3/nu3+qu39t+38xez70+z54+z39ur0++rzk///k///lf//l///m///n///pf//q///sv//uf//wv//zP//1v//4f//7f//+//+///+AACGAACFAACEAACDAACBAAB+BwB6IAB2PQBxWABscgBoigBkogBgugBb0gBX6wBT8QBSAACQAACPAACOAACNAACKAACHDgCDJwB+QgB4XQBzdQBujABopABjvABd0wBZ7ABU8QBUAAOXAAKWAAKVAAKUAAGRAQGNEgGJKwCERgB+XwB4dwB0jgBupQBovABi1ABc7QBV8gBUABKhABGhABCgAA+eAA6bAw2WFAySLwmNSQeGYgWAeQN7jwF1pgBvvQBp1QBi7QBa8wBZACSsACSrACOqACOoACKlByGhGB+dMR6YTBuSYxiNehWHkBCApwp5vgVy1gFq7gBh8wBgADa2ADa2ADa0ADWyAjWvDDSsHzOoNDGkTi+eZS2ZeyqUkiaOqCKHvxyA1hR37ghs9AZqAEvAAEvAAEq+AEq9BEm6Dkm3HUi0NkevUEWrZkOmfEGhkj2cqTmVvzSP1i2H7iR99CF7AF/KAF/JAF7IAF7HAF7FBF3CFl2/N1y7UVu3aFmzfVeuk1WqqVKkv06e1kmX7kGP8z6MAHLTAHLSAHLRAHLQAHHOBXHMIHHJO3DGVG/Cam6/f2y7lWu2qmiywGWs1mGm7Vuf81qdAIXaAIXZAIXZAITYAITWAITUIIPSP4PPV4LMbYHJgoDGl3/CrH2+wXu51ne07nOt83GsAJfiAJfhAJfhAJfgApffEZbdKpbbRZbZXJXWcZTUhZPRmZLNrpHJwo/F14zA7oi684e5AKnqAKnpAKnpAKnoBannHajmNajkTajiYqfgdafdiabbnKXYsKTUw6LR2KDM753H9JzGALvxALvxALrxBLrwGbrvLrruRLrtV7rrarrpfLnnjrnloLjis7ffxrbb2rTY8LHT9bHSBMv4C8v4Fcv4I8v3M8v3RMv2VMv0ZMvzdMryhMrwlcrupsnst8npycjm3Mbj8cTe9sPdN9z+Otz+P9z+Rtz+UNz9W9z9Z9z8c9z7gdz6j9z4ntv3rdv1vdvzzdrw39nt89fq+NfpX+3/Ye3/ZO3/aO3/bu3/de3/fu3/iO3/ku3/nu3/qu3+tu39xOz70+z64+v39ur0+ur0kv//k///lP//l///mv//n///pP//qv//sv//uf//wv//y///1v//4P//7P//+///////\",velvet50:\"AAAAAwAACwAAFgAAKAAAPQAAUQAAYwAAdAAAhQAAmAAAqgAAugAAyQAA2gAA6wAA/AAAAAQAAwMACwIAFgEAKAEAPQAAUQAAYwAAdAAAhQAAmAAAqQAAugAAyQAA2QAA6gAA/AAAAg0BBQwBCgoAFwkBKQYBPgMBUgIBYwEAdAABhgABmAAAqgAAugAAyQAA2QAA6gAA/AAABRsCCBoCDRkCFxgCKxQCQBECUw4CZAsCdQgChwUCmQIBqgEBuwABygAB2gAA6wAA/AAACDIEDDEEEjAEHC4ELCsEQCgEUyUEZSIEdh8EhxwDmhgDqxQDuxACygwC2wgB7AQB/QIABkgGCkcGEUYGGkUGKUIGP0AGUj0GZDoGdTcGiDQFmjEFqy0FuyoEyycD2yMD7B4C/hkBAVwJAlsJBloJDVgJJFYJPVQJUVIJY08JdU0JiEoImkcIq0QHvEEGyz0G3DoE7TYD/jICAG0NAG0NAGwNCmoNJGkNO2cNT2UNYmINdWAMiF0LmloLq1gKu1UJy1II3E8G7UsF/kcEAIAQAIAQAH8QB34QGXwQNnoQTHkQYHcQc3QPh3IPmm8OqmwNu2oMy2cL3GQJ7WAH/10FAJQTAJMTAJITAJETF5ATLo8TR40TXIsTcokShocSmIQRqYIQun8Pyn0O23oM7XYJ/nMHAKYWAKUWAKUWAKQXAqMXJqEXQqAXWp4WcJwWhJoWl5cVqJUUuZMTyZAR244P7YsM/ocJALcYALYYALYZALUZALQZGLMZPrEZWbAZb64Zg6wYlaoYp6cXuKUWyaMU26AS7Z4P/5sMAMgXAMgXAMcXAMcXAsYYEsQYOsMYV8EZbcAZgb4ZlLwZpboYt7gXyLUW2rMU7bAR/q0NANkbANkbANgbANgbANcbBtYbNNUbU9MbatEaftAZkc4Xo8wVtcoVx8gU2cYT7MMR/sANAOshAOshAOshAOohAOohAOkhK+chTeYgZOUfeuMejeEdoOAbs94YxdwT2NkM69cL/dQJAP4lAP4lAP0lAP0lAPwlAPslHvolQ/kkXfgkdPYjifUinfMgsfEdxO8Z1+0R6usA/OgAAP8oAP8oAP8oAP8oB/8nE/8nOP8nUf8mZv8mef8kjP8jn/8hsv8ew/8a1v8T6f0D+vsAAAABAwABCgABFgACKAACPQACUQACYwABdAAChgABmQABqgABugABygAA2gAA6wAA/QAAAAMDAwMDCgIDFgEDKAADPQADUQADYwADdAADhgADmAADqgACugACyQAB2gAB6wAA/AAAAgwEBQsECgoEFwkEKQYEPgMEUgIEZAEEdAAEhgAEmQADqgADugADygAC2gAC6wAB/QAABRsGCBoGDRkGFxcGKxQGQBEGUw4GZQoGdQcGhwQFmgIFqwEFuwAEygAD2wAD7AAC/QABCDIIDDEJEjAJHC4JLCsJQCgJUyUIZSIIdh8IiBsHmhgHqxQGuxAGywwF2wgE7AQD/gICBkgLCkcLEUYLGkULKUILP0ALUj0LZDoLdjcLiDQKmjEKrC0JvCoIyycH3CMG7R4E/hkDAVwOAlsOBloODVgOJFYPPVQPUVIOY08OdU0OiEoNm0cNrEQMvEELyz0J3DoI7TYG/jIEAG4SAG0SAGwSCmsSJGkSO2cSUGUSYmMSdWARiF0RmloQq1gPvFUOy1IM3E8K7UsI/kcGAIAVAIAVAH8VB34VGXwWNnsVTHkVYHcVdHQUh3IUmm8Tq2wSu2oRy2cP3GQN7WEL/10IAJQZAJMZAJMZAJIZF5AZLo8ZR40ZXIsZcokYhocYmIQXqYIWun8Uyn0T23oR7XcO/nMKAKYbAKUbAKUbAKQcAqMcJqIcQqAcWp4ccJwchZobl5gaqJUZuZMYyZEW244U7YsR/ogNALccALYcALYdALUdALQdGbMdPrIdWbAdb64dg6wdlaodp6gcuKUbyaMZ26AX7Z4U/5sPAMgfAMgfAMcgAMcgAsYgEsUgOsMfV8EfbcAfgb4elLwdpboct7gcyLUb2rMZ7bAW/q4RANklANklANglANglANclBtYlNNUlU9MlatIkftAkkc4jo8whtcofx8gb2sYX7MMV/sASAOspAOspAOspAOopAOopAOkpK+goTeYoZOUoeuMnjeImoOAks94ixtwf2NkZ69cP/dQMAP4rAP4rAP0rAP0rAPwrAPsrHvorQ/krXfgqdPYqifUpnfMosfEmxO8i1+0d6usT/OgAAP8vAP8vAP8vAP8vB/8uFP8uOP8tUf8tZv8sef8rjP8qn/8osv8mxP8i1v8d6f0U+/sAAAAFAwAFCgAFFQAFKAAFPQAFUQAFYwAFdAAFhgAFmQAEqgAEuwADygAD2wAC7AAB/QABAAMIAwMICgIIFgEIKAAIPQAIUQAIYwAIdAAIhgAHmQAHqgAGugAFygAF2gAE6wAD/QACAgwKBQsKCgoKFggKKQYKPgMKUgIKZAEKdQAKhgAJmQAJqgAIuwAHygAG2gAF7AAE/QACBRsNCBoNDRkNFxcNKxQNQBANUw4NZQoMdgcMhwQMmgILqwEKuwAJygAI2wAH7AAF/QADCDEQDDEQEjAQHC4QLCsQQCgQVCUQZSIQdh8PiBsOmxcOrBQNvBALywwK3AcJ7QQH/gEFBkgTCUcTEUYTGkUTKUITP0ATUz0TZToTdjcSiDQSmzERrC0QvCoPyyYN3CIL7R4J/hkHAVwWAlsWBloWDVgXJFYXPVQXUVIXY08WdU0WiEoVm0cUrEQTvEESzD0Q3DoO7jYM/jIJAG4aAG0aAGwaCmsaI2kaO2caUGUaYmMadWAZiF0Zm1oYrFgXvFUVy1IT3E8R7ksO/0cLAIAeAIAeAH8eBn4eGXweNnseTHkeYHcedHQeiHIdmm8cq20bu2oZy2cX3GQV7mES/10OAJQhAJMhAJMhAJIhF5AiLo8iR40iXYwicokhhochmIQgqYIfun8eyn0c3HoZ7ncV/nMRAKYjAKUjAKUjAKQkAqMkJqIkQqAkWp4kcZwkhZokl5gjqJUiuZMhypEg244d7Ysa/4gVALcoALcpALYpALUpALQpGbMpPrIpWbApb64og6wolqonp6gluKUkyaMi26Eg7Z4d/5sZAMgtAMgtAMctAMctAsYtEcUtOsMtV8ItbcAsgb4slLwrpboqt7goyLYl27Mi7bAe/64aANkxANkxANkxANgxANcxBtYxNNUxU9MxatIwftAwkc4vo80utcosx8gp2sYm7MMg/sEZAOw0AOs0AOs0AOs0AOo0AOk0K+g0TOY0ZOU0euMzjeIyoeAxs94wxtwt2dop69ck/dUYAP42AP42AP42AP02APw2APs2Hvs2Q/k2Xfg2dPc1ifU1nfMzsfEyxO8w1+0s6usm/OkZAP86AP86AP85AP85CP85FP85OP84Uf84Zv83ev82jf81n/8zsv8xxP8u1v8p6f4j+/sVAAAMAwAMCgAMFQAMJwAMPQAMUQAMZAAMdQAMhwALmQAKqwAKuwAJywAH2wAG7AAF/gADAAMRAwMRCgIRFgERJwARPQARUQAQYwAQdAAQhgAPmQAOqwANuwAMygAL2wAJ7AAH/gAFAgwUBQsUCgoUFggUKQYUPgMUUgIUZAATdQAThwASmQARqwAQuwAPygAN2wAL7AAJ/gAHBRoXCBoWDRkWFxcXKxQXQBAXUw0XZQoWdgcWiAQVmgIUqwETvAASywAQ2wAO7QAM/gAJCDEbDDEbEjAbHC4bLCsbQCgbVCUbZSIadh8aiBsZmxcYrBMXvBAVzAwT3AcR7QQP/wEMBkgfCUcfEUYfGkUfKUIfPz8fUz0fZTofdjcfiTQemzEdrC0bvCoazCYY3SIV7h4S/xkOAVwjAlskBlokDVgkJFYkPVQkUVIkZE8kdk0jiEojm0cirEQgvEEezD0c3ToZ7jYW/jESAG4oAG0oAGwoCmsoI2koO2coUGUoYmModWAoiF0nm1smrFglvFUkzFIh3U8e7ksb/0cWAIErAIArAH8rBn4rGH0rNnssTHksYHcsdHUriHIrmm8qq20pvGooy2cm3WQj7mEg/10aAJQvAJQvAJMvAJIvFpEvLo8vR44vXYwvcokvhocumYUuqoItuoAry30p3Hon7nck/3MfAKYzAKYzAKUzAKQzAqMzJqI0QqAzWp4zcZwzhZozl5gyqZYxuZMvypEt3I4r7osn/4gjALc3ALc3ALY4ALY4ALU4GbM4PrI4WbA4b643hKw3lqo2p6g1uaY0yaMy26Ev7p4r/5slAMg7AMg7AMg7AMc7AsY7EcU8OsQ8V8I7bcA7gr47lLw6pro5t7g4ybY227M07bAv/64oANk/ANk/ANk/ANg/ANc/BNY/NNU/U9Q/atI/ftA+kc8+o809tss8x8g62sY37cMz/sEsAOxCAOxCAOtCAOtCAOpCAOlCK+hCTOdCZOVCeuRBjuJBoeBAs94/xtw92do77Nc3/tUvAP5EAP5EAP5EAP1EAP1EAPxEHvtEQvpEXfhEdPdDifVDnfRCsfJBxPA/1+096us4/OkwAP9HAP9HAP9GAP9GCP9GFP9GOP9GUf9FZv9Fev9Ejf9DoP9Csv9BxP8+1v876f41+/wqAAAXAwAXCgAXFQAXJwAYPQAYUQAXZAAXdQAXhwAVmgAUqwATvAASywAQ3AAO7QAM/wAJAAMdAwMdCgIdFQEdJwAdPQAdUQAdYwAddQAchwAbmQAaqwAYuwAXywAV2wAT7QAQ/gANAgwhBQshCgohFgghKQUhPgMhUgIhZAAhdQAghwAfmgAeqwAduwAbywAZ2wAW7QAT/gAPBRomCBkmDRgmFxcmKxMmQBAmUw0mZQkmdgYliAQkmwIjrAEivAAgywAd3AAb7QAX/wATCTEqDDAqEi8rHC0rKysrQCcrVCQrZiErdx4qiRsqmxcorBMnvQ8lzAsj3Qcg7gMc/wEXBkgvCUcvEEYvGkUvKEIvPz8vUz0vZTovdjcviTQunDAtrS0svSorzCYo3SIl7h0h/xgcAVwzAlszBlozDVgzJFYzPVQ0UVI0ZE80dk0ziUoznEcyrUMxvUAwzD0u3Tor7zYn/zEiAG43AG03AGw3CWs3I2k4O2c4UGU4YmM4dWA4iV03m1s2rFg1vVU0zFIy3U8w70ss/0coAIE7AIA7AIA7BX47GH08Nns8THk8YHc8dHU8iHI7mm86q206vGo4zGc23WQ072Ex/10sAJQ/AJQ/AJM/AJI/FpE/Lo8/R44/XYw/coo/h4c/mYU+qoI9u4A8y3073Xo47nc1/3MwAKZCAKZDAKVDAKVDAqNDJqJDQqFDW59DcZxDhZpCl5hCqZZBupRAypE/3I487os5/4g1ALdGALdGALdGALZGALVGGLRGPrJHWbBGcK5GhKxGlqpGp6hFuaZEyqND3KFB7p49/5s5AMlKAMhKAMhKAMdKAcZKEcVKOsRKV8JKbcBKgr5KlL1JprpJuLhIybZH27NF7rFC/649ANpNANpNANlNANlNANhNA9dNM9VNU9RNatJNftFNkc9No81MtstLyMlK2sZI7cRF/8FBAOxQAOxQAOxQAOtQAOpQAOlQK+hQTOdQZOVQeuRQjuJQoeBPtN5OxtxN2dpL7NhI/tVEAP9RAP5RAP5SAP5SAP1SAPxSHvtSQvpSXflSdPdSifZRnvRRsfJQxPBP2O5N6+tJ/elEAP9UAP9UAP9UAP9UCP9UFP9UOf9UUv9TZ/9Tev9Tjf9SoP9Rs/9QxP9O1/9L6v5H+/xAAAAmAwAmCgAmFQAmJwAmPQAmUQAmZAAmdQAlhwAkmgAjrAAhvAAgywAd3AAa7QAX/wATAAQtAwMtCgItFQEtJwAuPQAuUQAtZAAtdQAthwAsmgArqwApvAAoywAl3AAi7QAe/wAZAgwxBAsyCgoyFggyKQYyPgMyUgEyZAAydQAyhwAxmgAwrAAuvAAtywAr3AAo7QAk/wAeBRo2Bxk2DRg2FxY2KxM3QA83VAw3ZQk2dgY2iAM2mwI1rAAzvAAyzAAw3AAt7gAp/wAkCTA6DDA6ES86Gy07Kyo7QCc7VCQ7ZiE7dx47iRo6nBY6rRI4vQ43zAo13QYz7gMv/wEqBkg+CUc+EUY/GkQ/KEI/Pz8/Uzw/ZTo/dzc/iTM/nDA+rS09vSk8zSY63SI47x01/xgwAVxCAltCBlpDDVlDI1ZDPVRDUVJDZE9Ddk1DiUlDnEZCrUNBvUBAzT0/3jk97zU6/zE1AG5GAG1GAGxGCWtHI2lHOmdHUGVHY2NHdmBHiV1HnFtGrVhFvVVEzVJD3k9B70s+/0c6AIFKAIFKAIBKBX9KF31KNntKTHlLYHdLdHVLiHJKm29KrG1JvGpIzGdH3mRF72FD/10/AJRNAJRNAJNNAJJOFZFOLZBOR45OXYxOcopOh4dOmYVNqoJNu4BMy31L3XpJ73dH/3NDAKZQAKZQAKZQAKVRAqRRJqJRQqFRW59RcZ1RhZpRmJhRqZZQupRQy5FP3Y5N74tL/4hHALhUALdUALdUALZUALVUGLRUPrJUWbBUcK5UhKxUlqpUqKhUuaZTyqNS3KFR7p5P/5tMAMlXAMlXAMhXAMhXAMdXEMZYOsRYV8JYbcFYgr9YlL1YprtXuLhXybZW3LNV7rFT/65PANpaANpaANpaANlaANhbAtdbM9ZbU9RbatNbftFbkc9bpM1atstayMlZ28ZY7cRW/8FSAO1dAOxdAOxdAOxdAOtdAOpdKuleTOdeZOZeeuRejuNdoeFdtN9cxt1c2dpa7NhY/tVUAP9fAP9fAP5fAP5fAP1fAPxgHvtgQfpgXflgdPhfifZfnvRfsfJexfBd2O5c6+xZ/elVAP9iAP9iAP9iAP9iCP9iFP9iOf9hUv9hZ/9hev9hjf9goP9fs/9exf9d1/9b6v5Y/PxSAAA2AwA2CgA3FQA3JwA3PQA3UQA3ZAA3dQA3iAA2mgA1rAAzvAAyzAAw3QAt7gAp/wAkAAM9AwM9CgI9FQE9JwE+PQA+UQA+ZAA+dQA9hwA9mgA8rAA7vAA6zAA43AA17gAy/wAtAgtBBAtBCglBFghCKAVCPgNCUgFCZABCdgBCiABBmgBArAA/vAA+zAA93AA67gA3/wAzBRlFBxlFDBhGFhZGKhNGQA9GUwxGZQlGdwVGiQNFmwFFrABEvQBDzABB3QA/7gA8/wA4CTBJCy9JES9KGy1KKypKQCdKVCNKZiBKdx1KiRlKnBVJrRJIvQ5HzQpG3gZE7wNB/wA+BkdNCkdNEUZNGURNKEJOPz9OUzxOZTlOdzZOijNOnC9NrSxNvilMzSVK3iFJ7xxG/xdDAVxQAltQBVpRDVhRI1ZRPVRRUVFRZE9RdkxRiUlRnEZRrUNQvkBQzT1P3jlN8DVL/zFIAG5UAG1UAGxUCGtUImlVOmdVUGVVY2NVdmBViV1VnFpVrVhUvVVTzVJT3k9R8EtP/0dMAIFXAIFXAIBYBH9YFX1YNXtYTHlYYHdYdHVYiXJYm3BYrG1YvWpXzWdW3mRV8GFT/11RAJVbAJRbAJRbAJNbFJFbLZBbR45cXYxcc4pch4dcmYVcq4Jbu4BbzH1a3XpZ73dX/3NVAKdeAKZeAKZeAKVeAqRfJaJfQqFfWp9fcZ1fhZpfmJhfqZZfupRey5Fe3Y5d74tb/4hZALhiALdiALdiALZiALViF7RiPbJiWbFjcK9jhK1jlqpjqKhiuaZiyqNh3aFg755f/5tcAMllAMllAMllAMhmAMdmD8ZmOcRmVsNmbcFmgr9mlL1mprtmuLllyrZl3LRk77Fi/65fANppANppANppANlpANhpAtdpMtZpUtRpadNpftFpkc9ppM5ptstpyMlo28dn7sRl/8FiAO1sAO1sAOxsAOxsAOtsAOpsKelsS+hsZOZseeVsjuNsoeFstN9rx91r2ttp7dho/9VkAP9uAP9uAP9uAP5uAP5uAP1uHfxuQPtuXPlvdPhuifZunvVusvNtxfFt2O5r6+xp/uplAP9xAP9xAP9xAP9xB/9xFP9xOf9wUv9wZ/9wev9wjv9vof9vs/9uxf9t2P9r6v9o/P1jAABFAwBGCgBGFQBGJgBGPQBGUQBGZABGdQBGiABGmwBFrABEvQBDzABB3QA/7wA8/wA4AQNMAwJMCgJMFQFMJgBMPABNUQBNZABNdQBNhwBMmgBMrABLvABKzABI3QBG7gBE/wBAAgtPBApQCglQFQdQKAVQPgNQUgFRZABRdgBQiABQmwBQrABPvQBOzABN3QBL7gBI/wBFBBlTBxhTDBdTFhVTKhJUQA9UUwtUZQhUdwVUiQNUnAFTrQBTvQBSzQBR3QBP7wBN/wBKCS9XCy9XEC5XGixXKilXQCZYVCNYZiBYdxxYihhYnBVXrRFXvg1WzQlV3gVU7wJS/wBPBkdaCkZaEEVaGURbKEFbPz5bUztbZTlbdzZbijJbnS9britbvihaziRZ3iBY8BtW/xZUAVxeAlteBVpeDVheIlZePFNfUVFfZE9fdkxfiklfnEZfrUNfvj9ezjxd3zhc8DRb/zBZAG5hAG1hAGxhBmtiIWliOmdiT2ViY2NjdmBjiV1jnFpjrVhivlVizVJh305g8Etf/0ddAIFlAIFlAIBlBH9lFH1lNXtmTHlmYHdmdXVmiXJmm29mrG1mvWpmzWdl3mRk8GBj/11hAJVoAJRoAJRpAJNpE5FpLJBpRo5pXYxqc4pqh4dqmoVqq4JqvIBpzH1p3npo8Hdn/3NlAKdsAKdsAKZsAKVsAaRtJKNtQaFtWp9tcZ1thppumJhtqZZtu5Rty5Fs3Y5s8Itq/4hoALhwALhwALdwALZwALVwFbRxPbNxWLFxcK9xhK1xlqtxqKhxuqZxy6Rw3aFv755u/5tsAMl0AMl0AMl0AMh0AMd0DsZ0OMR1VsN1bcF1gr91lL11prt1uLl0yrZ03LRz77Fx/65vANt3ANt3ANp4ANp4ANl4Adh4MdZ4UtV4adN4ftF4kdB4pM54t8x4ycl328d27sR1/8JyAO17AO17AO17AOx7AOt7AOp7J+l7Suh7Y+d7eeV7juN7oeF7tN97x9162tt57dh4/9Z1AP99AP99AP99AP99AP59AP19G/x9P/t+XPp+dPh+ifd+nvV9svN9xfF82e977Ox5/up2AP+AAP+AAP+AAP+AB/+AFP+AOP+AUv+AZ/9/e/9/jv9/of9+tP9+xv992P976/95/P11AABTAwBTCgBUFABUJgBUPABVUQBVZABVdgBViABUmwBUrQBTvQBSzQBR3gBQ7wBN/wBKAANZAwJZCQJZFAFZJgBaPABaUQBaYwBadQBaiABamwBZrABZvQBYzABX3QBW7wBU/wBSAgpcBApcCQlcFQddJwVdPQJdUgFeZABedgBeiABdmwBdrABdvQBczABb3QBa7wBZ/wBWBBhgBxdgCxZgFRVgKRFhPw5hUwphZQdhdwRhiQJhnAFhrQBhvQBgzQBf3gBe7wBd/wBbCC5jCy5kEC1kGitkKihkQCVlVCJlZh5ldxtlihdlnBRlrg9lvgxkzgdk3gRj8AFh/wBfB0ZnCkZnEEVnGENoJ0BoPj5oUztpZThpdzVpijJpnS5pritpvidoziNo3x9n8Bpm/xVkAVtrAltrBVprDFhrIFZsPFNsUVFsZE5td0ttikhtnUVtrkJtvj9szjts3zhr8TRq/y9oAG5vAG1vAGxvA2tvIGlwOWdwT2VwYmJwdmBxiV1xnFpxrVdxvlRwzlFw305v8Upu/0ZsAIFzAIFzAIBzA39zE31zNHt0THl0YHd0dXR0iXJ1m291rGx0vWp0zWd032Rz8WBy/11wAJV3AJR3AJR3AJN3EpF3K5B4Ro54XYx4cop4h4d4moV4q4J4vIB4zH143np38HZ2/3N0AKd7AKd7AKZ7AKV7AKR7I6N8QaF8Wp98cZ18hZp8mJh8qpZ8u5R8zJF83o578It6/4h4ALh/ALh/ALd/ALd/ALZ/ErR/PLOAWLGAb6+AhK2AlquAqKiAuqaAy6R/3aF/8J59/5t8AMqCAMmDAMmDAMiDAMeDDMaDN8WDVcODbcGEgb+ElL2EpruEubmDyraD3bSC77GB/65/ANuGANuGANqGANqGANmGAdiGMNaHUdWHadOHftKHkdCHpM6Ht8yHycqG3MeG78SE/8KCAO6JAO6JAO2JAO2JAOyJAOuJJeqJSeiKY+eKeeWKjeSKoeKKtOCJx92J29uI7tmH/9aFAP+LAP+LAP+LAP+LAP+LAP6MGP2MPvyMW/qMc/mMifeMnvWMsvOMxfGL2e+K7O2J/uqGAP+OAP+OAP+OAP+OBv+OFP+OOP+OUf+OZ/+Oe/+Ojv+Oof+NtP+Nxv+M2f+L6/+J/f2GAABfAwBgCQBgFABgJQBhPABhUQBhZABhdgBhiABhmwBhrQBhvQBgzQBf3gBe8ABd/wBaAANlAwJlCQJmFAFmJQBmOwBnUABnYwBndQBniABnmwBnrABnvQBmzQBm3gBl7wBj/wBhAgppBAlpCQhpFAZqJgRqPQJqUQFrZABrdgBriABrmwBrrQBrvQBqzQBq3gBp7wBn/wBmBBdtBhZtCxVtFRNtKBBuPg1uUgluZQZvdwRviQJvnABvrQBvvgBuzQBu3gBt7wBs/wBqBy1xCixxDyxxGSpxKSdyPyNyUyByZh1zdxpzihZznRJzrg5zvgpzzgZy3wNx8AFw/wBuCEV1CkV1D0R1F0J1J0B2Pj12Ujp3ZTd3dzR3ijB3nS13ril3vyZ3ziJ23x528Rl1/xNzAVt5Alp5BVl5DFd6H1V6O1J6UFB7ZE17dkt7ikd7nUR7rkF7vj57zjp73zd68TN5/y53AG59AG19AGx9Amt+Hml+OGd+TmR/YmJ/dl9/iVx/nFl/rVd/vlR/zlF/301+8Up9/0V7AIGBAIGBAICBAn6CEn2CM3uCS3mDYHeDdHSDiXGDm2+DrWyDvWmDzmaD32OC8WCB/1yAAJWFAJSFAJSFAJOGEJGGKpCGRY6GXIyHcomHh4eHmoSHq4KHvH+HzXyH33mG8XaF/3KEAKeJAKeJAKaJAKWJAKSJIaOKQKGKWZ+KcZ2KhZqLmJiLqpaLu5OLzJGK3o6K8YuJ/4eHALiNALiNALiNALeNALaNELSNO7ONV7GOb6+OhK2OlquOqKiOuqaOy6OO3qGN8J6N/5uLAMqQAMqQAMmQAMmQAMiQCsaRNsWRVcORbMGRgb+RlL2RpruRubmRyraR3bSR8LGQ/66OANuTANuTANuTANqTANmTAdiULteUUNWUaNSUftKUkdCUpM6Ut8yUycqU3MeU78WT/8KRAO6WAO6WAO6WAO2WAOyWAOuWIuqWSOmWYueXeeaXjeSXoeKXtOCXx96X29uW7tmV/9aTAP+YAP+YAP+YAP+YAP+YAP6YFf2ZPfyZWvuZc/mZiPiZnvaZsvSZxvKZ2e+Y7e2X/uuVAP+bAP+bAP+bAP+bBf+bE/+bN/+bUf+bZ/+be/+bjv+bof+btP+bx/+a2f+Z7P+Y/f6VAABsAwBtCQBtEwBtJABuOwBuUABvYwBvdgBviABvmwBvrQBvvgBuzQBu3gBt8ABs/wBqAAJyAgJzCAFzEwF0IwB0OgB0TwB1YwB1dQB1iAB1mwB1rAB1vQB1zQB03gB08ABy/wBxAgl2BAh3CAd3EwZ3JAR4OwJ4UAF5YwB5dQB5iAB5mwB5rQB5vQB5zQB43gB48AB3/wB1BBZ7BhV7CxR7FBJ8Jw98PQt8Ugh9ZAV9dgN9iQF9nAB9rQB9vgB9zQB93gB88AB7/wB5Byt/CSt/Dip/GCiAJyWAPiKAUx6BZRuBdxiBihSCnRCCrgyCvgiBzgSB3wKA8QB//wB+B0SDCUSDDkODFkGEJj6EPTyEUjmFZTWFdzKFii+GnSuGriiGvySFziCF4ByF8ReE/xGCAVqHAlmHBViHDFeIHVSIOlKIUE+JY02JdkqJikeKnUOKrkCKvz2KzzmJ4DaJ8TGI/y2HAG2LAG2LAGyLAGqMHGiMN2aMTmSNYmGNdl+NiVyNnFmNrlaOvlONzlCN4EyN8kmM/0WLAIGPAICPAICPAn6PEn2QMXuQSnmQX3aQdHSRiHGRm26RrWyRvmmRzmaR4GKQ8l+Q/1uPAJWSAJSTAJSTAJKTDZGTKI+TRI6UXIyUcomUh4eUmoSUq4KVvH+VzXyU33mU8XaT/3KSAKeWAKeWAKaWAKWWAKSXH6KXP6GXWZ+XcJyXhZqYmJiYqpaYu5OYzJGY346X8YqX/4eWALiZALiZALiZALeZALaaDbSaOrOaV7Gabq+bg62blqubqKibuqabzKOb3qGb8Z6a/5uZAMqcAMqdAMmdAMmdAMidCcadNMWdVMOebMGegb+elL2eprueubmey7ae3rSe8LGd/66cANygANugANugANqgANmgAdigK9egT9WhaNShfdKhkdChpM6ht8yhycqh3ceh8MWg/8KfAO+jAO6jAO6jAO2jAO2jAOyjH+qjRumjYeekeOakjeSkoeKktOCkyN6k29yk7tmj/9aiAP+lAP+lAP+lAP+lAP+lAP+mEf2mOvymWfumcvqmiPinnfansvSnxvKm2fCm7e2l/uukAP+oAP+oAP+oAP+oBP+oEv+oNf+pUP+pZv+pev+pjv+pof+ptf+ox/+o2v+n7P+m/v6kAAB6AwB7CQB7EgB7IwB8OgB8TwB9YwB9dQB9iAB9mwB9rQB+vgB9zgB93wB88AB7/wB5AAKBAgKBCAGBEgGCIgCCOQCDTwCDYgCDdQCEhwCEmwCErQCEvQCEzQCD3gCD8ACC/wCAAQiFAwiFCAeFEgWGIwOGOgGGTwCHYwCHdQCHiACImwCIrQCIvQCIzQCH3gCH8ACG/wCEAxSJBRSJCROJExGJJQ6KPAqKUQeLZASLdgKLiQGLnACMrQCMvgCLzgCL3wCL8ACK/wCIBimNCCmNDSiNFiaNJiOOPSCOUh2PZRmPdxaPihKPnQ2PrgmQvwaPzgOP3wGP8QCO/wCNBkORCEKRDUGRFECRJT2SPDqSUTeSZDSTdzGTii2TnSmTriaTvyKTzx6T4BqT8hWS/w+RAVmUAlmUBFiVDFaVG1OVOVGWT06WY0uWdkmXikWXnUKXrj+XvzyXzziX4DSX8jCW/yuVAG2YAGyYAGuYAGqYGmiYNWaZTWOZYWGZdV6aiVuanFiarlWav1Kaz0+a4Eya8kiZ/0SYAIGbAICbAH+bAX6cEHycL3qcSXidX3addHOdiHCdm26erWuevmiezmWe4GKd8l6d/1ucAJWfAJSfAJOfAJKfCpGfJ4+gQ42gW4ugcomgh4ahmoShq4GhvX6hzXyh33ih8nWg/3GgAKeiAKeiAKaiAKWjAKSjHaKjPaGjWJ+kcJykhZqkmJikqpWku5OlzZCk342k8Yqk/4ejALimALimALimALemALamCLSmOLOnVrGnbq+ng62nlqqoqKiouqaozKOo3qGo8Z6n/5qmAMqpAMqpAMmpAMmpAMiqB8eqMcWqUsOqa8GrgL+rlL2rprurubmry7ar3rSr8bGr/66qANysANysANutANutANqtANmtKNetTdatZ9SufdKukdCupM6ut8yuysqu3ceu8MWu/8KtAO+wAO+wAO6wAO6wAO2wAOywG+uwROmxYOixd+axjOSxoeKxtOCyyN6y29yx79mx/9awAP+zAP+zAP+zAP+zAP+zAP+zDP6zOP2zWPu0cfq0h/i0nfa0svW0xvK02vC07e60/+uyAP+2AP+2AP+2AP+2Av+2Ef+2M/+2T/+2Zf+3ev+3jv+3of+3tf+3x/+22v+27f+1/v6zAACIAgCIBwCJEQCJIQCKOACKTgCLYgCLdQCLiACMmwCMrQCMvgCLzgCL3wCL8QCK/wCJAAKOAgKOBwGPEAGPIACPNwCQTgCQYQCRdACRhwCRmwCRrQCRvQCRzQCR3gCQ8ACQ/wCPAQeSAweSBwaSEQWTIQOTOAGTTgCUYgCUdACUhwCVmwCVrQCVvQCVzQCV3gCU8ACT/wCTAxOVBRKWCRGWEQ+WIw2XOgmXUAaXYwOYdQGYiACYmwCYrQCYvgCYzgCY3wCY8QCX/wCWBieZByaZDCaZFCSaJCGaPB6bURqbZBabdhOciQ+cnAucrgecvwSczgKc4ACc8QCb/wCaBkGdB0GdC0CdEz6dJDueOjieUDWfZDKfdi+fiiufnSigriSgvyCgzxyg4Bef8hKf/wyeAVigAlihBVehC1WhGVKhN1CiTk2iYkqidkejiUSjnUGjrj2jvzqjzzaj4DKj8i6j/ymiAG2kAGykAGukAGmlFmelM2WlTGKmYWCmdV2miVqmnFenrlSnv1Gnz06n4Eqn8kem/0KmAICoAICoAH+oAX2oD3yoLHqpSHepXnWpc3KqiHCqm22qrWqqvmeqzmSq4GGq8l2q/1mpAJSrAJSrAJOrAJKrB5CsJY+sQo2sWoutcYithoatmYOuq4CuvX6uznuu4Hiu8nSt/3CtAKevAKevAKavAKWvAKSvGaKvPKCwV56wb5ywhJmxmJexqpWxu5KxzZCx342x8omx/4awALiyALiyALiyALezALazBbSzNbOzVLG0ba+0gqy0lqq1qKi1uqW1zKO136C18p21/5q0AMq2AMq2AMq2AMm2AMi2Bse3LsW3UcO3asG3gL+4lL24pru4ubm4y7a53rO48bC4/664ANy5ANy5ANu6ANu6ANq6ANm6I9e6S9a7ZdS7fNK7kNC8o868t8y8ysq83ce88MW8/8K7AO+9AO+9AO+9AO69AO29AOy+Fuu+Qem+Xui+dua/jOW/oOO/tOC/yN6/3Ny/79m//9a+AP/AAP/AAP/AAP/AAP/BAP/BCf7BNP3BVvzCb/rChvnCnPfCsfXDxvPD2vDD7u7C/+vBAP/EAP/EAP/EAP/EAP/EDv/EL//ETf/FZP/Fef/Fjf/Fof/Ftf/Fx//F2v/F7f/E/v/DAACVAgCVBwCWDwCWHwCXNgCXTQCXYQCYdACYhwCYmwCYrQCZvgCYzgCY3wCY8QCX/wCWAAGaAQGbBgGbDwCcHgCcNQCdTACdYQCdcwCehwCemgCerACevQCezQCe3wCe8QCd/wCcAQaeAgaeBgWfDwSfHwKfNwGgTQCgYQChdAChhwChmgChrACivQCizQCh3wCh8QCh/wCgAhGiBBCiBw+iEA6jIQujOAejTgSkYgKkdQGkiAClmwClrQClvgClzgCl3wCl8QCk/wCkBSSlBiSmCyOmEyGmIh6nOhunUBenYxOodhCoiQyonAeorgSpvgKpzgGp4ACp8gCo/wCnBT+pBj+pCj6pETyqIzmqOTaqTzOrYzCrdiysiSmsnCWsriGsvx2szxms4BSs8g+s/wmrAVetAlatBVWtClStGVGuNU6uTUuuYkivdUWviUKvnD+wrjuwvziwzzSw4TCw8yyw/yavAGywAGuwAGqxAGixEWaxMWSxSmGyYF+ydFyziFmznFazrlOzv1C0z0y04Um080Wz/0GzAIC0AH+0AH60AH20Dnu1KXm1R3e1XXS2c3K2h2+2m2y3rWm3vma3z2O34V+381y3/1i3AJS4AJO4AJO4AJG4ApC4Io65QIy5WYq5cIe6hoW6mYK6q4C7vX27znq74He783O7/2+6AKe7AKa7AKa8AKW8AKO8E6K8OaC9VZ69bpu9hJm+l5e+qZS+u5K+zY+/34y/8om//4W+ALi/ALi/ALe/ALfAALXAArTAMrLAUrDBbK7BgqzBlarCqKfCuqXCzKLC36DC8p3C/5nCAMrDAMrDAMnDAMnDAMjEBMbEKsXETsPEacHFf7/Fk73FprvGubjGy7bG3rPG8rDG/63GANzHANzHANzHANvHANrHANnHHNfISNbIZNTIe9LJj9DJo87JtszKysrK3cfK8cTK/8HJAPDKAO/KAO/KAO7KAO7LAOzLD+vLPurLXOjMdebMi+XMn+PNtOHNyN7N3NzN79nN/9bMAP/OAP/OAP/OAP/OAP/OAP/OBv/PLv3PU/zPbvvPhfnQm/fQsfXQxvPR2vHR7u7Q/+zPAP/SAP/SAP/SAP/SAP/SDP/SKf/SSv/TYv/TeP/TjP/ToP/TtP/Tx//T2//T7v/T///SAAChAQCiBQCiDgCiHQCjNACjSwCkYACkdACkhwClmwClrQClvgClzgCl3wCl8QCk/wCkAAGnAQGnBQCnDQCoHACoMwCpSgCpXwCqcwCqhgCqmgCqrACrvQCrzQCr3wCr8QCq/wCpAQWqAgWqBQSrDgOrHAKsNAGsSwCsYACtcwCthgCumgCurACuvQCuzQCu3wCu8QCu/wCtAg+uAw6uBg2uDgyvHgqvNgawTAOwYQGwdACxhwCxmgCxrQCxvgCyzgCy3wCy8QCx/wCxBCGyBSCyCR+yEB6yHxyzOBizThS0YhC0dQy0iAi1nAW1rQK1vgG1zgC14AC18gC1/wC1BDy1BTy1CDu2EDq2ITe2NzO3TTC3Yi24dSm4iSW4nCG5rh25vxq5zxW54RC58wu5/wW4AlW5A1S5BFS5CFK6GE+6Mky6S0m7YUa7dUO8iEC8nDy8rjm9vzW9zzK94S298ym9/yO8AGu9AGq9AGm9AGe9CWW+LmO+SGC+X12/c1q/iFfAm1TArVHAv07Bz0vB4UfB80PB/z/AAH/BAH/BAH3BAHzBDHrBJXjCRHXCXHPDcnDDh23DmmrErGjEvmXEz2HF4V7F81rF/1bEAJPEAJPFAJLFAJHFAI/FH43GPovGWInGb4fHhYTHmYHHq37IvXzIznnI4HXI83LI/27IAKbIAKbJAKXJAKTJAKPJDKHJNp/KVJ3KbJvLg5jLl5bLqZPMu5HMzY7M4IvM84jM/4TMALjMALjNALfNALbNALXNAbTNLrLOULDOaq7OgavPlanPp6fQuqTQzKLQ35/Q8pzQ/5jQAMrQAMrQAMnQAMnQAMjRA8bRI8XRS8PSZ8HSfr/SkrzTpbrTuLjTy7XU3rLU8q/U/6zUANzUANzUANzUANvUANrVANnVEdfVRNbVYdTWedLWjtDXos7XtszXysnY3cfY8cTY/8HXAPDYAPDYAO/YAO/YAO7YAO3ZB+vZOOrZWejac+baieXan+Pbs+Dbx97b3Nzc8Nnc/9bbAP/cAP/cAP/cAP/cAP/cAP/cAv/dJf7dT/zda/veg/nemvfesPXfxfPf2vHf7u7f/+zfAP/gAP/gAP/gAP/gAP/gB//hHv/hRf/hX//hdf/ii//in//is//ix//j2//j7v/i///iAACtAQCuBACuDACvGgCvMQCwSQCwXwCxcwCxhgCxmgCxrACyvgCyzgCy3wCy8gCx/wCxAAGzAQCzBAC0DAC0GQC1MAC1SAC1XgC2cgC2hQC3mQC3rAC3vQC3zQC33wC38QC3/wC3AAS2AQS3BAO3DAK3GQG4MQC4SAC5XgC5cgC6hQC6mQC6rAC7vQC7zQC73wC78QC7/wC6AQ26Agy6BQu7DAq7Gwi7MwS8SgK8XwG9cwC9hgC+mgC+rAC+vQC+zgC/3wC/8QC+/wC+Ax2+BB2+Bxy+Dhq/HBi/NRTATBDAYQzBdAjBhwXBmwLCrQHCvgDCzgDC4ADC8gDC/wDCAznCBDnCBzjCDjbDHzTDNDDDSyzEYSnEdCXFiCHFnB3FrhnGvxXGzxDG4QvG8wbG/wLGAlLGAlLGA1HGB1DGF03HL0rHSUfIYEPIdEDIiD3JmznJrjbKvzLKzy7K4SrK8yXK/x/KAGnKAGnKAGjKAGbKAmTLKmHLRl7LXVvMcljMh1XNm1LNrU/OvkzOz0jO4UXO80HO/zzOAH7OAH3OAHzOAHvOCHnPIXfPQnTPWnHQcG/QhmzRmmnRrGbRvmPSz1/S4VzS81jS/1TSAJPSAJLSAJHSAJDSAI7SGYzTO4rTVojUbYXUhILVmIDVqn3VvHrWznfW4HPW83DW/2zWAKbVAKbVAKXWAKTWAKLWBKDWMp7XUZzXaprYgZfYlpXYqZLZu5DZzY3a4Ira84ba/4LaALjZALjaALfaALbaALXaALPaKLHbTK/baK3cf6vclKjcp6bduaPdzKDe357e8pve/5feAMreAMreAMneAMjeAMfeAcbeGsTfR8LfZMDgfL7gkbzgpLnhuLfhy7Ti3rHi8q7i/6viANziANziANziANviANriANniBtfjP9XjXtTkd9LkjdDkoc7ltcvlycnm3cbm8cPm/8DmAPDmAPDmAO/mAO/mAO7mAO3mAOvnMOrnVejocOboiOToneLpsuDpx97q29vq8Njq/9bqAP/qAP/qAP/qAP/qAP/qAP/qAP/rGP7rSf3rZ/vsgfnsmPftrvXtxPPt2fHu7u7u/+vuAP/uAP/uAP/uAP/uAP/vAP/vEv/vPP/vWf/wcv/wiP/wnf/xsv/xxv/x2v/y7v/y///xAAC5AAC6AwC6CgC7GAC7LgC8RwC8XQC9cQC9hQC+mQC+rAC+vQC+zgC/4AC/8gC+/wC+AAC/AQDABADACgDAFgDBLADBRQDCXADCcADDhADDmADEqwDEvQDEzQDE3wDF8QDF/wDEAAPDAQPDAwLECgLEFwHFLQDFRQDGXADGcADGhADHmADHqwDIvQDIzQDI3wDI8QDI/wDIAQrHAgrHBAnHCgjIGAbILgPJRwHJXQDKcQDKhQDKmQDLqwDLvQDMzQDM3wDM8QDM/wDMAhnLAxnLBhjLDBfMGRTMMRDMSQzNXgjNcgXOhgPOmgHPrADPvQDPzgDQ4ADQ8gDQ/wDQAjTPBDTPBjPPDjLQHC/QMCzQSSjRXyTRcyDShxzSmxfTrRPTvg/TzwrU4AXU8wLU/wDUAU/TAk/TA07TB03TFkrUK0fURkPVXkDVcjzWhjnWmzXXrTHXvi7XzynY4SXY8yDY/xrYAGfXAGfXAGbXAGTXAmLYJF/YQ1zZW1nZcVbahlPamk/brEzbvknbz0Xc4UHc9D3c/zjcAH3bAHzbAHvbAHrbBHfcHXXcPnLdWG/db23ehGremWbfq2PfvWDfzl3g4Vng9FXg/1HgAJLfAJHfAJDfAI/fAI3fDYvgNojgU4bha4PhgoDil33iqnvjvHjjzXTj4HHk823k/2nkAKXjAKXjAKTjAKPjAKHjAp/kLZ3kTpvlaJjlgJbmlZPmqJHnuo7nzIvn4Ijo84To/4DoALfnALfnALbnALXnALTnALLoHrDoSK7pZazpfanqkqfqpqTruaLry5/r35zs85ns/5XsAMrrAMnrAMnrAMjrAMfsAMXsEMPsQcHtYL/teb3uj7vuo7jvt7bvyrPv3rDw8q3w/6rwANzvANzvANvvANrvANnwANjwAdfwNdXxWdPxdNHyi8/yn83ytMrzyMjz3cX08cL0/7/1APDzAPDzAO/zAO/zAO70AO30AOv0I+n1T+j1bOb1hOT2m+L2sN/3xt3329r48Nf4/9T5AP/3AP/3AP/3AP/3AP/3AP/4AP/4Cv74Pv35Yfv5fPn6lff6rPX7wvP72PD87u78/+v8AP/7AP/7AP/8AP/8AP/8AP/8Bv/8KP/8T//9av/9gv/9mP/+rv/+w///2P//7f//////\",everyday400:\"AAoJAwoJDgwKGw4JKg4IPA4IUwoHbQIEiQADowAAvQAA0gAA4wAA8gAA/xgA/ysA/z4AAA0KAw8KDhIJGxMJKxMIPRMHVBEGbgoEiQACowAAvQAA0AAA4gAA8Q8A/yEA/zAA/0EAABUMABYLCRkJHR0ILRsGQBsGVxoEcBcDihIApg0AvgsA0RAA4hcA8SEA/y4A/zkA/0gAACAPACAPBCMNFygJMSwDRSoDWScCcyYAkCUAqiYAwCcA0SkA4i0A8TQA/jwA/0UA/1IAAC0SAC0TAS4TDzMPJzoHS0QAZUIAfD0AkzoAqjsAvzwA0T0A4UEA70UA/UsA/1IA/1wAADwXAD4XAD4XCEIVIEwOQFYAcGcAiGIAnFoArFIAvlAAz1EA31MA7lcA/FsA/2AA/2gAAFEbAFYdAFYeAFcdGl4XN2gKXHMAjIIApH8AtHYAwm0AzmUA3mYA7GgA+2sA/28A/3YAAGgiAG0kAGskAGskFm8hMngZUYEIdosAoJYAuZYAx44A04UA330A6nkA+XsA/34A/4MAAH4sAIAtAH8tAH8uG4AuMoYpTY0hbJYSjZ4AsacAy6oA16IA4psA7ZMA+I0A/40A/5EAAI01AJE4AJE5C5A5JZA6OZQ4TpozZ6Asg6cioa4Mv7UA2roA5bQA8K0A+qgA/6IA/58AAJ5DBKFGFqFHJqFHNKBHQ6BIVKVFaKtBf7E8mLc0s70qzcMX58oA8cQA+78A/7oA/7UAKK9VMbBWNrBWPbBXR69XUq5YX7JYb7ZVgrxTlsBPrcZLxMtE2c878dYx+tIf/80D/8kASr9nTb9oUL9pVr9pXL5qZL5qbb5recJpiMZpmctnq89kvtNh0ddc49pW+OBS/91I/9k8Z8x8ac19a819bsx9c8x9ecx9gMx+ic5+lNF+odR9r9d8vtt7zd543OF17ORy/elv/+dqhNqShtuTh9qSidqTjNmTkdmTltmVnNqVo9uVrd6Vt+GUwuSUzuaS2eiR5uuP8uyM/++KoeappOerpOarpuerqOarquWrruass+atuOeuveitw+qty+yt0u2r2/Cr5fGq7vKn+PSlvfDBvvHCvvDCv/HCwPDCw/DDxfDEyPDFy/DGzvHF0vLF1vTF3PXF4PbD5/fD7fjC9PnBAQsNBAsNCwsNGgsNKQ4OOw4OUwsObQIMiAAKowAIvAADzwAA3wAA7wAA/RYA/ycA/zgAAA4NBBAODhEOGhIOKhMOPBMNUxEMbQwLhwEJowAGuwABzwAA3wAA7w4A/R8A/y4A/z0AABcQAxgQCxoPHBsNLRsNQBwNVxsMcRcKixMHpg0DvQwA0A8A4RgA7yEA/SwA/zgA/0QAACITAiMUByMTGCcQMCsMRCkKWicIdCcGkSYDqSYAvycA0CgA4S0A8DMA/TsA/0QA/08AAC8XAC8YBi8XEzMVKToQSkMEZEEBezsAlDoAqzsAvzwA0D4A4EAA70UA/UsA/1IA/1sAAD4bAEAcAUAcD0MbI0wWQVYKcGYAh2AAm1kAq1AAvlAAz1EA31MA7lYA/FsA/2AA/2cAAFMgAFgiAFgiC1ciH14eOmgVXXMAjIIAo30As3QAwGwAzWMA3mYA7GgA+msA/28A/3YAAGkmAG0oAG0pAmwpHG8nNXghU4EWeIsAoZYAuZYAxo0A0oQA3nwA63kA+XsA/34A/4MAAH8wAIEyAIEyA4AyIYAzNocvUI4obZYdjp4FsacAyqkA1qEA4pkA7ZMA+I0A/4wA/5AAAI45AJM+AJI9FpE9KpE+PJQ9UZo5aaEzhKgqoq8cv7UA2boA5LMA760A+qgA/6IA/54AAJ9HE6JKHqJLLKJLOaFMRqBMVqZKaqxGgLFCmbc7s70yzcIk58oN8cQA+74A/7kA/7UALrBYNrJbO7FbQrFbS7BbVbBbYbFbcLZZg7xXl8FUrcZQxMtK2tBC8dc6+tIq/80T/8kATL9qUcBsVMBsWL9sX79sZr5tb75ue8JticZsmcpqrM9ov9Nl0ddg5Npb+OBX/91N/9lBac1+a81/bc1/cc1/dcx/e8yAgcyAis6BldGBodR/r9d/vtt9zd573OF47ON0/ely/+ZuhdmTh9qUiNqUi9qVjtqVk9qWmNqXndqXpNyXrt6WuOGWw+SWzuaU2uiT5uqR8uyO/++No+arpeespeaspuasqeasrOatsOaus+avuOevvuivxOquy+yt0+6t3PCs5PGq7fKo9/OmvfDCvvDCvvDCwPDDwvHDw/DExvHFyPDFy/DGzvHG0vLG1/TG3PXF4PbE5/fE7fjD9PnBAgsUBgsUDAwUFw0UKA0VOwoUUwgVbgUXiQAVpAAUvAARzwAO3wAK7wEF/RkA/yoA/zsAAA8VBBAVDBIVGRMWKhIWPRIWVRMXbg0WiQIVpAATuwAQzgAN3wAI7xEC/CAA/y8A/z8AABgVAhkXDRoXHBsXLRwXQRwXWBsWchkVjRMTqBASvQ4OzxIK4BkF7yMA/S0A/zkA/0YAASQaByUbDyUbGygZMSoWRCgVXCgUdycTkicQqiYNvicJ0CkE4S4A7zUA/j0A/0UA/1EAAzMfBDIeDzIfGDQeKzoaS0EUZD4QezoNlToJrDsFvzwA0D4A4EEA70YA/UsA/1IA/1wAAEEiAEMjCkQjGUUkKE0gRVcZcGQIhl0BmVUAq08AvlAAz1EA31MA7lcA+1sA/2EA/2gAAFcnAFspAFspFVkqJV8oPWgiX3MXjYEAonsAsnIAv2kAzWQA3mYA7WgA+2sA/28A/3UAAGwuAHAwAHAxEW8xJnAxOngsVoEleYsXopcAt5MAxYsA0YIA3XoA63gA+XsA/34A/4MAAIE4AIM5AIM6FYI6KYI7O4Y4Uo4zb5Yrj54fsqcDyacA1aAA4ZgA7JEA94sA/40A/5EAAJBBAJRED5RFIpNFMpNFQZNFVJpCa6A9hac2oq4twLUc2bkA5LIA76wA+aYA/6EA/54AEKBNIaNRKaNRM6JRP6JSS6FSWqZRbKtOgbFKmrdFtL0+zcM05skn8MMP+r4A/7gA/7UANbBdPLJgQLJfR7FfT7FgWbFhY7FgcrZfhLxdmMFbrsZXxMtS2tBM8NZG+tE4/80n/8kSUb9uVMBwWMBwXMBwYsBxab9xcr5yfMFxi8Zxm8pvrM5tv9Nq0ddn49pi+OBf/91W/9lLa8yAbs6Cb82Cc82Cd82DfMyChMyEi82EltGEotSDsNeCvtuBzd1/3OF96+N6/Oh4/+d0htqVidqWitqWjNuXkNqXlNqYmdqYntmZptyZrt6ZuOGYw+OXzuaX2uiW5uqU8uyR/++Qo+aspeatpuatp+atqeatreausOautOavueewvuiwxOqwy+yv0+6v2++t5PGs7fKq9/OovvHDv/HDv/DDwPHEwvDEw/DExfDEyfDGzPDHzvHG0/PH1/TH3PXG4fbG5vbE7fjE9PjCAgwfBgwfDw0eGQ8eJg8eOQ8gUwghbwAgigAfpwAjvQAizwAh4AAe7wwc/R8Z/y4X/z4XABAfBBEgEBMgGhQgJxUgPBUhVQ8hcAEfjAAhpgAjvgAizwAg4Age7xgb/SUY/zIV/0IWABkiABkiDxwjHB0iKx4jQRwjWhkicxokkRgkqRQjvxQh0Rgf4R8c8Cga/jEX/zsU/0kUACYiAScjDyckHiklMSokRiokXiojeiojlSkjrCkhwCoe0Swc4TEa8DcX/j8U/0gQ/1MRAjQmCzUnFzYoIDYpLzsnTD4jZDshfzwglzwfrT0cwD4a0UAY4UMU8EgR/U4N/1QJ/14LBEUtEEkvFkovIkovL04uSVgqb2AjhlodmFEYrVEVv1ES0FMP4FUK71gG+1wB/2EA/2oAAFw0AWA2EF81IV82L2A2QmkyY3MsjX8foHgWsG8LvmUEz2UA32cA7WkA+20A/3AA/3cAAHA5AHQ9DHQ8IHM9MHI9QXg7WoI3e4ovpJchtpESxIgB0IAA3HgA7HoA+XwA/38A/4QAAIVEAYdGFIZGJYZGNYVGQ4ZFV45CcpY9kJ41s6cpyKYa1J4D4JYA7I8A94sA/40A/5EABJJLGpZPJJZPMJZQPZVQSpRQWZpOb6FLh6dGo65AwbU32Lgq47EX7qsA+aUA/58A/54AJqNXMKZbNqVbPqRbSKRcU6NcX6VbcKtZhLFWm7dStL1OzsNH5cg/78Mw+b0d/7cA/7QAP7JlRbRnSbNoT7JoVrJoX7JpabFpdrZoh7xnmsFlrsZjxMtf2s9a8NVW+dFK/8w7/8gtWMB1XMF3XsF3YsB3aMB3bsB4dr94gMF4jcV3nMp2rc51v9Nz0tZx5Npt9+Br/9xh/9lXb82Gcs6HdM6Hd86He86HgM2IhsyIjs2Jl9CIpNSJsdeIv9uHzt6G3eGE6+OB/emB/+Z8itqZjNuajduaj9qaktubltqbm9qboNqcp9ydsN6dueGcw+Ocz+ab2+mb5+uZ8uyX//CXpeaup+ewqOevqeewq+ewruawseaxteayuuayv+izxeqyy+uy0+2x3O+x5PGv7vKu9/OsvvDDv/DEwPDEwfDFwvDFxPDGxvDGyvHHzPDIz/HI0/LI1/TI3PXI4vbI5/fG7vjG9PjEAAwuBgwtEA0sHRAsKREsOBIsUA8vbAQxigAxpwAvvgAs0AAw4As08Bwz/iky/zYx/0QzABEtBBMuERQuHRYtKxctOhcuURUwbQwxiwAxqAAvvgAu0AEz4Bg07yMz/S0x/zgw/0cyABkwARsxEBwxIB4wLh8wPiAxVh8zcxgzkggyqwExvxQ10SE14Scz8C8y/Tcx/0Ew/04yACYyACczByc0ICk0MSw0Qyw0Xys1fCc1lyo2rS83wC810TI04jYy8D0x/UQw/0wv/1cwADYyADczDTg0IDo1ND04TT44Zz84gUA3mUA2rkA0wEEy0UMx4Ucv8Esu/VAs/1cs/2EuAEg3C0w6G048LFA/OVFATVk+bls5g1MzmVMxrlMwwFMu0FUt4Fgr7lsq+18o/2Uo/2wrBV9BFmRFJGRGL2ZHOmVGSWlGZ3RCi3s7nnIyrmkrv2Yo0Gcm32kl7Wsk+24i/3Ii/3gmGXVMJXlQKHlPL3lNPHhNSnhNX4JKfotGpJU/tI00woQqz3sh3Xkc7Hsa+n0a/4Aa/4UfH4lUJItWK4tVNYpVQYlVTohVXo5TdZZQkp1MtahFxqM605su35Mh6owU+IwN/44J/5IPJJZZMJpdNppePpleSZheVJheYJldcqBbiadYpK5UwrVO1rZF4rA57akq+KMZ/50B/58CN6VkP6dnRKdnS6dnU6ZnXKVnZqVodKpmh7FknLditb1fzsNb5MdU7sFI+bw6/7Yp/7MaS7RvULVyU7VyWLRyX7RyZ7NzcLNze7Zzibtym8BxsMZwxcpt2s9q7tVn+NBb/8xP/8dCYMJ+Y8J/ZcJ/acJ/bsF/dMF/fMGAhMGAkMWAnsp/rs5/wNJ90tZ849p59uB4/9xv/9hlds6MeM+Oec+NfM6NgM6Ohc6Oi86Pks2PmtCPpdOPsteOwNqOzt6O3eGM7OSL/OmL/+eFjdudj9uekdufk9ufltufmdufndqgo9qgqduhsd6huuChxOOhz+ah2+ig5uqf8+2e//Cep+axqOeyquezq+ezreezr+azs+a0tua0u+e1wOi2xuq2zey11O613O+05fCz7vKy9/Owv/DFwPHGwPDGwvHHw/DGxfHIx/DHyvHJzfHJ0PHK0/LK2PPK3PTJ4fbJ5/bI7fjI9PnHAA0/AQ0/Dw4/HxA/LxM/PxRATxI/agxCiABHpQBHvABF0ABB4wA+8QBD/StJ/0BM/01OABM/ABNBEBVBIBZBMBlBQRpCUBhAaxVEiQpHpQBHvQBF0ABB4gBA8RVF/TZK/0NM/1BNABxEABxFDx1EIh9FMyFERCJFVSJEcSBIjhpKqQxJvwBG0gBC4xdF8DNK/UJN/0pM/1ZNACVGACZIBShJISpINS1IRy9IXTBKeTFNlCxNrSZKwiFG0i5J4T5N70ZN/UxM/1NL/15OADdIADhJADhKGDlMNz5OTkJPZURRgEJRmkBOr0JNwUlP0UxO4U9M71NL/FhK/11J/2ZLAExJAFBNAVFOIVRQOlZTUllVbVpUhVlSm1lQr1lOwFlM0VtL4F1J7mBI+2RG/2lG/3BJAGJPD2dUIWlVM2pXRmxaVWxbanNZiHRTm2xLr2tJwGpH0GtG32xE7W9D+nJD/3VC/3xGFnhZJXxdMH1eQH5hTX9iV31gZYFfgItdopFYsohOv39EzntA3nw/7H4++YA+/4M+/4hCM41lO49oQ49pTI9qUI9nW41nZo1meZZllJ1itqdfxKBU0ZhJ3ZA+6ow4+I43/483/5M6RZpuS55xTJ5wTp1uV51uYJtuaZpteKBsjKdrpa1oxLZm1LRd4K1R7KZF96A5/50y/6A0S6hzUKt1U6t1WKp1X6l1Zql1b6h1eqp1irB0n7Zztrxxz8Nw4sVp7b9d97pR/7RD/7A4WLZ8Xbh+X7d+ZLd+abd+cLZ/eLV+gLV/jbp/nsB/scV+xct92s977dR49s9t/8ph/8ZWacOHbMSJbsOIcsOJdsOJfMOJgsKJicKJk8SJocmKsM6JwdKJ0taI49qH9d+G/tt9/9h0fM+Uf9CWgM+Vg9CWhtCWi8+WkM+Wls+Xnc+WqNOXtNaXwdqXz96X3eGW7OSW++mW/+aQktujlNykldykl9ykmtylndulodulptulrNums92nvOCnxeKn0OWn2+in5+un8uym//Cmque1q+e2rOe2rue2sOe3sue3tee3uee4vOe4wee5x+m5zuy51e253e+55fG47vK39/O2wPDHwvHIwvHIw/HJxPDJxvHJyfHKy/HLzvDL0fHM1fLM2PPM3fXM4/bM6PfM7vjK9PjKAA5XAA9YCA9YHxBXMhJXRRVXWBRYahBXhgdcoQBeuABezQBd4ABa8QBX/wBU/yhZ/0piABRXABVZCRZZIRdZNBhYRxtYWRpYaxhYhxNdoQ9fuQJfzQBd3wBa8ABX/wJU/zNa/1FjAB1bAB5dCB5dIh9cNiFcSiRbXSVccCNdjCJgpCJhuxthzxVf4RJc8hZZ/ypZ/0Vf/11pACZdAChgASpgIStgOC1gTzJhZDRjdjRikTVkqDRkvTBi0S5g4y5d8jRb/Uhg/1ll/2dqADZhADZiADhkFjpmOj9mVERnaEhnfEhnlEloq0dnwEVk0kNh4klg71Zk+mFn/2Zm/25pAE1iAFFnAFJoDFNqMVRrVFlsa11sgF1smFxrrlpnwFtmz2No32Zo7Whm+mtk/29j/3dnAGVjAGtpAGtpIW1qPm9sVnFubnNwhXJumnFsrXFpv3Fnz3Jm3nNk7HVi+Xdh/3th/4FkDHpoIX9tL4FuP4FvUYJxY4N0b4N0g4pznYptrYJkvoBizoBg3YFf64Je+IRd/4Zd/4tgM49yO5F1RJJ2UJJ3XZJ5apN7cZF5fpV4lp13sqJ0wZtqzZJf245Z6o9Y95BY/5JY/5ZaSJx5UqB+WKB+YaGAaqGBbKB+dZ5+fp99j6Z9p618xbZ70bFx3qpn6qNb9Z1S/59S/6JVXquDZa2HaK2HbK6HbK2EcqyDequEgaqDj7CDobaDt7yC0cSC4MN7671w9rdl/7FZ/65RbLmMb7qOb7qNcLqLdLmLermLgbiLiLeLkrqLocCLs8WLxsqL29CL7NKH9s19/8hx/8RndcWTd8aTeMaTe8WTf8WThMWTisSUkMOTmMSTo8mUss2UwtKV09aV5NqU9N6T/dqK/9eBhNGdhtKdh9GeitGejdGekdCeltCem9Ceoc+eqtKftdafwtqg0N6g3eGg6+Og++ih/+Wbltypmd2rmt2rnN2rntyrodyrpdyrqdyrr9ustd2tveCtx+Kt0OWu2+iu5+qu8uyu//CvrOe5rue6r+i6sOi7sue6tOe6t+e7uue7vue8w+e9yOm9zuu91u2+3e+95vG97/K89/O7wvHKwvDKw/HLxPDLxfHLx/HMyvHMzPHNz/HN0vHO1fLP2fPP3vXP4vXO6PfP7vjO9fnNAA9xAA9yAA9xHRFyNRJyShNxXxdxdRRziQt0ngp0tQt0yRN23A917RN0/Bxx/ylw/zpwABZxABVyABZzHhdyNhhySxpxYB1xdhx0iRd0nhd0tRl1yR523Bx17R5z/CVx/y5v/z9wAB5zAB51AB92ISB1OCF1TiR1ZSh2eih4jCZ2oSd2typ4yy143Sx37S50/TNy/ztw/0tyACl2ACp4ACt4ICx4PC96VDJ7ajd7fjl7jjh5pDl6uT16zD563j547kB2/UN0/0lx/114ADZ6ADd8ADl9HT5+PUB+V0V/bkp+gU1+kUx8p059ulF9zVB831B57lF3/FN0/192/3B+AEx7AE+AAE+BAFCBM1OCV1mCcF6Cg2GBk2F/qGOAvGN/zmJ932F67mN4+Wx6/3d9/4CCAGZ7AGqAAGuBAmyCMm2CTm2Eb3KFhHaElneEq3aCvnWAz3V+3XqA6n6A94B+/4J9/4h/AHx7AIKAA4KAKoOBRISCW4aEcYeFhomHmoiFrIiDvYeBzYd/3Id96oh79op7/4x6/5F8LZKBOZSEQ5SEUJWFXpaGbZaIfJeKh5aKmJyKrpyFvJV8zJN525N46ZN29ZV2/5d1/5p2SZ6FVKOKWaOKYqOLbKOMeKSOg6SQiaKNk6WNqK2NwrKLz6yC26V556Bx9KBv/6Fv/6VyYK2NZ7CRa7CRcbCSebCTgrCVha+Si66Rk66RpLWSuLyS0sWT3sCL6bqA9LR1/61q/7Bsc7uXeLyZeryZf7yahbybhryZiruYkbqXl7mXpL+YtMWZx8qZ3NGa6tCV9MuL/saA/8F2hcehiMeiiceiisihisefjceekcael8aensWdp8ietM2fw9Kg09ah5duh892f/NmV/9aNkNKokdOoktOoktOnlNKml9KnnNKnoNGmptGmrdGnt9aow9qp0d6q3uGr7OSr+ueq/+Wknd6xn96yn96xod6xo92xpt2xqd2yrd2ystyyt9yzv9+zyOK00uW13Oi15+q28uy2/vC3sOi+sem/sui/s+i/tei/t+i/uujAvejAwOfAxefByejB0OvC1+3C3u/C5vDC7vLC9/PBxPHNxPHOxfHOxfHNx/HOyPHOy/HPzfHP0PHQ0/HR1vLS2vPS3vTS4/bS6PbR7vfR9fnRAA+JABCJABCJHBCJNxGJTxKJZhWJfByKkBmMohGMsheIxh6J2CqK6TGL+TeK/z+J/0yLABaHABaJABaJHheJOBeJTxmIZxyKfSOLkSGMohyLsiGIxieK2DGK6TeL+DuK/0OJ/1CLAB+KAB+MACCMISCLPCKMVCSNayiNgS+Oky6OpCuNtC+LyDaM2T6M6kGM+EWL/0yK/1eMACqLACyPAC2PJS6QQDCRWDORbziRgz6QlT+QpT6PtkGNyUeO2k2P6k+O+VKN/1eL/2KNADiRADyTAD6TJECTQkKUW0WTckqThlCSl1KSpVGPt1SPyVmQ2l2Q6l6P+GCN/2OM/2uNAEmQAE2VAE6VAU+VPlaWWlmVcl2Vh2SUmGaUpWaRuGmSyWyS2myR6myQ+G2O/3CM/3iOAGOQAGeUAGeVAGeVKmeWUWuXcXKXh3eWmHqWp3qUuXyVynyU23uS6nuQ+HuO/4KQ/46VAHyPAICUAICUFIGVOIKVUoKWaoOXh4iYmYuYqIyXu4uWzIqV24qS6I6T9JKU/5ST/5iUApKREpSTKJWTPJaUUJaVY5eWdpiXiZqYmpuaq5uYu5qWy5mU2ZmT55qR85uQ/5yP/5+PRKGST6WWV6WWYaWXbaaYeaaZhqebkqecmqacqqycvq2Yy6WQ2aSN56SM8qSK/qWK/6iLYq+ZabKbbbKcc7KcerKdhLKejrKgmLOhnbKfp7Wfuryg0MKg27yX5rSN8q+F/rCE/7KGdryger6ifb6jgr6jiL6kj76kl76mmr6koL2jqL6jtsSlyMqm3tKo6M6g8siX/MKM/72Dh8mpismpjMmqkMmqlMmrmsmsnMmqn8ippcioqseotsypxNGq1das5tyu8tup+teg/9OXl9OxmdSym9SyntSzoNSzodSxotSvp9OvrNKvsdKvudSwxdmx0d2z3+G07OS1+eaz/+OtqN67qd68qd67qd66qt+6qt64rt65st65tt25u925wN65yeK70+W83ei96Ou+8+y+/vC/tejEt+nGt+nFt+nFt+jEuunFvOjFwOjFw+jFx+jGy+jG0erH1+3H3u7I5/HI7/LI9/TIxfHQxvLRx/LRyPLRyPHRyvLSzPHSzvHS0fLT1PLU1/LU2/PV3/TV5PXV6ffW7/jV9fjVABCdABCdABCeIxCdPRCdVBGdbBSeghuflSaepiOetx+fwyea1jGb5j6d9Eue/1Kf/12gABabABedABedJBedPhidVhqebRyfgiOflSyepiuetyeewy+a1Tic5UOd806e/1Se/2ChACCdACCeBCGfKSKfQiOgWiWgcCmhhS+glzigqDeguDWgxTuc1kOd5k2e9Feg/1yg/2eiAC2gAC6jDi+jLjCjRjKjXTSjczijiD+imUehqUeiukaixkue11Kf5lqg9GGg/2Wg/2+jAD6lAD+lDEClL0KlSEOlX0aldUqkiVCkmlijqlmjuFiixl2g12Oh5mqi82yi/2+h/3eiAEmiAEymAE6mKVWmRlemX1qmdV2liWOkm2qkqmykt2uixm+i1nOj5Xej83ij/3uh/4GjAF6hAGKlAGKlAGSmNWamXG2mdHGminamnHylqn6ltn6jx4Ck1oSl5oWl84Wj/4ai/4ujAHihAHylAH2lAX2lMX2lTn2mbIGniIennIumq4+mt4+mx5Gm15Gm5pGl85Gj/5Gi/5ejAJGhAJOkDpOkLpOkRpSkW5Slb5Wmgpanm5qoq56ouZ6oyZ6o2Z2m552k8p+l/KOm/6amKqGgOaWjQ6WkT6WkXqakbaalfaemjainnKmorKuqvKupyqqn2Kqm5aqk8aqj/Kuh/66iXLGkY7OmaLOmcLSnebSohLWpj7WqmrWrpbasrbasvLyuzbyq2LWi5bOf8LOd/LSd/7adeL+qfMCsf8CshMCticCtkcCumcCvosGwq8Gxr8CvucOwycqy3M+y5sqq8MOh+76Y/7+ZicqxjMuyjsuykcuylsuynMuzosu0qsu1rcu0ssqzucuzxtG11da359258Nmy+dSp/8+gmdW4m9W5ndW5oNW5pNW6qNW6rda7r9W6stW5t9S5vNO4xti60t284OG+7ubA+OW8/+K2qd/BrODCreDCr+DCsd/DteDDtuDCt9/Bu9/Bv97Aw97AyuDC1OTD3ujF6OrG8+3H/e7Gu+nMvOnMvenMvunMv+nMwOnLwOnKw+nKxunKyenLzenL0unM2OzN4O/N5/DO7/LO+PTPyvLWy/LWy/LWy/LWy/LWzPLVzfHV0PLV0/LW1fLX2fLY3PLZ4PTZ5fXZ6vfZ7/fZ9fnZABGvABGvEhCvLhGvRhGwXRKwcxaxhx2wmiewqzOvujKvyjKv0zir40St8VGu/WCv/22yABetABivFhivMRiwSBmwXhqwcx6whySwmi2wqzqvujmvyTiv0z6r40ms8VWu/WKv/2+yACKwAiKyHyOyNiOyTCWyYSeydiuyiTCxnDmxrESxu0SwykOx1Ems5FOu8V2v/mqw/3WzAC6xDTC0JTG0OjK0TzS0ZDa0eDqzi0CznUiyrVKxvFKxylKy1Feu416v8Wmw/HOy/3y0AEC0DEG1JUK1O0O1UEW1ZUe0eky0jVGznlizrmKyvGKyymKy1Wav422w8HWx/Hyz/4O0AE2yAFK1H1S1OFa1T1i1ZFq0eV60jWO0nmqzrnKzvHOyyXKz1Haw4nyy74Ky+4a0/4y1AF2yAGC1AGK1GmO1SGq1Ym21eHG0jHW0nnu0roK0u4S0x4Oz1Iay4ouz7o+1+5C0/5W2AHOxAHe0AHe0AHe0Mnm0VHy0dYK1i4a1nou0rpC0u5O1xZOz1JW04pm175q1+5q0/521AI2xAI+yAI+yIo+zPZCzVI+zaZC0g5O1nJm1rp21u6G2xqG21KO24qS276O1+6O0/6WzD6GvKKOyNKOyQ6OyU6SyZKSzdaWzhaW0laa1raq3vK63yK+41q+45K628K20+rC1/7W3SrKxUrOyWLSyYLSza7Szd7WzhbW0k7a1obe2r7m4vbu6yrq417q347q17rm0+bqz/7yzccC0dsK1esG1f8G2h8K3kMK3msO4pMO5rsS6t8S8vsS8y8q+2sq748Oy7sGv+cGu/8Ovi826js27kM27k827mc28ns28pM28rM29tM6+vM7Av82+yNC/1tbB5trC7tW6+dCy/8yqm9a/ndfAn9fAodfBpdfBqtfCr9fCtdfDu9fEvtfCwtbCyNfC09zF4OHH7+fJ9+TD/+C9q+DHreDHruDIsOHIs+HJt+HJuuDJv+DKwuDJxODIyODIzd/I1eTK3ufM6evO9O7P/O7NvOnQvenQvurQv+rRwenRxOrSx+rSyerSyerQzOrR0OrR1OrR2uvS4e7T6PDU8PLU+fTVzPLazPLazfLazvLa0PLb0fLb0fLb0vLa1PLa1/La2vPb3vLc4fPd5fXd6vbd8Pjd9fjdDRLAHRLALRLAQBPBUxPBZxXBehrAjSHAnyu/rzi/vkW/zEW+2kW/4Uu671i8+2W9/3a/Cxm+IBnBMBrBQRvBVBzBZx7BeiHAjSjAnzG/rz2/vkq+y0m+2Uq/4U+671u7+2i8/3jAFiPAJyXDNSXCRSbCVyfCairCfC3BjzTBoDzAsEe/v1O/zFO/2VO/4Vi772K8+26+/37BHDHBLDPDODPDSDXDWTbDbDnDfj3CkELBokrBsVTAwF/AzV/A2V/A4WO8722++ne+/4XBIUPDLEPEOETDSUbDWkfDbUrCf07CkVPBolrBsmPBv23AzG3A2W3A4XG97ni++oG//43DDlHBJ1XDNVbDR1fDWVnDbFzCf2DCkWXComvBsnPBv3vAy3zA2HvB4H6+7YW/+IzB/5bDAF3AAGHDFWPDPmnDVmzDam7DfXLCkHbCoXvCsYLBv4nBy4rB1orB4I3A7ZLB95jC/5/EAHC/AHTCAHTBHHbCQHfCYX3CeoLCjobCoIvCsZDBv5bBy5jC1ZfB4JvB7J/C9qPD/6bEAIm/AIvAAIrAFIrANorBUYvBbY7BiZTCnpjCsJzCv6LCyqXD06TB4KfC66rE9qvD/63EAJ6+FKC/JqC/OaDASqDAXKDAbaDAfqDBlqPCrqnDvq7DyrHE1LLD4LPE67TF97XE/7bEPbG+RLK/S7K/VbK/YbO/brPAfLPAirTBmbTCqLXDvbnFy73G1b7G4r7G7b3E+L3D/8DEYsG/Z8HAa8LAccLAecLBg8LBjsPCmsPCpsTDs8XEwMfGzMnI18jG4sfF7cfE98fD/8jCg87Chs7Dic7Djc7Dk87Em8/Eo8/Frc/GttDHvtDIxtHJzNDJ19XL5NbJ7NDB9869/8++ntjHoNjIodjIpNjJp9nJrNjJsdnKt9nKvdnLxNnMytnNzdjL1dvN4eHQ7uXR9uDK/93DruHNr+LOsOLOsuLOtOHPuOLPvOLPwOLQxuLRy+LSzuLR0eHQ1+LQ4OfT6urV9e7X/OzTverVv+vWv+rVwOrWwurWxevWyOvXy+rXz+vY0uvY1OvX1+vX2+rX4e3Y6fDa8fLb+fTczPLdzfPezfLezvLe0PLe0vPf1PPf1vPg2fPg2fPe3PPf3/Pg4/Th5vTh6/fi8fji9vjiNRbQOxbPRBfPUhjPYRrPcRzPgiHOlCnOpTLNtT/Nw0zMz1jL21nM51vN7l/I+mvJ/3vMMhzNPB7PRR7PUh/PYiHPciPPgyjOlC/OpTjNtEPMwlDMz1zM21zM513M7WHI+W3J/3zLNifOPynQSSrQVSrQYyzPdC/PhTTPlTrOpkLOtUzNw1nNz2PM22TM52TM7WjJ+HPJ/4PMOTXPQjfRSzjRVjnQZTrQdT3PhkLPlkfPp0/PtVjNxGTNz23M223N5m7N7XLJ+HvK/4nNO0bQQkfRS0jQV0nQZkvQdk7QhlLPl1fPp17PtWbOw3DOz3nN2nnN5nnN7H3K+IXL/5HONFXOQFjQSVnQVlrQZVzQdV/QhmPPlmfPpm7OtXXOw33Oz4bN2obN5IXN64jK9o/L/5rOIGTONWnQRGrPUmzPYm7Pc3DPhHTPlXjPpn3OtYPOworOzpLN2ZLO45LO65XL9pvN/6PPAHDNAHPPGnTPN3bPVXvPboDPgYPOk4fOpIvOtJDOwZbOzp7O2J7O4p7O6qHN9aXO/63QAITMAIXNAIbNHofOQYjOXIrOdo7OjpTOoZjOsp3OwaLOzajO16rO4anO6qzO9LDP/rXRAJrLAZvMGJzML5zMQ5zMVZvMaJvNgZ7NnKTOr6jOwK3PzbPP17XP37XO6bjP87rQ/b7SMa/LOrDLQrDLTLDMWbDMZrDMdbDMhLDNk6/NqLLOvrjPzL3Q18DR38HQ6cLR9MTS/sXRV7/LXcHMYcHMZ8HMcMHMesHMhsHNksLNn8LOrMLOusPPy8fR18vS4MzS68zT9MvR/szRds3Mec7NfM7Ngc7NiM/Nj8/OmM/Oos/OrdDPuNDQw9LRztPS2NXU4dTT69TS9dTR/tXRlNjOltnPmNjPnNnPodnQp9rQrtrRttrSvtvTxtzUzdzV1NzV2NzW4uDX7OHV9dzP/9rNr+PUsePVsuPVtOPVtuPWuuPWvuPWw+TXyOTYzeTY0uTY1+TZ2uPY4ObZ6+vc9e7d/OrXv+vbwOvbwevbwuzbxOzcxuzcyevczezd0ezd1eze2ezf2+zd3uzd4u3e6e/f8fLg+vTizfPizvPhz/Pi0PTi0PPi0vPi1fTj1/Tj2vTk3fTl4PTl4fTk5PTk5/Tl7Pbm8vjn9/nnTR3cUh7cWB7cYiDcbyPcfifbjCzbnDPbqz3auUjZx1XZ02PZ3m3Y6G7Y8m/Y+HLV/4HWTCPbUyTbWSXcYyfcbynbfi3bjDHbnDnbq0HauUzZx1jZ02bY3W/X6HDX83LY+HTV/4LWTi7bVTDdWzDcZTLccjTcfzjcjjzbnULbrEvbulXax1/Z02zZ3XXY6HbY8nfZ93nV/4fWUDrbVz3cXT3cZ0Ddc0LcgETcj0ncnk/brFfaul/ax2nZ03XZ3n3Z537Y8n/Z94HV/43XUkvcV0zcXU3cZ0/cc1HcgFTcjljbnV3brGTaumzax3TZ037Z3YfZ54fZ8YfZ9orW/5TXTFnbVV3cW13bZV/ccmHcf2TbjmfbnGzaq3LaunnaxoDZ0onZ3JHY5pHZ75HZ9JPV/53YRmraUG3cWG7cYm/cb3HbfXPbjHfbnHvbqoDauIXZxo3a0pTZ3JzZ5JvY7pvZ9J7W/qbYHXLaM3bbQ3nbV3zbaYDaeYPbiYXamYjaqY3auJLZxZjZ0Z/Z26bZ5KbZ7aXZ86jX/a/ZAITZCIXaJYbaPIbaUYjaaYzag5LalZXappnatp7ZxKPZ0KnZ2q/a47Da7K/a8rLZ/LjaAJbYAJjZCZfZJ5bYQJfYWpjZcZrZiZ7ZoaTZs6jZwq3Zz7La2rna4rra6rra8rza+8HbJ6zXMqzYOq3YRazYUqzYYKvYb6vYfqrZla3ZrrPav7jazbza2cLa4sTb6MTa8sfb+srcT77XVb/YWb/XYL7Yab7Yc77Yf7/YjL7Zmb7Zpr3YuMDazMba2cvb4s7c6c/c8tDc+9Lebs3Xcc3YdM3Xec3Yf83Yh87Ykc7Ym87Yps7Zsc7ZvM7Zx8/a19Pc4tfd6tje8tje/NjeidnYjNrYjtnYktnYltnYndrZpNrZrNrZtdvavtvbx9zb0d3d2d7d4t/f69/e9N/e+9/dpuTbp+TbqeTbq+Tbr+TbtOTcueTcwOXdxuXezeXf1OXf2ubg4Ofh4+bh6+rj9Ovi/ObbwOzgwe3hwuzhw+zgxezhyO3hy+3izu3i0u3j1u3j2u3k3+7k4+7l5e3k6u/l8vLn+vXoz/Tmz/Tmz/Pm0fTn0vTm0/Pm1vTn2PTn2/To3fTo4fXp5PTp5vXp6vXq7fbq8vfr9/nsZSvoZyvnbSzndS/ofzLoizbnmDznpUPmskzmwFfmzGHk127k4Xrj6YLi84Tj/IXj/4niYi7maTLobjPodTXogDjoizvml0DmpUfmsk/lv1nly2Tk1nDk4Hzi6YTi8oXj+4bj/4rhZDjnajvobzzodj7ngUHojETnmEnmpk/ms1fmwGHmzGrk13bk4IHj6Ynj8onj+orj/4/iZkXna0jocEnod0rngU3njVDnmVXnplvms2Hlv2nlzHPk133k4Ijj6Y/j8pDj+5Hk/5TiaFTna1bocFfneFjnglvnjV7nmWLnpmfms23mv3Tly33l1obk34/j6Jbj8Jfj+pjk/5nhY2HmamXnb2Xod2bngGnni2vmmG/mpXPlsnnmv3/ly4fl1o/k35fj557j8J7j+Z/k/aHhYHDmZnPna3Tnc3XnfXfmiXrml33mpIHlsYblvovlypLk1Znk3qDj56fj76fj96bk/KnhVX7mXoHnZYLmboPmeYXmhofmk4nloo3lsJLlvZbkyZzk1KLk3qnj5rDj7a/j96/k+7LhN4bmP4jmSYjmVonmao3lf5PljpXlnpjlrZzku6Dkx6bk06vk3bHj5bjj7bjk9bjk+rriCJTlI5blM5blRZflV5jla5nlfpzll6PlqKbkt6rkxq/k0bTj3Lrk5MDj7MDk9MDk+cPjJKjjL6nkN6nkQajjTqjkXKfkcajkh6rkna7ks7Tkw7nk0L3k28Lk5Mjk7Mnl8snk+czkTLzjUbzjVrzjXLzjZbzjb7vkervjhrrjk7njqLzkvcLkzcbk2srk5M/l69Ll8NLk+NTlaszibczjcMzidczjfMzjg8zjjMzjlsvjoMvjq8vjtsrjx83k2NLk49fl69rm8dvm+N3nhtnih9niitnjjdnjktnjmNrjntrjptnjr9rjuNrkwdrkytrk1Nrl4t7m7OLo8uPo+eTonuTjoOTkoeTkpOTkqOXkrOTjseTkt+XkvuXlxeXkzebm1ebm3efn5Ojo7Orp8uno+unnt+3mt+3lue3lvO7mvu3mwe3mxe3myu7nz+7o1O7o2u/p3+7p5O/q6e/r7O/r8/Lt+vLrzvTqz/Tr0PXr0fXr0vTq1PTr1/Xr2fXs3PXs3/Xs4vXt5fXt6fbu7fbv7vbu8/fv+Pnxez/yfUDygUHxh0Pxj0fxmUzxpFHxr1jwu2Dwxmnw0HPv2n3u5Int7JTt85ns+prs/5ztekLxfUTygkbyiEjykEvymE7wo1Twr1rwumPwxmvv0HXu23/t44rt65Xs8prs+prs/53tekrxfkzxg07yiE/xkFPymlfxpFzxr2Hwu2nwxnHv0Hru2oPu447t65js8p3s+p7s/6DtfFXygFjyhFnyilvykV3ymmHxpWXxr2rwu3Hwxnnw0IHv24ru45Pt653t8qLs+aLs/6XtfWLyf2PxhGXyiWbxkWnxmmvxpG/wr3Twu3rvxoHv0Inv2pDu4pnt66Pt8qft+Kfs/6jtem7xfnDxgXHxiHLxj3TxmXfwo3rwrn/vuoTvxYrvz5Hu2pju4qDt6afs8K3s+K3s/67sd3vwfH3xgH7xhn/xjoHwmIPwoobwrYrwuY/vxJTuz5ru2aDt4aft6a7s8LTs97Ps/7Xtcofwd4nwe4rwgovxio3wlY/woJLwrJXvt5nvw53uzaLu2Kju4a7t6bXs77rs9rrt/rvtXo7wZJDxbJHwd5Twg5fwj5nwm5vvqJ/vtaLvwabuzKvu17Du37Xt6Lzs7sHs9cDs/MHtSZjwUZnwWJrwYpvwb5zwf57wk6Tvo6jvsKvuvq7uyrPt1bjt3r3t58Pt7sjt9Mjt+8ntMKbvOqfvQ6fwUKfvYKjvcanvgqvvlK3vqrTuurfux7vu08Dt3sXt5srt7c/t9M/t+tDtULruVrvvWbruX7ruZ7rvcLnverjvibjunbrur73uw8Tu0cjt3Mzu5tHu7dXt89fu+dfubcvtb8vuccvtdsvte8vug8rui8rtlMntncntp8ftucruzM/u2tPu5dju7dzu897v+N7uiNntidnti9ntjtntktntl9ntndntpdnurNjutdjuvdfuxtfu1Nnu497u7OPv8+Xv9+Xvn+TtoOXtoeTtpOTtp+Xuq+XtsOXttuXuvOXuwuXuyeXu0OXu2OXu4OXv7Onw8+zw+OzwtO7ute7ute7ut+7uuu7uvO7uwO/uxO7uye/vz+/u1O7u2u/v4O/v5/Dw7fHw8/Lx+fHxx/Xvx/XvyPXvyfXvy/XvzfXv0PXv1PXw1/Xw3Pbx4Pbx5fbx6vby7vfz8ffz9fj0+fn1k1/7lWD7mGH7nGP6omb6qWr6sm/6u3X6xXz5zoP514v435P35pz27qX19K31+rH1/7L1kmD6lGL6l2P7nGX7omj6qWz6sXD5u3f5xX35zYT414z33pT255327aX18630+rL0/7P1kmb7lWj7mGn7nWz7om76qnH5snb6u3v5xYH4zoj415D435j35qD27af186/0+bT0/7X1k2/7lnD6mXH6nXP6o3b6qnn5s336u4H5xYf4zo7415T335z35qP17ar187P1+bf1/7j1lXn6lnr6mXv6nnz6o376qoH6s4X5u4n5xI74zpT41pr33qH25qj27a/187f1+br1/rv0koH6lYP6mIT6nYb6o4f6qYn6so35u5H4xJX4zZv41qH33qb25a317LP18rr0+L70/r/0kIv5lI36l476nI/6oZH5qZP5sZb5upn5w574zKL31af33a325bL17Ln18r/098P0/sT1jpb6kZj6lJj6mZn5n5r5ppz5r5/5uaL4wqb4y6r31K/33LP25Ln27L718cT098j1/cj1h576ip/6j6H6lKH5mqL5o6T5rKf5tqr4wK34ybH307X327n25L7268P18cn19s31/M31caH6d6L5e6P6gqT6jqf5m6v5pq74sbD4vLT4x7f30bv32r/248T26sn18M719tL0+9L1ZKv5aaz5b635dq36gK/6i7D5mLL5qbf4uLv4xL/3zsL32cb24sv26c/279P19dj1+tj1Yrr5ZLr5Z7r5a7r5c7n5gLr5jrv5nr35rL/4vsX4y8n318334NH36NX18Nn29d72+t72esz4fMz4f8z4gsv4hsv4i8v4kcr4mcn5pMn5s8v4w83409P33tf359r279/29OL1+eP2k9r3ldr4ldr4mNr3m9n3n9n3pNn3qtn4sdn4uNj4v9b4y9j429345eH37uT39Of2+en2qOb3qOX3qub3rOb3rub3seb3teb3uub3vuX3xOb4yuX40OX41uT44eX47er49e33+u/3u/D3uu/2vPD3ve/3v/D3we/3xPD3yPD3y/D30PD31PD32fD33u/35PD46vD48/H3+fT4yfb3yvf3yvb3y/f3zff3z/f30ff31Pf41/f32vj43ff34vf35vf36/j48Pj49fn5+vr6\",pastel400:\"BQUDCwUDGAYDJgcDNwYDTAICZgABgAAAmgAAsQAAxgAA1gAA5AAA7woA+yAA/y8A/zwAAwwDCw0DGA8DJhADNw8CTAwBZgUAgAAAmgAAsAAAxQAA1QAA4wUA7xoA+ykA/zUA/0EAARgEBxcEExgCJRsCOBsAThsAZhgAgBMAmQ0AsQgAxQ0A1RYA4iAA7ioA+jUA/z4A/0gAACYDACUEDiYDICgBNSoATSsAZyoAgCkAmigAsSkAxCsA0y8A4DUA7TwA+EMA/0sA/1MAADcBADYDADYDGTgAMDkASj0AZT4AgD8AmT4Arj8AwUEA0UQA3kkA600A9lMA/1gA/14AAEoAAEoAAEkACUoAKkwARE4AZFMAflQAllQArFQAvlUAzlcA3FsA6F4A9GIA/2YA/2sAAF8AAGAAAGAAAF4AIGAAP2IAW2QAemgAk2kAqGkAu2oAy2oA2WwA5W4A8XIA/XUA/3gAAHMAAHUAAHMAAHIADnIAOHQAVXUAcXcAjnoApHwAtn0Axn4A1X0A4n4A74AA+4IA/4UAAIcAAIcAAIUAAIQAAIMAMYQAUIUAbIcAhYgAnYoAso0Awo0A0Y4A344A7I4A+Y8A/5AAAJUAAJYAAJYAAJQAAJMAL5MATpQAaJUAgJYAl5cArJkAv5wAzp0A250A6JwA9ZwA/5wAAKQAAKUCAKUFAKQIAKIMMKEQTqIKaKIBfqMAk6QApqUAuKYAyqkA2KoA5aoA86oA/6oAALIVALIXALIYALEaHLAcOq8fU68faa8afrAVkbANo7EBtLIAxLMA1bYA47cA8bcA/7cAAL8nAL8oAL4pGL4qMr0rRrwuWLswbLstf7srkLwmob0isL4bv78Qz78C4MMA7sMA/MQAEMo5Hcs6Kco6Nso7Rck8VMg+YcdAccdAgcg+kMg8n8k5rsk2vMoxy8sr2swk7dAg+9AlO9dNQNhORddOTdZPVtZQYdZSbdVUeNRWhdRUktVToNZRrddPu9dMyNhI19lF5tpA+d0/VeRlWeVnXOVnYuVoaORpcONqeeNsguNvjeNwl+NuouRtruVsuuVpyOZo1edl5Ohi8+hecPOCcvODdPKDePOEfPKEgvKGifKIkfKLmfGNofGMq/KMtfOLwPSLy/SK2PWJ5veI9PeHBgUJDQYJGAQJJgQJOAULTQELZwAKgQAImwAHsgAExgAC1gAA4wAA7wwA+iAA/y8A/zsABA0JDQ4KGQ8KJg8KOA8KTQwJZgQIgQAHmgAFsgADxQAA1QAA4gUA7hoA+ikA/zUA/0AAAhkKChkKFhoKJhsIORsITxsIaBgGgRMEmg0CsQgAxQ0A1BYA4iAA7isA+TQA/z8A/0cAACgKAygLEScLIikJNysGTysDaCoBgikAmygAsSkAwysA0y8A4TUA7TwA+EMA/0sA/1IAADkJADgKBDcLHDkKMjoGTD0BZz8Agj4Amj4Arz8AwkEA0UQA30kA6k0A9lMA/1gA/14AAEsGAEwHAEsJDksILU4FR08AZVMAf1UAmFQArVMAv1UAzlgA3FsA6F4A9GIA/2YA/2sAAGEBAGICAGEFAF8HI2ECQWMAXWQAfGgAk2kAqGkAu2oAy2kA2WwA5W8A8nIA/XQA/3gAAHUAAHYAAHUBAHQEFHMDO3UAV3YAc3gAj3sApHwAtn0Axn0A1X0A434A74AA+4IA/4QAAIgAAIgAAIcAAIYEAYQINIUDUoYAbYcAhogAn4sAso0Aw44A0Y4A3o4A7I4A+I4A/5AAAJYBAJgFAJYHAJUKAJQOMZMOT5QHapUAgpYAmJcArJkAv5wAzZwA250A6Z0A9pwA/5wAAKUPAKURAKUTAKQVCaMYM6EbUKIXaaMSf6MIlKQApqUAuKYAyqoA2KoA5qoA86oA/6oAALIeALMgALMhALIiIrAjPa8mVK8maq8jf7AfkrAZpLERtLICxLMA1bcA47cA8bcA/7cAAL8tAL8vAL8vHr4wNb0xSbwzWro1bbszf7sxkbwtob0psb4jwL4cz8AQ4MMG7sMC/MQCFss9Iss+LMo/Oco/R8lAVclCY8hEcsdEgshDkchAoMk+rsk6vMo2y8sx2swq7NAo+9AsPNdQQddRRtdRT9dTWNdUY9ZVbdZXeNVZhdVYk9VWoNZVrddTu9dQydhN1tlI5tpE+d1DV+VoWuZqXeVpYuRqaeRrcORteuRvg+NxjeNyl+NxouRvruRuu+VsyOZq1eZn4+dj8+hgcfODcvOEdPKEePKFffOHg/KIivKKkfKMmfGOofKOq/KNtfSNwPSMy/SL2PWK5veK8/eJCgYUEAYUGgYVJwQUOQIVTwAVaQAVgwAYnAAXswAWxgAV1gAV4wAT7xET+iMT/zEV/z4ZBg0UDw8VGQ8VKA4VOg0VUAoVaQYXgwAWnAAWsgAVxgAU1QAT4wkS7x0R+SoS/zcU/0IYARoVCxsWGRwWKBwWOxwWURsWahgVhBQVnQ0UswoTxg4S1RcQ4iEP7iwO+TYP/0AR/0oWACsXCCsXFioXJisWOywUUisTaysShCoRnSgPsikOxCsM0zAK4TYI7D0I+EUJ/0wM/1QTAD0ZADwWDDsXIDsXNTwVUD8Raz8NhD4InD4GsUAEwkEB0kUA30kA604A9lQA/1kD/18MAE8UAE8VAE4WFk4YME8VSlERaFUKgVUCmVQArlQAwFUAz1gA3FsA6F8A9GMA/2cA/2sAAGQSAGQUAGQWAGIXKGIWRGQSYGYLfmkAlmoAqmoAvGkAzGoA2W0A5m8A8nIA/XUA/3gAAHcSAHgTAHcUAHYXHXQYPnUUWncNdXkDkXwApX0At30Ax30A1X0A434A74AA+4IA/4UAAIkTAIoVAIkWAIcYEYYbOIYYVYcTb4gMiIkBoIsAs40Aw44A0Y4A344A7I4A+Y4A/5EAAJgYAJgZAJgbAJYcDpYfNpQfUpUca5YXg5YPmJgDrZoAv5wAzp0A250A6Z0A9pwA/5wAAKYfAKchAKYiAKUjGaQlOKInU6MmaqMigKQdlKQXp6YOuaYAyqoA2KoA5qoA86oA/6oAALMqALMrALMrALIsKbEuQrAwVq8xbK8ugLArkrAnpLEitLIbxLMR1bYH47cA8bcA/7cAAL82AL83Br83Jb44Or45TL07XLs9brs7gLw6kb03ob0zsb4vwL8pz8Ai4MMe7sMb/cQaHspDKMtFMMtFPMpGSspHV8lIZchKcsdLgshJkchIoMlFrspCvMo+y8s62sw07M8y+9A2P9dVQ9hWSNdWUNdXWddYZNZabtVbedVdhtVdk9VboNZZrddXu9hVyNhS19lP5tlK+d1KWOVrW+VsXuVsY+RtaeRucuRweuNxg+NzjeN1mON0o+RzruRxu+Vwx+Zt1eZr4+dn8+hlcvOFc/OGdfOGefOHfvKIg/KJivGLkvKOmvKQovGQq/KQtfOPwPSPzPWO2PWM5vaM8/eLDQgiFAciHQciKQcjOQMjUgAjbAAkhgAlnwAltgApyAAr1wAq5AAq7xcr+igr/zUt/0IxCQ8iEhAjHREjKhEkOw4jUwckbAAlhgAlnwAmtQAqyAAq1wAp4xEq7yEq+S8r/zks/0YwBBokDRskHB0lKx0lPhslVRklbRYmhxMonxAptQ4pxxIp1hso4yYo7jAo+Tkq/0Ir/0wvACslBCwmFywmKi0nPy0nVi0mbiwmiCsnoComtCsmxi0m1DIl4Tgm7T8m+Ucn/04p/1YtAEAmAEAnFEAoJz4nOz8mVUAkbz8iiEAin0Ais0AhxEMh00Yh30sh608h9lUj/1ol/2AqAFMpAFUqAVQoIFMoNlIoUFQmbFYihlYenFQasFUawVcZ0Fka3Vwa6WAb9GMd/2cg/20mAGkpAGknAGgnEWYpMGUqSmYnZGgkgWsfmGsarGoUvmkNzWsN2m0O5m8Q8nMU/XUY/3kgAHsmAHwoAHsoAHkpKHcqQ3cpXnkmeHoik30bp30VuX0NyH0D1n0A438A74EC+4IK/4UWAIwoAI0pAIwqAIorIoktP4ctWYgqcoknioohoo0atI4TxI4J0o4A4I4A7I4A+I8A/5ECAJosAJotAJouAJkvIpcwPZYyV5Ywb5cthZgpmpkkr5sdwJ0Wzp0M3J0B6ZwA9psA/5wAAKgxAKkzAKgzAKc0KKY1QaQ3V6M2baQ0gqQxlqUtqKYpuqcjy6od2aoV5qoM86oB/6kAALQ4ALU6ALQ7F7M7M7I8SLE+Wq8/brA9grE7k7E4pLI0tbMwxbQr1rcn47ci8bcd/7caAMBCA8BDHMBDL79EQL5FUb1GYLxHcLxHgbxFkr1Dor1Asb49wL85z8A04MMy7sMv/MQvJ8tOL8xPN8xPQctPTspQW8pRZ8hSdMdUg8hTkshRoMlPrspNvctKy8tG2sxB7NBB+tBCQ9hdR9heTNheU9dfXNdgZdZhcNVie9Vkh9VklNZjoNZhrddfu9ddydlb19lY5tpU+d5VWuVwXeZyYOVyZeVza+V0c+R1e+R2hON4jeN6mON5o+R4r+R2u+V1yOZ01eZx5Odu8+hrcvKIdPKJdvOJevOKfvKLhPKNi/KOk/KQmvKSovKTq/KStvOSwfSSzPWR2PWQ5veQ8/eODgg0FQg0IAg0Lgg1PwU3UAA1bQA3iAA3ogA5twA5yQA62QA75A5D7yND+jFF/zxG/0hICxE0FRI1IBI1LhM2PxE3Ugs2bgA3iAA4ogA5uAA6yQA72AA+4x1C7ypD+TVD/z9E/0tIAxs2Eh42Hx03Lx84QR45VRo4cBE4igk5ogE6uAA7yQ0+1iNB4yxB7jZC+T9D/0dE/1FHACs3Bio4GSs4Li45Qi86WSw6cis6jCo8oyo9ty9AxzI/1jdA4jw/7URA+EtB/1JD/1pGAD86AD86DkA7KUE7QkI8WkM9c0M8i0M9oUM9tUQ8xUY800k84E0861M99lg+/11A/2NEAFg7AFk8AFk9KVg+P1c+Vlc8clg6ilY3n1Y3slc3wlk30Fs33V846WI59GY6/2o8/29BAG0+AG4/AG1AJWw/O2o+UWk+ams7hW04m2w0r2oxv2sxzm0x2m8y5nEz8nQ2/Xc4/3o8AH9CAIBDAIBAFH4+NXw/S3o/ZHs9fX07ln83qX8zun4vyX4r134q5IAs74Iv+4My/4Y4AJBBAJFAAJA/C44/MYxASYpBX4pAd4s+jYw7pI83tY8yxY8u044q4I4l7Y8m+Y8q/5EwAJ1AAJ5BAJ1CD5xCMppDSZlEXZdEc5hCiJk/nJo8sJw4wZ00z50w3Z0r6pwm95wg/5wmAKpEAKpFAKpGHKlGN6hHS6ZHXaRJcaRHhaVFl6ZCqac/u6k7y6o42Ks05qow86or/6koALZJALZLELZLKrVLP7RMULJNYLFOcbBOg7FLlbJKpbNHtbNExbRA1bc+47c78bc3/7c1EMFRH8FRK8FSOcBSSL9TVr5TZb1Vc7xWg7xUk71Sor5Qsb5NwL9Kz8BH4MNF7sND/cRCMsxaOM1bPsxbSMtbUstcXspda8ledshfhMhekshdoclbr8pZvctXy8xU281R7M9Q+tFRSNhmS9hnUNhoV9hoX9hpaddqctZrfdVsiNVtlNVsodZrrtdpu9hnydll19li59pg+d1gXeV3X+V4Y+Z5aOZ6beV6dOR7feR9huN+j+OAmeOApOR/sOV+u+Z9yOZ61eZ45Od28+hzdPONdvONd/KNe/OPf/KPhvORjPKRk/KUm/KWo/GXrPKXtvOWwPOVzPSV2PWU5vaT9PiTDAlIFglJIghJMQhLRQdMWQBOagBLhwBNowBOuQBPygBP2QBP5QBR8QtT+jBY/0Zf/1FhCxJJFRJJIhNKMhNLRRJNWg5PawJLiABOpABOuABOygBQ2ABQ5QBR8B5T+Txc/0lf/1NhCB5LFB5MIh9MMx9NRyBOWx5QbhhOiwdQpABQuQBQygBR2BBS5SJT7zdY+Ehd/1Be/1lgACpLBCtMHS1OMi9PRzBQXDBRcixRjidRpSVRuCdSyStT1zNU4kJZ7Uxb91Jc/1ld/2BgAD1NAD5OEj5PKz9PRkNSXkVUd0NUj0JTpUNUuEVVxktY1E9Y4FRY61hZ9l1a/2Ja/2hdAFZRAFdTAFhTJVlUQ1pVXltWd1tWjVtWoltVtFxVw11U0WBU3mNV6WZV9GpW/21Y/3JbAHJUAHNWAHNVInJWRXFXW25WcW5Uim9Sn21PsW1PwG5PznBP2nFQ5nRQ8ndS/HlT/31YAIRVAIVWAIRXLoRYRoJYV39Wan5VgoBUmYFRrIBNvH9Ky39J14BK5IJL74NN+4VP/4hUAJRZAJRbIZNbNpNbQJFWVY9XZ4xXfI1VkY5Tp5BQt5BNxo9J1I5G4Y5E7ZBG+JBI/5JNFaBeHqFdI6FbK6BYQp9YVZxYZZlZeJpYi5tWn5xTs55RwZ5Nz55K3Z1G6pxC95xB/51GAKxaCa1aG61aMaxaRapaVqlbZadbdqVaiKdZmqdXq6hVvapSzKtP2atM56tI9KpF/6lCCrdcHrhdK7ddOrdeSrZeWbVeaLNfdrFfhrJel7Ncp7RatrRYxrVV1rdT47hQ8rdN/7dLKcNhMcNiOcJiRcJiUcFjXsBjar5jd71khb1klL1io75hsr9ewcBcz8FZ4MNY78NW/cRVO81nQc5pRs1oT81pWcxpZMxqb8preslshshslMlroclpr8pnvctmy8xj281g7NBh+9FhTdlyUdlyVdlzW9lzY9h0bNd0ddd1f9Z2idV3ldV2otZ2rtd0u9hzydlx19pv59ps+d1tYOZ/Y+aAZuaBa+aCceaCd+WDf+SEiOSFkeOHmuOIpeOHsOWGvOaEyOaD1ueB5Od/8+h8dfOSePOTefOTffSTgfOUh/OVjfOXlPKYnPKapPKcrfKctvObwvSbzfWa2faa5vaZ9PeYCwpfFwpgJQphNghhSgZjYAJldQBnhgBkoABluABmywBl2QBl5QBl8QNm+yVo/zlr/0twDBRfGBRhJhRhNxRiSxJjYRFldQVnhgBkoQBmuQBmygBl2QBl5QBl8Bpn+i5o/z5r/1JxCh9hFyBiJiBjOCBjSyBkYSBmdxxoiBVmowFnuQBmygBm2Ahm5B5n8C9o+T1q/0pt/193ACthDy5jIy9kNzBkTDFmYzJoeTFqiy1opSdpuSNoySho1zBo4zpp7kRr+E5t/11z/2l4ADxhAD1iEz5jMEBmS0RnY0ZqekdsjURqpUFpuEJpyEVq1Utq4VFs7Flu9GZ0/2p0/3B3AFJlAFRnBFVnKlZoRVZpYltreV1tj1tspFtstl1sxWBt0WVv3Wpx6G1x8nBx/XNx/3h0AG1pAG9rAG9rInBsQ3FtXnFueXJujnJuoXJtsnJswXNtznVt2ndt5nlt8Xtt+31u/4FxAIhtAIluAIpuHohuRYduYoZudYNuiINsnYNproFmvYFmy4Jn14Nn44Vo74Zp+ohq/4ttAJhtAJhuD5huNpdvUJVvZZRwcpFtgpBtlZFrqZJouZJlx5Bi1ZBh4ZFi7ZJj+JNk/5RnDKNvJKRwNaRxSKNyWqJyYKFtcJ5tfpxtkJ1soZ5qtKBowp9l0J5i3p5e6pxc9p1d/55hOK9zQa91Sq91Ua5zVK5vYaxub6pvfKhvjKhtnKlsrKlqvqxozKxm2axi56pf9Klb/6lbRLl1R7p2SLpzSrlxVrhwY7dwcLVxfLNxibNwmbNvqLRtt7Vsx7dp1rhn5Lhl8bdi/7ZgP8RzQsRzRsRzT8NzWcNzZcFzccFzfL9ziL10lr5ypL9xs79wwcBu0MJs4MRq7sNo/cRnRs92Ss93UM93V853X814ac14dMx4fsp5icl5lcl4osp3r8p2vcx0y8xy2s1w7NBw+9FwVNp9WNp+W9p+YNp/aNl+cNl/eNeAgteAjNaBltaCotaBr9eAu9h+ydl919l759p5+d16ZOaIZ+aJauaKbuaKc+aKeuWLguWMiuSNkuSOm+OQpuSPseSOvOWNyOaM1ueL5eiI8+iGd/OYePOYe/OYfvOZgvOZiPOajvOclfOdnfKepfKhrvKhuPOhwvSgzfSg2vag5/ef9feeEQt2HAt2Kgp3PAh4UAV5ZQF6fAB8kQB+oQB9tQB8yAB82QB75QB68QB6+iF6/zZ8/0l/ExV2HhV2KxV3PRR4UBN5ZRB6fA58kAB+oQB9tAB8yQB82AB65QB68BR6+ix7/z18/06AEyF3HiF3LCF4PSF4USF6ZiF7fiF+khyAoRN9tQR9yQB92AB75Bl77it7+jt8/0h9/1eBDC93GzB4KzB4PTF5UjJ7aDN9fzV/kzOBoS9+tip+ySd91y184zd87kF9+Ex+/1d//2ODADx3AD14Hj96OkN7UUV8aEd+f0mAk0mCoUZ/tkR/yEN+1Ul+4U9+61d/9l6A/2aC/3CGAFB4AFJ6DlJ6LVN6SVZ9Zlp/fV6Bkl+Col2AtluAxV1/0mKA3WaA6GuB83CD/HmH/4CMAGl7AGp9AGp9Kmt9Rmx+X21/e3KBkHSDonOCs3OCwnWCz3eD2nyF5YCH74KH+oSH/4eJAIJ+AISAAISAJoSBRoWCYYWCeIaDjoeEoIeDsIeDvoeDy4iC1oiC4oqC7ouC+YyD/4+FAJuCAJyEAJuEKpuESpqEZZmEfJeEi5SDmpSBq5V/u5N9yJN81JN84JR87JV995V+/5eAAKeBFqiDLqeDRKeDWaaDbaWEfqOFh6CClJ+BpaCAtqF+xKF70Z943p516p529p93/6F6NLKDPrOESLKEVbGFZLCFdK+Gea6ChauBkKmBn6uAr6t/wK19za162ax256p09Klx/6pzTbyGU7yHWbuHYruIbbqIb7qEeLiChLeCjrSBnLWBqrWAuLZ+ybh81rh65Lh38rd0/7ZyXsaKYcWKZMWKZsWIZ8SFbcSDeMKDgsGDjL+DmL+Cpr+BtMCAwsF/0sN94cR87sR5/cN4XtCKX9CKYNCJYc+HZs+FcM6Gec2Gg8yGjcuGl8mGpMqFsMuEvsyDzM2C3M6A7NB/+9GAXduKX9uLYduKZtuKbNqKddqLfdmLhtiMj9eMmNaNpNaMr9eLvNiLydmJ2NqI59uG+d6IaeeSa+iSbeeTceeTd+eUfeaUhOaUjOWVleSWneSXp+SYseWXveWXyeaV1ueU5OeS9OmQefSeevSffPSff/SfhPSfifOgj/OhlvOin/OkpvKmr/KouPOnw/SnzvWm2vWm5/al9vilHgyJJwuKMwqKRAiLVwaMawGOgQCPlgCSqACTtwCTxQCP1QCP5ACN8ACM+x2M/zOM/0eOHxaJKBaKNBWKRBWLVxOMaxGNgRCPlxKSqASTtgCSxACP1QCP5ACN8AqM+SeM/zqM/0yOICKKKSKLNSKLRSKLWCONbSKPgiOQmCaTqCCUthiTxRSQ1RKQ5BWO7yiN+TiN/0aN/1WPHjGKKDKMNTKMRjKNWTOObjSQgzaRlzmTqDeUtTOTxTGR1TGQ4zSO7T+O+EqO/1WO/2KRCD+LIEKNMkONRESNWEaPbUeQgkmSl02UqE2VtEqSxEmR1EmQ4U2P7FWP9l2P/2WQ/26TAE+JAFGLHVGMN1ONVVmPa1uQgV2RlWGUpmKVsWCSw2CS0mCQ3mSQ6WqQ82+R/XWS/32VAGWKAGaMFGeMMWeMSWiNY2uQfnGSk3STpHaVsXWTwnSTz3aS23mS5n2T8IGU+4WV/4yaAH2OAH6PD36PMX+PS3+QYoCQeIGRkIaToYiWsIeUv4iUy4qU14yV4o6W7JOZ95SY/5aaAJWSAJWSE5WSNZWSTpWTZZaTepeUjpeVn5iWrpiWu5iVyJiV1JiU35mU65qU9puU/5yVAKmUB6qVJ6qVP6qVVaqWaqmVfqiVj6aWnKOUqaOUuKORxaGP0aGN3qKO6aGO9aKO/6OQLrWUObaURLWUUrWVYrSUc7OVg7KVkLCWmK6To62Ssa6Rwa+Qzq6N26yJ56qH9KuI/6yKSr6UUb+VWL+VYb6Vbb6WebyWhruXjLqTlbiSn7aSrLeRuriQyrqP17mL5biJ8raG/7WEXMiWYMiXZciXbMeXdceYf8aYg8WUicOSk8KSnMCSqMCRtcGQw8KP08SO4cSM78SK/cOIadGZbNGZcNKaddGaetCZfNCWgM+Uic6UksyUm8uUpcqTssySv8yRzc6Q3M6P7NCO+9CNdNydddydddycdtubd9yZetuXgtqXitmXk9iYm9eYpdaYsdeXvdmXytmW2dqV6NuT+d6VdOefduigd+ifd+ieeuedgeidh+eej+ael+afn+WgqOShsuWgvuWgyuaf1+ee5uic9Ombe/WmfPWmfvWmgfWmhfSmivSnkfSomPSpoPOqp/OrsPOtufOuxPSuz/Wt2/as6Pes9verLw2bNg2bQAucTwmdXwedcgOehgGgmgSirA+juwGkyACk0gCf4ACf7gKe+hWb/zCb/0ScLxeaNhebQRabTxacYBWdcxSfhxOgmhahrB2juhWkyAik0gyf4BCf7Red+SKb/zab/0qcMSSbOCSbQiSbUCSdYSSedCSfhyWgmyiirC6juiqkxySk0iWg4Cif7Sye+TSc/0Ob/1OdMTObODOcQzSdUTSdYjWedDafhzegmjqiq0Cjuj6kxjuk0Tug3z2g7ECe+Eed/1Kc/2CeLkSdNkWcQUWdUEaeYUefc0ifh0uhmU6iqlOjuFKkxFCj0FGh3lKg61Sf9lqd/2Kd/2yfBk+aHFCcMlOcTFidXlmecVufhV2gl2GhqGajtmakwWSizmWh3GWg6Wif822e/XOf/3uhAGKYC2SZJGSaO2WbUmedbm6fgnCglXShpnijtHmkvniizXii23ih5nug8X+h+4Oh/4mjAHiaCHqcJHqcOnqcTnucY3ucen+fkoSho4ijsYqkvYmjy4mi2Iqi442i7o+j+JKj/5amAI+eEJCeKJCfPpCeUpCfZ5CfepGfjJKgoJeir5mkvJmkyZmk1Zuk4Jyl656l9aGn/6OpDaKgJKOhNKSiRqShWKSia6SifaSjjqWjnqakraeluqelxqek0qak3aej6aej9Kej/6mkLLWjN7akQrakULakYLakcLalgbWkkLSkn7OkqbCjtbCjw7Chz66e266d566d9K+e/6+fR8KjTsKkVcKjX8Gka8CjecCkhb6kk72koLylprqisLmhvbqgy7uf2Lqc5bia8reX/7iZWsujXsujY8qja8qjdMmjf8mkisiklcelmsWio8OhrMGgt8KfxcOe1cWe4sWb78SZ/sOXZ9Ska9SkbtSkdNOle9OlhNKljtGmktCjmM+hoc2hqcugtMygwM2fzs6f39Ge7dCc+9Gcc92ndt6ned6ofd6og92oit2pjdymj9ujl9qjoNmjqNejstejvtijy9qi2dqh6dug+d2gf+msgemtg+ith+itiemsiuiqjOiok+eomuaoouapq+WptOWqv+Wpy+ep2Oeo5uin9ummiPWyifWyifWxivWxi/WwjfWukvSvmfSvofSwqfOxsvSzu/O1xfS00PW03Pa06fez9/izQg+qRw+rTw2rWwyragusewqtjQuung+vrxmwvSOxyh6y1haz3Ryt6iKt9ymr/zCp/0GpQhmqRxmqUBmrXBisaxisexetjRmunhyvriOwvSyxyimy1SOy3Cet6iut9zGs/zap/0apQyaqSSarUierXSasbCesfCetjSmuniyvrjGwvTqyyTey1TOz3TWu6jmt9j2r/0Kq/1CpQzWqSTasUjasXTasbDetfDitjTqunj2vrUKwvEqyyEiy1EWy3Eeu6Umt9k2s/1Cq/12qQkarR0arUEerW0esa0ite0qtjEyunFCvrFSwuluxxlqy0liy21mu6Fut9F2s/2Cr/2qrNVSpQFeqTFirWFmraFqseFusil6tmmKuqmawuGyxxGyyz2qx2Wuu5myu822s/nGr/3msHGKoKWOpN2SpSWWqYmurdW6sh3Ctl3Oup3ivtn2xwn6yy3yw13yv5Hyu8X6t+4Gs/4iuEHWnIXaoMHaoQXenVXipanqqg4GslIStpIevs4ywv42yyIyv1oyv44yu746u+ZCu/5WvGYupJoupNIupRIupVouqZ4ypeYyqjZCsoZWusJmwvZuyx5ux1Jqw4Zuv7Jyw956w/6KxJZ6rMZ6sPZ6sS56sW5+sa5+sfJ+sjJ+sm6CtraWwu6iyx6ex06ix3qmx6amx9Kuz/6+1N7CuP7GuSLCuVLGvYrGvcbGvgLGwj7GwnrGwrLOxubSyxbSy0LSx3LSx57Sx87Sx/7WyR8GwTcKxVMKxXcGxasKxd8KyhMKyksGyoMCyrb6ytryxwb2wzb2u2bqr5rqr8rqr/7usWM2wXM2wYc2xaM2wcs2xfcyxicuxlcqxocixrMexssWvu8SuyMWt1sas48Wq8MSn/sOmZdavadawbdawc9awetWwhNWxjdSwmNOxotKyp9Gvr86ut82tw86t0NCs4NGr7tGq/NGpceCxdOCxd9+xfOCygt+yid+ykt6zm92zoNyxpduvrdqvtdivwNmuzdqu29ut692t+t6sfeq0f+q1geq1heq1ium1kem2mOm3nOi1nuiypueyruazt+Wzweazzeey2eey5+ix9+mwiva6ivW6jfa6kPW7lPa7mfW7m/W6nfW4o/W4q/S5tPS6vfO7xvS80fS83fa76ve79/i6VRS5WRS5YBO5ahO5dhO6hRS7lBa7oxu7sSO9vi29yzi+1jS/4jG/5zS59Dm5/z+4/0e2VB24Wh25YB65ah25dh65hR+6kyC6oiW7sCu8vjS9yz6+1ju/4Ti/5zq58z65/0O3/0u3Vim4Wyq5YSq5ayu5dyu6hSy7ky67ojK7sDi8vkC9y0i+1Ua/4UPA5kW580i5/024/1S3VTi4Wjm5YTm5ajq5djq6hTy6kz67oUG7sEe8vU69yVa+1FS/4FG/5VK68lW6/lm4/163VEi4WUm4X0m4aUq5dUu5g026kU+6oFK7rle8u169x2W+02O/3mG/5WK68WS5/Wa4/2q3Tli2VFq4XFq4Zlu4cly4gV65kGC6nmS6rGi7uW69xXS90HO+3HG+43K68HO5/HS4/3e3NmK1QGS2TWe3YGu3b224fW+4jHG5m3S6qXi7t328w4O9zoO+2IG94YG77oK6+oK5/4a4K3OzNXW0P3W0Tna1X3e2dX23iIG4l4S5poe6tIu7wZG9zJG+1Y+84JC77ZC6+Y+5/5O5K4e0M4i0PYe0Soi0WYe0aoi0fYu2k5G4o5S5sZi7vp28yp6+0py835y77Jy79526/6C6Mpm1Opq2RJq2T5q2XZq1bJq2e5q2ipu2m564rqO6vKi8yKq+0am836i86qm79qq8/6y9Pqu3Rau3Tau4Vqu4Y6u4cKu4fqu4jay4m6y4qa25ubK8xrW+0bS93rW96bW99Le+/7i/S7u6ULy6Vry6X7y7ary6dry7g7y7kb27nr28q769uL++xcC/0MC+28C+5sC+8sC9/cC9V8y9Wsy8YMy9Z8y9cMy9fMy9h829lM2+oM2+rMu+uMq+wci8y8i82Mi65Ma48ca3/ca4Y9m9Z9m9atm9cdi9edi9gte9jNe9l9a9otW9rdS9uNK9vdC7xtC609G64tK57tG3/tG2b+K8cuK8deK8euG8gOG9iOG9keC9muC9pN6+rt6+s928u9u7w9m6z9u63dy67d65+964e+y+fey+f+y+g+u/iOu/j+u/l+q/n+q/qOrArum/sui9u+e9w+W9zua92+e86ei7+eq7hvfCiPfCiffCjPbDkfbCl/fDnvbEpfbEq/bErfbBtfXCv/XDyPXE0vXE3vbE6/fE+PjDaBzGahzGcB3GeB3Ggx7GjiDGmiPGpyjHtC/IwDjIzELJ1kvK4UnK7EbL8UfF/UzE/1TEZyTEayTFcCXFeCbGgibGjijGmivGpy/HtDbIvz7Iy0fJ10/K4E3K60rK8EvF/FDE/1bDZzDFazDFcDHGeDHFgjLGjjPGmjbHpjrHs0DIv0fIy1DJ1lfK31XK61PK8FPF/FfE/17EZz7Faz7FcD/Fdz/FgkDGjULGmUTGpkjHs03IvlPIylzJ1GPJ32DK6l7K71/F+2LE/2fEZU3EaU7Ebk7Fdk/FgFDFjFHFmFTGpFfGsVzHvWLHyGnJ03DK3W7K6GvK7mzF+m7E/3LEYlzDZl3Ea17Ec17EfWDEiWLFlmTFomfFrmvGu3DHxnfI0X3J3HvK5njK7HnF+XrE/37EWGrCX2zDZm3Db27Dem/DhnHEknTFn3fFrHrGuH/GxITIz4rJ2YnJ5IfJ64bF+IjF/4nEQ3TBSnXBUnbBXXfCcHzCgYDDjoLDnITEqYjFtYzGwZHHzZbI15XJ4ZPJ6pPG95PF/5XFOYS+P4W/SIW/VIa/Y4fAcojBhYzCl5HDpZTEs5jFv5zHy6HI1aHJ35/I6Z/G9p/G/5/FPZa/Q5bASpfAVJfAYJbAbZa/e5bAjJnBoJ/Dr6PEvabGyavI1KzJ3KvH6KrH9qrG/6vGRafBSqfBUKfBWafBZKfBcKfBfafBi6fBmKfBqKvDubDFx7TH07fJ3LbI6LXH9bbH/7fITrfDU7fDWbjDYLfDarfDdbfDgrfDjrfDm7jEp7jEtLnExL7H0sLK3MHJ6cHJ88LJ/sPLWMfFXMfGYcfGaMfGccfGe8fGhsfGksfHnsjHqsjHt8nIw8rJ0MzK28vK5svJ8svJ/czKYdbHZdfIaNfIb9fId9fIgNfIitfIltfJodjJrdjKudfJxNXJzNPI1tPH49PF8dLE/tLEbeTIcOTIc+TIeOTJf+TIh+TJkOPJmePJpOLJruHJud/Jw97JydzG0tvG393G7t7F/N7EeO7Ieu7Ife7Jge7Jh+7Jje3JlezJnuzKqOzKsevLu+rLwOnIyOjI0efH3efH6ujG+uvGgvjKg/jKhvjLivnLjvjLlPfLm/jMo/fNrPfNtffOvffNwfbLyvXM0/XN3/bN7PjN+vnNeCjReynRfynRhSrRjSzSmC/SojLSrDfSuD3TwkXTzU7U11jU4V7V61zV9lnV+1jR/1/PeC/Rei/RfzDRhTHRjTLRlzXSoTfSrD3Tt0LTwUnSzFLT1lvU4GHU6l/U9lzV+lzQ/2HPeDnRezrRfzrRhTvSjTzSlz/SoUHSq0XSt0vTwVHTzFnT1mLU4GfU6WXV9WPW+mLQ/2fPd0XQekbRfkbRhUjRjUnRlUrRoE3Sq1HStVbSwFzTymPT1WvU33DV6G7U9GzV+WvQ/3DPdlPQeFTQfFTQg1XRi1fRlFjRnlvRqV7RtGPSvmjSyW7T03XU3XvU53nU8nbV+HbQ/3nPcmHPdmPQemPPgGTQiWXQkmfQnGnQp2zRsnDRvXXSx3rS0oHT3IbU5oTU8YHU9oDP/4TQbnDOcXHPdnLPfHPPhHTPjnXPmXjQpHrQr37RuoLRxYfS0IzT25LU5I/U743V9YzQ/47QWnfNYXrOaXvOdH7Of4HOioPPlYTPoIfPrIrQuI7Qw5LRzpfS2ZzT45rU7pjU9JjQ/5nQT4XMVIbMWobMY4bMb4fMfovOkJHOnJLOqZbPtZnQwJ3RzKHS16bT4aTU66LU9KLR/6PRSJTKTZTLU5TLWpTKZZTKc5XLgpfMk5rNpJ/OsaLPvqbQyqrS1q/T4K/U6a3T9K3S/63RTKTLUaTMVqTMXaTLZ6TLcaPLfaPLiqPLmqbMrKzOuq/Qx7PQ1LfT37nU5rjS9LfS/7fSU7PNV7TOW7TNY7TNbLPNdrTNgbTNjLPNmbTMpLTMs7fOxLzQ0sHT3sPU58LT9MHT/8LTW8POXsPPYsPOaMLPccPPesPPhcPPj8LOm8PPp8PPssPPv8TPz8nS3c3V6MzU88zU/87VY9LRZtLRatLRb9LRdtLRf9LRidLRk9LRntPSqtPSttTSwtXTz9XU3NfV59fV8tfV/tfVa+LTbeLTceLUduLTfOLThOLTjuLUmOPUo+PUruPVuePVxeLV0eHV2N/T49/T8N/S/t7QdO/Td/DUevDUfu/UhO/UjO/VlO/Vne/Vp+7Vse3Vu+3VxuzV0OrV1enT3+jS7OnS/ezSfvrVf/rUgvrUhvrVi/rVkfnVmPnVofnVqvjWs/jWvvjXx/fXzvfV1/fW4PbW7ffW/PnWiDveijrdjTvdkj3emT/eoEHeqUXeskneu07exFXezlze12Tf4Gzf6nDf9G7f/2vg/2vchz7cikDdjUDdkkLdmUPen0XdqEjdsUzdulHdxFjezV/e1mfe4G/e6nPf9HHf/23f/23ch0bciUjdjUndkUndmEven03dp1DdsFTeulnew1/ezGXe1mze33Te6njf83Xf/nLf/3PchlHciVPdjFPdkFTcl1Xdn1jdp1rdsF7duWLdwmfey23d1XTe33ve6X/f83zf/nrg/3nchV7ch1/cil/cj2DclWLcnWPdpWbdrmndt23dwXLdynfe1H3e3YPe6Ife8YXf/YLg/4HbgmrbhWzciGzcjW3ck27cmnDcpHLcrHXctnjdv3zdyYHd04be3Izd5pDe8I3f/Ivg/4rbf3jbgXnbhHrbiXrbkHvbl33boH7cqoHcs4TcvYfcx4zd0ZHd25Xd5Zne75ff+pTf/5Tad4Pae4Taf4XahIbai4falIjbnYrap4zbsY/bu5LcxZbc0Jrd2p7d5aPe7qDe+Z7f/p3bZYnZaYnZbYrZdYvZgY7ZjpLamJTao5barZnauJzbw5/czqPc2Kbd46ve7anf+Kff/qbbWpTXX5XYY5XYa5bYdJbYf5fZjJrZnZ/ZqaLZtaTawKjby6vb16/d4rPe7LLf9rDe/rDcV6PXW6PXX6PXZKPXbaLXdqLXg6PXkaXYoKjZsK3ZvbDaybTb1bfd4bve67vf9Lre/rrdW7HYXrLYYrLYZ7LYb7LYeLHYgrHYjbHXmLHXp7PYt7jZxrza07/c38Te6sXf8sTd/sPdYMDZY8DZZsDZbMDZc8DZe8DZhb/Zj7/Zmr/YpL/Yr7/YvsLZz8jb3czd6s/g8s7f/87eZs7aaM7aa87bcc/bd87bf8/biM/bks7anM/bp8/bss/bvc/ays/a29Td6dng89jg/9ngbN7db97dcd7ddt7dfN7dg97djN7dlt/doN/dqt7dtt/ewt/ez+Df2+Hf6OPh8+Lg/uLgc+7fdO3ed+3ffO7fgu7fiO3fke7fmu7gpO7gr+7gu+/hxu7h0u3h3Ozg5Orf7+ve/uvdevvfe/vgfvvggvvgh/vfjvvglvvgn/rgqPrhsvrhvPrhx/nh0vnh3Pjh4/ff7/jg/vrgllHpmFLpmlLpnVPpolXpqVjpr1vpt17pv2Ppx2jpz27p2HTo4Xvo64Lp9ITp/YHp/3/qllTpl1XpmlfpnVfpo1npqFrorl3otmHovmXoxmrpznDo2Hbo4Xzp6oPp84Xp/YLp/4DqlVrol1zoml3pnV3ool/pqGHpr2Tptmfovmvpxm/oznTo13ro4IDo6ofp84np/Yfp/4TqlWTplmXomWbpnWfpoWjpp2rprmzptW/ovXLpxXbpznvp14Do4Ibo6ozp847p/Yzp/4rqlG7olW7omHDom3DooHHopXPorHXotHjovHvoxH7ozYPo1Yfo343o6ZPp85Xp+5Hp/4/qkXjnk3nnlXrnmXronnzopH3nqn7nsoHnuoTnw4foy4vo1Y/o3pTo6Jjo8Zvo+5jp/5bpjoPnkITnk4XnloXnm4bnoYfnqYnnsIvnuY7nwZDnypTo1Jjo3Zvo55/o8aLo+qDp/57qio3mjI7mj4/mk4/nmJDmn5HnppPnr5Xnt5fnv5nnyJzn0p/o3KPo56fo8Kro+qfp/6Xqf5PmgpTmhpXmjJfmk5nmm5rmopvmq53mtJ/mvaHmx6Tn0afn2qrn5a7o77Ho+K/p/63qc5rldpvmeZvlfpzmhZzmj57mnKPmpqXmsKfmuqnmxKzmz6/n2bLn5LXo77np+Lfp/7XqaaXka6Xkb6XldabkfKblhablkKflm6nlqq/ltrHmwbPmzLbn2Lnn473o7sHp97/p/77qarLkbLPlb7LkdLPlerLlgbLliLLkkrLkn7PlrLblvLvmyb7m1sHn4sXo7cjp98nq/8fpbMDlbsDmcMDldcDle8Dlgb/lib/kkr/km7/kpb7kssDlw8Xm0sjn4Mzo7NDp9tLq/tHpb83ncM3mc83md83mfc3ng83ni83mlM3mnc3mps3msMzlu8zlys7m3NTn6tjp9dvr/tnpctvndNzodtvnetvngNzohtznjdzoltzooNzoqdzotNznvtznytzn1tzn6ODp9eTr/+Prdevqd+vqeevpfevpguvqievpkOvqmOvpouvqrOvqt+vpw+zqzuvp2+zq5+3r9O7s/+7sePrrefrre/rrf/nrhfrri/nrk/nrnPrspfrrsPrsuvrrxvrr0vrs3frs6Pns8vns//rspHD1pXH1p3L1qXL0rHP0sXX0t3j0vXr0xH70y4H004X024n0447z7JPz9pjz/5j0/5b0o3L0pHL0pnT1qXT1rHX0sXf0tnn0vXz0xH/0yoL00obz2orz44/z7JPz9Zjy/pnz/5f0o3f0pHj0pnj0qXn1rHr0sXv0t370vYD0w4P0yob00or02o7045Lz7Jbz9Zry/pzz/5r0o330pH70pn70qH/0rID0sIH0toP0vIX0w4jzyov00o7z2pL045Xz65nz9Z7z/p/0/530o4X0pIX0pYb0qIf0q4f0sIn0tor0u4zzwo7zypH00ZTz2pfz45vz7J/z9aPz/qP0/6H0oYz0oo3zpI30p470q4/0r4/0tZH0u5PzwpXzyZfz0Zrz2Z3z4qDz66Py9Kfz/ajz/6X0n5TzoZX0o5bzppb0qZfzrpjzs5nzuprzwJzzyJ7z0KHz2KPz4abz66nz9Kzz/a7z/6z0nZ30n570oZ70o57zp57zrJ/zsqDzuKLzv6Pzx6Xzz6fz16nz4azz6q/z87Lz/LT0/7H0maT0m6T0naX0oKXzpKXzqabzr6fztanzvarzxKzzzq3z1rDz4LLz6bXz87jz/Lr0/7j0jKXzjqbzkaf0laj0m6rzo6zzq67zsq7zurDzwrLzy7Tz1bbz37jz6Lvz8r7z+8Dz/770hq7ziK7ziq7zjq/0kq/0mbDzoLH0q7Tztrfzv7nzybrz073z3r/06ML08cTz+8f0/8b0gbjzgrjzhLnzh7jzjLjzkrjzmrnzorr0rLvzur/0xcL00cT03Mb05sjz8cz0+8/1/871gMTzgcT0hMT0hsT0isTzkMT0lsPzncT0psT0sMX0vMb0zMr02M305M/08NP1+dX0/9b1gND1gtH1hND1h9D1i9D1kND1ltD0ndD1pdD1rdD1ts/0w9D009T14df17tr1+d31/972gN72gd31hN72ht72it32kN72lt72nd72pd31rt72t972wd32y9z12d326+L2+OX2/+f3gOv3gOv2g+z3hev3iuv3j+v3luz3nez3pez3ruz3uOv3wuv3zev32Ov35Ov39e33//D4ffn3f/n4gPn3hPn3iPn3jvn4lfn3nfr4pfr4r/r4uPn3w/r4z/n42vr45vr49Pv5//v5\",warm200:\"AAAABAAADQAAFwAAJQAANQAASwAAYgAAegAAkQAApwAAugAAygAA2AAA5wAA9gAA/wAAAgYABgYAEAUAGgUAKAQAOAMATgMAZQEAfAEAkwAAqAAAugAAyQAA1wAA5gAA9QAA/wAABhICCxICEREBHBABKg4AOw0AUAsAZwkAfgcAlQUAqgMAuwIAywEA2QAA5wAA9AAA/wAADSEGESAGFh8FHh4ELR0DPxsCVBkBaxcAgRQAmBEArQ4AvgoAzAcA2gQA6AIA9gAA/wAAFDQMFzQLHTMLJjIKMTAIRC4GWiwDcCkChiYAnCMAsB8AwRsAzxYA3RAA6wsA+AYA/wIAEUwSFUsSGksRIUoQM0gOSUcMX0UJdkIGjD8DojwBtTcAxTMA1C0A4ScA7yAA/BcA/w4AAGQYAWQYCGMXIGMWNmIUTmASZV8Pe10MkloJqFcGulQEylAC2EsB5kQA9D0A/zQA/ykABXwcB3wcD3wbH3saO3oZU3kXangVgXYSmHQQrHINvm8KzmwI3WgF62QD+V4C/1YB/0wAApMfCJMfFZMeKJIdQpIcWpEbcJAZh48XnY0UsYwSwooQ0ocO4IUM7oEJ/X0H/3cF/3ADCKUgFKUgI6UgN6UfTaQeY6QdeKMbjqIZo6EYtaAWxp8U1Z0T45sR8ZkP/5YN/5IK/40IIbYjKrYiOLUiSLUgWrUfbrQegrQclrMbqbIaurEZybAX168W5q4V9KwT/6oS/6cQ/6MOPsUpRMUoTcQoWcQmacQlesQjjcMgnsIdr8IavsEZzMAY2r8X6L4W9r0V/7sU/7kT/7YRVtMvWtMvYdMva9IueNIsh9Iql9EoptEmtNEjw9Ah0M8e3s8b684X+cwV/8sU/8kT/8cSbN83b983dd82fd81iN80lN8zod4xrd4vut4tx90s1N0p4dwn7tsk+9sg/9od/9gY/9YRgOo/g+o/h+o+juo9luo8oOo7quo6tek5wek4zOk22Ok05egy8ugw/uct/+Yq/+Um/+QhlPNFlvNFmfNEnvNEpPNDrPNCtfNCvvJAyPI/0/I+3vI86vE79vE5//E2//Az//Aw/+8spfxIp/xIqfxHrfxHsvxHuPxGwPxFyPxE0vxD2/xC5fxB8Ps/+/s9//s7//s4//o1//owAAACAwABCgABFAABIgAAMwAASQAAYAAAeAAAkAAApgAAuAAAyAAA1wAA5gAA9AAA/wAAAQUFBAQEDQQEFwMDJQMCNgIBSwIAYgEAeQAAkQAApgAAuAAAxwAA1gAA5QAA8wAA/wAABRAICQ8HDw8GGg4GJwwEOQsDTgoBZQcAfAYAkwQAqAMAugEAyQEA1wAA5QAA8wAA/wAADB8MDx4MFB0LHRwKKxsIPRkGUxcDaRUCgBIAlw8AqwwAvAkAywYA2QMA5wEA9QAA/wAAFDISFzIRHDERJDAPLy4OQiwLWCoIbygFhSUDmyEBrx0AwBkAzhQA3A8A6goA9wUA/wEAEkoXFUoXGUkWIUgVMkcTSEURXkMOdUELiz4HoToEtDYCxDEB0ywA4CUA7h4A+xYA/wwAAGMdAmMcB2McH2IbNWEZTV8XZF4UelwRkVkOp1YLuVMIyU4F2EkD5UMC8zwA/jIA/ycAA3whBnwgDnsgHXsfOXodUnkbaXgZgHYXl3QUrHERvm8PzWwM3GgK6mMH+F0E/1UC/0sBAZMjBZMjFJIiJpIhQJEgWJEfb5Adho4bnI0ZsIsWwYkU0YcS4IQQ7oEN/HwL/3cI/28FBqUlD6UkIKUkNKUjS6QiYaQhd6MfjaIdoqEbtKAaxZ4Y1J0X4psV8JkT/pYR/5IP/4wMHLYqJrUpNbUoRrUnWbUmbLQkgbQhlbMfp7IdubEcyLAb168a5a4Y86wX/6oW/6cT/6MSPMUwQsUvS8QvV8QuZ8QsecQqi8MoncImrcIjvcEgy8Ad2b8b574a9b0Z/7sY/7kW/7YVVNM3WNM2X9M2adM1d9I0htIyltIwpNEus9EswdAqz88o3M8l6s4i+M0f/8sb/8kX/8cWauA/bd8+c98+e989ht88kt87n985rN44ud42xt000t0y4Nww7dwu+tsr/9oo/9gk/9Yff+tFgepFhupFjOpElepDnupCqepBtOpAwOk+y+k91+k75Og58Og3/ec1/+Yy/+Yv/+QqkvNLlPNLl/NLnPNKovNKqvNJs/NIvfNHx/JG0vJF3fJD6fJC9fFA//E+//A7//A4/+80pP1Ppf1Pp/xOq/xOsfxOt/xNv/xMx/xL0PxK2fxJ5PxI7/tG+vtF//tD//tA//o9//o5AAAHAgAGCAAFEwAEIAADMQACRwABXwAAdwAAjwAApQAAtwAAxwAA1gAA5QAA8wAA/wAAAAQLAwMKCwMJFQMIIwIHNAEFSQEDYQEBeAAAkAAApQAAtwAAxwAA1QAA5AAA8wAA/wAAAw8PBw4ODQ0NGAwMJgsKNwoITQgFZAYDewUBkgMApwIAuQEAyAAA1gAA5AAA8gAA/wAACh0TDRwSExwSGxsQKRkPPBcMURYJaBMGfxEDlQ4BqgsAuwcAygUA2AIA5gEA9AAA/wAAEzAYFjAYGy8XIy4WLSwUQSoSVygPbSYLgyMHmh8ErhsCvhcBzRMA2w4A6QkA9gQA/wEAEUkeFEgeGUgdIUccMEUaRkQYXUIVcz8SijwOoDgKszQHwy8E0SoC3yQB7RwA+hQA/wsAAWIjAmIjBWIiHGEhM2AfS14dYl0beVsYkFgVplUSuFEOyE0L1kgI5EIG8joD/jAB/yUAAnsmBXsmDXsmG3olN3kjUHgiaHcffnUdlXMaqnEYvG4VzGsS22cQ6WIN91wK/1QH/0oEAZIqApIpEZIoI5IoPZEmVpAlbY8jhI4hm40fr4sdwIkbz4cZ3oQX7IAV+nwS/3YP/24MA6UvCKUuGqUuMKUsSKQrX6QpdaMmi6IkoKEis6Agw54f0p0e4Zsc75ga/ZUY/5EW/4wUErY1H7U1MLU0QrUzVrUxarQwfrQtk7MrprIot7ElxrAj1a8h460f8awe/qod/6cb/6MZNsU8PcU8RsQ7VMQ6ZMQ5dsQ3icM1m8Izq8Ixu8EuycAs178q5r4n870k/7sh/7ke/7YcUNNCVNNCW9NCZtNBdNI/g9I+k9I8otE7sdE5v9A3zc812s8z6M4x9s0u/8sr/8kn/8cjZuBJauBJcOBIeN9Ig99HkN9Fnd9Eqt5Dt95BxN5A0N0+3tw869w6+Ns4/9o1/9gy/9cufOtQfutQg+tPiepPkupOnOpNpupMsupLvepKyelI1elH4uhF7uhD++dB/+c//+Y8/+U5j/NWkfNWlPNVmfNVoPNUqPNUsfNTuvNSxfNR0PJQ2/JO5/JN8/FL/vFK//FH//BF/+9Bof1Zo/1Zpf1Yqf1Yrv1Ytf1XvPxWxfxWzvxV1/xU4vxS7fxR+PtQ//tO//tM//tJ//pFAAAOAQANBwAMEQALHgAJMAAHRgAFXgACdgABjgAApAAAtgAAxgAA1QAA5AAA8gAA/wAAAAMTAgMSCQIREwIQIQIPMgEMSAEJXwAGdwADjgABpAAAtQAAxQAA1AAA4wAA8gAA/wAAAg0XBQ0WCwwVFgsUJAoTNQgQSwcNYgYJeQQGkQMDpgIBtwAAxgAA1QAA4wAA8QAA/wAACBsbCxsbERoaGRkZJxcXOhYVTxQRZhIOfQ8KlAwHqAkEuQYCyAQB1wIA5QEA8gAA/wAAES4hFC4gGC0gICwfKyodPygaVSYXayQUgiEQmB0NrBkJvRUGyxEE2QwC5wcB9AMA/wAAEUcnFEcnGUYmIEUlLkMjREIhW0AecT0biDoXnjYUsTIRwS0N0CgK3SIH6xoE+BIC/wkBAWEsAmEsBGArF18qMF4pSF0nYFskd1kijlYfpFMbtk8ZxksW1EYT4j8Q8DgM/C4I/yMEAHoyAnoxC3oxGXkvMnguTXcsZXYqfHQnk3IlqHAium0gymke2GUb52EZ9FoW/1IT/0cOAJI4AJI3CpI3H5E1OZA0U5Ayao8vgo0tmIwqrIoovogmzYYk3IMi6n8g+Hse/3Ub/20YAKU+BKU9EqU9KqQ8Q6Q6W6M4cqM2iKI0naExsJ8uwZ4s0Jwp3pon7Jgl+5Uj/5Eh/4sfCLVEEbVEJ7VDPLVCUbVBZrQ/e7Q9kLM7o7I5tLE2xLA00q8y4a0w76wt/Kkq/6Yn/6IkLcVLNcVKQMVKTsRJX8RIcsRGhcNEl8NDqMJBuME/x8A91b8747458b02/rs0/7kx/7YtSdNRTtNRVtNQYdNQb9JPf9JNkNJMn9FKrtFJvNBHytBF2M9E5c5C880//8s9/8k6/8c3YeBYZeBXa+BXdOBWf99VjN9Umd9Tpt9StN5Rwd5Pzt1O291M6NxK9ttI/9pG/9lE/9dAeOteeutef+tehetdjutcmOpco+pbrupauupYxulX0ulW3+lU7OhT+OhR/+dP/+ZN/+VKi/RkjfRkkPRjlfRjnPNipPNirfNht/NgwvNfzfNe2PJd5PJc8PJa/PFY//FX//BV//BSnf1nn/1nof1mpf1mq/1msf1luf1kwv1ky/xj1fxi3/xh6vxf9vxe//xc//tb//tY//pVAAAYAQAWBQAVDwAUHAATLgAQRAANXAAKdAAHjAAEogACtQABxQAA1AAA4gAA8QAA/wAAAAMdAQIcBwIbEQIaHgEZMAEWRQATXQAQdQAMjAAIogAFtAADxAAC0wAB4QAA8AAA/gAAAQwhBAsgCQogEwofIQgdMwcbSQYYYAUVdwMRjwINpAEJtQAGxAAE0wAD4QAB8AAA/QAABhkmCRklDhglFhckJRYiNxQgTRIdZBAaew4WkgsSpggPtwULxgMJ1QEG4gAE8AAC/QABDiwsESwrFSsrHSoqKCgoPCYmUiQjaSIgfx8clhsZqhcVuhMSyQ8P1woM5QYJ8gIG/gADEUUyE0QyF0QxHUMwK0EvQT8tWD0qbjsohTgkmzQhri8evisbzSUY2h8U6BgR9RAN/wgJAl86Al85BV85EF44K102RVs0XVkydFcvi1QsoVEps00mw0kk0kMh3z0e7TUb+SwX/yASAHlBAHlBCHlAFng/LXc9SXY7YXU5eXM2kHE0pW4xt2sux2gs1mQq5F8n8Vgl/lAh/0UdAJFIAJFIApFHGJBGMpBFTo9DZo5Bfo0+lYs7qYk5uoc2yoU02YIy534v9Xks/3Qq/2snAKVPAKVOB6ROIaRNPKRLVqNKbaJIhKFFmqBDrZ9BvZ0/zJw925o66Zc4+JQ1/5Ay/4ovALVVBrVVGLVUM7VTSrVSYbRRd7NPjLNNn7JLsLFJwLBHz65F3a1D7KtB+qk+/6Y7/6I4H8VbKcVbN8VbSMRaWcRZbcRXgcNWk8JUpMJStMFRw8BP0b9N4L5L7r1J+7tH/7lF/7ZCQNNiRtNiT9NhW9NhatNge9Jei9Jdm9FbqtFauNBZx9BX1M9W4s5U8M1S/ctQ/8lO/8dLW+BoX+BoZeBobuBneuBmh99lld9kot9jsN5ivd5hyt1f191e5dxc8ttb/tpZ/9lW/9dUcutvdOtueetugOtuiettk+tsnuprqupqtuppwupoz+ln2+lm6Ohk9ehj/+dh/+Zf/+VchvR0iPR0i/RzkfRzmPRzoPRyqfNxs/NwvvNwyfNv1fNt4PJs7fJr+fJp//Fo//Fl//Bjmf13mv12nf12of12pv12rf11tf10vv10x/1z0f1y3P1x5/xw8vxu/fxt//xr//tp//tmAAAiAAAhAwAgDAAfGgAeKwAcQQAZWgAWcgATigAPoAAMswAJwwAH0gAF4QAD7wAC/QABAAIpAAIoBAInDQEmGwElLQAjQgAgWgAccgAZigAWnwASsQAPwgAN0AAK4AAI7gAF/AAEAAsuAgotBgksDwgsHgcqMAYoRQUlXQQidAMfjAEboQEYsgAVwgAS0AAP3wAM7QAJ+wAHAxgzBhczChYyExUxIRQwNBIuSRArYA4oeAwljwkhowYetAQbwwMY0gEW4AAS7QAP+wALCio6DSk6Eig5GSc4JCY3OCQ1TiIyZR8vfBwskxkpphUltxEjxg0g1Agd4gUa7wIW+wASDkJCEEJCFEFBG0BAKD8/PT09VDs6azg4gjU0mDExqywuuygrySIo1xwl5RYi8g4f/gYbAl1KA11KBlxJDFxJJVpHQFlFWVdDcFVAh1I9nU46r0o3wEY1zkAy3Dov6TIs9igo/x0kAHhSAHhSBXdRE3dQJnZPQ3RNXXNLdXFIjG9FomxDtGlAxGY+0mI84F057lY2+00z/0IvAJFaAJBZAJBZD5BYKo9WSI5VYo1TeoxQkYpOpohLt4ZJx4RH1YFF5H1D8nhA/3I9/2k5AKRgAKRgAKRfFKReNKNdUKNcaKJagKFYlqBWqZ5Tup1SyZtQ2JlO5pZM9JNJ/49G/4lEALVnALVnB7VmJ7VlQrRkW7RjcbNhh7Jfm7JdrLFbvK9ay65Y2q1W6KtU9qhS/6VQ/6FNEsVtGMVtKsRsPsRsUsRrZ8NpfMNoj8JmoMJlsMFjv8Bizr9g3L5f6rxd+Ltb/7hY/7VWNdN0O9NzRtNzU9NyY9NxddJwhtJvltFupdFttNBrwtBq0M9o3s5n7M1l+stj/8lh/8dfUuB6V+B6XuB5aOB5dOB4gt93kN92nd91q950ud5zxt1x091w4dxv79tt/Npr/9lp/9dna+t/but/c+t/eut/g+t+jut9met8pet8sep7vup6y+p41+l35Ol28eh0/edz/+Zx/+VvgPSEgvSEhvSDi/SDkvSDm/SCpPSBr/SBufOAxfN/0fN+3fN86fJ79fJ6//J4//F2//B0k/6Glf6GmP6GnP6Gof6FqP6FsP6Euf2Dw/2Dzf2C2P2B4/2A7/1++vx9//x8//x6//t3AAAwAAAvAgAuCAAtFgAsKAArPgAoVwAlcAAiiAAengAbsAAYwAAWzwAU3gAR7QAO+wALAAI3AAE3AgE2CQE1FwE0KQAyPwAwVwAtbwAphwAmnAAjrwAgvwAezgAc3QAZ6wAW+QATAAk9AQk9Awg8Cwc7GQY6LAU4QQQ2WQMzcQIwiAEsnQAprwAmvgAkzQAh3AAe6gAc+AAZARVDAxVDBxRDDhNCHBJALxA/RQ48XAw5dAo2iwczoAUwsQMtwAEqzgAo3AAl6gAh9wAeBidLCCZKDSVKFSRJICNINCFGSh9DYRxAeBk9jxY6oxI3sw80wgsy0Acv3gMs6wEp+AAlCj9TDD9TED5SFz1RJTtQODpOUDdMZzVJfjFGlC1DpylAtyQ9xh861Bk44RM17gsx+gUuAltcA1pbBlpbDFlaHlhZO1ZXVFRUbFJShE9PmUtMrEdJvEJHyj1E2DZB5S4+8iU7/Rk3AHZkAHZkAHVjDXViIXRhPXJfWHFdcW9aiG1YnmpVsGdTwGNRz19O3VpM6lNJ90pG/z5CAI9sAI9rAI9rBI5qIo5oQY1nXIxldYpjjYlgoodes4Vcw4Ja0n9Y4HtW7nZT/G9R/2ZNAKRzAKNyAKNyBaNxKqJwSKJuY6Fse6Bqkp9opZ1mtpxkxZpj1Jhh45Vf8ZJd/o1a/4dXALV5ALV5ALR4E7R4OLR2U7N1bLNzgrJyl7FwqLBuuK9tx65r1qxp5Kpo86hm/6Vj/6BhCMR/DMR/GcR/M8R+SsR9YMN8dsN6isJ5nMF3rMF2u8B1yr9z2L1x5rxw9Lpu/7hs/7RqJdOGLtOGOtOFStOEXNOEbtKCgNKBkdGAodF/sNB9vs98zM972s556Mx49st2/8l0/8dySOCLTeCLVeCKYOCKbeCJfOCIit+HmN+Gpt6FtN6Ewt2D0N2B3dyA69t++Np9/9l7/9d5ZOyQZ+yQbOyPc+yPfeuOieuOlOuNoOuMreuLuuqKx+qJ0+mI4emG7uiF+uiD/+eC/+WAe/WUfPWUgPWUhfWTjfSTlvSSn/SSqvSRtfSQwfSPzfOO2fON5fOL8vKK/fKJ//GH//CFjv6WkP6Wkv6Wl/6VnP6VpP6VrP6Utf6Tv/6Syf6S1P2R4P2Q6/2O9v2N//2M//yK//yIAABAAAA/AAA/BQA+EgA9JAA7OwA5UwA2bAAzhQAwmgAtrQArvQApzAAm2wAk6gAh+AAfAAFIAAFIAQBHBQBHEgBFJABEOgBBUgA+awA7gwA4mQA1qwAzuwAxygAv2gAs6AAq9gAnAAdOAAdOAQZNBgVNFAVMJwRKPQNHVQJFbQFChQA+mgA7qwA4uwA2ygA02QAy5wAv9QAtABNVARJUAxFUChBTFw9SKg5QQAxOWApLcAhIhwZFnARCrQI/vAE9ywA62QA45wA19AAyAyNcBCNcCCJbECFbGyBZLx5XRRxVXBlSdBZPixNMnxBJrwxHvghEzAVC2gI/6AA89AA5BjxkCDtkDTpkFDljIjhhMzZfSzNdYjFaei1XkClUoyVRsyBPwhtN0BZK3Q9H6glE9gNBA1dtBFdtB1ZsDFZsGFRqNlNoT1FmaE5jgEthlUdeqENbuD5ZxjhW1DJU4SpR7iBN+RVKAHN2AHN2AHN1BHJ0G3FzN3BxU25vbGxshWpqmmdnrGRlvGBjy1th2VZe5k9c80VY/jlVAI5+AI5+AI19AI18F4x7OYt5V4p3cYl1iIdznYVxr4Jvv39tznxr3Hhp6nNm+Gxk/2NgAKOFAKKFAKKEAKKDHKGCQKGBXaB/dp99jZ57oJx5sZp3wZl10JZ035Ry7ZBw+4tu/4VrALSLALSLALSLA7SKLLOJS7OHZrKGfbGEkrCCpK+AtK5/w6190qt84al676Z4/KN2/590AcSRBMSQC8SQIsOPQMOOWcONcMKMhMKKl8GJp8CHt7+Gxr6E1L2D47uB8bl//rd9/7R7GNOWHtOWLdOWQNOVVNKUZ9KTetKSjNGQnNGPq9COus+NyM6L182K5cyI88uG/8mE/8aCPeCbQ+CbTOCbWOCaZuCZduCYhd+XlN+Wot6VsN6Uvt2TzN2R2tyQ59uP9dqN/9mL/9aJXOyfX+yfZeyfbuyed+yeg+udj+ucnOubqeuatuqZw+qY0OmX3emW6uiU9+iT/+eR/+WPdPWjd/WjevWigPWiiPWikfWhm/SgpfSfsfSevfSdyfSc1vOb4vOa7vOZ+vKX//KW//GUif+liv+kjf+kkv+kmP+kn/6jp/6isf6iu/6hxf6g0f6f3P6e6P2d8/2b/v2a//2Z//yXAABSAABRAABRAgBQDQBPHwBONgBLTwBIaABGgQBClwBAqQA9ugA7yQA52AA35wA19QAyAABaAABZAABZAgBYDQBXHwBVNgBTTgBQZwBNgABLlQBIqABGuABExwBC1gBA5QA98wA7AAVgAAVfAARfAwReDwNdIgNcOAJZUAFWaABTgQBQlgBNpwBLtwBJxgBH1QBF5ABD8gBAABBmAA9mAQ9lBg5lEQxkJQtiPAlfUwhdawZagwRXlwNUqQFSuABPxgBN1QBK4wBI8QBGASBtAh9tBB5tCx1sFhxrKRppQBhmWBVkbxNhhhBemg1bqwlZugZXyANU1gFS5ABP8ABMBDd2BTd2CTZ1ETV0HjNzLzFxRi9vXSxsdShpiyRmniBjrhxhvRdfyxFc2Qta5gZX8gJUBFN/BVN/BlJ+DFF9F1B8ME56Skx4Y0l1e0ZykUJwoz5tszlrwjNo0Cxm3SRj6Rtg9RFcAHCIAHCHAG+HAG+GFG6FMGyDTmuBZ2h+gGZ8lmN5qGB3uFx1x1dz1VFw4klt7z9q+jNnAIyQAIuPAIuPAIuOB4qNMYmLUYiJbIaHhISEmYKCq3+Au3x+ynl82XV65m949Gh1/15yAKGWAKGVAKGVAKCUC6CTN5+RVp6QcZ2OiJyLnJqJrZmIvZeGzJSE25KC6Y6A94l+/4J7ALOcALObALObALOaHbKZQ7KXX7GWeLCUja+SoK6QsK2PwKuNzqqM3aiK66WI+aKG/52DAMOhAMOhBMOgFcOfNsOeUsKdasKbf8GaksCYo7+Xs76Vwr2U0byS37qR7biP+7aN/7KLC9OlEtOlHtKlNdKkTNKjYdKiddGhh9GfmNCep8+dts+bxc6a082Y4syX8MqV/ciT/8WRMeCqOOCpQuCpUOCoYOCocN+ngN+mj9+knt6jrN6iut2hyN2f1tye5Nud8tqb/tiZ/9aYVOytWOytXuytZ+yscuyrfuuri+uql+uppOuosuqnv+qmzeqk2umj5+ii9Oig/+ef/+WdbvWwcfWwdfWwe/WvgvWvjPWulvWtofStrfSsufSrxfSq0vSo3/On6/Om9/Kk//Kj//GihP+yhv+xiP+xjf+xk/+xm/+wo/+vrf+vt/+uwv6tzf6s2f6r5f6q8P6p+/2n//2m//2kAABjAABjAABiAABiCABhGwBgMgBdSwBbZQBYfgBVkwBSpgBQtgBOxQBM1QBK4wBI8QBGAABrAABrAABqAABqBwBpGgBnMQBlSgBjYwBgfABdkgBapABZtABXwwBV0wBT4QBR7wBPAARxAANxAANxAQJwCQJvHAFtMgFrSwBoZABlfABikQBgowBetABcwwBa0gBY4ABW7gBUAA14AAx4AAt3Agp3Cwl1Hwh0NgdxTgVvZwRsfgJpkwFmpABktABhwgBf0QBd4ABc7gBZABt/ABp/Ahp+Bhl+ERd8JBZ7OhR4UhF1ag9yggxvlgltpwZrtgRoxAJm0gBk4ABh7QBfAjGHAzGHBzCGDi+GGi2EKSuCQCmAWCZ9cCJ6hh93mht0qhdyuRJwxw1t1Qhr4gNo7gFlAk6QBE2PB0yPC0uOGEqNKkiLRUaIXkOGdkCDjDyAnzd9rzJ7vSx4yyV22R5z5RVw8QxtAGyXAGuXAGuXAGqWCWmUKmiTSGaQY2SOfGGLkV6JpFuGtFaEw1GC0Ut/30N86zh59it2AImfAImfAIieAIidAIecKIaaS4SYZ4OWgIGTlX6Rp3yPt3mNxnWL1XCJ42qG8WOE/FiAAJ+lAJ+lAJ+kAJ+jBJ6iLp2gUJyfbJuchJqamZiYqpeXupSVyZKT2I+R5ouP9IaM/36KALKqALKqALKpALGpELGnO7CmWrCkc6+iia6gnK2fraudvKqby6ia2qaY6aOW9qCU/5uSAMKvAMKuAMKuC8KtKsKsSsGrZcGpe8Cnjr+moL6ksL2jv7yhzrug3bme67ec+LSa/7GYANKzBtKyEtKyKNKxQ9GwW9GvcNGug9CslM+rpM+qs86ows2n0Myl38uk7cmi+seh/8SfJOC2K+C2OOC2SOC1Wd+0at+ze9+yi96xmt6wqN2vt92txdys09ur4dqp79mo/Nim/9WkTOy5UOy5V+y5Yey4bOy3eOu3huu2k+u1oOu0ruqzvOqyyuqw1+mv5eiu8uis/eer/+WpaPW8a/W7b/W7dfW7ffW6h/W6kvW5nfW4qfS3tfS2wvS1z/S03POz6fOy9fKw//Kv//Guf/+9gf+9g/+9iP+9jv+8lv+8n/+7qf+7s/+6vv+5yv+41v634v627f60+P6z//2y//2wAAB1AAB1AAB1AAB0BAB0FQByLQBwRgBtYABqeQBnkABlogBjsgBhwgBf0QBd4ABc7gBZAAB9AAB9AAB9AAB8BAB7FAB6KwB4RAB1XwByeABvjgBtoABrsQBpwABnzwBl3gBk7ABiAAKDAAKDAAKCAAGCBAGBFgB/LQB9RQB6XwB3eAB0jgByoABwsABuvwBszgBq3QBo6wBmAAmJAAmIAAiIAAeIBgaGGQWFMASCSAN/YQJ8egF5jgB3oAB0sAByvwBxzgBv3QBt6gBrABaPABaPABWOAhSOCxOMHhGLNQ+ITQ2FZQqCfQh/kQV9ogN6sQJ4wAB2zgBz3ABx6gBvACuWASqVAymVCCiUFCaTIyWROyKPUx+MaxyJghmGlRWDphGBtQ1+wwh80QR63gF36gB0AUeeA0edBkadDUWcF0ObJ0GZQD+WWjyTcjiQiDSOmzCLqyuIuSWGxx6D1ReB4g9+7Qd7AGemAGelAGalAGWkAmSiJWOgRGGeX16ceFyZjliWoFSUsFCRv0qPzUOM2zuK5zCG8iODAIWtAIWsAIWsAISrAIOpH4KoRoClY3+jfH2hknqepHectHSaw3CY0muW4GWT7V2Q+VGNAJ2yAJ2yAJ2xAJyxBJyvJJuuS5qsaJmpgZenlZalp5Sjt5Ghxo+f1Yud44eb8YKZ/XmWALC3ALC3ALC2ALC1C6+0Mq+yVK6xb62vhaytmKurqampuaioyaam16Sk5qGi9J2g/5ieAMG7AMG6AMG6BMG5IMG4Q8C3X7+1dr+0ir6ynL2wrLyvvLuty7ms2riq6Lao9rOm/6+kANG+ANG+B9G+HdG9OtG8VNC7atC6fs+4kM+3oM62sM20v8yzzsux3Mqw6siu98as/8OrGd/CH9/CK9/BPt/BUt/AZN+/dt6+ht69lt28pd26tNy5wty40du239q17Nmz+dey/9WwQuzFR+zFT+zEWezEZezDc+vCgevCj+vBnOvAquq/uOq9xum81Om74ui57+e4++a3/+W1YfXHZPXHaPXHb/XHePXGgvXGjfXFmfXEpfXDsvTCv/TBzPTA2fO/5vO+8vO8/fK7//G6ef/Je//Jfv/Jg//Jif/Ikv/Im//Hpf/Hr//Gu//Fx//E0//D3//C6v7B9f6///6+//29AACHAACHAACGAACGAQCFEACEKACBQQB+XAB8dQB5jAB2ngB0rwByvgBwzgBv3QBt6wBrAACOAACOAACNAACNAQCMDgCKJgCIQACFWwCCdAB/igB9nQB7rQB5vQB3zAB12wB06QByAAGSAAGSAAGSAACRAQCQEACOJwCMQACJWwCGdACDigCBnAB/rQB9vAB7ywB52gB36AB2AAaXAAWXAAWXAASWAwSVEwOTKgKRQwGOXQCLdQCHigCFnACDrQCBvAB/ywB92gB76AB5ABGdABCdABCcAA+cBg6aGAyYLwqWSAiTYQaQeQSNjQOKngGIrgCFvACDywCB2gB/5wB9ACOjACOjACKiBCGhDh+gHh2eNhubThmZZhaWfhOSkQ+QoguNsQiLvwWJzQKG2wCE5wCBAT+qAj+qBD6pCT2oFDunJTmlPDeiVTSfbjCchCyZlyeXpyKUtR2SwxeP0RCM3gqK6gSHAGGyAGCxAGCxAV+wBV6uH1ysQFqqW1endFSlilGinUyfrUedu0GayjqY1zKV5CeS7xuOAIC4AIC4AIC4AH+3A361GH2zQXyxX3qveXisj3WqoXKosW6mwGqjz2Wh3V6e6lWc9kiYAJq+AJq+AJq9AJm8AZm7G5i5RZe3ZJW1fZSzkpKxpJCvtI6tw4ur0oep4YKn73yk+3OiAK7CAK7CAK7CAK7BBK2/Kay+Tqy8aqu6gqq4lai3pqe1tqWzxqOy1aGw456u8Zqs/ZSpAMDGAMDGAMDGAL/FFr/EO77CWb7Bcr2/h7y+mbu8qbq6ubm5yLi317a25rO087Cy/6ywANDKANDKAdDJEdDJL9DITc/GZc/Fes7EjM3Cnc3BrMzAvMu+y8q92si76Me69cS4/8G2Bt/NEd/NHt/NM9/MSd7LXd7KcN7Jgd3Ikt3HodzGsNzFv9vDztrC3NnA6ti/99a9/9S8NezQO+zQRezQUevPX+vPbevOfOvNiuvMmOrLp+rKterJw+nI0ejG3+jF7OfE+ObC/+TBWfbTXPbTYfbTafXScvXSfPXRiPXQlPXQofXPrvTOu/TNyfTM1vTK4/PJ7/PI+vLH//HFdP/Vdv/Vef/Vfv/VhP/Ujf/Ulv/ToP/Sq//Rt//Rw//Q0P/P3P/O5//N8v7L/P7K//7JAACWAACWAACWAACVAACUCgCTIgCQPQCNWACKcgCHiQCFmwCDrACBuwB/ywB92gB76AB5AACcAACcAACbAACbAACaCQCYIQCWOwCTVgCQcACNhwCLmgCIqgCGugCFyQCD2ACB5gB/AACfAACfAACfAACeAACdCgCcIgCZPACXVwCUcACRhwCOmQCMqgCKuQCIyQCG2ACE5gCDAAOkAAOkAAKjAAKjAAGiDQGgJQCdPgCaWACXcQCUhwCRmgCPqgCNuQCMyACK1wCI5QCGAAypAAupAAuoAAqnAgmmEgikKgaiQwWfXAOcdQKZiQGWmwCTqgCRuQCPyACN1wCL5QCKAByuABuuABuuARmtCBirGBaqMBSnSRKkYg+heQyejQmbngaZrQSWvAKUygCS2ACP5QCNADa1ADW1AjW0BjOzEjKyIzCwNy2tUSqqaSenfyOkkh+hohqfsRWdwBCazgqY2wWV5wGSAFm9AFm8AVi8A1e7Cla5G1S3PFG1V0+ycEuvhkesmUOqqT2nuDelxjCi0yef4B6c6xKZAHvEAHrDAHrDAHnCAHjAGXe+PXa8XHO6dXG3i261nmuyrmewvWKuzFys2lWp50um8j2iAJbJAJbJAJbIAJXHAJXGGJTEQJLCYJHAeY++j428oYu6sYi4wYW20IG03nyy7HWv+GusAKzOAKzOAKzNAKvMAavLIarJSKnHZqjFfqfDkqXCo6TAtKK+w6C90p274Zq575a3+4+1AL7SAL7SAL7RAL3QCb3PMbzOU7zMbbvKg7rJlbnHprjFtrfExbXC1bPB47G/8a69/am7AM/WAM/VAM/VA87UI87TRc7SX83Qdc3PiMzOmcvMqcrLucnJyMjI18fG5cXF88LD/r/CAN7ZAd7ZDd7YJt7YP93XVt3Wat3VfNzTjdzSndvRrdvQvNrOy9nN2djL59bK9NXJ/9LHJuvcLevcOOvbR+vbV+vaZuvZdurYherXlOrWo+rVsenUwOnTzujS3OfQ6ebP9eXO/+PMT/bfU/beWfbeYfXea/Xdd/Xdg/XckPXbnPXaqvTZt/TYxfTX0/PW4PPV7PPU9/LS//HRbf/hb//hc//geP/gf//gh//fkf/fnP/ep//ds//cv//bzP/a2P/Z5P/Y7//X+v/W//7UAACkAACjAACjAACiAAChBgCgHQCeOQCbVACXbgCVhQCSmACQqQCNuQCMyACK1wCI5QCGAACpAACpAACoAACoAACnBQClHACjNwCgUwCdbQCahACXlwCVqACTtwCRxwCP1gCN5ACMAACsAACsAACrAACrAACqBgCoHQCmOACjUwCgbQCdhACalgCYpwCWtwCUxgCT1QCR4wCPAAGvAAGvAACvAACuAACtBwCrHwCpOQCmVACjbQCghACelwCbpwCatwCYxgCW1QCU4wCSAAe0AAa0AAazAAWzAASxDAOwJAKtPQKqVwGncACkhQChlwCfpwCdtwCbxgCZ1QCX4wCWABS5ABS5ABO4ABK4AxG2Eg+0Kg2yRAuvXQmsdQapiQSmmgKkqQGhuACfxwCd1QCb4wCZACy/ACu/ACq+Aym9DSi8HCa6MiO3TCC0ZB2xexmujhWsnhGprQ2nvAilygSi1wGg5ACdAE/HAU/HAk7GBk3FDUvDHUnBOEe/U0S8bEC5gjy2lTezpTGxtCuvwiSszxyp3BOm5wqjAHPOAHPOAHPNAHLMAHHLF2/JOW7HWGvEcmjBiGW/m2K8q126uli4yVG11kmz4z6w7jCsAJHUAJHUAJHTAJDSAI/RE47POo3NXIvLdorIi4fGnoXEroLCvn7AzXq+3HO86Wy59WG2AKnZAKjZAKjYAKjXAKfWF6bUQaXSYaTQeqPOjqLMoKDLsZ7JwJzH0JnF3pXE7JDB+Ii/ALvdALvdALvcALvbAbraJ7rZTLnXaLjVf7fUkrbSo7XQs7TPw7LN0rDM4K3K7qrI+qXGAM3hAM3gAM3gAM3fEszeO8zdWMvbcMvag8rYlcnXpsjWtsfUxcbT1MTR48LP8L/O/LzMAN3kAN3kAt3jFNzjM9ziTdzhZNvgd9veidrdmdrcqdnbudjZyNfY1tbW5dXV8tPT/dDSEevnGuvnKOvmOurmTerlXurkb+rjgOrij+nhnungrejfvOjey+fd2ebb5+Xa8+TZ/uLXQ/XqSPXpTvXpWPXpY/XocPXoffXnivXmmPXlpfTktPTjwvTiz/Ph3fPf6fLe9fLd//DcZf/sZ//sa//rcf/ref/rgv/qjP/ql//po//or//nvP/myf/l1f/k4f/j7f/i9//h//7fAACwAACvAACvAACuAACuAwCsGACqNACnUACkawChggCelgCcpwCatgCYxgCW1QCU4wCSAAC1AAC1AAC0AAC0AACzAgCxFwCvMgCsTgCpaQCmgQCjlAChpQCftQCdxACb1ACZ4gCYAAC4AAC3AAC3AAC3AAC2AgC0FwCyMwCvTgCsaQCpgACmlACkpQCitACgxACe0wCd4QCbAAC7AAC6AAC6AAC6AAC5AwC3GQC1NACyTwCvagCsgQCplACnpACltACjxACh0wCg4QCeAAK/AAK+AAK+AAK9AAG8BgG6HQC4NwC1UgCyawCvggCslACqpQCotACnxACl0wCj4QChAA3DAAzDAAzDAAvCAQrBDAi/JAe8PgW5VwS2cAKzhAGwlgCupQCstQCqxACo0wCm4QClACDJACDJAB/IAB7HBRzGFRvELBjBRha+XxO7dg+4iQy2mgmzqQWxuAOvxgGs1ACq4QCoAELRAELQAkHQBUDPDj7NHTzLMznITjbFZzLCfS6/kCm9oCS6rx64vRi1yxCz2Aqw5AOtAGrYAGnYAGnXAGjWAGfVDmXTNWPQVGDObl3LhFnIl1XGp1DDtkrBxUO+0jq83y656iC1AIvfAIrfAIreAIndAInbA4faNYbXV4TVcoLTiH/Qm3zOq3nMunTKym/I2GjF5mDD8VO/AKTkAKTkAKTjAKPiAKPhBqLfOqHdXJ/bdp7Zi5zXnZrVrpjTvZXRzZLP3I7N6ojL9n/JALjoALjnALjnALjmALflFLfjRbbhY7XgerTejrPcn7HasLDZwK7Xz6vW3qnU7KXS+KDQAMvrAMrrAMrrAMrqAsrpLsnnUcnmasjkf8fjkcbhosXgssTewsLd0cHb4L/a7rzY+rjWANvvANvuANvuAtvtIdrsQ9rrXNrqcdnphNnoldjmpdfltdbkxdXi1NTh4tLf79De+87cAeryAurxDurxKurxQenwVunvaOnueuntiujsmujrqufpuefoyObn1uXl5OTk8ePj/OHhM/X0OPX0QfX0TfXzWvXzZ/XydvXxhPTwk/TvofTur/TtvvPszPPr2vLq5/Lo8vHn/vDmW//2Xv/2Yv/2af/2cv/1e//1hv/0kv/znv/yq//xuP/wxf/v0v/u3v/t6v/s9P/r///qAAC7AAC7AAC7AAC6AAC5AAC4EgC1LgCzSwCvZwCsfwCpkwCnpACltACjwwCi0wCg4QCeAADBAADAAADAAAC/AAC+AAC9EQC6LQC4SgC1ZQCxfQCvkQCsogCqsgCpwgCn0QCl4ACjAADDAADDAADDAADCAADBAADAEQC9LQC6SgC4ZQC0fQCykQCvogCtsgCswQCq0QCo3wCmAADGAADGAADGAADFAADEAQDDEwDALwC9SwC6ZgC3fQC1kQCyoQCxsQCvwQCt0ACr3wCpAADJAADJAADJAADIAADHAgDFFwDDMgDATQC9ZwC6fgC4kQC2ogC0sQCywQCw0ACu3gCtAAbOAAXNAAXNAATMAAPLBQPJHQLHNwHEUQDAagC9gAC7kgC5ogC3sgC1wQCz0ACy3gCwABXTABTSABTSABPRARHQDRDOJQ7LPwvIWAnFcAbChAS/lQK9pQG7swC5wgC20QC13wCzADLaADLZADHZATDYCi7WGizULinRSCbOYSLLdx7IihrGmxXDqhHBuAu/xwa81AK64AC3AFziAFziAVvhAVrgBFneDlfcMVTaUFHXak3UgEnRkkTOoj/MsTjJwDDHzSfE2hzB5RC+AIHpAIHoAIHoAIDnAH/lCn3jMHzhU3nebnfchHTZl3DXqGzVt2fTxmHQ1VnN4k7K7D/HAJ7uAJ7tAJ3tAJ3sAJzqBpvpMZrnVpjkcZfih5XgmpLeq5DcuozayojY2YPW53zT83LRALTyALTyALTxALPwALPvCLLtO7HrXLDqda/oiq3mnKzkrarivajhzKXf3KLd6p7b9pfZAMf2AMf1AMf1AMb0AMbzHcXxSMXwZMTuesPtjcLrn8Hpr8Dov77mz7zl3rrj7Lfh+LLfANn5ANj4ANj4ANj3Bdj2Ntf1VNf0a9byf9bxkdXwotTustPtwtLr0dHq4M/o7c3n+cnlAOj7AOj7AOj7Cej6MOj5Suj4YOf3c+f2hef1leb0peXzteXxxeTw0+Pu4uLt7uDs+t7qEPX9HfX9LPX9PfT9TvT8XvT7bvT6ffT6jfP5nPP3q/P2uvL1yfL01/Hy5PHx8PDw/O/vTv//Uv//V///X///aP/+c//+f//9jP/8mP/7pv/6s//5wf/4zv/32//25//18v/0///yAADHAADHAADGAADGAADFAADDDADBKQC+RwC7YwC4ewC1kACzoQCxsQCvwQCt0ACr3wCpAADMAADLAADLAADLAADKAADICgDGJwDDRQDAYQC9egC6jgC4oAC2rwC0vwCyzwCw3QCvAADPAADOAADOAADNAADNAADLCgDJJwDGRQDDYAC/eQC9jQC7nwC5rwC3vwC1zgCz3QCyAADRAADRAADRAADQAADPAADODADMKADJRgDFYQDCeQDAjQC+nwC8rgC6vwC4zgC23AC1AADUAADUAADUAADTAADSAADRDwDOKwDLSADIYgDFegDDjgDBnwC/rwC9vgC7zgC53AC4AAHXAAHXAAHXAADWAADVAQDTFADRLwDOSwDLZQDIfADFjwDDoADCrwDAvwC+zgC83AC7AArcAAncAAnbAAjbAAfZBQbYHQXVNgPSUQLPagHLfgDJkADGoQDEsADDvwDBzwC/3AC+ACHiACDiACDhAB/gAh3fEBvdJhnaQRbXWhPUcQ/RhAzOlQjMpQXJswLHwgDF0ADD3QDBAErqAErqAEnpAkjoCEbnF0PkLkDiSj3fZDjbejTYjC7WnCjTqyLQuhvOyBPL1QrI4QTGAHTxAHPxAHPwAHLvAHDuBm/sLWzpT2nmambjgGPhk17epFncs1PZwkvX0EHU3TXQ5yXNAJT3AJT3AJT2AJP1AJL0AZHyK5DvUY7tbYzrhInol4bmqIPkuH7ix3ng1nPd42ra71zXAK37AK37AK36AK35AKz4Bav2Mar0VqnycafwhqbumaTsqqLrup/pypzn2Zjl55Li84rgAML+AML+AML+AML9AMH8E8D6PMD5Xb/3db71ib3zm7vyrLrwvbjuzLXs27Lr6q/p9qrnANX/ANT/ANT/ANT/ANT+ItP+SdP8ZNL7edH5jND4ns/2r871v83zzsvy3cnw68fu98PsAOb/AOb/AOb/AOX/EuX/OuX/VeT/a+T+fuP9kOP8oeL7seH5wuD40d/239717dzz+NnyAPP/AfP/A/P/HPP/OfP/T/P/YvP/dPL/hfL/lfL/pvH+tfH9xfD80/D74e/67u75+uz3NP//Of//Qf//S///WP//Zf//c///gf//kP//nv//rP//u///yf//1///4///7v//////\",instant:\"CQwMDAwLEg0MGQwMJAoMMwcMRQAMWgALcAALhwAKoAAItwAHygAE3AAB7gAA/QAA/wAACg8MDQ8MEhANGxANJg4MNgoMSAMLXQALdAAKiwAJpAAIuQAFzQAD4AAA8AAA/gAA/wAAChQMDRQMEhQLHRUMKhUMOxMMTwwLZAAKegAJkwAHqgAFvgAD0AAA4gAA8gAA/wAA/wAACBwMDB0MEx4LHh8LLh8JQSAJWB0JbRYIhgcGnQAEsgACxQAA1gAA5gAA9AAA/wAA/wAAAyoKCSoKEyoKHiwILy4GRC8FXTADeTABkywAqCMAvBcAzQQA3QAA6wAA+AAA/wAA/wAAADoGADsFDToFHjwDMD8AR0MAYUUAfkcAmUcAs0gAxkQA1T4A5DkA8DQA/DAA/y4A/zEAAE4AAE8AAVAAG08AL1IARlUAYFoAelwAl18AsWEAyGMA22UA6GEA810A/VoA/1kA/1oAAGMAAGUAAGMAE2MALGQAQ2cAXGsAd3AAknMArHYAxHoA130A54AA9IEA/n0A/3sA/3sAAHcAAHgAAHcABHYAKXYAQXkAWX0Ac4EAjoYApooAvo4A05EA45QA8ZYA/ZkA/5kA/5cAAIsAAI0AAIsAAIoAJYkAQIsAWI4AcJIAiZYAopsAuZ8AzaIA36UA7aYA+acA/6gA/6oAAJ4AAJ8AAJ4AAJ0AIpwAPpsAV54Ab6EAh6UAnqgAtawAya8A2rEA6rIA9rMA/7QA/7YAALAAALAAAK8AAK4AKa0AQ6wAWq0Aca8AiLIAnbUAs7cAx7oA2LsA5rwA874A/r8A/78AAL8AAL4AEr0AKr0APrwAUbsAZLsAeLwAjb4AocAAtcIAxsMA1sUA5McA8skA/coA/8sANcsAOssAQMoASckAVskAY8gAc8cAg8gAlckAp8oAt8wAx80A1s8A49EA79IA+tQA/9UAV9cAWtcAXdYAZNUAbNUAd9UAhNQAkNQAn9QArtYAvNcAytkA19sA4twA7t4A9+AA/+EAcuQAdeUAd+QAfOQAg+MAi+MAlOMAn+IAq+MAtuQAwuUAzeYA2OcA4ukA7OsA9uwA/+4AivQ8jPQ8jfQ6kfQ4lvM1nPMzpPMwrPMttfMpvfMqx/Qs0PYu2fcx4vgz6/o29Pw5/P09Cg0PDA0PEA0OGAwPJAsQMwYPRQAPWgAOcQAOiAANoAAMtQAKyQAI3AAF7QAC/AAA/wAACg8ODRAPEhAPGg8PJg0PNgkPSAEOXgAOdAAOjQANowALuQAJzAAH3wAE8AAB/gAA/wAAChQPDRQPExUPHRUPKxQPPBIPTwsPZQAOewANkwALqgAKvgAI0QAF4gAC8QAA/wAA/wAACB0QDR4QFB4PHx8OLyAOQh8OWBwNbxUMhwULngAJsgAHxQAF1wAB5gAA9QAA/wAA/wAAAioOCCsOEysOHywNMS8LRTAJYDAIfDAIlSsFqSIDvRYAzgIA3gAA7AAA+QAA/wAA/wAAADsLAD0LDj0KHz4JMkEGSUQBY0cAgEgAnEkAtUoAx0QA1z4A5TgA8TMA/TAA/y4A/zAAAFAEAFIDAVIDHFEBMVMASFcAYlsAfl4AmmEAtGMAymUA3GYA6WIA9F0A/loA/1kA/1oAAGUAAGcAAGYAFWUALmYARmkAX20AenIAlHUAr3kAxnwA2X4A6YEA9YIA/34A/3sA/3sAAHoAAHoAAHoABXkAKngARHwAXIAAdoQAkIkAqY0AwJAA1ZMA5ZUA8pgA/psA/5kA/5gAAI4AAJAAAI8AAI0AJ4wAQo4AWpEAc5UAjJkApZ4Au6EAz6QA4KUA7qcA+qgA/6kA/6sMAKEAAKIAAKEAAKAAJp8AQZ4AWaAAcaQAiacAoKoAt60AyrAA3LIA67MA+LQA/7UA/7YAALIAALMAALIACLAALq8ARq8AXK8Ac7EAirMAoLYAtbgAyLsA2bwA574A9L4A/8AA/8AAAMAABsEAHMAAL78AQr4AVL0AZrwAe74Aj78Ao8EAtsMAx8QA2MYA5sgA8skA/coA/8wAOMwAPswAQ8sATcsAWMoAZskAdMgAhckAl8oAqMsAucwAyM4A1tAA49EA79MA+tUA/9YAWdcAW9cAX9cAZtcAb9YAetYAhdUAkdUAoNUAr9cAvdgAytoA19sA490A7d4A+OAA/+EAc+UMduYNeOUHfOQBg+QAjOMAluQAoOMAq+QAt+QAwuUAzeYA2OgA4+oA7OsA9ewA/u4Ai/VDjPRDjvRBkfQ/l/Q+nfQ7pPQ5rPQ1tfQxvfQyx/U00Pc32fg54fg66/o99PxA/P1DCg0TDA0TEQ0UGA0TIwwUMwgURQAVWgAUcAAShwASngARswAQxwAP2wAM6wAJ+wAF/wACCg8TDA8TEhAUGhAUJg4VNgkVSQAVXQATdAATiwASogARtwAPywAO3QAM7QAI/QAF/wABChQUDRQVExUVHhQVKxMVPA8VTwgUZAAUewATkwASqAARvAAPzwAN4AAK8AAH/wAD/wAACB4WDR8XFB8WICAWMCAWQx8WWBsWbxIVhQATnAASsAAQwwAO1QAM5QAI9AAE/wAA/wAAASwXCCwWEy0WIS0VMi8USDATYjEVfC8VkykTqB8Rug8OzAAM3AAI6wAE+AAA/wAA/wAAAD4WAD8VDj8UIT8UM0ISS0UPZUgNgkgMnUkNtUgOxUEJ1TsF4zUB8C8A/CsA/ykA/yoAAFMSAFQRAVQRHVMPM1UNSlgIZFwCf18Am2IAtGMAy2UA22QA6F8A9FsA/lgA/1YA/1cAAGgLAGkJAGgJFmcGL2cER2oAYG4AfHMAlnYAsHkAxnwA2X4A6IEA9IAA/nwA/3oA/3kAAHwAAH0AAHwACHsALHoARHwAXYAAd4UAkYoAqo0AwZAA1ZMA5JUA8pgA/poB/5gA/5cAAJAAAJEAAJEAAI8AKY4AQo4AW5EAdJUAjJkApZ4Au6EAz6QA4KYA7qcA+qgA/6oH/6wlAKIAAKMAAKIAAKEAKKAAQp4AWqEAcqQAiacAoasAuK4Ay7AA3LIA67MA+LQA/7UA/7YcALMAALMAALIACrEAL7AAR7AAXK8Ac7EAirQAoLYAtbgAyLsA2bwA574A9L8A/78A/8AHAMAAB8EAHcAAML8AQ74AVb4AZr0Ae74Aj8AAo8EAtsMAyMQA2MYA5sgA8skA/csA/8wAOMwAPswAQ8sATMsAWcsAZskAdckAhMkAl8oAqMsAuMwAyM4A1s8A5NIA79MA+tQA/9YAWNgAW9gAXtcAZdcAbtYAedYAhdUAkdUAoNUArtYAvdgAytkA19sA49wA7t8A9+AA/+EAcuUfdeUgd+UcfOQYg+QUjOQNlOMCn+MAquMAtuQAwuYAzecA2OgA4ukA7OsA9ewA/u4civVLi/VKjfRIkfRHlvRFnPRDo/NArPQ9tfQ6vfQ6x/U80Pc+2fhB4vlD6vpE8/xH+/1LCQ4cCw0cEQ0dGg0eJQwdMwkdRwIeWwAfcAAfhgAfmwAcsAAYxAAX2AAV6QAT+QAR/wAOCA8bCxAdEhAeHBAgJw8eNgweSgMfXgAfcwAfiQAdoAAatQAZyAAX2wAV7AAU+wAR/wAOCBUdDBUeFBYgHhYhKhUgOxIgTgogYgAgegAdkAAcpgAaugAZzAAY3wAW7wAU/QAQ/wAOBx4gDB8hFSAiISEjMR8jQxwiVxchbQ0ghAAfmgAergAcwAAb0gAZ4wAW8wAU/wAQ/wAMAS0jCS4kFS4kIi8jNDAjSzEkYzEleywkkCQipRgguAQeyQAc2gAZ6QAW9wAT/wAP/wAKAEAkAEIlEEIlI0MkNkQjTkciaEghhEkknkkmskQjwzwg0zYd4i8a7ygW+yIS/x0M/yAHAFclAFglAlckH1cjNVYhTFofZl0cgmAanmIbtmMfy2Ui2mEf51wb8lcW/VQS/1EL/1MFAGohAG0hAGwgGGofMmkdSGsaYnAXfXQRmHcMsXoNx3wS2X8Z6IEg9H0c/XkX/3cQ/3cKAH8cAIAbAH8aC34ZLn4XRn4TX4IPeYYHk4oAq44AwpEA1ZQB5ZYO8pke/Zoo/5ci/5UfAJMUAJQVAJMTAJIRK5EORY8JXZMDdZYAjpsApp8AvaIA0KQA4aYA76gI+qke/6ku/6s4AKUJAKYJAKUGAKMDKqIARKEAW6EAc6UAiqgAoqsAuK4AzLEA3LIA67MA97QI/7Uj/7Y3ALQAALUAALQAELMAMbIASbEAXrAAdLIAi7QAoLcAtrkAybsA2b0A6L4A9L8A/8AS/8AtAMEADMIAIMEAMcAARMAAVr8AaL4Ae74Aj78Ao8EAtsMAyMQA2McA5sgA8soA/csP/8wpOc0HPs0GRM0DTcsAWcsAZ8oAdckAhMkAlsoAqMsAuM0AyM4A1tAA49IA79MA+tUB/9YiWNgbWtgaXtgXZdcSbtcNeNYEhNUAkdUAn9YArtcAvNgAytkA19sA490A7t8A9+AR/+IlcuUzdOYzd+UxfOUuguUsiuQolOQjnuMdqeMVtuQYweYbzOYd1+gg4uoi6+si9e0j/u4yifRUivRUjPRTkPRSlfRQm/ROovNMq/RJs/RGvPRGxfVHz/dK2fhL4vlO6vpQ8/xT+/1WAw4qBw4qDg0sGg0uKgowOActSQIsXQAscwAtiAAtnAAsrgArvwAp0wAj5gAe9wAc/wAaAxAqBxEsEBEtHBAwLA4xOgwuSwYtXwAtdgAujAAtngAssAArwwAn1wAh6QAf+AAc/wAbAxUsBxYuEBYvHhYyLxUzPhQxTw8vZAEvegAvjwAuoQAttAApyAAk2wAi6wAg+wAe/wAcAh8vBx8vEh8xISEzMiE2QiAzVhsybBEygAAxlQAuqQApvAAozgAm3wAk7wAh/gAe/wAcAC0yBi4zFC40Iy81NjI4SzA4YCw2dyY0jR0yoQ0wswAuxQAr1gAp5gAm9AAj/wAg/wAdAEI4AEQ5E0U6JUY6OUc6UUk5bEo6hUo8mkQ5rj02vzUz0C0w3iQt7Roq+Q0m/wQi/wQfAFg5AFs7AVs6Ilw6OFo4Tlw3aV82hWE2n2I4t2Q8yGA611s25FUy8VEu/E0q/0ol/0oiAG83AHE6AHA5Gm83NG41S20zZXEygHYwm3gvs3syyH022oA753068nk1/HUx/3Ir/3IoAIQ2AIU3AIQ2DoM0MYIySYAvYYMue4gslYwprY8ow5Ir1pQx5ZY48plA/Jg//5Q6/5M2AJczAJgzAJgyA5YwLpQuSJQsXpQpd5gnj5wlqKAivaIh0aUl4qcs76g3+qlB/6tL/6lKAKguAKguAKgtAacsLaUpR6QmXaMjdKUgjKkfo6wcua8azLEd3bMi67Qr97U3/7VD/7dRALcqALYqALYpFrUnNLQkS7MhX7IddbIai7UYobcYtrkXybsX2r0d574i9L8u/sA5/8FJAMMrEcMqIsIpNMEmRsEkV8Agab8be78Vj8AVo8IWtsMXx8UZ2Mcb5sgi8soo/cs3/8xEOc4uP84uRM0sTcwpWcwnZ8sjdcsehMkXlsoVp8sXuMwZyM4a1tAd49If79Qp+dUv/9c/V9k2Wdg2Xtg0ZNgybdgveNcshNYnkdYintYdrdcfvNghydkj1tsl4t0o7d8q9+E0/+I+cOZGcuZGdeZEeuZCgeVAieQ9k+Q6neQ2qOQxtOQywOYzzOc21+g44ek66+s79Ow8/e5HiPViifVii/Rgj/Vfk/RdmvRcofRaqvRXs/RUu/RSxfVUzvZW1/dY4Plb6fpd8vxg+/1kAA48AA09BA0/FQxDKglIPwJJUABDYgBAdgA+jAA/oAA9sQA8wgA70wA54gA28QAx/wAqABE9ABA/BRBCGBBFLA1JQgdLUgJEZABBeQBAjgA/ogA/swA9xAA81QA55AA29AAw/wAoABdBABdCCBdDGxdHLxZLRRNNVg5HaQdEfQBCkgBCpABAtgA+xgA81QA55gA09wAs/wAnAB9BACBDCyFFHiFIMiJMSCFOWx5KbhtHggpFlgBEqABBuAA+yAA82gA26wAv+wAs/wAqAC1EAC5FDi9GIC9INjJOTjRSYDJOdSxLiSFJnBBGrABBvwA70QA44gA18QAy/wAv/wAsAEJLAEROD0VPJkZQO0hTU0tWakdTgEJPlTtMqDNIuipFyx9C2w0+6QA79gA3/wAz/wAwAFtSAF1UC15UJl5UPF9VUl9TbGJSiGNUoWRWtF9TxFhO01JK4UxF7kdB+kI9/z04/z01AHFRAHRTAHRTHHVTOXRST3NPZ3ROg3hOnXtOtX1SyH9V2H1U5HdP8HNK+29F/2tA/2s9AIhQAIlRAIlRFolRNYhPTYZMY4VJfYpKl45Jr5FJxZRN1pVS5ZhY8Jha+pRU/5BO/49KAJxQAJ5RAJ1PDpxOM5pMS5hJYZZGeZpFkZ1EqaJFv6RF0aZJ4ahP7qlX+apf/6lh/6dcAKxMAKxNAKxLDatJMqlHSqhFYKZBdadAjapApK1AurBAzbJD3bNH67RN9rVW/7Zf/7dqALlJALlJALhIH7hGObdDTrZBYrQ+drM7jLU7org8t7o9ybw+2r1C6L9G88BO/sBX/8JjCsRIGsVIJ8RGOMRFScNDWsJBasA9fMA5j8A4o8I6tsQ7yMU92Mc/5clF8spJ/MtT/81eOs5JP89KRc5ITs5GWs1EaM1Cdss+hcs6lMo2pss4t806x8891tA+49JB79RH+dVL/9dZVtpOWdpOXdlNY9lLbNhJd9hGg9dDkNY/ndY6rNc7u9g9yNk/1dtB4d1E7d9H9uFO/+JVbudaceZac+ZYeeZWgOZViOVTkeVPnOVMp+RIs+RGv+VIy+dK1ulN4OpP6utR9O1S/O5ahvVwiPVwifVvjfVukvRsmPRroPRpqPRnsfRkuvRgxPVizfZk1vhn4Plp6Ptr8fxu+f1yAA5UAA5VAA1XBwtbJQdgQQBnWgBmbQBggABakQBVowBTtQBSxgBQ1gBO5QBM8wBJ/wBFABFVABFYABBaCxBdKAxiQwVoXABncABiggBckwBXpgBUuABTyABR2ABP5gBM9ABI/wBFABdYABdaABdcEBdeKxZjRhJpXwlpcwBkhQBelQBYqABWugBUygBS2QBP5wBM9QBI/wBEACFZACJbACJdFiJgLyNkSiJrZR9tdxpmiBRgmARaqwBYvABWzABT2gBP6ABL9QBF/wA/AC1YAC1ZAC9cGzFhMzNmTjVsaTVvezFoiyxiniFerwxbvwBXzgBT3ABO6gBI+gA//wA5AEFgAENjAERjH0VlN0dnUUtubU1yfklrkUFmozdhtCxdwxpY0wBQ5ABK8gBF/wBB/wA/AFlmAFxpAFxpJF5qPWBtVWJvbmVyhV9umllprFNlvU1gzkZc3T5X6jZS9i1N/yVI/yFFAHRsAHduA3dvJ3duPnhvVXlva3hthXttn31utX5xxXpt1HRo4W5j7Wld+WRX/2BS/15NAItsAIxtAIxtHI1tOo1sU45taItpf4xomZFosZNpxZVt1Zdx45dy7pJs+I5m/4pg/4hbAJ9qAKFtAKFrFqBrOqBrUZ5oZpxle5tikp9jq6NkwKZk0qdp4ahv7ap196t4/6dy/6VtAK9oALBpALBoIK9oOq5mT6xjZKtgeKlcjqtdpa5eurFfzbNi3bRm6rVs9bZz/rd6/7mCALxnALxnELtlKrtkP7phU7lfZrdceLZZjbZXorhZt7tbyb1d2b5g579k88Br/MBy/8J9G8ZlJcZkL8ZjPcViTMRgXMRdbcNbfcFWj8FTo8JVtsRXx8Va18hc5clh8Mpl+stt/813PdBlQtBkR9BjT89hW85faM5dd81ahcxWlMtRpcxSts1Uxs5W1dFZ4tNc7dRh+NZl/9dwVdpmWNtmXNtlY9pja9lhdtlfgthcj9dZnNdUqtdSudhVx9pX1NtZ4N1c699f9eFl/uJrbedub+ducudtd+dsfeZqheZokOZlmuVipeReseRavuZcyede1Ohh3+pj6exl8+1n++5uhPWAhfWBiPWAi/V+kPV9lvV7nvV5pfV3r/R0uPVxwvVxy/Zz1fh23vl45/t78Px++P6CAA1uAA1wAAxxAAp2HAV6PgCAXwCHeQCGjQB/nAB3qwBvuQBpygBo2gBn6ABj9gBh/wBeABJxABFzABB1AA93IAx7QAOBYQCIfACHjwCBngB3rQBxuwBrzABp2wBn6QBk9gBh/wBfABhyABh0ABh2ABd5JRV8RBGCZQeKfwCJkQCBnwB4rgBxvQBszQBq3ABn6gBk9wBh/wBeACJzACJ1ACN2AyN5KiN+SSOEaCCLgxqLkxCDogZ5rwByvwBuzwBr3QBo6gBk9wBg/wBbAC9yADB0ADF3DjN7MDR/TTaFazeLhTSMlS+Doyp7sSR0wQ1x0QBt3wBo7ABj+ABd/wBZAEF1AEN3AEN4FER6NEd/UEqFbU6LiE6Nl0mFpUR9tTt4xS5z0xpu4ABo7QBh+QBZ/wBSAFh6AFp8AFp9Glt+N12AUF6CbWSJiWeNmGKFqFp/uFF6x0V01Tdt4yFk8gBb/wBV/wBRAHF/AHSCAHSCIXWCPXaEV3iGb3yJh36LnHmHrnODv21+zmd43WBy6lls9lJm/0tg/0hbAI2FAI+HCY+HLZCHRZCIW5GJcJKJg5GHm5SGspaJxJiM0pSI34+C64l89oR2/4Bv/31pAKKFAKSHB6OHK6OGQ6OGWKOGbqOGgKGClKKArKWCwKeD0amI36qM6qqO9KaH/qKA/596ALKDALOFDrKELLKDQrGCWLGCa7CAfK58j615pbB7u7N9zbR/3bWD6LaJ87eO/LiT/7aPAL6BD76BIb2ANL5/Sr2AW7x+bLp6fbl3jbdyorl0t7x2yb142b985sCA8cGG+sGN/8OVJsl/MMiAPMiAR8h/U8d8YsZ6cMV3gMR0j8JvosJvtcRxx8Z018h35Mp78MuA+cyH/86QRNKASNJ/TdJ+VNF8XtB6atB4eM91hc1xlMxto8tptc1sxc9v1NFy4dN17dV699Z+/9iHV9x/Wtx/Xdt9Y9t8a9t6dtp3gdl1jtlxm9htqNdpt9hqxtpt09xw391z6t929OF8/eOBbOiEb+iEceeCduiCfOeAhOd9jeZ7mOZ4o+V1ruVwu+Vvx+dy0+h03ep35+t58e17+u+DhPaShfaShvWRifWQjvWOlPWNm/WLpPWJrfWGtvWDwPWAyfeD0/iF3fmI5fqL7vyO9/6TAAyJAAyKAAuLAAiODwOTOwCYXgCeggCmmQCiqgCbtQCRwwCJzwCC3QB+6wB8+AB6/wB5ABGKABCMABCNAA6QFQmUPgGYYQCfhACnmwCkrACctwCSxACK0ACD3gCA6wB9+AB7/wB5ABiMABiNABiOABeRHRaVQxGaZAWhhgConQCkrQCduACTxQCL0QCD3wCB7AB++QB6/wB5ACOMACSOACSPACSSJSSWRyObZyGhiBynnxKlrgKduQCTxgCL0QCE3wCB7QB++QB6/wB4ADGMADKOADOQADOSLDWWSzWbajegiTinoDSlri6duiqTxiSM0xaG4QCD7QB++gB5/wB2AECKAEONAESPBUaSMUiWT0qabE2gilGmolCmr0udu0eVxkGN1TSI4h+D7gB9+gB3/wByAFWMAFePAFePDFiPMVmRT16XbGOdiWijoWqlrWScul+VyFaP1kqJ4zqD7iJ7+wBz/wBrAG6RAHCTAHCTF3GUOHKUUXSWanaYhn2fn4Kkq3ycunSWyWmQ1l2I401/8Dt1/iFr/wdkAImWAIqXAIuXJYyYQY2ZWo+bcZGdiJSgnZiirpOevoyZzYaU23+N6HmH9HKA/2t5/2dzAKObDKWdJaWdPKWeUaaeZKafdqefiKefmKadraidwaqg0Kuj3Kif56OZ8p2S/JmK/5WEFrScJrWdMrSdQbScUbOcY7ScdLOchbOblbGXp7GVvLSYzbaa3Lef57ij8bil+rWf/7GZKr+aNMCbO7+aR7+aVb+ZZL6YdL6XhL2Vk7uRo7qOt7yQyb6T2cCW5cGa8MKg+MOl/8WsPcmYQsqZSMqYUMmXXMmWasiWeMeUhcaQlMWNocOHtMWKxsaM1smQ48uU7syY982f/8+nS9OXTtOWU9OWWtOVZtKVcNGTfNCQiM+Nlc6Jo82Es82Dw8+G0tGK4NSN69WR9daW/didXt2YYd2XY92WaNyVb9yTeNuRg9uOjtqLmtmIptiDtNh/w9qD0dyG3d6J6eCN8+GS/OOYb+iacumbc+mZd+iYfOiWheiUjeeSl+ePoeaMrOaIuOWDxOeF0eiI2+qL5uyO8O6R+O+XhPakhfakh/ajivaijvWgk/agmvWeovWcqvaZs/aXvfaTx/aT0fiW2/mZ5Puc7fyf9f6jAAugAAuiAAmjAAalAgCoOgCtXwCygQC4oQC+tAC5wwCyzACp1wCh4gCa7ACU+QCS/wCRABGiABCkAA+kAA2mDAiqPwCuYQCzgwC5ogC/tQC6xAC0zQCq2QCi4wCb7QCU+QCS/wCSABmjABikABilABenGRWrQxCvZAa0hAC5owC/tgC6xQC0zgCq2QCi4wCb7gCW+QCT/wCSACWkACWmACWnACWpIiSsRyOvZyG0hh25pBu+txG7xQG0zgCq2QCi4gCb7gCW+QCT/wCRADOkADSlADSmADWoKjWrSzauaTezhzi4pDu9tze6xTC0zi2q2Cii4iKb7gOX+gCT/wCRAEOhAEWkAEamAEeoMEmrT0uubE2yiFC3pFa8uFS7xE60zkqr2EWj4j2c7iqY+gST/wCPAFOdAFWfAFWgAFeiMFqmT16ra2Kvh2e1om26tm66wmiyzGOq1l2j4lGd70GX+imQ/wCLAGugAG2iAG2jCm2jMm6jTG+laXWrhXyxn4O2s4a4voCxyXqp1XCj4mKd7lKV+z2M/x+EAIWkAIamAIemHIenO4inVIqpa4yqgo6sm5azsZu3vJWwyIyq1YGj43Sb72OQ+1CG/z18AJ+pAKGrFKGrMaGrSaKsX6OtdaSviaaxnaizr6u2vaeyy6Gt2Jun5JWg8I2Z+4aR/4GLKrSuNbWvP7SvTbSwXrWwb7WxgLayj7aznrayq7Wxvbexzbi02rm35Laz7rKt+K2m/6qgR8GwTcGxUsGwW8GwZcCvcsCwf8CvjsCvnL+vqL2ruL6pycCs2MGv5MKz7sO39sS5/8G1U8qvVsuvW8uvYcqua8qudsqugsmtjsmsm8iqp8altMWixcel1Mqo4syr7M2w9c61/dC7XdSuYNSuY9StaNOscNOredOrhNOqkNKpmtGlps+gss6bws+e0dKh39Wl6tao89et+9mzZ96sad6ta96sb96rdd2qf92qidyoktylnNuip9qds9iYwdmYz9yc296f5+Cj8eKo+eOtc+mudequdumsfOmtgumsh+irj+ipmOinoeejq+egtuabwueZzuic2uqf5Ouh7u2k9++qh/e2iPe2ifa1i/a0j/azlPaxmvWwofauqfassfaqu/anxfekz/im2fmp4vus6/yv9P6zAAq3AAm4AAa4AAO6BgC9PgDBYgDFgQDInwDMugDQygDM1wDG3gC+6AC28QCv+QCp/wCoABC4ABC5AA66AAu8EwW/QgDBZADFggDJoADNvADRywDN2ADH3wC+6QC38QCw+gCq/wCpABq6ABm7ABm8ABe9HRW/RhDCZgbFhADJoQDNvADRywDN2ADI3wC/6AC48QCw+gCr/wCpACa5ACa7ACa8ACW9JiW/SiTBaCHFhh/Ioh/MvCTRyxzN2BLH3wy+6Ae38QGw+QCq/wCpADS4ADW5ADW6ADW7LDa+TTbAazfEhznHojzLu0LQyz/M1znH3jW+5zG38C2w+SSr/wOpAEW3AEe6AEe6AEi7Mkm9UEq/bU3DiFHGolbLu17PylvM1VXH3VG+5ky370aw+Tqr/yeoAFWyAFe0AFi1AFm3M1y6UV69bGLAh2bEoW3IuHXNx3XL027G22m942K27Vuv+Euq/zunAGewAGmyAGmxAGmxLWy0TXC4a3a9hXvBnYLFtIrKxIzKzoXE13684Xe17Guv+Fqp/0mjAIGzAIK0AIK0C4K0NIO1TYS2ZYa3gIy8mpTBsZzHwZ/Jy5nD1JK734m17Hqu+Gqm/1ifAJu3AJy4AJy4J5y4QZ24WJ65bZ+6gqG8lqK+rarEwK7JyKnC06G73pa064iq+Hag/2SVFbG7JbG7MrG7QrG7VLG8Z7K9erO+jbTAn7XCsLfEv7nHyrXE1bG+4Ku466Wx95+q/5qjTcG/U8HAWcHAYsDAb8HBfcHCi8HDmcHEpcLEscLEu8HCysLD2MTF4sTI68LF9L6//ru6aczCa8zCbsvCc8vCe8vChMvCjsvCmcrCo8rBr8rAuci8xcm81My/4M3B6s7F89DJ+tLMb9XBctXCdNXBeNXBf9TAhtTAjtS/mNS/otO9rNK7tdC3wdC00NK33da66Ne98tnB+drGdt/Bd9/Bed7Afd/Agt+/iN6+kN69mN28ot27q9y3tNuzv9quzdyx2d605eG47+O79+TAf+rBgOrBgerBhOrAiOm/jem+k+m9nOm8pOm6rOi3tuizwOeuzOiv2Oqy4uy17O639fC8i/fGi/bFjPbEjvbEkffDl/bCnPbCo/bAqva/sfa8ufe6wve2zPi21vm44Pu76f2/8v7CAAjLAAbLAATMAADNHADPRgDRZgDTgwDWngDZuADczgDf3ADb5wDX7QDP9QDJ/QDD/wC/AA/MAA/NAA7OAArOIwTQSgDSaADUhQDXoADZuQDdzwDg3ADc5wDX7gDQ9QDJ/QDE/wC/ABrMABvOABnOABjPKhXQTRHSagnUhgDWoADZuQDczxHf3Abc5wDX7QDP9ADJ/QDE/wvAACfMACjNACfNACfOLybPUCXRbCPThyLWoSTZuSnczjTf3DDb5inX7CXP9CTJ/CLD/ye/ADbLADfLADbMAjfNNDfOUjjQbjjSiDrVoj7YuUXbzk/e2kvb5UbX60LP8z/J+zzD/z2/AEfJAEnLAEnLEUnMOUrNVUzPcE7RilHUolfXuV7azGnd2Gba42DX6VvP8VbJ+VHC/1C+AFfFAFnHAFrIEFvKOV3LVV/Nb2LPiGfSoG3VtnXYyX/c1X3Z4HbV53HO72vI92TB/16+AGfBAGnCAGnCAGvEMm3GUnHJbHXMhnrPnYHTs4nWxZPZ0pLY3IvU44XN7H7H9Xa//2q8AH7BAH7CAH7BAH7BK37CSYDDZoXHgYzMmpPPr5vTwqPX0KTY2J3S4JfM6ZDF9IW//3e7AJfEAJjEAJfEGZjEOJjEUJjFZpnFeprGlKDLrKjRwK/WzrHX1azR3aXK5pzE85C+/4K3AK3GD63GI63GN63GSq3HXq7Hca7IhK/Jl7DLqLLMvbjSzbvW07fQ3LDK5qbC8pm5/4qvQb7KR77KTb7KV77KZL7Kc77Lg77Mk7/NosDPscHQwMPTzMXV1cHR3rzM6LfH87LA/q26acvObMvOcMvOd8vOgMvPisvPlsvQoszSrczSt8zSwM3TyMvQ1M3R4NDU6dHW8c/U+svQgdbSg9bShdbRiNbRjdbRk9XRmtXRo9XRq9XQs9XQvNXPxNPLz9TK3NfN59nP79rT+NzXheDRhuDRiN/Qi9/Qj9/QlN/Qmt/Pod/Pqd/Osd/Nud7LwdzGy9zD2N/H4+HK7ePM9uXQi+rSjOvRjurRj+rQk+rQl+rQnOrPourOqOrMsOrLuOnIwOnEyei/1erD4OzF6+7I9PDMlPfUlffUlvbUl/bTmfbSnffSoffRpffQrPfPs/fNuffLwvjIy/jG1PnG3/vJ6P3M8f7PAAfbAAbbAATcAADcMgDeUQDfbQDghwDioADktwDmzADp3QHq6QDn8wDk+QDe/wHY/xXVABHcABDdAA/dCAzdNwbeVADfcADhiQDjoQDluADmzQDo3hvq6Rjn8hLk+Qve/xbY/yPVABzcABzcABzdFBrdOxfeVxTfcRDhigviogvluRPnzSLp3jDq6C7n8irk+CXe/ijY/zDVACrbACrcACrcHSncPijdWSfecifgiyfioyrkuDDmzDvo3Efp50Tn8UDj9zve/TvY/z/UADjaADnbADnbIzncQTncXDredDvfjD3ho0LjuEjlzFPo21/p5lvm71bj9VHd/VDY/1HUAErZAEvaAEvaKkvbRUzcXk7ddlDejVTgo1niuGHkymvn2Xfp5HLm7Wzi82fc+2PX/2PUAFrWAFzYAFzYJ13YQ17ZXGDbdWTci2jeoW7gtXbix4Dl1ovn4Ifl6YHh8Hrc+HXX/3LSAGrSAGzTAGzUGm3UPW/WWHLYcXbZiXvcnoHes4ngxJLj05zl3Znk5pPg7Yza9YXV/oLSAHrOAHrOAHvPAHzQMH/RT4LTa4bWhIzYm5Pbr5rewaLh0ank26jj46Lg6pza8pfU+5HQAJPQAJPQAJPQBJPQL5PQSJPQX5TRe5nUlqHYrKfcv67fz7Tj2bTi4K/e56nZ76PS+prPAKrRAKrRDanRKqnRQKnSVKrSaKrSfKvTjqvTprDYvLbdzbzh2b7i3rnd5bPX7qzR+qLNM7vUOrvUQbvUTLvTWrvUabvUebvUirvVmrzWqbzXuL7ZysPe2Mji3cLc5LvW7rLP+qfIXsjWYcnXZsnXbcnXd8nXgcnYjsnYmsnZp8ratMvbwMzdzM7f1tHh3czd5sfY8MPU+7/PftTagNXag9Xah9XajtXbltXbntXcqdbcs9fevNjfxNjey9je0tfc3Nnd5tvf793h9tvglOHdleHeluDdmeHdnODdoeHdpuHdrOHds+HdueHdwODcx+Dbzd7X19/W4+PZ7OXb9Obel+vemOvdmevdm+vdnuvdouvdpuvcq+vcsevct+vbvevaw+rXyurU1OrR3uzU6O7W8vDZnvffn/ffoPffofffo/bfpfbeqfferfjesvjctvjbvfjbwvjYyvnW0vnT3fvV5/3Y8P/bAAroAAroBAfoLQToRwDqYADrdwDsjgDtpADuuADvzAbx3CPy6TTy8zXw/DPt/y7q/zjlABXpABTpGBPpMxDpSw3qYgjreQLrjwDtpQDuuQnvzBzw3DDy6T7y8z7w/Dzu/zjq/z/lACDpAyHpIB/pOB/qTh3qZBvrehnrkBjspRzuuSPvyy/w3D7y6Evy8krv+0fu/0Pq/0jlAC3oEC7oJi3oOy7pUS3pZizqfC3rkS7spTLtuTnvy0Pw21Dx51zy8Frv+lbt/1Lp/1XlATznEjznKDznPT3oUj7oZz7pfUDqkkPrpkfsuU7uyljv2WTw5W/x723v92nt/mPp/2XlBk3mH0/nLk/mQE/nVVDoaVLofVTpklfqpl3ruWTtyW7u13rv44Tw7IDv9Xvs+3Xo/3XkAV7kGWDlK2DlPmDlU2LmZ2PmfGfokWvppHHqtnjrx4Lt1Yzu4Jbv6ZLt8ozr+Ibn/4PjAG7hAG/iHHDiNXHjTHLjY3XkeHjljn3nooPotIrpxJPr0pzs3qTu56Dt75rq9ZTm/ZHiAH3eAH/eAH/eIX/fQIHgWoXhconjiY3knpTmsJrowaHq0Kns3bDu5K3s7Kjp86Pl+5/iAJDbAJHcAJDcAI/bKpDcS5LdZ5bfgZvhmaHkrafmv63ozrPq27nt47fr67Lp8a3l+KnhAKfcAKfcAKfcHqbcN6bcTKXcYKXcdaXdj6rfqLDju7bmzLvo2cHr4sHr6Lzn77fk97LfJrjdLrneNrndQrjeUbjeYLjecbjegrjekrjeobjftrziycLm18jq4crr5sXn7b/i9rneVcbfWcfgXcbfZMbgbsbgecbghsbgksbgoMfhrcfhucfixcjj1M/n4NTq5c7m7Mfh97/ddtPieNPie9Pif9LhhdPijtPil9PiotPjrNTjt9Xkwdbly9jn1dnn3dzp5djm7dPj987ej+DkkODkkuDlleDkmuDkoODlp+Dlr+Hmt+Hmv+Lnx+PnzePo1OTn2eLl4+Tm7Ofo8+jpo+znpOznpeznp+znqeznreznsOznte3nuu3nv+3nxO3mye3mz+3k1evh3uzg6O/j8vLmqfjoqffoqffoq/jorPfor/fosvjotfjnufjnvfjnwvnmxvnlzPnk0/ri3Pvg5v3i7//lLRXzNBTyPhPzThLzXxD0cQ30hAr1lwr2qg/2vBr3zCf32zj450n58VX4+lb3/1X1/1T0NB3zPB70RR30Uhz0Yhv0cxn0hRj0mBn1qh32vCX3zDH320D451H48Vz4+lz3/1v1/1nzNyjzPyjzSCj0VCfzZCf0dCb0hif0mSj1qyz2vDT3zD732kv35lv48Wb4+WX2/2L0/2DzOzXyQjbzSjbzVjXyZTXzdjb0hzf0mTn0qz31vET2y0722lr35Wj473P493H2/271/2vzPEPxQ0TyS0TyWETyZkXzd0fziUn0mkv0q1D1vFf1y2D22Gz343n37YL39YD1/n30/3jyP1XxR1bxT1byWlbyaFfyeFnyiVvzml/zq2T0u2v0yXT11n/24Yr26pL284/1+4v0/4bxPGXwRGbwTGbwV2fwZmjwdmrxh23xmHHyqXfyuX7zx4b01I/135n16aD28Zz0+Zfz/5LwMXXuOnbuQ3buUXfuYHjucnvvhH7vloLwp4jxto7yxZby0p7z3aX056z17qfz96Py/Z7vFoTsJYXsM4XsQ4bsVoftaortfo3ukZLvo5fvs53ww6Px0Kny26/z5rb17bP09a7y+6nvAJLpAJTpEZTpL5TpRpXqXpfrdJrri57tnqPtsKjuwa7wzrPx2rjy5b707Lvz87fx+bPuAKbnAKboAqXnIKTnN6TnS6PnZaTofqjplqzrq7HsvbbuzLvw2cDx48bz68Tz8b/w97vtKLfnL7jnNrfoQLfnTrbnXbbobLXofLXnjLTno7jpuL3sycLu1sfw4s3y6c3y78jv9cPsU8XoVsXpWsXoYcXoasXodMXpgMTpjMTpmcTppsPpssPpw8fr083u39Tw6Nfy7tLv9Mzsc9LqddHpd9Lpe9LqgdHqidHqktHqm9HqptHqsNLqu9LrxdLrz9Ts3Nvv5+Dy69ru9NLqit/ri9/rjd7rkN7rld/rmt7rod7rqd/ssd/sut/sw+Hty+Lt1OPu3OXv5Onw6uPt897qnuvtnuvsn+vso+ztpuvtquvtr+vttuzuvO3uwu3uyu7v0O7v1e/v2u/u3+7s6PHt8PPvsPjvsfjvsvjwtPjvtffvuPjvu/jvvvjvwvnwxfnvyfrvzfrv0vvu1vvt3Pvr5f3r7//uVyf7Wyf8YSb7aib7dib7hCf8kyj8oiv8sjD9wDf9zkH92k395Vv972v+93P9/nT8/3X7XC/8YC/8ZS/8bi78eS78hS77ky/8ozL8sjf9wD79zkf92lP95WH97nD99nj9/nj8/3n7XTf7YTj7Zzj8bzf7ejj8hzn8lDr8oz38skL9wEj8zVD92Vv95Gn97Xf99X/9/n/8/3/7YET7ZET7aUT8cUT8e0X8iEX8lkf8pEr8sk/8wFX9zV792Wj843T87IL99In8/Ij7/4f7YVD6Y1D6alH7cVH6fFP6iFT6llf7pFr7sl/7wGX8zG382Hf84oL8647885T8+pL6/5D6Y2H6Z2L6a2L6c2P6fWT6iWX6lmj6pGv6snD6v3b7y3771of74JH76Jr78J/7+Jz6/5r5YXH5ZXL5anL5cXL5fHT5iHX5lXj5o3z6sYH6vYf6yY761Jb63p3656T676n796b6/6P5XID4YIH3ZYH3bYL4eIP3hYX4koj4oIz4rpD5u5X4x5v506H63aj55q767rL69a/5/av5UY/2Vo/2XI/2ZZD2cZH2gJP3jpb3nZr3q573uaL3xaf40az527H45bb57br69Lf5+7X4RJz0SZ31T530Wp31Z571dqD1h6L1mKX2p6j2tqz2xLD30LX42rn45L757MP588D4+r34KajyMajyOqjzR6nyV6nzaarzfKvzj670obD1srT1wbj2zrz32cH448b468v58sj4+MX3RLnxSLnyS7jxUrjyXLfyZ7fyc7XyhLXxmLfyq7rzvb/0y8P12Mj34s346tL48dL49833YcbyY8byZcXxasXyccXxesXyg8TxjcPxmMPxo8LxtsTyx8j01c724NT36dv479v49df2etPye9LyfdLygNLyhdLyi9LyktHym9HypNHyrtDyt9DywdDyz9Pz3Nr15+H37eP38971jN7yjt/yj97ykd7ylt/zmt/yoN7yqN/zr97zt97zv9/yxt/zz9/z2ODz5Of17Oz38eb0nevznezznuvzoOvzpOvzp+vzrOzzsevzuOzzvuzzxezzzO300+7z2/D14vH16vT28e/zrPf0rPj0rff0rvfzsfj0tPfzuPj0vfj0wvn0x/r1zPr10vr12Pv13fz24f315v/07//1fUX/gEb/hEb/iUb/kUf/mkn/pUz/sE//vFT/xln/0WH/22r/5HT/7ID/84v/+pH//5P/gEz/gkz/hk3/i03/kk3/m0//plH/sVX/vFn/xl//0Wb/2m//5Hn/64P/8o7++pT//5X/gVT/hFT/h1T/jVX/k1X/nFb/pln/sVz/vGD/xmb/0W3/23b/43//64n/8pP/+Zj//5n/g17/hV//iF//jV//lGD/nWH/p2P/sWb/vGv/xnH/0Hf/2n//4of/6ZD/8Zr/+Z7//57/hGr/hmr/iWr/jmv/lWz/nm7/p3H/sXT/vHj/xn3/z4P/2Yv/4ZL/6Zr/8aH/+KX//qT/hnn/iHr/i3r/kHv/l3z/nn3/p4D/sYL/u4f/xYz/z5H/15f/4J3/6KP/76n/9qz//av/hYf/iIn/i4n/j4n/lor/nYz/p47/sJH/upT/xJj/zZ3/1qH/3qb/56z/7rD/9bP//bL/hJX/h5b/iZb/jZb/lJf/nJn/pZr/r53/uaD/w6P/zKf/1ar+3q//5rL+7bf+9br//Lj+gKL/gqH/haL/iqL+kaP+maT+o6b+raf+uKr+wqz+y7D+1LP+3bf+5br+7b7+9ML/+8D+fKv9fqz9gaz+hqz9jaz9lq39oK/9q7D9trL+wLT9y7f91Lr+3b395cH+7Mb+8sj++cf+dbT8eLT8e7X8f7X9h7X9kLX8m7b9prj8srn9vrz9yL7908H93MX95Mj968z98tH9+M/9db/8dr/7eL/7e777gL37ir77lb78ob/8rsD8usL9xsX80cj82sv94s/86tT98dn999f9hMr7hsv7h8v7isr7jcn7ksn7mMj7n8j7qMj8tcn7wsv8zs782NL84db86tz97+D89d/8ktb7k9f7lNb7ltb7mdb7ndX7otX6qNX7r9X7ttT7vdP7yNT71dj83t375+L87eb89Of8nuP7nuL6oOP7oeL7pOL6p+L7q+L6seL7teH6vOL7wuL7yOH7z+D72eL75ej87e788vD8qe/7qe76qu/7q+76ru/7sO76tO/7uO/7vO/7wu/6xu/6zO/70u/62fD74O/76fP78Pj7s/r6s/v6tPr6tfr6t/r6ufv6vPv6wPv7w/z7yPz7zPz60fz61/363f774//76f/88P/8\",mono400:\"CwsLDAwMDg4OEBAQExMTFxcXHBwcIiIiKSkpMTExOTk5QkJCTExMVlZWYmJicXFxg4ODDg4OEBAQExMTFhYWGBgYHBwcICAgJiYmLS0tNDQ0PT09RUVFTk5OWVlZZmZmc3NzhYWFExMTExMTGBgYHh4eISEhJCQkKSkpLi4uNDQ0Ozs7Q0NDS0tLVFRUXl5ea2treHh4ioqKGxsbGhoaHR0dJCQkLCwsMTExNTU1OTk5Pz8/RkZGTU1NVFRUXV1dZ2dncnJygICAkpKSJiYmJSUlJSUlKioqMjIyPj4+Q0NDSUlJTk5OU1NTWVlZYGBgaGhocXFxfX19ioqKmpqaMTExMTExLy8vMzMzOjo6Q0NDUlJSWFhYXV1dY2NjaWlpbm5udnZ2f39/ioqKlpaWpaWlQEBAPz8/Pj4+PT09Q0NDTExMVlZWZmZmbW1tcnJyeXl5gICAh4eHj4+PmZmZpKSksrKyTk5OT09PTU1NSkpKTU1NVVVVXl5eampqeXl5g4ODiIiIj4+Pl5eXoKCgqampsrKyvr6+X19fX19fXV1dWlpaWVlZYGBgaGhoc3Nzfn5+i4uLmZmZn5+fpqamrq6utra2v7+/yMjIbm5ub29vbW1ta2traGhobGxsc3NzfHx8h4eHkpKSnp6erq6utLS0urq6wsLCysrK09PTgICAgYGBgICAfn5+e3t7eHh4gICAiYmJk5OTnJycp6ensbGxwMDAxsbGzMzM09PT29vbk5OTlZWVlJSUkpKSj4+PjIyMj4+Pl5eXoKCgqKiosrKyurq6wsLC0NDQ1tbW3Nzc4+Pjp6enqKiop6enpqampKSkoKCgnp6epaWlra2ttLS0vLy8xMTEysrK0dHR3d3d5OTk6enpurq6u7u7urq6ubm5t7e3tbW1srKytbW1u7u7wsLCyMjIz8/P1dXV29vb4ODg6urq7+/vzc3Nzs7Ozc3NzMzMy8vLysrKycnJx8fHy8vL0dHR1tbW29vb4ODg5eXl6urq7u7u9PT039/f4eHh4ODg4ODg39/f3d3d3Nzc29vb3d3d4ODg5OTk6Ojo6+vr7+/v8vLy9fX19/f37u7u7u7u7u7u7u7u7e3t7e3t7Ozs6+vr6+vr7Ozs7+/v8vLy9fX19vb2+fn5+/v7/Pz8DQ0NDw8PEhISEhISFRUVGRkZHx8fJCQkKysrMjIyOzs7Q0NDTExMV1dXYmJib29vgICADw8PERERExMTFRUVGRkZHR0dIiIiKCgoLy8vNjY2PT09RkZGT09PWlpaZmZmcnJygoKCGBgYFxcXGhoaHh4eIiIiJiYmKioqLy8vNTU1PDw8RERETExMVVVVX19fampqeHh4iIiIISEhICAgHx8fJiYmLS0tMTExNjY2Ozs7QEBARkZGTU1NVVVVXV1dZ2dncnJygICAkJCQKioqKSkpJycnLCwsNDQ0Pj4+Q0NDSUlJTk5OVFRUWlpaYWFhaGhocnJyfX19iYmJmZmZNTU1NTU1MzMzNDQ0Ozs7REREU1NTWFhYXV1dZGRkaWlpb29vd3d3f39/ioqKlZWVpaWlQ0NDQ0NDQkJCPz8/RERETU1NV1dXZmZmbW1tc3NzeXl5gICAh4eHj4+PmZmZpKSksrKyUVFRUlJSUFBQTk5OTk5OV1dXX19fa2treXl5g4ODiYmJj4+Pl5eXoaGhqampsbGxvr6+YWFhYWFhYGBgXV1dWlpaYWFhaWlpc3Nzfn5+jIyMmZmZn5+fpqamrq6ut7e3vr6+yMjIcHBwcnJycHBwbm5ua2trbW1tdHR0fX19iIiIk5OTn5+frq6utLS0urq6wcHBycnJ09PTgoKCg4ODgoKCgICAfX19enp6gYGBioqKk5OTnp6ep6ensbGxwMDAxsbGzMzM09PT29vblZWVmJiYlpaWlZWVkpKSjo6OkJCQmJiYoKCgqampsrKyurq6w8PD0NDQ1tbW3Nzc4+PjqKioqqqqqampp6enpaWlo6OjoKCgpqamra2ttbW1vLy8xMTEy8vL0tLS3d3d5OTk6enpu7u7vLy8u7u7u7u7ubm5t7e3tLS0tra2vLy8wsLCycnJz8/P1dXV29vb4ODg6enp7+/vzc3Nz8/Pzs7Ozs7Ozc3NzMzMysrKx8fHzMzM0dHR1tbW3Nzc4ODg5ubm6enp7u7u9PT04ODg4eHh4ODg39/f39/f3t7e3t7e3Nzc3d3d4eHh5OTk6Ojo7Ozs7+/v8vLy9PT09/f37u7u7u7u7u7u7u7u7u7u7e3t7e3t7Ozs6+vr7e3t8PDw8/Pz9fX19vb2+fn5+/v7+/v7Dw8PERERFRUVGhoaHx8fICAgIyMjJycnLi4uNDQ0PDw8RUVFTk5OWFhYZGRkcXFxgYGBERERFBQUGBgYHh4eHx8fISEhJSUlKioqMTExODg4Pz8/R0dHUVFRW1tbZmZmc3Nzg4ODGRkZGxsbHBwcHx8fIiIiJycnKysrMTExNzc3Pj4+RUVFTU1NVlZWYGBga2treHh4iYmJJycnJycnJSUlKCgoLi4uMjIyNjY2Ozs7QUFBSEhITk5OVVVVXl5eaGhoc3NzgICAkZGRMTExMDAwLi4uLy8vNjY2Pj4+RERESUlJT09PVVVVWlpaYWFhaWlpcnJyfn5+ioqKmZmZPDw8Ozs7OTk5Nzc3PT09RkZGU1NTWFhYXl5eZGRkaWlpb29vd3d3gICAi4uLlpaWpaWlSEhISEhIR0dHRERER0dHT09PWVlZaGhobm5uc3NzeXl5gICAh4eHj4+PmZmZo6OjsbGxVlZWVlZWVVVVUlJSUVFRWFhYYWFhbGxse3t7g4ODiYmJj4+PmJiYoKCgqampsrKyvr6+ZmZmZmZmZGRkYmJiXl5eYmJia2trdXV1gICAjY2NmZmZn5+fpqamrq6ut7e3vr6+yMjIdXV1dXV1dXV1cnJyb29vbm5udnZ2f39/iYmJlJSUoKCgrq6us7Ozurq6wsLCycnJ09PThoaGh4eHhoaGhISEgYGBfX19goKCi4uLlJSUnp6eqKiosrKywMDAxsbGzMzM09PT29vbmJiYmpqamZmZl5eXlZWVkpKSkZGRmZmZoaGhqampsrKyu7u7w8PD0NDQ1tbW29vb4+Pjq6urrKysq6urqampqKiopaWloqKipqamrq6utbW1vLy8xMTEy8vL0tLS3d3d5OTk6urqvLy8vr6+vb29vLy8u7u7uLi4tra2tra2vLy8wsLCycnJz8/P1dXV29vb4eHh6enp8PDwz8/P0NDQz8/Pz8/Pzs7Ozc3Ny8vLyMjIzMzM0dHR1tbW29vb4eHh5eXl6urq7e3t8/Pz4ODg4eHh4eHh4ODg39/f39/f3t7e3Nzc3d3d4eHh5OTk6Ojo7Ozs7+/v8vLy9PT09/f37+/v7+/v7+/v7+/v7u7u7e3t7e3t7Ozs6+vr7e3t8PDw8/Pz9fX19/f3+Pj4+vr6+/v7FBQUFRUVGRkZHh4eJCQkLi4uMDAwMjIyNTU1OTk5Pz8/R0dHUFBQWVlZZWVlcnJygoKCFhYWGRkZHR0dISEhJycnLi4uMDAwMjIyNTU1OTk5QkJCSkpKUlJSXFxcZ2dnc3NzhISEGhoaHBwcISEhJiYmLCwsLi4uMDAwMjIyOTk5Pz8/R0dHT09PWFhYYWFhbW1teXl5ioqKJycnKCgoKSkpKysrLi4uMjIyNjY2PDw8QkJCSEhIUFBQV1dXX19faWlpdXV1gYGBkZGRODg4ODg4Nzc3NDQ0ODg4Pz8/RUVFSkpKT09PVVVVW1tbYmJiampqc3Nzfn5+ioqKmpqaQ0NDREREQkJCQEBAQUFBSUlJU1NTWVlZX19fZGRkaWlpcHBweHh4gICAi4uLlpaWpqamUVFRUVFRT09PTExMSkpKUVFRW1tbaWlpbm5udHR0enp6gICAiIiIkJCQmpqapKSksrKyXV1dXl5eXFxcWlpaVlZWW1tbZGRkbm5ufX19hISEioqKkZGRmZmZoaGhqampsbGxvr6+bW1tbW1ta2traWlpZmZmZWVlbW1td3d3gYGBj4+PmpqaoKCgpqamr6+vt7e3vr6+yMjIe3t7e3t7enp6eHh4dXV1cnJyeHh4gYGBi4uLlZWVoqKirq6utLS0u7u7wsLCycnJ09PTi4uLjY2Ni4uLiYmJh4eHg4ODhISEjY2NlpaWoKCgqamps7OzwMDAxsbGzMzM09PT29vbnZ2dn5+fnp6enJycmpqal5eXk5OTmpqao6Ojq6urs7OzvLy8xMTE0dHR1tbW3Nzc4+Pjrq6usLCwr6+vra2trKysqqqqpqamqKior6+vtra2vb29xcXFzMzM0tLS3t7e5OTk6enpv7+/wMDAwMDAv7+/vr6+vLy8ubm5t7e3vb29w8PDycnJ0NDQ1tbW29vb4eHh6urq8PDw0dHR0tLS0dHR0NDQ0NDQzs7Ozc3NysrKzc3N0tLS19fX3Nzc4eHh5ubm6+vr7u7u9PT04uLi4+Pj4uLi4uLi4eHh4ODg39/f3t7e3d3d4eHh5eXl6Ojo7Ozs7+/v8vLy9fX19/f37+/v7+/v7+/v7+/v7+/v7u7u7e3t7e3t7Ozs7e3t7+/v8/Pz9fX19/f3+Pj4+/v7+/v7GRkZGxsbHh4eJCQkKSkpLy8vPDw8QkJCRUVFR0dHSkpKTU1NUlJSW1tbZ2dnc3NzgoKCHBwcHx8fIiIiJycnLCwsMjIyPj4+QkJCRUVFR0dHSkpKTU1NVFRUXl5eaGhoc3NzhISEISEhJCQkJycnKysrMDAwNjY2QUFBQkJCRUVFR0dHSkpKT09PWFhYY2NjbW1teXl5ioqKKCgoKCgoKioqMDAwNTU1PT09QEBAQ0NDRUVFSUlJUFBQWFhYYGBgampqdXV1gYGBkZGRNjY2Nzc3ODg4OTk5PDw8QEBARERESkpKT09PVVVVW1tbY2NjampqdHR0f39/ioqKmpqaS0tLTExMS0tLSkpKR0dHTExMVVVVWlpaXl5eZGRkaWlpcHBweHh4gYGBi4uLlpaWpaWlWVlZWVlZWVlZV1dXU1NTVVVVXl5eaWlpb29vdXV1enp6gICAh4eHkJCQmpqapKSksbGxZ2dnaGhoZ2dnZGRkYWFhXl5eZ2dncXFxf39/hISEioqKkZGRmZmZoaGhqampsrKyvb29dnZ2dnZ2dXV1cnJyb29va2trcHBwenp6hISEkpKSmpqaoaGhqKior6+vt7e3vr6+x8fHg4ODhISEg4ODgYGBfn5+e3t7e3t7g4ODjY2Nl5eXpKSkrq6utLS0u7u7wsLCysrK09PTk5OTlJSUk5OTkZGRj4+Pi4uLiIiIj4+PmJiYoaGhqqqqtLS0wcHBxsbGzMzM09PT3NzcoqKipKSkpKSkoqKioKCgnp6empqanZ2dpKSkrKystbW1vLy8xcXF0dHR19fX3Nzc4+Pjs7OztLS0s7OzsrKysbGxrq6urKysqampsLCwt7e3vr6+xsbGzMzM09PT39/f5OTk6urqw8PDxMTEw8PDwsLCwcHBv7+/vb29u7u7vr6+xMTEysrK0NDQ1tbW3Nzc4uLi6urq8PDw09PT1NTU1NTU09PT0tLS0dHRz8/Pzc3Nzc3N0tLS19fX3Nzc4eHh5ubm6urq7+/v9PT04+Pj5OTk5OTk5OTk4+Pj4uLi4eHh39/f3t7e4eHh5eXl6enp7Ozs7+/v8vLy9fX19/f38PDw8PDw8PDw8PDw7+/v8PDw7u7u7u7u7e3t7e3t8PDw8vLy9PT09/f3+Pj4+vr6/Pz8ICAgIiIiJSUlKioqMTExNzc3PDw8SkpKWVlZWlpaW1tbXl5eYWFhZmZma2trcnJygYGBJCQkJiYmKSkpLS0tNDQ0OTk5Pj4+TExMWFhYWVlZW1tbXl5eYmJiZmZmampqdHR0g4ODKioqKysrLi4uMjIyODg4PT09QkJCUFBQWFhYWlpaXFxcXl5eYmJiZmZmbm5uenp6iYmJKioqLi4uMzMzNzc3PDw8QkJCSUlJVlZWV1dXWlpaW1tbX19fYmJia2trdXV1gYGBkZGRNzc3Nzc3OTk5Ozs7Q0NDSEhIUVFRVVVVV1dXWVlZXFxcY2Nja2trdXV1f39/ioqKmZmZR0dHSEhISkpKS0tLTk5OUVFRVVVVWVlZXl5eZGRkaWlpcXFxeHh4gYGBi4uLlpaWpaWlYmJiY2NjYmJiYWFhYGBgXFxcYmJiampqcHBwdXV1enp6gICAh4eHkJCQmZmZo6OjsbGxcHBwcXFxcHBwcHBwbW1taWlpa2trdHR0gICAhYWFi4uLkpKSmJiYoKCgqKiosbGxvb29f39/f39/f39/fn5+e3t7d3d3dHR0fX19h4eHlZWVm5uboaGhqKior6+vtra2vr6+x8fHjY2Njo6OjY2Ni4uLiYmJhYWFgYGBh4eHkJCQmpqap6enr6+vtbW1vLy8w8PDysrK0tLSm5ubnJycnJycmpqamJiYlZWVkZGRkpKSm5ubpKSkra2ttra2wcHBx8fHzc3N1NTU3NzcqqqqrKysq6urqqqqqKiopqamoqKioKCgp6enrq6utra2vr6+xsbG0dHR19fX3d3d5OTkubm5ubm5uLi4uLi4t7e3tbW1srKyr6+vsrKyubm5wMDAx8fHzc3N1NTU39/f5OTk6+vrx8fHyMjIx8fHx8fHxsbGxcXFwsLCwMDAv7+/xcXFy8vL0dHR19fX3d3d4uLi6+vr8fHx1tbW19fX19fX1tbW1tbW1NTU09PT0dHRz8/P09PT2NjY3d3d4uLi5ubm6+vr7+/v9fX15eXl5eXl5eXl5eXl5eXl5OTk4+Pj4uLi4ODg4eHh5eXl6enp7e3t8PDw8/Pz9fX19/f38fHx8vLy8fHx8fHx8fHx8PDw8PDw7+/v7u7u7e3t8PDw8vLy9fX19/f3+fn5+vr6+/v7KSkpKysrLi4uMzMzOTk5QUFBRkZGSkpKWVlZaWlpcHBwcnJydXV1eXl5fn5+g4ODioqKLCwsLy8vMTExNjY2Ozs7QkJCR0dHTExMW1tba2trcHBwcnJydXV1eXl5fX19gYGBioqKMjIyNDQ0NjY2OTk5Pz8/RUVFS0tLUFBQX19fbm5ucHBwcnJydXV1eXl5fn5+g4ODi4uLNjY2OTk5Ozs7Pz8/RERES0tLUFBQVlZWZGRkb29vcHBwcnJydnZ2eXl5fn5+goKCj4+POTk5Ojo6Pj4+RUVFSkpKUVFRV1dXXV1da2trbm5ucHBwcnJydXV1eXl5f39/iYmJmJiYSEhISkpKS0tLTU1NT09PWFhYXl5eZ2dna2trbW1tb29vcnJyeHh4gYGBioqKlJSUpKSkW1tbXV1dXV1dX19fYWFhY2NjZmZmampqbm5udHR0eXl5gICAh4eHj4+PmZmZoqKir6+veXl5e3t7e3t7enp6eHh4dnZ2cnJyeHh4gYGBhoaGi4uLkZGRmJiYoKCgp6ensLCwvLy8iIiIiYmJiIiIh4eHhoaGhISEf39/gYGBioqKlpaWnJycoqKiqampr6+vtra2vb29xsbGlZWVl5eXlpaWlpaWlJSUkZGRjY2Ni4uLk5OTnZ2dqqqqsLCwtra2vLy8w8PDycnJ0tLSpKSkpaWlpaWlpKSkoqKin5+fnJycl5eXnp6epqamrq6uubm5wcHBx8fHzc3N1NTU3NzcsbGxtLS0s7OzsrKysLCwrq6uq6urp6enqampsLCwuLi4wMDAyMjI0tLS19fX3d3d5OTkv7+/wMDAv7+/v7+/vb29u7u7ubm5tra2tLS0urq6wcHByMjIzs7O1dXV39/f5OTk6urqzMzMzc3Nzc3NzMzMy8vLycnJyMjIxcXFwsLCxsbGzMzM0tLS2NjY3d3d4uLi7Ozs8fHx2tra29vb29vb2tra2dnZ2NjY1tbW1dXV09PT1NTU2dnZ3d3d4uLi5+fn6+vr7+/v9fX16Ojo6Ojo6Ojo6Ojo5+fn5ubm5ubm5eXl4+Pj4uLi5eXl6enp7e3t8PDw8/Pz9vb2+Pj48vLy8vLy8vLy8vLy8vLy8vLy8fHx8fHx8PDw7+/v8PDw8vLy9fX19/f3+fn5+/v7/Pz8MzMzNTU1Nzc3Ozs7QUFBSEhIUVFRV1dXW1tbaWlpeXl5iYmJi4uLj4+Pk5OTmJiYoKCgNzc3ODg4Ojo6Pj4+RERES0tLU1NTWFhYXFxcampqe3t7iIiIi4uLjo6OkpKSlpaWoKCgOzs7PT09Pz8/QkJCR0dHTk5OVlZWXFxcX19fbW1tfn5+iYmJi4uLjo6Ok5OTmJiYoaGhQUFBQkJCRUVFSEhITExMU1NTW1tbYGBgZGRkcnJygoKCiIiIi4uLj4+Pk5OTmJiYoKCgQkJCRUVFSkpKT09PU1NTWVlZYGBgZmZmampqeXl5hoaGiIiIi4uLj4+PkpKSlpaWnp6eSkpKTExMTU1NUFBQWVlZYGBgZmZmbGxsc3NzgICAhYWFh4eHioqKjY2NkJCQlJSUoaGhXFxcXl5eX19fYGBgY2NjZWVlbm5udHR0fX19gYGBhISEhoaGiYmJj4+Pl5eXoKCgra2tb29vcXFxcnJyc3NzdXV1d3d3enp6fX19gYGBhYWFioqKkJCQl5eXnp6epqamrq6uurq6j4+PkZGRkZGRkpKSkZGRj4+PjY2NiYmJjo6Ol5eXnZ2doqKiqKiorq6utLS0vLy8xcXFnp6eoKCgn5+fn5+fnp6enZ2dm5ublpaWl5eXoKCgq6ursLCwtra2vLy8wsLCyMjI0dHRq6urra2tra2trKysq6urqqqqp6eno6OjoaGhqampsbGxvLy8wsLCyMjIzs7O1NTU3Nzcubm5urq6urq6ubm5ubm5t7e3tLS0sbGxrKyss7Ozurq6wcHBysrK0tLS2NjY3d3d5OTkxsbGx8fHxsbGxcXFxMTEw8PDwMDAvr6+urq6vLy8w8PDycnJ0NDQ19fX4ODg5eXl6urq0tLS0tLS0tLS0dHR0NDQz8/Pzs7Oy8vLyMjIx8fHzs7O09PT2dnZ39/f5OTk7Ozs8fHx3t7e39/f3t7e3t7e3d3d3Nzc29vb2dnZ19fX1dXV2dnZ3t7e4+Pj5+fn7Ozs8PDw9vb26urq6+vr6urq6urq6urq6enp6Ojo5+fn5ubm5OTk5eXl6enp7e3t8PDw8/Pz9fX1+Pj49PT09PT09PT08/Pz8/Pz8/Pz8/Pz8vLy8vLy8PDw8PDw8/Pz9fX19/f3+fn5+vr6/Pz8Pj4+Pz8/QkJCRkZGS0tLUlJSWlpaY2NjaGhobW1teXl5iYmJm5ubpKSkqKiorKystLS0QUFBQkJCRERESEhITU1NVFRUXFxcZWVlampqbm5uenp6i4uLnJycpKSkpqamq6urs7OzRkZGR0dHSUlJTExMUVFRV1dXX19faGhobGxscHBwfX19jo6On5+fpKSkqKiorKystLS0S0tLTU1NTk5OUlJSV1dXXFxcY2Nja2trcHBwdHR0goKCkpKSoaGhpKSkqKiorKystLS0UFBQU1NTVlZWWVlZXV1dYmJiaWlpcHBwdnZ2eXl5h4eHl5eXoaGhpKSkqKiorKyss7OzUFBQU1NTV1dXXV1dZGRkaWlpb29vdnZ2e3t7gICAjo6OnZ2doKCgo6OjpqamqqqqsbGxXV1dX19fYWFhYmJiZWVlbm5udnZ2fX19g4ODiYmJlpaWnJycn5+foqKipaWlqKior6+vcHBwcnJyc3NzdXV1dnZ2eHh4e3t7hYWFioqKk5OTmJiYm5ubnZ2dn5+fpKSkq6urt7e3hYWFhoaGh4eHiIiIiYmJi4uLjY2NkJCQk5OTl5eXnJycoaGhpqamrKyssrKyubm5wsLCoqKipKSkpKSkpaWlpqampqampKSkoqKinp6epKSkrKyssbGxtra2u7u7wMDAx8fH0NDQs7OztbW1tLS0tLS0s7OzsrKysbGxr6+vq6urrKystLS0vb29w8PDyMjIzs7O09PT29vbv7+/wcHBwcHBwMDAwMDAvr6+vb29u7u7t7e3tbW1vLy8w8PDzc3N09PT2NjY3d3d5OTky8vLzMzMzMzMy8vLy8vLysrKyMjIxcXFw8PDvr6+xcXFy8vL0tLS2dnZ4ODg5eXl6+vr1tbW19fX19fX19fX1tbW1dXV09PT0tLSz8/PzMzMz8/P1NTU2tra4ODg5eXl7Ozs8fHx4uLi4uLi4uLi4uLi4uLi4ODg4ODg3t7e3d3d2tra2tra39/f5OTk6Ojo7e3t8PDw9/f37Ozs7u7u7e3t7e3t7Ozs7Ozs6+vr6urq6enp6Ojo5ubm6urq7e3t8PDw9PT09vb2+Pj49fX19fX19vb29fX19fX19fX19PT09PT08/Pz8vLy8fHx8vLy9fX19/f3+fn5+/v7/Pz8SkpKS0tLTU1NUVFRVlZWXFxcZGRkbW1tdnZ2enp6f39/iYmJm5ubrKysurq6vr6+xcXFS0tLTU1NUFBQU1NTWFhYXl5eZWVlbm5ud3d3e3t7f39/i4uLnJycrKysubm5vb29xMTEUVFRUlJSVFRUV1dXXFxcYmJiaWlpcXFxeXl5fn5+goKCjY2Nnp6er6+vurq6vr6+xsbGVlZWWFhYWlpaXV1dYWFhZmZmbW1tdXV1fX19goKChoaGkZGRoqKisrKyurq6vr6+xsbGXl5eX19fYWFhY2NjZ2dnbGxscnJyeXl5gYGBhoaGioqKl5eXpqamtbW1urq6vr6+xMTEXl5eYmJiZWVlampqbm5ucnJyeHh4f39/h4eHjIyMj4+PnZ2dq6urtra2ubm5vLy8xMTEYGBgYmJiZWVlampqcXFxenp6f39/hoaGjY2NkpKSlpaWpKSksrKytra2ubm5u7u7wsLCcnJydHR0dXV1dnZ2eHh4e3t7hYWFjY2NlJSUmZmZn5+fq6ursrKytLS0t7e3ubm5v7+/hoaGiIiIiIiIiYmJi4uLjY2Nj4+PkpKSm5uboKCgqKiora2tr6+vsbGxs7Oztra2v7+/mZmZnJycnJycnZ2dnp6en5+foaGho6OjpaWlqKiorKysr6+vtLS0ubm5vr6+xMTEzc3Ns7Oztra2tra2t7e3uLi4ubm5ubm5uLi4tra2srKyt7e3vr6+w8PDyMjIzMzM0tLS2dnZxcXFx8fHx8fHx8fHxsbGxsbGxMTEw8PDwcHBvb29v7+/xsbGzs7O09PT2NjY3t7e5OTk0NDQ0dHR0dHR0NDQ0NDQ0NDQz8/Pzs7Oy8vLyMjIx8fHzc3N09PT3Nzc4eHh5ubm6+vr29vb29vb29vb29vb29vb2tra2tra2NjY1tbW1NTU0NDQ1tbW29vb4eHh5+fn7e3t8vLy5eXl5ubm5ubm5ubm5eXl5eXl5OTk4+Pj4uLi39/f3d3d39/f5eXl6enp7e3t8fHx9vb27+/v8PDw7+/v7+/v7+/v7u7u7u7u7u7u7e3t6+vr6urq6urq7e3t8fHx8/Pz9vb2+fn59/f39/f39/f39/f39/f39vb29fX19fX19fX19PT09PT08/Pz9fX19/f3+vr6+/v7/Pz8V1dXWFhYW1tbXl5eY2NjaGhocHBwd3d3gYGBioqKjo6OkpKSm5ubrKysurq6ycnJ09PTWVlZW1tbXV1dYGBgZWVlampqcXFxeXl5goKCi4uLj4+Pk5OTnJycrKysu7u7yMjI09PTXl5eYGBgYWFhZGRkaGhobm5udHR0fHx8hISEjo6OkZGRlpaWnp6er6+vvb29y8vL09PTYmJiZWVlZ2dnampqbW1tcnJyeHh4f39/iIiIkZGRlJSUmJiYoqKisbGxwMDAzMzM1NTUampqa2trbW1tb29vc3Nzd3d3fX19hISEjIyMlJSUmJiYnJycpqamtbW1w8PDzc3N09PTbm5ucXFxc3NzdnZ2eXl5fX19g4ODiYmJkZGRmZmZnZ2doKCgq6urubm5xsbGzc3N09PTbm5ucXFxdHR0eXl5gICAhISEiYmJkJCQl5eXnp6eo6OjpaWlsbGxvr6+ycnJzMzM0tLSdXV1d3d3eHh4eXl5f39/h4eHkZGRl5eXnZ2dpKSkqKiorKyst7e3w8PDyMjIy8vL0NDQiYmJioqKi4uLjIyMjo6Oj4+PkpKSm5uboqKiqamprq6us7Ozvr6+xMTExsbGycnJzc3NnJycnp6enp6en5+foKCgoaGho6OjpaWlp6enr6+vtLS0u7u7wMDAwcHBw8PDxcXFysrKra2tsLCwsLCwsLCwsbGxsrKytLS0tLS0tra2uLi4u7u7vr6+wcHBxsbGysrKz8/P19fXxMTExsbGxsbGxsbGyMjIycnJysrKysrKycnJx8fHxMTEycnJz8/P09PT19fX3Nzc4uLi1tbW19fX1tbW1tbW1tbW1tbW1NTU1NTU0tLS0dHRzs7O0NDQ1dXV3d3d4eHh5ubm6+vr39/f4ODg4ODg4ODg39/f39/f3t7e3t7e3d3d29vb2NjY19fX3d3d4uLi6enp7u7u8vLy6enp6enp6enp6enp6enp6enp6Ojo5+fn5ubm5eXl4+Pj4eHh5ubm6urq7u7u8vLy9vb28fHx8vLy8vLy8vLy8fHx8vLy8vLy8fHx8PDw7+/v7u7u7Ozs7u7u8vLy9PT09/f3+fn5+fn5+Pj4+Pj4+Pj4+Pj4+Pj49/f39/f39/f39vb29vb29PT09fX19/f3+fn5+/v7/Pz8Z2dnaGhoampqbm5ucnJyd3d3fX19hYWFjY2NlpaWn5+foqKipqamq6uru7u7ycnJ2NjYaWlpa2trbW1tb29vc3NzeXl5f39/hoaGjo6Ol5eXoKCgo6Ojp6enq6uru7u7ycnJ2NjYbW1tb29vcHBwc3Nzd3d3fHx8goKCiYmJkZGRmZmZoqKipaWlqKiorq6uvb29y8vL2tracXFxc3NzdXV1d3d3e3t7gICAhYWFjIyMlJSUnJycpKSkqKioq6ursbGxwMDAzc3N29vbeHh4eXl5enp6fX19gICAhYWFioqKkZGRmJiYoKCgp6enq6urrq6utbW1w8PDz8/P3NzcfX19gICAgICAg4ODhoaGioqKkJCQlpaWnZ2dpKSkq6urrq6usbGxuLi4xsbG0dHR3t7efn5+goKChISEiYmJjY2NkZGRlpaWm5uboqKiqKior6+vsrKytbW1vr6+ysrK1dXV3t7efn5+gYGBg4ODiIiIjo6OlpaWnJycoaGhp6enra2ttLS0t7e3ubm5w8PDzs7O2NjY3t7ejY2Njo6Ojo6Oj4+PkJCQlJSUnZ2dp6enrKyssrKyt7e3u7u7vb29yMjI0tLS2NjY3Nzcn5+foaGhoaGhoqKio6OjpKSkpqamp6enr6+vtra2vLy8wMDAxMTEzs7O1NTU1tbW2trasLCwsrKysrKys7OztLS0tLS0tbW1tra2uLi4ubm5wcHBxsbGzMzM0NDQ0tLS1NTU19fXwMDAwcHBwsLCwsLCwsLCw8PDxMTExcXFxsbGyMjIysrKzMzMz8/P0tLS1tbW2tra39/f09PT09PT1NTU1NTU1dXV1tbW19fX2NjY2dnZ2NjY19fX1NTU2NjY3d3d4uLi5ubm6urq4+Pj5OTk5OTk5OTk5OTk4+Pj4+Pj4+Pj4uLi4eHh4ODg3d3d39/f5OTk6urq7u7u8/Pz7Ozs7e3t7Ozs7Ozs7Ozs7Ozs6+vr6+vr6+vr6+vr6enp5+fn5ubm6+vr7+/v9PT09/f39PT09PT09PT09PT09PT09PT08/Pz8/Pz8/Pz8vLy8fHx8PDw7u7u8fHx9fX19/f3+vr6+fn5+vr6+fn5+fn5+fn5+fn5+fn5+fn5+fn5+Pj4+Pj49/f39/f39/f3+vr6+/v7/Pz8enp6e3t7fX19f39/g4ODiIiIjo6OlZWVnJycpKSkrKyss7Oztra2uLi4u7u7ysrK2NjYe3t7fX19fn5+gYGBhISEiYmJj4+PlpaWnp6epaWlra2ts7OztbW1uLi4u7u7ycnJ2NjYfn5+gICAgoKChISEh4eHjIyMkpKSmJiYoKCgp6enr6+vtbW1t7e3urq6vb29y8vL2tragoKChISEhoaGiIiIi4uLkJCQlZWVm5uboqKiqampsbGxt7e3urq6vLy8wMDAzc3N29vbiIiIiYmJi4uLjY2NkJCQlJSUmZmZn5+fpqamrKyss7Ozurq6vLy8vr6+wsLC0NDQ3d3djY2Nj4+PkJCQk5OTlpaWmZmZnp6eo6Ojqampr6+vtra2vLy8vr6+wMDAxcXF0tLS39/fkpKSlJSUlpaWmJiYm5ubn5+fo6OjqKiora2ts7Ozurq6v7+/wcHBxMTEycnJ1dXV4eHhkJCQk5OTlpaWmpqan5+fpKSkqKiora2tsrKyt7e3vb29wsLCxcXFx8fHzs7O2dnZ5OTkkJCQkpKSlJSUmJiYnZ2dpKSkrKyssbGxtra2u7u7wMDAxcXFyMjIysrK0tLS3Nzc5ubmoqKipaWlpaWlpqamp6enp6enqampsbGxurq6v7+/xMTEycnJzMzMzs7O19fX4ODg5+fntLS0tbW1tra2tra2t7e3t7e3uLi4ubm5urq6wcHByMjIzc3N0dHR1NTU3d3d4uLi5eXlw8PDxMTExcXFxcXFxsbGxsbGxsbGx8fHyMjIycnJy8vL0tLS1tbW29vb39/f4eHh5OTk0NDQ0dHR0dHR0dHR0tLS0tLS09PT1NTU1NTU1tbW19fX2dnZ29vb3d3d4ODg5OTk6enp39/f4ODg4ODg4eHh4uLi4uLi4+Pj5OTk5eXl5+fn5ubm5eXl4+Pj5ubm6urq7+/v8vLy7+/v7+/v7+/v8PDw7+/v7+/v7+/v7+/v7+/v7+/v7u7u7e3t6+vr7Ozs8PDw9fX19/f39vb29vb29vb29vb29vb29vb29fX19vb29vb29fX19fX19PT08/Pz8vLy9fX19/f3+vr6+vr6+vr6+vr6+/v7+vr6+vr6+/v7+vr6+vr6+vr6+vr6+fn5+Pj49/f3+vr6/Pz8/f39j4+PkJCQkZGRlJSUmJiYnJycoaGhp6enra2ts7Ozurq6wMDAxMTEx8fHycnJy8vL2dnZkJCQkZGRk5OTlZWVmJiYnZ2doqKiqKiora2ts7Ozurq6v7+/xMTExsbGycnJy8vL2NjYkpKSlJSUlpaWmJiYm5ubn5+fpKSkqampr6+vtbW1u7u7wcHBxsbGyMjIysrKzMzM2tralpaWmJiYmZmZm5ubnp6eoqKip6enrKyssbGxuLi4vb29w8PDyMjIycnJzMzMzs7O29vbm5ubnJycnZ2dn5+foqKipaWlqampr6+vtLS0urq6v7+/xcXFysrKy8vLzc3N0NDQ3d3dnp6eoKCgoqKio6Ojpqamqampra2tsrKyt7e3vLy8wcHBx8fHy8vLzc3Nzs7O0tLS39/fo6OjpqampqamqKioqqqqra2tsbGxtbW1urq6v7+/xMTEycnJzs7Oz8/P0dHR1dXV4uLipKSkpqamqKioq6urrq6usrKytbW1ubm5vb29wsLCx8fHy8vL0NDQ0tLS09PT2dnZ5OTkoqKipaWlpqamqqqqrq6utLS0ubm5vLy8wcHBxMTEycnJzs7O09PT1NTU1tbW3d3d5ubmp6enqampqampqampq6ursLCwt7e3vr6+w8PDyMjIzMzM0dHR1dXV19fX2tra4eHh6urqt7e3ubm5urq6urq6urq6urq6urq6vLy8w8PDy8vLz8/P1NTU2NjY3Nzc3d3d5eXl7e3txsbGyMjIyMjIyMjIycnJycnJysrKysrKy8vLzMzM0tLS19fX3Nzc39/f4uLi6urq7+/v09PT1dXV1dXV1dXV1dXV1dXV1tbW19fX2NjY2NjY2dnZ2tra39/f4+Pj6Ojo7Ozs7u7u39/f39/f39/f39/f4ODg4ODg4ODg4eHh4uLi4+Pj5OTk5eXl5ubm6Ojo6+vr7u7u8PDw7Ozs7Ozs7Ozs7Ozs7e3t7e3t7u7u7+/v8PDw8fHx8fHx8fHx8fHx7+/v8fHx9fX19/f39/f3+Pj4+Pj49/f39/f3+Pj4+Pj4+Pj4+Pj4+Pj49/f39/f39/f39fX19vb2+Pj4+/v7/Pz8+/v7+/v7/Pz8+/v7+/v7/Pz8+/v7+/v7+/v7+/v7+/v7+vr6+vr6+vr6+/v7/Pz8p6enp6enqKioq6urra2tsbGxtbW1ubm5vr6+w8PDx8fHzc3N0dHR1dXV19fX2dnZ3NzcpqamqKioqampq6urrq6usbGxtbW1ubm5vb29w8PDyMjIzMzM0dHR1dXV19fX2NjY3NzcqKioqqqqrKysra2tsLCwsrKytra2u7u7wMDAxMTEycnJzs7O0tLS1tbW19fX2dnZ3d3dq6urra2trq6ur6+vsrKytbW1ubm5vb29wcHBxsbGysrKz8/P1NTU2NjY2dnZ29vb3t7er6+vsLCwsbGxsrKytbW1uLi4u7u7v7+/w8PDyMjIzMzM0NDQ1dXV2dnZ2tra3Nzc39/fsbGxs7OztbW1tra2uLi4urq6vr6+wcHBxcXFycnJzs7O0tLS1tbW2tra29vb3d3d4ODgtbW1t7e3uLi4ubm5u7u7vr6+wcHBxMTEyMjIzMzMz8/P1NTU2NjY3Nzc3d3d39/f4uLit7e3urq6u7u7vLy8vr6+wcHBw8PDxsbGysrKzc3N0dHR1tbW2tra3t7e39/f4eHh5OTktra2uLi4ubm5u7u7v7+/w8PDxcXFycnJzMzMz8/P09PT19fX29vb4ODg4eHh4+Pj5ubmsrKytbW1tra2ubm5vLy8wcHBxsbGy8vLzs7O0tLS1tbW2dnZ3t7e4eHh4+Pj5eXl6urqvLy8vr6+vb29vb29vr6+vr6+wsLCycnJz8/P1NTU2NjY3Nzc4ODg5OTk5ubm5+fn7e3ty8vLzc3NzMzMzc3Nzc3Nzc3Nzc3Nzc3Nzc3N1NTU29vb39/f4uLi5ubm6enp6urq8PDw2NjY2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2tra2tra29vb29vb4ODg5eXl6enp7Ozs7+/v9PT04uLi4+Pj4+Pj4+Pj4+Pj5OTk5OTk5OTk5eXl5ubm5+fn5+fn6Ojo7Ozs8PDw8/Pz9vb27Ozs7Ozs7Ozs7Ozs7e3t7Ozs7e3t7u7u7u7u7u7u7+/v8PDw8fHx8vLy8/Pz9fX19vb29fX19fX19fX19vb29vb29vb29vb29/f3+Pj4+Pj4+fn5+fn5+fn5+Pj4+Pj4+fn5+/v7/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8+/v7+/v7/f39vr6+v7+/v7+/wMDAwsLCxcXFyMjIy8vLzs7O0tLS1tbW2tra3t7e4uLi5OTk5eXl5+fnvb29v7+/wMDAwcHBw8PDxMTEx8fHysrKzs7O0tLS1dXV2dnZ3d3d4eHh5OTk5eXl5+fnvr6+wMDAwsLCwsLCxcXFx8fHycnJzMzM0NDQ09PT1tbW2tra3t7e4uLi5eXl5ubm6OjowcHBwsLCxMTExcXFx8fHycnJy8vLzs7O0dHR1NTU2NjY29vb39/f4+Pj5eXl5+fn6enpxMTExMTExcXFxsbGyMjIysrKzMzMz8/P0tLS1dXV2dnZ3Nzc4ODg5OTk5+fn5+fn6urqxMTExsbGx8fHyMjIycnJy8vLzc3N0NDQ09PT1tbW2tra3d3d4eHh5OTk5ubm6Ojo6urqxsbGyMjIycnJysrKy8vLzc3N0NDQ0tLS1dXV2NjY3Nzc39/f4uLi5ubm6Ojo6enp7OzsycnJysrKy8vLzMzMzc3Nz8/P0dHR1NTU19fX2dnZ3d3d4eHh5OTk5+fn6enp6+vr7e3tycnJysrKy8vLzc3Nz8/P0dHR0tLS1dXV2NjY29vb3t7e4eHh5eXl6Ojo6+vr7Ozs7u7uxcXFx8fHyMjIysrKzc3N0NDQ1NTU1tbW2dnZ3Nzc39/f4+Pj5ubm6enp7Ozs7u7u7+/vwsLCw8PDxcXFx8fHysrKzc3N0dHR1tbW29vb3t7e4eHh5OTk6Ojo6+vr7u7u7+/v8PDw0dHR0tLS0dHR0tLS0dHR0tLS0dHR09PT2dnZ3t7e4+Pj5ubm6urq7e3t8PDw8vLy8/Pz3t7e3t7e3t7e3t7e3t7e3t7e3d3d3d3d3t7e3d3d4uLi6Ojo7Ozs7+/v8vLy9PT09fX16Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6enp6enp6enp6enp6enp7e3t8fHx9PT09vb29/f38PDw8PDw8PDw8PDw8fHx8fHx8fHx8fHx8fHx8vLy8vLy8vLy8/Pz8/Pz9vb2+Pj4+vr69vb29/f39vb29vb29/f39vb29/f39vb29/f39/f39/f3+Pj4+Pj4+fn5+fn5+vr6+vr6+/v7+/v7+/v7+/v7+/v7+/v7+/v7/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/f39/Pz8/f39/f391dXV1dXV1tbW1tbW19fX2dnZ29vb3d3d4ODg4uLi5eXl6Ojo6urq7e3t7+/v8fHx8vLy09PT1NTU1dXV1tbW19fX2dnZ2tra3d3d39/f4uLi5OTk5+fn6urq7Ozs7+/v8fHx8vLy1dXV1tbW1tbW2NjY2NjY2dnZ3Nzc3t7e4ODg4+Pj5ubm6Ojo6+vr7e3t7+/v8fHx8/Pz1tbW19fX2NjY2NjY2tra29vb3d3d3t7e4eHh5OTk5ubm6enp6+vr7e3t8PDw8vLy8/Pz2NjY2NjY2dnZ2tra29vb3Nzc3t7e39/f4uLi5OTk5+fn6enp7Ozs7u7u8fHx8vLy8/Pz2NjY2dnZ2tra29vb3Nzc3d3d39/f4ODg4uLi5eXl5+fn6urq7Ozs7u7u8fHx8vLy8/Pz2dnZ29vb29vb3Nzc3d3d3t7e4ODg4uLi4+Pj5ubm6Ojo6urq7e3t7+/v8fHx8/Pz9fX129vb3Nzc3Nzc3Nzc3t7e39/f4eHh4uLi5OTk5+fn6enp6+vr7u7u8PDw8vLy9PT09fX13Nzc3Nzc3d3d3t7e3t7e4ODg4eHh4+Pj5eXl5+fn6urq7Ozs7+/v8fHx8/Pz9fX19vb22NjY2dnZ29vb3Nzc3t7e4ODg4uLi4+Pj5ubm6Ojo6+vr7e3t7+/v8vLy9PT09vb29/f31dXV19fX2NjY2dnZ29vb3t7e4eHh5OTk5+fn6enp6+vr7u7u8fHx8/Pz9fX19/f3+Pj42NjY2NjY2dnZ2NjY2NjY29vb3t7e4uLi5ubm6urq7e3t7+/v8vLy9PT09/f3+fn5+fn55eXl5eXl5eXl5OTk5OTk5OTk4+Pj4+Pj5OTk6Ojo7Ozs8PDw8/Pz9fX1+Pj4+fn5+vr67u7u7+/v7u7u7u7u7u7u7u7u7u7u7u7u7u7u7e3t7Ozs7+/v9PT09vb2+fn5+fn5+/v79fX19fX19vb29fX19fX19fX19fX19vb29fX19vb29vb29fX19PT09/f3+vr6+/v7/Pz8+vr6+fn5+vr6+fn5+vr6+fn5+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+/v7+/v7/Pz8/Pz8/Pz8+/v7/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/f39/Pz8/Pz8/Pz8/f39/f39/f39/f39\"};function Rue(e){let t=XG[e]??XG.portrait400,s=atob(t),r=17,o=new Uint8Array(r*r*r*4);for(let a=0;a<r;a++)for(let i=0;i<r;i++)for(let l=0;l<r;l++){let c=((a*r+i)*r+l)*3,d=(i*r*r+a*r+l)*4;o[d]=s.charCodeAt(c),o[d+1]=s.charCodeAt(c+1),o[d+2]=s.charCodeAt(c+2),o[d+3]=255}return o}var Due=[1,.38,.16],Lue=.62,Uue=JU({amount:T(\"weave\"),field:\"weaveTime\"}),Oue=v8({select:T(\"stock\"),strength:T(\"strength\"),size:17,decode:Rue,fallback:\"portrait400\",label:\"filmstock-lut\"}),HG=ZU({strength:T(\"halation\"),radius:T(\"halationRadius\"),tint:Due,threshold:Lue,output:\"halationTexture\",extractName:\"filmStockHalationExtract\"}),jG=H({name:\"FilmStock\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Real analog film color from measured film-emulation LUTs \\u2014 ten classic stock looks with emulsion halation and projector gate weave\",requiresRTT:!0,requiresChild:!0,extraFields:{weaveTime:{schema:n,initial:0}},props:{stock:{default:\"portrait400\",description:\"Film stock \\u2014 each look is a real measured film-emulation color LUT\",compileTime:!0,ui:{type:\"select\",options:[...zue],label:\"Stock\",group:\"Stock\"}},strength:{default:1,description:\"How strongly the stock's color grade is applied\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Stock\"}},halation:{default:.4,transform:e=>e*10,description:\"Strength of the red-orange highlight glow (film emulsion backscatter)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Halation\",group:\"Halation\"}},halationRadius:{default:28,description:\"Spread of the halation glow in pixels\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Halation Radius\",group:\"Halation\"}},weave:{default:0,description:\"Gate weave \\u2014 subtle projector-like frame jitter and rotation\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Gate Weave\",group:\"Weave\"}}},compute:HG.compute,gpu:{fragment:e=>{let{childNode:t,computeOutputs:s,convertToTexture:r}=e;if(!t)return console.error(\"You must pass a child component into the FilmStock shader.\"),xt;let o=s?.childTexture??r(t),a=Uue(e),i=W(Yt,\"unpremultiplyAlpha\",[o.sample(a)]);return HG.screen(Oue(i,e),a,e)}}});var KG=H({name:\"Flip\",role:\"warp\",category:\"Distortions\",description:\"Mirror content horizontally, vertically, or both\",boundingBoxDeclaration:{aspectRatio:null},props:{flipX:{default:!1,transform:rs,description:\"Mirror the content horizontally (left \\u2194 right)\",ui:{type:\"checkbox\",label:\"Flip X\",group:\"Effect\"}},flipY:{default:!1,transform:rs,description:\"Mirror the content vertically (top \\u2194 bottom)\",ui:{type:\"checkbox\",label:\"Flip Y\",group:\"Effect\"}}},map:NL({flipX:T(\"flipX\"),flipY:T(\"flipY\")}),edges:\"none\",missingChildMessage:\"You must pass a child component into the Flip shader.\"});var $g=8192,Gue=3e3,K_=vn({desktop:1024,mobile:512}),ZG=\"rgba16float\",QG=bt,Fue=.12,Iue=.035,Yue=1.4,Wue=2,que=3.5,Vue=1.1,Nue=Wo,JG=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({color:S,count:n,dt:n,time:n,aspect:n,domainX:n,driftBase:n,angleRad:n,speedVar:n,angleVarRad:n,randomness:n,twinkle:n,softness:n,bodyR:n,cursorX:n,cursorY:n,cursorRadSq:n,cursorForce:n,dragMul:n}),\"SimParams\"),Ml=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({agents:{storage:Fe(S,$g),access:\"mutable\"},accumE:{storage:Fe(lo(M),K_*K_),access:\"mutable\"},params:{uniform:JG},outTex:{storageTexture:zt(ZG,\"write-only\")}}),\"simLayout\"),$ue=eg(Ml,\"floatingParticlesInit\"),Xue=On.drift2d(Ml,{drift:eO.variedHeading(Ml),gust:Za.cursorGust(Ml),name:\"floatingParticlesUpdate\"});function Y2(e){return Gn.pointWorld(Ml,{shape:e,res:K_,place:Dg.orbitalPlace(Ml,{radius:Iue,rate:Yue}),brightness:Dg.twinkleBrightness(Ml,{freq:Wue}),bodyRadius:Dg.presenceRadius(Ml),names:{splat:\"floatingParticlesSplat\",resolve:\"floatingParticlesResolve\"}})}var Hue=e=>Y2(e).splat.kernel,wxe=Hue(\"dot\"),Txe=Y2(\"dot\").resolve.kernel,jue=dz,eF=H({name:\"FloatingParticles\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Textures\",description:\"Drifting, twinkling motes \\u2014 thousands of real simulated particles floating in a shared heading with per-particle wander and variance, and a cursor that stirs the field with a gust that settles back into the drift\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{particleColor:{default:\"#ffffff\",transform:ce,description:\"Color of the particles\",ui:{type:\"color\",label:\"Particle Color\",group:\"Colors\"}},shape:{default:\"dot\",compileTime:!0,description:\"What each particle is drawn as \\u2014 a crisp dot or square (softness still feathers them), or a soft glow puff\",ui:{type:\"select\",options:cp,label:\"Shape\",group:\"Effect\"}},count:{default:1200,description:\"Number of particles\",ui:{type:\"range\",min:100,max:$g,step:100,label:\"Count\",group:\"Effect\"}},particleSize:{default:1.2,description:\"Size of the particles (each also varies slightly for depth)\",ui:{type:[\"range\",\"map\"],min:.3,max:4,step:.05,label:\"Particle Size\",group:\"Effect\"}},softness:{default:.1,description:\"Edge softness of each particle \\u2014 0 is a crisp shape, 1 a soft glow puff\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},speed:{default:.25,description:\"Speed of the shared drift\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Speed\",group:\"Animation\"}},angle:{default:90,description:\"Drift heading in degrees (0 = left, 90 = up, 180 = right, 270 = down \\u2014 the classic default floats upward)\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Angle\",group:\"Animation\"}},speedVariance:{default:.3,description:\"Per-particle speed variance around the shared drift (0 = everything moves in lockstep)\",ui:{type:\"range\",min:0,max:1,step:.05,label:\"Speed Variance\",group:\"Animation\"}},angleVariance:{default:30,description:\"Per-particle heading variance in degrees (0 = one shared direction, 180 = every which way)\",ui:{type:\"range\",min:0,max:180,step:1,label:\"Angle Variance\",group:\"Animation\"}},randomness:{default:.25,description:\"Orbital wander \\u2014 each particle circles lazily around its drift path instead of gliding straight\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Randomness\",group:\"Animation\"}},twinkle:{default:.5,description:\"Intensity of the twinkle (0 = steady, 1 = full shimmer)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Twinkle\",group:\"Animation\"}},cursorStrength:{default:0,description:\"How strongly the cursor stirs the field \\u2014 0 (default) leaves the drift undisturbed\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Cursor Gust\",group:\"Interaction\"}}},...Fn({layout:Ml,params:JG,maxAgents:$g,countCap:{desktop:$g,mobile:Gue},output:{key:\"outTex\",name:\"particleTexture\",size:[K_,K_],format:ZG},bake:({getCpuValue:e})=>{let t=Y2(e(\"shape\")||\"dot\");return{pipelines:{init:{kernel:$ue,threads:\"max\"},update:{kernel:Xue,threads:\"agents\"},splat:t.splat,resolve:t.resolve},initStep:\"init\",program:[\"update\",\"splat\",\"resolve\"]}},frame:(e,{getCpuValue:t})=>{let s=Li(t),r=0;return o=>{let{dt:a,aspect:i,pointerX:l,pointerY:c}=Di(o);r+=a;let d=Math.max(i,.01),u=e.resolveCount(s(\"count\",1200)),p=l*i,_=c,f=.22,h=t(\"particleColor\"),g=Math.min(Math.max(s(\"particleSize\",1.2),.3),4)*2/Ri;return e.writeParams({color:S(h?.x??1,h?.y??1,h?.z??1,h?.w??1),count:u,dt:a,time:r,aspect:i,domainX:d,driftBase:Math.min(Math.max(s(\"speed\",.25),0),1.5)*Fue/.25,angleRad:s(\"angle\",90)*QG+Nue,speedVar:Math.min(Math.max(s(\"speedVariance\",.3),0),1),angleVarRad:Math.min(Math.max(s(\"angleVariance\",30),0),180)*QG,randomness:Math.min(Math.max(s(\"randomness\",.25),0),1),twinkle:Math.min(Math.max(s(\"twinkle\",.5),0),1),softness:Math.min(Math.max(s(\"softness\",.1),0),1),bodyR:g,cursorX:p,cursorY:_,cursorRadSq:f*f,cursorForce:Math.min(Math.max(s(\"cursorStrength\",0),0),1)*que,dragMul:Sp(Vue,a)}),e.frame({count:u})}},fragment:{output:\"particleTexture\",fallback:\"child\",compose:(e,{childNode:t})=>t?W(jue,\"particleOverChild\",[e,t]):e}})});var tF=H({role:\"shape\",name:\"Flower\",description:\"Petal shape with N lobes and adjustable inner-to-outer radius ratio\",shape:{distance:({x:e,y:t},s)=>dR(e,t,s(\"radius\"),s(\"sides\"),s(\"innerRatio\")),colorDescription:\"Fill color of the flower\",centerDescription:\"Center position of the flower\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.4,description:\"Outer petal tip radius in UV space\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Radius\",group:\"Shape\"}},sides:{default:5,description:\"Number of petals\",ui:{type:\"range\",min:3,max:12,step:1,label:\"Petals\",group:\"Shape\"}},innerRatio:{default:.4,description:\"Inner valley radius as a ratio of outer radius \\u2014 lower values make deeper notches\",ui:{type:\"range\",min:.1,max:.95,step:.01,label:\"Inner Ratio\",group:\"Shape\"}}}}});var sF=H({name:\"FlowField\",role:\"warp\",category:\"Distortions\",description:\"Fluid-like distortion with constant smooth motion\",boundingBoxDeclaration:{aspectRatio:null},animatedTime:{speed:\"speed\"},extraAnimatedTimes:{evolution:\"evolutionSpeed\"},props:{strength:{default:.15,description:\"Intensity of the flow distortion\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.01,label:\"Strength\",group:\"Effect\"}},detail:{default:2,description:\"Scale of the flow patterns\",ui:{type:[\"range\",\"map\"],min:.5,max:5,step:.1,label:\"Detail\",group:\"Effect\"}},speed:{default:0,description:\"Speed of the flow\",ui:{type:\"range\",min:0,max:20,step:.1,label:\"Speed\",group:\"Animation\"}},evolutionSpeed:{default:0,description:\"How fast the flow field pattern reshapes over time\",ui:{type:\"range\",min:0,max:20,step:.1,label:\"Evolution Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed for flow pattern variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},edges:{default:\"mirror\",description:\"How to handle edges when distortion pushes content out of bounds\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},map:rU({strength:T(\"strength\"),detail:T(\"detail\"),seed:T(\"seed\")})});var Kue=4*Math.PI,Ep=[{scale:2,offsets:[[5.2,1.3],[1.7,9.2]],reach:.25},{scale:1.5,offsets:[[8.3,2.8],[3.1,7.4]],reach:.2}],rF=H({name:\"FlowingGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Liquid silk gradient with organic flowing color bands\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},extraFields:{convA:{schema:A,initial:[0,0,0]},convB:{schema:A,initial:[0,0,0]},convC:{schema:A,initial:[0,0,0]},convD:{schema:A,initial:[0,0,0]}},props:{colorA:{default:\"#0a0015\",transform:ce,description:\"Deep background color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#6b17e6\",transform:ce,description:\"Primary accent color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},colorC:{default:\"#ff4d6a\",transform:ce,description:\"Secondary accent color\",ui:{type:\"color\",label:\"Color C\",group:\"Colors\"}},colorD:{default:\"#ff6b35\",transform:ce,description:\"Tertiary accent color\",ui:{type:\"color\",label:\"Color D\",group:\"Colors\"}},colorSpace:{default:\"oklch\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},speed:{default:1,description:\"Animation speed\",ui:{type:\"range\",min:0,max:10,step:.1,label:\"Speed\",group:\"Effect\"}},distortion:{default:.5,description:\"Organic distortion intensity\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Distortion\",group:\"Effect\"}},seed:{default:0,description:\"Random seed for variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}}},paint:e=>{let{uv:t,viewport:s}=aa(e),r=z(Ce(s.member(\"x\"),it(s.member(\"y\"),1e-6)),\"aspect\"),o=z(v(wt(e),.075),\"flowTime\"),a=z(v(me(T(\"seed\"),e),.17),\"flowSeed\"),i=z(me(T(\"distortion\"),e),\"flowStrength\"),l=z(ne(t,.5),\"flowCentred\"),c=z(v(v(Jn(Qe(I(v(l.member(\"x\"),.8),v(o,.4)),ne(v(l.member(\"y\"),.8),v(o,.25)),a)),Kue),i),\"flowAngle\"),d=z(ng(rt(l.member(\"x\"),Ce(l.member(\"y\"),r)),Do(c),Vt(c)),\"flowSpun\"),u=z(rt(d.member(\"x\"),v(d.member(\"y\"),r)),\"flowRot\"),p=v(o,2),_=z(I(u.member(\"x\"),v(Ce(Vt(I(v(u.member(\"y\"),5),p)),50),i)),\"flowShearX\"),f=z(o2({at:rt(_,I(u.member(\"y\"),v(Ce(Vt(I(v(_,7.5),p)),25),i))),scale:Ep[0].scale,z:I(v(o,.5),a),offsets:Ep[0].offsets,reach:Ep[0].reach,strength:i}),\"flowWarp1\"),h=z(I(v(o,.3),a),\"flowTz\"),g=z(o2({at:f.member(\"xy\"),scale:Ep[1].scale,z:h,offsets:Ep[1].offsets,reach:Ep[1].reach,strength:i}),\"flowWarp2\"),y=Jn(Qe(v(g.member(\"x\"),1.8),v(g.member(\"y\"),1.8),h)),b=z(ts(I(v(f.member(\"z\"),.5),.5),1.5),\"flowHardness\"),x=z(et(.5,.008,b),\"flowEdge\"),P=z(ot(v(x,-1),x,y),\"flowT1\"),C=z(ot(v(x,-1),x,f.member(\"w\")),\"flowT2\"),E=z(OR({a:T(\"colorA\"),b:T(\"colorB\"),c:T(\"colorC\"),d:T(\"colorD\"),space:T(\"colorSpace\")})(P,C,e),\"flowBase\"),R=k=>ne(1,ms(ne(v(k,2),1))),U=v(it(R(P),R(C)),b),L=v(I(v(I(v(g.member(\"z\"),.5),.5),.12),.94),I(1,v(U,.35)));return Ot(Ge(v(E.member(\"rgb\"),L),kt(0),kt(1)),E.member(\"a\"))}});var oF=IU({shape:T(\"shape\"),angle:T(\"angle\"),frequency:T(\"frequency\"),softness:T(\"softness\"),waveAmplitude:T(\"waveAmplitude\"),waveFrequency:T(\"waveFrequency\"),refraction:T(\"refraction\"),aberration:T(\"aberration\")}),aF=H({name:\"FlutedGlass\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Distortions\",description:\"Full-screen fluted glass effect \\u2014 refracts content through repeating cylindrical bars\",animatedTime:{speed:\"speed\"},props:{shape:{default:\"bars\",description:\"Cross-section shape of each flute\",compileTime:!0,transform:e=>({bars:0,rounded:1,waves:2})[e]??0,ui:{type:\"select\",options:[{label:\"Bars\",value:\"bars\"},{label:\"Rounded\",value:\"rounded\"},{label:\"Waves\",value:\"waves\"}],label:\"Shape\",group:\"Effect\"}},angle:{default:0,description:\"Direction of the flutes in degrees (0 = vertical bars)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},frequency:{default:10,description:\"Number of flutes across the longest viewport axis\",ui:{type:[\"range\",\"map\"],min:1,max:20,step:1,label:\"Frequency\",group:\"Effect\"}},softness:{default:.5,description:\"How smoothly distortion fades from each flute centre to its edge (0 = flat middle / sharp seams, 1 = gentle curve)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},waveAmplitude:{default:.06,description:\"How far each flute sways horizontally as it travels (Waves shape only)\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.01,label:\"Wave Amplitude\",group:\"Effect\",condition:{shape:\"waves\"}}},waveFrequency:{default:1.5,description:\"How many sways fit along each flute (Waves shape only)\",ui:{type:[\"range\",\"map\"],min:.1,max:10,step:.1,label:\"Wave Frequency\",group:\"Effect\",condition:{shape:\"waves\"}}},speed:{default:0,description:\"Animation speed \\u2014 drifts the flute pattern over time and flows wave perturbations\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Speed\",group:\"Animation\"}},refraction:{default:1.5,description:\"How aggressively each flute bends content beneath it\",ui:{type:[\"range\",\"map\"],min:0,max:4,step:.01,label:\"Refraction\",group:\"Effect\"}},aberration:{default:.2,description:\"Chromatic aberration \\u2014 splits RGB along the refraction direction at flute seams\",recompile:hn((e,t)=>e>0!=t>0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Aberration\",group:\"Effect\"}},lightAngle:{default:30,description:\"Direction the light source is coming from (0 = head-on, 90 = grazing)\",ui:{type:\"range\",min:-90,max:90,step:1,label:\"Light Angle\",group:\"Highlight\"}},highlight:{default:.2,description:\"Strength of the specular reflection on each flute\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Highlight\",group:\"Highlight\"}},highlightSoftness:{default:.3,description:\"Spread of the specular peak (0 = pin-tight, 1 = broad sheen)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlight Softness\",group:\"Highlight\"}},highlightColor:{default:\"#ffffff\",transform:ce,description:\"Color of the specular highlight\",ui:{type:\"color\",label:\"Highlight Color\",group:\"Highlight\"}},edges:{default:\"mirror\",description:\"How to handle edges when distortion samples beyond the canvas\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},effect:$o(YU(oF,{aberration:T(\"aberration\"),edges:T(\"edges\")}),[WU(oF,{lightAngle:T(\"lightAngle\"),highlight:T(\"highlight\"),softness:T(\"highlightSoftness\"),color:T(\"highlightColor\")})],{resultAlpha:\"straight\"})});var en=256,Fd=en*en,Que=10,Zue=6,Jue=50,epe=.15,tpe=en*.6,iF=3,spe=\"rgba16float\",nF=Mn,lF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,time:n,seed:n,turbulence:n,curlStrength:n,velFade:n,blending:n,cursorX:n,cursorY:n,cursorVelX:n,cursorVelY:n,mouseActive:n,mouseRadSq:n}),\"FogParams\"),Q_=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,Fd),access:\"mutable\"},velB:{storage:Fe(S,Fd),access:\"mutable\"},dyeA:{storage:Fe(S,Fd),access:\"mutable\"},dyeB:{storage:Fe(S,Fd),access:\"mutable\"},pressure:{storage:Fe(n,Fd),access:\"mutable\"},divergence:{storage:Fe(n,Fd),access:\"mutable\"},params:{uniform:lF}}),\"fluidLayout\"),cF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({dyeA:{storage:Fe(S,Fd),access:\"readonly\"},outTex:{storageTexture:zt(spe,\"write-only\")}}),\"outputLayout\"),rpe=Pz(Q_,{n:en,namePrefix:\"fog\",frequency:iF}),ope=Cz(Q_,{n:en,namePrefix:\"fog\"}),dF=zn(Q_,{n:en,namePrefix:\"fog\",boundary:\"toroidal\",dye:\"densityAge\",velocityCap:tpe}),{curl:Fxe,vorticity:Ixe,divergence:Yxe,jacobi:Wxe,gradSubtract:qxe,advectVel:Vxe,copyVel:Nxe,advectDye:$xe,copyDye:Xxe}=dF,ape=Mz(Q_,{n:en,namePrefix:\"fog\",frequency:iF}),npe=Ui(cF,{n:en,namePrefix:\"fog\",dye:\"densityAge\"}),uF=H({name:\"Fog\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Fog that fills the screen and interacts with the mouse\",usesPointer:!0,props:{colorA:{default:\"#e0e0e0\",transform:ce,description:\"Primary fog color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#888888\",transform:ce,description:\"Secondary fog color \\u2014 creates variation across the field\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},seed:{default:0,description:\"Deterministic starting pattern \\u2014 different seeds produce different fog configurations\",ui:{type:\"range\",min:0,max:999,step:1,label:\"Seed\",group:\"Field\"}},speed:{default:1,description:\"Simulation speed multiplier\",ui:{type:\"range\",min:.1,max:3,step:.1,label:\"Speed\",group:\"Field\"}},turbulence:{default:1,description:\"Ambient motion strength\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Turbulence\",group:\"Field\"}},detail:{default:15,description:\"Fine-scale swirling structure \\u2014 higher values produce more intricate wisps and vortices\",ui:{type:\"range\",min:0,max:50,step:1,label:\"Detail\",group:\"Field\"}},blending:{default:.3,description:\"How much the two colors blend together \\u2014 0 behaves like oil & water (colors stay distinct with sharp boundaries), 1 behaves like food coloring (colors fully mix)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Blending\",group:\"Field\"}},mouseInfluence:{default:.1,description:\"Strength of cursor influence \\u2014 move the cursor to push fog\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Cursor Influence\",group:\"Interaction\"}},mouseRadius:{default:.1,description:\"Radius of cursor influence area\",ui:{type:\"range\",min:.02,max:.5,step:.01,label:\"Cursor Radius\",group:\"Interaction\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},...Ii(()=>{let e=t=>({seed:t.num(\"seed\",0),turbulence:t.num(\"turbulence\",1),curlStrength:t.num(\"detail\",15),velFade:epe,blending:t.num(\"blending\",.3)});return{resolution:en,layout:Q_,outputLayout:cF,paramsSchema:lF,solver:{kernels:dF,jacobiIters:Que},output:{kernel:npe,key:\"fogTexture\"},write:\"thunk\",pointer:{minDrag:5e-4},init:dO({kernel:rpe,seed:t=>t.num(\"seed\",0),warm:{steps:Jue,jacobiIters:Zue,dt:.1,startTime:()=>Math.random()*60,values:(t,s)=>({dt:.1,time:t,...e(s),cursorX:0,cursorY:0,cursorVelX:0,cursorVelY:0,mouseActive:0,mouseRadSq:1})}}),solve:[lO(ope),cO(ape)],values:t=>{let s=t.num(\"mouseInfluence\",.1),r=t.num(\"mouseRadius\",.1)*en,o=t.ptr,a=o.moving&&s>0;return{dt:t.dt*t.num(\"speed\",1),time:t.elapsed,...e(t),cursorX:a?o.x*en:0,cursorY:a?o.y*en:0,cursorVelX:a?o.dx*en*15*s:0,cursorVelY:a?o.dy*en*15*s:0,mouseActive:a?1:0,mouseRadSq:r*r}}}}),gpu:{fragment:({ctx:e,computeOutputs:t,propValues:s,uniforms:r})=>{let o=t?.fogTexture;if(!o)return xt;let a=o.sample(e.uv,\"linearClamp\"),i=a.member(\"x\"),l=a.member(\"y\"),c=s.colorSpace??0,d=W(nF.mixColorsVariants[c]??nF.mixColorsLinear,\"mixColors\",[r.colorA,r.colorB,l]);return Ct(d.member(\"rgb\"),d.member(\"a\").mul(i))}}});var pF=H({name:\"FractalNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Multi-octave fractal Brownian motion noise texture with true noise evolution\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#000000\",transform:ce,description:\"First color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Second color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),octaves:{default:4,compileTime:!0,description:\"Number of noise octaves (more = more detail)\",ui:{type:\"range\",min:1,max:8,step:1,label:\"Octaves\",group:\"Effect\"}},detail:{default:2,description:\"How much finer each successive octave becomes\",ui:{type:[\"range\",\"map\"],min:1,max:4,step:.1,label:\"Detail\",group:\"Effect\"}},contrast:{default:.5,description:\"How strongly finer octaves contribute \\u2014 higher values create more texture contrast\",ui:{type:[\"range\",\"map\"],min:.1,max:1,step:.01,label:\"Contrast\",group:\"Effect\"}},speed:{default:.15,description:\"Speed at which the noise pattern evolves in place\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Speed\",group:\"Animation\"}},angle:{default:0,transform:cs,description:\"Rotation angle in degrees\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Angle\",group:\"Effect\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:ZD({angle:T(\"angle\"),detail:T(\"detail\"),contrast:T(\"contrast\"),octaves:T(\"octaves\"),seed:T(\"seed\"),space:T(\"colorSpace\")})});var ipe=JSON.stringify({type:\"sphere3D\",radius:.35}),mF=H({name:\"Frost\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photoreal frozen ice \\u2014 thickness-driven subsurface scattering that reads as a solid block, with fine frost crystals creeping in from the edges\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",chord:\"firstLobe\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=e.volumetric,a=.01,i=Lr(e,t,{scale:s.scale}),{s0:l,sX:c,sY:d,sdf:u,pxH:p}=i,_=Ln(i),f=Jr(i,s.edgeSoftness),h=go(e,i),g=h.member(\"x\"),y=h.member(\"y\"),b=z(Ge(Ce(v(u,-1),it(.3,.01)),0,1),\"depthNorm\"),x,P;if(o){let Ve=z(Ge(Ce(ne(c.member(\"w\"),l.member(\"w\")),a),-4,4),\"ddx\"),qe=z(Ge(Ce(ne(d.member(\"w\"),l.member(\"w\")),a),-4,4),\"ddy\");x=z(Os(Qe(Ve,qe,-1)),\"nBase\"),P=z(Ce(1,I(1,v(Ge(I(v(Ve,Ve),v(qe,qe)),0,2),2))),\"steepAtten\")}else{let Ve=z(v(ho(it(ne(1,v(b,b)),0)),1.8),\"bevelSlope\");x=z(Os(Qe(v(_.member(\"x\"),Ve),v(_.member(\"y\"),Ve),-1)),\"nBase\"),P=It(1)}let C=z(v(Hu(i,e),s.density),\"thickness\"),E=z(Ge(v(C,1.2),0,1),\"depthT\"),R=z(wg(C,v(Qe(1.6,.85,.45),s.absorption)),\"transmit\"),U=z(v(et(Qe(1,1,1),s.iceColor.member(\"rgb\"),kt(E)),R),\"scatteredRgb\"),L=z(v(ne(1,ot(0,it(v(s.frostDepth,.3),.01),v(u,-1))),s.frostAmount),\"frostField\"),k=z(v(s.frostScale,60),\"frostScl\"),D=.004,O=z(co(v(g,k),v(y,k)),\"fn0\"),F=Ce(ne(co(v(I(g,D),k),v(y,k)),O),D),q=Ce(ne(co(v(g,k),v(I(y,D),k)),O),D),Y=z(v(v(L,s.frostRoughness),P),\"frostNormalAmp\"),V=v(s.frostScale,40),X=W(I3,\"mxWorleyNoiseFloat2Pub\",[rt(v(g,V),v(y,V)),It(1)]),Q=z(Js(Zo(L,0),Qe(v(v(F,Y),.03),v(v(q,Y),.03),v(ot(.25,0,X),L)),Qe(0,0,0),\"frostFx\",[h,L,k,P]),\"fx3\"),te=Gi(x,Q.member(\"x\"),Q.member(\"y\")),J=Un(Er(s.lightAngle,\"light\"),-.6),oe=J.member(\"L\"),le=z($t(te,oe),\"ndl\"),se=z(Ge($t(te,J.member(\"H\")),0,1),\"ndh\"),Te=ts(I(v(le,.5),.5),.7),de=v(v(v(R,Ge(I(v($t(oe,Qe(0,0,1)),.5),.5),0,1)),s.scatter),ne(1,v(E,.4))),xe=I(v(U,I(v(Te,.6),.5)),v(de,.5)),Ae=z(I(v(co(I(v(g,190),13.7),ne(v(y,190),4.3)),.5),.5),\"glintField\"),De=I(v(Vt(I(v(r,3),v(Ae,40))),.5),.5),Ee=Js(Zo(L,0),v(v(v(ot(.72,.96,v(Ae,De)),I(v(ts(se,6),.6),.4)),L),s.sparkle),It(0),\"sparkleGlint\",[h,L,se]),ze=v(Qe(.95,.98,1),I(v(Q.member(\"z\"),.4),v(Ee,4))),Ie=v(ts(Fi(te),3),s.fresnel),ke=v(Qe(.85,.93,1),v(Ie,1.5)),Me=z(v(v(ts(se,16),.4),s.gloss),\"broadSpec\"),we=z(I(I(I(I(xe,v(Qe(.4,.46,.55),s.ambient)),kt(Me)),ke),ze),\"iceRgb\");return Js(Dr(u,p),Ot(we,f),xt,\"frost\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the ice shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the ice shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the ice shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},iceColor:{default:\"#0597fc\",transform:ce,description:\"Ice tint \\u2014 the color deep ice saturates toward (thin edges stay near white)\",ui:{type:\"color\",label:\"Ice Color\",group:\"Ice\"}},ambient:{default:0,description:\"Cool ambient fill so shadowed ice never goes black\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Ambient\",group:\"Ice\"}},edgeSoftness:{default:.05,description:\"Softness of the shape boundary edge\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Ice\"}},density:{default:5,description:\"Optical depth multiplier \\u2014 how thick and deep the ice reads\",ui:{type:\"range\",min:0,max:10,step:.01,label:\"Density\",group:\"Subsurface\"}},absorption:{default:1,description:\"Beer\\u2013Lambert absorption strength \\u2014 higher drives a deeper blue-teal core\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Absorption\",group:\"Subsurface\"}},scatter:{default:.5,description:\"Forward back-lit glow transmitted through thin ice\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Scatter\",group:\"Subsurface\"}},frostAmount:{default:.8,description:\"Overall density of the rim frost crystals\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Frost\",group:\"Frost\"}},frostDepth:{default:.4,description:\"How far the frost creeps inward from the rim\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Frost Depth\",group:\"Frost\"}},frostScale:{default:1,description:\"Crystal size \\u2014 higher = finer, busier frost\",ui:{type:\"range\",min:.3,max:4,step:.01,label:\"Frost Scale\",group:\"Frost\"}},frostRoughness:{default:3,description:\"Feathery roughness of the frost crystal surface\",ui:{type:\"range\",min:1,max:5,step:.01,label:\"Frost Roughness\",group:\"Frost\"}},sparkle:{default:.5,description:\"Tiny bright crystal glints catching the light at the rim\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Sparkle\",group:\"Frost\"}},gloss:{default:.6,description:\"Surface sheen \\u2014 the broad soft icy specular\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Gloss\",group:\"Surface\"}},fresnel:{default:.02,description:\"Cold rim glow on grazing surfaces\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Fresnel\",group:\"Surface\"}},lightAngle:{default:300,description:\"Key light direction in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Surface\"}},speed:{default:.3,description:\"Sparkle twinkle speed. 0 freezes the sparkle.\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Speed\",group:\"Animation\"}},shape:{default:ipe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var _F=H({name:\"GaborNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Oriented sine-grain noise with a fingerprint-like flow\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:1.5,description:\"Pattern scale (higher = finer, more detailed patterns; each +1 roughly doubles the frequency)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},frequency:{default:8,description:\"Frequency of the oriented waves within each cell\",ui:{type:\"range\",min:1,max:24,step:.5,label:\"Frequency\",group:\"Effect\"}},...Ja(),...In()},paint:Vs(ua(Qa(gp(VD({frequency:T(\"frequency\")})),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var{applyGlassEffect:lpe}=ip,cpe=JSON.stringify({type:\"sphere3D\",radius:.35});function dpe(e){let{childNode:t,gpu:s,convertToTexture:r,registerComputeTexture:o,getCpuValue:a,onCleanup:i,onResize:l,dimensions:c}=e,d=s?.root,u=a(\"blur\");if(!t||!d||typeof u==\"number\"&&u<=0)return null;let p=r(t),_=Math.max(1,Math.round(c.width)),f=Math.max(1,Math.round(c.height)),h=Vh(d,null,_,f,i),g=o(h.outputTexture);return l(({width:y,height:b})=>{_=Math.max(1,Math.round(y)),f=Math.max(1,Math.round(b)),h.setInputDimensions(_,f)}),{outputs:{childTexture:p,blurredTexture:g},bindInputs:y=>{let b=y(p.key);b&&h.setInputTexture(b.texture)},getComputeNodes:()=>{let y=a(\"blur\");return typeof y==\"number\"&&h.updateRadius(y*2),h.computeSteps}}}var fF=Ts({chord:\"firstLobe\",gradSampler:\"same\",bakedGradients:\"all\",stencil:\"none\",child:\"required\",surface:(e,t)=>lpe(t,e.sampler,e.childTexture,t.computeOutputs?.blurredTexture,{volumetric:e.volumetric,bakedGradients:e.bakedGradients})}),upe=e=>{let t=fF.compute(e),s=dpe(e);return!t&&!s?null:{outputs:{...s?.outputs,...t?.outputs},bindInputs:s?.bindInputs,getComputeNodes:r=>{let o=[...s?.getComputeNodes()??[],...t?.getComputeNodes(r)??[]];return o.length?o:null}}},vF=H({name:\"Glass\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Optically realistic glass lens driven in a custom shape\",requiresRTT:!0,requiresChild:!0,...fF,compute:upe,props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the glass shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the glass shape (1 = default size)\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the glass shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},cutout:{default:!1,description:\"Cut out the alpha outside the glass shape\",transform:rs,compileTime:!0,ui:{type:\"checkbox\",label:\"Cutout\",group:\"Glass\"}},...gr({refraction:{default:1,description:\"Lens refraction \\u2014 how aggressively the edges warp content beneath (0 = none, 1 = max)\",ui:{max:2}},edgeSoftness:{default:.1,description:\"Edge softness \\u2014 higher values give a wider, softer fade at the glass boundary\"}}),blur:{default:0,recompile:hn((e,t)=>e>0!=t>0),description:\"Frosted blur amount \\u2014 0 = clear glass, higher = frosted/diffuse\",ui:{type:\"range\",min:0,max:20,step:.1,label:\"Blur\",group:\"Glass\"}},...gr({thickness:{transform:e=>e*.5,description:\"Glass depth \\u2014 how far inward from the edge the refraction extends\"}}),aberration:{default:.5,recompile:hn((e,t)=>e>0!=t>0),description:\"Chromatic aberration \\u2014 splits RGB channels along the refraction vector\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Aberration\",group:\"Glass\"}},innerZoom:{default:1,description:\"Inner zoom level \\u2014 magnifies content seen through the glass\",ui:{type:[\"range\",\"map\"],min:.5,max:3,step:.01,label:\"Inner Zoom\",group:\"Glass\"}},...gr({lightAngle:{default:300,description:\"Light angle in degrees\",ui:{group:\"Highlight\"}},highlight:{default:.05,description:\"Directional edge highlight \\u2014 bright rim on the light-facing boundary\"},highlightColor:{description:\"Color of the directional edge highlight and specular glint\"},highlightSoftness:{description:\"Specular highlight softness\"},fresnel:{description:\"Fresnel rim glow \\u2014 a soft luminous halo around the glass boundary\"},fresnelSoftness:!0,fresnelColor:!0}),tintColor:{default:\"#ffffff\",transform:ce,description:\"Color tint applied to the internal directional gradient\",ui:{type:\"color\",label:\"Tint Color\",group:\"Tint\"}},tintIntensity:{default:0,description:\"Intensity of the color tint applied to the glass interior\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Tint Intensity\",group:\"Tint\"}},tintPreserveLuminosity:{default:!0,transform:rs,description:\"Preserve original brightness when tinting\",ui:{type:\"checkbox\",label:\"Preserve Luminosity\",group:\"Tint\"}},shape:{default:cpe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file \\u2014 when non-empty, activates SVG mode and triggers a shader recompile\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type \\u2014 triggers recompile when shape is switched. When empty, derived from shape JSON at mount time.\"}}});var hF=H({name:\"GlassTiles\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Distortions\",description:\"Refraction-like distortion in a tile grid pattern\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:2,description:\"The intensity of the glass tiles effect\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Intensity\",group:\"Effect\"}},tileCount:{default:20,description:\"Number of tiles across the longest dimension\",ui:{type:[\"range\",\"map\"],min:5,max:50,step:1,label:\"Tile Count\",group:\"Effect\"}},rotation:{default:0,description:\"Rotation angle of the tile grid in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},roundness:{default:0,description:\"Makes tiles more circular instead of square\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Roundness\",group:\"Effect\"}}},gpu:{fragment:e=>e.childNode?Y4({uv:e.ctx.uv,aspect:e.ctx.aspect,intensity:me(T(\"intensity\"),e),tileCount:me(T(\"tileCount\"),e),rotation:me(T(\"rotation\"),e),roundness:me(T(\"roundness\"),e),texture:e.convertToTexture(e.childNode)}):(console.error(\"You must pass a child component into the Glass Tiles shader.\"),xt),uvRemap:({uv:e,mask:t,uniforms:s,aspect:r})=>({uv:W4({uv:e,aspect:r,intensity:me(T(\"intensity\"),{uniforms:s}),tileCount:me(T(\"tileCount\"),{uniforms:s}),rotation:me(T(\"rotation\"),{uniforms:s}),roundness:me(T(\"roundness\"),{uniforms:s})}),mask:t})}});var W2=PU({intensity:T(\"intensity\"),speed:T(\"speed\"),blockDensity:T(\"blockDensity\"),mirrorAmount:T(\"mirrorAmount\")}),gF=H({name:\"Glitch\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Digital glitch that melts pixels and distorts colors\",props:{intensity:{default:.5,description:\"Overall glitch strength and frequency of glitch bursts\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}},speed:{default:1,description:\"How fast the glitch pattern evolves\",ui:{type:\"range\",min:.1,max:5,step:.1,label:\"Speed\",group:\"Effect\"}},rgbShift:{default:5,description:\"Amount of chromatic aberration (RGB channel splitting)\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.5,label:\"RGB Shift\",group:\"Effect\"}},blockDensity:{default:10,description:\"Base number of horizontal glitch bands\",ui:{type:[\"range\",\"map\"],min:2,max:50,step:1,label:\"Block Density\",group:\"Effect\"}},colorBarIntensity:{default:.2,description:\"Intensity of vivid neon color bar overlay in glitch regions\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Color Bars\",group:\"Effect\"}},mirrorAmount:{default:.3,description:\"Chance of glitch blocks showing mirrored/flipped content\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Mirror\",group:\"Effect\"}},scanlineIntensity:{default:.2,description:\"Visibility of CRT-style horizontal scanlines in distorted areas\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Scanlines\",group:\"Effect\"}}},effect:$o(CU(W2,{shift:T(\"rgbShift\")}),[MU(W2,{intensity:T(\"colorBarIntensity\")}),EU(W2,{intensity:T(\"scanlineIntensity\")})])});var yF=H({name:\"Glow\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Soft glow effect with adjustable intensity\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:1,description:\"Glow intensity (brightness of the glow effect)\",ui:{type:[\"range\",\"map\"],min:0,max:50,step:.5,label:\"Intensity\",group:\"Effect\"}},threshold:{default:.5,description:\"Brightness threshold for glow extraction (lower = more glow)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Threshold\",group:\"Effect\"}},size:{default:25,description:\"Glow spread in pixels (clean up to ~72px, mild banding above)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Glow Size\",group:\"Effect\"}}},...LU({intensity:T(\"intensity\"),threshold:T(\"threshold\"),size:T(\"size\")})});var bF=H({name:\"Godrays\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Volumetric light rays emanating from a point\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{center:{default:{x:0,y:0},transform:Xe,description:\"The center point of the god rays\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},density:{default:.3,description:\"Frequency of ray sectors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Density\",group:\"Effect\"}},intensity:{default:.8,description:\"Ray visibility within sectors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Intensity\",group:\"Effect\"}},spotty:{default:1,description:\"Density of spots on rays (higher = more spots)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Spotty\",group:\"Effect\"}},speed:{default:.5,description:\"Animation speed of the rays\",ui:{type:\"range\",min:0,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},rayColor:{default:\"#4283fb\",transform:ce,description:\"Color of the light rays\",ui:{type:\"color\",label:\"Ray Color\",group:\"Colors\"}},backgroundColor:{default:\"transparent\",transform:ce,description:\"Background color\",ui:{type:\"color\",label:\"Background Color\",group:\"Colors\"}}},paint:UD({color:T(\"rayColor\"),background:T(\"backgroundColor\"),coverage:vg(_D(T(\"center\")),yD({density:T(\"density\"),intensity:T(\"intensity\"),spotty:T(\"spotty\")}))})});var ppe=JSON.stringify({type:\"sphere3D\",radius:.35}),q2=7,kp=e=>{let t=Math.sin(e*12.9898)*43758.5453;return t-Math.floor(t)},xF=[],AF=[];for(let e=0;e<q2;e++){let t=e*2.399963,s=Math.sqrt((e+.5)/q2);xF.push([s*Math.cos(t),s*Math.sin(t),.5+kp(e+1),.5+kp(e+7)]),AF.push([.5+kp(e+13),kp(e+2)*6.2831853,kp(e+5)*6.2831853,kp(e+9)*6.2831853])}var wF=H({name:\"Goo\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photoreal wet liquid \\u2014 animated 3D metaball blobs that merge and bulge to loosely form the shape, with sliding wet highlights, a clearcoat rim and a translucent body\",animatedTime:{speed:\"speed\"},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the goo\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the goo (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the goo in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},gooColor:{default:\"#37c95a\",transform:ce,description:\"Base liquid color (a glossy slime green by default)\",ui:{type:\"color\",label:\"Goo Color\",group:\"Goo\"}},translucency:{default:.6,description:\"Subsurface glow strength \\u2014 deep lobes transmit more color\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Translucency\",group:\"Goo\"}},absorb:{default:2,description:\"How fast thin areas clear and thick areas saturate (Beer\\u2013Lambert)\",ui:{type:\"range\",min:0,max:6,step:.01,label:\"Absorption\",group:\"Goo\"}},containment:{default:.9,description:\"0 = free blobs that ignore the shape, 1 = goo fills the shape and its surface conforms to the 3D form (highlights track the faces as it rotates)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Containment\",group:\"Goo\"}},edgeSoftness:{default:.06,description:\"Softness of the blob coverage edge\",ui:{type:\"range\",min:0,max:.3,step:.005,label:\"Edge Softness\",group:\"Goo\"}},blobScale:{default:.3,description:\"Base radius of each liquid lobe\",ui:{type:\"range\",min:.1,max:1,step:.01,label:\"Blob Size\",group:\"Blobs\"}},spread:{default:1,description:\"How far the lobes scatter across the shape\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Spread\",group:\"Blobs\"}},merge:{default:.6,description:\"How much the lobes fuse together \\u2014 low = distinct beads, high = one fused mass\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Merge\",group:\"Blobs\"}},bulge:{default:1,description:\"How 3D-rounded each lobe reads \\u2014 dome height of the wet surface\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Bulge\",group:\"Blobs\"}},threshold:{default:.5,description:\"Iso-surface level \\u2014 lower makes fatter, fuller blobs\",ui:{type:\"range\",min:.2,max:.9,step:.01,label:\"Threshold\",group:\"Blobs\"}},lightAngle:{default:300,description:\"Light direction in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Lighting\"}},wetness:{default:2,description:\"Master scale on the sharp specular highlight and clearcoat rim\",ui:{type:\"range\",min:0,max:5,step:.01,label:\"Highlights\",group:\"Lighting\"}},fresnel:{default:.25,description:\"Clearcoat fresnel rim strength wrapping each lobe\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Fresnel\",group:\"Lighting\"}},specColor:{default:\"#ffffff\",transform:ce,description:\"Highlight / rim color (white reads as a clear wet coat)\",ui:{type:\"color\",label:\"Highlight Color\",group:\"Lighting\"}},ambient:{default:.25,description:\"Base fill so shadowed goo isn't black\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Ambient\",group:\"Lighting\"}},speed:{default:.5,description:\"Drift and breathe speed of the blobs. 0 pauses.\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Speed\",group:\"Animation\"}},wobble:{default:.5,description:\"Drift amplitude \\u2014 how far the lobe centers wander\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Wobble\",group:\"Animation\"}},breathe:{default:.3,description:\"Radius pulsing amplitude \\u2014 lobes swell and shrink\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Breathe\",group:\"Animation\"}},seed:{default:1,description:\"Variation offset \\u2014 shifts the blob arrangement\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}},shape:{default:ppe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ts({chord:\"firstLobe\",stencil:{kind:\"forward\",eps:.004},surface:(e,t)=>{let{uniforms:s}=t,r=z(wt(t,\"seed\"),\"gooT\"),o=.004,a=z(e.surf0,\"s0\"),i=z(e.sdfUV,\"sdfUV\"),l=z(ne(i.member(\"x\"),.5),\"px\"),c=z(ne(i.member(\"y\"),.5),\"py\"),d=ho(I(v(l,l),v(c,c))),u=z(v(s.spread,.45),\"spread\"),p=z(v(s.wobble,.12),\"driftAmp\"),_=z(s.breathe,\"breatheAmp\"),f=I(.65,v(s.merge,.7)),h=z(v(v(s.blobScale,.38),f),\"baseR\"),g=I(I(I(u,v(h,I(1,_))),p),.2),y=null,b=null,x=null;for(let we=0;we<q2;we++){let Ve=xF[we],qe=AF[we],st=z(I(v(Ve[0],u),v(p,Vt(I(v(r,Ve[2]),qe[1])))),`cx${we}`),pt=z(I(v(Ve[1],u),v(p,Do(I(v(r,Ve[3]),qe[2])))),`cy${we}`),Ze=z(v(h,I(1,v(_,Vt(I(v(r,qe[0]),qe[3]))))),`rVal${we}`),nt=z(Ce(1,it(v(Ze,Ze),1e-4)),`invR2_${we}`),ht=z(ne(l,st),`dcx${we}`),yt=z(ne(c,pt),`dcy${we}`),St=tr(v(v(I(v(ht,ht),v(yt,yt)),nt),-1)),Mt=z(ne(I(l,o),st),`dxx${we}`),Rt=tr(v(v(I(v(Mt,Mt),v(yt,yt)),nt),-1)),Kt=z(ne(I(c,o),pt),`dyy${we}`),Wt=tr(v(v(I(v(ht,ht),v(Kt,Kt)),nt),-1));y=y?I(y,St):St,b=b?I(b,Rt):Rt,x=x?I(x,Wt):Wt}let P=z(ne(1,ot(-.06,.06,a.member(\"x\"))),\"inside0\"),C=z(ne(1,ot(-.06,.06,e.surfX.member(\"x\"))),\"insideX\"),E=z(ne(1,ot(-.06,.06,e.surfY.member(\"x\"))),\"insideY\"),R=s.containment,U=z(I(v(y,et(It(1),P,R)),v(P,R)),\"H0\"),L=z(I(v(b,et(It(1),C,R)),v(C,R)),\"Hx\"),k=z(I(v(x,et(It(1),E,R)),v(E,R)),\"Hy\"),D=z(it(s.edgeSoftness,.001),\"soft\"),O=ot(ne(s.threshold,D),I(s.threshold,D),U),F=Ce(ne(L,U),o),q=Ce(ne(k,U),o),Y=z(I(v(s.bulge,.12),.03),\"metaSlope\"),V=Yr(Yr(P,C),E),X=z(v(v(R,1.6),V),\"shapeRelief\"),Q=Ge(Ce(ne(e.surfX.member(\"w\"),a.member(\"w\")),o),-6,6),te=Ge(Ce(ne(e.surfY.member(\"w\"),a.member(\"w\")),o),-6,6),J=z(Os(Qe(I(v(v(F,-1),Y),v(Q,X)),I(v(v(q,-1),Y),v(te,X)),-1)),\"n\"),oe=z(Ge(I(v(ho(it(ne(U,s.threshold),0)),s.bulge),v(v(it(v(a.member(\"x\"),-1),0),R),1.2)),0,1),\"thick\"),le=Un(Er(s.lightAngle,\"light\"),-.8),se=z(Ge($t(J,le.member(\"L\")),0,1),\"ndl\"),Te=z(Ge($t(J,le.member(\"H\")),0,1),\"ndh\"),de=v(ts(Te,232),1.7),xe=v(ts(Te,32),.25),Ae=v(ot(.992,.999,Te),1.9),De=v(ts(Fi(J),5),s.fresnel),Ee=v(s.specColor.member(\"rgb\"),v(I(I(I(de,xe),Ae),De),s.wetness)),ze=tr(v(v(s.absorb,-1),ne(1,oe))),Ie=z(s.gooColor.member(\"xyz\"),\"gooRgb\"),ke=v(v(Ie,v(s.translucency,ze)),I(v(se,.5),.5)),Me=z(I(I(v(Ie,I(s.ambient,v(se,.7))),ke),Ee),\"gooOut\");return Js(Hz(d,g),Ot(Me,v(O,s.gooColor.member(\"w\"))),xt,\"goo\")}})});var TF=H({name:\"GradientMap\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Maps source luminance through an animated color gradient (Photoshop-style gradient map)\",props:{palette:{default:\"rainbow\",compileTime:!0,description:\"Built-in cosine palette, or Custom Colors to pick your own stops\",ui:{type:\"select\",options:[{label:\"Rainbow\",value:\"rainbow\"},{label:\"Sunset\",value:\"sunset\"},{label:\"Ocean\",value:\"ocean\"},{label:\"Fire\",value:\"fire\"},{label:\"Pastel\",value:\"pastel\"},{label:\"Neon\",value:\"neon\"},{label:\"Custom Colors\",value:\"custom\"}],label:\"Palette\",group:\"Colors\"}},colorLow:{default:\"#1a0b2e\",transform:ce,description:\"Color for the darkest tones\",ui:{type:\"color\",label:\"Shadows\",group:\"Colors\",condition:{palette:\"custom\"}}},colorMid:{default:\"#e94560\",transform:ce,description:\"Color for the midtones\",ui:{type:\"color\",label:\"Midtones\",group:\"Colors\",condition:{palette:\"custom\"}}},colorHigh:{default:\"#f9ed69\",transform:ce,description:\"Color for the brightest tones\",ui:{type:\"color\",label:\"Highlights\",group:\"Colors\",condition:{palette:\"custom\"}}},speed:{default:.15,description:\"Gradient animation speed (0 = static). The gradient cycles seamlessly.\",ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.01,label:\"Speed\",group:\"Animation\"}},contrast:{default:1,description:\"Steepness of the luminance-to-gradient mapping\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Contrast\",group:\"Effect\"}},blackPoint:{default:0,description:\"Input shadow clip \\u2014 luminance at/below this maps to the start of the gradient\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Black Point\",group:\"Effect\"}},whitePoint:{default:1,description:\"Input highlight clip \\u2014 luminance at/above this maps to the end of the gradient\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"White Point\",group:\"Effect\"}},strength:{default:1,description:\"Blend between the original image (0) and the gradient-mapped result (1)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},colorSpace:{default:\"oklch\",transform:at,compileTime:!0,description:\"Color space for interpolating custom colors\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\",condition:{palette:\"custom\"}}}},effect:f8({palette:T(\"palette\"),colorLow:T(\"colorLow\"),colorMid:T(\"colorMid\"),colorHigh:T(\"colorHigh\"),speed:T(\"speed\"),contrast:T(\"contrast\"),blackPoint:T(\"blackPoint\"),whitePoint:T(\"whitePoint\"),strength:T(\"strength\"),colorSpace:T(\"colorSpace\")}),identityWhen:Uo(\"strength\"),missingChildMessage:\"You must pass a child component into the GradientMap shader.\"});var SF=H({name:\"Grayscale\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Convert colors to black and white\",props:{},effect:n8(),missingChildMessage:\"You must pass a child component into the Grayscale shader.\"});var PF=QR({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"flippedY\"}),thickness:T(\"thickness\"),softness:T(\"softness\"),variation:T(\"variation\")}),CF=H({name:\"Grid\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Simple grid lines pattern with adjustable thickness and rotation\",acceptsUVContext:!0,props:{color:{default:\"#ffffff\",transform:ce,description:\"The color of the grid lines\",ui:{type:\"color\",label:\"Line Color\",group:\"Colors\"}},cellColor:{default:\"transparent\",transform:ce,description:\"Fill color of the cells (transparent = lines only). Pair with Variation for a tiled look.\",ui:{type:\"color\",label:\"Cell Color\",group:\"Colors\"}},cells:{default:10,description:\"Number of cells along the canvas height (cells stay square; the width fits as many as the aspect ratio allows)\",ui:{type:[\"range\",\"map\"],min:1,max:50,step:1,label:\"Cells\",group:\"Effect\"}},thickness:{default:1,description:\"Thickness of grid lines (normalized, 0.0-1.0)\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Thickness\",group:\"Effect\"}},rotation:{default:0,description:\"Rotation of the grid in degrees. At 45\\xB0 this produces a crosshatch/diamond pattern.\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the line edges (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},variation:{default:0,description:\"Per-cell random lightening/darkening of the cell fill color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},mapSampleUVs:_g({cells:T(\"cells\"),rotation:T(\"rotation\"),props:[\"thickness\",\"cells\",\"rotation\",\"softness\"]}),paint:Zr({fill:Sc(T(\"cellColor\"),PF.shade),stroke:T(\"color\"),mask:PF.lines,space:T(\"colorSpace\")})});var MF=H({name:\"GridDistortion\",role:\"warp\",category:\"Interactive\",description:\"Interactive grid distortion controlled by mouse position\",boundingBoxDeclaration:{aspectRatio:null},usesPointer:!0,props:{intensity:{default:1,description:\"Strength of the distortion effect\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Intensity\",group:\"Effect\"}},decay:{default:3,description:\"Rate of distortion decay (higher = faster)\",ui:{type:\"range\",min:0,max:10,step:.1,label:\"Decay\",group:\"Effect\"}},radius:{default:1,description:\"Radius of the distortion effect\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Radius\",group:\"Effect\"}},gridSize:{default:20,recompile:hn((e,t)=>Rg(e)!==Rg(t)),description:\"Resolution of the distortion grid (higher = more detailed)\",ui:{type:[\"range\",\"map\"],min:8,max:128,step:1,label:\"Grid Size\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},...Ec(I8({gridSize:T(\"gridSize\"),decay:T(\"decay\"),intensity:T(\"intensity\"),radius:T(\"radius\"),output:\"displacement\"})),map:nU({gridSize:T(\"gridSize\"),output:\"displacement\"}),uvRemapIdentityWhen:({computeOutputs:e})=>!e?.displacement});var EF=H({name:\"Group\",role:\"structural\",species:\"custom\",category:\"Utilities\",description:\"Container for organizing and composing child effects \\u2014 supports flex-like flow layout (column/row stacking via the flow prop)\",requiresChild:!0,boundingBoxDeclaration:{aspectRatio:null,boxResamplesContent:!0},props:{},gpu:{fragment:({childNode:e})=>e??xt}});var mpe=e=>e===\"cmyk\"?1:0,_pe=e=>e===\"cmyk\"||e===1?\"cmyk\":\"classic\",kF=H({name:\"Halftone\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Halftone dot pattern effect for printing aesthetics\",blendWithChildren:!1,props:{style:{default:\"classic\",transform:mpe,compileTime:!0,description:\"Halftone rendering style\",ui:{type:\"select\",options:[{label:\"Simple\",value:\"classic\"},{label:\"CMYK Print\",value:\"cmyk\"}],label:\"Style\",group:\"Effect\"}},frequency:{default:100,description:\"Frequency of the halftone dots\",ui:{type:[\"range\",\"map\"],min:10,max:300,step:1,label:\"Frequency\",group:\"Effect\"}},angle:{default:45,transform:cs,description:\"Rotation angle of the pattern (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\",condition:{style:\"classic\"}}},cyanAngle:{default:15,transform:cs,description:\"Screen angle for the cyan plate (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Cyan Angle\",group:\"Plate Angles\",condition:{style:\"cmyk\"}}},magentaAngle:{default:75,transform:cs,description:\"Screen angle for the magenta plate (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Magenta Angle\",group:\"Plate Angles\",condition:{style:\"cmyk\"}}},yellowAngle:{default:0,transform:cs,description:\"Screen angle for the yellow plate (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Yellow Angle\",group:\"Plate Angles\",condition:{style:\"cmyk\"}}},blackAngle:{default:45,transform:cs,description:\"Screen angle for the black plate (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Black Angle\",group:\"Plate Angles\",condition:{style:\"cmyk\"}}},misprint:{default:0,description:\"Simulated mis-registration between plates. Plates are offset around the misprint angle, producing color fringing at the edges of inked regions.\",ui:{type:[\"range\",\"map\"],min:0,max:.01,step:5e-4,label:\"Misprint\",group:\"Effect\",condition:{style:\"cmyk\"}}},misprintAngle:{default:0,transform:cs,description:\"Direction the plates drift apart. Rotating this rotates the color-fringing pattern.\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Misprint Angle\",group:\"Effect\",condition:{style:\"cmyk\"}}},paperColor:{default:\"#ffffff\",transform:ce,description:\"Paper/substrate color shown where no ink lands\",ui:{type:\"color\",label:\"Paper\",group:\"Inks\",condition:{style:\"cmyk\"}}},cyanColor:{default:\"#00ffff\",transform:ce,description:\"Cyan ink color\",ui:{type:\"color\",label:\"Cyan\",group:\"Inks\",condition:{style:\"cmyk\"}}},magentaColor:{default:\"#ff00ff\",transform:ce,description:\"Magenta ink color\",ui:{type:\"color\",label:\"Magenta\",group:\"Inks\",condition:{style:\"cmyk\"}}},yellowColor:{default:\"#ffff00\",transform:ce,description:\"Yellow ink color\",ui:{type:\"color\",label:\"Yellow\",group:\"Inks\",condition:{style:\"cmyk\"}}},blackColor:{default:\"#000000\",transform:ce,description:\"Black (key) ink color\",ui:{type:\"color\",label:\"Black\",group:\"Inks\",condition:{style:\"cmyk\"}}}},effect:nD(T(\"style\"),_pe,{classic:oD({angle:T(\"angle\"),frequency:T(\"frequency\")}),cmyk:aD({paper:T(\"paperColor\"),frequency:T(\"frequency\"),misprint:T(\"misprint\"),misprintAngle:T(\"misprintAngle\"),plates:[{channel:\"cyan\",screenAngle:T(\"cyanAngle\"),ink:T(\"cyanColor\")},{channel:\"magenta\",screenAngle:T(\"magentaAngle\"),ink:T(\"magentaColor\")},{channel:\"yellow\",screenAngle:T(\"yellowAngle\"),ink:T(\"yellowColor\")},{channel:\"black\",screenAngle:T(\"blackAngle\"),ink:T(\"blackColor\")}]})})});var BF=H({role:\"shape\",name:\"Heart\",description:\"Heart shape with adjustable size\",shape:{distance:({x:e,y:t},s)=>uR(e,t,s(\"radius\")),colorDescription:\"Fill color of the heart\",centerDescription:\"Center position of the heart\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.32,description:\"Size of the heart (fit radius)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Size\",group:\"Shape\"}}}}});var fpe=JSON.stringify({type:\"sphere3D\",radius:.35}),vpe=[{color:\"#02010f\",position:0},{color:\"#2a0a8a\",position:.2},{color:\"#a41c9b\",position:.45},{color:\"#e8632b\",position:.7},{color:\"#f9e25f\",position:.9},{color:\"#ffffff\",position:1}],V2=(e,t,s,r)=>{let o=et(-.6,.6,s),a=ne(hL(I(v(t,4),v(It(r),13.7)),I(v(s,3),v(It(r),7.1))),.5),i=z(I(ne(e,o),v(a,.12)),\"aEff\");return Ge(v(tr(Ce(v(v(i,i),-1),.0256)),v(ot(0,.15,s),ne(1,ot(.85,1,s)))),0,1)},zF=H({name:\"Heatmap\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Thermal-camera heat flowing through any 2D, SVG, or 3D shape. Powered by Paper Shaders.\",animatedTime:{speed:\"speed\"},...Ts({stencil:\"centre\",surface:(e,t)=>{let{uniforms:s,propValues:r}=t,o=wt(t),a=z(e.sdfUV,\"suv\"),i=it(s.scale,.001),l=z(Ce(e.surf0.member(\"r\"),i),\"f\"),c=z(ne(1,ot(-.003,.003,l)),\"inside\"),d=ii(s.angle,\"travel\"),u=z(ne(a.member(\"xy\"),rt(.5,.5)),\"rel\"),p=z($t(u,d.tangent),\"a\"),_=z(v($t(u,d.perp),-1),\"b\"),f=z(ne(v(.1,o),.3),\"tBase\"),h=I(.8,v(.8,tr(Ce(v(it(v(l,-1),0),-1),.12))));h=z(et(h,0,V2(p,_,Qr(f),1)),\"inner1\"),h=z(et(h,0,V2(p,_,Qr(I(f,1/3)),2)),\"inner2\"),h=z(et(h,0,V2(p,_,Qr(I(f,2/3)),3)),\"inner3\"),h=v(h,v(2,s.innerGlow)),h=I(h,v(v(s.contour,2),tr(Ce(v(ms(l),-1),.03)))),h=v(Yr(h,1),c),h=z(ts(it(h,0),1.2),\"inner\");let g=tr(Ce(v(it(l,0),-1),I(.05,v(.15,s.outerGlow)))),y=Qr(ne(v(f,3),.1)),b=z(Qr(ne(v(p,1.2),y)),\"yy\"),x=I(.5,v(ot(.3,.65,b),ne(1,ot(.65,1,b)))),P=z(v(v(v(v(v(v(.9,ts(g,.8)),x),5),s.outerGlow),s.outerGlow),ne(1,c)),\"outer\"),C=Ge(I(h,P),0,1),E=z(v(v(v(ot(-.06,.1,a.member(\"x\")),ne(1,ot(.9,1.06,a.member(\"x\")))),ot(-.06,.1,a.member(\"y\"))),ne(1,ot(.9,1.06,a.member(\"y\")))),\"frameFade\");C=v(C,E),C=I(C,v(.005,ne(xp(v(a.member(\"x\"),991.3),v(a.member(\"y\"),787.7)),.5))),C=z(Ge(C,0,1),\"heat\");let R=v(Ge(I(c,P),0,1),E),U=r.colorSpace??0,L=r.stopCount??0,k;L>1?k=En(C,{colorsArray:s.colorsArray,positionsArray:s.positionsArray,convertedColorsArray:s.convertedColorsArray,stopCount:s.stopCount},U):k=W(No[U]??Ar,\"mixColors\",[s.colorA,s.colorB,C]);let D=z(k,\"ramp\");return Ot(Ge(D.member(\"xyz\"),Qe(0,0,0),Qe(1,1,1)),v(D.member(\"w\"),R))}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the heatmap shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the heatmap shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the heatmap shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},colorA:{default:\"#02010f\",transform:ce,description:\"Cold color (used when no gradient stops are set)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#f9e25f\",transform:ce,description:\"Hot color (used when no gradient stops are set)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:{...Ms(),default:vpe},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the heat ramp interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},innerGlow:{default:.4,description:\"Heat filling the inside of the shape\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Inner Glow\",group:\"Heat\"}},outerGlow:{default:.2,description:\"Heat radiating beyond the silhouette\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Outer Glow\",group:\"Heat\"}},contour:{default:.5,description:\"Heat concentrated along the shape boundary\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Contour\",group:\"Heat\"}},angle:{default:90,description:\"Direction the heat waves travel, in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Angle\",group:\"Heat\"}},speed:{default:1,description:\"Speed of the flowing heat. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Heat\"}},shape:{default:fpe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var RF=ZR({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"clockwise\"}),thickness:T(\"thickness\"),softness:T(\"softness\"),variation:T(\"variation\")}),DF=H({name:\"HexGrid\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Honeycomb hexagonal grid pattern\",acceptsUVContext:!0,props:{colorA:{default:\"#000000\",transform:ce,description:\"Cell fill color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Grid line color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},cells:{default:8,description:\"Number of hexagons along the canvas height (the width fits as many as the aspect ratio allows)\",ui:{type:[\"range\",\"map\"],min:1,max:40,step:1,label:\"Cells\",group:\"Effect\"}},thickness:{default:1,description:\"Thickness of the hex grid lines\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Thickness\",group:\"Effect\"}},rotation:{default:0,transform:cs,description:\"Rotation of the grid in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the line edges (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},variation:{default:0,description:\"Per-cell random lightening/darkening of the cell fill color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:Sc(T(\"colorA\"),RF.shade),stroke:T(\"colorB\"),mask:RF.lines,space:T(\"colorSpace\")})});var hpe=JSON.stringify({type:\"sphere3D\",radius:.35}),gpe=Po,LF=H({name:\"Hologram\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Volumetric sci-fi hologram \\u2014 a translucent emissive projection of the shape with fresnel-lit edges, depth scan-lines that wrap 3D forms, CRT scanlines, flicker and beam wobble\",animatedTime:{speed:\"speed\"},...Ts({chord:\"firstLobe\",stencil:{kind:\"central\",eps:.008},placeUV:e=>{let{uniforms:t,ctx:s}=e,r=wt(e),o=s.uv.member(\"y\"),a=I(v(Vt(I(v(o,38),v(r,2.3))),.5),v(Vt(ne(v(o,11.5),v(r,1.7))),.5));return rt(I(s.uv.member(\"x\"),v(v(a,t.distortion),.012)),o)},surface:(e,t)=>{let{uniforms:s,ctx:r}=t,o=z(wt(t),\"animTime\"),a=e.volumetric,i=.008,l=z(e.surf0,\"sC\"),c=z(Ce(1,r.viewportSize.member(\"y\")),\"pxH\"),d=z(Ce(l.member(\"x\"),s.scale),\"sdf\"),u=z(e.surfX,\"sR\"),p=z(e.surfL,\"sL\"),_=z(e.surfY,\"sU\"),f=z(e.surfD,\"sD\"),h=it(v(s.edgeSoftness,.5),.001),g=z(Ce(ne(u.member(\"x\"),p.member(\"x\")),i*2),\"gradX\"),y=z(it(v(d,-1),0),\"thickness\"),b=z(tr(v(y,-5.5)),\"thin\"),x,P;if(a){let Ee=ts(Ge(ne(1,ms(z(Os(Qe(Ge(Ce(ne(u.member(\"w\"),p.member(\"w\")),i*2),-2,2),Ge(Ce(ne(_.member(\"w\"),f.member(\"w\")),i*2),-2,2),-1)),\"nrm\").member(\"z\"))),0,1),2.2);x=I(v(b,.55),Ee),P=l.member(\"w\")}else x=b,P=y;let C=z(v(x,s.edgeGlow),\"rim\"),E=v(ne(1,tr(v(y,-5))),s.fill),R=z(Qr(ne(v(P,s.depthScale),v(o,1.2))),\"phaseFract\"),U=v(ot(.06,0,Yr(R,ne(1,R))),s.depthLines),L=I(v(Vt(v(ne(v(r.uv.member(\"y\"),s.scanlineScale),v(o,v(s.scanlineSpeed,3))),gpe)),.5),.5),k=z(et(It(1),L,s.scanlines),\"scanMask\"),D=Qr(v(o,.16)),O=z(ne(r.uv.member(\"y\"),D),\"sweepD\"),F=z(v(tr(v(v(O,O),-650)),s.sweep),\"sweepGlow\"),q=I(v(Vt(v(o,40)),.04),v(Vt(v(o,23.3)),.03)),Y=ot(.6,.95,I(v(co(v(o,6),11),.5),.5)),V=v(I(1,q),ne(1,v(v(s.flicker,Y),.7))),X=I(1,v(v(ne(xp(I(v(r.uv.member(\"x\"),r.viewportSize.member(\"x\")),v(o,57.3)),v(r.uv.member(\"y\"),r.viewportSize.member(\"y\"))),.5),s.grain),.6)),Q=z(v(I(v(v(v(I(I(E,C),U),k),V),X),v(F,k)),s.brightness),\"core\"),te=I(v(s.distortion,.12),.04),J=z(v(v(v(C,g),te),.1),\"fringe\"),oe=I(Q,J),le=ne(Q,J),se=s.color.member(\"rgb\"),Te=Qe(v(se.member(\"x\"),oe),v(se.member(\"y\"),Q),v(se.member(\"z\"),le)),de=Ge(I(v(C,.5),F),0,.8),xe=z(et(Te,kt(Q),kt(de)),\"hotRgb\"),Ae=ot(0,1,Ge(v(Ce(v(d,-1),h),32),0,1)),De=v(Ge(Q,0,1),Ae);return Js(Dr(d,c),Ot(xe,De),xt,\"hologram\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the hologram shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the hologram shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the hologram shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},color:{default:\"#5ec8ff\",transform:ce,description:\"Projection tint \\u2014 the color the whole hologram emits (classic sci-fi cyan by default)\",ui:{type:\"color\",label:\"Color\",group:\"Style\"}},brightness:{default:2.5,description:\"Overall emission gain. Bright edges blow out toward white for a hot, projected look.\",ui:{type:\"range\",min:.2,max:3,step:.01,label:\"Brightness\",group:\"Style\"}},edgeSoftness:{default:.04,description:\"Softness of the shape boundary edge \\u2014 keep low for crisp projection edges\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Style\"}},fill:{default:.45,description:\"Translucent interior glow. Driven by optical thickness, so thicker parts of a 3D form glow more \\u2014 the core volumetric cue.\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Volume Fill\",group:\"Volume\"}},edgeGlow:{default:1,description:\"Fresnel rim intensity \\u2014 bright glowing silhouette and, on 3D shapes, the grazing internal walls of extruded logos\",ui:{type:\"range\",min:0,max:2.5,step:.01,label:\"Edge Glow\",group:\"Volume\"}},depthLines:{default:.5,description:\"Iso-depth scan-lines that hug the surface and reveal the 3D form like a volumetric scan. On flat shapes they become concentric contour rings.\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Depth Lines\",group:\"Volume\"}},depthScale:{default:26,description:\"Density of the depth scan-lines \\u2014 higher packs more contour bands through the volume\",ui:{type:\"range\",min:4,max:70,step:1,label:\"Depth Density\",group:\"Volume\"}},scanlines:{default:.55,description:\"CRT scanline contrast \\u2014 how dark the gaps between horizontal projection lines get\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Scanlines\",group:\"Scanlines\"}},scanlineScale:{default:130,description:\"Number of horizontal scanlines across the canvas height\",ui:{type:\"range\",min:20,max:400,step:1,label:\"Scanline Count\",group:\"Scanlines\"}},scanlineSpeed:{default:1,description:\"How fast the scanlines drift upward \\u2014 0 holds them static\",ui:{type:\"range\",min:0,max:5,step:.01,label:\"Scanline Drift\",group:\"Scanlines\"}},sweep:{default:.1,description:\"Brightness of the refresh bar that sweeps vertically through the projection\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Sweep Bar\",group:\"Scanlines\"}},flicker:{default:.3,description:\"Unstable-projector flicker \\u2014 a fast shimmer plus occasional brightness dropouts\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Flicker\",group:\"Interference\"}},distortion:{default:.1,description:\"Continuous horizontal wobble and chromatic edge fringing \\u2014 the wavering of an unstable beam\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Distortion\",group:\"Interference\"}},grain:{default:.7,description:\"Fine projection static \\u2014 animated per-pixel noise over the emission\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Grain\",group:\"Interference\"}},speed:{default:1,description:\"Master animation speed for every animated layer. 0 freezes the projection.\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Speed\",group:\"Animation\"}},shape:{default:hpe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file \\u2014 when non-empty, activates SVG mode and triggers a shader recompile\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type \\u2014 triggers recompile when shape is switched. When empty, derived from shape JSON at mount time.\"}}});var ype=JSON.stringify({type:\"sphere3D\",radius:.35}),bpe=1/3,xpe=2/3,Xg=Math.cos(300*Math.PI/180),Hg=Math.sin(300*Math.PI/180),Ape=.37164708971977234,wpe=-.6437116265296936,Tpe=-.6689647436141968,UF=e=>fL(e,[0,bpe,xpe]),OF=H({name:\"Holographic\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Iridescent holographic foil sticker with animated rainbow sheen and glitter flakes\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",stencil:{kind:\"forward\",eps:.008},surface:(e,t)=>{let{uniforms:s}=t,r=z(wt(t),\"animTime\"),o=e.volumetric,a=.008,i=Lr(e,t,{scale:s.scale}),{s0:l,sX:c,sY:d,sdf:u,pxH:p}=i,_=Jr(i,s.edgeSoftness),f=z(e.sdfUV,\"suv\"),h=go(e,i,{uv:f}),g=h.member(\"x\"),y=h.member(\"y\"),b=ne(f.member(\"x\"),.5),x=ne(f.member(\"y\"),.5),P,C,E;if(o){let pt=z(Ge(Ce(ne(c.member(\"w\"),l.member(\"w\")),a),-1.5,1.5),\"baseNxVol\"),Ze=z(Ge(Ce(ne(d.member(\"w\"),l.member(\"w\")),a),-1.5,1.5),\"baseNyVol\");P=pt,C=Ze,E=z(Ce(1,I(1,v(Ge(I(v(pt,pt),v(Ze,Ze)),0,2),2))),\"steepAtten\")}else{let pt=Ln(i,a),Ze=z(v(ne(1,Ge(Ce(v(u,-1),.06),0,1)),.35),\"domeSlope\");P=z(v(pt.member(\"x\"),Ze),\"baseNx\"),C=z(v(pt.member(\"y\"),Ze),\"baseNy\"),E=It(1)}let R=z(v(s.crinkleScale,1.35),\"crScale\"),U=z(v(v(s.crinkle,.8),E),\"crAmp\"),L=.02,k=z(co(I(v(g,R),2.7),I(v(y,R),11.3)),\"w0\"),D=Ce(ne(co(I(v(I(g,L),R),2.7),I(v(y,R),11.3)),k),L),O=Ce(ne(co(I(v(g,R),2.7),I(v(I(y,L),R),11.3)),k),L),F=z(Os(Qe(I(P,v(v(D,U),.4)),I(C,v(v(O,U),.4)),-1)),\"n\"),q=z(v(s.foilScale,2.2),\"fScale\"),Y=z(v(r,.06),\"drift\"),V=I(co(I(v(g,q),v(Y,Xg)),I(v(y,q),v(Y,Hg))),v(co(ne(I(v(v(g,q),2.1),5.2),v(v(Y,Xg),1.6)),ne(I(v(v(y,q),2.1),9.4),v(v(Y,Hg),1.6))),.4)),X=I(v(F.member(\"x\"),Xg),v(F.member(\"y\"),Hg)),Q=v(I(v(g,Xg),v(y,Hg)),.8),te=z(I(I(I(I(v(V,.45),Q),v(X,1.4)),v(r,.18)),Ce(s.hueShift,360)),\"hue\"),J=Ge(s.saturation,0,1),oe=UF(te),le=z(et(Qe(.72,.73,.76),oe,kt(v(J,.85))),\"foilTint\"),se=z(Ge(s.roughness,0,1),\"roughnessC\"),Te=z(Qe(Ape,wpe,Tpe),\"L\"),de=z(Os(I(Te,Qe(0,0,-1))),\"H\"),xe=z(Ge(I(v($t(F,Te),.5),.5),0,1),\"ndl\"),Ae=z(Ge($t(F,de),0,1),\"ndh\"),De=v(v(ts(Ae,7),.45),ne(1,v(se,.35))),Ee=v(v(ts(Ae,et(It(42),It(12),se)),.85),ne(1,v(se,.55))),ze=I(v(le,De),v(et(le,Qe(1,1,1),kt(.6)),Ee)),Ie=co(v(g,190),v(y,190)),ke=z(co(I(v(g,190),47.3),ne(v(y,190),31.1)),\"flakePhase\"),Me=z(I(v(Vt(I(v(r,2.2),v(ke,12.6))),.5),.5),\"twinkle\"),we=v(v(ot(.52,.62,I(v(Ie,.75),v(Me,.3))),Ge(s.sparkle,0,1)),E),Ve=v(v(UF(I(I(te,.33),v(ke,.5))),I(.85,v(Me,.55))),I(v(xe,.6),.7)),qe=I(1,v(v(co(v(b,420),v(x,420)),se),.28)),st=z(v(et(I(v(le,I(v(xe,.5),.62)),ze),Ve,kt(we)),qe),\"foilRgb\");return Js(Dr(u,p),Ot(st,_),xt,\"holo\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the foil shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the foil shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the foil shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},hueShift:{default:0,description:\"Rotates the rainbow spectrum across the foil\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Hue Shift\",group:\"Foil\"}},foilScale:{default:1.5,description:\"Size of the iridescent color patches \\u2014 higher = smaller, busier patches\",ui:{type:\"range\",min:.3,max:6,step:.01,label:\"Foil Scale\",group:\"Foil\"}},saturation:{default:.75,description:\"Rainbow saturation \\u2014 0 = plain silver foil, 1 = fully spectral\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Saturation\",group:\"Foil\"}},roughness:{default:.25,description:\"Matte laminate grain \\u2014 softens the gloss with fine paper-like noise\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Roughness\",group:\"Foil\"}},speed:{default:1,description:\"Animation speed \\u2014 the spectrum drifts across the foil as if the sticker is tilting in your hand. 0 pauses.\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Speed\",group:\"Animation\"}},crinkle:{default:.5,description:\"Laminate wrinkles \\u2014 perturbs the surface so the sheen and rainbow swirl locally instead of lying flat\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Crinkle\",group:\"Foil\"}},crinkleScale:{default:1,description:\"Size of the laminate wrinkles \\u2014 higher = smaller, denser wrinkles\",ui:{type:\"range\",min:.3,max:6,step:.01,label:\"Crinkle Scale\",group:\"Foil\"}},sparkle:{default:.4,description:\"Glitter flakes embedded in the foil \\u2014 tiny facets that refract a shifted hue and twinkle as the spectrum drifts\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Sparkle\",group:\"Foil\"}},edgeSoftness:{default:.05,description:\"Softness of the shape boundary edge\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Foil\"}},shape:{default:ype,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});function Spe(e){let{gpu:t,domCanvas:s,onBeforeRender:r,onResize:o,onCleanup:a}=e,i=t.device,l=Math.min(window.devicePixelRatio,2),c=gc(e,{label:\"HTMLInCanvas\",initial:{width:Math.max(1,Math.round(window.innerWidth*l)),height:Math.max(1,Math.round(window.innerHeight*l))},mipmaps:!1}),d=null,u=!1,p=!1,_=!1,f=!1,h=(g,y)=>c.ensureSize(g,y);return r(()=>{if(!(!s||c.disposed)){if(!d&&(d=s.firstElementChild,d)){let g=d.getBoundingClientRect(),y=Math.round(g.width*window.devicePixelRatio),b=Math.round(g.height*window.devicePixelRatio);y>0&&b>0&&h(y,b)}if(d){if(!p){s.getContext(\"2d\"),s.onpaint=()=>{_=!0},s.requestPaint?.(),p=!0;return}if(_&&d){let g=i.queue.copyElementImageToTexture;if(typeof g!=\"function\"){_=!1;return}try{if(f)g.call(i.queue,d,{texture:c.unwrap()});else try{g.call(i.queue,{source:d},{destination:{texture:c.unwrap()}})}catch(y){if(!(y instanceof TypeError))throw y;g.call(i.queue,d,{texture:c.unwrap()}),f=!0}_=!1}catch(y){u||(console.warn(`[HTMLInCanvas] copyElementImageToTexture failed.\nRequires Chrome Canary with chrome://flags/#canvas-draw-element enabled.\nThe element must be a direct child of a <canvas layoutsubtree>.`,y),u=!0)}}}}}),o(()=>{let g=Math.min(window.devicePixelRatio,2);h(Math.round(window.innerWidth*g),Math.round(window.innerHeight*g)),d=null,_=!1,s&&s.requestPaint?.()}),a(()=>{s&&(s.onpaint=null)}),c}var GF=H({name:\"HTMLInCanvas\",role:\"media\",species:\"custom\",deprecatedNames:[\"DOMTexture\"],boundingBoxDeclaration:{aspectRatio:null},category:\"Textures\",description:\"Render live HTML/DOM content as a WebGPU texture layer via the html-in-canvas API. Requires Chrome Canary with chrome://flags/#canvas-draw-element enabled.\",capturesDOM:!0,experimental:{message:\"Requires Chrome Canary with chrome://flags/#canvas-draw-element enabled. Do not use for production use.\",docs:\"This component is powered by the WICG html-in-canvas proposal, which is currently only available in Chrome Canary behind a feature flag. It is not suitable for production use and may change as the specification evolves.\",docsLink:{url:\"https://github.com/WICG/html-in-canvas\",label:\"View the spec\"}},props:{},gpu:{fragment:e=>{let t=Spe(e).kit.sample(e.ctx.uv);return Ct(W(Iu,\"srgbToLinear\",[t.member(\"rgb\")]),t.member(\"a\"))}}});var FF=H({name:\"HueShift\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Rotate hue around the color wheel\",props:{shift:{default:0,description:\"The amount to shift the hue by\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Shift\",group:\"Adjustments\"}}},effect:t8(T(\"shift\")),identityWhen:Uo(\"shift\"),missingChildMessage:\"You must pass a child component into the Hue Shift shader.\"});var IF=H({name:\"ImageTexture\",role:\"media\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Display an image with customizable object-fit modes\",acceptsUVContext:!0,naturalSizeKey:{fromProp:\"url\"},props:{url:{default:\"https://shaders.com/sample.jpg\",description:\"Upload an image or provide a URL\",ui:{type:\"image-upload\",label:\"Image\",group:\"Media\"}},objectFit:up(\"fill\",\"How the image should be sized within the viewport\",{allowNone:!1})},...LL({src:T(\"url\"),fit:T(\"objectFit\"),decode:\"srgb-linear-alpha-cut\",label:\"ImageTexture\",onError(e,t){let s=!t.startsWith(\"http://\")&&!t.startsWith(\"https://\")&&!t.startsWith(\"data:\");console.error(`[ImageTexture] Failed to load image\n  Requested URL: ${t}\n  Path type: ${s?\"relative\":\"absolute\"}\n  Error:`,e)}})});var Wi=256,El=Wi*Wi,Ppe=10,Cpe=\"rgba16float\",YF=.16,Mpe=16,Epe=.006,kpe=.06,WF=4,Bpe=.05,zpe=1.35,qF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,curlStrength:n,velFade:n,dyeFade:n}),\"FlowParams\"),VF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({posX:n,posY:n,velX:n,velY:n,colR:n,colG:n,colB:n,col2R:n,col2G:n,col2B:n,mixT:n,radius:n,strength:n}),\"SplatParams\"),NF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,El),access:\"mutable\"},velB:{storage:Fe(S,El),access:\"mutable\"},dyeA:{storage:Fe(S,El),access:\"mutable\"},dyeB:{storage:Fe(S,El),access:\"mutable\"},pressure:{storage:Fe(n,El),access:\"mutable\"},divergence:{storage:Fe(n,El),access:\"mutable\"},params:{uniform:qF}}),\"fluidLayout\"),$F=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,El),access:\"mutable\"},dyeA:{storage:Fe(S,El),access:\"mutable\"},sparams:{uniform:VF}}),\"splatLayout\"),XF=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({dyeA:{storage:Fe(S,El),access:\"readonly\"},outTex:{storageTexture:zt(Cpe,\"write-only\")}}),\"outputLayout\"),Bp=e=>Sz($F,{n:Wi,namePrefix:\"inkFlow\",colorSpace:e}),$2={0:Bp(0),1:Bp(1),2:Bp(2),3:Bp(3),4:Bp(4),5:Bp(5)},rAe=$2[2],HF=zn(NF,{n:Wi,namePrefix:\"inkFlow\",boundary:\"clamped\",dye:\"rgb\",dyeDissipation:!0}),{curl:oAe,vorticity:aAe,divergence:nAe,jacobi:iAe,gradSubtract:lAe,advectVel:cAe,copyVel:dAe,advectDye:uAe,copyDye:pAe}=HF,Rpe=Ui(XF,{n:Wi,namePrefix:\"inkFlow\",dye:\"rgb\"});function N2(e){return e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function Dpe(e){let t=(e%1+1)%1,s=Math.floor(t*6),r=t*6-s,o=1-r,a=0,i=0,l=0;return s===0?(a=1,i=r,l=0):s===1?(a=o,i=1,l=0):s===2?(a=0,i=1,l=r):s===3?(a=0,i=o,l=1):s===4?(a=r,i=0,l=1):(a=1,i=0,l=o),[N2(a),N2(i),N2(l)]}var jF=H({name:\"InkFlow\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Drag to paint swirling ribbons of ink through a real fluid field \\u2014 eddies keep evolving after you let go\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{colorMode:{default:\"rainbow\",description:\"How the brush color is chosen: an ever-cycling rainbow, a single fixed color, or a custom three-color cycle\",ui:{type:\"select\",options:[{label:\"Rainbow\",value:\"rainbow\"},{label:\"Single Color\",value:\"single\"},{label:\"Custom Colors\",value:\"custom\"}],label:\"Color Mode\",group:\"Color\"}},colorSpeed:{default:1,description:\"How quickly the brush cycles through the rainbow or the custom colors as you paint\",ui:{type:\"range\",min:0,max:4,step:.05,label:\"Color Speed\",group:\"Color\",condition:{colorMode:[\"rainbow\",\"custom\"]}}},color:{default:\"#ff2d7e\",transform:ce,description:\"Brush color used when Color Mode is Single\",ui:{type:\"color\",label:\"Brush Color\",group:\"Color\",condition:{colorMode:\"single\"}}},color1:{default:\"#4338ff\",transform:ce,description:\"First color of the custom brush cycle\",ui:{type:\"color\",label:\"Color 1\",group:\"Color\",condition:{colorMode:\"custom\"}}},color2:{default:\"#ff2d7e\",transform:ce,description:\"Second color of the custom brush cycle\",ui:{type:\"color\",label:\"Color 2\",group:\"Color\",condition:{colorMode:\"custom\"}}},color3:{default:\"#19e3ff\",transform:ce,description:\"Third color of the custom brush cycle\",ui:{type:\"color\",label:\"Color 3\",group:\"Color\",condition:{colorMode:\"custom\"}}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"color space the custom colors blend through as the brush cycles between them\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Color\",condition:{colorMode:\"custom\"}}},radius:{default:.3,description:\"Size of the ink splat painted under the cursor\",ui:{type:\"range\",min:.1,max:5,step:.05,label:\"Radius\",group:\"Brush\"}},force:{default:1,description:\"How hard the cursor's motion pushes the fluid \\u2014 higher throws faster, more violent jets and curls\",ui:{type:\"range\",min:0,max:3,step:.05,label:\"Force\",group:\"Brush\"}},curl:{default:0,description:\"Vorticity confinement \\u2014 swirl energy that spins the ink into persistent eddies and curls\",ui:{type:\"range\",min:0,max:60,step:1,label:\"Swirl\",group:\"Dynamics\"}},decay:{default:.5,description:\"How fast the ink fades away \\u2014 low values leave long, lingering trails\",ui:{type:\"range\",min:.05,max:4,step:.05,label:\"Decay\",group:\"Dynamics\"}},momentum:{default:.6,description:\"How long the fluid keeps flowing after a stroke \\u2014 high momentum lets the motion carry on and on\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Momentum\",group:\"Dynamics\"}}},...Ii((e,t)=>{let{getCpuValue:s,propValues:r}=e,o=r.colorSpace??2,a=$2[o]??$2[2],i=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(t.createUniform(VF),\"splatU\"),l=0,c=(p,_)=>{let f=p;return f&&typeof f.r==\"number\"?[f.r,f.g??0,f.b??0]:_},d=p=>fl(p[0],p[1],p[2],o),u=Og({activeWhen:p=>p.moving,fadeSeconds:p=>jh(64,p.num(\"decay\",.5)),onFrame:p=>{l+=p.dt*kpe*p.num(\"colorSpeed\",1)},pass:{setup:p=>{let _=p.root.createBindGroup($F,{velA:p.buffers.velA,dyeA:p.buffers.dyeA,sparams:i.buffer});return p.pass(a,_)}},ribbon:(p,_)=>{let f=p.num(\"force\",1),h=Math.max(p.num(\"radius\",.3)*.1*Wi,1),g=p.num(\"colorSpeed\",1),y=s(\"colorMode\")||\"rainbow\",b=y===\"custom\"?[d(c(s(\"color1\"),[.26,.22,1])),d(c(s(\"color2\"),[1,.18,.49])),d(c(s(\"color3\"),[.1,.89,1]))]:null,x=()=>{if(l+=Epe*g,b){let R=(l%1+1)%1,U=Math.min(2,Math.floor(R*3));return{a:b[U],b:b[(U+1)%3],t:R*3-U}}if(y===\"single\"){let R=d(c(s(\"color\"),[1,.18,.49]));return{a:R,b:R,t:0}}let E=d(Dpe(l));return{a:E,b:E,t:0}},P=_.velX*Wi*f*YF,C=_.velY*Wi*f*YF;return{stepSize:Math.max(.004,h/Wi*.6),maxSteps:Mpe,prepare:x,write:(E,R,U,L)=>i.write({posX:E,posY:R,velX:P,velY:C,colR:L.a[0],colG:L.a[1],colB:L.a[2],col2R:L.b[0],col2G:L.b[1],col2B:L.b[2],mixT:L.t,radius:h,strength:1})}}});return{resolution:Wi,layout:NF,outputLayout:XF,paramsSchema:qF,solver:{kernels:HF,jacobiIters:Ppe},output:{kernel:Rpe,key:\"dyeTexture\"},inject:[u],values:p=>{let _=Math.min(Math.max(p.num(\"momentum\",.6),0),1);return{dt:p.dt,curlStrength:p.num(\"curl\",0),velFade:WF*Math.pow(Bpe/WF,_),dyeFade:p.num(\"decay\",.5)}}}}),gpu:{fragment:({ctx:e,computeOutputs:t})=>{let s=t?.dyeTexture;if(!s)return xt;let r=z(s.sample(e.uv,\"linearClamp\").member(\"rgb\"),\"inkDye\");return Ct(r,Ge(v(it(r.member(\"x\"),it(r.member(\"y\"),r.member(\"z\"))),zpe),0,1))}}});var KF=H({name:\"Invert\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Invert RGB colors while preserving alpha\",props:{},effect:i8(),missingChildMessage:\"You must pass a child component into the Invert shader.\"});var QF=H({name:\"IrisWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Reveal through an expanding circle growing from a center point\",props:{progress:{default:.5,description:\"How far the iris has expanded (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Point the iris expands from\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},feather:{default:.1,description:\"Softness of the iris edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Wipe from the outside inward instead\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.radial(T(\"center\")),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Iris Wipe shader.\"});var Lpe=JSON.stringify({type:\"sphere3D\",radius:.35}),Upe=[{position:{x:.3,y:.3},color:\"#ffb347\",intensity:3},{position:{x:.72,y:.7},color:\"#7a5cff\",intensity:2}],Ope=6,jg={burst:128,motion:64,refine:32,refineFrames:12},Gpe=2,Fpe=32,ZF=12,Ipe=512,Ype=320,Wpe=2.5,Kg=.01,qpe=.3,Vpe=60,Npe=.6;function X2(e){let t=e.shadows,s=typeof t==\"number\"?t>0:t!==!1,r=Rr(),o=r?Gpe:1;return{rays:{burst:jg.burst/o,motion:jg.motion/o,refine:jg.refine/o,refineFrames:jg.refineFrames},shadowSteps:s?ZF:0,resolution:r?Ype:Ipe}}var JF={lights:{prop:\"lights\",position:\"position\",color:\"color\",intensity:\"intensity\"},reach:\"reach\",wrap:\"wrap\",lightRange:\"lightRange\",shadowSoftness:\"shadowSoftness\",steps:Fpe},$pe=Ts({stencil:\"centre\",centreTap:\"fast\",surface:(e,t)=>{let{uniforms:s,propValues:r}=t,{shadowSteps:o}=X2(r),a=o>0,i=Lr(e,t),l=P=>({...i,s0:z(e.sampler(e.sdfUV),`${P}S0`),sX:z(e.gradSampler(I(e.sdfUV,rt(Kg,0))),`${P}X`),sY:z(e.gradSampler(I(e.sdfUV,rt(0,Kg))),`${P}Y`)}),c=z(Ce(i.pxH,it(s.scale,.001)),\"pxUV\"),d=xL(e,i,t),u=uO(t,\"lights\"),p=P=>{let C=u.at(P);return{uv:bL(t,C.position(\"position\"),\"lightUV\"),rgb:v(C.color(\"color\").member(\"rgb\"),C.number(\"intensity\"))}},_=h2({...X2(r),...JF}).sample(t)??kt(0),f=z(ne(1,ot(0,v(c,Wpe),d.sdf)),\"rim\"),h=()=>{let P=AL(e.sdfUV,d.sdf,I(Ln(l(\"rim\"),Kg),d.outward));return S2(u,C=>{let E=p(C),R=u2({position:E.uv,height:s.lightHeight},P.point,\"rimLight\"),U=v(Pl(P.normal,R.toLight,{wrap:s.wrap}),p2(R.distance,s.lightRange));return a&&(U=v(U,IL({from:I(P.point,v(P.normal,v(c,2))),toward:E.uv,field:d.at,steps:ZF,surfaceEps:c,softness:s.shadowSoftness,hint:\"rimVisibility\"}))),v(E.rgb,U)},{zero:\"vec3f\",hint:\"rimSum\",deps:[P.point,P.normal]})},g=kL(m2(I(_,Js(Zo(f,0),v(h(),v(f,s.core)),kt(0),\"emitter\",[c]))),\"glow\"),y=()=>{let P=Dn(e,l(\"body\"),{bevelWidth:s.bevelWidth,bevelShape:It(0),eps:Kg}),C=S2(u,E=>{let R=p(E),U=u2({position:R.uv,height:s.lightHeight},e.sdfUV,\"bodyLight\"),L=z(v(p2(U.distance,s.lightRange),s.bodyLight),\"bodyLit\"),k=v(ts(it($t(P,Os(I(U.L,Qe(0,0,-1)))),0),Vpe),Npe);return I(v(s.bodyColor.member(\"rgb\"),v(Pl(P,U.L),L)),v(R.rgb,v(k,L)))},{zero:\"vec3f\",hint:\"bodySum\",deps:[P]});return m2(I(v(s.bodyColor.member(\"rgb\"),qpe),C))},b=Js(Dr(i.sdf,c),y(),kt(0),\"body\",[]),x=z(Jr({...i,pxH:c},s.edgeSoftness),\"cov\");return Ot(et(g.member(\"rgb\"),b,x),et(g.member(\"a\"),1,x))}}),eI=H({name:\"Irradiance\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Photorealistic light spilling around the edge of any 2D, SVG, or 3D shape \\u2014 any number of movable colored point lights strike the silhouette and the lit edges irradiate their surroundings, gathered in a compute pass with real cast shadows, mixing where they meet and burning to white at the rim\",props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},lights:fz({maxItems:Ope,minItems:1,itemLabel:\"Light\",item:{position:{kind:\"position\",default:{x:.3,y:.3},label:\"Position\",description:\"Where this light sits \\u2014 the edges facing it are struck and spill light outward. Map it to the mouse to carry the light around the shape.\"},color:{kind:\"color\",default:\"#ffb347\",label:\"Color\",description:\"The color this light casts\"},intensity:{kind:\"number\",default:3,label:\"Intensity\",min:0,max:8,step:.01,description:\"Brightness of this light \\u2014 past ~2 the rim it strikes overexposes to white\"}}},{default:Upe,description:\"The point lights striking the shape \\u2014 each with its own position, color and brightness\",label:\"Lights\",group:\"Lights\"}),lightHeight:{default:.5,description:\"Height of the lights above the shape plane, relative to the shape \\u2014 low lights graze the body, high lights flood it evenly\",ui:{type:[\"range\",\"map\"],min:.05,max:3,step:.01,label:\"Light Height\",group:\"Light\"}},lightRange:{default:1.2,description:\"Reach of each light \\u2014 the distance at which it has fallen to half strength, relative to the shape\",ui:{type:[\"range\",\"map\"],min:.1,max:4,step:.01,label:\"Range\",group:\"Light\"}},reach:{default:3,description:\"How far from the shape the spilled light is gathered, relative to the shape \\u2014 the glow fades out toward this distance\",ui:{type:[\"range\",\"map\"],min:.5,max:8,step:.01,label:\"Reach\",group:\"Glow\"}},core:{default:1,description:\"The lit edge seen directly \\u2014 the white-hot line along the rim where the lights strike\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Core\",group:\"Glow\"}},wrap:{default:.1,description:\"How far light wraps around edges that face away from it \\u2014 0 leaves the far side dark\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Wrap\",group:\"Glow\"}},shadows:{default:!0,compileTime:!0,transform:rs,description:\"Cast shadows \\u2014 the body blocks each light from edges it stands in front of (keeps holes and concave pockets dark)\",ui:{type:\"checkbox\",label:\"Shadows\",group:\"Glow\"}},shadowSoftness:{default:.15,description:\"Penumbra width of the cast shadows \\u2014 0 is razor sharp\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadow Softness\",group:\"Glow\"}},bodyColor:{default:\"#000000\",transform:ce,description:\"Color of the shape itself \\u2014 black for a pure occluder, lighter to see the lights land on its form\",ui:{type:\"color\",label:\"Body Color\",group:\"Body\"}},bodyLight:{default:.6,description:\"How much the lights illuminate the body \\u2014 the bevel of a flat shape or the form of a 3D one\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Body Light\",group:\"Body\"}},bevelWidth:{default:.01,description:\"Width of the rounded edge a flat shape catches the light on, relative to the shape\",ui:{type:[\"range\",\"map\"],min:.005,max:.2,step:.001,label:\"Bevel Width\",group:\"Body\"}},...gr({edgeSoftness:{ui:{group:\"Body\"}}}),shape:{default:Lpe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...$pe,compute:e=>h2({...X2(e.propValues),...JF}).compute(e)});var H2=rD({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"clockwise\"}),thickness:T(\"thickness\"),softness:T(\"softness\")}),Xpe=Zr({fill:T(\"colorA\"),stroke:T(\"colorB\"),mask:Y_(H2.random,T(\"colorVariation\")),space:T(\"colorSpace\")}),tI=H({name:\"IsometricCubes\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Isometric tumbling-blocks tiling \\u2014 a 3D cube illusion (rhombille pattern)\",acceptsUVContext:!0,props:{colorA:{default:\"#7c5cff\",transform:ce,description:\"Base cube color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ff5c9d\",transform:ce,description:\"Second cube color, mixed in per-cube for variation\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},lineColor:{default:\"#000000\",transform:ce,description:\"Color of the cube edge lines\",ui:{type:\"color\",label:\"Edges\",group:\"Colors\"}},cells:{default:6,description:\"Number of cubes across the shortest canvas edge\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Cells\",group:\"Effect\"}},thickness:{default:1,description:\"Thickness of the cube edge lines (0 = no edges)\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Thickness\",group:\"Effect\"}},rotation:{default:0,transform:cs,description:\"Rotation of the tiling in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the edge lines (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},colorVariation:{default:1,description:\"Per-cube random blend between the two colors (0 = uniform cubes, the clean 3D look)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:VR(fg(Y_(n2(Xpe),H2.tone),n2(T(\"lineColor\")),H2.wire))});var sI=H({name:\"Kaleidoscope\",role:\"warp\",category:\"Distortions\",description:\"Create a kaleidoscope effect with radial mirrored segments\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point of the kaleidoscope effect\"),segments:{default:6,description:\"Number of radial segments in the kaleidoscope\",ui:{type:[\"range\",\"map\"],min:2,max:24,step:1,label:\"Segments\",group:\"Effect\"}},angle:{default:0,description:\"Rotation offset for the entire kaleidoscope pattern\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},edges:fr(\"mirror\",\"How to handle edges when distortion pushes content out of bounds\")},map:jL({center:T(\"center\"),segments:T(\"segments\"),angle:T(\"angle\")}),missingChildMessage:\"You must pass a child component into the Kaleidoscope shader.\"});var rI=H({name:\"KeyFrames\",role:\"overlay\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Motion-tracking overlay \\u2014 trackers hunt and follow features in the content, dropping keyframe diamonds behind them along their motion paths, like a compositor mid-track\",requiresRTT:!0,requiresChild:!0,props:{trackers:{default:12,description:\"Number of simultaneous trackers.\",ui:{type:[\"range\",\"map\"],min:1,max:64,step:1,label:\"Trackers\",group:\"Tracking\"}},detect:{default:\"bright\",compileTime:!0,description:\"What the trackers hunt for.\",ui:{type:\"select\",options:[{label:\"Brightness\",value:\"bright\"},{label:\"Darkness\",value:\"dark\"},{label:\"Transparency\",value:\"alpha\"},{label:\"Red\",value:\"red\"},{label:\"Green\",value:\"green\"},{label:\"Blue\",value:\"blue\"}],label:\"Detect\",group:\"Tracking\"}},threshold:{default:.15,description:\"Feature floor \\u2014 content scoring below this doesn't count as trackable at all.\",ui:{type:\"range\",min:0,max:.9,step:.01,label:\"Threshold\",group:\"Tracking\"}},variance:{default:.8,description:\"Spread of tracker preferences \\u2014 0 hunts only the strongest features, 1 distributes trackers across the whole brightness range.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variance\",group:\"Tracking\"}},agility:{default:.5,description:\"How fast trackers travel and how wide they search.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Agility\",group:\"Tracking\"}},lifespan:{default:6,description:\"Seconds a tracker follows its target before retiring and reacquiring elsewhere. 0 = track forever.\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.5,label:\"Lifespan\",group:\"Tracking\"}},trail:{default:1,description:\"Visibility of the keyframe trail each tracker leaves behind.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Trail\",group:\"Style\"}},markerSize:{default:28,description:\"Tracker gizmo size in pixels.\",ui:{type:\"range\",min:8,max:80,step:1,label:\"Marker Size\",group:\"Style\"}},lineWidth:{default:1.5,description:\"Stroke width in pixels.\",ui:{type:\"range\",min:.5,max:6,step:.25,label:\"Line Width\",group:\"Style\"}},markerColor:{default:\"#54d66b\",transform:ce,description:\"Tracker gizmo color.\",ui:{type:\"color\",label:\"Tracker\",group:\"Colors\"}},keyframeColor:{default:\"#ffcf33\",transform:ce,description:\"Keyframe diamond color.\",ui:{type:\"color\",label:\"Keyframes\",group:\"Colors\"}},pathColor:{default:\"#ffffffb8\",transform:ce,description:\"Motion path color.\",ui:{type:\"color\",label:\"Path\",group:\"Colors\"}}},compute:e=>yB({mode:typeof e.propValues.detect==\"string\"?e.propValues.detect:\"bright\",trackers:\"trackers\",agility:\"agility\",threshold:\"threshold\",variance:\"variance\",lifespan:\"lifespan\"})(e),gpu:{fragment:F8({trackers:T(\"trackers\"),trail:T(\"trail\"),markerSize:T(\"markerSize\"),lineWidth:T(\"lineWidth\"),markerColor:T(\"markerColor\"),keyframeColor:T(\"keyframeColor\"),pathColor:T(\"pathColor\")})}});var oI=H({name:\"LensDistortion\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Split content into shifting chromatic layers with barrel or pincushion lens warp. Powered by Paper Shaders.\",requiresRTT:!0,requiresChild:!0,props:{center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the lens effect \\u2014 the focus zone, lens warp, circle crop and swirl all pivot around it\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},spread:{default:.6,description:\"Strength of the color split; how far the color layers are pushed apart (0 is off)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Spread\",group:\"Spread\"}},angle:{default:0,transform:cs,description:\"Direction of the spread in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Spread\"}},perspective:{default:.1,description:\"Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Perspective\",group:\"Spread\"}},bias:{default:1,description:\"Shifts the colors toward one end of the spread; 0 spaces them evenly\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Bias\",group:\"Spread\"}},count:{default:35,description:\"Number of sampled color layers along the spread; higher is smoother and costlier\",ui:{type:[\"range\",\"map\"],min:2,max:50,step:1,label:\"Count\",group:\"Spread\"}},dispersion:{default:1,description:\"Overall amount of color dispersion; 1 gives each layer its own color from the spectrum, 0 keeps the original color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Dispersion\",group:\"Dispersion\"}},dispersionShift:{default:0,description:\"Balance of the dispersion between a soft circular zone at the centre and the rest; -1 keeps it in the centre only, 1 at the edges only\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Shift\",group:\"Dispersion\"}},dispersionColor:{default:.6,description:\"Rotates the dispersion colors around the hue wheel, 0 to 1 for a full turn\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Hue\",group:\"Dispersion\"}},focusCenter:{default:.8,description:\"Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Focus Center\",group:\"Focus\"}},focusEdges:{default:1,description:\"Reduces the spread toward the edges; 1 restores the original image there\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Focus Edges\",group:\"Focus\"}},lensBulge:{default:0,description:\"Radial lens warp; positive bulges out like a fisheye, negative pinches in like a pincushion\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Bulge\",group:\"Lens\"}},lensCircle:{default:0,description:\"Squeezes pixels outside the inscribed circle inward so the outline becomes a circle\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Circle\",group:\"Lens\"}},swirl:{default:.35,description:\"Rotates the color layers around the centre by an angle growing along the spread; 0 is off\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Swirl\",group:\"Lens\"}},noise:{default:0,description:\"Scatters the spread direction with noise; 0 is off\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Noise\",group:\"Noise\"}},noiseFrequency:{default:.25,description:\"Frequency of the direction noise; higher is finer (no effect with noise at 0)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Frequency\",group:\"Noise\"}},noiseOffset:{default:0,description:\"Offsets the noise pattern for a different seed (no effect with noise at 0)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Seed\",group:\"Noise\"}},grainMixer:{default:0,description:\"Scatters the spread with grain noise, breaking up the edges of the color layers\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Grain Mixer\",group:\"Grain\"}},grainOverlay:{default:0,description:\"Post-processing black/white grain overlay\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Grain Overlay\",group:\"Grain\"}}},gpu:{fragment:U8({center:T(\"center\"),spread:T(\"spread\"),bias:T(\"bias\"),angle:T(\"angle\"),perspective:T(\"perspective\"),count:T(\"count\"),dispersion:T(\"dispersion\"),dispersionShift:T(\"dispersionShift\"),dispersionColor:T(\"dispersionColor\"),focusCenter:T(\"focusCenter\"),focusEdges:T(\"focusEdges\"),swirl:T(\"swirl\"),noise:T(\"noise\"),noiseFrequency:T(\"noiseFrequency\"),noiseOffset:T(\"noiseOffset\"),lensBulge:T(\"lensBulge\"),lensCircle:T(\"lensCircle\"),grainMixer:T(\"grainMixer\"),grainOverlay:T(\"grainOverlay\")})}});var aI=H({name:\"LensFlare\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Stylize\",description:\"Realistic camera lens flare with artifacts.\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{lightPosition:{default:{x:.3,y:.3},transform:Xe,description:\"Position of the light source\",ui:{type:\"position\",label:\"Light Position\",group:\"Light\"}},intensity:{default:.5,description:\"Master brightness of the entire lens flare effect\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Intensity\",group:\"Light\"}},ghostIntensity:{default:.4,description:\"Brightness of internal reflection ghost discs along the flare axis\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Ghosts\",group:\"Reflections\"}},ghostSpread:{default:.7,description:\"Spacing between ghost reflections along the flare axis\",ui:{type:\"range\",min:.1,max:2,step:.01,label:\"Ghost Spread\",group:\"Reflections\"}},ghostChroma:{default:.3,description:\"Rainbow chromatic fringing around ghost element edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Ghost Chroma\",group:\"Reflections\"}},haloIntensity:{default:.4,description:\"Brightness of the circular halo ring from internal reflection\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Halo\",group:\"Reflections\"}},haloRadius:{default:.6,description:\"Radius of the halo ring\",ui:{type:\"range\",min:.1,max:1,step:.01,label:\"Halo Size\",group:\"Reflections\"}},haloChroma:{default:.6,description:\"Spectral dispersion on the halo creating rainbow color separation\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Halo Chroma\",group:\"Reflections\"}},haloSoftness:{default:.8,description:\"Thickness and softness of the halo ring\",ui:{type:[\"range\",\"map\"],min:.01,max:3,step:.01,label:\"Halo Softness\",group:\"Reflections\"}},starburstIntensity:{default:.3,description:\"Brightness of diffraction spikes radiating from the light source\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Starburst\",group:\"Effects\"}},starburstPoints:{default:6,description:\"Number of starburst spikes (simulates aperture blade count)\",ui:{type:[\"range\",\"map\"],min:4,max:16,step:1,label:\"Blades\",group:\"Effects\"}},streakIntensity:{default:.15,description:\"Brightness of horizontal anamorphic light streak\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Streak\",group:\"Effects\"}},streakLength:{default:.5,description:\"Horizontal extent of the anamorphic streak\",ui:{type:[\"range\",\"map\"],min:.1,max:1,step:.01,label:\"Streak Length\",group:\"Effects\"}},glareIntensity:{default:.2,description:\"Soft veiling glare that washes out contrast around the light\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Glare\",group:\"Effects\"}},glareSize:{default:.5,description:\"Size of the soft glare glow\",ui:{type:[\"range\",\"map\"],min:.1,max:1,step:.01,label:\"Glare Size\",group:\"Effects\"}},edgeFade:{default:.2,description:\"How much the flare fades when the light source is near the screen edge (0 = no fade, 1 = heavy fade)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Edge Fade\",group:\"Light\"}},speed:{default:.5,description:\"Speed of subtle flare shimmer and starburst rotation\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Speed\",group:\"Animation\"}}},paint:e=>{let t=ns(SD({position:T(\"lightPosition\"),intensity:T(\"intensity\"),edgeFade:T(\"edgeFade\")})(e),\"flare\"),s=PD({intensity:T(\"ghostIntensity\"),spread:T(\"ghostSpread\"),chroma:T(\"ghostChroma\")});return zD(i2(i2(s(t,[.15,.2,.3,.1],[1,.95,.9,0],e),s(t,[.3,.12,.25,.65],[1,.85,.55,2.17],e),s(t,[.5,.08,.35,.15],[.62,.82,1,4.34],e),s(t,[.72,.14,.18,.75],[.75,1,.7,6.51],e),s(t,[.95,.06,.3,0],[1,.6,.85,8.68],e),s(t,[1.25,.1,.15,.8],[.55,.88,1,10.85],e),s(t,[1.55,.07,.2,.25],[1,.92,.6,13.02],e)),CD({intensity:T(\"haloIntensity\"),radius:T(\"haloRadius\"),chroma:T(\"haloChroma\"),softness:T(\"haloSoftness\")})(t,e),MD({intensity:T(\"starburstIntensity\"),points:T(\"starburstPoints\")})(t,e),ED({intensity:T(\"streakIntensity\"),length:T(\"streakLength\")})(t,e),kD({intensity:T(\"glareIntensity\"),size:T(\"glareSize\")})(t,e),BD({intensity:T(\"intensity\")})(t,e)),t)}});var Hpe=JSON.stringify({type:\"sphere3D\",radius:.35}),jpe=[{color:\"#7b2ff7\",position:0},{color:\"#00e0ff\",position:.5},{color:\"#ff3d81\",position:1}],nI=H({name:\"LightEdge\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Glowing, pulsing light racing around the edge of any 2D, SVG, or 3D shape. Powered by Paper Shaders.\",animatedTime:{speed:\"speed\"},...Ts({chord:\"firstLobe\",stencil:\"centre\",surface:(e,t)=>{let{uniforms:s,propValues:r}=t,o=wt(t),a=ns(W(h8,\"heartbeatPulse\",[o]),\"beat\"),i=W(g8,\"edgeGlowBand\",[e.surf0.member(\"r\"),e.sdfUV,t.ctx.viewportSize,s.scale,s.thickness,s.softness,s.smokeSize,s.smoke,o,s.pulse,a]),l=i.member(\"x\"),c=i.member(\"y\"),d=r.stopCount??0,u=d>1?Math.min(d,8):2,p=x8(r.colorSpace??0),_=g=>W(y8,\"orbitSpotsAccum\",[c,l,o,a,s.spots,s.spotSize,s.pulse,s.intensity,s.softness,s.seed,$e(g)]),f=g=>d>1?W(A8,\"colorAtIndex\",[s.colorsArray,$e(g)]):g===0?s.colorA:s.colorB,h=W(b8,\"edgeGlowAccumZero\",[]);for(let g=0;g<u;g++)h=W(p,\"edgeGlowAccumColor\",[h,f(g),_(g)]);return W(w8,\"edgeGlowCompose\",[h,s.bloom])}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},colorA:{default:\"#7b2ff7\",transform:ce,description:\"First border light color (used when no gradient stops are set)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#00e0ff\",transform:ce,description:\"Second border light color (used when no gradient stops are set)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:{...Ms(),default:jpe},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space overlapping lights blend in\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},thickness:{default:.25,description:\"Width of the border band\",ui:{type:[\"range\",\"map\"],min:.01,max:1,step:.01,label:\"Thickness\",group:\"Border\"}},softness:{default:.5,description:\"Edge softness \\u2014 0 = crisp band, 1 = wide smooth gradient\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Border\"}},intensity:{default:.6,description:\"Brightness of the individual light spots\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Light\"}},bloom:{default:.3,description:\"How additively overlapping lights pile up \\u2014 high values overdrive to white\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bloom\",group:\"Light\"}},spots:{default:3,description:\"Number of light spots orbiting per color\",ui:{type:\"range\",min:1,max:5,step:1,label:\"Spots\",group:\"Light\"}},spotSize:{default:.5,description:\"Angular size of each light spot\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Spot Size\",group:\"Light\"}},pulse:{default:0,description:\"Heartbeat pulsing \\u2014 synchronizes the lights to a double-beat rhythm\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Pulse\",group:\"Animation\"}},smoke:{default:.3,description:\"Noisy luminous smoke drifting along the border\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Smoke\",group:\"Smoke\"}},smokeSize:{default:.5,description:\"Scale of the smoke billows\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Smoke Size\",group:\"Smoke\"}},speed:{default:1,description:\"Animation speed. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed \\u2014 re-rolls spot speeds, directions and phases\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}},shape:{default:Hpe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var iI=H({name:\"LightLeak\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Photorealistic film light leak \\u2014 warm overexposed light bleeding in from a draggable anchor point, with streak bands, chromatic fringing, and slow breathing that evolves over time\",usesPointer:!1,animatedTime:{speed:\"speed\"},props:{position:{default:{x:.95,y:.4},transform:Xe,description:\"Anchor point of the leak \\u2014 the light beams from here toward the canvas centre\",ui:{type:\"position\",label:\"Position\",group:\"Leak\",units:[\"%\",\"px\"]}},spread:{default:.55,description:\"How far the light bleeds into the frame\",ui:{type:[\"range\",\"map\"],min:.1,max:1.5,step:.01,label:\"Spread\",group:\"Leak\"}},intensity:{default:.15,description:\"Exposure strength of the leak\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Leak\"}},streaks:{default:.6,description:\"Strength of the parallel streak bands further into the frame\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Streaks\",group:\"Leak\"}},colorHot:{default:\"#fff3c4\",transform:ce,description:\"Core color where the leak is fully overexposed\",ui:{type:\"color\",label:\"Hot Core\",group:\"Colors\"}},colorMid:{default:\"#ff7a2f\",transform:ce,description:\"Main body color of the leak\",ui:{type:\"color\",label:\"Midtone\",group:\"Colors\"}},colorFringe:{default:\"#a63d8f\",transform:ce,description:\"Outer fringe color where the light fades out\",ui:{type:\"color\",label:\"Fringe\",group:\"Colors\"}},flicker:{default:.35,description:\"Breathing of the leak intensity\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Flicker\",group:\"Animation\"}},speed:{default:1,description:\"Speed of the breathing, drift and evolution. 0 freezes the leak.\",ui:{type:\"range\",min:0,max:4,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed \\u2014 re-rolls the drift and breathing pattern\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}}},effect:OD(e=>{let t=ns(wt(e).mul(.6).add(At(T(\"seed\"),e).mul(7.9)),\"leakClock\"),s=ns(fD(T(\"position\"))(e),\"leakFrame\"),r=ns(bD({seed:T(\"seed\"),flicker:T(\"flicker\")})(t,e),\"leakDrift\"),o=ns(xD({spread:T(\"spread\")})(s,r,e),\"leakBloom\"),a=AD({spread:T(\"spread\"),strength:T(\"streaks\")})(s,r,e),i=o.member(\"x\").mul(1.25).add(o.member(\"y\")).add(a.mul(.85)).mul(At(T(\"intensity\"),e)).mul(2.2).mul(r.member(\"x\"));return DD({hot:T(\"colorHot\"),mid:T(\"colorMid\"),fringe:T(\"colorFringe\")}).taps(wD(i,e.ctx.uv,t),e)}),missingChildMessage:\"You must pass a child component into the Light Leak shader.\"});var Kpe={solid:0,dashed:1,dotted:2},Qpe=e=>Kpe[e]??0,Zpe={square:0,rounded:1},lI=e=>Zpe[e]??1,cI=[{label:\"Square\",value:\"square\"},{label:\"Rounded\",value:\"rounded\"}],dI=H({name:\"Line\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Shapes\",description:\"Draw a straight line between two points with color, thickness, and solid, dashed, or dotted styles\",acceptsUVContext:!0,props:{color:{default:\"#ffffff\",transform:ce,description:\"The color of the line\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},pointA:{default:{x:.2,y:.5},transform:Xe,description:\"The start point of the line\",ui:{type:\"position\",label:\"Point A\",group:\"Position\",units:[\"%\",\"px\"]}},pointB:{default:{x:.8,y:.5},transform:Xe,description:\"The end point of the line\",ui:{type:\"position\",label:\"Point B\",group:\"Position\",units:[\"%\",\"px\"]}},thickness:{default:.01,description:\"The thickness of the line. A value of one (1) matches the canvas height.\",ui:{type:[\"range\",\"map\"],min:0,max:.25,step:.001,label:\"Thickness\",group:\"Effect\",dimensional:\"canvas-height\"}},style:{default:\"solid\",transform:Qpe,description:\"The line style: solid, dashed, or dotted. Dashes and dots are spaced to land exactly on both endpoints.\",ui:{type:\"select\",options:[{label:\"Solid\",value:\"solid\"},{label:\"Dashed\",value:\"dashed\"},{label:\"Dotted\",value:\"dotted\"}],label:\"Style\",group:\"Style\"}},dashLength:{default:.05,description:\"The length of each dash, including its caps\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.001,label:\"Dash Length\",group:\"Style\",dimensional:\"canvas-height\",condition:{style:\"dashed\"}}},gapLength:{default:.025,description:\"The gap between dashes, or the spacing between dots\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.001,label:\"Gap Length\",group:\"Style\",dimensional:\"canvas-height\",condition:{style:[\"dashed\",\"dotted\"]}}},capStart:{default:\"rounded\",transform:lI,description:\"Cap shape at the start of the line (point A). Rounded caps center on the endpoint; square caps end flat exactly at it. Also shapes the A-facing end of each dash and dot.\",ui:{type:\"select\",options:cI,label:\"Start Cap\",group:\"Style\"}},capEnd:{default:\"rounded\",transform:lI,description:\"Cap shape at the end of the line (point B). Rounded caps center on the endpoint; square caps end flat exactly at it. Also shapes the B-facing end of each dash and dot.\",ui:{type:\"select\",options:cI,label:\"End Cap\",group:\"Style\"}}},paint:RL({from:T(\"pointA\"),to:T(\"pointB\"),width:T(\"thickness\"),style:T(\"style\"),dashLength:T(\"dashLength\"),gapLength:T(\"gapLength\"),capStart:T(\"capStart\"),capEnd:T(\"capEnd\"),color:T(\"color\")})});var uI=H({name:\"LinearBlur\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Directional motion blur in a specific angle\",props:{intensity:{default:30,description:\"Intensity of the linear blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Direction of the linear blur (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},dispersion:{default:0,description:\"Spectral color fringing along the streak, like light through a lens; negative flips which end is red\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Dispersion\",group:\"Lens\"}},falloff:{default:0,description:\"Concentrates the dispersion away from the center; 0 fringes everywhere equally\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Lens\"}},focus:{default:0,description:\"Radius of a clean zone around the center that stays sharp\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Focus\",group:\"Lens\"}},bias:{default:0,description:\"Trails the streak off one side of each pixel like a comet; the sign picks the direction\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Bias\",group:\"Streak\"}},highlights:{default:0,description:\"Lets bright pixels streak further than dark ones, like light rays\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlights\",group:\"Streak\"}},jitter:{default:0,description:\"Per-pixel noise on the tap spacing that hides banding in long streaks\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Jitter\",group:\"Streak\"}}},effect:Tp({path:wp.linear(T(\"angle\")),amount:T(\"intensity\"),detail:{dispersion:T(\"dispersion\"),falloff:T(\"falloff\"),focus:T(\"focus\"),bias:T(\"bias\"),highlights:T(\"highlights\"),jitter:T(\"jitter\")}})});var pI=H({name:\"LinearGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Create smooth linear color gradients\",acceptsUVContext:!0,props:{colorA:{default:\"#1aff00\",transform:ce,description:\"The starting color of the gradient\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#0000ff\",transform:ce,description:\"The ending color of the gradient\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),start:{default:{x:0,y:.5},transform:Xe,description:\"The starting point of the gradient\",ui:{type:\"position\",label:\"Start\",group:\"Position\"}},end:{default:{x:1,y:.5},transform:Xe,description:\"The ending point of the gradient\",ui:{type:\"position\",label:\"End\",group:\"Position\"}},angle:{default:0,transform:cs,description:\"Additional rotation angle of the gradient (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},edges:{default:\"stretch\",transform:Fr,compileTime:!0,description:\"How to handle areas beyond the gradient endpoints\",ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(Oi.linear({from:T(\"start\"),to:T(\"end\"),angle:T(\"angle\")}),Us(T(\"colorSpace\")),{edges:T(\"edges\")})});var mI=H({name:\"LinearWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away along a straight edge with a soft feathered transition\",props:{progress:{default:.5,description:\"How far the wipe has travelled (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},angle:{default:0,description:\"Direction of the wipe in degrees (0 = left to right)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Transition\"}},feather:{default:.1,description:\"Softness of the wipe edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse the direction the wipe travels\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.directional(T(\"angle\")),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Linear Wipe shader.\"});var{isMobileGpuViewport:Jpe}=kn,eme=JSON.stringify({type:\"sphere3D\",radius:.35});function _I(e,t,s,r,o,a,i){let l=z(ne(v(e,s),v(t,r)),\"ex\"),c=z(I(v(e,r),v(t,s)),\"ey\"),d=z(rt(l,c),\"p\"),u=z(ot(-.7,.85,c),\"sky\"),p=v(v(u,u),.4),_=z(v(o,1.05),\"r0\"),f=ot(_,v(_,.2),Kr(ne(d,rt(I(0,a),.55))));p=I(p,v(v(f,1.75),i));let h=z(v(o,1.25),\"r1\"),g=ot(h,v(h,.2),Kr(ne(d,rt(I(-.45,a),-.4))));p=I(p,v(v(g,1.15),i));let y=z(v(o,1.15),\"r2\"),b=ot(y,v(y,.2),Kr(ne(d,rt(I(.55,a),.1))));return I(p,v(v(b,1.1),i))}var fI=H({name:\"LiquidMetal\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Flowing liquid chrome \\u2014 a molten reflective surface that wraps the shape, sweeping a procedural studio reflection across animated folds with crisp speculars and prismatic edges\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=Jpe(),a=Lr(e,t,{scale:s.scale}),{sdf:i,pxH:l}=a,c=Dn(e,a,{bevelWidth:s.bevelWidth,bevelShape:s.bevelShape}),d=go(e,a),u=z(v(s.ripple,2.6),\"freq\"),p=z(v(r,.12),\"flowT\"),_=z(rt(v(d.member(\"x\"),u),v(d.member(\"y\"),u)),\"q\"),f=v(s.warp,.9),h=z(Ag(I(_,v(Tg(v(_,.6),p),f)),.62),\"hg\"),g=z(v(s.turbulence,.5),\"relief\"),y=Gi(c,v(v(h.member(\"x\"),g),-1),v(v(h.member(\"y\"),g),-1)),b=z(Lo(Ea(t,a),y),\"R\"),x=z(s.environment,\"envStr\"),P=z(Ge(s.sharpness,0,1),\"sharp\"),C=z(et(.5,.18,P),\"keyRad\"),E=z(v(Vt(v(r,.07)),.05),\"drift\"),R=Er(s.envRotation,\"env\"),U=R.member(\"x\"),L=R.member(\"y\"),k=z(Un(Er(s.lightAngle,\"light\"),-.7).member(\"H\"),\"Hh\"),D=z(xg(Ge(I(I(v(y.member(\"x\"),k.member(\"x\")),v(y.member(\"y\"),k.member(\"y\"))),v(y.member(\"z\"),k.member(\"z\"))),0,1),P,Ot(60,900,.4,1.4)),\"spec\"),O=z(I(_I(b.member(\"x\"),b.member(\"y\"),U,L,C,E,x),D),\"lumG\"),F,q;if(o)F=O,q=O;else{let Q=ne(1,Ge(v(y.member(\"z\"),-1),0,1)),te=z(v(v(s.dispersion,.09),I(.4,v(Q,1.3))),\"dth\"),J=z(I(_I(I(b.member(\"x\"),v(b.member(\"y\"),te)),ne(b.member(\"y\"),v(b.member(\"x\"),te)),U,L,C,E,x),D),\"lumBoffset\");q=J,F=ne(v(O,2),J)}let Y=z(s.darkColor.member(\"rgb\"),\"dark\"),V=z(s.lightColor.member(\"rgb\"),\"light\"),X=Ot(et(Y.member(\"x\"),V.member(\"x\"),Ge(F,0,1.8)),et(Y.member(\"y\"),V.member(\"y\"),Ge(O,0,1.8)),et(Y.member(\"z\"),V.member(\"z\"),Ge(q,0,1.8)),Jr(a,s.edgeSoftness));return Js(Dr(i,l),X,xt,\"liquidMetal\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the metal shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the metal shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the metal shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},lightColor:{default:\"#eef0f6\",transform:ce,description:\"The bright tone of the chrome highlights\",ui:{type:\"color\",label:\"Light Color\",group:\"Metal\"}},darkColor:{default:\"#141414\",transform:ce,description:\"The dark tone the reflection falls to in shadow \\u2014 recolor for tinted metal (gold, steel, copper\\u2026)\",ui:{type:\"color\",label:\"Dark Color\",group:\"Metal\"}},turbulence:{default:1,description:\"How molten the surface is \\u2014 the depth of the flowing folds that bend the reflection\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Turbulence\",group:\"Liquid\"}},ripple:{default:4,description:\"Scale of the molten folds \\u2014 higher = smaller, busier ripples\",ui:{type:\"range\",min:.5,max:8,step:.01,label:\"Ripple Scale\",group:\"Liquid\"}},warp:{default:1.5,description:\"Swirl of the flow \\u2014 domain-warps the folds into liquid curls\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Swirl\",group:\"Liquid\"}},speed:{default:.5,description:\"Speed of the molten flow and the slowly drifting reflection. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Liquid\"}},...gr({environment:{default:1.5,description:\"Strength of the reflected studio lighting \\u2014 the softboxes seen across the metal\",ui:{group:\"Surface\"}},envRotation:{ui:{group:\"Surface\"}}}),sharpness:{default:.6,description:\"Tightness of the specular highlights \\u2014 1 = razor chrome glints, 0 = soft satin\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Sharpness\",group:\"Surface\"}},...gr({dispersion:{description:\"Prismatic color fringing along the reflection edges\",ui:{group:\"Surface\"}},lightAngle:{default:265,description:\"Direction of the key light for the specular glints, in degrees\",ui:{group:\"Surface\"}}}),bevelWidth:{default:.05,description:\"Width of the edge bevel, relative to the shape \\u2014 thin for machined jewellery edges, wide for soft pillowed metal\",ui:{type:[\"range\",\"map\"],min:.005,max:.2,step:.001,label:\"Bevel Width\",group:\"Surface\"}},bevelShape:{default:0,description:\"Bevel profile \\u2014 0 = one smooth round fillet, 1 = machined: a steep outer fillet, a chamfer plateau and an inner knee, each catching its own line of light\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bevel Profile\",group:\"Surface\"}},...gr({edgeSoftness:{ui:{group:\"Surface\"}}}),shape:{default:eme,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var vI=H({name:\"Liquify\",role:\"warp\",category:\"Interactive\",description:\"Liquid-like interactive deformation effect\",boundingBoxDeclaration:{aspectRatio:null},usesPointer:!0,props:{intensity:{default:10,description:\"Scale of the fabric displacement effect\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Intensity\",group:\"Effect\"}},stiffness:{default:3,description:\"Fabric rigidity (higher = stiffer canvas, lower = stretchy silk)\",ui:{type:\"range\",min:1,max:30,step:.5,label:\"Stiffness\",group:\"Effect\"}},damping:{default:3,description:\"How quickly fabric motion settles\",ui:{type:\"range\",min:0,max:10,step:.1,label:\"Damping\",group:\"Effect\"}},radius:{default:1,description:\"Cursor influence area\",ui:{type:\"range\",min:.1,max:1.5,step:.1,label:\"Radius\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},...Ec(Y8({stiffness:T(\"stiffness\"),damping:T(\"damping\"),radius:T(\"radius\"),output:\"displacement\"})),map:iU({intensity:T(\"intensity\"),output:\"displacement\"}),uvRemapIdentityWhen:({computeOutputs:e})=>!e?.displacement});var Qg=12288,Id=vn({desktop:1024,mobile:768}),hI=\"rgba16float\",tme=14,sme=45,rme=6,ome=3,ame=9,nme=6,ime=1,lme=2.5,cme=.25,dme=.12,gI=.14,yI=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({colA:S,colB:S,count:n,dt:n,aspect:n,domainX:n,cursorX:n,cursorY:n,axisX:n,axisY:n,fieldType:n,restMode:n,strength:n,reachSq:n,alignK:n,damping:n,restK:n,pullK:n,homeK:n,omegaRef:n,agitCool:n,gridCols:n,cellW:n,cellH:n,jitter:n,bodyR:n}),\"SimParams\"),zc=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({agents:{storage:Fe(S,Qg),access:\"mutable\"},agit:{storage:Fe(n,Qg),access:\"mutable\"},accumE:{storage:Fe(lo(M),Id*Id),access:\"mutable\"},accumS:{storage:Fe(lo(M),Id*Id),access:\"mutable\"},params:{uniform:yI},outTex:{storageTexture:zt(hI,\"write-only\")}}),\"simLayout\"),ume=mz(zc,\"filingsInit\"),bI=w2.jitteredGridHome(zc,{overscan:gI}),pme=w2.dipoleOrRadial(zc),mme=On.orientation2d(zc,{home:bI,fieldAt:pme,torques:[A2.alignToField(zc),A2.rest(zc)],maxOffset:dme,name:\"filingsUpdate\"});function j2(e,t){return Gn.orientedWorld(zc,{shape:e,res:Id,splatRCap:tme,heading:\"angle\",agitation:\"buffer\",comet:!1,home:bI,ramp:{colorSpace:t,trails:\"none\"},names:{splat:\"filingsSplat\",resolve:\"filingsResolve\"}})}var _me=e=>j2(e,2).splat.kernel,fme=e=>j2(\"streak\",e).resolve.kernel,l2e=_me(\"streak\"),c2e=fme(2),xI=H({name:\"MagneticFilings\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Thousands of tiny iron filings scattered on paper that swing to align with a magnetic field around the cursor, tracing out the field lines \\u2014 a dipole whose axis follows the cursor's motion draws the classic two-lobed swirl, and every sweep of the magnet sends a glowing wave of filings flipping and settling\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{colorA:{default:\"#be1ef9\",transform:ce,description:\"Color of filings resting quietly, aligned and still\",ui:{type:\"color\",label:\"Rest Color\",group:\"Colors\"}},colorB:{default:\"#6326ff\",transform:ce,description:\"Color filings flash toward as they swing and settle \\u2014 a wave of it follows the moving magnet\",ui:{type:\"color\",label:\"Excited Color\",group:\"Colors\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the rest\\u2192excited color ramp\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},shape:{default:\"dot\",compileTime:!0,description:\"How each filing is drawn \\u2014 a slim streak or arrow reads as a compass needle, a box as a chunky filing, a dot or soft glow for a finer grain\",ui:{type:\"select\",options:lp,label:\"Shape\",group:\"Filings\"}},count:{default:8e3,description:\"Number of iron filings\",ui:{type:\"range\",min:500,max:12e3,step:500,label:\"Count\",group:\"Filings\"}},size:{default:1,description:\"Size of each filing\",ui:{type:\"range\",min:.5,max:3,step:.05,label:\"Size\",group:\"Filings\"}},restOrientation:{default:\"random\",description:\"Which way filings point where the field can't reach them \\u2014 randomly, all horizontal, or all vertical\",ui:{type:\"select\",options:[{label:\"Random\",value:\"random\"},{label:\"Horizontal\",value:\"horizontal\"},{label:\"Vertical\",value:\"vertical\"}],label:\"Rest Orientation\",group:\"Filings\"}},fieldType:{default:\"dipole\",description:\"The magnetic field the filings reveal \\u2014 a dipole (bar-magnet lobes that follow the cursor's motion) or a radial monopole (spokes out from the cursor)\",ui:{type:\"select\",options:[{label:\"Dipole\",value:\"dipole\"},{label:\"Radial\",value:\"radial\"}],label:\"Field Type\",group:\"Field\"}},strength:{default:1.7,description:\"How strongly the magnet grips the filings \\u2014 higher snaps more of them into sharp alignment\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Strength\",group:\"Field\"}},reach:{default:.6,description:\"How far the magnet's influence spreads (in screen heights) \\u2014 larger reveals more of the field at once\",ui:{type:\"range\",min:.1,max:1,step:.01,label:\"Reach\",group:\"Field\"}},response:{default:.5,description:\"How briskly filings swing and settle \\u2014 low is loose and floppy with a long wobble, high snaps into place quickly\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Response\",group:\"Field\"}}},...Fn({layout:zc,params:yI,maxAgents:Qg,countCap:{desktop:Qg,mobile:6e3},output:{key:\"outTex\",name:\"filingsTexture\",size:[Id,Id],format:hI},bake:({getCpuValue:e})=>{let t=j2(e(\"shape\")||\"streak\",e(\"colorSpace\")??2);return{pipelines:{init:{kernel:ume,threads:\"max\"},update:{kernel:mme,threads:\"agents\"},splat:t.splat,resolve:t.resolve},initStep:\"init\",program:[\"update\",\"splat\",\"resolve\"]}},frame:(e,{getCpuValue:t})=>{let s=Li(t),r=rO({smoothing:.1,teleport:.25});return o=>{let{dt:a,aspect:i,pointerX:l,pointerY:c}=Di(o),d=Math.max(i,.01),u=e.resolveCount(s(\"count\",5e3)),{cols:p,cellW:_,cellH:f}=oO(u,d,gI),h=l*i,g=c,y=r.update(h,g),b=(t(\"fieldType\")??\"dipole\")===\"radial\"?1:0,x=t(\"restOrientation\")??\"random\",P=x===\"horizontal\"?1:x===\"vertical\"?2:0,C=s(\"strength\",1),E=s(\"reach\",.35),R=Math.min(Math.max(s(\"response\",.5),0),1),U=t(\"colorA\"),L=t(\"colorB\"),k=Math.min(Math.max(s(\"size\",1),.5),3)*1.7/Ri;return e.writeParams({colA:S(U?.x??.522,U?.y??.573,U?.z??.639,U?.w??1),colB:S(L?.x??1,L?.y??.616,L?.z??.361,L?.w??1),count:u,dt:a,aspect:i,domainX:d,cursorX:h,cursorY:g,axisX:y.x,axisY:y.y,fieldType:b,restMode:P,strength:C,reachSq:E*E,alignK:sme,damping:ome+R*ame,restK:rme,pullK:cme,homeK:lme,omegaRef:nme,agitCool:ime,gridCols:p,cellW:_,cellH:f,jitter:.85,bodyR:k}),e.frame({count:u})}},fragment:{output:\"filingsTexture\",fallback:\"transparent\"}})});var vme=3.141592653589793,AI=H({name:\"Marble\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Classic marble swirl and vein texture using noise-warped sine waves\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#ffffff\",transform:ce,description:\"Base background color of the marble\",ui:{type:\"color\",label:\"Base\",group:\"Colors\"}},colorB:{default:\"#3a2d54\",transform:ce,description:\"Secondary marble tone\",ui:{type:\"color\",label:\"Tone\",group:\"Colors\"}},colorC:{default:\"#0f0f0f\",transform:ce,description:\"Deepest marble color\",ui:{type:\"color\",label:\"Depth\",group:\"Colors\"}},scale:{default:2,description:\"Scale and density of the marble vein pattern\",ui:{type:[\"range\",\"map\"],min:.1,max:10,step:.1,label:\"Scale\",group:\"Effect\"}},turbulence:{default:10,description:\"Amount of noise-driven distortion applied to the veins\",ui:{type:[\"range\",\"map\"],min:0,max:50,step:.5,label:\"Turbulence\",group:\"Effect\"}},speed:{default:.05,description:\"Animation speed\",ui:{type:\"range\",min:0,max:.25,step:.005,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:e=>{let{uv:t,viewport:s}=aa(e),r=z(Ce(s.member(\"x\"),it(s.member(\"y\"),1e-6)),\"aspect\"),o=z(wt(e),\"marbleTime\"),a=me(T(\"seed\"),e),i=z(v(rt(v(t.member(\"x\"),r),t.member(\"y\")),me(T(\"scale\"),e)),\"plane\"),l=i.member(\"x\"),c=i.member(\"y\"),d=z(I(I(pa(rt(I(l,a),I(c,o))),v(pa(rt(I(v(l,2),v(a,1.7)),I(v(c,2),v(o,.7)))),.5)),v(pa(rt(I(v(l,4),v(a,3.1)),I(v(c,4),v(o,1.3)))),.25)),\"marbleTurb\"),u=z(I(v(Vt(I(v(l,vme),v(d,me(T(\"turbulence\"),e)))),.5),.5),\"veinT\"),p=I(v(pa(rt(I(v(c,1.5),v(a,2)),v(l,1.2))),.3),.5);return pg({a:T(\"colorA\"),b:T(\"colorB\"),c:T(\"colorC\"),space:T(\"colorSpace\")})(ot(.3,.6,u),v(ot(.6,.9,u),p),e)}});var Zg=1.6,hme=.48,gme=.125,yme=3,wI=1.9,bme=1.4,xme=.045,Ame=9,wme=[{color:\"#1a0533\",position:0},{color:\"#6d2fd1\",position:.26},{color:\"#e04b9e\",position:.52},{color:\"#ff8c42\",position:.76},{color:\"#ffdf8e\",position:1}],TI=H({name:\"MeshGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Flowing mesh gradient of soft drifting color swaths whose seams wrap through the palette\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},extraFields:{meshAnchors:{schema:Fe(S,4),initial:new Array(16).fill(0)}},props:{colorA:{default:\"#1a0533\",transform:ce,description:\"First gradient color (two-color fallback when stops are cleared)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffdf8e\",transform:ce,description:\"Second gradient color (two-color fallback when stops are cleared)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:{...Ms(),default:wme},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},count:{default:5,description:\"Number of color points scattered across the canvas\",ui:{type:\"range\",min:2,max:8,step:1,label:\"Points\",group:\"Effect\"}},smoothness:{default:2,description:\"How smoothly the color points blend into each other\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.01,label:\"Smoothness\",group:\"Effect\"}},variation:{default:.35,recompile:Lt(0),description:\"Varies edge softness across the canvas \\u2014 some color boundaries crisp, others diffuse\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Effect\"}},swirl:{default:.3,description:\"Vortex rotation that spirals the field around the frame centre \\u2014 negative values spin the other way\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Swirl\",group:\"Effect\"}},drift:{default:.5,description:\"How far the color points wander from their home positions\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Drift\",group:\"Effect\"}},wrapping:{default:0,description:\"Wraps the palette back through itself where colors meet \\u2014 banded seams where the field pinches, smooth elsewhere\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Wrapping\",group:\"Effect\"}},speed:{default:1,description:\"Animation speed\",ui:{type:\"range\",min:0,max:10,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed for the point layout and palette assignment\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}}},paint:e=>{let{uv:t,viewport:s}=aa(e),r=z(Ce(s.member(\"x\"),it(s.member(\"y\"),1e-6)),\"aspect\"),o=z(wt(e),\"meshTime\"),a=me(T(\"seed\"),e),i=z(rt(ne(v(t.member(\"x\"),r),v(r,.5)),ne(t.member(\"y\"),.5)),\"meshCentred\"),l=z(v(v(-yme,me(T(\"swirl\"),e)),ot(0,1,Kr(i))),\"swirlAngle\"),c=z(ng(i,Do(l),Vt(l)),\"meshRot\"),d=z(rt(I(c.member(\"x\"),v(r,.5)),I(c.member(\"y\"),.5)),\"meshSwirled\"),u=z(LR({at:Qe(v(d.member(\"x\"),Zg),v(d.member(\"y\"),Zg),v(a,7.31)),time:v(o,gme),amount:hme,levels:Rr()?1:2}),\"meshWarp\"),p=z(rt(Ce(u.member(\"x\"),Zg),Ce(u.member(\"y\"),Zg)),\"meshQ\"),_=Ce(8,I(me(T(\"smoothness\"),e),.5)),f=e.propValues.variation,h=f!==void 0&&f!==0?Ge(v(_,tr(v(v(Jn(Qe(v(p.member(\"x\"),wI),v(p.member(\"y\"),wI),I(v(a,3.17),v(o,xme)))),me(T(\"variation\"),e)),bme))),.6,16):Ge(_,.6,16),g=zR(e,{count:T(\"count\"),seed:T(\"seed\"),drift:T(\"drift\"),field:\"meshAnchors\"});return BL(UR(RR({t:BR({at:p,count:me(T(\"count\"),e),seed:a,drift:me(T(\"drift\"),e),aspect:r,time:o,softness:h,anchors:g}),cycles:I(1,v(me(T(\"wrapping\"),e),Ame))}),e),e)}});var SI=H({name:\"Mirror\",role:\"warp\",category:\"Distortions\",description:\"Mirror content across a line defined by center point and angle\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The point the mirror line passes through\"),angle:{default:0,description:\"The angle of the mirror line in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},edges:fr(\"mirror\",\"How to handle edges when distortion pushes content out of bounds\")},map:VL({center:T(\"center\"),angle:T(\"angle\")}),missingChildMessage:\"You must pass a child component into the Mirror shader.\"});var PI=H({name:\"MultiPointGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Five individually placed color points blended together by proximity \\u2014 drag each point to shape the gradient\",acceptsUVContext:!0,props:{colorA:{default:\"#4776E6\",transform:ce,description:\"Color of control point A\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},positionA:{default:{x:.2,y:.2},transform:Xe,description:\"Position of control point A\",ui:{type:\"position\",label:\"Position A\",group:\"Position\"}},colorB:{default:\"#C44DFF\",transform:ce,description:\"Color of control point B\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},positionB:{default:{x:.8,y:.2},transform:Xe,description:\"Position of control point B\",ui:{type:\"position\",label:\"Position B\",group:\"Position\"}},colorC:{default:\"#1ABC9C\",transform:ce,description:\"Color of control point C\",ui:{type:\"color\",label:\"Color C\",group:\"Colors\"}},positionC:{default:{x:.2,y:.8},transform:Xe,description:\"Position of control point C\",ui:{type:\"position\",label:\"Position C\",group:\"Position\"}},colorD:{default:\"#F8BBD9\",transform:ce,description:\"Color of control point D\",ui:{type:\"color\",label:\"Color D\",group:\"Colors\"}},positionD:{default:{x:.8,y:.8},transform:Xe,description:\"Position of control point D\",ui:{type:\"position\",label:\"Position D\",group:\"Position\"}},colorE:{default:\"#FF8C42\",transform:ce,description:\"Color of control point E\",ui:{type:\"color\",label:\"Color E\",group:\"Colors\"}},positionE:{default:{x:.5,y:.5},transform:Xe,description:\"Position of control point E\",ui:{type:\"position\",label:\"Position E\",group:\"Position\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},smoothness:{default:2,description:\"Controls how smoothly colors blend.\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.01,label:\"Smoothness\",group:\"Effect\"}}},paint:IR({points:[{color:T(\"colorA\"),position:T(\"positionA\")},{color:T(\"colorB\"),position:T(\"positionB\")},{color:T(\"colorC\"),position:T(\"positionC\")},{color:T(\"colorD\"),position:T(\"positionD\")},{color:T(\"colorE\"),position:T(\"positionE\")}],smoothness:T(\"smoothness\"),space:T(\"colorSpace\")})});var{isMobileGpuViewport:Tme,VOLUMETRIC_FIELD_EXTRA_FIELDS:Sme}=kn,{ANALYTIC_SDF_EXTRA_FIELDS:Pme}=yc,Cme=JSON.stringify({type:\"sphere3D\",radius:.35}),Mme=6,Eme=4,CI=H({name:\"Nebula\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"A volumetric gas nebula sealed inside polished glass; billowing emission clouds with hollow dark cavities and hot glowing cores, star fields drifting at real depth behind the gas, all refracted through the curved walls of the shape and dressed with studio reflections\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=Tme()?Eme:Mme,a=t.propValues.colorSpace??0,i,l;if(a!==0){let Ee=\"\";t.onBeforeRender(()=>{let Ie=[\"veilColor\",\"gasColor\",\"coreColor\"].map(we=>t.getCpuValue(we));if(Ie.some(we=>!we))return;let ke=Ie.map(we=>`${we.x},${we.y},${we.z}`).join(\"|\");if(ke===Ee)return;Ee=ke;let Me=[\"convVeil\",\"convGas\",\"convCore\"];Ie.forEach((we,Ve)=>{let qe=fl(we.x,we.y,we.z,a);t.setExtraField(Me[Ve],[qe[0],qe[1],qe[2]])})});let ze=Bd[a]??hc;i=Ie=>W(ze,\"mixPreconvertedColors\",[s.convVeil,s.convGas,s.veilColor.member(\"a\"),s.gasColor.member(\"a\"),Ie]),l=(Ie,ke,Me)=>{let we=z(W(_l,\"mixInSpace\",[s.convVeil,s.veilColor.member(\"a\"),s.convGas,s.gasColor.member(\"a\"),Ie]),`${Me}AB`);return W(ze,\"mixPreconvertedColors\",[we.member(\"conv\"),s.convCore,we.member(\"alpha\"),s.coreColor.member(\"a\"),ke])}}else i=Ee=>W(Ar,\"mixColors\",[s.veilColor,s.gasColor,Ee]),l=(Ee,ze)=>W(Ar,\"mixColors\",[W(Ar,\"mixColors\",[s.veilColor,s.gasColor,Ee]),s.coreColor,ze]);let c=Lr(e,t,{scale:s.scale}),{sdf:d,pxH:u}=c,p=Dn(e,c,{bevelWidth:It(.09),bevelShape:It(0)}),_=Ea(t,c),f=yL(e,p,z(v(s.refraction,.16),\"bend\")),{x:h,y:g}=f.centered,y=gL(e),b=SL(c,e,{origin:f,view:_,shear:.55,lengthScale:.9}),x=b.length,P=z(v(s.gasScale,3.1),\"freq\"),C=z(v(r,.14),\"drift\"),E=z(v(s.seed,7.31),\"seedOff\"),R=z(v(xp(v(f.x,311.7),v(f.y,173.3)),.15/o),\"jit\"),U=z(v(pa(rt(I(I(v(h,1.3),v(C,.6)),v(E,.37)),I(v(g,1.3),v(E,.61)))),.5),\"macro\"),L=z(et(-.35,.5,s.cavity),\"cav\"),k=z(v(s.billow,.55),\"billowAmt\"),D=z(s.coreColor.member(\"rgb\"),\"coreRgb\"),O=z(s.veilColor.member(\"rgb\"),\"veilRgb\"),F=z(v(I(.3,v(x,.9)),6/o),\"eGain\"),q=z(v(v(I(.5,v(x,2.2)),s.density),3/o),\"aGain\"),Y=CL({steps:o,entry:b.entry,length:b.length,jitter:R,emissionGain:F,hint:\"gasAcc\"},({i:Ee,t:ze,z:Ie})=>{let ke=b.at(Ie),Me=PL({x:ke.x,y:ke.y,z:Ie},{frequency:P,drift:C,seed:E,billow:k,hint:`${Ee}`}),we=ot(0,.11,v(x,Math.min(ze,1-ze))),Ve=ne(1,Ge(v(ms(Me.fine),1.6),0,1)),qe=z(v(v(ot(0,.5,ne(I(Me.density,U),L)),we),I(.65,v(Ve,.7))),`dens${Ee}`),st=z(v(v(ot(.45,.75,Me.mid),we),s.dust),`dust${Ee}`),pt=z(l(ot(.05,.75,qe),ot(.68,1,qe),`cB${Ee}`),`cB${Ee}`).member(\"rgb\"),Ze=1-ze*.45,nt=v(ot(-.7,.3,Me.density),v(.05,we));return{emit:I(I(v(pt,v(qe,Ze)),v(D,v(ts(qe,3),v(s.glow,1.2*Ze)))),v(O,v(nt,Ze))),absorb:I(kt(v(qe,q)),v(Qe(1.5,1,.55),v(st,v(q,2.2))))}}),V=z(v(s.starScale,34),\"starFreq\"),X=z(v(s.twinkle,.5),\"twinkleAmt\"),Q=_2(b,{depth:1.1,pan:y,panRate:1.3,hint:\"far\"}),te=d2(z(rt(v(Q.x,V),v(Q.y,V)),\"farP\"),{variationFreq:.13,radius:[.004,.013],keep:[.35,.75],brightness:[.3,1.2],shimmer:Ee=>c2(r,Ee,{amount:X,rate:1.7,spread:40}),gain:s.stars,tint:{from:[.72,.82,1],to:[1,.92,.82],gain:1.6},hint:\"starFar\"}),J=_2(b,{depth:.25,pan:y,panRate:.55,hint:\"near\"}),oe=v(V,.56),le=d2(z(rt(I(v(J.x,oe),7.3),I(v(J.y,oe),2.9)),\"nearP\"),{variationFreq:.11,radius:[.003,.01],keep:[.45,.8],brightness:[.35,1.3],shimmer:Ee=>c2(r,Ee,{amount:X,rate:2.3,spread:34}),gain:s.stars,tint:{from:[.75,.85,1],to:[1,.95,.88],gain:1.8},hint:\"starNear\"}),se=Er(s.lightAngle,\"light\"),Te=z(Ge($t(p,Un(se,-.7).member(\"H\")),0,1),\"ndh\"),de=z(Lo(_,p),\"R\"),xe=z(Pc(de.member(\"x\"),de.member(\"y\"),{rotation:se,keyRadius:.2,drift:0,strength:s.environment,skyGain:.32}),\"envLum\"),Ae=wL(Te,{core:[140,1.3],halo:[22,.14],softness:s.highlightSoftness,gain:s.highlight}),De=yp(EL([{paint:i(It(.25)).member(\"rgb\"),opacity:.06,behind:Y.transmittance},Y.color,{paint:te.rgb,behind:Y.transmittance},{paint:le.rgb,behind:et(Y.transmittance,kt(1),.35)},{paint:v(Qe(.85,.92,1.08),xe),opacity:.38},v(Qe(1,.98,.95),Ae)]));return Js(Dr(d,u),Ot(De,Jr(c,s.edgeSoftness)),xt,\"nebula\")}}),extraFields:{...Sme,...Pme,convVeil:{schema:A,initial:[0,0,0]},convGas:{schema:A,initial:[0,0,0]},convCore:{schema:A,initial:[0,0,0]}},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the nebula shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the nebula shape (1 = default size)\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the nebula shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},coreColor:{default:\"#ffd9b0\",transform:ce,description:\"The hot color the densest gas cores glow with \\u2014 the embedded newborn stars\",ui:{type:\"color\",label:\"Core Color\",group:\"Nebula\"}},gasColor:{default:\"#ff5e7a\",transform:ce,description:\"The main emission color of the gas body \\u2014 H-alpha rose by default\",ui:{type:\"color\",label:\"Gas Color\",group:\"Nebula\"}},veilColor:{default:\"#3a5bdb\",transform:ce,description:\"The color of the thin outer veils and the deep-space background behind the gas\",ui:{type:\"color\",label:\"Veil Color\",group:\"Nebula\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space the veil \\u2192 gas \\u2192 core ramps blend in\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Nebula\"}},density:{default:1,description:\"Optical density of the gas \\u2014 how strongly clouds occlude the stars and gas behind them\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Density\",group:\"Nebula\"}},cavity:{default:0,description:\"How hollowed-out the nebula is \\u2014 0 fills the volume with cloud banks, high carves large dark voids between them\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Cavity\",group:\"Nebula\"}},dust:{default:.55,description:\"Cold dark dust lanes threading the gas \\u2014 absorb-only tendrils that redden the clouds and stars behind them\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Dust\",group:\"Nebula\"}},gasScale:{default:.4,description:\"Feature size of the gas \\u2014 higher = finer, busier cloud detail\",ui:{type:[\"range\",\"map\"],min:.1,max:1,step:.01,label:\"Gas Scale\",group:\"Nebula\"}},billow:{default:.6,description:\"Turbulent folding of the clouds \\u2014 0 = smooth drifting banks, 1 = heavily churned billows\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Billow\",group:\"Nebula\"}},glow:{default:0,description:\"Hot emission bloom of the dense cores\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Glow\",group:\"Nebula\"}},seed:{default:0,description:\"Random seed \\u2014 pans the gas field to a different region of noise space for a new cloud composition\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Nebula\"}},stars:{default:.07,description:\"Brightness of the star fields drifting at depth behind and inside the gas\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Stars\",group:\"Stars\"}},starScale:{default:1,description:\"Density of the star fields \\u2014 higher = smaller, busier stars\",ui:{type:[\"range\",\"map\"],min:.25,max:3,step:.01,label:\"Star Scale\",group:\"Stars\"}},twinkle:{default:.35,description:\"Intensity of the star twinkle (0 = steady stars, 1 = full shimmer)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Twinkle\",group:\"Stars\"}},...gr({refraction:{description:\"How strongly the curved glass walls bend the interior seen near the edges\"},environment:{default:.7,description:\"Strength of the studio softboxes reflected in the polished glass surface\"},highlight:{default:1.15,description:\"Sharp key-light glint on the glass\",ui:{group:\"Glass\"}},highlightSoftness:{ui:{group:\"Glass\"}},lightAngle:{description:\"Direction of the key light and reflected studio, in degrees\"},edgeSoftness:!0}),speed:{default:.3,description:\"Speed of the slowly churning gas and twinkling stars. 0 freezes the nebula.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Animation\"}},shape:{default:Cme,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var MI=H({name:\"NoiseDissolve\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Dissolve the content away through an organic noise pattern\",props:{progress:{default:.5,description:\"How far the dissolve has progressed (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},scale:{default:3,description:\"Frequency of the dissolve pattern (higher = smaller blobs)\",ui:{type:[\"range\",\"map\"],min:.5,max:20,step:.1,label:\"Scale\",group:\"Transition\"}},softness:{default:.25,description:\"How softly the erosion edge fades out (0 = hard-edged blobs)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Transition\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse the order the pattern dissolves in\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.noise(T(\"scale\"),T(\"seed\")),progress:T(\"progress\"),feather:T(\"softness\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Noise Dissolve shader.\"});var EI=H({name:\"ObjectTracker\",role:\"overlay\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Computer-vision style object detection overlay \\u2014 draws bounding boxes and labels around detected regions of the content below, with grid, quadtree and mosaic layouts.\",requiresRTT:!0,requiresChild:!0,props:{detectionMode:{default:\"bright\",compileTime:!0,description:\"What counts as a detectable object.\",ui:{type:\"select\",options:[{label:\"Transparency\",value:\"alpha\"},{label:\"Brightness\",value:\"bright\"},{label:\"Darkness\",value:\"dark\"},{label:\"Red\",value:\"red\"},{label:\"Green\",value:\"green\"},{label:\"Blue\",value:\"blue\"}],label:\"Detect\",group:\"Detection\"}},threshold:{default:.25,description:\"Detection cut-off.\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Threshold\",group:\"Detection\"}},layout:{default:\"mosaic\",compileTime:!0,description:\"How the canvas is partitioned into detection cells.\",ui:{type:\"select\",options:[{label:\"Grid\",value:\"grid\"},{label:\"Quadtree\",value:\"quadtree\"},{label:\"Mosaic\",value:\"mosaic\"}],label:\"Layout\",group:\"Detection\"}},cellSize:{default:400,description:\"Base cell size in pixels\",ui:{type:\"range\",min:24,max:800,step:1,label:\"Base Cell Size\",group:\"Detection\"}},maxDepth:{default:2,compileTime:!0,description:\"Maximum subdivision levels for Quadtree/Mosaic layouts (each level can quarter a cell).\",ui:{type:\"range\",min:1,max:5,step:1,label:\"Subdivision\",group:\"Detection\",condition:{layout:[\"quadtree\",\"mosaic\"]}}},boxStyle:{default:\"corners\",compileTime:!0,description:\"Bounding box outline style\",ui:{type:\"select\",options:[{label:\"Full\",value:\"full\"},{label:\"Corners\",value:\"corners\"}],label:\"Box Style\",group:\"Boxes\"}},lineWidth:{default:1.5,description:\"Thickness of the box outline in pixels.\",ui:{type:\"range\",min:.5,max:12,step:.5,label:\"Line Width\",group:\"Boxes\"}},cornerRadius:{default:0,description:\"Corner radius of the box outline in pixels\",ui:{type:\"range\",min:0,max:40,step:1,label:\"Corner Radius\",group:\"Boxes\",condition:{boxStyle:\"full\"}}},strokeColor:{default:\"#ffffff\",transform:ce,description:\"Bounding box outline color.\",ui:{type:\"color\",label:\"Stroke\",group:\"Colors\"}},fillColor:{default:\"#5a79911a\",transform:ce,description:\"Bounding box fill color.\",ui:{type:\"color\",label:\"Fill\",group:\"Colors\"}},labelColor:{default:\"#000000\",transform:ce,description:\"Label text color.\",ui:{type:\"color\",label:\"Label\",group:\"Colors\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},labelBackgroundColor:{default:\"#ffffff\",transform:ce,description:\"Label pill background color.\",ui:{type:\"color\",label:\"Label Background\",group:\"Colors\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},labelMode:{default:\"none\",compileTime:!0,description:\"What each label shows.\",ui:{type:\"select\",options:[{label:\"None\",value:\"none\"},{label:\"Dimensions\",value:\"dimensions\"},{label:\"Percentage\",value:\"percentage\"}],label:\"Label\",group:\"Labels\"}},labelPosition:{default:\"bottom-right\",compileTime:!0,description:\"Which corner of the box the label anchors to.\",ui:{type:\"select\",options:[{label:\"Bottom Left\",value:\"bottom-left\"},{label:\"Bottom Right\",value:\"bottom-right\"},{label:\"Top Left\",value:\"top-left\"},{label:\"Top Right\",value:\"top-right\"}],label:\"Label Position\",group:\"Labels\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},labelRadius:{default:0,description:\"Corner radius of the label pill in pixels.\",ui:{type:\"range\",min:0,max:30,step:1,label:\"Border Radius\",group:\"Labels\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},labelInset:{default:!0,transform:rs,description:\"Place the label inside or outside the box.\",ui:{type:\"checkbox\",label:\"Label Inset\",group:\"Labels\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},fontFamily:{default:\"Inter\",description:\"Google Fonts family used for label text.\",ui:{type:\"font-family\",label:\"Font\",group:\"Typography\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},fontWeight:{default:500,description:\"Font weight for label text.\",ui:{type:\"font-weight\",label:\"Weight\",group:\"Typography\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},fontSize:{default:.015,description:\"Label text size as a fraction of canvas height.\",ui:{type:\"range\",min:.01,max:.5,step:.005,label:\"Size\",group:\"Typography\",dimensional:\"canvas-height\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}},letterSpacing:{default:0,description:\"Letter spacing for label text, in em units.\",ui:{type:\"range\",min:-.1,max:.5,step:.005,label:\"Letter Spacing\",group:\"Typography\",condition:{labelMode:[\"dimensions\",\"percentage\"]}}}},gpu:{fragment:G8({detectionMode:T(\"detectionMode\"),threshold:T(\"threshold\"),layout:T(\"layout\"),cellSize:T(\"cellSize\"),maxDepth:T(\"maxDepth\"),boxStyle:T(\"boxStyle\"),lineWidth:T(\"lineWidth\"),cornerRadius:T(\"cornerRadius\"),strokeColor:T(\"strokeColor\"),fillColor:T(\"fillColor\"),labelColor:T(\"labelColor\"),labelBackgroundColor:T(\"labelBackgroundColor\"),labelMode:T(\"labelMode\"),labelPosition:T(\"labelPosition\"),labelInset:T(\"labelInset\"),labelRadius:T(\"labelRadius\"),fontFamily:T(\"fontFamily\"),fontWeight:T(\"fontWeight\"),fontSize:T(\"fontSize\"),letterSpacing:T(\"letterSpacing\")})}});var kme=JSON.stringify({type:\"sphere3D\",radius:.35}),Bme=[{color:\"#ff2f7a\",position:0},{color:\"#2a2cff\",position:.3},{color:\"#5fe6ff\",position:.55},{color:\"#a6ffd8\",position:.75},{color:\"#ffb3e6\",position:1}],zme=.9,Rme=.55,Dme=.25,kI=H({name:\"Obsidian\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Dark tinted glass whose faces stay near-black while every surface turning away from the viewer ignites with a flowing iridescent gradient \\u2014 vivid color living only on the oblique walls and bevels\",animatedTime:{speed:\"speed\"},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the glass shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the glass shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the glass shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},bodyColor:{default:\"#06071a\",transform:ce,description:\"The dark body of the glass \\u2014 what the faces looking straight at you show. Keep it deep for the iridescence to pop.\",ui:{type:\"color\",label:\"Body Color\",group:\"Colors\"}},colorA:{default:\"#ff2f7a\",transform:ce,description:\"First iridescent color (two-color fallback when stops are cleared)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#5fe6ff\",transform:ce,description:\"Second iridescent color (two-color fallback when stops are cleared)\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:{...Ms(),default:Bme},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},iridescence:{default:1,description:\"Strength of the color on the oblique surfaces \\u2014 0 = plain dark glass, above 1 = saturated neon walls\",ui:{type:[\"range\",\"map\"],min:0,max:1.5,step:.01,label:\"Iridescence\",group:\"Iridescence\"}},rimWidth:{default:.55,description:\"How far in from edge-on the color reaches \\u2014 small keeps it to the steepest walls, large lets it creep across gently turning faces\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Rim Width\",group:\"Iridescence\"}},rimSoftness:{default:.25,description:\"Softness of the transition from dark face to colored wall\",ui:{type:[\"range\",\"map\"],min:.02,max:.6,step:.01,label:\"Rim Softness\",group:\"Iridescence\"}},flowAngle:{default:30,description:\"Direction the gradient flows across the surface, in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Flow Angle\",group:\"Iridescence\"}},flowScale:{default:1,description:\"How many times the palette cycles across the shape \\u2014 higher = tighter color bands\",ui:{type:[\"range\",\"map\"],min:.2,max:4,step:.01,label:\"Flow Scale\",group:\"Iridescence\"}},speed:{default:.5,description:\"Speed the gradient flows along the walls. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Iridescence\"}},gloss:{default:.35,description:\"Strength of the studio softboxes reflected in the glass \\u2014 the pale highlights riding the top edges\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Gloss\",group:\"Surface\"}},...gr({envRotation:{ui:{group:\"Surface\"}}}),bevelWidth:{default:.08,description:\"Width of the edge bevel on flat shapes \\u2014 the band that turns away from the viewer and catches the color\",ui:{type:[\"range\",\"map\"],min:.005,max:.3,step:.001,label:\"Bevel Width\",group:\"Surface\"}},bevelShape:{default:0,description:\"Bevel profile \\u2014 0 = one smooth round fillet, 1 = machined: a steep outer fillet, a chamfer plateau and an inner knee\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bevel Profile\",group:\"Surface\"}},...gr({edgeSoftness:{ui:{group:\"Surface\"}}}),shape:{default:kme,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=Lr(e,t,{scale:s.scale}),a=Dn(e,o,{bevelWidth:s.bevelWidth,bevelShape:s.bevelShape}),i=Ea(t,o),l=ni(a,i),c=z(ne(1,s.rimWidth),\"rimEdge\"),d=z(ot(ne(c,s.rimSoftness),I(c,s.rimSoftness),l),\"rim\"),u=ii(s.flowAngle,\"flow\").coordsOf(go(e,o)).along,p=I(I(v(u,v(s.flowScale,1/Rme)),v(l,zme)),v(r,.2)),_=z(Us(T(\"colorSpace\"))(DR(p),t).member(\"rgb\"),\"iri\"),f=z(Lo(i,a),\"R\"),h=z(v(Pc(f.member(\"x\"),f.member(\"y\"),{rotation:Er(s.envRotation,\"env\"),keyRadius:.3,drift:0,strength:s.gloss,skyGain:Dme}),Ap(l,{r0:.04,gain:.96,power:4})),\"shine\"),g=I(et(s.bodyColor.member(\"rgb\"),_,kt(v(d,s.iridescence))),v(et(_,kt(1),kt(.6)),kt(v(h,.5))));return Js(Dr(o.sdf,o.pxH),Ot(g,Jr(o,s.edgeSoftness)),xt,\"obsidian\")}})});var Z_=qU({corner:T(\"corner\"),amount:T(\"amount\"),radius:T(\"radius\")}),BI=H({name:\"PagePeel\",role:\"filter\",species:\"gather\",category:\"Transitions\",description:\"Curl the content up from a corner like a peeling page\",boundingBoxDeclaration:{aspectRatio:null},props:{corner:{default:\"bottom-right\",description:\"Which corner the page peels from\",compileTime:!0,ui:{type:\"select\",options:[{label:\"Top Left\",value:\"top-left\"},{label:\"Top Right\",value:\"top-right\"},{label:\"Bottom Left\",value:\"bottom-left\"},{label:\"Bottom Right\",value:\"bottom-right\"}],label:\"Corner\",group:\"Effect\"}},amount:{default:.2,description:\"How far the peel has progressed across the page (0 = flat, 1 = fully peeled)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Amount\",group:\"Effect\"}},radius:{default:.2,description:\"Tightness of the curl (fraction of the page diagonal)\",ui:{type:[\"range\",\"map\"],min:.02,max:.4,step:.01,label:\"Curl Radius\",group:\"Effect\"}},shading:{default:.55,description:\"How much the underside of the curl is shaded for depth\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shading\",group:\"Effect\"}},highlight:{default:.4,description:\"Strength of the specular sheen running along the curl\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlight\",group:\"Effect\"}},highlightSoftness:{default:.2,description:\"Width of the specular highlight band (low = tight gloss, high = broad satin)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlight Softness\",group:\"Effect\"}},shadow:{default:1,description:\"Strength of the contact shadow the curl casts on the page\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadow\",group:\"Effect\"}}},effect:$o(HU(Z_,{shade:VU(Z_,{shading:T(\"shading\")}),sheen:NU(Z_,{highlight:T(\"highlight\"),softness:T(\"highlightSoftness\")}),overhang:$U(Z_,{amount:T(\"amount\"),shadow:T(\"shadow\")}),reveal:XU(Z_,{amount:T(\"amount\"),shadow:T(\"shadow\")})}),[])});var zI=H({name:\"Paper\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Applies realistic paper grain and surface roughness to child content\",requiresRTT:!0,requiresChild:!0,props:{roughness:{default:.3,description:\"Surface roughness \\u2014 higher values create more pronounced brightness variation\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Roughness\",group:\"Texture\"}},grainScale:{default:1,description:\"Scale of the paper grain \\u2014 lower = coarser, higher = finer\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Grain Scale\",group:\"Texture\"}},displacement:{default:.15,description:\"Surface micro-roughness \\u2014 shifts pixels at grain scale like real paper fiber bumps\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Displacement\",group:\"Texture\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Texture\"}}},gpu:{fragment:e=>{let{childNode:t,ctx:s,uniforms:r,convertToTexture:o}=e;if(!t)return console.error(\"You must pass a child component into the Paper shader.\"),xt;let a=o(t),i=W(X4,\"paperSurface\",[s.uv,s.aspect,r.grainScale,r.seed,r.displacement,r.roughness]),l=W(Yt,\"unpremultiplyAlpha\",[a.sample(i.member(\"xy\"))]);return Ct(l.member(\"rgb\").mul(i.member(\"z\")),l.member(\"a\"))}}});var RI=H({role:\"shape\",name:\"Parallelogram\",description:\"Parallelogram with adjustable width, height and skew\",shape:{distance:({x:e,y:t},s)=>gR(e,t,s(\"width\"),s(\"height\"),s(\"skew\")),colorDescription:\"Fill color of the parallelogram\",centerDescription:\"Center position of the parallelogram\",bounds:{size:{width:\"width\",height:\"height\"}},shapeProps:{width:{default:.32,description:\"Half-width of the parallelogram\",ui:{type:\"range\",min:.02,max:1,step:.01,label:\"Width\",group:\"Shape\"}},height:{default:.22,description:\"Half-height of the parallelogram\",ui:{type:\"range\",min:.02,max:1,step:.01,label:\"Height\",group:\"Shape\"}},skew:{default:.15,description:\"Horizontal skew of the top edge\",ui:{type:\"range\",min:-.4,max:.4,step:.01,label:\"Skew\",group:\"Shape\"}}}}});var K2=256,ey=K2*K2,DI=4e4,Lme=12e3,Yn=vn({desktop:1024,mobile:768}),Jg=Yn*Yn,Ume=8,Ome=.75,LI=.002,UI=\"rgba16float\",Gme=2048,Fme=1.15,Ime=2.6,OI=1.1,Yme=26,Wme=4.2,qme=4,Vme=10,Nme=4,GI=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({rowX:S,rowY:S,rowZ:S,dt:n,time:n,snap:n,gridW:n,gridH:n,outW:n,outH:n,depth:n,dragMul:n,wobbleAmp:n,depthShading:n,spacing:n,particleSize:n,aspect:n,zoom:n,transX:n,transY:n,pointerX:n,pointerY:n,cursorForce:n,cursorZForce:n,cursorRadSq:n}),\"SimParams\"),ty=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({pos:{storage:Fe(S,ey),access:\"mutable\"},vel:{storage:Fe(S,ey),access:\"mutable\"},col:{storage:Fe(S,ey),access:\"mutable\"},accumR:{storage:Fe(lo(M),Jg),access:\"mutable\"},accumG:{storage:Fe(lo(M),Jg),access:\"mutable\"},accumB:{storage:Fe(lo(M),Jg),access:\"mutable\"},accumW:{storage:Fe(lo(M),Jg),access:\"mutable\"},params:{uniform:GI},src:{texture:Zt(n)},outTex:{storageTexture:zt(UI,\"write-only\")}}),\"fieldLayout\"),$me={luminance:0,luminanceInverted:1,red:2,green:3,blue:4,saturation:5,alpha:6};function Xme(e){return On.relief(ty,{channel:x2.byMode[e]??x2.luminance,cursor:Za.cursorInView(ty,{perspK:OI}),stiffness:Yme,maxSpeed:qme,minAlpha:LI,name:\"particleFieldSim\"})}function Q2(e,t){return Gn.relief(ty,{shape:e,splatRCap:Ume,aaW:Ome,minAlpha:LI,fp:Gme,wobbleFreq:Fme,perspK:OI,out:t,weightedColor:{alphaK:Ime},names:{splat:\"particleFieldSplat\",resolve:\"particleFieldResolve\"}})}var Hme=e=>Q2(e,[Yn,Yn]).splat.kernel,Y2e=Hme(\"dot\"),W2e=Q2(\"dot\",[Yn,Yn]).resolve.kernel,FI=H({name:\"ParticleField\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Explodes the child content into a breathing 3D field of particles \\u2014 bright areas float toward you and dark areas recede, the whole relief can be orbited with the camera rotations, and the cursor physically throws particles that spring back home\",requiresRTT:!0,requiresChild:!0,usesPointer:!0,props:{count:{default:15e3,description:\"Number of particles \\u2014 how finely the child is sampled into the field\",ui:{type:\"range\",min:1e3,max:DI,step:500,label:\"Count\",group:\"Field\"}},depthSource:{default:\"luminance\",compileTime:!0,description:\"Which channel of the child drives each particle's depth toward or away from the camera\",ui:{type:\"select\",options:[{value:\"luminance\",label:\"Luminance\"},{value:\"luminanceInverted\",label:\"Luminance (Inverted)\"},{value:\"red\",label:\"Red\"},{value:\"green\",label:\"Green\"},{value:\"blue\",label:\"Blue\"},{value:\"saturation\",label:\"Saturation\"},{value:\"alpha\",label:\"Alpha\"}],label:\"Depth Source\",group:\"Field\"}},depth:{default:.7,description:\"Depth of the relief \\u2014 how far bright and dark areas rise toward and sink away from the camera\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Depth\",group:\"Field\"}},particleShape:{default:\"dot\",compileTime:!0,description:\"What each particle is drawn as \\u2014 a crisp dot or square, or a soft glow puff\",ui:{type:\"select\",options:cp,label:\"Shape\",group:\"Look\"}},particleSize:{default:.75,description:\"Size of each particle relative to the grid spacing\",ui:{type:[\"range\",\"map\"],min:.3,max:3,step:.01,label:\"Particle Size\",group:\"Look\"}},depthShading:{default:.6,description:\"How much far particles dim into the distance \\u2014 the main cue that sells the 3D relief\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Depth Shading\",group:\"Look\"}},wobble:{default:.35,description:\"Gentle idle drift that keeps the field breathing even on a still image\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Wobble\",group:\"Motion\"}},zoom:{default:1,description:\"Camera distance \\u2014 dolly the whole field closer or further away\",ui:{type:[\"range\",\"map\"],min:.3,max:2.5,step:.01,label:\"Zoom\",group:\"Camera\"}},rotationX:{default:0,description:\"Camera orbit around the horizontal axis \\u2014 tilt the relief toward or away from you\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Rotate X\",group:\"Camera\"}},rotationY:{default:0,description:\"Camera orbit around the vertical axis \\u2014 view the particle extrusion from the side\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Rotate Y\",group:\"Camera\"}},rotationZ:{default:0,description:\"Camera roll around the view axis\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Rotate Z\",group:\"Camera\"}},offsetX:{default:0,description:\"Shift the whole field horizontally across the frame (in screen widths)\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Offset X\",group:\"Camera\"}},offsetY:{default:0,description:\"Shift the whole field vertically across the frame (in screen heights) \\u2014 tilt it flat, then sink it toward the bottom like a ground plane\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Offset Y\",group:\"Camera\"}},cursorMode:{default:\"push\",description:\"How the cursor disturbs the field \\u2014 push throws particles away and toward you, pull gathers them in\",ui:{type:\"select\",options:[{value:\"none\",label:\"None\"},{value:\"push\",label:\"Push\"},{value:\"pull\",label:\"Pull\"}],label:\"Cursor Mode\",group:\"Interaction\"}},cursorStrength:{default:.8,description:\"Strength of the cursor force \\u2014 how hard particles get thrown before springing back\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Cursor Strength\",group:\"Interaction\"}},cursorRadius:{default:.2,description:\"Reach of the cursor field, in screen units\",ui:{type:\"range\",min:.05,max:1,step:.01,label:\"Cursor Radius\",group:\"Interaction\"}}},...Fn({layout:ty,params:GI,maxAgents:ey,countCap:{desktop:DI,mobile:Lme},externalKeys:[\"src\"],childTexture:{externalKey:\"src\"},output:{key:\"outTex\",name:\"field\",size:[Yn,Yn],format:UI},bake:e=>{let{getCpuValue:t,dimensions:s}=e,r=s.height>0?s.width/s.height:1,o=Yn,a=Yn;r>=1?a=Math.max(Math.round(Yn/r),1):o=Math.max(Math.round(Yn*r),1);let i=$me[t(\"depthSource\")||\"luminance\"]??0,l=Q2(t(\"particleShape\")||\"dot\",[o,a]);return{pipelines:{sim:{kernel:Xme(i),threads:\"grid\"},splat:l.splat,resolve:l.resolve},program:[\"sim\",\"splat\",\"resolve\"],outputSize:[o,a],setup:{outW:o,outH:a,aspect0:r}}},frame:(e,{getCpuValue:t},s)=>{let{outW:r,outH:o,aspect0:a}=s,i=Li(t),l=0,c=0,d=bt;return u=>{let p=Di(u,a),_=p.dt;l+=_;let f=p.aspect,h=e.resolveCount(i(\"count\",15e3),500),{w:g,h:y}=aO(h,f,8,K2),b=t(\"cursorMode\")||\"push\",x=b===\"pull\"?-1:1,P=b===\"none\"?0:i(\"cursorStrength\",.5),C=i(\"cursorRadius\",.22),E=nO(i(\"rotationX\",0)*d,i(\"rotationY\",0)*d,i(\"rotationZ\",0)*d);return e.writeParams({rowX:S(E.rowX[0],E.rowX[1],E.rowX[2],0),rowY:S(E.rowY[0],E.rowY[1],E.rowY[2],0),rowZ:S(E.rowZ[0],E.rowZ[1],E.rowZ[2],0),dt:_,time:l,snap:c===0?1:0,gridW:g,gridH:y,outW:r,outH:o,depth:i(\"depth\",.7),dragMul:Sp(Wme,_),wobbleAmp:i(\"wobble\",.35),depthShading:Math.min(Math.max(i(\"depthShading\",.6),0),1),spacing:Math.min(r/g,o/y),particleSize:i(\"particleSize\",1.3),aspect:f,zoom:Math.min(Math.max(i(\"zoom\",1),.05),10),transX:i(\"offsetX\",0),transY:i(\"offsetY\",0),pointerX:p.pointerX,pointerY:p.pointerY,cursorForce:x*P*Vme,cursorZForce:x*P*Nme,cursorRadSq:C*C}),c++,e.frame({count:h,grid:[g,y]})}},fragment:{output:\"field\",fallback:\"child\"}})});var ka=256,Rc=ka*ka,jme=vn({desktop:10,mobile:6}),qI=\"rgba16float\",kl=vn({desktop:1024,mobile:512}),VI=\"rgba16float\",sy=16384,Kme=6e3,Qme=14,II=.16,Zme=16,YI=.16*ka,WI=4,Jme=.05,e_e=26,t_e=2.4,s_e=.09,r_e=6,o_e=.35,a_e=1.8,n_e=3,NI=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,curlStrength:n,velFade:n,ambient:n,ambientTime:n,ambientFreq:n}),\"FlowParams\"),$I=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({posX:n,posY:n,velX:n,velY:n,radius:n}),\"SplatParams\"),XI=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({colA:S,colB:S,dt:n,aspect:n,trails:n,bodyR:n,speedNorm:n,advect:n}),\"SimParams\"),Z2=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,Rc),access:\"mutable\"},velB:{storage:Fe(S,Rc),access:\"mutable\"},pressure:{storage:Fe(n,Rc),access:\"mutable\"},divergence:{storage:Fe(n,Rc),access:\"mutable\"},params:{uniform:NI},sparams:{uniform:$I},velOutTex:{storageTexture:zt(qI,\"write-only\")}}),\"fluidLayout\"),ry=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({agents:{storage:Fe(S,sy),access:\"mutable\"},accumE:{storage:Fe(lo(M),kl*kl),access:\"mutable\"},accumS:{storage:Fe(lo(M),kl*kl),access:\"mutable\"},trailBuf:{storage:Fe(S,kl*kl),access:\"mutable\"},velTex:{texture:Zt(n)},params:{uniform:XI},outTex:{storageTexture:zt(VI,\"write-only\")}}),\"pfLayout\"),i_e=Tz(Z2,{n:ka,namePrefix:\"particleFlow\"}),J2=zn(Z2,{n:ka,namePrefix:\"particleFlow\",boundary:\"clamped\",dye:\"none\",publishVelocityTexture:!0,ambientWind:{gain:e_e}}),{curl:$2e,divergence:X2e,jacobi:H2e,gradSubtract:j2e,advectVel:K2e,copyVel:Q2e}=J2,l_e=J2.vorticity,c_e=eg(ry,\"particleFlowInit\"),d_e=On.advect2d(ry,{gridN:ka,inertiaRate:r_e,homeRate:a_e,name:\"particleFlowIntegrate\"});function e5(e,t,s){return Gn.orientedWorld(ry,{shape:e,res:kl,splatRCap:Qme,heading:\"velocity\",agitation:\"speed\",comet:!0,ramp:{colorSpace:t,trails:{baked:s}},names:{splat:\"particleFlowSplat\",resolve:\"particleFlowResolve\"}})}var u_e=e=>e5(e,2,!1).splat.kernel,p_e=(e,t)=>e5(\"streak\",e,t).resolve.kernel,Z2e=u_e(\"streak\"),J2e=p_e(2,!1),HI=H({name:\"ParticleFlow\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Thousands of drifting dust particles carried by a real incompressible fluid field the cursor stirs \\u2014 drag to plow a wake and the particles ride the eddies and vortices it leaves behind, while a gentle ambient breeze keeps the whole scene breathing on its own\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{colorA:{default:\"#8ec5ff\",transform:ce,description:\"Color of particles drifting slowly with the flow\",ui:{type:\"color\",label:\"Rest Color\",group:\"Colors\"}},colorB:{default:\"#ffd98e\",transform:ce,description:\"Color particles flash toward as they speed up in the fast jets and wakes\",ui:{type:\"color\",label:\"Excited Color\",group:\"Colors\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the rest\\u2192excited color ramp\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},shape:{default:\"streak\",compileTime:!0,description:\"What each particle is drawn as \\u2014 a streak or arrow that stretches along the flow, a plain dot, a square, or a soft glowing comet\",ui:{type:\"select\",options:lp,label:\"Shape\",group:\"Particles\"}},count:{default:6e3,description:\"Number of drifting particles\",ui:{type:\"range\",min:1e3,max:16e3,step:500,label:\"Count\",group:\"Particles\"}},size:{default:1.2,description:\"Size of each particle\",ui:{type:\"range\",min:.3,max:3,step:.05,label:\"Size\",group:\"Particles\"}},trails:{default:0,recompile:hn((e,t)=>e>0!=t>0),description:\"How long each particle's motion trail persists \\u2014 0 draws crisp dust, 1 leaves long flowing ribbons\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Trails\",group:\"Particles\"}},speed:{default:1,description:\"How strongly the fluid carries the particles \\u2014 higher makes the dust ride the flow faster\",ui:{type:\"range\",min:.2,max:3,step:.05,label:\"Flow Speed\",group:\"Particles\"}},force:{default:1,description:\"How hard the cursor's motion pushes the fluid \\u2014 higher plows faster, more violent wakes and jets\",ui:{type:\"range\",min:0,max:3,step:.05,label:\"Cursor Force\",group:\"Dynamics\"}},swirl:{default:25,description:\"Vorticity confinement \\u2014 swirl energy that spins the flow into persistent eddies and vortices the dust orbits\",ui:{type:\"range\",min:0,max:60,step:1,label:\"Swirl\",group:\"Dynamics\"}},momentum:{default:.6,description:\"How long the fluid keeps flowing after you stop dragging \\u2014 high momentum lets the eddies carry the dust on and on\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Momentum\",group:\"Dynamics\"}},ambient:{default:.15,description:\"A gentle, ever-evolving breeze that keeps the dust drifting even without the cursor \\u2014 set to 0 for a field that only moves when you stir it\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Ambient\",group:\"Dynamics\"}}},...Fn({layout:ry,params:XI,maxAgents:sy,countCap:{desktop:sy,mobile:Kme},output:{key:\"outTex\",name:\"flowTexture\",size:[kl,kl],format:VI},externalKeys:[\"velTex\"],bake:e=>{let{gpu:t,getCpuValue:s,onCleanup:r}=e,o=t.root,a=(globalThis.__TYPEGPU_AUTONAME__??(h=>h))(o.createTexture({size:[ka,ka],format:qI}).$usage(\"storage\",\"sampled\"),\"velTex\");r(()=>a.destroy());let i=(globalThis.__TYPEGPU_AUTONAME__??(h=>h))(o.createUniform(NI),\"paramsU\"),l=(globalThis.__TYPEGPU_AUTONAME__??(h=>h))(o.createUniform($I),\"splatU\"),c=o.createBindGroup(Z2,{velA:pr(o,S,Rc),velB:pr(o,S,Rc),pressure:pr(o,n,Rc),divergence:pr(o,n,Rc),params:i.buffer,sparams:l.buffer,velOutTex:a}),d=D_(o,i_e,{n:ka,bindGroup:c}),u=D_(o,l_e,{n:ka,bindGroup:c}),p=jA(o,J2,{n:ka,bindGroup:c}),_=s(\"trails\"),f=e5(s(\"shape\")||\"streak\",s(\"colorSpace\")??2,typeof _==\"number\"&&_>0);return{pipelines:{init:{kernel:c_e,threads:\"max\"},integrate:{kernel:d_e,threads:\"agents\"},splat:f.splat,resolve:f.resolve},program:[\"integrate\",\"splat\",\"resolve\"],bindNow:{velTex:a},setup:{force:d,vorticity:u,solver:p,paramsU:i,splatU:l}}},frame:(e,{getCpuValue:t},s)=>{let{force:r,vorticity:o,solver:a,paramsU:i,splatU:l}=s,c=Date.now(),d=0,u=!1,p=Sl(),_=lA({warmupFrames:n_e+1}),f=(h,g)=>{let y=t(h);return typeof y==\"number\"?y:g};return h=>{let g=h,y=Date.now(),b=Math.min(g.deltaTime??(y-c)/1e3,.033);if(c=y,b<.001)return null;let x=g.dimensions?.height??0,P=x>0?(g.dimensions?.width??0)/x:16/9,C=Math.max(P,.01),E=f(\"force\",1),R=f(\"swirl\",25),U=Math.min(Math.max(f(\"ambient\",.15),0),1),L=Math.min(Math.max(f(\"momentum\",.6),0),1),k=f(\"speed\",1),D=e.resolveCount(f(\"count\",6e3),100),O=Math.min(Math.max(f(\"size\",1.2),.3),3)*1.3/Ri,F=p.update(g.pointer,b),q=F.moving||U>.001;if(q&&_.markActive(),d+=b*s_e,u&&_.shouldSkip(2+L*13,q))return null;_.tickFrame(b);let Y=WI*Math.pow(Jme/WI,L);i.write({dt:b,curlStrength:R,velFade:Y,ambient:U,ambientTime:d,ambientFreq:t_e});let V=t(\"colorA\"),X=t(\"colorB\");e.writeParams({colA:S(V?.x??.56,V?.y??.77,V?.z??1,V?.w??1),colB:S(X?.x??1,X?.y??.85,X?.z??.56,X?.w??1),dt:b,aspect:C,trails:Math.min(Math.max(f(\"trails\",0),0),1)*.92,bodyR:O,speedNorm:o_e,advect:k});let Q=[];if(u||(u=!0,Q.push(()=>e.pipelines.init.dispatchThreads(sy))),F.moving){let te=F.velX*ka*E*II,J=F.velY*ka*E*II;cA(Q,{fromX:F.prevX,fromY:F.prevY,dx:F.dx,dy:F.dy,dragDist:F.dragDist,stepSize:Math.max(.004,YI/ka*.6),maxSteps:Zme,scale:ka,write:(oe,le)=>l.write({posX:oe,posY:le,velX:te,velY:J,radius:YI}),pass:r})}return Q.push(...a.solveSteps({jacobiIters:jme,vorticity:o})),Q.push(...e.frame({count:D})??[]),Q}},fragment:{output:\"flowTexture\",fallback:\"transparent\"}})});var{buildAnalyticSdfFn:m_e,createSdfDataTexture:__e,SVG_SDF_SIZE:f_e}=yc,{createAnalytic3dSdfSetup:v_e,createSvg3dSdfSetup:h_e,is3dShapeType:g_e,isSvg3dShapeType:t5,resolveShapeSubProp:y_e,SHAPE3D_DEFAULTS:b_e,SVG3D_DEFAULTS:jI,VOLUMETRIC_FIELD_FORMAT:KI}=kn,J_=bt,x_e=JSON.stringify({type:\"sphere3D\",radius:.35});function ZI(e){if(e&&typeof e==\"object\")return e;if(typeof e==\"string\")try{return JSON.parse(e)}catch{return{}}return{}}var ef=16e3,tf=32,A_e=tf*tf*tf,oy=1,Dc=vn({desktop:1024,mobile:768}),w_e=Dc/Ri,T_e=2.6,S_e=6,JI=3,e9=\"rgba16float\",t9=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({colA:S,colB:S,dt:n,time:n,spread:n,agitation:n,dragMul:n,gravX:n,gravY:n,halfDepth:n,cursorX:n,cursorY:n,cursorForce:n,cursorRadSq:n,saRadius:n,saSides:n,saRounding:n,saInnerRatio:n,saRotation:n,saHeight:n,saOffset:n,saAperture:n,centerX:n,centerYv:n,scale:n,rotC:n,rotS:n,aspect:n,size:n,exposure:n,softness:n,speedColorK:n,omegaX:n,omegaY:n,omegaZ:n,entrain:n,gridOffX:n,gridOffY:n,gridOffZ:n}),\"SimParams\"),li=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({pos:{storage:Fe(S,ef),access:\"mutable\"},vel:{storage:Fe(S,ef),access:\"mutable\"},dens:{storage:Fe(lo(M),A_e),access:\"mutable\"},accumE:{storage:Fe(lo(M),Dc*Dc),access:\"mutable\"},accumS:{storage:Fe(lo(M),Dc*Dc),access:\"mutable\"},params:{uniform:t9},outTex:{storageTexture:zt(e9,\"write-only\")}}),\"simLayout\"),QI=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({sdfSource:{texture:Zt(n)}}),\"svgSdfLayout\"),P_e=uz(li,\"particlesInit\"),zp=Za.pressure(li,{gridDim:tf,domain:oy,names:{clear:\"particlesClearDensity\",splat:\"particlesDensity\"}}),r5e=zp.clearKernel,o5e=zp.splatKernel;function C_e(e){return On.forces3d(li,{forces:[zp.force,Za.containment(li,{field:e,gradEps:.02,wall:{featherFrom:-.1,featherTo:.01,base:1.1,springK:22},recall:{from:oy*.85,to:oy*1.1,k:9},homing:{from:.12,to:.45,k:5},entrainment:{featherHalf:.05}}),Za.cursorXY(li),Za.gravity(li),Za.turbulence(li,{posScale:193,timeX:31.7,timeY:27.3,seedScale:997,gain:5})],maxSpeed:JI,posClamp:1.9,name:\"particlesUpdate\"})}function s9(e,t){return Gn.volume(li,{shape:e,outRes:Dc,maxSplatSize:S_e,extQ:T_e,ramp:{colorSpace:t,exposure:!0,trails:\"none\"},names:{splat:\"particlesSplat\",glow:\"particlesGlowProfile\",profile:\"particlesProfile\",resolve:\"particlesResolve\"},extraOnSplat:!0})}var M_e=e=>s9(e,0).splat.kernel;var a5e=M_e(\"dot\");function E_e(e,t,s){let{gpu:r,getCpuValue:o,onCleanup:a}=e,i=r.root;if(t&&t5(s)){let c=h_e(e,t,()=>o(\"shape\")),d=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(i.createTexture({size:[1,1],format:KI}).$usage(\"storage\"),\"dummyField\");a(()=>d.destroy());let u=i.createBindGroup(c.layout,{field:d,params:c.marchParamsBuffer.buffer,sdfSource:c.sdfSourceTexture});return{evalSdf:X_.svgLifted3d(c.layout,c.sdfFn),extraBindGroup:u,setup3d:c}}if(t){let c=__e(e,t),d=i.createBindGroup(QI,{sdfSource:c.texture.texture});return{evalSdf:X_.svgExtruded(li,QI,{size:f_e}),extraBindGroup:d,setup3d:null}}if(g_e(s)){let c=v_e(i,s,ZI(o(\"shape\")),()=>o(\"shape\")),d=(globalThis.__TYPEGPU_AUTONAME__??(p=>p))(i.createTexture({size:[1,1],format:KI}).$usage(\"storage\"),\"dummyField\");a(()=>d.destroy());let u=i.createBindGroup(c.layout,{field:d,params:c.marchParamsBuffer.buffer});return{evalSdf:X_.analytic3d(c.layout,c.sdfFn),extraBindGroup:u,setup3d:c}}let l=m_e(s);return{evalSdf:X_.analyticExtruded(li,l),extraBindGroup:null,setup3d:null}}var r9=H({name:\"Particles\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"A swarm of simulated particles that settles into the shape, filling it evenly \\u2014 flat, SVG or true 3D volumes \\u2014 scattering from the cursor (or chasing it), tumbling when the shape moves, and always drifting back home\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},colorA:{default:\"#8ec5ff\",transform:ce,description:\"Color of particles at rest\",ui:{type:\"color\",label:\"Rest Color\",group:\"Colors\"}},colorB:{default:\"#ff7ad9\",transform:ce,description:\"Color particles shift toward when agitated \\u2014 scattering from the cursor or catching up to a moving shape\",ui:{type:\"color\",label:\"Excited Color\",group:\"Colors\"}},count:{default:4e3,description:\"Number of simulated particles\",ui:{type:\"range\",min:1e3,max:ef,step:1e3,label:\"Count\",group:\"Particles\"}},size:{default:1.5,description:\"Base particle size (each particle also varies slightly, and grows as it drifts toward the viewer)\",ui:{type:\"range\",min:.6,max:6,step:.1,label:\"Size\",group:\"Particles\"}},particleShape:{default:\"dot\",compileTime:!0,description:\"What each particle is drawn as \\u2014 a crisp dot or square (softness still feathers them), or a soft glow puff\",ui:{type:\"select\",options:cp,label:\"Shape\",group:\"Particles\"}},spread:{default:1,description:\"The pressure pushing particles apart \\u2014 how strongly they insist on even spacing inside the shape\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Spread\",group:\"Physics\"}},agitation:{default:.12,description:\"Idle thermal motion of the swarm \\u2014 0 freezes it crystal-still once settled\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Agitation\",group:\"Physics\"}},damping:{default:.4,description:\"How quickly disturbed particles calm back down and settle into place\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Damping\",group:\"Physics\"}},gravity:{default:0,description:\"Downward pull on the swarm \\u2014 negative floats the particles upward\",ui:{type:\"range\",min:-2,max:2,step:.01,label:\"Gravity\",group:\"Physics\"}},mouseInfluence:{default:2,description:\"Strength of the cursor field. Positive scatters particles away; negative pulls the swarm toward the cursor\",ui:{type:\"range\",min:-5,max:5,step:.01,label:\"Cursor Force\",group:\"Interaction\"}},mouseRadius:{default:.2,description:\"Reach of the cursor field, in shape-local units\",ui:{type:\"range\",min:.05,max:.8,step:.01,label:\"Cursor Radius\",group:\"Interaction\"}},exposure:{default:1,description:\"Brightness of the additive particle glow\",ui:{type:\"range\",min:.2,max:3,step:.01,label:\"Exposure\",group:\"Particles\"}},softness:{default:.1,description:\"Edge softness of each particle \\u2014 0 is a crisp disc, 1 a soft glow puff\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Softness\",group:\"Particles\"}},depth:{default:.18,description:\"Thickness of the particle volume behind flat shapes (3D shapes use their own true depth)\",ui:{type:\"range\",min:.02,max:.6,step:.01,label:\"Depth\",group:\"Particles\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the rest\\u2192excited color ramp\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},shape:{default:x_e,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Fn({layout:li,params:t9,maxAgents:ef,countCap:{desktop:ef,mobile:6e3},output:{key:\"outTex\",name:\"particleTexture\",size:[Dc,Dc],format:e9},bake:e=>{let{getCpuValue:t}=e,s=t(\"shapeSdfUrl\")||\"\",r=bc(t(\"shapeType\")||\"\",t(\"shape\")),o=E_e(e,s,r),a=s9(t(\"particleShape\")||\"dot\",t(\"colorSpace\")??0);return{pipelines:{init:{kernel:P_e,threads:\"max\",extra:!0},clearDensity:{kernel:zp.clearKernel,threads:\"fixed\",size:[zp.cells]},density:{kernel:zp.splatKernel,threads:\"agents\",extra:!0},update:{kernel:C_e(o.evalSdf),threads:\"agents\",extra:!0},splat:a.splat,resolve:a.resolve},initStep:\"init\",program:[\"clearDensity\",\"density\",\"update\",\"splat\",\"resolve\"],extraBindGroup:o.extraBindGroup,setup:{shapeField:o,shapeSdfUrl:s,shapeType:r}}},frame:(e,{getCpuValue:t},s)=>{let{shapeField:r,shapeSdfUrl:o,shapeType:a}=s,i=r.setup3d,l=0,c=new Map,d=(g,y,b,x)=>y_e(g,y,b,x),u=0,p=0,_=null,f=2*oy/tf,h=Li(t);return g=>{let{dt:y,aspect:b,pointerX:x,pointerY:P}=Di(g,1);l+=y,u+=y;let C={elapsed:l,deltaTime:y,pointerX:x,pointerY:P,springs:c},E=t(\"center\"),R=E?.x??.5,U=1-(E?.y??.5),L=Math.max(h(\"scale\",1),.01),k=h(\"rotation\",0)*J_,D=Math.cos(k),O=Math.sin(k),F=tO({pointerX:x,pointerY:P,centerX:R,centerYv:U,scale:L,rotC:D,rotS:O,aspect:b}),q=h(\"gravity\",0)*1.2,Y=O*q,V=-(D*q),X=ZI(t(\"shape\")),Q=t5(a),te=o&&Q?d(X.depth,jI.depth??.25,C,\"depth\")*.5:Math.max(h(\"depth\",.18),.01)*.5,J=d(X.rotation,0,C,\"shapeRotation\"),oe={x:0,y:0,z:0};if(i){let Ie=t5(a)?jI:b_e[a]??{},ke={x:d(X.rotX,Ie.rotX??0,C,\"rotX\")*J_,y:d(X.rotY,Ie.rotY??0,C,\"rotY\")*J_,z:d(X.rotZ,Ie.rotZ??0,C,\"rotZ\")*J_};oe=T2(_,ke,y)??oe,_=ke}else if(!o){let Ie={x:0,y:0,z:J*J_};oe=T2(_,Ie,y)??oe,_=Ie}let{omegaX:le,omegaY:se,omegaZ:Te,entrain:de}=sO(oe,{cap:10,gainRate:2.5,gainMax:5}),xe=h(\"damping\",.4),Ae=h(\"mouseRadius\",.22),De=t(\"colorA\"),Ee=t(\"colorB\"),ze=iO(p,f);return e.writeParams({colA:S(De?.x??1,De?.y??1,De?.z??1,De?.w??1),colB:S(Ee?.x??1,Ee?.y??1,Ee?.z??1,Ee?.w??1),dt:y,time:u,spread:h(\"spread\",1),agitation:h(\"agitation\",.12),dragMul:Sp(2.2+xe*6,y),gravX:Y,gravY:V,halfDepth:te,cursorX:F.x,cursorY:F.y,cursorForce:h(\"mouseInfluence\",1.2)*3,cursorRadSq:Ae*Ae,saRadius:d(X.radius??X.width??X.bottomWidth,.35,C,\"radius\"),saSides:d(X.sides,6,C,\"sides\"),saRounding:d(X.rounding,0,C,\"rounding\"),saInnerRatio:d(X.innerRatio??X.thickness??X.spread??X.topWidth??X.topRatio,.4,C,\"innerRatio\"),saRotation:J,saHeight:d(X.height,.25,C,\"height\"),saOffset:d(X.offset??X.skew,.2,C,\"offset\"),saAperture:d(X.aperture,270,C,\"aperture\"),centerX:R,centerYv:U,scale:L,rotC:D,rotS:O,aspect:b,size:h(\"size\",2.2)*w_e,exposure:h(\"exposure\",1),softness:Math.min(Math.max(h(\"softness\",.5),0),1),speedColorK:2/JI,omegaX:le,omegaY:se,omegaZ:Te,entrain:de,gridOffX:ze.x,gridOffY:ze.y,gridOffZ:ze.z}),p++,i?.update({deltaTime:y,pointer:{x,y:P}}),e.frame({count:e.resolveCount(h(\"count\",4e3))})}},fragment:{output:\"particleTexture\",fallback:\"transparent\"}})});var o9=H({name:\"PerlinNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Smooth gradient noise that morphs over time\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:2,description:\"Pattern scale (higher = finer, more detailed patterns; each +1 roughly doubles the frequency)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},...Ja(),...In()},paint:Vs(ua(Qa(hp(Tc(\"perlin\"),{rate:.3}),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var a9=H({name:\"Perspective\",role:\"warp\",category:\"Distortions\",description:\"Rotate the plane in 3D space with pan and tilt\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"Center point of rotation\"),pan:{default:0,description:\"Horizontal rotation (left/right)\",ui:{type:[\"range\",\"map\"],min:-90,max:90,step:.1,label:\"Pan\",group:\"Effect\"}},tilt:{default:0,description:\"Vertical rotation (up/down)\",ui:{type:[\"range\",\"map\"],min:-90,max:90,step:.1,label:\"Tilt\",group:\"Effect\"}},fov:{default:60,description:\"Field of view - controls perspective intensity\",ui:{type:[\"range\",\"map\"],min:30,max:120,step:1,label:\"FOV\",group:\"Effect\"}},zoom:{default:1,description:\"Zoom in to fill the frame after rotation\",ui:{type:[\"range\",\"map\"],min:.5,max:3,step:.1,label:\"Zoom\",group:\"Effect\"}},offset:{default:{x:.5,y:.5},transform:Xe,description:\"Shift the result in X/Y\",ui:{type:\"position\",label:\"Offset\",group:\"Position\"}},edges:fr(\"transparent\",\"How to handle edges\")},map:tU({center:T(\"center\"),pan:T(\"pan\"),tilt:T(\"tilt\"),fov:T(\"fov\"),zoom:T(\"zoom\"),offset:T(\"offset\")})});var n9=H({name:\"Pixelate\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Pixelation effect with adjustable cell size\",props:{scale:{default:50,description:\"Number of pixels along the longest edge (higher = smaller pixels)\",ui:{type:[\"range\",\"map\"],min:1,max:200,step:1,label:\"Scale\",group:\"Effect\"}},gap:{default:0,description:\"Space between pixels as a fraction of cell size (0 = no gap, 1 = fully invisible)\",ui:{type:[\"range\",\"map\"],min:0,max:.95,step:.01,label:\"Gap\",group:\"Effect\"}},roundness:{default:0,description:\"Roundness of each pixel's corners (0 = square, 1 = circle)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Roundness\",group:\"Effect\"}}},effect:AU({scale:T(\"scale\"),gap:T(\"gap\"),roundness:T(\"roundness\")})});var Yd=512,s5=Yd*Yd,i9=\"rgba16float\",k_e=(e,t)=>Gz({n:Yd,format:i9,namePrefix:\"pixelSort\",vertical:e,direction:t}),l9=H({name:\"PixelSort\",role:\"simulation\",species:\"custom\",category:\"Interactive\",description:\"Pixels sort by brightness around the cursor and keep their sorted position, optionally decaying back over time\",requiresRTT:!0,requiresChild:!0,boundingBoxDeclaration:{aspectRatio:null},usesPointer:!0,props:{radius:{default:.4,description:\"Size of the cursor brush that sorts pixels\",ui:{type:[\"range\",\"map\"],min:.03,max:.6,step:.01,label:\"Radius\",group:\"Effect\"}},falloff:{default:1,description:\"Softness of the brush edge (0 = hard circle, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Effect\"}},strength:{default:.1,description:\"How fast pixels sort \\u2014 runs more sorting passes per frame\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},decay:{default:.1,description:\"How quickly sorted pixels melt back to their original position (0 = permanent)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Decay\",group:\"Effect\"}},axis:{default:\"vertical\",compileTime:!0,description:\"Axis pixels are sorted along\",ui:{type:\"select\",options:[{label:\"Horizontal\",value:\"horizontal\"},{label:\"Vertical\",value:\"vertical\"}],label:\"Axis\",group:\"Effect\"}},direction:{default:\"descending\",compileTime:!0,description:\"Sort order by brightness\",ui:{type:\"select\",options:[{label:\"Dark \\u2192 Light\",value:\"ascending\"},{label:\"Light \\u2192 Dark\",value:\"descending\"}],label:\"Direction\",group:\"Effect\"}}},...Ec((e,t)=>{let{childNode:s,convertToTexture:r,registerComputeTexture:o,propValues:a,onCleanup:i}=e;if(!s)return null;let l=a.axis===\"vertical\",c=a.direction===\"descending\"?-1:1,d=r(s),u=pr(t,n,s5),p=pr(t,n,s5),_=pr(t,n,s5),f=t.createTexture({size:[Yd,Yd],format:i9}).$usage(\"storage\",\"sampled\");i(()=>f.destroy());let h=o(f),g=k_e(l,c),y=t.createUniform(g.LumaParams),b=t.createUniform(g.SwapParams),x=Eg(e),P=[Yd,Yd],C=ai(t,g.key,{size:P}),E=!1,R=t.createBindGroup(g.swapLayout,{readBuf:u,writeBuf:p,lumaBuf:_,params:b.buffer}),U=t.createBindGroup(g.swapLayout,{readBuf:p,writeBuf:u,lumaBuf:_,params:b.buffer}),L=ai(t,g.swap0,{size:P}),k=ai(t,g.swap1,{size:P}),D=t.createBindGroup(g.outputLayout,{srcBuf:u,stateTex:f}),O=t.createBindGroup(g.outputLayout,{srcBuf:p,stateTex:f}),F=ai(t,g.output,{size:P}),q=0,Y=1,V=Ir.sortPass({passes:()=>Y,pass:(X,Q)=>(X?k:L).with(Q===\"A\"?R:U)});return{outputs:{state:h,childTexture:d},bindInputs:X=>{let Q=X(d.key);if(Q){let te=t.createBindGroup(g.lumaLayout,{input:Q.texture,lumaBuf:_,params:y.buffer});C=C.with(te),E=!0}},clampDt:.05,stages:[Ir.readyWhen(()=>E),Ir.values(\"brush+decay\",X=>{let Q=X.frameParams,te=x.safe(Q),J=x.tracked(),oe=X.num(\"strength\",.1),le=X.num(\"decay\",.1);Y=1+Math.round(oe*4),b.write({mouseX:Q.pointer?.x??.5,mouseY:Q.pointer?.y??.5,radius:X.num(\"radius\",.4),falloff:X.num(\"falloff\",1),decay:le*X.dt*2/Y,aspect:te.width/te.height,seed:q%1024}),y.write({inputWidth:J.width,inputHeight:J.height})}),Ir.cache(()=>C),V,Ir.publish(()=>F.with(V.side()===\"A\"?D:O)),Ir.values(\"frameAdvance\",()=>{q++})]}}),gpu:{fragment:({childNode:e,ctx:t,computeOutputs:s,propValues:r,convertToTexture:o})=>{if(!e)return xt;let a=s?.state,i=s?.childTexture;if(!a||!i)return W(Yt,\"unpremultiplyAlpha\",[o(e).sample(t.uv)]);let l=r.axis===\"vertical\",c=a.sample(t.uv,\"nearestClamp\").member(\"r\"),d=l?rt(t.uv.member(\"x\"),c):rt(c,t.uv.member(\"y\"));return W(Yt,\"unpremultiplyAlpha\",[i.sample(d)])}}});var c9=e=>({luminance:0,darkness:1,red:2,green:3,blue:4})[e]??0,vr=128,ay=(e,t)=>typeof e==\"number\"?e:t,d9=(e,t,s)=>ne(e,v(t,s)),{Taps:y5e,setup:B_e}=w4(vr,{fnName:\"pixelThrowBicubicSetup\",structName:\"PixelThrowBicubicTaps\"});function z_e(e,t,s){let r=Qe(.299,.587,.114);return et(1,Ge(e===1?ne(1,$t(t,r)):e===2?t.member(\"r\"):e===3?t.member(\"g\"):e===4?t.member(\"b\"):$t(t,r),0,1),s)}var u9=H({name:\"PixelThrow\",role:\"simulation\",species:\"custom\",category:\"Interactive\",description:\"Throws pixels along the cursor's path like a fluid \\u2014 brighter (or redder/greener/bluer) pixels are flung farther, then settle back with friction\",requiresRTT:!0,requiresChild:!0,boundingBoxDeclaration:{aspectRatio:null},usesPointer:!0,props:{throwKey:{default:\"luminance\",transform:c9,compileTime:!0,description:\"Which pixel value decides how far a pixel is thrown\",ui:{type:\"select\",options:[{label:\"Brightness\",value:\"luminance\"},{label:\"Darkness\",value:\"darkness\"},{label:\"Red\",value:\"red\"},{label:\"Green\",value:\"green\"},{label:\"Blue\",value:\"blue\"}],label:\"Throw By\",group:\"Effect\"}},strength:{default:.25,description:\"Maximum distance pixels can be thrown\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},keyInfluence:{default:.8,description:\"How much the chosen value modulates throw distance (0 = all pixels thrown equally, 1 = only high-value pixels move)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Key Influence\",group:\"Effect\"}},radius:{default:.2,description:\"Size of the cursor's throw brush\",ui:{type:[\"range\",\"map\"],min:.02,max:.6,step:.01,label:\"Radius\",group:\"Effect\"}},friction:{default:.3,description:\"How quickly thrown pixels settle back to their origin (0 = float almost forever, 1 = snap back fast)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Friction\",group:\"Effect\"}},momentum:{default:.5,description:\"How much the thrown flow keeps drifting and swirling after the cursor passes (fluidity)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Momentum\",group:\"Effect\"}},edges:{default:\"stretch\",description:\"How to handle pixels thrown beyond the canvas edges\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},gpu:{fragment:e=>{let{uniforms:t,ctx:s,childNode:r,convertToTexture:o,onBeforeRender:a,dimensions:i,propValues:l,getCpuValue:c}=e;if(!r)return xt;let d=o(r),u=l.edges??0,p=l.throwKey,_=typeof p==\"number\"?p:c9(String(p??\"luminance\")),f=new Float32Array(vr*vr*2),h=new Float32Array(vr*vr*2),g=Bg(e,{size:vr,label:\"pixelthrow-flow\"}),y=g.texture,b=.5,x=.5,P=0,C=0,E=0,R=.125,U=(Y,V,X)=>{let Q=Math.floor(Y),te=Math.floor(V),J=Q+1,oe=te+1;if(Q<0||te<0||J>=vr||oe>=vr)return 0;let le=Y-Q,se=V-te,Te=(te*vr+Q)*2+X,de=(te*vr+J)*2+X,xe=(oe*vr+Q)*2+X,Ae=(oe*vr+J)*2+X;return f[Te]*(1-le)*(1-se)+f[de]*le*(1-se)+f[xe]*(1-le)*se+f[Ae]*le*se},L=()=>Math.max(1e-4,i.width/Math.max(1,i.height));a(kg({clampDt:.016,steps:[gn(\"dtGuard\",Y=>Y.dt<=0?\"skip\":void 0),gn(\"cursorVelocity\",Y=>{let V=(Y.pointer.x-b)/Y.dt,X=(Y.pointer.y-x)/Y.dt;P=P*.8+V*.2,C=C*.8+X*.2,E=Math.sqrt(P*P+C*C)}),gn(\"advect+decay\",Y=>{let V=ay(c(\"friction\"),.3),X=ay(c(\"momentum\"),.5);R=ay(c(\"strength\"),.25)*.5;let Q=Math.max(0,1-V*Y.dt*4),te=X*Y.dt*12;for(let J=0;J<vr;J++)for(let oe=0;oe<vr;oe++){let le=(J*vr+oe)*2,se=oe-f[le]*vr*te,Te=J-f[le+1]*vr*te;h[le]=U(se,Te,0)*Q,h[le+1]=U(se,Te,1)*Q}}),gn(\"inject(brush)\",Y=>{if(E<=.02)return;let V=L(),X=Math.max(.001,ay(c(\"radius\"),.2)),Q=X*X,te=Math.exp(-1),J=1/(1-te),oe=Math.max(0,Math.floor((Y.pointer.y-X)*vr)),le=Math.min(vr-1,Math.ceil((Y.pointer.y+X)*vr)),se=Math.max(0,Math.floor((Y.pointer.x-X)*vr)),Te=Math.min(vr-1,Math.ceil((Y.pointer.x+X)*vr));for(let de=oe;de<=le;de++)for(let xe=se;xe<=Te;xe++){let Ae=(xe+.5)/vr,De=(de+.5)/vr,Ee=V>=1?(Ae-Y.pointer.x)*V:Ae-Y.pointer.x,ze=V>=1?De-Y.pointer.y:(De-Y.pointer.y)/V,Ie=Ee*Ee+ze*ze;if(Ie>Q)continue;let ke=(Math.exp(-Ie/Q)-te)*J,Me=(de*vr+xe)*2;h[Me]+=P*ke*Y.dt*4,h[Me+1]+=C*ke*Y.dt*4}}),gn(\"saturate(tanh)\",()=>{for(let Y=0;Y<vr*vr;Y++){let V=h[Y*2],X=h[Y*2+1],Q=Math.sqrt(V*V+X*X);if(Q>1e-6){let te=R*Math.tanh(Q/R)/Q;h[Y*2]=V*te,h[Y*2+1]=X*te}}}),gn(\"publish(halfFloat)\",Y=>{f.set(h);for(let V=0;V<vr*vr;V++)g.texData[V*4]=_o(f[V*2]),g.texData[V*4+1]=_o(f[V*2+1]);g.upload(),b=Y.pointer.x,x=Y.pointer.y})]}));let k=s.uv,D=W(B_e,\"pixelThrowBicubicSetup\",[k]),O=D.member(\"w\"),F=z(y.sample(D.member(\"uvA\"),\"linearClamp\").member(\"xy\").mul(O.member(\"x\")).add(y.sample(D.member(\"uvB\"),\"linearClamp\").member(\"xy\").mul(O.member(\"y\"))).add(y.sample(D.member(\"uvC\"),\"linearClamp\").member(\"xy\").mul(O.member(\"z\"))).add(y.sample(D.member(\"uvD\"),\"linearClamp\").member(\"xy\").mul(O.member(\"w\"))),\"flowVec\"),q=d9(k,F,.5);return W(Yt,\"unpremultiplyAlpha\",[Ha(z(d9(k,F,z_e(_,W(Yt,\"unpremultiplyAlpha\",[d.sample(q)]).member(\"rgb\"),t.keyInfluence)),\"displacedUV\"),Y=>d.sample(Y),u)])}}});var p9=H({name:\"Plasma\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Animated effect of glowing plasma\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{density:{default:2,description:\"Density of the plasma pattern\",ui:{type:[\"range\",\"map\"],min:0,max:4,step:.1,label:\"Density\",group:\"Effect\"}},speed:{default:2,description:\"Animation speed\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Effect\"}},intensity:{default:1.5,description:\"Brightness and spread of the plasma glow\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.1,label:\"Intensity\",group:\"Colors\"}},warp:{default:.4,description:\"How much the flow distorts and swirls\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Warp\",group:\"Effect\"}},contrast:{default:1,description:\"Push darks darker and lights lighter\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.1,label:\"Contrast\",group:\"Colors\"}},balance:{default:50,description:\"Skew color balance toward A (higher) or B (lower)\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Balance\",group:\"Colors\"}},colorA:{default:\"#7018be\",description:\"Primary color\",transform:ce,ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#000000\",description:\"Secondary color\",transform:ce,ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(wR(PR(SR(Tc(\"mx3\"),{amount:T(\"warp\"),amountScale:4,timeScale:.125}),{scale:T(\"density\")}),{glow:T(\"intensity\"),contrast:T(\"contrast\"),balance:T(\"balance\"),invert:!0}),Us(T(\"colorSpace\")))});var R_e=JSON.stringify({type:\"sphere3D\",radius:.35}),Wd=(e,t,s,r,o)=>{let a=z(r,`${o}S`),i=z(e,`${o}X`);return v(ot(ne(t,a),I(t,a),i),ne(1,ot(ne(s,a),I(s,a),i)))},r5=(e,t,s,r)=>{let o=rt(I(v(e,s),r),I(v(t,s),v(r,.617))),a=rt(I(I(v(v(e,s),2.3),13.1),r),I(I(v(v(t,s),2.3),7.7),v(r,.617)));return I(ms(pa(o)),v(ms(pa(a)),.45))},m9=H({name:\"Plastic\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Glossy molded plastic with photorealistic studio reflections, driven in a custom shape\",animatedTime:{speed:\"speed\"},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s,propValues:r}=t,o=wt(t),a=e.volumetric,i=Lr(e,t,{scale:s.scale}),{sdf:l,pxH:c}=i,d=z(e.sdfUV,\"sdfUV\"),u=Er(s.gradientAngle,\"gDir\"),p=ot(0,1,Ge(I(Ce($t(ne(d.member(\"xy\"),rt(.5,.5)),u),.7),.5),0,1)),_=r.colorSpace??0,f=r.stopCount??0,h;f>1?h=En(p,{colorsArray:s.colorsArray,positionsArray:s.positionsArray,convertedColorsArray:s.convertedColorsArray,stopCount:s.stopCount},_):h=W(No[_]??Ar,\"mixColors\",[s.colorA,s.colorB,p]);let g=h.member(\"rgb\"),y=Er(I(s.lightAngle,v(o,6)),\"light\"),b=y.member(\"x\"),x=y.member(\"y\"),P=Ln(i),C=Jr(i,s.edgeSoftness),E=go(e,i,{uv:d}),R=E.member(\"x\"),U=E.member(\"y\"),L=it(v(s.thickness,.3),.01),k=z(Ge(Ce(v(l,-1),L),0,1),\"depthNorm\"),D;if(a)D=Mc(i,4,\"nBase\");else{let Kt=z(v(ho(it(ne(1,v(k,k)),0)),2.4),\"bevelSlope\");D=z(Os(Qe(v(P.member(\"x\"),Kt),v(P.member(\"y\"),Kt),-1)),\"nBase\")}let O=z(v(s.crumpleScale,4),\"cScale\"),F=v(s.crumple,.9),q=z(v(s.seed,7.31),\"seedOff\"),Y=.012,V=z(r5(R,U,O,q),\"cr0\"),X=Ce(ne(r5(I(R,Y),U,O,q),V),Y),Q=Ce(ne(r5(R,I(U,Y),O,q),V),Y),te=Ge(s.crumpleCoverage,0,1),J=I(v(co(I(I(v(R,2.6),q),31.7),I(I(v(U,2.6),v(q,.617)),17.3)),.5),.5),oe=z(ne(1.25,v(te,1.75)),\"covThreshold\"),le=z(v(v(F,ot(ne(oe,.25),I(oe,.25),J)),I(v(ot(0,.35,k),.85),.15)),\"crumpleW\"),se=Gi(D,v(v(X,le),.25),v(v(Q,le),.25)),Te=Fi(se),de=z(Lo(Qe(0,0,1),se),\"R\"),xe=z(Ge(s.roughness,0,1),\"rough\"),Ae=z(v(v(xe,co(v(R,140),v(U,140))),.18),\"rJit\"),De=z(I(I(v(de.member(\"x\"),b),v(de.member(\"y\"),x)),Ae),\"eu\"),Ee=z(I(I(v(de.member(\"x\"),v(x,-1)),v(de.member(\"y\"),b)),v(Ae,.6)),\"ev\"),ze=z(et(.02,.45,xe),\"soft\"),Ie=v(v(Wd(De,.18,.95,ze,\"win1\"),Wd(Ee,-.42,.42,ze,\"win2\")),ne(1,v(v(Wd(Ee,-.045,.045,v(ze,.6),\"mull\"),.8),ne(1,xe)))),ke=v(v(Wd(De,-.16,-.05,v(ze,.7),\"strip1\"),Wd(Ee,-.85,.85,ze,\"strip2\")),.85),Me=v(v(Wd(De,-.95,-.45,v(ze,1.6),\"fill1\"),Wd(Ee,-.6,.6,v(ze,1.6),\"fill2\")),.3),we=v(Ge(I(v(De,.5),.5),0,1),.12),Ve=v(I(I(I(Ie,ke),Me),we),ne(1,v(xe,.55))),qe=I(.3,v(ts(Te,3),.7)),st=v(s.reflectivity,qe),pt=Un(y,-.8),Ze=pt.member(\"L\"),nt=z(v(I(Ve,v(v(ts(Ge($t(se,pt.member(\"H\")),0,1),U_(et(9,4,xe))),1.6),ne(1,v(xe,.7)))),st),\"specular\"),ht=z(I(v(Ge($t(se,Ze),-1,1),.5),.5),\"lit\"),yt=et(ne(1,v(s.shading,.75)),I(1,v(s.shading,.45)),ht),St=et(.6,1,ot(0,1,Ge(Ce(v(l,-1),.025),0,1))),Mt=v(v(v(ts(Te,3.5),s.rim),I(v(ht,.6),.4)),1.6),Rt=z(I(I(v(v(g,yt),St),kt(nt)),v(s.rimColor.member(\"rgb\"),Mt)),\"plasticRgb\");return Js(Dr(l,c),Ot(Rt,C),xt,\"plastic\")}}),props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the plastic shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the plastic shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the plastic shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},colorA:{default:\"#f5f5f7\",transform:ce,description:\"Body color at the start of the gradient\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#d9dade\",transform:ce,description:\"Body color at the end of the gradient\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),gradientAngle:{default:90,description:\"Direction of the body color gradient in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Gradient Angle\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for gradient color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},thickness:{default:.5,description:\"Puffiness \\u2014 how far the rounded bevel reaches inward from the edge before the surface flattens\",ui:{type:\"range\",min:.05,max:1,step:.01,label:\"Puffiness\",group:\"Plastic\"}},roughness:{default:.12,description:\"Surface roughness \\u2014 0 = polished gloss with crisp mirror reflections, 1 = matte satin\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Roughness\",group:\"Plastic\"}},reflectivity:{default:1,description:\"Strength of the environment reflections and specular highlights\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Reflectivity\",group:\"Plastic\"}},crumple:{default:.25,description:\"Surface crumple \\u2014 irregular dents that break reflections into hard organic shapes, like blown or vacuum-formed plastic\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Crumple\",group:\"Plastic\"}},crumpleScale:{default:1,description:\"Size of the crumple dents \\u2014 higher = smaller, denser dents\",ui:{type:\"range\",min:.5,max:10,step:.1,label:\"Crumple Scale\",group:\"Plastic\"}},crumpleCoverage:{default:1,description:\"How much of the surface is crumpled \\u2014 0 = pristine, 0.5 = patches of crumple, 1 = fully crumpled\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Crumple Coverage\",group:\"Plastic\"}},seed:{default:0,description:\"Random seed \\u2014 re-rolls the crumple pattern for reflection variety\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Plastic\"}},edgeSoftness:{default:.05,description:\"Softness of the shape boundary edge\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Plastic\"}},lightAngle:{default:315,description:\"Direction the studio key light comes from, in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Lighting\"}},speed:{default:1,description:\"Speed of the environment drift \\u2014 the studio reflections slowly orbit the surface. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Animation\"}},shading:{default:.35,description:\"Diffuse shading contrast \\u2014 how strongly the body darkens away from the light\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Shading\",group:\"Lighting\"}},rim:{default:.25,description:\"Fresnel rim light intensity on grazing surfaces\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Rim Light\",group:\"Lighting\"}},rimColor:{default:\"#ffffff\",transform:ce,description:\"Color of the fresnel rim light\",ui:{type:\"color\",label:\"Rim Color\",group:\"Lighting\"}},shape:{default:R_e,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}}});var _9=H({name:\"PolarCoordinates\",role:\"warp\",category:\"Distortions\",description:\"Convert rectangular coordinates to polar space\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point for polar coordinate conversion\"),wrap:{default:1,description:\"Controls how much of the angular range to use (1 = full 360\\xB0, 0.5 = 180\\xB0)\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Wrap\",group:\"Effect\"}},radius:{default:1,description:\"Controls how much of the radius range to use (affects the radial mapping)\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Radius\",group:\"Effect\"}},intensity:{default:1,description:\"Blends between original UVs (0) and polar coordinates (1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Intensity\",group:\"Effect\"}},edges:fr(\"transparent\",\"How to handle edges when distortion pushes content out of bounds\")},map:ZL({center:T(\"center\"),wrap:T(\"wrap\"),radius:T(\"radius\"),intensity:T(\"intensity\")}),missingChildMessage:\"You must pass a child component into the PolarCoordinates shader.\"});var f9=H({role:\"shape\",name:\"Polygon\",description:\"Regular polygon with adjustable sides and corner rounding\",shape:{distance:({x:e,y:t},s)=>et(lR(e,t,s(\"radius\"),s(\"sides\")),_p(e,t,s(\"radius\")),s(\"rounding\")),colorDescription:\"Fill color of the polygon\",centerDescription:\"Center position of the polygon\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.4,description:\"Inradius \\u2014 distance from the center to the middle of each side, in UV space (the vertices sit a little farther out)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Radius\",group:\"Shape\"}},sides:{default:6,description:\"Number of sides (3 = triangle, 4 = square, 6 = hexagon, etc.)\",ui:{type:\"range\",min:3,max:12,step:1,label:\"Sides\",group:\"Shape\"}},rounding:{default:0,description:\"Corner rounding \\u2014 0 is sharp vertices, 1 morphs into a circle\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Rounding\",group:\"Shape\"}}}}});var v9=H({name:\"Posterize\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Reduce color depth to create a poster effect\",props:{intensity:{default:5,description:\"The intensity of the posterization effect (lower is more posterized)\",ui:{type:[\"range\",\"map\"],min:2,max:20,step:1,label:\"Intensity\",group:\"Effect\"}}},effect:c8(T(\"intensity\")),missingChildMessage:\"You must pass a child component into the Posterize shader.\"});var h9=H({name:\"Prism\",role:\"generator\",category:\"Textures\",description:\"A beam of light that fans out and splits into a slowly-rotating rainbow past a controllable point. Transparent background \\u2014 composite over a dark layer.\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{position:{default:{x:.18,y:.18},transform:Xe,description:\"Where the beam originates\",ui:{type:\"position\",label:\"Source\",group:\"Beam\"}},beamWidth:{default:.04,description:\"Thickness of the white beam\",ui:{type:[\"range\",\"map\"],min:.002,max:.3,step:.002,label:\"Beam Width\",group:\"Beam\"}},intensity:{default:1.6,description:\"Brightness (above 1 blows the core to white)\",ui:{type:[\"range\",\"map\"],min:0,max:4,step:.01,label:\"Intensity\",group:\"Beam\"}},beamColor:{default:\"#ffffff\",transform:ce,description:\"color of the beam before it splits\",ui:{type:\"color\",label:\"Beam Color\",group:\"Beam\"}},startFalloff:{default:.15,description:\"How softly the beam fades in at its source\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Start Falloff\",group:\"Beam\"}},endFalloff:{default:.6,description:\"How quickly the rainbow fades out with distance\",ui:{type:[\"range\",\"map\"],min:.02,max:2,step:.01,label:\"End Falloff\",group:\"Beam\"}},splitPosition:{default:{x:.5,y:.42},transform:Xe,description:\"Where the beam starts to fan out and split (projected onto the beam axis)\",ui:{type:\"position\",label:\"Split Point\",group:\"Spectrum\"}},spread:{default:.7,description:\"How widely the rainbow diffuses as it travels past the split point\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Spread\",group:\"Spectrum\"}},softness:{default:.12,description:\"How quickly the white beam transitions into the rainbow\",ui:{type:[\"range\",\"map\"],min:.001,max:1,step:.005,label:\"Softness\",group:\"Spectrum\"}},saturation:{default:.95,description:\"Rainbow saturation \\u2014 0 = white, 1 = full color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Saturation\",group:\"Spectrum\"}},speed:{default:.1,description:\"Speed the hue slowly rotates through the rainbow (0 = static)\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Hue Speed\",group:\"Spectrum\"}}},paint:e=>{let t=f=>me(T(f),e),s=Pg({from:T(\"position\"),to:T(\"splitPosition\")})(e,aa(e)),r=z(it(t(\"softness\"),.001),\"soft\"),o=z(jz(ne(s.along,s.length),v(r,.6)),\"past\"),a=z(v(ot(0,it(t(\"startFalloff\"),.001),s.along),tr(er(Ce(o,it(t(\"endFalloff\"),.001))))),\"longEnv\"),i=it(I(t(\"beamWidth\"),v(o,t(\"spread\"))),.001),l=z(Ce(s.across,i),\"an\"),c=O_(l,2.5),d=ot(s.length,I(s.length,r),s.along),u=I(v(l,.42),wt(e)),p=et(t(\"beamColor\").member(\"rgb\"),ER(u),kt(v(d,t(\"saturation\")))),_=z(v(c,a),\"amt\");return Ot(z(v(v(p,_),t(\"intensity\")),\"beamRgb\").member(\"xyz\"),Ge(_,0,1))}});var g9=H({name:\"ProgressiveBlur\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Blur that increases progressively in one direction\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:50,description:\"Maximum intensity of the blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Direction of the blur gradient (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},center:{default:{x:0,y:.5},transform:Xe,description:\"Center point where blur begins\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},falloff:{default:1,description:\"Distance over which blur transitions to full strength\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Falloff\",group:\"Effect\"}},dispersion:{default:0,description:\"Blurs red and blue by different amounts for a lens-like color fringe that grows with the blur; negative swaps the colors\",recompile:Lt(0),ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Dispersion\",group:\"Lens\"}},jitter:{default:0,description:\"Per-pixel noise on the tap spacing that hides banding at large blur radii\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Jitter\",group:\"Lens\"}}},...RU({intensity:T(\"intensity\"),angle:T(\"angle\"),center:T(\"center\"),falloff:T(\"falloff\"),detail:{dispersion:T(\"dispersion\"),jitter:T(\"jitter\")}})});var y9=H({name:\"RadialGradient\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Radial gradient radiating from a center point\",acceptsUVContext:!0,props:{colorA:{default:\"#ff0000\",transform:ce,description:\"The starting color at the center of the gradient\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#0000ff\",transform:ce,description:\"The ending color at the edge of the gradient\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the radial gradient\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},radius:{default:1,description:\"The radius of the gradient (normalized, 0.0-1.0)\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Radius\",group:\"Effect\"}},repeat:{default:1,description:\"Number of times the gradient repeats. Values above 1 create concentric rings.\",ui:{type:[\"range\",\"map\"],min:1,max:20,step:.5,label:\"Repeat\",group:\"Effect\"}},aspect:{default:1,description:\"Stretches the gradient into an ellipse. Values below 1 compress vertically, above 1 compress horizontally.\",ui:{type:[\"range\",\"map\"],min:.1,max:4,step:.01,label:\"Aspect\",group:\"Effect\"}},skewAngle:{default:0,description:\"Rotates the ellipse axis in degrees. Only visible when Aspect is not 1.\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Skew Angle\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(mg(Oi.radial({center:T(\"center\"),radius:T(\"radius\"),aspect:T(\"aspect\"),skew:T(\"skewAngle\")}),T(\"repeat\")),Us(T(\"colorSpace\")))});var b9=H({name:\"RadialWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Sweep the content away in a clock-hand arc around a center point\",props:{progress:{default:.5,description:\"How far the sweep has travelled (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},startAngle:{default:0,description:\"Angle in degrees where the sweep begins\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Start Angle\",group:\"Transition\"}},direction:{default:\"cw\",compileTime:!0,description:\"Which way the sweep rotates\",ui:{type:\"select\",options:[{label:\"Clockwise\",value:\"cw\"},{label:\"Counter-Clockwise\",value:\"ccw\"},{label:\"Both\",value:\"both\"}],label:\"Direction\",group:\"Transition\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Point the sweep rotates around\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},feather:{default:.1,description:\"Softness of the sweeping edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse which side of the sweep is wiped\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.angular(T(\"center\"),T(\"startAngle\"),T(\"direction\")),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Radial Wipe shader.\"});var x9=H({name:\"RandomBars\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away as parallel bars vanishing in random order\",props:{progress:{default:.5,description:\"How far the wipe has progressed (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},angle:{default:0,description:\"Orientation of the bars in degrees (0 = vertical bars)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Transition\"}},barCount:{default:12,description:\"Number of bars across the frame\",ui:{type:[\"range\",\"map\"],min:2,max:100,step:1,label:\"Bars\",group:\"Transition\"}},softness:{default:.15,description:\"How softly each bar fades out (0 = hard-edged bars)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Reverse the order the bars vanish in\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.directional(T(\"angle\")).bands(T(\"barCount\")).shuffled(),progress:T(\"progress\"),feather:T(\"softness\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Random Bars shader.\"});var A9=Mn,tn=512,w9=tn*tn,M9=\"rgba16float\",T9=16,D_e=.06,ny=1.5/tn,S9=6,P9=.42,C9=.85,L_e=.55,U_e=18,O_e=.35,G_e=bt,F_e={coral:{feed:.0545,kill:.062},mitosis:{feed:.0367,kill:.0649},spots:{feed:.03,kill:.062},maze:{feed:.029,kill:.057},worms:{feed:.078,kill:.061},fingerprints:{feed:.037,kill:.06},holes:{feed:.039,kill:.058},solitons:{feed:.074,kill:.064},bubbles:{feed:.098,kill:.0555},waves:{feed:.014,kill:.045},flowers:{feed:.062,kill:.06093}},I_e=8,Y_e=.022,W_e=.014,q_e=.008,V_e=.11,N_e=.03,$_e=.075,o5=ZA({n:tn,namePrefix:\"reactionDiffusion\"}),a5=ZA({n:tn,namePrefix:\"reactionDiffusion\",drive:{contrastMax:I_e,dFeed:Y_e,dKill:W_e,feedMin:q_e,feedMax:V_e,killMin:N_e,killMax:$_e}}),n5=Uz({n:tn,density:D_e,namePrefix:\"reactionDiffusion\"}),i5=Oz({n:tn,format:M9,namePrefix:\"reactionDiffusion\"}),K5e=o5.kernel,Q5e=a5.kernel,Z5e=n5.kernel,J5e=i5.kernel,E9=H({name:\"ReactionDiffusion\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"A living Gray-Scott reaction-diffusion pattern that fills the layer and blooms wherever you drag the cursor\",requiresRTT:!1,requiresChild:!1,acceptsOptionalChild:!0,usesPointer:!0,props:{preset:{default:\"coral\",description:\"Named Gray-Scott regime \\u2014 drives the feed & kill rates for a whole family of patterns. Choose Custom to dial feed & kill by hand\",ui:{type:\"select\",options:[{label:\"Coral\",value:\"coral\"},{label:\"Mitosis\",value:\"mitosis\"},{label:\"Spots\",value:\"spots\"},{label:\"Maze\",value:\"maze\"},{label:\"Worms\",value:\"worms\"},{label:\"Fingerprints\",value:\"fingerprints\"},{label:\"Holes\",value:\"holes\"},{label:\"Solitons\",value:\"solitons\"},{label:\"Bubbles\",value:\"bubbles\"},{label:\"Waves\",value:\"waves\"},{label:\"Flowers\",value:\"flowers\"},{label:\"Custom\",value:\"custom\"}],label:\"Pattern\",group:\"Pattern\"}},feed:{default:.0545,description:\"Feed rate \\u2014 how fast chemical U is replenished (used when Pattern = Custom)\",ui:{type:\"range\",min:.01,max:.1,step:.001,label:\"Feed\",group:\"Pattern\",condition:{preset:\"custom\"}}},kill:{default:.062,description:\"Kill rate \\u2014 how fast chemical V is removed (used when Pattern = Custom)\",ui:{type:\"range\",min:.03,max:.07,step:.001,label:\"Kill\",group:\"Pattern\",condition:{preset:\"custom\"}}},diffusionRatio:{default:.5,description:\"Ratio of the two chemicals' diffusion rates \\u2014 shifts the pattern character (lower = tighter, more filament-like)\",ui:{type:\"range\",min:.2,max:.9,step:.01,label:\"Diffusion Ratio\",group:\"Pattern\"}},featureSize:{default:3,description:\"Size of the pattern features \\u2014 lower shows finer, smaller cells; higher magnifies them into larger cells\",ui:{type:\"range\",min:1,max:6,step:.1,label:\"Feature Size\",group:\"Pattern\"}},speed:{default:6,description:\"Simulation steps per frame \\u2014 higher evolves the pattern faster\",ui:{type:\"range\",min:1,max:T9,step:1,label:\"Speed\",group:\"Pattern\"}},brushSize:{default:.04,description:\"Radius of the cursor brush that seeds new reagent as you drag\",ui:{type:\"range\",min:.01,max:.3,step:.01,label:\"Brush Size\",group:\"Interaction\"}},brushStrength:{default:.6,description:\"How much reagent the cursor injects while dragging\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Brush Strength\",group:\"Interaction\"}},colorA:{default:\"transparent\",transform:ce,description:\"Background color (empty regions between cells)\",ui:{type:\"color\",label:\"Background\",group:\"Colors\"}},colorC:{default:\"#38bdf8\",transform:ce,description:\"Accent color at the cell walls (mid pattern values)\",ui:{type:\"color\",label:\"Wall Color\",group:\"Colors\"}},colorB:{default:\"#e2f5ff\",transform:ce,description:\"Color of the dense cell interiors (high pattern values)\",ui:{type:\"color\",label:\"Cell Color\",group:\"Colors\"}},contrast:{default:.5,description:\"Sharpness of the cell walls \\u2014 higher gives crisper, more graphic edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Contrast\",group:\"Colors\"}},threshold:{default:.28,description:\"Pattern level the colors are centered on \\u2014 shifts the balance of background vs. cells\",ui:{type:[\"range\",\"map\"],min:.05,max:.6,step:.01,label:\"Threshold\",group:\"Colors\"}},colorSpace:{default:\"oklch\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},relief:{default:.4,description:\"Treats the pattern as a raised surface and shades it with a light \\u2014 higher gives a more sculpted, three-dimensional look (0 for a flat, graphic look)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Relief\",group:\"Lighting\"}},lightAngle:{default:135,description:\"Direction the surface light comes from, in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Light Angle\",group:\"Lighting\"}},childInfluence:{default:.6,description:\"When a child layer is nested, how strongly its brightness biases the pattern \\u2014 the preset sets the character, the child steers where it grows dense vs. sparse (0 ignores the child)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Influence\",group:\"Child\"}},childContrast:{default:.5,description:\"Contrast applied to the child's brightness before it drives the pattern \\u2014 higher pushes toward a crisp two-tone split (edges), lower keeps smooth tonal shading\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Contrast\",group:\"Child\"}},childThreshold:{default:.5,description:\"Brightness level of the child that reads as neutral \\u2014 shifts which tones grow the pattern vs. clear it\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Threshold\",group:\"Child\"}},childInvert:{default:!1,transform:rs,description:\"Flip which tones of the child grow the pattern (bright vs. dark areas)\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Child\"}}},...Ec((e,t)=>{let{getCpuValue:s,registerComputeTexture:r,onCleanup:o,childNode:a}=e,i=!!a,l=pr(t,m,w9),c=pr(t,m,w9),d=t.createTexture({size:[tn,tn],format:M9}).$usage(\"storage\",\"sampled\");o(()=>d.destroy());let u=r(d),p=i?a5:o5,_=t.createUniform(p.Params),f=[tn,tn],h=ap(l,c),g=ai(t,i5.kernel,{size:f}),y=h.perSide(D=>g.with(t.createBindGroup(i5.layout,{srcBuf:D,outTex:d}))),b=ai(t,n5.kernel,{size:f,bindGroup:t.createBindGroup(n5.layout,{stateBuf:l})}),x=ai(t,p.kernel,{size:f}),P=i?null:h.groups((D,O)=>x.with(t.createBindGroup(o5.layout,{readBuf:D,writeBuf:O,params:_.buffer}))),C=i?nA(e,D=>h.groups((O,F)=>x.with(t.createBindGroup(a5.layout,{readBuf:O,writeBuf:F,params:_.buffer,driveTex:D})))):null,E=!0,R=\"\",U=Sl(),L=null,k=1;return{outputs:{rdTexture:u},...C?{bindInputs:C.bindInputs}:{},clampDt:.05,stages:[Ir.readyWhen(()=>(L=C?C.bound():P,!!L)),Ir.values(\"feed/kill+brush\",D=>{let O=D.frameParams,F=D.num,q=O.dimensions?.height??0,Y=q>0?(O.dimensions?.width??0)/q:1,V=s(\"preset\")||\"coral\";V!==R&&(R=V,E=!0);let X=F_e[V],Q=X?X.feed:F(\"feed\",.055),te=X?X.kill:F(\"kill\",.062),J=1/Math.max(F(\"featureSize\",3),1e-4),oe=Math.min(Y,1),le=Math.min(1/Y,1),se=U.update(O.pointer,.016),Te=(se.x-.5)*oe*J+.5,de=(se.y-.5)*le*J+.5,xe=Math.sqrt(se.dx*se.dx+se.dy*se.dy),Ae=xe>5e-4&&F(\"brushStrength\",.6)>0?Math.min(xe/.02,1):0,De=F(\"brushSize\",.04)*tn*J,Ee=s(\"childInvert\");k=Math.min(T9,Math.max(1,Math.round(F(\"speed\",6)))),_.write({feed:Q,kill:te,dv:F(\"diffusionRatio\",.5),brushRadSq:De*De,brushStrength:F(\"brushStrength\",.6),cursorX:Te*tn,cursorY:de*tn,cursorActive:Ae,driveInfluence:F(\"childInfluence\",.6),driveContrast:F(\"childContrast\",.5),driveThreshold:F(\"childThreshold\",.5),driveInvert:Ee===!0||Ee===1?1:0,driveMapX:1/(oe*J),driveMapY:1/(le*J),_pad0:0,_pad1:0})}),Ir.seedOnce({pass:b,staleWhen:()=>E,onSeed:()=>{E=!1,h.reset()}}),Ir.iterate({count:()=>k,step:(D,O)=>{O.push(L()),h.swap()}}),Ir.publish(()=>y.read())]}}),gpu:{fragment:e=>{let{ctx:t,computeOutputs:s,propValues:r,uniforms:o}=e,a=s?.rdTexture;if(!a)return xt;let i=z(Ce(1,o.featureSize),\"view\"),l=Yr(t.aspect,1),c=Yr(Ce(1,t.aspect),1),d=z(rt(I(v(v(ne(t.uv.member(\"x\"),.5),l),i),.5),I(v(v(ne(t.uv.member(\"y\"),.5),c),i),.5)),\"sampleUV\"),u=a.sample(d,\"linearClamp\").member(\"y\"),p=z(it(ne(.6,v(o.contrast,.55)),.02),\"rampW\"),_=z(ot(ne(o.threshold,p),I(o.threshold,p),u),\"rampT\"),f=r.colorSpace??0,h=A9.mixColorsVariants[f]??A9.mixColorsLinear,g=W(h,\"mixColors\",[o.colorA,o.colorC,Ge(v(_,2),0,1)]),y=W(h,\"mixColors\",[o.colorC,o.colorB,Ge(ne(v(_,2),1),0,1)]),b=z(br(Zo(_,.5),y,g),\"rampColor\"),x=a.sample(I(d,rt(-ny,0)),\"linearClamp\").member(\"y\"),P=a.sample(I(d,rt(ny,0)),\"linearClamp\").member(\"y\"),C=a.sample(I(d,rt(0,-ny)),\"linearClamp\").member(\"y\"),E=a.sample(I(d,rt(0,ny)),\"linearClamp\").member(\"y\"),R=v(ne(P,x),S9),U=v(ne(E,C),S9),L=z(Os(Qe(er(R),er(U),1)),\"reliefN\"),k=z(v(o.lightAngle,G_e),\"lightAng\"),D=z(Os(Qe(v(Do(k),C9),v(Vt(k),C9),L_e)),\"lightDir\"),O=Ge($t(L,D),0,1),F=I(P9,v(1-P9,O)),q=v(ts(Ge($t(L,Os(I(D,Qe(0,0,1)))),0,1),U_e),O_e),Y=I(v(b.member(\"rgb\"),F),kt(q));return Ot(et(b.member(\"rgb\"),Y,o.relief),b.member(\"a\"))}}});var k9=H({name:\"RectangularCoordinates\",role:\"warp\",category:\"Distortions\",description:\"Convert polar coordinates back to rectangular space\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point for rectangular coordinate conversion\"),scale:{default:1,description:\"Scale factor for the rectangular output\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.1,label:\"Scale\",group:\"Effect\"}},intensity:{default:1,description:\"Blends between original UVs (0) and rectangular coordinates (1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Intensity\",group:\"Effect\"}},edges:fr(\"transparent\",\"How to handle edges when distortion pushes content out of bounds\")},map:JL({center:T(\"center\"),scale:T(\"scale\"),intensity:T(\"intensity\")}),missingChildMessage:\"You must pass a child component into the RectangularCoordinates shader.\"});var B9=H({name:\"ReflectivePlane\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Reflective floor that mirrors the content above it\",requiresRTT:!0,requiresChild:!0,props:{height:{default:.7,description:\"Vertical position of the reflective surface\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Floor Height\",group:\"Plane\"}},distance:{default:.5,description:\"How far below the floor the reflection remains visible before fully fading to transparent.\",ui:{type:[\"range\",\"map\"],min:.01,max:1,step:.01,label:\"Reflection Distance\",group:\"Plane\"}},falloff:{default:.5,description:\"Width of the fade zone, as a fraction of reflection distance.\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Reflection Falloff\",group:\"Plane\"}},blur:{default:3,description:\"Maximum blur applied to the reflection far from the surface.\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.01,label:\"Blur\",group:\"Surface\"}},blurDistance:{default:.3,description:\"How far below the surface the blur takes to ramp from sharp to maximum.\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Blur Distance\",group:\"Surface\"}},edges:{default:\"stretch\",description:\"How to handle reflected samples that fall outside the source content.\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Surface\"}}},...KU({line:T(\"height\"),blur:T(\"blur\"),blurDistance:T(\"blurDistance\"),halfKernel:30}),gpu:{fragment:e=>{let{childNode:t,ctx:s,computeOutputs:r,convertToTexture:o}=e;if(!t)return xt;let a=r?.blurredTexture,i=r?.childTexture??o(t),l=a??i;return W(Yt,\"unpremultiplyAlpha\",[QU({line:T(\"height\"),distance:T(\"distance\"),falloff:T(\"falloff\"),edges:T(\"edges\")},i.sample(s.uv),c=>l.sample(c),jU(T(\"height\")))(e)])}}});var z9=H({name:\"Repeater\",role:\"structural\",species:\"custom\",category:\"Distortions\",description:\"Repeat the child content in grid, radial or linear layouts with per-instance variation\",requiresRTT:!0,requiresChild:!0,wantsBoundsParams:!0,boundingBoxDeclaration:{aspectRatio:null},props:{mode:{default:\"grid\",transform:e=>({grid:0,radial:1,linear:2})[e]??0,compileTime:!0,description:\"Layout used to place the repeated instances\",ui:{type:\"select\",options:[{label:\"Grid\",value:\"grid\"},{label:\"Radial\",value:\"radial\"},{label:\"Linear\",value:\"linear\"}],label:\"Mode\",group:\"Layout\"}},cropLeft:{default:0,description:\"Crop inset from the left edge of the source element (fraction of its width)\",ui:{type:[\"range\",\"map\"],min:0,max:.49,step:.005,label:\"Crop Left\",group:\"Source\"}},cropRight:{default:0,description:\"Crop inset from the right edge of the source element (fraction of its width)\",ui:{type:[\"range\",\"map\"],min:0,max:.49,step:.005,label:\"Crop Right\",group:\"Source\"}},cropTop:{default:0,description:\"Crop inset from the top edge of the source element (fraction of its height)\",ui:{type:[\"range\",\"map\"],min:0,max:.49,step:.005,label:\"Crop Top\",group:\"Source\"}},cropBottom:{default:0,description:\"Crop inset from the bottom edge of the source element (fraction of its height)\",ui:{type:[\"range\",\"map\"],min:0,max:.49,step:.005,label:\"Crop Bottom\",group:\"Source\"}},columns:{default:3,description:\"Number of grid columns\",ui:{type:[\"range\",\"map\"],min:1,max:12,step:1,label:\"Columns\",group:\"Layout\",condition:{mode:\"grid\"}}},rows:{default:3,description:\"Number of grid rows\",ui:{type:[\"range\",\"map\"],min:1,max:12,step:1,label:\"Rows\",group:\"Layout\",condition:{mode:\"grid\"}}},gapX:{default:.05,description:\"Horizontal gap between grid cells\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.005,label:\"Gap X\",group:\"Layout\",condition:{mode:\"grid\"},dimensional:\"canvas-width\"}},gapY:{default:.05,description:\"Vertical gap between grid cells\",ui:{type:[\"range\",\"map\"],min:0,max:.5,step:.005,label:\"Gap Y\",group:\"Layout\",condition:{mode:\"grid\"},dimensional:\"canvas-height\"}},stagger:{default:0,description:\"Horizontal offset applied to every other row (brick layout)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Stagger\",group:\"Layout\",condition:{mode:\"grid\"}}},flip:{default:\"none\",transform:e=>({none:0,\"alternate-flip-x\":1,\"alternate-flip-y\":2,both:3})[e]??0,compileTime:!0,description:\"Mirror alternating columns/rows for seamless tiling patterns\",ui:{type:\"select\",options:[{label:\"None\",value:\"none\"},{label:\"Alternate Flip X\",value:\"alternate-flip-x\"},{label:\"Alternate Flip Y\",value:\"alternate-flip-y\"},{label:\"Both\",value:\"both\"}],label:\"Flip\",group:\"Layout\",condition:{mode:\"grid\"}}},count:{default:8,description:\"Number of repeated instances\",ui:{type:[\"range\",\"map\"],min:1,max:64,step:1,label:\"Count\",group:\"Layout\",condition:{mode:[\"radial\",\"linear\"]}}},radius:{default:.3,description:\"Orbit radius of the radial layout\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Radius\",group:\"Layout\",condition:{mode:\"radial\"},dimensional:\"canvas-height\"}},startAngle:{default:0,description:\"Angle of the first instance on the orbit\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Start Angle\",group:\"Layout\",condition:{mode:\"radial\"}}},sweep:{default:360,description:\"Angular span the instances are distributed across\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Sweep\",group:\"Layout\",condition:{mode:\"radial\"}}},faceCenter:{default:!1,transform:rs,description:\"Rotate each instance to face the orbit center\",ui:{type:\"checkbox\",label:\"Face Center\",group:\"Layout\",condition:{mode:\"radial\"}}},direction:{default:0,description:\"Direction of the linear layout in degrees (0 = rightward)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Direction\",group:\"Layout\",condition:{mode:\"linear\"}}},spacing:{default:.2,description:\"Distance between consecutive instances\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Spacing\",group:\"Layout\",condition:{mode:\"linear\"},dimensional:\"canvas-height\"}},instanceScale:{default:1,description:\"Progressive scale multiplier applied per instance (compounds with each copy)\",ui:{type:[\"range\",\"map\"],min:.5,max:2,step:.005,label:\"Scale\",group:\"Instances\"}},instanceRotation:{default:0,transform:e=>e*Math.PI/180,description:\"Additional rotation in degrees applied per instance (accumulates with each copy)\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Rotation\",group:\"Instances\"}},instanceOpacity:{default:1,description:\"Progressive opacity falloff applied per instance (compounds with each copy)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Opacity\",group:\"Instances\"}},hueShift:{default:0,transform:e=>e*Math.PI/180,recompile:Lt(0),description:\"Hue rotation in degrees added per instance\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Hue Shift\",group:\"Instances\"}},phase:{default:0,description:\"Seamless layout animation phase (0-1 wraps to the identical layout)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.001,label:\"Phase\",group:\"Instances\"}},zOrder:{default:\"forward\",transform:e=>e===\"backward\"?1:0,compileTime:!0,description:\"Stacking order of overlapping instances\",ui:{type:\"select\",options:[{label:\"Forward\",value:\"forward\"},{label:\"Backward\",value:\"backward\"}],label:\"Z-Order\",group:\"Instances\"}},jitterPosition:{default:0,description:\"Random per-instance position offset\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Position\",group:\"Jitter\"}},jitterRotation:{default:0,description:\"Random per-instance rotation\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Rotation\",group:\"Jitter\"}},jitterScale:{default:0,description:\"Random per-instance scale variation\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Scale\",group:\"Jitter\"}},jitterOpacity:{default:0,description:\"Random per-instance opacity variation\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.005,label:\"Opacity\",group:\"Jitter\"}},seed:{default:0,description:\"Random seed for the jitter values\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Jitter\"}}},gpu:{fragment:D8({source:{crop:{left:T(\"cropLeft\"),right:T(\"cropRight\"),top:T(\"cropTop\"),bottom:T(\"cropBottom\")}},placement:R8(T(\"mode\"),{grid:k8({columns:T(\"columns\"),rows:T(\"rows\"),gapX:T(\"gapX\"),gapY:T(\"gapY\"),stagger:T(\"stagger\"),flip:T(\"flip\")}),radial:B8({count:T(\"count\"),radius:T(\"radius\"),startAngle:T(\"startAngle\"),sweep:T(\"sweep\"),faceCenter:T(\"faceCenter\")}),linear:z8({count:T(\"count\"),direction:T(\"direction\"),spacing:T(\"spacing\")})}),variation:{scale:T(\"instanceScale\"),rotation:T(\"instanceRotation\"),opacity:T(\"instanceOpacity\"),jitter:{position:T(\"jitterPosition\"),rotation:T(\"jitterRotation\"),scale:T(\"jitterScale\"),opacity:T(\"jitterOpacity\")},seed:T(\"seed\"),phase:T(\"phase\")},hueShift:T(\"hueShift\"),order:T(\"zOrder\")})}});var R9=H({role:\"shape\",name:\"Ring\",description:\"Annular ring (donut) with adjustable radius and band thickness\",shape:{distance:({x:e,y:t},s)=>mR(e,t,s(\"radius\"),s(\"thickness\")),rotatable:!1,colorDescription:\"Fill color of the ring\",centerDescription:\"Center position of the ring\",bounds:{custom:{propBindings:{x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"}},computeBounds(e,t,s){let r=e.radius??.5,o=e.thickness??0,a=typeof e.center?.x==\"number\"?e.center.x:.5,i=typeof e.center?.y==\"number\"?e.center.y:.5,l=(r+o)*2*s;return{centerXPx:a*t,centerYPx:i*s,widthPx:l,heightPx:l,rotationDeg:0}},writeBounds(e,t,s,r){let o=t.thickness??0,a=Math.max(0,e.widthPx/(2*r));return{center:{...t.center??{x:.5,y:.5},x:e.centerXPx/s,y:e.centerYPx/r},radius:Math.max(0,a-o)}}}},shapeProps:{radius:{default:.3,description:\"Distance from center to the ring's midline in UV space\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Radius\",group:\"Shape\"}},thickness:{default:.07,description:\"Half-width of the ring band \\u2014 total ring width is twice this value\",ui:{type:\"range\",min:.005,max:.3,step:.005,label:\"Thickness\",group:\"Shape\"}}},stroke:{softnessDescription:\"Edge softness for antialiasing (applied to both inner and outer ring edges)\",strokeThicknessMax:.1,strokePositionDescription:\"Position of the stroke relative to the ring edge\"}}});var D9=H({name:\"Ripples\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Concentric animated ripples emanating from a point\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point where ripples emanate from\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},colorA:{default:\"#ffffff\",transform:ce,description:\"Color of the ripple waves\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#000000\",transform:ce,description:\"Background color between ripples\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},speed:{default:1,description:\"Speed of ripple animation\",ui:{type:\"range\",min:-5,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},frequency:{default:20,description:\"Number of ripples/spacing between them\",ui:{type:[\"range\",\"map\"],min:1,max:80,step:.1,label:\"Frequency\",group:\"Effect\"}},softness:{default:0,description:\"Softness of ripple edges\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.1,label:\"Softness\",group:\"Effect\"}},thickness:{default:.5,description:\"Thickness of each ripple band\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Thickness\",group:\"Effect\"}},phase:{default:0,description:\"Phase offset for ripple animation\",ui:{type:[\"range\",\"map\"],min:0,max:6.28,step:.1,label:\"Phase\",group:\"Animation\"}}},paint:Zr({fill:T(\"colorB\"),stroke:T(\"colorA\"),mask:HR({center:T(\"center\"),frequency:T(\"frequency\"),thickness:T(\"thickness\"),softness:T(\"softness\"),phase:T(\"phase\")})})});var L9=H({name:\"RippleWipe\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away in concentric rings pulsing out from a center point\",props:{progress:{default:.5,description:\"How far the ripple has expanded (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Point the rings expand from\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},rings:{default:8,description:\"Number of concentric rings\",ui:{type:[\"range\",\"map\"],min:2,max:40,step:1,label:\"Rings\",group:\"Transition\"}},feather:{default:.2,description:\"How softly adjacent rings' timing overlaps (0 = discrete ring pops)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Wipe from the outside inward instead\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.radial(T(\"center\")).bands(T(\"rings\")),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Ripple Wipe shader.\"});var U9=H({role:\"shape\",name:\"RoundedRect\",description:\"Rounded rectangle with adjustable width, height, and corner rounding\",shape:{distance:({x:e,y:t},s)=>dg(e,t,s(\"width\"),s(\"height\"),s(\"rounding\")),colorDescription:\"Fill color of the rectangle\",centerDescription:\"Center position of the rectangle\",bounds:{size:{width:\"width\",height:\"height\"}},shapeProps:{width:{default:.35,description:\"Half-width of the rectangle\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Width\",group:\"Shape\"}},height:{default:.25,description:\"Half-height of the rectangle\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Height\",group:\"Shape\"}},rounding:{default:.05,description:\"Corner rounding radius \\u2014 set to min(width, height) for a pill shape\",ui:{type:\"range\",min:0,max:.5,step:.01,label:\"Rounding\",group:\"Shape\",dimensional:\"canvas-height\"}}}}});var W9=Rr(),O9=[{freq:3.1,speed:.35,amount:.045},{freq:6.7,speed:-.22,amount:.018},{freq:11.3,speed:.5,amount:.007}],X_e=W9?O9.slice(0,2):O9,H_e={amount:.3,freq:2.4,speed:.4},j_e=.08,K_e=.7,Q_e=2,Z_e=.8,J_e=.25,G9=.004,F9=.3,efe=.0012,I9={coarse:[14,420],fine:[26,900]},tfe=.1,sf={ambient:.18,key:.9,fill:.45,elevation:-.65,fillElevation:-.45},Y9=[.25,.9],l5={roughness:.85,anisotropy:.5,gain:.35},sfe=.6,q9=H({name:\"Satin\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Flowing satin \\u2014 a draped cloth of soft rippling folds lit like real fabric: a highlight streaked along the weave, a velvet sheen on the fold flanks and creases falling into shadow, swaying gently in a breeze\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{color:{default:\"#c92c40\",transform:ce,description:\"The dye of the cloth \\u2014 deep colors show the sheen best\",ui:{type:\"color\",label:\"Color\",group:\"Cloth\"}},highlightColor:{default:\"#c4667b\",transform:ce,description:\"The color of the light caught by the weave \\u2014 near white for silk, tinted toward the dye for a matte satin\",ui:{type:\"color\",label:\"Highlight\",group:\"Cloth\"}},foldCount:{default:7,compileTime:!0,description:\"How many ridges run across the cloth\",ui:{type:\"range\",min:1,max:12,step:1,label:\"Folds\",group:\"Folds\"}},foldDirection:{default:90,description:\"Direction the folds run, in degrees \\u2014 90 hangs them vertically like a curtain\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Direction\",group:\"Folds\"}},foldDepth:{default:.5,description:\"How deep the folds are \\u2014 0 is a flat sheet, 1 heavy gathered drapery\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Depth\",group:\"Folds\"}},foldWidth:{default:.14,description:\"Width of each ridge, relative to the canvas \\u2014 narrow for fine pleats, wide for broad soft folds\",ui:{type:[\"range\",\"map\"],min:.04,max:.4,step:.01,label:\"Width\",group:\"Folds\"}},pinning:{default:.6,description:\"How much the cloth is gathered at the top edge \\u2014 0 is a loose drape, 1 a curtain pinned at the rail with the folds opening as they fall\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Pinning\",group:\"Folds\"}},seed:{default:0,description:\"Random seed \\u2014 a different layout of folds\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Folds\"}},speed:{default:1,description:\"How fast the cloth moves. 0 pauses.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Motion\"}},breeze:{default:.4,description:\"Strength of the breeze \\u2014 how far the folds ripple and sway\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Breeze\",group:\"Motion\"}},gloss:{default:.1,description:\"How shiny the weave is \\u2014 0 soft matte satin, 1 glossy silk with a tight bright streak\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Gloss\",group:\"Finish\"}},anisotropy:{default:.7,description:\"How much the highlight stretches along the threads \\u2014 the long streak real satin shows\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Anisotropy\",group:\"Finish\"}},bloom:{default:.5,description:\"The broad soft glow around the highlight \\u2014 light the weave scatters sideways. 0 leaves the crisp streak alone, 1 a wide satin bloom\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Bloom\",group:\"Finish\"}},sheen:{default:.5,description:\"The velvet glow on the flanks of each fold where the fibres catch the light edge-on\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Sheen\",group:\"Finish\"}},weave:{default:.1,description:\"Visibility of the thread texture \\u2014 the fine grain that glints inside the highlight\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Weave\",group:\"Finish\"}},fiberAngle:{default:90,description:\"Direction the threads run, in degrees \\u2014 the highlight streaks along this axis. Usually the fold direction.\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Thread Angle\",group:\"Finish\"}},lightAngle:{default:215,description:\"Direction of the key light, in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Light Angle\",group:\"Light\"}},fill:{default:.35,description:\"Strength of the fill light from the opposite side \\u2014 lifts the shadowed flanks\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Fill\",group:\"Light\"}}},paint:e=>{let{uv:t,aspect:s}=aa(e),r=oe=>me(T(oe),e),o=z(wt(e),\"satinTime\"),a=z(rt(v(ne(t.member(\"x\"),.5),s),ne(t.member(\"y\"),.5)),\"q\"),i=ii(r(\"foldDirection\"),\"drape\"),l=Math.max(1,Math.min(12,Math.round(e.propValues.foldCount??7))),c=z(v(I(v(ms(i.perp.member(\"x\")),s),ms(i.perp.member(\"y\"))),1.1),\"spread\"),d=GD({count:l,seed:r(\"seed\"),spread:c,width:r(\"foldWidth\"),height:r(\"foldDepth\"),jitter:Z_e}),u=z(r(\"breeze\"),\"breeze\"),p={time:o,ripple:{waves:X_e,gain:et(.35,1,u)},sway:{amount:v(u,j_e),rate:.7},swell:H_e,pinning:z(r(\"pinning\"),\"pinning\"),sharpness:Q_e,crease:K_e},_=z(I(v(ms(i.tangent.member(\"x\")),s),ms(i.tangent.member(\"y\"))),\"alongExtent\"),f=oe=>{let le=i.coordsOf(oe);return{along:I(Ce(le.along,_),.5),across:le.across}},h=oe=>FD(f(oe),d,p),g=W9?null:vL(h,a,G9,\"cloth\"),y=g??bp(h,a,G9,\"cloth\"),b=Gi(Qe(0,0,-1),v(y.dx,-F9),v(y.dy,-F9),\"nFold\"),x=ID(f(a),d,{...p,foreshorten:J_e}),P=z(I(v(i.tangent,x.along),v(i.perp,x.across)),\"onCloth\"),C=ii(r(\"fiberAngle\"),\"fibre\"),E=C.coordsOf(P),R=yg(b,C,{across:v(z(I(Cc(E,{freq:I9.coarse}),v(Cc(E,{freq:I9.fine,offset:[3.1,7.7]}),.5)),\"threads\"),v(r(\"weave\"),tfe))}),U=Ea(e,{aspect:s}),L=Er(r(\"lightAngle\"),\"key\"),k=Ld(L,sf.elevation,\"L\"),D=Ld(er(L),sf.fillElevation,\"Lfill\"),O=z(I(I(sf.ambient,v(Pl(R,k,{wrap:.35}),sf.key)),v(Pl(R,D,{wrap:.6}),v(r(\"fill\"),sf.fill))),\"diffuse\"),F=g?z(Ge(ne(1,v(g.curvature,efe)),.6,1.1),\"cavity\"):1,q=z(r(\"gloss\"),\"gloss\"),Y=z(r(\"anisotropy\"),\"aniso\"),V=Ud({normal:R,tangent:C.tangent,light:k,view:U,alphas:V_(ne(1,q),Y),gain:et(Y9[0],Y9[1],q)}),X=Ud({normal:R,tangent:C.tangent,light:k,view:U,alphas:V_(l5.roughness,v(Y,l5.anisotropy)),gain:v(r(\"bloom\"),l5.gain)}),Q=TL({normal:R,light:k,view:U,roughness:et(.7,.3,q),gain:v(r(\"sheen\"),sfe)}),te=r(\"color\").member(\"rgb\"),J=r(\"highlightColor\").member(\"rgb\");return Ot(yp(I(v(te,v(O,F)),v(J,v(I(I(V,X),Q),F)))),1)}});var V9=H({name:\"Saturation\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Adjust color saturation intensity\",props:{intensity:{default:1,description:\"The intensity of the saturation effect (1 being no change)\",recompile:Lt(1),ui:{type:[\"range\",\"map\"],min:0,max:3,step:.1,label:\"Intensity\",group:\"Adjustments\"}}},effect:s8(T(\"intensity\")),identityWhen:cg(\"intensity\",1),missingChildMessage:\"You must pass a child component into the Saturation shader.\"});var N9=H({name:\"Scratches\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Fine hairline scratches, like a worn film or scratched surface\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...IO(),scale:{default:2,description:\"Pattern scale (higher = more, finer scratches)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},thickness:{default:1,description:\"Thickness of the scratches (higher = bolder streaks)\",ui:{type:\"range\",min:.2,max:5,step:.01,label:\"Thickness\",group:\"Effect\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},...In()},paint:Vs(ua(WD(jD({thickness:T(\"thickness\")}),{scale:T(\"scale\"),seed:T(\"seed\")})),QD(T(\"colorA\"),T(\"colorB\")))});var $9=H({name:\"Sharpness\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Adjust image sharpness using a convolution kernel\",props:{sharpness:{default:0,description:\"How sharp to make the underlying image\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Sharpness\",group:\"Adjustments\"}}},identityWhen:Uo(\"sharpness\"),effect:xU(T(\"sharpness\"))});var iy=e=>{let t=Math.sin(e)*1e4;return t-Math.floor(t)},Bl=16,rfe=1024,rf=Bl*4,ofe=S8(rf),X9=e=>{let t=new Float32Array(Bl*2);for(let s=0;s<Bl;s++)t[s*2]=iy(e+s*2),t[s*2+1]=iy(e+s*2+1);return t};function afe(e){let t=new Float32Array(Bl*4),s=new Float32Array(Bl*4),r=c=>{let d=X9(c);for(let u=0;u<Bl;u++)t[u*4]=d[u*2],t[u*4+1]=d[u*2+1],t[u*4+2]=iy(c+u*3),t[u*4+3]=iy(c+u*3+1)},o=e(\"seed\")??2;r(o);let a=1,i=.5,l=.5;return{cellData:t,displacementData:s,step(c,d,u){let p=Math.min(d,.016),_=e(\"seed\")??2;_!==o&&(o=_,r(o),s.fill(0));let f=e(\"intensity\")??4,h=e(\"radius\")??.4,g=e(\"decay\")??1,y=p>0?(c.x-i)/p:0,b=p>0?(c.y-l)/p:0,x=Math.sqrt(y*y+b*b),P=5+f*2;if(x>P){let C=P/x;y*=C,b*=C,x=P}for(let C=0;C<Bl;C++){let E=t[C*4],R=t[C*4+1],U=t[C*4+2]-.5,L=t[C*4+3]-.5,k=u>=1?(E-c.x)*u:E-c.x,D=u>=1?R-c.y:(R-c.y)/u,O=Math.sqrt(k*k+D*D),F=s[C*4],q=s[C*4+1],Y=Math.max(.01,g),V=Math.exp(-p/Y);F*=V,q*=V;let X=0,Q=0;if(O<h&&x>.01){let J=Math.max(0,1-O/h),oe=J*J,le=oe*x*f*p*.5;X=y*le,Q=b*le;let se=oe*x*f*p*.1;X+=U*se,Q+=L*se}let te=Math.min(1,a*p);s[C*4]=F+X*te,s[C*4+1]=q+Q*te}i=c.x,l=c.y}}}function nfe(e,t){let{createDataTexture:s,registerMediaTexture:r,onCleanup:o}=e,a=new Float32Array(rf),i=new Uint16Array(rf),l=()=>{for(let d=0;d<Bl;d++)a[d*4]=t.cellData[d*4],a[d*4+1]=t.cellData[d*4+1],a[d*4+2]=t.displacementData[d*4],a[d*4+3]=t.displacementData[d*4+1];for(let d=0;d<rf;d++)i[d]=_o(a[d])};l();let c=s({width:rf,height:1,format:\"r16float\",data:i,label:\"shatter-cells\"});return o(()=>c.destroy()),{dataKit:r(()=>c.texture),upload(){l(),c.write(i)}}}var H9=H({name:\"Shatter\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Broken glass effect with tectonic plate displacement\",requiresRTT:!0,requiresChild:!0,usesPointer:!0,props:{crackWidth:{default:1,description:\"Thickness of crack lines\",ui:{type:[\"range\",\"map\"],min:.5,max:5,step:.1,label:\"Crack Width\",group:\"Effect\"}},intensity:{default:4,description:\"How much shards shift\",ui:{type:\"range\",min:0,max:20,step:1,label:\"Intensity\",group:\"Effect\"}},radius:{default:.4,description:\"Cursor influence radius\",ui:{type:\"range\",min:.1,max:1,step:.1,label:\"Radius\",group:\"Effect\"}},decay:{default:1,description:\"How fast shards return to rest\",ui:{type:\"range\",min:.1,max:10,step:.1,label:\"Decay\",group:\"Effect\"}},seed:{default:2,description:\"Random seed for pattern\",ui:{type:\"range\",min:0,max:50,step:1,label:\"Seed\",group:\"Effect\"}},chromaticSplit:{default:1,description:\"RGB separation for prismatic glass effect\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Chromatic Split\",group:\"Effect\"}},refractionStrength:{default:5,description:\"How much cracks bend/distort the underlying image\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Refraction\",group:\"Effect\"}},shardLighting:{default:.1,description:\"Subtle lighting on tilted shards for 3D depth\",ui:{type:\"range\",min:0,max:.5,step:.1,label:\"Shard Lighting\",group:\"Effect\"}},edges:{default:\"mirror\",description:\"How to handle edges when displacement pushes content out of bounds\",transform:Fr,compileTime:!0,ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Effect\"}}},compute:E8({count:Bl,size:rfe,sites:X9,seedProp:\"seed\",seedDefault:2,output:\"voronoiField\"}),gpu:{fragment:e=>{let{childNode:t,ctx:s,computeOutputs:r,propValues:o,convertToTexture:a}=e;if(!t)return xt;let i=a(t),l=r?.voronoiField;if(!l)return W(Yt,\"unpremultiplyAlpha\",[i.sample(s.uv)]);let{onBeforeRender:c,getCpuValue:d,dimensions:u}=e,p=o.edges??0,_=afe(d),f=nfe(e,_),h=f.dataKit;c(F=>{let{pointer:q,deltaTime:Y}=F,V=u.width/Math.max(1,u.height);_.step(q,Y,V),f.upload()});let g=l.sample(s.uv,\"nearestClamp\"),y=g.member(\"r\"),b=g.member(\"g\"),x=\"nearestClamp\",P=(F,q)=>h.sample(W(ofe,\"cellLaneUV\",[F,$e(q)]),x).member(\"r\"),C=W(P8,\"crackGeom\",[s.uv,P(y,0),P(y,1),P(b,0),P(b,1),P(y,2),P(y,3),e.uniforms.crackWidth]),E=C.member(\"crackIntensity\"),R=C.member(\"edgeNormal\"),U=C.member(\"displacedUV\"),L=C.member(\"disp\"),k=F=>Ha(F,q=>i.sample(q),p),D=k(U),O=F=>W(C8,\"crackRefractUV\",[U,R,E,e.uniforms.refractionStrength,e.uniforms.chromaticSplit,$e(F)]);return W(Yt,\"unpremultiplyAlpha\",[W(M8,\"crackShardCompose\",[D,k(O(1)),k(O(0)),k(O(-1)),E,L,e.uniforms.shardLighting])])}}});var j9=H({name:\"SimplexNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Organic noise with animated movement\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:2,description:\"Pattern scale (higher = larger patterns)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},balance:{default:0,description:\"Balance between colors (negative = more colorB, positive = more colorA)\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.1,label:\"Balance\",group:\"Effect\"}},contrast:{default:0,description:\"Pattern contrast (higher = sharper transitions)\",ui:{type:[\"range\",\"map\"],min:-2,max:5,step:.1,label:\"Contrast\",group:\"Effect\"}},seed:{default:0,description:\"Random seed for pattern variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},speed:{default:1,description:\"Animation speed\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}}},paint:Vs(KD(Qa(hp(Tc(\"mx3signed\"),{rate:.5}),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var K9=H({name:\"SineWave\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Animated wave with thickness and softness\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{color:{default:\"#ffffff\",transform:ce,description:\"The color of the sine wave\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},amplitude:{default:.15,description:\"The height/amplitude of the sine wave\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Amplitude\",group:\"Effect\"}},frequency:{default:1,description:\"The frequency/number of wave cycles\",ui:{type:[\"range\",\"map\"],min:.1,max:20,step:.1,label:\"Frequency\",group:\"Effect\"}},speed:{default:1,description:\"The animation speed of the wave\",ui:{type:\"range\",min:-5,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},angle:{default:0,transform:cs,description:\"The rotation angle of the wave (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},position:{default:{x:.5,y:.5},transform:Xe,description:\"The center position of the wave\",ui:{type:\"position\",label:\"Position\",group:\"Position\"}},thickness:{default:.2,description:\"The thickness of the wave line\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Thickness\",group:\"Effect\"}},softness:{default:.4,description:\"Edge softness of the wave line\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Softness\",group:\"Effect\"}}},paint:DL({position:T(\"position\"),angle:T(\"angle\"),frequency:T(\"frequency\"),amplitude:T(\"amplitude\"),thickness:T(\"thickness\"),softness:T(\"softness\"),color:T(\"color\")})});var Q9=H({name:\"SliceWipe\",role:\"warp\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Slice the content into strips that slide away in alternating directions\",props:{progress:{default:.5,description:\"How far the strips have slid (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},angle:{default:0,description:\"Orientation of the strips in degrees (0 = vertical strips sliding up and down)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Transition\"}},sliceCount:{default:8,description:\"Number of strips across the frame\",ui:{type:[\"range\",\"map\"],min:2,max:60,step:1,label:\"Slices\",group:\"Transition\"}}},map:oU({angle:T(\"angle\"),count:T(\"sliceCount\"),progress:T(\"progress\")}),edges:1,resample:\"bilinear\",missingChildMessage:\"You must pass a child component into the Slice Wipe shader.\"});var Ba=256,qd=Ba*Ba,ife=10,lfe=.2,cfe=\"rgba16float\",Z9=Mn,J9=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,emitX:n,emitY:n,emitVelX:n,emitVelY:n,perpDirX:n,perpDirY:n,spreadFactor:n,emitRad:n,emitIntensity:n,dyeFade:n,velFade:n,curlStrength:n,gravity:n,cursorX:n,cursorY:n,cursorVelX:n,cursorVelY:n,mouseActive:n,mouseRadSq:n,colorDecay:n}),\"FluidParams\"),c5=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,qd),access:\"mutable\"},velB:{storage:Fe(S,qd),access:\"mutable\"},dyeA:{storage:Fe(S,qd),access:\"mutable\"},dyeB:{storage:Fe(S,qd),access:\"mutable\"},pressure:{storage:Fe(n,qd),access:\"mutable\"},divergence:{storage:Fe(n,qd),access:\"mutable\"},params:{uniform:J9}}),\"fluidLayout\"),eY=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({dyeA:{storage:Fe(S,qd),access:\"readonly\"},outTex:{storageTexture:zt(cfe,\"write-only\")}}),\"outputLayout\"),dfe=pp(c5,{n:Ba,namePrefix:\"smoke\",cone:!0,cursorPush:!0,densityGain:8,velocityBlendGain:3}),tY=zn(c5,{n:Ba,namePrefix:\"smoke\",boundary:\"clamped\",dye:\"densityAge\",dyeDissipation:!0,ageAdvance:!0}),{curl:W6e,vorticity:q6e,divergence:V6e,jacobi:N6e,gradSubtract:$6e,advectVel:X6e,copyVel:H6e,advectDye:j6e,copyDye:K6e}=tY,ufe=Ui(eY,{n:Ba,namePrefix:\"smoke\",dye:\"densityAge\"}),sY=H({name:\"Smoke\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Realistic fluid smoke simulation with vorticity dynamics\",usesPointer:!0,props:{colorA:{default:\"#fc83f9\",transform:ce,description:\"Color of fresh smoke\",ui:{type:\"color\",label:\"Start Color\",group:\"Colors\"}},colorB:{default:\"#c21c79\",transform:ce,description:\"Color smoke transitions to as it ages\",ui:{type:\"color\",label:\"End Color\",group:\"Colors\"}},stops:Ms(),emitFrom:{default:{x:.5,y:1},transform:Xe,description:\"The emission source point\",ui:{type:\"position\",label:\"Emit From\",group:\"Emission\"}},direction:{default:0,description:\"Emission direction (0 = up, 90 = right, 180 = down, 270 = left)\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Direction\",group:\"Emission\"}},speed:{default:20,description:\"Emission velocity strength\",ui:{type:\"range\",min:.1,max:50,step:.1,label:\"Speed\",group:\"Emission\"}},spread:{default:60,description:\"Emission cone angle in degrees\",ui:{type:\"range\",min:0,max:180,step:1,label:\"Spread\",group:\"Emission\"}},emitRadius:{default:.08,description:\"Size of the emission area\",ui:{type:\"range\",min:.01,max:.3,step:.01,label:\"Emit Size\",group:\"Emission\"}},intensity:{default:1,description:\"Smoke emission density\",ui:{type:\"range\",min:.1,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}},dissipation:{default:.2,description:\"How fast smoke fades over time\",ui:{type:\"range\",min:.1,max:3,step:.1,label:\"Dissipation\",group:\"Effect\"}},detail:{default:25,description:\"Fine-scale swirling detail\",ui:{type:\"range\",min:0,max:50,step:1,label:\"Detail\",group:\"Effect\"}},gravity:{default:.5,description:\"Downward gravitational pull on smoke\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Gravity\",group:\"Effect\"}},colorDecay:{default:.4,description:\"How quickly smoke shifts from Color A to Color B\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Color Decay\",group:\"Colors\"}},mouseInfluence:{default:.1,description:\"Strength of cursor influence\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Cursor Influence\",group:\"Interaction\"}},mouseRadius:{default:.1,description:\"Radius of cursor influence area\",ui:{type:\"range\",min:.02,max:.5,step:.01,label:\"Cursor Radius\",group:\"Interaction\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},...Ii(()=>({resolution:Ba,layout:c5,outputLayout:eY,paramsSchema:J9,solver:{kernels:tY,jacobiIters:ife},output:{kernel:ufe,key:\"smokeTexture\"},pointer:{minDrag:5e-4},inject:[Ug({kernel:dfe})],values:e=>{let t=e.num(\"speed\",3),s=e.num(\"direction\",0)*Math.PI/180,r=Math.min(89*Math.PI/180,e.num(\"spread\",45)*Math.PI/360),o=e.getCpuValue(\"emitFrom\"),a=o?.x??.5,i=o?.y??0,l=t*Ba*.15,c=e.num(\"mouseInfluence\",.5),d=e.num(\"mouseRadius\",.12),u=e.ptr,p=d*Ba,_=u.moving&&c>0;return{dt:e.dt,emitX:a*Ba,emitY:(1-i)*Ba,emitVelX:Math.sin(s)*l,emitVelY:-Math.cos(s)*l,perpDirX:Math.cos(s),perpDirY:Math.sin(s),spreadFactor:Math.tan(r),emitRad:e.num(\"emitRadius\",.06)*Ba,emitIntensity:e.num(\"intensity\",.7),dyeFade:e.num(\"dissipation\",1),velFade:lfe,curlStrength:e.num(\"detail\",30),gravity:e.num(\"gravity\",.5),colorDecay:e.num(\"colorDecay\",1),cursorX:_?u.x*Ba:0,cursorY:_?u.y*Ba:0,cursorVelX:_?u.dx*Ba*15*c:0,cursorVelY:_?u.dy*Ba*15*c:0,mouseActive:_?1:0,mouseRadSq:p*p}}})),gpu:{fragment:({ctx:e,computeOutputs:t,propValues:s,uniforms:r})=>{let o=t?.smokeTexture;if(!o)return xt;let a=o.sample(e.uv,\"linearClamp\"),i=a.member(\"x\"),l=a.member(\"y\"),c=s.colorSpace??0,d=s.stopCount??0,u;return d>1?u=En(l,{colorsArray:r.colorsArray,positionsArray:r.positionsArray,convertedColorsArray:r.convertedColorsArray,stopCount:r.stopCount},c):u=W(Z9.mixColorsVariants[c]??Z9.mixColorsLinear,\"mixColors\",[r.colorA,r.colorB,l]),Ct(u.member(\"rgb\"),u.member(\"a\").mul(i))}}});var{sdfSpaceUV:pfe}=ip,{createSvgSdfSampler:mfe,createSdfDataTexture:_fe,createAnalyticSdfSampler:ffe,driveAnalyticSubProps:vfe,ANALYTIC_SDF_EXTRA_FIELDS:hfe}=yc,{createVolumetricFieldComputeNode:gfe,buildVolumetricFieldSampler:yfe,VOLUMETRIC_FIELD_EXTRA_FIELDS:bfe}=kn,xfe=JSON.stringify({type:\"sphere3D\",radius:.35}),na=256,Lc=na*na,Afe=10,wfe=.2,Tfe=\"rgba16float\",rY=Mn,d5=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,emitX:n,emitY:n,emitVelX:n,emitVelY:n,perpDirX:n,perpDirY:n,spreadFactor:n,emitRad:n,emitIntensity:n,dyeFade:n,velFade:n,curlStrength:n,gravity:n,colorDecay:n,centerX:n,centerY:n,scale:n,rotation:n,aspect:n,cursorX:n,cursorY:n,cursorVelX:n,cursorVelY:n,mouseActive:n,mouseRadSq:n,saRadius:n,saSides:n,saRounding:n,saInnerRatio:n,saRotation:n,saHeight:n,saOffset:n,saAperture:n,vfOriginX:n,vfOriginY:n,vfSpanX:n,vfSpanY:n,vfActiveRes:n}),\"FillParams\"),u5=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,Lc),access:\"mutable\"},velB:{storage:Fe(S,Lc),access:\"mutable\"},dyeA:{storage:Fe(S,Lc),access:\"mutable\"},dyeB:{storage:Fe(S,Lc),access:\"mutable\"},pressure:{storage:Fe(n,Lc),access:\"mutable\"},divergence:{storage:Fe(n,Lc),access:\"mutable\"},maskBuf:{storage:Fe(S,Lc),access:\"readonly\"},params:{uniform:d5}}),\"fluidLayout\"),oY=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({dyeA:{storage:Fe(S,Lc),access:\"readonly\"},outTex:{storageTexture:zt(Tfe,\"write-only\")}}),\"outputLayout\"),Rp=Ez(d5,{n:na,namePrefix:\"smokeFill\"}),Sfe=Rp.analytic,Pfe=Rp.svgKernel,Cfe=Rp.fieldKernel,Mfe=pp(u5,{n:na,namePrefix:\"smokeFill\",cone:!0,cursorPush:!0,masked:!0,densityGain:8,velocityBlendGain:3}),aY=zn(u5,{n:na,namePrefix:\"smokeFill\",boundary:\"clamped\",dye:\"densityAge\",dyeDissipation:!0,ageAdvance:!0,solidMask:!0}),{curl:ewe,vorticity:twe,divergence:swe,jacobi:rwe,gradSubtract:owe,advectVel:awe,copyVel:nwe,advectDye:iwe,copyDye:lwe}=aY,Efe=Ui(oY,{n:na,namePrefix:\"smokeFill\",dye:\"densityAge\"});function kfe(e){let{getCpuValue:t}=e,s=t(\"shapeSdfUrl\")||\"\",r=bc(t(\"shapeType\")||\"\",t(\"shape\")),o=gfe(e,()=>t(\"shape\"),\"none\");return{setup(a){let{root:i}=a,l=a.buffers.maskBuf,c=a.paramsBuffer,d;if(o){let u=i.createBindGroup(Rp.fieldLayout,{maskBuf:l,params:c,fieldTex:o.fieldTexture});d=a.pass(Cfe,u)}else if(s){let u=_fe(a.params,s),p=i.createBindGroup(Rp.svgLayout,{maskBuf:l,params:c,sdfSource:u.texture.texture});d=a.pass(Pfe,p)}else{let u=i.createBindGroup(Rp.analyticLayout,{maskBuf:l,params:c});d=a.pass(Sfe(r),u)}return{maskPass:d,outputs:o?o.outputs:{},preFrame:u=>o?.getComputeNodes(u)??null}},domain:()=>o?.getFieldDomain()}}function Bfe(e){let t=(s,r)=>typeof s==\"number\"?s:r;return()=>{let s=e(\"shape\"),r={};if(s&&typeof s==\"object\")r=s;else if(typeof s==\"string\")try{r=JSON.parse(s)}catch{r={}}return{saRadius:t(r.radius,t(r.width,t(r.bottomWidth,.35))),saSides:t(r.sides,6),saRounding:t(r.rounding,0),saInnerRatio:t(r.innerRatio,t(r.thickness,t(r.spread,t(r.topWidth,t(r.topRatio,.4))))),saRotation:t(r.rotation,0),saHeight:t(r.height,.25),saOffset:t(r.offset,t(r.skew,.2)),saAperture:t(r.aperture,270)}}}var nY=H({name:\"SmokeFill\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Fill a shape with swirling fluid smoke that interacts with the shape boundary\",requiresRTT:!1,requiresChild:!1,usesPointer:!0,extraFields:{...bfe,...hfe},props:{colorA:{default:\"#8cf3ff\",transform:ce,description:\"Color of fresh smoke\",ui:{type:\"color\",label:\"Start Color\",group:\"Colors\"}},colorB:{default:\"#04a0d6\",transform:ce,description:\"Color smoke transitions to as it ages\",ui:{type:\"color\",label:\"End Color\",group:\"Colors\"}},origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},emitFrom:{default:{x:.5,y:.5},transform:Xe,description:\"Emission source point within the shape\",ui:{type:\"position\",label:\"Emit From\",group:\"Emission\"}},direction:{default:0,description:\"Emission direction (0 = up, 90 = right, 180 = down, 270 = left)\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Direction\",group:\"Emission\"}},speed:{default:10,description:\"Emission velocity strength\",ui:{type:\"range\",min:.1,max:30,step:.1,label:\"Speed\",group:\"Emission\"}},spread:{default:60,description:\"Emission cone angle in degrees\",ui:{type:\"range\",min:0,max:180,step:1,label:\"Spread\",group:\"Emission\"}},emitRadius:{default:.03,description:\"Size of the emission area\",ui:{type:\"range\",min:.01,max:.3,step:.01,label:\"Emit Size\",group:\"Emission\"}},intensity:{default:1,description:\"Smoke emission density\",ui:{type:\"range\",min:.1,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}},dissipation:{default:.3,description:\"How fast smoke fades over time\",ui:{type:\"range\",min:.1,max:5,step:.1,label:\"Dissipation\",group:\"Effect\"}},detail:{default:25,description:\"Fine-scale swirling detail\",ui:{type:\"range\",min:0,max:50,step:1,label:\"Detail\",group:\"Effect\"}},gravity:{default:.5,description:\"Downward gravitational pull on smoke \\u2014 0 = weightless, negative values = smoke rises\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Gravity\",group:\"Effect\"}},colorDecay:{default:.4,description:\"How quickly smoke shifts from Color A to Color B\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Color Decay\",group:\"Colors\"}},mouseInfluence:{default:.1,description:\"Strength of cursor influence\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Cursor Influence\",group:\"Interaction\"}},mouseRadius:{default:.1,description:\"Radius of cursor influence area\",ui:{type:\"range\",min:.02,max:.5,step:.01,label:\"Cursor Radius\",group:\"Interaction\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},shape:{default:xfe,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ii(e=>{let{getCpuValue:t}=e,s=kfe(e),r=Bfe(t);return{resolution:na,layout:u5,outputLayout:oY,paramsSchema:d5,solver:{kernels:aY,jacobiIters:Afe},output:{kernel:Efe,key:\"smokeTexture\"},pointer:{minDrag:5e-4},container:s,inject:[Ug({kernel:Mfe})],values:o=>{let a=o.num(\"speed\",2),i=o.num(\"direction\",0)*Math.PI/180,l=Math.min(89*Math.PI/180,o.num(\"spread\",60)*Math.PI/360),c=t(\"center\"),d=t(\"emitFrom\"),u=o.frameParams.dimensions?.height??0,p=u>0?(o.frameParams.dimensions?.width??0)/u:1,_=a*na*.15,f=r(),h=o.num(\"mouseInfluence\",.5),g=o.num(\"mouseRadius\",.12),y=o.ptr,b=y.moving&&h>0,x=g*na,P=s.domain();return{dt:o.dt,emitX:(d?.x??.5)*na,emitY:(1-(d?.y??.5))*na,emitVelX:Math.sin(i)*_,emitVelY:-Math.cos(i)*_,perpDirX:Math.cos(i),perpDirY:Math.sin(i),spreadFactor:Math.tan(l),emitRad:o.num(\"emitRadius\",.03)*na,emitIntensity:o.num(\"intensity\",.8),dyeFade:o.num(\"dissipation\",.5),velFade:wfe,curlStrength:o.num(\"detail\",30),gravity:o.num(\"gravity\",.5),colorDecay:o.num(\"colorDecay\",1),centerX:c?.x??.5,centerY:1-(c?.y??.5),scale:o.num(\"scale\",1),rotation:o.num(\"rotation\",0)*Math.PI/180,aspect:p,cursorX:b?y.x*na:0,cursorY:b?y.y*na:0,cursorVelX:b?y.dx*na*15*h:0,cursorVelY:b?y.dy*na*15*h:0,mouseActive:b?1:0,mouseRadSq:x*x,...f,vfOriginX:P?.originX??0,vfOriginY:P?.originY??0,vfSpanX:P?.spanX??1,vfSpanY:P?.spanY??1,vfActiveRes:P?.activeRes??1}}}}),gpu:{fragment:e=>{let{ctx:t,computeOutputs:s,propValues:r,uniforms:o,getCpuValue:a}=e,i=s?.smokeTexture;if(!i)return xt;let l,c=s?.volumetricFieldTexture;if(c)l=yfe(c,{originX:o._vfOriginX,originY:o._vfOriginY,spanX:o._vfSpanX,spanY:o._vfSpanY,activeResF:o._vfActiveRes,rBound:o._vfRBound}).volumetricFieldSampler;else{let y=r.shapeSdfUrl||\"\";y?l=mfe(e,y):l=ffe(bc(r.shapeType||\"\",a(\"shape\")),vfe(e,()=>a(\"shape\")))}let d=W(pfe,\"sdfSpaceUV\",[o.center,o.scale,o.rotation,t.uv,t.aspect]),u=Ge(v(l(d).member(\"x\"),-(na*4)),0,1),p=i.sample(t.uv,\"linearClamp\"),_=p.member(\"x\"),f=p.member(\"y\"),h=r.colorSpace??0,g=W(rY.mixColorsVariants[h]??rY.mixColorsLinear,\"mixColors\",[o.colorA,o.colorB,f]);return Ct(g.member(\"rgb\"),g.member(\"a\").mul(_).mul(u))}}});var ci=256,Vd=ci*ci,zfe=10,Rfe=.2,Dfe=16,Lfe=\"rgba16float\",iY=Mn,lY=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({dt:n,emitX:n,emitY:n,emitVelX:n,emitVelY:n,emitGate:n,emitRad:n,emitIntensity:n,dyeFade:n,velFade:n,curlStrength:n,gravity:n,colorDecay:n}),\"FlowParams\"),p5=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({velA:{storage:Fe(S,Vd),access:\"mutable\"},velB:{storage:Fe(S,Vd),access:\"mutable\"},dyeA:{storage:Fe(S,Vd),access:\"mutable\"},dyeB:{storage:Fe(S,Vd),access:\"mutable\"},pressure:{storage:Fe(n,Vd),access:\"mutable\"},divergence:{storage:Fe(n,Vd),access:\"mutable\"},params:{uniform:lY}}),\"fluidLayout\"),cY=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(w.bindGroupLayout({dyeA:{storage:Fe(S,Vd),access:\"readonly\"},outTex:{storageTexture:zt(Lfe,\"write-only\")}}),\"outputLayout\"),Ufe=pp(p5,{n:ci,namePrefix:\"smokeFlow\",cone:!1,cursorPush:!1,gated:!0,densityGain:10,velocityBlendGain:4}),dY=zn(p5,{n:ci,namePrefix:\"smokeFlow\",boundary:\"clamped\",dye:\"densityAge\",dyeDissipation:!0,ageAdvance:!0}),{curl:pwe,vorticity:mwe,divergence:_we,jacobi:fwe,gradSubtract:vwe,advectVel:hwe,copyVel:gwe,advectDye:ywe,copyDye:bwe}=dY,Ofe=Ui(cY,{n:ci,namePrefix:\"smokeFlow\",dye:\"densityAge\"}),uY=H({name:\"SmokeFlow\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Interactive\",description:\"Cursor-driven smoke that lingers, swirls, and dissipates with fluid dynamics\",usesPointer:!0,props:{colorA:{default:\"#e29c8b\",transform:ce,description:\"Color of fresh smoke\",ui:{type:\"color\",label:\"Fresh Color\",group:\"Colors\"}},colorB:{default:\"#d517f9\",transform:ce,description:\"Color smoke transitions to as it ages\",ui:{type:\"color\",label:\"Aged Color\",group:\"Colors\"}},stops:Ms(),intensity:{default:1,description:\"How much smoke is emitted as you move the cursor\",ui:{type:\"range\",min:.1,max:2,step:.05,label:\"Intensity\",group:\"Emission\"}},emitRadius:{default:.07,description:\"Size of smoke puff emitted at cursor\",ui:{type:\"range\",min:.01,max:.3,step:.01,label:\"Puff Size\",group:\"Emission\"}},momentum:{default:20,description:\"How much cursor velocity is transferred into the smoke (higher = more directed flow)\",ui:{type:\"range\",min:0,max:50,step:1,label:\"Momentum\",group:\"Emission\"}},dissipation:{default:.5,description:\"How fast smoke fades over time\",ui:{type:\"range\",min:.05,max:3,step:.05,label:\"Dissipation\",group:\"Effect\"}},detail:{default:10,description:\"Fine-scale swirling detail driven by vorticity confinement\",ui:{type:\"range\",min:0,max:60,step:1,label:\"Swirl Detail\",group:\"Effect\"}},gravity:{default:2,description:\"Vertical drift \\u2014 negative floats up, positive sinks down\",ui:{type:\"range\",min:-5,max:5,step:.05,label:\"Gravity\",group:\"Effect\"}},colorDecay:{default:.5,description:\"How quickly smoke shifts from fresh to aged color\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Color Decay\",group:\"Colors\"}},colorSpace:{default:\"OKLAB\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},...Ii(()=>{let e=s=>Math.min(s*s*60,1),t=Og({tracker:{teleportGuard:Number.POSITIVE_INFINITY},activeWhen:s=>e(s.smoothSpeed)>.005,fadeSeconds:s=>jh(255,s.num(\"dissipation\",.4)),pass:{kernel:Ufe},ribbon:(s,r,o)=>({stepSize:Math.max(.004,s.num(\"emitRadius\",.07)*.5),maxSteps:Dfe,write:(a,i)=>s.writeParams({...o,emitX:a,emitY:i})})});return{resolution:ci,layout:p5,outputLayout:cY,paramsSchema:lY,solver:{kernels:dY,jacobiIters:zfe},output:{kernel:Ofe,key:\"smokeTexture\"},inject:[t],values:s=>{let r=t.ptr(),o=s.num(\"momentum\",1.2),a=e(r.smoothSpeed),i=t.active(),l=s.num(\"emitRadius\",.07);return{dt:s.dt,emitX:r.x*ci,emitY:r.y*ci,emitGate:i?a:0,emitVelX:r.smoothVelX*ci*o*.12,emitVelY:r.smoothVelY*ci*o*.12,emitRad:l*ci,emitIntensity:s.num(\"intensity\",1.2),dyeFade:s.num(\"dissipation\",.4),velFade:Rfe,curlStrength:s.num(\"detail\",28),gravity:s.num(\"gravity\",-.3),colorDecay:s.num(\"colorDecay\",.5)}}}}),gpu:{fragment:({ctx:e,computeOutputs:t,propValues:s,uniforms:r})=>{let o=t?.smokeTexture;if(!o)return xt;let a=o.sample(e.uv,\"linearClamp\"),i=a.member(\"x\"),l=a.member(\"y\"),c=s.colorSpace??0,d=s.stopCount??0,u;return d>1?u=En(l,{colorsArray:r.colorsArray,positionsArray:r.positionsArray,convertedColorsArray:r.convertedColorsArray,stopCount:r.stopCount},c):u=W(iY.mixColorsVariants[c]??iY.mixColorsLinear,\"mixColors\",[r.colorA,r.colorB,l]),Ct(u.member(\"rgb\"),u.member(\"a\").mul(i))}}});var pY=H({name:\"Solarize\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Inverts tones above a luminance threshold \\u2014 a classic darkroom and photo effect\",props:{threshold:{default:.5,description:\"Luminance level above which colors are inverted. Pixels brighter than this threshold get flipped.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Threshold\",group:\"Effect\"}},strength:{default:1,description:\"Blend between original (0) and fully solarized (1)\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}}},effect:l8({threshold:T(\"threshold\"),strength:T(\"strength\")}),identityWhen:Uo(\"strength\"),missingChildMessage:\"You must pass a child component into the Solarize shader.\"});var mY=H({name:\"SolidColor\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null},category:\"Textures\",description:\"Fill the canvas with a single solid color\",acceptsUVContext:!0,props:{color:{default:\"#5b18ca\",transform:ce,description:\"The solid color to display\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}}},paint:GR(T(\"color\"))});var Gfe=[1,2/3],Ffe=.12,ly={core:.9,rayWidth:1.6,rayLength:.45,rays:.25},m5={minRate:.8,rateSpread:1.2,sharpness:8},Ife=.45,_Y=.35,Yfe=.6,Wfe=[.2126,.7152,.0722],qfe=4;function Vfe(e,t,s,r,o){let a=e.uniforms,i=z(v(a.size,s),`sparkleCell${r}`),l=(e.propValues.expand??0)>0,c=z(Ce(rt(a.expand,a.expand),e.ctx.logicalViewportSize),`sparkleExpand${r}`),d=z(Ce(ly.rayLength,it(a.rayLength,.01)),`sparkleRays${r}`);return qR(t,{cellSize:i,jitter:.6,seed:I(a.seed,r*.31)}).map(({point:u,offset:p,random:_})=>{let f=mU(e.sampleStraight,Ce(u,e.ctx.logicalViewportSize),{radius:c,taps:l?qfe:0,angle:r*45}),h=ot(a.threshold,I(a.threshold,Ffe),ho(f.luma)),g=cU(o,{rate:I(m5.minRate,v(_.member(\"z\"),m5.rateSpread)),offset:v(_.member(\"x\"),6.283185307179586),sharpness:m5.sharpness}),y=I(_Y,v(1-_Y,ag(Ife,_.member(\"z\")))),b=TD(p,{core:ly.core,rayWidth:ly.rayWidth,rayLength:d,rays:ly.rays,reach:v(i,.5)}),x=z(f.color.member(\"rgb\"),\"sparkleRgb\"),P=v(Ce(x,it($t(x,Qe(...Wfe)),.001)),Yfe);return v(et(kt(1),P,a.colorize),v(v(v(b,g),h),v(y,a.intensity)))}).reduce((u,p)=>I(u,p))}var fY=H({name:\"Sparkle\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Twinkling star glints over the bright parts of the layer inside\",animatedTime:{speed:\"speed\"},props:{size:{default:100,description:\"Spacing between glints, in CSS pixels (smaller means more glints)\",ui:{type:[\"range\",\"map\"],min:10,max:200,step:1,label:\"Spacing\",group:\"Effect\"}},intensity:{default:10,description:\"Brightness of the glints\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Intensity\",group:\"Effect\"}},threshold:{default:.2,description:\"How bright the layer must be before glints appear: 0 allows all but black, 0.5 is mid-grey and up, 1 only pure white\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Threshold\",group:\"Effect\"}},expand:{default:0,description:\"Lets glints appear up to this many CSS pixels outside the bright areas\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Expand\",group:\"Effect\"}},rayLength:{default:5,description:\"Length of the four rays around each glint (they fade out by half the spacing)\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Ray Length\",group:\"Effect\"}},colorize:{default:.45,description:\"How much each glint takes on the colour beneath it (0 is pure white)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Colorize\",group:\"Effect\"}},speed:{default:1,description:\"How fast the glints twinkle\",ui:{type:\"range\",min:0,max:5,step:.05,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Re-rolls where the glints sit and when they blink\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}}},identityWhen:Uo(\"intensity\"),effect:$o(e=>{let t=z(v(e.ctx.uv,e.ctx.logicalViewportSize),\"sparklePx\"),s=z(wt(e),\"sparkleT\"),r=z(Gfe.map((i,l)=>Vfe(e,t,i,l,s)).reduce((i,l)=>I(i,l)),\"sparkleGlow\"),o=z(e.texture.sample(e.ctx.uv),\"sparkleChild\"),a=it(it(r.member(\"r\"),r.member(\"g\")),r.member(\"b\"));return Ot(I(o.member(\"rgb\"),r),Yr(I(o.member(\"a\"),a),1))},[]),missingChildMessage:\"You must pass a child component into the Sparkle shader.\"});var vY=wU({center:T(\"center\"),radius:T(\"radius\"),depth:T(\"depth\")}),hY=H({name:\"Spherize\",role:\"filter\",species:\"gather\",category:\"Distortions\",description:\"Map content onto a 3D sphere surface with depth distortion\",boundingBoxDeclaration:{aspectRatio:null},props:{radius:{default:1,description:\"Radius of the sphere (1 = half viewport height)\",ui:{type:[\"range\",\"map\"],min:.1,max:3,step:.1,label:\"Radius\",group:\"Effect\"}},depth:{default:1,description:\"How much the sphere bulges toward viewer (0 = flat, higher = more bulge)\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.1,label:\"Depth\",group:\"Effect\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the sphere\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},lightPosition:{default:{x:.3,y:.3},transform:Xe,description:\"Position of the specular light source\",ui:{type:\"position\",label:\"Light Position\",group:\"Position\"}},lightIntensity:{default:.5,description:\"Intensity of the rim light (0 = off)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Light Intensity\",group:\"Effect\"}},lightSoftness:{default:.5,description:\"Softness of the rim light falloff (0 = hard edge, 1 = soft glow)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Light Softness\",group:\"Effect\"}},lightColor:{default:\"#ffffff\",transform:ce,description:\"Color of the specular highlight\",ui:{type:\"color\",label:\"Light Color\",group:\"Effect\"}}},effect:$o(TU(vY),[SU(vY,{position:T(\"lightPosition\"),intensity:T(\"lightIntensity\"),softness:T(\"lightSoftness\"),color:T(\"lightColor\")})],{resultAlpha:\"straight\"})});var gY=H({name:\"Spiral\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Rotating spiral pattern with animated movement\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#000000\",transform:ce,description:\"Background color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Spiral stroke color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},strokeWidth:{default:.5,description:\"Thickness of spiral stroke\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.1,label:\"Stroke Width\",group:\"Effect\"}},strokeFalloff:{default:0,description:\"Stroke losing width further from center\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Stroke Falloff\",group:\"Effect\"}},softness:{default:0,description:\"Color transition sharpness (0 = hard edge, 1 = smooth fade)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Softness\",group:\"Effect\"}},speed:{default:1,description:\"Animation speed (negative values reverse direction)\",ui:{type:\"range\",min:-3,max:3,step:.1,label:\"Speed\",group:\"Animation\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"The center point of the spiral\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},scale:{default:1,description:\"Scale factor for spiral bands (higher = more bands, lower = fewer bands)\",ui:{type:[\"range\",\"map\"],min:.1,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Vs(Oi.spiral({center:T(\"center\"),scale:T(\"scale\"),width:T(\"strokeWidth\"),falloff:T(\"strokeFalloff\"),softness:T(\"softness\")}),xR(T(\"colorA\"),T(\"colorB\"),T(\"colorSpace\")))});var yY=H({role:\"shape\",name:\"Star\",description:\"Classic star polygon with straight sides and sharp pointed tips\",shape:{distance:({x:e,y:t},s)=>cR(e,t,s(\"radius\"),s(\"sides\"),s(\"innerRatio\")),colorDescription:\"Fill color of the star\",centerDescription:\"Center position of the star\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.4,description:\"Outer tip radius \\u2014 distance from center to the pointed tips\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Radius\",group:\"Shape\"}},sides:{default:5,description:\"Number of points on the star\",ui:{type:\"range\",min:3,max:12,step:1,label:\"Points\",group:\"Shape\"}},innerRatio:{default:.4,description:\"Inner vertex radius as a ratio of outer radius (0.382 = golden-ratio 5-star)\",ui:{type:\"range\",min:.1,max:.9,step:.01,label:\"Inner Ratio\",group:\"Shape\"}}}}});var bY=H({name:\"Stone\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Applies a marbled stone relief and surface distortion to child content\",requiresRTT:!0,requiresChild:!0,props:Gg({intensity:.5,scale:1,contrast:0,distortion:.15}),gpu:{fragment:gg(hg.stone)}});var xY=H({name:\"Strands\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Flowing ribbons of light with a multi-color gradient\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},extraAnimatedTimes:{color:\"colorSpeed\"},props:{speed:{default:.5,description:\"Overall animation speed\",ui:{type:\"range\",min:0,max:2,step:.1,label:\"Speed\",group:\"Animation\"}},amplitude:{default:2,description:\"How far the strands wave away from their resting line\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Amplitude\",group:\"Effect\"}},frequency:{default:.3,description:\"How many waves run along each strand\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Frequency\",group:\"Effect\"}},lineCount:{default:8,description:\"Number of strands\",ui:{type:\"range\",min:1,max:16,step:1,label:\"Strand Count\",group:\"Effect\"}},lineWidth:{default:.05,description:\"Thickness of each strand\\u2019s solid core\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Strand Width\",group:\"Effect\"}},softness:{default:.05,description:\"Softness of each strand\\u2019s edge (0 is crisp, higher is feathered)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Strand Softness\",group:\"Effect\"}},spread:{default:.2,description:\"How far the strands fan out across the field\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Spread\",group:\"Effect\"}},pinEdges:{default:!1,transform:rs,description:\"Pin the strands so they converge to the start and end points\",ui:{type:\"checkbox\",label:\"Pin Edges\",group:\"Effect\"}},stops:{...Ms(),default:[{color:\"#00e5ff\",position:0},{color:\"#5b8cff\",position:.34},{color:\"#b06bff\",position:.67},{color:\"#ff5fa2\",position:1}]},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space used to blend the gradient\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},colorScale:{default:1,description:\"How many times the gradient repeats along the strands\",ui:{type:[\"range\",\"map\"],min:1,max:6,step:1,label:\"Color Scale\",group:\"Colors\"}},colorVariance:{default:1,description:\"At 0 every strand shares the same color; at 1 the strands spread across the full gradient\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.05,label:\"Color Variance\",group:\"Colors\"}},colorSpeed:{default:1,description:\"Speed at which the colors flow along the strands (loops)\",ui:{type:\"range\",min:0,max:2,step:.05,label:\"Color Speed\",group:\"Colors\"}},start:{default:{x:0,y:.5},transform:Xe,description:\"Starting point of the strands\",ui:{type:\"position\",label:\"Start\",group:\"Position\"}},end:{default:{x:1,y:.5},transform:Xe,description:\"Ending point of the strands\",ui:{type:\"position\",label:\"End\",group:\"Position\"}}},paint:oL(iL({from:T(\"start\"),to:T(\"end\"),count:T(\"lineCount\"),width:T(\"lineWidth\"),amplitude:T(\"amplitude\"),frequency:T(\"frequency\"),softness:T(\"softness\"),spread:T(\"spread\"),pinEdges:T(\"pinEdges\"),colorScale:T(\"colorScale\"),colorVariance:T(\"colorVariance\")}),aL({space:T(\"colorSpace\")}),nL())});var AY=H({name:\"Stretch\",role:\"warp\",category:\"Distortions\",description:\"Stretch content towards a direction from a center point\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point of the stretch effect\"),strength:{default:1,description:\"The intensity of the stretch effect\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},angle:{default:0,description:\"The direction of the stretch in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},falloff:{default:0,description:\"Controls the sharpness of the transition (0 = sharp edge, 1 = gradual transition)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},map:KL({center:T(\"center\"),strength:T(\"strength\"),angle:T(\"angle\"),falloff:T(\"falloff\")}),missingChildMessage:\"You must pass a child component into the Stretch shader.\"});var wY=H({name:\"Stripes\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Alternating colored stripes with animation\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#000000\",transform:ce,description:\"First stripe color\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Second stripe color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},angle:{default:45,transform:cs,description:\"Angle of stripes in degrees\",ui:{type:[\"range\",\"map\"],min:-180,max:180,step:1,label:\"Angle\",group:\"Effect\"}},density:{default:5,description:\"Number of stripe pairs visible\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:1,label:\"Density\",group:\"Effect\"}},balance:{default:.5,description:\"Ratio of the two colors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Balance\",group:\"Effect\"}},softness:{default:0,description:\"Edge softness\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Softness\",group:\"Effect\"}},speed:{default:.2,description:\"Animation speed\",ui:{type:\"range\",min:-1,max:1,step:.1,label:\"Speed\",group:\"Animation\"}},offset:{default:0,description:\"Phase offset for pattern positioning\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Offset\",group:\"Animation\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:T(\"colorA\"),stroke:T(\"colorB\"),mask:$R({angle:T(\"angle\"),density:T(\"density\"),balance:T(\"balance\"),softness:T(\"softness\"),offset:T(\"offset\")}),space:T(\"colorSpace\")})});var TY=Math.PI*2,SY=H({name:\"StudioBackground\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Multi-light studio background with ambient motion.\",extraFields:{ambPosA:{schema:S,initial:[0,0,0,0]},ambPosB:{schema:S,initial:[0,0,0,0]},ambSizeSq:{schema:S,initial:[.01,.01,.01,.01]}},props:{color:{default:\"#d8dbec\",transform:ce,description:\"Base studio surface color\",ui:{type:\"color\",label:\"Surface Color\",group:\"Colors\"}},keyColor:{default:\"#d5e4ea\",transform:ce,description:\"Color of the overhead key light\",ui:{type:\"color\",label:\"Key Color\",group:\"Key Light\"}},keyIntensity:{default:40,description:\"Intensity of the key light\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Key Light\"}},keySoftness:{default:50,description:\"How diffuse the key light is\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Softness\",group:\"Key Light\"}},fillColor:{default:\"#d5e4ea\",transform:ce,description:\"Color of the side fill lights\",ui:{type:\"color\",label:\"Fill Color\",group:\"Fill Lights\"}},fillIntensity:{default:10,description:\"Intensity of the fill lights\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Fill Lights\"}},fillSoftness:{default:70,description:\"How diffuse the fill lights are\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Softness\",group:\"Fill Lights\"}},fillAngle:{default:70,description:\"How far apart the fill lights are from center\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Spread\",group:\"Fill Lights\"}},backColor:{default:\"#c8d4e8\",transform:ce,description:\"Color of the upward back wash\",ui:{type:\"color\",label:\"Back Color\",group:\"Back Light\"}},backIntensity:{default:20,description:\"Intensity of the back wash\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Back Light\"}},backSoftness:{default:80,description:\"How diffuse the back wash is\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Softness\",group:\"Back Light\"}},brightness:{default:20,description:\"Overall ambient light level\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Ambient\",group:\"Lighting\"}},vignette:{default:0,description:\"Edge darkening\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Vignette\",group:\"Lighting\"}},center:{default:{x:.5,y:.8},transform:Xe,description:\"Where the spotlight meets the floor\",ui:{type:\"position\",label:\"Stage Center\",group:\"Scene\"}},lightTarget:{default:100,description:\"How far toward the floor vs wall the spotlights aim\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Light Depth\",group:\"Scene\"}},wallCurvature:{default:10,description:\"How rounded the cove is\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Wall Curvature\",group:\"Scene\"}},ambientIntensity:{default:50,description:\"Intensity of drifting ambient lights\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Ambient Detail\",group:\"Environment\"}},ambientSpeed:{default:2,description:\"Drift speed\",ui:{type:\"range\",min:-5,max:5,step:.1,label:\"Speed\",group:\"Environment\"}},seed:{default:0,description:\"Seed for ambient pattern\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Environment\"}}},paint:e=>{let{uniforms:t}=e,{uv:s,viewport:r}=aa(e),o=z(Ce(r.member(\"x\"),it(r.member(\"y\"),1e-6)),\"aspect\"),a=Me=>me(T(Me),e),i=z(v(a(\"brightness\"),.02),\"sbBrightness\"),l=z(v(a(\"keyIntensity\"),.015),\"sbKeyInt\"),c=z(v(a(\"fillIntensity\"),.015),\"sbFillInt\"),d=v(a(\"fillAngle\"),.005),u=v(a(\"backIntensity\"),.015),p=z(v(a(\"lightTarget\"),.01),\"sbTarget\"),_=z(v(a(\"wallCurvature\"),.01),\"sbCurvature\"),f=et(5,.8,v(a(\"keySoftness\"),.01)),h=et(5,.8,v(a(\"fillSoftness\"),.01)),g=z(et(5,.8,v(a(\"backSoftness\"),.01)),\"sbBackDecay\"),y=z(a(\"center\").member(\"x\"),\"coveCenterX\"),b=z(ne(1,a(\"center\").member(\"y\")),\"coveCenterY\"),x=z(et(.015,.15,_),\"coveTransition\"),P=z(ot(ne(b,x),I(b,x),s.member(\"y\")),\"coveIsFloor\"),C=z(ne(1,P),\"coveNotFloor\"),E=z(Ge(Ce(s.member(\"y\"),it(b,1e-6)),0,1),\"coveWallY\"),R=z(v(E,E),\"coveWallY2\"),U=Ge(Ce(ne(s.member(\"y\"),b),it(ne(1,b),1e-6)),0,1),L=z(v(ne(s.member(\"x\"),y),o),\"coveX\"),k=z(et(.05,.45,R),\"coveCone\"),D=v(ne(1,E),1.2),O=z(v(D,D),\"coveWallFall\"),F=I(b,v(ne(1,b),.3)),q=v(ne(s.member(\"y\"),F),4),Y=z(v(q,q),\"coveFloorFall\"),V=v(et(et(.2,.4,E),et(.38,.18,U),P),i),X=Ce(ne(s.member(\"y\"),b),I(x,.001)),Q=z(I(V,v(v(v(tr(v(v(-.5,X),X)),_),i),.1)),\"sbBase\"),te=z(I(v(ne(1,p),1.4),.3),\"sbWallBias\"),J=z(I(v(p,1.4),.3),\"sbFloorBias\"),oe=(Me,we)=>{let Ve=et(Me,y,R),qe=Ce(v(ne(s.member(\"x\"),Ve),o),k),st=v(v(tr(v(er(I(v(qe,qe),O)),we)),C),te),pt=et(y,Me,.25),Ze=v(v(ne(s.member(\"x\"),pt),o),1.4);return I(st,v(v(tr(v(er(I(v(Ze,Ze),Y)),v(we,1.5))),P),J))},le=z(Qe(Q,Q,Q),\"sbLight0\");le=I(le,v(a(\"keyColor\").member(\"rgb\"),v(z(oe(y,f),\"sbKey\"),l)));let se=I(v(z(oe(ne(y,d),h),\"sbFillL\"),c),v(z(oe(I(y,d),h),\"sbFillR\"),c));le=I(le,v(a(\"fillColor\").member(\"rgb\"),se));let Te=v(q_(v(L,.5),v(ne(s.member(\"y\"),et(1,b,.15)),4),g),P),de=v(v(q_(v(L,.6),v(ne(b,s.member(\"y\")),3),v(g,.8)),C),R);le=I(le,v(a(\"backColor\").member(\"rgb\"),v(I(Te,de),u)));let xe=v(v(q_(v(L,.7),v(I(ne(s.member(\"y\"),b),.02),10),It(2.5)),v(l,.12)),C);le=I(le,kt(xe));let Ae=ne(b,.2),De=I(I(I(W_({uv:s,aspect:o,x:I(y,t.ambPosA.member(\"x\")),y:I(Ae,t.ambPosA.member(\"y\")),sizeSq:t.ambSizeSq.member(\"x\")}),W_({uv:s,aspect:o,x:I(y,t.ambPosA.member(\"z\")),y:I(Ae,t.ambPosA.member(\"w\")),sizeSq:t.ambSizeSq.member(\"y\")})),W_({uv:s,aspect:o,x:I(y,t.ambPosB.member(\"x\")),y:I(Ae,t.ambPosB.member(\"y\")),sizeSq:t.ambSizeSq.member(\"z\")})),W_({uv:s,aspect:o,x:I(y,t.ambPosB.member(\"z\")),y:I(Ae,t.ambPosB.member(\"w\")),sizeSq:t.ambSizeSq.member(\"w\")}));le=I(le,kt(v(De,v(v(a(\"ambientIntensity\"),.012),.25))));let Ee=0,ze=NaN,Ie=[];e.onBeforeRender(Me=>{Ee+=(Me?.deltaTime??0)*(e.getCpuValue(\"ambientSpeed\")??2);let we=e.getCpuValue(\"seed\")??0;if(we!==ze){ze=we,Ie=[];for(let qe=0;qe<4;qe++){let st=we+qe*73.1,pt=wc(st+311)*.12+.1;Ie.push({sizeSq:pt*pt,radiusX:wc(st)*.5+.2,radiusY:wc(st+37)*.25+.1,freqX:wc(st+91)*.3+.1,freqY:wc(st+143)*.25+.08,phaseX:wc(st+200)*TY,phaseY:wc(st+257)*TY})}}let Ve=Ie.map(qe=>[Math.sin(Ee*qe.freqX+qe.phaseX)*qe.radiusX,Math.cos(Ee*qe.freqY+qe.phaseY)*qe.radiusY]);e.setExtraField(\"ambPosA\",[Ve[0][0],Ve[0][1],Ve[1][0],Ve[1][1]]),e.setExtraField(\"ambPosB\",[Ve[2][0],Ve[2][1],Ve[3][0],Ve[3][1]]),e.setExtraField(\"ambSizeSq\",[Ie[0].sizeSq,Ie[1].sizeSq,Ie[2].sizeSq,Ie[3].sizeSq])});let ke=RD({strength:T(\"vignette\"),scale:.025})(e);return Ot(Ge(v(v(a(\"color\").member(\"rgb\"),Ge(le,kt(0),kt(3))),ke),kt(0),kt(1)),1)}});var PY=H({name:\"SunBurst\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Radial sunburst rays emanating from a center point\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{color:{default:\"#ffdd88\",transform:ce,description:\"Ray color\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},background:{default:\"#000000\",transform:ce,description:\"Background color\",ui:{type:\"color\",label:\"Background\",group:\"Colors\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center point of the sunburst\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},rayCount:{default:12,description:\"Number of rays\",ui:{type:\"range\",min:3,max:64,step:1,label:\"Ray Count\",group:\"Effect\"}},softness:{default:.3,description:\"Softness of ray edges\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},radius:{default:.8,description:\"How far the rays extend from the center\",ui:{type:[\"range\",\"map\"],min:0,max:1.2,step:.01,label:\"Radius\",group:\"Effect\"}},feather:{default:.5,description:\"How gradually the rays fade at their outer edge\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.01,label:\"Feather\",group:\"Effect\"}},speed:{default:.2,description:\"Rotation speed \\u2014 positive values rotate clockwise\",ui:{type:\"range\",min:-2,max:2,step:.1,label:\"Speed\",group:\"Animation\"}}},paint:LD({color:T(\"color\"),background:T(\"background\"),coverage:vg(mD(T(\"center\")),vD(hD({count:T(\"rayCount\"),softness:T(\"softness\"),spin:-1}),gD({radius:T(\"radius\"),feather:T(\"feather\")})),\"burstFrame\"),hint:\"burstAlpha\"})});var _5=Rr()?960:1600,CY=1600,MY=900,Nfe=Rr()?12:16,$fe=6,EY=bt,kY=H({name:\"Surface3D\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Distortions\",description:\"Drapes child content over a 3D wave surface with perspective and lighting\",requiresRTT:!0,requiresChild:!0,usesPointer:!0,providesUVContextViaCompute:!0,props:{amplitude:{default:.3,description:\"Wave height\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Amplitude\",group:\"Waves\"}},waveType:{default:\"fractal\",compileTime:!0,description:\"Wave pattern type\",ui:{type:\"select\",options:[{label:\"Fractal\",value:\"fractal\"},{label:\"Sine\",value:\"sine\"},{label:\"Ridge\",value:\"ridge\"}],label:\"Wave Type\",group:\"Waves\"}},frequency:{default:1.5,description:\"Wave frequency\",ui:{type:\"range\",min:.1,max:5,step:.1,label:\"Frequency\",group:\"Waves\"}},octaves:{default:2,recompile:hn((e,t)=>Math.max(1,Math.floor(e))!==Math.max(1,Math.floor(t))),description:\"Detail octaves\",ui:{type:\"range\",min:1,max:5,step:1,label:\"Octaves\",group:\"Waves\"}},seed:{default:0,description:\"Random seed offset\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Waves\"}},speed:{default:.5,description:\"Animation speed\",ui:{type:\"range\",min:0,max:3,step:.1,label:\"Speed\",group:\"Waves\"}},tilt:{default:35,description:\"Camera tilt in degrees\",ui:{type:\"range\",min:0,max:85,step:1,label:\"Tilt\",group:\"Camera\"}},roll:{default:0,description:\"Camera roll in degrees\",ui:{type:\"range\",min:-45,max:45,step:1,label:\"Roll\",group:\"Camera\"}},height:{default:0,description:\"Camera height\",ui:{type:\"range\",min:-1,max:2,step:.01,label:\"Height\",group:\"Camera\"}},zoom:{default:1,description:\"Camera zoom\",ui:{type:\"range\",min:.3,max:3,step:.01,label:\"Zoom\",group:\"Camera\"}},nearCutoff:{default:0,description:\"Near fade distance\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Near Cutoff\",group:\"Camera\"}},farCutoff:{default:1,description:\"Far fade distance\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Far Cutoff\",group:\"Camera\"}},edgePinning:{default:0,description:\"Smoothly flatten the surface at its edges (orthogonal to edge mode)\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Pinning\",group:\"Waves\"}},edges:{default:\"mirror\",transform:Fr,compileTime:!0,description:\"How to handle content beyond the surface bounds\",ui:{type:\"select\",options:[{label:\"Stretch\",value:\"stretch\"},{label:\"Transparent\",value:\"transparent\"},{label:\"Mirror\",value:\"mirror\"},{label:\"Wrap\",value:\"wrap\"}],label:\"Edges\",group:\"Waves\"}},lighting:{default:30,recompile:hn((e,t)=>e<.01!=t<.01),description:\"Intensity of lighting and shading\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Lighting\",group:\"Lighting\"}},glossiness:{default:0,description:\"Surface glossiness\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Glossiness\",group:\"Lighting\"}},highlights:{default:15,description:\"Specular highlight intensity\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Highlights\",group:\"Lighting\"}},lightX:{default:.4,description:\"Light direction X\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Light X\",group:\"Lighting\"}},lightY:{default:-.6,description:\"Light direction Y\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Light Y\",group:\"Lighting\"}},lightZ:{default:.7,description:\"Light direction Z\",ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Light Z\",group:\"Lighting\"}},lightColor:{default:\"#ffffff\",transform:ce,description:\"Light color\",ui:{type:\"color\",label:\"Light Color\",group:\"Lighting\"}},cursorIntensity:{default:1,recompile:hn((e,t)=>e<.01!=t<.01),description:\"Strength of cursor ripples\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Cursor Ripples\",group:\"Interaction\"}},cursorSpeed:{default:.5,description:\"Speed of cursor ripple propagation\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Ripple Speed\",group:\"Interaction\"}}},compute:e=>{let{gpu:t,getCpuValue:s,registerComputeTexture:r,onCleanup:o,onResize:a,dimensions:i}=e,l=t?.root;if(!l)return null;let c=s(\"waveType\")===\"sine\"?1:s(\"waveType\")===\"ridge\"?2:0,d=Math.max(1,Math.floor(s(\"octaves\")??2)),u=s(\"edges\")??2,p=(s(\"lighting\")??30)>=.01,_=(s(\"cursorIntensity\")??1)>=.01,f=Math.round(i.width),h=Math.round(i.height),g=Math.min(_5,CY),y=Math.min(Math.round(_5*MY/CY),MY);if(f>=16&&h>=16){let xe=Math.min(1,_5/Math.max(f,h));g=Math.max(16,Math.round(f*xe)),y=Math.max(16,Math.round(h*xe))}let b=l.createTexture({size:[g,y],format:t_}).$usage(\"storage\",\"sampled\"),x=l.createTexture({size:[g,y],format:t_}).$usage(\"storage\",\"sampled\");o(()=>{b.destroy(),x.destroy()});let P=r(b),C=r(x),{bufferA:E,bufferB:R,readBuf:U}=Rz(l),L=zz(l),k=Math.max(1,Math.round(i.width)),D=Math.max(1,Math.round(i.height));a(({width:xe,height:Ae})=>{k=Math.max(1,Math.round(xe)),D=Math.max(1,Math.round(Ae))});let O=Lz({waveType:c,octaveCount:d,edgeMode:u,lightingEnabled:p,cursorEnabled:_,marchSteps:Nfe,marchRefine:$fe,computeW:g,computeH:y}),F=l.createBindGroup(xn,{readBuf:U,params:L.buffer,uvMaskTex:b,litTex:x}),q=QA(l,O,{size:[g,y],bindGroup:F}),Y=_?Dz():null,V=ap(E,R),X=_?V.groups((xe,Ae)=>l.createBindGroup(bn,{readSrc:xe,writeBuf:Ae,readBuf:U,params:L.buffer})):null,Q=_&&Y?QA(l,Y,{size:[256,256]}):null,te=.5,J=.5,oe=Date.now(),le=0,se=0,Te=null,de=(xe,Ae,De)=>{let Ee=Math.max(1,k)/Math.max(1,D),ze=(s(\"tilt\")??35)*EY,Ie=(s(\"roll\")??0)*EY,ke=s(\"height\")??0,Me=s(\"zoom\")??1,{camPos:we,fwd:Ve,camRight:qe,camUp:st,focal:pt}=Hfe(ze,Ie,ke,Me),Ze=xe.pointer?.x??.5,nt=xe.pointer?.y??.5,ht=(Ze-.5)*2*Ee,yt=-(nt-.5)*2,St=f5([Ve[0]*pt+qe[0]*ht+st[0]*yt,Ve[1]*pt+qe[1]*ht+st[1]*yt,Ve[2]*pt+qe[2]*ht+st[2]*yt]),Mt=Math.max(-St[2],.001),Rt=we[2]/Mt,Kt=we[0]+St[0]*Rt,Wt=we[1]+St[1]*Rt,ls=Kt*.5+.5,us=Wt*.5+.5,os=s(\"lightColor\"),bs=s(\"amplitude\")??.3,hs=s(\"cursorIntensity\")??1,gs=s(\"frequency\")??1.5,Gs=s(\"seed\")??0,Ss=s(\"edgePinning\")??0,or=(s(\"lighting\")??30)*.035,yr=(s(\"glossiness\")??0)*.01,lr=(s(\"highlights\")??15)*.04,Ur=s(\"lightX\")??.4,so=s(\"lightY\")??-.6,ro=s(\"lightZ\")??.7,_e=s(\"nearCutoff\")??0,We=s(\"farCutoff\")??1,Le=s(\"cursorSpeed\")??.5,Be=(ls-.5)/3+.5,Oe=(us-.5)/3+.5,tt=bs+Bz+(De>0?hs*.05*6:0);return L.write({amp:bs,freq:gs,seed:Gs,edgePin:Ss,cursorIntensity:hs,t:se,aspect:Ee,focal:pt,camPos:we,camForward:Ve,camRight:qe,camUp:st,lighting:or,glossiness:yr,highlights:lr,lightX:Ur,lightY:so,lightZ:ro,lightColor:[os?.x??1,os?.y??1,os?.z??1],nearCutoff:_e,farCutoff:We,cursorWaveX:Be,cursorWaveY:Oe,mouseSpeed:Ae,dt:.016,decay:10,radius:.025,waveSpeed:Le,heightEnvelope:tt,cursorActive:De}),[bs,gs,Gs,Ss,hs,se,Ee,ze,Ie,ke,Me,or,yr,lr,Ur,so,ro,os?.x??1,os?.y??1,os?.z??1,_e,We,Be,Oe,Ae,Le,tt,De].join(\",\")};return{outputs:{uvMaskMap:P,litMap:C},getComputeNodes:xe=>{let Ae=xe,De=Date.now(),Ee=Math.min(Ae.deltaTime??(De-oe)/1e3,.016);oe=De;let ze=Ae.pointer?.x??te,Ie=Ae.pointer?.y??J,ke=Ee>0?(ze-te)/Ee:0,Me=Ee>0?(Ie-J)/Ee:0,we=Math.min(Math.sqrt(ke*ke+Me*Me),2);te=ze,J=Ie,we>.01?le=0:le+=Ee,se+=Ee*(s(\"speed\")??.5);let Ve=!_||!Q||le*1e3>kz&&we<.01,qe=de(Ae,we,Ve?0:1);if(Ve&&qe===Te)return[];if(Te=qe,Ve||!Q||!X)return[q];let st=Q.with(X());return V.swap(),[st,q]}}},gpu:{fragment:e=>{let{childNode:t,ctx:s,computeOutputs:r,convertToTexture:o}=e;if(!t)return xt;let a=r?.uvMaskMap,i=r?.litMap;if(!a||!i)return xt;let l=z(a.sample(s.uv,\"linearClamp\"),\"uvMaskSample\"),c=i.sample(s.uv,\"linearClamp\"),d=Ot(l.member(\"r\"),l.member(\"g\"),0,0).member(\"xy\"),u=l.member(\"b\"),p=c.member(\"rgb\"),_=z(e.uvPropagationActive?t:o(t).sample(d),\"childColor\");return W(Yt,\"unpremultiplyAlpha\",[Ot(Ge(v(_.member(\"xyz\"),p),Qe(0,0,0),Qe(1,1,1)),v(_.member(\"w\"),u))])}}});function Xfe(e,t){return[e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]]}function f5(e){let t=Math.max(1e-6,Math.hypot(e[0],e[1],e[2]));return[e[0]/t,e[1]/t,e[2]/t]}function Hfe(e,t,s,r){let o=.85/Math.max(1e-4,r),a=-Math.sin(e)*o,i=Math.cos(e)*o,l=[0,a,i+s],c=f5([0,-a,-i]),d=[1,0,0],u=f5(Xfe(d,c)),p=Math.cos(t),_=Math.sin(t);return{camPos:l,fwd:c,camRight:[d[0]*p+u[0]*_,d[1]*p+u[1]*_,d[2]*p+u[2]*_],camUp:[u[0]*p-d[0]*_,u[1]*p-d[1]*_,u[2]*p-d[2]*_],focal:1.5}}var BY=F_(CR({detail:T(\"detail\")})),zY=H({name:\"Swirl\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null},category:\"Textures\",description:\"Flowing swirl pattern with multi-layered noise\",animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#1275d8\",description:\"Primary gradient color\",transform:ce,ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#e19136\",description:\"Secondary gradient color\",transform:ce,ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),speed:{default:1,description:\"Flow animation speed\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},detail:{default:1,description:\"Level of detail and intricacy in the swirl patterns\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.1,label:\"Detail\",group:\"Effect\"}},blend:{default:50,description:\"Skew color balance toward A (lower values) or B (higher values)\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Blend\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:kR(Vs(TR(BY,{low:.3,high:.7,bias:{base:T(\"blend\"),add:-50,mul:.006}}),Us(T(\"colorSpace\"))),MR(BY,{speed:2.5,span:8,depth:.015}))});var RY=H({role:\"shape\",name:\"Teardrop\",description:\"Teardrop \\u2014 a rounded bulb tapering to a sharp point\",shape:{distance:({x:e,y:t},s)=>hR(e,t,s(\"radius\"),s(\"height\")),colorDescription:\"Fill color of the teardrop\",centerDescription:\"Center position of the teardrop\",bounds:{size:{width:\"radius\",height:\"height\"}},shapeProps:{radius:{default:.22,description:\"Radius of the rounded bulb\",ui:{type:\"range\",min:.02,max:.6,step:.01,label:\"Width\",group:\"Shape\"}},height:{default:.4,description:\"Length from the bulb to the point\",ui:{type:\"range\",min:.1,max:.7,step:.01,label:\"Length\",group:\"Shape\"}}}}});function LY(e,t){switch(t){case\"uppercase\":return e.toUpperCase();case\"lowercase\":return e.toLowerCase();case\"capitalize\":return e.replace(/(^|\\s)(\\S)/g,(s,r,o)=>r+o.toUpperCase());default:return e}}var UY=0;function jfe(){UY++}var Dp=null,dy=!1;function OY(){if(Dp)return Dp;if(typeof document>\"u\")return null;let e=document.createElement(\"canvas\");return e.width=1,e.height=1,Dp=e.getContext(\"2d\",{willReadFrequently:!1}),Dp&&(dy=\"letterSpacing\"in Dp),Dp}function Kfe(){return OY(),dy}var Qfe=500,cy=new Map;function v5(e){let t=LY(e.text,e.textTransform),s=OY();if(!s)return{widthPer100:t.length*55,ascentPer100:80,descentPer100:20,inkAscentPer100:80,inkDescentPer100:20};let r=rB(e.fontFamily,e.fontWeight,e.italic),o=`${UY}|${r?1:0}|${e.fontFamily}|${e.fontWeight}|${e.italic?1:0}|${e.letterSpacingEm}|${t}`,a=cy.get(o);if(a)return a;s.font=`${e.italic?\"italic \":\"\"}${e.fontWeight} 100px \"${e.fontFamily}\", sans-serif`,dy&&(s.letterSpacing=`${e.letterSpacingEm*100}px`);let i=s.measureText(t),l=i.width;dy&&t.length>0&&(l=Math.max(0,l-e.letterSpacingEm*100));let c=i.fontBoundingBoxAscent??i.actualBoundingBoxAscent??80,d=i.fontBoundingBoxDescent??i.actualBoundingBoxDescent??20,u=i.actualBoundingBoxAscent,p=i.actualBoundingBoxDescent,_=u!==void 0&&p!==void 0&&u+p>1,f={widthPer100:l,ascentPer100:c,descentPer100:d,inkAscentPer100:_?u:c,inkDescentPer100:_?p:d};return cy.size>=Qfe&&cy.clear(),cy.set(o,f),f}function Zfe(e,t,s){let r=e.split(/\\r\\n|\\r|\\n/);if(!(t>0))return r;let o=[];for(let a of r){let i=a.split(\" \"),l=\"\";for(let c of i){let d=l?`${l} ${c}`:c;l&&s(d)>t?(o.push(l),l=c):l=d}o.push(l)}return o}function GY(e,t,s){let r=Zfe(LY(e.text,e.textTransform),s??0,d=>v5({...e,text:d,textTransform:\"none\"}).widthPer100),o=r.map(d=>v5({...e,text:d,textTransform:\"none\"})),a=Math.max(1,r.length),i=o[0]??v5(e),l=o[o.length-1]??i,c=Math.max(1,t*100);return{lines:r,lineWidthsPer100:o.map(d=>d.widthPer100),widthPer100:o.reduce((d,u)=>Math.max(d,u.widthPer100),0),ascentPer100:i.ascentPer100,descentPer100:i.descentPer100,inkAscentFirstPer100:i.inkAscentPer100,inkDescentLastPer100:l.inkDescentPer100,pitchPer100:c,blockHeightPer100:i.ascentPer100+(a-1)*c+i.descentPer100,inkHeightPer100:i.inkAscentPer100+(a-1)*c+l.inkDescentPer100}}var Jfe=4096,e1e=3,t1e=(e,t)=>e&&typeof e==\"object\"&&typeof e.value==\"number\"?e.unit===\"px\"?e.value:e.value*t:(typeof e==\"number\"?e:0)*t,DY=(e,t,s)=>{let r=s1e(e.fontSize,s),o=t1e(e.width,t);return{fontSizePx:r,widthPx:o,m:GY({text:String(e.text??\"\"),fontFamily:String(e.fontFamily??\"Inter\"),fontWeight:Number(e.fontWeight??400),italic:!!e.italic,letterSpacingEm:Number(e.letterSpacing??0),textTransform:e.textTransform??\"none\"},Number(e.lineHeight??1.2)||1.2,r>0&&o>0?o*100/r:0)}},s1e=(e,t)=>e&&typeof e==\"object\"&&typeof e.value==\"number\"?e.unit===\"px\"?e.value:e.value*t:(typeof e==\"number\"?e:.1)*t;function r1e(e){let{dimensions:t,getCpuValue:s,setExtraField:r}=e,o=w_(e,{maxAxis:Jfe,supersample:e1e}),a=o.context(),i=gc(e,{label:\"Text:glyph\",initial:{width:2,height:2}}),l=h5.props,c=()=>({text:String(s(\"text\")??l.text.default),fontFamily:String(s(\"fontFamily\")??l.fontFamily.default),fontWeight:Number(s(\"fontWeight\")??l.fontWeight.default),italic:Number(s(\"italic\")??0)>.5,fontSizeUV:Number(s(\"fontSize\")??l.fontSize.default),letterSpacing:Number(s(\"letterSpacing\")??l.letterSpacing.default),lineHeight:Number(s(\"lineHeight\")??l.lineHeight.default)||l.lineHeight.default,textAlign:String(s(\"textAlign\")??l.textAlign.default),widthUV:Number(s(\"width\")??l.width.default),textTransform:String(s(\"textTransform\")??l.textTransform.default)}),d=()=>{if(!a||o.disposed)return;let p=c(),_=t.width||1,f=t.height||1,h=p.fontSizeUV*f,g=p.widthUV*_;if(!p.text.trim()||h<=0){o.resize(2,2),i.ensureSize(2,2),i.write(o.canvas()),r(\"halfW\",.001),r(\"halfH\",.001);return}let y=GY({text:p.text,fontFamily:p.fontFamily,fontWeight:p.fontWeight,italic:p.italic,letterSpacingEm:p.letterSpacing,textTransform:p.textTransform},p.lineHeight,g>0?g*100/h:0),b=h/100,x=Math.max(1,(g>0?Math.max(g*100/h,y.widthPer100):y.widthPer100)*b),P=y.pitchPer100*b,C=Math.max(1,h*p.lineHeight),E=Math.max(C,y.blockHeightPer100*b),R=y.ascentPer100*b,U=y.descentPer100*b,L=Math.max(.25*h,(R+U-C)/2+.25*h),k=x+L*2,D=E+L*2,{scale:O,texW:F,texH:q}=o.rasterSize(k,D);o.resize(F,q),a.fillStyle=\"#ffffff\",a.font=`${p.italic?\"italic \":\"\"}${p.fontWeight} ${h*O}px \"${p.fontFamily}\", sans-serif`,Kfe()&&(a.letterSpacing=`${p.letterSpacing*h*O}px`),a.textAlign=\"left\",a.textBaseline=\"alphabetic\";let Y=q/2-E/2*O,V=(F-x*O)/2;for(let X=0;X<y.lines.length;X++){let Q=y.lines[X];if(!Q.trim())continue;let te=y.lineWidthsPer100[X]*b,J=p.textAlign===\"left\"?V:p.textAlign===\"right\"?V+(x-te)*O:V+(x-te)/2*O,oe=Y+(R+X*P)*O;a.fillText(Q,J,oe)}i.ensureSize(F,q),i.write(o.canvas()),r(\"halfW\",Math.max(1e-4,k/2/f)),r(\"halfH\",Math.max(1e-4,D/2/f))},u=S_({key:()=>{let p=c();return`${p.fontFamily}|${p.fontWeight}|${p.italic}`},load:()=>{let p=c();return Hh(p.fontFamily,p.fontWeight,p.italic)},onLoaded:()=>{jfe(),d()}});return setTimeout(()=>{d(),u.ensure()},0),T_(e,{key:()=>{let p=c();return`${p.text}|${p.fontFamily}|${p.fontWeight}|${p.italic}|${p.fontSizeUV}|${p.letterSpacing}|${p.lineHeight}|${p.textAlign}|${p.widthUV}|${p.textTransform}|${t.width}x${t.height}|${e.canvas.width}`},run:()=>{d(),u.ensure()}}),i}var o1e=({uniforms:e,ctx:t,uvContext:s})=>W(Z8,\"orientedBoxField\",[s??t.uv,t.aspect,e.center,e.rotation,e.halfW,e.halfH]),a1e=(e,t)=>g_(e,t.member(\"xy\"),\"linearClamp\",\"unit\"),n1e=(e,t,s)=>Ct(s.member(\"rgb\"),e.member(\"a\").mul(s.member(\"a\")).mul(t.member(\"z\"))),h5=H({name:\"Text\",role:\"media\",species:\"custom\",category:\"Textures\",description:\"Text with any Google font \\u2014 multi-line with wrapping, alignment and line height \\u2014 rendered crisp via a glyph texture\",acceptsUVContext:!0,extraFields:{halfW:{schema:n,initial:.001},halfH:{schema:n,initial:.001}},boundingBoxDeclaration:{propBindings:{x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"},rotation:{prop:\"rotation\",as:\"degrees\"}},computeBounds(e,t,s){let{fontSizePx:r,widthPx:o,m:a}=DY(e,t,s),i=r/100,l=typeof e.center?.x==\"number\"?e.center.x:.5,c=typeof e.center?.y==\"number\"?e.center.y:.5,d=(a.ascentPer100-a.descentPer100+(a.inkDescentLastPer100-a.inkAscentFirstPer100))/2*i;return{centerXPx:l*t,centerYPx:c*s+d,widthPx:Math.max(0,o>0?o:a.widthPer100*i),heightPx:Math.max(0,a.inkHeightPer100*i),rotationDeg:e.rotation??0}},writeBounds(e,t,s,r){let{fontSizePx:o,widthPx:a,m:i}=DY(t,s,r),l=Math.max(1,i.inkHeightPer100*o/100),c=e.heightPx/l;if(a>0){let h=e.widthPx/Math.max(1,a),g=e.heightPx/l;c=Math.abs(h-g)<.02?g:1}let d=Math.max(1,o*c),u=(i.ascentPer100-i.descentPer100+(i.inkDescentLastPer100-i.inkAscentFirstPer100))/2*(d/100),p=t.fontSize,_=p&&typeof p==\"object\"&&p.unit===\"px\"?{value:Math.max(1,Math.round(d)),unit:\"px\"}:Math.max(.001,r>0?d/r:.001),f={center:{...t.center??{x:.5,y:.5},x:e.centerXPx/s,y:(e.centerYPx-u)/r},fontSize:_,rotation:e.rotationDeg};if(a>0){let h=t.width;f.width=h&&typeof h==\"object\"&&h.unit===\"px\"?{value:Math.max(1,Math.round(e.widthPx)),unit:\"px\"}:Math.max(.001,s>0?e.widthPx/s:.001)}return f}},props:{text:{default:\"Hello World\",description:\"The text to display (single line)\",ui:{type:\"text\",label:\"Text\",group:\"Text\",hidden:!0}},fontFamily:{default:\"Inter\",description:\"Google Fonts family name\",ui:{type:\"font-family\",label:\"Font\",group:\"Text\"}},fontWeight:{default:400,description:\"Font weight (only weights published for the family render true; others are synthesized)\",ui:{type:\"font-weight\",label:\"Weight\",group:\"Text\"}},italic:{default:!1,description:\"Italic style\",ui:{type:\"checkbox\",label:\"Italic\",group:\"Text\"},transform:e=>e?1:0},textTransform:{default:\"none\",description:\"Case transformation applied to the text\",ui:{type:\"select\",options:[{label:\"None\",value:\"none\"},{label:\"Uppercase\",value:\"uppercase\"},{label:\"Lowercase\",value:\"lowercase\"},{label:\"Capitalize\",value:\"capitalize\"}],label:\"Transform\",group:\"Text\"}},fontSize:{default:.07,description:\"Font size as a fraction of canvas height\",ui:{type:\"range\",min:.01,max:.5,step:.005,label:\"Size\",group:\"Typography\",dimensional:\"canvas-height\"}},letterSpacing:{default:0,description:\"Letter spacing in em units\",ui:{type:\"range\",min:-.1,max:.5,step:.005,label:\"Letter Spacing\",group:\"Typography\"}},lineHeight:{default:1.2,description:\"Line height as a multiple of font size (multi-line text)\",ui:{type:\"range\",min:.8,max:2.5,step:.05,label:\"Line Height\",group:\"Paragraph\",hidden:!0}},textAlign:{default:\"center\",description:\"Horizontal alignment of lines within the text block\",ui:{type:\"select\",options:[{label:\"Left\",value:\"left\"},{label:\"Center\",value:\"center\"},{label:\"Right\",value:\"right\"}],label:\"Align\",group:\"Paragraph\",hidden:!0}},width:{default:0,description:\"Wrap width \\u2014 text wraps to fit; 0 = auto (hug contents, single line per explicit break). Edited via the Typography panel's Auto/Fixed width toggle and the side drag handles, not a slider.\",ui:{type:\"range\",min:0,max:1,step:.005,label:\"Max Width\",group:\"Paragraph\",units:[\"%\",\"px\"],dimensional:\"canvas-width\",hidden:!0}},color:{default:\"#ffffff\",transform:ce,description:\"Text color\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the text relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the text\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},rotation:{default:0,description:\"Rotation in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}}},gpu:{fragment:e=>{let t=r1e(e),s=o1e(e);return n1e(a1e(t.kit,s),s,e.uniforms.color)}}});var FY=H({name:\"TiltShift\",role:\"filter\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Selective focus blur mimicking tilt-shift photography\",requiresRTT:!0,requiresChild:!0,props:{intensity:{default:50,description:\"Maximum blur intensity at edges\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},width:{default:.3,description:\"Width of the sharp focus area\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Width\",group:\"Effect\"}},falloff:{default:.3,description:\"Distance over which blur transitions to full strength\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Falloff\",group:\"Effect\"}},angle:{default:0,transform:cs,description:\"Rotation angle of the focus line (in degrees)\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center point of the focus line\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},dispersion:{default:0,description:\"Blurs red and blue by different amounts for a lens-like color fringe outside the focus band; negative swaps the colors\",recompile:Lt(0),ui:{type:\"range\",min:-1,max:1,step:.01,label:\"Dispersion\",group:\"Lens\"}},jitter:{default:0,description:\"Per-pixel noise on the tap spacing that hides banding at large blur radii\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Jitter\",group:\"Lens\"}}},...DU({intensity:T(\"intensity\"),width:T(\"width\"),falloff:T(\"falloff\"),angle:T(\"angle\"),center:T(\"center\"),detail:{dispersion:T(\"dispersion\"),jitter:T(\"jitter\")}})});var uy=Rr()?384:640,YY=\"rgba16float\",i1e=2.5*.5/uy,l1e=.5,c1e=.35,d1e={none:0,color:1,rainbow:2},u1e={motion:1,alpha:0},WY=(e,t)=>Fz({res:uy,format:YY,namePrefix:\"timeTrail\",source:e,hueCycle:t,diffuseHalfUv:i1e,driftScale:l1e}),y7e=WY(\"motion\",!1).kernel,IY=e=>{let t=e===\"add\"?\"linearDodge\":String(e??\"normal\");return t in pc?t:\"normal\"},p1e=e=>e===\"screen\"?1:e===\"linearDodge\"?2:0,qY=H({name:\"TimeTrail\",role:\"simulation\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"An Echo-style temporal trail \\u2014 whatever moves sheds a smooth, decaying long-exposure ghost trail (frame-difference Motion mode works on opaque images and video; Alpha mode trails a shape's coverage), with optional zoom-feedback tunnels and rainbow light-painting\",requiresRTT:!0,requiresChild:!0,props:{trailSource:{default:\"motion\",description:\"What sheds the trail \\u2014 Motion trails whatever changes frame-to-frame (works on opaque images and video), Alpha trails the child's transparency coverage (best for a clean shape over empty canvas)\",compileTime:!0,transform:e=>u1e[e]??1,ui:{type:\"select\",options:[{label:\"Motion\",value:\"motion\"},{label:\"Alpha\",value:\"alpha\"}],label:\"Source\",group:\"Trail\"}},motionThreshold:{default:.06,description:\"How much a pixel must change between frames to shed a trail (Motion source only) \\u2014 lower catches subtle movement, higher only fast motion\",ui:{type:[\"range\",\"map\"],min:.01,max:.5,step:.01,label:\"Motion Threshold\",group:\"Trail\"}},trailLength:{default:.6,description:\"How long the trail lingers, in seconds \\u2014 the time constant of the fade (the star control)\",ui:{type:[\"range\",\"map\"],min:.05,max:4,step:.01,label:\"Trail Length\",group:\"Trail\"}},diffusion:{default:1,description:\"How much the trail smears and softens as it fades \\u2014 0 keeps crisp ghost frames, higher melts them together\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Diffusion\",group:\"Trail\"}},trailOpacity:{default:1,description:\"Overall strength of the trail behind the live frame\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Trail Opacity\",group:\"Trail\"}},trailBlend:{default:\"normal\",description:\"How the trail combines with the frame \\u2014 Add / Screen build glowing light-painting trails, Multiply lays down dark ink ghosts, plus the full standard blend set\",compileTime:!0,ui:{type:\"select\",options:[{label:\"Normal\",value:\"normal\"},{label:\"Add\",value:\"add\"},{label:\"Screen\",value:\"screen\"},{label:\"Multiply\",value:\"multiply\"},{label:\"Overlay\",value:\"overlay\"},{label:\"Soft Light\",value:\"softLight\"},{label:\"Hard Light\",value:\"hardLight\"},{label:\"Darken\",value:\"darken\"},{label:\"Lighten\",value:\"lighten\"},{label:\"Color Dodge\",value:\"colorDodge\"},{label:\"Color Burn\",value:\"colorBurn\"},{label:\"Linear Burn\",value:\"linearBurn\"},{label:\"Difference\",value:\"difference\"},{label:\"Exclusion\",value:\"exclusion\"},{label:\"Hue\",value:\"hue\"},{label:\"Saturation\",value:\"saturation\"},{label:\"Color\",value:\"color\"},{label:\"Luminosity\",value:\"luminosity\"}],label:\"Blend\",group:\"Trail\"}},driftX:{default:0,description:\"Sideways drift of the trail per second \\u2014 smears the trail in a direction\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Drift X\",group:\"Feedback\"}},driftY:{default:0,description:\"Vertical drift of the trail per second\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Drift Y\",group:\"Feedback\"}},zoom:{default:1,description:\"Per-frame zoom of the trail around the centre \\u2014 above 1 pulls the trail into an outward tunnel, below 1 sucks it inward\",ui:{type:[\"range\",\"map\"],min:.9,max:1.1,step:.001,label:\"Zoom\",group:\"Feedback\"}},tintMode:{default:\"none\",description:\"color the trail \\u2014 Color tints it a single hue, Rainbow cycles hue as the trail ages\",compileTime:!0,transform:e=>d1e[e]??0,ui:{type:\"select\",options:[{label:\"None\",value:\"none\"},{label:\"Color\",value:\"color\"},{label:\"Rainbow\",value:\"rainbow\"}],label:\"Tint Mode\",group:\"Color\"}},tint:{default:\"#33aaff\",transform:ce,description:\"Trail tint color (used in Color mode)\",ui:{type:\"color\",label:\"Tint\",group:\"Color\"}},speed:{default:1,description:\"Simulation speed. 0 freezes the trail in place.\",ui:{type:\"range\",min:0,max:4,step:.1,label:\"Speed\",group:\"Animation\"}}},...Lg((e,t)=>{let{getCpuValue:s,propValues:r}=e,o=Number(r.tintMode)||0,a=p1e(IY(r.trailBlend)),i=(r.trailSource==null?1:Number(r.trailSource))>.5,l=t.createSampler({magFilter:\"linear\",minFilter:\"linear\",addressModeU:\"clamp-to-edge\",addressModeV:\"clamp-to-edge\"}),c=WY(i?\"motion\":\"alpha\",o===2);return{size:uy,format:YY,extraSlots:{prevLive:i?uy:[1,1]},speedProp:\"speed\",paramsSchema:c.Params,bindGroups:({childTexture:d,display:u,paramsBuffer:p},_,f)=>t.createBindGroup(c.layout,{src:d,prev:_.state,next:f.state,display:u,prevLive:_.extra.prevLive,prevLiveOut:f.extra.prevLive,samp:l,params:p}),step:c.kernel,skip:()=>(s(\"trailOpacity\")??1)===0,values:({dt:d})=>{let u=s(\"zoom\")??1,p=Math.max(s(\"trailLength\")??.6,.001);return{persistence:Math.exp(-d/p),diffusion:s(\"diffusion\")??.3,driftX:s(\"driftX\")??0,driftY:s(\"driftY\")??0,dt:d,zoom:Math.pow(u,d*60),hueRate:o===2?c1e*d:0,blendMode:a,motionThreshold:s(\"motionThreshold\")??.06}}}}),gpu:{fragment:({childNode:e,ctx:t,computeOutputs:s,uniforms:r,propValues:o,convertToTexture:a})=>{if(!e)return xt;let i=s?.display,l=(s?.childTexture??a(e)).sample(t.uv);if(!i)return W(Yt,\"unpremultiplyAlpha\",[l]);let c=IY(o.trailBlend),d=pc[c],u=Number(o.tintMode)||0,p=o.trailSource==null?1:Number(o.trailSource),_=z(i.sample(t.uv,\"linearClamp\"),\"trail\");if(u===1){let g=it(_.member(\"a\"),1e-4),y=$t(Ce(_.member(\"rgb\"),g),Qe(.299,.587,.114));_=z(Ot(v(v(r.tint.member(\"rgb\"),v(y,1.5)),_.member(\"a\")),_.member(\"a\")),\"tinted\")}let f=W(Yt,\"unpremultiplyLive\",[l]);if(p===1){let g=W(Yt,\"unpremultiplyTrail\",[_]);return W(Yt,\"unpremultiplyOut\",[W(d,`trailBlend_${c}`,[f,g,r.trailOpacity])])}let h=W(Yt,\"unpremultiplyTrail\",[v(_,r.trailOpacity)]);return W(Yt,\"unpremultiplyOut\",[W(d,`trailBlend_${c}`,[h,f,$e(1)])])}}});var VY=H({name:\"Tint\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Apply a color tint to the image\",props:{color:{default:\"#ff8800\",transform:ce,description:\"Tint color\",ui:{type:\"color\",label:\"Tint Color\",group:\"Colors\"}},amount:{default:.5,description:\"Tint amount (0 = no tint, 1 = full tint)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Amount\",group:\"Effect\"}},preserveLuminosity:{default:!0,compileTime:!0,description:\"Preserve original brightness\",transform:e=>e?1:0,ui:{type:\"checkbox\",label:\"Preserve Luminosity\",group:\"Effect\"}}},effect:d8({color:T(\"color\"),amount:T(\"amount\"),preserveLuminosity:T(\"preserveLuminosity\")}),missingChildMessage:\"You must pass a child component into the Tint shader.\"});function py(e,t,s){let r=Number.isFinite(s)?s:.5;if(e!=null&&typeof e==\"object\"&&typeof e.value==\"number\"){let o=e;return Number.isFinite(o.value)?o.unit!==\"px\"?o.value:Number.isFinite(t)&&t!==0?o.value/(2*t):r:r}return typeof e==\"number\"&&Number.isFinite(e)?e:r}var NY=H({role:\"shape\",name:\"Trapezoid\",description:\"Trapezoid with adjustable top and bottom widths and height\",shape:{distance:({x:e,y:t},s)=>vR(e,t,s(\"topWidth\"),s(\"bottomWidth\"),s(\"height\")),colorDescription:\"Fill color of the trapezoid\",centerDescription:\"Center position of the trapezoid\",bounds:{custom:{propBindings:{x:{prop:\"center\",as:\"position-x\"},y:{prop:\"center\",as:\"position-y\"},rotation:{prop:\"rotation\",as:\"degrees\"}},computeBounds(e,t,s){let r=py(e.bottomWidth,s,.35),o=typeof e.topWidth==\"number\"?e.topWidth:.2,a=py(e.height,s,.25),i=typeof e.center?.x==\"number\"?e.center.x:.5,l=typeof e.center?.y==\"number\"?e.center.y:.5;return{centerXPx:i*t,centerYPx:l*s,widthPx:Math.max(o,r)*2*s,heightPx:a*2*s,rotationDeg:e.rotation??0}},writeBounds(e,t,s,r){let o=(p,_)=>p!=null&&typeof p==\"object\"&&p.unit===\"px\"?{value:Math.max(0,Math.round(_*2*r)),unit:\"px\"}:Math.max(.001,_),a=py(t.bottomWidth,r,.35),i=typeof t.topWidth==\"number\"?t.topWidth:.2,l=Math.max(i,a),c=r>0?e.widthPx/(2*r):l,d=l>0?c/l:1,u=t.center??{x:.5,y:.5};return{center:{...u,x:s>0?e.centerXPx/s:u.x??.5,y:r>0?e.centerYPx/r:u.y??.5},bottomWidth:o(t.bottomWidth,a*d),topWidth:Math.max(.001,i*d),height:o(t.height,r>0?e.heightPx/(2*r):py(t.height,r,.25)),rotation:e.rotationDeg}},freeResize:!0}},shapeProps:{bottomWidth:{default:.35,description:\"Half-width of the bottom edge\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Bottom Width\",group:\"Shape\",units:[\"%\",\"px\"],dimensional:\"half-canvas-height\"}},topWidth:{default:.2,description:\"Half-width of the top edge\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Top Width\",group:\"Shape\"}},height:{default:.25,description:\"Half-height of the trapezoid\",ui:{type:\"range\",min:.01,max:1,step:.01,label:\"Height\",group:\"Shape\",units:[\"%\",\"px\"],dimensional:\"half-canvas-height\"}}}}});var $Y=JR({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"flippedY\"}),thickness:T(\"thickness\"),softness:T(\"softness\"),variation:T(\"variation\"),speedVariance:T(\"speedVariance\")}),XY=H({name:\"TriangularGrid\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Tiling grid of equilateral triangles with optional animated row offsets\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#1a1a1a\",transform:ce,description:\"Triangle fill color\",ui:{type:\"color\",label:\"Fill\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Grid line color\",ui:{type:\"color\",label:\"Lines\",group:\"Colors\"}},cells:{default:8,description:\"Number of triangle rows down the shortest canvas edge\",ui:{type:[\"range\",\"map\"],min:1,max:40,step:1,label:\"Cells\",group:\"Effect\"}},thickness:{default:1,description:\"Thickness of the grid lines (0 = no lines)\",ui:{type:[\"range\",\"map\"],min:0,max:10,step:.1,label:\"Thickness\",group:\"Effect\"}},rotation:{default:0,transform:cs,description:\"Rotation of the grid in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the line edges (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},variation:{default:0,description:\"Per-triangle random lightening/darkening of the fill color\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Effect\"}},speed:{default:0,description:\"Animates the triangle rows drifting horizontally (0 = static)\",ui:{type:\"range\",min:-3,max:3,step:.01,label:\"Speed\",group:\"Animation\"}},speedVariance:{default:.3,description:\"Per-row random speed variance for irregular drifting motion\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Speed Variance\",group:\"Animation\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:Sc(T(\"colorA\"),$Y.shade),stroke:T(\"colorB\"),mask:$Y.lines,space:T(\"colorSpace\")})});var HY=H({name:\"Tritone\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Map colors to three tones: shadows, midtones, highlights\",props:{colorA:{default:\"#ce1bea\",transform:ce,description:\"First color (used for shadows/darkest areas)\",ui:{type:\"color\",label:\"Color A (Shadows)\",group:\"Colors\"}},colorB:{default:\"#2fff00\",transform:ce,description:\"Second color (used for midtones)\",ui:{type:\"color\",label:\"Color B (Midtones)\",group:\"Colors\"}},colorC:{default:\"#ffff00\",transform:ce,description:\"Third color (used for highlights/brightest areas)\",ui:{type:\"color\",label:\"Color C (Highlights)\",group:\"Colors\"}},blendMid:{default:.5,description:\"Midpoint position between the three colors\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.1,label:\"Midpoint\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},effect:_8({colorA:T(\"colorA\"),colorB:T(\"colorB\"),colorC:T(\"colorC\"),blendMid:T(\"blendMid\"),colorSpace:T(\"colorSpace\")})});var jY=H({name:\"Truchet\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Quarter-circle arc tiles that connect to form organic, maze-like flowing curves\",acceptsUVContext:!0,props:{colorA:{default:\"#000000\",transform:ce,description:\"Background color between the arcs\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ffffff\",transform:ce,description:\"Arc line color\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},cells:{default:10,description:\"Number of tiles across the shortest canvas edge\",ui:{type:[\"range\",\"map\"],min:2,max:40,step:1,label:\"Cells\",group:\"Effect\"}},thickness:{default:2,description:\"Thickness of the arc lines\",ui:{type:[\"range\",\"map\"],min:0,max:20,step:.1,label:\"Thickness\",group:\"Effect\"}},rotation:{default:0,transform:cs,description:\"Rotation of the tiling in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}},softness:{default:0,description:\"Softness of the arc edges (0 = crisp, 1 = very soft)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\"}},seed:{default:0,description:\"Random seed \\u2014 changes which tiles flip, producing a different maze pattern\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:Zr({fill:T(\"colorA\"),stroke:T(\"colorB\"),mask:tD({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"clockwise\"}),thickness:T(\"thickness\"),softness:T(\"softness\"),seed:T(\"seed\")}),space:T(\"colorSpace\")})});var KY=H({name:\"Twirl\",role:\"warp\",category:\"Distortions\",description:\"Rotate and twist content around a center point\",boundingBoxDeclaration:{aspectRatio:null},props:{center:zo(\"The center point of the twirl effect\"),intensity:{default:1,description:\"The strength of the twirl effect\",ui:{type:[\"range\",\"map\"],min:-5,max:5,step:.1,label:\"Intensity\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},map:HL({center:T(\"center\"),intensity:T(\"intensity\")}),missingChildMessage:\"You must pass a child component into the Twirl shader.\"});var QY=H({name:\"VenetianBlinds\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Transitions\",description:\"Wipe the content away behind a set of parallel closing strips\",props:{progress:{default:.5,description:\"How far the blinds have closed (0 = fully visible, 1 = fully wiped away)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Progress\",group:\"Transition\"}},angle:{default:0,description:\"Orientation of the strips in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Transition\"}},stripCount:{default:5,description:\"Number of strips across the frame\",ui:{type:[\"range\",\"map\"],min:2,max:60,step:1,label:\"Strips\",group:\"Transition\"}},feather:{default:.15,description:\"Softness of each closing strip edge\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Feather\",group:\"Transition\"}},invert:{default:!1,transform:rs,description:\"Close each strip from the opposite edge\",ui:{type:\"checkbox\",label:\"Invert\",group:\"Transition\"}}},effect:to({coverage:eo.directional(T(\"angle\")).tiled(T(\"stripCount\")),progress:T(\"progress\"),feather:T(\"feather\"),invert:T(\"invert\")}),missingChildMessage:\"You must pass a child component into the Venetian Blinds shader.\"});var ZY=H({role:\"shape\",name:\"Vesica\",description:\"Vesica piscis (lens shape) formed by the intersection of two overlapping circles\",shape:{distance:({x:e,y:t},s)=>_R(e,t,s(\"radius\"),s(\"spread\")),colorDescription:\"Fill color of the vesica\",centerDescription:\"Center position of the vesica\",bounds:{size:{width:\"radius\",height:\"radius\"}},shapeProps:{radius:{default:.35,description:\"Radius of the two overlapping circles\",ui:{type:\"range\",min:.05,max:1,step:.01,label:\"Size\",group:\"Shape\"}},spread:{default:.5,description:\"Circle separation \\u2014 0 = full circle overlap, 1 = infinitely thin lens\",ui:{type:\"range\",min:.05,max:.95,step:.01,label:\"Spread\",group:\"Shape\"}}}}});var JY=H({name:\"VHS\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Analog VHS tape with intermittent tape damage, chroma bleed, and per-scanline noise\",props:{wobble:{default:1,description:\"Overall amount of tape damage \\u2014 waves, creases, and head-switching noise. Bursts on and off organically over time.\",ui:{type:[\"range\",\"map\"],min:0,max:5,step:.01,label:\"Wobble\",group:\"Effect\"}},scanlineNoise:{default:.6,description:\"Per-scanline fine chroma/luma jitter. Adds the classic horizontal-streak detail.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Scanline Noise\",group:\"Effect\"}},smear:{default:.2,description:\"Horizontal chroma smear (color bleed) amount. Positive trails color to the right (classic VHS), negative trails it to the left.\",ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.01,label:\"Smear\",group:\"Effect\"}},speed:{default:1,description:\"Animation speed of the tape effects.\",ui:{type:\"range\",min:.1,max:3,step:.1,label:\"Speed\",group:\"Effect\"}}},effect:$o(pU({warp:uU({wobble:T(\"wobble\"),jitter:T(\"scanlineNoise\"),speed:T(\"speed\")}),smear:T(\"smear\")}),[yU({wobble:T(\"wobble\"),speed:T(\"speed\")})])});var eW=H({name:\"Vibrance\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Adjustments\",description:\"Selective saturation adjustment protecting skin tones\",props:{intensity:{default:0,description:\"The intensity of the vibrance effect\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-2,max:2,step:.1,label:\"Intensity\",group:\"Adjustments\"}}},effect:r8(T(\"intensity\")),identityWhen:Uo(\"intensity\"),missingChildMessage:\"You must pass a child component into the Vibrance shader.\"});var tW=H({name:\"VideoTexture\",role:\"media\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Display a video with customizable playback and object-fit modes\",acceptsUVContext:!0,naturalSizeKey:{fromProp:\"url\"},props:{url:{default:\"https://shaders.com/sample.mp4\",description:\"Upload a video or provide a URL\",ui:{type:\"video-upload\",label:\"Video\",group:\"Media\"}},objectFit:up(\"fill\",\"How the video should be sized within the viewport\",{allowNone:!1}),loop:{default:!0,description:\"Loop the video playback\",ui:{type:\"checkbox\",label:\"Loop\",group:\"Playback\"}}},...UL({src:T(\"url\"),fit:T(\"objectFit\"),loop:T(\"loop\"),decode:\"srgb-linear-alpha-cut\",metadataTimeoutMs:1e4,onError:(e,{stage:t})=>{t===\"autoplay\"?console.warn(\"[VideoTexture] Autoplay failed (browser policy):\",e):console.error(\"[VideoTexture] Failed to load video:\",e)}})});var sW=H({name:\"Vignette\",role:\"filter\",species:\"pointwise\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Darkens or tints the edges of the frame, drawing attention toward the center\",props:{color:{default:\"#000000\",transform:ce,description:\"Color of the vignette at the edges\",ui:{type:\"color\",label:\"Color\",group:\"Colors\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center of the clear area where the vignette begins\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},radius:{default:.5,description:\"Distance from center where the vignette begins to fade in\",ui:{type:[\"range\",\"map\"],min:0,max:1.5,step:.01,label:\"Radius\",group:\"Effect\"}},falloff:{default:.5,description:\"Width of the transition zone from clear to full vignette\",ui:{type:[\"range\",\"map\"],min:.01,max:1.5,step:.01,label:\"Falloff\",group:\"Effect\"}},intensity:{default:1,description:\"Strength of the vignette effect\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Intensity\",group:\"Effect\"}}},effect:Kz(T(\"color\"),{amount:tR({center:T(\"center\"),radius:T(\"radius\"),falloff:T(\"falloff\")}).times(T(\"intensity\"))}),identityWhen:Uo(\"intensity\"),missingChildMessage:\"You must pass a child component into the Vignette shader.\"});var rW=F_(eL({scale:T(\"scale\"),seed:T(\"seed\")})),oW=H({name:\"Voronoi\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Cellular pattern where each pixel is colored by its distance to the nearest of many scattered points\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#3186cf\",transform:ce,description:\"Color near each cell's center point\",ui:{type:\"color\",label:\"Color A (Center)\",group:\"Colors\"}},colorB:{default:\"#fc02dd\",transform:ce,description:\"Color at cell boundaries, far from any center point\",ui:{type:\"color\",label:\"Color B (Edge)\",group:\"Colors\"}},stops:Ms(),colorBorder:{default:\"#000000\",transform:ce,description:\"Color of the cell boundary lines\",ui:{type:\"color\",label:\"Border Color\",group:\"Colors\"}},scale:{default:6,description:\"Number of cells across the canvas\",ui:{type:[\"range\",\"map\"],min:1,max:20,step:.5,label:\"Scale\",group:\"Effect\"}},speed:{default:.5,description:\"Animation speed \\u2014 how fast the cell points drift\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},seed:{default:0,description:\"Random seed \\u2014 shifts the cell pattern without changing the overall structure\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},edgeIntensity:{default:.5,description:\"Controls how much of the cell interior is filled by the edge color. Low = center color dominates with a sharp boundary. High = edge color spreads further into the cell.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Edge Intensity\",group:\"Effect\"}},edgeSoftness:{default:.05,description:\"Width of the cell boundary lines.\",ui:{type:[\"range\",\"map\"],min:0,max:.4,step:.005,label:\"Edge Softness\",group:\"Effect\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}}},paint:rL(Vs(F_(tL(rW,{edgeIntensity:T(\"edgeIntensity\")})),Us(T(\"colorSpace\"))),sL(rW,{softness:T(\"edgeSoftness\"),scale:T(\"scale\")}),{color:T(\"colorBorder\")})});var aW=bt,m1e=JSON.stringify({type:\"sphere3D\",radius:.35}),_1e=.12,f1e=.35,nW=[3,7.5],v1e=.6,iW=[.04,.12],h1e=.75,lW=H({name:\"Voxels\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Rebuild any shape out of voxels \\u2014 a flat shape becomes chunky pixel art, a 3D shape a lit voxel model with smooth ambient occlusion, cast shadows, a key light you can orbit or drive, and per-cube color\",props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the voxel model\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the voxel model (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the voxel model in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},voxelSize:{default:.035,description:\"Edge length of one voxel relative to the shape field \\u2014 small for fine detail, large for chunky blocks\",ui:{type:\"range\",min:.006,max:.15,step:.001,label:\"Voxel Size\",group:\"Voxels\"}},voxelShape:{default:\"cube\",compileTime:!0,description:\"What each cell is built from: hard cubes, rounded cubes (see Bevel) or spheres\",ui:{type:\"select\",options:[{label:\"Cube\",value:\"cube\"},{label:\"Rounded Cube\",value:\"rounded\"},{label:\"Sphere\",value:\"sphere\"}],label:\"Voxel Shape\",group:\"Voxels\"}},voxelScale:{default:1,description:\"Size of each voxel inside its cell \\u2014 1 = touching neighbours, lower opens gaps between the blocks\",ui:{type:\"range\",min:.3,max:1,step:.01,label:\"Voxel Fill\",group:\"Voxels\"}},fill:{default:0,description:\"How eagerly cells fill in along the surface \\u2014 negative carves a thinner model, positive puffs it out\",ui:{type:\"range\",min:-.5,max:.5,step:.01,label:\"Surface Bias\",group:\"Voxels\"}},bevel:{default:.08,description:\"Edge rounding of each voxel as a fraction of its size \\u2014 a hairline chamfer catching light on cubes, the corner radius of rounded cubes\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Bevel\",group:\"Voxels\"}},depth:{default:.12,description:\"Thickness given to flat shapes (2D shapes and flat SVGs) so they become a slab of voxels \\u2014 3D shapes ignore it\",ui:{type:\"range\",min:.01,max:.6,step:.005,label:\"Depth\",group:\"Voxels\"}},gridSpace:{default:\"shape\",compileTime:!0,description:\"Model: the grid rotates with the shape like a built voxel model. Screen: the grid is fixed to the canvas and the shape moves through it like a 3D pixelation\",ui:{type:\"select\",options:[{label:\"Model\",value:\"shape\"},{label:\"Screen\",value:\"view\"}],label:\"Grid\",group:\"Voxels\"}},colorA:{default:\"#6ea8ff\",transform:ce,description:\"Base color of the voxels (the first color of the palette mode)\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#1d2b4f\",transform:ce,description:\"Second palette color \\u2014 blended in by height, depth, per voxel or on the side faces\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},colorMode:{default:\"height\",compileTime:!0,description:\"How the two colors are laid over the model: one solid color, a gradient by height or depth, a random pick per voxel, or Color A on top faces and Color B on the sides\",ui:{type:\"select\",options:[{label:\"Solid\",value:\"solid\"},{label:\"Height\",value:\"height\"},{label:\"Depth\",value:\"depth\"},{label:\"Random\",value:\"random\"},{label:\"Top / Sides\",value:\"faces\"}],label:\"Palette\",group:\"Colors\"}},colorVariation:{default:.25,description:\"Per-voxel tonal jitter \\u2014 the slightly-off shades that make blocks read as individual bricks\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Variation\",group:\"Colors\"}},colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},lightAngle:{default:225,description:\"Direction the key light comes from, in degrees around the canvas \\u2014 shadows fall away from it\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Light Angle\",group:\"Light\"}},lightElevation:{default:42,description:\"How high the key light sits above the canvas \\u2014 low for long raking shadows, high for a flat top-down light\",ui:{type:[\"range\",\"map\"],min:5,max:85,step:1,label:\"Light Elevation\",group:\"Light\"}},lightColor:{default:\"#fff2df\",transform:ce,description:\"color of the key light\",ui:{type:\"color\",label:\"Light Color\",group:\"Light\"}},lightIntensity:{default:1.1,description:\"Strength of the key light\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Light Intensity\",group:\"Light\"}},ambientColor:{default:\"#9fb4d8\",transform:ce,description:\"Sky color of the ambient light \\u2014 the fill on faces the key light misses\",ui:{type:\"color\",label:\"Ambient Color\",group:\"Light\"}},ambient:{default:.55,description:\"Strength of the ambient sky/ground fill\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Ambient\",group:\"Light\"}},shadows:{default:.85,description:\"Darkness of the shadows voxels cast onto each other\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadows\",group:\"Light\"}},shadowSoftness:{default:.35,description:\"Penumbra of the cast shadows \\u2014 0 is razor sharp, higher spreads the light into a soft area source whose shadows sharpen at contact\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Shadow Softness\",group:\"Light\"}},ao:{default:1,description:\"Ambient occlusion in the creases between voxels \\u2014 the soft contact darkening that sells the geometry\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Occlusion\",group:\"Light\"}},glossiness:{default:.35,description:\"How tight the specular highlight is \\u2014 satin plastic at 0, lacquered at 1\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Glossiness\",group:\"Surface\"}},specular:{default:.6,description:\"Strength of the specular highlight and the fresnel rim on grazing faces\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Specular\",group:\"Surface\"}},seams:{default:.2,description:\"Dark seam lines along every voxel edge \\u2014 0 for seamless blocks, higher for a drawn-outline brick look\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Seams\",group:\"Surface\"}},shape:{default:m1e,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...YL({style:T(\"voxelShape\"),gridSpace:T(\"gridSpace\"),voxelSize:T(\"voxelSize\"),fill:T(\"fill\"),voxelScale:T(\"voxelScale\"),bevel:T(\"bevel\"),shadowSoftness:T(\"shadowSoftness\"),lightAngle:T(\"lightAngle\"),lightElevation:T(\"lightElevation\"),depth:T(\"depth\"),surface:(e,t,s)=>{let{uniforms:r,propValues:o}=s,a=e.normal,i=ug(T(\"colorSpace\"),s),l=F=>W(i,\"mixColors\",[r.colorA,r.colorB,F]).member(\"rgb\"),c=z(Ge(er(a.member(\"y\")),0,1),\"upness\"),d;switch(o.colorMode){case\"height\":d=l(ne(1,e.heightT));break;case\"depth\":d=l(e.depthT);break;case\"random\":d=l(e.cellHash);break;case\"faces\":d=l(ne(1,ot(.35,.75,c)));break;default:d=r.colorA.member(\"rgb\")}let u=I(1,v(ne(e.cellHash,.5),v(r.colorVariation,v1e))),p=z(v(d,u),\"albedo\"),_=v(r.lightAngle,aW),f=v(r.lightElevation,aW),h=z(Do(f),\"cosEl\"),g=z(Os(Qe(v(Do(_),h),v(Vt(_),h),er(Vt(f)))),\"L\"),y=z(Pl(a,g,{wrap:_1e}),\"ndl\"),b=z(ne(1,v(e.shadow,r.shadows)),\"lit\"),x=z(v(r.lightColor.member(\"rgb\"),r.lightIntensity),\"keyRgb\"),P=v(x,v(y,b)),C=z(ne(1,v(r.ao,ne(1,e.ao))),\"occl\"),E=z(r.ambientColor.member(\"rgb\"),\"sky\"),R=z(v(v(et(v(E,f1e),E,c),r.ambient),it(C,0)),\"ambient\"),U=e.view,L=v(x,v(v(v(ts(Ge($t(a,z(Os(ne(g,U)),\"H\")),0,1),z(U_(et(nW[0],nW[1],r.glossiness)),\"shininess\")),et(.2,1.4,r.glossiness)),r.specular),b)),k=v(E,v(v(Ap(ni(a,U),{r0:.02,gain:v(r.glossiness,.5)}),v(r.specular,.5)),it(C,0))),D=ot(ne(1,I(iW[0],v(r.seams,iW[1]))),1,e.edge),O=ne(1,v(v(r.seams,h1e),D));return Ot(Ge(v(I(v(p,I(P,R)),I(L,k)),O),Qe(0,0,0),Qe(1,1,1)),e.coverage)}})});var g1e=JSON.stringify({type:\"sphere3D\",radius:.35}),g5=(e,t)=>{let s=z(e,t),r=pa(s),o=v(pa(I(v(s,2.07),rt(4.3,9.1))),.5),a=v(pa(I(v(s,4.13),rt(1.7,8.2))),.25);return v(I(I(r,o),a),.52)},Lp=e=>v(pa(e),.52),cW=(e,t,s,r,o,a,i,l)=>{let c=z(ne(v(e,o),v(t,a)),`${l}Ex`),d=z(I(v(e,a),v(t,o)),`${l}Ey`),u=z(Ge(I(v(d,.5),.5),0,1),`${l}Up`),p=Qe(.9,.96,1),_=Qe(.2,.46,.85),f=et(Qe(.04,.1,.16),et(p,_,kt(ot(.5,1,u))),kt(ot(.4,.55,u))),h=rt(v(s,.8),I(v(r,.8),.12)),g=z(et(.5,.13,i),`${l}SunRad`),y=Kr(ne(rt(c,d),h)),b=ot(g,v(g,.15),y);return I(f,v(Qe(1,.97,.88),v(b,1.5)))},dW=H({name:\"Water\",role:\"shapeEffect\",species:\"custom\",boundingBoxDeclaration:is,category:\"Shape Effects\",description:\"Translucent water \\u2014 a refractive, light-absorbing body that deepens to its color with thickness, wrapped in a wind-driven wave surface that reflects a procedural sky, with Fresnel-bright grazing edges and foam breaking on the crests and shoreline\",animatedTime:{speed:\"speed\"},props:{origin:{default:\"center\",description:\"Reference edge the center position is measured from (center default). Lets you pin the shape relative to a corner or the canvas centre.\",ui:{type:\"origin\",label:\"Origin\",group:\"Position\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center position of the water shape\",ui:{type:\"position\",label:\"Center\",group:\"Position\",units:[\"%\",\"px\"]}},scale:{default:1,description:\"Scale of the water shape (1 = default size)\",ui:{type:\"range\",min:.1,max:3,step:.01,label:\"Scale\",group:\"Position\"}},rotation:{default:0,description:\"Rotation of the water shape in degrees\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Rotation\",group:\"Position\"}},waterColor:{default:\"#00bfeb\",transform:ce,description:\"The color the water saturates toward with depth\",ui:{type:\"color\",label:\"Water Color\",group:\"Water\"}},clarity:{default:.4,description:\"How much of the reflected sky transmits through the body\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Clarity\",group:\"Water\"}},depth:{default:5,description:\"How quickly the water darkens and saturates toward its color with thickness\",ui:{type:\"range\",min:0,max:8,step:.01,label:\"Depth\",group:\"Water\"}},shallows:{default:.5,description:\"Brightness of the thin shallow water at the edges \\u2014 high fades the rim to light translucent water, low keeps the edges close to the deep water color\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Shallows\",group:\"Water\"}},caustics:{default:.15,description:\"Intensity of the light caustics bubbling up through the body \\u2014 bright veins of focused light churning inside the water\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Caustics\",group:\"Water\"}},causticScale:{default:6,description:\"Size of the caustic veins \\u2014 higher = finer, busier light patterns inside the body\",ui:{type:\"range\",min:1,max:20,step:.1,label:\"Caustic Scale\",group:\"Water\"}},choppiness:{default:.8,description:\"Height of the wind-driven waves \\u2014 the depth of the folds that bend the reflection\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Choppiness\",group:\"Waves\"}},waveScale:{default:3.5,description:\"Scale of the wave fronts \\u2014 higher = smaller, busier ripples\",ui:{type:\"range\",min:.5,max:8,step:.01,label:\"Wave Scale\",group:\"Waves\"}},swirl:{default:.6,description:\"Swirl of the flow \\u2014 domain-warps the wave fronts into curling eddies\",ui:{type:\"range\",min:0,max:1.5,step:.01,label:\"Swirl\",group:\"Waves\"}},speed:{default:.5,description:\"Speed of the rolling waves. 0 pauses the surface.\",ui:{type:\"range\",min:0,max:4,step:.01,label:\"Speed\",group:\"Waves\"}},reflection:{default:2,description:\"Strength of the reflected sky across the surface\",ui:{type:\"range\",min:0,max:2,step:.01,label:\"Reflection\",group:\"Surface\"}},envRotation:{default:0,description:\"Rotates the reflected sky \\u2014 spins where the bright sky and sun fall\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Env Rotation\",group:\"Surface\"}},sharpness:{default:.1,description:\"Tightness of the sun glint and reflected sun\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Sharpness\",group:\"Surface\"}},lightAngle:{default:30,description:\"Direction of the sun, in degrees \\u2014 drives the glint and where the sun reflects\",ui:{type:\"range\",min:0,max:360,step:1,label:\"Light Angle\",group:\"Surface\"}},foam:{default:.3,description:\"White foam breaking on the wave crests and along the shoreline edge\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Foam\",group:\"Surface\"}},edgeSoftness:{default:.05,description:\"Softness of the shape boundary edge\",ui:{type:\"range\",min:0,max:1,step:.01,label:\"Edge Softness\",group:\"Surface\"}},shape:{default:g1e,description:\"Serialized shape configuration (JSON)\",ui:{type:\"shape\",label:\"Shape\",group:\"Shape\"}},shapeSdfUrl:{default:\"\",compileTime:!0,description:\"URL to a pre-generated SDF .bin file\"},shapeType:{default:\"\",compileTime:!0,description:\"Active SDF shape type\"}},...Ts({pattern:\"raw\",surface:(e,t)=>{let{uniforms:s}=t,r=wt(t),o=e.volumetric,a=Lr(e,t,{scale:s.scale}),{sdf:i,pxH:l}=a,c=Er(s.lightAngle,\"light\"),d=c.member(\"x\"),u=c.member(\"y\"),p=Er(s.envRotation,\"env\"),_=p.member(\"x\"),f=p.member(\"y\"),h=Ln(a),g;if(o)g=Mc(a);else{let us=z(v(ne(1,Ge(Ce(v(i,-1),.05),0,1)),1.6),\"rimBevel\");g=z(Os(Qe(v(h.member(\"x\"),us),v(h.member(\"y\"),us),-1)),\"nGeo\")}let y=go(e,a),b=y.member(\"x\"),x=y.member(\"y\"),P=z(v(s.waveScale,2.4),\"freq\"),C=z(v(r,.2),\"flowT\"),E=z(rt(v(v(b,P),.8),v(v(x,P),1.25)),\"q\"),R=v(s.swirl,.9),U=z(I(I(E,v(Tg(v(E,.6),C),R)),rt(0,v(C,-1))),\"qw\"),L=.06,k=z(g5(U,\"fbmH0\"),\"h0\"),D=z(Lp(U),\"h0n1\"),O=Ge(Ce(ne(Lp(I(U,rt(L,0))),D),L),-4,4),F=Ge(Ce(ne(Lp(I(U,rt(0,L))),D),L),-4,4),q=.025,Y=z(I(I(v(U,3.3),rt(v(C,1.7),v(C,-1.15))),rt(11.3,4.7)),\"qf\"),V=z(Lp(Y),\"f0n1\"),X=Ge(Ce(ne(Lp(I(Y,rt(q,0))),V),q),-4,4),Q=Ge(Ce(ne(Lp(I(Y,rt(0,q))),V),q),-4,4),te=z(v(s.choppiness,.5),\"relief\"),J=z(v(s.choppiness,.22),\"reliefFine\"),oe=z(Os(Qe(ne(ne(g.member(\"x\"),v(O,te)),v(X,J)),ne(ne(g.member(\"y\"),v(F,te)),v(Q,J)),g.member(\"z\"))),\"n\"),le=z(Ge(s.sharpness,0,1),\"sharp\"),se=z(Lo(Qe(0,0,1),oe),\"R\"),Te=z(v(cW(se.member(\"x\"),se.member(\"y\"),d,u,_,f,le,\"refl\"),s.reflection),\"reflCol\"),de=z(v(Hu(a,e),s.depth),\"thickness\"),xe=z(Ge(v(de,1.3),0,1),\"depthT\"),Ae=z(s.waterColor.member(\"rgb\"),\"wc\"),De=z(wg(de,Qe(v(ne(1,Ae.member(\"x\")),2.2),v(ne(1,Ae.member(\"y\")),2.2),v(ne(1,Ae.member(\"z\")),2.2))),\"transmit\"),Ee=Ge(s.shallows,0,1),ze=z(I(v(et(et(Ae,Qe(1,1,1),kt(Ee)),Ae,kt(xe)),De),v(Ae,v(xe,.18))),\"bodyTint\"),Ie=Un(c,-.7),ke=z(I(v(ts(Ge(I(v($t(oe,Ie.member(\"L\")),.5),.5),0,1),.8),.5),.5),\"diffuse\"),Me=z(ig(Qe(0,0,1),oe,It(1/1.333)),\"tDir\"),we=v(cW(Me.member(\"x\"),Me.member(\"y\"),d,u,_,f,le,\"trans\"),De),Ve=v(Ge(s.clarity,0,1),ne(1,v(xe,.8))),qe=v(et(ze,I(v(we,.6),v(ze,.4)),kt(Ve)),ke),st=z(v(r,3),\"causT\"),pt=z(I(v(rt(b,x),s.causticScale),rt(v(Vt(v(st,.15)),.4),v(v(st,.3),-1))),\"causP\"),Ze=ne(1,ms(g5(pt,\"fbmC1\"))),nt=ne(1,ms(g5(I(v(pt,1.9),rt(3.7,v(v(st,.18),-1))),\"fbmC2\"))),ht=z(ts(Ge(I(v(Ze,.6),v(nt,.4)),0,1),3),\"causV\"),yt=z(v(I(v(xe,.8),.2),s.caustics),\"causMask\"),St=et(I(v(qe,I(1,v(v(ht,yt),.7))),v(Qe(.55,.82,1),v(v(ht,yt),.45))),Te,kt(Ge(Ap(Fi(oe),{gain:.96}),0,1))),Mt=xg(Ge($t(oe,Ie.member(\"H\")),0,1),le,Ot(40,600,.3,1.3)),Rt=I(St,v(Qe(1,.98,.92),Mt)),Kt=z(v(Ge(I(ot(.62,.92,Ge(I(v(k,.5),.5),0,1)),v(ne(1,ot(0,.045,v(i,-1))),.7)),0,1),s.foam),\"foamMask\"),Wt=z(et(Rt,Qe(1,1,1),kt(v(Kt,.85))),\"waterRgb\"),ls=Jr(a,s.edgeSoftness);return Js(Dr(i,l),Ot(Wt,ls),xt,\"water\")}})});var uW=W8({amount:T(\"bleed\")}),pW=H({name:\"Watercolor\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Painterly watercolor look \\u2014 Kuwahara flattening, pigment edge darkening, paper grain and bleeding\",props:{radius:{default:3,compileTime:!0,description:\"Brush size \\u2014 radius of the painterly flattening\",ui:{type:\"range\",min:1,max:8,step:1,label:\"Brush\",group:\"Effect\",hidden:!0}},bleed:{default:1,recompile:Lt(0),description:\"Amount the pigment bleeds and wobbles past hard edges\",ui:{type:[\"range\",\"map\"],min:0,max:3,step:.01,label:\"Bleed\",group:\"Effect\"}},strength:{default:1,recompile:Lt(0),description:\"Blend between the original image (0) and the full watercolor effect (1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},paper:{default:.35,recompile:Lt(0),description:\"Strength of the paper grain the pigment settles into\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Paper\",group:\"Effect\"}},paperColor:{default:\"#fbf7ec\",transform:ce,description:\"Tint of the paper grain\",ui:{type:\"color\",label:\"Paper Color\",group:\"Colors\"}}},effect:N8({at:uW,brush:q8({at:uW,radius:T(\"radius\")}),grain:V8({amount:T(\"paper\")}),paper:T(\"paper\"),paperColor:T(\"paperColor\"),strength:T(\"strength\")})});var mW=H({name:\"WaveDistortion\",role:\"warp\",category:\"Distortions\",description:\"Wave-based distortion with multiple waveform types\",boundingBoxDeclaration:{aspectRatio:null},animatedTime:{speed:\"speed\"},props:{strength:{default:.3,description:\"Distortion intensity\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Strength\",group:\"Effect\"}},frequency:{default:1,description:\"Number of bends/waves\",ui:{type:[\"range\",\"map\"],min:.1,max:10,step:.1,label:\"Frequency\",group:\"Effect\"}},speed:{default:1,description:\"Animation speed\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}},angle:{default:0,description:\"Direction of wave distortion in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Angle\",group:\"Effect\"}},waveType:{default:\"sine\",description:\"Shape of the distortion wave\",compileTime:!0,transform:e=>({sine:0,triangle:1,square:2,sawtooth:3,bounce:4})[e]??0,ui:{type:\"select\",options:[{label:\"Sine\",value:\"sine\"},{label:\"Triangle\",value:\"triangle\"},{label:\"Square\",value:\"square\"},{label:\"Sawtooth\",value:\"sawtooth\"},{label:\"Bounce\",value:\"bounce\"}],label:\"Wave Type\",group:\"Effect\"}},edges:fr(\"stretch\",\"How to handle edges when distortion pushes content out of bounds\")},map:QL({strength:T(\"strength\"),frequency:T(\"frequency\"),angle:T(\"angle\"),waveType:T(\"waveType\")}),missingChildMessage:\"You must pass a child component into the WaveDistortion shader.\"});var y1e=[{waves:[{x:5.1,t:1.3}],weight:.28},{waves:[{x:9.7,t:-1.9},{x:1.7,t:.6}],weight:.24},{waves:[{x:17.3,t:2.7}],weight:.18},{waves:[{x:31.9,t:-3.4},{x:3.3,t:-.9}],weight:.14},{waves:[{x:67.7,t:4.1}],weight:.09},{waves:[{x:131.3,t:-5.3},{x:7.9,t:1.1}],weight:.07}],b1e=.4,x1e=4,A1e=.45,w1e=.6,y5=.002,_W={bars:0,wave:1,line:2,dots:3},fW={mirrored:0,top:1,bottom:2},T1e=e=>typeof e==\"number\"?Object.keys(_W)[e]??\"bars\":e===\"wave\"||e===\"line\"||e===\"dots\"?e:\"bars\",S1e=e=>typeof e==\"number\"?Object.keys(fW)[e]??\"mirrored\":e===\"top\"||e===\"bottom\"?e:\"mirrored\",vW=H({name:\"Waveform\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Audio-visualizer waveform \\u2014 equalizer bars, a filled wave, an oscilloscope line, or a dot matrix \\u2014 driven by a simulated signal whose amplitude you can map to your own audio level\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{style:{default:\"bars\",transform:e=>_W[e]??0,compileTime:!0,description:\"Visualizer style: equalizer bars, a filled wave envelope, an oscilloscope line, or a dot matrix\",ui:{type:\"select\",options:[{label:\"Bars\",value:\"bars\"},{label:\"Wave\",value:\"wave\"},{label:\"Line\",value:\"line\"},{label:\"Dots\",value:\"dots\"}],label:\"Style\",group:\"Style\"}},colorA:{default:\"#3b5bff\",transform:ce,description:\"Color at the start\",ui:{type:\"color\",label:\"Color A\",group:\"Colors\"}},colorB:{default:\"#ff3bd4\",transform:ce,description:\"Color at the end\",ui:{type:\"color\",label:\"Color B\",group:\"Colors\"}},stops:Ms(),colorSpace:{default:\"oklab\",transform:at,compileTime:!0,description:\"Color space for the start\\u2192end gradient\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},from:{default:{x:0,y:.5},transform:Xe,description:\"Where the waveform starts (Color A end)\",ui:{type:\"position\",label:\"Start\",group:\"Position\",units:[\"%\",\"px\"]}},to:{default:{x:1,y:.5},transform:Xe,description:\"Where the waveform ends (Color B end)\",ui:{type:\"position\",label:\"End\",group:\"Position\",units:[\"%\",\"px\"]}},amplitude:{default:1,description:\"Overall signal level (0 = silence). Map this to your own audio level to make the waveform react to real sound.\",ui:{type:[\"range\",\"map\"],min:0,max:2,step:.01,label:\"Amplitude\",group:\"Signal\"}},frequency:{default:1,description:\"How busy the signal is across the width (higher = more peaks)\",ui:{type:[\"range\",\"map\"],min:.2,max:3,step:.01,label:\"Frequency\",group:\"Signal\"}},height:{default:.6,description:\"Maximum vertical extent of the waveform as a fraction of the canvas height\",ui:{type:[\"range\",\"map\"],min:.05,max:1,step:.01,label:\"Height\",group:\"Effect\"}},align:{default:\"mirrored\",transform:e=>fW[e]??0,compileTime:!0,description:\"Which side of the start\\u2192end baseline the waveform grows on: mirrored around it, above it (top), or below it (bottom)\",ui:{type:\"select\",options:[{label:\"Mirrored\",value:\"mirrored\"},{label:\"Top\",value:\"top\"},{label:\"Bottom\",value:\"bottom\"}],label:\"Align\",group:\"Effect\"}},count:{default:32,description:\"Number of bars (or dot columns) across the width\",ui:{type:[\"range\",\"map\"],min:4,max:128,step:1,label:\"Count\",group:\"Effect\",condition:{style:[\"bars\",\"dots\"]}}},barWidth:{default:.6,description:\"Bar thickness as a fraction of its column (1 = bars touch)\",ui:{type:[\"range\",\"map\"],min:.1,max:1,step:.01,label:\"Bar Width\",group:\"Effect\",condition:{style:\"bars\"}}},rounding:{default:1,description:\"Rounds the bar ends (0 = square, 1 = fully rounded caps)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Rounding\",group:\"Effect\",condition:{style:\"bars\"}}},dotSize:{default:.7,description:\"Dot diameter as a fraction of its cell\",ui:{type:[\"range\",\"map\"],min:.2,max:1,step:.01,label:\"Dot Size\",group:\"Effect\",condition:{style:\"dots\"}}},lineWidth:{default:.006,description:\"Stroke thickness of the line. A value of one (1) matches the canvas height.\",ui:{type:[\"range\",\"map\"],min:.001,max:.05,step:.001,label:\"Line Width\",group:\"Effect\",dimensional:\"canvas-height\",condition:{style:\"line\"}}},softness:{default:0,description:\"Feathers the wave's edge into a glow\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Softness\",group:\"Effect\",condition:{style:\"wave\"}}},speed:{default:1,description:\"Animation speed of the simulated signal\",ui:{type:\"range\",min:0,max:3,step:.01,label:\"Speed\",group:\"Animation\"}},seed:{default:1,description:\"Adjusts the starting state, useful for variation\",ui:{type:\"range\",min:0,max:100,step:1,label:\"Seed\",group:\"Animation\"}}},paint:e=>{let t=T1e(e.propValues.style),s=S1e(e.propValues.align),{viewport:r}=aa(e),o=V=>me(T(V),e),{along:a,across:i,length:l}=Pg({from:T(\"from\"),to:T(\"to\")})(e,aa(e)),c=z(Ce(a,l),\"wfX\"),d=z(Ce(1,r.member(\"y\")),\"wfPx\"),u=z(wt(e,\"seed\"),\"wfTime\"),p=o(\"amplitude\"),_=o(\"frequency\"),f=o(\"height\"),h=V=>vp({x:v(V,_),t:u},y1e),g=V=>et(b1e,1,O_(ne(V,.5),x1e)),y=V=>Ge(v(v(h(V),g(V)),p),-1,1),b=V=>Ge(v(v(I(A1e,v(h(V),w1e)),g(V)),p),0,1),x=s===\"mirrored\",P=z(s===\"top\"?er(i):i,\"wfDy\"),C=x?ms(P):P,E=V=>v(v(V,f),x?.5:1),R=V=>x?ne(C,V):it(ne(C,V),er(C)),U=o(\"count\"),L=z(Ce(l,U),\"wfCell\"),k=z(v(c,U),\"wfXCells\"),D=z(Ce(I(Xr(k),.5),U),\"wfColX\"),O=z(v(ne(Qr(k),.5),L),\"wfColDx\"),F;if(t===\"bars\"){let V=z(v(v(o(\"barWidth\"),L),.5),\"wfHalfW\"),X=z(it(E(b(D)),x?V:v(V,2)),\"wfExtent\"),Q=x?X:v(X,.5);F=mp({distance:dg(O,x?P:ne(P,Q),V,Q,v(o(\"rounding\"),V)),footprint:d})}else if(t===\"dots\"){let V=z(Ce(C,L),\"wfRows\"),X=z(v(I(Xr(V),.5),L),\"wfRowY\"),Q=z(E(b(D)),\"wfExtent\"),te=v(ne(1,ot(ne(Q,v(L,.5)),I(Q,v(L,.5)),X)),x?1:ag(0,C)),J=v(v(o(\"dotSize\"),L),.5);F=v(mp({distance:_p(O,v(ne(Qr(V),.5),L),J),footprint:d}),te)}else if(t===\"line\"){let V=te=>E(x?y(te):b(te)),X=z(V(c),\"wfCurve\"),Q=z(Ce(ne(V(I(c,y5)),V(ne(c,y5))),v(2*y5,l)),\"wfSlope\");F=eR({distance:Ce(ne(P,X),ho(I(1,v(Q,Q)))),width:o(\"lineWidth\"),footprint:d})}else F=mp({distance:R(z(E(b(c)),\"wfExtent\")),footprint:I(d,v(o(\"softness\"),.2))});let q=mp({distance:it(er(a),ne(a,l)),footprint:d}),Y=z(Us(T(\"colorSpace\"))(c,e),\"wfColor\");return Ot(Y.member(\"rgb\"),v(v(Y.member(\"a\"),F),q))}});var hW=H({name:\"WaveletNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Rotating banded wavelets that ripple as they animate\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{...ma(),scale:{default:1.5,description:\"Pattern scale (higher = larger patterns)\",ui:{type:\"range\",min:-2,max:5,step:.1,label:\"Scale\",group:\"Effect\"}},detail:{default:1.24,description:\"Per-octave frequency ratio \\u2014 higher adds finer wavelet detail\",ui:{type:\"range\",min:1.05,max:2,step:.01,label:\"Detail\",group:\"Effect\"}},...Ja(),...In()},paint:Vs(ua(Qa(gp(ND({detail:T(\"detail\")})),{scale:T(\"scale\"),seed:T(\"seed\")}),{contrast:T(\"contrast\"),balance:T(\"balance\")}),Us(T(\"colorSpace\")))});var gW=H({name:\"Weave\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Interlaced textile weave pattern with two thread colors going over and under each other\",acceptsUVContext:!0,props:{colorA:{default:\"#c4c4c4\",transform:ce,description:\"Horizontal thread color\",ui:{type:\"color\",label:\"X Color\",group:\"Colors\"}},colorB:{default:\"#4d4d4d\",transform:ce,description:\"Vertical thread color\",ui:{type:\"color\",label:\"Y Color\",group:\"Colors\"}},cells:{default:10,description:\"Number of threads across the shortest canvas edge\",ui:{type:[\"range\",\"map\"],min:2,max:40,step:1,label:\"Cells\",group:\"Effect\"}},gap:{default:.25,description:\"Gap between threads (0 = no gap, 0.5 = maximum gap)\",ui:{type:[\"range\",\"map\"],min:0,max:.45,step:.01,label:\"Gap\",group:\"Effect\"}},rotation:{default:0,description:\"Rotation of the weave pattern in degrees\",ui:{type:[\"range\",\"map\"],min:0,max:360,step:1,label:\"Rotation\",group:\"Effect\"}}},paint:sD({frame:Rn({cells:T(\"cells\"),rotation:T(\"rotation\"),convention:\"plain\"}),gap:T(\"gap\"),colors:[T(\"colorA\"),T(\"colorB\")]})});var yW=H({name:\"WebcamTexture\",role:\"media\",species:\"custom\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Display a live webcam feed with customizable object-fit modes\",acceptsUVContext:!0,naturalSizeKey:{fixed:\"webcam\"},props:{objectFit:up(\"cover\",\"How the webcam feed should be sized within the viewport\",{allowNone:!0}),mirror:{default:!0,description:\"Mirror the webcam feed horizontally (selfie mode)\",ui:{type:\"checkbox\",label:\"Mirror\",group:\"Effect\"}}},...OL({fit:T(\"objectFit\"),mirror:T(\"mirror\"),decode:\"srgb-linear-alpha-cut\",constraints:{video:{width:{ideal:1280},height:{ideal:720},facingMode:\"user\"},audio:!1},naturalSizeKey:\"webcam\",onError:e=>{e instanceof DOMException?e.name===\"NotAllowedError\"?console.error(\"[WebcamTexture] Camera permission denied by user\"):e.name===\"NotFoundError\"?console.error(\"[WebcamTexture] No camera found on this device\"):e.name===\"NotReadableError\"?console.error(\"[WebcamTexture] Camera is already in use by another application\"):console.error(\"[WebcamTexture] Camera error:\",e.message):console.error(\"[WebcamTexture] Failed to start webcam:\",e)}})});var bW=H({name:\"Wool\",role:\"filter\",species:\"custom\",requiresRTT:!0,requiresChild:!0,boundingBoxDeclaration:{aspectRatio:null},category:\"Stylize\",description:\"Applies an interwoven fibrous fabric texture and distortion to child content\",props:Gg({intensity:.5,scale:4,contrast:-.5,distortion:.15}),gpu:{fragment:gg(hg.wool)}});var xW=H({name:\"WorleyNoise\",role:\"generator\",boundingBoxDeclaration:{aspectRatio:null,supportsResizeFit:!0},category:\"Textures\",description:\"Cellular noise field \\u2014 distance-based, with selectable feature combinations and fractal octaves\",acceptsUVContext:!0,animatedTime:{speed:\"speed\"},props:{colorA:{default:\"#ffffff\",transform:ce,description:\"Color where the noise field is low (typically near cell centers)\",ui:{type:\"color\",label:\"Color A (Low)\",group:\"Colors\"}},colorB:{default:\"#000000\",transform:ce,description:\"Color where the noise field is high (typically near cell boundaries)\",ui:{type:\"color\",label:\"Color B (High)\",group:\"Colors\"}},colorSpace:{default:\"linear\",transform:at,compileTime:!0,description:\"Color space for color interpolation\",ui:{type:\"select\",options:dt,label:\"Color Space\",group:\"Colors\"}},scale:{default:6,description:\"Number of cells across the canvas at the base octave\",ui:{type:[\"range\",\"map\"],min:1,max:30,step:.5,label:\"Scale\",group:\"Effect\"}},mode:{default:\"f1\",transform:lL,compileTime:!0,description:\"Field type. F1 = distance to nearest point. F2 = distance to second-nearest. F2 \\u2212 F1 emphasises cell boundaries.\",ui:{type:\"select\",options:[{label:\"F1 (Cells)\",value:\"f1\"},{label:\"F2 (Wider Cells)\",value:\"f2\"},{label:\"F2 \\u2212 F1 (Edges)\",value:\"f2MinusF1\"},{label:\"F1 + F2\",value:\"f1PlusF2\"},{label:\"F1 \\xD7 F2\",value:\"f1TimesF2\"}],label:\"Mode\",group:\"Effect\"}},distance:{default:\"euclidean\",transform:cL,compileTime:!0,description:\"Distance metric. Euclidean = round cells. Manhattan = diamond. Chebyshev = square.\",ui:{type:\"select\",options:[{label:\"Euclidean\",value:\"euclidean\"},{label:\"Manhattan\",value:\"manhattan\"},{label:\"Chebyshev\",value:\"chebyshev\"}],label:\"Distance\",group:\"Effect\"}},octaves:{default:1,compileTime:!0,description:\"Number of fractal layers stacked at progressively finer scales\",ui:{type:\"range\",min:1,max:4,step:1,label:\"Octaves\",group:\"Effect\"}},lacunarity:{default:2,description:\"Scale multiplier between octaves (only active when Octaves > 1)\",ui:{type:[\"range\",\"map\"],min:1.5,max:4,step:.1,label:\"Lacunarity\",group:\"Effect\",condition:{octaves:[2,3,4]}}},persistence:{default:.5,description:\"Amplitude multiplier between octaves (only active when Octaves > 1)\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Persistence\",group:\"Effect\",condition:{octaves:[2,3,4]}}},jitter:{default:1,description:\"How much each cell's point drifts inside its cell. 0 = rigid grid (banded look), 1 = fully random.\",ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Jitter\",group:\"Effect\"}},contrast:{default:1,description:\"Steepness of the gradient between low and high regions\",ui:{type:[\"range\",\"map\"],min:.25,max:4,step:.01,label:\"Contrast\",group:\"Effect\"}},balance:{default:0,description:\"Shifts the gradient midpoint. Negative pulls the field toward Color A, positive toward Color B.\",ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Balance\",group:\"Effect\"}},seed:{default:0,description:\"Random seed \\u2014 shifts the cell pattern without changing its overall structure\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Seed\",group:\"Effect\"}},speed:{default:.5,description:\"Animation speed \\u2014 how fast each cell's point drifts\",ui:{type:\"range\",min:0,max:5,step:.1,label:\"Speed\",group:\"Animation\"}}},paint:JD({scale:T(\"scale\"),jitter:T(\"jitter\"),lacunarity:T(\"lacunarity\"),persistence:T(\"persistence\"),contrast:T(\"contrast\"),balance:T(\"balance\"),seed:T(\"seed\"),mode:T(\"mode\"),distance:T(\"distance\"),octaves:T(\"octaves\"),space:T(\"colorSpace\")})});var AW=H({name:\"ZoomBlur\",role:\"filter\",species:\"gather\",boundingBoxDeclaration:{aspectRatio:null},category:\"Blurs\",description:\"Radial zoom blur expanding from a center point\",props:{intensity:{default:30,description:\"Intensity of the zoom blur effect\",ui:{type:[\"range\",\"map\"],min:0,max:100,step:1,label:\"Intensity\",group:\"Effect\"}},center:{default:{x:.5,y:.5},transform:Xe,description:\"Center point of the zoom blur\",ui:{type:\"position\",label:\"Center\",group:\"Position\"}},dispersion:{default:0,description:\"Spectral color fringing along the streak, like light through a lens; negative flips which end is red\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Dispersion\",group:\"Lens\"}},falloff:{default:0,description:\"Concentrates the dispersion away from the center; 0 fringes everywhere equally\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Falloff\",group:\"Lens\"}},focus:{default:0,description:\"Radius of a clean zone around the center that stays sharp\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Focus\",group:\"Lens\"}},bias:{default:0,description:\"Slides the streak window: 0 streaks inward from each pixel, 1 streaks evenly both ways, -1 pushes it further in\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Bias\",group:\"Streak\"}},highlights:{default:0,description:\"Lets bright pixels streak further than dark ones, like light rays\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Highlights\",group:\"Streak\"}},spiral:{default:0,description:\"Twists the radial streak into a vortex around the center\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:-1,max:1,step:.01,label:\"Spiral\",group:\"Streak\"}},jitter:{default:0,description:\"Per-pixel noise on the tap spacing that hides banding in long streaks\",recompile:Lt(0),ui:{type:[\"range\",\"map\"],min:0,max:1,step:.01,label:\"Jitter\",group:\"Streak\"}}},effect:Tp({path:wp.zoom(T(\"center\")),amount:T(\"intensity\"),detail:{dispersion:T(\"dispersion\"),falloff:T(\"falloff\"),focus:T(\"focus\"),bias:T(\"bias\"),highlights:T(\"highlights\"),spiral:T(\"spiral\"),jitter:T(\"jitter\")}})});var P1e=()=>{let e={};return Object.entries({AngularBlur:MO,Arc:EO,Ascii:Vg,Aurora:RO,BarnDoors:DO,BarShift:LO,Beam:UO,Bend:OO,Blob:GO,BlockDissolve:FO,BlockNoise:YO,BlueNoise:WO,Blur:qO,Boids:$O,BokehBlur:XO,BrickPattern:jO,BrightnessContrast:KO,BrushedMetal:QO,Bulge:ZO,CarbonFiber:JO,Chalkboard:eG,ChannelBlur:tG,Checkerboard:sG,CheckerWipe:rG,Chevron:aG,ChromaFlow:nG,ChromaticAberration:iG,Chrome:lG,Circle:cG,ColorWheel:dG,CompressionArtifacts:_G,ConcentricSpin:fG,ConicGradient:vG,ContourLines:hG,CornerPin:gG,Crescent:yG,Cross:bG,CRTScreen:xG,Crystal:AG,CurlNoise:wG,CursorRipples:TG,CursorTrail:PG,DataMosh:EG,DiamondGradient:kG,DiamondWipe:BG,DiffuseBlur:zG,DisplacementMap:RG,Dither:LG,DotGrid:UG,DropShadow:OG,Duotone:GG,Ellipse:FG,Emboss:yO,Engraving:IG,ErosionNoise:YG,Exposure:WG,FallingLines:NG,FilmGrain:$G,FilmStock:jG,Flip:KG,FloatingParticles:eF,Flower:tF,FlowField:sF,FlowingGradient:rF,FlutedGlass:aF,Fog:uF,Form3D:xO,FractalNoise:pF,Frost:mF,GaborNoise:_F,Glass:vF,GlassTiles:hF,Glitch:gF,Glow:yF,Godrays:bF,Goo:wF,GradientMap:TF,Grayscale:SF,Grid:CF,GridDistortion:MF,Group:EF,Halftone:kF,Heart:BF,Heatmap:zF,HexGrid:DF,Hologram:LF,Holographic:OF,HTMLInCanvas:GF,HueShift:FF,ImageTexture:IF,InkFlow:jF,Invert:KF,IrisWipe:QF,Irradiance:eI,IsometricCubes:tI,Kaleidoscope:sI,KeyFrames:rI,LensDistortion:oI,LensFlare:aI,LightEdge:nI,LightLeak:iI,Line:dI,LinearBlur:uI,LinearGradient:pI,LinearWipe:mI,LiquidMetal:fI,Liquify:vI,MagneticFilings:xI,Marble:AI,MeshGradient:TI,Mirror:SI,MultiPointGradient:PI,Nebula:CI,Neon:fO,NoiseDissolve:MI,ObjectTracker:EI,Obsidian:kI,PagePeel:BI,Paper:zI,Parallelogram:RI,ParticleField:FI,ParticleFlow:HI,Particles:r9,PerlinNoise:o9,Perspective:a9,Pixelate:n9,PixelSort:l9,PixelThrow:u9,Plasma:p9,Plastic:m9,PolarCoordinates:_9,Polygon:f9,Posterize:v9,Prism:h9,ProgressiveBlur:g9,RadialGradient:y9,RadialWipe:b9,RandomBars:x9,ReactionDiffusion:E9,RectangularCoordinates:k9,ReflectivePlane:B9,Repeater:z9,Ring:R9,Ripples:D9,RippleWipe:L9,RoundedRect:U9,Satin:q9,Saturation:V9,Scratches:N9,Sharpness:$9,Shatter:H9,SimplexNoise:j9,SineWave:K9,SliceWipe:Q9,Smoke:sY,SmokeFill:nY,SmokeFlow:uY,Solarize:pY,SolidColor:mY,Sparkle:fY,Spherize:hY,Spiral:gY,Star:yY,Stone:bY,Strands:xY,Stretch:AY,Stripes:wY,StudioBackground:SY,SunBurst:PY,Surface3D:kY,Swirl:zY,Teardrop:RY,Text:h5,ThinFilm:CO,TiltShift:FY,TimeTrail:qY,Tint:VY,Trapezoid:NY,TriangularGrid:XY,Tritone:HY,Truchet:jY,Twirl:KY,VenetianBlinds:QY,Vesica:ZY,VHS:JY,Vibrance:eW,VideoTexture:tW,Vignette:sW,Voronoi:oW,Voxels:lW,Water:dW,Watercolor:pW,WaveDistortion:mW,Waveform:vW,WaveletNoise:hW,Weave:gW,WebcamTexture:yW,Wool:bW,WorleyNoise:xW,ZoomBlur:AW}).forEach(([s,r])=>{let o={};r.props&&Object.entries(r.props).forEach(([a,i])=>{i.ui&&(o[a]={ui:i.ui,default:i.default,description:i.description})}),e[r.name]={name:r.name,fileName:s,category:r.category||\"Uncategorized\",definition:r,propsMetadata:o}}),e},of=P1e(),C1e=(()=>{let e={};for(let t of Object.values(of))for(let s of t.definition.deprecatedNames??[])e[s]=t.name;return e})();function M1e(e){return of[e]?e:C1e[e]??e}function my(){return Object.values(of)}function b5(e){return of[M1e(e)]}var E1e=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(Fh({in:{vertexIndex:Qo.vertexIndex},out:{pos:Qo.position,uv:ps}})`{\n  const pos = array<vec2f, 3>(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3));\n  const uv = array<vec2f, 3>(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1));\n\n  return Out(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]);\n}`,\"fullScreenTriangle\");LC.disable(\"external-omitted\");var k1e=[\"float32-filterable\",\"timestamp-query\"],A5=new WeakMap,x5=new WeakMap,w5=new WeakSet,Up=null,B1e=new WeakSet;async function z1e(e={}){if(e.device&&!w5.has(e.device)){let r=e.device,o=A5.get(r);o||(o=w.initFromDevice({device:r}),A5.set(r,o));let a=wW(o,e.onDeviceLost);return{root:o,device:r,adapter:e.adapter,createdDevice:!1,release:a}}e.device&&di(\"[gpu] injected GPU device was lost \\u2014 falling back to the shared default device\");let t=await R1e(),s=wW(t.root,e.onDeviceLost);return{root:t.root,device:t.device,adapter:t.adapter,createdDevice:!1,release:s}}async function R1e(){for(;;){let t=Up;if(!t)break;let s=await t.catch(()=>null);if(s&&!B1e.has(t)&&!w5.has(s.device))return s;Up===t&&(Up=null)}let e=D1e();Up=e;try{return await e}catch(t){throw Up===e&&(Up=null),t}}async function D1e(){if(typeof navigator>\"u\"||!navigator.gpu)throw new Kd(\"unsupported\",\"[gpu] WebGPU is not available in this environment (navigator.gpu is undefined).\");let e;try{e=await w.init({adapter:{powerPreference:\"high-performance\"},device:{optionalFeatures:k1e}})}catch(t){let s=await G5();throw new Kd(s.supported?\"no-device\":s.reason??\"init-failed\",void 0,{cause:t})}if(!e.device)throw new Kd(\"no-device\",\"[gpu] adapter returned no device.\");return A5.set(e.device,e),{root:e,device:e.device,createdDevice:!0}}function L1e(e,t,s={}){return e.configureContext({canvas:t,alphaMode:\"premultiplied\",...s})}function wW(e,t){let s=e.device,r=x5.get(s);if(r||(r=new Set,x5.set(s,r),e.device.lost.then(a=>{if(w5.add(s),a.reason!==\"destroyed\")for(let i of[...x5.get(s)??[]])i(a)})),!t)return()=>{};let o=r;return o.add(t),()=>o.delete(t)}function U1e(){let e={value:0,advance(t,s){e.value+=t*s}};return e}var TW=new WeakMap;function zl(e){let t=TW.get(e);return t||(t=U1e(),TW.set(e,t)),t}var O1e=(globalThis.__TYPEGPU_AUTONAME__??(e=>e))(gt({time:n,viewportSize:m,logicalViewportSize:m,aspect:n,pointer:m,pointerActive:n}),\"SystemUniforms\");w.fn([m,m],m)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=((t,s)=>{\"use gpu\";return ra(t,s)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"uv\"},{type:\"i\",name:\"viewportSize\"}],body:[0,[[10,[6,\"std.mul\",[\"uv\",\"viewportSize\"]]]]]},externals:{\"std.mul\":()=>ra}})&&e.f)({}));w.fn([m],n)((e=>(globalThis.__TYPEGPU_META__??=new WeakMap).set(e.f=(t=>{\"use gpu\";return __tsover_div(t.x,t.y)}),{v:2,name:void 0,ast:{params:[{type:\"i\",name:\"viewportSize\"}],body:[0,[[10,[1,[7,\"viewportSize\",\"x\"],\"/\",[7,\"viewportSize\",\"y\"]]]]]},externals:{}})&&e.f)({}));function G1e(e){let t=2166136261,s=e.join(\"\u0001\");for(let r=0;r<s.length;r++)t^=s.charCodeAt(r),t=Math.imul(t,16777619);return`${(t>>>0).toString(16).padStart(8,\"0\")}:${s.length}`}function F1e(e={}){let t=e.maxSize??4,s=e.dispose,r=new Map,o=0,a=null,i=null;function l(d){d.lastUsed=++o}function c(){for(;r.size>t;){let d=null;for(let u of r.values())u.hash===a||u.hash===i||(!d||u.lastUsed<d.lastUsed)&&(d=u);if(!d)break;r.delete(d.hash),s?.(d.value,d.hash)}}return{getOrBuild(d,u){let p=r.get(d);if(p)return l(p),d===a?i=null:i=d,p.value;let _=u(),f={hash:d,value:_,lastUsed:++o};return r.set(d,f),a===null?(a=d,i=null):i=d,c(),_},markReady(d){if(d!==i)return;let u=r.get(d);u&&(a=d,i=null,l(u),c())},get renderValue(){let d=a??i;return d?r.get(d)?.value??null:null},get activeHash(){return a},get pendingHash(){return i},release(d){let u=r.get(d);u&&(r.delete(d),s?.(u.value,d),a===d&&(a=null),i===d&&(i=null))},has(d){return r.has(d)},get size(){return r.size},clear(){for(let d of r.values())s?.(d.value,d.hash);r.clear(),a=null,i=null}}}var Op=\"passManager\";function I1e(e,t){let{textureManager:s,dispatcher:r}=t,o=t.canvasFormat??Y1e(),a=t.rttFormat??\"rgba16float\",i=e.device?.limits?.maxSampledTexturesPerShaderStage,l=t.maxSampledTextures??(typeof i==\"number\"&&i>0?i:16),c=t.fullScreenTriangle??E1e,d=new Map,u=new Set,p=null;function _(){let k=s.samplers;try{return e.createBindGroup(V3,{linearClamp:k.linearClamp,nearestClamp:k.nearestClamp,linearRepeat:k.linearRepeat,nearestRepeat:k.nearestRepeat})}catch(D){Sr(`[${Op}] failed to build sampler bind group:`,D);return}}function f(k,D){let O=new Set;for(let F of k.textures)if(F.kind===\"rtt\")if(O.add(F.key),d.has(F.key))d.get(F.key).resize(D.width,D.height);else{let q=s.createRenderTexture({width:D.width,height:D.height,format:a,label:`rtt:${F.key}`});d.set(F.key,q),u.add(q)}for(let[F,q]of[...d])O.has(F)||(q.destroy(),u.delete(q),d.delete(F))}function h(k,D){if(!k.textureLayout||k.reads.length===0)return;let O={};for(let F of k.reads){let q=d.get(F),Y=D?.[F],V=q??Y;if(!V){Sr(`[${Op}] pass \"${k.entry}\" reads unknown texture \"${F}\"`);return}O[F]=V.texture}k.reads.length>l&&di(`[${Op}] pass samples ${k.reads.length} textures (> device limit ~${l}); composition may fail on this device.`);try{return e.createBindGroup(k.textureLayout,O)}catch(F){Sr(`[${Op}] failed to build texture bind group:`,F);return}}function g(k,D){return e.createRenderPipeline({vertex:c,fragment:k.entry,targets:{format:D}})}function y(k,D){if(!k.externalLayout||k.externalReads.length===0)return;let O=k.externalReads;if(O.length===1){let q=D.get(O[0]);if(!q){Sr(`[${Op}] external read \"${O[0]}\" has no registered source`);return}return s.createExternalTextureBinding({layout:k.externalLayout,entryKey:O[0],getSource:q})}let F=null;return{update(){let q={};for(let Y of O){let V=D.get(Y)?.();if(!V)return F=null,null;try{q[Y]=e.device.importExternalTexture({source:V})}catch{return F=null,null}}return F=e.createBindGroup(k.externalLayout,q),F},get current(){return F},destroy(){F=null}}}function b(k,D,O,F){let q=D.kind===\"canvas\"?o:a,Y=k.reads.filter(X=>O?.[X]),V=new Map(Y.map(X=>[X,O[X].texture]));return{fragment:k,pipeline:g(k,q),textureBindGroup:h(k,O),externalBinding:y(k,F),target:D,mediaReads:Y,mediaSnapshot:V}}function x(k,D,O,F){f(k,O);for(let Q of k.computeSteps)Q.bindInputs?.(te=>d.get(te)??F?.[te]);let q=_(),Y=new Map;for(let Q of k.externalTextures)Y.set(Q.key,Q.getSource);let V=k.rttPasses.map(Q=>b(Q.fragment,{kind:\"rtt\",key:Q.textureKey},F,Y)),X=b(k.finalPass,{kind:\"canvas\"},F,Y);p={ir:k,uniformBindGroup:D,samplerBindGroup:q,computeSteps:k.computeSteps,rttPasses:V,finalPass:X,size:O,mediaResources:F}}function P(k,D){let O=k.pipeline;if(p?.uniformBindGroup&&(O=O.with(p.uniformBindGroup)),k.mediaReads&&k.mediaReads.length>0&&p?.mediaResources){let F=!1;for(let q of k.mediaReads)p.mediaResources[q].texture!==k.mediaSnapshot.get(q)&&(F=!0);if(F){k.textureBindGroup=h(k.fragment,p.mediaResources);for(let q of k.mediaReads)k.mediaSnapshot.set(q,p.mediaResources[q].texture)}}if(k.textureBindGroup&&(O=O.with(k.textureBindGroup)),k.fragment.usesSamplers&&p?.samplerBindGroup&&(O=O.with(p.samplerBindGroup)),k.fragment.externalReads.length>0){let F=k.externalBinding?.update()??null;if(!F){k.externalSkipLogged||(di(`[${Op}] external texture not ready \\u2014 skipping pass (${k.fragment.externalReads.join(\", \")})`),k.externalSkipLogged=!0);return}O=O.with(F)}O.withColorAttachment({view:D}).draw(3)}function C(k,D,O){if(p){for(let F of p.computeSteps){let q=F.getComputeNodes(D);q&&q.length&&r.dispatch(q)}O?.(),E(),P(p.finalPass,k)}}function E(){if(p)for(let k of p.rttPasses){let D=d.get(k.target.key);D&&P(k,R(D))}}function R(k){return e.unwrap(k.texture).createView()}function U(k){if(!(!k.fragment.textureLayout||k.fragment.reads.length===0)&&(k.textureBindGroup=h(k.fragment,p?.mediaResources),k.mediaReads&&k.mediaSnapshot&&p?.mediaResources))for(let D of k.mediaReads)k.mediaSnapshot.set(D,p.mediaResources[D].texture)}function L(k,D){let O=!1;for(let F of d.values())F.resize(k,D)&&(O=!0);if(!p)return O;if(p.size={width:k,height:D},!O)return!1;for(let F of p.computeSteps)F.bindInputs?.(q=>d.get(q)??p.mediaResources?.[q]);for(let F of p.rttPasses)U(F);return U(p.finalPass),!0}return{setComposition:x,render:C,repaintRtt:E,resize:L,get textureCount(){return u.size},get passCount(){return p?p.rttPasses.length+1:0},dispose(){for(let k of[...u])k.destroy();u.clear(),d.clear(),p=null}}}function Y1e(){return typeof navigator<\"u\"&&navigator.gpu?navigator.gpu.getPreferredCanvasFormat():\"bgra8unorm\"}function SW(e){return`${e}-srgb`}function W1e(e){let t=new Set;function s(f,...h){return e.createTexture.call(e,f).$usage(...h)}let r,o,a,i,l={get linearClamp(){return r??=e.createSampler({magFilter:\"linear\",minFilter:\"linear\",mipmapFilter:\"linear\",addressModeU:\"clamp-to-edge\",addressModeV:\"clamp-to-edge\"})},get nearestClamp(){return o??=e.createSampler({magFilter:\"nearest\",minFilter:\"nearest\",addressModeU:\"clamp-to-edge\",addressModeV:\"clamp-to-edge\"})},get linearRepeat(){return a??=e.createSampler({magFilter:\"linear\",minFilter:\"linear\",mipmapFilter:\"linear\",addressModeU:\"repeat\",addressModeV:\"repeat\"})},get nearestRepeat(){return i??=e.createSampler({magFilter:\"nearest\",minFilter:\"nearest\",addressModeU:\"repeat\",addressModeV:\"repeat\"})}};function c(f,h,g,y,b){let x={texture:f,width:h,height:g,format:y,write(P){f.write(P)},unwrap(){return e.unwrap(f)},createSrgbView(){if(b)return e.unwrap(f).createView({format:SW(y)})},generateMipmaps(){f.generateMipmaps?.()},destroy(){t.has(x)&&(f.destroy(),t.delete(x))}};return t.add(x),x}function d(f){let{width:h,height:g,srgb:y=!1,mipLevelCount:b}=f,x=f.format??\"rgba8unorm\";return c(s({size:[h,g],format:x,...y?{viewFormats:[SW(x)]}:{},...b?{mipLevelCount:b}:{}},\"sampled\",\"render\"),h,g,x,y)}function u(f){let{width:h,height:g,format:y,data:b}=f,x=c(s({size:[h,g],format:y},\"sampled\"),h,g,y,!1);return b&&x.write(b),x}function p(f){let h=f.format??\"rgba16float\",{mipLevelCount:g}=f,y=f.width,b=f.height;function x(C,E){return c(s({size:[C,E],format:h,...g?{mipLevelCount:g}:{}},\"sampled\",\"render\"),C,E,h,!1)}let P=x(y,b);return{get texture(){return P.texture},get width(){return y},get height(){return b},format:h,write(C){P.write(C)},unwrap(){return P.unwrap()},createSrgbView(){return P.createSrgbView()},generateMipmaps(){P.generateMipmaps()},resize(C,E){return C===y&&E===b?!1:(P.destroy(),y=C,b=E,P=x(C,E),!0)},destroy(){P.destroy()}}}function _(f){let{layout:h,entryKey:g,getSource:y,staticEntries:b={}}=f,x=null;return{update(){let P=y();if(!P)return x=null,null;let C;try{C=e.device.importExternalTexture({source:P})}catch{return x=null,null}return x=e.createBindGroup(h,{...b,[g]:C}),x},get current(){return x},destroy(){x=null}}}return{samplers:l,createMediaTexture:d,createDataTexture:u,createRenderTexture:p,createExternalTextureBinding:_,get textureCount(){return t.size},destroy(){for(let f of[...t])f.destroy();t.clear()}}}function q1e(e,t){let s=b5(e)?.definition?.naturalSizeKey;if(!s)return null;if(\"fixed\"in s)return s.fixed||null;let r=t[s.fromProp];return typeof r==\"string\"&&r?r:null}function V1e(e,t,s){if(!e||typeof e!=\"object\")return null;let r=(i,l)=>!i||typeof i!=\"object\"||typeof i.value!=\"number\"?null:i.unit===\"px\"?i.value:i.value*l,o=r(e.width,t),a=r(e.height,s);return o!=null&&a!=null&&o>0&&a>0?{widthPx:o,heightPx:a}:null}function N1e(e,t,s){let{type:r,decl:o,props:a,boundingBox:i}=e;if(!(t>0)||!(s>0)||e.requiresChild)return null;if(o?.computeBounds){let u=o.computeBounds(a,t,s);return u.widthPx>0&&u.heightPx>0?{widthPx:u.widthPx,heightPx:u.heightPx}:null}let l=o?.propBindings;if(l?.width&&l?.height){let u=e_(l.width,a[l.width.prop],t,s),p=e_(l.height,a[l.height.prop],t,s);return u>0&&p>0?{widthPx:u,heightPx:p}:null}let c=V1e(i,t,s);if(c)return c;let d=q1e(r,a);if(d){let u=J4(d);if(u)return{widthPx:u.width,heightPx:u.height}}return null}function $1e(e,t){return e==null?0:typeof e==\"number\"?e:e.unit===\"px\"?e.value:e.value*t}function T5(e,t,s){return!e||e.mode!==\"column\"&&e.mode!==\"row\"?null:{mode:e.mode,gapPx:Math.max(0,$1e(e.gap,e.mode===\"column\"?s:t)),align:e.align??\"center\",anchor:e.anchor??\"center\",anchorOffsetX:e.anchorOffset?.x??0,anchorOffsetY:e.anchorOffset?.y??0}}var PW=e=>({ox:e.includes(\"left\")?\"start\":e.includes(\"right\")?\"end\":\"center\",oy:e.includes(\"top\")?\"start\":e.includes(\"bottom\")?\"end\":\"center\"}),_y=(e,t,s,r)=>e===\"start\"?r:e===\"end\"?s-t-r:(s-t)/2+r;function fy(e,t){let s=0,r=0;for(let o of t){let a=e.mode===\"column\"?o.size.heightPx:o.size.widthPx,i=e.mode===\"column\"?o.size.widthPx:o.size.heightPx;s+=a,r=Math.max(r,i)}return t.length>1&&(s+=e.gapPx*(t.length-1)),e.mode===\"column\"?{widthPx:r,heightPx:s}:{widthPx:s,heightPx:r}}function X1e(e,t,s,r){let o=new Map;if(t.length===0)return o;let a=fy(e,t),{ox:i,oy:l}=PW(e.anchor),c=_y(i,a.widthPx,s,e.anchorOffsetX),d=_y(l,a.heightPx,r,e.anchorOffsetY),u=(_,f)=>e.align===\"start\"?0:e.align===\"end\"?f-_:(f-_)/2,p=0;for(let _ of t)if(e.mode===\"column\"){let f=c+u(_.size.widthPx,a.widthPx)+_.size.widthPx/2,h=d+p+_.size.heightPx/2;o.set(_.id,{centerXPx:f,centerYPx:h}),p+=_.size.heightPx+e.gapPx}else{let f=c+p+_.size.widthPx/2,h=d+u(_.size.heightPx,a.heightPx)+_.size.heightPx/2;o.set(_.id,{centerXPx:f,centerYPx:h}),p+=_.size.widthPx+e.gapPx}return o}function H1e(e,t,s){let r=T5(e.layout,t,s);if(r){let o=S5(e,t,s);return o.length===0?null:fy(r,o)}return N1e({type:e.componentName,decl:e.decl,props:e.props,boundingBox:e.boundingBox,requiresChild:e.requiresChild,availableWidthPx:t},t,s)}function S5(e,t,s){let r=[];for(let o of e.children){if(o.visible===!1||o.absolute)continue;let a=H1e(o,t,s);a&&r.push({id:o.id,size:a,view:o})}return r}function j1e(e,t,s){if(!e.decl?.computeBounds)return{x:0,y:0};let r=e.decl.computeBounds(e.props,t,s),o=typeof e.props.center?.x==\"number\"?e.props.center.x:.5,a=typeof e.props.center?.y==\"number\"?e.props.center.y:.5;return{x:r.centerXPx-o*t,y:r.centerYPx-a*s}}function K1e(e,t,s,r,o,a,i,l){let c=T5(e.layout,o,a);if(c){CW(e,c,o,a,i,{centerXPx:s,centerYPx:r},l);return}let d=e.decl?.propBindings?.x?.prop??e.decl?.propBindings?.y?.prop;if(d){if(!e.positionDriven){let u=j1e(e,o,a);i.push({kind:\"position\",id:e.id,prop:d,value:{x:(s-u.x)/o,y:(r-u.y)/a}})}return}i.push({kind:\"box\",id:e.id,xPx:s-t.widthPx/2,yPx:r-t.heightPx/2,widthPx:t.widthPx,heightPx:t.heightPx})}function CW(e,t,s,r,o,a,i,l){let c=l??S5(e,s,r);if(c.length===0)return;let d=t;if(a){let p=fy(t,c);d={...t,anchor:\"top-left\",anchorOffsetX:a.centerXPx-p.widthPx/2,anchorOffsetY:a.centerYPx-p.heightPx/2}}if(i){let p=fy(d,c),{ox:_,oy:f}=PW(d.anchor);i.set(e.id,{leftPx:_y(_,p.widthPx,s,d.anchorOffsetX),topPx:_y(f,p.heightPx,r,d.anchorOffsetY),widthPx:p.widthPx,heightPx:p.heightPx})}let u=X1e(d,c,s,r);for(let p of c){let _=u.get(p.id);_&&K1e(p.view,p.size,_.centerXPx,_.centerYPx,s,r,o,i)}}function Q1e(e,t,s,r){let o=[],a=i=>{let l=T5(i.layout,t,s);if(l){let c=S5(i,t,s);CW(i,l,t,s,o,void 0,r,c);let d=new Set(c.map(u=>u.id));for(let u of i.children)u.visible!==!1&&(d.has(u.id)||a(u));return}for(let c of i.children)c.visible!==!1&&a(c)};for(let i of e)i.visible!==!1&&a(i);return o}var Z1e=class{frameTimesMs=[];frameTimesIndex=0;frameTimesCount=0;maxSamples=60;targetFrameTime=16.67;jankFrameCount=0;totalFrameCount=0;memorySnapshots=[];memorySnapshotIndex=0;maxMemorySnapshots=300;nodeCount=0;rttNodeCount=0;lastCpuTime=null;lastGpuTime=null;isRendering=!1;lastFrameTimestamp=0;frameIntervals=[];frameIntervalsIndex=0;frameIntervalsCount=0;pushToCircularBuffer(e,t,s,r,o){return e.length<o?(e.push(t),{index:e.length,count:e.length}):(e[s%o]=t,{index:s+1,count:o})}getBufferValues(e,t){return t<=e.length?e.slice(0,t):e}recordFrame(e){let t=this.pushToCircularBuffer(this.frameTimesMs,e,this.frameTimesIndex,this.frameTimesCount,this.maxSamples);this.frameTimesIndex=t.index,this.frameTimesCount=t.count,this.totalFrameCount++,e>this.targetFrameTime&&this.jankFrameCount++;let s=performance.now();if(this.lastFrameTimestamp>0){let r=s-this.lastFrameTimestamp,o=this.pushToCircularBuffer(this.frameIntervals,r,this.frameIntervalsIndex,this.frameIntervalsCount,this.maxSamples);this.frameIntervalsIndex=o.index,this.frameIntervalsCount=o.count}this.lastFrameTimestamp=s,this.recordMemorySnapshot()}recordCpuTime(e){this.lastCpuTime=e}recordGpuTime(e){this.lastGpuTime=e}updateNodeCounts(e,t){this.nodeCount=e,this.rttNodeCount=t}setRendering(e){this.isRendering=e}recordMemorySnapshot(){if(!performance.memory)return;let e=performance.now(),t=performance.memory.usedJSHeapSize;if(this.memorySnapshots.length<this.maxMemorySnapshots)this.memorySnapshots.push({time:e,bytes:t});else{let s=this.memorySnapshots[this.memorySnapshotIndex];s.time=e,s.bytes=t,this.memorySnapshotIndex=(this.memorySnapshotIndex+1)%this.maxMemorySnapshots}}calculateMemoryGrowthRate(){if(this.memorySnapshots.length<2)return null;let e=this.memorySnapshots.length,t=e<this.maxMemorySnapshots?0:this.memorySnapshotIndex,s=e<this.maxMemorySnapshots?e-1:(this.memorySnapshotIndex-1+e)%e,r=this.memorySnapshots[t],o=this.memorySnapshots[s],a=(o.time-r.time)/1e3;return a<=0?null:(o.bytes-r.bytes)/(1024*1024)/a}calculateStdDev(e){if(e.length===0)return 0;let t=e.reduce((r,o)=>r+o,0)/e.length,s=0;for(let r=0;r<e.length;r++){let o=e[r]-t;s+=o*o}return Math.sqrt(s/e.length)}sortBuffer=[];calculateP99(e){let t=e.length;if(t===0)return 0;for(;this.sortBuffer.length<t;)this.sortBuffer.push(0);for(let r=0;r<t;r++)this.sortBuffer[r]=e[r];for(let r=1;r<t;r++){let o=this.sortBuffer[r],a=r-1;for(;a>=0&&this.sortBuffer[a]>o;)this.sortBuffer[a+1]=this.sortBuffer[a],a--;this.sortBuffer[a+1]=o}let s=Math.floor(t*.99);return this.sortBuffer[Math.min(s,t-1)]}calculateComplexityScore(){return this.nodeCount+this.rttNodeCount*10}calculateIntensityScore(){let e=this.getBufferValues(this.frameTimesMs,this.frameTimesCount),t=e.length;if(t===0)return{score:0,label:\"N/A\"};let s=e.reduce((u,p)=>u+p,0)/t,r=Math.min(s/16.67*100,100),o=this.calculateComplexityScore(),a=Math.min(o/100*100,100),i=0;this.lastGpuTime!==null&&(i=Math.min(this.lastGpuTime/16.67*100,100));let l=this.lastGpuTime!==null?{frame:.4,gpu:.4,complexity:.2}:{frame:.7,gpu:0,complexity:.3},c=Math.round(r*l.frame+i*l.gpu+a*l.complexity),d;return c<=20?d=\"Very Light\":c<=40?d=\"Light\":c<=60?d=\"Medium\":c<=80?d=\"Heavy\":d=\"Very Heavy\",{score:Math.min(c,100),label:d}}getStats(e){let t=this.getBufferValues(this.frameTimesMs,this.frameTimesCount),s=this.getBufferValues(this.frameIntervals,this.frameIntervalsCount),r=t.length,o=s.length>0?1e3/(s.reduce((P,C)=>P+C,0)/s.length):0,a=r>0?t.reduce((P,C)=>P+C,0)/r:0,i=r>0?Math.min(...t):0,l=r>0?Math.max(...t):0,c=this.calculateP99(t),d=this.calculateStdDev(t),u=this.totalFrameCount>0?this.jankFrameCount/this.totalFrameCount*100:0,p=performance.memory,_=p?p.usedJSHeapSize/(1024*1024):null,f=this.calculateMemoryGrowthRate(),h=a>0?a/this.targetFrameTime*100:0,g=e?.render?.calls??null,y=e?.programs?.length??null,b=e?.memory?.textures??null,x=this.calculateIntensityScore();return{fps:Math.round(o),avgFrameTime:Math.round(a*100)/100,minFrameTime:Math.round(i*100)/100,maxFrameTime:Math.round(l*100)/100,p99FrameTime:Math.round(c*100)/100,stdDevFrameTime:Math.round(d*100)/100,jankCount:this.jankFrameCount,jankPercent:Math.round(u*10)/10,nodeCount:this.nodeCount,rttNodeCount:this.rttNodeCount,complexityScore:this.calculateComplexityScore(),memoryUsedMB:_!==null?Math.round(_*100)/100:null,memoryGrowthRate:f!==null?Math.round(f*1e3)/1e3:null,cpuTime:this.lastCpuTime!==null?Math.round(this.lastCpuTime*100)/100:null,gpuTime:this.lastGpuTime!==null?Math.round(this.lastGpuTime*100)/100:null,budgetUsed:Math.round(h*10)/10,drawCalls:g,shaderPrograms:y,textureCount:b,intensityScore:x.score,intensityLabel:x.label,isRendering:this.isRendering}}reset(){this.frameTimesMs.length=0,this.frameTimesIndex=0,this.frameTimesCount=0,this.frameIntervals.length=0,this.frameIntervalsIndex=0,this.frameIntervalsCount=0,this.lastFrameTimestamp=0,this.jankFrameCount=0,this.totalFrameCount=0,this.memorySnapshots.length=0,this.memorySnapshotIndex=0,this.sortBuffer.length=0,this.lastCpuTime=null,this.lastGpuTime=null}},J1e=32,eve=5,tve=(()=>{let e=0,t=new Set,s=(d,u)=>{t.forEach(p=>{try{p[d](u)}catch(_){Sr(\"[Shaders] Error in pointer handler:\",_)}})},r=d=>s(\"onMouseMove\",d),o=d=>s(\"onTouchMove\",d),a=d=>s(\"onMouseDown\",d),i=d=>s(\"onTouchStart\",d),l=()=>s(\"onMouseUp\"),c=()=>s(\"onTouchEnd\");return{register(d){return t.add(d),e++,e===1&&typeof window<\"u\"&&(window.addEventListener(\"mousemove\",r),window.addEventListener(\"touchmove\",o),window.addEventListener(\"mousedown\",a,!0),window.addEventListener(\"touchstart\",i,!0),window.addEventListener(\"mouseup\",l,!0),window.addEventListener(\"touchend\",c,!0)),()=>{t.delete(d),e--,e===0&&typeof window<\"u\"&&(window.removeEventListener(\"mousemove\",r),window.removeEventListener(\"touchmove\",o),window.removeEventListener(\"mousedown\",a,!0),window.removeEventListener(\"touchstart\",i,!0),window.removeEventListener(\"mouseup\",l,!0),window.removeEventListener(\"touchend\",c,!0))}}}})();function sve(e){if(!e)return!1;let t=e;if(typeof e==\"string\")try{t=JSON.parse(e)}catch{return!1}if(!t||typeof t!=\"object\")return!1;for(let s in t){let r=t[s];if(r&&typeof r==\"object\"&&r.type===\"mouse\")return!0}return!1}function MW(){let e=null,t=null,s=null,r=null,o=null,a=null,i=!1,l=!1,c=!1,d=null,u=new Map,p=null,_=new Map,f=[],h=[],g=F1e({maxSize:4,dispose:$=>hf($)}),y=new Set,b=null,x=null,P=!0,C=0,E=0,R=0,U=0,L=1,k=8192,D=0,O=0,F=!1,q=()=>R||C||1,Y=()=>U||E||1,V=!0,X=!1,Q=!1,te=0,J=0,oe=0,le=null,se=null,Te=!1,de=-10,xe=-10,Ae=!1,De=!1,Ee=null,ze=!1,Ie=null,ke=null,Me=null,we=null,Ve=null,qe=null,st=!1,pt=\"p3-linear\",Ze=\"linear\",nt=!1,ht=null,yt=null,St=!1,Mt=!1,Rt=null,Kt=!1,Wt=null,ls=null,us=!1,os=null,bs=null,hs=0,gs=0,Gs=new Z1e,Ss={deltaTime:0,pointer:{x:-10,y:-10,seen:!1},pointerActive:!1,dimensions:{width:0,height:0}},or=LB($=>wf($)),yr=$=>$.replace(/[^a-zA-Z0-9_]/g,\"_\"),lr=$=>{let Z=_.get($);if(!Z)return[];let ee=[];for(let ge of Z){let ue=u.get(ge);ue&&ee.push(ue)}return ee},Ur=$=>{let Z=yr($);for(let[ee,ge]of u)if(ge.metadata.id===Z)return ee;return null},so=($,Z)=>{if(Z===\"center\")return;let ee=$.boundingBoxDeclaration;if(!ee)return;let ge={};for(let[ue,Pe]of Object.entries($.uniforms))ge[ue]=Pe._rawDimensional!==void 0?Pe._rawDimensional:Pe.value;return _e($,ge.shapeSdfUrl),NA(ee,ge,q(),Y())},ro=new WeakMap,_e=($,Z)=>{if(typeof Z!=\"string\"||!Z||kB(Z))return;let ee=ro.get($)??new Set;ro.set($,ee),!ee.has(Z)&&(ee.add(Z),Kh(Z,()=>{ee.delete(Z),u.get($.id)===$&&(We($),ar()&&Ds(),Da())}))},We=$=>{let Z=$.boundingBoxDeclaration,ee=$.extraDimProps,ge=dp(Z,ee);if(!ge)return;let ue=$.uniforms.origin?.value??\"center\",Pe=so($,ue),Ke=[...ge.positionProps,...ge.sizeProps.keys()];for(let ut of Ke){let mt=$.uniforms[ut];if(!mt||mt._rawDimensional===void 0)continue;let Gt=R_(Z,ut,mt._rawDimensional,ue,q(),Y(),ee,Pe);mt.value=Gt,qi($,ut,Gt)}},Le=($,Z,ee)=>{if(typeof ee==\"number\")return ee;let ge=$.uniforms.origin?.value??\"center\",ue=R_($.boundingBoxDeclaration,Z,ee,ge,q(),Y(),$.extraDimProps);return typeof ue==\"number\"?ue:ee?.value??0},Be=()=>{let{width:$,height:Z}=Vi();return $/Math.max(1,Z)},Oe=$=>{let Z=$.metadata.transform;return{offsetX:Z?.offsetX??0,offsetY:Z?.offsetY??0,rotation:Z?.rotation??0,scale:Z?.scale??1,anchorX:Z?.anchorX??.5,anchorY:Z?.anchorY??.5,aspectRatio:Be()}},tt=$=>{let Z=$.metadata.boundingBox;if(!Z)return null;let ee=q3(Z,q(),Y());return{centerX:ee.centerX,centerY:ee.centerY,halfWidth:ee.halfWidthUV,halfHeight:ee.halfHeightUV,cornerRadius:ee.cornerRadiusUV,rotation:ee.rotation,aspectRatio:Be()}},_t=$=>!!$.metadata.boundingBox&&!!$.boundingBoxDeclaration&&!Yh($.metadata.boundingBox),Bt=$=>{let Z=$.boundingBoxDeclaration;if(!Z||!$.uniforms||!Z.computeBounds&&!Z.propBindings)return null;let ee={};for(let[mt,Gt]of Object.entries($.uniforms))ee[mt]=Gt._rawDimensional!==void 0?Gt._rawDimensional:Gt.value;let ge=NA(Z,ee,q(),Y());if(ge.hwx<=0&&ge.hhy<=0)return null;let ue=.5,Pe=.5,Ke=Z.propBindings?.x?.prop??Z.propBindings?.y?.prop,ut=Ke?$.uniforms[Ke]?.value:void 0;if(ut&&typeof ut.x==\"number\"&&typeof ut.y==\"number\"&&(ue=ut.x,Pe=ut.y),Ke){let mt=$.metadata.maps?.[Ke];if(mt?.type===\"mouse-position\"){let Gt=$.mouseDriverState[Ke];Gt&&(ue=Gt.liveX??(typeof mt.x==\"number\"?mt.x:Gt.currentX),Pe=Gt.liveY??(typeof mt.y==\"number\"?mt.y:Gt.currentY))}}return{centerX:ue,centerY:Pe,halfWidth:ge.hwx,halfHeight:ge.hhy}},Dt=$=>{if(!$.definition.wantsBoundsParams)return null;let Z=lr($.id).sort((ee,ge)=>ee.metadata.renderOrder-ge.metadata.renderOrder);for(let ee of Z){if(ee.metadata.visible===!1)continue;if(_t(ee))return null;let ge=Bt(ee);if(ge)return ge}return null},Nt=()=>{for(let $ of u.values()){if(!$.definition.wantsBoundsParams)continue;let Z=Dt($);Z&&(Qs($,\"_childBounds_centerX\",Z.centerX),Qs($,\"_childBounds_centerY\",Z.centerY),Qs($,\"_childBounds_halfWidth\",Z.halfWidth),Qs($,\"_childBounds_halfHeight\",Z.halfHeight))}},Ks=!1,Ds=()=>{Ks=!0,Da()},ar=()=>{for(let $ of u.values()){let Z=$.metadata.flow?.mode;if(Z===\"column\"||Z===\"row\")return!0}return!1},Xo=$=>{let Z={};for(let[Pe,Ke]of Object.entries($.uniforms))Z[Pe]=Ke._rawDimensional!==void 0?Ke._rawDimensional:Ke.value;let ee=$.boundingBoxDeclaration?.propBindings?.x?.prop??$.boundingBoxDeclaration?.propBindings?.y?.prop,ge=ee?$.uniforms[ee]?.value:void 0,ue=!!(ee&&(ge?.type===\"mouse-position\"||$.metadata.maps?.[ee]));return{id:$.id,componentName:$.componentName,visible:$.metadata.visible!==!1,absolute:$.metadata.absolute===!0,requiresChild:$.requiresChild,layout:$.metadata.flow,decl:$.boundingBoxDeclaration,props:Z,boundingBox:$.metadata.boundingBox,positionDriven:ue,children:lr($.id).sort((Pe,Ke)=>Pe.metadata.renderOrder-Ke.metadata.renderOrder).map(Xo)}},$r=()=>{if(!ar()){Ks=!1;return}let $=q(),Z=Y();if($<=1||Z<=1)return;Ks=!1;let ee=[];for(let ue of u.values())ue.parentId===null&&ee.push(Xo(ue));let ge=Q1e(ee,$,Z);for(let ue of ge){let Pe=u.get(ue.id);if(Pe)if(ue.kind===\"position\"){let Ke=Pe.uniforms[ue.prop];if(!Ke)continue;let ut=Ke.value,mt=ut&&typeof ut==\"object\"?{...ut,x:ue.value.x,y:ue.value.y}:ue.value;Ke.value=mt,qi(Pe,ue.prop,mt)}else{let Ke=_t(Pe);if(Pe.metadata.boundingBox={x:{value:ue.xPx,unit:\"px\"},y:{value:ue.yPx,unit:\"px\"},width:{value:ue.widthPx,unit:\"px\"},height:{value:ue.heightPx,unit:\"px\"},origin:\"top-left\",rotation:Pe.metadata.boundingBox?.rotation??0,...Pe.metadata.boundingBox?.cornerRadius?{cornerRadius:Pe.metadata.boundingBox.cornerRadius}:{},...Pe.metadata.boundingBox?.lockAspect!==void 0?{lockAspect:Pe.metadata.boundingBox.lockAspect}:{}},!Ke&&_t(Pe))qn();else{let ut=tt(Pe);if(ut)for(let[mt,Gt]of Object.entries(ut))Qs(Pe,`_bbox_${mt}`,Gt)}}}},cr=()=>{Ks&&$r()},za=null,ia=()=>{ar()&&Ds()},Ra=()=>{za||(za=eB(()=>{ar()&&Ds()})),typeof document<\"u\"&&document.fonts?.addEventListener&&document.fonts.addEventListener(\"loadingdone\",ia)};Ra();let Wn=$=>el($.metadata.transform),Uc=($,Z,ee)=>{let ge=$.mapValues[Z]??{inputMin:0,inputMax:1,outputMin:0,outputMax:1,curve:0};if(ee.type===\"map\"){let ue=ee;ge.inputMin=ue.inputMin,ge.inputMax=ue.inputMax,ge.outputMin=Le($,Z,ue.outputMin),ge.outputMax=Le($,Z,ue.outputMax),ge.curve=ue.curve??0}else if(ee.type===\"mouse\"){let ue=ee;ge.outputMin=Le($,Z,ue.outputMin),ge.outputMax=Le($,Z,ue.outputMax),ge.curve=ue.curve??0}else if(ee.type===\"auto-animate\"){let ue=ee;ge.outputMin=Le($,Z,ue.outputMin),ge.outputMax=Le($,Z,ue.outputMax)}return $.mapValues[Z]=ge,ge},Gp=($,Z,ee)=>{let ge=$.mouseDriverState[Z];if(ge)return ge;if(ee.type===\"mouse-position\"){let ue=ee.x===void 0||ee.x===\"mouse\",Pe=ee.y===void 0||ee.y===\"mouse\";ge={currentX:ue?de:ee.x,currentY:Pe?xe:ee.y,velocityX:0,velocityY:0,pointerSnapped:De}}else ge={currentX:Math.max(.001,ee.axis===\"x\"?de:xe),currentY:0,velocityX:0,velocityY:0,pointerSnapped:De};return $.mouseDriverState[Z]=ge,ge},Fp=$=>{let Z=$.metadata.maps;if(Z)for(let[ee,ge]of Object.entries(Z))ee in $.uniforms&&(ge.type===\"map\"?Uc($,ee,ge):ge.type===\"mouse-position\"?Gp($,ee,ge):ge.type===\"mouse\"?(Gp($,ee,ge),Uc($,ee,ge)):ge.type===\"auto-animate\"&&($.autoAnimateState[ee]||($.autoAnimateState[ee]={}),Uc($,ee,ge)))},Oc=$=>{let Z=$.definition.animatedTime;return(Z?$.uniforms[Z.speed]:void 0)??$},$d=($,Z)=>$.uniforms[Z]??$,af=$=>{let Z=[];for(let[ue,Pe]of Object.entries($.uniforms))Z.push({name:ue,initial:Pe.value,transform:Pe.transform,cpu:Pe.cpu,schema:Pe.schema});if(Z.push({name:\"_opacity\",schema:n,initial:$.metadata.opacity??1}),$.definition.animatedTime&&Z.push({name:\"_animTime\",schema:n,initial:zl(Oc($)).value}),$.definition.extraAnimatedTimes)for(let[ue,Pe]of Object.entries($.definition.extraAnimatedTimes))Z.push({name:`_animTime_${ue}`,schema:n,initial:zl($d($,Pe)).value});let ee=$.definition.extraFields;if(ee)for(let[ue,Pe]of Object.entries(ee))Z.push({name:ue,schema:Pe.schema,initial:Pe.initial});if(Wn($)){let ue=Oe($);for(let[Pe,Ke]of Object.entries(ue))Z.push({name:`_xform_${Pe}`,schema:n,initial:Ke})}if(_t($)){let ue=tt($);if(ue)for(let[Pe,Ke]of Object.entries(ue))Z.push({name:`_bbox_${Pe}`,schema:n,initial:Ke})}if($.definition.wantsBoundsParams){let ue=Dt($)??{centerX:.5,centerY:.5,halfWidth:.5,halfHeight:.5};for(let[Pe,Ke]of Object.entries(ue))Z.push({name:`_childBounds_${Pe}`,schema:n,initial:Ke})}let ge=$.metadata.maps;if(ge){Fp($);for(let[ue,Pe]of Object.entries(ge))if(ue in $.uniforms){if(Pe.type===\"map\"){let Ke=$.mapValues[ue];Z.push({name:`_map_${ue}_inputMin`,schema:n,initial:Ke.inputMin}),Z.push({name:`_map_${ue}_inputMax`,schema:n,initial:Ke.inputMax}),Z.push({name:`_map_${ue}_outputMin`,schema:n,initial:Ke.outputMin}),Z.push({name:`_map_${ue}_outputMax`,schema:n,initial:Ke.outputMax}),Z.push({name:`_map_${ue}_curve`,schema:n,initial:Ke.curve})}else if(Pe.type===\"mouse-position\"){let Ke=$.mouseDriverState[ue];Z.push({name:`_smoothed_${ue}`,schema:m,initial:[Ke.currentX,1-Ke.currentY]})}else if(Pe.type===\"mouse\"){let Ke=$.mapValues[ue];Z.push({name:`_smoothed_${ue}`,schema:n,initial:Ke.outputMin})}else if(Pe.type===\"auto-animate\"){let Ke=$.mapValues[ue];Z.push({name:`_smoothed_${ue}`,schema:n,initial:Ke.outputMin})}}}return Z},qi=($,Z,ee)=>{let ge=$._liveHandles?.[Z];ge&&(ht?.(pt),Bk(ge,ee))},Qs=($,Z,ee)=>{let ge=$._liveHandles?.[Z];ge instanceof si&&(ge.value=ee)},Ip=($,Z,ee)=>{let ge=$._liveHandles?.[Z];ge instanceof si?ge.value=ee:ge instanceof Nm&&Array.isArray(ee)&&(ge.array=ee)},nf=$=>{for(let Z of Object.values($.uniforms))if(Z.transform===at){let ee=Z.value;if(typeof ee==\"string\")return at(ee);if(typeof ee==\"number\")return ee}return 0},lf=$=>{if(!$.uniforms.convertedColorsArray)return;let Z=null;for(let Pe of Object.values($.uniforms))if(Pe.transform===hl){Z=Pe.value??null;break}let ee=J3(Z,B_),ge=eA(ee.colors,ee.stopCount,nf($)),ue=(Pe,Ke)=>{let ut=$.uniforms[Pe];ut&&(ut.value=Ke),qi($,Pe,Ke)};ue(\"colorsArray\",ee.colors),ue(\"positionsArray\",ee.positions),ue(\"convertedColorsArray\",ge),ue(\"stopCount\",ee.stopCount)},cf=($,Z)=>{let ee=$.uniforms[Z],ge=ee?._listSpec;if(!ee||!ge)return;let ue=ee._rawList,Pe=new Set,Ke=IA(ue,ge,{color:B_,drivenPosition:(Gt,Es,Tr)=>{let dr=vz(Z,Gt,Es);Pe.add(dr);let ss=Gp($,dr,Tr);return ss.liveX!==void 0&&ss.liveY!==void 0?{x:ss.liveX,y:1-ss.liveY}:void 0}});for(let Gt of Object.keys($.mouseDriverState)){let Es=FA(Gt);Es&&Es.prop===Z&&!Pe.has(Gt)&&delete $.mouseDriverState[Gt]}ee.value=Ke,qi($,Z,Ke);let ut=YA(Ke,ge),mt=(Gt,Es)=>{let Tr=$.uniforms[Gt];Tr&&(Tr.value=Es),qi($,Gt,Es)};for(let[Gt,Es]of Object.entries(ut.fields))mt(tg(Z,Gt),Es);mt(sg(Z),ut.count)},vy=($,Z)=>{let ee=$.metadata.maps?.[Z];if(ee)return ee;let ge=FA(Z);if(!ge)return;let ue=$.uniforms[ge.prop]?._rawList,Pe=(Array.isArray(ue)?ue[ge.index]:void 0)?.[ge.field];return rg(Pe)?Pe:void 0},df=$=>Object.values($.uniforms).some(Z=>Z._listSpec&&WA(Z._rawList,Z._listSpec)),hy=$=>{if(Qs($,\"_opacity\",$.metadata.opacity??1),$.definition.animatedTime&&Qs($,\"_animTime\",zl(Oc($)).value),$.definition.extraAnimatedTimes)for(let[ee,ge]of Object.entries($.definition.extraAnimatedTimes))Qs($,`_animTime_${ee}`,zl($d($,ge)).value);if(Wn($))for(let[ee,ge]of Object.entries(Oe($)))Qs($,`_xform_${ee}`,ge);if(_t($)){let ee=tt($);if(ee)for(let[ge,ue]of Object.entries(ee))Qs($,`_bbox_${ge}`,ue)}let Z=$.metadata.maps;if(Z){for(let[ee,ge]of Object.entries(Z))if(ge.type===\"map\"&&$.mapValues[ee]){let ue=$.mapValues[ee];Qs($,`_map_${ee}_inputMin`,ue.inputMin),Qs($,`_map_${ee}_inputMax`,ue.inputMax),Qs($,`_map_${ee}_outputMin`,ue.outputMin),Qs($,`_map_${ee}_outputMax`,ue.outputMax),Qs($,`_map_${ee}_curve`,ue.curve)}}},gy=($,Z)=>{let ee={};for(let ge of Object.keys($.uniforms)){let ue=Z[ge];ue&&(ee[ge]=ue)}return ee},yy=$=>{let Z={};for(let[ee,ge]of Object.entries($.uniforms))Z[ee]={accessorPath:\"\",cpu:!!ge.cpu,value:ge.value};return Z},by=($,Z)=>({id:$.id,customId:$.metadata.id,componentName:$.componentName,parentId:$.parentId,definition:$.definition,metadata:$.metadata,handles:Z,domCanvas:$.domCanvas}),uf=($,Z)=>{let ee=new Map,ge=Pe=>by(Pe,$&&Z?gy(Pe,Z.get(Pe.id)??{}):yy(Pe)),ue=Pe=>{let Ke=ee.get(Pe);if(Ke)return Ke;let ut=u.get(Pe);if(!ut)return;let mt=ge(ut);return ee.set(Pe,mt),mt};return{rootId:p,getNode:ue,getChildren:Pe=>lr(Pe).map(Ke=>ue(Ke.id)),resolveCustomId:Pe=>Ur(Pe),store:{gpuAccessor:(Pe,Ke)=>$?$.gpuAccessor(Pe,Ke):\"\",layout:$?.layout}}},pf=()=>({toneMapping:Ze,flipY:!1,premultiplyAlpha:!0,canvas:a??void 0,dimensions:Vi(),gpu:t?{device:t.device,root:t}:void 0,writeExtraField:($,Z,ee)=>{let ge=u.get($);ge&&Ip(ge,Z,ee)},createMediaTexture:$=>{if(!r)throw new Error(\"[gpu] createMediaTexture before init\");return r.createMediaTexture($)},createDataTexture:$=>{if(!r)throw new Error(\"[gpu] createDataTexture before init\");return r.createDataTexture($)},resolveMapWindow:($,Z)=>{let ee=u.get($),ge=ee?.metadata.maps?.[Z];if(!ee||!ge||ge.type!==\"map\")return;let ue=Uc(ee,Z,ge);return{inputMin:ue.inputMin,inputMax:ue.inputMax,outputMin:ue.outputMin,outputMax:ue.outputMax,curve:ue.curve}},resolveDriverCpuValue:($,Z)=>{let ee=u.get($);if(!ee)return;if(Z===\"_animTime\"||Z.startsWith(\"_animTime_\")){let ue=ee._liveHandles?.[Z];return ue instanceof si?ue.value:void 0}let ge=ee.metadata.maps?.[Z];if(ge){if(ge.type===\"mouse-position\"){let ue=ee.mouseDriverState[Z];return!ue||ue.liveX===void 0||ue.liveY===void 0?void 0:{x:ue.liveX,y:1-ue.liveY}}if(ge.type===\"mouse\"||ge.type===\"auto-animate\"){let ue=ee._liveHandles?.[`_smoothed_${Z}`];return ue instanceof si?ue.value:void 0}}}}),xy=()=>{let $=[];for(let[Z,ee]of u){let ge=ee.metadata;$.push(`aopac:${Z}:${(ge.opacity??1)<1}`);let ue=ee.uniforms.stopCount?.value;typeof ue==\"number\"&&$.push(`stops:${Z}:${ue>1?ue:0}`);for(let[Pe,Ke]of Object.entries(ee.uniforms)){if(!Ke.compileTimeWhen)continue;let ut=Ke._lastCompiledValue;typeof ut!=\"number\"&&typeof ut!=\"boolean\"||$.push(`ctw:${Z}:${Pe}:${String(ut)}`)}if(ge.maps){let Pe=Object.entries(ge.maps).sort(([Ke],[ut])=>Ke<ut?-1:Ke>ut?1:0).map(([Ke,ut])=>{let mt=ut;return`${Ke}=${mt.type}:${mt.source??\"\"}:${mt.channel??\"\"}:${mt.axis??\"\"}`}).join(\",\");$.push(`mapfp:${Z}:${Pe}`)}}return $},mf=()=>p?G1e([...A4(uf(null,null),pf()),...xy()]):null,Vi=()=>({width:Math.max(1,Math.round(C*L)),height:Math.max(1,Math.round(E*L))}),_f=$=>{let Z={};for(let ee of $.textures)ee.kind!==\"rtt\"&&ee.resource&&(Z[ee.key]=ee.resource);return Z},ff=$=>{if(!t||!r||!o)throw new Error(\"[gpu] buildComposition before init\");let Z=kk(t,{systemSchema:O1e}),ee=new Map;for(let[ut,mt]of u)ee.set(ut,Z.defineNode(ut,af(mt)));Z.defineSystem(),Z.finalize();let ge=x4(uf(Z,ee),pf()),ue=I1e(t,{textureManager:r,dispatcher:o}),Pe={hash:$,store:Z,ir:ge,passManager:ue,handlesById:ee,fresh:!0,validationFrames:3},Ke=ge.usesCustomWgsl?t.device:void 0;Ke&&Ke.pushErrorScope(\"validation\");try{ue.setComposition(ge,Z.bindGroup,Vi(),_f(ge))}finally{Ke&&Ke.popErrorScope().then(ut=>{ut&&!Pe.broken&&vf(Pe,ut.message)}).catch(()=>{})}return y.add(Pe),Pe},vf=($,Z)=>{$.broken=Z,Mf(\"[gpu] a shader in this composition failed GPU validation \\u2014 it will not draw until it changes. If you are writing a wgsl`\\u2026` body, the message below points at the line:\\n\"+Z)},hf=$=>{try{for(let Z of $.ir.onCleanup)Z()}catch(Z){Sr(\"[gpu] composition onCleanup error:\",Z)}try{$.passManager.dispose()}catch(Z){Sr(\"[gpu] passManager dispose error:\",Z)}try{$.store.destroy()}catch(Z){Sr(\"[gpu] store destroy error:\",Z)}y.delete($),b===$&&(b=null)},Ay=$=>{let Z=!1;for(let[ee,ge]of $.handlesById){let ue=u.get(ee);ue&&ue._liveHandles!==ge&&(ue._liveHandles=ge,Z=!0)}if(!($===b&&!Z)){if(!$.fresh){for(let ee of u.values()){for(let[ge,ue]of Object.entries(ee.uniforms))qi(ee,ge,ue.value);Fp(ee),hy(ee)}$.store.writeAll()}$.fresh=!1,b=$,!St&&$.ir.composedNodeIds.size>0&&(Mt=!0)}},qn=()=>{P=!0,ar()&&(Ks=!0),Da()},Da=()=>{Wt||Te||or.running||se===null&&typeof requestAnimationFrame==\"function\"&&(se=requestAnimationFrame($=>{se=null,wf($)}))},Xd=($,Z,ee,ge,ue={},Pe,Ke)=>{if(Wt)return;if(Z===null){let ss=f.findIndex(Oo=>Oo.id===$);if(ss!==-1){f.splice(ss,1),h=h.filter(Oo=>Oo.id!==$);return}Hd($);return}if(!Pe){console.error(`[gpu] registerNode(\"${$}\") requires a componentDefinition`);return}if(!c){let ss=f.findIndex(Vn=>Vn.id===$),Oo={id:$,definition:Pe,parentId:ee,metadata:ge,uniforms:ue,domCanvas:Ke};ss!==-1?(f[ss]=Oo,h=h.filter(Vn=>Vn.id!==$)):f.push(Oo);return}let ut=0;if(ee!==null){let ss=_.get(ee);if(ss&&ss.size>0){let Oo=0;for(let Vn of ss){let La=u.get(Vn);La&&La.metadata.renderOrder>Oo&&(Oo=La.metadata.renderOrder)}ut=Oo+1}}let mt={blendMode:ge?.blendMode||\"normal\",opacity:ge?.opacity,visible:ge?.visible!==!1,id:ge?.id?yr(ge.id):ge?.id,mask:ge?.mask,maps:ge?.maps,renderOrder:ge?.renderOrder!==void 0?ge.renderOrder:ut,transform:ge?.transform,boundingBox:ge?.boundingBox,flow:ge?.flow,absolute:ge?.absolute},Gt=Pe.boundingBoxDeclaration,Es=Pe.props?VA(Pe.props):void 0,Tr={id:$,componentName:Pe.name||\"Unknown\",parentId:ee,definition:Pe,metadata:mt,uniforms:ue,domCanvas:Ke,requiresRTT:!!Pe.requiresRTT,requiresChild:!!Pe.requiresChild,blendWithChildren:!!Pe.blendWithChildren,acceptsUVContext:!!Pe.acceptsUVContext,providesUVContextViaCompute:!!Pe.providesUVContextViaCompute,usesPointer:!!Pe.usesPointer,hasUvRemap:!!Pe.uvRemap,boundingBoxDeclaration:Gt,wantsBoundsParams:!!Pe.wantsBoundsParams,extraDimProps:Es,mouseDriverState:{},autoAnimateState:{},mapValues:{},_liveHandles:null};if(Gt||Es){let ss=dp(Gt,Es);if(ss){let Oo=ue.origin?.value??\"center\",Vn=so(Tr,Oo);for(let La of[...ss.positionProps,...ss.sizeProps.keys()]){let _a=ue[La];_a&&(_a._rawDimensional===void 0&&(_a._rawDimensional=_a.value),_a.value=R_(Gt,La,_a._rawDimensional,Oo,q(),Y(),Es,Vn))}}}for(let ss of Object.values(ue))ss.compileTimeWhen&&ss._lastCompiledValue===void 0&&(ss._lastCompiledValue=ss.value);u.set($,Tr),ee===null&&(p=$);let dr=_.get(ee)||new Set;dr.add($),_.set(ee,dr),Np(),qn(),ar()&&Ds()},gf=$=>{f.some(Z=>Z.id===$.id)&&h.push($)},yf=($,Z,ee)=>{let ge=u.get($);if(!ge){gf({id:$,kind:\"uniform\",name:Z,value:ee});return}let ue=ge.uniforms[Z];if(!ue)return;let Pe=ge.boundingBoxDeclaration,Ke=ge.extraDimProps;if((Pe||Ke)&&$A(Pe,Z,Ke)){ue._rawDimensional=ee;let ut=ge.uniforms.origin?.value??\"center\",mt=so(ge,ut);ee=R_(Pe,Z,ee,ut,q(),Y(),Ke,mt)}if(ue.transform===_c){ue._rawList=ee,cf(ge,Z),Np(),Da();return}if(ue.value=ee,qi(ge,Z,ee),(ue.transform===hl||ue.transform===at)&&lf(ge),Z===\"origin\"&&Pe)We(ge);else if(Pe&&(ge.uniforms.origin?.value??\"center\")!==\"center\"){let ut=dp(Pe,Ke),mt=!!Pe.computeBounds||(ut?.sizeProps.has(Z)??!1),Gt=ut?.positionProps.has(Z)??!1;mt&&!Gt&&We(ge)}if(Z===\"shape\"&&Np(),ar()&&Ds(),ue.compileTime){qn();return}if(ue.compileTimeWhen){let ut=ue._lastCompiledValue;if(ue.compileTimeWhen(ut,ee)){ue._lastCompiledValue=ee,qn();return}}Da()},bf=($,Z)=>{let ee=u.get($);if(!ee){gf({id:$,kind:\"metadata\",metadata:Z});return}let ge=!1;if(Z.blendMode!==void 0&&ee.metadata.blendMode!==Z.blendMode&&(ee.metadata.blendMode=Z.blendMode,ge=!0),Z.opacity!==void 0&&ee.metadata.opacity!==Z.opacity){let ue=ee.metadata.opacity??1,Pe=Z.opacity;ee.metadata.opacity=Pe,Qs(ee,\"_opacity\",Pe),ue>=1!=Pe>=1&&(ge=!0)}if(Z.renderOrder!==void 0&&ee.metadata.renderOrder!==Z.renderOrder&&(ee.metadata.renderOrder=Z.renderOrder,ge=!0),\"visible\"in Z){let ue=Z.visible!==!1;ee.metadata.visible!==ue&&(ee.metadata.visible=ue,ge=!0)}if(Z.id!==void 0){let ue=yr(Z.id);ee.metadata.id!==ue&&(ee.metadata.id=ue,ge=!0)}if(\"mask\"in Z&&(ee.metadata.mask?.source!==Z.mask?.source||ee.metadata.mask?.type!==Z.mask?.type)&&(ee.metadata.mask=Z.mask,ge=!0),\"maps\"in Z&&JSON.stringify(ee.metadata.maps)!==JSON.stringify(Z.maps)){let ue=ee.metadata.maps,Pe=Z.maps;if(ee.metadata.maps=Pe,Np(),(()=>{if(!ue||!Pe)return!0;let Ke=Object.keys(ue),ut=Object.keys(Pe);if(Ke.length!==ut.length||!Ke.every(mt=>mt in Pe))return!0;for(let mt of ut){let Gt=ue[mt],Es=Pe[mt];if(Gt.type!==Es.type)return!0;if(Es.type===\"map\"){let Tr=Gt,dr=Es;if(Tr.source!==dr.source||Tr.channel!==dr.channel)return!0}else if(Es.type===\"mouse\"&&Gt.axis!==Es.axis)return!0}return!1})()){if(ue&&Pe)for(let Ke of Object.keys(ue))Ke in Pe&&ue[Ke].type!==Pe[Ke].type&&(delete ee.mouseDriverState[Ke],delete ee.autoAnimateState[Ke],delete ee.mapValues[Ke]);ge=!0}else if(Pe)for(let[Ke,ut]of Object.entries(Pe)){let mt=Uc(ee,Ke,ut);ut.type===\"map\"&&(Qs(ee,`_map_${Ke}_inputMin`,mt.inputMin),Qs(ee,`_map_${Ke}_inputMax`,mt.inputMax),Qs(ee,`_map_${Ke}_outputMin`,mt.outputMin),Qs(ee,`_map_${Ke}_outputMax`,mt.outputMax),Qs(ee,`_map_${Ke}_curve`,mt.curve))}}if(Z.transform!==void 0){let ue=ee.metadata.transform,Pe={offsetX:Z.transform.offsetX??ue?.offsetX??0,offsetY:Z.transform.offsetY??ue?.offsetY??0,rotation:Z.transform.rotation??ue?.rotation??0,scale:Z.transform.scale??ue?.scale??1,anchorX:Z.transform.anchorX??ue?.anchorX??.5,anchorY:Z.transform.anchorY??ue?.anchorY??.5,edges:Z.transform.edges??ue?.edges??\"transparent\"},Ke=Wn(ee);ee.metadata.transform=Pe;let ut=el(Pe);if(Ke!==ut||ue&&ue.edges!==Pe.edges)ge=!0;else if(ut)for(let[mt,Gt]of Object.entries(Oe(ee)))Qs(ee,`_xform_${mt}`,Gt)}if(Z.boundingBox!==void 0&&Z.boundingBox!==null&&ee.boundingBoxDeclaration!==void 0){let ue=_t(ee);ee.metadata.boundingBox=Z.boundingBox;let Pe=_t(ee);if(ue!==Pe)ge=!0;else if(Pe){let Ke=tt(ee);if(Ke)for(let[ut,mt]of Object.entries(Ke))Qs(ee,`_bbox_${ut}`,mt)}}else Z.boundingBox===null&&ee.metadata.boundingBox!==void 0&&(ee.metadata.boundingBox=void 0,ge=!0);if(\"flow\"in Z&&JSON.stringify(ee.metadata.flow)!==JSON.stringify(Z.flow)){let ue=ee.metadata.flow?.mode===\"column\"||ee.metadata.flow?.mode===\"row\";ee.metadata.flow=Z.flow??void 0;let Pe=ee.metadata.flow?.mode===\"column\"||ee.metadata.flow?.mode===\"row\";if(ue&&!Pe){let Ke=ut=>{for(let mt of lr(ut))We(mt),Ke(mt.id)};Ke($)}Ds()}\"absolute\"in Z&&ee.metadata.absolute!==Z.absolute&&(ee.metadata.absolute=Z.absolute,Z.absolute&&We(ee),Ds()),ar()&&Ds(),ge?qn():Da()},Hd=$=>{let Z=u.get($);if(!Z)return;for(let ge of lr($))Hd(ge.id);p===$&&(p=null);let ee=_.get(Z.parentId);ee&&(ee.delete($),ee.size===0&&_.delete(Z.parentId)),u.delete($),Np(),qn(),ar()&&Ds()},Yp=()=>{let $=Ss.deltaTime;oe=Te?oe+$:zB(le,oe,$,performance.now());for(let Z of u.values()){let ee=Z.metadata.maps??{};for(let ge in Z.mouseDriverState){let ue=Z.mouseDriverState[ge],Pe=vy(Z,ge);if(Pe){if(Pe.type===\"mouse-position\"){let Ke=Pe.smoothing??0,ut=Pe.momentum??0,mt=Pe.x===void 0||Pe.x===\"mouse\",Gt=Pe.y===void 0||Pe.y===\"mouse\";!ue.pointerSnapped&&De&&(ue.pointerSnapped=!0,mt&&(ue.currentX=de,ue.velocityX=0),Gt&&(ue.currentY=xe,ue.velocityY=0));let Es,Tr;if(mt){let[Nn,jd]=C_(ue.currentX,ue.velocityX,de,Ke,ut,$);ue.currentX=Nn,ue.velocityX=jd,Es=Nn}else Es=Pe.x,ue.velocityX=0;if(Gt){let[Nn,jd]=C_(ue.currentY,ue.velocityY,xe,Ke,ut,$);ue.currentY=Nn,ue.velocityY=jd,Tr=Nn}else Tr=Pe.y,ue.velocityY=0;let dr=Pe.reach??1,ss=Pe.originX??.5,Oo=Pe.originY??.5,Vn=Pe.invertX?-1:1,La=Pe.invertY?-1:1,_a=ss+Vn*(Es-ss)*dr,yn=Oo+La*(Tr-Oo)*dr,Ic=_a,$p=yn,Sf=Z.metadata.boundingBox;if(Sf&&Z.boundingBoxDeclaration?.supportsResizeFit&&_t(Z)){let Nn=q3(Sf,q(),Y()),jd=y4(_a,yn,{centerX:Nn.centerX,centerY:Nn.centerY,halfWidthUV:Nn.halfWidthUV,halfHeightUV:Nn.halfHeightUV,rotationDeg:Nn.rotation,aspectRatio:Be()});Ic=jd.x,$p=jd.y}ue.liveX=Ic,ue.liveY=$p;let R5=Z._liveHandles?.[`_smoothed_${ge}`];R5 instanceof si&&R5.setComponents(Ic,1-$p)}else if(Pe.type===\"mouse\"){let Ke=Pe.smoothing??0,ut=Pe.momentum??0,mt=Pe.axis===\"x\"?de:xe;!ue.pointerSnapped&&De&&(ue.pointerSnapped=!0,ue.currentX=Math.max(.001,mt),ue.velocityX=0);let[Gt,Es]=C_(ue.currentX,ue.velocityX,mt,Ke,ut,$);ue.currentX=Gt,ue.velocityX=Es;let Tr=Math.max(.001,Gt),dr=Z.mapValues[ge],ss=dr?dr.outputMin+(dr.outputMax-dr.outputMin)*Math.pow(Tr,Math.pow(2,-dr.curve*2)):Tr;ue.liveScalar=ss,Qs(Z,`_smoothed_${ge}`,ss)}}}for(let[ge,ue]of Object.entries(Z.uniforms))ue._listSpec&&WA(ue._rawList,ue._listSpec)&&cf(Z,ge);for(let ge in Z.autoAnimateState){let ue=ee[ge];if(!ue||ue.type!==\"auto-animate\")continue;let Pe=oe*(ue.speed??1)*.2,Ke,ut=ue.easing??ue.waveform??\"sine\";if(ue.mode===\"loop\")Ke=pA((Pe%1+1)%1,ut);else{let ss=(Pe%1+1)%1;Ke=pA(ss<.5?ss*2:(1-ss)*2,ut)}let mt=Z.mapValues[ge],Gt=mt?mt.outputMin:Le(Z,ge,ue.outputMin),Es=mt?mt.outputMax:Le(Z,ge,ue.outputMax),Tr=Gt+Ke*(Es-Gt),dr=Z.uniforms[ge];if(dr?.compileTimeWhen){let ss=dr._lastCompiledValue;dr.compileTimeWhen(ss,Tr)&&(dr._lastCompiledValue=Tr,qn())}Qs(Z,`_smoothed_${ge}`,Tr)}}},xf=()=>{let $=Ss.deltaTime;for(let Z of u.values()){let ee=Z.definition.animatedTime;if(ee){let ue=Z.uniforms[ee.speed],Pe=ue&&Number(ue.value)||0,Ke=zl(Oc(Z));Ke.advance($,Pe),Qs(Z,\"_animTime\",Ke.value)}let ge=Z.definition.extraAnimatedTimes;if(ge)for(let[ue,Pe]of Object.entries(ge)){let Ke=Z.uniforms[Pe],ut=Ke&&Number(Ke.value)||0,mt=zl($d(Z,Pe));mt.advance($,ut),Qs(Z,`_animTime_${ue}`,mt.value)}}},Gc=($,Z)=>BB($,Z,{maxTextureDim:k,pixelRatio:L,viewportWidth:typeof window<\"u\"?window.innerWidth:$,viewportHeight:typeof window<\"u\"?window.innerHeight:Z}),Wp=()=>{let $=(typeof window<\"u\"?window.devicePixelRatio:1)||1;return Math.min($,vB())},Fc=($,Z,ee,ge)=>{if(F&&$===C&&Z===E&&ee===R&&ge===U){let{width:ut,height:mt}=Vi();if(!a||a.width===ut&&a.height===mt)return}C=$,E=Z,R=ee,U=ge,qA(ee,ge),F||(F=!0,u.size>0&&qn());let{width:ue,height:Pe}=Vi();a&&(a.width=ue,a.height=Pe);let Ke=!1;for(let ut of y)ut.passManager.resize(ue,Pe)&&(Ke=!0);for(let ut of y)for(let mt of ut.ir.onResize)try{mt({width:ue,height:Pe})}catch(Gt){Sr(\"[gpu] composition onResize error:\",Gt)}for(let ut of u.values()){if(Wn(ut)&&Qs(ut,\"_xform_aspectRatio\",Be()),_t(ut)){let mt=tt(ut);if(mt)for(let[Gt,Es]of Object.entries(mt))Qs(ut,`_bbox_${Gt}`,Es)}We(ut)}if($r(),Ke)for(let ut of y)M5(ut.store),ut.store.flush(),ut.passManager.repaintRtt();Te||(te=0,wf())},qp=($,Z)=>{$<=0||Z<=0||$===D&&Z===O||(D=$,O=Z,qe={width:$,height:Z},!st&&typeof requestAnimationFrame==\"function\"&&(st=!0,requestAnimationFrame(()=>{if(st=!1,!qe)return;let ee=qe;qe=null;let{width:ge,height:ue}=Gc(ee.width,ee.height);Fc(ge,ue,ee.width,ee.height)})))},Af=$=>{let Z=$[0];if(!Z)return;let ee,ge;if(Z.contentBoxSize){let ue=Array.isArray(Z.contentBoxSize)?Z.contentBoxSize[0]:Z.contentBoxSize;ue?(ee=Math.round(ue.inlineSize),ge=Math.round(ue.blockSize)):(ee=Math.round(Z.contentRect.width),ge=Math.round(Z.contentRect.height))}else ee=Math.round(Z.contentRect.width),ge=Math.round(Z.contentRect.height);ee===0||ge===0||qp(ee,ge)},RW=$=>{let Z=$[0];Z&&(X=Z.isIntersecting)},C5=()=>i&&!!t&&!!s&&C>0&&E>0,M5=$=>{let Z=$.systemHandles,{width:ee,height:ge}=Vi(),ue=Wp(),Pe=(Ke,ut)=>{let mt=Z[Ke];mt instanceof si&&(mt.value=ut)};Pe(\"time\",oe),Pe(\"viewportSize\",[ee,ge]),Pe(\"logicalViewportSize\",[q()*ue,Y()*ue]),Pe(\"aspect\",ee/Math.max(1,ge)),Pe(\"pointer\",[de,xe]),Pe(\"pointerActive\",Ae?1:0)},E5=$=>{for(let Z of $)try{Z(Ss)}catch(ee){Sr(\"[gpu] render callback error:\",ee)}},wy=($,Z)=>{if(Wt||!C5())return;let ee=performance.now(),ge=Z??ee,ue;if($!==void 0)ue=$;else{let mt=RB(ge,{lastRenderTime:te,isVisible:X,forceFullFrameRate:Q,minInterval:J});if(!mt.render)return;ue=mt.deltaTime}te=ge,nt&&performance.mark(\"shader-cpu-start\");let Pe=de>-5;Ss.deltaTime=ue,Ss.pointer.x=Pe?de:.5,Ss.pointer.y=Pe?xe:.5,Ss.pointer.seen=De,Ss.pointerActive=Ae,Ss.dimensions.width=C,Ss.dimensions.height=E;let Ke=null,ut=null;try{DB({updateDrivers:()=>{Yp(),xf(),Nt(),cr()},ensureComposition:()=>((P||x===null)&&(x=mf(),P=!1),x===null?!1:(Ke=g.getOrBuild(x,()=>ff(x)),Ay(Ke),!!b)),beforeRender:()=>E5(b.ir.onBeforeRender),flush:()=>{M5(b.store),b.store.flush()},render:()=>{if(nt&&performance.mark(\"shader-gpu-start\"),ut=g.renderValue??Ke,ut?.broken)return!1;let mt=ut,Gt=mt&&mt.validationFrames>0&&mt.ir.usesCustomWgsl?t?.device:void 0;Gt&&Gt.pushErrorScope(\"validation\");try{mt?.passManager.render(s,Ss,()=>b?.store.flush())}finally{Gt&&mt&&(mt.validationFrames--,Gt.popErrorScope().then(Es=>{Es&&!mt.broken&&vf(mt,Es.message)}).catch(()=>{}))}if(nt){performance.mark(\"shader-gpu-end\");try{performance.measure(\"shader-gpu-time\",\"shader-gpu-start\",\"shader-gpu-end\");let Es=performance.getEntriesByName(\"shader-gpu-time\")[0];Es&&Gs.recordGpuTime(Es.duration),performance.clearMeasures(\"shader-gpu-time\"),performance.clearMarks(\"shader-gpu-start\"),performance.clearMarks(\"shader-gpu-end\")}catch{}}},markReady:()=>{g.markReady(x),Mt&&!St&&(St=!0,Mt=!1,yt&&queueMicrotask(yt))},afterRender:()=>E5((ut??b).ir.onAfterRender)}),gs=0}catch(mt){WW(mt);return}if(nt){performance.mark(\"shader-cpu-end\");try{performance.measure(\"shader-cpu-time\",\"shader-cpu-start\",\"shader-cpu-end\");let mt=performance.getEntriesByName(\"shader-cpu-time\")[0];mt&&Gs.recordCpuTime(mt.duration),performance.clearMarks(\"shader-cpu-start\"),performance.clearMarks(\"shader-cpu-end\"),performance.clearMeasures(\"shader-cpu-time\")}catch{}}Gs.recordFrame(performance.now()-ee)},wf=$=>wy(void 0,$),DW=async $=>{if(Wt)return;if(!C5())throw new Error(\"Renderer is not ready\");let Z=or.running;Z&&Vp(),te=0,wy(Te?0:void 0),t&&$?.waitForGpu!==!1&&await mA(t),Z&&Ty()},LW=async($,Z)=>{if(!Number.isFinite($))throw new TypeError(`[gpu] renderSyntheticFrame: deltaSeconds must be a finite number, got ${$}`);wy($),t&&Z?.waitForGpu!==!1&&await mA(t)},Ty=()=>{Wt||Te||or.running||!V||(Gs.setRendering(!0),or.start())},Vp=()=>{or.running&&(or.stop(),Gs.setRendering(!1))},Tf=$=>{if(!a)return;Ee=a.getBoundingClientRect();let Z,ee;if(\"touches\"in $){if($.touches.length===0)return;Z=$.touches[0].clientX,ee=$.touches[0].clientY}else Z=$.clientX,ee=$.clientY;de=(Z-Ee.left)/Ee.width,xe=(ee-Ee.top)/Ee.height,De=!0},UW=$=>{!i||!a||(Tf($),Ae=!0,Da())},OW=$=>{!i||!a||(Tf($),Ae=!0,Da())},GW=()=>{i&&(Ae=!1,Da())},FW=()=>{i&&(Ae=!1,Da())},k5=($,Z)=>{if(!i||!a)return!1;let ee=a.getBoundingClientRect();return ee.width===0||ee.height===0?!1:$>=ee.left&&$<=ee.right&&Z>=ee.top&&Z<=ee.bottom},IW=$=>{$.button===0&&k5($.clientX,$.clientY)&&(Ae=!0,Tf($),Da())},YW=$=>{$.touches.length!==0&&k5($.touches[0].clientX,$.touches[0].clientY)&&(Ae=!0,Tf($),Da())},Np=()=>{let $=!1;for(let Z of u.values()){if(Z.usesPointer){$=!0;break}if(sve(Z.uniforms?.shape?.value)||df(Z)){$=!0;break}if(Z.metadata.maps){for(let ee of Object.values(Z.metadata.maps))if(ee&&typeof ee==\"object\"&&\"type\"in ee){let ge=ee.type;if(ge===\"mouse\"||ge===\"mouse-position\"){$=!0;break}}if($)break}}$&&!ze?(ze=!0,!Ie&&a&&(Ie=tve.register({onMouseMove:UW,onTouchMove:OW,onMouseDown:IW,onTouchStart:YW,onMouseUp:GW,onTouchEnd:FW}))):!$&&ze&&(ze=!1,Ie&&(Ie(),Ie=null))},B5=()=>{if(f.length===0)return;let $=[...f];f=[];for(let ee of $)Xd(ee.id,ee.definition.fragment,ee.parentId,ee.metadata,ee.uniforms,ee.definition,ee.domCanvas);let Z=h;h=[];for(let ee of Z)ee.kind===\"uniform\"?yf(ee.id,ee.name,ee.value):bf(ee.id,ee.metadata)},Rl=($,Z)=>{if(!Wt){Wt=$,F5($),Sr(`[gpu] renderer stopped (${$}):`,Z);try{Sy()}catch(ee){Sr(\"[gpu] cleanup during failure threw:\",ee)}if(!us&&ls){us=!0;let ee=ls;queueMicrotask(()=>{try{ee($,Z)}catch(ge){Sr(\"[gpu] onUnavailable callback threw:\",ge)}})}}},WW=$=>{if(Cy($)){Rl($.reason,$);return}gs++,gs===1&&Mf(\"[gpu] render error:\",$),gs>=eve&&Rl(\"render-failed\",$)},qW=$=>{!$||typeof $.addEventListener!=\"function\"||(z5(),os=$,bs=Z=>{try{Z.preventDefault()}catch{}let ee=Z.error;if(Mf(\"[gpu] uncaptured device error (a custom WGSL body that fails to compile reports here \\u2014 read the message for the line):\",ee),typeof GPUOutOfMemoryError<\"u\"&&ee instanceof GPUOutOfMemoryError){Rl(\"out-of-memory\",ee);return}if(typeof GPUInternalError<\"u\"&&ee instanceof GPUInternalError){Rl(\"gpu-error\",ee);return}hs++,hs>=J1e&&Rl(\"gpu-error\",ee)},$.addEventListener(\"uncapturederror\",bs))},z5=()=>{if(os&&bs)try{os.removeEventListener(\"uncapturederror\",bs)}catch{}os=null,bs=null},VW=$=>{if(!Kt){Kt=!0;try{Vp()}catch{}if(Rt){let Z=Rt;queueMicrotask(()=>{try{Z($)}catch(ee){Sr(\"[gpu] onDeviceLost callback threw:\",ee)}});return}Rl(\"device-lost\",$)}},NW=async $=>{if($.context!==void 0)throw new Error(\"[gpu] initialize({ context }) is not supported \\u2014 this renderer is WebGPU-only. Remove the WebGL `context` option.\");if(Wt)return;let Z=I5();if(Z){Rl(Z);return}if(i||l)return;let{canvas:ee,resizeTarget:ge,enablePerformanceTracking:ue=!1,colorSpace:Pe=\"p3-linear\",toneMapping:Ke=\"linear\",gpu:ut,forceFullFrameRate:mt=!1,observeElement:Gt=!0,timeOrigin:Es}=$;Es!=null&&(le=Es),nt=ue,Q=mt,pt=Pe,Ze=Ke,Ra(),l=!0,d=new AbortController;let Tr=d;try{if(ht=gz,ht(Pe),Tr.signal.aborted||(a=ee,e=await z1e({device:ut?.device,adapter:ut?.adapter,onDeviceLost:yn=>VW(yn.message||yn.reason||\"unknown\")}),Tr.signal.aborted))return;t=e.root,qW(t.device),L=Wp();let dr=t.device.limits?.maxTextureDimension2D;typeof dr==\"number\"&&dr>0&&(k=dr),r=W1e(t),o=C4(t),typeof window<\"u\"&&(we=()=>Sy(),window.addEventListener(\"beforeunload\",we),Ve=()=>{if(D>0&&O>0){let yn=D,Ic=O;D=0,O=0,qp(yn,Ic)}},window.addEventListener(\"resize\",Ve,{passive:!0}));let ss=ee.getBoundingClientRect();Ee=ss,X=!Gt||ss.width>0&&ss.height>0&&ss.top<window.innerHeight&&ss.bottom>0&&ss.left<window.innerWidth&&ss.right>0,V=!0;let Oo=ss.width>0?ss.width:ee.width,Vn=ss.height>0?ss.height:ee.height,La=Math.round(Oo),_a=Math.round(Vn);if(La>0&&_a>0){D=La,O=_a;let{width:yn,height:Ic}=Gc(La,_a);C=yn,E=Ic,R=La,U=_a,F=!0,qA(La,_a);let{width:$p,height:Sf}=Vi();ee.width=$p,ee.height=Sf}else F=!1;if(s=L1e(t,ee),Gt&&typeof ResizeObserver<\"u\"){let yn=ge??ee.parentElement;yn?(ke=new ResizeObserver(Af),ke.observe(yn)):console.warn(\"[gpu] no element available for resize observation \\u2014 use resize() manually\"),typeof IntersectionObserver<\"u\"&&(Me=new IntersectionObserver(RW,{threshold:0,rootMargin:\"128px\"}),Me.observe(ee))}if(Tr.signal.aborted)return;i=!0,c=!0,B5(),C>0&&E>0&&wf(),V&&Ty()}catch(dr){let ss=Cy(dr)?dr.reason:\"init-failed\";l=!1,d===Tr&&(d=null),Rl(ss,dr);return}finally{l=!1,d===Tr&&(d=null)}},Sy=()=>{d&&d.abort(),ke&&(ke.disconnect(),ke=null),Me&&(Me.disconnect(),Me=null),Ie&&(Ie(),Ie=null),we&&typeof window<\"u\"&&(window.removeEventListener(\"beforeunload\",we),we=null),Ve&&typeof window<\"u\"&&(window.removeEventListener(\"resize\",Ve),Ve=null),se!==null&&typeof cancelAnimationFrame==\"function\"&&(cancelAnimationFrame(se),se=null),Vp(),z5(),za?.(),za=null,typeof document<\"u\"&&document.fonts?.removeEventListener&&document.fonts.removeEventListener(\"loadingdone\",ia);try{e?.release?.()}catch{}g.clear();for(let $ of[...y])hf($);b=null;try{r?.destroy()}catch{}try{e?.createdDevice&&t?.destroy()}catch{}u.clear(),p=null,_.clear(),f=[],h=[],c=!1,qe=null,st=!1,a=null,s=null,r=null,o=null,t=null,e=null,i=!1,l=!1,F=!1,d=null,C=0,E=0,R=0,U=0,x=null,P=!0,X=!1,V=!0,Te=!1,de=-10,xe=-10,Ae=!1,De=!1,ze=!1,yt=null,St=!1,Mt=!1,Rt=null,Kt=!1,hs=0,gs=0};return{initialize:NW,cleanup:Sy,registerNode:Xd,removeNode:Hd,updateUniformValue:yf,updateNodeMetadata:bf,isInitialized:()=>i,resize:($,Z)=>{t&&qp($,Z)},startAnimation:Ty,stopAnimation:Vp,renderAndWait:DW,renderSyntheticFrame:LW,getPerformanceStats:()=>{let $=Math.max(0,u.size-1),Z=b?b.ir.rttPasses.length:0;Gs.updateNodeCounts($,Z);let ee={render:{calls:b?b.passManager.passCount:0},programs:{length:g.size},memory:{textures:r?r.textureCount:0}};return Gs.getStats(ee)},getLiveDriverValue:($,Z)=>{let ee=u.get($)?.mouseDriverState?.[Z];return!ee||ee.liveX===void 0||ee.liveY===void 0?null:{x:ee.liveX,y:ee.liveY}},getLiveScalarValue:($,Z)=>{let ee=u.get($);if(!ee)return null;let ge=ee.metadata.maps?.[Z];if(ge?.type===\"auto-animate\"){let ue=ee._liveHandles?.[`_smoothed_${Z}`],Pe=ue instanceof si?ue.value:void 0;return typeof Pe==\"number\"?Pe:null}return ge?.type===\"mouse\"?ee.mouseDriverState[Z]?.liveScalar??null:null},getNodeRegistry:()=>({nodes:new Map(u)}),getRendererType:()=>\"webgpu\",getInternalRenderer:()=>t?{device:t.device,adapter:e?.adapter,root:t}:null,setForceFullFrameRate:$=>{let Z=Q;return Q=$,Z},beginRecordingResolution:($,Z,ee)=>{if(!t||$<=0||Z<=0)return()=>{};let ge=L,ue=C,Pe=E,Ke=R,ut=U,mt=D,Gt=O;return L=ee,Fc($,Z,$,Z),()=>{t&&(L=ge,Fc(ue,Pe,Ke,ut),D=mt,O=Gt)}},setResolutionScale:$=>{if(!t)return;let Z=Math.max(.05,Math.min($,1)),ee=Math.max(.01,Wp()*Z);if(Math.abs(L-ee)<.001)return;L=ee;let ge=R||C,ue=U||E;if(ge<=0||ue<=0)return;let{width:Pe,height:Ke}=Gc(ge,ue);Fc(Pe,Ke,ge,ue)},setFrameRateCap:$=>{J=$&&$>0?1e3/Math.max($,10)-1:0},setTimeOrigin:$=>{le=$},setFrameLocked:$=>{let Z=Te;if(Te=$,!(!$||Z)){Vp(),se!==null&&typeof cancelAnimationFrame==\"function\"&&(cancelAnimationFrame(se),se=null),oe=0;for(let ee of u.values()){ee.definition.animatedTime&&(zl(Oc(ee)).value=0,Qs(ee,\"_animTime\",0));let ge=ee.definition.extraAnimatedTimes;if(ge)for(let[ue,Pe]of Object.entries(ge))zl($d(ee,Pe)).value=0,Qs(ee,`_animTime_${ue}`,0)}}},setOnReady:$=>{yt=$,$&&St&&queueMicrotask($)},setOnDeviceLost:$=>{Rt=$},setOnUnavailable:$=>{if(ls=$,$&&Wt&&!us){us=!0;let Z=Wt;queueMicrotask(()=>{try{$(Z)}catch(ee){Sr(\"[gpu] onUnavailable callback threw:\",ee)}})}},getFailureReason:()=>Wt,__testing:{needsTransformation:el,findChildNodes:lr,buildFieldInits:$=>{let Z=u.get($);return Z?af(Z):null},ensureDriverState:$=>{let Z=u.get($);Z&&Fp(Z)},stepMouseDrivers:$=>{Ss.deltaTime=$,Yp()},stepAnimatedTime:$=>{Ss.deltaTime=$,xf()},getAnimatedTimeValue:$=>{let Z=u.get($);return Z?.definition.animatedTime?zl(Oc(Z)).value:null},writeExtraField:($,Z,ee)=>{let ge=u.get($);ge&&Ip(ge,Z,ee)},readExtraField:($,Z)=>{let ee=u.get($)?._liveHandles?.[Z];return ee instanceof si?ee.value:void 0},getNodeRegistry:()=>({nodes:u,rootId:p,parentToChildren:_}),getParentToChildren:()=>_,getPendingRegistrations:()=>[...f],getFrameDiagnostics:()=>({deltaTime:Ss.deltaTime,pointerX:de,pointerY:xe,pointerActive:Ae,globalElapsedTime:oe,lastRenderTime:te,isVisible:X,forceFullFrameRate:Q,sharedTimeOrigin:le,animating:or.running,mouseListenersNeeded:ze}),computeStructuralHash:mf,isStructuralDirty:()=>P,clearStructuralDirty:()=>{P=!1},setTestReady:$=>{i=!0,c=!0,$&&(C=$.width,E=$.height,R=$.width,U=$.height,F=!0),B5()}}}}var Nd={samplingRate:.05,collectionDuration:5e3,warmupDuration:500,sampleInterval:500,apiEndpoint:\"https://shaders.com/api/telemetry\"};function rve(e){return e.includes(\"Edg/\")||e.includes(\"EdgiOS\")?\"Edge\":e.includes(\"OPR/\")||e.includes(\"Opera/\")?\"Opera\":e.includes(\"Chrome/\")||e.includes(\"CriOS\")?\"Chrome\":e.includes(\"Firefox/\")||e.includes(\"FxiOS\")?\"Firefox\":e.includes(\"Safari/\")&&!e.includes(\"Chrome\")?\"Safari\":\"Other\"}function ove(e){return/iPad|Tablet|PlayBook/i.test(e)||/Android/i.test(e)&&!/Mobile/i.test(e)?\"tablet\":/Mobile|Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(e)?\"mobile\":\"desktop\"}function ave(){if(typeof window>\"u\"||typeof navigator>\"u\")return{domain:\"unknown\",browserFamily:\"unknown\",deviceType:\"unknown\"};let e=navigator.userAgent;return{domain:window.location.hostname,browserFamily:rve(e),deviceType:ove(e)}}var nve=class{renderer;version;sourceType;sessionId;frameSamples=[];sampleInterval=null;stopped=!1;constructor(e,t,s){this.renderer=e,this.version=t,this.sourceType=s,this.sessionId=`tel_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}stop(){this.stopped=!0,this.sampleInterval&&(clearInterval(this.sampleInterval),this.sampleInterval=null)}async start(){try{if(await this.sleep(Nd.warmupDuration),this.stopped||(this.sampleInterval=setInterval(()=>{if(!this.stopped)try{let e=this.renderer.getPerformanceStats();this.frameSamples.push(e)}catch(e){Sr(\"Telemetry sampling error:\",e)}},Nd.sampleInterval),await this.sleep(Nd.collectionDuration-Nd.warmupDuration),this.stopped))return;if(this.sampleInterval&&(clearInterval(this.sampleInterval),this.sampleInterval=null),!this.stopped&&this.frameSamples.length>=5){let e=this.aggregateData();await this.sendTelemetry(e)}}catch(e){Sr(\"Telemetry collection error:\",e)}}sleep(e){return new Promise(t=>setTimeout(t,e))}aggregateData(){let e=this.aggregatePerformance(),t=this.detectRendererType(),s=this.extractComponents(),r=ave();return{sessionId:this.sessionId,timestamp:Date.now(),collectionDuration:Nd.collectionDuration,version:this.version,sourceType:this.sourceType,performance:e,renderer:t,components:s,environment:r}}aggregatePerformance(){if(this.frameSamples.length===0)return{fps:0,frameTime:{avg:0,min:0,max:0,p99:0},jankPercent:0,budgetUsed:0};let e=this.frameSamples.map(p=>p.avgFrameTime),t=this.frameSamples.map(p=>p.jankPercent),s=this.frameSamples.map(p=>p.budgetUsed),r=this.frameSamples.map(p=>p.fps),o=this.calculateMedian(r),a=this.calculateMean(e),i=Math.min(...e),l=Math.max(...e),c=this.calculatePercentile(e,99),d=this.calculateMean(t),u=this.calculateMean(s);return{fps:Math.round(o*10)/10,frameTime:{avg:Math.round(a*10)/10,min:Math.round(i*10)/10,max:Math.round(l*10)/10,p99:Math.round(c*10)/10},jankPercent:Math.round(d*10)/10,budgetUsed:Math.round(u*10)/10}}detectRendererType(){let e=this.frameSamples[this.frameSamples.length-1];return{type:this.renderer.getRendererType?.()||\"webgl\",drawCalls:e?.drawCalls??null,textureCount:e?.textureCount??null}}extractComponents(){try{let e=this.renderer.getNodeRegistry(),t=[];for(let[s,r]of e.nodes)r.parentId!==null&&t.push({name:r.componentName,requiresRTT:r.requiresRTT,renderOrder:r.metadata.renderOrder});return t.sort((s,r)=>s.renderOrder-r.renderOrder)}catch(e){return Sr(\"Failed to extract components:\",e),[]}}async sendTelemetry(e){try{let t=await fetch(Nd.apiEndpoint,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify(e)});t.ok||di(\"Telemetry send failed:\",t.status,t.statusText)}catch(t){Sr(\"Telemetry send error:\",t)}}calculateMean(e){return e.length===0?0:e.reduce((t,s)=>t+s,0)/e.length}calculateMedian(e){if(e.length===0)return 0;let t=[...e].sort((r,o)=>r-o),s=Math.floor(t.length/2);return t.length%2===0?(t[s-1]+t[s])/2:t[s]}calculatePercentile(e,t){if(e.length===0)return 0;let s=[...e].sort((o,a)=>o-a),r=Math.ceil(t/100*s.length)-1;return s[Math.max(0,r)]}};function ive(e=!1,t=!1){return e||typeof navigator<\"u\"&&navigator.doNotTrack===\"1\"?!1:t?!0:Math.random()<Nd.samplingRate}function EW(e,t,s=!1,r=!1){return ive(s,r)?new nve(e,t,r?\"preview\":\"shader\"):null}function kW(){if(typeof window>\"u\"||!window.location)return!1;let e=window.location.hostname;return!e.includes(\"shaders.com\")&&e!==\"localhost\"&&e!==\"127.0.0.1\"}function P5(e){if(typeof e!=\"object\"||e===null||!(\"type\"in e))return!1;let t=e;return t.type===\"map\"||t.type===\"mouse\"||t.type===\"mouse-position\"||t.type===\"auto-animate\"}var BW=new Set([\"opacity\",\"blendMode\",\"visible\",\"transform\",\"boundingBox\",\"maskSource\",\"maskType\",\"flow\",\"absolute\"]);async function zW(e,t,s){let r=new Map;my().forEach(k=>{r.set(k.definition.name,k.definition);for(let D of k.definition.deprecatedNames??[])r.set(D,k.definition)});for(let k of s?.components??[])r.set(k.name,k);if(!e.style.width||!e.style.height){let k=e.getBoundingClientRect(),D=k.width>0?k.width:e.width,O=k.height>0?k.height:e.height;D>0&&!e.style.width&&(e.style.width=`${D}px`),O>0&&!e.style.height&&(e.style.height=`${O}px`)}function o(k){return{...k,props:k.props?{...k.props}:void 0,children:k.children?.map(o)}}let a={...t,components:t.components.map(o)},i=null,l=!1,c=!1,d=3,u=6e4,p=0,_=0,f=null;function h(k){try{s?.onError?.(k)}catch(D){Sr(\"[createShader] onError callback threw:\",D)}}let g=!1;function y(k){g||(g=!0,f=k,h(k))}function b(k,D,O,F,q){let Y=r.get(O.type)??P2(O.type);if(!Y){console.warn(`[createShader] Unknown component type: ${O.type}`);return}let V=O.id||`${O.type}_${Date.now()}_${Math.random().toString(36).slice(2,9)}`,X={};for(let[J,oe]of Object.entries(O.props||{}))!BW.has(J)&&Object.prototype.hasOwnProperty.call(Y.props,J)&&P5(oe)&&(X[J]=oe);let Q=HA(Y,Object.fromEntries(Object.entries(Y.props).map(([J,oe])=>{let le=O.props?.[J]!==void 0?O.props[J]:oe.default;return[J,P5(le)?oe.default:le]})),V),te={blendMode:O.props?.blendMode||\"normal\",opacity:O.props?.opacity,visible:O.props?.visible,renderOrder:q,id:O.id,maps:Object.keys(X).length>0?{...X}:void 0,mask:O.props?.maskSource?{source:O.props.maskSource,type:O.props.maskType||\"alpha\"}:void 0,transform:O.props?.transform?{offsetX:0,offsetY:0,rotation:0,scale:1,anchorX:.5,anchorY:.5,edges:\"transparent\",...O.props.transform}:void 0,boundingBox:Ih(O.props?.boundingBox),flow:O.props?.flow,absolute:O.props?.absolute};k.registerNode(V,Y.fragment,F,te,Q,Y),D.set(V,{componentDef:Y,uniforms:Q,nodeId:V,component:O,currentMaps:{...X}}),O.children?.forEach((J,oe)=>{b(k,D,J,V,oe)})}async function x(){let k=MW(),D=new Map;s?.onReady&&k.setOnReady(s.onReady),k.setOnUnavailable(q=>{l||y(q)}),await k.initialize({canvas:e,resizeTarget:e,enablePerformanceTracking:s?.enablePerformanceTracking||!1,colorSpace:s?.colorSpace,toneMapping:s?.toneMapping,observeElement:s?.observeElement,gpu:s?.gpu});let O={renderer:k,nodeEntryMap:D,telemetryCollector:null,telemetryStartTimeout:null};k.setOnDeviceLost(q=>{if(l||c||f||i!==null&&i!==O)return;c=!0,h(q);let Y=Date.now();if(Y-_>u&&(p=0),_=Y,++p>d){P(O),i=null,c=!1,y(\"unrecoverable\");return}(async()=>{try{if(P(O),i=null,l)return;let V=await x();if(l){P(V);return}i=V}catch(V){Sr(\"[createShader] rebuild after device loss failed:\",V),y(\"rebuild_failed\")}finally{c=!1}})()});let F=\"shader-root\";if(k.registerNode(F,og.fragment,null,null,{},og),a.components.forEach((q,Y)=>{b(k,D,q,F,Y)}),kW()){let Y=0,V=()=>{if(l||f||i!==O||++Y>40){O.telemetryStartTimeout=null;return}k.getPerformanceStats().fps>0?(O.telemetryCollector=EW(k,\"4.0.4\",s?.disableTelemetry||!1,!1),O.telemetryCollector&&O.telemetryCollector.start(),O.telemetryStartTimeout=null):O.telemetryStartTimeout=setTimeout(V,500)};O.telemetryStartTimeout=setTimeout(V,500)}return O}function P(k){if(k.telemetryCollector){try{k.telemetryCollector.stop()}catch{}k.telemetryCollector=null}k.telemetryStartTimeout!==null&&(clearTimeout(k.telemetryStartTimeout),k.telemetryStartTimeout=null);try{k.renderer.cleanup()}catch{}}i=await x();function C(k,D){if(!i)return;let{renderer:O,nodeEntryMap:F}=i,q=F.get(k);if(!q){console.warn(`[createShader] Component ID not found: ${k}`);return}q.component.props||(q.component.props={});let Y=q.component.props;for(let[V,X]of Object.entries(D))if(BW.has(V))if(V===\"maskSource\")O.updateNodeMetadata(q.nodeId,{mask:X?{source:X,type:D.maskType||Y.maskType||\"alpha\"}:void 0}),Y.maskSource=X,\"maskType\"in D&&(Y.maskType=D.maskType);else{if(V===\"maskType\")continue;V===\"transform\"?(O.updateNodeMetadata(q.nodeId,{transform:X}),Y.transform=X):V===\"boundingBox\"?(O.updateNodeMetadata(q.nodeId,{boundingBox:Ih(X)}),Y.boundingBox=X):(O.updateNodeMetadata(q.nodeId,{[V]:X}),Y[V]=X)}else P5(X)?Object.prototype.hasOwnProperty.call(q.componentDef.props,V)?(q.currentMaps[V]=X,O.updateNodeMetadata(q.nodeId,{maps:{...q.currentMaps}}),Y[V]=X):console.warn(`[createShader] Ignoring PropDriver for unknown prop \"${V}\" on ${q.component.type}`):(V in q.currentMaps&&(delete q.currentMaps[V],O.updateNodeMetadata(q.nodeId,{maps:Object.keys(q.currentMaps).length>0?{...q.currentMaps}:void 0})),Object.prototype.hasOwnProperty.call(q.componentDef.props,V)&&(O.updateUniformValue(q.nodeId,V,X),Y[V]=X))}function E(k,D){let O=e.getBoundingClientRect(),F=k??(O.width>0?O.width:e.width),q=D??(O.height>0?O.height:e.height);F>0&&q>0&&(e.style.width=`${F}px`,e.style.height=`${q}px`,i&&i.renderer.resize(F,q))}function R(){l=!0,i&&(P(i),i=null)}function U(){i&&i.renderer.stopAnimation()}function L(){i&&i.renderer.startAnimation()}return{update:C,resize:E,pause:U,resume:L,destroy:R,getFailureReason:()=>f}}export{zW as createShader};\n","war-room/account-api.mjs":"const changed=()=>Object.assign(Error('Account changed. Refresh the War Room before saving.'),{code:'account_scope_changed'});\r\n// Scope comes only from an authenticated, epoch-consistent room read. It never\r\n// refreshes or relabels a pending body when the account generation changes.\r\nexport function createWarRoomApi({request,transitions}){\r\n let scope=null,readGeneration=0;\r\n const clear=()=>{scope=null;readGeneration++;};\r\n const unsubscribe=transitions.subscribe(clear);\r\n async function api(path,options={}){\r\n  const ticket=transitions.capture(),method=(options.method||'GET').toUpperCase();\r\n  const roomRead=path==='/api/war-room'&&method==='GET',write=path.startsWith('/api/war-room/')&&!['GET','HEAD'].includes(method);\r\n  const expected=scope,generation=roomRead?++readGeneration:readGeneration,headers=new Headers(options.headers);\r\n  if(roomRead)scope=null;\r\n  if(write){if(!expected)throw changed();headers.set('X-Target-Account',expected.owner);headers.set('X-Expected-Data-Epoch',String(expected.epoch));}\r\n  const response=await request(path,{...options,method,headers,signal:ticket.signal,credentials:'same-origin',cache:'no-store'});\r\n  transitions.assertCurrent(ticket);\r\n  const value=await response.json();transitions.assertCurrent(ticket);\r\n  if((roomRead&&generation!==readGeneration)||(write&&scope!==expected))throw changed();\r\n  if(!response.ok)throw Object.assign(Error(value.error||'The War Room service is unavailable.'),{status:response.status,code:value.code});\r\n  if(roomRead){if(typeof value.targetAccountId!=='string'||!value.targetAccountId||!Number.isSafeInteger(value.dataEpoch)||value.dataEpoch<1)throw changed();scope=Object.freeze({owner:value.targetAccountId,epoch:value.dataEpoch});}\r\n  return value;\r\n }\r\n return {api,clear,dispose:()=>{clear();unsubscribe?.();}};\r\n}\r\n","war-room/base-window.mjs":"import {FACTIONS,FACTION_BY_ID} from '../modules/coach-world/catalog.mjs';\r\nimport {assertBase} from '../modules/coach-world/state.mjs';\r\nimport {createPixelBillboards,disposePixelObject} from '../modules/coach-world/sprite-billboards.mjs';\r\nimport {ART_DIRECTION} from '../modules/coach-world/pixel-art.mjs';\r\n\r\nexport const validWindowBiome=value=>typeof value==='string'&&Object.hasOwn(FACTION_BY_ID,value)?value:'chest';\r\nexport function guestWindowBiome({storage,owner,account}={}){\r\n if(account?.user?.id||typeof owner!=='string'||!owner.startsWith('guest:')||owner==='guest:unavailable')return 'chest';\r\n try{const base=JSON.parse(storage?.getItem(`myr5-coach-world-v1/${encodeURIComponent(owner)}`));assertBase(base);return base.faction;}catch{return 'chest';}\r\n}\r\n\r\nexport function createWindowThemeController({apply,initialBiome,guestTheme=()=> 'chest',request,transitions}){\r\n let disposed=false,generation=0;apply(validWindowBiome(initialBiome));\r\n const reset=()=>{generation++;if(!disposed)apply('chest');},unsubscribe=transitions.subscribe(reset);\r\n return {reset,async sync(account){const run=++generation;if(disposed)return;\r\n  if(!account){apply(validWindowBiome(guestTheme()));return;}\r\n  apply('chest');const ticket=transitions.capture(),owner=account.user?.id,epoch=account.dataEpoch;\r\n  if(typeof owner!=='string'||!Number.isSafeInteger(epoch)||epoch<1)return;\r\n  try{const response=await request('/api/world',{method:'GET',headers:{'X-Target-Account':owner,'X-Expected-Data-Epoch':String(epoch)},signal:ticket.signal,cache:'no-store'});transitions.assertCurrent(ticket);if(disposed||run!==generation||!response.ok)return;const data=await response.json();transitions.assertCurrent(ticket);if(disposed||run!==generation||data.targetAccountId!==owner||data.dataEpoch!==epoch)return;const faction=data.environment?.faction??data.base?.faction;if(typeof faction==='string'&&Object.hasOwn(FACTION_BY_ID,faction))apply(faction,{phase:data.environment?.phase||'stable',source:data.environment?.source||'civilization'});\r\n  }catch{/* Keep the neutral default while account authority is unavailable. */}\r\n },dispose(){disposed=true;generation++;unsubscribe?.();}};\r\n}\r\n\r\nexport function mountBaseWindow(stage,T,initialBiome='chest',{getCanvas}={}){\r\n let group=null,disposed=false,current=null,currentPhase='stable',roomMode='flat';\r\n function remove(){if(!group)return;disposePixelObject(group);group=null;}\r\n function setBiome(value,{phase='stable'}={}){if(disposed)return;const id=validWindowBiome(value);phase=['stable','warzone','converting','healing','contested'].includes(phase)?phase:'stable';if(current===id&&currentPhase===phase&&group)return;remove();current=id;currentPhase=phase;const f=FACTION_BY_ID[id],index=FACTIONS.findIndex(x=>x.id===id);group=new T.Group();group.name='coach-base-window';group.position.y=roomMode==='cage'?-7:0;group.userData.biome=id;group.userData.phase=phase;group.userData.silhouette=ART_DIRECTION[id]?.shape||id;\r\n  const frame=new T.MeshBasicMaterial({color:'#243139'}),accent=new T.MeshBasicMaterial({color:f.accent}),add=(geometry,material,x,y,z)=>{const mesh=new T.Mesh(geometry,material);mesh.position.set(x,y,z);mesh.name='base-window-frame';group.add(mesh);return mesh;},box=(w,h,d,m,x,y,z)=>add(new T.BoxGeometry(w,h,d),m,x,y,z);\r\n  // The distant sky and mountains are raster layers, never polygon architecture.\r\n  const pixels=new Uint8Array(64*32*4),sky=new T.Color(f.dark),light=new T.Color(f.color);for(let y=0;y<32;y++)for(let x=0;x<64;x++){const p=(y*64+x)*4,c=sky.clone().lerp(light,(y/31)*.3),ridge=22-Math.abs((x+index*7)%22-11)*.7;if(y>ridge)c.copy(light).multiplyScalar(.45);if(y<15&&(x*19+y*31+index*11)%101===0)c.set(f.accent);pixels[p]=Math.round(c.r*255);pixels[p+1]=Math.round(c.g*255);pixels[p+2]=Math.round(c.b*255);pixels[p+3]=255;}\r\n  const texture=new T.DataTexture(pixels,64,32,T.RGBAFormat);texture.magFilter=texture.minFilter=T.NearestFilter;texture.generateMipmaps=false;texture.needsUpdate=true;texture.colorSpace=T.SRGBColorSpace;const skySprite=new T.Sprite(new T.SpriteMaterial({map:texture,toneMapped:false}));skySprite.name='pixel-window-sky';skySprite.position.set(0,7,-27);skySprite.scale.set(48,32,1);group.add(skySprite);\r\n  const layer=createPixelBillboards(T,{getCanvas});for(let i=0;i<3;i++)layer.add({faction:id,kind:i===1?'hq':'hall',variant:i,x:(i-1)*5.3,z:-13-(i%2)*5,height:4.4+(i%2)*1.4,tint:phase==='stable'?0xffffff:phase==='healing'?0xe0d9c7:0xaaa19c});layer.add({faction:id,kind:'ship',variant:0,x:-5,y:5,z:-18,height:2.3});layer.add({faction:id,kind:'resource',variant:0,x:3.8,z:-11,height:1.5});for(let i=0;i<2;i++)layer.add({faction:id,kind:'crew',variant:0,x:-3+i*6,z:-10,height:1.1});if(phase!=='stable')for(let i=0;i<4;i++)layer.add({faction:id,kind:'rubble',variant:0,x:-6+i*4,z:-10-(i%2),height:1.2});group.add(layer.group);\r\n  // Only the actual window frame remains a physical object in the existing room.\r\n  box(.45,10,.5,frame,-8,3.5,-8);box(.45,10,.5,frame,8,3.5,-8);box(16.5,.45,.5,frame,0,8.3,-8);box(16.5,.6,1.2,frame,0,-1.15,-8);box(.12,9,.2,accent,-4.8,3.5,-8.05);box(.12,9,.2,accent,4.8,3.5,-8.05);\r\n  stage.scene.add(group);stage.mount?.setAttribute('data-base-biome',id);\r\n }\r\n setBiome(initialBiome);return {setBiome,setRoomMode(mode){if(disposed)return;roomMode=mode==='cage'?'cage':'flat';if(group)group.position.y=roomMode==='cage'?-7:0;},get roomMode(){return roomMode;},get biome(){return current;},get group(){return group;},dispose(){if(disposed)return;disposed=true;remove();stage.mount?.removeAttribute('data-base-biome');}};\r\n}\r\n","war-room/coach-sprites.js":"var qt=Object.defineProperty;var $t=(e,a)=>()=>(e&&(a=e(e=0)),a);var Sa=(e,a)=>{for(var t in a)qt(e,t,{get:a[t],enumerable:!0})};function X(e=globalThis.localStorage){try{let a=JSON.parse(e?.getItem(Ke)||\"null\");return Ra(a)?a:null}catch{return null}}function C(e,a=globalThis.localStorage){let t=_e??X(a);return t?.gameGeneration>1?`${e}/game/${t.gameGeneration}`:e}function Zt(e=globalThis.localStorage){if(!e)return;let a=Array.from({length:e.length},(t,r)=>e.key(r));for(let t of a)/^(?:myr5-roguelite-preview-v1\\/|myr5-world-run-outbox-v1\\/)/.test(t)&&e.removeItem(t)}function er(e,{storage:a=globalThis.localStorage}={}){if(!Ra(e))throw We();let t=_e??X(a);if(t&&e.gameGeneration<t.gameGeneration)throw We(t);let r={gameGeneration:e.gameGeneration,gameResetAt:e.gameResetAt};if(t&&t.gameGeneration===r.gameGeneration&&t.gameResetAt!==r.gameResetAt)throw We(t);if(_e=r,(!t||t.gameGeneration!==r.gameGeneration)&&(r.gameResetAt>0&&Zt(a),a?.setItem(Ke,JSON.stringify(r)),t)){for(let s of Qt)s(r);globalThis.dispatchEvent?.(new CustomEvent(\"myr5:game-generation-changed\",{detail:r}))}return r}function V(e=globalThis.myr5AuthenticatedAccount){let a=_e??X();return!a?.gameResetAt||!e?.user?.id||e.gameGeneration===a.gameGeneration}var Ke,_e,Qt,Ra,We,Se=$t(()=>{Ke=\"myr5-game-generation-v1\",_e=null,Qt=new Set,Ra=e=>Number.isSafeInteger(e?.gameGeneration)&&e.gameGeneration>=1&&Number.isSafeInteger(e?.gameResetAt)&&e.gameResetAt>=0,We=e=>Object.assign(Error(\"Game progress changed. Reopen the game.\"),{code:\"game_generation_mismatch\",status:409,...e});typeof globalThis.window==\"object\"&&typeof globalThis.addEventListener==\"function\"&&globalThis.addEventListener(\"storage\",e=>{if(!(e.key!==Ke||!e.newValue))try{er(JSON.parse(e.newValue))}catch{}})});var xa=Object.freeze({jogging:{name:\"Subway Burpees\",types:[\"cardio\",\"legs\"]},\"plank-flight\":{name:\"Jet Plank Joyride\",types:[\"core\",\"balance\"]},cover:{name:\"Crouching Crisis\",types:[\"legs\",\"boxing\"]},dance:{name:\"Dance, Dance, and Obey\",types:[\"cardio\",\"martial-arts\"]}}),Y=[{id:\"chest\",name:\"Chest\",description:\"Push-up progressions\"},{id:\"legs\",name:\"Legs\",description:\"Squats and split squats\"},{id:\"hips\",name:\"Hips & glutes\",description:\"Hinges and bridges\"},{id:\"core\",name:\"Core\",description:\"Plank holds\"},{id:\"shoulders\",name:\"Shoulders\",description:\"Controlled arm movement\"},{id:\"balance\",name:\"Balance\",description:\"Single-leg yoga holds\"},{id:\"yoga\",name:\"Yoga\",description:\"Standing poses\"},{id:\"stances\",name:\"Martial stances\",description:\"Controlled stance holds\"},{id:\"boxing\",name:\"Boxing\",description:\"Slow shadowboxing\"},{id:\"cardio\",name:\"Cardio\",description:\"Marches and jacks\"}],de=[];function x(e,a,t,r,s={}){let o=s.kind??([\"plank\",\"sideplank\",\"balance\",\"yoga\",\"stance\"].includes(r)?\"hold\":r===\"boxing\"?\"pace\":r===\"march\"?\"steps\":\"reps\"),c=s.view??([\"pushup\",\"hinge\",\"bridge\",\"plank\",\"split\"].includes(r)?\"side\":\"front\"),n=o===\"hold\"?\"Pose hold\":o===\"pace\"?\"Active hand time\":o===\"steps\"?\"Knee lifts\":\"Repetitions\",u=s.limits??\"Counts the visible movement; does not grade technique.\",i=c===\"side\"?\"Camera beside you.\":c===\"angle\"?\"Camera at a slight angle.\":\"Face the camera.\";de.push({id:a,group:e,name:t,detector:r,kind:o,view:c,measurement:n,limits:u,difficulty:de.filter(l=>l.group===e).length+1,defaultGoal:o===\"hold\"||o===\"pace\"?15:5,evidence:\"rule-based; camera validation pending\",source:\"mediapipe\",...s,hint:`${i} ${s.cue??\"Keep the joints used for this movement visible.\"}`})}var jt=\"Counts elbow bend and return. Hand shape, support stability and chest depth are not verified.\";for(let[e,a,t]of[[\"knee-pushup\",\"Knee push-up\",{support:\"knees\",cue:\"Knees down; show shoulder, elbow, wrist and hip.\"}],[\"high-incline-pushup\",\"High incline push-up\",{incline:!0,cue:\"Hands on a stable high surface; show your full arm and torso.\"}],[\"low-incline-pushup\",\"Low incline push-up\",{incline:!0,cue:\"Hands on a stable lower surface; show your full arm and torso.\"}],[\"pushup\",\"Regular push-up\",{}],[\"wide-pushup\",\"Wide push-up\",{wide:!0}],[\"slow-pushup\",\"Slow push-up\",{minCycle:1.2}],[\"diamond-pushup\",\"Diamond push-up\",{narrow:!0}],[\"decline-pushup\",\"Feet-elevated push-up\",{decline:!0,cue:\"Feet on a stable low surface; show your full arm and torso.\"}]])x(\"chest\",e,a,\"pushup\",{cue:\"Show shoulder, elbow, wrist and hip. Start with your arm extended.\",limits:jt,...t});for(let[e,a,t]of[[\"shallow-squat\",\"Shallow squat\",{travel:.18}],[\"squat\",\"Bodyweight squat\",{}],[\"wide-squat\",\"Wide squat\",{wide:!0}],[\"pause-squat\",\"Pause squat\",{dwell:.8}],[\"slow-squat\",\"Slow squat\",{minCycle:1.2}],[\"split-left\",\"Split squat \\xB7 left\",{detector:\"split\",side:\"left\"}],[\"split-right\",\"Split squat \\xB7 right\",{detector:\"split\",side:\"right\"}],[\"wall-sit\",\"Wall sit\",{detector:\"yoga\",pose:\"wall-sit\",kind:\"hold\",view:\"side\",cue:\"Back on a wall, thighs level. Show shoulder, hip and knee.\",limits:\"Times the hip angle and thigh level. Wall contact and knee position are not tracked.\"}]])x(\"legs\",e,a,t.detector??\"squat\",{cue:t.side?\"Show the front leg from hip to knee. Start tall.\":\"Show shoulders and hips. Start standing tall.\",...t});for(let[e,a,t,r]of[[\"small-hinge\",\"Small hip hinge\",\"hinge\",{bend:25}],[\"hip-hinge\",\"Hip hinge\",\"hinge\",{}],[\"good-morning\",\"Bodyweight good morning\",\"hinge\",{arms:\"chest\"}],[\"glute-bridge\",\"Glute bridge\",\"bridge\",{}],[\"pause-bridge\",\"Pause glute bridge\",\"bridge\",{dwell:.8}]])x(\"hips\",e,a,t,{cue:t===\"bridge\"?\"Lie on your back, knees bent. Show shoulder, hip and knee. Start with hips down.\":\"Show shoulder, hip and knee. Start upright; hinge slowly.\",limits:\"Tracks hip movement. Back curvature and muscle engagement are not verified.\",...r});for(let[e,a,t]of[[\"knee-plank\",\"Knee plank\",{support:\"knees\"}],[\"high-plank\",\"High plank\",{}],[\"forearm-plank\",\"Forearm plank\",{forearm:!0}],[\"side-knee-left\",\"Side knee plank \\xB7 left\",{side:\"left\",support:\"knees\"}],[\"side-knee-right\",\"Side knee plank \\xB7 right\",{side:\"right\",support:\"knees\"}],[\"side-plank-left\",\"Side plank \\xB7 left\",{side:\"left\"}],[\"side-plank-right\",\"Side plank \\xB7 right\",{side:\"right\"}]])x(\"core\",e,a,t.side?\"sideplank\":\"plank\",{view:t.side?\"front\":\"side\",cue:t.side?\"Show both shoulders, hips and the supporting knee. Stack your shoulders.\":\"Show shoulder, elbow, wrist, hip and supporting knee.\",limits:\"Times the shoulder\\u2013hip\\u2013knee line. Feet, floor contact and loading are not tracked.\",...t});for(let[e,a,t,r]of[[\"front-raise\",\"Front arm raise\",\"raise\",{view:\"side\",plane:\"front\"}],[\"lateral-raise\",\"Lateral arm raise\",\"raise\",{}],[\"overhead-reach\",\"Overhead reach\",\"raise\",{overhead:!0}],[\"standing-press\",\"Standing arm press\",\"press\",{}],[\"slow-press\",\"Slow arm press\",\"press\",{minCycle:1.2}]])x(\"shoulders\",e,a,t,{cue:\"Keep both shoulders, elbows, wrists and hips visible. Use controlled, unloaded movement.\",...r});for(let[e,a,t]of[[\"knee-balance\",\"Knee-lift balance\",{kneeLift:!0}],[\"low-tree\",\"Low tree pose\",{low:!0}],[\"tree\",\"Tree pose\",{}],[\"overhead-tree\",\"Tree \\xB7 arms overhead\",{overhead:!0}]])x(\"balance\",e,a,\"balance\",{cue:\"Show both hips and knees. Either leg works; keep a support nearby if needed.\",limits:\"Times the visible leg shape. Balance, foot pressure and support use are not verified.\",...t});for(let[e,a,t]of[[\"mountain\",\"Mountain\",\"mountain\"],[\"salute\",\"Upward salute\",\"salute\"],[\"chair\",\"Chair pose\",\"chair\"],[\"warrior-one\",\"Warrior I\",\"warrior-one\"],[\"warrior\",\"Warrior II\",\"warrior\"],[\"goddess\",\"Goddess pose\",\"goddess\"]])x(\"yoga\",e,a,\"yoga\",{pose:t,view:[\"chair\",\"warrior-one\"].includes(t)?\"side\":\"front\",cue:\"Show arms, hips and knees. Hold only a comfortable position.\",limits:\"Times broad pose shape. Foot rotation and joint alignment are not graded.\"});for(let[e,a,t]of[[\"high-horse\",\"High horse stance\",{high:!0}],[\"horse\",\"Horse stance\",{}],[\"low-horse\",\"Lower horse stance\",{low:!0}],[\"front-stance-left\",\"Front stance \\xB7 left\",{side:\"left\"}],[\"front-stance-right\",\"Front stance \\xB7 right\",{side:\"right\"}]])x(\"stances\",e,a,\"stance\",{view:t.side?\"side\":\"front\",cue:\"Keep hips and knees visible. Move slowly into a comfortable stance.\",limits:\"Times stance shape only. This is not martial-arts technique instruction.\",...t});for(let[e,a,t]of[[\"jab-left\",\"Left straight practice\",{side:\"left\"}],[\"jab-right\",\"Right straight practice\",{side:\"right\"}],[\"boxing\",\"Alternating straights\",{}],[\"double-jab\",\"Double-jab practice\",{double:!0}]])x(\"boxing\",e,a,\"boxing\",{view:\"angle\",cue:\"Show shoulders, elbows, wrists and hips. Practice slow, controlled punches into empty space.\",limits:\"Tracks active hand time and relative pace. Punch type, power, accuracy and guard are not verified.\",...t});for(let[e,a,t,r]of[[\"march\",\"Easy march\",\"march\",{}],[\"high-march\",\"High-knee march\",\"march\",{lift:.4}],[\"jogging\",xa.jogging.name,\"march\",{lift:.08,runner:!0,endless:!0,cue:\"The route moves automatically. Crouch deeply to duck, raise both arms to jump, and lean left or right to change lanes. Keep your upper body visible.\"}],[\"step-jack\",\"Step jack\",\"jack\",{step:!0}],[\"jumping-jack\",\"Jumping jack\",\"jack\",{}]])x(\"cardio\",e,a,t,{cue:t===\"jack\"?\"Show your upper body and knees. Start with arms down and knees close together.\":\"Show shoulders, hips and both knees. Start standing tall.\",limits:t===\"jack\"?\"Counts arm-and-knee opening cycles. Feet, airtime and landings are not tracked.\":\"Counts knee lifts. Footfalls and impact are not verified.\",...r});x(\"core\",\"plank-flight\",xa[\"plank-flight\"].name,\"plank-flight\",{kind:\"hold\",view:\"front\",flight:!0,standalone:!0,endless:!0,cue:\"Phone faces you from a low stable stand. Keep both hands down, shoulders, arms, hips and knees visible. Rise/lower comfortably; lean left/right to fly.\",limits:\"Estimates face-on height and lean. Floor contact, arm loading and technique are not measured.\"});var A=Object.freeze(Object.fromEntries(de.map(e=>[e.id,Object.freeze(e)]))),qe=Object.freeze(Object.fromEntries(Y.map(e=>[e.id,Object.freeze(de.filter(a=>a.group===e.id&&!a.standalone))]))),Os=de.length;var R=Object.freeze([\"roster/21-flyer--winged_humanoid_3d_model\",\"roster/18-quad-all--wooden_robot_3d_model\"]),Ht=[[\"myr5\",\"original\",[\"MYR5 Jacked LaLAN\",\"Jack LaLanne\",\"jacked strength\",\"local area network\"],[\"MYR5 AT-Lift Prime\",\"Charles Atlas\",\"lift\",\"model designation\"]],[\"02-taper-tallstalk--humanoid_robot_3d_model1\",\"arms\",[\"Arnoid Press\",\"Arnold Schwarzenegger\",\"Arnold press\",\"android\"],[\"ScotTorque Curl\",\"Larry Scott\",\"curl\",\"motor torque\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model1\",\"arms\",[\"Ferri-Delt L0U\",\"Lou Ferrigno\",\"delt\",\"robot designation\"],[\"R0N Curlman\",\"Ronnie Coleman\",\"curl\",\"engine manifold\"]],[\"19-genie-multi--multi-armed_humanoid_3d_model\",\"arms\",[\"Frank Delt-Zan3\",\"Frank Zane\",\"delt\",\"magnetic coil\"],[\"Cutlerator Curl\",\"Jay Cutler\",\"curl\",\"accelerator\"]],[\"17-shellcap-manyarm--mushroom_creature_3d_model\",\"arms\",[\"KAI-nematic Raise\",\"Kai Greene\",\"raise\",\"kinematic mechanism\"],[\"Philament Heave Press\",\"Phil Heath\",\"press\",\"electrical filament\"]],[\"17-shellcap-manyarm--mushroom_robot_3d_model\",\"arms\",[\"Wheel-er Servo-Shrug\",\"Flex Wheeler\",\"shrug\",\"servo wheel\"]],[\"23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"arms\",[\"Ree-volts Raise\",\"Steve Reeves\",\"raise\",\"voltage\"]],[\"08-shard-asym--robot_3d_model4\",\"arms\",[\"S3RG Oliv-Armature\",\"Sergio Oliva\",\"arm\",\"motor armature\"]],[\"17-shellcap-manyarm--3d_character_figurine\",\"arms\",[\"Delt-alinn Bailey\",\"Dana Linn Bailey\",\"delt\",\"Delta designation\"]],[\"06-ridge-triad--geometric_robot_3d_model1\",\"chest\",[\"Oliva-C Pec\",\"Sergio Oliva\",\"pec\",\"vacuum drive\"]],[\"08-shard-asym--geometric_robot_3d_model\",\"chest\",[\"Pec-n0ld Reactor\",\"Arnold Schwarzenegger\",\"pec\",\"reactor\"]],[\"16-spade-arch--pyramid_head_figure_3d_model\",\"chest\",[\"Franx Pec-Zane\",\"Frank Zane\",\"pec\",\"motor drive\"],[\"Y4TES Yoke\",\"Dorian Yates\",\"press\",\"powered yoke\"]],[\"04-crest-wedge--robot_creature_3d_model\",\"chest\",[\"L0U Ferri-pecctor\",\"Lou Ferrigno\",\"pec\",\"reactor\"],[\"Wheeler Wheel-Bench\",\"Flex Wheeler\",\"bench\",\"drive wheel\"]],[\"16-spade-arch--stylized_humanoid_3d_model\",\"chest\",[\"Cutlerator Press\",\"Jay Cutler\",\"press\",\"accelerator\"]],[\"04-crest-wedge--stylized_3d_character\",\"chest\",[\"Gr33ne Grid-Press\",\"Kai Greene\",\"press\",\"power grid designation\"]],[\"09-monolith-tanka--3d_robot_model\",\"chest\",[\"chris BenchSTEAD\",\"Chris Bumstead\",\"bench\",\"robot\"]],[\"09-monolith-tanka--white_horned_robot_3d_model\",\"chest\",[\"Cavalier-EX Chest\",\"Jeff Cavaliere\",\"chest\",\"exercise model EX\"]],[\"15-orbital-coili--robot_character_3d_model\",\"quads\",[\"t-OHM Platzma\",\"Tom Platz\",\"squat\",\"plasma\"]],[\"13-fan-split--stylized_3d_character2\",\"quads\",[\"Quad-Cutler LEGS THO\",\"Jay Cutler\",\"quad\",\"circuit\"],[\"BumSTEADy Squat\",\"Chris Bumstead\",\"squat\",\"steady-state control\"]],[\"22-curve--stylized_cartoon_figure_3d_model\",\"quads\",[\"Kai Gr33ne Lunge\",\"Kai Greene\",\"lunge\",\"gear designation\"]],[\"02-taper-tallstalk--3d_humanoid_figure\",\"quads\",[\"Hane-Yoke Lunge\",\"Lee Haney\",\"lunge\",\"powered yoke\"],[\"Stern-on Squat\",\"Erin Stern\",\"squat\",\"power-on switch\"]],[\"02-taper-tallstalk--white_humanoid_doll_3d_model\",\"quads\",[\"Oli-Volt Lunge\",\"Sergio Oliva\",\"lunge\",\"voltage\"],[\"Y4TES Yoke-Squat\",\"Dorian Yates\",\"squat\",\"robot designation\"]],[\"18-quad-all--wooden_four-legged_robot_3d_model\",\"quads\",[\"Priest-L33 Squat\",\"Lee Priest\",\"squat\",\"piston\"]],[\"18-quad-all--stylized_quadruped_3d_model\",\"quads\",[\"Quadzilla Platz\",\"Tom Platz\",\"quads\",\"kaiju creature\"],[\"SternShift Squat\",\"Erin Stern\",\"squat\",\"gear shift\"]],[\"23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"glutes\",[\"Con-Thrust-Era\",\"Bret Contreras\",\"hip thrust\",\"thruster\"]],[\"23-blob-texture-bodies--sand_creature_3d_model\",\"glutes\",[\"Tingularity Thrust\",\"Chloe Ting\",\"hip thrust\",\"singularity\"]],[\"03-pearl-orb-ring--stylized_3d_character1\",\"glutes\",[\"Gyrvanium Glute-Drive\",\"Caroline Girvan\",\"glute\",\"futuristic alloy\"],[\"Jill-iON Hinge-Flex\",\"Jillian Michaels\",\"hip hinge\",\"ion drive\"]],[\"03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"glutes\",[\"Trac-ION Hip-Thrust\",\"Tracy Anderson\",\"hip thrust\",\"ion drive\"],[\"Simmonics Bridge-Servo\",\"Whitney Simmons\",\"bridge\",\"electronics servo\"]],[\"03-pearl-orb-ring--abstract_humanoid_3d_model\",\"glutes\",[\"Robo-ertson Bridge\",\"Heather Robertson\",\"bridge\",\"robot\"],[\"Hearn-ess ExoHinge\",\"Katy Hearn\",\"hinge\",\"exosuit harness\"]],[\"03-pearl-orb-ring--circle_center\",\"glutes\",[\"Con-Terra Coil-Bridge\",\"Bret Contreras\",\"bridge\",\"planetary coil\"],[\"Cyb-Ho Holo-Bridge\",\"Cassey Ho\",\"bridge\",\"cyborg hologram\"]],[\"23-blob-texture-bodies--cute_blob_creature_3d_model\",\"glutes\",[\"Chlo-Turbo Thrust\",\"Chloe Ting\",\"thrust\",\"turbine\"]],[\"10-petal-wisp--fantasy_creature_3d_model4\",\"yoga\",[\"Adriene A-Sana Mk II\",\"Adriene Mishler\",\"asana\",\"model revision\"]],[\"19-genie-multi--fantasy_creature_3d_model\",\"yoga\",[\"Mat-Gregor Matrix\",\"Kino MacGregor\",\"mat\",\"simulation matrix\"]],[\"10-petal-wisp--ghost_character_3d_model\",\"yoga\",[\"Warri0r-ner Drive\",\"Dylan Werner\",\"warrior pose\",\"motor designation\"],[\"Kathryn Bend-1g\",\"Kathryn Budig\",\"bend\",\"robot\"]],[\"08-shard-asym--stylized_action_figure_3d_model\",\"yoga\",[\"Yee-lastic Flow\",\"Rodney Yee\",\"yoga flow\",\"elastic mechanics\"]],[\"22-curve--stylized_alien_3d_model\",\"yoga\",[\"Strom Flex Current\",\"Max Strom\",\"stretch\",\"electric current\"]],[\"10-petal-wisp--stylized_creature_3d_model\",\"yoga\",[\"Iyeng-Arc Inversion\",\"B. K. S. Iyengar\",\"inversion\",\"powered arc\"]],[\"19-genie-multi--stylized_octopus_3d_model\",\"yoga\",[\"Yee Gyro-Yogi\",\"Rodney Yee\",\"yoga\",\"gyroscope\"]],[\"01-seed-pearo--cute_alien_figure_3d_model\",\"yoga\",[\"Wern-Axis Flow\",\"Dylan Werner\",\"flow\",\"robotic axis\"],[\"Mat-Gregor Drive\",\"Kino MacGregor\",\"mat practice\",\"motor drive\"]],[\"01-seed-pearo--white_3d_character_model\",\"yoga\",[\"Budig-ital Balance\",\"Kathryn Budig\",\"balance\",\"digital\"],[\"Corn Kernel Cobra\",\"Seane Corn\",\"cobra pose\",\"computing kernel\"]],[\"09-monolith-tanka--boxy_humanoid_3d_model\",\"martial-arts\",[\"Bruce L3300\",\"Bruce Lee\",\"kick\",\"alphanumeric call sign\"],[\"GSP-Drive\",\"Georges St-Pierre\",\"spar\",\"motor drive\"]],[\"11-anvil-cask--clay-style_robot_3d_model\",\"martial-arts\",[\"Anderson Sil-VOLT\",\"Anderson Silva\",\"strike\",\"voltage\"],[\"r0nda Rous-Bot\",\"Ronda Rousey\",\"armbar\",\"robot\"]],[\"05-slope-bobble--clay_humanoid_figure_3d_model\",\"martial-arts\",[\"C0N0R McG-Gear\",\"Conor McGregor\",\"kick\",\"gear transmission\"],[\"Nor-Rotor Punch\",\"Chuck Norris\",\"punch\",\"rotor\"]],[\"14-chisel-spire--cone_head_3d_model\",\"martial-arts\",[\"LiDAR Jett-Jab\",\"Jet Li\",\"jab\",\"laser ranging\"],[\"Jaa-Bot Jab\",\"Tony Jaa\",\"jab\",\"robot\"]],[\"08-shard-asym--fantasy_creature_3d_model2\",\"martial-arts\",[\"Waters0nic\",\"Michelle Waterson\",\"kick\",\"sonic drive\"],[\"Amanda Nu-NEURAL\",\"Amanda Nunes\",\"strike\",\"neural implant\"]],[\"09-monolith-tanka--mini_robot_3d_model\",\"martial-arts\",[\"Machi-Droid-a\",\"Lyoto Machida\",\"chop\",\"android\"],[\"Bas Rut-TORque\",\"Bas Rutten\",\"kick\",\"motor torque\"]],[\"09-monolith-tanka--robot_3d_model3\",\"martial-arts\",[\"Israel Ade-SYNC-ya\",\"Israel Adesanya\",\"strike\",\"synchronization\"],[\"Shev-Servo Sweep\",\"Valentina Shevchenko\",\"sweep\",\"servo\"]],[\"13-fan-split--stylized_toy_3d_model\",\"martial-arts\",[\"BlankSaber Kick\",\"Billy Blanks\",\"kick\",\"energy saber\"],[\"Wilsonic Kick\",\"Don Wilson\",\"kick\",\"sonic drive\"]],[\"09-monolith-tanka--robot_3d_model2\",\"martial-arts\",[\"Rous-Torque Throw\",\"Ronda Rousey\",\"throw\",\"motor torque\"],[\"Jackie Chain-Kick\",\"Jackie Chan\",\"kick\",\"chain drive\"]],[\"07-bulb-sphereling--humanoid_robot_3d_model\",\"cardio\",[\"B0LT Sprint-Drive\",\"Usain Bolt\",\"sprint\",\"electrical bolt drive\"]],[\"03-pearl-orb-ring--ringed_humanoid_3d_model\",\"cardio\",[\"Fon-Dynamo Footwork\",\"Jane Fonda\",\"footwork\",\"dynamo\"],[\"Simmonics Sweatstream\",\"Richard Simmons\",\"aerobic sweat\",\"electronics stream\"]],[\"06-ridge-triad--robot_3d_model1\",\"cardio\",[\"M0 Farah Fastlane\",\"Mo Farah\",\"fast run\",\"electric capacitance\"]],[\"03-pearl-orb-ring--robot_3d_model\",\"cardio\",[\"Feli-X Footdrive\",\"Allyson Felix\",\"footwork\",\"X-model drive\"],[\"Rad-Clutch Run\",\"Paula Radcliffe\",\"run\",\"motor clutch\"]],[\"20-lume--robotic_figure_3d_model\",\"cardio\",[\"Pre-Font-AI-ne Pace\",\"Steve Prefontaine\",\"pace\",\"AI running model\"],[\"Ohm-wens Run\",\"Jesse Owens\",\"run\",\"electrical resistance\"]],[\"07-bulb-sphereling--cute_robot_3d_model\",\"cardio\",[\"Billy Blanx Beam\",\"Billy Blanks\",\"boot camp\",\"waveform\"],[\"GebreShell Runloop\",\"Haile Gebrselassie\",\"run\",\"software shell loop\"]],[\"01-seed-pearo--blank_toy_figure_3d_model\",\"cardio\",[\"Phelpeller Pulse\",\"Michael Phelps\",\"cardio pulse\",\"propeller\"],[\"Fon-Droid Footwork\",\"Jane Fonda\",\"footwork\",\"android\"]],[\"18-quad-all--dragon_creature_3d_model\",\"meditation\",[\"H0F-Lung\",\"Wim Hof\",\"breathing\",\"oxygen-code lung\"]],[\"18-quad-all--fantasy_creature_3d_model1\",\"meditation\",[\"Max Ohm-Strom Breath\",\"Max Strom\",\"breathing\",\"electrical resistance\"],[\"Yee-uro Zen\",\"Rodney Yee\",\"mindful movement\",\"neural interface\"]],[\"18-quad-all--fantasy_creature_3d_model3\",\"meditation\",[\"Shiva Rea-spatory\",\"Shiva Rea\",\"respiration\",\"airlock\"]],[\"18-quad-all--four-legged_robot_3d_model\",\"meditation\",[\"Tara-5tile5-Standby\",\"Tara Stiles\",\"breathing\",\"computer standby\"]],[\"18-quad-all--quadruped_robot_3d_model1\",\"meditation\",[\"Werner Wind0wn\",\"Dylan Werner\",\"wind-down practice\",\"alphanumeric standby mode\"]],[\"18-quad-all--quadruped_robot_3d_model\",\"meditation\",[\"Yee Holo-Om\",\"Rodney Yee\",\"mindful movement\",\"hologram\"]],[\"18-quad-all--robotic_dog_3d_model\",\"meditation\",[\"Budig-ital Breath\",\"Kathryn Budig\",\"breathing\",\"digital\"]],[\"12-seedpod-snailslug--stylized_slug_3d_model\",\"meditation\",[\"H0F Hold-Circuit\",\"Wim Hof\",\"breath hold\",\"circuit and model code\"],[\"Ohm-Strom Still\",\"Max Strom\",\"stillness\",\"electrical resistance\"]],[\"12-seedpod-snailslug--stylized_worm_3d_model\",\"meditation\",[\"Pilat-OS Calm-Circuit\",\"Joseph Pilates\",\"mindful breathing\",\"operating system circuit\"]],[\"14-chisel-spire--low_poly_robot_3d_model\",\"meditation\",[\"Rea-Spire Relay\",\"Shiva Rea\",\"respiration\",\"air relay\"]],[\"01-seed-pearo--3d_character_model\",\"meditation\",[\"McKe-O2 Nose-Cone\",\"Patrick McKeown\",\"nasal breathing\",\"rocket nose cone\"],[\"Vertu-al Vent\",\"Shona Vertue\",\"ventilation\",\"virtual reality\"]],[\"23-blob-texture-bodies--blob_creature_3d_model\",\"meditation\",[\"Blob 1 \\xB7 H0F Hold-Drive\",\"Wim Hof\",\"breath hold\",\"air drive and model code\"]],[\"01-seed-pearo--blank_humanoid_figure_3d_model\",\"meditation\",[\"Corn Kernel Om\",\"Seane Corn\",\"meditation\",\"computing kernel\"],[\"Kino-Kinetic Breath\",\"Kino MacGregor\",\"breathing\",\"kinematics\"]]],Ca=Object.freeze(Object.fromEntries(Ht.map(([e,a,...t])=>[e===\"myr5\"?e:`roster/${e}`,Object.freeze({track:a,names:Object.freeze(t.map(([r,s,o,c])=>Object.freeze({name:r,person:s,workout:o,scifi:c})))})])));var Ts=Object.freeze(Object.fromEntries(Object.entries(Ca).map(([e,a])=>[e,Object.freeze(a.names.map(t=>t.name))])));function Wt(e){let a=Ca[e];if(!a)throw RangeError(`No coach name for ${e}.`);return a}var q=(e,a=!1)=>{let t=Wt(e).names;return t[a?1:0]?.name??t[0].name};var $=Object.freeze({sprintRounds:5,gentleActiveSeconds:300,paceActiveSeconds:300,meditationDays:Object.freeze([3,6,9,12])}),Kt=[{id:\"myr5\",sourceLabel:\"Original MYR5\"},{id:\"roster/01-seed-pearo--3d_character_model\",sourceLabel:\"Seed \\xB7 Pearo 1\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 2\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",sourceLabel:\"Seed \\xB7 Pearo 5\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",sourceLabel:\"Taper \\xB7 Tallstalk 1\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",sourceLabel:\"Taper \\xB7 Tallstalk 2\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",sourceLabel:\"Taper \\xB7 Tallstalk 3\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 1\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--circle_center\",sourceLabel:\"Pearl \\xB7 Orb 2\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 3\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 4\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",sourceLabel:\"Pearl \\xB7 Orb 5\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",sourceLabel:\"Pearl \\xB7 Orb 6\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",sourceLabel:\"Crest \\xB7 Wedge 1\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",sourceLabel:\"Crest \\xB7 Wedge 2\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",sourceLabel:\"Slope \\xB7 Bobble 1\",group:\"Slope \\xB7 Bobble\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 1\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",sourceLabel:\"Ridge \\xB7 Triad 2\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 1\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",sourceLabel:\"Bulb \\xB7 Sphereling 2\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",sourceLabel:\"Shard \\xB7 Asym 1\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--robot_3d_model4\",sourceLabel:\"Shard \\xB7 Asym 2\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",sourceLabel:\"Shard \\xB7 Asym 3\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",sourceLabel:\"Shard \\xB7 Asym 4\",group:\"Shard \\xB7 Asym\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",sourceLabel:\"Monolith \\xB7 Tanka 1\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 2\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 3\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",sourceLabel:\"Monolith \\xB7 Tanka 5\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",sourceLabel:\"Monolith \\xB7 Tanka 6\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 1\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",sourceLabel:\"Petal \\xB7 Wisp 2\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",sourceLabel:\"Petal \\xB7 Wisp 3\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",sourceLabel:\"Anvil \\xB7 Cask 1\",group:\"Anvil \\xB7 Cask\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 1\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",sourceLabel:\"Seedpod \\xB7 Snailslug 2\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/13-fan-split--stylized_3d_character2\",sourceLabel:\"Fan \\xB7 Split 1\",group:\"Fan \\xB7 Split\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",sourceLabel:\"Fan \\xB7 Split 2\",group:\"Fan \\xB7 Split\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 1\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",sourceLabel:\"Chisel \\xB7 Spire 2\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",sourceLabel:\"Orbital \\xB7 Coili 1\",group:\"Orbital \\xB7 Coili\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",sourceLabel:\"Spade \\xB7 Arch 1\",group:\"Spade \\xB7 Arch\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",sourceLabel:\"Spade \\xB7 Arch 2\",group:\"Spade \\xB7 Arch\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",sourceLabel:\"Shellcap \\xB7 Manyarm 1\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 2\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",sourceLabel:\"Shellcap \\xB7 Manyarm 3\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",sourceLabel:\"Four-legged 1\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",sourceLabel:\"Four-legged 2\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",sourceLabel:\"Four-legged 3\",group:\"Four-legged\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",sourceLabel:\"Four-legged 4\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",sourceLabel:\"Four-legged 5\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",sourceLabel:\"Four-legged 6\",group:\"Four-legged\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",sourceLabel:\"Four-legged 7\",group:\"Four-legged\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",sourceLabel:\"Four-legged 9\",group:\"Four-legged\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",sourceLabel:\"Genie \\xB7 Multi 1\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",sourceLabel:\"Genie \\xB7 Multi 2\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",sourceLabel:\"Genie \\xB7 Multi 3\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",sourceLabel:\"Genie \\xB7 Multi 4\",group:\"Genie \\xB7 Multi\"},{id:\"roster/20-lume--robotic_figure_3d_model\",sourceLabel:\"Lume 1\",group:\"Lume\"},{id:\"roster/22-curve--stylized_alien_3d_model\",sourceLabel:\"Curve 1\",group:\"Curve\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",sourceLabel:\"Curve 2\",group:\"Curve\"},{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",sourceLabel:\"Blob 1\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",sourceLabel:\"Blob 2\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",sourceLabel:\"Blob 3\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",sourceLabel:\"Blob 4\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",sourceLabel:\"Blob 5\",group:\"Blob\"}],F=Kt.map(e=>({...e,label:q(e.id)})),Yt=[{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",tracks:[\"meditation\"],difficulty:\"easy\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",tracks:[\"meditation\"],difficulty:\"hard\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",tracks:[\"meditation\"],difficulty:\"expert\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",tracks:[\"cardio\"],difficulty:\"easy\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/20-lume--robotic_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"expert\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",tracks:[\"arms\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",tracks:[\"arms\"],difficulty:\"medium\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",tracks:[\"arms\"],difficulty:\"hard\"},{id:\"roster/08-shard-asym--robot_3d_model4\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",tracks:[\"chest\",\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",tracks:[\"chest\"],difficulty:\"expert\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",tracks:[\"glutes\"],difficulty:\"hard\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",tracks:[\"glutes\"],difficulty:\"expert\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",tracks:[\"quads\"],difficulty:\"easy\"},{id:\"roster/13-fan-split--stylized_3d_character2\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"easy\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"medium\"},{id:\"roster/22-curve--stylized_alien_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",tracks:[\"yoga\"],difficulty:\"expert\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"easy\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",tracks:[\"martial-arts\"],difficulty:\"medium\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",tracks:[\"martial-arts\"],difficulty:\"hard\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",tracks:[\"martial-arts\"],difficulty:\"expert\"},{id:\"roster/01-seed-pearo--3d_character_model\",tracks:[\"meditation\"],difficulty:\"medium\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",tracks:[\"yoga\"],difficulty:\"hard\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",tracks:[\"cardio\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",tracks:[\"quads\"],difficulty:\"hard\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",tracks:[\"quads\"],difficulty:\"expert\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",tracks:[\"glutes\"],difficulty:\"easy\"},{id:\"roster/03-pearl-orb-ring--circle_center\",tracks:[\"glutes\"],difficulty:\"medium\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",tracks:[\"chest\"],difficulty:\"easy\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",tracks:[\"cardio\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",tracks:[\"chest\"],difficulty:\"medium\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",tracks:[\"chest\"],difficulty:\"hard\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",tracks:[\"arms\"],difficulty:\"expert\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",tracks:[\"quads\"],difficulty:\"medium\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",tracks:[\"glutes\"],difficulty:\"hard\"}],Xt={chest:[\"chest\"],quads:[\"legs\"],glutes:[\"hips\"],arms:[\"shoulders\"],yoga:[\"core\",\"balance\",\"yoga\"],\"martial-arts\":[\"stances\",\"boxing\"],cardio:[\"cardio\"],meditation:[\"meditation\"]},be=Object.freeze([\"chest\",\"quads\",\"glutes\",\"arms-shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),Vt=Object.freeze({arms:\"arms-shoulders\"}),Jt=e=>e===\"arms-shoulders\"?\"arms\":e,$e=e=>[...new Set((Array.isArray(e)?e:[]).map(a=>Vt[a]??a).filter(a=>be.includes(a)))];var M=Object.freeze([\"myr5\",\"roster/23-blob-texture-bodies--blob_creature_3d_model\"]);function je(e){if(e===\"plank-flight\")return\"easy\";let a=A[e];if(!a)return\"easy\";let t=qe[a.group].filter(r=>r.kind===a.kind);return[\"easy\",\"medium\",\"hard\",\"expert\"][Math.min(3,Math.floor(t.indexOf(a)*4/t.length))]}var B=Yt.filter(e=>!M.includes(e.id)).map(e=>({...e,name:q(e.id),groups:e.tracks.flatMap(a=>Xt[a])})),ke=Object.freeze(Object.fromEntries(be.map(e=>[e,B.find(a=>a.tracks[0]===Jt(e)&&a.difficulty===\"easy\").id]))),we=e=>$e(e).map(a=>ke[a]),He=[{id:\"supportive\",group:\"legs\",kind:\"hold\"},{id:\"direct\",group:\"legs\",kind:\"reps\"},{id:\"analytical\",group:\"chest\",kind:\"reps\"},{id:\"playful\",group:\"stances\",kind:\"hold\"},{id:\"calm\",group:\"yoga\",kind:\"hold\"},{id:\"mom\",group:\"hips\",kind:\"reps\"}];function va(e,a){return B.filter(t=>t.groups.includes(e)&&t.difficulty===a)}Se();var xe=Object.freeze([\"easy\",\"medium\",\"hard\",\"expert\"]),Fa=e=>Object.freeze(e.map(a=>Object.freeze(a.map(t=>t*10)))),Gs=Fa([[4,8,16],[8,12,20],[12,16,24],[16,20,28]]),Us=Fa([[1,2,4],[2,3,5],[3,4,6],[4,5,7]]);var Ea=600;var ar=15;var tr=Object.freeze({uncommon:1,rare:2,legendary:3,secret:3}),rr=Object.freeze({uncommon:Object.freeze({color:90,\"64-bit\":7,texture:3}),rare:Object.freeze({color:80,\"64-bit\":15,texture:5}),legendary:Object.freeze({color:70,\"64-bit\":20,texture:10}),secret:Object.freeze({color:60,\"64-bit\":20,texture:20})}),sr=2,Xe=500*10,J=Object.freeze({sessions:120,activeMinutes:30,expertXpPerMinute:280,meditationMultiplier:sr,trioBonus:Xe}),or=J.sessions*(J.activeMinutes*J.expertXpPerMinute*J.meditationMultiplier+J.trioBonus),qs=J.expertXpPerMinute*ar/60;var Ma=250,Ye=Ma-1,Aa=40,cr=or/10,D=Object.freeze(Array.from({length:Ma},(e,a)=>10*(Aa*a+Math.floor((cr-Aa*Ye)*a*(a-1)/(Ye*(Ye-1))))));function La(e,a){if(!Number.isFinite(e)||e<0)throw RangeError(`Invalid ${a}.`);return e}function Oa(e,a){if(La(a,\"performance\"),e===\"hold\")return{weapon1:a>=60,weapon2:a>=180,coach:a>=300,golden:a>=Ea,special:a>=Ea};if(e===\"reps\")return{weapon1:a>=8,weapon2:a>=12,coach:a>=15,golden:!1,special:!1};throw RangeError(\"Unknown performance kind.\")}function Ia(e){La(e,\"XP\");let a=0;for(;a<D.length-1&&e>=D[a+1];)a++;let t=a===D.length-1;return{level:a+1,xp:e,floor:D[a],next:t?null:D[a+1],into:t?0:e-D[a],need:t?0:D[a+1]-D[a],max:t}}var Ta=\"myr5-performance-progress-v2\";function v(e=globalThis.localStorage,a=globalThis.myr5AuthenticatedAccount){let t=a?.user?.id;if(t)return`account:${t}${a.dataEpoch!=null?`:${a.dataEpoch}`:\"\"}`;if(!e)return\"guest:unavailable\";let r=\"myr5-local-guest-owner-v1\";try{let s=e.getItem(r);return s||(s=globalThis.crypto?.randomUUID?.()||`guest-${Date.now()}-${Math.random()}`,e.setItem(r,s)),`guest:${s}`}catch{return\"guest:unavailable\"}}var Ce=3,Pa=()=>({version:2,accessVersion:Ce,xpScale:10,paths:[],sessions:{},days:{},coaches:[...M],goldenCoaches:[],weapons:{},ships:[],completions:{},totalXp:0,excludedCoaches:[]});function ir(e={}){let a=Object.hasOwn(e,\"storage\")?e.storage:globalThis.localStorage,t=Object.hasOwn(e,\"account\")?e.account:globalThis.myr5AuthenticatedAccount;return{storage:a,owner:e.owner??v(a,t)}}var N=e=>e&&typeof e==\"object\"&&!Array.isArray(e),ue=e=>Number.isFinite(e)&&e>=0,Ve=e=>typeof e==\"string\"&&/^\\d{4}-\\d{2}-\\d{2}$/.test(e)&&new Date(`${e}T12:00:00Z`).toISOString().slice(0,10)===e,lr=e=>Array.isArray(e)&&(e.length===0||e.length===2&&new Set(e).size===2&&e.every(a=>be.includes(a)));function dr(e){return e?.version===2&&(e.accessVersion===void 0||e.accessVersion===Ce)&&(e.xpScale===void 0||e.xpScale===10)&&(e.paths===void 0||lr(e.paths))&&(e.activities===void 0||N(e.activities)&&Object.values(e.activities).every(a=>N(a)&&[\"meditation\",\"food\"].includes(a.kind)&&Ve(a.day)))&&N(e.sessions)&&N(e.days)&&N(e.weapons)&&N(e.completions)&&Array.isArray(e.coaches)&&e.coaches.every(a=>F.some(t=>t.id===a)||R.includes(a))&&new Set(e.coaches).size===e.coaches.length&&Array.isArray(e.goldenCoaches)&&e.goldenCoaches.every(a=>e.coaches.includes(a))&&Array.isArray(e.ships)&&e.ships.every(a=>He.some(t=>t.id===a))&&Object.values(e.weapons).every(a=>Number.isInteger(a)&&a>=0&&a<=20)&&Object.values(e.completions).every(a=>Number.isSafeInteger(a)&&a>=0)&&Object.entries(e.days).every(([a,t])=>Ve(a)&&N(t)&&ue(t.workoutXp)&&[\"workout\",\"meditation\",\"food\"].every(r=>typeof t[r]==\"boolean\"))&&Object.values(e.sessions).every(a=>N(a)&&Ve(a.day)&&ue(a.xp)&&ue(a.value)&&ue(a.continuous))}var Ba={sprint:$.sprintRounds,gentle:$.gentleActiveSeconds,pace:$.paceActiveSeconds};function br(e){let a=new Set(M),t=(s,o)=>{for(let c of va(s,o))a.add(c.id)};for(let s of Object.values(e.sessions))if(s.kind===\"hold\"||s.kind===\"reps\")for(let[o,c]of s.kind===\"hold\"?Object.entries(s.perDifficultyContinuous??{[s.difficulty]:s.continuous}):[[s.difficulty,s.value]])ue(c)&&Oa(s.kind,c).coach&&t(s.group,o);else s.kind in Ba&&s.value>=Ba[s.kind]&&t(s.group,s.difficulty);let r=Object.values(e.days).filter(s=>s.meditation).length;return xe.forEach((s,o)=>{r>=$.meditationDays[o]&&t(\"meditation\",s)}),a}function ur(e){if(e.xpScale!==10){for(let a of Object.values(e.days))a.workoutXp*=10;for(let a of Object.values(e.sessions))a.xp*=10;e.xpScale=10}e.excludedCoaches??=[];for(let a of R)e.excludedCoaches.includes(a)||e.excludedCoaches.push(a);if(e.paths??=[],e.accessVersion!==Ce){let a=new Set([...br(e),...we(e.paths)]);e.coaches=F.map(t=>t.id).filter(t=>a.has(t)),e.accessVersion=Ce}e.coaches=e.coaches.filter(a=>!R.includes(a));for(let a of M)mr(e.coaches,a);return e.goldenCoaches=e.goldenCoaches.filter(a=>e.coaches.includes(a)),e}function E(e={}){try{let{storage:a,owner:t}=ir(e),r=JSON.parse(a?.getItem(t===\"server-derivation\"?`${Ta}/${t}`:C(`${Ta}/${t}`,a))||\"null\");return dr(r)?fr(ur(r)):Pa()}catch{return Pa()}}function fr(e){return e.totalXp=Object.values(e.days).reduce((a,t)=>a+t.workoutXp*(t.meditation?2:1)+(t.workout&&t.meditation&&t.food?Xe:0),0)+(Number.isFinite(e.worldBonusXp)&&e.worldBonusXp>=0?e.worldBonusXp:0),e}var mr=(e,a)=>{e.includes(a)||e.push(a)};function Je(e){let a=Object.values(e?.days??{}),t=Object.keys(e?.sessions??{}).length>0||a.some(o=>o.workout),r=a.some(o=>o.meditation),s=a.some(o=>o.food);return{workout:t,meditation:r,food:s,done:t&&r&&s}}var Qe=(e={})=>{let a=E(e);return Je(a).done?F.map(t=>t.id).filter(t=>!R.includes(t)):a.coaches},ve=(e,a={})=>Qe(a).includes(e),Re=(e,a={})=>E(a).goldenCoaches.includes(e);var Ze={single:{label:\"One cyclops eye\",eyes:[[0,2.1575,.55,.605,0]]},horizontal:{label:\"Horizontal pair\",eyes:[[-.34,2.25,.49,.32,0],[.34,2.25,.49,.32,0]]},vertical:{label:\"Vertical pair\",eyes:[[0,2.03,.49,.32,0],[0,2.73,.39,.31,0]]},triangle:{label:\"Triangle\",eyes:[[-.33,2.17,.49,.3,0],[.33,2.17,.49,.3,0],[0,2.8,.36,.28,0]]},spider:{label:\"Spider \\xB7 two big + two small\",eyes:[[-.34,2.16,.49,.32,0],[.34,2.16,.49,.32,0],[-.235,2.77,.375,.2,0],[.235,2.77,.375,.2,0]]},square:{label:\"Four small \\xB7 square\",eyes:[[-.285,2.16,.49,.25,0],[.285,2.16,.49,.25,0],[-.285,2.73,.375,.25,0],[.285,2.73,.375,.25,0]]}};var Ee=[{id:\"supportive\",name:\"Supportive\",tone:\"Warm, patient, encouraging.\",approach:\"Acknowledge effort. Offer one manageable next step. Celebrate progress without pressure.\",lines:{start:\"We can start small. What is one thing you could do in the next five minutes?\",stuck:\"You do not have to solve everything at once. Let\\u2019s make the next step smaller.\",missed:\"One missed goal does not erase your progress. What would make trying again easier?\",win:\"You followed through. Take a moment to notice what helped you do that.\"}},{id:\"direct\",name:\"Direct\",tone:\"Clear, firm, practical.\",approach:\"Be concise. Turn vague intentions into a specific next action. Hold the user accountable without insults.\",lines:{start:\"Pick one task. Decide what finished looks like. Begin with the first step.\",stuck:\"Name the obstacle. Choose one action that is still under your control.\",missed:\"Review what got in the way, adjust the plan, and choose your next attempt.\",win:\"You did what you said you would. Keep the part of the plan that worked.\"}},{id:\"analytical\",name:\"Analytical\",tone:\"Methodical, curious, precise.\",approach:\"Ask about constraints. Break goals into observable steps. Treat setbacks as information, not character flaws.\",lines:{start:\"What is the goal, what time do you have, and what is the smallest useful result?\",stuck:\"Is the obstacle time, clarity, energy, or resources? Let\\u2019s change one variable.\",missed:\"Compare the plan with what happened. Which assumption needs updating?\",win:\"What specifically helped? Record it so you can repeat the conditions.\"}},{id:\"playful\",name:\"Playful\",tone:\"Mischievous, upbeat, lightly teasing.\",approach:\"Use small quests and creature humor. Keep encouragement specific. Never shame the user.\",lines:{start:\"Tiny quest unlocked: defeat the first five minutes. The giant task can wait its turn.\",stuck:\"This boss fight needs a smaller boss. What can we turn into a side quest?\",missed:\"The quest log has been updated, not destroyed. Pick your next checkpoint.\",win:\"Achievement unlocked: actually doing the thing. My one eye is impressed.\"}},{id:\"calm\",name:\"Calm\",tone:\"Steady, spacious, grounded.\",approach:\"Use short, unhurried language. Leave room for the user\\u2019s choices. Focus on the present next step.\",lines:{start:\"There is no need to rush the whole plan. Choose one thing to begin.\",stuck:\"Pause for a moment. What feels possible from where you are right now?\",missed:\"That attempt is over. You can choose a gentler, more workable next one.\",win:\"Notice this moment. You made progress, and you can let that be enough for now.\"}},{id:\"mom\",name:\"MOM Inc.\",tone:\"Polite, theatrical, dry corporate humor.\",approach:\"Use fictional MOM Inc. bureaucracy and understated absurdity. Keep choices voluntary and practical.\",lines:{start:\"MOM Inc. has approved one manageable task. Please select yours; the paperwork can wait.\",stuck:\"Your obstacle has been escalated to the Department of Smaller Steps. What can we simplify?\",missed:\"No disciplinary forms today. We are revising the plan, not your worth.\",win:\"Progress officially recorded. Please accept one highly regulated moment of pride.\"}}];var ea=[[\"round\",\"Round\"],[\"vertical\",\"Vertical slit\"],[\"horizontal\",\"Horizontal slit\"],[\"oval\",\"Oval\"],[\"diamond\",\"Diamond\"],[\"star\",\"Star\"],[\"heart\",\"Heart\"],[\"cross\",\"Cross\"]];var hr=[{id:\"nails\",label:\"Nails\",short:\"Nails\",description:\"Five claw and nail forms\"},{id:\"fingertips\",label:\"Finger tips\",short:\"Tips\",description:\"Distal pads and final phalanges\"},{id:\"fingers\",label:\"Fingers & knuckles\",short:\"Fingers\",description:\"Joined finger shafts, bases and knuckles\"},{id:\"palm\",label:\"Palm\",short:\"Palm\",description:\"The inner hand surface\"},{id:\"back_of_hand\",label:\"Back of hand\",short:\"Back\",description:\"The dorsal hand surface\"},{id:\"wrist\",label:\"Wrist\",short:\"Wrist\",description:\"Stump, cuff and transition\"}],j=[{id:0,name:\"Mortal\",realm:\"Baseline \\xB7 Earth\",primary:\"#b9856f\",secondary:\"#714c42\",accent:\"#f0c0a3\",emissive:\"#000000\",roughness:.72,metalness:.02,detail:\"clean\"},{id:1,name:\"Verdant\",realm:\"Wildroot \\xB7 Dimension 12\",primary:\"#3f7448\",secondary:\"#1e3b2b\",accent:\"#b8dd6e\",emissive:\"#172d13\",roughness:.88,metalness:0,detail:\"vine\"},{id:2,name:\"Mycelial\",realm:\"Sporesea \\xB7 Dimension 09\",primary:\"#d5c7ae\",secondary:\"#776b82\",accent:\"#f59ec4\",emissive:\"#6d234a\",roughness:.92,metalness:0,detail:\"caps\"},{id:3,name:\"Chitin\",realm:\"Carapace \\xB7 Dimension 31\",primary:\"#4f263d\",secondary:\"#180f20\",accent:\"#d17663\",emissive:\"#240716\",roughness:.3,metalness:.28,detail:\"plates\"},{id:4,name:\"Reptilian\",realm:\"Scalehold \\xB7 Dimension 04\",primary:\"#5f7a3f\",secondary:\"#283621\",accent:\"#cfb85c\",emissive:\"#101c08\",roughness:.76,metalness:.04,detail:\"scales\"},{id:5,name:\"Abyssal\",realm:\"Deep Trench \\xB7 Dimension 88\",primary:\"#132a42\",secondary:\"#06111f\",accent:\"#4edfd3\",emissive:\"#0b756f\",roughness:.34,metalness:.12,detail:\"spines\"},{id:6,name:\"Coral\",realm:\"Living Reef \\xB7 Dimension 22\",primary:\"#e46867\",secondary:\"#70344c\",accent:\"#ffd27a\",emissive:\"#8b2438\",roughness:.83,metalness:0,detail:\"coral\"},{id:7,name:\"Skeletal\",realm:\"Ossuary \\xB7 Dimension 00\",primary:\"#d8d0af\",secondary:\"#80785f\",accent:\"#fff7d1\",emissive:\"#17160e\",roughness:.9,metalness:0,detail:\"bone\"},{id:8,name:\"Spectral\",realm:\"Veil \\xB7 Dimension 13\",primary:\"#8ed9cd\",secondary:\"#294c58\",accent:\"#d5fff8\",emissive:\"#248e86\",roughness:.18,metalness:.04,detail:\"mist\"},{id:9,name:\"Infernal\",realm:\"Cinder Court \\xB7 Dimension 66\",primary:\"#7b211e\",secondary:\"#260b0a\",accent:\"#ff8a2a\",emissive:\"#d33812\",roughness:.58,metalness:.1,detail:\"flame\"},{id:10,name:\"Celestial\",realm:\"High Orbit \\xB7 Dimension 07\",primary:\"#e6dca4\",secondary:\"#786da7\",accent:\"#fffbd7\",emissive:\"#8d74d6\",roughness:.24,metalness:.5,detail:\"halo\"},{id:11,name:\"Voidborn\",realm:\"Null Expanse \\xB7 Dimension \\u2205\",primary:\"#16101f\",secondary:\"#060409\",accent:\"#9f64ff\",emissive:\"#482285\",roughness:.16,metalness:.36,detail:\"void\"},{id:12,name:\"Eldritch\",realm:\"Watcher Fold \\xB7 Dimension 47\",primary:\"#6b426c\",secondary:\"#252038\",accent:\"#b6ef63\",emissive:\"#4e7628\",roughness:.64,metalness:.04,detail:\"eyes\"},{id:13,name:\"Stone Golem\",realm:\"Deep Strata \\xB7 Dimension 19\",primary:\"#6f7068\",secondary:\"#393b37\",accent:\"#b7a37d\",emissive:\"#171811\",roughness:.98,metalness:0,detail:\"rock\"},{id:14,name:\"Crystal\",realm:\"Prism Vault \\xB7 Dimension 52\",primary:\"#896bc2\",secondary:\"#302e66\",accent:\"#e0c8ff\",emissive:\"#5a3da8\",roughness:.12,metalness:.3,detail:\"crystal\"},{id:15,name:\"Magma\",realm:\"Mantle \\xB7 Dimension 03\",primary:\"#4a1713\",secondary:\"#140706\",accent:\"#ffb22f\",emissive:\"#e44712\",roughness:.78,metalness:.06,detail:\"magma\"},{id:16,name:\"Glacial\",realm:\"White Silence \\xB7 Dimension 71\",primary:\"#93c9df\",secondary:\"#315a76\",accent:\"#ebfdff\",emissive:\"#3c93bb\",roughness:.2,metalness:.14,detail:\"ice\"},{id:17,name:\"Stormcharged\",realm:\"Tempest Ring \\xB7 Dimension 28\",primary:\"#48536e\",secondary:\"#1a2236\",accent:\"#c3f7ff\",emissive:\"#3ebde0\",roughness:.4,metalness:.32,detail:\"storm\"},{id:18,name:\"Clockwork Robot\",realm:\"Brass Meridian \\xB7 Dimension 14\",primary:\"#9a6a37\",secondary:\"#33251d\",accent:\"#efd08c\",emissive:\"#5b2c12\",roughness:.32,metalness:.82,detail:\"gears\"},{id:19,name:\"Neon Synth\",realm:\"Aftergrid \\xB7 Dimension 20\",primary:\"#251d54\",secondary:\"#0b0921\",accent:\"#ff5bd7\",emissive:\"#b519ac\",roughness:.22,metalness:.55,detail:\"neon\"},{id:20,name:\"Fluffy\",realm:\"Cloudburrow \\xB7 Soft forms\",primary:\"#d6a987\",secondary:\"#bb8969\",accent:\"#ffe4c3\",emissive:\"#000000\",roughness:.88,metalness:0,detail:\"fur\"},{id:21,name:\"Jelly\",realm:\"Lime Lagoon \\xB7 Soft forms\",primary:\"#a7ec69\",secondary:\"#f1ffd4\",accent:\"#c9ff90\",emissive:\"#000000\",roughness:.13,metalness:0,detail:\"jelly\"},{id:22,name:\"Baby\",realm:\"Little One \\xB7 Soft forms\",primary:\"#efb398\",secondary:\"#d68e7c\",accent:\"#ffe2d5\",emissive:\"#000000\",roughness:.52,metalness:0,detail:\"baby\"}];var so=j.map(e=>({name:e.name,description:`A pure ${e.name.toLowerCase()} hand`,values:Object.fromEntries(hr.map(a=>[a.id,e.id]))})),oo=j.filter(e=>![8,11].includes(e.id));var pr=[{id:\"roster/01-seed-pearo--3d_character_model\",label:\"Seed \\xB7 Pearo 1\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--cute_alien_figure_3d_model\",label:\"Seed \\xB7 Pearo 2\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--white_3d_character_model\",label:\"Seed \\xB7 Pearo 3\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_humanoid_figure_3d_model\",label:\"Seed \\xB7 Pearo 4\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/01-seed-pearo--blank_toy_figure_3d_model\",label:\"Seed \\xB7 Pearo 5\",group:\"Seed \\xB7 Pearo\"},{id:\"roster/02-taper-tallstalk--3d_humanoid_figure\",label:\"Taper \\xB7 Tallstalk 1\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",label:\"Taper \\xB7 Tallstalk 2\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/02-taper-tallstalk--white_humanoid_doll_3d_model\",label:\"Taper \\xB7 Tallstalk 3\",group:\"Taper \\xB7 Tallstalk\"},{id:\"roster/03-pearl-orb-ring--abstract_humanoid_3d_model\",label:\"Pearl \\xB7 Orb 1\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--circle_center\",label:\"Pearl \\xB7 Orb 2\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",label:\"Pearl \\xB7 Orb 3\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--robot_3d_model\",label:\"Pearl \\xB7 Orb 4\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_3d_character1\",label:\"Pearl \\xB7 Orb 5\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",label:\"Pearl \\xB7 Orb 6\",group:\"Pearl \\xB7 Orb\"},{id:\"roster/04-crest-wedge--robot_creature_3d_model\",label:\"Crest \\xB7 Wedge 1\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/04-crest-wedge--stylized_3d_character\",label:\"Crest \\xB7 Wedge 2\",group:\"Crest \\xB7 Wedge\"},{id:\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",label:\"Slope \\xB7 Bobble 1\",group:\"Slope \\xB7 Bobble\"},{id:\"roster/06-ridge-triad--geometric_robot_3d_model1\",label:\"Ridge \\xB7 Triad 1\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/06-ridge-triad--robot_3d_model1\",label:\"Ridge \\xB7 Triad 2\",group:\"Ridge \\xB7 Triad\"},{id:\"roster/07-bulb-sphereling--cute_robot_3d_model\",label:\"Bulb \\xB7 Sphereling 1\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",label:\"Bulb \\xB7 Sphereling 2\",group:\"Bulb \\xB7 Sphereling\"},{id:\"roster/08-shard-asym--geometric_robot_3d_model\",label:\"Shard \\xB7 Asym 1\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--robot_3d_model4\",label:\"Shard \\xB7 Asym 2\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--fantasy_creature_3d_model2\",label:\"Shard \\xB7 Asym 3\",group:\"Shard \\xB7 Asym\"},{id:\"roster/08-shard-asym--stylized_action_figure_3d_model\",label:\"Shard \\xB7 Asym 4\",group:\"Shard \\xB7 Asym\"},{id:\"roster/09-monolith-tanka--3d_robot_model\",label:\"Monolith \\xB7 Tanka 1\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",label:\"Monolith \\xB7 Tanka 2\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--mini_robot_3d_model\",label:\"Monolith \\xB7 Tanka 3\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model2\",label:\"Monolith \\xB7 Tanka 4\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--robot_3d_model3\",label:\"Monolith \\xB7 Tanka 5\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/09-monolith-tanka--white_horned_robot_3d_model\",label:\"Monolith \\xB7 Tanka 6\",group:\"Monolith \\xB7 Tanka\"},{id:\"roster/10-petal-wisp--stylized_creature_3d_model\",label:\"Petal \\xB7 Wisp 1\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--ghost_character_3d_model\",label:\"Petal \\xB7 Wisp 2\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/10-petal-wisp--fantasy_creature_3d_model4\",label:\"Petal \\xB7 Wisp 3\",group:\"Petal \\xB7 Wisp\"},{id:\"roster/11-anvil-cask--clay-style_robot_3d_model\",label:\"Anvil \\xB7 Cask 1\",group:\"Anvil \\xB7 Cask\"},{id:\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",label:\"Seedpod \\xB7 Snailslug 1\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",label:\"Seedpod \\xB7 Snailslug 2\",group:\"Seedpod \\xB7 Snailslug\"},{id:\"roster/13-fan-split--stylized_3d_character2\",label:\"Fan \\xB7 Split 1\",group:\"Fan \\xB7 Split\"},{id:\"roster/13-fan-split--stylized_toy_3d_model\",label:\"Fan \\xB7 Split 2\",group:\"Fan \\xB7 Split\"},{id:\"roster/14-chisel-spire--cone_head_3d_model\",label:\"Chisel \\xB7 Spire 1\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/14-chisel-spire--low_poly_robot_3d_model\",label:\"Chisel \\xB7 Spire 2\",group:\"Chisel \\xB7 Spire\"},{id:\"roster/15-orbital-coili--robot_character_3d_model\",label:\"Orbital \\xB7 Coili 1\",group:\"Orbital \\xB7 Coili\"},{id:\"roster/16-spade-arch--pyramid_head_figure_3d_model\",label:\"Spade \\xB7 Arch 1\",group:\"Spade \\xB7 Arch\"},{id:\"roster/16-spade-arch--stylized_humanoid_3d_model\",label:\"Spade \\xB7 Arch 2\",group:\"Spade \\xB7 Arch\"},{id:\"roster/17-shellcap-manyarm--3d_character_figurine\",label:\"Shellcap \\xB7 Manyarm 1\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",label:\"Shellcap \\xB7 Manyarm 2\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",label:\"Shellcap \\xB7 Manyarm 3\",group:\"Shellcap \\xB7 Manyarm\"},{id:\"roster/18-quad-all--dragon_creature_3d_model\",label:\"Four-legged 1\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model1\",label:\"Four-legged 2\",group:\"Four-legged\"},{id:\"roster/18-quad-all--fantasy_creature_3d_model3\",label:\"Four-legged 3\",group:\"Four-legged\"},{id:\"roster/18-quad-all--four-legged_robot_3d_model\",label:\"Four-legged 4\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model1\",label:\"Four-legged 5\",group:\"Four-legged\"},{id:\"roster/18-quad-all--quadruped_robot_3d_model\",label:\"Four-legged 6\",group:\"Four-legged\"},{id:\"roster/18-quad-all--robotic_dog_3d_model\",label:\"Four-legged 7\",group:\"Four-legged\"},{id:\"roster/18-quad-all--stylized_quadruped_3d_model\",label:\"Four-legged 8\",group:\"Four-legged\"},{id:\"roster/18-quad-all--wooden_four-legged_robot_3d_model\",label:\"Four-legged 9\",group:\"Four-legged\"},{id:\"roster/19-genie-multi--fantasy_creature_3d_model\",label:\"Genie \\xB7 Multi 1\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",label:\"Genie \\xB7 Multi 2\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",label:\"Genie \\xB7 Multi 3\",group:\"Genie \\xB7 Multi\"},{id:\"roster/19-genie-multi--stylized_octopus_3d_model\",label:\"Genie \\xB7 Multi 4\",group:\"Genie \\xB7 Multi\"},{id:\"roster/20-lume--robotic_figure_3d_model\",label:\"Lume 1\",group:\"Lume\"},{id:\"roster/22-curve--stylized_alien_3d_model\",label:\"Curve 1\",group:\"Curve\"},{id:\"roster/22-curve--stylized_cartoon_figure_3d_model\",label:\"Curve 2\",group:\"Curve\"},{id:\"roster/23-blob-texture-bodies--blob_creature_3d_model\",label:\"Blob 1\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--cute_blob_creature_3d_model\",label:\"Blob 2\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",label:\"Blob 3\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",label:\"Blob 4\",group:\"Blob\"},{id:\"roster/23-blob-texture-bodies--sand_creature_3d_model\",label:\"Blob 5\",group:\"Blob\"}],gr=pr.filter(e=>!R.includes(e.id)).map(e=>({...e,sourceLabel:e.label,label:q(e.id)})),Da=gr;var fe=Object.freeze([\"clay\",\"martial-arts-canvas-gi\",\"arms-rope\",\"yoga-cork\",\"glutes-peach\"]),aa=Object.freeze({\"legacy-0\":\"clay\",\"legacy-21\":\"opal-jelly\",\"bubble-glass\":\"glitter-resin\"}),H=Object.freeze([\"flat\",\"legacy-22\",...Object.keys(aa)]),ta=e=>Object.hasOwn(aa,e)?aa[e]:e,Na=e=>{let a=ta(e);return H.includes(a)?\"clay\":a},za=e=>e===22?0:e;var yr=Object.freeze([[\"open\",\"Curious\"],[\"sleepy\",\"Unimpressed\"],[\"wide\",\"Wide awake\"],[\"anime\",\"Anime\"],[\"squinty\",\"Squinty\"],[\"bloodshot\",\"Bloodshot\"],[\"blind\",\"Blind\"]]),Ga=Object.freeze(yr.map(([e])=>e));var Ae=[\"head\",\"eye\",\"collar\",\"body\",\"arms\",\"feet\"],qa={body:\"body\",arms:\"body\",feet:\"body\",collar:\"body\",head:\"body\",eye:\"eye\"};var z=[{...j[0],name:\"Original MYR5\",primary:\"#7946aa\",secondary:\"#351344\",accent:\"#b373d4\",roughness:.62},...j.slice(1)],kr=z.filter(e=>![8,11,22].includes(e.id)),ra=[{id:\"myr5\",label:q(\"myr5\"),group:\"MYR5\"},...Da],sa=new Set([\"roster/01-seed-pearo--white_3d_character_model\",\"roster/01-seed-pearo--blank_humanoid_figure_3d_model\",\"roster/09-monolith-tanka--robot_3d_model2\",\"roster/18-quad-all--stylized_quadruped_3d_model\"]),wr=ra.filter(e=>!sa.has(e.id)),_r=[\"headFrom\",\"armsFrom\",\"feetFrom\"],Ua=e=>typeof e==\"string\"&&ra.some(a=>a.id===e),Sr=e=>!!e&&typeof e==\"object\"&&typeof e.textureId==\"string\"&&typeof e.colorId==\"string\"&&Number.isFinite(e.sparkle)&&e.sparkle>=0&&e.sparkle<=1&&Number.isFinite(e.metallic)&&e.metallic>=0&&e.metallic<=1,xr=e=>e===void 0||!!e&&typeof e==\"object\"&&!Array.isArray(e)&&Object.entries(e).every(([a,t])=>Ae.includes(a)&&Sr(t)),Fe=()=>({version:1,styles:{head:0,eye:0,collar:0,body:0,arms:0,feet:0},eye:\"open\",fur:1,iris:1,pupil:\"round\",pupilSize:1,detail:1,coach:\"supportive\",shipId:\"supportive\",shipColor:null,fingers:4,toes:3,eyeLayout:\"single\",body:\"myr5\",headFrom:\"myr5\",armsFrom:\"myr5\",feetFrom:\"myr5\"});function oa(e){let a=JSON.parse(e);a.pupil??=\"round\",a.pupilSize??=1,a.detail??=1,a.coach??=\"supportive\",a.shipId??=a.coach,a.shipColor??=null,a.fingers??=4,a.toes??=3,a.eyeLayout??=\"single\",Ua(a.body)||(a.body=\"myr5\");for(let r of _r)Ua(a[r])||(a[r]=a.body);let t={around:\"triangle\",frontBack:\"horizontal\"};if(t[a.eyeLayout]&&(a.eyeLayout=t[a.eyeLayout]),!Number.isInteger(a.fingers)||a.fingers<2||a.fingers>6||!Number.isInteger(a.toes)||a.toes<1||a.toes>6||!Object.hasOwn(Ze,a.eyeLayout)||a.version!==1||!a.styles||!Ae.every(r=>Number.isInteger(a.styles[r])&&a.styles[r]>=0&&a.styles[r]<z.length)||!Ga.includes(a.eye)||!Number.isFinite(a.fur)||a.fur<.65||a.fur>1.4||!Number.isFinite(a.iris)||a.iris<.7||a.iris>1.25||!ea.some(r=>r[0]===a.pupil)||!Number.isFinite(a.pupilSize)||a.pupilSize<.6||a.pupilSize>1.15||!Number.isFinite(a.detail)||a.detail<.5||a.detail>1.5||!Ee.some(r=>r.id===a.coach)||!Ee.some(r=>r.id===a.shipId)||!(a.shipColor===null||/^#[0-9a-f]{6}$/i.test(a.shipColor))||!xr(a.materials))throw Error(\"Choose a valid MYR5 recipe.\");return{golden:typeof a.golden==\"boolean\"?a.golden:void 0,...a.blackSclera===!0?{blackSclera:!0}:{},...a.colourPupil===!0?{colourPupil:!0}:{},version:1,styles:Object.fromEntries(Ae.map(r=>[r,za(a.styles[r])])),eye:a.eye,fur:a.fur,iris:a.iris,pupil:a.pupil,pupilSize:a.pupilSize,detail:a.detail,coach:a.coach,shipId:a.shipId,shipColor:a.shipColor,fingers:a.fingers,toes:a.toes,eyeLayout:a.eyeLayout,body:a.body,headFrom:a.headFrom,armsFrom:a.armsFrom,feetFrom:a.feetFrom,materials:a.materials&&Object.fromEntries(Object.entries(a.materials).map(([r,s])=>[r,{...s,textureId:Na(s.textureId),...r===\"head\"&&a.materials.body?{colorId:a.materials.body.colorId}:{}}]))}}var ca=Object.freeze([{id:\"supportive\",name:\"Bubble shuttle\",description:\"A rounded cockpit with four bulbous engine pods around a compact hull.\"},{id:\"direct\",name:\"Wing fighter\",description:\"A pointed cockpit between broad swept wings and long engine rails.\"},{id:\"analytical\",name:\"Ring-engine scout\",description:\"A narrow cockpit and small fins ahead of a large circular rear engine.\"},{id:\"playful\",name:\"Fork interceptor\",description:\"A dark central cockpit between long forked nose rails, with two side engine pods.\"},{id:\"calm\",name:\"Capsule pod\",description:\"A short rounded capsule with a forward cockpit and a thick rear housing.\"},{id:\"mom\",name:\"Cargo carrier\",description:\"A broad blocky hull with a raised centre tower and two large front engine housings.\"}].map(e=>Object.freeze({...e,assetPath:`assets/ships/${e.id}.glb`}))),na=e=>ca.find(a=>a.id===e)||ca[0];var me=[{id:\"pal-01\",name:\"Acid Forge\",tagline:\"Lime bites oxblood and chrome\",colors:[\"#C6FF00\",\"#4A0E17\",\"#C0C7CE\"],unlockRule:\"aura-milestone\",unlockAtDay:5,finish:\"glitter\"},{id:\"pal-02\",name:\"Neon Afterburn Haze\",tagline:\"Orange aqua contrails with violet flare\",colors:[\"#FF6D00\",\"#00E0B0\",\"#C800FF\"],unlockRule:\"aura-milestone\",unlockAtDay:10,finish:\"brushed-metal\"},{id:\"pal-03\",name:\"Petrel Copper\",tagline:\"Petrol depths meet intense violet\",colors:[\"#0E5A61\",\"#B87333\",\"#6913E7\"],unlockRule:\"aura-milestone\",unlockAtDay:15,finish:\"polished-metal\"},{id:\"pal-04\",name:\"Vinaigrette Rust\",tagline:\"Oil pigment rust swirl\",colors:[\"#8A3B12\",\"#556B2F\",\"#E7A213\"],unlockRule:\"aura-milestone\",unlockAtDay:20,finish:\"marbled\"},{id:\"pal-05\",name:\"Neon Relic\",tagline:\"Hot magenta neon amber on bone\",colors:[\"#FF009F\",\"#FFB500\",\"#F2EFE6\"],unlockRule:\"aura-milestone\",unlockAtDay:25,finish:\"glitter\"},{id:\"pal-06\",name:\"Glacier Ember\",tagline:\"Ice teal over ember red\",colors:[\"#13E7D1\",\"#D9381E\",\"#2B2530\"],unlockRule:\"aura-milestone\",unlockAtDay:30,finish:\"pearl\"},{id:\"pal-07\",name:\"Chartreuse Storm\",tagline:\"Sludge green meets slate violet\",colors:[\"#B5C700\",\"#3D3654\",\"#E85D75\"],unlockRule:\"aura-milestone\",unlockAtDay:35,finish:\"matte\"},{id:\"pal-08\",name:\"Saffron Voltage\",tagline:\"Saffron arcs on ink blue\",colors:[\"#FFB000\",\"#101D42\",\"#00FAA6\"],unlockRule:\"aura-milestone\",unlockAtDay:40,finish:\"polished-metal\"},{id:\"pal-09\",name:\"Neon Venom Signal\",tagline:\"Poison lime warning under arterial red\",colors:[\"#A6FF00\",\"#FF2442\",\"#101828\"],unlockRule:\"aura-milestone\",unlockAtDay:45,finish:\"watercolor\"},{id:\"pal-10\",name:\"Mercury Moss\",tagline:\"Silver moss over saturated gold\",colors:[\"#AEB6BC\",\"#5C6B3C\",\"#E7A913\"],unlockRule:\"aura-milestone\",unlockAtDay:50,finish:\"brushed-metal\"},{id:\"pal-11\",name:\"Ultraviolet Tar\",tagline:\"Violet glow on tar black\",colors:[\"#7B2FF7\",\"#141414\",\"#FFC93C\"],unlockRule:\"aura-milestone\",unlockAtDay:55,finish:\"glaze\"},{id:\"pal-12\",name:\"Salmon Circuit\",tagline:\"Salmon pink on circuit green\",colors:[\"#FA2200\",\"#00A86B\",\"#2E2A5C\"],unlockRule:\"aura-milestone\",unlockAtDay:60,finish:\"pearl\"},{id:\"pal-13\",name:\"Muddy Pop\",tagline:\"Ochre mud sparks cerulean\",colors:[\"#8C6A2F\",\"#0081C8\",\"#E71362\"],unlockRule:\"battle-pass\",reward:\"strider-2:L1\",finish:\"matte\"},{id:\"pal-14\",name:\"Neon Vapor Crown\",tagline:\"Laser pink blue over dark purple\",colors:[\"#FF00AA\",\"#00D0FF\",\"#1B0033\"],unlockRule:\"battle-pass\",reward:\"strider-2:L3\",finish:\"watercolor\"},{id:\"pal-15\",name:\"Brick Vein\",tagline:\"Brick red veined in steel\",colors:[\"#A0342B\",\"#8C9BA5\",\"#D7E713\"],unlockRule:\"battle-pass\",reward:\"strider-2:L4\",finish:\"marbled\"},{id:\"pal-16\",name:\"Peacock Static\",tagline:\"Peacock green hums gold\",colors:[\"#046A5B\",\"#FAAF00\",\"#5B1E4B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L1\",finish:\"polished-metal\"},{id:\"pal-17\",name:\"Foghorn Flame\",tagline:\"Fog gray with fire-orange siren\",colors:[\"#B8C0C2\",\"#FA3F00\",\"#123C69\"],unlockRule:\"battle-pass\",reward:\"strider-3:L3\",finish:\"brushed-metal\"},{id:\"pal-18\",name:\"Toxic Honey\",tagline:\"Honey drips on toxic lime\",colors:[\"#D9A404\",\"#9BF00B\",\"#4A154B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L4\",finish:\"glaze\"},{id:\"pal-19\",name:\"Neon Crimson Mirage\",tagline:\"Scarlet toxic lime over deep jade\",colors:[\"#FF1744\",\"#76FF03\",\"#005E3C\"],unlockRule:\"battle-pass\",reward:\"strider-4:L1\",finish:\"matte\"},{id:\"pal-20\",name:\"Opal Drain\",tagline:\"Opal sheen on drainpipe green\",colors:[\"#13E7B9\",\"#3E5641\",\"#D1495B\"],unlockRule:\"battle-pass\",reward:\"strider-4:L3\",finish:\"pearl\"},{id:\"pal-21\",name:\"Rust Halo\",tagline:\"Rust ringed by mint flash\",colors:[\"#9C4221\",\"#12E898\",\"#2C2C54\"],unlockRule:\"battle-pass\",reward:\"strider-4:L4\",finish:\"brushed-metal\"},{id:\"pal-22\",name:\"Volt Quartz\",tagline:\"Electric yellow on rose quartz\",colors:[\"#F5E900\",\"#E71363\",\"#264653\"],unlockRule:\"battle-pass\",reward:\"strider-5:L1\",finish:\"glitter\"},{id:\"pal-23\",name:\"Neon Flare Garden\",tagline:\"Laser lime pink on teal steel\",colors:[\"#8CFF00\",\"#FF006E\",\"#00B8A9\"],unlockRule:\"battle-pass\",reward:\"strider-5:L3\",finish:\"matte\"},{id:\"pal-24\",name:\"Crimson Tidebreak\",tagline:\"Crimson breaks on seafoam\",colors:[\"#B91338\",\"#13E7B5\",\"#2A1E5C\"],unlockRule:\"battle-pass\",reward:\"strider-5:L4\",finish:\"watercolor\"},{id:\"pal-25\",name:\"Brass Mirage\",tagline:\"Brass shimmer dusky plum\",colors:[\"#C59B42\",\"#5A2A52\",\"#0D94ED\"],unlockRule:\"battle-pass\",reward:\"strider-6:L1\",finish:\"polished-metal\"},{id:\"pal-26\",name:\"Neon Infrared Bloom\",tagline:\"Red heat sliced by frozen cyan\",colors:[\"#FF0000\",\"#00F5FF\",\"#5B00B5\"],unlockRule:\"battle-pass\",reward:\"strider-6:L3\",finish:\"glaze\"},{id:\"pal-27\",name:\"Ice Magma\",tagline:\"Pigmented ice over magma orange\",colors:[\"#D6F0F0\",\"#F45B0F\",\"#3D2C8D\"],unlockRule:\"battle-pass\",reward:\"strider-6:L4\",finish:\"marbled\"},{id:\"pal-28\",name:\"Fern Radio\",tagline:\"Fern green static magenta\",colors:[\"#3F7A3A\",\"#E4007C\",\"#E7BE13\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L1\",finish:\"matte\"},{id:\"pal-29\",name:\"Porcelain Fuel\",tagline:\"Porcelain white fuel orange\",colors:[\"#F2F2F0\",\"#FF7A1A\",\"#14507A\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L3\",finish:\"glaze\"},{id:\"pal-30\",name:\"Neon Photon Alley\",tagline:\"Yellow signage aqua and violet glass\",colors:[\"#FFD300\",\"#00FFCC\",\"#7A00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L4\",finish:\"brushed-metal\"},{id:\"pal-31\",name:\"Neon Solar Flare\",tagline:\"Hot yellow ember in plum space\",colors:[\"#FFEA00\",\"#FF3D00\",\"#26142B\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L1\",finish:\"polished-metal\"},{id:\"pal-32\",name:\"Wet Cement Rose\",tagline:\"Cement gray wild rose\",colors:[\"#8D9096\",\"#D84A87\",\"#F0E130\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L3\",finish:\"matte\"},{id:\"pal-33\",name:\"Neon Laser Tropics\",tagline:\"Chartreuse beams with scorched orange shadows\",colors:[\"#C6FF00\",\"#FF4D00\",\"#001219\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L4\",finish:\"watercolor\"},{id:\"pal-34\",name:\"Copper Fog\",tagline:\"Copper glows through fog\",colors:[\"#B46A30\",\"#C4CBCE\",\"#B600F5\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L1\",finish:\"brushed-metal\"},{id:\"pal-35\",name:\"Laser Moss\",tagline:\"Laser cyan on moss\",colors:[\"#00E5FF\",\"#546B2F\",\"#FF4FA3\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L3\",finish:\"glitter\"},{id:\"pal-36\",name:\"Bone Ember\",tagline:\"Bone white ember orange\",colors:[\"#EDE8DC\",\"#E4572E\",\"#BA00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L4\",finish:\"matte\"},{id:\"pal-37\",name:\"Plum Static\",tagline:\"Plum noise with electric lime\",colors:[\"#4A1E4F\",\"#B8F000\",\"#33B5C7\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L1\",finish:\"glaze\"},{id:\"pal-38\",name:\"Neon Lime Vortex\",tagline:\"Acid green swirling through electric blue\",colors:[\"#BFFF00\",\"#00E5FF\",\"#12100F\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L3\",finish:\"pearl\"},{id:\"pal-39\",name:\"Clay Command\",tagline:\"Terracotta clay cobalt order\",colors:[\"#C06B55\",\"#2E4BC6\",\"#EDE6D6\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L4\",finish:\"marbled\"},{id:\"pal-40\",name:\"Static Cream\",tagline:\"Cream noise shock green\",colors:[\"#F4EDDE\",\"#39FF14\",\"#4A306D\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L1\",finish:\"glitter\"},{id:\"pal-41\",name:\"Midnight Papaya\",tagline:\"Midnight blue papaya burst\",colors:[\"#191970\",\"#FF9E42\",\"#13E796\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L3\",finish:\"polished-metal\"},{id:\"pal-42\",name:\"Dusty Laser\",tagline:\"Dusty mauve laser green\",colors:[\"#A27E8E\",\"#7CFC00\",\"#1B3B6F\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L4\",finish:\"matte\"},{id:\"pal-43\",name:\"Neon Atomic Sunset\",tagline:\"Amber fallout glowing beside ultraviolet pink\",colors:[\"#FF9100\",\"#FF00F5\",\"#062A2A\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L1\",finish:\"glaze\"},{id:\"pal-44\",name:\"Fern Flame\",tagline:\"Cool fern fierce flame\",colors:[\"#4F7942\",\"#FF3D2E\",\"#E75A13\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L3\",finish:\"brushed-metal\"},{id:\"pal-45\",name:\"Violet Quarry\",tagline:\"Quarry gray violet eruption\",colors:[\"#7D7F87\",\"#8B3DFF\",\"#DCE35B\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L4\",finish:\"matte\"},{id:\"pal-46\",name:\"Cadmium Deep\",tagline:\"Cadmium red deep lagoon\",colors:[\"#D32E12\",\"#046E8F\",\"#E79E13\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L1\",finish:\"polished-metal\"},{id:\"pal-47\",name:\"Pigment Riot\",tagline:\"Intense blue maroon and hot orange\",colors:[\"#139FE7\",\"#6E1423\",\"#FF7300\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L3\",finish:\"watercolor\"},{id:\"pal-48\",name:\"Neon Prism Riot\",tagline:\"Sea-glass cyan, magenta bite, lemon flash\",colors:[\"#00FFC6\",\"#FF00B8\",\"#F5FF00\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L4\",finish:\"pearl\"},{id:\"pal-49\",name:\"Neon Tropic Surge\",tagline:\"Aqua tide, citrus flare, violet thunder\",colors:[\"#00FF9D\",\"#FFB000\",\"#D000FF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L1\",finish:\"glitter\"},{id:\"pal-50\",name:\"Ash Honey\",tagline:\"Ash gray golden honey\",colors:[\"#9BA0A6\",\"#E0A100\",\"#2F6690\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L3\",finish:\"matte\"},{id:\"pal-51\",name:\"Cherry Motor\",tagline:\"Cherry motor oil with electric blue\",colors:[\"#B22234\",\"#3B2F2F\",\"#005CFF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L4\",finish:\"brushed-metal\"},{id:\"pal-52\",name:\"Glacier Punch\",tagline:\"Glacier pigmented hot punch\",colors:[\"#13A6E7\",\"#F52549\",\"#3D405B\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L1\",finish:\"glaze\"},{id:\"pal-53\",name:\"Tarnish Bloom\",tagline:\"Tarnished teal floral orange\",colors:[\"#3A7D7B\",\"#F4852B\",\"#E9D6EC\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L3\",finish:\"marbled\"},{id:\"pal-54\",name:\"Umber Signal\",tagline:\"Umber earth neon signal\",colors:[\"#635147\",\"#D0FF4B\",\"#7A28C0\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L4\",finish:\"matte\"},{id:\"pal-55\",name:\"Rose Quartz Coil\",tagline:\"Soft rose coil of teal\",colors:[\"#E7134B\",\"#0FA3A3\",\"#2B2118\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L1\",finish:\"pearl\"},{id:\"pal-56\",name:\"Sulfur Deck\",tagline:\"Sulfur yellow blue and hot orange\",colors:[\"#E3D200\",\"#274C77\",\"#FF4A00\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L3\",finish:\"polished-metal\"},{id:\"pal-57\",name:\"Neon Ember Static\",tagline:\"Orange yellow buzzing with royal voltage\",colors:[\"#FF5E00\",\"#FFE600\",\"#3A0CA3\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L4\",finish:\"watercolor\"},{id:\"pal-58\",name:\"Mango Ash\",tagline:\"Mango bright volcanic ash\",colors:[\"#FFA12B\",\"#59595E\",\"#64DDBB\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L1\",finish:\"matte\"},{id:\"pal-59\",name:\"Steel Petal\",tagline:\"Steel gray crushed petals\",colors:[\"#8A8F98\",\"#E71813\",\"#D8E26E\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L3\",finish:\"brushed-metal\"},{id:\"pal-60\",name:\"Ninja Cream\",tagline:\"Hidden navy cream coral\",colors:[\"#1E2A4A\",\"#F0EBE0\",\"#7EEC00\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L4\",finish:\"glaze\"},{id:\"pal-61\",name:\"Tangerine Static\",tagline:\"Tangerine buzz grape shadow\",colors:[\"#F28500\",\"#3D2159\",\"#202020\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L1\",finish:\"glitter\"},{id:\"pal-62\",name:\"Sage Pistol\",tagline:\"Muted sage pistol pink\",colors:[\"#9AB5A1\",\"#E45C98\",\"#33272A\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L3\",finish:\"matte\"},{id:\"pal-63\",name:\"Lagoon Battery\",tagline:\"Lagoon green charged yellow\",colors:[\"#1D8A7A\",\"#FAE000\",\"#76305B\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L4\",finish:\"polished-metal\"},{id:\"pal-64\",name:\"Wine Cooling\",tagline:\"Warm wine cooled by mint\",colors:[\"#701F3C\",\"#13E78C\",\"#E8D44D\"],unlockRule:\"battle-pass\",reward:\"warden-1:L1\",finish:\"pearl\"},{id:\"pal-65\",name:\"Charcoal Tulip\",tagline:\"Charcoal tulip flame combo\",colors:[\"#2E3033\",\"#FF4E50\",\"#CA00C7\"],unlockRule:\"battle-pass\",reward:\"warden-1:L3\",finish:\"matte\"},{id:\"pal-66\",name:\"Neon Acid Harbor\",tagline:\"Lime light cutting across magenta docks\",colors:[\"#39FF14\",\"#FF007F\",\"#001B2E\"],unlockRule:\"battle-pass\",reward:\"warden-1:L4\",finish:\"pearl\"},{id:\"pal-67\",name:\"Rustic Neon\",tagline:\"Barn red barn lime buzz\",colors:[\"#8A3324\",\"#C1FF33\",\"#2D4A6B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L1\",finish:\"marbled\"},{id:\"pal-68\",name:\"Alice Warning\",tagline:\"Alice blue caution orange\",colors:[\"#1399E7\",\"#FF7E1F\",\"#EEF700\"],unlockRule:\"battle-pass\",reward:\"blob-2:L3\",finish:\"watercolor\"},{id:\"pal-69\",name:\"Smog Candy\",tagline:\"Smog beige candy teal\",colors:[\"#E79813\",\"#00C2A8\",\"#D8395B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L4\",finish:\"matte\"},{id:\"pal-70\",name:\"Violet Furnace\",tagline:\"Furnace violet cooled sky\",colors:[\"#6A2D9E\",\"#F57C3B\",\"#302012\"],unlockRule:\"battle-pass\",reward:\"blob-3:L1\",finish:\"glaze\"},{id:\"pal-71\",name:\"Khaki Storm\",tagline:\"Khaki field storm magenta\",colors:[\"#A89B6E\",\"#E0218A\",\"#2A6F97\"],unlockRule:\"battle-pass\",reward:\"blob-3:L3\",finish:\"brushed-metal\"},{id:\"pal-72\",name:\"Neon Circuit Surge\",tagline:\"Turquoise sparks over midnight violet\",colors:[\"#00F5D4\",\"#F72585\",\"#0B0321\"],unlockRule:\"battle-pass\",reward:\"blob-3:L4\",finish:\"glitter\"},{id:\"pal-73\",name:\"Cedar Ultraviolet\",tagline:\"Cedar wood ultraviolet light\",colors:[\"#7A4532\",\"#8246FF\",\"#E9E4D8\"],unlockRule:\"battle-pass\",reward:\"blob-4:L1\",finish:\"matte\"},{id:\"pal-74\",name:\"Frosted Chili\",tagline:\"Frost blue chili red\",colors:[\"#13AAE7\",\"#C13A2F\",\"#4C5B2C\"],unlockRule:\"battle-pass\",reward:\"blob-4:L3\",finish:\"polished-metal\"},{id:\"pal-75\",name:\"Moss Rocket\",tagline:\"Moss green rocket pink\",colors:[\"#5B7042\",\"#FA0057\",\"#061F5B\"],unlockRule:\"battle-pass\",reward:\"blob-4:L4\",finish:\"matte\"},{id:\"pal-76\",name:\"Pewter Punch\",tagline:\"Pewter softened hot punch\",colors:[\"#99A0A8\",\"#FF3D7F\",\"#164D37\"],unlockRule:\"battle-pass\",reward:\"cap-2:L1\",finish:\"brushed-metal\"},{id:\"pal-77\",name:\"Neon Glacier Riot\",tagline:\"Cyan hot pink and radioactive lime\",colors:[\"#00F0FF\",\"#FF00E5\",\"#CCFF00\"],unlockRule:\"battle-pass\",reward:\"cap-2:L3\",finish:\"watercolor\"},{id:\"pal-78\",name:\"Cola Lightning\",tagline:\"Cola brown lightning yellow\",colors:[\"#3A2618\",\"#F7FF00\",\"#FA005E\"],unlockRule:\"battle-pass\",reward:\"cap-2:L4\",finish:\"glaze\"},{id:\"pal-79\",name:\"Periwinkle Fuel\",tagline:\"Soft periwinkle diesel orange\",colors:[\"#1325E7\",\"#E0641E\",\"#2F4730\"],unlockRule:\"battle-pass\",reward:\"cap-3:L1\",finish:\"matte\"},{id:\"pal-80\",name:\"Jade Alarm\",tagline:\"Jade calm siren red\",colors:[\"#57A78A\",\"#D91E36\",\"#E7AF13\"],unlockRule:\"battle-pass\",reward:\"cap-3:L3\",finish:\"pearl\"},{id:\"pal-81\",name:\"Neon Ultraviolet Pulse\",tagline:\"Violet beams with mint-charged current\",colors:[\"#8C00FF\",\"#00FFB2\",\"#160021\"],unlockRule:\"battle-pass\",reward:\"cap-3:L4\",finish:\"matte\"},{id:\"pal-82\",name:\"Neon Nightcaller\",tagline:\"Cyan violet flame in black-green fog\",colors:[\"#00FFFF\",\"#9400FF\",\"#131A00\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L1\",finish:\"marbled\"},{id:\"pal-83\",name:\"Ivory Shock\",tagline:\"Ivory calm violet shock\",colors:[\"#F0EEE4\",\"#7C1FD3\",\"#FFB200\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L3\",finish:\"glaze\"},{id:\"pal-84\",name:\"Neon Ruby Arc\",tagline:\"Red-hot signal over green afterglow\",colors:[\"#FF073A\",\"#00FF87\",\"#080F1B\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L4\",finish:\"glitter\"},{id:\"pal-85\",name:\"Maroon Cloud\",tagline:\"Maroon heavy cloud blue\",colors:[\"#6D1E2E\",\"#1381E7\",\"#E5D352\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L1\",finish:\"watercolor\"},{id:\"pal-86\",name:\"Chrome Berry\",tagline:\"Chrome band over berry\",colors:[\"#C3C9D1\",\"#8E2A5C\",\"#FFB347\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L3\",finish:\"polished-metal\"},{id:\"pal-87\",name:\"Lentil Neon\",tagline:\"Lentil tan electric aqua\",colors:[\"#B09A6E\",\"#00FFF0\",\"#5F1B63\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L4\",finish:\"matte\"},{id:\"pal-88\",name:\"Neon Cobalt Fuse\",tagline:\"Blue ignition wired to blazing yellow\",colors:[\"#004DFF\",\"#FFEE00\",\"#0D0221\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L1\",finish:\"glaze\"},{id:\"pal-89\",name:\"Spruce Bonfire\",tagline:\"Dark spruce bonfire orange\",colors:[\"#23493C\",\"#FF7A3D\",\"#0C1735\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L3\",finish:\"matte\"},{id:\"pal-90\",name:\"Lilac Foundry\",tagline:\"Lilac meets foundry steel\",colors:[\"#9813E7\",\"#7B8894\",\"#D4522A\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L4\",finish:\"brushed-metal\"},{id:\"pal-91\",name:\"Topaz Battle\",tagline:\"Topaz gold navy clash\",colors:[\"#F0B429\",\"#182952\",\"#FA007D\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L1\",finish:\"polished-metal\"},{id:\"pal-92\",name:\"Dusk Celery\",tagline:\"Dusky purple celery snap\",colors:[\"#574B6B\",\"#C8D86A\",\"#FA2100\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L3\",finish:\"matte\"},{id:\"pal-93\",name:\"Tide Copperfuse\",tagline:\"Tidal teal copperfuse glow\",colors:[\"#0F7B7B\",\"#C07840\",\"#C2F000\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L4\",finish:\"pearl\"},{id:\"pal-94\",name:\"Punch Driftwood\",tagline:\"Fruit punch on driftwood\",colors:[\"#E63362\",\"#6E5B4A\",\"#1648EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L1\",finish:\"watercolor\"},{id:\"pal-95\",name:\"Ink Lime\",tagline:\"Ink smudge streaked lime\",colors:[\"#23232A\",\"#A8E10C\",\"#7E10EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L3\",finish:\"glaze\"},{id:\"pal-96\",name:\"Porcelain Prism\",tagline:\"Porcelain violet over tidal blue\",colors:[\"#F5F1E8\",\"#BD05DA\",\"#005F73\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L4\",finish:\"marbled\"},{id:\"pal-97\",name:\"Wasabi Cloud\",tagline:\"Wasabi heat cloud gray\",colors:[\"#9FBF3B\",\"#8E94A0\",\"#E71363\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L1\",finish:\"matte\"},{id:\"pal-98\",name:\"Eggplant Flare\",tagline:\"Eggplant deep flare yellow\",colors:[\"#41284E\",\"#FFD23F\",\"#5BC0BE\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L3\",finish:\"glitter\"},{id:\"pal-99\",name:\"Stone Rose\",tagline:\"Stone gray antique rose\",colors:[\"#8F8A82\",\"#C46E8E\",\"#2F6DA3\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L4\",finish:\"brushed-metal\"},{id:\"pal-100\",name:\"Neon Quasar Pop\",tagline:\"Yellow green comet over indigo gravity\",colors:[\"#FFEF00\",\"#00FF3C\",\"#2D02B8\"],unlockRule:\"battle-pass\",reward:\"lume-1:L1\",finish:\"glaze\"},{id:\"pal-101\",name:\"Arctic Rust\",tagline:\"Arctic white rusted orange\",colors:[\"#E9F2F5\",\"#9E4A1D\",\"#3E3F8F\"],unlockRule:\"battle-pass\",reward:\"lume-1:L3\",finish:\"brushed-metal\"},{id:\"pal-102\",name:\"Magenta Selvage\",tagline:\"Magenta edge denim blue\",colors:[\"#D8117E\",\"#3B5B8C\",\"#E7D313\"],unlockRule:\"battle-pass\",reward:\"lume-1:L4\",finish:\"matte\"},{id:\"pal-103\",name:\"Haze Saffron\",tagline:\"Purple haze saffron spark\",colors:[\"#8467B8\",\"#F4A81D\",\"#1D4E50\"],unlockRule:\"battle-pass\",reward:\"food:L1\",finish:\"pearl\"},{id:\"pal-104\",name:\"Neon Cyber Reed\",tagline:\"Green circuitry humming beside lemon static\",colors:[\"#00FF66\",\"#FFEA00\",\"#4B0082\"],unlockRule:\"battle-pass\",reward:\"food:L2\",finish:\"glitter\"},{id:\"pal-105\",name:\"Neon Magma Bloom\",tagline:\"Molten orange meeting synthetic magenta\",colors:[\"#FF2E00\",\"#F000FF\",\"#0A0A0F\"],unlockRule:\"battle-pass\",reward:\"food:L3\",finish:\"polished-metal\"},{id:\"pal-106\",name:\"Dune Violet\",tagline:\"Warm dune electric violet\",colors:[\"#C9AE7E\",\"#7D2BF0\",\"#13E7E3\"],unlockRule:\"battle-pass\",reward:\"food:L4\",finish:\"marbled\"},{id:\"pal-107\",name:\"Ember Nightshade\",tagline:\"Violet shadows ember teal\",colors:[\"#523090\",\"#EF4E23\",\"#0C5D68\"],unlockRule:\"battle-pass\",reward:\"food:L5\",finish:\"watercolor\"},{id:\"opal-jelly\",name:\"Opal Jelly\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b3fff0\",\"#9767ff\",\"#fff1ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"bubble-glass\",name:\"Bubble Glass\",tagline:\"Shifting highlights and luminous color\",colors:[\"#58eaff\",\"#b8fff8\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"prism-crystal\",name:\"Prism Crystal\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b18aff\",\"#447bff\",\"#fff2ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"holo-foil\",name:\"Holographic Foil\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff54de\",\"#39e5ff\",\"#fff56b\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"glitter-resin\",name:\"Glitter Resin\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff319c\",\"#7025ff\",\"#fff6db\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"galaxy-geode\",name:\"Galaxy Geode\",tagline:\"Shifting highlights and luminous color\",colors:[\"#21134e\",\"#7b39e8\",\"#63ffff\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"caustic-slime\",name:\"Caustic Slime\",tagline:\"Organic horror surface\",colors:[\"#8aa52b\",\"#20300b\",\"#dcff57\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"blister-hide\",name:\"Blister Hide\",tagline:\"Organic horror surface\",colors:[\"#9e435f\",\"#2d1727\",\"#e7bb72\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"rotten-rind\",name:\"Rotten Rind\",tagline:\"Organic horror surface\",colors:[\"#626124\",\"#211916\",\"#a2b16b\"],unlockRule:\"battle-pass\",finish:\"matte\"},{id:\"parasite-nest\",name:\"Parasite Nest\",tagline:\"Organic horror surface\",colors:[\"#786541\",\"#151c14\",\"#ece2b0\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"exposed-sinew\",name:\"Exposed Sinew\",tagline:\"Organic horror surface\",colors:[\"#b72945\",\"#300b24\",\"#efb5a1\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"abyssal-maw\",name:\"Abyssal Maw\",tagline:\"Organic horror surface\",colors:[\"#522045\",\"#100d19\",\"#d9e6a7\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"circuit-alloy\",name:\"Circuit Alloy\",tagline:\"Machine surface\",colors:[\"#273f50\",\"#0a111c\",\"#4effdc\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"servo-armor\",name:\"Servo Armor\",tagline:\"Machine surface\",colors:[\"#617080\",\"#141b25\",\"#ff6739\"],unlockRule:\"battle-pass\",finish:\"brushed-metal\"},{id:\"chrome-rib\",name:\"Chrome Rib\",tagline:\"Machine surface\",colors:[\"#bfcde1\",\"#202f46\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"carbon-mech\",name:\"Carbon Mech\",tagline:\"Machine surface\",colors:[\"#323d46\",\"#080d15\",\"#95bac9\"],unlockRule:\"battle-pass\"},{id:\"hazard-panel\",name:\"Hazard Panel\",tagline:\"Machine surface\",colors:[\"#edb932\",\"#131a20\",\"#ffe9a2\"],unlockRule:\"battle-pass\"},{id:\"reactor-glass\",name:\"Reactor Glass\",tagline:\"Machine surface\",colors:[\"#48e8ff\",\"#063a55\",\"#d4ffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"}];var $a={schemaVersion:1,additive:!0,colorSpace:{basecolor:\"srgb\",tintMask:\"srgb\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"srgb\"},normalConvention:\"OpenGL +Y\",tinting:\"tint-mask multiplied by selected creature palette\",collections:[{id:\"creature-forged-realms\",packetId:\"coach-creature-forged-realms-v1\",displayName:\"Coach Creature Skins \\u2014 Forged Realms\",description:\"Additional fantasy\\u2013science-fiction creature skin unlocks for the Coach app. Separate from the original fitness-material battle-pass set.\",runtimeNotes:{normalConvention:\"OpenGL (+Y)\",tinting:\"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",recommendedColorSpace:{basecolor:\"sRGB\",tintMask:\"sRGB\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"sRGB\"}}},{id:\"creature-celestial-rift\",packetId:\"coach-creature-celestial-rift-v1\",displayName:\"Coach Creature Skins \\u2014 Celestial Rift\",description:\"A second non-overlapping set of fantasy\\u2013science-fiction creature skin unlocks for the Coach app. Additive to Forged Realms and the original fitness-material set.\",runtimeNotes:{normalConvention:\"OpenGL (+Y)\",tinting:\"Use tint-mask.webp as a neutral grayscale base multiplied by the creature palette.\",recommendedColorSpace:{basecolor:\"sRGB\",tintMask:\"sRGB\",normal:\"linear\",roughness:\"linear\",height:\"linear\",ao:\"linear\",metalness:\"linear\",opacity:\"linear\",emissive:\"sRGB\"}}}],skins:[{id:\"creature-starforged-plate\",displayName:\"Starforged Plate\",track:\"chest\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Metal009\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal009\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-starforged-plate/basecolor.webp\",bytes:8170,sha256:\"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"},normal:{file:\"textures/creature-starforged-plate/normal.webp\",bytes:878,sha256:\"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"},roughness:{file:\"textures/creature-starforged-plate/roughness.webp\",bytes:16326,sha256:\"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"},height:{file:\"textures/creature-starforged-plate/height.webp\",bytes:37602,sha256:\"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"},metalness:{file:\"textures/creature-starforged-plate/metalness.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-starforged-plate/tint-mask.webp\",bytes:47604,sha256:\"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"},preview:{file:\"textures/creature-starforged-plate/preview.webp\",bytes:9336,sha256:\"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"}},runtime:{unlock:{level:1,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-starforged-plate/basecolor\",bytes:8170,sha256:\"c4c112d6a9ef20989d9cc048ec1bd4680e25df03fa9a99e4d124b0058ffe66e9\"},normal:{entry:\"creature-starforged-plate/normal\",bytes:878,sha256:\"a69782b9e6c01a41c9bde8e47c4f67490bf97869cf4fa332454e1d371ce0e1af\"},roughness:{entry:\"creature-starforged-plate/roughness\",bytes:16326,sha256:\"eb8bf7a214631382971d470f7566e664167f395d27a51428c02edb7e9065ad39\"},height:{entry:\"creature-starforged-plate/height\",bytes:37602,sha256:\"520bb285c4862740f48d076d49320b74f196bd7463e4dd0c338a0bcb181a103e\"},metalness:{entry:\"creature-starforged-plate/metalness\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-starforged-plate/tintMask\",bytes:47604,sha256:\"4faadbe93f4126eac9cfba939acd1db3c3d4b5ff7a954ca33ae72ee128555b6c\"},preview:{entry:\"creature-starforged-plate/preview\",bytes:9336,sha256:\"0493936cdccec9486471163fb6b3a2fdc7195fc9535653cb714ed32fd5e32d15\"}}}},{id:\"creature-obsidian-carapace\",displayName:\"Obsidian Carapace\",track:\"chest\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Onyx013\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Onyx013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-obsidian-carapace/basecolor.webp\",bytes:50752,sha256:\"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"},normal:{file:\"textures/creature-obsidian-carapace/normal.webp\",bytes:596,sha256:\"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"},roughness:{file:\"textures/creature-obsidian-carapace/roughness.webp\",bytes:1382,sha256:\"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"},height:{file:\"textures/creature-obsidian-carapace/height.webp\",bytes:2988,sha256:\"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"},tintMask:{file:\"textures/creature-obsidian-carapace/tint-mask.webp\",bytes:65032,sha256:\"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"},preview:{file:\"textures/creature-obsidian-carapace/preview.webp\",bytes:58128,sha256:\"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"}},runtime:{unlock:{level:3,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-obsidian-carapace/basecolor\",bytes:50752,sha256:\"1bf15ad9d98a4c5dfdc03623a52b62995659ca592d832652f17cc26e873d093c\"},normal:{entry:\"creature-obsidian-carapace/normal\",bytes:596,sha256:\"72e8331843ae2e2d4018ef40d888cbc1ddeb7d46786874eea50ca36837b3b1a7\"},roughness:{entry:\"creature-obsidian-carapace/roughness\",bytes:1382,sha256:\"a66f044c77d913409a96ef8eaf4a0e59df22ac3bec63b89a3484259269aa33cc\"},height:{entry:\"creature-obsidian-carapace/height\",bytes:2988,sha256:\"760380dff035f707b917266f6bbf72ffe671fde8394b243652398b543dd71046\"},tintMask:{entry:\"creature-obsidian-carapace/tintMask\",bytes:65032,sha256:\"c50038331782d6ddd1dcfac8df9158043eb5efd085a0fe56e1074d55123a10c5\"},preview:{entry:\"creature-obsidian-carapace/preview\",bytes:58128,sha256:\"eb650060773ef1a11f0f8dcaf310735f709ba3e05d2d3158148524113c7013e9\"}}}},{id:\"creature-hive-armor\",displayName:\"Hive Armor\",track:\"chest\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"MetalPlates013\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/MetalPlates013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-hive-armor/basecolor.webp\",bytes:24764,sha256:\"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"},normal:{file:\"textures/creature-hive-armor/normal.webp\",bytes:14104,sha256:\"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"},roughness:{file:\"textures/creature-hive-armor/roughness.webp\",bytes:44122,sha256:\"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"},height:{file:\"textures/creature-hive-armor/height.webp\",bytes:9344,sha256:\"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"},ao:{file:\"textures/creature-hive-armor/ao.webp\",bytes:5394,sha256:\"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"},metalness:{file:\"textures/creature-hive-armor/metalness.webp\",bytes:38740,sha256:\"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"},tintMask:{file:\"textures/creature-hive-armor/tint-mask.webp\",bytes:61850,sha256:\"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"},preview:{file:\"textures/creature-hive-armor/preview.webp\",bytes:30780,sha256:\"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"}},runtime:{unlock:{level:5,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-hive-armor/basecolor\",bytes:24764,sha256:\"a98dcfd52d19fc449d94a1a151f8281f1eda86ad6c7d0bce7719dddc1e50e34f\"},normal:{entry:\"creature-hive-armor/normal\",bytes:14104,sha256:\"50f6713c0ed2ec0b1c5b4ba589011499e92d4645550b79ec6a41b6727b8bd521\"},roughness:{entry:\"creature-hive-armor/roughness\",bytes:44122,sha256:\"083f43ab705595a824e70ec97f4afc0670bca3e929b180cb374212a25715c18a\"},height:{entry:\"creature-hive-armor/height\",bytes:9344,sha256:\"fb5d1b4c2c1732a3e5f42fb6e16a6a5b3ad24955069a5adbe3908f3619ea6b82\"},ao:{entry:\"creature-hive-armor/ao\",bytes:5394,sha256:\"7c97ef834a1bc65cc2a61419a1d6730dca231c912edf0572f06ecfe3157250ab\"},metalness:{entry:\"creature-hive-armor/metalness\",bytes:38740,sha256:\"62aa9640f463a8866fdd53a961041ff6d6b8c14415b31f241d85658544a2c5aa\"},tintMask:{entry:\"creature-hive-armor/tintMask\",bytes:61850,sha256:\"d2e7066bad7574d1b694d8186212ce56dcc4757033b061f6b6e3b08251c5046c\"},preview:{entry:\"creature-hive-armor/preview\",bytes:30780,sha256:\"a7c7bb3c7d17804bd44ce5d54e57681f2036b9758e1d3ac34dc2b238a7aab250\"}}}},{id:\"creature-magma-scale\",displayName:\"Magma Scale\",track:\"quads\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Lava004\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Lava004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-magma-scale/basecolor.webp\",bytes:34804,sha256:\"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"},normal:{file:\"textures/creature-magma-scale/normal.webp\",bytes:55692,sha256:\"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"},roughness:{file:\"textures/creature-magma-scale/roughness.webp\",bytes:22680,sha256:\"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"},height:{file:\"textures/creature-magma-scale/height.webp\",bytes:12946,sha256:\"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"},emissive:{file:\"textures/creature-magma-scale/emissive.webp\",bytes:41142,sha256:\"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"},tintMask:{file:\"textures/creature-magma-scale/tint-mask.webp\",bytes:41404,sha256:\"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"},preview:{file:\"textures/creature-magma-scale/preview.webp\",bytes:38666,sha256:\"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"}},runtime:{unlock:{level:1,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-magma-scale/basecolor\",bytes:34804,sha256:\"e57fb5ed1a1449ba5a46535e62b14c73781e4e768d837ea5d80e241995ab3fb7\"},normal:{entry:\"creature-magma-scale/normal\",bytes:55692,sha256:\"873ead60d1c158910ffee486bc6a55ee4cb4ad1d73cea2f351a9d095a1798ef9\"},roughness:{entry:\"creature-magma-scale/roughness\",bytes:22680,sha256:\"f4eab6ab6dac73119e8e36c4f880115fc6e3a65252084a7412a811066110c103\"},height:{entry:\"creature-magma-scale/height\",bytes:12946,sha256:\"8ddb62676ce447fb46030d7e65d9a6290ae993126dac3dc57b8faecd7dc7f662\"},emissive:{entry:\"creature-magma-scale/emissive\",bytes:41142,sha256:\"3c0b9641bd6fe9465085ee4ce1f7657fa6fe5dd80f49c667498bdb96c7b4b5c8\"},tintMask:{entry:\"creature-magma-scale/tintMask\",bytes:41404,sha256:\"2248005bce453ca3c2f57f4a460b7a561a697537d6e921766d3b45d156211cb3\"},preview:{entry:\"creature-magma-scale/preview\",bytes:38666,sha256:\"411d36c7435a414cb7e304b9099e0db9f35f95e24fc1f281a380020b51ddf180\"}}}},{id:\"creature-frost-veins\",displayName:\"Frost Veins\",track:\"quads\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Ice003\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Ice003\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-frost-veins/basecolor.webp\",bytes:22338,sha256:\"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"},normal:{file:\"textures/creature-frost-veins/normal.webp\",bytes:4622,sha256:\"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"},roughness:{file:\"textures/creature-frost-veins/roughness.webp\",bytes:13946,sha256:\"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"},height:{file:\"textures/creature-frost-veins/height.webp\",bytes:12916,sha256:\"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"},tintMask:{file:\"textures/creature-frost-veins/tint-mask.webp\",bytes:26114,sha256:\"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"},preview:{file:\"textures/creature-frost-veins/preview.webp\",bytes:26196,sha256:\"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"}},runtime:{unlock:{level:3,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-frost-veins/basecolor\",bytes:22338,sha256:\"7d21627868aba7ad76ba80359897464cee48faef462a06974e24c823a19d06b4\"},normal:{entry:\"creature-frost-veins/normal\",bytes:4622,sha256:\"95179159bbe6d580b6e3e12de417064e65fe8fb287dbab1756a0ff47dfe337c0\"},roughness:{entry:\"creature-frost-veins/roughness\",bytes:13946,sha256:\"dfbbd3f6e4293432dbd9c2d1cdd6904d3523223507c9a0f8d781c1f0fd2d9c25\"},height:{entry:\"creature-frost-veins/height\",bytes:12916,sha256:\"ef9943193c04a33123c42d3af2d4a3a2fc06b0d2ac46c8fd12d4b17b745d1359\"},tintMask:{entry:\"creature-frost-veins/tintMask\",bytes:26114,sha256:\"8c9a1385824ab96b15c42f0afccf237a40507b427af4bc6b827e50b984cf2097\"},preview:{entry:\"creature-frost-veins/preview\",bytes:26196,sha256:\"7462a4fb4044ae6e6521d79829b7dac42766c3e87f4092567829d7e0f93bb951\"}}}},{id:\"creature-storm-carbon\",displayName:\"Storm Carbon\",track:\"quads\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"Fabric004\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Fabric004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-storm-carbon/basecolor.webp\",bytes:4186,sha256:\"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"},normal:{file:\"textures/creature-storm-carbon/normal.webp\",bytes:21570,sha256:\"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"},roughness:{file:\"textures/creature-storm-carbon/roughness.webp\",bytes:13466,sha256:\"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"},height:{file:\"textures/creature-storm-carbon/height.webp\",bytes:10398,sha256:\"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"},metalness:{file:\"textures/creature-storm-carbon/metalness.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-storm-carbon/tint-mask.webp\",bytes:11298,sha256:\"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"},preview:{file:\"textures/creature-storm-carbon/preview.webp\",bytes:4638,sha256:\"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"}},runtime:{unlock:{level:5,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-storm-carbon/basecolor\",bytes:4186,sha256:\"2f8c8777cc5df50ebb81c63fd5e09f3a4ec572b19b47e6c1f04fd7d5c4641b27\"},normal:{entry:\"creature-storm-carbon/normal\",bytes:21570,sha256:\"0364397ee006f2e0cfd26a8b05fc43f3604acef539de8e53d85aaa679eae80d5\"},roughness:{entry:\"creature-storm-carbon/roughness\",bytes:13466,sha256:\"cbd3523dfa84268974de80205d042b4339b9bea70ebe19c3b5827f3f500484b0\"},height:{entry:\"creature-storm-carbon/height\",bytes:10398,sha256:\"46d6212cbedabf6bc51bcc61c7d03bb1711f574fbcb70c47cfbef46c9521dffb\"},metalness:{entry:\"creature-storm-carbon/metalness\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-storm-carbon/tintMask\",bytes:11298,sha256:\"3e5019a042c69df2b895973c0678b05b0ae9d6d8c1673ca5fde515d19e37b318\"},preview:{entry:\"creature-storm-carbon/preview\",bytes:4638,sha256:\"813c9f6903f3fe0f9c7a4306a319e33e6b255237722607f5ca96d067bb59be4c\"}}}},{id:\"creature-ancient-bark\",displayName:\"Ancient Bark\",track:\"glutes\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Bark015\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Bark015\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ancient-bark/basecolor.webp\",bytes:76532,sha256:\"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"},normal:{file:\"textures/creature-ancient-bark/normal.webp\",bytes:81960,sha256:\"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"},roughness:{file:\"textures/creature-ancient-bark/roughness.webp\",bytes:30736,sha256:\"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"},height:{file:\"textures/creature-ancient-bark/height.webp\",bytes:12470,sha256:\"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"},ao:{file:\"textures/creature-ancient-bark/ao.webp\",bytes:62736,sha256:\"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"},tintMask:{file:\"textures/creature-ancient-bark/tint-mask.webp\",bytes:84688,sha256:\"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"},preview:{file:\"textures/creature-ancient-bark/preview.webp\",bytes:85306,sha256:\"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"}},runtime:{unlock:{level:1,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-ancient-bark/basecolor\",bytes:76532,sha256:\"4f104cc845070e5952eef5428e55cf37871a53b920539509d127484ef56fc492\"},normal:{entry:\"creature-ancient-bark/normal\",bytes:81960,sha256:\"6805d1f6c82e7aeadfbbee3fe039c35773d49f26b527266ac0b8122c65e55f7c\"},roughness:{entry:\"creature-ancient-bark/roughness\",bytes:30736,sha256:\"4fc3cf417cb8c3621f49bb36eb8c0e1f41ac501d2bdab16e768dc8eebccc617b\"},height:{entry:\"creature-ancient-bark/height\",bytes:12470,sha256:\"8364e839245b1a832ae8f2195ff212f4fb0e4d6bd8b58909b90389e261eea9b5\"},ao:{entry:\"creature-ancient-bark/ao\",bytes:62736,sha256:\"1490dc238669befa434ed5bd28336b865c8869a75b9db742272e60541ae99b68\"},tintMask:{entry:\"creature-ancient-bark/tintMask\",bytes:84688,sha256:\"b1a08d54081eb2e811acf490187e2be77186e366d82ddd18f68c6ef240ddf1ed\"},preview:{entry:\"creature-ancient-bark/preview\",bytes:85306,sha256:\"95d00c586199990218c99d2366252ed877877885be164ce7e3186421cb339ea1\"}}}},{id:\"creature-mossback\",displayName:\"Mossback\",track:\"glutes\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bark013\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bark013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mossback/basecolor.webp\",bytes:62984,sha256:\"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"},normal:{file:\"textures/creature-mossback/normal.webp\",bytes:74686,sha256:\"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"},roughness:{file:\"textures/creature-mossback/roughness.webp\",bytes:42160,sha256:\"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"},height:{file:\"textures/creature-mossback/height.webp\",bytes:23126,sha256:\"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"},ao:{file:\"textures/creature-mossback/ao.webp\",bytes:44736,sha256:\"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"},tintMask:{file:\"textures/creature-mossback/tint-mask.webp\",bytes:71276,sha256:\"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"},preview:{file:\"textures/creature-mossback/preview.webp\",bytes:71140,sha256:\"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"}},runtime:{unlock:{level:3,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-mossback/basecolor\",bytes:62984,sha256:\"1dbaed4381988af8d7145a14a15163047e8bd1ecbd0222101ba8f66c4109b88a\"},normal:{entry:\"creature-mossback/normal\",bytes:74686,sha256:\"d8c6fdbf999eae4f19b5f6e62acfcadbb0ba8d61d98302898438c307083f1ddb\"},roughness:{entry:\"creature-mossback/roughness\",bytes:42160,sha256:\"daa3d7d0555e81d07ff13fbb1f481d805a79ae991712b1dde396ad8a43bf4190\"},height:{entry:\"creature-mossback/height\",bytes:23126,sha256:\"1977403ce1a9ad3b3606d20ec8d367df41647947291a8a6baef85f6d64933b3a\"},ao:{entry:\"creature-mossback/ao\",bytes:44736,sha256:\"3b536128f80b296e091ea080d2fb8ac53ca06ea2c44d21b34168333f4410e22f\"},tintMask:{entry:\"creature-mossback/tintMask\",bytes:71276,sha256:\"58497e16f8e9e8986f08381ba4482586d0d1e5b5714d4f17b058d93de724fc17\"},preview:{entry:\"creature-mossback/preview\",bytes:71140,sha256:\"478378a5c04eea0df87ba8d939215f04475914d38b3dba3a75fdcf99cf7f5985\"}}}},{id:\"creature-rootbound\",displayName:\"Rootbound Hide\",track:\"glutes\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Ground076\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Ground076\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-rootbound/basecolor.webp\",bytes:80318,sha256:\"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"},normal:{file:\"textures/creature-rootbound/normal.webp\",bytes:68262,sha256:\"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"},roughness:{file:\"textures/creature-rootbound/roughness.webp\",bytes:52578,sha256:\"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"},height:{file:\"textures/creature-rootbound/height.webp\",bytes:23352,sha256:\"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"},ao:{file:\"textures/creature-rootbound/ao.webp\",bytes:66244,sha256:\"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"},tintMask:{file:\"textures/creature-rootbound/tint-mask.webp\",bytes:88550,sha256:\"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"},preview:{file:\"textures/creature-rootbound/preview.webp\",bytes:86566,sha256:\"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"}},runtime:{unlock:{level:5,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-rootbound/basecolor\",bytes:80318,sha256:\"e7ea9cf40442e9784dddc75399ee818a56b32110b70c7c97f5160f66a8468227\"},normal:{entry:\"creature-rootbound/normal\",bytes:68262,sha256:\"6619d18b0159c1c6433224079bd8c7af4427330312fd95bf4dc3f8817ddf06bb\"},roughness:{entry:\"creature-rootbound/roughness\",bytes:52578,sha256:\"35f4e9ae25db1ab566e1585b50a0e16d6568c5bc28854b701f56fb32635d47f9\"},height:{entry:\"creature-rootbound/height\",bytes:23352,sha256:\"197ddfca9c4d2eaf8ad0abfe1487d3c28e7b6bb3ce2b84805c5ba41cebb440a1\"},ao:{entry:\"creature-rootbound/ao\",bytes:66244,sha256:\"67b002796100a011fdf5502f8245981fd3cdb9b932968be285611e8a4f0bbffd\"},tintMask:{entry:\"creature-rootbound/tintMask\",bytes:88550,sha256:\"f3be9091137218b440b1b58b0cb826863a1f3bff4cbd8a46d8d91413912c8fd7\"},preview:{entry:\"creature-rootbound/preview\",bytes:86566,sha256:\"d0d304a702c7e9ea486a4cd7432e90d6d82272dfe8e59468060525a17474e12f\"}}}},{id:\"creature-hammered-bronze\",displayName:\"Hammered Bronze Golem\",track:\"arms\",realm:\"hybrid\",rarity:\"rare\",sourceAssetId:\"Metal008\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal008\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-hammered-bronze/basecolor.webp\",bytes:21270,sha256:\"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"},normal:{file:\"textures/creature-hammered-bronze/normal.webp\",bytes:1860,sha256:\"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"},roughness:{file:\"textures/creature-hammered-bronze/roughness.webp\",bytes:22538,sha256:\"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"},height:{file:\"textures/creature-hammered-bronze/height.webp\",bytes:3670,sha256:\"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"},metalness:{file:\"textures/creature-hammered-bronze/metalness.webp\",bytes:3062,sha256:\"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"},tintMask:{file:\"textures/creature-hammered-bronze/tint-mask.webp\",bytes:69154,sha256:\"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"},preview:{file:\"textures/creature-hammered-bronze/preview.webp\",bytes:26964,sha256:\"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"}},runtime:{unlock:{level:1,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-hammered-bronze/basecolor\",bytes:21270,sha256:\"beb51e4c3e9cbc126c7e684904a9848d6c49f593c7351c77df7d838a0b3031a2\"},normal:{entry:\"creature-hammered-bronze/normal\",bytes:1860,sha256:\"343ca61b6eac8a6ed0f1c8ece185543a57e19af2890393100da77b8c208df81f\"},roughness:{entry:\"creature-hammered-bronze/roughness\",bytes:22538,sha256:\"d2b4ab3952757b6e165e5b131865e89312daa81f2e7b20f4533d424b1d5d2846\"},height:{entry:\"creature-hammered-bronze/height\",bytes:3670,sha256:\"966a3de7da93c1aa338a7d5fd25b8b8b60e1b0eae492c6633a5256091e803520\"},metalness:{entry:\"creature-hammered-bronze/metalness\",bytes:3062,sha256:\"c94836b0fd1152b518b7f126528630d10c215c8cf1d8927d91652496844311bb\"},tintMask:{entry:\"creature-hammered-bronze/tintMask\",bytes:69154,sha256:\"977b36bfb25617394eda6993fe98972e87575b0a4f20c638fd7816627c50530c\"},preview:{entry:\"creature-hammered-bronze/preview\",bytes:26964,sha256:\"ac7d9a6bd1fef3088df0196c355462a24ae5f59276c31b2a84f32277e5174093\"}}}},{id:\"creature-rope-sinew\",displayName:\"Rope Sinew\",track:\"arms\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rope001\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rope001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-rope-sinew/basecolor.webp\",bytes:29552,sha256:\"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"},normal:{file:\"textures/creature-rope-sinew/normal.webp\",bytes:58934,sha256:\"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"},roughness:{file:\"textures/creature-rope-sinew/roughness.webp\",bytes:12446,sha256:\"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"},height:{file:\"textures/creature-rope-sinew/height.webp\",bytes:8678,sha256:\"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"},metalness:{file:\"textures/creature-rope-sinew/metalness.webp\",bytes:530,sha256:\"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"},tintMask:{file:\"textures/creature-rope-sinew/tint-mask.webp\",bytes:45544,sha256:\"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"},preview:{file:\"textures/creature-rope-sinew/preview.webp\",bytes:35204,sha256:\"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"}},runtime:{unlock:{level:3,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-rope-sinew/basecolor\",bytes:29552,sha256:\"2100056331b8dd71fa4df45d64142272a4eda355df847f835e1f4a45ec0215ac\"},normal:{entry:\"creature-rope-sinew/normal\",bytes:58934,sha256:\"5a2f64efff064472eee99a9f2ad9e727a2f34807df7f4e4d1ee13cbd57eb9f87\"},roughness:{entry:\"creature-rope-sinew/roughness\",bytes:12446,sha256:\"d95a57e890f030607ebc323ca242cc89a64f380f4f13edf12435a486f8892923\"},height:{entry:\"creature-rope-sinew/height\",bytes:8678,sha256:\"41492b35711c17826fe23f1b06e4f61880ee084c5184ba8e7ad2a4bae058ce1b\"},metalness:{entry:\"creature-rope-sinew/metalness\",bytes:530,sha256:\"0d61075921f8ced6ab09e8ff7a70d4732d0bbfa62467afcdb18dce2231550116\"},tintMask:{entry:\"creature-rope-sinew/tintMask\",bytes:45544,sha256:\"760c9886d716bb31396be5d943f4547dc05090e4c787ff15245c260306ee268f\"},preview:{entry:\"creature-rope-sinew/preview\",bytes:35204,sha256:\"b59e86c79490250b094d7857eeffb5b2c716bb2896394643247f805e509fd9c8\"}}}},{id:\"creature-runic-leather\",displayName:\"Runic Leather\",track:\"arms\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Leather032\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Leather032\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-runic-leather/basecolor.webp\",bytes:28382,sha256:\"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"},normal:{file:\"textures/creature-runic-leather/normal.webp\",bytes:7148,sha256:\"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"},roughness:{file:\"textures/creature-runic-leather/roughness.webp\",bytes:85118,sha256:\"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"},height:{file:\"textures/creature-runic-leather/height.webp\",bytes:90332,sha256:\"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"},tintMask:{file:\"textures/creature-runic-leather/tint-mask.webp\",bytes:89338,sha256:\"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"},preview:{file:\"textures/creature-runic-leather/preview.webp\",bytes:35058,sha256:\"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"}},runtime:{unlock:{level:5,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-runic-leather/basecolor\",bytes:28382,sha256:\"bfd8b97143b2df95e1209492ae5d4b2c6bc61b33d82e5d5382a5162392065c66\"},normal:{entry:\"creature-runic-leather/normal\",bytes:7148,sha256:\"ddd41e34b6d8ec05f19374ff263344b38c811564f6e7882b5cb48ca71f6cb371\"},roughness:{entry:\"creature-runic-leather/roughness\",bytes:85118,sha256:\"651e177fa84f3e1f9eebe5a7e7514a28d1bebab1867ec0f495a10aec997353ef\"},height:{entry:\"creature-runic-leather/height\",bytes:90332,sha256:\"66a3c469fd1cb3c120082855bfcef6dadc921e01e3d4a36b1994ee0ea4bf642b\"},tintMask:{entry:\"creature-runic-leather/tintMask\",bytes:89338,sha256:\"5332d654a93dc4f4992f571fd2e5caccad444aa84670cb96da497acca8c9f72f\"},preview:{entry:\"creature-runic-leather/preview\",bytes:35058,sha256:\"09b7cf1f2654343134c9e0a795afbab3881ba0d10b62c5a006cade89eae4eae3\"}}}},{id:\"creature-crystal-core\",displayName:\"Crystal Core\",track:\"yoga\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Onyx006\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Onyx006\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-crystal-core/basecolor.webp\",bytes:50726,sha256:\"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"},normal:{file:\"textures/creature-crystal-core/normal.webp\",bytes:572,sha256:\"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"},roughness:{file:\"textures/creature-crystal-core/roughness.webp\",bytes:15314,sha256:\"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"},height:{file:\"textures/creature-crystal-core/height.webp\",bytes:2740,sha256:\"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"},tintMask:{file:\"textures/creature-crystal-core/tint-mask.webp\",bytes:51868,sha256:\"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"},preview:{file:\"textures/creature-crystal-core/preview.webp\",bytes:56978,sha256:\"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"}},runtime:{unlock:{level:1,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-crystal-core/basecolor\",bytes:50726,sha256:\"b40390dfbd54cd5582d63d154fc924302d6c04acb1722ce2219f8b67ddaeb36d\"},normal:{entry:\"creature-crystal-core/normal\",bytes:572,sha256:\"b913752028fa3b5d7d102f5eba23aa17b9e0ee1324f52765cb249e36e189ded5\"},roughness:{entry:\"creature-crystal-core/roughness\",bytes:15314,sha256:\"88749dba30f831a86dadbe7bca39039297837646ee2d5330085019eaa7fa4074\"},height:{entry:\"creature-crystal-core/height\",bytes:2740,sha256:\"196a000792095c5dfa85554a2395e8bc1ffc856039b62eacbdd666f587a38c2a\"},tintMask:{entry:\"creature-crystal-core/tintMask\",bytes:51868,sha256:\"22ef052c4767e7be3f3e6187fa7e6a1e5baf2a0131d47c45cee616ef082b9df6\"},preview:{entry:\"creature-crystal-core/preview\",bytes:56978,sha256:\"ee40a473889f2844fa35a8057a6001827945dd43863c9c12da092039b2b8c76a\"}}}},{id:\"creature-moon-marble\",displayName:\"Moon Marble\",track:\"yoga\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Marble016\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Marble016\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-moon-marble/basecolor.webp\",bytes:60346,sha256:\"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"},normal:{file:\"textures/creature-moon-marble/normal.webp\",bytes:558,sha256:\"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"},roughness:{file:\"textures/creature-moon-marble/roughness.webp\",bytes:3740,sha256:\"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"},height:{file:\"textures/creature-moon-marble/height.webp\",bytes:54702,sha256:\"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"},tintMask:{file:\"textures/creature-moon-marble/tint-mask.webp\",bytes:74936,sha256:\"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"},preview:{file:\"textures/creature-moon-marble/preview.webp\",bytes:68934,sha256:\"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"}},runtime:{unlock:{level:3,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-moon-marble/basecolor\",bytes:60346,sha256:\"30857f841da460f9f6878279498e11805b26bb1b9387302bd2a74d2f11f850fe\"},normal:{entry:\"creature-moon-marble/normal\",bytes:558,sha256:\"0abacb9ea21b4e643690ae88919a195040b0c0a22994003cddc2ea40a078e242\"},roughness:{entry:\"creature-moon-marble/roughness\",bytes:3740,sha256:\"78bd9171c82cef1e9ab5b6f4df1f0b36021fcb76487af7c187aada745e346795\"},height:{entry:\"creature-moon-marble/height\",bytes:54702,sha256:\"2ba36b84ef5e549ede30e4203ce6e8e2d415d6f0f2c095d40bb0a13aa4cf81bb\"},tintMask:{entry:\"creature-moon-marble/tintMask\",bytes:74936,sha256:\"a72497a56824baa0bab493baae6ce2b814f204b1b870426ff8eccddcc5f46030\"},preview:{entry:\"creature-moon-marble/preview\",bytes:68934,sha256:\"977f080377efdb814dc01a6a825d1df941566fe6298699a9ee191eaafa866553\"}}}},{id:\"creature-fae-petal\",displayName:\"Fae Petal\",track:\"yoga\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"FlowerSet001\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/FlowerSet001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-fae-petal/basecolor.webp\",bytes:17208,sha256:\"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"},normal:{file:\"textures/creature-fae-petal/normal.webp\",bytes:19898,sha256:\"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"},roughness:{file:\"textures/creature-fae-petal/roughness.webp\",bytes:6054,sha256:\"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"},height:{file:\"textures/creature-fae-petal/height.webp\",bytes:6176,sha256:\"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"},ao:{file:\"textures/creature-fae-petal/ao.webp\",bytes:8140,sha256:\"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"},opacity:{file:\"textures/creature-fae-petal/opacity.webp\",bytes:9784,sha256:\"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"},tintMask:{file:\"textures/creature-fae-petal/tint-mask.webp\",bytes:15026,sha256:\"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"},preview:{file:\"textures/creature-fae-petal/preview.webp\",bytes:19274,sha256:\"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"}},runtime:{unlock:{level:5,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-fae-petal/basecolor\",bytes:17208,sha256:\"53bc7d03952fb1244b75ba1e015cf36173c326271005c3330c01fccb63d74946\"},normal:{entry:\"creature-fae-petal/normal\",bytes:19898,sha256:\"bb402d8c39e986a0ba5fd1bbdcbcb8cbbdba29a75d4e932b39c5851b2350f75f\"},roughness:{entry:\"creature-fae-petal/roughness\",bytes:6054,sha256:\"4b66923e2349ed42ebbf202c8a42aa33fb759c1713cea678dbd0ac93bd175aec\"},height:{entry:\"creature-fae-petal/height\",bytes:6176,sha256:\"4dd774718a94f5b87de223c8ab27b18bde4df1249e76f7df7a9627143ca4b830\"},ao:{entry:\"creature-fae-petal/ao\",bytes:8140,sha256:\"9eabbf557041b2bdcc58f52cc5821de36470b43f44aac3a6cb87cee3aee645eb\"},opacity:{entry:\"creature-fae-petal/opacity\",bytes:9784,sha256:\"7e7d2e3432143c925e111a2e763d3230fa88cc2512155059b020d4516d68cbad\"},tintMask:{entry:\"creature-fae-petal/tintMask\",bytes:15026,sha256:\"255ef36fc492b422a991192efead4b414f0a3ccd57bdcd3212033c82a4533608\"},preview:{entry:\"creature-fae-petal/preview\",bytes:19274,sha256:\"afc9fe5d4f588ca9c78e54032aedbdfcd97c991d384f8dbc6ca803f5c6eada5a\"}}}},{id:\"creature-dragon-scale\",displayName:\"Dragon Scale\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"RoofingTiles013A\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/RoofingTiles013A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-dragon-scale/basecolor.webp\",bytes:2668,sha256:\"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"},normal:{file:\"textures/creature-dragon-scale/normal.webp\",bytes:20688,sha256:\"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"},roughness:{file:\"textures/creature-dragon-scale/roughness.webp\",bytes:16142,sha256:\"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"},height:{file:\"textures/creature-dragon-scale/height.webp\",bytes:8400,sha256:\"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"},ao:{file:\"textures/creature-dragon-scale/ao.webp\",bytes:14048,sha256:\"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"},opacity:{file:\"textures/creature-dragon-scale/opacity.webp\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{file:\"textures/creature-dragon-scale/tint-mask.webp\",bytes:33612,sha256:\"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"},preview:{file:\"textures/creature-dragon-scale/preview.webp\",bytes:3430,sha256:\"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"}},runtime:{unlock:{level:1,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-dragon-scale/basecolor\",bytes:2668,sha256:\"663af0ac46b5c88304bf252b913714ee7315062ffaf8d41e1f98437436cfe933\"},normal:{entry:\"creature-dragon-scale/normal\",bytes:20688,sha256:\"9bdd90ed2cbf7eaa94c24f290906c764b4a4c16ceb184114dbb22b749209d3a8\"},roughness:{entry:\"creature-dragon-scale/roughness\",bytes:16142,sha256:\"8edb0acad8dfa41c361067e49cc1aa13a23cd1e179b9fd02621c6a49cf5f4ee6\"},height:{entry:\"creature-dragon-scale/height\",bytes:8400,sha256:\"f53bd1a0068dd2cc2cd31b67f2bd6800680c4d58443418bfdb2f1998ea78fbcb\"},ao:{entry:\"creature-dragon-scale/ao\",bytes:14048,sha256:\"5e9e5483fdea8249867c9532a991b11b2518e1c725e43e84835428d13d0791e5\"},opacity:{entry:\"creature-dragon-scale/opacity\",bytes:528,sha256:\"ce7a484a8cfe1a10a88aa05beaa84c80880572df797dfe8664e43f7a51a32c27\"},tintMask:{entry:\"creature-dragon-scale/tintMask\",bytes:33612,sha256:\"d77eb358cbfb4252db8a6df4ff0536bb5d45e7f74dbac6fa6ead5c2c6fb30adf\"},preview:{entry:\"creature-dragon-scale/preview\",bytes:3430,sha256:\"f2ff67b80aedfbb8c3c5e1e2412b54c6b6d88ba256e80d9604103fe4ac90435b\"}}}},{id:\"creature-bamboo-hide\",displayName:\"Bamboo Hide\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bamboo001A\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bamboo001A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-bamboo-hide/basecolor.webp\",bytes:11654,sha256:\"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"},normal:{file:\"textures/creature-bamboo-hide/normal.webp\",bytes:20194,sha256:\"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"},roughness:{file:\"textures/creature-bamboo-hide/roughness.webp\",bytes:12634,sha256:\"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"},height:{file:\"textures/creature-bamboo-hide/height.webp\",bytes:7926,sha256:\"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"},ao:{file:\"textures/creature-bamboo-hide/ao.webp\",bytes:7788,sha256:\"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"},opacity:{file:\"textures/creature-bamboo-hide/opacity.webp\",bytes:2412,sha256:\"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"},tintMask:{file:\"textures/creature-bamboo-hide/tint-mask.webp\",bytes:37906,sha256:\"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"},preview:{file:\"textures/creature-bamboo-hide/preview.webp\",bytes:14130,sha256:\"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"}},runtime:{unlock:{level:3,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-bamboo-hide/basecolor\",bytes:11654,sha256:\"8a26d7c21793d2d02c4b6d3313ec4fb4646d48dc97d3a3d0e9a51e6787fd5337\"},normal:{entry:\"creature-bamboo-hide/normal\",bytes:20194,sha256:\"0c249768ad2e7285a506a67b932f96e7a0472bec4a5e7b26f0b672ca34eab9b6\"},roughness:{entry:\"creature-bamboo-hide/roughness\",bytes:12634,sha256:\"3e57e47e22afa27294a0137519906146e27444654a0d02765b7c414ede8bc154\"},height:{entry:\"creature-bamboo-hide/height\",bytes:7926,sha256:\"17c7dce94d4eae86698c08537620db4ba94534200acfa3f8cc1323a5c824473b\"},ao:{entry:\"creature-bamboo-hide/ao\",bytes:7788,sha256:\"20df27a385c6cac19c1c083d8938d7c3e76e117e19bc1087c33dd4f4634b3c3c\"},opacity:{entry:\"creature-bamboo-hide/opacity\",bytes:2412,sha256:\"b7de9d512266f259b7910820d03689d0680fea8a7d1f865cfb28dc3010652be7\"},tintMask:{entry:\"creature-bamboo-hide/tintMask\",bytes:37906,sha256:\"e6b2ebfedeb9ab63529780556808f7397d3db969fc5862e78d70e6cbdc78ffed\"},preview:{entry:\"creature-bamboo-hide/preview\",bytes:14130,sha256:\"468e73ebe9c1552645ec4bfa0e9a417a8976210005686772416d8a142b74b3d7\"}}}},{id:\"creature-ivory-chitin\",displayName:\"Ivory Chitin\",track:\"martial-arts\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Marble021\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Marble021\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ivory-chitin/basecolor.webp\",bytes:15470,sha256:\"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"},normal:{file:\"textures/creature-ivory-chitin/normal.webp\",bytes:546,sha256:\"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"},roughness:{file:\"textures/creature-ivory-chitin/roughness.webp\",bytes:3860,sha256:\"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"},height:{file:\"textures/creature-ivory-chitin/height.webp\",bytes:50360,sha256:\"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"},tintMask:{file:\"textures/creature-ivory-chitin/tint-mask.webp\",bytes:97890,sha256:\"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"},preview:{file:\"textures/creature-ivory-chitin/preview.webp\",bytes:19758,sha256:\"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"}},runtime:{unlock:{level:5,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-ivory-chitin/basecolor\",bytes:15470,sha256:\"81219c6653e2322483827286ce187f288c370e953d63768758d5ab1d5f139f01\"},normal:{entry:\"creature-ivory-chitin/normal\",bytes:546,sha256:\"bb0dca1732b8beac8a047157a25c3325d0e82ee854775c9b30a3d0d3bb1c84d4\"},roughness:{entry:\"creature-ivory-chitin/roughness\",bytes:3860,sha256:\"cbe0b85ba9dd43d3a8de3e2b1f49ea26f078836685ab9fcbb6012adaf3186259\"},height:{entry:\"creature-ivory-chitin/height\",bytes:50360,sha256:\"f1baf51d0f8bbebdd08b037b41dc0a52e8cdbe1d3ee3f3c8e229e10ecceaad8b\"},tintMask:{entry:\"creature-ivory-chitin/tintMask\",bytes:97890,sha256:\"dab2a9ef91fbfd6e0563073bcd2594c15a12037a215e5e4a206c850585d0a4a0\"},preview:{entry:\"creature-ivory-chitin/preview\",bytes:19758,sha256:\"57ca0354c5b20b764d90bc4f8d55f0a4c5f505e55f46ac3165f4268ccaf52317\"}}}},{id:\"creature-circuit-hide\",displayName:\"Circuit Hide\",track:\"cardio\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Chip001\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Chip001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-circuit-hide/basecolor.webp\",bytes:40778,sha256:\"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"},normal:{file:\"textures/creature-circuit-hide/normal.webp\",bytes:27864,sha256:\"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"},roughness:{file:\"textures/creature-circuit-hide/roughness.webp\",bytes:26718,sha256:\"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"},height:{file:\"textures/creature-circuit-hide/height.webp\",bytes:23204,sha256:\"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"},metalness:{file:\"textures/creature-circuit-hide/metalness.webp\",bytes:24542,sha256:\"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"},tintMask:{file:\"textures/creature-circuit-hide/tint-mask.webp\",bytes:37986,sha256:\"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"},preview:{file:\"textures/creature-circuit-hide/preview.webp\",bytes:44810,sha256:\"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"}},runtime:{unlock:{level:1,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-circuit-hide/basecolor\",bytes:40778,sha256:\"096cd7675f3b9908bea4f2414e1c2c36d5b7f11c80055647805b0ffea3aeca4d\"},normal:{entry:\"creature-circuit-hide/normal\",bytes:27864,sha256:\"d8064a373c7c347760ecb731bb61b203e1893d8db280062d6bfdc180ebb044d9\"},roughness:{entry:\"creature-circuit-hide/roughness\",bytes:26718,sha256:\"350c942f434549bafd6a47f4e6d4acef03e666eea5f284779cf533591562663d\"},height:{entry:\"creature-circuit-hide/height\",bytes:23204,sha256:\"f31a4005001a2319418f6de50e502f80e48f04c77fd725bd55270843c1d6a049\"},metalness:{entry:\"creature-circuit-hide/metalness\",bytes:24542,sha256:\"7fdd6b09dfdd23b2c362218a3f98a5af3291367c2b3f58da3bd1cd29a37da8b9\"},tintMask:{entry:\"creature-circuit-hide/tintMask\",bytes:37986,sha256:\"efe041e77fbd766eefec21bca9b2ddfedb2acb20d43955ea44a46ad42065f22c\"},preview:{entry:\"creature-circuit-hide/preview\",bytes:44810,sha256:\"2b020253316ab4d3084073de0b90cf4c4575317475207998ee5cf8c4ae093c16\"}}}},{id:\"creature-reactor-rust\",displayName:\"Reactor Rust\",track:\"cardio\",realm:\"sci-fi\",rarity:\"epic\",sourceAssetId:\"Metal063\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Metal063\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-reactor-rust/basecolor.webp\",bytes:11264,sha256:\"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"},normal:{file:\"textures/creature-reactor-rust/normal.webp\",bytes:550,sha256:\"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"},roughness:{file:\"textures/creature-reactor-rust/roughness.webp\",bytes:29148,sha256:\"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"},height:{file:\"textures/creature-reactor-rust/height.webp\",bytes:37754,sha256:\"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"},metalness:{file:\"textures/creature-reactor-rust/metalness.webp\",bytes:43140,sha256:\"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"},tintMask:{file:\"textures/creature-reactor-rust/tint-mask.webp\",bytes:97766,sha256:\"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"},preview:{file:\"textures/creature-reactor-rust/preview.webp\",bytes:17150,sha256:\"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"}},runtime:{unlock:{level:3,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-reactor-rust/basecolor\",bytes:11264,sha256:\"ffdb6f1feeaac227647437e25e4d06291c6ee547061c60b61819277cfc37d932\"},normal:{entry:\"creature-reactor-rust/normal\",bytes:550,sha256:\"f0f42188a939998becdd27cc27174e355f5fe0cffff56f440af5183fce757854\"},roughness:{entry:\"creature-reactor-rust/roughness\",bytes:29148,sha256:\"8a34c7db1239c7127956f7daa263fe89cb5d7f05614938c12b5da82098408907\"},height:{entry:\"creature-reactor-rust/height\",bytes:37754,sha256:\"0ce87e82026e669fb796708337c3e1614cef174134b7ecb9bb901f2fccf439ae\"},metalness:{entry:\"creature-reactor-rust/metalness\",bytes:43140,sha256:\"47ca127726b3e24b6c07713a7f50fe6baf5a3d3e99f925e3c47d0fd5c01a2ab9\"},tintMask:{entry:\"creature-reactor-rust/tintMask\",bytes:97766,sha256:\"9190d9c0246cb5e52f8e2e6ffe9b99f604681bc96c6d9bf86cf5a93a7c4b9abc\"},preview:{entry:\"creature-reactor-rust/preview\",bytes:17150,sha256:\"1537f736edba5d9c5f78988cbb50e49ab83a293b9c94aad49c25fdccad94bfe8\"}}}},{id:\"creature-aero-mesh\",displayName:\"Aero Mesh\",track:\"cardio\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"MetalWalkway013\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/MetalWalkway013\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-aero-mesh/basecolor.webp\",bytes:9048,sha256:\"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"},normal:{file:\"textures/creature-aero-mesh/normal.webp\",bytes:55928,sha256:\"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"},roughness:{file:\"textures/creature-aero-mesh/roughness.webp\",bytes:27652,sha256:\"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"},height:{file:\"textures/creature-aero-mesh/height.webp\",bytes:29354,sha256:\"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"},metalness:{file:\"textures/creature-aero-mesh/metalness.webp\",bytes:1340,sha256:\"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"},opacity:{file:\"textures/creature-aero-mesh/opacity.webp\",bytes:28608,sha256:\"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"},tintMask:{file:\"textures/creature-aero-mesh/tint-mask.webp\",bytes:23612,sha256:\"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"},preview:{file:\"textures/creature-aero-mesh/preview.webp\",bytes:11188,sha256:\"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"}},runtime:{unlock:{level:5,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-aero-mesh/basecolor\",bytes:9048,sha256:\"efd7e746572308d9717ac3ff5f401830ca4202b89e9b731598e0011cff27300f\"},normal:{entry:\"creature-aero-mesh/normal\",bytes:55928,sha256:\"4517755111af640a8fcd23b09769a51d97356b3595320ec249b3e6d0286c28c9\"},roughness:{entry:\"creature-aero-mesh/roughness\",bytes:27652,sha256:\"bda6c12306cb56d53cd964083db281078eaedb32e0588d2e39ff3619c73b4721\"},height:{entry:\"creature-aero-mesh/height\",bytes:29354,sha256:\"0d4e1d671a7d57525e21e35cbfc83d89d824c7b89f3a9c85188be87243afc118\"},metalness:{entry:\"creature-aero-mesh/metalness\",bytes:1340,sha256:\"365eb05114bf220e641ba92ffe9c2032041e050f5fa03e856479edadfca04ce4\"},opacity:{entry:\"creature-aero-mesh/opacity\",bytes:28608,sha256:\"fd306eedd40b7130a73447594b0b2889735a722f85b8fb74e91024e0bb48653e\"},tintMask:{entry:\"creature-aero-mesh/tintMask\",bytes:23612,sha256:\"7d4e51edf8c6e2961a92d10fd204e03676f1a886dcc3f94f80ec7f5bf934a756\"},preview:{entry:\"creature-aero-mesh/preview\",bytes:11188,sha256:\"e249038b305943c5cc58fdaceed7307e77515a33d88c7fb020e832b0ba6a1a75\"}}}},{id:\"creature-zen-sand\",displayName:\"Zen Sand\",track:\"meditation\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Ground097\",collection:\"creature-forged-realms\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Ground097\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-zen-sand/basecolor.webp\",bytes:2566,sha256:\"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"},normal:{file:\"textures/creature-zen-sand/normal.webp\",bytes:25542,sha256:\"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"},roughness:{file:\"textures/creature-zen-sand/roughness.webp\",bytes:9672,sha256:\"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"},height:{file:\"textures/creature-zen-sand/height.webp\",bytes:14040,sha256:\"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"},ao:{file:\"textures/creature-zen-sand/ao.webp\",bytes:2322,sha256:\"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"},tintMask:{file:\"textures/creature-zen-sand/tint-mask.webp\",bytes:27572,sha256:\"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"},preview:{file:\"textures/creature-zen-sand/preview.webp\",bytes:3032,sha256:\"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"}},runtime:{unlock:{level:1,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-zen-sand/basecolor\",bytes:2566,sha256:\"0913f83f5a430d643903700a9ddba413618b974d1284ab5bb33235e09bef6d6f\"},normal:{entry:\"creature-zen-sand/normal\",bytes:25542,sha256:\"acf6ca0faf928441a99d871c6bcc05fe1b1644e8cd5a4cd12c807b8572aa5219\"},roughness:{entry:\"creature-zen-sand/roughness\",bytes:9672,sha256:\"89cd80ff7f889b753fb059e427bc082c02f223da30cf388d3ac41c1ada5e3cc9\"},height:{entry:\"creature-zen-sand/height\",bytes:14040,sha256:\"55c05b9ab62b320dc74c5e37d871ec9f2a63804e654b3bac2d3df4dc045a9246\"},ao:{entry:\"creature-zen-sand/ao\",bytes:2322,sha256:\"0d241c3387cc7d877016d4db9225cc0aae6fed59f7d9fff78797f6b204466697\"},tintMask:{entry:\"creature-zen-sand/tintMask\",bytes:27572,sha256:\"92f6c6974aa9641af624ac98c6acc383ca0094ffd8c60c43c81c4e94781d1441\"},preview:{entry:\"creature-zen-sand/preview\",bytes:3032,sha256:\"965d0ecf578a8d912910f99fba4bc09e5a61ba069d5fb2be14cbd02e4d133a86\"}}}},{id:\"creature-river-stone\",displayName:\"River Stone\",track:\"meditation\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rock020\",collection:\"creature-forged-realms\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rock020\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-river-stone/basecolor.webp\",bytes:49168,sha256:\"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"},normal:{file:\"textures/creature-river-stone/normal.webp\",bytes:99336,sha256:\"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"},roughness:{file:\"textures/creature-river-stone/roughness.webp\",bytes:13096,sha256:\"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"},height:{file:\"textures/creature-river-stone/height.webp\",bytes:9202,sha256:\"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"},ao:{file:\"textures/creature-river-stone/ao.webp\",bytes:22322,sha256:\"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"},tintMask:{file:\"textures/creature-river-stone/tint-mask.webp\",bytes:76542,sha256:\"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"},preview:{file:\"textures/creature-river-stone/preview.webp\",bytes:57812,sha256:\"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"}},runtime:{unlock:{level:3,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-river-stone/basecolor\",bytes:49168,sha256:\"e8cdd973998441e09a8dc4b44ff9da77dec6fad11f8523138ced65b1e9e4a3f9\"},normal:{entry:\"creature-river-stone/normal\",bytes:99336,sha256:\"f31db446c61f36d6775548bff4d04ea21b54dfc1e3271d39b719ab6c48b9c208\"},roughness:{entry:\"creature-river-stone/roughness\",bytes:13096,sha256:\"65ca034f5f657db0fcf502c93b2bacc3f1a66a572ed3e24d09ba3fadbbb80757\"},height:{entry:\"creature-river-stone/height\",bytes:9202,sha256:\"8e5b35579dce02cbb1fbfa415762a6b65724ef86898c3cc53de36b37398f90ce\"},ao:{entry:\"creature-river-stone/ao\",bytes:22322,sha256:\"985e2218cda815c2549c0a926df7d26f050ba1e34eccb4a859fba17c6ec9489e\"},tintMask:{entry:\"creature-river-stone/tintMask\",bytes:76542,sha256:\"b603234beb199542b602fd2a8823ba1416f4be342b69168bc85e6bfc15a76e4f\"},preview:{entry:\"creature-river-stone/preview\",bytes:57812,sha256:\"09b86e6e3c10f2c1581c3a46507d593fe561e8bfed7628d35665d695ae4926a7\"}}}},{id:\"creature-biolume-moss\",displayName:\"Biolume Moss\",track:\"meditation\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Moss002\",collection:\"creature-forged-realms\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Moss002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-biolume-moss/basecolor.webp\",bytes:73648,sha256:\"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"},normal:{file:\"textures/creature-biolume-moss/normal.webp\",bytes:87824,sha256:\"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"},roughness:{file:\"textures/creature-biolume-moss/roughness.webp\",bytes:39816,sha256:\"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"},height:{file:\"textures/creature-biolume-moss/height.webp\",bytes:25618,sha256:\"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"},ao:{file:\"textures/creature-biolume-moss/ao.webp\",bytes:78346,sha256:\"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"},tintMask:{file:\"textures/creature-biolume-moss/tint-mask.webp\",bytes:99630,sha256:\"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"},preview:{file:\"textures/creature-biolume-moss/preview.webp\",bytes:77278,sha256:\"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"}},runtime:{unlock:{level:5,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-biolume-moss/basecolor\",bytes:73648,sha256:\"269fd39b656f9e882f9b6ce516b3f0ae5ea9017a03b6851260618786f9263be6\"},normal:{entry:\"creature-biolume-moss/normal\",bytes:87824,sha256:\"9d94ac281cdfc8297c1dd5b49be3270eee9464bfa9a0e2c39339efab2c89a21b\"},roughness:{entry:\"creature-biolume-moss/roughness\",bytes:39816,sha256:\"4583d9d7365e367dde4f2dea9a4cbc3f4e96670a46fa16d808f8378bb2a9ea62\"},height:{entry:\"creature-biolume-moss/height\",bytes:25618,sha256:\"f4f5a28835d31558f808f1889a69753b478f09a169d28ac51d44bc5971776836\"},ao:{entry:\"creature-biolume-moss/ao\",bytes:78346,sha256:\"99d195c2cc601acfbf776b793ca98cb79eceab56bdafc50b788e3231734cf773\"},tintMask:{entry:\"creature-biolume-moss/tintMask\",bytes:99630,sha256:\"45f5f81dd4fe98c3df7bfd5fd912fbd239d2e53e2d8bde98b3f724c83eefebda\"},preview:{entry:\"creature-biolume-moss/preview\",bytes:77278,sha256:\"4da02ca977708a4ebfa784dd8d51012455c1d85071a6074748db15f5c3d9744d\"}}}},{id:\"creature-mirror-knight\",displayName:\"Mirror Knight\",track:\"chest\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Metal048A\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Metal048A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mirror-knight/basecolor.webp\",bytes:998,sha256:\"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"},normal:{file:\"textures/creature-mirror-knight/normal.webp\",bytes:594,sha256:\"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"},roughness:{file:\"textures/creature-mirror-knight/roughness.webp\",bytes:732,sha256:\"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"},height:{file:\"textures/creature-mirror-knight/height.webp\",bytes:526,sha256:\"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"},metalness:{file:\"textures/creature-mirror-knight/metalness.webp\",bytes:532,sha256:\"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"},tintMask:{file:\"textures/creature-mirror-knight/tint-mask.webp\",bytes:67594,sha256:\"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"},preview:{file:\"textures/creature-mirror-knight/preview.webp\",bytes:1298,sha256:\"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"}},runtime:{unlock:{level:1,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-mirror-knight/basecolor\",bytes:998,sha256:\"cf989101d5f5483fe0618ff51ec7b02ff6ab0afa5f01ff3f7fa2b3bba49e0f3f\"},normal:{entry:\"creature-mirror-knight/normal\",bytes:594,sha256:\"5a32ff9d82106a7811bf45e72645c15d9a8e2609a262c106ffb75126f8911156\"},roughness:{entry:\"creature-mirror-knight/roughness\",bytes:732,sha256:\"cc0c496cd6cc5503fab83d82acd8ae3f1a7b5b25657ee25fffbed6308e26b5d5\"},height:{entry:\"creature-mirror-knight/height\",bytes:526,sha256:\"abb51cd0690994d97aff0ad32ab12896592506ffd61886f569963b2f71e20c0f\"},metalness:{entry:\"creature-mirror-knight/metalness\",bytes:532,sha256:\"cf3db3d5638b9b2a0d343c9db9efb673235385846a75c8edd0a88a7fb6bec60a\"},tintMask:{entry:\"creature-mirror-knight/tintMask\",bytes:67594,sha256:\"b444a54a2f7db6df7e4afc8c80fecd82887db13cb7052f78fafd1e2593d31d06\"},preview:{entry:\"creature-mirror-knight/preview\",bytes:1298,sha256:\"ae8103593152bf336133fc8752255d8b40652633bb8bab142cf9c6c24d7927f6\"}}}},{id:\"creature-tarnished-crown\",displayName:\"Tarnished Crown\",track:\"chest\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Metal048C\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Metal048C\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-tarnished-crown/basecolor.webp\",bytes:10056,sha256:\"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"},normal:{file:\"textures/creature-tarnished-crown/normal.webp\",bytes:902,sha256:\"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"},roughness:{file:\"textures/creature-tarnished-crown/roughness.webp\",bytes:23264,sha256:\"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"},height:{file:\"textures/creature-tarnished-crown/height.webp\",bytes:6612,sha256:\"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"},metalness:{file:\"textures/creature-tarnished-crown/metalness.webp\",bytes:9582,sha256:\"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"},tintMask:{file:\"textures/creature-tarnished-crown/tint-mask.webp\",bytes:48438,sha256:\"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"},preview:{file:\"textures/creature-tarnished-crown/preview.webp\",bytes:11978,sha256:\"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"}},runtime:{unlock:{level:3,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-tarnished-crown/basecolor\",bytes:10056,sha256:\"454f8c9a4fd90d206920b5c41beb67ddc47d36867a2d36a48798b7ddfc630acf\"},normal:{entry:\"creature-tarnished-crown/normal\",bytes:902,sha256:\"7542c0e567980a2a46af77aeadd5c76784dc4347c4fc691e87b9c751c5a6d1af\"},roughness:{entry:\"creature-tarnished-crown/roughness\",bytes:23264,sha256:\"3be7019d924a77751fbbf4665ea1db70ebccc721303c8d247094af69a16df282\"},height:{entry:\"creature-tarnished-crown/height\",bytes:6612,sha256:\"9c8916c0d96e11bbc8e1d67b8d343fd2f99d8f8d97ff49469ee319cd36c19239\"},metalness:{entry:\"creature-tarnished-crown/metalness\",bytes:9582,sha256:\"b9fcaedd9ea3a6871f2d74879ca30ca16f75e359544654ee54ee5792ba3887f5\"},tintMask:{entry:\"creature-tarnished-crown/tintMask\",bytes:48438,sha256:\"caf136c6c920e0d4e7f52274c82d49b8e9474ec7d3c85534fae44cf7a50700d5\"},preview:{entry:\"creature-tarnished-crown/preview\",bytes:11978,sha256:\"ef892c02b1101c732a7f015956460d93fe490fc0507a4263d723c0bc4f0dbbf4\"}}}},{id:\"creature-golden-scarab\",displayName:\"Golden Scarab\",track:\"chest\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Metal007\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Metal007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-golden-scarab/basecolor.webp\",bytes:18264,sha256:\"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"},normal:{file:\"textures/creature-golden-scarab/normal.webp\",bytes:1042,sha256:\"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"},roughness:{file:\"textures/creature-golden-scarab/roughness.webp\",bytes:15890,sha256:\"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"},height:{file:\"textures/creature-golden-scarab/height.webp\",bytes:1714,sha256:\"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"},metalness:{file:\"textures/creature-golden-scarab/metalness.webp\",bytes:1280,sha256:\"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"},tintMask:{file:\"textures/creature-golden-scarab/tint-mask.webp\",bytes:65652,sha256:\"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"},preview:{file:\"textures/creature-golden-scarab/preview.webp\",bytes:22470,sha256:\"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"}},runtime:{unlock:{level:5,track:\"chest\"},pack:\"track-chest\",bundle:\"assets/chest.m5bundle\",maps:{basecolor:{entry:\"creature-golden-scarab/basecolor\",bytes:18264,sha256:\"2daf24017e791d9cc0aa9cc72faf573da795869f14ecf318afee20f960bbfb83\"},normal:{entry:\"creature-golden-scarab/normal\",bytes:1042,sha256:\"a709a2db3fe891ed772681bfaa2a15d31dd1be3dd4e363b5a596b56fe5f9ad2c\"},roughness:{entry:\"creature-golden-scarab/roughness\",bytes:15890,sha256:\"114f37f6188582bfa8b3dc368f523139c85eeca8383c90e812b40e7e2e96e0a1\"},height:{entry:\"creature-golden-scarab/height\",bytes:1714,sha256:\"36bb936c56b9bc9f9edfe115c07c6f45d9be80864ab3f1aace77e5477ca92a65\"},metalness:{entry:\"creature-golden-scarab/metalness\",bytes:1280,sha256:\"d01c0d2347828b7537974009e32fb25d455bb447ea01f459bdc02840e81d3c31\"},tintMask:{entry:\"creature-golden-scarab/tintMask\",bytes:65652,sha256:\"43a5c55a78fa3ed7f775c0ca959082b2463f0e48ae75d273468169abf0f50c5d\"},preview:{entry:\"creature-golden-scarab/preview\",bytes:22470,sha256:\"99ddd90d95b9b01691fad6aff7e60cef3335c570d3bb30641fa73b29af0631b7\"}}}},{id:\"creature-emberflow\",displayName:\"Emberflow\",track:\"quads\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Lava001\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Lava001\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-emberflow/basecolor.webp\",bytes:55502,sha256:\"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"},normal:{file:\"textures/creature-emberflow/normal.webp\",bytes:105598,sha256:\"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"},roughness:{file:\"textures/creature-emberflow/roughness.webp\",bytes:12882,sha256:\"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"},height:{file:\"textures/creature-emberflow/height.webp\",bytes:13998,sha256:\"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"},emissive:{file:\"textures/creature-emberflow/emissive.webp\",bytes:52850,sha256:\"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"},tintMask:{file:\"textures/creature-emberflow/tint-mask.webp\",bytes:51236,sha256:\"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"},preview:{file:\"textures/creature-emberflow/preview.webp\",bytes:57834,sha256:\"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"}},runtime:{unlock:{level:1,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-emberflow/basecolor\",bytes:55502,sha256:\"8d7c4615eb329d5255a75792a997eb8ecae3102fb0782a5a2c46e11f9b4341fe\"},normal:{entry:\"creature-emberflow/normal\",bytes:105598,sha256:\"1873609c656f2b3764003fbc9b97b91ad3f5e3e69a2aaa4d201d9e7cdc4aec4c\"},roughness:{entry:\"creature-emberflow/roughness\",bytes:12882,sha256:\"d48539733cedaf93bdad6b4c4c30708479a743c4fcbef499b3e362e312d021e0\"},height:{entry:\"creature-emberflow/height\",bytes:13998,sha256:\"306286c666118aa7da6fc4b8f769a9b3735b40c0003d154d942ca9363f0acb46\"},emissive:{entry:\"creature-emberflow/emissive\",bytes:52850,sha256:\"bda5fa0ee23d6b225e2782f34061a4e017831e2e78287a3d781e1a2ac02cd01d\"},tintMask:{entry:\"creature-emberflow/tintMask\",bytes:51236,sha256:\"5b3f83b8fa0c50e04da28b1f8a6ad0789cd376613700502a023e1775b3ec66ac\"},preview:{entry:\"creature-emberflow/preview\",bytes:57834,sha256:\"e4df8266f2fadc583a59d6357a024597de261bfb24bb316c7933ead30de08530\"}}}},{id:\"creature-volcanic-heart\",displayName:\"Volcanic Heart\",track:\"quads\",realm:\"hybrid\",rarity:\"epic\",sourceAssetId:\"Lava005\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Lava005\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-volcanic-heart/basecolor.webp\",bytes:52804,sha256:\"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"},normal:{file:\"textures/creature-volcanic-heart/normal.webp\",bytes:54736,sha256:\"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"},roughness:{file:\"textures/creature-volcanic-heart/roughness.webp\",bytes:20200,sha256:\"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"},height:{file:\"textures/creature-volcanic-heart/height.webp\",bytes:11990,sha256:\"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"},emissive:{file:\"textures/creature-volcanic-heart/emissive.webp\",bytes:61420,sha256:\"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"},tintMask:{file:\"textures/creature-volcanic-heart/tint-mask.webp\",bytes:53772,sha256:\"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"},preview:{file:\"textures/creature-volcanic-heart/preview.webp\",bytes:51986,sha256:\"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"}},runtime:{unlock:{level:3,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-volcanic-heart/basecolor\",bytes:52804,sha256:\"24781f33f16d0161ca0485e6a732e97c1b9d4a91540c189bf8f748769a27f635\"},normal:{entry:\"creature-volcanic-heart/normal\",bytes:54736,sha256:\"ddd24a1373442f4b842c73a45b771aa149357a9d82faf8be149a61c0b9348418\"},roughness:{entry:\"creature-volcanic-heart/roughness\",bytes:20200,sha256:\"cf52abbc709aefd88e4329ff6804d850235f0532933ec1d3cd103f0016daabfe\"},height:{entry:\"creature-volcanic-heart/height\",bytes:11990,sha256:\"467c8ffc3521c5ca0450ebc4160b392c1f758d4d7222abbc2d3845006af5fe98\"},emissive:{entry:\"creature-volcanic-heart/emissive\",bytes:61420,sha256:\"e2651a8d3fa04f74745ccad8f023e5bbe5129e500002f88fb3097fe9ae3aa06f\"},tintMask:{entry:\"creature-volcanic-heart/tintMask\",bytes:53772,sha256:\"eebca000a86cc39d9763ce73e65b718bceda6680db69695b2cf997d625db79cc\"},preview:{entry:\"creature-volcanic-heart/preview\",bytes:51986,sha256:\"53c8262cb4b87704104d0bb9e484a5140c839a3bf051a0adbd962306c6e06b84\"}}}},{id:\"creature-aurora-ice\",displayName:\"Aurora Ice\",track:\"quads\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Ice004\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Ice004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-aurora-ice/basecolor.webp\",bytes:28102,sha256:\"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"},normal:{file:\"textures/creature-aurora-ice/normal.webp\",bytes:9418,sha256:\"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"},roughness:{file:\"textures/creature-aurora-ice/roughness.webp\",bytes:16912,sha256:\"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"},height:{file:\"textures/creature-aurora-ice/height.webp\",bytes:24584,sha256:\"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"},tintMask:{file:\"textures/creature-aurora-ice/tint-mask.webp\",bytes:33120,sha256:\"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"},preview:{file:\"textures/creature-aurora-ice/preview.webp\",bytes:33622,sha256:\"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"}},runtime:{unlock:{level:5,track:\"quads\"},pack:\"track-quads\",bundle:\"assets/quads.m5bundle\",maps:{basecolor:{entry:\"creature-aurora-ice/basecolor\",bytes:28102,sha256:\"a0538e8060a8e9c00a04c5f7ac9234416cd40f85a1f5be2e6a7419d890eadf47\"},normal:{entry:\"creature-aurora-ice/normal\",bytes:9418,sha256:\"c933ddbc26012694f81f9533fcf670782c573daf6c300317b0ee70807d7474f6\"},roughness:{entry:\"creature-aurora-ice/roughness\",bytes:16912,sha256:\"aa50ef45b5034739a1f48d34a8c842ad2444596d1bb7f60ce3f71be88c1dff65\"},height:{entry:\"creature-aurora-ice/height\",bytes:24584,sha256:\"ff582e80c4af2ac6b52e235055a6d19ddc34213b22fe6ac90beb6c09b8788ef9\"},tintMask:{entry:\"creature-aurora-ice/tintMask\",bytes:33120,sha256:\"b89c0d07aae4bccea7ac26b589e72ff5ececca87bc166d2dc23406dd943d51b4\"},preview:{entry:\"creature-aurora-ice/preview\",bytes:33622,sha256:\"fec978367e0c56323ce5b3f91bef4c1a624e8934e6d2933174b957264fc5229a\"}}}},{id:\"creature-ironbark\",displayName:\"Ironbark\",track:\"glutes\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Bark014\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Bark014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-ironbark/basecolor.webp\",bytes:60524,sha256:\"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"},normal:{file:\"textures/creature-ironbark/normal.webp\",bytes:39240,sha256:\"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"},roughness:{file:\"textures/creature-ironbark/roughness.webp\",bytes:61578,sha256:\"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"},height:{file:\"textures/creature-ironbark/height.webp\",bytes:11624,sha256:\"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"},ao:{file:\"textures/creature-ironbark/ao.webp\",bytes:46376,sha256:\"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"},tintMask:{file:\"textures/creature-ironbark/tint-mask.webp\",bytes:71852,sha256:\"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"},preview:{file:\"textures/creature-ironbark/preview.webp\",bytes:68160,sha256:\"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"}},runtime:{unlock:{level:1,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-ironbark/basecolor\",bytes:60524,sha256:\"60f5086a4946ae52491ea871bbec6dde8a4209cad3dc6711c66d0f520e9bd1c7\"},normal:{entry:\"creature-ironbark/normal\",bytes:39240,sha256:\"9e83a63d061651cfaa0892d55ed76762d8493589327db6fb2724254afffad688\"},roughness:{entry:\"creature-ironbark/roughness\",bytes:61578,sha256:\"ff1bfd55ebc76fc610082d172138cc5b971c2de306a7626696fddd2ac2c0e304\"},height:{entry:\"creature-ironbark/height\",bytes:11624,sha256:\"24dca043922c977d7cd8b58a7a9f8aa5132919693a5d666e4a358a3216a66b15\"},ao:{entry:\"creature-ironbark/ao\",bytes:46376,sha256:\"3df1b05d4423870ddf7ab97338ad860ec2f87924492fa008c28e4ac3225c1484\"},tintMask:{entry:\"creature-ironbark/tintMask\",bytes:71852,sha256:\"d1e6acc31473ff62d8dd0022ddad5b542067eea35877b051a1460929d7934ee4\"},preview:{entry:\"creature-ironbark/preview\",bytes:68160,sha256:\"8ba5961f132720e02a894e9da7228305ca5ee4a2c2fc720406f8505055a56dd6\"}}}},{id:\"creature-oakscale\",displayName:\"Oakscale\",track:\"glutes\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Bark012\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Bark012\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-oakscale/basecolor.webp\",bytes:71646,sha256:\"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"},normal:{file:\"textures/creature-oakscale/normal.webp\",bytes:122322,sha256:\"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"},roughness:{file:\"textures/creature-oakscale/roughness.webp\",bytes:56118,sha256:\"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"},height:{file:\"textures/creature-oakscale/height.webp\",bytes:36178,sha256:\"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"},ao:{file:\"textures/creature-oakscale/ao.webp\",bytes:59772,sha256:\"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"},tintMask:{file:\"textures/creature-oakscale/tint-mask.webp\",bytes:89244,sha256:\"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"},preview:{file:\"textures/creature-oakscale/preview.webp\",bytes:80436,sha256:\"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"}},runtime:{unlock:{level:3,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-oakscale/basecolor\",bytes:71646,sha256:\"8d3becd0bdfbd0af4b40925b5f8f2d29ea593f1116656a871e6f1c84be542b1a\"},normal:{entry:\"creature-oakscale/normal\",bytes:122322,sha256:\"3ea9636011eea9ee7e49d4cfa6010271004abb65356a9c700d9ef3a9ddbdf8c1\"},roughness:{entry:\"creature-oakscale/roughness\",bytes:56118,sha256:\"99e51498071e74aa167b77ec90fe99834061c115f521b11aedb61e742ef15883\"},height:{entry:\"creature-oakscale/height\",bytes:36178,sha256:\"92ee5632dde0de291489635709bc55a45ac89d3cb6f834362c52ca1546954303\"},ao:{entry:\"creature-oakscale/ao\",bytes:59772,sha256:\"fdf1d31853c9c9629dddd9c705f24058b8b75ab120ac6d7a017382aa3e45917d\"},tintMask:{entry:\"creature-oakscale/tintMask\",bytes:89244,sha256:\"2d996f056ca9fa7a6b1ecd25d85fca69c1de2c609dc44e6caea2873a03452060\"},preview:{entry:\"creature-oakscale/preview\",bytes:80436,sha256:\"8f988fc7f1ebbae37fcb79fd405acbc22d377985fe7fa26e1eefb7f0dbc7d157\"}}}},{id:\"creature-mosswood\",displayName:\"Mosswood\",track:\"glutes\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Bark008\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Bark008\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-mosswood/basecolor.webp\",bytes:59368,sha256:\"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"},normal:{file:\"textures/creature-mosswood/normal.webp\",bytes:115452,sha256:\"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"},roughness:{file:\"textures/creature-mosswood/roughness.webp\",bytes:14720,sha256:\"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"},height:{file:\"textures/creature-mosswood/height.webp\",bytes:14968,sha256:\"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"},ao:{file:\"textures/creature-mosswood/ao.webp\",bytes:30290,sha256:\"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"},tintMask:{file:\"textures/creature-mosswood/tint-mask.webp\",bytes:71764,sha256:\"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"},preview:{file:\"textures/creature-mosswood/preview.webp\",bytes:68928,sha256:\"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"}},runtime:{unlock:{level:5,track:\"glutes\"},pack:\"track-glutes\",bundle:\"assets/glutes.m5bundle\",maps:{basecolor:{entry:\"creature-mosswood/basecolor\",bytes:59368,sha256:\"890bb90b337f9a4663ed45f17530a98f5bc40f04abd9a29cc780b5ac830e49e7\"},normal:{entry:\"creature-mosswood/normal\",bytes:115452,sha256:\"20240bb5ea158afe45c86d472671e936b74115dcc7dbab57e512f3614bca4b65\"},roughness:{entry:\"creature-mosswood/roughness\",bytes:14720,sha256:\"52fa0eddcfb1857471dfc86cce4be8a75dd36181f9ed10c0a0252257022611b8\"},height:{entry:\"creature-mosswood/height\",bytes:14968,sha256:\"3ca291939907de9d57ae256ee4a017ab3f225ef10ef246b1769a53d4368fe38e\"},ao:{entry:\"creature-mosswood/ao\",bytes:30290,sha256:\"5d159bd50f689dd5550f5ddf1a1b043f79e3f17b72f50ddbfdb7af704967e84a\"},tintMask:{entry:\"creature-mosswood/tintMask\",bytes:71764,sha256:\"12749b87113aadaa3e904354fd91a46d0b2f7b1fd3bbb080f68ebdb27c12fc69\"},preview:{entry:\"creature-mosswood/preview\",bytes:68928,sha256:\"cd7f6299839d2cd701535eba1526d1054b9f763539668a9aa7b6dd7d7460ea71\"}}}},{id:\"creature-blood-onyx\",displayName:\"Blood Onyx\",track:\"arms\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Onyx010\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Onyx010\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-blood-onyx/basecolor.webp\",bytes:49258,sha256:\"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"},normal:{file:\"textures/creature-blood-onyx/normal.webp\",bytes:570,sha256:\"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"},roughness:{file:\"textures/creature-blood-onyx/roughness.webp\",bytes:3160,sha256:\"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"},height:{file:\"textures/creature-blood-onyx/height.webp\",bytes:2910,sha256:\"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"},tintMask:{file:\"textures/creature-blood-onyx/tint-mask.webp\",bytes:55394,sha256:\"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"},preview:{file:\"textures/creature-blood-onyx/preview.webp\",bytes:56594,sha256:\"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"}},runtime:{unlock:{level:1,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-blood-onyx/basecolor\",bytes:49258,sha256:\"1e4ece1d137a8e59717dedb50696af52443a48bca8f6ed704c3bcb477e237bd2\"},normal:{entry:\"creature-blood-onyx/normal\",bytes:570,sha256:\"89b1d318ca9930effc69d7231739e063be9589e63823131668f65fcb7151fa25\"},roughness:{entry:\"creature-blood-onyx/roughness\",bytes:3160,sha256:\"933156eac22cd38b2a55f39ff1af0eeb4cfd5fa3e09e29677c108d7ca0e5fd4e\"},height:{entry:\"creature-blood-onyx/height\",bytes:2910,sha256:\"028bb3656a9f50c23a20d3985793c1d1629ad2164d8ec53a04fc333944d41dd9\"},tintMask:{entry:\"creature-blood-onyx/tintMask\",bytes:55394,sha256:\"4d6ba964d297a4329ffdf69a3dbede66982b83703d2e44187ee233f062cbe69d\"},preview:{entry:\"creature-blood-onyx/preview\",bytes:56594,sha256:\"d6b36ee15022c78d7b968d51451e912ac91e4952fb46d8d9fe0a017d72a5fe31\"}}}},{id:\"creature-honey-crystal\",displayName:\"Honey Crystal\",track:\"arms\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Onyx007\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Onyx007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-honey-crystal/basecolor.webp\",bytes:22816,sha256:\"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"},normal:{file:\"textures/creature-honey-crystal/normal.webp\",bytes:560,sha256:\"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"},roughness:{file:\"textures/creature-honey-crystal/roughness.webp\",bytes:3020,sha256:\"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"},height:{file:\"textures/creature-honey-crystal/height.webp\",bytes:2954,sha256:\"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"},tintMask:{file:\"textures/creature-honey-crystal/tint-mask.webp\",bytes:34036,sha256:\"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"},preview:{file:\"textures/creature-honey-crystal/preview.webp\",bytes:26820,sha256:\"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"}},runtime:{unlock:{level:3,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-honey-crystal/basecolor\",bytes:22816,sha256:\"98aa336fb9c96553927f664e9b78d7664389e93a81555ef84870111fa958c67a\"},normal:{entry:\"creature-honey-crystal/normal\",bytes:560,sha256:\"bf3e55e83aeb3bce5033fd00ac0fb19ac787e3ca01d8573930809f453872035a\"},roughness:{entry:\"creature-honey-crystal/roughness\",bytes:3020,sha256:\"b0000cf36bad38e720c4b5cbffa50bf28ee5a251b2cf0117ed531803a55dc886\"},height:{entry:\"creature-honey-crystal/height\",bytes:2954,sha256:\"0a16ca42ec013f3eafc4342ed4940eb005379adbd6d879720a0969b5a37e8e89\"},tintMask:{entry:\"creature-honey-crystal/tintMask\",bytes:34036,sha256:\"71cca8020dd06078c658313ddadf4d214f870a5264891bdb1f93d1773d03c789\"},preview:{entry:\"creature-honey-crystal/preview\",bytes:26820,sha256:\"6c9b3fcc76753d66a1059cfce8c6b0a1594eca5077ed9a55cfa747ce1892bae2\"}}}},{id:\"creature-void-mirror\",displayName:\"Void Mirror\",track:\"arms\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Onyx014\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Onyx014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-void-mirror/basecolor.webp\",bytes:53930,sha256:\"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"},normal:{file:\"textures/creature-void-mirror/normal.webp\",bytes:576,sha256:\"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"},roughness:{file:\"textures/creature-void-mirror/roughness.webp\",bytes:3126,sha256:\"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"},height:{file:\"textures/creature-void-mirror/height.webp\",bytes:3520,sha256:\"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"},tintMask:{file:\"textures/creature-void-mirror/tint-mask.webp\",bytes:62026,sha256:\"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"},preview:{file:\"textures/creature-void-mirror/preview.webp\",bytes:61294,sha256:\"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"}},runtime:{unlock:{level:5,track:\"arms\"},pack:\"track-arms\",bundle:\"assets/arms.m5bundle\",maps:{basecolor:{entry:\"creature-void-mirror/basecolor\",bytes:53930,sha256:\"cb69edc751c1c91fd28bd9904ad3c097d8dcf6edaaa570694c3fb9d6d6422a9d\"},normal:{entry:\"creature-void-mirror/normal\",bytes:576,sha256:\"17b7ad457b39e89d3b2da88be6dd5af7465bfd2a687e6e9574dd876d1c7f0d8c\"},roughness:{entry:\"creature-void-mirror/roughness\",bytes:3126,sha256:\"616b3ba7e46a502a1bcfe1c280e30dd8c743f7e18d7f51aea0fec96372e9e14c\"},height:{entry:\"creature-void-mirror/height\",bytes:3520,sha256:\"511981c03b2b6d5113991eeb75a42ffdda63e10637f05485fec7cbe6a2340df5\"},tintMask:{entry:\"creature-void-mirror/tintMask\",bytes:62026,sha256:\"26cb90d6461d7324ab564e1e3bc9b587b7429cd5f4aad544ba0629a7b641b405\"},preview:{entry:\"creature-void-mirror/preview\",bytes:61294,sha256:\"5d17783eb003707e0ee546300ccd46d645db2b6743e0f68d7ed7c965440fd2d9\"}}}},{id:\"creature-snow-wraith\",displayName:\"Snow Wraith\",track:\"yoga\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Snow014\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Snow014\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-snow-wraith/basecolor.webp\",bytes:6634,sha256:\"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"},normal:{file:\"textures/creature-snow-wraith/normal.webp\",bytes:25340,sha256:\"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"},roughness:{file:\"textures/creature-snow-wraith/roughness.webp\",bytes:11026,sha256:\"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"},height:{file:\"textures/creature-snow-wraith/height.webp\",bytes:11568,sha256:\"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"},ao:{file:\"textures/creature-snow-wraith/ao.webp\",bytes:4376,sha256:\"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"},tintMask:{file:\"textures/creature-snow-wraith/tint-mask.webp\",bytes:47502,sha256:\"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"},preview:{file:\"textures/creature-snow-wraith/preview.webp\",bytes:8238,sha256:\"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"}},runtime:{unlock:{level:1,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-snow-wraith/basecolor\",bytes:6634,sha256:\"b44309b17f88a5dfe5afb6ae11d38eff932fc4ca31cf57de57dd177c7ad690cc\"},normal:{entry:\"creature-snow-wraith/normal\",bytes:25340,sha256:\"c660422b765aca94ffbe27547a70572642798298148e138a175c775efb8d7b83\"},roughness:{entry:\"creature-snow-wraith/roughness\",bytes:11026,sha256:\"cf39931b6ecaacadf1f308870a1d6e1eb45a73e2b46e117f4b4e0a76d7b44ce1\"},height:{entry:\"creature-snow-wraith/height\",bytes:11568,sha256:\"6075c35c42067b2b476a0ba6aae9c727b946ae75b3afd5b6e168d9bd3f51385b\"},ao:{entry:\"creature-snow-wraith/ao\",bytes:4376,sha256:\"08233059a6af6b3c8c14077b17b6d7325895eda0b725b91e55fcead6f887c042\"},tintMask:{entry:\"creature-snow-wraith/tintMask\",bytes:47502,sha256:\"21fa7be4a5d63492a2a37787d68d3ceb586c995715fa1668f07f338998ef7b1e\"},preview:{entry:\"creature-snow-wraith/preview\",bytes:8238,sha256:\"b3ad0d3e51855f8bc66f7f2064d4a8e220b9bd6bbb85113ce7e12249f3b4c151\"}}}},{id:\"creature-moon-drift\",displayName:\"Moon Drift\",track:\"yoga\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Snow008A\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Snow008A\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-moon-drift/basecolor.webp\",bytes:1988,sha256:\"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"},normal:{file:\"textures/creature-moon-drift/normal.webp\",bytes:19748,sha256:\"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"},roughness:{file:\"textures/creature-moon-drift/roughness.webp\",bytes:42334,sha256:\"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"},height:{file:\"textures/creature-moon-drift/height.webp\",bytes:10336,sha256:\"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"},ao:{file:\"textures/creature-moon-drift/ao.webp\",bytes:5248,sha256:\"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"},tintMask:{file:\"textures/creature-moon-drift/tint-mask.webp\",bytes:62850,sha256:\"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"},preview:{file:\"textures/creature-moon-drift/preview.webp\",bytes:2558,sha256:\"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"}},runtime:{unlock:{level:3,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-moon-drift/basecolor\",bytes:1988,sha256:\"a0a0deef40357c248c3cd25b62c94196dee75bda282579f2f0af607091ecddd7\"},normal:{entry:\"creature-moon-drift/normal\",bytes:19748,sha256:\"b41d538eb2be50037fc7646838ff397a30bad39e2c215c8e6ec0f7cc5aab3abb\"},roughness:{entry:\"creature-moon-drift/roughness\",bytes:42334,sha256:\"940f50573bb0c93f00b1716d5fc8351aaea5cfd688248ab7e3b69555a68fedf4\"},height:{entry:\"creature-moon-drift/height\",bytes:10336,sha256:\"00cda68ddb767c0578a91423d56f9f0ca59a30c24a5663c2c720a135a522f8dc\"},ao:{entry:\"creature-moon-drift/ao\",bytes:5248,sha256:\"638072ac50033602f96649275c9d1ff2cc406c5a371fdaac55408ff476dd03a5\"},tintMask:{entry:\"creature-moon-drift/tintMask\",bytes:62850,sha256:\"2bea2a3b58ed3f2a1b1179cd07e38f556e4c592607461c32f4cba4f1dbb0d21d\"},preview:{entry:\"creature-moon-drift/preview\",bytes:2558,sha256:\"425a6acfc362bfe037e494df1adb4d6676d8c3825a6dcd3d5a0d8f488bc65f87\"}}}},{id:\"creature-pearl-spirit\",displayName:\"Pearl Spirit\",track:\"yoga\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Onyx015\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Onyx015\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-pearl-spirit/basecolor.webp\",bytes:29558,sha256:\"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"},normal:{file:\"textures/creature-pearl-spirit/normal.webp\",bytes:578,sha256:\"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"},roughness:{file:\"textures/creature-pearl-spirit/roughness.webp\",bytes:2712,sha256:\"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"},height:{file:\"textures/creature-pearl-spirit/height.webp\",bytes:2632,sha256:\"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"},tintMask:{file:\"textures/creature-pearl-spirit/tint-mask.webp\",bytes:70938,sha256:\"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"},preview:{file:\"textures/creature-pearl-spirit/preview.webp\",bytes:36178,sha256:\"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"}},runtime:{unlock:{level:5,track:\"yoga\"},pack:\"track-yoga\",bundle:\"assets/yoga.m5bundle\",maps:{basecolor:{entry:\"creature-pearl-spirit/basecolor\",bytes:29558,sha256:\"ecd31972e0f2bd3dcbb56dd83966989cbaa724514cc57576769886ee69277bc4\"},normal:{entry:\"creature-pearl-spirit/normal\",bytes:578,sha256:\"bc6c55f9b7c548eb294b544cffd95bead865725a62e2b07b26a954175f8f7de2\"},roughness:{entry:\"creature-pearl-spirit/roughness\",bytes:2712,sha256:\"e4d14e77c9b45e27f077ba5a1a549ce863650380a48babb5c3b02a6d9322c2d8\"},height:{entry:\"creature-pearl-spirit/height\",bytes:2632,sha256:\"5b01611a42e4e95216112bc1c263ad8c730fb412d74ed44f5e07085eecf24432\"},tintMask:{entry:\"creature-pearl-spirit/tintMask\",bytes:70938,sha256:\"000adcbc57cc83e8e2205c13dc421e9b8929d602006c2e3ca780725819dbf3c6\"},preview:{entry:\"creature-pearl-spirit/preview\",bytes:36178,sha256:\"5494688e1ef1a80ddce772b1391f81c43c666d3595cc4dd567b807b393aafcda\"}}}},{id:\"creature-red-desert-scale\",displayName:\"Red Desert Scale\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"rare\",sourceAssetId:\"Rock029\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Rock029\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-red-desert-scale/basecolor.webp\",bytes:51964,sha256:\"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"},normal:{file:\"textures/creature-red-desert-scale/normal.webp\",bytes:120276,sha256:\"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"},roughness:{file:\"textures/creature-red-desert-scale/roughness.webp\",bytes:28424,sha256:\"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"},height:{file:\"textures/creature-red-desert-scale/height.webp\",bytes:8842,sha256:\"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"},ao:{file:\"textures/creature-red-desert-scale/ao.webp\",bytes:19082,sha256:\"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"},tintMask:{file:\"textures/creature-red-desert-scale/tint-mask.webp\",bytes:83596,sha256:\"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"},preview:{file:\"textures/creature-red-desert-scale/preview.webp\",bytes:57218,sha256:\"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"}},runtime:{unlock:{level:1,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-red-desert-scale/basecolor\",bytes:51964,sha256:\"7160db9a779efc4fb62326be77ba443ee4ae5604884e1ed691e902e47323b156\"},normal:{entry:\"creature-red-desert-scale/normal\",bytes:120276,sha256:\"450837f83a5ebbb2b9f31970d1c575fc18084358a7bf8fcf3912b5d5eddc3ec3\"},roughness:{entry:\"creature-red-desert-scale/roughness\",bytes:28424,sha256:\"dd03959c9520cc2b6025073ef096c089b0f2d38b9f984ef1820d95caafc1992d\"},height:{entry:\"creature-red-desert-scale/height\",bytes:8842,sha256:\"0e342c324ce19607da0bd50a966fa94b5c2e38fad0f37da848c7d666ed4518fe\"},ao:{entry:\"creature-red-desert-scale/ao\",bytes:19082,sha256:\"7ab01e1e8509a7d83f58c00ae0f702834d99120a4070ed92cedb6710a802a766\"},tintMask:{entry:\"creature-red-desert-scale/tintMask\",bytes:83596,sha256:\"19a1bbac2eb458c1c6c36968566ac8e43e52cc62e5f57e9d1e47bfc81734e315\"},preview:{entry:\"creature-red-desert-scale/preview\",bytes:57218,sha256:\"d84d2ea7022224cea294456c3c8656abf0979856274bcc64be90a8b1378b39d2\"}}}},{id:\"creature-cave-drake\",displayName:\"Cave Drake\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Rock035\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Rock035\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-cave-drake/basecolor.webp\",bytes:42954,sha256:\"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"},normal:{file:\"textures/creature-cave-drake/normal.webp\",bytes:126116,sha256:\"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"},roughness:{file:\"textures/creature-cave-drake/roughness.webp\",bytes:37152,sha256:\"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"},height:{file:\"textures/creature-cave-drake/height.webp\",bytes:10778,sha256:\"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"},ao:{file:\"textures/creature-cave-drake/ao.webp\",bytes:20824,sha256:\"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"},tintMask:{file:\"textures/creature-cave-drake/tint-mask.webp\",bytes:59714,sha256:\"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"},preview:{file:\"textures/creature-cave-drake/preview.webp\",bytes:52250,sha256:\"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"}},runtime:{unlock:{level:3,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-cave-drake/basecolor\",bytes:42954,sha256:\"891a196afba62375788c0b090019fd19941c04d25f9d760f135d57e60a1de00a\"},normal:{entry:\"creature-cave-drake/normal\",bytes:126116,sha256:\"8283888c871969dfe2b35f0cebd352a66a2cc46ad64bcb2bb5c5f8663b9c146e\"},roughness:{entry:\"creature-cave-drake/roughness\",bytes:37152,sha256:\"ad45c8e7102239aef39f0154e1c57b3932742255f5799c0074a8b5435cecc5c5\"},height:{entry:\"creature-cave-drake/height\",bytes:10778,sha256:\"bef40f1f39e9bf0e716195a417fa0fcaeb67ed09ec43b2ee5fb59030648e34d4\"},ao:{entry:\"creature-cave-drake/ao\",bytes:20824,sha256:\"e085d1a569564fdbe39a9dd18fa133a14ffa83117e8cdda10425bace18a70bd6\"},tintMask:{entry:\"creature-cave-drake/tintMask\",bytes:59714,sha256:\"febea8d8da737dd8127c8049724d22d621219c483682ded32a4169c1f651c63a\"},preview:{entry:\"creature-cave-drake/preview\",bytes:52250,sha256:\"fd405d3986dd0eea39be9f341ec58591aa6e6ab66e25787adf3b2e16c62e634a\"}}}},{id:\"creature-sandstone-titan\",displayName:\"Sandstone Titan\",track:\"martial-arts\",realm:\"fantasy\",rarity:\"legendary\",sourceAssetId:\"Rock061\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Rock061\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-sandstone-titan/basecolor.webp\",bytes:19630,sha256:\"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"},normal:{file:\"textures/creature-sandstone-titan/normal.webp\",bytes:59070,sha256:\"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"},roughness:{file:\"textures/creature-sandstone-titan/roughness.webp\",bytes:55850,sha256:\"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"},height:{file:\"textures/creature-sandstone-titan/height.webp\",bytes:8100,sha256:\"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"},ao:{file:\"textures/creature-sandstone-titan/ao.webp\",bytes:15348,sha256:\"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"},tintMask:{file:\"textures/creature-sandstone-titan/tint-mask.webp\",bytes:41274,sha256:\"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"},preview:{file:\"textures/creature-sandstone-titan/preview.webp\",bytes:24198,sha256:\"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"}},runtime:{unlock:{level:5,track:\"martial-arts\"},pack:\"track-martial-arts\",bundle:\"assets/martial-arts.m5bundle\",maps:{basecolor:{entry:\"creature-sandstone-titan/basecolor\",bytes:19630,sha256:\"bb2d67e9addc03ae6dd1052db82b6f36d03d5af77cc2a63d48921844b652a3e0\"},normal:{entry:\"creature-sandstone-titan/normal\",bytes:59070,sha256:\"a9f2c10deb50d06074111b4f3cf220edb876633fae81c239f1c99718d1d07d46\"},roughness:{entry:\"creature-sandstone-titan/roughness\",bytes:55850,sha256:\"d32bb23773fd03bedc81edeec043c823319e295e48198016095ff29ff988c373\"},height:{entry:\"creature-sandstone-titan/height\",bytes:8100,sha256:\"cf13bbcf1fad8c96f467f178d905fb55f4d254fdcc8fa52f506f80de8362f264\"},ao:{entry:\"creature-sandstone-titan/ao\",bytes:15348,sha256:\"454cc3f34a0c059b046ee536585296a9195bb1259bc1ebb98b3377aeb19ad750\"},tintMask:{entry:\"creature-sandstone-titan/tintMask\",bytes:41274,sha256:\"168d8de923125957c85b30e3e378b0586a7fd91d270a8c19042bea518d1d2529\"},preview:{entry:\"creature-sandstone-titan/preview\",bytes:24198,sha256:\"b379980f2c2907823755986994431b2fbe0d8e2a254616a89cefa1c3f1a810c3\"}}}},{id:\"creature-neon-mainframe\",displayName:\"Neon Mainframe\",track:\"cardio\",realm:\"sci-fi\",rarity:\"rare\",sourceAssetId:\"Chip002\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Chip002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-neon-mainframe/basecolor.webp\",bytes:56744,sha256:\"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"},normal:{file:\"textures/creature-neon-mainframe/normal.webp\",bytes:37362,sha256:\"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"},roughness:{file:\"textures/creature-neon-mainframe/roughness.webp\",bytes:41338,sha256:\"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"},height:{file:\"textures/creature-neon-mainframe/height.webp\",bytes:33104,sha256:\"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"},metalness:{file:\"textures/creature-neon-mainframe/metalness.webp\",bytes:45012,sha256:\"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"},tintMask:{file:\"textures/creature-neon-mainframe/tint-mask.webp\",bytes:50140,sha256:\"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"},preview:{file:\"textures/creature-neon-mainframe/preview.webp\",bytes:61118,sha256:\"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"}},runtime:{unlock:{level:1,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-neon-mainframe/basecolor\",bytes:56744,sha256:\"7af9b36dc24154d4dfe3b151dcafe44c1488643845a380e97d44c61d495c4b03\"},normal:{entry:\"creature-neon-mainframe/normal\",bytes:37362,sha256:\"413a59522d8e711071777b6a026805d22fb848881e396ae2e55892c47d3e621e\"},roughness:{entry:\"creature-neon-mainframe/roughness\",bytes:41338,sha256:\"911155142f0e2266a6df466e0993e44b5039ac75e0d3489e37e0cffaa49df930\"},height:{entry:\"creature-neon-mainframe/height\",bytes:33104,sha256:\"d1a60696213f4e3bb1af2d335aa37c7358fb9efa8877cd4b465422cba460ddcd\"},metalness:{entry:\"creature-neon-mainframe/metalness\",bytes:45012,sha256:\"d8294af74841524440d65a0fc0390f11fb1e46fa9086b5adcae23f00e760b7bd\"},tintMask:{entry:\"creature-neon-mainframe/tintMask\",bytes:50140,sha256:\"e0c65204a15a3f678c15e017fb84817d1d2f8e7f7184450cd85034303ad844e9\"},preview:{entry:\"creature-neon-mainframe/preview\",bytes:61118,sha256:\"6b904429406f8ff2f6decf39552837fbb1a69b51f6bd1f84ac1ae51b7b1ece44\"}}}},{id:\"creature-reactor-grid\",displayName:\"Reactor Grid\",track:\"cardio\",realm:\"sci-fi\",rarity:\"epic\",sourceAssetId:\"Chip004\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Chip004\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-reactor-grid/basecolor.webp\",bytes:68780,sha256:\"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"},normal:{file:\"textures/creature-reactor-grid/normal.webp\",bytes:30258,sha256:\"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"},roughness:{file:\"textures/creature-reactor-grid/roughness.webp\",bytes:28938,sha256:\"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"},height:{file:\"textures/creature-reactor-grid/height.webp\",bytes:18820,sha256:\"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"},metalness:{file:\"textures/creature-reactor-grid/metalness.webp\",bytes:8980,sha256:\"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"},tintMask:{file:\"textures/creature-reactor-grid/tint-mask.webp\",bytes:50226,sha256:\"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"},preview:{file:\"textures/creature-reactor-grid/preview.webp\",bytes:70556,sha256:\"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"}},runtime:{unlock:{level:3,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-reactor-grid/basecolor\",bytes:68780,sha256:\"87f6c7fad31ff2a102dd430f1b8e6a37efb6ea41d28c7c43ecd96a8ecff4ba87\"},normal:{entry:\"creature-reactor-grid/normal\",bytes:30258,sha256:\"29097e24c7f4977faa6b8de2cab3968aeb0d786f3304c5c30392e7217afa944a\"},roughness:{entry:\"creature-reactor-grid/roughness\",bytes:28938,sha256:\"b259990ff0909332bf236b4be50db3aa119887ebcd2f9d59d28c20e0cf529da1\"},height:{entry:\"creature-reactor-grid/height\",bytes:18820,sha256:\"2b43a252f9398122436a011c785fb684ef15b181442db3c784aebda559e57bb4\"},metalness:{entry:\"creature-reactor-grid/metalness\",bytes:8980,sha256:\"07523450a21492207c75694b9a6413499417262e6439b07ddd21604ac728d05c\"},tintMask:{entry:\"creature-reactor-grid/tintMask\",bytes:50226,sha256:\"730ad1404d4067b92705945e94980c58f8e069e3badf53f288b0559f27d2a386\"},preview:{entry:\"creature-reactor-grid/preview\",bytes:70556,sha256:\"b8d73dfa85b4af6399099734708bd705cfe9d9cf88942a6844a737c0a7c9e3f2\"}}}},{id:\"creature-quantum-core\",displayName:\"Quantum Core\",track:\"cardio\",realm:\"sci-fi\",rarity:\"legendary\",sourceAssetId:\"Chip007\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Chip007\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-quantum-core/basecolor.webp\",bytes:51286,sha256:\"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"},normal:{file:\"textures/creature-quantum-core/normal.webp\",bytes:31990,sha256:\"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"},roughness:{file:\"textures/creature-quantum-core/roughness.webp\",bytes:31284,sha256:\"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"},height:{file:\"textures/creature-quantum-core/height.webp\",bytes:29580,sha256:\"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"},metalness:{file:\"textures/creature-quantum-core/metalness.webp\",bytes:38108,sha256:\"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"},tintMask:{file:\"textures/creature-quantum-core/tint-mask.webp\",bytes:41968,sha256:\"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"},preview:{file:\"textures/creature-quantum-core/preview.webp\",bytes:56764,sha256:\"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"}},runtime:{unlock:{level:5,track:\"cardio\"},pack:\"track-cardio\",bundle:\"assets/cardio.m5bundle\",maps:{basecolor:{entry:\"creature-quantum-core/basecolor\",bytes:51286,sha256:\"60c77e1451ddd9d4acf761146d68273493a07ef74320e893906f896883e7f933\"},normal:{entry:\"creature-quantum-core/normal\",bytes:31990,sha256:\"adf7da0427d32ab38dace38c6656e873a6b9e345d6f2a41c42cfc640d437c46a\"},roughness:{entry:\"creature-quantum-core/roughness\",bytes:31284,sha256:\"d489ea08a4eafc75c5c7c1af6cb535e6d56516906f9a2210b0313407b48713fd\"},height:{entry:\"creature-quantum-core/height\",bytes:29580,sha256:\"7d44a79d77ba776128ff8836156b73f010c56ff8543d7fe1bf6994f21686efd7\"},metalness:{entry:\"creature-quantum-core/metalness\",bytes:38108,sha256:\"2845ad0fae0dc3dd3c07aafe5e1758fb322dea33ec636e8036cebbb3f5e7c66b\"},tintMask:{entry:\"creature-quantum-core/tintMask\",bytes:41968,sha256:\"5ec244d327e08f7f5cc6022500a829fb2ce697a6749506ca09de5c832efc8493\"},preview:{entry:\"creature-quantum-core/preview\",bytes:56764,sha256:\"51f0687bf78e65155d2e6e156e21bb6c2ce6f64b35fc7e7e9623fc94752b52f5\"}}}},{id:\"creature-celestial-mosaic\",displayName:\"Celestial Mosaic\",track:\"meditation\",realm:\"hybrid\",rarity:\"rare\",sourceAssetId:\"Tiles070\",collection:\"creature-celestial-rift\",collectionSlot:1,sourceUrl:\"https://ambientcg.com/a/Tiles070\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-celestial-mosaic/basecolor.webp\",bytes:72806,sha256:\"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"},normal:{file:\"textures/creature-celestial-mosaic/normal.webp\",bytes:40154,sha256:\"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"},roughness:{file:\"textures/creature-celestial-mosaic/roughness.webp\",bytes:47808,sha256:\"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"},height:{file:\"textures/creature-celestial-mosaic/height.webp\",bytes:38486,sha256:\"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"},ao:{file:\"textures/creature-celestial-mosaic/ao.webp\",bytes:46104,sha256:\"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"},tintMask:{file:\"textures/creature-celestial-mosaic/tint-mask.webp\",bytes:63262,sha256:\"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"},preview:{file:\"textures/creature-celestial-mosaic/preview.webp\",bytes:76982,sha256:\"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"}},runtime:{unlock:{level:1,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-celestial-mosaic/basecolor\",bytes:72806,sha256:\"2e293d1642aa668793919b7d46bb50e94bcc36c0ce5d1f775463269a949f0978\"},normal:{entry:\"creature-celestial-mosaic/normal\",bytes:40154,sha256:\"995e3446cf6f85eb57d7925922e187fc497a69355b82dfd069bcbd3f0029a175\"},roughness:{entry:\"creature-celestial-mosaic/roughness\",bytes:47808,sha256:\"29cbfb28025d5943765276c118b38d186b9294c6458bb6dc7a7da886ed989dd3\"},height:{entry:\"creature-celestial-mosaic/height\",bytes:38486,sha256:\"15cace7739e375a6dad5f5251d1b4e9cc8495abbba9f1560844e272cc5401ef0\"},ao:{entry:\"creature-celestial-mosaic/ao\",bytes:46104,sha256:\"0da24064e9f490c342bf09e2149b58945e0ef0c555aa95b8c542615cf82b9fc1\"},tintMask:{entry:\"creature-celestial-mosaic/tintMask\",bytes:63262,sha256:\"c73737938011dfd0894b37e613d9de159b672e2813a34cd0f134080961935268\"},preview:{entry:\"creature-celestial-mosaic/preview\",bytes:76982,sha256:\"06b94f2aed6383bdcd79769cc0649b71ac08fb0dcdf26d7aa5c578f66ff974ca\"}}}},{id:\"creature-shellborn\",displayName:\"Shellborn\",track:\"meditation\",realm:\"fantasy\",rarity:\"epic\",sourceAssetId:\"Ground060\",collection:\"creature-celestial-rift\",collectionSlot:2,sourceUrl:\"https://ambientcg.com/a/Ground060\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-shellborn/basecolor.webp\",bytes:9684,sha256:\"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"},normal:{file:\"textures/creature-shellborn/normal.webp\",bytes:23222,sha256:\"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"},roughness:{file:\"textures/creature-shellborn/roughness.webp\",bytes:8118,sha256:\"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"},height:{file:\"textures/creature-shellborn/height.webp\",bytes:17122,sha256:\"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"},ao:{file:\"textures/creature-shellborn/ao.webp\",bytes:9036,sha256:\"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"},tintMask:{file:\"textures/creature-shellborn/tint-mask.webp\",bytes:50694,sha256:\"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"},preview:{file:\"textures/creature-shellborn/preview.webp\",bytes:12672,sha256:\"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"}},runtime:{unlock:{level:3,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-shellborn/basecolor\",bytes:9684,sha256:\"2ffb18fed343bdcf0b2ba3d4b0dea9e8e53711fb266563af356fc940dc129a40\"},normal:{entry:\"creature-shellborn/normal\",bytes:23222,sha256:\"d1a9e0f90dce9c6a37afe8819f96ce7af37ca19f8a3ed035bb2f2dc262695f83\"},roughness:{entry:\"creature-shellborn/roughness\",bytes:8118,sha256:\"c7356a52eed60b3dd71af8ef399b497c5a984b1f076df5c12f07e1a2a57a3971\"},height:{entry:\"creature-shellborn/height\",bytes:17122,sha256:\"7731dee5c6bafef91432e5df689468c0bd108baee671021effef7fdd6fed8545\"},ao:{entry:\"creature-shellborn/ao\",bytes:9036,sha256:\"8bb1e0ef1415f81b31b967d8df9c22dbcad3160a17e7410fcd85ad5819c7c60e\"},tintMask:{entry:\"creature-shellborn/tintMask\",bytes:50694,sha256:\"655e52806a1abc2750780ffa28d94b85cd1ebef231624f83b8ff41c345960e89\"},preview:{entry:\"creature-shellborn/preview\",bytes:12672,sha256:\"3f5d1a52e02032ce1513807c7e03898af630842323e99f12948f40da880c23d3\"}}}},{id:\"creature-cracked-starlight\",displayName:\"Cracked Starlight\",track:\"meditation\",realm:\"hybrid\",rarity:\"legendary\",sourceAssetId:\"Paint002\",collection:\"creature-celestial-rift\",collectionSlot:3,sourceUrl:\"https://ambientcg.com/a/Paint002\",license:\"CC0-1.0\",author:\"ambientCG\",resolution:512,maps:{basecolor:{file:\"textures/creature-cracked-starlight/basecolor.webp\",bytes:2308,sha256:\"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"},normal:{file:\"textures/creature-cracked-starlight/normal.webp\",bytes:36906,sha256:\"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"},roughness:{file:\"textures/creature-cracked-starlight/roughness.webp\",bytes:27138,sha256:\"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"},height:{file:\"textures/creature-cracked-starlight/height.webp\",bytes:21122,sha256:\"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"},ao:{file:\"textures/creature-cracked-starlight/ao.webp\",bytes:39506,sha256:\"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"},tintMask:{file:\"textures/creature-cracked-starlight/tint-mask.webp\",bytes:37962,sha256:\"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"},preview:{file:\"textures/creature-cracked-starlight/preview.webp\",bytes:2858,sha256:\"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"}},runtime:{unlock:{level:5,track:\"meditation\"},pack:\"track-meditation\",bundle:\"assets/meditation.m5bundle\",maps:{basecolor:{entry:\"creature-cracked-starlight/basecolor\",bytes:2308,sha256:\"212ef8ce6348eab7babafd1302f6de8e0ff645ded3417c37b0c14c368bd5d3c3\"},normal:{entry:\"creature-cracked-starlight/normal\",bytes:36906,sha256:\"7363169a2812312622da8ea9802490d0f121523a63f797d7da80f3e6599b06cc\"},roughness:{entry:\"creature-cracked-starlight/roughness\",bytes:27138,sha256:\"e34af0b29b2da65da14698dd33851295fbd1c00d02220613487de2bab8cb15a1\"},height:{entry:\"creature-cracked-starlight/height\",bytes:21122,sha256:\"d6bceaf4d67569f417fcef6bd23e17458e885bbd77faff67b77c5254a50fc7a0\"},ao:{entry:\"creature-cracked-starlight/ao\",bytes:39506,sha256:\"3fbaf579d17290d907480db250ee33863f169674e1f57f66a51b05d725e54f02\"},tintMask:{entry:\"creature-cracked-starlight/tintMask\",bytes:37962,sha256:\"92b4eed22f7d0a3af5233adb61c825b286e7553f65eafdc0557de5f1e8d2d9a2\"},preview:{entry:\"creature-cracked-starlight/preview\",bytes:2858,sha256:\"07c798d4bde111c432f31209c9a960b7525f34922813203a1be22d5220493fe1\"}}}}]};var ja=1.1;var ee=Object.freeze([{id:\"strider\",name:\"Strider\",track:\"chest\",bosses:6},{id:\"ringer\",name:\"Ringer\",track:\"quads\",bosses:5},{id:\"manyarm\",name:\"Manyarm\",track:\"glutes\",bosses:5},{id:\"wedge\",name:\"Wedge\",track:\"arms-shoulders\",bosses:5},{id:\"warden\",name:\"Warden\",track:null,bosses:1},{id:\"blob\",name:\"Blob\",track:\"yoga\",bosses:4},{id:\"cap\",name:\"Cap\",track:\"martial-arts\",bosses:3},{id:\"stalk\",name:\"Stalk\",track:\"cardio\",bosses:4},{id:\"tanka\",name:\"Tanka\",track:\"meditation\",bosses:4},{id:\"lume\",name:\"Lume\",track:null,bosses:1}]),ia=5,Le=Object.freeze(ee.flatMap(e=>Array.from({length:e.bosses},(a,t)=>({id:`${e.id}-${t+1}`,name:e.bosses>1?`${e.name} ${t+1}`:e.name,row:e.id,track:e.track,index:t+1})))),O=Object.freeze({chest:{circuit:\"chest\",catalog:\"chest\",family:\"push\",name:\"Chest\",palette:\"pal-01\"},quads:{circuit:\"legs\",catalog:\"quads\",family:\"legs\",name:\"Quads\",palette:\"pal-02\"},glutes:{circuit:\"hips\",catalog:\"glutes\",family:\"legs\",name:\"Glutes\",palette:\"pal-03\"},\"arms-shoulders\":{circuit:\"shoulders\",catalog:\"arms\",family:\"push\",name:\"Arms & Shoulders\",palette:\"pal-04\"},yoga:{circuit:\"yoga\",catalog:\"yoga\",family:\"flow\",name:\"Yoga\",palette:\"pal-05\"},\"martial-arts\":{circuit:\"martial-arts\",catalog:\"martial-arts\",family:\"motion\",name:\"Martial Arts\",palette:\"pal-06\"},cardio:{circuit:\"cardio\",catalog:\"cardio\",family:\"motion\",name:\"Cardio\",palette:\"pal-07\"},meditation:{circuit:\"meditation\",catalog:\"meditation\",family:\"meditation\",name:\"Meditation\",palette:\"pal-08\"}}),la=Object.freeze({push:\"pal-09\",legs:\"pal-10\",motion:\"pal-11\"}),Rr=Object.freeze($a.skins.map(e=>Object.freeze({kind:\"creature-skin\",id:e.id,name:e.displayName,line:`${e.realm} creature finish \\xB7 ${e.rarity}`,track:e.track,level:e.runtime.unlock.level,pack:e.runtime.pack,collection:e.collection}))),Ha=[\"supportive\",\"direct\",\"analytical\",\"playful\",\"calm\",\"mom\"],Er=Object.freeze(Object.values(O).flatMap(({catalog:e},a)=>[3,5].map((t,r)=>{let s=Ha[(a*2+r)%Ha.length];return Object.freeze({id:`ship-${s}`,ship:s,name:na(s).name,track:e,level:t})}))),Wa=[[\"chest-w1\",\"Glove on a Stick\",\"For winning arguments with the air.\"],[\"chest-w2\",\"Chrome Bar Mace\",\"It benches you.\"],[\"arms-w1\",\"Dumbbellchucks\",\"Twice the dumbbell, half the coordination.\"],[\"arms-w2\",\"Pull-Up Staff\",\"For reaching the top shelf of victory.\"],[\"quads-w1\",\"Squat-Rack Axe\",\"Leg day has a new ambassador.\"],[\"quads-w2\",\"Kettlebell Flail\",\"Swings hard, asks questions never.\"],[\"glutes-w1\",\"Band Slingshot\",\"Resistance is futile. Literally.\"],[\"glutes-w2\",\"Hip-Thrust Hammer\",\"Bridges: now with consequences.\"],[\"yoga-w1\",\"Mat Bo Staff\",\"Rolled with intention. Swung with more.\"],[\"yoga-w2\",\"Block Gauntlets\",\"Namaste out of the way.\"],[\"martial-arts-w1\",\"Black-Belt Whip\",\"It earned its own belt.\"],[\"martial-arts-w2\",\"Dummy Club\",\"The dummy saw it coming. Still lost.\"],[\"cardio-w1\",\"Jump-Rope Lasso\",\"For roping runaway heart rates.\"],[\"cardio-w2\",\"Shoe Boomerang\",\"It always comes back. Like soreness.\"],[\"meditation-w1\",\"Singing-Bowl Aegis\",\"Om. Also, clang.\"],[\"meditation-w2\",\"Incense Wand\",\"Smells like victory. Allegedly.\"]],Xa=[[\"chest-plate-steel\",\"Plate Steel\",\"Now with 40% more clank.\"],[\"chest-rubber-grip\",\"Rubber Grip\",\"For hands that mean business.\"],[\"chest-chain-mail\",\"Chain Mail\",\"Medieval. But make it cardio.\"],[\"quads-track-rubber\",\"Track Rubber\",\"Smells faintly of starting blocks.\"],[\"quads-denim\",\"Denim\",\"For legs that never skip jeans either.\"],[\"quads-hex-tread\",\"Hex Tread\",\"Grip: gecko-grade.\"],[\"glutes-sweatshirt-fleece\",\"Sweatshirt Fleece\",\"Like a hug from laundry day.\"],[\"glutes-quilted\",\"Quilted\",\"Grandma approved. Gains approved harder.\"],[\"glutes-peach\",\"Speckled\",\"Ripe. Do not squeeze the coach.\"],[\"arms-hammered-bronze\",\"Hammered Bronze\",\"Shiny. Slightly dented. Like all of us.\"],[\"arms-rope\",\"Fine Stripe\",\"Climb every metaphor.\"],[\"arms-leather\",\"Snake Skin\",\"Broken in. Like you, after set three.\"],[\"yoga-cork\",\"Holey\",\"Floats. Like your downward dog.\"],[\"yoga-woven-mat\",\"Woven Mat\",\"Hand-woven by extremely calm spiders.\"],[\"yoga-petal\",\"Petal\",\"Delicate. Unlike your warrior two.\"],[\"martial-arts-canvas-gi\",\"Graph Paper\",\"It has seen things. Mostly laundry.\"],[\"martial-arts-bamboo\",\"Bamboo\",\"Bends. Does not break. Unlike resolutions.\"],[\"martial-arts-dragon-scale\",\"Dragon Scale\",\"The dragon is fine. It donated.\"],[\"cardio-mesh\",\"Cool Graph Paper\",\"Maximum airflow. Minimum excuses.\"],[\"cardio-terry-cloth\",\"Terry Cloth\",\"Absorbs sweat and bad decisions.\"],[\"cardio-pebble-path\",\"Pebble Path\",\"A tiny trail, wherever you go.\"],[\"meditation-sand-garden\",\"Wiggles\",\"Raked by a very patient rake.\"],[\"meditation-river-stone\",\"River Stone\",\"Smooth. Unbothered. Goals.\"],[\"meditation-moss\",\"Moss\",\"Grows on you. Literally, now.\"]],da=Object.freeze({\"chest-rubber-grip\":[\"legacy-15\",\"Magma\",\"Runs hot. Cools never.\"],\"glutes-sweatshirt-fleece\":[\"legacy-13\",\"Stone Golem\",\"Built slow. Built to last.\"],\"arms-rope\":[\"legacy-14\",\"Crystal\",\"Clear eyes. Sharp facets.\"],\"arms-leather\":[\"legacy-8\",\"Spectral\",\"Here in spirit. Also in body.\"],\"yoga-cork\":[\"legacy-20\",\"Fluffy\",\"Maximum floof. Minimum mercy.\"],\"yoga-woven-mat\":[\"legacy-21\",\"Jelly\",\"Wobbles. Never falls.\"],\"cardio-terry-cloth\":[\"legacy-16\",\"Glacial\",\"Cool under pressure. Very cool.\"]}),Ar={push:[\"Forged from lost remote controls.\",\"For the comfiest conqueror.\"],legs:[\"Always moving. Never in a hurry.\",\"Polished by a thousand commutes.\"],motion:[\"Nine more minutes. Forever.\",\"Shiny. Like your intentions at 6 AM.\"],flow:[\"Eight arms. Zero flexibility.\",\"Breathe in. The kraken breathes out.\"],meditation:[\"99+ unread. All of them urgent.\",\"Silent. At last.\"]},Fr={push:\"It followed you home from the gym. Keep it.\",legs:\"It never skips leg day. It never skips anything.\",motion:\"Fast. Loud. Naps hard.\",flow:\"Bends in ways physics dislikes.\",meditation:\"It hums when you breathe. Do not ask how.\"},L=(e,[a,t,r])=>({kind:e,id:a,name:t,line:r}),Z=(e,a)=>({kind:\"reward-pack\",id:`reward-pack:${e}:${a}`,tier:e,name:`${e[0].toUpperCase()+e.slice(1)} Pack`,line:\"Open for one random cosmetic.\"}),Va=Object.freeze([\"Mortal\",\"Verdant\",\"Mycelial\",\"Chitin\",\"Reptilian\",\"Abyssal\",\"Coral\",\"Skeletal\",\"Spectral\",\"Infernal\",\"Celestial\",\"Voidborn\",\"Eldritch\",\"Stone Golem\",\"Crystal\",\"Magma\",\"Glacial\",\"Stormcharged\",\"Clockwork Robot\",\"Neon Synth\",\"Fluffy\",\"Jelly\",\"Baby\"]),Ja=()=>[...Xa.map(e=>L(\"texture\",e)),...[[\"opal-jelly\",\"Opal Jelly\"],[\"bubble-glass\",\"Bubble Glass\"],[\"prism-crystal\",\"Prism Crystal\"],[\"holo-foil\",\"Holographic Foil\"],[\"glitter-resin\",\"Glitter Resin\"],[\"galaxy-geode\",\"Galaxy Geode\"],[\"caustic-slime\",\"Caustic Slime\"],[\"blister-hide\",\"Blister Hide\"],[\"rotten-rind\",\"Rotten Rind\"],[\"parasite-nest\",\"Parasite Nest\"],[\"exposed-sinew\",\"Exposed Sinew\"],[\"abyssal-maw\",\"Abyssal Maw\"],[\"circuit-alloy\",\"Circuit Alloy\"],[\"servo-armor\",\"Servo Armor\"],[\"chrome-rib\",\"Chrome Rib\"],[\"carbon-mech\",\"Carbon Mech\"],[\"hazard-panel\",\"Hazard Panel\"],[\"reactor-glass\",\"Reactor Glass\"]].map(([e,a])=>L(\"texture\",[e,a,\"\"])),...Va.map((e,a)=>L(\"texture\",[`legacy-${a}`,e,\"\"])),L(\"texture\",[\"coach-64-bit\",\"64-bit Pixel Finish\",\"A pixel finish for a coach with a boss skin.\"])].filter(e=>!fe.includes(e.id)&&!H.includes(e.id)),ba=Object.freeze([\"#060409\",\"#ffffff\",\"#7f7d78\",\"#ff3b30\",\"#ff8a2a\",\"#ffd100\",\"#2bd97c\",\"#008c8c\",\"#2454d6\",\"#6a2bd9\",\"#f59ec4\",\"#7a5530\",\"#c4a77d\",\"#0b1a45\",\"#9fe2bf\"]),Do=Object.freeze([\"Black\",\"White\",\"Grey\",\"Red\",\"Orange\",\"Yellow\",\"Green\",\"Teal\",\"Blue\",\"Purple\",\"Pink\",\"Brown\",\"Tan\",\"Navy\",\"Mint\"]),Qa=()=>[...me.map(e=>({kind:\"palette\",id:e.id,name:e.name})),...[[\"default-slate\",\"Slate\",\"#8b8f9a\"],[\"default-clay\",\"Warm Clay\",\"#b7a68e\"],[\"default-ruby\",\"Ruby\",\"#a23b4a\"],[\"default-sapphire\",\"Sapphire\",\"#2d5aa0\"],[\"default-moss\",\"Moss\",\"#4c7a3f\"],[\"default-gold\",\"Gold\",\"#c9a13a\"],[\"default-charcoal\",\"Charcoal\",\"#333238\"],[\"default-blush\",\"Blush\",\"#d98fa0\"]].map(([e,a,t])=>({kind:\"color\",id:e,name:a,hex:t})),...Va.map((e,a)=>({kind:\"color\",id:`legacy-color-${a}`,name:`${e} (original)`,hex:a?j[a].primary:\"#7946aa\"}))];function Ka(e,a){for(let r=0;r<e.length;r++)e[r]=e[r].flatMap(s=>s.kind===\"palette\"?[Z(\"uncommon\",`${a}:L${r+1}:${s.id}`)]:s.kind===\"texture\"?[Z(\"legendary\",`${a}:L${r+1}:${s.id}`)]:s.kind===\"boss-skin\"?[{kind:\"boss-unlock\",id:a,name:s.name.replace(/ Skin$/,\" Beaten\"),line:\"Boss cleared at level 5.\"}]:[s]);e[3].push(Z(\"rare\",`${a}:L4:64-bit`));let t=r=>r.some(s=>s.kind===\"reward-pack\");return t(e[1])||e[1].push(Z(\"uncommon\",`${a}:L2:bonus`)),t(e[4])||e[4].push(Z(\"legendary\",`${a}:L5:bonus`)),e}var Ya=(e,a)=>e.find(t=>t[0]===a);function Oe(e){let a=me.find(t=>t.id===e);return{kind:\"palette\",id:e,name:a.name,line:a.tagline}}var Mr=new Map(me.filter(e=>e.reward).map(e=>[e.reward,e.id])),Za=(e,a)=>Array.from({length:a},(t,r)=>Mr.get(`${e}:L${r+1}`)).map(t=>t?[Oe(t)]:[]),Lr=e=>({kind:\"pet\",id:`${e}-pet`,name:`${e[0].toUpperCase()}${e.slice(1)} pet`,line:Fr[e],family:e});function et(e){let a=Le.find(n=>n.id===e);if(!a)return null;let t=a.track&&O[a.track],r=Ar[t?.family]||[null,null],s=Za(a.id,ia);if(s[1].push({kind:\"boss-texture\",id:`${a.id}-texture`,name:`${a.name} Texture`,line:r[0]}),s[4].push({kind:\"boss-skin\",id:`${a.id}-skin`,name:`${a.name} Skin`,line:r[1]}),!t||a.index!==1)return Ka(s,a.id);let o=t.catalog,c=Xa.filter(n=>n[0].startsWith(o+\"-\")).map(n=>da[n[0]]??n);s[0].unshift(L(\"weapon\",Ya(Wa,`${o}-w1`)),L(\"texture\",c[0])),s[1].unshift(Oe(t.palette)),s[2].push(L(\"weapon\",Ya(Wa,`${o}-w2`)),{kind:\"special\",id:`${o}-special`,name:`${t.name} Special`,line:\"Unlocked at level 3.\"},L(\"texture\",c[1])),s[3].push(Lr(t.family)),s[4].unshift({kind:\"aura\",id:`${o}-aura`,name:`${t.name} Aura`,line:\"Your first aura look.\"}),s[4].push(L(\"texture\",c[2]));for(let n of Rr.filter(u=>u.track===o))s[n.level-1].push(n);for(let n of Er.filter(u=>u.track===o))s[n.level-1].push({kind:\"ship\",id:n.id,name:n.name,line:na(n.ship).description,ship:n.ship,pack:\"coach-ships-biomes\"});return Ka(s,a.id)}var Me=Object.freeze({id:\"food-bonus\",name:\"Second Helping\",line:\"Hits harder for one day. Chew responsibly.\",effect:Object.freeze({type:\"damage-multiplier\",days:1,value:ja})}),Ie=me.filter(e=>e.reward?.startsWith(\"food:\")).length,at=()=>Za(\"food\",Ie).map((e,a)=>[...e.map(t=>Z(\"uncommon\",`food:L${a+1}:${t.id}`)),{kind:\"bonus\",id:`${Me.id}-${a+1}`,name:Me.name,line:Me.line,effect:Me.effect}]);var Or=Object.freeze({chest:\"\\u{1F3CB}\\uFE0F\",quads:\"\\u{1F9B5}\",legs:\"\\u{1F9B5}\",glutes:\"\\u{1F351}\",hips:\"\\u{1F351}\",arms:\"\\u{1F4AA}\",\"arms-shoulders\":\"\\u{1F938}\",shoulders:\"\\u{1F938}\",core:\"\\u{1F3AF}\",balance:\"\\u2696\\uFE0F\",yoga:\"\\u{1F9D8}\",\"martial-arts\":\"\\u{1F94B}\",stances:\"\\u{1F94B}\",boxing:\"\\u{1F94A}\",cardio:\"\\u26A1\",meditation:\"\\u{1F319}\",original:\"\\u25C8\"});var Ir=Object.fromEntries([...Y.map(e=>[e.name,e.id]),[\"Quads\",\"quads\"],[\"Glutes\",\"glutes\"],[\"Arms & shoulders\",\"arms-shoulders\"],[\"Arms & Shoulders\",\"arms-shoulders\"],[\"Arms\",\"arms\"],[\"Martial arts\",\"martial-arts\"],[\"Martial Arts\",\"martial-arts\"],[\"Meditation\",\"meditation\"]]),Tr=e=>e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\"),$o=new RegExp(`(?<![\\\\w])(?<!Cool )(${Object.keys(Ir).sort((e,a)=>a.length-e.length).map(Tr).join(\"|\")})(?![\\\\w])`,\"g\");var ua=e=>A[e]?.group||(e===\"jumping\"?\"cardio\":null);var Wo=Object.freeze({advance:\"That wasn't enough to impress my clipboard. Ready for the next level?\",max:\"Maximum difficulty? Fine. One more than your last round. Now we're negotiating.\",limit:\"Five rounds. Even I have to respect the paperwork. This exercise family is done for today.\",rest:\"Rest first. The next challenge can wait.\"});var Xo=Object.freeze([{key:\"pushups\",mode:\"pushup\",goal:10,voice:!0},{key:\"squats\",mode:\"squat\",goal:10,voice:!0},{key:\"situps\",mode:\"high-plank\",goal:15,voice:!0},{key:\"meditation\",mode:null,goal:null,voice:!0},{key:\"food\",mode:null,goal:null,voice:!1}]),tt=Object.freeze({chest:\"chest\",legs:\"legs\",hips:\"hips\",shoulders:\"shoulders\",yoga:\"yoga\",stances:\"martial-arts\",boxing:\"martial-arts\",cardio:\"cardio\"}),Pr=Object.freeze([\"chest\",\"legs\",\"hips\",\"shoulders\",\"yoga\",\"martial-arts\",\"cardio\"]),Vo=Object.freeze([...Pr,\"meditation\",\"food\"]);var Te=5;var tc=Object.freeze({chest:\"chest\",legs:\"quads\",hips:\"glutes\",shoulders:\"arms-shoulders\",core:\"yoga\",balance:\"yoga\",yoga:\"yoga\",stances:\"martial-arts\",boxing:\"martial-arts\",cardio:\"cardio\"});var rt=(e={})=>E(e).paths;var De={};Sa(De,{cosmeticId:()=>w,currentCosmeticCoach:()=>I,grantUnlock:()=>re,grantUnlocks:()=>ga,grantedIds:()=>$r,isGranted:()=>oe,isGrantedToOriginalCoach:()=>lt,migrateSavedCosmetics:()=>ha,ownerGrantedIds:()=>pa});Se();var fa=\"myr5-unlock-pending-v1\",Pe=Object.freeze([\"creature-skin\",\"ship\",\"reward-pack\"]),Br=new Set([...Pe,\"texture\",\"color\",\"palette\",\"boss-skin\"]),Dr=({account:e=globalThis.myr5AuthenticatedAccount}={},a=!0)=>{try{let t=typeof e==\"string\"?e:e?.user?.id;if(typeof t==\"string\"&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(t))return`${fa}/account/${t}${typeof e==\"object\"&&e?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(e.dataEpoch))}`:\"\"}`;let r=a?v(globalThis.localStorage,e):localStorage.getItem(\"myr5-local-guest-owner-v1\");return r?`${fa}/guest/${encodeURIComponent(a?r:`guest:${r}`)}`:null}catch{return null}},st=(e,a={})=>Br.has(e)||a.accountScoped===!0?Dr(a):fa;var ot=e=>{try{let a=JSON.parse(localStorage.getItem(e)||\"{}\");return a&&typeof a==\"object\"&&!Array.isArray(a)?a:{}}catch{return{}}},ct=(e,a)=>Array.isArray(e[a])?e[a]:[];function Nr(e,a,t){if(!e)return;let r=ot(e),s=ct(r,a),o=t(s);if(o!==s)try{localStorage.setItem(e,JSON.stringify({...r,[a]:o}))}catch{}}function Be(e,a){let t=new Map;for(let{kind:r,id:s}of e){let o=st(r,a);if(!o)continue;let c=t.get(o);c||(c={data:ot(o),known:new Map},t.set(o,c)),c.known.has(r)||(c.data[r]=[...ct(c.data,r)],c.known.set(r,new Set(c.data[r])));let n=c.known.get(r);n.has(s)||(n.add(s),c.data[r].push(s))}for(let[r,s]of t)try{localStorage.setItem(r,JSON.stringify(s.data))}catch{}}var ae=(e,a,t)=>Nr(st(e,t),e,r=>r.includes(a)?r:[...r,a]);var zr=\"myr5-unlocks-v2\",Gr=()=>({texture:[],color:[],palette:[]}),w=(e,a)=>`coach:${encodeURIComponent(e)}:${a}`,te=(e,a)=>e===\"texture\"?ta(a):a,Ur=(e,a)=>{let t=/^(coach:[^:]*:)(.*)$/.exec(a);return t?t[1]+te(e,t[2]):a};function I(){try{return JSON.parse(localStorage.getItem(\"myr5-recipe-v1\")||\"{}\").body||\"myr5\"}catch{return\"myr5\"}}var ma=()=>C(`${zr}/${encodeURIComponent(v())}`);function se(){try{let e=JSON.parse(localStorage.getItem(ma())||\"{}\");return Object.fromEntries([\"texture\",\"color\",\"palette\"].map(a=>[a,Array.isArray(e[a])?[...new Set(e[a].filter(t=>typeof t==\"string\").map(t=>Ur(a,t)))]:[]]))}catch{return Gr()}}function nt(e){try{return localStorage.setItem(ma(),JSON.stringify(e)),!0}catch{return!1}}var it=(e,a,t)=>se()[e]?.includes(w(t,te(e,a)))??!1;function qr(e,a){let t=te(e,a);return(se()[e]||[]).some(r=>{let s=/^coach:([^:]+):(.*)$/.exec(r);if(!s||s[2]!==t)return!1;try{return decodeURIComponent(s[1])!==\"myr5\"}catch{return!1}})}var lt=(e,a)=>it(e,a,\"myr5\")||qr(e,a),oe=(e,a,t=I())=>t===\"myr5\"?lt(e,a):it(e,a,t);function dt(e){let a=globalThis.CustomEvent;if(!(typeof globalThis.dispatchEvent!=\"function\"||typeof a!=\"function\"||!e.length))try{globalThis.dispatchEvent(new a(\"myr5:cosmetics-unlocked\",{detail:{owner:v(),granted:e}}))}catch{}}function re(e,a,t=I()){if(!V()||![\"texture\",\"color\",\"palette\"].includes(e)||typeof a!=\"string\"||!a||typeof t!=\"string\"||!t)return!1;let r=se(),s=w(t,te(e,a));if(r[e].includes(s)||(r[e].push(s),!nt(r)))return!1;let o=te(e,a),c=[{kind:e,id:o,coachId:t}];return ae(e,s),t!==\"myr5\"&&ae(e,w(\"myr5\",o)),dt(c),!0}var $r=(e,a=I())=>{let t=se()[e]||[],r=w(a,\"\"),s=t.filter(c=>c.startsWith(r)).map(c=>c.slice(r.length));if(a!==\"myr5\")return s;let o=t.flatMap(c=>{let n=/^coach:([^:]+):(.*)$/.exec(c);if(!n||!n[2])return[];try{return decodeURIComponent(n[1])===\"myr5\"?[]:[n[2]]}catch{return[]}});return[...new Set([...s,...o])]};function ha(e){if((X()?.gameResetAt??0)>0)return;let a=e?.body;if(a)try{let t=`${ma()}/saved-look/${encodeURIComponent(a)}`;if(localStorage.getItem(t))return;let r=JSON.parse(localStorage.getItem(\"myr5-unlocks-v1\")||\"{}\");for(let s of Object.values(e.materials||{}))if(s?.textureId&&r.texture?.includes(s.textureId)&&re(\"texture\",s.textureId,a),s?.colorId)for(let o of[\"color\",\"palette\"])r[o]?.includes(s.colorId)&&re(o,s.colorId,a);localStorage.setItem(t,\"1\")}catch{}}var pa=e=>{let a=se()[e]||[],t=a.flatMap(r=>{let s=/^coach:([^:]+):(.*)$/.exec(r);if(!s||!s[2])return[];try{return decodeURIComponent(s[1])===\"myr5\"?[]:[w(\"myr5\",s[2])]}catch{return[]}});return[...new Set([...a,...t])]};function ga(e){if(!V())return 0;let a=se(),t=Object.fromEntries([\"texture\",\"color\",\"palette\"].map(o=>[o,new Set(a[o])])),r=[];for(let o of e){if(!t[o.kind]||typeof o.id!=\"string\"||!o.id||typeof o.coachId!=\"string\"||!o.coachId)continue;let c=te(o.kind,o.id),n=w(o.coachId,c);t[o.kind].has(n)||(t[o.kind].add(n),a[o.kind].push(n),r.push({kind:o.kind,id:c,coachId:o.coachId}))}if(!r.length||!nt(a))return 0;let s=r.flatMap(({kind:o,id:c,coachId:n})=>[{kind:o,id:w(n,c)},...n===\"myr5\"?[]:[{kind:o,id:w(\"myr5\",c)}]]);return Be(s),dt(r),r.length}var Ue={};Sa(Ue,{ACCOUNT_SCOPED_LEDGER_KINDS:()=>Pe,LEDGER_KEY:()=>ce,LEDGER_KINDS:()=>ne,grantUnlock:()=>Ge,grantUnlocks:()=>ka,grantedIds:()=>he,isGranted:()=>pe});Se();var ce=\"myr5-battle-pass-ledger-v1\",ne=Object.freeze([\"weapon\",\"pet\",\"boss-texture\",\"boss-skin\",\"boss-unlock\",\"special\",\"aura\",\"bonus\",\"creature-skin\",\"ship\",\"reward-pack\"]);function Ne(){try{let e=JSON.parse(localStorage.getItem(C(ce))||\"{}\");return Object.fromEntries(ne.map(a=>[a,Array.isArray(e[a])?e[a]:[]]))}catch{return Object.fromEntries(ne.map(e=>[e,[]]))}}var bt=new Set([...Pe,\"boss-skin\"]),ze=({account:e=globalThis.myr5AuthenticatedAccount}={},a=!0)=>{try{let t=typeof e==\"string\"?e:e?.user?.id;if(typeof t==\"string\"&&/^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,127})$/.test(t))return`${ce}/account/${t}${typeof e==\"object\"&&e?.dataEpoch!=null?`/epoch/${encodeURIComponent(String(e.dataEpoch))}`:\"\"}`;let r=a?v(globalThis.localStorage,e):localStorage.getItem(\"myr5-local-guest-owner-v1\");return r?`${ce}/guest/${encodeURIComponent(a?r:`guest:${r}`)}`:null}catch{return null}};function ya(e){try{let a=ze(e,!1),t=a?C(a):null;if(!t)return{};let r=JSON.parse(localStorage.getItem(t)||\"{}\");return r&&typeof r==\"object\"&&!Array.isArray(r)?r:{}}catch{return{}}}var he=(e,a)=>{if(!ne.includes(e))return[];let t=ya(a)[e],r=Array.isArray(t)?t:[];return bt.has(e)?r:r.length?[...new Set([...Ne()[e],...r])]:Ne()[e]},pe=(e,a,t)=>he(e,t).includes(a),ut=(e,a)=>bt.has(e)||a?.accountScoped===!0;function Ge(e,a,t){if(!V(t?.account)||!ne.includes(e)||typeof a!=\"string\"||!a)return!1;if(ut(e,t)){if(pe(e,a,t))return!1;let s=ze(t),o=s?C(s):null;if(!o)return!1;let c=ya(t),n=Array.isArray(c[e])?c[e]:[];c[e]=[...n,a];try{localStorage.setItem(o,JSON.stringify(c))}catch{return!1}return ae(e,a,{...t,accountScoped:!0}),!0}let r=Ne();if(r[e].includes(a))return!1;r[e].push(a);try{localStorage.setItem(C(ce),JSON.stringify(r))}catch{return!1}return ae(e,a),!0}function ka(e,a,t){if(!V(t?.account)||!ne.includes(e)||!Array.isArray(a))return 0;let r=ut(e,t),s=r?ze(t)?C(ze(t)):null:C(ce);if(!s)return 0;let o=r?ya(t):Ne(),c=new Set(r?he(e,t):o[e]||[]),n=new Set(o[e]||[]),u=[];for(let i of a)typeof i==\"string\"&&i&&!c.has(i)&&(c.add(i),n.add(i),u.push(i));if(!u.length)return 0;o[e]=[...n];try{localStorage.setItem(s,JSON.stringify(o))}catch{return 0}return Be(u.map(i=>({kind:e,id:i})),r?{...t,accountScoped:!0}:t),u.length}var pc=Object.freeze({rare:2,legendary:3,odds:Object.freeze({secret:Object.freeze({color:75,\"64-bit\":0})})});var jr={color:Qa(),texture:Ja()};var mt=e=>w(e.coachId,e.id);var Hr=(e,a)=>a.flatMap(t=>e===\"64-bit\"?[{kind:\"boss-skin\",id:`${t}-skin`,name:\"64-bit Pixel Finish\",coachId:t}]:jr[e].map(r=>({...r,coachId:t})));function ft({coaches:e=Qe()}={}){let a=[...new Set(e)].filter(r=>ve(r)),t=Object.fromEntries([\"color\",\"palette\",\"texture\"].map(r=>[r,new Set(pa(r))]));return t[\"boss-skin\"]=new Set(he(\"boss-skin\")),[\"color\",\"64-bit\",\"texture\"].flatMap(r=>Hr(r,a).map(s=>({...s,category:r}))).filter(r=>!t[r.kind].has(mt(r)))}function ht(){let e=ft(),a=e.filter(s=>s.kind!==\"boss-skin\"),t=e.filter(s=>s.kind===\"boss-skin\");ga(a),ka(\"boss-skin\",t.map(mt));let r=ft().length;return{granted:e.length-r,remaining:r}}var Wr=Object.fromEntries(Object.entries(O).map(([e,a])=>[a.circuit,e]));function yt(e=globalThis.myr5AuthenticatedAccount){let a=rt();return a.length||(a=(e?.onboarding?.data?.profile?.exercises||[]).map(t=>Wr[tt[ua(t)]]).filter(Boolean)),new Set([\"meditation\",...a])}function kt(){let e=globalThis.coachProgress?.circuit?.tracks;if(e)return e;try{return JSON.parse(localStorage.getItem(\"myr5-workout-progress-v1\"))?.circuit?.tracks||{}}catch{return{}}}var Kr=(e,a=Te)=>Math.floor(Math.max(0,Number(e)||0)/a);function Yr({tracks:e=kt(),account:a,stepsPerLevel:t=Te}={}){let r=E({account:a}),s=Object.fromEntries(Le.map(o=>[o.id,0]));for(let o of ee){if(!o.track)continue;let c=B.filter(n=>n.tracks.includes(O[o.track].catalog));for(let n=0;n<o.bosses;n++){let u=c[Math.min(c.length-1,Math.floor(n*c.length/o.bosses))];u&&(s[`${o.id}-${n+1}`]=r.goldenCoaches.includes(u.id)?5:r.coaches.includes(u.id)?4:0)}}return s}var Xr=new Set([\"texture\",\"color\",\"palette\"]),pt=(e,a)=>(Xr.has(e.kind)?De:Ue).isGranted(e.kind,e.id,a);function Vr(e={}){let a=e.stepsPerLevel??Te,t=yt(e.account),r=Yr({...e,stepsPerLevel:a}),s=Math.max(0,Number((e.tracks??kt())?.food?.steps)||0),o=Math.min(Ie,Kr(s,a)),c=i=>ee.find(l=>l.track&&O[l.track].family===i&&t.has(l.track)&&r[`${l.id}-1`]>=4)?.id,n=Le.map(i=>({...i,levels:r[i.id],rewards:et(i.id).map((l,d)=>({level:d+1,beaten:r[i.id]>d,items:l.map(b=>{let y=b.kind===\"pet\"&&la[b.family]&&c(b.family),h=y&&y!==i.row?(()=>{let k=Oe(la[b.family]);return{kind:\"reward-pack\",id:`reward-pack:uncommon:${i.id}:L4:${k.id}`,tier:\"uncommon\",name:\"Uncommon Pack\",line:\"Open for one random cosmetic.\"}})():b;return{...h,granted:pt(h,e)}})}))})),u={steps:s,levels:o,maxLevel:Ie,rewards:at().map((i,l)=>({level:l+1,beaten:o>l,items:i.map(d=>({...d,granted:pt(d)}))}))};return{stepsPerLevel:a,available:[...t],progress:r,bosses:n,food:u}}function gt(e={}){let a=Vr(e),t=[],r=Ia(E(e).totalXp);for(let s=2;s<=r.level;s++){let o=s%10===0?\"legendary\":s%5===0?\"rare\":\"uncommon\",c={kind:\"reward-pack\",id:`reward-pack:${o}:cosmetic-pass-v2:L${s}`,tier:o,name:`${o} pack`};Ge(c.kind,c.id,e)&&t.push(c)}return r.max&&ht(e),t.length&&typeof window<\"u\"&&window.dispatchEvent&&window.dispatchEvent(new CustomEvent(\"myr5:battle-pass\",{detail:{granted:t,state:a}})),{granted:t,state:a}}typeof window<\"u\"&&window.addEventListener&&(window.addEventListener(\"myr5:account-progress\",()=>gt()),window.addEventListener(\"myr5:performance-progress\",()=>gt()));var Jr=Object.fromEntries([...Y.map(e=>[e.id,e.name]),[\"meditation\",\"Meditation\"]]),Qr=F.map(e=>({...e,name:e.label,requirement:B.find(a=>a.id===e.id)||null})),Zr=e=>e[0].toUpperCase()+e.slice(1),wt=e=>Jr[e]||e;function _t(e){let a=Qr.find(n=>n.id===e);if(!a)return null;let t=a.requirement;if(!t){let n=M.includes(e);return{starter:n,unlock:n?\"Available from the start\":\"Requirement pending placement.\",gold:\"Select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:[]}}if(t.groups.includes(\"meditation\"))return{starter:!1,unlock:`Complete meditation on ${$.meditationDays[xe.indexOf(t.difficulty)]} separate days.`,gold:\"Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:[]};let r=Object.values(A).filter(n=>t.groups.includes(n.group)&&je(n.id)===t.difficulty),s=Object.values(ke).includes(e)?`Choose ${t.groups.map(wt).join(\" / \")} as one of your two starting workout paths OR `:\"\",o=[...new Set(r.map(n=>n.kind))],c=[];return o.includes(\"hold\")&&c.push(\"hold for 5 uninterrupted minutes\"),o.includes(\"reps\")&&c.push(\"complete 15 reps in one working set\"),t.groups.includes(\"cardio\")?c.push(\"complete 5 sprint rounds or 5 active minutes of gentle cardio\"):o.some(n=>n===\"pace\"||n===\"steps\")&&c.push(\"complete 5 active minutes\"),{starter:!1,difficulty:t.difficulty,groups:t.groups,unlock:`${s}${Zr(t.difficulty)} \\xB7 ${t.groups.map(wt).join(\" / \")}: ${c.join(\" OR \")}.`,gold:o.includes(\"hold\")?\"Complete a 10-minute uninterrupted hold at this difficulty in one of these groups. Breaks and difficulty changes restart the uninterrupted attempt.\":\"Once unlocked, select this coach and complete a 10-minute uninterrupted hold to earn its golden version.\",exercises:r.map(n=>n.name)}}var es=[{id:\"strider\",track:\"chest\",color:\"#b48cff\",boxes:[[10.96,9.7,10.78,8.8],[28.4,8.05,7.89,10.45],[37.51,9.2,11.92,9.3],[51.62,8.05,11.48,10.45],[63.37,8.05,12.8,10.45],[78.18,7.9,15.43,10.6]]},{id:\"ringer\",track:\"quads\",color:\"#ff8a2a\",boxes:[[20.16,18.05,7.89,8.1],[31.11,17.95,10.69,8.4],[44.26,17.95,9.55,8.25],[57.06,17.95,9.03,8.3],[69.76,17.95,11.13,8.5]]},{id:\"manyarm\",track:\"glutes\",color:\"#7dff5a\",boxes:[[22.17,26.65,8.33,8.6],[34.18,26.45,9.73,8.8],[46.54,27.25,10.08,8],[57.49,27.4,7.54,7.85],[65.64,27.2,12.62,8.05]]},{id:\"wedge\",track:\"arms\",color:\"#39a8ff\",boxes:[[23.4,34.7,10.52,7.8],[35.06,34.7,8.15,7.45],[43.21,34.7,11.48,7.75],[55.04,34.7,9.82,7.75],[65.21,34.7,11.22,7.75]]},{id:\"warden\",track:null,color:\"#39a8ff\",boxes:[[45.57,41.95,8.24,5.7]]},{id:\"blob\",track:\"yoga\",color:\"#c65cff\",boxes:[[26.29,47.25,10.87,6.95],[38.56,47.1,9.55,7.2],[49.87,47.1,7.8,6.95],[60.74,47.3,9.55,7.45]]},{id:\"cap\",track:\"martial-arts\",color:\"#ff4fa0\",boxes:[[33.04,54.6,7.62,7.4],[40.93,54.55,11.13,7.45],[53.46,54.65,10.78,7.35]]},{id:\"stalk\",track:\"cardio\",color:\"#ff4a3d\",boxes:[[30.5,61.45,7.01,7.5],[38.21,61.45,8.15,7.25],[49.52,61.45,9.29,7.5],[58.55,61.45,8.59,7.5]]},{id:\"tanka\",track:\"meditation\",color:\"#ffd23a\",boxes:[[29.27,68.65,9.38,7.85],[39.09,68.65,9.64,7.8],[48.47,68.45,10.6,8],[59.95,68.5,8.33,8]]},{id:\"lume\",track:null,color:\"#ffc94a\",boxes:[[39.35,75.95,15.34,8.8]]}],zc=es.flatMap(e=>e.boxes.map((a,t)=>({id:`${e.id}-${t+1}`,row:e.id,color:e.color,box:a})));var as=[\"chest\",\"quads\",\"glutes\",\"arms\",\"yoga\",\"martial-arts\",\"cardio\",\"meditation\"],ts=\"Original artwork -- MYR5 alien pipeline (Tripo Studio capture by Ian, low-poly re-export, Claude eval); no third-party license asserted (see creature/NOTICE.md).\",rs=e=>`creature/models/${e}.glb`,ss=[[\"roster/23-blob-texture-bodies--blob_creature_3d_model\",\"Blob 1\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--dragon_creature_3d_model\",\"Four-legged 1\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--fantasy_creature_3d_model1\",\"Four-legged 2\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--fantasy_creature_3d_model3\",\"Four-legged 3\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--four-legged_robot_3d_model\",\"Four-legged 4\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--quadruped_robot_3d_model1\",\"Four-legged 5\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--quadruped_robot_3d_model\",\"Four-legged 6\",[\"meditation\"],!0,!1],[\"roster/18-quad-all--robotic_dog_3d_model\",\"Four-legged 7\",[\"meditation\"],!0,!0,\"meditation\"],[\"roster/12-seedpod-snailslug--stylized_slug_3d_model\",\"Seedpod \\xB7 Snailslug 1\",[\"meditation\"],!0,!1],[\"roster/12-seedpod-snailslug--stylized_worm_3d_model\",\"Seedpod \\xB7 Snailslug 2\",[\"meditation\"],!0,!1],[\"roster/14-chisel-spire--low_poly_robot_3d_model\",\"Chisel \\xB7 Spire 2\",[\"meditation\"],!1,!1],[\"roster/07-bulb-sphereling--humanoid_robot_3d_model\",\"Bulb \\xB7 Sphereling 2\",[\"cardio\"],!1,!1],[\"roster/03-pearl-orb-ring--ringed_humanoid_3d_model\",\"Pearl \\xB7 Orb 3\",[\"cardio\"],!1,!1],[\"roster/06-ridge-triad--robot_3d_model1\",\"Ridge \\xB7 Triad 2\",[\"cardio\"],!1,!1],[\"roster/03-pearl-orb-ring--robot_3d_model\",\"Pearl \\xB7 Orb 4\",[\"cardio\"],!1,!1],[\"roster/20-lume--robotic_figure_3d_model\",\"Lume 1\",[\"cardio\"],!1,!1,\"cardio\"],[\"roster/02-taper-tallstalk--humanoid_robot_3d_model1\",\"Taper \\xB7 Tallstalk 2\",[\"arms\"],!1,!1],[\"roster/19-genie-multi--multi-armed_humanoid_3d_model1\",\"Genie \\xB7 Multi 2\",[\"arms\"],!1,!1],[\"roster/19-genie-multi--multi-armed_humanoid_3d_model\",\"Genie \\xB7 Multi 3\",[\"arms\"],!1,!1],[\"roster/17-shellcap-manyarm--mushroom_creature_3d_model\",\"Shellcap \\xB7 Manyarm 2\",[\"arms\"],!1,!1],[\"roster/17-shellcap-manyarm--mushroom_robot_3d_model\",\"Shellcap \\xB7 Manyarm 3\",[\"arms\"],!1,!1],[\"roster/23-blob-texture-bodies--patchwork_plush_figure_3d_model\",\"Blob 4\",[\"arms\"],!1,!1],[\"roster/08-shard-asym--robot_3d_model4\",\"Shard \\xB7 Asym 2\",[\"arms\"],!1,!1,\"arms\"],[\"roster/06-ridge-triad--geometric_robot_3d_model1\",\"Ridge \\xB7 Triad 1\",[\"chest\"],!1,!1],[\"roster/08-shard-asym--geometric_robot_3d_model\",\"Shard \\xB7 Asym 1\",[\"chest\"],!1,!1],[\"roster/16-spade-arch--pyramid_head_figure_3d_model\",\"Spade \\xB7 Arch 1\",[\"chest\",\"martial-arts\"],!1,!1],[\"roster/04-crest-wedge--robot_creature_3d_model\",\"Crest \\xB7 Wedge 1\",[\"chest\"],!1,!1],[\"roster/16-spade-arch--stylized_humanoid_3d_model\",\"Spade \\xB7 Arch 2\",[\"chest\"],!1,!1,\"chest\"],[\"roster/23-blob-texture-bodies--honeycomb_humanoid_3d_model\",\"Blob 3\",[\"glutes\"],!1,!1],[\"roster/23-blob-texture-bodies--sand_creature_3d_model\",\"Blob 5\",[\"glutes\"],!1,!1],[\"roster/03-pearl-orb-ring--stylized_3d_character1\",\"Pearl \\xB7 Orb 5\",[\"glutes\"],!1,!1],[\"roster/03-pearl-orb-ring--stylized_humanoid_figure_3d_model\",\"Pearl \\xB7 Orb 6\",[\"glutes\"],!1,!1,\"glutes\"],[\"roster/15-orbital-coili--robot_character_3d_model\",\"Orbital \\xB7 Coili 1\",[\"quads\"],!1,!1],[\"roster/13-fan-split--stylized_3d_character2\",\"Fan \\xB7 Split 1\",[\"quads\"],!1,!1],[\"roster/22-curve--stylized_cartoon_figure_3d_model\",\"Curve 2\",[\"quads\"],!1,!1,\"quads\"],[\"roster/10-petal-wisp--fantasy_creature_3d_model4\",\"Petal \\xB7 Wisp 3\",[\"yoga\"],!1,!1],[\"roster/19-genie-multi--fantasy_creature_3d_model\",\"Genie \\xB7 Multi 1\",[\"yoga\"],!1,!1],[\"roster/10-petal-wisp--ghost_character_3d_model\",\"Petal \\xB7 Wisp 2\",[\"yoga\"],!1,!1],[\"roster/08-shard-asym--stylized_action_figure_3d_model\",\"Shard \\xB7 Asym 4\",[\"yoga\"],!1,!1],[\"roster/22-curve--stylized_alien_3d_model\",\"Curve 1\",[\"yoga\"],!1,!1],[\"roster/10-petal-wisp--stylized_creature_3d_model\",\"Petal \\xB7 Wisp 1\",[\"yoga\"],!1,!1],[\"roster/19-genie-multi--stylized_octopus_3d_model\",\"Genie \\xB7 Multi 4\",[\"yoga\"],!1,!1,\"yoga\"],[\"roster/09-monolith-tanka--boxy_humanoid_3d_model\",\"Monolith \\xB7 Tanka 2\",[\"martial-arts\"],!1,!1],[\"roster/11-anvil-cask--clay-style_robot_3d_model\",\"Anvil \\xB7 Cask 1\",[\"martial-arts\"],!1,!1],[\"roster/05-slope-bobble--clay_humanoid_figure_3d_model\",\"Slope \\xB7 Bobble 1\",[\"martial-arts\"],!1,!1],[\"roster/14-chisel-spire--cone_head_3d_model\",\"Chisel \\xB7 Spire 1\",[\"martial-arts\"],!1,!1],[\"roster/08-shard-asym--fantasy_creature_3d_model2\",\"Shard \\xB7 Asym 3\",[\"martial-arts\"],!1,!1],[\"roster/09-monolith-tanka--mini_robot_3d_model\",\"Monolith \\xB7 Tanka 3\",[\"martial-arts\"],!1,!1],[\"roster/09-monolith-tanka--robot_3d_model3\",\"Monolith \\xB7 Tanka 5\",[\"martial-arts\"],!1,!1],[\"roster/13-fan-split--stylized_toy_3d_model\",\"Fan \\xB7 Split 2\",[\"martial-arts\"],!1,!1,\"martial-arts\"]],Kc=ss.map(([e,a,t,r,s,o])=>({stableId:e,displayName:a,tracks:t,petEligible:r,mountEligible:s,bossOf:o,sourceAsset:rs(e),license:ts,unlockRule:\"performance-milestone\",renderRecipe:\"tbd\"})),os=e=>ee.find(a=>a.track&&O[a.track].catalog===e);var Yc=Object.fromEntries(as.map(e=>[e,O[os(e).track].name]));function St(e,a,t=[]){return sa.has(e)||R.includes(e)?\"Unavailable coach\":M.includes(e)||ve(e)?null:_t(e)?.unlock||\"Coach performance milestone required\"}var xt=[{id:\"pal-01\",name:\"Acid Forge\",tagline:\"Lime bites oxblood and chrome\",colors:[\"#C6FF00\",\"#4A0E17\",\"#C0C7CE\"],unlockRule:\"aura-milestone\",unlockAtDay:5,finish:\"glitter\"},{id:\"pal-02\",name:\"Neon Afterburn Haze\",tagline:\"Orange aqua contrails with violet flare\",colors:[\"#FF6D00\",\"#00E0B0\",\"#C800FF\"],unlockRule:\"aura-milestone\",unlockAtDay:10,finish:\"brushed-metal\"},{id:\"pal-03\",name:\"Petrel Copper\",tagline:\"Petrol depths meet intense violet\",colors:[\"#0E5A61\",\"#B87333\",\"#6913E7\"],unlockRule:\"aura-milestone\",unlockAtDay:15,finish:\"polished-metal\"},{id:\"pal-04\",name:\"Vinaigrette Rust\",tagline:\"Oil pigment rust swirl\",colors:[\"#8A3B12\",\"#556B2F\",\"#E7A213\"],unlockRule:\"aura-milestone\",unlockAtDay:20,finish:\"marbled\"},{id:\"pal-05\",name:\"Neon Relic\",tagline:\"Hot magenta neon amber on bone\",colors:[\"#FF009F\",\"#FFB500\",\"#F2EFE6\"],unlockRule:\"aura-milestone\",unlockAtDay:25,finish:\"glitter\"},{id:\"pal-06\",name:\"Glacier Ember\",tagline:\"Ice teal over ember red\",colors:[\"#13E7D1\",\"#D9381E\",\"#2B2530\"],unlockRule:\"aura-milestone\",unlockAtDay:30,finish:\"pearl\"},{id:\"pal-07\",name:\"Chartreuse Storm\",tagline:\"Sludge green meets slate violet\",colors:[\"#B5C700\",\"#3D3654\",\"#E85D75\"],unlockRule:\"aura-milestone\",unlockAtDay:35,finish:\"matte\"},{id:\"pal-08\",name:\"Saffron Voltage\",tagline:\"Saffron arcs on ink blue\",colors:[\"#FFB000\",\"#101D42\",\"#00FAA6\"],unlockRule:\"aura-milestone\",unlockAtDay:40,finish:\"polished-metal\"},{id:\"pal-09\",name:\"Neon Venom Signal\",tagline:\"Poison lime warning under arterial red\",colors:[\"#A6FF00\",\"#FF2442\",\"#101828\"],unlockRule:\"aura-milestone\",unlockAtDay:45,finish:\"watercolor\"},{id:\"pal-10\",name:\"Mercury Moss\",tagline:\"Silver moss over saturated gold\",colors:[\"#AEB6BC\",\"#5C6B3C\",\"#E7A913\"],unlockRule:\"aura-milestone\",unlockAtDay:50,finish:\"brushed-metal\"},{id:\"pal-11\",name:\"Ultraviolet Tar\",tagline:\"Violet glow on tar black\",colors:[\"#7B2FF7\",\"#141414\",\"#FFC93C\"],unlockRule:\"aura-milestone\",unlockAtDay:55,finish:\"glaze\"},{id:\"pal-12\",name:\"Salmon Circuit\",tagline:\"Salmon pink on circuit green\",colors:[\"#FA2200\",\"#00A86B\",\"#2E2A5C\"],unlockRule:\"aura-milestone\",unlockAtDay:60,finish:\"pearl\"},{id:\"pal-13\",name:\"Muddy Pop\",tagline:\"Ochre mud sparks cerulean\",colors:[\"#8C6A2F\",\"#0081C8\",\"#E71362\"],unlockRule:\"battle-pass\",reward:\"strider-2:L1\",finish:\"matte\"},{id:\"pal-14\",name:\"Neon Vapor Crown\",tagline:\"Laser pink blue over dark purple\",colors:[\"#FF00AA\",\"#00D0FF\",\"#1B0033\"],unlockRule:\"battle-pass\",reward:\"strider-2:L3\",finish:\"watercolor\"},{id:\"pal-15\",name:\"Brick Vein\",tagline:\"Brick red veined in steel\",colors:[\"#A0342B\",\"#8C9BA5\",\"#D7E713\"],unlockRule:\"battle-pass\",reward:\"strider-2:L4\",finish:\"marbled\"},{id:\"pal-16\",name:\"Peacock Static\",tagline:\"Peacock green hums gold\",colors:[\"#046A5B\",\"#FAAF00\",\"#5B1E4B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L1\",finish:\"polished-metal\"},{id:\"pal-17\",name:\"Foghorn Flame\",tagline:\"Fog gray with fire-orange siren\",colors:[\"#B8C0C2\",\"#FA3F00\",\"#123C69\"],unlockRule:\"battle-pass\",reward:\"strider-3:L3\",finish:\"brushed-metal\"},{id:\"pal-18\",name:\"Toxic Honey\",tagline:\"Honey drips on toxic lime\",colors:[\"#D9A404\",\"#9BF00B\",\"#4A154B\"],unlockRule:\"battle-pass\",reward:\"strider-3:L4\",finish:\"glaze\"},{id:\"pal-19\",name:\"Neon Crimson Mirage\",tagline:\"Scarlet toxic lime over deep jade\",colors:[\"#FF1744\",\"#76FF03\",\"#005E3C\"],unlockRule:\"battle-pass\",reward:\"strider-4:L1\",finish:\"matte\"},{id:\"pal-20\",name:\"Opal Drain\",tagline:\"Opal sheen on drainpipe green\",colors:[\"#13E7B9\",\"#3E5641\",\"#D1495B\"],unlockRule:\"battle-pass\",reward:\"strider-4:L3\",finish:\"pearl\"},{id:\"pal-21\",name:\"Rust Halo\",tagline:\"Rust ringed by mint flash\",colors:[\"#9C4221\",\"#12E898\",\"#2C2C54\"],unlockRule:\"battle-pass\",reward:\"strider-4:L4\",finish:\"brushed-metal\"},{id:\"pal-22\",name:\"Volt Quartz\",tagline:\"Electric yellow on rose quartz\",colors:[\"#F5E900\",\"#E71363\",\"#264653\"],unlockRule:\"battle-pass\",reward:\"strider-5:L1\",finish:\"glitter\"},{id:\"pal-23\",name:\"Neon Flare Garden\",tagline:\"Laser lime pink on teal steel\",colors:[\"#8CFF00\",\"#FF006E\",\"#00B8A9\"],unlockRule:\"battle-pass\",reward:\"strider-5:L3\",finish:\"matte\"},{id:\"pal-24\",name:\"Crimson Tidebreak\",tagline:\"Crimson breaks on seafoam\",colors:[\"#B91338\",\"#13E7B5\",\"#2A1E5C\"],unlockRule:\"battle-pass\",reward:\"strider-5:L4\",finish:\"watercolor\"},{id:\"pal-25\",name:\"Brass Mirage\",tagline:\"Brass shimmer dusky plum\",colors:[\"#C59B42\",\"#5A2A52\",\"#0D94ED\"],unlockRule:\"battle-pass\",reward:\"strider-6:L1\",finish:\"polished-metal\"},{id:\"pal-26\",name:\"Neon Infrared Bloom\",tagline:\"Red heat sliced by frozen cyan\",colors:[\"#FF0000\",\"#00F5FF\",\"#5B00B5\"],unlockRule:\"battle-pass\",reward:\"strider-6:L3\",finish:\"glaze\"},{id:\"pal-27\",name:\"Ice Magma\",tagline:\"Pigmented ice over magma orange\",colors:[\"#D6F0F0\",\"#F45B0F\",\"#3D2C8D\"],unlockRule:\"battle-pass\",reward:\"strider-6:L4\",finish:\"marbled\"},{id:\"pal-28\",name:\"Fern Radio\",tagline:\"Fern green static magenta\",colors:[\"#3F7A3A\",\"#E4007C\",\"#E7BE13\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L1\",finish:\"matte\"},{id:\"pal-29\",name:\"Porcelain Fuel\",tagline:\"Porcelain white fuel orange\",colors:[\"#F2F2F0\",\"#FF7A1A\",\"#14507A\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L3\",finish:\"glaze\"},{id:\"pal-30\",name:\"Neon Photon Alley\",tagline:\"Yellow signage aqua and violet glass\",colors:[\"#FFD300\",\"#00FFCC\",\"#7A00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-2:L4\",finish:\"brushed-metal\"},{id:\"pal-31\",name:\"Neon Solar Flare\",tagline:\"Hot yellow ember in plum space\",colors:[\"#FFEA00\",\"#FF3D00\",\"#26142B\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L1\",finish:\"polished-metal\"},{id:\"pal-32\",name:\"Wet Cement Rose\",tagline:\"Cement gray wild rose\",colors:[\"#8D9096\",\"#D84A87\",\"#F0E130\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L3\",finish:\"matte\"},{id:\"pal-33\",name:\"Neon Laser Tropics\",tagline:\"Chartreuse beams with scorched orange shadows\",colors:[\"#C6FF00\",\"#FF4D00\",\"#001219\"],unlockRule:\"battle-pass\",reward:\"ringer-3:L4\",finish:\"watercolor\"},{id:\"pal-34\",name:\"Copper Fog\",tagline:\"Copper glows through fog\",colors:[\"#B46A30\",\"#C4CBCE\",\"#B600F5\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L1\",finish:\"brushed-metal\"},{id:\"pal-35\",name:\"Laser Moss\",tagline:\"Laser cyan on moss\",colors:[\"#00E5FF\",\"#546B2F\",\"#FF4FA3\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L3\",finish:\"glitter\"},{id:\"pal-36\",name:\"Bone Ember\",tagline:\"Bone white ember orange\",colors:[\"#EDE8DC\",\"#E4572E\",\"#BA00FF\"],unlockRule:\"battle-pass\",reward:\"ringer-4:L4\",finish:\"matte\"},{id:\"pal-37\",name:\"Plum Static\",tagline:\"Plum noise with electric lime\",colors:[\"#4A1E4F\",\"#B8F000\",\"#33B5C7\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L1\",finish:\"glaze\"},{id:\"pal-38\",name:\"Neon Lime Vortex\",tagline:\"Acid green swirling through electric blue\",colors:[\"#BFFF00\",\"#00E5FF\",\"#12100F\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L3\",finish:\"pearl\"},{id:\"pal-39\",name:\"Clay Command\",tagline:\"Terracotta clay cobalt order\",colors:[\"#C06B55\",\"#2E4BC6\",\"#EDE6D6\"],unlockRule:\"battle-pass\",reward:\"ringer-5:L4\",finish:\"marbled\"},{id:\"pal-40\",name:\"Static Cream\",tagline:\"Cream noise shock green\",colors:[\"#F4EDDE\",\"#39FF14\",\"#4A306D\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L1\",finish:\"glitter\"},{id:\"pal-41\",name:\"Midnight Papaya\",tagline:\"Midnight blue papaya burst\",colors:[\"#191970\",\"#FF9E42\",\"#13E796\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L3\",finish:\"polished-metal\"},{id:\"pal-42\",name:\"Dusty Laser\",tagline:\"Dusty mauve laser green\",colors:[\"#A27E8E\",\"#7CFC00\",\"#1B3B6F\"],unlockRule:\"battle-pass\",reward:\"manyarm-2:L4\",finish:\"matte\"},{id:\"pal-43\",name:\"Neon Atomic Sunset\",tagline:\"Amber fallout glowing beside ultraviolet pink\",colors:[\"#FF9100\",\"#FF00F5\",\"#062A2A\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L1\",finish:\"glaze\"},{id:\"pal-44\",name:\"Fern Flame\",tagline:\"Cool fern fierce flame\",colors:[\"#4F7942\",\"#FF3D2E\",\"#E75A13\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L3\",finish:\"brushed-metal\"},{id:\"pal-45\",name:\"Violet Quarry\",tagline:\"Quarry gray violet eruption\",colors:[\"#7D7F87\",\"#8B3DFF\",\"#DCE35B\"],unlockRule:\"battle-pass\",reward:\"manyarm-3:L4\",finish:\"matte\"},{id:\"pal-46\",name:\"Cadmium Deep\",tagline:\"Cadmium red deep lagoon\",colors:[\"#D32E12\",\"#046E8F\",\"#E79E13\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L1\",finish:\"polished-metal\"},{id:\"pal-47\",name:\"Pigment Riot\",tagline:\"Intense blue maroon and hot orange\",colors:[\"#139FE7\",\"#6E1423\",\"#FF7300\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L3\",finish:\"watercolor\"},{id:\"pal-48\",name:\"Neon Prism Riot\",tagline:\"Sea-glass cyan, magenta bite, lemon flash\",colors:[\"#00FFC6\",\"#FF00B8\",\"#F5FF00\"],unlockRule:\"battle-pass\",reward:\"manyarm-4:L4\",finish:\"pearl\"},{id:\"pal-49\",name:\"Neon Tropic Surge\",tagline:\"Aqua tide, citrus flare, violet thunder\",colors:[\"#00FF9D\",\"#FFB000\",\"#D000FF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L1\",finish:\"glitter\"},{id:\"pal-50\",name:\"Ash Honey\",tagline:\"Ash gray golden honey\",colors:[\"#9BA0A6\",\"#E0A100\",\"#2F6690\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L3\",finish:\"matte\"},{id:\"pal-51\",name:\"Cherry Motor\",tagline:\"Cherry motor oil with electric blue\",colors:[\"#B22234\",\"#3B2F2F\",\"#005CFF\"],unlockRule:\"battle-pass\",reward:\"manyarm-5:L4\",finish:\"brushed-metal\"},{id:\"pal-52\",name:\"Glacier Punch\",tagline:\"Glacier pigmented hot punch\",colors:[\"#13A6E7\",\"#F52549\",\"#3D405B\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L1\",finish:\"glaze\"},{id:\"pal-53\",name:\"Tarnish Bloom\",tagline:\"Tarnished teal floral orange\",colors:[\"#3A7D7B\",\"#F4852B\",\"#E9D6EC\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L3\",finish:\"marbled\"},{id:\"pal-54\",name:\"Umber Signal\",tagline:\"Umber earth neon signal\",colors:[\"#635147\",\"#D0FF4B\",\"#7A28C0\"],unlockRule:\"battle-pass\",reward:\"wedge-2:L4\",finish:\"matte\"},{id:\"pal-55\",name:\"Rose Quartz Coil\",tagline:\"Soft rose coil of teal\",colors:[\"#E7134B\",\"#0FA3A3\",\"#2B2118\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L1\",finish:\"pearl\"},{id:\"pal-56\",name:\"Sulfur Deck\",tagline:\"Sulfur yellow blue and hot orange\",colors:[\"#E3D200\",\"#274C77\",\"#FF4A00\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L3\",finish:\"polished-metal\"},{id:\"pal-57\",name:\"Neon Ember Static\",tagline:\"Orange yellow buzzing with royal voltage\",colors:[\"#FF5E00\",\"#FFE600\",\"#3A0CA3\"],unlockRule:\"battle-pass\",reward:\"wedge-3:L4\",finish:\"watercolor\"},{id:\"pal-58\",name:\"Mango Ash\",tagline:\"Mango bright volcanic ash\",colors:[\"#FFA12B\",\"#59595E\",\"#64DDBB\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L1\",finish:\"matte\"},{id:\"pal-59\",name:\"Steel Petal\",tagline:\"Steel gray crushed petals\",colors:[\"#8A8F98\",\"#E71813\",\"#D8E26E\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L3\",finish:\"brushed-metal\"},{id:\"pal-60\",name:\"Ninja Cream\",tagline:\"Hidden navy cream coral\",colors:[\"#1E2A4A\",\"#F0EBE0\",\"#7EEC00\"],unlockRule:\"battle-pass\",reward:\"wedge-4:L4\",finish:\"glaze\"},{id:\"pal-61\",name:\"Tangerine Static\",tagline:\"Tangerine buzz grape shadow\",colors:[\"#F28500\",\"#3D2159\",\"#202020\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L1\",finish:\"glitter\"},{id:\"pal-62\",name:\"Sage Pistol\",tagline:\"Muted sage pistol pink\",colors:[\"#9AB5A1\",\"#E45C98\",\"#33272A\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L3\",finish:\"matte\"},{id:\"pal-63\",name:\"Lagoon Battery\",tagline:\"Lagoon green charged yellow\",colors:[\"#1D8A7A\",\"#FAE000\",\"#76305B\"],unlockRule:\"battle-pass\",reward:\"wedge-5:L4\",finish:\"polished-metal\"},{id:\"pal-64\",name:\"Wine Cooling\",tagline:\"Warm wine cooled by mint\",colors:[\"#701F3C\",\"#13E78C\",\"#E8D44D\"],unlockRule:\"battle-pass\",reward:\"warden-1:L1\",finish:\"pearl\"},{id:\"pal-65\",name:\"Charcoal Tulip\",tagline:\"Charcoal tulip flame combo\",colors:[\"#2E3033\",\"#FF4E50\",\"#CA00C7\"],unlockRule:\"battle-pass\",reward:\"warden-1:L3\",finish:\"matte\"},{id:\"pal-66\",name:\"Neon Acid Harbor\",tagline:\"Lime light cutting across magenta docks\",colors:[\"#39FF14\",\"#FF007F\",\"#001B2E\"],unlockRule:\"battle-pass\",reward:\"warden-1:L4\",finish:\"pearl\"},{id:\"pal-67\",name:\"Rustic Neon\",tagline:\"Barn red barn lime buzz\",colors:[\"#8A3324\",\"#C1FF33\",\"#2D4A6B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L1\",finish:\"marbled\"},{id:\"pal-68\",name:\"Alice Warning\",tagline:\"Alice blue caution orange\",colors:[\"#1399E7\",\"#FF7E1F\",\"#EEF700\"],unlockRule:\"battle-pass\",reward:\"blob-2:L3\",finish:\"watercolor\"},{id:\"pal-69\",name:\"Smog Candy\",tagline:\"Smog beige candy teal\",colors:[\"#E79813\",\"#00C2A8\",\"#D8395B\"],unlockRule:\"battle-pass\",reward:\"blob-2:L4\",finish:\"matte\"},{id:\"pal-70\",name:\"Violet Furnace\",tagline:\"Furnace violet cooled sky\",colors:[\"#6A2D9E\",\"#F57C3B\",\"#302012\"],unlockRule:\"battle-pass\",reward:\"blob-3:L1\",finish:\"glaze\"},{id:\"pal-71\",name:\"Khaki Storm\",tagline:\"Khaki field storm magenta\",colors:[\"#A89B6E\",\"#E0218A\",\"#2A6F97\"],unlockRule:\"battle-pass\",reward:\"blob-3:L3\",finish:\"brushed-metal\"},{id:\"pal-72\",name:\"Neon Circuit Surge\",tagline:\"Turquoise sparks over midnight violet\",colors:[\"#00F5D4\",\"#F72585\",\"#0B0321\"],unlockRule:\"battle-pass\",reward:\"blob-3:L4\",finish:\"glitter\"},{id:\"pal-73\",name:\"Cedar Ultraviolet\",tagline:\"Cedar wood ultraviolet light\",colors:[\"#7A4532\",\"#8246FF\",\"#E9E4D8\"],unlockRule:\"battle-pass\",reward:\"blob-4:L1\",finish:\"matte\"},{id:\"pal-74\",name:\"Frosted Chili\",tagline:\"Frost blue chili red\",colors:[\"#13AAE7\",\"#C13A2F\",\"#4C5B2C\"],unlockRule:\"battle-pass\",reward:\"blob-4:L3\",finish:\"polished-metal\"},{id:\"pal-75\",name:\"Moss Rocket\",tagline:\"Moss green rocket pink\",colors:[\"#5B7042\",\"#FA0057\",\"#061F5B\"],unlockRule:\"battle-pass\",reward:\"blob-4:L4\",finish:\"matte\"},{id:\"pal-76\",name:\"Pewter Punch\",tagline:\"Pewter softened hot punch\",colors:[\"#99A0A8\",\"#FF3D7F\",\"#164D37\"],unlockRule:\"battle-pass\",reward:\"cap-2:L1\",finish:\"brushed-metal\"},{id:\"pal-77\",name:\"Neon Glacier Riot\",tagline:\"Cyan hot pink and radioactive lime\",colors:[\"#00F0FF\",\"#FF00E5\",\"#CCFF00\"],unlockRule:\"battle-pass\",reward:\"cap-2:L3\",finish:\"watercolor\"},{id:\"pal-78\",name:\"Cola Lightning\",tagline:\"Cola brown lightning yellow\",colors:[\"#3A2618\",\"#F7FF00\",\"#FA005E\"],unlockRule:\"battle-pass\",reward:\"cap-2:L4\",finish:\"glaze\"},{id:\"pal-79\",name:\"Periwinkle Fuel\",tagline:\"Soft periwinkle diesel orange\",colors:[\"#1325E7\",\"#E0641E\",\"#2F4730\"],unlockRule:\"battle-pass\",reward:\"cap-3:L1\",finish:\"matte\"},{id:\"pal-80\",name:\"Jade Alarm\",tagline:\"Jade calm siren red\",colors:[\"#57A78A\",\"#D91E36\",\"#E7AF13\"],unlockRule:\"battle-pass\",reward:\"cap-3:L3\",finish:\"pearl\"},{id:\"pal-81\",name:\"Neon Ultraviolet Pulse\",tagline:\"Violet beams with mint-charged current\",colors:[\"#8C00FF\",\"#00FFB2\",\"#160021\"],unlockRule:\"battle-pass\",reward:\"cap-3:L4\",finish:\"matte\"},{id:\"pal-82\",name:\"Neon Nightcaller\",tagline:\"Cyan violet flame in black-green fog\",colors:[\"#00FFFF\",\"#9400FF\",\"#131A00\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L1\",finish:\"marbled\"},{id:\"pal-83\",name:\"Ivory Shock\",tagline:\"Ivory calm violet shock\",colors:[\"#F0EEE4\",\"#7C1FD3\",\"#FFB200\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L3\",finish:\"glaze\"},{id:\"pal-84\",name:\"Neon Ruby Arc\",tagline:\"Red-hot signal over green afterglow\",colors:[\"#FF073A\",\"#00FF87\",\"#080F1B\"],unlockRule:\"battle-pass\",reward:\"stalk-2:L4\",finish:\"glitter\"},{id:\"pal-85\",name:\"Maroon Cloud\",tagline:\"Maroon heavy cloud blue\",colors:[\"#6D1E2E\",\"#1381E7\",\"#E5D352\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L1\",finish:\"watercolor\"},{id:\"pal-86\",name:\"Chrome Berry\",tagline:\"Chrome band over berry\",colors:[\"#C3C9D1\",\"#8E2A5C\",\"#FFB347\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L3\",finish:\"polished-metal\"},{id:\"pal-87\",name:\"Lentil Neon\",tagline:\"Lentil tan electric aqua\",colors:[\"#B09A6E\",\"#00FFF0\",\"#5F1B63\"],unlockRule:\"battle-pass\",reward:\"stalk-3:L4\",finish:\"matte\"},{id:\"pal-88\",name:\"Neon Cobalt Fuse\",tagline:\"Blue ignition wired to blazing yellow\",colors:[\"#004DFF\",\"#FFEE00\",\"#0D0221\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L1\",finish:\"glaze\"},{id:\"pal-89\",name:\"Spruce Bonfire\",tagline:\"Dark spruce bonfire orange\",colors:[\"#23493C\",\"#FF7A3D\",\"#0C1735\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L3\",finish:\"matte\"},{id:\"pal-90\",name:\"Lilac Foundry\",tagline:\"Lilac meets foundry steel\",colors:[\"#9813E7\",\"#7B8894\",\"#D4522A\"],unlockRule:\"battle-pass\",reward:\"stalk-4:L4\",finish:\"brushed-metal\"},{id:\"pal-91\",name:\"Topaz Battle\",tagline:\"Topaz gold navy clash\",colors:[\"#F0B429\",\"#182952\",\"#FA007D\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L1\",finish:\"polished-metal\"},{id:\"pal-92\",name:\"Dusk Celery\",tagline:\"Dusky purple celery snap\",colors:[\"#574B6B\",\"#C8D86A\",\"#FA2100\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L3\",finish:\"matte\"},{id:\"pal-93\",name:\"Tide Copperfuse\",tagline:\"Tidal teal copperfuse glow\",colors:[\"#0F7B7B\",\"#C07840\",\"#C2F000\"],unlockRule:\"battle-pass\",reward:\"tanka-2:L4\",finish:\"pearl\"},{id:\"pal-94\",name:\"Punch Driftwood\",tagline:\"Fruit punch on driftwood\",colors:[\"#E63362\",\"#6E5B4A\",\"#1648EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L1\",finish:\"watercolor\"},{id:\"pal-95\",name:\"Ink Lime\",tagline:\"Ink smudge streaked lime\",colors:[\"#23232A\",\"#A8E10C\",\"#7E10EA\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L3\",finish:\"glaze\"},{id:\"pal-96\",name:\"Porcelain Prism\",tagline:\"Porcelain violet over tidal blue\",colors:[\"#F5F1E8\",\"#BD05DA\",\"#005F73\"],unlockRule:\"battle-pass\",reward:\"tanka-3:L4\",finish:\"marbled\"},{id:\"pal-97\",name:\"Wasabi Cloud\",tagline:\"Wasabi heat cloud gray\",colors:[\"#9FBF3B\",\"#8E94A0\",\"#E71363\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L1\",finish:\"matte\"},{id:\"pal-98\",name:\"Eggplant Flare\",tagline:\"Eggplant deep flare yellow\",colors:[\"#41284E\",\"#FFD23F\",\"#5BC0BE\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L3\",finish:\"glitter\"},{id:\"pal-99\",name:\"Stone Rose\",tagline:\"Stone gray antique rose\",colors:[\"#8F8A82\",\"#C46E8E\",\"#2F6DA3\"],unlockRule:\"battle-pass\",reward:\"tanka-4:L4\",finish:\"brushed-metal\"},{id:\"pal-100\",name:\"Neon Quasar Pop\",tagline:\"Yellow green comet over indigo gravity\",colors:[\"#FFEF00\",\"#00FF3C\",\"#2D02B8\"],unlockRule:\"battle-pass\",reward:\"lume-1:L1\",finish:\"glaze\"},{id:\"pal-101\",name:\"Arctic Rust\",tagline:\"Arctic white rusted orange\",colors:[\"#E9F2F5\",\"#9E4A1D\",\"#3E3F8F\"],unlockRule:\"battle-pass\",reward:\"lume-1:L3\",finish:\"brushed-metal\"},{id:\"pal-102\",name:\"Magenta Selvage\",tagline:\"Magenta edge denim blue\",colors:[\"#D8117E\",\"#3B5B8C\",\"#E7D313\"],unlockRule:\"battle-pass\",reward:\"lume-1:L4\",finish:\"matte\"},{id:\"pal-103\",name:\"Haze Saffron\",tagline:\"Purple haze saffron spark\",colors:[\"#8467B8\",\"#F4A81D\",\"#1D4E50\"],unlockRule:\"battle-pass\",reward:\"food:L1\",finish:\"pearl\"},{id:\"pal-104\",name:\"Neon Cyber Reed\",tagline:\"Green circuitry humming beside lemon static\",colors:[\"#00FF66\",\"#FFEA00\",\"#4B0082\"],unlockRule:\"battle-pass\",reward:\"food:L2\",finish:\"glitter\"},{id:\"pal-105\",name:\"Neon Magma Bloom\",tagline:\"Molten orange meeting synthetic magenta\",colors:[\"#FF2E00\",\"#F000FF\",\"#0A0A0F\"],unlockRule:\"battle-pass\",reward:\"food:L3\",finish:\"polished-metal\"},{id:\"pal-106\",name:\"Dune Violet\",tagline:\"Warm dune electric violet\",colors:[\"#C9AE7E\",\"#7D2BF0\",\"#13E7E3\"],unlockRule:\"battle-pass\",reward:\"food:L4\",finish:\"marbled\"},{id:\"pal-107\",name:\"Ember Nightshade\",tagline:\"Violet shadows ember teal\",colors:[\"#523090\",\"#EF4E23\",\"#0C5D68\"],unlockRule:\"battle-pass\",reward:\"food:L5\",finish:\"watercolor\"},{id:\"opal-jelly\",name:\"Opal Jelly\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b3fff0\",\"#9767ff\",\"#fff1ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"bubble-glass\",name:\"Bubble Glass\",tagline:\"Shifting highlights and luminous color\",colors:[\"#58eaff\",\"#b8fff8\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"prism-crystal\",name:\"Prism Crystal\",tagline:\"Shifting highlights and luminous color\",colors:[\"#b18aff\",\"#447bff\",\"#fff2ff\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"holo-foil\",name:\"Holographic Foil\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff54de\",\"#39e5ff\",\"#fff56b\"],unlockRule:\"battle-pass\",finish:\"pearl\"},{id:\"glitter-resin\",name:\"Glitter Resin\",tagline:\"Shifting highlights and luminous color\",colors:[\"#ff319c\",\"#7025ff\",\"#fff6db\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"galaxy-geode\",name:\"Galaxy Geode\",tagline:\"Shifting highlights and luminous color\",colors:[\"#21134e\",\"#7b39e8\",\"#63ffff\"],unlockRule:\"battle-pass\",finish:\"glitter\"},{id:\"caustic-slime\",name:\"Caustic Slime\",tagline:\"Organic horror surface\",colors:[\"#8aa52b\",\"#20300b\",\"#dcff57\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"blister-hide\",name:\"Blister Hide\",tagline:\"Organic horror surface\",colors:[\"#9e435f\",\"#2d1727\",\"#e7bb72\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"rotten-rind\",name:\"Rotten Rind\",tagline:\"Organic horror surface\",colors:[\"#626124\",\"#211916\",\"#a2b16b\"],unlockRule:\"battle-pass\",finish:\"matte\"},{id:\"parasite-nest\",name:\"Parasite Nest\",tagline:\"Organic horror surface\",colors:[\"#786541\",\"#151c14\",\"#ece2b0\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"exposed-sinew\",name:\"Exposed Sinew\",tagline:\"Organic horror surface\",colors:[\"#b72945\",\"#300b24\",\"#efb5a1\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"abyssal-maw\",name:\"Abyssal Maw\",tagline:\"Organic horror surface\",colors:[\"#522045\",\"#100d19\",\"#d9e6a7\"],unlockRule:\"battle-pass\",finish:\"glaze\"},{id:\"circuit-alloy\",name:\"Circuit Alloy\",tagline:\"Machine surface\",colors:[\"#273f50\",\"#0a111c\",\"#4effdc\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"servo-armor\",name:\"Servo Armor\",tagline:\"Machine surface\",colors:[\"#617080\",\"#141b25\",\"#ff6739\"],unlockRule:\"battle-pass\",finish:\"brushed-metal\"},{id:\"chrome-rib\",name:\"Chrome Rib\",tagline:\"Machine surface\",colors:[\"#bfcde1\",\"#202f46\",\"#ffffff\"],unlockRule:\"battle-pass\",finish:\"polished-metal\"},{id:\"carbon-mech\",name:\"Carbon Mech\",tagline:\"Machine surface\",colors:[\"#323d46\",\"#080d15\",\"#95bac9\"],unlockRule:\"battle-pass\"},{id:\"hazard-panel\",name:\"Hazard Panel\",tagline:\"Machine surface\",colors:[\"#edb932\",\"#131a20\",\"#ffe9a2\"],unlockRule:\"battle-pass\"},{id:\"reactor-glass\",name:\"Reactor Glass\",tagline:\"Machine surface\",colors:[\"#48e8ff\",\"#063a55\",\"#d4ffff\"],unlockRule:\"battle-pass\",finish:\"glaze\"}];var Ct=\"myr5-recipe-v1\";var Zc=64*1024;var ns=[{id:32,name:\"Plate Steel\",roughness:.34,metalness:.82,relief:.024,bump:.012,sheen:0,sheenRoughness:.7,clearcoat:.35,clearcoatRoughness:.24},{id:33,name:\"Rubber Grip\",roughness:.95,metalness:0,relief:.055,bump:.022,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:34,name:\"Chain Mail\",roughness:.41,metalness:.78,relief:.034,bump:.014,sheen:0,sheenRoughness:.7,clearcoat:.3,clearcoatRoughness:.25},{id:35,name:\"Track Rubber\",roughness:.94,metalness:0,relief:.038,bump:.016,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:36,name:\"Denim\",roughness:.86,metalness:0,relief:.012,bump:.011,sheen:.22,sheenRoughness:.96,clearcoat:0,clearcoatRoughness:.5},{id:37,name:\"Hex Tread\",roughness:.82,metalness:.04,relief:.052,bump:.014,sheen:0,sheenRoughness:.9,clearcoat:.08,clearcoatRoughness:.4},{id:38,name:\"Sweatshirt Fleece\",roughness:.99,metalness:0,relief:.05,bump:.032,sheen:.74,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},{id:39,name:\"Quilted\",roughness:.89,metalness:0,relief:.07,bump:.012,sheen:.14,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:40,name:\"Speckled\",roughness:.96,metalness:0,relief:.03,bump:.028,sheen:.34,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5},{id:41,name:\"Hammered Bronze\",roughness:.39,metalness:.78,relief:.025,bump:.025,sheen:0,sheenRoughness:.7,clearcoat:.28,clearcoatRoughness:.22},{id:42,name:\"Fine Stripe\",roughness:.91,metalness:0,relief:.042,bump:.012,sheen:.08,sheenRoughness:.92,clearcoat:0,clearcoatRoughness:.5},{id:43,name:\"Snake Skin\",roughness:.68,metalness:.02,relief:.019,bump:.023,sheen:.1,sheenRoughness:.76,clearcoat:.08,clearcoatRoughness:.4},{id:44,name:\"Holey\",roughness:.96,metalness:0,relief:.039,bump:.025,sheen:0,sheenRoughness:.9,clearcoat:0,clearcoatRoughness:.5},{id:45,name:\"Woven Mat\",roughness:.9,metalness:0,relief:.014,bump:.014,sheen:.08,sheenRoughness:.94,clearcoat:0,clearcoatRoughness:.5},{id:46,name:\"Petal\",roughness:.51,metalness:0,relief:.025,bump:.009,sheen:.52,sheenRoughness:.72,clearcoat:.12,clearcoatRoughness:.3},{id:47,name:\"Graph Paper\",roughness:.93,metalness:0,relief:.012,bump:.017,sheen:.16,sheenRoughness:.97,clearcoat:0,clearcoatRoughness:.5},{id:48,name:\"Bamboo\",roughness:.57,metalness:0,relief:.027,bump:.008,sheen:.18,sheenRoughness:.62,clearcoat:.12,clearcoatRoughness:.28},{id:49,name:\"Dragon Scale\",roughness:.31,metalness:.16,relief:.043,bump:.01,sheen:.24,sheenRoughness:.42,clearcoat:.72,clearcoatRoughness:.16},{id:50,name:\"Cool Graph Paper\",roughness:.56,metalness:.23,relief:.021,bump:.008,sheen:0,sheenRoughness:.8,clearcoat:.12,clearcoatRoughness:.3},{id:51,name:\"Terry Cloth\",roughness:.98,metalness:0,relief:.014,bump:.026,sheen:.62,sheenRoughness:.99,clearcoat:0,clearcoatRoughness:.5},{id:52,name:\"Pebble Path\",roughness:.77,metalness:0,relief:.038,bump:.018,sheen:0,sheenRoughness:.8,clearcoat:.08,clearcoatRoughness:.4},{id:53,name:\"Wiggles\",roughness:.99,metalness:0,relief:.016,bump:.016,sheen:0,sheenRoughness:.95,clearcoat:0,clearcoatRoughness:.5},{id:54,name:\"River Stone\",roughness:.72,metalness:.01,relief:.031,bump:.019,sheen:.04,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.32},{id:55,name:\"Moss\",roughness:.99,metalness:0,relief:.022,bump:.023,sheen:.38,sheenRoughness:.98,clearcoat:0,clearcoatRoughness:.5}],is={id:56,name:\"64-bit Skin\",roughness:.58,metalness:.12,relief:.018,bump:.01,sheen:0,sheenRoughness:.7,clearcoat:.1,clearcoatRoughness:.4},ls=[{id:57,name:\"Opal Jelly\",roughness:.06,metalness:0,relief:.004,bump:.001,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.035},{id:58,name:\"Bubble Glass\",roughness:.035,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},{id:59,name:\"Prism Crystal\",roughness:.045,metalness:0,relief:.052,bump:.008,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.03},{id:60,name:\"Holographic Foil\",roughness:.18,metalness:.88,relief:.022,bump:.013,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.045},{id:61,name:\"Glitter Resin\",roughness:.075,metalness:0,relief:.006,bump:.002,sheen:0,sheenRoughness:.3,clearcoat:1,clearcoatRoughness:.025},{id:62,name:\"Galaxy Geode\",roughness:.22,metalness:.25,relief:.065,bump:.014,sheen:0,sheenRoughness:.3,clearcoat:.8,clearcoatRoughness:.06},{id:63,name:\"Caustic Slime\",roughness:.055,metalness:0,relief:.035,bump:.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:64,name:\"Blister Hide\",roughness:.18,metalness:0,relief:.065,bump:.023,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:65,name:\"Rotten Rind\",roughness:.94,metalness:0,relief:.05,bump:.025,sheen:0,sheenRoughness:.6,clearcoat:0,clearcoatRoughness:.07},{id:66,name:\"Parasite Nest\",roughness:.31,metalness:0,relief:.07,bump:.027,sheen:0,sheenRoughness:.6,clearcoat:.6,clearcoatRoughness:.07},{id:67,name:\"Exposed Sinew\",roughness:.2,metalness:0,relief:.045,bump:.017,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:68,name:\"Abyssal Maw\",roughness:.26,metalness:0,relief:.065,bump:.023,sheen:0,sheenRoughness:.6,clearcoat:.8,clearcoatRoughness:.07},{id:69,name:\"Circuit Alloy\",roughness:.19,metalness:.83,relief:.027,bump:.01,sheen:0,sheenRoughness:.6,clearcoat:.6,clearcoatRoughness:.07},{id:70,name:\"Servo Armor\",roughness:.34,metalness:.82,relief:.055,bump:.013,sheen:0,sheenRoughness:.6,clearcoat:.4,clearcoatRoughness:.07},{id:71,name:\"Chrome Rib\",roughness:.09,metalness:.96,relief:.05,bump:.012,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07},{id:72,name:\"Carbon Mech\",roughness:.64,metalness:.18,relief:.018,bump:.013,sheen:0,sheenRoughness:.6,clearcoat:.25,clearcoatRoughness:.07},{id:73,name:\"Hazard Panel\",roughness:.46,metalness:.55,relief:.032,bump:.014,sheen:0,sheenRoughness:.6,clearcoat:.4,clearcoatRoughness:.07},{id:74,name:\"Reactor Glass\",roughness:.045,metalness:0,relief:.018,bump:.004,sheen:0,sheenRoughness:.6,clearcoat:1,clearcoatRoughness:.07}],ds=new Map([...ns,is,...ls].map(e=>[e.id,e])),vt=e=>ds.get(e);var At=(e,a,t)=>a.unlockRule===\"default\"||oe(e,a.id,typeof t==\"string\"?t:void 0),Ft=e=>ba.includes(e.primary.toLowerCase())?\"default\":\"battle-pass\",bs=(e=I())=>pe(\"boss-skin\",w(typeof e==\"string\"?e:I(),`${typeof e==\"string\"?e:I()}-skin`)),us=(e,a)=>!H.includes(e.id)&&(fe.includes(e.id)||oe(\"texture\",e.id,a))&&(e.id!==\"coach-64-bit\"||bs(a)),Mt=(e,a)=>At(\"color\",e,a),Lt=(e,a)=>At(\"palette\",e,a),fs={id:30,name:\"Flat\",realm:\"\",primary:\"#c7c3ce\",secondary:\"#6d6a75\",accent:\"#ffffff\",emissive:\"#000000\",roughness:.55,metalness:0,detail:\"flat\"},wa={id:31,name:\"Clay\",realm:\"\",primary:\"#b7a68e\",secondary:\"#7a6b57\",accent:\"#ddcdb3\",emissive:\"#000000\",roughness:.92,metalness:0,detail:\"clay\"},pn={id:\"flat\",displayName:\"Flat\",unlockRule:\"battle-pass\",familyId:fs.id,defaultColorId:\"#7f7d78\"},Ot={id:\"clay\",displayName:\"Clay\",unlockRule:\"default\",familyId:wa.id,defaultColorId:\"#c4a77d\"},ms=z.map(e=>({id:\"legacy-color-\"+e.id,displayName:e.name+\" (original)\",unlockRule:Ft(e),primary:e.primary,secondary:e.secondary,accent:e.accent})),hs=[{id:\"default-slate\",displayName:\"Slate\",unlockRule:\"default\",primary:\"#8b8f9a\",secondary:\"#4a4d55\",accent:\"#e7e9ee\"},{id:\"default-clay\",displayName:\"Warm Clay\",unlockRule:\"default\",primary:\"#b7a68e\",secondary:\"#7a6b57\",accent:\"#ddcdb3\"},{id:\"default-ruby\",displayName:\"Ruby\",unlockRule:\"default\",primary:\"#a23b4a\",secondary:\"#4f1620\",accent:\"#f2a3ae\"},{id:\"default-sapphire\",displayName:\"Sapphire\",unlockRule:\"default\",primary:\"#2d5aa0\",secondary:\"#122a4d\",accent:\"#a9c9f5\"},{id:\"default-moss\",displayName:\"Moss\",unlockRule:\"default\",primary:\"#4c7a3f\",secondary:\"#20351a\",accent:\"#c3e6a8\"},{id:\"default-gold\",displayName:\"Gold\",unlockRule:\"default\",primary:\"#c9a13a\",secondary:\"#5f4a15\",accent:\"#ffe9a8\"},{id:\"default-charcoal\",displayName:\"Charcoal\",unlockRule:\"default\",primary:\"#333238\",secondary:\"#131318\",accent:\"#8d8d96\"},{id:\"default-blush\",displayName:\"Blush\",unlockRule:\"default\",primary:\"#d98fa0\",secondary:\"#7a3d49\",accent:\"#ffdbe4\"}].map(e=>({...e,unlockRule:Ft(e)})),ps={\"texture-1\":1,\"texture-2\":3,\"texture-3\":5},gs=[{id:\"chest-plate-steel\",name:\"Plate Steel\",slot:\"texture-1\",track:\"chest\",familyId:32},{id:\"chest-rubber-grip\",name:\"Rubber Grip\",slot:\"texture-2\",track:\"chest\",familyId:33},{id:\"chest-chain-mail\",name:\"Chain Mail\",slot:\"texture-3\",track:\"chest\",familyId:34},{id:\"quads-track-rubber\",name:\"Track Rubber\",slot:\"texture-1\",track:\"quads\",familyId:35},{id:\"quads-denim\",name:\"Denim\",slot:\"texture-2\",track:\"quads\",familyId:36},{id:\"quads-hex-tread\",name:\"Hex Tread\",slot:\"texture-3\",track:\"quads\",familyId:37},{id:\"glutes-sweatshirt-fleece\",name:\"Sweatshirt Fleece\",slot:\"texture-1\",track:\"glutes\",familyId:38},{id:\"glutes-quilted\",name:\"Quilted\",slot:\"texture-2\",track:\"glutes\",familyId:39},{id:\"glutes-peach\",name:\"Speckled\",slot:\"texture-3\",track:\"glutes\",familyId:40},{id:\"arms-hammered-bronze\",name:\"Hammered Bronze\",slot:\"texture-1\",track:\"arms\",familyId:41},{id:\"arms-rope\",name:\"Fine Stripe\",slot:\"texture-2\",track:\"arms\",familyId:42},{id:\"arms-leather\",name:\"Snake Skin\",slot:\"texture-3\",track:\"arms\",familyId:43},{id:\"yoga-cork\",name:\"Holey\",slot:\"texture-1\",track:\"yoga\",familyId:44},{id:\"yoga-woven-mat\",name:\"Woven Mat\",slot:\"texture-2\",track:\"yoga\",familyId:45},{id:\"yoga-petal\",name:\"Petal\",slot:\"texture-3\",track:\"yoga\",familyId:46},{id:\"martial-arts-canvas-gi\",name:\"Graph Paper\",slot:\"texture-1\",track:\"martial-arts\",familyId:47},{id:\"martial-arts-bamboo\",name:\"Bamboo\",slot:\"texture-2\",track:\"martial-arts\",familyId:48},{id:\"martial-arts-dragon-scale\",name:\"Dragon Scale\",slot:\"texture-3\",track:\"martial-arts\",familyId:49},{id:\"cardio-mesh\",name:\"Cool Graph Paper\",slot:\"texture-1\",track:\"cardio\",familyId:50},{id:\"cardio-terry-cloth\",name:\"Terry Cloth\",slot:\"texture-2\",track:\"cardio\",familyId:51},{id:\"cardio-pebble-path\",name:\"Pebble Path\",slot:\"texture-3\",track:\"cardio\",familyId:52},{id:\"meditation-sand-garden\",name:\"Wiggles\",slot:\"texture-1\",track:\"meditation\",familyId:53},{id:\"meditation-river-stone\",name:\"River Stone\",slot:\"texture-2\",track:\"meditation\",familyId:54},{id:\"meditation-moss\",name:\"Moss\",slot:\"texture-3\",track:\"meditation\",familyId:55}],ys=new Map(Object.entries(da).map(([e,[a]])=>[a,e])),It=e=>fe.includes(e)?\"default\":\"battle-pass\",Tt=gs.map(e=>({id:e.id,displayName:e.name,unlockRule:It(e.id),track:e.track,passLevel:ps[e.slot],packId:\"pack-\"+e.track,familyId:e.familyId,defaultColorId:\"#7f7d78\"})),ks=z.map(e=>{let a=\"legacy-\"+e.id,t=Tt.find(r=>r.id===ys.get(a));return{id:a,displayName:e.name,unlockRule:It(a),track:t?.track,passLevel:t?.passLevel,legacy:!0,familyId:e.id,defaultColorId:\"legacy-color-\"+e.id}}),ws={id:\"coach-64-bit\",displayName:\"64-bit Pixel Finish\",unlockRule:\"battle-pass\",familyId:56,defaultColorId:\"#7f7d78\"},_s=[{id:\"opal-jelly\",displayName:\"Opal Jelly\",unlockRule:\"battle-pass\",familyId:57,defaultColorId:\"opal-jelly\"},{id:\"bubble-glass\",displayName:\"Bubble Glass\",unlockRule:\"battle-pass\",familyId:58,defaultColorId:\"bubble-glass\"},{id:\"prism-crystal\",displayName:\"Prism Crystal\",unlockRule:\"battle-pass\",familyId:59,defaultColorId:\"prism-crystal\"},{id:\"holo-foil\",displayName:\"Holographic Foil\",unlockRule:\"battle-pass\",familyId:60,defaultColorId:\"holo-foil\"},{id:\"glitter-resin\",displayName:\"Glitter Resin\",unlockRule:\"battle-pass\",familyId:61,defaultColorId:\"glitter-resin\"},{id:\"galaxy-geode\",displayName:\"Galaxy Geode\",unlockRule:\"battle-pass\",familyId:62,defaultColorId:\"galaxy-geode\"},{id:\"caustic-slime\",displayName:\"Caustic Slime\",unlockRule:\"battle-pass\",familyId:63,defaultColorId:\"caustic-slime\"},{id:\"blister-hide\",displayName:\"Blister Hide\",unlockRule:\"battle-pass\",familyId:64,defaultColorId:\"blister-hide\"},{id:\"rotten-rind\",displayName:\"Rotten Rind\",unlockRule:\"battle-pass\",familyId:65,defaultColorId:\"rotten-rind\"},{id:\"parasite-nest\",displayName:\"Parasite Nest\",unlockRule:\"battle-pass\",familyId:66,defaultColorId:\"parasite-nest\"},{id:\"exposed-sinew\",displayName:\"Exposed Sinew\",unlockRule:\"battle-pass\",familyId:67,defaultColorId:\"exposed-sinew\"},{id:\"abyssal-maw\",displayName:\"Abyssal Maw\",unlockRule:\"battle-pass\",familyId:68,defaultColorId:\"abyssal-maw\"},{id:\"circuit-alloy\",displayName:\"Circuit Alloy\",unlockRule:\"battle-pass\",familyId:69,defaultColorId:\"circuit-alloy\"},{id:\"servo-armor\",displayName:\"Servo Armor\",unlockRule:\"battle-pass\",familyId:70,defaultColorId:\"servo-armor\"},{id:\"chrome-rib\",displayName:\"Chrome Rib\",unlockRule:\"battle-pass\",familyId:71,defaultColorId:\"chrome-rib\"},{id:\"carbon-mech\",displayName:\"Carbon Mech\",unlockRule:\"battle-pass\",familyId:72,defaultColorId:\"carbon-mech\"},{id:\"hazard-panel\",displayName:\"Hazard Panel\",unlockRule:\"battle-pass\",familyId:73,defaultColorId:\"hazard-panel\"},{id:\"reactor-glass\",displayName:\"Reactor Glass\",unlockRule:\"battle-pass\",familyId:74,defaultColorId:\"reactor-glass\"}].filter(e=>!H.includes(e.id)),Ss=[..._s,Ot,...ks,...Tt,ws].filter(e=>!H.includes(e.id)),Pt=[...hs,...ms],Bt=xt.map(({name:e,...a})=>({...a,displayName:e})),xs=e=>Ss.find(a=>a.id===e),Cs=e=>Pt.find(a=>a.id===e),vs=e=>Bt.find(a=>a.id===e);function Rs(e){let[a,t,r]=e.colors;return{primary:a,secondary:t,accent:r,paletteId:e.id}}var Es=/^#[0-9a-f]{6}$/i,Rt=(e,a,t)=>\"#\"+[1,3,5].map(r=>Math.round(parseInt(e.slice(r,r+2),16)*(1-t)+a*t).toString(16).padStart(2,\"0\")).join(\"\"),As=(e,a)=>ba.includes(e)||Pt.some(t=>Mt(t,a)&&[t.primary,t.secondary,t.accent].some(r=>r.toLowerCase()===e))||Bt.some(t=>Lt(t,a)&&t.colors.some(r=>r.toLowerCase()===e));function Et(e,a=!1,t){if(Es.test(e)){let o=e.toLowerCase();return a||As(o,t)?{primary:o,secondary:Rt(o,0,.55),accent:Rt(o,255,.55)}:void 0}let r=Cs(e);if(r)return a||Mt(r,t)?{primary:r.primary,secondary:r.secondary,accent:r.accent}:void 0;let s=vs(e);if(s)return a||Lt(s,t)?Rs(s):void 0}function Dt(e){try{if(!e)return;ha(e)}catch{}}try{let e=localStorage.getItem(Ct);e&&Dt(JSON.parse(e))}catch{}globalThis.addEventListener?.(\"myr5:recipe\",e=>Dt(e.detail));function Nt(e,a){let t=e?.[a],r=e?.[qa[a]];return t&&r&&r!==t?{...t,colorId:r.colorId}:t}function zt(e,a,t=!1,r){if(!a)return{...z[e],sparkle:0,paletteId:void 0};let s=xs(a.textureId),o=s&&(t||us(s,r))&&s.familyId>=0?s:Ot,c=vt(o.familyId),n=o.legacy?z[o.familyId]:o.id===\"clay\"?wa:c?{id:c.id,name:c.name,realm:\"\",primary:\"#8b8f9a\",secondary:\"#4a4d55\",accent:\"#e7e9ee\",emissive:\"#000000\",roughness:c.roughness,metalness:c.metalness,detail:c.name.toLowerCase().replace(/[^a-z0-9]+/g,\"-\")}:wa,u=Et(a.colorId,t,r)??Et(o.defaultColorId,t,r)??n,i=Number.isFinite(a.metallic)?Math.max(0,Math.min(1,a.metallic)):n.metalness,l=Number.isFinite(a.sparkle)?Math.max(0,Math.min(1,a.sparkle)):0;return{...n,...u,paletteId:\"paletteId\"in u?u.paletteId:void 0,metalness:i,sparkle:l}}var Fs=\"myr5-war-room-coaches-v2\",Gt=()=>`${Fs}/${encodeURIComponent(v())}`;async function Ms(e){let a=await fetch(\"/pod/gala-coaches/manifest.json\");if(!a.ok)throw Error(\"Coach sprites are unavailable.\");let t=await a.json(),r=t.sprites.filter(i=>F.some(l=>l.id===i.id)),s=new Map,o=async i=>{let l=new Image;return l.src=\"/pod/gala-coaches/\"+i,await l.decode(),l};await Promise.all(Object.entries(t.sheets).map(async([i,l])=>{let[d,b]=await Promise.all([o(l.image),o(l.mask)]);s.set(i,{image:d,mask:b})}));let c=()=>{try{return oa(localStorage.getItem(\"myr5-recipe-v1\")||\"null\")}catch{return Fe()}},n=new Map;function u(i,l=!1,d,b=!1){let y=c(),h=d&&globalThis.GalaAvatar?.skinColor?.(d),k=b&&Re(i.id)?[\"#e8bc4d\",\"#ffe4a1\",\"#6b4214\"]:h?[h,h,\"#fff9df\"]:[\"body\",\"head\",\"eye\"].map(S=>zt(y.styles[S],Nt(y.materials,S),!0,i.id).primary),T=JSON.stringify([i.id,l,k]);if(n.has(T))return n.get(T);let[ge,f,p,m]=i.frame,_=e.createElement(\"canvas\");_.width=p,_.height=m;let G=_.getContext(\"2d\",{willReadFrequently:!0}),P=e.createElement(\"canvas\");P.width=p,P.height=m;let ie=s.get(i.sheet);G.drawImage(ie.image,ge,f,p,m,0,0,p,m);let g=G.getImageData(0,0,p,m),W=P.getContext(\"2d\",{willReadFrequently:!0});W.drawImage(ie.mask,ge,f,p,m,0,0,p,m);let K=W.getImageData(0,0,p,m).data,ye=S=>{let U=parseInt(S.slice(1),16);return[U>>16,U>>8&255,U&255]};for(let S=0;S<g.data.length;S+=4){if(!g.data[S+3])continue;let U=K[S],_a=U===0?[33,23,46]:ye(k[Math.min(2,Math.round(U/64)-1)]),Ut=g.data[S]/184;for(let le=0;le<3;le++)g.data[S+le]=l?35:U===0?_a[le]:Math.min(255,Math.round(_a[le]*Ut))}return G.putImageData(g,0,0),n.set(T,_),_}return{sprites:r,art:u}}async function Cn(e){let{sprites:a,art:t}=await Ms(e),r=i=>St(i,{}),s={body:null,pet:null,goldenBody:!1},o=\"\";function c(){let i=Gt(),l=null;try{l=localStorage.getItem(i)}catch{}if(i+\" \"+l!==o){o=i+\" \"+l,s={body:null,pet:null,goldenBody:!1};try{let d=JSON.parse(l||\"null\");for(let b of[\"body\",\"pet\"])a.some(y=>y.id===d?.[b]&&(b===\"body\"||y.kind===b))&&!r(d[b])&&(s[b]=d[b]);s.goldenBody=d?.goldenBody===!0&&!!s.body&&Re(s.body)}catch{}}}c();function n(i,l){c();let d=e.createElement(\"fieldset\"),b=e.createElement(\"legend\");b.textContent=i===\"body\"?\"Coach body\":\"Coach pets\",d.append(b),d.className=\"gala-coach-choices\";let y=e.createElement(\"p\");y.className=\"help\",y.textContent=i===\"body\"?\"Choose a coach character, then change its colour in the mirror. Choose Gala body to show your Gala wardrobe.\":\"Coach pets unlock with their coach bodies.\",d.append(y);let h=(f,p=!1)=>{if(f&&r(f))return;let m={...s,[i]:f,...i===\"body\"?{goldenBody:p}:{}};try{localStorage.setItem(Gt(),JSON.stringify(m)),s=m,l()}catch{d.setAttribute(\"aria-label\",\"This choice could not be saved. Storage is unavailable.\")}},k=e.createElement(\"button\");k.type=\"button\",k.textContent=i===\"body\"?\"Gala body\":\"Gala pet\",k.setAttribute(\"aria-pressed\",String(!s[i])),k.onclick=()=>h(null),d.append(k);let T=new Set(we(E().paths)),ge=[...a.filter(f=>i===\"body\"||f.kind===i)].sort((f,p)=>+!!r(f.id)-+!!r(p.id)||Number(T.has(p.id))-Number(T.has(f.id)));for(let f of ge){let p=r(f.id),m=e.createElement(\"button\"),_=F.find(g=>g.id===f.id)?.label||f.family;m.type=\"button\",m.dataset.coachSprite=f.id,m.setAttribute(\"aria-label\",_),m.setAttribute(\"aria-pressed\",String(s[i]===f.id&&(i!==\"body\"||!s.goldenBody)));let G=t(f),P=e.createElement(\"canvas\");P.width=G.width,P.height=G.height,P.getContext(\"2d\").drawImage(G,0,0);let ie=e.createElement(\"span\");if(ie.textContent=_,m.append(P,ie),m.onclick=()=>{if(p){y.textContent=`Preview only \\xB7 How to unlock ${_}: ${p}.`;return}h(f.id)},d.append(m),i===\"body\"&&Re(f.id)&&!p){let g=e.createElement(\"button\");g.type=\"button\",g.dataset.coachSprite=f.id,g.dataset.goldenCoach=\"true\",g.setAttribute(\"aria-label\",\"Golden \"+_),g.setAttribute(\"aria-pressed\",String(s.body===f.id&&s.goldenBody));let W=t(f,!1,void 0,!0),K=e.createElement(\"canvas\");K.width=W.width,K.height=W.height,K.getContext(\"2d\").drawImage(W,0,0);let ye=e.createElement(\"span\");ye.textContent=\"Golden \"+_,g.append(K,ye),g.onclick=()=>h(f.id,!0),d.append(g)}}return d}function u(i,l,d,b={}){c();let y=b.petOnly?s.pet:s.body,h=a.find(T=>T.id===y);if(!h||r(h.id)){i(l,d,b);return}l.width=64,l.height=96;let k=l.getContext(\"2d\");k.imageSmoothingEnabled=!1,b.petOnly?k.drawImage(t(h,!1,d,!b.petOnly&&s.goldenBody),0,0,48,36,0,60,48,36):h.kind===\"pet\"?k.drawImage(t(h,!1,d,!b.petOnly&&s.goldenBody),0,0,48,36,0,48,64,48):k.drawImage(t(h,!1,d,!b.petOnly&&s.goldenBody),0,0)}return{picker:n,draw:u,get hasBody(){return c(),!!s.body&&!r(s.body)},get hasPet(){return c(),!!s.pet&&!r(s.pet)}}}export{Ms as loadCoachSpritePreviews,Cn as loadWarRoomCoaches};\n","war-room/effect-picker.mjs":"import { EFFECT_GROUPS, AURA_OPTIONS, EFFECT_CHOICES_KEY, readEffectChoices, saveEffectChoice } from '../arcade/power/aura-options.mjs';\r\n\r\nconst animalFrames = {\r\n  rabbit:[32,26], fox:[64,36], bear:[64,33], deer:[48,52], wolf:[64,40], boar:[64,40],\r\n};\r\nconst plantCells = { fern:1, vine:14, flower:48, mushroom:70, grass:0, reed:6 };\r\nconst crystalAssets = {\r\n  blue:'crystal-blue.png', ice:'crystal-icy.png', red:'crystal-red-small.png',\r\n  green:'crystal-green-alt.png', gold:'crystal-gold.png', purple:'crystal-purple.png',\r\n};\r\nconst imageCache = new Map();\r\n\r\nfunction sourceFor(option) {\r\n  const id=option.id, ecology=option.ecology;\r\n  if (ecology?.kind==='plant') return {url:'/arcade/hold/assets/flowers-plants_2.png',sx:plantCells[ecology.variant]*12,sy:0,sw:12,sh:24};\r\n  if (ecology?.kind==='animal') {\r\n    const [sw,sh]=animalFrames[ecology.variant]||[64,40],name=ecology.variant[0].toUpperCase()+ecology.variant.slice(1);\r\n    const action=ecology.variant==='wolf'?'Walk':'Idle';\r\n    return {url:`/arcade/hold/assets/${ecology.variant}/${name}/${name}_${action}.png`,sx:0,sy:0,sw,sh};\r\n  }\r\n  if (ecology?.kind==='mineral'&&ecology.variant==='rock') return {url:'/arcade/hold/assets/rocks-original.png',sx:0,sy:0,sw:32,sh:32};\r\n  if (ecology?.kind==='mineral'&&ecology.variant==='crystal') return {url:`/arcade/hold/assets/crystals/PNG/32x32/${crystalAssets[ecology.palette]||crystalAssets.blue}`};\r\n  if (option.asset) return {url:`/arcade/${id==='blizzard'?'hold':'power'}/assets/${option.asset}`,sx:0,sy:0,sw:100,sh:100};\r\n  if (id==='lightning') return {url:'/arcade/power/assets/pixelated_tilesheet.png',sx:0,sy:0,sw:64,sh:64};\r\n  if (id==='floor') return {url:'/arcade/power/assets/11_fire_spritesheet.png',sx:0,sy:0,sw:100,sh:100};\r\n  // Random and Classic keep a real, familiar effect swatch without generating emoji art.\r\n  return {url:'/arcade/power/assets/9_brightfire_spritesheet.png',sx:0,sy:0,sw:100,sh:100,frame:24};\r\n}\r\nfunction loadImage(url) {\r\n  if(imageCache.has(url)) return imageCache.get(url);\r\n  const promise=new Promise((resolve,reject)=>{const image=new Image();image.onload=()=>resolve(image);image.onerror=()=>reject(Error(`Could not load effect preview ${url}`));image.src=url;});\r\n  imageCache.set(url,promise);return promise;\r\n}\r\nasync function paint(canvas,option,detached=false) {\r\n  const ctx=canvas.getContext('2d');if(!ctx)return false;\r\n  const spec=sourceFor(option);\r\n  try {\r\n    const image=await loadImage(spec.url);if(!detached&&!canvas.isConnected)return false;\r\n    const sx=spec.sx??0,sy=spec.sy??0,sw=spec.sw??image.naturalWidth,sh=spec.sh??image.naturalHeight;\r\n    let frame=spec.frame??0,columns=option.columns;\r\n    if(option.asset&&columns){const count=columns*(option.rows||columns);frame=Math.min(count-1,Math.floor(count/3));}\r\n    else if(option.id==='floor')frame=9;\r\n    if(option.asset&&columns){const size=100;spec.sx=(frame%columns)*size;spec.sy=Math.floor(frame/columns)*size;}\r\n    if(option.id==='floor'){spec.sx=(frame%8)*100;spec.sy=Math.floor(frame/8)*100;}\r\n    const cropX=spec.sx??sx,cropY=spec.sy??sy;\r\n    const tile=canvas.ownerDocument.createElement('canvas');tile.width=spec.sw||sw;tile.height=spec.sh||sh;\r\n    const tileCtx=tile.getContext('2d');tileCtx.drawImage(image,cropX,cropY,tile.width,tile.height,0,0,tile.width,tile.height);\r\n    const pixels=tileCtx.getImageData(0,0,tile.width,tile.height),data=pixels.data;\r\n    let left=tile.width,top=tile.height,right=-1,bottom=-1;\r\n    for(let y=0;y<tile.height;y++)for(let x=0;x<tile.width;x++){\r\n      const i=(y*tile.width+x)*4;\r\n      if(option.ecology?.kind==='plant'&&data[i]===34&&data[i+1]===32&&data[i+2]===52)data[i+3]=0;\r\n      if(data[i+3]>12){left=Math.min(left,x);right=Math.max(right,x);top=Math.min(top,y);bottom=Math.max(bottom,y);}\r\n    }\r\n    if(right<left)return false;\r\n    tileCtx.putImageData(pixels,0,0);\r\n    const visibleW=right-left+1,visibleH=bottom-top+1,scale=Math.min(34/visibleW,34/visibleH);\r\n    const w=visibleW*scale,h=visibleH*scale;\r\n    ctx.clearRect(0,0,40,40);ctx.imageSmoothingEnabled=false;\r\n    ctx.drawImage(tile,left,top,visibleW,visibleH,(40-w)/2,(40-h)/2,w,h);\r\n    return true;\r\n  } catch { ctx.clearRect(0,0,40,40);return false; }\r\n}\r\n\r\n// Auras live in the War Room's own pixel bay panel (window.warRoomGala.showBay('auras')): each choice is a\r\n// tile previewing the aura on the player's DFO-rig Gala character, like every other bay.\r\nasync function paintOnCharacter(canvas,option,avatar,look){\r\n  const ctx=canvas.getContext('2d');if(!ctx)return false;\r\n  const fx=canvas.ownerDocument.createElement('canvas');fx.width=fx.height=40;\r\n  const ok=await paint(fx,option,true);if(!canvas.isConnected)return false;\r\n  ctx.clearRect(0,0,canvas.width,canvas.height);ctx.imageSmoothingEnabled=false;\r\n  if(ok){ctx.globalAlpha=.9;ctx.drawImage(fx,0,0,40,40,-4,6,56,56);ctx.globalAlpha=1;}\r\n  if(avatar&&look){const body=canvas.ownerDocument.createElement('canvas');avatar.draw(body,look,{base:false,weapon:false,companion:false,prop:false});ctx.drawImage(body,8,2,48,90,8,2,32,60);}\r\n  if(ok){ctx.globalAlpha=.45;ctx.drawImage(fx,0,0,40,40,4,20,40,40);ctx.globalAlpha=1;}\r\n  return true;\r\n}\r\n\r\nexport function effectPanel(doc,{avatar=globalThis.GalaAvatar,look=null}={}){\r\n  const root=doc.createElement('div');root.className='gala-aura-panel';\r\n  const status=doc.createElement('p');status.className='help gala-preview-status';status.setAttribute('role','status');\r\n  let choices=readEffectChoices();\r\n  const optionMap=new Map(AURA_OPTIONS.map(option=>[option.id,option]));\r\n  for(const group of EFFECT_GROUPS){\r\n    const fieldset=doc.createElement('fieldset');fieldset.className='gala-editor-group';fieldset.dataset.effectGroup=group.id;\r\n    const legend=doc.createElement('legend');legend.textContent=group.label;fieldset.append(legend);\r\n    const grid=doc.createElement('div');grid.className='gala-option-grid';grid.setAttribute('role','radiogroup');grid.setAttribute('aria-label',`${group.label} aura`);\r\n    const selected=choices[group.id]||group.defaultEffect||'random';\r\n    for(const id of group.options){\r\n      const option=optionMap.get(id);if(!option)continue;\r\n      const button=doc.createElement('button');button.type='button';button.className='gala-option';button.dataset.effectGroup=group.id;button.dataset.effectChoice=id;\r\n      button.setAttribute('aria-pressed',String(id===selected));button.setAttribute('aria-label',`${group.label}: ${option.label}`);\r\n      const art=doc.createElement('canvas');art.width=48;art.height=64;art.className='gala-option-art is-tall';art.setAttribute('aria-hidden','true');\r\n      const name=doc.createElement('span');name.className='gala-option-name';name.textContent=option.label;button.append(art,name);\r\n      button.addEventListener('click',()=>{\r\n        if(!saveEffectChoice(group.id,id)){status.textContent='This aura could not be saved on this device.';return;}\r\n        choices={...choices,[group.id]:id};grid.querySelectorAll('.gala-option').forEach(item=>item.setAttribute('aria-pressed',String(item===button)));\r\n        status.textContent=`Saved on this device · ${group.label}: ${option.label}.`;\r\n      });\r\n      grid.append(button);\r\n      queueMicrotask(()=>{void paintOnCharacter(art,optionMap.get(id==='random'?group.defaultEffect:id)||option,avatar,look);});\r\n    }\r\n    fieldset.append(grid);root.append(fieldset);\r\n  }\r\n  root.append(status);return root;\r\n}\r\n\r\n/** The \"Auras\" key in the bay row: opens the aura bay in the same pixel panel as Pets/Weapons/Mirror/Clothes. */\r\nexport function mountEffectPicker(anchor,{document:doc=anchor?.ownerDocument||globalThis.document}={}){\r\n  if(!anchor||!doc)return {element:null,dispose(){}};\r\n  anchor.parentElement?.querySelector(':scope > .gala-auras-key')?.remove();\r\n  const button=doc.createElement('button');button.type='button';button.className='bay-terminal-line gala-auras-key';button.dataset.cageSection='auras';button.textContent='Auras';button.setAttribute('aria-label','Auras: choose an aura by exercise');\r\n  button.addEventListener('click',()=>{for(const x of button.parentElement?.children||[])x.setAttribute?.('aria-pressed',String(x===button));globalThis.warRoomGala?.showBay('auras');});\r\n  anchor.insertAdjacentElement('afterend',button);\r\n  return {element:button,dispose(){button.remove();}};\r\n}\r\n","war-room/gala-bay.css":".gala-bay-stage {\r\n  background: radial-gradient(ellipse at 50% 14%, #52290d 0%, #1a1008 48%, #080a08 100%);\r\n}\r\n.gala-bay-stage::before {\r\n  z-index: 1;\r\n  background: repeating-linear-gradient(0deg, #ff9d2410 0 1px, transparent 1px 4px),\r\n    linear-gradient(90deg, #ff781208, transparent 28%, #ffb42a08 72%, transparent);\r\n}\r\n.gala-bay-panel {\r\n  position: relative;\r\n  z-index: 2;\r\n  max-height: 56%;\r\n  border-top: 2px solid #ff9a22;\r\n  background: linear-gradient(145deg, #18110bf5, #090b09f5);\r\n  box-shadow: 0 -8px 24px #ff79052b, inset 0 0 22px #ff82120e;\r\n  scrollbar-color: #a95b17 #15100b;\r\n}\r\n.gala-bay-panel h3,\r\n.gala-bay-panel .gala-editor-group legend {\r\n  color: #ffd28a;\r\n  font-family: \"Courier New\", monospace;\r\n  letter-spacing: .04em;\r\n  text-shadow: 0 0 8px #ff861d88;\r\n}\r\n.war-room .gala-bay-panel label { color: #ffd28a; font-family: \"Courier New\", monospace; }\r\n.war-room .gala-bay-panel select {\r\n  border: 1px solid #a95b17;\r\n  border-radius: 3px;\r\n  background: #18110b;\r\n  color: #ffe0aa;\r\n  font-family: \"Courier New\", monospace;\r\n}\r\n.war-room .gala-bay-panel select:focus-visible { outline: 2px solid #ffd28a; outline-offset: 1px; }\r\n.gala-bay-panel > div { display: block; }\r\n.gala-editor-group {\r\n  min-width: 0;\r\n  margin: 0 0 10px;\r\n  padding: 5px 7px 8px;\r\n  border: 1px solid #bd6c2480;\r\n  background: linear-gradient(#ff8c1009, #00000020);\r\n  box-shadow: inset 0 0 12px #ff7c100b;\r\n}\r\n.gala-editor-group legend {\r\n  padding: 0 6px;\r\n  font-size: 12px;\r\n  font-weight: 700;\r\n  text-transform: uppercase;\r\n}\r\n.gala-editor-part { min-width: 0; margin: 4px 0 9px; }\r\n.gala-editor-part h4,\r\n.gala-editor-weapons h4 {\r\n  margin: 0 0 5px;\r\n  color: #ffab46;\r\n  font: 700 11px/1.2 \"Courier New\", monospace;\r\n  letter-spacing: .05em;\r\n  text-transform: uppercase;\r\n}\r\n.gala-option-grid {\r\n  display: grid;\r\n  grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));\r\n  gap: 5px;\r\n  align-items: stretch;\r\n}\r\n.gala-option {\r\n  min-width: 0;\r\n  min-height: 76px;\r\n  padding: 4px 3px;\r\n  display: flex;\r\n  flex-direction: column;\r\n  align-items: center;\r\n  justify-content: center;\r\n  gap: 3px;\r\n  overflow: hidden;\r\n  border: 1px solid #87501f;\r\n  border-radius: 4px;\r\n  color: #f4d4a7;\r\n  background: linear-gradient(145deg, #251a10, #11100c);\r\n  font: 10px/1.15 \"Courier New\", monospace;\r\n  text-shadow: 0 0 5px #ff780033;\r\n  touch-action: manipulation;\r\n}\r\n.gala-option:hover { border-color: #ffab46; background: #392311; }\r\n.gala-option:focus-visible { outline: 2px solid #ffd28a; outline-offset: 1px; }\r\n.gala-option[aria-pressed=\"true\"] {\r\n  border-color: #ffb347;\r\n  color: #fff0cf;\r\n  background: linear-gradient(145deg, #56300e, #211307);\r\n  box-shadow: inset 0 0 0 1px #ffad3c, 0 0 9px #ff861d66;\r\n}\r\n.gala-option:disabled { opacity: .48; }\r\n.war-room .gala-coach-choices { border-color: #bd6c2480; background: #120e09; }\r\n.war-room .gala-coach-choices button {\r\n  border-color: #87501f;\r\n  background: linear-gradient(145deg, #251a10, #11100c);\r\n  color: #f4d4a7;\r\n  font-family: \"Courier New\", monospace;\r\n}\r\n.war-room .gala-coach-choices button[aria-pressed=\"true\"] {\r\n  border-color: #ffb347;\r\n  outline: 2px solid #ffad3c;\r\n  color: #fff0cf;\r\n  background: linear-gradient(145deg, #56300e, #211307);\r\n}\r\n.war-room .gala-coach-choices button:focus-visible { outline: 2px solid #ffd28a; outline-offset: 1px; }\r\n.gala-option-art {\r\n  display: block;\r\n  width: auto;\r\n  height: 42px;\r\n  max-width: 100%;\r\n  image-rendering: pixelated;\r\n  background: radial-gradient(ellipse, #ff9d2419, transparent 72%);\r\n}\r\n.gala-option-name {\r\n  display: -webkit-box;\r\n  max-width: 100%;\r\n  overflow: hidden;\r\n  -webkit-box-orient: vertical;\r\n  -webkit-line-clamp: 2;\r\n  overflow-wrap: anywhere;\r\n  text-align: center;\r\n}\r\n.gala-dye-grid { grid-template-columns: repeat(auto-fill, minmax(62px, 1fr)); }\r\n.gala-dye-option { min-height: 66px; }\r\n.gala-swatch-art { height: 38px; width: 32px; image-rendering: pixelated; }\r\n.gala-weapon-grid { grid-template-columns: repeat(auto-fill, minmax(78px, 1fr)); }\r\n.gala-tier-grid { grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); }\r\n.gala-weapon-art { width: auto; height: 60px; }\r\n.gala-editor-weapons .help { margin: 7px 0 2px; color: #d8ae7a; font-size: 11px; }\r\n@media (max-width: 560px) {\r\n  .gala-bay-panel { max-height: 62%; padding: 7px; }\r\n  .gala-option-grid { grid-template-columns: repeat(auto-fill, minmax(66px, 1fr)); gap: 4px; }\r\n  .gala-option { min-height: 70px; }\r\n}\r\n@media (prefers-reduced-motion: reduce) {\r\n  .gala-option { scroll-behavior: auto; }\r\n}\r\n/* Phone landscape: stage and menu side by side, so the menu is not squeezed to a sliver. */\r\n@media (orientation: landscape) and (max-height: 500px) {\r\n  .war-room .character-bay-stage { flex-direction: row; }\r\n  .war-room .character-bay-stage .gala-bay-stage { min-height: 0; flex: 1 1 45%; }\r\n  .war-room .character-bay-stage .gala-bay-panel { max-height: none; flex: 1 1 55%; overflow: auto; }\r\n}\r\n.gala-option-art.is-tall { height: 58px; }\r\n/* Coach blocks share the bay's pixel type and fold away under the Gala choices. */\r\n.war-room .gala-coach-block { margin: 8px 0; border: 1px solid #bd6c2480; background: #120e09; }\r\n.war-room .gala-coach-block > summary { padding: 8px; min-height: 40px; color: #ffd28a; font: 700 13px \"Courier New\", monospace; cursor: pointer; }\r\n.war-room .gala-coach-choices, .war-room .gala-coach-choices legend, .war-room .gala-coach-choices .help, .war-room .gala-coach-choices button { font-family: \"Courier New\", monospace; }\r\n/* Captions wrap at word boundaries; weapon and tier grids get wider tiles. */\r\n.gala-option-name { overflow-wrap: break-word; hyphens: auto; }\r\n.gala-weapon-grid, .gala-tier-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }\r\n/* The panel is opaque above the room; the bay keys sit above the stage glow. */\r\n.war-room .gala-bay-panel { background: #0d0a07; z-index: 5; }\r\n.war-room .cage-bays { z-index: 6; }\r\n/* Ian 20 Oct: each Mirror/Clothes sub-menu folds into a 44px row, one open at a time (gala-editor.js). */\r\ndetails.gala-editor-part { margin: 0 0 5px; border: 1px solid #87501f; border-radius: 4px; background: #120d08; }\r\ndetails.gala-editor-part[open] { border-color: #ffb347; }\r\n.gala-part-summary {\r\n  display: flex; align-items: center; justify-content: space-between; min-height: 44px; padding: 0 10px;\r\n  color: #ffab46; font: 700 13px/1.2 \"Courier New\", monospace; letter-spacing: .05em; text-transform: uppercase;\r\n  list-style: none; cursor: pointer; touch-action: manipulation;\r\n}\r\n.gala-part-summary::-webkit-details-marker { display: none; }\r\n.gala-part-summary::after { content: '>'; transition: transform .15s; }\r\ndetails[open] > .gala-part-summary::after { transform: rotate(90deg); }\r\n.gala-part-summary:focus-visible { outline: 2px solid #ffd28a; outline-offset: -2px; }\r\ndetails.gala-editor-part > .gala-option-grid { padding: 0 6px 8px; }\r\n@media (prefers-reduced-motion: reduce) { .gala-part-summary::after { transition: none; } }\r\n/* The stage renders at <= 1.5x; the phone upscales it nearest-neighbour so the sprite's face stays crisp. */\r\n.war-room .gala-bay-stage canvas { image-rendering: pixelated; }\r\n","war-room/gala-editor.js":"import { makeDyePreview, makePartPreview, makeWeaponPreview } from './gala-option-previews.js';\r\n\r\nconst DYE_NAMES = ['Nebula velvet', 'Plasma rose', 'Tidal silk', 'Reactor satin', 'Comet gold', 'Void blue', 'Solar copper', 'Lunar pearl', 'Obsidian smoke', 'Glacier mint'];\r\n\r\nconst PART_GROUPS = [\r\n  { title: 'Species & alien form', ids: ['body', 'skin', 'face', 'hair', 'facial', 'build', 'head', 'crown', 'jaw', 'tail'] },\r\n  { title: 'Head & neckwear', ids: ['headwear', 'neck'] },\r\n  { title: 'Formalwear', ids: ['torso'] },\r\n  { title: 'Shoulders, sleeves & gloves', ids: ['shoulders', 'arms', 'hands'] },\r\n  { title: 'Pants & footwear', ids: ['legs', 'feet'] },\r\n  { title: 'Armour', ids: ['armor', 'alloy', 'shield'] },\r\n  { title: 'Accessories', ids: ['held', 'back', 'base'] },\r\n  { title: 'Companion', ids: ['pet'] }\r\n];\r\n\r\nfunction optionButton(doc, { label, preview, selected, value, onClick, className = '' }) {\r\n  const button = doc.createElement('button');\r\n  button.type = 'button';\r\n  button.className = `gala-option${className ? ` ${className}` : ''}`;\r\n  button.setAttribute('aria-label', label);\r\n  button.setAttribute('aria-pressed', String(selected));\r\n  button.dataset.optionValue = String(value);\r\n  button.append(preview);\r\n  const caption = doc.createElement('span');\r\n  caption.className = 'gala-option-name';\r\n  caption.textContent = label;\r\n  button.append(caption);\r\n  button.addEventListener('click', () => onClick(button));\r\n  return button;\r\n}\r\n\r\n// A cut duplicate's kept pick can still carry its collection prefix (e.g. \"Xyrr Zor whiskers\"); one design, one plain name.\r\nexport const optionName = (name) => { const plain = String(name).replace(/^(?:Xyrr|Auv|Oth) /, ''); return plain[0].toUpperCase() + plain.slice(1); };\r\n\r\n// openPart/onToggle: with more than one sub-menu, each is a collapsed accordion row (one open at a time);\r\n// its option previews are only drawn when it first opens.\r\nexport function createGalaEditor({ document: doc, avatar, weapons, look, onPart, onDye, onTrim, onWeapon, locked, previewDraw = avatar.draw, openPart = null, onToggle }) {\r\n  const make = (tag, className, text) => {\r\n    const element = doc.createElement(tag);\r\n    if (className) element.className = className;\r\n    if (text) element.textContent = text;\r\n    return element;\r\n  };\r\n  let current = openPart;\r\n  const rows = [];\r\n  // A sub-menu: plain when it is the bay's only one, else a <details> row whose grid is filled on first open.\r\n  function subMenu(id, title, className, fill, accordion) {\r\n    const part = make(accordion ? 'details' : 'section', className);\r\n    part.dataset.part = id;\r\n    const grid = make('div', 'gala-option-grid');\r\n    const heading = make(accordion ? 'summary' : 'h4', accordion ? 'gala-part-summary' : '', title);\r\n    part.append(heading, grid);\r\n    if (!accordion) { fill(grid); return part; }\r\n    let filled = false;\r\n    const ensure = () => { if (!filled) { filled = true; fill(grid); } };\r\n    if (id === current) { part.open = true; ensure(); }\r\n    part.addEventListener('toggle', () => {\r\n      if (part.open) {\r\n        ensure();\r\n        for (const other of rows) if (other !== part && other.open) other.open = false;\r\n        if (current !== id) { current = id; onToggle?.(id); }\r\n      } else if (current === id) { current = null; onToggle?.(null); }\r\n    });\r\n    rows.push(part);\r\n    return part;\r\n  }\r\n\r\n  function partSections(ids, { accordion = ids.length > 1 } = {}) {\r\n    const wanted = new Set(ids);\r\n    const entries = PART_GROUPS.map(group => ({ ...group, ids: group.ids.filter(id => wanted.has(id)) })).filter(group => group.ids.length);\r\n    return entries.map(group => {\r\n      const fieldset = make('fieldset', 'gala-editor-group');\r\n      const legend = make('legend', '', group.title);\r\n      fieldset.append(legend);\r\n      for (const id of group.ids) {\r\n        const section = avatar.sections.find(item => item.id === id);\r\n        if (!section) continue;\r\n        fieldset.append(subMenu(id, section.label, 'gala-editor-part', grid => {\r\n          const chosen = look.parts[id] ?? 0;\r\n          const values = section.choices.includes(chosen) ? section.choices : [chosen, ...section.choices];\r\n          for (const value of values) {\r\n            const label = section.names[value] ? optionName(section.names[value]) : `${section.label} ${value + 1}`;\r\n            const button = optionButton(doc, {\r\n              label, value, selected: value === chosen,\r\n              preview: makePartPreview(doc, avatar, look, id, value, previewDraw),\r\n              onClick: target => {\r\n                grid.querySelectorAll('.gala-option').forEach(item => item.setAttribute('aria-pressed', String(item === target)));\r\n                onPart(id, value, label);\r\n              }\r\n            });\r\n            button.dataset.galaPart = id;\r\n            grid.append(button);\r\n          }\r\n        }, accordion));\r\n      }\r\n      return fieldset;\r\n    });\r\n  }\r\n\r\n  function dyeControl({ trim = false, accordion = true } = {}) {\r\n    const id = trim ? 'trim' : 'dye';\r\n    return subMenu(id, trim ? 'Trim colour' : 'Silk colour', `gala-editor-part ${trim ? 'gala-trim-part' : 'gala-dye-part'}`, grid => {\r\n      grid.classList.add('gala-dye-grid');\r\n      avatar.dyes.forEach((color, value) => {\r\n        const button = optionButton(doc, {\r\n          label: DYE_NAMES[value], value, selected: value === (trim ? look.trim ?? look.dye : look.dye),\r\n          preview: makeDyePreview(doc, avatar, look, value), className: 'gala-dye-option',\r\n          onClick: target => {\r\n            grid.querySelectorAll('.gala-option').forEach(item => item.setAttribute('aria-pressed', String(item === target)));\r\n            (trim ? onTrim : onDye)?.(value);\r\n          }\r\n        });\r\n        button.dataset.galaPart = id;\r\n        grid.append(button);\r\n      });\r\n    }, accordion);\r\n  }\r\n\r\n  function weaponControl() {\r\n    const root = make('div', 'gala-editor-weapons');\r\n    const typeHeading = make('h4', '', 'Weapon');\r\n    const typeGrid = make('div', 'gala-option-grid gala-weapon-grid');\r\n    const chosen = look.weapon || { type: 'rapier', tier: 0 };\r\n    for (const type of weapons.types) {\r\n      const current = { type: type.id, tier: 0 };\r\n      const button = optionButton(doc, {\r\n        label: type.name, value: type.id, selected: type.id === chosen.type,\r\n        preview: makeWeaponPreview(doc, weapons, current),\r\n        onClick: target => {\r\n          typeGrid.querySelectorAll('.gala-option').forEach(item => item.setAttribute('aria-pressed', String(item === target)));\r\n          onWeapon({ type: type.id, tier: type.id === chosen.type ? chosen.tier : 0 });\r\n        }\r\n      });\r\n      button.dataset.galaWeapon = 'type';\r\n      button.dataset.weaponType = type.id;\r\n      typeGrid.append(button);\r\n    }\r\n    const tierHeading = make('h4', '', 'Upgrade');\r\n    const tierGrid = make('div', 'gala-option-grid gala-tier-grid');\r\n    const previewStatus = make('p', 'help gala-preview-status');\r\n    previewStatus.setAttribute('role', 'status');\r\n    for (let tier = 0; tier < weapons.tiers.length; tier++) {\r\n      const weapon = { type: chosen.type, tier };\r\n      const isLocked = Boolean(locked?.(weapon));\r\n      const button = optionButton(doc, {\r\n        label: weapons.tiers[tier], value: tier,\r\n        selected: tier === chosen.tier, preview: makeWeaponPreview(doc, weapons, weapon),\r\n        className: isLocked ? 'is-locked' : '',\r\n        onClick: target => {\r\n          if (isLocked) {\r\n            previewStatus.textContent = `Preview only · How to unlock ${weapons.tiers[tier]}: ${weapons.requirements(weapon).label}.`;\r\n            return;\r\n          }\r\n          previewStatus.textContent = '';\r\n          tierGrid.querySelectorAll('.gala-option').forEach(item => item.setAttribute('aria-pressed', String(item === target)));\r\n          onWeapon({ type: chosen.type, tier });\r\n        }\r\n      });\r\n      button.dataset.galaWeapon = 'tier';\r\n      if (isLocked) {\r\n        button.dataset.locked = 'true';\r\n      }\r\n      tierGrid.append(button);\r\n    }\r\n    root.append(typeHeading, typeGrid, tierHeading, tierGrid, previewStatus, make('p', 'help', 'Workout performance earns weapon upgrades.'));\r\n    return root;\r\n  }\r\n\r\n  return { parts: partSections, dye: () => dyeControl(), trim: () => dyeControl({ trim: true }), weapons: weaponControl, get open() { return current; } };\r\n}\r\n","war-room/gala-option-previews.js":"// Crops of the 64x96 DFO-rig sprite (feet at y 84, chin ~29, shoulders ~32, hips ~52).\r\nconst cropFor = {\r\n  body: [12, 0, 40, 46], skin: [18, 8, 28, 30], face: [19, 12, 26, 22], hair: [15, 3, 34, 30], facial: [19, 16, 26, 22],\r\n  headwear: [14, 0, 36, 26], neck: [22, 26, 20, 18], torso: [16, 28, 32, 30], shoulders: [11, 26, 42, 22], arms: [8, 30, 48, 34], hands: [8, 50, 48, 20],\r\n  legs: [16, 48, 32, 38], feet: [16, 74, 32, 20], held: [0, 20, 64, 66], back: [2, 10, 60, 80], base: [13, 80, 39, 16], pet: [0, 62, 24, 24], dye: [20, 32, 24, 31],\r\n  build: [8, 3, 48, 91], head: [18, 4, 28, 30], crown: [14, 0, 36, 30], jaw: [19, 18, 26, 20], tail: [4, 30, 58, 60], armor: [8, 3, 48, 91], alloy: [8, 3, 48, 91], shield: [2, 30, 40, 40]\r\n};\r\n\r\n// The DFO rig renders each preview on the CPU (a few ms each), so a bay's previews fill in over a few\r\n// frames instead of blocking the menu from opening.\r\nconst queue = [];\r\nlet pumping = false;\r\nfunction pump() {\r\n  const until = performance.now() + 8;\r\n  while (queue.length && performance.now() < until) queue.shift()();\r\n  if (queue.length) requestAnimationFrame(pump); else pumping = false;\r\n}\r\nfunction later(job) {\r\n  if (typeof requestAnimationFrame !== 'function') return job();\r\n  queue.push(job);\r\n  if (!pumping) { pumping = true; requestAnimationFrame(pump); }\r\n}\r\n\r\nexport function makePartPreview(doc, avatar, look, sectionId, value, draw = avatar.draw) {\r\n  const preview = doc.createElement('canvas');\r\n  const crop = cropFor[sectionId] || cropFor.body;\r\n  const next = structuredClone(look);\r\n  next.parts[sectionId] = value;\r\n  if (sectionId === 'alloy' && !next.parts.armor) next.parts.armor = 9; // alloy only shows on armour\r\n  if (sectionId === 'body') next.parts.skin = value; // each species in its own skin\r\n  preview.width = crop[2];\r\n  preview.height = crop[3];\r\n  later(() => {\r\n    if (typeof preview.isConnected === 'boolean' && !preview.isConnected) return; // menu already replaced\r\n    const source = doc.createElement('canvas');\r\n    const yaw = avatar.PREVIEW_YAW?.[sectionId];\r\n    const bare = avatar.PREVIEW_BARE?.[sectionId];\r\n    draw(source, next, { weapon: false, companion: sectionId === 'pet', prop: false, base: !bare, petOnly: sectionId === 'pet', ...(yaw == null ? {} : { yaw }), ...(bare ? { bare } : {}) });\r\n    const ctx = preview.getContext('2d');\r\n    ctx.imageSmoothingEnabled = false;\r\n    ctx.clearRect(0, 0, preview.width, preview.height);\r\n    ctx.drawImage(source, ...crop, 0, 0, preview.width, preview.height);\r\n  });\r\n  preview.className = crop[3] > 50 ? 'gala-option-art is-tall' : 'gala-option-art';\r\n  preview.setAttribute('aria-hidden', 'true');\r\n  return preview;\r\n}\r\n\r\nexport function makeDyePreview(doc, avatar, look, dye) {\r\n  const canvas = doc.createElement('canvas');\r\n  const ctx = canvas.getContext('2d');\r\n  canvas.width = 34;\r\n  canvas.height = 40;\r\n  ctx.fillStyle = '#17100b';\r\n  ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n  ctx.fillStyle = avatar.dyes[dye];\r\n  ctx.fillRect(5, 5, 24, 29);\r\n  ctx.fillStyle = '#fff4d5';\r\n  ctx.fillRect(12, 8, 3, 22);\r\n  ctx.fillStyle = '#382318';\r\n  ctx.fillRect(5, 31, 24, 3);\r\n  canvas.className = 'gala-option-art gala-swatch-art';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  return canvas;\r\n}\r\n\r\nexport function makeWeaponPreview(doc, weapons, weapon) {\r\n  const canvas = doc.createElement('canvas');\r\n  canvas.width = 42;\r\n  canvas.height = 76;\r\n  const ctx = canvas.getContext('2d');\r\n  ctx.imageSmoothingEnabled = false;\r\n  weapons.draw(ctx, weapon, { x: 1, y: 2, scale: 1 });\r\n  canvas.className = 'gala-option-art gala-weapon-art';\r\n  canvas.setAttribute('aria-hidden', 'true');\r\n  return canvas;\r\n}\r\n","war-room/index.html":"<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\r\n  <meta name=\"theme-color\" content=\"#081f2b\"><title>MYR5 · War Room</title>\r\n  <link rel=\"stylesheet\" href=\"/shader-effects.css\"><script type=\"module\" src=\"/shader-entry.mjs\"></script>\r\n  <script type=\"importmap\">{\"imports\":{\"three\":\"/vendor/three/three.module.js\"}}</script>\r\n  <script src=\"/workout-tracks.js\"></script><script src=\"/pod/gala-weapons.js\"></script><script src=\"/pod/gala-avatar.js\"></script><script src=\"/pod/gala-performer.js\"></script>\r\n  <script type=\"module\" src=\"/war-room/war-room.mjs\"></script>\r\n<script type=\"module\" src=\"/audio/standalone-sound.mjs\"></script><link rel=\"stylesheet\" href=\"/modules/shell/shell.css\" data-myr5-shell><script type=\"module\" src=\"/shell-entry.mjs\"></script></head>\r\n<body>\r\n  <main class=\"war-room\">\r\n    <section class=\"panel character-bay\" aria-labelledby=\"characterBayTitle\">\r\n      <div class=\"character-bay-head\">\r\n        <a href=\"/pose.html#world\" class=\"portal-link\">← Back to base</a>\r\n        <h1 id=\"characterBayTitle\">WAR ROOM</h1>\r\n        <span id=\"access\" role=\"status\">Checking…</span>\r\n        <p id=\"characterBayStatus\" role=\"status\" class=\"bay-status\">Opening…</p>\r\n      </div>\r\n      <div id=\"warRoomGalaHost\" class=\"character-bay-stage\"></div>\r\n    </section>\r\n\r\n    <details class=\"panel controls-drawer\">\r\n      <summary>Controls</summary>\r\n      <div class=\"controls-grid\">\r\n        <section aria-labelledby=\"clearanceTitle\">\r\n          <h2 id=\"clearanceTitle\">Clearance</h2>\r\n          <p id=\"runStatus\" role=\"status\">Loading…</p>\r\n          <dl id=\"checks\" class=\"checks\"></dl>\r\n        </section>\r\n        <section aria-labelledby=\"identityTitle\">\r\n          <h2 id=\"identityTitle\">Identity</h2>\r\n          <p id=\"identity\">No DJ identity.</p>\r\n        </section>\r\n        <section class=\"board\" aria-labelledby=\"boardTitle\">\r\n          <div class=\"section-heading\"><h2 id=\"boardTitle\">Rankings</h2><button id=\"refresh\" type=\"button\">Refresh</button></div>\r\n          <p id=\"boardStatus\" role=\"status\">Loading…</p>\r\n          <table><thead><tr><th>Rank</th><th>DJ</th><th>Time</th></tr></thead><tbody id=\"leaderRows\"></tbody></table>\r\n        </section>\r\n        <section class=\"arsenal\" aria-labelledby=\"arsenalTitle\">\r\n          <h2 id=\"arsenalTitle\">Arsenal</h2>\r\n          <label>Weapon <select id=\"weaponType\"><option value=\"rapier\">Rapier</option><option value=\"greatsword\">Greatsword</option><option value=\"dagger\">Dagger</option><option value=\"sabre\">Sabre</option><option value=\"axe\">Axe</option><option value=\"hammer\">Hammer</option><option value=\"mace\">Mace</option><option value=\"flail\">Flail</option><option value=\"spear\">Spear</option><option value=\"trident\">Trident</option><option value=\"halberd\">Halberd</option><option value=\"scythe\">Scythe</option><option value=\"bow\">Bow</option><option value=\"crossbow\">Crossbow</option><option value=\"chakram\">Chakram</option><option value=\"gauntlets\">Gauntlets</option><option value=\"staff\">Staff</option><option value=\"wand\">Wand</option><option value=\"tome\">Tome</option><option value=\"cannon\">Cannon</option></select></label>\r\n          <label>Tier <input id=\"weaponTier\" type=\"number\" min=\"0\" max=\"20\" step=\"1\"></label>\r\n          <button id=\"saveLoadout\" type=\"button\">Save</button>\r\n          <p id=\"arsenalStatus\" role=\"status\"></p>\r\n        </section>\r\n      </div>\r\n    </details>\r\n  </main>\r\n  <!-- R20: the app's dock console and metal housing, as in the customizer -->\r\n  <nav id=\"coachDock\" class=\"coach-dock\" aria-label=\"Coach tools\"><a class=\"dock-control dock-settings\" data-route=\"customizeCoach\" href=\"/pose.html#customizeCoach\" aria-label=\"Customize coach\" title=\"Customize coach\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><circle cx=\"12\" cy=\"12\" r=\"6.5\"/><path d=\"M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9 7 7M17 17l2.1 2.1M4.9 19.1 7 17M17 7l2.1-2.1\"/></svg></span></a><a class=\"dock-control dock-food\" data-route=\"food\" href=\"/pose.html#food\" aria-label=\"Food\" title=\"Food\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 8h4l2-3h6l2 3h4v11H3z\"/><circle cx=\"12\" cy=\"13\" r=\"3.5\"/></svg></span></a><a class=\"dock-portal\" data-route=\"portal\" href=\"/pose.html\" aria-label=\"Portal or workout pod\" title=\"Portal or workout pod\"><svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 11.5 12 4l9 7.5M5.5 10v10h13V10M10 20v-6h4v6\"/></svg><span aria-hidden=\"true\">PORTAL</span></a><a class=\"dock-control dock-scoreboard\" data-route=\"scoreboard\" href=\"/pose.html#scoreboard\" aria-label=\"Classroom leaderboard\" title=\"Classroom leaderboard\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"8\" r=\"3\"/><circle cx=\"17\" cy=\"9\" r=\"2.5\"/><path d=\"M3 20c0-4 3-6 6-6s6 2 6 6M15 14c3 0 6 1.5 6 5\"/></svg></span></a><a class=\"dock-control dock-achievements\" data-route=\"achievements\" href=\"/pose.html#achievements\" aria-label=\"Achievements\" title=\"Achievements\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 4h10v5a5 5 0 0 1-10 0zM7 6H3c0 3 1 4 4 4M17 6h4c0 3-1 4-4 4M12 14v4M8 21h8\"/></svg></span></a><a class=\"dock-control dock-pod\" data-route=\"reminders\" href=\"/pose.html#reminders\" aria-label=\"Reminders computer\" title=\"Reminders computer\"><span aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A15 15 0 0 1 3 6a2 2 0 0 1 2-2z\"/></svg></span></a><span class=\"dock-live\" role=\"status\" aria-live=\"polite\"></span></nav>\r\n  <script type=\"module\">import{mountStandaloneHousing}from'/modules/portal/standalone-housing.mjs';await mountStandaloneHousing({content:document.querySelector('.war-room')});</script>\r\n</body>\r\n</html>\r\n","war-room/war-room.css":":root{color-scheme:dark;--ink:#d7f8ed;--muted:#8daeb0;--line:#356473;--panel:#0e2c38;--accent:#91f6c4;--yellow:#e4f28c;--bar:44px}*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#0b0716;color:var(--ink);font:16px/1.5 system-ui,sans-serif}\r\n.war-room{position:fixed;inset:15px 15px calc(var(--myr5-bar,88px) + 15px);overflow:hidden} /* R20: inside the metal housing, above the dock */\r\n.panel{background:linear-gradient(145deg,#123747,#0a2430);border:1px solid var(--line);box-shadow:0 12px 30px #0004}\r\nbutton{border:1px solid var(--accent);background:#183f48;color:var(--ink);padding:10px 14px;cursor:pointer;font-weight:600;min-height:44px}button:disabled{cursor:not-allowed;opacity:.55}\r\n[hidden]{display:none!important}\r\nlabel{font-size:13px}\r\n.checks{margin:8px 0 0}.checks div{display:flex;justify-content:space-between;border-top:1px solid #35647366;padding:8px 0}.checks dd,.checks dt{margin:0}.checks dd{color:var(--accent);font-family:monospace}\r\n.section-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}\r\ntable{width:100%;border-collapse:collapse;margin-top:8px}th,td{text-align:left;padding:8px;border-bottom:1px solid #35647366}th{color:var(--muted);font:11px monospace;text-transform:uppercase}td:first-child,td:last-child{font-family:monospace}\r\n\r\n/* The 3D character bay is the whole viewport; a CSS backdrop stands in for a room until the room model loads. */\r\n.character-bay{position:absolute;inset:0;z-index:0;border:0;box-shadow:none}\r\n.character-bay-head{position:absolute;top:0;left:0;right:0;z-index:2;display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;padding:8px 10px;background:linear-gradient(#000c,#0000)}\r\n.character-bay-head h1{flex:1 1 auto;margin:0;font:700 15px/1.1 monospace;color:#f1ffcc;letter-spacing:.04em}\r\n.character-bay-head #access{font:11px monospace;color:var(--accent)}\r\n.portal-link{color:var(--accent);font:11px monospace;text-decoration:none;white-space:nowrap}\r\n.bay-status{flex-basis:100%;margin:0;font:11px monospace;color:#bde6e2}\r\n/* Bottom padding reserves the collapsed controls bar (and its safe area) so the cage's own bay buttons,\r\n   pinned to the stage's bottom edge, never sit under the fixed drawer. */\r\n.character-bay-stage{position:absolute;inset:0;display:flex;flex-direction:column;gap:6px;padding:0 0 calc(var(--bar) + env(safe-area-inset-bottom))}\r\n.gala-bay-stage{position:relative;flex:1 1 60%;min-height:260px;overflow:hidden;background:radial-gradient(ellipse at 50% 12%,#4a3a72 0%,#1c1330 55%,#0b0716 100%)}\r\n.gala-bay-stage::before{content:'';position:absolute;inset:0;background:repeating-linear-gradient(0deg,#ffffff0a 0 2px,#0000 2px 4px);pointer-events:none}\r\n.gala-bay-stage canvas{position:relative;display:block;width:100%;height:100%;touch-action:none}\r\n.gala-bay-panel{flex:0 1 auto;max-height:40%;overflow:auto;padding:8px 10px;background:#161230f2;border-top:2px solid #6c74ae}\r\n.gala-bay-panel h3{margin:0 0 6px;font:700 14px monospace;color:#f1ffcc}\r\n.gala-bay-panel>div{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:6px 10px}\r\n.gala-bay-panel label{display:flex;flex-direction:column;gap:2px;font-size:12px}.gala-bay-panel select{min-height:44px;width:100%}\r\n.gala-weapon{display:contents}.gala-bay-panel .help{grid-column:1/-1;margin:2px 0;font-size:12px;color:var(--muted)}\r\n.gala-coach-choices{grid-column:1/-1;min-width:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:6px;border:1px solid #6c74ae;padding:8px}\r\n.gala-coach-choices button{min-height:44px;min-width:0;padding:6px;display:flex;flex-direction:column;align-items:center;gap:4px;font-size:10px;overflow-wrap:anywhere}\r\n.gala-coach-choices button[aria-pressed=true]{outline:2px solid #cdb3ff;outline-offset:-2px}\r\n.gala-coach-choices button:disabled{opacity:.65}.gala-coach-choices canvas{width:auto;height:96px;max-width:100%;image-rendering:pixelated}\r\n.gala-coach-choices canvas[height=\"36\"]{height:36px;width:48px}\r\n\r\n/* Controls: a compact, collapsed-by-default drawer that overlays the bay, pinned above the bottom safe area. */\r\n.controls-drawer{position:absolute;left:0;right:0;bottom:0;z-index:3;max-height:70vh;overflow:auto;border-left:0;border-right:0;border-bottom:0;padding:0 12px env(safe-area-inset-bottom)}\r\n.controls-drawer summary{display:flex;align-items:center;min-height:var(--bar);margin:0;cursor:pointer;font:700 13px monospace;color:var(--accent);list-style:none}\r\n.controls-drawer summary::-webkit-details-marker{display:none}\r\n.controls-drawer summary::before{content:'\\25b2 '}\r\n.controls-drawer[open] summary::before{content:'\\25bc '}\r\n.controls-grid{display:flex;flex-direction:column;gap:14px;padding-bottom:8px}\r\n.controls-grid h2{margin:0 0 6px;font:13px monospace;color:#f1ffcc;text-transform:uppercase;letter-spacing:.05em}\r\n.war-room-effects{position:relative;flex:0 0 auto;align-self:center;font:12px monospace}\r\n.war-room-effects>summary{display:flex;align-items:center;justify-content:center;min-height:36px;min-width:62px;padding:4px 8px;border:1px solid #ff880088;color:#ffb65f;background:#211306;cursor:pointer;list-style:none;white-space:nowrap}\r\n.war-room-effects>summary::-webkit-details-marker,.effect-group>summary::-webkit-details-marker{display:none}\r\n.war-room-effects>summary::before{content:'> '}.war-room-effects[open]>summary{color:#ffe3ae;background:#48280c}\r\n.war-room-effects :focus-visible,.effect-picker-menu :focus-visible{outline:2px solid #fff0a8;outline-offset:2px}\r\n.effect-picker-menu{position:fixed;inset:auto;left:max(8px,env(safe-area-inset-left));right:max(8px,env(safe-area-inset-right));bottom:calc(var(--myr5-bar,88px) + 54px + env(safe-area-inset-bottom));z-index:2147483000;width:auto;max-width:480px;max-height:min(62dvh,520px);margin:0 auto;overflow:auto;padding:8px;border:1px solid #ff9a1f;border-radius:9px;background:#100e16f5;box-shadow:0 8px 30px #000b;overscroll-behavior:contain}\r\n.effect-picker-menu[popover]{position:fixed;inset:auto;left:max(8px,env(safe-area-inset-left));right:max(8px,env(safe-area-inset-right));bottom:calc(var(--myr5-bar,88px) + 54px + env(safe-area-inset-bottom));margin:0 auto}\r\n.effect-group{border:1px solid #795125;border-radius:6px;margin:3px 0;background:#21190ff0}\r\n.effect-group>summary{padding:7px 9px;min-height:40px;color:#f5d9ab;font-weight:700;cursor:pointer;list-style:none}\r\n.effect-group>summary{display:flex;align-items:center;justify-content:space-between;gap:8px}\r\n.effect-current{max-width:58%;overflow:hidden;color:#d4c2a7;font:10px/1.2 system-ui,sans-serif;text-align:right;text-overflow:ellipsis;white-space:nowrap}\r\n.effect-group[open]>summary{background:#51300f;color:#fff0cb}\r\n.effect-choice-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px;margin:0;padding:7px;border:0}\r\n.effect-choice-list legend{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\r\n.effect-choice{display:flex;align-items:center;gap:7px;min-width:0;min-height:48px;padding:4px;border:1px solid #624921;border-radius:5px;background:#17131c;color:#f4e8d2;font:11px/1.25 system-ui,sans-serif;cursor:pointer}\r\n.effect-choice:has(input:checked){border-color:#ffbf67;background:#40280f;box-shadow:inset 0 0 0 1px #ffbf67}\r\n.effect-choice input{flex:0 0 auto;width:17px;height:17px;margin:0;accent-color:#ff9a1f}\r\n.effect-thumb{flex:0 0 40px;width:40px;height:40px;border:1px solid #644927;background:#0e1117;image-rendering:pixelated}\r\n.effect-choice span{min-width:0;overflow-wrap:anywhere}\r\n.effect-picker-status{margin:7px 2px 2px;color:#dbd3c4;font:11px/1.35 system-ui,sans-serif}\r\n/* R18 H: the 3D bay buttons are an orange terminal in a translucent glass hologram panel (War Room page only: this file is not loaded elsewhere). */\r\n.war-room .cage-bays{gap:2px;padding:4px 6px;border:1px solid #ff880088;border-radius:8px;background:linear-gradient(#0a120e66,#04080acc),repeating-linear-gradient(0deg,#0000 0 2px,#ff880014 2px 3px);background-blend-mode:normal;backdrop-filter:blur(3px);box-shadow:0 0 14px #ff880055,inset 0 0 18px #ff880022}\r\n.war-room .cage-bays button{flex:1 1 auto;background:none;border:0;border-radius:0;box-shadow:none;color:#ff9a1f;font:700 .8125rem 'Courier New',monospace;letter-spacing:.04em;text-transform:uppercase;text-shadow:0 0 6px #ff8800aa;min-height:36px;padding:4px 6px;display:inline-flex;flex-direction:row;align-items:center;justify-content:center;white-space:nowrap}\r\n.war-room .cage-bays button::before{content:'> '}.war-room .cage-bays button::after{content:'_';animation:bay-cursor 1s steps(1) infinite}\r\n.war-room .cage-bays button[aria-pressed=true]{background:#ff88002e;color:#ffd08a;text-shadow:0 0 8px #ffb347}\r\n.war-room .cage-bays button:hover{background:#ff880022}\r\n@keyframes bay-cursor{50%{opacity:0}}@media(prefers-reduced-motion:reduce){.war-room .cage-bays button::after{animation:none}}\r\n","war-room/war-room.mjs":"import {createStandaloneAccountContext} from '../standalone-account-context.mjs';\r\nimport {authFetch} from '../auth-client.mjs';\r\nimport {equipmentProgress} from '../pod/rest-arena.mjs';\r\nimport {authTransitions} from '../auth-transition.mjs';\r\nimport {createWarRoomApi} from './account-api.mjs';\r\nimport {mountEffectPicker} from './effect-picker.mjs';\r\nimport {mountBaseWindow,createWindowThemeController,guestWindowBiome,validWindowBiome} from './base-window.mjs';\r\nimport {performanceOwner} from '../performance-progress.mjs';\r\nimport * as THREE from 'three';\r\nconst css=document.createElement('link');css.rel='stylesheet';css.href='/war-room/war-room.css';document.head.append(css);\r\nconst bayCss=document.createElement('link');bayCss.rel='stylesheet';bayCss.href='/war-room/gala-bay.css';document.head.append(bayCss);\r\nconst $=id=>document.getElementById(id);\r\nconst CHECKS=[['djscratch','DJ Scratch'],['gala','Gala'],['lilboyfriend','Lil Boyfriend'],['corgi','Corgi'],['hand','Helping Hand'],['armie','Coach Armie']];\r\nconst transitions=authTransitions(),{api}=createWarRoomApi({request:authFetch,transitions}),accountContext=createStandaloneAccountContext({transitions});\r\nconst time=ms=>{if(!Number.isFinite(Number(ms)))return '—';const s=Math.max(0,Math.round(Number(ms)/1000));return `${Math.floor(s/60)}:${String(s%60).padStart(2,'0')}`;};\r\nconst GUEST='Sign in to use your saved run and loadouts.';\r\nlet arsenal=null;\r\nlet refreshBaseWindow=()=>{};\r\n// Gala weapon upgrades unlock from the signed-in account's training, as in the pod; guests keep base tiers.\r\nconst galaProgress=progress=>{window.GalaProgress=progress?{read:()=>equipmentProgress(progress)}:undefined;window.dispatchEvent(new Event('mominc-avatar-change'));};\r\ntransitions.subscribe(()=>{$('refresh').disabled=false;galaProgress(null);arsenal=null;$('saveLoadout').disabled=true;$('arsenalStatus').textContent='Account changed. Refresh the War Room.';});\r\nfunction renderArsenal(state){arsenal=state;$('saveLoadout').disabled=false;$('weaponType').value=state.loadout.type;$('weaponTier').value=String(state.loadout.tier);$('arsenalStatus').textContent=state.updatedAt?'Saved to this account.':'No saved loadout yet.';}\r\nfunction renderRun(run){$('runStatus').textContent=run?'Run '+(run.completedAt?'complete':'in progress')+'.':'No saved Gala run found.';$('identity').textContent=run?.djName||'No DJ identity attached.';$('checks').replaceChildren(...CHECKS.map(([key,label])=>{const row=document.createElement('div'),dt=document.createElement('dt'),dd=document.createElement('dd');dt.textContent=label;dd.textContent=run?.completed?.includes(key)?'✓ complete':'— pending';row.append(dt,dd);return row;}));}\r\nfunction renderBoard(board){const rows=board.items||[];$('leaderRows').replaceChildren(...rows.map(item=>{const tr=document.createElement('tr');for(const value of [String(item.rank).padStart(2,'0'),item.djName||'Unnamed DJ',time(item.durationMs)]){const td=document.createElement('td');td.textContent=value;tr.append(td);}return tr;}));$('boardStatus').textContent=rows.length?'Live escapee records.':'No ranked runs yet.';}\r\n// Public room and leaderboard; saved run and arsenal need a signed-in account and the server API.\r\nasync function load(){\r\n const ticket=transitions.beginRefresh();arsenal=null;$('saveLoadout').disabled=true;$('refresh').disabled=true;\r\n const board=api('/api/gala/leaderboard').then(value=>{if(transitions.isCurrent(ticket))renderBoard(value);},error=>{if(transitions.isCurrent(ticket))$('boardStatus').textContent='Leaderboard unavailable: '+error.message;});\r\n try{\r\n  const account=await accountContext.read({ticket});transitions.assertCurrent(ticket);refreshBaseWindow(account);\r\n  if(!account){galaProgress(null);$('access').textContent='Public War Room';$('runStatus').textContent=GUEST;$('checks').replaceChildren();$('arsenalStatus').textContent=GUEST;return;}\r\n  $('access').textContent='Signed-in War Room';galaProgress(account.progress);\r\n  const [runReply,room]=await Promise.all([api('/api/gala/install-draft'),api('/api/war-room')]);transitions.assertCurrent(ticket);renderRun(runReply.data);renderArsenal(room.state);\r\n }catch(error){if(!transitions.isCurrent(ticket))return;$('runStatus').textContent=error.message;$('arsenalStatus').textContent='Arsenal unavailable: '+error.message;$('access').textContent='Signed in · service unavailable';}\r\n finally{await board;if(transitions.isCurrent(ticket))$('refresh').disabled=false;}\r\n}\r\n$('refresh').addEventListener('click',load);window.addEventListener('myr5:login-ready',()=>void load());window.addEventListener('pageshow',event=>{if(event.persisted){accountContext.clear();void load();}});load();\r\n$('saveLoadout').addEventListener('click',async()=>{if(!arsenal)return;const ticket=transitions.capture(),button=$('saveLoadout');button.disabled=true;try{const reply=await api('/api/war-room/loadout',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({revision:arsenal.revision,loadout:{type:$('weaponType').value,tier:Number($('weaponTier').value)}})});transitions.assertCurrent(ticket);renderArsenal(reply.state);}catch(error){if(transitions.isCurrent(ticket))$('arsenalStatus').textContent=error.message;}finally{if(transitions.isCurrent(ticket))button.disabled=false;}});\r\n\r\n// The character bay dresses the player's own 64-bit Gala character in the cage room. It never opens the\r\n// coach customizer, so it needs no ship admission; guests use it too, saving to this device.\r\nfunction mountCharacterBay(){\r\n const host=$('warRoomGalaHost'),status=$('characterBayStatus');\r\n if(!host||!status)return;\r\n const tell=text=>{status.textContent=text;};\r\n let bay=null,module=null,generation=0,effectPicker=null,baseWindow=null,windowTheme=null,placementCage=null;\r\n const syncRoomPlacement=()=>{const cage=bay?.cage;if(!cage){baseWindow?.setRoomMode('flat');return;}if(cage===placementCage)return;placementCage=cage;const run=generation,exterior=baseWindow;void Promise.resolve(cage.ready).then(ok=>{if(run===generation&&baseWindow===exterior&&bay?.cage===cage)exterior?.setRoomMode(ok?'cage':'flat');}).catch(()=>{if(run===generation&&baseWindow===exterior)exterior?.setRoomMode('flat');});};\r\n const attachEffects=()=>{\r\n  syncRoomPlacement();\r\n  const pets=host.querySelector('[data-cage-section=\"pets\"]');\r\n  if(!pets)return;\r\n  if(effectPicker?.element?.isConnected&&effectPicker.element.previousElementSibling===pets)return;\r\n  effectPicker?.dispose();effectPicker=mountEffectPicker(pets);\r\n };\r\n const effectObserver=new MutationObserver(attachEffects);effectObserver.observe(host,{childList:true,subtree:true});\r\n const close=()=>{generation++;effectObserver.disconnect();effectPicker?.dispose();effectPicker=null;windowTheme?.dispose();windowTheme=null;baseWindow?.dispose();baseWindow=null;placementCage=null;refreshBaseWindow=()=>{};bay?.dispose();bay=null;window.warRoomGala=null;};\r\n const open=()=>{\r\n  if(!document.hidden&&host.isConnected)effectObserver.observe(host,{childList:true,subtree:true});\r\n  if(document.hidden||!host.isConnected||bay)return;\r\n  const run=++generation;\r\n  module??=import('/war-room/gala-bay.js');\r\n  module.then(({mountGalaBay})=>{\r\n   if(run!==generation||document.hidden||!host.isConnected)return;\r\n   bay=mountGalaBay(host,{tell});window.warRoomGala=bay;\r\n   const initial=validWindowBiome(new URLSearchParams(location.search).get('biome'));baseWindow=mountBaseWindow(bay.stage,THREE,initial);\r\n   windowTheme=createWindowThemeController({initialBiome:initial,apply:(biome,options)=>baseWindow?.setBiome(biome,options),transitions,request:authFetch,guestTheme:()=>{let storage;try{storage=localStorage;}catch{}return guestWindowBiome({storage,owner:performanceOwner(),account:accountContext.account});}});\r\n   refreshBaseWindow=account=>void windowTheme?.sync(account);refreshBaseWindow(accountContext.account);syncRoomPlacement();tell('Tap the room or your character.');\r\n  }).catch(()=>{if(run===generation)tell('Bay unavailable on this device.');});\r\n };\r\n addEventListener('pagehide',close);\r\n addEventListener('myr5:account-ready',()=>refreshBaseWindow(accountContext.account));\r\n addEventListener('pageshow',event=>{if(event.persisted)open();});\r\n document.addEventListener('visibilitychange',()=>{if(document.hidden)close();else open();});\r\n open();\r\n}\r\nmountCharacterBay();\r\n","weapon-rewards.mjs":"import {TRAINING_TRACKS} from './weapon-training.mjs';\r\nimport {readPerformanceProgress} from './performance-progress.mjs';\r\nexport function weaponRewardSummary(group,{kind,options={}}={}){\r\n const track=TRAINING_TRACKS[group];if(!track)return null;\r\n const state=readPerformanceProgress(options),weapons=track.weapons.map(type=>({type,tier:state.weapons[type]??0}));\r\n const hold='Holds: 1 uninterrupted minute earns the first tier in the difficulty block; 3 minutes earns the second; 5 minutes unlocks the coach. Repeat a 5-minute clear for another tier. 10 minutes finishes the block and earns gold.';\r\n const reps='Working reps: 8 earns the first tier in the difficulty block; 12 earns the second; 15 unlocks the coach. Repeat the 15-rep clear on an eligible day for another tier.';\r\n const timed='Active movement: complete 5 active minutes to earn a weapon tier. '+(group==='cardio'?'For sprinting, complete 5 rounds. ':'')+'Repeat at the same difficulty to improve its block.';\r\n return {name:track.name,weapons,blocks:'Easy tiers 1–5 · Medium 6–10 · Hard 11–15 · Expert 16–20',target:kind==='hold'?hold:kind==='reps'?reps:kind==='cardio'?timed:[hold,reps].join(' ')};\r\n}\r\nexport function mountWeaponRewards(host,getGroup,getKind=()=>undefined){\r\n const strip=document.createElement('section');strip.className='training-rewards';strip.setAttribute('aria-label','Performance weapon rewards');host.after(strip);\r\n function paint(){\r\n  const summary=weaponRewardSummary(getGroup(),{kind:getKind()});strip.replaceChildren();if(!summary)return;\r\n  const W=window.GalaWeapons,heading=document.createElement('p');heading.textContent=`${summary.name} · Earned weapon tiers`;strip.append(heading);\r\n  const row=document.createElement('div');row.className='training-reward-weapons';\r\n  for(const {type,tier} of summary.weapons){\r\n   const card=document.createElement('div'),canvas=document.createElement('canvas'),label=document.createElement('span');canvas.width=80;canvas.height=144;canvas.setAttribute('aria-hidden','true');W?.draw?.(canvas.getContext('2d'),{type,tier},{scale:2});const name=W?.types?.find(w=>w.id===type)?.name||type[0].toUpperCase()+type.slice(1);label.textContent=`${name} · ${tier===0?'Starter':`Tier ${tier} / 20`}`;card.dataset.weapon=type;card.dataset.tier=String(tier);card.append(canvas,label);row.append(card);\r\n  }\r\n  strip.append(row);for(const text of [summary.blocks,summary.target]){const status=document.createElement('small');status.textContent=text;strip.append(status);}\r\n }\r\n for(const event of ['myr5:performance-progress','myr5:account-progress','myr5:account-ready','myr5:account-cleared','storage'])window.addEventListener(event,paint);\r\n paint();return {paint,element:strip};\r\n}\r\n","weapon-training.mjs":"import {EXERCISES,FOCUS_GROUPS} from './exercise-library.mjs';\r\nexport {playerLevel,levelFor,xpFromHistory,LEVEL_XP,XP_KNOBS} from './player-level.mjs';\r\nexport {weaponTierFor} from './performance-progress.mjs';\r\n\r\nconst families={chest:['crossbow','cannon'],legs:['greatsword','hammer'],hips:['scythe','halberd'],core:['mace','tome'],shoulders:['bow','trident'],balance:['axe','sabre'],yoga:['staff','wand'],stances:['dagger','spear'],boxing:['gauntlets','rapier'],cardio:['chakram','flail']};\r\nexport const TRAINING_TRACKS=Object.freeze(Object.fromEntries(FOCUS_GROUPS.map(g=>[g.id,Object.freeze({id:g.id,name:g.name,weapons:Object.freeze(families[g.id])})])));\r\nexport const WEAPON_TRACK=Object.freeze(Object.fromEntries(Object.entries(families).flatMap(([group,ids])=>ids.map(id=>[id,group]))));\r\nexport const trainingGroup=mode=>EXERCISES[mode]?.group||(mode==='jumping'?'cardio':null);\r\nexport function emptyTraining(){return Object.fromEntries(Object.keys(TRAINING_TRACKS).map(id=>[id,{activeDays:0,completedSets:0,totalXp:0,strength:1}]));}\r\nexport function trainingFromDaily(rows){\r\n const result=emptyTraining(),days=Object.fromEntries(Object.keys(result).map(id=>[id,new Set()]));\r\n for(const row of rows){const group=trainingGroup(row.mode),sets=Number(row.sets),day=Number(row.day);if(!group||!Number.isSafeInteger(sets)||sets<1||!Number.isSafeInteger(day)||day<0)continue;result[group].completedSets+=sets;days[group].add(day);}\r\n for(const [group,p] of Object.entries(result)){p.activeDays=days[group].size;p.totalXp=p.activeDays*100;p.strength=1+Math.floor(p.completedSets/4);}\r\n return result;\r\n}\r\nexport function trainingFromWorkouts(workouts){\r\n const seen=new Set(),rows=[];\r\n for(const row of workouts){if(row.completed_at==null||!Number.isSafeInteger(row.completed_at)||row.completed_at<0||!row.id||seen.has(row.id))continue;seen.add(row.id);rows.push({mode:row.mode,day:Math.floor(row.completed_at/86400000),sets:1});}\r\n return trainingFromDaily(rows);\r\n}\r\nexport function trackProgress(value,group){\r\n const p=value?.training?.[group];\r\n if(value?.trainingVersion!==1||!p||!Number.isSafeInteger(p.activeDays)||p.activeDays<0||!Number.isSafeInteger(p.completedSets)||p.completedSets<p.activeDays||p.completedSets>1000000)return {activeDays:0,completedSets:0,totalXp:0,strength:1};\r\n return {activeDays:p.activeDays,completedSets:p.completedSets,totalXp:p.activeDays*100,strength:1+Math.floor(p.completedSets/4)};\r\n}\r\n","workout-import-codec.mjs":"import {projectImportedPerformance,validateImportedPerformance} from './performance-import-codec.mjs';\r\n// Shared browser/Worker codec; no authentication or network I/O.\r\n// Version 1 is immutable: preserve its validation and bytes for old replays.\r\nexport const IMPORT_DIGEST_VERSION = 1;\r\nexport const IMPORT_SNAPSHOT_KEYS = Object.freeze(['schemaVersion','clientWorkoutId','mode','goal','restSeconds','startedAt','completedAt','value','activeSeconds','elapsedSeconds']);\r\n// Pinned from exercise-library.mjs and server/domain.mjs on 2026-09-21.\r\nconst timedModes = 'knee-plank high-plank forearm-plank side-knee-left side-knee-right side-plank-left side-plank-right knee-balance low-tree tree overhead-tree mountain salute chair warrior-one warrior goddess high-horse horse low-horse front-stance-left front-stance-right wall-sit jab-left jab-right boxing double-jab plank-flight'.split(' ');\r\nconst countModes = 'knee-pushup high-incline-pushup low-incline-pushup pushup wide-pushup slow-pushup diamond-pushup decline-pushup shallow-squat squat wide-squat pause-squat slow-squat split-left split-right small-hinge hip-hinge good-morning glute-bridge pause-bridge front-raise lateral-raise overhead-reach standing-press slow-press march high-march jogging step-jack jumping-jack jumping'.split(' ');\r\nexport const IMPORT_MODE_LIMITS = Object.freeze(Object.fromEntries([...timedModes.map(mode=>[mode,Object.freeze({goal:7200,value:7200})]),...countModes.map(mode=>[mode,Object.freeze({goal:100000,value:100000})])]));\r\nexport class WorkoutImportCodecError extends Error {\r\n  constructor(code,message=code){super(message);this.name='WorkoutImportCodecError';this.code=code;}\r\n}\r\nconst fail=(code='invalid_snapshot')=>{throw new WorkoutImportCodecError(code);};\r\nconst own=(value,key)=>Object.prototype.hasOwnProperty.call(value,key);\r\n// Never run an accessor while validating. Detached descriptor values are read once.\r\nfunction record(raw,code){\r\n  try {\r\n    if(!raw||typeof raw!=='object'||Array.isArray(raw)||![null,Object.prototype].includes(Object.getPrototypeOf(raw)))fail(code);\r\n    const descriptors=Object.getOwnPropertyDescriptors(raw),out=Object.create(null);\r\n    if(Object.getOwnPropertySymbols(descriptors).length)fail(code);\r\n    for(const [key,d] of Object.entries(descriptors)){\r\n      if(!own(d,'value')||!d.enumerable)fail(code);\r\n      out[key]=d.value;\r\n    }\r\n    return out;\r\n  }catch{fail(code);}\r\n}\r\nfunction exact(raw,keys,code){\r\n  const value=record(raw,code),actual=Object.keys(value);\r\n  if(actual.length!==keys.length||keys.some(key=>!own(value,key)))fail(code);\r\n  return value;\r\n}\r\nfunction integer(value,min,max,code){if(!Number.isSafeInteger(value)||Object.is(value,-0)||value<min||value>max)fail(code);}\r\nfunction uuid(value,code){if(typeof value!=='string'||!(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/).test(value))fail(code);}\r\nfunction version(value){if(![1,2].includes(value))fail('unsupported_digest_version');}\r\nexport function validateImportSnapshot(raw){\r\n  const supplied=record(raw,'invalid_snapshot');const s=exact(raw,own(supplied,'performance')?[...IMPORT_SNAPSHOT_KEYS,'performance']:IMPORT_SNAPSHOT_KEYS,'invalid_snapshot');\r\n  if(s.schemaVersion!==1)fail();\r\n  uuid(s.clientWorkoutId,'invalid_snapshot');\r\n  if(typeof s.mode!=='string'||!own(IMPORT_MODE_LIMITS,s.mode))fail();\r\n  const limits=IMPORT_MODE_LIMITS[s.mode];\r\n  integer(s.goal,1,limits.goal,'invalid_snapshot');\r\n  integer(s.restSeconds,15,180,'invalid_snapshot');\r\n  integer(s.startedAt,0,8640000000000000,'invalid_snapshot');\r\n  integer(s.completedAt,s.startedAt,8640000000000000,'invalid_snapshot');\r\n  if(own(s,'performance')){for(const [value,min,max] of [[s.value,0,limits.value],[s.activeSeconds,0,7200],[s.elapsedSeconds,s.activeSeconds,7200]])if(!Number.isFinite(value)||Object.is(value,-0)||value<min||value>max)fail();}\r\n  else{integer(s.value,0,limits.value,'invalid_snapshot');integer(s.activeSeconds,0,7200,'invalid_snapshot');integer(s.elapsedSeconds,s.activeSeconds,7200,'invalid_snapshot');}\r\n  const base=Object.fromEntries(IMPORT_SNAPSHOT_KEYS.map(key=>[key,s[key]]));if(own(s,'performance')){try{base.performance=validateImportedPerformance(s.performance,base);}catch{fail();}}return Object.freeze(base);\r\n}\r\n// Caller must obtain this row through its owner-scoped repository. Shape checks\r\n// are not an ownership check. Only these explicit source fields are projected.\r\nexport function snapshotFromCompletedWorkout(raw){\r\n  try {\r\n    const row=record(raw,'snapshot_unavailable');\r\n    if(row.status!=='completed')fail('snapshot_unavailable');\r\n    const completion=record(row.completion,'snapshot_unavailable');\r\n    const base={schemaVersion:row.schemaVersion,clientWorkoutId:row.clientWorkoutId,mode:row.mode,goal:row.goal,restSeconds:row.restSeconds,startedAt:row.startedAt,completedAt:row.completedAt,value:completion.value,activeSeconds:completion.activeSeconds,elapsedSeconds:completion.elapsedSeconds};\r\n    const descriptor=row.progress&&typeof row.progress==='object'?Object.getOwnPropertyDescriptor(row.progress,'performance'):null;\r\n    if(descriptor&&own(descriptor,'value')&&descriptor.value){const p=record(descriptor.value,'snapshot_unavailable');if(!p.preparation&&p.earned!==false&&(p.kind!=='hold'||Array.isArray(p.holdBlocks)))return validateImportSnapshot({...base,performance:projectImportedPerformance(p,base)});}\r\n    return validateImportSnapshot(base);\r\n  }catch{fail('snapshot_unavailable');}\r\n}\r\nexport function canonicalImportSnapshot(raw,digestVersion=IMPORT_DIGEST_VERSION){\r\n  version(digestVersion);\r\n  const snapshot=validateImportSnapshot(raw);if((snapshot.performance?2:1)!==digestVersion)fail('unsupported_digest_version');return JSON.stringify([digestVersion,snapshot]);\r\n}\r\nasync function sha256(text){\r\n  if(typeof globalThis.crypto?.subtle?.digest!=='function')fail('crypto_unavailable');\r\n  let bytes;\r\n  try{bytes=await globalThis.crypto.subtle.digest('SHA-256',new TextEncoder().encode(text));}\r\n  catch{fail('crypto_unavailable');}\r\n  return Array.from(new Uint8Array(bytes),byte=>byte.toString(16).padStart(2,'0')).join('');\r\n}\r\nexport async function fingerprintImportSnapshot(raw,digestVersion=IMPORT_DIGEST_VERSION){\r\n  return sha256(canonicalImportSnapshot(raw,digestVersion));\r\n}\r\nexport function canonicalImportKey(raw){\r\n  const k=exact(raw,['digestVersion','targetAccountId','targetDataEpoch','clientWorkoutId','fingerprint'],'invalid_key_input');\r\n  version(k.digestVersion);\r\n  if(typeof k.targetAccountId!=='string'||!k.targetAccountId||k.targetAccountId.length>512||k.targetAccountId.trim()!==k.targetAccountId||/[\\u0000-\\u001f\\u007f]/.test(k.targetAccountId))fail('invalid_key_input');\r\n  integer(k.targetDataEpoch,1,Number.MAX_SAFE_INTEGER,'invalid_key_input');\r\n  uuid(k.clientWorkoutId,'invalid_key_input');\r\n  if(typeof k.fingerprint!=='string'||!(/^[a-f0-9]{64}$/).test(k.fingerprint))fail('invalid_key_input');\r\n  return JSON.stringify([k.digestVersion,k.targetAccountId,k.targetDataEpoch,k.clientWorkoutId,k.fingerprint]);\r\n}\r\nexport async function importIdempotencyKey(raw){return sha256(canonicalImportKey(raw));}\r\nexport async function prepareWorkoutImport(snapshotInput,targetInput){\r\n  // Capture all untrusted inputs synchronously before the first async boundary.\r\n  const snapshot=validateImportSnapshot(snapshotInput);\r\n  const digestVersion=snapshot.performance?2:1;\r\n  const target=exact(targetInput,['targetAccountId','targetDataEpoch'],'invalid_key_input');\r\n  canonicalImportKey({digestVersion,...target,clientWorkoutId:snapshot.clientWorkoutId,fingerprint:'0'.repeat(64)});\r\n  const fingerprint=await fingerprintImportSnapshot(snapshot,digestVersion);\r\n  const idempotencyKey=await importIdempotencyKey({digestVersion,...target,clientWorkoutId:snapshot.clientWorkoutId,fingerprint});\r\n  return Object.freeze({snapshot,digestVersion,fingerprint,idempotencyKey});\r\n}\r\n","workout-levels.mjs":"import {EXERCISES,FOCUS_GROUPS,GROUP_EXERCISES} from './exercise-library.mjs';\r\nimport {exerciseDifficulty} from './performance-catalog.mjs';\r\nimport {DIFFICULTIES,HOLD_XP_PER_MINUTE,REP_XP,XP_SCALE,SPRINT_ROUND_XP,SPRINT_ROUNDS} from './progression-rules.mjs';\r\nexport const WORKOUT_KINDS=[{id:'hold',name:'Holds'},{id:'reps',name:'Working reps'},{id:'cardio',name:'Cardio & active movement'}];\r\nexport const workoutKind=mode=>EXERCISES[mode]?.kind==='hold'?'hold':EXERCISES[mode]?.kind==='reps'?'reps':'cardio';\r\nexport function workoutChoices(group,kind){return (GROUP_EXERCISES[group]||[]).filter(e=>workoutKind(e.id)===kind);}\r\nexport function workoutLevel(mode){\r\n const e=EXERCISES[mode];if(!e)return null;\r\n const difficulty=exerciseDifficulty(mode),i=DIFFICULTIES.indexOf(difficulty),label=difficulty[0].toUpperCase()+difficulty.slice(1),kind=workoutKind(mode);\r\n return {difficulty,label,kind,group:e.group,groupName:FOCUS_GROUPS.find(g=>g.id===e.group)?.name||e.group,\r\n  xp:kind==='hold'?`${HOLD_XP_PER_MINUTE[i].join(' / ')} base XP per active minute at 1 / 3 / 5-minute tiers`:kind==='reps'?`${REP_XP[i].join(' / ')} base XP per rep at 1–7 / 8–11 / 12–15 reps`:e.group==='cardio'?`Gentle cardio: ${4*XP_SCALE} base XP per active minute. Sprinting: ${SPRINT_ROUND_XP*SPRINT_ROUNDS} base XP for five rounds.`:`${4*XP_SCALE} base XP per active minute`,\r\n  unlock:kind==='hold'?'Weapons at 1 and 3 uninterrupted minutes · Coach at 5 · Golden at 10':kind==='reps'?'Weapons at 8 and 12 reps · Coach at 15':e.group==='cardio'?'Coach at 5 sprint rounds or 5 active minutes':'Coach at 5 active minutes',\r\n  target:kind==='hold'?300:kind==='reps'?15:300,unit:kind==='reps'?'reps':'seconds'};\r\n}\r\n","workout-route-ui.mjs":"import {workoutSymbol} from './workout-symbols.mjs';\r\nimport {EXERCISES,GROUP_EXERCISES} from './exercise-library.mjs';\r\nimport {workoutEligibility,readPerformanceProgress} from './performance-progress.mjs';\r\nimport {workoutLevel,workoutKind} from './workout-levels.mjs';\r\nimport {DIFFICULTIES} from './progression-rules.mjs';\r\nexport function nextPerformanceChallenge(mode,options={}){\r\n const current=EXERCISES[mode];if(!current)return null;\r\n const level=workoutLevel(mode),choices=GROUP_EXERCISES[current.group].filter(e=>workoutKind(e.id)===level.kind),state=readPerformanceProgress(options);\r\n const cleared=Object.values(state.sessions).some(s=>{\r\n  if(s.group!==current.group||s.kind==='hold'&&level.kind!=='hold'||s.kind==='reps'&&level.kind!=='reps'||!['hold','reps'].includes(s.kind)&&level.kind!=='cardio')return false;\r\n  if(s.kind==='hold')return (s.perDifficultyContinuous?.[level.difficulty]??(s.difficulty===level.difficulty?s.continuous:0))>=300;\r\n  return s.difficulty===level.difficulty&&(s.kind==='reps'?s.value>=15:s.kind==='sprint'?s.value>=5:s.value>=300);\r\n });\r\n const next=cleared?choices.find(e=>DIFFICULTIES.indexOf(workoutLevel(e.id).difficulty)>DIFFICULTIES.indexOf(level.difficulty))||current:current;\r\n const eligibility=workoutEligibility(next.id,options);if(!eligibility.allowed)return {allowed:false,reason:eligibility.reason};\r\n const info=workoutLevel(next.id);return {allowed:true,mode:next.id,name:next.name,goal:info.target,unit:info.unit,difficulty:info.label,line:info.unlock};\r\n}\r\nexport function mountWorkoutRoute({mode,busy,pending,onNext}){\r\n const card=document.createElement('section');card.className='workout-route';card.setAttribute('aria-label','Your performance progression');\r\n card.innerHTML='<small>PERFORMANCE PROGRESSION</small><p data-next role=\"status\"></p><p data-rounds></p><button type=\"button\" data-follow>Preview exercise →</button><details><summary>Difficulty milestones</summary><ol data-path></ol><p>Choose any level when ready. Holds and working reps have separate targets. Cosmetic XP does not unlock exercises.</p></details>';\r\n document.querySelector('#podGoals .pod-goals-menu').append(card);\r\n const rest=document.createElement('section');rest.className='workout-route rest-challenge';rest.hidden=true;rest.setAttribute('aria-label','Next performance challenge');rest.innerHTML='<p data-line></p><strong data-next></strong><p data-rounds></p>';document.querySelector('.rest-receipt').after(rest);\r\n const suggestion=()=>pending()?null:nextPerformanceChallenge(mode());\r\n function render(){\r\n  const next=suggestion(),level=workoutLevel(mode()),waiting=pending();\r\n  card.querySelector('[data-next]').textContent=waiting?'Saving your performance…':next?.allowed?`${next.difficulty} · ${workoutSymbol(next.mode)}`:next?.reason||'Choose an exercise to see its milestones.';\r\n  card.querySelector('[data-next]').setAttribute('aria-label',waiting?'Saving your performance…':next?.allowed?`${next.difficulty}: ${next.name}`:card.querySelector('[data-next]').textContent);\r\n  card.querySelector('[data-next]').title=next?.name??'';\r\n  card.querySelector('[data-rounds]').textContent=level?level.unlock:'';\r\n  const button=card.querySelector('[data-follow]');button.disabled=busy()||!next?.allowed;\r\n  card.querySelector('[data-path]').replaceChildren(...DIFFICULTIES.map((d,i)=>{const li=document.createElement('li');li.textContent=`${d[0].toUpperCase()+d.slice(1)} · weapon tiers ${i*5+1}–${i*5+5}`;return li;}));\r\n  if(!rest.hidden){rest.querySelector('[data-line]').textContent='Recover first. Tap the rest boss for daily pack rewards.';rest.querySelector('[data-next]').textContent=next?.allowed?`${next.difficulty} · ${workoutSymbol(next.mode)}`:next?.reason||'Performance saved';rest.querySelector('[data-next]').setAttribute('aria-label',next?.allowed?`${next.difficulty}: ${next.name}`:rest.querySelector('[data-next]').textContent);rest.querySelector('[data-next]').title=next?.name??'';rest.querySelector('[data-rounds]').textContent=next?.allowed?next.line:'';}\r\n }\r\n card.querySelector('[data-follow]').onclick=()=>{const next=suggestion();if(next?.allowed&&!busy())onNext(next);};\r\n for(const event of ['myr5:account-progress','myr5:account-cleared','myr5:movement-configured','myr5:performance-progress'])window.addEventListener(event,render);\r\n document.addEventListener('visibilitychange',()=>{if(!document.hidden)render();});\r\n render();return {render,suggestion,showResult(){rest.hidden=false;render();},hideResult(){rest.hidden=true;},canStart(id){return !pending()&&workoutEligibility(id).allowed;}};\r\n}\r\n","workout-route.mjs":"import {EXERCISES,FOCUS_GROUPS,GROUP_EXERCISES} from './exercise-library.mjs';\r\n\r\nexport const DAILY_ROUND_LIMIT=5;\r\nexport const exerciseFamily=mode=>EXERCISES[mode]?.group||(mode==='jumping'?'cardio':null);\r\nexport const exerciseUnit=mode=>['hold','pace'].includes(EXERCISES[mode]?.kind)?'seconds':EXERCISES[mode]?.kind==='steps'?'steps':'reps';\r\nexport const ROUTE_LINES=Object.freeze({\r\n advance:\"That wasn't enough to impress my clipboard. Ready for the next level?\",\r\n max:\"Maximum difficulty? Fine. One more than your last round. Now we're negotiating.\",\r\n limit:\"Five rounds. Even I have to respect the paperwork. This exercise family is done for today.\",\r\n rest:'Rest first. The next challenge can wait.'\r\n});\r\n\r\nconst dateFormats=new Map();\r\nexport function routeDay(now,timezone){\r\n let format=dateFormats.get(timezone);if(!format){format=new Intl.DateTimeFormat('en-CA',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit'});if(dateFormats.size>=32)dateFormats.delete(dateFormats.keys().next().value);dateFormats.set(timezone,format);}\r\n const fields=Object.fromEntries(format.formatToParts(new Date(now)).map(p=>[p.type,p.value]));\r\n return `${fields.year}-${fields.month}-${fields.day}`;\r\n}\r\nexport function makeExerciseRoute(history,today,{day,timezone,goals={}}){\r\n const records=new Map(history.map(r=>[r.mode,r])),rounds=Object.fromEntries(FOCUS_GROUPS.map(g=>[g.id,0]));\r\n for(const row of today){const group=exerciseFamily(row.mode);if(group)rounds[group]+=Number(row.sets)||0;}\r\n const groups=Object.fromEntries(FOCUS_GROUPS.map(group=>{\r\n  const choices=GROUP_EXERCISES[group.id];let highest=-1;\r\n  const steps=choices.map((m,index)=>{const complete=Number(records.get(m.id)?.sets)>0;if(complete)highest=index;return {mode:m.id,name:m.name,level:index+1,complete};});\r\n  const index=Math.min(choices.length-1,highest+1),movement=choices[index],mastered=highest===choices.length-1,previous=records.get(movement.id);\r\n  const goal=mastered&&Number.isFinite(Number(previous?.value))?Math.floor(Number(previous.value))+1:Number(goals[movement.id])||movement.defaultGoal;\r\n  return [group.id,{id:group.id,name:group.name,steps,mastered,level:index+1,maxLevel:choices.length,today:rounds[group.id],remaining:Math.max(0,DAILY_ROUND_LIMIT-rounds[group.id]),next:{mode:movement.id,name:movement.name,goal:Math.max(1,goal),unit:exerciseUnit(movement.id),group:group.id}}];\r\n }));\r\n return {version:1,day,timezone,limit:DAILY_ROUND_LIMIT,groups};\r\n}\r\nexport function nextChallenge(route,mode){\r\n const current=route?.groups?.[exerciseFamily(mode)];if(!current)return null;\r\n if(current.remaining)return {...current.next,line:current.mastered?ROUTE_LINES.max:ROUTE_LINES.advance,level:current.level,maxLevel:current.maxLevel,round:current.today+1};\r\n // Move through the existing focus order after a family's daily allowance.\r\n const index=FOCUS_GROUPS.findIndex(g=>g.id===current.id),ordered=[...FOCUS_GROUPS.slice(index+1),...FOCUS_GROUPS.slice(0,index)];\r\n const available=ordered.map(g=>route.groups[g.id]).filter(g=>g.remaining>0),next=available.find(g=>!g.mastered)||available[0];\r\n return next?{...next.next,line:ROUTE_LINES.limit,level:next.level,maxLevel:next.maxLevel,round:next.today+1,changedFamily:true}:null;\r\n}\r\n","workout-symbols.mjs":"import {EXERCISES,FOCUS_GROUPS} from './exercise-library.mjs';\r\nimport {PATH_SYMBOLS} from './coach-path-symbols.mjs';\r\n\r\n// Same symbols used by the coach picker, with a rung number for exercise identity.\r\n/** Workout style/group or coach path id -> symbol (no rung number). */\r\nexport const styleSymbol=id=>PATH_SYMBOLS[id]??PATH_SYMBOLS.original;\r\nexport function workoutSymbol(id){\r\n const exercise=EXERCISES[id];\r\n return exercise?`${styleSymbol(exercise.group)} ${exercise.difficulty}`:styleSymbol(id);\r\n}\r\nexport function paintWorkoutSymbol(node,id,label=EXERCISES[id]?.name??id){\r\n node.textContent=workoutSymbol(id);node.setAttribute('aria-label',label);node.title=label;\r\n}\r\n\r\n// Display words for workout styles (focus groups + coach paths). Longest first so \"Arms & shoulders\" beats \"Arms\".\r\nconst STYLE_WORDS=Object.fromEntries([...FOCUS_GROUPS.map(g=>[g.name,g.id]),['Quads','quads'],['Glutes','glutes'],['Arms & shoulders','arms-shoulders'],['Arms & Shoulders','arms-shoulders'],['Arms','arms'],['Martial arts','martial-arts'],['Martial Arts','martial-arts'],['Meditation','meditation']]);\r\nconst escape=s=>s.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$&');\r\n// \"Subway Burpees\" is a game name, not the style.\r\nconst STYLE_RE=new RegExp(`(?<![\\\\w])(?<!Cool )(${Object.keys(STYLE_WORDS).sort((a,b)=>b.length-a.length).map(escape).join('|')})(?![\\\\w])`,'g');\r\n/** One symbol element; screen readers and long-press still get the word. */\r\nexport function styleSymbolNode(id,label,doc=document){\r\n const span=doc.createElement('span');span.className='style-symbol';span.setAttribute('style','display:inline');span.setAttribute('role','img');span.setAttribute('aria-label',label);span.title=label;span.textContent=styleSymbol(id);return span;\r\n}\r\n/** Replace every workout style word in text with its symbol; all other text stays as-is. */\r\nexport function paintStyleText(node,text){\r\n const doc=node.ownerDocument,out=[];let at=0;text=String(text??'');\r\n for(const m of text.matchAll(STYLE_RE)){if(m.index>at)out.push(text.slice(at,m.index));out.push(styleSymbolNode(STYLE_WORDS[m[1]],m[1],doc));at=m.index+m[1].length;}\r\n if(at<text.length)out.push(text.slice(at));\r\n node.replaceChildren(...out);return node;\r\n}\r\n","wrangler.coach-world-preview.json":"{\r\n  \"name\": \"myr5-coach-world-1016\",\r\n  \"main\": \"dist/server/index.js\",\r\n  \"compatibility_date\": \"2026-05-15\",\r\n  \"compatibility_flags\": [\r\n    \"nodejs_compat\"\r\n  ],\r\n  \"workers_dev\": true,\r\n  \"preview_urls\": false,\r\n  \"routes\": [\r\n    {\r\n      \"pattern\": \"coach-world.mominc.online\",\r\n      \"custom_domain\": true\r\n    }\r\n  ],\r\n  \"assets\": {\r\n    \"directory\": \"dist/client\",\r\n    \"binding\": \"ASSETS\",\r\n    \"run_worker_first\": [\r\n      \"/audio/music/*\"\r\n    ]\r\n  },\r\n  \"observability\": {\r\n    \"enabled\": true,\r\n    \"logs\": {\r\n      \"enabled\": true,\r\n      \"head_sampling_rate\": 1\r\n    },\r\n    \"traces\": {\r\n      \"enabled\": true,\r\n      \"head_sampling_rate\": 0.01\r\n    }\r\n  },\r\n  \"triggers\": {\r\n    \"crons\": [\r\n      \"* * * * *\"\r\n    ]\r\n  },\r\n  \"vars\": {\r\n    \"WORKOUT_IMPORTS_ENABLED\": \"true\",\r\n    \"CLERK_PUBLISHABLE_KEY\": \"pk_live_Y2xlcmsubW9taW5jLm9ubGluZSQ\",\r\n    \"CLERK_FRONTEND_URL\": \"https://clerk.mominc.online\",\r\n    \"CLERK_AUTHORIZED_PARTIES\": \"https://coach-world.mominc.online\",\r\n    \"CLERK_JWT_KEY\": \"-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rzkD3s3Q0klhjek/H6t\\n8x9OEYKO7aqAhpbe0vKVfwuc+cRQNVxNFLp1l/ekL49Hmsey+2yfijpSPJZ9gu/d\\nkQ9sgJLdWO+Um4SZEyt7iCwIpqTbtAheWjRpO8SRBqpNl/bRRzqndyr6D60EiYXh\\n0Hval+ebbMVL33IVuq9FwLmndrIHt6kq1pXgg1RuAari9/WlI/75tHum1XcxujLP\\nRCb4jIjRuaq9Q+gfx5snUiBGBTFM4yMPCj29wvQWggcEYHqklKRqc8w4yXUX7vEb\\nscoZuuvV1nFYgR4dA0DLopBa4jPSdZwLaefYsj8A0RY/abXW3pWYgNDSIXzmGo9W\\nBwIDAQAB\\n-----END PUBLIC KEY-----\\n\",\r\n    \"RELEASE_ORIGIN\": \"https://coach-world.mominc.online\"\r\n  },\r\n  \"d1_databases\": [\r\n    {\r\n      \"binding\": \"DB\",\r\n      \"database_name\": \"myr5-coach-world-1016\",\r\n      \"database_id\": \"7fd38892-c994-4a0a-9486-fe4311650591\",\r\n      \"migrations_dir\": \"drizzle\"\r\n    }\r\n  ]\r\n}\r\n","wrangler.dance-music.jsonc":"{\r\n  \"$schema\": \"node_modules/wrangler/config-schema.json\",\r\n  \"name\": \"myr5-dance-music-lab\",\r\n  \"compatibility_date\": \"2026-10-17\",\r\n  \"workers_dev\": true,\r\n  \"preview_urls\": false,\r\n  \"assets\": {\"directory\": \"dist/dance-music-lab\", \"not_found_handling\": \"404-page\"}\r\n}\r\n","wrangler.grimoire-preview.jsonc":"{\r\n  \"$schema\": \"node_modules/wrangler/config-schema.json\",\r\n  \"name\": \"myr5-grimoire-review-1020\",\r\n  \"account_id\": \"f35c131b47d223ae301a8b577d6e2d59\",\r\n  \"compatibility_date\": \"2026-10-20\",\r\n  \"workers_dev\": true,\r\n  \"routes\": [],\r\n  \"assets\": { \"directory\": \"artifacts/grimoire-review\" }\r\n}\r\n","wrangler.jsonc":"// MyR5 Coach on Cloudflare Workers (replaces OpenAI Sites hosting).\r\n// `npm run build` first; the Worker is dist/server/index.js and the static app is dist/client.\r\n// Production: `wrangler deploy`. Staging (workers.dev, empty D1, no cron): `wrangler deploy --env staging`.\r\n// Secrets are `wrangler secret put <NAME>` per Worker, never in this file.\r\n{\r\n\t\"$schema\": \"node_modules/wrangler/config-schema.json\",\r\n\t\"name\": \"myr5-coach\",\r\n\t\"main\": \"dist/server/index.js\",\r\n\t\"compatibility_date\": \"2026-05-15\",\r\n\t// The Vite SSR bundle resolves @clerk/backend's crypto to `node:crypto` (webcrypto), as on Sites.\r\n\t\"compatibility_flags\": [\"nodejs_compat\"],\r\n\t\"workers_dev\": false,\r\n\t\"preview_urls\": false,\r\n\t// Production custom domain. Cloudflare provisions DNS and TLS for this Worker route.\r\n\t\"routes\": [{ \"pattern\": \"myr5.mominc.online\", \"custom_domain\": true }],\r\n\t// Assets first, as on Sites: `/` serves index.html (200), /pose.html 307s to /pose, and\r\n\t// unmatched paths and the bounded /audio/music/* byte-serving route reach the Worker.\r\n\t\"assets\": { \"directory\": \"dist/client\", \"binding\": \"ASSETS\", \"run_worker_first\": [\"/audio/music/*\"] },\r\n\t\"observability\": {\r\n\t\t\"enabled\": true,\r\n\t\t\"logs\": { \"enabled\": true, \"head_sampling_rate\": 1 },\r\n\t\t\"traces\": { \"enabled\": true, \"head_sampling_rate\": 0.01 }\r\n\t},\r\n\t\"triggers\": { \"crons\": [\"* * * * *\"] },\r\n\t// Public values (live /api/auth/config serves the Clerk ones). Clerk sign-in also needs the\r\n\t// CLERK_JWT_KEY secret; release email stays off unless the RESEND_API_KEY secret exists.\r\n\t\"vars\": {\n\t\t\"WORLD_RUNS\": \"1\",\n\t\t\"WORLD_RAIDS\": \"1\",\n\t\t\"WORKOUT_IMPORTS_ENABLED\": \"true\",\n\t\t\"CLERK_PUBLISHABLE_KEY\": \"pk_live_Y2xlcmsubW9taW5jLm9ubGluZSQ\",\r\n\t\t\"CLERK_FRONTEND_URL\": \"https://clerk.mominc.online\",\r\n\t\t\"CLERK_AUTHORIZED_PARTIES\": \"https://myr5.mominc.online\",\r\n\t\t\"RELEASE_ORIGIN\": \"https://myr5.mominc.online\",\r\n\t\t\"SPOTIFY_CLIENT_ID\": \"9eb27ceb2aed4ee59517d5b0854a8b79\",\r\n\t\t\"SPOTIFY_REDIRECT_URI\": \"https://myr5.mominc.online/api/spotify/callback\",\r\n\t\t\"RELEASE_EMAIL_FROM\": \"dispatch@mominc.online\",\r\n\t\t\"VAPID_SUBJECT\": \"https://myr5.mominc.online\"\r\n\t},\r\n\t\"d1_databases\": [\r\n\t\t{\r\n\t\t\t\"binding\": \"DB\",\r\n\t\t\t\"database_name\": \"myr5-coach\",\r\n\t\t\t\"database_id\": \"5731afde-1437-4fcf-9b25-1ad868b24fe3\",\r\n\t\t\t\"migrations_dir\": \"drizzle\"\r\n\t\t}\r\n\t],\r\n\t\"env\": {\r\n\t\t\"staging\": {\r\n\t\t\t\"name\": \"myr5-coach-staging\",\r\n\t\t\t\"workers_dev\": true,\r\n\t\t\"routes\": [],\r\n\t\t\t\"triggers\": { \"crons\": [] },\r\n\t\t\t// No Clerk (its production keys only accept mominc.online) and no release email on staging.\r\n\t\t\t\"vars\": { \"WORKOUT_IMPORTS_ENABLED\": \"true\" },\r\n\t\t\t\"d1_databases\": [\r\n\t\t\t\t{\r\n\t\t\t\t\t\"binding\": \"DB\",\r\n\t\t\t\t\t\"database_name\": \"myr5-coach-staging\",\r\n\t\t\t\t\t\"database_id\": \"4b93f2db-21cb-4697-95ef-4611f07818e8\",\r\n\t\t\t\t\t\"migrations_dir\": \"drizzle\"\r\n\t\t\t\t}\r\n\t\t\t]\r\n\t\t}\r\n\t}\r\n}\r\n"}